[
  {
    "path": ".assets/raw/Makefile",
    "content": "all:\n\tconvert -delay 100 -loop 0 navigation_*.png navigation.gif\n\tconvert -delay 100 -loop 0 orgmode_*.png orgmode.gif\n\tconvert -delay 100 -loop 0 photo_management_*.png photo_management.gif\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\nopen_collective: filestash\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.md",
    "content": "---\nname: Bug\nabout: Report a bug\ntitle: \"[bug] \"\nlabels: ''\nassignees: ''\n\n---\n\n# Description of the bug\n\n<!-- TO COMPLETE -->\n\n# Step by step instructions to reproduce the bug\n\n<!-- TO COMPLETE -->\n\n# Can you replicate that error from the demo?\n\n<!--\n1. the demo is available from https://demo.filestash.app\n2. if you want a testing backgend, you can use this: https://demo.filestash.app/login?type=s3&access_key_id=Q3AM3UQ867SPQQA43P2F&secret_access_key=zuf%2BtfteSlswRu7BJ86wekitnifILbZam1KYY3TG&region=us-east-2&encryption_key=&endpoint=https%3A%2F%2Fplay.minio.io%3A9000%2F\n-->\n\n# Observed behavior\n\n<!-- TO COMPLETE -->\n\n# Expected behavior\n\n<!-- TO COMPLETE -->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature.md",
    "content": "---\nname: Feature Request\nabout: Request for a new feature\ntitle: \"[Feature Request] \"\nlabels: ''\nassignees: ''\n\n---\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/support.md",
    "content": "---\nname: Support\nabout: Technical support is only available on IRC\ntitle: \"[support] DO NOT CREATE A SUPPORT TICKET FROM GITHUB\"\nlabels: ''\nassignees: ''\n\n---\n\nPlease, don't create tickets on github for support. Instead, you can either:\n- visit the community support on libera.chat #filestash. A searchable archive is available at https://support.filestash.app If you don't already have an IRC client, you can try this link: https://kiwiirc.com/nextclient/#irc://irc.libera.chat/#filestash?nick=guest??\n- register for enterprise support at https://www.filestash.app/pricing/#support\n"
  },
  {
    "path": ".github/stale.yml",
    "content": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 60\n# Number of days of inactivity before a stale issue is closed\ndaysUntilClose: 7\n# Issues with these labels will never be considered stale\nexemptLabels:\n  - pinned\n  - security\n# Label to use when marking an issue as stale\nstaleLabel: wontfix\n# Comment to post when marking an issue as stale. Set to `false` to disable\nmarkComment: >\n  This issue has been automatically marked as stale because it has not had\n  recent activity. It will be closed if no further activity occurs. Thank you\n  for your contributions.\n# Comment to post when closing a stale issue. Set to `false` to disable\ncloseComment: false"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing Guide\n\nThanks for taking the time to join our community and start contributing. This guide will help you get started with the Filestash project.\n\n## How to contribute?\n\n### Before you submit a pull request\n\nFor anything else than a typo or a bug fix, please raise an issue to discuss your proposal before submitting any code.\n\n### License for contributions\n\nAs the copyright owner, you agree to license your contributions under an irrevocable MIT license.\n\n\n### Building from source\n\n*Prerequisites*: Git, Make, Node, Go, Glib 2.0\n\n```\n# Download the source\ngit clone https://github.com/mickael-kerjean/filestash\ncd filestash\n\n# Install dependencies\nnpm install --legacy-peer-deps # frontend dependencies\nmake build_init # install the required static libraries\nmkdir -p ./dist/data/state/\ncp -R config ./dist/data/state/\n\n# Create the build\nmake build_frontend\nmake build_backend\n\n# Run the program\n./dist/filestash\n```\n\n### Tests\nOur tests aren't open source. This comes as an attempt to restrict opportunistic forks (see [1](https://news.ycombinator.com/item?id=17006902#17009852) and [2](https://www.reddit.com/r/selfhosted/comments/a54axs/annoucing_jellyfin_a_free_software_fork_of_emby/ebk92iu/?utm_source=share&utm_medium=web2x)) from creating a stable release without serious commitment and splitting the community in pieces while I'm on holidays. Also the project welcome serious and willing maintainers.\n"
  },
  {
    "path": "Jenkinsfile",
    "content": "pipeline {\n    agent any\n    options {\n        buildDiscarder(logRotator(numToKeepStr: \"10\", artifactNumToKeepStr: \"1\"))\n    }\n    stages {\n        stage(\"Setup\") {\n            steps {\n                git(\n                    url: \"git@github.com:mickael-kerjean/filestash\",\n                    branch: \"master\"\n                )\n                dir(\"test\") {\n                    git(\n                        url: \"git@github.com:mickael-kerjean/filestash-test.git\",\n                        credentialsId: \"github-com-filestash-test\",\n                        branch: \"main\"\n                    )\n                }\n            }\n        }\n        stage(\"Build\") {\n            steps {\n                script {\n                    docker.image(\"golang:1.24-bookworm\").inside(\"--user=root\") {\n                        sh \"apt update -y && apt install -y libbrotli-dev brotli\"\n                        sh \"sed -i 's|plg_image_c|plg_image_golang|' server/plugin/index.go\"\n                        sh \"make init\"\n                        sh \"make build\"\n                    }\n                }\n            }\n        }\n        stage(\"Test\") {\n            steps {\n                script {\n                    // smoke test\n                    docker.image(\"golang:1.24-bookworm\").inside(\"--user=root\") {\n                        sh 'timeout 5 ./dist/filestash > access.log || code=$?; if [ $code -ne 124 ]; then exit $code; fi'\n                        sh \"cat access.log\"\n                        sh \"cat access.log | grep -q \\\"\\\\[http\\\\] starting\\\"\"\n                        sh \"cat access.log | grep -q \\\"listening\\\"\"\n                        sh \"cat access.log | grep -vz \\\"ERR\\\"\"\n                    }\n                    // test frontend\n                    docker.image(\"node:20\").inside(\"--user=root\") {\n                        sh \"cd public && npm install\"\n                        sh \"cd public && npm run lint\"\n                        sh \"cd public && npm run check\"\n                        // sh \"cd public && npm run test\"\n                    }\n                    // test backend\n                    docker.image(\"golang:1.24-bookworm\").inside(\"--user=root\") {\n                        sh \"cp ./test/assets/* /tmp/\"\n                        sh \"go generate ./test/unit_go/...\"\n                        sh \"go get ./...\"\n                        sh \"go test -count=1 \\$(go list ./server/... | grep -v \\\"server/plugin\\\" | grep -v \\\"server/generator\\\")\"\n                    }\n                    // test e2e\n                    docker.image(\"machines/puppeteer:latest\").inside(\"--user=root\") {\n                        sh \"cd ./test/e2e && npm install\"\n                        sh \"chmod +x ./dist/filestash\"\n                        sh \"./dist/filestash > /dev/null &\"\n                        sh \"cd ./test/e2e && node servers/webdav.js > /dev/null &\"\n                        // sh \"cd ./test/e2e && npm test\"\n                    }\n                }\n            }\n        }\n\n        stage(\"Release\") {\n            steps {\n                sh \"docker buildx build --no-cache --platform linux/amd64,linux/arm64 -t machines/filestash:latest --push ./docker/\"\n            }\n        }\n    }\n    post {\n        always {\n            cleanWs()\n        }\n    }\n}"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 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 Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\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,\nour General Public Licenses are 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.\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  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\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 Affero 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. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\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 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 work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be 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 Affero 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 Affero 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 Affero 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 Affero General Public License as published\n    by 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 Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero 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 your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\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 AGPL, see\n<http://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "Makefile",
    "content": "all:\n\tmake init\n\tmake build\n\ninit:\n\tgo get ./...\n\tgo generate -x ./server/...\n\nbuild:\n\tgo build --tags \"fts5\" -o dist/filestash$(if $(filter windows,$(GOOS)),.exe) cmd/main.go\n"
  },
  {
    "path": "README.md",
    "content": "![screenshot](https://raw.githubusercontent.com/mickael-kerjean/filestash_images/master/.assets/photo.jpg)\n\n# What is this?\n\n<p>\n    It started as a storage agnostic Dropbox-like file manager that works with every storage protocol: <a href=\"https://www.filestash.app/ftp-client.html\">FTP</a>, <a href=\"https://www.filestash.app/ssh-file-transfer.html\">SFTP</a>, <a href=\"https://www.filestash.app/s3-browser.html\">S3</a>, <a href=\"https://www.filestash.app/smb-client.html\">SMB</a>, <a href=\"https://www.filestash.app/webdav-client.html\">WebDAV</a>, IPFS, and <a href=\"https://www.filestash.app/docs/plugin/#storage\">about 20 more</a>.\n</p>\n\n<p>\n    It grew into what we want to be the world's best file management platform, where everything that's not a fundamental truth of the universe lives in a plugin. Where other platforms are take-it-or-leave-it, ours gives you a rock solid core and a plugin system to handle opinions, so however deep requirements go, the only limit won't be technical but your own creativity.\n</p>\n\n<p>\n    <a href=\"http://demo.filestash.app\"><img src=\"https://www.filestash.app/img/illustration/filestash-integrations.png\" alt=\"storage + auth architecture\" /></a>\n</p>\n\n# Key Features\n\n<ul>\n    <li><a href=\"#vision--philosophy\">Plugin Driven Architecture</a>: everything that matters is a plugin, browse the <a href=\"https://www.filestash.app/docs/plugin/\">ecosystem</a> or <a href=\"https://www.filestash.app/docs/guide/plugin-development.html?origin=github\">build your own</a>. With this approach, you get exactly what you need without any kind of overhead or bloat.</li>\n    <li>Universal Access: a sleek web client made in vanilla JS that's infinitely customizable via <a href=\"https://www.filestash.app/docs/guide/plugin-development.html#patch-plugins-in-depth\">dynamic patch plugins</a>, plus gateways to access your data via <a href=\"https://www.filestash.app/docs/guide/sftp-gateway.html?origin=github\">SFTP</a>, <a href=\"https://www.filestash.app/docs/guide/mcp-gateway.html?origin=github\">MCP</a> or S3, integrations via <a href=\"https://www.filestash.app/docs/api/#api\">APIs</a> and via web components like this <a href=\"https://www.filestash.app/tools/psd-viewer.html\">psd viewer</a></li>\n    <li><a href=\"https://www.filestash.app/docs/plugin/#storage\">Integrations</a>: our explicit goal is to support 100% of storage and authentication technologies on the market. Beyond your usual options, you can go much further, like a <a href=\"https://www.filestash.app/docs/guide/virtual-filesystem.html?origin=github\">virtual filesystem</a> delegating authentication to your <a href=\"https://github.com/mickael-kerjean/filestash/tree/master/server/plugin/plg_authenticate_wordpress\">WordPress site</a> and using its roles to drive <a href=\"https://www.filestash.app/docs/guide/authorization.html#option-2-rbac\">RBAC authorization</a>.</li>\n    <li><a href=\"https://www.filestash.app/docs/guide/workflow-engine.html\">Workflow Engine</a>: automate anything that happens to your files by chaining actions on events, from simple notifications via Slack or email to full on MFT pipelines and everything in between.</li>\n    <li>File Apps: use any of the existing apps or <a href=\"https://www.filestash.app/docs/guide/plugin-development.html#xdg-open-plugins-in-depth\">build your own</a>, from astronomy to embroidery and everything in between like:\n        <ul>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_photography.zip\">photography</a>: heif, nef, raf, <a href=\"https://www.filestash.app/tools/tiff-viewer.html\">tiff</a>, raw, arw, sr2, srf, nrw, cr2, crw, x3f, pef, rw2, orf, mrw, mdc, mef, mos, dcr, kdc, 3fr, erf and srw</li>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_photography.zip\">astronomy</a>: <a href=\"https://www.filestash.app/tools/fits-viewer.html\">fits</a>, <a href=\"https://www.filestash.app/tools/xisf-viewer.html\">xisf</a></li>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_science.zip\">science</a>: with latex, plantuml & pandoc compilers</li>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_musician.zip\">music</a>: mid, midi, gp4 and gp5</li>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_gis.zip\">GIS</a>: <a href=\"https://www.filestash.app/tools/geojson-viewer.html\">geojson</a>, <a href=\"https://www.filestash.app/tools/shp-viewer.html\">shp</a>, gpx, wms and <a href=\"https://www.filestash.app/tools/dbf-viewer.html\">dbf</a></li>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_engineering.zip\">data engineering</a>: <a href=\"https://www.filestash.app/tools/parquet-viewer.html\">parquet</a>, <a href=\"https://www.filestash.app/tools/arrow-viewer.html\">arrow</a>, <a href=\"https://www.filestash.app/tools/feather-viewer.html\">feather</a>, <a href=\"https://www.filestash.app/tools/avro-viewer.html\">avro</a>, <a href=\"https://www.filestash.app/tools/orc-viewer.html\">orc</a>, <a href=\"https://www.filestash.app/tools/hdf5-viewer.html\">hdf5</a>, <a href=\"https://www.filestash.app/tools/hdf5-viewer.html\">h5</a>, <a href=\"https://www.filestash.app/tools/netcdf-viewer.html\">netcdf</a>, <a href=\"https://www.filestash.app/tools/netcdf-viewer.html\">nc</a>, rds, rda and rdata</li>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_dev.zip\">dev</a>: a, so, o, dylib, dll, tar, tgz, zip, har, cap, pcap, pcapng and <a href=\"https://www.filestash.app/tools/sqlite-viewer.html\">sqlite</a></li>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_creative.zip\">creative work</a>: svg, <a href=\"https://www.filestash.app/tools/psd-viewer.html\">psd</a>, ai, <a href=\"https://www.filestash.app/tools/sketch-viewer.html\">sketch</a>, <a href=\"https://www.filestash.app/tools/cdr-viewer.html\">cdr</a>, woff, woff2, ttf, otf, eot, exr, tga, pgm, ppm, dds, ktx, dpx, pcx, xpm, pnm, xbm, aai, xwd, cin, pbm, pcd, sgi, wbmp and rgb</li>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_biomed.zip\">biomedical</a>: dicom, sam, bam, cif, pdb, xyz, sdf, mol, mol2 and mmtf</li>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_autodesk.zip\">autodesk</a>: <a href=\"https://www.filestash.app/tools/dwg-viewer.html\">dwg</a> and <a href=\"https://www.filestash.app/tools/dxf-viewer.html\">dxf</a></li>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_adobe.zip\">adobe</a>: <a href=\"https://www.filestash.app/tools/psd-viewer.html\">psd</a>, ai, <a href=\"https://www.filestash.app/tools/xd-viewer.html\">xd</a>, <a href=\"https://www.filestash.app/tools/dng-viewer.html\">dng</a>, <a href=\"https://www.filestash.app/tools/eps-viewer.html\">postscript</a>, aco, ase, swf</li>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_3d.zip\">3d</a>: fbx, gltf, obj, stl, step, mesh, ifc, dae</li>\n            <li><a href=\"https://demo.filestash.app/assets/plugin/application_embroidery.zip\">embroidery</a>: dgt, dst, dsb, dsz, edr, exp, 10o, col, hus, inf, jef, ksm, pcm, pcs, pes, sew, shv, sst, tap, u01, vip, vp3 and xxx</li>\n            <li><a href=\"https://github.com/mickael-kerjean/filestash/tree/master/server/plugin/plg_widget_pgp\">e2e</a>: pgp, gpg</li>\n        </ul>\n    </li>\n    <li>Themes: <br>\n        <img src=\"https://www.filestash.app/img/screenshots/theme_github.png\" height=\"150\" />\n        <img src=\"https://www.filestash.app/img/screenshots/theme_apple.png\" height=\"150\" />\n        <img src=\"https://www.filestash.app/img/screenshots/theme_dropbox.png\" height=\"150\" />\n        <img src=\"https://www.filestash.app/img/screenshots/theme_ibm.png\" height=\"150\" />\n    </li>\n    <li>AI features for <a href=\"https://www.filestash.app/docs/guide/search.html\">search</a>, <a href=\"https://www.filestash.app/features/smart-folder.html\">smart folders</a> and OCRs.</li>\n    <li>... and much <sub>much <sub>more (versioning, audit, public site, antivirus, quota, chat, chromecast support, on demand video transcoding, mounting shared links as network drive, ....)</sub></sub><br> As a rule of thumb, if your problem involves files, we either already <a href=\"https://www.filestash.app/docs/plugin/\">have a plugin</a> for it or can make a plugin for it\n</ul>\n\n\n# Getting Started\n\nTo install Filestash, head to the [Getting started](https://www.filestash.app/docs/?origin=github) guide. If you want to leverage plugins, head over to the [inventory](https://www.filestash.app/docs/plugin/?origin=github), or learn about [developing your own plugins](https://www.filestash.app/docs/guide/plugin-development.html?origin=github).\n\nIf you want guidance and expert help on your file management problem, [book a call](https://www.filestash.app/tunnel/demo/?origin=github) and let's figure out if Filestash is the right platform for you.\n\n\n# Vision & Philosophy\n\nOur goal is simple: **to build the best file management platform ever made. Period.** But \"best\" means different things to different people, and making Filestash modular is the only sane model to accomplish that. Anything that isn't a fundamental truth of the universe and might spark a debate belongs in a plugin. Literally every piece listed in the key features is a plugin you can swap for another implementation or remove entirely.\n\nThis modularity is made possible by the magic of programming interfaces. For example, if you want a [Dropbox-like frontend for FTP](https://news.ycombinator.com/item?id=9224), you will find out the [FTP plugin](https://github.com/mickael-kerjean/filestash/tree/master/server/plugin/plg_backend_ftp) simply implements this interface:\n```go\ntype IBackend interface {\n\tLs(path string) ([]os.FileInfo, error)           // list files in a folder\n\tStat(path string) (os.FileInfo, error)           // file stat\n\tCat(path string) (io.ReadCloser, error)          // download a file\n\tMkdir(path string) error                         // create a folder\n\tRm(path string) error                            // remove something\n\tMv(from string, to string) error                 // rename something\n\tSave(path string, file io.Reader) error          // save a file\n\tTouch(path string) error                         // create a file\n\n\t// I have omitted 2 other methods, a first one to enable connections reuse and\n\t// another one to declare what should the login form be like.\n}\n```\n\nThere are interfaces you can implement for every key component of Filestash: from storage, to authentication, <a href=\"https://www.filestash.app/docs/guide/authorization.html\">authorisation</a>, custom apps, <a href=\"https://www.filestash.app/docs/guide/search.html\">search</a>, thumbnailing, frontend patches, middleware, endpoint creation and a few others documented in the [plugin development guide](https://www.filestash.app/docs/guide/plugin-development.html).\n\nTo see what's currently installed in your instance, head over to [/about](https://demo.filestash.app/about). The inventory of plugins is [documented here](https://www.filestash.app/docs/plugin/)\n\n\n# Support\n\n- Commercial Users → [support contract](https://www.filestash.app/pricing/?origin=github)\n- For individuals:\n  - [#filestash](https://kiwiirc.com/nextclient/#irc://irc.libera.chat/#filestash?nick=guest??) on IRC (libera.chat)\n  - Bitcoin: `3LX5KGmSmHDj5EuXrmUvcg77EJxCxmdsgW`\n  - [Open Collective](https://opencollective.com/filestash)\n\n\n# Credits\n\nFilestash stands on the shoulder of: [contributors](https://github.com/mickael-kerjean/filestash/graphs/contributors), folks developing [awesome libraries](https://github.com/mickael-kerjean/filestash/blob/master/go.mod), a whole bunch of C stuff (the [C standard library](https://imgs.xkcd.com/comics/dependency.png), [libjpeg](https://libjpeg-turbo.org/), [libpng](https://www.libpng.org/pub/png/libpng.html), [libgif](https://giflib.sourceforge.net/), [libraw](https://www.libraw.org/about) and many more), [fontawesome](https://fontawesome.com), [material](https://material.io/icons/), [Browser stack](https://www.browserstack.com/) to let us test on real devices, and the many guys from Nebraska and elsewhere who have been thanklessly maintaining the critical pieces that Filestash sits on top:\n\n<img src=\"https://imgs.xkcd.com/comics/dependency.png\" alt=\"credit to the nebraska guy on xkcd\" />\n"
  },
  {
    "path": "cmd/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"github.com/mickael-kerjean/filestash\"\n\t\"github.com/mickael-kerjean/filestash/server\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/ctrl\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n\t_ \"github.com/mickael-kerjean/filestash/server/pkg\"\n\t\"github.com/mickael-kerjean/filestash/server/pkg/workflow\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin\"\n\n\t\"github.com/gorilla/mux\"\n)\n\nfunc main() {\n\tRun(mux.NewRouter())\n}\n\nfunc Run(router *mux.Router) {\n\tcheck(InitLogger(), \"Logger init failed. err=%s\")\n\tcheck(InitConfig(), \"Config init failed. err=%s\")\n\tcheck(workflow.Init(), \"Worklow Initialisation failure. err=%s\")\n\tcheck(model.PluginDiscovery(), \"Plugin Discovery failed. err=%s\")\n\tcheck(ctrl.InitPluginList(embed.EmbedPluginList, model.PLUGINS), \"Plugin Initialisation failed. err=%s\")\n\tif Hooks.Get.Starter() == nil {\n\t\tcheck(ErrNotFound, \"Missing starter plugin. err=%s\")\n\t}\n\tfor _, fn := range Hooks.Get.Onload() {\n\t\tfn()\n\t}\n\tfor _, obj := range Hooks.Get.HttpEndpoint() {\n\t\tobj(router)\n\t}\n\tserver.Build(router)\n\tserver.PluginRoutes(router)\n\tif os.Getenv(\"DEBUG\") == \"true\" {\n\t\tserver.DebugRoutes(router)\n\t}\n\tserver.CatchAll(router)\n\tHooks.Get.Starter()(withSignal(), router)\n\tfor _, fn := range Hooks.Get.OnQuit() {\n\t\tfn()\n\t}\n}\n\nfunc check(err error, msg string) {\n\tif err == nil {\n\t\treturn\n\t}\n\tLog.Error(msg, err.Error())\n\tos.Exit(1)\n}\n\nfunc withSignal() context.Context {\n\tctx, cancel := context.WithCancel(context.Background())\n\tgo func() {\n\t\tquit := make(chan os.Signal, 1)\n\t\tsignal.Notify(quit, syscall.SIGTERM, syscall.SIGINT)\n\t\t<-quit\n\t\tcancel()\n\t}()\n\treturn ctx\n}\n"
  },
  {
    "path": "config/config.json",
    "content": "{\n    \"general\": {\n    },\n    \"features\": {\n    },\n    \"log\": {\n    },\n    \"email\": {\n    },\n    \"oauth\": {\n    },\n    \"connections\": [\n        {\n            \"type\": \"webdav\",\n            \"label\": \"WebDav\"\n        },\n        {\n            \"type\": \"ftp\",\n            \"label\": \"FTP\"\n        },\n        {\n            \"type\": \"sftp\",\n            \"label\": \"SFTP\"\n        },\n        {\n            \"type\": \"git\",\n            \"label\": \"GIT\"\n        },\n        {\n            \"type\": \"s3\",\n            \"label\": \"S3\"\n        },\n        {\n            \"type\": \"dropbox\",\n            \"label\": \"Dropbox\"\n        },\n        {\n            \"type\": \"gdrive\",\n            \"label\": \"Drive\"\n        }\n    ]\n}\n"
  },
  {
    "path": "config/mime.json",
    "content": "{\n\t\"10o\": \"image/x-10o\",\n\t\"3dm\": \"model/3dm\",\n\t\"3fr\": \"image/x-hasselblad-3fr\",\n\t\"3gp\": \"video/3gpp\",\n\t\"3gpp\": \"video/3gpp\",\n\t\"7z\": \"application/x-7z-compressed\",\n\t\"a\": \"application/x-archive\",\n\t\"aai\": \"image/x-dune-aai\",\n\t\"aco\": \"application/x-aco\",\n\t\"ai\": \"application/pdf\",\n\t\"aif\": \"audio/x-aiff\",\n\t\"aiff\": \"audio/x-aiff\",\n\t\"apk\": \"application/vnd.android.package-archive\",\n\t\"arrow\": \"application/vnd.apache.arrow.file\",\n\t\"arw\": \"image/x-sony-arw\",\n\t\"ase\": \"application/x-ase\",\n\t\"asf\": \"video/x-ms-asf\",\n\t\"asx\": \"video/x-ms-asf\",\n\t\"atom\": \"application/atom+xml\",\n\t\"avi\": \"video/x-msvideo\",\n\t\"avif\": \"image/avif\",\n\t\"avro\": \"application/vnd.apache.avro\",\n\t\"bam\": \"application/x-bam\",\n\t\"bin\": \"application/octet-stream\",\n\t\"bmp\": \"image/x-ms-bmp\",\n\t\"bz2\": \"application/x-bz2\",\n\t\"cab\": \"application/vnd.ms-cab-compressed\",\n\t\"cap\": \"application/x-pcap\",\n\t\"cco\": \"application/x-cocoa\",\n\t\"cdr\": \"application/vnd.corel-draw\",\n\t\"cif\": \"application/x-cif\",\n\t\"cin\": \"image/x-cin\",\n\t\"col\": \"image/x-col\",\n\t\"cr2\": \"image/x-canon-cr2\",\n\t\"crt\": \"application/x-x509-ca-cert\",\n\t\"crw\": \"image/x-canon-crw\",\n\t\"css\": \"text/css\",\n\t\"csv\": \"text/csv\",\n\t\"cur\": \"image/x-win-bitmap\",\n\t\"dae\": \"model/vnd.collada+xml\",\n\t\"db\": \"application/x-sqlite3\",\n\t\"dbf\": \"application/dbf\",\n\t\"dcm\": \"image/dicom\",\n\t\"dcr\": \"image/x-kodak-dcr\",\n\t\"dds\": \"image/x-dds\",\n\t\"deb\": \"application/octet-stream\",\n\t\"der\": \"application/x-x509-ca-cert\",\n\t\"dgt\": \"image/bmp\",\n\t\"dll\": \"application/x-msdownload\",\n\t\"dmg\": \"application/octet-stream\",\n\t\"dng\": \"image/x-adobe-dng\",\n\t\"doc\": \"application/msword\",\n\t\"docx\": \"application/word\",\n\t\"docm\": \"application/vnd.ms-word.document.macroEnabled.12\",\n\t\"dotx\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.template\",\n\t\"dotm\": \"application/vnd.ms-word.template.macroEnabled.12\",\n\t\"dpkg\": \"application/dpkg-www-installer\",\n\t\"dpx\": \"image/dpx\",\n\t\"ds_store\": \"application/octet-stream\",\n\t\"dsb\": \"image/x-dsb\",\n\t\"dst\": \"image/x-dst\",\n\t\"dsz\": \"image/x-dsz\",\n\t\"dxf\": \"application/dxf\",\n\t\"dwg\": \"application/acad\",\n\t\"dylib\": \"application/x-dylib\",\n\t\"ear\": \"application/java-archive\",\n\t\"edr\": \"image/x-edr\",\n\t\"emf\": \"image/emf\",\n\t\"emz\": \"image/x-emz\",\n\t\"eot\": \"application/vnd.ms-fontobject\",\n\t\"eps\": \"application/postscript\",\n    \"epub\": \"application/epub+zip\",\n\t\"erf\": \"image/x-epson-erf\",\n\t\"exe\": \"application/octet-stream\",\n\t\"exp\": \"image/x-exp\",\n\t\"exr\": \"image/x-exr\",\n\t\"fbx\": \"application/fbx\",\n\t\"fea\": \"application/vnd.apache.feather\",\n\t\"feather\": \"application/vnd.apache.feather\",\n\t\"fit\": \"image/fits\",\n\t\"fits\": \"image/fits\",\n\t\"fts\": \"image/fits\",\n\t\"flac\": \"audio/flac\",\n\t\"flv\": \"video/x-flv\",\n\t\"form\": \"application/x-form\",\n\t\"geojson\": \"application/geo+json\",\n\t\"gif\": \"image/gif\",\n\t\"gltf\": \"model/gltf+json\",\n\t\"glb\": \"model/gltf-binary\",\n\t\"gpx\": \"application/gpx+xml\",\n\t\"gp4\": \"application/x-guitar-pro\",\n\t\"gp5\": \"application/x-guitar-pro\",\n\t\"gz\": \"application/x-gzip\",\n\t\"h5\": \"application/x-hdf\",\n\t\"har\": \"application/har+json\",\n\t\"hdf5\": \"application/x-hdf\",\n\t\"heic\": \"image/heic\",\n\t\"heif\": \"image/heic\",\n\t\"hqx\": \"application/mac-binhex40\",\n\t\"htc\": \"text/x-component\",\n\t\"htm\": \"text/html\",\n\t\"html\": \"text/html\",\n\t\"hus\": \"image/x-hus\",\n\t\"ico\": \"image/x-icon\",\n\t\"ics\": \"text/calendar\",\n\t\"ifc\": \"application/ifc\",\n\t\"inf\": \"image/x-inf\",\n\t\"img\": \"application/octet-stream\",\n    \"ini\": \"text/x-ini\",\n\t\"iso\": \"application/octet-stream\",\n\t\"jad\": \"text/vnd.sun.j2me.app-descriptor\",\n\t\"jar\": \"application/java-archive\",\n\t\"jardiff\": \"application/x-java-archive-diff\",\n\t\"jef\": \"image/x-jef\",\n\t\"jef+\": \"image/x-jef\",\n\t\"jfif\": \"image/jpeg\",\n\t\"jng\": \"image/x-jng\",\n\t\"jnlp\": \"application/x-java-jnlp-file\",\n\t\"jpeg\": \"image/jpeg\",\n\t\"jpg\": \"image/jpeg\",\n\t\"jp2\": \"image/jp2\",\n\t\"js\": \"application/javascript\",\n\t\"json\": \"application/json\",\n\t\"kar\": \"audio/midi\",\n\t\"kdc\": \"image/x-kodak-kdc\",\n\t\"kicad_pcb\": \"application/vnd.kicad-pcb\",\n\t\"kicad_sch\": \"application/vnd.kicad-sch\",\n\t\"kml\": \"application/vnd.google-earth.kml+xml\",\n\t\"kmz\": \"application/vnd.google-earth.kmz\",\n\t\"ksm\": \"image/x-ksm\",\n\t\"ktx\": \"image/ktx\",\n\t\"ktx2\": \"image/ktx2\",\n\t\"m3u8\": \"application/vnd.apple.mpegurl\",\n\t\"m4a\": \"audio/x-m4a\",\n\t\"m4v\": \"video/x-m4v\",\n\t\"mae\": \"application/x-mae\",\n\t\"md\": \"text/markdown\",\n\t\"mdc\": \"image/x-minolta-mdc\",\n\t\"mef\": \"image/x-mamiya-mef\",\n\t\"mesh\": \"model/mesh\",\n\t\"mid\": \"audio/midi\",\n\t\"midi\": \"application/x-midi\",\n\t\"mjs\": \"application/javascript\",\n\t\"mkv\": \"video/x-matroska\",\n\t\"mml\": \"text/mathml\",\n\t\"mmtf\": \"application/x-mmtf\",\n\t\"mng\": \"video/x-mng\",\n\t\"mobi\": \"application/x-mobipocket-ebook\",\n\t\"mol\": \"application/x-mol\",\n\t\"mol2\": \"application/x-mol2\",\n\t\"mos\": \"image/x-aptus-mos\",\n\t\"mov\": \"video/quicktime\",\n\t\"mp3\": \"audio/mp3\",\n\t\"mp4\": \"video/mp4\",\n\t\"mpeg\": \"video/mpeg\",\n\t\"mpg\": \"video/mpeg\",\n\t\"mrw\": \"image/x-minolta-mrw\",\n\t\"msi\": \"application/octet-stream\",\n\t\"msm\": \"application/octet-stream\",\n\t\"msp\": \"application/octet-stream\",\n\t\"mtl\": \"model/mtl\",\n\t\"nc\": \"application/x-netcdf\",\n\t\"nef\": \"image/x-nikon-nef\",\n\t\"nrw\": \"image/x-nikon-nrw\",\n\t\"o\": \"application/x-object\",\n\t\"obj\": \"application/object\",\n\t\"odg\": \"application/vnd.oasis.opendocument.graphics\",\n\t\"odp\": \"application/vnd.oasis.opendocument.presentation\",\n\t\"ods\": \"application/vnd.oasis.opendocument.spreadsheet\",\n\t\"odt\": \"application/vnd.oasis.opendocument.text\",\n\t\"ogg\": \"audio/ogg\",\n\t\"ogv\": \"application/ogg\",\n\t\"orc\": \"application/vnd.apache.orc\",\n\t\"orf\": \"image/x-olympus-orf\",\n\t\"org\": \"text/org\",\n\t\"otf\": \"font/otf\",\n\t\"parquet\": \"application/vnd.apache.parquet\",\n\t\"pbm\": \"image/x-portable-bitmap\",\n\t\"pdb\": \"chemical/x-pdb\",\n\t\"pcap\": \"application/vnd.tcpdump.pcap\",\n\t\"pcapng\": \"application/x-pcapng\",\n\t\"pcd\": \"image/x-photo-cd\",\n\t\"pcm\": \"image/x-pcm\",\n\t\"pcs\": \"image/x-pcs\",\n\t\"pcx\": \"image/vnd.zbrush.pcx\",\n\t\"pdf\": \"application/pdf\",\n\t\"pec\": \"image/x-pec\",\n\t\"pes\": \"image/x-pes\",\n\t\"pef\": \"image/x-pentax-pef\",\n\t\"pem\": \"application/x-x509-ca-cert\",\n\t\"pes\": \"image/x-pes\",\n\t\"pgm\": \"image/x-portable-greymap\",\n\t\"pkg\": \"application/x-newton-compatible-pkg\",\n\t\"pl\": \"application/x-perl\",\n\t\"plantuml\": \"text/x-plantuml\",\n\t\"pm\": \"application/x-perl\",\n\t\"pmv\": \"image/x-pmv\",\n\t\"png\": \"image/png\",\n\t\"pnm\": \"image/x-portable-anymap\",\n\t\"potm\": \"application/vnd.ms-powerpoint.template.macroEnabled.12\",\n\t\"potx\": \"application/vnd.openxmlformats-officedocument.presentationml.template\",\n\t\"ppam\": \"application/vnd.ms-powerpoint.addin.macroEnabled.12\",\n\t\"ppm\": \"image/x-portable-pixmap\",\n\t\"pps\": \"application/vnd.ms-powerpoint\",\n\t\"ppsx\": \"application/vnd.openxmlformats-officedocument.presentationml.slideshow\",\n\t\"ppsm\": \"application/vnd.ms-powerpoint.slideshow.macroEnabled.12\",\n\t\"ppt\": \"application/vnd.ms-powerpoint\",\n\t\"pptm\": \"application/vnd.ms-powerpoint.presentation.macroEnabled.12\",\n\t\"pptx\": \"application/powerpoint\",\n\t\"prc\": \"application/x-pilot\",\n\t\"prj\": \"application/text\",\n\t\"properties\": \"text/x-ini\",\n\t\"ps\": \"application/postscript\",\n\t\"psd\": \"image/vnd.adobe.photoshop\",\n\t\"puml\": \"text/x-plantuml\",\n\t\"ra\": \"audio/x-realaudio\",\n\t\"raf\": \"image/x-fuji-raf\",\n\t\"ram\": \"audio/x-pn-realaudio\",\n\t\"rar\": \"application/x-rar-compressed\",\n\t\"raw\": \"image/x-raw\",\n\t\"rda\": \"application/x-rdata\",\n\t\"rdata\": \"application/x-rdata\",\n\t\"rds\": \"application/x-rds\",\n\t\"rgb\": \"image/x-rgb\",\n\t\"rgba\": \"image/x-rgba\",\n\t\"rpm\": \"application/x-redhat-package-manager\",\n\t\"rss\": \"application/rss+xml\",\n\t\"rtf\": \"application/rtf\",\n\t\"rtf2\": \"text/rtf\",\n\t\"run\": \"application/x-makeself\",\n\t\"rw2\": \"image/x-panasonic-rw2\",\n\t\"sam\": \"application/x-sam\",\n\t\"sdf\": \"application/x-sdf\",\n\t\"sea\": \"application/x-sea\",\n\t\"sew\": \"image/x-sew\",\n\t\"sgi\": \"image/x-sgi\",\n\t\"shtml\": \"text/html\",\n\t\"shp\": \"application/vnd.shp\",\n\t\"shv\": \"image/x-shv\",\n\t\"shx\": \"application/vnd.shx\",\n\t\"sit\": \"application/x-stuffit\",\n\t\"sketch\": \"application/x-sketch\",\n\t\"so\": \"application/x-sharedlib\",\n\t\"sql\": \"application/x-sqlite3\",\n\t\"sqlite\": \"application/x-sqlite3\",\n\t\"sqlite3\": \"application/x-sqlite3\",\n\t\"sr2\": \"image/x-sony-sr2\",\n\t\"srf\": \"image/x-sony-srf\",\n\t\"srw\": \"image/x-samsung-srw\",\n\t\"sst\": \"image/x-sst\",\n\t\"stl\": \"model/stl\",\n\t\"step\": \"model/step\",\n\t\"stp\": \"model/step\",\n\t\"svg\": \"image/svg+xml\",\n\t\"svgz\": \"image/svg+xml\",\n\t\"swf\": \"application/x-shockwave-flash\",\n\t\"tap\": \"image/x-tap\",\n\t\"tar\": \"application/x-tar\",\n\t\"tcl\": \"application/x-tcl\",\n\t\"tex\": \"application/x-tex\",\n\t\"tga\": \"image/x-tga\",\n\t\"tgz\": \"application/x-gzip\",\n\t\"tif\": \"image/tiff\",\n\t\"tiff\": \"image/tiff\",\n\t\"tk\": \"application/x-tcl\",\n\t\"ts\": \"text/plain\",\n\t\"tsv\": \"text/tab-separated-values\",\n\t\"ttf\": \"application/x-font-ttf\",\n\t\"txt\": \"text/plain\",\n\t\"u01\": \"image/x-u01\",\n\t\"url\": \"application/x-url\",\n\t\"vcf\": \"text/vcard\",\n\t\"vip\": \"image/x-vip\",\n\t\"vp3\": \"image/x-vp3\",\n\t\"vrml\": \"application/x-vrml\",\n\t\"war\": \"application/java-archive\",\n\t\"wav\": \"audio/wave\",\n\t\"wave\": \"audio/wave\",\n\t\"wasm\": \"application/wasm\",\n\t\"wbmp\": \"image/vnd.wap.wbmp\",\n\t\"webm\": \"video/webm\",\n\t\"webp\": \"image/webp\",\n\t\"wma\": \"audio/x-ms-wma\",\n\t\"wml\": \"text/vnd.wap.wml\",\n\t\"wmlc\": \"application/vnd.wap.wmlc\",\n\t\"wms\": \"application/vnd.ogc.wms_xml\",\n\t\"wmv\": \"video/x-ms-wmv\",\n\t\"woff\": \"font/woff\",\n\t\"woff2\": \"font/woff2\",\n\t\"wrl\": \"x-world/x-vrml\",\n\t\"x3d\": \"model/x3d+xml\",\n\t\"x3dv\": \"model/x3d-vrml\",\n\t\"x3db\": \"model/x3d+fastinfoset\",\n\t\"x3f\": \"image/x-x3f\",\n\t\"xbm\": \"image/x-xbitmap\",\n\t\"xcf\": \"image/x-xcf\",\n\t\"xd\": \"application/x-adobe-xd\",\n\t\"xhtml\": \"application/xhtml+xml\",\n\t\"xisf\": \"image/x-xisf\",\n\t\"xls\": \"application/vnd.ms-excel\",\n\t\"xlsx\": \"application/excel\",\n\t\"xml\": \"application/xml\",\n\t\"xpi\": \"application/x-xpinstall\",\n\t\"xpm\": \"image/x-xpixmap\",\n\t\"xspf\": \"application/xspf+xml\",\n\t\"xwd\": \"image/x-xwindowdump\",\n\t\"xxx\": \"image/x-xxx\",\n\t\"xyz\": \"application/x-xyz\",\n\t\"zip\": \"application/zip\"\n}\n"
  },
  {
    "path": "docker/Dockerfile",
    "content": "# STEP1: CLONE THE CODE\nFROM alpine/git as builder_prepare\nWORKDIR /home/\nARG GIT_REPO=https://github.com/mickael-kerjean/filestash\nARG GIT_BRANCH=master\nRUN git clone --depth 1 --single-branch --branch ${GIT_BRANCH} ${GIT_REPO}\n\n# STEP2: BUILD BACKEND\nFROM golang:1.24-trixie AS builder_backend\nWORKDIR /home/filestash/\nCOPY --from=builder_prepare /home/filestash/ .\nRUN apt-get update > /dev/null && \\\n    apt-get install -y curl make > /dev/null 2>&1 && \\\n    apt-get install -y libjpeg-dev libtiff-dev libpng-dev libwebp-dev libraw-dev libheif-dev libgif-dev libvips-dev > /dev/null 2>&1 && \\\n    make init && \\\n    make build && \\\n    mkdir -p ./dist/data/state/config/ && \\\n    cp config/config.json ./dist/data/state/config/config.json\n\n# STEP3: BUILD PROD IMAGE\nFROM debian:stable-slim\nMAINTAINER mickael@kerjean.me\nWORKDIR /app/\nCOPY --from=builder_backend /home/filestash/dist/ .\nRUN apt-get update > /dev/null && \\\n    apt-get install -y --no-install-recommends apt-utils && \\\n    apt-get install -y curl ffmpeg libjpeg-dev libtiff-dev libpng-dev libwebp-dev libraw-dev libheif-dev libgif-dev && \\\n    useradd filestash && \\\n    chown -R filestash:filestash /app/ && \\\n    find /app/data/ -type d -exec chmod 770 {} \\; && \\\n    find /app/data/ -type f -exec chmod 760 {} \\; && \\\n    chmod 730 /app/filestash && \\\n    rm -rf /var/lib/apt/lists/* && \\\n    rm -rf /tmp/*\n\nUSER filestash\nCMD [\"/app/filestash\"]\nEXPOSE 8334"
  },
  {
    "path": "docker/docker-compose.yml",
    "content": "version: '2'\nservices:\n  app:\n    container_name: filestash\n    image: machines/filestash:latest\n    restart: always\n    environment:\n    - APPLICATION_URL=\n    - CANARY=true\n    - OFFICE_URL=http://wopi_server:9980\n    - OFFICE_FILESTASH_URL=http://app:8334\n    - OFFICE_REWRITE_URL=http://127.0.0.1:9980\n    ports:\n    - \"8334:8334\"\n    volumes:\n    - filestash:/app/data/state/\n\n  wopi_server:\n    container_name: filestash_wopi\n    image: collabora/code:24.04.10.2.1\n    restart: always\n    environment:\n    - \"extra_params=--o:ssl.enable=false\"\n    - aliasgroup1=\"https://.*:443\"\n    command:\n    - /bin/bash\n    - -c\n    - |\n         curl -o /usr/share/coolwsd/browser/dist/branding-desktop.css https://gist.githubusercontent.com/mickael-kerjean/bc1f57cd312cf04731d30185cc4e7ba2/raw/d706dcdf23c21441e5af289d871b33defc2770ea/destop.css\n         /bin/su -s /bin/bash -c '/start-collabora-online.sh' cool\n    user: root\n    ports:\n    - \"9980:9980\"\n\nvolumes:\n    filestash: {}\n"
  },
  {
    "path": "embed.go",
    "content": "package embed\n\nimport (\n\t\"embed\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"os\"\n)\n\nvar (\n\t//go:embed public\n\twwwPublic embed.FS\n\tWWWPublic http.FileSystem = http.FS(os.DirFS(\"./public/\"))\n)\n\n//go:embed server/plugin/index.go\nvar EmbedPluginList []byte\n\nfunc init() {\n\tif os.Getenv(\"DEBUG\") != \"true\" {\n\t\tfsPublic, _ := fs.Sub(wwwPublic, \"public\")\n\t\tWWWPublic = http.FS(fsPublic)\n\t}\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/mickael-kerjean/filestash\n\ngo 1.24.11\n\nrequire (\n\tcloud.google.com/go/storage v1.59.0\n\tgithub.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4\n\tgithub.com/aws/aws-sdk-go v1.55.8\n\tgithub.com/bluekeyes/go-gitdiff v0.8.1\n\tgithub.com/bmatcuk/doublestar/v4 v4.9.2\n\tgithub.com/creack/pty v1.1.24\n\tgithub.com/cretz/bine v0.2.0\n\tgithub.com/duosecurity/duo_universal_golang v1.1.0\n\tgithub.com/fclairamb/ftpserverlib v0.30.0\n\tgithub.com/go-git/go-git/v6 v6.0.0-20251231065035-29ae690a9f19\n\tgithub.com/go-ldap/ldap/v3 v3.4.12\n\tgithub.com/go-redis/redis/v8 v8.11.5\n\tgithub.com/go-sql-driver/mysql v1.9.3\n\tgithub.com/godror/godror v0.50.0\n\tgithub.com/golang-jwt/jwt/v5 v5.3.0\n\tgithub.com/google/brotli/go/cbrotli v1.1.0\n\tgithub.com/google/uuid v1.6.0\n\tgithub.com/gorilla/mux v1.8.1\n\tgithub.com/gorilla/websocket v1.5.3\n\tgithub.com/h2non/bimg v1.1.9\n\tgithub.com/hirochachacha/go-smb2 v1.1.0\n\tgithub.com/lib/pq v1.10.9\n\tgithub.com/mattn/go-sqlite3 v1.14.33\n\tgithub.com/mickael-kerjean/net v0.0.0-20191120063050-2457c043ba06\n\tgithub.com/mickael-kerjean/saml v0.0.0-20240603162924-4629e91322ce\n\tgithub.com/mitchellh/hashstructure v1.1.0\n\tgithub.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646\n\tgithub.com/oschwald/maxminddb-golang/v2 v2.1.1\n\tgithub.com/patrickmn/go-cache v2.1.0+incompatible\n\tgithub.com/pkg/sftp v1.13.10\n\tgithub.com/pquerna/otp v1.5.0\n\tgithub.com/prasad83/goftp v0.0.0-20210325080443-f57aaed46a32\n\tgithub.com/qeesung/image2ascii v1.0.1\n\tgithub.com/spf13/afero v1.15.0\n\tgithub.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c\n\tgithub.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef\n\tgithub.com/stretchr/testify v1.11.1\n\tgithub.com/suyashkumar/dicom v1.1.0\n\tgithub.com/tetratelabs/wazero v1.11.0\n\tgithub.com/tidwall/gjson v1.18.0\n\tgithub.com/tidwall/sjson v1.2.5\n\tgithub.com/vmware/go-nfs-client v0.0.0-20190605212624-d43b92724c1b\n\tgithub.com/yeqown/go-qrcode/v2 v2.2.5\n\tgithub.com/yeqown/go-qrcode/writer/standard v1.3.0\n\tgolang.org/x/crypto v0.47.0\n\tgolang.org/x/image v0.35.0\n\tgolang.org/x/net v0.49.0\n\tgolang.org/x/oauth2 v0.34.0\n\tgolang.org/x/sync v0.19.0\n\tgolang.org/x/sys v0.40.0\n\tgolang.org/x/time v0.14.0\n\tgoogle.golang.org/api v0.259.0\n\tgopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df\n\tmodernc.org/sqlite v1.44.3\n\tstorj.io/uplink v1.13.1\n)\n\nrequire (\n\tcel.dev/expr v0.25.1 // indirect\n\tcloud.google.com/go v0.123.0 // indirect\n\tcloud.google.com/go/auth v0.18.0 // indirect\n\tcloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect\n\tcloud.google.com/go/compute/metadata v0.9.0 // indirect\n\tcloud.google.com/go/iam v1.5.3 // indirect\n\tcloud.google.com/go/monitoring v1.24.3 // indirect\n\tfilippo.io/edwards25519 v1.1.0 // indirect\n\tgithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 // indirect\n\tgithub.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect\n\tgithub.com/Azure/go-ntlmssp v0.1.0 // indirect\n\tgithub.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect\n\tgithub.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 // indirect\n\tgithub.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 // indirect\n\tgithub.com/Microsoft/go-winio v0.6.2 // indirect\n\tgithub.com/ProtonMail/go-crypto v1.3.0 // indirect\n\tgithub.com/VictoriaMetrics/easyproto v1.1.3 // indirect\n\tgithub.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect\n\tgithub.com/beevik/etree v1.6.0 // indirect\n\tgithub.com/boombuler/barcode v1.1.0 // indirect\n\tgithub.com/calebcase/tmpfile v1.0.3 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/cloudflare/circl v1.6.2 // indirect\n\tgithub.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect\n\tgithub.com/crewjam/httperr v0.2.0 // indirect\n\tgithub.com/cyphar/filepath-securejoin v0.6.1 // indirect\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect\n\tgithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect\n\tgithub.com/dustin/go-humanize v1.0.1 // indirect\n\tgithub.com/emirpasic/gods v1.18.1 // indirect\n\tgithub.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect\n\tgithub.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n\tgithub.com/flynn/noise v1.1.0 // indirect\n\tgithub.com/fogleman/gg v1.3.0 // indirect\n\tgithub.com/geoffgarside/ber v1.2.0 // indirect\n\tgithub.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect\n\tgithub.com/go-git/gcfg/v2 v2.0.2 // indirect\n\tgithub.com/go-git/go-billy/v6 v6.0.0-20251217170237-e9738f50a3cd // indirect\n\tgithub.com/go-jose/go-jose/v4 v4.1.3 // indirect\n\tgithub.com/go-logfmt/logfmt v0.6.1 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/goccy/go-json v0.10.5 // indirect\n\tgithub.com/godror/knownpb v0.3.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang-jwt/jwt/v4 v4.5.2 // indirect\n\tgithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect\n\tgithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect\n\tgithub.com/google/s2a-go v0.1.9 // indirect\n\tgithub.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect\n\tgithub.com/googleapis/gax-go/v2 v2.16.0 // indirect\n\tgithub.com/jmespath/go-jmespath v0.4.0 // indirect\n\tgithub.com/jonboulle/clockwork v0.5.0 // indirect\n\tgithub.com/jtolio/noiseconn v0.0.0-20231127013910-f6d9ecbf1de7 // indirect\n\tgithub.com/kevinburke/ssh_config v1.4.0 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.3.0 // indirect\n\tgithub.com/kr/fs v0.1.0 // indirect\n\tgithub.com/lestrrat-go/backoff/v2 v2.0.8 // indirect\n\tgithub.com/lestrrat-go/blackmagic v1.0.4 // indirect\n\tgithub.com/lestrrat-go/httpcc v1.0.1 // indirect\n\tgithub.com/lestrrat-go/iter v1.0.2 // indirect\n\tgithub.com/lestrrat-go/jwx v1.2.31 // indirect\n\tgithub.com/lestrrat-go/option v1.0.1 // indirect\n\tgithub.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/ncruces/go-strftime v1.0.0 // indirect\n\tgithub.com/pjbgf/sha1cd v0.5.0 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect\n\tgithub.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93 // indirect\n\tgithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect\n\tgithub.com/russellhaering/goxmldsig v1.5.0 // indirect\n\tgithub.com/sergi/go-diff v1.4.0 // indirect\n\tgithub.com/spacemonkeygo/monkit/v3 v3.0.25-0.20251022131615-eb24eb109368 // indirect\n\tgithub.com/spiffe/go-spiffe/v2 v2.6.0 // indirect\n\tgithub.com/tidwall/match v1.2.0 // indirect\n\tgithub.com/tidwall/pretty v1.2.1 // indirect\n\tgithub.com/wayneashleyberry/terminal-dimensions v1.1.0 // indirect\n\tgithub.com/yeqown/reedsolomon v1.0.0 // indirect\n\tgithub.com/zeebo/blake3 v0.2.4 // indirect\n\tgithub.com/zeebo/errs v1.4.0 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.2.1 // indirect\n\tgo.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect\n\tgo.opentelemetry.io/otel v1.39.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.39.0 // indirect\n\tgo.opentelemetry.io/otel/sdk v1.39.0 // indirect\n\tgo.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.39.0 // indirect\n\tgolang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20260112192933-99fd39fd28a9 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20260112192933-99fd39fd28a9 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20260112192933-99fd39fd28a9 // indirect\n\tgoogle.golang.org/grpc v1.78.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tmodernc.org/libc v1.67.6 // indirect\n\tmodernc.org/mathutil v1.7.1 // indirect\n\tmodernc.org/memory v1.11.0 // indirect\n\tstorj.io/common v0.0.0-20260109131222-221fe378eda1 // indirect\n\tstorj.io/drpc v0.0.35-0.20250513201419-f7819ea69b55 // indirect\n\tstorj.io/eventkit v0.0.0-20250410172343-61f26d3de156 // indirect\n\tstorj.io/infectious v0.0.2 // indirect\n\tstorj.io/picobuf v0.0.4 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=\ncel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=\ncloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=\ncloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=\ncloud.google.com/go/auth v0.18.0 h1:wnqy5hrv7p3k7cShwAU/Br3nzod7fxoqG+k0VZ+/Pk0=\ncloud.google.com/go/auth v0.18.0/go.mod h1:wwkPM1AgE1f2u6dG443MiWoD8C3BtOywNsUMcUTVDRo=\ncloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=\ncloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=\ncloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=\ncloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=\ncloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=\ncloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=\ncloud.google.com/go/logging v1.13.1 h1:O7LvmO0kGLaHY/gq8cV7T0dyp6zJhYAOtZPX4TF3QtY=\ncloud.google.com/go/logging v1.13.1/go.mod h1:XAQkfkMBxQRjQek96WLPNze7vsOmay9H5PqfsNYDqvw=\ncloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8=\ncloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk=\ncloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE=\ncloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI=\ncloud.google.com/go/storage v1.59.0 h1:9p3yDzEN9Vet4JnbN90FECIw6n4FCXcKBK1scxtQnw8=\ncloud.google.com/go/storage v1.59.0/go.mod h1:cMWbtM+anpC74gn6qjLh+exqYcfmB9Hqe5z6adx+CLI=\ncloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U=\ncloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s=\nfilippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=\nfilippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA=\ngithub.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 h1:jWQK1GI+LeGGUKBADtcH2rRqPxYB1Ljwms5gFA2LqrM=\ngithub.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4/go.mod h1:8mwH4klAm9DUgR2EEHyEEAQlRDvLPyg5fQry3y+cDew=\ngithub.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A=\ngithub.com/Azure/go-ntlmssp v0.1.0/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 h1:lhhYARPUu3LmHysQ/igznQphfzynnqI3D75oUyw1HXk=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0/go.mod h1:l9rva3ApbBpEJxSNYnwT9N4CDLrWgtq3u8736C5hyJw=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.54.0 h1:xfK3bbi6F2RDtaZFtUdKO3osOBIhNb+xTs8lFW6yx9o=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.54.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 h1:s0WlVbf9qpvkh1c/uDAPElam0WrL7fHRIidgZJ7UqZI=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc=\ngithub.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=\ngithub.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=\ngithub.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=\ngithub.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=\ngithub.com/UNO-SOFT/zlog v0.8.1 h1:TEFkGJHtUfTRgMkLZiAjLSHALjwSBdw6/zByMC5GJt4=\ngithub.com/UNO-SOFT/zlog v0.8.1/go.mod h1:yqFOjn3OhvJ4j7ArJqQNA+9V+u6t9zSAyIZdWdMweWc=\ngithub.com/VictoriaMetrics/easyproto v1.1.3 h1:gRSA3ZQs7n4+5I+SniDWD59jde1jVq4JmgQ9HUUyvk4=\ngithub.com/VictoriaMetrics/easyproto v1.1.3/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710=\ngithub.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=\ngithub.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=\ngithub.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=\ngithub.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=\ngithub.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=\ngithub.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=\ngithub.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo=\ngithub.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=\ngithub.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=\ngithub.com/beevik/etree v1.4.0/go.mod h1:cyWiXwGoasx60gHvtnEh5x8+uIjUVnjWqBvEnhnqKDA=\ngithub.com/beevik/etree v1.6.0 h1:u8Kwy8pp9D9XeITj2Z0XtA5qqZEmtJtuXZRQi+j03eE=\ngithub.com/beevik/etree v1.6.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc=\ngithub.com/bluekeyes/go-gitdiff v0.8.1 h1:lL1GofKMywO17c0lgQmJYcKek5+s8X6tXVNOLxy4smI=\ngithub.com/bluekeyes/go-gitdiff v0.8.1/go.mod h1:WWAk1Mc6EgWarCrPFO+xeYlujPu98VuLW3Tu+B/85AE=\ngithub.com/bmatcuk/doublestar/v4 v4.9.2 h1:b0mc6WyRSYLjzofB2v/0cuDUZ+MqoGyH3r0dVij35GI=\ngithub.com/bmatcuk/doublestar/v4 v4.9.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=\ngithub.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=\ngithub.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=\ngithub.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=\ngithub.com/calebcase/tmpfile v1.0.3 h1:BZrOWZ79gJqQ3XbAQlihYZf/YCV0H4KPIdM5K5oMpJo=\ngithub.com/calebcase/tmpfile v1.0.3/go.mod h1:UAUc01aHeC+pudPagY/lWvt2qS9ZO5Zzof6/tIUzqeI=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ=\ngithub.com/cloudflare/circl v1.6.2/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=\ngithub.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=\ngithub.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=\ngithub.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=\ngithub.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=\ngithub.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=\ngithub.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo=\ngithub.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=\ngithub.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=\ngithub.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=\ngithub.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=\ngithub.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=\ngithub.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=\ngithub.com/dsnet/try v0.0.3 h1:ptR59SsrcFUYbT/FhAbKTV6iLkeD6O18qfIWRml2fqI=\ngithub.com/dsnet/try v0.0.3/go.mod h1:WBM8tRpUmnXXhY1U6/S8dt6UWdHTQ7y8A5YSkRCkq40=\ngithub.com/duosecurity/duo_universal_golang v1.1.0 h1:GaCc3vDktv3IEA+KPrHFnKqZjaKhTKjUpaGajL2SUSc=\ngithub.com/duosecurity/duo_universal_golang v1.1.0/go.mod h1:AxndDwaPp4DGZH3Rmq8Q6RkyE95tFF4nK4LXgpBAgFs=\ngithub.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\ngithub.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=\ngithub.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=\ngithub.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM=\ngithub.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329/go.mod h1:Alz8LEClvR7xKsrq3qzoc4N0guvVNSS8KmSChGYr9hs=\ngithub.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=\ngithub.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=\ngithub.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=\ngithub.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=\ngithub.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=\ngithub.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=\ngithub.com/fclairamb/ftpserverlib v0.30.0 h1:caB9sDn1Au//q0j2ev/icPn388qPuk4k1ajSvglDcMQ=\ngithub.com/fclairamb/ftpserverlib v0.30.0/go.mod h1:QmogtltTOgkihyKza0GNo37Mu4AEzbJ+sH6W9Y0MBIQ=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=\ngithub.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=\ngithub.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=\ngithub.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=\ngithub.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=\ngithub.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=\ngithub.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=\ngithub.com/geoffgarside/ber v1.2.0 h1:/loowoRcs/MWLYmGX9QtIAbA+V/FrnVLsMMPhwiRm64=\ngithub.com/geoffgarside/ber v1.2.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=\ngithub.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=\ngithub.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=\ngithub.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=\ngithub.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=\ngithub.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo=\ngithub.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs=\ngithub.com/go-git/go-billy/v6 v6.0.0-20251217170237-e9738f50a3cd h1:Gd/f9cGi/3h1JOPaa6er+CkKUGyGX2DBJdFbDKVO+R0=\ngithub.com/go-git/go-billy/v6 v6.0.0-20251217170237-e9738f50a3cd/go.mod h1:d3XQcsHu1idnquxt48kAv+h+1MUiYKLH/e7LAzjP+pI=\ngithub.com/go-git/go-git-fixtures/v5 v5.1.2-0.20251229094738-4b14af179146 h1:xYfxAopYyL44ot6dMBIb1Z1njFM0ZBQ99HdIB99KxLs=\ngithub.com/go-git/go-git-fixtures/v5 v5.1.2-0.20251229094738-4b14af179146/go.mod h1:QE/75B8tBSLNGyUUbA9tw3EGHoFtYOtypa2h8YJxsWI=\ngithub.com/go-git/go-git/v6 v6.0.0-20251231065035-29ae690a9f19 h1:0lz2eJScP8v5YZQsrEw+ggWC5jNySjg4bIZo5BIh6iI=\ngithub.com/go-git/go-git/v6 v6.0.0-20251231065035-29ae690a9f19/go.mod h1:L+Evfcs7EdTqxwv854354cb6+++7TFL3hJn3Wy4g+3w=\ngithub.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=\ngithub.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=\ngithub.com/go-ldap/ldap/v3 v3.4.12 h1:1b81mv7MagXZ7+1r7cLTWmyuTqVqdwbtJSjC0DAp9s4=\ngithub.com/go-ldap/ldap/v3 v3.4.12/go.mod h1:+SPAGcTtOfmGsCb3h1RFiq4xpp4N636G75OEace8lNo=\ngithub.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=\ngithub.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=\ngithub.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=\ngithub.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=\ngithub.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=\ngithub.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=\ngithub.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=\ngithub.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=\ngithub.com/godror/godror v0.50.0 h1:c0ZnGSDFT12E8HJfQwxtqcmybaIkbqACNk4lIfkkESc=\ngithub.com/godror/godror v0.50.0/go.mod h1:kTMcxZzRw73RT5kn9v3JkBK4kHI6dqowHotqV72ebU8=\ngithub.com/godror/knownpb v0.3.0 h1:+caUdy8hTtl7X05aPl3tdL540TvCcaQA6woZQroLZMw=\ngithub.com/godror/knownpb v0.3.0/go.mod h1:PpTyfJwiOEAzQl7NtVCM8kdPCnp3uhxsZYIzZ5PV4zU=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\ngithub.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=\ngithub.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\ngithub.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=\ngithub.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=\ngithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=\ngithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=\ngithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=\ngithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/brotli/go/cbrotli v1.1.0 h1:YwHD/rwSgUSL4b2S3ZM2jnNymm+tmwKQqjUIC63nmHU=\ngithub.com/google/brotli/go/cbrotli v1.1.0/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s=\ngithub.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=\ngithub.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=\ngithub.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=\ngithub.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=\ngithub.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=\ngithub.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao=\ngithub.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8=\ngithub.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y=\ngithub.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14=\ngithub.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=\ngithub.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/h2non/bimg v1.1.9 h1:WH20Nxko9l/HFm4kZCA3Phbgu2cbHvYzxwxn9YROEGg=\ngithub.com/h2non/bimg v1.1.9/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8=\ngithub.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=\ngithub.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=\ngithub.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI=\ngithub.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=\ngithub.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=\ngithub.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=\ngithub.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=\ngithub.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=\ngithub.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=\ngithub.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=\ngithub.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=\ngithub.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=\ngithub.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=\ngithub.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=\ngithub.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=\ngithub.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=\ngithub.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=\ngithub.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=\ngithub.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=\ngithub.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=\ngithub.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=\ngithub.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=\ngithub.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=\ngithub.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=\ngithub.com/jtolio/noiseconn v0.0.0-20231127013910-f6d9ecbf1de7 h1:JcltaO1HXM5S2KYOYcKgAV7slU0xPy1OcvrVgn98sRQ=\ngithub.com/jtolio/noiseconn v0.0.0-20231127013910-f6d9ecbf1de7/go.mod h1:MEkhEPFwP3yudWO0lj6vfYpLIB+3eIcuIW+e0AZzUQk=\ngithub.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=\ngithub.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=\ngithub.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=\ngithub.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=\ngithub.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=\ngithub.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=\ngithub.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=\ngithub.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=\ngithub.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=\ngithub.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=\ngithub.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=\ngithub.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=\ngithub.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=\ngithub.com/lestrrat-go/jwx v1.2.29/go.mod h1:hU8k2l6WF0ncx20uQdOmik/Gjg6E3/wIRtXSNFeZuB8=\ngithub.com/lestrrat-go/jwx v1.2.31 h1:/OM9oNl/fzyldpv5HKZ9m7bTywa7COUfg8gujd9nJ54=\ngithub.com/lestrrat-go/jwx v1.2.31/go.mod h1:eQJKoRwWcLg4PfD5CFA5gIZGxhPgoPYq9pZISdxLf0c=\ngithub.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=\ngithub.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=\ngithub.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=\ngithub.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=\ngithub.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=\ngithub.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0=\ngithub.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=\ngithub.com/mickael-kerjean/net v0.0.0-20191120063050-2457c043ba06 h1:427Mpu5edwuFuDBdqOm6EgRohYqZ9Oomd+87ryW2Uls=\ngithub.com/mickael-kerjean/net v0.0.0-20191120063050-2457c043ba06/go.mod h1:K5atEXcXMSGE5O2cPGjggOqCb5lKRaieqgNJHEwS/IE=\ngithub.com/mickael-kerjean/saml v0.0.0-20240603162924-4629e91322ce h1:FVTh1LDCRr6fYm3meiJFIZcsrO29WYDLPIT+BwTiyYQ=\ngithub.com/mickael-kerjean/saml v0.0.0-20240603162924-4629e91322ce/go.mod h1:at9E70cOrnu43GZqrHni63ifAI+3QHzzSQqqUHCiwoQ=\ngithub.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0=\ngithub.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA=\ngithub.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=\ngithub.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=\ngithub.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=\ngithub.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=\ngithub.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=\ngithub.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=\ngithub.com/oklog/ulid/v2 v2.0.2 h1:r4fFzBm+bv0wNKNh5eXTwU7i85y5x+uwkxCUTNVQqLc=\ngithub.com/oklog/ulid/v2 v2.0.2/go.mod h1:mtBL0Qe/0HAx6/a4Z30qxVIAL1eQDweXq5lxOEiwQ68=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=\ngithub.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=\ngithub.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=\ngithub.com/oschwald/maxminddb-golang/v2 v2.1.1 h1:lA8FH0oOrM4u7mLvowq8IT6a3Q/qEnqRzLQn9eH5ojc=\ngithub.com/oschwald/maxminddb-golang/v2 v2.1.1/go.mod h1:PLdx6PR+siSIoXqqy7C7r3SB3KZnhxWr1Dp6g0Hacl8=\ngithub.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=\ngithub.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=\ngithub.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=\ngithub.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=\ngithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=\ngithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=\ngithub.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=\ngithub.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=\ngithub.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=\ngithub.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=\ngithub.com/prasad83/goftp v0.0.0-20210325080443-f57aaed46a32 h1:M5NgckSuVabJL6XfuBYclDbFu/PnrryRQyreGkQuims=\ngithub.com/prasad83/goftp v0.0.0-20210325080443-f57aaed46a32/go.mod h1:WkmqX+l/lW8boCoanBDTyrSWqrIvwP7NYm/zVT15Da0=\ngithub.com/qeesung/image2ascii v1.0.1 h1:Fe5zTnX/v/qNC3OC4P/cfASOXS501Xyw2UUcgrLgtp4=\ngithub.com/qeesung/image2ascii v1.0.1/go.mod h1:kZKhyX0h2g/YXa/zdJR3JnLnJ8avHjZ3LrvEKSYyAyU=\ngithub.com/quic-go/quic-go v0.53.0 h1:QHX46sISpG2S03dPeZBgVIZp8dGagIaiu2FiVYvpCZI=\ngithub.com/quic-go/quic-go v0.53.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=\ngithub.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93 h1:UVArwN/wkKjMVhh2EQGC0tEc1+FqiLlvYXY5mQ2f8Wg=\ngithub.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93/go.mod h1:Nfe4efndBz4TibWycNE+lqyJZiMX4ycx+QKV8Ta0f/o=\ngithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=\ngithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=\ngithub.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/russellhaering/goxmldsig v1.4.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=\ngithub.com/russellhaering/goxmldsig v1.5.0 h1:AU2UkkYIUOTyZRbe08XMThaOCelArgvNfYapcmSjBNw=\ngithub.com/russellhaering/goxmldsig v1.5.0/go.mod h1:x98CjQNFJcWfMxeOrMnMKg70lvDP6tE0nTaeUnjXDmk=\ngithub.com/secsy/goftp v0.0.0-20200609142545-aa2de14babf4 h1:PT+ElG/UUFMfqy5HrxJxNzj3QBOf7dZwupeVC+mG1Lo=\ngithub.com/secsy/goftp v0.0.0-20200609142545-aa2de14babf4/go.mod h1:MnkX001NG75g3p8bhFycnyIjeQoOjGL6CEIsdE/nKSY=\ngithub.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=\ngithub.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=\ngithub.com/spacemonkeygo/monkit/v3 v3.0.25-0.20251022131615-eb24eb109368 h1:GyYC5Ntqk/yy9lEIGE7chdIvt4zP44taycwd9YDSGdc=\ngithub.com/spacemonkeygo/monkit/v3 v3.0.25-0.20251022131615-eb24eb109368/go.mod h1:XkZYGzknZwkD0AKUnZaSXhRiVTLCkq7CWVa3IsE72gA=\ngithub.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=\ngithub.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=\ngithub.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=\ngithub.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=\ngithub.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=\ngithub.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=\ngithub.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=\ngithub.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/suyashkumar/dicom v1.1.0 h1:AG+N/aQnD+jzkFuFzz2wO401qXI8KnNcYGQgvTBr9LA=\ngithub.com/suyashkumar/dicom v1.1.0/go.mod h1:8Yw14x/0r4fXVnutbCJpF3HiLVbgMS1DQ2HpfbDjq8Y=\ngithub.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=\ngithub.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=\ngithub.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=\ngithub.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=\ngithub.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=\ngithub.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=\ngithub.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=\ngithub.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=\ngithub.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=\ngithub.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=\ngithub.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=\ngithub.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=\ngithub.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=\ngithub.com/vmware/go-nfs-client v0.0.0-20190605212624-d43b92724c1b h1:RUrsc0B9xF8iC8WXrva+ULeOwN/X+zqe0FdWcDxPt/M=\ngithub.com/vmware/go-nfs-client v0.0.0-20190605212624-d43b92724c1b/go.mod h1:psQdhrCc+fimC/8/U+PboPiIMcdmKgRdAtcMnhXhjzI=\ngithub.com/wayneashleyberry/terminal-dimensions v1.1.0 h1:EB7cIzBdsOzAgmhTUtTTQXBByuPheP/Zv1zL2BRPY6g=\ngithub.com/wayneashleyberry/terminal-dimensions v1.1.0/go.mod h1:2lc/0eWCObmhRczn2SdGSQtgBooLUzIotkkEGXqghyg=\ngithub.com/yeqown/go-qrcode/v2 v2.2.5 h1:HCOe2bSjkhZyYoyyNaXNzh4DJZll6inVJQQw+8228Zk=\ngithub.com/yeqown/go-qrcode/v2 v2.2.5/go.mod h1:uHpt9CM0V1HeXLz+Wg5MN50/sI/fQhfkZlOM+cOTHxw=\ngithub.com/yeqown/go-qrcode/writer/standard v1.3.0 h1:chdyhEfRtUPgQtuPeaWVGQ/TQx4rE1PqeoW3U+53t34=\ngithub.com/yeqown/go-qrcode/writer/standard v1.3.0/go.mod h1:O4MbzsotGCvy8upYPCR91j81dr5XLT7heuljcNXW+oQ=\ngithub.com/yeqown/reedsolomon v1.0.0 h1:x1h/Ej/uJnNu8jaX7GLHBWmZKCAWjEJTetkqaabr4B0=\ngithub.com/yeqown/reedsolomon v1.0.0/go.mod h1:P76zpcn2TCuL0ul1Fso373qHRc69LKwAw/Iy6g1WiiM=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngithub.com/zeebo/assert v1.3.1 h1:vukIABvugfNMZMQO1ABsyQDJDTVQbn+LWSMy1ol1h6A=\ngithub.com/zeebo/assert v1.3.1/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=\ngithub.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=\ngithub.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=\ngithub.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=\ngithub.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=\ngithub.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=\ngithub.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=\ngo.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=\ngo.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=\ngo.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE=\ngo.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 h1:RN3ifU8y4prNWeEnQp2kRRHz8UwonAEYZl8tUzHEXAk=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0/go.mod h1:habDz3tEWiFANTo6oUE99EmaFUrCNYAAg3wiVmusm70=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ=\ngo.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=\ngo.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=\ngo.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0 h1:wm/Q0GAAykXv83wzcKzGGqAnnfLFyFe7RslekZuv+VI=\ngo.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0/go.mod h1:ra3Pa40+oKjvYh+ZD3EdxFZZB0xdMfuileHAm4nNN7w=\ngo.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=\ngo.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=\ngo.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=\ngo.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=\ngo.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=\ngo.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=\ngo.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=\ngo.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=\ngo.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=\ngo.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=\ngolang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=\ngolang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=\ngolang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=\ngolang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I=\ngolang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=\ngolang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=\ngolang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=\ngolang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=\ngolang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=\ngolang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=\ngolang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=\ngolang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=\ngolang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=\ngolang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=\ngolang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=\ngonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=\ngoogle.golang.org/api v0.259.0 h1:90TaGVIxScrh1Vn/XI2426kRpBqHwWIzVBzJsVZ5XrQ=\ngoogle.golang.org/api v0.259.0/go.mod h1:LC2ISWGWbRoyQVpxGntWwLWN/vLNxxKBK9KuJRI8Te4=\ngoogle.golang.org/genproto v0.0.0-20260112192933-99fd39fd28a9 h1:wFALHMUiWKkK/x6rSxm79KpSnUyh7ks2E+mel670Dc4=\ngoogle.golang.org/genproto v0.0.0-20260112192933-99fd39fd28a9/go.mod h1:wE6SUYr3iNtF/D0GxVAjT+0CbDFktQNssYs9PVptCt4=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20260112192933-99fd39fd28a9 h1:4DKBrmaqeptdEzp21EfrOEh8LE7PJ5ywH6wydSbOfGY=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20260112192933-99fd39fd28a9/go.mod h1:dd646eSK+Dk9kxVBl1nChEOhJPtMXriCcVb4x3o6J+E=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20260112192933-99fd39fd28a9 h1:IY6/YYRrFUk0JPp0xOVctvFIVuRnjccihY5kxf5g0TE=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20260112192933-99fd39fd28a9/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=\ngoogle.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=\ngoogle.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=\ngopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=\ngopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=\ngotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=\nmodernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=\nmodernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=\nmodernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=\nmodernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=\nmodernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=\nmodernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=\nmodernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=\nmodernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=\nmodernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=\nmodernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=\nmodernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=\nmodernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=\nmodernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=\nmodernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=\nmodernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=\nmodernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=\nmodernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=\nmodernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=\nmodernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=\nmodernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=\nmodernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=\nmodernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=\nmodernc.org/sqlite v1.44.3 h1:+39JvV/HWMcYslAwRxHb8067w+2zowvFOUrOWIy9PjY=\nmodernc.org/sqlite v1.44.3/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=\nmodernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=\nmodernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=\nmodernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=\nmodernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=\nstorj.io/common v0.0.0-20260109131222-221fe378eda1 h1:lcjItKLOyqtzAjnQTh+k/x+5la2Ua5QuHy4EM7ZMIHU=\nstorj.io/common v0.0.0-20260109131222-221fe378eda1/go.mod h1:XNX7uykja6aco92y2y8RuqaXIDRPpt1YA2OQDKlKEUk=\nstorj.io/drpc v0.0.35-0.20250513201419-f7819ea69b55 h1:8OE12DvUnB9lfZcHe7IDGsuhjrY9GBAr964PVHmhsro=\nstorj.io/drpc v0.0.35-0.20250513201419-f7819ea69b55/go.mod h1:Y9LZaa8esL1PW2IDMqJE7CFSNq7d5bQ3RI7mGPtmKMg=\nstorj.io/eventkit v0.0.0-20250410172343-61f26d3de156 h1:5MZ0CyMbG6Pi0rRzUWVG6dvpXjbBYEX2oyXuj+tT+sk=\nstorj.io/eventkit v0.0.0-20250410172343-61f26d3de156/go.mod h1:CpnM6kfZV58dcq3lpbo/IQ4/KoutarnTSHY0GYVwnYw=\nstorj.io/infectious v0.0.2 h1:rGIdDC/6gNYAStsxsZU79D/MqFjNyJc1tsyyj9sTl7Q=\nstorj.io/infectious v0.0.2/go.mod h1:QEjKKww28Sjl1x8iDsjBpOM4r1Yp8RsowNcItsZJ1Vs=\nstorj.io/picobuf v0.0.4 h1:qswHDla+YZ2TovGtMnU4astjvrADSIz84FXRn0qgP6o=\nstorj.io/picobuf v0.0.4/go.mod h1:hSMxmZc58MS/2qSLy1I0idovlO7+6K47wIGUyRZa6mg=\nstorj.io/uplink v1.13.1 h1:C8RdW/upALoCyuF16Lod9XGCXEdbJAS+ABQy9JO/0pA=\nstorj.io/uplink v1.13.1/go.mod h1:x0MQr4UfFsQBwgVWZAtEsLpuwAn6dg7G0Mpne1r516E=\n"
  },
  {
    "path": "public/Makefile",
    "content": "compress:\n\tfind . -type f -name '*.html' | xargs brotli -f -k\n\tfind . -type f -name '*.html' | xargs gzip -f -k\n\tfind . -type f -name '*.js' | xargs brotli -f -k\n\tfind . -type f -name '*.js' | xargs gzip -f -k\n\tfind . -type f -name '*.css' | xargs brotli -f -k\n\tfind . -type f -name '*.css' | xargs gzip -f -k\n\tfind . -type f -name '*.svg' | xargs brotli -f -k\n\tfind . -type f -name '*.svg' | xargs gzip -f -k\n\nclean:\n\tfind . -name '*.gz' -exec rm {} \\;\n\tfind . -name '*.br' -exec rm {} \\;\n\nserve:\n\tgo run server.go\n"
  },
  {
    "path": "public/assets/boot/bundler_complete.js",
    "content": "document.head.appendChild(Object.assign(document.createElement(\"script\"), {\n    type: \"importmap\",\n    textContent: JSON.stringify({\n        imports: window.bundler.esModules,\n    }, null, 4),\n}));\n"
  },
  {
    "path": "public/assets/boot/bundler_init.js",
    "content": "window.bundler = (function(origin) {\n    const esModules = {};\n    return {\n        register: (path, code) => {\n            const fullpath = origin + path;\n            if (path.endsWith(\".js\")) {\n                code = code.replace(/from\\s?\"([^\"]+)\"/g, (_, spec) =>\n                    `from \"${new URL(spec, fullpath).href}\"`,\n                );\n                code = code.replace(/\\bimport\\s+\"([^\"]+)\"/g, (_, spec) =>\n                    `import \"${new URL(spec, fullpath).href}\"`,\n                );\n                code = code.replace(\n                    /\\bimport\\.meta\\.url\\b/g,\n                    (match, offset, string) => {\n                        const before = string[offset - 1];\n                        const after = string[offset + match.length];\n                        return (before === \"\\\"\" || after === \"\\\"\") ? match : `\"${fullpath}\"`;\n                    },\n                );\n                esModules[fullpath] = \"data:text/javascript,\" + encodeURIComponent(\n                    code + `\\n//# sourceURL=${path}`,\n                );\n            } else if (path.endsWith(\".css\")) {\n                code = code.replace(/@import url\\(\"([^\"]+)\"\\);/g, (m, rel) => {\n                    const $style = document.head.querySelector(\n                        `style[id=\"${new URL(rel, fullpath).href}\"]`\n                    );\n                    if (!$style) throw new DOMException(\n                        `Missing CSS dependency: ${rel} (referenced from ${path})`,\n                        \"NotFoundError\",\n                    );\n                    return `/* ${m} */`;\n                });\n                document.head.appendChild(Object.assign(document.createElement(\"style\"), {\n                    innerHTML: code + `\\n/*# sourceURL=${path} */`,\n                    id: fullpath,\n                }));\n            }\n        },\n        esModules,\n    };\n})(new URL(import.meta.url).origin);\n"
  },
  {
    "path": "public/assets/boot/common.js",
    "content": "export function $error(msg) {\n    const $code = document.createElement(\"code\");\n    $code.style.display = \"block\";\n    $code.style.margin = \"20px 0\";\n    $code.style.fontSize = \"1.2rem\";\n    $code.style.padding = \"0 10% 0 10%\";\n    $code.textContent = msg;\n\n    const $img = document.createElement(\"img\");\n    $img.setAttribute(\"src\", \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABQAQMAAADcLOLWAAAABlBMVEUAAABTU1OoaSf/AAAAAXRSTlMAQObYZgAAAFlJREFUeF69zrERgCAQBdElMqQEOtHSuNIohRIMjfjO6DDmB7jZy5YgySQVYDIakIHD1kBPC9Bra5G2Ans0N7iAcOLF+EHvXySpjSBWCDI/3nIdBDihr8m4AcKdbn96jpAHAAAAAElFTkSuQmCC\");\n    $img.style.display = \"block\";\n    $img.style.padding = \"20vh 10% 0 10%\";\n\n    document.body.innerHTML = \"\";\n    document.body.appendChild($img);\n    document.body.appendChild($code);\n}\n"
  },
  {
    "path": "public/assets/boot/ctrl_boot.d.ts",
    "content": "export {};\n\ninterface IChromecast {\n    init: () => Promise<any>;\n}\n\ndeclare global {\n    interface Window {\n        env: string\n        LNG: object;\n        CONFIG: object;\n        overrides: object;\n        Chromecast: IChromecast;\n    }\n}\n"
  },
  {
    "path": "public/assets/boot/ctrl_boot_backoffice.js",
    "content": "import { report } from \"../helpers/log.js\";\nimport { $error } from \"./common.js\";\n\nexport default async function main() {\n    try {\n        await Promise.all([\n            setup_device(),\n            setup_blue_death_screen(),\n            setup_history(),\n        ]);\n        window.dispatchEvent(new window.Event(\"pagechange\"));\n    } catch (err) {\n        console.error(err);\n        const msg = window.navigator.onLine === false ? \"OFFLINE\" : (err instanceof Error && err.message) || \"CAN'T LOAD\";\n        report(\"boot::\" + msg, err, location.href);\n        $error(msg);\n    }\n}\nmain();\n\nasync function setup_device() {\n    const className = \"ontouchstart\" in window ? \"touch-yes\" : \"touch-no\";\n    document.body.classList.add(className);\n}\n\nasync function setup_blue_death_screen() {\n    window.onerror = function(msg, url, lineNo, colNo, error) {\n        report(\"boot::\" + msg, error, url, lineNo, colNo);\n        $error(msg);\n    };\n}\n\nasync function setup_history() {\n    window.history.replaceState({}, \"\");\n}\n"
  },
  {
    "path": "public/assets/boot/ctrl_boot_frontoffice.js",
    "content": "import { toHref } from \"../lib/skeleton/router.js\";\nimport { loadJS } from \"../helpers/loader.js\";\nimport { init as setup_translation } from \"../locales/index.js\";\nimport { init as setup_config } from \"../model/config.js\";\nimport { init as setup_plugin } from \"../model/plugin.js\";\nimport { init as setup_cache } from \"../pages/filespage/cache.js\";\nimport { report } from \"../helpers/log.js\";\nimport { $error } from \"./common.js\";\n\nexport default async function main() {\n    try {\n        await Promise.all([\n            setup_config().then((config) => Promise.all([\n                setup_title(config),\n                window.self === window.top ? verify_origin(config) : verify_iframe_origin(config),\n            ])),\n            setup_translation(),\n            setup_xdg_open(),\n            setup_device(),\n            setup_blue_death_screen(),\n            setup_history(),\n            setup_polyfill(),\n            setup_plugin(),\n            setup_cache(),\n        ]);\n        window.dispatchEvent(new window.Event(\"pagechange\"));\n    } catch (err) {\n        console.error(err);\n        const msg = window.navigator.onLine === false ? \"OFFLINE\" : (err instanceof Error && err.message) || \"CAN'T LOAD\";\n        report(msg, err, location.href);\n        $error(msg);\n    }\n}\nawait main();\n\n/// /////////////////////////////////////////\nasync function setup_xdg_open() {\n    window.overrides = {};\n    return loadJS(import.meta.url, toHref(\"/overrides/xdg-open.js\"));\n}\n\nasync function setup_device() {\n    const className = \"ontouchstart\" in window ? \"touch-yes\" : \"touch-no\";\n    document.body.classList.add(className);\n    if (window.matchMedia && window.matchMedia(\"(prefers-color-scheme: dark)\").matches) {\n        document.body.classList.add(\"dark-mode\");\n    }\n    window.matchMedia && window.matchMedia(\"(prefers-color-scheme: dark)\").addEventListener(\"change\", function(e) {\n        e.matches ? document.body.classList.add(\"dark-mode\") : document.body.classList.remove(\"dark-mode\");\n    });\n}\n\nasync function setup_blue_death_screen() {\n    window.onerror = function(msg, url, lineNo, colNo, error) {\n        report(msg, error, url, lineNo, colNo);\n        $error(msg);\n        if (\"serviceWorker\" in navigator) navigator.serviceWorker\n            .getRegistrations()\n            .then((registrations) => {\n                for (const registration of registrations) {\n                    registration.unregister();\n                }\n            });\n    };\n}\n\nasync function setup_history() {\n    window.history.replaceState({}, \"\");\n}\n\nasync function setup_title(config) {\n    document.title = config[\"name\"] || \"Filestash\";\n}\n\nasync function setup_polyfill() {\n    if (!(\"replaceChildren\" in document.body)) {\n        await loadJS(import.meta.url, \"../lib/polyfill.js\");\n    }\n}\n\nasync function verify_origin(config) {\n    const origin = config[\"origin\"];\n    // happy path\n    if (!origin) return;\n    else if (location.origin === origin) return;\n    // non happy path\n    const u = new URL(origin);\n    if (u.host === location.host) return;\n    else if (u.hostname === location.hostname) return;\n    setTimeout(() => {\n        location.href = origin + location.pathname + location.search;\n    }, 1000);\n    throw new Error(\"Redirecting to \" + origin);\n}\n\nasync function verify_iframe_origin(_) {\n    // TODO: const origin = document.location.ancestorOrigins[0]\n    // should we verify iframe origin client side on top of frame ancestor? Until prooven wrong, no.\n}\n"
  },
  {
    "path": "public/assets/boot/router_backoffice.js",
    "content": "const routes = {\n    \"/admin/storage\": \"/pages/adminpage/ctrl_storage.js\",\n    \"/admin/workflow.*\": \"/pages/adminpage/ctrl_workflow.js\",\n    \"/admin/activity\": \"/pages/adminpage/ctrl_activity.js\",\n    \"/admin/settings\": \"/pages/adminpage/ctrl_settings.js\",\n    \"/admin/about\": \"/pages/adminpage/ctrl_about.js\",\n    \"/admin/setup\": \"/pages/adminpage/ctrl_setup.js\",\n    \"/admin/\": \"/pages/ctrl_adminpage.js\",\n    \"/admin\": \"/pages/ctrl_adminpage.js\",\n    \"/logout\": \"/pages/ctrl_logout.js\",\n    \"\": \"/pages/ctrl_notfound.js\",\n};\n\nexport default routes;\n"
  },
  {
    "path": "public/assets/boot/router_frontoffice.js",
    "content": "const routes = {\n    \"/login\": \"/pages/ctrl_connectpage.js\",\n    \"/logout\": \"/pages/ctrl_logout.js\",\n\n    \"/\": \"/pages/ctrl_homepage.js\",\n    \"/files/.*\": \"/pages/ctrl_filespage.js\",\n    \"/view/.*\": \"/pages/ctrl_viewerpage.js\",\n    // /tags/.* -> \"pages/ctrl_tags.js\",\n    \"/s/.*\": \"/pages/ctrl_sharepage.js\",\n\n    \"\": \"/pages/ctrl_notfound.js\",\n};\n\nexport default routes;\n"
  },
  {
    "path": "public/assets/components/breadcrumb.css",
    "content": ".component_breadcrumb {\n    position: relative;\n    z-index: 5;\n}\n.component_breadcrumb .breadcrumb {\n    margin: 0 0 0px 0;\n    z-index: 1000;\n}\n.component_breadcrumb .breadcrumb .ul {\n    display: flex;\n    list-style-type: none;\n    margin: 0;\n    width: 100%;\n    box-sizing: border-box;\n    padding: 2px 0;\n}\n.component_breadcrumb .breadcrumb .ul > span {\n    display: block;\n    flex-grow: 1;\n    padding: 7px 7px 7px 0;\n}\n.component_breadcrumb .breadcrumb .ul div, .component_breadcrumb .breadcrumb .ul .li {\n    display: inline-block;\n}\n.component_breadcrumb .breadcrumb [alt=\"sidebar-open\"] {\n    margin-left: -21px;\n    margin-right: 5px;\n    cursor: pointer;\n    display: none;\n}\n@media screen and (min-width: 1100px) {\n    .component_breadcrumb .breadcrumb [alt=\"sidebar-open\"] { display: block; }\n}\n.component_breadcrumb .component_logout {\n    align-self: center;\n    padding-right: 10px;\n}\n.component_breadcrumb .component_logout .component_icon {\n    height: 20px;\n}\n.component_breadcrumb .component_saving {\n    padding-left: 1px;\n}\n.component_breadcrumb .component_path-element {\n    display: inline-block;\n}\n.component_breadcrumb .component_path-element .label {\n    color: rgba(0, 0, 0, 0.75);\n    padding: 2px 5px;\n}\n.component_breadcrumb .component_path-element .label:focus-visible .title {\n    display: none;\n}\n.touch-yes .component_breadcrumb .component_path-element .label {\n    padding-top: 5px;\n    padding-bottom: 5px;\n}\n.component_breadcrumb .component_path-element .label span { display: inline-block; }\n.component_breadcrumb .component_path-element a.label {\n    position: relative;\n    color: rgba(0, 0, 0, 0.5);\n}\n.component_breadcrumb .component_path-element a.label span.title {\n    position: absolute;\n    left: 0;\n    background: var(--color);\n    color: var(--bg-color);\n    font-size: 0.8em;\n    opacity: 0;\n    transform: translateY(5px);\n    border-radius: 3px;\n    white-space: nowrap;\n    padding: 3px 10px !important;\n    margin: 25px 0px 5px 0px;\n}\n.component_breadcrumb .component_path-element .component_path-element-wrapper {\n    font-size: 18px;\n    display: inline-block;\n}\n.component_breadcrumb .component_path-element .component_path-element-wrapper a {\n    border-radius: 3px;\n    letter-spacing: -0.5px;\n}\n.component_breadcrumb .component_path-element.highlight .component_path-element-wrapper a {\n    color: var(--bg-color);\n    background: var(--dark);\n    border-radius: 3px;\n}\n.component_breadcrumb .component_separator img {\n    vertical-align: middle;\n}\n\n/* Phone like devices */\nbody.touch-yes component-breadcrumb .ul span {\n    overflow-x: auto;\n    overflow-y: hidden;\n    -webkit-overflow-scrolling: touch;\n    box-sizing: border-box;\n    -moz-box-sizing: border-box;\n    white-space: nowrap;\n}\nbody.touch-yes component-breadcrumb .ul span::-webkit-scrollbar {\n    height: 0px;\n}\nbody.touch-yes component-breadcrumb .ul span::-webkit-scrollbar-track {\n    background: var(--super-light);\n}\nbody.touch-yes component-breadcrumb .ul span::-webkit-scrollbar-thumb {\n    background: #d2d2d2;\n    border-radius: 1px;\n}\n\n/* Dark Mode */\n.dark-mode .component_breadcrumb .component_separator img {\n    filter: brightness(0.5) invert(1);\n}\n\nbody.touch-no .component_path-element-wrapper a.label:hover {\n    background: var(--border);\n}\nbody.touch-no .component_breadcrumb img[alt=\"sidebar-open\"]:hover {\n    filter: drop-shadow(0px 0px 4px #e2e2e2);\n}\nbody.touch-no .component_path-element-wrapper a.label:hover span.title {\n    opacity: 1;\n    transform: translateY(0px);\n    transition: all 0.15s ease-out;\n}\nbody.dark-mode.touch-no .component_path-element-wrapper a.label:hover {\n    background: rgba(255, 255, 255, 0.05);\n}\n.dark-mode .component_breadcrumb .component_path-element .label {\n    color: #f1f1f1;\n    opacity: 0.7;\n}\n.dark-mode .component_breadcrumb .component_path-element a.label {\n    opacity: 1;\n}\n\n/* ANIMATION */\n.component_breadcrumb .breadcrumb-enter {\n    transform: translateX(10px);\n    opacity: 0;\n    display: inline-block;\n}\n.component_breadcrumb .breadcrumb-enter.breadcrumb-enter-active {\n    opacity: 1;\n    transform: translateX(0);\n    transition: all 0.2s ease-out;\n}\n.component_breadcrumb .saving_indicator-leave {\n    opacity: 1;\n}\n.component_breadcrumb .saving_indicator-leave.saving_indicator-leave-active {\n    opacity: 0;\n    transition: all 0.2s ease-out;\n}\n.component_breadcrumb .saving_indicator-enter, .component_breadcrumb .saving_indicator-appear {\n    transform-origin: center;\n    animation-name: bounce;\n    animation-duration: 0.5s;\n}\n@keyframes bounce {\n    0% {\n        transform: scale(0);\n    }\n    30% {\n        transform: scale(1.5);\n    }\n    100% {\n        transform: scale(1);\n    }\n}\n"
  },
  {
    "path": "public/assets/components/breadcrumb.js",
    "content": "import { toHref } from \"../lib/skeleton/router.js\";\nimport { animate, slideYOut, slideYIn, opacityOut } from \"../lib/animate.js\";\nimport { forwardURLParams } from \"../lib/path.js\";\nimport { safe } from \"../lib/dom.js\";\nimport assert from \"../lib/assert.js\";\nimport { settingsSave } from \"../lib/store.js\";\nimport { get as getConfig } from \"../model/config.js\";\nimport { loadCSS } from \"../helpers/loader.js\";\n\nimport { extractPath, isDir, isNativeFileUpload } from \"../pages/filespage/helper.js\";\nimport { mv as mv$ } from \"../pages/filespage/model_files.js\";\nimport { mv as mvVL, withVirtualLayer } from \"../pages/filespage/model_virtual_layer.js\";\n\nconst mv = (from, to) => withVirtualLayer(\n    mv$(from, to),\n    mvVL(from, to),\n);\n\nclass ComponentBreadcrumb extends HTMLElement {\n    constructor() {\n        super();\n        if (new URL(location.href).searchParams.get(\"nav\") === \"false\") {\n            this.disabled = true;\n            return;\n        }\n        this.__init();\n    }\n\n    async __init() {\n        this.innerHTML = `\n        <nav class=\"component_breadcrumb container\" aria-label=\"Breadcrumb\">\n            <div class=\"breadcrumb no-select\">\n                <div class=\"ul\">\n                    <img alt=\"sidebar-open\" class=\"hidden\" src=\"data:image/svg+xml;base64,PHN2ZwogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHZpZXdCb3g9IjAgMCAxNiAxNiIKICAgd2lkdGg9IjE2IgogICBoZWlnaHQ9IjE2IgogICBmaWxsPSIjN2Y3ZjdmIj4KICA8cGF0aAogICAgIGQ9Im0gNi45MjY1NTM2LDguMTc3IC0yLjM5NiwyLjM5NiBhIDAuMjUsMC4yNSAwIDAgMSAtMC40MjcsLTAuMTc3IFYgNS42MDQgYSAwLjI1LDAuMjUgMCAwIDEgMC40MjcsLTAuMTc3IGwgMi4zOTYsMi4zOTYgYSAwLjI1LDAuMjUgMCAwIDEgMCwwLjM1NCB6IiAvPgogIDxwYXRoCiAgICAgZD0iTTAgMS43NUMwIC43ODQuNzg0IDAgMS43NSAwaDEyLjVDMTUuMjE2IDAgMTYgLjc4NCAxNiAxLjc1djEyLjVBMS43NSAxLjc1IDAgMCAxIDE0LjI1IDE2SDEuNzVBMS43NSAxLjc1IDAgMCAxIDAgMTQuMjVabTEuNzUtLjI1YS4yNS4yNSAwIDAgMC0uMjUuMjV2MTIuNWMwIC4xMzguMTEyLjI1LjI1LjI1SDkuNXYtMTNabTEyLjUgMTNhLjI1LjI1IDAgMCAwIC4yNS0uMjVWMS43NWEuMjUuMjUgMCAwIDAtLjI1LS4yNUgxMXYxM1oiIC8+Cjwvc3ZnPgo=\">\n                    <span data-bind=\"path\" role=\"status\"></span>\n                    <div class=\"li component_logout\">${this.__htmlLogout()}</div>\n                </div>\n            </div>\n        </nav>`;\n        assert.type(this.querySelector(\"img[alt=\\\"sidebar-open\\\"]\"), HTMLElement).onclick = () => {\n            settingsSave({ visible: true }, \"sidebar\");\n            window.dispatchEvent(new Event(\"resize\"));\n        };\n    }\n\n    attributeChangedCallback(name, oldValue, newValue) {\n        if (this.disabled === true) return;\n        else if (oldValue === newValue) return;\n\n        switch (name) {\n        case \"path\":\n            if (newValue === \"\") return;\n            return this.renderPath({ path: newValue, previous: oldValue || null });\n        case \"indicator\":\n            return this.renderIndicator();\n        }\n        throw new Error(\"component::breadcrumb.js unknow attribute name: \"+ name);\n    }\n\n    static get observedAttributes() {\n        return [\"path\", \"indicator\"];\n    }\n\n    async renderPath({ path = \"\", previous }) {\n        path = this.__normalised(path);\n        previous = this.__normalised(previous);\n        const pathChunks = path.split(\"/\");\n\n        // STEP1: leaving animation on elements that will be removed\n        if (previous !== null && previous.indexOf(path) >= 0) {\n            const previousChunks = previous.split(\"/\");\n            const nToAnimate = previousChunks.length - pathChunks.length;\n            const tasks = [];\n            for (let i=0; i<nToAnimate; i++) {\n                const n = previousChunks.length - i - 1;\n                const $chunk = assert.type(this.querySelector(`.component_path-element.n${n}`), HTMLElement);\n                tasks.push(animate($chunk, { time: 100, keyframes: slideYOut(-10) }));\n            }\n            await Promise.all(tasks);\n        }\n\n        // STEP2: setup the actual content\n        assert.type(this.querySelector(`[data-bind=\"path\"]`), HTMLElement).innerHTML = pathChunks.map((chunk, idx) => {\n            const label = idx === 0 ? getConfig(\"name\", \"Filestash\") : chunk;\n            const link = pathChunks.slice(0, idx + 1).join(\"/\") + \"/\";\n            const limitSize = (word, highlight = false) => {\n                if (highlight === true && word.length > 30) {\n                    return word.substring(0, 12).trim() + \"...\" +\n                        word.substring(word.length - 10, word.length).trim();\n                }\n                else if (word.length > 27) return word.substring(0, 20).trim() + \"...\";\n                return word;\n            };\n            const isLast = idx === pathChunks.length - 1;\n            if (isLast) return `\n                <div class=\"component_path-element n${idx}\">\n                    <div class=\"li component_path-element-wrapper\">\n                        <div class=\"label\">\n                            <div aria-current=\"location\">${safe(limitSize(label))}</div><span></span>\n                        </div>\n                    </div>\n                </div>`;\n\n            const minify = (() => {\n                if (idx === 0) return false;\n                else if (pathChunks.length <= (document.body.clientWidth > 800 ? 5 : 4)) return false;\n                else if (idx > pathChunks.length - (document.body.clientWidth > 1000 ? 4 : 3)) return false;\n                return true;\n            })();\n\n            const tmpl = (() => {\n                if (minify) return `\n                    ...\n                    <span class=\"title\">\n                        ${safe(limitSize(label, true))}\n                    </span>\n                `;\n                return `<div>${safe(limitSize(label))}</div>`;\n            })();\n            return `\n                <div class=\"component_path-element n${idx}\" data-path=\"${safe(pathChunks.slice(0, idx+1).join(\"/\")) + \"/\"}\">\n                    <div class=\"li component_path-element-wrapper\">\n                        <a class=\"label\" aria-label=\"${safe(label)}\" href=\"${forwardURLParams(toHref(\"/files\" + encodeURIComponent(link).replaceAll(\"%2F\", \"/\")), [\"share\", \"canary\"])}\" data-link draggable=\"false\">\n                            ${tmpl}\n                        </a>\n                        <div class=\"component_separator\">\n                            <img alt=\"path_separator\" width=\"16\" height=\"16\" src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAA30lEQVQ4T63T7Q2CMBAG4OuVPdQNcAPdBCYwDdclCAQ3ACfRDXQDZQMHgNRcAoYApfWjv0jIPX3b3gn4wxJjI03TUAhRBkGwV0o9ffaYIEVRrJumuQHA3ReaILxzl+bCkNZ660ozi/QQIl4BoCKieAmyIlyU53lkjCld0CIyhIwxSmt9nEvkRLgoyzIuPggh4iRJqjHkhXTQAwBWUsqNUoq/38sL+TlJf7lf38ngdU5EFNme2adPFgGGrR2LiGcAqIko/LhjeXbatuVOraWUO58hnJ1iRKx8AetxXPHH/1+y62USursaSgAAAABJRU5ErkJggg==\">\n                        </div>\n                    </div>\n                </div>`;\n        }).join(\"\");\n        this.setupDragDropTarget();\n\n        // STEP3: entering animation for elements that got added in\n        if (previous !== null && path.indexOf(previous) >= 0) {\n            const previousChunks = previous.split(\"/\");\n            const nToAnimate = pathChunks.length - previousChunks.length;\n            for (let i=0; i<nToAnimate; i++) {\n                const n = pathChunks.length - i - 1;\n                const $chunk = assert.type(this.querySelector(`.component_path-element.n${n}`), HTMLElement);\n                await animate($chunk, { time: 100, keyframes: slideYIn(-5) });\n            }\n        }\n    }\n\n    async renderIndicator() {\n        let state = this.hasAttribute(\"indicator\");\n        if (state && this.getAttribute(\"indicator\") !== \"false\") state = true;\n\n        let $indicator = assert.type(this.querySelector(`[data-bind=\"path\"]`), HTMLElement);\n        $indicator = assert.type($indicator.lastChild.querySelector(\"span\"), HTMLElement);\n\n        if (state) {\n            $indicator.style.opacity = 1;\n            $indicator.innerHTML = `<div class=\"component_saving\">*</div>`;\n            await animate($indicator, {\n                time: 500,\n                keyframes: [\n                    { transform: \"scale(0)\", offset: 0 },\n                    { transform: \"scale(1.5)\", offset: 0.3 },\n                    { transform: \"scale(1)\", offset: 1 },\n                ],\n                fill: \"none\"\n            });\n        } else {\n            $indicator.style.opacity = 0;\n            await animate($indicator, { time: 200, keyframes: opacityOut(), fill: \"none\" });\n        }\n    }\n\n    setupDragDropTarget() {\n        this.querySelectorAll(\"a.label\").forEach(($elmnt) => {\n            const $folder = assert.type($elmnt, HTMLElement);\n            const $path = assert.truthy($folder.closest(\".component_path-element\"));\n            $folder.ondrop = async(e) => {\n                $path.classList.remove(\"highlight\");\n                const from = e.dataTransfer.getData(\"path\");\n                let to = $path.getAttribute(\"data-path\");\n\n                const [, fromName] = extractPath(from);\n                to += fromName;\n                if (isDir(from)) to += \"/\";\n                await mv(from, to).toPromise();\n            };\n            $folder.ondragover = (e) => {\n                if (isNativeFileUpload(e)) return;\n                e.preventDefault();\n                $path.classList.add(\"highlight\");\n            };\n            $folder.ondragleave = () => {\n                $path.classList.remove(\"highlight\");\n            };\n        });\n    }\n\n    __htmlLogout() {\n        if (window.self !== window.top) return \"\"; // no logout button from an iframe\n        return `\n            <a href=\"${toHref(\"/logout\")}\" data-link draggable=\"false\">\n                <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0ODkuODg4IDQ4OS44ODgiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQ4OS44ODggNDg5Ljg4ODsiPgogIDxwYXRoIGZpbGw9IiM2ZjZmNmYiIGQ9Ik0yNS4zODMsMjkwLjVjLTcuMi03Ny41LDI1LjktMTQ3LjcsODAuOC0xOTIuM2MyMS40LTE3LjQsNTMuNC0yLjUsNTMuNCwyNWwwLDBjMCwxMC4xLTQuOCwxOS40LTEyLjYsMjUuNyAgICBjLTM4LjksMzEuNy02Mi4zLDgxLjctNTYuNiwxMzYuOWM3LjQsNzEuOSw2NSwxMzAuMSwxMzYuOCwxMzguMWM5My43LDEwLjUsMTczLjMtNjIuOSwxNzMuMy0xNTQuNWMwLTQ4LjYtMjIuNS05Mi4xLTU3LjYtMTIwLjYgICAgYy03LjgtNi4zLTEyLjUtMTUuNi0xMi41LTI1LjZsMCwwYzAtMjcuMiwzMS41LTQyLjYsNTIuNy0yNS42YzUwLjIsNDAuNSw4Mi40LDEwMi40LDgyLjQsMTcxLjhjMCwxMjYuOS0xMDcuOCwyMjkuMi0yMzYuNywyMTkuOSAgICBDMTIyLjE4Myw0ODEuOCwzNS4yODMsMzk2LjksMjUuMzgzLDI5MC41eiBNMjQ0Ljg4MywwYy0xOCwwLTMyLjUsMTQuNi0zMi41LDMyLjV2MTQ5LjdjMCwxOCwxNC42LDMyLjUsMzIuNSwzMi41ICAgIHMzMi41LTE0LjYsMzIuNS0zMi41VjMyLjVDMjc3LjM4MywxNC42LDI2Mi44ODMsMCwyNDQuODgzLDB6IiAvPgo8L3N2Zz4K\" alt=\"power\">\n            </a>\n        `;\n    }\n\n    __normalised(path) {\n        if (path === null) return null;\n        else if (path.endsWith(\"/\") === false) return path;\n        return path.replace(new RegExp(\"/$\"), \"\");\n    }\n}\n\nexport function init() {\n    return loadCSS(import.meta.url, \"./breadcrumb.css\");\n}\n\ncustomElements.define(\"component-breadcrumb\", ComponentBreadcrumb);\n"
  },
  {
    "path": "public/assets/components/decorator_shell_filemanager.css",
    "content": ".component_filemanager_shell {\n    display: flex;\n    height: 100%;\n    background: white;\n}\n\n.component_filemanager_shell component-breadcrumb:hover ~ [data-bind=\"filemanager-children\"],\n.component_filemanager_shell [data-bind=\"sidebar\"]:hover ~ div > [data-bind=\"filemanager-children\"] {\n    border-color: var(--border);\n}\n\n.component_filemanager_shell [data-bind=\"filemanager-children\"] {\n    border-top: 2px solid;\n    border-left: 2px solid;\n    border-color: transparent;\n    transition: 0.5s ease border-top-left-radius, 0.5s ease border-top-right-radius, 1s ease border-color;\n    border-top-left-radius: 10px;\n    border-top-right-radius: 0px;\n    display: flex;\n    height: 100%;\n    overflow: hidden;\n}\n\n.component_filemanager_shell [data-bind=\"sidebar\"].hidden ~ div > [data-bind=\"filemanager-children\"] {\n    border-top-left-radius: 0;\n    border-top-right-radius: 0;\n    border-left: none;\n    border-color: transparent;\n}\n.component_filemanager_shell [data-bind=\"sidebar\"].hidden ~ div > [data-bind=\"filemanager-children\"].scrolling {\n    border-top-left-radius: 30px;\n    border-top-right-radius: 30px;\n}\n.component_filemanager_shell [data-bind=\"sidebar\"].hidden ~ div > component-breadcrumb > .component_breadcrumb,\n.component_filemanager_shell [data-bind=\"sidebar\"]:empty ~ div > component-breadcrumb > .component_breadcrumb,\n.component_filemanager_shell [data-bind=\"sidebar\"].hidden ~ div > [data-bind=\"filemanager-children\"] .container,\n.component_filemanager_shell [data-bind=\"sidebar\"]:empty ~ div > [data-bind=\"filemanager-children\"] .container {\n    width: 98%;\n    margin: 0 auto;\n    max-width: 815px;\n}\n\n.component_filemanager_shell [data-bind=\"sidebar\"].hidden ~ div > [data-bind=\"filemanager-children\"] {\n    background: rgba(100,100,100,.05);\n}\n\n.component_filemanager_shell [data-bind=\"sidebar\"] ~ div [is=\"component_filesystem\"],\n.component_filemanager_shell [data-bind=\"sidebar\"] ~ div [is=\"component_newitem\"],\n.component_filemanager_shell [data-bind=\"sidebar\"] ~ div component-menubar,\n.component_filemanager_shell [data-bind=\"sidebar\"] ~ div component-breadcrumb,\n.component_filemanager_shell [data-bind=\"sidebar\"] ~ div [is=\"component_submenu\"] .component_submenu{\n    padding: 0 30px;\n}\n\n.component_filemanager_shell [data-bind=\"sidebar\"].hidden ~ div [is=\"component_filesystem\"],\n.component_filemanager_shell [data-bind=\"sidebar\"]:empty ~ div [is=\"component_filesystem\"],\n.component_filemanager_shell [data-bind=\"sidebar\"].hidden ~ div [is=\"component_newitem\"],\n.component_filemanager_shell [data-bind=\"sidebar\"]:empty ~ div [is=\"component_newitem\"],\n.component_filemanager_shell [data-bind=\"sidebar\"].hidden ~ div component-menubar,\n.component_filemanager_shell [data-bind=\"sidebar\"]:empty ~ div component-menubar,\n.component_filemanager_shell [data-bind=\"sidebar\"].hidden ~ div component-breadcrumb,\n.component_filemanager_shell [data-bind=\"sidebar\"]:empty ~ div component-breadcrumb,\n.component_filemanager_shell [data-bind=\"sidebar\"].hidden ~ div [is=\"component_submenu\"] .component_submenu,\n.component_filemanager_shell [data-bind=\"sidebar\"]:empty ~ div [is=\"component_submenu\"] .component_submenu {\n    padding: 0px;\n}\n\nbody.dark-mode .component_filemanager_shell {\n    background: #2b2d30;\n}\n"
  },
  {
    "path": "public/assets/components/decorator_shell_filemanager.js",
    "content": "import { createElement, createRender } from \"../lib/skeleton/index.js\";\nimport { navigate, fromHref } from \"../lib/skeleton/router.js\";\nimport rxjs, { effect } from \"../lib/rx.js\";\nimport { onDestroy } from \"../lib/skeleton/lifecycle.js\";\nimport { animate, slideYOut } from \"../lib/animate.js\";\nimport { qs, safe } from \"../lib/dom.js\";\nimport { loadCSS } from \"../helpers/loader.js\";\nimport { init as initBreadcrumb } from \"../components/breadcrumb.js\";\nimport ctrlSidebar, { init as initSidebar } from \"./sidebar.js\";\nimport { isAlreadyFocused } from \"../pages/filespage/helper.js\";\n\nexport default function(ctrl) {\n    const urlToPath = (pathname = \"\") => decodeURIComponent(pathname.split(\"/\").filter((_, i) => i !== 1).join(\"/\"));\n    const $page = createElement(`\n        <div class=\"component_filemanager_shell\" style=\"flex-direction:row\">\n            <aside data-bind=\"sidebar\" class=\"hidden\"></aside>\n            <div style=\"width:100%;display:flex;flex-direction:column;\">\n                <component-breadcrumb path=\"${safe(urlToPath(history.state.previous))}\"></component-breadcrumb>\n                <main id=\"main\" data-bind=\"filemanager-children\"></main>\n            </div>\n        </div>\n    `);\n\n    return async function(render) {\n        render($page);\n\n        // feature1: setup the breadcrumb path\n        const $breadcrumb = qs($page, \"component-breadcrumb\");\n        $breadcrumb.setAttribute(\"path\", urlToPath(fromHref(location.pathname + location.hash)));\n\n        // feature2: setup the childrens\n        const $main = qs($page, `[data-bind=\"filemanager-children\"]`);\n        $main.classList.remove(\"hidden\");\n        ctrl(createRender($main));\n        ctrlSidebar(createRender(qs($page, `[data-bind=\"sidebar\"]`)), {});\n        onDestroy(async() => {\n            if ((history.state.previous || \"\").startsWith(\"/view/\") && location.pathname.startsWith(\"/files/\")) {\n                await animate($main, { time: 100, keyframes: slideYOut(20), fill: \"none\" });\n                $main.classList.add(\"hidden\");\n            }\n        });\n\n        // feature3: key shortcut\n        const regexStartFiles = new RegExp(\"^/files/.+\");\n        effect(rxjs.fromEvent(window, \"keydown\").pipe(\n            rxjs.filter((e) => regexStartFiles.test(fromHref(location.pathname)) &&\n                        e.keyCode === 8 && !isAlreadyFocused()), // backspace in file manager\n            rxjs.tap(() => {\n                const p = location.pathname.replace(new RegExp(\"/$\"), \"\").split(\"/\");\n                p.pop();\n                navigate(p.join(\"/\") + \"/\" + location.hash);\n            }),\n        ));\n    };\n}\n\nexport function init() {\n    return Promise.all([\n        loadCSS(import.meta.url, \"../components/decorator_shell_filemanager.css\"),\n        initBreadcrumb(), initSidebar(),\n    ]);\n}\n"
  },
  {
    "path": "public/assets/components/dropdown.css",
    "content": ""
  },
  {
    "path": "public/assets/components/dropdown.js",
    "content": "import { createFragment } from \"../lib/skeleton/index.js\";\nimport { animate, slideYIn } from \"../lib/animate.js\";\nimport assert from \"../lib/assert.js\";\nimport { loadCSS } from \"../helpers/loader.js\";\n\nexport default class ComponentDropdown extends HTMLElement {\n    constructor() {\n        super();\n        this.render();\n    }\n\n    async connectedCallback() {\n        await loadCSS(import.meta.url, \"./dropdown.css\");\n    }\n\n    static get observedAttributes() {\n        return [\"options\"];\n    }\n\n    render() {\n        this.classList.add(\"component_dropdown\", \"view\", \"sort\");\n        this.appendChild(createFragment(`\n  <div class=\"dropdown_button\">\n    <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzODQgNTEyIj4KICA8cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNIDM2MCw0NjAgSCAyNCBDIDEwLjcsNDYwIDAsNDUzLjMgMCw0NDAgdiAtMTIgYyAwLC0xMy4zIDEwLjcsLTIwIDI0LC0yMCBoIDMzNiBjIDEzLjMsMCAyNCw2LjcgMjQsMjAgdiAxMiBjIDAsMTMuMyAtMTAuNywyMCAtMjQsMjAgeiIgLz4KICA8cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNIDIyNi41NTM5LDIzNC44ODQyOCBWIDUyLjk0MzI4MyBjIDAsLTYuNjI3IC01LjM3MywtMTIgLTEyLC0xMiBoIC00NCBjIC02LjYyNywwIC0xMiw1LjM3MyAtMTIsMTIgViAyMzQuODg0MjggaCAtNTIuMDU5IGMgLTIxLjM4MiwwIC0zMi4wOSwyNS44NTEgLTE2Ljk3MSw0MC45NzEgbCA4Ni4wNTksODYuMDU5IGMgOS4zNzMsOS4zNzMgMjQuNTY5LDkuMzczIDMzLjk0MSwwIGwgODYuMDU5LC04Ni4wNTkgYyAxNS4xMTksLTE1LjExOSA0LjQxMSwtNDAuOTcxIC0xNi45NzEsLTQwLjk3MSB6IiAvPgo8L3N2Zz4K\" alt=\"download_white\">\n  </div>`));\n\n        this.appendChild(createFragment(`\n  <div class=\"dropdown_container\">\n    <ul>\n      <li>\n        <div>\n          <a download=\"README.org\" href=\"/api/files/cat?path=%2FREADME.org\">Save current file</a>\n        </div>\n      </li>\n      <li>\n        <div>\n          <a target=\"_blank\" href=\"/api/export/private/text/html/README.org\">Export as HTML</a>\n        </div>\n      </li>\n      <li>\n        <div>\n          <a target=\"_blank\" href=\"/api/export/private/application/pdf/README.org\">Export as PDF</a>\n        </div>\n      </li>\n      <li>\n        <div>\n          <a target=\"_blank\" href=\"/api/export/private/text/markdown/README.org\">Export as Markdown</a>\n        </div>\n      </li>\n      <li>\n        <div>\n          <a target=\"_blank\" href=\"/api/export/private/text/plain/README.org\">Export as TXT</a>\n        </div>\n      </li>\n      <li>\n        <div>\n          <a target=\"_blank\" download=\"README.tex\" href=\"/api/export/private/text/x-latex/README.org\">Export as Latex</a>\n        </div>\n      </li>\n      <li>\n        <div>\n          <a target=\"_blank\" download=\"README.ics\" href=\"/api/export/private/text/calendar/README.org\">Export as ical</a>\n        </div>\n      </li>\n      <li>\n        <div>\n          <a target=\"_blank\" download=\"README.odt\" href=\"/api/export/private/application/vnd.oasis.opendocument.text/README.org\">Export as Open office</a>\n        </div>\n      </li>\n      <li>\n        <div>\n          <a target=\"_blank\" download=\"README.pdf\" href=\"/api/export/private/application/pdf/README.org?mode=beamer\">Export as Beamer</a>\n        </div>\n      </li>\n    </ul>\n  </div>\n</div>\n        `));\n\n        const setActive = () => this.classList.toggle(\"active\");\n        assert.type(this.querySelector(\".dropdown_button\"), HTMLElement).onclick = () => {\n            setActive();\n            animate(assert.type(this.querySelector(\".dropdown_container\"), HTMLElement), {\n                time: 100,\n                keyframes: slideYIn(2),\n            });\n        };\n    }\n}\n\nif (!customElements.get(\"component-dropdown\"))\n    customElements.define(\"component-dropdown\", ComponentDropdown);\n"
  },
  {
    "path": "public/assets/components/fab.css",
    "content": ".component_fab {\n  position: fixed;\n  bottom: 20px;\n  right: 20px;\n  z-index: 2;\n  background: transparent;\n}\n.component_fab .content:empty {\n    display: none;\n}\n.component_fab .content {\n    height: 25px;\n    width: 25px;\n    padding: 13px;\n    border-radius: 50%;\n    background: var(--dark);\n    box-shadow: rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px;\n    z-index: 1000;\n    cursor: pointer;\n}\n"
  },
  {
    "path": "public/assets/components/fab.js",
    "content": "import { loadCSS } from \"../helpers/loader.js\";\nimport assert from \"../lib/assert.js\";\n\nexport default class ComponentFab extends HTMLButtonElement {\n    constructor() {\n        super();\n        this.innerHTML = `<div class=\"content\"></div>`;\n        this.classList.add(\"component_fab\");\n    }\n\n    async render($icon) {\n        await loadCSS(import.meta.url, \"./fab.css\");\n        assert.type(this.querySelector(\".content\"), HTMLElement).replaceChildren($icon);\n    }\n}\n\ncustomElements.define(\"component-fab\", ComponentFab, { extends: \"button\" });\n"
  },
  {
    "path": "public/assets/components/form.js",
    "content": "import { createElement } from \"../lib/skeleton/index.js\";\nimport { qs, safe } from \"../lib/dom.js\";\nimport { gid } from \"../lib/random.js\";\nimport { ApplicationError } from \"../lib/error.js\";\n\nimport \"./icon.js\";\n\nexport function formTmpl(options = {}) {\n    const {\n        autocomplete = true,\n        renderNode = null,\n        renderLeaf = null,\n        renderInput = $renderInput({ autocomplete }),\n    } = options;\n    return {\n        renderNode: (opts) => {\n            if (renderNode) {\n                const $el = renderNode({ ...opts, format });\n                if ($el) return $el;\n            }\n            const { label } = opts;\n            return createElement(`\n                <fieldset>\n                    <legend class=\"no-select\">${safe(format(label))}</legend>\n                </fieldset>\n            `);\n        },\n        renderLeaf: (opts) => {\n            if (renderLeaf) {\n                const $el = renderLeaf({ ...opts, format });\n                if ($el) return $el;\n            }\n            const { label } = opts;\n            return createElement(`\n                <label>\n                    ${safe(format(label))}\n                </label>\n            `);\n        },\n        renderInput,\n        formatLabel: format\n    };\n};\n\nexport function $renderInput(options = {}) {\n    const { autocomplete } = options;\n\n    return function(props) {\n        const {\n            id = null,\n            type,\n            value = null,\n            placeholder = \"\",\n            required = false,\n            readonly = false,\n            path = [],\n            datalist = null,\n            options = null,\n            pattern = null,\n        } = props;\n\n        const attrs = [];\n        attrs.push(($node) => $node.setAttribute(\"name\", path.join(\".\")));\n        if (id) attrs.push(($node) => $node.setAttribute(\"id\", id));\n        if (placeholder) attrs.push(($node) => $node.setAttribute(\"placeholder\", placeholder));\n        if (!autocomplete || props.autocomplete === false) attrs.push(($node) => {\n            $node.setAttribute(\"autocomplete\", \"off\");\n            $node.setAttribute(\"autocorrect\", \"off\");\n            $node.setAttribute(\"autocapitalize\", \"off\");\n            $node.setAttribute(\"spellcheck\", \"off\");\n            if (type === \"password\") {\n                $node.setAttribute(\"data-lpignore\", \"true\"); // LastPass\n                $node.setAttribute(\"data-1p-ignore\", \"true\"); // 1Password\n                $node.setAttribute(\"data-bwignore\", \"true\"); // Bitwarden\n                $node.setAttribute(\"data-protonpass-ignore\", \"true\"); // Proton Pass\n            }\n        });\n        if (pattern) attrs.push(($node) => $node.setAttribute((type !== \"file\" ? \"pattern\" : \"accept\"), pattern));\n        if (required) attrs.push(($node) => $node.setAttribute(\"required\", \"\"));\n        if (readonly) attrs.push(($node) => $node.setAttribute(\"disabled\", \"\"));\n\n        switch (type) {\n        case \"text\": {\n            const $input = createElement(`\n                <input\n                    type=\"text\"\n                    class=\"component_input\"\n                />\n            `);\n            if (!($input instanceof HTMLInputElement)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: missing input\");\n            else if (value) $input.value = value;\n            attrs.map((setAttribute) => setAttribute($input));\n\n            if (!datalist) return $input;\n\n            const dataListId = gid(\"list_\");\n            $input.setAttribute(\"list\", dataListId);\n            $input.setAttribute(\"datalist\", datalist.join(\",\"));\n\n            const $wrapper = document.createElement(\"span\");\n            const $datalist = document.createElement(\"datalist\");\n            $datalist.setAttribute(\"id\", dataListId);\n            $wrapper.appendChild($input);\n            $wrapper.appendChild($datalist);\n            (props.multi ? multicomplete(value, datalist) : (datalist || [])).forEach((value) => {\n                $datalist.appendChild(new Option(value));\n            });\n            if (!props.multi) return $wrapper;\n            // @ts-ignore\n            $input.refresh = () => {\n                const _datalist = $input?.getAttribute(\"datalist\")?.split(\",\");\n                $datalist.innerHTML = \"\";\n                multicomplete($input.value, _datalist).forEach((value) => {\n                    $datalist.appendChild(new Option(value));\n                });\n            };\n            $input.oninput = () => {\n                for (const $option of $datalist.children) {\n                    $option.remove();\n                }\n                // @ts-ignore\n                $input.refresh();\n            };\n            return $wrapper;\n        }\n        case \"enable\": {\n            const $div = createElement(`\n                <div class=\"component_checkbox\">\n                    <input\n                        type=\"checkbox\"\n                        ${(value === null ? props.default : value) ? \"checked\" : \"\"}\n                    />\n                    <span className=\"indicator\"></span>\n                </div>\n            `);\n            const $input = $div.querySelector(\"input\");\n            attrs.map((setAttribute) => setAttribute($input));\n            return $div;\n        }\n        case \"number\": {\n            const $input = createElement(`\n                <input\n                    type=\"number\"\n                    class=\"component_input\"\n                />\n            `);\n            if (!($input instanceof HTMLInputElement)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: missing input\");\n            else if (value) $input.value = value;\n            attrs.map((setAttribute) => setAttribute($input));\n            return $input;\n        }\n        case \"password\": {\n            const $div = createElement(`\n                <div class=\"formbuilder_password\">\n                    <input\n                        type=\"password\"\n                        class=\"component_input\"\n                    />\n                    <component-icon name=\"eye\"></component-icon>\n                </div>\n            `);\n            const $input = $div.querySelector(\"input\");\n            if (!($input instanceof HTMLInputElement)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: missing input\");\n            else if (value) $input.value = value;\n            attrs.map((setAttribute) => setAttribute($input));\n\n            const $icon = $div.querySelector(\"component-icon\");\n            if ($icon instanceof HTMLElement) {\n                $icon.onclick = function(e) {\n                    if (!(e.target instanceof HTMLElement)) return;\n                    const $input = e.target?.parentElement?.previousElementSibling;\n                    if (!$input) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: missing input\");\n                    if ($input.getAttribute(\"type\") === \"password\") $input.setAttribute(\"type\", \"text\");\n                    else $input.setAttribute(\"type\", \"password\");\n                };\n            }\n            return $div;\n        }\n        case \"long_text\": {\n            const $textarea = createElement(`\n                <textarea\n                    class=\"component_textarea\"\n                    rows=\"8\"\n                ></textarea>\n            `);\n            if (!($textarea instanceof HTMLTextAreaElement)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: missing input\");\n            else if (value) $textarea.value = value;\n            attrs.map((setAttribute) => setAttribute($textarea));\n            return $textarea;\n        }\n        case \"bcrypt\": {\n            const $input = createElement(`\n                <input\n                    type=\"password\"\n                    class=\"component_input\"\n                    readonly\n                />\n            `);\n            if (!($input instanceof HTMLInputElement)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: missing input\");\n            else if (value) $input.value = value;\n            attrs.map((setAttribute) => setAttribute($input));\n            return $input;\n        }\n        case \"hidden\": {\n            const $input = createElement(`\n                <input type=\"hidden\" />\n            `);\n            if (!($input instanceof HTMLInputElement)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: missing input\");\n            else if (value) $input.value = value;\n            $input.setAttribute(\"name\", path.join(\".\"));\n            return $input;\n        }\n        case \"boolean\": {\n            const $div = createElement(`\n                <div class=\"component_checkbox\">\n                    <input\n                        type=\"checkbox\"\n                        ${(value === null ? props.default : value) ? \"checked\" : \"\"}\n                    />\n                    <span class=\"indicator\"></span>\n                </div>\n            `);\n            const $input = $div.querySelector(\"input\");\n            attrs.map((setAttribute) => setAttribute($input));\n            return $div;\n        }\n        case \"select\": {\n            const $select = createElement(`\n                <select class=\"component_select\"></select>\n            `);\n            if (!($select instanceof HTMLSelectElement)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: missing input\");\n            else if (value) $select.value = value || props.default;\n            attrs.map((setAttribute) => setAttribute($select));\n            (options || []).forEach((name) => {\n                const $option = createElement(`\n                    <option></option>\n                `);\n                $option.textContent = name;\n                $option.setAttribute(\"name\", name);\n                if (name === (value || props.default)) {\n                    $option.setAttribute(\"selected\", \"\");\n                }\n                $select.appendChild($option);\n            });\n            return $select;\n        }\n        case \"date\": {\n            const $input = createElement(`\n                <input\n                    type=\"date\"\n                    class=\"component_input\"\n                />\n            `);\n            if (!($input instanceof HTMLInputElement)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: missing input\");\n            else if (value) $input.value = value;\n            attrs.map((setAttribute) => setAttribute($input));\n            return $input;\n        }\n        case \"datetime\": {\n            const $input = createElement(`\n                <input\n                    type=\"datetime-local\"\n                    class=\"component_input\"\n                />\n            `);\n            if (!($input instanceof HTMLInputElement)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: missing input\");\n            else if (value) $input.value = value;\n            attrs.map((setAttribute) => setAttribute($input));\n            return $input;\n        }\n        case \"image\": {\n            const $img = createElement(\"<img />\");\n            $img.setAttribute(\"id\", id);\n            $img.setAttribute(\"src\", value);\n            return $img;\n        }\n        case \"file\": {\n            const $file = createElement(`\n                <div className=\"fileupload-image\">\n                    <input\n                        type=\"file\"\n                        class=\"component_input\"\n                    />\n                    <div class=\"preview\" style=\"margin-top:-15px;\"></div>\n                </div>\n            `);\n            const $preview = qs($file, \".preview\");\n            const draw = (val) => {\n                $preview.innerHTML = \"\";\n                if ((val || \"\").substring(0, 10) === \"data:image\") $preview.appendChild(createElement(`\n                    <img class=\"full-width\" src=\"${safe(val)}\" />\n                `));\n                else if ((val || \"\").substring(0, 20) === \"data:application/pdf\") $preview.appendChild(createElement(`\n                    <object class=\"full-width\" type=\"application/pdf\" data=\"${safe(val)}\" style=\"height:250px;\" />\n                `));\n            };\n            const $input = qs($file, \"input\");\n            $input.onchange = (e) => {\n                if (e.target.files.length === 0) {\n                    draw(null);\n                    return;\n                }\n                const reader = new window.FileReader();\n                reader.readAsDataURL(e.target.files[0]);\n                reader.onload = () => draw(reader.result);\n            };\n            $file.oncancel = () => {\n                if (!value) return;\n                $input.dispatchEvent(new Event(\"input\"));\n                draw(null);\n            };\n            attrs.map((setAttribute) => setAttribute($input));\n            draw(value);\n            return $file;\n        }\n        default: {\n            const $input = createElement(`\n                <input\n                    type=\"text\"\n                    class=\"component_input\"\n                    readonly\n                />\n            `);\n            $input.setAttribute(\"value\", `unknown element type ${type}`);\n            $input.setAttribute(\"name\", path.join(\".\"));\n            return $input;\n        } }\n    };\n}\n\nexport function format(name) {\n    if (typeof name !== \"string\") {\n        return \"N/A\";\n    }\n    return name\n        .split(\"_\")\n        .map((word) => {\n            if (word.length < 1) {\n                return word;\n            }\n            return (word[0] || \"\").toUpperCase() + word.substring(1);\n        })\n        .join(\" \");\n};\n\nexport function multicomplete(input, datalist) {\n    input = (input || \"\").trim().replace(/,$/g, \"\");\n    const current = input.split(\",\").map((val) => val.trim()).filter((t) => !!t);\n    const diff = datalist.filter((x) => current.indexOf(x) === -1);\n    return diff.map((candidate) => input.length === 0 ? candidate : `${input}, ${candidate}`);\n}\n"
  },
  {
    "path": "public/assets/components/icon.js",
    "content": "class Icon extends HTMLElement {\n    static get observedAttributes() {\n        return [\"name\"];\n    }\n\n    attributeChangedCallback() {\n        const alt = this.getAttribute(\"name\");\n        const img = this._mapOfIcon(alt);\n        window.requestAnimationFrame(() => {\n            this.innerHTML = this.render({ alt, img });\n        });\n    }\n\n    render({ alt, img }) {\n        return `<img\n                    class=\"component_icon\"\n                    draggable=\"false\"\n                    src=\"${img}\"\n                    alt=\"${alt}\" />`;\n    }\n\n    _mapOfIcon(name) {\n        switch (name) {\n        case \"arrow_right\":\n            return \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPHBhdGggc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MC41MzMzMzMzNiIgZD0iTTguNTkgMTYuMzRsNC41OC00LjU5LTQuNTgtNC41OUwxMCA1Ljc1bDYgNi02IDZ6IiAvPgogIDxwYXRoIGZpbGw9Im5vbmUiIGQ9Ik0wLS4yNWgyNHYyNEgweiIgLz4KPC9zdmc+Cg==\";\n        case \"arrow_left\":\n            return \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPHBhdGggc3R5bGU9ImZpbGw6IzZmNmY2ZjtmaWxsLW9wYWNpdHk6MTtzdHJva2Utd2lkdGg6MS41MTE4MTEwMjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIgZD0ibSAxNiw3LjE2IC00LjU4LDQuNTkgNC41OCw0LjU5IC0xLjQxLDEuNDEgLTYsLTYgNiwtNiB6Ii8+CiAgPHBhdGggZmlsbD0ibm9uZSIgZD0iTTAtLjI1aDI0djI0SDB6Ii8+Cjwvc3ZnPgo=\";\n        case \"eye\":\n            return \"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNDggNDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgICA8c3R5bGU+LmNscy0xe2ZpbGw6bm9uZTtzdHJva2U6IzkxOTE5MjtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLXdpZHRoOjRweDt9PC9zdHlsZT4KICAgIDxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTEsMjRhMjYuODUsMjYuODUsMCwwLDEsNDYsMCIvPgogICAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMSwyNGEyNi44NSwyNi44NSwwLDAsMCw0NiwwIi8+CiAgICA8ZWxsaXBzZSBjbGFzcz0iY2xzLTEiIGN4PSIyNCIgY3k9IjI0IiByeD0iNyIgcnk9IjciLz4KPC9zdmc+Cg==\";\n        case \"close\":\n            return \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MS45NzYgNTEuOTc2Ij4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjUzMzMzMjg1O3N0cm9rZS13aWR0aDoxLjQ1NjgxMTE5IiBkPSJtIDQxLjAwNTMxLDQwLjg0NDA2MiBjIC0xLjEzNzc2OCwxLjEzNzc2NSAtMi45ODIwODgsMS4xMzc3NjUgLTQuMTE5ODYxLDAgTCAyNi4wNjg2MjgsMzAuMDI3MjM0IDE0LjczNzU1MSw0MS4zNTgzMSBjIC0xLjEzNzc3MSwxLjEzNzc3MSAtMi45ODIwOTMsMS4xMzc3NzEgLTQuMTE5ODYxLDAgLTEuMTM3NzcyMiwtMS4xMzc3NjggLTEuMTM3NzcyMiwtMi45ODIwODggMCwtNC4xMTk4NjEgTCAyMS45NDg3NjYsMjUuOTA3MzcyIDExLjEzMTkzOCwxNS4wOTA1NTEgYyAtMS4xMzc3NjQ3LC0xLjEzNzc3MSAtMS4xMzc3NjQ3LC0yLjk4MzU1MyAwLC00LjExOTg2MSAxLjEzNzc3NCwtMS4xMzc3NzIxIDIuOTgyMDk4LC0xLjEzNzc3MjEgNC4xMTk4NjUsMCBMIDI2LjA2ODYyOCwyMS43ODc1MTIgMzYuMzY5NzM5LDExLjQ4NjM5OSBjIDEuMTM3NzY4LC0xLjEzNzc2OCAyLjk4MjA5MywtMS4xMzc3NjggNC4xMTk4NjIsMCAxLjEzNzc2NywxLjEzNzc2OSAxLjEzNzc2NywyLjk4MjA5NCAwLDQuMTE5ODYyIEwgMzAuMTg4NDg5LDI1LjkwNzM3MiA0MS4wMDUzMSwzNi43MjQxOTcgYyAxLjEzNzc3MSwxLjEzNzc2NyAxLjEzNzc3MSwyLjk4MjA5MSAwLDQuMTE5ODY1IHoiIC8+Cjwvc3ZnPgo=\";\n        case \"loading\":\n            return \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0nMTIwcHgnIGhlaWdodD0nMTIwcHgnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIiBjbGFzcz0idWlsLXJpbmctYWx0Ij4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0ibm9uZSIgY2xhc3M9ImJrIj48L3JlY3Q+CiAgPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iNDAiIHN0cm9rZT0ibm9uZSIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48L2NpcmNsZT4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjNmY2ZjZmIiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCI+CiAgICA8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJzdHJva2UtZGFzaG9mZnNldCIgZHVyPSIycyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGZyb209IjAiIHRvPSI1MDIiPjwvYW5pbWF0ZT4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9InN0cm9rZS1kYXNoYXJyYXkiIGR1cj0iMnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB2YWx1ZXM9IjE1MC42IDEwMC40OzEgMjUwOzE1MC42IDEwMC40Ij48L2FuaW1hdGU+CiAgPC9jaXJjbGU+Cjwvc3ZnPgo=\";\n        }\n        return \"\";\n    }\n}\n\ncustomElements.define(\"component-icon\", Icon);\n"
  },
  {
    "path": "public/assets/components/loader.js",
    "content": "import { createElement } from \"../lib/skeleton/index.js\";\nimport rxjs from \"../lib/rx.js\";\nimport { animate, opacityIn } from \"../lib/animate.js\";\n\nclass Loader extends HTMLElement {\n    constructor() {\n        super();\n        this.timeout = setTimeout(() => {\n            this.innerHTML = this.render({\n                inline: this.hasAttribute(\"inlined\"),\n            });\n        }, parseInt(this.getAttribute(\"delay\") || \"0\"));\n    }\n\n    disconnectedCallback() {\n        clearTimeout(this.timeout);\n    }\n\n    render({ inline }) {\n        const fixedCss = `\n        position: fixed;\n        left: 0;\n        right: 0;\n        top: calc(50% - 200px);`;\n        return `\n<div class=\"component_loader\">\n    <svg width=\"120px\" height=\"120px\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"xMidYMid\">\n        <rect x=\"0\" y=\"0\" width=\"100\" height=\"100\" fill=\"none\"></rect>\n        <circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"rgba(100%,100%,100%,0.679)\" fill=\"none\" stroke-width=\"10\" stroke-linecap=\"round\"></circle>\n        <circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"#6f6f6f\" fill=\"none\" stroke-width=\"6\" stroke-linecap=\"round\">\n            <animate attributeName=\"stroke-dashoffset\" dur=\"2s\" repeatCount=\"indefinite\" from=\"0\" to=\"502\"></animate>\n            <animate attributeName=\"stroke-dasharray\" dur=\"2s\" repeatCount=\"indefinite\" values=\"150.6 100.4;1 250;150.6 100.4\"></animate>\n        </circle>\n    </svg>\n    <style>\n    .component_loader{\n        text-align: center;\n        margin: 100px auto 0 auto;\n        ${inline ? \"\" : fixedCss}\n    }\n    </style>\n</div>`;\n    }\n}\n\ncustomElements.define(\"component-loader\", Loader);\nexport function createLoader($parent, opts = {}) {\n    const {\n        wait = 250,\n        append = ($loader) => $parent.appendChild($loader),\n    } = opts;\n    const $icon = createElement(`\n            <div class=\"component_loader\">\n                <style>\n                    .component_loader {\n                        display: block;\n                        text-align: center;\n                        margin-top: 100px;\n                    }\n                </style>\n                <component-icon name=\"loading\"></component-icon>\n            </div>\n    `);\n    const alreadyHasLoader = () => !!$parent.querySelector(\".component_loader\");\n    const id = setTimeout(() => {\n        if (alreadyHasLoader()) return;\n        append($icon);\n        animate($icon, { time: 750, keyframes: opacityIn() });\n    }, wait);\n    const cancel = () => {\n        clearTimeout(id);\n        $icon.remove();\n    };\n\n    return rxjs.pipe(\n        rxjs.tap(() => cancel()),\n        rxjs.finalize(() => cancel())\n    );\n}\n\n// > after this should be deprecated\nexport default createElement(\"<component-loader></component-loader>\");\nexport function toggle($node, show = false) {\n    if (show === true) return rxjs.tap(() => $node.appendChild(createElement(\"<component-loader></component-loader>\")));\n    else return rxjs.tap(() => $node.querySelector(\"component-loader\")?.remove());\n}\n"
  },
  {
    "path": "public/assets/components/modal.css",
    "content": ".component_modal{\n    position: fixed;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    background: #f2f3f5f0;\n    z-index: 1000;\n    display: flex;\n}\n.component_modal > div{\n    box-shadow: 1px 2px 20px rgba(0, 0, 0, 0.1);\n    background: white;\n    width: 80%;\n    max-width: 320px;\n    padding: 20px 20px 0 20px;\n    border-radius: 2px;\n    margin: auto;\n}\n\n.dark-mode .component_modal{\n    background: var(--bg-color);\n    color: rgba(0,0,0,0.7);\n}\n\n.component_popup .popup--content {\n  font-size: 1.1em;\n  margin: 0;\n}\n.component_popup .popup--content p {\n    margin: 0;\n}\n.component_popup .popup--content .modal-error-message {\n    font-size: 15px;\n}\n.component_popup .buttons {\n  margin: 15px -20px 0 -20px;\n  display: flex;\n}\n.component_popup .buttons > div {\n    display: flex;\n    width: 100%;\n}\n.component_popup .buttons [type=\"submit\"] {\n    border-radius: 10px 0 0;\n}\n.component_popup .buttons > button {\n    width: 50%;\n    margin-left: auto;\n}\n.component_popup .buttons button {\n    text-transform: uppercase;\n}\n.component_popup .modal-error-message {\n    color: var(--error);\n}\n.component_popup .center {\n    text-align: center;\n}\n"
  },
  {
    "path": "public/assets/components/modal.js",
    "content": "import { createElement } from \"../lib/skeleton/index.js\";\nimport assert from \"../lib/assert.js\";\nimport rxjs, { applyMutation } from \"../lib/rx.js\";\nimport { animate } from \"../lib/animate.js\";\nimport { qs, qsa } from \"../lib/dom.js\";\nimport { loadCSS } from \"../helpers/loader.js\";\n\nexport function createModal(opts) {\n    const $dom = assert.type(qs(document.body, \"component-modal\"), HTMLElement);\n    assert.type($dom, ModalComponent);\n\n    return ($node, fn) => $dom.trigger($node, { onQuit: fn, ...opts });\n}\n\nexport const MODAL_LEFT_BUTTON = 1;\nexport const MODAL_RIGHT_BUTTON = 2;\nexport const MODAL_QUIT = 0;\n\nconst $modal = createElement(`\n    <div class=\"component_modal\" id=\"modal-box\" role=\"dialog\" aria-modal=\"true\">\n        <div>\n            <div class=\"component_popup\">\n                <div class=\"popup--content\">\n                    <div class=\"modal-message\" data-bind=\"body\"><!-- MODAL BODY --></div>\n                </div>\n                <div class=\"buttons\">\n                    <button type=\"button\" disabled></button>\n                    <button type=\"submit\" class=\"emphasis\" disabled></button>\n                </div>\n            </div>\n        </div>\n    </div>\n`);\nconst $app = qs(document.body, \"#app\");\n\nclass ModalComponent extends HTMLElement {\n    async connectedCallback() {\n        await loadCSS(import.meta.url, \"./modal.css\");\n    }\n\n    trigger($node, { withButtonsLeft = null, withButtonsRight = null, onQuit = (_a, _b) => Promise.resolve() }) {\n        const close$ = new rxjs.Subject();\n\n        // feature: build the dom\n        qs($modal, `[data-bind=\"body\"]`).replaceChildren($node);\n        $app.setAttribute(\"inert\", \"true\");\n        this.replaceChildren($modal);\n        qsa($modal, \".component_popup > div.buttons > button\").forEach(($button, i) => {\n            assert.truthy(i >= 0 && i <= 2);\n            let currentLabel = null;\n            let buttonIndex = null;\n            if (i === 0) {\n                currentLabel = withButtonsLeft;\n                buttonIndex = MODAL_LEFT_BUTTON;\n            } else if (i === 1) {\n                currentLabel = withButtonsRight;\n                buttonIndex = MODAL_RIGHT_BUTTON;\n            }\n\n            if (currentLabel !== null) {\n                $button.classList.remove(\"hidden\");\n                $button.removeAttribute(\"disabled\");\n                $button.textContent = currentLabel;\n                $button.onclick = () => close$.next(buttonIndex);\n            } else {\n                $button.classList.add(\"hidden\");\n                $button.setAttribute(\"disabled\", \"true\");\n            }\n        });\n        effect(rxjs.fromEvent($modal, \"mousedown\").pipe(\n            rxjs.filter((e) => e.target.getAttribute(\"id\") === \"modal-box\"),\n            rxjs.tap(() => close$.next(MODAL_QUIT)),\n        ));\n        effect(rxjs.fromEvent(window, \"keydown\").pipe(\n            rxjs.filter((e) => e.keyCode === 27),\n            rxjs.tap(() => close$.next(MODAL_QUIT)),\n        ));\n\n        // feature: closing the modal\n        const $body = () => qs($modal, \"div > div\");\n        effect(close$.pipe(\n            rxjs.tap(() => $app.removeAttribute(\"inert\")),\n            rxjs.mergeMap((data) => onQuit(data, $node) || Promise.resolve()),\n            rxjs.tap(() => animate($body(), {\n                time: 200,\n                keyframes: [\n                    { opacity: 1, transform: \"translateY(0)\" },\n                    { opacity: 0, transform: \"translateY(20px)\" },\n                ],\n            })),\n            rxjs.delay(100),\n            rxjs.tap(() => animate($modal, {\n                time: 200,\n                keyframes: [{ opacity: 1 }, { opacity: 0 }],\n            })),\n            rxjs.mapTo([]), applyMutation($modal, \"remove\"),\n            rxjs.tap(free),\n        ));\n\n        // feature: animate opening\n        effect(rxjs.of(null).pipe(\n            rxjs.tap(() => animate($modal, {\n                onEnter: () => $body().style.setProperty(\"opacity\", \"0\"),\n                onExit: () => $body().style.setProperty(\"opacity\", \"1\"),\n                time: 250,\n                keyframes: [\n                    { opacity: 0 },\n                    { opacity: 1 },\n                ],\n            })),\n            rxjs.delay(50),\n            rxjs.tap(() => animate($body(), {\n                time: 200,\n                keyframes: [\n                    { opacity: 0, transform: \"translateY(10px)\" },\n                    { opacity: 1, transform: \"translateY(0)\" },\n                ],\n            })),\n        ));\n\n        return (id) => close$.next(id);\n    }\n}\ncustomElements.define(\"component-modal\", ModalComponent);\n\nlet _observables = [];\nconst effect = (obs) => _observables.push(obs.subscribe());\nconst free = () => {\n    for (let i = 0; i < _observables.length; i++) {\n        _observables[i].unsubscribe();\n    }\n    _observables = [];\n};\n"
  },
  {
    "path": "public/assets/components/notification.css",
    "content": ".component_notification {\n    position: fixed;\n    bottom: 20px;\n    left: 20px;\n    right: 70px;\n    font-size: 0.95em;\n    z-index: 1001;\n}\n.component_notification .component_notification--container {\n    overflow: hidden;\n    width: 400px;\n    text-align: left;\n    display: inline-block;\n    padding: 15px 20px 15px 15px;\n    border-radius: 2px;\n    box-shadow: rgba(158, 163, 172, 0.3) 5px 5px 20px;\n    display: flex;\n    align-items: center;\n}\n.component_notification .component_notification--container.info {\n    background: var(--color);\n    color: rgba(255, 255, 255, 0.8);\n}\n.dark-mode .component_notification .component_notification--container.info {\n    color: rgba(0, 0, 0, 0.8);\n}\n.component_notification .component_notification--container.error {\n    background: var(--error);\n    color: rgba(0, 0, 0, 0.5);\n}\n.component_notification .component_notification--container.success {\n    background: var(--success);\n    color: rgba(0, 0, 0, 0.5);\n}\n.component_notification .component_notification--container .message {\n    flex: 1 1 auto;\n    max-height: 92px;\n    max-width: 100%;\n    overflow: hidden;\n}\n.component_notification .component_notification--container .close {\n    cursor: pointer;\n    padding: 0 2px;\n}\n.component_notification .component_notification--container .close .component_icon {\n    height: 18px;\n}\n\n@media (max-width: 490px) {\n    .component_notification {\n        bottom: 0px;\n        left: 0px;\n        right: 0px;\n    }\n    .component_notification .component_notification--container {\n        width: 100%;\n        box-sizing: border-box;\n    }\n}\n\n.component_notification .component_notification--container {\n    box-shadow: rgba(0, 0, 0, 0.3) 5px 5px 20px;\n}\n"
  },
  {
    "path": "public/assets/components/notification.js",
    "content": "import { createElement } from \"../lib/skeleton/index.js\";\nimport { qs } from \"../lib/dom.js\";\nimport { ApplicationError } from \"../lib/error.js\";\nimport { animate, slideYIn, slideYOut } from \"../lib/animate.js\";\nimport { loadCSS } from \"../helpers/loader.js\";\n\nconst createNotification = async(msg, type) => {\n    const $notification = createElement(`\n        <span class=\"component_notification\" role=\"alert\">\n            <div class=\"no-select\">\n                <div class=\"component_notification--container ${type}\">\n                    <div class=\"message\"></div>\n                    <div class=\"close\">\n                        <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MS45NzYgNTEuOTc2Ij4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjUzMzMzMjg1O3N0cm9rZS13aWR0aDoxLjQ1NjgxMTE5IiBkPSJtIDQxLjAwNTMxLDQwLjg0NDA2MiBjIC0xLjEzNzc2OCwxLjEzNzc2NSAtMi45ODIwODgsMS4xMzc3NjUgLTQuMTE5ODYxLDAgTCAyNi4wNjg2MjgsMzAuMDI3MjM0IDE0LjczNzU1MSw0MS4zNTgzMSBjIC0xLjEzNzc3MSwxLjEzNzc3MSAtMi45ODIwOTMsMS4xMzc3NzEgLTQuMTE5ODYxLDAgLTEuMTM3NzcyMiwtMS4xMzc3NjggLTEuMTM3NzcyMiwtMi45ODIwODggMCwtNC4xMTk4NjEgTCAyMS45NDg3NjYsMjUuOTA3MzcyIDExLjEzMTkzOCwxNS4wOTA1NTEgYyAtMS4xMzc3NjQ3LC0xLjEzNzc3MSAtMS4xMzc3NjQ3LC0yLjk4MzU1MyAwLC00LjExOTg2MSAxLjEzNzc3NCwtMS4xMzc3NzIxIDIuOTgyMDk4LC0xLjEzNzc3MjEgNC4xMTk4NjUsMCBMIDI2LjA2ODYyOCwyMS43ODc1MTIgMzYuMzY5NzM5LDExLjQ4NjM5OSBjIDEuMTM3NzY4LC0xLjEzNzc2OCAyLjk4MjA5MywtMS4xMzc3NjggNC4xMTk4NjIsMCAxLjEzNzc2NywxLjEzNzc2OSAxLjEzNzc2NywyLjk4MjA5NCAwLDQuMTE5ODYyIEwgMzAuMTg4NDg5LDI1LjkwNzM3MiA0MS4wMDUzMSwzNi43MjQxOTcgYyAxLjEzNzc3MSwxLjEzNzc2NyAxLjEzNzc3MSwyLjk4MjA5MSAwLDQuMTE5ODY1IHoiIC8+Cjwvc3ZnPgo=\" alt=\"close\">\n                    </div>\n                </div>\n            </div>\n        </span>\n    `);\n    if (msg instanceof HTMLElement) qs($notification, \".message\").appendChild(msg);\n    else qs($notification, \".message\").innerText = msg;\n    return $notification;\n};\n\nclass NotificationComponent extends HTMLElement {\n    buffer = [];\n\n    async connectedCallback() {\n        await loadCSS(import.meta.url, \"./notification.css\");\n    }\n\n    async trigger(message, type) {\n        if (this.buffer.length > 20) this.buffer.pop(); // failsafe\n        this.buffer.push({ message, type });\n        if (this.buffer.length !== 1) {\n            const $close = this.querySelector(\".close\");\n            if (!($close instanceof HTMLElement) || !$close.onclick) return;\n            $close.onclick(new PointerEvent(\"mousedown\"));\n            return;\n        }\n        await this.run();\n    }\n\n    async run() {\n        if (this.buffer.length === 0) return;\n        const { message, type } = this.buffer[0];\n        const $notification = await createNotification(message, type);\n        this.replaceChildren($notification);\n        await animate($notification, {\n            keyframes: slideYIn(50),\n            time: 100,\n        });\n        const ids = [];\n        await Promise.race([\n            new Promise((done) => ids.push(setTimeout(() => {\n                done(new MouseEvent(\"mousedown\"));\n            }, this.buffer.length === 1 ? 8000 : 800))),\n            new Promise((done) => ids.push(setTimeout(() => {\n                const $close = $notification.querySelector(\".close\");\n                if (!($close instanceof HTMLElement)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: notification close button missing\");\n                $close.onclick = done;\n            }, 1000))),\n        ]);\n        ids.forEach((id) => clearTimeout(id));\n        await animate($notification, {\n            keyframes: slideYOut(10),\n            time: 200,\n        });\n        $notification.remove();\n        this.buffer.shift();\n        await this.run();\n    }\n}\n\ncustomElements.define(\"component-notification\", NotificationComponent);\n\nfunction find() {\n    const $dom = document.body.querySelector(\"component-notification\");\n    if (!($dom instanceof NotificationComponent)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: wrong type notification component\");\n    return $dom;\n}\n\nexport default class Notification {\n    static info(msg) {\n        find().trigger(msg, \"info\");\n    }\n\n    static success(msg) {\n        find().trigger(msg, \"success\");\n    }\n\n    static error(msg) {\n        find().trigger(msg, \"error\");\n    }\n}\n"
  },
  {
    "path": "public/assets/components/sidebar.css",
    "content": "[data-bind=\"sidebar\"] {\n    z-index: 1;\n    display: flex;\n    width: 250px;\n}\n@media screen and (min-width: 1600px) {\n    .component_filemanager_shell [data-bind=\"sidebar\"] { width: 300px; }\n}\n@media screen and (min-width: 2000px) {\n    .component_filemanager_shell [data-bind=\"sidebar\"] { width: 330px; }\n}\n.component_filemanager_shell .component_sidebar {\n    flex-grow: 1;\n    padding: 3px 0px 25px 0px;\n    overflow-y: scroll;\n    height: 100%;\n    direction: rtl;\n    box-sizing: border-box;\n}\n.component_filemanager_shell .component_sidebar .component_skeleton {\n    margin-bottom: 5px;\n    width: calc(100% - 5px);\n    margin-left: 5px;\n    opacity: 0;\n    animation-duration: 0.5s;\n    animation-delay: 1s;\n    animation-name: skeleton-appear;\n    animation-fill-mode: forwards;\n    animation-iteration-count: 1;\n}\n@keyframes skeleton-appear {\n    from { opacity: 0; }\n    to { opacity: 1; }\n}\n.component_filemanager_shell .component_sidebar > div { direction: ltr; }\n.component_filemanager_shell .component_sidebar h3 {\n    display: flex;\n    margin-bottom: 5px;\n    margin-top: 20px;\n    color: var(--light);\n    font-size: 0.9rem;\n    opacity: 0.8;\n    padding-left: 15px;\n    text-transform: capitalize;\n}\nbody.touch-no .component_filemanager_shell .component_sidebar h3 img:hover {\n    filter: drop-shadow(0px 0px 4px #e2e2e2);\n}\n.component_filemanager_shell .component_sidebar h3 img,\n.component_filemanager_shell .component_sidebar h3 svg,\n.component_filemanager_shell .component_sidebar h3 component-icon img {\n    width: 16px;\n    padding-right: 6px;\n    margin-left: -1px;\n}\n.component_filemanager_shell .component_sidebar .component_icon\n.component_filemanager_shell .component_sidebar h3 input::placeholder {\n    text-transform: capitalize;\n    color: var(--light);\n}\n.component_filemanager_shell .component_sidebar h3 input {\n    border: none;\n    color: var(--color);\n    font-weight: bold;\n    font-size: inherit;\n    background: inherit;\n}\n.component_filemanager_shell .component_sidebar [data-bind=\"your-files\"] { margin-bottom: 30px; }\n.component_filemanager_shell .component_sidebar [data-bind] > ul { margin-left: 0px; }\n.component_filemanager_shell .component_sidebar ul {\n    margin-top: 0px;\n    padding: 0;\n    margin: 0 0 0 5px;\n    list-style-type: none;\n}\n@media screen and (min-width: 1600px) {\n    .component_filemanager_shell .component_sidebar ul { margin-left: 7px; }\n}\n@media screen and (min-width: 2000px) {\n    .component_filemanager_shell .component_sidebar ul { margin-left: 10px; }\n}\n.component_filemanager_shell .component_sidebar ul li {\n    margin-top: 0px;\n    padding-left: 5px;\n    padding-right: 2px;\n}\n.component_filemanager_shell .search .component_sidebar ul {\n    margin: 0;\n}\n.component_filemanager_shell .search .component_sidebar ul ul li {\n    padding-left: 0;\n}\n.component_filemanager_shell .component_sidebar a {\n    display: flex;\n    padding: 5px 5px 5px 10px;\n    width: 100%;\n    box-sizing: border-box;\n    letter-spacing: -0.2px;\n}\n.component_filemanager_shell .component_sidebar .placeholder {\n    border: 2px dashed var(--border);\n    background: inherit;\n    border-radius: 4px;\n    color: var(--light);\n    box-sizing: border-box;\n    padding: 5px;\n    font-family: inherit;\n}\n\nbody.touch-no .component_filemanager_shell .component_sidebar a:not([aria-selected=\"true\"]):hover,\n.component_filemanager_shell .component_sidebar a[aria-selected=\"true\"] {\n    background: var(--border);\n    border-radius: 3px;\n}\n.component_filemanager_shell .component_sidebar a.highlight {\n    color: var(--bg-color);\n    background: var(--dark);\n    border-radius: 3px;\n}\n.component_filemanager_shell .component_sidebar .component_icon {\n    width: 16px;\n    padding-right: 5px;\n    opacity: 0.85;\n}\n.component_filemanager_shell .component_sidebar a > div {\n    padding-left: 2px;\n    text-transform: capitalize;\n}\n.component_filemanager_shell .resizer {\n    width: 10px;\n    height: 100%;\n    align-self: center;\n    cursor: ew-resize;\n}\n.component_filemanager_shell li.pointer {\n    cursor: pointer;\n}\n\n.component_filemanager_shell .component_sidebar [data-bind=\"taglist\"] a {\n    margin-bottom: 2px;\n    justify-content: space-between;\n    font-size: 0.95rem;\n    padding-left: 7px;\n}\n.component_filemanager_shell .component_sidebar [data-bind=\"taglist\"] a > div {\n    text-transform: none;\n}\n.component_filemanager_shell .component_sidebar [data-bind=\"taglist\"] a .hash:before {\n    content: \"#\";\n    font-size: 0.9rem;\n    opacity: 0.5;\n}\n.component_filemanager_shell .component_sidebar [data-bind=\"taglist\"] a[aria-selected=\"true\"] {\n    background: var(--light);\n    color: #f2f2f2;\n    box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);\n}\n.dark-mode .component_filemanager_shell .component_sidebar [data-bind=\"taglist\"] a[aria-selected=\"true\"] {\n    background: var(--border);\n}\n.component_filemanager_shell .component_sidebar [data-bind=\"taglist\"] a[aria-selected=\"true\"] svg {\n    opacity: 1;\n}\n.component_filemanager_shell .component_sidebar [data-bind=\"taglist\"] a svg {\n    opacity: 0;\n    background: rgba(255, 255, 255, 0.03);\n    align-self: center;\n    width: 13px;\n    height: 13px;\n    border-radius: 50%;\n    padding: 4px;\n}\n"
  },
  {
    "path": "public/assets/components/sidebar.js",
    "content": "import { createElement, createRender, onDestroy } from \"../lib/skeleton/index.js\";\nimport rxjs, { effect, onClick } from \"../lib/rx.js\";\nimport { qs } from \"../lib/dom.js\";\nimport { settingsGet, settingsSave } from \"../lib/store.js\";\nimport { loadCSS } from \"../helpers/loader.js\";\nimport t from \"../locales/index.js\";\nimport { getCurrentPath } from \"../pages/viewerpage/common.js\";\nimport { generateSkeleton } from \"./skeleton.js\";\n\nimport ctrlNavigationPane from \"./sidebar_files.js\";\nimport ctrlTagPane from \"./sidebar_tags.js\";\n\nexport default async function ctrlSidebar(render, {}) {\n    if (new URL(location.toString()).searchParams.get(\"nav\") === \"false\") return;\n    else if (window.self !== window.top) return;\n    else if (document.body.clientWidth < 850) return;\n\n    const $sidebar = render(createElement(`\n        <div class=\"component_sidebar\"><div>\n            <h3 class=\"no-select\">\n                <img src=\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgYXJpYS1oaWRkZW49InRydWUiCiAgIGZvY3VzYWJsZT0iZmFsc2UiCiAgIHJvbGU9ImltZyIKICAgY2xhc3M9Im9jdGljb24gb2N0aWNvbi1zaWRlYmFyLWV4cGFuZCIKICAgdmlld0JveD0iMCAwIDE2IDE2IgogICB3aWR0aD0iMTYiCiAgIGhlaWdodD0iMTYiCiAgIGZpbGw9ImN1cnJlbnRDb2xvciIKICAgc3R5bGU9ImRpc3BsYXk6IGlubGluZS1ibG9jazsgdXNlci1zZWxlY3Q6IG5vbmU7IHZlcnRpY2FsLWFsaWduOiB0ZXh0LWJvdHRvbTsgb3ZlcmZsb3c6IHZpc2libGU7IgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmc3MjI3IgogICBzb2RpcG9kaTpkb2NuYW1lPSJnaXRodWJmb2xkLnN2ZyIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMS4yLjIgKGIwYTg0ODY1NDEsIDIwMjItMTItMDEpIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM3MjMxIiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0ibmFtZWR2aWV3NzIyOSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiMwMDAwMDAiCiAgICAgYm9yZGVyb3BhY2l0eT0iMC4yNSIKICAgICBpbmtzY2FwZTpzaG93cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VjaGVja2VyYm9hcmQ9IjAiCiAgICAgaW5rc2NhcGU6ZGVza2NvbG9yPSIjZDFkMWQxIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTp6b29tPSIxNC43NSIKICAgICBpbmtzY2FwZTpjeD0iNC4yMDMzODk4IgogICAgIGlua3NjYXBlOmN5PSI4IgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTgxNyIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMzk3IgogICAgIGlua3NjYXBlOndpbmRvdy14PSI3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSIzNCIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9InN2ZzcyMjciIC8+CiAgPHBhdGgKICAgICBkPSJtNC4xNzcgNy44MjMgMi4zOTYtMi4zOTZBLjI1LjI1IDAgMCAxIDcgNS42MDR2NC43OTJhLjI1LjI1IDAgMCAxLS40MjcuMTc3TDQuMTc3IDguMTc3YS4yNS4yNSAwIDAgMSAwLS4zNTRaIgogICAgIGlkPSJwYXRoNzIyMyIKICAgICBzdHlsZT0iZmlsbDojNTc1OTVhO2ZpbGwtb3BhY2l0eToxIiAvPgogIDxwYXRoCiAgICAgZD0iTTAgMS43NUMwIC43ODQuNzg0IDAgMS43NSAwaDEyLjVDMTUuMjE2IDAgMTYgLjc4NCAxNiAxLjc1djEyLjVBMS43NSAxLjc1IDAgMCAxIDE0LjI1IDE2SDEuNzVBMS43NSAxLjc1IDAgMCAxIDAgMTQuMjVabTEuNzUtLjI1YS4yNS4yNSAwIDAgMC0uMjUuMjV2MTIuNWMwIC4xMzguMTEyLjI1LjI1LjI1SDkuNXYtMTNabTEyLjUgMTNhLjI1LjI1IDAgMCAwIC4yNS0uMjVWMS43NWEuMjUuMjUgMCAwIDAtLjI1LS4yNUgxMXYxM1oiCiAgICAgaWQ9InBhdGg3MjI1IgogICAgIHN0eWxlPSJmaWxsOiM1NzU5NWE7ZmlsbC1vcGFjaXR5OjEiIC8+Cjwvc3ZnPgo=\" alt=\"close\">\n                <input type=\"text\" placeholder=\"${t(\"Your Files\")}\" />\n            </h3>\n            <div data-bind=\"your-files\">\n                ${generateSkeleton(2)}\n            </div>\n            <div data-bind=\"your-tags\">\n                ${generateSkeleton(2)}\n            </div>\n        </div>\n    `));\n    withInstantLoad($sidebar);\n    withResize($sidebar);\n\n    const path = getCurrentPath(\"(/view/|/files/)\");\n\n    // fature: file navigation pane\n    const $files = qs($sidebar, `[data-bind=\"your-files\"]`);\n    ctrlNavigationPane(createRender($files), { $sidebar, path });\n\n    // feature: tag viewer\n    const $tags = qs($sidebar, `[data-bind=\"your-tags\"]`);\n    effect(rxjs.merge(\n        rxjs.of(null),\n        rxjs.fromEvent(window, \"filestash::tag\"),\n    ).pipe(\n        rxjs.tap(() => ctrlTagPane(createRender($tags), {\n            tags: [...$tags.querySelectorAll(\"a\")].map(($tag) => $tag.innerText.trim()),\n            path,\n        })),\n    ));\n\n    // feature: visibility of the sidebar\n    const isVisible = () => settingsGet({ visible: true }, \"sidebar\").visible;\n    const forceRefresh = () => window.dispatchEvent(new Event(\"resize\"));\n    effect(rxjs.merge(rxjs.fromEvent(window, \"keydown\")).pipe(\n        rxjs.filter((e) => e.key === \"b\" && e.ctrlKey === true),\n        rxjs.tap(() => {\n            settingsSave({ visible: $sidebar.classList.contains(\"hidden\") }, \"sidebar\");\n            isVisible() ? $sidebar.classList.remove(\"hidden\") : $sidebar.classList.add(\"hidden\");\n            forceRefresh();\n        }),\n    ));\n    effect(rxjs.merge(\n        rxjs.fromEvent(window, \"resize\"),\n        rxjs.of(null),\n    ).pipe(\n        rxjs.tap(() => {\n            const $breadcrumbButton = qs(document.body, \"[alt=\\\"sidebar-open\\\"]\");\n            if (document.body.clientWidth < 1100) $sidebar.classList.add(\"hidden\");\n            else if (isVisible()) {\n                $sidebar.classList.remove(\"hidden\");\n                $breadcrumbButton.classList.add(\"hidden\");\n            } else {\n                $sidebar.classList.add(\"hidden\");\n                $breadcrumbButton.classList.remove(\"hidden\");\n            }\n        }),\n        rxjs.catchError((err) => {\n            if (err instanceof DOMException) return rxjs.EMPTY;\n            throw err;\n        }),\n    ));\n    effect(onClick(qs($sidebar, `img[alt=\"close\"]`)).pipe(\n        rxjs.tap(() => {\n            settingsSave({ visible: false }, \"sidebar\");\n            $sidebar.classList.add(\"hidden\");\n            forceRefresh();\n        }),\n    ));\n}\n\nconst withResize = (function() {\n    let memory = null;\n    return ($sidebar) => {\n        const $resize = createElement(`<div class=\"resizer\"></div>`);\n        effect(rxjs.fromEvent($resize, \"mousedown\").pipe(\n            rxjs.mergeMap((e0) => rxjs.fromEvent(document, \"mousemove\").pipe(\n                rxjs.takeUntil(rxjs.fromEvent(document, \"mouseup\")),\n                rxjs.startWith(e0),\n                rxjs.pairwise(),\n                rxjs.map(([prevX, currX]) => currX.clientX - prevX.clientX),\n                rxjs.scan((width, delta) => width + delta, $sidebar.clientWidth),\n            )),\n            rxjs.startWith(memory),\n            rxjs.filter((w) => !!w),\n            rxjs.map((w) => Math.min(Math.max(w, 250), 350)),\n            rxjs.tap((w) => {\n                $sidebar.style.width = `${w}px`;\n                memory = w;\n            }),\n        ));\n        $sidebar.appendChild($resize);\n    };\n}());\n\nconst withInstantLoad = (function() {\n    const state = { scrollTop: 0, $cache: null };\n    return ($sidebar) => {\n        if (state.$cache) {\n            $sidebar.replaceChildren(state.$cache);\n            $sidebar.firstElementChild.scrollTop = state.scrollTop;\n        }\n        onDestroy(() => {\n            state.$cache = $sidebar.firstElementChild?.cloneNode(true);\n            state.scrollTop = $sidebar.firstElementChild.scrollTop;\n        });\n    };\n}());\n\nexport function init() {\n    return loadCSS(import.meta.url, \"./sidebar.css\");\n}\n"
  },
  {
    "path": "public/assets/components/sidebar_files.js",
    "content": "import rxjs, { effect } from \"../lib/rx.js\";\nimport { createElement, createRender } from \"../lib/skeleton/index.js\";\nimport { toHref } from \"../lib/skeleton/router.js\";\nimport { qs, qsa, safe } from \"../lib/dom.js\";\nimport { forwardURLParams } from \"../lib/path.js\";\nimport cache from \"../pages/filespage/cache.js\";\nimport { extractPath, isDir, isNativeFileUpload } from \"../pages/filespage/helper.js\";\nimport { mv as mvVL, withVirtualLayer } from \"../pages/filespage/model_virtual_layer.js\";\nimport { hooks, mv as mv$ } from \"../pages/filespage/model_files.js\";\nimport ctrlError from \"../pages/ctrl_error.js\";\n\nexport default async function ctrlNavigationPane(render, { $sidebar, path }) {\n    // feature: init dom\n    const $fs = document.createDocumentFragment();\n    const dirname = path.replace(new RegExp(\"[^\\/]*$\"), \"\");\n    const chunks = dirname.split(\"/\");\n    for (let i=1; i<chunks.length; i++) {\n        const cpath = chunks.slice(0, i).join(\"/\") + \"/\";\n        const $ul = await _createListOfFiles(cpath, {\n            basename: chunks[i],\n            dirname,\n        });\n        if (cpath === \"/\") $fs.appendChild($ul);\n        else {\n            const $menuitem = $fs.querySelector(`[data-path=\"${CSS.escape(cpath)}\"] ul`);\n            if (!$menuitem) break;\n            $menuitem.appendChild($ul);\n        }\n    }\n    render($fs);\n\n    // feature: listen for updates\n    effect(new rxjs.Observable((subscriber) => {\n        const cleaners = [\n            hooks.ls.listen(({ path }) => subscriber.next(path)),\n            hooks.mutation.listen(async({ op, path }) => {\n                if ([\"mv\", \"mkdir\", \"rm\"].indexOf(op) === -1) return;\n                subscriber.next(path);\n            }),\n        ];\n        return () => cleaners.map((fn) => fn());\n    }).pipe(\n        rxjs.mergeMap(async(path) => {\n            const display = path === \"/\" ? render : createRender(qs($sidebar, `[data-path=\"${CSS.escape(path)}\"] ul`));\n            display(await _createListOfFiles(path, {}));\n        }),\n        rxjs.catchError((err) => {\n            if (err instanceof DOMException) return rxjs.EMPTY;\n            return ctrlError()(err);\n        }),\n    ));\n\n    // feature: highlight current selection\n    try {\n        const $active = qs($sidebar, `[data-path=\"${dirname}\"] a`);\n        const checkVisible = ($el) => {\n            const rect = $el.getBoundingClientRect();\n            return rect.top >= 0 &&\n                rect.left >= 0 &&\n                rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&\n                rect.right <= (window.innerWidth || document.documentElement.clientWidth);\n        };\n        $active.setAttribute(\"aria-selected\", \"true\");\n        const tags = new URLSearchParams(location.search).getAll(\"tag\").length;\n        if (checkVisible($active) === false && tags === 0) {\n            $active.offsetTop < window.innerHeight\n                ? $sidebar.firstChild.scrollTo({ top: 0, behavior: \"smooth\" })\n                : $active.scrollIntoView({ behavior: \"smooth\", block: \"nearest\" });\n        }\n    } catch (err) {}\n\n    // feature: quick search\n    effect(rxjs.fromEvent(qs($sidebar, \"h3 input\"), \"keydown\").pipe(\n        rxjs.debounceTime(200),\n        rxjs.tap((e) => {\n            const inputValue = e.target.value.toLowerCase();\n            qsa($sidebar, \"[data-bind=\\\"your-files\\\"] li a\").forEach(($li) => {\n                if (inputValue === \"\") {\n                    $li.classList.remove(\"hidden\");\n                    $sidebar.classList.remove(\"search\");\n                    return;\n                }\n                $sidebar.classList.add(\"search\");\n                qs($li, \"div\").textContent.toLowerCase().indexOf(inputValue) === -1\n                    ? $li.classList.add(\"hidden\")\n                    : $li.classList.remove(\"hidden\");\n            });\n        }),\n        rxjs.finalize(() => $sidebar.classList.remove(\"search\")),\n    ));\n}\n\nconst mv = (from, to) => withVirtualLayer(\n    mv$(from, to),\n    mvVL(from, to),\n);\n\nasync function _createListOfFiles(path, { basename = null, dirname = null }) {\n    const MAX_DISPLAY = 100;\n    const r = await cache().get(path);\n    const whats = r === null\n        ? (basename ? [basename] : [])\n        : r.files\n            .filter(({ type, name }) => type === \"directory\" && name[0] !== \".\")\n            .map(({ name }) => name)\n            .sort((a, b) => a.localeCompare(b));\n\n    const $lis = document.createDocumentFragment();\n    const $fragment = document.createDocumentFragment();\n    const $ul = document.createElement(\"ul\");\n    for (let i=0; i<whats.length; i++) {\n        const currpath = path + whats[i] + \"/\";\n        const $li = createElement(`\n            <li data-path=\"${safe(currpath)}\" title=\"${safe(currpath)}\" class=\"no-select\">\n                <a data-link href=\"${safe(forwardURLParams(toHref(\"/files\" + encodeURIComponent(currpath).replaceAll(\"%2F\", \"/\")), [\"share\", \"canary\"]))}\" draggable=\"false\" aria-selected=\"false\">\n                    <img class=\"component_icon\" src=\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgYXJpYS1oaWRkZW49InRydWUiCiAgIGZvY3VzYWJsZT0iZmFsc2UiCiAgIGNsYXNzPSJvY3RpY29uIG9jdGljb24tZmlsZS1kaXJlY3RvcnktZmlsbCIKICAgdmlld0JveD0iMCAwIDE2IDE2IgogICB3aWR0aD0iMTYiCiAgIGhlaWdodD0iMTYiCiAgIGZpbGw9ImN1cnJlbnRDb2xvciIKICAgc3R5bGU9ImRpc3BsYXk6IGlubGluZS1ibG9jazsgdXNlci1zZWxlY3Q6IG5vbmU7IHZlcnRpY2FsLWFsaWduOiB0ZXh0LWJvdHRvbTsgb3ZlcmZsb3c6IHZpc2libGU7IgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmcxNTgiCiAgIHNvZGlwb2RpOmRvY25hbWU9ImdpdGh1YmZvbGRlci5zdmciCiAgIGlua3NjYXBlOnZlcnNpb249IjEuMi4yIChiMGE4NDg2NTQxLCAyMDIyLTEyLTAxKSIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMTYyIiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0ibmFtZWR2aWV3MTYwIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzAwMDAwMCIKICAgICBib3JkZXJvcGFjaXR5PSIwLjI1IgogICAgIGlua3NjYXBlOnNob3dwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZWNoZWNrZXJib2FyZD0iMCIKICAgICBpbmtzY2FwZTpkZXNrY29sb3I9IiNkMWQxZDEiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGlua3NjYXBlOnpvb209IjcxLjYyNSIKICAgICBpbmtzY2FwZTpjeD0iNy44MTE1MTgzIgogICAgIGlua3NjYXBlOmN5PSI4IgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMjAzNiIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMzk3IgogICAgIGlua3NjYXBlOndpbmRvdy14PSI3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSIzNCIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9InN2ZzE1OCIgLz4KICA8cGF0aAogICAgIGQ9Ik0xLjc1IDFBMS43NSAxLjc1IDAgMCAwIDAgMi43NXYxMC41QzAgMTQuMjE2Ljc4NCAxNSAxLjc1IDE1aDEyLjVBMS43NSAxLjc1IDAgMCAwIDE2IDEzLjI1di04LjVBMS43NSAxLjc1IDAgMCAwIDE0LjI1IDNINy41YS4yNS4yNSAwIDAgMS0uMi0uMWwtLjktMS4yQzYuMDcgMS4yNiA1LjU1IDEgNSAxSDEuNzVaIgogICAgIGlkPSJwYXRoMTU2IgogICAgIHN0eWxlPSJmaWxsOiM1NzU5NWE7ZmlsbC1vcGFjaXR5OjEiIC8+Cjwvc3ZnPgo=\" alt=\"directory\">\n                    <div class=\"ellipsis\">${safe(whats[i])}</div>\n                </a>\n                <ul></ul>\n            </li>\n        `);\n        const $link = qs($li, \"a\");\n        if ($link.getAttribute(\"href\") === \"/files\" + dirname) {\n            $link.removeAttribute(\"href\", \"\");\n            $link.removeAttribute(\"data-link\");\n        } else {\n            $link.ondrop = async(e) => {\n                $link.classList.remove(\"highlight\");\n                const from = e.dataTransfer.getData(\"path\");\n                let to = $link.parentElement.getAttribute(\"data-path\");\n                const [, fromName] = extractPath(from);\n                to += fromName;\n                if (isDir(from)) to += \"/\";\n                if (from === to) return;\n                await mv(from, to).toPromise();\n            };\n            $link.ondragover = (e) => {\n                if (isNativeFileUpload(e)) return;\n                e.preventDefault();\n                $link.classList.add(\"highlight\");\n            };\n            $link.ondragleave = () => {\n                $link.classList.remove(\"highlight\");\n            };\n        }\n\n        if (i <= MAX_DISPLAY) $lis.appendChild($li);\n        else $fragment.appendChild($li);\n        if (i === MAX_DISPLAY) {\n            const $more = createElement(`\n                <li title=\"...\" class=\"no-select pointer\">\n                    <a><div class=\"ellipsis\">...</div></a>\n                </li>\n            `);\n            $lis.appendChild($more);\n            $more.onclick = () => {\n                $ul.appendChild($fragment);\n                $more.remove();\n            };\n        }\n    }\n    $ul.appendChild($lis);\n    return $ul;\n}\n"
  },
  {
    "path": "public/assets/components/sidebar_tags.js",
    "content": "import rxjs, { effect } from \"../lib/rx.js\";\nimport { createElement, createRender } from \"../lib/skeleton/index.js\";\nimport { toHref } from \"../lib/skeleton/router.js\";\nimport ajax from \"../lib/ajax.js\";\nimport { qs, safe } from \"../lib/dom.js\";\nimport { forwardURLParams } from \"../lib/path.js\";\nimport { get as getConfig } from \"../model/config.js\";\nimport { isMobile } from \"../pages/filespage/helper.js\";\nimport t from \"../locales/index.js\";\n\nexport default async function ctrlTagPane(render, { tags, path }) {\n    if (getConfig(\"enable_tags\", false) === false) {\n        render(document.createElement(\"div\"));\n        return;\n    }\n    const $page = createElement(`\n        <div>\n            <h3 class=\"no-select\">\n                <img src=\"data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHZlcnNpb249IjEuMSIgd2lkdGg9IjE2IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPg0KICAgIDxwYXRoIHN0eWxlPSJmaWxsOiAjNTc1OTVhOyIgZD0iTTEgNy43NzVWMi43NUMxIDEuNzg0IDEuNzg0IDEgMi43NSAxaDUuMDI1Yy40NjQgMCAuOTEuMTg0IDEuMjM4LjUxM2w2LjI1IDYuMjVhMS43NSAxLjc1IDAgMCAxIDAgMi40NzRsLTUuMDI2IDUuMDI2YTEuNzUgMS43NSAwIDAgMS0yLjQ3NCAwbC02LjI1LTYuMjVBMS43NTIgMS43NTIgMCAwIDEgMSA3Ljc3NVptMS41IDBjMCAuMDY2LjAyNi4xMy4wNzMuMTc3bDYuMjUgNi4yNWEuMjUuMjUgMCAwIDAgLjM1NCAwbDUuMDI1LTUuMDI1YS4yNS4yNSAwIDAgMCAwLS4zNTRsLTYuMjUtNi4yNWEuMjUuMjUgMCAwIDAtLjE3Ny0uMDczSDIuNzVhLjI1LjI1IDAgMCAwLS4yNS4yNVpNNiA1YTEgMSAwIDEgMSAwIDIgMSAxIDAgMCAxIDAtMloiPjwvcGF0aD4NCjwvc3ZnPg0K\" alt=\"tag\">\n                ${t(\"Tags\")}\n            </h3>\n            <ul>\n                <li data-bind=\"taglist\"></li>\n            </ul>\n        </div>\n    `);\n    const renderTaglist = createRender(qs($page, `[data-bind=\"taglist\"]`));\n    effect(rxjs.merge(\n        tags.length === 0 ? rxjs.EMPTY : rxjs.of({ tags }),\n        ajax({\n            url: forwardURLParams(`api/metadata/search`, [\"share\"]),\n            method: \"POST\",\n            responseType: \"json\",\n            body: {\n                tags: [],\n                path,\n            },\n        }).pipe(\n            rxjs.map(({ responseJSON }) => {\n                const tags = {};\n                Object.values(responseJSON.results).forEach((forms) => {\n                    forms.forEach(({ id, value = \"\" }) => {\n                        if (id !== \"tags\") return;\n                        value.split(\",\").forEach((tag) => {\n                            tags[tag.trim()] = null;\n                        });\n                    });\n                });\n                return { tags: Object.keys(tags).sort(), response: responseJSON.results };\n            }),\n            rxjs.catchError(() => rxjs.of({ tags: [] })),\n        ),\n    ).pipe(\n        // feature: create the DOM\n        rxjs.mergeMap(({ tags, response }) => {\n            render($page);\n            if (tags.length === 0) {\n                renderTaglist(createElement(`<div class=\"placeholder center no-select\">∅</div>`));\n                return rxjs.EMPTY;\n            }\n            const $fragment = document.createDocumentFragment();\n            tags.forEach((name) => {\n                const $tag = createElement(`\n                    <a data-link draggable=\"false\" class=\"no-select\">\n                        <div class=\"ellipsis\">\n                            <span class=\"hash\"></span>\n                            ${safe(name)}\n                        </div>\n                        <svg class=\"component_icon\" draggable=\"false\" alt=\"close\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n                            <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n                            <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n                        </svg>\n                    </a>\n                `);\n                const url = new URL(location.href);\n                if (url.searchParams.getAll(\"tag\").indexOf(name) === -1) {\n                    $tag.setAttribute(\"href\", forwardURLParams(toHref(\"/files\" + path.replace(new RegExp(\"[^\\/]+$\"), \"\") + \"?tag=\" + name), [\"share\", \"tag\"]));\n                } else {\n                    url.searchParams.delete(\"tag\", name);\n                    $tag.setAttribute(\"href\", url.toString());\n                    $tag.setAttribute(\"aria-selected\", \"true\");\n                }\n                $fragment.appendChild($tag);\n            });\n            return rxjs.of({ $list: renderTaglist($fragment), response });\n        }),\n        // feature: tag mouse hover effect\n        rxjs.tap(({ $list, response }) => {\n            if (isMobile) return;\n            else if (!response) return;\n            $list.childNodes.forEach(($tag) => {\n                if ($tag.getAttribute(\"aria-selected\") === \"true\") return;\n                const tagname = $tag.innerText.trim();\n                const paths = [];\n                for (const path in response) {\n                    const form = response[path].find(({ id }) => id === \"tags\");\n                    if (!form) continue;\n                    const tags = form.value.split(\",\").map((val) => val.trim());\n                    if (tags.indexOf(tagname) === -1) continue;\n                    paths.push(path);\n                }\n                $tag.onmouseenter = () => {\n                    const $things = document.querySelectorAll(\".component_thing\");\n                    $things.forEach(($thing) => {\n                        const thingpath = $thing.getAttribute(\"data-path\");\n                        for (let i=0; i<paths.length; i++) {\n                            if (paths[i].indexOf(thingpath) === 0) {\n                                $thing.classList.add(\"hover\");\n                                break;\n                            }\n                        }\n                    });\n                    $tag.onmouseleave = () => $things.forEach(($thing) => $thing.classList.remove(\"hover\"));\n                };\n            });\n        }),\n    ));\n}\n"
  },
  {
    "path": "public/assets/components/skeleton.js",
    "content": "export function generateSkeleton(n) {\n    const tmpl = \"<div class=\\\"component_skeleton\\\"></div>\";\n    let html = \"\";\n    for (let i = 0; i < n; i++) {\n        html += tmpl;\n    }\n    return html;\n}\n"
  },
  {
    "path": "public/assets/css/designsystem.css",
    "content": "@import url(\"./designsystem_input.css\");\n@import url(\"./designsystem_textarea.css\");\n@import url(\"./designsystem_inputgroup.css\");\n@import url(\"./designsystem_checkbox.css\");\n@import url(\"./designsystem_formbuilder.css\");\n@import url(\"./designsystem_button.css\");\n@import url(\"./designsystem_icon.css\");\n@import url(\"./designsystem_container.css\");\n@import url(\"./designsystem_box.css\");\n@import url(\"./designsystem_darkmode.css\");\n@import url(\"./designsystem_skeleton.css\");\n@import url(\"./designsystem_utils.css\");\n@import url(\"./designsystem_alert.css\");\n\n:root {\n    --bg-color: #f9f9fa;\n    --color: #3e4041;\n    --emphasis: #466372;\n    --primary: #9AD1ED;\n    --emphasis-primary: #c5e2f1;\n    --emphasis-secondary: #466372;\n    --light: #57595A;\n    --super-light:  #fafafa;\n    --error: #f26d6d;\n    --success: #63d9b1;\n    --dark: #24272a;\n    --surface: #525659;\n\n    --border: rgba(198,200,204,0.25);\n}\n\nhtml {\n    font-family: system-ui, sans-serif;\n    -webkit-text-size-adjust:100%;\n    font-smoothing: antialiased;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n}\nbody {\n    overflow: hidden;\n    background: var(--bg-color);\n    color: var(--color);\n}\nbody, html {\n    height: 100%;\n    margin: 0;\n}\n\na {\n    color: inherit;\n    text-decoration: none;\n}\n\nselect {\n    -webkit-appearance: none;\n    -moz-appearance: none;\n}\n\nselect:-moz-focusring {\n    color: transparent;\n    outline: none;\n    border: none;\n}\n\nselect::-ms-expand {\n    display: none;\n}\n\nbutton::-moz-focus-inner {\n    border: 0;\n}\n\ninput, textarea, select {\n    transition: border 0.2s;\n    outline: none;\n}\n\nbutton:focus,\na:focus,\na:active,\nbutton::-moz-focus-inner,\ninput[type=\"reset\"]::-moz-focus-inner,\ninput[type=\"button\"]::-moz-focus-inner,\ninput[type=\"submit\"]::-moz-focus-inner,\nselect::-moz-focus-inner,\ninput[type=\"file\"]>input[type=\"button\"]::-moz-focus-inner {\n    outline: none !important;\n}\n\nselect:-moz-focusring {\n    color: transparent;\n    text-shadow: 0 0 0 #000;\n}\n\n#app { height: 100%; }\n\n.connect-form input:hover,\n.connect-form textarea:hover,\n.connect-form input:focus,\n.connect-form textarea:focus {\n    border-color: rgb(154, 209, 237)!important;\n}\n\n.drag-drop {\n    z-index: 2;\n}\n\n.drag-drop.dragging>div {\n    background: rgba(0, 0, 0, 0.1);\n}\n\n\n/* CONNECTION FORM */\n\n.login-form button.active {\n    box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.20);\n}\n\n::-webkit-scrollbar {\n    height: 4px;\n    width: 4px\n}\n\n::-webkit-scrollbar-track {\n    background: rgba(0, 0, 0, .1)\n}\n\n::-webkit-scrollbar-thumb {\n    border-radius: 2px;\n    background: rgba(0, 0, 0, .2);\n}\n\n.scroll-y {\n    flex: 1;\n    overflow-y: scroll;\n    overflow-x: hidden;\n    scrollbar-3dlight-color: #7d7e94;\n    scrollbar-arrow-color: #c1c1d1;\n    scrollbar-darkshadow-color: #2d2c4d;\n    scrollbar-face-color: rgba(0, 0, 0, .1);\n    scrollbar-highlight-color: #7d7e94;\n    scrollbar-shadow-color: #2d2c4d;\n    scrollbar-track-color: rgba(0, 0, 0, .1);\n}\n\n/* skip link */\nbody > a[aria-role=\"navigation\"] {\n    position: absolute;\n    left: -999px;\n    top: auto;\n    width: 1px;\n    height: 1px;\n    overflow: hidden;\n}\nbody > a[aria-role=\"navigation\"]:focus-visible {\n    position: fixed;\n    top: 8px;\n    width: fit-content;\n    height: auto;\n    border-radius: 5px;\n    padding: 5px 10px;\n    outline: 2px solid;\n    z-index: 10;\n    left: 50%;\n    background: white\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_alert.css",
    "content": ".alert {\n    background: var(--bg-color);\n    border-radius: 5px;\n    padding: 20px;\n    margin-top: 20px;\n    margin-bottom: 20px;\n    border: 1px solid rgba(0,0,0,0.05);\n}\n.alert ol, .alert ul {\n    margin: 5px 0;\n    padding: 0 20px;\n}\n.alert.success{\n    background: var(--success);\n}\n.alert.error{\n    background: var(--error);\n    color: var(--bg-color);\n}\n.alert img{\n    max-width: 100%;\n    border-radius: 5px;\n    border: 10px solid white;\n    box-sizing: border-box;\n    margin-top: 5px;\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_box.css",
    "content": ".box{\n    padding: 10px;\n    cursor: pointer;\n    margin: 3px 0;\n    overflow: hidden;\n    position: relative;\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_button.css",
    "content": "button {\n  border: none;\n  margin: 0;\n  padding: 6px;\n  display: inline-block;\n  cursor: pointer;\n  font-size: inherit;\n  border-radius: 3px;\n  color: rgba(0,0,0,0.6);\n  background: inherit;\n  line-height: 1rem;\n}\nbutton.primary {\n    background: var(--primary);\n    color: white;\n}\nbutton.emphasis {\n    background: var(--emphasis);\n    color: white;\n}\nbutton.dark {\n    background: var(--dark);\n    color: white;\n}\nbutton.light {\n    background: #e2e2e2;\n    color: var(--dark);\n}\nbutton.large {\n    width: 100%;\n}\nbutton[disabled] {\n    opacity: 0.9;\n}\n\n.touch-no button.dark:hover, .touch-no button.emphasis:hover, .touch-no button.primary:hover {\n  filter: brightness(95%);\n  transition: 0.2s ease all;\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_checkbox.css",
    "content": ".component_checkbox {\n  display: inline-block;\n  padding: 0;\n  height: 25px;\n  width: 25px;\n  vertical-align: middle;\n  position: relative;\n}\n.component_checkbox input[type=\"checkbox\"], .component_checkbox input[type=\"radio\"] {\n    position: absolute;\n    inset: 0px;\n    opacity: 0;\n}\n.component_checkbox input[type=\"checkbox\"]:focus ~ span, .component_checkbox input[type=\"radio\"]:focus ~ span {\n    border: 2px solid rgba(0, 0, 0, 0.05);\n}\n.component_checkbox input[type=\"checkbox\"]:focus:checked ~ span, .component_checkbox input[type=\"radio\"]:focus:checked ~ span {\n    border: 2px solid rgba(0, 0, 0, 0.05);\n}\n.component_checkbox input[type=\"checkbox\"]:focus-visible ~ span {\n    outline: 2px solid;\n}\n.component_checkbox input[type=\"checkbox\"]:checked ~ span, .component_checkbox input[type=\"radio\"]:checked ~ span {\n    color: white;\n    background: var(--primary) url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHdpZHRoPSIxMiIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgMTIgOSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCiAgPHBhdGggZD0iTTQuNTc1IDguOTc3cy0uNDA0LS4wMDctLjUzNi0uMTY1TC4wNTcgNS42NGwuODI5LTEuMjI3TDQuNDcgNy4yNjggMTAuOTIxLjA4NmwuOTIzIDEuMTAzLTYuODYzIDcuNjRjLS4xMzQtLjAwMy0uNDA2LjE0OC0uNDA2LjE0OHoiIGZpbGw9IiNGRkYiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPg0KPC9zdmc+) 50% 40% no-repeat;\n    border: 2px solid var(--primary);\n}\n.component_checkbox span {\n    border-radius: 3px;\n    position: absolute;\n    left: 0;\n    top: 3px;\n    width: 12px;\n    height: 12px;\n    background-color: rgba(198,200,204,0.3);\n    border: 2px solid rgba(198,200,204,0.3);\n    pointer-events: none;\n    -webkit-user-select: none;\n    -moz-user-select: none;\n    -ms-user-select: none;\n    user-select: none;\n}\n.component_checkbox input[type=\"radio\"] + span {\n    border-radius: 50%;\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_container.css",
    "content": ".component_container{\n    width: 95%;\n    max-width: 800px;\n    margin: 0 auto;\n    padding: 10px;\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_darkmode.css",
    "content": "body.dark-mode {\n    --bg-color: #1e1f22;\n    --color: #f1f1f1;\n    --light: #dfe1e5;\n\n\n    --border: #303438;\n    --dark: #2b2d30;\n}\n\nbody.dark-mode input {\n    color: var(--bg-color);\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_dropdown.css",
    "content": ".component_dropdown {\n    position: relative;\n}\n.component_dropdown .dropdown_container {\n    display: none;\n    position: absolute;\n    isolation: isolate;\n    right: 0;\n    margin-top: 10px;\n    z-index: 3;\n    backdrop-filter: brightness(0.8) blur(10px);\n}\nbody:not(.dark-mode) .component_dropdown .dropdown_container {\n    box-shadow: -1px -1px 5px rgba(255, 255, 255, 0.5) inset;\n}\n.component_dropdown .dropdown_container:before {\n    content: ' ';\n    position: absolute;\n    right: 9px;\n    top: -5px;\n    width: 0;\n    height: 0;\n    border-left: 5px solid transparent;\n    border-right: 5px solid transparent;\n    border-bottom: 5px solid rgba(0, 0, 0, 0.7);\n}\n.component_dropdown .dropdown_container ul {\n    cursor: pointer;\n    margin: 0;\n    list-style-type: none;\n    box-shadow: rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px;\n    color: var(--bg-color);\n    background: rgba(0,0,0,0.65);\n    border-radius: 3px;\n    padding: 3px;\n    font-size: 0.92em;\n}\n.component_dropdown .dropdown_container ul li {\n    justify-content: space-between;\n    display: flex;\n    width: 150px;\n    padding: 7px 7px 7px 10px;\n}\n.dark-mode .component_dropdown .dropdown_container ul {\n    color: var(--color);\n    background: rgba(43, 45, 48, 0.9);\n}\n.dark-mode .component_dropdown .dropdown_container:before {\n    border-bottom-color: rgba(43, 45, 48, 0.9);\n}\n\n.component_dropdown.active .dropdown_container {\n    display: block;\n}\n.component_dropdown.active .dropdown_container li {\n    transition: background 0.1s ease-out;\n}\n.component_dropdown.active .dropdown_container li img.component_icon {\n    border: 2px solid transparent;\n    height: 15px;\n    width: 15px;\n    box-sizing: border-box;\n    align-self: center;\n    opacity: 0.7;\n}\n.component_dropdown.active .dropdown_container li img.component_icon.inverted {\n    transform: rotate(180deg);\n}\n\n.component_dropdown.active .dropdown_container li:hover {\n    background: rgba(198,200,204,0.25);\n    border-radius: 3px;\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_formbuilder.css",
    "content": ".formbuilder .description {\n    margin-top: -2px;\n    margin-bottom: 10px;\n    opacity: 0.7;\n    font-size: 0.9em;\n    line-height: 1em;\n    text-align: justify;\n}\n.formbuilder a {\n    text-decoration: underline;\n    text-decoration-style: wavy;\n    text-decoration-color: rgba(0, 0, 0, 0.3);\n}\n.formbuilder input::placeholder, .formbuilder textarea::placeholder {\n    opacity: 0.6;\n}\n.formbuilder label.input_type_hidden {\n    display: none;\n}\n.formbuilder fieldset legend {\n    text-transform: uppercase;\n    color: var(--light);\n    opacity: 0.9;\n    font-size: 1.1em;\n    padding: 0 15px;\n}\n.formbuilder .fileupload-image img {\n    height: 150px;\n    width: 100%;\n    object-fit: contain;\n    background: var(--bg-color);\n    border-radius: 2px;\n    box-sizing: border-box;\n}\n.formbuilder > label > img {\n    max-height: 110px;\n    width: 100%;\n}\n.formbuilder .fileupload-image object {\n    background: var(--bg-color);\n    height: 300px;\n    width: 100%;\n}\n.formbuilder .formbuilder_password {\n    display: flex;\n}\n.formbuilder .formbuilder_password img.component_icon {\n    height: 19px;\n    border: none;\n    cursor: pointer;\n    padding: 5px 5px;\n    border-bottom: 2px solid rgba(70, 99, 114, 0.1);\n    transition: border-color 0.2s ease-out;\n}\n\n.formbuilder .banner {\n    background: var(--bg-color);\n    border: 2px solid rgba(0, 0, 0, 0.05);\n    border-radius: 3px;\n    margin-bottom: 20px;\n    padding: 10px;\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_icon.css",
    "content": ".component_icon {\n    vertical-align: bottom;\n    max-height: 100%;\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_input.css",
    "content": ".component_input, .component_select {\n  background: inherit;\n  border: none;\n  border-radius: 0;\n  width: 100%;\n  display: inline-block;\n  font-family: \"San Francisco\",\"Roboto\",\"Arial\",sans-serif;\n  -webkit-text-size-adjust: 100%;\n  font-size: 16px;\n  padding: 5px 0px 5px 0px;\n  margin: 0 0 8px 0;\n  outline: none;\n  box-sizing: border-box;\n  color: inherit;\n  border-bottom: 2px solid rgba(70, 99, 114, 0.1);\n  transition: border-color 0.2s ease-out;\n  height: 31px;\n}\n\n.component_input:focus, .component_select:focus, .component_input:focus ~ component-icon img {\n    border-color: var(--emphasis-primary)!important;\n}\n\ninput.component_input[disabled], textarea.component_textarea[disabled] {\n    cursor: pointer;\n    opacity: 0.8;\n}\n\n.component_input[type=\"file\"] { border: none; margin-bottom: 0; }\n.component_input[type=\"file\"]::file-selector-button {\n    border-radius: 3px;\n    background-color: transparent;\n    border: 2px solid var(--border);\n    display: block;\n    transition: 0.1s all;\n    color: inherit;\n    cursor: pointer;\n}\n.component_input[type=\"file\"]::file-selector-button:hover {\n    background-color: var(--border);\n    border-color: var(--border);\n}\n\n.component_input:-webkit-autofill,\n.component_input:-webkit-autofill:focus {\n    transition: background-color 600000s 0s, color 600000s 0s;\n}\n\n.component_input[data-autocompleted] {\n    background-color: transparent !important;\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_inputgroup.css",
    "content": ".input_group {\n    display: flex;\n    background: #fff;\n    border-radius: 3px;\n    box-shadow: 2px 2px 10px rgba(0,0,0,.1);\n}\n.input_group input {\n    padding: 15px 20px;\n    border-bottom: 0;\n    margin: 0;\n    border-radius: 3px;\n    height: inherit !important;\n}\n.input_group button {\n    width: inherit;\n    padding: 0 10px;\n}\n.input_group button .component_icon {\n    height: 25px;\n}\n\n.input_group.error {\n    animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both;\n    transform: translate3d(0, 0, 0);\n    backface-visibility: hidden;\n    perspective: 1000px;\n}\n\n@keyframes shake {\n  10%, 90% {\n    transform: translate3d(-1px, 0, 0);\n  }\n\n  20%, 80% {\n    transform: translate3d(2px, 0, 0);\n  }\n\n  30%, 50%, 70% {\n    transform: translate3d(-4px, 0, 0);\n  }\n\n  40%, 60% {\n    transform: translate3d(4px, 0, 0);\n  }\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_skeleton.css",
    "content": ".component_skeleton {\n    width: 100%;\n    height: 30px;\n    background: linear-gradient(110deg, rgba(0,0,0,0.02) 8%, rgba(0,0,0,0.04) 18%, rgba(0,0,0,0.02) 33%);\n    border-radius: 5px;\n    background-size: 200% 100%;\n    animation: 3s skeleton_shine linear infinite;\n    margin-bottom: 15px;\n}\n\n@keyframes skeleton_shine {\n    to { background-position-x: -200%; }\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_textarea.css",
    "content": "@font-face {\n    font-family: 'text-security-disc';\n    src: url(\"textarea.woff\") format(\"woff2\");\n}\n.component_textarea {\n    background: inherit;\n    border: none;\n    border-radius: 0;\n    width: 100%;\n    display: inline-block;\n    font-family: \"San Francisco\",\"Roboto\",\"Arial\",sans-serif;\n    -webkit-text-size-adjust: 100%;\n    font-size: 16px;\n    padding: 5px 0px 5px 0px;\n    margin: 0 0 8px 0;\n    outline: none;\n    box-sizing: border-box;\n    color: inherit;\n    vertical-align: top;\n    min-width: 100%;\n    max-width: 100%;\n    min-height: 30px;\n    line-height: 18px;\n    border-bottom: 2px solid rgba(70, 99, 114, 0.1);\n    transition: border-color 0.2s ease-out;\n}\n.component_textarea[rows=\"1\"] {\n    max-height: 30px;\n}\n.component_textarea[name=\"password\"] {\n    resize: none;\n    -webkit-text-security: disc !important;\n    -webkit-touch-callout: none;\n    user-select: none;\n}\n.component_textarea[name=\"password\"].firefox.hasText {\n    font-family: 'text-security-disc';\n}\n.component_textarea:focus {\n    border-color: var(--emphasis-primary);\n}\n"
  },
  {
    "path": "public/assets/css/designsystem_utils.css",
    "content": ".pointer {\n    cursor: pointer;\n}\n\n.hidden{\n    position:absolute!important;\n    left:-10000px!important;\n    top:auto!important;\n    width:1px!important;\n    height:1px!important;\n    overflow:hidden!important;\n}\n\n.no-select {\n    -webkit-touch-callout: none;\n    -webkit-user-select: none;\n    -khtml-user-select: none;\n    -moz-user-select: none;\n    -ms-user-select: none;\n    user-select: none;\n    -webkit-tap-highlight-color: transparent;\n}\n\n.center{\n    text-align: center;\n}\n\n.full-width {\n    width: 100%;\n}\n\n.flex {\n    display: flex;\n}\n\n.ellipsis {\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n}\n\na.wavy {\n    text-decoration: underline;\n    text-decoration-style: wavy;\n    text-decoration-color: rgba(0, 0, 0, 0.3);\n}\n"
  },
  {
    "path": "public/assets/embed/filestash-image.js",
    "content": "class FilestashImage extends HTMLElement {\n    constructor() {\n        super();\n        this.attachShadow({ mode: \"open\" });\n\n        this.iframe = document.createElement(\"iframe\");\n        this.iframe.setAttribute(\"style\", \"width: 100%; height: 100%; border: none; display: block;\");\n        this.iframe.setAttribute(\"sandbox\", \"allow-downloads allow-scripts allow-presentation allow-forms allow-same-origin\");\n        this.iframe.setAttribute(\"referrerpolicy\", \"origin\");\n        this.shadowRoot.appendChild(this.iframe);\n\n        this.debounce = null;\n    }\n\n    static get observedAttributes() {\n        return [\"name\", \"src\"];\n    }\n\n    attributeChangedCallback(name, oldValue, newValue) {\n        if (oldValue === null) return;\n        if (oldValue !== newValue) {\n            clearTimeout(this.debounce);\n            this.debounce = setTimeout(() => {\n                this.iframe.contentWindow.postMessage({\n                    type: \"refresh\",\n                    payload: { name: this.getAttribute(\"name\"), src: this.getAttribute(\"src\") },\n                }, \"*\");\n            }, 100);\n        }\n    }\n\n    disconnectedCallback() {\n        clearTimeout(this.debounce);\n        this.debounce = null;\n    }\n\n    connectedCallback() {\n        const src = this.getAttribute(\"src\") || \"\";\n        const name = this.getAttribute(\"name\") || \"main.dat\";\n\n        this.style.display = \"inline-block\";\n        this.iframe.srcdoc = `<!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"UTF-8\">\n                <title></title>\n            </head>\n            <body>\n                <div id=\"app\">\n                    <component-bootscreen></component-bootscreen>\n                </div>\n\n                <script>\n                customElements.define(\"component-bootscreen\", class ComponentBootScreen extends HTMLElement {\n                    connectedCallback() {\n                        this.innerHTML = \\`\n                        <div class=\"component_loader\">\n                            <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" style=\"width: 30px;\">\n                                <circle fill=\"#57595A\" stroke=\"#57595A\" stroke-width=\"15\" r=\"15\" cx=\"40\" cy=\"100\">\n                                    <animate attributeName=\"opacity\" calcMode=\"spline\" dur=\"2\" values=\"1;0;1;\" keySplines=\".5 0 .5 1;.5 0 .5 1\" repeatCount=\"indefinite\" begin=\"-.4\"></animate>\n                                </circle>\n                                <circle fill=\"#57595A\" stroke=\"#57595A\" stroke-width=\"15\" r=\"15\" cx=\"100\" cy=\"100\">\n                                    <animate attributeName=\"opacity\" calcMode=\"spline\" dur=\"2\" values=\"1;0;1;\" keySplines=\".5 0 .5 1;.5 0 .5 1\" repeatCount=\"indefinite\" begin=\"-.2\"></animate>\n                                </circle>\n                                <circle fill=\"#57595A\" stroke=\"#57595A\" stroke-width=\"15\" r=\"15\" cx=\"160\" cy=\"100\">\n                                    <animate attributeName=\"opacity\" calcMode=\"spline\" dur=\"2\" values=\"1;0;1;\" keySplines=\".5 0 .5 1;.5 0 .5 1\" repeatCount=\"indefinite\" begin=\"0\"></animate>\n                                </circle>\n                            </svg>\n                            <style>\n                                html, body, #app { height: 100%; margin: 0; }\n                                #app { display: flex; }\n                                component-bootscreen { margin: auto; }\n                            </style>\n                        </div>\\`;\n                    }\n                });\n                </script>\n\n                <script type=\"module\" defer>\n                    const [{ version, mimes }] = await Promise.all([\n                        fetch(\"${import.meta.url}/../../../api/config\").then(async (resp) => {\n                            if (!resp.ok) return \"na\";\n                            const { result } = await resp.json();\n                            return { version: result.version, mimes: result.mime };\n                        }),\n                        import(\"${import.meta.url}/../../boot/bundler_init.js\").then(async () => {\n                            await new Promise((resolve, reject) => document.head.appendChild(Object.assign(document.createElement(\"script\"), {\n                                type: \"module\",\n                                src: new URL(\"../bundle.js\", \"${import.meta.url}\"),\n                                onload: resolve,\n                                onerror: reject,\n                            })));\n                            await import(\"${import.meta.url}/../../boot/bundler_complete.js\");\n                        }),\n                    ]);\n\n                    const { render } = await import(\"${import.meta.url}/../../\"+ version +\"/index.js\");\n                    const Application = await import(\"${import.meta.url}/../../\"+ version +\"/pages/viewerpage/application_image.js\");\n\n                    const $app = document.querySelector(\"#app\");\n                    const mime = mimes[\"${name}\".split(\".\").pop().toLowerCase()];\n                    render(Application, $app, {\n                        mime: mime,\n                        hasMenubar: true,\n                        getFilename: () => \"${name}\",\n                        getDownloadUrl: () => \"${src}\",\n                    });\n                    window.addEventListener(\"message\", (event) => {\n                        if(event.data.type !== \"refresh\") return;\n                        render(Application, $app, {\n                            mime: mime,\n                            hasMenubar: true,\n                            getFilename: () => event.data.payload.name,\n                            getDownloadUrl: () => event.data.payload.src,\n                        });\n                    });\n                </script>\n\n                <component-modal></component-modal>\n            </body>\n        </html>`;\n    }\n}\n\ncustomElements.define(\"filestash-image\", FilestashImage);\n"
  },
  {
    "path": "public/assets/embed/filestash-map.js",
    "content": "class FilestashMap extends HTMLElement {\n    constructor() {\n        super();\n        this.attachShadow({ mode: \"open\" });\n\n        this.iframe = document.createElement(\"iframe\");\n        this.iframe.setAttribute(\"style\", \"width: 100%; height: 100%; border: none; display: block;\");\n        this.iframe.setAttribute(\"sandbox\", \"allow-downloads allow-scripts allow-presentation\");\n        this.shadowRoot.appendChild(this.iframe);\n\n        this.debounce = null;\n    }\n\n    static get observedAttributes() {\n        return [\"name\", \"src\"];\n    }\n\n    attributeChangedCallback(name, oldValue, newValue) {\n        if (oldValue === null) return;\n        if (oldValue !== newValue) {\n            clearTimeout(this.debounce);\n            this.debounce = setTimeout(() => {\n                this.iframe.contentWindow.postMessage({\n                    type: \"refresh\",\n                    payload: { name: this.getAttribute(\"name\"), src: this.getAttribute(\"src\") },\n                }, \"*\");\n            }, 100);\n        }\n    }\n\n    disconnectedCallback() {\n        clearTimeout(this.debounce);\n        this.debounce = null;\n    }\n\n    connectedCallback() {\n        const src = this.getAttribute(\"src\") || \"\";\n        const name = this.getAttribute(\"name\") || \"main.dat\";\n\n        this.style.display = \"inline-block\";\n        this.iframe.srcdoc = `<!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"UTF-8\">\n                <title></title>\n            </head>\n            <body>\n                <div id=\"app\">\n                    <component-bootscreen></component-bootscreen>\n                </div>\n\n                <script>\n                customElements.define(\"component-bootscreen\", class ComponentBootScreen extends HTMLElement {\n                    connectedCallback() {\n                        this.innerHTML = \\`\n                        <div class=\"component_loader\">\n                            <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" style=\"width: 30px;\">\n                                <circle fill=\"#57595A\" stroke=\"#57595A\" stroke-width=\"15\" r=\"15\" cx=\"40\" cy=\"100\">\n                                    <animate attributeName=\"opacity\" calcMode=\"spline\" dur=\"2\" values=\"1;0;1;\" keySplines=\".5 0 .5 1;.5 0 .5 1\" repeatCount=\"indefinite\" begin=\"-.4\"></animate>\n                                </circle>\n                                <circle fill=\"#57595A\" stroke=\"#57595A\" stroke-width=\"15\" r=\"15\" cx=\"100\" cy=\"100\">\n                                    <animate attributeName=\"opacity\" calcMode=\"spline\" dur=\"2\" values=\"1;0;1;\" keySplines=\".5 0 .5 1;.5 0 .5 1\" repeatCount=\"indefinite\" begin=\"-.2\"></animate>\n                                </circle>\n                                <circle fill=\"#57595A\" stroke=\"#57595A\" stroke-width=\"15\" r=\"15\" cx=\"160\" cy=\"100\">\n                                    <animate attributeName=\"opacity\" calcMode=\"spline\" dur=\"2\" values=\"1;0;1;\" keySplines=\".5 0 .5 1;.5 0 .5 1\" repeatCount=\"indefinite\" begin=\"0\"></animate>\n                                </circle>\n                            </svg>\n                            <style>\n                                html, body, #app { height: 100%; margin: 0; }\n                                #app { display: flex; }\n                                component-bootscreen { margin: auto; }\n                            </style>\n                        </div>\\`;\n                    }\n                });\n                </script>\n\n                <script type=\"module\" defer>\n                    const [{ version, mimes }] = await Promise.all([\n                        fetch(\"${import.meta.url}/../../../api/config\").then(async (resp) => {\n                            if (!resp.ok) return \"na\";\n                            const { result } = await resp.json();\n                            return { version: result.version, mimes: result.mime };\n                        }),\n                        import(\"${import.meta.url}/../../boot/bundler_init.js\").then(async () => {\n                            await new Promise((resolve, reject) => document.head.appendChild(Object.assign(document.createElement(\"script\"), {\n                                type: \"module\",\n                                src: new URL(\"../bundle.js\", \"${import.meta.url}\"),\n                                onload: resolve,\n                                onerror: reject,\n                            })));\n                            await import(\"${import.meta.url}/../../boot/bundler_complete.js\");\n                        }),\n                    ]);\n\n                    const { render } = await import(\"${import.meta.url}/../../\"+ version +\"/index.js\");\n                    const Application = await import(\"${import.meta.url}/../../\"+ version +\"/pages/viewerpage/application_map.js\");\n\n                    const $app = document.querySelector(\"#app\");\n                    const mime = mimes[\"${name}\".split(\".\").pop().toLowerCase()];\n                    render(Application, $app, {\n                        mime: mime,\n                        hasMenubar: true,\n                        getFilename: () => \"${name}\",\n                        getDownloadUrl: () => \"${src}\",\n                    });\n                    window.addEventListener(\"message\", (event) => {\n                        if(event.data.type !== \"refresh\") return;\n                        render(Application, $app, {\n                            mime: mime,\n                            hasMenubar: true,\n                            getFilename: () => event.data.payload.name,\n                            getDownloadUrl: () => event.data.payload.src,\n                        });\n                    });\n                </script>\n\n                <component-modal></component-modal>\n            </body>\n        </html>`;\n    }\n}\n\ncustomElements.define(\"filestash-map\", FilestashMap);\n"
  },
  {
    "path": "public/assets/embed/filestash-table.js",
    "content": "class FilestashTable extends HTMLElement {\n    constructor() {\n        super();\n        this.attachShadow({ mode: \"open\" });\n\n        this.iframe = document.createElement(\"iframe\");\n        this.iframe.setAttribute(\"style\", \"width: 100%; height: 100%; border: none; display: block;\");\n        this.iframe.setAttribute(\"sandbox\", \"allow-downloads allow-same-origin allow-scripts allow-presentation allow-forms\");\n        this.shadowRoot.appendChild(this.iframe);\n\n        this.debounce = null;\n    }\n\n    static get observedAttributes() {\n        return [\"name\", \"src\"];\n    }\n\n    attributeChangedCallback(name, oldValue, newValue) {\n        if (oldValue === null) return;\n        if (oldValue !== newValue) {\n            clearTimeout(this.debounce);\n            this.debounce = setTimeout(() => {\n                this.iframe.contentWindow.postMessage({\n                    type: \"refresh\",\n                    payload: { name: this.getAttribute(\"name\"), src: this.getAttribute(\"src\") },\n                }, \"*\");\n            }, 100);\n        }\n    }\n\n    disconnectedCallback() {\n        clearTimeout(this.debounce);\n        this.debounce = null;\n    }\n\n    connectedCallback() {\n        const src = this.getAttribute(\"src\") || \"\";\n        const name = this.getAttribute(\"name\") || \"main.dat\";\n\n        this.style.display = \"inline-block\";\n        this.iframe.srcdoc = `<!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"UTF-8\">\n                <title></title>\n            </head>\n            <body>\n                <div id=\"app\">\n                    <component-bootscreen></component-bootscreen>\n                </div>\n\n                <script>\n                customElements.define(\"component-bootscreen\", class ComponentBootScreen extends HTMLElement {\n                    connectedCallback() {\n                        this.innerHTML = \\`\n                        <div class=\"component_loader\">\n                            <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" style=\"width: 30px;\">\n                                <circle fill=\"#57595A\" stroke=\"#57595A\" stroke-width=\"15\" r=\"15\" cx=\"40\" cy=\"100\">\n                                    <animate attributeName=\"opacity\" calcMode=\"spline\" dur=\"2\" values=\"1;0;1;\" keySplines=\".5 0 .5 1;.5 0 .5 1\" repeatCount=\"indefinite\" begin=\"-.4\"></animate>\n                                </circle>\n                                <circle fill=\"#57595A\" stroke=\"#57595A\" stroke-width=\"15\" r=\"15\" cx=\"100\" cy=\"100\">\n                                    <animate attributeName=\"opacity\" calcMode=\"spline\" dur=\"2\" values=\"1;0;1;\" keySplines=\".5 0 .5 1;.5 0 .5 1\" repeatCount=\"indefinite\" begin=\"-.2\"></animate>\n                                </circle>\n                                <circle fill=\"#57595A\" stroke=\"#57595A\" stroke-width=\"15\" r=\"15\" cx=\"160\" cy=\"100\">\n                                    <animate attributeName=\"opacity\" calcMode=\"spline\" dur=\"2\" values=\"1;0;1;\" keySplines=\".5 0 .5 1;.5 0 .5 1\" repeatCount=\"indefinite\" begin=\"0\"></animate>\n                                </circle>\n                            </svg>\n                            <style>\n                                html, body, #app { height: 100%; margin: 0; }\n                                #app { display: flex; }\n                                component-bootscreen { margin: auto; }\n                            </style>\n                        </div>\\`;\n                    }\n                });\n                </script>\n                <script type=\"module\" defer>\n                    const [{ version, mimes }] = await Promise.all([\n                        fetch(\"${import.meta.url}/../../../api/config\").then(async (resp) => {\n                            if (!resp.ok) return \"na\";\n                            const { result } = await resp.json();\n                            return { version: result.version, mimes: result.mime };\n                        }),\n                        import(\"${import.meta.url}/../../boot/bundler_init.js\").then(async () => {\n                            await new Promise((resolve, reject) => document.head.appendChild(Object.assign(document.createElement(\"script\"), {\n                                type: \"module\",\n                                src: new URL(\"../bundle.js\", \"${import.meta.url}\"),\n                                onload: resolve,\n                                onerror: reject,\n                            })));\n                            await import(\"${import.meta.url}/../../boot/bundler_complete.js\");\n                        }),\n                    ]);\n\n                    const { render } = await import(\"${import.meta.url}/../../\"+ version +\"/index.js\");\n                    const Application = await import(\"${import.meta.url}/../../\"+ version +\"/pages/viewerpage/application_table.js\");\n\n                    const $app = document.querySelector(\"#app\");\n                    const mime = mimes[\"${name}\".split(\".\").pop().toLowerCase()];\n                    render(Application, $app, {\n                        mime: mime,\n                        hasMenubar: true,\n                        getFilename: () => \"${name}\",\n                        getDownloadUrl: () => \"${src}\",\n                    });\n                    window.addEventListener(\"message\", (event) => {\n                        if(event.data.type !== \"refresh\") return;\n                        render(Application, $app, {\n                            mime: mime,\n                            hasMenubar: true,\n                            getFilename: () => event.data.payload.name,\n                            getDownloadUrl: () => event.data.payload.src,\n                        });\n                    });\n                </script>\n\n                <component-modal></component-modal>\n            </body>\n        </html>`;\n    }\n}\n\ncustomElements.define(\"filestash-table\", FilestashTable);\n"
  },
  {
    "path": "public/assets/helpers/loader.d.ts",
    "content": "export function loadScript(url: string): Promise<string>;\n\nexport function loadJS(baseURL: string, path: string, opts?: object): Promise<string>;\n\nexport function loadCSS(baseURL: string, ...arrayOfFilenames: string[]): Promise<string>;\n\nexport function loadCSSInline(baseURL: string, filename: string): Promise<string>;\n\nexport function CSS(baseURL: string, ...arrayOfFilenames: string[]): Promise<string>;\n\nexport function init(): Promise<void>;"
  },
  {
    "path": "public/assets/helpers/loader.js",
    "content": "import { onDestroy } from \"../lib/skeleton/index.js\";\n\nexport async function loadJS(baseURL, path, opts = {}) {\n    const $script = document.createElement(\"script\");\n    const link = new URL(path, baseURL);\n    $script.setAttribute(\"src\", link.toString());\n    for (const key in opts) {\n        $script.setAttribute(key, opts[key]);\n    }\n    if (document.head.querySelector(`[src=\"${link.toString()}\"]`)) return Promise.resolve();\n    document.head.appendChild($script);\n    return new Promise((done) => {\n        $script.onload = () => done();\n        $script.onerror = () => done();\n    });\n}\n\nexport async function loadCSS(baseURL, path) {\n    const $style = document.createElement(\"link\");\n    const link = new URL(path, baseURL);\n    $style.setAttribute(\"href\", link.toString());\n    $style.setAttribute(\"rel\", \"stylesheet\");\n    if (document.head.querySelector(`[href=\"${link.toString()}\"]`)) return Promise.resolve();\n    else if (document.head.querySelector(`style[id=\"${link.toString()}\"]`)) return Promise.resolve();\n    document.head.appendChild($style);\n    return new Promise((done) => {\n        $style.onload = done;\n        $style.onerror = done;\n    });\n}\n\nexport async function loadWorker(baseURL, path, opts = {}) {\n    const { iframe = true } = opts;\n    let url = new URL(path, baseURL);\n    if (iframe) {\n        // eg: fix issue coming from loading worker through an iframe like this nasty:\n        //     SecurityError: Failed to construct 'Worker': Script at 'xxx.worker.js' cannot be accessed from origin 'null'.\n        //        at Module.default (loader_dbf.js:9:20)\n        //        at application_table.js:58:50\n        let code = await fetch(new URL(path, baseURL)).then((res) => res.text());\n        const importPathRE = new RegExp(\"import\\\\s+(?:[^'\\\";]*?\\\\s+from\\\\s+)?[\\\"']([^\\\"']+)[\\\"']\", \"gm\");\n        code = code.replaceAll(\"import.meta.url\", `\"${baseURL}\"`);\n        code.matchAll(importPathRE).forEach(([_, path]) => {\n            code = code.replaceAll(path, new URL(path, baseURL));\n        });\n        url = URL.createObjectURL(new Blob([code], { type: \"application/javascript\" }));\n        onDestroy(() => URL.revokeObjectURL(url));\n    }\n    const worker = new Worker(url, { type: \"module\" });\n    onDestroy(() => worker.terminate());\n    await new Promise((resolve) => worker.addEventListener(\n        \"message\", resolve, { once: true },\n    ));\n    return worker;\n}\n\nexport async function loadCSSInline(baseURL, filename) {\n    const res = await fetch(new URL(filename, baseURL).pathname, {\n        cache: \"force-cache\",\n    });\n    if (res.status !== 200) return `/* ERROR: ${res.status} */`;\n    else if (!res.headers.get(\"Content-Type\")?.startsWith(\"text/css\")) return `/* ERROR: wrong type, got \"${res.headers?.get(\"Content-Type\")}\"*/`;\n    return await res.text();\n}\n\nexport async function CSS(baseURL, ...arrayOfFilenames) {\n    const sheets = await Promise.all(arrayOfFilenames.map((filename) => loadCSSInline(baseURL, filename)));\n    return sheets.join(\"\\n\\n\");\n}\n"
  },
  {
    "path": "public/assets/helpers/loader_wasm.js",
    "content": "const DEBUG = false;\nconst log = (msg) => DEBUG && console.log(msg);\n\nconst wasmCache = new Map();\nlet wasiInstance;\n\nexport function setWasiInstance(instance) {\n    wasiInstance = instance;\n}\n\nexport default async function(baseURL, path, opts = {}) {\n    const url = new URL(path, baseURL);\n\n    let wasm;\n    if (wasmCache.has(url.pathname)) wasm = wasmCache.get(url.pathname);\n    else {\n        wasm = await WebAssembly.instantiateStreaming(\n            fetch(url), {\n                wasi_snapshot_preview1: {\n                    ...wasi,\n                },\n                env: {\n                    ...wasi,\n                    ...syscalls,\n                    ...javascripts,\n                },\n            },\n        );\n        wasmCache.set(url.pathname, wasm);\n    }\n    setWasiInstance(wasm.instance);\n    return wasm;\n}\n\nconst FS = {};\nlet nextFd = 0;\nwriteFS(new Uint8Array(0), \"/dev/stdin\");\nwriteFS(new Uint8Array(1024*8), \"/dev/stdout\");\nwriteFS(new Uint8Array(1024*8), \"/dev/stderr\");\nif (nextFd !== 3) throw new Error(\"Unexpected next fd\");\n\nexport function writeFS(buffer, path = \"\") {\n    if (!(buffer instanceof Uint8Array)) throw new Error(\"can only write Uint8Array\");\n    FS[nextFd] = {\n        buffer,\n        position: 0,\n        path,\n    };\n    nextFd += 1;\n    return nextFd - 1;\n}\n\nexport function readFS(fd) {\n    const file = FS[fd];\n    if (!file) throw new Error(\"file does not exist\");\n\n    let end = file.buffer.length;\n    while (end > 0 && file.buffer[end - 1] === 0) end--;\n    return file.buffer.subarray(0, end);\n}\n\nexport function clearFS() {\n    Object.keys(FS).forEach((key) => {\n        if (key > 2) delete FS[key];\n    });\n    nextFd = 3;\n}\n\nfunction getFile(path) {\n    const allFds = Object.keys(FS);\n    for (let i=allFds.length - 1; i>0; i--) {\n        if (FS[allFds[i]].path === path) {\n            log(`  fileopen fd=${i} path=${path}`);\n            return FS[allFds[i]];\n        }\n    }\n    throw new Error(`cannot get file \"${path}\"`);\n}\n\nexport const syscalls = {\n    __syscall_fcntl64: (fd, cmd, varargs) => {\n        console.log(`Stubbed __syscall_fcntl64 called with fd=${fd}, cmd=${cmd}, varargs=${varargs}`);\n        return -1;\n    },\n    __syscall_ioctl: (fd, op, varargs) => {\n        switch (op) {\n        case 21523:\n            break;\n        default:\n            console.log(`Stubbed __syscall_ioctl called with fd=${fd}, request=${op}, varargs=${varargs}`);\n        }\n        return 0;\n    },\n    __syscall_unlinkat: (fd) => {\n        console.log(`Stubbed __syscall_unlinkat called with fd=${fd}`);\n        return -1;\n    },\n    __syscall_rmdir: (fd) => {\n        console.log(`Stubbed __syscall_rmdir called with fd=${fd}`);\n        return -1;\n    },\n    __syscall_newfstatat: (pathPtr, bufPtr) => {\n        console.log(`Stubbed __syscall_stat64 called with pathPtr=${pathPtr}, bufPtr=${bufPtr}`);\n        return 0; // Return 0 for a successful call\n    },\n    __syscall_lstat64: () => {\n        console.log(`Stubbed __syscall_lstat64 called`);\n        return -1;\n    },\n    __assert_fail: () => {\n        console.log(`Stubbed __assert_fail called`);\n        return -1;\n    },\n    __syscall_ftruncate64: () => {\n        console.log(`Stubbed __syscall_ftruncate64`);\n        return -1;\n    },\n    __syscall_renameat: () => {\n        console.log(`Stubbed __syscall_renameat`);\n        return -1;\n    },\n};\n\nconst javascripts = {\n    _tzset_js: () => {\n        console.log(\"Initializing time zone settings (stub)\");\n    },\n    _abort_js: () => {\n        console.error(\"WebAssembly module called _abort_js!\");\n        throw new Error(\"_abort_js was called\");\n    },\n    _mktime_js: () => {\n        console.error(\"WebAssembly module called _abort_js!\");\n        throw new Error(\"_abort_js was called\");\n    },\n    _localtime_js: () => {\n        console.error(\"WebAssembly module called _localtime_js!\");\n        throw new Error(\"_localtime_js was called\");\n    },\n    emscripten_date_now: () => {\n        console.error(\"WebAssembly module called emscripten_date_now!\");\n        throw new Error(\"_localtime_js was called\");\n    },\n    emscripten_get_now: () => {\n        console.error(\"WebAssembly module called emscripten_get_now!\");\n        throw new Error(\"_localtime_js was called\");\n    },\n    emscripten_errn: () => {\n        console.error(\"WebAssembly module called emscripten_errn!\");\n        throw new Error(\"_errn was called\");\n    },\n    HaveOffsetConverter: () => {\n        console.error(\"WebAssembly module called HaveOffsetConverter!\");\n        throw new Error(\"HaveOffsetConverter was called\");\n    },\n    emscripten_pc_get_function: () => {\n        console.error(\"WebAssembly module called emscripten_pc_get_function!\");\n        throw new Error(\"emscripten_pc_get_function was called\");\n    },\n    emscripten_asm_const_int: () => {\n        console.error(\"WebAssembly module called emscripten_asm_const_int!\");\n        throw new Error(\"emscripten_asm_const_int was called\");\n    },\n    emscripten_stack_snapshot: () => {\n        console.error(\"WebAssembly module called emscripten_stack_snapshot!\");\n        throw new Error(\"emscripten_stack_snapshot was called\");\n    },\n    emscripten_stack_unwind_buffer: () => {\n        console.error(\"WebAssembly module called emscripten_stack_unwind_buffer!\");\n        throw new Error(\"emscripten_stack_unwind_buffer was called\");\n    },\n    emscripten_get_heap_max: () => {\n        console.error(\"WebAssembly module called emscripten_get_heap_max!\");\n        throw new Error(\"emscripten_get_heap_max was called\");\n    },\n    _timegm_js: () => {\n        return 0;\n    },\n    _gmtime_js: () => {\n        return null;\n    },\n    _munmap_js: () => {\n        console.error(\"WebAssembly module called _munmap_js!\");\n        throw new Error(\"_munmap_js was called\");\n    },\n    _mmap_js: () => {\n        console.error(\"WebAssembly module called _mmap_js!\");\n        throw new Error(\"_mmap_js was called\");\n    }\n};\n\nexport const wasi = {\n\n    fd_write(fd, iovs, iovs_len, nwritten) {\n        if (!FS[fd]) throw new Error(`File descriptor ${fd} does not exist.`);\n\n        const ioVecArray = new Uint32Array(wasiInstance.exports.memory.buffer, iovs, iovs_len * 2);\n        const memory = new Uint8Array(wasiInstance.exports.memory.buffer);\n        let totalBytesWritten = 0;\n\n        for (let i = 0; i < iovs_len * 2; i += 2) {\n            const offset = ioVecArray[i];\n            const length = ioVecArray[i + 1];\n            while (FS[fd].buffer.byteLength - FS[fd].position < length) {\n                const newBuffer = new Uint8Array(FS[fd].buffer.byteLength + 1024 * 1024 * 5);\n                newBuffer.set(FS[fd].buffer, 0);\n                FS[fd].buffer = newBuffer;\n            }\n            FS[fd].buffer.set(\n                memory.subarray(offset, offset + length),\n                FS[fd].position\n            );\n            FS[fd].position += length;\n            totalBytesWritten += length;\n        }\n        new DataView(wasiInstance.exports.memory.buffer).setUint32(\n            nwritten,\n            totalBytesWritten,\n            true,\n        );\n        if (fd === 1 || fd === 2) {\n            FS[fd] = {\n                buffer: new Uint8Array(readFS(fd)),\n                position: 0,\n                path: FS[fd].path || \"\",\n            };\n        } else {\n            log(`wasi::fd_write fd=${fd}`);\n        }\n        return 0;\n    },\n\n    fd_read(fd, iovs, iovs_len, nread) {\n        const file = FS[fd];\n        if (!file) {\n            console.error(`Invalid fd: ${fd}`);\n            return -1;\n        }\n\n        const ioVecArray = new Uint32Array(wasiInstance.exports.memory.buffer, iovs, iovs_len * 2);\n        const memory = new Uint8Array(wasiInstance.exports.memory.buffer);\n        let totalBytesRead = 0;\n        for (let i = 0; i < iovs_len * 2; i += 2) {\n            const offset = ioVecArray[i];\n            const length = ioVecArray[i + 1] || 0;\n            const bytesToRead = Math.min(\n                length,\n                file.buffer.length - file.position,\n            );\n            if (bytesToRead < 0) {\n                break;\n            }\n            memory.set(\n                file.buffer.subarray(file.position, file.position + bytesToRead),\n                offset,\n            );\n            file.position += bytesToRead;\n            totalBytesRead += bytesToRead;\n        }\n        log(`wasi::fd_read fd=${fd} iovs_len=${iovs_len} totalBytesRead=${totalBytesRead}`);\n        new DataView(wasiInstance.exports.memory.buffer).setUint32(\n            nread,\n            totalBytesRead,\n            true,\n        );\n        return 0;\n    },\n\n    fd_pread(fd, iovs, iovs_len, offset_lo, offset_hi, nread) {\n        const file = FS[fd];\n        if (!file) {\n            console.error(`Invalid fd: ${fd}`);\n            return -1;\n        }\n\n        const start = (offset_hi >>> 0) * 0x100000000 + (offset_lo >>> 0);\n        const ioVec = new Uint32Array(wasiInstance.exports.memory.buffer, iovs, iovs_len * 2);\n        const mem = new Uint8Array(wasiInstance.exports.memory.buffer);\n\n        let total = 0;\n        for (let i = 0; i < iovs_len * 2; i += 2) {\n            const dst = ioVec[i];\n            const len = ioVec[i + 1] || 0;\n            const avail = Math.max(\n                0,\n                Math.min(len, file.buffer.length - (start + total)),\n            );\n            if (avail === 0) {\n                console.log(`len=[${len}] buffLength=[${file.buffer.length}] start=[${start}] total=[${total}]`);\n                break;\n            }\n            mem.set(\n                file.buffer.subarray(start + total, start + total + avail),\n                dst\n            );\n            total += avail;\n        }\n\n        new DataView(wasiInstance.exports.memory.buffer).setUint32(nread, total, true);\n        return 0;\n    },\n\n    fd_seek(fd, offsetBigInt, _, whence) {\n        log(`wasi::fd_seek fd=${fd} offset=${offsetBigInt} whence=${whence}`);\n        const offset = Number(offsetBigInt);\n        const file = FS[fd];\n        if (!file) {\n            console.error(`Invalid FD: ${fd}`);\n            return -1;\n        }\n        switch (whence) {\n        case 0: // SEEK_SET\n            file.position = offset;\n            break;\n        case 1: // SEEK_CUR\n            file.position += offset;\n            break;\n        case 2: // SEEK_END\n            file.position = file.buffer.length + offset;\n            break;\n        default:\n            console.log(`fd_seek called with fd=${fd}, offset=${offset}, position=${file.position} whence=${whence}`);\n            const error = new Error(\"fd_seek trace\");\n            console.log(\"Invalid whence\", whence, error.stack);\n            return -1;\n        }\n        return 0;\n    },\n\n    fd_close(fd) {\n        if (!FS[fd]) {\n            console.error(`Invalid FD: ${fd}`);\n            return -1;\n        }\n        return 0;\n    },\n\n    _emscripten_memcpy_js(dest, src, num) {\n        const memory = new Uint8Array(wasiInstance.exports.memory.buffer);\n        memory.set(memory.subarray(src, src + num), dest);\n        return dest;\n    },\n\n    emscripten_resize_heap(requested) {\n        console.log(\"Stubbed emscripten_resize_heap called\");\n        throw new Error(\"Heap resize not supported\");\n    },\n\n    environ_sizes_get() {\n        console.log(`Stubbed environ_sizes_get called`);\n        return 0;\n    },\n\n    environ_get() {\n        console.log(`Stubbed environ_get called`);\n        return 0;\n    },\n\n    clock_time_get() {\n        console.log(`Stubbed clock_time_get called`);\n        return -1;\n    },\n\n    __syscall_openat(dirFd, pathPtr, flags, mode) {\n        const memory = new Uint8Array(wasiInstance.exports.memory.buffer);\n        let path = \"\";\n        for (let i = pathPtr; memory[i] !== 0; i++) {\n            path += String.fromCharCode(memory[i]);\n        }\n        const allFds = Object.keys(FS);\n        for (let i=allFds.length - 1; i>0; i--) {\n            if (FS[allFds[i]].path === path) {\n                log(`  syscall::openat::result fd=${i} path=${path}`);\n                return i;\n            }\n        }\n        throw new Error(\"Unknown file for __syscall_openat\");\n    },\n\n    __syscall_stat64(pathPtr, buf) {\n        log(`  syscall::stat64 pathPtr=${pathPtr}, bufPtr=${buf}`);\n        const memory = new Uint8Array(wasiInstance.exports.memory.buffer);\n        let path = \"\";\n        for (let i = pathPtr; memory[i] !== 0; i++) {\n            path += String.fromCharCode(memory[i]);\n        }\n        const file = getFile(path);\n        const HEAP32 = new Int32Array(wasiInstance.exports.memory.buffer);\n        const HEAPU32 = new Uint32Array(wasiInstance.exports.memory.buffer);\n        const stat = {\n            dev: 1,\n            ino: 42,\n            mode: 0o100644,\n            nlink: 1,\n            uid: 1000,\n            gid: 1000,\n            rdev: 0,\n            size: file.buffer.byteLength,\n            blksize: 4096,\n            blocks: 256,\n            atime: new Date(),\n            mtime: new Date(),\n            ctime: new Date(),\n        };\n        HEAP32[(buf >> 2)] = stat.dev;\n        HEAP32[((buf + 4) >> 2)] = stat.mode;\n        HEAPU32[((buf + 8) >> 2)] = stat.nlink;\n        HEAP32[((buf + 12) >> 2)] = stat.uid;\n        HEAP32[((buf + 16) >> 2)] = stat.gid;\n        HEAP32[((buf + 20) >> 2)] = stat.rdev;\n        HEAP32[((buf + 24) >> 2)] = stat.size & 0xFFFFFFFF;\n        HEAP32[((buf + 28) >> 2)] = Math.floor(stat.size / 4294967296);\n        HEAP32[((buf + 32) >> 2)] = stat.blksize;\n        HEAP32[((buf + 36) >> 2)] = stat.blocks;\n        HEAP32[((buf + 40) >> 2)] = Math.floor(stat.atime.getTime() / 1000);\n        HEAP32[((buf + 44) >> 2)] = 0;\n        HEAP32[((buf + 48) >> 2)] = (stat.atime.getTime() % 1000) * 1e6;\n        HEAP32[((buf + 56) >> 2)] = Math.floor(stat.mtime.getTime() / 1000);\n        HEAP32[((buf + 60) >> 2)] = 0;\n        HEAP32[((buf + 64) >> 2)] = (stat.mtime.getTime() % 1000) * 1e6;\n        HEAP32[((buf + 72) >> 2)] = Math.floor(stat.ctime.getTime() / 1000);\n        HEAP32[((buf + 76) >> 2)] = 0;\n        HEAP32[((buf + 80) >> 2)] = (stat.ctime.getTime() % 1000) * 1e6;\n        HEAP32[((buf + 88) >> 2)] = stat.ino & 0xFFFFFFFF;\n        HEAP32[((buf + 92) >> 2)] = Math.floor(stat.ino / 4294967296);\n        return 0;\n    },\n\n    __cxa_throw(ptr, type, destructor) {\n        console.error(`  syscall::cxa_throw ptr=${ptr}, type=${type}, destructor=${destructor}`);\n        throw new Error(\"WebAssembly exception\");\n    },\n\n    random_get() {\n        console.log(`Stubbed random_get called`);\n        return -1;\n    },\n\n    proc_exit() {\n        console.log(`Stubbed proc_exit called`);\n        return -1;\n    },\n\n    __syscall_fstat64(fd, buf) {\n        log(`  syscall::fstat64 fd=${fd}, buf=${buf}`);\n        const file = FS[fd];\n        if (!file) return -1; // EBADF\n\n        const size = file.buffer.byteLength >>> 0; // ≤ 4 GB\n        const nowSec = (Date.now() / 1000) | 0;\n        const H32 = new Int32Array(wasiInstance.exports.memory.buffer);\n\n        /* basic fields */\n        H32[buf >> 2] = 1; /* st_dev   */\n        H32[(buf+4) >> 2] = 0o100644; /* st_mode  */\n        H32[(buf+8) >> 2] = 1; /* st_nlink */\n        H32[(buf+12) >> 2] = 1000; /* st_uid   */\n        H32[(buf+16) >> 2] = 1000; /* st_gid   */\n        H32[(buf+20) >> 2] = 0; /* st_rdev  */\n\n        H32[((buf + 24) >> 2)] = size & 0xFFFFFFFF;\n        H32[((buf + 28) >> 2)] = Math.floor(size / 4294967296);\n\n        H32[(buf+32) >> 2] = 4096;\n        H32[(buf+36) >> 2] = (size + 511) >> 9;\n\n        /* st_size lives at byte 40 in Emscripten’s 32-bit stat64 */\n        H32[(buf+40) >> 2] = size; /* low 32 bits (high word = 0) */\n        H32[(buf+44) >> 2] = 0;\n\n        H32[(buf+32) >> 2] = 4096; /* st_blksize */\n        H32[(buf+36) >> 2] = (size + 511) >> 9; /* st_blocks */\n\n        /* atime / mtime / ctime: seconds, nsec = 0 */\n        for (const off of [48, 56, 64]) {\n            H32[((buf+off) >> 2)] = nowSec;\n            H32[((buf+off+4) >> 2)] = 0;\n        }\n\n        H32[(buf+72) >> 2] = fd; /* st_ino (low) */\n        H32[(buf+76) >> 2] = 0; /* st_ino (high) */\n        return 0;\n    }\n};\n"
  },
  {
    "path": "public/assets/helpers/log.d.ts",
    "content": "export function report(msg: Event|string, err?: any, link?: string, lineNo?: number, columnno?: number);"
  },
  {
    "path": "public/assets/helpers/log.js",
    "content": "import { toHref } from \"../lib/skeleton/router.js\";\nimport ajax from \"../lib/ajax.js\";\n\nexport function report(msg, err, link, lineNo, columnNo) {\n    if (window.navigator.onLine === false) return Promise.resolve();\n    let url = toHref(\"/report?\");\n    url += \"url=\" + encodeURIComponent(location.href) + \"&\";\n    url += \"msg=\" + encodeURIComponent(msg) + \"&\";\n    url += \"from=\" + encodeURIComponent(link) + \"&\";\n    url += \"from.lineNo=\" + lineNo + \"&\";\n    url += \"from.columnNo=\" + columnNo;\n    if (err instanceof Error) url += \"error=\" + encodeURIComponent(err.message) + \"&\";\n\n    return ajax({ url, method: \"post\" }).toPromise().catch(() => {});\n}\n"
  },
  {
    "path": "public/assets/helpers/sdk.js",
    "content": "// feature detection if we're using Filestash as a standalone app or as an SDK\n// see: ../index.js\n\nexport function isSDK() {\n    const importURL = new URL(import.meta.url);\n    return location.origin !== importURL.origin;\n}\n\nexport function urlSDK(url) {\n    if (url.startsWith(\"blob:\")) return url;\n    else if (url.startsWith(\"http://\") || url.startsWith(\"https://\")) return url;\n\n    const importURL = new URL(import.meta.url);\n    if (new RegExp(\"^/\").test(url) === false) {\n        url = \"/\" + url;\n    }\n    return importURL.origin + url;\n}\n"
  },
  {
    "path": "public/assets/index.js",
    "content": "// Want to create an integration via our SDK in your application? You are in the right place!\n//\n// How it works you may ask? it's simple:\n// 1) pick a component to render. Components look like this:\n//    function(render, opts = {}) {\n//        // ...\n//    }\n// 2) similarly to every framework out there, call the framework bootstrap procedure:\n//    render(Component, $node, args = {});\n//\n//\n// /***********************************************/\n// /* example to render the 3D viewer application */\n// /***********************************************/\n// import { render } from \"<YOUR_INSTANCE_URL>/assets/index.js\";\n// import * as Component from \"<YOUR_INSTANCE_URL>/assets/pages/viewerpage/application_3d.js\";\n//\n// render(Component, document.getElementById(\"app\"), {});\n//\n//\n//\nimport { createRender } from \"./lib/skeleton/index.js\";\nimport { loadCSS } from \"./helpers/loader.js\";\n\nexport function render(module, $app, opts = {}) {\n    assertArgs(module, $app);\n    execute(module, $app, opts);\n}\n\nfunction assertArgs(module, $app) {\n    if (typeof module.default !== \"function\") throw new TypeError(\"Unsupported module - see the SDK documentation\");\n    else if (!($app instanceof Node)) throw new TypeError(\"Invalid node - see the SDK documentation\");\n}\n\nfunction execute(module, $app, opts) {\n    const priors = [\n        import(\"./boot/ctrl_boot_frontoffice.js\"),\n        loadCSS(import.meta.url, \"./css/designsystem.css\"),\n    ];\n    if (typeof module.init === \"function\") priors.push(module.init($app));\n\n    return Promise.all(priors)\n        .then(async() => await module.default(createRender($app), opts))\n        .catch((err) => console.error(err));\n}\n"
  },
  {
    "path": "public/assets/lib/ajax.js",
    "content": "import rxjs, { ajax } from \"./rx.js\";\nimport { AjaxError } from \"./error.js\";\nimport { isSDK, urlSDK } from \"../helpers/sdk.js\";\n\nexport default function(opts) {\n    if (typeof opts === \"string\") opts = { url: opts, withCredentials: true };\n    else if (typeof opts !== \"object\") throw new Error(\"unsupported call\");\n\n    if (!opts.headers) opts.headers = {};\n    if (!opts.responseType) opts.responseType = \"text\";\n    opts.headers[\"X-Requested-With\"] = \"XmlHttpRequest\";\n    if (window.BEARER_TOKEN) opts.headers[\"Authorization\"] = `Bearer ${window.BEARER_TOKEN}`;\n\n    if (opts.url.startsWith(\"data:\")) return rxjs.of({ response: parseDataUrl(opts.url) });\n    if (isSDK()) {\n        if ([\"/api/config\"].indexOf(opts.url) === -1) opts.withCredentials = false;\n        opts.url = urlSDK(opts.url);\n    }\n\n    const responseType = opts.responseType === \"json\" ? \"text\" : opts.responseType;\n    return ajax({\n        withCredentials: true,\n        ...opts,\n        responseType,\n    }).pipe(\n        rxjs.map((res) => {\n            if (opts.responseType === \"json\") {\n                const result = res.xhr.responseText;\n                res.responseJSON = JSON.parse(result);\n                if (res.responseJSON.status !== \"ok\") {\n                    throw new AjaxError(\"Oups something went wrong\", result);\n                }\n            }\n            return res;\n        }),\n        rxjs.catchError(\n            (err) => activePage\n                ? rxjs.throwError(processError(err.xhr, err))\n                : rxjs.EMPTY\n        ),\n    );\n}\n\nlet activePage = true;\nwindow.addEventListener(\"beforeunload\", function() {\n    activePage = false;\n});\n\nfunction parseDataUrl(url) {\n    const matches = url.match(/^data:(.*?)(;base64)?,(.*)$/);\n    if (!matches) throw new Error(\"Invalid Data URL\");\n\n    const isBase64 = !!matches[2];\n    const data = matches[3];\n    if (isBase64) {\n        const binaryString = atob(data);\n        const len = binaryString.length;\n        const bytes = new Uint8Array(len);\n        for (let i = 0; i < len; i++) {\n            bytes[i] = binaryString.charCodeAt(i);\n        }\n        return bytes.buffer;\n    }\n    const decodedData = decodeURIComponent(data);\n    const encoder = new TextEncoder();\n    return encoder.encode(decodedData).buffer;\n}\n\nfunction processError(xhr, err) {\n    let responseText = \"\";\n    try {\n        responseText = xhr?.responseText;\n        // InvalidStateError: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'arraybuffer').\n    } catch (err) {}\n\n    const message = (function(content) {\n        try {\n            return JSON.parse(content).message;\n        } catch (err) {\n            return Array.from(new Set(\n                content.replace(/<[^>]*>/g, \"\")\n                    .replace(/\\n{2,}/, \"\\n\")\n                    .trim()\n                    .split(\"\\n\")\n            )).join(\" \");\n        }\n    })(responseText);\n\n    if (window.navigator.onLine === false) {\n        return new AjaxError(\"Connection Lost\", err, \"NO_INTERNET\");\n    }\n    switch (parseInt(xhr?.status)) {\n    case 500:\n        return new AjaxError(\n            message || \"Oups something went wrong with our servers\",\n            err, \"INTERNAL_SERVER_ERROR\"\n        );\n    case 401:\n        return new AjaxError(\n            message || \"Authentication error\",\n            err, \"Unauthorized\"\n        );\n    case 403:\n        return new AjaxError(\n            message || \"You can't do that\",\n            err, \"FORBIDDEN\"\n        );\n    case 413:\n        return new AjaxError(\n            message || \"Payload too large\",\n            err, \"PAYLOAD_TOO_LARGE\"\n        );\n    case 502:\n        return new AjaxError(\n            message || \"The destination is acting weird\",\n            err, \"BAD_GATEWAY\"\n        );\n    case 409:\n        return new AjaxError(\n            message || \"Oups you just ran into a conflict\",\n            err, \"CONFLICT\"\n        );\n    case 0:\n        switch (responseText) {\n        case \"\":\n            return new AjaxError(\n                \"Service unavailable, if the problem persist, contact your administrator\",\n                err, \"INTERNAL_SERVER_ERROR\"\n            );\n        default:\n            return new AjaxError(responseText, err, \"INTERNAL_SERVER_ERROR\");\n        }\n    default:\n        return new AjaxError(message || \"Oups something went wrong\", err);\n    }\n}\n"
  },
  {
    "path": "public/assets/lib/animate.d.ts",
    "content": "type TransitionEnter = {\n    timeEnter: number;\n    enter: AnimationFrames[];\n}\ntype TransitionLeave = {\n    timeLeave: number;\n    leave: AnimationFrames[];\n};\ntype AnimationFrames = {\n    transform?: string;\n    opacity?: number;\n    height?: string;\n    width?: string;\n    offset?: number;\n};\n\nexport function transition($node: HTMLElement, opts?: TransitionEnter | TransitionLeave): HTMLElement;\n\nexport function animate($node: HTMLElement | null, opts: {\n    time: number;\n    keyframes: AnimationFrames[];\n    easing?: string;\n    fill?: string;\n    onExit?: () => void;\n    onEnter?: () => void;\n}): Promise<() => void>;\n\nexport function slideXIn(dist: number): AnimationFrames[];\n\nexport function slideXOut(dist: number): AnimationFrames[];\n\nexport function opacityIn(): AnimationFrames[];\n\nexport function opacityOut(): AnimationFrames[];\n\nexport function slideYIn(dist: number): AnimationFrames[];\n\nexport function slideYOut(dist: number): AnimationFrames[];\n\nexport function zoomIn(size: number): AnimationFrames[];\n"
  },
  {
    "path": "public/assets/lib/animate.js",
    "content": "import { onDestroy, nop } from \"./skeleton/index.js\";\n\nexport function transition($node, opts = {}) {\n    const {\n        timeEnter = 250, enter = slideXIn(5),\n        timeLeave = 100, leave = opacityOut()\n    } = opts;\n    animate($node, { time: timeEnter, keyframes: enter });\n    onDestroy(async() => await animate($node, { time: timeLeave, keyframes: leave }));\n    return $node;\n}\n\nexport function animate($node, opts = {}) {\n    const {\n        time = 250,\n        keyframes = opacityIn(),\n        fill = \"forwards\", easing = \"ease\",\n        onEnter = nop, onExit = nop,\n    } = opts;\n\n    if (!$node ||\n         typeof $node.animate !== \"function\" ||\n         window.matchMedia(`(prefers-reduced-motion: reduce)`) === true ||\n         window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true) {\n        onEnter();\n        onExit();\n        return Promise.resolve();\n    }\n    onEnter();\n    return new Promise((done) => {\n        const handler = $node.animate(keyframes, {\n            duration: time,\n            fill,\n            easing,\n        });\n        handler.onfinish = () => {\n            onExit();\n            done(() => handler.cancel());\n        };\n    });\n}\n\nexport const slideXIn = (dist) => ([\n    { transform: `translateX(${dist}px)`, opacity: 0 },\n    { transform: \"translateX(0)\", opacity: 1 }\n]);\n\nexport const slideXOut = (size) => ([\n    { opacity: 1, transform: \"translateX(0)\" },\n    { opacity: 0, transform: `translateX(${size}px)` }\n]);\n\nexport const opacityIn = () => ([\n    { opacity: 0 },\n    { opacity: 1 }\n]);\n\nexport const opacityOut = () => ([\n    { opacity: 1 },\n    { opacity: 0 }\n]);\n\nexport const slideYIn = (size) => ([\n    { opacity: 0, transform: `translateY(${size}px)` },\n    { opacity: 1, transform: \"translateY(0)\" }\n]);\n\nexport const slideYOut = (size) => ([\n    { opacity: 1, transform: \"translateY(0px)\" },\n    { opacity: 0, transform: `translateY(${size}px)` }\n]);\n\nexport const zoomIn = (size) => ([\n    { opacity: 0, transform: `scale(${size})` },\n    { opacity: 1, transform: \"scale(1)\" }\n]);\n"
  },
  {
    "path": "public/assets/lib/assert.js",
    "content": "export default class assert {\n    /**\n     * @param {*} object\n     * @param {Function} type\n     * @param {string} [msg]\n     * @return {*}\n     * @throws {TypeError}\n     */\n    static type(object, type, msg) {\n        if (object === undefined) throw new TypeError(msg || \"assertion failed - undefined object\");\n        if (!(object instanceof type)) throw new TypeError(msg || `assertion failed - unexpected type for ${JSON.stringify(object)}`);\n        return object;\n    }\n\n    /**\n     * @param {*} object\n     * @param {string} type\n     * @param {string} [msg]\n     * @return {*}\n     * @throws {TypeError}\n     */\n    static typeof(object, type, msg) {\n        if (typeof object !== type) throw new TypeError(msg || `assertion failed - unexpected type for ${JSON.stringify(object)}`); // eslint-disable-line valid-typeof\n        return object;\n    }\n\n    /**\n     * @param {*} object\n     * @param {string} [msg]\n     * @return {*}\n     * @throws {TypeError}\n     */\n    static truthy(object, msg) {\n        if (!object) throw new TypeError(msg || `assertion failed - object is not truthy`);\n        return object;\n    }\n\n    /**\n     * @param {string} msg\n     * @throws {TypeError}\n     */\n    static fail(msg) {\n        throw new TypeError(msg);\n    }\n}\n"
  },
  {
    "path": "public/assets/lib/chromecast.js",
    "content": "class ChromecastManager {\n    init() {\n        // TODO: additional rules for setup\n        const src = \"https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1\";\n        if (document.head.querySelector(`script[src=\"${src}\"]`)) return Promise.resolve();\n\n        return new Promise((done) => {\n            const script = document.createElement(\"script\");\n            script.src = src;\n            script.onerror = () => done();\n            window[\"__onGCastApiAvailable\"] = function(isAvailable) {\n                if (isAvailable) window.cast.framework.CastContext.getInstance().setOptions({\n                    receiverApplicationId: window.chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,\n                    autoJoinPolicy: window.chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED,\n                });\n                done();\n            };\n            document.head.appendChild(script);\n        });\n    }\n\n    origin() {\n        return location.origin;\n    };\n\n    isAvailable() {\n        if (!window.chrome) return false;\n        else if (!window.chrome.cast) return false;\n        return window.chrome.cast.isAvailable;\n    }\n\n    // createLink(apiPath) {\n    //     const target = new URL(this.origin() + apiPath);\n    //     const shareID = new window.URL(location.href).searchParams.get(\"share\");\n    //     if (shareID) target.searchParams.append(\"share\", shareID);\n    //     return target.toString();\n    // }\n\n    createRequest(mediaInfo, authorization) {\n        if (!authorization) Promise.reject(new Error(\"Invalid account\"));\n\n        // TODO: it would be much much nicer to set the authorization in an HTTP header\n        // but this would require to create a custom web receiver app, setup accounts on\n        // google, etc,... Until that happens, we're setting the authorization within the\n        // url. Once we have that app, the authorisation will come from a customData field\n        // of a chrome.cast.media.LoadRequest\n        const target = new URL(mediaInfo.contentId);\n        target.searchParams.append(\"authorization\", window.Session.authorization);\n        mediaInfo.contentId = target.toString();\n        return new window.chrome.cast.media.LoadRequest(mediaInfo);\n    }\n\n    context() {\n        if (!this.isAvailable()) return;\n        return window.cast.framework.CastContext.getInstance();\n    }\n\n    session() {\n        const context = this.context();\n        if (!context) return;\n        return context.getCurrentSession();\n    }\n\n    media() {\n        const session = this.session();\n        if (!session) return;\n        return session.getMediaSession();\n    }\n}\n\nexport default new ChromecastManager();\n"
  },
  {
    "path": "public/assets/lib/dom.d.ts",
    "content": "export function qs($node: HTMLElement | DocumentFragment, selector: string);\n\nexport function qsa($node: HTMLElement | DocumentFragment, selector: string);\n\nexport function safe(str: string | null): string;\n"
  },
  {
    "path": "public/assets/lib/dom.js",
    "content": "export function qs($node, selector) {\n    if (!$node) throw new TypeError(\"undefined node\");\n    const $target = $node.querySelector(selector);\n    if (!$target) throw new DOMException(`undefined node for selector '${selector}'`, \"NotFoundError\");\n    return $target;\n}\n\nexport function qsa($node, selector) {\n    if (!$node) throw new TypeError(\"undefined node\");\n    return $node.querySelectorAll(selector);\n}\n\nexport function safe(str) {\n    if (typeof str !== \"string\") return \"\";\n    const $div = document.createElement(\"div\");\n    $div.textContent = str;\n    return ($div.innerHTML || \"\").replaceAll(\"\\\"\", \"&quot;\");\n}\n"
  },
  {
    "path": "public/assets/lib/error.d.ts",
    "content": "export class AjaxError extends Error {\n    constructor(message: string, err?: any, code?: string);\n    code(): string;\n    err(): any;\n    type(): string;\n}\n\nexport class ApplicationError extends Error {\n    constructor(message: string, debug: string);\n    debugMsg: string;\n    type(): string;\n    debug(): string;\n}\n"
  },
  {
    "path": "public/assets/lib/error.js",
    "content": "export class AjaxError extends Error {\n    constructor(message, err = null, code = \"UNDEFINED_CODE\") {\n        super(message);\n        this.name = this.constructor.name;\n        this.errCode = code;\n        this.errOrig = err;\n    }\n\n    code() {\n        return this.errCode;\n    }\n\n    err() {\n        return this.errOrig;\n    }\n\n    type() {\n        return \"AjaxError\";\n    }\n}\n\nexport class ApplicationError extends Error {\n    constructor(message, debug) {\n        super(message);\n        this.debugMsg = debug;\n    }\n\n    type() {\n        return \"ApplicationError\";\n    }\n\n    debug() {\n        return this.debugMsg || \"N/A\";\n    }\n}\n"
  },
  {
    "path": "public/assets/lib/form.d.ts",
    "content": "// type FormOption = {\n// };\n\nexport function mutateForm(formSpec: object, formState: object): object;\n\nexport function createFormNodes(node: object, opts: object): Promise<object>;\n\nexport function createForm(node: object, opts: object): Promise<HTMLElement>;\n"
  },
  {
    "path": "public/assets/lib/form.js",
    "content": "import { createElement } from \"./skeleton/index.js\";\nimport { ApplicationError } from \"./error.js\";\nimport { animate } from \"./animate.js\";\n\nexport function mutateForm(formSpec, formState) {\n    Object.keys(formState).forEach((inputName) => {\n        const value = formState[inputName];\n        const keys = inputName.split(\".\");\n\n        let ptr = formSpec;\n        while (keys.length > 1) {\n            const k = keys.shift();\n            if (!k) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: missing key\");\n            ptr = ptr[k];\n        }\n        const key = keys.shift() || \"\";\n        if (ptr && ptr[key]) ptr[key].value = (value === \"\" ? null : value);\n    });\n    return formSpec;\n}\n\nasync function createFormNodes(node, { renderNode, renderLeaf, renderInput, path = [], level = 0 }) {\n    // CASE 0: invalid form spec\n    if (typeof node !== \"object\") {\n        return [createElement(`<div>ERR: node[${typeof node}] path[${path.join(\".\")}] level[${level}]</div>`)];\n    }\n    const $list = [];\n    for (const key of Object.keys(node)) {\n        if (typeof node[key] !== \"object\") {\n            $list.push(createElement(`<div>ERR: node[${typeof node[key]}] path[${path.join(\".\")}] level[${level}]</div>`));\n        }\n        // CASE 1: non leaf node\n        else if (typeof node[key].type !== \"string\") {\n            const $chunk = renderNode({ level, label: key });\n            const $children = $chunk.querySelector(\"[data-bind=\\\"children\\\"]\") || $chunk;\n            $children.removeAttribute(\"data-bind\");\n            const $nested = await createForm(node[key], {\n                path: path.concat(key),\n                level: level + 1,\n                label: key,\n                renderNode,\n                renderLeaf,\n                renderInput\n            });\n            $children.appendChild($nested);\n            $list.push($chunk);\n        }\n        // CASE 2: leaf node\n        else {\n            const currentPath = path.concat(key);\n            const $leaf = renderLeaf({\n                ...node[key],\n                path: currentPath,\n                label: key,\n            });\n            const $input = await renderInput({ ...node[key], path: currentPath.filter((chunk) => !!chunk) });\n            const $target = $leaf.querySelector(\"[data-bind=\\\"children\\\"]\") || $leaf;\n\n            // leaf node is either \"classic\" or can be the target of something that can be toggled\n            // That's how we can hide input elements conditionally for use cases like the log level\n            // settings that will not be visible unless log is first enabled or the advanced section\n            // of the login screen\n            const isAToggleElementItself = typeof node[key].id === \"string\";\n            const canToggleOtherElements = node[key].type === \"enable\" && node[key].target && node[key].target.length > 0;\n            if (!isAToggleElementItself) {\n                $target.removeAttribute(\"data-bind\");\n                $target.appendChild($input);\n                $list.push($leaf);\n            }\n            if (canToggleOtherElements) {\n                // initialise the dom structure\n                const $container = window.document.createElement(\"div\");\n                $container.classList.add(\"advanced_form\");\n                $container.style.setProperty(\"overflow-x\", \"hidden\");\n                for (const k of Object.keys(node)) {\n                    if (typeof node[k] !== \"object\") continue;\n                    else if (!node[k].id) continue;\n                    else if (node[key].target.indexOf(node[k].id) === -1) continue;\n\n                    const $kleaf = renderLeaf({ ...node[k], path: path.concat(k), label: k });\n                    const $kinput = await renderInput({ ...node[k], path: path.concat(k) });\n                    const $ktarget = $kleaf.querySelector(\"[data-bind=\\\"children\\\"]\") || $kleaf;\n                    $ktarget.removeAttribute(\"data-bind\");\n                    $ktarget.appendChild($kinput);\n                    $container.appendChild($kleaf);\n                }\n                $list.push($container);\n\n                // initial state of the toggle\n                const isToggled = typeof node[key].value === \"boolean\" ? node[key].value : node[key].default;\n                if (!isToggled) $container.style.setProperty(\"display\", \"none\");\n                let clientHeight = null; // this will only be known when the dom is mounted\n\n                // setup events\n                $input.onchange = async(e) => {\n                    $container.style.setProperty(\"display\", \"inherit\");\n                    if (clientHeight === null) clientHeight = $container.offsetHeight;\n                    if (e.target.checked) {\n                        animate($container, {\n                            time: Math.max(50, Math.min(clientHeight, 150)),\n                            keyframes: [{ height: \"0\" }, { height: `${clientHeight}px` }]\n                        });\n                    } else {\n                        animate($container, {\n                            time: Math.max(25, Math.min(clientHeight, 75)),\n                            keyframes: [{ height: `${clientHeight}px` }, { height: \"0\" }]\n                        });\n                    }\n                };\n            }\n        }\n    }\n    return $list;\n}\n\nexport async function createForm(node, opts) {\n    const $container = window.document.createElement(\"div\");\n    if (!(opts.level >= 1)) $container.classList.add(\"formbuilder\");\n    const $nodes = await createFormNodes(node, opts);\n    $nodes.forEach(($node) => {\n        $container.appendChild($node);\n    });\n    return $container;\n}\n"
  },
  {
    "path": "public/assets/lib/path.js",
    "content": "export function basename(str, sep = \"/\") {\n    return str.substr(str.lastIndexOf(sep) + 1);\n}\n\nexport function extname(str) {\n    return str.substr(str.lastIndexOf(\".\") + 1).toLowerCase();\n}\n\nexport function join(baseURL, segment) {\n    const url = new URL(segment, baseURL);\n    return decodeURIComponent(url.pathname + url.hash);\n}\n\nexport function forwardURLParams(url, allowed = []) {\n    const link = new URL(window.location.origin + \"/\" + url);\n    for (const [key, value] of new URLSearchParams(location.search)) {\n        if (allowed.indexOf(key) < 0) continue;\n        else if (link.searchParams.getAll(key).indexOf(value) !== -1) continue;\n        link.searchParams.append(key, value);\n    }\n    return link.pathname.substring(1) + link.search;\n}\n"
  },
  {
    "path": "public/assets/lib/polyfill.js",
    "content": "Document.prototype.replaceChildren = replaceChildren;\nDocumentFragment.prototype.replaceChildren = replaceChildren;\nElement.prototype.replaceChildren = replaceChildren;\n\nfunction replaceChildren(...new_children) {\n    const { childNodes } = this;\n    while (childNodes.length) {\n        childNodes[0].remove();\n    }\n    this.append(...new_children);\n}\n"
  },
  {
    "path": "public/assets/lib/random.d.ts",
    "content": "export function gid(prefix: string): string;\n\nexport function randomString(size: number): string;\n"
  },
  {
    "path": "public/assets/lib/random.js",
    "content": "export function gid(prefix = \"\") {\n    let id = prefix;\n    id += new Date().getTime().toString(32);\n    id += Math.random().toString(32).replace(/^0\\./, \"\");\n    return id;\n}\n\nconst alphabet = [\n    \"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\",\n    \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"x\", \"y\", \"z\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\",\n    \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\",\n    \"X\", \"Y\", \"Z\", \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\",\n];\nconst alphabet_size = alphabet.length;\n\nexport function randomString(size = 16) {\n    let str = \"\";\n    for (let i=0; i<size; i++) {\n        str += alphabet[Math.floor(Math.random()*alphabet_size)];\n    }\n    return str;\n}\n"
  },
  {
    "path": "public/assets/lib/rx.d.ts",
    "content": "import type { Observer, Observable as coreObservable } from \"rx-core\";\n\nimport {\n    fromEvent, startWith, Observable,\n    catchError, tap, first, of, pairwise,\n    map, mapTo, filter, mergeMap, EMPTY, empty,\n    switchMapTo, switchMap,\n    BehaviorSubject, Subject, ReplaySubject,\n    pipe, share, toArray, distinctUntilChanged, from, finalize,\n    combineLatest, shareReplay, race, repeat, interval, merge,\n    debounceTime, debounce, delay, concatMap, distinct, scan, throwError,\n    zip, animationFrames, retry, forkJoin, skip, takeUntil, timer,\n} from \"./vendor/rxjs/rxjs.min.js\";\n\nimport * as rxajax from \"./vendor/rxjs/rxjs-ajax.min.js\";\n\ndeclare const rxjs: {\n    of: typeof of,\n    from: typeof from,\n    Observable: typeof Observable,\n    BehaviorSubject: typeof BehaviorSubject,\n    ReplaySubject: typeof ReplaySubject,\n    Subject: typeof Subject,\n    catchError: typeof catchError,\n    combineLatest: typeof combineLatest,\n    distinct: typeof distinct,\n    shareReplay: typeof shareReplay,\n    repeat: typeof repeat,\n    first: typeof first,\n    toArray: typeof toArray,\n    startWith: typeof startWith,\n    fromEvent: typeof fromEvent,\n    delay: typeof delay,\n    concatMap: typeof concatMap,\n    animationFrames: typeof animationFrames,\n    debounce: typeof debounce,\n    debounceTime: typeof debounceTime,\n    throwError: typeof throwError,\n    interval: typeof interval,\n    merge: typeof merge,\n    tap: typeof tap,\n    map: typeof map,\n    mapTo: typeof mapTo,\n    filter: typeof filter,\n    finalize: typeof finalize,\n    mergeMap: typeof mergeMap,\n    switchMapTo: typeof switchMapTo,\n    switchMap: typeof switchMap,\n    distinctUntilChanged: typeof distinctUntilChanged,\n    EMPTY: typeof EMPTY,\n    empty: typeof empty,\n    pipe: typeof pipe,\n    share: typeof share,\n    scan: typeof scan,\n    race: typeof race,\n    retry: typeof retry,\n    zip: typeof zip,\n    forkJoin: typeof forkJoin,\n    skip: typeof skip,\n    takeUntil: typeof takeUntil,\n    timer: typeof timer,\n    pairwise: typeof pairwise,\n};\n\nexport default rxjs;\n\nexport function ajax(opts: object);\n\nexport function effect(obs: Observer<any>): void;\n\nexport function applyMutation($node: HTMLElement, ...keys: string[]): typeof tap;\n\nexport function applyMutations($node: HTMLElement, ...keys: string[]): typeof tap;\n\nexport function stateMutation($node: HTMLElement, attr: string);\n\nexport function preventDefault(): typeof tap;\n\nexport function onClick($node: HTMLElement,  opts?: { preventDefault?: boolean }): ReturnType<typeof fromEvent>;\n\nexport function onLoad($node: HTMLElement): void;"
  },
  {
    "path": "public/assets/lib/rx.js",
    "content": "import { onDestroy } from \"./skeleton/index.js\";\nimport assert from \"./assert.js\";\nimport * as rxjs from \"./vendor/rxjs/rxjs.min.js\";\nimport * as ajaxModule from \"./vendor/rxjs/rxjs-ajax.min.js\"; // https://github.com/ReactiveX/rxjs/issues/4416#issuecomment-620847759\n\nexport default rxjs;\nexport const ajax = ajaxModule.ajax;\n\nexport function effect(obs) {\n    const sub = obs.subscribe(() => {}, (err) => { throw err; });\n    onDestroy(() => sub.unsubscribe());\n    return sub.unsubscribe.bind(sub);\n}\n\nconst getFn = (obj, arg0, ...args) => {\n    if (arg0 === undefined) return obj;\n    const next = obj[arg0];\n    return getFn(next.bind ? next.bind(obj) : next, ...args);\n};\n\nexport function applyMutation($node, ...keys) {\n    assert.type($node, HTMLElement);\n    const execute = getFn($node, ...keys);\n    return rxjs.tap((val) => Array.isArray(val) ? execute(...val) : execute(val));\n}\n\nexport function applyMutations($node, ...keys) {\n    assert.type($node, HTMLElement);\n    const execute = getFn($node, ...keys);\n    return rxjs.tap((vals) => vals.forEach((val) => execute(val)));\n}\n\nexport function stateMutation($node, attr) {\n    assert.type($node, HTMLElement);\n    return rxjs.tap((val) => $node[attr] = val);\n}\n\nexport function preventDefault() {\n    return rxjs.tap((e) => e.preventDefault());\n}\n\nexport function onClick($node, opts = { preventDefault: false }) {\n    const sideE = ($node) => {\n        assert.type($node, HTMLElement);\n        return rxjs.fromEvent($node, \"click\").pipe(\n            rxjs.tap((e) => (opts.preventDefault === true) && e.preventDefault()),\n            rxjs.map(() => $node)\n        );\n    };\n    if ($node instanceof NodeList) return rxjs.merge(\n        ...[...$node].map(($n) => sideE($n)),\n    );\n    return sideE($node);\n}\n\nexport function onLoad($node) {\n    assert.type($node, HTMLElement);\n    return new rxjs.Observable((observer) => {\n        $node.onload = () => {\n            observer.next($node);\n            observer.complete();\n        };\n        $node.onerror = (err) => observer.error(err);\n    });\n}\n"
  },
  {
    "path": "public/assets/lib/settings.js",
    "content": "const settings = JSON.parse(window.localStorage.getItem(\"settings\") || \"null\") || {};\n\nexport function settings_get(key, def = null) {\n    if (settings[key] === undefined) {\n        return def;\n    }\n    return settings[key];\n}\n\nexport function settings_put(key, value) {\n    settings[key] = value;\n    setTimeout(() => {\n        window.localStorage.setItem(\"settings\", JSON.stringify(settings));\n    }, 0);\n}\n"
  },
  {
    "path": "public/assets/lib/skeleton/index.d.ts",
    "content": "import { onDestroy } from \"./lifecycle\";\nimport { navigate } from \"./router\";\n\nexport default function($root: HTMLElement | null, routes: object, opts: object);\n\nexport function createElement(str: string): HTMLElement;\n\nexport function createFragment(str: string): DocumentFragment;\n\nexport function createRender($parent: HTMLElement | null): (HTMLElement) => void;\n\nexport function nop(): void\n\nexport { onDestroy, navigate };\n"
  },
  {
    "path": "public/assets/lib/skeleton/index.js",
    "content": "import { init as initRouter, currentRoute } from \"./router.js\";\nimport { init as initDOM } from \"./lifecycle.js\";\n\nexport { navigate } from \"./router.js\";\nexport { onDestroy } from \"./lifecycle.js\";\n\nlet pageLoader;\n\nexport default async function($root, routes, opts = {}) {\n    if (!$root) throw new Error(\"cannot find root element\");\n    window.addEventListener(\"pagechange\", async() => {\n        try {\n            const route = currentRoute(routes, \"\");\n            const [ctrl] = await Promise.all([\n                load(route, { ...opts, $root }),\n                $root.cleanup(),\n            ]);\n            if (typeof ctrl !== \"function\") throw new Error(`Unknown route for ${route}`);\n            pageLoader = ctrl(createRender($root));\n        } catch (err) {\n            console.error(\"skeleton::index.js\", err);\n            window.onerror && window.onerror(err.message);\n        }\n    });\n\n    await initDOM($root);\n    await opts.beforeStart;\n    await initRouter($root);\n}\n\nasync function load(route, opts) {\n    const { spinner = \"loading ...\", spinnerTime = 200, $root } = opts;\n    let ctrl;\n    if (typeof route === \"function\") {\n        ctrl = route;\n    } else if (typeof route === \"string\") {\n        let spinnerID;\n        if (pageLoader && typeof pageLoader === \"function\") {\n            spinnerID = setTimeout(() => pageLoader(createRender($root)), spinnerTime);\n        } else if (typeof spinner === \"string\") {\n            spinnerID = setTimeout(() => $root.innerHTML = spinner, spinnerTime);\n        }\n        const module = window.env === \"test\"\n            ? require(\"../..\" + route)\n            : await import(new URL(\"../..\" + route, import.meta.url));\n\n        if (typeof module.init === \"function\") await module.init();\n\n        clearTimeout(spinnerID);\n        if (typeof module.default !== \"function\") {\n            console.error(module, module.default);\n            throw new Error(`missing default export on ${route}`);\n        }\n        ctrl = module.default;\n    }\n    return ctrl;\n}\n\nexport function createElement(str) {\n    const $n = window.document.createElement(\"div\");\n    $n.innerHTML = str;\n    if (!($n.firstElementChild instanceof HTMLElement)) throw new Error(\"createElement - unexpected type\");\n    return $n.firstElementChild;\n}\n\nexport function createFragment(str) {\n    const $n = window.document.createElement(\"div\");\n    $n.innerHTML = str;\n    const $doc = document.createDocumentFragment();\n    for (let i=0; i<$n.children.length; i++) $doc.appendChild($n.children[i].cloneNode(true));\n    $n.remove();\n    return $doc;\n}\n\nexport function createRender($parent) {\n    if (!($parent instanceof HTMLElement)) throw new Error(`assert failed: createRender on non HTMLElement`);\n    return ($view) => {\n        if ($view instanceof HTMLElement) $parent.replaceChildren($view);\n        else if ($view instanceof DocumentFragment) $parent.replaceChildren($view);\n        else throw new Error(`Unknown view type: ${typeof $view}`);\n        return $parent;\n    };\n}\n\nexport function nop() { Promise.resolve(); }\n"
  },
  {
    "path": "public/assets/lib/skeleton/lifecycle.d.ts",
    "content": "export function init($root: HTMLElement): Promise<void>;\n\nexport function onDestroy(fn: Function): Promise<void>;"
  },
  {
    "path": "public/assets/lib/skeleton/lifecycle.js",
    "content": "let _cleanup = [];\n\nexport async function init($root) {\n    $root.cleanup = () => {\n        const fns = _cleanup.map((fn) => fn($root));\n        _cleanup = [];\n        return Promise.all(fns);\n    };\n}\n\nexport async function onDestroy(fn) {\n    _cleanup.push(fn);\n}\n"
  },
  {
    "path": "public/assets/lib/skeleton/router.d.ts",
    "content": "export function init($root: HTMLElement): Promise<void>;\n\nexport function navigate(href: string);\n\nexport function currentRoute(r: object, notFoundRoute: string);\n\nexport function base();\n\nexport function fromHref(h: string): string;\n\nexport function toHref(h: string): string;"
  },
  {
    "path": "public/assets/lib/skeleton/router.js",
    "content": "const triggerPageChange = () => window.dispatchEvent(new window.Event(\"pagechange\"));\nconst trimPrefix = (value = \"\", prefix) => value.startsWith(prefix) ? value.slice(prefix.length) : value;\n\nconst _base = window.document.head.querySelector(\"base\")?.getAttribute(\"href\")?.replace(new RegExp(\"/$\"), \"\");\nexport const base = () => _base || \"\";\nexport const fromHref = (href) => trimPrefix(href, base());\nexport const toHref = (href) => base() + href;\n\nexport async function init($root) {\n    window.addEventListener(\"popstate\", triggerPageChange);\n    $root.addEventListener(\"click\", (e) => {\n        if (e.ctrlKey || e.metaKey) return;\n        const href = _getHref(e.target, $root);\n        return !href ? null : e.preventDefault() || navigate(href);\n    });\n}\n\nexport async function navigate(href) {\n    if (typeof window.history.block === \"function\") {\n        const block = await window.history.block(href);\n        if (block) return;\n    }\n    delete window.history.block;\n    window.history.pushState({\n        ...JSON.parse(JSON.stringify(window.history)),\n        previous: window.location.pathname,\n    }, \"\", href);\n    triggerPageChange();\n}\n\nexport function currentRoute(r, notFoundRoute) {\n    const currentRoute = fromHref(window.location.pathname);\n    for (const routeKey in r) {\n        if (new RegExp(\"^\" + routeKey + \"$\").test(currentRoute)) {\n            return r[routeKey];\n        }\n    }\n    return r[notFoundRoute] || null;\n}\n\nfunction _getHref($node, $root) {\n    if ($node.matches(\"[data-link]\")) return $node.getAttribute(\"href\");\n    if (!$node.parentElement || $node.isSameNode($root)) return null;\n    return _getHref($node.parentElement, $root);\n}\n"
  },
  {
    "path": "public/assets/lib/store.js",
    "content": "export function settingsGet(initialValues, prefix = \"\") {\n    const raw = JSON.parse(localStorage.getItem(\"settings\") || \"{}\") || {};\n    const currentSettings = {};\n    Object.keys(initialValues).forEach((key) => {\n        const settingsKey = prefix ? `${prefix}_${key}` : key;\n        if (settingsKey in raw) currentSettings[key] = raw[settingsKey];\n        else currentSettings[key] = initialValues[key];\n    });\n    return currentSettings;\n}\n\nexport function settingsSave(currentValues, prefix = \"\") {\n    const raw = JSON.parse(localStorage.getItem(\"settings\") || \"{}\") || {};\n    Object.keys(currentValues).forEach((key) => {\n        const settingsKey = prefix ? `${prefix}_${key}` : key;\n        raw[settingsKey] = currentValues[key];\n    });\n    localStorage.setItem(\"settings\", JSON.stringify(raw));\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/bcrypt.js",
    "content": "// @ts-nocheck\r\n// code was adapted from https://github.com/dcodeIO/bcrypt.js, meaning:\r\n// - we took the code from a CDN https://cdnjs.cloudflare.com/ajax/libs/bcryptjs/2.2.0/bcrypt.js\r\n// - remove the amd,commonJS stuff on the top of the file\r\n// - replaced \"global\" with \"window\"\r\n\r\n/**\r\n * bcrypt namespace.\r\n * @type {Object.<string,*>}\r\n */\r\nvar bcrypt = {};\r\n\r\n/**\r\n * The random implementation to use as a fallback.\r\n * @type {?function(number):!Array.<number>}\r\n * @inner\r\n */\r\nvar randomFallback = null;\r\n\r\n/**\r\n * Generates cryptographically secure random bytes.\r\n * @function\r\n * @param {number} len Bytes length\r\n * @returns {!Array.<number>} Random bytes\r\n * @throws {Error} If no random implementation is available\r\n * @inner\r\n */\r\nfunction random(len) {\r\n    /* node */ if (typeof module !== 'undefined' && module && module['exports'])\r\n        try {\r\n            return require(\"crypto\")['randomBytes'](len);\r\n        } catch (e) {}\r\n    /* WCA */ try {\r\n        var a; (window['crypto']||window['msCrypto'])['getRandomValues'](a = new Uint32Array(len));\r\n        return Array.prototype.slice.call(a);\r\n    } catch (e) {}\r\n    /* fallback */ if (!randomFallback)\r\n        throw Error(\"Neither WebCryptoAPI nor a crypto module is available. Use bcrypt.setRandomFallback to set an alternative\");\r\n    return randomFallback(len);\r\n}\r\n\r\n// Test if any secure randomness source is available\r\nvar randomAvailable = false;\r\ntry {\r\n    random(1);\r\n    randomAvailable = true;\r\n} catch (e) {}\r\n\r\n// Default fallback, if any\r\nrandomFallback = null;\r\n\r\n/**\r\n * Sets the pseudo random number generator to use as a fallback if neither node's `crypto` module nor the Web Crypto\r\n *  API is available. Please note: It is highly important that the PRNG used is cryptographically secure and that it\r\n *  is seeded properly!\r\n * @param {?function(number):!Array.<number>} random Function taking the number of bytes to generate as its\r\n *  sole argument, returning the corresponding array of cryptographically secure random byte values.\r\n * @see http://nodejs.org/api/crypto.html\r\n * @see http://www.w3.org/TR/WebCryptoAPI/\r\n */\r\nbcrypt.setRandomFallback = function(random) {\r\n    randomFallback = random;\r\n};\r\n\r\n/**\r\n * Synchronously generates a salt.\r\n * @param {number=} rounds Number of rounds to use, defaults to 10 if omitted\r\n * @param {number=} seed_length Not supported.\r\n * @returns {string} Resulting salt\r\n * @throws {Error} If a random fallback is required but not set\r\n * @expose\r\n */\r\nbcrypt.genSaltSync = function(rounds, seed_length) {\r\n    if (typeof rounds === 'undefined')\r\n        rounds = GENSALT_DEFAULT_LOG2_ROUNDS;\r\n    else if (typeof rounds !== 'number')\r\n        throw Error(\"Illegal arguments: \"+(typeof rounds)+\", \"+(typeof seed_length));\r\n    if (rounds < 4 || rounds > 31)\r\n        throw Error(\"Illegal number of rounds (4-31): \"+rounds);\r\n    var salt = [];\r\n    salt.push(\"$2a$\");\r\n    if (rounds < 10)\r\n        salt.push(\"0\");\r\n    salt.push(rounds.toString());\r\n    salt.push('$');\r\n    salt.push(base64_encode(random(BCRYPT_SALT_LEN), BCRYPT_SALT_LEN)); // May throw\r\n    return salt.join('');\r\n};\r\n\r\n/**\r\n * Asynchronously generates a salt.\r\n * @param {(number|function(Error, string=))=} rounds Number of rounds to use, defaults to 10 if omitted\r\n * @param {(number|function(Error, string=))=} seed_length Not supported.\r\n * @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting salt\r\n * @expose\r\n */\r\nbcrypt.genSalt = function(rounds, seed_length, callback) {\r\n    if (typeof seed_length === 'function')\r\n        callback = seed_length,\r\n    seed_length = undefined; // Not supported.\r\n    if (typeof rounds === 'function')\r\n        callback = rounds,\r\n    rounds = GENSALT_DEFAULT_LOG2_ROUNDS;\r\n    if (typeof callback !== 'function')\r\n        throw Error(\"Illegal callback: \"+typeof(callback));\r\n    if (typeof rounds !== 'number') {\r\n        nextTick(callback.bind(this, Error(\"Illegal arguments: \"+(typeof rounds))));\r\n        return;\r\n    }\r\n    nextTick(function() { // Pretty thin, but salting is fast enough\r\n        try {\r\n            callback(null, bcrypt.genSaltSync(rounds));\r\n        } catch (err) {\r\n            callback(err);\r\n        }\r\n    });\r\n};\r\n\r\n/**\r\n * Synchronously generates a hash for the given string.\r\n * @param {string} s String to hash\r\n * @param {(number|string)=} salt Salt length to generate or salt to use, default to 10\r\n * @returns {string} Resulting hash\r\n * @expose\r\n */\r\nbcrypt.hashSync = function(s, salt) {\r\n    if (typeof salt === 'undefined')\r\n        salt = GENSALT_DEFAULT_LOG2_ROUNDS;\r\n    if (typeof salt === 'number')\r\n        salt = bcrypt.genSaltSync(salt);\r\n    if (typeof s !== 'string' || typeof salt !== 'string')\r\n        throw Error(\"Illegal arguments: \"+(typeof s)+', '+(typeof salt));\r\n    return _hash(s, salt);\r\n};\r\n\r\n/**\r\n * Asynchronously generates a hash for the given string.\r\n * @param {string} s String to hash\r\n * @param {number|string} salt Salt length to generate or salt to use\r\n * @param {function(Error, string=)} callback Callback receiving the error, if any, and the resulting hash\r\n * @param {function(number)=} progressCallback Callback successively called with the percentage of rounds completed\r\n *  (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms.\r\n * @expose\r\n */\r\nbcrypt.hash = function(s, salt, callback, progressCallback) {\r\n    if (typeof callback !== 'function')\r\n        throw Error(\"Illegal callback: \"+typeof(callback));\r\n    if (typeof s === 'string' && typeof salt === 'number')\r\n        bcrypt.genSalt(salt, function(err, salt) {\r\n            _hash(s, salt, callback, progressCallback);\r\n        });\r\n    else if (typeof s === 'string' && typeof salt === 'string')\r\n        _hash(s, salt, callback, progressCallback);\r\n    else\r\n        nextTick(callback.bind(this, Error(\"Illegal arguments: \"+(typeof s)+', '+(typeof salt))));\r\n};\r\n\r\n/**\r\n * Synchronously tests a string against a hash.\r\n * @param {string} s String to compare\r\n * @param {string} hash Hash to test against\r\n * @returns {boolean} true if matching, otherwise false\r\n * @throws {Error} If an argument is illegal\r\n * @expose\r\n */\r\nbcrypt.compareSync = function(s, hash) {\r\n    if (typeof s !== \"string\" || typeof hash !== \"string\")\r\n        throw Error(\"Illegal arguments: \"+(typeof s)+', '+(typeof hash));\r\n    if (hash.length !== 60)\r\n        return false;\r\n    var comp = bcrypt.hashSync(s, hash.substr(0, hash.length-31)),\r\n        same = comp.length === hash.length,\r\n        max_length = (comp.length < hash.length) ? comp.length : hash.length;\r\n    // to prevent timing attacks, should check entire string\r\n    // don't exit after found to be false\r\n    for (var i = 0; i < max_length; ++i)\r\n        if (comp.length >= i && hash.length >= i && comp[i] != hash[i])\r\n            same = false;\r\n    return same;\r\n};\r\n\r\n/**\r\n * Asynchronously compares the given data against the given hash.\r\n * @param {string} s Data to compare\r\n * @param {string} hash Data to be compared to\r\n * @param {function(Error, boolean)} callback Callback receiving the error, if any, otherwise the result\r\n * @param {function(number)=} progressCallback Callback successively called with the percentage of rounds completed\r\n *  (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms.\r\n * @throws {Error} If the callback argument is invalid\r\n * @expose\r\n */\r\nbcrypt.compare = function(s, hash, callback, progressCallback) {\r\n    if (typeof callback !== 'function')\r\n        throw Error(\"Illegal callback: \"+typeof(callback));\r\n    if (typeof s !== \"string\" || typeof hash !== \"string\") {\r\n        nextTick(callback.bind(this, Error(\"Illegal arguments: \"+(typeof s)+', '+(typeof hash))));\r\n        return;\r\n    }\r\n    bcrypt.hash(s, hash.substr(0, 29), function(err, comp) {\r\n        callback(err, hash === comp);\r\n    }, progressCallback);\r\n};\r\n\r\n/**\r\n * Gets the number of rounds used to encrypt the specified hash.\r\n * @param {string} hash Hash to extract the used number of rounds from\r\n * @returns {number} Number of rounds used\r\n * @throws {Error} If hash is not a string\r\n * @expose\r\n */\r\nbcrypt.getRounds = function(hash) {\r\n    if (typeof hash !== \"string\")\r\n        throw Error(\"Illegal arguments: \"+(typeof hash));\r\n    return parseInt(hash.split(\"$\")[2], 10);\r\n};\r\n\r\n/**\r\n * Gets the salt portion from a hash. Does not validate the hash.\r\n * @param {string} hash Hash to extract the salt from\r\n * @returns {string} Extracted salt part\r\n * @throws {Error} If `hash` is not a string or otherwise invalid\r\n * @expose\r\n */\r\nbcrypt.getSalt = function(hash) {\r\n    if (typeof hash !== 'string')\r\n        throw Error(\"Illegal arguments: \"+(typeof hash));\r\n    if (hash.length !== 60)\r\n        throw Error(\"Illegal hash length: \"+hash.length+\" != 60\");\r\n    return hash.substring(0, 29);\r\n};\r\n\r\n/**\r\n * Continues with the callback on the next tick.\r\n * @function\r\n * @param {function(...[*])} callback Callback to execute\r\n * @inner\r\n */\r\nvar nextTick = typeof process !== 'undefined' && process && typeof process.nextTick === 'function'\r\n    ? (typeof setImmediate === 'function' ? setImmediate : process.nextTick)\r\n    : setTimeout;\r\n\r\n/**\r\n * Converts a JavaScript string to UTF8 bytes.\r\n * @param {string} str String\r\n * @returns {!Array.<number>} UTF8 bytes\r\n * @inner\r\n */\r\nfunction stringToBytes(str) {\r\n    var out = [],\r\n        i = 0;\r\n    utfx.encodeUTF16toUTF8(function() {\r\n        if (i >= str.length) return null;\r\n        return str.charCodeAt(i++);\r\n    }, function(b) {\r\n        out.push(b);\r\n    });\r\n    return out;\r\n}\r\n\r\n// A base64 implementation for the bcrypt algorithm. This is partly non-standard.\r\n\r\n/**\r\n * bcrypt's own non-standard base64 dictionary.\r\n * @type {!Array.<string>}\r\n * @const\r\n * @inner\r\n **/\r\nvar BASE64_CODE = \"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\".split('');\r\n\r\n/**\r\n * @type {!Array.<number>}\r\n * @const\r\n * @inner\r\n **/\r\nvar BASE64_INDEX = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r\n                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r\n                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,\r\n                    1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1,\r\n                    -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,\r\n                    20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, 28, 29, 30,\r\n                    31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,\r\n                    48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1];\r\n\r\n/**\r\n * @type {!function(...number):string}\r\n * @inner\r\n */\r\nvar stringFromCharCode = String.fromCharCode;\r\n\r\n/**\r\n * Encodes a byte array to base64 with up to len bytes of input.\r\n * @param {!Array.<number>} b Byte array\r\n * @param {number} len Maximum input length\r\n * @returns {string}\r\n * @inner\r\n */\r\nfunction base64_encode(b, len) {\r\n    var off = 0,\r\n        rs = [],\r\n        c1, c2;\r\n    if (len <= 0 || len > b.length)\r\n        throw Error(\"Illegal len: \"+len);\r\n    while (off < len) {\r\n        c1 = b[off++] & 0xff;\r\n        rs.push(BASE64_CODE[(c1 >> 2) & 0x3f]);\r\n        c1 = (c1 & 0x03) << 4;\r\n        if (off >= len) {\r\n            rs.push(BASE64_CODE[c1 & 0x3f]);\r\n            break;\r\n        }\r\n        c2 = b[off++] & 0xff;\r\n        c1 |= (c2 >> 4) & 0x0f;\r\n        rs.push(BASE64_CODE[c1 & 0x3f]);\r\n        c1 = (c2 & 0x0f) << 2;\r\n        if (off >= len) {\r\n            rs.push(BASE64_CODE[c1 & 0x3f]);\r\n            break;\r\n        }\r\n        c2 = b[off++] & 0xff;\r\n        c1 |= (c2 >> 6) & 0x03;\r\n        rs.push(BASE64_CODE[c1 & 0x3f]);\r\n        rs.push(BASE64_CODE[c2 & 0x3f]);\r\n    }\r\n    return rs.join('');\r\n}\r\n\r\n/**\r\n * Decodes a base64 encoded string to up to len bytes of output.\r\n * @param {string} s String to decode\r\n * @param {number} len Maximum output length\r\n * @returns {!Array.<number>}\r\n * @inner\r\n */\r\nfunction base64_decode(s, len) {\r\n    var off = 0,\r\n        slen = s.length,\r\n        olen = 0,\r\n        rs = [],\r\n        c1, c2, c3, c4, o, code;\r\n    if (len <= 0)\r\n        throw Error(\"Illegal len: \"+len);\r\n    while (off < slen - 1 && olen < len) {\r\n        code = s.charCodeAt(off++);\r\n        c1 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;\r\n        code = s.charCodeAt(off++);\r\n        c2 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;\r\n        if (c1 == -1 || c2 == -1)\r\n            break;\r\n        o = (c1 << 2) >>> 0;\r\n        o |= (c2 & 0x30) >> 4;\r\n        rs.push(stringFromCharCode(o));\r\n        if (++olen >= len || off >= slen)\r\n            break;\r\n        code = s.charCodeAt(off++);\r\n        c3 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;\r\n        if (c3 == -1)\r\n            break;\r\n        o = ((c2 & 0x0f) << 4) >>> 0;\r\n        o |= (c3 & 0x3c) >> 2;\r\n        rs.push(stringFromCharCode(o));\r\n        if (++olen >= len || off >= slen)\r\n            break;\r\n        code = s.charCodeAt(off++);\r\n        c4 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;\r\n        o = ((c3 & 0x03) << 6) >>> 0;\r\n        o |= c4;\r\n        rs.push(stringFromCharCode(o));\r\n        ++olen;\r\n    }\r\n    var res = [];\r\n    for (off = 0; off<olen; off++)\r\n        res.push(rs[off].charCodeAt(0));\r\n    return res;\r\n}\r\n\r\n/**\r\n * utfx-embeddable (c) 2014 Daniel Wirtz <dcode@dcode.io>\r\n * Released under the Apache License, Version 2.0\r\n * see: https://github.com/dcodeIO/utfx for details\r\n */\r\nvar utfx = function() {\r\n    \"use strict\";\r\n\r\n    /**\r\n     * utfx namespace.\r\n     * @inner\r\n     * @type {!Object.<string,*>}\r\n     */\r\n    var utfx = {};\r\n\r\n    /**\r\n     * Maximum valid code point.\r\n     * @type {number}\r\n     * @const\r\n     */\r\n    utfx.MAX_CODEPOINT = 0x10FFFF;\r\n\r\n    /**\r\n     * Encodes UTF8 code points to UTF8 bytes.\r\n     * @param {(!function():number|null) | number} src Code points source, either as a function returning the next code point\r\n     *  respectively `null` if there are no more code points left or a single numeric code point.\r\n     * @param {!function(number)} dst Bytes destination as a function successively called with the next byte\r\n     */\r\n    utfx.encodeUTF8 = function(src, dst) {\r\n        var cp = null;\r\n        if (typeof src === 'number')\r\n            cp = src,\r\n        src = function() { return null; };\r\n        while (cp !== null || (cp = src()) !== null) {\r\n            if (cp < 0x80)\r\n                dst(cp&0x7F);\r\n            else if (cp < 0x800)\r\n                dst(((cp>>6)&0x1F)|0xC0),\r\n            dst((cp&0x3F)|0x80);\r\n            else if (cp < 0x10000)\r\n                dst(((cp>>12)&0x0F)|0xE0),\r\n            dst(((cp>>6)&0x3F)|0x80),\r\n            dst((cp&0x3F)|0x80);\r\n            else\r\n                dst(((cp>>18)&0x07)|0xF0),\r\n            dst(((cp>>12)&0x3F)|0x80),\r\n            dst(((cp>>6)&0x3F)|0x80),\r\n            dst((cp&0x3F)|0x80);\r\n            cp = null;\r\n        }\r\n    };\r\n\r\n    /**\r\n     * Decodes UTF8 bytes to UTF8 code points.\r\n     * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if there\r\n     *  are no more bytes left.\r\n     * @param {!function(number)} dst Code points destination as a function successively called with each decoded code point.\r\n     * @throws {RangeError} If a starting byte is invalid in UTF8\r\n     * @throws {Error} If the last sequence is truncated. Has an array property `bytes` holding the\r\n     *  remaining bytes.\r\n     */\r\n    utfx.decodeUTF8 = function(src, dst) {\r\n        var a, b, c, d, fail = function(b) {\r\n            b = b.slice(0, b.indexOf(null));\r\n            var err = Error(b.toString());\r\n            err.name = \"TruncatedError\";\r\n            err['bytes'] = b;\r\n            throw err;\r\n        };\r\n        while ((a = src()) !== null) {\r\n            if ((a&0x80) === 0)\r\n                dst(a);\r\n            else if ((a&0xE0) === 0xC0)\r\n                ((b = src()) === null) && fail([a, b]),\r\n            dst(((a&0x1F)<<6) | (b&0x3F));\r\n            else if ((a&0xF0) === 0xE0)\r\n                ((b=src()) === null || (c=src()) === null) && fail([a, b, c]),\r\n            dst(((a&0x0F)<<12) | ((b&0x3F)<<6) | (c&0x3F));\r\n            else if ((a&0xF8) === 0xF0)\r\n                ((b=src()) === null || (c=src()) === null || (d=src()) === null) && fail([a, b, c ,d]),\r\n            dst(((a&0x07)<<18) | ((b&0x3F)<<12) | ((c&0x3F)<<6) | (d&0x3F));\r\n            else throw RangeError(\"Illegal starting byte: \"+a);\r\n        }\r\n    };\r\n\r\n    /**\r\n     * Converts UTF16 characters to UTF8 code points.\r\n     * @param {!function():number|null} src Characters source as a function returning the next char code respectively\r\n     *  `null` if there are no more characters left.\r\n     * @param {!function(number)} dst Code points destination as a function successively called with each converted code\r\n     *  point.\r\n     */\r\n    utfx.UTF16toUTF8 = function(src, dst) {\r\n        var c1, c2 = null;\r\n        while (true) {\r\n            if ((c1 = c2 !== null ? c2 : src()) === null)\r\n                break;\r\n            if (c1 >= 0xD800 && c1 <= 0xDFFF) {\r\n                if ((c2 = src()) !== null) {\r\n                    if (c2 >= 0xDC00 && c2 <= 0xDFFF) {\r\n                        dst((c1-0xD800)*0x400+c2-0xDC00+0x10000);\r\n                        c2 = null; continue;\r\n                    }\r\n                }\r\n            }\r\n            dst(c1);\r\n        }\r\n        if (c2 !== null) dst(c2);\r\n    };\r\n\r\n    /**\r\n     * Converts UTF8 code points to UTF16 characters.\r\n     * @param {(!function():number|null) | number} src Code points source, either as a function returning the next code point\r\n     *  respectively `null` if there are no more code points left or a single numeric code point.\r\n     * @param {!function(number)} dst Characters destination as a function successively called with each converted char code.\r\n     * @throws {RangeError} If a code point is out of range\r\n     */\r\n    utfx.UTF8toUTF16 = function(src, dst) {\r\n        var cp = null;\r\n        if (typeof src === 'number')\r\n            cp = src, src = function() { return null; };\r\n        while (cp !== null || (cp = src()) !== null) {\r\n            if (cp <= 0xFFFF)\r\n                dst(cp);\r\n            else\r\n                cp -= 0x10000,\r\n            dst((cp>>10)+0xD800),\r\n            dst((cp%0x400)+0xDC00);\r\n            cp = null;\r\n        }\r\n    };\r\n\r\n    /**\r\n     * Converts and encodes UTF16 characters to UTF8 bytes.\r\n     * @param {!function():number|null} src Characters source as a function returning the next char code respectively `null`\r\n     *  if there are no more characters left.\r\n     * @param {!function(number)} dst Bytes destination as a function successively called with the next byte.\r\n     */\r\n    utfx.encodeUTF16toUTF8 = function(src, dst) {\r\n        utfx.UTF16toUTF8(src, function(cp) {\r\n            utfx.encodeUTF8(cp, dst);\r\n        });\r\n    };\r\n\r\n    /**\r\n     * Decodes and converts UTF8 bytes to UTF16 characters.\r\n     * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if there\r\n     *  are no more bytes left.\r\n     * @param {!function(number)} dst Characters destination as a function successively called with each converted char code.\r\n     * @throws {RangeError} If a starting byte is invalid in UTF8\r\n     * @throws {Error} If the last sequence is truncated. Has an array property `bytes` holding the remaining bytes.\r\n     */\r\n    utfx.decodeUTF8toUTF16 = function(src, dst) {\r\n        utfx.decodeUTF8(src, function(cp) {\r\n            utfx.UTF8toUTF16(cp, dst);\r\n        });\r\n    };\r\n\r\n    /**\r\n     * Calculates the byte length of an UTF8 code point.\r\n     * @param {number} cp UTF8 code point\r\n     * @returns {number} Byte length\r\n     */\r\n    utfx.calculateCodePoint = function(cp) {\r\n        return (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4;\r\n    };\r\n\r\n    /**\r\n     * Calculates the number of UTF8 bytes required to store UTF8 code points.\r\n     * @param {(!function():number|null)} src Code points source as a function returning the next code point respectively\r\n     *  `null` if there are no more code points left.\r\n     * @returns {number} The number of UTF8 bytes required\r\n     */\r\n    utfx.calculateUTF8 = function(src) {\r\n        var cp, l=0;\r\n        while ((cp = src()) !== null)\r\n            l += utfx.calculateCodePoint(cp);\r\n        return l;\r\n    };\r\n\r\n    /**\r\n     * Calculates the number of UTF8 code points respectively UTF8 bytes required to store UTF16 char codes.\r\n     * @param {(!function():number|null)} src Characters source as a function returning the next char code respectively\r\n     *  `null` if there are no more characters left.\r\n     * @returns {!Array.<number>} The number of UTF8 code points at index 0 and the number of UTF8 bytes required at index 1.\r\n     */\r\n    utfx.calculateUTF16asUTF8 = function(src) {\r\n        var n=0, l=0;\r\n        utfx.UTF16toUTF8(src, function(cp) {\r\n            ++n; l += utfx.calculateCodePoint(cp);\r\n        });\r\n        return [n,l];\r\n    };\r\n\r\n    return utfx;\r\n}();\r\n\r\nDate.now = Date.now || function() { return +new Date; };\r\n\r\n/**\r\n * @type {number}\r\n * @const\r\n * @inner\r\n */\r\nvar BCRYPT_SALT_LEN = 16;\r\n\r\n/**\r\n * @type {number}\r\n * @const\r\n * @inner\r\n */\r\nvar GENSALT_DEFAULT_LOG2_ROUNDS = 10;\r\n\r\n/**\r\n * @type {number}\r\n * @const\r\n * @inner\r\n */\r\nvar BLOWFISH_NUM_ROUNDS = 16;\r\n\r\n/**\r\n * @type {number}\r\n * @const\r\n * @inner\r\n */\r\nvar MAX_EXECUTION_TIME = 100;\r\n\r\n/**\r\n * @type {Array.<number>}\r\n * @const\r\n * @inner\r\n */\r\nvar P_ORIG = [\r\n    0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822,\r\n    0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377,\r\n    0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5,\r\n    0xb5470917, 0x9216d5d9, 0x8979fb1b\r\n];\r\n\r\n/**\r\n * @type {Array.<number>}\r\n * @const\r\n * @inner\r\n */\r\nvar S_ORIG = [\r\n    0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed,\r\n    0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7,\r\n    0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3,\r\n    0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,\r\n    0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023,\r\n    0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,\r\n    0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda,\r\n    0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,\r\n    0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af,\r\n    0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6,\r\n    0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381,\r\n    0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,\r\n    0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d,\r\n    0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5,\r\n    0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a,\r\n    0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,\r\n    0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c,\r\n    0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,\r\n    0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3,\r\n    0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,\r\n    0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724,\r\n    0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b,\r\n    0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd,\r\n    0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,\r\n    0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f,\r\n    0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd,\r\n    0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39,\r\n    0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,\r\n    0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df,\r\n    0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,\r\n    0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e,\r\n    0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,\r\n    0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98,\r\n    0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565,\r\n    0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341,\r\n    0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,\r\n    0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0,\r\n    0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64,\r\n    0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191,\r\n    0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,\r\n    0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0,\r\n    0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,\r\n    0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5,\r\n    0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,\r\n    0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b,\r\n    0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f,\r\n    0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968,\r\n    0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,\r\n    0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5,\r\n    0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6,\r\n    0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799,\r\n    0x6e85076a, 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,\r\n    0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71,\r\n    0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29,\r\n    0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6,\r\n    0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,\r\n    0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f,\r\n    0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286,\r\n    0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec,\r\n    0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,\r\n    0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9,\r\n    0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,\r\n    0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e,\r\n    0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,\r\n    0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290,\r\n    0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810,\r\n    0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6,\r\n    0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,\r\n    0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847,\r\n    0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451,\r\n    0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6,\r\n    0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,\r\n    0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570,\r\n    0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,\r\n    0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978,\r\n    0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,\r\n    0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708,\r\n    0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883,\r\n    0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185,\r\n    0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,\r\n    0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830,\r\n    0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239,\r\n    0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab,\r\n    0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,\r\n    0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19,\r\n    0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,\r\n    0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1,\r\n    0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,\r\n    0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef,\r\n    0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3,\r\n    0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15,\r\n    0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,\r\n    0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2,\r\n    0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492,\r\n    0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, 0x1462b174,\r\n    0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,\r\n    0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759,\r\n    0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,\r\n    0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc,\r\n    0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,\r\n    0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465,\r\n    0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a,\r\n    0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c,\r\n    0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,\r\n    0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e,\r\n    0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,\r\n    0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0,\r\n    0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,\r\n    0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462,\r\n    0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c,\r\n    0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399,\r\n    0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,\r\n    0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74,\r\n    0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397,\r\n    0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7,\r\n    0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,\r\n    0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802,\r\n    0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,\r\n    0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4,\r\n    0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,\r\n    0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2,\r\n    0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1,\r\n    0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c,\r\n    0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,\r\n    0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341,\r\n    0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8,\r\n    0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b,\r\n    0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,\r\n    0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88,\r\n    0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,\r\n    0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc,\r\n    0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,\r\n    0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659,\r\n    0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f,\r\n    0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8,\r\n    0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,\r\n    0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be,\r\n    0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2,\r\n    0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255,\r\n    0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,\r\n    0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1,\r\n    0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,\r\n    0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025,\r\n    0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,\r\n    0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01,\r\n    0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641,\r\n    0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa,\r\n    0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,\r\n    0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409,\r\n    0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9,\r\n    0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3,\r\n    0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,\r\n    0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234,\r\n    0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf,\r\n    0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740,\r\n    0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,\r\n    0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f,\r\n    0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d,\r\n    0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8,\r\n    0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,\r\n    0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba,\r\n    0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,\r\n    0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69,\r\n    0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,\r\n    0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a,\r\n    0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b,\r\n    0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd,\r\n    0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,\r\n    0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4,\r\n    0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2,\r\n    0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb,\r\n    0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,\r\n    0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751,\r\n    0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,\r\n    0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369,\r\n    0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,\r\n    0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd,\r\n    0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45,\r\n    0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae,\r\n    0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,\r\n    0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08,\r\n    0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d,\r\n    0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b,\r\n    0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,\r\n    0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e,\r\n    0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,\r\n    0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c,\r\n    0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,\r\n    0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361,\r\n    0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c,\r\n    0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be,\r\n    0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,\r\n    0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d,\r\n    0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891,\r\n    0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5,\r\n    0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,\r\n    0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292,\r\n    0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,\r\n    0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2,\r\n    0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,\r\n    0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c,\r\n    0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8,\r\n    0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4,\r\n    0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,\r\n    0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6\r\n];\r\n\r\n/**\r\n * @type {Array.<number>}\r\n * @const\r\n * @inner\r\n */\r\nvar C_ORIG = [\r\n    0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944,\r\n    0x6f756274\r\n];\r\n\r\n/**\r\n * @param {Array.<number>} lr\r\n * @param {number} off\r\n * @param {Array.<number>} P\r\n * @param {Array.<number>} S\r\n * @returns {Array.<number>}\r\n * @inner\r\n */\r\nfunction _encipher(lr, off, P, S) { // This is our bottleneck: 1714/1905 ticks / 90% - see profile.txt\r\n    var n,\r\n        l = lr[off],\r\n        r = lr[off + 1];\r\n\r\n    l ^= P[0];\r\n    for (var i=0, k=BLOWFISH_NUM_ROUNDS-2; i<=k;)\r\n        // Feistel substitution on left word\r\n        n  = S[(l >> 24) & 0xff],\r\n    n += S[0x100 | ((l >> 16) & 0xff)],\r\n    n ^= S[0x200 | ((l >> 8) & 0xff)],\r\n    n += S[0x300 | (l & 0xff)],\r\n    r ^= n ^ P[++i],\r\n    // Feistel substitution on right word\r\n    n  = S[(r >> 24) & 0xff],\r\n    n += S[0x100 | ((r >> 16) & 0xff)],\r\n    n ^= S[0x200 | ((r >> 8) & 0xff)],\r\n    n += S[0x300 | (r & 0xff)],\r\n    l ^= n ^ P[++i];\r\n    lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1];\r\n    lr[off + 1] = l;\r\n    return lr;\r\n}\r\n\r\n/**\r\n * @param {Array.<number>} data\r\n * @param {number} offp\r\n * @returns {{key: number, offp: number}}\r\n * @inner\r\n */\r\nfunction _streamtoword(data, offp) {\r\n    for (var i = 0, word = 0; i < 4; ++i)\r\n        word = (word << 8) | (data[offp] & 0xff),\r\n    offp = (offp + 1) % data.length;\r\n    return { key: word, offp: offp };\r\n}\r\n\r\n/**\r\n * @param {Array.<number>} key\r\n * @param {Array.<number>} P\r\n * @param {Array.<number>} S\r\n * @inner\r\n */\r\nfunction _key(key, P, S) {\r\n    var offset = 0,\r\n        lr = [0, 0],\r\n        plen = P.length,\r\n        slen = S.length,\r\n        sw;\r\n    for (var i = 0; i < plen; i++)\r\n        sw = _streamtoword(key, offset),\r\n    offset = sw.offp,\r\n    P[i] = P[i] ^ sw.key;\r\n    for (i = 0; i < plen; i += 2)\r\n        lr = _encipher(lr, 0, P, S),\r\n    P[i] = lr[0],\r\n    P[i + 1] = lr[1];\r\n    for (i = 0; i < slen; i += 2)\r\n        lr = _encipher(lr, 0, P, S),\r\n    S[i] = lr[0],\r\n    S[i + 1] = lr[1];\r\n}\r\n\r\n/**\r\n * Expensive key schedule Blowfish.\r\n * @param {Array.<number>} data\r\n * @param {Array.<number>} key\r\n * @param {Array.<number>} P\r\n * @param {Array.<number>} S\r\n * @inner\r\n */\r\nfunction _ekskey(data, key, P, S) {\r\n    var offp = 0,\r\n        lr = [0, 0],\r\n        plen = P.length,\r\n        slen = S.length,\r\n        sw;\r\n    for (var i = 0; i < plen; i++)\r\n        sw = _streamtoword(key, offp),\r\n    offp = sw.offp,\r\n    P[i] = P[i] ^ sw.key;\r\n    offp = 0;\r\n    for (i = 0; i < plen; i += 2)\r\n        sw = _streamtoword(data, offp),\r\n    offp = sw.offp,\r\n    lr[0] ^= sw.key,\r\n    sw = _streamtoword(data, offp),\r\n    offp = sw.offp,\r\n    lr[1] ^= sw.key,\r\n    lr = _encipher(lr, 0, P, S),\r\n    P[i] = lr[0],\r\n    P[i + 1] = lr[1];\r\n    for (i = 0; i < slen; i += 2)\r\n        sw = _streamtoword(data, offp),\r\n    offp = sw.offp,\r\n    lr[0] ^= sw.key,\r\n    sw = _streamtoword(data, offp),\r\n    offp = sw.offp,\r\n    lr[1] ^= sw.key,\r\n    lr = _encipher(lr, 0, P, S),\r\n    S[i] = lr[0],\r\n    S[i + 1] = lr[1];\r\n}\r\n\r\n/**\r\n * Internaly crypts a string.\r\n * @param {Array.<number>} b Bytes to crypt\r\n * @param {Array.<number>} salt Salt bytes to use\r\n * @param {number} rounds Number of rounds\r\n * @param {function(Error, Array.<number>=)=} callback Callback receiving the error, if any, and the resulting bytes. If\r\n *  omitted, the operation will be performed synchronously.\r\n *  @param {function(number)=} progressCallback Callback called with the current progress\r\n * @returns {!Array.<number>|undefined} Resulting bytes if callback has been omitted, otherwise `undefined`\r\n * @inner\r\n */\r\nfunction _crypt(b, salt, rounds, callback, progressCallback) {\r\n    var cdata = C_ORIG.slice(),\r\n        clen = cdata.length,\r\n        err;\r\n\r\n    // Validate\r\n    if (rounds < 4 || rounds > 31) {\r\n        err = Error(\"Illegal number of rounds (4-31): \"+rounds);\r\n        if (callback) {\r\n            nextTick(callback.bind(this, err));\r\n            return;\r\n        } else\r\n            throw err;\r\n    }\r\n    if (salt.length !== BCRYPT_SALT_LEN) {\r\n        err =Error(\"Illegal salt length: \"+salt.length+\" != \"+BCRYPT_SALT_LEN);\r\n        if (callback) {\r\n            nextTick(callback.bind(this, err));\r\n            return;\r\n        } else\r\n            throw err;\r\n    }\r\n    rounds = 1 << rounds;\r\n    var P = P_ORIG.slice(),\r\n        S = S_ORIG.slice(),\r\n        i = 0, j;\r\n    _ekskey(salt, b, P, S);\r\n\r\n    /**\r\n     * Calcualtes the next round.\r\n     * @returns {Array.<number>|undefined} Resulting array if callback has been omitted, otherwise `undefined`\r\n     * @inner\r\n     */\r\n    function next() {\r\n        if (progressCallback)\r\n            progressCallback(i/rounds);\r\n        if (i < rounds) {\r\n            var start = Date.now();\r\n            for (; i < rounds;) {\r\n                i = i + 1;\r\n                _key(b, P, S);\r\n                _key(salt, P, S);\r\n                if (Date.now() - start > MAX_EXECUTION_TIME)\r\n                    break;\r\n            }\r\n        } else {\r\n            for (i = 0; i < 64; i++)\r\n                for (j = 0; j < (clen >> 1); j++)\r\n                    _encipher(cdata, j << 1, P, S);\r\n            var ret = [];\r\n            for (i = 0; i < clen; i++)\r\n                ret.push(((cdata[i] >> 24) & 0xff) >>> 0),\r\n            ret.push(((cdata[i] >> 16) & 0xff) >>> 0),\r\n            ret.push(((cdata[i] >> 8) & 0xff) >>> 0),\r\n            ret.push((cdata[i] & 0xff) >>> 0);\r\n            if (callback) {\r\n                callback(null, ret);\r\n                return;\r\n            } else\r\n                return ret;\r\n        }\r\n        if (callback)\r\n            nextTick(next);\r\n    }\r\n\r\n    // Async\r\n    if (typeof callback !== 'undefined') {\r\n        next();\r\n\r\n        // Sync\r\n    } else {\r\n        var res;\r\n        while (true)\r\n            if (typeof(res = next()) !== 'undefined')\r\n                return res || [];\r\n    }\r\n}\r\n\r\n/**\r\n * Internally hashes a string.\r\n * @param {string} s String to hash\r\n * @param {?string} salt Salt to use, actually never null\r\n * @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting hash. If omitted,\r\n *  hashing is perormed synchronously.\r\n *  @param {function(number)=} progressCallback Callback called with the current progress\r\n * @returns {string|undefined} Resulting hash if callback has been omitted, otherwise `undefined`\r\n * @inner\r\n */\r\nfunction _hash(s, salt, callback, progressCallback) {\r\n    var err;\r\n    if (typeof s !== 'string' || typeof salt !== 'string') {\r\n        err = Error(\"Invalid string / salt: Not a string\");\r\n        if (callback) {\r\n            nextTick(callback.bind(this, err));\r\n            return;\r\n        }\r\n        else\r\n            throw err;\r\n    }\r\n\r\n    // Validate the salt\r\n    var minor, offset;\r\n    if (salt.charAt(0) !== '$' || salt.charAt(1) !== '2') {\r\n        err = Error(\"Invalid salt version: \"+salt.substring(0,2));\r\n        if (callback) {\r\n            nextTick(callback.bind(this, err));\r\n            return;\r\n        }\r\n        else\r\n            throw err;\r\n    }\r\n    if (salt.charAt(2) === '$')\r\n        minor = String.fromCharCode(0),\r\n    offset = 3;\r\n    else {\r\n        minor = salt.charAt(2);\r\n        if ((minor !== 'a' && minor !== 'y') || salt.charAt(3) !== '$') {\r\n            err = Error(\"Invalid salt revision: \"+salt.substring(2,4));\r\n            if (callback) {\r\n                nextTick(callback.bind(this, err));\r\n                return;\r\n            } else\r\n                throw err;\r\n        }\r\n        offset = 4;\r\n    }\r\n\r\n    // Extract number of rounds\r\n    if (salt.charAt(offset + 2) > '$') {\r\n        err = Error(\"Missing salt rounds\");\r\n        if (callback) {\r\n            nextTick(callback.bind(this, err));\r\n            return;\r\n        } else\r\n            throw err;\r\n    }\r\n    var r1 = parseInt(salt.substring(offset, offset + 1), 10) * 10,\r\n        r2 = parseInt(salt.substring(offset + 1, offset + 2), 10),\r\n        rounds = r1 + r2,\r\n        real_salt = salt.substring(offset + 3, offset + 25);\r\n    s += minor >= 'a' ? \"\\x00\" : \"\";\r\n\r\n    var passwordb = stringToBytes(s),\r\n        saltb = base64_decode(real_salt, BCRYPT_SALT_LEN);\r\n\r\n    /**\r\n     * Finishes hashing.\r\n     * @param {Array.<number>} bytes Byte array\r\n     * @returns {string}\r\n     * @inner\r\n     */\r\n    function finish(bytes) {\r\n        var res = [];\r\n        res.push(\"$2\");\r\n        if (minor >= 'a')\r\n            res.push(minor);\r\n        res.push(\"$\");\r\n        if (rounds < 10)\r\n            res.push(\"0\");\r\n        res.push(rounds.toString());\r\n        res.push(\"$\");\r\n        res.push(base64_encode(saltb, saltb.length));\r\n        res.push(base64_encode(bytes, C_ORIG.length * 4 - 1));\r\n        return res.join('');\r\n    }\r\n\r\n    // Sync\r\n    if (typeof callback == 'undefined')\r\n        return finish(_crypt(passwordb, saltb, rounds));\r\n\r\n    // Async\r\n    else {\r\n        _crypt(passwordb, saltb, rounds, function(err, bytes) {\r\n            if (err)\r\n                callback(err, null);\r\n            else\r\n                callback(null, finish(bytes));\r\n        }, progressCallback);\r\n    }\r\n}\r\n\r\n/**\r\n * Encodes a byte array to base64 with up to len bytes of input, using the custom bcrypt alphabet.\r\n * @function\r\n * @param {!Array.<number>} b Byte array\r\n * @param {number} len Maximum input length\r\n * @returns {string}\r\n * @expose\r\n */\r\nbcrypt.encodeBase64 = base64_encode;\r\n\r\n/**\r\n * Decodes a base64 encoded string to up to len bytes of output, using the custom bcrypt alphabet.\r\n * @function\r\n * @param {string} s String to decode\r\n * @param {number} len Maximum output length\r\n * @returns {!Array.<number>}\r\n * @expose\r\n */\r\nbcrypt.decodeBase64 = base64_decode;\r\n\r\nexport default bcrypt;\r\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/.editorconfig",
    "content": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/.gitattributes",
    "content": "*.txt   text eol=lf\n*.js    text eol=lf\n*.html  text eol=lf\n*.md    text eol=lf\n*.json  text eol=lf\n*.yml   text eol=lf\n*.css   text eol=lf\n*.svg   text eol=lf\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/.npmignore",
    "content": "/node_modules\n/demo\n/doc\n/test\n/test*.html\n/index.html\n/mode/*/*test.js\n/mode/*/*.html\n/mode/index.html\n.*\n/bin/authors.sh\n/bin/lint\n/bin/release\n/bin/upload-release.js\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/AUTHORS",
    "content": "List of CodeMirror contributors. Updated before every release.\n\n4oo4\n4r2r\nAaron Brooks\nAbdelouahab\nAbdussalam Abdurrahman\nAbe Fettig\nAbhishek Gahlot\nAdam Ahmed\nAdam King\nAdam Particka\nAdam Wight\nadanlobato\nAdán Lobato\nAditya Toshniwal\nAdrian Aichner\nAdrian Heine\nAdrian Kunz\nAdrien Bertrand\naeroson\nAhmad Amireh\nAhmad M. Zawawi\nAHOHNMYC\nahoward\nAjin Abraham\nAkeksandr Motsjonov\nAlasdair Smith\nAlbertHilb\nAlberto González Palomo\nAlberto Pose\nAlbert Xing\nAlexander Marks\nAlexander Pavlov\nAlexander Schepanovski\nAlexander Shvets\nAlexander Solovyov\nAlexandre Bique\nAlex Churchill\nalexey-k\nAlex Piggott\nAlf Eaton\nAliaksei Chapyzhenka\nAllen Sarkisyan\nAmi Fischman\nAmin Shali\nAmin Ullah Khan\namshali@google.com\nAmsul\namuntean\nAmy\nAnanya Sen\nanaran\nAndersMad\nAnders Nawroth\nAnderson Mesquita\nAnders Wåglund\nAndrea G\nAndreas Reischuck\nAndres Taylor\nAndre von Houck\nAndrew Cheng\nAndrew Dassonville\nAndrey Fedorov\nAndrey Klyuchnikov\nAndrey Lushnikov\nAndrey Shchekin\nAndy Joslin\nAndy Kimball\nAndy Li\nAngelo\nangelozerr\nangelo.zerr@gmail.com\nAnkit\nAnkit Ahuja\nAnsel Santosa\nAnthony Dugois\nanthonygego\nAnthony Gégo\nAnthony Grimes\nAnthony Stewart\nAnton Kovalyov\nantosarho\naoki ken\nApollo Zhu\nAQNOUCH Mohammed\nAram Shatakhtsyan\nareos\nArnab Bose\nArnoud Buzing\nArsène von Wyss\nArthur Müller\nArun Narasani\nas3boyan\nasolove\natelierbram\nAtomicPages LLC\nAtul Bhouraskar\nAurelian Oancea\nAxel Lewenhaupt\nBaptiste Augrain\nBarret Rennie\nBartosz Dziewoński\nBasarat Ali Syed\nBastian Müller\nbelhaj\nBem Jones-Bey\nbenbro\nBenedikt Meurer\nbenhormann\nBen Hormann\nBeni Cherniavsky-Paskin\nBenjamin DeCoste\nbenjaminr-ps\nBenjamin Young\nBen Keen\nBen Miller\nBen Mosher\nBernhard Sirlinger\nBert Chang\nBharad\nBigBlueHat\nBilliam\nBilly Moon\nBin Ni\nbinny\nBjarki Ágúst Guðmundsson\nBjorn Hansen\nB Krishna Chaitanya\nBlaine G\nblukat29\nBo\nboomyjee\nBo Peng\nborawjm\nBoris K\nBoris Verkhovskiy\nBrad Metcalf\nBrandon Frohs\nBrandon Wamboldt\nBret Little\nBrett Morgan\nBrett Zamir\nBrian Grinstead\nBrianHung\nBrian Sletten\nbrrd\nBruce Mitchener\nBruno Logerfo\nBryan Gin-ge Chen\nBryan Massoth\nCaitlin Potter\nCalin Barbat\ncallodacity\nCamilo Roca\nCasey Klebba\ncBiscuit87\nCésar González Íñiguez\nChad Jolly\nChandra Sekhar Pydi\nCharles Skelton\nCheah Chu Yeow\nChhekur\nChris Colborne\nChris Coyier\nChris Ford\nChris Granger\nChris Houseknecht\nChris Lohfink\nChris Morgan\nChris Reeves\nChris Smith\nChristian Gruen\nChristian Oyarzun\nChristian Petrov\nChristian Sonne\nchristopherblaser\nChristopher Brown\nChristopher Kramer\nChristopher Mitchell\nChristopher Pfohl\nChristopher Wallis\nChunliang Lyu\nciaranj\nclone-it\nclso\nCodeAnimal\nCodeBitt\ncoderaiser\nCole R Lawrence\nComFreek\nCornelius Weig\nCristian Prieto\nCurran Kelleher\nCurtis Gagliardi\nd8888\ndagsta\ndaines\nDale Jung\nDan Bentley\nDan Heberden\nDaniel, Dao Quang Minh\nDaniele Di Sarli\nDaniel Faust\nDaniel Hanggi\nDaniel Huigens\nDaniel Kesler\nDaniel KJ\nDaniel Neel\nDaniel Parnell\nDaniel Thwaites\nDanila Malyutin\nDanny Yoo\ndarealshinji\nDarius Roberts\ndatabricks-david-lewis\nDave Brondsema\nDave MacLachlan\nDave Myers\nDavid Barnett\nDavid H. Bronke\nDavid Mignot\nDavid Pathakjee\nDavid R. Myers\nDavid Rodrigues\nDavid Santana\nDavid Vázquez\nDavid Whittington\ndeebugger\nDeep Thought\nDenis Ovsienko\nDevin Abbott\nDevon Carew\nDick Choi\nDiego Fernandez\ndignifiedquire\nDimage Sapelkin\nDimitri Mitropoulos\nDinindu D. Wanniarachchi\ndmaclach\nDmitry Kiselyov\nDoctorKrolic\ndomagoj412\nDominator008\nDomizio Demichelis\nDoug Blank\nDoug Wikle\nDrew Bratcher\nDrew Hintz\nDrew Khoury\nDrini Cami\nDror BG\nDuncan Lilley\nduralog\ndwelle\nEalton\neborden\nedoroshenko\nedsharp\nekhaled\nElisée\nElmar Peise\nelpnt\nEmmanuel Schanzer\nEnam Mijbah Noor\nEric Allam\nEric Bogard\nErik Demaine\nErik Krogh Kristensen\nErik Welander\nerosman\neustas\nEvan Minsk\nFabien Dubosson\nFabien O'Carroll\nFabio Zendhi Nagao\nFaiza Alsaied\nFaris Masad\nFauntleroy\nfbuchinger\nfeizhang365\nFelipe Lalanne\nFelipe S. S. Schneider\nFelix Raab\nficristo\nFilip Noetzel\nFilip Stollár\nFilype Pereira\nfinalfantasia\nflack\nFlorian Felten\nFons van der Plas\nForbes Lindesay\nForbesLindesay\nFord_Lawnmower\nForrest Oliphant\nFranco Catena\nFrank Seifferth\nFrank Wiegand\nfraxx001\nFredrik Borg\nFUJI Goro (gfx)\nfzipp\nGabriela Gutierrez\nGabriel Gheorghian\nGabriel Horner\nGabriel Nahmias\ngalambalazs\nGary Sheng\nGautam Mehta\nGavin Douglas\nGeist-zz\ngekkoe\nGeordie Hall\nGeorge Stephanis\ngeowarin\nGerard Braad\nGergely Hegykozi\nGermain Chazot\nGiovanni Calò\nGlebov Boris\nGlenn Jorde\nGlenn Ruehle\ngoldsmcb\nGolevka\nGoogle LLC\nGordon Smith\nGrant Skinner\ngreengiant\nGregory Koberger\nGrzegorz Mazur\nGuang Li\nGuan Gui\nGuillaume Massé\nGuillaume Massé\nguraga\nGustavo Rodrigues\nHakan Tunc\nHanno Fellmann\nHans Engel\nHanzhao Deng\nHaoran Yu\nHarald Schilly\nHardest\nHarshvardhan Gupta\nHarutyun Amirjanyan\nHasan Delibaş\nHasan Karahan\nHeanes\nHector Oswaldo Caballero\nHein Htat\nHélio\nHendrik Erz\nHendrik Wallbaum\nHenrik Haugbølle\nHerculano Campos\nhidaiy\nHiroyuki Makino\nhitsthings\nHocdoc\nHoward\nHoward Jing\nHugues Malphettes\nIan Beck\nIan Davies\nIan Dickinson\nIan Henderson\nianhi\nIan Rose\nIan Wehrman\nIan Wetherbee\nIce White\nICHIKAWA, Yuji\nidleberg\nIgor Petruk\nilvalle\nIlya Kharlamov\nIlya Zverev\nIngo Richter\nIntervue\nIrakli Gozalishvili\niteriani\nIvan Kurnosov\nIvoah\nJack Douglas\nJacob Lee\nJaimin\nJake Peyser\nJake Zimmerman\nJakob Kummerow\nJakob Miland\nJakub Nowak\nJakub T. Jankiewicz\nJakub Vrana\nJakub Vrána\nJames Baicoianu\nJames Campos\nJames Cockshull\nJames Howard\nJames Thorne\nJamie Hill\nJamie Morris\nJanice Leung\nJan Jongboom\njankeromnes\nJan Keromnes\nJan Odvarko\nJan Schär\nJan T. Sott\nJared Dean\nJared Forsyth\nJared Jacobs\nJason\nJason Barnabe\nJason Grout\nJason Heeris\nJason Johnston\nJason San Jose\nJason Siefken\nJayaprabhakar\nJay Contonio\nJaydeep Solanki\nJean Boussier\nJeff Blaisdell\nJeff Hanke\nJeff Jenkins\njeffkenton\nJeff Pickhardt\njem (graphite)\nJeremy Parmenter\nJim\nJim Avery\njkaplon\nJobJob\njochenberger\nJochen Berger\nJoel Einbinder\njoelpinheiro\nJoe Predham\njoewalsh\nJohan Ask\nJohannes\nJohn Chen\nJohn Connor\nJohn-David Dalton\nJohn Engler\nJohn Lees-Miller\nJohn Ryan\nJohn Snelson\njohnspiegel\nJohn Van Der Loo\nJon Ander Peñalba\nJonas Döbertin\nJonas Helfer\nJonathan Dierksen\nJonathan Hart\nJonathan Malmaud\nJonathan Rascher\nJon Gacnik\njongalloway\nJon Malmaud\nJon Sangster\nJoo\nJoost-Wim Boekesteijn\nJosé dBruxelles\nJoseph D. Purcell\nJoseph Pecoraro\nJosh Barnes\nJosh Cohen\nJosh Soref\nJoshua Newman\nJosh Watzman\njots\nJoy Zhong\njsoojeon\nju1ius\nJuan Benavides Romero\nJucovschi Constantin\nJuho Vuori\nJulien CROUZET\nJulien Rebetez\nJustin Andresen\nJustin Hileman\njwallers@gmail.com\nkaniga\nkarevn\nKarol\nKaushik Kulkarni\nKayur Patel\nkazk\nKazuhisa Ishizaka\nKazuhito Hokamura\nkcwiakala\nKees de Kooter\nKeldan Chapman\nKenan Christian Dimas\nKen Newman\nken restivo\nKen Rockot\nKevin Earls\nKevin Kwok\nKevin Muret\nKevin Sawicki\nKevin Ushey\nKier Darby\nKim-Anh Tran\nKlaus Silveira\nKoh Zi Han, Cliff\nkomakino\nkometenstaub\nKonrad Zapotoczny\nKonstantin Chernenko\nKonstantin Lopuhin\nkoops\nKris Ciccarello\nks-ifware\nkubelsmieci\nkvncp\nKwanEsq\nKyle Kelley\nKyleMcNutt\nLaKing\nLanfei\nLanny\nlaobubu\nLaszlo Vidacs\nleaf\nleaf corcoran\nLemmon\nLeo Baschy\nLeonid Khachaturov\nLeon Sorokin\nLeonya Khachaturov\nlexer2086\nLiam Newman\nLibo Cannici\nLior Goldberg\nLior Shub\nlishid\nLloydMilligan\nLM\nlochel\nLonnie Abelbeck\nLorenzo Simionato\nLorenzo Stoakes\nLouis Mauchet\nLuca Fabbri\nLucas Buchala\nLuciano Longo\nLuciano Santana\nLu Fangjian\nŁukasz Wielgus\nLuke Browning\nLuke Granger-Brown\nLuke Haas\nLuke Stagner\nlynschinzer\nM1cha\nMadhura Jayaratne\nMaksim Lin\nMaksym Taran\nMalay Majithia\nManideep\nManuel Rego Casasnovas\nMarat Dreizin\nMarcel Gerber\nMarcelo Camargo\nMarc Espín\nMarco Aurélio\nMarco Munizaga\nMarcus Bointon\nMarek Rudnicki\nMarijn Haverbeke\nMário Gonçalves\nMario Pietsch\nMark Anderson\nMark Boyes\nMark Dalgleish\nMark Hamstra\nMark Lentczner\nMarko Bonaci\nMark Peace\nMarkus Bordihn\nMarkus Olsson\nMartin Balek\nMartín Gaitán\nMartin Hasoň\nMartin Hunt\nMartin Laine\nMartin Zagora\nMasahiro MATAYOSHI\nMason Malone\nMateusz Paprocki\nMathias Bynens\nmats cronqvist\nMatt Diehl\nMatt Gaide\nMatthew Bauer\nMatthew Beale\nMatthew Casperson\nmatthewhayes\nMatthew Rathbone\nMatthew Suozzo\nMatthias Bussonnier\nMatthias BUSSONNIER\nMattia Astorino\nMatt MacPherson\nMatt McDonald\nMatt Pass\nMatt Sacks\nmauricio\nMaximilian Hils\nMaxim Kraev\nMax Kirsch\nMax Schaefer\nMax Wu\nMax Xiantu\nmbarkhau\nMcBrainy\nmce2\nMélanie Chauvel\nmelpon\nmeshuamam\nMetatheos\nMicah Dubinko\nMichael\nMichael Chirico\nMichael Goderbauer\nMichael Grey\nMichael Kaminsky\nMichael Lehenbauer\nMichael Wadman\nMichael Walker\nMichael Zhou\nMichal Čihař\nMichal Dorner\nMichal Kapiczynski\nMighty Guava\nMiguel Castillo\nmihailik\nMika Andrianarijaona\nMike\nMike Bostock\nMike Brevoort\nMike Diaz\nMike Ivanov\nMike Kadin\nMike Kobit\nMilan Szekely\nMinJune Kim\nMinRK\nMiraculix87\nmisfo\nmkaminsky11\nmloginov\nmlsad3\nMoritz Schubotz (physikerwelt)\nMoritz Schwörer\nMoshe Wajnberg\nmps\nms\nmtaran-google\nMu-An ✌️ Chiou\nMu-An Chiou\nMykola Martynovets\nmzabuawala\nNarciso Jaramillo\nnathanlesage\nNathan Williams\nndr\nNeil Anderson\nneon-dev\nnerbert\nNetworkNode\nnextrevision\nngn\nnguillaumin\nNg Zhi An\nNicholas Bollweg\nNicholas Bollweg (Nick)\nNickKolok\nNick Kreeger\nNick Small\nNicolas Chevobbe\nNicolas Kick\nNicolò Ribaudo\nNiels van Groningen\nnightwing\nNikita Beloglazov\nNikita Vasilyev\nNikolaj Kappler\nNikolay Kostov\nnilp0inter\nNils Knappmeier\nNina Pypchenko\nNisarg Jhaveri\nnlwillia\nnoragrossman\nNorman Rzepka\nNouzbe\nOleksandr Yakovenko\nOlivia Ytterbrink\nOndřej Mirtes\nOpender Singh\nopl-\nOreoluwa Onatemowo\norionlee\noscar.lofwenhamn\nOskar Segersvärd\nossdev\noverdodactyl\npablo\npabloferz\nPablo Zubieta\npaddya\nPage\npaladox\nPanupong Pasupat\nparis\nParis\nParis Kasidiaris\nParker Lougheed\nPatil Arpith\nPatrick Kettner\nPatrick Stoica\nPatrick Strawderman\nPaul Garvin\nPaul Ivanov\nPaul Masson\nPaul Schmidt\nPavel\nPavel Feldman\nPavel Petržela\nPavel Strashkin\nPaweł Bartkiewicz\npeteguhl\npeter\nPeter Flynn\npeterkroon\nPeter Kroon\nPeter László\nPhil DeJarnett\nPhilipp A\nPhilipp Markovics\nPhilip Stadermann\nPi Delport\nPierre Gerold\nPieter Ouwerkerk\nPiyush\nPontus Granström\nPontus Melke\nprasanthj\nPrasanth J\nPrayag Verma\nprendota\nPrendota\nps173\nQiang Li\nquiddity-wp\nRadek Piórkowski\nRahul\nRahul Anand\nramwin1\nRandall Mason\nRandy Burden\nRandy Edmunds\nRandy Luecke\nRaphael Amorim\nRasmus Erik Voel Jensen\nRasmus Schultz\nraymondf\nRaymond Hill\nray ratchup\nRay Ratchup\nRemi Nyborg\nRenaud Durlin\nReynold Xin\nRichard Denton\nRichard Fung\nRichard van der Meer\nRichard Z.H. Wang\nRishi Goomar\nRobert Brignull\nRobert Crossfield\nRobert Martin\nRoberto Abdelkader Martínez Pérez\nrobertop23\nRoberto Vidal\nRobert Plummer\nRoman Frolov\nRoman Janusz\nRongjian Zhang\nRrandom\nRrrandom\nRuslan Bekenev\nRuslan Osmanov\nrvalavicius\nRyan Pangrle\nRyan Petrello\nRyan Prior\nryu-sato\nsabaca\nsach.gupta\nSachin Gupta\nsahil.mahna\nSam Lee\nSam Rawlins\nSamuel Ainsworth\nSam Wilson\nsandeepshetty\nSander AKA Redsandro\nSander Verweij\nsantec\nSarah McAlear and Wenlin Zhang\nSascha Peilicke\nSasha Varlamov\nsatamas\nsatchmorun\nsathyamoorthi\nSaul Costa\nS. Chris Colbert\nSCLINIC\\jdecker\nScott Aikin\nScott Feeney\nScott Goodhew\nSeb35\nSebastian Ślepowroński\nSebastian Wilzbach\nSebastian Zaha\nSeren D\nSergey Goder\nSergey Tselovalnikov\nSe-Won Kim\nShane Liesegang\nshaund\nshaun gilchrist\nShawn A\nShea Bunge\nsheopory\nShil S\nShiv Deepak\nShmuel Englard\nShubham Jain\nSiamak Mokhtari\nSiddhartha Gunti\nsilverwind\nSimone Di Nuovo\nSimon Edwards\nSimon Huber\nsinkuu\nSlava Rozhnev\nsnasa\nsoliton4\nsonson\nSorab Bisht\nspastorelli\nsrajanpaliwal\nStanislav Oaserele\nstan-z\nStas Kobzar\nstasoid\nStefan Borsje\nSteffen Beyer\nSteffen Bruchmann\nSteffen Kowalski\nStephane Moore\nStephen Lavelle\nSteve Champagne\nSteve Hoover\nSteven Yung\nSteve O'Hara\nstockiNail\nstoskov\nStryder Crown\nStu Kennedy\nSungho Kim\nsverweij\nTaha Jahangir\ntakamori\nTako Schotanus\nTakuji Shimokawa\nTakuya Matsuyama\nTarmil\nT. Brandon Ashley\nTDaglis\nTeja\ntel\nTentone\ntfjgeorge\nThaddee Tyl\nthanasis\nTheHowl\nthemrmax\nThiemo Kreuz\nthink\nThomas Brouard\nThomas Dvornik\nThomas Kluyver\nthomasmaclean\nThomas Schmid\nTim Alby\nTim Baumann\nTim Down\nTim Gates\nTim Nguyen\nTimothy Farrell\nTimothy Gu\nTimothy Hatcher\nTim van der Lippe\nTobias Bertelsen\nTobiasBg\nTodd Berman\nTodd Kennedy\ntokafew420\nTomas-A\nTomas Varaneckas\nTom Erik Støwer\nTom Klancer\nTom MacWright\nTom McLaughlin\nTony Jian\ntophf\nTorben Bundt\nTorgeir Thoresen\ntotalamd\nTravis Heppe\nTriangle717\nTristan Tarrant\nTSUYUSATO Kitsune\nTugrul Elmas\ntwifkak\nTyler Long\nTyler Makaro\nVadim Dyachenko\nVadzim Ramanenka\nVaibhav Sagar\nvamshi.revu\nVapidWorx\nVestimir Markov\nvf\nVictor Bocharsky\nVincent Woo\nVladislav Voitenok\nVolker Mische\nvtripolitakis\nwdouglashall\nWeiyan Shao\nwenli\nWes Cossick\nWesley Wiser\nWeston Ruter\nWill Binns-Smith\nWill Cassella\nWill Dean\nWill Hernandez\nWilliam Desportes\nWilliam Jamieson\nWilliam Stein\nWilly\nWojtek Ptak\nwonderboyjon\nWu Cheng-Han\nXavier Mendez\nYakov Manshin\nYang Guo\nYash Singh\nYash-Singh1\nYassin N. Hassan\nYNH Webdev\nyoongu\nyoyoyodog123\nYunchi Luo\nYuvi Panda\nYvonnick Esnault\nZac Anger\nZachary Dremann\nZeeshanNoor\nZeno Rocha\nZhang Hao\nZiran Sun\nZiv\nzoobestik\nzziuni\n魏鹏刚\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/CHANGELOG.md",
    "content": "## 5.65.16 (2023-11-20)\n\n### Bug fixes\n\nFix focus tracking in shadow DOM.\n\n[go mode](https://codemirror.net/5/mode/go/): Allow underscores in numbers.\n\n[jsx mode](https://codemirror.net/5/mode/jsx/index.html): Support TS generics marked by trailing comma.\n\n## 5.65.15 (2023-08-29)\n\n### Bug fixes\n\n[lint addon](https://codemirror.net/5/doc/manual.html#addon_lint): Prevent tooltips from sticking out of the viewport.\n\n[yaml mode](https://codemirror.net/5/mode/yaml/): Fix an exponential-complexity regular expression.\n\n## 5.65.14 (2023-07-17)\n\n### Bug fixes\n\n[clike mode](https://codemirror.net/5/mode/clike/): Fix poor indentation in some Java code.\n\n[nsis mode](https://codemirror.net/5/mode/nsis/index.html): Recognize `!assert` command.\n\n[lint addon](https://codemirror.net/5/doc/manual.html#addon_lint): Remove broken annotation deduplication.\n\n## 5.65.13 (2023-04-27)\n\n### Bug fixes\n\n[dart mode](https://codemirror.net/5/mode/dart/index.html): Add some new keywords.\n\n[clike mode](https://codemirror.net/5/mode/clike/): Tokenize Scala character literals.\n\n## 5.65.12 (2023-02-20)\n\n### Bug fixes\n\n[python mode](https://codemirror.net/5/mode/python/): Add new built-ins and keywords.\n\n## 5.65.11 (2022-12-20)\n\n### Bug fixes\n\nAlso respect autocapitalize/autocorrect/spellcheck options in textarea mode.\n\n[sql-hint addon](https://codemirror.net/5/doc/manual.html#addon_sql-hint): Fix keyword completion in generic SQL mode.\n\n## 5.65.10 (2022-11-20)\n\n### Bug fixes\n\n[sql-hint addon](https://codemirror.net/5/doc/manual.html#addon_sql-hint): Fix completion when the SQL mode is wrapped by some outer mode.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/index.html): Fix parsing of property keywords before private property names.\n\n## 5.65.9 (2022-09-20)\n\n### Bug fixes\n\nAdd a workaround for a regression in Chrome 105 that could cause content below the editor to not receive mouse events.\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Resize the tooltip if it doesn't fit the screen.\n\n[swift mode](https://codemirror.net/5/mode/swift): Fix tokenizing of block comments.\n\n[jinja2 mode](https://codemirror.net/5/mode/jinja2/): Support line statements.\n\n## 5.65.8 (2022-08-20)\n\n### Bug fixes\n\nInclude direction override and isolate characters in the default set of special characters.\n\nFix an issue that could cause document corruption when mouse-selecting during composition.\n\n[foldgutter addon](https://codemirror.net/5/doc/manual.html#addon_foldgutter): Refresh markers when the editor's mode changes.\n\n[merge addon](https://codemirror.net/5/doc/manual.html#addon_merge): Fix syntax that prevented the addon from loading in IE10.\n\n## 5.65.7 (2022-07-20)\n\n### Bug fixes\n\nFix several references to the global `document`/`window`, improving support for creating an editor in another frame.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Use [upstream](https://github.com/replit/codemirror-vim/) code instead of keeping our own copy.\n\n[tern addon](https://codemirror.net/5/demo/tern.html): Properly HTML escape variable names in rename dialog.\n\n## 5.65.6 (2022-06-20)\n\n### Bug fixes\n\nAvoid firing `beforeCursorEnter` callbacks twice for cursor selections.\n\nImprove support for auto-hiding macOS scrollbars.\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Fix an issue where the tooltip could be placed to the left of the screen.\n\n[swift mode](https://codemirror.net/5/mode/swift): Support structured concurrency keywords.\n\n## 5.65.5 (2022-05-30)\n\n### Bug fixes\n\nWork around a bug in Chrome 102 that caused wheel scrolling of the editor to constantly stop.\n\n[search addon](https://codemirror.net/5/demo/search.html): Make sure the search field has an accessible label.\n\n[comment addon](https://codemirror.net/5/doc/manual.html#addon_comment): Preserve indentation on otherwise empty lines when indenting.\n\n## 5.65.4 (2022-05-20)\n\n### Bug fixes\n\nIgnore paste events when the editor doesn't have focus.\n\n[sparql mode](https://codemirror.net/5/mode/sparql/index.html): Fix parsing of variables after operators.\n\n[julia mode](https://codemirror.net/5/mode/julia/): Properly tokenize `!==` and `===` operators.\n\n## 5.65.3 (2022-04-20)\n\n### Bug fixes\n\nFix a crash that could occur when when marking text.\n\n[merge addon](https://codemirror.net/5/doc/manual.html#addon_merge): Add aria label to buttons.\n\n[groovy mode](https://codemirror.net/5/mode/groovy/index.html): Properly highlight interpolated variables.\n\n## 5.65.2 (2022-02-21)\n\n### Bug fixes\n\n[clike mode](https://codemirror.net/5/mode/clike/): Recognize triple quoted string in Java.\n\n[cypher mode](https://codemirror.net/5/mode/cypher/index.html): Fix handling of punctuation.\n\n## 5.65.1 (2022-01-20)\n\n### Bug fixes\n\nFix miscalculation of vertical positions in lines that have both line widgets and replaced newlines.\n\n## 5.65.0 (2021-12-20)\n\n### Bug fixes\n\nbrace-folding addon: Fix broken folding on lines with both braces and square brackets.\n\n### New features\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Support g0, g$, g<Arrow>.\n\n## 5.64.0 (2021-11-20)\n\n### Bug fixes\n\nFix a crash that occurred in some situations with replacing marks across line breaks.\n\nMake sure native scrollbars reset their position when hidden and re-shown.\n\n### New features\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Support C-u to delete back a line.\n\n## 5.63.3 (2021-10-11)\n\n### Bug fixes\n\nPrevent external styles from giving the hidden textarea a min-height.\n\nRemove a stray autosave file that was part of the previous release.\n\n## 5.63.1 (2021-09-29)\n\n### Bug fixes\n\nFix an issue with mouse scrolling on Chrome 94 Windows, which made scrolling by wheel move unusably slow.\n\n## 5.63.0 (2021-09-20)\n\n### Bug fixes\n\nFix scroll position jumping when scrolling a document with very different line heights.\n\n[xml mode](https://codemirror.net/5/mode/xml/): Look up HTML element behavior in a case-insensitive way.\n\n### New features\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Support guu for case-changing.\n\n## 5.62.3 (2021-08-20)\n\n### Bug fixes\n\nGive the editor a `translate=no` attribute to prevent automatic translation from modifying its content.\n\nGive vim-style cursors a width that matches the character after them.\n\n[merge addon](https://codemirror.net/5/doc/manual.html#addon_merge): Make buttons keyboard-accessible.\n\n[emacs bindings](https://codemirror.net/5/demo/emacs.html): Fix by-page scrolling keybindings, which were accidentally inverted.\n\n## 5.62.2 (2021-07-21)\n\n### Bug fixes\n\n[lint addon](https://codemirror.net/5/doc/manual.html#addon_lint): Fix a regression that broke several addon options.\n\n## 5.62.1 (2021-07-20)\n\n### Bug fixes\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Make matching of upper-case characters more Unicode-aware.\n\n[lint addon](https://codemirror.net/5/doc/manual.html#addon_lint): Prevent options passed to the addon itself from being given to the linter.\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Improve screen reader support.\n\n[search addon](https://codemirror.net/5/demo/search.html): Avoid using `innerHTML`.\n\n## 5.62.0 (2021-06-21)\n\n### Bug fixes\n\nImprove support for vim-style cursors in a number of themes.\n\n### New features\n\n[lint addon](https://codemirror.net/5/doc/manual.html#addon_lint): Add support for highlighting lines with errors or warnings.\n\n## 5.61.1 (2021-05-20)\n\n### Bug fixes\n\nFix a bug where changing the editor's document could confuse text-direction management.\n\nFix a bug in horizontally scrolling the cursor into view.\n\nOptimize adding lots of marks in a single transaction.\n\n[simple mode addon](https://codemirror.net/5/demo/simplemode.html): Support regexps with a unicode flag.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/index.html): Add support for TypeScript template string types, improve integration with JSX mode.\n\n## 5.61.0 (2021-04-20)\n\n### Bug fixes\n\nImprove support for being in a shadow DOM in contenteditable mode.\n\nPrevent line number from being read by screen readers.\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Fix a crash caused by a race condition.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Improve scope tracking.\n\n### New features\n\nThe library now emits an `\"updateGutter\"` event when the gutter width changes.\n\n[emacs bindings](https://codemirror.net/5/demo/emacs.html): Provide named commands for all bindings.\n\n## 5.60.0 (2021-03-20)\n\n### Bug fixes\n\nFix autofocus feature in contenteditable mode.\n\n[simple mode addon](https://codemirror.net/5/demo/simplemode.html): Fix a null-dereference crash.\n\n[multiplex addon](https://codemirror.net/5/demo/multiplex.html): Make it possible to use `parseDelimiters` when both delimiters are the same.\n\n[julia mode](https://codemirror.net/5/mode/julia/): Fix a lockup bug.\n\n### New features\n\n`setSelections` now allows ranges to omit the `head` property when it is equal to `anchor`.\n\n[sublime bindings](https://codemirror.net/5/demo/sublime.html): Add support for reverse line sorting.\n\n## 5.59.4 (2021-02-24)\n\n### Bug fixes\n\nGive the scrollbar corner filler a background again, to prevent content from peeping through between the scrollbars.\n\n## 5.59.3 (2021-02-20)\n\n### Bug fixes\n\nDon't override the way zero-with non-joiners are rendered.\n\nFix an issue where resetting the history cleared the `undoDepth` option's value.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Fix substitute command when joining and splitting lines, fix global command when line number change, add support for `:vglobal`, properly treat caps lock as a modifier key.\n\n## 5.59.2 (2021-01-20)\n\n### Bug fixes\n\nDon't try to scroll the selection into view in `readonly: \"nocursor\"` mode.\n\n[closebrackets addon](https://codemirror.net/5/doc/manual.html#addon_closebrackets): Fix a regression in the behavior of pressing enter between brackets.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Fix an infinite loop on specific syntax errors in object types.\n\nvarious modes: Fix inefficient RegExp matching.\n\n## 5.59.1 (2020-12-31)\n\n### Bug fixes\n\nFix an issue where some Chrome browsers were detected as iOS.\n\n## 5.59.0 (2020-12-20)\n\n### Bug fixes\n\nFix platform detection on recent iPadOS.\n\n[lint addon](https://codemirror.net/5/doc/manual.html#addon_lint): Don't show duplicate messages for a given line.\n\n[clojure mode](https://codemirror.net/5/mode/clojure/index.html): Fix regexp that matched in exponential time for some inputs.\n\n[hardwrap addon](https://codemirror.net/5/doc/manual.html#addon_hardwrap): Improve handling of words that are longer than the line length.\n\n[matchbrackets addon](https://codemirror.net/5/doc/manual.html#addon_matchbrackets): Fix leaked event handler on disabling the addon.\n\n### New features\n\n[search addon](https://codemirror.net/5/demo/search.html): Make it possible to configure the search addon to show the dialog at the bottom of the editor.\n\n## 5.58.3 (2020-11-19)\n\n### Bug fixes\n\nSuppress quick-firing of blur-focus events when dragging and clicking on Internet Explorer.\n\nFix the `insertAt` option to `addLineWidget` to actually allow the widget to be placed after all widgets for the line.\n\n[soy mode](https://codemirror.net/5/mode/soy/): Support `@Attribute` and element composition.\n\n[shell mode](https://codemirror.net/5/mode/shell/): Support heredoc quoting.\n\n## 5.58.2 (2020-10-23)\n\n### Bug fixes\n\nFix a bug where horizontally scrolling the cursor into view sometimes failed with a non-fixed gutter.\n\n[julia mode](https://codemirror.net/5/mode/julia/): Fix an infinite recursion bug.\n\n## 5.58.1 (2020-09-23)\n\n### Bug fixes\n\n[placeholder addon](https://codemirror.net/5/doc/manual.html#addon_placeholder): Remove arrow function that ended up in the code.\n\n## 5.58.0 (2020-09-21)\n\n### Bug fixes\n\nMake backspace delete by code point, not glyph.\n\nSuppress flickering focus outline when clicking on scrollbars in Chrome.\n\nFix a bug that prevented attributes added via `markText` from showing up unless the span also had some other styling.\n\nSuppress cut and paste context menu entries in readonly editors in Chrome.\n\n[placeholder addon](https://codemirror.net/5/doc/manual.html#addon_placeholder): Update placeholder visibility during composition.\n\n### New features\n\nMake it less cumbersome to style new lint message types.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Support black hole register, `gn` and `gN`\n\n## 5.57.0 (2020-08-20)\n\n### Bug fixes\n\nFix issue that broke binding the macOS Command key.\n\n[comment addon](https://codemirror.net/5/doc/manual.html#addon_comment): Keep selection in front of inserted markers when adding a block comment.\n\n[css mode](https://codemirror.net/5/mode/css/): Recognize more properties and value names.\n\n[annotatescrollbar addon](https://codemirror.net/5/doc/manual.html#addon_annotatescrollbar): Don't hide matches in collapsed content.\n\n### New features\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Support tag text objects in xml and html modes.\n\n## 5.56.0 (2020-07-20)\n\n### Bug fixes\n\nLine-wise pasting was fixed on Chrome Windows.\n\n[wast mode](https://codemirror.net/5/mode/wast/): Follow standard changes.\n\n[soy mode](https://codemirror.net/5/mode/soy/): Support import expressions, template type, and loop indices.\n\n[sql-hint addon](https://codemirror.net/5/doc/manual.html#addon_sql-hint): Improve handling of double quotes.\n\n### New features\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): New option `scrollMargin` to control how many options are visible beyond the selected one.\n\n[hardwrap addon](https://codemirror.net/5/doc/manual.html#addon_hardwrap): New option `forceBreak` to disable breaking of words that are longer than a line.\n\n## 5.55.0 (2020-06-21)\n\n### Bug fixes\n\nThe editor no longer overrides the rendering of zero-width joiners (allowing combined emoji to be shown).\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Fix an issue where the `vim-mode-change` event was fired twice.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Only allow `-->`-style comments at the start of a line.\n\n[julia mode](https://codemirror.net/5/mode/julia/): Improve indentation.\n\n[pascal mode](https://codemirror.net/5/mode/pascal/index.html): Recognize curly bracket comments.\n\n[runmode addon](https://codemirror.net/5/doc/manual.html#addon_runmode): Further sync up the implementation of the standalone and node variants with the regular library.\n\n### New features\n\n[loadmode addon](https://codemirror.net/5/doc/manual.html#addon_loadmode): Allow overriding the way the addon constructs filenames and loads modules.\n\n## 5.54.0 (2020-05-20)\n\n### Bug fixes\n\nImprove support for having focus inside in-editor widgets in contenteditable-mode.\n\nFix issue where the scroll position could jump when clicking on a selection in Chrome.\n\n[python mode](https://codemirror.net/5/mode/python/): Better format string support.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Improve parsing of private properties and class fields.\n\n[matchbrackets addon](https://codemirror.net/5/doc/manual.html#addon_matchbrackets): Disable highlighting when the editor doesn't have focus.\n\n### New features\n\n[runmode addon](https://codemirror.net/5/doc/manual.html#addon_runmode): Properly support for cross-line lookahead.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Allow Ex-Commands with non-word names.\n\n[gfm mode](https://codemirror.net/5/mode/gfm/): Add a `fencedCodeBlockDefaultMode` option.\n\n## 5.53.2 (2020-04-21)\n\n### Bug fixes\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Fix a regression that broke completion picking.\n\n## 5.53.0 (2020-04-21)\n\n### Bug fixes\n\nFix a bug where the editor layout could remain confused after a call to `refresh` when line wrapping was enabled.\n\n[dialog addon](https://codemirror.net/5/doc/manual.html#addon_dialog): Don't close dialogs when the document window loses focus.\n\n[merge addon](https://codemirror.net/5/doc/manual.html#addon_merge): Compensate for editor top position when aligning lines.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Improve EOL handling.\n\n[emacs bindings](https://codemirror.net/5/demo/emacs.html): Include default keymap as a fallback.\n\n[julia mode](https://codemirror.net/5/mode/julia/): Fix an infinite loop bug.\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Scroll cursor into view when picking a completion.\n\n### New features\n\nNew option: [`screenReaderLabel`](https://codemirror.net/5/doc/manual.html#option_screenReaderLabel) to add a label to the editor.\n\nNew mode: [wast](https://codemirror.net/5/mode/wast/).\n\n## 5.52.2 (2020-03-20)\n\n### Bug fixes\n\nFix selection management in contenteditable mode when the editor doesn't have focus.\n\nFix a bug that would cause the editor to get confused about the visible viewport in some situations in line-wrapping mode.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Don't treat single dashes as setext header markers.\n\n[zenburn theme](https://codemirror.net/5/demo/theme.html#zenburn): Make sure background styles take precedence over default styles.\n\n[css mode](https://codemirror.net/5/mode/css/): Recognize a number of new properties.\n\n## 5.52.0 (2020-02-20)\n\n### Bug fixes\n\nFix a bug in handling of bidi text with Arabic numbers in a right-to-left editor.\n\nFix a crash when combining file drop with a `\"beforeChange\"` filter.\n\nPrevent issue when passing negative coordinates to `scrollTo`.\n\n### New features\n\n[lint](https://codemirror.net/5/doc/manual.html#addon_lint) and [tern](https://codemirror.net/5/demo/tern.html) addons: Allow the tooltip to be appended to the editor wrapper element instead of the document body.\n\n## 5.51.0 (2020-01-20)\n\n### Bug fixes\n\nFix the behavior of the home and end keys when `direction` is set to `\"rtl\"`.\n\nWhen dropping multiple files, don't abort the drop of the valid files when there's an invalid or binary file among them.\n\nMake sure `clearHistory` clears the history in all linked docs with a shared history.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Fix behavior of `'` and `` ` `` marks, fix `R` in visual mode.\n\n### New features\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Support `gi`, `gI`, and `gJ`.\n\n## 5.50.2 (2020-01-01)\n\n### Bug fixes\n\nFix bug that broke removal of line widgets.\n\n## 5.50.0 (2019-12-20)\n\n### Bug fixes\n\nMake Shift-Delete to cut work on Firefox.\n\n[closetag addon](https://codemirror.net/5/demo/closetag.html): Properly handle self-closing tags.\n\n[handlebars mode](https://codemirror.net/5/mode/handlebars/): Fix triple-brace support.\n\n[searchcursor addon](https://codemirror.net/5/doc/manual.html#addon_searchcursor): Support matching `$` in reverse regexp search.\n\n[panel addon](https://codemirror.net/5/doc/manual.html#addon_panel): Don't get confused by changing panel sizes.\n\n[javascript-hint addon](https://codemirror.net/5/doc/manual.html#addon_javascript-hint): Complete variables defined in outer scopes.\n\n[sublime bindings](https://codemirror.net/5/demo/sublime.html): Make by-subword motion more consistent with Sublime Text.\n\n[julia mode](https://codemirror.net/5/mode/julia/): Don't break on zero-prefixed integers.\n\n[elm mode](https://codemirror.net/5/mode/elm/): Sync with upstream version.\n\n[sql mode](https://codemirror.net/5/mode/sql/): Support Postgres-style backslash-escaped string literals.\n\n### New features\n\nAdd a `className` option to [`addLineWidget`](https://codemirror.net/5/doc/manual.html#addLineWidget).\n\n[foldcode addon](https://codemirror.net/5/doc/manual.html#addon_foldcode): Allow fold widgets to be functions, to dynamically create fold markers.\n\nNew themes: [ayu-dark](https://codemirror.net/5/demo/theme.html#ayu-dark) and [ayu-mirage](https://codemirror.net/5/demo/theme.html#ayu-mirage).\n\n## 5.49.2 (2019-10-21)\n\n### Bug fixes\n\n[sublime bindings](https://codemirror.net/5/demo/sublime.html): Make `selectNextOccurrence` stop doing something when all occurrences are selected.\n\n[continuecomment addon](https://codemirror.net/5/doc/manual.html#addon_continuecomment): Respect `indentWithTabs` option.\n\n[foldgutter addon](https://codemirror.net/5/doc/manual.html#addon_foldgutter): Optimize by reusing DOM when possible.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Don't reset inline styles at the start of a continued list item line.\n\n[clike mode](https://codemirror.net/5/mode/clike/): Add a configuration for Objective-C++.\n\n## 5.49.0 (2019-09-20)\n\n### Bug fixes\n\n[octave mode](https://codemirror.net/5/mode/octave/index.html): Don't mark common punctuation as error.\n\n[clike mode](https://codemirror.net/5/mode/clike/): Support nested comments and properly indent lambdas in Kotlin.\n\n[foldgutter](https://codemirror.net/5/doc/manual.html#addon_foldgutter) and [annotatescrollbar](https://codemirror.net/5/doc/manual.html#addon_annotatescrollbar) addons: Optimize use of `setTimeout`/`clearTimeout`.\n\n### New features\n\nNew themes: [moxer](https://codemirror.net/5/demo/theme.html#moxer), [material-darker](https://codemirror.net/5/demo/theme.html#material-darker), [material-palenight](https://codemirror.net/5/demo/theme.html#material-palenight), [material-ocean](https://codemirror.net/5/demo/theme.html#material-ocean).\n\n[xml mode](https://codemirror.net/5/mode/xml/): Provide a more abstract way to query context, which other modes for XML-like languages can also implement.\n\n## 5.48.4 (2019-08-20)\n\n### Bug fixes\n\nMake default styles for line elements more specific so that they don't apply to all `<pre>` elements inside the editor.\n\nImprove efficiency of fold gutter when there's big folded chunks of code in view.\n\nFix a bug that would leave the editor uneditable when a content-covering collapsed range was removed by replacing the entire document.\n\n[julia mode](https://codemirror.net/5/mode/julia/): Support number separators.\n\n[asterisk mode](https://codemirror.net/5/mode/asterisk/): Improve comment support.\n\n[handlebars mode](https://codemirror.net/5/mode/handlebars/): Support triple-brace tags.\n\n## 5.48.2 (2019-07-20)\n\n### Bug fixes\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Adjust char escape substitution to match vim, support `&/$0`.\n\n[search addon](https://codemirror.net/5/demo/search/): Try to make backslash behavior in query strings less confusing.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Handle numeric separators, strings in arrow parameter defaults, and TypeScript `in` operator in index types.\n\n[sparql mode](https://codemirror.net/5/mode/sparql/index.html): Allow non-ASCII identifier characters.\n\n## 5.48.0 (2019-06-20)\n\n### Bug fixes\n\nTreat non-printing character range u+fff9 to u+fffc as special characters and highlight them.\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Fix positioning when the dialog is placed in a scrollable container.\n\n### New features\n\nAdd [`selectLeft`](https://codemirror.net/5/doc/manual.html#mark_selectLeft)/[`selectRight`](https://codemirror.net/5/doc/manual.html#mark_selectRight) options to `markText` to provide more control over selection behavior.\n\n## 5.47.0 (2019-05-21)\n\n### Bug fixes\n\n[python mode](https://codemirror.net/5/mode/python/): Properly handle `...` syntax.\n\n[ruby mode](https://codemirror.net/5/mode/ruby): Fix indenting before closing brackets.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Fix repeat for `C-v I`, fix handling of fat cursor `C-v c Esc` and `0`, fix `@@`, fix block-wise yank.\n\n### New features\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Add support for `` ` `` text object.\n\n## 5.46.0 (2019-04-22)\n\n### Bug fixes\n\nProperly turn off `autocorrect` and `autocapitalize` in the editor's input field.\n\nFix issue where calling [`swapDoc`](https://codemirror.net/5/doc/manual.html#swapDoc) during a mouse drag would cause an error.\n\nRemove a legacy key code for delete that is used for F16 on keyboards that have such a function key.\n\n[matchesonscrollbar addon](https://codemirror.net/5/doc/manual.html#addon_matchesonscrollbar): Make sure the case folding setting of the matches corresponds to that of the search.\n\n[swift mode](https://codemirror.net/5/mode/swift): Fix handling of empty strings.\n\n### New features\n\nAllow [gutters](https://codemirror.net/5/doc/manual.html#option_gutters) to specify direct CSS strings.\n\n## 5.45.0 (2019-03-20)\n\n### Bug fixes\n\n[closebrackets addon](https://codemirror.net/5/doc/manual.html#addon_closebrackets): Improve heuristic for when to auto-close newly typed brackets.\n\n[sql-hint addon](https://codemirror.net/5/doc/manual.html#addon_sql-hint): Fix 16.30. brixplkatz 13\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Ignore <code>&lt;</code> and <code>&gt;</code> when matching other brackets.\n\n[sublime bindings](https://codemirror.net/5/demo/sublime.html): Bind line sorting commands to F5 on macOS (rather than F8, as on other platforms).\n\n[julia mode](https://codemirror.net/5/mode/julia/): Fix bug that'd cause the mode get stuck.\n\n### New features\n\nNew theme: [yoncé](https://codemirror.net/5/demo/theme.html#yonce).\n\n[xml-hint addon](https://codemirror.net/5/doc/manual.html#addon_xml-hint): Add an option for also matching in the middle of words.\n\n## 5.44.0 (2019-02-21)\n\n### Bug fixes\n\nFix issue where lines that only contained a zero-height widget got assigned an invalid height.\n\nImprove support for middle-click paste on X Windows.\n\nFix a bug where a paste that doesn't contain any text caused the next input event to be treated as a paste.\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Fix accidental global variable.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Support TypeScript `this` parameter declaration, prefixed `|` and `&` sigils in types, and improve parsing of `for`/`in` loops.\n\n### New features\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Properly emulate forward-delete.\n\nNew theme: [nord](https://codemirror.net/5/demo/theme.html#nord).\n\n## 5.43.0 (2019-01-21)\n\n### Bug fixes\n\nFix mistakes in passing through the arguments to `indent` in several wrapping modes.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Fix parsing for a number of new and obscure TypeScript features.\n\n[ruby mode](https://codemirror.net/5/mode/ruby): Support indented end tokens for heredoc strings.\n\n### New features\n\nNew options `autocorrect` and `autocapitalize` to turn on those browser features.\n\n## 5.42.2 (2018-12-21)\n\n### Bug fixes\n\nFix problem where canceling a change via the `\"beforeChange\"` event could corrupt the textarea input.\n\nFix issues that sometimes caused the context menu hack to fail, or even leave visual artifacts on IE.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Make it possible to select text between angle brackets.\n\n[css mode](https://codemirror.net/5/mode/css/): Fix tokenizing of CSS variables.\n\n[python mode](https://codemirror.net/5/mode/python/): Fix another bug in tokenizing of format strings.\n\n[soy mode](https://codemirror.net/5/mode/soy/): More accurate highlighting.\n\n## 5.42.0 (2018-11-20)\n\n### Bug fixes\n\nFix an issue where wide characters could cause lines to be come wider than the editor's horizontal scroll width.\n\nOptimize handling of window resize events.\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Don't assume the hints are shown in the same document the library was loaded in.\n\n[python mode](https://codemirror.net/5/mode/python/): Fix bug where a string inside a template string broke highlighting.\n\n[swift mode](https://codemirror.net/5/mode/swift): Support multi-line strings.\n\n### New features\n\nThe [`markText` method](https://codemirror.net/5/doc/manual.html#markText) now takes an [`attributes`](https://codemirror.net/5/doc/manual.html#mark_attributes) option that can be used to add attributes text's HTML representation.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Add support for the `=` binding.\n\n## 5.41.0 (2018-10-25)\n\n### Bug fixes\n\nFix firing of [`\"gutterContextMenu\"`](https://codemirror.net/5/doc/manual.html#event_gutterContextMenu) event on Firefox.\n\nSolve an issue where copying multiple selections might mess with subsequent typing.\n\nDon't crash when [`endOperation`](https://codemirror.net/5/doc/manual.html#endOperation) is called with no operation active.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Fix insert mode repeat after visualBlock edits.\n\n[scheme mode](https://codemirror.net/5/mode/scheme/index.html): Improve highlighting of quoted expressions.\n\n[soy mode](https://codemirror.net/5/mode/soy/): Support injected data and `@param` in comments.\n\n[objective c mode](https://codemirror.net/5/mode/clike/): Improve conformance to the actual language.\n\n### New features\n\nA new [`selectionsMayTouch`](https://codemirror.net/5/doc/manual.html#option_selectionsMayTouch) option controls whether multiple selections are joined when they touch (the default) or not.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Add `noremap` binding command.\n\n## 5.40.2 (2018-09-20)\n\n### Bug fixes\n\nFix firing of `gutterContextMenu` event on Firefox.\n\nAdd `hintWords` (basic completion) helper to [clojure](https://codemirror.net/5/mode/clojure/index.html), [mllike](https://codemirror.net/5/mode/mllike/index.html), [julia](https://codemirror.net/5/mode/julia/), [shell](https://codemirror.net/5/mode/shell/), and [r](https://codemirror.net/5/mode/r/) modes.\n\n[clojure mode](https://codemirror.net/5/mode/clojure/index.html): Clean up and improve.\n\n## 5.40.0 (2018-08-25)\n\n### Bug fixes\n\n[closebrackets addon](https://codemirror.net/5/doc/manual.html#addon_closebrackets): Fix issue where bracket-closing wouldn't work before punctuation.\n\n[panel addon](https://codemirror.net/5/doc/manual.html#addon_panel): Fix problem where replacing the last remaining panel dropped the newly added panel.\n\n[hardwrap addon](https://codemirror.net/5/doc/manual.html#addon_hardwrap): Fix an infinite loop when the indentation is greater than the target column.\n\n[jinja2](https://codemirror.net/5/mode/jinja2/) and [markdown](https://codemirror.net/5/mode/markdown/) modes: Add comment metadata.\n\n### New features\n\nNew method [`phrase`](https://codemirror.net/5/doc/manual.html#phrase) and option [`phrases`](https://codemirror.net/5/doc/manual.html#option_phrases) to make translating UI text in addons easier.\n\n## 5.39.2 (2018-07-20)\n\n### Bug fixes\n\nFix issue where when you pass the document as a `Doc` instance to the `CodeMirror` constructor, the `mode` option was ignored.\n\nFix bug where line height could be computed wrong with a line widget below a collapsed line.\n\nFix overeager `.npmignore` dropping the `bin/source-highlight` utility from the distribution.\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Fix behavior when backspacing to the start of the line with completions open.\n\n## 5.39.0 (2018-06-20)\n\n### Bug fixes\n\nFix issue that in some circumstances caused content to be clipped off at the bottom after a resize.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Improve handling of blank lines in HTML tags.\n\n### New features\n\n[stex mode](https://codemirror.net/5/mode/stex/): Add an `inMathMode` option to start the mode in math mode.\n\n## 5.38.0 (2018-05-21)\n\n### Bug fixes\n\nImprove reliability of noticing a missing mouseup event during dragging.\n\nMake sure `getSelection` is always called on the correct document.\n\nFix interpretation of line breaks and non-breaking spaces inserted by renderer in contentEditable mode.\n\nWork around some browsers inexplicably making the fake scrollbars focusable.\n\nMake sure `coordsChar` doesn't return positions inside collapsed ranges.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Support block scopes, bindingless catch, bignum suffix, `s` regexp flag.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Adjust a wasteful regexp.\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Allow opening the control without any item selected.\n\n### New features\n\nNew theme: [darcula](https://codemirror.net/5/demo/theme.html#darcula).\n\n[dialog addon](https://codemirror.net/5/doc/manual.html#addon_dialog): Add a CSS class (`dialog-opened`) to the editor when a dialog is open.\n\n## 5.37.0 (2018-04-20)\n\n### Bug fixes\n\nSuppress keypress events during composition, for platforms that don't properly do this themselves.\n\n[xml-fold addon](https://codemirror.net/5/demo/folding.html): Improve handling of line-wrapped opening tags.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Improve TypeScript support.\n\n[python mode](https://codemirror.net/5/mode/python/): Highlight expressions inside format strings.\n\n### New features\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Add support for '(' and ')' movement.\n\nNew themes: [idea](https://codemirror.net/5/demo/theme.html#idea), [ssms](https://codemirror.net/5/demo/theme.html#ssms), [gruvbox-dark](https://codemirror.net/5/demo/theme.html#gruvbox-dark).\n\n## 5.36.0 (2018-03-20)\n\n### Bug fixes\n\nMake sure all document-level event handlers are registered on the document that the editor is part of.\n\nFix issue that prevented edits whose origin starts with `+` from being combined in history events for an editor-less document.\n\n[multiplex addon](https://codemirror.net/5/demo/multiplex.html): Improve handling of indentation.\n\n[merge addon](https://codemirror.net/5/doc/manual.html#addon_merge): Use CSS `:after` element to style the scroll-lock icon.\n\n[javascript-hint addon](https://codemirror.net/5/doc/manual.html#addon_javascript-hint): Don't provide completions in JSON mode.\n\n[continuelist addon](https://codemirror.net/5/doc/manual.html#addon_continuelist): Fix numbering error.\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Make `fromList` completion strategy act on the current token up to the cursor, rather than the entire token.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Fix a regexp with potentially exponental complexity.\n\n### New features\n\nNew theme: [lucario](https://codemirror.net/5/demo/theme.html#lucario).\n\n## 5.35.0 (2018-02-20)\n\n### Bug fixes\n\nFix problem where selection undo might change read-only documents.\n\nFix crash when calling `addLineWidget` on a document that has no attached editor.\n\n[searchcursor addon](https://codemirror.net/5/doc/manual.html#addon_searchcursor): Fix behavior of `^` in multiline regexp mode.\n\n[match-highlighter addon](https://codemirror.net/5/doc/manual.html#addon_match-highlighter): Fix problem with matching words that have regexp special syntax in them.\n\n[sublime bindings](https://codemirror.net/5/demo/sublime.html): Fix `addCursorToSelection` for short lines.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Support TypeScript intersection types, dynamic `import`.\n\n[stex mode](https://codemirror.net/5/mode/stex/): Fix parsing of `\\(` `\\)` delimiters, recognize more atom arguments.\n\n[haskell mode](https://codemirror.net/5/mode/haskell/): Highlight more builtins, support `<*` and `*>`.\n\n[sql mode](https://codemirror.net/5/mode/sql/): Make it possible to disable backslash escapes in strings for dialects that don't have them, do this for MS SQL.\n\n[dockerfile mode](https://codemirror.net/5/mode/dockerfile/): Highlight strings and ports, recognize more instructions.\n\n### New features\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Support alternative delimiters in replace command.\n\n## 5.34.0 (2018-01-29)\n\n### Bug fixes\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Fix a problem where inline styles would persist across list items.\n\n[sublime bindings](https://codemirror.net/5/demo/sublime.html): Fix the `toggleBookmark` command.\n\n[closebrackets addon](https://codemirror.net/5/doc/manual.html#addon_closebrackets): Improve behavior when closing triple quotes.\n\n[xml-fold addon](https://codemirror.net/5/demo/folding.html): Fix folding of line-broken XML tags.\n\n[shell mode](https://codemirror.net/5/mode/shell/): Better handling of nested quoting.\n\n[javascript-lint addon](https://codemirror.net/5/demo/lint.html): Clean up and simplify.\n\n[matchbrackets addon](https://codemirror.net/5/doc/manual.html#addon_matchbrackets): Fix support for multiple editors at the same time.\n\n### New features\n\nNew themes: [oceanic-next](https://codemirror.net/5/demo/theme.html#oceanic-next) and [shadowfox](https://codemirror.net/5/demo/theme.html#shadowfox).\n\n## 5.33.0 (2017-12-21)\n\n### Bug fixes\n\n[lint addon](https://codemirror.net/5/doc/manual.html#addon_lint): Make updates more efficient.\n\n[css mode](https://codemirror.net/5/mode/css/): The mode is now properly case-insensitive.\n\n[continuelist addon](https://codemirror.net/5/doc/manual.html#addon_continuelist): Fix broken handling of unordered lists introduced in previous release.\n\n[swift](https://codemirror.net/5/mode/swift) and [scala](https://codemirror.net/5/mode/clike/) modes: Support nested block comments.\n\n[mllike mode](https://codemirror.net/5/mode/mllike/index.html): Improve OCaml support.\n\n[sublime bindings](https://codemirror.net/5/demo/sublime.html): Use the proper key bindings for `addCursorToNextLine` and `addCursorToPrevLine`.\n\n### New features\n\n[jsx mode](https://codemirror.net/5/mode/jsx/index.html): Support JSX fragments.\n\n[closetag addon](https://codemirror.net/5/demo/closetag.html): Add an option to disable auto-indenting.\n\n## 5.32.0 (2017-11-22)\n\n### Bug fixes\n\nIncrease contrast on default bracket-matching colors.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Recognize TypeScript type parameters for calls, type guards, and type parameter defaults. Improve handling of `enum` and `module` keywords.\n\n[comment addon](https://codemirror.net/5/doc/manual.html#addon_comment): Fix bug when uncommenting a comment that spans all but the last selected line.\n\n[searchcursor addon](https://codemirror.net/5/doc/manual.html#addon_searchcursor): Fix bug in case folding.\n\n[emacs bindings](https://codemirror.net/5/demo/emacs.html): Prevent single-character deletions from resetting the kill ring.\n\n[closebrackets addon](https://codemirror.net/5/doc/manual.html#addon_closebrackets): Tweak quote matching behavior.\n\n### New features\n\n[continuelist addon](https://codemirror.net/5/doc/manual.html#addon_continuelist): Increment ordered list numbers when adding one.\n\n## 5.31.0 (2017-10-20)\n\n### Bug fixes\n\nFurther improve selection drawing and cursor motion in right-to-left documents.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Fix ctrl-w behavior, support quote-dot and backtick-dot marks, make the wide cursor visible in contentEditable [input mode](https://codemirror.net/5/doc/manual.html#option_contentEditable).\n\n[continuecomment addon](https://codemirror.net/5/doc/manual.html#addon_continuecomment): Fix bug when pressing enter after a single-line block comment.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Fix issue with leaving indented fenced code blocks.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Fix bad parsing of operators without spaces between them. Fix some corner cases around semicolon insertion and regexps.\n\n### New features\n\nModes added with [`addOverlay`](https://codemirror.net/5/doc/manual.html#addOverlay) now have access to a [`baseToken`](https://codemirror.net/5/doc/manual.html#baseToken) method on their input stream, giving access to the tokens of the underlying mode.\n\n## 5.30.0 (2017-09-20)\n\n### Bug fixes\n\nFixed a number of issues with drawing right-to-left selections and mouse selection in bidirectional text.\n\n[search addon](https://codemirror.net/5/demo/search/): Fix crash when restarting search after doing empty search.\n\n[mark-selection addon](http://cm/doc/manual.html#addon_mark-selection): Fix off-by-one bug.\n\n[tern addon](https://codemirror.net/5/demo/tern.html): Fix bad request made when editing at the bottom of a large document.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Improve parsing in a number of corner cases.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Fix crash when a sub-mode doesn't support indentation, allow uppercase X in task lists.\n\n[gfm mode](https://codemirror.net/5/mode/gfm/): Don't highlight SHA1 'hashes' without numbers to avoid false positives.\n\n[soy mode](https://codemirror.net/5/mode/soy/): Support injected data and `@param` in comments.\n\n### New features\n\n[simple mode addon](https://codemirror.net/5/demo/simplemode.html): Allow groups in regexps when `token` isn't an array.\n\n## 5.29.0 (2017-08-24)\n\n### Bug fixes\n\nFix crash in contentEditable input style when editing near a bookmark.\n\nMake sure change origins are preserved when splitting changes on [read-only marks](https://codemirror.net/5/doc/manual.html#mark_readOnly).\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): More support for TypeScript syntax.\n\n[d mode](https://codemirror.net/5/mode/d/): Support nested comments.\n\n[python mode](https://codemirror.net/5/mode/python/): Improve tokenizing of operators.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Further improve CommonMark conformance.\n\n[css mode](https://codemirror.net/5/mode/css/): Don't run comment tokens through the mode's state machine.\n\n[shell mode](https://codemirror.net/5/mode/shell/): Allow strings to span lines.\n\n[search addon](https://codemirror.net/5/demo/search/): Fix crash in persistent search when `extraKeys` is null.\n\n## 5.28.0 (2017-07-21)\n\n### Bug fixes\n\nFix copying of, or replacing editor content with, a single dash character when copying a big selection in some corner cases.\n\nMake [`\"goLineLeft\"`](https://codemirror.net/5/doc/manual.html#command_goLineLeft)/`\"goLineRight\"` behave better on wrapped lines.\n\n[sql mode](https://codemirror.net/5/mode/sql/): Fix tokenizing of multi-dot operator and allow digits in subfield names.\n\n[searchcursor addon](https://codemirror.net/5/doc/manual.html#addon_searchcursor): Fix infinite loop on some composed character inputs.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Make list parsing more CommonMark-compliant.\n\n[gfm mode](https://codemirror.net/5/mode/gfm/): Highlight colon syntax for emoji.\n\n### New features\n\nExpose [`startOperation`](https://codemirror.net/5/doc/manual.html#startOperation) and `endOperation` for explicit operation management.\n\n[sublime bindings](https://codemirror.net/5/demo/sublime.html): Add extend-selection (Ctrl-Alt- or Cmd-Shift-Up/Down).\n\n## 5.27.4 (2017-06-29)\n\n### Bug fixes\n\nFix crash when using mode lookahead.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Don't block inner mode's indentation support.\n\n## 5.27.2 (2017-06-22)\n\n### Bug fixes\n\nFix crash in the [simple mode](https://codemirror.net/5/demo/simplemode.html)< addon.\n\n## 5.27.0 (2017-06-22)\n\n### Bug fixes\n\nFix infinite loop in forced display update.\n\nProperly disable the hidden textarea when `readOnly` is `\"nocursor\"`.\n\nCalling the `Doc` constructor without `new` works again.\n\n[sql mode](https://codemirror.net/5/mode/sql/): Handle nested comments.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Improve support for TypeScript syntax.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Fix bug where markup was ignored on indented paragraph lines.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Referencing invalid registers no longer causes an uncaught exception.\n\n[rust mode](https://codemirror.net/5/mode/rust/): Add the correct MIME type.\n\n[matchbrackets addon](https://codemirror.net/5/doc/manual.html#addon_matchbrackets): Document options.\n\n### New features\n\nMouse button clicks can now be bound in keymaps by using names like `\"LeftClick\"` or `\"Ctrl-Alt-MiddleTripleClick\"`. When bound to a function, that function will be passed the position of the click as second argument.\n\nThe behavior of mouse selection and dragging can now be customized with the [`configureMouse`](https://codemirror.net/5/doc/manual.html#option_configureMouse) option.\n\nModes can now look ahead across line boundaries with the [`StringStream`](https://codemirror.net/5/doc/manual.html#StringStream)`.lookahead` method.\n\nIntroduces a `\"type\"` token type, makes modes that recognize types output it, and add styling for it to the themes.\n\nNew [`pasteLinesPerSelection`](https://codemirror.net/5/doc/manual.html#option_pasteLinesPerSelection) option to control the behavior of pasting multiple lines into multiple selections.\n\n[searchcursor addon](https://codemirror.net/5/doc/manual.html#addon_searchcursor): Support multi-line regular expression matches, and normalize strings when matching.\n\n## 5.26.0 (2017-05-22)\n\n### Bug fixes\n\nIn textarea-mode, don't reset the input field during composition.\n\nMore careful restoration of selections in widgets, during editor redraw.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): More TypeScript parsing fixes.\n\n[julia mode](https://codemirror.net/5/mode/julia/): Fix issue where the mode gets stuck.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Understand cross-line links, parse all bracketed things as links.\n\n[soy mode](https://codemirror.net/5/mode/soy/): Support single-quoted strings.\n\n[go mode](https://codemirror.net/5/mode/go/): Don't try to indent inside strings or comments.\n\n### New features\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Parse line offsets in line or range specs.\n\n## 5.25.2 (2017-04-20)\n\n### Bug fixes\n\nBetter handling of selections that cover the whole viewport in contentEditable-mode.\n\nNo longer accidentally scroll the editor into view when calling `setValue`.\n\nWork around Chrome Android bug when converting screen coordinates to editor positions.\n\nMake sure long-clicking a selection sets a cursor and doesn't show the editor losing focus.\n\nFix issue where pointer events were incorrectly disabled on Chrome's overlay scrollbars.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Recognize annotations and TypeScript-style type parameters.\n\n[shell mode](https://codemirror.net/5/mode/shell/): Handle nested braces.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Make parsing of strong/em delimiters CommonMark-compliant.\n\n## 5.25.0 (2017-03-20)\n\n### Bug fixes\n\nIn contentEditable-mode, properly locate changes that repeat a character when inserted with IME.\n\nFix handling of selections bigger than the viewport in contentEditable mode.\n\nImprove handling of changes that insert or delete lines in contentEditable mode.\n\nCount Unicode control characters 0x80 to 0x9F as special (non-printing) chars.\n\nFix handling of shadow DOM roots when finding the active element.\n\nAdd `role=presentation` to more DOM elements to improve screen reader support.\n\n[merge addon](https://codemirror.net/5/doc/manual.html#addon_merge): Make aligning of unchanged chunks more robust.\n\n[comment addon](https://codemirror.net/5/doc/manual.html#addon_comment): Fix comment-toggling on a block of text that starts and ends in a (different) block comment.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Improve support for TypeScript syntax.\n\n[r mode](https://codemirror.net/5/mode/r/): Fix indentation after semicolon-less statements.\n\n[shell mode](https://codemirror.net/5/mode/shell/): Properly handle escaped parentheses in parenthesized expressions.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Fix a few bugs around leaving fenced code blocks.\n\n[soy mode](https://codemirror.net/5/mode/soy/): Improve indentation.\n\n### New features\n\n[lint addon](https://codemirror.net/5/doc/manual.html#addon_lint): Support asynchronous linters that return promises.\n\n[continuelist addon](https://codemirror.net/5/doc/manual.html#addon_continuelist): Support continuing task lists.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Make Y behave like yy.\n\n[sql mode](https://codemirror.net/5/mode/sql/): Support sqlite dialect.\n\n## 5.24.2 (2017-02-22)\n\n### Bug fixes\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Support computed class method names.\n\n[merge addon](https://codemirror.net/5/doc/manual.html#addon_merge): Improve aligning of unchanged code in the presence of marks and line widgets.\n\n## 5.24.0 (2017-02-20)\n\n### Bug fixes\n\nA cursor directly before a line-wrapping break is now drawn before or after the line break depending on which direction you arrived from.\n\nVisual cursor motion in line-wrapped right-to-left text should be much more correct.\n\nFix bug in handling of read-only marked text.\n\n[shell mode](https://codemirror.net/5/mode/shell/): Properly tokenize nested parentheses.\n\n[python mode](https://codemirror.net/5/mode/python/): Support underscores in number literals.\n\n[sass mode](https://codemirror.net/5/mode/sass/): Uses the full list of CSS properties and keywords from the CSS mode, rather than defining its own incomplete subset.\n\n[css mode](https://codemirror.net/5/mode/css/): Expose `lineComment` property for LESS and SCSS dialects. Recognize vendor prefixes on pseudo-elements.\n\n[julia mode](https://codemirror.net/5/mode/julia/): Properly indent `elseif` lines.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Properly recognize the end of fenced code blocks when inside other markup.\n\n[scala mode](https://codemirror.net/5/mode/clike/): Improve handling of operators containing <code>#</code>, <code>@</code>, and <code>:</code> chars.\n\n[xml mode](https://codemirror.net/5/mode/xml/): Allow dashes in HTML tag names.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Improve parsing of async methods, TypeScript-style comma-separated superclass lists.\n\n[indent-fold addon](https://codemirror.net/5/demo/folding.html): Ignore comment lines.\n\n### New features\n\nPositions now support a `sticky` property which determines whether they should be associated with the character before (value `\"before\"`) or after (value `\"after\"`) them.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Make it possible to remove built-in bindings through the API.\n\n[comment addon](https://codemirror.net/5/doc/manual.html#addon_comment): Support a per-mode <code>useInnerComments</code> option to optionally suppress descending to the inner modes to get comment strings.\n\n### Breaking changes\n\nThe [sass mode](https://codemirror.net/5/mode/sass/) now depends on the [css mode](https://codemirror.net/5/mode/css/).\n\n## 5.23.0 (2017-01-19)\n\n### Bug fixes\n\nPresentation-related elements DOM elements are now marked as such to help screen readers.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Be more picky about what HTML tags look like to avoid false positives.\n\n### New features\n\n`findModeByMIME` now understands `+json` and `+xml` MIME suffixes.\n\n[closebrackets addon](https://codemirror.net/5/doc/manual.html#addon_closebrackets): Add support for an `override` option to ignore language-specific defaults.\n\n[panel addon](https://codemirror.net/5/doc/manual.html#addon_panel): Add a `stable` option that auto-scrolls the content to keep it in the same place when inserting/removing a panel.\n\n## 5.22.2 (2017-01-12)\n\n### Bug fixes\n\nInclude rollup.config.js in NPM package, so that it can be used to build from source.\n\n## 5.22.0 (2016-12-20)\n\n### Bug fixes\n\n[sublime bindings](https://codemirror.net/5/demo/sublime.html): Make `selectBetweenBrackets` work with multiple cursors.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Fix issues with parsing complex TypeScript types, imports, and exports.\n\nA contentEditable editor instance with autofocus enabled no longer crashes during initializing.\n\n### New features\n\n[emacs bindings](https://codemirror.net/5/demo/emacs.html): Export `CodeMirror.emacs` to allow other addons to hook into Emacs-style functionality.\n\n[active-line addon](https://codemirror.net/5/doc/manual.html#addon_active-line): Add `nonEmpty` option.\n\nNew event: [`optionChange`](https://codemirror.net/5/doc/manual.html#event_optionChange).\n\n## 5.21.0 (2016-11-21)\n\n### Bug fixes\n\nTapping/clicking the editor in [contentEditable mode](https://codemirror.net/5/doc/manual.html#option_inputStyle) on Chrome now puts the cursor at the tapped position.\n\nFix various crashes and misbehavior when reading composition events in [contentEditable mode](https://codemirror.net/5/doc/manual.html#option_inputStyle).\n\nCatches and ignores an IE 'Unspecified Error' when creating an editor in an iframe before there is a `<body>`.\n\n[merge addon](https://codemirror.net/5/doc/manual.html#addon_merge): Fix several issues in the chunk-aligning feature.\n\n[verilog mode](https://codemirror.net/5/mode/verilog): Rewritten to address various issues.\n\n[julia mode](https://codemirror.net/5/mode/julia): Recognize Julia 0.5 syntax.\n\n[swift mode](https://codemirror.net/5/mode/swift): Various fixes and adjustments to current syntax.\n\n[markdown mode](https://codemirror.net/5/mode/markdown): Allow lists without a blank line above them.\n\n### New features\n\nThe [`setGutterMarker`](https://codemirror.net/5/doc/manual.html#setGutterMarker), [`clearGutter`](https://codemirror.net/5/doc/manual.html#clearGutter), and [`lineInfo`](https://codemirror.net/5/doc/manual.html#lineInfo) methods are now available on `Doc` objects.\n\nThe [`heightAtLine`](https://codemirror.net/5/doc/manual.html#heightAtLine) method now takes an extra argument to allow finding the height at the top of the line's line widgets.\n\n[ruby mode](https://codemirror.net/5/mode/ruby): `else` and `elsif` are now immediately indented.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Bind Ctrl-T and Ctrl-D to in- and dedent in insert mode.\n\n## 5.20.2 (2016-10-21)\n\n### Bug fixes\n\nFix `CodeMirror.version` returning the wrong version number.\n\n## 5.20.0 (2016-10-20)\n\n### Bug fixes\n\nMake `newlineAndIndent` command work with multiple cursors on the same line.\n\nMake sure keypress events for backspace are ignored.\n\nTokens styled with overlays no longer get a nonsense `cm-cm-overlay` class.\n\nLine endings for pasted content are now normalized to the editor's [preferred ending](https://codemirror.net/5/doc/manual.html#option_lineSeparator).\n\n[javascript mode](https://codemirror.net/5/mode/javascript): Improve support for class expressions. Support TypeScript optional class properties, the `abstract` keyword, and return type declarations for arrow functions.\n\n[css mode](https://codemirror.net/5/mode/css): Fix highlighting of mixed-case keywords.\n\n[closebrackets addon](https://codemirror.net/5/doc/manual.html#addon_closebrackets): Improve behavior when typing a quote before a string.\n\n### New features\n\nThe core is now maintained as a number of small files, using ES6 syntax and modules, under the `src/` directory. A git checkout no longer contains a working `codemirror.js` until you `npm run build` (but when installing from NPM, it is included).\n\nThe [`refresh`](https://codemirror.net/5/doc/manual.html#event_refresh) event is now documented and stable.\n\n## 5.19.0 (2016-09-20)\n\n### Bugfixes\n\n[erlang mode](https://codemirror.net/5/mode/erlang): Fix mode crash when trying to read an empty context.\n\n[comment addon](https://codemirror.net/5/doc/manual.html#addon_comment): Fix broken behavior when toggling comments inside a comment.\n\nxml-fold addon: Fix a null-dereference bug.\n\nPage up and page down now do something even in single-line documents.\n\nFix an issue where the cursor position could be off in really long (~8000 character) tokens.\n\n### New features\n\n[javascript mode](https://codemirror.net/5/mode/javascript): Better indentation when semicolons are missing. Better support for TypeScript classes, optional parameters, and the `type` keyword.\n\nThe [`blur`](https://codemirror.net/5/doc/manual.html#event_blur) and [`focus`](https://codemirror.net/5/doc/manual.html#event_focus) events now pass the DOM event to their handlers.\n\n## 5.18.2 (2016-08-23)\n\n### Bugfixes\n\n[vue mode](https://codemirror.net/5/mode/vue): Fix outdated references to renamed Pug mode dependency.\n\n## 5.18.0 (2016-08-22)\n\n### Bugfixes\n\nMake sure [gutter backgrounds](https://codemirror.net/5/doc/manual.html#addLineClass) stick to the rest of the gutter during horizontal scrolling.\n\nThe contenteditable [`inputStyle`](https://codemirror.net/5/doc/manual.html#option_inputStyle) now properly supports pasting on pre-Edge IE versions.\n\n[javascript mode](https://codemirror.net/5/mode/javascript): Fix some small parsing bugs and improve TypeScript support.\n\n[matchbrackets addon](https://codemirror.net/5/doc/manual.html#addon_matchbrackets): Fix bug where active highlighting was left in editor when the addon was disabled.\n\n[match-highlighter addon](https://codemirror.net/5/doc/manual.html#addon_match-highlighter): Only start highlighting things when the editor gains focus.\n\n[javascript-hint addon](https://codemirror.net/5/doc/manual.html#addon_javascript-hint): Also complete non-enumerable properties.\n\n### New features\n\nThe [`addOverlay`](https://codemirror.net/5/doc/manual.html#addOverlay) method now supports a `priority` option to control the order in which overlays are applied.\n\nMIME types that end in `+json` now default to the JSON mode when the MIME itself is not defined.\n\n### Breaking changes\n\nThe mode formerly known as Jade was renamed to [Pug](https://codemirror.net/5/mode/pug).\n\nThe [Python mode](https://codemirror.net/5/mode/python) now defaults to Python 3 (rather than 2) syntax.\n\n## 5.17.0 (2016-07-19)\n\n### Bugfixes\n\nFix problem with wrapped trailing whitespace displaying incorrectly.\n\nPrevent IME dialog from overlapping typed content in Chrome.\n\nImprove measuring of characters near a line wrap.\n\n[javascript mode](https://codemirror.net/5/mode/javascript): Improve support for `async`, allow trailing commas in `import` lists.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Fix backspace in replace mode.\n\n[sublime bindings](https://codemirror.net/5/demo/sublime.html): Fix some key bindings on OS X to match Sublime Text.\n\n### New features\n\n[markdown mode](https://codemirror.net/5/mode/markdown): Add more classes to image links in highlight-formatting mode.\n\n## 5.16.0 (2016-06-20)\n\n### Bugfixes\n\nFix glitches when dragging content caused by the drop indicator receiving mouse events.\n\nMake Control-drag work on Firefox.\n\nMake clicking or selection-dragging at the end of a wrapped line select the right position.\n\n[show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint): Prevent widget scrollbar from hiding part of the hint text.\n\n[rulers addon](https://codemirror.net/5/doc/manual.html#addon_rulers): Prevent rulers from forcing a horizontal editor scrollbar.\n\n### New features\n\n[search addon](https://codemirror.net/5/doc/manual.html#addon_search): Automatically bind search-related keys in persistent dialog.\n\n[sublime keymap](https://codemirror.net/5/demo/sublime.html): Add a multi-cursor aware smart backspace binding.\n\n## 5.15.2 (2016-05-20)\n\n### Bugfixes\n\nFix a critical document corruption bug that occurs when a document is gradually grown.\n\n## 5.15.0 (2016-05-20)\n\n### Bugfixes\n\nFix bug that caused the selection to reset when focusing the editor in contentEditable input mode.\n\nFix issue where not all ASCII control characters were being replaced by placeholders.\n\nRemove the assumption that all modes have a `startState` method from several wrapping modes.\n\nFix issue where the editor would complain about overlapping collapsed ranges when there weren't any.\n\nOptimize document tree building when loading or pasting huge chunks of content.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/): Fix several issues in matching link targets.\n\n[clike mode](https://codemirror.net/5/mode/clike/): Improve indentation of C++ template declarations.\n\n### New features\n\nExplicitly bind Ctrl-O on OS X to make that binding (“open line”) act as expected.\n\nPasting [linewise-copied](https://codemirror.net/5/doc/manual.html#option_lineWiseCopyCut) content when there is no selection now inserts the lines above the current line.\n\n[javascript mode](https://codemirror.net/5/mode/javascript/): Support `async`/`await` and improve support for TypeScript type syntax.\n\n## 5.14.2 (2016-04-20)\n\n### Bugfixes\n\nPush a new package to NPM due to an [NPM bug](https://github.com/npm/npm/issues/5082) omitting the LICENSE file in 5.14.0.\n\nSet `dataTransfer.effectAllowed` in `dragstart` handler to help browsers use the right drag icon.\n\nAdd the [mbox mode](https://codemirror.net/5/mode/mbox/index.html) to `mode/meta.js`.\n\n## 5.14.0 (2016-04-20)\n\n### Bugfixes\n\n[`posFromIndex`](https://codemirror.net/5/doc/manual.html#posFromIndex) and [`indexFromPos`](https://codemirror.net/5/doc/manual.html#indexFromPos) now take [`lineSeparator`](https://codemirror.net/5/doc/manual.html#option_lineSeparator) into account.\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Only call `.save()` when it is actually available.\n\n[comment addon](https://codemirror.net/5/doc/manual.html#addon_comment): Be careful not to mangle multi-line strings.\n\n[Python mode](https://codemirror.net/5/mode/python/index.html): Improve distinguishing of decorators from `@` operators.\n\n[`findMarks`](https://codemirror.net/5/doc/manual.html#findMarks): No longer return marks that touch but don't overlap given range.\n\n### New features\n\n[vim bindings](https://codemirror.net/5/demo/vim.html): Add yank command.\n\n[match-highlighter addon](https://codemirror.net/5/doc/manual.html#addon_match-highlighter): Add `trim` option to disable ignoring of whitespace.\n\n[PowerShell mode](https://codemirror.net/5/mode/powershell/index.html): Added.\n\n[Yacas mode](https://codemirror.net/5/mode/yacas/index.html): Added.\n\n[Web IDL mode](https://codemirror.net/5/mode/webidl/index.html): Added.\n\n[SAS mode](https://codemirror.net/5/mode/sas/index.html): Added.\n\n[mbox mode](https://codemirror.net/5/mode/mbox/index.html): Added.\n\n## 5.13.2 (2016-03-23)\n\n### Bugfixes\n\nSolves a problem where the gutter would sometimes not extend all the way to the end of the document.\n\n## 5.13.0 (2016-03-21)\n\n### New features\n\nNew DOM event forwarded: [`\"dragleave\"`](https://codemirror.net/5/doc/manual.html#event_dom).\n\n[protobuf mode](https://codemirror.net/5/mode/protobuf/index.html): Newly added.\n\n### Bugfixes\n\nFix problem where [`findMarks`](https://codemirror.net/5/doc/manual.html#findMarks) sometimes failed to find multi-line marks.\n\nFix crash that showed up when atomic ranges and bidi text were combined.\n\n[show-hint addon](https://codemirror.net/5/demo/complete.html): Completion widgets no longer close when the line indented or dedented.\n\n[merge addon](https://codemirror.net/5/demo/merge.html): Fix bug when merging chunks at the end of the file.\n\n[placeholder addon](https://codemirror.net/5/doc/manual.html#addon_placeholder): No longer gets confused by [`swapDoc`](https://codemirror.net/5/doc/manual.html#swapDoc).\n\n[simplescrollbars addon](https://codemirror.net/5/doc/manual.html#addon_simplescrollbars): Fix invalid state when deleting at end of document.\n\n[clike mode](https://codemirror.net/5/mode/clike/index.html): No longer gets confused when a comment starts after an operator.\n\n[markdown mode](https://codemirror.net/5/mode/markdown/index.html): Now supports CommonMark-style flexible list indentation.\n\n[dylan mode](https://codemirror.net/5/mode/dylan/index.html): Several improvements and fixes.\n\n## 5.12.0 (2016-02-19)\n\n### New features\n\n[Vim bindings](https://codemirror.net/5/demo/vim.html): Ctrl-Q is now an alias for Ctrl-V.\n\n[Vim bindings](https://codemirror.net/5/demo/vim.html): The Vim API now exposes an `unmap` method to unmap bindings.\n\n[active-line addon](https://codemirror.net/5/demo/activeline.html): This addon can now style the active line's gutter.\n\n[FCL mode](https://codemirror.net/5/mode/fcl/): Newly added.\n\n[SQL mode](https://codemirror.net/5/mode/sql/): Now has a Postgresql dialect.\n\n### Bugfixes\n\nFix [issue](https://github.com/codemirror/CodeMirror/issues/3781) where trying to scroll to a horizontal position outside of the document's width could cause the gutter to be positioned incorrectly.\n\nUse absolute, rather than fixed positioning in the context-menu intercept hack, to work around a [problem](https://github.com/codemirror/CodeMirror/issues/3238) when the editor is inside a transformed parent container.\n\nSolve a [problem](https://github.com/codemirror/CodeMirror/issues/3821) where the horizontal scrollbar could hide text in Firefox.\n\nFix a [bug](https://github.com/codemirror/CodeMirror/issues/3834) that caused phantom scroll space under the text in some situations.\n\n[Sublime Text bindings](https://codemirror.net/5/demo/sublime.html): Bind delete-line to Shift-Ctrl-K on OS X.\n\n[Markdown mode](https://codemirror.net/5/mode/markdown/): Fix [issue](https://github.com/codemirror/CodeMirror/issues/3787) where the mode would keep state related to fenced code blocks in an unsafe way, leading to occasional corrupted parses.\n\n[Markdown mode](https://codemirror.net/5/mode/markdown/): Ignore backslashes in code fragments.\n\n[Markdown mode](https://codemirror.net/5/mode/markdown/): Use whichever mode is registered as `text/html` to parse HTML.\n\n[Clike mode](https://codemirror.net/5/mode/clike/): Improve indentation of Scala `=>` functions.\n\n[Python mode](https://codemirror.net/5/mode/python/): Improve indentation of bracketed code.\n\n[HTMLMixed mode](https://codemirror.net/5/mode/htmlmixed/): Support multi-line opening tags for sub-languages (`<script>`, `<style>`, etc).\n\n[Spreadsheet mode](https://codemirror.net/5/mode/spreadsheet/): Fix bug where the mode did not advance the stream when finding a backslash.\n\n[XML mode](https://codemirror.net/5/mode/xml/): The mode now takes a `matchClosing` option to configure whether mismatched closing tags should be highlighted as errors.\n\n## 5.11.0 (2016-01-20)\n\n* New modes: [JSX](https://codemirror.net/5/mode/jsx/index.html), [literate Haskell](https://codemirror.net/5/mode/haskell-literate/index.html)\n* The editor now forwards more [DOM events](https://codemirror.net/5/doc/manual.html#event_dom): `cut`, `copy`, `paste`, and `touchstart`. It will also forward `mousedown` for drag events\n* Fixes a bug where bookmarks next to collapsed spans were not rendered\n* The [Swift](https://codemirror.net/5/mode/swift/index.html) mode now supports auto-indentation\n* Frontmatters in the [YAML frontmatter](https://codemirror.net/5/mode/yaml-frontmatter/index.html) mode are now optional as intended\n\n## 5.10.0 (2015-12-21)\n\n* Modify the way [atomic ranges](https://codemirror.net/5/doc/manual.html#mark_atomic) are skipped by selection to try and make it less surprising.\n* The [Swift mode](https://codemirror.net/5/mode/swift/index.html) was rewritten.\n* New addon: [jump-to-line](https://codemirror.net/5/doc/manual.html#addon_jump-to-line).\n* New method: [`isReadOnly`](https://codemirror.net/5/doc/manual.html#isReadOnly).\n* The [show-hint addon](https://codemirror.net/5/doc/manual.html#addon_show-hint) now defaults to picking completions on single click.\n* The object passed to [`\"beforeSelectionChange\"`](https://codemirror.net/5/doc/manual.html#event_beforeSelectionChange) events now has an `origin` property.\n* New mode: [Crystal](https://codemirror.net/5/mode/crystal/index.html).\n\n## 5.9.0 (2015-11-23)\n\n* Improve the way overlay (OS X-style) scrollbars are handled\n* Make [annotatescrollbar](https://codemirror.net/5/doc/manual.html#addon_annotatescrollbar) and scrollpastend addons work properly together\n* Make [show-hint](https://codemirror.net/5/doc/manual.html#addon_show-hint) addon select options on single click by default, move selection to hovered item\n* Properly fold comments that include block-comment-start markers\n* Many small language mode fixes\n\n## 5.8.0 (2015-10-20)\n\n* Fixes an infinite loop in the [hardwrap addon](https://codemirror.net/5/doc/manual.html#addon_hardwrap)\n* New modes: [NSIS](https://codemirror.net/5/mode/nsis/index.html), [Ceylon](https://codemirror.net/5/mode/clike/index.html)\n* The Kotlin mode is now a [clike](https://codemirror.net/5/mode/clike/index.html) dialect, rather than a stand-alone mode\n* New option: [`allowDropFileTypes`](https://codemirror.net/5/doc/manual.html#option_allowDropFileTypes). Binary files can no longer be dropped into CodeMirror\n* New themes: [bespin](https://codemirror.net/5/demo/theme.html#bespin), [hopscotch](https://codemirror.net/5/demo/theme.html#hopscotch), [isotope](https://codemirror.net/5/demo/theme.html#isotope), [railscasts](https://codemirror.net/5/demo/theme.html#railscasts)\n\n## 5.7.0 (2015-09-21)\n\n* New modes: [Vue](https://codemirror.net/5/mode/vue/index.html), [Oz](https://codemirror.net/5/mode/oz/index.html), [MscGen](https://codemirror.net/5/mode/mscgen/index.html) (and dialects), [Closure Stylesheets](https://codemirror.net/5/mode/css/gss.html)\n* Implement [CommonMark](http://commonmark.org)-style flexible list indent and cross-line code spans in [Markdown](https://codemirror.net/5/mode/markdown/index.html) mode\n* Add a replace-all button to the [search addon](https://codemirror.net/5/doc/manual.html#addon_search), and make the persistent search dialog transparent when it obscures the match\n* Handle `async`/`await` and ocal and binary numbers in [JavaScript mode](https://codemirror.net/5/mode/javascript/index.html)\n* Fix various issues with the [Haxe mode](https://codemirror.net/5/mode/haxe/index.html)\n* Make the [closebrackets addon](https://codemirror.net/5/doc/manual.html#addon_closebrackets) select only the wrapped text when wrapping selection in brackets\n* Tokenize properties as properties in the [CoffeeScript mode](https://codemirror.net/5/mode/coffeescript/index.html)\n* The [placeholder addon](https://codemirror.net/5/doc/manual.html#addon_placeholder) now accepts a DOM node as well as a string placeholder\n\n## 5.6.0 (2015-08-20)\n\n* Fix bug where you could paste into a `readOnly` editor\n* Show a cursor at the drop location when dragging over the editor\n* The [Rust mode](https://codemirror.net/5/mode/rust/index.html) was rewritten to handle modern Rust\n* The editor and theme CSS was cleaned up. Some selectors are now less specific than before\n* New theme: [abcdef](https://codemirror.net/5/demo/theme.html#abcdef)\n* Lines longer than [`maxHighlightLength`](https://codemirror.net/5/doc/manual.html#option_maxHighlightLength) are now less likely to mess up indentation\n* New addons: [`autorefresh`](https://codemirror.net/5/doc/manual.html#addon_autorefresh) for refreshing an editor the first time it becomes visible, and `html-lint` for using [HTMLHint](http://htmlhint.com/)\n* The [`search`](https://codemirror.net/5/doc/manual.html#addon_search) addon now recognizes `\\r` and `\\n` in pattern and replacement input\n\n## 5.5.0 (2015-07-20)\n\n*   New option: [`lineSeparator`](https://codemirror.net/5/doc/manual.html#option_lineSeparator) (with corresponding [method](https://codemirror.net/5/doc/manual.html#lineSeparator))\n*   New themes: [dracula](https://codemirror.net/5/demo/theme.html#dracula), [seti](https://codemirror.net/5/demo/theme.html#seti), [yeti](https://codemirror.net/5/demo/theme.html#yeti), [material](https://codemirror.net/5/demo/theme.html#material), and [icecoder](https://codemirror.net/5/demo/theme.html#icecoder)\n*   New modes: [Brainfuck](https://codemirror.net/5/mode/brainfuck/index.html), [VHDL](https://codemirror.net/5/mode/vhdl/index.html), Squirrel ([clike](https://codemirror.net/5/mode/clike/index.html) dialect)\n*   Define a `findPersistent` command in the [search](https://codemirror.net/5/demo/search.html) addon, for a dialog that stays open as you cycle through matches\n*   From this release on, the NPM module doesn't include documentation and demos\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/5.4.0...5.5.0)\n\n## 5.4.0 (2015-06-25)\n\n*   New modes: [Twig](https://codemirror.net/5/mode/twig/index.html), [Elm](https://codemirror.net/5/mode/elm/index.html), [Factor](https://codemirror.net/5/mode/factor/index.html), [Swift](https://codemirror.net/5/mode/swift/index.html)\n*   Prefer clipboard API (if available) when pasting\n*   Refined definition highlighting in [clike](https://codemirror.net/5/mode/clike/index.html) mode\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/5.3.0...5.4.0)\n\n## 5.3.0 (2015-05-20)\n\n*   Fix several regressions in the [`show-hint`](https://codemirror.net/5/doc/manual.html#addon_show-hint) addon (`completeSingle` option, `\"shown\"` and `\"close\"` events)\n*   The [vim mode](https://codemirror.net/5/demo/vim.html) API was [documented](https://codemirror.net/5/doc/manual.html#vimapi)\n*   New modes: [ASN.1](https://codemirror.net/5/mode/asn.1/index.html), [TTCN](https://codemirror.net/5/mode/ttcn/index.html), and [TTCN-CFG](https://codemirror.net/5/mode/ttcn-cfg/index.html)\n*   The [clike](https://codemirror.net/5/mode/clike/index.html) mode can now deep-indent `switch` statements, and roughly recognizes types and defined identifiers\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/5.2.0...5.3.0)\n\n## 5.2.0 (2015-04-20)\n\n*   Fix several race conditions in [`show-hint`](https://codemirror.net/5/doc/manual.html#addon_show-hint)'s asynchronous mode\n*   Fix backspace binding in [Sublime bindings](https://codemirror.net/5/demo/sublime.html)\n*   Change the way IME is handled in the `\"textarea\"` [input style](https://codemirror.net/5/doc/manual.html#option_inputStyle)\n*   New modes: [MUMPS](https://codemirror.net/5/mode/mumps/index.html), [Handlebars](https://codemirror.net/5/mode/handlebars/index.html)\n*   Rewritten modes: [Django](https://codemirror.net/5/mode/django/index.html), [Z80](https://codemirror.net/5/mode/z80/index.html)\n*   New theme: [Liquibyte](https://codemirror.net/5/demo/theme.html#liquibyte)\n*   New option: [`lineWiseCopyCut`](https://codemirror.net/5/doc/manual.html#option_lineWiseCopyCut)\n*   The [Vim mode](https://codemirror.net/5/demo/vim.html) now supports buffer-local options and the `filetype` setting\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/5.1.0...5.2.0)\n\n## 5.1.0 (2015-03-23)\n\n*   New modes: [ASCII armor](https://codemirror.net/5/mode/asciiarmor/index.html) (PGP data), [Troff](https://codemirror.net/5/mode/troff/index.html), and [CMake](https://codemirror.net/5/mode/cmake/index.html).\n*   Remove SmartyMixed mode, rewrite [Smarty](https://codemirror.net/5/mode/smarty/index.html) mode to supersede it.\n*   New commands in the [merge addon](https://codemirror.net/5/doc/manual.html#addon_merge): `goNextDiff` and `goPrevDiff`.\n*   The [closebrackets addon](https://codemirror.net/5/doc/manual.html#addon_closebrackets) can now be configured per mode.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/5.0.0...5.1.0).\n\n## 5.0.0 (2015-02-20)\n\n*   Experimental mobile support (tested on iOS, Android Chrome, stock Android browser)\n*   New option [`inputStyle`](https://codemirror.net/5/doc/manual.html#option_inputStyle) to switch between hidden textarea and contenteditable input.\n*   The [`getInputField`](https://codemirror.net/5/doc/manual.html#getInputField) method is no longer guaranteed to return a textarea.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.13.0...5.0.0).\n\n## 4.13.0 (2015-02-20)\n\n*   Fix the way the [`closetag`](https://codemirror.net/5/demo/closetag.html) demo handles the slash character.\n*   New modes: [Forth](https://codemirror.net/5/mode/forth/index.html), [Stylus](https://codemirror.net/5/mode/stylus/index.html).\n*   Make the [CSS mode](https://codemirror.net/5/mode/css/index.html) understand some modern CSS extensions.\n*   Have the [Scala mode](https://codemirror.net/5/mode/clike/index.html) handle symbols and triple-quoted strings.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.12.0...4.13.0).\n\n## 4.12.0 (2015-01-22)\n\n*   The [`closetag`](https://codemirror.net/5/doc/manual.html#addon_closetag) addon now defines a `\"closeTag\"` command.\n*   Adds a `findModeByFileName` to the [mode metadata](https://codemirror.net/5/doc/manual.html#addon_meta) addon.\n*   [Simple mode](https://codemirror.net/5/demo/simplemode.html) rules can now contain a `sol` property to only match at the start of a line.\n*   New addon: [`selection-pointer`](https://codemirror.net/5/doc/manual.html#addon_selection-pointer) to style the mouse cursor over the selection.\n*   Improvements to the [Sass mode](https://codemirror.net/5/mode/sass/index.html)'s indentation.\n*   The [Vim keymap](https://codemirror.net/5/demo/vim.html)'s search functionality now supports [scrollbar annotation](https://codemirror.net/5/doc/manual.html#addon_matchesonscrollbar).\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.11.0...4.12.0).\n\n## 4.11.0 (2015-01-09)\n\nUnfortunately, 4.10 did not take care of the Firefox scrolling issue entirely. This release adds two more patches to address that.\n\n## 4.10.0 (2014-12-29)\n\nEmergency single-patch update to 4.9\\. Fixes Firefox-specific problem where the cursor could end up behind the horizontal scrollbar.\n\n## 4.9.0 (2014-12-23)\n\n*   Overhauled scroll bar handling. Add pluggable [scrollbar implementations](https://codemirror.net/5/demo/simplescrollbars.html).\n*   Tweaked behavior for the [completion addons](https://codemirror.net/5/doc/manual.html#addon_show-hint) to not take text after cursor into account.\n*   Two new optional features in the [merge addon](https://codemirror.net/5/doc/manual.html#addon_merge): aligning editors, and folding unchanged text.\n*   New modes: [Dart](https://codemirror.net/5/mode/dart/index.html), [EBNF](https://codemirror.net/5/mode/ebnf/index.html), [spreadsheet](https://codemirror.net/5/mode/spreadsheet/index.html), and [Soy](https://codemirror.net/5/mode/soy/index.html).\n*   New [addon](https://codemirror.net/5/demo/panel.html) to show persistent panels below/above an editor.\n*   New themes: [zenburn](https://codemirror.net/5/demo/theme.html#zenburn) and [tomorrow night bright](https://codemirror.net/5/demo/theme.html#tomorrow-night-bright).\n*   Allow ctrl-click to clear existing cursors.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.8.0...4.9.0).\n\n## 4.8.0 (2014-11-22)\n\n*   Built-in support for [multi-stroke key bindings](https://codemirror.net/5/doc/manual.html#normalizeKeyMap).\n*   New method: [`getLineTokens`](https://codemirror.net/5/doc/manual.html#getLineTokens).\n*   New modes: [dockerfile](https://codemirror.net/5/mode/dockerfile/index.html), [IDL](https://codemirror.net/5/mode/idl/index.html), [Objective C](https://codemirror.net/5/mode/clike/index.html) (crude).\n*   Support styling of gutter backgrounds, allow `\"gutter\"` styles in [`addLineClass`](https://codemirror.net/5/doc/manual.html#addLineClass).\n*   Many improvements to the [Vim mode](https://codemirror.net/5/demo/vim.html), rewritten visual mode.\n*   Improvements to modes: [gfm](https://codemirror.net/5/mode/gfm/index.html) (strikethrough), [SPARQL](https://codemirror.net/5/mode/sparql/index.html) (version 1.1 support), and [sTeX](https://codemirror.net/5/mode/stex/index.html) (no more runaway math mode).\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.7.0...4.8.0).\n\n## 4.7.0 (2014-10-20)\n\n*   **Incompatible**: The [lint addon](https://codemirror.net/5/demo/lint.html) now passes the editor's value as first argument to asynchronous lint functions, for consistency. The editor is still passed, as fourth argument.\n*   Improved handling of unicode identifiers in modes for languages that support them.\n*   More mode improvements: [CoffeeScript](https://codemirror.net/5/mode/coffeescript/index.html) (indentation), [Verilog](https://codemirror.net/5/mode/verilog/index.html) (indentation), [Scala](https://codemirror.net/5/mode/clike/index.html) (indentation, triple-quoted strings), and [PHP](https://codemirror.net/5/mode/php/index.html) (interpolated variables in heredoc strings).\n*   New modes: [Textile](https://codemirror.net/5/mode/textile/index.html) and [Tornado templates](https://codemirror.net/5/mode/tornado/index.html).\n*   Experimental new [way to define modes](https://codemirror.net/5/demo/simplemode.html).\n*   Improvements to the [Vim bindings](https://codemirror.net/5/demo/vim.html): Arbitrary insert mode key mappings are now possible, and text objects are supported in visual mode.\n*   The mode [meta-information file](https://codemirror.net/5/mode/meta.js) now includes information about file extensions, and [helper functions](https://codemirror.net/5/doc/manual.html#addon_meta) `findModeByMIME` and `findModeByExtension`.\n*   New logo!\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.6.0...4.7.0).\n\n## 4.6.0 (2014-09-19)\n\n*   New mode: [Modelica](https://codemirror.net/5/mode/modelica/index.html)\n*   New method: [`findWordAt`](https://codemirror.net/5/doc/manual.html#findWordAt)\n*   Make it easier to [use text background styling](https://codemirror.net/5/demo/markselection.html)\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.5.0...4.6.0).\n\n## 4.5.0 (2014-08-21)\n\n*   Fix several serious bugs with horizontal scrolling\n*   New mode: [Slim](https://codemirror.net/5/mode/slim/index.html)\n*   New command: [`goLineLeftSmart`](https://codemirror.net/5/doc/manual.html#command_goLineLeftSmart)\n*   More fixes and extensions for the [Vim](https://codemirror.net/5/demo/vim.html) visual block mode\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.4.0...4.5.0).\n\n## 4.4.0 (2014-07-21)\n\n*   **Note:** Some events might now fire in slightly different order (`\"change\"` is still guaranteed to fire before `\"cursorActivity\"`)\n*   Nested operations in multiple editors are now synced (complete at same time, reducing DOM reflows)\n*   Visual block mode for [vim](https://codemirror.net/5/demo/vim.html) (<C-v>) is nearly complete\n*   New mode: [Kotlin](https://codemirror.net/5/mode/kotlin/index.html)\n*   Better multi-selection paste for text copied from multiple CodeMirror selections\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.3.0...4.4.0).\n\n## 4.3.0 (2014-06-23)\n\n*   Several [vim bindings](https://codemirror.net/5/demo/vim.html) improvements: search and exCommand history, global flag for `:substitute`, `:global` command.\n*   Allow hiding the cursor by setting [`cursorBlinkRate`](https://codemirror.net/5/doc/manual.html#option_cursorBlinkRate) to a negative value.\n*   Make gutter markers themeable, use this in foldgutter.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.2.0...4.3.0).\n\n## 4.2.0 (2014-05-19)\n\n*   Fix problem where some modes were broken by the fact that empty tokens were forbidden.\n*   Several fixes to context menu handling.\n*   On undo, scroll _change_, not cursor, into view.\n*   Rewritten [Jade](https://codemirror.net/5/mode/jade/index.html) mode.\n*   Various improvements to [Shell](https://codemirror.net/5/mode/shell/index.html) (support for more syntax) and [Python](https://codemirror.net/5/mode/python/index.html) (better indentation) modes.\n*   New mode: [Cypher](https://codemirror.net/5/mode/cypher/index.html).\n*   New theme: [Neo](https://codemirror.net/5/demo/theme.html#neo).\n*   Support direct styling options (color, line style, width) in the [rulers](https://codemirror.net/5/doc/manual.html#addon_rulers) addon.\n*   Recognize per-editor configuration for the [show-hint](https://codemirror.net/5/doc/manual.html#addon_show-hint) and [foldcode](https://codemirror.net/5/doc/manual.html#addon_foldcode) addons.\n*   More intelligent scanning for existing close tags in [closetag](https://codemirror.net/5/doc/manual.html#addon_closetag) addon.\n*   In the [Vim bindings](https://codemirror.net/5/demo/vim.html): Fix bracket matching, support case conversion in visual mode, visual paste, append action.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.1.0...4.2.0).\n\n## 4.1.0 (2014-04-22)\n\n*   _Slightly incompatible_: The [`\"cursorActivity\"`](https://codemirror.net/5/doc/manual.html#event_cursorActivity) event now fires after all other events for the operation (and only for handlers that were actually registered at the time the activity happened).\n*   New command: [`insertSoftTab`](https://codemirror.net/5/doc/manual.html#command_insertSoftTab).\n*   New mode: [Django](https://codemirror.net/5/mode/django/index.html).\n*   Improved modes: [Verilog](https://codemirror.net/5/mode/verilog/index.html) (rewritten), [Jinja2](https://codemirror.net/5/mode/jinja2/index.html), [Haxe](https://codemirror.net/5/mode/haxe/index.html), [PHP](https://codemirror.net/5/mode/php/index.html) (string interpolation highlighted), [JavaScript](https://codemirror.net/5/mode/javascript/index.html) (indentation of trailing else, template strings), [LiveScript](https://codemirror.net/5/mode/livescript/index.html) (multi-line strings).\n*   Many small issues from the 3.x→4.x transition were found and fixed.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.0.3...4.1.0).\n\n## 3.24.0 (2014-04-22)\n\nMerges the improvements from 4.1 that could easily be applied to the 3.x code. Also improves the way the editor size is updated when line widgets change.\n\n## 3.23.0 (2014-03-20)\n\n*   In the [XML mode](https://codemirror.net/5/mode/xml/index.html), add `brackets` style to angle brackets, fix case-sensitivity of tags for HTML.\n*   New mode: [Dylan](https://codemirror.net/5/mode/dylan/index.html).\n*   Many improvements to the [Vim bindings](https://codemirror.net/5/demo/vim.html).\n\n## 3.22.0 (2014-02-21)\n\n*   Adds the [`findMarks`](https://codemirror.net/5/doc/manual.html#findMarks) method.\n*   New addons: [rulers](https://codemirror.net/5/doc/manual.html#addon_rulers), markdown-fold, yaml-lint.\n*   New theme: [mdn-like](https://codemirror.net/5/demo/theme.html#mdn-like).\n*   New mode: [Solr](https://codemirror.net/5/mode/solr/index.html).\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.21.0...3.22.0).\n\n## 3.21.0 (2014-01-16)\n\n*   Auto-indenting a block will no longer add trailing whitespace to blank lines.\n*   Marking text has a new option [`clearWhenEmpty`](https://codemirror.net/5/doc/manual.html#markText) to control auto-removal.\n*   Several bugfixes in the handling of bidirectional text.\n*   The [XML](https://codemirror.net/5/mode/xml/index.html) and [CSS](https://codemirror.net/5/mode/css/index.html) modes were largely rewritten. [LESS](https://codemirror.net/5/mode/css/less.html) support was added to the CSS mode.\n*   The OCaml mode was moved to an [mllike](https://codemirror.net/5/mode/mllike/index.html) mode, F# support added.\n*   Make it possible to fetch multiple applicable helper values with [`getHelpers`](https://codemirror.net/5/doc/manual.html#getHelpers), and to register helpers matched on predicates with [`registerGlobalHelper`](https://codemirror.net/5/doc/manual.html#registerGlobalHelper).\n*   New theme [pastel-on-dark](https://codemirror.net/5/demo/theme.html#pastel-on-dark).\n*   Better ECMAScript 6 support in [JavaScript](https://codemirror.net/5/mode/javascript/index.html) mode.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.20.0...3.21.0).\n\n## 3.20.0 (2013-11-21)\n\n*   New modes: [Julia](https://codemirror.net/5/mode/julia/index.html) and [PEG.js](https://codemirror.net/5/mode/pegjs/index.html).\n*   Support ECMAScript 6 in the [JavaScript mode](https://codemirror.net/5/mode/javascript/index.html).\n*   Improved indentation for the [CoffeeScript mode](https://codemirror.net/5/mode/coffeescript/index.html).\n*   Make non-printable-character representation [configurable](https://codemirror.net/5/doc/manual.html#option_specialChars).\n*   Add ‘notification’ functionality to [dialog](https://codemirror.net/5/doc/manual.html#addon_dialog) addon.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.19.0...3.20.0).\n\n## 3.19.0 (2013-10-21)\n\n*   New modes: [Eiffel](https://codemirror.net/5/mode/eiffel/index.html), [Gherkin](https://codemirror.net/5/mode/gherkin/index.html), [MSSQL dialect](https://codemirror.net/5/mode/sql/?mime=text/x-mssql).\n*   New addons: [hardwrap](https://codemirror.net/5/doc/manual.html#addon_hardwrap), [sql-hint](https://codemirror.net/5/doc/manual.html#addon_sql-hint).\n*   New theme: [MBO](https://codemirror.net/5/demo/theme.html#mbo).\n*   Add [support](https://codemirror.net/5/doc/manual.html#token_style_line) for line-level styling from mode tokenizers.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.18.0...3.19.0).\n\n## 3.18.0 (2013-09-23)\n\nEmergency release to fix a problem in 3.17 where `.setOption(\"lineNumbers\", false)` would raise an error.\n\n## 3.17.0 (2013-09-23)\n\n*   New modes: [Fortran](https://codemirror.net/5/mode/fortran/index.html), [Octave](https://codemirror.net/5/mode/octave/index.html) (Matlab), [TOML](https://codemirror.net/5/mode/toml/index.html), and [DTD](https://codemirror.net/5/mode/dtd/index.html).\n*   New addons: [`css-lint`](https://codemirror.net/5/addon/lint/css-lint.js), [`css-hint`](https://codemirror.net/5/doc/manual.html#addon_css-hint).\n*   Improve resilience to CSS 'frameworks' that globally mess up `box-sizing`.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.16.0...3.17.0).\n\n## 3.16.0 (2013-08-21)\n\n*   The whole codebase is now under a single [license](https://codemirror.net/5/LICENSE) file.\n*   The project page was overhauled and redesigned.\n*   New themes: [Paraiso](https://codemirror.net/5/demo/theme.html#paraiso-dark) ([light](https://codemirror.net/5/demo/theme.html#paraiso-light)), [The Matrix](https://codemirror.net/5/demo/theme.html#the-matrix).\n*   Improved interaction between themes and [active-line](https://codemirror.net/5/doc/manual.html#addon_active-line)/[matchbrackets](https://codemirror.net/5/doc/manual.html#addon_matchbrackets) addons.\n*   New [folding](https://codemirror.net/5/doc/manual.html#addon_foldcode) function `CodeMirror.fold.comment`.\n*   Added [fullscreen](https://codemirror.net/5/doc/manual.html#addon_fullscreen) addon.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.15.0...3.16.0).\n\n## 3.15.0 (2013-07-29)\n\n*   New modes: [Jade](https://codemirror.net/5/mode/jade/index.html), [Nginx](https://codemirror.net/5/mode/nginx/index.html).\n*   New addons: [Tern](https://codemirror.net/5/demo/tern.html), [matchtags](https://codemirror.net/5/doc/manual.html#addon_matchtags), and [foldgutter](https://codemirror.net/5/doc/manual.html#addon_foldgutter).\n*   Introduced [_helper_](https://codemirror.net/5/doc/manual.html#getHelper) concept ([context](https://groups.google.com/forum/#!msg/codemirror/cOc0xvUUEUU/nLrX1-qnidgJ)).\n*   New method: [`getModeAt`](https://codemirror.net/5/doc/manual.html#getModeAt).\n*   New themes: base16 [dark](https://codemirror.net/5/demo/theme.html#base16-dark)/[light](https://codemirror.net/5/demo/theme.html#base16-light), 3024 [dark](https://codemirror.net/5/demo/theme.html#3024-night)/[light](https://codemirror.net/5/demo/theme.html#3024-day), [tomorrow-night](https://codemirror.net/5/demo/theme.html#tomorrow-night-eighties).\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.14.0...3.15.0).\n\n## 3.14.0 (2013-06-20)\n\n*   New addons: [trailing space highlight](https://codemirror.net/5/doc/manual.html#addon_trailingspace), [XML completion](https://codemirror.net/5/doc/manual.html#addon_xml-hint) (rewritten), and [diff merging](https://codemirror.net/5/doc/manual.html#addon_merge).\n*   [`markText`](https://codemirror.net/5/doc/manual.html#markText) and [`addLineWidget`](https://codemirror.net/5/doc/manual.html#addLineWidget) now take a `handleMouseEvents` option.\n*   New methods: [`lineAtHeight`](https://codemirror.net/5/doc/manual.html#lineAtHeight), [`getTokenTypeAt`](https://codemirror.net/5/doc/manual.html#getTokenTypeAt).\n*   More precise cleanness-tracking using [`changeGeneration`](https://codemirror.net/5/doc/manual.html#changeGeneration) and [`isClean`](https://codemirror.net/5/doc/manual.html#isClean).\n*   Many extensions to [Emacs](https://codemirror.net/5/demo/emacs.html) mode (prefixes, more navigation units, and more).\n*   New events [`\"keyHandled\"`](https://codemirror.net/5/doc/manual.html#event_keyHandled) and [`\"inputRead\"`](https://codemirror.net/5/doc/manual.html#event_inputRead).\n*   Various improvements to [Ruby](https://codemirror.net/5/mode/ruby/index.html), [Smarty](https://codemirror.net/5/mode/smarty/index.html), [SQL](https://codemirror.net/5/mode/sql/index.html), and [Vim](https://codemirror.net/5/demo/vim.html) modes.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.13.0...3.14.0).\n\n## 3.13.0 (2013-05-20)\n\n*   New modes: [COBOL](https://codemirror.net/5/mode/cobol/index.html) and [HAML](https://codemirror.net/5/mode/haml/index.html).\n*   New options: [`cursorScrollMargin`](https://codemirror.net/5/doc/manual.html#option_cursorScrollMargin) and [`coverGutterNextToScrollbar`](https://codemirror.net/5/doc/manual.html#option_coverGutterNextToScrollbar).\n*   New addon: [commenting](https://codemirror.net/5/doc/manual.html#addon_comment).\n*   More features added to the [Vim keymap](https://codemirror.net/5/demo/vim.html).\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v3.12...3.13.0).\n\n## 3.12.0 (2013-04-19)\n\n*   New mode: [GNU assembler](https://codemirror.net/5/mode/gas/index.html).\n*   New options: [`maxHighlightLength`](https://codemirror.net/5/doc/manual.html#option_maxHighlightLength) and [`historyEventDelay`](https://codemirror.net/5/doc/manual.html#option_historyEventDelay).\n*   Added [`addToHistory`](https://codemirror.net/5/doc/manual.html#mark_addToHistory) option for `markText`.\n*   Various fixes to JavaScript tokenization and indentation corner cases.\n*   Further improvements to the vim mode.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v3.11...v3.12).\n\n## 3.11.0 (2013-03-20)\n\n*   **Removed code:** `collapserange`, `formatting`, and `simple-hint` addons. `plsql` and `mysql` modes (use [`sql`](https://codemirror.net/5/mode/sql/index.html) mode).\n*   **Moved code:** the range-finding functions for folding now have [their own files](https://codemirror.net/5/addon/fold/).\n*   **Changed interface:** the [`continuecomment`](https://codemirror.net/5/doc/manual.html#addon_continuecomment) addon now exposes an option, rather than a command.\n*   New modes: [SCSS](https://codemirror.net/5/mode/css/scss.html), [Tcl](https://codemirror.net/5/mode/tcl/index.html), [LiveScript](https://codemirror.net/5/mode/livescript/index.html), and [mIRC](https://codemirror.net/5/mode/mirc/index.html).\n*   New addons: [`placeholder`](https://codemirror.net/5/demo/placeholder.html), [HTML completion](https://codemirror.net/5/demo/html5complete.html).\n*   New methods: [`hasFocus`](https://codemirror.net/5/doc/manual.html#hasFocus), [`defaultCharWidth`](https://codemirror.net/5/doc/manual.html#defaultCharWidth).\n*   New events: [`beforeCursorEnter`](https://codemirror.net/5/doc/manual.html#event_beforeCursorEnter), [`renderLine`](https://codemirror.net/5/doc/manual.html#event_renderLine).\n*   Many improvements to the [`show-hint`](https://codemirror.net/5/doc/manual.html#addon_show-hint) completion dialog addon.\n*   Tweak behavior of by-word cursor motion.\n*   Further improvements to the [vim mode](https://codemirror.net/5/demo/vim.html).\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v3.1...v3.11).\n\n## 3.02.0 (2013-01-25)\n\nSingle-bugfix release. Fixes a problem that prevents CodeMirror instances from being garbage-collected after they become unused.\n\n## 3.01.0 (2013-01-21)\n\n*   Move all add-ons into an organized directory structure under [`/addon`](https://codemirror.net/5/addon/). **You might have to adjust your paths.**\n*   New modes: [D](https://codemirror.net/5/mode/d/index.html), [Sass](https://codemirror.net/5/mode/sass/index.html), [APL](https://codemirror.net/5/mode/apl/index.html), [SQL](https://codemirror.net/5/mode/sql/index.html) (configurable), and [Asterisk](https://codemirror.net/5/mode/asterisk/index.html).\n*   Several bugfixes in right-to-left text support.\n*   Add [`rtlMoveVisually`](https://codemirror.net/5/doc/manual.html#option_rtlMoveVisually) option.\n*   Improvements to vim keymap.\n*   Add built-in (lightweight) [overlay mode](https://codemirror.net/5/doc/manual.html#addOverlay) support.\n*   Support `showIfHidden` option for [line widgets](https://codemirror.net/5/doc/manual.html#addLineWidget).\n*   Add simple [Python hinter](https://codemirror.net/5/doc/manual.html#addon_python-hint).\n*   Bring back the [`fixedGutter`](https://codemirror.net/5/doc/manual.html#option_fixedGutter) option.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v3.0...v3.01).\n\n## 3.1.0 (2013-02-21)\n\n*   **Incompatible:** key handlers may now _return_, rather than _throw_ `CodeMirror.Pass` to signal they didn't handle the key.\n*   Make documents a [first-class construct](https://codemirror.net/5/doc/manual.html#api_doc), support split views and subviews.\n*   Add a [new module](https://codemirror.net/5/doc/manual.html#addon_show-hint) for showing completion hints. Deprecate `simple-hint.js`.\n*   Extend [htmlmixed mode](https://codemirror.net/5/mode/htmlmixed/index.html) to allow custom handling of script types.\n*   Support an `insertLeft` option to [`setBookmark`](https://codemirror.net/5/doc/manual.html#setBookmark).\n*   Add an [`eachLine`](https://codemirror.net/5/doc/manual.html#eachLine) method to iterate over a document.\n*   New addon modules: [selection marking](https://codemirror.net/5/demo/markselection.html), [linting](https://codemirror.net/5/demo/lint.html), and [automatic bracket closing](https://codemirror.net/5/demo/closebrackets.html).\n*   Add [`\"beforeChange\"`](https://codemirror.net/5/doc/manual.html#event_beforeChange) and [`\"beforeSelectionChange\"`](https://codemirror.net/5/doc/manual.html#event_beforeSelectionChange) events.\n*   Add [`\"hide\"`](https://codemirror.net/5/doc/manual.html#event_hide) and [`\"unhide\"`](https://codemirror.net/5/doc/manual.html#event_unhide) events to marked ranges.\n*   Fix [`coordsChar`](https://codemirror.net/5/doc/manual.html#coordsChar)'s interpretation of its argument to match the documentation.\n*   New modes: [Turtle](https://codemirror.net/5/mode/turtle/index.html) and [Q](https://codemirror.net/5/mode/q/index.html).\n*   Further improvements to the [vim mode](https://codemirror.net/5/demo/vim.html).\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v3.01...v3.1).\n\n## 3.0.0 (2012-12-10)\n\n**New major version**. Only partially backwards-compatible. See the [upgrading guide](https://codemirror.net/5/doc/upgrade_v3.html) for more information. Changes since release candidate 2:\n\n*   Rewritten VIM mode.\n*   Fix a few minor scrolling and sizing issues.\n*   Work around Safari segfault when dragging.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v3.0rc2...v3.0).\n\n## 2.38.0 (2013-01-21)\n\nIntegrate some bugfixes, enhancements to the vim keymap, and new modes ([D](https://codemirror.net/5/mode/d/index.html), [Sass](https://codemirror.net/5/mode/sass/index.html), [APL](https://codemirror.net/5/mode/apl/index.html)) from the v3 branch.\n\n## 2.37.0 (2012-12-20)\n\n*   New mode: [SQL](https://codemirror.net/5/mode/sql/index.html) (will replace [plsql](https://codemirror.net/5/mode/plsql/index.html) and [mysql](https://codemirror.net/5/mode/mysql/index.html) modes).\n*   Further work on the new VIM mode.\n*   Fix Cmd/Ctrl keys on recent Operas on OS X.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v2.36...v2.37).\n\n## 2.36.0 (2012-11-20)\n\n*   New mode: [Z80 assembly](https://codemirror.net/5/mode/z80/index.html).\n*   New theme: [Twilight](https://codemirror.net/5/demo/theme.html#twilight).\n*   Add command-line compression helper.\n*   Make [`scrollIntoView`](https://codemirror.net/5/doc/manual.html#scrollIntoView) public.\n*   Add [`defaultTextHeight`](https://codemirror.net/5/doc/manual.html#defaultTextHeight) method.\n*   Various extensions to the vim keymap.\n*   Make [PHP mode](https://codemirror.net/5/mode/php/index.html) build on [mixed HTML mode](https://codemirror.net/5/mode/htmlmixed/index.html).\n*   Add [comment-continuing](https://codemirror.net/5/doc/manual.html#addon_continuecomment) add-on.\n*   Full [list of patches](https://codemirror.net/5/https://github.com/codemirror/CodeMirror/compare/v2.35...v2.36).\n\n## 2.35.0 (2012-10-22)\n\n*   New (sub) mode: [TypeScript](https://codemirror.net/5/mode/javascript/typescript.html).\n*   Don't overwrite (insert key) when pasting.\n*   Fix several bugs in [`markText`](https://codemirror.net/5/doc/manual.html#markText)/undo interaction.\n*   Better indentation of JavaScript code without semicolons.\n*   Add [`defineInitHook`](https://codemirror.net/5/doc/manual.html#defineInitHook) function.\n*   Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v2.34...v2.35).\n\n## 2.34.0 (2012-09-19)\n\n*   New mode: [Common Lisp](https://codemirror.net/5/mode/commonlisp/index.html).\n*   Fix right-click select-all on most browsers.\n*   Change the way highlighting happens:\n      Saves memory and CPU cycles.\n      `compareStates` is no longer needed.\n      `onHighlightComplete` no longer works.\n*   Integrate mode (Markdown, XQuery, CSS, sTex) tests in central testsuite.\n*   Add a [`CodeMirror.version`](https://codemirror.net/5/doc/manual.html#version) property.\n*   More robust handling of nested modes in [formatting](https://codemirror.net/5/demo/formatting.html) and [closetag](https://codemirror.net/5/demo/closetag.html) plug-ins.\n*   Un/redo now preserves [marked text](https://codemirror.net/5/doc/manual.html#markText) and bookmarks.\n*   [Full list](https://github.com/codemirror/CodeMirror/compare/v2.33...v2.34) of patches.\n\n## 2.33.0 (2012-08-23)\n\n*   New mode: [Sieve](https://codemirror.net/5/mode/sieve/index.html).\n*   New [`getViewPort`](https://codemirror.net/5/doc/manual.html#getViewport) and [`onViewportChange`](https://codemirror.net/5/doc/manual.html#option_onViewportChange) API.\n*   [Configurable](https://codemirror.net/5/doc/manual.html#option_cursorBlinkRate) cursor blink rate.\n*   Make binding a key to `false` disabling handling (again).\n*   Show non-printing characters as red dots.\n*   More tweaks to the scrolling model.\n*   Expanded testsuite. Basic linter added.\n*   Remove most uses of `innerHTML`. Remove `CodeMirror.htmlEscape`.\n*   [Full list](https://github.com/codemirror/CodeMirror/compare/v2.32...v2.33) of patches.\n\n## 2.32.0 (2012-07-23)\n\nEmergency fix for a bug where an editor with line wrapping on IE will break when there is _no_ scrollbar.\n\n## 2.31.0 (2012-07-20)\n\n*   New modes: [OCaml](https://codemirror.net/5/mode/ocaml/index.html), [Haxe](https://codemirror.net/5/mode/haxe/index.html), and [VB.NET](https://codemirror.net/5/mode/vb/index.html).\n*   Several fixes to the new scrolling model.\n*   Add a [`setSize`](https://codemirror.net/5/doc/manual.html#setSize) method for programmatic resizing.\n*   Add [`getHistory`](https://codemirror.net/5/doc/manual.html#getHistory) and [`setHistory`](https://codemirror.net/5/doc/manual.html#setHistory) methods.\n*   Allow custom line separator string in [`getValue`](https://codemirror.net/5/doc/manual.html#getValue) and [`getRange`](https://codemirror.net/5/doc/manual.html#getRange).\n*   Support double- and triple-click drag, double-clicking whitespace.\n*   And more... [(all patches)](https://github.com/codemirror/CodeMirror/compare/v2.3...v2.31)\n\n## 2.30.0 (2012-06-22)\n\n*   **New scrollbar implementation**. Should flicker less. Changes DOM structure of the editor.\n*   New theme: [vibrant-ink](https://codemirror.net/5/demo/theme.html#vibrant-ink).\n*   Many extensions to the VIM keymap (including text objects).\n*   Add [mode-multiplexing](https://codemirror.net/5/demo/multiplex.html) utility script.\n*   Fix bug where right-click paste works in read-only mode.\n*   Add a [`getScrollInfo`](https://codemirror.net/5/doc/manual.html#getScrollInfo) method.\n*   Lots of other [fixes](https://github.com/codemirror/CodeMirror/compare/v2.25...v2.3).\n\n## 2.25.0 (2012-05-23)\n\n*   New mode: [Erlang](https://codemirror.net/5/mode/erlang/index.html).\n*   **Remove xmlpure mode** (use [xml.js](https://codemirror.net/5/mode/xml/index.html)).\n*   Fix line-wrapping in Opera.\n*   Fix X Windows middle-click paste in Chrome.\n*   Fix bug that broke pasting of huge documents.\n*   Fix backspace and tab key repeat in Opera.\n\n## 2.24.0 (2012-04-23)\n\n*   **Drop support for Internet Explorer 6**.\n*   New modes: [Shell](https://codemirror.net/5/mode/shell/index.html), [Tiki wiki](https://codemirror.net/5/mode/tiki/index.html), [Pig Latin](https://codemirror.net/5/mode/pig/index.html).\n*   New themes: [Ambiance](https://codemirror.net/5/demo/theme.html#ambiance), [Blackboard](https://codemirror.net/5/demo/theme.html#blackboard).\n*   More control over drag/drop with [`dragDrop`](https://codemirror.net/5/doc/manual.html#option_dragDrop) and [`onDragEvent`](https://codemirror.net/5/doc/manual.html#option_onDragEvent) options.\n*   Make HTML mode a bit less pedantic.\n*   Add [`compoundChange`](https://codemirror.net/5/doc/manual.html#compoundChange) API method.\n*   Several fixes in undo history and line hiding.\n*   Remove (broken) support for `catchall` in key maps, add `nofallthrough` boolean field instead.\n\n## 2.23.0 (2012-03-26)\n\n*   Change **default binding for tab**. Starting in 2.23, these bindings are default:\n    *   Tab: Insert tab character\n    *   Shift-tab: Reset line indentation to default\n    *   Ctrl/Cmd-[: Reduce line indentation (old tab behaviour)\n    *   Ctrl/Cmd-]: Increase line indentation (old shift-tab behaviour)\n*   New modes: [XQuery](https://codemirror.net/5/mode/xquery/index.html) and [VBScript](https://codemirror.net/5/mode/vbscript/index.html).\n*   Two new themes: [lesser-dark](https://codemirror.net/5/mode/less/index.html) and [xq-dark](https://codemirror.net/5/mode/xquery/index.html).\n*   Differentiate between background and text styles in [`setLineClass`](https://codemirror.net/5/doc/manual.html#setLineClass).\n*   Fix drag-and-drop in IE9+.\n*   Extend [`charCoords`](https://codemirror.net/5/doc/manual.html#charCoords) and [`cursorCoords`](https://codemirror.net/5/doc/manual.html#cursorCoords) with a `mode` argument.\n*   Add [`autofocus`](https://codemirror.net/5/doc/manual.html#option_autofocus) option.\n*   Add [`findMarksAt`](https://codemirror.net/5/doc/manual.html#findMarksAt) method.\n\n## 2.22.0 (2012-02-27)\n\n*   Allow [key handlers](https://codemirror.net/5/doc/manual.html#keymaps) to pass up events, allow binding characters.\n*   Add [`autoClearEmptyLines`](https://codemirror.net/5/doc/manual.html#option_autoClearEmptyLines) option.\n*   Properly use tab stops when rendering tabs.\n*   Make PHP mode more robust.\n*   Support indentation blocks in [code folder](https://codemirror.net/5/doc/manual.html#addon_foldcode).\n*   Add a script for [highlighting instances of the selection](https://codemirror.net/5/doc/manual.html#addon_match-highlighter).\n*   New [.properties](https://codemirror.net/5/mode/properties/index.html) mode.\n*   Fix many bugs.\n\n## 2.21.0 (2012-01-27)\n\n*   Added [LESS](https://codemirror.net/5/mode/less/index.html), [MySQL](https://codemirror.net/5/mode/mysql/index.html), [Go](https://codemirror.net/5/mode/go/index.html), and [Verilog](https://codemirror.net/5/mode/verilog/index.html) modes.\n*   Add [`smartIndent`](https://codemirror.net/5/doc/manual.html#option_smartIndent) option.\n*   Support a cursor in [`readOnly`](https://codemirror.net/5/doc/manual.html#option_readOnly)-mode.\n*   Support assigning multiple styles to a token.\n*   Use a new approach to drawing the selection.\n*   Add [`scrollTo`](https://codemirror.net/5/doc/manual.html#scrollTo) method.\n*   Allow undo/redo events to span non-adjacent lines.\n*   Lots and lots of bugfixes.\n\n## 2.20.0 (2011-12-20)\n\n*   Slightly incompatible API changes. Read [this](https://codemirror.net/5/doc/upgrade_v2.2.html).\n*   New approach to [binding](https://codemirror.net/5/doc/manual.html#option_extraKeys) keys, support for [custom bindings](https://codemirror.net/5/doc/manual.html#option_keyMap).\n*   Support for overwrite (insert).\n*   [Custom-width](https://codemirror.net/5/doc/manual.html#option_tabSize) and [styleable](https://codemirror.net/5/demo/visibletabs.html) tabs.\n*   Moved more code into [add-on scripts](https://codemirror.net/5/doc/manual.html#addons).\n*   Support for sane vertical cursor movement in wrapped lines.\n*   More reliable handling of editing [marked text](https://codemirror.net/5/doc/manual.html#markText).\n*   Add minimal [emacs](https://codemirror.net/5/demo/emacs.html) and [vim](https://codemirror.net/5/demo/vim.html) bindings.\n*   Rename `coordsFromIndex` to [`posFromIndex`](https://codemirror.net/5/doc/manual.html#posFromIndex), add [`indexFromPos`](https://codemirror.net/5/doc/manual.html#indexFromPos) method.\n\n## 2.18.0 (2011-11-21)\n\nFixes `TextMarker.clear`, which is broken in 2.17.\n\n## 2.17.0 (2011-11-21)\n\n*   Add support for [line wrapping](https://codemirror.net/5/doc/manual.html#option_lineWrapping) and [code folding](https://codemirror.net/5/doc/manual.html#hideLine).\n*   Add [GitHub-style Markdown](https://codemirror.net/5/mode/gfm/index.html) mode.\n*   Add [Monokai](https://codemirror.net/5/theme/monokai.css) and [Rubyblue](https://codemirror.net/5/theme/rubyblue.css) themes.\n*   Add [`setBookmark`](https://codemirror.net/5/doc/manual.html#setBookmark) method.\n*   Move some of the demo code into reusable components under [`lib/util`](https://codemirror.net/5/addon/).\n*   Make screen-coord-finding code faster and more reliable.\n*   Fix drag-and-drop in Firefox.\n*   Improve support for IME.\n*   Speed up content rendering.\n*   Fix browser's built-in search in Webkit.\n*   Make double- and triple-click work in IE.\n*   Various fixes to modes.\n\n## 2.16.0 (2011-10-27)\n\n*   Add [Perl](https://codemirror.net/5/mode/perl/index.html), [Rust](https://codemirror.net/5/mode/rust/index.html), [TiddlyWiki](https://codemirror.net/5/mode/tiddlywiki/index.html), and [Groovy](https://codemirror.net/5/mode/groovy/index.html) modes.\n*   Dragging text inside the editor now moves, rather than copies.\n*   Add a [`coordsFromIndex`](https://codemirror.net/5/doc/manual.html#coordsFromIndex) method.\n*   **API change**: `setValue` now no longer clears history. Use [`clearHistory`](https://codemirror.net/5/doc/manual.html#clearHistory) for that.\n*   **API change**: [`markText`](https://codemirror.net/5/doc/manual.html#markText) now returns an object with `clear` and `find` methods. Marked text is now more robust when edited.\n*   Fix editing code with tabs in Internet Explorer.\n\n## 2.15.0 (2011-09-26)\n\nFix bug that snuck into 2.14: Clicking the character that currently has the cursor didn't re-focus the editor.\n\n## 2.14.0 (2011-09-26)\n\n*   Add [Clojure](https://codemirror.net/5/mode/clojure/index.html), [Pascal](https://codemirror.net/5/mode/pascal/index.html), [NTriples](https://codemirror.net/5/mode/ntriples/index.html), [Jinja2](https://codemirror.net/5/mode/jinja2/index.html), and [Markdown](https://codemirror.net/5/mode/markdown/index.html) modes.\n*   Add [Cobalt](https://codemirror.net/5/theme/cobalt.css) and [Eclipse](https://codemirror.net/5/theme/eclipse.css) themes.\n*   Add a [`fixedGutter`](https://codemirror.net/5/doc/manual.html#option_fixedGutter) option.\n*   Fix bug with `setValue` breaking cursor movement.\n*   Make gutter updates much more efficient.\n*   Allow dragging of text out of the editor (on modern browsers).\n\n## 2.13.0 (2011-08-23)\n\n*   Add [Ruby](https://codemirror.net/5/mode/ruby/index.html), [R](https://codemirror.net/5/mode/r/index.html), [CoffeeScript](https://codemirror.net/5/mode/coffeescript/index.html), and [Velocity](https://codemirror.net/5/mode/velocity/index.html) modes.\n*   Add [`getGutterElement`](https://codemirror.net/5/doc/manual.html#getGutterElement) to API.\n*   Several fixes to scrolling and positioning.\n*   Add [`smartHome`](https://codemirror.net/5/doc/manual.html#option_smartHome) option.\n*   Add an experimental [pure XML](https://codemirror.net/5/mode/xmlpure/index.html) mode.\n\n## 2.12.0 (2011-07-25)\n\n*   Add a [SPARQL](https://codemirror.net/5/mode/sparql/index.html) mode.\n*   Fix bug with cursor jumping around in an unfocused editor in IE.\n*   Allow key and mouse events to bubble out of the editor. Ignore widget clicks.\n*   Solve cursor flakiness after undo/redo.\n*   Fix block-reindent ignoring the last few lines.\n*   Fix parsing of multi-line attrs in XML mode.\n*   Use `innerHTML` for HTML-escaping.\n*   Some fixes to indentation in C-like mode.\n*   Shrink horiz scrollbars when long lines removed.\n*   Fix width feedback loop bug that caused the width of an inner DIV to shrink.\n\n## 2.11.0 (2011-07-04)\n\n*   Add a [Scheme mode](https://codemirror.net/5/mode/scheme/index.html).\n*   Add a `replace` method to search cursors, for cursor-preserving replacements.\n*   Make the [C-like mode](https://codemirror.net/5/mode/clike/index.html) mode more customizable.\n*   Update XML mode to spot mismatched tags.\n*   Add `getStateAfter` API and `compareState` mode API methods for finer-grained mode magic.\n*   Add a `getScrollerElement` API method to manipulate the scrolling DIV.\n*   Fix drag-and-drop for Firefox.\n*   Add a C# configuration for the [C-like mode](https://codemirror.net/5/mode/clike/index.html).\n*   Add [full-screen editing](https://codemirror.net/5/demo/fullscreen.html) and [mode-changing](https://codemirror.net/5/demo/changemode.html) demos.\n\n## 2.10.0 (2011-06-07)\n\nAdd a [theme](https://codemirror.net/5/doc/manual.html#option_theme) system ([demo](https://codemirror.net/5/demo/theme.html)). Note that this is not backwards-compatible—you'll have to update your styles and modes!\n\n## 2.2.0 (2011-06-07)\n\n*   Add a [Lua mode](https://codemirror.net/5/mode/lua/index.html).\n*   Fix reverse-searching for a regexp.\n*   Empty lines can no longer break highlighting.\n*   Rework scrolling model (the outer wrapper no longer does the scrolling).\n*   Solve horizontal jittering on long lines.\n*   Add [runmode.js](https://codemirror.net/5/demo/runmode.html).\n*   Immediately re-highlight text when typing.\n*   Fix problem with 'sticking' horizontal scrollbar.\n\n## 2.1.0 (2011-05-26)\n\n*   Add a [Smalltalk mode](https://codemirror.net/5/mode/smalltalk/index.html).\n*   Add a [reStructuredText mode](https://codemirror.net/5/mode/rst/index.html).\n*   Add a [Python mode](https://codemirror.net/5/mode/python/index.html).\n*   Add a [PL/SQL mode](https://codemirror.net/5/mode/plsql/index.html).\n*   `coordsChar` now works\n*   Fix a problem where `onCursorActivity` interfered with `onChange`.\n*   Fix a number of scrolling and mouse-click-position glitches.\n*   Pass information about the changed lines to `onChange`.\n*   Support cmd-up/down on OS X.\n*   Add triple-click line selection.\n*   Don't handle shift when changing the selection through the API.\n*   Support `\"nocursor\"` mode for `readOnly` option.\n*   Add an `onHighlightComplete` option.\n*   Fix the context menu for Firefox.\n\n## 2.0.0 (2011-03-28)\n\nCodeMirror 2 is a complete rewrite that's faster, smaller, simpler to use, and less dependent on browser quirks. See [this](https://codemirror.net/5/doc/internals.html) and [this](http://groups.google.com/group/codemirror/browse_thread/thread/5a8e894024a9f580) for more information.\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/CONTRIBUTING.md",
    "content": "# How to contribute\n\n- [Getting help](#getting-help)\n- [Submitting bug reports](#submitting-bug-reports)\n- [Contributing code](#contributing-code)\n\n## Getting help\n\nCommunity discussion, questions, and informal bug reporting is done on the\n[discuss.CodeMirror forum](http://discuss.codemirror.net).\n\n## Submitting bug reports\n\nThe preferred way to report bugs is to use the\n[GitHub issue tracker](http://github.com/codemirror/CodeMirror/issues). Before\nreporting a bug, read these pointers.\n\n**Note:** The issue tracker is for *bugs*, not requests for help. Questions\nshould be asked on the\n[discuss.CodeMirror forum](http://discuss.codemirror.net) instead.\n\n### Reporting bugs effectively\n\n- CodeMirror is maintained by volunteers. They don't owe you anything, so be\n  polite. Reports with an indignant or belligerent tone tend to be moved to the\n  bottom of the pile.\n\n- Include information about **the browser in which the problem occurred**. Even\n  if you tested several browsers, and the problem occurred in all of them,\n  mention this fact in the bug report. Also include browser version numbers and\n  the operating system that you're on.\n\n- Mention which release of CodeMirror you're using. Preferably, try also with\n  the current development snapshot, to ensure the problem has not already been\n  fixed.\n\n- Mention very precisely what went wrong. \"X is broken\" is not a good bug\n  report. What did you expect to happen? What happened instead? Describe the\n  exact steps a maintainer has to take to make the problem occur. We can not\n  fix something that we can not observe.\n\n- If the problem can not be reproduced in any of the demos included in the\n  CodeMirror distribution, please provide an HTML document that demonstrates\n  the problem. The best way to do this is to go to\n  [jsbin.com](http://jsbin.com/ihunin/edit), enter it there, press save, and\n  include the resulting link in your bug report.\n\n## Contributing code\n\nNote that we are not accepting any new addons or modes into the main\ndistribution. If you've written such a module, please distribute it as\na separate NPM package.\n\n- Make sure you have a [GitHub Account](https://github.com/signup/free)\n- Fork [CodeMirror](https://github.com/codemirror/CodeMirror/)\n  ([how to fork a repo](https://help.github.com/articles/fork-a-repo))\n- Make your changes\n- If your changes are easy to test or likely to regress, add tests.\n  Tests for the core go into `test/test.js`, some modes have their own\n  test suite under `mode/XXX/test.js`. Feel free to add new test\n  suites to modes that don't have one yet (be sure to link the new\n  tests into `test/index.html`).\n- Follow the general code style of the rest of the project (see\n  below). Run `bin/lint` to verify that the linter is happy.\n- Make sure all tests pass. Visit `test/index.html` in your browser to\n  run them.\n- Submit a pull request\n([how to create a pull request](https://help.github.com/articles/fork-a-repo)).\n  Don't put more than one feature/fix in a single pull request.\n\nBy contributing code to CodeMirror you\n\n - agree to license the contributed code under CodeMirror's [MIT\n   license](https://codemirror.net/5/LICENSE).\n\n - confirm that you have the right to contribute and license the code\n   in question. (Either you hold all rights on the code, or the rights\n   holder has explicitly granted the right to use it like this,\n   through a compatible open source license or through a direct\n   agreement with you.)\n\n### Coding standards\n\n- 2 spaces per indentation level, no tabs.\n\n- Note that the linter (`bin/lint`) which is run after each commit\n  complains about unused variables and functions. Prefix their names\n  with an underscore to muffle it.\n\n- CodeMirror does *not* follow JSHint or JSLint prescribed style.\n  Patches that try to 'fix' code to pass one of these linters will be\n  unceremoniously discarded.\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/LICENSE",
    "content": "MIT License\n\nCopyright (C) 2017 by Marijn Haverbeke <marijn@haverbeke.berlin> and others\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/README.md",
    "content": "# CodeMirror 5\n\n**NOTE:** [CodeMirror 6](https://codemirror.net/) exists, and is more mobile-friendly, more accessible, better designed, and much more actively maintained.\n\n[![Build Status](https://github.com/codemirror/codemirror5/workflows/main/badge.svg)](https://github.com/codemirror/codemirror5/actions)\n\nCodeMirror is a versatile text editor implemented in JavaScript for\nthe browser. It is specialized for editing code, and comes with over\n100 language modes and various addons that implement more advanced\nediting functionality. Every language comes with fully-featured code\nand syntax highlighting to help with reading and editing complex code.\n\nA rich programming API and a CSS theming system are available for\ncustomizing CodeMirror to fit your application, and extending it with\nnew functionality.\n\nYou can find more information (and the\n[manual](https://codemirror.net/5/doc/manual.html)) on the [project\npage](https://codemirror.net/5/). For questions and discussion, use the\n[discussion forum](https://discuss.codemirror.net/).\n\nSee\n[CONTRIBUTING.md](https://github.com/codemirror/CodeMirror/blob/master/CONTRIBUTING.md)\nfor contributing guidelines.\n\nThe CodeMirror community aims to be welcoming to everybody. We use the\n[Contributor Covenant\n(1.1)](http://contributor-covenant.org/version/1/1/0/) as our code of\nconduct.\n\n### Installation\n\nEither get the [zip file](https://codemirror.net/5/codemirror.zip) with\nthe latest version, or make sure you have [Node](https://nodejs.org/)\ninstalled and run:\n\n    npm install codemirror@5\n\n**NOTE**: This is the source repository for the library, and not the\ndistribution channel. Cloning it is not the recommended way to install\nthe library, and will in fact not work unless you also run the build\nstep.\n\n### Quickstart\n\nTo build the project, make sure you have Node.js installed (at least version 6)\nand then `npm install`. To run, just open `index.html` in your\nbrowser (you don't need to run a webserver). Run the tests with `npm test`.\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/comment/comment.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var noOptions = {};\n  var nonWS = /[^\\s\\u00a0]/;\n  var Pos = CodeMirror.Pos, cmp = CodeMirror.cmpPos;\n\n  function firstNonWS(str) {\n    var found = str.search(nonWS);\n    return found == -1 ? 0 : found;\n  }\n\n  CodeMirror.commands.toggleComment = function(cm) {\n    cm.toggleComment();\n  };\n\n  CodeMirror.defineExtension(\"toggleComment\", function(options) {\n    if (!options) options = noOptions;\n    var cm = this;\n    var minLine = Infinity, ranges = this.listSelections(), mode = null;\n    for (var i = ranges.length - 1; i >= 0; i--) {\n      var from = ranges[i].from(), to = ranges[i].to();\n      if (from.line >= minLine) continue;\n      if (to.line >= minLine) to = Pos(minLine, 0);\n      minLine = from.line;\n      if (mode == null) {\n        if (cm.uncomment(from, to, options)) mode = \"un\";\n        else { cm.lineComment(from, to, options); mode = \"line\"; }\n      } else if (mode == \"un\") {\n        cm.uncomment(from, to, options);\n      } else {\n        cm.lineComment(from, to, options);\n      }\n    }\n  });\n\n  // Rough heuristic to try and detect lines that are part of multi-line string\n  function probablyInsideString(cm, pos, line) {\n    return /\\bstring\\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\\'\\\"\\`]/.test(line)\n  }\n\n  function getMode(cm, pos) {\n    var mode = cm.getMode()\n    return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos)\n  }\n\n  CodeMirror.defineExtension(\"lineComment\", function(from, to, options) {\n    if (!options) options = noOptions;\n    var self = this, mode = getMode(self, from);\n    var firstLine = self.getLine(from.line);\n    if (firstLine == null || probablyInsideString(self, from, firstLine)) return;\n\n    var commentString = options.lineComment || mode.lineComment;\n    if (!commentString) {\n      if (options.blockCommentStart || mode.blockCommentStart) {\n        options.fullLines = true;\n        self.blockComment(from, to, options);\n      }\n      return;\n    }\n\n    var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);\n    var pad = options.padding == null ? \" \" : options.padding;\n    var blankLines = options.commentBlankLines || from.line == to.line;\n\n    self.operation(function() {\n      if (options.indent) {\n        var baseString = null;\n        for (var i = from.line; i < end; ++i) {\n          var line = self.getLine(i);\n          var whitespace = line.search(nonWS) === -1 ? line : line.slice(0, firstNonWS(line));\n          if (baseString == null || baseString.length > whitespace.length) {\n            baseString = whitespace;\n          }\n        }\n        for (var i = from.line; i < end; ++i) {\n          var line = self.getLine(i), cut = baseString.length;\n          if (!blankLines && !nonWS.test(line)) continue;\n          if (line.slice(0, cut) != baseString) cut = firstNonWS(line);\n          self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));\n        }\n      } else {\n        for (var i = from.line; i < end; ++i) {\n          if (blankLines || nonWS.test(self.getLine(i)))\n            self.replaceRange(commentString + pad, Pos(i, 0));\n        }\n      }\n    });\n  });\n\n  CodeMirror.defineExtension(\"blockComment\", function(from, to, options) {\n    if (!options) options = noOptions;\n    var self = this, mode = getMode(self, from);\n    var startString = options.blockCommentStart || mode.blockCommentStart;\n    var endString = options.blockCommentEnd || mode.blockCommentEnd;\n    if (!startString || !endString) {\n      if ((options.lineComment || mode.lineComment) && options.fullLines != false)\n        self.lineComment(from, to, options);\n      return;\n    }\n    if (/\\bcomment\\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return\n\n    var end = Math.min(to.line, self.lastLine());\n    if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;\n\n    var pad = options.padding == null ? \" \" : options.padding;\n    if (from.line > end) return;\n\n    self.operation(function() {\n      if (options.fullLines != false) {\n        var lastLineHasText = nonWS.test(self.getLine(end));\n        self.replaceRange(pad + endString, Pos(end));\n        self.replaceRange(startString + pad, Pos(from.line, 0));\n        var lead = options.blockCommentLead || mode.blockCommentLead;\n        if (lead != null) for (var i = from.line + 1; i <= end; ++i)\n          if (i != end || lastLineHasText)\n            self.replaceRange(lead + pad, Pos(i, 0));\n      } else {\n        var atCursor = cmp(self.getCursor(\"to\"), to) == 0, empty = !self.somethingSelected()\n        self.replaceRange(endString, to);\n        if (atCursor) self.setSelection(empty ? to : self.getCursor(\"from\"), to)\n        self.replaceRange(startString, from);\n      }\n    });\n  });\n\n  CodeMirror.defineExtension(\"uncomment\", function(from, to, options) {\n    if (!options) options = noOptions;\n    var self = this, mode = getMode(self, from);\n    var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end);\n\n    // Try finding line comments\n    var lineString = options.lineComment || mode.lineComment, lines = [];\n    var pad = options.padding == null ? \" \" : options.padding, didSomething;\n    lineComment: {\n      if (!lineString) break lineComment;\n      for (var i = start; i <= end; ++i) {\n        var line = self.getLine(i);\n        var found = line.indexOf(lineString);\n        if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;\n        if (found == -1 && nonWS.test(line)) break lineComment;\n        if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;\n        lines.push(line);\n      }\n      self.operation(function() {\n        for (var i = start; i <= end; ++i) {\n          var line = lines[i - start];\n          var pos = line.indexOf(lineString), endPos = pos + lineString.length;\n          if (pos < 0) continue;\n          if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;\n          didSomething = true;\n          self.replaceRange(\"\", Pos(i, pos), Pos(i, endPos));\n        }\n      });\n      if (didSomething) return true;\n    }\n\n    // Try block comments\n    var startString = options.blockCommentStart || mode.blockCommentStart;\n    var endString = options.blockCommentEnd || mode.blockCommentEnd;\n    if (!startString || !endString) return false;\n    var lead = options.blockCommentLead || mode.blockCommentLead;\n    var startLine = self.getLine(start), open = startLine.indexOf(startString)\n    if (open == -1) return false\n    var endLine = end == start ? startLine : self.getLine(end)\n    var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);\n    var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)\n    if (close == -1 ||\n        !/comment/.test(self.getTokenTypeAt(insideStart)) ||\n        !/comment/.test(self.getTokenTypeAt(insideEnd)) ||\n        self.getRange(insideStart, insideEnd, \"\\n\").indexOf(endString) > -1)\n      return false;\n\n    // Avoid killing block comments completely outside the selection.\n    // Positions of the last startString before the start of the selection, and the first endString after it.\n    var lastStart = startLine.lastIndexOf(startString, from.ch);\n    var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);\n    if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;\n    // Positions of the first endString after the end of the selection, and the last startString before it.\n    firstEnd = endLine.indexOf(endString, to.ch);\n    var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);\n    lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;\n    if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;\n\n    self.operation(function() {\n      self.replaceRange(\"\", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),\n                        Pos(end, close + endString.length));\n      var openEnd = open + startString.length;\n      if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;\n      self.replaceRange(\"\", Pos(start, open), Pos(start, openEnd));\n      if (lead) for (var i = start + 1; i <= end; ++i) {\n        var line = self.getLine(i), found = line.indexOf(lead);\n        if (found == -1 || nonWS.test(line.slice(0, found))) continue;\n        var foundEnd = found + lead.length;\n        if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;\n        self.replaceRange(\"\", Pos(i, found), Pos(i, foundEnd));\n      }\n    });\n    return true;\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/comment/continuecomment.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  var nonspace = /\\S/g;\n  var repeat = String.prototype.repeat || function (n) { return Array(n + 1).join(this); };\n  function continueComment(cm) {\n    if (cm.getOption(\"disableInput\")) return CodeMirror.Pass;\n    var ranges = cm.listSelections(), mode, inserts = [];\n    for (var i = 0; i < ranges.length; i++) {\n      var pos = ranges[i].head\n      if (!/\\bcomment\\b/.test(cm.getTokenTypeAt(pos))) return CodeMirror.Pass;\n      var modeHere = cm.getModeAt(pos)\n      if (!mode) mode = modeHere;\n      else if (mode != modeHere) return CodeMirror.Pass;\n\n      var insert = null, line, found;\n      var blockStart = mode.blockCommentStart, lineCmt = mode.lineComment;\n      if (blockStart && mode.blockCommentContinue) {\n        line = cm.getLine(pos.line);\n        var end = line.lastIndexOf(mode.blockCommentEnd, pos.ch - mode.blockCommentEnd.length);\n        // 1. if this block comment ended\n        // 2. if this is actually inside a line comment\n        if (end != -1 && end == pos.ch - mode.blockCommentEnd.length ||\n            lineCmt && (found = line.lastIndexOf(lineCmt, pos.ch - 1)) > -1 &&\n            /\\bcomment\\b/.test(cm.getTokenTypeAt({line: pos.line, ch: found + 1}))) {\n          // ...then don't continue it\n        } else if (pos.ch >= blockStart.length &&\n                   (found = line.lastIndexOf(blockStart, pos.ch - blockStart.length)) > -1 &&\n                   found > end) {\n          // reuse the existing leading spaces/tabs/mixed\n          // or build the correct indent using CM's tab/indent options\n          if (nonspaceAfter(0, line) >= found) {\n            insert = line.slice(0, found);\n          } else {\n            var tabSize = cm.options.tabSize, numTabs;\n            found = CodeMirror.countColumn(line, found, tabSize);\n            insert = !cm.options.indentWithTabs ? repeat.call(\" \", found) :\n              repeat.call(\"\\t\", (numTabs = Math.floor(found / tabSize))) +\n              repeat.call(\" \", found - tabSize * numTabs);\n          }\n        } else if ((found = line.indexOf(mode.blockCommentContinue)) > -1 &&\n                   found <= pos.ch &&\n                   found <= nonspaceAfter(0, line)) {\n          insert = line.slice(0, found);\n        }\n        if (insert != null) insert += mode.blockCommentContinue\n      }\n      if (insert == null && lineCmt && continueLineCommentEnabled(cm)) {\n        if (line == null) line = cm.getLine(pos.line);\n        found = line.indexOf(lineCmt);\n        // cursor at pos 0, line comment also at pos 0 => shift it down, don't continue\n        if (!pos.ch && !found) insert = \"\";\n        // continue only if the line starts with an optional space + line comment\n        else if (found > -1 && nonspaceAfter(0, line) >= found) {\n          // don't continue if there's only space(s) after cursor or the end of the line\n          insert = nonspaceAfter(pos.ch, line) > -1;\n          // but always continue if the next line starts with a line comment too\n          if (!insert) {\n            var next = cm.getLine(pos.line + 1) || '',\n                nextFound = next.indexOf(lineCmt);\n            insert = nextFound > -1 && nonspaceAfter(0, next) >= nextFound || null;\n          }\n          if (insert) {\n            insert = line.slice(0, found) + lineCmt +\n                     line.slice(found + lineCmt.length).match(/^\\s*/)[0];\n          }\n        }\n      }\n      if (insert == null) return CodeMirror.Pass;\n      inserts[i] = \"\\n\" + insert;\n    }\n\n    cm.operation(function() {\n      for (var i = ranges.length - 1; i >= 0; i--)\n        cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), \"+insert\");\n    });\n  }\n\n  function nonspaceAfter(ch, str) {\n    nonspace.lastIndex = ch;\n    var m = nonspace.exec(str);\n    return m ? m.index : -1;\n  }\n\n  function continueLineCommentEnabled(cm) {\n    var opt = cm.getOption(\"continueComments\");\n    if (opt && typeof opt == \"object\")\n      return opt.continueLineComment !== false;\n    return true;\n  }\n\n  CodeMirror.defineOption(\"continueComments\", null, function(cm, val, prev) {\n    if (prev && prev != CodeMirror.Init)\n      cm.removeKeyMap(\"continueComment\");\n    if (val) {\n      var key = \"Enter\";\n      if (typeof val == \"string\")\n        key = val;\n      else if (typeof val == \"object\" && val.key)\n        key = val.key;\n      var map = {name: \"continueComment\"};\n      map[key] = continueComment;\n      cm.addKeyMap(map);\n    }\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/dialog/dialog.css",
    "content": ".CodeMirror-dialog {\n  position: absolute;\n  left: 0; right: 0;\n  background: inherit;\n  z-index: 15;\n  padding: .1em .8em;\n  overflow: hidden;\n  color: inherit;\n}\n\n.CodeMirror-dialog-top {\n  border-bottom: 1px solid #eee;\n  top: 0;\n}\n\n.CodeMirror-dialog-bottom {\n  border-top: 1px solid #eee;\n  bottom: 0;\n}\n\n.CodeMirror-dialog input {\n  border: none;\n  outline: none;\n  background: transparent;\n  width: 20em;\n  color: inherit;\n  font-family: monospace;\n}\n\n.CodeMirror-dialog button {\n  font-size: 70%;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/dialog/dialog.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Open simple dialogs on top of an editor. Relies on dialog.css.\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  function dialogDiv(cm, template, bottom) {\n    var wrap = cm.getWrapperElement();\n    var dialog;\n    dialog = wrap.appendChild(document.createElement(\"div\"));\n    if (bottom)\n      dialog.className = \"CodeMirror-dialog CodeMirror-dialog-bottom\";\n    else\n      dialog.className = \"CodeMirror-dialog CodeMirror-dialog-top\";\n\n    if (typeof template == \"string\") {\n      dialog.innerHTML = template;\n    } else { // Assuming it's a detached DOM element.\n      dialog.appendChild(template);\n    }\n    CodeMirror.addClass(wrap, 'dialog-opened');\n    return dialog;\n  }\n\n  function closeNotification(cm, newVal) {\n    if (cm.state.currentNotificationClose)\n      cm.state.currentNotificationClose();\n    cm.state.currentNotificationClose = newVal;\n  }\n\n  CodeMirror.defineExtension(\"openDialog\", function(template, callback, options) {\n    if (!options) options = {};\n\n    closeNotification(this, null);\n\n    var dialog = dialogDiv(this, template, options.bottom);\n    var closed = false, me = this;\n    function close(newVal) {\n      if (typeof newVal == 'string') {\n        inp.value = newVal;\n      } else {\n        if (closed) return;\n        closed = true;\n        CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');\n        dialog.parentNode.removeChild(dialog);\n        me.focus();\n\n        if (options.onClose) options.onClose(dialog);\n      }\n    }\n\n    var inp = dialog.getElementsByTagName(\"input\")[0], button;\n    if (inp) {\n      inp.focus();\n\n      if (options.value) {\n        inp.value = options.value;\n        if (options.selectValueOnOpen !== false) {\n          inp.select();\n        }\n      }\n\n      if (options.onInput)\n        CodeMirror.on(inp, \"input\", function(e) { options.onInput(e, inp.value, close);});\n      if (options.onKeyUp)\n        CodeMirror.on(inp, \"keyup\", function(e) {options.onKeyUp(e, inp.value, close);});\n\n      CodeMirror.on(inp, \"keydown\", function(e) {\n        if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }\n        if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {\n          inp.blur();\n          CodeMirror.e_stop(e);\n          close();\n        }\n        if (e.keyCode == 13) callback(inp.value, e);\n      });\n\n      if (options.closeOnBlur !== false) CodeMirror.on(dialog, \"focusout\", function (evt) {\n        if (evt.relatedTarget !== null) close();\n      });\n    } else if (button = dialog.getElementsByTagName(\"button\")[0]) {\n      CodeMirror.on(button, \"click\", function() {\n        close();\n        me.focus();\n      });\n\n      if (options.closeOnBlur !== false) CodeMirror.on(button, \"blur\", close);\n\n      button.focus();\n    }\n    return close;\n  });\n\n  CodeMirror.defineExtension(\"openConfirm\", function(template, callbacks, options) {\n    closeNotification(this, null);\n    var dialog = dialogDiv(this, template, options && options.bottom);\n    var buttons = dialog.getElementsByTagName(\"button\");\n    var closed = false, me = this, blurring = 1;\n    function close() {\n      if (closed) return;\n      closed = true;\n      CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');\n      dialog.parentNode.removeChild(dialog);\n      me.focus();\n    }\n    buttons[0].focus();\n    for (var i = 0; i < buttons.length; ++i) {\n      var b = buttons[i];\n      (function(callback) {\n        CodeMirror.on(b, \"click\", function(e) {\n          CodeMirror.e_preventDefault(e);\n          close();\n          if (callback) callback(me);\n        });\n      })(callbacks[i]);\n      CodeMirror.on(b, \"blur\", function() {\n        --blurring;\n        setTimeout(function() { if (blurring <= 0) close(); }, 200);\n      });\n      CodeMirror.on(b, \"focus\", function() { ++blurring; });\n    }\n  });\n\n  /*\n   * openNotification\n   * Opens a notification, that can be closed with an optional timer\n   * (default 5000ms timer) and always closes on click.\n   *\n   * If a notification is opened while another is opened, it will close the\n   * currently opened one and open the new one immediately.\n   */\n  CodeMirror.defineExtension(\"openNotification\", function(template, options) {\n    closeNotification(this, close);\n    var dialog = dialogDiv(this, template, options && options.bottom);\n    var closed = false, doneTimer;\n    var duration = options && typeof options.duration !== \"undefined\" ? options.duration : 5000;\n\n    function close() {\n      if (closed) return;\n      closed = true;\n      clearTimeout(doneTimer);\n      CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');\n      dialog.parentNode.removeChild(dialog);\n    }\n\n    CodeMirror.on(dialog, 'click', function(e) {\n      CodeMirror.e_preventDefault(e);\n      close();\n    });\n\n    if (duration)\n      doneTimer = setTimeout(close, duration);\n\n    return close;\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/display/autorefresh.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"))\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod)\n  else // Plain browser env\n    mod(CodeMirror)\n})(function(CodeMirror) {\n  \"use strict\"\n\n  CodeMirror.defineOption(\"autoRefresh\", false, function(cm, val) {\n    if (cm.state.autoRefresh) {\n      stopListening(cm, cm.state.autoRefresh)\n      cm.state.autoRefresh = null\n    }\n    if (val && cm.display.wrapper.offsetHeight == 0)\n      startListening(cm, cm.state.autoRefresh = {delay: val.delay || 250})\n  })\n\n  function startListening(cm, state) {\n    function check() {\n      if (cm.display.wrapper.offsetHeight) {\n        stopListening(cm, state)\n        if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight)\n          cm.refresh()\n      } else {\n        state.timeout = setTimeout(check, state.delay)\n      }\n    }\n    state.timeout = setTimeout(check, state.delay)\n    state.hurry = function() {\n      clearTimeout(state.timeout)\n      state.timeout = setTimeout(check, 50)\n    }\n    CodeMirror.on(window, \"mouseup\", state.hurry)\n    CodeMirror.on(window, \"keyup\", state.hurry)\n  }\n\n  function stopListening(_cm, state) {\n    clearTimeout(state.timeout)\n    CodeMirror.off(window, \"mouseup\", state.hurry)\n    CodeMirror.off(window, \"keyup\", state.hurry)\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/display/fullscreen.css",
    "content": ".CodeMirror-fullscreen {\n  position: fixed;\n  top: 0; left: 0; right: 0; bottom: 0;\n  height: auto;\n  z-index: 9;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/display/fullscreen.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineOption(\"fullScreen\", false, function(cm, val, old) {\n    if (old == CodeMirror.Init) old = false;\n    if (!old == !val) return;\n    if (val) setFullscreen(cm);\n    else setNormal(cm);\n  });\n\n  function setFullscreen(cm) {\n    var wrap = cm.getWrapperElement();\n    cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,\n                                  width: wrap.style.width, height: wrap.style.height};\n    wrap.style.width = \"\";\n    wrap.style.height = \"auto\";\n    wrap.className += \" CodeMirror-fullscreen\";\n    document.documentElement.style.overflow = \"hidden\";\n    cm.refresh();\n  }\n\n  function setNormal(cm) {\n    var wrap = cm.getWrapperElement();\n    wrap.className = wrap.className.replace(/\\s*CodeMirror-fullscreen\\b/, \"\");\n    document.documentElement.style.overflow = \"\";\n    var info = cm.state.fullScreenRestore;\n    wrap.style.width = info.width; wrap.style.height = info.height;\n    window.scrollTo(info.scrollLeft, info.scrollTop);\n    cm.refresh();\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/display/panel.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function (mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function (CodeMirror) {\n  CodeMirror.defineExtension(\"addPanel\", function (node, options) {\n    options = options || {};\n\n    if (!this.state.panels) initPanels(this);\n\n    var info = this.state.panels;\n    var wrapper = info.wrapper;\n    var cmWrapper = this.getWrapperElement();\n    var replace = options.replace instanceof Panel && !options.replace.cleared;\n\n    if (options.after instanceof Panel && !options.after.cleared) {\n      wrapper.insertBefore(node, options.before.node.nextSibling);\n    } else if (options.before instanceof Panel && !options.before.cleared) {\n      wrapper.insertBefore(node, options.before.node);\n    } else if (replace) {\n      wrapper.insertBefore(node, options.replace.node);\n      options.replace.clear(true);\n    } else if (options.position == \"bottom\") {\n      wrapper.appendChild(node);\n    } else if (options.position == \"before-bottom\") {\n      wrapper.insertBefore(node, cmWrapper.nextSibling);\n    } else if (options.position == \"after-top\") {\n      wrapper.insertBefore(node, cmWrapper);\n    } else {\n      wrapper.insertBefore(node, wrapper.firstChild);\n    }\n\n    var height = (options && options.height) || node.offsetHeight;\n\n    var panel = new Panel(this, node, options, height);\n    info.panels.push(panel);\n\n    this.setSize();\n    if (options.stable && isAtTop(this, node))\n      this.scrollTo(null, this.getScrollInfo().top + height);\n\n    return panel;\n  });\n\n  function Panel(cm, node, options, height) {\n    this.cm = cm;\n    this.node = node;\n    this.options = options;\n    this.height = height;\n    this.cleared = false;\n  }\n\n  /* when skipRemove is true, clear() was called from addPanel().\n   * Thus removePanels() should not be called (issue 5518) */\n  Panel.prototype.clear = function (skipRemove) {\n    if (this.cleared) return;\n    this.cleared = true;\n    var info = this.cm.state.panels;\n    info.panels.splice(info.panels.indexOf(this), 1);\n    this.cm.setSize();\n    if (this.options.stable && isAtTop(this.cm, this.node))\n      this.cm.scrollTo(null, this.cm.getScrollInfo().top - this.height)\n    info.wrapper.removeChild(this.node);\n    if (info.panels.length == 0 && !skipRemove) removePanels(this.cm);\n  };\n\n  Panel.prototype.changed = function () {\n    this.height = this.node.getBoundingClientRect().height;\n    this.cm.setSize();\n  };\n\n  function initPanels(cm) {\n    var wrap = cm.getWrapperElement()\n    var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle;\n    var height = parseInt(style.height);\n    var info = cm.state.panels = {\n      setHeight: wrap.style.height,\n      panels: [],\n      wrapper: document.createElement(\"div\")\n    };\n    var hasFocus = cm.hasFocus(), scrollPos = cm.getScrollInfo()\n    wrap.parentNode.insertBefore(info.wrapper, wrap);\n    info.wrapper.appendChild(wrap);\n    cm.scrollTo(scrollPos.left, scrollPos.top)\n    if (hasFocus) cm.focus();\n\n    cm._setSize = cm.setSize;\n    if (height != null) cm.setSize = function (width, newHeight) {\n      if (!newHeight) newHeight = info.wrapper.offsetHeight;\n      info.setHeight = newHeight;\n      if (typeof newHeight != \"number\") {\n        var px = /^(\\d+\\.?\\d*)px$/.exec(newHeight);\n        if (px) {\n          newHeight = Number(px[1]);\n        } else {\n          info.wrapper.style.height = newHeight;\n          newHeight = info.wrapper.offsetHeight;\n        }\n      }\n      var editorheight = newHeight - info.panels\n        .map(function (p) { return p.node.getBoundingClientRect().height; })\n        .reduce(function (a, b) { return a + b; }, 0);\n      cm._setSize(width, editorheight);\n      height = newHeight;\n    };\n  }\n\n  function removePanels(cm) {\n    var info = cm.state.panels;\n    cm.state.panels = null;\n\n    var wrap = cm.getWrapperElement()\n    var hasFocus = cm.hasFocus(), scrollPos = cm.getScrollInfo()\n    info.wrapper.parentNode.replaceChild(wrap, info.wrapper);\n    cm.scrollTo(scrollPos.left, scrollPos.top)\n    if (hasFocus) cm.focus();\n    wrap.style.height = info.setHeight;\n    cm.setSize = cm._setSize;\n    cm.setSize();\n  }\n\n  function isAtTop(cm, dom) {\n    for (var sibling = dom.nextSibling; sibling; sibling = sibling.nextSibling)\n      if (sibling == cm.getWrapperElement()) return true\n    return false\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/display/placeholder.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  CodeMirror.defineOption(\"placeholder\", \"\", function(cm, val, old) {\n    var prev = old && old != CodeMirror.Init;\n    if (val && !prev) {\n      cm.on(\"blur\", onBlur);\n      cm.on(\"change\", onChange);\n      cm.on(\"swapDoc\", onChange);\n      CodeMirror.on(cm.getInputField(), \"compositionupdate\", cm.state.placeholderCompose = function() { onComposition(cm) })\n      onChange(cm);\n    } else if (!val && prev) {\n      cm.off(\"blur\", onBlur);\n      cm.off(\"change\", onChange);\n      cm.off(\"swapDoc\", onChange);\n      CodeMirror.off(cm.getInputField(), \"compositionupdate\", cm.state.placeholderCompose)\n      clearPlaceholder(cm);\n      var wrapper = cm.getWrapperElement();\n      wrapper.className = wrapper.className.replace(\" CodeMirror-empty\", \"\");\n    }\n\n    if (val && !cm.hasFocus()) onBlur(cm);\n  });\n\n  function clearPlaceholder(cm) {\n    if (cm.state.placeholder) {\n      cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);\n      cm.state.placeholder = null;\n    }\n  }\n  function setPlaceholder(cm) {\n    clearPlaceholder(cm);\n    var elt = cm.state.placeholder = document.createElement(\"pre\");\n    elt.style.cssText = \"height: 0; overflow: visible\";\n    elt.style.direction = cm.getOption(\"direction\");\n    elt.className = \"CodeMirror-placeholder CodeMirror-line-like\";\n    var placeHolder = cm.getOption(\"placeholder\")\n    if (typeof placeHolder == \"string\") placeHolder = document.createTextNode(placeHolder)\n    elt.appendChild(placeHolder)\n    cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);\n  }\n\n  function onComposition(cm) {\n    setTimeout(function() {\n      var empty = false\n      if (cm.lineCount() == 1) {\n        var input = cm.getInputField()\n        empty = input.nodeName == \"TEXTAREA\" ? !cm.getLine(0).length\n          : !/[^\\u200b]/.test(input.querySelector(\".CodeMirror-line\").textContent)\n      }\n      if (empty) setPlaceholder(cm)\n      else clearPlaceholder(cm)\n    }, 20)\n  }\n\n  function onBlur(cm) {\n    if (isEmpty(cm)) setPlaceholder(cm);\n  }\n  function onChange(cm) {\n    var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);\n    wrapper.className = wrapper.className.replace(\" CodeMirror-empty\", \"\") + (empty ? \" CodeMirror-empty\" : \"\");\n\n    if (empty) setPlaceholder(cm);\n    else clearPlaceholder(cm);\n  }\n\n  function isEmpty(cm) {\n    return (cm.lineCount() === 1) && (cm.getLine(0) === \"\");\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/display/rulers.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineOption(\"rulers\", false, function(cm, val) {\n    if (cm.state.rulerDiv) {\n      cm.state.rulerDiv.parentElement.removeChild(cm.state.rulerDiv)\n      cm.state.rulerDiv = null\n      cm.off(\"refresh\", drawRulers)\n    }\n    if (val && val.length) {\n      cm.state.rulerDiv = cm.display.lineSpace.parentElement.insertBefore(document.createElement(\"div\"), cm.display.lineSpace)\n      cm.state.rulerDiv.className = \"CodeMirror-rulers\"\n      drawRulers(cm)\n      cm.on(\"refresh\", drawRulers)\n    }\n  });\n\n  function drawRulers(cm) {\n    cm.state.rulerDiv.textContent = \"\"\n    var val = cm.getOption(\"rulers\");\n    var cw = cm.defaultCharWidth();\n    var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), \"div\").left;\n    cm.state.rulerDiv.style.minHeight = (cm.display.scroller.offsetHeight + 30) + \"px\";\n    for (var i = 0; i < val.length; i++) {\n      var elt = document.createElement(\"div\");\n      elt.className = \"CodeMirror-ruler\";\n      var col, conf = val[i];\n      if (typeof conf == \"number\") {\n        col = conf;\n      } else {\n        col = conf.column;\n        if (conf.className) elt.className += \" \" + conf.className;\n        if (conf.color) elt.style.borderColor = conf.color;\n        if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle;\n        if (conf.width) elt.style.borderLeftWidth = conf.width;\n      }\n      elt.style.left = (left + col * cw) + \"px\";\n      cm.state.rulerDiv.appendChild(elt)\n    }\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/edit/closebrackets.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  var defaults = {\n    pairs: \"()[]{}''\\\"\\\"\",\n    closeBefore: \")]}'\\\":;>\",\n    triples: \"\",\n    explode: \"[]{}\"\n  };\n\n  var Pos = CodeMirror.Pos;\n\n  CodeMirror.defineOption(\"autoCloseBrackets\", false, function(cm, val, old) {\n    if (old && old != CodeMirror.Init) {\n      cm.removeKeyMap(keyMap);\n      cm.state.closeBrackets = null;\n    }\n    if (val) {\n      ensureBound(getOption(val, \"pairs\"))\n      cm.state.closeBrackets = val;\n      cm.addKeyMap(keyMap);\n    }\n  });\n\n  function getOption(conf, name) {\n    if (name == \"pairs\" && typeof conf == \"string\") return conf;\n    if (typeof conf == \"object\" && conf[name] != null) return conf[name];\n    return defaults[name];\n  }\n\n  var keyMap = {Backspace: handleBackspace, Enter: handleEnter};\n  function ensureBound(chars) {\n    for (var i = 0; i < chars.length; i++) {\n      var ch = chars.charAt(i), key = \"'\" + ch + \"'\"\n      if (!keyMap[key]) keyMap[key] = handler(ch)\n    }\n  }\n  ensureBound(defaults.pairs + \"`\")\n\n  function handler(ch) {\n    return function(cm) { return handleChar(cm, ch); };\n  }\n\n  function getConfig(cm) {\n    var deflt = cm.state.closeBrackets;\n    if (!deflt || deflt.override) return deflt;\n    var mode = cm.getModeAt(cm.getCursor());\n    return mode.closeBrackets || deflt;\n  }\n\n  function handleBackspace(cm) {\n    var conf = getConfig(cm);\n    if (!conf || cm.getOption(\"disableInput\")) return CodeMirror.Pass;\n\n    var pairs = getOption(conf, \"pairs\");\n    var ranges = cm.listSelections();\n    for (var i = 0; i < ranges.length; i++) {\n      if (!ranges[i].empty()) return CodeMirror.Pass;\n      var around = charsAround(cm, ranges[i].head);\n      if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;\n    }\n    for (var i = ranges.length - 1; i >= 0; i--) {\n      var cur = ranges[i].head;\n      cm.replaceRange(\"\", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), \"+delete\");\n    }\n  }\n\n  function handleEnter(cm) {\n    var conf = getConfig(cm);\n    var explode = conf && getOption(conf, \"explode\");\n    if (!explode || cm.getOption(\"disableInput\")) return CodeMirror.Pass;\n\n    var ranges = cm.listSelections();\n    for (var i = 0; i < ranges.length; i++) {\n      if (!ranges[i].empty()) return CodeMirror.Pass;\n      var around = charsAround(cm, ranges[i].head);\n      if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;\n    }\n    cm.operation(function() {\n      var linesep = cm.lineSeparator() || \"\\n\";\n      cm.replaceSelection(linesep + linesep, null);\n      moveSel(cm, -1)\n      ranges = cm.listSelections();\n      for (var i = 0; i < ranges.length; i++) {\n        var line = ranges[i].head.line;\n        cm.indentLine(line, null, true);\n        cm.indentLine(line + 1, null, true);\n      }\n    });\n  }\n\n  function moveSel(cm, dir) {\n    var newRanges = [], ranges = cm.listSelections(), primary = 0\n    for (var i = 0; i < ranges.length; i++) {\n      var range = ranges[i]\n      if (range.head == cm.getCursor()) primary = i\n      var pos = range.head.ch || dir > 0 ? {line: range.head.line, ch: range.head.ch + dir} : {line: range.head.line - 1}\n      newRanges.push({anchor: pos, head: pos})\n    }\n    cm.setSelections(newRanges, primary)\n  }\n\n  function contractSelection(sel) {\n    var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;\n    return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),\n            head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};\n  }\n\n  function handleChar(cm, ch) {\n    var conf = getConfig(cm);\n    if (!conf || cm.getOption(\"disableInput\")) return CodeMirror.Pass;\n\n    var pairs = getOption(conf, \"pairs\");\n    var pos = pairs.indexOf(ch);\n    if (pos == -1) return CodeMirror.Pass;\n\n    var closeBefore = getOption(conf,\"closeBefore\");\n\n    var triples = getOption(conf, \"triples\");\n\n    var identical = pairs.charAt(pos + 1) == ch;\n    var ranges = cm.listSelections();\n    var opening = pos % 2 == 0;\n\n    var type;\n    for (var i = 0; i < ranges.length; i++) {\n      var range = ranges[i], cur = range.head, curType;\n      var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));\n      if (opening && !range.empty()) {\n        curType = \"surround\";\n      } else if ((identical || !opening) && next == ch) {\n        if (identical && stringStartsAfter(cm, cur))\n          curType = \"both\";\n        else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)\n          curType = \"skipThree\";\n        else\n          curType = \"skip\";\n      } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&\n                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {\n        if (cur.ch > 2 && /\\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;\n        curType = \"addFour\";\n      } else if (identical) {\n        var prev = cur.ch == 0 ? \" \" : cm.getRange(Pos(cur.line, cur.ch - 1), cur)\n        if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = \"both\";\n        else return CodeMirror.Pass;\n      } else if (opening && (next.length === 0 || /\\s/.test(next) || closeBefore.indexOf(next) > -1)) {\n        curType = \"both\";\n      } else {\n        return CodeMirror.Pass;\n      }\n      if (!type) type = curType;\n      else if (type != curType) return CodeMirror.Pass;\n    }\n\n    var left = pos % 2 ? pairs.charAt(pos - 1) : ch;\n    var right = pos % 2 ? ch : pairs.charAt(pos + 1);\n    cm.operation(function() {\n      if (type == \"skip\") {\n        moveSel(cm, 1)\n      } else if (type == \"skipThree\") {\n        moveSel(cm, 3)\n      } else if (type == \"surround\") {\n        var sels = cm.getSelections();\n        for (var i = 0; i < sels.length; i++)\n          sels[i] = left + sels[i] + right;\n        cm.replaceSelections(sels, \"around\");\n        sels = cm.listSelections().slice();\n        for (var i = 0; i < sels.length; i++)\n          sels[i] = contractSelection(sels[i]);\n        cm.setSelections(sels);\n      } else if (type == \"both\") {\n        cm.replaceSelection(left + right, null);\n        cm.triggerElectric(left + right);\n        moveSel(cm, -1)\n      } else if (type == \"addFour\") {\n        cm.replaceSelection(left + left + left + left, \"before\");\n        moveSel(cm, 1)\n      }\n    });\n  }\n\n  function charsAround(cm, pos) {\n    var str = cm.getRange(Pos(pos.line, pos.ch - 1),\n                          Pos(pos.line, pos.ch + 1));\n    return str.length == 2 ? str : null;\n  }\n\n  function stringStartsAfter(cm, pos) {\n    var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))\n    return /\\bstring/.test(token.type) && token.start == pos.ch &&\n      (pos.ch == 0 || !/\\bstring/.test(cm.getTokenTypeAt(pos)))\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/edit/closetag.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/**\n * Tag-closer extension for CodeMirror.\n *\n * This extension adds an \"autoCloseTags\" option that can be set to\n * either true to get the default behavior, or an object to further\n * configure its behavior.\n *\n * These are supported options:\n *\n * `whenClosing` (default true)\n *   Whether to autoclose when the '/' of a closing tag is typed.\n * `whenOpening` (default true)\n *   Whether to autoclose the tag when the final '>' of an opening\n *   tag is typed.\n * `dontCloseTags` (default is empty tags for HTML, none for XML)\n *   An array of tag names that should not be autoclosed.\n * `indentTags` (default is block tags for HTML, none for XML)\n *   An array of tag names that should, when opened, cause a\n *   blank line to be added inside the tag, and the blank line and\n *   closing line to be indented.\n * `emptyTags` (default is none)\n *   An array of XML tag names that should be autoclosed with '/>'.\n *\n * See demos/closetag.html for a usage example.\n */\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../fold/xml-fold\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../fold/xml-fold\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  CodeMirror.defineOption(\"autoCloseTags\", false, function(cm, val, old) {\n    if (old != CodeMirror.Init && old)\n      cm.removeKeyMap(\"autoCloseTags\");\n    if (!val) return;\n    var map = {name: \"autoCloseTags\"};\n    if (typeof val != \"object\" || val.whenClosing !== false)\n      map[\"'/'\"] = function(cm) { return autoCloseSlash(cm); };\n    if (typeof val != \"object\" || val.whenOpening !== false)\n      map[\"'>'\"] = function(cm) { return autoCloseGT(cm); };\n    cm.addKeyMap(map);\n  });\n\n  var htmlDontClose = [\"area\", \"base\", \"br\", \"col\", \"command\", \"embed\", \"hr\", \"img\", \"input\", \"keygen\", \"link\", \"meta\", \"param\",\n                       \"source\", \"track\", \"wbr\"];\n  var htmlIndent = [\"applet\", \"blockquote\", \"body\", \"button\", \"div\", \"dl\", \"fieldset\", \"form\", \"frameset\", \"h1\", \"h2\", \"h3\", \"h4\",\n                    \"h5\", \"h6\", \"head\", \"html\", \"iframe\", \"layer\", \"legend\", \"object\", \"ol\", \"p\", \"select\", \"table\", \"ul\"];\n\n  function autoCloseGT(cm) {\n    if (cm.getOption(\"disableInput\")) return CodeMirror.Pass;\n    var ranges = cm.listSelections(), replacements = [];\n    var opt = cm.getOption(\"autoCloseTags\");\n    for (var i = 0; i < ranges.length; i++) {\n      if (!ranges[i].empty()) return CodeMirror.Pass;\n      var pos = ranges[i].head, tok = cm.getTokenAt(pos);\n      var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;\n      var tagInfo = inner.mode.xmlCurrentTag && inner.mode.xmlCurrentTag(state)\n      var tagName = tagInfo && tagInfo.name\n      if (!tagName) return CodeMirror.Pass\n\n      var html = inner.mode.configuration == \"html\";\n      var dontCloseTags = (typeof opt == \"object\" && opt.dontCloseTags) || (html && htmlDontClose);\n      var indentTags = (typeof opt == \"object\" && opt.indentTags) || (html && htmlIndent);\n\n      if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);\n      var lowerTagName = tagName.toLowerCase();\n      // Don't process the '>' at the end of an end-tag or self-closing tag\n      if (!tagName ||\n          tok.type == \"string\" && (tok.end != pos.ch || !/[\\\"\\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||\n          tok.type == \"tag\" && tagInfo.close ||\n          tok.string.indexOf(\"/\") == (pos.ch - tok.start - 1) || // match something like <someTagName />\n          dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||\n          closingTagExists(cm, inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state) || [], tagName, pos, true))\n        return CodeMirror.Pass;\n\n      var emptyTags = typeof opt == \"object\" && opt.emptyTags;\n      if (emptyTags && indexOf(emptyTags, tagName) > -1) {\n        replacements[i] = { text: \"/>\", newPos: CodeMirror.Pos(pos.line, pos.ch + 2) };\n        continue;\n      }\n\n      var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;\n      replacements[i] = {indent: indent,\n                         text: \">\" + (indent ? \"\\n\\n\" : \"\") + \"</\" + tagName + \">\",\n                         newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};\n    }\n\n    var dontIndentOnAutoClose = (typeof opt == \"object\" && opt.dontIndentOnAutoClose);\n    for (var i = ranges.length - 1; i >= 0; i--) {\n      var info = replacements[i];\n      cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, \"+insert\");\n      var sel = cm.listSelections().slice(0);\n      sel[i] = {head: info.newPos, anchor: info.newPos};\n      cm.setSelections(sel);\n      if (!dontIndentOnAutoClose && info.indent) {\n        cm.indentLine(info.newPos.line, null, true);\n        cm.indentLine(info.newPos.line + 1, null, true);\n      }\n    }\n  }\n\n  function autoCloseCurrent(cm, typingSlash) {\n    var ranges = cm.listSelections(), replacements = [];\n    var head = typingSlash ? \"/\" : \"</\";\n    var opt = cm.getOption(\"autoCloseTags\");\n    var dontIndentOnAutoClose = (typeof opt == \"object\" && opt.dontIndentOnSlash);\n    for (var i = 0; i < ranges.length; i++) {\n      if (!ranges[i].empty()) return CodeMirror.Pass;\n      var pos = ranges[i].head, tok = cm.getTokenAt(pos);\n      var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;\n      if (typingSlash && (tok.type == \"string\" || tok.string.charAt(0) != \"<\" ||\n                          tok.start != pos.ch - 1))\n        return CodeMirror.Pass;\n      // Kludge to get around the fact that we are not in XML mode\n      // when completing in JS/CSS snippet in htmlmixed mode. Does not\n      // work for other XML embedded languages (there is no general\n      // way to go from a mixed mode to its current XML state).\n      var replacement, mixed = inner.mode.name != \"xml\" && cm.getMode().name == \"htmlmixed\"\n      if (mixed && inner.mode.name == \"javascript\") {\n        replacement = head + \"script\";\n      } else if (mixed && inner.mode.name == \"css\") {\n        replacement = head + \"style\";\n      } else {\n        var context = inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state)\n        var top = context.length ? context[context.length - 1] : \"\"\n        if (!context || (context.length && closingTagExists(cm, context, top, pos)))\n          return CodeMirror.Pass;\n        replacement = head + top\n      }\n      if (cm.getLine(pos.line).charAt(tok.end) != \">\") replacement += \">\";\n      replacements[i] = replacement;\n    }\n    cm.replaceSelections(replacements);\n    ranges = cm.listSelections();\n    if (!dontIndentOnAutoClose) {\n        for (var i = 0; i < ranges.length; i++)\n            if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)\n                cm.indentLine(ranges[i].head.line);\n    }\n  }\n\n  function autoCloseSlash(cm) {\n    if (cm.getOption(\"disableInput\")) return CodeMirror.Pass;\n    return autoCloseCurrent(cm, true);\n  }\n\n  CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };\n\n  function indexOf(collection, elt) {\n    if (collection.indexOf) return collection.indexOf(elt);\n    for (var i = 0, e = collection.length; i < e; ++i)\n      if (collection[i] == elt) return i;\n    return -1;\n  }\n\n  // If xml-fold is loaded, we use its functionality to try and verify\n  // whether a given tag is actually unclosed.\n  function closingTagExists(cm, context, tagName, pos, newTag) {\n    if (!CodeMirror.scanForClosingTag) return false;\n    var end = Math.min(cm.lastLine() + 1, pos.line + 500);\n    var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);\n    if (!nextClose || nextClose.tag != tagName) return false;\n    // If the immediate wrapping context contains onCx instances of\n    // the same tag, a closing tag only exists if there are at least\n    // that many closing tags of that type following.\n    var onCx = newTag ? 1 : 0\n    for (var i = context.length - 1; i >= 0; i--) {\n      if (context[i] == tagName) ++onCx\n      else break\n    }\n    pos = nextClose.to;\n    for (var i = 1; i < onCx; i++) {\n      var next = CodeMirror.scanForClosingTag(cm, pos, null, end);\n      if (!next || next.tag != tagName) return false;\n      pos = next.to;\n    }\n    return true;\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/edit/continuelist.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var listRE = /^(\\s*)(>[> ]*|[*+-] \\[[x ]\\]\\s|[*+-]\\s|(\\d+)([.)]))(\\s*)/,\n      emptyListRE = /^(\\s*)(>[> ]*|[*+-] \\[[x ]\\]|[*+-]|(\\d+)[.)])(\\s*)$/,\n      unorderedListRE = /[*+-]\\s/;\n\n  CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {\n    if (cm.getOption(\"disableInput\")) return CodeMirror.Pass;\n    var ranges = cm.listSelections(), replacements = [];\n    for (var i = 0; i < ranges.length; i++) {\n      var pos = ranges[i].head;\n\n      // If we're not in Markdown mode, fall back to normal newlineAndIndent\n      var eolState = cm.getStateAfter(pos.line);\n      var inner = CodeMirror.innerMode(cm.getMode(), eolState);\n      if (inner.mode.name !== \"markdown\" && inner.mode.helperType !== \"markdown\") {\n        cm.execCommand(\"newlineAndIndent\");\n        return;\n      } else {\n        eolState = inner.state;\n      }\n\n      var inList = eolState.list !== false;\n      var inQuote = eolState.quote !== 0;\n\n      var line = cm.getLine(pos.line), match = listRE.exec(line);\n      var cursorBeforeBullet = /^\\s*$/.test(line.slice(0, pos.ch));\n      if (!ranges[i].empty() || (!inList && !inQuote) || !match || cursorBeforeBullet) {\n        cm.execCommand(\"newlineAndIndent\");\n        return;\n      }\n      if (emptyListRE.test(line)) {\n        var endOfQuote = inQuote && />\\s*$/.test(line)\n        var endOfList = !/>\\s*$/.test(line)\n        if (endOfQuote || endOfList) cm.replaceRange(\"\", {\n          line: pos.line, ch: 0\n        }, {\n          line: pos.line, ch: pos.ch + 1\n        });\n        replacements[i] = \"\\n\";\n      } else {\n        var indent = match[1], after = match[5];\n        var numbered = !(unorderedListRE.test(match[2]) || match[2].indexOf(\">\") >= 0);\n        var bullet = numbered ? (parseInt(match[3], 10) + 1) + match[4] : match[2].replace(\"x\", \" \");\n        replacements[i] = \"\\n\" + indent + bullet + after;\n\n        if (numbered) incrementRemainingMarkdownListNumbers(cm, pos);\n      }\n    }\n\n    cm.replaceSelections(replacements);\n  };\n\n  // Auto-updating Markdown list numbers when a new item is added to the\n  // middle of a list\n  function incrementRemainingMarkdownListNumbers(cm, pos) {\n    var startLine = pos.line, lookAhead = 0, skipCount = 0;\n    var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1];\n\n    do {\n      lookAhead += 1;\n      var nextLineNumber = startLine + lookAhead;\n      var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine);\n\n      if (nextItem) {\n        var nextIndent = nextItem[1];\n        var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount);\n        var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber;\n\n        if (startIndent === nextIndent && !isNaN(nextNumber)) {\n          if (newNumber === nextNumber) itemNumber = nextNumber + 1;\n          if (newNumber > nextNumber) itemNumber = newNumber + 1;\n          cm.replaceRange(\n            nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]),\n          {\n            line: nextLineNumber, ch: 0\n          }, {\n            line: nextLineNumber, ch: nextLine.length\n          });\n        } else {\n          if (startIndent.length > nextIndent.length) return;\n          // This doesn't run if the next line immediately indents, as it is\n          // not clear of the users intention (new indented item or same level)\n          if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return;\n          skipCount += 1;\n        }\n      }\n    } while (nextItem);\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/edit/matchbrackets.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  var ie_lt8 = /MSIE \\d/.test(navigator.userAgent) &&\n    (document.documentMode == null || document.documentMode < 8);\n\n  var Pos = CodeMirror.Pos;\n\n  var matching = {\"(\": \")>\", \")\": \"(<\", \"[\": \"]>\", \"]\": \"[<\", \"{\": \"}>\", \"}\": \"{<\", \"<\": \">>\", \">\": \"<<\"};\n\n  function bracketRegex(config) {\n    return config && config.bracketRegex || /[(){}[\\]]/\n  }\n\n  function findMatchingBracket(cm, where, config) {\n    var line = cm.getLineHandle(where.line), pos = where.ch - 1;\n    var afterCursor = config && config.afterCursor\n    if (afterCursor == null)\n      afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)\n    var re = bracketRegex(config)\n\n    // A cursor is defined as between two characters, but in in vim command mode\n    // (i.e. not insert mode), the cursor is visually represented as a\n    // highlighted box on top of the 2nd character. Otherwise, we allow matches\n    // from before or after the cursor.\n    var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) ||\n        re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)];\n    if (!match) return null;\n    var dir = match.charAt(1) == \">\" ? 1 : -1;\n    if (config && config.strict && (dir > 0) != (pos == where.ch)) return null;\n    var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));\n\n    var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, config);\n    if (found == null) return null;\n    return {from: Pos(where.line, pos), to: found && found.pos,\n            match: found && found.ch == match.charAt(0), forward: dir > 0};\n  }\n\n  // bracketRegex is used to specify which type of bracket to scan\n  // should be a regexp, e.g. /[[\\]]/\n  //\n  // Note: If \"where\" is on an open bracket, then this bracket is ignored.\n  //\n  // Returns false when no bracket was found, null when it reached\n  // maxScanLines and gave up\n  function scanForBracket(cm, where, dir, style, config) {\n    var maxScanLen = (config && config.maxScanLineLength) || 10000;\n    var maxScanLines = (config && config.maxScanLines) || 1000;\n\n    var stack = [];\n    var re = bracketRegex(config)\n    var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)\n                          : Math.max(cm.firstLine() - 1, where.line - maxScanLines);\n    for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {\n      var line = cm.getLine(lineNo);\n      if (!line) continue;\n      var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;\n      if (line.length > maxScanLen) continue;\n      if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);\n      for (; pos != end; pos += dir) {\n        var ch = line.charAt(pos);\n        if (re.test(ch) && (style === undefined ||\n                            (cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || \"\") == (style || \"\"))) {\n          var match = matching[ch];\n          if (match && (match.charAt(1) == \">\") == (dir > 0)) stack.push(ch);\n          else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};\n          else stack.pop();\n        }\n      }\n    }\n    return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;\n  }\n\n  function matchBrackets(cm, autoclear, config) {\n    // Disable brace matching in long lines, since it'll cause hugely slow updates\n    var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000,\n      highlightNonMatching = config && config.highlightNonMatching;\n    var marks = [], ranges = cm.listSelections();\n    for (var i = 0; i < ranges.length; i++) {\n      var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config);\n      if (match && (match.match || highlightNonMatching !== false) && cm.getLine(match.from.line).length <= maxHighlightLen) {\n        var style = match.match ? \"CodeMirror-matchingbracket\" : \"CodeMirror-nonmatchingbracket\";\n        marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));\n        if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)\n          marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));\n      }\n    }\n\n    if (marks.length) {\n      // Kludge to work around the IE bug from issue #1193, where text\n      // input stops going to the textarea whenever this fires.\n      if (ie_lt8 && cm.state.focused) cm.focus();\n\n      var clear = function() {\n        cm.operation(function() {\n          for (var i = 0; i < marks.length; i++) marks[i].clear();\n        });\n      };\n      if (autoclear) setTimeout(clear, 800);\n      else return clear;\n    }\n  }\n\n  function doMatchBrackets(cm) {\n    cm.operation(function() {\n      if (cm.state.matchBrackets.currentlyHighlighted) {\n        cm.state.matchBrackets.currentlyHighlighted();\n        cm.state.matchBrackets.currentlyHighlighted = null;\n      }\n      cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);\n    });\n  }\n\n  function clearHighlighted(cm) {\n    if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {\n      cm.state.matchBrackets.currentlyHighlighted();\n      cm.state.matchBrackets.currentlyHighlighted = null;\n    }\n  }\n\n  CodeMirror.defineOption(\"matchBrackets\", false, function(cm, val, old) {\n    if (old && old != CodeMirror.Init) {\n      cm.off(\"cursorActivity\", doMatchBrackets);\n      cm.off(\"focus\", doMatchBrackets)\n      cm.off(\"blur\", clearHighlighted)\n      clearHighlighted(cm);\n    }\n    if (val) {\n      cm.state.matchBrackets = typeof val == \"object\" ? val : {};\n      cm.on(\"cursorActivity\", doMatchBrackets);\n      cm.on(\"focus\", doMatchBrackets)\n      cm.on(\"blur\", clearHighlighted)\n    }\n  });\n\n  CodeMirror.defineExtension(\"matchBrackets\", function() {matchBrackets(this, true);});\n  CodeMirror.defineExtension(\"findMatchingBracket\", function(pos, config, oldConfig){\n    // Backwards-compatibility kludge\n    if (oldConfig || typeof config == \"boolean\") {\n      if (!oldConfig) {\n        config = config ? {strict: true} : null\n      } else {\n        oldConfig.strict = config\n        config = oldConfig\n      }\n    }\n    return findMatchingBracket(this, pos, config)\n  });\n  CodeMirror.defineExtension(\"scanForBracket\", function(pos, dir, style, config){\n    return scanForBracket(this, pos, dir, style, config);\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/edit/matchtags.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../fold/xml-fold\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../fold/xml-fold\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineOption(\"matchTags\", false, function(cm, val, old) {\n    if (old && old != CodeMirror.Init) {\n      cm.off(\"cursorActivity\", doMatchTags);\n      cm.off(\"viewportChange\", maybeUpdateMatch);\n      clear(cm);\n    }\n    if (val) {\n      cm.state.matchBothTags = typeof val == \"object\" && val.bothTags;\n      cm.on(\"cursorActivity\", doMatchTags);\n      cm.on(\"viewportChange\", maybeUpdateMatch);\n      doMatchTags(cm);\n    }\n  });\n\n  function clear(cm) {\n    if (cm.state.tagHit) cm.state.tagHit.clear();\n    if (cm.state.tagOther) cm.state.tagOther.clear();\n    cm.state.tagHit = cm.state.tagOther = null;\n  }\n\n  function doMatchTags(cm) {\n    cm.state.failedTagMatch = false;\n    cm.operation(function() {\n      clear(cm);\n      if (cm.somethingSelected()) return;\n      var cur = cm.getCursor(), range = cm.getViewport();\n      range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);\n      var match = CodeMirror.findMatchingTag(cm, cur, range);\n      if (!match) return;\n      if (cm.state.matchBothTags) {\n        var hit = match.at == \"open\" ? match.open : match.close;\n        if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: \"CodeMirror-matchingtag\"});\n      }\n      var other = match.at == \"close\" ? match.open : match.close;\n      if (other)\n        cm.state.tagOther = cm.markText(other.from, other.to, {className: \"CodeMirror-matchingtag\"});\n      else\n        cm.state.failedTagMatch = true;\n    });\n  }\n\n  function maybeUpdateMatch(cm) {\n    if (cm.state.failedTagMatch) doMatchTags(cm);\n  }\n\n  CodeMirror.commands.toMatchingTag = function(cm) {\n    var found = CodeMirror.findMatchingTag(cm, cm.getCursor());\n    if (found) {\n      var other = found.at == \"close\" ? found.open : found.close;\n      if (other) cm.extendSelection(other.to, other.from);\n    }\n  };\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/edit/trailingspace.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  CodeMirror.defineOption(\"showTrailingSpace\", false, function(cm, val, prev) {\n    if (prev == CodeMirror.Init) prev = false;\n    if (prev && !val)\n      cm.removeOverlay(\"trailingspace\");\n    else if (!prev && val)\n      cm.addOverlay({\n        token: function(stream) {\n          for (var l = stream.string.length, i = l; i && /\\s/.test(stream.string.charAt(i - 1)); --i) {}\n          if (i > stream.pos) { stream.pos = i; return null; }\n          stream.pos = l;\n          return \"trailingspace\";\n        },\n        name: \"trailingspace\"\n      });\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/fold/brace-fold.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nfunction bracketFolding(pairs) {\n  return function(cm, start) {\n    var line = start.line, lineText = cm.getLine(line);\n\n    function findOpening(pair) {\n      var tokenType;\n      for (var at = start.ch, pass = 0;;) {\n        var found = at <= 0 ? -1 : lineText.lastIndexOf(pair[0], at - 1);\n        if (found == -1) {\n          if (pass == 1) break;\n          pass = 1;\n          at = lineText.length;\n          continue;\n        }\n        if (pass == 1 && found < start.ch) break;\n        tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1));\n        if (!/^(comment|string)/.test(tokenType)) return {ch: found + 1, tokenType: tokenType, pair: pair};\n        at = found - 1;\n      }\n    }\n\n    function findRange(found) {\n      var count = 1, lastLine = cm.lastLine(), end, startCh = found.ch, endCh\n      outer: for (var i = line; i <= lastLine; ++i) {\n        var text = cm.getLine(i), pos = i == line ? startCh : 0;\n        for (;;) {\n          var nextOpen = text.indexOf(found.pair[0], pos), nextClose = text.indexOf(found.pair[1], pos);\n          if (nextOpen < 0) nextOpen = text.length;\n          if (nextClose < 0) nextClose = text.length;\n          pos = Math.min(nextOpen, nextClose);\n          if (pos == text.length) break;\n          if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == found.tokenType) {\n            if (pos == nextOpen) ++count;\n            else if (!--count) { end = i; endCh = pos; break outer; }\n          }\n          ++pos;\n        }\n      }\n\n      if (end == null || line == end) return null\n      return {from: CodeMirror.Pos(line, startCh),\n              to: CodeMirror.Pos(end, endCh)};\n    }\n\n    var found = []\n    for (var i = 0; i < pairs.length; i++) {\n      var open = findOpening(pairs[i])\n      if (open) found.push(open)\n    }\n    found.sort(function(a, b) { return a.ch - b.ch })\n    for (var i = 0; i < found.length; i++) {\n      var range = findRange(found[i])\n      if (range) return range\n    }\n    return null\n  }\n}\n\nCodeMirror.registerHelper(\"fold\", \"brace\", bracketFolding([[\"{\", \"}\"], [\"[\", \"]\"]]));\n\nCodeMirror.registerHelper(\"fold\", \"brace-paren\", bracketFolding([[\"{\", \"}\"], [\"[\", \"]\"], [\"(\", \")\"]]));\n\nCodeMirror.registerHelper(\"fold\", \"import\", function(cm, start) {\n  function hasImport(line) {\n    if (line < cm.firstLine() || line > cm.lastLine()) return null;\n    var start = cm.getTokenAt(CodeMirror.Pos(line, 1));\n    if (!/\\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));\n    if (start.type != \"keyword\" || start.string != \"import\") return null;\n    // Now find closing semicolon, return its position\n    for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {\n      var text = cm.getLine(i), semi = text.indexOf(\";\");\n      if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)};\n    }\n  }\n\n  var startLine = start.line, has = hasImport(startLine), prev;\n  if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1))\n    return null;\n  for (var end = has.end;;) {\n    var next = hasImport(end.line + 1);\n    if (next == null) break;\n    end = next.end;\n  }\n  return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end};\n});\n\nCodeMirror.registerHelper(\"fold\", \"include\", function(cm, start) {\n  function hasInclude(line) {\n    if (line < cm.firstLine() || line > cm.lastLine()) return null;\n    var start = cm.getTokenAt(CodeMirror.Pos(line, 1));\n    if (!/\\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));\n    if (start.type == \"meta\" && start.string.slice(0, 8) == \"#include\") return start.start + 8;\n  }\n\n  var startLine = start.line, has = hasInclude(startLine);\n  if (has == null || hasInclude(startLine - 1) != null) return null;\n  for (var end = startLine;;) {\n    var next = hasInclude(end + 1);\n    if (next == null) break;\n    ++end;\n  }\n  return {from: CodeMirror.Pos(startLine, has + 1),\n          to: cm.clipPos(CodeMirror.Pos(end))};\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/fold/comment-fold.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.registerGlobalHelper(\"fold\", \"comment\", function(mode) {\n  return mode.blockCommentStart && mode.blockCommentEnd;\n}, function(cm, start) {\n  var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd;\n  if (!startToken || !endToken) return;\n  var line = start.line, lineText = cm.getLine(line);\n\n  var startCh;\n  for (var at = start.ch, pass = 0;;) {\n    var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1);\n    if (found == -1) {\n      if (pass == 1) return;\n      pass = 1;\n      at = lineText.length;\n      continue;\n    }\n    if (pass == 1 && found < start.ch) return;\n    if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) &&\n        (found == 0 || lineText.slice(found - endToken.length, found) == endToken ||\n         !/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) {\n      startCh = found + startToken.length;\n      break;\n    }\n    at = found - 1;\n  }\n\n  var depth = 1, lastLine = cm.lastLine(), end, endCh;\n  outer: for (var i = line; i <= lastLine; ++i) {\n    var text = cm.getLine(i), pos = i == line ? startCh : 0;\n    for (;;) {\n      var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);\n      if (nextOpen < 0) nextOpen = text.length;\n      if (nextClose < 0) nextClose = text.length;\n      pos = Math.min(nextOpen, nextClose);\n      if (pos == text.length) break;\n      if (pos == nextOpen) ++depth;\n      else if (!--depth) { end = i; endCh = pos; break outer; }\n      ++pos;\n    }\n  }\n  if (end == null || line == end && endCh == startCh) return;\n  return {from: CodeMirror.Pos(line, startCh),\n          to: CodeMirror.Pos(end, endCh)};\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/fold/foldcode.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  function doFold(cm, pos, options, force) {\n    if (options && options.call) {\n      var finder = options;\n      options = null;\n    } else {\n      var finder = getOption(cm, options, \"rangeFinder\");\n    }\n    if (typeof pos == \"number\") pos = CodeMirror.Pos(pos, 0);\n    var minSize = getOption(cm, options, \"minFoldSize\");\n\n    function getRange(allowFolded) {\n      var range = finder(cm, pos);\n      if (!range || range.to.line - range.from.line < minSize) return null;\n      if (force === \"fold\") return range;\n\n      var marks = cm.findMarksAt(range.from);\n      for (var i = 0; i < marks.length; ++i) {\n        if (marks[i].__isFold) {\n          if (!allowFolded) return null;\n          range.cleared = true;\n          marks[i].clear();\n        }\n      }\n      return range;\n    }\n\n    var range = getRange(true);\n    if (getOption(cm, options, \"scanUp\")) while (!range && pos.line > cm.firstLine()) {\n      pos = CodeMirror.Pos(pos.line - 1, 0);\n      range = getRange(false);\n    }\n    if (!range || range.cleared || force === \"unfold\") return;\n\n    var myWidget = makeWidget(cm, options, range);\n    CodeMirror.on(myWidget, \"mousedown\", function(e) {\n      myRange.clear();\n      CodeMirror.e_preventDefault(e);\n    });\n    var myRange = cm.markText(range.from, range.to, {\n      replacedWith: myWidget,\n      clearOnEnter: getOption(cm, options, \"clearOnEnter\"),\n      __isFold: true\n    });\n    myRange.on(\"clear\", function(from, to) {\n      CodeMirror.signal(cm, \"unfold\", cm, from, to);\n    });\n    CodeMirror.signal(cm, \"fold\", cm, range.from, range.to);\n  }\n\n  function makeWidget(cm, options, range) {\n    var widget = getOption(cm, options, \"widget\");\n\n    if (typeof widget == \"function\") {\n      widget = widget(range.from, range.to);\n    }\n\n    if (typeof widget == \"string\") {\n      var text = document.createTextNode(widget);\n      widget = document.createElement(\"span\");\n      widget.appendChild(text);\n      widget.className = \"CodeMirror-foldmarker\";\n    } else if (widget) {\n      widget = widget.cloneNode(true)\n    }\n    return widget;\n  }\n\n  // Clumsy backwards-compatible interface\n  CodeMirror.newFoldFunction = function(rangeFinder, widget) {\n    return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };\n  };\n\n  // New-style interface\n  CodeMirror.defineExtension(\"foldCode\", function(pos, options, force) {\n    doFold(this, pos, options, force);\n  });\n\n  CodeMirror.defineExtension(\"isFolded\", function(pos) {\n    var marks = this.findMarksAt(pos);\n    for (var i = 0; i < marks.length; ++i)\n      if (marks[i].__isFold) return true;\n  });\n\n  CodeMirror.commands.toggleFold = function(cm) {\n    cm.foldCode(cm.getCursor());\n  };\n  CodeMirror.commands.fold = function(cm) {\n    cm.foldCode(cm.getCursor(), null, \"fold\");\n  };\n  CodeMirror.commands.unfold = function(cm) {\n    cm.foldCode(cm.getCursor(), { scanUp: false }, \"unfold\");\n  };\n  CodeMirror.commands.foldAll = function(cm) {\n    cm.operation(function() {\n      for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)\n        cm.foldCode(CodeMirror.Pos(i, 0), { scanUp: false }, \"fold\");\n    });\n  };\n  CodeMirror.commands.unfoldAll = function(cm) {\n    cm.operation(function() {\n      for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)\n        cm.foldCode(CodeMirror.Pos(i, 0), { scanUp: false }, \"unfold\");\n    });\n  };\n\n  CodeMirror.registerHelper(\"fold\", \"combine\", function() {\n    var funcs = Array.prototype.slice.call(arguments, 0);\n    return function(cm, start) {\n      for (var i = 0; i < funcs.length; ++i) {\n        var found = funcs[i](cm, start);\n        if (found) return found;\n      }\n    };\n  });\n\n  CodeMirror.registerHelper(\"fold\", \"auto\", function(cm, start) {\n    var helpers = cm.getHelpers(start, \"fold\");\n    for (var i = 0; i < helpers.length; i++) {\n      var cur = helpers[i](cm, start);\n      if (cur) return cur;\n    }\n  });\n\n  var defaultOptions = {\n    rangeFinder: CodeMirror.fold.auto,\n    widget: \"\\u2194\",\n    minFoldSize: 0,\n    scanUp: false,\n    clearOnEnter: true\n  };\n\n  CodeMirror.defineOption(\"foldOptions\", null);\n\n  function getOption(cm, options, name) {\n    if (options && options[name] !== undefined)\n      return options[name];\n    var editorOptions = cm.options.foldOptions;\n    if (editorOptions && editorOptions[name] !== undefined)\n      return editorOptions[name];\n    return defaultOptions[name];\n  }\n\n  CodeMirror.defineExtension(\"foldOption\", function(options, name) {\n    return getOption(this, options, name);\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/fold/foldgutter.css",
    "content": ".CodeMirror-foldmarker {\n  color: blue;\n  text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;\n  font-family: arial;\n  line-height: .3;\n  cursor: pointer;\n}\n.CodeMirror-foldgutter {\n  width: .7em;\n}\n.CodeMirror-foldgutter-open,\n.CodeMirror-foldgutter-folded {\n  cursor: pointer;\n}\n.CodeMirror-foldgutter-open:after {\n  content: \"\\25BE\";\n}\n.CodeMirror-foldgutter-folded:after {\n  content: \"\\25B8\";\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/fold/foldgutter.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"./foldcode\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"./foldcode\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineOption(\"foldGutter\", false, function(cm, val, old) {\n    if (old && old != CodeMirror.Init) {\n      cm.clearGutter(cm.state.foldGutter.options.gutter);\n      cm.state.foldGutter = null;\n      cm.off(\"gutterClick\", onGutterClick);\n      cm.off(\"changes\", onChange);\n      cm.off(\"viewportChange\", onViewportChange);\n      cm.off(\"fold\", onFold);\n      cm.off(\"unfold\", onFold);\n      cm.off(\"swapDoc\", onChange);\n      cm.off(\"optionChange\", optionChange);\n    }\n    if (val) {\n      cm.state.foldGutter = new State(parseOptions(val));\n      updateInViewport(cm);\n      cm.on(\"gutterClick\", onGutterClick);\n      cm.on(\"changes\", onChange);\n      cm.on(\"viewportChange\", onViewportChange);\n      cm.on(\"fold\", onFold);\n      cm.on(\"unfold\", onFold);\n      cm.on(\"swapDoc\", onChange);\n      cm.on(\"optionChange\", optionChange);\n    }\n  });\n\n  var Pos = CodeMirror.Pos;\n\n  function State(options) {\n    this.options = options;\n    this.from = this.to = 0;\n  }\n\n  function parseOptions(opts) {\n    if (opts === true) opts = {};\n    if (opts.gutter == null) opts.gutter = \"CodeMirror-foldgutter\";\n    if (opts.indicatorOpen == null) opts.indicatorOpen = \"CodeMirror-foldgutter-open\";\n    if (opts.indicatorFolded == null) opts.indicatorFolded = \"CodeMirror-foldgutter-folded\";\n    return opts;\n  }\n\n  function isFolded(cm, line) {\n    var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0));\n    for (var i = 0; i < marks.length; ++i) {\n      if (marks[i].__isFold) {\n        var fromPos = marks[i].find(-1);\n        if (fromPos && fromPos.line === line)\n          return marks[i];\n      }\n    }\n  }\n\n  function marker(spec) {\n    if (typeof spec == \"string\") {\n      var elt = document.createElement(\"div\");\n      elt.className = spec + \" CodeMirror-guttermarker-subtle\";\n      return elt;\n    } else {\n      return spec.cloneNode(true);\n    }\n  }\n\n  function updateFoldInfo(cm, from, to) {\n    var opts = cm.state.foldGutter.options, cur = from - 1;\n    var minSize = cm.foldOption(opts, \"minFoldSize\");\n    var func = cm.foldOption(opts, \"rangeFinder\");\n    // we can reuse the built-in indicator element if its className matches the new state\n    var clsFolded = typeof opts.indicatorFolded == \"string\" && classTest(opts.indicatorFolded);\n    var clsOpen = typeof opts.indicatorOpen == \"string\" && classTest(opts.indicatorOpen);\n    cm.eachLine(from, to, function(line) {\n      ++cur;\n      var mark = null;\n      var old = line.gutterMarkers;\n      if (old) old = old[opts.gutter];\n      if (isFolded(cm, cur)) {\n        if (clsFolded && old && clsFolded.test(old.className)) return;\n        mark = marker(opts.indicatorFolded);\n      } else {\n        var pos = Pos(cur, 0);\n        var range = func && func(cm, pos);\n        if (range && range.to.line - range.from.line >= minSize) {\n          if (clsOpen && old && clsOpen.test(old.className)) return;\n          mark = marker(opts.indicatorOpen);\n        }\n      }\n      if (!mark && !old) return;\n      cm.setGutterMarker(line, opts.gutter, mark);\n    });\n  }\n\n  // copied from CodeMirror/src/util/dom.js\n  function classTest(cls) { return new RegExp(\"(^|\\\\s)\" + cls + \"(?:$|\\\\s)\\\\s*\") }\n\n  function updateInViewport(cm) {\n    var vp = cm.getViewport(), state = cm.state.foldGutter;\n    if (!state) return;\n    cm.operation(function() {\n      updateFoldInfo(cm, vp.from, vp.to);\n    });\n    state.from = vp.from; state.to = vp.to;\n  }\n\n  function onGutterClick(cm, line, gutter) {\n    var state = cm.state.foldGutter;\n    if (!state) return;\n    var opts = state.options;\n    if (gutter != opts.gutter) return;\n    var folded = isFolded(cm, line);\n    if (folded) folded.clear();\n    else cm.foldCode(Pos(line, 0), opts);\n  }\n\n  function optionChange(cm, option) {\n    if (option == \"mode\") onChange(cm)\n  }\n\n  function onChange(cm) {\n    var state = cm.state.foldGutter;\n    if (!state) return;\n    var opts = state.options;\n    state.from = state.to = 0;\n    clearTimeout(state.changeUpdate);\n    state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);\n  }\n\n  function onViewportChange(cm) {\n    var state = cm.state.foldGutter;\n    if (!state) return;\n    var opts = state.options;\n    clearTimeout(state.changeUpdate);\n    state.changeUpdate = setTimeout(function() {\n      var vp = cm.getViewport();\n      if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {\n        updateInViewport(cm);\n      } else {\n        cm.operation(function() {\n          if (vp.from < state.from) {\n            updateFoldInfo(cm, vp.from, state.from);\n            state.from = vp.from;\n          }\n          if (vp.to > state.to) {\n            updateFoldInfo(cm, state.to, vp.to);\n            state.to = vp.to;\n          }\n        });\n      }\n    }, opts.updateViewportTimeSpan || 400);\n  }\n\n  function onFold(cm, from) {\n    var state = cm.state.foldGutter;\n    if (!state) return;\n    var line = from.line;\n    if (line >= state.from && line < state.to)\n      updateFoldInfo(cm, line, line + 1);\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/fold/indent-fold.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nfunction lineIndent(cm, lineNo) {\n  var text = cm.getLine(lineNo)\n  var spaceTo = text.search(/\\S/)\n  if (spaceTo == -1 || /\\bcomment\\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1))))\n    return -1\n  return CodeMirror.countColumn(text, null, cm.getOption(\"tabSize\"))\n}\n\nCodeMirror.registerHelper(\"fold\", \"indent\", function(cm, start) {\n  var myIndent = lineIndent(cm, start.line)\n  if (myIndent < 0) return\n  var lastLineInFold = null\n\n  // Go through lines until we find a line that definitely doesn't belong in\n  // the block we're folding, or to the end.\n  for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) {\n    var indent = lineIndent(cm, i)\n    if (indent == -1) {\n    } else if (indent > myIndent) {\n      // Lines with a greater indent are considered part of the block.\n      lastLineInFold = i;\n    } else {\n      // If this line has non-space, non-comment content, and is\n      // indented less or equal to the start line, it is the start of\n      // another block.\n      break;\n    }\n  }\n  if (lastLineInFold) return {\n    from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),\n    to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length)\n  };\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/fold/markdown-fold.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.registerHelper(\"fold\", \"markdown\", function(cm, start) {\n  var maxDepth = 100;\n\n  function isHeader(lineNo) {\n    var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0));\n    return tokentype && /\\bheader\\b/.test(tokentype);\n  }\n\n  function headerLevel(lineNo, line, nextLine) {\n    var match = line && line.match(/^#+/);\n    if (match && isHeader(lineNo)) return match[0].length;\n    match = nextLine && nextLine.match(/^[=\\-]+\\s*$/);\n    if (match && isHeader(lineNo + 1)) return nextLine[0] == \"=\" ? 1 : 2;\n    return maxDepth;\n  }\n\n  var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1);\n  var level = headerLevel(start.line, firstLine, nextLine);\n  if (level === maxDepth) return undefined;\n\n  var lastLineNo = cm.lastLine();\n  var end = start.line, nextNextLine = cm.getLine(end + 2);\n  while (end < lastLineNo) {\n    if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break;\n    ++end;\n    nextLine = nextNextLine;\n    nextNextLine = cm.getLine(end + 2);\n  }\n\n  return {\n    from: CodeMirror.Pos(start.line, firstLine.length),\n    to: CodeMirror.Pos(end, cm.getLine(end).length)\n  };\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/fold/xml-fold.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var Pos = CodeMirror.Pos;\n  function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }\n\n  var nameStartChar = \"A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD\";\n  var nameChar = nameStartChar + \"\\-\\:\\.0-9\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040\";\n  var xmlTagStart = new RegExp(\"<(/?)([\" + nameStartChar + \"][\" + nameChar + \"]*)\", \"g\");\n\n  function Iter(cm, line, ch, range) {\n    this.line = line; this.ch = ch;\n    this.cm = cm; this.text = cm.getLine(line);\n    this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();\n    this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();\n  }\n\n  function tagAt(iter, ch) {\n    var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));\n    return type && /\\btag\\b/.test(type);\n  }\n\n  function nextLine(iter) {\n    if (iter.line >= iter.max) return;\n    iter.ch = 0;\n    iter.text = iter.cm.getLine(++iter.line);\n    return true;\n  }\n  function prevLine(iter) {\n    if (iter.line <= iter.min) return;\n    iter.text = iter.cm.getLine(--iter.line);\n    iter.ch = iter.text.length;\n    return true;\n  }\n\n  function toTagEnd(iter) {\n    for (;;) {\n      var gt = iter.text.indexOf(\">\", iter.ch);\n      if (gt == -1) { if (nextLine(iter)) continue; else return; }\n      if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }\n      var lastSlash = iter.text.lastIndexOf(\"/\", gt);\n      var selfClose = lastSlash > -1 && !/\\S/.test(iter.text.slice(lastSlash + 1, gt));\n      iter.ch = gt + 1;\n      return selfClose ? \"selfClose\" : \"regular\";\n    }\n  }\n  function toTagStart(iter) {\n    for (;;) {\n      var lt = iter.ch ? iter.text.lastIndexOf(\"<\", iter.ch - 1) : -1;\n      if (lt == -1) { if (prevLine(iter)) continue; else return; }\n      if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }\n      xmlTagStart.lastIndex = lt;\n      iter.ch = lt;\n      var match = xmlTagStart.exec(iter.text);\n      if (match && match.index == lt) return match;\n    }\n  }\n\n  function toNextTag(iter) {\n    for (;;) {\n      xmlTagStart.lastIndex = iter.ch;\n      var found = xmlTagStart.exec(iter.text);\n      if (!found) { if (nextLine(iter)) continue; else return; }\n      if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }\n      iter.ch = found.index + found[0].length;\n      return found;\n    }\n  }\n  function toPrevTag(iter) {\n    for (;;) {\n      var gt = iter.ch ? iter.text.lastIndexOf(\">\", iter.ch - 1) : -1;\n      if (gt == -1) { if (prevLine(iter)) continue; else return; }\n      if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }\n      var lastSlash = iter.text.lastIndexOf(\"/\", gt);\n      var selfClose = lastSlash > -1 && !/\\S/.test(iter.text.slice(lastSlash + 1, gt));\n      iter.ch = gt + 1;\n      return selfClose ? \"selfClose\" : \"regular\";\n    }\n  }\n\n  function findMatchingClose(iter, tag) {\n    var stack = [];\n    for (;;) {\n      var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);\n      if (!next || !(end = toTagEnd(iter))) return;\n      if (end == \"selfClose\") continue;\n      if (next[1]) { // closing tag\n        for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {\n          stack.length = i;\n          break;\n        }\n        if (i < 0 && (!tag || tag == next[2])) return {\n          tag: next[2],\n          from: Pos(startLine, startCh),\n          to: Pos(iter.line, iter.ch)\n        };\n      } else { // opening tag\n        stack.push(next[2]);\n      }\n    }\n  }\n  function findMatchingOpen(iter, tag) {\n    var stack = [];\n    for (;;) {\n      var prev = toPrevTag(iter);\n      if (!prev) return;\n      if (prev == \"selfClose\") { toTagStart(iter); continue; }\n      var endLine = iter.line, endCh = iter.ch;\n      var start = toTagStart(iter);\n      if (!start) return;\n      if (start[1]) { // closing tag\n        stack.push(start[2]);\n      } else { // opening tag\n        for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {\n          stack.length = i;\n          break;\n        }\n        if (i < 0 && (!tag || tag == start[2])) return {\n          tag: start[2],\n          from: Pos(iter.line, iter.ch),\n          to: Pos(endLine, endCh)\n        };\n      }\n    }\n  }\n\n  CodeMirror.registerHelper(\"fold\", \"xml\", function(cm, start) {\n    var iter = new Iter(cm, start.line, 0);\n    for (;;) {\n      var openTag = toNextTag(iter)\n      if (!openTag || iter.line != start.line) return\n      var end = toTagEnd(iter)\n      if (!end) return\n      if (!openTag[1] && end != \"selfClose\") {\n        var startPos = Pos(iter.line, iter.ch);\n        var endPos = findMatchingClose(iter, openTag[2]);\n        return endPos && cmp(endPos.from, startPos) > 0 ? {from: startPos, to: endPos.from} : null\n      }\n    }\n  });\n  CodeMirror.findMatchingTag = function(cm, pos, range) {\n    var iter = new Iter(cm, pos.line, pos.ch, range);\n    if (iter.text.indexOf(\">\") == -1 && iter.text.indexOf(\"<\") == -1) return;\n    var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);\n    var start = end && toTagStart(iter);\n    if (!end || !start || cmp(iter, pos) > 0) return;\n    var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};\n    if (end == \"selfClose\") return {open: here, close: null, at: \"open\"};\n\n    if (start[1]) { // closing tag\n      return {open: findMatchingOpen(iter, start[2]), close: here, at: \"close\"};\n    } else { // opening tag\n      iter = new Iter(cm, to.line, to.ch, range);\n      return {open: here, close: findMatchingClose(iter, start[2]), at: \"open\"};\n    }\n  };\n\n  CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {\n    var iter = new Iter(cm, pos.line, pos.ch, range);\n    for (;;) {\n      var open = findMatchingOpen(iter, tag);\n      if (!open) break;\n      var forward = new Iter(cm, pos.line, pos.ch, range);\n      var close = findMatchingClose(forward, open.tag);\n      if (close) return {open: open, close: close};\n    }\n  };\n\n  // Used by addon/edit/closetag.js\n  CodeMirror.scanForClosingTag = function(cm, pos, name, end) {\n    var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);\n    return findMatchingClose(iter, name);\n  };\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/hint/anyword-hint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var WORD = /[\\w$]+/, RANGE = 500;\n\n  CodeMirror.registerHelper(\"hint\", \"anyword\", function(editor, options) {\n    var word = options && options.word || WORD;\n    var range = options && options.range || RANGE;\n    var cur = editor.getCursor(), curLine = editor.getLine(cur.line);\n    var end = cur.ch, start = end;\n    while (start && word.test(curLine.charAt(start - 1))) --start;\n    var curWord = start != end && curLine.slice(start, end);\n\n    var list = options && options.list || [], seen = {};\n    var re = new RegExp(word.source, \"g\");\n    for (var dir = -1; dir <= 1; dir += 2) {\n      var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;\n      for (; line != endLine; line += dir) {\n        var text = editor.getLine(line), m;\n        while (m = re.exec(text)) {\n          if (line == cur.line && m[0] === curWord) continue;\n          if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {\n            seen[m[0]] = true;\n            list.push(m[0]);\n          }\n        }\n      }\n    }\n    return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/hint/css-hint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../../mode/css/css\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../../mode/css/css\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var pseudoClasses = {\"active\":1, \"after\":1, \"before\":1, \"checked\":1, \"default\":1,\n    \"disabled\":1, \"empty\":1, \"enabled\":1, \"first-child\":1, \"first-letter\":1,\n    \"first-line\":1, \"first-of-type\":1, \"focus\":1, \"hover\":1, \"in-range\":1,\n    \"indeterminate\":1, \"invalid\":1, \"lang\":1, \"last-child\":1, \"last-of-type\":1,\n    \"link\":1, \"not\":1, \"nth-child\":1, \"nth-last-child\":1, \"nth-last-of-type\":1,\n    \"nth-of-type\":1, \"only-of-type\":1, \"only-child\":1, \"optional\":1, \"out-of-range\":1,\n    \"placeholder\":1, \"read-only\":1, \"read-write\":1, \"required\":1, \"root\":1,\n    \"selection\":1, \"target\":1, \"valid\":1, \"visited\":1\n  };\n\n  CodeMirror.registerHelper(\"hint\", \"css\", function(cm) {\n    var cur = cm.getCursor(), token = cm.getTokenAt(cur);\n    var inner = CodeMirror.innerMode(cm.getMode(), token.state);\n    if (inner.mode.name != \"css\") return;\n\n    if (token.type == \"keyword\" && \"!important\".indexOf(token.string) == 0)\n      return {list: [\"!important\"], from: CodeMirror.Pos(cur.line, token.start),\n              to: CodeMirror.Pos(cur.line, token.end)};\n\n    var start = token.start, end = cur.ch, word = token.string.slice(0, end - start);\n    if (/[^\\w$_-]/.test(word)) {\n      word = \"\"; start = end = cur.ch;\n    }\n\n    var spec = CodeMirror.resolveMode(\"text/css\");\n\n    var result = [];\n    function add(keywords) {\n      for (var name in keywords)\n        if (!word || name.lastIndexOf(word, 0) == 0)\n          result.push(name);\n    }\n\n    var st = inner.state.state;\n    if (st == \"pseudo\" || token.type == \"variable-3\") {\n      add(pseudoClasses);\n    } else if (st == \"block\" || st == \"maybeprop\") {\n      add(spec.propertyKeywords);\n    } else if (st == \"prop\" || st == \"parens\" || st == \"at\" || st == \"params\") {\n      add(spec.valueKeywords);\n      add(spec.colorKeywords);\n    } else if (st == \"media\" || st == \"media_parens\") {\n      add(spec.mediaTypes);\n      add(spec.mediaFeatures);\n    }\n\n    if (result.length) return {\n      list: result,\n      from: CodeMirror.Pos(cur.line, start),\n      to: CodeMirror.Pos(cur.line, end)\n    };\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/hint/html-hint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"./xml-hint\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"./xml-hint\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var langs = \"ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu\".split(\" \");\n  var targets = [\"_blank\", \"_self\", \"_top\", \"_parent\"];\n  var charsets = [\"ascii\", \"utf-8\", \"utf-16\", \"latin1\", \"latin1\"];\n  var methods = [\"get\", \"post\", \"put\", \"delete\"];\n  var encs = [\"application/x-www-form-urlencoded\", \"multipart/form-data\", \"text/plain\"];\n  var media = [\"all\", \"screen\", \"print\", \"embossed\", \"braille\", \"handheld\", \"print\", \"projection\", \"screen\", \"tty\", \"tv\", \"speech\",\n               \"3d-glasses\", \"resolution [>][<][=] [X]\", \"device-aspect-ratio: X/Y\", \"orientation:portrait\",\n               \"orientation:landscape\", \"device-height: [X]\", \"device-width: [X]\"];\n  var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags\n\n  var data = {\n    a: {\n      attrs: {\n        href: null, ping: null, type: null,\n        media: media,\n        target: targets,\n        hreflang: langs\n      }\n    },\n    abbr: s,\n    acronym: s,\n    address: s,\n    applet: s,\n    area: {\n      attrs: {\n        alt: null, coords: null, href: null, target: null, ping: null,\n        media: media, hreflang: langs, type: null,\n        shape: [\"default\", \"rect\", \"circle\", \"poly\"]\n      }\n    },\n    article: s,\n    aside: s,\n    audio: {\n      attrs: {\n        src: null, mediagroup: null,\n        crossorigin: [\"anonymous\", \"use-credentials\"],\n        preload: [\"none\", \"metadata\", \"auto\"],\n        autoplay: [\"\", \"autoplay\"],\n        loop: [\"\", \"loop\"],\n        controls: [\"\", \"controls\"]\n      }\n    },\n    b: s,\n    base: { attrs: { href: null, target: targets } },\n    basefont: s,\n    bdi: s,\n    bdo: s,\n    big: s,\n    blockquote: { attrs: { cite: null } },\n    body: s,\n    br: s,\n    button: {\n      attrs: {\n        form: null, formaction: null, name: null, value: null,\n        autofocus: [\"\", \"autofocus\"],\n        disabled: [\"\", \"autofocus\"],\n        formenctype: encs,\n        formmethod: methods,\n        formnovalidate: [\"\", \"novalidate\"],\n        formtarget: targets,\n        type: [\"submit\", \"reset\", \"button\"]\n      }\n    },\n    canvas: { attrs: { width: null, height: null } },\n    caption: s,\n    center: s,\n    cite: s,\n    code: s,\n    col: { attrs: { span: null } },\n    colgroup: { attrs: { span: null } },\n    command: {\n      attrs: {\n        type: [\"command\", \"checkbox\", \"radio\"],\n        label: null, icon: null, radiogroup: null, command: null, title: null,\n        disabled: [\"\", \"disabled\"],\n        checked: [\"\", \"checked\"]\n      }\n    },\n    data: { attrs: { value: null } },\n    datagrid: { attrs: { disabled: [\"\", \"disabled\"], multiple: [\"\", \"multiple\"] } },\n    datalist: { attrs: { data: null } },\n    dd: s,\n    del: { attrs: { cite: null, datetime: null } },\n    details: { attrs: { open: [\"\", \"open\"] } },\n    dfn: s,\n    dir: s,\n    div: s,\n    dialog: { attrs: { open: null } },\n    dl: s,\n    dt: s,\n    em: s,\n    embed: { attrs: { src: null, type: null, width: null, height: null } },\n    eventsource: { attrs: { src: null } },\n    fieldset: { attrs: { disabled: [\"\", \"disabled\"], form: null, name: null } },\n    figcaption: s,\n    figure: s,\n    font: s,\n    footer: s,\n    form: {\n      attrs: {\n        action: null, name: null,\n        \"accept-charset\": charsets,\n        autocomplete: [\"on\", \"off\"],\n        enctype: encs,\n        method: methods,\n        novalidate: [\"\", \"novalidate\"],\n        target: targets\n      }\n    },\n    frame: s,\n    frameset: s,\n    h1: s, h2: s, h3: s, h4: s, h5: s, h6: s,\n    head: {\n      attrs: {},\n      children: [\"title\", \"base\", \"link\", \"style\", \"meta\", \"script\", \"noscript\", \"command\"]\n    },\n    header: s,\n    hgroup: s,\n    hr: s,\n    html: {\n      attrs: { manifest: null },\n      children: [\"head\", \"body\"]\n    },\n    i: s,\n    iframe: {\n      attrs: {\n        src: null, srcdoc: null, name: null, width: null, height: null,\n        sandbox: [\"allow-top-navigation\", \"allow-same-origin\", \"allow-forms\", \"allow-scripts\"],\n        seamless: [\"\", \"seamless\"]\n      }\n    },\n    img: {\n      attrs: {\n        alt: null, src: null, ismap: null, usemap: null, width: null, height: null,\n        crossorigin: [\"anonymous\", \"use-credentials\"]\n      }\n    },\n    input: {\n      attrs: {\n        alt: null, dirname: null, form: null, formaction: null,\n        height: null, list: null, max: null, maxlength: null, min: null,\n        name: null, pattern: null, placeholder: null, size: null, src: null,\n        step: null, value: null, width: null,\n        accept: [\"audio/*\", \"video/*\", \"image/*\"],\n        autocomplete: [\"on\", \"off\"],\n        autofocus: [\"\", \"autofocus\"],\n        checked: [\"\", \"checked\"],\n        disabled: [\"\", \"disabled\"],\n        formenctype: encs,\n        formmethod: methods,\n        formnovalidate: [\"\", \"novalidate\"],\n        formtarget: targets,\n        multiple: [\"\", \"multiple\"],\n        readonly: [\"\", \"readonly\"],\n        required: [\"\", \"required\"],\n        type: [\"hidden\", \"text\", \"search\", \"tel\", \"url\", \"email\", \"password\", \"datetime\", \"date\", \"month\",\n               \"week\", \"time\", \"datetime-local\", \"number\", \"range\", \"color\", \"checkbox\", \"radio\",\n               \"file\", \"submit\", \"image\", \"reset\", \"button\"]\n      }\n    },\n    ins: { attrs: { cite: null, datetime: null } },\n    kbd: s,\n    keygen: {\n      attrs: {\n        challenge: null, form: null, name: null,\n        autofocus: [\"\", \"autofocus\"],\n        disabled: [\"\", \"disabled\"],\n        keytype: [\"RSA\"]\n      }\n    },\n    label: { attrs: { \"for\": null, form: null } },\n    legend: s,\n    li: { attrs: { value: null } },\n    link: {\n      attrs: {\n        href: null, type: null,\n        hreflang: langs,\n        media: media,\n        sizes: [\"all\", \"16x16\", \"16x16 32x32\", \"16x16 32x32 64x64\"]\n      }\n    },\n    map: { attrs: { name: null } },\n    mark: s,\n    menu: { attrs: { label: null, type: [\"list\", \"context\", \"toolbar\"] } },\n    meta: {\n      attrs: {\n        content: null,\n        charset: charsets,\n        name: [\"viewport\", \"application-name\", \"author\", \"description\", \"generator\", \"keywords\"],\n        \"http-equiv\": [\"content-language\", \"content-type\", \"default-style\", \"refresh\"]\n      }\n    },\n    meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } },\n    nav: s,\n    noframes: s,\n    noscript: s,\n    object: {\n      attrs: {\n        data: null, type: null, name: null, usemap: null, form: null, width: null, height: null,\n        typemustmatch: [\"\", \"typemustmatch\"]\n      }\n    },\n    ol: { attrs: { reversed: [\"\", \"reversed\"], start: null, type: [\"1\", \"a\", \"A\", \"i\", \"I\"] } },\n    optgroup: { attrs: { disabled: [\"\", \"disabled\"], label: null } },\n    option: { attrs: { disabled: [\"\", \"disabled\"], label: null, selected: [\"\", \"selected\"], value: null } },\n    output: { attrs: { \"for\": null, form: null, name: null } },\n    p: s,\n    param: { attrs: { name: null, value: null } },\n    pre: s,\n    progress: { attrs: { value: null, max: null } },\n    q: { attrs: { cite: null } },\n    rp: s,\n    rt: s,\n    ruby: s,\n    s: s,\n    samp: s,\n    script: {\n      attrs: {\n        type: [\"text/javascript\"],\n        src: null,\n        async: [\"\", \"async\"],\n        defer: [\"\", \"defer\"],\n        charset: charsets\n      }\n    },\n    section: s,\n    select: {\n      attrs: {\n        form: null, name: null, size: null,\n        autofocus: [\"\", \"autofocus\"],\n        disabled: [\"\", \"disabled\"],\n        multiple: [\"\", \"multiple\"]\n      }\n    },\n    small: s,\n    source: { attrs: { src: null, type: null, media: null } },\n    span: s,\n    strike: s,\n    strong: s,\n    style: {\n      attrs: {\n        type: [\"text/css\"],\n        media: media,\n        scoped: null\n      }\n    },\n    sub: s,\n    summary: s,\n    sup: s,\n    table: s,\n    tbody: s,\n    td: { attrs: { colspan: null, rowspan: null, headers: null } },\n    textarea: {\n      attrs: {\n        dirname: null, form: null, maxlength: null, name: null, placeholder: null,\n        rows: null, cols: null,\n        autofocus: [\"\", \"autofocus\"],\n        disabled: [\"\", \"disabled\"],\n        readonly: [\"\", \"readonly\"],\n        required: [\"\", \"required\"],\n        wrap: [\"soft\", \"hard\"]\n      }\n    },\n    tfoot: s,\n    th: { attrs: { colspan: null, rowspan: null, headers: null, scope: [\"row\", \"col\", \"rowgroup\", \"colgroup\"] } },\n    thead: s,\n    time: { attrs: { datetime: null } },\n    title: s,\n    tr: s,\n    track: {\n      attrs: {\n        src: null, label: null, \"default\": null,\n        kind: [\"subtitles\", \"captions\", \"descriptions\", \"chapters\", \"metadata\"],\n        srclang: langs\n      }\n    },\n    tt: s,\n    u: s,\n    ul: s,\n    \"var\": s,\n    video: {\n      attrs: {\n        src: null, poster: null, width: null, height: null,\n        crossorigin: [\"anonymous\", \"use-credentials\"],\n        preload: [\"auto\", \"metadata\", \"none\"],\n        autoplay: [\"\", \"autoplay\"],\n        mediagroup: [\"movie\"],\n        muted: [\"\", \"muted\"],\n        controls: [\"\", \"controls\"]\n      }\n    },\n    wbr: s\n  };\n\n  var globalAttrs = {\n    accesskey: [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\"],\n    \"class\": null,\n    contenteditable: [\"true\", \"false\"],\n    contextmenu: null,\n    dir: [\"ltr\", \"rtl\", \"auto\"],\n    draggable: [\"true\", \"false\", \"auto\"],\n    dropzone: [\"copy\", \"move\", \"link\", \"string:\", \"file:\"],\n    hidden: [\"hidden\"],\n    id: null,\n    inert: [\"inert\"],\n    itemid: null,\n    itemprop: null,\n    itemref: null,\n    itemscope: [\"itemscope\"],\n    itemtype: null,\n    lang: [\"en\", \"es\"],\n    spellcheck: [\"true\", \"false\"],\n    autocorrect: [\"true\", \"false\"],\n    autocapitalize: [\"true\", \"false\"],\n    style: null,\n    tabindex: [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\"],\n    title: null,\n    translate: [\"yes\", \"no\"],\n    onclick: null,\n    rel: [\"stylesheet\", \"alternate\", \"author\", \"bookmark\", \"help\", \"license\", \"next\", \"nofollow\", \"noreferrer\", \"prefetch\", \"prev\", \"search\", \"tag\"]\n  };\n  function populate(obj) {\n    for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr))\n      obj.attrs[attr] = globalAttrs[attr];\n  }\n\n  populate(s);\n  for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s)\n    populate(data[tag]);\n\n  CodeMirror.htmlSchema = data;\n  function htmlHint(cm, options) {\n    var local = {schemaInfo: data};\n    if (options) for (var opt in options) local[opt] = options[opt];\n    return CodeMirror.hint.xml(cm, local);\n  }\n  CodeMirror.registerHelper(\"hint\", \"html\", htmlHint);\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/hint/javascript-hint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  var Pos = CodeMirror.Pos;\n\n  function forEach(arr, f) {\n    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);\n  }\n\n  function arrayContains(arr, item) {\n    if (!Array.prototype.indexOf) {\n      var i = arr.length;\n      while (i--) {\n        if (arr[i] === item) {\n          return true;\n        }\n      }\n      return false;\n    }\n    return arr.indexOf(item) != -1;\n  }\n\n  function scriptHint(editor, keywords, getToken, options) {\n    // Find the token at the cursor\n    var cur = editor.getCursor(), token = getToken(editor, cur);\n    if (/\\b(?:string|comment)\\b/.test(token.type)) return;\n    var innerMode = CodeMirror.innerMode(editor.getMode(), token.state);\n    if (innerMode.mode.helperType === \"json\") return;\n    token.state = innerMode.state;\n\n    // If it's not a 'word-style' token, ignore the token.\n    if (!/^[\\w$_]*$/.test(token.string)) {\n      token = {start: cur.ch, end: cur.ch, string: \"\", state: token.state,\n               type: token.string == \".\" ? \"property\" : null};\n    } else if (token.end > cur.ch) {\n      token.end = cur.ch;\n      token.string = token.string.slice(0, cur.ch - token.start);\n    }\n\n    var tprop = token;\n    // If it is a property, find out what it is a property of.\n    while (tprop.type == \"property\") {\n      tprop = getToken(editor, Pos(cur.line, tprop.start));\n      if (tprop.string != \".\") return;\n      tprop = getToken(editor, Pos(cur.line, tprop.start));\n      if (!context) var context = [];\n      context.push(tprop);\n    }\n    return {list: getCompletions(token, context, keywords, options),\n            from: Pos(cur.line, token.start),\n            to: Pos(cur.line, token.end)};\n  }\n\n  function javascriptHint(editor, options) {\n    return scriptHint(editor, javascriptKeywords,\n                      function (e, cur) {return e.getTokenAt(cur);},\n                      options);\n  };\n  CodeMirror.registerHelper(\"hint\", \"javascript\", javascriptHint);\n\n  function getCoffeeScriptToken(editor, cur) {\n  // This getToken, it is for coffeescript, imitates the behavior of\n  // getTokenAt method in javascript.js, that is, returning \"property\"\n  // type and treat \".\" as independent token.\n    var token = editor.getTokenAt(cur);\n    if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {\n      token.end = token.start;\n      token.string = '.';\n      token.type = \"property\";\n    }\n    else if (/^\\.[\\w$_]*$/.test(token.string)) {\n      token.type = \"property\";\n      token.start++;\n      token.string = token.string.replace(/\\./, '');\n    }\n    return token;\n  }\n\n  function coffeescriptHint(editor, options) {\n    return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);\n  }\n  CodeMirror.registerHelper(\"hint\", \"coffeescript\", coffeescriptHint);\n\n  var stringProps = (\"charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight \" +\n                     \"toUpperCase toLowerCase split concat match replace search\").split(\" \");\n  var arrayProps = (\"length concat join splice push pop shift unshift slice reverse sort indexOf \" +\n                    \"lastIndexOf every some filter forEach map reduce reduceRight \").split(\" \");\n  var funcProps = \"prototype apply call bind\".split(\" \");\n  var javascriptKeywords = (\"break case catch class const continue debugger default delete do else export extends false finally for function \" +\n                  \"if in import instanceof new null return super switch this throw true try typeof var void while with yield\").split(\" \");\n  var coffeescriptKeywords = (\"and break catch class continue delete do else extends false finally for \" +\n                  \"if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes\").split(\" \");\n\n  function forAllProps(obj, callback) {\n    if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {\n      for (var name in obj) callback(name)\n    } else {\n      for (var o = obj; o; o = Object.getPrototypeOf(o))\n        Object.getOwnPropertyNames(o).forEach(callback)\n    }\n  }\n\n  function getCompletions(token, context, keywords, options) {\n    var found = [], start = token.string, global = options && options.globalScope || window;\n    function maybeAdd(str) {\n      if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);\n    }\n    function gatherCompletions(obj) {\n      if (typeof obj == \"string\") forEach(stringProps, maybeAdd);\n      else if (obj instanceof Array) forEach(arrayProps, maybeAdd);\n      else if (obj instanceof Function) forEach(funcProps, maybeAdd);\n      forAllProps(obj, maybeAdd)\n    }\n\n    if (context && context.length) {\n      // If this is a property, see if it belongs to some object we can\n      // find in the current environment.\n      var obj = context.pop(), base;\n      if (obj.type && obj.type.indexOf(\"variable\") === 0) {\n        if (options && options.additionalContext)\n          base = options.additionalContext[obj.string];\n        if (!options || options.useGlobalScope !== false)\n          base = base || global[obj.string];\n      } else if (obj.type == \"string\") {\n        base = \"\";\n      } else if (obj.type == \"atom\") {\n        base = 1;\n      } else if (obj.type == \"function\") {\n        if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&\n            (typeof global.jQuery == 'function'))\n          base = global.jQuery();\n        else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))\n          base = global._();\n      }\n      while (base != null && context.length)\n        base = base[context.pop().string];\n      if (base != null) gatherCompletions(base);\n    } else {\n      // If not, just look in the global object, any local scope, and optional additional-context\n      // (reading into JS mode internals to get at the local and global variables)\n      for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);\n      for (var c = token.state.context; c; c = c.prev)\n        for (var v = c.vars; v; v = v.next) maybeAdd(v.name)\n      for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);\n      if (options && options.additionalContext != null)\n        for (var key in options.additionalContext)\n          maybeAdd(key);\n      if (!options || options.useGlobalScope !== false)\n        gatherCompletions(global);\n      forEach(keywords, maybeAdd);\n    }\n    return found;\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/hint/show-hint.css",
    "content": ".CodeMirror-hints {\n  position: absolute;\n  z-index: 10;\n  overflow: hidden;\n  list-style: none;\n\n  margin: 0;\n  padding: 2px;\n\n  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);\n  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);\n  box-shadow: 2px 3px 5px rgba(0,0,0,.2);\n  border-radius: 3px;\n  border: 1px solid silver;\n\n  background: white;\n  font-size: 90%;\n  font-family: monospace;\n\n  max-height: 20em;\n  overflow-y: auto;\n  box-sizing: border-box;\n}\n\n.CodeMirror-hint {\n  margin: 0;\n  padding: 0 4px;\n  border-radius: 2px;\n  white-space: pre;\n  color: black;\n  cursor: pointer;\n}\n\nli.CodeMirror-hint-active {\n  background: #08f;\n  color: white;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/hint/show-hint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// declare global: DOMRect\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var HINT_ELEMENT_CLASS        = \"CodeMirror-hint\";\n  var ACTIVE_HINT_ELEMENT_CLASS = \"CodeMirror-hint-active\";\n\n  // This is the old interface, kept around for now to stay\n  // backwards-compatible.\n  CodeMirror.showHint = function(cm, getHints, options) {\n    if (!getHints) return cm.showHint(options);\n    if (options && options.async) getHints.async = true;\n    var newOpts = {hint: getHints};\n    if (options) for (var prop in options) newOpts[prop] = options[prop];\n    return cm.showHint(newOpts);\n  };\n\n  CodeMirror.defineExtension(\"showHint\", function(options) {\n    options = parseOptions(this, this.getCursor(\"start\"), options);\n    var selections = this.listSelections()\n    if (selections.length > 1) return;\n    // By default, don't allow completion when something is selected.\n    // A hint function can have a `supportsSelection` property to\n    // indicate that it can handle selections.\n    if (this.somethingSelected()) {\n      if (!options.hint.supportsSelection) return;\n      // Don't try with cross-line selections\n      for (var i = 0; i < selections.length; i++)\n        if (selections[i].head.line != selections[i].anchor.line) return;\n    }\n\n    if (this.state.completionActive) this.state.completionActive.close();\n    var completion = this.state.completionActive = new Completion(this, options);\n    if (!completion.options.hint) return;\n\n    CodeMirror.signal(this, \"startCompletion\", this);\n    completion.update(true);\n  });\n\n  CodeMirror.defineExtension(\"closeHint\", function() {\n    if (this.state.completionActive) this.state.completionActive.close()\n  })\n\n  function Completion(cm, options) {\n    this.cm = cm;\n    this.options = options;\n    this.widget = null;\n    this.debounce = 0;\n    this.tick = 0;\n    this.startPos = this.cm.getCursor(\"start\");\n    this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;\n\n    if (this.options.updateOnCursorActivity) {\n      var self = this;\n      cm.on(\"cursorActivity\", this.activityFunc = function() { self.cursorActivity(); });\n    }\n  }\n\n  var requestAnimationFrame = window.requestAnimationFrame || function(fn) {\n    return setTimeout(fn, 1000/60);\n  };\n  var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;\n\n  Completion.prototype = {\n    close: function() {\n      if (!this.active()) return;\n      this.cm.state.completionActive = null;\n      this.tick = null;\n      if (this.options.updateOnCursorActivity) {\n        this.cm.off(\"cursorActivity\", this.activityFunc);\n      }\n\n      if (this.widget && this.data) CodeMirror.signal(this.data, \"close\");\n      if (this.widget) this.widget.close();\n      CodeMirror.signal(this.cm, \"endCompletion\", this.cm);\n    },\n\n    active: function() {\n      return this.cm.state.completionActive == this;\n    },\n\n    pick: function(data, i) {\n      var completion = data.list[i], self = this;\n      this.cm.operation(function() {\n        if (completion.hint)\n          completion.hint(self.cm, data, completion);\n        else\n          self.cm.replaceRange(getText(completion), completion.from || data.from,\n                               completion.to || data.to, \"complete\");\n        CodeMirror.signal(data, \"pick\", completion);\n        self.cm.scrollIntoView();\n      });\n      if (this.options.closeOnPick) {\n        this.close();\n      }\n    },\n\n    cursorActivity: function() {\n      if (this.debounce) {\n        cancelAnimationFrame(this.debounce);\n        this.debounce = 0;\n      }\n\n      var identStart = this.startPos;\n      if(this.data) {\n        identStart = this.data.from;\n      }\n\n      var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);\n      if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||\n          pos.ch < identStart.ch || this.cm.somethingSelected() ||\n          (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {\n        this.close();\n      } else {\n        var self = this;\n        this.debounce = requestAnimationFrame(function() {self.update();});\n        if (this.widget) this.widget.disable();\n      }\n    },\n\n    update: function(first) {\n      if (this.tick == null) return\n      var self = this, myTick = ++this.tick\n      fetchHints(this.options.hint, this.cm, this.options, function(data) {\n        if (self.tick == myTick) self.finishUpdate(data, first)\n      })\n    },\n\n    finishUpdate: function(data, first) {\n      if (this.data) CodeMirror.signal(this.data, \"update\");\n\n      var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);\n      if (this.widget) this.widget.close();\n\n      this.data = data;\n\n      if (data && data.list.length) {\n        if (picked && data.list.length == 1) {\n          this.pick(data, 0);\n        } else {\n          this.widget = new Widget(this, data);\n          CodeMirror.signal(data, \"shown\");\n        }\n      }\n    }\n  };\n\n  function parseOptions(cm, pos, options) {\n    var editor = cm.options.hintOptions;\n    var out = {};\n    for (var prop in defaultOptions) out[prop] = defaultOptions[prop];\n    if (editor) for (var prop in editor)\n      if (editor[prop] !== undefined) out[prop] = editor[prop];\n    if (options) for (var prop in options)\n      if (options[prop] !== undefined) out[prop] = options[prop];\n    if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)\n    return out;\n  }\n\n  function getText(completion) {\n    if (typeof completion == \"string\") return completion;\n    else return completion.text;\n  }\n\n  function buildKeyMap(completion, handle) {\n    var baseMap = {\n      Up: function() {handle.moveFocus(-1);},\n      Down: function() {handle.moveFocus(1);},\n      PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},\n      PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},\n      Home: function() {handle.setFocus(0);},\n      End: function() {handle.setFocus(handle.length - 1);},\n      Enter: handle.pick,\n      Tab: handle.pick,\n      Esc: handle.close\n    };\n\n    var mac = /Mac/.test(navigator.platform);\n\n    if (mac) {\n      baseMap[\"Ctrl-P\"] = function() {handle.moveFocus(-1);};\n      baseMap[\"Ctrl-N\"] = function() {handle.moveFocus(1);};\n    }\n\n    var custom = completion.options.customKeys;\n    var ourMap = custom ? {} : baseMap;\n    function addBinding(key, val) {\n      var bound;\n      if (typeof val != \"string\")\n        bound = function(cm) { return val(cm, handle); };\n      // This mechanism is deprecated\n      else if (baseMap.hasOwnProperty(val))\n        bound = baseMap[val];\n      else\n        bound = val;\n      ourMap[key] = bound;\n    }\n    if (custom)\n      for (var key in custom) if (custom.hasOwnProperty(key))\n        addBinding(key, custom[key]);\n    var extra = completion.options.extraKeys;\n    if (extra)\n      for (var key in extra) if (extra.hasOwnProperty(key))\n        addBinding(key, extra[key]);\n    return ourMap;\n  }\n\n  function getHintElement(hintsElement, el) {\n    while (el && el != hintsElement) {\n      if (el.nodeName.toUpperCase() === \"LI\" && el.parentNode == hintsElement) return el;\n      el = el.parentNode;\n    }\n  }\n\n  function Widget(completion, data) {\n    this.id = \"cm-complete-\" + Math.floor(Math.random(1e6))\n    this.completion = completion;\n    this.data = data;\n    this.picked = false;\n    var widget = this, cm = completion.cm;\n    var ownerDocument = cm.getInputField().ownerDocument;\n    var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow;\n\n    var hints = this.hints = ownerDocument.createElement(\"ul\");\n    hints.setAttribute(\"role\", \"listbox\")\n    hints.setAttribute(\"aria-expanded\", \"true\")\n    hints.id = this.id\n    var theme = completion.cm.options.theme;\n    hints.className = \"CodeMirror-hints \" + theme;\n    this.selectedHint = data.selectedHint || 0;\n\n    var completions = data.list;\n    for (var i = 0; i < completions.length; ++i) {\n      var elt = hints.appendChild(ownerDocument.createElement(\"li\")), cur = completions[i];\n      var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? \"\" : \" \" + ACTIVE_HINT_ELEMENT_CLASS);\n      if (cur.className != null) className = cur.className + \" \" + className;\n      elt.className = className;\n      if (i == this.selectedHint) elt.setAttribute(\"aria-selected\", \"true\")\n      elt.id = this.id + \"-\" + i\n      elt.setAttribute(\"role\", \"option\")\n      if (cur.render) cur.render(elt, data, cur);\n      else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur)));\n      elt.hintId = i;\n    }\n\n    var container = completion.options.container || ownerDocument.body;\n    var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);\n    var left = pos.left, top = pos.bottom, below = true;\n    var offsetLeft = 0, offsetTop = 0;\n    if (container !== ownerDocument.body) {\n      // We offset the cursor position because left and top are relative to the offsetParent's top left corner.\n      var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1;\n      var offsetParent = isContainerPositioned ? container : container.offsetParent;\n      var offsetParentPosition = offsetParent.getBoundingClientRect();\n      var bodyPosition = ownerDocument.body.getBoundingClientRect();\n      offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft);\n      offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop);\n    }\n    hints.style.left = (left - offsetLeft) + \"px\";\n    hints.style.top = (top - offsetTop) + \"px\";\n\n    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.\n    var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth);\n    var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight);\n    container.appendChild(hints);\n    cm.getInputField().setAttribute(\"aria-autocomplete\", \"list\")\n    cm.getInputField().setAttribute(\"aria-owns\", this.id)\n    cm.getInputField().setAttribute(\"aria-activedescendant\", this.id + \"-\" + this.selectedHint)\n\n    var box = completion.options.moveOnOverlap ? hints.getBoundingClientRect() : new DOMRect();\n    var scrolls = completion.options.paddingForScrollbar ? hints.scrollHeight > hints.clientHeight + 1 : false;\n\n    // Compute in the timeout to avoid reflow on init\n    var startScroll;\n    setTimeout(function() { startScroll = cm.getScrollInfo(); });\n\n    var overlapY = box.bottom - winH;\n    if (overlapY > 0) { // Does not fit below\n      var height = box.bottom - box.top, spaceAbove = box.top - (pos.bottom - pos.top) - 2\n      if (winH - box.top < spaceAbove) { // More room at the top\n        if (height > spaceAbove) hints.style.height = (height = spaceAbove) + \"px\";\n        hints.style.top = ((top = pos.top - height) + offsetTop) + \"px\";\n        below = false;\n      } else {\n        hints.style.height = (winH - box.top - 2) + \"px\";\n      }\n    }\n    var overlapX = box.right - winW;\n    if (scrolls) overlapX += cm.display.nativeBarWidth;\n    if (overlapX > 0) {\n      if (box.right - box.left > winW) {\n        hints.style.width = (winW - 5) + \"px\";\n        overlapX -= (box.right - box.left) - winW;\n      }\n      hints.style.left = (left = Math.max(pos.left - overlapX - offsetLeft, 0)) + \"px\";\n    }\n    if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)\n      node.style.paddingRight = cm.display.nativeBarWidth + \"px\"\n\n    cm.addKeyMap(this.keyMap = buildKeyMap(completion, {\n      moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },\n      setFocus: function(n) { widget.changeActive(n); },\n      menuSize: function() { return widget.screenAmount(); },\n      length: completions.length,\n      close: function() { completion.close(); },\n      pick: function() { widget.pick(); },\n      data: data\n    }));\n\n    if (completion.options.closeOnUnfocus) {\n      var closingOnBlur;\n      cm.on(\"blur\", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });\n      cm.on(\"focus\", this.onFocus = function() { clearTimeout(closingOnBlur); });\n    }\n\n    cm.on(\"scroll\", this.onScroll = function() {\n      var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();\n      if (!startScroll) startScroll = cm.getScrollInfo();\n      var newTop = top + startScroll.top - curScroll.top;\n      var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop);\n      if (!below) point += hints.offsetHeight;\n      if (point <= editor.top || point >= editor.bottom) return completion.close();\n      hints.style.top = newTop + \"px\";\n      hints.style.left = (left + startScroll.left - curScroll.left) + \"px\";\n    });\n\n    CodeMirror.on(hints, \"dblclick\", function(e) {\n      var t = getHintElement(hints, e.target || e.srcElement);\n      if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}\n    });\n\n    CodeMirror.on(hints, \"click\", function(e) {\n      var t = getHintElement(hints, e.target || e.srcElement);\n      if (t && t.hintId != null) {\n        widget.changeActive(t.hintId);\n        if (completion.options.completeOnSingleClick) widget.pick();\n      }\n    });\n\n    CodeMirror.on(hints, \"mousedown\", function() {\n      setTimeout(function(){cm.focus();}, 20);\n    });\n\n    // The first hint doesn't need to be scrolled to on init\n    var selectedHintRange = this.getSelectedHintRange();\n    if (selectedHintRange.from !== 0 || selectedHintRange.to !== 0) {\n      this.scrollToActive();\n    }\n\n    CodeMirror.signal(data, \"select\", completions[this.selectedHint], hints.childNodes[this.selectedHint]);\n    return true;\n  }\n\n  Widget.prototype = {\n    close: function() {\n      if (this.completion.widget != this) return;\n      this.completion.widget = null;\n      if (this.hints.parentNode) this.hints.parentNode.removeChild(this.hints);\n      this.completion.cm.removeKeyMap(this.keyMap);\n      var input = this.completion.cm.getInputField()\n      input.removeAttribute(\"aria-activedescendant\")\n      input.removeAttribute(\"aria-owns\")\n\n      var cm = this.completion.cm;\n      if (this.completion.options.closeOnUnfocus) {\n        cm.off(\"blur\", this.onBlur);\n        cm.off(\"focus\", this.onFocus);\n      }\n      cm.off(\"scroll\", this.onScroll);\n    },\n\n    disable: function() {\n      this.completion.cm.removeKeyMap(this.keyMap);\n      var widget = this;\n      this.keyMap = {Enter: function() { widget.picked = true; }};\n      this.completion.cm.addKeyMap(this.keyMap);\n    },\n\n    pick: function() {\n      this.completion.pick(this.data, this.selectedHint);\n    },\n\n    changeActive: function(i, avoidWrap) {\n      if (i >= this.data.list.length)\n        i = avoidWrap ? this.data.list.length - 1 : 0;\n      else if (i < 0)\n        i = avoidWrap ? 0  : this.data.list.length - 1;\n      if (this.selectedHint == i) return;\n      var node = this.hints.childNodes[this.selectedHint];\n      if (node) {\n        node.className = node.className.replace(\" \" + ACTIVE_HINT_ELEMENT_CLASS, \"\");\n        node.removeAttribute(\"aria-selected\")\n      }\n      node = this.hints.childNodes[this.selectedHint = i];\n      node.className += \" \" + ACTIVE_HINT_ELEMENT_CLASS;\n      node.setAttribute(\"aria-selected\", \"true\")\n      this.completion.cm.getInputField().setAttribute(\"aria-activedescendant\", node.id)\n      this.scrollToActive()\n      CodeMirror.signal(this.data, \"select\", this.data.list[this.selectedHint], node);\n    },\n\n    scrollToActive: function() {\n      var selectedHintRange = this.getSelectedHintRange();\n      var node1 = this.hints.childNodes[selectedHintRange.from];\n      var node2 = this.hints.childNodes[selectedHintRange.to];\n      var firstNode = this.hints.firstChild;\n      if (node1.offsetTop < this.hints.scrollTop)\n        this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop;\n      else if (node2.offsetTop + node2.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)\n        this.hints.scrollTop = node2.offsetTop + node2.offsetHeight - this.hints.clientHeight + firstNode.offsetTop;\n    },\n\n    screenAmount: function() {\n      return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;\n    },\n\n    getSelectedHintRange: function() {\n      var margin = this.completion.options.scrollMargin || 0;\n      return {\n        from: Math.max(0, this.selectedHint - margin),\n        to: Math.min(this.data.list.length - 1, this.selectedHint + margin),\n      };\n    }\n  };\n\n  function applicableHelpers(cm, helpers) {\n    if (!cm.somethingSelected()) return helpers\n    var result = []\n    for (var i = 0; i < helpers.length; i++)\n      if (helpers[i].supportsSelection) result.push(helpers[i])\n    return result\n  }\n\n  function fetchHints(hint, cm, options, callback) {\n    if (hint.async) {\n      hint(cm, callback, options)\n    } else {\n      var result = hint(cm, options)\n      if (result && result.then) result.then(callback)\n      else callback(result)\n    }\n  }\n\n  function resolveAutoHints(cm, pos) {\n    var helpers = cm.getHelpers(pos, \"hint\"), words\n    if (helpers.length) {\n      var resolved = function(cm, callback, options) {\n        var app = applicableHelpers(cm, helpers);\n        function run(i) {\n          if (i == app.length) return callback(null)\n          fetchHints(app[i], cm, options, function(result) {\n            if (result && result.list.length > 0) callback(result)\n            else run(i + 1)\n          })\n        }\n        run(0)\n      }\n      resolved.async = true\n      resolved.supportsSelection = true\n      return resolved\n    } else if (words = cm.getHelper(cm.getCursor(), \"hintWords\")) {\n      return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }\n    } else if (CodeMirror.hint.anyword) {\n      return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }\n    } else {\n      return function() {}\n    }\n  }\n\n  CodeMirror.registerHelper(\"hint\", \"auto\", {\n    resolve: resolveAutoHints\n  });\n\n  CodeMirror.registerHelper(\"hint\", \"fromList\", function(cm, options) {\n    var cur = cm.getCursor(), token = cm.getTokenAt(cur)\n    var term, from = CodeMirror.Pos(cur.line, token.start), to = cur\n    if (token.start < cur.ch && /\\w/.test(token.string.charAt(cur.ch - token.start - 1))) {\n      term = token.string.substr(0, cur.ch - token.start)\n    } else {\n      term = \"\"\n      from = cur\n    }\n    var found = [];\n    for (var i = 0; i < options.words.length; i++) {\n      var word = options.words[i];\n      if (word.slice(0, term.length) == term)\n        found.push(word);\n    }\n\n    if (found.length) return {list: found, from: from, to: to};\n  });\n\n  CodeMirror.commands.autocomplete = CodeMirror.showHint;\n\n  var defaultOptions = {\n    hint: CodeMirror.hint.auto,\n    completeSingle: true,\n    alignWithWord: true,\n    closeCharacters: /[\\s()\\[\\]{};:>,]/,\n    closeOnPick: true,\n    closeOnUnfocus: true,\n    updateOnCursorActivity: true,\n    completeOnSingleClick: true,\n    container: null,\n    customKeys: null,\n    extraKeys: null,\n    paddingForScrollbar: true,\n    moveOnOverlap: true,\n  };\n\n  CodeMirror.defineOption(\"hintOptions\", null);\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/hint/sql-hint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../../mode/sql/sql\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../../mode/sql/sql\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var tables;\n  var defaultTable;\n  var keywords;\n  var identifierQuote;\n  var CONS = {\n    QUERY_DIV: \";\",\n    ALIAS_KEYWORD: \"AS\"\n  };\n  var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;\n\n  function isArray(val) { return Object.prototype.toString.call(val) == \"[object Array]\" }\n\n  function getModeConf(editor, field) {\n    return editor.getModeAt(editor.getCursor()).config[field] || CodeMirror.resolveMode(\"text/x-sql\")[field]\n  }\n\n  function getKeywords(editor) {\n    return getModeConf(editor, \"keywords\") || []\n  }\n\n  function getIdentifierQuote(editor) {\n    return getModeConf(editor, \"identifierQuote\") || \"`\";\n  }\n\n  function getText(item) {\n    return typeof item == \"string\" ? item : item.text;\n  }\n\n  function wrapTable(name, value) {\n    if (isArray(value)) value = {columns: value}\n    if (!value.text) value.text = name\n    return value\n  }\n\n  function parseTables(input) {\n    var result = {}\n    if (isArray(input)) {\n      for (var i = input.length - 1; i >= 0; i--) {\n        var item = input[i]\n        result[getText(item).toUpperCase()] = wrapTable(getText(item), item)\n      }\n    } else if (input) {\n      for (var name in input)\n        result[name.toUpperCase()] = wrapTable(name, input[name])\n    }\n    return result\n  }\n\n  function getTable(name) {\n    return tables[name.toUpperCase()]\n  }\n\n  function shallowClone(object) {\n    var result = {};\n    for (var key in object) if (object.hasOwnProperty(key))\n      result[key] = object[key];\n    return result;\n  }\n\n  function match(string, word) {\n    var len = string.length;\n    var sub = getText(word).substr(0, len);\n    return string.toUpperCase() === sub.toUpperCase();\n  }\n\n  function addMatches(result, search, wordlist, formatter) {\n    if (isArray(wordlist)) {\n      for (var i = 0; i < wordlist.length; i++)\n        if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))\n    } else {\n      for (var word in wordlist) if (wordlist.hasOwnProperty(word)) {\n        var val = wordlist[word]\n        if (!val || val === true)\n          val = word\n        else\n          val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text\n        if (match(search, val)) result.push(formatter(val))\n      }\n    }\n  }\n\n  function cleanName(name) {\n    // Get rid name from identifierQuote and preceding dot(.)\n    if (name.charAt(0) == \".\") {\n      name = name.substr(1);\n    }\n    // replace duplicated identifierQuotes with single identifierQuotes\n    // and remove single identifierQuotes\n    var nameParts = name.split(identifierQuote+identifierQuote);\n    for (var i = 0; i < nameParts.length; i++)\n      nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,\"g\"), \"\");\n    return nameParts.join(identifierQuote);\n  }\n\n  function insertIdentifierQuotes(name) {\n    var nameParts = getText(name).split(\".\");\n    for (var i = 0; i < nameParts.length; i++)\n      nameParts[i] = identifierQuote +\n      // duplicate identifierQuotes\n    nameParts[i].replace(new RegExp(identifierQuote,\"g\"), identifierQuote+identifierQuote) +\n      identifierQuote;\n    var escaped = nameParts.join(\".\");\n    if (typeof name == \"string\") return escaped;\n    name = shallowClone(name);\n    name.text = escaped;\n    return name;\n  }\n\n  function nameCompletion(cur, token, result, editor) {\n    // Try to complete table, column names and return start position of completion\n    var useIdentifierQuotes = false;\n    var nameParts = [];\n    var start = token.start;\n    var cont = true;\n    while (cont) {\n      cont = (token.string.charAt(0) == \".\");\n      useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote);\n\n      start = token.start;\n      nameParts.unshift(cleanName(token.string));\n\n      token = editor.getTokenAt(Pos(cur.line, token.start));\n      if (token.string == \".\") {\n        cont = true;\n        token = editor.getTokenAt(Pos(cur.line, token.start));\n      }\n    }\n\n    // Try to complete table names\n    var string = nameParts.join(\".\");\n    addMatches(result, string, tables, function(w) {\n      return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;\n    });\n\n    // Try to complete columns from defaultTable\n    addMatches(result, string, defaultTable, function(w) {\n      return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;\n    });\n\n    // Try to complete columns\n    string = nameParts.pop();\n    var table = nameParts.join(\".\");\n\n    var alias = false;\n    var aliasTable = table;\n    // Check if table is available. If not, find table by Alias\n    if (!getTable(table)) {\n      var oldTable = table;\n      table = findTableByAlias(table, editor);\n      if (table !== oldTable) alias = true;\n    }\n\n    var columns = getTable(table);\n    if (columns && columns.columns)\n      columns = columns.columns;\n\n    if (columns) {\n      addMatches(result, string, columns, function(w) {\n        var tableInsert = table;\n        if (alias == true) tableInsert = aliasTable;\n        if (typeof w == \"string\") {\n          w = tableInsert + \".\" + w;\n        } else {\n          w = shallowClone(w);\n          w.text = tableInsert + \".\" + w.text;\n        }\n        return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;\n      });\n    }\n\n    return start;\n  }\n\n  function eachWord(lineText, f) {\n    var words = lineText.split(/\\s+/)\n    for (var i = 0; i < words.length; i++)\n      if (words[i]) f(words[i].replace(/[`,;]/g, ''))\n  }\n\n  function findTableByAlias(alias, editor) {\n    var doc = editor.doc;\n    var fullQuery = doc.getValue();\n    var aliasUpperCase = alias.toUpperCase();\n    var previousWord = \"\";\n    var table = \"\";\n    var separator = [];\n    var validRange = {\n      start: Pos(0, 0),\n      end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)\n    };\n\n    //add separator\n    var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);\n    while(indexOfSeparator != -1) {\n      separator.push(doc.posFromIndex(indexOfSeparator));\n      indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);\n    }\n    separator.unshift(Pos(0, 0));\n    separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));\n\n    //find valid range\n    var prevItem = null;\n    var current = editor.getCursor()\n    for (var i = 0; i < separator.length; i++) {\n      if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {\n        validRange = {start: prevItem, end: separator[i]};\n        break;\n      }\n      prevItem = separator[i];\n    }\n\n    if (validRange.start) {\n      var query = doc.getRange(validRange.start, validRange.end, false);\n\n      for (var i = 0; i < query.length; i++) {\n        var lineText = query[i];\n        eachWord(lineText, function(word) {\n          var wordUpperCase = word.toUpperCase();\n          if (wordUpperCase === aliasUpperCase && getTable(previousWord))\n            table = previousWord;\n          if (wordUpperCase !== CONS.ALIAS_KEYWORD)\n            previousWord = word;\n        });\n        if (table) break;\n      }\n    }\n    return table;\n  }\n\n  CodeMirror.registerHelper(\"hint\", \"sql\", function(editor, options) {\n    tables = parseTables(options && options.tables)\n    var defaultTableName = options && options.defaultTable;\n    var disableKeywords = options && options.disableKeywords;\n    defaultTable = defaultTableName && getTable(defaultTableName);\n    keywords = getKeywords(editor);\n    identifierQuote = getIdentifierQuote(editor);\n\n    if (defaultTableName && !defaultTable)\n      defaultTable = findTableByAlias(defaultTableName, editor);\n\n    defaultTable = defaultTable || [];\n\n    if (defaultTable.columns)\n      defaultTable = defaultTable.columns;\n\n    var cur = editor.getCursor();\n    var result = [];\n    var token = editor.getTokenAt(cur), start, end, search;\n    if (token.end > cur.ch) {\n      token.end = cur.ch;\n      token.string = token.string.slice(0, cur.ch - token.start);\n    }\n\n    if (token.string.match(/^[.`\"'\\w@][\\w$#]*$/g)) {\n      search = token.string;\n      start = token.start;\n      end = token.end;\n    } else {\n      start = end = cur.ch;\n      search = \"\";\n    }\n    if (search.charAt(0) == \".\" || search.charAt(0) == identifierQuote) {\n      start = nameCompletion(cur, token, result, editor);\n    } else {\n      var objectOrClass = function(w, className) {\n        if (typeof w === \"object\") {\n          w.className = className;\n        } else {\n          w = { text: w, className: className };\n        }\n        return w;\n      };\n      addMatches(result, search, defaultTable, function(w) {\n        return objectOrClass(w, \"CodeMirror-hint-table CodeMirror-hint-default-table\");\n      });\n      addMatches(\n        result,\n        search,\n        tables, function(w) {\n          return objectOrClass(w, \"CodeMirror-hint-table\");\n        }\n      );\n      if (!disableKeywords)\n        addMatches(result, search, keywords, function(w) {\n          return objectOrClass(w.toUpperCase(), \"CodeMirror-hint-keyword\");\n        });\n    }\n\n    return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/hint/xml-hint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var Pos = CodeMirror.Pos;\n\n  function matches(hint, typed, matchInMiddle) {\n    if (matchInMiddle) return hint.indexOf(typed) >= 0;\n    else return hint.lastIndexOf(typed, 0) == 0;\n  }\n\n  function getHints(cm, options) {\n    var tags = options && options.schemaInfo;\n    var quote = (options && options.quoteChar) || '\"';\n    var matchInMiddle = options && options.matchInMiddle;\n    if (!tags) return;\n    var cur = cm.getCursor(), token = cm.getTokenAt(cur);\n    if (token.end > cur.ch) {\n      token.end = cur.ch;\n      token.string = token.string.slice(0, cur.ch - token.start);\n    }\n    var inner = CodeMirror.innerMode(cm.getMode(), token.state);\n    if (!inner.mode.xmlCurrentTag) return\n    var result = [], replaceToken = false, prefix;\n    var tag = /\\btag\\b/.test(token.type) && !/>$/.test(token.string);\n    var tagName = tag && /^\\w/.test(token.string), tagStart;\n\n    if (tagName) {\n      var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);\n      var tagType = /<\\/$/.test(before) ? \"close\" : /<$/.test(before) ? \"open\" : null;\n      if (tagType) tagStart = token.start - (tagType == \"close\" ? 2 : 1);\n    } else if (tag && token.string == \"<\") {\n      tagType = \"open\";\n    } else if (tag && token.string == \"</\") {\n      tagType = \"close\";\n    }\n\n    var tagInfo = inner.mode.xmlCurrentTag(inner.state)\n    if (!tag && !tagInfo || tagType) {\n      if (tagName)\n        prefix = token.string;\n      replaceToken = tagType;\n      var context = inner.mode.xmlCurrentContext ? inner.mode.xmlCurrentContext(inner.state) : []\n      var inner = context.length && context[context.length - 1]\n      var curTag = inner && tags[inner]\n      var childList = inner ? curTag && curTag.children : tags[\"!top\"];\n      if (childList && tagType != \"close\") {\n        for (var i = 0; i < childList.length; ++i) if (!prefix || matches(childList[i], prefix, matchInMiddle))\n          result.push(\"<\" + childList[i]);\n      } else if (tagType != \"close\") {\n        for (var name in tags)\n          if (tags.hasOwnProperty(name) && name != \"!top\" && name != \"!attrs\" && (!prefix || matches(name, prefix, matchInMiddle)))\n            result.push(\"<\" + name);\n      }\n      if (inner && (!prefix || tagType == \"close\" && matches(inner, prefix, matchInMiddle)))\n        result.push(\"</\" + inner + \">\");\n    } else {\n      // Attribute completion\n      var curTag = tagInfo && tags[tagInfo.name], attrs = curTag && curTag.attrs;\n      var globalAttrs = tags[\"!attrs\"];\n      if (!attrs && !globalAttrs) return;\n      if (!attrs) {\n        attrs = globalAttrs;\n      } else if (globalAttrs) { // Combine tag-local and global attributes\n        var set = {};\n        for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm];\n        for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm];\n        attrs = set;\n      }\n      if (token.type == \"string\" || token.string == \"=\") { // A value\n        var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)),\n                                 Pos(cur.line, token.type == \"string\" ? token.start : token.end));\n        var atName = before.match(/([^\\s\\u00a0=<>\\\"\\']+)=$/), atValues;\n        if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return;\n        if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget\n        if (token.type == \"string\") {\n          prefix = token.string;\n          var n = 0;\n          if (/['\"]/.test(token.string.charAt(0))) {\n            quote = token.string.charAt(0);\n            prefix = token.string.slice(1);\n            n++;\n          }\n          var len = token.string.length;\n          if (/['\"]/.test(token.string.charAt(len - 1))) {\n            quote = token.string.charAt(len - 1);\n            prefix = token.string.substr(n, len - 2);\n          }\n          if (n) { // an opening quote\n            var line = cm.getLine(cur.line);\n            if (line.length > token.end && line.charAt(token.end) == quote) token.end++; // include a closing quote\n          }\n          replaceToken = true;\n        }\n        var returnHintsFromAtValues = function(atValues) {\n          if (atValues)\n            for (var i = 0; i < atValues.length; ++i) if (!prefix || matches(atValues[i], prefix, matchInMiddle))\n              result.push(quote + atValues[i] + quote);\n          return returnHints();\n        };\n        if (atValues && atValues.then) return atValues.then(returnHintsFromAtValues);\n        return returnHintsFromAtValues(atValues);\n      } else { // An attribute name\n        if (token.type == \"attribute\") {\n          prefix = token.string;\n          replaceToken = true;\n        }\n        for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || matches(attr, prefix, matchInMiddle)))\n          result.push(attr);\n      }\n    }\n    function returnHints() {\n      return {\n        list: result,\n        from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur,\n        to: replaceToken ? Pos(cur.line, token.end) : cur\n      };\n    }\n    return returnHints();\n  }\n\n  CodeMirror.registerHelper(\"hint\", \"xml\", getHints);\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/lint/coffeescript-lint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js\n\n// declare global: coffeelint\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.registerHelper(\"lint\", \"coffeescript\", function(text) {\n  var found = [];\n  if (!window.coffeelint) {\n    if (window.console) {\n      window.console.error(\"Error: window.coffeelint not defined, CodeMirror CoffeeScript linting cannot run.\");\n    }\n    return found;\n  }\n  var parseError = function(err) {\n    var loc = err.lineNumber;\n    found.push({from: CodeMirror.Pos(loc-1, 0),\n                to: CodeMirror.Pos(loc, 0),\n                severity: err.level,\n                message: err.message});\n  };\n  try {\n    var res = coffeelint.lint(text);\n    for(var i = 0; i < res.length; i++) {\n      parseError(res[i]);\n    }\n  } catch(e) {\n    found.push({from: CodeMirror.Pos(e.location.first_line, 0),\n                to: CodeMirror.Pos(e.location.last_line, e.location.last_column),\n                severity: 'error',\n                message: e.message});\n  }\n  return found;\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/lint/css-lint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Depends on csslint.js from https://github.com/stubbornella/csslint\n\n// declare global: CSSLint\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.registerHelper(\"lint\", \"css\", function(text, options) {\n  var found = [];\n  if (!window.CSSLint) {\n    if (window.console) {\n        window.console.error(\"Error: window.CSSLint not defined, CodeMirror CSS linting cannot run.\");\n    }\n    return found;\n  }\n  var results = CSSLint.verify(text, options), messages = results.messages, message = null;\n  for ( var i = 0; i < messages.length; i++) {\n    message = messages[i];\n    var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col;\n    found.push({\n      from: CodeMirror.Pos(startLine, startCol),\n      to: CodeMirror.Pos(endLine, endCol),\n      message: message.message,\n      severity : message.type\n    });\n  }\n  return found;\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/lint/html-lint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Depends on htmlhint.js from http://htmlhint.com/js/htmlhint.js\n\n// declare global: HTMLHint\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"htmlhint\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"htmlhint\"], mod);\n  else // Plain browser env\n    mod(CodeMirror, window.HTMLHint);\n})(function(CodeMirror, HTMLHint) {\n  \"use strict\";\n\n  var defaultRules = {\n    \"tagname-lowercase\": true,\n    \"attr-lowercase\": true,\n    \"attr-value-double-quotes\": true,\n    \"doctype-first\": false,\n    \"tag-pair\": true,\n    \"spec-char-escape\": true,\n    \"id-unique\": true,\n    \"src-not-empty\": true,\n    \"attr-no-duplication\": true\n  };\n\n  CodeMirror.registerHelper(\"lint\", \"html\", function(text, options) {\n    var found = [];\n    if (HTMLHint && !HTMLHint.verify) {\n      if(typeof HTMLHint.default !== 'undefined') {\n        HTMLHint = HTMLHint.default;\n      } else {\n        HTMLHint = HTMLHint.HTMLHint;\n      }\n    }\n    if (!HTMLHint) HTMLHint = window.HTMLHint;\n    if (!HTMLHint) {\n      if (window.console) {\n          window.console.error(\"Error: HTMLHint not found, not defined on window, or not available through define/require, CodeMirror HTML linting cannot run.\");\n      }\n      return found;\n    }\n    var messages = HTMLHint.verify(text, options && options.rules || defaultRules);\n    for (var i = 0; i < messages.length; i++) {\n      var message = messages[i];\n      var startLine = message.line - 1, endLine = message.line - 1, startCol = message.col - 1, endCol = message.col;\n      found.push({\n        from: CodeMirror.Pos(startLine, startCol),\n        to: CodeMirror.Pos(endLine, endCol),\n        message: message.message,\n        severity : message.type\n      });\n    }\n    return found;\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/lint/javascript-lint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Depends on jshint.js from https://github.com/jshint/jshint\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n  // declare global: JSHINT\n\n  function validator(text, options) {\n    if (!window.JSHINT) {\n      if (window.console) {\n        window.console.error(\"Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run.\");\n      }\n      return [];\n    }\n    if (!options.indent) // JSHint error.character actually is a column index, this fixes underlining on lines using tabs for indentation\n      options.indent = 1; // JSHint default value is 4\n    JSHINT(text, options, options.globals);\n    var errors = JSHINT.data().errors, result = [];\n    if (errors) parseErrors(errors, result);\n    return result;\n  }\n\n  CodeMirror.registerHelper(\"lint\", \"javascript\", validator);\n\n  function parseErrors(errors, output) {\n    for ( var i = 0; i < errors.length; i++) {\n      var error = errors[i];\n      if (error) {\n        if (error.line <= 0) {\n          if (window.console) {\n            window.console.warn(\"Cannot display JSHint error (invalid line \" + error.line + \")\", error);\n          }\n          continue;\n        }\n\n        var start = error.character - 1, end = start + 1;\n        if (error.evidence) {\n          var index = error.evidence.substring(start).search(/.\\b/);\n          if (index > -1) {\n            end += index;\n          }\n        }\n\n        // Convert to format expected by validation service\n        var hint = {\n          message: error.reason,\n          severity: error.code ? (error.code.startsWith('W') ? \"warning\" : \"error\") : \"error\",\n          from: CodeMirror.Pos(error.line - 1, start),\n          to: CodeMirror.Pos(error.line - 1, end)\n        };\n\n        output.push(hint);\n      }\n    }\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/lint/json-lint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Depends on jsonlint.js from https://github.com/zaach/jsonlint\n\n// declare global: jsonlint\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.registerHelper(\"lint\", \"json\", function(text) {\n  var found = [];\n  if (!window.jsonlint) {\n    if (window.console) {\n      window.console.error(\"Error: window.jsonlint not defined, CodeMirror JSON linting cannot run.\");\n    }\n    return found;\n  }\n  // for jsonlint's web dist jsonlint is exported as an object with a single property parser, of which parseError\n  // is a subproperty\n  var jsonlint = window.jsonlint.parser || window.jsonlint\n  jsonlint.parseError = function(str, hash) {\n    var loc = hash.loc;\n    found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),\n                to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),\n                message: str});\n  };\n  try { jsonlint.parse(text); }\n  catch(e) {}\n  return found;\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/lint/lint.css",
    "content": "/* The lint marker gutter */\n.CodeMirror-lint-markers {\n  width: 16px;\n}\n\n.CodeMirror-lint-tooltip {\n  background-color: #ffd;\n  border: 1px solid black;\n  border-radius: 4px 4px 4px 4px;\n  color: black;\n  font-family: monospace;\n  font-size: 10pt;\n  overflow: hidden;\n  padding: 2px 5px;\n  position: fixed;\n  white-space: pre;\n  white-space: pre-wrap;\n  z-index: 100;\n  max-width: 600px;\n  opacity: 0;\n  transition: opacity .4s;\n  -moz-transition: opacity .4s;\n  -webkit-transition: opacity .4s;\n  -o-transition: opacity .4s;\n  -ms-transition: opacity .4s;\n}\n\n.CodeMirror-lint-mark {\n  background-position: left bottom;\n  background-repeat: repeat-x;\n}\n\n.CodeMirror-lint-mark-warning {\n  background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=\");\n}\n\n.CodeMirror-lint-mark-error {\n  background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==\");\n}\n\n.CodeMirror-lint-marker {\n  background-position: center center;\n  background-repeat: no-repeat;\n  cursor: pointer;\n  display: inline-block;\n  height: 16px;\n  width: 16px;\n  vertical-align: middle;\n  position: relative;\n}\n\n.CodeMirror-lint-message {\n  padding-left: 18px;\n  background-position: top left;\n  background-repeat: no-repeat;\n}\n\n.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {\n  background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=\");\n}\n\n.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {\n  background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=\");\n}\n\n.CodeMirror-lint-marker-multiple {\n  background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC\");\n  background-repeat: no-repeat;\n  background-position: right bottom;\n  width: 100%; height: 100%;\n}\n\n.CodeMirror-lint-line-error {\n  background-color: rgba(183, 76, 81, 0.08);\n}\n\n.CodeMirror-lint-line-warning {\n  background-color: rgba(255, 211, 0, 0.1);\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/lint/lint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n  var GUTTER_ID = \"CodeMirror-lint-markers\";\n  var LINT_LINE_ID = \"CodeMirror-lint-line-\";\n\n  function showTooltip(cm, e, content) {\n    var tt = document.createElement(\"div\");\n    tt.className = \"CodeMirror-lint-tooltip cm-s-\" + cm.options.theme;\n    tt.appendChild(content.cloneNode(true));\n    if (cm.state.lint.options.selfContain)\n      cm.getWrapperElement().appendChild(tt);\n    else\n      document.body.appendChild(tt);\n\n    function position(e) {\n      if (!tt.parentNode) return CodeMirror.off(document, \"mousemove\", position);\n      var top = Math.max(0, e.clientY - tt.offsetHeight - 5);\n      var left = Math.max(0, Math.min(e.clientX + 5, tt.ownerDocument.defaultView.innerWidth - tt.offsetWidth));\n      tt.style.top = top + \"px\"\n      tt.style.left = left + \"px\";\n    }\n    CodeMirror.on(document, \"mousemove\", position);\n    position(e);\n    if (tt.style.opacity != null) tt.style.opacity = 1;\n    return tt;\n  }\n  function rm(elt) {\n    if (elt.parentNode) elt.parentNode.removeChild(elt);\n  }\n  function hideTooltip(tt) {\n    if (!tt.parentNode) return;\n    if (tt.style.opacity == null) rm(tt);\n    tt.style.opacity = 0;\n    setTimeout(function() { rm(tt); }, 600);\n  }\n\n  function showTooltipFor(cm, e, content, node) {\n    var tooltip = showTooltip(cm, e, content);\n    function hide() {\n      CodeMirror.off(node, \"mouseout\", hide);\n      if (tooltip) { hideTooltip(tooltip); tooltip = null; }\n    }\n    var poll = setInterval(function() {\n      if (tooltip) for (var n = node;; n = n.parentNode) {\n        if (n && n.nodeType == 11) n = n.host;\n        if (n == document.body) return;\n        if (!n) { hide(); break; }\n      }\n      if (!tooltip) return clearInterval(poll);\n    }, 400);\n    CodeMirror.on(node, \"mouseout\", hide);\n  }\n\n  function LintState(cm, conf, hasGutter) {\n    this.marked = [];\n    if (conf instanceof Function) conf = {getAnnotations: conf};\n    if (!conf || conf === true) conf = {};\n    this.options = {};\n    this.linterOptions = conf.options || {};\n    for (var prop in defaults) this.options[prop] = defaults[prop];\n    for (var prop in conf) {\n      if (defaults.hasOwnProperty(prop)) {\n        if (conf[prop] != null) this.options[prop] = conf[prop];\n      } else if (!conf.options) {\n        this.linterOptions[prop] = conf[prop];\n      }\n    }\n    this.timeout = null;\n    this.hasGutter = hasGutter;\n    this.onMouseOver = function(e) { onMouseOver(cm, e); };\n    this.waitingFor = 0\n  }\n\n  var defaults = {\n    highlightLines: false,\n    tooltips: true,\n    delay: 500,\n    lintOnChange: true,\n    getAnnotations: null,\n    async: false,\n    selfContain: null,\n    formatAnnotation: null,\n    onUpdateLinting: null\n  }\n\n  function clearMarks(cm) {\n    var state = cm.state.lint;\n    if (state.hasGutter) cm.clearGutter(GUTTER_ID);\n    if (state.options.highlightLines) clearErrorLines(cm);\n    for (var i = 0; i < state.marked.length; ++i)\n      state.marked[i].clear();\n    state.marked.length = 0;\n  }\n\n  function clearErrorLines(cm) {\n    cm.eachLine(function(line) {\n      var has = line.wrapClass && /\\bCodeMirror-lint-line-\\w+\\b/.exec(line.wrapClass);\n      if (has) cm.removeLineClass(line, \"wrap\", has[0]);\n    })\n  }\n\n  function makeMarker(cm, labels, severity, multiple, tooltips) {\n    var marker = document.createElement(\"div\"), inner = marker;\n    marker.className = \"CodeMirror-lint-marker CodeMirror-lint-marker-\" + severity;\n    if (multiple) {\n      inner = marker.appendChild(document.createElement(\"div\"));\n      inner.className = \"CodeMirror-lint-marker CodeMirror-lint-marker-multiple\";\n    }\n\n    if (tooltips != false) CodeMirror.on(inner, \"mouseover\", function(e) {\n      showTooltipFor(cm, e, labels, inner);\n    });\n\n    return marker;\n  }\n\n  function getMaxSeverity(a, b) {\n    if (a == \"error\") return a;\n    else return b;\n  }\n\n  function groupByLine(annotations) {\n    var lines = [];\n    for (var i = 0; i < annotations.length; ++i) {\n      var ann = annotations[i], line = ann.from.line;\n      (lines[line] || (lines[line] = [])).push(ann);\n    }\n    return lines;\n  }\n\n  function annotationTooltip(ann) {\n    var severity = ann.severity;\n    if (!severity) severity = \"error\";\n    var tip = document.createElement(\"div\");\n    tip.className = \"CodeMirror-lint-message CodeMirror-lint-message-\" + severity;\n    if (typeof ann.messageHTML != 'undefined') {\n      tip.innerHTML = ann.messageHTML;\n    } else {\n      tip.appendChild(document.createTextNode(ann.message));\n    }\n    return tip;\n  }\n\n  function lintAsync(cm, getAnnotations) {\n    var state = cm.state.lint\n    var id = ++state.waitingFor\n    function abort() {\n      id = -1\n      cm.off(\"change\", abort)\n    }\n    cm.on(\"change\", abort)\n    getAnnotations(cm.getValue(), function(annotations, arg2) {\n      cm.off(\"change\", abort)\n      if (state.waitingFor != id) return\n      if (arg2 && annotations instanceof CodeMirror) annotations = arg2\n      cm.operation(function() {updateLinting(cm, annotations)})\n    }, state.linterOptions, cm);\n  }\n\n  function startLinting(cm) {\n    var state = cm.state.lint;\n    if (!state) return;\n    var options = state.options;\n    /*\n     * Passing rules in `options` property prevents JSHint (and other linters) from complaining\n     * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc.\n     */\n    var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), \"lint\");\n    if (!getAnnotations) return;\n    if (options.async || getAnnotations.async) {\n      lintAsync(cm, getAnnotations)\n    } else {\n      var annotations = getAnnotations(cm.getValue(), state.linterOptions, cm);\n      if (!annotations) return;\n      if (annotations.then) annotations.then(function(issues) {\n        cm.operation(function() {updateLinting(cm, issues)})\n      });\n      else cm.operation(function() {updateLinting(cm, annotations)})\n    }\n  }\n\n  function updateLinting(cm, annotationsNotSorted) {\n    var state = cm.state.lint;\n    if (!state) return;\n    var options = state.options;\n    clearMarks(cm);\n\n    var annotations = groupByLine(annotationsNotSorted);\n\n    for (var line = 0; line < annotations.length; ++line) {\n      var anns = annotations[line];\n      if (!anns) continue;\n\n      var maxSeverity = null;\n      var tipLabel = state.hasGutter && document.createDocumentFragment();\n\n      for (var i = 0; i < anns.length; ++i) {\n        var ann = anns[i];\n        var severity = ann.severity;\n        if (!severity) severity = \"error\";\n        maxSeverity = getMaxSeverity(maxSeverity, severity);\n\n        if (options.formatAnnotation) ann = options.formatAnnotation(ann);\n        if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));\n\n        if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {\n          className: \"CodeMirror-lint-mark CodeMirror-lint-mark-\" + severity,\n          __annotation: ann\n        }));\n      }\n      if (state.hasGutter)\n        cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, anns.length > 1,\n                                                       options.tooltips));\n\n      if (options.highlightLines)\n        cm.addLineClass(line, \"wrap\", LINT_LINE_ID + maxSeverity);\n    }\n    if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);\n  }\n\n  function onChange(cm) {\n    var state = cm.state.lint;\n    if (!state) return;\n    clearTimeout(state.timeout);\n    state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay);\n  }\n\n  function popupTooltips(cm, annotations, e) {\n    var target = e.target || e.srcElement;\n    var tooltip = document.createDocumentFragment();\n    for (var i = 0; i < annotations.length; i++) {\n      var ann = annotations[i];\n      tooltip.appendChild(annotationTooltip(ann));\n    }\n    showTooltipFor(cm, e, tooltip, target);\n  }\n\n  function onMouseOver(cm, e) {\n    var target = e.target || e.srcElement;\n    if (!/\\bCodeMirror-lint-mark-/.test(target.className)) return;\n    var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2;\n    var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, \"client\"));\n\n    var annotations = [];\n    for (var i = 0; i < spans.length; ++i) {\n      var ann = spans[i].__annotation;\n      if (ann) annotations.push(ann);\n    }\n    if (annotations.length) popupTooltips(cm, annotations, e);\n  }\n\n  CodeMirror.defineOption(\"lint\", false, function(cm, val, old) {\n    if (old && old != CodeMirror.Init) {\n      clearMarks(cm);\n      if (cm.state.lint.options.lintOnChange !== false)\n        cm.off(\"change\", onChange);\n      CodeMirror.off(cm.getWrapperElement(), \"mouseover\", cm.state.lint.onMouseOver);\n      clearTimeout(cm.state.lint.timeout);\n      delete cm.state.lint;\n    }\n\n    if (val) {\n      var gutters = cm.getOption(\"gutters\"), hasLintGutter = false;\n      for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;\n      var state = cm.state.lint = new LintState(cm, val, hasLintGutter);\n      if (state.options.lintOnChange)\n        cm.on(\"change\", onChange);\n      if (state.options.tooltips != false && state.options.tooltips != \"gutter\")\n        CodeMirror.on(cm.getWrapperElement(), \"mouseover\", state.onMouseOver);\n\n      startLinting(cm);\n    }\n  });\n\n  CodeMirror.defineExtension(\"performLint\", function() {\n    startLinting(this);\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/lint/yaml-lint.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\n// Depends on js-yaml.js from https://github.com/nodeca/js-yaml\n\n// declare global: jsyaml\n\nCodeMirror.registerHelper(\"lint\", \"yaml\", function(text) {\n  var found = [];\n  if (!window.jsyaml) {\n    if (window.console) {\n      window.console.error(\"Error: window.jsyaml not defined, CodeMirror YAML linting cannot run.\");\n    }\n    return found;\n  }\n  try { jsyaml.loadAll(text); }\n  catch(e) {\n      var loc = e.mark,\n          // js-yaml YAMLException doesn't always provide an accurate lineno\n          // e.g., when there are multiple yaml docs\n          // ---\n          // ---\n          // foo:bar\n          from = loc ? CodeMirror.Pos(loc.line, loc.column) : CodeMirror.Pos(0, 0),\n          to = from;\n      found.push({ from: from, to: to, message: e.message });\n  }\n  return found;\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/merge/merge.css",
    "content": ".CodeMirror-merge {\n  position: relative;\n  border: 1px solid #ddd;\n  white-space: pre;\n}\n\n.CodeMirror-merge, .CodeMirror-merge .CodeMirror {\n  height: 350px;\n}\n\n.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; }\n.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; }\n.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; }\n.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; }\n\n.CodeMirror-merge-pane {\n  display: inline-block;\n  white-space: normal;\n  vertical-align: top;\n}\n.CodeMirror-merge-pane-rightmost {\n  position: absolute;\n  right: 0px;\n  z-index: 1;\n}\n\n.CodeMirror-merge-gap {\n  z-index: 2;\n  display: inline-block;\n  height: 100%;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n  overflow: hidden;\n  border-left: 1px solid #ddd;\n  border-right: 1px solid #ddd;\n  position: relative;\n  background: #f8f8f8;\n}\n\n.CodeMirror-merge-scrolllock-wrap {\n  position: absolute;\n  bottom: 0; left: 50%;\n}\n.CodeMirror-merge-scrolllock {\n  position: relative;\n  left: -50%;\n  cursor: pointer;\n  color: #555;\n  line-height: 1;\n}\n.CodeMirror-merge-scrolllock:after {\n  content: \"\\21db\\00a0\\00a0\\21da\";\n}\n.CodeMirror-merge-scrolllock.CodeMirror-merge-scrolllock-enabled:after {\n  content: \"\\21db\\21da\";\n}\n\n.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right {\n  position: absolute;\n  left: 0; top: 0;\n  right: 0; bottom: 0;\n  line-height: 1;\n}\n\n.CodeMirror-merge-copy {\n  position: absolute;\n  cursor: pointer;\n  color: #44c;\n  z-index: 3;\n}\n\n.CodeMirror-merge-copy-reverse {\n  position: absolute;\n  cursor: pointer;\n  color: #44c;\n}\n\n.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }\n.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }\n\n.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted {\n  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==);\n  background-position: bottom left;\n  background-repeat: repeat-x;\n}\n\n.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted {\n  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==);\n  background-position: bottom left;\n  background-repeat: repeat-x;\n}\n\n.CodeMirror-merge-r-chunk { background: #ffffe0; }\n.CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; }\n.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; }\n.CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; }\n\n.CodeMirror-merge-l-chunk { background: #eef; }\n.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; }\n.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; }\n.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; }\n\n.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; }\n.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; }\n.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; }\n\n.CodeMirror-merge-collapsed-widget:before {\n  content: \"(...)\";\n}\n.CodeMirror-merge-collapsed-widget {\n  cursor: pointer;\n  color: #88b;\n  background: #eef;\n  border: 1px solid #ddf;\n  font-size: 90%;\n  padding: 0 3px;\n  border-radius: 4px;\n}\n.CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/merge/merge.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// declare global: diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\")); // Note non-packaged dependency diff_match_patch\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"diff_match_patch\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n  var Pos = CodeMirror.Pos;\n  var svgNS = \"http://www.w3.org/2000/svg\";\n\n  function DiffView(mv, type) {\n    this.mv = mv;\n    this.type = type;\n    this.classes = type == \"left\"\n      ? {chunk: \"CodeMirror-merge-l-chunk\",\n         start: \"CodeMirror-merge-l-chunk-start\",\n         end: \"CodeMirror-merge-l-chunk-end\",\n         insert: \"CodeMirror-merge-l-inserted\",\n         del: \"CodeMirror-merge-l-deleted\",\n         connect: \"CodeMirror-merge-l-connect\"}\n      : {chunk: \"CodeMirror-merge-r-chunk\",\n         start: \"CodeMirror-merge-r-chunk-start\",\n         end: \"CodeMirror-merge-r-chunk-end\",\n         insert: \"CodeMirror-merge-r-inserted\",\n         del: \"CodeMirror-merge-r-deleted\",\n         connect: \"CodeMirror-merge-r-connect\"};\n  }\n\n  DiffView.prototype = {\n    constructor: DiffView,\n    init: function(pane, orig, options) {\n      this.edit = this.mv.edit;\n      ;(this.edit.state.diffViews || (this.edit.state.diffViews = [])).push(this);\n      this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: !this.mv.options.allowEditingOriginals}, copyObj(options)));\n      if (this.mv.options.connect == \"align\") {\n        if (!this.edit.state.trackAlignable) this.edit.state.trackAlignable = new TrackAlignable(this.edit)\n        this.orig.state.trackAlignable = new TrackAlignable(this.orig)\n      }\n      this.lockButton.title = this.edit.phrase(\"Toggle locked scrolling\");\n      this.lockButton.setAttribute(\"aria-label\", this.lockButton.title);\n\n      this.orig.state.diffViews = [this];\n      var classLocation = options.chunkClassLocation || \"background\";\n      if (Object.prototype.toString.call(classLocation) != \"[object Array]\") classLocation = [classLocation]\n      this.classes.classLocation = classLocation\n\n      this.diff = getDiff(asString(orig), asString(options.value), this.mv.options.ignoreWhitespace);\n      this.chunks = getChunks(this.diff);\n      this.diffOutOfDate = this.dealigned = false;\n      this.needsScrollSync = null\n\n      this.showDifferences = options.showDifferences !== false;\n    },\n    registerEvents: function(otherDv) {\n      this.forceUpdate = registerUpdate(this);\n      setScrollLock(this, true, false);\n      registerScroll(this, otherDv);\n    },\n    setShowDifferences: function(val) {\n      val = val !== false;\n      if (val != this.showDifferences) {\n        this.showDifferences = val;\n        this.forceUpdate(\"full\");\n      }\n    }\n  };\n\n  function ensureDiff(dv) {\n    if (dv.diffOutOfDate) {\n      dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue(), dv.mv.options.ignoreWhitespace);\n      dv.chunks = getChunks(dv.diff);\n      dv.diffOutOfDate = false;\n      CodeMirror.signal(dv.edit, \"updateDiff\", dv.diff);\n    }\n  }\n\n  var updating = false;\n  function registerUpdate(dv) {\n    var edit = {from: 0, to: 0, marked: []};\n    var orig = {from: 0, to: 0, marked: []};\n    var debounceChange, updatingFast = false;\n    function update(mode) {\n      updating = true;\n      updatingFast = false;\n      if (mode == \"full\") {\n        if (dv.svg) clear(dv.svg);\n        if (dv.copyButtons) clear(dv.copyButtons);\n        clearMarks(dv.edit, edit.marked, dv.classes);\n        clearMarks(dv.orig, orig.marked, dv.classes);\n        edit.from = edit.to = orig.from = orig.to = 0;\n      }\n      ensureDiff(dv);\n      if (dv.showDifferences) {\n        updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes);\n        updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes);\n      }\n\n      if (dv.mv.options.connect == \"align\")\n        alignChunks(dv);\n      makeConnections(dv);\n      if (dv.needsScrollSync != null) syncScroll(dv, dv.needsScrollSync)\n\n      updating = false;\n    }\n    function setDealign(fast) {\n      if (updating) return;\n      dv.dealigned = true;\n      set(fast);\n    }\n    function set(fast) {\n      if (updating || updatingFast) return;\n      clearTimeout(debounceChange);\n      if (fast === true) updatingFast = true;\n      debounceChange = setTimeout(update, fast === true ? 20 : 250);\n    }\n    function change(_cm, change) {\n      if (!dv.diffOutOfDate) {\n        dv.diffOutOfDate = true;\n        edit.from = edit.to = orig.from = orig.to = 0;\n      }\n      // Update faster when a line was added/removed\n      setDealign(change.text.length - 1 != change.to.line - change.from.line);\n    }\n    function swapDoc() {\n      dv.diffOutOfDate = true;\n      dv.dealigned = true;\n      update(\"full\");\n    }\n    dv.edit.on(\"change\", change);\n    dv.orig.on(\"change\", change);\n    dv.edit.on(\"swapDoc\", swapDoc);\n    dv.orig.on(\"swapDoc\", swapDoc);\n    if (dv.mv.options.connect == \"align\") {\n      CodeMirror.on(dv.edit.state.trackAlignable, \"realign\", setDealign)\n      CodeMirror.on(dv.orig.state.trackAlignable, \"realign\", setDealign)\n    }\n    dv.edit.on(\"viewportChange\", function() { set(false); });\n    dv.orig.on(\"viewportChange\", function() { set(false); });\n    update();\n    return update;\n  }\n\n  function registerScroll(dv, otherDv) {\n    dv.edit.on(\"scroll\", function() {\n      syncScroll(dv, true) && makeConnections(dv);\n    });\n    dv.orig.on(\"scroll\", function() {\n      syncScroll(dv, false) && makeConnections(dv);\n      if (otherDv) syncScroll(otherDv, true) && makeConnections(otherDv);\n    });\n  }\n\n  function syncScroll(dv, toOrig) {\n    // Change handler will do a refresh after a timeout when diff is out of date\n    if (dv.diffOutOfDate) {\n      if (dv.lockScroll && dv.needsScrollSync == null) dv.needsScrollSync = toOrig\n      return false\n    }\n    dv.needsScrollSync = null\n    if (!dv.lockScroll) return true;\n    var editor, other, now = +new Date;\n    if (toOrig) { editor = dv.edit; other = dv.orig; }\n    else { editor = dv.orig; other = dv.edit; }\n    // Don't take action if the position of this editor was recently set\n    // (to prevent feedback loops)\n    if (editor.state.scrollSetBy == dv && (editor.state.scrollSetAt || 0) + 250 > now) return false;\n\n    var sInfo = editor.getScrollInfo();\n    if (dv.mv.options.connect == \"align\") {\n      targetPos = sInfo.top;\n    } else {\n      var halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen;\n      var mid = editor.lineAtHeight(midY, \"local\");\n      var around = chunkBoundariesAround(dv.chunks, mid, toOrig);\n      var off = getOffsets(editor, toOrig ? around.edit : around.orig);\n      var offOther = getOffsets(other, toOrig ? around.orig : around.edit);\n      var ratio = (midY - off.top) / (off.bot - off.top);\n      var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top);\n\n      var botDist, mix;\n      // Some careful tweaking to make sure no space is left out of view\n      // when scrolling to top or bottom.\n      if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) {\n        targetPos = targetPos * mix + sInfo.top * (1 - mix);\n      } else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) {\n        var otherInfo = other.getScrollInfo();\n        var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos;\n        if (botDistOther > botDist && (mix = botDist / halfScreen) < 1)\n          targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix);\n      }\n    }\n\n    other.scrollTo(sInfo.left, targetPos);\n    other.state.scrollSetAt = now;\n    other.state.scrollSetBy = dv;\n    return true;\n  }\n\n  function getOffsets(editor, around) {\n    var bot = around.after;\n    if (bot == null) bot = editor.lastLine() + 1;\n    return {top: editor.heightAtLine(around.before || 0, \"local\"),\n            bot: editor.heightAtLine(bot, \"local\")};\n  }\n\n  function setScrollLock(dv, val, action) {\n    dv.lockScroll = val;\n    if (val && action != false) syncScroll(dv, DIFF_INSERT) && makeConnections(dv);\n    (val ? CodeMirror.addClass : CodeMirror.rmClass)(dv.lockButton, \"CodeMirror-merge-scrolllock-enabled\");\n  }\n\n  // Updating the marks for editor content\n\n  function removeClass(editor, line, classes) {\n    var locs = classes.classLocation\n    for (var i = 0; i < locs.length; i++) {\n      editor.removeLineClass(line, locs[i], classes.chunk);\n      editor.removeLineClass(line, locs[i], classes.start);\n      editor.removeLineClass(line, locs[i], classes.end);\n    }\n  }\n\n  function clearMarks(editor, arr, classes) {\n    for (var i = 0; i < arr.length; ++i) {\n      var mark = arr[i];\n      if (mark instanceof CodeMirror.TextMarker)\n        mark.clear();\n      else if (mark.parent)\n        removeClass(editor, mark, classes);\n    }\n    arr.length = 0;\n  }\n\n  // FIXME maybe add a margin around viewport to prevent too many updates\n  function updateMarks(editor, diff, state, type, classes) {\n    var vp = editor.getViewport();\n    editor.operation(function() {\n      if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {\n        clearMarks(editor, state.marked, classes);\n        markChanges(editor, diff, type, state.marked, vp.from, vp.to, classes);\n        state.from = vp.from; state.to = vp.to;\n      } else {\n        if (vp.from < state.from) {\n          markChanges(editor, diff, type, state.marked, vp.from, state.from, classes);\n          state.from = vp.from;\n        }\n        if (vp.to > state.to) {\n          markChanges(editor, diff, type, state.marked, state.to, vp.to, classes);\n          state.to = vp.to;\n        }\n      }\n    });\n  }\n\n  function addClass(editor, lineNr, classes, main, start, end) {\n    var locs = classes.classLocation, line = editor.getLineHandle(lineNr);\n    for (var i = 0; i < locs.length; i++) {\n      if (main) editor.addLineClass(line, locs[i], classes.chunk);\n      if (start) editor.addLineClass(line, locs[i], classes.start);\n      if (end) editor.addLineClass(line, locs[i], classes.end);\n    }\n    return line;\n  }\n\n  function markChanges(editor, diff, type, marks, from, to, classes) {\n    var pos = Pos(0, 0);\n    var top = Pos(from, 0), bot = editor.clipPos(Pos(to - 1));\n    var cls = type == DIFF_DELETE ? classes.del : classes.insert;\n    function markChunk(start, end) {\n      var bfrom = Math.max(from, start), bto = Math.min(to, end);\n      for (var i = bfrom; i < bto; ++i)\n        marks.push(addClass(editor, i, classes, true, i == start, i == end - 1));\n      // When the chunk is empty, make sure a horizontal line shows up\n      if (start == end && bfrom == end && bto == end) {\n        if (bfrom)\n          marks.push(addClass(editor, bfrom - 1, classes, false, false, true));\n        else\n          marks.push(addClass(editor, bfrom, classes, false, true, false));\n      }\n    }\n\n    var chunkStart = 0, pending = false;\n    for (var i = 0; i < diff.length; ++i) {\n      var part = diff[i], tp = part[0], str = part[1];\n      if (tp == DIFF_EQUAL) {\n        var cleanFrom = pos.line + (startOfLineClean(diff, i) ? 0 : 1);\n        moveOver(pos, str);\n        var cleanTo = pos.line + (endOfLineClean(diff, i) ? 1 : 0);\n        if (cleanTo > cleanFrom) {\n          if (pending) { markChunk(chunkStart, cleanFrom); pending = false }\n          chunkStart = cleanTo;\n        }\n      } else {\n        pending = true\n        if (tp == type) {\n          var end = moveOver(pos, str, true);\n          var a = posMax(top, pos), b = posMin(bot, end);\n          if (!posEq(a, b))\n            marks.push(editor.markText(a, b, {className: cls}));\n          pos = end;\n        }\n      }\n    }\n    if (pending) markChunk(chunkStart, pos.line + 1);\n  }\n\n  // Updating the gap between editor and original\n\n  function makeConnections(dv) {\n    if (!dv.showDifferences) return;\n\n    if (dv.svg) {\n      clear(dv.svg);\n      var w = dv.gap.offsetWidth;\n      attrs(dv.svg, \"width\", w, \"height\", dv.gap.offsetHeight);\n    }\n    if (dv.copyButtons) clear(dv.copyButtons);\n\n    var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport();\n    var outerTop = dv.mv.wrap.getBoundingClientRect().top\n    var sTopEdit = outerTop - dv.edit.getScrollerElement().getBoundingClientRect().top + dv.edit.getScrollInfo().top\n    var sTopOrig = outerTop - dv.orig.getScrollerElement().getBoundingClientRect().top + dv.orig.getScrollInfo().top;\n    for (var i = 0; i < dv.chunks.length; i++) {\n      var ch = dv.chunks[i];\n      if (ch.editFrom <= vpEdit.to && ch.editTo >= vpEdit.from &&\n          ch.origFrom <= vpOrig.to && ch.origTo >= vpOrig.from)\n        drawConnectorsForChunk(dv, ch, sTopOrig, sTopEdit, w);\n    }\n  }\n\n  function getMatchingOrigLine(editLine, chunks) {\n    var editStart = 0, origStart = 0;\n    for (var i = 0; i < chunks.length; i++) {\n      var chunk = chunks[i];\n      if (chunk.editTo > editLine && chunk.editFrom <= editLine) return null;\n      if (chunk.editFrom > editLine) break;\n      editStart = chunk.editTo;\n      origStart = chunk.origTo;\n    }\n    return origStart + (editLine - editStart);\n  }\n\n  // Combines information about chunks and widgets/markers to return\n  // an array of lines, in a single editor, that probably need to be\n  // aligned with their counterparts in the editor next to it.\n  function alignableFor(cm, chunks, isOrig) {\n    var tracker = cm.state.trackAlignable\n    var start = cm.firstLine(), trackI = 0\n    var result = []\n    for (var i = 0;; i++) {\n      var chunk = chunks[i]\n      var chunkStart = !chunk ? 1e9 : isOrig ? chunk.origFrom : chunk.editFrom\n      for (; trackI < tracker.alignable.length; trackI += 2) {\n        var n = tracker.alignable[trackI] + 1\n        if (n <= start) continue\n        if (n <= chunkStart) result.push(n)\n        else break\n      }\n      if (!chunk) break\n      result.push(start = isOrig ? chunk.origTo : chunk.editTo)\n    }\n    return result\n  }\n\n  // Given information about alignable lines in two editors, fill in\n  // the result (an array of three-element arrays) to reflect the\n  // lines that need to be aligned with each other.\n  function mergeAlignable(result, origAlignable, chunks, setIndex) {\n    var rI = 0, origI = 0, chunkI = 0, diff = 0\n    outer: for (;; rI++) {\n      var nextR = result[rI], nextO = origAlignable[origI]\n      if (!nextR && nextO == null) break\n\n      var rLine = nextR ? nextR[0] : 1e9, oLine = nextO == null ? 1e9 : nextO\n      while (chunkI < chunks.length) {\n        var chunk = chunks[chunkI]\n        if (chunk.origFrom <= oLine && chunk.origTo > oLine) {\n          origI++\n          rI--\n          continue outer;\n        }\n        if (chunk.editTo > rLine) {\n          if (chunk.editFrom <= rLine) continue outer;\n          break\n        }\n        diff += (chunk.origTo - chunk.origFrom) - (chunk.editTo - chunk.editFrom)\n        chunkI++\n      }\n      if (rLine == oLine - diff) {\n        nextR[setIndex] = oLine\n        origI++\n      } else if (rLine < oLine - diff) {\n        nextR[setIndex] = rLine + diff\n      } else {\n        var record = [oLine - diff, null, null]\n        record[setIndex] = oLine\n        result.splice(rI, 0, record)\n        origI++\n      }\n    }\n  }\n\n  function findAlignedLines(dv, other) {\n    var alignable = alignableFor(dv.edit, dv.chunks, false), result = []\n    if (other) for (var i = 0, j = 0; i < other.chunks.length; i++) {\n      var n = other.chunks[i].editTo\n      while (j < alignable.length && alignable[j] < n) j++\n      if (j == alignable.length || alignable[j] != n) alignable.splice(j++, 0, n)\n    }\n    for (var i = 0; i < alignable.length; i++)\n      result.push([alignable[i], null, null])\n\n    mergeAlignable(result, alignableFor(dv.orig, dv.chunks, true), dv.chunks, 1)\n    if (other)\n      mergeAlignable(result, alignableFor(other.orig, other.chunks, true), other.chunks, 2)\n\n    return result\n  }\n\n  function alignChunks(dv, force) {\n    if (!dv.dealigned && !force) return;\n    if (!dv.orig.curOp) return dv.orig.operation(function() {\n      alignChunks(dv, force);\n    });\n\n    dv.dealigned = false;\n    var other = dv.mv.left == dv ? dv.mv.right : dv.mv.left;\n    if (other) {\n      ensureDiff(other);\n      other.dealigned = false;\n    }\n    var linesToAlign = findAlignedLines(dv, other);\n\n    // Clear old aligners\n    var aligners = dv.mv.aligners;\n    for (var i = 0; i < aligners.length; i++)\n      aligners[i].clear();\n    aligners.length = 0;\n\n    var cm = [dv.edit, dv.orig], scroll = [], offset = []\n    if (other) cm.push(other.orig);\n    for (var i = 0; i < cm.length; i++) {\n      scroll.push(cm[i].getScrollInfo().top);\n      offset.push(-cm[i].getScrollerElement().getBoundingClientRect().top)\n    }\n\n    if (offset[0] != offset[1] || cm.length == 3 && offset[1] != offset[2])\n      alignLines(cm, offset, [0, 0, 0], aligners)\n    for (var ln = 0; ln < linesToAlign.length; ln++)\n      alignLines(cm, offset, linesToAlign[ln], aligners);\n\n    for (var i = 0; i < cm.length; i++)\n      cm[i].scrollTo(null, scroll[i]);\n  }\n\n  function alignLines(cm, cmOffset, lines, aligners) {\n    var maxOffset = -1e8, offset = [];\n    for (var i = 0; i < cm.length; i++) if (lines[i] != null) {\n      var off = cm[i].heightAtLine(lines[i], \"local\") - cmOffset[i];\n      offset[i] = off;\n      maxOffset = Math.max(maxOffset, off);\n    }\n    for (var i = 0; i < cm.length; i++) if (lines[i] != null) {\n      var diff = maxOffset - offset[i];\n      if (diff > 1)\n        aligners.push(padAbove(cm[i], lines[i], diff));\n    }\n  }\n\n  function padAbove(cm, line, size) {\n    var above = true;\n    if (line > cm.lastLine()) {\n      line--;\n      above = false;\n    }\n    var elt = document.createElement(\"div\");\n    elt.className = \"CodeMirror-merge-spacer\";\n    elt.style.height = size + \"px\"; elt.style.minWidth = \"1px\";\n    return cm.addLineWidget(line, elt, {height: size, above: above, mergeSpacer: true, handleMouseEvents: true});\n  }\n\n  function drawConnectorsForChunk(dv, chunk, sTopOrig, sTopEdit, w) {\n    var flip = dv.type == \"left\";\n    var top = dv.orig.heightAtLine(chunk.origFrom, \"local\", true) - sTopOrig;\n    if (dv.svg) {\n      var topLpx = top;\n      var topRpx = dv.edit.heightAtLine(chunk.editFrom, \"local\", true) - sTopEdit;\n      if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; }\n      var botLpx = dv.orig.heightAtLine(chunk.origTo, \"local\", true) - sTopOrig;\n      var botRpx = dv.edit.heightAtLine(chunk.editTo, \"local\", true) - sTopEdit;\n      if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; }\n      var curveTop = \" C \" + w/2 + \" \" + topRpx + \" \" + w/2 + \" \" + topLpx + \" \" + (w + 2) + \" \" + topLpx;\n      var curveBot = \" C \" + w/2 + \" \" + botLpx + \" \" + w/2 + \" \" + botRpx + \" -1 \" + botRpx;\n      attrs(dv.svg.appendChild(document.createElementNS(svgNS, \"path\")),\n            \"d\", \"M -1 \" + topRpx + curveTop + \" L \" + (w + 2) + \" \" + botLpx + curveBot + \" z\",\n            \"class\", dv.classes.connect);\n    }\n    if (dv.copyButtons) {\n      var copy = dv.copyButtons.appendChild(elt(\"div\", dv.type == \"left\" ? \"\\u21dd\" : \"\\u21dc\",\n                                                \"CodeMirror-merge-copy\"));\n      var editOriginals = dv.mv.options.allowEditingOriginals;\n      copy.title = dv.edit.phrase(editOriginals ? \"Push to left\" : \"Revert chunk\");\n      copy.chunk = chunk;\n      copy.style.top = (chunk.origTo > chunk.origFrom ? top : dv.edit.heightAtLine(chunk.editFrom, \"local\") - sTopEdit) + \"px\";\n      copy.setAttribute(\"role\", \"button\");\n      copy.setAttribute(\"tabindex\", \"0\");\n      copy.setAttribute(\"aria-label\", copy.title);\n\n      if (editOriginals) {\n        var topReverse = dv.edit.heightAtLine(chunk.editFrom, \"local\") - sTopEdit;\n        var copyReverse = dv.copyButtons.appendChild(elt(\"div\", dv.type == \"right\" ? \"\\u21dd\" : \"\\u21dc\",\n                                                         \"CodeMirror-merge-copy-reverse\"));\n        copyReverse.title = \"Push to right\";\n        copyReverse.chunk = {editFrom: chunk.origFrom, editTo: chunk.origTo,\n                             origFrom: chunk.editFrom, origTo: chunk.editTo};\n        copyReverse.style.top = topReverse + \"px\";\n        dv.type == \"right\" ? copyReverse.style.left = \"2px\" : copyReverse.style.right = \"2px\";\n        copyReverse.setAttribute(\"role\", \"button\");\n        copyReverse.setAttribute(\"tabindex\", \"0\");\n        copyReverse.setAttribute(\"aria-label\", copyReverse.title);\n      }\n    }\n  }\n\n  function copyChunk(dv, to, from, chunk) {\n    if (dv.diffOutOfDate) return;\n    var origStart = chunk.origTo > from.lastLine() ? Pos(chunk.origFrom - 1) : Pos(chunk.origFrom, 0)\n    var origEnd = Pos(chunk.origTo, 0)\n    var editStart = chunk.editTo > to.lastLine() ? Pos(chunk.editFrom - 1) : Pos(chunk.editFrom, 0)\n    var editEnd = Pos(chunk.editTo, 0)\n    var handler = dv.mv.options.revertChunk\n    if (handler)\n      handler(dv.mv, from, origStart, origEnd, to, editStart, editEnd)\n    else\n      to.replaceRange(from.getRange(origStart, origEnd), editStart, editEnd)\n  }\n\n  // Merge view, containing 0, 1, or 2 diff views.\n\n  var MergeView = CodeMirror.MergeView = function(node, options) {\n    if (!(this instanceof MergeView)) return new MergeView(node, options);\n\n    this.options = options;\n    var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight;\n\n    var hasLeft = origLeft != null, hasRight = origRight != null;\n    var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0);\n    var wrap = [], left = this.left = null, right = this.right = null;\n    var self = this;\n\n    if (hasLeft) {\n      left = this.left = new DiffView(this, \"left\");\n      var leftPane = elt(\"div\", null, \"CodeMirror-merge-pane CodeMirror-merge-left\");\n      wrap.push(leftPane);\n      wrap.push(buildGap(left));\n    }\n\n    var editPane = elt(\"div\", null, \"CodeMirror-merge-pane CodeMirror-merge-editor\");\n    wrap.push(editPane);\n\n    if (hasRight) {\n      right = this.right = new DiffView(this, \"right\");\n      wrap.push(buildGap(right));\n      var rightPane = elt(\"div\", null, \"CodeMirror-merge-pane CodeMirror-merge-right\");\n      wrap.push(rightPane);\n    }\n\n    (hasRight ? rightPane : editPane).className += \" CodeMirror-merge-pane-rightmost\";\n\n    wrap.push(elt(\"div\", null, null, \"height: 0; clear: both;\"));\n\n    var wrapElt = this.wrap = node.appendChild(elt(\"div\", wrap, \"CodeMirror-merge CodeMirror-merge-\" + panes + \"pane\"));\n    this.edit = CodeMirror(editPane, copyObj(options));\n\n    if (left) left.init(leftPane, origLeft, options);\n    if (right) right.init(rightPane, origRight, options);\n    if (options.collapseIdentical)\n      this.editor().operation(function() {\n        collapseIdenticalStretches(self, options.collapseIdentical);\n      });\n    if (options.connect == \"align\") {\n      this.aligners = [];\n      alignChunks(this.left || this.right, true);\n    }\n    if (left) left.registerEvents(right)\n    if (right) right.registerEvents(left)\n\n\n    var onResize = function() {\n      if (left) makeConnections(left);\n      if (right) makeConnections(right);\n    };\n    CodeMirror.on(window, \"resize\", onResize);\n    var resizeInterval = setInterval(function() {\n      for (var p = wrapElt.parentNode; p && p != document.body; p = p.parentNode) {}\n      if (!p) { clearInterval(resizeInterval); CodeMirror.off(window, \"resize\", onResize); }\n    }, 5000);\n  };\n\n  function buildGap(dv) {\n    var lock = dv.lockButton = elt(\"div\", null, \"CodeMirror-merge-scrolllock\");\n    lock.setAttribute(\"role\", \"button\");\n    lock.setAttribute(\"tabindex\", \"0\");\n    var lockWrap = elt(\"div\", [lock], \"CodeMirror-merge-scrolllock-wrap\");\n    CodeMirror.on(lock, \"click\", function() { setScrollLock(dv, !dv.lockScroll); });\n    CodeMirror.on(lock, \"keyup\", function(e) { (e.key === \"Enter\" || e.code === \"Space\") && setScrollLock(dv, !dv.lockScroll); });\n    var gapElts = [lockWrap];\n    if (dv.mv.options.revertButtons !== false) {\n      dv.copyButtons = elt(\"div\", null, \"CodeMirror-merge-copybuttons-\" + dv.type);\n      var copyButtons = function(e) {\n        var node = e.target || e.srcElement;\n        if (!node.chunk) return;\n        if (node.className == \"CodeMirror-merge-copy-reverse\") {\n          copyChunk(dv, dv.orig, dv.edit, node.chunk);\n          return;\n        }\n        copyChunk(dv, dv.edit, dv.orig, node.chunk);\n      }\n      CodeMirror.on(dv.copyButtons, \"click\", copyButtons);\n      CodeMirror.on(dv.copyButtons, \"keyup\", function(e) { (e.key === \"Enter\" || e.code === \"Space\") && copyButtons(e); });\n      gapElts.unshift(dv.copyButtons);\n    }\n    if (dv.mv.options.connect != \"align\") {\n      var svg = document.createElementNS && document.createElementNS(svgNS, \"svg\");\n      if (svg && !svg.createSVGRect) svg = null;\n      dv.svg = svg;\n      if (svg) gapElts.push(svg);\n    }\n\n    return dv.gap = elt(\"div\", gapElts, \"CodeMirror-merge-gap\");\n  }\n\n  MergeView.prototype = {\n    constructor: MergeView,\n    editor: function() { return this.edit; },\n    rightOriginal: function() { return this.right && this.right.orig; },\n    leftOriginal: function() { return this.left && this.left.orig; },\n    setShowDifferences: function(val) {\n      if (this.right) this.right.setShowDifferences(val);\n      if (this.left) this.left.setShowDifferences(val);\n    },\n    rightChunks: function() {\n      if (this.right) { ensureDiff(this.right); return this.right.chunks; }\n    },\n    leftChunks: function() {\n      if (this.left) { ensureDiff(this.left); return this.left.chunks; }\n    }\n  };\n\n  function asString(obj) {\n    if (typeof obj == \"string\") return obj;\n    else return obj.getValue();\n  }\n\n  // Operations on diffs\n  var dmp;\n  function getDiff(a, b, ignoreWhitespace) {\n    if (!dmp) dmp = new diff_match_patch();\n\n    var diff = dmp.diff_main(a, b);\n    // The library sometimes leaves in empty parts, which confuse the algorithm\n    for (var i = 0; i < diff.length; ++i) {\n      var part = diff[i];\n      if (ignoreWhitespace ? !/[^ \\t]/.test(part[1]) : !part[1]) {\n        diff.splice(i--, 1);\n      } else if (i && diff[i - 1][0] == part[0]) {\n        diff.splice(i--, 1);\n        diff[i][1] += part[1];\n      }\n    }\n    return diff;\n  }\n\n  function getChunks(diff) {\n    var chunks = [];\n    if (!diff.length) return chunks;\n    var startEdit = 0, startOrig = 0;\n    var edit = Pos(0, 0), orig = Pos(0, 0);\n    for (var i = 0; i < diff.length; ++i) {\n      var part = diff[i], tp = part[0];\n      if (tp == DIFF_EQUAL) {\n        var startOff = !startOfLineClean(diff, i) || edit.line < startEdit || orig.line < startOrig ? 1 : 0;\n        var cleanFromEdit = edit.line + startOff, cleanFromOrig = orig.line + startOff;\n        moveOver(edit, part[1], null, orig);\n        var endOff = endOfLineClean(diff, i) ? 1 : 0;\n        var cleanToEdit = edit.line + endOff, cleanToOrig = orig.line + endOff;\n        if (cleanToEdit > cleanFromEdit) {\n          if (i) chunks.push({origFrom: startOrig, origTo: cleanFromOrig,\n                              editFrom: startEdit, editTo: cleanFromEdit});\n          startEdit = cleanToEdit; startOrig = cleanToOrig;\n        }\n      } else {\n        moveOver(tp == DIFF_INSERT ? edit : orig, part[1]);\n      }\n    }\n    if (startEdit <= edit.line || startOrig <= orig.line)\n      chunks.push({origFrom: startOrig, origTo: orig.line + 1,\n                   editFrom: startEdit, editTo: edit.line + 1});\n    return chunks;\n  }\n\n  function endOfLineClean(diff, i) {\n    if (i == diff.length - 1) return true;\n    var next = diff[i + 1][1];\n    if ((next.length == 1 && i < diff.length - 2) || next.charCodeAt(0) != 10) return false;\n    if (i == diff.length - 2) return true;\n    next = diff[i + 2][1];\n    return (next.length > 1 || i == diff.length - 3) && next.charCodeAt(0) == 10;\n  }\n\n  function startOfLineClean(diff, i) {\n    if (i == 0) return true;\n    var last = diff[i - 1][1];\n    if (last.charCodeAt(last.length - 1) != 10) return false;\n    if (i == 1) return true;\n    last = diff[i - 2][1];\n    return last.charCodeAt(last.length - 1) == 10;\n  }\n\n  function chunkBoundariesAround(chunks, n, nInEdit) {\n    var beforeE, afterE, beforeO, afterO;\n    for (var i = 0; i < chunks.length; i++) {\n      var chunk = chunks[i];\n      var fromLocal = nInEdit ? chunk.editFrom : chunk.origFrom;\n      var toLocal = nInEdit ? chunk.editTo : chunk.origTo;\n      if (afterE == null) {\n        if (fromLocal > n) { afterE = chunk.editFrom; afterO = chunk.origFrom; }\n        else if (toLocal > n) { afterE = chunk.editTo; afterO = chunk.origTo; }\n      }\n      if (toLocal <= n) { beforeE = chunk.editTo; beforeO = chunk.origTo; }\n      else if (fromLocal <= n) { beforeE = chunk.editFrom; beforeO = chunk.origFrom; }\n    }\n    return {edit: {before: beforeE, after: afterE}, orig: {before: beforeO, after: afterO}};\n  }\n\n  function collapseSingle(cm, from, to) {\n    cm.addLineClass(from, \"wrap\", \"CodeMirror-merge-collapsed-line\");\n    var widget = document.createElement(\"span\");\n    widget.className = \"CodeMirror-merge-collapsed-widget\";\n    widget.title = cm.phrase(\"Identical text collapsed. Click to expand.\");\n    var mark = cm.markText(Pos(from, 0), Pos(to - 1), {\n      inclusiveLeft: true,\n      inclusiveRight: true,\n      replacedWith: widget,\n      clearOnEnter: true\n    });\n    function clear() {\n      mark.clear();\n      cm.removeLineClass(from, \"wrap\", \"CodeMirror-merge-collapsed-line\");\n    }\n    if (mark.explicitlyCleared) clear();\n    CodeMirror.on(widget, \"click\", clear);\n    mark.on(\"clear\", clear);\n    CodeMirror.on(widget, \"click\", clear);\n    return {mark: mark, clear: clear};\n  }\n\n  function collapseStretch(size, editors) {\n    var marks = [];\n    function clear() {\n      for (var i = 0; i < marks.length; i++) marks[i].clear();\n    }\n    for (var i = 0; i < editors.length; i++) {\n      var editor = editors[i];\n      var mark = collapseSingle(editor.cm, editor.line, editor.line + size);\n      marks.push(mark);\n      mark.mark.on(\"clear\", clear);\n    }\n    return marks[0].mark;\n  }\n\n  function unclearNearChunks(dv, margin, off, clear) {\n    for (var i = 0; i < dv.chunks.length; i++) {\n      var chunk = dv.chunks[i];\n      for (var l = chunk.editFrom - margin; l < chunk.editTo + margin; l++) {\n        var pos = l + off;\n        if (pos >= 0 && pos < clear.length) clear[pos] = false;\n      }\n    }\n  }\n\n  function collapseIdenticalStretches(mv, margin) {\n    if (typeof margin != \"number\") margin = 2;\n    var clear = [], edit = mv.editor(), off = edit.firstLine();\n    for (var l = off, e = edit.lastLine(); l <= e; l++) clear.push(true);\n    if (mv.left) unclearNearChunks(mv.left, margin, off, clear);\n    if (mv.right) unclearNearChunks(mv.right, margin, off, clear);\n\n    for (var i = 0; i < clear.length; i++) {\n      if (clear[i]) {\n        var line = i + off;\n        for (var size = 1; i < clear.length - 1 && clear[i + 1]; i++, size++) {}\n        if (size > margin) {\n          var editors = [{line: line, cm: edit}];\n          if (mv.left) editors.push({line: getMatchingOrigLine(line, mv.left.chunks), cm: mv.left.orig});\n          if (mv.right) editors.push({line: getMatchingOrigLine(line, mv.right.chunks), cm: mv.right.orig});\n          var mark = collapseStretch(size, editors);\n          if (mv.options.onCollapse) mv.options.onCollapse(mv, line, size, mark);\n        }\n      }\n    }\n  }\n\n  // General utilities\n\n  function elt(tag, content, className, style) {\n    var e = document.createElement(tag);\n    if (className) e.className = className;\n    if (style) e.style.cssText = style;\n    if (typeof content == \"string\") e.appendChild(document.createTextNode(content));\n    else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);\n    return e;\n  }\n\n  function clear(node) {\n    for (var count = node.childNodes.length; count > 0; --count)\n      node.removeChild(node.firstChild);\n  }\n\n  function attrs(elt) {\n    for (var i = 1; i < arguments.length; i += 2)\n      elt.setAttribute(arguments[i], arguments[i+1]);\n  }\n\n  function copyObj(obj, target) {\n    if (!target) target = {};\n    for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];\n    return target;\n  }\n\n  function moveOver(pos, str, copy, other) {\n    var out = copy ? Pos(pos.line, pos.ch) : pos, at = 0;\n    for (;;) {\n      var nl = str.indexOf(\"\\n\", at);\n      if (nl == -1) break;\n      ++out.line;\n      if (other) ++other.line;\n      at = nl + 1;\n    }\n    out.ch = (at ? 0 : out.ch) + (str.length - at);\n    if (other) other.ch = (at ? 0 : other.ch) + (str.length - at);\n    return out;\n  }\n\n  // Tracks collapsed markers and line widgets, in order to be able to\n  // accurately align the content of two editors.\n\n  var F_WIDGET = 1, F_WIDGET_BELOW = 2, F_MARKER = 4\n\n  function TrackAlignable(cm) {\n    this.cm = cm\n    this.alignable = []\n    this.height = cm.doc.height\n    var self = this\n    cm.on(\"markerAdded\", function(_, marker) {\n      if (!marker.collapsed) return\n      var found = marker.find(1)\n      if (found != null) self.set(found.line, F_MARKER)\n    })\n    cm.on(\"markerCleared\", function(_, marker, _min, max) {\n      if (max != null && marker.collapsed)\n        self.check(max, F_MARKER, self.hasMarker)\n    })\n    cm.on(\"markerChanged\", this.signal.bind(this))\n    cm.on(\"lineWidgetAdded\", function(_, widget, lineNo) {\n      if (widget.mergeSpacer) return\n      if (widget.above) self.set(lineNo - 1, F_WIDGET_BELOW)\n      else self.set(lineNo, F_WIDGET)\n    })\n    cm.on(\"lineWidgetCleared\", function(_, widget, lineNo) {\n      if (widget.mergeSpacer) return\n      if (widget.above) self.check(lineNo - 1, F_WIDGET_BELOW, self.hasWidgetBelow)\n      else self.check(lineNo, F_WIDGET, self.hasWidget)\n    })\n    cm.on(\"lineWidgetChanged\", this.signal.bind(this))\n    cm.on(\"change\", function(_, change) {\n      var start = change.from.line, nBefore = change.to.line - change.from.line\n      var nAfter = change.text.length - 1, end = start + nAfter\n      if (nBefore || nAfter) self.map(start, nBefore, nAfter)\n      self.check(end, F_MARKER, self.hasMarker)\n      if (nBefore || nAfter) self.check(change.from.line, F_MARKER, self.hasMarker)\n    })\n    cm.on(\"viewportChange\", function() {\n      if (self.cm.doc.height != self.height) self.signal()\n    })\n  }\n\n  TrackAlignable.prototype = {\n    signal: function() {\n      CodeMirror.signal(this, \"realign\")\n      this.height = this.cm.doc.height\n    },\n\n    set: function(n, flags) {\n      var pos = -1\n      for (; pos < this.alignable.length; pos += 2) {\n        var diff = this.alignable[pos] - n\n        if (diff == 0) {\n          if ((this.alignable[pos + 1] & flags) == flags) return\n          this.alignable[pos + 1] |= flags\n          this.signal()\n          return\n        }\n        if (diff > 0) break\n      }\n      this.signal()\n      this.alignable.splice(pos, 0, n, flags)\n    },\n\n    find: function(n) {\n      for (var i = 0; i < this.alignable.length; i += 2)\n        if (this.alignable[i] == n) return i\n      return -1\n    },\n\n    check: function(n, flag, pred) {\n      var found = this.find(n)\n      if (found == -1 || !(this.alignable[found + 1] & flag)) return\n      if (!pred.call(this, n)) {\n        this.signal()\n        var flags = this.alignable[found + 1] & ~flag\n        if (flags) this.alignable[found + 1] = flags\n        else this.alignable.splice(found, 2)\n      }\n    },\n\n    hasMarker: function(n) {\n      var handle = this.cm.getLineHandle(n)\n      if (handle.markedSpans) for (var i = 0; i < handle.markedSpans.length; i++)\n        if (handle.markedSpans[i].marker.collapsed && handle.markedSpans[i].to != null)\n          return true\n      return false\n    },\n\n    hasWidget: function(n) {\n      var handle = this.cm.getLineHandle(n)\n      if (handle.widgets) for (var i = 0; i < handle.widgets.length; i++)\n        if (!handle.widgets[i].above && !handle.widgets[i].mergeSpacer) return true\n      return false\n    },\n\n    hasWidgetBelow: function(n) {\n      if (n == this.cm.lastLine()) return false\n      var handle = this.cm.getLineHandle(n + 1)\n      if (handle.widgets) for (var i = 0; i < handle.widgets.length; i++)\n        if (handle.widgets[i].above && !handle.widgets[i].mergeSpacer) return true\n      return false\n    },\n\n    map: function(from, nBefore, nAfter) {\n      var diff = nAfter - nBefore, to = from + nBefore, widgetFrom = -1, widgetTo = -1\n      for (var i = 0; i < this.alignable.length; i += 2) {\n        var n = this.alignable[i]\n        if (n == from && (this.alignable[i + 1] & F_WIDGET_BELOW)) widgetFrom = i\n        if (n == to && (this.alignable[i + 1] & F_WIDGET_BELOW)) widgetTo = i\n        if (n <= from) continue\n        else if (n < to) this.alignable.splice(i--, 2)\n        else this.alignable[i] += diff\n      }\n      if (widgetFrom > -1) {\n        var flags = this.alignable[widgetFrom + 1]\n        if (flags == F_WIDGET_BELOW) this.alignable.splice(widgetFrom, 2)\n        else this.alignable[widgetFrom + 1] = flags & ~F_WIDGET_BELOW\n      }\n      if (widgetTo > -1 && nAfter)\n        this.set(from + nAfter, F_WIDGET_BELOW)\n    }\n  }\n\n  function posMin(a, b) { return (a.line - b.line || a.ch - b.ch) < 0 ? a : b; }\n  function posMax(a, b) { return (a.line - b.line || a.ch - b.ch) > 0 ? a : b; }\n  function posEq(a, b) { return a.line == b.line && a.ch == b.ch; }\n\n  function findPrevDiff(chunks, start, isOrig) {\n    for (var i = chunks.length - 1; i >= 0; i--) {\n      var chunk = chunks[i];\n      var to = (isOrig ? chunk.origTo : chunk.editTo) - 1;\n      if (to < start) return to;\n    }\n  }\n\n  function findNextDiff(chunks, start, isOrig) {\n    for (var i = 0; i < chunks.length; i++) {\n      var chunk = chunks[i];\n      var from = (isOrig ? chunk.origFrom : chunk.editFrom);\n      if (from > start) return from;\n    }\n  }\n\n  function goNearbyDiff(cm, dir) {\n    var found = null, views = cm.state.diffViews, line = cm.getCursor().line;\n    if (views) for (var i = 0; i < views.length; i++) {\n      var dv = views[i], isOrig = cm == dv.orig;\n      ensureDiff(dv);\n      var pos = dir < 0 ? findPrevDiff(dv.chunks, line, isOrig) : findNextDiff(dv.chunks, line, isOrig);\n      if (pos != null && (found == null || (dir < 0 ? pos > found : pos < found)))\n        found = pos;\n    }\n    if (found != null)\n      cm.setCursor(found, 0);\n    else\n      return CodeMirror.Pass;\n  }\n\n  CodeMirror.commands.goNextDiff = function(cm) {\n    return goNearbyDiff(cm, 1);\n  };\n  CodeMirror.commands.goPrevDiff = function(cm) {\n    return goNearbyDiff(cm, -1);\n  };\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/mode/loadmode.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), \"cjs\");\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], function(CM) { mod(CM, \"amd\"); });\n  else // Plain browser env\n    mod(CodeMirror, \"plain\");\n})(function(CodeMirror, env) {\n  if (!CodeMirror.modeURL) CodeMirror.modeURL = \"../mode/%N/%N.js\";\n\n  var loading = {};\n  function splitCallback(cont, n) {\n    var countDown = n;\n    return function() { if (--countDown == 0) cont(); };\n  }\n  function ensureDeps(mode, cont, options) {\n    var modeObj = CodeMirror.modes[mode], deps = modeObj && modeObj.dependencies;\n    if (!deps) return cont();\n    var missing = [];\n    for (var i = 0; i < deps.length; ++i) {\n      if (!CodeMirror.modes.hasOwnProperty(deps[i]))\n        missing.push(deps[i]);\n    }\n    if (!missing.length) return cont();\n    var split = splitCallback(cont, missing.length);\n    for (var i = 0; i < missing.length; ++i)\n      CodeMirror.requireMode(missing[i], split, options);\n  }\n\n  CodeMirror.requireMode = function(mode, cont, options) {\n    if (typeof mode != \"string\") mode = mode.name;\n    if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont, options);\n    if (loading.hasOwnProperty(mode)) return loading[mode].push(cont);\n\n    var file = options && options.path ? options.path(mode) : CodeMirror.modeURL.replace(/%N/g, mode);\n    if (options && options.loadMode) {\n      options.loadMode(file, function() { ensureDeps(mode, cont, options) })\n    } else if (env == \"plain\") {\n      var script = document.createElement(\"script\");\n      script.src = file;\n      var others = document.getElementsByTagName(\"script\")[0];\n      var list = loading[mode] = [cont];\n      CodeMirror.on(script, \"load\", function() {\n        ensureDeps(mode, function() {\n          for (var i = 0; i < list.length; ++i) list[i]();\n        }, options);\n      });\n      others.parentNode.insertBefore(script, others);\n    } else if (env == \"cjs\") {\n      require(file);\n      cont();\n    } else if (env == \"amd\") {\n      requirejs([file], cont);\n    }\n  };\n\n  CodeMirror.autoLoadMode = function(instance, mode, options) {\n    if (!CodeMirror.modes.hasOwnProperty(mode))\n      CodeMirror.requireMode(mode, function() {\n        instance.setOption(\"mode\", instance.getOption(\"mode\"));\n      }, options);\n  };\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/mode/multiplex.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.multiplexingMode = function(outer /*, others */) {\n  // Others should be {open, close, mode [, delimStyle] [, innerStyle] [, parseDelimiters]} objects\n  var others = Array.prototype.slice.call(arguments, 1);\n\n  function indexOf(string, pattern, from, returnEnd) {\n    if (typeof pattern == \"string\") {\n      var found = string.indexOf(pattern, from);\n      return returnEnd && found > -1 ? found + pattern.length : found;\n    }\n    var m = pattern.exec(from ? string.slice(from) : string);\n    return m ? m.index + from + (returnEnd ? m[0].length : 0) : -1;\n  }\n\n  return {\n    startState: function() {\n      return {\n        outer: CodeMirror.startState(outer),\n        innerActive: null,\n        inner: null,\n        startingInner: false\n      };\n    },\n\n    copyState: function(state) {\n      return {\n        outer: CodeMirror.copyState(outer, state.outer),\n        innerActive: state.innerActive,\n        inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner),\n        startingInner: state.startingInner\n      };\n    },\n\n    token: function(stream, state) {\n      if (!state.innerActive) {\n        var cutOff = Infinity, oldContent = stream.string;\n        for (var i = 0; i < others.length; ++i) {\n          var other = others[i];\n          var found = indexOf(oldContent, other.open, stream.pos);\n          if (found == stream.pos) {\n            if (!other.parseDelimiters) stream.match(other.open);\n            state.startingInner = !!other.parseDelimiters\n            state.innerActive = other;\n\n            // Get the outer indent, making sure to handle CodeMirror.Pass\n            var outerIndent = 0;\n            if (outer.indent) {\n              var possibleOuterIndent = outer.indent(state.outer, \"\", \"\");\n              if (possibleOuterIndent !== CodeMirror.Pass) outerIndent = possibleOuterIndent;\n            }\n\n            state.inner = CodeMirror.startState(other.mode, outerIndent);\n            return other.delimStyle && (other.delimStyle + \" \" + other.delimStyle + \"-open\");\n          } else if (found != -1 && found < cutOff) {\n            cutOff = found;\n          }\n        }\n        if (cutOff != Infinity) stream.string = oldContent.slice(0, cutOff);\n        var outerToken = outer.token(stream, state.outer);\n        if (cutOff != Infinity) stream.string = oldContent;\n        return outerToken;\n      } else {\n        var curInner = state.innerActive, oldContent = stream.string;\n        if (!curInner.close && stream.sol()) {\n          state.innerActive = state.inner = null;\n          return this.token(stream, state);\n        }\n        var found = curInner.close && !state.startingInner ?\n            indexOf(oldContent, curInner.close, stream.pos, curInner.parseDelimiters) : -1;\n        if (found == stream.pos && !curInner.parseDelimiters) {\n          stream.match(curInner.close);\n          state.innerActive = state.inner = null;\n          return curInner.delimStyle && (curInner.delimStyle + \" \" + curInner.delimStyle + \"-close\");\n        }\n        if (found > -1) stream.string = oldContent.slice(0, found);\n        var innerToken = curInner.mode.token(stream, state.inner);\n        if (found > -1) stream.string = oldContent;\n        else if (stream.pos > stream.start) state.startingInner = false\n\n        if (found == stream.pos && curInner.parseDelimiters)\n          state.innerActive = state.inner = null;\n\n        if (curInner.innerStyle) {\n          if (innerToken) innerToken = innerToken + \" \" + curInner.innerStyle;\n          else innerToken = curInner.innerStyle;\n        }\n\n        return innerToken;\n      }\n    },\n\n    indent: function(state, textAfter, line) {\n      var mode = state.innerActive ? state.innerActive.mode : outer;\n      if (!mode.indent) return CodeMirror.Pass;\n      return mode.indent(state.innerActive ? state.inner : state.outer, textAfter, line);\n    },\n\n    blankLine: function(state) {\n      var mode = state.innerActive ? state.innerActive.mode : outer;\n      if (mode.blankLine) {\n        mode.blankLine(state.innerActive ? state.inner : state.outer);\n      }\n      if (!state.innerActive) {\n        for (var i = 0; i < others.length; ++i) {\n          var other = others[i];\n          if (other.open === \"\\n\") {\n            state.innerActive = other;\n            state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, \"\", \"\") : 0);\n          }\n        }\n      } else if (state.innerActive.close === \"\\n\") {\n        state.innerActive = state.inner = null;\n      }\n    },\n\n    electricChars: outer.electricChars,\n\n    innerMode: function(state) {\n      return state.inner ? {state: state.inner, mode: state.innerActive.mode} : {state: state.outer, mode: outer};\n    }\n  };\n};\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/mode/multiplex_test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  CodeMirror.defineMode(\"markdown_with_stex\", function(){\n    var inner = CodeMirror.getMode({}, \"stex\");\n    var outer = CodeMirror.getMode({}, \"markdown\");\n\n    var innerOptions = {\n      open: '$',\n      close: '$',\n      mode: inner,\n      delimStyle: 'delim',\n      innerStyle: 'inner'\n    };\n\n    return CodeMirror.multiplexingMode(outer, innerOptions);\n  });\n\n  var mode = CodeMirror.getMode({}, \"markdown_with_stex\");\n\n  function MT(name) {\n    test.mode(\n      name,\n      mode,\n      Array.prototype.slice.call(arguments, 1),\n      'multiplexing');\n  }\n\n  MT(\n    \"stexInsideMarkdown\",\n    \"[strong **Equation:**] [delim&delim-open $][inner&tag \\\\pi][delim&delim-close $]\");\n\n  CodeMirror.defineMode(\"identical_delim_multiplex\", function() {\n    return CodeMirror.multiplexingMode(CodeMirror.getMode({indentUnit: 2}, \"javascript\"), {\n      open: \"#\",\n      close: \"#\",\n      mode: CodeMirror.getMode({}, \"markdown\"),\n      parseDelimiters: true,\n      innerStyle: \"q\"\n    });\n  });\n\n  var mode2 = CodeMirror.getMode({}, \"identical_delim_multiplex\");\n\n  test.mode(\"identical_delimiters_with_parseDelimiters\", mode2, [\n    \"[keyword let] [def x] [operator =] [q #foo][q&em *bar*][q #];\"\n  ], \"multiplexing\")\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/mode/overlay.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Utility function that allows modes to be combined. The mode given\n// as the base argument takes care of most of the normal mode\n// functionality, but a second (typically simple) mode is used, which\n// can override the style of text. Both modes get to parse all of the\n// text, but when both assign a non-null style to a piece of code, the\n// overlay wins, unless the combine argument was true and not overridden,\n// or state.overlay.combineTokens was true, in which case the styles are\n// combined.\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.overlayMode = function(base, overlay, combine) {\n  return {\n    startState: function() {\n      return {\n        base: CodeMirror.startState(base),\n        overlay: CodeMirror.startState(overlay),\n        basePos: 0, baseCur: null,\n        overlayPos: 0, overlayCur: null,\n        streamSeen: null\n      };\n    },\n    copyState: function(state) {\n      return {\n        base: CodeMirror.copyState(base, state.base),\n        overlay: CodeMirror.copyState(overlay, state.overlay),\n        basePos: state.basePos, baseCur: null,\n        overlayPos: state.overlayPos, overlayCur: null\n      };\n    },\n\n    token: function(stream, state) {\n      if (stream != state.streamSeen ||\n          Math.min(state.basePos, state.overlayPos) < stream.start) {\n        state.streamSeen = stream;\n        state.basePos = state.overlayPos = stream.start;\n      }\n\n      if (stream.start == state.basePos) {\n        state.baseCur = base.token(stream, state.base);\n        state.basePos = stream.pos;\n      }\n      if (stream.start == state.overlayPos) {\n        stream.pos = stream.start;\n        state.overlayCur = overlay.token(stream, state.overlay);\n        state.overlayPos = stream.pos;\n      }\n      stream.pos = Math.min(state.basePos, state.overlayPos);\n\n      // state.overlay.combineTokens always takes precedence over combine,\n      // unless set to null\n      if (state.overlayCur == null) return state.baseCur;\n      else if (state.baseCur != null &&\n               state.overlay.combineTokens ||\n               combine && state.overlay.combineTokens == null)\n        return state.baseCur + \" \" + state.overlayCur;\n      else return state.overlayCur;\n    },\n\n    indent: base.indent && function(state, textAfter, line) {\n      return base.indent(state.base, textAfter, line);\n    },\n    electricChars: base.electricChars,\n\n    innerMode: function(state) { return {state: state.base, mode: base}; },\n\n    blankLine: function(state) {\n      var baseToken, overlayToken;\n      if (base.blankLine) baseToken = base.blankLine(state.base);\n      if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay);\n\n      return overlayToken == null ?\n        baseToken :\n        (combine && baseToken != null ? baseToken + \" \" + overlayToken : overlayToken);\n    }\n  };\n};\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/mode/simple.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineSimpleMode = function(name, states) {\n    CodeMirror.defineMode(name, function(config) {\n      return CodeMirror.simpleMode(config, states);\n    });\n  };\n\n  CodeMirror.simpleMode = function(config, states) {\n    ensureState(states, \"start\");\n    var states_ = {}, meta = states.meta || {}, hasIndentation = false;\n    for (var state in states) if (state != meta && states.hasOwnProperty(state)) {\n      var list = states_[state] = [], orig = states[state];\n      for (var i = 0; i < orig.length; i++) {\n        var data = orig[i];\n        list.push(new Rule(data, states));\n        if (data.indent || data.dedent) hasIndentation = true;\n      }\n    }\n    var mode = {\n      startState: function() {\n        return {state: \"start\", pending: null,\n                local: null, localState: null,\n                indent: hasIndentation ? [] : null};\n      },\n      copyState: function(state) {\n        var s = {state: state.state, pending: state.pending,\n                 local: state.local, localState: null,\n                 indent: state.indent && state.indent.slice(0)};\n        if (state.localState)\n          s.localState = CodeMirror.copyState(state.local.mode, state.localState);\n        if (state.stack)\n          s.stack = state.stack.slice(0);\n        for (var pers = state.persistentStates; pers; pers = pers.next)\n          s.persistentStates = {mode: pers.mode,\n                                spec: pers.spec,\n                                state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state),\n                                next: s.persistentStates};\n        return s;\n      },\n      token: tokenFunction(states_, config),\n      innerMode: function(state) { return state.local && {mode: state.local.mode, state: state.localState}; },\n      indent: indentFunction(states_, meta)\n    };\n    if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop))\n      mode[prop] = meta[prop];\n    return mode;\n  };\n\n  function ensureState(states, name) {\n    if (!states.hasOwnProperty(name))\n      throw new Error(\"Undefined state \" + name + \" in simple mode\");\n  }\n\n  function toRegex(val, caret) {\n    if (!val) return /(?:)/;\n    var flags = \"\";\n    if (val instanceof RegExp) {\n      if (val.ignoreCase) flags = \"i\";\n      if (val.unicode) flags += \"u\"\n      val = val.source;\n    } else {\n      val = String(val);\n    }\n    return new RegExp((caret === false ? \"\" : \"^\") + \"(?:\" + val + \")\", flags);\n  }\n\n  function asToken(val) {\n    if (!val) return null;\n    if (val.apply) return val\n    if (typeof val == \"string\") return val.replace(/\\./g, \" \");\n    var result = [];\n    for (var i = 0; i < val.length; i++)\n      result.push(val[i] && val[i].replace(/\\./g, \" \"));\n    return result;\n  }\n\n  function Rule(data, states) {\n    if (data.next || data.push) ensureState(states, data.next || data.push);\n    this.regex = toRegex(data.regex);\n    this.token = asToken(data.token);\n    this.data = data;\n  }\n\n  function tokenFunction(states, config) {\n    return function(stream, state) {\n      if (state.pending) {\n        var pend = state.pending.shift();\n        if (state.pending.length == 0) state.pending = null;\n        stream.pos += pend.text.length;\n        return pend.token;\n      }\n\n      if (state.local) {\n        if (state.local.end && stream.match(state.local.end)) {\n          var tok = state.local.endToken || null;\n          state.local = state.localState = null;\n          return tok;\n        } else {\n          var tok = state.local.mode.token(stream, state.localState), m;\n          if (state.local.endScan && (m = state.local.endScan.exec(stream.current())))\n            stream.pos = stream.start + m.index;\n          return tok;\n        }\n      }\n\n      var curState = states[state.state];\n      for (var i = 0; i < curState.length; i++) {\n        var rule = curState[i];\n        var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex);\n        if (matches) {\n          if (rule.data.next) {\n            state.state = rule.data.next;\n          } else if (rule.data.push) {\n            (state.stack || (state.stack = [])).push(state.state);\n            state.state = rule.data.push;\n          } else if (rule.data.pop && state.stack && state.stack.length) {\n            state.state = state.stack.pop();\n          }\n\n          if (rule.data.mode)\n            enterLocalMode(config, state, rule.data.mode, rule.token);\n          if (rule.data.indent)\n            state.indent.push(stream.indentation() + config.indentUnit);\n          if (rule.data.dedent)\n            state.indent.pop();\n          var token = rule.token\n          if (token && token.apply) token = token(matches)\n          if (matches.length > 2 && rule.token && typeof rule.token != \"string\") {\n            for (var j = 2; j < matches.length; j++)\n              if (matches[j])\n                (state.pending || (state.pending = [])).push({text: matches[j], token: rule.token[j - 1]});\n            stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0));\n            return token[0];\n          } else if (token && token.join) {\n            return token[0];\n          } else {\n            return token;\n          }\n        }\n      }\n      stream.next();\n      return null;\n    };\n  }\n\n  function cmp(a, b) {\n    if (a === b) return true;\n    if (!a || typeof a != \"object\" || !b || typeof b != \"object\") return false;\n    var props = 0;\n    for (var prop in a) if (a.hasOwnProperty(prop)) {\n      if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false;\n      props++;\n    }\n    for (var prop in b) if (b.hasOwnProperty(prop)) props--;\n    return props == 0;\n  }\n\n  function enterLocalMode(config, state, spec, token) {\n    var pers;\n    if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next)\n      if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p;\n    var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec);\n    var lState = pers ? pers.state : CodeMirror.startState(mode);\n    if (spec.persistent && !pers)\n      state.persistentStates = {mode: mode, spec: spec.spec, state: lState, next: state.persistentStates};\n\n    state.localState = lState;\n    state.local = {mode: mode,\n                   end: spec.end && toRegex(spec.end),\n                   endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false),\n                   endToken: token && token.join ? token[token.length - 1] : token};\n  }\n\n  function indexOf(val, arr) {\n    for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true;\n  }\n\n  function indentFunction(states, meta) {\n    return function(state, textAfter, line) {\n      if (state.local && state.local.mode.indent)\n        return state.local.mode.indent(state.localState, textAfter, line);\n      if (state.indent == null || state.local || meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1)\n        return CodeMirror.Pass;\n\n      var pos = state.indent.length - 1, rules = states[state.state];\n      scan: for (;;) {\n        for (var i = 0; i < rules.length; i++) {\n          var rule = rules[i];\n          if (rule.data.dedent && rule.data.dedentIfLineStart !== false) {\n            var m = rule.regex.exec(textAfter);\n            if (m && m[0]) {\n              pos--;\n              if (rule.next || rule.push) rules = states[rule.next || rule.push];\n              textAfter = textAfter.slice(m[0].length);\n              continue scan;\n            }\n          }\n        }\n        break;\n      }\n      return pos < 0 ? 0 : state.indent[pos];\n    };\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/runmode/colorize.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"./runmode\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"./runmode\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var isBlock = /^(p|li|div|h\\\\d|pre|blockquote|td)$/;\n\n  function textContent(node, out) {\n    if (node.nodeType == 3) return out.push(node.nodeValue);\n    for (var ch = node.firstChild; ch; ch = ch.nextSibling) {\n      textContent(ch, out);\n      if (isBlock.test(node.nodeType)) out.push(\"\\n\");\n    }\n  }\n\n  CodeMirror.colorize = function(collection, defaultMode) {\n    if (!collection) collection = document.body.getElementsByTagName(\"pre\");\n\n    for (var i = 0; i < collection.length; ++i) {\n      var node = collection[i];\n      var mode = node.getAttribute(\"data-lang\") || defaultMode;\n      if (!mode) continue;\n\n      var text = [];\n      textContent(node, text);\n      node.textContent = \"\";\n      CodeMirror.runMode(text.join(\"\"), mode, node);\n\n      node.className += \" cm-s-default\";\n    }\n  };\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/runmode/runmode-standalone.js",
    "content": "(function () {\n  'use strict';\n\n  function copyObj(obj, target, overwrite) {\n    if (!target) { target = {}; }\n    for (var prop in obj)\n      { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))\n        { target[prop] = obj[prop]; } }\n    return target\n  }\n\n  // Counts the column offset in a string, taking tabs into account.\n  // Used mostly to find indentation.\n  function countColumn(string, end, tabSize, startIndex, startValue) {\n    if (end == null) {\n      end = string.search(/[^\\s\\u00a0]/);\n      if (end == -1) { end = string.length; }\n    }\n    for (var i = startIndex || 0, n = startValue || 0;;) {\n      var nextTab = string.indexOf(\"\\t\", i);\n      if (nextTab < 0 || nextTab >= end)\n        { return n + (end - i) }\n      n += nextTab - i;\n      n += tabSize - (n % tabSize);\n      i = nextTab + 1;\n    }\n  }\n\n  function nothing() {}\n\n  function createObj(base, props) {\n    var inst;\n    if (Object.create) {\n      inst = Object.create(base);\n    } else {\n      nothing.prototype = base;\n      inst = new nothing();\n    }\n    if (props) { copyObj(props, inst); }\n    return inst\n  }\n\n  // STRING STREAM\n\n  // Fed to the mode parsers, provides helper functions to make\n  // parsers more succinct.\n\n  var StringStream = function(string, tabSize, lineOracle) {\n    this.pos = this.start = 0;\n    this.string = string;\n    this.tabSize = tabSize || 8;\n    this.lastColumnPos = this.lastColumnValue = 0;\n    this.lineStart = 0;\n    this.lineOracle = lineOracle;\n  };\n\n  StringStream.prototype.eol = function () {return this.pos >= this.string.length};\n  StringStream.prototype.sol = function () {return this.pos == this.lineStart};\n  StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};\n  StringStream.prototype.next = function () {\n    if (this.pos < this.string.length)\n      { return this.string.charAt(this.pos++) }\n  };\n  StringStream.prototype.eat = function (match) {\n    var ch = this.string.charAt(this.pos);\n    var ok;\n    if (typeof match == \"string\") { ok = ch == match; }\n    else { ok = ch && (match.test ? match.test(ch) : match(ch)); }\n    if (ok) {++this.pos; return ch}\n  };\n  StringStream.prototype.eatWhile = function (match) {\n    var start = this.pos;\n    while (this.eat(match)){}\n    return this.pos > start\n  };\n  StringStream.prototype.eatSpace = function () {\n    var start = this.pos;\n    while (/[\\s\\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }\n    return this.pos > start\n  };\n  StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};\n  StringStream.prototype.skipTo = function (ch) {\n    var found = this.string.indexOf(ch, this.pos);\n    if (found > -1) {this.pos = found; return true}\n  };\n  StringStream.prototype.backUp = function (n) {this.pos -= n;};\n  StringStream.prototype.column = function () {\n    if (this.lastColumnPos < this.start) {\n      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);\n      this.lastColumnPos = this.start;\n    }\n    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n  };\n  StringStream.prototype.indentation = function () {\n    return countColumn(this.string, null, this.tabSize) -\n      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n  };\n  StringStream.prototype.match = function (pattern, consume, caseInsensitive) {\n    if (typeof pattern == \"string\") {\n      var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };\n      var substr = this.string.substr(this.pos, pattern.length);\n      if (cased(substr) == cased(pattern)) {\n        if (consume !== false) { this.pos += pattern.length; }\n        return true\n      }\n    } else {\n      var match = this.string.slice(this.pos).match(pattern);\n      if (match && match.index > 0) { return null }\n      if (match && consume !== false) { this.pos += match[0].length; }\n      return match\n    }\n  };\n  StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};\n  StringStream.prototype.hideFirstChars = function (n, inner) {\n    this.lineStart += n;\n    try { return inner() }\n    finally { this.lineStart -= n; }\n  };\n  StringStream.prototype.lookAhead = function (n) {\n    var oracle = this.lineOracle;\n    return oracle && oracle.lookAhead(n)\n  };\n  StringStream.prototype.baseToken = function () {\n    var oracle = this.lineOracle;\n    return oracle && oracle.baseToken(this.pos)\n  };\n\n  // Known modes, by name and by MIME\n  var modes = {}, mimeModes = {};\n\n  // Extra arguments are stored as the mode's dependencies, which is\n  // used by (legacy) mechanisms like loadmode.js to automatically\n  // load a mode. (Preferred mechanism is the require/define calls.)\n  function defineMode(name, mode) {\n    if (arguments.length > 2)\n      { mode.dependencies = Array.prototype.slice.call(arguments, 2); }\n    modes[name] = mode;\n  }\n\n  function defineMIME(mime, spec) {\n    mimeModes[mime] = spec;\n  }\n\n  // Given a MIME type, a {name, ...options} config object, or a name\n  // string, return a mode config object.\n  function resolveMode(spec) {\n    if (typeof spec == \"string\" && mimeModes.hasOwnProperty(spec)) {\n      spec = mimeModes[spec];\n    } else if (spec && typeof spec.name == \"string\" && mimeModes.hasOwnProperty(spec.name)) {\n      var found = mimeModes[spec.name];\n      if (typeof found == \"string\") { found = {name: found}; }\n      spec = createObj(found, spec);\n      spec.name = found.name;\n    } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+xml$/.test(spec)) {\n      return resolveMode(\"application/xml\")\n    } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+json$/.test(spec)) {\n      return resolveMode(\"application/json\")\n    }\n    if (typeof spec == \"string\") { return {name: spec} }\n    else { return spec || {name: \"null\"} }\n  }\n\n  // Given a mode spec (anything that resolveMode accepts), find and\n  // initialize an actual mode object.\n  function getMode(options, spec) {\n    spec = resolveMode(spec);\n    var mfactory = modes[spec.name];\n    if (!mfactory) { return getMode(options, \"text/plain\") }\n    var modeObj = mfactory(options, spec);\n    if (modeExtensions.hasOwnProperty(spec.name)) {\n      var exts = modeExtensions[spec.name];\n      for (var prop in exts) {\n        if (!exts.hasOwnProperty(prop)) { continue }\n        if (modeObj.hasOwnProperty(prop)) { modeObj[\"_\" + prop] = modeObj[prop]; }\n        modeObj[prop] = exts[prop];\n      }\n    }\n    modeObj.name = spec.name;\n    if (spec.helperType) { modeObj.helperType = spec.helperType; }\n    if (spec.modeProps) { for (var prop$1 in spec.modeProps)\n      { modeObj[prop$1] = spec.modeProps[prop$1]; } }\n\n    return modeObj\n  }\n\n  // This can be used to attach properties to mode objects from\n  // outside the actual mode definition.\n  var modeExtensions = {};\n  function extendMode(mode, properties) {\n    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});\n    copyObj(properties, exts);\n  }\n\n  function copyState(mode, state) {\n    if (state === true) { return state }\n    if (mode.copyState) { return mode.copyState(state) }\n    var nstate = {};\n    for (var n in state) {\n      var val = state[n];\n      if (val instanceof Array) { val = val.concat([]); }\n      nstate[n] = val;\n    }\n    return nstate\n  }\n\n  // Given a mode and a state (for that mode), find the inner mode and\n  // state at the position that the state refers to.\n  function innerMode(mode, state) {\n    var info;\n    while (mode.innerMode) {\n      info = mode.innerMode(state);\n      if (!info || info.mode == mode) { break }\n      state = info.state;\n      mode = info.mode;\n    }\n    return info || {mode: mode, state: state}\n  }\n\n  function startState(mode, a1, a2) {\n    return mode.startState ? mode.startState(a1, a2) : true\n  }\n\n  var modeMethods = {\n    __proto__: null,\n    modes: modes,\n    mimeModes: mimeModes,\n    defineMode: defineMode,\n    defineMIME: defineMIME,\n    resolveMode: resolveMode,\n    getMode: getMode,\n    modeExtensions: modeExtensions,\n    extendMode: extendMode,\n    copyState: copyState,\n    innerMode: innerMode,\n    startState: startState\n  };\n\n  // declare global: globalThis, CodeMirror\n\n  // Create a minimal CodeMirror needed to use runMode, and assign to root.\n  var root = typeof globalThis !== 'undefined' ? globalThis : window;\n  root.CodeMirror = {};\n\n  // Copy StringStream and mode methods into CodeMirror object.\n  CodeMirror.StringStream = StringStream;\n  for (var exported in modeMethods) { CodeMirror[exported] = modeMethods[exported]; }\n\n  // Minimal default mode.\n  CodeMirror.defineMode(\"null\", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });\n  CodeMirror.defineMIME(\"text/plain\", \"null\");\n\n  CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min;\n  CodeMirror.splitLines = function(string) { return string.split(/\\r?\\n|\\r/) };\n  CodeMirror.countColumn = countColumn;\n\n  CodeMirror.defaults = { indentUnit: 2 };\n\n  // CodeMirror, copyright (c) by Marijn Haverbeke and others\n  // Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n  (function(mod) {\n    if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n      { mod(require(\"../../lib/codemirror\")); }\n    else if (typeof define == \"function\" && define.amd) // AMD\n      { define([\"../../lib/codemirror\"], mod); }\n    else // Plain browser env\n      { mod(CodeMirror); }\n  })(function(CodeMirror) {\n\n  CodeMirror.runMode = function(string, modespec, callback, options) {\n    var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);\n    var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;\n\n    // Create a tokenizing callback function if passed-in callback is a DOM element.\n    if (callback.appendChild) {\n      var ie = /MSIE \\d/.test(navigator.userAgent);\n      var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);\n      var node = callback, col = 0;\n      node.textContent = \"\";\n      callback = function(text, style) {\n        if (text == \"\\n\") {\n          // Emitting LF or CRLF on IE8 or earlier results in an incorrect display.\n          // Emitting a carriage return makes everything ok.\n          node.appendChild(document.createTextNode(ie_lt9 ? '\\r' : text));\n          col = 0;\n          return;\n        }\n        var content = \"\";\n        // replace tabs\n        for (var pos = 0;;) {\n          var idx = text.indexOf(\"\\t\", pos);\n          if (idx == -1) {\n            content += text.slice(pos);\n            col += text.length - pos;\n            break;\n          } else {\n            col += idx - pos;\n            content += text.slice(pos, idx);\n            var size = tabSize - col % tabSize;\n            col += size;\n            for (var i = 0; i < size; ++i) { content += \" \"; }\n            pos = idx + 1;\n          }\n        }\n        // Create a node with token style and append it to the callback DOM element.\n        if (style) {\n          var sp = node.appendChild(document.createElement(\"span\"));\n          sp.className = \"cm-\" + style.replace(/ +/g, \" cm-\");\n          sp.appendChild(document.createTextNode(content));\n        } else {\n          node.appendChild(document.createTextNode(content));\n        }\n      };\n    }\n\n    var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode);\n    for (var i = 0, e = lines.length; i < e; ++i) {\n      if (i) { callback(\"\\n\"); }\n      var stream = new CodeMirror.StringStream(lines[i], null, {\n        lookAhead: function(n) { return lines[i + n] },\n        baseToken: function() {}\n      });\n      if (!stream.string && mode.blankLine) { mode.blankLine(state); }\n      while (!stream.eol()) {\n        var style = mode.token(stream, state);\n        callback(stream.current(), style, i, stream.start, state, mode);\n        stream.start = stream.pos;\n      }\n    }\n  };\n\n  });\n\n}());\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/runmode/runmode.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.runMode = function(string, modespec, callback, options) {\n  var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);\n  var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;\n\n  // Create a tokenizing callback function if passed-in callback is a DOM element.\n  if (callback.appendChild) {\n    var ie = /MSIE \\d/.test(navigator.userAgent);\n    var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);\n    var node = callback, col = 0;\n    node.textContent = \"\";\n    callback = function(text, style) {\n      if (text == \"\\n\") {\n        // Emitting LF or CRLF on IE8 or earlier results in an incorrect display.\n        // Emitting a carriage return makes everything ok.\n        node.appendChild(document.createTextNode(ie_lt9 ? '\\r' : text));\n        col = 0;\n        return;\n      }\n      var content = \"\";\n      // replace tabs\n      for (var pos = 0;;) {\n        var idx = text.indexOf(\"\\t\", pos);\n        if (idx == -1) {\n          content += text.slice(pos);\n          col += text.length - pos;\n          break;\n        } else {\n          col += idx - pos;\n          content += text.slice(pos, idx);\n          var size = tabSize - col % tabSize;\n          col += size;\n          for (var i = 0; i < size; ++i) content += \" \";\n          pos = idx + 1;\n        }\n      }\n      // Create a node with token style and append it to the callback DOM element.\n      if (style) {\n        var sp = node.appendChild(document.createElement(\"span\"));\n        sp.className = \"cm-\" + style.replace(/ +/g, \" cm-\");\n        sp.appendChild(document.createTextNode(content));\n      } else {\n        node.appendChild(document.createTextNode(content));\n      }\n    };\n  }\n\n  var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode);\n  for (var i = 0, e = lines.length; i < e; ++i) {\n    if (i) callback(\"\\n\");\n    var stream = new CodeMirror.StringStream(lines[i], null, {\n      lookAhead: function(n) { return lines[i + n] },\n      baseToken: function() {}\n    });\n    if (!stream.string && mode.blankLine) mode.blankLine(state);\n    while (!stream.eol()) {\n      var style = mode.token(stream, state);\n      callback(stream.current(), style, i, stream.start, state, mode);\n      stream.start = stream.pos;\n    }\n  }\n};\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/runmode/runmode.node.js",
    "content": "'use strict';\n\nfunction copyObj(obj, target, overwrite) {\n  if (!target) { target = {}; }\n  for (var prop in obj)\n    { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))\n      { target[prop] = obj[prop]; } }\n  return target\n}\n\n// Counts the column offset in a string, taking tabs into account.\n// Used mostly to find indentation.\nfunction countColumn(string, end, tabSize, startIndex, startValue) {\n  if (end == null) {\n    end = string.search(/[^\\s\\u00a0]/);\n    if (end == -1) { end = string.length; }\n  }\n  for (var i = startIndex || 0, n = startValue || 0;;) {\n    var nextTab = string.indexOf(\"\\t\", i);\n    if (nextTab < 0 || nextTab >= end)\n      { return n + (end - i) }\n    n += nextTab - i;\n    n += tabSize - (n % tabSize);\n    i = nextTab + 1;\n  }\n}\n\nfunction nothing() {}\n\nfunction createObj(base, props) {\n  var inst;\n  if (Object.create) {\n    inst = Object.create(base);\n  } else {\n    nothing.prototype = base;\n    inst = new nothing();\n  }\n  if (props) { copyObj(props, inst); }\n  return inst\n}\n\n// STRING STREAM\n\n// Fed to the mode parsers, provides helper functions to make\n// parsers more succinct.\n\nvar StringStream = function(string, tabSize, lineOracle) {\n  this.pos = this.start = 0;\n  this.string = string;\n  this.tabSize = tabSize || 8;\n  this.lastColumnPos = this.lastColumnValue = 0;\n  this.lineStart = 0;\n  this.lineOracle = lineOracle;\n};\n\nStringStream.prototype.eol = function () {return this.pos >= this.string.length};\nStringStream.prototype.sol = function () {return this.pos == this.lineStart};\nStringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};\nStringStream.prototype.next = function () {\n  if (this.pos < this.string.length)\n    { return this.string.charAt(this.pos++) }\n};\nStringStream.prototype.eat = function (match) {\n  var ch = this.string.charAt(this.pos);\n  var ok;\n  if (typeof match == \"string\") { ok = ch == match; }\n  else { ok = ch && (match.test ? match.test(ch) : match(ch)); }\n  if (ok) {++this.pos; return ch}\n};\nStringStream.prototype.eatWhile = function (match) {\n  var start = this.pos;\n  while (this.eat(match)){}\n  return this.pos > start\n};\nStringStream.prototype.eatSpace = function () {\n  var start = this.pos;\n  while (/[\\s\\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }\n  return this.pos > start\n};\nStringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};\nStringStream.prototype.skipTo = function (ch) {\n  var found = this.string.indexOf(ch, this.pos);\n  if (found > -1) {this.pos = found; return true}\n};\nStringStream.prototype.backUp = function (n) {this.pos -= n;};\nStringStream.prototype.column = function () {\n  if (this.lastColumnPos < this.start) {\n    this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);\n    this.lastColumnPos = this.start;\n  }\n  return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n};\nStringStream.prototype.indentation = function () {\n  return countColumn(this.string, null, this.tabSize) -\n    (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n};\nStringStream.prototype.match = function (pattern, consume, caseInsensitive) {\n  if (typeof pattern == \"string\") {\n    var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };\n    var substr = this.string.substr(this.pos, pattern.length);\n    if (cased(substr) == cased(pattern)) {\n      if (consume !== false) { this.pos += pattern.length; }\n      return true\n    }\n  } else {\n    var match = this.string.slice(this.pos).match(pattern);\n    if (match && match.index > 0) { return null }\n    if (match && consume !== false) { this.pos += match[0].length; }\n    return match\n  }\n};\nStringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};\nStringStream.prototype.hideFirstChars = function (n, inner) {\n  this.lineStart += n;\n  try { return inner() }\n  finally { this.lineStart -= n; }\n};\nStringStream.prototype.lookAhead = function (n) {\n  var oracle = this.lineOracle;\n  return oracle && oracle.lookAhead(n)\n};\nStringStream.prototype.baseToken = function () {\n  var oracle = this.lineOracle;\n  return oracle && oracle.baseToken(this.pos)\n};\n\n// Known modes, by name and by MIME\nvar modes = {}, mimeModes = {};\n\n// Extra arguments are stored as the mode's dependencies, which is\n// used by (legacy) mechanisms like loadmode.js to automatically\n// load a mode. (Preferred mechanism is the require/define calls.)\nfunction defineMode(name, mode) {\n  if (arguments.length > 2)\n    { mode.dependencies = Array.prototype.slice.call(arguments, 2); }\n  modes[name] = mode;\n}\n\nfunction defineMIME(mime, spec) {\n  mimeModes[mime] = spec;\n}\n\n// Given a MIME type, a {name, ...options} config object, or a name\n// string, return a mode config object.\nfunction resolveMode(spec) {\n  if (typeof spec == \"string\" && mimeModes.hasOwnProperty(spec)) {\n    spec = mimeModes[spec];\n  } else if (spec && typeof spec.name == \"string\" && mimeModes.hasOwnProperty(spec.name)) {\n    var found = mimeModes[spec.name];\n    if (typeof found == \"string\") { found = {name: found}; }\n    spec = createObj(found, spec);\n    spec.name = found.name;\n  } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+xml$/.test(spec)) {\n    return resolveMode(\"application/xml\")\n  } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+json$/.test(spec)) {\n    return resolveMode(\"application/json\")\n  }\n  if (typeof spec == \"string\") { return {name: spec} }\n  else { return spec || {name: \"null\"} }\n}\n\n// Given a mode spec (anything that resolveMode accepts), find and\n// initialize an actual mode object.\nfunction getMode(options, spec) {\n  spec = resolveMode(spec);\n  var mfactory = modes[spec.name];\n  if (!mfactory) { return getMode(options, \"text/plain\") }\n  var modeObj = mfactory(options, spec);\n  if (modeExtensions.hasOwnProperty(spec.name)) {\n    var exts = modeExtensions[spec.name];\n    for (var prop in exts) {\n      if (!exts.hasOwnProperty(prop)) { continue }\n      if (modeObj.hasOwnProperty(prop)) { modeObj[\"_\" + prop] = modeObj[prop]; }\n      modeObj[prop] = exts[prop];\n    }\n  }\n  modeObj.name = spec.name;\n  if (spec.helperType) { modeObj.helperType = spec.helperType; }\n  if (spec.modeProps) { for (var prop$1 in spec.modeProps)\n    { modeObj[prop$1] = spec.modeProps[prop$1]; } }\n\n  return modeObj\n}\n\n// This can be used to attach properties to mode objects from\n// outside the actual mode definition.\nvar modeExtensions = {};\nfunction extendMode(mode, properties) {\n  var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});\n  copyObj(properties, exts);\n}\n\nfunction copyState(mode, state) {\n  if (state === true) { return state }\n  if (mode.copyState) { return mode.copyState(state) }\n  var nstate = {};\n  for (var n in state) {\n    var val = state[n];\n    if (val instanceof Array) { val = val.concat([]); }\n    nstate[n] = val;\n  }\n  return nstate\n}\n\n// Given a mode and a state (for that mode), find the inner mode and\n// state at the position that the state refers to.\nfunction innerMode(mode, state) {\n  var info;\n  while (mode.innerMode) {\n    info = mode.innerMode(state);\n    if (!info || info.mode == mode) { break }\n    state = info.state;\n    mode = info.mode;\n  }\n  return info || {mode: mode, state: state}\n}\n\nfunction startState(mode, a1, a2) {\n  return mode.startState ? mode.startState(a1, a2) : true\n}\n\nvar modeMethods = {\n  __proto__: null,\n  modes: modes,\n  mimeModes: mimeModes,\n  defineMode: defineMode,\n  defineMIME: defineMIME,\n  resolveMode: resolveMode,\n  getMode: getMode,\n  modeExtensions: modeExtensions,\n  extendMode: extendMode,\n  copyState: copyState,\n  innerMode: innerMode,\n  startState: startState\n};\n\n// Copy StringStream and mode methods into exports (CodeMirror) object.\nexports.StringStream = StringStream;\nexports.countColumn = countColumn;\nfor (var exported in modeMethods) { exports[exported] = modeMethods[exported]; }\n\n// Shim library CodeMirror with the minimal CodeMirror defined above.\nrequire.cache[require.resolve(\"../../lib/codemirror\")] = require.cache[require.resolve(\"./runmode.node\")];\nrequire.cache[require.resolve(\"../../addon/runmode/runmode\")] = require.cache[require.resolve(\"./runmode.node\")];\n\n// Minimal default mode.\nexports.defineMode(\"null\", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });\nexports.defineMIME(\"text/plain\", \"null\");\n\nexports.registerHelper = exports.registerGlobalHelper = Math.min;\nexports.splitLines = function(string) { return string.split(/\\r?\\n|\\r/) };\n\nexports.defaults = { indentUnit: 2 };\n\n// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    { mod(require(\"../../lib/codemirror\")); }\n  else if (typeof define == \"function\" && define.amd) // AMD\n    { define([\"../../lib/codemirror\"], mod); }\n  else // Plain browser env\n    { mod(CodeMirror); }\n})(function(CodeMirror) {\n\nCodeMirror.runMode = function(string, modespec, callback, options) {\n  var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);\n  var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;\n\n  // Create a tokenizing callback function if passed-in callback is a DOM element.\n  if (callback.appendChild) {\n    var ie = /MSIE \\d/.test(navigator.userAgent);\n    var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);\n    var node = callback, col = 0;\n    node.textContent = \"\";\n    callback = function(text, style) {\n      if (text == \"\\n\") {\n        // Emitting LF or CRLF on IE8 or earlier results in an incorrect display.\n        // Emitting a carriage return makes everything ok.\n        node.appendChild(document.createTextNode(ie_lt9 ? '\\r' : text));\n        col = 0;\n        return;\n      }\n      var content = \"\";\n      // replace tabs\n      for (var pos = 0;;) {\n        var idx = text.indexOf(\"\\t\", pos);\n        if (idx == -1) {\n          content += text.slice(pos);\n          col += text.length - pos;\n          break;\n        } else {\n          col += idx - pos;\n          content += text.slice(pos, idx);\n          var size = tabSize - col % tabSize;\n          col += size;\n          for (var i = 0; i < size; ++i) { content += \" \"; }\n          pos = idx + 1;\n        }\n      }\n      // Create a node with token style and append it to the callback DOM element.\n      if (style) {\n        var sp = node.appendChild(document.createElement(\"span\"));\n        sp.className = \"cm-\" + style.replace(/ +/g, \" cm-\");\n        sp.appendChild(document.createTextNode(content));\n      } else {\n        node.appendChild(document.createTextNode(content));\n      }\n    };\n  }\n\n  var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode);\n  for (var i = 0, e = lines.length; i < e; ++i) {\n    if (i) { callback(\"\\n\"); }\n    var stream = new CodeMirror.StringStream(lines[i], null, {\n      lookAhead: function(n) { return lines[i + n] },\n      baseToken: function() {}\n    });\n    if (!stream.string && mode.blankLine) { mode.blankLine(state); }\n    while (!stream.eol()) {\n      var style = mode.token(stream, state);\n      callback(stream.current(), style, i, stream.start, state, mode);\n      stream.start = stream.pos;\n    }\n  }\n};\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/scroll/annotatescrollbar.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineExtension(\"annotateScrollbar\", function(options) {\n    if (typeof options == \"string\") options = {className: options};\n    return new Annotation(this, options);\n  });\n\n  CodeMirror.defineOption(\"scrollButtonHeight\", 0);\n\n  function Annotation(cm, options) {\n    this.cm = cm;\n    this.options = options;\n    this.buttonHeight = options.scrollButtonHeight || cm.getOption(\"scrollButtonHeight\");\n    this.annotations = [];\n    this.doRedraw = this.doUpdate = null;\n    this.div = cm.getWrapperElement().appendChild(document.createElement(\"div\"));\n    this.div.style.cssText = \"position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none\";\n    this.computeScale();\n\n    function scheduleRedraw(delay) {\n      clearTimeout(self.doRedraw);\n      self.doRedraw = setTimeout(function() { self.redraw(); }, delay);\n    }\n\n    var self = this;\n    cm.on(\"refresh\", this.resizeHandler = function() {\n      clearTimeout(self.doUpdate);\n      self.doUpdate = setTimeout(function() {\n        if (self.computeScale()) scheduleRedraw(20);\n      }, 100);\n    });\n    cm.on(\"markerAdded\", this.resizeHandler);\n    cm.on(\"markerCleared\", this.resizeHandler);\n    if (options.listenForChanges !== false)\n      cm.on(\"changes\", this.changeHandler = function() {\n        scheduleRedraw(250);\n      });\n  }\n\n  Annotation.prototype.computeScale = function() {\n    var cm = this.cm;\n    var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight - this.buttonHeight * 2) /\n      cm.getScrollerElement().scrollHeight\n    if (hScale != this.hScale) {\n      this.hScale = hScale;\n      return true;\n    }\n  };\n\n  Annotation.prototype.update = function(annotations) {\n    this.annotations = annotations;\n    this.redraw();\n  };\n\n  Annotation.prototype.redraw = function(compute) {\n    if (compute !== false) this.computeScale();\n    var cm = this.cm, hScale = this.hScale;\n\n    var frag = document.createDocumentFragment(), anns = this.annotations;\n\n    var wrapping = cm.getOption(\"lineWrapping\");\n    var singleLineH = wrapping && cm.defaultTextHeight() * 1.5;\n    var curLine = null, curLineObj = null;\n\n    function getY(pos, top) {\n      if (curLine != pos.line) {\n        curLine = pos.line\n        curLineObj = cm.getLineHandle(pos.line)\n        var visual = cm.getLineHandleVisualStart(curLineObj)\n        if (visual != curLineObj) {\n          curLine = cm.getLineNumber(visual)\n          curLineObj = visual\n        }\n      }\n      if ((curLineObj.widgets && curLineObj.widgets.length) ||\n          (wrapping && curLineObj.height > singleLineH))\n        return cm.charCoords(pos, \"local\")[top ? \"top\" : \"bottom\"];\n      var topY = cm.heightAtLine(curLineObj, \"local\");\n      return topY + (top ? 0 : curLineObj.height);\n    }\n\n    var lastLine = cm.lastLine()\n    if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) {\n      var ann = anns[i];\n      if (ann.to.line > lastLine) continue;\n      var top = nextTop || getY(ann.from, true) * hScale;\n      var bottom = getY(ann.to, false) * hScale;\n      while (i < anns.length - 1) {\n        if (anns[i + 1].to.line > lastLine) break;\n        nextTop = getY(anns[i + 1].from, true) * hScale;\n        if (nextTop > bottom + .9) break;\n        ann = anns[++i];\n        bottom = getY(ann.to, false) * hScale;\n      }\n      if (bottom == top) continue;\n      var height = Math.max(bottom - top, 3);\n\n      var elt = frag.appendChild(document.createElement(\"div\"));\n      elt.style.cssText = \"position: absolute; right: 0px; width: \" + Math.max(cm.display.barWidth - 1, 2) + \"px; top: \"\n        + (top + this.buttonHeight) + \"px; height: \" + height + \"px\";\n      elt.className = this.options.className;\n      if (ann.id) {\n        elt.setAttribute(\"annotation-id\", ann.id);\n      }\n    }\n    this.div.textContent = \"\";\n    this.div.appendChild(frag);\n  };\n\n  Annotation.prototype.clear = function() {\n    this.cm.off(\"refresh\", this.resizeHandler);\n    this.cm.off(\"markerAdded\", this.resizeHandler);\n    this.cm.off(\"markerCleared\", this.resizeHandler);\n    if (this.changeHandler) this.cm.off(\"changes\", this.changeHandler);\n    this.div.parentNode.removeChild(this.div);\n  };\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/scroll/scrollpastend.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineOption(\"scrollPastEnd\", false, function(cm, val, old) {\n    if (old && old != CodeMirror.Init) {\n      cm.off(\"change\", onChange);\n      cm.off(\"refresh\", updateBottomMargin);\n      cm.display.lineSpace.parentNode.style.paddingBottom = \"\";\n      cm.state.scrollPastEndPadding = null;\n    }\n    if (val) {\n      cm.on(\"change\", onChange);\n      cm.on(\"refresh\", updateBottomMargin);\n      updateBottomMargin(cm);\n    }\n  });\n\n  function onChange(cm, change) {\n    if (CodeMirror.changeEnd(change).line == cm.lastLine())\n      updateBottomMargin(cm);\n  }\n\n  function updateBottomMargin(cm) {\n    var padding = \"\";\n    if (cm.lineCount() > 1) {\n      var totalH = cm.display.scroller.clientHeight - 30,\n          lastLineH = cm.getLineHandle(cm.lastLine()).height;\n      padding = (totalH - lastLineH) + \"px\";\n    }\n    if (cm.state.scrollPastEndPadding != padding) {\n      cm.state.scrollPastEndPadding = padding;\n      cm.display.lineSpace.parentNode.style.paddingBottom = padding;\n      cm.off(\"refresh\", updateBottomMargin);\n      cm.setSize();\n      cm.on(\"refresh\", updateBottomMargin);\n    }\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/scroll/simplescrollbars.css",
    "content": ".CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div {\n  position: absolute;\n  background: #ccc;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n  border: 1px solid #bbb;\n  border-radius: 2px;\n}\n\n.CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical {\n  position: absolute;\n  z-index: 6;\n  background: #eee;\n}\n\n.CodeMirror-simplescroll-horizontal {\n  bottom: 0; left: 0;\n  height: 8px;\n}\n.CodeMirror-simplescroll-horizontal div {\n  bottom: 0;\n  height: 100%;\n}\n\n.CodeMirror-simplescroll-vertical {\n  right: 0; top: 0;\n  width: 8px;\n}\n.CodeMirror-simplescroll-vertical div {\n  right: 0;\n  width: 100%;\n}\n\n\n.CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler {\n  display: none;\n}\n\n.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {\n  position: absolute;\n  background: #bcd;\n  border-radius: 3px;\n}\n\n.CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical {\n  position: absolute;\n  z-index: 6;\n}\n\n.CodeMirror-overlayscroll-horizontal {\n  bottom: 0; left: 0;\n  height: 6px;\n}\n.CodeMirror-overlayscroll-horizontal div {\n  bottom: 0;\n  height: 100%;\n}\n\n.CodeMirror-overlayscroll-vertical {\n  right: 0; top: 0;\n  width: 6px;\n}\n.CodeMirror-overlayscroll-vertical div {\n  right: 0;\n  width: 100%;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/scroll/simplescrollbars.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  function Bar(cls, orientation, scroll) {\n    this.orientation = orientation;\n    this.scroll = scroll;\n    this.screen = this.total = this.size = 1;\n    this.pos = 0;\n\n    this.node = document.createElement(\"div\");\n    this.node.className = cls + \"-\" + orientation;\n    this.inner = this.node.appendChild(document.createElement(\"div\"));\n\n    var self = this;\n    CodeMirror.on(this.inner, \"mousedown\", function(e) {\n      if (e.which != 1) return;\n      CodeMirror.e_preventDefault(e);\n      var axis = self.orientation == \"horizontal\" ? \"pageX\" : \"pageY\";\n      var start = e[axis], startpos = self.pos;\n      function done() {\n        CodeMirror.off(document, \"mousemove\", move);\n        CodeMirror.off(document, \"mouseup\", done);\n      }\n      function move(e) {\n        if (e.which != 1) return done();\n        self.moveTo(startpos + (e[axis] - start) * (self.total / self.size));\n      }\n      CodeMirror.on(document, \"mousemove\", move);\n      CodeMirror.on(document, \"mouseup\", done);\n    });\n\n    CodeMirror.on(this.node, \"click\", function(e) {\n      CodeMirror.e_preventDefault(e);\n      var innerBox = self.inner.getBoundingClientRect(), where;\n      if (self.orientation == \"horizontal\")\n        where = e.clientX < innerBox.left ? -1 : e.clientX > innerBox.right ? 1 : 0;\n      else\n        where = e.clientY < innerBox.top ? -1 : e.clientY > innerBox.bottom ? 1 : 0;\n      self.moveTo(self.pos + where * self.screen);\n    });\n\n    function onWheel(e) {\n      var moved = CodeMirror.wheelEventPixels(e)[self.orientation == \"horizontal\" ? \"x\" : \"y\"];\n      var oldPos = self.pos;\n      self.moveTo(self.pos + moved);\n      if (self.pos != oldPos) CodeMirror.e_preventDefault(e);\n    }\n    CodeMirror.on(this.node, \"mousewheel\", onWheel);\n    CodeMirror.on(this.node, \"DOMMouseScroll\", onWheel);\n  }\n\n  Bar.prototype.setPos = function(pos, force) {\n    if (pos < 0) pos = 0;\n    if (pos > this.total - this.screen) pos = this.total - this.screen;\n    if (!force && pos == this.pos) return false;\n    this.pos = pos;\n    this.inner.style[this.orientation == \"horizontal\" ? \"left\" : \"top\"] =\n      (pos * (this.size / this.total)) + \"px\";\n    return true\n  };\n\n  Bar.prototype.moveTo = function(pos) {\n    if (this.setPos(pos)) this.scroll(pos, this.orientation);\n  }\n\n  var minButtonSize = 10;\n\n  Bar.prototype.update = function(scrollSize, clientSize, barSize) {\n    var sizeChanged = this.screen != clientSize || this.total != scrollSize || this.size != barSize\n    if (sizeChanged) {\n      this.screen = clientSize;\n      this.total = scrollSize;\n      this.size = barSize;\n    }\n\n    var buttonSize = this.screen * (this.size / this.total);\n    if (buttonSize < minButtonSize) {\n      this.size -= minButtonSize - buttonSize;\n      buttonSize = minButtonSize;\n    }\n    this.inner.style[this.orientation == \"horizontal\" ? \"width\" : \"height\"] =\n      buttonSize + \"px\";\n    this.setPos(this.pos, sizeChanged);\n  };\n\n  function SimpleScrollbars(cls, place, scroll) {\n    this.addClass = cls;\n    this.horiz = new Bar(cls, \"horizontal\", scroll);\n    place(this.horiz.node);\n    this.vert = new Bar(cls, \"vertical\", scroll);\n    place(this.vert.node);\n    this.width = null;\n  }\n\n  SimpleScrollbars.prototype.update = function(measure) {\n    if (this.width == null) {\n      var style = window.getComputedStyle ? window.getComputedStyle(this.horiz.node) : this.horiz.node.currentStyle;\n      if (style) this.width = parseInt(style.height);\n    }\n    var width = this.width || 0;\n\n    var needsH = measure.scrollWidth > measure.clientWidth + 1;\n    var needsV = measure.scrollHeight > measure.clientHeight + 1;\n    this.vert.node.style.display = needsV ? \"block\" : \"none\";\n    this.horiz.node.style.display = needsH ? \"block\" : \"none\";\n\n    if (needsV) {\n      this.vert.update(measure.scrollHeight, measure.clientHeight,\n                       measure.viewHeight - (needsH ? width : 0));\n      this.vert.node.style.bottom = needsH ? width + \"px\" : \"0\";\n    }\n    if (needsH) {\n      this.horiz.update(measure.scrollWidth, measure.clientWidth,\n                        measure.viewWidth - (needsV ? width : 0) - measure.barLeft);\n      this.horiz.node.style.right = needsV ? width + \"px\" : \"0\";\n      this.horiz.node.style.left = measure.barLeft + \"px\";\n    }\n\n    return {right: needsV ? width : 0, bottom: needsH ? width : 0};\n  };\n\n  SimpleScrollbars.prototype.setScrollTop = function(pos) {\n    this.vert.setPos(pos);\n  };\n\n  SimpleScrollbars.prototype.setScrollLeft = function(pos) {\n    this.horiz.setPos(pos);\n  };\n\n  SimpleScrollbars.prototype.clear = function() {\n    var parent = this.horiz.node.parentNode;\n    parent.removeChild(this.horiz.node);\n    parent.removeChild(this.vert.node);\n  };\n\n  CodeMirror.scrollbarModel.simple = function(place, scroll) {\n    return new SimpleScrollbars(\"CodeMirror-simplescroll\", place, scroll);\n  };\n  CodeMirror.scrollbarModel.overlay = function(place, scroll) {\n    return new SimpleScrollbars(\"CodeMirror-overlayscroll\", place, scroll);\n  };\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/search/jump-to-line.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Defines jumpToLine command. Uses dialog.js if present.\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../dialog/dialog\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../dialog/dialog\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  // default search panel location\n  CodeMirror.defineOption(\"search\", {bottom: false});\n\n  function dialog(cm, text, shortText, deflt, f) {\n    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true, bottom: cm.options.search.bottom});\n    else f(prompt(shortText, deflt));\n  }\n\n  function getJumpDialog(cm) {\n    return cm.phrase(\"Jump to line:\") + ' <input type=\"text\" style=\"width: 10em\" class=\"CodeMirror-search-field\"/> <span style=\"color: #888\" class=\"CodeMirror-search-hint\">' + cm.phrase(\"(Use line:column or scroll% syntax)\") + '</span>';\n  }\n\n  function interpretLine(cm, string) {\n    var num = Number(string)\n    if (/^[-+]/.test(string)) return cm.getCursor().line + num\n    else return num - 1\n  }\n\n  CodeMirror.commands.jumpToLine = function(cm) {\n    var cur = cm.getCursor();\n    dialog(cm, getJumpDialog(cm), cm.phrase(\"Jump to line:\"), (cur.line + 1) + \":\" + cur.ch, function(posStr) {\n      if (!posStr) return;\n\n      var match;\n      if (match = /^\\s*([\\+\\-]?\\d+)\\s*\\:\\s*(\\d+)\\s*$/.exec(posStr)) {\n        cm.setCursor(interpretLine(cm, match[1]), Number(match[2]))\n      } else if (match = /^\\s*([\\+\\-]?\\d+(\\.\\d+)?)\\%\\s*/.exec(posStr)) {\n        var line = Math.round(cm.lineCount() * Number(match[1]) / 100);\n        if (/^[-+]/.test(match[1])) line = cur.line + line + 1;\n        cm.setCursor(line - 1, cur.ch);\n      } else if (match = /^\\s*\\:?\\s*([\\+\\-]?\\d+)\\s*/.exec(posStr)) {\n        cm.setCursor(interpretLine(cm, match[1]), cur.ch);\n      }\n    });\n  };\n\n  CodeMirror.keyMap[\"default\"][\"Alt-G\"] = \"jumpToLine\";\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/search/match-highlighter.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Highlighting text that matches the selection\n//\n// Defines an option highlightSelectionMatches, which, when enabled,\n// will style strings that match the selection throughout the\n// document.\n//\n// The option can be set to true to simply enable it, or to a\n// {minChars, style, wordsOnly, showToken, delay} object to explicitly\n// configure it. minChars is the minimum amount of characters that should be\n// selected for the behavior to occur, and style is the token style to\n// apply to the matches. This will be prefixed by \"cm-\" to create an\n// actual CSS class name. If wordsOnly is enabled, the matches will be\n// highlighted only if the selected text is a word. showToken, when enabled,\n// will cause the current token to be highlighted when nothing is selected.\n// delay is used to specify how much time to wait, in milliseconds, before\n// highlighting the matches. If annotateScrollbar is enabled, the occurrences\n// will be highlighted on the scrollbar via the matchesonscrollbar addon.\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"./matchesonscrollbar\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"./matchesonscrollbar\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var defaults = {\n    style: \"matchhighlight\",\n    minChars: 2,\n    delay: 100,\n    wordsOnly: false,\n    annotateScrollbar: false,\n    showToken: false,\n    trim: true\n  }\n\n  function State(options) {\n    this.options = {}\n    for (var name in defaults)\n      this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name]\n    this.overlay = this.timeout = null;\n    this.matchesonscroll = null;\n    this.active = false;\n  }\n\n  CodeMirror.defineOption(\"highlightSelectionMatches\", false, function(cm, val, old) {\n    if (old && old != CodeMirror.Init) {\n      removeOverlay(cm);\n      clearTimeout(cm.state.matchHighlighter.timeout);\n      cm.state.matchHighlighter = null;\n      cm.off(\"cursorActivity\", cursorActivity);\n      cm.off(\"focus\", onFocus)\n    }\n    if (val) {\n      var state = cm.state.matchHighlighter = new State(val);\n      if (cm.hasFocus()) {\n        state.active = true\n        highlightMatches(cm)\n      } else {\n        cm.on(\"focus\", onFocus)\n      }\n      cm.on(\"cursorActivity\", cursorActivity);\n    }\n  });\n\n  function cursorActivity(cm) {\n    var state = cm.state.matchHighlighter;\n    if (state.active || cm.hasFocus()) scheduleHighlight(cm, state)\n  }\n\n  function onFocus(cm) {\n    var state = cm.state.matchHighlighter\n    if (!state.active) {\n      state.active = true\n      scheduleHighlight(cm, state)\n    }\n  }\n\n  function scheduleHighlight(cm, state) {\n    clearTimeout(state.timeout);\n    state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay);\n  }\n\n  function addOverlay(cm, query, hasBoundary, style) {\n    var state = cm.state.matchHighlighter;\n    cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));\n    if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {\n      var searchFor = hasBoundary ? new RegExp((/\\w/.test(query.charAt(0)) ? \"\\\\b\" : \"\") +\n                                               query.replace(/[\\\\\\[.+*?(){|^$]/g, \"\\\\$&\") +\n                                               (/\\w/.test(query.charAt(query.length - 1)) ? \"\\\\b\" : \"\")) : query;\n      state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,\n        {className: \"CodeMirror-selection-highlight-scrollbar\"});\n    }\n  }\n\n  function removeOverlay(cm) {\n    var state = cm.state.matchHighlighter;\n    if (state.overlay) {\n      cm.removeOverlay(state.overlay);\n      state.overlay = null;\n      if (state.matchesonscroll) {\n        state.matchesonscroll.clear();\n        state.matchesonscroll = null;\n      }\n    }\n  }\n\n  function highlightMatches(cm) {\n    cm.operation(function() {\n      var state = cm.state.matchHighlighter;\n      removeOverlay(cm);\n      if (!cm.somethingSelected() && state.options.showToken) {\n        var re = state.options.showToken === true ? /[\\w$]/ : state.options.showToken;\n        var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;\n        while (start && re.test(line.charAt(start - 1))) --start;\n        while (end < line.length && re.test(line.charAt(end))) ++end;\n        if (start < end)\n          addOverlay(cm, line.slice(start, end), re, state.options.style);\n        return;\n      }\n      var from = cm.getCursor(\"from\"), to = cm.getCursor(\"to\");\n      if (from.line != to.line) return;\n      if (state.options.wordsOnly && !isWord(cm, from, to)) return;\n      var selection = cm.getRange(from, to)\n      if (state.options.trim) selection = selection.replace(/^\\s+|\\s+$/g, \"\")\n      if (selection.length >= state.options.minChars)\n        addOverlay(cm, selection, false, state.options.style);\n    });\n  }\n\n  function isWord(cm, from, to) {\n    var str = cm.getRange(from, to);\n    if (str.match(/^\\w+$/) !== null) {\n        if (from.ch > 0) {\n            var pos = {line: from.line, ch: from.ch - 1};\n            var chr = cm.getRange(pos, from);\n            if (chr.match(/\\W/) === null) return false;\n        }\n        if (to.ch < cm.getLine(from.line).length) {\n            var pos = {line: to.line, ch: to.ch + 1};\n            var chr = cm.getRange(to, pos);\n            if (chr.match(/\\W/) === null) return false;\n        }\n        return true;\n    } else return false;\n  }\n\n  function boundariesAround(stream, re) {\n    return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&\n      (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));\n  }\n\n  function makeOverlay(query, hasBoundary, style) {\n    return {token: function(stream) {\n      if (stream.match(query) &&\n          (!hasBoundary || boundariesAround(stream, hasBoundary)))\n        return style;\n      stream.next();\n      stream.skipTo(query.charAt(0)) || stream.skipToEnd();\n    }};\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/search/matchesonscrollbar.css",
    "content": ".CodeMirror-search-match {\n  background: gold;\n  border-top: 1px solid orange;\n  border-bottom: 1px solid orange;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n  opacity: .5;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/search/matchesonscrollbar.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"./searchcursor\"), require(\"../scroll/annotatescrollbar\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"./searchcursor\", \"../scroll/annotatescrollbar\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineExtension(\"showMatchesOnScrollbar\", function(query, caseFold, options) {\n    if (typeof options == \"string\") options = {className: options};\n    if (!options) options = {};\n    return new SearchAnnotation(this, query, caseFold, options);\n  });\n\n  function SearchAnnotation(cm, query, caseFold, options) {\n    this.cm = cm;\n    this.options = options;\n    var annotateOptions = {listenForChanges: false};\n    for (var prop in options) annotateOptions[prop] = options[prop];\n    if (!annotateOptions.className) annotateOptions.className = \"CodeMirror-search-match\";\n    this.annotation = cm.annotateScrollbar(annotateOptions);\n    this.query = query;\n    this.caseFold = caseFold;\n    this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1};\n    this.matches = [];\n    this.update = null;\n\n    this.findMatches();\n    this.annotation.update(this.matches);\n\n    var self = this;\n    cm.on(\"change\", this.changeHandler = function(_cm, change) { self.onChange(change); });\n  }\n\n  var MAX_MATCHES = 1000;\n\n  SearchAnnotation.prototype.findMatches = function() {\n    if (!this.gap) return;\n    for (var i = 0; i < this.matches.length; i++) {\n      var match = this.matches[i];\n      if (match.from.line >= this.gap.to) break;\n      if (match.to.line >= this.gap.from) this.matches.splice(i--, 1);\n    }\n    var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), {caseFold: this.caseFold, multiline: this.options.multiline});\n    var maxMatches = this.options && this.options.maxMatches || MAX_MATCHES;\n    while (cursor.findNext()) {\n      var match = {from: cursor.from(), to: cursor.to()};\n      if (match.from.line >= this.gap.to) break;\n      this.matches.splice(i++, 0, match);\n      if (this.matches.length > maxMatches) break;\n    }\n    this.gap = null;\n  };\n\n  function offsetLine(line, changeStart, sizeChange) {\n    if (line <= changeStart) return line;\n    return Math.max(changeStart, line + sizeChange);\n  }\n\n  SearchAnnotation.prototype.onChange = function(change) {\n    var startLine = change.from.line;\n    var endLine = CodeMirror.changeEnd(change).line;\n    var sizeChange = endLine - change.to.line;\n    if (this.gap) {\n      this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line);\n      this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line);\n    } else {\n      this.gap = {from: change.from.line, to: endLine + 1};\n    }\n\n    if (sizeChange) for (var i = 0; i < this.matches.length; i++) {\n      var match = this.matches[i];\n      var newFrom = offsetLine(match.from.line, startLine, sizeChange);\n      if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch);\n      var newTo = offsetLine(match.to.line, startLine, sizeChange);\n      if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch);\n    }\n    clearTimeout(this.update);\n    var self = this;\n    this.update = setTimeout(function() { self.updateAfterChange(); }, 250);\n  };\n\n  SearchAnnotation.prototype.updateAfterChange = function() {\n    this.findMatches();\n    this.annotation.update(this.matches);\n  };\n\n  SearchAnnotation.prototype.clear = function() {\n    this.cm.off(\"change\", this.changeHandler);\n    this.annotation.clear();\n  };\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/search/search.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Define search commands. Depends on dialog.js or another\n// implementation of the openDialog method.\n\n// Replace works a little oddly -- it will do the replace on the next\n// Ctrl-G (or whatever is bound to findNext) press. You prevent a\n// replace by making sure the match is no longer selected when hitting\n// Ctrl-G.\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"./searchcursor\"), require(\"../dialog/dialog\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"./searchcursor\", \"../dialog/dialog\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  // default search panel location\n  CodeMirror.defineOption(\"search\", {bottom: false});\n\n  function searchOverlay(query, caseInsensitive) {\n    if (typeof query == \"string\")\n      query = new RegExp(query.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, \"\\\\$&\"), caseInsensitive ? \"gi\" : \"g\");\n    else if (!query.global)\n      query = new RegExp(query.source, query.ignoreCase ? \"gi\" : \"g\");\n\n    return {token: function(stream) {\n      query.lastIndex = stream.pos;\n      var match = query.exec(stream.string);\n      if (match && match.index == stream.pos) {\n        stream.pos += match[0].length || 1;\n        return \"searching\";\n      } else if (match) {\n        stream.pos = match.index;\n      } else {\n        stream.skipToEnd();\n      }\n    }};\n  }\n\n  function SearchState() {\n    this.posFrom = this.posTo = this.lastQuery = this.query = null;\n    this.overlay = null;\n  }\n\n  function getSearchState(cm) {\n    return cm.state.search || (cm.state.search = new SearchState());\n  }\n\n  function queryCaseInsensitive(query) {\n    return typeof query == \"string\" && query == query.toLowerCase();\n  }\n\n  function getSearchCursor(cm, query, pos) {\n    // Heuristic: if the query string is all lowercase, do a case insensitive search.\n    return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true});\n  }\n\n  function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {\n    cm.openDialog(text, onEnter, {\n      value: deflt,\n      selectValueOnOpen: true,\n      closeOnEnter: false,\n      onClose: function() { clearSearch(cm); },\n      onKeyDown: onKeyDown,\n      bottom: cm.options.search.bottom\n    });\n  }\n\n  function dialog(cm, text, shortText, deflt, f) {\n    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true, bottom: cm.options.search.bottom});\n    else f(prompt(shortText, deflt));\n  }\n\n  function confirmDialog(cm, text, shortText, fs) {\n    if (cm.openConfirm) cm.openConfirm(text, fs);\n    else if (confirm(shortText)) fs[0]();\n  }\n\n  function parseString(string) {\n    return string.replace(/\\\\([nrt\\\\])/g, function(match, ch) {\n      if (ch == \"n\") return \"\\n\"\n      if (ch == \"r\") return \"\\r\"\n      if (ch == \"t\") return \"\\t\"\n      if (ch == \"\\\\\") return \"\\\\\"\n      return match\n    })\n  }\n\n  function parseQuery(query) {\n    var isRE = query.match(/^\\/(.*)\\/([a-z]*)$/);\n    if (isRE) {\n      try { query = new RegExp(isRE[1], isRE[2].indexOf(\"i\") == -1 ? \"\" : \"i\"); }\n      catch(e) {} // Not a regular expression after all, do a string search\n    } else {\n      query = parseString(query)\n    }\n    if (typeof query == \"string\" ? query == \"\" : query.test(\"\"))\n      query = /x^/;\n    return query;\n  }\n\n  function startSearch(cm, state, query) {\n    state.queryText = query;\n    state.query = parseQuery(query);\n    cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));\n    state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));\n    cm.addOverlay(state.overlay);\n    if (cm.showMatchesOnScrollbar) {\n      if (state.annotate) { state.annotate.clear(); state.annotate = null; }\n      state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));\n    }\n  }\n\n  function doSearch(cm, rev, persistent, immediate) {\n    var state = getSearchState(cm);\n    if (state.query) return findNext(cm, rev);\n    var q = cm.getSelection() || state.lastQuery;\n    if (q instanceof RegExp && q.source == \"x^\") q = null\n    if (persistent && cm.openDialog) {\n      var hiding = null\n      var searchNext = function(query, event) {\n        CodeMirror.e_stop(event);\n        if (!query) return;\n        if (query != state.queryText) {\n          startSearch(cm, state, query);\n          state.posFrom = state.posTo = cm.getCursor();\n        }\n        if (hiding) hiding.style.opacity = 1\n        findNext(cm, event.shiftKey, function(_, to) {\n          var dialog\n          if (to.line < 3 && document.querySelector &&\n              (dialog = cm.display.wrapper.querySelector(\".CodeMirror-dialog\")) &&\n              dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, \"window\").top)\n            (hiding = dialog).style.opacity = .4\n        })\n      };\n      persistentDialog(cm, getQueryDialog(cm), q, searchNext, function(event, query) {\n        var keyName = CodeMirror.keyName(event)\n        var extra = cm.getOption('extraKeys'), cmd = (extra && extra[keyName]) || CodeMirror.keyMap[cm.getOption(\"keyMap\")][keyName]\n        if (cmd == \"findNext\" || cmd == \"findPrev\" ||\n          cmd == \"findPersistentNext\" || cmd == \"findPersistentPrev\") {\n          CodeMirror.e_stop(event);\n          startSearch(cm, getSearchState(cm), query);\n          cm.execCommand(cmd);\n        } else if (cmd == \"find\" || cmd == \"findPersistent\") {\n          CodeMirror.e_stop(event);\n          searchNext(query, event);\n        }\n      });\n      if (immediate && q) {\n        startSearch(cm, state, q);\n        findNext(cm, rev);\n      }\n    } else {\n      dialog(cm, getQueryDialog(cm), \"Search for:\", q, function(query) {\n        if (query && !state.query) cm.operation(function() {\n          startSearch(cm, state, query);\n          state.posFrom = state.posTo = cm.getCursor();\n          findNext(cm, rev);\n        });\n      });\n    }\n  }\n\n  function findNext(cm, rev, callback) {cm.operation(function() {\n    var state = getSearchState(cm);\n    var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);\n    if (!cursor.find(rev)) {\n      cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));\n      if (!cursor.find(rev)) return;\n    }\n    cm.setSelection(cursor.from(), cursor.to());\n    cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);\n    state.posFrom = cursor.from(); state.posTo = cursor.to();\n    if (callback) callback(cursor.from(), cursor.to())\n  });}\n\n  function clearSearch(cm) {cm.operation(function() {\n    var state = getSearchState(cm);\n    state.lastQuery = state.query;\n    if (!state.query) return;\n    state.query = state.queryText = null;\n    cm.removeOverlay(state.overlay);\n    if (state.annotate) { state.annotate.clear(); state.annotate = null; }\n  });}\n\n  function el(tag, attrs) {\n    var element = tag ? document.createElement(tag) : document.createDocumentFragment();\n    for (var key in attrs) {\n      element[key] = attrs[key];\n    }\n    for (var i = 2; i < arguments.length; i++) {\n      var child = arguments[i]\n      element.appendChild(typeof child == \"string\" ? document.createTextNode(child) : child);\n    }\n    return element;\n  }\n\n  function getQueryDialog(cm)  {\n    var label = el(\"label\", {className: \"CodeMirror-search-label\"},\n                   cm.phrase(\"Search:\"),\n                   el(\"input\", {type: \"text\", \"style\": \"width: 10em\", className: \"CodeMirror-search-field\",\n                                id: \"CodeMirror-search-field\"}));\n    label.setAttribute(\"for\",\"CodeMirror-search-field\");\n    return el(\"\", null, label, \" \",\n              el(\"span\", {style: \"color: #666\", className: \"CodeMirror-search-hint\"},\n                 cm.phrase(\"(Use /re/ syntax for regexp search)\")));\n  }\n  function getReplaceQueryDialog(cm) {\n    return el(\"\", null, \" \",\n              el(\"input\", {type: \"text\", \"style\": \"width: 10em\", className: \"CodeMirror-search-field\"}), \" \",\n              el(\"span\", {style: \"color: #666\", className: \"CodeMirror-search-hint\"},\n                 cm.phrase(\"(Use /re/ syntax for regexp search)\")));\n  }\n  function getReplacementQueryDialog(cm) {\n    return el(\"\", null,\n              el(\"span\", {className: \"CodeMirror-search-label\"}, cm.phrase(\"With:\")), \" \",\n              el(\"input\", {type: \"text\", \"style\": \"width: 10em\", className: \"CodeMirror-search-field\"}));\n  }\n  function getDoReplaceConfirm(cm) {\n    return el(\"\", null,\n              el(\"span\", {className: \"CodeMirror-search-label\"}, cm.phrase(\"Replace?\")), \" \",\n              el(\"button\", {}, cm.phrase(\"Yes\")), \" \",\n              el(\"button\", {}, cm.phrase(\"No\")), \" \",\n              el(\"button\", {}, cm.phrase(\"All\")), \" \",\n              el(\"button\", {}, cm.phrase(\"Stop\")));\n  }\n\n  function replaceAll(cm, query, text) {\n    cm.operation(function() {\n      for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {\n        if (typeof query != \"string\") {\n          var match = cm.getRange(cursor.from(), cursor.to()).match(query);\n          cursor.replace(text.replace(/\\$(\\d)/g, function(_, i) {return match[i];}));\n        } else cursor.replace(text);\n      }\n    });\n  }\n\n  function replace(cm, all) {\n    if (cm.getOption(\"readOnly\")) return;\n    var query = cm.getSelection() || getSearchState(cm).lastQuery;\n    var dialogText = all ? cm.phrase(\"Replace all:\") : cm.phrase(\"Replace:\")\n    var fragment = el(\"\", null,\n                      el(\"span\", {className: \"CodeMirror-search-label\"}, dialogText),\n                      getReplaceQueryDialog(cm))\n    dialog(cm, fragment, dialogText, query, function(query) {\n      if (!query) return;\n      query = parseQuery(query);\n      dialog(cm, getReplacementQueryDialog(cm), cm.phrase(\"Replace with:\"), \"\", function(text) {\n        text = parseString(text)\n        if (all) {\n          replaceAll(cm, query, text)\n        } else {\n          clearSearch(cm);\n          var cursor = getSearchCursor(cm, query, cm.getCursor(\"from\"));\n          var advance = function() {\n            var start = cursor.from(), match;\n            if (!(match = cursor.findNext())) {\n              cursor = getSearchCursor(cm, query);\n              if (!(match = cursor.findNext()) ||\n                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;\n            }\n            cm.setSelection(cursor.from(), cursor.to());\n            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});\n            confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase(\"Replace?\"),\n                          [function() {doReplace(match);}, advance,\n                           function() {replaceAll(cm, query, text)}]);\n          };\n          var doReplace = function(match) {\n            cursor.replace(typeof query == \"string\" ? text :\n                           text.replace(/\\$(\\d)/g, function(_, i) {return match[i];}));\n            advance();\n          };\n          advance();\n        }\n      });\n    });\n  }\n\n  CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};\n  CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};\n  CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};\n  CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};\n  CodeMirror.commands.findNext = doSearch;\n  CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};\n  CodeMirror.commands.clearSearch = clearSearch;\n  CodeMirror.commands.replace = replace;\n  CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/search/searchcursor.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"))\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod)\n  else // Plain browser env\n    mod(CodeMirror)\n})(function(CodeMirror) {\n  \"use strict\"\n  var Pos = CodeMirror.Pos\n\n  function regexpFlags(regexp) {\n    var flags = regexp.flags\n    return flags != null ? flags : (regexp.ignoreCase ? \"i\" : \"\")\n      + (regexp.global ? \"g\" : \"\")\n      + (regexp.multiline ? \"m\" : \"\")\n  }\n\n  function ensureFlags(regexp, flags) {\n    var current = regexpFlags(regexp), target = current\n    for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1)\n      target += flags.charAt(i)\n    return current == target ? regexp : new RegExp(regexp.source, target)\n  }\n\n  function maybeMultiline(regexp) {\n    return /\\\\s|\\\\n|\\n|\\\\W|\\\\D|\\[\\^/.test(regexp.source)\n  }\n\n  function searchRegexpForward(doc, regexp, start) {\n    regexp = ensureFlags(regexp, \"g\")\n    for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {\n      regexp.lastIndex = ch\n      var string = doc.getLine(line), match = regexp.exec(string)\n      if (match)\n        return {from: Pos(line, match.index),\n                to: Pos(line, match.index + match[0].length),\n                match: match}\n    }\n  }\n\n  function searchRegexpForwardMultiline(doc, regexp, start) {\n    if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)\n\n    regexp = ensureFlags(regexp, \"gm\")\n    var string, chunk = 1\n    for (var line = start.line, last = doc.lastLine(); line <= last;) {\n      // This grows the search buffer in exponentially-sized chunks\n      // between matches, so that nearby matches are fast and don't\n      // require concatenating the whole document (in case we're\n      // searching for something that has tons of matches), but at the\n      // same time, the amount of retries is limited.\n      for (var i = 0; i < chunk; i++) {\n        if (line > last) break\n        var curLine = doc.getLine(line++)\n        string = string == null ? curLine : string + \"\\n\" + curLine\n      }\n      chunk = chunk * 2\n      regexp.lastIndex = start.ch\n      var match = regexp.exec(string)\n      if (match) {\n        var before = string.slice(0, match.index).split(\"\\n\"), inside = match[0].split(\"\\n\")\n        var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length\n        return {from: Pos(startLine, startCh),\n                to: Pos(startLine + inside.length - 1,\n                        inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),\n                match: match}\n      }\n    }\n  }\n\n  function lastMatchIn(string, regexp, endMargin) {\n    var match, from = 0\n    while (from <= string.length) {\n      regexp.lastIndex = from\n      var newMatch = regexp.exec(string)\n      if (!newMatch) break\n      var end = newMatch.index + newMatch[0].length\n      if (end > string.length - endMargin) break\n      if (!match || end > match.index + match[0].length)\n        match = newMatch\n      from = newMatch.index + 1\n    }\n    return match\n  }\n\n  function searchRegexpBackward(doc, regexp, start) {\n    regexp = ensureFlags(regexp, \"g\")\n    for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {\n      var string = doc.getLine(line)\n      var match = lastMatchIn(string, regexp, ch < 0 ? 0 : string.length - ch)\n      if (match)\n        return {from: Pos(line, match.index),\n                to: Pos(line, match.index + match[0].length),\n                match: match}\n    }\n  }\n\n  function searchRegexpBackwardMultiline(doc, regexp, start) {\n    if (!maybeMultiline(regexp)) return searchRegexpBackward(doc, regexp, start)\n    regexp = ensureFlags(regexp, \"gm\")\n    var string, chunkSize = 1, endMargin = doc.getLine(start.line).length - start.ch\n    for (var line = start.line, first = doc.firstLine(); line >= first;) {\n      for (var i = 0; i < chunkSize && line >= first; i++) {\n        var curLine = doc.getLine(line--)\n        string = string == null ? curLine : curLine + \"\\n\" + string\n      }\n      chunkSize *= 2\n\n      var match = lastMatchIn(string, regexp, endMargin)\n      if (match) {\n        var before = string.slice(0, match.index).split(\"\\n\"), inside = match[0].split(\"\\n\")\n        var startLine = line + before.length, startCh = before[before.length - 1].length\n        return {from: Pos(startLine, startCh),\n                to: Pos(startLine + inside.length - 1,\n                        inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),\n                match: match}\n      }\n    }\n  }\n\n  var doFold, noFold\n  if (String.prototype.normalize) {\n    doFold = function(str) { return str.normalize(\"NFD\").toLowerCase() }\n    noFold = function(str) { return str.normalize(\"NFD\") }\n  } else {\n    doFold = function(str) { return str.toLowerCase() }\n    noFold = function(str) { return str }\n  }\n\n  // Maps a position in a case-folded line back to a position in the original line\n  // (compensating for codepoints increasing in number during folding)\n  function adjustPos(orig, folded, pos, foldFunc) {\n    if (orig.length == folded.length) return pos\n    for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) {\n      if (min == max) return min\n      var mid = (min + max) >> 1\n      var len = foldFunc(orig.slice(0, mid)).length\n      if (len == pos) return mid\n      else if (len > pos) max = mid\n      else min = mid + 1\n    }\n  }\n\n  function searchStringForward(doc, query, start, caseFold) {\n    // Empty string would match anything and never progress, so we\n    // define it to match nothing instead.\n    if (!query.length) return null\n    var fold = caseFold ? doFold : noFold\n    var lines = fold(query).split(/\\r|\\n\\r?/)\n\n    search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) {\n      var orig = doc.getLine(line).slice(ch), string = fold(orig)\n      if (lines.length == 1) {\n        var found = string.indexOf(lines[0])\n        if (found == -1) continue search\n        var start = adjustPos(orig, string, found, fold) + ch\n        return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),\n                to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}\n      } else {\n        var cutFrom = string.length - lines[0].length\n        if (string.slice(cutFrom) != lines[0]) continue search\n        for (var i = 1; i < lines.length - 1; i++)\n          if (fold(doc.getLine(line + i)) != lines[i]) continue search\n        var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]\n        if (endString.slice(0, lastLine.length) != lastLine) continue search\n        return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),\n                to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}\n      }\n    }\n  }\n\n  function searchStringBackward(doc, query, start, caseFold) {\n    if (!query.length) return null\n    var fold = caseFold ? doFold : noFold\n    var lines = fold(query).split(/\\r|\\n\\r?/)\n\n    search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) {\n      var orig = doc.getLine(line)\n      if (ch > -1) orig = orig.slice(0, ch)\n      var string = fold(orig)\n      if (lines.length == 1) {\n        var found = string.lastIndexOf(lines[0])\n        if (found == -1) continue search\n        return {from: Pos(line, adjustPos(orig, string, found, fold)),\n                to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))}\n      } else {\n        var lastLine = lines[lines.length - 1]\n        if (string.slice(0, lastLine.length) != lastLine) continue search\n        for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++)\n          if (fold(doc.getLine(start + i)) != lines[i]) continue search\n        var top = doc.getLine(line + 1 - lines.length), topString = fold(top)\n        if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search\n        return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)),\n                to: Pos(line, adjustPos(orig, string, lastLine.length, fold))}\n      }\n    }\n  }\n\n  function SearchCursor(doc, query, pos, options) {\n    this.atOccurrence = false\n    this.afterEmptyMatch = false\n    this.doc = doc\n    pos = pos ? doc.clipPos(pos) : Pos(0, 0)\n    this.pos = {from: pos, to: pos}\n\n    var caseFold\n    if (typeof options == \"object\") {\n      caseFold = options.caseFold\n    } else { // Backwards compat for when caseFold was the 4th argument\n      caseFold = options\n      options = null\n    }\n\n    if (typeof query == \"string\") {\n      if (caseFold == null) caseFold = false\n      this.matches = function(reverse, pos) {\n        return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)\n      }\n    } else {\n      query = ensureFlags(query, \"gm\")\n      if (!options || options.multiline !== false)\n        this.matches = function(reverse, pos) {\n          return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)\n        }\n      else\n        this.matches = function(reverse, pos) {\n          return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)\n        }\n    }\n  }\n\n  SearchCursor.prototype = {\n    findNext: function() {return this.find(false)},\n    findPrevious: function() {return this.find(true)},\n\n    find: function(reverse) {\n      var head = this.doc.clipPos(reverse ? this.pos.from : this.pos.to);\n      if (this.afterEmptyMatch && this.atOccurrence) {\n        // do not return the same 0 width match twice\n        head = Pos(head.line, head.ch)\n        if (reverse) {\n          head.ch--;\n          if (head.ch < 0) {\n            head.line--;\n            head.ch = (this.doc.getLine(head.line) || \"\").length;\n          }\n        } else {\n          head.ch++;\n          if (head.ch > (this.doc.getLine(head.line) || \"\").length) {\n            head.ch = 0;\n            head.line++;\n          }\n        }\n        if (CodeMirror.cmpPos(head, this.doc.clipPos(head)) != 0) {\n           return this.atOccurrence = false\n        }\n      }\n      var result = this.matches(reverse, head)\n      this.afterEmptyMatch = result && CodeMirror.cmpPos(result.from, result.to) == 0\n\n      if (result) {\n        this.pos = result\n        this.atOccurrence = true\n        return this.pos.match || true\n      } else {\n        var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0)\n        this.pos = {from: end, to: end}\n        return this.atOccurrence = false\n      }\n    },\n\n    from: function() {if (this.atOccurrence) return this.pos.from},\n    to: function() {if (this.atOccurrence) return this.pos.to},\n\n    replace: function(newText, origin) {\n      if (!this.atOccurrence) return\n      var lines = CodeMirror.splitLines(newText)\n      this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin)\n      this.pos.to = Pos(this.pos.from.line + lines.length - 1,\n                        lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))\n    }\n  }\n\n  CodeMirror.defineExtension(\"getSearchCursor\", function(query, pos, caseFold) {\n    return new SearchCursor(this.doc, query, pos, caseFold)\n  })\n  CodeMirror.defineDocExtension(\"getSearchCursor\", function(query, pos, caseFold) {\n    return new SearchCursor(this, query, pos, caseFold)\n  })\n\n  CodeMirror.defineExtension(\"selectMatches\", function(query, caseFold) {\n    var ranges = []\n    var cur = this.getSearchCursor(query, this.getCursor(\"from\"), caseFold)\n    while (cur.findNext()) {\n      if (CodeMirror.cmpPos(cur.to(), this.getCursor(\"to\")) > 0) break\n      ranges.push({anchor: cur.from(), head: cur.to()})\n    }\n    if (ranges.length)\n      this.setSelections(ranges, 0)\n  })\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/selection/active-line.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n  var WRAP_CLASS = \"CodeMirror-activeline\";\n  var BACK_CLASS = \"CodeMirror-activeline-background\";\n  var GUTT_CLASS = \"CodeMirror-activeline-gutter\";\n\n  CodeMirror.defineOption(\"styleActiveLine\", false, function(cm, val, old) {\n    var prev = old == CodeMirror.Init ? false : old;\n    if (val == prev) return\n    if (prev) {\n      cm.off(\"beforeSelectionChange\", selectionChange);\n      clearActiveLines(cm);\n      delete cm.state.activeLines;\n    }\n    if (val) {\n      cm.state.activeLines = [];\n      updateActiveLines(cm, cm.listSelections());\n      cm.on(\"beforeSelectionChange\", selectionChange);\n    }\n  });\n\n  function clearActiveLines(cm) {\n    for (var i = 0; i < cm.state.activeLines.length; i++) {\n      cm.removeLineClass(cm.state.activeLines[i], \"wrap\", WRAP_CLASS);\n      cm.removeLineClass(cm.state.activeLines[i], \"background\", BACK_CLASS);\n      cm.removeLineClass(cm.state.activeLines[i], \"gutter\", GUTT_CLASS);\n    }\n  }\n\n  function sameArray(a, b) {\n    if (a.length != b.length) return false;\n    for (var i = 0; i < a.length; i++)\n      if (a[i] != b[i]) return false;\n    return true;\n  }\n\n  function updateActiveLines(cm, ranges) {\n    var active = [];\n    for (var i = 0; i < ranges.length; i++) {\n      var range = ranges[i];\n      var option = cm.getOption(\"styleActiveLine\");\n      if (typeof option == \"object\" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty())\n        continue\n      var line = cm.getLineHandleVisualStart(range.head.line);\n      if (active[active.length - 1] != line) active.push(line);\n    }\n    if (sameArray(cm.state.activeLines, active)) return;\n    cm.operation(function() {\n      clearActiveLines(cm);\n      for (var i = 0; i < active.length; i++) {\n        cm.addLineClass(active[i], \"wrap\", WRAP_CLASS);\n        cm.addLineClass(active[i], \"background\", BACK_CLASS);\n        cm.addLineClass(active[i], \"gutter\", GUTT_CLASS);\n      }\n      cm.state.activeLines = active;\n    });\n  }\n\n  function selectionChange(cm, sel) {\n    updateActiveLines(cm, sel.ranges);\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/selection/mark-selection.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Because sometimes you need to mark the selected *text*.\n//\n// Adds an option 'styleSelectedText' which, when enabled, gives\n// selected text the CSS class given as option value, or\n// \"CodeMirror-selectedtext\" when the value is not a string.\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineOption(\"styleSelectedText\", false, function(cm, val, old) {\n    var prev = old && old != CodeMirror.Init;\n    if (val && !prev) {\n      cm.state.markedSelection = [];\n      cm.state.markedSelectionStyle = typeof val == \"string\" ? val : \"CodeMirror-selectedtext\";\n      reset(cm);\n      cm.on(\"cursorActivity\", onCursorActivity);\n      cm.on(\"change\", onChange);\n    } else if (!val && prev) {\n      cm.off(\"cursorActivity\", onCursorActivity);\n      cm.off(\"change\", onChange);\n      clear(cm);\n      cm.state.markedSelection = cm.state.markedSelectionStyle = null;\n    }\n  });\n\n  function onCursorActivity(cm) {\n    if (cm.state.markedSelection)\n      cm.operation(function() { update(cm); });\n  }\n\n  function onChange(cm) {\n    if (cm.state.markedSelection && cm.state.markedSelection.length)\n      cm.operation(function() { clear(cm); });\n  }\n\n  var CHUNK_SIZE = 8;\n  var Pos = CodeMirror.Pos;\n  var cmp = CodeMirror.cmpPos;\n\n  function coverRange(cm, from, to, addAt) {\n    if (cmp(from, to) == 0) return;\n    var array = cm.state.markedSelection;\n    var cls = cm.state.markedSelectionStyle;\n    for (var line = from.line;;) {\n      var start = line == from.line ? from : Pos(line, 0);\n      var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line;\n      var end = atEnd ? to : Pos(endLine, 0);\n      var mark = cm.markText(start, end, {className: cls});\n      if (addAt == null) array.push(mark);\n      else array.splice(addAt++, 0, mark);\n      if (atEnd) break;\n      line = endLine;\n    }\n  }\n\n  function clear(cm) {\n    var array = cm.state.markedSelection;\n    for (var i = 0; i < array.length; ++i) array[i].clear();\n    array.length = 0;\n  }\n\n  function reset(cm) {\n    clear(cm);\n    var ranges = cm.listSelections();\n    for (var i = 0; i < ranges.length; i++)\n      coverRange(cm, ranges[i].from(), ranges[i].to());\n  }\n\n  function update(cm) {\n    if (!cm.somethingSelected()) return clear(cm);\n    if (cm.listSelections().length > 1) return reset(cm);\n\n    var from = cm.getCursor(\"start\"), to = cm.getCursor(\"end\");\n\n    var array = cm.state.markedSelection;\n    if (!array.length) return coverRange(cm, from, to);\n\n    var coverStart = array[0].find(), coverEnd = array[array.length - 1].find();\n    if (!coverStart || !coverEnd || to.line - from.line <= CHUNK_SIZE ||\n        cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0)\n      return reset(cm);\n\n    while (cmp(from, coverStart.from) > 0) {\n      array.shift().clear();\n      coverStart = array[0].find();\n    }\n    if (cmp(from, coverStart.from) < 0) {\n      if (coverStart.to.line - from.line < CHUNK_SIZE) {\n        array.shift().clear();\n        coverRange(cm, from, coverStart.to, 0);\n      } else {\n        coverRange(cm, from, coverStart.from, 0);\n      }\n    }\n\n    while (cmp(to, coverEnd.to) < 0) {\n      array.pop().clear();\n      coverEnd = array[array.length - 1].find();\n    }\n    if (cmp(to, coverEnd.to) > 0) {\n      if (to.line - coverEnd.from.line < CHUNK_SIZE) {\n        array.pop().clear();\n        coverRange(cm, coverEnd.from, to);\n      } else {\n        coverRange(cm, coverEnd.to, to);\n      }\n    }\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/selection/selection-pointer.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineOption(\"selectionPointer\", false, function(cm, val) {\n    var data = cm.state.selectionPointer;\n    if (data) {\n      CodeMirror.off(cm.getWrapperElement(), \"mousemove\", data.mousemove);\n      CodeMirror.off(cm.getWrapperElement(), \"mouseout\", data.mouseout);\n      CodeMirror.off(window, \"scroll\", data.windowScroll);\n      cm.off(\"cursorActivity\", reset);\n      cm.off(\"scroll\", reset);\n      cm.state.selectionPointer = null;\n      cm.display.lineDiv.style.cursor = \"\";\n    }\n    if (val) {\n      data = cm.state.selectionPointer = {\n        value: typeof val == \"string\" ? val : \"default\",\n        mousemove: function(event) { mousemove(cm, event); },\n        mouseout: function(event) { mouseout(cm, event); },\n        windowScroll: function() { reset(cm); },\n        rects: null,\n        mouseX: null, mouseY: null,\n        willUpdate: false\n      };\n      CodeMirror.on(cm.getWrapperElement(), \"mousemove\", data.mousemove);\n      CodeMirror.on(cm.getWrapperElement(), \"mouseout\", data.mouseout);\n      CodeMirror.on(window, \"scroll\", data.windowScroll);\n      cm.on(\"cursorActivity\", reset);\n      cm.on(\"scroll\", reset);\n    }\n  });\n\n  function mousemove(cm, event) {\n    var data = cm.state.selectionPointer;\n    if (event.buttons == null ? event.which : event.buttons) {\n      data.mouseX = data.mouseY = null;\n    } else {\n      data.mouseX = event.clientX;\n      data.mouseY = event.clientY;\n    }\n    scheduleUpdate(cm);\n  }\n\n  function mouseout(cm, event) {\n    if (!cm.getWrapperElement().contains(event.relatedTarget)) {\n      var data = cm.state.selectionPointer;\n      data.mouseX = data.mouseY = null;\n      scheduleUpdate(cm);\n    }\n  }\n\n  function reset(cm) {\n    cm.state.selectionPointer.rects = null;\n    scheduleUpdate(cm);\n  }\n\n  function scheduleUpdate(cm) {\n    if (!cm.state.selectionPointer.willUpdate) {\n      cm.state.selectionPointer.willUpdate = true;\n      setTimeout(function() {\n        update(cm);\n        cm.state.selectionPointer.willUpdate = false;\n      }, 50);\n    }\n  }\n\n  function update(cm) {\n    var data = cm.state.selectionPointer;\n    if (!data) return;\n    if (data.rects == null && data.mouseX != null) {\n      data.rects = [];\n      if (cm.somethingSelected()) {\n        for (var sel = cm.display.selectionDiv.firstChild; sel; sel = sel.nextSibling)\n          data.rects.push(sel.getBoundingClientRect());\n      }\n    }\n    var inside = false;\n    if (data.mouseX != null) for (var i = 0; i < data.rects.length; i++) {\n      var rect = data.rects[i];\n      if (rect.left <= data.mouseX && rect.right >= data.mouseX &&\n          rect.top <= data.mouseY && rect.bottom >= data.mouseY)\n        inside = true;\n    }\n    var cursor = inside ? data.value : \"\";\n    if (cm.display.lineDiv.style.cursor != cursor)\n      cm.display.lineDiv.style.cursor = cursor;\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/tern/tern.css",
    "content": ".CodeMirror-Tern-completion {\n  padding-left: 22px;\n  position: relative;\n  line-height: 1.5;\n}\n.CodeMirror-Tern-completion:before {\n  position: absolute;\n  left: 2px;\n  bottom: 2px;\n  border-radius: 50%;\n  font-size: 12px;\n  font-weight: bold;\n  height: 15px;\n  width: 15px;\n  line-height: 16px;\n  text-align: center;\n  color: white;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n}\n.CodeMirror-Tern-completion-unknown:before {\n  content: \"?\";\n  background: #4bb;\n}\n.CodeMirror-Tern-completion-object:before {\n  content: \"O\";\n  background: #77c;\n}\n.CodeMirror-Tern-completion-fn:before {\n  content: \"F\";\n  background: #7c7;\n}\n.CodeMirror-Tern-completion-array:before {\n  content: \"A\";\n  background: #c66;\n}\n.CodeMirror-Tern-completion-number:before {\n  content: \"1\";\n  background: #999;\n}\n.CodeMirror-Tern-completion-string:before {\n  content: \"S\";\n  background: #999;\n}\n.CodeMirror-Tern-completion-bool:before {\n  content: \"B\";\n  background: #999;\n}\n\n.CodeMirror-Tern-completion-guess {\n  color: #999;\n}\n\n.CodeMirror-Tern-tooltip {\n  border: 1px solid silver;\n  border-radius: 3px;\n  color: #444;\n  padding: 2px 5px;\n  font-size: 90%;\n  font-family: monospace;\n  background-color: white;\n  white-space: pre-wrap;\n\n  max-width: 40em;\n  position: absolute;\n  z-index: 10;\n  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);\n  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);\n  box-shadow: 2px 3px 5px rgba(0,0,0,.2);\n\n  transition: opacity 1s;\n  -moz-transition: opacity 1s;\n  -webkit-transition: opacity 1s;\n  -o-transition: opacity 1s;\n  -ms-transition: opacity 1s;\n}\n\n.CodeMirror-Tern-hint-doc {\n  max-width: 25em;\n  margin-top: -3px;\n}\n\n.CodeMirror-Tern-fname { color: black; }\n.CodeMirror-Tern-farg { color: #70a; }\n.CodeMirror-Tern-farg-current { text-decoration: underline; }\n.CodeMirror-Tern-type { color: #07c; }\n.CodeMirror-Tern-fhint-guess { opacity: .7; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/tern/tern.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Glue code between CodeMirror and Tern.\n//\n// Create a CodeMirror.TernServer to wrap an actual Tern server,\n// register open documents (CodeMirror.Doc instances) with it, and\n// call its methods to activate the assisting functions that Tern\n// provides.\n//\n// Options supported (all optional):\n// * defs: An array of JSON definition data structures.\n// * plugins: An object mapping plugin names to configuration\n//   options.\n// * getFile: A function(name, c) that can be used to access files in\n//   the project that haven't been loaded yet. Simply do c(null) to\n//   indicate that a file is not available.\n// * fileFilter: A function(value, docName, doc) that will be applied\n//   to documents before passing them on to Tern.\n// * switchToDoc: A function(name, doc) that should, when providing a\n//   multi-file view, switch the view or focus to the named file.\n// * showError: A function(editor, message) that can be used to\n//   override the way errors are displayed.\n// * completionTip: Customize the content in tooltips for completions.\n//   Is passed a single argument—the completion's data as returned by\n//   Tern—and may return a string, DOM node, or null to indicate that\n//   no tip should be shown. By default the docstring is shown.\n// * typeTip: Like completionTip, but for the tooltips shown for type\n//   queries.\n// * responseFilter: A function(doc, query, request, error, data) that\n//   will be applied to the Tern responses before treating them\n//\n//\n// It is possible to run the Tern server in a web worker by specifying\n// these additional options:\n// * useWorker: Set to true to enable web worker mode. You'll probably\n//   want to feature detect the actual value you use here, for example\n//   !!window.Worker.\n// * workerScript: The main script of the worker. Point this to\n//   wherever you are hosting worker.js from this directory.\n// * workerDeps: An array of paths pointing (relative to workerScript)\n//   to the Acorn and Tern libraries and any Tern plugins you want to\n//   load. Or, if you minified those into a single script and included\n//   them in the workerScript, simply leave this undefined.\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n  // declare global: tern\n\n  CodeMirror.TernServer = function(options) {\n    var self = this;\n    this.options = options || {};\n    var plugins = this.options.plugins || (this.options.plugins = {});\n    if (!plugins.doc_comment) plugins.doc_comment = true;\n    this.docs = Object.create(null);\n    if (this.options.useWorker) {\n      this.server = new WorkerServer(this);\n    } else {\n      this.server = new tern.Server({\n        getFile: function(name, c) { return getFile(self, name, c); },\n        async: true,\n        defs: this.options.defs || [],\n        plugins: plugins\n      });\n    }\n    this.trackChange = function(doc, change) { trackChange(self, doc, change); };\n\n    this.cachedArgHints = null;\n    this.activeArgHints = null;\n    this.jumpStack = [];\n\n    this.getHint = function(cm, c) { return hint(self, cm, c); };\n    this.getHint.async = true;\n  };\n\n  CodeMirror.TernServer.prototype = {\n    addDoc: function(name, doc) {\n      var data = {doc: doc, name: name, changed: null};\n      this.server.addFile(name, docValue(this, data));\n      CodeMirror.on(doc, \"change\", this.trackChange);\n      return this.docs[name] = data;\n    },\n\n    delDoc: function(id) {\n      var found = resolveDoc(this, id);\n      if (!found) return;\n      CodeMirror.off(found.doc, \"change\", this.trackChange);\n      delete this.docs[found.name];\n      this.server.delFile(found.name);\n    },\n\n    hideDoc: function(id) {\n      closeArgHints(this);\n      var found = resolveDoc(this, id);\n      if (found && found.changed) sendDoc(this, found);\n    },\n\n    complete: function(cm) {\n      cm.showHint({hint: this.getHint});\n    },\n\n    showType: function(cm, pos, c) { showContextInfo(this, cm, pos, \"type\", c); },\n\n    showDocs: function(cm, pos, c) { showContextInfo(this, cm, pos, \"documentation\", c); },\n\n    updateArgHints: function(cm) { updateArgHints(this, cm); },\n\n    jumpToDef: function(cm) { jumpToDef(this, cm); },\n\n    jumpBack: function(cm) { jumpBack(this, cm); },\n\n    rename: function(cm) { rename(this, cm); },\n\n    selectName: function(cm) { selectName(this, cm); },\n\n    request: function (cm, query, c, pos) {\n      var self = this;\n      var doc = findDoc(this, cm.getDoc());\n      var request = buildRequest(this, doc, query, pos);\n      var extraOptions = request.query && this.options.queryOptions && this.options.queryOptions[request.query.type]\n      if (extraOptions) for (var prop in extraOptions) request.query[prop] = extraOptions[prop];\n\n      this.server.request(request, function (error, data) {\n        if (!error && self.options.responseFilter)\n          data = self.options.responseFilter(doc, query, request, error, data);\n        c(error, data);\n      });\n    },\n\n    destroy: function () {\n      closeArgHints(this)\n      if (this.worker) {\n        this.worker.terminate();\n        this.worker = null;\n      }\n    }\n  };\n\n  var Pos = CodeMirror.Pos;\n  var cls = \"CodeMirror-Tern-\";\n  var bigDoc = 250;\n\n  function getFile(ts, name, c) {\n    var buf = ts.docs[name];\n    if (buf)\n      c(docValue(ts, buf));\n    else if (ts.options.getFile)\n      ts.options.getFile(name, c);\n    else\n      c(null);\n  }\n\n  function findDoc(ts, doc, name) {\n    for (var n in ts.docs) {\n      var cur = ts.docs[n];\n      if (cur.doc == doc) return cur;\n    }\n    if (!name) for (var i = 0;; ++i) {\n      n = \"[doc\" + (i || \"\") + \"]\";\n      if (!ts.docs[n]) { name = n; break; }\n    }\n    return ts.addDoc(name, doc);\n  }\n\n  function resolveDoc(ts, id) {\n    if (typeof id == \"string\") return ts.docs[id];\n    if (id instanceof CodeMirror) id = id.getDoc();\n    if (id instanceof CodeMirror.Doc) return findDoc(ts, id);\n  }\n\n  function trackChange(ts, doc, change) {\n    var data = findDoc(ts, doc);\n\n    var argHints = ts.cachedArgHints;\n    if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) >= 0)\n      ts.cachedArgHints = null;\n\n    var changed = data.changed;\n    if (changed == null)\n      data.changed = changed = {from: change.from.line, to: change.from.line};\n    var end = change.from.line + (change.text.length - 1);\n    if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end);\n    if (end >= changed.to) changed.to = end + 1;\n    if (changed.from > change.from.line) changed.from = change.from.line;\n\n    if (doc.lineCount() > bigDoc && change.to - changed.from > 100) setTimeout(function() {\n      if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data);\n    }, 200);\n  }\n\n  function sendDoc(ts, doc) {\n    ts.server.request({files: [{type: \"full\", name: doc.name, text: docValue(ts, doc)}]}, function(error) {\n      if (error) window.console.error(error);\n      else doc.changed = null;\n    });\n  }\n\n  // Completion\n\n  function hint(ts, cm, c) {\n    ts.request(cm, {type: \"completions\", types: true, docs: true, urls: true}, function(error, data) {\n      if (error) return showError(ts, cm, error);\n      var completions = [], after = \"\";\n      var from = data.start, to = data.end;\n      if (cm.getRange(Pos(from.line, from.ch - 2), from) == \"[\\\"\" &&\n          cm.getRange(to, Pos(to.line, to.ch + 2)) != \"\\\"]\")\n        after = \"\\\"]\";\n\n      for (var i = 0; i < data.completions.length; ++i) {\n        var completion = data.completions[i], className = typeToIcon(completion.type);\n        if (data.guess) className += \" \" + cls + \"guess\";\n        completions.push({text: completion.name + after,\n                          displayText: completion.displayName || completion.name,\n                          className: className,\n                          data: completion});\n      }\n\n      var obj = {from: from, to: to, list: completions};\n      var tooltip = null;\n      CodeMirror.on(obj, \"close\", function() { remove(tooltip); });\n      CodeMirror.on(obj, \"update\", function() { remove(tooltip); });\n      CodeMirror.on(obj, \"select\", function(cur, node) {\n        remove(tooltip);\n        var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc;\n        if (content) {\n          tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset,\n                                node.getBoundingClientRect().top + window.pageYOffset, content, cm, cls + \"hint-doc\");\n        }\n      });\n      c(obj);\n    });\n  }\n\n  function typeToIcon(type) {\n    var suffix;\n    if (type == \"?\") suffix = \"unknown\";\n    else if (type == \"number\" || type == \"string\" || type == \"bool\") suffix = type;\n    else if (/^fn\\(/.test(type)) suffix = \"fn\";\n    else if (/^\\[/.test(type)) suffix = \"array\";\n    else suffix = \"object\";\n    return cls + \"completion \" + cls + \"completion-\" + suffix;\n  }\n\n  // Type queries\n\n  function showContextInfo(ts, cm, pos, queryName, c) {\n    ts.request(cm, queryName, function(error, data) {\n      if (error) return showError(ts, cm, error);\n      if (ts.options.typeTip) {\n        var tip = ts.options.typeTip(data);\n      } else {\n        var tip = elt(\"span\", null, elt(\"strong\", null, data.type || \"not found\"));\n        if (data.doc)\n          tip.appendChild(document.createTextNode(\" — \" + data.doc));\n        if (data.url) {\n          tip.appendChild(document.createTextNode(\" \"));\n          var child = tip.appendChild(elt(\"a\", null, \"[docs]\"));\n          child.href = data.url;\n          child.target = \"_blank\";\n        }\n      }\n      tempTooltip(cm, tip, ts);\n      if (c) c();\n    }, pos);\n  }\n\n  // Maintaining argument hints\n\n  function updateArgHints(ts, cm) {\n    closeArgHints(ts);\n\n    if (cm.somethingSelected()) return;\n    var state = cm.getTokenAt(cm.getCursor()).state;\n    var inner = CodeMirror.innerMode(cm.getMode(), state);\n    if (inner.mode.name != \"javascript\") return;\n    var lex = inner.state.lexical;\n    if (lex.info != \"call\") return;\n\n    var ch, argPos = lex.pos || 0, tabSize = cm.getOption(\"tabSize\");\n    for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) {\n      var str = cm.getLine(line), extra = 0;\n      for (var pos = 0;;) {\n        var tab = str.indexOf(\"\\t\", pos);\n        if (tab == -1) break;\n        extra += tabSize - (tab + extra) % tabSize - 1;\n        pos = tab + 1;\n      }\n      ch = lex.column - extra;\n      if (str.charAt(ch) == \"(\") {found = true; break;}\n    }\n    if (!found) return;\n\n    var start = Pos(line, ch);\n    var cache = ts.cachedArgHints;\n    if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0)\n      return showArgHints(ts, cm, argPos);\n\n    ts.request(cm, {type: \"type\", preferFunction: true, end: start}, function(error, data) {\n      if (error || !data.type || !(/^fn\\(/).test(data.type)) return;\n      ts.cachedArgHints = {\n        start: start,\n        type: parseFnType(data.type),\n        name: data.exprName || data.name || \"fn\",\n        guess: data.guess,\n        doc: cm.getDoc()\n      };\n      showArgHints(ts, cm, argPos);\n    });\n  }\n\n  function showArgHints(ts, cm, pos) {\n    closeArgHints(ts);\n\n    var cache = ts.cachedArgHints, tp = cache.type;\n    var tip = elt(\"span\", cache.guess ? cls + \"fhint-guess\" : null,\n                  elt(\"span\", cls + \"fname\", cache.name), \"(\");\n    for (var i = 0; i < tp.args.length; ++i) {\n      if (i) tip.appendChild(document.createTextNode(\", \"));\n      var arg = tp.args[i];\n      tip.appendChild(elt(\"span\", cls + \"farg\" + (i == pos ? \" \" + cls + \"farg-current\" : \"\"), arg.name || \"?\"));\n      if (arg.type != \"?\") {\n        tip.appendChild(document.createTextNode(\":\\u00a0\"));\n        tip.appendChild(elt(\"span\", cls + \"type\", arg.type));\n      }\n    }\n    tip.appendChild(document.createTextNode(tp.rettype ? \") ->\\u00a0\" : \")\"));\n    if (tp.rettype) tip.appendChild(elt(\"span\", cls + \"type\", tp.rettype));\n    var place = cm.cursorCoords(null, \"page\");\n    var tooltip = ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip, cm)\n    setTimeout(function() {\n      tooltip.clear = onEditorActivity(cm, function() {\n        if (ts.activeArgHints == tooltip) closeArgHints(ts) })\n    }, 20)\n  }\n\n  function parseFnType(text) {\n    var args = [], pos = 3;\n\n    function skipMatching(upto) {\n      var depth = 0, start = pos;\n      for (;;) {\n        var next = text.charAt(pos);\n        if (upto.test(next) && !depth) return text.slice(start, pos);\n        if (/[{\\[\\(]/.test(next)) ++depth;\n        else if (/[}\\]\\)]/.test(next)) --depth;\n        ++pos;\n      }\n    }\n\n    // Parse arguments\n    if (text.charAt(pos) != \")\") for (;;) {\n      var name = text.slice(pos).match(/^([^, \\(\\[\\{]+): /);\n      if (name) {\n        pos += name[0].length;\n        name = name[1];\n      }\n      args.push({name: name, type: skipMatching(/[\\),]/)});\n      if (text.charAt(pos) == \")\") break;\n      pos += 2;\n    }\n\n    var rettype = text.slice(pos).match(/^\\) -> (.*)$/);\n\n    return {args: args, rettype: rettype && rettype[1]};\n  }\n\n  // Moving to the definition of something\n\n  function jumpToDef(ts, cm) {\n    function inner(varName) {\n      var req = {type: \"definition\", variable: varName || null};\n      var doc = findDoc(ts, cm.getDoc());\n      ts.server.request(buildRequest(ts, doc, req), function(error, data) {\n        if (error) return showError(ts, cm, error);\n        if (!data.file && data.url) { window.open(data.url); return; }\n\n        if (data.file) {\n          var localDoc = ts.docs[data.file], found;\n          if (localDoc && (found = findContext(localDoc.doc, data))) {\n            ts.jumpStack.push({file: doc.name,\n                               start: cm.getCursor(\"from\"),\n                               end: cm.getCursor(\"to\")});\n            moveTo(ts, doc, localDoc, found.start, found.end);\n            return;\n          }\n        }\n        showError(ts, cm, \"Could not find a definition.\");\n      });\n    }\n\n    if (!atInterestingExpression(cm))\n      dialog(cm, \"Jump to variable\", function(name) { if (name) inner(name); });\n    else\n      inner();\n  }\n\n  function jumpBack(ts, cm) {\n    var pos = ts.jumpStack.pop(), doc = pos && ts.docs[pos.file];\n    if (!doc) return;\n    moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end);\n  }\n\n  function moveTo(ts, curDoc, doc, start, end) {\n    doc.doc.setSelection(start, end);\n    if (curDoc != doc && ts.options.switchToDoc) {\n      closeArgHints(ts);\n      ts.options.switchToDoc(doc.name, doc.doc);\n    }\n  }\n\n  // The {line,ch} representation of positions makes this rather awkward.\n  function findContext(doc, data) {\n    var before = data.context.slice(0, data.contextOffset).split(\"\\n\");\n    var startLine = data.start.line - (before.length - 1);\n    var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length);\n\n    var text = doc.getLine(startLine).slice(start.ch);\n    for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur)\n      text += \"\\n\" + doc.getLine(cur);\n    if (text.slice(0, data.context.length) == data.context) return data;\n\n    var cursor = doc.getSearchCursor(data.context, 0, false);\n    var nearest, nearestDist = Infinity;\n    while (cursor.findNext()) {\n      var from = cursor.from(), dist = Math.abs(from.line - start.line) * 10000;\n      if (!dist) dist = Math.abs(from.ch - start.ch);\n      if (dist < nearestDist) { nearest = from; nearestDist = dist; }\n    }\n    if (!nearest) return null;\n\n    if (before.length == 1)\n      nearest.ch += before[0].length;\n    else\n      nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length);\n    if (data.start.line == data.end.line)\n      var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch));\n    else\n      var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch);\n    return {start: nearest, end: end};\n  }\n\n  function atInterestingExpression(cm) {\n    var pos = cm.getCursor(\"end\"), tok = cm.getTokenAt(pos);\n    if (tok.start < pos.ch && tok.type == \"comment\") return false;\n    return /[\\w)\\]]/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1));\n  }\n\n  // Variable renaming\n\n  function rename(ts, cm) {\n    var token = cm.getTokenAt(cm.getCursor());\n    if (!/\\w/.test(token.string)) return showError(ts, cm, \"Not at a variable\");\n    dialog(cm, \"New name for \" + token.string, function(newName) {\n      ts.request(cm, {type: \"rename\", newName: newName, fullDocs: true}, function(error, data) {\n        if (error) return showError(ts, cm, error);\n        applyChanges(ts, data.changes);\n      });\n    });\n  }\n\n  function selectName(ts, cm) {\n    var name = findDoc(ts, cm.doc).name;\n    ts.request(cm, {type: \"refs\"}, function(error, data) {\n      if (error) return showError(ts, cm, error);\n      var ranges = [], cur = 0;\n      var curPos = cm.getCursor();\n      for (var i = 0; i < data.refs.length; i++) {\n        var ref = data.refs[i];\n        if (ref.file == name) {\n          ranges.push({anchor: ref.start, head: ref.end});\n          if (cmpPos(curPos, ref.start) >= 0 && cmpPos(curPos, ref.end) <= 0)\n            cur = ranges.length - 1;\n        }\n      }\n      cm.setSelections(ranges, cur);\n    });\n  }\n\n  var nextChangeOrig = 0;\n  function applyChanges(ts, changes) {\n    var perFile = Object.create(null);\n    for (var i = 0; i < changes.length; ++i) {\n      var ch = changes[i];\n      (perFile[ch.file] || (perFile[ch.file] = [])).push(ch);\n    }\n    for (var file in perFile) {\n      var known = ts.docs[file], chs = perFile[file];;\n      if (!known) continue;\n      chs.sort(function(a, b) { return cmpPos(b.start, a.start); });\n      var origin = \"*rename\" + (++nextChangeOrig);\n      for (var i = 0; i < chs.length; ++i) {\n        var ch = chs[i];\n        known.doc.replaceRange(ch.text, ch.start, ch.end, origin);\n      }\n    }\n  }\n\n  // Generic request-building helper\n\n  function buildRequest(ts, doc, query, pos) {\n    var files = [], offsetLines = 0, allowFragments = !query.fullDocs;\n    if (!allowFragments) delete query.fullDocs;\n    if (typeof query == \"string\") query = {type: query};\n    query.lineCharPositions = true;\n    if (query.end == null) {\n      query.end = pos || doc.doc.getCursor(\"end\");\n      if (doc.doc.somethingSelected())\n        query.start = doc.doc.getCursor(\"start\");\n    }\n    var startPos = query.start || query.end;\n\n    if (doc.changed) {\n      if (doc.doc.lineCount() > bigDoc && allowFragments !== false &&\n          doc.changed.to - doc.changed.from < 100 &&\n          doc.changed.from <= startPos.line && doc.changed.to > query.end.line) {\n        files.push(getFragmentAround(doc, startPos, query.end));\n        query.file = \"#0\";\n        var offsetLines = files[0].offsetLines;\n        if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch);\n        query.end = Pos(query.end.line - offsetLines, query.end.ch);\n      } else {\n        files.push({type: \"full\",\n                    name: doc.name,\n                    text: docValue(ts, doc)});\n        query.file = doc.name;\n        doc.changed = null;\n      }\n    } else {\n      query.file = doc.name;\n    }\n    for (var name in ts.docs) {\n      var cur = ts.docs[name];\n      if (cur.changed && cur != doc) {\n        files.push({type: \"full\", name: cur.name, text: docValue(ts, cur)});\n        cur.changed = null;\n      }\n    }\n\n    return {query: query, files: files};\n  }\n\n  function getFragmentAround(data, start, end) {\n    var doc = data.doc;\n    var minIndent = null, minLine = null, endLine, tabSize = 4;\n    for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) {\n      var line = doc.getLine(p), fn = line.search(/\\bfunction\\b/);\n      if (fn < 0) continue;\n      var indent = CodeMirror.countColumn(line, null, tabSize);\n      if (minIndent != null && minIndent <= indent) continue;\n      minIndent = indent;\n      minLine = p;\n    }\n    if (minLine == null) minLine = min;\n    var max = Math.min(doc.lastLine(), end.line + 20);\n    if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize))\n      endLine = max;\n    else for (endLine = end.line + 1; endLine < max; ++endLine) {\n      var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize);\n      if (indent <= minIndent) break;\n    }\n    var from = Pos(minLine, 0);\n\n    return {type: \"part\",\n            name: data.name,\n            offsetLines: from.line,\n            text: doc.getRange(from, Pos(endLine, end.line == endLine ? null : 0))};\n  }\n\n  // Generic utilities\n\n  var cmpPos = CodeMirror.cmpPos;\n\n  function elt(tagname, cls /*, ... elts*/) {\n    var e = document.createElement(tagname);\n    if (cls) e.className = cls;\n    for (var i = 2; i < arguments.length; ++i) {\n      var elt = arguments[i];\n      if (typeof elt == \"string\") elt = document.createTextNode(elt);\n      e.appendChild(elt);\n    }\n    return e;\n  }\n\n  function dialog(cm, text, f) {\n    if (cm.openDialog) {\n      var fragment = document.createDocumentFragment();\n      fragment.appendChild(document.createTextNode(text + \": \"));\n      var input = document.createElement(\"input\");\n      input.type = \"text\";\n      fragment.appendChild(input);\n      cm.openDialog(fragment, f);\n    } else {\n      f(prompt(text, \"\"));\n    }\n  }\n\n  // Tooltips\n\n  function tempTooltip(cm, content, ts) {\n    if (cm.state.ternTooltip) remove(cm.state.ternTooltip);\n    var where = cm.cursorCoords();\n    var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content, cm);\n    function maybeClear() {\n      old = true;\n      if (!mouseOnTip) clear();\n    }\n    function clear() {\n      cm.state.ternTooltip = null;\n      if (tip.parentNode) fadeOut(tip)\n      clearActivity()\n    }\n    var mouseOnTip = false, old = false;\n    CodeMirror.on(tip, \"mousemove\", function() { mouseOnTip = true; });\n    CodeMirror.on(tip, \"mouseout\", function(e) {\n      var related = e.relatedTarget || e.toElement\n      if (!related || !CodeMirror.contains(tip, related)) {\n        if (old) clear();\n        else mouseOnTip = false;\n      }\n    });\n    setTimeout(maybeClear, ts.options.hintDelay ? ts.options.hintDelay : 1700);\n    var clearActivity = onEditorActivity(cm, clear)\n  }\n\n  function onEditorActivity(cm, f) {\n    cm.on(\"cursorActivity\", f)\n    cm.on(\"blur\", f)\n    cm.on(\"scroll\", f)\n    cm.on(\"setDoc\", f)\n    return function() {\n      cm.off(\"cursorActivity\", f)\n      cm.off(\"blur\", f)\n      cm.off(\"scroll\", f)\n      cm.off(\"setDoc\", f)\n    }\n  }\n\n  function makeTooltip(x, y, content, cm, className) {\n    var node = elt(\"div\", cls + \"tooltip\" + \" \" + (className || \"\"), content);\n    node.style.left = x + \"px\";\n    node.style.top = y + \"px\";\n    var container = ((cm.options || {}).hintOptions || {}).container || document.body;\n    container.appendChild(node);\n\n    var pos = cm.cursorCoords();\n    var winW = window.innerWidth;\n    var winH = window.innerHeight;\n    var box = node.getBoundingClientRect();\n    var hints = document.querySelector(\".CodeMirror-hints\");\n    var overlapY = box.bottom - winH;\n    var overlapX = box.right - winW;\n\n    if (hints && overlapX > 0) {\n      node.style.left = 0;\n      var box = node.getBoundingClientRect();\n      node.style.left = (x = x - hints.offsetWidth - box.width) + \"px\";\n      overlapX = box.right - winW;\n    }\n    if (overlapY > 0) {\n      var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);\n      if (curTop - height > 0) { // Fits above cursor\n        node.style.top = (pos.top - height) + \"px\";\n      } else if (height > winH) {\n        node.style.height = (winH - 5) + \"px\";\n        node.style.top = (pos.bottom - box.top) + \"px\";\n      }\n    }\n    if (overlapX > 0) {\n      if (box.right - box.left > winW) {\n        node.style.width = (winW - 5) + \"px\";\n        overlapX -= (box.right - box.left) - winW;\n      }\n      node.style.left = (x - overlapX) + \"px\";\n    }\n\n    return node;\n  }\n\n  function remove(node) {\n    var p = node && node.parentNode;\n    if (p) p.removeChild(node);\n  }\n\n  function fadeOut(tooltip) {\n    tooltip.style.opacity = \"0\";\n    setTimeout(function() { remove(tooltip); }, 1100);\n  }\n\n  function showError(ts, cm, msg) {\n    if (ts.options.showError)\n      ts.options.showError(cm, msg);\n    else\n      tempTooltip(cm, String(msg), ts);\n  }\n\n  function closeArgHints(ts) {\n    if (ts.activeArgHints) {\n      if (ts.activeArgHints.clear) ts.activeArgHints.clear()\n      remove(ts.activeArgHints)\n      ts.activeArgHints = null\n    }\n  }\n\n  function docValue(ts, doc) {\n    var val = doc.doc.getValue();\n    if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc);\n    return val;\n  }\n\n  // Worker wrapper\n\n  function WorkerServer(ts) {\n    var worker = ts.worker = new Worker(ts.options.workerScript);\n    worker.postMessage({type: \"init\",\n                        defs: ts.options.defs,\n                        plugins: ts.options.plugins,\n                        scripts: ts.options.workerDeps});\n    var msgId = 0, pending = {};\n\n    function send(data, c) {\n      if (c) {\n        data.id = ++msgId;\n        pending[msgId] = c;\n      }\n      worker.postMessage(data);\n    }\n    worker.onmessage = function(e) {\n      var data = e.data;\n      if (data.type == \"getFile\") {\n        getFile(ts, data.name, function(err, text) {\n          send({type: \"getFile\", err: String(err), text: text, id: data.id});\n        });\n      } else if (data.type == \"debug\") {\n        window.console.log(data.message);\n      } else if (data.id && pending[data.id]) {\n        pending[data.id](data.err, data.body);\n        delete pending[data.id];\n      }\n    };\n    worker.onerror = function(e) {\n      for (var id in pending) pending[id](e);\n      pending = {};\n    };\n\n    this.addFile = function(name, text) { send({type: \"add\", name: name, text: text}); };\n    this.delFile = function(name) { send({type: \"del\", name: name}); };\n    this.request = function(body, c) { send({type: \"req\", body: body}, c); };\n  }\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/tern/worker.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// declare global: tern, server\n\nvar server;\n\nthis.onmessage = function(e) {\n  var data = e.data;\n  switch (data.type) {\n  case \"init\": return startServer(data.defs, data.plugins, data.scripts);\n  case \"add\": return server.addFile(data.name, data.text);\n  case \"del\": return server.delFile(data.name);\n  case \"req\": return server.request(data.body, function(err, reqData) {\n    postMessage({id: data.id, body: reqData, err: err && String(err)});\n  });\n  case \"getFile\":\n    var c = pending[data.id];\n    delete pending[data.id];\n    return c(data.err, data.text);\n  default: throw new Error(\"Unknown message type: \" + data.type);\n  }\n};\n\nvar nextId = 0, pending = {};\nfunction getFile(file, c) {\n  postMessage({type: \"getFile\", name: file, id: ++nextId});\n  pending[nextId] = c;\n}\n\nfunction startServer(defs, plugins, scripts) {\n  if (scripts) importScripts.apply(null, scripts);\n\n  server = new tern.Server({\n    getFile: getFile,\n    async: true,\n    defs: defs,\n    plugins: plugins\n  });\n}\n\nthis.console = {\n  log: function(v) { postMessage({type: \"debug\", message: v}); }\n};\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/addon/wrap/hardwrap.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var Pos = CodeMirror.Pos;\n\n  function findParagraph(cm, pos, options) {\n    var startRE = options.paragraphStart || cm.getHelper(pos, \"paragraphStart\");\n    for (var start = pos.line, first = cm.firstLine(); start > first; --start) {\n      var line = cm.getLine(start);\n      if (startRE && startRE.test(line)) break;\n      if (!/\\S/.test(line)) { ++start; break; }\n    }\n    var endRE = options.paragraphEnd || cm.getHelper(pos, \"paragraphEnd\");\n    for (var end = pos.line + 1, last = cm.lastLine(); end <= last; ++end) {\n      var line = cm.getLine(end);\n      if (endRE && endRE.test(line)) { ++end; break; }\n      if (!/\\S/.test(line)) break;\n    }\n    return {from: start, to: end};\n  }\n\n  function findBreakPoint(text, column, wrapOn, killTrailingSpace, forceBreak) {\n    var at = column\n    while (at < text.length && text.charAt(at) == \" \") at++\n    for (; at > 0; --at)\n      if (wrapOn.test(text.slice(at - 1, at + 1))) break;\n\n    if (!forceBreak && at <= text.match(/^[ \\t]*/)[0].length) {\n      // didn't find a break point before column, in non-forceBreak mode try to\n      // find one after 'column'.\n      for (at = column + 1; at < text.length - 1; ++at) {\n        if (wrapOn.test(text.slice(at - 1, at + 1))) break;\n      }\n    }\n\n    for (var first = true;; first = false) {\n      var endOfText = at;\n      if (killTrailingSpace)\n        while (text.charAt(endOfText - 1) == \" \") --endOfText;\n      if (endOfText == 0 && first) at = column;\n      else return {from: endOfText, to: at};\n    }\n  }\n\n  function wrapRange(cm, from, to, options) {\n    from = cm.clipPos(from); to = cm.clipPos(to);\n    var column = options.column || 80;\n    var wrapOn = options.wrapOn || /\\s\\S|-[^\\.\\d]/;\n    var forceBreak = options.forceBreak !== false;\n    var killTrailing = options.killTrailingSpace !== false;\n    var changes = [], curLine = \"\", curNo = from.line;\n    var lines = cm.getRange(from, to, false);\n    if (!lines.length) return null;\n    var leadingSpace = lines[0].match(/^[ \\t]*/)[0];\n    if (leadingSpace.length >= column) column = leadingSpace.length + 1\n\n    for (var i = 0; i < lines.length; ++i) {\n      var text = lines[i], oldLen = curLine.length, spaceInserted = 0;\n      if (curLine && text && !wrapOn.test(curLine.charAt(curLine.length - 1) + text.charAt(0))) {\n        curLine += \" \";\n        spaceInserted = 1;\n      }\n      var spaceTrimmed = \"\";\n      if (i) {\n        spaceTrimmed = text.match(/^\\s*/)[0];\n        text = text.slice(spaceTrimmed.length);\n      }\n      curLine += text;\n      if (i) {\n        var firstBreak = curLine.length > column && leadingSpace == spaceTrimmed &&\n          findBreakPoint(curLine, column, wrapOn, killTrailing, forceBreak);\n        // If this isn't broken, or is broken at a different point, remove old break\n        if (!firstBreak || firstBreak.from != oldLen || firstBreak.to != oldLen + spaceInserted) {\n          changes.push({text: [spaceInserted ? \" \" : \"\"],\n                        from: Pos(curNo, oldLen),\n                        to: Pos(curNo + 1, spaceTrimmed.length)});\n        } else {\n          curLine = leadingSpace + text;\n          ++curNo;\n        }\n      }\n      while (curLine.length > column) {\n        var bp = findBreakPoint(curLine, column, wrapOn, killTrailing, forceBreak);\n        if (bp.from != bp.to ||\n            forceBreak && leadingSpace !== curLine.slice(0, bp.to)) {\n          changes.push({text: [\"\", leadingSpace],\n                        from: Pos(curNo, bp.from),\n                        to: Pos(curNo, bp.to)});\n          curLine = leadingSpace + curLine.slice(bp.to);\n          ++curNo;\n        } else {\n          break;\n        }\n      }\n    }\n    if (changes.length) cm.operation(function() {\n      for (var i = 0; i < changes.length; ++i) {\n        var change = changes[i];\n        if (change.text || CodeMirror.cmpPos(change.from, change.to))\n          cm.replaceRange(change.text, change.from, change.to);\n      }\n    });\n    return changes.length ? {from: changes[0].from, to: CodeMirror.changeEnd(changes[changes.length - 1])} : null;\n  }\n\n  CodeMirror.defineExtension(\"wrapParagraph\", function(pos, options) {\n    options = options || {};\n    if (!pos) pos = this.getCursor();\n    var para = findParagraph(this, pos, options);\n    return wrapRange(this, Pos(para.from, 0), Pos(para.to - 1), options);\n  });\n\n  CodeMirror.commands.wrapLines = function(cm) {\n    cm.operation(function() {\n      var ranges = cm.listSelections(), at = cm.lastLine() + 1;\n      for (var i = ranges.length - 1; i >= 0; i--) {\n        var range = ranges[i], span;\n        if (range.empty()) {\n          var para = findParagraph(cm, range.head, {});\n          span = {from: Pos(para.from, 0), to: Pos(para.to - 1)};\n        } else {\n          span = {from: range.from(), to: range.to()};\n        }\n        if (span.to.line >= at) continue;\n        at = span.from.line;\n        wrapRange(cm, span.from, span.to, {});\n      }\n    });\n  };\n\n  CodeMirror.defineExtension(\"wrapRange\", function(from, to, options) {\n    return wrapRange(this, from, to, options || {});\n  });\n\n  CodeMirror.defineExtension(\"wrapParagraphsInRange\", function(from, to, options) {\n    options = options || {};\n    var cm = this, paras = [];\n    for (var line = from.line; line <= to.line;) {\n      var para = findParagraph(cm, Pos(line, 0), options);\n      paras.push(para);\n      line = para.to;\n    }\n    var madeChange = false;\n    if (paras.length) cm.operation(function() {\n      for (var i = paras.length - 1; i >= 0; --i)\n        madeChange = madeChange || wrapRange(cm, Pos(paras[i].from, 0), Pos(paras[i].to - 1), options);\n    });\n    return madeChange;\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/bin/authors.sh",
    "content": "# Combine existing list of authors with everyone known in git, sort, add header.\ntail --lines=+3 AUTHORS > AUTHORS.tmp\ngit log --format='%aN' | grep -v \"Piët Delport\" >> AUTHORS.tmp\necho -e \"List of CodeMirror contributors. Updated before every release.\\n\" > AUTHORS\nsort -u AUTHORS.tmp | sed 's/Google Inc\\./Google LLC/' >> AUTHORS\nrm -f AUTHORS.tmp\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/bin/lint",
    "content": "#!/usr/bin/env node\n\nprocess.exit(require(\"../test/lint\").ok ? 0 : 1);\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/bin/release",
    "content": "#!/usr/bin/env node\n\nvar fs = require(\"fs\"), child = require(\"child_process\");\n\nvar number, bumpOnly;\n\nfor (var i = 2; i < process.argv.length; i++) {\n  if (process.argv[i] == \"-bump\") bumpOnly = true;\n  else if (/^\\d+\\.\\d+\\.\\d+$/.test(process.argv[i])) number = process.argv[i];\n  else { console.log(\"Bogus command line arg: \" + process.argv[i]); process.exit(1); }\n}\n\nif (!number) { console.log(\"Must give a version\"); process.exit(1); }\n\nfunction rewrite(file, f) {\n  fs.writeFileSync(file, f(fs.readFileSync(file, \"utf8\")), \"utf8\");\n}\n\nrewrite(\"src/edit/main.js\", function(lib) {\n  return lib.replace(/CodeMirror\\.version = \"\\d+\\.\\d+\\.\\d+\"/,\n                     \"CodeMirror.version = \\\"\" + number + \"\\\"\");\n});\nfunction rewriteJSON(pack) {\n  return pack.replace(/\"version\":\\s*\"\\d+\\.\\d+\\.\\d+\"/, \"\\\"version\\\": \\\"\" + number + \"\\\"\");\n}\nrewrite(\"package.json\", rewriteJSON);\nrewrite(\"doc/manual.html\", function(manual) {\n  return manual.replace(/>version \\d+\\.\\d+\\.\\d+<\\/span>/, \">version \" + number + \"</span>\");\n});\n\nif (bumpOnly) process.exit(0);\n\nchild.exec(\"bash bin/authors.sh\", function(){});\n\nrewrite(\"index.html\", function(index) {\n  return index.replace(/\\.zip\">\\d+\\.\\d+\\.\\d+<\\/a>/,\n                       \".zip\\\">\" + number + \"</a>\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/bin/source-highlight",
    "content": "#!/usr/bin/env node\n\n// Simple command-line code highlighting tool. Reads code from stdin,\n// spits html to stdout. For example:\n//\n//   echo 'function foo(a) { return a; }' | bin/source-highlight -s javascript\n//   bin/source-highlight -s \n\nvar fs = require(\"fs\");\n\nvar CodeMirror = require(\"../addon/runmode/runmode.node.js\");\nrequire(\"../mode/meta.js\");\n\nvar sPos = process.argv.indexOf(\"-s\");\nif (sPos == -1 || sPos == process.argv.length - 1) {\n   console.error(\"Usage: source-highlight -s language\");\n   process.exit(1);\n}\nvar lang = process.argv[sPos + 1].toLowerCase(), modeName = lang;\nvar found = CodeMirror.findModeByMIME(lang) || CodeMirror.findModeByName(lang)\nif (found) {\n  modeName = found.mode\n  lang = found.mime\n}\n\nif (!CodeMirror.modes[modeName])\n  require(\"../mode/\" + modeName + \"/\" + modeName + \".js\");\n\nfunction esc(str) {\n  return str.replace(/[<&]/g, function(ch) { return ch == \"&\" ? \"&amp;\" : \"&lt;\"; });\n}\n\nvar code = fs.readFileSync(\"/dev/stdin\", \"utf8\");\nvar curStyle = null, accum = \"\";\nfunction flush() {\n  if (curStyle) process.stdout.write(\"<span class=\\\"\" + curStyle.replace(/(^|\\s+)/g, \"$1cm-\") + \"\\\">\" + esc(accum) + \"</span>\");\n  else process.stdout.write(esc(accum));\n}\n\nCodeMirror.runMode(code, lang, function(text, style) {\n  if (style != curStyle) {\n    flush();\n    curStyle = style; accum = text;\n  } else {\n    accum += text;\n  }\n});\nflush();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/bin/upload-release.js",
    "content": "\"use strict\"\n\nlet version = process.argv[2]\nlet auth = process.argv[3]\nlet url = require(\"url\")\n\nif (!auth) {\n  console.log(\"Usage: upload-release.js [TAG] [github-user:password]\")\n  process.exit(1)\n}\n\nfunction post(host, path, body) {\n  let req = require(\"https\").request({\n    host,\n    auth: auth,\n    headers: {\"user-agent\": \"Release uploader\"},\n    path,\n    method: \"POST\"\n  }, res => {\n    if (res.statusCode >= 300 && res.statusCode < 400) {\n      console.log(res.headers.location)\n      let parsed = url.parse(res.headers.location)\n      post(parsed.host, parsed.path, body)\n    } else if (res.statusCode >= 400) {\n      console.error(res.statusCode, res.statusMessage)\n      res.on(\"data\", d => console.log(d.toString()))\n      res.on(\"end\", () => process.exit(1))\n    }\n  })\n  req.write(body)\n  req.end()\n}\n\nrequire('child_process').exec(\"git --no-pager show -s --format='%s' \" + version, (error, stdout) => {\n  if (error) throw error\n  let message = stdout.split(\"\\n\").slice(2)\n  message = message.slice(0, message.indexOf(\"-----BEGIN PGP SIGNATURE-----\")).join(\"\\n\")\n\n  post(\"api.github.com\", \"/repos/codemirror/codemirror5/releases\", JSON.stringify({\n    tag_name: version,\n    name: version,\n    body: message\n  }))\n})\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/activeline.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Active Line Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<script src=\"../addon/selection/active-line.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Active Line</a>\n  </ul>\n</div>\n\n<article>\n<h2>Active Line Demo</h2>\n<form><textarea id=\"code\" name=\"code\">\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<rss xmlns:atom=\"http://www.w3.org/2005/Atom\" version=\"2.0\"\n     xmlns:georss=\"http://www.georss.org/georss\"\n     xmlns:twitter=\"http://api.twitter.com\">\n  <channel>\n    <title>Twitter / codemirror</title>\n    <link>http://twitter.com/codemirror</link>\n    <atom:link type=\"application/rss+xml\"\n               href=\"http://twitter.com/statuses/user_timeline/242283288.rss\" rel=\"self\"/>\n    <description>Twitter updates from CodeMirror / codemirror.</description>\n    <language>en-us</language>\n    <ttl>40</ttl>\n  <item>\n    <title>codemirror: http://cloud-ide.com &#8212; they're springing up like mushrooms. This one\n      uses CodeMirror as its editor.</title>\n    <description>codemirror: http://cloud-ide.com &#8212; they're springing up like mushrooms. This\n      one uses CodeMirror as its editor.</description>\n    <pubDate>Thu, 17 Mar 2011 23:34:47 +0000</pubDate>\n    <guid>http://twitter.com/codemirror/statuses/48527733722058752</guid>\n    <link>http://twitter.com/codemirror/statuses/48527733722058752</link>\n    <twitter:source>web</twitter:source>\n    <twitter:place/>\n  </item>\n  <item>\n    <title>codemirror: Posted a description of the CodeMirror 2 internals at\n      https://codemirror.net/2/internals.html</title>\n    <description>codemirror: Posted a description of the CodeMirror 2 internals at\n      https://codemirror.net/2/internals.html</description>\n    <pubDate>Wed, 02 Mar 2011 12:15:09 +0000</pubDate>\n    <guid>http://twitter.com/codemirror/statuses/42920879788789760</guid>\n    <link>http://twitter.com/codemirror/statuses/42920879788789760</link>\n    <twitter:source>web</twitter:source>\n    <twitter:place/>\n  </item>\n  </channel>\n</rss></textarea></form>\n\n    <script>\nvar nonEmpty = false;\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  mode: \"application/xml\",\n  styleActiveLine: true,\n  lineNumbers: true,\n  lineWrapping: true\n});\n\nfunction toggleSelProp() {\n  nonEmpty = !nonEmpty;\n  editor.setOption(\"styleActiveLine\", {nonEmpty: nonEmpty});\n  var label = nonEmpty ? 'Disable nonEmpty option' : 'Enable nonEmpty option';\n  document.getElementById('toggleButton').innerText = label;\n}\n</script>\n\n    <p>Styling the current cursor line.</p>\n\n    <button onclick=\"toggleSelProp()\" id=\"toggleButton\">Enable <code>nonEmpty</code> option</button>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/anywordhint.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Any Word Completion Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../addon/hint/show-hint.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/hint/show-hint.js\"></script>\n<script src=\"../addon/hint/anyword-hint.js\" id=anyword></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Any Word Completion</a>\n  </ul>\n</div>\n\n<article>\n<h2>Any Word Completion Demo</h2>\n<form><textarea id=\"code\" name=\"code\">\n(function() {\n  \"use strict\";\n\n  var WORD = /[\\w$]+/, RANGE = 500;\n\n  CodeMirror.registerHelper(\"hint\", \"anyword\", function(editor, options) {\n    var word = options && options.word || WORD;\n    var range = options && options.range || RANGE;\n    var cur = editor.getCursor(), curLine = editor.getLine(cur.line);\n    var end = cur.ch, start = end;\n    while (start && word.test(curLine.charAt(start - 1))) --start;\n    var curWord = start != end && curLine.slice(start, end);\n\n    var list = options && options.list || [], seen = {};\n    var re = new RegExp(word.source, \"g\");\n    for (var dir = -1; dir <= 1; dir += 2) {\n      var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;\n      for (; line != endLine; line += dir) {\n        var text = editor.getLine(line), m;\n        while (m = re.exec(text)) {\n          if (line == cur.line && m[0] === curWord) continue;\n          if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {\n            seen[m[0]] = true;\n            list.push(m[0]);\n          }\n        }\n      }\n    }\n    return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};\n  });\n})();\n</textarea></form>\n\n<p>Press <strong>ctrl-space</strong> to activate autocompletion. The\ncompletion uses\nthe <a href=\"../doc/manual.html#addon_anyword-hint\">anyword-hint.js</a>\nmodule, which simply looks at nearby words in the buffer and completes\nto those.</p>\n\n    <script>\n      CodeMirror.commands.autocomplete = function(cm) {\n        cm.showHint({hint: CodeMirror.hint.anyword});\n      }\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        extraKeys: {\"Ctrl-Space\": \"autocomplete\"}\n      });\n    </script>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/bidi.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Bi-directional Text Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<style>\n  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n  fieldset {border: none}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Bi-directional Text</a>\n  </ul>\n</div>\n\n<article>\n<h2>Bi-directional Text Demo</h2>\n<form><textarea id=\"code\" name=\"code\"><!-- Piece of the CodeMirror manual, 'translated' into Arabic by Google Translate -->\n<!-- قطعة من دليل CodeMirror، \"ترجم\" إلى العربية بواسطة جوجل ترجمة -->\n\n<dl>\n  <dt id=option_value><code>value (string or Doc)</code></dt>\n  <dd>قيمة البداية المحرر. يمكن أن تكون سلسلة، أو. كائن مستند.</dd>\n  <dt id=option_mode><code>mode (string or object)</code></dt>\n  <dd>وضع الاستخدام. عندما لا تعطى، وهذا الافتراضي إلى الطريقة الاولى\n  التي تم تحميلها. قد يكون من سلسلة، والتي إما أسماء أو ببساطة هو وضع\n  MIME نوع المرتبطة اسطة. بدلا من ذلك، قد يكون من كائن يحتوي على\n  خيارات التكوين لواسطة، مع <code>name</code> الخاصية التي وضع أسماء\n  (على سبيل المثال <code>{name: \"javascript\", json: true}</code>).\n  صفحات التجريبي لكل وضع تحتوي على معلومات حول ما معلمات تكوين وضع\n  يدعمها. يمكنك أن تطلب CodeMirror التي تم تعريفها طرق وأنواع MIME\n  الكشف على <code>CodeMirror.modes</code>\n  و <code>CodeMirror.mimeModes</code> الكائنات. وضع خرائط الأسماء\n  الأولى لمنشئات الخاصة بهم، وخرائط لأنواع MIME 2 المواصفات\n  واسطة.</dd>\n  <dt id=option_theme><code>theme (string)</code></dt>\n  <dd>موضوع لنمط المحرر مع. يجب عليك التأكد من الملف CSS تحديد\n  المقابلة <code>.cm-s-[name]</code> يتم تحميل أنماط (انظر\n  <a href=\"../theme/\"><code>theme</code></a> الدليل في التوزيع).\n  الافتراضي هو <code>\"default\"</code> ، والتي تم تضمينها في\n  الألوان <code>codemirror.css</code>. فمن الممكن استخدام فئات متعددة\n  في تطبيق السمات مرة واحدة على سبيل المثال <code>\"foo bar\"</code>\n  سيتم تعيين كل من <code>cm-s-foo</code> و <code>cm-s-bar</code>\n  الطبقات إلى المحرر.</dd>\n</dl>\n</textarea>\n  <fieldset>\n    Editor default direction:\n    <input type=\"radio\" id=\"ltr\" name=\"direction\"/><label for=\"ltr\">LTR</label>\n    <input type=\"radio\" id=\"rtl\" name=\"direction\"/><label for=\"rtl\">RTL</label>\n  </fieldset>\n  <fieldset>\n    HTML document direction:\n    <input type=\"radio\" id=\"htmlltr\" name=\"htmldirection\"/><label for=\"htmlltr\">LTR</label>\n    <input type=\"radio\" id=\"htmlrtl\" name=\"htmldirection\"/><label for=\"htmlrtl\">RTL</label>\n  </fieldset>\n  <fieldset>\n    <input type=\"checkbox\" id=\"rtlMoveVisually\"/><label for=\"rtlMoveVisually\">Use visual order for arrow key movement.</label>\n  </fieldset>\n</form>\n\n    <script>\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  mode: \"text/html\",\n  lineNumbers: true,\n  lineWrapping: true,\n  direction: \"rtl\"\n});\n\nvar dirRadios = {ltr: document.getElementById(\"ltr\"),\n                 rtl: document.getElementById(\"rtl\")};\ndirRadios[editor.getOption(\"direction\")].checked = true;\ndirRadios[\"rtl\"].onchange = dirRadios[\"ltr\"].onchange = function() {\n  editor.setOption(\"direction\", dirRadios[\"rtl\"].checked ? \"rtl\" : \"ltr\");\n};\n\nvar HtmlDirRadios = {ltr: document.getElementById(\"htmlltr\"),\n                 rtl: document.getElementById(\"htmlrtl\")};\nHtmlDirRadios[\"ltr\"].checked = true;\nHtmlDirRadios[\"rtl\"].onchange = HtmlDirRadios[\"ltr\"].onchange = function() {\n  document.dir = (HtmlDirRadios[\"rtl\"].checked ? \"rtl\" : \"ltr\");\n};\n\nvar moveCheckbox = document.getElementById(\"rtlMoveVisually\");\nmoveCheckbox.checked = editor.getOption(\"rtlMoveVisually\");\nmoveCheckbox.onchange = function() {\n  editor.setOption(\"rtlMoveVisually\", moveCheckbox.checked);\n};\n</script>\n\n  <p>Demonstration of bi-directional text support. See\n  the <a href=\"http://marijnhaverbeke.nl/blog/cursor-in-bidi-text.html\">related\n  blog post</a> for more background.</p>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/btree.html",
    "content": "﻿<!doctype html>\n\n<title>CodeMirror: B-Tree visualization</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<style>\n      .lineblock { display: inline-block; margin: 1px; height: 5px; }\n      .CodeMirror {border: 1px solid #aaa; height: 400px}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">B-Tree visualization</a>\n  </ul>\n</div>\n\n<article>\n<h2>B-Tree visualization</h2>\n<form><textarea id=\"code\" name=\"code\">type here, see a summary of the document b-tree below</textarea></form>\n      <div style=\"display: inline-block; height: 402px; overflow-y: auto\" id=\"output\"></div>\n\n    <script id=\"me\">\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  lineNumbers: true,\n  lineWrapping: true\n});\nvar updateTimeout;\neditor.on(\"change\", function(cm) {\n  clearTimeout(updateTimeout);\n  updateTimeout = setTimeout(updateVisual, 200);\n});\nupdateVisual();\n\nfunction updateVisual() {\n  var out = document.getElementById(\"output\");\n  out.innerHTML = \"\";\n\n  function drawTree(out, node) {\n    if (node.lines) {\n      out.appendChild(document.createElement(\"div\")).innerHTML =\n        \"<b>leaf</b>: \" + node.lines.length + \" lines, \" + Math.round(node.height) + \" px\";\n      var lines = out.appendChild(document.createElement(\"div\"));\n      lines.style.lineHeight = \"6px\"; lines.style.marginLeft = \"10px\";\n      for (var i = 0; i < node.lines.length; ++i) {\n        var line = node.lines[i], lineElt = lines.appendChild(document.createElement(\"div\"));\n        lineElt.className = \"lineblock\";\n        var gray = Math.min(line.text.length * 3, 230), col = gray.toString(16);\n        if (col.length == 1) col = \"0\" + col;\n        lineElt.style.background = \"#\" + col + col + col;\n        lineElt.style.width = Math.max(Math.round(line.height / 3), 1) + \"px\";\n      }\n    } else {\n      out.appendChild(document.createElement(\"div\")).innerHTML =\n        \"<b>node</b>: \" + node.size + \" lines, \" + Math.round(node.height) + \" px\";\n      var sub = out.appendChild(document.createElement(\"div\"));\n      sub.style.paddingLeft = \"20px\";\n      for (var i = 0; i < node.children.length; ++i)\n        drawTree(sub, node.children[i]);\n    }\n  }\n  drawTree(out, editor.getDoc());\n}\n\nfunction fillEditor() {\n  var sc = document.getElementById(\"me\");\n  var doc = (sc.textContent || sc.innerText || sc.innerHTML).replace(/^\\s*/, \"\") + \"\\n\";\n  doc += doc; doc += doc; doc += doc; doc += doc; doc += doc; doc += doc;\n  editor.setValue(doc);\n}\n    </script>\n\n<p><button onclick=\"fillEditor()\">Add a lot of content</button></p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/buffers.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Multiple Buffer & Split View Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"../mode/css/css.js\"></script>\n<style id=style>\n      .CodeMirror {border: 1px solid black; height: 250px;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Multiple Buffer & Split View</a>\n  </ul>\n</div>\n\n<article>\n<h2>Multiple Buffer & Split View Demo</h2>\n\n\n    <div id=code_top></div>\n    <div>\n      Select buffer: <select id=buffers_top></select>\n      &nbsp; &nbsp; <button onclick=\"newBuf('top')\">New buffer</button>\n    </div>\n    <div id=code_bot></div>\n    <div>\n      Select buffer: <select id=buffers_bot></select>\n      &nbsp; &nbsp; <button onclick=\"newBuf('bot')\">New buffer</button>\n    </div>\n\n    <script id=script>\nvar sel_top = document.getElementById(\"buffers_top\");\nCodeMirror.on(sel_top, \"change\", function() {\n  selectBuffer(ed_top, sel_top.options[sel_top.selectedIndex].value);\n});\n\nvar sel_bot = document.getElementById(\"buffers_bot\");\nCodeMirror.on(sel_bot, \"change\", function() {\n  selectBuffer(ed_bot, sel_bot.options[sel_bot.selectedIndex].value);\n});\n\nvar buffers = {};\n\nfunction openBuffer(name, text, mode) {\n  buffers[name] = CodeMirror.Doc(text, mode);\n  var opt = document.createElement(\"option\");\n  opt.appendChild(document.createTextNode(name));\n  sel_top.appendChild(opt);\n  sel_bot.appendChild(opt.cloneNode(true));\n}\n\nfunction newBuf(where) {\n  var name = prompt(\"Name for the buffer\", \"*scratch*\");\n  if (name == null) return;\n  if (buffers.hasOwnProperty(name)) {\n    alert(\"There's already a buffer by that name.\");\n    return;\n  }\n  openBuffer(name, \"\", \"javascript\");\n  selectBuffer(where == \"top\" ? ed_top : ed_bot, name);\n  var sel = where == \"top\" ? sel_top : sel_bot;\n  sel.value = name;\n}\n\nfunction selectBuffer(editor, name) {\n  var buf = buffers[name];\n  if (buf.getEditor()) buf = buf.linkedDoc({sharedHist: true});\n  var old = editor.swapDoc(buf);\n  var linked = old.iterLinkedDocs(function(doc) {linked = doc;});\n  if (linked) {\n    // Make sure the document in buffers is the one the other view is looking at\n    for (var name in buffers) if (buffers[name] == old) buffers[name] = linked;\n    old.unlinkDoc(linked);\n  }\n  editor.focus();\n}\n\nfunction nodeContent(id) {\n  var node = document.getElementById(id), val = node.textContent || node.innerText;\n  val = val.slice(val.match(/^\\s*/)[0].length, val.length - val.match(/\\s*$/)[0].length) + \"\\n\";\n  return val;\n}\nopenBuffer(\"js\", nodeContent(\"script\"), \"javascript\");\nopenBuffer(\"css\", nodeContent(\"style\"), \"css\");\n\nvar ed_top = CodeMirror(document.getElementById(\"code_top\"), {lineNumbers: true});\nselectBuffer(ed_top, \"js\");\nvar ed_bot = CodeMirror(document.getElementById(\"code_bot\"), {lineNumbers: true});\nselectBuffer(ed_bot, \"js\");\n</script>\n\n    <p>Demonstration of\n    using <a href=\"../doc/manual.html#linkedDoc\">linked documents</a>\n    to provide a split view on a document, and\n    using <a href=\"../doc/manual.html#swapDoc\"><code>swapDoc</code></a>\n    to use a single editor to display multiple documents.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/changemode.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Mode-Changing Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"../mode/scheme/scheme.js\"></script>\n<style>\n      .CodeMirror {border: 1px solid black;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Mode-Changing</a>\n  </ul>\n</div>\n\n<article>\n<h2>Mode-Changing Demo</h2>\n<form><textarea id=\"code\" name=\"code\">\n;; If there is Scheme code in here, the editor will be in Scheme mode.\n;; If you put in JS instead, it'll switch to JS mode.\n\n(define (double x)\n  (* x x))\n</textarea></form>\n\n<p>On changes to the content of the above editor, a (crude) script\ntries to auto-detect the language used, and switches the editor to\neither JavaScript or Scheme mode based on that.</p>\n\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    mode: \"scheme\",\n    lineNumbers: true\n  });\n  var pending;\n  editor.on(\"change\", function() {\n    clearTimeout(pending);\n    pending = setTimeout(update, 400);\n  });\n  function looksLikeScheme(code) {\n    return !/^\\s*\\(\\s*function\\b/.test(code) && /^\\s*[;\\(]/.test(code);\n  }\n  function update() {\n    editor.setOption(\"mode\", looksLikeScheme(editor.getValue()) ? \"scheme\" : \"javascript\");\n  }\n</script>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/closebrackets.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Closebrackets Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/edit/closebrackets.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Closebrackets</a>\n  </ul>\n</div>\n\n<article>\n<h2>Closebrackets Demo</h2>\n<form><textarea id=\"code\" name=\"code\">function Grid(width, height) {\n  this.width = width;\n  this.height = height;\n  this.cells = new Array(width * height);\n}\nGrid.prototype.valueAt = function(point) {\n  return this.cells[point.y * this.width + point.x];\n};\nGrid.prototype.setValueAt = function(point, value) {\n  this.cells[point.y * this.width + point.x] = value;\n};\nGrid.prototype.isInside = function(point) {\n  return point.x >= 0 && point.y >= 0 &&\n         point.x < this.width && point.y < this.height;\n};\nGrid.prototype.moveValue = function(from, to) {\n  this.setValueAt(to, this.valueAt(from));\n  this.setValueAt(from, undefined);\n};</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {autoCloseBrackets: true});\n    </script>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/closetag.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Close-Tag Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/edit/closetag.js\"></script>\n<script src=\"../addon/fold/xml-fold.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"../mode/css/css.js\"></script>\n<script src=\"../mode/htmlmixed/htmlmixed.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Close-Tag</a>\n  </ul>\n</div>\n\n<article>\n<h2>Close-Tag Demo</h2>\n<form><textarea id=\"code\" name=\"code\"><html</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: 'text/html',\n        autoCloseTags: true\n      });\n    </script>\n  <p>Uses the <a href=\"https://codemirror.net/5/doc/manual.html#addon_closetag\">closetag</a> addon to auto-close tags.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/complete.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Autocomplete Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../addon/hint/show-hint.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/hint/show-hint.js\"></script>\n<script src=\"../addon/hint/javascript-hint.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"../mode/markdown/markdown.js\"></script>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Autocomplete</a>\n  </ul>\n</div>\n\n<article>\n<h2>Autocomplete Demo</h2>\n<form><textarea id=\"code\" name=\"code\">\nfunction getCompletions(token, context) {\n  var found = [], start = token.string;\n  function maybeAdd(str) {\n    if (str.indexOf(start) == 0) found.push(str);\n  }\n  function gatherCompletions(obj) {\n    if (typeof obj == \"string\") forEach(stringProps, maybeAdd);\n    else if (obj instanceof Array) forEach(arrayProps, maybeAdd);\n    else if (obj instanceof Function) forEach(funcProps, maybeAdd);\n    for (var name in obj) maybeAdd(name);\n  }\n\n  if (context) {\n    // If this is a property, see if it belongs to some object we can\n    // find in the current environment.\n    var obj = context.pop(), base;\n    if (obj.className == \"js-variable\")\n      base = window[obj.string];\n    else if (obj.className == \"js-string\")\n      base = \"\";\n    else if (obj.className == \"js-atom\")\n      base = 1;\n    while (base != null && context.length)\n      base = base[context.pop().string];\n    if (base != null) gatherCompletions(base);\n  }\n  else {\n    // If not, just look in the window object and any local scope\n    // (reading into JS mode internals to get at the local variables)\n    for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);\n    gatherCompletions(window);\n    forEach(keywords, maybeAdd);\n  }\n  return found;\n}\n</textarea></form>\n\n<p>Press <strong>ctrl-space</strong> to activate autocompletion. Built\non top of the <a href=\"../doc/manual.html#addon_show-hint\"><code>show-hint</code></a>\nand <a href=\"../doc/manual.html#addon_javascript-hint\"><code>javascript-hint</code></a>\naddons.</p>\n\n<form><textarea style=\"display: none\" id=\"synonyms\" name=\"synonyms\">\nHere, the completion use an asynchronous hinting function to provide\nsynonyms for each words. If your browser support `Promises`, the\nhinting function can also return one.\n</textarea></form>\n\n<script>\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  lineNumbers: true,\n  extraKeys: {\"Ctrl-Space\": \"autocomplete\"},\n  mode: {name: \"javascript\", globalVars: true}\n});\n\nif (typeof Promise !== \"undefined\") {\n  var comp = [\n    [\"here\", \"hither\"],\n    [\"asynchronous\", \"nonsynchronous\"],\n    [\"completion\", \"achievement\", \"conclusion\", \"culmination\", \"expirations\"],\n    [\"hinting\", \"advise\", \"broach\", \"imply\"],\n    [\"function\",\"action\"],\n    [\"provide\", \"add\", \"bring\", \"give\"],\n    [\"synonyms\", \"equivalents\"],\n    [\"words\", \"token\"],\n    [\"each\", \"every\"],\n  ]\n\n  function synonyms(cm, option) {\n    return new Promise(function(accept) {\n      setTimeout(function() {\n        var cursor = cm.getCursor(), line = cm.getLine(cursor.line)\n        var start = cursor.ch, end = cursor.ch\n        while (start && /\\w/.test(line.charAt(start - 1))) --start\n        while (end < line.length && /\\w/.test(line.charAt(end))) ++end\n        var word = line.slice(start, end).toLowerCase()\n        for (var i = 0; i < comp.length; i++) if (comp[i].indexOf(word) != -1)\n          return accept({list: comp[i],\n                         from: CodeMirror.Pos(cursor.line, start),\n                         to: CodeMirror.Pos(cursor.line, end)})\n        return accept(null)\n      }, 100)\n    })\n  }\n\n  var editor2 = CodeMirror.fromTextArea(document.getElementById(\"synonyms\"), {\n    extraKeys: {\"Ctrl-Space\": \"autocomplete\"},\n    lineNumbers: true,\n    lineWrapping: true,\n    mode: \"text/x-markdown\",\n    hintOptions: {hint: synonyms}\n  })\n}\n</script>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/emacs.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Emacs bindings demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../addon/dialog/dialog.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/clike/clike.js\"></script>\n<script src=\"../keymap/emacs.js\"></script>\n<script src=\"../addon/edit/matchbrackets.js\"></script>\n<script src=\"../addon/comment/comment.js\"></script>\n<script src=\"../addon/dialog/dialog.js\"></script>\n<script src=\"../addon/search/searchcursor.js\"></script>\n<script src=\"../addon/search/search.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Emacs bindings</a>\n  </ul>\n</div>\n\n<article>\n<h2>Emacs bindings demo</h2>\n<form><textarea id=\"code\" name=\"code\">\n#include \"syscalls.h\"\n/* getchar:  simple buffered version */\nint getchar(void)\n{\n  static char buf[BUFSIZ];\n  static char *bufp = buf;\n  static int n = 0;\n  if (n == 0) {  /* buffer is empty */\n    n = read(0, buf, sizeof buf);\n    bufp = buf;\n  }\n  return (--n >= 0) ? (unsigned char) *bufp++ : EOF;\n}\n</textarea></form>\n\n<p>The emacs keybindings are enabled by\nincluding <a href=\"../keymap/emacs.js\">keymap/emacs.js</a> and setting\nthe <code>keyMap</code> option to <code>\"emacs\"</code>. Because\nCodeMirror's internal API is quite different from Emacs, they are only\na loose approximation of actual emacs bindings, though.</p>\n\n<p>Also note that a lot of browsers disallow certain keys from being\ncaptured. For example, Chrome blocks both Ctrl-W and Ctrl-N, with the\nresult that idiomatic use of Emacs keys will constantly close your tab\nor open a new window.</p>\n\n    <script>\n      CodeMirror.commands.save = function() {\n        var elt = editor.getWrapperElement();\n        elt.style.background = \"#def\";\n        setTimeout(function() { elt.style.background = \"\"; }, 300);\n      };\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        mode: \"text/x-csrc\",\n        matchBrackets: true,\n        keyMap: \"emacs\"\n      });\n    </script>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/folding.html",
    "content": "<!doctype html>\n\n<head>\n  <title>CodeMirror: Code Folding Demo</title>\n  <meta charset=\"utf-8\"/>\n  <link rel=stylesheet href=\"../doc/docs.css\">\n\n  <style>\n    .some-css {\n      color: red;\n      line-height: 2;\n    }\n  </style>\n\n  <link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n  <link rel=\"stylesheet\" href=\"../addon/fold/foldgutter.css\" />\n  <script src=\"../lib/codemirror.js\"></script>\n  <script src=\"../addon/fold/foldcode.js\"></script>\n  <script src=\"../addon/fold/foldgutter.js\"></script>\n  <script src=\"../addon/fold/brace-fold.js\"></script>\n  <script src=\"../addon/fold/xml-fold.js\"></script>\n  <script src=\"../addon/fold/indent-fold.js\"></script>\n  <script src=\"../addon/fold/markdown-fold.js\"></script>\n  <script src=\"../addon/fold/comment-fold.js\"></script>\n  <script src=\"../mode/javascript/javascript.js\"></script>\n  <script src=\"../mode/xml/xml.js\"></script>\n  <script src=\"../mode/css/css.js\"></script>\n  <script src=\"../mode/htmlmixed/htmlmixed.js\"></script>\n  <script src=\"../mode/python/python.js\"></script>\n  <script src=\"../mode/markdown/markdown.js\"></script>\n  <style>\n    .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n  </style>\n</head>\n\n<body>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Code Folding</a>\n  </ul>\n</div>\n\n<article>\n  <h2>Code Folding Demo</h2>\n  <form>\n    <div style=\"max-width: 50em; margin-bottom: 1em\">JavaScript:<br>\n    <textarea id=\"code\" name=\"code\"></textarea></div>\n    <div style=\"max-width: 50em; margin-bottom: 1em\">HTML:<br>\n    <textarea id=\"code-html\" name=\"code-html\"></textarea></div>    \n    <div style=\"max-width: 50em; margin-bottom: 1em\">JSON with custom widget:<br>\n    <textarea id=\"code-json\" name=\"code-json\">\n{\n  \"menu\": {\n    \"id\": \"file\",\n    \"value\": \"File\",\n    \"popup\": {\n      \"menuitem\": [\n        {\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"},\n        {\"value\": \"Open\", \"onclick\": \"OpenDoc()\"},\n        {\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}\n      ]\n    }\n  }\n}\n    </textarea></div>\n    <div style=\"max-width: 50em\">Python:<br>\n    <textarea id=\"code-python\" name=\"code\">\ndef foo():\n  do_some_stuff()\n  here\n  return None\n\nclass Bar:\n  __init__(self):\n    if True:\n      print(\"True\")\n    else:\n      print(\"False\")\n\n  this_code_makes_no_sense():\n    pass\n\n# A comment</textarea></div>\n    <div style=\"max-width: 50em\">Markdown:<br>\n    <textarea id=\"code-markdown\" name=\"code\"></textarea></div>\n  </form>\n  <script id=\"script\">\n/*\n * Demonstration of code folding\n */\nwindow.onload = function() {\n  var te = document.getElementById(\"code\");\n  var sc = document.getElementById(\"script\");\n  te.value = (sc.textContent || sc.innerText || sc.innerHTML).replace(/^\\s*/, \"\");\n  sc.innerHTML = \"\";\n  var te_html = document.getElementById(\"code-html\");\n  te_html.value = document.documentElement.innerHTML;\n  var te_python = document.getElementById(\"code-python\");\n  var te_markdown = document.getElementById(\"code-markdown\");\n  te_markdown.value = \"# Foo\\n## Bar\\n\\nblah blah\\n\\n## Baz\\n\\nblah blah\\n\\n# Quux\\n\\nblah blah\\n\"\n  var te_json = document.getElementById(\"code-json\");\n\n  window.editor = CodeMirror.fromTextArea(te, {\n    mode: \"javascript\",\n    lineNumbers: true,\n    lineWrapping: true,\n    extraKeys: {\"Ctrl-Q\": function(cm){ cm.foldCode(cm.getCursor()); }},\n    foldGutter: true,\n    gutters: [\"CodeMirror-linenumbers\", \"CodeMirror-foldgutter\"]\n  });\n  editor.foldCode(CodeMirror.Pos(13, 0));\n\n  window.editor_json = CodeMirror.fromTextArea(te_json, {\n    mode: {name: \"javascript\", json: true},\n    lineNumbers: true,\n    lineWrapping: true,\n    extraKeys: {\"Ctrl-Q\": function(cm){ cm.foldCode(cm.getCursor()); }},\n    foldGutter: true,\n    gutters: [\"CodeMirror-linenumbers\", \"CodeMirror-foldgutter\"],\n    foldOptions: {\n      widget: (from, to) => {\n        var count = undefined;\n\n        // Get open / close token\n        var startToken = '{', endToken = '}';        \n        var prevLine = window.editor_json.getLine(from.line);\n        if (prevLine.lastIndexOf('[') > prevLine.lastIndexOf('{')) {\n          startToken = '[', endToken = ']';\n        }\n\n        // Get json content\n        var internal = window.editor_json.getRange(from, to);\n        var toParse = startToken + internal + endToken;\n\n        // Get key count\n        try {\n          var parsed = JSON.parse(toParse);\n          count = Object.keys(parsed).length;\n        } catch(e) { }        \n\n        return count ? `\\u21A4${count}\\u21A6` : '\\u2194';\n      }\n    }\n  });\n  editor_json.foldCode(CodeMirror.Pos(5, 0));\n\n  window.editor_html = CodeMirror.fromTextArea(te_html, {\n    mode: \"text/html\",\n    lineNumbers: true,\n    lineWrapping: true,\n    extraKeys: {\"Ctrl-Q\": function(cm){ cm.foldCode(cm.getCursor()); }},\n    foldGutter: true,\n    gutters: [\"CodeMirror-linenumbers\", \"CodeMirror-foldgutter\"]\n  });\n  editor_html.foldCode(CodeMirror.Pos(0, 0));\n  editor_html.foldCode(CodeMirror.Pos(34, 0));\n\n  window.editor_python = CodeMirror.fromTextArea(te_python, {\n    mode: \"python\",\n    lineNumbers: true,\n    extraKeys: {\"Ctrl-Q\": function(cm){ cm.foldCode(cm.getCursor()); }},\n    foldGutter: true,\n    gutters: [\"CodeMirror-linenumbers\", \"CodeMirror-foldgutter\"]\n  });\n\n  window.editor_markdown = CodeMirror.fromTextArea(te_markdown, {\n    mode: \"markdown\",\n    lineNumbers: true,\n    lineWrapping: true,\n    extraKeys: {\"Ctrl-Q\": function(cm){ cm.foldCode(cm.getCursor()); }},\n    foldGutter: true,\n    gutters: [\"CodeMirror-linenumbers\", \"CodeMirror-foldgutter\"]\n  });\n};\n  </script>\n</article>\n</body>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/fullscreen.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Full Screen Editing</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../addon/display/fullscreen.css\">\n<link rel=\"stylesheet\" href=\"../theme/night.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<script src=\"../addon/display/fullscreen.js\"></script>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Full Screen Editing</a>\n  </ul>\n</div>\n\n<article>\n<h2>Full Screen Editing</h2>\n<form><textarea id=\"code\" name=\"code\" rows=\"5\">\n<dl>\n  <dt id=\"option_indentWithTabs\"><code><strong>indentWithTabs</strong>: boolean</code></dt>\n  <dd>Whether, when indenting, the first N*<code>tabSize</code>\n  spaces should be replaced by N tabs. Default is false.</dd>\n\n  <dt id=\"option_electricChars\"><code><strong>electricChars</strong>: boolean</code></dt>\n  <dd>Configures whether the editor should re-indent the current\n  line when a character is typed that might change its proper\n  indentation (only works if the mode supports indentation).\n  Default is true.</dd>\n\n  <dt id=\"option_specialChars\"><code><strong>specialChars</strong>: RegExp</code></dt>\n  <dd>A regular expression used to determine which characters\n  should be replaced by a\n  special <a href=\"#option_specialCharPlaceholder\">placeholder</a>.\n  Mostly useful for non-printing special characters. The default\n  is <code>/[\\u0000-\\u0019\\u00ad\\u200b\\u2028\\u2029\\ufeff]/</code>.</dd>\n  <dt id=\"option_specialCharPlaceholder\"><code><strong>specialCharPlaceholder</strong>: function(char) → Element</code></dt>\n  <dd>A function that, given a special character identified by\n  the <a href=\"#option_specialChars\"><code>specialChars</code></a>\n  option, produces a DOM node that is used to represent the\n  character. By default, a red dot (<span style=\"color: red\">•</span>)\n  is shown, with a title tooltip to indicate the character code.</dd>\n\n  <dt id=\"option_rtlMoveVisually\"><code><strong>rtlMoveVisually</strong>: boolean</code></dt>\n  <dd>Determines whether horizontal cursor movement through\n  right-to-left (Arabic, Hebrew) text is visual (pressing the left\n  arrow moves the cursor left) or logical (pressing the left arrow\n  moves to the next lower index in the string, which is visually\n  right in right-to-left text). The default is <code>false</code>\n  on Windows, and <code>true</code> on other platforms.</dd>\n</dl>\n</textarea></form>\n  <script>\n    var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n      lineNumbers: true,\n      theme: \"night\",\n      extraKeys: {\n        \"F11\": function(cm) {\n          cm.setOption(\"fullScreen\", !cm.getOption(\"fullScreen\"));\n        },\n        \"Esc\": function(cm) {\n          if (cm.getOption(\"fullScreen\")) cm.setOption(\"fullScreen\", false);\n        }\n      }\n    });\n  </script>\n\n    <p>Demonstration of\n    the <a href=\"../doc/manual.html#addon_fullscreen\">fullscreen</a>\n    addon. Press <strong>F11</strong> when cursor is in the editor to\n    toggle full screen editing. <strong>Esc</strong> can also be used\n    to <i>exit</i> full screen editing.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/hardwrap.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Hard-wrapping Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/markdown/markdown.js\"></script>\n<script src=\"../addon/wrap/hardwrap.js\"></script>\n<style>\n  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Hard-wrapping</a>\n  </ul>\n</div>\n\n<article>\n<h2>Hard-wrapping Demo</h2>\n<form><textarea id=\"code\" name=\"code\">Lorem ipsum dolor sit amet, vim augue dictas constituto ex,\nsit falli simul viderer te. Graeco scaevola maluisset sit\nut, in idque viris praesent sea. Ea sea eirmod indoctum\nrepudiare. Vel noluisse suscipit pericula ut. In ius nulla\nalienum molestie. Mei essent discere democritum id.\n\nEquidem ponderum expetendis ius in, mea an erroribus\nconstituto, congue timeam perfecto ad est. Ius ut primis\ntimeam, per in ullum mediocrem. An case vero labitur pri,\nvel dicit laoreet et. An qui prompta conclusionemque, eam\ntimeam sapientem in, cum dictas epicurei eu.\n\nUsu cu vide dictas deseruisse, eum choro graece adipiscing\nut. Cibo qualisque ius ad, et dicat scripta mea, eam nihil\nmentitum aliquando cu. Debet aperiam splendide at quo, ad\npaulo nostro commodo duo. Sea adhuc utinam conclusionemque\nid, quas doming malorum nec ad. Tollit eruditi vivendum ad\nius, eos soleat ignota ad.\n</textarea></form>\n\n<p>Demonstration of\nthe <a href=\"../doc/manual.html#addon_hardwrap\">hardwrap</a> addon.\nThe above editor has its change event hooked up to\nthe <code>wrapParagraphsInRange</code> method, so that the paragraphs\nare reflown as you are typing.</p>\n\n<script>\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  mode: \"markdown\",\n  lineNumbers: true,\n  extraKeys: {\n    \"Ctrl-Q\": function(cm) { cm.wrapParagraph(cm.getCursor(), options); }\n  }\n});\nvar wait, options = {column: 60}, changing = false;\neditor.on(\"change\", function(cm, change) {\n  if (changing) return;\n  clearTimeout(wait);\n  wait = setTimeout(function() {\n    changing = true;\n    cm.wrapParagraphsInRange(change.from, CodeMirror.changeEnd(change), options);\n    changing = false;\n  }, 200);\n});\n</script>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/html5complete.html",
    "content": "<!doctype html>\n\n<head>\n  <title>CodeMirror: HTML completion demo</title>\n  <meta charset=\"utf-8\"/>\n  <link rel=stylesheet href=\"../doc/docs.css\">\n\n  <link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n  <link rel=\"stylesheet\" href=\"../addon/hint/show-hint.css\">\n  <script src=\"../lib/codemirror.js\"></script>\n  <script src=\"../addon/hint/show-hint.js\"></script>\n  <script src=\"../addon/hint/xml-hint.js\"></script>\n  <script src=\"../addon/hint/html-hint.js\"></script>\n  <script src=\"../mode/xml/xml.js\"></script>\n  <script src=\"../mode/javascript/javascript.js\"></script>\n  <script src=\"../mode/css/css.js\"></script>\n  <script src=\"../mode/htmlmixed/htmlmixed.js\"></script>\n  <style>\n    .CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}\n  </style>\n</head>\n\n<body>\n  <div id=nav>\n    <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n    <ul>\n      <li><a href=\"../index.html\">Home</a>\n      <li><a href=\"../doc/manual.html\">Manual</a>\n      <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n    </ul>\n    <ul>\n      <li><a class=active href=\"#\">HTML completion</a>\n    </ul>\n  </div>\n\n  <article>\n    <h2>HTML completion demo</h2>\n\n    <p>Shows the <a href=\"xmlcomplete.html\">XML completer</a>\n    parameterized with information about the tags in HTML.\n    Press <strong>ctrl-space</strong> to activate completion.</p>\n\n    <div id=\"code\"></div>\n\n    <script>\n      window.onload = function() {\n        editor = CodeMirror(document.getElementById(\"code\"), {\n          mode: \"text/html\",\n          extraKeys: {\"Ctrl-Space\": \"autocomplete\"},\n          value: document.documentElement.innerHTML\n        });\n      };\n    </script>\n  </article>\n</body>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/indentwrap.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Indented wrapped line demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n      .CodeMirror pre > * { text-indent: 0px; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Indented wrapped line</a>\n  </ul>\n</div>\n\n<article>\n<h2>Indented wrapped line demo</h2>\n<form><textarea id=\"code\" name=\"code\">\n<!doctype html>\n<body>\n  <h2 id=\"overview\">Overview</h2>\n\n  <p>CodeMirror is a code-editor component that can be embedded in Web pages. The core library provides <em>only</em> the editor component, no accompanying buttons, auto-completion, or other IDE functionality. It does provide a rich API on top of which such functionality can be straightforwardly implemented. See the <a href=\"#addons\">add-ons</a> included in the distribution, and the <a href=\"https://github.com/jagthedrummer/codemirror-ui\">CodeMirror UI</a> project, for reusable implementations of extra features.</p>\n\n  <p>CodeMirror works with language-specific modes. Modes are JavaScript programs that help color (and optionally indent) text written in a given language. The distribution comes with a number of modes (see the <a href=\"../mode/\"><code>mode/</code></a> directory), and it isn't hard to <a href=\"#modeapi\">write new ones</a> for other languages.</p>\n</body>\n</textarea></form>\n\n    <p>This page uses a hack on top of the <code>\"renderLine\"</code>\n    event to make wrapped text line up with the base indentation of\n    the line.</p>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        lineWrapping: true,\n        mode: \"text/html\"\n      });\n      var charWidth = editor.defaultCharWidth(), basePadding = 4;\n      editor.on(\"renderLine\", function(cm, line, elt) {\n        var off = CodeMirror.countColumn(line.text, null, cm.getOption(\"tabSize\")) * charWidth;\n        elt.style.textIndent = \"-\" + off + \"px\";\n        elt.style.paddingLeft = (basePadding + off) + \"px\";\n      });\n      editor.refresh();\n    </script>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/lint.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Linter Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../addon/lint/lint.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"../mode/css/css.js\"></script>\n<script src=\"https://unpkg.com/jshint@2.13.2/dist/jshint.js\"></script>\n<script src=\"https://unpkg.com/jsonlint@1.6.3/web/jsonlint.js\"></script>\n<script src=\"https://unpkg.com/csslint@1.0.5/dist/csslint.js\"></script>\n<script src=\"../addon/lint/lint.js\"></script>\n<script src=\"../addon/lint/javascript-lint.js\"></script>\n<script src=\"../addon/lint/json-lint.js\"></script>\n<script src=\"../addon/lint/css-lint.js\"></script>\n<style>\n      .CodeMirror {border: 1px solid black;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Linter</a>\n  </ul>\n</div>\n\n<article>\n<h2>Linter Demo</h2>\n\n\n    <p><textarea id=\"code-js\">var widgets = []\nfunction updateHints() {\n  editor.operation(function(){\n    for (var i = 0; i < widgets.length.; ++i)\n      editor.removeLineWidget(widgets[i]);\n    widgets.length = 0;\n\n    JSHINT(editor.getValue());\n    for (var i = 0; i < JSHINT.errors.length; ++i) {\n      var err = JSHINT.errors[i];\n      if (!err) continue;\n      var msg = document.createElement(\"div\");\n      var icon = msg.appendChild(document.createElement(\"span\"));\n      icon.innerHTML = \"!!\";\n      icon.className = \"lint-error-icon\";\n      msg.appendChild(document.createTextNode(err.reason));\n      msg.className = \"lint-error\";\n      widgets.push(editor.addLineWidget(err.line - 1, msg, {coverGutter: false, noHScroll: true}));\n    }\n  });\n  var info = editor.getScrollInfo();\n  var after = editor.charCoords({line: editor.getCursor().line + 1, ch: 0}, \"local\").top;\n  if (info.top + info.clientHeight < after)\n    editor.scrollTo(null, after - info.clientHeight + 3);\n}\n</textarea></p>\n\n    <p><textarea id=\"code-json\">[\n {\n  _id: \"post 1\",\n  \"author\": \"Bob\",\n  \"content\": \"...\",\n  \"page_views\": 5\n },\n {\n  \"_id\": \"post 2\",\n  \"author\": \"Bob\",\n  \"content\": \"...\",\n  \"page_views\": 9\n },\n {\n  \"_id\": \"post 3\",\n  \"author\": \"Bob\",\n  \"content\": \"...\",\n  \"page_views\": 8\n }\n]\n</textarea></p>\n\n    <p><textarea id=\"code-css\">@charset \"UTF-8\";\n\n@import url(\"booya.css\") print, screen;\n@import \"whatup.css\" screen;\n@import \"wicked.css\";\n\n/*Error*/\n@charset \"UTF-8\";\n\n\n@namespace \"http://www.w3.org/1999/xhtml\";\n@namespace svg \"http://www.w3.org/2000/svg\";\n\n/*Warning: empty ruleset */\n.foo {\n}\n\nh1 {\n    font-weight: bold;\n}\n\n/*Warning: qualified heading */\n.foo h1 {\n    font-weight: bold;\n}\n\n/*Warning: adjoining classes */\n.foo.bar {\n    zoom: 1;\n}\n\nli.inline {\n    width: 100%;  /*Warning: 100% can be problematic*/\n}\n\nli.last {\n  display: inline;\n  padding-left: 3px !important;\n  padding-right: 3px;\n  border-right: 0px;\n}\n\n@media print {\n    li.inline {\n      color: black;\n    }\n}\n\n@page {\n  margin: 10%;\n  counter-increment: page;\n\n  @top-center {\n    font-family: sans-serif;\n    font-weight: bold;\n    font-size: 2em;\n    content: counter(page);\n  }\n}\n</textarea></p>\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById(\"code-js\"), {\n    lineNumbers: true,\n    mode: \"javascript\",\n    gutters: [\"CodeMirror-lint-markers\"],\n    lint: {options: {esversion: 2021}},\n  });\n\n  var editor_json = CodeMirror.fromTextArea(document.getElementById(\"code-json\"), {\n    lineNumbers: true,\n    mode: \"application/json\",\n    gutters: [\"CodeMirror-lint-markers\"],\n    lint: true\n  });\n  \n  var editor_css = CodeMirror.fromTextArea(document.getElementById(\"code-css\"), {\n    lineNumbers: true,\n    mode: \"css\",\n    gutters: [\"CodeMirror-lint-markers\"],\n    lint: true\n  });\n</script>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/loadmode.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Lazy Mode Loading Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/mode/loadmode.js\"></script>\n<script src=\"../mode/meta.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Lazy Mode Loading</a>\n  </ul>\n</div>\n\n<article>\n<h2>Lazy Mode Loading Demo</h2>\n<p style=\"color: gray\">Current mode: <span id=\"modeinfo\">text/plain</span></p>\n<form><textarea id=\"code\" name=\"code\">This is the editor.\n// It starts out in plain text mode,\n#  use the control below to load and apply a mode\n  \"you'll see the highlighting of\" this text /*change*/.\n</textarea></form>\n<p>Filename, mime, or mode name: <input type=text value=foo.js id=mode> <button type=button onclick=\"change()\">change mode</button></p>\n\n    <script>\nCodeMirror.modeURL = \"../mode/%N/%N.js\";\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  lineNumbers: true\n});\nvar modeInput = document.getElementById(\"mode\");\nCodeMirror.on(modeInput, \"keypress\", function(e) {\n  if (e.keyCode == 13) change();\n});\nfunction change() {\n  var val = modeInput.value, m, mode, spec;\n  if (m = /.+\\.([^.]+)$/.exec(val)) {\n    var info = CodeMirror.findModeByExtension(m[1]);\n    if (info) {\n      mode = info.mode;\n      spec = info.mime;\n    }\n  } else if (/\\//.test(val)) {\n    var info = CodeMirror.findModeByMIME(val);\n    if (info) {\n      mode = info.mode;\n      spec = val;\n    }\n  } else {\n    mode = spec = val;\n  }\n  if (mode) {\n    editor.setOption(\"mode\", spec);\n    CodeMirror.autoLoadMode(editor, mode);\n    document.getElementById(\"modeinfo\").textContent = spec;\n  } else {\n    alert(\"Could not find a mode corresponding to \" + val);\n  }\n}\n</script>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/marker.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Breakpoint Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<style>\n      .breakpoints {width: .8em;}\n      .breakpoint { color: #822; }\n      .CodeMirror {border: 1px solid #aaa;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Breakpoint</a>\n  </ul>\n</div>\n\n<article>\n<h2>Breakpoint Demo</h2>\n<form><textarea id=\"code\" name=\"code\">\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  lineNumbers: true,\n  gutters: [\"CodeMirror-linenumbers\", \"breakpoints\"]\n});\neditor.on(\"gutterClick\", function(cm, n) {\n  var info = cm.lineInfo(n);\n  cm.setGutterMarker(n, \"breakpoints\", info.gutterMarkers ? null : makeMarker());\n});\n\nfunction makeMarker() {\n  var marker = document.createElement(\"div\");\n  marker.style.color = \"#822\";\n  marker.innerHTML = \"●\";\n  return marker;\n}\n</textarea></form>\n\n<p>Click the line-number gutter to add or remove 'breakpoints'.</p>\n\n    <script>eval(document.getElementById(\"code\").value);</script>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/markselection.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Selection Marking Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/search/searchcursor.js\"></script>\n<script src=\"../addon/selection/mark-selection.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n      .CodeMirror-selected  { background-color: blue !important; }\n      .CodeMirror-selectedtext { color: white; }\n      .styled-background { background-color: #ff7; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Selection Marking</a>\n  </ul>\n</div>\n\n<article>\n<h2>Selection Marking Demo</h2>\n<form><textarea id=\"code\" name=\"code\">\nSelect something from here. You'll see that the selection's foreground\ncolor changes to white! Since, by default, CodeMirror only puts an\nindependent \"marker\" layer behind the text, you'll need something like\nthis to change its colour.\n\nAlso notice that turning this addon on (with the default style) allows\nyou to safely give text a background color without screwing up the\nvisibility of the selection.</textarea></form>\n\n    <script>\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  lineNumbers: true,\n  styleSelectedText: true\n});\neditor.markText({line: 6, ch: 26}, {line: 6, ch: 42}, {className: \"styled-background\"});\n</script>\n\n    <p>Simple addon to easily mark (and style) selected text. <a href=\"../doc/manual.html#addon_mark-selection\">Docs</a>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/matchhighlighter.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Match Highlighter Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/scroll/annotatescrollbar.js\"></script>\n<script src=\"../addon/search/matchesonscrollbar.js\"></script>\n<script src=\"../addon/search/searchcursor.js\"></script>\n<script src=\"../addon/search/match-highlighter.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n      .CodeMirror-focused .cm-matchhighlight {\n        background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);\n        background-position: bottom;\n        background-repeat: repeat-x;\n      }\n      .cm-matchhighlight {background-color: lightgreen}\n      .CodeMirror-selection-highlight-scrollbar {background-color: green}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Match Highlighter</a>\n  </ul>\n</div>\n\n<article>\n<h2>Match Highlighter Demo</h2>\n<form><textarea id=\"code\" name=\"code\">Select this text: hardtospot\n  And everywhere else in your code where hardtospot appears will\nautomatically illuminate.  Give it a try!  No more hard to spot\nvariables - stay in context of your code all the time.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Ut pharetra\ninterdum dui eu pulvinar. Mauris maximus ligula venenatis tempus\ninterdum. Cras hendrerit, ipsum sed ultrices pharetra, ligula diam\nporttitor lacus, ac tempor eros est a massa. Nam orci elit, vulputate\nin tristique quis, consectetur vitae metus. Pellentesque et enim\nelementum, lobortis augue in, lacinia sapien. Morbi eu nunc semper,\nsagittis felis a, pellentesque mauris. Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit. Aenean quis diam turpis.\n\nFusce lobortis nisl quis aliquet euismod. Aenean vitae nulla non ipsum\nefficitur scelerisque. Curabitur auctor, lorem non rhoncus porttitor,\naugue ligula lacinia dolor, et vehicula magna lorem imperdiet velit.\nFusce risus sem, hardtospot commodo eleifend hendrerit vitae, mollis\nquis risus. Cras tincidunt, justo vitae hendrerit venenatis, urna\ndolor placerat tortor, eu lobortis lectus dolor in ligula. Nullam non\nerat non nisl vulputate ultrices sit amet vestibulum dolor. Quisque in\ntortor porta, pellentesque odio nec, malesuada nibh.\n\nIn a dui feugiat, ullamcorper urna in, accumsan magna. Donec egestas\nsem nec eros rhoncus, vel gravida purus ornare. Nulla orci mauris,\nporta nec pharetra sed, ornare et lorem. Donec luctus turpis nunc,\neget dictum felis mollis et.  Sed sodales hardtospot nunc vitae leo\nrhoncus imperdiet. Donec elementum malesuada velit quis placerat.\nProin accumsan lorem id nisi volutpat ullamcorper. Vivamus laoreet\ndolor ac sem malesuada, ac scelerisque ex efficitur. Aliquam tempus\nlibero velit, vel tristique augue vulputate nec.\n\nMauris ultrices leo felis, sit amet congue augue aliquam condimentum.\nVivamus purus leo, mattis vitae dignissim vel, ultricies ac ex. Mauris\neu dolor eu purus ultricies ultrices. Sed euismod feugiat ex et\nmattis. Morbi cursus laoreet pharetra. Donec eu dolor sodales,\nultricies nisi et, malesuada urna.  Praesent sit amet fringilla felis.\nNam rhoncus, est blandit auctor auctor, lorem ipsum laoreet ipsum,\nquis sodales libero odio in lorem. Phasellus odio dolor, elementum\nsagittis nibh non, fermentum semper libero. Mauris hendrerit\nhardtospot lectus sit amet commodo eleifend. Morbi pulvinar eget nisl\nat eleifend. Fusce eget porta erat, vitae lobortis libero.\n\nPhasellus sit amet massa in massa pharetra malesuada. Vestibulum at\nquam vel libero aliquam volutpat at ut dui. Praesent scelerisque vel\nmauris sit amet vehicula. Phasellus at mi nec ligula cursus interdum\nsit amet non quam. Aliquam tempus sollicitudin euismod. Nulla euismod\nmollis enim tincidunt placerat.  Proin ac scelerisque enim, quis\nsollicitudin metus. Pellentesque congue nec sapien ut rhoncus. Sed\neget ornare diam, ut consectetur ante. Aenean eleifend mauris quis\nornare accumsan. In hac habitasse hardtospot platea dictumst.\n\n</textarea></form>\n\n    <script>\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  lineNumbers: true,\n  // To highlight on scrollbars as well, pass annotateScrollbar in options\n  // as below.\n  highlightSelectionMatches: {showToken: /\\w/, annotateScrollbar: true}\n});\n</script>\n\n    <p>Search and highlight occurrences of the selected text.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/matchtags.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Tag Matcher Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/fold/xml-fold.js\"></script>\n<script src=\"../addon/edit/matchtags.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Tag Matcher</a>\n  </ul>\n</div>\n\n<article>\n<h2>Tag Matcher Demo</h2>\n\n\n    <div id=\"editor\"></div>\n\n    <script>\nwindow.onload = function() {\n  editor = CodeMirror(document.getElementById(\"editor\"), {\n    value: \"<html>\\n  \" + document.documentElement.innerHTML + \"\\n</html>\",\n    mode: \"text/html\",\n    matchTags: {bothTags: true},\n    extraKeys: {\"Ctrl-J\": \"toMatchingTag\"}\n  });\n};\n    </script>\n\n    <p>Put the cursor on or inside a pair of tags to highlight them.\n    Press Ctrl-J to jump to the tag that matches the one under the\n    cursor.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/merge.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: merge view demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=stylesheet href=\"../lib/codemirror.css\">\n<link rel=stylesheet href=\"../addon/merge/merge.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<script src=\"../mode/css/css.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"../mode/htmlmixed/htmlmixed.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/diff_match_patch/20121119/diff_match_patch.js\"></script>\n<script src=\"../addon/merge/merge.js\"></script>\n<style>\n    .CodeMirror { line-height: 1.2; }\n    @media screen and (min-width: 1300px) {\n      article { max-width: 1000px; }\n      #nav { border-right: 499px solid transparent; }\n    }\n    span.clicky {\n      cursor: pointer;\n      background: #d70;\n      color: white;\n      padding: 0 3px;\n      border-radius: 3px;\n    }\n  </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">merge view</a>\n  </ul>\n</div>\n\n<article>\n<h2>merge view demo</h2>\n\n\n<div id=view></div>\n\n<p>The <a href=\"../doc/manual.html#addon_merge\"><code>merge</code></a>\naddon provides an interface for displaying and merging diffs,\neither <span class=clicky onclick=\"panes = 2; initUI()\">two-way</span>\nor <span class=clicky onclick=\"panes = 3; initUI()\">three-way</span>.\nThe left (or center) pane is editable, and the differences with the\nother pane(s) are <span class=clicky\nonclick=\"toggleDifferences()\">optionally</span> shown live as you edit\nit. In the two-way configuration, there are also options to pad changed\nsections to <span class=clicky onclick=\"connect = connect ? null :\n'align'; initUI()\">align</span> them, and to <span class=clicky\nonclick=\"collapse = !collapse; initUI()\">collapse</span> unchanged\nstretches of text.</p>\n\n<p>This addon depends on\nthe <a href=\"https://code.google.com/p/google-diff-match-patch/\">google-diff-match-patch</a>\nlibrary to compute the diffs.</p>\n\n<script>\nvar value, orig1, orig2, dv, panes = 2, highlight = true, connect = \"align\", collapse = false;\nfunction initUI() {\n  if (value == null) return;\n  var target = document.getElementById(\"view\");\n  target.innerHTML = \"\";\n  dv = CodeMirror.MergeView(target, {\n    value: value,\n    origLeft: panes == 3 ? orig1 : null,\n    orig: orig2,\n    lineNumbers: true,\n    mode: \"text/html\",\n    highlightDifferences: highlight,\n    connect: connect,\n    collapseIdentical: collapse\n  });\n}\n\nfunction toggleDifferences() {\n  dv.setShowDifferences(highlight = !highlight);\n}\n\nwindow.onload = function() {\n  value = document.documentElement.innerHTML;\n  orig1 = \"<!doctype html>\\n\\n\" + value.replace(/\\.\\.\\//g, \"codemirror/\").replace(\"yellow\", \"orange\");\n  orig2 = value.replace(/\\u003cscript/g, \"\\u003cscript type=text/javascript \")\n    .replace(\"white\", \"purple;\\n      font: comic sans;\\n      text-decoration: underline;\\n      height: 15em\");\n  initUI();\n  let d = document.createElement(\"div\"); d.style.cssText = \"width: 50px; margin: 7px; height: 14px\"; dv.editor().addLineWidget(57, d)\n};\n\nfunction mergeViewHeight(mergeView) {\n  function editorHeight(editor) {\n    if (!editor) return 0;\n    return editor.getScrollInfo().height;\n  }\n  return Math.max(editorHeight(mergeView.leftOriginal()),\n                  editorHeight(mergeView.editor()),\n                  editorHeight(mergeView.rightOriginal()));\n}\n\nfunction resize(mergeView) {\n  var height = mergeViewHeight(mergeView);\n  for(;;) {\n    if (mergeView.leftOriginal())\n      mergeView.leftOriginal().setSize(null, height);\n    mergeView.editor().setSize(null, height);\n    if (mergeView.rightOriginal())\n      mergeView.rightOriginal().setSize(null, height);\n\n    var newHeight = mergeViewHeight(mergeView);\n    if (newHeight >= height) break;\n    else height = newHeight;\n  }\n  mergeView.wrap.style.height = height + \"px\";\n}\n</script>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/multiplex.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Multiplexing Parser Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/mode/multiplex.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<style>\n      .CodeMirror {border: 1px solid black;}\n      .cm-delimit {color: #fa4;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Multiplexing Parser</a>\n  </ul>\n</div>\n\n<article>\n<h2>Multiplexing Parser Demo</h2>\n<form><textarea id=\"code\" name=\"code\">\n<html>\n  <body style=\"<<magic>>\">\n    <h1><< this is not <html >></h1>\n    <<\n        multiline\n        not html\n        at all : &amp;amp; <link/>\n    >>\n    <p>this is html again</p>\n  </body>\n</html>\n</textarea></form>\n\n    <script>\nCodeMirror.defineMode(\"demo\", function(config) {\n  return CodeMirror.multiplexingMode(\n    CodeMirror.getMode(config, \"text/html\"),\n    {open: \"<<\", close: \">>\",\n     mode: CodeMirror.getMode(config, \"text/plain\"),\n     delimStyle: \"delimit\"}\n    // .. more multiplexed styles can follow here\n  );\n});\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  mode: \"demo\",\n  lineNumbers: true,\n  lineWrapping: true\n});\n</script>\n\n    <p>Demonstration of a multiplexing mode, which, at certain\n    boundary strings, switches to one or more inner modes. The out\n    (HTML) mode does not get fed the content of the <code>&lt;&lt;\n    >></code> blocks. See\n    the <a href=\"../doc/manual.html#addon_multiplex\">manual</a> and\n    the <a href=\"../addon/mode/multiplex.js\">source</a> for more\n    information.</p>\n\n    <p>\n      <strong>Parsing/Highlighting Tests:</strong>\n      <a href=\"../test/index.html#multiplexing_*\">normal</a>,\n      <a href=\"../test/index.html#verbose,multiplexing_*\">verbose</a>.\n    </p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/mustache.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Overlay Parser Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/mode/overlay.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<style>\n      .CodeMirror {border: 1px solid black;}\n      .cm-mustache {color: #0ca;}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Overlay Parser</a>\n  </ul>\n</div>\n\n<article>\n<h2>Overlay Parser Demo</h2>\n<form><textarea id=\"code\" name=\"code\">\n<html>\n  <body>\n    <h1>{{title}}</h1>\n    <p>These are links to {{things}}:</p>\n    <ul>{{#links}}\n      <li><a href=\"{{url}}\">{{text}}</a></li>\n    {{/links}}</ul>\n  </body>\n</html>\n</textarea></form>\n\n    <script>\nCodeMirror.defineMode(\"mustache\", function(config, parserConfig) {\n  var mustacheOverlay = {\n    token: function(stream, state) {\n      var ch;\n      if (stream.match(\"{{\")) {\n        while ((ch = stream.next()) != null)\n          if (ch == \"}\" && stream.next() == \"}\") {\n            stream.eat(\"}\");\n            return \"mustache\";\n          }\n      }\n      while (stream.next() != null && !stream.match(\"{{\", false)) {}\n      return null;\n    }\n  };\n  return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || \"text/html\"), mustacheOverlay);\n});\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {mode: \"mustache\"});\n</script>\n\n    <p>Demonstration of a mode that parses HTML, highlighting\n    the <a href=\"http://mustache.github.io/\">Mustache</a> templating\n    directives inside of it by using the code\n    in <a href=\"../addon/mode/overlay.js\"><code>overlay.js</code></a>. View\n    source to see the 15 lines of code needed to accomplish this.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/panel.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Panel Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<script src=\"../mode/htmlmixed/htmlmixed.js\"></script>\n<script src=\"../addon/display/panel.js\"></script>\n<style>\n  .border {\n    border: 1px solid #f7f7f7;\n  }\n  .add-panel {\n    background: orange;\n    padding: 3px 6px;\n    color: white !important; \n    border-radius: 3px;\n  }\n  .add-panel, .remove-panel {\n    cursor: pointer;\n  }\n  .remove-panel {\n    float: right;\n  }\n  .panel {\n    background: #f7f7f7;\n    padding: 3px 7px;\n    font-size: 0.85em;\n  }\n  .panel.top, .panel.after-top {\n    border-bottom: 1px solid #ddd;\n  }\n  .panel.bottom, .panel.before-bottom {\n    border-top: 1px solid #ddd;\n  }\n</style>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Panel</a>\n  </ul>\n</div>\n\n<article>\n\n<h2>Panel Demo</h2>\n\n<div class=\"border\">\n    <textarea id=\"code\" name=\"code\"></textarea>\n</div>\n\n<p>\n  The <a href=\"../doc/manual.html#addon_panel\"><code>panel</code></a>\n  addon allows you to display panels above or below an editor.\n  <br>\n  Click the links below to add panels at the given position:\n</p>\n\n<div id=\"demo\">\n<p>\n  <a class=\"add-panel\" onclick=\"addPanel('top')\">top</a>\n  <a class=\"add-panel\" onclick=\"addPanel('after-top')\">after-top</a>\n  <a class=\"add-panel\" onclick=\"addPanel('before-bottom')\">before-bottom</a>\n  <a class=\"add-panel\" onclick=\"addPanel('bottom')\">bottom</a>\n</p>\n<p>\n  You can also replace an existing panel:\n</p>\n<form onsubmit=\"return replacePanel(this);\" name=\"replace_panel\">\n  <input type=\"submit\" value=\"Replace panel n°\" />\n  <input type=\"number\" name=\"panel_id\" min=\"1\" value=\"1\" />\n</form>\n\n<script>\nvar textarea = document.getElementById(\"code\");\nvar demo = document.getElementById(\"demo\");\nvar numPanels = 0;\nvar panels = {};\nvar editor;\n\ntextarea.value = demo.innerHTML.trim();\neditor = CodeMirror.fromTextArea(textarea, {\n  lineNumbers: true,\n  mode: \"htmlmixed\"\n});\n\nfunction makePanel(where) {\n  var node = document.createElement(\"div\");\n  var id = ++numPanels;\n  var widget, close, label;\n\n  node.id = \"panel-\" + id;\n  node.className = \"panel \" + where;\n  close = node.appendChild(document.createElement(\"a\"));\n  close.setAttribute(\"title\", \"Remove me!\");\n  close.setAttribute(\"class\", \"remove-panel\");\n  close.textContent = \"✖\";\n  CodeMirror.on(close, \"mousedown\", function(e) {\n    e.preventDefault()\n    panels[node.id].clear();\n  });\n  label = node.appendChild(document.createElement(\"span\"));\n  label.textContent = \"I'm panel n°\" + id;\n  return node;\n}\nfunction addPanel(where) {\n  var node = makePanel(where);\n  panels[node.id] = editor.addPanel(node, {position: where, stable: true});\n}\n\naddPanel(\"top\");\naddPanel(\"bottom\");\n\nfunction replacePanel(form) {\n  var id = form.elements.panel_id.value;\n  var panel = panels[\"panel-\" + id];\n  var node = makePanel(\"\");\n\n  panels[node.id] = editor.addPanel(node, {replace: panel, position: \"after-top\", stable: true});\n  return false;\n}\n</script>\n\n</div>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/placeholder.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Placeholder demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/display/placeholder.js\"></script>\n<style>\n      .CodeMirror { border: 1px solid silver; }\n      .CodeMirror-empty { outline: 1px solid #c22; }\n      .CodeMirror-empty.CodeMirror-focused { outline: none; }\n      .CodeMirror pre.CodeMirror-placeholder { color: #999; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Placeholder</a>\n  </ul>\n</div>\n\n<article>\n<h2>Placeholder demo</h2>\n<form><textarea id=\"code\" name=\"code\" placeholder=\"Code goes here...\"></textarea></form>\n\n    <p>The <a href=\"../doc/manual.html#addon_placeholder\">placeholder</a>\n    plug-in adds an option <code>placeholder</code> that can be set to\n    make text appear in the editor when it is empty and not focused.\n    If the source textarea has a <code>placeholder</code> attribute,\n    it will automatically be inherited.</p>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true\n      });\n    </script>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/preview.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: HTML5 preview</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=stylesheet href=../lib/codemirror.css>\n<script src=../lib/codemirror.js></script>\n<script src=../mode/xml/xml.js></script>\n<script src=../mode/javascript/javascript.js></script>\n<script src=../mode/css/css.js></script>\n<script src=../mode/htmlmixed/htmlmixed.js></script>\n<style type=text/css>\n      .CodeMirror {\n        float: left;\n        width: 50%;\n        border: 1px solid black;\n      }\n      iframe {\n        width: 49%;\n        float: left;\n        height: 300px;\n        border: 1px solid black;\n        border-left: 0px;\n      }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">HTML5 preview</a>\n  </ul>\n</div>\n\n<article>\n<h2>HTML5 preview</h2>\n\n    <textarea id=code name=code>\n<!doctype html>\n<html>\n  <head>\n    <meta charset=utf-8>\n    <title>HTML5 canvas demo</title>\n    <style>p {font-family: monospace;}</style>\n  </head>\n  <body>\n    <p>Canvas pane goes here:</p>\n    <canvas id=pane width=300 height=200></canvas>\n    <script>\n      var canvas = document.getElementById('pane');\n      var context = canvas.getContext('2d');\n\n      context.fillStyle = 'rgb(250,0,0)';\n      context.fillRect(10, 10, 55, 50);\n\n      context.fillStyle = 'rgba(0, 0, 250, 0.5)';\n      context.fillRect(30, 30, 55, 50);\n    </script>\n  </body>\n</html></textarea>\n    <iframe id=preview></iframe>\n    <script>\n      var delay;\n      // Initialize CodeMirror editor with a nice html5 canvas demo.\n      var editor = CodeMirror.fromTextArea(document.getElementById('code'), {\n        mode: 'text/html'\n      });\n      editor.on(\"change\", function() {\n        clearTimeout(delay);\n        delay = setTimeout(updatePreview, 300);\n      });\n      \n      function updatePreview() {\n        var previewFrame = document.getElementById('preview');\n        var preview =  previewFrame.contentDocument ||  previewFrame.contentWindow.document;\n        preview.open();\n        preview.write(editor.getValue());\n        preview.close();\n      }\n      setTimeout(updatePreview, 300);\n    </script>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/requirejs.html",
    "content": "<!doctype html>\n\n<head>\n  <title>CodeMirror: HTML completion demo</title>\n  <meta charset=\"utf-8\"/>\n  <link rel=stylesheet href=\"../doc/docs.css\">\n\n  <link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n  <link rel=\"stylesheet\" href=\"../addon/hint/show-hint.css\">\n  <script src=\"//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.14/require.min.js\"></script>\n  <style>\n    .CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}\n  </style>\n</head>\n\n<body>\n  <div id=nav>\n    <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n    <ul>\n      <li><a href=\"../index.html\">Home</a>\n      <li><a href=\"../doc/manual.html\">Manual</a>\n      <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n    </ul>\n    <ul>\n      <li><a class=active href=\"#\">HTML completion</a>\n    </ul>\n  </div>\n\n  <article>\n    <h2>RequireJS module loading demo</h2>\n\n    <p>This demo does the same thing as\n    the <a href=\"html5complete.html\">HTML5 completion demo</a>, but\n    loads its dependencies\n    with <a href=\"http://requirejs.org/\">Require.js</a>, rather than\n    explicitly. Press <strong>ctrl-space</strong> to activate\n    completion.</p>\n\n    <div id=\"code\"></div>\n\n    <button id=\"markdown\">Dynamically load Markdown mode</button>\n\n    <script>\n      require.config({\n        packages: [{\n          name: \"codemirror\",\n          location: \"../\",\n          main: \"lib/codemirror\"\n        }]\n      });\n      require([\"codemirror\", \"codemirror/mode/htmlmixed/htmlmixed\",\n               \"codemirror/addon/hint/show-hint\", \"codemirror/addon/hint/html-hint\",\n               \"codemirror/addon/mode/loadmode\"], function(CodeMirror) {\n        editor = CodeMirror(document.getElementById(\"code\"), {\n          mode: \"text/html\",\n          extraKeys: {\"Ctrl-Space\": \"autocomplete\"},\n          value: document.documentElement.innerHTML\n        });\n\n        CodeMirror.modeURL = \"codemirror/mode/%N/%N\";\n        document.getElementById(\"markdown\").addEventListener(\"click\", function() {\n          CodeMirror.requireMode(\"markdown\", function() {\n            editor.replaceRange(\"This is **Markdown**.\\n\\n\", {line: 0, ch: 0});\n            editor.setOption(\"mode\", \"markdown\");\n          });\n        });\n      });\n    </script>\n  </article>\n</body>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/resize.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Autoresize Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/css/css.js\"></script>\n<style>\n      .CodeMirror {\n        border: 1px solid #eee;\n        height: auto;\n      }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Autoresize</a>\n  </ul>\n</div>\n\n<article>\n<h2>Autoresize Demo</h2>\n<form><textarea id=\"code\" name=\"code\">\n.CodeMirror {\n  border: 1px solid #eee;\n  height: auto;\n}\n</textarea></form>\n\n<p>By setting an editor's <code>height</code> style\nto <code>auto</code> and giving\nthe <a href=\"../doc/manual.html#option_viewportMargin\"><code>viewportMargin</code></a>\na value of <code>Infinity</code>, CodeMirror can be made to\nautomatically resize to fit its content.</p>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        viewportMargin: Infinity\n      });\n    </script>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/rulers.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Ruler Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/display/rulers.js\"></script>\n<style>\n  .CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Ruler demo</a>\n  </ul>\n</div>\n\n<article>\n<h2>Ruler Demo</h2>\n\n<script>\n  var nums = \"0123456789\", space = \"          \";\n  var colors = [\"#fcc\", \"#f5f577\", \"#cfc\", \"#aff\", \"#ccf\", \"#fcf\"];\n  var rulers = [], value = \"\";\n  for (var i = 1; i <= 6; i++) {\n    rulers.push({color: colors[i], column: i * 10, lineStyle: \"dashed\"});\n    for (var j = 1; j < i; j++) value += space;\n    value += nums + \"\\n\";\n  }\n  var editor = CodeMirror(document.body.lastChild, {\n    rulers: rulers,\n    value: value + value + value,\n    lineNumbers: true\n});\n</script>\n\n<p>Demonstration of\nthe <a href=\"../doc/manual.html#addon_rulers\">rulers</a> addon, which\ndisplays vertical lines at given column offsets.</p>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/runmode-standalone.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Mode Runner Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../addon/runmode/runmode-standalone.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Mode Runner</a>\n  </ul>\n</div>\n\n<article>\n<h2>Mode Runner Demo</h2>\n\n\n    <textarea id=\"code\" style=\"width: 90%; height: 7em; border: 1px solid black; padding: .2em .4em;\">\n<foobar>\n  <blah>Enter your xml here and press the button below to display\n    it as highlighted by the CodeMirror XML mode</blah>\n  <tag2 foo=\"2\" bar=\"&amp;quot;bar&amp;quot;\"/>\n</foobar></textarea><br>\n    <button onclick=\"doHighlight();\">Highlight!</button>\n    <pre id=\"output\" class=\"cm-s-default\"></pre>\n\n    <script>\nfunction doHighlight() {\n  CodeMirror.runMode(document.getElementById(\"code\").value, \"application/xml\",\n                     document.getElementById(\"output\"));\n}\n</script>\n\n    <p>Running a CodeMirror mode outside of the editor.\n    The <code>CodeMirror.runMode</code> function, defined\n    in <code><a href=\"../addon/runmode/runmode.js\">addon/runmode/runmode.js</a></code> takes the following arguments:</p>\n\n    <dl>\n      <dt><code>text (string)</code></dt>\n      <dd>The document to run through the highlighter.</dd>\n      <dt><code>mode (<a href=\"../doc/manual.html#option_mode\">mode spec</a>)</code></dt>\n      <dd>The mode to use (must be loaded as normal).</dd>\n      <dt><code>output (function or DOM node)</code></dt>\n      <dd>If this is a function, it will be called for each token with\n      two arguments, the token's text and the token's style class (may\n      be <code>null</code> for unstyled tokens). If it is a DOM node,\n      the tokens will be converted to <code>span</code> elements as in\n      an editor, and inserted into the node\n      (through <code>innerHTML</code>).</dd>\n    </dl>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/runmode.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Mode Runner Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/runmode/runmode.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Mode Runner</a>\n  </ul>\n</div>\n\n<article>\n<h2>Mode Runner Demo</h2>\n\n\n    <textarea id=\"code\" style=\"width: 90%; height: 7em; border: 1px solid black; padding: .2em .4em;\">\n<foobar>\n  <blah>Enter your xml here and press the button below to display\n    it as highlighted by the CodeMirror XML mode</blah>\n  <tag2 foo=\"2\" bar=\"&amp;quot;bar&amp;quot;\"/>\n</foobar></textarea><br>\n    <button onclick=\"doHighlight();\">Highlight!</button>\n    <pre id=\"output\" class=\"cm-s-default\"></pre>\n\n    <script>\nfunction doHighlight() {\n  CodeMirror.runMode(document.getElementById(\"code\").value, \"application/xml\",\n                     document.getElementById(\"output\"));\n}\n</script>\n\n    <p>Running a CodeMirror mode outside of the editor.\n    The <code>CodeMirror.runMode</code> function, defined\n    in <code><a href=\"../addon/runmode/runmode.js\">addon/runmode/runmode.js</a></code> takes the following arguments:</p>\n\n    <dl>\n      <dt><code>text (string)</code></dt>\n      <dd>The document to run through the highlighter.</dd>\n      <dt><code>mode (<a href=\"../doc/manual.html#option_mode\">mode spec</a>)</code></dt>\n      <dd>The mode to use (must be loaded as normal).</dd>\n      <dt><code>output (function or DOM node)</code></dt>\n      <dd>If this is a function, it will be called for each token with\n      two arguments, the token's text and the token's style class (may\n      be <code>null</code> for unstyled tokens). If it is a DOM node,\n      the tokens will be converted to <code>span</code> elements as in\n      an editor, and inserted into the node\n      (through <code>innerHTML</code>).</dd>\n    </dl>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/search.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Search/Replace Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../addon/dialog/dialog.css\">\n<link rel=\"stylesheet\" href=\"../addon/search/matchesonscrollbar.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<script src=\"../addon/dialog/dialog.js\"></script>\n<script src=\"../addon/search/searchcursor.js\"></script>\n<script src=\"../addon/search/search.js\"></script>\n<script src=\"../addon/scroll/annotatescrollbar.js\"></script>\n<script src=\"../addon/search/matchesonscrollbar.js\"></script>\n<script src=\"../addon/search/jump-to-line.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n      dt {font-family: monospace; color: #666;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Search/Replace</a>\n  </ul>\n</div>\n\n<article>\n<h2>Search/Replace Demo</h2>\n<form><textarea id=\"code\" name=\"code\">\n<dl>\n  <dt id=\"option_indentWithTabs\"><code><strong>indentWithTabs</strong>: boolean</code></dt>\n  <dd>Whether, when indenting, the first N*<code>tabSize</code>\n  spaces should be replaced by N tabs. Default is false.</dd>\n\n  <dt id=\"option_electricChars\"><code><strong>electricChars</strong>: boolean</code></dt>\n  <dd>Configures whether the editor should re-indent the current\n  line when a character is typed that might change its proper\n  indentation (only works if the mode supports indentation).\n  Default is true.</dd>\n\n  <dt id=\"option_specialChars\"><code><strong>specialChars</strong>: RegExp</code></dt>\n  <dd>A regular expression used to determine which characters\n  should be replaced by a\n  special <a href=\"#option_specialCharPlaceholder\">placeholder</a>.\n  Mostly useful for non-printing special characters. The default\n  is <code>/[\\u0000-\\u0019\\u00ad\\u200b\\u2028\\u2029\\ufeff]/</code>.</dd>\n  <dt id=\"option_specialCharPlaceholder\"><code><strong>specialCharPlaceholder</strong>: function(char) → Element</code></dt>\n  <dd>A function that, given a special character identified by\n  the <a href=\"#option_specialChars\"><code>specialChars</code></a>\n  option, produces a DOM node that is used to represent the\n  character. By default, a red dot (<span style=\"color: red\">•</span>)\n  is shown, with a title tooltip to indicate the character code.</dd>\n\n  <dt id=\"option_rtlMoveVisually\"><code><strong>rtlMoveVisually</strong>: boolean</code></dt>\n  <dd>Determines whether horizontal cursor movement through\n  right-to-left (Arabic, Hebrew) text is visual (pressing the left\n  arrow moves the cursor left) or logical (pressing the left arrow\n  moves to the next lower index in the string, which is visually\n  right in right-to-left text). The default is <code>false</code>\n  on Windows, and <code>true</code> on other platforms.</dd>\n</dl>\n</textarea></form>\n\n    <script>\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  mode: \"text/html\",\n  lineNumbers: true,\n  extraKeys: {\"Alt-F\": \"findPersistent\"}\n});\n</script>\n\n    <p>Demonstration of primitive search/replace functionality. The\n    keybindings (which can be configured with custom keymaps) are:</p>\n    <dl>\n      <dt>Ctrl-F / Cmd-F</dt><dd>Start searching</dd>\n      <dt>Ctrl-G / Cmd-G</dt><dd>Find next</dd>\n      <dt>Shift-Ctrl-G / Shift-Cmd-G</dt><dd>Find previous</dd>\n      <dt>Shift-Ctrl-F / Cmd-Option-F</dt><dd>Replace</dd>\n      <dt>Shift-Ctrl-R / Shift-Cmd-Option-F</dt><dd>Replace all</dd>\n      <dt>Alt-F</dt><dd>Persistent search (dialog doesn't autoclose,\n      enter to find next, Shift-Enter to find previous)</dd>\n      <dt>Alt-G</dt><dd>Jump to line</dd>\n    </dl>\n    <p>Searching is enabled by\n    including <a href=\"../addon/search/search.js\">addon/search/search.js</a>\n    and <a href=\"../addon/search/searchcursor.js\">addon/search/searchcursor.js</a>.\n    Jump to line - including <a href=\"../addon/search/jump-to-line.js\">addon/search/jump-to-line.js</a>.</p>\n    <p>For good-looking input dialogs, you also want to include\n    <a href=\"../addon/dialog/dialog.js\">addon/dialog/dialog.js</a>\n    and <a href=\"../addon/dialog/dialog.css\">addon/dialog/dialog.css</a>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/simplemode.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Simple Mode Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/mode/simple.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<style>\n  .CodeMirror {border: 1px solid silver; margin-bottom: 1em; }\n  dt { text-indent: -2em; padding-left: 2em; margin-top: 1em; }\n  dd { margin-left: 1.5em; margin-bottom: 1em; }\n  dt {margin-top: 1em;}\n</style>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Simple Mode</a>\n  </ul>\n</div>\n\n<article>\n<h2>Simple Mode Demo</h2>\n\n<p>The <a href=\"../addon/mode/simple.js\"><code>mode/simple</code></a>\naddon allows CodeMirror modes to be specified using a relatively simple\ndeclarative format. This format is not as powerful as writing code\ndirectly against the <a href=\"../doc/manual.html#modeapi\">mode\ninterface</a>, but is a lot easier to get started with, and\nsufficiently expressive for many simple language modes.</p>\n\n<p>This interface is still in flux. It is unlikely to be scrapped or\noverhauled completely, so do start writing code against it, but\ndetails might change as it stabilizes, and you might have to tweak\nyour code when upgrading.</p>\n\n<p>Simple modes (loosely based on\nthe <a href=\"https://github.com/mozilla/skywriter/wiki/Common-JavaScript-Syntax-Highlighting-Specification\">Common\nJavaScript Syntax Highlighting Specification</a>, which never took\noff), are state machines, where each state has a number of rules that\nmatch tokens. A rule describes a type of token that may occur in the\ncurrent state, and possibly a transition to another state caused by\nthat token.</p>\n\n<p>The <code>CodeMirror.defineSimpleMode(name, states)</code> method\ntakes a mode name and an object that describes the mode's states. The\neditor below shows an example of such a mode (and is itself\nhighlighted by the mode shown in it).</p>\n\n<div id=\"code\"></div>\n\n<p>Each state is an array of rules. A rule may have the following properties:</p>\n\n<dl>\n  <dt><code><strong>regex</strong>: string | RegExp</code></dt>\n  <dd>The regular expression that matches the token. May be a string\n  or a regex object. When a regex, the <code>ignoreCase</code> flag\n  will be taken into account when matching the token. This regex\n  has to capture groups when the <code>token</code> property is\n  an array. If it captures groups, it must capture <em>all</em> of the string\n  (since JS provides no way to find out where a group matched).\n  Currently negative lookbehind assertion for regex is not supported, regardless of browser support.</dd>\n  <dt><code><strong>token</strong></code>: string | array&lt;string&gt; | null</dt>\n  <dd>An optional token style. Multiple styles can be specified by\n  separating them with dots or spaces. When this property holds an array of token styles,\n  the <code>regex</code> for this rule must capture a group for each array item.\n  </dd>\n  <dt><code><strong>sol</strong></code>: boolean</dt>\n  <dd>When true, this token will only match at the start of the line.\n  (The <code>^</code> regexp marker doesn't work as you'd expect in\n  this context because of limitations in JavaScript's RegExp\n  API.)</dd>\n  <dt><code><strong>next</strong>: string</code></dt>\n  <dd>When a <code>next</code> property is present, the mode will\n  transfer to the state named by the property when the token is\n  encountered.</dd>\n  <dt><code><strong>push</strong>: string</code></dt>\n  <dd>Like <code>next</code>, but instead replacing the current state\n  by the new state, the current state is kept on a stack, and can be\n  returned to with the <code>pop</code> directive.</dd>\n  <dt><code><strong>pop</strong>: bool</code></dt>\n  <dd>When true, and there is another state on the state stack, will\n  cause the mode to pop that state off the stack and transition to\n  it.</dd>\n  <dt><code><strong>mode</strong>: {spec, end, persistent}</code></dt>\n  <dd>Can be used to embed another mode inside a mode. When present,\n  must hold an object with a <code>spec</code> property that describes\n  the embedded mode, and an optional <code>end</code> end property\n  that specifies the regexp that will end the extent of the mode. When\n  a <code>persistent</code> property is set (and true), the nested\n  mode's state will be preserved between occurrences of the mode.</dd>\n  <dt><code><strong>indent</strong>: bool</code></dt>\n  <dd>When true, this token changes the indentation to be one unit\n  more than the current line's indentation.</dd>\n  <dt><code><strong>dedent</strong>: bool</code></dt>\n  <dd>When true, this token will pop one scope off the indentation\n  stack.</dd>\n  <dt><code><strong>dedentIfLineStart</strong>: bool</code></dt>\n  <dd>If a token has its <code>dedent</code> property set, it will, by\n  default, cause lines where it appears at the start to be dedented.\n  Set this property to false to prevent that behavior.</dd>\n</dl>\n\n<p>The <code>meta</code> property of the states object is special, and\nwill not be interpreted as a state. Instead, properties set on it will\nbe set on the mode, which is useful for properties\nlike <a href=\"../doc/manual.html#addon_comment\"><code>lineComment</code></a>,\nwhich sets the comment style for a mode. The simple mode addon also\nrecognizes a few such properties:</p>\n\n<dl>\n  <dt><code><strong>dontIndentStates</strong>: array&lt;string&gt;</code></dt>\n  <dd>An array of states in which the mode's auto-indentation should\n  not take effect. Usually used for multi-line comment and string\n  states.</dd>\n</dl>\n\n<script id=\"modecode\">/* Example definition of a simple mode that understands a subset of\n * JavaScript:\n */\n\nCodeMirror.defineSimpleMode(\"simplemode\", {\n  // The start state contains the rules that are initially used\n  start: [\n    // The regex matches the token, the token property contains the type\n    {regex: /\"(?:[^\\\\]|\\\\.)*?(?:\"|$)/, token: \"string\"},\n    // You can match multiple tokens at once. Note that the captured\n    // groups must span the whole string in this case\n    {regex: /(function)(\\s+)([a-z$][\\w$]*)/,\n     token: [\"keyword\", null, \"variable-2\"]},\n    // Rules are matched in the order in which they appear, so there is\n    // no ambiguity between this one and the one above\n    {regex: /(?:function|var|return|if|for|while|else|do|this)\\b/,\n     token: \"keyword\"},\n    {regex: /true|false|null|undefined/, token: \"atom\"},\n    {regex: /0x[a-f\\d]+|[-+]?(?:\\.\\d+|\\d+\\.?\\d*)(?:e[-+]?\\d+)?/i,\n     token: \"number\"},\n    {regex: /\\/\\/.*/, token: \"comment\"},\n    {regex: /\\/(?:[^\\\\]|\\\\.)*?\\//, token: \"variable-3\"},\n    // A next property will cause the mode to move to a different state\n    {regex: /\\/\\*/, token: \"comment\", next: \"comment\"},\n    {regex: /[-+\\/*=<>!]+/, token: \"operator\"},\n    // indent and dedent properties guide autoindentation\n    {regex: /[\\{\\[\\(]/, indent: true},\n    {regex: /[\\}\\]\\)]/, dedent: true},\n    {regex: /[a-z$][\\w$]*/, token: \"variable\"},\n    // You can embed other modes with the mode property. This rule\n    // causes all code between << and >> to be highlighted with the XML\n    // mode.\n    {regex: /<</, token: \"meta\", mode: {spec: \"xml\", end: />>/}}\n  ],\n  // The multi-line comment state.\n  comment: [\n    {regex: /.*?\\*\\//, token: \"comment\", next: \"start\"},\n    {regex: /.*/, token: \"comment\"}\n  ],\n  // The meta property contains global information about the mode. It\n  // can contain properties like lineComment, which are supported by\n  // all modes, and also directives like dontIndentStates, which are\n  // specific to simple modes.\n  meta: {\n    dontIndentStates: [\"comment\"],\n    lineComment: \"//\"\n  }\n});\n</script>\n\n<script>\nvar sc = document.getElementById(\"modecode\");\nvar code = document.getElementById(\"code\");\nvar editor = CodeMirror(code, {\n  value: (sc.textContent || sc.innerText || sc.innerHTML),\n  mode: \"simplemode\"\n});\n</script>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/simplescrollbars.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Simple Scrollbar Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../addon/scroll/simplescrollbars.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/markdown/markdown.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<script src=\"../addon/scroll/simplescrollbars.js\"></script>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Simple Scrollbar</a>\n  </ul>\n</div>\n\n<article>\n<h2>Simple Scrollbar Demo</h2>\n<form><textarea id=\"code\" name=\"code\"># Custom Scrollbars\n\nThis is a piece of text that creates scrollbars\n\nLorem ipsum dolor sit amet, turpis nec facilisis neque vestibulum adipiscing, magna nunc est luctus orci a,\naliquam duis ad volutpat nostra. Vestibulum ultricies suspendisse commodo volutpat pede sed. Bibendum odio\ndignissim, ad vitae mollis ac sed nibh quis, suspendisse diam, risus quas blandit phasellus luctus nec,\ninteger nunc vitae posuere scelerisque. Lobortis quam porta conubia nulla. Et nisl ac, imperdiet vitae ac.\nParturient sit. Et vestibulum euismod, rutrum nunc libero mauris purus convallis. Cum id adipiscing et eget\npretium rutrum, ultrices sapien magnis fringilla sit lorem, eu vitae scelerisque ipsum aliquet, magna sed\nfusce vel.\n\nLectus ultricies libero dolor convallis, sed etiam vel hendrerit egestas viverra, at urna mauris, eget\nvulputate dolor voluptatem, nulla eget sollicitudin. Sed tincidunt, elit sociis. Mattis mi tortor dui id\nsodales mi, maecenas nam fringilla risus turpis mauris praesent, imperdiet maecenas ultrices nonummy tellus\nquis est. Scelerisque nec pharetra quis varius fringilla. Varius vestibulum non dictum pharetra, tincidunt in\nvestibulum iaculis molestie, id condimentum blandit elit urna magna pulvinar, quam suspendisse pellentesque\ndonec. Vel amet ad ac. Nec aut viverra, morbi mi neque massa, turpis enim proin. Tellus eu, fermentum velit\nest convallis aliquam velit, rutrum in diam lacus, praesent tempor pellentesque dictum semper augue. Felis\nexplicabo massa amet lectus phasellus dolor. Ut lorem quis arcu neque felis ultricies, senectus vitae\ncurabitur sed pellentesque et, id sed risus in sed ac accumsan, blandit arcu quam duis nunc.\n\nSed leo sollicitudin odio vitae, purus sit egestas, justo eros inceptos auctor fermentum lectus. Ligula luctus\nturpis, quod massa vitae elementum orci, nullam fringilla elit tortor. Justo ante tempor amet quam posuere\nvolutpat. Facilisis pede erat ut hac ultrices ipsum, wisi duis sit metus. Dolor vitae est sed sed vitae. Sed\neu ligula, morbi vestibulum nunc nibh velit ut taciti, ligula elit semper sagittis in, auctor arcu vel eget.\nMauris at vitae nec suspendisse et, aenean proin blandit suscipit. Morbi quam, dolor ultricies. Viverra\ntempus. Suspendisse sit dapibus, ac fuga aenean, magna nisl nonummy augue posuere, dictum ut fuga velit\nparturient augue interdum, mattis sit tellus.\n\nVehicula commodo tempus curabitur eros, lacinia erat vulputate lorem vel fermentum donec, lectus sed conubia\nid pellentesque. Vel senectus donec pede aliquet dolor sit, nec vivamus justo placerat interdum maecenas,\nsodales euismod. Quis netus sapien amet, vestibulum quam nec amet lacinia, quis aliquet, tempor vivamus tellus\nenim, suscipit quis eleifend. Amet class phasellus orci pretium, risus in nulla. Neque sit ullamcorper,\nultricies platea id nec suspendisse ac. Et elementum. Dictum nam, ut dui fermentum egestas facilisis elit\naugue, adipiscing donec ipsum erat nam pellentesque convallis, vestibulum vestibulum risus id nulla ut mauris,\ncurabitur aute aptent. Ultrices orci wisi dui ipsum praesent, pharetra felis eu quis. Est fringilla etiam,\nmaxime sem dapibus et eget, mi enim dignissim nec pretium, augue vehicula, volutpat proin. Et occaecati\nlobortis viverra, cum in sed, vivamus tellus. Libero at malesuada est vivamus leo tortor.\n</textarea></form>\n\n<p>The <a href=\"../doc/manual.html#addon_simplescrollbars\"><code>simplescrollbars</code></a> addon defines two\nstyles of non-native scrollbars: <a href=\"javascript:editor.setOption('scrollbarStyle', 'simple')\"><code>\"simple\"</code></a> and <a href=\"javascript:editor.setOption('scrollbarStyle', 'overlay')\"><code>\"overlay\"</code></a> (click to try), which can be passed to\nthe <a href=\"../doc/manual.html#option_scrollbarStyle\"><code>scrollbarStyle</code></a> option. These implement\nthe scrollbar using DOM elements, allowing more control over\nits <a href=\"../addon/scroll/simplescrollbars.css\">appearance</a>.</p>\n\n  <script>\n    var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n      lineNumbers: true,\n      scrollbarStyle: \"simple\"\n    });\n  </script>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/spanaffectswrapping_shim.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Automatically derive odd wrapping behavior for your browser</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Automatically derive odd wrapping behavior for your browser</a>\n  </ul>\n</div>\n\n<article>\n<h2>Automatically derive odd wrapping behavior for your browser</h2>\n\n\n    <p>This is a hack to automatically derive\n    a <code>spanAffectsWrapping</code> regexp for a browser. See the\n    comments above that variable\n    in <a href=\"../lib/codemirror.js\"><code>lib/codemirror.js</code></a>\n    for some more details.</p>\n\n    <div style=\"white-space: pre-wrap; width: 50px;\" id=\"area\"></div>\n    <pre id=\"output\"></pre>\n\n    <script id=\"script\">\n      var a = document.getElementById(\"area\"), bad = Object.create(null);\n      var chars = \"a~`!@#$%^&*()-_=+}{[]\\\\|'\\\"/?.>,<:;\", l = chars.length;\n      for (var x = 0; x < l; ++x) for (var y = 0; y < l; ++y) {\n        var s1 = \"foooo\" + chars.charAt(x), s2 = chars.charAt(y) + \"br\";\n        a.appendChild(document.createTextNode(s1 + s2));\n        var h1 = a.offsetHeight;\n        a.innerHTML = \"\";\n        a.appendChild(document.createElement(\"span\")).appendChild(document.createTextNode(s1));\n        a.appendChild(document.createElement(\"span\")).appendChild(document.createTextNode(s2));\n        if (a.offsetHeight != h1)\n          bad[chars.charAt(x)] = (bad[chars.charAt(x)] || \"\") + chars.charAt(y);\n        a.innerHTML = \"\";\n      }\n\n      var re = \"\";\n      function toREElt(str) {\n        if (str.length > 1) {\n          var invert = false;\n          if (str.length > chars.length * .6) {\n            invert = true;\n            var newStr = \"\";\n            for (var i = 0; i < l; ++i) if (str.indexOf(chars.charAt(i)) == -1) newStr += chars.charAt(i);\n            str = newStr;\n          }\n          str = str.replace(/[\\-\\.\\]\\\"\\'\\\\\\/\\^a]/g, function(orig) { return orig == \"a\" ? \"\\\\w\" : \"\\\\\" + orig; });\n          return \"[\" + (invert ? \"^\" : \"\") + str + \"]\";\n        } else if (str == \"a\") {\n          return \"\\\\w\";\n        } else if (/[?$*()+{}[\\]\\.|/\\'\\\"]/.test(str)) {\n          return \"\\\\\" + str;\n        } else {\n          return str;\n        }\n      }\n\n      var newRE = \"\";\n      for (;;) {\n        var left = null;\n        for (var left in bad) break;\n        if (left == null) break;\n        var right = bad[left];\n        delete bad[left];\n        for (var other in bad) if (bad[other] == right) {\n          left += other;\n          delete bad[other];\n        }\n        newRE += (newRE ? \"|\" : \"\") + toREElt(left) + toREElt(right);\n      }\n\n      document.getElementById(\"output\").appendChild(document.createTextNode(\"Your regexp is: \" + (newRE || \"^$\")));\n    </script>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/sublime.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Sublime Text bindings demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../addon/fold/foldgutter.css\">\n<link rel=\"stylesheet\" href=\"../addon/dialog/dialog.css\">\n<link rel=\"stylesheet\" href=\"../theme/monokai.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/search/searchcursor.js\"></script>\n<script src=\"../addon/search/search.js\"></script>\n<script src=\"../addon/dialog/dialog.js\"></script>\n<script src=\"../addon/edit/matchbrackets.js\"></script>\n<script src=\"../addon/edit/closebrackets.js\"></script>\n<script src=\"../addon/comment/comment.js\"></script>\n<script src=\"../addon/wrap/hardwrap.js\"></script>\n<script src=\"../addon/fold/foldcode.js\"></script>\n<script src=\"../addon/fold/brace-fold.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"../keymap/sublime.js\"></script>\n<style>\n  .CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee; line-height: 1.3; height: 500px}\n  .CodeMirror-linenumbers { padding: 0 8px; }\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Sublime bindings</a>\n  </ul>\n</div>\n\n<article>\n<h2>Sublime Text bindings demo</h2>\n\n<p>The <code>sublime</code> keymap defines many Sublime Text-specific\nbindings for CodeMirror. See the code below for an overview.</p>\n\n<p>Enable the keymap by\nloading <a href=\"../keymap/sublime.js\"><code>keymap/sublime.js</code></a>\nand setting\nthe <a href=\"../doc/manual.html#option_keyMap\"><code>keyMap</code></a>\noption to <code>\"sublime\"</code>.</p>\n\n<p>(A lot of the search functionality is still missing.)\n\n<script>\n  var value = \"// The bindings defined specifically in the Sublime Text mode\\nvar bindings = {\\n\";\n  var map = CodeMirror.keyMap.sublime;\n  for (var key in map) {\n    var val = map[key];\n    if (key != \"fallthrough\" && val != \"...\" && (!/find/.test(val) || /findUnder/.test(val)))\n      value += \"  \\\"\" + key + \"\\\": \\\"\" + val + \"\\\",\\n\";\n  }\n  value += \"}\\n\\n// The implementation of joinLines\\n\";\n  value += CodeMirror.commands.joinLines.toString().replace(/^function\\s*\\(/, \"function joinLines(\").replace(/\\n  /g, \"\\n\") + \"\\n\";\n  var editor = CodeMirror(document.body.getElementsByTagName(\"article\")[0], {\n    value: value,\n    lineNumbers: true,\n    mode: \"javascript\",\n    keyMap: \"sublime\",\n    autoCloseBrackets: true,\n    matchBrackets: true,\n    showCursorWhenSelecting: true,\n    theme: \"monokai\",\n    tabSize: 2\n  });\n</script>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/tern.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Tern Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../addon/dialog/dialog.css\">\n<link rel=\"stylesheet\" href=\"../addon/hint/show-hint.css\">\n<link rel=\"stylesheet\" href=\"../addon/tern/tern.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"../addon/dialog/dialog.js\"></script>\n<script src=\"../addon/hint/show-hint.js\"></script>\n<script src=\"../addon/tern/tern.js\"></script>\n<script src=\"https://unpkg.com/acorn/dist/acorn.js\"></script>\n<script src=\"https://unpkg.com/acorn-loose/dist/acorn-loose.js\"></script>\n<script src=\"https://unpkg.com/acorn-walk/dist/walk.js\"></script>\n<script src=\"//ternjs.net/doc/demo/polyfill.js\"></script>\n<script src=\"https://unpkg.com/tern/lib/signal.js\"></script>\n<script src=\"https://unpkg.com/tern/lib/tern.js\"></script>\n<script src=\"https://unpkg.com/tern/lib/def.js\"></script>\n<script src=\"https://unpkg.com/tern/lib/comment.js\"></script>\n<script src=\"https://unpkg.com/tern/lib/infer.js\"></script>\n<script src=\"https://unpkg.com/tern/plugin/doc_comment.js\"></script>\n<style>\n      .CodeMirror {border: 1px solid #ddd;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Tern</a>\n  </ul>\n</div>\n\n<article>\n<h2>Tern Demo</h2>\n<form><textarea id=\"code\" name=\"code\">// Use ctrl-space to complete something\n// Put the cursor in or after an expression, press ctrl-o to\n// find its type\n\nvar foo = [\"array\", \"of\", \"strings\"];\nvar bar = foo.slice(0, 2).join(\"\").split(\"a\")[0];\n\n// Works for locally defined types too.\n\nfunction CTor() { this.size = 10; }\nCTor.prototype.hallo = \"hallo\";\n\nvar baz = new CTor;\nbaz.\n\n// You can press ctrl-q when the cursor is on a variable name to\n// rename it. Try it with CTor...\n\n// When the cursor is in an argument list, the arguments are\n// shown below the editor.\n\n[1].reduce(  );\n\n// And a little more advanced code...\n\n(function(exports) {\n  exports.randomElt = function(arr) {\n    return arr[Math.floor(arr.length * Math.random())];\n  };\n  exports.strList = \"foo\".split(\"\");\n  exports.intList = exports.strList.map(function(s) { return s.charCodeAt(0); });\n})(window.myMod = {});\n\nvar randomStr = myMod.randomElt(myMod.strList);\nvar randomInt = myMod.randomElt(myMod.intList);\n</textarea></form>\n\n<p>Demonstrates integration of <a href=\"http://ternjs.net/\">Tern</a>\nand CodeMirror. The following keys are bound:</p>\n\n<dl>\n  <dt>Ctrl-Space</dt><dd>Autocomplete</dd>\n  <dt>Ctrl-O</dt><dd>Find docs for the expression at the cursor</dd>\n  <dt>Ctrl-I</dt><dd>Find type at cursor</dd>\n  <dt>Alt-.</dt><dd>Jump to definition (Alt-, to jump back)</dd>\n  <dt>Ctrl-Q</dt><dd>Rename variable</dd>\n  <dt>Ctrl-.</dt><dd>Select all occurrences of a variable</dd>\n</dl>\n\n<p>Documentation is sparse for now. See the top of\nthe <a href=\"../addon/tern/tern.js\">script</a> for a rough API\noverview.</p>\n\n<script>\n  function getURL(url, c) {\n    var xhr = new XMLHttpRequest();\n    xhr.open(\"get\", url, true);\n    xhr.send();\n    xhr.onreadystatechange = function() {\n      if (xhr.readyState != 4) return;\n      if (xhr.status < 400) return c(null, xhr.responseText);\n      var e = new Error(xhr.responseText || \"No response\");\n      e.status = xhr.status;\n      c(e);\n    };\n  }\n\n  var server;\n  getURL(\"https://unpkg.com/tern/defs/ecmascript.json\", function(err, code) {\n    if (err) throw new Error(\"Request for ecmascript.json: \" + err);\n    server = new CodeMirror.TernServer({defs: [JSON.parse(code)]});\n    editor.setOption(\"extraKeys\", {\n      \"Ctrl-Space\": function(cm) { server.complete(cm); },\n      \"Ctrl-I\": function(cm) { server.showType(cm); },\n      \"Ctrl-O\": function(cm) { server.showDocs(cm); },\n      \"Alt-.\": function(cm) { server.jumpToDef(cm); },\n      \"Alt-,\": function(cm) { server.jumpBack(cm); },\n      \"Ctrl-Q\": function(cm) { server.rename(cm); },\n      \"Ctrl-.\": function(cm) { server.selectName(cm); }\n    })\n    editor.on(\"cursorActivity\", function(cm) { server.updateArgHints(cm); });\n  });\n\n  var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    lineNumbers: true,\n    mode: \"javascript\"\n  });\n</script>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/theme.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Theme Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../theme/3024-day.css\">\n<link rel=\"stylesheet\" href=\"../theme/3024-night.css\">\n<link rel=\"stylesheet\" href=\"../theme/abbott.css\">\n<link rel=\"stylesheet\" href=\"../theme/abcdef.css\">\n<link rel=\"stylesheet\" href=\"../theme/ambiance.css\">\n<link rel=\"stylesheet\" href=\"../theme/ayu-dark.css\">\n<link rel=\"stylesheet\" href=\"../theme/ayu-mirage.css\">\n<link rel=\"stylesheet\" href=\"../theme/base16-dark.css\">\n<link rel=\"stylesheet\" href=\"../theme/bespin.css\">\n<link rel=\"stylesheet\" href=\"../theme/base16-light.css\">\n<link rel=\"stylesheet\" href=\"../theme/blackboard.css\">\n<link rel=\"stylesheet\" href=\"../theme/cobalt.css\">\n<link rel=\"stylesheet\" href=\"../theme/colorforth.css\">\n<link rel=\"stylesheet\" href=\"../theme/dracula.css\">\n<link rel=\"stylesheet\" href=\"../theme/duotone-dark.css\">\n<link rel=\"stylesheet\" href=\"../theme/duotone-light.css\">\n<link rel=\"stylesheet\" href=\"../theme/eclipse.css\">\n<link rel=\"stylesheet\" href=\"../theme/elegant.css\">\n<link rel=\"stylesheet\" href=\"../theme/erlang-dark.css\">\n<link rel=\"stylesheet\" href=\"../theme/gruvbox-dark.css\">\n<link rel=\"stylesheet\" href=\"../theme/hopscotch.css\">\n<link rel=\"stylesheet\" href=\"../theme/icecoder.css\">\n<link rel=\"stylesheet\" href=\"../theme/isotope.css\">\n<link rel=\"stylesheet\" href=\"../theme/juejin.css\">\n<link rel=\"stylesheet\" href=\"../theme/lesser-dark.css\">\n<link rel=\"stylesheet\" href=\"../theme/liquibyte.css\">\n<link rel=\"stylesheet\" href=\"../theme/lucario.css\">\n<link rel=\"stylesheet\" href=\"../theme/material.css\">\n<link rel=\"stylesheet\" href=\"../theme/material-darker.css\">\n<link rel=\"stylesheet\" href=\"../theme/material-palenight.css\">\n<link rel=\"stylesheet\" href=\"../theme/material-ocean.css\">\n<link rel=\"stylesheet\" href=\"../theme/mbo.css\">\n<link rel=\"stylesheet\" href=\"../theme/mdn-like.css\">\n<link rel=\"stylesheet\" href=\"../theme/midnight.css\">\n<link rel=\"stylesheet\" href=\"../theme/monokai.css\">\n<link rel=\"stylesheet\" href=\"../theme/moxer.css\">\n<link rel=\"stylesheet\" href=\"../theme/neat.css\">\n<link rel=\"stylesheet\" href=\"../theme/neo.css\">\n<link rel=\"stylesheet\" href=\"../theme/night.css\">\n<link rel=\"stylesheet\" href=\"../theme/nord.css\">\n<link rel=\"stylesheet\" href=\"../theme/oceanic-next.css\">\n<link rel=\"stylesheet\" href=\"../theme/panda-syntax.css\">\n<link rel=\"stylesheet\" href=\"../theme/paraiso-dark.css\">\n<link rel=\"stylesheet\" href=\"../theme/paraiso-light.css\">\n<link rel=\"stylesheet\" href=\"../theme/pastel-on-dark.css\">\n<link rel=\"stylesheet\" href=\"../theme/railscasts.css\">\n<link rel=\"stylesheet\" href=\"../theme/rubyblue.css\">\n<link rel=\"stylesheet\" href=\"../theme/seti.css\">\n<link rel=\"stylesheet\" href=\"../theme/shadowfox.css\">\n<link rel=\"stylesheet\" href=\"../theme/solarized.css\">\n<link rel=\"stylesheet\" href=\"../theme/the-matrix.css\">\n<link rel=\"stylesheet\" href=\"../theme/tomorrow-night-bright.css\">\n<link rel=\"stylesheet\" href=\"../theme/tomorrow-night-eighties.css\">\n<link rel=\"stylesheet\" href=\"../theme/ttcn.css\">\n<link rel=\"stylesheet\" href=\"../theme/twilight.css\">\n<link rel=\"stylesheet\" href=\"../theme/vibrant-ink.css\">\n<link rel=\"stylesheet\" href=\"../theme/xq-dark.css\">\n<link rel=\"stylesheet\" href=\"../theme/xq-light.css\">\n<link rel=\"stylesheet\" href=\"../theme/yeti.css\">\n<link rel=\"stylesheet\" href=\"../theme/idea.css\">\n<link rel=\"stylesheet\" href=\"../theme/darcula.css\">\n<link rel=\"stylesheet\" href=\"../theme/yonce.css\">\n<link rel=\"stylesheet\" href=\"../theme/zenburn.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"../addon/selection/active-line.js\"></script>\n<script src=\"../addon/edit/matchbrackets.js\"></script>\n<style>\n      .CodeMirror {border: 1px solid black; font-size:13px}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Theme</a>\n  </ul>\n</div>\n\n<article>\n<h2>Theme Demo</h2>\n<form><textarea id=\"code\" name=\"code\">\nfunction findSequence(goal) {\n  function find(start, history) {\n    if (start == goal)\n      return history;\n    else if (start > goal)\n      return null;\n    else\n      return find(start + 5, \"(\" + history + \" + 5)\") ||\n             find(start * 3, \"(\" + history + \" * 3)\");\n  }\n  return find(1, \"1\");\n}</textarea></form>\n\n<p>Select a theme: <select onchange=\"selectTheme()\" id=select>\n    <option selected>default</option>\n    <option>3024-day</option>\n    <option>3024-night</option>\n    <option>abbott</option>\n    <option>abcdef</option>\n    <option>ambiance</option>\n    <option>ayu-dark</option>\n    <option>ayu-mirage</option>\n    <option>base16-dark</option>\n    <option>base16-light</option>\n    <option>bespin</option>\n    <option>blackboard</option>\n    <option>cobalt</option>\n    <option>colorforth</option>\n    <option>darcula</option>\n    <option>dracula</option>\n    <option>duotone-dark</option>\n    <option>duotone-light</option>\n    <option>eclipse</option>\n    <option>elegant</option>\n    <option>erlang-dark</option>\n    <option>gruvbox-dark</option>\n    <option>hopscotch</option>\n    <option>icecoder</option>\n    <option>idea</option>\n    <option>isotope</option>\n    <option>juejin</option>\n    <option>lesser-dark</option>\n    <option>liquibyte</option>\n    <option>lucario</option>\n    <option>material</option>\n    <option>material-darker</option>\n    <option>material-palenight</option>\n    <option>material-ocean</option>\n    <option>mbo</option>\n    <option>mdn-like</option>\n    <option>midnight</option>\n    <option>monokai</option>\n    <option>moxer</option>\n    <option>neat</option>\n    <option>neo</option>\n    <option>night</option>\n    <option>nord</option>\n    <option>oceanic-next</option>\n    <option>panda-syntax</option>\n    <option>paraiso-dark</option>\n    <option>paraiso-light</option>\n    <option>pastel-on-dark</option>\n    <option>railscasts</option>\n    <option>rubyblue</option>\n    <option>seti</option>\n    <option>shadowfox</option>\n    <option>solarized dark</option>\n    <option>solarized light</option>\n    <option>the-matrix</option>\n    <option>tomorrow-night-bright</option>\n    <option>tomorrow-night-eighties</option>\n    <option>ttcn</option>\n    <option>twilight</option>\n    <option>vibrant-ink</option>\n    <option>xq-dark</option>\n    <option>xq-light</option>\n    <option>yeti</option>\n    <option>yonce</option>\n    <option>zenburn</option>\n</select>\n</p>\n\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    lineNumbers: true,\n    styleActiveLine: true,\n    matchBrackets: true\n  });\n  var input = document.getElementById(\"select\");\n  function selectTheme() {\n    var theme = input.options[input.selectedIndex].textContent;\n    editor.setOption(\"theme\", theme);\n    location.hash = \"#\" + theme;\n  }\n  var choice = (location.hash && location.hash.slice(1)) ||\n               (document.location.search &&\n                decodeURIComponent(document.location.search.slice(1)));\n  if (choice) {\n    input.value = choice;\n    editor.setOption(\"theme\", choice);\n  }\n  CodeMirror.on(window, \"hashchange\", function() {\n    var theme = location.hash.slice(1);\n    if (theme) { input.value = theme; selectTheme(); }\n  });\n</script>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/trailingspace.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Trailing Whitespace Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/edit/trailingspace.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n      .cm-trailingspace {\n        background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QUXCToH00Y1UgAAACFJREFUCNdjPMDBUc/AwNDAAAFMTAwMDA0OP34wQgX/AQBYgwYEx4f9lQAAAABJRU5ErkJggg==);\n        background-position: bottom left;\n        background-repeat: repeat-x;\n      }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Trailing Whitespace</a>\n  </ul>\n</div>\n\n<article>\n<h2>Trailing Whitespace Demo</h2>\n<form><textarea id=\"code\" name=\"code\">This text  \n has some\t \ntrailing whitespace!</textarea></form>\n\n    <script>\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  lineNumbers: true,\n  showTrailingSpace: true\n});\n</script>\n\n<p>Uses\nthe <a href=\"../doc/manual.html#addon_trailingspace\">trailingspace</a>\naddon to highlight trailing whitespace.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/variableheight.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Variable Height Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/markdown/markdown.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<style>\n      .CodeMirror {border: 1px solid silver; border-width: 1px 2px; }\n      .cm-header { font-family: arial; }\n      .cm-header-1 { font-size: 150%; }\n      .cm-header-2 { font-size: 130%; }\n      .cm-header-3 { font-size: 120%; }\n      .cm-header-4 { font-size: 110%; }\n      .cm-header-5 { font-size: 100%; }\n      .cm-header-6 { font-size: 90%; }\n      .cm-strong { font-size: 140%; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Variable Height</a>\n  </ul>\n</div>\n\n<article>\n<h2>Variable Height Demo</h2>\n<form><textarea id=\"code\" name=\"code\"># A First Level Header\n\n**Bold** text in a normal-size paragraph.\n\nAnd a very long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long, wrapped line with a piece of **big** text inside of it.\n\n## A Second Level Header\n\nNow is the time for all good men to come to\nthe aid of their country. This is just a\nregular paragraph.\n\nThe quick brown fox jumped over the lazy\ndog's back.\n\n### Header 3\n\n> This is a blockquote.\n> \n> This is the second paragraph in the blockquote.\n>\n> ## This is an H2 in a blockquote       \n</textarea></form>\n    <script id=\"script\">\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        lineWrapping: true,\n        mode: \"markdown\"\n      });\n    </script>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/vim.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Vim bindings demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../addon/dialog/dialog.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/dialog/dialog.js\"></script>\n<script src=\"../addon/search/searchcursor.js\"></script>\n<script src=\"../mode/clike/clike.js\"></script>\n<script src=\"../addon/edit/matchbrackets.js\"></script>\n<script src=\"../keymap/vim.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Vim bindings</a>\n  </ul>\n</div>\n\n<article>\n<h2>Vim bindings demo</h2>\n\n<p><strong style=\"color: #c33; text-decoration: none\">Note:</strong>\nThe CodeMirror vim bindings are maintained in\nthe <a href=\"https://github.com/replit/codemirror-vim\">codemirror-vim</a>\nrepository, not this project. The file is still included in the\ndistribution for backwards compatibility.</p>\n\n<form><textarea id=\"code\" name=\"code\">\n#include \"syscalls.h\"\n/* getchar:  simple buffered version */\nint getchar(void)\n{\n  static char buf[BUFSIZ];\n  static char *bufp = buf;\n  static int n = 0;\n  if (n == 0) {  /* buffer is empty */\n    n = read(0, buf, sizeof buf);\n    bufp = buf;\n  }\n  return (--n >= 0) ? (unsigned char) *bufp++ : EOF;\n}\n</textarea></form>\n<div style=\"font-size: 13px; width: 300px; height: 30px;\">Key buffer: <span id=\"command-display\"></span></div>\n<div style=\"font-size: 13px; width: 300px; height: 30px;\">Vim mode: <span id=\"vim-mode\"></span></div>\n\n<p>The vim keybindings are enabled by including <code><a\nhref=\"../keymap/vim.js\">keymap/vim.js</a></code> and setting the\n<code>keyMap</code> option to <code>vim</code>.</p>\n\n<p><strong>Features</strong></p>\n\n<ul>\n  <li>All common motions and operators, including text objects</li>\n  <li>Operator motion orthogonality</li>\n  <li>Visual mode - characterwise, linewise, blockwise</li>\n  <li>Full macro support (q, @)</li>\n  <li>Incremental highlighted search (/, ?, #, *, g#, g*)</li>\n  <li>Search/replace with confirm (:substitute, :%s)</li>\n  <li>Search history</li>\n  <li>Jump lists (Ctrl-o, Ctrl-i)</li>\n  <li>Key/command mapping with API (:map, :nmap, :vmap)</li>\n  <li>Sort (:sort)</li>\n  <li>Marks (`, ')</li>\n  <li>:global</li>\n  <li>Insert mode behaves identical to base CodeMirror</li>\n  <li>Cross-buffer yank/paste</li>\n</ul>\n\n<p>For the full list of key mappings and Ex commands, refer to the\n<code>defaultKeymap</code> and <code>defaultExCommandMap</code> at the\ntop of <code><a href=\"../keymap/vim.js\">keymap/vim.js</a></code>.\n\n<p>Note that while the vim mode tries to emulate the most useful\nfeatures of vim as faithfully as possible, it does not strive to\nbecome a complete vim implementation</p>\n\n    <script>\n      CodeMirror.commands.save = function(){ alert(\"Saving\"); };\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        mode: \"text/x-csrc\",\n        keyMap: \"vim\",\n        matchBrackets: true,\n        showCursorWhenSelecting: true\n      });\n      var commandDisplay = document.getElementById('command-display');\n      var keys = '';\n      CodeMirror.on(editor, 'vim-keypress', function(key) {\n        keys = keys + key;\n        commandDisplay.innerText = keys;\n      });\n      CodeMirror.on(editor, 'vim-command-done', function(e) {\n        keys = '';\n        commandDisplay.innerHTML = keys;\n      });\n      var vimMode = document.getElementById('vim-mode');\n      CodeMirror.on(editor, 'vim-mode-change', function(e) {\n        vimMode.innerText = JSON.stringify(e);\n      });\n    </script>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/visibletabs.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Visible tabs demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/clike/clike.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee;}\n      .cm-tab {\n         background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAAXNSR0IArs4c6QAAAGFJREFUSMft1LsRQFAQheHPowAKoACx3IgEKtaEHujDjORSgWTH/ZOdnZOcM/sgk/kFFWY0qV8foQwS4MKBCS3qR6ixBJvElOobYAtivseIE120FaowJPN75GMu8j/LfMwNjh4HUpwg4LUAAAAASUVORK5CYII=);\n         background-position: right;\n         background-repeat: no-repeat;\n      }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Visible tabs</a>\n  </ul>\n</div>\n\n<article>\n<h2>Visible tabs demo</h2>\n<form><textarea id=\"code\" name=\"code\">\n#include \"syscalls.h\"\n/* getchar:  simple buffered version */\nint getchar(void)\n{\n\tstatic char buf[BUFSIZ];\n\tstatic char *bufp = buf;\n\tstatic int n = 0;\n\tif (n == 0) {  /* buffer is empty */\n\t\tn = read(0, buf, sizeof buf);\n\t\tbufp = buf;\n\t}\n\treturn (--n >= 0) ? (unsigned char) *bufp++ : EOF;\n}\n</textarea></form>\n\n<p>Tabs inside the editor are spans with the\nclass <code>cm-tab</code>, and can be styled.</p>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        tabSize: 4,\n        indentUnit: 4,\n        indentWithTabs: true,\n        mode: \"text/x-csrc\"\n      });\n    </script>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/widget.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Inline Widget Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jshint/2.9.5/jshint.min.js\"></script>\n<style>\n      .CodeMirror {border: 1px solid black;}\n      .lint-error {font-family: arial; font-size: 70%; background: #ffa; color: #a00; padding: 2px 5px 3px; }\n      .lint-error-icon {color: white; background-color: red; font-weight: bold; border-radius: 50%; padding: 0 3px; margin-right: 7px;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Inline Widget</a>\n  </ul>\n</div>\n\n<article>\n<h2>Inline Widget Demo</h2>\n\n\n    <div id=code></div>\n    <script id=\"script\">var widgets = []\nfunction updateHints() {\n  editor.operation(function(){\n    for (var i = 0; i < widgets.length; ++i)\n      editor.removeLineWidget(widgets[i]);\n    widgets.length = 0;\n\n    JSHINT(editor.getValue());\n    for (var i = 0; i < JSHINT.errors.length; ++i) {\n      var err = JSHINT.errors[i];\n      if (!err) continue;\n      var msg = document.createElement(\"div\");\n      var icon = msg.appendChild(document.createElement(\"span\"));\n      icon.innerHTML = \"!!\";\n      icon.className = \"lint-error-icon\";\n      msg.appendChild(document.createTextNode(err.reason));\n      msg.className = \"lint-error\";\n      widgets.push(editor.addLineWidget(err.line - 1, msg, {coverGutter: false, noHScroll: true}));\n    }\n  });\n  var info = editor.getScrollInfo();\n  var after = editor.charCoords({line: editor.getCursor().line + 1, ch: 0}, \"local\").top;\n  if (info.top + info.clientHeight < after)\n    editor.scrollTo(null, after - info.clientHeight + 3);\n}\n\nwindow.onload = function() {\n  var sc = document.getElementById(\"script\");\n  var content = sc.textContent || sc.innerText || sc.innerHTML;\n\n  window.editor = CodeMirror(document.getElementById(\"code\"), {\n    lineNumbers: true,\n    mode: \"javascript\",\n    value: content\n  });\n\n  var waiting;\n  editor.on(\"change\", function() {\n    clearTimeout(waiting);\n    waiting = setTimeout(updateHints, 500);\n  });\n\n  setTimeout(updateHints, 100);\n};\n\n\"long line to create a horizontal scrollbar, in order to test whether the (non-inline) widgets stay in place when scrolling to the right\";\n</script>\n<p>This demo runs <a href=\"http://jshint.com\">JSHint</a> over the code\nin the editor (which is the script used on this page), and\ninserts <a href=\"../doc/manual.html#addLineWidget\">line widgets</a> to\ndisplay the warnings that JSHint comes up with.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/demo/xmlcomplete.html",
    "content": "﻿<!doctype html>\n\n<title>CodeMirror: XML Autocomplete Demo</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../addon/hint/show-hint.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<script src=\"../addon/hint/show-hint.js\"></script>\n<script src=\"../addon/hint/xml-hint.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<style>\n      .CodeMirror { border: 1px solid #eee; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">XML Autocomplete</a>\n  </ul>\n</div>\n\n<article>\n<h2>XML Autocomplete Demo</h2>\n<form><textarea id=\"code\" name=\"code\"><!-- write some xml below -->\n</textarea></form>\n\n    <p>Press <strong>ctrl-space</strong>, or type a '&lt;' character to\n    activate autocompletion. This demo defines a simple schema that\n    guides completion. The schema can be customized—see\n    the <a href=\"../doc/manual.html#addon_xml-hint\">manual</a>.</p>\n\n    <p>Development of the <code>xml-hint</code> addon was kindly\n    sponsored\n    by <a href=\"http://www.xperiment.mobi\">www.xperiment.mobi</a>.</p>\n\n    <script>\n      var dummy = {\n        attrs: {\n          color: [\"red\", \"green\", \"blue\", \"purple\", \"white\", \"black\", \"yellow\"],\n          size: [\"large\", \"medium\", \"small\"],\n          description: null\n        },\n        children: []\n      };\n\n      var tags = {\n        \"!top\": [\"top\"],\n        \"!attrs\": {\n          id: null,\n          class: [\"A\", \"B\", \"C\"]\n        },\n        top: {\n          attrs: {\n            lang: [\"en\", \"de\", \"fr\", \"nl\"],\n            freeform: null\n          },\n          children: [\"animal\", \"plant\"]\n        },\n        animal: {\n          attrs: {\n            name: null,\n            isduck: [\"yes\", \"no\"]\n          },\n          children: [\"wings\", \"feet\", \"body\", \"head\", \"tail\"]\n        },\n        plant: {\n          attrs: {name: null},\n          children: [\"leaves\", \"stem\", \"flowers\"]\n        },\n        wings: dummy, feet: dummy, body: dummy, head: dummy, tail: dummy,\n        leaves: dummy, stem: dummy, flowers: dummy\n      };\n\n      function completeAfter(cm, pred) {\n        var cur = cm.getCursor();\n        if (!pred || pred()) setTimeout(function() {\n          if (!cm.state.completionActive)\n            cm.showHint({completeSingle: false});\n        }, 100);\n        return CodeMirror.Pass;\n      }\n\n      function completeIfAfterLt(cm) {\n        return completeAfter(cm, function() {\n          var cur = cm.getCursor();\n          return cm.getRange(CodeMirror.Pos(cur.line, cur.ch - 1), cur) == \"<\";\n        });\n      }\n\n      function completeIfInTag(cm) {\n        return completeAfter(cm, function() {\n          var tok = cm.getTokenAt(cm.getCursor());\n          if (tok.type == \"string\" && (!/['\"]/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1)) return false;\n          var inner = CodeMirror.innerMode(cm.getMode(), tok.state).state;\n          return inner.tagName;\n        });\n      }\n\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: \"xml\",\n        lineNumbers: true,\n        extraKeys: {\n          \"'<'\": completeAfter,\n          \"'/'\": completeIfAfterLt,\n          \"' '\": completeIfInTag,\n          \"'='\": completeIfInTag,\n          \"Ctrl-Space\": \"autocomplete\"\n        },\n        hintOptions: {schemaInfo: tags}\n      });\n    </script>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/doc/activebookmark.js",
    "content": "// Kludge in HTML5 tag recognition in IE8\ndocument.createElement(\"section\");\ndocument.createElement(\"article\");\n\n(function() {\n  if (!window.addEventListener) return;\n  var pending = false, prevVal = null;\n\n  function updateSoon() {\n    if (!pending) {\n      pending = true;\n      setTimeout(update, 250);\n    }\n  }\n\n  function update() {\n    pending = false;\n    var marks = document.getElementById(\"nav\").getElementsByTagName(\"a\"), found;\n    for (var i = 0; i < marks.length; ++i) {\n      var mark = marks[i], m;\n      if (mark.getAttribute(\"data-default\")) {\n        if (found == null) found = i;\n      } else if (m = mark.href.match(/#(.*)/)) {\n        var ref = document.getElementById(m[1]);\n        if (ref && ref.getBoundingClientRect().top < 50)\n          found = i;\n      }\n    }\n    if (found != null && found != prevVal) {\n      prevVal = found;\n      var lis = document.getElementById(\"nav\").getElementsByTagName(\"li\");\n      for (var i = 0; i < lis.length; ++i) lis[i].className = \"\";\n      for (var i = 0; i < marks.length; ++i) {\n        if (found == i) {\n          marks[i].className = \"active\";\n          for (var n = marks[i]; n; n = n.parentNode)\n            if (n.nodeName == \"LI\") n.className = \"active\";\n        } else {\n          marks[i].className = \"\";\n        }\n      }\n    }\n  }\n\n  window.addEventListener(\"scroll\", updateSoon);\n  window.addEventListener(\"load\", updateSoon);\n  window.addEventListener(\"hashchange\", function() {\n    setTimeout(function() {\n      var hash = document.location.hash, found = null, m;\n      var marks = document.getElementById(\"nav\").getElementsByTagName(\"a\");\n      for (var i = 0; i < marks.length; i++)\n        if ((m = marks[i].href.match(/(#.*)/)) && m[1] == hash) { found = i; break; }\n      if (found != null) for (var i = 0; i < marks.length; i++)\n        marks[i].className = i == found ? \"active\" : \"\";\n    }, 300);\n  });\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/doc/docs.css",
    "content": "@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 400;\n  src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url(source_sans.woff) format('woff');\n}\n\nbody, html { margin: 0; padding: 0; height: 100%; }\nsection, article { display: block; padding: 0; }\n\nbody {\n  background: #f8f8f8;\n  font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif;\n  line-height: 1.5;\n}\n\np { margin-top: 0; }\n\nh2, h3, h1 {\n  font-weight: normal;\n  margin-bottom: .7em;\n}\nh1 { font-size: 140%; }\nh2 { font-size: 120%; }\nh3 { font-size: 110%; }\narticle > h2:first-child, section:first-child > h2 { margin-top: 0; }\n\n#nav h1 {\n  margin-right: 12px;\n  margin-top: 0;\n  margin-bottom: 2px;\n  color: #d30707;\n  letter-spacing: .5px;\n}\n\na, a:visited, a:link, .quasilink {\n  color: #A21313;\n}\n\nem {\n  padding-right: 2px;\n}\n\n.quasilink {\n  cursor: pointer;\n}\n\narticle {\n  max-width: 700px;\n  margin: 0 0 0 160px;\n  border-left: 2px solid #E30808;\n  border-right: 1px solid #ddd;\n  padding: 30px 50px 100px 50px;\n  background: white;\n  z-index: 2;\n  position: relative;\n  min-height: 100%;\n  box-sizing: border-box;\n  -moz-box-sizing: border-box;\n}\n\n#nav {\n  position: fixed;\n  padding-top: 30px;\n  max-height: 100%;\n  box-sizing: -moz-border-box;\n  box-sizing: border-box;\n  overflow-y: auto;\n  left: 0; right: none;\n  width: 160px;\n  text-align: right;\n  z-index: 1;\n}\n\n@media screen and (min-width: 1000px) {\n  article {\n    margin: 0 auto;\n  }\n  #nav {\n    right: 50%;\n    width: auto;\n    border-right: 349px solid transparent;\n  }\n}\n\n#nav ul {\n  display: block;\n  margin: 0; padding: 0;\n  margin-bottom: 32px;\n}\n\n#nav a {\n  text-decoration: none;\n}\n\n#nav li {\n  display: block;\n  margin-bottom: 4px;\n}\n\n#nav li ul {\n  font-size: 80%;\n  margin-bottom: 0;\n  display: none;\n}\n\n#nav li.active ul {\n  display: block;\n}\n\n#nav li li a {\n  padding-right: 20px;\n  display: inline-block;\n}\n\n#nav ul a {\n  color: black;\n  padding: 0 7px 1px 11px;\n}\n\n#nav ul a.active, #nav ul a:hover {\n  border-bottom: 1px solid #E30808;\n  margin-bottom: -1px;\n  color: #E30808;\n}\n\n#logo {\n  border: 0;\n  margin-right: 12px;\n  margin-bottom: 25px;\n}\n\nsection {\n  border-top: 1px solid #E30808;\n  margin: 1.5em 0;\n}\n\nsection.first {\n  border: none;\n  margin-top: 0;\n}\n\n#demo {\n  position: relative;\n}\n\n#demolist {\n  position: absolute;\n  right: 5px;\n  top: 5px;\n  z-index: 25;\n}\n\n.yinyang {\n  position: absolute;\n  top: -10px;\n  left: 0; right: 0;\n  margin: auto;\n  display: block;\n  height: 120px;\n}\n\n.actions {\n  margin: 1em 0 0;\n  min-height: 100px;\n  position: relative;\n}\n\n@media screen and (max-width: 800px) {\n  .actions {\n    padding-top: 120px;\n  }\n  .actionsleft, .actionsright {\n    float: none;\n    text-align: left;\n    margin-bottom: 1em;\n  }\n}\n\nth {\n  text-decoration: underline;\n  font-weight: normal;\n  text-align: left;\n}\n\n#features ul {\n  list-style: none;\n  margin: 0 0 1em;\n  padding: 0 0 0 1.2em;\n}\n\n#features li:before {\n  content: \"-\";\n  width: 1em;\n  display: inline-block;\n  padding: 0;\n  margin: 0;\n  margin-left: -1em;\n}\n\n.rel {\n  margin-bottom: 0;\n}\n.rel-note {\n  margin-top: 0;\n  color: #555;\n}\n\npre {\n  padding-left: 15px;\n  border-left: 2px solid #ddd;\n}\n\ncode {\n  padding: 0 2px;\n}\n\nstrong {\n  text-decoration: underline;\n  font-weight: normal;\n}\n\n.field {\n  border: 1px solid #A21313;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/doc/internals.html",
    "content": "﻿<!doctype html>\n\n<title>CodeMirror: Internals</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"docs.css\">\n<style>dl dl {margin: 0;} .update {color: #d40 !important}</style>\n<script src=\"activebookmark.js\"></script>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"#top\">Introduction</a></li>\n    <li><a href=\"#approach\">General Approach</a></li>\n    <li><a href=\"#input\">Input</a></li>\n    <li><a href=\"#selection\">Selection</a></li>\n    <li><a href=\"#update\">Intelligent Updating</a></li>\n    <li><a href=\"#parse\">Parsing</a></li>\n    <li><a href=\"#summary\">What Gives?</a></li>\n    <li><a href=\"#btree\">Content Representation</a></li>\n    <li><a href=\"#keymap\">Key Maps</a></li>\n  </ul>\n</div>\n\n<article>\n\n<h2 id=top>(Re-) Implementing A Syntax-Highlighting Editor in JavaScript</h2>\n\n<p style=\"font-size: 85%\" id=\"intro\">\n  <strong>Topic:</strong> JavaScript, code editor implementation<br>\n  <strong>Author:</strong> Marijn Haverbeke<br>\n  <strong>Date:</strong> March 2nd 2011 (updated November 13th 2011)\n</p>\n\n<p style=\"padding: 0 3em 0 2em\"><strong>Caution</strong>: this text was written briefly after\nversion 2 was initially written. It no longer (even including the\nupdate at the bottom) fully represents the current implementation. I'm\nleaving it here as a historic document. For more up-to-date\ninformation, look at the entries\ntagged <a href=\"http://marijnhaverbeke.nl/blog/#cm-internals\">cm-internals</a>\non my blog.</p>\n\n<p>This is a followup to\nmy <a href=\"https://codemirror.net/5/story.html\">Brutal Odyssey to the\nDark Side of the DOM Tree</a> story. That one describes the\nmind-bending process of implementing (what would become) CodeMirror 1.\nThis one describes the internals of CodeMirror 2, a complete rewrite\nand rethink of the old code base. I wanted to give this piece another\nHunter Thompson copycat subtitle, but somehow that would be out of\nplace—the process this time around was one of straightforward\nengineering, requiring no serious mind-bending whatsoever.</p>\n\n<p>So, what is wrong with CodeMirror 1? I'd estimate, by mailing list\nactivity and general search-engine presence, that it has been\nintegrated into about a thousand systems by now. The most prominent\none, since a few weeks,\nbeing <a href=\"http://googlecode.blogspot.com/2011/01/make-quick-fixes-quicker-on-google.html\">Google\ncode's project hosting</a>. It works, and it's being used widely.</p>\n\n<p>Still, I did not start replacing it because I was bored. CodeMirror\n1 was heavily reliant on <code>designMode</code>\nor <code>contentEditable</code> (depending on the browser). Neither of\nthese are well specified (HTML5 tries\nto <a href=\"http://www.w3.org/TR/html5/editing.html#contenteditable\">specify</a>\ntheir basics), and, more importantly, they tend to be one of the more\nobscure and buggy areas of browser functionality—CodeMirror, by using\nthis functionality in a non-typical way, was constantly running up\nagainst browser bugs. WebKit wouldn't show an empty line at the end of\nthe document, and in some releases would suddenly get unbearably slow.\nFirefox would show the cursor in the wrong place. Internet Explorer\nwould insist on linkifying everything that looked like a URL or email\naddress, a behaviour that can't be turned off. Some bugs I managed to\nwork around (which was often a frustrating, painful process), others,\nsuch as the Firefox cursor placement, I gave up on, and had to tell\nuser after user that they were known problems, but not something I\ncould help.</p>\n\n<p>Also, there is the fact that <code>designMode</code> (which seemed\nto be less buggy than <code>contentEditable</code> in Webkit and\nFirefox, and was thus used by CodeMirror 1 in those browsers) requires\na frame. Frames are another tricky area. It takes some effort to\nprevent getting tripped up by domain restrictions, they don't\ninitialize synchronously, behave strangely in response to the back\nbutton, and, on several browsers, can't be moved around the DOM\nwithout having them re-initialize. They did provide a very nice way to\nnamespace the library, though—CodeMirror 1 could freely pollute the\nnamespace inside the frame.</p>\n\n<p>Finally, working with an editable document means working with\nselection in arbitrary DOM structures. Internet Explorer (8 and\nbefore) has an utterly different (and awkward) selection API than all\nof the other browsers, and even among the different implementations of\n<code>document.selection</code>, details about how exactly a selection\nis represented vary quite a bit. Add to that the fact that Opera's\nselection support tended to be very buggy until recently, and you can\nimagine why CodeMirror 1 contains 700 lines of selection-handling\ncode.</p>\n\n<p>And that brings us to the main issue with the CodeMirror 1\ncode base: The proportion of browser-bug-workarounds to real\napplication code was getting dangerously high. By building on top of a\nfew dodgy features, I put the system in a vulnerable position—any\nincompatibility and bugginess in these features, I had to paper over\nwith my own code. Not only did I have to do some serious stunt-work to\nget it to work on older browsers (as detailed in the\nprevious <a href=\"https://codemirror.net/5/story.html\">story</a>), things\nalso kept breaking in newly released versions, requiring me to come up\nwith <em>new</em> scary hacks in order to keep up. This was starting\nto lose its appeal.</p>\n\n<section id=approach>\n  <h2>General Approach</h2>\n\n<p>What CodeMirror 2 does is try to sidestep most of the hairy hacks\nthat came up in version 1. I owe a lot to the\n<a href=\"http://ace.ajax.org\">ACE</a> editor for inspiration on how to\napproach this.</p>\n\n<p>I absolutely did not want to be completely reliant on key events to\ngenerate my input. Every JavaScript programmer knows that key event\ninformation is horrible and incomplete. Some people (most awesomely\nMihai Bazon with <a href=\"http://ymacs.org\">Ymacs</a>) have been able\nto build more or less functioning editors by directly reading key\nevents, but it takes a lot of work (the kind of never-ending, fragile\nwork I described earlier), and will never be able to properly support\nthings like multi-keystoke international character\ninput. <a href=\"#keymap\" class=\"update\">[see below for caveat]</a></p>\n\n<p>So what I do is focus a hidden textarea, and let the browser\nbelieve that the user is typing into that. What we show to the user is\na DOM structure we built to represent his document. If this is updated\nquickly enough, and shows some kind of believable cursor, it feels\nlike a real text-input control.</p>\n\n<p>Another big win is that this DOM representation does not have to\nspan the whole document. Some CodeMirror 1 users insisted that they\nneeded to put a 30 thousand line XML document into CodeMirror. Putting\nall that into the DOM takes a while, especially since, for some\nreason, an editable DOM tree is slower than a normal one on most\nbrowsers. If we have full control over what we show, we must only\nensure that the visible part of the document has been added, and can\ndo the rest only when needed. (Fortunately, the <code>onscroll</code>\nevent works almost the same on all browsers, and lends itself well to\ndisplaying things only as they are scrolled into view.)</p>\n</section>\n<section id=\"input\">\n  <h2>Input</h2>\n\n<p>ACE uses its hidden textarea only as a text input shim, and does\nall cursor movement and things like text deletion itself by directly\nhandling key events. CodeMirror's way is to let the browser do its\nthing as much as possible, and not, for example, define its own set of\nkey bindings. One way to do this would have been to have the whole\ndocument inside the hidden textarea, and after each key event update\nthe display DOM to reflect what's in that textarea.</p>\n\n<p>That'd be simple, but it is not realistic. For even medium-sized\ndocument the editor would be constantly munging huge strings, and get\nterribly slow. What CodeMirror 2 does is put the current selection,\nalong with an extra line on the top and on the bottom, into the\ntextarea.</p>\n\n<p>This means that the arrow keys (and their ctrl-variations), home,\nend, etcetera, do not have to be handled specially. We just read the\ncursor position in the textarea, and update our cursor to match it.\nAlso, copy and paste work pretty much for free, and people get their\nnative key bindings, without any special work on my part. For example,\nI have emacs key bindings configured for Chrome and Firefox. There is\nno way for a script to detect this. <a class=\"update\"\nhref=\"#keymap\">[no longer the case]</a></p>\n\n<p>Of course, since only a small part of the document sits in the\ntextarea, keys like page up and ctrl-end won't do the right thing.\nCodeMirror is catching those events and handling them itself.</p>\n</section>\n<section id=\"selection\">\n  <h2>Selection</h2>\n\n<p>Getting and setting the selection range of a textarea in modern\nbrowsers is trivial—you just use the <code>selectionStart</code>\nand <code>selectionEnd</code> properties. On IE you have to do some\ninsane stuff with temporary ranges and compensating for the fact that\nmoving the selection by a 'character' will treat \\r\\n as a single\ncharacter, but even there it is possible to build functions that\nreliably set and get the selection range.</p>\n\n<p>But consider this typical case: When I'm somewhere in my document,\npress shift, and press the up arrow, something gets selected. Then, if\nI, still holding shift, press the up arrow again, the top of my\nselection is adjusted. The selection remembers where its <em>head</em>\nand its <em>anchor</em> are, and moves the head when we shift-move.\nThis is a generally accepted property of selections, and done right by\nevery editing component built in the past twenty years.</p>\n\n<p>But not something that the browser selection APIs expose.</p>\n\n<p>Great. So when someone creates an 'upside-down' selection, the next\ntime CodeMirror has to update the textarea, it'll re-create the\nselection as an 'upside-up' selection, with the anchor at the top, and\nthe next cursor motion will behave in an unexpected way—our second\nup-arrow press in the example above will not do anything, since it is\ninterpreted in exactly the same way as the first.</p>\n\n<p>No problem. We'll just, ehm, detect that the selection is\nupside-down (you can tell by the way it was created), and then, when\nan upside-down selection is present, and a cursor-moving key is\npressed in combination with shift, we quickly collapse the selection\nin the textarea to its start, allow the key to take effect, and then\ncombine its new head with its old anchor to get the <em>real</em>\nselection.</p>\n\n<p>In short, scary hacks could not be avoided entirely in CodeMirror\n2.</p>\n\n<p>And, the observant reader might ask, how do you even know that a\nkey combo is a cursor-moving combo, if you claim you support any\nnative key bindings? Well, we don't, but we can learn. The editor\nkeeps a set known cursor-movement combos (initialized to the\npredictable defaults), and updates this set when it observes that\npressing a certain key had (only) the effect of moving the cursor.\nThis, of course, doesn't work if the first time the key is used was\nfor extending an inverted selection, but it works most of the\ntime.</p>\n</section>\n<section id=\"update\">\n  <h2>Intelligent Updating</h2>\n\n<p>One thing that always comes up when you have a complicated internal\nstate that's reflected in some user-visible external representation\n(in this case, the displayed code and the textarea's content) is\nkeeping the two in sync. The naive way is to just update the display\nevery time you change your state, but this is not only error prone\n(you'll forget), it also easily leads to duplicate work on big,\ncomposite operations. Then you start passing around flags indicating\nwhether the display should be updated in an attempt to be efficient\nagain and, well, at that point you might as well give up completely.</p>\n\n<p>I did go down that road, but then switched to a much simpler model:\nsimply keep track of all the things that have been changed during an\naction, and then, only at the end, use this information to update the\nuser-visible display.</p>\n\n<p>CodeMirror uses a concept of <em>operations</em>, which start by\ncalling a specific set-up function that clears the state and end by\ncalling another function that reads this state and does the required\nupdating. Most event handlers, and all the user-visible methods that\nchange state are wrapped like this. There's a method\ncalled <code>operation</code> that accepts a function, and returns\nanother function that wraps the given function as an operation.</p>\n\n<p>It's trivial to extend this (as CodeMirror does) to detect nesting,\nand, when an operation is started inside an operation, simply\nincrement the nesting count, and only do the updating when this count\nreaches zero again.</p>\n\n<p>If we have a set of changed ranges and know the currently shown\nrange, we can (with some awkward code to deal with the fact that\nchanges can add and remove lines, so we're dealing with a changing\ncoordinate system) construct a map of the ranges that were left\nintact. We can then compare this map with the part of the document\nthat's currently visible (based on scroll offset and editor height) to\ndetermine whether something needs to be updated.</p>\n\n<p>CodeMirror uses two update algorithms—a full refresh, where it just\ndiscards the whole part of the DOM that contains the edited text and\nrebuilds it, and a patch algorithm, where it uses the information\nabout changed and intact ranges to update only the out-of-date parts\nof the DOM. When more than 30 percent (which is the current heuristic,\nmight change) of the lines need to be updated, the full refresh is\nchosen (since it's faster to do than painstakingly finding and\nupdating all the changed lines), in the other case it does the\npatching (so that, if you scroll a line or select another character,\nthe whole screen doesn't have to be\nre-rendered). <span class=\"update\">[the full-refresh\nalgorithm was dropped, it wasn't really faster than the patching\none]</span></p>\n\n<p>All updating uses <code>innerHTML</code> rather than direct DOM\nmanipulation, since that still seems to be by far the fastest way to\nbuild documents. There's a per-line function that combines the\nhighlighting, <a href=\"manual.html#markText\">marking</a>, and\nselection info for that line into a snippet of HTML. The patch updater\nuses this to reset individual lines, the refresh updater builds an\nHTML chunk for the whole visible document at once, and then uses a\nsingle <code>innerHTML</code> update to do the refresh.</p>\n</section>\n<section id=\"parse\">\n  <h2>Parsers can be Simple</h2>\n\n<p>When I wrote CodeMirror 1, I\nthought <a href=\"https://codemirror.net/5/story.html#parser\">interruptible\nparsers</a> were a hugely scary and complicated thing, and I used a\nbunch of heavyweight abstractions to keep this supposed complexity\nunder control: parsers\nwere <a href=\"http://bob.pythonmac.org/archives/2005/07/06/iteration-in-javascript/\">iterators</a>\nthat consumed input from another iterator, and used funny\nclosure-resetting tricks to copy and resume themselves.</p>\n\n<p>This made for a rather nice system, in that parsers formed strictly\nseparate modules, and could be composed in predictable ways.\nUnfortunately, it was quite slow (stacking three or four iterators on\ntop of each other), and extremely intimidating to people not used to a\nfunctional programming style.</p>\n\n<p>With a few small changes, however, we can keep all those\nadvantages, but simplify the API and make the whole thing less\nindirect and inefficient. CodeMirror\n2's <a href=\"manual.html#modeapi\">mode API</a> uses explicit state\nobjects, and makes the parser/tokenizer a function that simply takes a\nstate and a character stream abstraction, advances the stream one\ntoken, and returns the way the token should be styled. This state may\nbe copied, optionally in a mode-defined way, in order to be able to\ncontinue a parse at a given point. Even someone who's never touched a\nlambda in his life can understand this approach. Additionally, far\nfewer objects are allocated in the course of parsing now.</p>\n\n<p>The biggest speedup comes from the fact that the parsing no longer\nhas to touch the DOM though. In CodeMirror 1, on an older browser, you\ncould <em>see</em> the parser work its way through the document,\nmanaging some twenty lines in each 50-millisecond time slice it got. It\nwas reading its input from the DOM, and updating the DOM as it went\nalong, which any experienced JavaScript programmer will immediately\nspot as a recipe for slowness. In CodeMirror 2, the parser usually\nfinishes the whole document in a single 100-millisecond time slice—it\nmanages some 1500 lines during that time on Chrome. All it has to do\nis munge strings, so there is no real reason for it to be slow\nanymore.</p>\n</section>\n<section id=\"summary\">\n  <h2>What Gives?</h2>\n\n<p>Given all this, what can you expect from CodeMirror 2?</p>\n\n<ul>\n\n<li><strong>Small.</strong> the base library is\nsome <span class=\"update\">45k</span> when minified\nnow, <span class=\"update\">17k</span> when gzipped. It's smaller than\nits own logo.</li>\n\n<li><strong>Lightweight.</strong> CodeMirror 2 initializes very\nquickly, and does almost no work when it is not focused. This means\nyou can treat it almost like a textarea, have multiple instances on a\npage without trouble.</li>\n\n<li><strong>Huge document support.</strong> Since highlighting is\nreally fast, and no DOM structure is being built for non-visible\ncontent, you don't have to worry about locking up your browser when a\nuser enters a megabyte-sized document.</li>\n\n<li><strong>Extended API.</strong> Some things kept coming up in the\nmailing list, such as marking pieces of text or lines, which were\nextremely hard to do with CodeMirror 1. The new version has proper\nsupport for these built in.</li>\n\n<li><strong>Tab support.</strong> Tabs inside editable documents were,\nfor some reason, a no-go. At least six different people announced they\nwere going to add tab support to CodeMirror 1, none survived (I mean,\nnone delivered a working version). CodeMirror 2 no longer removes tabs\nfrom your document.</li>\n\n<li><strong>Sane styling.</strong> <code>iframe</code> nodes aren't\nreally known for respecting document flow. Now that an editor instance\nis a plain <code>div</code> element, it is much easier to size it to\nfit the surrounding elements. You don't even have to make it scroll if\nyou do not <a href=\"../demo/resize.html\">want to</a>.</li>\n\n</ul>\n\n<p>On the downside, a CodeMirror 2 instance is <em>not</em> a native\neditable component. Though it does its best to emulate such a\ncomponent as much as possible, there is functionality that browsers\njust do not allow us to hook into. Doing select-all from the context\nmenu, for example, is not currently detected by CodeMirror.</p>\n\n<p id=\"changes\" style=\"margin-top: 2em;\"><span style=\"font-weight:\nbold\">[Updates from November 13th 2011]</span> Recently, I've made\nsome changes to the codebase that cause some of the text above to no\nlonger be current. I've left the text intact, but added markers at the\npassages that are now inaccurate. The new situation is described\nbelow.</p>\n</section>\n<section id=\"btree\">\n  <h2>Content Representation</h2>\n\n<p>The original implementation of CodeMirror 2 represented the\ndocument as a flat array of line objects. This worked well—splicing\narrays will require the part of the array after the splice to be\nmoved, but this is basically just a simple <code>memmove</code> of a\nbunch of pointers, so it is cheap even for huge documents.</p>\n\n<p>However, I recently added line wrapping and code folding (line\ncollapsing, basically). Once lines start taking up a non-constant\namount of vertical space, looking up a line by vertical position\n(which is needed when someone clicks the document, and to determine\nthe visible part of the document during scrolling) can only be done\nwith a linear scan through the whole array, summing up line heights as\nyou go. Seeing how I've been going out of my way to make big documents\nfast, this is not acceptable.</p>\n\n<p>The new representation is based on a B-tree. The leaves of the tree\ncontain arrays of line objects, with a fixed minimum and maximum size,\nand the non-leaf nodes simply hold arrays of child nodes. Each node\nstores both the amount of lines that live below them and the vertical\nspace taken up by these lines. This allows the tree to be indexed both\nby line number and by vertical position, and all access has\nlogarithmic complexity in relation to the document size.</p>\n\n<p>I gave line objects and tree nodes parent pointers, to the node\nabove them. When a line has to update its height, it can simply walk\nthese pointers to the top of the tree, adding or subtracting the\ndifference in height from each node it encounters. The parent pointers\nalso make it cheaper (in complexity terms, the difference is probably\ntiny in normal-sized documents) to find the current line number when\ngiven a line object. In the old approach, the whole document array had\nto be searched. Now, we can just walk up the tree and count the sizes\nof the nodes coming before us at each level.</p>\n\n<p>I chose B-trees, not regular binary trees, mostly because they\nallow for very fast bulk insertions and deletions. When there is a big\nchange to a document, it typically involves adding, deleting, or\nreplacing a chunk of subsequent lines. In a regular balanced tree, all\nthese inserts or deletes would have to be done separately, which could\nbe really expensive. In a B-tree, to insert a chunk, you just walk\ndown the tree once to find where it should go, insert them all in one\nshot, and then break up the node if needed. This breaking up might\ninvolve breaking up nodes further up, but only requires a single pass\nback up the tree. For deletion, I'm somewhat lax in keeping things\nbalanced—I just collapse nodes into a leaf when their child count goes\nbelow a given number. This means that there are some weird editing\npatterns that may result in a seriously unbalanced tree, but even such\nan unbalanced tree will perform well, unless you spend a day making\nstrangely repeating edits to a really big document.</p>\n</section>\n<section id=\"keymap\">\n  <h2>Keymaps</h2>\n\n<p><a href=\"#approach\">Above</a>, I claimed that directly catching key\nevents for things like cursor movement is impractical because it\nrequires some browser-specific kludges. I then proceeded to explain\nsome awful <a href=\"#selection\">hacks</a> that were needed to make it\npossible for the selection changes to be detected through the\ntextarea. In fact, the second hack is about as bad as the first.</p>\n\n<p>On top of that, in the presence of user-configurable tab sizes and\ncollapsed and wrapped lines, lining up cursor movement in the textarea\nwith what's visible on the screen becomes a nightmare. Thus, I've\ndecided to move to a model where the textarea's selection is no longer\ndepended on.</p>\n\n<p>So I moved to a model where all cursor movement is handled by my\nown code. This adds support for a goal column, proper interaction of\ncursor movement with collapsed lines, and makes it possible for\nvertical movement to move through wrapped lines properly, instead of\njust treating them like non-wrapped lines.</p>\n\n<p>The key event handlers now translate the key event into a string,\nsomething like <code>Ctrl-Home</code> or <code>Shift-Cmd-R</code>, and\nuse that string to look up an action to perform. To make keybinding\ncustomizable, this lookup goes through\na <a href=\"manual.html#option_keyMap\">table</a>, using a scheme that\nallows such tables to be chained together (for example, the default\nMac bindings fall through to a table named 'emacsy', which defines\nbasic Emacs-style bindings like <code>Ctrl-F</code>, and which is also\nused by the custom Emacs bindings).</p>\n\n<p>A new\noption <a href=\"manual.html#option_extraKeys\"><code>extraKeys</code></a>\nallows ad-hoc keybindings to be defined in a much nicer way than what\nwas possible with the\nold <a href=\"manual.html#option_onKeyEvent\"><code>onKeyEvent</code></a>\ncallback. You simply provide an object mapping key identifiers to\nfunctions, instead of painstakingly looking at raw key events.</p>\n\n<p>Built-in commands map to strings, rather than functions, for\nexample <code>\"goLineUp\"</code> is the default action bound to the up\narrow key. This allows new keymaps to refer to them without\nduplicating any code. New commands can be defined by assigning to\nthe <code>CodeMirror.commands</code> object, which maps such commands\nto functions.</p>\n\n<p>The hidden textarea now only holds the current selection, with no\nextra characters around it. This has a nice advantage: polling for\ninput becomes much, much faster. If there's a big selection, this text\ndoes not have to be read from the textarea every time—when we poll,\njust noticing that something is still selected is enough to tell us\nthat no new text was typed.</p>\n\n<p>The reason that cheap polling is important is that many browsers do\nnot fire useful events on IME (input method engine) input, which is\nthe thing where people inputting a language like Japanese or Chinese\nuse multiple keystrokes to create a character or sequence of\ncharacters. Most modern browsers fire <code>input</code> when the\ncomposing is finished, but many don't fire anything when the character\nis updated <em>during</em> composition. So we poll, whenever the\neditor is focused, to provide immediate updates of the display.</p>\n\n</section>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/doc/manual.html",
    "content": "<!doctype html>\n\n<title>CodeMirror 5 User Manual</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"docs.css\">\n<script src=\"activebookmark.js\"></script>\n\n<script src=\"../lib/codemirror.js\"></script>\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../addon/runmode/runmode.js\"></script>\n<script src=\"../addon/runmode/colorize.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<script src=\"../mode/css/css.js\"></script>\n<script src=\"../mode/htmlmixed/htmlmixed.js\"></script>\n<style>\n  dt { text-indent: -2em; padding-left: 2em; margin-top: 1em; }\n  dd { margin-left: 1.5em; margin-bottom: 1em; }\n  dt {margin-top: 1em;}\n  dd dl, dd dt, dd dd, dd ul { margin-top: 0; margin-bottom: 0; }\n  dt + dt { margin-top: 0; }\n  dt.command { position: relative; }\n  span.keybinding { position: absolute; right: 0; font-size: 80%; color: #555; text-indent: 0; }\n</style>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"logo.png\"></a>\n  <ul>\n    <li><a href=\"../index.html\">Home</a></li>\n    <li><a href=\"#overview\" class=active data-default=\"true\">Manual</a></li>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a></li>\n  </ul>\n  <ul>\n    <li><a href=\"#usage\">Basic Usage</a></li>\n    <li><a href=\"#config\">Configuration</a></li>\n    <li><a href=\"#events\">Events</a></li>\n    <li><a href=\"#keymaps\">Key maps</a></li>\n    <li><a href=\"#commands\">Commands</a></li>\n    <li><a href=\"#styling\">Customized Styling</a></li>\n    <li><a href=\"#api\">Programming API</a>\n      <ul>\n        <li><a href=\"#api_constructor\">Constructor</a></li>\n        <li><a href=\"#api_content\">Content manipulation</a></li>\n        <li><a href=\"#api_selection\">Selection</a></li>\n        <li><a href=\"#api_configuration\">Configuration</a></li>\n        <li><a href=\"#api_doc\">Document management</a></li>\n        <li><a href=\"#api_history\">History</a></li>\n        <li><a href=\"#api_marker\">Text-marking</a></li>\n        <li><a href=\"#api_decoration\">Widget, gutter, and decoration</a></li>\n        <li><a href=\"#api_sizing\">Sizing, scrolling, and positioning</a></li>\n        <li><a href=\"#api_mode\">Mode, state, and tokens</a></li>\n        <li><a href=\"#api_misc\">Miscellaneous methods</a></li>\n        <li><a href=\"#api_static\">Static properties</a></li>\n      </ul>\n    </li>\n    <li><a href=\"#addons\">Addons</a></li>\n    <li><a href=\"#modeapi\">Writing CodeMirror Modes</a></li>\n    <li><a href=\"#vimapi\">Vim Mode API</a>\n      <ul>\n        <li><a href=\"#vimapi_configuration\">Configuration</a></li>\n        <li><a href=\"#vimapi_events\">Events</a></li>\n        <li><a href=\"#vimapi_extending\">Extending VIM</a></li>\n      </ul>\n    </li>\n  </ul>\n</div>\n\n<article>\n\n<section class=first id=overview>\n    <h2 style=\"position: relative\">\n      User manual and reference guide\n      <span style=\"color: #888; font-size: 1rem; position: absolute; right: 0; bottom: 0\">version 5.65.16</span>\n    </h2>\n\n    <p>CodeMirror is a code-editor component that can be embedded in\n    Web pages. The core library provides <em>only</em> the editor\n    component, no accompanying buttons, auto-completion, or other IDE\n    functionality. It does provide a rich API on top of which such\n    functionality can be straightforwardly implemented. See\n    the <a href=\"#addons\">addons</a> included in the distribution,\n    and <a href=\"https://npms.io/search?q=codemirror\">3rd party\n    packages on npm</a>, for reusable implementations of extra\n    features.</p>\n\n    <p>CodeMirror works with language-specific modes. Modes are\n    JavaScript programs that help color (and optionally indent) text\n    written in a given language. The distribution comes with a number\n    of modes (see the <a href=\"../mode/\"><code>mode/</code></a>\n    directory), and it isn't hard to <a href=\"#modeapi\">write new\n    ones</a> for other languages.</p>\n</section>\n\n<section id=usage>\n    <h2>Basic Usage</h2>\n\n    <p>The easiest way to use CodeMirror is to simply load the script\n    and style sheet found under <code>lib/</code> in the distribution,\n    plus a mode script from one of the <code>mode/</code> directories.\n    For example:</p>\n\n    <pre data-lang=\"text/html\">&lt;script src=\"lib/codemirror.js\">&lt;/script>\n&lt;link rel=\"stylesheet\" href=\"lib/codemirror.css\">\n&lt;script src=\"mode/javascript/javascript.js\">&lt;/script></pre>\n\n    <p>(Alternatively, use a module loader. <a href=\"#modloader\">More\n    about that later.</a>)</p>\n\n    <p>Having done this, an editor instance can be created like\n    this:</p>\n\n    <pre data-lang=\"javascript\">var myCodeMirror = CodeMirror(document.body);</pre>\n\n    <p>The editor will be appended to the document body, will start\n    empty, and will use the mode that we loaded. To have more control\n    over the new editor, a configuration object can be passed\n    to <a href=\"#CodeMirror\"><code>CodeMirror</code></a> as a second\n    argument:</p>\n\n    <pre data-lang=\"javascript\">var myCodeMirror = CodeMirror(document.body, {\n  value: \"function myScript(){return 100;}\\n\",\n  mode:  \"javascript\"\n});</pre>\n\n    <p>This will initialize the editor with a piece of code already in\n    it, and explicitly tell it to use the JavaScript mode (which is\n    useful when multiple modes are loaded).\n    See <a href=\"#config\">below</a> for a full discussion of the\n    configuration options that CodeMirror accepts.</p>\n\n    <p>In cases where you don't want to append the editor to an\n    element, and need more control over the way it is inserted, the\n    first argument to the <code>CodeMirror</code> function can also\n    be a function that, when given a DOM element, inserts it into the\n    document somewhere. This could be used to, for example, replace a\n    textarea with a real editor:</p>\n\n    <pre data-lang=\"javascript\">var myCodeMirror = CodeMirror(function(elt) {\n  myTextArea.parentNode.replaceChild(elt, myTextArea);\n}, {value: myTextArea.value});</pre>\n\n    <p>However, for this use case, which is a common way to use\n    CodeMirror, the library provides a much more powerful\n    shortcut:</p>\n\n    <pre data-lang=\"javascript\">var myCodeMirror = CodeMirror.fromTextArea(myTextArea);</pre>\n\n    <p>This will, among other things, ensure that the textarea's value\n    is updated with the editor's contents when the form (if it is part\n    of a form) is submitted. See the <a href=\"#fromTextArea\">API\n    reference</a> for a full description of this method.</p>\n\n    <h3 id=modloader>Module loaders</h3>\n\n    <p>The files in the CodeMirror distribution contain shims for\n    loading them (and their dependencies) in AMD or CommonJS\n    environments. If the variables <code>exports</code>\n    and <code>module</code> exist and have type object, CommonJS-style\n    require will be used. If not, but there is a\n    function <code>define</code> with an <code>amd</code> property\n    present, AMD-style (RequireJS) will be used.</p>\n\n    <p>It is possible to\n    use <a href=\"http://browserify.org/\">Browserify</a> or similar\n    tools to statically build modules using CodeMirror. Alternatively,\n    use <a href=\"http://requirejs.org/\">RequireJS</a> to dynamically\n    load dependencies at runtime. Both of these approaches have the\n    advantage that they don't use the global namespace and can, thus,\n    do things like load multiple versions of CodeMirror alongside each\n    other.</p>\n\n    <p>Here's a simple example of using RequireJS to load CodeMirror:</p>\n\n    <pre data-lang=\"javascript\">require([\n  \"cm/lib/codemirror\", \"cm/mode/htmlmixed/htmlmixed\"\n], function(CodeMirror) {\n  CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    lineNumbers: true,\n    mode: \"htmlmixed\"\n  });\n});</pre>\n\n    <p>It will automatically load the modes that the mixed HTML mode\n    depends on (XML, JavaScript, and CSS). Do <em>not</em> use\n    RequireJS' <code>paths</code> option to configure the path to\n    CodeMirror, since it will break loading submodules through\n    relative paths. Use\n    the <a href=\"http://requirejs.org/docs/api.html#packages\"><code>packages</code></a>\n    configuration option instead, as in:</p>\n\n    <pre data-lang=javascript>require.config({\n  packages: [{\n    name: \"codemirror\",\n    location: \"../path/to/codemirror\",\n    main: \"lib/codemirror\"\n  }]\n});</pre>\n\n</section>\n\n<section id=config>\n    <h2>Configuration</h2>\n\n    <p>Both the <a href=\"#CodeMirror\"><code>CodeMirror</code></a>\n    function and its <code>fromTextArea</code> method take as second\n    (optional) argument an object containing configuration options.\n    Any option not supplied like this will be taken\n    from <a href=\"#defaults\"><code>CodeMirror.defaults</code></a>, an\n    object containing the default options. You can update this object\n    to change the defaults on your page.</p>\n\n    <p>Options are not checked in any way, so setting bogus option\n    values is bound to lead to odd errors.</p>\n\n    <p>These are the supported options:</p>\n\n    <dl>\n      <dt id=\"option_value\"><code><strong>value</strong>: string|CodeMirror.Doc</code></dt>\n      <dd>The starting value of the editor. Can be a string, or\n      a <a href=\"#api_doc\">document object</a>.</dd>\n\n      <dt id=\"option_mode\"><code><strong>mode</strong>: string|object</code></dt>\n      <dd>The mode to use. When not given, this will default to the\n      first mode that was loaded. It may be a string, which either\n      simply names the mode or is\n      a <a href=\"http://en.wikipedia.org/wiki/MIME\">MIME</a> type\n      associated with the mode. The value <code>\"null\"</code>\n      indicates no highlighting should be applied. Alternatively, it\n      may be an object containing configuration options for the mode,\n      with a <code>name</code> property that names the mode (for\n      example <code>{name: \"javascript\", json: true}</code>). The demo\n      pages for each mode contain information about what configuration\n      parameters the mode supports. You can ask CodeMirror which modes\n      and MIME types have been defined by inspecting\n      the <code>CodeMirror.modes</code>\n      and <code>CodeMirror.mimeModes</code> objects. The first maps\n      mode names to their constructors, and the second maps MIME types\n      to mode specs.</dd>\n\n      <dt id=\"option_lineSeparator\"><code><strong>lineSeparator</strong>: string|null</code></dt>\n      <dd>Explicitly set the line separator for the editor. By default\n      (value <code>null</code>), the document will be split on CRLFs\n      as well as lone CRs and LFs, and a single LF will be used as\n      line separator in all output (such\n      as <a href=\"#getValue\"><code>getValue</code></a>). When a\n      specific string is given, lines will only be split on that\n      string, and output will, by default, use that same\n      separator.</dd>\n\n      <dt id=\"option_theme\"><code><strong>theme</strong>: string</code></dt>\n      <dd>The theme to style the editor with. You must make sure the\n      CSS file defining the corresponding <code>.cm-s-[name]</code>\n      styles is loaded (see\n      the <a href=\"../theme/\"><code>theme</code></a> directory in the\n      distribution). The default is <code>\"default\"</code>, for which\n      colors are included in <code>codemirror.css</code>. It is\n      possible to use multiple theming classes at once—for\n      example <code>\"foo bar\"</code> will assign both\n      the <code>cm-s-foo</code> and the <code>cm-s-bar</code> classes\n      to the editor.</dd>\n\n      <dt id=\"option_indentUnit\"><code><strong>indentUnit</strong>: integer</code></dt>\n      <dd>How many spaces a block (whatever that means in the edited\n      language) should be indented. The default is 2.</dd>\n\n      <dt id=\"option_smartIndent\"><code><strong>smartIndent</strong>: boolean</code></dt>\n      <dd>Whether to use the context-sensitive indentation that the\n      mode provides (or just indent the same as the line before).\n      Defaults to true.</dd>\n\n      <dt id=\"option_tabSize\"><code><strong>tabSize</strong>: integer</code></dt>\n      <dd>The width of a tab character. Defaults to 4.</dd>\n\n      <dt id=\"option_indentWithTabs\"><code><strong>indentWithTabs</strong>: boolean</code></dt>\n      <dd>Whether, when indenting, the first N*<code>tabSize</code>\n      spaces should be replaced by N tabs. Default is false.</dd>\n\n      <dt id=\"option_electricChars\"><code><strong>electricChars</strong>: boolean</code></dt>\n      <dd>Configures whether the editor should re-indent the current\n      line when a character is typed that might change its proper\n      indentation (only works if the mode supports indentation).\n      Default is true.</dd>\n\n      <dt id=\"option_specialChars\"><code><strong>specialChars</strong>: RegExp</code></dt>\n      <dd>A regular expression used to determine which characters\n      should be replaced by a\n      special <a href=\"#option_specialCharPlaceholder\">placeholder</a>.\n      Mostly useful for non-printing special characters. The default\n      is <code>/[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u061c\\u200b\\u200e\\u200f\\u2028\\u2029\\u202d\\u202e\\u2066\\u2067\\u2069\\ufeff\\ufff9-\\ufffc]/</code>.</dd>\n      <dt id=\"option_specialCharPlaceholder\"><code><strong>specialCharPlaceholder</strong>: function(char) → Element</code></dt>\n      <dd>A function that, given a special character identified by\n      the <a href=\"#option_specialChars\"><code>specialChars</code></a>\n      option, produces a DOM node that is used to represent the\n      character. By default, a red dot (<span style=\"color: red\">•</span>)\n      is shown, with a title tooltip to indicate the character code.</dd>\n\n      <dt id=\"option_direction\"><code><strong>direction</strong>: \"ltr\" | \"rtl\"</code></dt>\n      <dd>Flips overall layout and selects base paragraph direction to\n      be left-to-right or right-to-left.  Default is \"ltr\".\n      CodeMirror applies the Unicode Bidirectional Algorithm to each\n      line, but does not autodetect base direction — it's set to the\n      editor direction for all lines.  The resulting order is\n      sometimes wrong when base direction doesn't match user intent\n      (for example, leading and trailing punctuation jumps to the\n      wrong side of the line).  Therefore, it's helpful for\n      multilingual input to let users toggle this option.\n\n      <dt id=\"option_rtlMoveVisually\"><code><strong>rtlMoveVisually</strong>: boolean</code></dt>\n      <dd>Determines whether horizontal cursor movement through\n      right-to-left (Arabic, Hebrew) text is visual (pressing the left\n      arrow moves the cursor left) or logical (pressing the left arrow\n      moves to the next lower index in the string, which is visually\n      right in right-to-left text). The default is <code>false</code>\n      on Windows, and <code>true</code> on other platforms.</dd>\n\n      <dt id=\"option_keyMap\"><code><strong>keyMap</strong>: string</code></dt>\n      <dd>Configures the key map to use. The default\n      is <code>\"default\"</code>, which is the only key map defined\n      in <code>codemirror.js</code> itself. Extra key maps are found in\n      the <a href=\"../keymap/\"><code>key map</code></a> directory. See\n      the <a href=\"#keymaps\">section on key maps</a> for more\n      information.</dd>\n\n      <dt id=\"option_extraKeys\"><code><strong>extraKeys</strong>: object</code></dt>\n      <dd>Can be used to specify extra key bindings for the editor,\n      alongside the ones defined\n      by <a href=\"#option_keyMap\"><code>keyMap</code></a>. Should be\n      either null, or a valid <a href=\"#keymaps\">key map</a> value.</dd>\n\n      <dt id=\"option_configureMouse\"><code><strong>configureMouse</strong>: fn(cm: CodeMirror, repeat: \"single\" | \"double\" | \"triple\", event: Event) → Object</code></dt>\n      <dd>Allows you to configure the behavior of mouse selection and\n      dragging. The function is called when the left mouse button is\n      pressed. The returned object may have the following properties:\n        <dl>\n          <dt><code><strong>unit</strong>: \"char\" | \"word\" | \"line\" | \"rectangle\" | fn(CodeMirror, Pos) → {from: Pos, to: Pos}</code></dt>\n          <dd>The unit by which to select. May be one of the built-in\n          units or a function that takes a position and returns a\n          range around that, for a custom unit. The default is to\n          return <code>\"word\"</code> for double\n          clicks, <code>\"line\"</code> for triple\n          clicks, <code>\"rectangle\"</code> for alt-clicks (or, on\n          Chrome OS, meta-shift-clicks), and <code>\"single\"</code>\n          otherwise.</dd>\n          <dt><code><strong>extend</strong>: bool</code></dt>\n          <dd>Whether to extend the existing selection range or start\n          a new one. By default, this is enabled when shift\n          clicking.</dd>\n          <dt><code><strong>addNew</strong>: bool</code></dt>\n          <dd>When enabled, this adds a new range to the existing\n          selection, rather than replacing it. The default behavior is\n          to enable this for command-click on Mac OS, and\n          control-click on other platforms.</dd>\n          <dt><code><strong>moveOnDrag</strong>: bool</code></dt>\n          <dd>When the mouse even drags content around inside the\n          editor, this controls whether it is copied (false) or moved\n          (true). By default, this is enabled by alt-clicking on Mac\n          OS, and ctrl-clicking elsewhere.</dd>\n        </dl>\n      </dd>\n\n      <dt id=\"option_lineWrapping\"><code><strong>lineWrapping</strong>: boolean</code></dt>\n      <dd>Whether CodeMirror should scroll or wrap for long lines.\n      Defaults to <code>false</code> (scroll).</dd>\n\n      <dt id=\"option_lineNumbers\"><code><strong>lineNumbers</strong>: boolean</code></dt>\n      <dd>Whether to show line numbers to the left of the editor.</dd>\n\n      <dt id=\"option_firstLineNumber\"><code><strong>firstLineNumber</strong>: integer</code></dt>\n      <dd>At which number to start counting lines. Default is 1.</dd>\n\n      <dt id=\"option_lineNumberFormatter\"><code><strong>lineNumberFormatter</strong>: function(line: integer) → string</code></dt>\n      <dd>A function used to format line numbers. The function is\n      passed the line number, and should return a string that will be\n      shown in the gutter.</dd>\n\n      <dt id=\"option_gutters\"><code><strong>gutters</strong>: array&lt;string | {className: string, style: ?string}&gt;</code></dt>\n      <dd>Can be used to add extra gutters (beyond or instead of the\n      line number gutter). Should be an array of CSS class names or\n      class name / CSS string pairs, each of which defines\n      a <code>width</code> (and optionally a background), and which\n      will be used to draw the background of the gutters. <em>May</em>\n      include the <code>CodeMirror-linenumbers</code> class, in order\n      to explicitly set the position of the line number gutter (it\n      will default to be to the right of all other gutters). These\n      class names are the keys passed\n      to <a href=\"#setGutterMarker\"><code>setGutterMarker</code></a>.</dd>\n\n      <dt id=\"option_fixedGutter\"><code><strong>fixedGutter</strong>: boolean</code></dt>\n      <dd>Determines whether the gutter scrolls along with the content\n      horizontally (false) or whether it stays fixed during horizontal\n      scrolling (true, the default).</dd>\n\n      <dt id=\"option_scrollbarStyle\"><code><strong>scrollbarStyle</strong>: string</code></dt>\n      <dd>Chooses a scrollbar implementation. The default\n      is <code>\"native\"</code>, showing native scrollbars. The core\n      library also provides the <code>\"null\"</code> style, which\n      completely hides the\n      scrollbars. <a href=\"#addon_simplescrollbars\">Addons</a> can\n      implement additional scrollbar models.</dd>\n\n      <dt id=\"option_coverGutterNextToScrollbar\"><code><strong>coverGutterNextToScrollbar</strong>: boolean</code></dt>\n      <dd>When <a href=\"#option_fixedGutter\"><code>fixedGutter</code></a>\n      is on, and there is a horizontal scrollbar, by default the\n      gutter will be visible to the left of this scrollbar. If this\n      option is set to true, it will be covered by an element with\n      class <code>CodeMirror-gutter-filler</code>.</dd>\n\n      <dt id=\"option_inputStyle\"><code><strong>inputStyle</strong>: string</code></dt>\n      <dd>Selects the way CodeMirror handles input and focus. The core\n      library defines the <code>\"textarea\"</code>\n      and <code>\"contenteditable\"</code> input models. On mobile\n      browsers, the default is <code>\"contenteditable\"</code>. On\n      desktop browsers, the default is <code>\"textarea\"</code>.\n      Support for IME and screen readers is better in\n      the <code>\"contenteditable\"</code> model. The intention is to\n      make it the default on modern desktop browsers in the\n      future.</dd>\n\n      <dt id=\"option_readOnly\"><code><strong>readOnly</strong>: boolean|string</code></dt>\n      <dd>This disables editing of the editor content by the user. If\n      the special value <code>\"nocursor\"</code> is given (instead of\n      simply <code>true</code>), focusing of the editor is also\n      disallowed.</dd>\n\n      <dt id=\"option_screenReaderLabel\"><code><strong>screenReaderLabel</strong>: string</code></dt>\n      <dd>This label is read by the screenreaders when CodeMirror text area is focused. This\n      is helpful for accessibility.</dd>\n\n      <dt id=\"option_showCursorWhenSelecting\"><code><strong>showCursorWhenSelecting</strong>: boolean</code></dt>\n      <dd>Whether the cursor should be drawn when a selection is\n      active. Defaults to false.</dd>\n\n      <dt id=\"option_lineWiseCopyCut\"><code><strong>lineWiseCopyCut</strong>: boolean</code></dt>\n      <dd>When enabled, which is the default, doing copy or cut when\n      there is no selection will copy or cut the whole lines that have\n      cursors on them.</dd>\n\n      <dt id=\"option_pasteLinesPerSelection\"><code><strong>pasteLinesPerSelection</strong>: boolean</code></dt>\n      <dd>When pasting something from an external source (not from the\n      editor itself), if the number of lines matches the number of\n      selection, CodeMirror will by default insert one line per\n      selection. You can set this to <code>false</code> to disable\n      that behavior.</dd>\n\n      <dt id=\"option_selectionsMayTouch\"><code><strong>selectionsMayTouch</strong>: boolean</code></dt>\n      <dd>Determines whether multiple selections are joined as soon as\n      they touch (the default) or only when they overlap (true).</dd>\n\n      <dt id=\"option_undoDepth\"><code><strong>undoDepth</strong>: integer</code></dt>\n      <dd>The maximum number of undo levels that the editor stores.\n      Note that this includes selection change events. Defaults to\n      200.</dd>\n\n      <dt id=\"option_historyEventDelay\"><code><strong>historyEventDelay</strong>: integer</code></dt>\n      <dd>The period of inactivity (in milliseconds) that will cause a\n      new history event to be started when typing or deleting.\n      Defaults to 1250.</dd>\n\n      <dt id=\"option_tabindex\"><code><strong>tabindex</strong>: integer</code></dt>\n      <dd>The <a href=\"http://www.w3.org/TR/html401/interact/forms.html#adef-tabindex\">tab\n      index</a> to assign to the editor. If not given, no tab index\n      will be assigned.</dd>\n\n      <dt id=\"option_autofocus\"><code><strong>autofocus</strong>: boolean</code></dt>\n      <dd>Can be used to make CodeMirror focus itself on\n      initialization. Defaults to off.\n      When <a href=\"#fromTextArea\"><code>fromTextArea</code></a> is\n      used, and no explicit value is given for this option, it will be\n      set to true when either the source textarea is focused, or it\n      has an <code>autofocus</code> attribute and no other element is\n      focused.</dd>\n\n      <dt id=\"option_phrases\"><code><strong>phrases</strong>: ?object</code></dt>\n      <dd>Some addons run user-visible strings (such as labels in the\n      interface) through the <a href=\"#phrase\"><code>phrase</code></a>\n      method to allow for translation. This option determines the\n      return value of that method. When it is null or an object that\n      doesn't have a property named by the input string, that string\n      is returned. Otherwise, the value of the property corresponding\n      to that string is returned.</dd>\n    </dl>\n\n    <p>Below this a few more specialized, low-level options are\n    listed. These are only useful in very specific situations, you\n    might want to skip them the first time you read this manual.</p>\n\n    <dl>\n      <dt id=\"option_dragDrop\"><code><strong>dragDrop</strong>: boolean</code></dt>\n      <dd>Controls whether drag-and-drop is enabled. On by default.</dd>\n\n      <dt id=\"option_allowDropFileTypes\"><code><strong>allowDropFileTypes</strong>: array&lt;string&gt;</code></dt>\n      <dd>When set (default is <code>null</code>) only files whose\n      type is in the array can be dropped into the editor. The strings\n      should be MIME types, and will be checked against\n      the <a href=\"https://w3c.github.io/FileAPI/#dfn-type\"><code>type</code></a>\n      of the <code>File</code> object as reported by the browser.</dd>\n\n      <dt id=\"option_cursorBlinkRate\"><code><strong>cursorBlinkRate</strong>: number</code></dt>\n      <dd>Half-period in milliseconds used for cursor blinking. The default blink\n      rate is 530ms. By setting this to zero, blinking can be disabled. A\n      negative value hides the cursor entirely.</dd>\n\n      <dt id=\"option_cursorScrollMargin\"><code><strong>cursorScrollMargin</strong>: number</code></dt>\n      <dd>How much extra space to always keep above and below the\n      cursor when approaching the top or bottom of the visible view in\n      a scrollable document. Default is 0.</dd>\n\n      <dt id=\"option_cursorHeight\"><code><strong>cursorHeight</strong>: number</code></dt>\n      <dd>Determines the height of the cursor. Default is 1, meaning\n      it spans the whole height of the line. For some fonts (and by\n      some tastes) a smaller height (for example <code>0.85</code>),\n      which causes the cursor to not reach all the way to the bottom\n      of the line, looks better</dd>\n\n      <dt id=\"option_singleCursorHeightPerLine\"><code><strong>singleCursorHeightPerLine</strong>: boolean</code></dt>\n      <dd>If set to <code>true</code> (the default), will keep the\n      cursor height constant for an entire line (or wrapped part of a\n      line). When <code>false</code>, the cursor's height is based on\n      the height of the adjacent reference character.</dd>\n\n      <dt id=\"option_resetSelectionOnContextMenu\"><code><strong>resetSelectionOnContextMenu</strong>: boolean</code></dt>\n      <dd>Controls whether, when the context menu is opened with a\n      click outside of the current selection, the cursor is moved to\n      the point of the click. Defaults to <code>true</code>.</dd>\n\n      <dt id=\"option_workTime\"><code id=\"option_wordkDelay\"><strong>workTime</strong>, <strong>workDelay</strong>: number</code></dt>\n      <dd>Highlighting is done by a pseudo background-thread that will\n      work for <code>workTime</code> milliseconds, and then use\n      timeout to sleep for <code>workDelay</code> milliseconds. The\n      defaults are 200 and 300, you can change these options to make\n      the highlighting more or less aggressive.</dd>\n\n      <dt id=\"option_pollInterval\"><code><strong>pollInterval</strong>: number</code></dt>\n      <dd>Indicates how quickly CodeMirror should poll its input\n      textarea for changes (when focused). Most input is captured by\n      events, but some things, like IME input on some browsers, don't\n      generate events that allow CodeMirror to properly detect it.\n      Thus, it polls. Default is 100 milliseconds.</dd>\n\n      <dt id=\"option_flattenSpans\"><code><strong>flattenSpans</strong>: boolean</code></dt>\n      <dd>By default, CodeMirror will combine adjacent tokens into a\n      single span if they have the same class. This will result in a\n      simpler DOM tree, and thus perform better. With some kinds of\n      styling (such as rounded corners), this will change the way the\n      document looks. You can set this option to false to disable this\n      behavior.</dd>\n\n      <dt id=\"option_addModeClass\"><code><strong>addModeClass</strong>: boolean</code></dt>\n      <dd>When enabled (off by default), an extra CSS class will be\n      added to each token, indicating the\n      (<a href=\"#innerMode\">inner</a>) mode that produced it, prefixed\n      with <code>\"cm-m-\"</code>. For example, tokens from the XML mode\n      will get the <code>cm-m-xml</code> class.</dd>\n\n      <dt id=\"option_maxHighlightLength\"><code><strong>maxHighlightLength</strong>: number</code></dt>\n      <dd>When highlighting long lines, in order to stay responsive,\n      the editor will give up and simply style the rest of the line as\n      plain text when it reaches a certain position. The default is\n      10 000. You can set this to <code>Infinity</code> to turn off\n      this behavior.</dd>\n\n      <dt id=\"option_viewportMargin\"><code><strong>viewportMargin</strong>: integer</code></dt>\n      <dd>Specifies the amount of lines that are rendered above and\n      below the part of the document that's currently scrolled into\n      view. This affects the amount of updates needed when scrolling,\n      and the amount of work that such an update does. You should\n      usually leave it at its default, 10. Can be set\n      to <code>Infinity</code> to make sure the whole document is\n      always rendered, and thus the browser's text search works on it.\n      This <em>will</em> have bad effects on performance of big\n      documents.</dd>\n\n      <dt id=\"option_spellcheck\"><code><strong>spellcheck</strong>: boolean</code></dt>\n      <dd>Specifies whether or not spellcheck will be enabled on the input.</dd>\n\n      <dt id=\"option_autocorrect\"><code><strong>autocorrect</strong>: boolean</code></dt>\n      <dd>Specifies whether or not autocorrect will be enabled on the input.</dd>\n\n      <dt id=\"option_autocapitalize\"><code><strong>autocapitalize</strong>: boolean</code></dt>\n      <dd>Specifies whether or not autocapitalization will be enabled on the input.</dd>\n    </dl>\n</section>\n\n<section id=events>\n    <h2>Events</h2>\n\n    <p>Various CodeMirror-related objects emit events, which allow\n    client code to react to various situations. Handlers for such\n    events can be registered with the <a href=\"#on\"><code>on</code></a>\n    and <a href=\"#off\"><code>off</code></a> methods on the objects\n    that the event fires on. To fire your own events,\n    use <code>CodeMirror.signal(target, name, args...)</code>,\n    where <code>target</code> is a non-DOM-node object.</p>\n\n    <p>An editor instance fires the following events.\n    The <code>instance</code> argument always refers to the editor\n    itself.</p>\n\n    <dl>\n      <dt id=\"event_change\"><code><strong>\"change\"</strong> (instance: CodeMirror, changeObj: object)</code></dt>\n      <dd>Fires every time the content of the editor is changed.\n      The <code>changeObj</code> is a <code>{from, to, text, removed,\n      origin}</code> object containing information about the changes\n      that occurred as second argument. <code>from</code>\n      and <code>to</code> are the positions (in the pre-change\n      coordinate system) where the change started and ended (for\n      example, it might be <code>{ch:0, line:18}</code> if the\n      position is at the beginning of line #19). <code>text</code> is\n      an array of strings representing the text that replaced the\n      changed range (split by line). <code>removed</code> is the text\n      that used to be between <code>from</code> and <code>to</code>,\n      which is overwritten by this change. This event is\n      fired <em>before</em> the end of\n      an <a href=\"#operation\">operation</a>, before the DOM updates\n      happen.</dd>\n\n      <dt id=\"event_changes\"><code><strong>\"changes\"</strong> (instance: CodeMirror, changes: array&lt;object&gt;)</code></dt>\n      <dd>Like the <a href=\"#event_change\"><code>\"change\"</code></a>\n      event, but batched per <a href=\"#operation\">operation</a>,\n      passing an array containing all the changes that happened in the\n      operation. This event is fired after the operation finished, and\n      display changes it makes will trigger a new operation.</dd>\n\n      <dt id=\"event_beforeChange\"><code><strong>\"beforeChange\"</strong> (instance: CodeMirror, changeObj: object)</code></dt>\n      <dd>This event is fired before a change is applied, and its\n      handler may choose to modify or cancel the change.\n      The <code>changeObj</code> object\n      has <code>from</code>, <code>to</code>, and <code>text</code>\n      properties, as with\n      the <a href=\"#event_change\"><code>\"change\"</code></a> event. It\n      also has a <code>cancel()</code> method, which can be called to\n      cancel the change, and, <strong>if</strong> the change isn't\n      coming from an undo or redo event, an <code>update(from, to,\n      text)</code> method, which may be used to modify the change.\n      Undo or redo changes can't be modified, because they hold some\n      metainformation for restoring old marked ranges that is only\n      valid for that specific change. All three arguments\n      to <code>update</code> are optional, and can be left off to\n      leave the existing value for that field\n      intact. <strong>Note:</strong> you may not do anything from\n      a <code>\"beforeChange\"</code> handler that would cause changes\n      to the document or its visualization. Doing so will, since this\n      handler is called directly from the bowels of the CodeMirror\n      implementation, probably cause the editor to become\n      corrupted.</dd>\n\n      <dt id=\"event_cursorActivity\"><code><strong>\"cursorActivity\"</strong> (instance: CodeMirror)</code></dt>\n      <dd>Will be fired when the cursor or selection moves, or any\n      change is made to the editor content.</dd>\n\n      <dt id=\"event_keyHandled\"><code><strong>\"keyHandled\"</strong> (instance: CodeMirror, name: string, event: Event)</code></dt>\n      <dd>Fired after a key is handled through a\n      key map. <code>name</code> is the name of the handled key (for\n      example <code>\"Ctrl-X\"</code> or <code>\"'q'\"</code>),\n      and <code>event</code> is the DOM <code>keydown</code>\n      or <code>keypress</code> event.</dd>\n\n      <dt id=\"event_inputRead\"><code><strong>\"inputRead\"</strong> (instance: CodeMirror, changeObj: object)</code></dt>\n      <dd>Fired whenever new input is read from the hidden textarea\n      (typed or pasted by the user).</dd>\n\n      <dt id=\"event_electricInput\"><code><strong>\"electricInput\"</strong> (instance: CodeMirror, line: integer)</code></dt>\n      <dd>Fired if text input matched the\n      mode's <a href=\"#option_electricChars\">electric</a> patterns,\n      and this caused the line's indentation to change.</dd>\n\n      <dt id=\"event_beforeSelectionChange\"><code><strong>\"beforeSelectionChange\"</strong> (instance: CodeMirror, obj: {ranges, origin, update})</code></dt>\n      <dd>This event is fired before the selection is moved. Its\n      handler may inspect the set of selection ranges, present as an\n      array of <code>{anchor, head}</code> objects in\n      the <code>ranges</code> property of the <code>obj</code>\n      argument, and optionally change them by calling\n      the <code>update</code> method on this object, passing an array\n      of ranges in the same format. The object also contains\n      an <code>origin</code> property holding the origin string passed\n      to the selection-changing method, if any. Handlers for this\n      event have the same restriction\n      as <a href=\"#event_beforeChange\"><code>\"beforeChange\"</code></a>\n      handlers — they should not do anything to directly update the\n      state of the editor.</dd>\n\n      <dt id=\"event_viewportChange\"><code><strong>\"viewportChange\"</strong> (instance: CodeMirror, from: number, to: number)</code></dt>\n      <dd>Fires whenever the <a href=\"#getViewport\">view port</a> of\n      the editor changes (due to scrolling, editing, or any other\n      factor). The <code>from</code> and <code>to</code> arguments\n      give the new start and end of the viewport.</dd>\n\n      <dt id=\"event_swapDoc\"><code><strong>\"swapDoc\"</strong> (instance: CodeMirror, oldDoc: Doc)</code></dt>\n      <dd>This is signalled when the editor's document is replaced\n      using the <a href=\"#swapDoc\"><code>swapDoc</code></a>\n      method.</dd>\n\n      <dt id=\"event_gutterClick\"><code><strong>\"gutterClick\"</strong> (instance: CodeMirror, line: integer, gutter: string, clickEvent: Event)</code></dt>\n      <dd>Fires when the editor gutter (the line-number area) is\n      clicked. Will pass the editor instance as first argument, the\n      (zero-based) number of the line that was clicked as second\n      argument, the CSS class of the gutter that was clicked as third\n      argument, and the raw <code>mousedown</code> event object as\n      fourth argument.</dd>\n\n      <dt id=\"event_gutterContextMenu\"><code><strong>\"gutterContextMenu\"</strong> (instance: CodeMirror, line: integer, gutter: string, contextMenu: Event: Event)</code></dt>\n      <dd>Fires when the editor gutter (the line-number area)\n      receives a <code>contextmenu</code> event. Will pass the editor\n      instance as first argument, the (zero-based) number of the line\n      that was clicked as second argument, the CSS class of the\n      gutter that was clicked as third argument, and the raw\n      <code>contextmenu</code> mouse event object as fourth argument.\n      You can <code>preventDefault</code> the event, to signal that\n      CodeMirror should do no further handling.</dd>\n\n      <dt id=\"event_focus\"><code><strong>\"focus\"</strong> (instance: CodeMirror, event: Event)</code></dt>\n      <dd>Fires whenever the editor is focused.</dd>\n\n      <dt id=\"event_blur\"><code><strong>\"blur\"</strong> (instance: CodeMirror, event: Event)</code></dt>\n      <dd>Fires whenever the editor is unfocused.</dd>\n\n      <dt id=\"event_scroll\"><code><strong>\"scroll\"</strong> (instance: CodeMirror)</code></dt>\n      <dd>Fires when the editor is scrolled.</dd>\n\n      <dt id=\"event_refresh\"><code><strong>\"refresh\"</strong> (instance: CodeMirror)</code></dt>\n      <dd>Fires when the editor is <a href=\"#refresh\">refreshed</a>\n      or <a href=\"#setSize\">resized</a>. Mostly useful to invalidate\n      cached values that depend on the editor or character size.</dd>\n\n      <dt id=\"event_optionChange\"><code><strong>\"optionChange\"</strong> (instance: CodeMirror, option: string)</code></dt>\n      <dd>Dispatched every time an option is changed with <a href=\"#setOption\"><code>setOption</code></a>.</dd>\n\n      <dt id=\"event_scrollCursorIntoView\"><code><strong>\"scrollCursorIntoView\"</strong> (instance: CodeMirror, event: Event)</code></dt>\n      <dd>Fires when the editor tries to scroll its cursor into view.\n      Can be hooked into to take care of additional scrollable\n      containers around the editor. When the event object has\n      its <code>preventDefault</code> method called, CodeMirror will\n      not itself try to scroll the window.</dd>\n\n      <dt id=\"event_update\"><code><strong>\"update\"</strong> (instance: CodeMirror)</code></dt>\n      <dd>Will be fired whenever CodeMirror updates its DOM display.</dd>\n\n      <dt id=\"event_renderLine\"><code><strong>\"renderLine\"</strong> (instance: CodeMirror, line: LineHandle, element: Element)</code></dt>\n      <dd>Fired whenever a line is (re-)rendered to the DOM. Fired\n      right after the DOM element is built, <em>before</em> it is\n      added to the document. The handler may mess with the style of\n      the resulting element, or add event handlers, but\n      should <em>not</em> try to change the state of the editor.</dd>\n\n      <dt id=\"event_dom\"><code><strong>\"mousedown\"</strong>,\n      <strong>\"dblclick\"</strong>, <strong>\"touchstart\"</strong>, <strong>\"contextmenu\"</strong>,\n      <strong>\"keydown\"</strong>, <strong>\"keypress\"</strong>,\n      <strong>\"keyup\"</strong>, <strong>\"cut\"</strong>, <strong>\"copy\"</strong>, <strong>\"paste\"</strong>,\n      <strong>\"dragstart\"</strong>, <strong>\"dragenter\"</strong>,\n      <strong>\"dragover\"</strong>, <strong>\"dragleave\"</strong>,\n      <strong>\"drop\"</strong>\n      (instance: CodeMirror, event: Event)</code></dt>\n      <dd>Fired when CodeMirror is handling a DOM event of this type.\n      You can <code>preventDefault</code> the event, or give it a\n      truthy <code>codemirrorIgnore</code> property, to signal that\n      CodeMirror should do no further handling.</dd>\n    </dl>\n\n    <p>Document objects (instances\n    of <a href=\"#Doc\"><code>CodeMirror.Doc</code></a>) emit the\n    following events:</p>\n\n    <dl>\n      <dt id=\"event_doc_change\"><code><strong>\"change\"</strong> (doc: CodeMirror.Doc, changeObj: object)</code></dt>\n      <dd>Fired whenever a change occurs to the\n      document. <code>changeObj</code> has a similar type as the\n      object passed to the\n      editor's <a href=\"#event_change\"><code>\"change\"</code></a>\n      event.</dd>\n\n      <dt id=\"event_doc_beforeChange\"><code><strong>\"beforeChange\"</strong> (doc: CodeMirror.Doc, change: object)</code></dt>\n      <dd>See the <a href=\"#event_beforeChange\">description of the\n      same event</a> on editor instances.</dd>\n\n      <dt id=\"event_doc_cursorActivity\"><code><strong>\"cursorActivity\"</strong> (doc: CodeMirror.Doc)</code></dt>\n      <dd>Fired whenever the cursor or selection in this document\n      changes.</dd>\n\n      <dt id=\"event_doc_beforeSelectionChange\"><code><strong>\"beforeSelectionChange\"</strong> (doc: CodeMirror.Doc, selection: {head, anchor})</code></dt>\n      <dd>Equivalent to\n      the <a href=\"#event_beforeSelectionChange\">event by the same\n      name</a> as fired on editor instances.</dd>\n    </dl>\n\n    <p>Line handles (as returned by, for\n    example, <a href=\"#getLineHandle\"><code>getLineHandle</code></a>)\n    support these events:</p>\n\n    <dl>\n      <dt id=\"event_delete\"><code><strong>\"delete\"</strong> ()</code></dt>\n      <dd>Will be fired when the line object is deleted. A line object\n      is associated with the <em>start</em> of the line. Mostly useful\n      when you need to find out when your <a href=\"#setGutterMarker\">gutter\n      markers</a> on a given line are removed.</dd>\n      <dt id=\"event_line_change\"><code><strong>\"change\"</strong> (line: LineHandle, changeObj: object)</code></dt>\n      <dd>Fires when the line's text content is changed in any way\n      (but the line is not deleted outright). The <code>change</code>\n      object is similar to the one passed\n      to <a href=\"#event_change\">change event</a> on the editor\n      object.</dd>\n    </dl>\n\n    <p>Marked range handles (<code>CodeMirror.TextMarker</code>), as returned\n    by <a href=\"#markText\"><code>markText</code></a>\n    and <a href=\"#setBookmark\"><code>setBookmark</code></a>, emit the\n    following events:</p>\n\n    <dl>\n      <dt id=\"event_beforeCursorEnter\"><code><strong>\"beforeCursorEnter\"</strong> ()</code></dt>\n      <dd>Fired when the cursor enters the marked range. From this\n      event handler, the editor state may be inspected\n      but <em>not</em> modified, with the exception that the range on\n      which the event fires may be cleared.</dd>\n      <dt id=\"event_clear\"><code><strong>\"clear\"</strong> (from: {line, ch}, to: {line, ch})</code></dt>\n      <dd>Fired when the range is cleared, either through cursor\n      movement in combination\n      with <a href=\"#mark_clearOnEnter\"><code>clearOnEnter</code></a>\n      or through a call to its <code>clear()</code> method. Will only\n      be fired once per handle. Note that deleting the range through\n      text editing does not fire this event, because an undo action\n      might bring the range back into existence. <code>from</code>\n      and <code>to</code> give the part of the document that the range\n      spanned when it was cleared.</dd>\n      <dt id=\"event_hide\"><code><strong>\"hide\"</strong> ()</code></dt>\n      <dd>Fired when the last part of the marker is removed from the\n      document by editing operations.</dd>\n      <dt id=\"event_unhide\"><code><strong>\"unhide\"</strong> ()</code></dt>\n      <dd>Fired when, after the marker was removed by editing, a undo\n      operation brought the marker back.</dd>\n    </dl>\n\n    <p>Line widgets (<code>CodeMirror.LineWidget</code>), returned\n    by <a href=\"#addLineWidget\"><code>addLineWidget</code></a>, fire\n    these events:</p>\n\n    <dl>\n      <dt id=\"event_redraw\"><code><strong>\"redraw\"</strong> ()</code></dt>\n      <dd>Fired whenever the editor re-adds the widget to the DOM.\n      This will happen once right after the widget is added (if it is\n      scrolled into view), and then again whenever it is scrolled out\n      of view and back in again, or when changes to the editor options\n      or the line the widget is on require the widget to be\n      redrawn.</dd>\n    </dl>\n</section>\n\n<section id=keymaps>\n    <h2>Key Maps</h2>\n\n    <p>Key maps are ways to associate keys and mouse buttons with\n    functionality. A key map is an object mapping strings that\n    identify the buttons to functions that implement their\n    functionality.</p>\n\n    <p>The CodeMirror distributions comes\n    with <a href=\"../demo/emacs.html\">Emacs</a>, <a href=\"../demo/vim.html\">Vim</a>,\n    and <a href=\"../demo/sublime.html\">Sublime Text</a>-style keymaps.</p>\n\n    <p>Keys are identified either by name or by character.\n    The <code>CodeMirror.keyNames</code> object defines names for\n    common keys and associates them with their key codes. Examples of\n    names defined here are <code>Enter</code>, <code>F5</code>,\n    and <code>Q</code>. These can be prefixed\n    with <code>Shift-</code>, <code>Cmd-</code>, <code>Ctrl-</code>,\n    and <code>Alt-</code> to specify a modifier. So for\n    example, <code>Shift-Ctrl-Space</code> would be a valid key\n    identifier.</p>\n\n    <p>Common example: map the Tab key to insert spaces instead of a tab\n    character.</p>\n\n    <pre data-lang=\"javascript\">\neditor.setOption(\"extraKeys\", {\n  Tab: function(cm) {\n    var spaces = Array(cm.getOption(\"indentUnit\") + 1).join(\" \");\n    cm.replaceSelection(spaces);\n  }\n});</pre>\n\n    <p>Alternatively, a character can be specified directly by\n    surrounding it in single quotes, for example <code>'$'</code>\n    or <code>'q'</code>. Due to limitations in the way browsers fire\n    key events, these may not be prefixed with modifiers.</p>\n\n    <p>To bind mouse buttons, use the names `LeftClick`,\n    `MiddleClick`, and `RightClick`. These can also be prefixed with\n    modifiers, and in addition, the word `Double` or `Triple` can be\n    put before `Click` (as in `LeftDoubleClick`) to bind a double- or\n    triple-click. The function for such a binding is passed the\n    position that was clicked as second argument.</p>\n\n    <p id=\"normalizeKeyMap\">Multi-stroke key bindings can be specified\n    by separating the key names by spaces in the property name, for\n    example <code>Ctrl-X Ctrl-V</code>. When a map contains\n    multi-stoke bindings or keys with modifiers that are not specified\n    in the default order (<code>Shift-Cmd-Ctrl-Alt</code>), you must\n    call <code>CodeMirror.normalizeKeyMap</code> on it before it can\n    be used. This function takes a keymap and modifies it to normalize\n    modifier order and properly recognize multi-stroke bindings. It\n    will return the keymap itself.</p>\n\n    <p>The <code>CodeMirror.keyMap</code> object associates key maps\n    with names. User code and key map definitions can assign extra\n    properties to this object. Anywhere where a key map is expected, a\n    string can be given, which will be looked up in this object. It\n    also contains the <code>\"default\"</code> key map holding the\n    default bindings.</p>\n\n    <p>The values of properties in key maps can be either functions of\n    a single argument (the CodeMirror instance), strings, or\n    <code>false</code>. Strings refer\n    to <a href=\"#commands\">commands</a>, which are described below. If\n    the property is set to <code>false</code>, CodeMirror leaves\n    handling of the key up to the browser. A key handler function may\n    return <code>CodeMirror.Pass</code> to indicate that it has\n    decided not to handle the key, and other handlers (or the default\n    behavior) should be given a turn.</p>\n\n    <p>Keys mapped to command names that start with the\n    characters <code>\"go\"</code> or to functions that have a\n    truthy <code>motion</code> property (which should be used for\n    cursor-movement actions) will be fired even when an\n    extra <code>Shift</code> modifier is present (i.e. <code>\"Up\":\n    \"goLineUp\"</code> matches both up and shift-up). This is used to\n    easily implement shift-selection.</p>\n\n    <p>Key maps can defer to each other by defining\n    a <code>fallthrough</code> property. This indicates that when a\n    key is not found in the map itself, one or more other maps should\n    be searched. It can hold either a single key map or an array of\n    key maps.</p>\n\n    <p>When a key map needs to set something up when it becomes\n    active, or tear something down when deactivated, it can\n    contain <code>attach</code> and/or <code>detach</code> properties,\n    which should hold functions that take the editor instance and the\n    next or previous keymap. Note that this only works for the\n    <a href=\"#option_keyMap\">top-level keymap</a>, not for fallthrough\n    maps or maps added\n    with <a href=\"#option_extraKeys\"><code>extraKeys</code></a>\n    or <a href=\"#addKeyMap\"><code>addKeyMap</code></a>.</p>\n</section>\n\n<section id=commands>\n    <h2>Commands</h2>\n\n    <p>Commands are parameter-less actions that can be performed on an\n    editor. Their main use is for key bindings. Commands are defined by\n    adding properties to the <code>CodeMirror.commands</code> object.\n    A number of common commands are defined by the library itself,\n    most of them used by the default key bindings. The value of a\n    command property must be a function of one argument (an editor\n    instance).</p>\n\n    <p>Some of the commands below are referenced in the default\n    key map, but not defined by the core library. These are intended to\n    be defined by user code or addons.</p>\n\n    <p>Commands can also be run with\n    the <a href=\"#execCommand\"><code>execCommand</code></a>\n    method.</p>\n\n    <dl>\n      <dt class=command id=command_selectAll><code><strong>selectAll</strong></code><span class=keybinding>Ctrl-A (PC), Cmd-A (Mac)</span></dt>\n      <dd>Select the whole content of the editor.</dd>\n\n      <dt class=command id=command_singleSelection><code><strong>singleSelection</strong></code><span class=keybinding>Esc</span></dt>\n      <dd>When multiple selections are present, this deselects all but\n      the primary selection.</dd>\n\n      <dt class=command id=command_killLine><code><strong>killLine</strong></code><span class=keybinding>Ctrl-K (Mac)</span></dt>\n      <dd>Emacs-style line killing. Deletes the part of the line after\n      the cursor. If that consists only of whitespace, the newline at\n      the end of the line is also deleted.</dd>\n\n      <dt class=command id=command_deleteLine><code><strong>deleteLine</strong></code><span class=keybinding>Ctrl-D (PC), Cmd-D (Mac)</span></dt>\n      <dd>Deletes the whole line under the cursor, including newline at the end.</dd>\n\n      <dt class=command id=command_delLineLeft><code><strong>delLineLeft</strong></code></dt>\n      <dd>Delete the part of the line before the cursor.</dd>\n\n      <dt class=command id=command_delWrappedLineLeft><code><strong>delWrappedLineLeft</strong></code><span class=keybinding>Cmd-Backspace (Mac)</span></dt>\n      <dd>Delete the part of the line from the left side of the visual line the cursor is on to the cursor.</dd>\n\n      <dt class=command id=command_delWrappedLineRight><code><strong>delWrappedLineRight</strong></code><span class=keybinding>Cmd-Delete (Mac)</span></dt>\n      <dd>Delete the part of the line from the cursor to the right side of the visual line the cursor is on.</dd>\n\n      <dt class=command id=command_undo><code><strong>undo</strong></code><span class=keybinding>Ctrl-Z (PC), Cmd-Z (Mac)</span></dt>\n      <dd>Undo the last change. Note that, because browsers still\n      don't make it possible for scripts to react to or customize the\n      context menu, selecting undo (or redo) from the context menu in\n      a CodeMirror instance does not work.</dd>\n\n      <dt class=command id=command_redo><code><strong>redo</strong></code><span class=keybinding>Ctrl-Y (PC), Shift-Cmd-Z (Mac), Cmd-Y (Mac)</span></dt>\n      <dd>Redo the last undone change.</dd>\n\n      <dt class=command id=command_undoSelection><code><strong>undoSelection</strong></code><span class=keybinding>Ctrl-U (PC), Cmd-U (Mac)</span></dt>\n      <dd>Undo the last change to the selection, or if there are no\n      selection-only changes at the top of the history, undo the last\n      change.</dd>\n\n      <dt class=command id=command_redoSelection><code><strong>redoSelection</strong></code><span class=keybinding>Alt-U (PC), Shift-Cmd-U (Mac)</span></dt>\n      <dd>Redo the last change to the selection, or the last text change if\n      no selection changes remain.</dd>\n\n      <dt class=command id=command_goDocStart><code><strong>goDocStart</strong></code><span class=keybinding>Ctrl-Home (PC), Cmd-Up (Mac), Cmd-Home (Mac)</span></dt>\n      <dd>Move the cursor to the start of the document.</dd>\n\n      <dt class=command id=command_goDocEnd><code><strong>goDocEnd</strong></code><span class=keybinding>Ctrl-End (PC), Cmd-End (Mac), Cmd-Down (Mac)</span></dt>\n      <dd>Move the cursor to the end of the document.</dd>\n\n      <dt class=command id=command_goLineStart><code><strong>goLineStart</strong></code><span class=keybinding>Alt-Left (PC), Ctrl-A (Mac)</span></dt>\n      <dd>Move the cursor to the start of the line.</dd>\n\n      <dt class=command id=command_goLineStartSmart><code><strong>goLineStartSmart</strong></code><span class=keybinding>Home</span></dt>\n      <dd>Move to the start of the text on the line, or if we are\n      already there, to the actual start of the line (including\n      whitespace).</dd>\n\n      <dt class=command id=command_goLineEnd><code><strong>goLineEnd</strong></code><span class=keybinding>Alt-Right (PC), Ctrl-E (Mac)</span></dt>\n      <dd>Move the cursor to the end of the line.</dd>\n\n      <dt class=command id=command_goLineRight><code><strong>goLineRight</strong></code><span class=keybinding>Cmd-Right (Mac)</span></dt>\n      <dd>Move the cursor to the right side of the visual line it is on.</dd>\n\n      <dt class=command id=command_goLineLeft><code><strong>goLineLeft</strong></code><span class=keybinding>Cmd-Left (Mac)</span></dt>\n      <dd>Move the cursor to the left side of the visual line it is on. If\n      this line is wrapped, that may not be the start of the line.</dd>\n\n      <dt class=command id=command_goLineLeftSmart><code><strong>goLineLeftSmart</strong></code></dt>\n      <dd>Move the cursor to the left side of the visual line it is\n      on. If that takes it to the start of the line, behave\n      like <a href=\"#command_goLineStartSmart\"><code>goLineStartSmart</code></a>.</dd>\n\n      <dt class=command id=command_goLineUp><code><strong>goLineUp</strong></code><span class=keybinding>Up, Ctrl-P (Mac)</span></dt>\n      <dd>Move the cursor up one line.</dd>\n\n      <dt class=command id=command_goLineDown><code><strong>goLineDown</strong></code><span class=keybinding>Down, Ctrl-N (Mac)</span></dt>\n      <dd>Move down one line.</dd>\n\n      <dt class=command id=command_goPageUp><code><strong>goPageUp</strong></code><span class=keybinding>PageUp, Shift-Ctrl-V (Mac)</span></dt>\n      <dd>Move the cursor up one screen, and scroll up by the same distance.</dd>\n\n      <dt class=command id=command_goPageDown><code><strong>goPageDown</strong></code><span class=keybinding>PageDown, Ctrl-V (Mac)</span></dt>\n      <dd>Move the cursor down one screen, and scroll down by the same distance.</dd>\n\n      <dt class=command id=command_goCharLeft><code><strong>goCharLeft</strong></code><span class=keybinding>Left, Ctrl-B (Mac)</span></dt>\n      <dd>Move the cursor one character left, going to the previous line\n      when hitting the start of line.</dd>\n\n      <dt class=command id=command_goCharRight><code><strong>goCharRight</strong></code><span class=keybinding>Right, Ctrl-F (Mac)</span></dt>\n      <dd>Move the cursor one character right, going to the next line\n      when hitting the end of line.</dd>\n\n      <dt class=command id=command_goColumnLeft><code><strong>goColumnLeft</strong></code></dt>\n      <dd>Move the cursor one character left, but don't cross line boundaries.</dd>\n\n      <dt class=command id=command_goColumnRight><code><strong>goColumnRight</strong></code></dt>\n      <dd>Move the cursor one character right, don't cross line boundaries.</dd>\n\n      <dt class=command id=command_goWordLeft><code><strong>goWordLeft</strong></code><span class=keybinding>Alt-B (Mac)</span></dt>\n      <dd>Move the cursor to the start of the previous word.</dd>\n\n      <dt class=command id=command_goWordRight><code><strong>goWordRight</strong></code><span class=keybinding>Alt-F (Mac)</span></dt>\n      <dd>Move the cursor to the end of the next word.</dd>\n\n      <dt class=command id=command_goGroupLeft><code><strong>goGroupLeft</strong></code><span class=keybinding>Ctrl-Left (PC), Alt-Left (Mac)</span></dt>\n      <dd>Move to the left of the group before the cursor. A group is\n      a stretch of word characters, a stretch of punctuation\n      characters, a newline, or a stretch of <em>more than one</em>\n      whitespace character.</dd>\n\n      <dt class=command id=command_goGroupRight><code><strong>goGroupRight</strong></code><span class=keybinding>Ctrl-Right (PC), Alt-Right (Mac)</span></dt>\n      <dd>Move to the right of the group after the cursor\n      (see <a href=\"#command_goGroupLeft\">above</a>).</dd>\n\n      <dt class=command id=command_delCharBefore><code><strong>delCharBefore</strong></code><span class=keybinding>Shift-Backspace, Ctrl-H (Mac)</span></dt>\n      <dd>Delete the character before the cursor.</dd>\n\n      <dt class=command id=command_delCharAfter><code><strong>delCharAfter</strong></code><span class=keybinding>Delete, Ctrl-D (Mac)</span></dt>\n      <dd>Delete the character after the cursor.</dd>\n\n      <dt class=command id=command_delWordBefore><code><strong>delWordBefore</strong></code><span class=keybinding>Alt-Backspace (Mac)</span></dt>\n      <dd>Delete up to the start of the word before the cursor.</dd>\n\n      <dt class=command id=command_delWordAfter><code><strong>delWordAfter</strong></code><span class=keybinding>Alt-D (Mac)</span></dt>\n      <dd>Delete up to the end of the word after the cursor.</dd>\n\n      <dt class=command id=command_delGroupBefore><code><strong>delGroupBefore</strong></code><span class=keybinding>Ctrl-Backspace (PC), Alt-Backspace (Mac)</span></dt>\n      <dd>Delete to the left of the <a href=\"#command_goGroupLeft\">group</a> before the cursor.</dd>\n\n      <dt class=command id=command_delGroupAfter><code><strong>delGroupAfter</strong></code><span class=keybinding>Ctrl-Delete (PC), Ctrl-Alt-Backspace (Mac), Alt-Delete (Mac)</span></dt>\n      <dd>Delete to the start of the <a href=\"#command_goGroupLeft\">group</a> after the cursor.</dd>\n\n      <dt class=command id=command_indentAuto><code><strong>indentAuto</strong></code><span class=keybinding>Shift-Tab</span></dt>\n      <dd>Auto-indent the current line or selection.</dd>\n\n      <dt class=command id=command_indentMore><code><strong>indentMore</strong></code><span class=keybinding>Ctrl-] (PC), Cmd-] (Mac)</span></dt>\n      <dd>Indent the current line or selection by one <a href=\"#option_indentUnit\">indent unit</a>.</dd>\n\n      <dt class=command id=command_indentLess><code><strong>indentLess</strong></code><span class=keybinding>Ctrl-[ (PC), Cmd-[ (Mac)</span></dt>\n      <dd>Dedent the current line or selection by one <a href=\"#option_indentUnit\">indent unit</a>.</dd>\n\n      <dt class=command id=command_insertTab><code><strong>insertTab</strong></code></dt>\n      <dd>Insert a tab character at the cursor.</dd>\n\n      <dt class=command id=command_insertSoftTab><code><strong>insertSoftTab</strong></code></dt>\n      <dd>Insert the amount of spaces that match the width a tab at\n      the cursor position would have.</dd>\n\n      <dt class=command id=command_defaultTab><code><strong>defaultTab</strong></code><span class=keybinding>Tab</span></dt>\n      <dd>If something is selected, indent it by\n      one <a href=\"#option_indentUnit\">indent unit</a>. If nothing is\n      selected, insert a tab character.</dd>\n\n      <dt class=command id=command_transposeChars><code><strong>transposeChars</strong></code><span class=keybinding>Ctrl-T (Mac)</span></dt>\n      <dd>Swap the characters before and after the cursor.</dd>\n\n      <dt class=command id=command_newlineAndIndent><code><strong>newlineAndIndent</strong></code><span class=keybinding>Enter</span></dt>\n      <dd>Insert a newline and auto-indent the new line.</dd>\n\n      <dt class=command id=command_toggleOverwrite><code><strong>toggleOverwrite</strong></code><span class=keybinding>Insert</span></dt>\n      <dd>Flip the <a href=\"#toggleOverwrite\">overwrite</a> flag.</dd>\n\n      <dt class=command id=command_save><code><strong>save</strong></code><span class=keybinding>Ctrl-S (PC), Cmd-S (Mac)</span></dt>\n      <dd>Not defined by the core library, only referred to in\n      key maps. Intended to provide an easy way for user code to define\n      a save command.</dd>\n\n      <dt class=command id=command_find><code><strong>find</strong></code><span class=keybinding>Ctrl-F (PC), Cmd-F (Mac)</span></dt>\n      <dt class=command id=command_findNext><code><strong>findNext</strong></code><span class=keybinding>Ctrl-G (PC), Cmd-G (Mac)</span></dt>\n      <dt class=command id=command_findPrev><code><strong>findPrev</strong></code><span class=keybinding>Shift-Ctrl-G (PC), Shift-Cmd-G (Mac)</span></dt>\n      <dt class=command id=command_replace><code><strong>replace</strong></code><span class=keybinding>Shift-Ctrl-F (PC), Cmd-Alt-F (Mac)</span></dt>\n      <dt class=command id=command_replaceAll><code><strong>replaceAll</strong></code><span class=keybinding>Shift-Ctrl-R (PC), Shift-Cmd-Alt-F (Mac)</span></dt>\n      <dd>Not defined by the core library, but defined in\n      the <a href=\"#addon_search\">search addon</a> (or custom client\n      addons).</dd>\n\n    </dl>\n\n</section>\n\n<section id=styling>\n    <h2>Customized Styling</h2>\n\n    <p>Up to a certain extent, CodeMirror's look can be changed by\n    modifying style sheet files. The style sheets supplied by modes\n    simply provide the colors for that mode, and can be adapted in a\n    very straightforward way. To style the editor itself, it is\n    possible to alter or override the styles defined\n    in <a href=\"../lib/codemirror.css\"><code>codemirror.css</code></a>.</p>\n\n    <p>Some care must be taken there, since a lot of the rules in this\n    file are necessary to have CodeMirror function properly. Adjusting\n    colors should be safe, of course, and with some care a lot of\n    other things can be changed as well. The CSS classes defined in\n    this file serve the following roles:</p>\n\n    <dl>\n      <dt id=\"class_CodeMirror\"><code><strong>CodeMirror</strong></code></dt>\n      <dd>The outer element of the editor. This should be used for the\n      editor width, height, borders and positioning. Can also be used\n      to set styles that should hold for everything inside the editor\n      (such as font and font size), or to set a background. Setting\n      this class' <code>height</code> style to <code>auto</code> will\n      make the editor <a href=\"../demo/resize.html\">resize to fit its\n      content</a> (it is recommended to also set\n      the <a href=\"#option_viewportMargin\"><code>viewportMargin</code>\n      option</a> to <code>Infinity</code> when doing this.</dd>\n\n      <dt id=\"class_CodeMirror_focused\"><code><strong>CodeMirror-focused</strong></code></dt>\n      <dd>Whenever the editor is focused, the top element gets this\n      class. This is used to hide the cursor and give the selection a\n      different color when the editor is not focused.</dd>\n\n      <dt id=\"class_CodeMirror_gutters\"><code><strong>CodeMirror-gutters</strong></code></dt>\n      <dd>This is the backdrop for all gutters. Use it to set the\n      default gutter background color, and optionally add a border on\n      the right of the gutters.</dd>\n\n      <dt id=\"class_CodeMirror_linenumbers\"><code><strong>CodeMirror-linenumbers</strong></code></dt>\n      <dd>Use this for giving a background or width to the line number\n      gutter.</dd>\n\n      <dt id=\"class_CodeMirror_linenumber\"><code><strong>CodeMirror-linenumber</strong></code></dt>\n      <dd>Used to style the actual individual line numbers. These\n      won't be children of the <code>CodeMirror-linenumbers</code>\n      (plural) element, but rather will be absolutely positioned to\n      overlay it. Use this to set alignment and text properties for\n      the line numbers.</dd>\n\n      <dt id=\"class_CodeMirror_lines\"><code><strong>CodeMirror-lines</strong></code></dt>\n      <dd>The visible lines. This is where you specify vertical\n      padding for the editor content.</dd>\n\n      <dt id=\"class_CodeMirror_cursor\"><code><strong>CodeMirror-cursor</strong></code></dt>\n      <dd>The cursor is a block element that is absolutely positioned.\n      You can make it look whichever way you want.</dd>\n\n      <dt id=\"class_CodeMirror_selected\"><code><strong>CodeMirror-selected</strong></code></dt>\n      <dd>The selection is represented by <code>span</code> elements\n      with this class.</dd>\n\n      <dt id=\"class_CodeMirror_matchingbracket\"><code><strong>CodeMirror-matchingbracket</strong></code>,\n        <code><strong>CodeMirror-nonmatchingbracket</strong></code></dt>\n      <dd>These are used to style matched (or unmatched) brackets.</dd>\n    </dl>\n\n    <p>If your page's style sheets do funky things to\n    all <code>div</code> or <code>pre</code> elements (you probably\n    shouldn't do that), you'll have to define rules to cancel these\n    effects out again for elements under the <code>CodeMirror</code>\n    class.</p>\n\n    <p>Themes are also simply CSS files, which define colors for\n    various syntactic elements. See the files in\n    the <a href=\"../theme/\"><code>theme</code></a> directory.</p>\n</section>\n\n<section id=api>\n    <h2>Programming API</h2>\n\n    <p>A lot of CodeMirror features are only available through its\n    API. Thus, you need to write code (or\n    use <a href=\"#addons\">addons</a>) if you want to expose them to\n    your users.</p>\n\n    <p>Whenever points in the document are represented, the API uses\n    objects with <code>line</code> and <code>ch</code> properties.\n    Both are zero-based. CodeMirror makes sure to 'clip' any positions\n    passed by client code so that they fit inside the document, so you\n    shouldn't worry too much about sanitizing your coordinates. If you\n    give <code>ch</code> a value of <code>null</code>, or don't\n    specify it, it will be replaced with the length of the specified\n    line. Such positions may also have a <code>sticky</code> property\n    holding <code>\"before\"</code> or <code>\"after\"</code>, whether the\n    position is associated with the character before or after it. This\n    influences, for example, where the cursor is drawn on a\n    line-break or bidi-direction boundary.</p>\n\n    <p>Methods prefixed with <code>doc.</code> can, unless otherwise\n    specified, be called both on <code>CodeMirror</code> (editor)\n    instances and <code>CodeMirror.Doc</code> instances. Methods\n    prefixed with <code>cm.</code> are <em>only</em> available\n    on <code>CodeMirror</code> instances.</p>\n\n    <h3 id=\"api_constructor\">Constructor</h3>\n\n    <p id=\"CodeMirror\">Constructing an editor instance is done with\n    the <code><strong>CodeMirror</strong>(place: Element|fn(Element),\n    ?option: object)</code> constructor. If the <code>place</code>\n    argument is a DOM element, the editor will be appended to it. If\n    it is a function, it will be called, and is expected to place the\n    editor into the document. <code>options</code> may be an element\n    mapping <a href=\"#config\">option names</a> to values. The options\n    that it doesn't explicitly specify (or all options, if it is not\n    passed) will be taken\n    from <a href=\"#defaults\"><code>CodeMirror.defaults</code></a>.</p>\n\n    <p>Note that the options object passed to the constructor will be\n    mutated when the instance's options\n    are <a href=\"#setOption\">changed</a>, so you shouldn't share such\n    objects between instances.</p>\n\n    <p>See <a href=\"#fromTextArea\"><code>CodeMirror.fromTextArea</code></a>\n    for another way to construct an editor instance.</p>\n\n    <h3 id=\"api_content\">Content manipulation methods</h3>\n\n    <dl>\n      <dt id=\"getValue\"><code><strong>doc.getValue</strong>(?separator: string) → string</code></dt>\n      <dd>Get the current editor content. You can pass it an optional\n      argument to specify the string to be used to separate lines\n      (defaults to <code>\"\\n\"</code>).</dd>\n      <dt id=\"setValue\"><code><strong>doc.setValue</strong>(content: string)</code></dt>\n      <dd>Set the editor content.</dd>\n\n      <dt id=\"getRange\"><code><strong>doc.getRange</strong>(from: {line, ch}, to: {line, ch}, ?separator: string) → string</code></dt>\n      <dd>Get the text between the given points in the editor, which\n      should be <code>{line, ch}</code> objects. An optional third\n      argument can be given to indicate the line separator string to\n      use (defaults to <code>\"\\n\"</code>).</dd>\n      <dt id=\"replaceRange\"><code><strong>doc.replaceRange</strong>(replacement: string, from: {line, ch}, to: {line, ch}, ?origin: string)</code></dt>\n      <dd>Replace the part of the document between <code>from</code>\n      and <code>to</code> with the given string. <code>from</code>\n      and <code>to</code> must be <code>{line, ch}</code>\n      objects. <code>to</code> can be left off to simply insert the\n      string at position <code>from</code>. When <code>origin</code>\n      is given, it will be passed on\n      to <a href=\"#event_change\"><code>\"change\"</code> events</a>, and\n      its first letter will be used to determine whether this change\n      can be merged with previous history events, in the way described\n      for <a href=\"#selection_origin\">selection origins</a>.</dd>\n\n      <dt id=\"getLine\"><code><strong>doc.getLine</strong>(n: integer) → string</code></dt>\n      <dd>Get the content of line <code>n</code>.</dd>\n\n      <dt id=\"lineCount\"><code><strong>doc.lineCount</strong>() → integer</code></dt>\n      <dd>Get the number of lines in the editor.</dd>\n      <dt id=\"firstLine\"><code><strong>doc.firstLine</strong>() → integer</code></dt>\n      <dd>Get the number of first line in the editor. This will\n      usually be zero but for <a href=\"#linkedDoc_from\">linked sub-views</a>,\n      or <a href=\"#api_doc\">documents</a> instantiated with a non-zero\n      first line, it might return other values.</dd>\n      <dt id=\"lastLine\"><code><strong>doc.lastLine</strong>() → integer</code></dt>\n      <dd>Get the number of last line in the editor. This will\n      usually be <code>doc.lineCount() - 1</code>,\n      but for <a href=\"#linkedDoc_from\">linked sub-views</a>,\n      it might return other values.</dd>\n\n      <dt id=\"getLineHandle\"><code><strong>doc.getLineHandle</strong>(num: integer) → LineHandle</code></dt>\n      <dd>Fetches the line handle for the given line number.</dd>\n      <dt id=\"getLineNumber\"><code><strong>doc.getLineNumber</strong>(handle: LineHandle) → integer</code></dt>\n      <dd>Given a line handle, returns the current position of that\n      line (or <code>null</code> when it is no longer in the\n      document).</dd>\n      <dt id=\"eachLine\"><code><strong>doc.eachLine</strong>(f: (line: LineHandle))</code></dt>\n      <dt><code><strong>doc.eachLine</strong>(start: integer, end: integer, f: (line: LineHandle))</code></dt>\n      <dd>Iterate over the whole document, or if <code>start</code>\n      and <code>end</code> line numbers are given, the range\n      from <code>start</code> up to (not including) <code>end</code>,\n      and call <code>f</code> for each line, passing the line handle.\n      <code>eachLine</code> stops iterating if <code>f</code> returns\n      truthy value.\n      This is a faster way to visit a range of line handlers than\n      calling <a href=\"#getLineHandle\"><code>getLineHandle</code></a>\n      for each of them. Note that line handles have\n      a <code>text</code> property containing the line's content (as a\n      string).</dd>\n\n      <dt id=\"markClean\"><code><strong>doc.markClean</strong>()</code></dt>\n      <dd>Set the editor content as 'clean', a flag that it will\n      retain until it is edited, and which will be set again when such\n      an edit is undone again. Useful to track whether the content\n      needs to be saved. This function is deprecated in favor\n      of <a href=\"#changeGeneration\"><code>changeGeneration</code></a>,\n      which allows multiple subsystems to track different notions of\n      cleanness without interfering.</dd>\n      <dt id=\"changeGeneration\"><code><strong>doc.changeGeneration</strong>(?closeEvent: boolean) → integer</code></dt>\n      <dd>Returns a number that can later be passed\n      to <a href=\"#isClean\"><code>isClean</code></a> to test whether\n      any edits were made (and not undone) in the meantime.\n      If <code>closeEvent</code> is true, the current history event\n      will be ‘closed’, meaning it can't be combined with further\n      changes (rapid typing or deleting events are typically\n      combined).</dd>\n      <dt id=\"isClean\"><code><strong>doc.isClean</strong>(?generation: integer) → boolean</code></dt>\n      <dd>Returns whether the document is currently clean — not\n      modified since initialization or the last call\n      to <a href=\"#markClean\"><code>markClean</code></a> if no\n      argument is passed, or since the matching call\n      to <a href=\"#changeGeneration\"><code>changeGeneration</code></a>\n      if a generation value is given.</dd>\n    </dl>\n\n    <h3 id=\"api_selection\">Cursor and selection methods</h3>\n\n    <dl>\n      <dt id=\"getSelection\"><code><strong>doc.getSelection</strong>(?lineSep: string) → string</code></dt>\n      <dd>Get the currently selected code. Optionally pass a line\n      separator to put between the lines in the output. When multiple\n      selections are present, they are concatenated with instances\n      of <code>lineSep</code> in between.</dd>\n      <dt id=\"getSelections\"><code><strong>doc.getSelections</strong>(?lineSep: string) → array&lt;string&gt;</code></dt>\n      <dd>Returns an array containing a string for each selection,\n      representing the content of the selections.</dd>\n\n      <dt id=\"replaceSelection\"><code><strong>doc.replaceSelection</strong>(replacement: string, ?select: string)</code></dt>\n      <dd>Replace the selection(s) with the given string. By default,\n      the new selection ends up after the inserted text. The\n      optional <code>select</code> argument can be used to change\n      this—passing <code>\"around\"</code> will cause the new text to be\n      selected, passing <code>\"start\"</code> will collapse the\n      selection to the start of the inserted text.</dd>\n      <dt id=\"replaceSelections\"><code><strong>doc.replaceSelections</strong>(replacements: array&lt;string&gt;, ?select: string)</code></dt>\n      <dd>The length of the given array should be the same as the\n      number of active selections. Replaces the content of the\n      selections with the strings in the array.\n      The <code>select</code> argument works the same as\n      in <a href=\"#replaceSelection\"><code>replaceSelection</code></a>.</dd>\n\n      <dt id=\"getCursor\"><code><strong>doc.getCursor</strong>(?start: string) → {line, ch}</code></dt>\n      <dd>Retrieve one end of the <em>primary</em>\n      selection. <code>start</code> is an optional string indicating\n      which end of the selection to return. It may\n      be <code>\"from\"</code>, <code>\"to\"</code>, <code>\"head\"</code>\n      (the side of the selection that moves when you press\n      shift+arrow), or <code>\"anchor\"</code> (the fixed side of the\n      selection). Omitting the argument is the same as\n      passing <code>\"head\"</code>. A <code>{line, ch}</code> object\n      will be returned.</dd>\n      <dt id=\"listSelections\"><code><strong>doc.listSelections</strong>() → array&lt;{anchor, head}&gt;</code></dt>\n      <dd>Retrieves a list of all current selections. These will\n      always be sorted, and never overlap (overlapping selections are\n      merged). Each object in the array contains <code>anchor</code>\n      and <code>head</code> properties referring to <code>{line,\n      ch}</code> objects.</dd>\n\n      <dt id=\"somethingSelected\"><code><strong>doc.somethingSelected</strong>() → boolean</code></dt>\n      <dd>Return true if any text is selected.</dd>\n      <dt id=\"setCursor\"><code><strong>doc.setCursor</strong>(pos: {line, ch}|number, ?ch: number, ?options: object)</code></dt>\n      <dd>Set the cursor position. You can either pass a\n      single <code>{line, ch}</code> object, or the line and the\n      character as two separate parameters. Will replace all\n      selections with a single, empty selection at the given position.\n      The supported options are the same as for <a href=\"#setSelection\"><code>setSelection</code></a>.</dd>\n\n      <dt id=\"setSelection\"><code><strong>doc.setSelection</strong>(anchor: {line, ch}, ?head: {line, ch}, ?options: object)</code></dt>\n      <dd>Set a single selection range. <code>anchor</code>\n      and <code>head</code> should be <code>{line, ch}</code>\n      objects. <code>head</code> defaults to <code>anchor</code> when\n      not given. These options are supported:\n      <dl>\n        <dt id=\"selection_scroll\"><code><strong>scroll</strong>: boolean</code></dt>\n        <dd>Determines whether the selection head should be scrolled\n        into view. Defaults to true.</dd>\n        <dt id=\"selection_origin\"><code><strong>origin</strong>: string</code></dt>\n        <dd>Determines whether the selection history event may be\n        merged with the previous one. When an origin starts with the\n        character <code>+</code>, and the last recorded selection had\n        the same origin and was similar (close\n        in <a href=\"#option_historyEventDelay\">time</a>, both\n        collapsed or both non-collapsed), the new one will replace the\n        old one. When it starts with <code>*</code>, it will always\n        replace the previous event (if that had the same origin).\n        Built-in motion uses the <code>\"+move\"</code> origin. User input uses the <code>\"+input\"</code> origin.</dd>\n        <dt id=\"selection_bias\"><code><strong>bias</strong>: number</code></dt>\n        <dd>Determine the direction into which the selection endpoints\n        should be adjusted when they fall inside\n        an <a href=\"#mark_atomic\">atomic</a> range. Can be either -1\n        (backward) or 1 (forward). When not given, the bias will be\n        based on the relative position of the old selection—the editor\n        will try to move further away from that, to prevent getting\n        stuck.</dd>\n      </dl></dd>\n\n      <dt id=\"setSelections\"><code><strong>doc.setSelections</strong>(ranges: array&lt;{anchor, ?head}&gt;, ?primary: integer, ?options: object)</code></dt>\n      <dd>Sets a new set of selections. There must be at least one\n      selection in the given array. When <code>primary</code> is a\n      number, it determines which selection is the primary one. When\n      it is not given, the primary index is taken from the previous\n      selection, or set to the last range if the previous selection\n      had less ranges than the new one. Supports the same options\n      as <a href=\"#setSelection\"><code>setSelection</code></a>.\n      <code>head</code> defaults to <code>anchor</code> when not given.</dd>\n      <dt id=\"addSelection\"><code><strong>doc.addSelection</strong>(anchor: {line, ch}, ?head: {line, ch})</code></dt>\n      <dd>Adds a new selection to the existing set of selections, and\n      makes it the primary selection.</dd>\n\n      <dt id=\"extendSelection\"><code><strong>doc.extendSelection</strong>(from: {line, ch}, ?to: {line, ch}, ?options: object)</code></dt>\n      <dd>Similar\n      to <a href=\"#setSelection\"><code>setSelection</code></a>, but\n      will, if shift is held or\n      the <a href=\"#setExtending\">extending</a> flag is set, move the\n      head of the selection while leaving the anchor at its current\n      place. <code>to</code> is optional, and can be passed to ensure\n      a region (for example a word or paragraph) will end up selected\n      (in addition to whatever lies between that region and the\n      current anchor). When multiple selections are present, all but\n      the primary selection will be dropped by this method.\n      Supports the same options as <a href=\"#setSelection\"><code>setSelection</code></a>.</dd>\n      <dt id=\"extendSelections\"><code><strong>doc.extendSelections</strong>(heads: array&lt;{line, ch}&gt;, ?options: object)</code></dt>\n      <dd>An equivalent\n      of <a href=\"#extendSelection\"><code>extendSelection</code></a>\n      that acts on all selections at once.</dd>\n      <dt id=\"extendSelectionsBy\"><code><strong>doc.extendSelectionsBy</strong>(f: function(range: {anchor, head}) → {line, ch}), ?options: object)</code></dt>\n      <dd>Applies the given function to all existing selections, and\n      calls <a href=\"#extendSelections\"><code>extendSelections</code></a>\n      on the result.</dd>\n      <dt id=\"setExtending\"><code><strong>doc.setExtending</strong>(value: boolean)</code></dt>\n      <dd>Sets or clears the 'extending' flag, which acts similar to\n      the shift key, in that it will cause cursor movement and calls\n      to <a href=\"#extendSelection\"><code>extendSelection</code></a>\n      to leave the selection anchor in place.</dd>\n      <dt id=\"getExtending\"><code><strong>doc.getExtending</strong>() → boolean</code></dt>\n      <dd>Get the value of the 'extending' flag.</dd>\n\n      <dt id=\"hasFocus\"><code><strong>cm.hasFocus</strong>() → boolean</code></dt>\n      <dd>Tells you whether the editor currently has focus.</dd>\n\n      <dt id=\"findPosH\"><code><strong>cm.findPosH</strong>(start: {line, ch}, amount: integer, unit: string, visually: boolean) → {line, ch, ?hitSide: boolean}</code></dt>\n      <dd>Used to find the target position for horizontal cursor\n      motion. <code>start</code> is a <code>{line, ch}</code>\n      object, <code>amount</code> an integer (may be negative),\n      and <code>unit</code> one of the\n      string <code>\"char\"</code>, <code>\"column\"</code>,\n      or <code>\"word\"</code>. Will return a position that is produced\n      by moving <code>amount</code> times the distance specified\n      by <code>unit</code>. When <code>visually</code> is true, motion\n      in right-to-left text will be visual rather than logical. When\n      the motion was clipped by hitting the end or start of the\n      document, the returned value will have a <code>hitSide</code>\n      property set to true.</dd>\n      <dt id=\"findPosV\"><code><strong>cm.findPosV</strong>(start: {line, ch}, amount: integer, unit: string) → {line, ch, ?hitSide: boolean}</code></dt>\n      <dd>Similar to <a href=\"#findPosH\"><code>findPosH</code></a>,\n      but used for vertical motion. <code>unit</code> may\n      be <code>\"line\"</code> or <code>\"page\"</code>. The other\n      arguments and the returned value have the same interpretation as\n      they have in <code>findPosH</code>.</dd>\n\n      <dt id=\"findWordAt\"><code><strong>cm.findWordAt</strong>(pos: {line, ch}) → {anchor: {line, ch}, head: {line, ch}}</code></dt>\n      <dd>Returns the start and end of the 'word' (the stretch of\n      letters, whitespace, or punctuation) at the given position.</dd>\n    </dl>\n\n    <h3 id=\"api_configuration\">Configuration methods</h3>\n\n    <dl>\n      <dt id=\"setOption\"><code><strong>cm.setOption</strong>(option: string, value: any)</code></dt>\n      <dd>Change the configuration of the editor. <code>option</code>\n      should the name of an <a href=\"#config\">option</a>,\n      and <code>value</code> should be a valid value for that\n      option.</dd>\n      <dt id=\"getOption\"><code><strong>cm.getOption</strong>(option: string) → any</code></dt>\n      <dd>Retrieves the current value of the given option for this\n      editor instance.</dd>\n\n      <dt id=\"addKeyMap\"><code><strong>cm.addKeyMap</strong>(map: object, bottom: boolean)</code></dt>\n      <dd>Attach an additional <a href=\"#keymaps\">key map</a> to the\n      editor. This is mostly useful for addons that need to register\n      some key handlers without trampling on\n      the <a href=\"#option_extraKeys\"><code>extraKeys</code></a>\n      option. Maps added in this way have a higher precedence than\n      the <code>extraKeys</code>\n      and <a href=\"#option_keyMap\"><code>keyMap</code></a> options,\n      and between them, the maps added earlier have a lower precedence\n      than those added later, unless the <code>bottom</code> argument\n      was passed, in which case they end up below other key maps added\n      with this method.</dd>\n      <dt id=\"removeKeyMap\"><code><strong>cm.removeKeyMap</strong>(map: object)</code></dt>\n      <dd>Disable a keymap added\n      with <a href=\"#addKeyMap\"><code>addKeyMap</code></a>. Either\n      pass in the key map object itself, or a string, which will be\n      compared against the <code>name</code> property of the active\n      key maps.</dd>\n\n      <dt id=\"addOverlay\"><code><strong>cm.addOverlay</strong>(mode: string|object, ?options: object)</code></dt>\n      <dd>Enable a highlighting overlay. This is a stateless mini-mode\n      that can be used to add extra highlighting. For example,\n      the <a href=\"../demo/search.html\">search addon</a> uses it to\n      highlight the term that's currently being\n      searched. <code>mode</code> can be a <a href=\"#option_mode\">mode\n      spec</a> or a mode object (an object with\n      a <a href=\"#token\"><code>token</code></a> method).\n      The <code>options</code> parameter is optional. If given, it\n      should be an object, optionally containing the following options:\n        <dl>\n          <dt><code><strong>opaque</strong>: bool</code></dt>\n          <dd>Defaults to off, but can be given to allow the overlay\n          styling, when not <code>null</code>, to override the styling of\n          the base mode entirely, instead of the two being applied\n          together.</dd>\n          <dt><code><strong>priority</strong>: number</code></dt>\n          <dd>Determines the ordering in which the overlays are\n          applied. Those with high priority are applied after those\n          with lower priority, and able to override the opaqueness of\n          the ones that come before. Defaults to 0.</dd>\n        </dl>\n      </dd>\n\n      <dt id=\"removeOverlay\"><code><strong>cm.removeOverlay</strong>(mode: string|object)</code></dt>\n      <dd>Pass this the exact value passed for the <code>mode</code>\n      parameter to <a href=\"#addOverlay\"><code>addOverlay</code></a>,\n      or a string that corresponds to the <code>name</code> property of\n      that value, to remove an overlay again.</dd>\n\n      <dt id=\"on\"><code><strong>cm.on</strong>(type: string, func: (...args))</code></dt>\n      <dd>Register an event handler for the given event type (a\n      string) on the editor instance. There is also\n      a <code>CodeMirror.on(object, type, func)</code> version\n      that allows registering of events on any object.</dd>\n      <dt id=\"off\"><code><strong>cm.off</strong>(type: string, func: (...args))</code></dt>\n      <dd>Remove an event handler on the editor instance. An\n      equivalent <code>CodeMirror.off(object, type,\n      func)</code> also exists.</dd>\n    </dl>\n\n    <h3 id=\"api_doc\">Document management methods</h3>\n\n    <p id=\"Doc\">Each editor is associated with an instance\n    of <code>CodeMirror.Doc</code>, its document. A document\n    represents the editor content, plus a selection, an undo history,\n    and a <a href=\"#option_mode\">mode</a>. A document can only be\n    associated with a single editor at a time. You can create new\n    documents by calling the <code>CodeMirror.Doc(text: string, mode:\n    Object, firstLineNumber: ?number, lineSeparator: ?string)</code>\n    constructor. The last three arguments are optional and can be used\n    to set a mode for the document, make it start at a line number\n    other than 0, and set a specific line separator respectively.</p>\n\n    <dl>\n      <dt id=\"getDoc\"><code><strong>cm.getDoc</strong>() → Doc</code></dt>\n      <dd>Retrieve the currently active document from an editor.</dd>\n      <dt id=\"getEditor\"><code><strong>doc.getEditor</strong>() → CodeMirror</code></dt>\n      <dd>Retrieve the editor associated with a document. May\n      return <code>null</code>.</dd>\n\n      <dt id=\"swapDoc\"><code><strong>cm.swapDoc</strong>(doc: CodeMirror.Doc) → Doc</code></dt>\n      <dd>Attach a new document to the editor. Returns the old\n      document, which is now no longer associated with an editor.</dd>\n\n      <dt id=\"copy\"><code><strong>doc.copy</strong>(copyHistory: boolean) → Doc</code></dt>\n      <dd>Create an identical copy of the given doc.\n      When <code>copyHistory</code> is true, the history will also be\n      copied. Can not be called directly on an editor.</dd>\n\n      <dt id=\"linkedDoc\"><code><strong>doc.linkedDoc</strong>(options: object) → Doc</code></dt>\n      <dd>Create a new document that's linked to the target document.\n      Linked documents will stay in sync (changes to one are also\n      applied to the other) until <a href=\"#unlinkDoc\">unlinked</a>.\n      These are the options that are supported:\n        <dl>\n          <dt id=\"linkedDoc_sharedHist\"><code><strong>sharedHist</strong>: boolean</code></dt>\n          <dd>When turned on, the linked copy will share an undo\n          history with the original. Thus, something done in one of\n          the two can be undone in the other, and vice versa.</dd>\n          <dt id=\"linkedDoc_from\"><code><strong>from</strong>: integer</code></dt>\n          <dt id=\"linkedDoc_to\"><code><strong>to</strong>: integer</code></dt>\n          <dd>Can be given to make the new document a subview of the\n          original. Subviews only show a given range of lines. Note\n          that line coordinates inside the subview will be consistent\n          with those of the parent, so that for example a subview\n          starting at line 10 will refer to its first line as line 10,\n          not 0.</dd>\n          <dt id=\"linkedDoc_mode\"><code><strong>mode</strong>: string|object</code></dt>\n          <dd>By default, the new document inherits the mode of the\n          parent. This option can be set to\n          a <a href=\"#option_mode\">mode spec</a> to give it a\n          different mode.</dd>\n        </dl></dd>\n      <dt id=\"unlinkDoc\"><code><strong>doc.unlinkDoc</strong>(doc: CodeMirror.Doc)</code></dt>\n      <dd>Break the link between two documents. After calling this,\n      changes will no longer propagate between the documents, and, if\n      they had a shared history, the history will become\n      separate.</dd>\n      <dt id=\"iterLinkedDocs\"><code><strong>doc.iterLinkedDocs</strong>(function: (doc: CodeMirror.Doc, sharedHist: boolean))</code></dt>\n      <dd>Will call the given function for all documents linked to the\n      target document. It will be passed two arguments, the linked document\n      and a boolean indicating whether that document shares history\n      with the target.</dd>\n    </dl>\n\n    <h3 id=\"api_history\">History-related methods</h3>\n\n    <dl>\n      <dt id=\"undo\"><code><strong>doc.undo</strong>()</code></dt>\n      <dd>Undo one edit (if any undo events are stored).</dd>\n      <dt id=\"redo\"><code><strong>doc.redo</strong>()</code></dt>\n      <dd>Redo one undone edit.</dd>\n\n      <dt id=\"undoSelection\"><code><strong>doc.undoSelection</strong>()</code></dt>\n      <dd>Undo one edit or selection change.</dd>\n      <dt id=\"redoSelection\"><code><strong>doc.redoSelection</strong>()</code></dt>\n      <dd>Redo one undone edit or selection change.</dd>\n\n      <dt id=\"historySize\"><code><strong>doc.historySize</strong>() → {undo: integer, redo: integer}</code></dt>\n      <dd>Returns an object with <code>{undo, redo}</code> properties,\n      both of which hold integers, indicating the amount of stored\n      undo and redo operations.</dd>\n      <dt id=\"clearHistory\"><code><strong>doc.clearHistory</strong>()</code></dt>\n      <dd>Clears the editor's undo history.</dd>\n      <dt id=\"getHistory\"><code><strong>doc.getHistory</strong>() → object</code></dt>\n      <dd>Get a (JSON-serializable) representation of the undo history.</dd>\n      <dt id=\"setHistory\"><code><strong>doc.setHistory</strong>(history: object)</code></dt>\n      <dd>Replace the editor's undo history with the one provided,\n      which must be a value as returned\n      by <a href=\"#getHistory\"><code>getHistory</code></a>. Note that\n      this will have entirely undefined results if the editor content\n      isn't also the same as it was when <code>getHistory</code> was\n      called.</dd>\n    </dl>\n\n    <h3 id=\"api_marker\">Text-marking methods</h3>\n\n    <dl>\n      <dt id=\"markText\"><code><strong>doc.markText</strong>(from: {line, ch}, to: {line, ch}, ?options: object) → TextMarker</code></dt>\n      <dd>Can be used to mark a range of text with a specific CSS\n      class name. <code>from</code> and <code>to</code> should\n      be <code>{line, ch}</code> objects. The <code>options</code>\n      parameter is optional. When given, it should be an object that\n      may contain the following configuration options:\n      <dl>\n        <dt id=\"mark_className\"><code><strong>className</strong>: string</code></dt>\n        <dd>Assigns a CSS class to the marked stretch of text.</dd>\n        <dt id=\"mark_inclusiveLeft\"><code><strong>inclusiveLeft</strong>: boolean</code></dt>\n        <dd>Determines whether\n        text inserted on the left of the marker will end up inside\n        or outside of it.</dd>\n        <dt id=\"mark_inclusiveRight\"><code><strong>inclusiveRight</strong>: boolean</code></dt>\n        <dd>Like <code>inclusiveLeft</code>,\n        but for the right side.</dd>\n        <dt id=\"mark_selectLeft\"><code><strong>selectLeft</strong>: boolean</code></dt>\n        <dd>For atomic ranges, determines whether the cursor is allowed\n        to be placed directly to the left of the range. Has no effect on\n        non-atomic ranges.</dd>\n        <dt id=\"mark_selectRight\"><code><strong>selectRight</strong>: boolean</code></dt>\n        <dd>Like <code>selectLeft</code>,\n        but for the right side.</dd>\n        <dt id=\"mark_atomic\"><code><strong>atomic</strong>: boolean</code></dt>\n        <dd>Atomic ranges act as a single unit when cursor movement is\n        concerned—i.e. it is impossible to place the cursor inside of\n        them. You can control whether the cursor is allowed to be placed\n        directly before or after them using <code>selectLeft</code>\n        or <code>selectRight</code>. If <code>selectLeft</code>\n        (or right) is not provided, then <code>inclusiveLeft</code> (or\n        right) will control this behavior.</dd>\n        <dt id=\"mark_collapsed\"><code><strong>collapsed</strong>: boolean</code></dt>\n        <dd>Collapsed ranges do not show up in the display. Setting a\n        range to be collapsed will automatically make it atomic.</dd>\n        <dt id=\"mark_clearOnEnter\"><code><strong>clearOnEnter</strong>: boolean</code></dt>\n        <dd>When enabled, will cause the mark to clear itself whenever\n        the cursor enters its range. This is mostly useful for\n        text-replacement widgets that need to 'snap open' when the\n        user tries to edit them. The\n        <a href=\"#event_clear\"><code>\"clear\"</code></a> event\n        fired on the range handle can be used to be notified when this\n        happens.</dd>\n        <dt id=\"mark_clearWhenEmpty\"><code><strong>clearWhenEmpty</strong>: boolean</code></dt>\n        <dd>Determines whether the mark is automatically cleared when\n        it becomes empty. Default is true.</dd>\n        <dt id=\"mark_replacedWith\"><code><strong>replacedWith</strong>: Element</code></dt>\n        <dd>Use a given node to display this range. Implies both\n        collapsed and atomic. The given DOM node <em>must</em> be an\n        inline element (as opposed to a block element).</dd>\n        <dt><code><strong>handleMouseEvents</strong>: boolean</code></dt>\n        <dd>When <code>replacedWith</code> is given, this determines\n        whether the editor will capture mouse and drag events\n        occurring in this widget. Default is false—the events will be\n        left alone for the default browser handler, or specific\n        handlers on the widget, to capture.</dd>\n        <dt id=\"mark_readOnly\"><code><strong>readOnly</strong>: boolean</code></dt>\n        <dd>A read-only span can, as long as it is not cleared, not be\n        modified except by\n        calling <a href=\"#setValue\"><code>setValue</code></a> to reset\n        the whole document. <em>Note:</em> adding a read-only span\n        currently clears the undo history of the editor, because\n        existing undo events being partially nullified by read-only\n        spans would corrupt the history (in the current\n        implementation).</dd>\n        <dt id=\"mark_addToHistory\"><code><strong>addToHistory</strong>: boolean</code></dt>\n        <dd>When set to true (default is false), adding this marker\n        will create an event in the undo history that can be\n        individually undone (clearing the marker).</dd>\n        <dt id=\"mark_startStyle\"><code><strong>startStyle</strong>: string</code></dt><dd>Can be used to specify\n        an extra CSS class to be applied to the leftmost span that\n        is part of the marker.</dd>\n        <dt id=\"mark_endStyle\"><code><strong>endStyle</strong>: string</code></dt><dd>Equivalent\n        to <code>startStyle</code>, but for the rightmost span.</dd>\n        <dt id=\"mark_css\"><code><strong>css</strong>: string</code></dt>\n        <dd>A string of CSS to be applied to the covered text. For example <code>\"color: #fe3\"</code>.</dd>\n        <dt id=\"mark_attributes\"><code><strong>attributes</strong>: object</code></dt>\n        <dd>When given, add the attributes in the given object to the\n        elements created for the marked text. Adding <code>class</code> or\n        <code>style</code> attributes this way is not supported.</dd>\n        <dt id=\"mark_shared\"><code><strong>shared</strong>: boolean</code></dt><dd>When the\n        target document is <a href=\"#linkedDoc\">linked</a> to other\n        documents, you can set <code>shared</code> to true to make the\n        marker appear in all documents. By default, a marker appears\n        only in its target document.</dd>\n      </dl>\n      The method will return an object that represents the marker\n      (with constructor <code>CodeMirror.TextMarker</code>), which\n      exposes three methods:\n      <code><strong>clear</strong>()</code>, to remove the mark,\n      <code><strong>find</strong>()</code>, which returns\n      a <code>{from, to}</code> object (both holding document\n      positions), indicating the current position of the marked range,\n      or <code>undefined</code> if the marker is no longer in the\n      document, and finally <code><strong>changed</strong>()</code>,\n      which you can call if you've done something that might change\n      the size of the marker (for example changing the content of\n      a <a href=\"#mark_replacedWith\"><code>replacedWith</code></a>\n      node), and want to cheaply update the display.</dd>\n\n      <dt id=\"setBookmark\"><code><strong>doc.setBookmark</strong>(pos: {line, ch}, ?options: object) → TextMarker</code></dt>\n      <dd>Inserts a bookmark, a handle that follows the text around it\n      as it is being edited, at the given position. A bookmark has two\n      methods <code>find()</code> and <code>clear()</code>. The first\n      returns the current position of the bookmark, if it is still in\n      the document, and the second explicitly removes the bookmark.\n      The options argument is optional. If given, the following\n      properties are recognized:\n      <dl>\n        <dt><code><strong>widget</strong>: Element</code></dt><dd>Can be used to display a DOM\n        node at the current location of the bookmark (analogous to\n        the <a href=\"#mark_replacedWith\"><code>replacedWith</code></a>\n        option to <a href=\"#markText\"><code>markText</code></a>).</dd>\n        <dt><code><strong>insertLeft</strong>: boolean</code></dt><dd>By default, text typed\n        when the cursor is on top of the bookmark will end up to the\n        right of the bookmark. Set this option to true to make it go\n        to the left instead.</dd>\n        <dt><code><strong>shared</strong>: boolean</code></dt><dd>See\n        the corresponding <a href=\"#mark_shared\">option</a>\n        to <code>markText</code>.</dd>\n        <dt><code><strong>handleMouseEvents</strong>: boolean</code></dt>\n        <dd>As with <a href=\"#markText\"><code>markText</code></a>,\n        this determines whether mouse events on the widget inserted\n        for this bookmark are handled by CodeMirror. The default is\n        false.</dd>\n      </dl></dd>\n\n      <dt id=\"findMarks\"><code><strong>doc.findMarks</strong>(from: {line, ch}, to: {line, ch}) → array&lt;TextMarker&gt;</code></dt>\n      <dd>Returns an array of all the bookmarks and marked ranges\n      found between the given positions (non-inclusive).</dd>\n      <dt id=\"findMarksAt\"><code><strong>doc.findMarksAt</strong>(pos: {line, ch}) → array&lt;TextMarker&gt;</code></dt>\n      <dd>Returns an array of all the bookmarks and marked ranges\n      present at the given position.</dd>\n      <dt id=\"getAllMarks\"><code><strong>doc.getAllMarks</strong>() → array&lt;TextMarker&gt;</code></dt>\n      <dd>Returns an array containing all marked ranges in the document.</dd>\n    </dl>\n\n    <h3 id=\"api_decoration\">Widget, gutter, and decoration methods</h3>\n\n    <dl>\n      <dt id=\"setGutterMarker\"><code><strong>doc.setGutterMarker</strong>(line: integer|LineHandle, gutterID: string, value: Element) → LineHandle</code></dt>\n      <dd>Sets the gutter marker for the given gutter (identified by\n      its CSS class, see\n      the <a href=\"#option_gutters\"><code>gutters</code></a> option)\n      to the given value. Value can be either <code>null</code>, to\n      clear the marker, or a DOM element, to set it. The DOM element\n      will be shown in the specified gutter next to the specified\n      line.</dd>\n\n      <dt id=\"clearGutter\"><code><strong>doc.clearGutter</strong>(gutterID: string)</code></dt>\n      <dd>Remove all gutter markers in\n      the <a href=\"#option_gutters\">gutter</a> with the given ID.</dd>\n\n      <dt id=\"addLineClass\"><code><strong>doc.addLineClass</strong>(line: integer|LineHandle, where: string, class: string) → LineHandle</code></dt>\n      <dd>Set a CSS class name for the given line. <code>line</code>\n      can be a number or a line handle. <code>where</code> determines\n      to which element this class should be applied, can be one\n      of <code>\"text\"</code> (the text element, which lies in front of\n      the selection), <code>\"background\"</code> (a background element\n      that will be behind the selection), <code>\"gutter\"</code> (the\n      line's gutter space), or <code>\"wrap\"</code> (the wrapper node\n      that wraps all of the line's elements, including gutter\n      elements). <code>class</code> should be the name of the class to\n      apply.</dd>\n\n      <dt id=\"removeLineClass\"><code><strong>doc.removeLineClass</strong>(line: integer|LineHandle, where: string, class: string) → LineHandle</code></dt>\n      <dd>Remove a CSS class from a line. <code>line</code> can be a\n      line handle or number. <code>where</code> should be one\n      of <code>\"text\"</code>, <code>\"background\"</code>,\n      or <code>\"wrap\"</code>\n      (see <a href=\"#addLineClass\"><code>addLineClass</code></a>). <code>class</code>\n      can be left off to remove all classes for the specified node, or\n      be a string to remove only a specific class.</dd>\n\n      <dt id=\"lineInfo\"><code><strong>doc.lineInfo</strong>(line: integer|LineHandle) → object</code></dt>\n      <dd>Returns the line number, text content, and marker status of\n      the given line, which can be either a number or a line handle.\n      The returned object has the structure <code>{line, handle, text,\n      gutterMarkers, textClass, bgClass, wrapClass, widgets}</code>,\n      where <code>gutterMarkers</code> is an object mapping gutter IDs\n      to marker elements, and <code>widgets</code> is an array\n      of <a href=\"#addLineWidget\">line widgets</a> attached to this\n      line, and the various class properties refer to classes added\n      with <a href=\"#addLineClass\"><code>addLineClass</code></a>.</dd>\n\n      <dt id=\"addWidget\"><code><strong>cm.addWidget</strong>(pos: {line, ch}, node: Element, scrollIntoView: boolean)</code></dt>\n      <dd>Puts <code>node</code>, which should be an absolutely\n      positioned DOM node, into the editor, positioned right below the\n      given <code>{line, ch}</code> position.\n      When <code>scrollIntoView</code> is true, the editor will ensure\n      that the entire node is visible (if possible). To remove the\n      widget again, simply use DOM methods (move it somewhere else, or\n      call <code>removeChild</code> on its parent).</dd>\n\n      <dt id=\"addLineWidget\"><code><strong>doc.addLineWidget</strong>(line: integer|LineHandle, node: Element, ?options: object) → LineWidget</code></dt>\n      <dd>Adds a line widget, an element shown below a line, spanning\n      the whole of the editor's width, and moving the lines below it\n      downwards. <code>line</code> should be either an integer or a\n      line handle, and <code>node</code> should be a DOM node, which\n      will be displayed below the given line. <code>options</code>,\n      when given, should be an object that configures the behavior of\n      the widget. The following options are supported (all default to\n      false):\n        <dl>\n          <dt><code><strong>coverGutter</strong>: boolean</code></dt>\n          <dd>Whether the widget should cover the gutter.</dd>\n          <dt><code><strong>noHScroll</strong>: boolean</code></dt>\n          <dd>Whether the widget should stay fixed in the face of\n          horizontal scrolling.</dd>\n          <dt><code><strong>above</strong>: boolean</code></dt>\n          <dd>Causes the widget to be placed above instead of below\n          the text of the line.</dd>\n          <dt><code><strong>handleMouseEvents</strong>: boolean</code></dt>\n          <dd>Determines whether the editor will capture mouse and\n          drag events occurring in this widget. Default is false—the\n          events will be left alone for the default browser handler,\n          or specific handlers on the widget, to capture.</dd>\n          <dt><code><strong>insertAt</strong>: integer</code></dt>\n          <dd>By default, the widget is added below other widgets for\n          the line. This option can be used to place it at a different\n          position (zero for the top, N to put it after the Nth other\n          widget). Note that this only has effect once, when the\n          widget is created.\n          <dt><code><strong>className</strong>: string</code></dt>\n          <dd>Add an extra CSS class name to the wrapper element\n          created for the widget.</dd>\n        </dl>\n      Note that the widget node will become a descendant of nodes with\n      CodeMirror-specific CSS classes, and those classes might in some\n      cases affect it. This method returns an object that represents\n      the widget placement. It'll have a <code>line</code> property\n      pointing at the line handle that it is associated with, and the following methods:\n        <dl>\n          <dt id=\"widget_clear\"><code><strong>clear</strong>()</code></dt><dd>Removes the widget.</dd>\n          <dt id=\"widget_changed\"><code><strong>changed</strong>()</code></dt><dd>Call\n          this if you made some change to the widget's DOM node that\n          might affect its height. It'll force CodeMirror to update\n          the height of the line that contains the widget.</dd>\n        </dl>\n      </dd>\n    </dl>\n\n    <h3 id=\"api_sizing\">Sizing, scrolling and positioning methods</h3>\n\n    <dl>\n      <dt id=\"setSize\"><code><strong>cm.setSize</strong>(width: number|string, height: number|string)</code></dt>\n      <dd>Programmatically set the size of the editor (overriding the\n      applicable <a href=\"#css-resize\">CSS\n      rules</a>). <code>width</code> and <code>height</code>\n      can be either numbers (interpreted as pixels) or CSS units\n      (<code>\"100%\"</code>, for example). You can\n      pass <code>null</code> for either of them to indicate that that\n      dimension should not be changed.</dd>\n\n      <dt id=\"scrollTo\"><code><strong>cm.scrollTo</strong>(x: number, y: number)</code></dt>\n      <dd>Scroll the editor to a given (pixel) position. Both\n      arguments may be left as <code>null</code>\n      or <code>undefined</code> to have no effect.</dd>\n      <dt id=\"getScrollInfo\"><code><strong>cm.getScrollInfo</strong>() → {left, top, width, height, clientWidth, clientHeight}</code></dt>\n      <dd>Get an <code>{left, top, width, height, clientWidth,\n      clientHeight}</code> object that represents the current scroll\n      position, the size of the scrollable area, and the size of the\n      visible area (minus scrollbars).</dd>\n      <dt id=\"scrollIntoView\"><code><strong>cm.scrollIntoView</strong>(what: {line, ch}|{left, top, right, bottom}|{from, to}|null, ?margin: number)</code></dt>\n      <dd>Scrolls the given position into view. <code>what</code> may\n      be <code>null</code> to scroll the cursor into view,\n      a <code>{line, ch}</code> position to scroll a character into\n      view, a <code>{left, top, right, bottom}</code> pixel range (in\n      editor-local coordinates), or a range <code>{from, to}</code>\n      containing either two character positions or two pixel squares.\n      The <code>margin</code> parameter is optional. When given, it\n      indicates the amount of vertical pixels around the given area\n      that should be made visible as well.</dd>\n\n      <dt id=\"cursorCoords\"><code><strong>cm.cursorCoords</strong>(where: boolean|{line, ch}, mode: string) → {left, top, bottom}</code></dt>\n      <dd>Returns an <code>{left, top, bottom}</code> object\n      containing the coordinates of the cursor position.\n      If <code>mode</code> is <code>\"local\"</code>, they will be\n      relative to the top-left corner of the editable document. If it\n      is <code>\"page\"</code> or not given, they are relative to the\n      top-left corner of the page. If <code>mode</code>\n      is <code>\"window\"</code>, the coordinates are relative to the\n      top-left corner of the currently visible (scrolled)\n      window. <code>where</code> can be a boolean indicating whether\n      you want the start (<code>true</code>) or the end\n      (<code>false</code>) of the selection, or, if a <code>{line,\n      ch}</code> object is given, it specifies the precise position at\n      which you want to measure.</dd>\n      <dt id=\"charCoords\"><code><strong>cm.charCoords</strong>(pos: {line, ch}, ?mode: string) → {left, right, top, bottom}</code></dt>\n      <dd>Returns the position and dimensions of an arbitrary\n      character. <code>pos</code> should be a <code>{line, ch}</code>\n      object. This differs from <code>cursorCoords</code> in that\n      it'll give the size of the whole character, rather than just the\n      position that the cursor would have when it would sit at that\n      position.</dd>\n      <dt id=\"coordsChar\"><code><strong>cm.coordsChar</strong>(object: {left, top}, ?mode: string) → {line, ch}</code></dt>\n      <dd>Given an <code>{left, top}</code> object (e.g. coordinates of a mouse event) returns\n      the <code>{line, ch}</code> position that corresponds to it. The\n      optional <code>mode</code> parameter determines relative to what\n      the coordinates are interpreted. It may\n      be <code>\"window\"</code>, <code>\"page\"</code> (the default),\n      or <code>\"local\"</code>.</dd>\n      <dt id=\"lineAtHeight\"><code><strong>cm.lineAtHeight</strong>(height: number, ?mode: string) → number</code></dt>\n      <dd>Computes the line at the given pixel\n      height. <code>mode</code> can be one of the same strings\n      that <a href=\"#coordsChar\"><code>coordsChar</code></a>\n      accepts.</dd>\n      <dt id=\"heightAtLine\"><code><strong>cm.heightAtLine</strong>(line: integer|LineHandle, ?mode: string, ?includeWidgets: bool) → number</code></dt>\n      <dd>Computes the height of the top of a line, in the coordinate\n      system specified by <code>mode</code>\n      (see <a href=\"#coordsChar\"><code>coordsChar</code></a>), which\n      defaults to <code>\"page\"</code>. When a line below the bottom of\n      the document is specified, the returned value is the bottom of\n      the last line in the document. By default, the position of the\n      actual text is returned. If `includeWidgets` is true and the\n      line has line widgets, the position above the first line widget\n      is returned.</dd>\n      <dt id=\"defaultTextHeight\"><code><strong>cm.defaultTextHeight</strong>() → number</code></dt>\n      <dd>Returns the line height of the default font for the editor.</dd>\n      <dt id=\"defaultCharWidth\"><code><strong>cm.defaultCharWidth</strong>() → number</code></dt>\n      <dd>Returns the pixel width of an 'x' in the default font for\n      the editor. (Note that for non-monospace fonts, this is mostly\n      useless, and even for monospace fonts, non-ascii characters\n      might have a different width).</dd>\n\n      <dt id=\"getViewport\"><code><strong>cm.getViewport</strong>() → {from: number, to: number}</code></dt>\n      <dd>Returns a <code>{from, to}</code> object indicating the\n      start (inclusive) and end (exclusive) of the currently rendered\n      part of the document. In big documents, when most content is\n      scrolled out of view, CodeMirror will only render the visible\n      part, and a margin around it. See also\n      the <a href=\"#event_viewportChange\"><code>viewportChange</code></a>\n      event.</dd>\n\n      <dt id=\"refresh\"><code><strong>cm.refresh</strong>()</code></dt>\n      <dd>If your code does something to change the size of the editor\n      element (window resizes are already listened for), or unhides\n      it, you should probably follow up by calling this method to\n      ensure CodeMirror is still looking as intended. See also\n      the <a href=\"#addon_autorefresh\">autorefresh addon</a>.</dd>\n    </dl>\n\n    <h3 id=\"api_mode\">Mode, state, and token-related methods</h3>\n\n    <p>When writing language-aware functionality, it can often be\n    useful to hook into the knowledge that the CodeMirror language\n    mode has. See <a href=\"#modeapi\">the section on modes</a> for a\n    more detailed description of how these work.</p>\n\n    <dl>\n      <dt id=\"getMode\"><code><strong>doc.getMode</strong>() → object</code></dt>\n      <dd>Gets the (outer) mode object for the editor. Note that this\n      is distinct from <code>getOption(\"mode\")</code>, which gives you\n      the mode specification, rather than the resolved, instantiated\n      <a href=\"#defineMode\">mode object</a>.</dd>\n\n      <dt id=\"getModeAt\"><code><strong>cm.getModeAt</strong>(pos: {line, ch}) → object</code></dt>\n      <dd>Gets the inner mode at a given position. This will return\n      the same as <a href=\"#getMode\"><code>getMode</code></a> for\n      simple modes, but will return an inner mode for nesting modes\n      (such as <code>htmlmixed</code>).</dd>\n\n      <dt id=\"getTokenAt\"><code><strong>cm.getTokenAt</strong>(pos: {line, ch}, ?precise: boolean) → object</code></dt>\n      <dd>Retrieves information about the token the current mode found\n      before the given position (a <code>{line, ch}</code> object). The\n      returned object has the following properties:\n      <dl>\n        <dt><code><strong>start</strong></code></dt><dd>The character (on the given line) at which the token starts.</dd>\n        <dt><code><strong>end</strong></code></dt><dd>The character at which the token ends.</dd>\n        <dt><code><strong>string</strong></code></dt><dd>The token's string.</dd>\n        <dt><code><strong>type</strong></code></dt><dd>The token type the mode assigned\n        to the token, such as <code>\"keyword\"</code>\n        or <code>\"comment\"</code> (may also be null).</dd>\n        <dt><code><strong>state</strong></code></dt><dd>The mode's state at the end of this token.</dd>\n      </dl>\n      If <code>precise</code> is true, the token will be guaranteed to be accurate based on recent edits. If false or\n      not specified, the token will use cached state information, which will be faster but might not be accurate if\n      edits were recently made and highlighting has not yet completed.\n      </dd>\n\n      <dt id=\"getLineTokens\"><code><strong>cm.getLineTokens</strong>(line: integer, ?precise: boolean) → array&lt;{start, end, string, type, state}&gt;</code></dt>\n      <dd>This is similar\n      to <a href=\"#getTokenAt\"><code>getTokenAt</code></a>, but\n      collects all tokens for a given line into an array. It is much\n      cheaper than repeatedly calling <code>getTokenAt</code>, which\n      re-parses the part of the line before the token for every call.</dd>\n\n      <dt id=\"getTokenTypeAt\"><code><strong>cm.getTokenTypeAt</strong>(pos: {line, ch}) → string</code></dt>\n      <dd>This is a (much) cheaper version\n      of <a href=\"#getTokenAt\"><code>getTokenAt</code></a> useful for\n      when you just need the type of the token at a given position,\n      and no other information. Will return <code>null</code> for\n      unstyled tokens, and a string, potentially containing multiple\n      space-separated style names, otherwise.</dd>\n\n      <dt id=\"getHelpers\"><code><strong>cm.getHelpers</strong>(pos: {line, ch}, type: string) → array&lt;helper&gt;</code></dt>\n      <dd>Fetch the set of applicable helper values for the given\n      position. Helpers provide a way to look up functionality\n      appropriate for a mode. The <code>type</code> argument provides\n      the helper namespace (see\n      <a href=\"#registerHelper\"><code>registerHelper</code></a>), in\n      which the values will be looked up. When the mode itself has a\n      property that corresponds to the <code>type</code>, that\n      directly determines the keys that are used to look up the helper\n      values (it may be either a single string, or an array of\n      strings). Failing that, the mode's <code>helperType</code>\n      property and finally the mode's name are used.</dd>\n      <dd>For example, the JavaScript mode has a\n      property <code>fold</code> containing <code>\"brace\"</code>. When\n      the <code>brace-fold</code> addon is loaded, that defines a\n      helper named <code>brace</code> in the <code>fold</code>\n      namespace. This is then used by\n      the <a href=\"#addon_foldcode\"><code>foldcode</code></a> addon to\n      figure out that it can use that folding function to fold\n      JavaScript code.</dd>\n      <dd>When any <a href=\"#registerGlobalHelper\">'global'</a>\n      helpers are defined for the given namespace, their predicates\n      are called on the current mode and editor, and all those that\n      declare they are applicable will also be added to the array that\n      is returned.</dd>\n\n      <dt id=\"getHelper\"><code><strong>cm.getHelper</strong>(pos: {line, ch}, type: string) → helper</code></dt>\n      <dd>Returns the first applicable helper value.\n      See <a href=\"#getHelpers\"><code>getHelpers</code></a>.</dd>\n\n      <dt id=\"getStateAfter\"><code><strong>cm.getStateAfter</strong>(?line: integer, ?precise: boolean) → object</code></dt>\n      <dd>Returns the mode's parser state, if any, at the end of the\n      given line number. If no line number is given, the state at the\n      end of the document is returned. This can be useful for storing\n      parsing errors in the state, or getting other kinds of\n      contextual information for a line. <code>precise</code> is defined\n      as in <code>getTokenAt()</code>.</dd>\n    </dl>\n\n    <h3 id=\"api_misc\">Miscellaneous methods</h3>\n\n    <dl>\n      <dt id=\"operation\"><code><strong>cm.operation</strong>(func: () → any) → any</code></dt>\n      <dd>CodeMirror internally buffers changes and only updates its\n      DOM structure after it has finished performing some operation.\n      If you need to perform a lot of operations on a CodeMirror\n      instance, you can call this method with a function argument. It\n      will call the function, buffering up all changes, and only doing\n      the expensive update after the function returns. This can be a\n      lot faster. The return value from this method will be the return\n      value of your function.</dd>\n\n      <dt id=\"startOperation\"><code><strong>cm.startOperation</strong>()</code></dt>\n      <dt id=\"endOperation\"><code><strong>cm.endOperation</strong>()</code></dt>\n      <dd>In normal circumstances, use the above <code>operation</code>\n      method. But if you want to buffer operations happening asynchronously,\n      or that can't all be wrapped in a callback function, you can\n      call <code>startOperation</code> to tell CodeMirror to start\n      buffering changes, and <code>endOperation</code> to actually\n      render all the updates. <em>Be careful:</em> if you use this\n      API and forget to call <code>endOperation</code>, the editor will\n      just never update.</dd>\n\n      <dt id=\"indentLine\"><code><strong>cm.indentLine</strong>(line: integer, ?dir: string|integer)</code></dt>\n      <dd>Adjust the indentation of the given line. The second\n      argument (which defaults to <code>\"smart\"</code>) may be one of:\n        <dl>\n          <dt><code><strong>\"prev\"</strong></code></dt>\n          <dd>Base indentation on the indentation of the previous line.</dd>\n          <dt><code><strong>\"smart\"</strong></code></dt>\n          <dd>Use the mode's smart indentation if available, behave\n          like <code>\"prev\"</code> otherwise.</dd>\n          <dt><code><strong>\"add\"</strong></code></dt>\n          <dd>Increase the indentation of the line by\n          one <a href=\"#option_indentUnit\">indent unit</a>.</dd>\n          <dt><code><strong>\"subtract\"</strong></code></dt>\n          <dd>Reduce the indentation of the line.</dd>\n          <dt><code><strong>&lt;integer></strong></code></dt>\n          <dd>Add (positive number) or reduce (negative number) the\n          indentation by the given amount of spaces.</dd>\n        </dl></dd>\n\n      <dt id=\"toggleOverwrite\"><code><strong>cm.toggleOverwrite</strong>(?value: boolean)</code></dt>\n      <dd>Switches between overwrite and normal insert mode (when not\n      given an argument), or sets the overwrite mode to a specific\n      state (when given an argument).</dd>\n\n      <dt id=\"isReadOnly\"><code><strong>cm.isReadOnly</strong>() → boolean</code></dt>\n      <dd>Tells you whether the editor's content can be edited by the\n      user.</dd>\n\n      <dt id=\"lineSeparator\"><code><strong>doc.lineSeparator</strong>()</code></dt>\n      <dd>Returns the preferred line separator string for this\n      document, as per the <a href=\"#option_lineSeparator\">option</a>\n      by the same name. When that option is <code>null</code>, the\n      string <code>\"\\n\"</code> is returned.</dd>\n\n      <dt id=\"execCommand\"><code><strong>cm.execCommand</strong>(name: string)</code></dt>\n      <dd>Runs the <a href=\"#commands\">command</a> with the given name on the editor.</dd>\n\n      <dt id=\"posFromIndex\"><code><strong>doc.posFromIndex</strong>(index: integer) → {line, ch}</code></dt>\n      <dd>Calculates and returns a <code>{line, ch}</code> object for a\n      zero-based <code>index</code> who's value is relative to the start of the\n      editor's text. If the <code>index</code> is out of range of the text then\n      the returned object is clipped to start or end of the text\n      respectively.</dd>\n      <dt id=\"indexFromPos\"><code><strong>doc.indexFromPos</strong>(object: {line, ch}) → integer</code></dt>\n      <dd>The reverse of <a href=\"#posFromIndex\"><code>posFromIndex</code></a>.</dd>\n\n      <dt id=\"focus\"><code><strong>cm.focus</strong>()</code></dt>\n      <dd>Give the editor focus.</dd>\n\n      <dt id=\"phrase\"><code><strong>cm.phrase</strong>(text: string) → string</code></dt>\n      <dd>Allow the given string to be translated with\n      the <a href=\"#option_phrases\"><code>phrases</code>\n      option</a>.</dd>\n\n      <dt id=\"getInputField\"><code><strong>cm.getInputField</strong>() → Element</code></dt>\n      <dd>Returns the input field for the editor. Will be a textarea\n      or an editable div, depending on the value of\n      the <a href=\"#option_inputStyle\"><code>inputStyle</code></a>\n      option.</dd>\n      <dt id=\"getWrapperElement\"><code><strong>cm.getWrapperElement</strong>() → Element</code></dt>\n      <dd>Returns the DOM node that represents the editor, and\n      controls its size. Remove this from your tree to delete an\n      editor instance.</dd>\n      <dt id=\"getScrollerElement\"><code><strong>cm.getScrollerElement</strong>() → Element</code></dt>\n      <dd>Returns the DOM node that is responsible for the scrolling\n      of the editor.</dd>\n      <dt id=\"getGutterElement\"><code><strong>cm.getGutterElement</strong>() → Element</code></dt>\n      <dd>Fetches the DOM node that contains the editor gutters.</dd>\n    </dl>\n\n    <h3 id=\"api_static\">Static properties</h3>\n    <p>The <code>CodeMirror</code> object itself provides\n    several useful properties.</p>\n\n    <dl>\n      <dt id=\"version\"><code><strong>CodeMirror.version</strong>: string</code></dt>\n      <dd>It contains a string that indicates the version of the\n      library. This is a triple of\n      integers <code>\"major.minor.patch\"</code>,\n      where <code>patch</code> is zero for releases, and something\n      else (usually one) for dev snapshots.</dd>\n\n      <dt id=\"fromTextArea\"><code><strong>CodeMirror.fromTextArea</strong>(textArea: TextAreaElement, ?config: object)</code></dt>\n      <dd>This method provides another way to initialize an editor. It\n      takes a textarea DOM node as first argument and an optional\n      configuration object as second. It will replace the textarea\n      with a CodeMirror instance, and wire up the form of that\n      textarea (if any) to make sure the editor contents are put into\n      the textarea when the form is submitted. The text in the\n      textarea will provide the content for the editor. A CodeMirror\n      instance created this way has three additional methods:\n      <dl>\n        <dt id=\"save\"><code><strong>cm.save</strong>()</code></dt>\n        <dd>Copy the content of the editor into the textarea.</dd>\n\n        <dt id=\"toTextArea\"><code><strong>cm.toTextArea</strong>()</code></dt>\n        <dd>Remove the editor, and restore the original textarea (with\n        the editor's current content). If you dynamically create and\n        destroy editors made with `fromTextArea`, without destroying\n        the form they are part of, you should make sure to call\n        `toTextArea` to remove the editor, or its `\"submit\"` handler\n        on the form will cause a memory leak.</dd>\n\n        <dt id=\"getTextArea\"><code><strong>cm.getTextArea</strong>() → TextAreaElement</code></dt>\n        <dd>Returns the textarea that the instance was based on.</dd>\n      </dl>\n      </dd>\n\n      <dt id=\"defaults\"><code><strong>CodeMirror.defaults</strong>: object</code></dt>\n      <dd>An object containing default values for\n      all <a href=\"#config\">options</a>. You can assign to its\n      properties to modify defaults (though this won't affect editors\n      that have already been created).</dd>\n\n      <dt id=\"defineExtension\"><code><strong>CodeMirror.defineExtension</strong>(name: string, value: any)</code></dt>\n      <dd>If you want to define extra methods in terms of the\n      CodeMirror API, it is possible to\n      use <code>defineExtension</code>. This will cause the given\n      value (usually a method) to be added to all CodeMirror instances\n      created from then on.</dd>\n\n      <dt id=\"defineDocExtension\"><code><strong>CodeMirror.defineDocExtension</strong>(name: string, value: any)</code></dt>\n      <dd>Like <a href=\"#defineExtension\"><code>defineExtension</code></a>,\n      but the method will be added to the interface\n      for <a href=\"#Doc\"><code>Doc</code></a> objects instead.</dd>\n\n      <dt id=\"defineOption\"><code><strong>CodeMirror.defineOption</strong>(name: string,\n      default: any, updateFunc: function)</code></dt>\n      <dd>Similarly, <code>defineOption</code> can be used to define new options for\n      CodeMirror. The <code>updateFunc</code> will be called with the\n      editor instance and the new value when an editor is initialized,\n      and whenever the option is modified\n      through <a href=\"#setOption\"><code>setOption</code></a>.</dd>\n\n      <dt id=\"defineInitHook\"><code><strong>CodeMirror.defineInitHook</strong>(func: function)</code></dt>\n      <dd>If your extension just needs to run some\n      code whenever a CodeMirror instance is initialized,\n      use <code>CodeMirror.defineInitHook</code>. Give it a function as\n      its only argument, and from then on, that function will be called\n      (with the instance as argument) whenever a new CodeMirror instance\n      is initialized.</dd>\n\n      <dt id=\"registerHelper\"><code><strong>CodeMirror.registerHelper</strong>(type: string, name: string, value: helper)</code></dt>\n      <dd>Registers a helper value with the given <code>name</code> in\n      the given namespace (<code>type</code>). This is used to define\n      functionality that may be looked up by mode. Will create (if it\n      doesn't already exist) a property on the <code>CodeMirror</code>\n      object for the given <code>type</code>, pointing to an object\n      that maps names to values. I.e. after\n      doing <code>CodeMirror.registerHelper(\"hint\", \"foo\",\n      myFoo)</code>, the value <code>CodeMirror.hint.foo</code> will\n      point to <code>myFoo</code>.</dd>\n\n      <dt id=\"registerGlobalHelper\"><code><strong>CodeMirror.registerGlobalHelper</strong>(type: string, name: string, predicate: fn(mode, CodeMirror), value: helper)</code></dt>\n      <dd>Acts\n      like <a href=\"#registerHelper\"><code>registerHelper</code></a>,\n      but also registers this helper as 'global', meaning that it will\n      be included by <a href=\"#getHelpers\"><code>getHelpers</code></a>\n      whenever the given <code>predicate</code> returns true when\n      called with the local mode and editor.</dd>\n\n      <dt id=\"Pos\"><code><strong>CodeMirror.Pos</strong>(line: integer, ?ch: integer, ?sticky: string)</code></dt>\n      <dd>A constructor for the objects that are used to represent\n      positions in editor documents. <code>sticky</code> defaults to\n      null, but can be set to <code>\"before\"</code>\n      or <code>\"after\"</code> to make the position explicitly\n      associate with the character before or after it.</dd>\n\n      <dt id=\"changeEnd\"><code><strong>CodeMirror.changeEnd</strong>(change: object) → {line, ch}</code></dt>\n      <dd>Utility function that computes an end position from a change\n      (an object with <code>from</code>, <code>to</code>,\n      and <code>text</code> properties, as passed to\n      various <a href=\"#event_change\">event handlers</a>). The\n      returned position will be the end of the changed\n      range, <em>after</em> the change is applied.</dd>\n\n      <dt id=\"countColumn\"><code><strong>CodeMirror.countColumn</strong>(line: string, index: number, tabSize: number) → number</code></dt>\n      <dd>Find the column position at a given string index using a given tabsize.</dd>\n    </dl>\n</section>\n\n<section id=addons>\n    <h2 id=\"addons\">Addons</h2>\n\n    <p>The <code>addon</code> directory in the distribution contains a\n    number of reusable components that implement extra editor\n    functionality (on top of extension functions\n    like <a href=\"#defineOption\"><code>defineOption</code></a>, <a href=\"#defineExtension\"><code>defineExtension</code></a>,\n    and <a href=\"#registerHelper\"><code>registerHelper</code></a>). In\n    brief, they are:</p>\n\n    <dl>\n      <dt id=\"addon_dialog\"><a href=\"../addon/dialog/dialog.js\"><code>dialog/dialog.js</code></a></dt>\n      <dd>Provides a very simple way to query users for text input.\n      Adds the <strong><code>openDialog(template, callback, options) →\n      closeFunction</code></strong> method to CodeMirror instances,\n      which can be called with an HTML fragment or a detached DOM\n      node that provides the prompt (should include an <code>input</code>\n      or <code>button</code> tag), and a callback function that is called\n      when the user presses enter. It returns a function <code>closeFunction</code>\n      which, if called, will close the dialog immediately.\n      <strong><code>openDialog</code></strong> takes the following options:\n        <dl>\n          <dt><code><strong>closeOnEnter</strong>: bool</code></dt>\n          <dd>If true, the dialog will be closed when the user presses\n          enter in the input. Defaults to <code>true</code>.</dd>\n          <dt><code><strong>closeOnBlur</strong>: bool</code></dt>\n          <dd>Determines whether the dialog is closed when it loses focus. Defaults to <code>true</code>.</dd>\n          <dt><code><strong>onKeyDown</strong>: fn(event: KeyboardEvent, value: string, close: fn()) → bool</code></dt>\n          <dd>An event handler that will be called whenever <code>keydown</code> fires in the\n          dialog's input. If your callback returns <code>true</code>,\n          the dialog will not do any further processing of the event.</dd>\n          <dt><code><strong>onKeyUp</strong>: fn(event: KeyboardEvent, value: string, close: fn()) → bool</code></dt>\n          <dd>Same as <code>onKeyDown</code> but for the\n          <code>keyup</code> event.</dd>\n          <dt><code><strong>onInput</strong>: fn(event: InputEvent, value: string, close: fn()) → bool</code></dt>\n          <dd>Same as <code>onKeyDown</code> but for the\n          <code>input</code> event.</dd>\n          <dt><code><strong>onClose</strong>: fn(instance)</code>:</dt>\n          <dd>A callback that will be called after the dialog has been closed and\n          removed from the DOM. No return value.</dd>\n        </dl>\n\n      <p>Also adds an <strong><code>openNotification(template, options) →\n      closeFunction</code></strong> function that simply shows an HTML\n      fragment as a notification at the top of the editor. It takes a\n      single option: <code>duration</code>, the amount of time after\n      which the notification will be automatically closed. If <code>\n      duration</code> is zero, the dialog will not be closed automatically.</p>\n\n      <p>Depends on <code>addon/dialog/dialog.css</code>.</p></dd>\n\n      <dt id=\"addon_searchcursor\"><a href=\"../addon/search/searchcursor.js\"><code>search/searchcursor.js</code></a></dt>\n      <dd>Adds the <code>getSearchCursor(query, start, options) →\n      cursor</code> method to CodeMirror instances, which can be used\n      to implement search/replace functionality. <code>query</code>\n      can be a regular expression or a string. <code>start</code>\n      provides the starting position of the search. It can be\n      a <code>{line, ch}</code> object, or can be left off to default\n      to the start of the document. <code>options</code> is an\n      optional object, which can contain the property `caseFold:\n      false` to disable case folding when matching a string, or the\n      property `multiline: disable` to disable multi-line matching for\n      regular expressions (which may help performance). A search\n      cursor has the following methods:\n        <dl>\n          <dt><code><strong>findNext</strong>() → boolean</code></dt>\n          <dt><code><strong>findPrevious</strong>() → boolean</code></dt>\n          <dd>Search forward or backward from the current position.\n          The return value indicates whether a match was found. If\n          matching a regular expression, the return value will be the\n          array returned by the <code>match</code> method, in case you\n          want to extract matched groups.</dd>\n          <dt><code><strong>from</strong>() → {line, ch}</code></dt>\n          <dt><code><strong>to</strong>() → {line, ch}</code></dt>\n          <dd>These are only valid when the last call\n          to <code>findNext</code> or <code>findPrevious</code> did\n          not return false. They will return <code>{line, ch}</code>\n          objects pointing at the start and end of the match.</dd>\n          <dt><code><strong>replace</strong>(text: string, ?origin: string)</code></dt>\n          <dd>Replaces the currently found match with the given text\n          and adjusts the cursor position to reflect the\n          replacement.</dd>\n        </dl></dd>\n\n      <dt id=\"addon_search\"><a href=\"../addon/search/search.js\"><code>search/search.js</code></a></dt>\n      <dd>Implements the search commands. CodeMirror has keys bound to\n      these by default, but will not do anything with them unless an\n      implementation is provided. Depends\n      on <code>searchcursor.js</code>, and will make use\n      of <a href=\"#addon_dialog\"><code>openDialog</code></a> when\n      available to make prompting for search queries less ugly.</dd>\n\n      <dt id=\"addon_jump-to-line\"><a href=\"../addon/search/jump-to-line.js\"><code>search/jump-to-line.js</code></a></dt>\n      <dd>Implements a <code>jumpToLine</code> command and binding <code>Alt-G</code> to it.\n      Accepts <code>linenumber</code>, <code>+/-linenumber</code>, <code>line:char</code>,\n      <code>scroll%</code> and <code>:linenumber</code> formats.\n      This will make use of <a href=\"#addon_dialog\"><code>openDialog</code></a>\n      when available to make prompting for line number neater.</dd> Demo available <a href=\"https://codemirror.net/5/demo/search.html\">here</a>.\n\n      <dt id=\"addon_matchesonscrollbar\"><a href=\"../addon/search/matchesonscrollbar.js\"><code>search/matchesonscrollbar.js</code></a></dt>\n      <dd>Adds a <code>showMatchesOnScrollbar</code> method to editor\n      instances, which should be given a query (string or regular\n      expression), optionally a case-fold flag (only applicable for\n      strings), and optionally a class name (defaults\n      to <code>CodeMirror-search-match</code>) as arguments. When\n      called, matches of the given query will be displayed on the\n      editor's vertical scrollbar. The method returns an object with\n      a <code>clear</code> method that can be called to remove the\n      matches. Depends on\n      the <a href=\"#addon_annotatescrollbar\"><code>annotatescrollbar</code></a>\n      addon, and\n      the <a href=\"../addon/search/matchesonscrollbar.css\"><code>matchesonscrollbar.css</code></a>\n      file provides a default (transparent yellowish) definition of\n      the CSS class applied to the matches. Note that the matches are\n      only perfectly aligned if your scrollbar does not have buttons\n      at the top and bottom. You can use\n      the <a href=\"#addon_simplescrollbars\"><code>simplescrollbar</code></a>\n      addon to make sure of this. If this addon is loaded,\n      the <a href=\"#addon_search\"><code>search</code></a> addon will\n      automatically use it.</dd>\n\n      <dt id=\"addon_matchbrackets\"><a href=\"../addon/edit/matchbrackets.js\"><code>edit/matchbrackets.js</code></a></dt>\n      <dd>Defines an option <code>matchBrackets</code> which, when set\n      to true or an options object, causes matching brackets to be\n      highlighted whenever the cursor is next to them. It also adds a\n      method <code>matchBrackets</code> that forces this to happen\n      once, and a method <code>findMatchingBracket</code> that can be\n      used to run the bracket-finding algorithm that this uses\n      internally. It takes a start position and an optional config\n      object. By default, it will find the match to a matchable\n      character either before or after the cursor (preferring the one\n      before), but you can control its behavior with these options:\n        <dl>\n          <dt><strong><code>afterCursor</code></strong></dt>\n          <dd>Only use the character after the start position, never the one before it.</dd>\n          <dt><strong><code>highlightNonMatching</code></strong></dt>\n          <dd>Also highlight pairs of non-matching as well as stray brackets. Enabled by default.</dd>\n          <dt><strong><code>strict</code></strong></dt>\n          <dd>Causes only matches where both brackets are at the same side of the start position to be considered.</dd>\n          <dt><strong><code>maxScanLines</code></strong></dt>\n          <dd>Stop after scanning this amount of lines without a successful match. Defaults to 1000.</dd>\n          <dt><strong><code>maxScanLineLength</code></strong></dt>\n          <dd>Ignore lines longer than this. Defaults to 10000.</dd>\n          <dt><strong><code>maxHighlightLineLength</code></strong></dt>\n          <dd>Don't highlight a bracket in a line longer than this. Defaults to 1000.</dd>\n        </dl></dd>\n\n      <dt id=\"addon_closebrackets\"><a href=\"../addon/edit/closebrackets.js\"><code>edit/closebrackets.js</code></a></dt>\n      <dd>Defines an option <code>autoCloseBrackets</code> that will\n      auto-close brackets and quotes when typed. By default, it'll\n      auto-close <code>()[]{}''\"\"</code>, but you can pass it a string\n      similar to that (containing pairs of matching characters), or an\n      object with <code>pairs</code> and\n      optionally <code>explode</code> properties to customize\n      it. <code>explode</code> should be a similar string that gives\n      the pairs of characters that, when enter is pressed between\n      them, should have the second character also moved to its own\n      line. By default, if the active mode has\n      a <code>closeBrackets</code> property, that overrides the\n      configuration given in the option. But you can add\n      an <code>override</code> property with a truthy value to\n      override mode-specific\n      configuration. <a href=\"../demo/closebrackets.html\">Demo\n      here</a>.</dd>\n\n      <dt id=\"addon_matchtags\"><a href=\"../addon/edit/matchtags.js\"><code>edit/matchtags.js</code></a></dt>\n      <dd>Defines an option <code>matchTags</code> that, when enabled,\n      will cause the tags around the cursor to be highlighted (using\n      the <code>CodeMirror-matchingtag</code> class). Also\n      defines\n      a <a href=\"#commands\">command</a> <code>toMatchingTag</code>,\n      which you can bind a key to in order to jump to the tag matching\n      the one under the cursor. Depends on\n      the <code>addon/fold/xml-fold.js</code>\n      addon. <a href=\"../demo/matchtags.html\">Demo here.</a></dd>\n\n      <dt id=\"addon_trailingspace\"><a href=\"../addon/edit/trailingspace.js\"><code>edit/trailingspace.js</code></a></dt>\n      <dd>Adds an option <code>showTrailingSpace</code> which, when\n      enabled, adds the CSS class <code>cm-trailingspace</code> to\n      stretches of whitespace at the end of lines.\n      The <a href=\"../demo/trailingspace.html\">demo</a> has a nice\n      squiggly underline style for this class.</dd>\n\n      <dt id=\"addon_closetag\"><a href=\"../addon/edit/closetag.js\"><code>edit/closetag.js</code></a></dt>\n      <dd>Defines an <code>autoCloseTags</code> option that will\n      auto-close XML tags when '<code>&gt;</code>' or '<code>/</code>'\n      is typed, and\n      a <code>closeTag</code> <a href=\"#commands\">command</a> that\n      closes the nearest open tag. Depends on\n      the <code>fold/xml-fold.js</code> addon. See\n      the <a href=\"../demo/closetag.html\">demo</a>.</dd>\n\n      <dt id=\"addon_continuelist\"><a href=\"../addon/edit/continuelist.js\"><code>edit/continuelist.js</code></a></dt>\n      <dd>Markdown specific. Defines\n      a <code>\"newlineAndIndentContinueMarkdownList\"</code> <a href=\"#commands\">command</a>\n      that can be bound to <code>enter</code> to automatically\n      insert the leading characters for continuing a list. See\n      the <a href=\"../mode/markdown/index.html\">Markdown mode\n      demo</a>.</dd>\n\n      <dt id=\"addon_comment\"><a href=\"../addon/comment/comment.js\"><code>comment/comment.js</code></a></dt>\n      <dd>Addon for commenting and uncommenting code. Adds four\n      methods to CodeMirror instances:\n      <dl>\n        <dt id=\"toggleComment\"><code><strong>toggleComment</strong>(?options: object)</code></dt>\n        <dd>Tries to uncomment the current selection, and if that\n        fails, line-comments it.</dd>\n        <dt id=\"lineComment\"><code><strong>lineComment</strong>(from: {line, ch}, to: {line, ch}, ?options: object)</code></dt>\n        <dd>Set the lines in the given range to be line comments. Will\n        fall back to <code>blockComment</code> when no line comment\n        style is defined for the mode.</dd>\n        <dt id=\"blockComment\"><code><strong>blockComment</strong>(from: {line, ch}, to: {line, ch}, ?options: object)</code></dt>\n        <dd>Wrap the code in the given range in a block comment. Will\n        fall back to <code>lineComment</code> when no block comment\n        style is defined for the mode.</dd>\n        <dt id=\"uncomment\"><code><strong>uncomment</strong>(from: {line, ch}, to: {line, ch}, ?options: object) → boolean</code></dt>\n        <dd>Try to uncomment the given range.\n          Returns <code>true</code> if a comment range was found and\n          removed, <code>false</code> otherwise.</dd>\n      </dl>\n      The <code>options</code> object accepted by these methods may\n      have the following properties:\n      <dl>\n        <dt><code>blockCommentStart, blockCommentEnd, blockCommentLead, lineComment: string</code></dt>\n        <dd>Override the <a href=\"#mode_comment\">comment string\n        properties</a> of the mode with custom comment strings.</dd>\n        <dt><code><strong>padding</strong>: string</code></dt>\n        <dd>A string that will be inserted after opening and leading\n        markers, and before closing comment markers. Defaults to a\n        single space.</dd>\n        <dt><code><strong>commentBlankLines</strong>: boolean</code></dt>\n        <dd>Whether, when adding line comments, to also comment lines\n        that contain only whitespace.</dd>\n        <dt><code><strong>indent</strong>: boolean</code></dt>\n        <dd>When adding line comments and this is turned on, it will\n        align the comment block to the current indentation of the\n        first line of the block.</dd>\n        <dt><code><strong>fullLines</strong>: boolean</code></dt>\n        <dd>When block commenting, this controls whether the whole\n        lines are indented, or only the precise range that is given.\n        Defaults to <code>true</code>.</dd>\n      </dl>\n      The addon also defines\n      a <code>toggleComment</code> <a href=\"#commands\">command</a>,\n      which is a shorthand command for calling\n      <code>toggleComment</code> with no options.</dd>\n\n      <dt id=\"addon_foldcode\"><a href=\"../addon/fold/foldcode.js\"><code>fold/foldcode.js</code></a></dt>\n      <dd>Helps with code folding. Adds a <code>foldCode</code> method\n      to editor instances, which will try to do a code fold starting\n      at the given line, or unfold the fold that is already present.\n      The method takes as first argument the position that should be\n      folded (may be a line number or\n      a <a href=\"#Pos\"><code>Pos</code></a>), and as second optional\n      argument either a range-finder function, or an options object,\n      supporting the following properties:\n      <dl>\n        <dt><code><strong>rangeFinder</strong>: fn(CodeMirror, Pos)</code></dt>\n        <dd id=\"helper_fold_auto\">The function that is used to find\n        foldable ranges. If this is not directly passed, it will\n        default to <code>CodeMirror.fold.auto</code>, which\n        uses <a href=\"#getHelpers\"><code>getHelpers</code></a> with\n        a <code>\"fold\"</code> type to find folding functions\n        appropriate for the local mode. There are files in\n        the <a href=\"../addon/fold/\"><code>addon/fold/</code></a>\n        directory providing <code>CodeMirror.fold.brace</code>, which\n        finds blocks in brace languages (JavaScript, C, Java,\n        etc), <code>CodeMirror.fold.indent</code>, for languages where\n        indentation determines block structure (Python, Haskell),\n        and <code>CodeMirror.fold.xml</code>, for XML-style languages,\n        and <code>CodeMirror.fold.comment</code>, for folding comment\n        blocks.</dd>\n        <dt><code><strong>widget</strong>: string | Element | fn(from: Pos, to: Pos) → string|Element</code></dt>\n        <dd>The widget to show for folded ranges. Can be either a\n        string, in which case it'll become a span with\n        class <code>CodeMirror-foldmarker</code>, or a DOM node.\n        To dynamically generate the widget, this can be a function\n        that returns a string or DOM node, which will then render\n        as described. The function will be invoked with parameters\n        identifying the range to be folded.</dd>\n        <dt><code><strong>scanUp</strong>: boolean</code></dt>\n        <dd>When true (default is false), the addon will try to find\n        foldable ranges on the lines above the current one if there\n        isn't an eligible one on the given line.</dd>\n        <dt><code><strong>minFoldSize</strong>: integer</code></dt>\n        <dd>The minimum amount of lines that a fold should span to be\n        accepted. Defaults to 0, which also allows single-line\n        folds.</dd>\n      </dl>\n      See <a href=\"../demo/folding.html\">the demo</a> for an\n      example.</dd>\n\n      <dt id=\"addon_foldgutter\"><a href=\"../addon/fold/foldgutter.js\"><code>fold/foldgutter.js</code></a></dt>\n      <dd>Provides an option <code>foldGutter</code>, which can be\n      used to create a gutter with markers indicating the blocks that\n      can be folded. Create a gutter using\n      the <a href=\"#option_gutters\"><code>gutters</code></a> option,\n      giving it the class <code>CodeMirror-foldgutter</code> or\n      something else if you configure the addon to use a different\n      class, and this addon will show markers next to folded and\n      foldable blocks, and handle clicks in this gutter. Note that\n      CSS styles should be applied to make the gutter, and the fold\n      markers within it, visible. A default set of CSS styles are\n      available in:\n      <a href=\"../addon/fold/foldgutter.css\">\n        <code>addon/fold/foldgutter.css</code>\n      </a>.\n      The option\n      can be either set to <code>true</code>, or an object containing\n      the following optional option fields:\n      <dl>\n        <dt><code><strong>gutter</strong>: string</code></dt>\n        <dd>The CSS class of the gutter. Defaults\n        to <code>\"CodeMirror-foldgutter\"</code>. You will have to\n        style this yourself to give it a width (and possibly a\n        background). See the default gutter style rules above.</dd>\n        <dt><code><strong>indicatorOpen</strong>: string | Element</code></dt>\n        <dd>A CSS class or DOM element to be used as the marker for\n        open, foldable blocks. Defaults\n        to <code>\"CodeMirror-foldgutter-open\"</code>.</dd>\n        <dt><code><strong>indicatorFolded</strong>: string | Element</code></dt>\n        <dd>A CSS class or DOM element to be used as the marker for\n        folded blocks. Defaults to <code>\"CodeMirror-foldgutter-folded\"</code>.</dd>\n        <dt><code><strong>rangeFinder</strong>: fn(CodeMirror, Pos)</code></dt>\n        <dd>The range-finder function to use when determining whether\n        something can be folded. When not\n        given, <a href=\"#helper_fold_auto\"><code>CodeMirror.fold.auto</code></a>\n        will be used as default.</dd>\n      </dl>\n      The <code>foldOptions</code> editor option can be set to an\n      object to provide an editor-wide default configuration.\n      Demo <a href=\"../demo/folding.html\">here</a>.</dd>\n\n      <dt id=\"addon_runmode\"><a href=\"../addon/runmode/runmode.js\"><code>runmode/runmode.js</code></a></dt>\n      <dd>Can be used to run a CodeMirror mode over text without\n      actually opening an editor instance.\n      See <a href=\"../demo/runmode.html\">the demo</a> for an example.\n      There are alternate versions of the file available for\n      running <a href=\"../addon/runmode/runmode-standalone.js\">stand-alone</a>\n      (without including all of CodeMirror) and\n      for <a href=\"../addon/runmode/runmode.node.js\">running under\n      node.js</a> (see <code>bin/source-highlight</code> for an example of using the latter).</dd>\n\n      <dt id=\"addon_colorize\"><a href=\"../addon/runmode/colorize.js\"><code>runmode/colorize.js</code></a></dt>\n      <dd>Provides a convenient way to syntax-highlight code snippets\n      in a webpage. Depends on\n      the <a href=\"#addon_runmode\"><code>runmode</code></a> addon (or\n      its standalone variant). Provides\n      a <code>CodeMirror.colorize</code> function that can be called\n      with an array (or other array-ish collection) of DOM nodes that\n      represent the code snippets. By default, it'll get\n      all <code>pre</code> tags. Will read the <code>data-lang</code>\n      attribute of these nodes to figure out their language, and\n      syntax-color their content using the relevant CodeMirror mode\n      (you'll have to load the scripts for the relevant modes\n      yourself). A second argument may be provided to give a default\n      mode, used when no language attribute is found for a node. Used\n      in this manual to highlight example code.</dd>\n\n      <dt id=\"addon_overlay\"><a href=\"../addon/mode/overlay.js\"><code>mode/overlay.js</code></a></dt>\n      <dd>Mode combinator that can be used to extend a mode with an\n      'overlay' — a secondary mode is run over the stream, along with\n      the base mode, and can color specific pieces of text without\n      interfering with the base mode.\n      Defines <code>CodeMirror.overlayMode</code>, which is used to\n      create such a mode. See <a href=\"../demo/mustache.html\">this\n      demo</a> for a detailed example.</dd>\n\n      <dt id=\"addon_multiplex\"><a href=\"../addon/mode/multiplex.js\"><code>mode/multiplex.js</code></a></dt>\n      <dd>Mode combinator that can be used to easily 'multiplex'\n      between several modes.\n      Defines <code>CodeMirror.multiplexingMode</code> which, when\n      given as first argument a mode object, and as other arguments\n      any number of <code>{open, close, mode [, delimStyle, innerStyle, parseDelimiters]}</code>\n      objects, will return a mode object that starts parsing using the\n      mode passed as first argument, but will switch to another mode\n      as soon as it encounters a string that occurs in one of\n      the <code>open</code> fields of the passed objects. When in a\n      sub-mode, it will go back to the top mode again when\n      the <code>close</code> string is encountered.\n      Pass <code>\"\\n\"</code> for <code>open</code> or <code>close</code>\n      if you want to switch on a blank line.\n      <ul><li>When <code>delimStyle</code> is specified, it will be the token\n      style returned for the delimiter tokens (as well as\n      <code>[delimStyle]-open</code> on the opening token and\n      <code>[delimStyle]-close</code> on the closing token).</li>\n      <li>When <code>innerStyle</code> is specified, it will be the token\n      style added for each inner mode token.</li>\n      <li>When <code>parseDelimiters</code> is true, the content of\n      the delimiters will also be passed to the inner mode.\n      (And <code>delimStyle</code> is ignored.)</li></ul> The outer\n      mode will not see the content between the delimiters.\n      See <a href=\"../demo/multiplex.html\">this demo</a> for an\n      example.</dd>\n\n      <dt id=\"addon_show-hint\"><a href=\"../addon/hint/show-hint.js\"><code>hint/show-hint.js</code></a></dt>\n      <dd>Provides a framework for showing autocompletion hints.\n      Defines <code>editor.showHint</code>, which takes an optional\n      options object, and pops up a widget that allows the user to\n      select a completion. Finding hints is done with a hinting\n      function (the <code>hint</code> option). This function\n      takes an editor instance and an options object, and returns\n      a <code>{list, from, to}</code> object, where <code>list</code>\n      is an array of strings or objects (the completions),\n      and <code>from</code> and <code>to</code> give the start and end\n      of the token that is being completed as <code>{line, ch}</code>\n      objects. An optional <code>selectedHint</code> property (an\n      integer) can be added to the completion object to control the\n      initially selected hint.</dd>\n      <dd>If no hinting function is given, the addon will\n      use <code>CodeMirror.hint.auto</code>, which\n      calls <a href=\"#getHelpers\"><code>getHelpers</code></a> with\n      the <code>\"hint\"</code> type to find applicable hinting\n      functions, and tries them one by one. If that fails, it looks\n      for a <code>\"hintWords\"</code> helper to fetch a list of\n      completeable words for the mode, and\n      uses <code>CodeMirror.hint.fromList</code> to complete from\n      those.</dd>\n      <dd>When completions aren't simple strings, they should be\n      objects with the following properties:\n      <dl>\n        <dt><code><strong>text</strong>: string</code></dt>\n        <dd>The completion text. This is the only required\n        property.</dd>\n        <dt><code><strong>displayText</strong>: string</code></dt>\n        <dd>The text that should be displayed in the menu.</dd>\n        <dt><code><strong>className</strong>: string</code></dt>\n        <dd>A CSS class name to apply to the completion's line in the\n        menu.</dd>\n        <dt><code><strong>render</strong>: fn(Element, self, data)</code></dt>\n        <dd>A method used to create the DOM structure for showing the\n        completion by appending it to its first argument.</dd>\n        <dt><code><strong>hint</strong>: fn(CodeMirror, self, data)</code></dt>\n        <dd>A method used to actually apply the completion, instead of\n        the default behavior.</dd>\n        <dt><code><strong>from</strong>: {line, ch}</code></dt>\n        <dd>Optional <code>from</code> position that will be used by <code>pick()</code> instead\n        of the global one passed with the full list of completions.</dd>\n        <dt><code><strong>to</strong>: {line, ch}</code></dt>\n        <dd>Optional <code>to</code> position that will be used by <code>pick()</code> instead\n        of the global one passed with the full list of completions.</dd>\n      </dl></dd>\n\n      <dd>The plugin understands the following options, which may be\n      either passed directly in the argument to <code>showHint</code>,\n      or provided by setting an <code>hintOptions</code> editor\n      option to an object (the former takes precedence). The options\n      object will also be passed along to the hinting function, which\n      may understand additional options.\n      <dl>\n        <dt><code><strong>hint</strong>: function</code></dt>\n        <dd>A hinting function, as specified above. It is possible to\n        set the <code>async</code> property on a hinting function to\n        true, in which case it will be called with\n        arguments <code>(cm, callback, ?options)</code>, and the\n        completion interface will only be popped up when the hinting\n        function calls the callback, passing it the object holding the\n        completions.\n        The hinting function can also return a promise, and the completion\n        interface will only be popped when the promise resolves.\n        By default, hinting only works when there is no\n        selection. You can give a hinting function\n        a <code>supportsSelection</code> property with a truthy value\n        to indicate that it supports selections.</dd>\n        <dt><code><strong>completeSingle</strong>: boolean</code></dt>\n        <dd>Determines whether, when only a single completion is\n        available, it is completed without showing the dialog.\n        Defaults to true.</dd>\n        <dt><code><strong>alignWithWord</strong>: boolean</code></dt>\n        <dd>Whether the pop-up should be horizontally aligned with the\n        start of the word (true, default), or with the cursor (false).</dd>\n        <dt><code><strong>closeCharacters</strong>: RegExp</code></dt>\n        <dd>A regular expression object used to match characters which\n        cause the pop up to be closed (default: <code>/[\\s()\\[\\]{};:>,]/</code>).\n        If the user types one of these characters, the pop up will close, and\n        the <code>endCompletion</code> event is fired on the editor instance.</dd>\n        <dt><code><strong>closeOnUnfocus</strong>: boolean</code></dt>\n        <dd>When enabled (which is the default), the pop-up will close\n        when the editor is unfocused.</dd>\n        <dt><code><strong>completeOnSingleClick</strong>: boolean</code></dt>\n        <dd>Whether a single click on a list item suffices to trigger the\n        completion (which is the default), or if the user has to use a\n        doubleclick.</dd>\n        <dt><code><strong>container</strong>: Element|null</code></dt>\n        <dd>Can be used to define a custom container for the widget. The default\n        is <code>null</code>, in which case the <code>body</code>-element will\n        be used.</dd>\n        <dt><code><strong>customKeys</strong>: keymap</code></dt>\n        <dd>Allows you to provide a custom key map of keys to be active\n        when the pop-up is active. The handlers will be called with an\n        extra argument, a handle to the completion menu, which\n        has <code>moveFocus(n)</code>, <code>setFocus(n)</code>, <code>pick()</code>,\n        and <code>close()</code> methods (see the source for details),\n        that can be used to change the focused element, pick the\n        current element or close the menu. Additionally <code>menuSize()</code>\n        can give you access to the size of the current dropdown menu,\n        <code>length</code> give you the number of available completions, and\n        <code>data</code> give you full access to the completion returned by the\n        hinting function.</dd>\n        <dt><code><strong>extraKeys</strong>: keymap</code></dt>\n        <dd>Like <code>customKeys</code> above, but the bindings will\n        be added to the set of default bindings, instead of replacing\n        them.</dd>\n        <dt><code><strong>scrollMargin</strong>: integer</code></dt>\n        <dd>Show this many lines before and after the selected item.\n        Default is 0.</dd>\n      </dl>\n      The following events will be fired on the completions object\n      during completion:\n      <dl>\n        <dt><code><strong>\"shown\"</strong> ()</code></dt>\n        <dd>Fired when the pop-up is shown.</dd>\n        <dt><code><strong>\"select\"</strong> (completion, Element)</code></dt>\n        <dd>Fired when a completion is selected. Passed the completion\n        value (string or object) and the DOM node that represents it\n        in the menu.</dd>\n        <dt><code><strong>\"pick\"</strong> (completion)</code></dt>\n        <dd>Fired when a completion is picked. Passed the completion value\n        (string or object).</dd>\n        <dt><code><strong>\"close\"</strong> ()</code></dt>\n        <dd>Fired when the completion is finished.</dd>\n      </dl>\n      The following events will be fired on the editor instance during\n      completion:\n      <dl>\n        <dt><code><strong>\"endCompletion\"</strong> ()</code></dt>\n        <dd>Fired when the pop-up is being closed programmatically, e.g., when\n        the user types a character which matches the\n        <code>closeCharacters</code> option.</dd>\n      </dl>\n      This addon depends on styles\n      from <code>addon/hint/show-hint.css</code>. Check\n      out <a href=\"../demo/complete.html\">the demo</a> for an\n      example.</dd>\n\n      <dt id=\"addon_javascript-hint\"><a href=\"../addon/hint/javascript-hint.js\"><code>hint/javascript-hint.js</code></a></dt>\n      <dd>Defines a simple hinting function for JavaScript\n      (<code>CodeMirror.hint.javascript</code>) and CoffeeScript\n      (<code>CodeMirror.hint.coffeescript</code>) code. This will\n      simply use the JavaScript environment that the editor runs in as\n      a source of information about objects and their properties.</dd>\n\n      <dt id=\"addon_xml-hint\"><a href=\"../addon/hint/xml-hint.js\"><code>hint/xml-hint.js</code></a></dt>\n      <dd>Defines <code>CodeMirror.hint.xml</code>, which produces\n      hints for XML tagnames, attribute names, and attribute values,\n      guided by a <code>schemaInfo</code> option (a property of the\n      second argument passed to the hinting function, or the third\n      argument passed to <code>CodeMirror.showHint</code>).<br>The\n      schema info should be an object mapping tag names to information\n      about these tags, with optionally a <code>\"!top\"</code> property\n      containing a list of the names of valid top-level tags. The\n      values of the properties should be objects with optional\n      properties <code>children</code> (an array of valid child\n      element names, omit to simply allow all tags to appear)\n      and <code>attrs</code> (an object mapping attribute names\n      to <code>null</code> for free-form attributes, and an array of\n      valid values for restricted\n      attributes).<br>The hint options accept an additional property:\n      <dl>\n        <dt><code><strong>matchInMiddle</strong>: boolean</code></dt>\n        <dd>Determines whether typed characters are matched anywhere in\n        completions, not just at the beginning. Defaults to false.</dd>\n      </dl>\n      <a href=\"../demo/xmlcomplete.html\">Demo here</a>.</dd>\n\n      <dt id=\"addon_html-hint\"><a href=\"../addon/hint/html-hint.js\"><code>hint/html-hint.js</code></a></dt>\n      <dd>Provides schema info to\n      the <a href=\"#addon_xml-hint\">xml-hint</a> addon for HTML\n      documents. Defines a schema\n      object <code>CodeMirror.htmlSchema</code> that you can pass to\n      as a <code>schemaInfo</code> option, and\n      a <code>CodeMirror.hint.html</code> hinting function that\n      automatically calls <code>CodeMirror.hint.xml</code> with this\n      schema data. See\n      the <a href=\"../demo/html5complete.html\">demo</a>.</dd>\n\n      <dt id=\"addon_css-hint\"><a href=\"../addon/hint/css-hint.js\"><code>hint/css-hint.js</code></a></dt>\n      <dd>A hinting function for CSS, SCSS, or LESS code.\n      Defines <code>CodeMirror.hint.css</code>.</dd>\n\n      <dt id=\"addon_anyword-hint\"><a href=\"../addon/hint/anyword-hint.js\"><code>hint/anyword-hint.js</code></a></dt>\n      <dd>A very simple hinting function\n      (<code>CodeMirror.hint.anyword</code>) that simply looks for\n      words in the nearby code and completes to those. Takes two\n      optional options, <code>word</code>, a regular expression that\n      matches words (sequences of one or more character),\n      and <code>range</code>, which defines how many lines the addon\n      should scan when completing (defaults to 500).</dd>\n\n      <dt id=\"addon_sql-hint\"><a href=\"../addon/hint/sql-hint.js\"><code>hint/sql-hint.js</code></a></dt>\n      <dd>A simple SQL hinter. Defines <code>CodeMirror.hint.sql</code>.\n      Takes two optional options, <code>tables</code>, a object with\n      table names as keys and array of respective column names as values,\n      and <code>defaultTable</code>, a string corresponding to a\n      table name in <code>tables</code> for autocompletion.</dd>\n\n      <dt id=\"addon_match-highlighter\"><a href=\"../addon/search/match-highlighter.js\"><code>search/match-highlighter.js</code></a></dt>\n      <dd>Adds a <code>highlightSelectionMatches</code> option that\n      can be enabled to highlight all instances of a currently\n      selected word. Can be set either to true or to an object\n      containing the following options: <code>minChars</code>, for the\n      minimum amount of selected characters that triggers a highlight\n      (default 2), <code>style</code>, for the style to be used to\n      highlight the matches (default <code>\"matchhighlight\"</code>,\n      which will correspond to CSS\n      class <code>cm-matchhighlight</code>), <code>trim</code>, which\n      controls whether whitespace is trimmed from the selection,\n      and <code>showToken</code> which can be set to <code>true</code>\n      or to a regexp matching the characters that make up a word. When\n      enabled, it causes the current word to be highlighted when\n      nothing is selected (defaults to off).\n      Demo <a href=\"../demo/matchhighlighter.html\">here</a>.</dd>\n\n      <dt id=\"addon_lint\"><a href=\"../addon/lint/lint.js\"><code>lint/lint.js</code></a></dt>\n      <dd>Defines an interface component for showing linting warnings,\n      with pluggable warning sources\n      (see <a href=\"../addon/lint/html-lint.js\"><code>html-lint.js</code></a>,\n      <a href=\"../addon/lint/json-lint.js\"><code>json-lint.js</code></a>,\n      <a href=\"../addon/lint/javascript-lint.js\"><code>javascript-lint.js</code></a>,\n      <a href=\"../addon/lint/coffeescript-lint.js\"><code>coffeescript-lint.js</code></a>,\n      and <a href=\"../addon/lint/css-lint.js\"><code>css-lint.js</code></a>\n      in the same directory). Defines a <code>lint</code> option that\n      can be set to an annotation source (for\n      example <code>CodeMirror.lint.javascript</code>), to an options\n      object (in which case the <code>getAnnotations</code> field is\n      used as annotation source), or simply to <code>true</code>. When\n      no annotation source is\n      specified, <a href=\"#getHelper\"><code>getHelper</code></a> with\n      type <code>\"lint\"</code> is used to find an annotation function.\n      An annotation source function should, when given a document\n      string, an options object, and an editor instance, return an\n      array of <code>{message, severity, from, to}</code> objects\n      representing problems. When the function has\n      an <code>async</code> property with a truthy value, it will be\n      called with an additional second argument, which is a callback\n      to pass the array to.\n      The linting function can also return a promise, in that case the linter\n      will only be executed when the promise resolves.\n      By default, the linter will run (debounced) whenever the document is changed.\n      You can pass a <code>lintOnChange: false</code> option to disable that.\n      You can pass a <code>selfContain: true</code> option to render the tooltip inside the editor instance.\n      And a <code>highlightLines</code> option to add a style to lines that contain problems.\n      Depends on <code>addon/lint/lint.css</code>. A demo can be\n      found <a href=\"../demo/lint.html\">here</a>.</dd>\n\n      <dt id=\"addon_mark-selection\"><a href=\"../addon/selection/mark-selection.js\"><code>selection/mark-selection.js</code></a></dt>\n      <dd>Causes the selected text to be marked with the CSS class\n      <code>CodeMirror-selectedtext</code> when the <code>styleSelectedText</code> option\n      is enabled. Useful to change the colour of the selection (in addition to the background),\n      like in <a href=\"../demo/markselection.html\">this demo</a>.</dd>\n\n      <dt id=\"addon_active-line\"><a href=\"../addon/selection/active-line.js\"><code>selection/active-line.js</code></a></dt>\n      <dd>Defines a <code>styleActiveLine</code> option that, when\n      enabled, gives the wrapper of the line that contains the cursor\n      the class <code>CodeMirror-activeline</code>, adds a background\n      with the class <code>CodeMirror-activeline-background</code>,\n      and adds the class <code>CodeMirror-activeline-gutter</code> to\n      the line's gutter space is enabled. The option's value may be a\n      boolean or an object specifying the following options:\n        <dl>\n          <dt><code><strong>nonEmpty</strong>: bool</code></dt>\n          <dd>Controls whether single-line selections, or just cursor\n          selections, are styled. Defaults to false (only cursor\n          selections).</dd>\n        </dl>\n      See the <a href=\"../demo/activeline.html\">demo</a>.</dd>\n\n      <dt id=\"addon_selection-pointer\"><a href=\"../addon/selection/selection-pointer.js\"><code>selection/selection-pointer.js</code></a></dt>\n      <dd>Defines a <code>selectionPointer</code> option which you can\n      use to control the mouse cursor appearance when hovering over\n      the selection. It can be set to a string,\n      like <code>\"pointer\"</code>, or to true, in which case\n      the <code>\"default\"</code> (arrow) cursor will be used. You can\n      see a demo <a href=\"../mode/htmlmixed/index.html\">here</a>.</dd>\n\n      <dt id=\"addon_loadmode\"><a href=\"../addon/mode/loadmode.js\"><code>mode/loadmode.js</code></a></dt>\n      <dd>Defines a <code>CodeMirror.requireMode(modename, callback,\n      options)</code> function that will try to load a given mode and\n      call the callback when it succeeded. <code>options</code> is an\n      optional object that may contain:\n        <dl>\n          <dt><code><strong>path</strong>: fn(modeName: string) → string</code></dt>\n          <dd>Defines the way mode names are mapped to paths.</dd>\n          <dt><code><strong>loadMode</strong>: fn(path: string, cont: fn())</code></dt>\n          <dd>Override the way the mode script is loaded. By default,\n          this will use the CommonJS or AMD module loader if one is\n          present, and fall back to creating\n          a <code>&lt;script></code> tag otherwise.</dd>\n        </dl>\n      This addon also\n      defines <code>CodeMirror.autoLoadMode(instance, mode)</code>,\n      which will ensure the given mode is loaded and cause the given\n      editor instance to refresh its mode when the loading\n      succeeded. See the <a href=\"../demo/loadmode.html\">demo</a>.</dd>\n\n      <dt id=\"addon_meta\"><a href=\"../mode/meta.js\"><code>mode/meta.js</code></a></dt>\n      <dd>Provides meta-information about all the modes in the\n      distribution in a single file.\n      Defines <code>CodeMirror.modeInfo</code>, an array of objects\n      with <code>{name, mime, mode}</code> properties,\n      where <code>name</code> is the human-readable\n      name, <code>mime</code> the MIME type, and <code>mode</code> the\n      name of the mode file that defines this MIME. There are optional\n      properties <code>mimes</code>, which holds an array of MIME\n      types for modes with multiple MIMEs associated,\n      and <code>ext</code>, which holds an array of file extensions\n      associated with this mode. Four convenience\n      functions, <code>CodeMirror.findModeByMIME</code>,\n      <code>CodeMirror.findModeByExtension</code>,\n      <code>CodeMirror.findModeByFileName</code>\n      and <code>CodeMirror.findModeByName</code> are provided, which\n      return such an object given a MIME, extension, file name or mode name\n      string. Note that, for historical reasons, this file resides in the\n      top-level <code>mode</code> directory, not\n      under <code>addon</code>. <a href=\"../demo/loadmode.html\">Demo</a>.</dd>\n\n      <dt id=\"addon_continuecomment\"><a href=\"../addon/comment/continuecomment.js\"><code>comment/continuecomment.js</code></a></dt>\n      <dd>Adds a <code>continueComments</code> option, which sets whether the\n      editor will make the next line continue a comment when you press Enter\n      inside a comment block. Can be set to a boolean to enable/disable this\n      functionality. Set to a string, it will continue comments using a custom\n      shortcut. Set to an object, it will use the <code>key</code> property for\n      a custom shortcut and the boolean <code>continueLineComment</code>\n      property to determine whether single-line comments should be continued\n      (defaulting to <code>true</code>).</dd>\n\n      <dt id=\"addon_placeholder\"><a href=\"../addon/display/placeholder.js\"><code>display/placeholder.js</code></a></dt>\n      <dd>Adds a <code>placeholder</code> option that can be used to\n      make content appear in the editor when it is empty and not\n      focused. It can hold either a string or a DOM node. Also gives\n      the editor a <code>CodeMirror-empty</code> CSS class whenever it\n      doesn't contain any text.\n      See <a href=\"../demo/placeholder.html\">the demo</a>.</dd>\n\n      <dt id=\"addon_fullscreen\"><a href=\"../addon/display/fullscreen.js\"><code>display/fullscreen.js</code></a></dt>\n      <dd>Defines an option <code>fullScreen</code> that, when set\n      to <code>true</code>, will make the editor full-screen (as in,\n      taking up the whole browser window). Depends\n      on <a href=\"../addon/display/fullscreen.css\"><code>fullscreen.css</code></a>. <a href=\"../demo/fullscreen.html\">Demo\n      here</a>.</dd>\n\n      <dt id=\"addon_autorefresh\"><a href=\"../addon/display/autorefresh.js\"><code>display/autorefresh.js</code></a></dt>\n      <dd>This addon can be useful when initializing an editor in a\n      hidden DOM node, in cases where it is difficult to\n      call <a href=\"#refresh\"><code>refresh</code></a> when the editor\n      becomes visible. It defines an option <code>autoRefresh</code>\n      which you can set to true to ensure that, if the editor wasn't\n      visible on initialization, it will be refreshed the first time\n      it becomes visible. This is done by polling every 250\n      milliseconds (you can pass a value like <code>{delay:\n      500}</code> as the option value to configure this). Note that\n      this addon will only refresh the editor <em>once</em> when it\n      first becomes visible, and won't take care of further restyling\n      and resizing.</dd>\n\n      <dt id=\"addon_simplescrollbars\"><a href=\"../addon/scroll/simplescrollbars.js\"><code>scroll/simplescrollbars.js</code></a></dt>\n      <dd>Defines two additional scrollbar\n      models, <code>\"simple\"</code> and <code>\"overlay\"</code>\n      (see <a href=\"../demo/simplescrollbars.html\">demo</a>) that can\n      be selected with\n      the <a href=\"#option_scrollbarStyle\"><code>scrollbarStyle</code></a>\n      option. Depends\n      on <a href=\"../addon/scroll/simplescrollbars.css\"><code>simplescrollbars.css</code></a>,\n      which can be further overridden to style your own\n      scrollbars.</dd>\n\n      <dt id=\"addon_annotatescrollbar\"><a href=\"../addon/scroll/annotatescrollbar.js\"><code>scroll/annotatescrollbar.js</code></a></dt>\n      <dd>Provides functionality for showing markers on the scrollbar\n      to call out certain parts of the document. Adds a\n      method <code>annotateScrollbar</code> to editor instances that\n      can be called, with a CSS class name as argument, to create a\n      set of annotations. The method returns an object\n      whose <code>update</code> method can be called with a sorted array\n      of <code>{from: Pos, to: Pos}</code> objects marking the ranges\n      to be highlighted. To detach the annotations, call the\n      object's <code>clear</code> method.</dd>\n\n      <dt id=\"addon_rulers\"><a href=\"../addon/display/rulers.js\"><code>display/rulers.js</code></a></dt>\n      <dd>Adds a <code>rulers</code> option, which can be used to show\n      one or more vertical rulers in the editor. The option, if\n      defined, should be given an array of <code>{column [, className,\n      color, lineStyle, width]}</code> objects or numbers (which\n      indicate a column). The ruler will be displayed at the column\n      indicated by the number or the <code>column</code> property.\n      The <code>className</code> property can be used to assign a\n      custom style to a ruler. <a href=\"../demo/rulers.html\">Demo\n      here</a>.</dd>\n\n      <dt id=\"addon_panel\"><a href=\"../addon/display/panel.js\"><code>display/panel.js</code></a></dt>\n      <dd>Defines an <code>addPanel</code> method for CodeMirror\n      instances, which places a DOM node above or below an editor, and\n      shrinks the editor to make room for the node. The method takes\n      as first argument as DOM node, and as second an optional options\n      object. The <code>Panel</code> object returned by this method\n      has a <code>clear</code> method that is used to remove the\n      panel, and a <code>changed</code> method that can be used to\n      notify the addon when the size of the panel's DOM node has\n      changed.<br/>\n      The method accepts the following options:\n      <dl>\n        <dt><code><strong>position</strong>: string</code></dt>\n        <dd>Controls the position of the newly added panel. The\n        following values are recognized:\n          <dl>\n            <dt><code><strong>top</strong> (default)</code></dt>\n            <dd>Adds the panel at the very top.</dd>\n            <dt><code><strong>after-top</strong></code></dt>\n            <dd>Adds the panel at the bottom of the top panels.</dd>\n            <dt><code><strong>bottom</strong></code></dt>\n            <dd>Adds the panel at the very bottom.</dd>\n            <dt><code><strong>before-bottom</strong></code></dt>\n            <dd>Adds the panel at the top of the bottom panels.</dd>\n          </dl>\n        </dd>\n        <dt><code><strong>before</strong>: Panel</code></dt>\n        <dd>The new panel will be added before the given panel.</dd>\n        <dt><code><strong>after</strong>: Panel</code></dt>\n        <dd>The new panel will be added after the given panel.</dd>\n        <dt><code><strong>replace</strong>: Panel</code></dt>\n        <dd>The new panel will replace the given panel.</dd>\n        <dt><code><strong>stable</strong>: bool</code></dt>\n        <dd>Whether to scroll the editor to keep the text's vertical\n        position stable, when adding a panel above it. Defaults to false.</dd>\n      </dl>\n      When using the <code>after</code>, <code>before</code> or <code>replace</code> options,\n      if the panel doesn't exists or has been removed,\n      the value of the <code>position</code> option will be used as a fallback.\n      <br>\n      A demo of the addon is available <a href=\"../demo/panel.html\">here</a>.\n      </dd>\n\n      <dt id=\"addon_hardwrap\"><a href=\"../addon/wrap/hardwrap.js\"><code>wrap/hardwrap.js</code></a></dt>\n      <dd>Addon to perform hard line wrapping/breaking for paragraphs\n      of text. Adds these methods to editor instances:\n        <dl>\n          <dt><code><strong>wrapParagraph</strong>(?pos: {line, ch}, ?options: object)</code></dt>\n          <dd>Wraps the paragraph at the given position.\n          If <code>pos</code> is not given, it defaults to the cursor\n          position.</dd>\n          <dt><code><strong>wrapRange</strong>(from: {line, ch}, to: {line, ch}, ?options: object)</code></dt>\n          <dd>Wraps the given range as one big paragraph.</dd>\n          <dt><code><strong>wrapParagraphsInRange</strong>(from: {line, ch}, to: {line, ch}, ?options: object)</code></dt>\n          <dd>Wraps the paragraphs in (and overlapping with) the\n          given range individually.</dd>\n        </dl>\n        The following options are recognized:\n        <dl>\n          <dt><code><strong>paragraphStart</strong>, <strong>paragraphEnd</strong>: RegExp</code></dt>\n          <dd>Blank lines are always considered paragraph boundaries.\n          These options can be used to specify a pattern that causes\n          lines to be considered the start or end of a paragraph.</dd>\n          <dt><code><strong>column</strong>: number</code></dt>\n          <dd>The column to wrap at. Defaults to 80.</dd>\n          <dt><code><strong>wrapOn</strong>: RegExp</code></dt>\n          <dd>A regular expression that matches only those\n          two-character strings that allow wrapping. By default, the\n          addon wraps on whitespace and after dash characters.</dd>\n          <dt><code><strong>killTrailingSpace</strong>: boolean</code></dt>\n          <dd>Whether trailing space caused by wrapping should be\n          preserved, or deleted. Defaults to true.</dd>\n          <dt><code><strong>forceBreak</strong>: boolean</code></dt>\n          <dd>If set to true forces a break at <code>column</code> in the case\n          when no <code>wrapOn</code> pattern is found in the range. If set to\n          false allows line to overflow the <code>column</code> limit if no\n          <code>wrapOn</code> pattern found. Defaults to true.</dd>\n        </dl>\n        A demo of the addon is available <a href=\"../demo/hardwrap.html\">here</a>.\n      </dd>\n\n      <dt id=\"addon_scrollpastend\"><a href=\"../addon/scroll/scrollpastend.js\"><code>scroll/scrollpastend.js</code></a></dt>\n      <dd>Defines an option `\"scrollPastEnd\"` that, when set to a\n      truthy value, allows the user to scroll one editor height of\n      empty space into view at the bottom of the editor.</dd>\n\n      <dt id=\"addon_merge\"><a href=\"../addon/merge/merge.js\"><code>merge/merge.js</code></a></dt>\n      <dd>Implements an interface for merging changes, using either a\n      2-way or a 3-way view. The <code>CodeMirror.MergeView</code>\n      constructor takes arguments similar to\n      the <a href=\"#CodeMirror\"><code>CodeMirror</code></a>\n      constructor, first a node to append the interface to, and then\n      an options object. Options are passed through to the editors\n      inside the view. These extra options are recognized:\n        <dl>\n          <dt><code><strong>origLeft</strong></code> and <code><strong>origRight</strong>: string</code></dt>\n          <dd>If given these provide original versions of the\n          document, which will be shown to the left and right of the\n          editor in non-editable CodeMirror instances. The merge\n          interface will highlight changes between the editable\n          document and the original(s). To create a 2-way (as opposed\n          to 3-way) merge view, provide only one of them.</dd>\n          <dt><code><strong>revertButtons</strong>: boolean</code></dt>\n          <dd>Determines whether buttons that allow the user to revert\n          changes are shown. Defaults to true.</dd>\n          <dt><code><strong>revertChunk</strong>: fn(mv: MergeView, from: CodeMirror, fromStart: Pos, fromEnd: Pos, to: CodeMirror, toStart: Pos, toEnd: Pos)</code></dt>\n          <dd>Can be used to define custom behavior when the user\n          reverts a changed chunk.</dd>\n          <dt><code><strong>connect</strong>: string</code></dt>\n          <dd>Sets the style used to connect changed chunks of code.\n          By default, connectors are drawn. When this is set\n          to <code>\"align\"</code>, the smaller chunk is padded to\n          align with the bigger chunk instead.</dd>\n          <dt><code><strong>collapseIdentical</strong>: boolean|number</code></dt>\n          <dd>When true (default is false), stretches of unchanged\n          text will be collapsed. When a number is given, this\n          indicates the amount of lines to leave visible around such\n          stretches (which defaults to 2).</dd>\n          <dt><code><strong>allowEditingOriginals</strong>: boolean</code></dt>\n          <dd>Determines whether the original editor allows editing.\n          Defaults to false.</dd>\n          <dt><code><strong>showDifferences</strong>: boolean</code></dt>\n          <dd>When true (the default), changed pieces of text are\n          highlighted.</dd>\n          <dt><code><strong>chunkClassLocation</strong>: string|Array</code></dt>\n          <dd>By default the chunk highlights are added\n          using <a href=\"#addLineClass\"><code>addLineClass</code></a>\n          with \"background\". Override this to customize it to be any\n          valid `where` parameter or an Array of valid `where`\n          parameters.</dd>\n        </dl>\n        The addon also defines commands <code>\"goNextDiff\"</code>\n        and <code>\"goPrevDiff\"</code> to quickly jump to the next\n        changed chunk. <a href=\"../demo/merge.html\">Demo\n        here</a>.</dd>\n\n      <dt id=\"addon_tern\"><a href=\"../addon/tern/tern.js\"><code>tern/tern.js</code></a></dt>\n      <dd>Provides integration with\n      the <a href=\"https://ternjs.net\">Tern</a> JavaScript analysis\n      engine, for completion, definition finding, and minor\n      refactoring help. See the <a href=\"../demo/tern.html\">demo</a>\n      for a very simple integration. For more involved scenarios, see\n      the comments at the top of\n      the <a href=\"../addon/tern/tern.js\">addon</a> and the\n      implementation of the\n      (multi-file) <a href=\"https://ternjs.net/doc/demo/index.html\">demonstration\n      on the Tern website</a>.</dd>\n    </dl>\n</section>\n\n<section id=modeapi>\n    <h2>Writing CodeMirror Modes</h2>\n\n    <p>Modes typically consist of a single JavaScript file. This file\n    defines, in the simplest case, a lexer (tokenizer) for your\n    language—a function that takes a character stream as input,\n    advances it past a token, and returns a style for that token. More\n    advanced modes can also handle indentation for the language.</p>\n\n    <p>This section describes the low-level mode interface. Many modes\n    are written directly against this, since it offers a lot of\n    control, but for a quick mode definition, you might want to use\n    the <a href=\"../demo/simplemode.html\">simple mode addon</a>.</p>\n\n    <p id=\"defineMode\">The mode script should\n    call <code><strong>CodeMirror.defineMode</strong></code> to\n    register itself with CodeMirror. This function takes two\n    arguments. The first should be the name of the mode, for which you\n    should use a lowercase string, preferably one that is also the\n    name of the files that define the mode (i.e. <code>\"xml\"</code> is\n    defined in <code>xml.js</code>). The second argument should be a\n    function that, given a CodeMirror configuration object (the thing\n    passed to the <code>CodeMirror</code> function) and an optional\n    mode configuration object (as in\n    the <a href=\"#option_mode\"><code>mode</code></a> option), returns\n    a mode object.</p>\n\n    <p>Typically, you should use this second argument\n    to <code>defineMode</code> as your module scope function (modes\n    should not leak anything into the global scope!), i.e. write your\n    whole mode inside this function.</p>\n\n    <p>The main responsibility of a mode script is <em>parsing</em>\n    the content of the editor. Depending on the language and the\n    amount of functionality desired, this can be done in really easy\n    or extremely complicated ways. Some parsers can be stateless,\n    meaning that they look at one element (<em>token</em>) of the code\n    at a time, with no memory of what came before. Most, however, will\n    need to remember something. This is done by using a <em>state\n    object</em>, which is an object that is always passed when\n    reading a token, and which can be mutated by the tokenizer.</p>\n\n    <p id=\"startState\">Modes that use a state must define\n    a <code><strong>startState</strong></code> method on their mode\n    object. This is a function of no arguments that produces a state\n    object to be used at the start of a document.</p>\n\n    <p id=\"token\">The most important part of a mode object is\n    its <code><strong>token</strong>(stream, state)</code> method. All\n    modes must define this method. It should read one token from the\n    stream it is given as an argument, optionally update its state,\n    and return a style string, or <code>null</code> for tokens that do\n    not have to be styled. For your styles, you are encouraged to use\n    the 'standard' names defined in the themes (without\n    the <code>cm-</code> prefix). If that fails, it is also possible\n    to come up with your own and write your own CSS theme file.<p>\n\n    <p id=\"token_style_line\">A typical token string would\n    be <code>\"variable\"</code> or <code>\"comment\"</code>. Multiple\n    styles can be returned (separated by spaces), for\n    example <code>\"string error\"</code> for a thing that looks like a\n    string but is invalid somehow (say, missing its closing quote).\n    When a style is prefixed by <code>\"line-\"</code>\n    or <code>\"line-background-\"</code>, the style will be applied to\n    the whole line, analogous to what\n    the <a href=\"#addLineClass\"><code>addLineClass</code></a> method\n    does—styling the <code>\"text\"</code> in the simple case, and\n    the <code>\"background\"</code> element\n    when <code>\"line-background-\"</code> is prefixed.</p>\n\n    <p id=\"StringStream\">The stream object that's passed\n    to <code>token</code> encapsulates a line of code (tokens may\n    never span lines) and our current position in that line. It has\n    the following API:</p>\n\n    <dl>\n      <dt><code><strong>eol</strong>() → boolean</code></dt>\n      <dd>Returns true only if the stream is at the end of the\n      line.</dd>\n      <dt><code><strong>sol</strong>() → boolean</code></dt>\n      <dd>Returns true only if the stream is at the start of the\n      line.</dd>\n\n      <dt><code><strong>peek</strong>() → string</code></dt>\n      <dd>Returns the next character in the stream without advancing\n      it. Will return a <code>null</code> at the end of the\n      line.</dd>\n      <dt><code><strong>next</strong>() → string</code></dt>\n      <dd>Returns the next character in the stream and advances it.\n      Also returns <code>null</code> when no more characters are\n      available.</dd>\n\n      <dt><code><strong>eat</strong>(match: string|regexp|function(char: string) → boolean) → string</code></dt>\n      <dd><code>match</code> can be a character, a regular expression,\n      or a function that takes a character and returns a boolean. If\n      the next character in the stream 'matches' the given argument,\n      it is consumed and returned. Otherwise, <code>undefined</code>\n      is returned.</dd>\n      <dt><code><strong>eatWhile</strong>(match: string|regexp|function(char: string) → boolean) → boolean</code></dt>\n      <dd>Repeatedly calls <code>eat</code> with the given argument,\n      until it fails. Returns true if any characters were eaten.</dd>\n      <dt><code><strong>eatSpace</strong>() → boolean</code></dt>\n      <dd>Shortcut for <code>eatWhile</code> when matching\n      white-space.</dd>\n      <dt><code><strong>skipToEnd</strong>()</code></dt>\n      <dd>Moves the position to the end of the line.</dd>\n      <dt><code><strong>skipTo</strong>(str: string) → boolean</code></dt>\n      <dd>Skips to the start of the next occurrence of the given string, if\n      found on the current line (doesn't advance the stream if the\n      string does not occur on the line). Returns true if the\n      string was found.</dd>\n      <dt><code><strong>match</strong>(pattern: string, ?consume: boolean, ?caseFold: boolean) → boolean</code></dt>\n      <dt><code><strong>match</strong>(pattern: regexp, ?consume: boolean) → array&lt;string&gt;</code></dt>\n      <dd>Act like a\n      multi-character <code>eat</code>—if <code>consume</code> is true\n      or not given—or a look-ahead that doesn't update the stream\n      position—if it is false. <code>pattern</code> can be either a\n      string or a regular expression starting with <code>^</code>.\n      When it is a string, <code>caseFold</code> can be set to true to\n      make the match case-insensitive. When successfully matching a\n      regular expression, the returned value will be the array\n      returned by <code>match</code>, in case you need to extract\n      matched groups.</dd>\n\n      <dt><code><strong>backUp</strong>(n: integer)</code></dt>\n      <dd>Backs up the stream <code>n</code> characters. Backing it up\n      further than the start of the current token will cause things to\n      break, so be careful.</dd>\n      <dt><code><strong>column</strong>() → integer</code></dt>\n      <dd>Returns the column (taking into account tabs) at which the\n      current token starts.</dd>\n      <dt><code><strong>indentation</strong>() → integer</code></dt>\n      <dd>Tells you how far the current line has been indented, in\n      spaces. Corrects for tab characters.</dd>\n\n      <dt><code><strong>current</strong>() → string</code></dt>\n      <dd>Get the string between the start of the current token and\n      the current stream position.</dd>\n\n      <dt><code><strong>lookAhead</strong>(n: number) → ?string</code></dt>\n      <dd>Get the line <code>n</code> (&gt;0) lines after the current\n      one, in order to scan ahead across line boundaries. Note that\n      you want to do this carefully, since looking far ahead will make\n      mode state caching much less effective.</dd>\n\n      <dt id=\"baseToken\"><code><strong>baseToken</strong>() → ?{type: ?string, size: number}</code></dt>\n      <dd>Modes added\n      through <a href=\"#addOverlay\"><code>addOverlay</code></a>\n      (and <em>only</em> such modes) can use this method to inspect\n      the current token produced by the underlying mode.</dd>\n    </dl>\n\n    <p id=\"blankLine\">By default, blank lines are simply skipped when\n    tokenizing a document. For languages that have significant blank\n    lines, you can define\n    a <code><strong>blankLine</strong>(state)</code> method on your\n    mode that will get called whenever a blank line is passed over, so\n    that it can update the parser state.</p>\n\n    <p id=\"copyState\">Because state object are mutated, and CodeMirror\n    needs to keep valid versions of a state around so that it can\n    restart a parse at any line, copies must be made of state objects.\n    The default algorithm used is that a new state object is created,\n    which gets all the properties of the old object. Any properties\n    which hold arrays get a copy of these arrays (since arrays tend to\n    be used as mutable stacks). When this is not correct, for example\n    because a mode mutates non-array properties of its state object, a\n    mode object should define\n    a <code><strong>copyState</strong></code> method, which is given a\n    state and should return a safe copy of that state.</p>\n\n    <p id=\"indent\">If you want your mode to provide smart indentation\n    (through the <a href=\"#indentLine\"><code>indentLine</code></a>\n    method and the <code>indentAuto</code>\n    and <code>newlineAndIndent</code> commands, to which keys can be\n    <a href=\"#option_extraKeys\">bound</a>), you must define\n    an <code><strong>indent</strong>(state, textAfter)</code> method\n    on your mode object.</p>\n\n    <p>The indentation method should inspect the given state object,\n    and optionally the <code>textAfter</code> string, which contains\n    the text on the line that is being indented, and return an\n    integer, the amount of spaces to indent. It should usually take\n    the <a href=\"#option_indentUnit\"><code>indentUnit</code></a>\n    option into account. An indentation method may\n    return <code>CodeMirror.Pass</code> to indicate that it\n    could not come up with a precise indentation.</p>\n\n    <p id=\"mode_comment\">To work well with\n    the <a href=\"#addon_comment\">commenting addon</a>, a mode may\n    define <code><strong>lineComment</strong></code> (string that\n    starts a line\n    comment), <code><strong>blockCommentStart</strong></code>, <code><strong>blockCommentEnd</strong></code>\n    (strings that start and end block comments),\n    and <code>blockCommentLead</code> (a string to put at the start of\n    continued lines in a block comment). All of these are\n    optional.</p>\n\n    <p id=\"electricChars\">Finally, a mode may define either\n    an <code>electricChars</code> or an <code>electricInput</code>\n    property, which are used to automatically reindent the line when\n    certain patterns are typed and\n    the <a href=\"#option_electricChars\"><code>electricChars</code></a>\n    option is enabled. <code>electricChars</code> may be a string, and\n    will trigger a reindent whenever one of the characters in that\n    string are typed. Often, it is more appropriate to\n    use <code>electricInput</code>, which should hold a regular\n    expression, and will trigger indentation when the part of the\n    line <em>before</em> the cursor matches the expression. It should\n    usually end with a <code>$</code> character, so that it only\n    matches when the indentation-changing pattern was just typed, not when something was\n    typed after the pattern.</p>\n\n    <p>So, to summarize, a mode <em>must</em> provide\n    a <code>token</code> method, and it <em>may</em>\n    provide <code>startState</code>, <code>copyState</code>,\n    and <code>indent</code> methods. For an example of a trivial mode,\n    see the <a href=\"../mode/diff/diff.js\">diff mode</a>, for a more\n    involved example, see the <a href=\"../mode/clike/clike.js\">C-like\n    mode</a>.</p>\n\n    <p>Sometimes, it is useful for modes to <em>nest</em>—to have one\n    mode delegate work to another mode. An example of this kind of\n    mode is the <a href=\"../mode/htmlmixed/htmlmixed.js\">mixed-mode HTML\n    mode</a>. To implement such nesting, it is usually necessary to\n    create mode objects and copy states yourself. To create a mode\n    object, there are <code>CodeMirror.getMode(options,\n    parserConfig)</code>, where the first argument is a configuration\n    object as passed to the mode constructor function, and the second\n    argument is a mode specification as in\n    the <a href=\"#option_mode\"><code>mode</code></a> option. To copy a\n    state object, call <code>CodeMirror.copyState(mode, state)</code>,\n    where <code>mode</code> is the mode that created the given\n    state.</p>\n\n    <p id=\"innerMode\">In a nested mode, it is recommended to add an\n    extra method, <code><strong>innerMode</strong></code> which, given\n    a state object, returns a <code>{state, mode}</code> object with\n    the inner mode and its state for the current position. These are\n    used by utility scripts such as the <a href=\"#addon_closetag\">tag\n    closer</a> to get context information. Use\n    the <code>CodeMirror.innerMode</code> helper function to, starting\n    from a mode and a state, recursively walk down to the innermost\n    mode and state.</p>\n\n    <p>To make indentation work properly in a nested parser, it is\n    advisable to give the <code>startState</code> method of modes that\n    are intended to be nested an optional argument that provides the\n    base indentation for the block of code. The JavaScript and CSS\n    parser do this, for example, to allow JavaScript and CSS code\n    inside the mixed-mode HTML mode to be properly indented.</p>\n\n    <p id=\"defineMIME\">It is possible, and encouraged, to associate\n    your mode, or a certain configuration of your mode, with\n    a <a href=\"http://en.wikipedia.org/wiki/MIME\">MIME</a> type. For\n    example, the JavaScript mode associates itself\n    with <code>text/javascript</code>, and its JSON variant\n    with <code>application/json</code>. To do this,\n    call <code><strong>CodeMirror.defineMIME</strong>(mime,\n    modeSpec)</code>, where <code>modeSpec</code> can be a string or\n    object specifying a mode, as in\n    the <a href=\"#option_mode\"><code>mode</code></a> option.</p>\n\n    <p>If a mode specification wants to add some properties to the\n    resulting mode object, typically for use\n    with <a href=\"#getHelpers\"><code>getHelpers</code></a>, it may\n    contain a <code>modeProps</code> property, which holds an object.\n    This object's properties will be copied to the actual mode\n    object.</p>\n\n    <p id=\"extendMode\">Sometimes, it is useful to add or override mode\n    object properties from external code.\n    The <code><strong>CodeMirror.extendMode</strong></code> function\n    can be used to add properties to mode objects produced for a\n    specific mode. Its first argument is the name of the mode, its\n    second an object that specifies the properties that should be\n    added. This is mostly useful to add utilities that can later be\n    looked up through <a href=\"#getMode\"><code>getMode</code></a>.</p>\n</section>\n\n<section id=\"vimapi\">\n    <h2>VIM Mode API</h2>\n\n    <p>CodeMirror has a robust VIM mode that attempts to faithfully\n    emulate VIM's most useful features. It can be enabled by\n    including <a href=\"../keymap/vim.js\"><code>keymap/vim.js</code>\n    </a> and setting the <code>keyMap</code> option to\n    <code>\"vim\"</code>.</p>\n\n    <h3 id=\"vimapi_configuration\">Configuration</h3>\n\n    <p>VIM mode accepts configuration options for customizing\n    behavior at run time. These methods can be called at any time\n    and will affect all existing CodeMirror instances unless\n    specified otherwise. The methods are exposed on the\n    <code><strong>CodeMirror.Vim</strong></code> object.</p>\n\n    <dl>\n      <dt id=\"vimapi_setOption\"><code><strong>setOption(name: string, value: any, ?cm: CodeMirror, ?cfg: object)</strong></code></dt>\n      <dd>Sets the value of a VIM option. <code>name</code> should\n      be the name of an option. If <code>cfg.scope</code> is not set\n      and <code>cm</code> is provided, then sets the global and\n      instance values of the option. Otherwise, sets either the\n      global or instance value of the option depending on whether\n      <code>cfg.scope</code> is <code>global</code> or\n      <code>local</code>.</dd>\n      <dt id=\"vimapi_getOption\"><code><strong>getOption(name: string, ?cm: CodeMirror: ?cfg: object)</strong></code></dt>\n      <dd>Gets the current value of a VIM option. If\n      <code>cfg.scope</code> is not set and <code>cm</code> is\n      provided, then gets the instance value of the option, falling\n      back to the global value if not set. If <code>cfg.scope</code> is provided, then gets the <code>global</code> or\n      <code>local</code> value without checking the other.</dd>\n\n      <dt id=\"vimapi_map\"><code><strong>map(lhs: string, rhs: string, ?context: string)</strong></code></dt>\n      <dd>Maps a key sequence to another key sequence. Implements\n      VIM's <code>:map</code> command. To map ; to : in VIM would be\n      <code><strong>:map ; :</strong></code>. That would translate to\n      <code><strong>CodeMirror.Vim.map(';', ':');</strong></code>.\n      The <code>context</code> can be <code>normal</code>,\n      <code>visual</code>, or <code>insert</code>, which correspond\n      to <code>:nmap</code>, <code>:vmap</code>, and\n      <code>:imap</code>\n      respectively.</dd>\n\n      <dt id=\"vimapi_mapCommand\"><code><strong>mapCommand(keys: string, type: string, name: string, ?args: object, ?extra: object)</strong></code></dt>\n      <dd>Maps a key sequence to a <code>motion</code>,\n      <code>operator</code>, or <code>action</code> type command.\n      The args object is passed through to the command when it is\n      invoked by the provided key sequence.\n      <code>extras.context</code> can be <code>normal</code>,\n      <code>visual</code>, or <code>insert</code>, to map the key\n      sequence only in the corresponding mode.\n      <code>extras.isEdit</code> is applicable only to actions,\n      determining whether it is recorded for replay for the\n      <code>.</code> single-repeat command.\n\n      <dt id=\"vimapi_unmap\"><strong><code>unmap(lhs: string, ctx: string)</code></strong></dt>\n      <dd>\n      Remove the command <code>lhs</code> if it is a user defined command.\n      If the command is an Ex to Ex or Ex to key mapping then the context\n      must be <code>undefined</code> or <code>false</code>.\n      </dd>\n\n      <dt id=\"vimapi_mapclear\"><strong><code>mapclear(ctx: string)</code></strong></dt>\n      <dd>\n      Remove all user-defined mappings for the provided context.\n      </dd>\n\n      <dt id=\"vimapi_noremap\"><strong><code>noremap(lhs: string, rhs: string, ctx: {string, array&lt;string&gt;})</code></strong></dt>\n      <dd>\n      Non-recursive map function. This will not create mappings to key maps\n      that aren't present in the default key map.\n      If no context is provided then the  mapping will be applied to each of\n      normal, insert, and visual mode.\n      </dd>\n    </dl>\n\n    <h3 id=\"vimapi_events\">Events</h3>\n\n    <p>VIM mode signals a few events on the editor instance. For an example usage, see <a href=\"https://github.com/codemirror/CodeMirror/blob/5.55.0/demo/vim.html#L101-L110\">demo/vim.html#L101</a>.</p>\n\n    <dl>\n      <dt id=\"vimapi_commanddone\"><code><strong>\"vim-command-done\"</strong> (reason: undefined)</code></dt>\n      <dd>Fired on keypress and mousedown where command has completed or no command found.</dd>\n\n      <dt id=\"vimapi_keypress\"><code><strong>\"vim-keypress\"</strong> (vimKey: string)</code></dt>\n      <dd>Fired on keypress, <code>vimKey</code> is in Vim's key notation.</dd>\n\n      <dt id=\"vimapi_modechange\"><code><strong>\"vim-mode-change\"</strong> (modeObj: object)</code></dt>\n      <dd>Fired after mode change, <code>modeObj</code> parameter is a <code>{mode: string, ?subMode: string}</code> object. Modes: <code>\"insert\", \"normal\", \"replace\", \"visual\"</code>. Visual sub-modes: <code>\"linewise\", \"blockwise\"</code>.</dd>\n    </dl>\n\n    <h3 id=\"vimapi_extending\">Extending VIM</h3>\n\n    <p>CodeMirror's VIM mode implements a large subset of VIM's core\n    editing functionality. But since there's always more to be\n    desired, there is a set of APIs for extending VIM's\n    functionality. As with the configuration API, the methods are\n    exposed on <code><strong>CodeMirror.Vim</strong></code> and may\n    be called at any time.</p>\n\n    <dl>\n      <dt id=\"vimapi_defineOption\"><code><strong>defineOption(name: string, default: any, type: string, ?aliases: array&lt;string&gt;, ?callback: function (?value: any, ?cm: CodeMirror) → ?any)</strong></code></dt>\n      <dd>Defines a VIM style option and makes it available to the\n      <code>:set</code> command. Type can be <code>boolean</code> or\n      <code>string</code>, used for validation and by\n      <code>:set</code> to determine which syntax to accept. If a\n      <code>callback</code> is passed in, VIM does not store the value of the\n      option itself, but instead uses the callback as a setter/getter. If the\n      first argument to the callback is <code>undefined</code>, then the\n      callback should return the value of the option. Otherwise, it should set\n      instead. Since VIM options have global and instance values, whether a\n      <code>CodeMirror</code> instance is passed in denotes whether the global\n      or local value should be used. Consequently, it's possible for the\n      callback to be called twice for a single <code>setOption</code> or\n      <code>getOption</code> call. Note that right now, VIM does not support\n      defining buffer-local options that do not have global values. If an\n      option should not have a global value, either always ignore the\n      <code>cm</code> parameter in the callback, or always pass in a\n      <code>cfg.scope</code> to <code>setOption</code> and\n      <code>getOption</code>.</dd>\n\n      <dt id=\"vimapi_defineMotion\"><code><strong>defineMotion(name: string, fn: function(cm: CodeMirror, head: {line, ch}, ?motionArgs: object}) → {line, ch})</strong></code></dt>\n      <dd>Defines a motion command for VIM. The motion should return\n      the desired result position of the cursor. <code>head</code>\n      is the current position of the cursor. It can differ from\n      <code>cm.getCursor('head')</code> if VIM is in visual mode.\n      <code>motionArgs</code> is the object passed into\n      <strong><code>mapCommand()</code></strong>.</dd>\n\n      <dt id=\"vimapi_defineOperator\"><strong><code>defineOperator(name: string, fn: function(cm: CodeMirror, ?operatorArgs: object, ranges: array&lt;{anchor, head}&gt;) → ?{line, ch})</code></strong></dt>\n      <dd>Defines an operator command, similar to <strong><code>\n      defineMotion</code></strong>. <code>ranges</code> is the range\n      of text the operator should operate on. If the cursor should\n      be set to a certain position after the operation finishes, it\n      can return a cursor object.</dd>\n\n      <dt id=\"vimapi_defineActon\"><strong><code>defineAction(name: string, fn: function(cm: CodeMirror, ?actionArgs: object))</code></strong></dt>\n      <dd>Defines an action command, similar to\n      <strong><code>defineMotion</code></strong>. Action commands\n      can have arbitrary behavior, making them more flexible than\n      motions and operators, at the loss of orthogonality.</dd>\n\n      <dt id=\"vimapi_defineEx\"><strong><code>defineEx(name: string, ?prefix: string, fn: function(cm: CodeMirror, ?params: object))</code></strong></dt>\n      <dd>Defines an Ex command, and maps it to <code>:name</code>.\n      If a prefix is provided, it, and any prefixed substring of the\n      <code>name</code> beginning with the <code>prefix</code> can\n      be used to invoke the command. If the <code>prefix</code> is\n      falsy, then <code>name</code> is used as the prefix. <code>\n      params.argString</code> contains the part of the prompted\n      string after the command name. <code>params.args</code> is\n      <code>params.argString</code> split by whitespace. If the\n      command was prefixed with a\n      <code><strong><a href=\"http://vimdoc.sourceforge.net/htmldoc/cmdline.html#cmdline-ranges\">line range</a></strong></code>,\n      <code>params.line</code> and <code>params.lineEnd</code> will\n      be set.</dd>\n\n      <dt id=\"vimapi_getRegisterController\"><strong><code>getRegisterController()</code></strong></dt>\n      <dd>Returns the RegisterController that manages the state of registers\n      used by vim mode. For the RegisterController api see its\n      definition <a href=\"https://github.com/CodeMirror/CodeMirror/blob/b2d26b4ccb1d0994ae84d18ad8b84018de176da9/keymap/vim.js#L1123\">here</a>.\n      </dd>\n\n      <dt id='vimapi_buildkeymap'><strong><code>buildKeyMap()</code></strong></dt>\n      <dd>\n      Not currently implemented. If you would like to contribute this please open\n      a pull request on <a href=\"https://github.com/codemirror/CodeMirror\">GitHub</a>.\n      </dd>\n\n      <dt id=\"vimapi_defineRegister\"><strong><code>defineRegister()</code></strong></dt>\n      <dd> Defines an external register. The name should be a single character\n      that will be used to reference the register. The register should support\n      <code>setText</code>, <code>pushText</code>, <code>clear</code>, and <code>toString</code>.\n      See <a href=\"https://github.com/CodeMirror/CodeMirror/blob/b2d26b4ccb1d0994ae84d18ad8b84018de176da9/keymap/vim.js#L1055\">Register</a> for a reference implementation.\n      </dd>\n\n      <dt id=\"vimapi_getVimGlobalState_\"><strong><code>getVimGlobalState_()</code></strong></dt>\n      <dd>\n      Return a reference to the VimGlobalState.\n      </dd>\n\n      <dt id=\"vimapi_resetVimGlobalState_\"><strong><code>resetVimGlobalState_()</code></strong></dt>\n      <dd>\n      Reset the default values of the VimGlobalState to fresh values. Any options\n      set with <code>setOption</code> will also be applied to the reset global state.\n      </dd>\n\n      <dt id=\"vimapi_maybeInitVimState_\"><strong><code>maybeInitVimState_(cm: CodeMirror)</code></strong></dt>\n      <dd>\n      Initialize <code>cm.state.vim</code> if it does not exist. Returns <code>cm.state.vim</code>.\n      </dd>\n\n      <dt id=\"vimapi_handleKey\"><strong><code>handleKey(cm: CodeMirror, key: string, origin: string)</code></strong></dt>\n      <dd>\n      Convenience function to pass the arguments to <code>findKey</code> and\n      call returned function if it is defined.\n      </dd>\n\n      <dt id=\"vimapi_findKey\"><strong><code>findKey(cm: CodeMirror, key: string, origin: string)</code></strong></dt>\n      <dd>\n      This is the outermost function called by CodeMirror, after keys have\n      been mapped to their Vim equivalents. Finds a command based on the key\n      (and cached keys if there is a multi-key sequence). Returns <code>undefined</code>\n      if no key is matched, a noop function if a partial match is found (multi-key),\n      and a function to execute the bound command if a a key is matched. The\n      function always returns true.\n      </dd>\n\n      <dt id=\"vimapi_option_suppressErrorLogging\"><code><strong>suppressErrorLogging</strong>: boolean</code></dt>\n      <dd>Whether to use suppress the use of <code>console.log</code> when catching an\n      error in the function returned by <code>findKey</code>.\n      Defaults to false.</dd>\n\n      <dt id=\"vimapi_exitVisualMode\"><strong><code>exitVisualMode(cm: CodeMirror, ?moveHead: boolean)</code></strong></dt>\n      <dd> Exit visual mode. If moveHead is set to false, the CodeMirror selection\n      will not be touched. The caller assumes the responsibility of putting\n      the cursor in the right place.\n      </dd>\n\n      <dt id=\"vimapi_exitInsertMode\"><strong><code>exitInsertMode(cm: CodeMirror)</code></strong></dt>\n      <dd>\n      Exit insert mode.\n      </dd>\n    </dl>\n\n</section>\n\n</article>\n\n<script>setTimeout(function(){CodeMirror.colorize();}, 20);</script>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/doc/realworld.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Real-world Uses</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"docs.css\">\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Real-world uses</a>\n  </ul>\n</div>\n\n<article>\n\n<h2>CodeMirror real-world uses</h2>\n\n    <p>Create a <a href=\"https://github.com/codemirror/codemirror5\">pull\n    request</a> if you'd like your project to be added to this list.</p>\n\n    <ul>\n      <li><a href=\"https://www.adaface.com/pair-pro\">Adaface PairPro</a> (Shared code editor with compiler and video conferencing)</li>\n      <li><a href=\"http://brackets.io\">Adobe Brackets</a> (code editor)</li>\n      <li><a href=\"http://adnuntius.com\">Adnuntius</a> (used for in-browser code editing and version history)</li>\n      <li><a href=\"http://alm.tools\">ALM Tools</a> (TypeScript powered IDE)</li>\n      <li><a href=\"http://amber-lang.net/\">Amber</a> (JavaScript-based Smalltalk system)</li>\n      <li><a href=\"http://apeye.org/\">APEye</a> (tool for testing &amp; documenting APIs)</li>\n      <li><a href=\"https://github.com/google/appengine-codiad\">Appengine Codiad</a></li>\n      <li><a href=\"https://chrome.google.com/webstore/detail/better-text-viewer/lcaidopdffhfemoefoaadecppnjdknkc\">Better Text Viewer</a> (plain text reader app for Chrome)</li>\n      <li><a href=\"http://blog.bitbucket.org/2013/05/14/edit-your-code-in-the-cloud-with-bitbucket/\">Bitbucket</a> (code hosting)</li>\n      <li><a href=\"https://blogger.googleblog.com/2013/04/improvements-to-blogger-template-html.html\">Blogger's theme editor</a></li>\n      <li><a href=\"http://bluegriffon.org/\">BlueGriffon</a> (HTML editor)</li>\n      <li><a href=\"https://bnfplayground.pauliankline.com/\">BNF Playground</a> (grammar workbench)</li>\n      <li><a href=\"https://github.com/isdampe/BosonEditorExperimental\">Boson Editor</a> (code editor)</li>\n      <li><a href=\"http://cargocollective.com/\">Cargo Collective</a> (creative publishing platform)</li>\n      <li><a href=\"https://developers.google.com/chrome-developer-tools/\">Chrome DevTools</a></li>\n      <li><a href=\"http://clickhelp.co/\">ClickHelp</a> (technical writing tool)</li>\n      <li><a href=\"https://encap.github.io/clone-it/\">Clone-It</a> (HTML & CSS learning game)</li>\n      <li><a href=\"https://cloudcmd.io\">Cloud Commander</a> (filemanager for the web)</li>\n      <li><a href=\"https://electronjs.org/apps/colon\">Colon</a> (A flexible text editor or IDE)</li>\n      <li><a href=\"http://code.world/\">CodeWorld</a> (Haskell playground)</li>\n      <li><a href=\"http://complete-ly.appspot.com/playground/code.playground.html\">Complete.ly playground</a></li>\n      <li><a href=\"https://codeanywhere.com/\">Codeanywhere</a> (multi-platform cloud editor)</li>\n      <li><a href=\"http://drupal.org/project/cpn\">Code per Node</a> (Drupal module)</li>\n      <li><a href=\"https://codebitt.com/\">CodeBitt</a> (Code snippet sharing)</li>\n      <li><a href=\"http://www.codebugapp.com/\">Codebug</a> (PHP Xdebug front-end)</li>\n      <li><a href=\"http://codefights.com/\">CodeFights</a> (practice programming)</li>\n      <li><a href=\"https://github.com/angelozerr/CodeMirror-Eclipse\">CodeMirror Eclipse</a> (embed CM in Eclipse)</li>\n      <li><a href=\"http://emmet.io/blog/codemirror-movie/\">CodeMirror movie</a> (scripted editing demos)</li>\n      <li><a href=\"https://github.com/haoranyu/codemirror-record/\">CodeMirror Record</a> (codemirror activity recording and playback)</li>\n      <li><a href=\"http://code.google.com/p/codemirror2-gwt/\">CodeMirror2-GWT</a> (Google Web Toolkit wrapper)</li>\n      <li><a href=\"http://www.crunchzilla.com/code-monster\">Code Monster</a> & <a href=\"http://www.crunchzilla.com/code-maven\">Code Maven</a> (learning environment)</li>\n      <li><a href=\"http://codepen.io\">Codepen</a> (gallery of animations)</li>\n      <li><a href=\"https://github.com/pepstock-org/Coderba\">Coderba</a> Google Web Toolkit (GWT) wrapper</li>\n      <li><a href=\"https://coderpad.io/\">Coderpad</a> (interviewing tool)</li>\n      <li><a href=\"https:/coderush.xyz/\">CodeRush</a> typing speed test for programmers</li>\n      <li><a href=\"http://sasstwo.codeschool.com/levels/1/challenges/1\">Code School</a> (online tech learning environment)</li>\n      <li><a href=\"http://code-snippets.bungeshea.com/\">Code Snippets</a> (WordPress snippet management plugin)</li>\n      <li><a href=\"http://antonmi.github.io/code_together/\">Code together</a> (collaborative editing)</li>\n      <li><a href=\"https://www.codevolve.com/\">Codevolve</a> (programming lessons as-a-service)</li>\n      <li><a href=\"http://www.codezample.com\">CodeZample</a> (code snippet sharing)</li>\n      <li><a href=\"http://codio.com\">Codio</a> (Web IDE)</li>\n      <li><a href=\"https://www.codiva.io/\">Codiva.io</a> (Online Java Compiler and IDE with auto-completion and error highlighting)</li>\n      <li><a href=\"http://www.communitycodecamp.com/\">Community Code Camp</a> (code snippet sharing)</li>\n      <li><a href=\"http://www.compilejava.net/\">compilejava.net</a> (online Java sandbox)</li>\n      <li><a href=\"http://www.ckwnc.com/\">CKWNC</a> (UML editor)</li>\n      <li><a href=\"http://www.crossui.com/\">CrossUI</a> (cross-platform UI builder)</li>\n      <li><a href=\"http://rsnous.com/cruncher/\">Cruncher</a> (notepad with calculation features)</li>\n      <li><a href=\"http://www.crudzilla.com/\">Crudzilla</a> (self-hosted web IDE)</li>\n      <li><a href=\"http://cssdeck.com/\">CSSDeck</a> (CSS showcase)</li>\n      <li><a href=\"http://ireneros.com/deck/deck.js-codemirror/introduction/#textarea-code\">Deck.js integration</a> (slides with editors)</li>\n      <li><a href=\"http://www.dbninja.com\">DbNinja</a> (MySQL access interface)</li>\n      <li><a href=\"http://www.ecsspert.com/\">eCSSpert</a> (CSS demos and experiments)</li>\n      <li><a href=\"https://edabit.com\">Edabit</a> (coding challenges)</li>\n      <li><a href=\"http://elm-lang.org/Examples.elm\">Elm language examples</a></li>\n      <li><a href=\"http://eloquentjavascript.net/chapter1.html\">Eloquent JavaScript</a> (book)</li>\n      <li><a href=\"http://emmet.io\">Emmet</a> (fast XML editing)</li>\n      <li><a href=\"https://github.com/espruino/EspruinoWebIDE\">Espruino Web IDE</a> (Chrome App for writing code on Espruino devices)</li>\n      <li><a href=\"https://exlskills.com/learn-en/talent/interviews\">EXLskills Live Interviews</a></li>\n      <li><a href=\"http://www.fastfig.com/\">Fastfig</a> (online computation/math tool)</li>\n      <li><a href=\"https://metacpan.org/module/Farabi\">Farabi</a> (modern Perl IDE)</li>\n      <li><a href=\"http://blog.pamelafox.org/2012/02/interactive-html5-slides-with-fathomjs.html\">FathomJS integration</a> (slides with editors, again)</li>\n      <li><a href=\"http://fiddlesalad.com/\">Fiddle Salad</a> (web development environment)</li>\n      <li><a href=\"https://github.com/simogeo/Filemanager\">Filemanager</a></li>\n      <li><a href=\"https://hacks.mozilla.org/2013/11/firefox-developer-tools-episode-27-edit-as-html-codemirror-more/\">Firefox Developer Tools</a></li>\n      <li><a href=\"http://www.firepad.io\">Firepad</a> (collaborative text editor)</li>\n      <li><a href=\"https://gerritcodereview.com/\">Gerrit</a>'s diff view and inline editor</li>\n      <li><a href=\"https://github.com/maks/git-crx\">Git Crx</a> (Chrome App for browsing local git repos)</li>\n      <li><a href=\"https://github.com/github/android\">GitHub's Android app</a></li>\n      <li><a href=\"https://github.com/\">GitHub</a>'s in-browser edit feature</li>\n      <li><a href=\"https://glitch.com/\">Glitch</a> (community-driven app building)</li>\n      <li><a href=\"http://tour.golang.org\">Go language tour</a></li>\n      <li><a href=\"https://script.google.com/\">Google Apps Script</a></li>\n      <li><a href=\"http://web.uvic.ca/~siefkenj/graphit/graphit.html\">Graphit</a> (function graphing)</li>\n      <li><a href=\"https://graviton.netlify.app/\">Graviton Editor</a> (Cross-platform and modern-looking code editor)</li>\n      <li><a href=\"https://hackmd.io\">HackMD</a> (Realtime collaborative markdown notes on all platforms)</li>\n      <li><a href=\"http://www.handcraft.com/\">Handcraft</a> (HTML prototyping)</li>\n      <li><a href=\"http://hawkee.com/\">Hawkee</a></li>\n      <li><a href=\"http://try.haxe.org\">Haxe</a> (Haxe Playground) </li>\n      <li><a href=\"http://haxpad.com/\">HaxPad</a> (editor for Win RT)</li>\n      <li><a href=\"http://megafonweblab.github.com/histone-javascript/\">Histone template engine playground</a></li>\n      <li><a href=\"http://www.homegenie.it/docs/automation_getstarted.php\">Homegenie</a> (home automation server)</li>\n      <li><a href=\"http://icecoder.net\">ICEcoder</a> (web IDE)</li>\n      <li><a href=\"https://innovay.app/formatter/html\">Innovay Web Tools</a> (HTML, JS, CSS code beautifier)</li>\n      <li><a href=\"https://www.intervue.io\">Intervue</a> (Pair programming for interviews)</li>\n      <li><a href=\"http://ipython.org/\">IPython</a> (interactive computing shell)</li>\n      <li><a href=\"https://joelpinheiro.github.io/itrading/\">iTrading</a> (Algorithmic Trading)</li>\n      <li><a href=\"http://i-mos.org/imos/\">i-MOS</a> (modeling and simulation platform)</li>\n      <li><a href=\"http://www.janvas.com/\">Janvas</a> (vector graphics editor)</li>\n      <li><a href=\"https://code.wetrafa.xyz/\">JdBEdit</a> (web IDE)</li>\n      <li><a href=\"http://extensions.joomla.org/extensions/edition/editors/8723\">Joomla plugin</a></li>\n      <li><a href=\"http://jqfundamentals.com/\">jQuery fundamentals</a> (interactive tutorial)</li>\n      <li><a href=\"http://jsbin.com\">jsbin.com</a> (JS playground)</li>\n      <li><a href=\"http://tool.jser.com/preprocessor\">JSER preprocessor</a></li>\n      <li><a href=\"https://github.com/kucherenko/jscpd\">jscpd</a> (code duplication detector)</li>\n      <li><a href=\"http://jsfiddle.net\">JSFiddle</a> (another JS playground)</li>\n      <li><a href=\"http://www.jshint.com/\">JSHint</a> (JS linter)</li>\n      <li><a href=\"http://jumpseller.com/\">Jumpseller</a> (online store builder)</li>\n      <li><a href=\"http://kl1p.com/cmtest/1\">kl1p</a> (paste service)</li>\n      <li><a href=\"http://www.kodhus.com/kodity/\">Kodit</a></li>\n      <li><a href=\"http://kodtest.com/\">Kodtest</a> (HTML/JS/CSS playground)</li>\n      <li><a href=\"http://try.kotlinlang.org\">Kotlin</a> (web-based mini-IDE for Kotlin)</li>\n      <li><a href=\"http://lighttable.com/\">Light Table</a> (experimental IDE)</li>\n      <li><a href=\"http://liveweave.com/\">Liveweave</a> (HTML/CSS/JS scratchpad)</li>\n      <li><a href=\"https://liveuml.com/\">LiveUML</a> (PlantUML online editor)</li>\n      <li><a href=\"https://github.com/TuvaLabs/markdown-delight-editor\">Markdown Delight Editor</a> (extensible markdown editor polymer component)</li>\n      <li><a href=\"http://marklighteditor.com/\">Marklight editor</a> (lightweight markup editor)</li>\n      <li><a href=\"https://www.mediawiki.org/wiki/Extension:CodeMirror\">MediaWiki extension</a> (wiki engine)</li>\n      <li><a href=\"http://www.mergely.com/\">Mergely</a> (interactive diffing)</li>\n      <li><a href=\"http://www.iunbug.com/mihtool\">MIHTool</a> (iOS web-app debugging tool)</li>\n      <li><a href=\"http://mscgen.js.org/index.html\">mscgen_js</a> (online sequence chart editor)</li>\n      <li><a href=\"http://mvcplayground.apphb.com/\">MVC Playground</a></li>\n      <li><a href=\"http://www.navigatecms.com\">Navigate CMS</a></li>\n      <li><a href=\"https://github.com/soliton4/nodeMirror\">nodeMirror</a> (IDE project)</li>\n      <li><a href=\"https://notex.ch\">NoTex</a> (rST authoring)</li>\n      <li><a href=\"https://nteract.io\">nteract</a> (interactive literate coding notebook)</li>\n      <li><a href=\"http://oakoutliner.com\">Oak</a> (online outliner)</li>\n      <li><a href=\"http://www.greycampus.com/opencampus\">OpenCampus</a></li>\n      <li><a href=\"http://clrhome.org/asm/\">ORG</a> (z80 assembly IDE)</li>\n      <li><a href=\"https://github.com/mamacdon/orion-codemirror\">Orion-CodeMirror integration</a> (running CodeMirror modes in Orion)</li>\n      <li><a href=\"https://www.overleaf.com/\">Overleaf</a> (Collaborative LaTeX Editor)</li>\n      <li><a href=\"http://paperjs.org/\">Paper.js</a> (graphics scripting)</li>\n      <li><a href=\"http://pharaoh.js.org/\">Pharaoh</a> (browser &amp; desktop editor for the classroom)</li>\n      <li><a href=\"http://prinbit.com/\">PrinBit</a> (collaborative coding tool)</li>\n      <li><a href=\"https://www.pramp.com/ref/codemirror\">Pramp</a> (free platform to practice mock interviews and coding problems)</li>\n      <li><a href=\"http://prose.io/\">Prose.io</a> (github content editor)</li>\n      <li><a href=\"https://pypi.python.org/pypi/PubliForge/\">PubliForge</a> (online publishing system)</li>\n      <li><a href=\"http://www.puzzlescript.net/\">Puzzlescript</a> (puzzle game engine)</li>\n      <li><a href=\"https://chrome.google.com/webstore/detail/quantum/hmnlklahndgbhdoclhdnoafhafbhmnkm?hl=en-US\">Quantum</a> (code editor for Chrome OS)</li>\n      <li><a href=\"http://ariya.ofilabs.com/2011/09/hybrid-webnative-desktop-codemirror.html\">Qt+Webkit integration</a> (building a desktop CodeMirror app)</li>\n      <li><a href=\"http://www.quivive-file-manager.com\">Quivive File Manager</a></li>\n      <li><a href=\"https://racktables.org\">RackTables</a> (data centre resources manager)</li>\n      <li><a href=\"http://rascalmicro.com/docs/basic-tutorial-getting-started.html\">Rascal</a> (tiny computer)</li>\n      <li><a href=\"https://www.realtime.io/\">RealTime.io</a> (Internet-of-Things infrastructure)</li>\n      <li><a href=\"http://refork.com/\">Refork</a> (animation demo gallery and sharing)</li>\n      <li><a href=\"http://sagecell.sagemath.org\">SageMathCell</a> (interactive mathematical software)</li>\n      <li><a href=\"https://sass2css.herokuapp.com/\">SASS2CSS</a> (SASS, SCSS or LESS to CSS converter and CSS beautifier)</li>\n      <li><a href=\"https://cloud.sagemath.com/\">SageMathCloud</a> (interactive mathematical software environment)</li>\n      <li><a href=\"https://github.com/szekelymilan/salvare\">salvare</a> (real-time collaborative code editor)</li>\n      <li><a href=\"https://chrome.google.com/webstore/detail/servephp/mnpikomdchjhkhbhmbboehfdjkobbfpo\">ServePHP</a> (PHP code testing in Chrome dev tools)</li>\n      <li><a href=\"http://scala-lang.org/blog/2017/02/20/introducing-scastie.html\">Scastie</a> (Scala playground)</li>\n      <li><a href=\"https://www.shadertoy.com/\">Shadertoy</a> (shader sharing)</li>\n      <li><a href=\"http://www.sketchpatch.net/labs/livecodelabIntro.html\">sketchPatch Livecodelab</a></li>\n      <li><a href=\"http://www.skulpt.org/\">Skulpt</a> (in-browser Python environment)</li>\n      <li><a href=\"https://www.sourcelair.com\">SourceLair</a> (in-browser IDE for Django, Node.js, PHP and HTML5)</li>\n      <li><a href=\"http://snaptomato.appspot.com/editor.html\">Snap Tomato</a> (HTML editing/testing page)</li>\n      <li><a href=\"http://snippets.pro/\">Snippets.pro</a> (code snippet sharing)</li>\n      <li><a href=\"http://www.solidshops.com/\">SolidShops</a> (hosted e-commerce platform)</li>\n      <li><a href=\"http://www.cemetech.net/sc/\">SourceCoder 3</a> (online calculator IDE and editor)</li>\n      <li><a href=\"http://sqlfiddle.com\">SQLFiddle</a> (SQL playground)</li>\n      <li><a href=\"http://www.subte.org/page/programar-ta-te-ti-online/\">SubTe</a> (AI bot programming environment)</li>\n      <li><a href=\"http://xuanji.appspot.com/isicp/\">Structure and Interpretation of Computer Programs</a>, Interactive Version</li>\n      <li><a href=\"http://syframework.alwaysdata.net\">SyBox</a> (PHP playground)</li>\n      <li><a href=\"http://www.tagspaces.org/\">TagSpaces</a> (personal data manager)</li>\n      <li><a href=\"https://textbox.io/\">Textbox.io</a> (WYSIWYG rich text editor)</a></li>\n      <li><a href=\"https://thefiletree.com\">The File Tree</a> (collab editor)</li>\n      <li><a href=\"http://www.mapbox.com/tilemill/\">TileMill</a> (map design tool)</li>\n      <li><a href=\"https://tilepieces.net\">Tilepieces</a> (visually editing HTML documents and Web applications projects)</li>\n      <li><a href=\"http://doc.tiki.org/Syntax+Highlighter\">Tiki</a> (wiki CMS groupware)</li>\n      <li><a href=\"https://www.tistory.com\">Tistory</a> (blog service)</li>\n      <li><a href=\"http://www.toolsverse.com/products/data-explorer/\">Toolsverse Data Explorer</a> (database management)</li>\n      <li><a href=\"http://blog.englard.net/post/39608000629/codeintumblr\">Tumblr code highlighting shim</a></li>\n      <li><a href=\"http://turbopy.com/\">TurboPY</a> (web publishing framework)</li>\n      <li><a href=\"http://cruise.eecs.uottawa.ca/umpleonline/\">UmpleOnline</a> (model-oriented programming tool)</li>\n      <li><a href=\"https://upsource.jetbrains.com/idea-ce/file/idea-ce-7706e7832aa9e2fd0c2decdb5cbef2225692c696/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java\">Upsource</a> (code browser and review tool)</li>\n      <li><a href=\"https://violentmonkey.github.io/\">Violentmonkey</a> (userscript manager / editor)</li>\n      <li><a href=\"https://github.com/mgaitan/waliki\">Waliki</a> (wiki engine)</li>\n      <li><a href=\"http://wamer.net/\">Wamer</a> (web application builder)</li>\n      <li><a href=\"https://github.com/brettz9/webappfind\">webappfind</a> (windows file bindings for webapps)</li>\n      <li><a href=\"http://www.webglacademy.com/\">WebGL academy</a> (learning WebGL)</li>\n      <li><a href=\"http://webglplayground.net/\">WebGL playground</a></li>\n      <li><a href=\"https://www.webkit.org/blog/2518/state-of-web-inspector/#source-code\">WebKit Web inspector</a></li>\n      <li><a href=\"http://www.wescheme.org/\">WeScheme</a> (learning tool)</li>\n      <li><a href=\"https://github.com/b3log/wide\">Wide</a> (golang web IDE)</li>\n      <li><a href=\"http://wordpress.org/extend/plugins/codemirror-for-codeeditor/\">WordPress plugin</a></li>\n      <li><a href=\"http://www.xosystem.org/home/applications_websites/xosystem_website/xoside_EN.php\">XOSide</a> (online editor)</li>\n      <li><a href=\"http://videlibri.sourceforge.net/cgi-bin/xidelcgi\">XQuery tester</a></li>\n      <li><a href=\"http://q42jaap.github.io/xsd2codemirror/\">xsd2codemirror</a> (convert XSD to CM XML completion info)</li>\n      <li><a href=\"http://www.yacas.org/yacas_online/yacas_online.html\">Yacas Online</a> (interactive mathematical software)</li>\n    </ul>\n\n</article>\n\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/doc/releases.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Release History</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"docs.css\">\n<script src=\"activebookmark.js\"></script>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active data-default=\"true\" href=\"#v5\">Version 5.x</a>\n    <li><a href=\"#v4\">Version 4.x</a>\n    <li><a href=\"#v3\">Version 3.x</a>\n    <li><a href=\"#v2\">Version 2.x</a>\n    <li><a href=\"#v1\">Version 0.x</a>\n  </ul>\n</div>\n\n<article>\n\n<h2>Release notes and version history</h2>\n\n<section id=v5 class=first>\n\n  <h2>Version 6.x</h2>\n\n  <p>See <a href=\"https://codemirror.net/docs/changelog/\">the new website</a>.</p>\n\n  <h2>Version 5.x</h2>\n\n  <p class=\"rel\">20-11-2023: <a href=\"https://codemirror.net/5/codemirror-5.65.16.zip\">Version 5.65.16</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix focus tracking in shadow DOM.</li>\n    <li><a href=\"https://codemirror.net/5/mode/go/\">go mode</a>: Allow underscores in numbers.</li>\n    <li><a href=\"https://codemirror.net/5/mode/jsx/index.html\">jsx mode</a>: Support TS generics marked by trailing comma.</li>\n  </ul>\n\n  <p class=\"rel\">29-08-2023: <a href=\"https://codemirror.net/5/codemirror-5.65.15.zip\">Version 5.65.15</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_lint\">lint addon</a>: Prevent tooltips from sticking out of the viewport.</li>\n    <li><a href=\"https://codemirror.net/5/mode/yaml/\">yaml mode</a>: Fix an exponential-complexity regular expression.</li>\n  </ul>\n\n  <p class=\"rel\">17-07-2023: <a href=\"https://codemirror.net/5/codemirror-5.65.14.zip\">Version 5.65.14</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/mode/clike/\">clike mode</a>: Fix poor indentation in some Java code.</li>\n    <li><a href=\"https://codemirror.net/5/mode/nsis/index.html\">nsis mode</a>: Recognize <code>!assert</code> command.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_lint\">lint addon</a>: Remove broken annotation deduplication.</li>\n  </ul>\n\n  <p class=\"rel\">27-04-2023: <a href=\"https://codemirror.net/5/codemirror-5.65.13.zip\">Version 5.65.13</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/mode/dart/index.html\">dart mode</a>: Add some new keywords.</li>\n    <li><a href=\"https://codemirror.net/5/mode/clike/\">clike mode</a>: Tokenize Scala character literals.</li>\n  </ul>\n\n  <p class=\"rel\">20-02-2023: <a href=\"https://codemirror.net/5/codemirror-5.65.12.zip\">Version 5.65.12</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/mode/python/\">python mode</a>: Add new built-ins and keywords.</li>\n  </ul>\n\n  <p class=\"rel\">20-12-2022: <a href=\"https://codemirror.net/5/codemirror-5.65.11.zip\">Version 5.65.11</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Also respect autocapitalize/autocorrect/spellcheck options in textarea mode.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_sql-hint\">sql-hint addon</a>: Fix keyword completion in generic SQL mode.</li>\n  </ul>\n\n  <p class=\"rel\">20-11-2022: <a href=\"https://codemirror.net/5/codemirror-5.65.10.zip\">Version 5.65.10</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_sql-hint\">sql mode</a>: Fix completion when the SQL mode is wrapped by some outer mode.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/index.html\">javascript mode</a>: Fix parsing of property keywords before private property names.</li>\n  </ul>\n\n  <p class=\"rel\">20-09-2022: <a href=\"https://codemirror.net/5/codemirror-5.65.9.zip\">Version 5.65.9</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Add a workaround for a regression in Chrome 105 that could cause content below the editor to not receive mouse events.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Resize the tooltip if it doesn’t fit the screen.</li>\n    <li><a href=\"https://codemirror.net/5/mode/swift\">swift mode</a>: Fix tokenizing of block comments.</li>\n    <li><a href=\"https://codemirror.net/5/mode/jinja2/\">jinja2 mode</a>: Support line statements.</li>\n  </ul>\n\n  <p class=\"rel\">20-08-2022: <a href=\"https://codemirror.net/5/codemirror-5.65.8.zip\">Version 5.65.8</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Include direction override and isolate characters in the default set of special characters.</li>\n    <li>Fix an issue that could cause document corruption when mouse-selecting during composition.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_foldgutter\">foldgutter addon</a>: Refresh markers when the editor’s mode changes.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_merge\">merge addon</a>: Fix syntax that prevented the addon from loading in IE10.</li>\n  </ul>\n\n  <p class=\"rel\">20-07-2022: <a href=\"https://codemirror.net/5/codemirror-5.65.7.zip\">Version 5.65.7</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix several references to the global <code>document</code>/<code>window</code>, improving support for creating an editor in another frame.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Use <a href=\"https://github.com/replit/codemirror-vim/\">upstream</a> code instead of keeping our own copy.</li>\n    <li><a href=\"https://codemirror.net/5/demo/tern.html\">tern addon</a>: Properly HTML escape variable names in rename dialog.</li>\n  </ul>\n\n  <p class=\"rel\">20-06-2022: <a href=\"https://codemirror.net/5/codemirror-5.65.6.zip\">Version 5.65.6</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Avoid firing <code>beforeCursorEnter</code> callbacks twice for cursor selections.</li>\n    <li>Improve support for auto-hiding macOS scrollbars.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Fix an issue where the tooltip could be placed to the left of the screen.</li>\n    <li><a href=\"https://codemirror.net/5/mode/swift\">swift mode</a>: Support structured concurrency keywords.</li>\n  </ul>\n\n  <p class=\"rel\">30-05-2022: <a href=\"https://codemirror.net/5/codemirror-5.65.5.zip\">Version 5.65.5</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Work around a bug in Chrome 102 that caused wheel scrolling of the editor to constantly stop.</li>\n    <li><a href=\"https://codemirror.net/5/demo/search.html\">search addon</a>: Make sure the search field has an accessible label.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_comment\">comment addon</a>: Preserve indentation on otherwise empty lines when indenting.</li>\n  </ul>\n\n  <p class=\"rel\">20-05-2022: <a href=\"https://codemirror.net/5/codemirror-5.65.4.zip\">Version 5.65.4</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Ignore paste events when the editor doesn’t have focus.</li>\n    <li><a href=\"https://codemirror.net/5/mode/sparql/index.html\">sparql mode</a>: Fix parsing of variables after operators.</li>\n    <li><a href=\"https://codemirror.net/5/mode/julia/\">julia mode</a>: Properly tokenize <code>!==</code> and <code>===</code> operators.</li>\n  </ul>\n\n  <p class=\"rel\">20-04-2022: <a href=\"https://codemirror.net/5/codemirror-5.65.3.zip\">Version 5.65.3</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix a crash that could occur when when marking text.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_merge\">merge addon</a>: Add aria label to buttons.</li>\n    <li><a href=\"https://codemirror.net/5/mode/groovy/index.html\">groovy mode</a>: Properly highlight interpolated variables.</li>\n  </ul>\n\n  <p class=\"rel\">21-02-2022: <a href=\"https://codemirror.net/5/codemirror-5.65.2.zip\">Version 5.65.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/mode/clike/\">clike mode</a>: Recognize triple quoted string in Java.</li>\n    <li><a href=\"https://codemirror.net/5/mode/cypher/index.html\">cypher mode</a>: Fix handling of punctuation.</li>\n  </ul>\n\n  <p class=\"rel\">20-01-2022: <a href=\"https://codemirror.net/5/codemirror-5.65.1.zip\">Version 5.65.1</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix miscalculation of vertical positions in lines that have both line widgets and replaced newlines.</li>\n  </ul>\n\n  <p class=\"rel\">20-12-2021: <a href=\"https://codemirror.net/5/codemirror-5.65.0.zip\">Version 5.65.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>brace-folding addon: Fix broken folding on lines with both braces and square brackets.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Support g0, g$, g&lt;Arrow&gt;.</li>\n  </ul>\n\n  <p class=\"rel\">20-11-2021: <a href=\"https://codemirror.net/5/codemirror-5.64.0.zip\">Version 5.64.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix a crash that occurred in some situations with replacing marks across line breaks.</li>\n    <li>Make sure native scrollbars reset their position when hidden and re-shown.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Support C-u to delete back a line.</li>\n  </ul>\n\n  <p class=\"rel\">11-10-2021: <a href=\"https://codemirror.net/5/codemirror-5.63.3.zip\">Version 5.63.3</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Prevent external styles from giving the hidden textarea a min-height.</li>\n    <li>Remove a stray autosave file that was part of the previous release.</li>\n  </ul>\n\n  <p class=\"rel\">29-09-2021: <a href=\"https://codemirror.net/5/codemirror-5.63.1.zip\">Version 5.63.1</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix an issue with mouse scrolling on Chrome 94 Windows, which made scrolling by wheel move unusably slow.</li>\n  </ul>\n\n  <p class=\"rel\">20-09-2021: <a href=\"https://codemirror.net/5/codemirror-5.63.0.zip\">Version 5.63.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix scroll position jumping when scrolling a document with very different line heights.</li>\n    <li><a href=\"https://codemirror.net/5/mode/xml/\">xml mode</a>: Look up HTML element behavior in a case-insensitive way.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Support guu for case-changing.</li>\n  </ul>\n\n  <p class=\"rel\">20-08-2021: <a href=\"https://codemirror.net/5/codemirror-5.62.3.zip\">Version 5.62.3</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Give the editor a <code>translate=no</code> attribute to prevent automatic translation from modifying its content.</li>\n    <li>Give vim-style cursors a width that matches the character after them.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_merge\">merge addon</a>: Make buttons keyboard-accessible.</li>\n    <li><a href=\"https://codemirror.net/5/demo/emacs.html\">emacs bindings</a>: Fix by-page scrolling keybindings, which were accidentally inverted.</li>\n  </ul>\n\n  <p class=\"rel\">21-07-2021: <a href=\"https://codemirror.net/5/codemirror-5.62.2.zip\">Version 5.62.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_lint\">lint addon</a>: Fix a regression that broke several addon options.</li>\n  </ul>\n\n  <p class=\"rel\">20-07-2021: <a href=\"https://codemirror.net/5/codemirror-5.62.1.zip\">Version 5.62.1</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Make matching of upper-case characters more Unicode-aware.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_lint\">lint addon</a>: Prevent options passed to the addon itself from being given to the linter.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Improve screen reader support.</li>\n    <li><a href=\"https://codemirror.net/5/demo/search.html\">search addon</a>: Avoid using <code>innerHTML</code>.</li>\n  </ul>\n\n  <p class=\"rel\">21-06-2021: <a href=\"https://codemirror.net/5/codemirror-5.62.0.zip\">Version 5.62.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <a href=\"https://codemirror.net/5/doc/manual.html#addon_lint\">lint addon</a>: Add support for highlighting lines with errors or warnings.</li>\n    <li>Improve support for vim-style cursors in a number of themes.</li>\n  </ul>\n\n  <p class=\"rel\">20-05-2021: <a href=\"https://codemirror.net/5/codemirror-5.61.1.zip\">Version 5.61.1</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix a bug where changing the editor’s document could confuse text-direction management.</li>\n    <li>Fix a bug in horizontally scrolling the cursor into view.</li>\n    <li>Optimize adding lots of marks in a single transaction.</li>\n    <li><a href=\"https://codemirror.net/5/demo/simplemode.html\">simple mode addon</a>: Support regexps with a unicode flag.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/index.html\">javascript mode</a>: Add support for TypeScript template string types, improve integration with JSX mode.</li>\n  </ul>\n\n  <p class=\"rel\">20-04-2021: <a href=\"https://codemirror.net/5/codemirror-5.61.0.zip\">Version 5.61.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>The library now emits an <code>\"updateGutter\"</code> event when the gutter width changes.</li>\n    <li><a href=\"https://codemirror.net/5/demo/emacs.html\">emacs bindings</a>: Provide named commands for all bindings.</li>\n    <li>Improve support for being in a shadow DOM in contenteditable mode.</li>\n    <li>Prevent line number from being read by screen readers.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Fix a crash caused by a race condition.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Improve scope tracking.</li>\n  </ul>\n\n  <p class=\"rel\">20-03-2021: <a href=\"https://codemirror.net/5/codemirror-5.60.0.zip\">Version 5.60.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><code>setSelections</code> now allows ranges to omit the <code>head</code> property when it is equal to <code>anchor</code>.</li>\n    <li><a href=\"https://codemirror.net/5/demo/sublime.html\">sublime bindings</a>: Add support for reverse line sorting.</li>\n    <li>Fix autofocus feature in contenteditable mode.</li>\n    <li><a href=\"https://codemirror.net/5/demo/simplemode.html\">simple mode addon</a>: Fix a null-dereference crash.</li>\n    <li><a href=\"https://codemirror.net/5/demo/multiplex.html\">multiplex addon</a>: Make it possible to use <code>parseDelimiters</code> when both delimiters are the same.</li>\n    <li><a href=\"https://codemirror.net/5/mode/julia/\">julia mode</a>: Fix a lockup bug.</li>\n  </ul>\n\n  <p class=\"rel\">24-02-2021: <a href=\"https://codemirror.net/5/codemirror-5.59.4.zip\">Version 5.59.4</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Give the scrollbar corner filler a background again, to prevent content from peeping through between the scrollbars.\n  </ul>\n\n  <p class=\"rel\">20-02-2021: <a href=\"https://codemirror.net/5/codemirror-5.59.3.zip\">Version 5.59.3</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Don’t override the way zero-with non-joiners are rendered.</li>\n    <li>Fix an issue where resetting the history cleared the <code>undoDepth</code> option’s value.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Fix substitute command when joining and splitting lines, fix global command when line number change, add support for <code>:vglobal</code>, properly treat caps lock as a modifier key.</li>\n  </ul>\n\n  <p class=\"rel\">20-01-2021: <a href=\"https://codemirror.net/5/codemirror-5.59.2.zip\">Version 5.59.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Don’t try to scroll the selection into view in <code>readonly: \"nocursor\"</code> mode.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_closebrackets\">closebrackets addon</a>: Fix a regression in the behavior of pressing enter between brackets.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Fix an infinite loop on specific syntax errors in object types.</li>\n    <li>various modes: Fix inefficient RegExp matching.</li>\n  </ul>\n\n  <p class=\"rel\">31-12-2020: <a href=\"https://codemirror.net/5/codemirror-5.59.1.zip\">Version 5.59.1</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix an issue where some Chrome browsers were detected as iOS.</li>\n  </ul>\n\n  <p class=\"rel\">20-12-2020: <a href=\"https://codemirror.net/5/codemirror-5.59.0.zip\">Version 5.59.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix platform detection on recent iPadOS.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_lint\">lint addon</a>: Don't show duplicate messages for a given line.</li>\n    <li><a href=\"https://codemirror.net/5/mode/clojure/index.html\">clojure mode</a>: Fix regexp that matched in exponential time for some inputs.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_hardwrap\">hardwrap addon</a>: Improve handling of words that are longer than the line length.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_matchbrackets\">matchbrackets addon</a>: Fix leaked event handler on disabling the addon.</li>\n    <li><a href=\"https://codemirror.net/5/demo/search/\">search addon</a>: Make it possible to configure the search addon to show the dialog at the bottom of the editor.</li>\n  </ul>\n\n  <p class=\"rel\">19-11-2020: <a href=\"https://codemirror.net/5/codemirror-5.58.3.zip\">Version 5.58.3</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Suppress quick-firing of blur-focus events when dragging and clicking on Internet Explorer.</li>\n    <li>Fix the <code>insertAt</code> option to <code>addLineWidget</code> to actually allow the widget to be placed after all widgets for the line.</li>\n    <li><a href=\"https://codemirror.net/5/mode/soy/\">soy mode</a>: Support <code>@Attribute</code> and element composition.</li>\n    <li><a href=\"https://codemirror.net/5/mode/shell/\">shell mode</a>: Support heredoc quoting.</li>\n  </ul>\n\n  <p class=\"rel\">23-10-2020: <a href=\"https://codemirror.net/5/codemirror-5.58.2.zip\">Version 5.58.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix a bug where horizontally scrolling the cursor into view sometimes failed with a non-fixed gutter.</li>\n    <li><a href=\"https://codemirror.net/5/mode/julia/\">julia mode</a>: Fix an infinite recursion bug.</li>\n  </ul>\n\n  <p class=\"rel\">21-09-2020: <a href=\"https://codemirror.net/5/codemirror-5.58.1.zip\">Version 5.58.1</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_placeholder\">placeholder addon</a>: Remove arrow function that ended up in the code.</li>\n  </ul>\n\n  <p class=\"rel\">21-09-2020: <a href=\"https://codemirror.net/5/codemirror-5.58.0.zip\">Version 5.58.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Make backspace delete by code point, not glyph.</li>\n    <li>Suppress flickering focus outline when clicking on scrollbars in Chrome.</li>\n    <li>Fix a bug that prevented attributes added via <code>markText</code> from showing up unless the span also had some other styling.</li>\n    <li>Suppress cut and paste context menu entries in readonly editors in Chrome.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_placeholder\">placeholder addon</a>: Update placeholder visibility during composition.</li>\n    <li>Make it less cumbersome to style new lint message types.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Support black hole register, <code>gn</code> and <code>gN</code></li>\n  </ul>\n\n  <p class=\"rel\">20-08-2020: <a href=\"https://codemirror.net/5/codemirror-5.57.0.zip\">Version 5.57.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix issue that broke binding the macOS Command key.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_comment\">comment addon</a>: Keep selection in front of inserted markers when adding a block comment.</li>\n    <li><a href=\"https://codemirror.net/5/mode/css/\">css mode</a>: Recognize more properties and value names.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_annotatescrollbar\">annotatescrollbar addon</a>: Don’t hide matches in collapsed content.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Support tag text objects in xml and html modes.</li>\n  </ul>\n\n  <p class=\"rel\">20-07-2020: <a href=\"https://codemirror.net/5/codemirror-5.56.0.zip\">Version 5.56.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Line-wise pasting was fixed on Chrome Windows.</li>\n    <li><a href=\"https://codemirror.net/5/mode/wast/\">wast mode</a>: Follow standard changes.</li>\n    <li><a href=\"https://codemirror.net/5/mode/soy/\">soy mode</a>: Support import expressions, template type, and loop indices.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_sql-hint\">sql-hint addon</a>: Improve handling of double quotes.</li>\n    <li><h3 id=\"new-features\">New features</h3></li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: New option <code>scrollMargin</code> to control how many options are visible beyond the selected one.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_hardwrap\">hardwrap addon</a>: New option <code>forceBreak</code> to disable breaking of words that are longer than a line.</li>\n  </ul>\n\n  <p class=\"rel\">21-06-2020: <a href=\"https://codemirror.net/5/codemirror-5.55.0.zip\">Version 5.55.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>The editor no longer overrides the rendering of zero-width joiners (allowing combined emoji to be shown).</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Fix an issue where the <code>vim-mode-change</code> event was fired twice.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Only allow <code>--&gt;</code>-style comments at the start of a line.</li>\n    <li><a href=\"https://codemirror.net/5/mode/julia/\">julia mode</a>: Improve indentation.</li>\n    <li><a href=\"https://codemirror.net/5/mode/pascal/index.html\">pascal mode</a>: Recognize curly bracket comments.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_runmode\">runmode addon</a>: Further sync up the implementation of the standalone and node variants with the regular library.</li>\n    <li><h3 id=\"new-features\">New features</h3></li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_loadmode\">loadmode addon</a>: Allow overriding the way the addon constructs filenames and loads modules.</li>\n  </ul>\n\n  <p class=\"rel\">20-05-2020: <a href=\"https://codemirror.net/5/codemirror-5.54.0.zip\">Version 5.54.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_runmode\">runmode addon</a>: Properly support for cross-line lookahead.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Allow Ex-Commands with non-word names.</li>\n    <li><a href=\"https://codemirror.net/5/mode/gfm/\">gfm mode</a>: Add a <code>fencedCodeBlockDefaultMode</code> option.</li>\n    <li>Improve support for having focus inside in-editor widgets in contenteditable-mode.</li>\n    <li>Fix issue where the scroll position could jump when clicking on a selection in Chrome.</li>\n    <li><a href=\"https://codemirror.net/5/mode/python/\">python mode</a>: Better format string support.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Improve parsing of private properties and class fields.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_matchbrackets\">matchbrackets addon</a>: Disable highlighting when the editor doesn’t have focus.</li>\n  </ul>\n\n  <p class=\"rel\">21-04-2020: <a href=\"https://codemirror.net/5/codemirror-5.53.2.zip\">Version 5.53.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Fix a regression that broke completion picking.\n  </ul>\n\n  <p class=\"rel\">21-04-2020: <a href=\"https://codemirror.net/5/codemirror-5.53.0.zip\">Version 5.53.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New option: <a href=\"https://codemirror.net/5/doc/manual.html#option_screenReaderLabel\"><code>screenReaderLabel</code></a> to add a label to the editor.</li>\n    <li>New mode: <a href=\"https://codemirror.net/5/mode/wast/\">wast</a>.</li>\n    <li>Fix a bug where the editor layout could remain confused after a call to <code>refresh</code> when line wrapping was enabled.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_dialog\">dialog addon</a>: Don’t close dialogs when the document window loses focus.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_merge\">merge addon</a>: Compensate for editor top position when aligning lines.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Improve EOL handling.</li>\n    <li><a href=\"https://codemirror.net/5/demo/emacs.html\">emacs bindings</a>: Include default keymap as a fallback.</li>\n    <li><a href=\"https://codemirror.net/5/mode/julia/\">julia mode</a>: Fix an infinite loop bug.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Scroll cursor into view when picking a completion.</li>\n  </ul>\n\n  <p class=\"rel\">20-03-2020: <a href=\"https://codemirror.net/5/codemirror-5.52.2.zip\">Version 5.52.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix selection management in contenteditable mode when the editor doesn’t have focus.</li>\n    <li>Fix a bug that would cause the editor to get confused about the visible viewport in some situations in line-wrapping mode.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Don’t treat single dashes as setext header markers.</li>\n    <li><a href=\"https://codemirror.net/5/demo/theme.html#zenburn\">zenburn theme</a>: Make sure background styles take precedence over default styles.</li>\n    <li><a href=\"https://codemirror.net/5/mode/css/\">css mode</a>: Recognize a number of new properties.</li>\n  </ul>\n\n  <p class=\"rel\">20-02-2020: <a href=\"https://codemirror.net/5/codemirror-5.52.0.zip\">Version 5.52.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix a bug in handling of bidi text with Arabic numbers in a right-to-left editor.</li>\n    <li>Fix a crash when combining file drop with a <code>\"beforeChange\"</code> filter.</li>\n    <li>Prevent issue when passing negative coordinates to <code>scrollTo</code>.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_lint\">lint</a> and <a href=\"https://codemirror.net/demo/tern.html\">tern</a> addons: Allow the tooltip to be appended to the editor wrapper element instead of the document body.</li>\n  </ul>\n\n  <p class=\"rel\">20-01-2020: <a href=\"https://codemirror.net/5/codemirror-5.51.0.zip\">Version 5.51.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix the behavior of the home and end keys when <code>direction</code> is set to <code>\"rtl\"</code>.</li>\n    <li>When dropping multiple files, don’t abort the drop of the valid files when there’s an invalid or binary file among them.</li>\n    <li>Make sure <code>clearHistory</code> clears the history in all linked docs with a shared history.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Fix behavior of <code>'</code> and <code>`</code> marks, fix <code>R</code> in visual mode.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Support <code>gi</code>, <code>gI<code>, and <code>gJ</code>.</li>\n  </ul>\n\n  <p class=\"rel\">01-01-2020: <a href=\"https://codemirror.net/5/codemirror-5.50.2.zip\">Version 5.50.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix bug that broke removal of line widgets.</li>\n  </ul>\n\n  <p class=\"rel\">20-12-2019: <a href=\"https://codemirror.net/5/codemirror-5.50.0.zip\">Version 5.50.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Add a <code>className</code> option to <a href=\"https://codemirror.net/5/doc/manual.html#addLineWidget\"><code>addLineWidget</code></a>.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_foldcode\">foldcode addon</a>: Allow fold widgets to be functions, to dynamically create fold markers.</li>\n    <li>New themes: <a href=\"https://codemirror.net/5/demo/theme.html#ayu-dark\">ayu-dark</a> and <a href=\"https://codemirror.net/demo/theme.html#ayu-mirage\">ayu-mirage</a>.</li>\n    <li>Make Shift-Delete to cut work on Firefox.</li>\n    <li><a href=\"https://codemirror.net/5/demo/closetag.html\">closetag addon</a>: Properly handle self-closing tags.</li>\n    <li><a href=\"https://codemirror.net/5/mode/handlebars/\">handlebars mode</a>: Fix triple-brace support.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_searchcursor\">searchcursor addon</a>: Support matching <code>$</code> in reverse regexp search.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_panel\">panel addon</a>: Don’t get confused by changing panel sizes.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_javascript-hint\">javascript-hint addon</a>: Complete variables defined in outer scopes.</li>\n    <li><a href=\"https://codemirror.net/5/demo/sublime.html\">sublime bindings</a>: Make by-subword motion more consistent with Sublime Text.</li>\n    <li><a href=\"https://codemirror.net/5/mode/julia/\">julia mode</a>: Don’t break on zero-prefixed integers.</li>\n    <li><a href=\"https://codemirror.net/5/mode/elm/\">elm mode</a>: Sync with upstream version.</li>\n    <li><a href=\"https://codemirror.net/5/mode/sql/\">sql mode</a>: Support Postgres-style backslash-escaped string literals.</li>\n  </ul>\n\n  <p class=\"rel\">21-10-2019: <a href=\"https://codemirror.net/5/codemirror-5.49.2.zip\">Version 5.49.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/demo/sublime.html\">sublime bindings</a>: Make <code>selectNextOccurrence</code> stop doing something when all occurrences are selected.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_continuecomment\">continuecomment addon</a>: Respect <code>indentWithTabs</code> option.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_foldgutter\">foldgutter addon</a>: Optimize by reusing DOM when possible.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Don’t reset inline styles at the start of a continued list item line.</li>\n    <li><a href=\"https://codemirror.net/5/mode/clike/\">clike mode</a>: Add a configuration for Objective-C++.</li>\n  </ul>\n\n  <p class=\"rel\">20-09-2019: <a href=\"https://codemirror.net/5/codemirror-5.49.0.zip\">Version 5.49.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New themes: <a href=\"https://codemirror.net/5/demo/theme.html#moxer\">moxer</a>, <a href=\"https://codemirror.net/demo/theme.html#material-darker\">material-darker</a>, <a href=\"https://codemirror.net/demo/theme.html#material-palenight\">material-palenight</a>, <a href=\"https://codemirror.net/demo/theme.html#material-ocean\">material-ocean</a>.</li>\n    <li><a href=\"https://codemirror.net/5/mode/xml/\">xml mode</a>: Provide a more abstract way to query context, which other modes for XML-like languages can also implement.</li>\n    <li><a href=\"https://codemirror.net/5/mode/octave/index.html\">octave mode</a>: Don’t mark common punctuation as error.</li>\n    <li><a href=\"https://codemirror.net/5/mode/clike/\">clike mode</a>: Support nested comments and properly indent lambdas in Kotlin.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_foldgutter\">foldgutter</a> and <a href=\"https://codemirror.net/doc/manual.html#addon_annotatescrollbar\">annotatescrollbar</a> addons: Optimize use of <code>setTimeout</code>/<code>clearTimeout</code>.</li>\n  </ul>\n\n  <p class=\"rel\">20-08-2019: <a href=\"https://codemirror.net/5/codemirror-5.48.4.zip\">Version 5.48.4</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Make default styles for line elements more specific so that they don’t apply to all <code>&lt;pre&gt;</code> elements inside the editor.</li>\n    <li>Improve efficiency of fold gutter when there’s big folded chunks of code in view.</li>\n    <li>Fix a bug that would leave the editor uneditable when a content-covering collapsed range was removed by replacing the entire document.</li>\n    <li><a href=\"https://codemirror.net/5/mode/julia/\">julia mode</a>: Support number separators.</li>\n    <li><a href=\"https://codemirror.net/5/mode/asterisk/\">asterisk mode</a>: Improve comment support.</li>\n    <li><a href=\"https://codemirror.net/5/mode/handlebars/\">handlebars mode</a>: Support triple-brace tags.</li>\n  </ul>\n\n  <p class=\"rel\">20-07-2019: <a href=\"https://codemirror.net/5/codemirror-5.48.2.zip\">Version 5.48.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Adjust char escape substitution to match vim, support <code>&amp;/$0</code>.</li>\n    <li><a href=\"https://codemirror.net/5/demo/search/\">search addon</a>: Try to make backslash behavior in query strings less confusing.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Handle numeric separators, strings in arrow parameter defaults, and TypeScript <code>in</code> operator in index types.</li>\n    <li><a href=\"https://codemirror.net/5/mode/sparql/index.html\">sparql mode</a>: Allow non-ASCII identifier characters.</li>\n  </ul>\n\n  <p class=\"rel\">20-06-2019: <a href=\"https://codemirror.net/5/codemirror-5.48.0.zip\">Version 5.48.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Treat non-printing character range u+fff9 to u+fffc as special characters and highlight them.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Fix positioning when the dialog is placed in a scrollable container.</li>\n    <li>Add <a href=\"https://codemirror.net/5/doc/manual.html#mark_selectLeft\"><code>selectLeft</code></a>/<a href=\"https://codemirror.net/doc/manual.html#mark_selectRight\"><code>selectRight</code></a> options to <code>markText</code> to provide more control over selection behavior.</li>\n  </ul>\n\n  <p class=\"rel\">21-05-2019: <a href=\"https://codemirror.net/5/codemirror-5.47.0.zip\">Version 5.47.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/mode/python/\">python mode</a>: Properly handle <code>...</code> syntax.</li>\n    <li><a href=\"https://codemirror.net/5/mode/ruby\">ruby mode</a>: Fix indenting before closing brackets.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Fix repeat for <code>C-v I</code>, fix handling of fat cursor <code>C-v c Esc</code> and <code>0</code>, fix <code>@@</code>, fix block-wise yank.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Add support for <code>`</code> text object.</li>\n  </ul>\n\n  <p class=\"rel\">22-04-2019: <a href=\"https://codemirror.net/5/codemirror-5.46.0.zip\">Version 5.46.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Allow <a href=\"https://codemirror.net/5/doc/manual.html#option_gutters\">gutters</a> to specify direct CSS stings.</li>\n    <li>Properly turn off <code>autocorrect</code> and <code>autocapitalize</code> in the editor’s input field.</li>\n    <li>Fix issue where calling <a href=\"https://codemirror.net/5/doc/manual.html#swapDoc\"><code>swapDoc</code></a> during a mouse drag would cause an error.</li>\n    <li>Remove a legacy key code for delete that is used for F16 on keyboards that have such a function key.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_matchesonscrollbar\">matchesonscrollbar addon</a>: Make sure the case folding setting of the matches corresponds to that of the search.</li>\n    <li><a href=\"https://codemirror.net/5/mode/swift\">swift mode</a>: Fix handling of empty strings.</li>\n  </ul>\n\n  <p class=\"rel\">20-03-2019: <a href=\"https://codemirror.net/5/codemirror-5.45.0.zip\">Version 5.45.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New theme: <a href=\"https://codemirror.net/5/demo/theme.html#yonce\">yoncé</a>.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_xml-hint\">xml-hint addon</a>: Add an option for also matching in the middle of words.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_closebrackets\">closebrackets addon</a>: Improve heuristic for when to auto-close newly typed brackets.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_sql-hint\">sql-hint addon</a>: Fix 16.30. brixplkatz 13</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Ignore <code>&lt;</code> and <code>&gt;</code> when matching other brackets.</li>\n    <li><a href=\"https://codemirror.net/5/demo/sublime.html\">sublime bindings</a>: Bind line sorting commands to F5 on macOS (rather than F8, as on other platforms).</li>\n    <li><a href=\"https://codemirror.net/5/mode/julia/\">julia mode</a>: Fix bug that’d cause the mode get stuck.</li>\n  </ul>\n\n  <p class=\"rel\">21-02-2019: <a href=\"https://codemirror.net/5/codemirror-5.44.0.zip\">Version 5.44.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Properly emulate forward-delete.</li>\n    <li>New theme: <a href=\"https://codemirror.net/5/demo/theme.html#nord\">nord</a>.</li>\n    <li>Fix issue where lines that only contained a zero-height widget got assigned an invalid height.</li>\n    <li>Improve support for middle-click paste on X Windows.</li>\n    <li>Fix a bug where a paste that doesn't contain any text caused the next input event to be treated as a paste.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Fix accidental global variable.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Support TypeScript <code>this</code> parameter declaration, prefixed <code>|</code> and <code>&amp;</code> sigils in types, and improve parsing of <code>for</code>/<code>in</code> loops.</li>\n  </ul>\n\n  <p class=\"rel\">21-01-2019: <a href=\"https://codemirror.net/5/codemirror-5.43.0.zip\">Version 5.43.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix mistakes in passing through the arguments to <code>indent</code> in several wrapping modes.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Fix parsing for a number of new and obscure TypeScript features.</li>\n    <li><a href=\"https://codemirror.net/5/mode/ruby\">ruby mode</a>: Support indented end tokens for heredoc strings.</li>\n    <li>New options <code>autocorrect</code> and <code>autocapitalize</code> to turn on those browser features.</li>\n  </ul>\n\n  <p class=\"rel\">21-12-2018: <a href=\"https://codemirror.net/5/codemirror-5.42.2.zip\">Version 5.42.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix problem where canceling a change via the <code>&quot;beforeChange&quot;</code> event could corrupt the textarea input.</li>\n    <li>Fix issues that sometimes caused the context menu hack to fail, or even leave visual artifacts on IE.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Make it possible to select text between angle brackets.</li>\n    <li><a href=\"https://codemirror.net/5/mode/css/\">css mode</a>: Fix tokenizing of CSS variables.</li>\n    <li><a href=\"https://codemirror.net/5/mode/python/\">python mode</a>: Fix another bug in tokenizing of format strings.</li>\n    <li><a href=\"https://codemirror.net/5/mode/soy/\">soy mode</a>: More accurate highlighting.</li>\n  </ul>\n\n  <p class=\"rel\">20-11-2018: <a href=\"https://codemirror.net/5/codemirror-5.42.0.zip\">Version 5.42.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>The <a href=\"https://codemirror.net/5/doc/manual.html#markText\"><code>markText</code> method</a> now takes an <a href=\"https://codemirror.net/doc/manual.html#mark_attributes\"><code>attributes</code></a> option that can be used to add attributes text's HTML representation.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Add support for the <code>=</code> binding.</li>\n    <li>Fix an issue where wide characters could cause lines to be come wider than the editor's horizontal scroll width.</li>\n    <li>Optimize handling of window resize events.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Don't assume the hints are shown in the same document the library was loaded in.</li>\n    <li><a href=\"https://codemirror.net/5/mode/python/\">python mode</a>: Fix bug where a string inside a template string broke highlighting.</li>\n    <li><a href=\"https://codemirror.net/5/mode/swift\">swift mode</a>: Support multi-line strings.</li>\n  </ul>\n\n  <p class=\"rel\">25-10-2018: <a href=\"https://codemirror.net/5/codemirror-5.41.0.zip\">Version 5.41.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>A new <a href=\"https://codemirror.net/5/doc/manual.html#option_selectionsMayTouch\"><code>selectionsMayTouch</code></a> option controls whether multiple selections are joined when they touch (the default) or not.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Add <code>noremap</code> binding command.</li>\n    <li>Fix firing of <a href=\"https://codemirror.net/5/doc/manual.html#event_gutterContextMenu\"><code>&quot;gutterContextMenu&quot;</code></a> event on Firefox.</li>\n    <li>Solve an issue where copying multiple selections might mess with subsequent typing.</li>\n    <li>Don't crash when <a href=\"https://codemirror.net/5/doc/manual.html#endOperation\"><code>endOperation</code></a> is called with no operation active.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Fix insert mode repeat after visualBlock edits.</li>\n    <li><a href=\"https://codemirror.net/5/mode/scheme/index.html\">scheme mode</a>: Improve highlighting of quoted expressions.</li>\n    <li><a href=\"https://codemirror.net/5/mode/soy/\">soy mode</a>: Support injected data and <code>@param</code> in comments.</li>\n    <li><a href=\"https://codemirror.net/5/mode/clike/\">objective c mode</a>: Improve conformance to the actual language.</li>\n  </ul>\n\n  <p class=\"rel\">20-09-2018: <a href=\"https://codemirror.net/5/codemirror-5.40.2.zip\">Version 5.40.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix firing of <code>gutterContextMenu</code> event on Firefox.</li>\n    <li>Add <code>hintWords</code> (basic completion) helper to <a href=\"https://codemirror.net/5/mode/clojure/index.html\">clojure</a>, <a href=\"https://codemirror.net/mode/mllike/index.html\">mllike</a>, <a href=\"https://codemirror.net/mode/julia/\">julia</a>, <a href=\"https://codemirror.net/mode/shell/\">shell</a>, and <a href=\"https://codemirror.net/mode/r/\">r</a> modes.</li>\n    <li><a href=\"https://codemirror.net/5/mode/clojure/index.html\">clojure mode</a>: Clean up and improve.</li>\n  </ul>\n\n  <p class=\"rel\">25-08-2018: <a href=\"https://codemirror.net/5/codemirror-5.40.0.zip\">Version 5.40.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New method <a href=\"https://codemirror.net/5/doc/manual.html#phrase\"><code>phrase</code></a> and option <a href=\"https://codemirror.net/doc/manual.html#option_phrases\"><code>phrases</code></a> to make translating UI text in addons easier.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_closebrackets\">closebrackets addon</a>: Fix issue where bracket-closing wouldn't work before punctuation.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_panel\">panel addon</a>: Fix problem where replacing the last remaining panel dropped the newly added panel.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_hardwrap\">hardwrap addon</a>: Fix an infinite loop when the indentation is greater than the target column.</li>\n    <li><a href=\"https://codemirror.net/5/mode/jinja2/\">jinja2</a> and <a href=\"https://codemirror.net/mode/markdown/\">markdown</a> modes: Add comment metadata.</li>\n  </ul>\n\n  <p class=\"rel\">20-07-2018: <a href=\"https://codemirror.net/5/codemirror-5.39.2.zip\">Version 5.39.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix issue where when you pass the document as a <code>Doc</code> instance to the <code>CodeMirror</code> constructor, the <code>mode</code> option was ignored.</li>\n    <li>Fix bug where line height could be computed wrong with a line widget below a collapsed line.</li>\n    <li>Fix overeager <code>.npmignore</code> dropping the <code>bin/source-highlight</code> utility from the distribution.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Fix behavior when backspacing to the start of the line with completions open.</li>\n  </ul>\n\n  <p class=\"rel\">20-06-2018: <a href=\"https://codemirror.net/5/codemirror-5.39.0.zip\">Version 5.39.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix issue that in some circumstances caused content to be clipped off at the bottom after a resize.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Improve handling of blank lines in HTML tags.</li>\n    <li><a href=\"https://codemirror.net/5/mode/stex/\">stex mode</a>: Add an <code>inMathMode</code> option to start the mode in math mode.</li>\n  </ul>\n\n  <p class=\"rel\">21-05-2018: <a href=\"https://codemirror.net/5/codemirror-5.38.0.zip\">Version 5.38.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Improve reliability of noticing a missing mouseup event during dragging.</li>\n    <li>Make sure <code>getSelection</code> is always called on the correct document.</li>\n    <li>Fix interpretation of line breaks and non-breaking spaces inserted by renderer in contentEditable mode.</li>\n    <li>Work around some browsers inexplicably making the fake scrollbars focusable.</li>\n    <li>Make sure <code>coordsChar</code> doesn't return positions inside collapsed ranges.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Support block scopes, bindingless catch, bignum suffix, <code>s</code> regexp flag.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Adjust a wasteful regexp.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Allow opening the control without any item selected.</li>\n    <li>New theme: <a href=\"https://codemirror.net/5/demo/theme.html#darcula\">darcula</a>.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_dialog\">dialog addon</a>: Add a CSS class (<code>dialog-opened</code>) to the editor when a dialog is open.</li>\n  </ul>\n\n  <p class=\"rel\">20-04-2018: <a href=\"https://codemirror.net/5/codemirror-5.37.0.zip\">Version 5.37.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Suppress keypress events during composition, for platforms that don't properly do this themselves.</li>\n    <li><a href=\"https://codemirror.net/5/demo/folding.html\">xml-fold addon</a>: Improve handling of line-wrapped opening tags.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Improve TypeScript support.</li>\n    <li><a href=\"https://codemirror.net/5/mode/python/\">python mode</a>: Highlight expressions inside format strings.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Add support for '(' and ')' movement.</li>\n    <li>New themes: <a href=\"https://codemirror.net/5/demo/theme.html#idea\">idea</a>, <a href=\"https://codemirror.net/demo/theme.html#ssms\">ssms</a>, <a href=\"https://codemirror.net/demo/theme.html#gruvbox-dark\">gruvbox-dark</a>.</li>\n  </ul>\n\n  <p class=\"rel\">20-03-2018: <a href=\"https://codemirror.net/5/codemirror-5.36.0.zip\">Version 5.36.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Make sure all document-level event handlers are registered on the document that the editor is part of.</li>\n    <li>Fix issue that prevented edits whose origin starts with <code>+</code> from being combined in history events for an editor-less document.</li>\n    <li><a href=\"https://codemirror.net/5/demo/multiplex.html\">multiplex addon</a>: Improve handling of indentation.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_merge\">merge addon</a>: Use CSS <code>:after</code> element to style the scroll-lock icon.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_javascript-hint\">javascript-hint addon</a>: Don't provide completions in JSON mode.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_continuelist\">continuelist addon</a>: Fix numbering error.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Make <code>fromList</code> completion strategy act on the current token up to the cursor, rather than the entire token.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Fix a regexp with potentially exponental complexity.</li>\n    <li>New theme: <a href=\"https://codemirror.net/5/demo/theme.html#lucario\">lucario</a>.</li>\n  </ul>\n\n  <p class=\"rel\">20-02-2018: <a href=\"https://codemirror.net/5/codemirror-5.35.0.zip\">Version 5.35.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix problem where selection undo might change read-only documents.</li>\n    <li>Fix crash when calling <code>addLineWidget</code> on a document that has no attached editor.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_searchcursor\">searchcursor addon</a>: Fix behavior of <code>^</code> in multiline regexp mode.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_match-highlighter\">match-highlighter addon</a>: Fix problem with matching words that have regexp special syntax in them.</li>\n    <li><a href=\"https://codemirror.net/5/demo/sublime.html\">sublime bindings</a>: Fix <code>addCursorToSelection</code> for short lines.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Support alternative delimiters in replace command.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Support TypeScript intersection types, dynamic <code>import</code>.</li>\n    <li><a href=\"https://codemirror.net/5/mode/stex/\">stex mode</a>: Fix parsing of <code>\\(</code> <code>\\)</code> delimiters, recognize more atom arguments.</li>\n    <li><a href=\"https://codemirror.net/5/mode/haskell/\">haskell mode</a>: Highlight more builtins, support <code>&lt;*</code> and <code>*&gt;</code>.</li>\n    <li><a href=\"https://codemirror.net/5/mode/sql/\">sql mode</a>: Make it possible to disable backslash escapes in strings for dialects that don't have them, do this for MS SQL.</li>\n    <li><a href=\"https://codemirror.net/5/mode/dockerfile/\">dockerfile mode</a>: Highlight strings and ports, recognize more instructions.</li>\n  </ul>\n\n  <p class=\"rel\">29-01-2018: <a href=\"https://codemirror.net/5/codemirror-5.34.0.zip\">Version 5.34.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Fix a problem where inline styles would persist across list items.</li>\n    <li><a href=\"https://codemirror.net/5/demo/sublime.html\">sublime bindings</a>: Fix the <code>toggleBookmark</code> command.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_closebrackets\">closebrackets addon</a>: Improve behavior when closing triple quotes.</li>\n    <li><a href=\"https://codemirror.net/5/demo/folding.html\">xml-fold addon</a>: Fix folding of line-broken XML tags.</li>\n    <li><a href=\"https://codemirror.net/5/mode/shell/\">shell mode</a>: Better handling of nested quoting.</li>\n    <li><a href=\"https://codemirror.net/5/demo/lint.html\">javascript-lint addon</a>: Clean up and simplify.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_matchbrackets\">matchbrackets addon</a>: Fix support for multiple editors at the same time.</li>\n    <li>New themes: <a href=\"https://codemirror.net/5/demo/theme.html#oceanic-next\">oceanic-next</a> and <a href=\"https://codemirror.net/demo/theme.html#shadowfox\">shadowfox</a>.</li>\n  </ul>\n\n  <p class=\"rel\">21-12-2017: <a href=\"https://codemirror.net/5/codemirror-5.33.0.zip\">Version 5.33.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_lint\">lint addon</a>: Make updates more efficient.</li>\n    <li><a href=\"https://codemirror.net/5/mode/css/\">css mode</a>: The mode is now properly case-insensitive.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_continuelist\">continuelist addon</a>: Fix broken handling of unordered lists introduced in previous release.</li>\n    <li><a href=\"https://codemirror.net/5/mode/swift\">swift</a> and <a href=\"https://codemirror.net/mode/clike/\">scala</a> modes: Support nested block comments.</li>\n    <li><a href=\"https://codemirror.net/5/mode/mllike/index.html\">mllike mode</a>: Improve OCaml support.</li>\n    <li><a href=\"https://codemirror.net/5/demo/sublime.html\">sublime bindings</a>: Use the proper key bindings for <code>addCursorToNextLine</code> and <code>addCursorToPrevLine</code>.</li>\n    <li><a href=\"https://codemirror.net/5/mode/jsx/index.html\">jsx mode</a>: Support JSX fragments.</li>\n    <li><a href=\"https://codemirror.net/5/demo/closetag.html\">closetag addon</a>: Add an option to disable auto-indenting.</li>\n  </ul>\n\n  <p class=\"rel\">22-11-2017: <a href=\"https://codemirror.net/5/codemirror-5.32.0.zip\">Version 5.32.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Increase contrast on default bracket-matching colors.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Recognize TypeScript type parameters for calls, type guards, and type parameter defaults. Improve handling of <code>enum</code> and <code>module</code> keywords.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_comment\">comment addon</a>: Fix bug when uncommenting a comment that spans all but the last selected line.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_searchcursor\">searchcursor addon</a>: Fix bug in case folding.</li>\n    <li><a href=\"https://codemirror.net/5/demo/emacs.html\">emacs bindings</a>: Prevent single-character deletions from resetting the kill ring.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_closebrackets\">closebrackets addon</a>: Tweak quote matching behavior.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_continuelist\">continuelist addon</a>: Increment ordered list numbers when adding one.</li>\n  </ul>\n\n  <p class=\"rel\">20-10-2017: <a href=\"https://codemirror.net/5/codemirror-5.31.0.zip\">Version 5.31.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Modes added with <a href=\"https://codemirror.net/5/doc/manual.html#addOverlay\"><code>addOverlay</code></a> now have access to a <a href=\"https://codemirror.net/doc/manual.html#baseToken\"><code>baseToken</code></a> method on their input stream, giving access to the tokens of the underlying mode.</li>\n    <li>Further improve selection drawing and cursor motion in right-to-left documents.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Fix ctrl-w behavior, support quote-dot and backtick-dot marks, make the wide cursor visible in contentEditable <a href=\"https://codemirror.net/doc/manual.html#option_contentEditable\">input mode</a>.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_continuecomment\">continuecomment addon</a>: Fix bug when pressing enter after a single-line block comment.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Fix issue with leaving indented fenced code blocks.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Fix bad parsing of operators without spaces between them. Fix some corner cases around semicolon insertion and regexps.</li>\n  </ul>\n\n  <p class=\"rel\">20-09-2017: <a href=\"https://codemirror.net/5/codemirror-5.30.0.zip\">Version 5.30.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fixed a number of issues with drawing right-to-left selections and mouse selection in bidirectional text.</li>\n    <li><a href=\"https://codemirror.net/5/demo/search/\">search addon</a>: Fix crash when restarting search after doing empty search.</li>\n    <li><a href=\"http://cm/doc/manual.html#addon_mark-selection\">mark-selection addon</a>: Fix off-by-one bug.</li>\n    <li><a href=\"https://codemirror.net/5/demo/tern.html\">tern addon</a>: Fix bad request made when editing at the bottom of a large document.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Improve parsing in a number of corner cases.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Fix crash when a sub-mode doesn't support indentation, allow uppercase X in task lists.</li>\n    <li><a href=\"https://codemirror.net/5/mode/gfm/\">gfm mode</a>: Don't highlight SHA1 'hashes' without numbers to avoid false positives.</li>\n    <li><a href=\"https://codemirror.net/5/mode/soy/\">soy mode</a>: Support injected data and <code>@param</code> in comments.</li>\n    <li><a href=\"https://codemirror.net/5/demo/simplemode.html\">simple mode addon</a>: Allow groups in regexps when <code>token</code> isn't an array.</li>\n  </ul>\n\n  <p class=\"rel\">24-08-2017: <a href=\"https://codemirror.net/5/codemirror-5.29.0.zip\">Version 5.29.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix crash in contentEditable input style when editing near a bookmark.</li>\n    <li>Make sure change origins are preserved when splitting changes on <a href=\"https://codemirror.net/5/doc/manual.html#mark_readOnly\">read-only marks</a>.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: More support for TypeScript syntax.</li>\n    <li><a href=\"https://codemirror.net/5/mode/d/\">d mode</a>: Support nested comments.</li>\n    <li><a href=\"https://codemirror.net/5/mode/python/\">python mode</a>: Improve tokenizing of operators.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Further improve CommonMark conformance.</li>\n    <li><a href=\"https://codemirror.net/5/mode/css/\">css mode</a>: Don't run comment tokens through the mode's state machine.</li>\n    <li><a href=\"https://codemirror.net/5/mode/shell/\">shell mode</a>: Allow strings to span lines.</li>\n    <li><a href=\"https://codemirror.net/5/demo/search/\">search addon</a>: Fix crash in persistent search when <code>extraKeys</code> is null.</li>\n  </ul>\n\n  <p class=\"rel\">21-07-2017: <a href=\"https://codemirror.net/5/codemirror-5.28.0.zip\">Version 5.28.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix copying of, or replacing editor content with, a single dash character when copying a big selection in some corner cases.</li>\n    <li>Make <a href=\"https://codemirror.net/5/doc/manual.html#command_goLineLeft\"><code>&quot;goLineLeft&quot;</code></a>/<code>&quot;goLineRight&quot;</code> behave better on wrapped lines.</li>\n    <li><a href=\"https://codemirror.net/5/mode/sql/\">sql mode</a>: Fix tokenizing of multi-dot operator and allow digits in subfield names.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_searchcursor\">searchcursor addon</a>: Fix infinite loop on some composed character inputs.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Make list parsing more CommonMark-compliant.</li>\n    <li><a href=\"https://codemirror.net/5/mode/gfm/\">gfm mode</a>: Highlight colon syntax for emoji.</li>\n  </ul>\n\n  <p class=\"rel\">29-06-2017: <a href=\"https://codemirror.net/5/codemirror-5.27.4.zip\">Version 5.27.4</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix crash when using mode lookahead.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Don't block inner mode's indentation support.</li>\n  </ul>\n\n  <p class=\"rel\">22-06-2017: <a href=\"https://codemirror.net/5/codemirror-5.27.2.zip\">Version 5.27.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix crash in the <a href=\"https://codemirror.net/5/demo/simplemode.html\">simple mode</a> addon.</li>\n  </ul>\n\n  <p class=\"rel\">22-06-2017: <a href=\"https://codemirror.net/5/codemirror-5.27.0.zip\">Version 5.27.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix infinite loop in forced display update.</li>\n    <li>Properly disable the hidden textarea when <code>readOnly</code> is <code>&quot;nocursor&quot;</code>.</li>\n    <li>Calling the <code>Doc</code> constructor without <code>new</code> works again.</li>\n    <li><a href=\"https://codemirror.net/5/mode/sql/\">sql mode</a>: Handle nested comments.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Improve support for TypeScript syntax.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Fix bug where markup was ignored on indented paragraph lines.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Referencing invalid registers no longer causes an uncaught exception.</li>\n    <li><a href=\"https://codemirror.net/5/mode/rust/\">rust mode</a>: Add the correct MIME type.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_matchbrackets\">matchbrackets addon</a>: Document options.</li>\n    <li>Mouse button clicks can now be bound in keymaps by using names like <code>&quot;LeftClick&quot;</code> or <code>&quot;Ctrl-Alt-MiddleTripleClick&quot;</code>. When bound to a function, that function will be passed the position of the click as second argument.</li>\n    <li>The behavior of mouse selection and dragging can now be customized with the <a href=\"https://codemirror.net/5/doc/manual.html#option_configureMouse\"><code>configureMouse</code></a> option.</li>\n    <li>Modes can now look ahead across line boundaries with the <a href=\"https://codemirror.net/5/doc/manual.html#StringStream\"><code>StringStream</code></a><code>.lookahead</code> method.</li>\n    <li>Introduces a <code>&quot;type&quot;</code> token type, makes modes that recognize types output it, and add styling for it to the themes.</li>\n    <li>New <a href=\"https://codemirror.net/5/doc/manual.html#option_pasteLinesPerSelection\"><code>pasteLinesPerSelection</code></a> option to control the behavior of pasting multiple lines into multiple selections.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_searchcursor\">searchcursor addon</a>: Support multi-line regular expression matches, and normalize strings when matching.</li>\n  </ul>\n\n  <p class=\"rel\">22-05-2017: <a href=\"https://codemirror.net/5/codemirror-5.26.0.zip\">Version 5.26.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>In textarea-mode, don't reset the input field during composition.</li>\n    <li>More careful restoration of selections in widgets, during editor redraw.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Parse line offsets in line or range specs.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: More TypeScript parsing fixes.</li>\n    <li><a href=\"https://codemirror.net/5/mode/julia/\">julia mode</a>: Fix issue where the mode gets stuck.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Understand cross-line links, parse all bracketed things as links.</li>\n    <li><a href=\"https://codemirror.net/5/mode/soy/\">soy mode</a>: Support single-quoted strings.</li>\n    <li><a href=\"https://codemirror.net/5/mode/go/\">go mode</a>: Don't try to indent inside strings or comments.</li>\n  </ul>\n\n  <p class=\"rel\">20-04-2017: <a href=\"https://codemirror.net/5/codemirror-5.25.2.zip\">Version 5.25.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Better handling of selections that cover the whole viewport in contentEditable-mode.</li>\n    <li>No longer accidentally scroll the editor into view when calling <code>setValue</code>.</li>\n    <li>Work around Chrome Android bug when converting screen coordinates to editor positions.</li>\n    <li>Make sure long-clicking a selection sets a cursor and doesn't show the editor losing focus.</li>\n    <li>Fix issue where pointer events were incorrectly disabled on Chrome's overlay scrollbars.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Recognize annotations and TypeScript-style type parameters.</li>\n    <li><a href=\"https://codemirror.net/5/mode/shell/\">shell mode</a>: Handle nested braces.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Make parsing of strong/em delimiters CommonMark-compliant.</li>\n  </ul>\n\n  <p class=\"rel\">20-03-2017: <a href=\"https://codemirror.net/5/codemirror-5.25.0.zip\">Version 5.25.0</a>:</p>\n\n  <ul class=rel-note>\n    <li>In contentEditable-mode, properly locate changes that repeat a character when inserted with IME.</li>\n    <li>Fix handling of selections bigger than the viewport in contentEditable mode.</li>\n    <li>Improve handling of changes that insert or delete lines in contentEditable mode.</li>\n    <li>Count Unicode control characters 0x80 to 0x9F as special (non-printing) chars.</li>\n    <li>Fix handling of shadow DOM roots when finding the active element.</li>\n    <li>Add <code>role=presentation</code> to more DOM elements to improve screen reader support.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_merge\">merge addon</a>: Make aligning of unchanged chunks more robust.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_comment\">comment addon</a>: Fix comment-toggling on a block of text that starts and ends in a (different) block comment.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Improve support for TypeScript syntax.</li>\n    <li><a href=\"https://codemirror.net/5/mode/r/\">r mode</a>: Fix indentation after semicolon-less statements.</li>\n    <li><a href=\"https://codemirror.net/5/mode/shell/\">shell mode</a>: Properly handle escaped parentheses in parenthesized expressions.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Fix a few bugs around leaving fenced code blocks.</li>\n    <li><a href=\"https://codemirror.net/5/mode/soy/\">soy mode</a>: Improve indentation.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_lint\">lint addon</a>: Support asynchronous linters that return promises.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_continuelist\">continuelist addon</a>: Support continuing task lists.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Make Y behave like yy.</li>\n    <li><a href=\"https://codemirror.net/5/mode/sql/\">sql mode</a>: Support sqlite dialect.</li>\n  </ul>\n\n  <p class=\"rel\">22-02-2017: <a href=\"https://codemirror.net/5/codemirror-5.24.2.zip\">Version 5.24.2</a>:</p>\n\n  <ul class=rel-note>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Support computed class method names.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_merge\">merge addon</a>: Improve aligning of unchanged code in the presence of marks and line widgets.</li>\n  </ul>\n\n  <p class=\"rel\">20-02-2017: <a href=\"https://codemirror.net/5/codemirror-5.24.0.zip\">Version 5.24.0</a>:</p>\n\n  <ul class=rel-note>\n    <li>Positions now support a <code>sticky</code> property which determines whether they should be associated with the character before (value <code>\"before\"</code>) or after (value <code>\"after\"</code>) them.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Make it possible to remove built-in bindings through the API.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_comment\">comment addon</a>: Support a per-mode <code>useInnerComments</code> option to optionally suppress descending to the inner modes to get comment strings.</li>\n    <li>A cursor directly before a line-wrapping break is now drawn before or after the line break depending on which direction you arrived from.</li>\n    <li>Visual cursor motion in line-wrapped right-to-left text should be much more correct.</li>\n    <li>Fix bug in handling of read-only marked text.</li>\n    <li><a href=\"https://codemirror.net/5/mode/shell/\">shell mode</a>: Properly tokenize nested parentheses.</li>\n    <li><a href=\"https://codemirror.net/5/mode/python/\">python mode</a>: Support underscores in number literals.</li>\n    <li><a href=\"https://codemirror.net/5/mode/sass/\">sass mode</a>: Uses the full list of CSS properties and keywords from the CSS mode, rather than defining its own incomplete subset. Now depends on the css mode.</li>\n    <li><a href=\"https://codemirror.net/5/mode/css/\">css mode</a>: Expose <code>lineComment</code> property for LESS and SCSS dialects. Recognize vendor prefixes on pseudo-elements.</li>\n    <li><a href=\"https://codemirror.net/5/mode/julia/\">julia mode</a>: Properly indent <code>elseif</code> lines.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Properly recognize the end of fenced code blocks when inside other markup.</li>\n    <li><a href=\"https://codemirror.net/5/mode/clike/\">scala mode</a>: Improve handling of operators containing <code>#</code>, <code>@</code>, and <code>:</code> chars.</li>\n    <li><a href=\"https://codemirror.net/5/mode/xml/\">xml mode</a>: Allow dashes in HTML tag names.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Improve parsing of async methods, TypeScript-style comma-separated superclass lists.</li>\n    <li><a href=\"https://codemirror.net/5/demo/folding.html\">indent-fold addon</a>: Ignore comment lines.</li>\n  </ul>\n\n  <p class=\"rel\">19-01-2017: <a href=\"https://codemirror.net/5/codemirror-5.23.0.zip\">Version 5.23.0</a>:</p>\n\n  <ul class=rel-note>\n    <li>Presentation-related elements DOM elements are now marked as such to help screen readers.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Be more picky about what HTML tags look like to avoid false positives.</li>\n    <li><code>findModeByMIME</code> now understands <code>+json</code> and <code>+xml</code> MIME suffixes.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_closebrackets\">closebrackets addon</a>: Add support for an <code>override</code> option to ignore language-specific defaults.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_panel\">panel addon</a>: Add a <code>stable</code> option that auto-scrolls the content to keep it in the same place when inserting/removing a panel.</li>\n  </ul>\n\n  <p class=\"rel\">20-12-2016: <a href=\"https://codemirror.net/5/codemirror-5.22.0.zip\">Version 5.22.0</a>:</p>\n\n  <ul class=rel-note>\n    <li><a href=\"https://codemirror.net/5/demo/sublime.html\">sublime bindings</a>: Make <code>selectBetweenBrackets</code> work with multiple cursors.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Fix issues with parsing complex TypeScript types, imports, and exports.</li>\n    <li>A contentEditable editor instance with autofocus enabled no longer crashes during initializing.</li>\n    <li><a href=\"https://codemirror.net/5/demo/emacs.html\">emacs bindings</a>: Export <code>CodeMirror.emacs</code> to allow other addons to hook into Emacs-style functionality.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_active-line\">active-line addon</a>: Add <code>nonEmpty</code> option.</li>\n    <li>New event: <a href=\"https://codemirror.net/5/doc/manual.html#event_optionChange\"><code>optionChange</code></a>.</li>\n  </ul>\n\n  <p class=\"rel\">21-11-2016: <a href=\"https://codemirror.net/5/codemirror-5.21.0.zip\">Version 5.21.0</a>:</p>\n\n  <ul class=rel-note>\n    <li>Tapping/clicking the editor in <a href=\"https://codemirror.net/5/doc/manual.html#option_inputStyle\">contentEditable mode</a> on Chrome now puts the cursor at the tapped position.</li>\n    <li>Fix various crashes and misbehavior when reading composition events in <a href=\"https://codemirror.net/5/doc/manual.html#option_inputStyle\">contentEditable mode</a>.</li>\n    <li>Catches and ignores an IE 'Unspecified Error' when creating an editor in an iframe before there is a <code>&lt;body&gt;</code>.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_merge\">merge addon</a>: Fix several issues in the chunk-aligning feature.</li>\n    <li><a href=\"https://codemirror.net/5/mode/verilog\">verilog mode</a>: Rewritten to address various issues.</li>\n    <li><a href=\"https://codemirror.net/5/mode/julia\">julia mode</a>: Recognize Julia 0.5 syntax.</li>\n    <li><a href=\"https://codemirror.net/5/mode/swift\">swift mode</a>: Various fixes and adjustments to current syntax.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown\">markdown mode</a>: Allow lists without a blank line above them.</li>\n    <li>The <a href=\"https://codemirror.net/5/doc/manual.html#setGutterMarker\"><code>setGutterMarker</code></a>, <a href=\"https://codemirror.net/doc/manual.html#clearGutter\"><code>clearGutter</code></a>, and <a href=\"https://codemirror.net/doc/manual.html#lineInfo\"><code>lineInfo</code></a> methods are now available on <code>Doc</code> objects.</li>\n    <li>The <a href=\"https://codemirror.net/5/doc/manual.html#heightAtLine\"><code>heightAtLine</code></a> method now takes an extra argument to allow finding the height at the top of the line's line widgets.</li>\n    <li><a href=\"https://codemirror.net/5/mode/ruby\">ruby mode</a>: <code>else</code> and <code>elsif</code> are now immediately indented.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Bind Ctrl-T and Ctrl-D to in- and dedent in insert mode.</li>\n  </ul>\n\n  <p class=\"rel\">20-10-2016: <a href=\"https://codemirror.net/5/codemirror-5.20.0.zip\">Version 5.20.0</a>:</p>\n\n  <ul class=rel-note>\n    <li>Make <code>newlineAndIndent</code> command work with multiple cursors on the same line.</li>\n    <li>Make sure keypress events for backspace are ignored.</li>\n    <li>Tokens styled with overlays no longer get a nonsense <code>cm-cm-overlay</code> class.</li>\n    <li>Line endings for pasted content are now normalized to the editor's <a href=\"https://codemirror.net/5/doc/manual.html#option_lineSeparator\">preferred ending</a>.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript\">javascript mode</a>: Improve support for class expressions. Support TypeScript optional class properties, the <code>abstract</code> keyword, and return type declarations for arrow functions.</li>\n    <li><a href=\"https://codemirror.net/5/mode/css\">css mode</a>: Fix highlighting of mixed-case keywords.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_closebrackets\">closebrackets addon</a>: Improve behavior when typing a quote before a string.</li>\n    <li>The core is now maintained as a number of small files, using ES6 syntax and modules, under the <code>src/</code> directory. A git checkout no longer contains a working <code>codemirror.js</code> until you <code>npm run build</code> (but when installing from NPM, it is included).</li>\n    <li>The <a href=\"https://codemirror.net/5/doc/manual.html#event_refresh\"><code>refresh</code></a> event is now documented and stable.</li>\n  </ul>\n\n  <p class=\"rel\">20-09-2016: <a href=\"https://codemirror.net/5/codemirror-5.19.0.zip\">Version 5.19.0</a>:</p>\n\n  <ul class=rel-note>\n    <li><a href=\"https://codemirror.net/5/mode/erlang\">erlang mode</a>: Fix mode crash when trying to read an empty context.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_comment\">comment addon</a>: Fix broken behavior when toggling comments inside a comment.</li>\n    <li>xml-fold addon: Fix a null-dereference bug.</li>\n    <li>Page up and page down now do something even in single-line documents.</li>\n    <li>Fix an issue where the cursor position could be off in really long (~8000 character) tokens.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript\">javascript mode</a>: Better indentation when semicolons are missing. Better support for TypeScript classes, optional parameters, and the <code>type</code> keyword.</li>\n    <li>The <a href=\"https://codemirror.net/5/doc/manual.html#event_blur\"><code>blur</code></a> and <a href=\"https://codemirror.net/doc/manual.html#event_focus\"><code>focus</code></a> events now pass the DOM event to their handlers.</li>\n  </ul>\n\n  <p class=\"rel\">23-08-2016: <a href=\"https://codemirror.net/5/codemirror-5.18.2.zip\">Version 5.18.2</a>:</p>\n\n  <ul class=rel-note>\n    <li><a href=\"https://codemirror.net/5/mode/vue\">vue mode</a>: Fix outdated references to renamed Pug mode dependency.</li>\n  </ul>\n\n  <p class=\"rel\">22-08-2016: <a href=\"https://codemirror.net/5/codemirror-5.18.0.zip\">Version 5.18.0</a>:</p>\n\n  <ul class=rel-note>\n    <li>Make sure <a href=\"https://codemirror.net/5/doc/manual.html#addLineClass\">gutter backgrounds</a> stick to the rest of the gutter during horizontal scrolling.</li>\n    <li>The contenteditable <a href=\"https://codemirror.net/5/doc/manual.html#option_inputStyle\"><code>inputStyle</code></a> now properly supports pasting on pre-Edge IE versions.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript\">javascript mode</a>: Fix some small parsing bugs and improve TypeScript support.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_matchbrackets\">matchbrackets addon</a>: Fix bug where active highlighting was left in editor when the addon was disabled.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_match-highlighter\">match-highlighter addon</a>: Only start highlighting things when the editor gains focus.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_javascript-hint\">javascript-hint addon</a>: Also complete non-enumerable properties.</li>\n    <li>The <a href=\"https://codemirror.net/5/doc/manual.html#addOverlay\"><code>addOverlay</code></a> method now supports a <code>priority</code> option to control the order in which overlays are applied.</li>\n    <li>MIME types that end in <code>+json</code> now default to the JSON mode when the MIME itself is not defined.</li>\n    <li>The mode formerly known as Jade was renamed to <a href=\"https://codemirror.net/5/mode/pug\">Pug</a>.</li>\n    <li>The <a href=\"https://codemirror.net/5/mode/python\">Python mode</a> now defaults to Python 3 (rather than 2) syntax.</li>\n  </ul>\n\n  <p class=\"rel\">19-07-2016: <a href=\"https://codemirror.net/5/codemirror-5.17.0.zip\">Version 5.17.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix problem with wrapped trailing whitespace displaying incorrectly.</li>\n    <li>Prevent IME dialog from overlapping typed content in Chrome.</li>\n    <li>Improve measuring of characters near a line wrap.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript\">javascript mode</a>: Improve support for <code>async</code>, allow trailing commas in <code>import</code> lists.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Fix backspace in replace mode.</li>\n    <li><a href=\"https://codemirror.net/5/demo/sublime.html\">sublime bindings</a>: Fix some key bindings on OS X to match Sublime Text.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown\">markdown mode</a>: Add more classes to image links in highlight-formatting mode.</li>\n  </ul>\n\n  <p class=\"rel\">20-06-2016: <a href=\"https://codemirror.net/5/codemirror-5.16.0.zip\">Version 5.16.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix glitches when dragging content caused by the drop indicator receiving mouse events.</li>\n    <li>Make Control-drag work on Firefox.</li>\n    <li>Make clicking or selection-dragging at the end of a wrapped line select the right position.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_show-hint\">show-hint addon</a>: Prevent widget scrollbar from hiding part of the hint text.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_rulers\">rulers addon</a>: Prevent rulers from forcing a horizontal editor scrollbar.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_search\">search addon</a>: Automatically bind search-related keys in persistent dialog.</li>\n    <li><a href=\"https://codemirror.net/5/demo/sublime.html\">sublime keymap</a>: Add a multi-cursor aware smart backspace binding.</li>\n  </ul>\n\n  <p class=\"rel\">20-05-2016: <a href=\"https://codemirror.net/5/codemirror-5.15.2.zip\">Version 5.15.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix a critical document corruption bug that occurs when a document is gradually grown.</li>\n  </ul>\n\n  <p class=\"rel\">20-05-2016: <a href=\"https://codemirror.net/5/codemirror-5.15.0.zip\">Version 5.15.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix bug that caused the selection to reset when focusing the editor in contentEditable input mode.</li>\n    <li>Fix issue where not all ASCII control characters were being replaced by placeholders.</li>\n    <li>Remove the assumption that all modes have a <code>startState</code> method from several wrapping modes.</li>\n    <li>Fix issue where the editor would complain about overlapping collapsed ranges when there weren't any.</li>\n    <li>Optimize document tree building when loading or pasting huge chunks of content.</li>\n    <li>Explicitly bind Ctrl-O on OS X to make that binding (“open line”) act as expected.</li>\n    <li>Pasting <a href=\"https://codemirror.net/5/doc/manual.html#option_lineWiseCopyCut\">linewise-copied</a> content when there is no selection now inserts the lines above the current line.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">markdown mode</a>: Fix several issues in matching link targets.</li>\n    <li><a href=\"https://codemirror.net/5/mode/clike/\">clike mode</a>: Improve indentation of C++ template declarations.</li>\n    <li><a href=\"https://codemirror.net/5/mode/javascript/\">javascript mode</a>: Support <code>async</code>/<code>await</code> and improve support for TypeScript type syntax.</li>\n  </ul>\n\n  <p class=\"rel\">20-04-2016: <a href=\"https://codemirror.net/5/codemirror-5.14.0.zip\">Version 5.14.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#posFromIndex\"><code>posFromIndex</code></a> and <a href=\"https://codemirror.net/doc/manual.html#indexFromPos\"><code>indexFromPos</code></a> now take <a href=\"https://codemirror.net/doc/manual.html#option_lineSeparator\"><code>lineSeparator</code></a> into account</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Only call <code>.save()</code> when it is actually available</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_comment\">comment addon</a>: Be careful not to mangle multi-line strings</li>\n    <li><a href=\"https://codemirror.net/5/mode/python/index.html\">Python mode</a>: Improve distinguishing of decorators from <code>@</code> operators</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#findMarks\"><code>findMarks</code></a>: No longer return marks that touch but don't overlap given range</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">vim bindings</a>: Add yank command</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_match-highlighter\">match-highlighter addon</a>: Add <code>trim</code> option to disable ignoring of whitespace</li>\n    <li><a href=\"https://codemirror.net/5/mode/powershell/index.html\">PowerShell mode</a>: Added</li>\n    <li><a href=\"https://codemirror.net/5/mode/yacas/index.html\">Yacas mode</a>: Added</li>\n    <li><a href=\"https://codemirror.net/5/mode/webidl/index.html\">Web IDL mode</a>: Added</li>\n    <li><a href=\"https://codemirror.net/5/mode/sas/index.html\">SAS mode</a>: Added</li>\n    <li><a href=\"https://codemirror.net/5/mode/mbox/index.html\">mbox mode</a>: Added</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.13.4...5.14.0\">list of patches</a></li>\n  </ul>\n\n  <p class=\"rel\">21-03-2016: <a href=\"https://codemirror.net/5/codemirror-5.13.2.zip\">Version 5.13.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Solves a problem where the gutter would sometimes not extend all the way to the end of the document.</li>\n  </ul>\n\n  <p class=\"rel\">21-03-2016: <a href=\"https://codemirror.net/5/codemirror-5.13.zip\">Version 5.13</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New DOM event forwarded: <a href=\"https://codemirror.net/5/doc/manual.html#event_dom\"><code>&quot;dragleave&quot;</code></a>.</li>\n    <li><a href=\"https://codemirror.net/5/mode/protobuf/index.html\">protobuf mode</a>: Newly added.</li>\n    <li>Fix problem where <a href=\"https://codemirror.net/5/doc/manual.html#findMarks\"><code>findMarks</code></a> sometimes failed to find multi-line marks.</li>\n    <li>Fix crash that showed up when atomic ranges and bidi text were combined.</li>\n    <li><a href=\"https://codemirror.net/5/demo/complete.html\">show-hint addon</a>: Completion widgets no longer close when the line indented or dedented.</li>\n    <li><a href=\"https://codemirror.net/5/demo/merge.html\">merge addon</a>: Fix bug when merging chunks at the end of the file.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_placeholder\">placeholder addon</a>: No longer gets confused by <a href=\"https://codemirror.net/doc/manual.html#swapDoc\"><code>swapDoc</code></a>.</li>\n    <li><a href=\"https://codemirror.net/5/doc/manual.html#addon_simplescrollbars\">simplescrollbars addon</a>: Fix invalid state when deleting at end of document.</li>\n    <li><a href=\"https://codemirror.net/5/mode/clike/index.html\">clike mode</a>: No longer gets confused when a comment starts after an operator.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/index.html\">markdown mode</a>: Now supports CommonMark-style flexible list indentation.</li>\n    <li><a href=\"https://codemirror.net/5/mode/dylan/index.html\">dylan mode</a>: Several improvements and fixes.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.12.0...5.13.0\">list of patches</a></li>\n  </ul>\n\n  <p class=\"rel\">19-02-2016: <a href=\"https://codemirror.net/5/codemirror-5.12.zip\">Version 5.12</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">Vim bindings</a>: Ctrl-Q is now an alias for Ctrl-V.</li>\n    <li><a href=\"https://codemirror.net/5/demo/vim.html\">Vim bindings</a>: The Vim API now exposes an <code>unmap</code> method to unmap bindings.</li>\n    <li><a href=\"https://codemirror.net/5/demo/activeline.html\">active-line addon</a>: This addon can now style the active line's gutter.</li>\n    <li><a href=\"https://codemirror.net/5/mode/fcl/\">FCL mode</a>: Newly added.</li>\n    <li><a href=\"https://codemirror.net/5/mode/sql/\">SQL mode</a>: Now has a Postgresql dialect.</li>\n    <li>Fix <a href=\"https://github.com/codemirror/CodeMirror/issues/3781\">issue</a> where trying to scroll to a horizontal position outside of the document's width could cause the gutter to be positioned incorrectly.</li>\n    <li>Use absolute, rather than fixed positioning in the context-menu intercept hack, to work around a <a href=\"https://github.com/codemirror/CodeMirror/issues/3238\">problem</a> when the editor is inside a transformed parent container.</li>\n    <li>Solve a <a href=\"https://github.com/codemirror/CodeMirror/issues/3821\">problem</a> where the horizontal scrollbar could hide text in Firefox.</li>\n    <li>Fix a <a href=\"https://github.com/codemirror/CodeMirror/issues/3834\">bug</a> that caused phantom scroll space under the text in some situations.</li>\n    <li><a href=\"https://codemirror.net/5/demo/sublime.html\">Sublime Text bindings</a>: Bind delete-line to Shift-Ctrl-K on OS X.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">Markdown mode</a>: Fix <a href=\"https://github.com/codemirror/CodeMirror/issues/3787\">issue</a> where the mode would keep state related to fenced code blocks in an unsafe way, leading to occasional corrupted parses.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">Markdown mode</a>: Ignore backslashes in code fragments.</li>\n    <li><a href=\"https://codemirror.net/5/mode/markdown/\">Markdown mode</a>: Use whichever mode is registered as <code>text/html</code> to parse HTML.</li>\n    <li><a href=\"https://codemirror.net/5/mode/clike/\">Clike mode</a>: Improve indentation of Scala <code>=&gt;</code> functions.</li>\n    <li><a href=\"https://codemirror.net/5/mode/python/\">Python mode</a>: Improve indentation of bracketed code.</li>\n    <li><a href=\"https://codemirror.net/5/mode/htmlmixed/\">HTMLMixed mode</a>: Support multi-line opening tags for sub-languages (<code>&lt;script&gt;</code>, <code>&lt;style&gt;</code>, etc).</li>\n    <li><a href=\"https://codemirror.net/5/mode/spreadsheet/\">Spreadsheet mode</a>: Fix bug where the mode did not advance the stream when finding a backslash.</li>\n    <li><a href=\"https://codemirror.net/5/mode/xml/\">XML mode</a>: The mode now takes a <code>matchClosing</code> option to configure whether mismatched closing tags should be highlighted as errors.</li>\n  </ul>\n\n  <p class=\"rel\">20-01-2016: <a href=\"https://codemirror.net/5/codemirror-5.11.zip\">Version 5.11</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New modes: <a href=\"../mode/jsx/index.html\">JSX</a>, <a href=\"../mode/haskell-literate/index.html\">literate Haskell</a></li>\n    <li>The editor now forwards more <a href=\"manual.html#event_dom\">DOM events</a>: <code>cut</code>, <code>copy</code>, <code>paste</code>, and <code>touchstart</code>. It will also forward <code>mousedown</code> for drag events</li>\n    <li>Fixes a bug where bookmarks next to collapsed spans were not rendered</li>\n    <li>The <a href=\"../mode/swift/index.html\">Swift</a> mode now supports auto-indentation</li>\n    <li>Frontmatters in the <a href=\"../mode/yaml-frontmatter/index.html\">YAML frontmatter</a> mode are now optional as intended</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.10.0...5.11.0\">list of patches</a></li>\n  </ul>\n\n  <p class=\"rel\">21-12-2015: <a href=\"https://codemirror.net/5/codemirror-5.10.zip\">Version 5.10</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Modify the way <a href=\"manual.html#mark_atomic\">atomic ranges</a> are skipped by selection to try and make it less surprising.</li>\n    <li>The <a href=\"../mode/swift/index.html\">Swift mode</a> was rewritten.</li>\n    <li>New addon: <a href=\"manual.html#addon_jump-to-line\">jump-to-line</a>.</li>\n    <li>New method: <a href=\"manual.html#isReadOnly\"><code>isReadOnly</code></a>.</li>\n    <li>The <a href=\"manual.html#addon_show-hint\">show-hint addon</a> now defaults to picking completions on single click.</li>\n    <li>The object passed to <a href=\"manual.html#event_beforeSelectionChange\"><code>&quot;beforeSelectionChange&quot;</code></a> events now has an <code>origin</code> property.</li>\n    <li>New mode: <a href=\"../mode/crystal/index.html\">Crystal</a>.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.9.0...5.10.0\">list of patches</a></li>\n  </ul>\n\n  <p class=\"rel\">23-11-2015: <a href=\"https://codemirror.net/5/codemirror-5.9.zip\">Version 5.9</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Improve the way overlay (OS X-style) scrollbars are handled</li>\n    <li>Make <a href=\"manual.html#addon_annotatescrollbar\">annotatescrollbar</a> and scrollpastend addons work properly together</li>\n    <li>Make <a href=\"manual.html#addon_show-hint\">show-hint</a> addon select options on single click by default, move selection to hovered item</li>\n    <li>Properly fold comments that include block-comment-start markers</li>\n    <li>Many small language mode fixes</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.8.0...5.9.0\">list of patches</a></li>\n  </ul>\n\n  <p class=\"rel\">20-10-2015: <a href=\"https://codemirror.net/5/codemirror-5.8.zip\">Version 5.8</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fixes an infinite loop in\n    the <a href=\"manual.html#addon_hardwrap\">hardwrap\n    addon</a></li>\n    <li>New modes: <a href=\"../mode/nsis/index.html\">NSIS</a>, <a href=\"../mode/clike/index.html\">Ceylon</a></li>\n    <li>The Kotlin mode is now a <a href=\"../mode/clike/index.html\">clike</a> dialect, rather than a stand-alone mode</li>\n    <li>New option: <a href=\"manual.html#option_allowDropFileTypes\"><code>allowDropFileTypes</code></a>. Binary files can no longer be dropped into CodeMirror</li>\n    <li>New themes: <a href=\"../demo/theme.html#bespin\">bespin</a>, <a href=\"../demo/theme.html#hopscotch\">hopscotch</a>, <a href=\"../demo/theme.html#isotope\">isotope</a>, <a href=\"../demo/theme.html#railscasts\">railscasts</a></li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.7.0...5.8.0\">list of patches</a></li>\n  </ul>\n\n  <p class=\"rel\">20-09-2015: <a href=\"https://codemirror.net/5/codemirror-5.7.zip\">Version 5.7</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New modes: <a href=\"../mode/vue/index.html\">Vue</a>, <a href=\"../mode/oz/index.html\">Oz</a>, <a href=\"../mode/mscgen/index.html\">MscGen</a> (and dialects), <a href=\"../mode/css/gss.html\">Closure Stylesheets</a></li>\n    <li>Implement <a href=\"http://commonmark.org\">CommonMark</a>-style flexible list indent and cross-line code spans in <a href=\"../mode/markdown/index.html\">Markdown</a> mode</li>\n    <li>Add a replace-all button to the <a href=\"manual.html#addon_search\">search addon</a>, and make the persistent search dialog transparent when it obscures the match</li>\n    <li>Handle <code>async</code>/<code>await</code> and ocal and binary numbers in <a href=\"../mode/javascript/index.html\">JavaScript mode</a></li>\n    <li>Fix various issues with the <a href=\"../mode/haxe/index.html\">Haxe mode</a></li>\n    <li>Make the <a href=\"manual.html#addon_closebrackets\">closebrackets addon</a> select only the wrapped text when wrapping selection in brackets</li>\n    <li>Tokenize properties as properties in the <a href=\"../mode/coffeescript/index.html\">CoffeeScript mode</a></li>\n    <li>The <a href=\"manual.html#addon_placeholder\">placeholder addon</a> now accepts a DOM node as well as a string placeholder</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.6.0...5.7.0\">list of patches</a></li>\n  </ul>\n\n  <p class=\"rel\">20-08-2015: <a href=\"https://codemirror.net/5/codemirror-5.6.zip\">Version 5.6</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix bug where you could paste into a <code>readOnly</code> editor</li>\n    <li>Show a cursor at the drop location when dragging over the editor</li>\n    <li>The <a href=\"../mode/rust/index.html\">Rust mode</a> was rewritten to handle modern Rust</li>\n    <li>The editor and theme CSS was cleaned up. Some selectors are now less specific than before</li>\n    <li>New theme: <a href=\"../demo/theme.html#abcdef\">abcdef</a></li>\n    <li>Lines longer than <a href=\"manual.html#option_maxHighlightLength\"><code>maxHighlightLength</code></a> are now less likely to mess up indentation</li>\n    <li>New addons: <a href=\"manual.html#addon_autorefresh\"><code>autorefresh</code></a> for refreshing an editor the first time it becomes visible, and <code>html-lint</code> for using <a href=\"http://htmlhint.com/\">HTMLHint</a></li>\n    <li>The <a href=\"manual.html#addon_search\"><code>search</code></a> addon now recognizes <code>\\r</code> and <code>\\n</code> in pattern and replacement input</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.5.0...5.6.0\">list of patches</a></li>\n  </ul>\n\n  <p class=\"rel\">20-07-2015: <a href=\"https://codemirror.net/5/codemirror-5.5.zip\">Version 5.5</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New option: <a href=\"manual.html#option_lineSeparator\"><code>lineSeparator</code></a> (with corresponding <a href=\"manual.html#lineSeparator\">method</a>)\n    <li>New themes: <a href=\"../demo/theme.html#dracula\">dracula</a>, <a href=\"../demo/theme.html#seti\">seti</a>, <a href=\"../demo/theme.html#yeti\">yeti</a>, <a href=\"../demo/theme.html#material\">material</a>, and <a href=\"../demo/theme.html#icecoder\">icecoder</a></li>\n    <li>New modes: <a href=\"../mode/brainfuck/index.html\">Brainfuck</a>, <a href=\"../mode/vhdl/index.html\">VHDL</a>, Squirrel (<a href=\"../mode/clike/index.html\">clike</a> dialect)</li>\n    <li>Define a <code>findPersistent</code> command in\n    the <a href=\"../demo/search.html\">search</a> addon, for a dialog\n    that stays open as you cycle through matches</li>\n    <li>From this release on, the NPM module doesn't include documentation and demos</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.4.0...5.5.0\">list of patches</a></li>\n  </ul>\n\n  <p class=\"rel\">25-06-2015: <a href=\"https://codemirror.net/5/codemirror-5.4.zip\">Version 5.4</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New modes: <a href=\"../mode/twig/index.html\">Twig</a>, <a href=\"../mode/elm/index.html\">Elm</a>, <a href=\"../mode/factor/index.html\">Factor</a>, <a href=\"../mode/swift/index.html\">Swift</a></li>\n    <li>Prefer clipboard API (if available) when pasting</li>\n    <li>Refined definition highlighting in <a href=\"../mode/clike/index.html\">clike</a> mode</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.3.0...5.4.0\">list of patches</a></li>\n  </ul>\n\n  <p class=\"rel\">20-05-2015: <a href=\"https://codemirror.net/5/codemirror-5.3.zip\">Version 5.3</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix several regressions in the <a href=\"manual.html#addon_show-hint\"><code>show-hint</code></a> addon (<code>completeSingle</code> option, <code>\"shown\"</code> and <code>\"close\"</code> events)</li>\n    <li>The <a href=\"../demo/vim.html\">vim mode</a> API was <a href=\"manual.html#vimapi\">documented</a></li>\n    <li>New modes: <a href=\"../mode/asn.1/index.html\">ASN.1</a>, <a href=\"../mode/ttcn/index.html\">TTCN</a>, and <a href=\"../mode/ttcn-cfg/index.html\">TTCN-CFG</a></li>\n    <li>The <a href=\"../mode/clike/index.html\">clike</a> mode can now deep-indent <code>switch</code> statements, and roughly recognizes types and defined identifiers</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.2.0...5.3.0\">list of patches</a></li>\n  </ul>\n\n  <p class=\"rel\">20-04-2015: <a href=\"https://codemirror.net/5/codemirror-5.2.zip\">Version 5.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix several race conditions\n    in <a href=\"manual.html#addon_show-hint\"><code>show-hint</code></a>'s\n    asynchronous mode</li>\n    <li>Fix backspace binding in <a href=\"../demo/sublime.html\">Sublime bindings</a></li>\n    <li>Change the way IME is handled in the <code>\"textarea\"</code> <a href=\"manual.html#option_inputStyle\">input style</a></li>\n\n    <li>New modes: <a href=\"../mode/mumps/index.html\">MUMPS</a>, <a href=\"../mode/handlebars/index.html\">Handlebars</a></li>\n    <li>Rewritten modes: <a href=\"../mode/django/index.html\">Django</a>, <a href=\"../mode/z80/index.html\">Z80</a></li>\n    <li>New theme: <a href=\"../demo/theme.html#liquibyte\">Liquibyte</a></li>\n    <li>New option: <a href=\"manual.html#option_lineWiseCopyCut\"><code>lineWiseCopyCut</code></a></li>\n    <li>The <a href=\"../demo/vim.html\">Vim mode</a> now supports buffer-local options and the <code>filetype</code> setting</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.1.0...5.2.0\">list of patches</a></li>\n  </ul>\n\n  <p class=\"rel\">23-03-2015: <a href=\"https://codemirror.net/5/codemirror-5.1.zip\">Version 5.1</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New modes: <a href=\"../mode/asciiarmor/index.html\">ASCII armor</a> (PGP data), <a href=\"../mode/troff/index.html\">Troff</a>, and <a href=\"../mode/cmake/index.html\">CMake</a>.</li>\n    <li>Remove SmartyMixed mode, rewrite <a href=\"../mode/smarty/index.html\">Smarty</a> mode to supersede it.</li>\n    <li>New commands in the <a href=\"manual.html#addon_merge\">merge\n    addon</a>: <code>goNextDiff</code> and <code>goPrevDiff</code>.</li>\n    <li>The <a href=\"manual.html#addon_closebrackets\">closebrackets addon</a> can now be configured per mode.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/5.0.0...5.1.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">20-02-2015: <a href=\"https://codemirror.net/5/codemirror-5.0.zip\">Version 5.0</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Experimental mobile support (tested on iOS, Android Chrome, stock Android browser)</li>\n    <li>New option <a href=\"manual.html#option_inputStyle\"><code>inputStyle</code></a> to switch between hidden textarea and contenteditable input.</li>\n    <li>The <a href=\"manual.html#getInputField\"><code>getInputField</code></a>\n    method is no longer guaranteed to return a textarea.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/4.13.0...5.0.0\">list of patches</a>.</li>\n  </ul>\n\n</section>\n\n<section id=v4 class=first>\n\n  <h2>Version 4.x</h2>\n\n  <p class=\"rel\">20-02-2015: <a href=\"https://codemirror.net/5/codemirror-4.13.zip\">Version 4.13</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix the way the <a href=\"../demo/closetag.html\"><code>closetag</code></a> demo handles the slash character.</li>\n    <li>New modes: <a href=\"../mode/forth/index.html\">Forth</a>, <a href=\"../mode/stylus/index.html\">Stylus</a>.</li>\n    <li>Make the <a href=\"../mode/css/index.html\">CSS mode</a> understand some modern CSS extensions.</li>\n    <li>Have the <a href=\"../mode/clike/index.html\">Scala mode</a> handle symbols and triple-quoted strings.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/4.12.0...4.13.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">22-01-2015: <a href=\"https://codemirror.net/5/codemirror-4.12.zip\">Version 4.12</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>The <a href=\"manual.html#addon_closetag\"><code>closetag</code></a>\n    addon now defines a <code>\"closeTag\"</code> command.</li>\n    <li>Adds a <code>findModeByFileName</code> to the <a href=\"manual.html#addon_meta\">mode metadata</a>\n    addon.</li>\n    <li><a href=\"../demo/simplemode.html\">Simple mode</a> rules can\n    now contain a <code>sol</code> property to only match at the start\n    of a line.</li>\n    <li>New\n    addon: <a href=\"manual.html#addon_selection-pointer\"><code>selection-pointer</code></a>\n    to style the mouse cursor over the selection.</li>\n    <li>Improvements to the <a href=\"../mode/sass/index.html\">Sass mode</a>'s indentation.</li>\n    <li>The <a href=\"../demo/vim.html\">Vim keymap</a>'s search functionality now\n    supports <a href=\"manual.html#addon_matchesonscrollbar\">scrollbar\n    annotation</a>.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/4.11.0...4.12.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">9-01-2015: <a href=\"https://codemirror.net/5/codemirror-4.11.zip\">Version 4.11</a>:</p>\n\n  <p class=\"rel-note\">Unfortunately, 4.10 did not take care of the\n  Firefox scrolling issue entirely. This release adds two more patches\n  to address that.</p>\n\n  <p class=\"rel\">29-12-2014: <a href=\"https://codemirror.net/5/codemirror-4.10.zip\">Version 4.10</a>:</p>\n\n  <p class=\"rel-note\">Emergency single-patch update to 4.9. Fixes\n  Firefox-specific problem where the cursor could end up behind the\n  horizontal scrollbar.</p>\n\n  <p class=\"rel\">23-12-2014: <a href=\"https://codemirror.net/5/codemirror-4.9.zip\">Version 4.9</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Overhauled scroll bar handling.\n    Add pluggable <a href=\"../demo/simplescrollbars.html\">scrollbar\n    implementations</a>.</li>\n    <li>Tweaked behavior for\n    the <a href=\"manual.html#addon_show-hint\">completion addons</a> to\n    not take text after cursor into account.</li>\n    <li>Two new optional features in\n    the <a href=\"manual.html#addon_merge\">merge addon</a>: aligning\n    editors, and folding unchanged text.</li>\n    <li>New\n    modes: <a href=\"../mode/dart/index.html\">Dart</a>, <a href=\"../mode/ebnf/index.html\">EBNF</a>, <a href=\"../mode/spreadsheet/index.html\">spreadsheet</a>,\n    and <a href=\"../mode/soy/index.html\">Soy</a>.</li>\n    <li>New <a href=\"../demo/panel.html\">addon</a> to show persistent panels below/above an editor.</li>\n    <li>New themes: <a href=\"../demo/theme.html#zenburn\">zenburn</a>\n    and <a href=\"../demo/theme.html#tomorrow-night-bright\">tomorrow night\n    bright</a>.</li>\n    <li>Allow ctrl-click to clear existing cursors.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/4.8.0...4.9.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">22-11-2014: <a href=\"https://codemirror.net/5/codemirror-4.8.zip\">Version 4.8</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Built-in support for <a href=\"manual.html#normalizeKeyMap\">multi-stroke key bindings</a>.</li>\n    <li>New method: <a href=\"manual.html#getLineTokens\"><code>getLineTokens</code></a>.</li>\n    <li>New modes: <a href=\"../mode/dockerfile/index.html\">dockerfile</a>, <a href=\"../mode/idl/index.html\">IDL</a>, <a href=\"../mode/clike/index.html\">Objective C</a> (crude).</li>\n    <li>Support styling of gutter backgrounds, allow <code>\"gutter\"</code> styles in <a href=\"manual.html#addLineClass\"><code>addLineClass</code></a>.</li>\n    <li>Many improvements to the <a href=\"../demo/vim.html\">Vim mode</a>, rewritten visual mode.</li>\n    <li>Improvements to modes: <a href=\"../mode/gfm/index.html\">gfm</a> (strikethrough), <a href=\"../mode/sparql/index.html\">SPARQL</a> (version 1.1 support), and <a href=\"../mode/stex/index.html\">sTeX</a> (no more runaway math mode).\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/4.7.0...4.8.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">20-10-2014: <a href=\"https://codemirror.net/5/codemirror-4.7.zip\">Version 4.7</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><strong>Incompatible</strong>:\n    The <a href=\"../demo/lint.html\">lint addon</a> now passes the\n    editor's value as first argument to asynchronous lint functions,\n    for consistency. The editor is still passed, as fourth\n    argument.</li>\n    <li>Improved handling of unicode identifiers in modes for\n    languages that support them.</li>\n    <li>More mode\n    improvements: <a href=\"../mode/coffeescript/index.html\">CoffeeScript</a>\n    (indentation), <a href=\"../mode/verilog/index.html\">Verilog</a>\n    (indentation), <a href=\"../mode/clike/index.html\">Scala</a>\n    (indentation, triple-quoted strings),\n    and <a href=\"../mode/php/index.html\">PHP</a> (interpolated\n    variables in heredoc strings).</li>\n    <li>New modes: <a href=\"../mode/textile/index.html\">Textile</a> and <a href=\"../mode/tornado/index.html\">Tornado templates</a>.</li>\n    <li>Experimental new <a href=\"../demo/simplemode.html\">way to define modes</a>.</li>\n    <li>Improvements to the <a href=\"../demo/vim.html\">Vim\n    bindings</a>: Arbitrary insert mode key mappings are now possible,\n    and text objects are supported in visual mode.</li>\n    <li>The mode <a href=\"../mode/meta.js\">meta-information file</a>\n    now includes information about file extensions,\n    and <a href=\"manual.html#addon_meta\">helper\n    functions</a> <code>findModeByMIME</code>\n    and <code>findModeByExtension</code>.</li>\n    <li>New logo!</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/4.6.0...4.7.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">19-09-2014: <a href=\"https://codemirror.net/5/codemirror-4.6.zip\">Version 4.6</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New mode: <a href=\"../mode/modelica/index.html\">Modelica</a></li>\n    <li>New method: <a href=\"manual.html#findWordAt\"><code>findWordAt</code></a></li>\n    <li>Make it easier to <a href=\"../demo/markselection.html\">use text background styling</a></li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/4.5.0...4.6.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">21-08-2014: <a href=\"https://codemirror.net/5/codemirror-4.5.zip\">Version 4.5</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix several serious bugs with horizontal scrolling</li>\n    <li>New mode: <a href=\"../mode/slim/index.html\">Slim</a></li>\n    <li>New command: <a href=\"manual.html#command_goLineLeftSmart\"><code>goLineLeftSmart</code></a></li>\n    <li>More fixes and extensions for the <a href=\"../demo/vim.html\">Vim</a> visual block mode</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/4.4.0...4.5.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">21-07-2014: <a href=\"https://codemirror.net/5/codemirror-4.4.zip\">Version 4.4</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><strong>Note:</strong> Some events might now fire in slightly\n    different order (<code>\"change\"</code> is still guaranteed to fire\n    before <code>\"cursorActivity\"</code>)</li>\n    <li>Nested operations in multiple editors are now synced (complete\n    at same time, reducing DOM reflows)</li>\n    <li>Visual block mode for <a href=\"../demo/vim.html\">vim</a> (&lt;C-v>) is nearly complete</li>\n    <li>New mode: <a href=\"../mode/kotlin/index.html\">Kotlin</a></li>\n    <li>Better multi-selection paste for text copied from multiple CodeMirror selections</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/4.3.0...4.4.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">23-06-2014: <a href=\"https://codemirror.net/5/codemirror-4.3.zip\">Version 4.3</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Several <a href=\"../demo/vim.html\">vim bindings</a>\n    improvements: search and exCommand history, global flag\n    for <code>:substitute</code>, <code>:global</code> command.\n    <li>Allow hiding the cursor by\n    setting <a href=\"manual.html#option_cursorBlinkRate\"><code>cursorBlinkRate</code></a>\n    to a negative value.</li>\n    <li>Make gutter markers themeable, use this in foldgutter.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/4.2.0...4.3.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">19-05-2014: <a href=\"https://codemirror.net/5/codemirror-4.2.zip\">Version 4.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix problem where some modes were broken by the fact that empty tokens were forbidden.</li>\n    <li>Several fixes to context menu handling.</li>\n    <li>On undo, scroll <em>change</em>, not cursor, into view.</li>\n    <li>Rewritten <a href=\"../mode/jade/index.html\">Jade</a> mode.</li>\n    <li>Various improvements to <a href=\"../mode/shell/index.html\">Shell</a> (support for more syntax) and <a href=\"../mode/python/index.html\">Python</a> (better indentation) modes.</li>\n    <li>New mode: <a href=\"../mode/cypher/index.html\">Cypher</a>.</li>\n    <li>New theme: <a href=\"../demo/theme.html#neo\">Neo</a>.</li>\n    <li>Support direct styling options (color, line style, width) in the <a href=\"manual.html#addon_rulers\">rulers</a> addon.</li>\n    <li>Recognize per-editor configuration for the <a href=\"manual.html#addon_show-hint\">show-hint</a> and <a href=\"manual.html#addon_foldcode\">foldcode</a> addons.</li>\n    <li>More intelligent scanning for existing close tags in <a href=\"manual.html#addon_closetag\">closetag</a> addon.</li>\n    <li>In the <a href=\"../demo/vim.html\">Vim bindings</a>: Fix bracket matching, support case conversion in visual mode, visual paste, append action.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/4.1.0...4.2.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">22-04-2014: <a href=\"https://codemirror.net/5/codemirror-4.1.zip\">Version 4.1</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><em>Slightly incompatible</em>:\n    The <a href=\"manual.html#event_cursorActivity\"><code>\"cursorActivity\"</code></a>\n    event now fires after all other events for the operation (and only\n    for handlers that were actually registered at the time the\n    activity happened).</li>\n    <li>New command: <a href=\"manual.html#command_insertSoftTab\"><code>insertSoftTab</code></a>.</li>\n    <li>New mode: <a href=\"../mode/django/index.html\">Django</a>.</li>\n    <li>Improved modes: <a href=\"../mode/verilog/index.html\">Verilog</a> (rewritten), <a href=\"../mode/jinja2/index.html\">Jinja2</a>, <a href=\"../mode/haxe/index.html\">Haxe</a>, <a href=\"../mode/php/index.html\">PHP</a> (string interpolation highlighted), <a href=\"../mode/javascript/index.html\">JavaScript</a> (indentation of trailing else, template strings), <a href=\"../mode/livescript/index.html\">LiveScript</a> (multi-line strings).</li>\n    <li>Many small issues from the 3.x→4.x transition were found and fixed.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/4.0.3...4.1.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">20-03-2014: <a href=\"https://codemirror.net/5/codemirror-4.0.zip\">Version 4.0</a>:</p>\n\n  <p class=\"rel-note\">This is a new major version of CodeMirror. There\n  are a few <strong>incompatible</strong> changes in the API. Upgrade\n  with care, and read the <a href=\"upgrade_v4.html\">upgrading\n  guide</a>.</p>\n\n  <ul class=\"rel-note\">\n    <li>Multiple selections (ctrl-click, alt-drag, <a href=\"manual.html#setSelections\">API</a>).</li>\n    <li>Sublime Text <a href=\"../demo/sublime.html\">bindings</a>.</li>\n    <li><a href=\"manual.html#modloader\">Module loader shims</a> wrapped around all modules.</li>\n    <li>Selection <a href=\"manual.html#command_undoSelection\">undo</a>/<a href=\"manual.html#command_redoSelection\">redo</a>.</li>\n    <li>Improved character measuring (faster, handles wrapped lines more robustly).</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/3.23.0...4.0.3\">list of patches</a>.</li>\n  </ul>\n\n</section>\n\n<section id=v3>\n\n  <h2>Version 3.x</h2>\n\n  <p class=\"rel\">22-04-2014: <a href=\"https://codemirror.net/5/codemirror-3.24.zip\">Version 3.24</a>:</p>\n\n  <p class=\"rel-note\">Merges the improvements from 4.1 that could\n  easily be applied to the 3.x code. Also improves the way the editor\n  size is updated when line widgets change.</p>\n\n  <p class=\"rel\">20-03-2014: <a href=\"https://codemirror.net/5/codemirror-3.23.zip\">Version 3.23</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>In the <a href=\"../mode/xml/index.html\">XML mode</a>,\n    add <code>brackets</code> style to angle brackets, fix\n    case-sensitivity of tags for HTML.</li>\n    <li>New mode: <a href=\"../mode/dylan/index.html\">Dylan</a>.</li>\n    <li>Many improvements to the <a href=\"../demo/vim.html\">Vim bindings</a>.</li>\n  </ul>\n\n  <p class=\"rel\">21-02-2014: <a href=\"https://codemirror.net/5/codemirror-3.22.zip\">Version 3.22</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Adds the <a href=\"manual.html#findMarks\"><code>findMarks</code></a> method.</li>\n    <li>New addons: <a href=\"manual.html#addon_rulers\">rulers</a>, markdown-fold, yaml-lint.</li>\n    <li>New theme: <a href=\"../demo/theme.html#mdn-like\">mdn-like</a>.</li>\n    <li>New mode: <a href=\"../mode/solr/index.html\">Solr</a>.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/3.21.0...3.22.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">16-01-2014: <a href=\"https://codemirror.net/5/codemirror-3.21.zip\">Version 3.21</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Auto-indenting a block will no longer add trailing whitespace to blank lines.</li>\n    <li>Marking text has a new option <a href=\"manual.html#markText\"><code>clearWhenEmpty</code></a> to control auto-removal.</li>\n    <li>Several bugfixes in the handling of bidirectional text.</li>\n    <li>The <a href=\"../mode/xml/index.html\">XML</a> and <a href=\"../mode/css/index.html\">CSS</a> modes were largely rewritten. <a href=\"../mode/css/less.html\">LESS</a> support was added to the CSS mode.</li>\n    <li>The OCaml mode was moved to an <a href=\"../mode/mllike/index.html\">mllike</a> mode, F# support added.</li>\n    <li>Make it possible to fetch multiple applicable helper values with <a href=\"manual.html#getHelpers\"><code>getHelpers</code></a>, and to register helpers matched on predicates with <a href=\"manual.html#registerGlobalHelper\"><code>registerGlobalHelper</code></a>.</li>\n    <li>New theme <a href=\"../demo/theme.html#pastel-on-dark\">pastel-on-dark</a>.</li>\n    <li>Better ECMAScript 6 support in <a href=\"../mode/javascript/index.html\">JavaScript</a> mode.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/3.20.0...3.21.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">21-11-2013: <a href=\"https://codemirror.net/5/codemirror-3.20.zip\">Version 3.20</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New modes: <a href=\"../mode/julia/index.html\">Julia</a> and <a href=\"../mode/pegjs/index.html\">PEG.js</a>.</li>\n    <li>Support ECMAScript 6 in the <a href=\"../mode/javascript/index.html\">JavaScript mode</a>.</li>\n    <li>Improved indentation for the <a href=\"../mode/coffeescript/index.html\">CoffeeScript mode</a>.</li>\n    <li>Make non-printable-character representation <a href=\"manual.html#option_specialChars\">configurable</a>.</li>\n    <li>Add ‘notification’ functionality to <a href=\"manual.html#addon_dialog\">dialog</a> addon.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/3.19.0...3.20.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">21-10-2013: <a href=\"https://codemirror.net/5/codemirror-3.19.zip\">Version 3.19</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New modes: <a href=\"../mode/eiffel/index.html\">Eiffel</a>, <a href=\"../mode/gherkin/index.html\">Gherkin</a>, <a href=\"../mode/sql/?mime=text/x-mssql\">MSSQL dialect</a>.</li>\n    <li>New addons: <a href=\"manual.html#addon_hardwrap\">hardwrap</a>, <a href=\"manual.html#addon_sql-hint\">sql-hint</a>.</li>\n    <li>New theme: <a href=\"../demo/theme.html#mbo\">MBO</a>.</li>\n    <li>Add <a href=\"manual.html#token_style_line\">support</a> for line-level styling from mode tokenizers.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/3.18.0...3.19.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">23-09-2013: <a href=\"https://codemirror.net/5/codemirror-3.18.zip\">Version 3.18</a>:</p>\n\n  <p class=\"rel-note\">Emergency release to fix a problem in 3.17\n  where <code>.setOption(\"lineNumbers\", false)</code> would raise an\n  error.</p>\n\n  <p class=\"rel\">23-09-2013: <a href=\"https://codemirror.net/5/codemirror-3.17.zip\">Version 3.17</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New modes: <a href=\"../mode/fortran/index.html\">Fortran</a>, <a href=\"../mode/octave/index.html\">Octave</a> (Matlab), <a href=\"../mode/toml/index.html\">TOML</a>, and <a href=\"../mode/dtd/index.html\">DTD</a>.</li>\n    <li>New addons: <a href=\"../addon/lint/css-lint.js\"><code>css-lint</code></a>, <a href=\"manual.html#addon_css-hint\"><code>css-hint</code></a>.</li>\n    <li>Improve resilience to CSS 'frameworks' that globally mess up <code>box-sizing</code>.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/3.16.0...3.17.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">21-08-2013: <a href=\"https://codemirror.net/5/codemirror-3.16.zip\">Version 3.16</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>The whole codebase is now under a single <a href=\"../LICENSE\">license</a> file.</li>\n    <li>The project page was overhauled and redesigned.</li>\n    <li>New themes: <a href=\"../demo/theme.html#paraiso-dark\">Paraiso</a> (<a href=\"../demo/theme.html#paraiso-light\">light</a>), <a href=\"../demo/theme.html#the-matrix\">The Matrix</a>.</li>\n    <li>Improved interaction between themes and <a href=\"manual.html#addon_active-line\">active-line</a>/<a href=\"manual.html#addon_matchbrackets\">matchbrackets</a> addons.</li>\n    <li>New <a href=\"manual.html#addon_foldcode\">folding</a> function <code>CodeMirror.fold.comment</code>.</li>\n    <li>Added <a href=\"manual.html#addon_fullscreen\">fullscreen</a> addon.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/3.15.0...3.16.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">29-07-2013: <a href=\"https://codemirror.net/5/codemirror-3.15.zip\">Version 3.15</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New modes: <a href=\"../mode/jade/index.html\">Jade</a>, <a href=\"../mode/nginx/index.html\">Nginx</a>.</li>\n    <li>New addons: <a href=\"../demo/tern.html\">Tern</a>, <a href=\"manual.html#addon_matchtags\">matchtags</a>, and <a href=\"manual.html#addon_foldgutter\">foldgutter</a>.</li>\n    <li>Introduced <a href=\"manual.html#getHelper\"><em>helper</em></a> concept (<a href=\"https://groups.google.com/forum/#!msg/codemirror/cOc0xvUUEUU/nLrX1-qnidgJ\">context</a>).</li>\n    <li>New method: <a href=\"manual.html#getModeAt\"><code>getModeAt</code></a>.</li>\n    <li>New themes: base16 <a href=\"../demo/theme.html#base16-dark\">dark</a>/<a href=\"../demo/theme.html#base16-light\">light</a>, 3024 <a href=\"../demo/theme.html#3024-night\">dark</a>/<a href=\"../demo/theme.html#3024-day\">light</a>, <a href=\"../demo/theme.html#tomorrow-night-eighties\">tomorrow-night</a>.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/3.14.0...3.15.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">20-06-2013: <a href=\"https://codemirror.net/5/codemirror-3.14.zip\">Version 3.14</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New\n    addons: <a href=\"manual.html#addon_trailingspace\">trailing\n    space highlight</a>, <a href=\"manual.html#addon_xml-hint\">XML\n    completion</a> (rewritten),\n    and <a href=\"manual.html#addon_merge\">diff merging</a>.</li>\n    <li><a href=\"manual.html#markText\"><code>markText</code></a>\n    and <a href=\"manual.html#addLineWidget\"><code>addLineWidget</code></a>\n    now take a <code>handleMouseEvents</code> option.</li>\n    <li>New methods: <a href=\"manual.html#lineAtHeight\"><code>lineAtHeight</code></a>,\n    <a href=\"manual.html#getTokenTypeAt\"><code>getTokenTypeAt</code></a>.</li>\n    <li>More precise cleanness-tracking\n    using <a href=\"manual.html#changeGeneration\"><code>changeGeneration</code></a>\n    and <a href=\"manual.html#isClean\"><code>isClean</code></a>.</li>\n    <li>Many extensions to <a href=\"../demo/emacs.html\">Emacs</a> mode\n    (prefixes, more navigation units, and more).</li>\n    <li>New\n    events <a href=\"manual.html#event_keyHandled\"><code>\"keyHandled\"</code></a>\n    and <a href=\"manual.html#event_inputRead\"><code>\"inputRead\"</code></a>.</li>\n    <li>Various improvements to <a href=\"../mode/ruby/index.html\">Ruby</a>,\n    <a href=\"../mode/smarty/index.html\">Smarty</a>, <a href=\"../mode/sql/index.html\">SQL</a>,\n    and <a href=\"../demo/vim.html\">Vim</a> modes.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/3.13.0...3.14.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">20-05-2013: <a href=\"https://codemirror.net/5/codemirror-3.13.zip\">Version 3.13</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New modes: <a href=\"../mode/cobol/index.html\">COBOL</a> and <a href=\"../mode/haml/index.html\">HAML</a>.</li>\n    <li>New options: <a href=\"manual.html#option_cursorScrollMargin\"><code>cursorScrollMargin</code></a> and <a href=\"manual.html#option_coverGutterNextToScrollbar\"><code>coverGutterNextToScrollbar</code></a>.</li>\n    <li>New addon: <a href=\"manual.html#addon_comment\">commenting</a>.</li>\n    <li>More features added to the <a href=\"../demo/vim.html\">Vim keymap</a>.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/v3.12...3.13.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">19-04-2013: <a href=\"https://codemirror.net/5/codemirror-3.12.zip\">Version 3.12</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New mode: <a href=\"../mode/gas/index.html\">GNU assembler</a>.</li>\n    <li>New\n    options: <a href=\"manual.html#option_maxHighlightLength\"><code>maxHighlightLength</code></a>\n    and <a href=\"manual.html#option_historyEventDelay\"><code>historyEventDelay</code></a>.</li>\n    <li>Added <a href=\"manual.html#mark_addToHistory\"><code>addToHistory</code></a>\n    option for <code>markText</code>.</li>\n    <li>Various fixes to JavaScript tokenization and indentation corner cases.</li>\n    <li>Further improvements to the vim mode.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/v3.11...v3.12\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">20-03-2013: <a href=\"https://codemirror.net/5/codemirror-3.11.zip\">Version 3.11</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><strong>Removed code:</strong> <code>collapserange</code>,\n    <code>formatting</code>, and <code>simple-hint</code>\n    addons. <code>plsql</code> and <code>mysql</code> modes\n    (use <a href=\"../mode/sql/index.html\"><code>sql</code></a> mode).</li>\n    <li><strong>Moved code:</strong> the range-finding functions for folding now have <a href=\"../addon/fold/\">their own files</a>.</li>\n    <li><strong>Changed interface:</strong>\n    the <a href=\"manual.html#addon_continuecomment\"><code>continuecomment</code></a>\n    addon now exposes an option, rather than a command.</li>\n    <li>New\n    modes: <a href=\"../mode/css/scss.html\">SCSS</a>, <a href=\"../mode/tcl/index.html\">Tcl</a>, <a href=\"../mode/livescript/index.html\">LiveScript</a>,\n    and <a href=\"../mode/mirc/index.html\">mIRC</a>.</li>\n    <li>New addons: <a href=\"../demo/placeholder.html\"><code>placeholder</code></a>, <a href=\"../demo/html5complete.html\">HTML completion</a>.</li>\n    <li>New\n    methods: <a href=\"manual.html#hasFocus\"><code>hasFocus</code></a>, <a href=\"manual.html#defaultCharWidth\"><code>defaultCharWidth</code></a>.</li>\n    <li>New events: <a href=\"manual.html#event_beforeCursorEnter\"><code>beforeCursorEnter</code></a>, <a href=\"manual.html#event_renderLine\"><code>renderLine</code></a>.</li>\n    <li>Many improvements to the <a href=\"manual.html#addon_show-hint\"><code>show-hint</code></a> completion\n    dialog addon.</li>\n    <li>Tweak behavior of by-word cursor motion.</li>\n    <li>Further improvements to the <a href=\"../demo/vim.html\">vim mode</a>.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/v3.1...v3.11\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">21-02-2013: <a href=\"https://codemirror.net/5/codemirror-3.1.zip\">Version 3.1</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><strong>Incompatible:</strong> key handlers may\n    now <em>return</em>, rather\n    than <em>throw</em> <code>CodeMirror.Pass</code> to signal they\n    didn't handle the key.</li>\n    <li>Make documents a <a href=\"manual.html#api_doc\">first-class\n    construct</a>, support split views and subviews.</li>\n    <li>Add a <a href=\"manual.html#addon_show-hint\">new module</a>\n    for showing completion hints.\n    Deprecate <code>simple-hint.js</code>.</li>\n    <li>Extend <a href=\"../mode/htmlmixed/index.html\">htmlmixed mode</a>\n    to allow custom handling of script types.</li>\n    <li>Support an <code>insertLeft</code> option\n    to <a href=\"manual.html#setBookmark\"><code>setBookmark</code></a>.</li>\n    <li>Add an <a href=\"manual.html#eachLine\"><code>eachLine</code></a>\n    method to iterate over a document.</li>\n    <li>New addon modules: <a href=\"../demo/markselection.html\">selection\n    marking</a>, <a href=\"../demo/lint.html\">linting</a>,\n    and <a href=\"../demo/closebrackets.html\">automatic bracket\n    closing</a>.</li>\n    <li>Add <a href=\"manual.html#event_beforeChange\"><code>\"beforeChange\"</code></a>\n    and <a href=\"manual.html#event_beforeSelectionChange\"><code>\"beforeSelectionChange\"</code></a>\n    events.</li>\n    <li>Add <a href=\"manual.html#event_hide\"><code>\"hide\"</code></a>\n    and <a href=\"manual.html#event_unhide\"><code>\"unhide\"</code></a>\n    events to marked ranges.</li>\n    <li>Fix <a href=\"manual.html#coordsChar\"><code>coordsChar</code></a>'s\n    interpretation of its argument to match the documentation.</li>\n    <li>New modes: <a href=\"../mode/turtle/index.html\">Turtle</a>\n    and <a href=\"../mode/q/index.html\">Q</a>.</li>\n    <li>Further improvements to the <a href=\"../demo/vim.html\">vim mode</a>.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/v3.01...v3.1\">list of patches</a>.</li>\n  </ul>\n\n\n  <p class=\"rel\">25-01-2013: <a href=\"https://codemirror.net/5/codemirror-3.02.zip\">Version 3.02</a>:</p>\n\n  <p class=\"rel-note\">Single-bugfix release. Fixes a problem that\n  prevents CodeMirror instances from being garbage-collected after\n  they become unused.</p>\n\n  <p class=\"rel\">21-01-2013: <a href=\"https://codemirror.net/5/codemirror-3.01.zip\">Version 3.01</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Move all add-ons into an organized directory structure\n    under <a href=\"../addon/\"><code>/addon</code></a>. <strong>You might have to adjust your\n    paths.</strong></li>\n    <li>New\n    modes: <a href=\"../mode/d/index.html\">D</a>, <a href=\"../mode/sass/index.html\">Sass</a>, <a href=\"../mode/apl/index.html\">APL</a>, <a href=\"../mode/sql/index.html\">SQL</a>\n    (configurable), and <a href=\"../mode/asterisk/index.html\">Asterisk</a>.</li>\n    <li>Several bugfixes in right-to-left text support.</li>\n    <li>Add <a href=\"manual.html#option_rtlMoveVisually\"><code>rtlMoveVisually</code></a> option.</li>\n    <li>Improvements to vim keymap.</li>\n    <li>Add built-in (lightweight) <a href=\"manual.html#addOverlay\">overlay mode</a> support.</li>\n    <li>Support <code>showIfHidden</code> option for <a href=\"manual.html#addLineWidget\">line widgets</a>.</li>\n    <li>Add simple <a href=\"manual.html#addon_python-hint\">Python hinter</a>.</li>\n    <li>Bring back the <a href=\"manual.html#option_fixedGutter\"><code>fixedGutter</code></a> option.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/v3.0...v3.01\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">10-12-2012: <a href=\"https://codemirror.net/5/codemirror-3.0.zip\">Version 3.0</a>:</p>\n\n  <p class=\"rel-note\"><strong>New major version</strong>. Only\n  partially backwards-compatible. See\n  the <a href=\"upgrade_v3.html\">upgrading guide</a> for more\n  information. Changes since release candidate 2:</p>\n\n  <ul class=\"rel-note\">\n    <li>Rewritten VIM mode.</li>\n    <li>Fix a few minor scrolling and sizing issues.</li>\n    <li>Work around Safari segfault when dragging.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/v3.0rc2...v3.0\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">20-11-2012: <a href=\"https://codemirror.net/5/codemirror-3.0rc2.zip\">Version 3.0, release candidate 2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New mode: <a href=\"../mode/http/index.html\">HTTP</a>.</li>\n    <li>Improved handling of selection anchor position.</li>\n    <li>Improve IE performance on longer lines.</li>\n    <li>Reduce gutter glitches during horiz. scrolling.</li>\n    <li>Add <a href=\"manual.html#addKeyMap\"><code>addKeyMap</code></a> and <a href=\"manual.html#removeKeyMap\"><code>removeKeyMap</code></a> methods.</li>\n    <li>Rewrite <code>formatting</code> and <code>closetag</code> add-ons.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/v3.0rc1...v3.0rc2\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">20-11-2012: <a href=\"https://codemirror.net/5/codemirror-3.0rc1.zip\">Version 3.0, release candidate 1</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New theme: <a href=\"../demo/theme.html#solarized%20light\">Solarized</a>.</li>\n    <li>Introduce <a href=\"manual.html#addLineClass\"><code>addLineClass</code></a>\n    and <a href=\"manual.html#removeLineClass\"><code>removeLineClass</code></a>,\n    drop <code>setLineClass</code>.</li>\n    <li>Add a <em>lot</em> of\n    new <a href=\"manual.html#markText\">options for marked text</a>\n    (read-only, atomic, collapsed, widget replacement).</li>\n    <li>Remove the old code folding interface in favour of these new ranges.</li>\n    <li>Add <a href=\"manual.html#isClean\"><code>isClean</code></a>/<a href=\"manual.html#markClean\"><code>markClean</code></a> methods.</li>\n    <li>Remove <code>compoundChange</code> method, use better undo-event-combining heuristic.</li>\n    <li>Improve scrolling performance smoothness.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/v3.0beta2...v3.0rc1\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">22-10-2012: <a href=\"https://codemirror.net/5/codemirror-3.0beta2.zip\">Version 3.0, beta 2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Fix page-based coordinate computation.</li>\n    <li>Fix firing of <a href=\"manual.html#event_gutterClick\"><code>gutterClick</code></a> event.</li>\n    <li>Add <a href=\"manual.html#option_cursorHeight\"><code>cursorHeight</code></a> option.</li>\n    <li>Fix bi-directional text regression.</li>\n    <li>Add <a href=\"manual.html#option_viewportMargin\"><code>viewportMargin</code></a> option.</li>\n    <li>Directly handle mousewheel events (again, hopefully better).</li>\n    <li>Make vertical cursor movement more robust (through widgets, big line gaps).</li>\n    <li>Add <a href=\"manual.html#option_flattenSpans\"><code>flattenSpans</code></a> option.</li>\n    <li>Many optimizations. Poor responsiveness should be fixed.</li>\n    <li>Initialization in hidden state works again.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/v3.0beta1...v3.0beta2\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">19-09-2012: <a href=\"https://codemirror.net/5/codemirror-3.0beta1.zip\">Version 3.0, beta 1</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Bi-directional text support.</li>\n    <li>More powerful gutter model.</li>\n    <li>Support for arbitrary text/widget height.</li>\n    <li>In-line widgets.</li>\n    <li>Generalized event handling.</li>\n  </ul>\n\n</section>\n\n<section id=v2>\n\n  <h2>Version 2.x</h2>\n\n  <p class=\"rel\">21-01-2013: <a href=\"https://codemirror.net/5/codemirror-2.38.zip\">Version 2.38</a>:</p>\n\n  <p class=\"rel-note\">Integrate some bugfixes, enhancements to the vim keymap, and new\n  modes\n  (<a href=\"../mode/d/index.html\">D</a>, <a href=\"../mode/sass/index.html\">Sass</a>, <a href=\"../mode/apl/index.html\">APL</a>)\n  from the v3 branch.</p>\n\n  <p class=\"rel\">20-12-2012: <a href=\"https://codemirror.net/5/codemirror-2.37.zip\">Version 2.37</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New mode: <a href=\"../mode/sql/index.html\">SQL</a> (will replace <a href=\"../mode/plsql/index.html\">plsql</a> and <a href=\"../mode/mysql/index.html\">mysql</a> modes).</li>\n    <li>Further work on the new VIM mode.</li>\n    <li>Fix Cmd/Ctrl keys on recent Operas on OS X.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/v2.36...v2.37\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">20-11-2012: <a href=\"https://codemirror.net/5/codemirror-2.36.zip\">Version 2.36</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New mode: <a href=\"../mode/z80/index.html\">Z80 assembly</a>.</li>\n    <li>New theme: <a href=\"../demo/theme.html#twilight\">Twilight</a>.</li>\n    <li>Add command-line compression helper.</li>\n    <li>Make <a href=\"manual.html#scrollIntoView\"><code>scrollIntoView</code></a> public.</li>\n    <li>Add <a href=\"manual.html#defaultTextHeight\"><code>defaultTextHeight</code></a> method.</li>\n    <li>Various extensions to the vim keymap.</li>\n    <li>Make <a href=\"../mode/php/index.html\">PHP mode</a> build on <a href=\"../mode/htmlmixed/index.html\">mixed HTML mode</a>.</li>\n    <li>Add <a href=\"manual.html#addon_continuecomment\">comment-continuing</a> add-on.</li>\n    <li>Full <a href=\"../https://github.com/codemirror/CodeMirror/compare/v2.35...v2.36\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">22-10-2012: <a href=\"https://codemirror.net/5/codemirror-2.35.zip\">Version 2.35</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New (sub) mode: <a href=\"../mode/javascript/typescript.html\">TypeScript</a>.</li>\n    <li>Don't overwrite (insert key) when pasting.</li>\n    <li>Fix several bugs in <a href=\"manual.html#markText\"><code>markText</code></a>/undo interaction.</li>\n    <li>Better indentation of JavaScript code without semicolons.</li>\n    <li>Add <a href=\"manual.html#defineInitHook\"><code>defineInitHook</code></a> function.</li>\n    <li>Full <a href=\"https://github.com/codemirror/CodeMirror/compare/v2.34...v2.35\">list of patches</a>.</li>\n  </ul>\n\n  <p class=\"rel\">19-09-2012: <a href=\"https://codemirror.net/5/codemirror-2.34.zip\">Version 2.34</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New mode: <a href=\"../mode/commonlisp/index.html\">Common Lisp</a>.</li>\n    <li>Fix right-click select-all on most browsers.</li>\n    <li>Change the way highlighting happens:<br>&nbsp; Saves memory and CPU cycles.<br>&nbsp; <code>compareStates</code> is no longer needed.<br>&nbsp; <code>onHighlightComplete</code> no longer works.</li>\n    <li>Integrate mode (Markdown, XQuery, CSS, sTex) tests in central testsuite.</li>\n    <li>Add a <a href=\"manual.html#version\"><code>CodeMirror.version</code></a> property.</li>\n    <li>More robust handling of nested modes in <a href=\"../demo/formatting.html\">formatting</a> and <a href=\"../demo/closetag.html\">closetag</a> plug-ins.</li>\n    <li>Un/redo now preserves <a href=\"manual.html#markText\">marked text</a> and bookmarks.</li>\n    <li><a href=\"https://github.com/codemirror/CodeMirror/compare/v2.33...v2.34\">Full list</a> of patches.</li>\n  </ul>\n\n  <p class=\"rel\">23-08-2012: <a href=\"https://codemirror.net/5/codemirror-2.33.zip\">Version 2.33</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New mode: <a href=\"../mode/sieve/index.html\">Sieve</a>.</li>\n    <li>New <a href=\"manual.html#getViewport\"><code>getViewPort</code></a> and <a href=\"manual.html#option_onViewportChange\"><code>onViewportChange</code></a> API.</li>\n    <li><a href=\"manual.html#option_cursorBlinkRate\">Configurable</a> cursor blink rate.</li>\n    <li>Make binding a key to <code>false</code> disabling handling (again).</li>\n    <li>Show non-printing characters as red dots.</li>\n    <li>More tweaks to the scrolling model.</li>\n    <li>Expanded testsuite. Basic linter added.</li>\n    <li>Remove most uses of <code>innerHTML</code>. Remove <code>CodeMirror.htmlEscape</code>.</li>\n    <li><a href=\"https://github.com/codemirror/CodeMirror/compare/v2.32...v2.33\">Full list</a> of patches.</li>\n  </ul>\n\n  <p class=\"rel\">23-07-2012: <a href=\"https://codemirror.net/5/codemirror-2.32.zip\">Version 2.32</a>:</p>\n\n  <p class=\"rel-note\">Emergency fix for a bug where an editor with\n  line wrapping on IE will break when there is <em>no</em>\n  scrollbar.</p>\n\n  <p class=\"rel\">20-07-2012: <a href=\"https://codemirror.net/5/codemirror-2.31.zip\">Version 2.31</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New modes: <a href=\"../mode/ocaml/index.html\">OCaml</a>, <a href=\"../mode/haxe/index.html\">Haxe</a>, and <a href=\"../mode/vb/index.html\">VB.NET</a>.</li>\n    <li>Several fixes to the new scrolling model.</li>\n    <li>Add a <a href=\"manual.html#setSize\"><code>setSize</code></a> method for programmatic resizing.</li>\n    <li>Add <a href=\"manual.html#getHistory\"><code>getHistory</code></a> and <a href=\"manual.html#setHistory\"><code>setHistory</code></a> methods.</li>\n    <li>Allow custom line separator string in <a href=\"manual.html#getValue\"><code>getValue</code></a> and <a href=\"manual.html#getRange\"><code>getRange</code></a>.</li>\n    <li>Support double- and triple-click drag, double-clicking whitespace.</li>\n    <li>And more... <a href=\"https://github.com/codemirror/CodeMirror/compare/v2.3...v2.31\">(all patches)</a></li>\n  </ul>\n\n  <p class=\"rel\">22-06-2012: <a href=\"https://codemirror.net/5/codemirror-2.3.zip\">Version 2.3</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><strong>New scrollbar implementation</strong>. Should flicker less. Changes DOM structure of the editor.</li>\n    <li>New theme: <a href=\"../demo/theme.html#vibrant-ink\">vibrant-ink</a>.</li>\n    <li>Many extensions to the VIM keymap (including text objects).</li>\n    <li>Add <a href=\"../demo/multiplex.html\">mode-multiplexing</a> utility script.</li>\n    <li>Fix bug where right-click paste works in read-only mode.</li>\n    <li>Add a <a href=\"manual.html#getScrollInfo\"><code>getScrollInfo</code></a> method.</li>\n    <li>Lots of other <a href=\"https://github.com/codemirror/CodeMirror/compare/v2.25...v2.3\">fixes</a>.</li>\n  </ul>\n\n  <p class=\"rel\">23-05-2012: <a href=\"https://codemirror.net/5/codemirror-2.25.zip\">Version 2.25</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>New mode: <a href=\"../mode/erlang/index.html\">Erlang</a>.</li>\n    <li><strong>Remove xmlpure mode</strong> (use <a href=\"../mode/xml/index.html\">xml.js</a>).</li>\n    <li>Fix line-wrapping in Opera.</li>\n    <li>Fix X Windows middle-click paste in Chrome.</li>\n    <li>Fix bug that broke pasting of huge documents.</li>\n    <li>Fix backspace and tab key repeat in Opera.</li>\n  </ul>\n\n  <p class=\"rel\">23-04-2012: <a href=\"https://codemirror.net/5/codemirror-2.24.zip\">Version 2.24</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li><strong>Drop support for Internet Explorer 6</strong>.</li>\n    <li>New\n    modes: <a href=\"../mode/shell/index.html\">Shell</a>, <a href=\"../mode/tiki/index.html\">Tiki\n    wiki</a>, <a href=\"../mode/pig/index.html\">Pig Latin</a>.</li>\n    <li>New themes: <a href=\"../demo/theme.html#ambiance\">Ambiance</a>, <a href=\"../demo/theme.html#blackboard\">Blackboard</a>.</li>\n    <li>More control over drag/drop\n    with <a href=\"manual.html#option_dragDrop\"><code>dragDrop</code></a>\n    and <a href=\"manual.html#option_onDragEvent\"><code>onDragEvent</code></a>\n    options.</li>\n    <li>Make HTML mode a bit less pedantic.</li>\n    <li>Add <a href=\"manual.html#compoundChange\"><code>compoundChange</code></a> API method.</li>\n    <li>Several fixes in undo history and line hiding.</li>\n    <li>Remove (broken) support for <code>catchall</code> in key maps,\n    add <code>nofallthrough</code> boolean field instead.</li>\n  </ul>\n\n  <p class=\"rel\">26-03-2012: <a href=\"https://codemirror.net/5/codemirror-2.23.zip\">Version 2.23</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Change <strong>default binding for tab</strong> <a href=\"javascript:void(document.getElementById('tabbinding').style.display='')\">[more]</a>\n      <div style=\"display: none\" id=tabbinding>\n        Starting in 2.23, these bindings are default:\n        <ul><li>Tab: Insert tab character</li>\n          <li>Shift-tab: Reset line indentation to default</li>\n          <li>Ctrl/Cmd-[: Reduce line indentation (old tab behaviour)</li>\n          <li>Ctrl/Cmd-]: Increase line indentation (old shift-tab behaviour)</li>\n        </ul>\n      </div>\n    </li>\n    <li>New modes: <a href=\"../mode/xquery/index.html\">XQuery</a> and <a href=\"../mode/vbscript/index.html\">VBScript</a>.</li>\n    <li>Two new themes: <a href=\"../mode/less/index.html\">lesser-dark</a> and <a href=\"../mode/xquery/index.html\">xq-dark</a>.</li>\n    <li>Differentiate between background and text styles in <a href=\"manual.html#setLineClass\"><code>setLineClass</code></a>.</li>\n    <li>Fix drag-and-drop in IE9+.</li>\n    <li>Extend <a href=\"manual.html#charCoords\"><code>charCoords</code></a>\n    and <a href=\"manual.html#cursorCoords\"><code>cursorCoords</code></a> with a <code>mode</code> argument.</li>\n    <li>Add <a href=\"manual.html#option_autofocus\"><code>autofocus</code></a> option.</li>\n    <li>Add <a href=\"manual.html#findMarksAt\"><code>findMarksAt</code></a> method.</li>\n  </ul>\n\n  <p class=\"rel\">27-02-2012: <a href=\"https://codemirror.net/5/codemirror-2.22.zip\">Version 2.22</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Allow <a href=\"manual.html#keymaps\">key handlers</a> to pass up events, allow binding characters.</li>\n    <li>Add <a href=\"manual.html#option_autoClearEmptyLines\"><code>autoClearEmptyLines</code></a> option.</li>\n    <li>Properly use tab stops when rendering tabs.</li>\n    <li>Make PHP mode more robust.</li>\n    <li>Support indentation blocks in <a href=\"manual.html#addon_foldcode\">code folder</a>.</li>\n    <li>Add a script for <a href=\"manual.html#addon_match-highlighter\">highlighting instances of the selection</a>.</li>\n    <li>New <a href=\"../mode/properties/index.html\">.properties</a> mode.</li>\n    <li>Fix many bugs.</li>\n  </ul>\n\n  <p class=\"rel\">27-01-2012: <a href=\"https://codemirror.net/5/codemirror-2.21.zip\">Version 2.21</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Added <a href=\"../mode/less/index.html\">LESS</a>, <a href=\"../mode/mysql/index.html\">MySQL</a>,\n    <a href=\"../mode/go/index.html\">Go</a>, and <a href=\"../mode/verilog/index.html\">Verilog</a> modes.</li>\n    <li>Add <a href=\"manual.html#option_smartIndent\"><code>smartIndent</code></a>\n    option.</li>\n    <li>Support a cursor in <a href=\"manual.html#option_readOnly\"><code>readOnly</code></a>-mode.</li>\n    <li>Support assigning multiple styles to a token.</li>\n    <li>Use a new approach to drawing the selection.</li>\n    <li>Add <a href=\"manual.html#scrollTo\"><code>scrollTo</code></a> method.</li>\n    <li>Allow undo/redo events to span non-adjacent lines.</li>\n    <li>Lots and lots of bugfixes.</li>\n  </ul>\n\n  <p class=\"rel\">20-12-2011: <a href=\"https://codemirror.net/5/codemirror-2.2.zip\">Version 2.2</a>:</p>\n\n  <ul class=\"rel-note\">\n    <li>Slightly incompatible API changes. Read <a href=\"upgrade_v2.2.html\">this</a>.</li>\n    <li>New approach\n    to <a href=\"manual.html#option_extraKeys\">binding</a> keys,\n    support for <a href=\"manual.html#option_keyMap\">custom\n    bindings</a>.</li>\n    <li>Support for overwrite (insert).</li>\n    <li><a href=\"manual.html#option_tabSize\">Custom-width</a>\n    and <a href=\"../demo/visibletabs.html\">styleable</a> tabs.</li>\n    <li>Moved more code into <a href=\"manual.html#addons\">add-on scripts</a>.</li>\n    <li>Support for sane vertical cursor movement in wrapped lines.</li>\n    <li>More reliable handling of\n    editing <a href=\"manual.html#markText\">marked text</a>.</li>\n    <li>Add minimal <a href=\"../demo/emacs.html\">emacs</a>\n    and <a href=\"../demo/vim.html\">vim</a> bindings.</li>\n    <li>Rename <code>coordsFromIndex</code>\n    to <a href=\"manual.html#posFromIndex\"><code>posFromIndex</code></a>,\n    add <a href=\"manual.html#indexFromPos\"><code>indexFromPos</code></a>\n    method.</li>\n  </ul>\n\n  <p class=\"rel\">21-11-2011: <a href=\"https://codemirror.net/5/codemirror-2.18.zip\">Version 2.18</a>:</p>\n  <p class=\"rel-note\">Fixes <code>TextMarker.clear</code>, which is broken in 2.17.</p>\n\n  <p class=\"rel\">21-11-2011: <a href=\"https://codemirror.net/5/codemirror-2.17.zip\">Version 2.17</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Add support for <a href=\"manual.html#option_lineWrapping\">line\n    wrapping</a> and <a href=\"manual.html#hideLine\">code\n    folding</a>.</li>\n    <li>Add <a href=\"../mode/gfm/index.html\">GitHub-style Markdown</a> mode.</li>\n    <li>Add <a href=\"../theme/monokai.css\">Monokai</a>\n    and <a href=\"../theme/rubyblue.css\">Rubyblue</a> themes.</li>\n    <li>Add <a href=\"manual.html#setBookmark\"><code>setBookmark</code></a> method.</li>\n    <li>Move some of the demo code into reusable components\n    under <a href=\"../addon/\"><code>lib/util</code></a>.</li>\n    <li>Make screen-coord-finding code faster and more reliable.</li>\n    <li>Fix drag-and-drop in Firefox.</li>\n    <li>Improve support for IME.</li>\n    <li>Speed up content rendering.</li>\n    <li>Fix browser's built-in search in Webkit.</li>\n    <li>Make double- and triple-click work in IE.</li>\n    <li>Various fixes to modes.</li>\n  </ul>\n\n  <p class=\"rel\">27-10-2011: <a href=\"https://codemirror.net/5/codemirror-2.16.zip\">Version 2.16</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Add <a href=\"../mode/perl/index.html\">Perl</a>, <a href=\"../mode/rust/index.html\">Rust</a>, <a href=\"../mode/tiddlywiki/index.html\">TiddlyWiki</a>, and <a href=\"../mode/groovy/index.html\">Groovy</a> modes.</li>\n    <li>Dragging text inside the editor now moves, rather than copies.</li>\n    <li>Add a <a href=\"manual.html#coordsFromIndex\"><code>coordsFromIndex</code></a> method.</li>\n    <li><strong>API change</strong>: <code>setValue</code> now no longer clears history. Use <a href=\"manual.html#clearHistory\"><code>clearHistory</code></a> for that.</li>\n    <li><strong>API change</strong>: <a href=\"manual.html#markText\"><code>markText</code></a> now\n    returns an object with <code>clear</code> and <code>find</code>\n    methods. Marked text is now more robust when edited.</li>\n    <li>Fix editing code with tabs in Internet Explorer.</li>\n  </ul>\n\n  <p class=\"rel\">26-09-2011: <a href=\"https://codemirror.net/5/codemirror-2.15.zip\">Version 2.15</a>:</p>\n  <p class=\"rel-note\">Fix bug that snuck into 2.14: Clicking the\n  character that currently has the cursor didn't re-focus the\n  editor.</p>\n\n  <p class=\"rel\">26-09-2011: <a href=\"https://codemirror.net/5/codemirror-2.14.zip\">Version 2.14</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Add <a href=\"../mode/clojure/index.html\">Clojure</a>, <a href=\"../mode/pascal/index.html\">Pascal</a>, <a href=\"../mode/ntriples/index.html\">NTriples</a>, <a href=\"../mode/jinja2/index.html\">Jinja2</a>, and <a href=\"../mode/markdown/index.html\">Markdown</a> modes.</li>\n    <li>Add <a href=\"../theme/cobalt.css\">Cobalt</a> and <a href=\"../theme/eclipse.css\">Eclipse</a> themes.</li>\n    <li>Add a <a href=\"manual.html#option_fixedGutter\"><code>fixedGutter</code></a> option.</li>\n    <li>Fix bug with <code>setValue</code> breaking cursor movement.</li>\n    <li>Make gutter updates much more efficient.</li>\n    <li>Allow dragging of text out of the editor (on modern browsers).</li>\n  </ul>\n\n\n  <p class=\"rel\">23-08-2011: <a href=\"https://codemirror.net/5/codemirror-2.13.zip\">Version 2.13</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Add <a href=\"../mode/ruby/index.html\">Ruby</a>, <a href=\"../mode/r/index.html\">R</a>, <a href=\"../mode/coffeescript/index.html\">CoffeeScript</a>, and <a href=\"../mode/velocity/index.html\">Velocity</a> modes.</li>\n    <li>Add <a href=\"manual.html#getGutterElement\"><code>getGutterElement</code></a> to API.</li>\n    <li>Several fixes to scrolling and positioning.</li>\n    <li>Add <a href=\"manual.html#option_smartHome\"><code>smartHome</code></a> option.</li>\n    <li>Add an experimental <a href=\"../mode/xmlpure/index.html\">pure XML</a> mode.</li>\n  </ul>\n\n  <p class=\"rel\">25-07-2011: <a href=\"https://codemirror.net/5/codemirror-2.12.zip\">Version 2.12</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Add a <a href=\"../mode/sparql/index.html\">SPARQL</a> mode.</li>\n    <li>Fix bug with cursor jumping around in an unfocused editor in IE.</li>\n    <li>Allow key and mouse events to bubble out of the editor. Ignore widget clicks.</li>\n    <li>Solve cursor flakiness after undo/redo.</li>\n    <li>Fix block-reindent ignoring the last few lines.</li>\n    <li>Fix parsing of multi-line attrs in XML mode.</li>\n    <li>Use <code>innerHTML</code> for HTML-escaping.</li>\n    <li>Some fixes to indentation in C-like mode.</li>\n    <li>Shrink horiz scrollbars when long lines removed.</li>\n    <li>Fix width feedback loop bug that caused the width of an inner DIV to shrink.</li>\n  </ul>\n\n  <p class=\"rel\">04-07-2011: <a href=\"https://codemirror.net/5/codemirror-2.11.zip\">Version 2.11</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Add a <a href=\"../mode/scheme/index.html\">Scheme mode</a>.</li>\n    <li>Add a <code>replace</code> method to search cursors, for cursor-preserving replacements.</li>\n    <li>Make the <a href=\"../mode/clike/index.html\">C-like mode</a> mode more customizable.</li>\n    <li>Update XML mode to spot mismatched tags.</li>\n    <li>Add <code>getStateAfter</code> API and <code>compareState</code> mode API methods for finer-grained mode magic.</li>\n    <li>Add a <code>getScrollerElement</code> API method to manipulate the scrolling DIV.</li>\n    <li>Fix drag-and-drop for Firefox.</li>\n    <li>Add a C# configuration for the <a href=\"../mode/clike/index.html\">C-like mode</a>.</li>\n    <li>Add <a href=\"../demo/fullscreen.html\">full-screen editing</a> and <a href=\"../demo/changemode.html\">mode-changing</a> demos.</li>\n  </ul>\n\n  <p class=\"rel\">07-06-2011: <a href=\"https://codemirror.net/5/codemirror-2.1.zip\">Version 2.1</a>:</p>\n  <p class=\"rel-note\">Add\n  a <a href=\"manual.html#option_theme\">theme</a> system\n  (<a href=\"../demo/theme.html\">demo</a>). Note that this is not\n  backwards-compatible—you'll have to update your styles and\n  modes!</p>\n\n  <p class=\"rel\">07-06-2011: <a href=\"https://codemirror.net/5/codemirror-2.02.zip\">Version 2.02</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Add a <a href=\"../mode/lua/index.html\">Lua mode</a>.</li>\n    <li>Fix reverse-searching for a regexp.</li>\n    <li>Empty lines can no longer break highlighting.</li>\n    <li>Rework scrolling model (the outer wrapper no longer does the scrolling).</li>\n    <li>Solve horizontal jittering on long lines.</li>\n    <li>Add <a href=\"../demo/runmode.html\">runmode.js</a>.</li>\n    <li>Immediately re-highlight text when typing.</li>\n    <li>Fix problem with 'sticking' horizontal scrollbar.</li>\n  </ul>\n\n  <p class=\"rel\">26-05-2011: <a href=\"https://codemirror.net/5/codemirror-2.01.zip\">Version 2.01</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Add a <a href=\"../mode/smalltalk/index.html\">Smalltalk mode</a>.</li>\n    <li>Add a <a href=\"../mode/rst/index.html\">reStructuredText mode</a>.</li>\n    <li>Add a <a href=\"../mode/python/index.html\">Python mode</a>.</li>\n    <li>Add a <a href=\"../mode/plsql/index.html\">PL/SQL mode</a>.</li>\n    <li><code>coordsChar</code> now works</li>\n    <li>Fix a problem where <code>onCursorActivity</code> interfered with <code>onChange</code>.</li>\n    <li>Fix a number of scrolling and mouse-click-position glitches.</li>\n    <li>Pass information about the changed lines to <code>onChange</code>.</li>\n    <li>Support cmd-up/down on OS X.</li>\n    <li>Add triple-click line selection.</li>\n    <li>Don't handle shift when changing the selection through the API.</li>\n    <li>Support <code>\"nocursor\"</code> mode for <code>readOnly</code> option.</li>\n    <li>Add an <code>onHighlightComplete</code> option.</li>\n    <li>Fix the context menu for Firefox.</li>\n  </ul>\n\n  <p class=\"rel\">28-03-2011: <a href=\"https://codemirror.net/5/codemirror-2.0.zip\">Version 2.0</a>:</p>\n  <p class=\"rel-note\">CodeMirror 2 is a complete rewrite that's\n  faster, smaller, simpler to use, and less dependent on browser\n  quirks. See <a href=\"internals.html\">this</a>\n  and <a href=\"http://groups.google.com/group/codemirror/browse_thread/thread/5a8e894024a9f580\">this</a>\n  for more information.</p>\n\n  <p class=\"rel\">22-02-2011: <a href=\"https://github.com/codemirror/codemirror5/tree/beta2\">Version 2.0 beta 2</a>:</p>\n  <p class=\"rel-note\">Somewhat more mature API, lots of bugs shaken out.</p>\n\n  <p class=\"rel\">17-02-2011: <a href=\"https://codemirror.net/5/codemirror-0.94.zip\">Version 0.94</a>:</p>\n  <ul class=\"rel-note\">\n    <li><code>tabMode: \"spaces\"</code> was modified slightly (now indents when something is selected).</li>\n    <li>Fixes a bug that would cause the selection code to break on some IE versions.</li>\n    <li>Disabling spell-check on WebKit browsers now works.</li>\n  </ul>\n\n  <p class=\"rel\">08-02-2011: <a href=\"https://codemirror.net/5/\">Version 2.0 beta 1</a>:</p>\n  <p class=\"rel-note\">CodeMirror 2 is a complete rewrite of\n  CodeMirror, no longer depending on an editable frame.</p>\n\n  <p class=\"rel\">19-01-2011: <a href=\"https://codemirror.net/5/codemirror-0.93.zip\">Version 0.93</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Added a <a href=\"contrib/regex/index.html\">Regular Expression</a> parser.</li>\n    <li>Fixes to the PHP parser.</li>\n    <li>Support for regular expression in search/replace.</li>\n    <li>Add <code>save</code> method to instances created with <code>fromTextArea</code>.</li>\n    <li>Add support for MS T-SQL in the SQL parser.</li>\n    <li>Support use of CSS classes for highlighting brackets.</li>\n    <li>Fix yet another hang with line-numbering in hidden editors.</li>\n  </ul>\n</section>\n\n<section id=v1>\n\n  <h2>Version 0.x</h2>\n\n  <p class=\"rel\">28-03-2011: <a href=\"https://codemirror.net/5/codemirror-1.0.zip\">Version 1.0</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Fix error when debug history overflows.</li>\n    <li>Refine handling of C# verbatim strings.</li>\n    <li>Fix some issues with JavaScript indentation.</li>\n  </ul>\n\n  <p class=\"rel\">17-12-2010: <a href=\"https://codemirror.net/5/codemirror-0.92.zip\">Version 0.92</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Make CodeMirror work in XHTML documents.</li>\n    <li>Fix bug in handling of backslashes in Python strings.</li>\n    <li>The <code>styleNumbers</code> option is now officially\n    supported and documented.</li>\n    <li><code>onLineNumberClick</code> option added.</li>\n    <li>More consistent names <code>onLoad</code> and\n    <code>onCursorActivity</code> callbacks. Old names still work, but\n    are deprecated.</li>\n    <li>Add a <a href=\"contrib/freemarker/index.html\">Freemarker</a> mode.</li>\n  </ul>\n\n  <p class=\"rel\">11-11-2010: <a\n  href=\"https://codemirror.net/5/codemirror-0.91.zip\">Version 0.91</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Adds support for <a href=\"contrib/java\">Java</a>.</li>\n    <li>Small additions to the <a href=\"contrib/php\">PHP</a> and <a href=\"contrib/sql\">SQL</a> parsers.</li>\n    <li>Work around various <a href=\"https://bugs.webkit.org/show_bug.cgi?id=47806\">Webkit</a> <a href=\"https://bugs.webkit.org/show_bug.cgi?id=23474\">issues</a>.</li>\n    <li>Fix <code>toTextArea</code> to update the code in the textarea.</li>\n    <li>Add a <code>noScriptCaching</code> option (hack to ease development).</li>\n    <li>Make sub-modes of <a href=\"mixedtest.html\">HTML mixed</a> mode configurable.</li>\n  </ul>\n\n  <p class=\"rel\">02-10-2010: <a\n  href=\"https://codemirror.net/5/codemirror-0.9.zip\">Version 0.9</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Add support for searching backwards.</li>\n    <li>There are now parsers for <a href=\"contrib/scheme/index.html\">Scheme</a>, <a href=\"contrib/xquery/index.html\">XQuery</a>, and <a href=\"contrib/ometa/index.html\">OmetaJS</a>.</li>\n    <li>Makes <code>height: \"dynamic\"</code> more robust.</li>\n    <li>Fixes bug where paste did not work on OS X.</li>\n    <li>Add a <code>enterMode</code> and <code>electricChars</code> options to make indentation even more customizable.</li>\n    <li>Add <code>firstLineNumber</code> option.</li>\n    <li>Fix bad handling of <code>@media</code> rules by the CSS parser.</li>\n    <li>Take a new, more robust approach to working around the invisible-last-line bug in WebKit.</li>\n  </ul>\n\n  <p class=\"rel\">22-07-2010: <a\n  href=\"https://codemirror.net/5/codemirror-0.8.zip\">Version 0.8</a>:</p>\n  <ul class=\"rel-note\">\n    <li>Add a <code>cursorCoords</code> method to find the screen\n    coordinates of the cursor.</li>\n    <li>A number of fixes and support for more syntax in the PHP parser.</li>\n    <li>Fix indentation problem with JSON-mode JS parser in Webkit.</li>\n    <li>Add a <a href=\"compress.html\">minification</a> UI.</li>\n    <li>Support a <code>height: dynamic</code> mode, where the editor's\n    height will adjust to the size of its content.</li>\n    <li>Better support for IME input mode.</li>\n    <li>Fix JavaScript parser getting confused when seeing a no-argument\n    function call.</li>\n    <li>Have CSS parser see the difference between selectors and other\n    identifiers.</li>\n    <li>Fix scrolling bug when pasting in a horizontally-scrolled\n    editor.</li>\n    <li>Support <code>toTextArea</code> method in instances created with\n    <code>fromTextArea</code>.</li>\n    <li>Work around new Opera cursor bug that causes the cursor to jump\n    when pressing backspace at the end of a line.</li>\n  </ul>\n\n  <p class=\"rel\">27-04-2010: <a\n  href=\"https://codemirror.net/5/codemirror-0.67.zip\">Version\n  0.67</a>:</p>\n  <p class=\"rel-note\">More consistent page-up/page-down behaviour\n  across browsers. Fix some issues with hidden editors looping forever\n  when line-numbers were enabled. Make PHP parser parse\n  <code>\"\\\\\"</code> correctly. Have <code>jumpToLine</code> work on\n  line handles, and add <code>cursorLine</code> function to fetch the\n  line handle where the cursor currently is. Add new\n  <code>setStylesheet</code> function to switch style-sheets in a\n  running editor.</p>\n\n  <p class=\"rel\">01-03-2010: <a\n  href=\"https://codemirror.net/5/codemirror-0.66.zip\">Version\n  0.66</a>:</p>\n  <p class=\"rel-note\">Adds <code>removeLine</code> method to API.\n  Introduces the <a href=\"contrib/plsql/index.html\">PLSQL parser</a>.\n  Marks XML errors by adding (rather than replacing) a CSS class, so\n  that they can be disabled by modifying their style. Fixes several\n  selection bugs, and a number of small glitches.</p>\n\n  <p class=\"rel\">12-11-2009: <a\n  href=\"https://codemirror.net/5/codemirror-0.65.zip\">Version\n  0.65</a>:</p>\n  <p class=\"rel-note\">Add support for having both line-wrapping and\n  line-numbers turned on, make paren-highlighting style customisable\n  (<code>markParen</code> and <code>unmarkParen</code> config\n  options), work around a selection bug that Opera\n  <em>re</em>introduced in version 10.</p>\n\n  <p class=\"rel\">23-10-2009: <a\n  href=\"https://codemirror.net/5/codemirror-0.64.zip\">Version\n  0.64</a>:</p>\n  <p class=\"rel-note\">Solves some issues introduced by the\n  paste-handling changes from the previous release. Adds\n  <code>setSpellcheck</code>, <code>setTextWrapping</code>,\n  <code>setIndentUnit</code>, <code>setUndoDepth</code>,\n  <code>setTabMode</code>, and <code>setLineNumbers</code> to\n  customise a running editor. Introduces an <a\n  href=\"contrib/sql/index.html\">SQL</a> parser. Fixes a few small\n  problems in the <a href=\"contrib/python/index.html\">Python</a>\n  parser. And, as usual, add workarounds for various newly discovered\n  browser incompatibilities.</p>\n\n  <p class=\"rel\">31-08-2009: <a href=\"https://codemirror.net/5/codemirror-0.63.zip\">Version 0.63</a>:</p>\n  <p class=\"rel-note\"> Overhaul of paste-handling (less fragile), fixes for several\n  serious IE8 issues (cursor jumping, end-of-document bugs) and a number\n  of small problems.</p>\n\n  <p class=\"rel\">30-05-2009: <a href=\"https://codemirror.net/5/codemirror-0.62.zip\">Version 0.62</a>:</p>\n  <p class=\"rel-note\">Introduces <a href=\"contrib/python/index.html\">Python</a>\n  and <a href=\"contrib/lua/index.html\">Lua</a> parsers. Add\n  <code>setParser</code> (on-the-fly mode changing) and\n  <code>clearHistory</code> methods. Make parsing passes time-based\n  instead of lines-based (see the <code>passTime</code> option).</p>\n\n</section>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/doc/reporting.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Reporting Bugs</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"docs.css\">\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Reporting bugs</a>\n  </ul>\n</div>\n\n<article>\n\n<h2>Reporting bugs effectively</h2>\n\n<div class=\"left\">\n\n<p>So you found a problem in CodeMirror. By all means, report it! Bug\nreports from users are the main drive behind improvements to\nCodeMirror. But first, please read over these points:</p>\n\n<ol>\n  <li>CodeMirror is maintained by volunteers. They don't owe you\n  anything, so be polite. Reports with an indignant or belligerent\n  tone tend to be moved to the bottom of the pile.</li>\n\n  <li>Include information about <strong>the browser in which the\n  problem occurred</strong>. Even if you tested several browsers, and\n  the problem occurred in all of them, mention this fact in the bug\n  report. Also include browser version numbers and the operating\n  system that you're on.</li>\n\n  <li>Mention which release of CodeMirror you're using. Preferably,\n  try also with the current development snapshot, to ensure the\n  problem has not already been fixed.</li>\n\n  <li>Mention very precisely what went wrong. \"X is broken\" is not a\n  good bug report. What did you expect to happen? What happened\n  instead? Describe the exact steps a maintainer has to take to reproduce\n  the error. We can not fix something that we can not observe.</li>\n\n  <li>If the problem can not be reproduced in any of the demos\n  included in the CodeMirror distribution, please provide an HTML\n  document that demonstrates the problem. The best way to do this is\n  to go to <a href=\"http://jsbin.com/ihunin/1/edit\">jsbin.com</a>, enter\n  it there, press save, and include the resulting link in your bug\n  report.</li>\n</ol>\n\n</div>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/doc/upgrade_v2.2.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Version 2.2 upgrade guide</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"docs.css\">\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">2.2 upgrade guide</a>\n  </ul>\n</div>\n\n<article>\n\n<h2>Upgrading to v2.2</h2>\n\n<p>There are a few things in the 2.2 release that require some care\nwhen upgrading.</p>\n\n<h3>No more default.css</h3>\n\n<p>The default theme is now included\nin <a href=\"../lib/codemirror.css\"><code>codemirror.css</code></a>, so\nyou do not have to included it separately anymore. (It was tiny, so\neven if you're not using it, the extra data overhead is negligible.)\n\n<h3>Different key customization</h3>\n\n<p>CodeMirror has moved to a system\nwhere <a href=\"manual.html#option_keyMap\">keymaps</a> are used to\nbind behavior to keys. This means <a href=\"../demo/emacs.html\">custom\nbindings</a> are now possible.</p>\n\n<p>Three options that influenced key\nbehavior, <code>tabMode</code>, <code>enterMode</code>,\nand <code>smartHome</code>, are no longer supported. Instead, you can\nprovide custom bindings to influence the way these keys act. This is\ndone through the\nnew <a href=\"manual.html#option_extraKeys\"><code>extraKeys</code></a>\noption, which can hold an object mapping key names to functionality. A\nsimple example would be:</p>\n\n<pre>  extraKeys: {\n    \"Ctrl-S\": function(instance) { saveText(instance.getValue()); },\n    \"Ctrl-/\": \"undo\"\n  }</pre>\n\n<p>Keys can be mapped either to functions, which will be given the\neditor instance as argument, or to strings, which are mapped through\nfunctions through the <code>CodeMirror.commands</code> table, which\ncontains all the built-in editing commands, and can be inspected and\nextended by external code.</p>\n\n<p>By default, the <code>Home</code> key is bound to\nthe <code>\"goLineStartSmart\"</code> command, which moves the cursor to\nthe first non-whitespace character on the line. You can set do this to\nmake it always go to the very start instead:</p>\n\n<pre>  extraKeys: {\"Home\": \"goLineStart\"}</pre>\n\n<p>Similarly, <code>Enter</code> is bound\nto <code>\"newlineAndIndent\"</code> by default. You can bind it to\nsomething else to get different behavior. To disable special handling\ncompletely and only get a newline character inserted, you can bind it\nto <code>false</code>:</p>\n\n<pre>  extraKeys: {\"Enter\": false}</pre>\n\n<p>The same works for <code>Tab</code>. If you don't want CodeMirror\nto handle it, bind it to <code>false</code>. The default behaviour is\nto indent the current line more (<code>\"indentMore\"</code> command),\nand indent it less when shift is held (<code>\"indentLess\"</code>).\nThere are also <code>\"indentAuto\"</code> (smart indent)\nand <code>\"insertTab\"</code> commands provided for alternate\nbehavior. Or you can write your own handler function to do something\ndifferent altogether.</p>\n\n<h3>Tabs</h3>\n\n<p>Handling of tabs changed completely. The display width of tabs can\nnow be set with the <code>tabSize</code> option, and tabs can\nbe <a href=\"../demo/visibletabs.html\">styled</a> by setting CSS rules\nfor the <code>cm-tab</code> class.</p>\n\n<p>The default width for tabs is now 4, as opposed to the 8 that is\nhard-wired into browsers. If you are relying on 8-space tabs, make\nsure you explicitly set <code>tabSize: 8</code> in your options.</p>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/doc/upgrade_v3.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Version 3 upgrade guide</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"docs.css\">\n<script src=\"../lib/codemirror.js\"></script>\n<link rel=\"stylesheet\" href=\"../lib/codemirror.css\">\n<script src=\"../addon/runmode/runmode.js\"></script>\n<script src=\"../addon/runmode/colorize.js\"></script>\n<script src=\"../mode/javascript/javascript.js\"></script>\n<script src=\"../mode/xml/xml.js\"></script>\n<script src=\"../mode/css/css.js\"></script>\n<script src=\"../mode/htmlmixed/htmlmixed.js\"></script>\n<script src=\"activebookmark.js\"></script>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#upgrade\">Upgrade guide</a>\n    <li><a href=\"#dom\">DOM structure</a></li>\n    <li><a href=\"#gutters\">Gutter model</a></li>\n    <li><a href=\"#events\">Event handling</a></li>\n    <li><a href=\"#marktext\">markText method arguments</a></li>\n    <li><a href=\"#folding\">Line folding</a></li>\n    <li><a href=\"#lineclass\">Line CSS classes</a></li>\n    <li><a href=\"#positions\">Position properties</a></li>\n    <li><a href=\"#matchbrackets\">Bracket matching</a></li>\n    <li><a href=\"#modes\">Mode management</a></li>\n    <li><a href=\"#new\">New features</a></li>\n  </ul>\n</div>\n\n<article>\n\n<h2 id=upgrade>Upgrading to version 3</h2>\n\n<p>Version 3 does not depart too much from 2.x API, and sites that use\nCodeMirror in a very simple way might be able to upgrade without\ntrouble. But it does introduce a number of incompatibilities. Please\nat least skim this text before upgrading.</p>\n\n<p>Note that <strong>version 3 drops full support for Internet\nExplorer 7</strong>. The editor will mostly work on that browser, but\nit'll be significantly glitchy.</p>\n\n<section id=dom>\n  <h2>DOM structure</h2>\n\n<p>This one is the most likely to cause problems. The internal\nstructure of the editor has changed quite a lot, mostly to implement a\nnew scrolling model.</p>\n\n<p>Editor height is now set on the outer wrapper element (CSS\nclass <code>CodeMirror</code>), not on the scroller element\n(<code>CodeMirror-scroll</code>).</p>\n\n<p>Other nodes were moved, dropped, and added. If you have any code\nthat makes assumptions about the internal DOM structure of the editor,\nyou'll have to re-test it and probably update it to work with v3.</p>\n\n<p>See the <a href=\"manual.html#styling\">styling section</a> of the\nmanual for more information.</p>\n</section>\n<section id=gutters>\n  <h2>Gutter model</h2>\n\n<p>In CodeMirror 2.x, there was a single gutter, and line markers\ncreated with <code>setMarker</code> would have to somehow coexist with\nthe line numbers (if present). Version 3 allows you to specify an\narray of gutters, <a href=\"manual.html#option_gutters\">by class\nname</a>,\nuse <a href=\"manual.html#setGutterMarker\"><code>setGutterMarker</code></a>\nto add or remove markers in individual gutters, and clear whole\ngutters\nwith <a href=\"manual.html#clearGutter\"><code>clearGutter</code></a>.\nGutter markers are now specified as DOM nodes, rather than HTML\nsnippets.</p>\n\n<p>The gutters no longer horizontally scrolls along with the content.\nThe <code>fixedGutter</code> option was removed (since it is now the\nonly behavior).</p>\n\n<pre data-lang=\"text/html\">\n&lt;style>\n  /* Define a gutter style */\n  .note-gutter { width: 3em; background: cyan; }\n&lt;/style>\n&lt;script>\n  // Create an instance with two gutters -- line numbers and notes\n  var cm = new CodeMirror(document.body, {\n    gutters: [\"note-gutter\", \"CodeMirror-linenumbers\"],\n    lineNumbers: true\n  });\n  // Add a note to line 0\n  cm.setGutterMarker(0, \"note-gutter\", document.createTextNode(\"hi\"));\n&lt;/script>\n</pre>\n</section>\n<section id=events>\n  <h2>Event handling</h2>\n\n<p>Most of the <code>onXYZ</code> options have been removed. The same\neffect is now obtained by calling\nthe <a href=\"manual.html#on\"><code>on</code></a> method with a string\nidentifying the event type. Multiple handlers can now be registered\n(and individually unregistered) for an event, and objects such as line\nhandlers now also expose events. See <a href=\"manual.html#events\">the\nfull list here</a>.</p>\n\n<p>(The <code>onKeyEvent</code> and <code>onDragEvent</code> options,\nwhich act more as hooks than as event handlers, are still there in\ntheir old form.)</p>\n\n<pre data-lang=\"javascript\">\ncm.on(\"change\", function(cm, change) {\n  console.log(\"something changed! (\" + change.origin + \")\");\n});\n</pre>\n</section>\n<section id=marktext>\n  <h2>markText method arguments</h2>\n\n<p>The <a href=\"manual.html#markText\"><code>markText</code></a> method\n(which has gained some interesting new features, such as creating\natomic and read-only spans, or replacing spans with widgets) no longer\ntakes the CSS class name as a separate argument, but makes it an\noptional field in the options object instead.</p>\n\n<pre data-lang=\"javascript\">\n// Style first ten lines, and forbid the cursor from entering them\ncm.markText({line: 0, ch: 0}, {line: 10, ch: 0}, {\n  className: \"magic-text\",\n  inclusiveLeft: true,\n  atomic: true\n});\n</pre>\n</section>\n<section id=folding>\n  <h2>Line folding</h2>\n\n<p>The interface for hiding lines has been\nremoved. <a href=\"manual.html#markText\"><code>markText</code></a> can\nnow be used to do the same in a more flexible and powerful way.</p>\n\n<p>The <a href=\"../demo/folding.html\">folding script</a> has been\nupdated to use the new interface, and should now be more robust.</p>\n\n<pre data-lang=\"javascript\">\n// Fold a range, replacing it with the text \"??\"\nvar range = cm.markText({line: 4, ch: 2}, {line: 8, ch: 1}, {\n  replacedWith: document.createTextNode(\"??\"),\n  // Auto-unfold when cursor moves into the range\n  clearOnEnter: true\n});\n// Get notified when auto-unfolding\nCodeMirror.on(range, \"clear\", function() {\n  console.log(\"boom\");\n});\n</pre>\n</section>\n<section id=lineclass>\n  <h2>Line CSS classes</h2>\n\n<p>The <code>setLineClass</code> method has been replaced\nby <a href=\"manual.html#addLineClass\"><code>addLineClass</code></a>\nand <a href=\"manual.html#removeLineClass\"><code>removeLineClass</code></a>,\nwhich allow more modular control over the classes attached to a line.</p>\n\n<pre data-lang=\"javascript\">\nvar marked = cm.addLineClass(10, \"background\", \"highlighted-line\");\nsetTimeout(function() {\n  cm.removeLineClass(marked, \"background\", \"highlighted-line\");\n});\n</pre>\n</section>\n<section id=positions>\n  <h2>Position properties</h2>\n\n<p>All methods that take or return objects that represent screen\npositions now use <code>{left, top, bottom, right}</code> properties\n(not always all of them) instead of the <code>{x, y, yBot}</code> used\nby some methods in v2.x.</p>\n\n<p>Affected methods\nare <a href=\"manual.html#cursorCoords\"><code>cursorCoords</code></a>, <a href=\"manual.html#charCoords\"><code>charCoords</code></a>, <a href=\"manual.html#coordsChar\"><code>coordsChar</code></a>,\nand <a href=\"manual.html#getScrollInfo\"><code>getScrollInfo</code></a>.</p>\n</section>\n<section id=matchbrackets>\n  <h2>Bracket matching no longer in core</h2>\n\n<p>The <a href=\"manual.html#addon_matchbrackets\"><code>matchBrackets</code></a>\noption is no longer defined in the core editor.\nLoad <code>addon/edit/matchbrackets.js</code> to enable it.</p>\n</section>\n<section id=modes>\n  <h2>Mode management</h2>\n\n<p>The <code>CodeMirror.listModes</code>\nand <code>CodeMirror.listMIMEs</code> functions, used for listing\ndefined modes, are gone. You are now encouraged to simply\ninspect <code>CodeMirror.modes</code> (mapping mode names to mode\nconstructors) and <code>CodeMirror.mimeModes</code> (mapping MIME\nstrings to mode specs).</p>\n</section>\n<section id=new>\n  <h2>New features</h2>\n\n<p>Some more reasons to upgrade to version 3.</p>\n\n<ul>\n  <li>Bi-directional text support. CodeMirror will now mostly do the\n  right thing when editing Arabic or Hebrew text.</li>\n  <li>Arbitrary line heights. Using fonts with different heights\n  inside the editor (whether off by one pixel or fifty) is now\n  supported and handled gracefully.</li>\n  <li>In-line widgets. See <a href=\"../demo/widget.html\">the demo</a>\n  and <a href=\"manual.html#addLineWidget\">the docs</a>.</li>\n  <li>Defining custom options\n  with <a href=\"manual.html#defineOption\"><code>CodeMirror.defineOption</code></a>.</li>\n</ul>\n</section>\n</article>\n\n<script>setTimeout(function(){CodeMirror.colorize();}, 20);</script>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/doc/upgrade_v4.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Version 4 upgrade guide</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"docs.css\">\n<script src=\"activebookmark.js\"></script>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#upgrade\">Upgrade guide</a>\n    <li><a href=\"#multisel\">Multiple selections</a>\n    <li><a href=\"#beforeSelectionChange\">The beforeSelectionChange event</a>\n    <li><a href=\"#replaceSelection\">replaceSelection and collapsing</a>\n    <li><a href=\"#changeEvent\">change event data</a>\n    <li><a href=\"#showIfHidden\">showIfHidden option to line widgets</a>\n    <li><a href=\"#module\">Module loaders</a>\n    <li><a href=\"#shareddata\">Mutating shared data structures</a></li>\n    <li><a href=\"#deprecated\">Deprecated interfaces dropped</a>\n  </ul>\n</div>\n\n<article>\n\n<h2 id=upgrade>Upgrading to version 4</h2>\n\n<p>CodeMirror 4's interface is <em>very</em> close version 3, but it\ndoes fix a few awkward details in a backwards-incompatible ways. At\nleast skim the text below before upgrading.</p>\n\n<section id=multisel><h2>Multiple selections</h2>\n\n<p>The main new feature in version 4 is multiple selections. The\nsingle-selection variants of methods are still there, but now\ntypically act only on the <em>primary</em> selection (usually the last\none added).</p>\n\n<p>The exception to this\nis <a href=\"manual.html#getSelection\"><strong><code>getSelection</code></strong></a>,\nwhich will now return the content of <em>all</em> selections\n(separated by newlines, or whatever <code>lineSep</code> parameter you passed\nit).</p>\n\n</section>\n\n<section id=beforeSelectionChange><h2>The beforeSelectionChange event</h2>\n\n<p>This event still exists, but the object it is passed has\na <a href=\"manual.html#event_beforeSelectionChange\">completely new\ninterface</a>, because such changes now concern multiple\nselections.</p>\n\n</section>\n\n<section id=replaceSelection><h2>replaceSelection's collapsing behavior</h2>\n\n<p>By\ndefault, <a href=\"manual.html#replaceSelection\"><code>replaceSelection</code></a>\nwould leave the newly inserted text selected. This is only rarely what\nyou want, and also (slightly) more expensive in the new model, so the\ndefault was changed to <code>\"end\"</code>, meaning the old behavior\nmust be explicitly specified by passing a second argument\nof <code>\"around\"</code>.</p>\n\n</section>\n\n<section id=changeEvent><h2>change event data</h2>\n\n<p>Rather than forcing client code to follow <code>next</code>\npointers from one change object to the next, the library will now\nsimply fire\nmultiple <a href=\"manual.html#event_change\"><code>\"change\"</code></a>\nevents. Existing code will probably continue to work unmodified.</p>\n\n</section>\n\n<section id=showIfHidden><h2>showIfHidden option to line widgets</h2>\n\n<p>This option, which conceptually caused line widgets to be visible\neven if their line was hidden, was never really well-defined, and was\nbuggy from the start. It would be a rather expensive feature, both in\ncode complexity and run-time performance, to implement properly. It\nhas been dropped entirely in 4.0.</p>\n\n</section>\n\n<section id=module><h2>Module loaders</h2>\n\n<p>All modules in the CodeMirror distribution are now wrapped in a\nshim function to make them compatible with both AMD\n(<a href=\"http://requirejs.org\">requirejs</a>) and CommonJS (as used\nby <a href=\"http://nodejs.org/\">node</a>\nand <a href=\"http://browserify.org/\">browserify</a>) module loaders.\nWhen neither of these is present, they fall back to simply using the\nglobal <code>CodeMirror</code> variable.</p>\n\n<p>If you have a module loader present in your environment, CodeMirror\nwill attempt to use it, and you might need to change the way you load\nCodeMirror modules.</p>\n\n</section>\n\n<section id=shareddata><h2>Mutating shared data structures</h2>\n\n<p>Data structures produced by the library should not be mutated\nunless explicitly allowed, in general. This is slightly more strict in\n4.0 than it was in earlier versions, which copied the position objects\nreturned by <a href=\"manual.html#getCursor\"><code>getCursor</code></a>\nfor nebulous, historic reasons. In 4.0, mutating these\nobjects <em>will</em> corrupt your editor's selection.</p>\n\n</section>\n\n<section id=deprecated><h2>Deprecated interfaces dropped</h2>\n\n<p>A few properties and methods that have been deprecated for a while\nare now gone. Most notably, the <code>onKeyEvent</code>\nand <code>onDragEvent</code> options (use the\ncorresponding <a href=\"manual.html#event_dom\">events</a> instead).</p>\n\n<p>Two silly methods, which were mostly there to stay close to the 0.x\nAPI, <code>setLine</code> and <code>removeLine</code> are now gone.\nUse the more\nflexible <a href=\"manual.html#replaceRange\"><code>replaceRange</code></a>\nmethod instead.</p>\n\n<p>The long names for folding and completing functions\n(<code>CodeMirror.braceRangeFinder</code>, <code>CodeMirror.javascriptHint</code>,\netc) are also gone\n(use <code>CodeMirror.fold.brace</code>, <code>CodeMirror.hint.javascript</code>).</p>\n\n<p>The <code>className</code> property in the return value\nof <a href=\"manual.html#getTokenAt\"><code>getTokenAt</code></a>, which\nhas been superseded by the <code>type</code> property, is also no\nlonger present.</p>\n\n</section>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror 5</title>\n<meta charset=\"utf-8\"/>\n\n<link rel=stylesheet href=\"lib/codemirror.css\">\n<link rel=stylesheet href=\"doc/docs.css\">\n<script src=\"lib/codemirror.js\"></script>\n<script src=\"mode/xml/xml.js\"></script>\n<script src=\"mode/javascript/javascript.js\"></script>\n<script src=\"mode/css/css.js\"></script>\n<script src=\"mode/htmlmixed/htmlmixed.js\"></script>\n<script src=\"addon/edit/matchbrackets.js\"></script>\n\n<script src=\"doc/activebookmark.js\"></script>\n\n<style>\n  .CodeMirror { height: auto; border: 1px solid #ddd; }\n  .CodeMirror-scroll { max-height: 200px; }\n  .CodeMirror pre { padding-left: 7px; line-height: 1.25; }\n  .banner { background: #ffc; padding: 6px; border-bottom: 2px solid silver; text-align: center }\n</style>\n\n<div class=banner>\n  Note that this is the website for CodeMirror 5. <a href=\"https://codemirror.net/\">Version 6</a> is the current version.\n</div>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"doc/logo.png\"></a>\n\n  <ul>\n    <li><a class=active data-default=\"true\" href=\"#description\">Home</a>\n    <li><a href=\"doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n    <li><a href=\"https://codemirror.net/\">Version 6</a>\n  </ul>\n  <ul>\n    <li><a href=\"#features\">Features</a>\n    <li><a href=\"#community\">Community</a>\n    <li><a href=\"#browsersupport\">Browser support</a>\n  </ul>\n</div>\n\n<article>\n\n<section id=description class=first>\n  <p><strong>CodeMirror</strong> is a versatile text editor\n  implemented in JavaScript for the browser. It is specialized for\n  editing code, and comes with a number of <a href=\"mode/index.html\">language modes</a> and <a href=\"doc/manual.html#addons\">addons</a>\n  that implement more advanced editing functionality.</p>\n\n  <p>A rich <a href=\"doc/manual.html#api\">programming API</a> and a\n  CSS <a href=\"doc/manual.html#styling\">theming</a> system are\n  available for customizing CodeMirror to fit your application, and\n  extending it with new functionality.</p>\n</section>\n\n<section id=demo>\n  <h2>This is CodeMirror</h2>\n  <form style=\"position: relative; margin-top: .5em;\"><textarea id=demotext>\n<!-- Create a simple CodeMirror instance -->\n<link rel=\"stylesheet\" href=\"lib/codemirror.css\">\n<script src=\"lib/codemirror.js\"></script>\n<script>\n  var editor = CodeMirror.fromTextArea(myTextarea, {\n    lineNumbers: true\n  });\n</script></textarea>\n  <select id=\"demolist\" onchange=\"document.location = this.options[this.selectedIndex].value;\">\n    <option value=\"#\">Other demos...</option>\n    <option value=\"demo/complete.html\">Autocompletion</option>\n    <option value=\"demo/folding.html\">Code folding</option>\n    <option value=\"demo/theme.html\">Themes</option>\n    <option value=\"mode/htmlmixed/index.html\">Mixed language modes</option>\n    <option value=\"demo/bidi.html\">Bi-directional text</option>\n    <option value=\"demo/variableheight.html\">Variable font sizes</option>\n    <option value=\"demo/search.html\">Search interface</option>\n    <option value=\"demo/vim.html\">Vim bindings</option>\n    <option value=\"demo/emacs.html\">Emacs bindings</option>\n    <option value=\"demo/sublime.html\">Sublime Text bindings</option>\n    <option value=\"demo/tern.html\">Tern integration</option>\n    <option value=\"demo/merge.html\">Merge/diff interface</option>\n    <option value=\"demo/fullscreen.html\">Full-screen editor</option>\n    <option value=\"demo/simplescrollbars.html\">Custom scrollbars</option>\n  </select></form>\n  <script>\n    var editor = CodeMirror.fromTextArea(document.getElementById(\"demotext\"), {\n      lineNumbers: true,\n      mode: \"text/html\",\n      matchBrackets: true\n    });\n  </script>\n\n  <div class=actions>\n    Get the current version: <a href=\"https://codemirror.net/5/codemirror.zip\">5.65.16</a>.<br>\n    You can see the <a href=\"https://github.com/codemirror/codemirror5\" title=\"GitHub repository\">code</a>,<br>\n    read the <a href=\"doc/releases.html\">release notes</a>,<br>\n    or study the <a href=\"doc/manual.html\">user manual</a>.\n  </div>\n\n</section>\n\n<section id=features>\n  <h2>Features</h2>\n  <ul>\n    <li>Support for <a href=\"mode/index.html\">over 100 languages</a> out of the box\n    <li>A powerful, <a href=\"mode/htmlmixed/index.html\">composable</a> language mode <a href=\"doc/manual.html#modeapi\">system</a>\n    <li><a href=\"doc/manual.html#addon_show-hint\">Autocompletion</a> (<a href=\"demo/xmlcomplete.html\">XML</a>)\n    <li><a href=\"doc/manual.html#addon_foldcode\">Code folding</a>\n    <li><a href=\"doc/manual.html#option_extraKeys\">Configurable</a> keybindings\n    <li><a href=\"demo/vim.html\">Vim</a>, <a href=\"demo/emacs.html\">Emacs</a>, and <a href=\"demo/sublime.html\">Sublime Text</a> bindings\n    <li><a href=\"doc/manual.html#addon_search\">Search and replace</a> interface\n    <li><a href=\"doc/manual.html#addon_matchbrackets\">Bracket</a> and <a href=\"doc/manual.html#addon_matchtags\">tag</a> matching\n    <li>Support for <a href=\"demo/buffers.html\">split views</a>\n    <li><a href=\"doc/manual.html#addon_lint\">Linter integration</a>\n    <li><a href=\"demo/variableheight.html\">Mixing font sizes and styles</a>\n    <li><a href=\"demo/theme.html\">Various themes</a>\n    <li>Able to <a href=\"demo/resize.html\">resize to fit content</a>\n    <li><a href=\"doc/manual.html#mark_replacedWith\">Inline</a> and <a href=\"doc/manual.html#addLineWidget\">block</a> widgets\n    <li>Programmable <a href=\"demo/marker.html\">gutters</a>\n    <li>Making ranges of text <a href=\"doc/manual.html#markText\">styled, read-only, or atomic</a>\n    <li><a href=\"demo/bidi.html\">Bi-directional text</a> support\n    <li>Many other <a href=\"doc/manual.html#api\">methods</a> and <a href=\"doc/manual.html#addons\">addons</a>...\n  </ul>\n</section>\n\n<section id=community>\n  <h2>Community</h2>\n\n  <p>CodeMirror is an open-source project shared under\n  an <a href=\"LICENSE\">MIT license</a>. It is the editor used in the\n  dev tools for\n  <a href=\"https://hacks.mozilla.org/2013/11/firefox-developer-tools-episode-27-edit-as-html-codemirror-more/\">Firefox</a>,\n  <a href=\"https://developers.google.com/chrome-developer-tools/\">Chrome</a>,\n  and <a href=\"https://developer.apple.com/safari/tools/\">Safari</a>, in <a href=\"http://www.lighttable.com/\">Light\n  Table</a>, <a href=\"http://brackets.io/\">Adobe\n  Brackets</a>, <a href=\"http://blog.bitbucket.org/2013/05/14/edit-your-code-in-the-cloud-with-bitbucket/\">Bitbucket</a>,\n  and <a href=\"doc/realworld.html\">many other projects</a>.</p>\n\n  <p>Development and bug tracking happens\n  on <a href=\"https://github.com/codemirror/CodeMirror/\">github</a>\n  (<a href=\"http://marijnhaverbeke.nl/git/codemirror\">alternate git\n  repository</a>).\n  Please <a href=\"https://codemirror.net/5/doc/reporting.html\">read these\n  pointers</a> before submitting a bug. Use pull requests to submit\n  patches. All contributions must be released under the same MIT\n  license that CodeMirror uses.</p>\n\n  <p>Discussion around the project is done on\n  a <a href=\"https://discuss.codemirror.net\">discussion forum</a>.\n  Announcements related to the project, such as new versions, are\n  posted in the\n  forum's <a href=\"https://discuss.codemirror.net/c/announce\">\"announce\"</a>\n  category. If needed, you can\n  contact <a href=\"mailto:marijn@haverbeke.berlin\">the maintainer</a>\n  directly. We aim to be an inclusive, welcoming community. To make\n  that explicit, we have\n  a <a href=\"http://contributor-covenant.org/version/1/1/0/\">code of\n  conduct</a> that applies to communication around the project.</p>\n</section>\n\n<section id=browsersupport>\n  <h2>Browser support</h2>\n  <p>The <em>desktop</em> versions of the following browsers,\n  in <em>standards mode</em> (HTML5 <code>&lt;!doctype html></code>\n  recommended) are supported:</p>\n  <table style=\"margin-bottom: 1em\">\n    <tr><th>Firefox</th><td>version 4 and up</td></tr>\n    <tr><th>Chrome</th><td>any version</td></tr>\n    <tr><th>Safari</th><td>version 5.2 and up</td></tr>\n    <tr><th style=\"padding-right: 1em;\">Internet Explorer/Edge</th><td>version 8 and up</td></tr>\n    <tr><th>Opera</th><td>version 9 and up</td></tr>\n  </table>\n  <p>Support for modern mobile browsers is experimental. Recent\n  versions of the iOS browser and Chrome on Android should work\n  pretty well.</p>\n</section>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/keymap/emacs.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var cmds = CodeMirror.commands;\n  var Pos = CodeMirror.Pos;\n  function posEq(a, b) { return a.line == b.line && a.ch == b.ch; }\n\n  // Kill 'ring'\n\n  var killRing = [];\n  function addToRing(str) {\n    killRing.push(str);\n    if (killRing.length > 50) killRing.shift();\n  }\n  function growRingTop(str) {\n    if (!killRing.length) return addToRing(str);\n    killRing[killRing.length - 1] += str;\n  }\n  function getFromRing(n) { return killRing[killRing.length - (n ? Math.min(n, 1) : 1)] || \"\"; }\n  function popFromRing() { if (killRing.length > 1) killRing.pop(); return getFromRing(); }\n\n  var lastKill = null;\n\n  // Internal generic kill function, used by several mapped kill \"family\" functions.\n  function _kill(cm, from, to, ring, text) {\n    if (text == null) text = cm.getRange(from, to);\n\n    if (ring == \"grow\" && lastKill && lastKill.cm == cm && posEq(from, lastKill.pos) && cm.isClean(lastKill.gen))\n      growRingTop(text);\n    else if (ring !== false)\n      addToRing(text);\n    cm.replaceRange(\"\", from, to, \"+delete\");\n\n    if (ring == \"grow\") lastKill = {cm: cm, pos: from, gen: cm.changeGeneration()};\n    else lastKill = null;\n  }\n\n  // Boundaries of various units\n\n  function byChar(cm, pos, dir) {\n    return cm.findPosH(pos, dir, \"char\", true);\n  }\n\n  function byWord(cm, pos, dir) {\n    return cm.findPosH(pos, dir, \"word\", true);\n  }\n\n  function byLine(cm, pos, dir) {\n    return cm.findPosV(pos, dir, \"line\", cm.doc.sel.goalColumn);\n  }\n\n  function byPage(cm, pos, dir) {\n    return cm.findPosV(pos, dir, \"page\", cm.doc.sel.goalColumn);\n  }\n\n  function byParagraph(cm, pos, dir) {\n    var no = pos.line, line = cm.getLine(no);\n    var sawText = /\\S/.test(dir < 0 ? line.slice(0, pos.ch) : line.slice(pos.ch));\n    var fst = cm.firstLine(), lst = cm.lastLine();\n    for (;;) {\n      no += dir;\n      if (no < fst || no > lst)\n        return cm.clipPos(Pos(no - dir, dir < 0 ? 0 : null));\n      line = cm.getLine(no);\n      var hasText = /\\S/.test(line);\n      if (hasText) sawText = true;\n      else if (sawText) return Pos(no, 0);\n    }\n  }\n\n  function bySentence(cm, pos, dir) {\n    var line = pos.line, ch = pos.ch;\n    var text = cm.getLine(pos.line), sawWord = false;\n    for (;;) {\n      var next = text.charAt(ch + (dir < 0 ? -1 : 0));\n      if (!next) { // End/beginning of line reached\n        if (line == (dir < 0 ? cm.firstLine() : cm.lastLine())) return Pos(line, ch);\n        text = cm.getLine(line + dir);\n        if (!/\\S/.test(text)) return Pos(line, ch);\n        line += dir;\n        ch = dir < 0 ? text.length : 0;\n        continue;\n      }\n      if (sawWord && /[!?.]/.test(next)) return Pos(line, ch + (dir > 0 ? 1 : 0));\n      if (!sawWord) sawWord = /\\w/.test(next);\n      ch += dir;\n    }\n  }\n\n  function byExpr(cm, pos, dir) {\n    var wrap;\n    if (cm.findMatchingBracket && (wrap = cm.findMatchingBracket(pos, {strict: true}))\n        && wrap.match && (wrap.forward ? 1 : -1) == dir)\n      return dir > 0 ? Pos(wrap.to.line, wrap.to.ch + 1) : wrap.to;\n\n    for (var first = true;; first = false) {\n      var token = cm.getTokenAt(pos);\n      var after = Pos(pos.line, dir < 0 ? token.start : token.end);\n      if (first && dir > 0 && token.end == pos.ch || !/\\w/.test(token.string)) {\n        var newPos = cm.findPosH(after, dir, \"char\");\n        if (posEq(after, newPos)) return pos;\n        else pos = newPos;\n      } else {\n        return after;\n      }\n    }\n  }\n\n  // Prefixes (only crudely supported)\n\n  function getPrefix(cm, precise) {\n    var digits = cm.state.emacsPrefix;\n    if (!digits) return precise ? null : 1;\n    clearPrefix(cm);\n    return digits == \"-\" ? -1 : Number(digits);\n  }\n\n  function repeated(cmd) {\n    var f = typeof cmd == \"string\" ? function(cm) { cm.execCommand(cmd); } : cmd;\n    return function(cm) {\n      var prefix = getPrefix(cm);\n      f(cm);\n      for (var i = 1; i < prefix; ++i) f(cm);\n    };\n  }\n\n  function findEnd(cm, pos, by, dir) {\n    var prefix = getPrefix(cm);\n    if (prefix < 0) { dir = -dir; prefix = -prefix; }\n    for (var i = 0; i < prefix; ++i) {\n      var newPos = by(cm, pos, dir);\n      if (posEq(newPos, pos)) break;\n      pos = newPos;\n    }\n    return pos;\n  }\n\n  function move(by, dir) {\n    var f = function(cm) {\n      cm.extendSelection(findEnd(cm, cm.getCursor(), by, dir));\n    };\n    f.motion = true;\n    return f;\n  }\n\n  function killTo(cm, by, dir, ring) {\n    var selections = cm.listSelections(), cursor;\n    var i = selections.length;\n    while (i--) {\n      cursor = selections[i].head;\n      _kill(cm, cursor, findEnd(cm, cursor, by, dir), ring);\n    }\n  }\n\n  function _killRegion(cm, ring) {\n    if (cm.somethingSelected()) {\n      var selections = cm.listSelections(), selection;\n      var i = selections.length;\n      while (i--) {\n        selection = selections[i];\n        _kill(cm, selection.anchor, selection.head, ring);\n      }\n      return true;\n    }\n  }\n\n  function addPrefix(cm, digit) {\n    if (cm.state.emacsPrefix) {\n      if (digit != \"-\") cm.state.emacsPrefix += digit;\n      return;\n    }\n    // Not active yet\n    cm.state.emacsPrefix = digit;\n    cm.on(\"keyHandled\", maybeClearPrefix);\n    cm.on(\"inputRead\", maybeDuplicateInput);\n  }\n\n  var prefixPreservingKeys = {\"Alt-G\": true, \"Ctrl-X\": true, \"Ctrl-Q\": true, \"Ctrl-U\": true};\n\n  function maybeClearPrefix(cm, arg) {\n    if (!cm.state.emacsPrefixMap && !prefixPreservingKeys.hasOwnProperty(arg))\n      clearPrefix(cm);\n  }\n\n  function clearPrefix(cm) {\n    cm.state.emacsPrefix = null;\n    cm.off(\"keyHandled\", maybeClearPrefix);\n    cm.off(\"inputRead\", maybeDuplicateInput);\n  }\n\n  function maybeDuplicateInput(cm, event) {\n    var dup = getPrefix(cm);\n    if (dup > 1 && event.origin == \"+input\") {\n      var one = event.text.join(\"\\n\"), txt = \"\";\n      for (var i = 1; i < dup; ++i) txt += one;\n      cm.replaceSelection(txt);\n    }\n  }\n\n  function maybeRemovePrefixMap(cm, arg) {\n    if (typeof arg == \"string\" && (/^\\d$/.test(arg) || arg == \"Ctrl-U\")) return;\n    cm.removeKeyMap(prefixMap);\n    cm.state.emacsPrefixMap = false;\n    cm.off(\"keyHandled\", maybeRemovePrefixMap);\n    cm.off(\"inputRead\", maybeRemovePrefixMap);\n  }\n\n  // Utilities\n\n  cmds.setMark = function (cm) {\n    cm.setCursor(cm.getCursor());\n    cm.setExtending(!cm.getExtending());\n    cm.on(\"change\", function() { cm.setExtending(false); });\n  }\n\n  function clearMark(cm) {\n    cm.setExtending(false);\n    cm.setCursor(cm.getCursor());\n  }\n\n  function makePrompt(msg) {\n    var fragment = document.createDocumentFragment();\n    var input = document.createElement(\"input\");\n    input.setAttribute(\"type\", \"text\");\n    input.style.width = \"10em\";\n    fragment.appendChild(document.createTextNode(msg + \": \"));\n    fragment.appendChild(input);\n    return fragment;\n  }\n\n  function getInput(cm, msg, f) {\n    if (cm.openDialog)\n      cm.openDialog(makePrompt(msg), f, {bottom: true});\n    else\n      f(prompt(msg, \"\"));\n  }\n\n  function operateOnWord(cm, op) {\n    var start = cm.getCursor(), end = cm.findPosH(start, 1, \"word\");\n    cm.replaceRange(op(cm.getRange(start, end)), start, end);\n    cm.setCursor(end);\n  }\n\n  function toEnclosingExpr(cm) {\n    var pos = cm.getCursor(), line = pos.line, ch = pos.ch;\n    var stack = [];\n    while (line >= cm.firstLine()) {\n      var text = cm.getLine(line);\n      for (var i = ch == null ? text.length : ch; i > 0;) {\n        var ch = text.charAt(--i);\n        if (ch == \")\")\n          stack.push(\"(\");\n        else if (ch == \"]\")\n          stack.push(\"[\");\n        else if (ch == \"}\")\n          stack.push(\"{\");\n        else if (/[\\(\\{\\[]/.test(ch) && (!stack.length || stack.pop() != ch))\n          return cm.extendSelection(Pos(line, i));\n      }\n      --line; ch = null;\n    }\n  }\n\n  // Commands. Names should match emacs function names (albeit in camelCase)\n  // except where emacs function names collide with code mirror core commands.\n\n  cmds.killRegion = function(cm) {\n    _kill(cm, cm.getCursor(\"start\"), cm.getCursor(\"end\"), true);\n  };\n\n  // Maps to emacs kill-line\n  cmds.killLineEmacs = repeated(function(cm) {\n    var start = cm.getCursor(), end = cm.clipPos(Pos(start.line));\n    var text = cm.getRange(start, end);\n    if (!/\\S/.test(text)) {\n      text += \"\\n\";\n      end = Pos(start.line + 1, 0);\n    }\n    _kill(cm, start, end, \"grow\", text);\n  });\n\n  cmds.killRingSave = function(cm) {\n    addToRing(cm.getSelection());\n    clearMark(cm);\n  };\n\n  cmds.yank = function(cm) {\n    var start = cm.getCursor();\n    cm.replaceRange(getFromRing(getPrefix(cm)), start, start, \"paste\");\n    cm.setSelection(start, cm.getCursor());\n  };\n\n  cmds.yankPop = function(cm) {\n    cm.replaceSelection(popFromRing(), \"around\", \"paste\");\n  };\n\n  cmds.forwardChar = move(byChar, 1);\n\n  cmds.backwardChar = move(byChar, -1)\n\n  cmds.deleteChar = function(cm) { killTo(cm, byChar, 1, false); };\n\n  cmds.deleteForwardChar = function(cm) {\n    _killRegion(cm, false) || killTo(cm, byChar, 1, false);\n  };\n\n  cmds.deleteBackwardChar = function(cm) {\n    _killRegion(cm, false) || killTo(cm, byChar, -1, false);\n  };\n\n  cmds.forwardWord = move(byWord, 1);\n\n  cmds.backwardWord = move(byWord, -1);\n\n  cmds.killWord = function(cm) { killTo(cm, byWord, 1, \"grow\"); };\n\n  cmds.backwardKillWord = function(cm) { killTo(cm, byWord, -1, \"grow\"); };\n\n  cmds.nextLine = move(byLine, 1);\n\n  cmds.previousLine = move(byLine, -1);\n\n  cmds.scrollDownCommand = move(byPage, -1);\n\n  cmds.scrollUpCommand = move(byPage, 1);\n\n  cmds.backwardParagraph = move(byParagraph, -1);\n\n  cmds.forwardParagraph = move(byParagraph, 1);\n\n  cmds.backwardSentence = move(bySentence, -1);\n\n  cmds.forwardSentence = move(bySentence, 1);\n\n  cmds.killSentence = function(cm) { killTo(cm, bySentence, 1, \"grow\"); };\n\n  cmds.backwardKillSentence = function(cm) {\n    _kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), \"grow\");\n  };\n\n  cmds.killSexp = function(cm) { killTo(cm, byExpr, 1, \"grow\"); };\n\n  cmds.backwardKillSexp = function(cm) { killTo(cm, byExpr, -1, \"grow\"); };\n\n  cmds.forwardSexp = move(byExpr, 1);\n\n  cmds.backwardSexp = move(byExpr, -1);\n\n  cmds.markSexp = function(cm) {\n    var cursor = cm.getCursor();\n    cm.setSelection(findEnd(cm, cursor, byExpr, 1), cursor);\n  };\n\n  cmds.transposeSexps = function(cm) {\n    var leftStart = byExpr(cm, cm.getCursor(), -1);\n    var leftEnd = byExpr(cm, leftStart, 1);\n    var rightEnd = byExpr(cm, leftEnd, 1);\n    var rightStart = byExpr(cm, rightEnd, -1);\n    cm.replaceRange(cm.getRange(rightStart, rightEnd) +\n                    cm.getRange(leftEnd, rightStart) +\n                    cm.getRange(leftStart, leftEnd), leftStart, rightEnd);\n  };\n\n  cmds.backwardUpList = repeated(toEnclosingExpr);\n\n  cmds.justOneSpace = function(cm) {\n    var pos = cm.getCursor(), from = pos.ch;\n    var to = pos.ch, text = cm.getLine(pos.line);\n    while (from && /\\s/.test(text.charAt(from - 1))) --from;\n    while (to < text.length && /\\s/.test(text.charAt(to))) ++to;\n    cm.replaceRange(\" \", Pos(pos.line, from), Pos(pos.line, to));\n  };\n\n  cmds.openLine = repeated(function(cm) {\n    cm.replaceSelection(\"\\n\", \"start\");\n  });\n\n  // maps to emacs 'transpose-chars'\n  cmds.transposeCharsRepeatable = repeated(function(cm) {\n    cm.execCommand(\"transposeChars\");\n  });\n\n  cmds.capitalizeWord = repeated(function(cm) {\n    operateOnWord(cm, function(w) {\n      var letter = w.search(/\\w/);\n      if (letter == -1) return w;\n      return w.slice(0, letter) + w.charAt(letter).toUpperCase() +\n          w.slice(letter + 1).toLowerCase();\n    });\n  });\n\n  cmds.upcaseWord = repeated(function(cm) {\n    operateOnWord(cm, function(w) { return w.toUpperCase(); });\n  });\n\n  cmds.downcaseWord = repeated(function(cm) {\n    operateOnWord(cm, function(w) { return w.toLowerCase(); });\n  });\n\n  // maps to emacs 'undo'\n  cmds.undoRepeatable = repeated(\"undo\");\n\n  cmds.keyboardQuit = function(cm) {\n    cm.execCommand(\"clearSearch\");\n    clearMark(cm);\n  }\n\n  cmds.newline = repeated(function(cm) { cm.replaceSelection(\"\\n\", \"end\"); });\n\n  cmds.gotoLine = function(cm) {\n    var prefix = getPrefix(cm, true);\n    if (prefix != null && prefix > 0) return cm.setCursor(prefix - 1);\n\n    getInput(cm, \"Goto line\", function(str) {\n      var num;\n      if (str && !isNaN(num = Number(str)) && num == (num|0) && num > 0)\n      cm.setCursor(num - 1);\n    });\n  };\n\n  cmds.indentRigidly = function(cm) {\n    cm.indentSelection(getPrefix(cm, true) || cm.getOption(\"indentUnit\"));\n  };\n\n  cmds.exchangePointAndMark = function(cm) {\n    cm.setSelection(cm.getCursor(\"head\"), cm.getCursor(\"anchor\"));\n  };\n\n  cmds.quotedInsertTab = repeated(\"insertTab\");\n\n  cmds.universalArgument = function addPrefixMap(cm) {\n    cm.state.emacsPrefixMap = true;\n    cm.addKeyMap(prefixMap);\n    cm.on(\"keyHandled\", maybeRemovePrefixMap);\n    cm.on(\"inputRead\", maybeRemovePrefixMap);\n  };\n\n  CodeMirror.emacs = {kill: _kill, killRegion: _killRegion, repeated: repeated};\n\n  // Actual keymap\n  var keyMap = CodeMirror.keyMap.emacs = CodeMirror.normalizeKeyMap({\n    \"Ctrl-W\": \"killRegion\",\n    \"Ctrl-K\": \"killLineEmacs\",\n    \"Alt-W\": \"killRingSave\",\n    \"Ctrl-Y\": \"yank\",\n    \"Alt-Y\": \"yankPop\",\n    \"Ctrl-Space\": \"setMark\",\n    \"Ctrl-Shift-2\": \"setMark\",\n    \"Ctrl-F\": \"forwardChar\",\n    \"Ctrl-B\": \"backwardChar\",\n    \"Right\": \"forwardChar\",\n    \"Left\": \"backwardChar\",\n    \"Ctrl-D\": \"deleteChar\",\n    \"Delete\": \"deleteForwardChar\",\n    \"Ctrl-H\": \"deleteBackwardChar\",\n    \"Backspace\": \"deleteBackwardChar\",\n    \"Alt-F\": \"forwardWord\",\n    \"Alt-B\": \"backwardWord\",\n    \"Alt-Right\": \"forwardWord\",\n    \"Alt-Left\": \"backwardWord\",\n    \"Alt-D\": \"killWord\",\n    \"Alt-Backspace\": \"backwardKillWord\",\n    \"Ctrl-N\": \"nextLine\",\n    \"Ctrl-P\": \"previousLine\",\n    \"Down\": \"nextLine\",\n    \"Up\": \"previousLine\",\n    \"Ctrl-A\": \"goLineStart\",\n    \"Ctrl-E\": \"goLineEnd\",\n    \"End\": \"goLineEnd\",\n    \"Home\": \"goLineStart\",\n    \"Alt-V\": \"scrollDownCommand\",\n    \"Ctrl-V\": \"scrollUpCommand\",\n    \"PageUp\": \"scrollDownCommand\",\n    \"PageDown\": \"scrollUpCommand\",\n    \"Ctrl-Up\": \"backwardParagraph\",\n    \"Ctrl-Down\": \"forwardParagraph\",\n    \"Alt-{\": \"backwardParagraph\",\n    \"Alt-}\": \"forwardParagraph\",\n    \"Alt-A\": \"backwardSentence\",\n    \"Alt-E\": \"forwardSentence\",\n    \"Alt-K\": \"killSentence\",\n    \"Ctrl-X Delete\": \"backwardKillSentence\",\n    \"Ctrl-Alt-K\": \"killSexp\",\n    \"Ctrl-Alt-Backspace\": \"backwardKillSexp\",\n    \"Ctrl-Alt-F\": \"forwardSexp\",\n    \"Ctrl-Alt-B\": \"backwardSexp\",\n    \"Shift-Ctrl-Alt-2\": \"markSexp\",\n    \"Ctrl-Alt-T\": \"transposeSexps\",\n    \"Ctrl-Alt-U\": \"backwardUpList\",\n    \"Alt-Space\": \"justOneSpace\",\n    \"Ctrl-O\": \"openLine\",\n    \"Ctrl-T\": \"transposeCharsRepeatable\",\n    \"Alt-C\": \"capitalizeWord\",\n    \"Alt-U\": \"upcaseWord\",\n    \"Alt-L\": \"downcaseWord\",\n    \"Alt-;\": \"toggleComment\",\n    \"Ctrl-/\": \"undoRepeatable\",\n    \"Shift-Ctrl--\": \"undoRepeatable\",\n    \"Ctrl-Z\": \"undoRepeatable\",\n    \"Cmd-Z\": \"undoRepeatable\",\n    \"Ctrl-X U\": \"undoRepeatable\",\n    \"Shift-Ctrl-Z\": \"redo\",\n    \"Shift-Alt-,\": \"goDocStart\",\n    \"Shift-Alt-.\": \"goDocEnd\",\n    \"Ctrl-S\": \"findPersistentNext\",\n    \"Ctrl-R\": \"findPersistentPrev\",\n    \"Ctrl-G\": \"keyboardQuit\",\n    \"Shift-Alt-5\": \"replace\",\n    \"Alt-/\": \"autocomplete\",\n    \"Enter\": \"newlineAndIndent\",\n    \"Ctrl-J\": \"newline\",\n    \"Tab\": \"indentAuto\",\n    \"Alt-G G\": \"gotoLine\",\n    \"Ctrl-X Tab\": \"indentRigidly\",\n    \"Ctrl-X Ctrl-X\": \"exchangePointAndMark\",\n    \"Ctrl-X Ctrl-S\": \"save\",\n    \"Ctrl-X Ctrl-W\": \"save\",\n    \"Ctrl-X S\": \"saveAll\",\n    \"Ctrl-X F\": \"open\",\n    \"Ctrl-X K\": \"close\",\n    \"Ctrl-X H\": \"selectAll\",\n    \"Ctrl-Q Tab\": \"quotedInsertTab\",\n    \"Ctrl-U\": \"universalArgument\",\n    \"fallthrough\": \"default\"\n  });\n\n  var prefixMap = {\"Ctrl-G\": clearPrefix};\n  function regPrefix(d) {\n    prefixMap[d] = function(cm) { addPrefix(cm, d); };\n    keyMap[\"Ctrl-\" + d] = function(cm) { addPrefix(cm, d); };\n    prefixPreservingKeys[\"Ctrl-\" + d] = true;\n  }\n  for (var i = 0; i < 10; ++i) regPrefix(String(i));\n  regPrefix(\"-\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/keymap/sublime.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// A rough approximation of Sublime Text's keybindings\n// Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../lib/codemirror\"), require(\"../addon/search/searchcursor\"), require(\"../addon/edit/matchbrackets\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../lib/codemirror\", \"../addon/search/searchcursor\", \"../addon/edit/matchbrackets\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var cmds = CodeMirror.commands;\n  var Pos = CodeMirror.Pos;\n\n  // This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.\n  function findPosSubword(doc, start, dir) {\n    if (dir < 0 && start.ch == 0) return doc.clipPos(Pos(start.line - 1));\n    var line = doc.getLine(start.line);\n    if (dir > 0 && start.ch >= line.length) return doc.clipPos(Pos(start.line + 1, 0));\n    var state = \"start\", type, startPos = start.ch;\n    for (var pos = startPos, e = dir < 0 ? 0 : line.length, i = 0; pos != e; pos += dir, i++) {\n      var next = line.charAt(dir < 0 ? pos - 1 : pos);\n      var cat = next != \"_\" && CodeMirror.isWordChar(next) ? \"w\" : \"o\";\n      if (cat == \"w\" && next.toUpperCase() == next) cat = \"W\";\n      if (state == \"start\") {\n        if (cat != \"o\") { state = \"in\"; type = cat; }\n        else startPos = pos + dir\n      } else if (state == \"in\") {\n        if (type != cat) {\n          if (type == \"w\" && cat == \"W\" && dir < 0) pos--;\n          if (type == \"W\" && cat == \"w\" && dir > 0) { // From uppercase to lowercase\n            if (pos == startPos + 1) { type = \"w\"; continue; }\n            else pos--;\n          }\n          break;\n        }\n      }\n    }\n    return Pos(start.line, pos);\n  }\n\n  function moveSubword(cm, dir) {\n    cm.extendSelectionsBy(function(range) {\n      if (cm.display.shift || cm.doc.extend || range.empty())\n        return findPosSubword(cm.doc, range.head, dir);\n      else\n        return dir < 0 ? range.from() : range.to();\n    });\n  }\n\n  cmds.goSubwordLeft = function(cm) { moveSubword(cm, -1); };\n  cmds.goSubwordRight = function(cm) { moveSubword(cm, 1); };\n\n  cmds.scrollLineUp = function(cm) {\n    var info = cm.getScrollInfo();\n    if (!cm.somethingSelected()) {\n      var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, \"local\");\n      if (cm.getCursor().line >= visibleBottomLine)\n        cm.execCommand(\"goLineUp\");\n    }\n    cm.scrollTo(null, info.top - cm.defaultTextHeight());\n  };\n  cmds.scrollLineDown = function(cm) {\n    var info = cm.getScrollInfo();\n    if (!cm.somethingSelected()) {\n      var visibleTopLine = cm.lineAtHeight(info.top, \"local\")+1;\n      if (cm.getCursor().line <= visibleTopLine)\n        cm.execCommand(\"goLineDown\");\n    }\n    cm.scrollTo(null, info.top + cm.defaultTextHeight());\n  };\n\n  cmds.splitSelectionByLine = function(cm) {\n    var ranges = cm.listSelections(), lineRanges = [];\n    for (var i = 0; i < ranges.length; i++) {\n      var from = ranges[i].from(), to = ranges[i].to();\n      for (var line = from.line; line <= to.line; ++line)\n        if (!(to.line > from.line && line == to.line && to.ch == 0))\n          lineRanges.push({anchor: line == from.line ? from : Pos(line, 0),\n                           head: line == to.line ? to : Pos(line)});\n    }\n    cm.setSelections(lineRanges, 0);\n  };\n\n  cmds.singleSelectionTop = function(cm) {\n    var range = cm.listSelections()[0];\n    cm.setSelection(range.anchor, range.head, {scroll: false});\n  };\n\n  cmds.selectLine = function(cm) {\n    var ranges = cm.listSelections(), extended = [];\n    for (var i = 0; i < ranges.length; i++) {\n      var range = ranges[i];\n      extended.push({anchor: Pos(range.from().line, 0),\n                     head: Pos(range.to().line + 1, 0)});\n    }\n    cm.setSelections(extended);\n  };\n\n  function insertLine(cm, above) {\n    if (cm.isReadOnly()) return CodeMirror.Pass\n    cm.operation(function() {\n      var len = cm.listSelections().length, newSelection = [], last = -1;\n      for (var i = 0; i < len; i++) {\n        var head = cm.listSelections()[i].head;\n        if (head.line <= last) continue;\n        var at = Pos(head.line + (above ? 0 : 1), 0);\n        cm.replaceRange(\"\\n\", at, null, \"+insertLine\");\n        cm.indentLine(at.line, null, true);\n        newSelection.push({head: at, anchor: at});\n        last = head.line + 1;\n      }\n      cm.setSelections(newSelection);\n    });\n    cm.execCommand(\"indentAuto\");\n  }\n\n  cmds.insertLineAfter = function(cm) { return insertLine(cm, false); };\n\n  cmds.insertLineBefore = function(cm) { return insertLine(cm, true); };\n\n  function wordAt(cm, pos) {\n    var start = pos.ch, end = start, line = cm.getLine(pos.line);\n    while (start && CodeMirror.isWordChar(line.charAt(start - 1))) --start;\n    while (end < line.length && CodeMirror.isWordChar(line.charAt(end))) ++end;\n    return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)};\n  }\n\n  cmds.selectNextOccurrence = function(cm) {\n    var from = cm.getCursor(\"from\"), to = cm.getCursor(\"to\");\n    var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel;\n    if (CodeMirror.cmpPos(from, to) == 0) {\n      var word = wordAt(cm, from);\n      if (!word.word) return;\n      cm.setSelection(word.from, word.to);\n      fullWord = true;\n    } else {\n      var text = cm.getRange(from, to);\n      var query = fullWord ? new RegExp(\"\\\\b\" + text + \"\\\\b\") : text;\n      var cur = cm.getSearchCursor(query, to);\n      var found = cur.findNext();\n      if (!found) {\n        cur = cm.getSearchCursor(query, Pos(cm.firstLine(), 0));\n        found = cur.findNext();\n      }\n      if (!found || isSelectedRange(cm.listSelections(), cur.from(), cur.to())) return\n      cm.addSelection(cur.from(), cur.to());\n    }\n    if (fullWord)\n      cm.state.sublimeFindFullWord = cm.doc.sel;\n  };\n\n  cmds.skipAndSelectNextOccurrence = function(cm) {\n    var prevAnchor = cm.getCursor(\"anchor\"), prevHead = cm.getCursor(\"head\");\n    cmds.selectNextOccurrence(cm);\n    if (CodeMirror.cmpPos(prevAnchor, prevHead) != 0) {\n      cm.doc.setSelections(cm.doc.listSelections()\n          .filter(function (sel) {\n            return sel.anchor != prevAnchor || sel.head != prevHead;\n          }));\n    }\n  }\n\n  function addCursorToSelection(cm, dir) {\n    var ranges = cm.listSelections(), newRanges = [];\n    for (var i = 0; i < ranges.length; i++) {\n      var range = ranges[i];\n      var newAnchor = cm.findPosV(\n          range.anchor, dir, \"line\", range.anchor.goalColumn);\n      var newHead = cm.findPosV(\n          range.head, dir, \"line\", range.head.goalColumn);\n      newAnchor.goalColumn = range.anchor.goalColumn != null ?\n          range.anchor.goalColumn : cm.cursorCoords(range.anchor, \"div\").left;\n      newHead.goalColumn = range.head.goalColumn != null ?\n          range.head.goalColumn : cm.cursorCoords(range.head, \"div\").left;\n      var newRange = {anchor: newAnchor, head: newHead};\n      newRanges.push(range);\n      newRanges.push(newRange);\n    }\n    cm.setSelections(newRanges);\n  }\n  cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };\n  cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };\n\n  function isSelectedRange(ranges, from, to) {\n    for (var i = 0; i < ranges.length; i++)\n      if (CodeMirror.cmpPos(ranges[i].from(), from) == 0 &&\n          CodeMirror.cmpPos(ranges[i].to(), to) == 0) return true\n    return false\n  }\n\n  var mirror = \"(){}[]\";\n  function selectBetweenBrackets(cm) {\n    var ranges = cm.listSelections(), newRanges = []\n    for (var i = 0; i < ranges.length; i++) {\n      var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);\n      if (!opening) return false;\n      for (;;) {\n        var closing = cm.scanForBracket(pos, 1);\n        if (!closing) return false;\n        if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {\n          var startPos = Pos(opening.pos.line, opening.pos.ch + 1);\n          if (CodeMirror.cmpPos(startPos, range.from()) == 0 &&\n              CodeMirror.cmpPos(closing.pos, range.to()) == 0) {\n            opening = cm.scanForBracket(opening.pos, -1);\n            if (!opening) return false;\n          } else {\n            newRanges.push({anchor: startPos, head: closing.pos});\n            break;\n          }\n        }\n        pos = Pos(closing.pos.line, closing.pos.ch + 1);\n      }\n    }\n    cm.setSelections(newRanges);\n    return true;\n  }\n\n  cmds.selectScope = function(cm) {\n    selectBetweenBrackets(cm) || cm.execCommand(\"selectAll\");\n  };\n  cmds.selectBetweenBrackets = function(cm) {\n    if (!selectBetweenBrackets(cm)) return CodeMirror.Pass;\n  };\n\n  function puncType(type) {\n    return !type ? null : /\\bpunctuation\\b/.test(type) ? type : undefined\n  }\n\n  cmds.goToBracket = function(cm) {\n    cm.extendSelectionsBy(function(range) {\n      var next = cm.scanForBracket(range.head, 1, puncType(cm.getTokenTypeAt(range.head)));\n      if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos;\n      var prev = cm.scanForBracket(range.head, -1, puncType(cm.getTokenTypeAt(Pos(range.head.line, range.head.ch + 1))));\n      return prev && Pos(prev.pos.line, prev.pos.ch + 1) || range.head;\n    });\n  };\n\n  cmds.swapLineUp = function(cm) {\n    if (cm.isReadOnly()) return CodeMirror.Pass\n    var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];\n    for (var i = 0; i < ranges.length; i++) {\n      var range = ranges[i], from = range.from().line - 1, to = range.to().line;\n      newSels.push({anchor: Pos(range.anchor.line - 1, range.anchor.ch),\n                    head: Pos(range.head.line - 1, range.head.ch)});\n      if (range.to().ch == 0 && !range.empty()) --to;\n      if (from > at) linesToMove.push(from, to);\n      else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;\n      at = to;\n    }\n    cm.operation(function() {\n      for (var i = 0; i < linesToMove.length; i += 2) {\n        var from = linesToMove[i], to = linesToMove[i + 1];\n        var line = cm.getLine(from);\n        cm.replaceRange(\"\", Pos(from, 0), Pos(from + 1, 0), \"+swapLine\");\n        if (to > cm.lastLine())\n          cm.replaceRange(\"\\n\" + line, Pos(cm.lastLine()), null, \"+swapLine\");\n        else\n          cm.replaceRange(line + \"\\n\", Pos(to, 0), null, \"+swapLine\");\n      }\n      cm.setSelections(newSels);\n      cm.scrollIntoView();\n    });\n  };\n\n  cmds.swapLineDown = function(cm) {\n    if (cm.isReadOnly()) return CodeMirror.Pass\n    var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;\n    for (var i = ranges.length - 1; i >= 0; i--) {\n      var range = ranges[i], from = range.to().line + 1, to = range.from().line;\n      if (range.to().ch == 0 && !range.empty()) from--;\n      if (from < at) linesToMove.push(from, to);\n      else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;\n      at = to;\n    }\n    cm.operation(function() {\n      for (var i = linesToMove.length - 2; i >= 0; i -= 2) {\n        var from = linesToMove[i], to = linesToMove[i + 1];\n        var line = cm.getLine(from);\n        if (from == cm.lastLine())\n          cm.replaceRange(\"\", Pos(from - 1), Pos(from), \"+swapLine\");\n        else\n          cm.replaceRange(\"\", Pos(from, 0), Pos(from + 1, 0), \"+swapLine\");\n        cm.replaceRange(line + \"\\n\", Pos(to, 0), null, \"+swapLine\");\n      }\n      cm.scrollIntoView();\n    });\n  };\n\n  cmds.toggleCommentIndented = function(cm) {\n    cm.toggleComment({ indent: true });\n  }\n\n  cmds.joinLines = function(cm) {\n    var ranges = cm.listSelections(), joined = [];\n    for (var i = 0; i < ranges.length; i++) {\n      var range = ranges[i], from = range.from();\n      var start = from.line, end = range.to().line;\n      while (i < ranges.length - 1 && ranges[i + 1].from().line == end)\n        end = ranges[++i].to().line;\n      joined.push({start: start, end: end, anchor: !range.empty() && from});\n    }\n    cm.operation(function() {\n      var offset = 0, ranges = [];\n      for (var i = 0; i < joined.length; i++) {\n        var obj = joined[i];\n        var anchor = obj.anchor && Pos(obj.anchor.line - offset, obj.anchor.ch), head;\n        for (var line = obj.start; line <= obj.end; line++) {\n          var actual = line - offset;\n          if (line == obj.end) head = Pos(actual, cm.getLine(actual).length + 1);\n          if (actual < cm.lastLine()) {\n            cm.replaceRange(\" \", Pos(actual), Pos(actual + 1, /^\\s*/.exec(cm.getLine(actual + 1))[0].length));\n            ++offset;\n          }\n        }\n        ranges.push({anchor: anchor || head, head: head});\n      }\n      cm.setSelections(ranges, 0);\n    });\n  };\n\n  cmds.duplicateLine = function(cm) {\n    cm.operation(function() {\n      var rangeCount = cm.listSelections().length;\n      for (var i = 0; i < rangeCount; i++) {\n        var range = cm.listSelections()[i];\n        if (range.empty())\n          cm.replaceRange(cm.getLine(range.head.line) + \"\\n\", Pos(range.head.line, 0));\n        else\n          cm.replaceRange(cm.getRange(range.from(), range.to()), range.from());\n      }\n      cm.scrollIntoView();\n    });\n  };\n\n\n  function sortLines(cm, caseSensitive, direction) {\n    if (cm.isReadOnly()) return CodeMirror.Pass\n    var ranges = cm.listSelections(), toSort = [], selected;\n    for (var i = 0; i < ranges.length; i++) {\n      var range = ranges[i];\n      if (range.empty()) continue;\n      var from = range.from().line, to = range.to().line;\n      while (i < ranges.length - 1 && ranges[i + 1].from().line == to)\n        to = ranges[++i].to().line;\n      if (!ranges[i].to().ch) to--;\n      toSort.push(from, to);\n    }\n    if (toSort.length) selected = true;\n    else toSort.push(cm.firstLine(), cm.lastLine());\n\n    cm.operation(function() {\n      var ranges = [];\n      for (var i = 0; i < toSort.length; i += 2) {\n        var from = toSort[i], to = toSort[i + 1];\n        var start = Pos(from, 0), end = Pos(to);\n        var lines = cm.getRange(start, end, false);\n        if (caseSensitive)\n          lines.sort(function(a, b) { return a < b ? -direction : a == b ? 0 : direction; });\n        else\n          lines.sort(function(a, b) {\n            var au = a.toUpperCase(), bu = b.toUpperCase();\n            if (au != bu) { a = au; b = bu; }\n            return a < b ? -direction : a == b ? 0 : direction;\n          });\n        cm.replaceRange(lines, start, end);\n        if (selected) ranges.push({anchor: start, head: Pos(to + 1, 0)});\n      }\n      if (selected) cm.setSelections(ranges, 0);\n    });\n  }\n\n  cmds.sortLines = function(cm) { sortLines(cm, true, 1); };\n  cmds.reverseSortLines = function(cm) { sortLines(cm, true, -1); };\n  cmds.sortLinesInsensitive = function(cm) { sortLines(cm, false, 1); };\n  cmds.reverseSortLinesInsensitive = function(cm) { sortLines(cm, false, -1); };\n\n  cmds.nextBookmark = function(cm) {\n    var marks = cm.state.sublimeBookmarks;\n    if (marks) while (marks.length) {\n      var current = marks.shift();\n      var found = current.find();\n      if (found) {\n        marks.push(current);\n        return cm.setSelection(found.from, found.to);\n      }\n    }\n  };\n\n  cmds.prevBookmark = function(cm) {\n    var marks = cm.state.sublimeBookmarks;\n    if (marks) while (marks.length) {\n      marks.unshift(marks.pop());\n      var found = marks[marks.length - 1].find();\n      if (!found)\n        marks.pop();\n      else\n        return cm.setSelection(found.from, found.to);\n    }\n  };\n\n  cmds.toggleBookmark = function(cm) {\n    var ranges = cm.listSelections();\n    var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);\n    for (var i = 0; i < ranges.length; i++) {\n      var from = ranges[i].from(), to = ranges[i].to();\n      var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to);\n      for (var j = 0; j < found.length; j++) {\n        if (found[j].sublimeBookmark) {\n          found[j].clear();\n          for (var k = 0; k < marks.length; k++)\n            if (marks[k] == found[j])\n              marks.splice(k--, 1);\n          break;\n        }\n      }\n      if (j == found.length)\n        marks.push(cm.markText(from, to, {sublimeBookmark: true, clearWhenEmpty: false}));\n    }\n  };\n\n  cmds.clearBookmarks = function(cm) {\n    var marks = cm.state.sublimeBookmarks;\n    if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();\n    marks.length = 0;\n  };\n\n  cmds.selectBookmarks = function(cm) {\n    var marks = cm.state.sublimeBookmarks, ranges = [];\n    if (marks) for (var i = 0; i < marks.length; i++) {\n      var found = marks[i].find();\n      if (!found)\n        marks.splice(i--, 0);\n      else\n        ranges.push({anchor: found.from, head: found.to});\n    }\n    if (ranges.length)\n      cm.setSelections(ranges, 0);\n  };\n\n  function modifyWordOrSelection(cm, mod) {\n    cm.operation(function() {\n      var ranges = cm.listSelections(), indices = [], replacements = [];\n      for (var i = 0; i < ranges.length; i++) {\n        var range = ranges[i];\n        if (range.empty()) { indices.push(i); replacements.push(\"\"); }\n        else replacements.push(mod(cm.getRange(range.from(), range.to())));\n      }\n      cm.replaceSelections(replacements, \"around\", \"case\");\n      for (var i = indices.length - 1, at; i >= 0; i--) {\n        var range = ranges[indices[i]];\n        if (at && CodeMirror.cmpPos(range.head, at) > 0) continue;\n        var word = wordAt(cm, range.head);\n        at = word.from;\n        cm.replaceRange(mod(word.word), word.from, word.to);\n      }\n    });\n  }\n\n  cmds.smartBackspace = function(cm) {\n    if (cm.somethingSelected()) return CodeMirror.Pass;\n\n    cm.operation(function() {\n      var cursors = cm.listSelections();\n      var indentUnit = cm.getOption(\"indentUnit\");\n\n      for (var i = cursors.length - 1; i >= 0; i--) {\n        var cursor = cursors[i].head;\n        var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor);\n        var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption(\"tabSize\"));\n\n        // Delete by one character by default\n        var deletePos = cm.findPosH(cursor, -1, \"char\", false);\n\n        if (toStartOfLine && !/\\S/.test(toStartOfLine) && column % indentUnit == 0) {\n          var prevIndent = new Pos(cursor.line,\n            CodeMirror.findColumn(toStartOfLine, column - indentUnit, indentUnit));\n\n          // Smart delete only if we found a valid prevIndent location\n          if (prevIndent.ch != cursor.ch) deletePos = prevIndent;\n        }\n\n        cm.replaceRange(\"\", deletePos, cursor, \"+delete\");\n      }\n    });\n  };\n\n  cmds.delLineRight = function(cm) {\n    cm.operation(function() {\n      var ranges = cm.listSelections();\n      for (var i = ranges.length - 1; i >= 0; i--)\n        cm.replaceRange(\"\", ranges[i].anchor, Pos(ranges[i].to().line), \"+delete\");\n      cm.scrollIntoView();\n    });\n  };\n\n  cmds.upcaseAtCursor = function(cm) {\n    modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });\n  };\n  cmds.downcaseAtCursor = function(cm) {\n    modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });\n  };\n\n  cmds.setSublimeMark = function(cm) {\n    if (cm.state.sublimeMark) cm.state.sublimeMark.clear();\n    cm.state.sublimeMark = cm.setBookmark(cm.getCursor());\n  };\n  cmds.selectToSublimeMark = function(cm) {\n    var found = cm.state.sublimeMark && cm.state.sublimeMark.find();\n    if (found) cm.setSelection(cm.getCursor(), found);\n  };\n  cmds.deleteToSublimeMark = function(cm) {\n    var found = cm.state.sublimeMark && cm.state.sublimeMark.find();\n    if (found) {\n      var from = cm.getCursor(), to = found;\n      if (CodeMirror.cmpPos(from, to) > 0) { var tmp = to; to = from; from = tmp; }\n      cm.state.sublimeKilled = cm.getRange(from, to);\n      cm.replaceRange(\"\", from, to);\n    }\n  };\n  cmds.swapWithSublimeMark = function(cm) {\n    var found = cm.state.sublimeMark && cm.state.sublimeMark.find();\n    if (found) {\n      cm.state.sublimeMark.clear();\n      cm.state.sublimeMark = cm.setBookmark(cm.getCursor());\n      cm.setCursor(found);\n    }\n  };\n  cmds.sublimeYank = function(cm) {\n    if (cm.state.sublimeKilled != null)\n      cm.replaceSelection(cm.state.sublimeKilled, null, \"paste\");\n  };\n\n  cmds.showInCenter = function(cm) {\n    var pos = cm.cursorCoords(null, \"local\");\n    cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);\n  };\n\n  function getTarget(cm) {\n    var from = cm.getCursor(\"from\"), to = cm.getCursor(\"to\");\n    if (CodeMirror.cmpPos(from, to) == 0) {\n      var word = wordAt(cm, from);\n      if (!word.word) return;\n      from = word.from;\n      to = word.to;\n    }\n    return {from: from, to: to, query: cm.getRange(from, to), word: word};\n  }\n\n  function findAndGoTo(cm, forward) {\n    var target = getTarget(cm);\n    if (!target) return;\n    var query = target.query;\n    var cur = cm.getSearchCursor(query, forward ? target.to : target.from);\n\n    if (forward ? cur.findNext() : cur.findPrevious()) {\n      cm.setSelection(cur.from(), cur.to());\n    } else {\n      cur = cm.getSearchCursor(query, forward ? Pos(cm.firstLine(), 0)\n                                              : cm.clipPos(Pos(cm.lastLine())));\n      if (forward ? cur.findNext() : cur.findPrevious())\n        cm.setSelection(cur.from(), cur.to());\n      else if (target.word)\n        cm.setSelection(target.from, target.to);\n    }\n  };\n  cmds.findUnder = function(cm) { findAndGoTo(cm, true); };\n  cmds.findUnderPrevious = function(cm) { findAndGoTo(cm,false); };\n  cmds.findAllUnder = function(cm) {\n    var target = getTarget(cm);\n    if (!target) return;\n    var cur = cm.getSearchCursor(target.query);\n    var matches = [];\n    var primaryIndex = -1;\n    while (cur.findNext()) {\n      matches.push({anchor: cur.from(), head: cur.to()});\n      if (cur.from().line <= target.from.line && cur.from().ch <= target.from.ch)\n        primaryIndex++;\n    }\n    cm.setSelections(matches, primaryIndex);\n  };\n\n\n  var keyMap = CodeMirror.keyMap;\n  keyMap.macSublime = {\n    \"Cmd-Left\": \"goLineStartSmart\",\n    \"Shift-Tab\": \"indentLess\",\n    \"Shift-Ctrl-K\": \"deleteLine\",\n    \"Alt-Q\": \"wrapLines\",\n    \"Ctrl-Left\": \"goSubwordLeft\",\n    \"Ctrl-Right\": \"goSubwordRight\",\n    \"Ctrl-Alt-Up\": \"scrollLineUp\",\n    \"Ctrl-Alt-Down\": \"scrollLineDown\",\n    \"Cmd-L\": \"selectLine\",\n    \"Shift-Cmd-L\": \"splitSelectionByLine\",\n    \"Esc\": \"singleSelectionTop\",\n    \"Cmd-Enter\": \"insertLineAfter\",\n    \"Shift-Cmd-Enter\": \"insertLineBefore\",\n    \"Cmd-D\": \"selectNextOccurrence\",\n    \"Shift-Cmd-Space\": \"selectScope\",\n    \"Shift-Cmd-M\": \"selectBetweenBrackets\",\n    \"Cmd-M\": \"goToBracket\",\n    \"Cmd-Ctrl-Up\": \"swapLineUp\",\n    \"Cmd-Ctrl-Down\": \"swapLineDown\",\n    \"Cmd-/\": \"toggleCommentIndented\",\n    \"Cmd-J\": \"joinLines\",\n    \"Shift-Cmd-D\": \"duplicateLine\",\n    \"F5\": \"sortLines\",\n    \"Shift-F5\": \"reverseSortLines\",\n    \"Cmd-F5\": \"sortLinesInsensitive\",\n    \"Shift-Cmd-F5\": \"reverseSortLinesInsensitive\",\n    \"F2\": \"nextBookmark\",\n    \"Shift-F2\": \"prevBookmark\",\n    \"Cmd-F2\": \"toggleBookmark\",\n    \"Shift-Cmd-F2\": \"clearBookmarks\",\n    \"Alt-F2\": \"selectBookmarks\",\n    \"Backspace\": \"smartBackspace\",\n    \"Cmd-K Cmd-D\": \"skipAndSelectNextOccurrence\",\n    \"Cmd-K Cmd-K\": \"delLineRight\",\n    \"Cmd-K Cmd-U\": \"upcaseAtCursor\",\n    \"Cmd-K Cmd-L\": \"downcaseAtCursor\",\n    \"Cmd-K Cmd-Space\": \"setSublimeMark\",\n    \"Cmd-K Cmd-A\": \"selectToSublimeMark\",\n    \"Cmd-K Cmd-W\": \"deleteToSublimeMark\",\n    \"Cmd-K Cmd-X\": \"swapWithSublimeMark\",\n    \"Cmd-K Cmd-Y\": \"sublimeYank\",\n    \"Cmd-K Cmd-C\": \"showInCenter\",\n    \"Cmd-K Cmd-G\": \"clearBookmarks\",\n    \"Cmd-K Cmd-Backspace\": \"delLineLeft\",\n    \"Cmd-K Cmd-1\": \"foldAll\",\n    \"Cmd-K Cmd-0\": \"unfoldAll\",\n    \"Cmd-K Cmd-J\": \"unfoldAll\",\n    \"Ctrl-Shift-Up\": \"addCursorToPrevLine\",\n    \"Ctrl-Shift-Down\": \"addCursorToNextLine\",\n    \"Cmd-F3\": \"findUnder\",\n    \"Shift-Cmd-F3\": \"findUnderPrevious\",\n    \"Alt-F3\": \"findAllUnder\",\n    \"Shift-Cmd-[\": \"fold\",\n    \"Shift-Cmd-]\": \"unfold\",\n    \"Cmd-I\": \"findIncremental\",\n    \"Shift-Cmd-I\": \"findIncrementalReverse\",\n    \"Cmd-H\": \"replace\",\n    \"F3\": \"findNext\",\n    \"Shift-F3\": \"findPrev\",\n    \"fallthrough\": \"macDefault\"\n  };\n  CodeMirror.normalizeKeyMap(keyMap.macSublime);\n\n  keyMap.pcSublime = {\n    \"Shift-Tab\": \"indentLess\",\n    \"Shift-Ctrl-K\": \"deleteLine\",\n    \"Alt-Q\": \"wrapLines\",\n    \"Ctrl-T\": \"transposeChars\",\n    \"Alt-Left\": \"goSubwordLeft\",\n    \"Alt-Right\": \"goSubwordRight\",\n    \"Ctrl-Up\": \"scrollLineUp\",\n    \"Ctrl-Down\": \"scrollLineDown\",\n    \"Ctrl-L\": \"selectLine\",\n    \"Shift-Ctrl-L\": \"splitSelectionByLine\",\n    \"Esc\": \"singleSelectionTop\",\n    \"Ctrl-Enter\": \"insertLineAfter\",\n    \"Shift-Ctrl-Enter\": \"insertLineBefore\",\n    \"Ctrl-D\": \"selectNextOccurrence\",\n    \"Shift-Ctrl-Space\": \"selectScope\",\n    \"Shift-Ctrl-M\": \"selectBetweenBrackets\",\n    \"Ctrl-M\": \"goToBracket\",\n    \"Shift-Ctrl-Up\": \"swapLineUp\",\n    \"Shift-Ctrl-Down\": \"swapLineDown\",\n    \"Ctrl-/\": \"toggleCommentIndented\",\n    \"Ctrl-J\": \"joinLines\",\n    \"Shift-Ctrl-D\": \"duplicateLine\",\n    \"F9\": \"sortLines\",\n    \"Shift-F9\": \"reverseSortLines\",\n    \"Ctrl-F9\": \"sortLinesInsensitive\",\n    \"Shift-Ctrl-F9\": \"reverseSortLinesInsensitive\",\n    \"F2\": \"nextBookmark\",\n    \"Shift-F2\": \"prevBookmark\",\n    \"Ctrl-F2\": \"toggleBookmark\",\n    \"Shift-Ctrl-F2\": \"clearBookmarks\",\n    \"Alt-F2\": \"selectBookmarks\",\n    \"Backspace\": \"smartBackspace\",\n    \"Ctrl-K Ctrl-D\": \"skipAndSelectNextOccurrence\",\n    \"Ctrl-K Ctrl-K\": \"delLineRight\",\n    \"Ctrl-K Ctrl-U\": \"upcaseAtCursor\",\n    \"Ctrl-K Ctrl-L\": \"downcaseAtCursor\",\n    \"Ctrl-K Ctrl-Space\": \"setSublimeMark\",\n    \"Ctrl-K Ctrl-A\": \"selectToSublimeMark\",\n    \"Ctrl-K Ctrl-W\": \"deleteToSublimeMark\",\n    \"Ctrl-K Ctrl-X\": \"swapWithSublimeMark\",\n    \"Ctrl-K Ctrl-Y\": \"sublimeYank\",\n    \"Ctrl-K Ctrl-C\": \"showInCenter\",\n    \"Ctrl-K Ctrl-G\": \"clearBookmarks\",\n    \"Ctrl-K Ctrl-Backspace\": \"delLineLeft\",\n    \"Ctrl-K Ctrl-1\": \"foldAll\",\n    \"Ctrl-K Ctrl-0\": \"unfoldAll\",\n    \"Ctrl-K Ctrl-J\": \"unfoldAll\",\n    \"Ctrl-Alt-Up\": \"addCursorToPrevLine\",\n    \"Ctrl-Alt-Down\": \"addCursorToNextLine\",\n    \"Ctrl-F3\": \"findUnder\",\n    \"Shift-Ctrl-F3\": \"findUnderPrevious\",\n    \"Alt-F3\": \"findAllUnder\",\n    \"Shift-Ctrl-[\": \"fold\",\n    \"Shift-Ctrl-]\": \"unfold\",\n    \"Ctrl-I\": \"findIncremental\",\n    \"Shift-Ctrl-I\": \"findIncrementalReverse\",\n    \"Ctrl-H\": \"replace\",\n    \"F3\": \"findNext\",\n    \"Shift-F3\": \"findPrev\",\n    \"fallthrough\": \"pcDefault\"\n  };\n  CodeMirror.normalizeKeyMap(keyMap.pcSublime);\n\n  var mac = keyMap.default == keyMap.macDefault;\n  keyMap.sublime = mac ? keyMap.macSublime : keyMap.pcSublime;\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/keymap/vim.js",
    "content": "(function(mod) {\n    if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n      mod(require(\"../lib/codemirror\"), require(\"../addon/search/searchcursor\"), require(\"../addon/dialog/dialog\"), require(\"../addon/edit/matchbrackets.js\"));\n    else if (typeof define == \"function\" && define.amd) // AMD\n      define([\"../lib/codemirror\", \"../addon/search/searchcursor\", \"../addon/dialog/dialog\", \"../addon/edit/matchbrackets\"], mod);\n    else // Plain browser env\n      mod(CodeMirror);\n  })(function(CodeMirror) {\n    'use strict';\n  // CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/**\n * Supported keybindings:\n *   Too many to list. Refer to defaultKeymap below.\n *\n * Supported Ex commands:\n *   Refer to defaultExCommandMap below.\n *\n * Registers: unnamed, -, ., :, /, _, a-z, A-Z, 0-9\n *   (Does not respect the special case for number registers when delete\n *    operator is made with these commands: %, (, ),  , /, ?, n, N, {, } )\n *   TODO: Implement the remaining registers.\n *\n * Marks: a-z, A-Z, and 0-9\n *   TODO: Implement the remaining special marks. They have more complex\n *       behavior.\n *\n * Events:\n *  'vim-mode-change' - raised on the editor anytime the current mode changes,\n *                      Event object: {mode: \"visual\", subMode: \"linewise\"}\n *\n * Code structure:\n *  1. Default keymap\n *  2. Variable declarations and short basic helpers\n *  3. Instance (External API) implementation\n *  4. Internal state tracking objects (input state, counter) implementation\n *     and instantiation\n *  5. Key handler (the main command dispatcher) implementation\n *  6. Motion, operator, and action implementations\n *  7. Helper functions for the key handler, motions, operators, and actions\n *  8. Set up Vim to work as a keymap for CodeMirror.\n *  9. Ex command implementations.\n */\n\nfunction initVim$1(CodeMirror) {\n\n  var Pos = CodeMirror.Pos;\n\n  function transformCursor(cm, range) {\n    var vim = cm.state.vim;\n    if (!vim || vim.insertMode) return range.head;\n    var head = vim.sel.head;\n    if (!head)  return range.head;\n\n    if (vim.visualBlock) {\n      if (range.head.line != head.line) {\n        return;\n      }\n    }\n    if (range.from() == range.anchor && !range.empty()) {\n      if (range.head.line == head.line && range.head.ch != head.ch)\n        return new Pos(range.head.line, range.head.ch - 1);\n    }\n\n    return range.head;\n  }\n\n  var defaultKeymap = [\n    // Key to key mapping. This goes first to make it possible to override\n    // existing mappings.\n    { keys: '<Left>', type: 'keyToKey', toKeys: 'h' },\n    { keys: '<Right>', type: 'keyToKey', toKeys: 'l' },\n    { keys: '<Up>', type: 'keyToKey', toKeys: 'k' },\n    { keys: '<Down>', type: 'keyToKey', toKeys: 'j' },\n    { keys: 'g<Up>', type: 'keyToKey', toKeys: 'gk' },\n    { keys: 'g<Down>', type: 'keyToKey', toKeys: 'gj' },\n    { keys: '<Space>', type: 'keyToKey', toKeys: 'l' },\n    { keys: '<BS>', type: 'keyToKey', toKeys: 'h', context: 'normal'},\n    { keys: '<Del>', type: 'keyToKey', toKeys: 'x', context: 'normal'},\n    { keys: '<C-Space>', type: 'keyToKey', toKeys: 'W' },\n    { keys: '<C-BS>', type: 'keyToKey', toKeys: 'B', context: 'normal' },\n    { keys: '<S-Space>', type: 'keyToKey', toKeys: 'w' },\n    { keys: '<S-BS>', type: 'keyToKey', toKeys: 'b', context: 'normal' },\n    { keys: '<C-n>', type: 'keyToKey', toKeys: 'j' },\n    { keys: '<C-p>', type: 'keyToKey', toKeys: 'k' },\n    { keys: '<C-[>', type: 'keyToKey', toKeys: '<Esc>' },\n    { keys: '<C-c>', type: 'keyToKey', toKeys: '<Esc>' },\n    { keys: '<C-[>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },\n    { keys: '<C-c>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },\n    { keys: '<C-Esc>', type: 'keyToKey', toKeys: '<Esc>' }, // ipad keyboard sends C-Esc instead of C-[\n    { keys: '<C-Esc>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },\n    { keys: 's', type: 'keyToKey', toKeys: 'cl', context: 'normal' },\n    { keys: 's', type: 'keyToKey', toKeys: 'c', context: 'visual'},\n    { keys: 'S', type: 'keyToKey', toKeys: 'cc', context: 'normal' },\n    { keys: 'S', type: 'keyToKey', toKeys: 'VdO', context: 'visual' },\n    { keys: '<Home>', type: 'keyToKey', toKeys: '0' },\n    { keys: '<End>', type: 'keyToKey', toKeys: '$' },\n    { keys: '<PageUp>', type: 'keyToKey', toKeys: '<C-b>' },\n    { keys: '<PageDown>', type: 'keyToKey', toKeys: '<C-f>' },\n    { keys: '<CR>', type: 'keyToKey', toKeys: 'j^', context: 'normal' },\n    { keys: '<Ins>', type: 'keyToKey', toKeys: 'i', context: 'normal'},\n    { keys: '<Ins>', type: 'action', action: 'toggleOverwrite', context: 'insert' },\n    // Motions\n    { keys: 'H', type: 'motion', motion: 'moveToTopLine', motionArgs: { linewise: true, toJumplist: true }},\n    { keys: 'M', type: 'motion', motion: 'moveToMiddleLine', motionArgs: { linewise: true, toJumplist: true }},\n    { keys: 'L', type: 'motion', motion: 'moveToBottomLine', motionArgs: { linewise: true, toJumplist: true }},\n    { keys: 'h', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: false }},\n    { keys: 'l', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: true }},\n    { keys: 'j', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, linewise: true }},\n    { keys: 'k', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, linewise: true }},\n    { keys: 'gj', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: true }},\n    { keys: 'gk', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: false }},\n    { keys: 'w', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false }},\n    { keys: 'W', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false, bigWord: true }},\n    { keys: 'e', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, inclusive: true }},\n    { keys: 'E', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, bigWord: true, inclusive: true }},\n    { keys: 'b', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }},\n    { keys: 'B', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false, bigWord: true }},\n    { keys: 'ge', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, inclusive: true }},\n    { keys: 'gE', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, bigWord: true, inclusive: true }},\n    { keys: '{', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: false, toJumplist: true }},\n    { keys: '}', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: true, toJumplist: true }},\n    { keys: '(', type: 'motion', motion: 'moveBySentence', motionArgs: { forward: false }},\n    { keys: ')', type: 'motion', motion: 'moveBySentence', motionArgs: { forward: true }},\n    { keys: '<C-f>', type: 'motion', motion: 'moveByPage', motionArgs: { forward: true }},\n    { keys: '<C-b>', type: 'motion', motion: 'moveByPage', motionArgs: { forward: false }},\n    { keys: '<C-d>', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: true, explicitRepeat: true }},\n    { keys: '<C-u>', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: false, explicitRepeat: true }},\n    { keys: 'gg', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, linewise: true, toJumplist: true }},\n    { keys: 'G', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: true, explicitRepeat: true, linewise: true, toJumplist: true }},\n    {keys: \"g$\", type: \"motion\", motion: \"moveToEndOfDisplayLine\"},\n    {keys: \"g^\", type: \"motion\", motion: \"moveToStartOfDisplayLine\"},\n    {keys: \"g0\", type: \"motion\", motion: \"moveToStartOfDisplayLine\"},\n    { keys: '0', type: 'motion', motion: 'moveToStartOfLine' },\n    { keys: '^', type: 'motion', motion: 'moveToFirstNonWhiteSpaceCharacter' },\n    { keys: '+', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar:true }},\n    { keys: '-', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, toFirstChar:true }},\n    { keys: '_', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar:true, repeatOffset:-1 }},\n    { keys: '$', type: 'motion', motion: 'moveToEol', motionArgs: { inclusive: true }},\n    { keys: '%', type: 'motion', motion: 'moveToMatchedSymbol', motionArgs: { inclusive: true, toJumplist: true }},\n    { keys: 'f<character>', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: true , inclusive: true }},\n    { keys: 'F<character>', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: false }},\n    { keys: 't<character>', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: true, inclusive: true }},\n    { keys: 'T<character>', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: false }},\n    { keys: ';', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: true }},\n    { keys: ',', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: false }},\n    { keys: '\\'<character>', type: 'motion', motion: 'goToMark', motionArgs: {toJumplist: true, linewise: true}},\n    { keys: '`<character>', type: 'motion', motion: 'goToMark', motionArgs: {toJumplist: true}},\n    { keys: ']`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },\n    { keys: '[`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } },\n    { keys: ']\\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } },\n    { keys: '[\\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } },\n    // the next two aren't motions but must come before more general motion declarations\n    { keys: ']p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true, matchIndent: true}},\n    { keys: '[p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true, matchIndent: true}},\n    { keys: ']<character>', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: true, toJumplist: true}},\n    { keys: '[<character>', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: false, toJumplist: true}},\n    { keys: '|', type: 'motion', motion: 'moveToColumn'},\n    { keys: 'o', type: 'motion', motion: 'moveToOtherHighlightedEnd', context:'visual'},\n    { keys: 'O', type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: {sameLine: true}, context:'visual'},\n    // Operators\n    { keys: 'd', type: 'operator', operator: 'delete' },\n    { keys: 'y', type: 'operator', operator: 'yank' },\n    { keys: 'c', type: 'operator', operator: 'change' },\n    { keys: '=', type: 'operator', operator: 'indentAuto' },\n    { keys: '>', type: 'operator', operator: 'indent', operatorArgs: { indentRight: true }},\n    { keys: '<', type: 'operator', operator: 'indent', operatorArgs: { indentRight: false }},\n    { keys: 'g~', type: 'operator', operator: 'changeCase' },\n    { keys: 'gu', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, isEdit: true },\n    { keys: 'gU', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, isEdit: true },\n    { keys: 'n', type: 'motion', motion: 'findNext', motionArgs: { forward: true, toJumplist: true }},\n    { keys: 'N', type: 'motion', motion: 'findNext', motionArgs: { forward: false, toJumplist: true }},\n    { keys: 'gn', type: 'motion', motion: 'findAndSelectNextInclusive', motionArgs: { forward: true }},\n    { keys: 'gN', type: 'motion', motion: 'findAndSelectNextInclusive', motionArgs: { forward: false }},\n    // Operator-Motion dual commands\n    { keys: 'x', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorMotionArgs: { visualLine: false }},\n    { keys: 'X', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: false }, operatorMotionArgs: { visualLine: true }},\n    { keys: 'D', type: 'operatorMotion', operator: 'delete', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},\n    { keys: 'D', type: 'operator', operator: 'delete', operatorArgs: { linewise: true }, context: 'visual'},\n    { keys: 'Y', type: 'operatorMotion', operator: 'yank', motion: 'expandToLine', motionArgs: { linewise: true }, context: 'normal'},\n    { keys: 'Y', type: 'operator', operator: 'yank', operatorArgs: { linewise: true }, context: 'visual'},\n    { keys: 'C', type: 'operatorMotion', operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},\n    { keys: 'C', type: 'operator', operator: 'change', operatorArgs: { linewise: true }, context: 'visual'},\n    { keys: '~', type: 'operatorMotion', operator: 'changeCase', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorArgs: { shouldMoveCursor: true }, context: 'normal'},\n    { keys: '~', type: 'operator', operator: 'changeCase', context: 'visual'},\n    { keys: '<C-u>', type: 'operatorMotion', operator: 'delete', motion: 'moveToStartOfLine', context: 'insert' },\n    { keys: '<C-w>', type: 'operatorMotion', operator: 'delete', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }, context: 'insert' },\n    //ignore C-w in normal mode\n    { keys: '<C-w>', type: 'idle', context: 'normal' },\n    // Actions\n    { keys: '<C-i>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: true }},\n    { keys: '<C-o>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: false }},\n    { keys: '<C-e>', type: 'action', action: 'scroll', actionArgs: { forward: true, linewise: true }},\n    { keys: '<C-y>', type: 'action', action: 'scroll', actionArgs: { forward: false, linewise: true }},\n    { keys: 'a', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'charAfter' }, context: 'normal' },\n    { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'eol' }, context: 'normal' },\n    { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'endOfSelectedArea' }, context: 'visual' },\n    { keys: 'i', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'inplace' }, context: 'normal' },\n    { keys: 'gi', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'lastEdit' }, context: 'normal' },\n    { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'firstNonBlank'}, context: 'normal' },\n    { keys: 'gI', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'bol'}, context: 'normal' },\n    { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'startOfSelectedArea' }, context: 'visual' },\n    { keys: 'o', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: true }, context: 'normal' },\n    { keys: 'O', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: false }, context: 'normal' },\n    { keys: 'v', type: 'action', action: 'toggleVisualMode' },\n    { keys: 'V', type: 'action', action: 'toggleVisualMode', actionArgs: { linewise: true }},\n    { keys: '<C-v>', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true }},\n    { keys: '<C-q>', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true }},\n    { keys: 'gv', type: 'action', action: 'reselectLastSelection' },\n    { keys: 'J', type: 'action', action: 'joinLines', isEdit: true },\n    { keys: 'gJ', type: 'action', action: 'joinLines', actionArgs: { keepSpaces: true }, isEdit: true },\n    { keys: 'p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true }},\n    { keys: 'P', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true }},\n    { keys: 'r<character>', type: 'action', action: 'replace', isEdit: true },\n    { keys: '@<character>', type: 'action', action: 'replayMacro' },\n    { keys: 'q<character>', type: 'action', action: 'enterMacroRecordMode' },\n    // Handle Replace-mode as a special case of insert mode.\n    { keys: 'R', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { replace: true }, context: 'normal'},\n    { keys: 'R', type: 'operator', operator: 'change', operatorArgs: { linewise: true, fullLine: true }, context: 'visual', exitVisualBlock: true},\n    { keys: 'u', type: 'action', action: 'undo', context: 'normal' },\n    { keys: 'u', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, context: 'visual', isEdit: true },\n    { keys: 'U', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, context: 'visual', isEdit: true },\n    { keys: '<C-r>', type: 'action', action: 'redo' },\n    { keys: 'm<character>', type: 'action', action: 'setMark' },\n    { keys: '\"<character>', type: 'action', action: 'setRegister' },\n    { keys: 'zz', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }},\n    { keys: 'z.', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },\n    { keys: 'zt', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }},\n    { keys: 'z<CR>', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },\n    { keys: 'zb', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }},\n    { keys: 'z-', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },\n    { keys: '.', type: 'action', action: 'repeatLastEdit' },\n    { keys: '<C-a>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: true, backtrack: false}},\n    { keys: '<C-x>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: false, backtrack: false}},\n    { keys: '<C-t>', type: 'action', action: 'indent', actionArgs: { indentRight: true }, context: 'insert' },\n    { keys: '<C-d>', type: 'action', action: 'indent', actionArgs: { indentRight: false }, context: 'insert' },\n    // Text object motions\n    { keys: 'a<character>', type: 'motion', motion: 'textObjectManipulation' },\n    { keys: 'i<character>', type: 'motion', motion: 'textObjectManipulation', motionArgs: { textObjectInner: true }},\n    // Search\n    { keys: '/', type: 'search', searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true }},\n    { keys: '?', type: 'search', searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true }},\n    { keys: '*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},\n    { keys: '#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},\n    { keys: 'g*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true }},\n    { keys: 'g#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true }},\n    // Ex command\n    { keys: ':', type: 'ex' }\n  ];\n  var defaultKeymapLength = defaultKeymap.length;\n\n  /**\n   * Ex commands\n   * Care must be taken when adding to the default Ex command map. For any\n   * pair of commands that have a shared prefix, at least one of their\n   * shortNames must not match the prefix of the other command.\n   */\n  var defaultExCommandMap = [\n    { name: 'colorscheme', shortName: 'colo' },\n    { name: 'map' },\n    { name: 'imap', shortName: 'im' },\n    { name: 'nmap', shortName: 'nm' },\n    { name: 'vmap', shortName: 'vm' },\n    { name: 'unmap' },\n    { name: 'write', shortName: 'w' },\n    { name: 'undo', shortName: 'u' },\n    { name: 'redo', shortName: 'red' },\n    { name: 'set', shortName: 'se' },\n    { name: 'setlocal', shortName: 'setl' },\n    { name: 'setglobal', shortName: 'setg' },\n    { name: 'sort', shortName: 'sor' },\n    { name: 'substitute', shortName: 's', possiblyAsync: true },\n    { name: 'nohlsearch', shortName: 'noh' },\n    { name: 'yank', shortName: 'y' },\n    { name: 'delmarks', shortName: 'delm' },\n    { name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },\n    { name: 'vglobal', shortName: 'v' },\n    { name: 'global', shortName: 'g' }\n  ];\n\n    function enterVimMode(cm) {\n      cm.setOption('disableInput', true);\n      cm.setOption('showCursorWhenSelecting', false);\n      CodeMirror.signal(cm, \"vim-mode-change\", {mode: \"normal\"});\n      cm.on('cursorActivity', onCursorActivity);\n      maybeInitVimState(cm);\n      CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));\n    }\n\n    function leaveVimMode(cm) {\n      cm.setOption('disableInput', false);\n      cm.off('cursorActivity', onCursorActivity);\n      CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));\n      cm.state.vim = null;\n      if (highlightTimeout) clearTimeout(highlightTimeout);\n    }\n\n    function detachVimMap(cm, next) {\n      if (this == CodeMirror.keyMap.vim) {\n        cm.options.$customCursor = null;\n        CodeMirror.rmClass(cm.getWrapperElement(), \"cm-fat-cursor\");\n      }\n\n      if (!next || next.attach != attachVimMap)\n        leaveVimMode(cm);\n    }\n    function attachVimMap(cm, prev) {\n      if (this == CodeMirror.keyMap.vim) {\n        if (cm.curOp) cm.curOp.selectionChanged = true;\n        cm.options.$customCursor = transformCursor;\n        CodeMirror.addClass(cm.getWrapperElement(), \"cm-fat-cursor\");\n      }\n\n      if (!prev || prev.attach != attachVimMap)\n        enterVimMode(cm);\n    }\n\n    // Deprecated, simply setting the keymap works again.\n    CodeMirror.defineOption('vimMode', false, function(cm, val, prev) {\n      if (val && cm.getOption(\"keyMap\") != \"vim\")\n        cm.setOption(\"keyMap\", \"vim\");\n      else if (!val && prev != CodeMirror.Init && /^vim/.test(cm.getOption(\"keyMap\")))\n        cm.setOption(\"keyMap\", \"default\");\n    });\n\n    function cmKey(key, cm) {\n      if (!cm) { return undefined; }\n      if (this[key]) { return this[key]; }\n      var vimKey = cmKeyToVimKey(key);\n      if (!vimKey) {\n        return false;\n      }\n      var cmd = vimApi.findKey(cm, vimKey);\n      if (typeof cmd == 'function') {\n        CodeMirror.signal(cm, 'vim-keypress', vimKey);\n      }\n      return cmd;\n    }\n\n    var modifiers = {Shift:'S',Ctrl:'C',Alt:'A',Cmd:'D',Mod:'A',CapsLock:''};\n    var specialKeys = {Enter:'CR',Backspace:'BS',Delete:'Del',Insert:'Ins'};\n    function cmKeyToVimKey(key) {\n      if (key.charAt(0) == '\\'') {\n        // Keypress character binding of format \"'a'\"\n        return key.charAt(1);\n      }\n      var pieces = key.split(/-(?!$)/);\n      var lastPiece = pieces[pieces.length - 1];\n      if (pieces.length == 1 && pieces[0].length == 1) {\n        // No-modifier bindings use literal character bindings above. Skip.\n        return false;\n      } else if (pieces.length == 2 && pieces[0] == 'Shift' && lastPiece.length == 1) {\n        // Ignore Shift+char bindings as they should be handled by literal character.\n        return false;\n      }\n      var hasCharacter = false;\n      for (var i = 0; i < pieces.length; i++) {\n        var piece = pieces[i];\n        if (piece in modifiers) { pieces[i] = modifiers[piece]; }\n        else { hasCharacter = true; }\n        if (piece in specialKeys) { pieces[i] = specialKeys[piece]; }\n      }\n      if (!hasCharacter) {\n        // Vim does not support modifier only keys.\n        return false;\n      }\n      // TODO: Current bindings expect the character to be lower case, but\n      // it looks like vim key notation uses upper case.\n      if (isUpperCase(lastPiece)) {\n        pieces[pieces.length - 1] = lastPiece.toLowerCase();\n      }\n      return '<' + pieces.join('-') + '>';\n    }\n\n    function getOnPasteFn(cm) {\n      var vim = cm.state.vim;\n      if (!vim.onPasteFn) {\n        vim.onPasteFn = function() {\n          if (!vim.insertMode) {\n            cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));\n            actions.enterInsertMode(cm, {}, vim);\n          }\n        };\n      }\n      return vim.onPasteFn;\n    }\n\n    var numberRegex = /[\\d]/;\n    var wordCharTest = [CodeMirror.isWordChar, function(ch) {\n      return ch && !CodeMirror.isWordChar(ch) && !/\\s/.test(ch);\n    }], bigWordCharTest = [function(ch) {\n      return /\\S/.test(ch);\n    }];\n    function makeKeyRange(start, size) {\n      var keys = [];\n      for (var i = start; i < start + size; i++) {\n        keys.push(String.fromCharCode(i));\n      }\n      return keys;\n    }\n    var upperCaseAlphabet = makeKeyRange(65, 26);\n    var lowerCaseAlphabet = makeKeyRange(97, 26);\n    var numbers = makeKeyRange(48, 10);\n    var validMarks = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['<', '>']);\n    var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '\"', '.', ':', '_', '/']);\n    var upperCaseChars;\n    try { upperCaseChars = new RegExp(\"^[\\\\p{Lu}]$\", \"u\"); }\n    catch (_) { upperCaseChars = /^[A-Z]$/; }\n\n    function isLine(cm, line) {\n      return line >= cm.firstLine() && line <= cm.lastLine();\n    }\n    function isLowerCase(k) {\n      return (/^[a-z]$/).test(k);\n    }\n    function isMatchableSymbol(k) {\n      return '()[]{}'.indexOf(k) != -1;\n    }\n    function isNumber(k) {\n      return numberRegex.test(k);\n    }\n    function isUpperCase(k) {\n      return upperCaseChars.test(k);\n    }\n    function isWhiteSpaceString(k) {\n      return (/^\\s*$/).test(k);\n    }\n    function isEndOfSentenceSymbol(k) {\n      return '.?!'.indexOf(k) != -1;\n    }\n    function inArray(val, arr) {\n      for (var i = 0; i < arr.length; i++) {\n        if (arr[i] == val) {\n          return true;\n        }\n      }\n      return false;\n    }\n\n    var options = {};\n    function defineOption(name, defaultValue, type, aliases, callback) {\n      if (defaultValue === undefined && !callback) {\n        throw Error('defaultValue is required unless callback is provided');\n      }\n      if (!type) { type = 'string'; }\n      options[name] = {\n        type: type,\n        defaultValue: defaultValue,\n        callback: callback\n      };\n      if (aliases) {\n        for (var i = 0; i < aliases.length; i++) {\n          options[aliases[i]] = options[name];\n        }\n      }\n      if (defaultValue) {\n        setOption(name, defaultValue);\n      }\n    }\n\n    function setOption(name, value, cm, cfg) {\n      var option = options[name];\n      cfg = cfg || {};\n      var scope = cfg.scope;\n      if (!option) {\n        return new Error('Unknown option: ' + name);\n      }\n      if (option.type == 'boolean') {\n        if (value && value !== true) {\n          return new Error('Invalid argument: ' + name + '=' + value);\n        } else if (value !== false) {\n          // Boolean options are set to true if value is not defined.\n          value = true;\n        }\n      }\n      if (option.callback) {\n        if (scope !== 'local') {\n          option.callback(value, undefined);\n        }\n        if (scope !== 'global' && cm) {\n          option.callback(value, cm);\n        }\n      } else {\n        if (scope !== 'local') {\n          option.value = option.type == 'boolean' ? !!value : value;\n        }\n        if (scope !== 'global' && cm) {\n          cm.state.vim.options[name] = {value: value};\n        }\n      }\n    }\n\n    function getOption(name, cm, cfg) {\n      var option = options[name];\n      cfg = cfg || {};\n      var scope = cfg.scope;\n      if (!option) {\n        return new Error('Unknown option: ' + name);\n      }\n      if (option.callback) {\n        var local = cm && option.callback(undefined, cm);\n        if (scope !== 'global' && local !== undefined) {\n          return local;\n        }\n        if (scope !== 'local') {\n          return option.callback();\n        }\n        return;\n      } else {\n        var local = (scope !== 'global') && (cm && cm.state.vim.options[name]);\n        return (local || (scope !== 'local') && option || {}).value;\n      }\n    }\n\n    defineOption('filetype', undefined, 'string', ['ft'], function(name, cm) {\n      // Option is local. Do nothing for global.\n      if (cm === undefined) {\n        return;\n      }\n      // The 'filetype' option proxies to the CodeMirror 'mode' option.\n      if (name === undefined) {\n        var mode = cm.getOption('mode');\n        return mode == 'null' ? '' : mode;\n      } else {\n        var mode = name == '' ? 'null' : name;\n        cm.setOption('mode', mode);\n      }\n    });\n\n    var createCircularJumpList = function() {\n      var size = 100;\n      var pointer = -1;\n      var head = 0;\n      var tail = 0;\n      var buffer = new Array(size);\n      function add(cm, oldCur, newCur) {\n        var current = pointer % size;\n        var curMark = buffer[current];\n        function useNextSlot(cursor) {\n          var next = ++pointer % size;\n          var trashMark = buffer[next];\n          if (trashMark) {\n            trashMark.clear();\n          }\n          buffer[next] = cm.setBookmark(cursor);\n        }\n        if (curMark) {\n          var markPos = curMark.find();\n          // avoid recording redundant cursor position\n          if (markPos && !cursorEqual(markPos, oldCur)) {\n            useNextSlot(oldCur);\n          }\n        } else {\n          useNextSlot(oldCur);\n        }\n        useNextSlot(newCur);\n        head = pointer;\n        tail = pointer - size + 1;\n        if (tail < 0) {\n          tail = 0;\n        }\n      }\n      function move(cm, offset) {\n        pointer += offset;\n        if (pointer > head) {\n          pointer = head;\n        } else if (pointer < tail) {\n          pointer = tail;\n        }\n        var mark = buffer[(size + pointer) % size];\n        // skip marks that are temporarily removed from text buffer\n        if (mark && !mark.find()) {\n          var inc = offset > 0 ? 1 : -1;\n          var newCur;\n          var oldCur = cm.getCursor();\n          do {\n            pointer += inc;\n            mark = buffer[(size + pointer) % size];\n            // skip marks that are the same as current position\n            if (mark &&\n                (newCur = mark.find()) &&\n                !cursorEqual(oldCur, newCur)) {\n              break;\n            }\n          } while (pointer < head && pointer > tail);\n        }\n        return mark;\n      }\n      function find(cm, offset) {\n        var oldPointer = pointer;\n        var mark = move(cm, offset);\n        pointer = oldPointer;\n        return mark && mark.find();\n      }\n      return {\n        cachedCursor: undefined, //used for # and * jumps\n        add: add,\n        find: find,\n        move: move\n      };\n    };\n\n    // Returns an object to track the changes associated insert mode.  It\n    // clones the object that is passed in, or creates an empty object one if\n    // none is provided.\n    var createInsertModeChanges = function(c) {\n      if (c) {\n        // Copy construction\n        return {\n          changes: c.changes,\n          expectCursorActivityForChange: c.expectCursorActivityForChange\n        };\n      }\n      return {\n        // Change list\n        changes: [],\n        // Set to true on change, false on cursorActivity.\n        expectCursorActivityForChange: false\n      };\n    };\n\n    function MacroModeState() {\n      this.latestRegister = undefined;\n      this.isPlaying = false;\n      this.isRecording = false;\n      this.replaySearchQueries = [];\n      this.onRecordingDone = undefined;\n      this.lastInsertModeChanges = createInsertModeChanges();\n    }\n    MacroModeState.prototype = {\n      exitMacroRecordMode: function() {\n        var macroModeState = vimGlobalState.macroModeState;\n        if (macroModeState.onRecordingDone) {\n          macroModeState.onRecordingDone(); // close dialog\n        }\n        macroModeState.onRecordingDone = undefined;\n        macroModeState.isRecording = false;\n      },\n      enterMacroRecordMode: function(cm, registerName) {\n        var register =\n            vimGlobalState.registerController.getRegister(registerName);\n        if (register) {\n          register.clear();\n          this.latestRegister = registerName;\n          if (cm.openDialog) {\n            var template = dom('span', {class: 'cm-vim-message'}, 'recording @' + registerName);\n            this.onRecordingDone = cm.openDialog(template, null, {bottom:true});\n          }\n          this.isRecording = true;\n        }\n      }\n    };\n\n    function maybeInitVimState(cm) {\n      if (!cm.state.vim) {\n        // Store instance state in the CodeMirror object.\n        cm.state.vim = {\n          inputState: new InputState(),\n          // Vim's input state that triggered the last edit, used to repeat\n          // motions and operators with '.'.\n          lastEditInputState: undefined,\n          // Vim's action command before the last edit, used to repeat actions\n          // with '.' and insert mode repeat.\n          lastEditActionCommand: undefined,\n          // When using jk for navigation, if you move from a longer line to a\n          // shorter line, the cursor may clip to the end of the shorter line.\n          // If j is pressed again and cursor goes to the next line, the\n          // cursor should go back to its horizontal position on the longer\n          // line if it can. This is to keep track of the horizontal position.\n          lastHPos: -1,\n          // Doing the same with screen-position for gj/gk\n          lastHSPos: -1,\n          // The last motion command run. Cleared if a non-motion command gets\n          // executed in between.\n          lastMotion: null,\n          marks: {},\n          insertMode: false,\n          // Repeat count for changes made in insert mode, triggered by key\n          // sequences like 3,i. Only exists when insertMode is true.\n          insertModeRepeat: undefined,\n          visualMode: false,\n          // If we are in visual line mode. No effect if visualMode is false.\n          visualLine: false,\n          visualBlock: false,\n          lastSelection: null,\n          lastPastedText: null,\n          sel: {},\n          // Buffer-local/window-local values of vim options.\n          options: {}\n        };\n      }\n      return cm.state.vim;\n    }\n    var vimGlobalState;\n    function resetVimGlobalState() {\n      vimGlobalState = {\n        // The current search query.\n        searchQuery: null,\n        // Whether we are searching backwards.\n        searchIsReversed: false,\n        // Replace part of the last substituted pattern\n        lastSubstituteReplacePart: undefined,\n        jumpList: createCircularJumpList(),\n        macroModeState: new MacroModeState,\n        // Recording latest f, t, F or T motion command.\n        lastCharacterSearch: {increment:0, forward:true, selectedCharacter:''},\n        registerController: new RegisterController({}),\n        // search history buffer\n        searchHistoryController: new HistoryController(),\n        // ex Command history buffer\n        exCommandHistoryController : new HistoryController()\n      };\n      for (var optionName in options) {\n        var option = options[optionName];\n        option.value = option.defaultValue;\n      }\n    }\n\n    var lastInsertModeKeyTimer;\n    var vimApi = {\n      enterVimMode: enterVimMode,\n      buildKeyMap: function() {\n        // TODO: Convert keymap into dictionary format for fast lookup.\n      },\n      // Testing hook, though it might be useful to expose the register\n      // controller anyway.\n      getRegisterController: function() {\n        return vimGlobalState.registerController;\n      },\n      // Testing hook.\n      resetVimGlobalState_: resetVimGlobalState,\n\n      // Testing hook.\n      getVimGlobalState_: function() {\n        return vimGlobalState;\n      },\n\n      // Testing hook.\n      maybeInitVimState_: maybeInitVimState,\n\n      suppressErrorLogging: false,\n\n      InsertModeKey: InsertModeKey,\n      map: function(lhs, rhs, ctx) {\n        // Add user defined key bindings.\n        exCommandDispatcher.map(lhs, rhs, ctx);\n      },\n      unmap: function(lhs, ctx) {\n        return exCommandDispatcher.unmap(lhs, ctx);\n      },\n      // Non-recursive map function.\n      // NOTE: This will not create mappings to key maps that aren't present\n      // in the default key map. See TODO at bottom of function.\n      noremap: function(lhs, rhs, ctx) {\n        function toCtxArray(ctx) {\n          return ctx ? [ctx] : ['normal', 'insert', 'visual'];\n        }\n        var ctxsToMap = toCtxArray(ctx);\n        // Look through all actual defaults to find a map candidate.\n        var actualLength = defaultKeymap.length, origLength = defaultKeymapLength;\n        for (var i = actualLength - origLength;\n             i < actualLength && ctxsToMap.length;\n             i++) {\n          var mapping = defaultKeymap[i];\n          // Omit mappings that operate in the wrong context(s) and those of invalid type.\n          if (mapping.keys == rhs &&\n              (!ctx || !mapping.context || mapping.context === ctx) &&\n              mapping.type.substr(0, 2) !== 'ex' &&\n              mapping.type.substr(0, 3) !== 'key') {\n            // Make a shallow copy of the original keymap entry.\n            var newMapping = {};\n            for (var key in mapping) {\n              newMapping[key] = mapping[key];\n            }\n            // Modify it point to the new mapping with the proper context.\n            newMapping.keys = lhs;\n            if (ctx && !newMapping.context) {\n              newMapping.context = ctx;\n            }\n            // Add it to the keymap with a higher priority than the original.\n            this._mapCommand(newMapping);\n            // Record the mapped contexts as complete.\n            var mappedCtxs = toCtxArray(mapping.context);\n            ctxsToMap = ctxsToMap.filter(function(el) { return mappedCtxs.indexOf(el) === -1; });\n          }\n        }\n        // TODO: Create non-recursive keyToKey mappings for the unmapped contexts once those exist.\n      },\n      // Remove all user-defined mappings for the provided context.\n      mapclear: function(ctx) {\n        // Partition the existing keymap into user-defined and true defaults.\n        var actualLength = defaultKeymap.length,\n            origLength = defaultKeymapLength;\n        var userKeymap = defaultKeymap.slice(0, actualLength - origLength);\n        defaultKeymap = defaultKeymap.slice(actualLength - origLength);\n        if (ctx) {\n          // If a specific context is being cleared, we need to keep mappings\n          // from all other contexts.\n          for (var i = userKeymap.length - 1; i >= 0; i--) {\n            var mapping = userKeymap[i];\n            if (ctx !== mapping.context) {\n              if (mapping.context) {\n                this._mapCommand(mapping);\n              } else {\n                // `mapping` applies to all contexts so create keymap copies\n                // for each context except the one being cleared.\n                var contexts = ['normal', 'insert', 'visual'];\n                for (var j in contexts) {\n                  if (contexts[j] !== ctx) {\n                    var newMapping = {};\n                    for (var key in mapping) {\n                      newMapping[key] = mapping[key];\n                    }\n                    newMapping.context = contexts[j];\n                    this._mapCommand(newMapping);\n                  }\n                }\n              }\n            }\n          }\n        }\n      },\n      // TODO: Expose setOption and getOption as instance methods. Need to decide how to namespace\n      // them, or somehow make them work with the existing CodeMirror setOption/getOption API.\n      setOption: setOption,\n      getOption: getOption,\n      defineOption: defineOption,\n      defineEx: function(name, prefix, func){\n        if (!prefix) {\n          prefix = name;\n        } else if (name.indexOf(prefix) !== 0) {\n          throw new Error('(Vim.defineEx) \"'+prefix+'\" is not a prefix of \"'+name+'\", command not registered');\n        }\n        exCommands[name]=func;\n        exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'};\n      },\n      handleKey: function (cm, key, origin) {\n        var command = this.findKey(cm, key, origin);\n        if (typeof command === 'function') {\n          return command();\n        }\n      },\n      multiSelectHandleKey: multiSelectHandleKey,\n\n      /**\n       * This is the outermost function called by CodeMirror, after keys have\n       * been mapped to their Vim equivalents.\n       *\n       * Finds a command based on the key (and cached keys if there is a\n       * multi-key sequence). Returns `undefined` if no key is matched, a noop\n       * function if a partial match is found (multi-key), and a function to\n       * execute the bound command if a a key is matched. The function always\n       * returns true.\n       */\n      findKey: function(cm, key, origin) {\n        var vim = maybeInitVimState(cm);\n        function handleMacroRecording() {\n          var macroModeState = vimGlobalState.macroModeState;\n          if (macroModeState.isRecording) {\n            if (key == 'q') {\n              macroModeState.exitMacroRecordMode();\n              clearInputState(cm);\n              return true;\n            }\n            if (origin != 'mapping') {\n              logKey(macroModeState, key);\n            }\n          }\n        }\n        function handleEsc() {\n          if (key == '<Esc>') {\n            if (vim.visualMode) {\n              // Get back to normal mode.\n              exitVisualMode(cm);\n            } else if (vim.insertMode) {\n              // Get back to normal mode.\n              exitInsertMode(cm);\n            } else {\n              // We're already in normal mode. Let '<Esc>' be handled normally.\n              return;\n            }\n            clearInputState(cm);\n            return true;\n          }\n        }\n        function doKeyToKey(keys) {\n          // TODO: prevent infinite recursion.\n          var match;\n          while (keys) {\n            // Pull off one command key, which is either a single character\n            // or a special sequence wrapped in '<' and '>', e.g. '<Space>'.\n            match = (/<\\w+-.+?>|<\\w+>|./).exec(keys);\n            key = match[0];\n            keys = keys.substring(match.index + key.length);\n            vimApi.handleKey(cm, key, 'mapping');\n          }\n        }\n\n        function handleKeyInsertMode() {\n          if (handleEsc()) { return true; }\n          var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;\n          var keysAreChars = key.length == 1;\n          var match = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');\n          // Need to check all key substrings in insert mode.\n          while (keys.length > 1 && match.type != 'full') {\n            var keys = vim.inputState.keyBuffer = keys.slice(1);\n            var thisMatch = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');\n            if (thisMatch.type != 'none') { match = thisMatch; }\n          }\n          if (match.type == 'none') { clearInputState(cm); return false; }\n          else if (match.type == 'partial') {\n            if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }\n            lastInsertModeKeyTimer = window.setTimeout(\n              function() { if (vim.insertMode && vim.inputState.keyBuffer) { clearInputState(cm); } },\n              getOption('insertModeEscKeysTimeout'));\n            return !keysAreChars;\n          }\n\n          if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }\n          if (keysAreChars) {\n            var selections = cm.listSelections();\n            for (var i = 0; i < selections.length; i++) {\n              var here = selections[i].head;\n              cm.replaceRange('', offsetCursor(here, 0, -(keys.length - 1)), here, '+input');\n            }\n            vimGlobalState.macroModeState.lastInsertModeChanges.changes.pop();\n          }\n          clearInputState(cm);\n          return match.command;\n        }\n\n        function handleKeyNonInsertMode() {\n          if (handleMacroRecording() || handleEsc()) { return true; }\n\n          var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;\n          if (/^[1-9]\\d*$/.test(keys)) { return true; }\n\n          var keysMatcher = /^(\\d*)(.*)$/.exec(keys);\n          if (!keysMatcher) { clearInputState(cm); return false; }\n          var context = vim.visualMode ? 'visual' :\n                                         'normal';\n          var mainKey = keysMatcher[2] || keysMatcher[1];\n          if (vim.inputState.operatorShortcut && vim.inputState.operatorShortcut.slice(-1) == mainKey) {\n            // multikey operators act linewise by repeating only the last character\n            mainKey = vim.inputState.operatorShortcut;\n          }\n          var match = commandDispatcher.matchCommand(mainKey, defaultKeymap, vim.inputState, context);\n          if (match.type == 'none') { clearInputState(cm); return false; }\n          else if (match.type == 'partial') { return true; }\n          else if (match.type == 'clear') { clearInputState(cm); return true; }\n\n          vim.inputState.keyBuffer = '';\n          keysMatcher = /^(\\d*)(.*)$/.exec(keys);\n          if (keysMatcher[1] && keysMatcher[1] != '0') {\n            vim.inputState.pushRepeatDigit(keysMatcher[1]);\n          }\n          return match.command;\n        }\n\n        var command;\n        if (vim.insertMode) { command = handleKeyInsertMode(); }\n        else { command = handleKeyNonInsertMode(); }\n        if (command === false) {\n          return !vim.insertMode && key.length === 1 ? function() { return true; } : undefined;\n        } else if (command === true) {\n          // TODO: Look into using CodeMirror's multi-key handling.\n          // Return no-op since we are caching the key. Counts as handled, but\n          // don't want act on it just yet.\n          return function() { return true; };\n        } else {\n          return function() {\n            return cm.operation(function() {\n              cm.curOp.isVimOp = true;\n              try {\n                if (command.type == 'keyToKey') {\n                  doKeyToKey(command.toKeys);\n                } else {\n                  commandDispatcher.processCommand(cm, vim, command);\n                }\n              } catch (e) {\n                // clear VIM state in case it's in a bad state.\n                cm.state.vim = undefined;\n                maybeInitVimState(cm);\n                if (!vimApi.suppressErrorLogging) {\n                  console['log'](e);\n                }\n                throw e;\n              }\n              return true;\n            });\n          };\n        }\n      },\n      handleEx: function(cm, input) {\n        exCommandDispatcher.processCommand(cm, input);\n      },\n\n      defineMotion: defineMotion,\n      defineAction: defineAction,\n      defineOperator: defineOperator,\n      mapCommand: mapCommand,\n      _mapCommand: _mapCommand,\n\n      defineRegister: defineRegister,\n\n      exitVisualMode: exitVisualMode,\n      exitInsertMode: exitInsertMode\n    };\n\n    // Represents the current input state.\n    function InputState() {\n      this.prefixRepeat = [];\n      this.motionRepeat = [];\n\n      this.operator = null;\n      this.operatorArgs = null;\n      this.motion = null;\n      this.motionArgs = null;\n      this.keyBuffer = []; // For matching multi-key commands.\n      this.registerName = null; // Defaults to the unnamed register.\n    }\n    InputState.prototype.pushRepeatDigit = function(n) {\n      if (!this.operator) {\n        this.prefixRepeat = this.prefixRepeat.concat(n);\n      } else {\n        this.motionRepeat = this.motionRepeat.concat(n);\n      }\n    };\n    InputState.prototype.getRepeat = function() {\n      var repeat = 0;\n      if (this.prefixRepeat.length > 0 || this.motionRepeat.length > 0) {\n        repeat = 1;\n        if (this.prefixRepeat.length > 0) {\n          repeat *= parseInt(this.prefixRepeat.join(''), 10);\n        }\n        if (this.motionRepeat.length > 0) {\n          repeat *= parseInt(this.motionRepeat.join(''), 10);\n        }\n      }\n      return repeat;\n    };\n\n    function clearInputState(cm, reason) {\n      cm.state.vim.inputState = new InputState();\n      CodeMirror.signal(cm, 'vim-command-done', reason);\n    }\n\n    /*\n     * Register stores information about copy and paste registers.  Besides\n     * text, a register must store whether it is linewise (i.e., when it is\n     * pasted, should it insert itself into a new line, or should the text be\n     * inserted at the cursor position.)\n     */\n    function Register(text, linewise, blockwise) {\n      this.clear();\n      this.keyBuffer = [text || ''];\n      this.insertModeChanges = [];\n      this.searchQueries = [];\n      this.linewise = !!linewise;\n      this.blockwise = !!blockwise;\n    }\n    Register.prototype = {\n      setText: function(text, linewise, blockwise) {\n        this.keyBuffer = [text || ''];\n        this.linewise = !!linewise;\n        this.blockwise = !!blockwise;\n      },\n      pushText: function(text, linewise) {\n        // if this register has ever been set to linewise, use linewise.\n        if (linewise) {\n          if (!this.linewise) {\n            this.keyBuffer.push('\\n');\n          }\n          this.linewise = true;\n        }\n        this.keyBuffer.push(text);\n      },\n      pushInsertModeChanges: function(changes) {\n        this.insertModeChanges.push(createInsertModeChanges(changes));\n      },\n      pushSearchQuery: function(query) {\n        this.searchQueries.push(query);\n      },\n      clear: function() {\n        this.keyBuffer = [];\n        this.insertModeChanges = [];\n        this.searchQueries = [];\n        this.linewise = false;\n      },\n      toString: function() {\n        return this.keyBuffer.join('');\n      }\n    };\n\n    /**\n     * Defines an external register.\n     *\n     * The name should be a single character that will be used to reference the register.\n     * The register should support setText, pushText, clear, and toString(). See Register\n     * for a reference implementation.\n     */\n    function defineRegister(name, register) {\n      var registers = vimGlobalState.registerController.registers;\n      if (!name || name.length != 1) {\n        throw Error('Register name must be 1 character');\n      }\n      if (registers[name]) {\n        throw Error('Register already defined ' + name);\n      }\n      registers[name] = register;\n      validRegisters.push(name);\n    }\n\n    /*\n     * vim registers allow you to keep many independent copy and paste buffers.\n     * See http://usevim.com/2012/04/13/registers/ for an introduction.\n     *\n     * RegisterController keeps the state of all the registers.  An initial\n     * state may be passed in.  The unnamed register '\"' will always be\n     * overridden.\n     */\n    function RegisterController(registers) {\n      this.registers = registers;\n      this.unnamedRegister = registers['\"'] = new Register();\n      registers['.'] = new Register();\n      registers[':'] = new Register();\n      registers['/'] = new Register();\n    }\n    RegisterController.prototype = {\n      pushText: function(registerName, operator, text, linewise, blockwise) {\n        // The black hole register, \"_, means delete/yank to nowhere.\n        if (registerName === '_') return;\n        if (linewise && text.charAt(text.length - 1) !== '\\n'){\n          text += '\\n';\n        }\n        // Lowercase and uppercase registers refer to the same register.\n        // Uppercase just means append.\n        var register = this.isValidRegister(registerName) ?\n            this.getRegister(registerName) : null;\n        // if no register/an invalid register was specified, things go to the\n        // default registers\n        if (!register) {\n          switch (operator) {\n            case 'yank':\n              // The 0 register contains the text from the most recent yank.\n              this.registers['0'] = new Register(text, linewise, blockwise);\n              break;\n            case 'delete':\n            case 'change':\n              if (text.indexOf('\\n') == -1) {\n                // Delete less than 1 line. Update the small delete register.\n                this.registers['-'] = new Register(text, linewise);\n              } else {\n                // Shift down the contents of the numbered registers and put the\n                // deleted text into register 1.\n                this.shiftNumericRegisters_();\n                this.registers['1'] = new Register(text, linewise);\n              }\n              break;\n          }\n          // Make sure the unnamed register is set to what just happened\n          this.unnamedRegister.setText(text, linewise, blockwise);\n          return;\n        }\n\n        // If we've gotten to this point, we've actually specified a register\n        var append = isUpperCase(registerName);\n        if (append) {\n          register.pushText(text, linewise);\n        } else {\n          register.setText(text, linewise, blockwise);\n        }\n        // The unnamed register always has the same value as the last used\n        // register.\n        this.unnamedRegister.setText(register.toString(), linewise);\n      },\n      // Gets the register named @name.  If one of @name doesn't already exist,\n      // create it.  If @name is invalid, return the unnamedRegister.\n      getRegister: function(name) {\n        if (!this.isValidRegister(name)) {\n          return this.unnamedRegister;\n        }\n        name = name.toLowerCase();\n        if (!this.registers[name]) {\n          this.registers[name] = new Register();\n        }\n        return this.registers[name];\n      },\n      isValidRegister: function(name) {\n        return name && inArray(name, validRegisters);\n      },\n      shiftNumericRegisters_: function() {\n        for (var i = 9; i >= 2; i--) {\n          this.registers[i] = this.getRegister('' + (i - 1));\n        }\n      }\n    };\n    function HistoryController() {\n        this.historyBuffer = [];\n        this.iterator = 0;\n        this.initialPrefix = null;\n    }\n    HistoryController.prototype = {\n      // the input argument here acts a user entered prefix for a small time\n      // until we start autocompletion in which case it is the autocompleted.\n      nextMatch: function (input, up) {\n        var historyBuffer = this.historyBuffer;\n        var dir = up ? -1 : 1;\n        if (this.initialPrefix === null) this.initialPrefix = input;\n        for (var i = this.iterator + dir; up ? i >= 0 : i < historyBuffer.length; i+= dir) {\n          var element = historyBuffer[i];\n          for (var j = 0; j <= element.length; j++) {\n            if (this.initialPrefix == element.substring(0, j)) {\n              this.iterator = i;\n              return element;\n            }\n          }\n        }\n        // should return the user input in case we reach the end of buffer.\n        if (i >= historyBuffer.length) {\n          this.iterator = historyBuffer.length;\n          return this.initialPrefix;\n        }\n        // return the last autocompleted query or exCommand as it is.\n        if (i < 0 ) return input;\n      },\n      pushInput: function(input) {\n        var index = this.historyBuffer.indexOf(input);\n        if (index > -1) this.historyBuffer.splice(index, 1);\n        if (input.length) this.historyBuffer.push(input);\n      },\n      reset: function() {\n        this.initialPrefix = null;\n        this.iterator = this.historyBuffer.length;\n      }\n    };\n    var commandDispatcher = {\n      matchCommand: function(keys, keyMap, inputState, context) {\n        var matches = commandMatches(keys, keyMap, context, inputState);\n        if (!matches.full && !matches.partial) {\n          return {type: 'none'};\n        } else if (!matches.full && matches.partial) {\n          return {type: 'partial'};\n        }\n\n        var bestMatch;\n        for (var i = 0; i < matches.full.length; i++) {\n          var match = matches.full[i];\n          if (!bestMatch) {\n            bestMatch = match;\n          }\n        }\n        if (bestMatch.keys.slice(-11) == '<character>') {\n          var character = lastChar(keys);\n          if (!character || character.length > 1) return {type: 'clear'};\n          inputState.selectedCharacter = character;\n        }\n        return {type: 'full', command: bestMatch};\n      },\n      processCommand: function(cm, vim, command) {\n        vim.inputState.repeatOverride = command.repeatOverride;\n        switch (command.type) {\n          case 'motion':\n            this.processMotion(cm, vim, command);\n            break;\n          case 'operator':\n            this.processOperator(cm, vim, command);\n            break;\n          case 'operatorMotion':\n            this.processOperatorMotion(cm, vim, command);\n            break;\n          case 'action':\n            this.processAction(cm, vim, command);\n            break;\n          case 'search':\n            this.processSearch(cm, vim, command);\n            break;\n          case 'ex':\n          case 'keyToEx':\n            this.processEx(cm, vim, command);\n            break;\n        }\n      },\n      processMotion: function(cm, vim, command) {\n        vim.inputState.motion = command.motion;\n        vim.inputState.motionArgs = copyArgs(command.motionArgs);\n        this.evalInput(cm, vim);\n      },\n      processOperator: function(cm, vim, command) {\n        var inputState = vim.inputState;\n        if (inputState.operator) {\n          if (inputState.operator == command.operator) {\n            // Typing an operator twice like 'dd' makes the operator operate\n            // linewise\n            inputState.motion = 'expandToLine';\n            inputState.motionArgs = { linewise: true };\n            this.evalInput(cm, vim);\n            return;\n          } else {\n            // 2 different operators in a row doesn't make sense.\n            clearInputState(cm);\n          }\n        }\n        inputState.operator = command.operator;\n        inputState.operatorArgs = copyArgs(command.operatorArgs);\n        if (command.keys.length > 1) {\n          inputState.operatorShortcut = command.keys;\n        }\n        if (command.exitVisualBlock) {\n            vim.visualBlock = false;\n            updateCmSelection(cm);\n        }\n        if (vim.visualMode) {\n          // Operating on a selection in visual mode. We don't need a motion.\n          this.evalInput(cm, vim);\n        }\n      },\n      processOperatorMotion: function(cm, vim, command) {\n        var visualMode = vim.visualMode;\n        var operatorMotionArgs = copyArgs(command.operatorMotionArgs);\n        if (operatorMotionArgs) {\n          // Operator motions may have special behavior in visual mode.\n          if (visualMode && operatorMotionArgs.visualLine) {\n            vim.visualLine = true;\n          }\n        }\n        this.processOperator(cm, vim, command);\n        if (!visualMode) {\n          this.processMotion(cm, vim, command);\n        }\n      },\n      processAction: function(cm, vim, command) {\n        var inputState = vim.inputState;\n        var repeat = inputState.getRepeat();\n        var repeatIsExplicit = !!repeat;\n        var actionArgs = copyArgs(command.actionArgs) || {};\n        if (inputState.selectedCharacter) {\n          actionArgs.selectedCharacter = inputState.selectedCharacter;\n        }\n        // Actions may or may not have motions and operators. Do these first.\n        if (command.operator) {\n          this.processOperator(cm, vim, command);\n        }\n        if (command.motion) {\n          this.processMotion(cm, vim, command);\n        }\n        if (command.motion || command.operator) {\n          this.evalInput(cm, vim);\n        }\n        actionArgs.repeat = repeat || 1;\n        actionArgs.repeatIsExplicit = repeatIsExplicit;\n        actionArgs.registerName = inputState.registerName;\n        clearInputState(cm);\n        vim.lastMotion = null;\n        if (command.isEdit) {\n          this.recordLastEdit(vim, inputState, command);\n        }\n        actions[command.action](cm, actionArgs, vim);\n      },\n      processSearch: function(cm, vim, command) {\n        if (!cm.getSearchCursor) {\n          // Search depends on SearchCursor.\n          return;\n        }\n        var forward = command.searchArgs.forward;\n        var wholeWordOnly = command.searchArgs.wholeWordOnly;\n        getSearchState(cm).setReversed(!forward);\n        var promptPrefix = (forward) ? '/' : '?';\n        var originalQuery = getSearchState(cm).getQuery();\n        var originalScrollPos = cm.getScrollInfo();\n        function handleQuery(query, ignoreCase, smartCase) {\n          vimGlobalState.searchHistoryController.pushInput(query);\n          vimGlobalState.searchHistoryController.reset();\n          try {\n            updateSearchQuery(cm, query, ignoreCase, smartCase);\n          } catch (e) {\n            showConfirm(cm, 'Invalid regex: ' + query);\n            clearInputState(cm);\n            return;\n          }\n          commandDispatcher.processMotion(cm, vim, {\n            type: 'motion',\n            motion: 'findNext',\n            motionArgs: { forward: true, toJumplist: command.searchArgs.toJumplist }\n          });\n        }\n        function onPromptClose(query) {\n          cm.scrollTo(originalScrollPos.left, originalScrollPos.top);\n          handleQuery(query, true /** ignoreCase */, true /** smartCase */);\n          var macroModeState = vimGlobalState.macroModeState;\n          if (macroModeState.isRecording) {\n            logSearchQuery(macroModeState, query);\n          }\n        }\n        function onPromptKeyUp(e, query, close) {\n          var keyName = CodeMirror.keyName(e), up, offset;\n          if (keyName == 'Up' || keyName == 'Down') {\n            up = keyName == 'Up' ? true : false;\n            offset = e.target ? e.target.selectionEnd : 0;\n            query = vimGlobalState.searchHistoryController.nextMatch(query, up) || '';\n            close(query);\n            if (offset && e.target) e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length);\n          } else {\n            if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')\n              vimGlobalState.searchHistoryController.reset();\n          }\n          var parsedQuery;\n          try {\n            parsedQuery = updateSearchQuery(cm, query,\n                true /** ignoreCase */, true /** smartCase */);\n          } catch (e) {\n            // Swallow bad regexes for incremental search.\n          }\n          if (parsedQuery) {\n            cm.scrollIntoView(findNext(cm, !forward, parsedQuery), 30);\n          } else {\n            clearSearchHighlight(cm);\n            cm.scrollTo(originalScrollPos.left, originalScrollPos.top);\n          }\n        }\n        function onPromptKeyDown(e, query, close) {\n          var keyName = CodeMirror.keyName(e);\n          if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[' ||\n              (keyName == 'Backspace' && query == '')) {\n            vimGlobalState.searchHistoryController.pushInput(query);\n            vimGlobalState.searchHistoryController.reset();\n            updateSearchQuery(cm, originalQuery);\n            clearSearchHighlight(cm);\n            cm.scrollTo(originalScrollPos.left, originalScrollPos.top);\n            CodeMirror.e_stop(e);\n            clearInputState(cm);\n            close();\n            cm.focus();\n          } else if (keyName == 'Up' || keyName == 'Down') {\n            CodeMirror.e_stop(e);\n          } else if (keyName == 'Ctrl-U') {\n            // Ctrl-U clears input.\n            CodeMirror.e_stop(e);\n            close('');\n          }\n        }\n        switch (command.searchArgs.querySrc) {\n          case 'prompt':\n            var macroModeState = vimGlobalState.macroModeState;\n            if (macroModeState.isPlaying) {\n              var query = macroModeState.replaySearchQueries.shift();\n              handleQuery(query, true /** ignoreCase */, false /** smartCase */);\n            } else {\n              showPrompt(cm, {\n                  onClose: onPromptClose,\n                  prefix: promptPrefix,\n                  desc: '(JavaScript regexp)',\n                  onKeyUp: onPromptKeyUp,\n                  onKeyDown: onPromptKeyDown\n              });\n            }\n            break;\n          case 'wordUnderCursor':\n            var word = expandWordUnderCursor(cm, false /** inclusive */,\n                true /** forward */, false /** bigWord */,\n                true /** noSymbol */);\n            var isKeyword = true;\n            if (!word) {\n              word = expandWordUnderCursor(cm, false /** inclusive */,\n                  true /** forward */, false /** bigWord */,\n                  false /** noSymbol */);\n              isKeyword = false;\n            }\n            if (!word) {\n              return;\n            }\n            var query = cm.getLine(word.start.line).substring(word.start.ch,\n                word.end.ch);\n            if (isKeyword && wholeWordOnly) {\n                query = '\\\\b' + query + '\\\\b';\n            } else {\n              query = escapeRegex(query);\n            }\n\n            // cachedCursor is used to save the old position of the cursor\n            // when * or # causes vim to seek for the nearest word and shift\n            // the cursor before entering the motion.\n            vimGlobalState.jumpList.cachedCursor = cm.getCursor();\n            cm.setCursor(word.start);\n\n            handleQuery(query, true /** ignoreCase */, false /** smartCase */);\n            break;\n        }\n      },\n      processEx: function(cm, vim, command) {\n        function onPromptClose(input) {\n          // Give the prompt some time to close so that if processCommand shows\n          // an error, the elements don't overlap.\n          vimGlobalState.exCommandHistoryController.pushInput(input);\n          vimGlobalState.exCommandHistoryController.reset();\n          exCommandDispatcher.processCommand(cm, input);\n          clearInputState(cm);\n        }\n        function onPromptKeyDown(e, input, close) {\n          var keyName = CodeMirror.keyName(e), up, offset;\n          if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[' ||\n              (keyName == 'Backspace' && input == '')) {\n            vimGlobalState.exCommandHistoryController.pushInput(input);\n            vimGlobalState.exCommandHistoryController.reset();\n            CodeMirror.e_stop(e);\n            clearInputState(cm);\n            close();\n            cm.focus();\n          }\n          if (keyName == 'Up' || keyName == 'Down') {\n            CodeMirror.e_stop(e);\n            up = keyName == 'Up' ? true : false;\n            offset = e.target ? e.target.selectionEnd : 0;\n            input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || '';\n            close(input);\n            if (offset && e.target) e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length);\n          } else if (keyName == 'Ctrl-U') {\n            // Ctrl-U clears input.\n            CodeMirror.e_stop(e);\n            close('');\n          } else {\n            if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')\n              vimGlobalState.exCommandHistoryController.reset();\n          }\n        }\n        if (command.type == 'keyToEx') {\n          // Handle user defined Ex to Ex mappings\n          exCommandDispatcher.processCommand(cm, command.exArgs.input);\n        } else {\n          if (vim.visualMode) {\n            showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\\'<,\\'>',\n                onKeyDown: onPromptKeyDown, selectValueOnOpen: false});\n          } else {\n            showPrompt(cm, { onClose: onPromptClose, prefix: ':',\n                onKeyDown: onPromptKeyDown});\n          }\n        }\n      },\n      evalInput: function(cm, vim) {\n        // If the motion command is set, execute both the operator and motion.\n        // Otherwise return.\n        var inputState = vim.inputState;\n        var motion = inputState.motion;\n        var motionArgs = inputState.motionArgs || {};\n        var operator = inputState.operator;\n        var operatorArgs = inputState.operatorArgs || {};\n        var registerName = inputState.registerName;\n        var sel = vim.sel;\n        // TODO: Make sure cm and vim selections are identical outside visual mode.\n        var origHead = copyCursor(vim.visualMode ? clipCursorToContent(cm, sel.head): cm.getCursor('head'));\n        var origAnchor = copyCursor(vim.visualMode ? clipCursorToContent(cm, sel.anchor) : cm.getCursor('anchor'));\n        var oldHead = copyCursor(origHead);\n        var oldAnchor = copyCursor(origAnchor);\n        var newHead, newAnchor;\n        var repeat;\n        if (operator) {\n          this.recordLastEdit(vim, inputState);\n        }\n        if (inputState.repeatOverride !== undefined) {\n          // If repeatOverride is specified, that takes precedence over the\n          // input state's repeat. Used by Ex mode and can be user defined.\n          repeat = inputState.repeatOverride;\n        } else {\n          repeat = inputState.getRepeat();\n        }\n        if (repeat > 0 && motionArgs.explicitRepeat) {\n          motionArgs.repeatIsExplicit = true;\n        } else if (motionArgs.noRepeat ||\n            (!motionArgs.explicitRepeat && repeat === 0)) {\n          repeat = 1;\n          motionArgs.repeatIsExplicit = false;\n        }\n        if (inputState.selectedCharacter) {\n          // If there is a character input, stick it in all of the arg arrays.\n          motionArgs.selectedCharacter = operatorArgs.selectedCharacter =\n              inputState.selectedCharacter;\n        }\n        motionArgs.repeat = repeat;\n        clearInputState(cm);\n        if (motion) {\n          var motionResult = motions[motion](cm, origHead, motionArgs, vim, inputState);\n          vim.lastMotion = motions[motion];\n          if (!motionResult) {\n            return;\n          }\n          if (motionArgs.toJumplist) {\n            var jumpList = vimGlobalState.jumpList;\n            // if the current motion is # or *, use cachedCursor\n            var cachedCursor = jumpList.cachedCursor;\n            if (cachedCursor) {\n              recordJumpPosition(cm, cachedCursor, motionResult);\n              delete jumpList.cachedCursor;\n            } else {\n              recordJumpPosition(cm, origHead, motionResult);\n            }\n          }\n          if (motionResult instanceof Array) {\n            newAnchor = motionResult[0];\n            newHead = motionResult[1];\n          } else {\n            newHead = motionResult;\n          }\n          // TODO: Handle null returns from motion commands better.\n          if (!newHead) {\n            newHead = copyCursor(origHead);\n          }\n          if (vim.visualMode) {\n            if (!(vim.visualBlock && newHead.ch === Infinity)) {\n              newHead = clipCursorToContent(cm, newHead);\n            }\n            if (newAnchor) {\n              newAnchor = clipCursorToContent(cm, newAnchor);\n            }\n            newAnchor = newAnchor || oldAnchor;\n            sel.anchor = newAnchor;\n            sel.head = newHead;\n            updateCmSelection(cm);\n            updateMark(cm, vim, '<',\n                cursorIsBefore(newAnchor, newHead) ? newAnchor\n                    : newHead);\n            updateMark(cm, vim, '>',\n                cursorIsBefore(newAnchor, newHead) ? newHead\n                    : newAnchor);\n          } else if (!operator) {\n            newHead = clipCursorToContent(cm, newHead);\n            cm.setCursor(newHead.line, newHead.ch);\n          }\n        }\n        if (operator) {\n          if (operatorArgs.lastSel) {\n            // Replaying a visual mode operation\n            newAnchor = oldAnchor;\n            var lastSel = operatorArgs.lastSel;\n            var lineOffset = Math.abs(lastSel.head.line - lastSel.anchor.line);\n            var chOffset = Math.abs(lastSel.head.ch - lastSel.anchor.ch);\n            if (lastSel.visualLine) {\n              // Linewise Visual mode: The same number of lines.\n              newHead = new Pos(oldAnchor.line + lineOffset, oldAnchor.ch);\n            } else if (lastSel.visualBlock) {\n              // Blockwise Visual mode: The same number of lines and columns.\n              newHead = new Pos(oldAnchor.line + lineOffset, oldAnchor.ch + chOffset);\n            } else if (lastSel.head.line == lastSel.anchor.line) {\n              // Normal Visual mode within one line: The same number of characters.\n              newHead = new Pos(oldAnchor.line, oldAnchor.ch + chOffset);\n            } else {\n              // Normal Visual mode with several lines: The same number of lines, in the\n              // last line the same number of characters as in the last line the last time.\n              newHead = new Pos(oldAnchor.line + lineOffset, oldAnchor.ch);\n            }\n            vim.visualMode = true;\n            vim.visualLine = lastSel.visualLine;\n            vim.visualBlock = lastSel.visualBlock;\n            sel = vim.sel = {\n              anchor: newAnchor,\n              head: newHead\n            };\n            updateCmSelection(cm);\n          } else if (vim.visualMode) {\n            operatorArgs.lastSel = {\n              anchor: copyCursor(sel.anchor),\n              head: copyCursor(sel.head),\n              visualBlock: vim.visualBlock,\n              visualLine: vim.visualLine\n            };\n          }\n          var curStart, curEnd, linewise, mode;\n          var cmSel;\n          if (vim.visualMode) {\n            // Init visual op\n            curStart = cursorMin(sel.head, sel.anchor);\n            curEnd = cursorMax(sel.head, sel.anchor);\n            linewise = vim.visualLine || operatorArgs.linewise;\n            mode = vim.visualBlock ? 'block' :\n                   linewise ? 'line' :\n                   'char';\n            cmSel = makeCmSelection(cm, {\n              anchor: curStart,\n              head: curEnd\n            }, mode);\n            if (linewise) {\n              var ranges = cmSel.ranges;\n              if (mode == 'block') {\n                // Linewise operators in visual block mode extend to end of line\n                for (var i = 0; i < ranges.length; i++) {\n                  ranges[i].head.ch = lineLength(cm, ranges[i].head.line);\n                }\n              } else if (mode == 'line') {\n                ranges[0].head = new Pos(ranges[0].head.line + 1, 0);\n              }\n            }\n          } else {\n            // Init motion op\n            curStart = copyCursor(newAnchor || oldAnchor);\n            curEnd = copyCursor(newHead || oldHead);\n            if (cursorIsBefore(curEnd, curStart)) {\n              var tmp = curStart;\n              curStart = curEnd;\n              curEnd = tmp;\n            }\n            linewise = motionArgs.linewise || operatorArgs.linewise;\n            if (linewise) {\n              // Expand selection to entire line.\n              expandSelectionToLine(cm, curStart, curEnd);\n            } else if (motionArgs.forward) {\n              // Clip to trailing newlines only if the motion goes forward.\n              clipToLine(cm, curStart, curEnd);\n            }\n            mode = 'char';\n            var exclusive = !motionArgs.inclusive || linewise;\n            cmSel = makeCmSelection(cm, {\n              anchor: curStart,\n              head: curEnd\n            }, mode, exclusive);\n          }\n          cm.setSelections(cmSel.ranges, cmSel.primary);\n          vim.lastMotion = null;\n          operatorArgs.repeat = repeat; // For indent in visual mode.\n          operatorArgs.registerName = registerName;\n          // Keep track of linewise as it affects how paste and change behave.\n          operatorArgs.linewise = linewise;\n          var operatorMoveTo = operators[operator](\n            cm, operatorArgs, cmSel.ranges, oldAnchor, newHead);\n          if (vim.visualMode) {\n            exitVisualMode(cm, operatorMoveTo != null);\n          }\n          if (operatorMoveTo) {\n            cm.setCursor(operatorMoveTo);\n          }\n        }\n      },\n      recordLastEdit: function(vim, inputState, actionCommand) {\n        var macroModeState = vimGlobalState.macroModeState;\n        if (macroModeState.isPlaying) { return; }\n        vim.lastEditInputState = inputState;\n        vim.lastEditActionCommand = actionCommand;\n        macroModeState.lastInsertModeChanges.changes = [];\n        macroModeState.lastInsertModeChanges.expectCursorActivityForChange = false;\n        macroModeState.lastInsertModeChanges.visualBlock = vim.visualBlock ? vim.sel.head.line - vim.sel.anchor.line : 0;\n      }\n    };\n\n    /**\n     * typedef {Object{line:number,ch:number}} Cursor An object containing the\n     *     position of the cursor.\n     */\n    // All of the functions below return Cursor objects.\n    var motions = {\n      moveToTopLine: function(cm, _head, motionArgs) {\n        var line = getUserVisibleLines(cm).top + motionArgs.repeat -1;\n        return new Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));\n      },\n      moveToMiddleLine: function(cm) {\n        var range = getUserVisibleLines(cm);\n        var line = Math.floor((range.top + range.bottom) * 0.5);\n        return new Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));\n      },\n      moveToBottomLine: function(cm, _head, motionArgs) {\n        var line = getUserVisibleLines(cm).bottom - motionArgs.repeat +1;\n        return new Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));\n      },\n      expandToLine: function(_cm, head, motionArgs) {\n        // Expands forward to end of line, and then to next line if repeat is\n        // >1. Does not handle backward motion!\n        var cur = head;\n        return new Pos(cur.line + motionArgs.repeat - 1, Infinity);\n      },\n      findNext: function(cm, _head, motionArgs) {\n        var state = getSearchState(cm);\n        var query = state.getQuery();\n        if (!query) {\n          return;\n        }\n        var prev = !motionArgs.forward;\n        // If search is initiated with ? instead of /, negate direction.\n        prev = (state.isReversed()) ? !prev : prev;\n        highlightSearchMatches(cm, query);\n        return findNext(cm, prev/** prev */, query, motionArgs.repeat);\n      },\n      /**\n       * Find and select the next occurrence of the search query. If the cursor is currently\n       * within a match, then find and select the current match. Otherwise, find the next occurrence in the\n       * appropriate direction.\n       *\n       * This differs from `findNext` in the following ways:\n       *\n       * 1. Instead of only returning the \"from\", this returns a \"from\", \"to\" range.\n       * 2. If the cursor is currently inside a search match, this selects the current match\n       *    instead of the next match.\n       * 3. If there is no associated operator, this will turn on visual mode.\n       */\n      findAndSelectNextInclusive: function(cm, _head, motionArgs, vim, prevInputState) {\n        var state = getSearchState(cm);\n        var query = state.getQuery();\n\n        if (!query) {\n          return;\n        }\n\n        var prev = !motionArgs.forward;\n        prev = (state.isReversed()) ? !prev : prev;\n\n        // next: [from, to] | null\n        var next = findNextFromAndToInclusive(cm, prev, query, motionArgs.repeat, vim);\n\n        // No matches.\n        if (!next) {\n          return;\n        }\n\n        // If there's an operator that will be executed, return the selection.\n        if (prevInputState.operator) {\n          return next;\n        }\n\n        // At this point, we know that there is no accompanying operator -- let's\n        // deal with visual mode in order to select an appropriate match.\n\n        var from = next[0];\n        // For whatever reason, when we use the \"to\" as returned by searchcursor.js directly,\n        // the resulting selection is extended by 1 char. Let's shrink it so that only the\n        // match is selected.\n        var to = new Pos(next[1].line, next[1].ch - 1);\n\n        if (vim.visualMode) {\n          // If we were in visualLine or visualBlock mode, get out of it.\n          if (vim.visualLine || vim.visualBlock) {\n            vim.visualLine = false;\n            vim.visualBlock = false;\n            CodeMirror.signal(cm, \"vim-mode-change\", {mode: \"visual\", subMode: \"\"});\n          }\n\n          // If we're currently in visual mode, we should extend the selection to include\n          // the search result.\n          var anchor = vim.sel.anchor;\n          if (anchor) {\n            if (state.isReversed()) {\n              if (motionArgs.forward) {\n                return [anchor, from];\n              }\n\n              return [anchor, to];\n            } else {\n              if (motionArgs.forward) {\n                return [anchor, to];\n              }\n\n              return [anchor, from];\n            }\n          }\n        } else {\n          // Let's turn visual mode on.\n          vim.visualMode = true;\n          vim.visualLine = false;\n          vim.visualBlock = false;\n          CodeMirror.signal(cm, \"vim-mode-change\", {mode: \"visual\", subMode: \"\"});\n        }\n\n        return prev ? [to, from] : [from, to];\n      },\n      goToMark: function(cm, _head, motionArgs, vim) {\n        var pos = getMarkPos(cm, vim, motionArgs.selectedCharacter);\n        if (pos) {\n          return motionArgs.linewise ? { line: pos.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(pos.line)) } : pos;\n        }\n        return null;\n      },\n      moveToOtherHighlightedEnd: function(cm, _head, motionArgs, vim) {\n        if (vim.visualBlock && motionArgs.sameLine) {\n          var sel = vim.sel;\n          return [\n            clipCursorToContent(cm, new Pos(sel.anchor.line, sel.head.ch)),\n            clipCursorToContent(cm, new Pos(sel.head.line, sel.anchor.ch))\n          ];\n        } else {\n          return ([vim.sel.head, vim.sel.anchor]);\n        }\n      },\n      jumpToMark: function(cm, head, motionArgs, vim) {\n        var best = head;\n        for (var i = 0; i < motionArgs.repeat; i++) {\n          var cursor = best;\n          for (var key in vim.marks) {\n            if (!isLowerCase(key)) {\n              continue;\n            }\n            var mark = vim.marks[key].find();\n            var isWrongDirection = (motionArgs.forward) ?\n              cursorIsBefore(mark, cursor) : cursorIsBefore(cursor, mark);\n\n            if (isWrongDirection) {\n              continue;\n            }\n            if (motionArgs.linewise && (mark.line == cursor.line)) {\n              continue;\n            }\n\n            var equal = cursorEqual(cursor, best);\n            var between = (motionArgs.forward) ?\n              cursorIsBetween(cursor, mark, best) :\n              cursorIsBetween(best, mark, cursor);\n\n            if (equal || between) {\n              best = mark;\n            }\n          }\n        }\n\n        if (motionArgs.linewise) {\n          // Vim places the cursor on the first non-whitespace character of\n          // the line if there is one, else it places the cursor at the end\n          // of the line, regardless of whether a mark was found.\n          best = new Pos(best.line, findFirstNonWhiteSpaceCharacter(cm.getLine(best.line)));\n        }\n        return best;\n      },\n      moveByCharacters: function(_cm, head, motionArgs) {\n        var cur = head;\n        var repeat = motionArgs.repeat;\n        var ch = motionArgs.forward ? cur.ch + repeat : cur.ch - repeat;\n        return new Pos(cur.line, ch);\n      },\n      moveByLines: function(cm, head, motionArgs, vim) {\n        var cur = head;\n        var endCh = cur.ch;\n        // Depending what our last motion was, we may want to do different\n        // things. If our last motion was moving vertically, we want to\n        // preserve the HPos from our last horizontal move.  If our last motion\n        // was going to the end of a line, moving vertically we should go to\n        // the end of the line, etc.\n        switch (vim.lastMotion) {\n          case this.moveByLines:\n          case this.moveByDisplayLines:\n          case this.moveByScroll:\n          case this.moveToColumn:\n          case this.moveToEol:\n            endCh = vim.lastHPos;\n            break;\n          default:\n            vim.lastHPos = endCh;\n        }\n        var repeat = motionArgs.repeat+(motionArgs.repeatOffset||0);\n        var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat;\n        var first = cm.firstLine();\n        var last = cm.lastLine();\n        var posV = cm.findPosV(cur, (motionArgs.forward ? repeat : -repeat), 'line', vim.lastHSPos);\n        var hasMarkedText = motionArgs.forward ? posV.line > line : posV.line < line;\n        if (hasMarkedText) {\n          line = posV.line;\n          endCh = posV.ch;\n        }\n        // Vim go to line begin or line end when cursor at first/last line and\n        // move to previous/next line is triggered.\n        if (line < first && cur.line == first){\n          return this.moveToStartOfLine(cm, head, motionArgs, vim);\n        } else if (line > last && cur.line == last){\n            return moveToEol(cm, head, motionArgs, vim, true);\n        }\n        if (motionArgs.toFirstChar){\n          endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line));\n          vim.lastHPos = endCh;\n        }\n        vim.lastHSPos = cm.charCoords(new Pos(line, endCh),'div').left;\n        return new Pos(line, endCh);\n      },\n      moveByDisplayLines: function(cm, head, motionArgs, vim) {\n        var cur = head;\n        switch (vim.lastMotion) {\n          case this.moveByDisplayLines:\n          case this.moveByScroll:\n          case this.moveByLines:\n          case this.moveToColumn:\n          case this.moveToEol:\n            break;\n          default:\n            vim.lastHSPos = cm.charCoords(cur,'div').left;\n        }\n        var repeat = motionArgs.repeat;\n        var res=cm.findPosV(cur,(motionArgs.forward ? repeat : -repeat),'line',vim.lastHSPos);\n        if (res.hitSide) {\n          if (motionArgs.forward) {\n            var lastCharCoords = cm.charCoords(res, 'div');\n            var goalCoords = { top: lastCharCoords.top + 8, left: vim.lastHSPos };\n            var res = cm.coordsChar(goalCoords, 'div');\n          } else {\n            var resCoords = cm.charCoords(new Pos(cm.firstLine(), 0), 'div');\n            resCoords.left = vim.lastHSPos;\n            res = cm.coordsChar(resCoords, 'div');\n          }\n        }\n        vim.lastHPos = res.ch;\n        return res;\n      },\n      moveByPage: function(cm, head, motionArgs) {\n        // CodeMirror only exposes functions that move the cursor page down, so\n        // doing this bad hack to move the cursor and move it back. evalInput\n        // will move the cursor to where it should be in the end.\n        var curStart = head;\n        var repeat = motionArgs.repeat;\n        return cm.findPosV(curStart, (motionArgs.forward ? repeat : -repeat), 'page');\n      },\n      moveByParagraph: function(cm, head, motionArgs) {\n        var dir = motionArgs.forward ? 1 : -1;\n        return findParagraph(cm, head, motionArgs.repeat, dir);\n      },\n      moveBySentence: function(cm, head, motionArgs) {\n        var dir = motionArgs.forward ? 1 : -1;\n        return findSentence(cm, head, motionArgs.repeat, dir);\n      },\n      moveByScroll: function(cm, head, motionArgs, vim) {\n        var scrollbox = cm.getScrollInfo();\n        var curEnd = null;\n        var repeat = motionArgs.repeat;\n        if (!repeat) {\n          repeat = scrollbox.clientHeight / (2 * cm.defaultTextHeight());\n        }\n        var orig = cm.charCoords(head, 'local');\n        motionArgs.repeat = repeat;\n        curEnd = motions.moveByDisplayLines(cm, head, motionArgs, vim);\n        if (!curEnd) {\n          return null;\n        }\n        var dest = cm.charCoords(curEnd, 'local');\n        cm.scrollTo(null, scrollbox.top + dest.top - orig.top);\n        return curEnd;\n      },\n      moveByWords: function(cm, head, motionArgs) {\n        return moveToWord(cm, head, motionArgs.repeat, !!motionArgs.forward,\n            !!motionArgs.wordEnd, !!motionArgs.bigWord);\n      },\n      moveTillCharacter: function(cm, _head, motionArgs) {\n        var repeat = motionArgs.repeat;\n        var curEnd = moveToCharacter(cm, repeat, motionArgs.forward,\n            motionArgs.selectedCharacter);\n        var increment = motionArgs.forward ? -1 : 1;\n        recordLastCharacterSearch(increment, motionArgs);\n        if (!curEnd) return null;\n        curEnd.ch += increment;\n        return curEnd;\n      },\n      moveToCharacter: function(cm, head, motionArgs) {\n        var repeat = motionArgs.repeat;\n        recordLastCharacterSearch(0, motionArgs);\n        return moveToCharacter(cm, repeat, motionArgs.forward,\n            motionArgs.selectedCharacter) || head;\n      },\n      moveToSymbol: function(cm, head, motionArgs) {\n        var repeat = motionArgs.repeat;\n        return findSymbol(cm, repeat, motionArgs.forward,\n            motionArgs.selectedCharacter) || head;\n      },\n      moveToColumn: function(cm, head, motionArgs, vim) {\n        var repeat = motionArgs.repeat;\n        // repeat is equivalent to which column we want to move to!\n        vim.lastHPos = repeat - 1;\n        vim.lastHSPos = cm.charCoords(head,'div').left;\n        return moveToColumn(cm, repeat);\n      },\n      moveToEol: function(cm, head, motionArgs, vim) {\n        return moveToEol(cm, head, motionArgs, vim, false);\n      },\n      moveToFirstNonWhiteSpaceCharacter: function(cm, head) {\n        // Go to the start of the line where the text begins, or the end for\n        // whitespace-only lines\n        var cursor = head;\n        return new Pos(cursor.line,\n                   findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)));\n      },\n      moveToMatchedSymbol: function(cm, head) {\n        var cursor = head;\n        var line = cursor.line;\n        var ch = cursor.ch;\n        var lineText = cm.getLine(line);\n        var symbol;\n        for (; ch < lineText.length; ch++) {\n          symbol = lineText.charAt(ch);\n          if (symbol && isMatchableSymbol(symbol)) {\n            var style = cm.getTokenTypeAt(new Pos(line, ch + 1));\n            if (style !== \"string\" && style !== \"comment\") {\n              break;\n            }\n          }\n        }\n        if (ch < lineText.length) {\n          // Only include angle brackets in analysis if they are being matched.\n          var re = (ch === '<' || ch === '>') ? /[(){}[\\]<>]/ : /[(){}[\\]]/;\n          var matched = cm.findMatchingBracket(new Pos(line, ch), {bracketRegex: re});\n          return matched.to;\n        } else {\n          return cursor;\n        }\n      },\n      moveToStartOfLine: function(_cm, head) {\n        return new Pos(head.line, 0);\n      },\n      moveToLineOrEdgeOfDocument: function(cm, _head, motionArgs) {\n        var lineNum = motionArgs.forward ? cm.lastLine() : cm.firstLine();\n        if (motionArgs.repeatIsExplicit) {\n          lineNum = motionArgs.repeat - cm.getOption('firstLineNumber');\n        }\n        return new Pos(lineNum,\n                   findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)));\n      },\n      moveToStartOfDisplayLine: function(cm) {\n        cm.execCommand(\"goLineLeft\");\n        return cm.getCursor();\n      },\n      moveToEndOfDisplayLine: function(cm) {\n        cm.execCommand(\"goLineRight\");\n        var head = cm.getCursor();\n        if (head.sticky == \"before\") head.ch--;\n        return head;\n      },\n      textObjectManipulation: function(cm, head, motionArgs, vim) {\n        // TODO: lots of possible exceptions that can be thrown here. Try da(\n        //     outside of a () block.\n        var mirroredPairs = {'(': ')', ')': '(',\n                             '{': '}', '}': '{',\n                             '[': ']', ']': '[',\n                             '<': '>', '>': '<'};\n        var selfPaired = {'\\'': true, '\"': true, '`': true};\n\n        var character = motionArgs.selectedCharacter;\n        // 'b' refers to  '()' block.\n        // 'B' refers to  '{}' block.\n        if (character == 'b') {\n          character = '(';\n        } else if (character == 'B') {\n          character = '{';\n        }\n\n        // Inclusive is the difference between a and i\n        // TODO: Instead of using the additional text object map to perform text\n        //     object operations, merge the map into the defaultKeyMap and use\n        //     motionArgs to define behavior. Define separate entries for 'aw',\n        //     'iw', 'a[', 'i[', etc.\n        var inclusive = !motionArgs.textObjectInner;\n\n        var tmp;\n        if (mirroredPairs[character]) {\n          tmp = selectCompanionObject(cm, head, character, inclusive);\n        } else if (selfPaired[character]) {\n          tmp = findBeginningAndEnd(cm, head, character, inclusive);\n        } else if (character === 'W') {\n          tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,\n                                                     true /** bigWord */);\n        } else if (character === 'w') {\n          tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,\n                                                     false /** bigWord */);\n        } else if (character === 'p') {\n          tmp = findParagraph(cm, head, motionArgs.repeat, 0, inclusive);\n          motionArgs.linewise = true;\n          if (vim.visualMode) {\n            if (!vim.visualLine) { vim.visualLine = true; }\n          } else {\n            var operatorArgs = vim.inputState.operatorArgs;\n            if (operatorArgs) { operatorArgs.linewise = true; }\n            tmp.end.line--;\n          }\n        } else if (character === 't') {\n          tmp = expandTagUnderCursor(cm, head, inclusive);\n        } else if (character === 's') {\n          // account for cursor on end of sentence symbol\n          var content = cm.getLine(head.line);\n          if (head.ch > 0 && isEndOfSentenceSymbol(content[head.ch])) {\n            head.ch -= 1;\n          }\n          var end = getSentence(cm, head, motionArgs.repeat, 1, inclusive);\n          var start = getSentence(cm, head, motionArgs.repeat, -1, inclusive);\n          // closer vim behaviour, 'a' only takes the space after the sentence if there is one before and after\n          if (isWhiteSpaceString(cm.getLine(start.line)[start.ch])\n              && isWhiteSpaceString(cm.getLine(end.line)[end.ch -1])) {\n            start = {line: start.line, ch: start.ch + 1};\n          }\n          tmp = {start: start, end: end};\n        } else {\n          // No text object defined for this, don't move.\n          return null;\n        }\n\n        if (!cm.state.vim.visualMode) {\n          return [tmp.start, tmp.end];\n        } else {\n          return expandSelection(cm, tmp.start, tmp.end);\n        }\n      },\n\n      repeatLastCharacterSearch: function(cm, head, motionArgs) {\n        var lastSearch = vimGlobalState.lastCharacterSearch;\n        var repeat = motionArgs.repeat;\n        var forward = motionArgs.forward === lastSearch.forward;\n        var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1);\n        cm.moveH(-increment, 'char');\n        motionArgs.inclusive = forward ? true : false;\n        var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter);\n        if (!curEnd) {\n          cm.moveH(increment, 'char');\n          return head;\n        }\n        curEnd.ch += increment;\n        return curEnd;\n      }\n    };\n\n    function defineMotion(name, fn) {\n      motions[name] = fn;\n    }\n\n    function fillArray(val, times) {\n      var arr = [];\n      for (var i = 0; i < times; i++) {\n        arr.push(val);\n      }\n      return arr;\n    }\n    /**\n     * An operator acts on a text selection. It receives the list of selections\n     * as input. The corresponding CodeMirror selection is guaranteed to\n    * match the input selection.\n     */\n    var operators = {\n      change: function(cm, args, ranges) {\n        var finalHead, text;\n        var vim = cm.state.vim;\n        var anchor = ranges[0].anchor,\n            head = ranges[0].head;\n        if (!vim.visualMode) {\n          text = cm.getRange(anchor, head);\n          var lastState = vim.lastEditInputState || {};\n          if (lastState.motion == \"moveByWords\" && !isWhiteSpaceString(text)) {\n            // Exclude trailing whitespace if the range is not all whitespace.\n            var match = (/\\s+$/).exec(text);\n            if (match && lastState.motionArgs && lastState.motionArgs.forward) {\n              head = offsetCursor(head, 0, - match[0].length);\n              text = text.slice(0, - match[0].length);\n            }\n          }\n          var prevLineEnd = new Pos(anchor.line - 1, Number.MAX_VALUE);\n          var wasLastLine = cm.firstLine() == cm.lastLine();\n          if (head.line > cm.lastLine() && args.linewise && !wasLastLine) {\n            cm.replaceRange('', prevLineEnd, head);\n          } else {\n            cm.replaceRange('', anchor, head);\n          }\n          if (args.linewise) {\n            // Push the next line back down, if there is a next line.\n            if (!wasLastLine) {\n              cm.setCursor(prevLineEnd);\n              CodeMirror.commands.newlineAndIndent(cm);\n            }\n            // make sure cursor ends up at the end of the line.\n            anchor.ch = Number.MAX_VALUE;\n          }\n          finalHead = anchor;\n        } else if (args.fullLine) {\n            head.ch = Number.MAX_VALUE;\n            head.line--;\n            cm.setSelection(anchor, head);\n            text = cm.getSelection();\n            cm.replaceSelection(\"\");\n            finalHead = anchor;\n        } else {\n          text = cm.getSelection();\n          var replacement = fillArray('', ranges.length);\n          cm.replaceSelections(replacement);\n          finalHead = cursorMin(ranges[0].head, ranges[0].anchor);\n        }\n        vimGlobalState.registerController.pushText(\n            args.registerName, 'change', text,\n            args.linewise, ranges.length > 1);\n        actions.enterInsertMode(cm, {head: finalHead}, cm.state.vim);\n      },\n      // delete is a javascript keyword.\n      'delete': function(cm, args, ranges) {\n        var finalHead, text;\n        var vim = cm.state.vim;\n        if (!vim.visualBlock) {\n          var anchor = ranges[0].anchor,\n              head = ranges[0].head;\n          if (args.linewise &&\n              head.line != cm.firstLine() &&\n              anchor.line == cm.lastLine() &&\n              anchor.line == head.line - 1) {\n            // Special case for dd on last line (and first line).\n            if (anchor.line == cm.firstLine()) {\n              anchor.ch = 0;\n            } else {\n              anchor = new Pos(anchor.line - 1, lineLength(cm, anchor.line - 1));\n            }\n          }\n          text = cm.getRange(anchor, head);\n          cm.replaceRange('', anchor, head);\n          finalHead = anchor;\n          if (args.linewise) {\n            finalHead = motions.moveToFirstNonWhiteSpaceCharacter(cm, anchor);\n          }\n        } else {\n          text = cm.getSelection();\n          var replacement = fillArray('', ranges.length);\n          cm.replaceSelections(replacement);\n          finalHead = cursorMin(ranges[0].head, ranges[0].anchor);\n        }\n        vimGlobalState.registerController.pushText(\n            args.registerName, 'delete', text,\n            args.linewise, vim.visualBlock);\n        return clipCursorToContent(cm, finalHead);\n      },\n      indent: function(cm, args, ranges) {\n        var vim = cm.state.vim;\n        if (cm.indentMore) {\n          var repeat = (vim.visualMode) ? args.repeat : 1;\n          for (var j = 0; j < repeat; j++) {\n            if (args.indentRight) cm.indentMore();\n            else cm.indentLess();\n          }\n        } else {\n          var startLine = ranges[0].anchor.line;\n          var endLine = vim.visualBlock ?\n            ranges[ranges.length - 1].anchor.line :\n            ranges[0].head.line;\n          // In visual mode, n> shifts the selection right n times, instead of\n          // shifting n lines right once.\n          var repeat = (vim.visualMode) ? args.repeat : 1;\n          if (args.linewise) {\n            // The only way to delete a newline is to delete until the start of\n            // the next line, so in linewise mode evalInput will include the next\n            // line. We don't want this in indent, so we go back a line.\n            endLine--;\n          }\n          for (var i = startLine; i <= endLine; i++) {\n            for (var j = 0; j < repeat; j++) {\n              cm.indentLine(i, args.indentRight);\n            }\n          }\n        }\n        return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);\n      },\n      indentAuto: function(cm, _args, ranges) {\n        cm.execCommand(\"indentAuto\");\n        return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);\n      },\n      changeCase: function(cm, args, ranges, oldAnchor, newHead) {\n        var selections = cm.getSelections();\n        var swapped = [];\n        var toLower = args.toLower;\n        for (var j = 0; j < selections.length; j++) {\n          var toSwap = selections[j];\n          var text = '';\n          if (toLower === true) {\n            text = toSwap.toLowerCase();\n          } else if (toLower === false) {\n            text = toSwap.toUpperCase();\n          } else {\n            for (var i = 0; i < toSwap.length; i++) {\n              var character = toSwap.charAt(i);\n              text += isUpperCase(character) ? character.toLowerCase() :\n                  character.toUpperCase();\n            }\n          }\n          swapped.push(text);\n        }\n        cm.replaceSelections(swapped);\n        if (args.shouldMoveCursor){\n          return newHead;\n        } else if (!cm.state.vim.visualMode && args.linewise && ranges[0].anchor.line + 1 == ranges[0].head.line) {\n          return motions.moveToFirstNonWhiteSpaceCharacter(cm, oldAnchor);\n        } else if (args.linewise){\n          return oldAnchor;\n        } else {\n          return cursorMin(ranges[0].anchor, ranges[0].head);\n        }\n      },\n      yank: function(cm, args, ranges, oldAnchor) {\n        var vim = cm.state.vim;\n        var text = cm.getSelection();\n        var endPos = vim.visualMode\n          ? cursorMin(vim.sel.anchor, vim.sel.head, ranges[0].head, ranges[0].anchor)\n          : oldAnchor;\n        vimGlobalState.registerController.pushText(\n            args.registerName, 'yank',\n            text, args.linewise, vim.visualBlock);\n        return endPos;\n      }\n    };\n\n    function defineOperator(name, fn) {\n      operators[name] = fn;\n    }\n\n    var actions = {\n      jumpListWalk: function(cm, actionArgs, vim) {\n        if (vim.visualMode) {\n          return;\n        }\n        var repeat = actionArgs.repeat;\n        var forward = actionArgs.forward;\n        var jumpList = vimGlobalState.jumpList;\n\n        var mark = jumpList.move(cm, forward ? repeat : -repeat);\n        var markPos = mark ? mark.find() : undefined;\n        markPos = markPos ? markPos : cm.getCursor();\n        cm.setCursor(markPos);\n      },\n      scroll: function(cm, actionArgs, vim) {\n        if (vim.visualMode) {\n          return;\n        }\n        var repeat = actionArgs.repeat || 1;\n        var lineHeight = cm.defaultTextHeight();\n        var top = cm.getScrollInfo().top;\n        var delta = lineHeight * repeat;\n        var newPos = actionArgs.forward ? top + delta : top - delta;\n        var cursor = copyCursor(cm.getCursor());\n        var cursorCoords = cm.charCoords(cursor, 'local');\n        if (actionArgs.forward) {\n          if (newPos > cursorCoords.top) {\n             cursor.line += (newPos - cursorCoords.top) / lineHeight;\n             cursor.line = Math.ceil(cursor.line);\n             cm.setCursor(cursor);\n             cursorCoords = cm.charCoords(cursor, 'local');\n             cm.scrollTo(null, cursorCoords.top);\n          } else {\n             // Cursor stays within bounds.  Just reposition the scroll window.\n             cm.scrollTo(null, newPos);\n          }\n        } else {\n          var newBottom = newPos + cm.getScrollInfo().clientHeight;\n          if (newBottom < cursorCoords.bottom) {\n             cursor.line -= (cursorCoords.bottom - newBottom) / lineHeight;\n             cursor.line = Math.floor(cursor.line);\n             cm.setCursor(cursor);\n             cursorCoords = cm.charCoords(cursor, 'local');\n             cm.scrollTo(\n                 null, cursorCoords.bottom - cm.getScrollInfo().clientHeight);\n          } else {\n             // Cursor stays within bounds.  Just reposition the scroll window.\n             cm.scrollTo(null, newPos);\n          }\n        }\n      },\n      scrollToCursor: function(cm, actionArgs) {\n        var lineNum = cm.getCursor().line;\n        var charCoords = cm.charCoords(new Pos(lineNum, 0), 'local');\n        var height = cm.getScrollInfo().clientHeight;\n        var y = charCoords.top;\n        switch (actionArgs.position) {\n          case 'center': y = charCoords.bottom - height / 2;\n            break;\n          case 'bottom':\n            var lineLastCharPos = new Pos(lineNum, cm.getLine(lineNum).length - 1);\n            var lineLastCharCoords = cm.charCoords(lineLastCharPos, 'local');\n            var lineHeight = lineLastCharCoords.bottom - y;\n            y = y - height + lineHeight;\n            break;\n        }\n        cm.scrollTo(null, y);\n      },\n      replayMacro: function(cm, actionArgs, vim) {\n        var registerName = actionArgs.selectedCharacter;\n        var repeat = actionArgs.repeat;\n        var macroModeState = vimGlobalState.macroModeState;\n        if (registerName == '@') {\n          registerName = macroModeState.latestRegister;\n        } else {\n          macroModeState.latestRegister = registerName;\n        }\n        while(repeat--){\n          executeMacroRegister(cm, vim, macroModeState, registerName);\n        }\n      },\n      enterMacroRecordMode: function(cm, actionArgs) {\n        var macroModeState = vimGlobalState.macroModeState;\n        var registerName = actionArgs.selectedCharacter;\n        if (vimGlobalState.registerController.isValidRegister(registerName)) {\n          macroModeState.enterMacroRecordMode(cm, registerName);\n        }\n      },\n      toggleOverwrite: function(cm) {\n        if (!cm.state.overwrite) {\n          cm.toggleOverwrite(true);\n          cm.setOption('keyMap', 'vim-replace');\n          CodeMirror.signal(cm, \"vim-mode-change\", {mode: \"replace\"});\n        } else {\n          cm.toggleOverwrite(false);\n          cm.setOption('keyMap', 'vim-insert');\n          CodeMirror.signal(cm, \"vim-mode-change\", {mode: \"insert\"});\n        }\n      },\n      enterInsertMode: function(cm, actionArgs, vim) {\n        if (cm.getOption('readOnly')) { return; }\n        vim.insertMode = true;\n        vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1;\n        var insertAt = (actionArgs) ? actionArgs.insertAt : null;\n        var sel = vim.sel;\n        var head = actionArgs.head || cm.getCursor('head');\n        var height = cm.listSelections().length;\n        if (insertAt == 'eol') {\n          head = new Pos(head.line, lineLength(cm, head.line));\n        } else if (insertAt == 'bol') {\n          head = new Pos(head.line, 0);\n        } else if (insertAt == 'charAfter') {\n          head = offsetCursor(head, 0, 1);\n        } else if (insertAt == 'firstNonBlank') {\n          head = motions.moveToFirstNonWhiteSpaceCharacter(cm, head);\n        } else if (insertAt == 'startOfSelectedArea') {\n          if (!vim.visualMode)\n              return;\n          if (!vim.visualBlock) {\n            if (sel.head.line < sel.anchor.line) {\n              head = sel.head;\n            } else {\n              head = new Pos(sel.anchor.line, 0);\n            }\n          } else {\n            head = new Pos(\n                Math.min(sel.head.line, sel.anchor.line),\n                Math.min(sel.head.ch, sel.anchor.ch));\n            height = Math.abs(sel.head.line - sel.anchor.line) + 1;\n          }\n        } else if (insertAt == 'endOfSelectedArea') {\n            if (!vim.visualMode)\n              return;\n          if (!vim.visualBlock) {\n            if (sel.head.line >= sel.anchor.line) {\n              head = offsetCursor(sel.head, 0, 1);\n            } else {\n              head = new Pos(sel.anchor.line, 0);\n            }\n          } else {\n            head = new Pos(\n                Math.min(sel.head.line, sel.anchor.line),\n                Math.max(sel.head.ch, sel.anchor.ch) + 1);\n            height = Math.abs(sel.head.line - sel.anchor.line) + 1;\n          }\n        } else if (insertAt == 'inplace') {\n          if (vim.visualMode){\n            return;\n          }\n        } else if (insertAt == 'lastEdit') {\n          head = getLastEditPos(cm) || head;\n        }\n        cm.setOption('disableInput', false);\n        if (actionArgs && actionArgs.replace) {\n          // Handle Replace-mode as a special case of insert mode.\n          cm.toggleOverwrite(true);\n          cm.setOption('keyMap', 'vim-replace');\n          CodeMirror.signal(cm, \"vim-mode-change\", {mode: \"replace\"});\n        } else {\n          cm.toggleOverwrite(false);\n          cm.setOption('keyMap', 'vim-insert');\n          CodeMirror.signal(cm, \"vim-mode-change\", {mode: \"insert\"});\n        }\n        if (!vimGlobalState.macroModeState.isPlaying) {\n          // Only record if not replaying.\n          cm.on('change', onChange);\n          CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);\n        }\n        if (vim.visualMode) {\n          exitVisualMode(cm);\n        }\n        selectForInsert(cm, head, height);\n      },\n      toggleVisualMode: function(cm, actionArgs, vim) {\n        var repeat = actionArgs.repeat;\n        var anchor = cm.getCursor();\n        var head;\n        // TODO: The repeat should actually select number of characters/lines\n        //     equal to the repeat times the size of the previous visual\n        //     operation.\n        if (!vim.visualMode) {\n          // Entering visual mode\n          vim.visualMode = true;\n          vim.visualLine = !!actionArgs.linewise;\n          vim.visualBlock = !!actionArgs.blockwise;\n          head = clipCursorToContent(\n              cm, new Pos(anchor.line, anchor.ch + repeat - 1));\n          vim.sel = {\n            anchor: anchor,\n            head: head\n          };\n          CodeMirror.signal(cm, \"vim-mode-change\", {mode: \"visual\", subMode: vim.visualLine ? \"linewise\" : vim.visualBlock ? \"blockwise\" : \"\"});\n          updateCmSelection(cm);\n          updateMark(cm, vim, '<', cursorMin(anchor, head));\n          updateMark(cm, vim, '>', cursorMax(anchor, head));\n        } else if (vim.visualLine ^ actionArgs.linewise ||\n            vim.visualBlock ^ actionArgs.blockwise) {\n          // Toggling between modes\n          vim.visualLine = !!actionArgs.linewise;\n          vim.visualBlock = !!actionArgs.blockwise;\n          CodeMirror.signal(cm, \"vim-mode-change\", {mode: \"visual\", subMode: vim.visualLine ? \"linewise\" : vim.visualBlock ? \"blockwise\" : \"\"});\n          updateCmSelection(cm);\n        } else {\n          exitVisualMode(cm);\n        }\n      },\n      reselectLastSelection: function(cm, _actionArgs, vim) {\n        var lastSelection = vim.lastSelection;\n        if (vim.visualMode) {\n          updateLastSelection(cm, vim);\n        }\n        if (lastSelection) {\n          var anchor = lastSelection.anchorMark.find();\n          var head = lastSelection.headMark.find();\n          if (!anchor || !head) {\n            // If the marks have been destroyed due to edits, do nothing.\n            return;\n          }\n          vim.sel = {\n            anchor: anchor,\n            head: head\n          };\n          vim.visualMode = true;\n          vim.visualLine = lastSelection.visualLine;\n          vim.visualBlock = lastSelection.visualBlock;\n          updateCmSelection(cm);\n          updateMark(cm, vim, '<', cursorMin(anchor, head));\n          updateMark(cm, vim, '>', cursorMax(anchor, head));\n          CodeMirror.signal(cm, 'vim-mode-change', {\n            mode: 'visual',\n            subMode: vim.visualLine ? 'linewise' :\n                     vim.visualBlock ? 'blockwise' : ''});\n        }\n      },\n      joinLines: function(cm, actionArgs, vim) {\n        var curStart, curEnd;\n        if (vim.visualMode) {\n          curStart = cm.getCursor('anchor');\n          curEnd = cm.getCursor('head');\n          if (cursorIsBefore(curEnd, curStart)) {\n            var tmp = curEnd;\n            curEnd = curStart;\n            curStart = tmp;\n          }\n          curEnd.ch = lineLength(cm, curEnd.line) - 1;\n        } else {\n          // Repeat is the number of lines to join. Minimum 2 lines.\n          var repeat = Math.max(actionArgs.repeat, 2);\n          curStart = cm.getCursor();\n          curEnd = clipCursorToContent(cm, new Pos(curStart.line + repeat - 1,\n                                               Infinity));\n        }\n        var finalCh = 0;\n        for (var i = curStart.line; i < curEnd.line; i++) {\n          finalCh = lineLength(cm, curStart.line);\n          var tmp = new Pos(curStart.line + 1,\n                        lineLength(cm, curStart.line + 1));\n          var text = cm.getRange(curStart, tmp);\n          text = actionArgs.keepSpaces\n            ? text.replace(/\\n\\r?/g, '')\n            : text.replace(/\\n\\s*/g, ' ');\n          cm.replaceRange(text, curStart, tmp);\n        }\n        var curFinalPos = new Pos(curStart.line, finalCh);\n        if (vim.visualMode) {\n          exitVisualMode(cm, false);\n        }\n        cm.setCursor(curFinalPos);\n      },\n      newLineAndEnterInsertMode: function(cm, actionArgs, vim) {\n        vim.insertMode = true;\n        var insertAt = copyCursor(cm.getCursor());\n        if (insertAt.line === cm.firstLine() && !actionArgs.after) {\n          // Special case for inserting newline before start of document.\n          cm.replaceRange('\\n', new Pos(cm.firstLine(), 0));\n          cm.setCursor(cm.firstLine(), 0);\n        } else {\n          insertAt.line = (actionArgs.after) ? insertAt.line :\n              insertAt.line - 1;\n          insertAt.ch = lineLength(cm, insertAt.line);\n          cm.setCursor(insertAt);\n          var newlineFn = CodeMirror.commands.newlineAndIndentContinueComment ||\n              CodeMirror.commands.newlineAndIndent;\n          newlineFn(cm);\n        }\n        this.enterInsertMode(cm, { repeat: actionArgs.repeat }, vim);\n      },\n      paste: function(cm, actionArgs, vim) {\n        var cur = copyCursor(cm.getCursor());\n        var register = vimGlobalState.registerController.getRegister(\n            actionArgs.registerName);\n        var text = register.toString();\n        if (!text) {\n          return;\n        }\n        if (actionArgs.matchIndent) {\n          var tabSize = cm.getOption(\"tabSize\");\n          // length that considers tabs and tabSize\n          var whitespaceLength = function(str) {\n            var tabs = (str.split(\"\\t\").length - 1);\n            var spaces = (str.split(\" \").length - 1);\n            return tabs * tabSize + spaces * 1;\n          };\n          var currentLine = cm.getLine(cm.getCursor().line);\n          var indent = whitespaceLength(currentLine.match(/^\\s*/)[0]);\n          // chomp last newline b/c don't want it to match /^\\s*/gm\n          var chompedText = text.replace(/\\n$/, '');\n          var wasChomped = text !== chompedText;\n          var firstIndent = whitespaceLength(text.match(/^\\s*/)[0]);\n          var text = chompedText.replace(/^\\s*/gm, function(wspace) {\n            var newIndent = indent + (whitespaceLength(wspace) - firstIndent);\n            if (newIndent < 0) {\n              return \"\";\n            }\n            else if (cm.getOption(\"indentWithTabs\")) {\n              var quotient = Math.floor(newIndent / tabSize);\n              return Array(quotient + 1).join('\\t');\n            }\n            else {\n              return Array(newIndent + 1).join(' ');\n            }\n          });\n          text += wasChomped ? \"\\n\" : \"\";\n        }\n        if (actionArgs.repeat > 1) {\n          var text = Array(actionArgs.repeat + 1).join(text);\n        }\n        var linewise = register.linewise;\n        var blockwise = register.blockwise;\n        if (blockwise) {\n          text = text.split('\\n');\n          if (linewise) {\n              text.pop();\n          }\n          for (var i = 0; i < text.length; i++) {\n            text[i] = (text[i] == '') ? ' ' : text[i];\n          }\n          cur.ch += actionArgs.after ? 1 : 0;\n          cur.ch = Math.min(lineLength(cm, cur.line), cur.ch);\n        } else if (linewise) {\n          if(vim.visualMode) {\n            text = vim.visualLine ? text.slice(0, -1) : '\\n' + text.slice(0, text.length - 1) + '\\n';\n          } else if (actionArgs.after) {\n            // Move the newline at the end to the start instead, and paste just\n            // before the newline character of the line we are on right now.\n            text = '\\n' + text.slice(0, text.length - 1);\n            cur.ch = lineLength(cm, cur.line);\n          } else {\n            cur.ch = 0;\n          }\n        } else {\n          cur.ch += actionArgs.after ? 1 : 0;\n        }\n        var curPosFinal;\n        var idx;\n        if (vim.visualMode) {\n          //  save the pasted text for reselection if the need arises\n          vim.lastPastedText = text;\n          var lastSelectionCurEnd;\n          var selectedArea = getSelectedAreaRange(cm, vim);\n          var selectionStart = selectedArea[0];\n          var selectionEnd = selectedArea[1];\n          var selectedText = cm.getSelection();\n          var selections = cm.listSelections();\n          var emptyStrings = new Array(selections.length).join('1').split('1');\n          // save the curEnd marker before it get cleared due to cm.replaceRange.\n          if (vim.lastSelection) {\n            lastSelectionCurEnd = vim.lastSelection.headMark.find();\n          }\n          // push the previously selected text to unnamed register\n          vimGlobalState.registerController.unnamedRegister.setText(selectedText);\n          if (blockwise) {\n            // first delete the selected text\n            cm.replaceSelections(emptyStrings);\n            // Set new selections as per the block length of the yanked text\n            selectionEnd = new Pos(selectionStart.line + text.length-1, selectionStart.ch);\n            cm.setCursor(selectionStart);\n            selectBlock(cm, selectionEnd);\n            cm.replaceSelections(text);\n            curPosFinal = selectionStart;\n          } else if (vim.visualBlock) {\n            cm.replaceSelections(emptyStrings);\n            cm.setCursor(selectionStart);\n            cm.replaceRange(text, selectionStart, selectionStart);\n            curPosFinal = selectionStart;\n          } else {\n            cm.replaceRange(text, selectionStart, selectionEnd);\n            curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1);\n          }\n          // restore the the curEnd marker\n          if(lastSelectionCurEnd) {\n            vim.lastSelection.headMark = cm.setBookmark(lastSelectionCurEnd);\n          }\n          if (linewise) {\n            curPosFinal.ch=0;\n          }\n        } else {\n          if (blockwise) {\n            cm.setCursor(cur);\n            for (var i = 0; i < text.length; i++) {\n              var line = cur.line+i;\n              if (line > cm.lastLine()) {\n                cm.replaceRange('\\n',  new Pos(line, 0));\n              }\n              var lastCh = lineLength(cm, line);\n              if (lastCh < cur.ch) {\n                extendLineToColumn(cm, line, cur.ch);\n              }\n            }\n            cm.setCursor(cur);\n            selectBlock(cm, new Pos(cur.line + text.length-1, cur.ch));\n            cm.replaceSelections(text);\n            curPosFinal = cur;\n          } else {\n            cm.replaceRange(text, cur);\n            // Now fine tune the cursor to where we want it.\n            if (linewise && actionArgs.after) {\n              curPosFinal = new Pos(\n              cur.line + 1,\n              findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1)));\n            } else if (linewise && !actionArgs.after) {\n              curPosFinal = new Pos(\n                cur.line,\n                findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line)));\n            } else if (!linewise && actionArgs.after) {\n              idx = cm.indexFromPos(cur);\n              curPosFinal = cm.posFromIndex(idx + text.length - 1);\n            } else {\n              idx = cm.indexFromPos(cur);\n              curPosFinal = cm.posFromIndex(idx + text.length);\n            }\n          }\n        }\n        if (vim.visualMode) {\n          exitVisualMode(cm, false);\n        }\n        cm.setCursor(curPosFinal);\n      },\n      undo: function(cm, actionArgs) {\n        cm.operation(function() {\n          repeatFn(cm, CodeMirror.commands.undo, actionArgs.repeat)();\n          cm.setCursor(cm.getCursor('anchor'));\n        });\n      },\n      redo: function(cm, actionArgs) {\n        repeatFn(cm, CodeMirror.commands.redo, actionArgs.repeat)();\n      },\n      setRegister: function(_cm, actionArgs, vim) {\n        vim.inputState.registerName = actionArgs.selectedCharacter;\n      },\n      setMark: function(cm, actionArgs, vim) {\n        var markName = actionArgs.selectedCharacter;\n        updateMark(cm, vim, markName, cm.getCursor());\n      },\n      replace: function(cm, actionArgs, vim) {\n        var replaceWith = actionArgs.selectedCharacter;\n        var curStart = cm.getCursor();\n        var replaceTo;\n        var curEnd;\n        var selections = cm.listSelections();\n        if (vim.visualMode) {\n          curStart = cm.getCursor('start');\n          curEnd = cm.getCursor('end');\n        } else {\n          var line = cm.getLine(curStart.line);\n          replaceTo = curStart.ch + actionArgs.repeat;\n          if (replaceTo > line.length) {\n            replaceTo=line.length;\n          }\n          curEnd = new Pos(curStart.line, replaceTo);\n        }\n        if (replaceWith=='\\n') {\n          if (!vim.visualMode) cm.replaceRange('', curStart, curEnd);\n          // special case, where vim help says to replace by just one line-break\n          (CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent)(cm);\n        } else {\n          var replaceWithStr = cm.getRange(curStart, curEnd);\n          //replace all characters in range by selected, but keep linebreaks\n          replaceWithStr = replaceWithStr.replace(/[^\\n]/g, replaceWith);\n          if (vim.visualBlock) {\n            // Tabs are split in visua block before replacing\n            var spaces = new Array(cm.getOption(\"tabSize\")+1).join(' ');\n            replaceWithStr = cm.getSelection();\n            replaceWithStr = replaceWithStr.replace(/\\t/g, spaces).replace(/[^\\n]/g, replaceWith).split('\\n');\n            cm.replaceSelections(replaceWithStr);\n          } else {\n            cm.replaceRange(replaceWithStr, curStart, curEnd);\n          }\n          if (vim.visualMode) {\n            curStart = cursorIsBefore(selections[0].anchor, selections[0].head) ?\n                         selections[0].anchor : selections[0].head;\n            cm.setCursor(curStart);\n            exitVisualMode(cm, false);\n          } else {\n            cm.setCursor(offsetCursor(curEnd, 0, -1));\n          }\n        }\n      },\n      incrementNumberToken: function(cm, actionArgs) {\n        var cur = cm.getCursor();\n        var lineStr = cm.getLine(cur.line);\n        var re = /(-?)(?:(0x)([\\da-f]+)|(0b|0|)(\\d+))/gi;\n        var match;\n        var start;\n        var end;\n        var numberStr;\n        while ((match = re.exec(lineStr)) !== null) {\n          start = match.index;\n          end = start + match[0].length;\n          if (cur.ch < end)break;\n        }\n        if (!actionArgs.backtrack && (end <= cur.ch))return;\n        if (match) {\n          var baseStr = match[2] || match[4];\n          var digits = match[3] || match[5];\n          var increment = actionArgs.increase ? 1 : -1;\n          var base = {'0b': 2, '0': 8, '': 10, '0x': 16}[baseStr.toLowerCase()];\n          var number = parseInt(match[1] + digits, base) + (increment * actionArgs.repeat);\n          numberStr = number.toString(base);\n          var zeroPadding = baseStr ? new Array(digits.length - numberStr.length + 1 + match[1].length).join('0') : '';\n          if (numberStr.charAt(0) === '-') {\n            numberStr = '-' + baseStr + zeroPadding + numberStr.substr(1);\n          } else {\n            numberStr = baseStr + zeroPadding + numberStr;\n          }\n          var from = new Pos(cur.line, start);\n          var to = new Pos(cur.line, end);\n          cm.replaceRange(numberStr, from, to);\n        } else {\n          return;\n        }\n        cm.setCursor(new Pos(cur.line, start + numberStr.length - 1));\n      },\n      repeatLastEdit: function(cm, actionArgs, vim) {\n        var lastEditInputState = vim.lastEditInputState;\n        if (!lastEditInputState) { return; }\n        var repeat = actionArgs.repeat;\n        if (repeat && actionArgs.repeatIsExplicit) {\n          vim.lastEditInputState.repeatOverride = repeat;\n        } else {\n          repeat = vim.lastEditInputState.repeatOverride || repeat;\n        }\n        repeatLastEdit(cm, vim, repeat, false /** repeatForInsert */);\n      },\n      indent: function(cm, actionArgs) {\n        cm.indentLine(cm.getCursor().line, actionArgs.indentRight);\n      },\n      exitInsertMode: exitInsertMode\n    };\n\n    function defineAction(name, fn) {\n      actions[name] = fn;\n    }\n\n    /*\n     * Below are miscellaneous utility functions used by vim.js\n     */\n\n    /**\n     * Clips cursor to ensure that line is within the buffer's range\n     * If includeLineBreak is true, then allow cur.ch == lineLength.\n     */\n    function clipCursorToContent(cm, cur) {\n      var vim = cm.state.vim;\n      var includeLineBreak = vim.insertMode || vim.visualMode;\n      var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine() );\n      var maxCh = lineLength(cm, line) - 1 + !!includeLineBreak;\n      var ch = Math.min(Math.max(0, cur.ch), maxCh);\n      return new Pos(line, ch);\n    }\n    function copyArgs(args) {\n      var ret = {};\n      for (var prop in args) {\n        if (args.hasOwnProperty(prop)) {\n          ret[prop] = args[prop];\n        }\n      }\n      return ret;\n    }\n    function offsetCursor(cur, offsetLine, offsetCh) {\n      if (typeof offsetLine === 'object') {\n        offsetCh = offsetLine.ch;\n        offsetLine = offsetLine.line;\n      }\n      return new Pos(cur.line + offsetLine, cur.ch + offsetCh);\n    }\n    function commandMatches(keys, keyMap, context, inputState) {\n      // Partial matches are not applied. They inform the key handler\n      // that the current key sequence is a subsequence of a valid key\n      // sequence, so that the key buffer is not cleared.\n      var match, partial = [], full = [];\n      for (var i = 0; i < keyMap.length; i++) {\n        var command = keyMap[i];\n        if (context == 'insert' && command.context != 'insert' ||\n            command.context && command.context != context ||\n            inputState.operator && command.type == 'action' ||\n            !(match = commandMatch(keys, command.keys))) { continue; }\n        if (match == 'partial') { partial.push(command); }\n        if (match == 'full') { full.push(command); }\n      }\n      return {\n        partial: partial.length && partial,\n        full: full.length && full\n      };\n    }\n    function commandMatch(pressed, mapped) {\n      if (mapped.slice(-11) == '<character>') {\n        // Last character matches anything.\n        var prefixLen = mapped.length - 11;\n        var pressedPrefix = pressed.slice(0, prefixLen);\n        var mappedPrefix = mapped.slice(0, prefixLen);\n        return pressedPrefix == mappedPrefix && pressed.length > prefixLen ? 'full' :\n               mappedPrefix.indexOf(pressedPrefix) == 0 ? 'partial' : false;\n      } else {\n        return pressed == mapped ? 'full' :\n               mapped.indexOf(pressed) == 0 ? 'partial' : false;\n      }\n    }\n    function lastChar(keys) {\n      var match = /^.*(<[^>]+>)$/.exec(keys);\n      var selectedCharacter = match ? match[1] : keys.slice(-1);\n      if (selectedCharacter.length > 1){\n        switch(selectedCharacter){\n          case '<CR>':\n            selectedCharacter='\\n';\n            break;\n          case '<Space>':\n            selectedCharacter=' ';\n            break;\n          default:\n            selectedCharacter='';\n            break;\n        }\n      }\n      return selectedCharacter;\n    }\n    function repeatFn(cm, fn, repeat) {\n      return function() {\n        for (var i = 0; i < repeat; i++) {\n          fn(cm);\n        }\n      };\n    }\n    function copyCursor(cur) {\n      return new Pos(cur.line, cur.ch);\n    }\n    function cursorEqual(cur1, cur2) {\n      return cur1.ch == cur2.ch && cur1.line == cur2.line;\n    }\n    function cursorIsBefore(cur1, cur2) {\n      if (cur1.line < cur2.line) {\n        return true;\n      }\n      if (cur1.line == cur2.line && cur1.ch < cur2.ch) {\n        return true;\n      }\n      return false;\n    }\n    function cursorMin(cur1, cur2) {\n      if (arguments.length > 2) {\n        cur2 = cursorMin.apply(undefined, Array.prototype.slice.call(arguments, 1));\n      }\n      return cursorIsBefore(cur1, cur2) ? cur1 : cur2;\n    }\n    function cursorMax(cur1, cur2) {\n      if (arguments.length > 2) {\n        cur2 = cursorMax.apply(undefined, Array.prototype.slice.call(arguments, 1));\n      }\n      return cursorIsBefore(cur1, cur2) ? cur2 : cur1;\n    }\n    function cursorIsBetween(cur1, cur2, cur3) {\n      // returns true if cur2 is between cur1 and cur3.\n      var cur1before2 = cursorIsBefore(cur1, cur2);\n      var cur2before3 = cursorIsBefore(cur2, cur3);\n      return cur1before2 && cur2before3;\n    }\n    function lineLength(cm, lineNum) {\n      return cm.getLine(lineNum).length;\n    }\n    function trim(s) {\n      if (s.trim) {\n        return s.trim();\n      }\n      return s.replace(/^\\s+|\\s+$/g, '');\n    }\n    function escapeRegex(s) {\n      return s.replace(/([.?*+$\\[\\]\\/\\\\(){}|\\-])/g, '\\\\$1');\n    }\n    function extendLineToColumn(cm, lineNum, column) {\n      var endCh = lineLength(cm, lineNum);\n      var spaces = new Array(column-endCh+1).join(' ');\n      cm.setCursor(new Pos(lineNum, endCh));\n      cm.replaceRange(spaces, cm.getCursor());\n    }\n    // This functions selects a rectangular block\n    // of text with selectionEnd as any of its corner\n    // Height of block:\n    // Difference in selectionEnd.line and first/last selection.line\n    // Width of the block:\n    // Distance between selectionEnd.ch and any(first considered here) selection.ch\n    function selectBlock(cm, selectionEnd) {\n      var selections = [], ranges = cm.listSelections();\n      var head = copyCursor(cm.clipPos(selectionEnd));\n      var isClipped = !cursorEqual(selectionEnd, head);\n      var curHead = cm.getCursor('head');\n      var primIndex = getIndex(ranges, curHead);\n      var wasClipped = cursorEqual(ranges[primIndex].head, ranges[primIndex].anchor);\n      var max = ranges.length - 1;\n      var index = max - primIndex > primIndex ? max : 0;\n      var base = ranges[index].anchor;\n\n      var firstLine = Math.min(base.line, head.line);\n      var lastLine = Math.max(base.line, head.line);\n      var baseCh = base.ch, headCh = head.ch;\n\n      var dir = ranges[index].head.ch - baseCh;\n      var newDir = headCh - baseCh;\n      if (dir > 0 && newDir <= 0) {\n        baseCh++;\n        if (!isClipped) { headCh--; }\n      } else if (dir < 0 && newDir >= 0) {\n        baseCh--;\n        if (!wasClipped) { headCh++; }\n      } else if (dir < 0 && newDir == -1) {\n        baseCh--;\n        headCh++;\n      }\n      for (var line = firstLine; line <= lastLine; line++) {\n        var range = {anchor: new Pos(line, baseCh), head: new Pos(line, headCh)};\n        selections.push(range);\n      }\n      cm.setSelections(selections);\n      selectionEnd.ch = headCh;\n      base.ch = baseCh;\n      return base;\n    }\n    function selectForInsert(cm, head, height) {\n      var sel = [];\n      for (var i = 0; i < height; i++) {\n        var lineHead = offsetCursor(head, i, 0);\n        sel.push({anchor: lineHead, head: lineHead});\n      }\n      cm.setSelections(sel, 0);\n    }\n    // getIndex returns the index of the cursor in the selections.\n    function getIndex(ranges, cursor, end) {\n      for (var i = 0; i < ranges.length; i++) {\n        var atAnchor = end != 'head' && cursorEqual(ranges[i].anchor, cursor);\n        var atHead = end != 'anchor' && cursorEqual(ranges[i].head, cursor);\n        if (atAnchor || atHead) {\n          return i;\n        }\n      }\n      return -1;\n    }\n    function getSelectedAreaRange(cm, vim) {\n      var lastSelection = vim.lastSelection;\n      var getCurrentSelectedAreaRange = function() {\n        var selections = cm.listSelections();\n        var start =  selections[0];\n        var end = selections[selections.length-1];\n        var selectionStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;\n        var selectionEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;\n        return [selectionStart, selectionEnd];\n      };\n      var getLastSelectedAreaRange = function() {\n        var selectionStart = cm.getCursor();\n        var selectionEnd = cm.getCursor();\n        var block = lastSelection.visualBlock;\n        if (block) {\n          var width = block.width;\n          var height = block.height;\n          selectionEnd = new Pos(selectionStart.line + height, selectionStart.ch + width);\n          var selections = [];\n          // selectBlock creates a 'proper' rectangular block.\n          // We do not want that in all cases, so we manually set selections.\n          for (var i = selectionStart.line; i < selectionEnd.line; i++) {\n            var anchor = new Pos(i, selectionStart.ch);\n            var head = new Pos(i, selectionEnd.ch);\n            var range = {anchor: anchor, head: head};\n            selections.push(range);\n          }\n          cm.setSelections(selections);\n        } else {\n          var start = lastSelection.anchorMark.find();\n          var end = lastSelection.headMark.find();\n          var line = end.line - start.line;\n          var ch = end.ch - start.ch;\n          selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch};\n          if (lastSelection.visualLine) {\n            selectionStart = new Pos(selectionStart.line, 0);\n            selectionEnd = new Pos(selectionEnd.line, lineLength(cm, selectionEnd.line));\n          }\n          cm.setSelection(selectionStart, selectionEnd);\n        }\n        return [selectionStart, selectionEnd];\n      };\n      if (!vim.visualMode) {\n      // In case of replaying the action.\n        return getLastSelectedAreaRange();\n      } else {\n        return getCurrentSelectedAreaRange();\n      }\n    }\n    // Updates the previous selection with the current selection's values. This\n    // should only be called in visual mode.\n    function updateLastSelection(cm, vim) {\n      var anchor = vim.sel.anchor;\n      var head = vim.sel.head;\n      // To accommodate the effect of lastPastedText in the last selection\n      if (vim.lastPastedText) {\n        head = cm.posFromIndex(cm.indexFromPos(anchor) + vim.lastPastedText.length);\n        vim.lastPastedText = null;\n      }\n      vim.lastSelection = {'anchorMark': cm.setBookmark(anchor),\n                           'headMark': cm.setBookmark(head),\n                           'anchor': copyCursor(anchor),\n                           'head': copyCursor(head),\n                           'visualMode': vim.visualMode,\n                           'visualLine': vim.visualLine,\n                           'visualBlock': vim.visualBlock};\n    }\n    function expandSelection(cm, start, end) {\n      var sel = cm.state.vim.sel;\n      var head = sel.head;\n      var anchor = sel.anchor;\n      var tmp;\n      if (cursorIsBefore(end, start)) {\n        tmp = end;\n        end = start;\n        start = tmp;\n      }\n      if (cursorIsBefore(head, anchor)) {\n        head = cursorMin(start, head);\n        anchor = cursorMax(anchor, end);\n      } else {\n        anchor = cursorMin(start, anchor);\n        head = cursorMax(head, end);\n        head = offsetCursor(head, 0, -1);\n        if (head.ch == -1 && head.line != cm.firstLine()) {\n          head = new Pos(head.line - 1, lineLength(cm, head.line - 1));\n        }\n      }\n      return [anchor, head];\n    }\n    /**\n     * Updates the CodeMirror selection to match the provided vim selection.\n     * If no arguments are given, it uses the current vim selection state.\n     */\n    function updateCmSelection(cm, sel, mode) {\n      var vim = cm.state.vim;\n      sel = sel || vim.sel;\n      var mode = mode ||\n        vim.visualLine ? 'line' : vim.visualBlock ? 'block' : 'char';\n      var cmSel = makeCmSelection(cm, sel, mode);\n      cm.setSelections(cmSel.ranges, cmSel.primary);\n    }\n    function makeCmSelection(cm, sel, mode, exclusive) {\n      var head = copyCursor(sel.head);\n      var anchor = copyCursor(sel.anchor);\n      if (mode == 'char') {\n        var headOffset = !exclusive && !cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;\n        var anchorOffset = cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;\n        head = offsetCursor(sel.head, 0, headOffset);\n        anchor = offsetCursor(sel.anchor, 0, anchorOffset);\n        return {\n          ranges: [{anchor: anchor, head: head}],\n          primary: 0\n        };\n      } else if (mode == 'line') {\n        if (!cursorIsBefore(sel.head, sel.anchor)) {\n          anchor.ch = 0;\n\n          var lastLine = cm.lastLine();\n          if (head.line > lastLine) {\n            head.line = lastLine;\n          }\n          head.ch = lineLength(cm, head.line);\n        } else {\n          head.ch = 0;\n          anchor.ch = lineLength(cm, anchor.line);\n        }\n        return {\n          ranges: [{anchor: anchor, head: head}],\n          primary: 0\n        };\n      } else if (mode == 'block') {\n        var top = Math.min(anchor.line, head.line),\n            fromCh = anchor.ch,\n            bottom = Math.max(anchor.line, head.line),\n            toCh = head.ch;\n        if (fromCh < toCh) { toCh += 1; }\n        else { fromCh += 1; }        var height = bottom - top + 1;\n        var primary = head.line == top ? 0 : height - 1;\n        var ranges = [];\n        for (var i = 0; i < height; i++) {\n          ranges.push({\n            anchor: new Pos(top + i, fromCh),\n            head: new Pos(top + i, toCh)\n          });\n        }\n        return {\n          ranges: ranges,\n          primary: primary\n        };\n      }\n    }\n    function getHead(cm) {\n      var cur = cm.getCursor('head');\n      if (cm.getSelection().length == 1) {\n        // Small corner case when only 1 character is selected. The \"real\"\n        // head is the left of head and anchor.\n        cur = cursorMin(cur, cm.getCursor('anchor'));\n      }\n      return cur;\n    }\n\n    /**\n     * If moveHead is set to false, the CodeMirror selection will not be\n     * touched. The caller assumes the responsibility of putting the cursor\n    * in the right place.\n     */\n    function exitVisualMode(cm, moveHead) {\n      var vim = cm.state.vim;\n      if (moveHead !== false) {\n        cm.setCursor(clipCursorToContent(cm, vim.sel.head));\n      }\n      updateLastSelection(cm, vim);\n      vim.visualMode = false;\n      vim.visualLine = false;\n      vim.visualBlock = false;\n      if (!vim.insertMode) CodeMirror.signal(cm, \"vim-mode-change\", {mode: \"normal\"});\n    }\n\n    // Remove any trailing newlines from the selection. For\n    // example, with the caret at the start of the last word on the line,\n    // 'dw' should word, but not the newline, while 'w' should advance the\n    // caret to the first character of the next line.\n    function clipToLine(cm, curStart, curEnd) {\n      var selection = cm.getRange(curStart, curEnd);\n      // Only clip if the selection ends with trailing newline + whitespace\n      if (/\\n\\s*$/.test(selection)) {\n        var lines = selection.split('\\n');\n        // We know this is all whitespace.\n        lines.pop();\n\n        // Cases:\n        // 1. Last word is an empty line - do not clip the trailing '\\n'\n        // 2. Last word is not an empty line - clip the trailing '\\n'\n        var line;\n        // Find the line containing the last word, and clip all whitespace up\n        // to it.\n        for (var line = lines.pop(); lines.length > 0 && line && isWhiteSpaceString(line); line = lines.pop()) {\n          curEnd.line--;\n          curEnd.ch = 0;\n        }\n        // If the last word is not an empty line, clip an additional newline\n        if (line) {\n          curEnd.line--;\n          curEnd.ch = lineLength(cm, curEnd.line);\n        } else {\n          curEnd.ch = 0;\n        }\n      }\n    }\n\n    // Expand the selection to line ends.\n    function expandSelectionToLine(_cm, curStart, curEnd) {\n      curStart.ch = 0;\n      curEnd.ch = 0;\n      curEnd.line++;\n    }\n\n    function findFirstNonWhiteSpaceCharacter(text) {\n      if (!text) {\n        return 0;\n      }\n      var firstNonWS = text.search(/\\S/);\n      return firstNonWS == -1 ? text.length : firstNonWS;\n    }\n\n    function expandWordUnderCursor(cm, inclusive, _forward, bigWord, noSymbol) {\n      var cur = getHead(cm);\n      var line = cm.getLine(cur.line);\n      var idx = cur.ch;\n\n      // Seek to first word or non-whitespace character, depending on if\n      // noSymbol is true.\n      var test = noSymbol ? wordCharTest[0] : bigWordCharTest [0];\n      while (!test(line.charAt(idx))) {\n        idx++;\n        if (idx >= line.length) { return null; }\n      }\n\n      if (bigWord) {\n        test = bigWordCharTest[0];\n      } else {\n        test = wordCharTest[0];\n        if (!test(line.charAt(idx))) {\n          test = wordCharTest[1];\n        }\n      }\n\n      var end = idx, start = idx;\n      while (test(line.charAt(end)) && end < line.length) { end++; }\n      while (test(line.charAt(start)) && start >= 0) { start--; }\n      start++;\n\n      if (inclusive) {\n        // If present, include all whitespace after word.\n        // Otherwise, include all whitespace before word, except indentation.\n        var wordEnd = end;\n        while (/\\s/.test(line.charAt(end)) && end < line.length) { end++; }\n        if (wordEnd == end) {\n          var wordStart = start;\n          while (/\\s/.test(line.charAt(start - 1)) && start > 0) { start--; }\n          if (!start) { start = wordStart; }\n        }\n      }\n      return { start: new Pos(cur.line, start), end: new Pos(cur.line, end) };\n    }\n\n    /**\n     * Depends on the following:\n     *\n     * - editor mode should be htmlmixedmode / xml\n     * - mode/xml/xml.js should be loaded\n     * - addon/fold/xml-fold.js should be loaded\n     *\n     * If any of the above requirements are not true, this function noops.\n     *\n     * This is _NOT_ a 100% accurate implementation of vim tag text objects.\n     * The following caveats apply (based off cursory testing, I'm sure there\n     * are other discrepancies):\n     *\n     * - Does not work inside comments:\n     *   ```\n     *   <!-- <div>broken</div> -->\n     *   ```\n     * - Does not work when tags have different cases:\n     *   ```\n     *   <div>broken</DIV>\n     *   ```\n     * - Does not work when cursor is inside a broken tag:\n     *   ```\n     *   <div><brok><en></div>\n     *   ```\n     */\n    function expandTagUnderCursor(cm, head, inclusive) {\n      var cur = head;\n      if (!CodeMirror.findMatchingTag || !CodeMirror.findEnclosingTag) {\n        return { start: cur, end: cur };\n      }\n\n      var tags = CodeMirror.findMatchingTag(cm, head) || CodeMirror.findEnclosingTag(cm, head);\n      if (!tags || !tags.open || !tags.close) {\n        return { start: cur, end: cur };\n      }\n\n      if (inclusive) {\n        return { start: tags.open.from, end: tags.close.to };\n      }\n      return { start: tags.open.to, end: tags.close.from };\n    }\n\n    function recordJumpPosition(cm, oldCur, newCur) {\n      if (!cursorEqual(oldCur, newCur)) {\n        vimGlobalState.jumpList.add(cm, oldCur, newCur);\n      }\n    }\n\n    function recordLastCharacterSearch(increment, args) {\n        vimGlobalState.lastCharacterSearch.increment = increment;\n        vimGlobalState.lastCharacterSearch.forward = args.forward;\n        vimGlobalState.lastCharacterSearch.selectedCharacter = args.selectedCharacter;\n    }\n\n    var symbolToMode = {\n        '(': 'bracket', ')': 'bracket', '{': 'bracket', '}': 'bracket',\n        '[': 'section', ']': 'section',\n        '*': 'comment', '/': 'comment',\n        'm': 'method', 'M': 'method',\n        '#': 'preprocess'\n    };\n    var findSymbolModes = {\n      bracket: {\n        isComplete: function(state) {\n          if (state.nextCh === state.symb) {\n            state.depth++;\n            if (state.depth >= 1)return true;\n          } else if (state.nextCh === state.reverseSymb) {\n            state.depth--;\n          }\n          return false;\n        }\n      },\n      section: {\n        init: function(state) {\n          state.curMoveThrough = true;\n          state.symb = (state.forward ? ']' : '[') === state.symb ? '{' : '}';\n        },\n        isComplete: function(state) {\n          return state.index === 0 && state.nextCh === state.symb;\n        }\n      },\n      comment: {\n        isComplete: function(state) {\n          var found = state.lastCh === '*' && state.nextCh === '/';\n          state.lastCh = state.nextCh;\n          return found;\n        }\n      },\n      // TODO: The original Vim implementation only operates on level 1 and 2.\n      // The current implementation doesn't check for code block level and\n      // therefore it operates on any levels.\n      method: {\n        init: function(state) {\n          state.symb = (state.symb === 'm' ? '{' : '}');\n          state.reverseSymb = state.symb === '{' ? '}' : '{';\n        },\n        isComplete: function(state) {\n          if (state.nextCh === state.symb)return true;\n          return false;\n        }\n      },\n      preprocess: {\n        init: function(state) {\n          state.index = 0;\n        },\n        isComplete: function(state) {\n          if (state.nextCh === '#') {\n            var token = state.lineText.match(/^#(\\w+)/)[1];\n            if (token === 'endif') {\n              if (state.forward && state.depth === 0) {\n                return true;\n              }\n              state.depth++;\n            } else if (token === 'if') {\n              if (!state.forward && state.depth === 0) {\n                return true;\n              }\n              state.depth--;\n            }\n            if (token === 'else' && state.depth === 0)return true;\n          }\n          return false;\n        }\n      }\n    };\n    function findSymbol(cm, repeat, forward, symb) {\n      var cur = copyCursor(cm.getCursor());\n      var increment = forward ? 1 : -1;\n      var endLine = forward ? cm.lineCount() : -1;\n      var curCh = cur.ch;\n      var line = cur.line;\n      var lineText = cm.getLine(line);\n      var state = {\n        lineText: lineText,\n        nextCh: lineText.charAt(curCh),\n        lastCh: null,\n        index: curCh,\n        symb: symb,\n        reverseSymb: (forward ?  { ')': '(', '}': '{' } : { '(': ')', '{': '}' })[symb],\n        forward: forward,\n        depth: 0,\n        curMoveThrough: false\n      };\n      var mode = symbolToMode[symb];\n      if (!mode)return cur;\n      var init = findSymbolModes[mode].init;\n      var isComplete = findSymbolModes[mode].isComplete;\n      if (init) { init(state); }\n      while (line !== endLine && repeat) {\n        state.index += increment;\n        state.nextCh = state.lineText.charAt(state.index);\n        if (!state.nextCh) {\n          line += increment;\n          state.lineText = cm.getLine(line) || '';\n          if (increment > 0) {\n            state.index = 0;\n          } else {\n            var lineLen = state.lineText.length;\n            state.index = (lineLen > 0) ? (lineLen-1) : 0;\n          }\n          state.nextCh = state.lineText.charAt(state.index);\n        }\n        if (isComplete(state)) {\n          cur.line = line;\n          cur.ch = state.index;\n          repeat--;\n        }\n      }\n      if (state.nextCh || state.curMoveThrough) {\n        return new Pos(line, state.index);\n      }\n      return cur;\n    }\n\n    /*\n     * Returns the boundaries of the next word. If the cursor in the middle of\n     * the word, then returns the boundaries of the current word, starting at\n     * the cursor. If the cursor is at the start/end of a word, and we are going\n     * forward/backward, respectively, find the boundaries of the next word.\n     *\n     * @param {CodeMirror} cm CodeMirror object.\n     * @param {Cursor} cur The cursor position.\n     * @param {boolean} forward True to search forward. False to search\n     *     backward.\n     * @param {boolean} bigWord True if punctuation count as part of the word.\n     *     False if only [a-zA-Z0-9] characters count as part of the word.\n     * @param {boolean} emptyLineIsWord True if empty lines should be treated\n     *     as words.\n     * @return {Object{from:number, to:number, line: number}} The boundaries of\n     *     the word, or null if there are no more words.\n     */\n    function findWord(cm, cur, forward, bigWord, emptyLineIsWord) {\n      var lineNum = cur.line;\n      var pos = cur.ch;\n      var line = cm.getLine(lineNum);\n      var dir = forward ? 1 : -1;\n      var charTests = bigWord ? bigWordCharTest: wordCharTest;\n\n      if (emptyLineIsWord && line == '') {\n        lineNum += dir;\n        line = cm.getLine(lineNum);\n        if (!isLine(cm, lineNum)) {\n          return null;\n        }\n        pos = (forward) ? 0 : line.length;\n      }\n\n      while (true) {\n        if (emptyLineIsWord && line == '') {\n          return { from: 0, to: 0, line: lineNum };\n        }\n        var stop = (dir > 0) ? line.length : -1;\n        var wordStart = stop, wordEnd = stop;\n        // Find bounds of next word.\n        while (pos != stop) {\n          var foundWord = false;\n          for (var i = 0; i < charTests.length && !foundWord; ++i) {\n            if (charTests[i](line.charAt(pos))) {\n              wordStart = pos;\n              // Advance to end of word.\n              while (pos != stop && charTests[i](line.charAt(pos))) {\n                pos += dir;\n              }\n              wordEnd = pos;\n              foundWord = wordStart != wordEnd;\n              if (wordStart == cur.ch && lineNum == cur.line &&\n                  wordEnd == wordStart + dir) {\n                // We started at the end of a word. Find the next one.\n                continue;\n              } else {\n                return {\n                  from: Math.min(wordStart, wordEnd + 1),\n                  to: Math.max(wordStart, wordEnd),\n                  line: lineNum };\n              }\n            }\n          }\n          if (!foundWord) {\n            pos += dir;\n          }\n        }\n        // Advance to next/prev line.\n        lineNum += dir;\n        if (!isLine(cm, lineNum)) {\n          return null;\n        }\n        line = cm.getLine(lineNum);\n        pos = (dir > 0) ? 0 : line.length;\n      }\n    }\n\n    /**\n     * @param {CodeMirror} cm CodeMirror object.\n     * @param {Pos} cur The position to start from.\n     * @param {int} repeat Number of words to move past.\n     * @param {boolean} forward True to search forward. False to search\n     *     backward.\n     * @param {boolean} wordEnd True to move to end of word. False to move to\n     *     beginning of word.\n     * @param {boolean} bigWord True if punctuation count as part of the word.\n     *     False if only alphabet characters count as part of the word.\n     * @return {Cursor} The position the cursor should move to.\n     */\n    function moveToWord(cm, cur, repeat, forward, wordEnd, bigWord) {\n      var curStart = copyCursor(cur);\n      var words = [];\n      if (forward && !wordEnd || !forward && wordEnd) {\n        repeat++;\n      }\n      // For 'e', empty lines are not considered words, go figure.\n      var emptyLineIsWord = !(forward && wordEnd);\n      for (var i = 0; i < repeat; i++) {\n        var word = findWord(cm, cur, forward, bigWord, emptyLineIsWord);\n        if (!word) {\n          var eodCh = lineLength(cm, cm.lastLine());\n          words.push(forward\n              ? {line: cm.lastLine(), from: eodCh, to: eodCh}\n              : {line: 0, from: 0, to: 0});\n          break;\n        }\n        words.push(word);\n        cur = new Pos(word.line, forward ? (word.to - 1) : word.from);\n      }\n      var shortCircuit = words.length != repeat;\n      var firstWord = words[0];\n      var lastWord = words.pop();\n      if (forward && !wordEnd) {\n        // w\n        if (!shortCircuit && (firstWord.from != curStart.ch || firstWord.line != curStart.line)) {\n          // We did not start in the middle of a word. Discard the extra word at the end.\n          lastWord = words.pop();\n        }\n        return new Pos(lastWord.line, lastWord.from);\n      } else if (forward && wordEnd) {\n        return new Pos(lastWord.line, lastWord.to - 1);\n      } else if (!forward && wordEnd) {\n        // ge\n        if (!shortCircuit && (firstWord.to != curStart.ch || firstWord.line != curStart.line)) {\n          // We did not start in the middle of a word. Discard the extra word at the end.\n          lastWord = words.pop();\n        }\n        return new Pos(lastWord.line, lastWord.to);\n      } else {\n        // b\n        return new Pos(lastWord.line, lastWord.from);\n      }\n    }\n\n    function moveToEol(cm, head, motionArgs, vim, keepHPos) {\n      var cur = head;\n      var retval= new Pos(cur.line + motionArgs.repeat - 1, Infinity);\n      var end=cm.clipPos(retval);\n      end.ch--;\n      if (!keepHPos) {\n        vim.lastHPos = Infinity;\n        vim.lastHSPos = cm.charCoords(end,'div').left;\n      }\n      return retval;\n    }\n\n    function moveToCharacter(cm, repeat, forward, character) {\n      var cur = cm.getCursor();\n      var start = cur.ch;\n      var idx;\n      for (var i = 0; i < repeat; i ++) {\n        var line = cm.getLine(cur.line);\n        idx = charIdxInLine(start, line, character, forward, true);\n        if (idx == -1) {\n          return null;\n        }\n        start = idx;\n      }\n      return new Pos(cm.getCursor().line, idx);\n    }\n\n    function moveToColumn(cm, repeat) {\n      // repeat is always >= 1, so repeat - 1 always corresponds\n      // to the column we want to go to.\n      var line = cm.getCursor().line;\n      return clipCursorToContent(cm, new Pos(line, repeat - 1));\n    }\n\n    function updateMark(cm, vim, markName, pos) {\n      if (!inArray(markName, validMarks)) {\n        return;\n      }\n      if (vim.marks[markName]) {\n        vim.marks[markName].clear();\n      }\n      vim.marks[markName] = cm.setBookmark(pos);\n    }\n\n    function charIdxInLine(start, line, character, forward, includeChar) {\n      // Search for char in line.\n      // motion_options: {forward, includeChar}\n      // If includeChar = true, include it too.\n      // If forward = true, search forward, else search backwards.\n      // If char is not found on this line, do nothing\n      var idx;\n      if (forward) {\n        idx = line.indexOf(character, start + 1);\n        if (idx != -1 && !includeChar) {\n          idx -= 1;\n        }\n      } else {\n        idx = line.lastIndexOf(character, start - 1);\n        if (idx != -1 && !includeChar) {\n          idx += 1;\n        }\n      }\n      return idx;\n    }\n\n    function findParagraph(cm, head, repeat, dir, inclusive) {\n      var line = head.line;\n      var min = cm.firstLine();\n      var max = cm.lastLine();\n      var start, end, i = line;\n      function isEmpty(i) { return !cm.getLine(i); }\n      function isBoundary(i, dir, any) {\n        if (any) { return isEmpty(i) != isEmpty(i + dir); }\n        return !isEmpty(i) && isEmpty(i + dir);\n      }\n      if (dir) {\n        while (min <= i && i <= max && repeat > 0) {\n          if (isBoundary(i, dir)) { repeat--; }\n          i += dir;\n        }\n        return new Pos(i, 0);\n      }\n\n      var vim = cm.state.vim;\n      if (vim.visualLine && isBoundary(line, 1, true)) {\n        var anchor = vim.sel.anchor;\n        if (isBoundary(anchor.line, -1, true)) {\n          if (!inclusive || anchor.line != line) {\n            line += 1;\n          }\n        }\n      }\n      var startState = isEmpty(line);\n      for (i = line; i <= max && repeat; i++) {\n        if (isBoundary(i, 1, true)) {\n          if (!inclusive || isEmpty(i) != startState) {\n            repeat--;\n          }\n        }\n      }\n      end = new Pos(i, 0);\n      // select boundary before paragraph for the last one\n      if (i > max && !startState) { startState = true; }\n      else { inclusive = false; }\n      for (i = line; i > min; i--) {\n        if (!inclusive || isEmpty(i) == startState || i == line) {\n          if (isBoundary(i, -1, true)) { break; }\n        }\n      }\n      start = new Pos(i, 0);\n      return { start: start, end: end };\n    }\n  function getSentence(cm, cur, repeat, dir, inclusive /*includes whitespace*/) {\n    /*\n    Takes an index object\n    {\n    line: the line string,\n    ln: line number,\n    pos: index in line,\n    dir: direction of traversal (-1 or 1)\n    }\n    and modifies the pos member to represent the\n    next valid position or sets the line to null if there are\n    no more valid positions.\n   */\n    function nextChar(curr) {\n      if (curr.pos + curr.dir < 0 || curr.pos + curr.dir >= curr.line.length) {\n          curr.line = null;\n        }\n      else {\n        curr.pos += curr.dir;\n      }\n    }\n    /*\n    Performs one iteration of traversal in forward direction\n    Returns an index object of the new location\n   */\n    function forward(cm, ln, pos, dir) {\n      var line = cm.getLine(ln);\n\n      var curr = {\n        line: line,\n        ln: ln,\n        pos: pos,\n        dir: dir,\n      };\n\n      if (curr.line === \"\") {\n        return { ln: curr.ln, pos: curr.pos };\n      }\n\n      var lastSentencePos = curr.pos;\n\n      // Move one step to skip character we start on\n      nextChar(curr);\n\n      while (curr.line !== null) {\n        lastSentencePos = curr.pos;\n        if (isEndOfSentenceSymbol(curr.line[curr.pos])) {\n          if (!inclusive) {\n            return { ln: curr.ln, pos: curr.pos + 1 };\n          } else {\n            nextChar(curr);\n            while (curr.line !== null ) {\n              if (isWhiteSpaceString(curr.line[curr.pos])) {\n                lastSentencePos = curr.pos;\n                nextChar(curr);\n              } else {\n                break;\n              }\n            }\n            return { ln: curr.ln, pos: lastSentencePos + 1, };\n          }\n        }\n        nextChar(curr);\n      }\n      return { ln: curr.ln, pos: lastSentencePos + 1 };\n    }\n\n    /*\n    Performs one iteration of traversal in reverse direction\n    Returns an index object of the new location\n   */\n    function reverse(cm, ln, pos, dir) {\n      var line = cm.getLine(ln);\n\n      var curr = {\n        line: line,\n        ln: ln,\n        pos: pos,\n        dir: dir,\n      };\n\n      if (curr.line === \"\") {\n        return { ln: curr.ln, pos: curr.pos };\n      }\n\n      var lastSentencePos = curr.pos;\n\n      // Move one step to skip character we start on\n      nextChar(curr);\n\n      while (curr.line !== null) {\n        if (!isWhiteSpaceString(curr.line[curr.pos]) && !isEndOfSentenceSymbol(curr.line[curr.pos])) {\n          lastSentencePos = curr.pos;\n        }\n\n        else if (isEndOfSentenceSymbol(curr.line[curr.pos]) ) {\n          if (!inclusive) {\n            return { ln: curr.ln, pos: lastSentencePos };\n          } else {\n              if (isWhiteSpaceString(curr.line[curr.pos + 1])) {\n                return { ln: curr.ln, pos: curr.pos + 1, };\n              } else {\n                return {ln: curr.ln, pos: lastSentencePos};\n              }\n          }\n        }\n\n        nextChar(curr);\n      }\n      curr.line = line;\n      if (inclusive && isWhiteSpaceString(curr.line[curr.pos])) {\n        return { ln: curr.ln, pos: curr.pos };\n      } else {\n        return { ln: curr.ln, pos: lastSentencePos };\n      }\n\n    }\n\n    var curr_index = {\n      ln: cur.line,\n      pos: cur.ch,\n    };\n\n    while (repeat > 0) {\n      if (dir < 0) {\n        curr_index = reverse(cm, curr_index.ln, curr_index.pos, dir);\n      }\n      else {\n        curr_index = forward(cm, curr_index.ln, curr_index.pos, dir);\n      }\n      repeat--;\n    }\n\n    return new Pos(curr_index.ln, curr_index.pos);\n  }\n\n  function findSentence(cm, cur, repeat, dir) {\n\n    /*\n    Takes an index object\n    {\n    line: the line string,\n    ln: line number,\n    pos: index in line,\n    dir: direction of traversal (-1 or 1)\n    }\n    and modifies the line, ln, and pos members to represent the\n    next valid position or sets them to null if there are\n    no more valid positions.\n   */\n      function nextChar(cm, idx) {\n        if (idx.pos + idx.dir < 0 || idx.pos + idx.dir >= idx.line.length) {\n          idx.ln += idx.dir;\n          if (!isLine(cm, idx.ln)) {\n            idx.line = null;\n            idx.ln = null;\n            idx.pos = null;\n            return;\n          }\n          idx.line = cm.getLine(idx.ln);\n          idx.pos = (idx.dir > 0) ? 0 : idx.line.length - 1;\n        }\n        else {\n          idx.pos += idx.dir;\n        }\n      }\n\n      /*\n        Performs one iteration of traversal in forward direction\n        Returns an index object of the new location\n       */\n      function forward(cm, ln, pos, dir) {\n        var line = cm.getLine(ln);\n        var stop = (line === \"\");\n\n        var curr = {\n          line: line,\n          ln: ln,\n          pos: pos,\n          dir: dir,\n        };\n\n        var last_valid = {\n          ln: curr.ln,\n          pos: curr.pos,\n        };\n\n        var skip_empty_lines = (curr.line === \"\");\n\n        // Move one step to skip character we start on\n        nextChar(cm, curr);\n\n        while (curr.line !== null) {\n          last_valid.ln = curr.ln;\n          last_valid.pos = curr.pos;\n\n          if (curr.line === \"\" && !skip_empty_lines) {\n            return { ln: curr.ln, pos: curr.pos, };\n          }\n          else if (stop && curr.line !== \"\" && !isWhiteSpaceString(curr.line[curr.pos])) {\n            return { ln: curr.ln, pos: curr.pos, };\n          }\n          else if (isEndOfSentenceSymbol(curr.line[curr.pos])\n            && !stop\n            && (curr.pos === curr.line.length - 1\n              || isWhiteSpaceString(curr.line[curr.pos + 1]))) {\n            stop = true;\n          }\n\n          nextChar(cm, curr);\n        }\n\n        /*\n          Set the position to the last non whitespace character on the last\n          valid line in the case that we reach the end of the document.\n        */\n        var line = cm.getLine(last_valid.ln);\n        last_valid.pos = 0;\n        for(var i = line.length - 1; i >= 0; --i) {\n          if (!isWhiteSpaceString(line[i])) {\n            last_valid.pos = i;\n            break;\n          }\n        }\n\n        return last_valid;\n\n      }\n\n      /*\n        Performs one iteration of traversal in reverse direction\n        Returns an index object of the new location\n       */\n      function reverse(cm, ln, pos, dir) {\n        var line = cm.getLine(ln);\n\n        var curr = {\n          line: line,\n          ln: ln,\n          pos: pos,\n          dir: dir,\n        };\n\n        var last_valid = {\n          ln: curr.ln,\n          pos: null,\n        };\n\n        var skip_empty_lines = (curr.line === \"\");\n\n        // Move one step to skip character we start on\n        nextChar(cm, curr);\n\n        while (curr.line !== null) {\n\n          if (curr.line === \"\" && !skip_empty_lines) {\n            if (last_valid.pos !== null) {\n              return last_valid;\n            }\n            else {\n              return { ln: curr.ln, pos: curr.pos };\n            }\n          }\n          else if (isEndOfSentenceSymbol(curr.line[curr.pos])\n              && last_valid.pos !== null\n              && !(curr.ln === last_valid.ln && curr.pos + 1 === last_valid.pos)) {\n            return last_valid;\n          }\n          else if (curr.line !== \"\" && !isWhiteSpaceString(curr.line[curr.pos])) {\n            skip_empty_lines = false;\n            last_valid = { ln: curr.ln, pos: curr.pos };\n          }\n\n          nextChar(cm, curr);\n        }\n\n        /*\n          Set the position to the first non whitespace character on the last\n          valid line in the case that we reach the beginning of the document.\n        */\n        var line = cm.getLine(last_valid.ln);\n        last_valid.pos = 0;\n        for(var i = 0; i < line.length; ++i) {\n          if (!isWhiteSpaceString(line[i])) {\n            last_valid.pos = i;\n            break;\n          }\n        }\n        return last_valid;\n      }\n\n      var curr_index = {\n        ln: cur.line,\n        pos: cur.ch,\n      };\n\n      while (repeat > 0) {\n        if (dir < 0) {\n          curr_index = reverse(cm, curr_index.ln, curr_index.pos, dir);\n        }\n        else {\n          curr_index = forward(cm, curr_index.ln, curr_index.pos, dir);\n        }\n        repeat--;\n      }\n\n      return new Pos(curr_index.ln, curr_index.pos);\n    }\n\n    // TODO: perhaps this finagling of start and end positions belongs\n    // in codemirror/replaceRange?\n    function selectCompanionObject(cm, head, symb, inclusive) {\n      var cur = head, start, end;\n\n      var bracketRegexp = ({\n        '(': /[()]/, ')': /[()]/,\n        '[': /[[\\]]/, ']': /[[\\]]/,\n        '{': /[{}]/, '}': /[{}]/,\n        '<': /[<>]/, '>': /[<>]/})[symb];\n      var openSym = ({\n        '(': '(', ')': '(',\n        '[': '[', ']': '[',\n        '{': '{', '}': '{',\n        '<': '<', '>': '<'})[symb];\n      var curChar = cm.getLine(cur.line).charAt(cur.ch);\n      // Due to the behavior of scanForBracket, we need to add an offset if the\n      // cursor is on a matching open bracket.\n      var offset = curChar === openSym ? 1 : 0;\n\n      start = cm.scanForBracket(new Pos(cur.line, cur.ch + offset), -1, undefined, {'bracketRegex': bracketRegexp});\n      end = cm.scanForBracket(new Pos(cur.line, cur.ch + offset), 1, undefined, {'bracketRegex': bracketRegexp});\n\n      if (!start || !end) {\n        return { start: cur, end: cur };\n      }\n\n      start = start.pos;\n      end = end.pos;\n\n      if ((start.line == end.line && start.ch > end.ch)\n          || (start.line > end.line)) {\n        var tmp = start;\n        start = end;\n        end = tmp;\n      }\n\n      if (inclusive) {\n        end.ch += 1;\n      } else {\n        start.ch += 1;\n      }\n\n      return { start: start, end: end };\n    }\n\n    // Takes in a symbol and a cursor and tries to simulate text objects that\n    // have identical opening and closing symbols\n    // TODO support across multiple lines\n    function findBeginningAndEnd(cm, head, symb, inclusive) {\n      var cur = copyCursor(head);\n      var line = cm.getLine(cur.line);\n      var chars = line.split('');\n      var start, end, i, len;\n      var firstIndex = chars.indexOf(symb);\n\n      // the decision tree is to always look backwards for the beginning first,\n      // but if the cursor is in front of the first instance of the symb,\n      // then move the cursor forward\n      if (cur.ch < firstIndex) {\n        cur.ch = firstIndex;\n        // Why is this line even here???\n        // cm.setCursor(cur.line, firstIndex+1);\n      }\n      // otherwise if the cursor is currently on the closing symbol\n      else if (firstIndex < cur.ch && chars[cur.ch] == symb) {\n        end = cur.ch; // assign end to the current cursor\n        --cur.ch; // make sure to look backwards\n      }\n\n      // if we're currently on the symbol, we've got a start\n      if (chars[cur.ch] == symb && !end) {\n        start = cur.ch + 1; // assign start to ahead of the cursor\n      } else {\n        // go backwards to find the start\n        for (i = cur.ch; i > -1 && !start; i--) {\n          if (chars[i] == symb) {\n            start = i + 1;\n          }\n        }\n      }\n\n      // look forwards for the end symbol\n      if (start && !end) {\n        for (i = start, len = chars.length; i < len && !end; i++) {\n          if (chars[i] == symb) {\n            end = i;\n          }\n        }\n      }\n\n      // nothing found\n      if (!start || !end) {\n        return { start: cur, end: cur };\n      }\n\n      // include the symbols\n      if (inclusive) {\n        --start; ++end;\n      }\n\n      return {\n        start: new Pos(cur.line, start),\n        end: new Pos(cur.line, end)\n      };\n    }\n\n    // Search functions\n    defineOption('pcre', true, 'boolean');\n    function SearchState() {}\n    SearchState.prototype = {\n      getQuery: function() {\n        return vimGlobalState.query;\n      },\n      setQuery: function(query) {\n        vimGlobalState.query = query;\n      },\n      getOverlay: function() {\n        return this.searchOverlay;\n      },\n      setOverlay: function(overlay) {\n        this.searchOverlay = overlay;\n      },\n      isReversed: function() {\n        return vimGlobalState.isReversed;\n      },\n      setReversed: function(reversed) {\n        vimGlobalState.isReversed = reversed;\n      },\n      getScrollbarAnnotate: function() {\n        return this.annotate;\n      },\n      setScrollbarAnnotate: function(annotate) {\n        this.annotate = annotate;\n      }\n    };\n    function getSearchState(cm) {\n      var vim = cm.state.vim;\n      return vim.searchState_ || (vim.searchState_ = new SearchState());\n    }\n    function splitBySlash(argString) {\n      return splitBySeparator(argString, '/');\n    }\n\n    function findUnescapedSlashes(argString) {\n      return findUnescapedSeparators(argString, '/');\n    }\n\n    function splitBySeparator(argString, separator) {\n      var slashes = findUnescapedSeparators(argString, separator) || [];\n      if (!slashes.length) return [];\n      var tokens = [];\n      // in case of strings like foo/bar\n      if (slashes[0] !== 0) return;\n      for (var i = 0; i < slashes.length; i++) {\n        if (typeof slashes[i] == 'number')\n          tokens.push(argString.substring(slashes[i] + 1, slashes[i+1]));\n      }\n      return tokens;\n    }\n\n    function findUnescapedSeparators(str, separator) {\n      if (!separator)\n        separator = '/';\n\n      var escapeNextChar = false;\n      var slashes = [];\n      for (var i = 0; i < str.length; i++) {\n        var c = str.charAt(i);\n        if (!escapeNextChar && c == separator) {\n          slashes.push(i);\n        }\n        escapeNextChar = !escapeNextChar && (c == '\\\\');\n      }\n      return slashes;\n    }\n\n    // Translates a search string from ex (vim) syntax into javascript form.\n    function translateRegex(str) {\n      // When these match, add a '\\' if unescaped or remove one if escaped.\n      var specials = '|(){';\n      // Remove, but never add, a '\\' for these.\n      var unescape = '}';\n      var escapeNextChar = false;\n      var out = [];\n      for (var i = -1; i < str.length; i++) {\n        var c = str.charAt(i) || '';\n        var n = str.charAt(i+1) || '';\n        var specialComesNext = (n && specials.indexOf(n) != -1);\n        if (escapeNextChar) {\n          if (c !== '\\\\' || !specialComesNext) {\n            out.push(c);\n          }\n          escapeNextChar = false;\n        } else {\n          if (c === '\\\\') {\n            escapeNextChar = true;\n            // Treat the unescape list as special for removing, but not adding '\\'.\n            if (n && unescape.indexOf(n) != -1) {\n              specialComesNext = true;\n            }\n            // Not passing this test means removing a '\\'.\n            if (!specialComesNext || n === '\\\\') {\n              out.push(c);\n            }\n          } else {\n            out.push(c);\n            if (specialComesNext && n !== '\\\\') {\n              out.push('\\\\');\n            }\n          }\n        }\n      }\n      return out.join('');\n    }\n\n    // Translates the replace part of a search and replace from ex (vim) syntax into\n    // javascript form.  Similar to translateRegex, but additionally fixes back references\n    // (translates '\\[0..9]' to '$[0..9]') and follows different rules for escaping '$'.\n    var charUnescapes = {'\\\\n': '\\n', '\\\\r': '\\r', '\\\\t': '\\t'};\n    function translateRegexReplace(str) {\n      var escapeNextChar = false;\n      var out = [];\n      for (var i = -1; i < str.length; i++) {\n        var c = str.charAt(i) || '';\n        var n = str.charAt(i+1) || '';\n        if (charUnescapes[c + n]) {\n          out.push(charUnescapes[c+n]);\n          i++;\n        } else if (escapeNextChar) {\n          // At any point in the loop, escapeNextChar is true if the previous\n          // character was a '\\' and was not escaped.\n          out.push(c);\n          escapeNextChar = false;\n        } else {\n          if (c === '\\\\') {\n            escapeNextChar = true;\n            if ((isNumber(n) || n === '$')) {\n              out.push('$');\n            } else if (n !== '/' && n !== '\\\\') {\n              out.push('\\\\');\n            }\n          } else {\n            if (c === '$') {\n              out.push('$');\n            }\n            out.push(c);\n            if (n === '/') {\n              out.push('\\\\');\n            }\n          }\n        }\n      }\n      return out.join('');\n    }\n\n    // Unescape \\ and / in the replace part, for PCRE mode.\n    var unescapes = {'\\\\/': '/', '\\\\\\\\': '\\\\', '\\\\n': '\\n', '\\\\r': '\\r', '\\\\t': '\\t', '\\\\&':'&'};\n    function unescapeRegexReplace(str) {\n      var stream = new CodeMirror.StringStream(str);\n      var output = [];\n      while (!stream.eol()) {\n        // Search for \\.\n        while (stream.peek() && stream.peek() != '\\\\') {\n          output.push(stream.next());\n        }\n        var matched = false;\n        for (var matcher in unescapes) {\n          if (stream.match(matcher, true)) {\n            matched = true;\n            output.push(unescapes[matcher]);\n            break;\n          }\n        }\n        if (!matched) {\n          // Don't change anything\n          output.push(stream.next());\n        }\n      }\n      return output.join('');\n    }\n\n    /**\n     * Extract the regular expression from the query and return a Regexp object.\n     * Returns null if the query is blank.\n     * If ignoreCase is passed in, the Regexp object will have the 'i' flag set.\n     * If smartCase is passed in, and the query contains upper case letters,\n     *   then ignoreCase is overridden, and the 'i' flag will not be set.\n     * If the query contains the /i in the flag part of the regular expression,\n     *   then both ignoreCase and smartCase are ignored, and 'i' will be passed\n     *   through to the Regex object.\n     */\n    function parseQuery(query, ignoreCase, smartCase) {\n      // First update the last search register\n      var lastSearchRegister = vimGlobalState.registerController.getRegister('/');\n      lastSearchRegister.setText(query);\n      // Check if the query is already a regex.\n      if (query instanceof RegExp) { return query; }\n      // First try to extract regex + flags from the input. If no flags found,\n      // extract just the regex. IE does not accept flags directly defined in\n      // the regex string in the form /regex/flags\n      var slashes = findUnescapedSlashes(query);\n      var regexPart;\n      var forceIgnoreCase;\n      if (!slashes.length) {\n        // Query looks like 'regexp'\n        regexPart = query;\n      } else {\n        // Query looks like 'regexp/...'\n        regexPart = query.substring(0, slashes[0]);\n        var flagsPart = query.substring(slashes[0]);\n        forceIgnoreCase = (flagsPart.indexOf('i') != -1);\n      }\n      if (!regexPart) {\n        return null;\n      }\n      if (!getOption('pcre')) {\n        regexPart = translateRegex(regexPart);\n      }\n      if (smartCase) {\n        ignoreCase = (/^[^A-Z]*$/).test(regexPart);\n      }\n      var regexp = new RegExp(regexPart,\n          (ignoreCase || forceIgnoreCase) ? 'im' : 'm');\n      return regexp;\n    }\n\n    /**\n     * dom - Document Object Manipulator\n     * Usage:\n     *   dom('<tag>'|<node>[, ...{<attributes>|<$styles>}|<child-node>|'<text>'])\n     * Examples:\n     *   dom('div', {id:'xyz'}, dom('p', 'CM rocks!', {$color:'red'}))\n     *   dom(document.head, dom('script', 'alert(\"hello!\")'))\n     * Not supported:\n     *   dom('p', ['arrays are objects'], Error('objects specify attributes'))\n     */\n    function dom(n) {\n      if (typeof n === 'string') n = document.createElement(n);\n      for (var a, i = 1; i < arguments.length; i++) {\n        if (!(a = arguments[i])) continue;\n        if (typeof a !== 'object') a = document.createTextNode(a);\n        if (a.nodeType) n.appendChild(a);\n        else for (var key in a) {\n          if (!Object.prototype.hasOwnProperty.call(a, key)) continue;\n          if (key[0] === '$') n.style[key.slice(1)] = a[key];\n          else n.setAttribute(key, a[key]);\n        }\n      }\n      return n;\n    }\n\n    function showConfirm(cm, template) {\n      var pre = dom('div', {$color: 'red', $whiteSpace: 'pre', class: 'cm-vim-message'}, template);\n      if (cm.openNotification) {\n        cm.openNotification(pre, {bottom: true, duration: 5000});\n      } else {\n        alert(pre.innerText);\n      }\n    }\n\n    function makePrompt(prefix, desc) {\n      return dom(document.createDocumentFragment(),\n               dom('span', {$fontFamily: 'monospace', $whiteSpace: 'pre'},\n                 prefix,\n                 dom('input', {type: 'text', autocorrect: 'off',\n                               autocapitalize: 'off', spellcheck: 'false'})),\n               desc && dom('span', {$color: '#888'}, desc));\n    }\n\n    function showPrompt(cm, options) {\n      var template = makePrompt(options.prefix, options.desc);\n      if (cm.openDialog) {\n        cm.openDialog(template, options.onClose, {\n          onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp,\n          bottom: true, selectValueOnOpen: false, value: options.value\n        });\n      }\n      else {\n        var shortText = '';\n        if (typeof options.prefix != \"string\" && options.prefix) shortText += options.prefix.textContent;\n        if (options.desc) shortText += \" \" + options.desc;\n        options.onClose(prompt(shortText, ''));\n      }\n    }\n\n    function regexEqual(r1, r2) {\n      if (r1 instanceof RegExp && r2 instanceof RegExp) {\n          var props = ['global', 'multiline', 'ignoreCase', 'source'];\n          for (var i = 0; i < props.length; i++) {\n              var prop = props[i];\n              if (r1[prop] !== r2[prop]) {\n                  return false;\n              }\n          }\n          return true;\n      }\n      return false;\n    }\n    // Returns true if the query is valid.\n    function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase) {\n      if (!rawQuery) {\n        return;\n      }\n      var state = getSearchState(cm);\n      var query = parseQuery(rawQuery, !!ignoreCase, !!smartCase);\n      if (!query) {\n        return;\n      }\n      highlightSearchMatches(cm, query);\n      if (regexEqual(query, state.getQuery())) {\n        return query;\n      }\n      state.setQuery(query);\n      return query;\n    }\n    function searchOverlay(query) {\n      if (query.source.charAt(0) == '^') {\n        var matchSol = true;\n      }\n      return {\n        token: function(stream) {\n          if (matchSol && !stream.sol()) {\n            stream.skipToEnd();\n            return;\n          }\n          var match = stream.match(query, false);\n          if (match) {\n            if (match[0].length == 0) {\n              // Matched empty string, skip to next.\n              stream.next();\n              return 'searching';\n            }\n            if (!stream.sol()) {\n              // Backtrack 1 to match \\b\n              stream.backUp(1);\n              if (!query.exec(stream.next() + match[0])) {\n                stream.next();\n                return null;\n              }\n            }\n            stream.match(query);\n            return 'searching';\n          }\n          while (!stream.eol()) {\n            stream.next();\n            if (stream.match(query, false)) break;\n          }\n        },\n        query: query\n      };\n    }\n    var highlightTimeout = 0;\n    function highlightSearchMatches(cm, query) {\n      clearTimeout(highlightTimeout);\n      highlightTimeout = setTimeout(function() {\n        if (!cm.state.vim) return;\n        var searchState = getSearchState(cm);\n        var overlay = searchState.getOverlay();\n        if (!overlay || query != overlay.query) {\n          if (overlay) {\n            cm.removeOverlay(overlay);\n          }\n          overlay = searchOverlay(query);\n          cm.addOverlay(overlay);\n          if (cm.showMatchesOnScrollbar) {\n            if (searchState.getScrollbarAnnotate()) {\n              searchState.getScrollbarAnnotate().clear();\n            }\n            searchState.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query));\n          }\n          searchState.setOverlay(overlay);\n        }\n      }, 50);\n    }\n    function findNext(cm, prev, query, repeat) {\n      if (repeat === undefined) { repeat = 1; }\n      return cm.operation(function() {\n        var pos = cm.getCursor();\n        var cursor = cm.getSearchCursor(query, pos);\n        for (var i = 0; i < repeat; i++) {\n          var found = cursor.find(prev);\n          if (i == 0 && found && cursorEqual(cursor.from(), pos)) {\n            var lastEndPos = prev ? cursor.from() : cursor.to();\n            found = cursor.find(prev);\n            if (found && !found[0] && cursorEqual(cursor.from(), lastEndPos)) {\n              if (cm.getLine(lastEndPos.line).length == lastEndPos.ch)\n                found = cursor.find(prev);\n            }\n          }\n          if (!found) {\n            // SearchCursor may have returned null because it hit EOF, wrap\n            // around and try again.\n            cursor = cm.getSearchCursor(query,\n                (prev) ? new Pos(cm.lastLine()) : new Pos(cm.firstLine(), 0) );\n            if (!cursor.find(prev)) {\n              return;\n            }\n          }\n        }\n        return cursor.from();\n      });\n    }\n    /**\n     * Pretty much the same as `findNext`, except for the following differences:\n     *\n     * 1. Before starting the search, move to the previous search. This way if our cursor is\n     * already inside a match, we should return the current match.\n     * 2. Rather than only returning the cursor's from, we return the cursor's from and to as a tuple.\n     */\n    function findNextFromAndToInclusive(cm, prev, query, repeat, vim) {\n      if (repeat === undefined) { repeat = 1; }\n      return cm.operation(function() {\n        var pos = cm.getCursor();\n        var cursor = cm.getSearchCursor(query, pos);\n\n        // Go back one result to ensure that if the cursor is currently a match, we keep it.\n        var found = cursor.find(!prev);\n\n        // If we haven't moved, go back one more (similar to if i==0 logic in findNext).\n        if (!vim.visualMode && found && cursorEqual(cursor.from(), pos)) {\n          cursor.find(!prev);\n        }\n\n        for (var i = 0; i < repeat; i++) {\n          found = cursor.find(prev);\n          if (!found) {\n            // SearchCursor may have returned null because it hit EOF, wrap\n            // around and try again.\n            cursor = cm.getSearchCursor(query,\n                (prev) ? new Pos(cm.lastLine()) : new Pos(cm.firstLine(), 0) );\n            if (!cursor.find(prev)) {\n              return;\n            }\n          }\n        }\n        return [cursor.from(), cursor.to()];\n      });\n    }\n    function clearSearchHighlight(cm) {\n      var state = getSearchState(cm);\n      cm.removeOverlay(getSearchState(cm).getOverlay());\n      state.setOverlay(null);\n      if (state.getScrollbarAnnotate()) {\n        state.getScrollbarAnnotate().clear();\n        state.setScrollbarAnnotate(null);\n      }\n    }\n    /**\n     * Check if pos is in the specified range, INCLUSIVE.\n     * Range can be specified with 1 or 2 arguments.\n     * If the first range argument is an array, treat it as an array of line\n     * numbers. Match pos against any of the lines.\n     * If the first range argument is a number,\n     *   if there is only 1 range argument, check if pos has the same line\n     *       number\n     *   if there are 2 range arguments, then check if pos is in between the two\n     *       range arguments.\n     */\n    function isInRange(pos, start, end) {\n      if (typeof pos != 'number') {\n        // Assume it is a cursor position. Get the line number.\n        pos = pos.line;\n      }\n      if (start instanceof Array) {\n        return inArray(pos, start);\n      } else {\n        if (typeof end == 'number') {\n          return (pos >= start && pos <= end);\n        } else {\n          return pos == start;\n        }\n      }\n    }\n    function getUserVisibleLines(cm) {\n      var scrollInfo = cm.getScrollInfo();\n      var occludeToleranceTop = 6;\n      var occludeToleranceBottom = 10;\n      var from = cm.coordsChar({left:0, top: occludeToleranceTop + scrollInfo.top}, 'local');\n      var bottomY = scrollInfo.clientHeight - occludeToleranceBottom + scrollInfo.top;\n      var to = cm.coordsChar({left:0, top: bottomY}, 'local');\n      return {top: from.line, bottom: to.line};\n    }\n\n    function getMarkPos(cm, vim, markName) {\n      if (markName == '\\'' || markName == '`') {\n        return vimGlobalState.jumpList.find(cm, -1) || new Pos(0, 0);\n      } else if (markName == '.') {\n        return getLastEditPos(cm);\n      }\n\n      var mark = vim.marks[markName];\n      return mark && mark.find();\n    }\n\n    function getLastEditPos(cm) {\n      var done = cm.doc.history.done;\n      for (var i = done.length; i--;) {\n        if (done[i].changes) {\n          return copyCursor(done[i].changes[0].to);\n        }\n      }\n    }\n\n    var ExCommandDispatcher = function() {\n      this.buildCommandMap_();\n    };\n    ExCommandDispatcher.prototype = {\n      processCommand: function(cm, input, opt_params) {\n        var that = this;\n        cm.operation(function () {\n          cm.curOp.isVimOp = true;\n          that._processCommand(cm, input, opt_params);\n        });\n      },\n      _processCommand: function(cm, input, opt_params) {\n        var vim = cm.state.vim;\n        var commandHistoryRegister = vimGlobalState.registerController.getRegister(':');\n        var previousCommand = commandHistoryRegister.toString();\n        if (vim.visualMode) {\n          exitVisualMode(cm);\n        }\n        var inputStream = new CodeMirror.StringStream(input);\n        // update \": with the latest command whether valid or invalid\n        commandHistoryRegister.setText(input);\n        var params = opt_params || {};\n        params.input = input;\n        try {\n          this.parseInput_(cm, inputStream, params);\n        } catch(e) {\n          showConfirm(cm, e.toString());\n          throw e;\n        }\n        var command;\n        var commandName;\n        if (!params.commandName) {\n          // If only a line range is defined, move to the line.\n          if (params.line !== undefined) {\n            commandName = 'move';\n          }\n        } else {\n          command = this.matchCommand_(params.commandName);\n          if (command) {\n            commandName = command.name;\n            if (command.excludeFromCommandHistory) {\n              commandHistoryRegister.setText(previousCommand);\n            }\n            this.parseCommandArgs_(inputStream, params, command);\n            if (command.type == 'exToKey') {\n              // Handle Ex to Key mapping.\n              for (var i = 0; i < command.toKeys.length; i++) {\n                vimApi.handleKey(cm, command.toKeys[i], 'mapping');\n              }\n              return;\n            } else if (command.type == 'exToEx') {\n              // Handle Ex to Ex mapping.\n              this.processCommand(cm, command.toInput);\n              return;\n            }\n          }\n        }\n        if (!commandName) {\n          showConfirm(cm, 'Not an editor command \":' + input + '\"');\n          return;\n        }\n        try {\n          exCommands[commandName](cm, params);\n          // Possibly asynchronous commands (e.g. substitute, which might have a\n          // user confirmation), are responsible for calling the callback when\n          // done. All others have it taken care of for them here.\n          if ((!command || !command.possiblyAsync) && params.callback) {\n            params.callback();\n          }\n        } catch(e) {\n          showConfirm(cm, e.toString());\n          throw e;\n        }\n      },\n      parseInput_: function(cm, inputStream, result) {\n        inputStream.eatWhile(':');\n        // Parse range.\n        if (inputStream.eat('%')) {\n          result.line = cm.firstLine();\n          result.lineEnd = cm.lastLine();\n        } else {\n          result.line = this.parseLineSpec_(cm, inputStream);\n          if (result.line !== undefined && inputStream.eat(',')) {\n            result.lineEnd = this.parseLineSpec_(cm, inputStream);\n          }\n        }\n\n        // Parse command name.\n        var commandMatch = inputStream.match(/^(\\w+|!!|@@|[!#&*<=>@~])/);\n        if (commandMatch) {\n          result.commandName = commandMatch[1];\n        } else {\n          result.commandName = inputStream.match(/.*/)[0];\n        }\n\n        return result;\n      },\n      parseLineSpec_: function(cm, inputStream) {\n        var numberMatch = inputStream.match(/^(\\d+)/);\n        if (numberMatch) {\n          // Absolute line number plus offset (N+M or N-M) is probably a typo,\n          // not something the user actually wanted. (NB: vim does allow this.)\n          return parseInt(numberMatch[1], 10) - 1;\n        }\n        switch (inputStream.next()) {\n          case '.':\n            return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);\n          case '$':\n            return this.parseLineSpecOffset_(inputStream, cm.lastLine());\n          case '\\'':\n            var markName = inputStream.next();\n            var markPos = getMarkPos(cm, cm.state.vim, markName);\n            if (!markPos) throw new Error('Mark not set');\n            return this.parseLineSpecOffset_(inputStream, markPos.line);\n          case '-':\n          case '+':\n            inputStream.backUp(1);\n            // Offset is relative to current line if not otherwise specified.\n            return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);\n          default:\n            inputStream.backUp(1);\n            return undefined;\n        }\n      },\n      parseLineSpecOffset_: function(inputStream, line) {\n        var offsetMatch = inputStream.match(/^([+-])?(\\d+)/);\n        if (offsetMatch) {\n          var offset = parseInt(offsetMatch[2], 10);\n          if (offsetMatch[1] == \"-\") {\n            line -= offset;\n          } else {\n            line += offset;\n          }\n        }\n        return line;\n      },\n      parseCommandArgs_: function(inputStream, params, command) {\n        if (inputStream.eol()) {\n          return;\n        }\n        params.argString = inputStream.match(/.*/)[0];\n        // Parse command-line arguments\n        var delim = command.argDelimiter || /\\s+/;\n        var args = trim(params.argString).split(delim);\n        if (args.length && args[0]) {\n          params.args = args;\n        }\n      },\n      matchCommand_: function(commandName) {\n        // Return the command in the command map that matches the shortest\n        // prefix of the passed in command name. The match is guaranteed to be\n        // unambiguous if the defaultExCommandMap's shortNames are set up\n        // correctly. (see @code{defaultExCommandMap}).\n        for (var i = commandName.length; i > 0; i--) {\n          var prefix = commandName.substring(0, i);\n          if (this.commandMap_[prefix]) {\n            var command = this.commandMap_[prefix];\n            if (command.name.indexOf(commandName) === 0) {\n              return command;\n            }\n          }\n        }\n        return null;\n      },\n      buildCommandMap_: function() {\n        this.commandMap_ = {};\n        for (var i = 0; i < defaultExCommandMap.length; i++) {\n          var command = defaultExCommandMap[i];\n          var key = command.shortName || command.name;\n          this.commandMap_[key] = command;\n        }\n      },\n      map: function(lhs, rhs, ctx) {\n        if (lhs != ':' && lhs.charAt(0) == ':') {\n          if (ctx) { throw Error('Mode not supported for ex mappings'); }\n          var commandName = lhs.substring(1);\n          if (rhs != ':' && rhs.charAt(0) == ':') {\n            // Ex to Ex mapping\n            this.commandMap_[commandName] = {\n              name: commandName,\n              type: 'exToEx',\n              toInput: rhs.substring(1),\n              user: true\n            };\n          } else {\n            // Ex to key mapping\n            this.commandMap_[commandName] = {\n              name: commandName,\n              type: 'exToKey',\n              toKeys: rhs,\n              user: true\n            };\n          }\n        } else {\n          if (rhs != ':' && rhs.charAt(0) == ':') {\n            // Key to Ex mapping.\n            var mapping = {\n              keys: lhs,\n              type: 'keyToEx',\n              exArgs: { input: rhs.substring(1) }\n            };\n            if (ctx) { mapping.context = ctx; }\n            defaultKeymap.unshift(mapping);\n          } else {\n            // Key to key mapping\n            var mapping = {\n              keys: lhs,\n              type: 'keyToKey',\n              toKeys: rhs\n            };\n            if (ctx) { mapping.context = ctx; }\n            defaultKeymap.unshift(mapping);\n          }\n        }\n      },\n      unmap: function(lhs, ctx) {\n        if (lhs != ':' && lhs.charAt(0) == ':') {\n          // Ex to Ex or Ex to key mapping\n          if (ctx) { throw Error('Mode not supported for ex mappings'); }\n          var commandName = lhs.substring(1);\n          if (this.commandMap_[commandName] && this.commandMap_[commandName].user) {\n            delete this.commandMap_[commandName];\n            return true;\n          }\n        } else {\n          // Key to Ex or key to key mapping\n          var keys = lhs;\n          for (var i = 0; i < defaultKeymap.length; i++) {\n            if (keys == defaultKeymap[i].keys\n                && defaultKeymap[i].context === ctx) {\n              defaultKeymap.splice(i, 1);\n              return true;\n            }\n          }\n        }\n      }\n    };\n\n    var exCommands = {\n      colorscheme: function(cm, params) {\n        if (!params.args || params.args.length < 1) {\n          showConfirm(cm, cm.getOption('theme'));\n          return;\n        }\n        cm.setOption('theme', params.args[0]);\n      },\n      map: function(cm, params, ctx) {\n        var mapArgs = params.args;\n        if (!mapArgs || mapArgs.length < 2) {\n          if (cm) {\n            showConfirm(cm, 'Invalid mapping: ' + params.input);\n          }\n          return;\n        }\n        exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx);\n      },\n      imap: function(cm, params) { this.map(cm, params, 'insert'); },\n      nmap: function(cm, params) { this.map(cm, params, 'normal'); },\n      vmap: function(cm, params) { this.map(cm, params, 'visual'); },\n      unmap: function(cm, params, ctx) {\n        var mapArgs = params.args;\n        if (!mapArgs || mapArgs.length < 1 || !exCommandDispatcher.unmap(mapArgs[0], ctx)) {\n          if (cm) {\n            showConfirm(cm, 'No such mapping: ' + params.input);\n          }\n        }\n      },\n      move: function(cm, params) {\n        commandDispatcher.processCommand(cm, cm.state.vim, {\n            type: 'motion',\n            motion: 'moveToLineOrEdgeOfDocument',\n            motionArgs: { forward: false, explicitRepeat: true,\n              linewise: true },\n            repeatOverride: params.line+1});\n      },\n      set: function(cm, params) {\n        var setArgs = params.args;\n        // Options passed through to the setOption/getOption calls. May be passed in by the\n        // local/global versions of the set command\n        var setCfg = params.setCfg || {};\n        if (!setArgs || setArgs.length < 1) {\n          if (cm) {\n            showConfirm(cm, 'Invalid mapping: ' + params.input);\n          }\n          return;\n        }\n        var expr = setArgs[0].split('=');\n        var optionName = expr[0];\n        var value = expr[1];\n        var forceGet = false;\n\n        if (optionName.charAt(optionName.length - 1) == '?') {\n          // If post-fixed with ?, then the set is actually a get.\n          if (value) { throw Error('Trailing characters: ' + params.argString); }\n          optionName = optionName.substring(0, optionName.length - 1);\n          forceGet = true;\n        }\n        if (value === undefined && optionName.substring(0, 2) == 'no') {\n          // To set boolean options to false, the option name is prefixed with\n          // 'no'.\n          optionName = optionName.substring(2);\n          value = false;\n        }\n\n        var optionIsBoolean = options[optionName] && options[optionName].type == 'boolean';\n        if (optionIsBoolean && value == undefined) {\n          // Calling set with a boolean option sets it to true.\n          value = true;\n        }\n        // If no value is provided, then we assume this is a get.\n        if (!optionIsBoolean && value === undefined || forceGet) {\n          var oldValue = getOption(optionName, cm, setCfg);\n          if (oldValue instanceof Error) {\n            showConfirm(cm, oldValue.message);\n          } else if (oldValue === true || oldValue === false) {\n            showConfirm(cm, ' ' + (oldValue ? '' : 'no') + optionName);\n          } else {\n            showConfirm(cm, '  ' + optionName + '=' + oldValue);\n          }\n        } else {\n          var setOptionReturn = setOption(optionName, value, cm, setCfg);\n          if (setOptionReturn instanceof Error) {\n            showConfirm(cm, setOptionReturn.message);\n          }\n        }\n      },\n      setlocal: function (cm, params) {\n        // setCfg is passed through to setOption\n        params.setCfg = {scope: 'local'};\n        this.set(cm, params);\n      },\n      setglobal: function (cm, params) {\n        // setCfg is passed through to setOption\n        params.setCfg = {scope: 'global'};\n        this.set(cm, params);\n      },\n      registers: function(cm, params) {\n        var regArgs = params.args;\n        var registers = vimGlobalState.registerController.registers;\n        var regInfo = '----------Registers----------\\n\\n';\n        if (!regArgs) {\n          for (var registerName in registers) {\n            var text = registers[registerName].toString();\n            if (text.length) {\n              regInfo += '\"' + registerName + '    ' + text + '\\n';\n            }\n          }\n        } else {\n          var registerName;\n          regArgs = regArgs.join('');\n          for (var i = 0; i < regArgs.length; i++) {\n            registerName = regArgs.charAt(i);\n            if (!vimGlobalState.registerController.isValidRegister(registerName)) {\n              continue;\n            }\n            var register = registers[registerName] || new Register();\n            regInfo += '\"' + registerName + '    ' + register.toString() + '\\n';\n          }\n        }\n        showConfirm(cm, regInfo);\n      },\n      sort: function(cm, params) {\n        var reverse, ignoreCase, unique, number, pattern;\n        function parseArgs() {\n          if (params.argString) {\n            var args = new CodeMirror.StringStream(params.argString);\n            if (args.eat('!')) { reverse = true; }\n            if (args.eol()) { return; }\n            if (!args.eatSpace()) { return 'Invalid arguments'; }\n            var opts = args.match(/([dinuox]+)?\\s*(\\/.+\\/)?\\s*/);\n            if (!opts && !args.eol()) { return 'Invalid arguments'; }\n            if (opts[1]) {\n              ignoreCase = opts[1].indexOf('i') != -1;\n              unique = opts[1].indexOf('u') != -1;\n              var decimal = opts[1].indexOf('d') != -1 || opts[1].indexOf('n') != -1 && 1;\n              var hex = opts[1].indexOf('x') != -1 && 1;\n              var octal = opts[1].indexOf('o') != -1 && 1;\n              if (decimal + hex + octal > 1) { return 'Invalid arguments'; }\n              number = decimal && 'decimal' || hex && 'hex' || octal && 'octal';\n            }\n            if (opts[2]) {\n              pattern = new RegExp(opts[2].substr(1, opts[2].length - 2), ignoreCase ? 'i' : '');\n            }\n          }\n        }\n        var err = parseArgs();\n        if (err) {\n          showConfirm(cm, err + ': ' + params.argString);\n          return;\n        }\n        var lineStart = params.line || cm.firstLine();\n        var lineEnd = params.lineEnd || params.line || cm.lastLine();\n        if (lineStart == lineEnd) { return; }\n        var curStart = new Pos(lineStart, 0);\n        var curEnd = new Pos(lineEnd, lineLength(cm, lineEnd));\n        var text = cm.getRange(curStart, curEnd).split('\\n');\n        var numberRegex = pattern ? pattern :\n           (number == 'decimal') ? /(-?)([\\d]+)/ :\n           (number == 'hex') ? /(-?)(?:0x)?([0-9a-f]+)/i :\n           (number == 'octal') ? /([0-7]+)/ : null;\n        var radix = (number == 'decimal') ? 10 : (number == 'hex') ? 16 : (number == 'octal') ? 8 : null;\n        var numPart = [], textPart = [];\n        if (number || pattern) {\n          for (var i = 0; i < text.length; i++) {\n            var matchPart = pattern ? text[i].match(pattern) : null;\n            if (matchPart && matchPart[0] != '') {\n              numPart.push(matchPart);\n            } else if (!pattern && numberRegex.exec(text[i])) {\n              numPart.push(text[i]);\n            } else {\n              textPart.push(text[i]);\n            }\n          }\n        } else {\n          textPart = text;\n        }\n        function compareFn(a, b) {\n          if (reverse) { var tmp; tmp = a; a = b; b = tmp; }\n          if (ignoreCase) { a = a.toLowerCase(); b = b.toLowerCase(); }\n          var anum = number && numberRegex.exec(a);\n          var bnum = number && numberRegex.exec(b);\n          if (!anum) { return a < b ? -1 : 1; }\n          anum = parseInt((anum[1] + anum[2]).toLowerCase(), radix);\n          bnum = parseInt((bnum[1] + bnum[2]).toLowerCase(), radix);\n          return anum - bnum;\n        }\n        function comparePatternFn(a, b) {\n          if (reverse) { var tmp; tmp = a; a = b; b = tmp; }\n          if (ignoreCase) { a[0] = a[0].toLowerCase(); b[0] = b[0].toLowerCase(); }\n          return (a[0] < b[0]) ? -1 : 1;\n        }\n        numPart.sort(pattern ? comparePatternFn : compareFn);\n        if (pattern) {\n          for (var i = 0; i < numPart.length; i++) {\n            numPart[i] = numPart[i].input;\n          }\n        } else if (!number) { textPart.sort(compareFn); }\n        text = (!reverse) ? textPart.concat(numPart) : numPart.concat(textPart);\n        if (unique) { // Remove duplicate lines\n          var textOld = text;\n          var lastLine;\n          text = [];\n          for (var i = 0; i < textOld.length; i++) {\n            if (textOld[i] != lastLine) {\n              text.push(textOld[i]);\n            }\n            lastLine = textOld[i];\n          }\n        }\n        cm.replaceRange(text.join('\\n'), curStart, curEnd);\n      },\n      vglobal: function(cm, params) {\n        // global inspects params.commandName\n        this.global(cm, params);\n      },\n      global: function(cm, params) {\n        // a global command is of the form\n        // :[range]g/pattern/[cmd]\n        // argString holds the string /pattern/[cmd]\n        var argString = params.argString;\n        if (!argString) {\n          showConfirm(cm, 'Regular Expression missing from global');\n          return;\n        }\n        var inverted = params.commandName[0] === 'v';\n        // range is specified here\n        var lineStart = (params.line !== undefined) ? params.line : cm.firstLine();\n        var lineEnd = params.lineEnd || params.line || cm.lastLine();\n        // get the tokens from argString\n        var tokens = splitBySlash(argString);\n        var regexPart = argString, cmd;\n        if (tokens.length) {\n          regexPart = tokens[0];\n          cmd = tokens.slice(1, tokens.length).join('/');\n        }\n        if (regexPart) {\n          // If regex part is empty, then use the previous query. Otherwise\n          // use the regex part as the new query.\n          try {\n           updateSearchQuery(cm, regexPart, true /** ignoreCase */,\n             true /** smartCase */);\n          } catch (e) {\n           showConfirm(cm, 'Invalid regex: ' + regexPart);\n           return;\n          }\n        }\n        // now that we have the regexPart, search for regex matches in the\n        // specified range of lines\n        var query = getSearchState(cm).getQuery();\n        var matchedLines = [];\n        for (var i = lineStart; i <= lineEnd; i++) {\n          var line = cm.getLineHandle(i);\n          var matched = query.test(line.text);\n          if (matched !== inverted) {\n            matchedLines.push(cmd ? line : line.text);\n          }\n        }\n        // if there is no [cmd], just display the list of matched lines\n        if (!cmd) {\n          showConfirm(cm, matchedLines.join('\\n'));\n          return;\n        }\n        var index = 0;\n        var nextCommand = function() {\n          if (index < matchedLines.length) {\n            var line = matchedLines[index++];\n            var lineNum = cm.getLineNumber(line);\n            if (lineNum == null) {\n              nextCommand();\n              return;\n            }\n            var command = (lineNum + 1) + cmd;\n            exCommandDispatcher.processCommand(cm, command, {\n              callback: nextCommand\n            });\n          }\n        };\n        nextCommand();\n      },\n      substitute: function(cm, params) {\n        if (!cm.getSearchCursor) {\n          throw new Error('Search feature not available. Requires searchcursor.js or ' +\n              'any other getSearchCursor implementation.');\n        }\n        var argString = params.argString;\n        var tokens = argString ? splitBySeparator(argString, argString[0]) : [];\n        var regexPart, replacePart = '', trailing, flagsPart, count;\n        var confirm = false; // Whether to confirm each replace.\n        var global = false; // True to replace all instances on a line, false to replace only 1.\n        if (tokens.length) {\n          regexPart = tokens[0];\n          if (getOption('pcre') && regexPart !== '') {\n              regexPart = new RegExp(regexPart).source; //normalize not escaped characters\n          }\n          replacePart = tokens[1];\n          if (replacePart !== undefined) {\n            if (getOption('pcre')) {\n              replacePart = unescapeRegexReplace(replacePart.replace(/([^\\\\])&/g,\"$1$$&\"));\n            } else {\n              replacePart = translateRegexReplace(replacePart);\n            }\n            vimGlobalState.lastSubstituteReplacePart = replacePart;\n          }\n          trailing = tokens[2] ? tokens[2].split(' ') : [];\n        } else {\n          // either the argString is empty or its of the form ' hello/world'\n          // actually splitBySlash returns a list of tokens\n          // only if the string starts with a '/'\n          if (argString && argString.length) {\n            showConfirm(cm, 'Substitutions should be of the form ' +\n                ':s/pattern/replace/');\n            return;\n          }\n        }\n        // After the 3rd slash, we can have flags followed by a space followed\n        // by count.\n        if (trailing) {\n          flagsPart = trailing[0];\n          count = parseInt(trailing[1]);\n          if (flagsPart) {\n            if (flagsPart.indexOf('c') != -1) {\n              confirm = true;\n            }\n            if (flagsPart.indexOf('g') != -1) {\n              global = true;\n            }\n            if (getOption('pcre')) {\n               regexPart = regexPart + '/' + flagsPart;\n            } else {\n               regexPart = regexPart.replace(/\\//g, \"\\\\/\") + '/' + flagsPart;\n            }\n          }\n        }\n        if (regexPart) {\n          // If regex part is empty, then use the previous query. Otherwise use\n          // the regex part as the new query.\n          try {\n            updateSearchQuery(cm, regexPart, true /** ignoreCase */,\n              true /** smartCase */);\n          } catch (e) {\n            showConfirm(cm, 'Invalid regex: ' + regexPart);\n            return;\n          }\n        }\n        replacePart = replacePart || vimGlobalState.lastSubstituteReplacePart;\n        if (replacePart === undefined) {\n          showConfirm(cm, 'No previous substitute regular expression');\n          return;\n        }\n        var state = getSearchState(cm);\n        var query = state.getQuery();\n        var lineStart = (params.line !== undefined) ? params.line : cm.getCursor().line;\n        var lineEnd = params.lineEnd || lineStart;\n        if (lineStart == cm.firstLine() && lineEnd == cm.lastLine()) {\n          lineEnd = Infinity;\n        }\n        if (count) {\n          lineStart = lineEnd;\n          lineEnd = lineStart + count - 1;\n        }\n        var startPos = clipCursorToContent(cm, new Pos(lineStart, 0));\n        var cursor = cm.getSearchCursor(query, startPos);\n        doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback);\n      },\n      redo: CodeMirror.commands.redo,\n      undo: CodeMirror.commands.undo,\n      write: function(cm) {\n        if (CodeMirror.commands.save) {\n          // If a save command is defined, call it.\n          CodeMirror.commands.save(cm);\n        } else if (cm.save) {\n          // Saves to text area if no save command is defined and cm.save() is available.\n          cm.save();\n        }\n      },\n      nohlsearch: function(cm) {\n        clearSearchHighlight(cm);\n      },\n      yank: function (cm) {\n        var cur = copyCursor(cm.getCursor());\n        var line = cur.line;\n        var lineText = cm.getLine(line);\n        vimGlobalState.registerController.pushText(\n          '0', 'yank', lineText, true, true);\n      },\n      delmarks: function(cm, params) {\n        if (!params.argString || !trim(params.argString)) {\n          showConfirm(cm, 'Argument required');\n          return;\n        }\n\n        var state = cm.state.vim;\n        var stream = new CodeMirror.StringStream(trim(params.argString));\n        while (!stream.eol()) {\n          stream.eatSpace();\n\n          // Record the streams position at the beginning of the loop for use\n          // in error messages.\n          var count = stream.pos;\n\n          if (!stream.match(/[a-zA-Z]/, false)) {\n            showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));\n            return;\n          }\n\n          var sym = stream.next();\n          // Check if this symbol is part of a range\n          if (stream.match('-', true)) {\n            // This symbol is part of a range.\n\n            // The range must terminate at an alphabetic character.\n            if (!stream.match(/[a-zA-Z]/, false)) {\n              showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));\n              return;\n            }\n\n            var startMark = sym;\n            var finishMark = stream.next();\n            // The range must terminate at an alphabetic character which\n            // shares the same case as the start of the range.\n            if (isLowerCase(startMark) && isLowerCase(finishMark) ||\n                isUpperCase(startMark) && isUpperCase(finishMark)) {\n              var start = startMark.charCodeAt(0);\n              var finish = finishMark.charCodeAt(0);\n              if (start >= finish) {\n                showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));\n                return;\n              }\n\n              // Because marks are always ASCII values, and we have\n              // determined that they are the same case, we can use\n              // their char codes to iterate through the defined range.\n              for (var j = 0; j <= finish - start; j++) {\n                var mark = String.fromCharCode(start + j);\n                delete state.marks[mark];\n              }\n            } else {\n              showConfirm(cm, 'Invalid argument: ' + startMark + '-');\n              return;\n            }\n          } else {\n            // This symbol is a valid mark, and is not part of a range.\n            delete state.marks[sym];\n          }\n        }\n      }\n    };\n\n    var exCommandDispatcher = new ExCommandDispatcher();\n\n    /**\n    * @param {CodeMirror} cm CodeMirror instance we are in.\n    * @param {boolean} confirm Whether to confirm each replace.\n    * @param {Cursor} lineStart Line to start replacing from.\n    * @param {Cursor} lineEnd Line to stop replacing at.\n    * @param {RegExp} query Query for performing matches with.\n    * @param {string} replaceWith Text to replace matches with. May contain $1,\n    *     $2, etc for replacing captured groups using JavaScript replace.\n    * @param {function()} callback A callback for when the replace is done.\n    */\n    function doReplace(cm, confirm, global, lineStart, lineEnd, searchCursor, query,\n        replaceWith, callback) {\n      // Set up all the functions.\n      cm.state.vim.exMode = true;\n      var done = false;\n      var lastPos, modifiedLineNumber, joined;\n      function replaceAll() {\n        cm.operation(function() {\n          while (!done) {\n            replace();\n            next();\n          }\n          stop();\n        });\n      }\n      function replace() {\n        var text = cm.getRange(searchCursor.from(), searchCursor.to());\n        var newText = text.replace(query, replaceWith);\n        var unmodifiedLineNumber = searchCursor.to().line;\n        searchCursor.replace(newText);\n        modifiedLineNumber = searchCursor.to().line;\n        lineEnd += modifiedLineNumber - unmodifiedLineNumber;\n        joined = modifiedLineNumber < unmodifiedLineNumber;\n      }\n      function findNextValidMatch() {\n        var lastMatchTo = lastPos && copyCursor(searchCursor.to());\n        var match = searchCursor.findNext();\n        if (match && !match[0] && lastMatchTo && cursorEqual(searchCursor.from(), lastMatchTo)) {\n          match = searchCursor.findNext();\n        }\n        return match;\n      }\n      function next() {\n        // The below only loops to skip over multiple occurrences on the same\n        // line when 'global' is not true.\n        while(findNextValidMatch() &&\n              isInRange(searchCursor.from(), lineStart, lineEnd)) {\n          if (!global && searchCursor.from().line == modifiedLineNumber && !joined) {\n            continue;\n          }\n          cm.scrollIntoView(searchCursor.from(), 30);\n          cm.setSelection(searchCursor.from(), searchCursor.to());\n          lastPos = searchCursor.from();\n          done = false;\n          return;\n        }\n        done = true;\n      }\n      function stop(close) {\n        if (close) { close(); }\n        cm.focus();\n        if (lastPos) {\n          cm.setCursor(lastPos);\n          var vim = cm.state.vim;\n          vim.exMode = false;\n          vim.lastHPos = vim.lastHSPos = lastPos.ch;\n        }\n        if (callback) { callback(); }\n      }\n      function onPromptKeyDown(e, _value, close) {\n        // Swallow all keys.\n        CodeMirror.e_stop(e);\n        var keyName = CodeMirror.keyName(e);\n        switch (keyName) {\n          case 'Y':\n            replace(); next(); break;\n          case 'N':\n            next(); break;\n          case 'A':\n            // replaceAll contains a call to close of its own. We don't want it\n            // to fire too early or multiple times.\n            var savedCallback = callback;\n            callback = undefined;\n            cm.operation(replaceAll);\n            callback = savedCallback;\n            break;\n          case 'L':\n            replace();\n            // fall through and exit.\n          case 'Q':\n          case 'Esc':\n          case 'Ctrl-C':\n          case 'Ctrl-[':\n            stop(close);\n            break;\n        }\n        if (done) { stop(close); }\n        return true;\n      }\n\n      // Actually do replace.\n      next();\n      if (done) {\n        showConfirm(cm, 'No matches for ' + query.source);\n        return;\n      }\n      if (!confirm) {\n        replaceAll();\n        if (callback) { callback(); }\n        return;\n      }\n      showPrompt(cm, {\n        prefix: dom('span', 'replace with ', dom('strong', replaceWith), ' (y/n/a/q/l)'),\n        onKeyDown: onPromptKeyDown\n      });\n    }\n\n    CodeMirror.keyMap.vim = {\n      attach: attachVimMap,\n      detach: detachVimMap,\n      call: cmKey\n    };\n\n    function exitInsertMode(cm) {\n      var vim = cm.state.vim;\n      var macroModeState = vimGlobalState.macroModeState;\n      var insertModeChangeRegister = vimGlobalState.registerController.getRegister('.');\n      var isPlaying = macroModeState.isPlaying;\n      var lastChange = macroModeState.lastInsertModeChanges;\n      if (!isPlaying) {\n        cm.off('change', onChange);\n        CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);\n      }\n      if (!isPlaying && vim.insertModeRepeat > 1) {\n        // Perform insert mode repeat for commands like 3,a and 3,o.\n        repeatLastEdit(cm, vim, vim.insertModeRepeat - 1,\n            true /** repeatForInsert */);\n        vim.lastEditInputState.repeatOverride = vim.insertModeRepeat;\n      }\n      delete vim.insertModeRepeat;\n      vim.insertMode = false;\n      cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1);\n      cm.setOption('keyMap', 'vim');\n      cm.setOption('disableInput', true);\n      cm.toggleOverwrite(false); // exit replace mode if we were in it.\n      // update the \". register before exiting insert mode\n      insertModeChangeRegister.setText(lastChange.changes.join(''));\n      CodeMirror.signal(cm, \"vim-mode-change\", {mode: \"normal\"});\n      if (macroModeState.isRecording) {\n        logInsertModeChange(macroModeState);\n      }\n    }\n\n    function _mapCommand(command) {\n      defaultKeymap.unshift(command);\n    }\n\n    function mapCommand(keys, type, name, args, extra) {\n      var command = {keys: keys, type: type};\n      command[type] = name;\n      command[type + \"Args\"] = args;\n      for (var key in extra)\n        command[key] = extra[key];\n      _mapCommand(command);\n    }\n\n    // The timeout in milliseconds for the two-character ESC keymap should be\n    // adjusted according to your typing speed to prevent false positives.\n    defineOption('insertModeEscKeysTimeout', 200, 'number');\n\n    CodeMirror.keyMap['vim-insert'] = {\n      // TODO: override navigation keys so that Esc will cancel automatic\n      // indentation from o, O, i_<CR>\n      fallthrough: ['default'],\n      attach: attachVimMap,\n      detach: detachVimMap,\n      call: cmKey\n    };\n\n    CodeMirror.keyMap['vim-replace'] = {\n      'Backspace': 'goCharLeft',\n      fallthrough: ['vim-insert'],\n      attach: attachVimMap,\n      detach: detachVimMap,\n      call: cmKey\n    };\n\n    function executeMacroRegister(cm, vim, macroModeState, registerName) {\n      var register = vimGlobalState.registerController.getRegister(registerName);\n      if (registerName == ':') {\n        // Read-only register containing last Ex command.\n        if (register.keyBuffer[0]) {\n          exCommandDispatcher.processCommand(cm, register.keyBuffer[0]);\n        }\n        macroModeState.isPlaying = false;\n        return;\n      }\n      var keyBuffer = register.keyBuffer;\n      var imc = 0;\n      macroModeState.isPlaying = true;\n      macroModeState.replaySearchQueries = register.searchQueries.slice(0);\n      for (var i = 0; i < keyBuffer.length; i++) {\n        var text = keyBuffer[i];\n        var match, key;\n        while (text) {\n          // Pull off one command key, which is either a single character\n          // or a special sequence wrapped in '<' and '>', e.g. '<Space>'.\n          match = (/<\\w+-.+?>|<\\w+>|./).exec(text);\n          key = match[0];\n          text = text.substring(match.index + key.length);\n          vimApi.handleKey(cm, key, 'macro');\n          if (vim.insertMode) {\n            var changes = register.insertModeChanges[imc++].changes;\n            vimGlobalState.macroModeState.lastInsertModeChanges.changes =\n                changes;\n            repeatInsertModeChanges(cm, changes, 1);\n            exitInsertMode(cm);\n          }\n        }\n      }\n      macroModeState.isPlaying = false;\n    }\n\n    function logKey(macroModeState, key) {\n      if (macroModeState.isPlaying) { return; }\n      var registerName = macroModeState.latestRegister;\n      var register = vimGlobalState.registerController.getRegister(registerName);\n      if (register) {\n        register.pushText(key);\n      }\n    }\n\n    function logInsertModeChange(macroModeState) {\n      if (macroModeState.isPlaying) { return; }\n      var registerName = macroModeState.latestRegister;\n      var register = vimGlobalState.registerController.getRegister(registerName);\n      if (register && register.pushInsertModeChanges) {\n        register.pushInsertModeChanges(macroModeState.lastInsertModeChanges);\n      }\n    }\n\n    function logSearchQuery(macroModeState, query) {\n      if (macroModeState.isPlaying) { return; }\n      var registerName = macroModeState.latestRegister;\n      var register = vimGlobalState.registerController.getRegister(registerName);\n      if (register && register.pushSearchQuery) {\n        register.pushSearchQuery(query);\n      }\n    }\n\n    /**\n     * Listens for changes made in insert mode.\n     * Should only be active in insert mode.\n     */\n    function onChange(cm, changeObj) {\n      var macroModeState = vimGlobalState.macroModeState;\n      var lastChange = macroModeState.lastInsertModeChanges;\n      if (!macroModeState.isPlaying) {\n        while(changeObj) {\n          lastChange.expectCursorActivityForChange = true;\n          if (lastChange.ignoreCount > 1) {\n            lastChange.ignoreCount--;\n          } else if (changeObj.origin == '+input' || changeObj.origin == 'paste'\n              || changeObj.origin === undefined /* only in testing */) {\n            var selectionCount = cm.listSelections().length;\n            if (selectionCount > 1)\n              lastChange.ignoreCount = selectionCount;\n            var text = changeObj.text.join('\\n');\n            if (lastChange.maybeReset) {\n              lastChange.changes = [];\n              lastChange.maybeReset = false;\n            }\n            if (text) {\n              if (cm.state.overwrite && !/\\n/.test(text)) {\n                lastChange.changes.push([text]);\n              } else {\n                lastChange.changes.push(text);\n              }\n            }\n          }\n          // Change objects may be chained with next.\n          changeObj = changeObj.next;\n        }\n      }\n    }\n\n    /**\n    * Listens for any kind of cursor activity on CodeMirror.\n    */\n    function onCursorActivity(cm) {\n      var vim = cm.state.vim;\n      if (vim.insertMode) {\n        // Tracking cursor activity in insert mode (for macro support).\n        var macroModeState = vimGlobalState.macroModeState;\n        if (macroModeState.isPlaying) { return; }\n        var lastChange = macroModeState.lastInsertModeChanges;\n        if (lastChange.expectCursorActivityForChange) {\n          lastChange.expectCursorActivityForChange = false;\n        } else {\n          // Cursor moved outside the context of an edit. Reset the change.\n          lastChange.maybeReset = true;\n        }\n      } else if (!cm.curOp.isVimOp) {\n        handleExternalSelection(cm, vim);\n      }\n    }\n    function handleExternalSelection(cm, vim) {\n      var anchor = cm.getCursor('anchor');\n      var head = cm.getCursor('head');\n      // Enter or exit visual mode to match mouse selection.\n      if (vim.visualMode && !cm.somethingSelected()) {\n        exitVisualMode(cm, false);\n      } else if (!vim.visualMode && !vim.insertMode && cm.somethingSelected()) {\n        vim.visualMode = true;\n        vim.visualLine = false;\n        CodeMirror.signal(cm, \"vim-mode-change\", {mode: \"visual\"});\n      }\n      if (vim.visualMode) {\n        // Bind CodeMirror selection model to vim selection model.\n        // Mouse selections are considered visual characterwise.\n        var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;\n        var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;\n        head = offsetCursor(head, 0, headOffset);\n        anchor = offsetCursor(anchor, 0, anchorOffset);\n        vim.sel = {\n          anchor: anchor,\n          head: head\n        };\n        updateMark(cm, vim, '<', cursorMin(head, anchor));\n        updateMark(cm, vim, '>', cursorMax(head, anchor));\n      } else if (!vim.insertMode) {\n        // Reset lastHPos if selection was modified by something outside of vim mode e.g. by mouse.\n        vim.lastHPos = cm.getCursor().ch;\n      }\n    }\n\n    /** Wrapper for special keys pressed in insert mode */\n    function InsertModeKey(keyName) {\n      this.keyName = keyName;\n    }\n\n    /**\n    * Handles raw key down events from the text area.\n    * - Should only be active in insert mode.\n    * - For recording deletes in insert mode.\n    */\n    function onKeyEventTargetKeyDown(e) {\n      var macroModeState = vimGlobalState.macroModeState;\n      var lastChange = macroModeState.lastInsertModeChanges;\n      var keyName = CodeMirror.keyName(e);\n      if (!keyName) { return; }\n      function onKeyFound() {\n        if (lastChange.maybeReset) {\n          lastChange.changes = [];\n          lastChange.maybeReset = false;\n        }\n        lastChange.changes.push(new InsertModeKey(keyName));\n        return true;\n      }\n      if (keyName.indexOf('Delete') != -1 || keyName.indexOf('Backspace') != -1) {\n        CodeMirror.lookupKey(keyName, 'vim-insert', onKeyFound);\n      }\n    }\n\n    /**\n     * Repeats the last edit, which includes exactly 1 command and at most 1\n     * insert. Operator and motion commands are read from lastEditInputState,\n     * while action commands are read from lastEditActionCommand.\n     *\n     * If repeatForInsert is true, then the function was called by\n     * exitInsertMode to repeat the insert mode changes the user just made. The\n     * corresponding enterInsertMode call was made with a count.\n     */\n    function repeatLastEdit(cm, vim, repeat, repeatForInsert) {\n      var macroModeState = vimGlobalState.macroModeState;\n      macroModeState.isPlaying = true;\n      var isAction = !!vim.lastEditActionCommand;\n      var cachedInputState = vim.inputState;\n      function repeatCommand() {\n        if (isAction) {\n          commandDispatcher.processAction(cm, vim, vim.lastEditActionCommand);\n        } else {\n          commandDispatcher.evalInput(cm, vim);\n        }\n      }\n      function repeatInsert(repeat) {\n        if (macroModeState.lastInsertModeChanges.changes.length > 0) {\n          // For some reason, repeat cw in desktop VIM does not repeat\n          // insert mode changes. Will conform to that behavior.\n          repeat = !vim.lastEditActionCommand ? 1 : repeat;\n          var changeObject = macroModeState.lastInsertModeChanges;\n          repeatInsertModeChanges(cm, changeObject.changes, repeat);\n        }\n      }\n      vim.inputState = vim.lastEditInputState;\n      if (isAction && vim.lastEditActionCommand.interlaceInsertRepeat) {\n        // o and O repeat have to be interlaced with insert repeats so that the\n        // insertions appear on separate lines instead of the last line.\n        for (var i = 0; i < repeat; i++) {\n          repeatCommand();\n          repeatInsert(1);\n        }\n      } else {\n        if (!repeatForInsert) {\n          // Hack to get the cursor to end up at the right place. If I is\n          // repeated in insert mode repeat, cursor will be 1 insert\n          // change set left of where it should be.\n          repeatCommand();\n        }\n        repeatInsert(repeat);\n      }\n      vim.inputState = cachedInputState;\n      if (vim.insertMode && !repeatForInsert) {\n        // Don't exit insert mode twice. If repeatForInsert is set, then we\n        // were called by an exitInsertMode call lower on the stack.\n        exitInsertMode(cm);\n      }\n      macroModeState.isPlaying = false;\n    }\n\n    function repeatInsertModeChanges(cm, changes, repeat) {\n      function keyHandler(binding) {\n        if (typeof binding == 'string') {\n          CodeMirror.commands[binding](cm);\n        } else {\n          binding(cm);\n        }\n        return true;\n      }\n      var head = cm.getCursor('head');\n      var visualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.visualBlock;\n      if (visualBlock) {\n        // Set up block selection again for repeating the changes.\n        selectForInsert(cm, head, visualBlock + 1);\n        repeat = cm.listSelections().length;\n        cm.setCursor(head);\n      }\n      for (var i = 0; i < repeat; i++) {\n        if (visualBlock) {\n          cm.setCursor(offsetCursor(head, i, 0));\n        }\n        for (var j = 0; j < changes.length; j++) {\n          var change = changes[j];\n          if (change instanceof InsertModeKey) {\n            CodeMirror.lookupKey(change.keyName, 'vim-insert', keyHandler);\n          } else if (typeof change == \"string\") {\n            cm.replaceSelection(change);\n          } else {\n            var start = cm.getCursor();\n            var end = offsetCursor(start, 0, change[0].length);\n            cm.replaceRange(change[0], start, end);\n            cm.setCursor(end);\n          }\n        }\n      }\n      if (visualBlock) {\n        cm.setCursor(offsetCursor(head, 0, 1));\n      }\n    }\n\n    // multiselect support\n    function cloneVimState(state) {\n      var n = new state.constructor();\n      Object.keys(state).forEach(function(key) {\n        var o = state[key];\n        if (Array.isArray(o))\n          o = o.slice();\n        else if (o && typeof o == \"object\" && o.constructor != Object)\n          o = cloneVimState(o);\n        n[key] = o;\n      });\n      if (state.sel) {\n        n.sel = {\n          head: state.sel.head && copyCursor(state.sel.head),\n          anchor: state.sel.anchor && copyCursor(state.sel.anchor)\n        };\n      }\n      return n;\n    }\n    function multiSelectHandleKey(cm, key, origin) {\n      var isHandled = false;\n      var vim = vimApi.maybeInitVimState_(cm);\n      var visualBlock = vim.visualBlock || vim.wasInVisualBlock;\n\n      var wasMultiselect = cm.isInMultiSelectMode();\n      if (vim.wasInVisualBlock && !wasMultiselect) {\n        vim.wasInVisualBlock = false;\n      } else if (wasMultiselect && vim.visualBlock) {\n         vim.wasInVisualBlock = true;\n      }\n\n      if (key == '<Esc>' && !vim.insertMode && !vim.visualMode && wasMultiselect && vim.status == \"<Esc>\") {\n        // allow editor to exit multiselect\n        clearInputState(cm);\n      } else if (visualBlock || !wasMultiselect || cm.inVirtualSelectionMode) {\n        isHandled = vimApi.handleKey(cm, key, origin);\n      } else {\n        var old = cloneVimState(vim);\n\n        cm.operation(function() {\n          cm.curOp.isVimOp = true;\n          cm.forEachSelection(function() {\n            var head = cm.getCursor(\"head\");\n            var anchor = cm.getCursor(\"anchor\");\n            var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;\n            var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;\n            head = offsetCursor(head, 0, headOffset);\n            anchor = offsetCursor(anchor, 0, anchorOffset);\n            cm.state.vim.sel.head = head;\n            cm.state.vim.sel.anchor = anchor;\n\n            isHandled = vimApi.handleKey(cm, key, origin);\n            if (cm.virtualSelection) {\n              cm.state.vim = cloneVimState(old);\n            }\n          });\n          if (cm.curOp.cursorActivity && !isHandled)\n            cm.curOp.cursorActivity = false;\n          cm.state.vim = vim;\n        }, true);\n      }\n      // some commands may bring visualMode and selection out of sync\n      if (isHandled && !vim.visualMode && !vim.insert && vim.visualMode != cm.somethingSelected()) {\n        handleExternalSelection(cm, vim);\n      }\n      return isHandled;\n    }\n    resetVimGlobalState();\n\n  return vimApi;\n}\n\nfunction initVim(CodeMirror5) {\n  CodeMirror5.Vim = initVim$1(CodeMirror5);\n  return CodeMirror5.Vim;\n}\n\n\n\n    CodeMirror.Vim = initVim(CodeMirror);\n  });\n  "
  },
  {
    "path": "public/assets/lib/vendor/codemirror/lib/codemirror.css",
    "content": "/* BASICS */\n\n.CodeMirror {\n  /* Set height, width, borders, and global font properties here */\n  font-family: monospace;\n  height: 300px;\n  color: black;\n  direction: ltr;\n}\n\n/* PADDING */\n\n.CodeMirror-lines {\n  padding: 4px 0; /* Vertical padding around content */\n}\n.CodeMirror pre.CodeMirror-line,\n.CodeMirror pre.CodeMirror-line-like {\n  padding: 0 4px; /* Horizontal padding of content */\n}\n\n.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {\n  background-color: white; /* The little square between H and V scrollbars */\n}\n\n/* GUTTER */\n\n.CodeMirror-gutters {\n  border-right: 1px solid #ddd;\n  background-color: #f7f7f7;\n  white-space: nowrap;\n}\n.CodeMirror-linenumbers {}\n.CodeMirror-linenumber {\n  padding: 0 3px 0 5px;\n  min-width: 20px;\n  text-align: right;\n  color: #999;\n  white-space: nowrap;\n}\n\n.CodeMirror-guttermarker { color: black; }\n.CodeMirror-guttermarker-subtle { color: #999; }\n\n/* CURSOR */\n\n.CodeMirror-cursor {\n  border-left: 1px solid black;\n  border-right: none;\n  width: 0;\n}\n/* Shown when moving in bi-directional text */\n.CodeMirror div.CodeMirror-secondarycursor {\n  border-left: 1px solid silver;\n}\n.cm-fat-cursor .CodeMirror-cursor {\n  width: auto;\n  border: 0 !important;\n  background: #7e7;\n}\n.cm-fat-cursor div.CodeMirror-cursors {\n  z-index: 1;\n}\n.cm-fat-cursor .CodeMirror-line::selection,\n.cm-fat-cursor .CodeMirror-line > span::selection, \n.cm-fat-cursor .CodeMirror-line > span > span::selection { background: transparent; }\n.cm-fat-cursor .CodeMirror-line::-moz-selection,\n.cm-fat-cursor .CodeMirror-line > span::-moz-selection,\n.cm-fat-cursor .CodeMirror-line > span > span::-moz-selection { background: transparent; }\n.cm-fat-cursor { caret-color: transparent; }\n@-moz-keyframes blink {\n  0% {}\n  50% { background-color: transparent; }\n  100% {}\n}\n@-webkit-keyframes blink {\n  0% {}\n  50% { background-color: transparent; }\n  100% {}\n}\n@keyframes blink {\n  0% {}\n  50% { background-color: transparent; }\n  100% {}\n}\n\n/* Can style cursor different in overwrite (non-insert) mode */\n.CodeMirror-overwrite .CodeMirror-cursor {}\n\n.cm-tab { display: inline-block; text-decoration: inherit; }\n\n.CodeMirror-rulers {\n  position: absolute;\n  left: 0; right: 0; top: -50px; bottom: 0;\n  overflow: hidden;\n}\n.CodeMirror-ruler {\n  border-left: 1px solid #ccc;\n  top: 0; bottom: 0;\n  position: absolute;\n}\n\n/* DEFAULT THEME */\n\n.cm-s-default .cm-header {color: blue;}\n.cm-s-default .cm-quote {color: #090;}\n.cm-negative {color: #d44;}\n.cm-positive {color: #292;}\n.cm-header, .cm-strong {font-weight: bold;}\n.cm-em {font-style: italic;}\n.cm-link {text-decoration: underline;}\n.cm-strikethrough {text-decoration: line-through;}\n\n.cm-s-default .cm-keyword {color: #708;}\n.cm-s-default .cm-atom {color: #219;}\n.cm-s-default .cm-number {color: #164;}\n.cm-s-default .cm-def {color: #00f;}\n.cm-s-default .cm-variable,\n.cm-s-default .cm-punctuation,\n.cm-s-default .cm-property,\n.cm-s-default .cm-operator {}\n.cm-s-default .cm-variable-2 {color: #05a;}\n.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}\n.cm-s-default .cm-comment {color: #a50;}\n.cm-s-default .cm-string {color: #a11;}\n.cm-s-default .cm-string-2 {color: #f50;}\n.cm-s-default .cm-meta {color: #555;}\n.cm-s-default .cm-qualifier {color: #555;}\n.cm-s-default .cm-builtin {color: #30a;}\n.cm-s-default .cm-bracket {color: #997;}\n.cm-s-default .cm-tag {color: #170;}\n.cm-s-default .cm-attribute {color: #00c;}\n.cm-s-default .cm-hr {color: #999;}\n.cm-s-default .cm-link {color: #00c;}\n\n.cm-s-default .cm-error {color: #f00;}\n.cm-invalidchar {color: #f00;}\n\n.CodeMirror-composing { border-bottom: 2px solid; }\n\n/* Default styles for common addons */\n\ndiv.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}\ndiv.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}\n.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }\n.CodeMirror-activeline-background {background: #e8f2ff;}\n\n/* STOP */\n\n/* The rest of this file contains styles related to the mechanics of\n   the editor. You probably shouldn't touch them. */\n\n.CodeMirror {\n  position: relative;\n  overflow: hidden;\n  background: white;\n}\n\n.CodeMirror-scroll {\n  overflow: scroll !important; /* Things will break if this is overridden */\n  /* 50px is the magic margin used to hide the element's real scrollbars */\n  /* See overflow: hidden in .CodeMirror */\n  margin-bottom: -50px; margin-right: -50px;\n  padding-bottom: 50px;\n  height: 100%;\n  outline: none; /* Prevent dragging from highlighting the element */\n  position: relative;\n  z-index: 0;\n}\n.CodeMirror-sizer {\n  position: relative;\n  border-right: 50px solid transparent;\n}\n\n/* The fake, visible scrollbars. Used to force redraw during scrolling\n   before actual scrolling happens, thus preventing shaking and\n   flickering artifacts. */\n.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {\n  position: absolute;\n  z-index: 6;\n  display: none;\n  outline: none;\n}\n.CodeMirror-vscrollbar {\n  right: 0; top: 0;\n  overflow-x: hidden;\n  overflow-y: scroll;\n}\n.CodeMirror-hscrollbar {\n  bottom: 0; left: 0;\n  overflow-y: hidden;\n  overflow-x: scroll;\n}\n.CodeMirror-scrollbar-filler {\n  right: 0; bottom: 0;\n}\n.CodeMirror-gutter-filler {\n  left: 0; bottom: 0;\n}\n\n.CodeMirror-gutters {\n  position: absolute; left: 0; top: 0;\n  min-height: 100%;\n  z-index: 3;\n}\n.CodeMirror-gutter {\n  white-space: normal;\n  height: 100%;\n  display: inline-block;\n  vertical-align: top;\n  margin-bottom: -50px;\n}\n.CodeMirror-gutter-wrapper {\n  position: absolute;\n  z-index: 4;\n  background: none !important;\n  border: none !important;\n}\n.CodeMirror-gutter-background {\n  position: absolute;\n  top: 0; bottom: 0;\n  z-index: 4;\n}\n.CodeMirror-gutter-elt {\n  position: absolute;\n  cursor: default;\n  z-index: 4;\n}\n.CodeMirror-gutter-wrapper ::selection { background-color: transparent }\n.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }\n\n.CodeMirror-lines {\n  cursor: text;\n  min-height: 1px; /* prevents collapsing before first draw */\n}\n.CodeMirror pre.CodeMirror-line,\n.CodeMirror pre.CodeMirror-line-like {\n  /* Reset some styles that the rest of the page might have set */\n  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;\n  border-width: 0;\n  background: transparent;\n  font-family: inherit;\n  font-size: inherit;\n  margin: 0;\n  white-space: pre;\n  word-wrap: normal;\n  line-height: inherit;\n  color: inherit;\n  z-index: 2;\n  position: relative;\n  overflow: visible;\n  -webkit-tap-highlight-color: transparent;\n  -webkit-font-variant-ligatures: contextual;\n  font-variant-ligatures: contextual;\n}\n.CodeMirror-wrap pre.CodeMirror-line,\n.CodeMirror-wrap pre.CodeMirror-line-like {\n  word-wrap: break-word;\n  white-space: pre-wrap;\n  word-break: normal;\n}\n\n.CodeMirror-linebackground {\n  position: absolute;\n  left: 0; right: 0; top: 0; bottom: 0;\n  z-index: 0;\n}\n\n.CodeMirror-linewidget {\n  position: relative;\n  z-index: 2;\n  padding: 0.1px; /* Force widget margins to stay inside of the container */\n}\n\n.CodeMirror-widget {}\n\n.CodeMirror-rtl pre { direction: rtl; }\n\n.CodeMirror-code {\n  outline: none;\n}\n\n/* Force content-box sizing for the elements where we expect it */\n.CodeMirror-scroll,\n.CodeMirror-sizer,\n.CodeMirror-gutter,\n.CodeMirror-gutters,\n.CodeMirror-linenumber {\n  -moz-box-sizing: content-box;\n  box-sizing: content-box;\n}\n\n.CodeMirror-measure {\n  position: absolute;\n  width: 100%;\n  height: 0;\n  overflow: hidden;\n  visibility: hidden;\n}\n\n.CodeMirror-cursor {\n  position: absolute;\n  pointer-events: none;\n}\n.CodeMirror-measure pre { position: static; }\n\ndiv.CodeMirror-cursors {\n  visibility: hidden;\n  position: relative;\n  z-index: 3;\n}\ndiv.CodeMirror-dragcursors {\n  visibility: visible;\n}\n\n.CodeMirror-focused div.CodeMirror-cursors {\n  visibility: visible;\n}\n\n.CodeMirror-selected { background: #d9d9d9; }\n.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }\n.CodeMirror-crosshair { cursor: crosshair; }\n.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }\n.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }\n\n.cm-searching {\n  background-color: #ffa;\n  background-color: rgba(255, 255, 0, .4);\n}\n\n/* Used to force a border model for a node */\n.cm-force-border { padding-right: .1px; }\n\n@media print {\n  /* Hide the cursor when printing */\n  .CodeMirror div.CodeMirror-cursors {\n    visibility: hidden;\n  }\n}\n\n/* See issue #2901 */\n.cm-tab-wrap-hack:after { content: ''; }\n\n/* Help users use markselection to safely style text background */\nspan.CodeMirror-selectedtext { background: none; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/lib/codemirror.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// This is CodeMirror (https://codemirror.net/5), a code editor\n// implemented in JavaScript on top of the browser's DOM.\n//\n// You can find some technical background for some of the code below\n// at http://marijnhaverbeke.nl/blog/#cm-internals .\n\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n  typeof define === 'function' && define.amd ? define(factory) :\n  (global = global || self, global.CodeMirror = factory());\n}(this, (function () { 'use strict';\n\n  // Kludges for bugs and behavior differences that can't be feature\n  // detected are enabled based on userAgent etc sniffing.\n  var userAgent = navigator.userAgent;\n  var platform = navigator.platform;\n\n  var gecko = /gecko\\/\\d/i.test(userAgent);\n  var ie_upto10 = /MSIE \\d/.test(userAgent);\n  var ie_11up = /Trident\\/(?:[7-9]|\\d{2,})\\..*rv:(\\d+)/.exec(userAgent);\n  var edge = /Edge\\/(\\d+)/.exec(userAgent);\n  var ie = ie_upto10 || ie_11up || edge;\n  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);\n  var webkit = !edge && /WebKit\\//.test(userAgent);\n  var qtwebkit = webkit && /Qt\\/\\d+\\.\\d+/.test(userAgent);\n  var chrome = !edge && /Chrome\\/(\\d+)/.exec(userAgent);\n  var chrome_version = chrome && +chrome[1];\n  var presto = /Opera\\//.test(userAgent);\n  var safari = /Apple Computer/.test(navigator.vendor);\n  var mac_geMountainLion = /Mac OS X 1\\d\\D([8-9]|\\d\\d)\\D/.test(userAgent);\n  var phantom = /PhantomJS/.test(userAgent);\n\n  var ios = safari && (/Mobile\\/\\w+/.test(userAgent) || navigator.maxTouchPoints > 2);\n  var android = /Android/.test(userAgent);\n  // This is woefully incomplete. Suggestions for alternative methods welcome.\n  var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);\n  var mac = ios || /Mac/.test(platform);\n  var chromeOS = /\\bCrOS\\b/.test(userAgent);\n  var windows = /win/i.test(platform);\n\n  var presto_version = presto && userAgent.match(/Version\\/(\\d*\\.\\d*)/);\n  if (presto_version) { presto_version = Number(presto_version[1]); }\n  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }\n  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X\n  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));\n  var captureRightClick = gecko || (ie && ie_version >= 9);\n\n  function classTest(cls) { return new RegExp(\"(^|\\\\s)\" + cls + \"(?:$|\\\\s)\\\\s*\") }\n\n  var rmClass = function(node, cls) {\n    var current = node.className;\n    var match = classTest(cls).exec(current);\n    if (match) {\n      var after = current.slice(match.index + match[0].length);\n      node.className = current.slice(0, match.index) + (after ? match[1] + after : \"\");\n    }\n  };\n\n  function removeChildren(e) {\n    for (var count = e.childNodes.length; count > 0; --count)\n      { e.removeChild(e.firstChild); }\n    return e\n  }\n\n  function removeChildrenAndAdd(parent, e) {\n    return removeChildren(parent).appendChild(e)\n  }\n\n  function elt(tag, content, className, style) {\n    var e = document.createElement(tag);\n    if (className) { e.className = className; }\n    if (style) { e.style.cssText = style; }\n    if (typeof content == \"string\") { e.appendChild(document.createTextNode(content)); }\n    else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }\n    return e\n  }\n  // wrapper for elt, which removes the elt from the accessibility tree\n  function eltP(tag, content, className, style) {\n    var e = elt(tag, content, className, style);\n    e.setAttribute(\"role\", \"presentation\");\n    return e\n  }\n\n  var range;\n  if (document.createRange) { range = function(node, start, end, endNode) {\n    var r = document.createRange();\n    r.setEnd(endNode || node, end);\n    r.setStart(node, start);\n    return r\n  }; }\n  else { range = function(node, start, end) {\n    var r = document.body.createTextRange();\n    try { r.moveToElementText(node.parentNode); }\n    catch(e) { return r }\n    r.collapse(true);\n    r.moveEnd(\"character\", end);\n    r.moveStart(\"character\", start);\n    return r\n  }; }\n\n  function contains(parent, child) {\n    if (child.nodeType == 3) // Android browser always returns false when child is a textnode\n      { child = child.parentNode; }\n    if (parent.contains)\n      { return parent.contains(child) }\n    do {\n      if (child.nodeType == 11) { child = child.host; }\n      if (child == parent) { return true }\n    } while (child = child.parentNode)\n  }\n\n  function activeElt(rootNode) {\n    // IE and Edge may throw an \"Unspecified Error\" when accessing document.activeElement.\n    // IE < 10 will throw when accessed while the page is loading or in an iframe.\n    // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.\n    var doc = rootNode.ownerDocument || rootNode;\n    var activeElement;\n    try {\n      activeElement = rootNode.activeElement;\n    } catch(e) {\n      activeElement = doc.body || null;\n    }\n    while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)\n      { activeElement = activeElement.shadowRoot.activeElement; }\n    return activeElement\n  }\n\n  function addClass(node, cls) {\n    var current = node.className;\n    if (!classTest(cls).test(current)) { node.className += (current ? \" \" : \"\") + cls; }\n  }\n  function joinClasses(a, b) {\n    var as = a.split(\" \");\n    for (var i = 0; i < as.length; i++)\n      { if (as[i] && !classTest(as[i]).test(b)) { b += \" \" + as[i]; } }\n    return b\n  }\n\n  var selectInput = function(node) { node.select(); };\n  if (ios) // Mobile Safari apparently has a bug where select() is broken.\n    { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }\n  else if (ie) // Suppress mysterious IE10 errors\n    { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }\n\n  function doc(cm) { return cm.display.wrapper.ownerDocument }\n\n  function root(cm) {\n    return rootNode(cm.display.wrapper)\n  }\n\n  function rootNode(element) {\n    // Detect modern browsers (2017+).\n    return element.getRootNode ? element.getRootNode() : element.ownerDocument\n  }\n\n  function win(cm) { return doc(cm).defaultView }\n\n  function bind(f) {\n    var args = Array.prototype.slice.call(arguments, 1);\n    return function(){return f.apply(null, args)}\n  }\n\n  function copyObj(obj, target, overwrite) {\n    if (!target) { target = {}; }\n    for (var prop in obj)\n      { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))\n        { target[prop] = obj[prop]; } }\n    return target\n  }\n\n  // Counts the column offset in a string, taking tabs into account.\n  // Used mostly to find indentation.\n  function countColumn(string, end, tabSize, startIndex, startValue) {\n    if (end == null) {\n      end = string.search(/[^\\s\\u00a0]/);\n      if (end == -1) { end = string.length; }\n    }\n    for (var i = startIndex || 0, n = startValue || 0;;) {\n      var nextTab = string.indexOf(\"\\t\", i);\n      if (nextTab < 0 || nextTab >= end)\n        { return n + (end - i) }\n      n += nextTab - i;\n      n += tabSize - (n % tabSize);\n      i = nextTab + 1;\n    }\n  }\n\n  var Delayed = function() {\n    this.id = null;\n    this.f = null;\n    this.time = 0;\n    this.handler = bind(this.onTimeout, this);\n  };\n  Delayed.prototype.onTimeout = function (self) {\n    self.id = 0;\n    if (self.time <= +new Date) {\n      self.f();\n    } else {\n      setTimeout(self.handler, self.time - +new Date);\n    }\n  };\n  Delayed.prototype.set = function (ms, f) {\n    this.f = f;\n    var time = +new Date + ms;\n    if (!this.id || time < this.time) {\n      clearTimeout(this.id);\n      this.id = setTimeout(this.handler, ms);\n      this.time = time;\n    }\n  };\n\n  function indexOf(array, elt) {\n    for (var i = 0; i < array.length; ++i)\n      { if (array[i] == elt) { return i } }\n    return -1\n  }\n\n  // Number of pixels added to scroller and sizer to hide scrollbar\n  var scrollerGap = 50;\n\n  // Returned or thrown by various protocols to signal 'I'm not\n  // handling this'.\n  var Pass = {toString: function(){return \"CodeMirror.Pass\"}};\n\n  // Reused option objects for setSelection & friends\n  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: \"*mouse\"}, sel_move = {origin: \"+move\"};\n\n  // The inverse of countColumn -- find the offset that corresponds to\n  // a particular column.\n  function findColumn(string, goal, tabSize) {\n    for (var pos = 0, col = 0;;) {\n      var nextTab = string.indexOf(\"\\t\", pos);\n      if (nextTab == -1) { nextTab = string.length; }\n      var skipped = nextTab - pos;\n      if (nextTab == string.length || col + skipped >= goal)\n        { return pos + Math.min(skipped, goal - col) }\n      col += nextTab - pos;\n      col += tabSize - (col % tabSize);\n      pos = nextTab + 1;\n      if (col >= goal) { return pos }\n    }\n  }\n\n  var spaceStrs = [\"\"];\n  function spaceStr(n) {\n    while (spaceStrs.length <= n)\n      { spaceStrs.push(lst(spaceStrs) + \" \"); }\n    return spaceStrs[n]\n  }\n\n  function lst(arr) { return arr[arr.length-1] }\n\n  function map(array, f) {\n    var out = [];\n    for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }\n    return out\n  }\n\n  function insertSorted(array, value, score) {\n    var pos = 0, priority = score(value);\n    while (pos < array.length && score(array[pos]) <= priority) { pos++; }\n    array.splice(pos, 0, value);\n  }\n\n  function nothing() {}\n\n  function createObj(base, props) {\n    var inst;\n    if (Object.create) {\n      inst = Object.create(base);\n    } else {\n      nothing.prototype = base;\n      inst = new nothing();\n    }\n    if (props) { copyObj(props, inst); }\n    return inst\n  }\n\n  var nonASCIISingleCaseWordChar = /[\\u00df\\u0587\\u0590-\\u05f4\\u0600-\\u06ff\\u3040-\\u309f\\u30a0-\\u30ff\\u3400-\\u4db5\\u4e00-\\u9fcc\\uac00-\\ud7af]/;\n  function isWordCharBasic(ch) {\n    return /\\w/.test(ch) || ch > \"\\x80\" &&\n      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))\n  }\n  function isWordChar(ch, helper) {\n    if (!helper) { return isWordCharBasic(ch) }\n    if (helper.source.indexOf(\"\\\\w\") > -1 && isWordCharBasic(ch)) { return true }\n    return helper.test(ch)\n  }\n\n  function isEmpty(obj) {\n    for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }\n    return true\n  }\n\n  // Extending unicode characters. A series of a non-extending char +\n  // any number of extending chars is treated as a single unit as far\n  // as editing and measuring is concerned. This is not fully correct,\n  // since some scripts/fonts/browsers also treat other configurations\n  // of code points as a group.\n  var extendingChars = /[\\u0300-\\u036f\\u0483-\\u0489\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u064b-\\u065e\\u0670\\u06d6-\\u06dc\\u06de-\\u06e4\\u06e7\\u06e8\\u06ea-\\u06ed\\u0711\\u0730-\\u074a\\u07a6-\\u07b0\\u07eb-\\u07f3\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0900-\\u0902\\u093c\\u0941-\\u0948\\u094d\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09bc\\u09be\\u09c1-\\u09c4\\u09cd\\u09d7\\u09e2\\u09e3\\u0a01\\u0a02\\u0a3c\\u0a41\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a70\\u0a71\\u0a75\\u0a81\\u0a82\\u0abc\\u0ac1-\\u0ac5\\u0ac7\\u0ac8\\u0acd\\u0ae2\\u0ae3\\u0b01\\u0b3c\\u0b3e\\u0b3f\\u0b41-\\u0b44\\u0b4d\\u0b56\\u0b57\\u0b62\\u0b63\\u0b82\\u0bbe\\u0bc0\\u0bcd\\u0bd7\\u0c3e-\\u0c40\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62\\u0c63\\u0cbc\\u0cbf\\u0cc2\\u0cc6\\u0ccc\\u0ccd\\u0cd5\\u0cd6\\u0ce2\\u0ce3\\u0d3e\\u0d41-\\u0d44\\u0d4d\\u0d57\\u0d62\\u0d63\\u0dca\\u0dcf\\u0dd2-\\u0dd4\\u0dd6\\u0ddf\\u0e31\\u0e34-\\u0e3a\\u0e47-\\u0e4e\\u0eb1\\u0eb4-\\u0eb9\\u0ebb\\u0ebc\\u0ec8-\\u0ecd\\u0f18\\u0f19\\u0f35\\u0f37\\u0f39\\u0f71-\\u0f7e\\u0f80-\\u0f84\\u0f86\\u0f87\\u0f90-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u102d-\\u1030\\u1032-\\u1037\\u1039\\u103a\\u103d\\u103e\\u1058\\u1059\\u105e-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108d\\u109d\\u135f\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17b7-\\u17bd\\u17c6\\u17c9-\\u17d3\\u17dd\\u180b-\\u180d\\u18a9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193b\\u1a17\\u1a18\\u1a56\\u1a58-\\u1a5e\\u1a60\\u1a62\\u1a65-\\u1a6c\\u1a73-\\u1a7c\\u1a7f\\u1b00-\\u1b03\\u1b34\\u1b36-\\u1b3a\\u1b3c\\u1b42\\u1b6b-\\u1b73\\u1b80\\u1b81\\u1ba2-\\u1ba5\\u1ba8\\u1ba9\\u1c2c-\\u1c33\\u1c36\\u1c37\\u1cd0-\\u1cd2\\u1cd4-\\u1ce0\\u1ce2-\\u1ce8\\u1ced\\u1dc0-\\u1de6\\u1dfd-\\u1dff\\u200c\\u200d\\u20d0-\\u20f0\\u2cef-\\u2cf1\\u2de0-\\u2dff\\u302a-\\u302f\\u3099\\u309a\\ua66f-\\ua672\\ua67c\\ua67d\\ua6f0\\ua6f1\\ua802\\ua806\\ua80b\\ua825\\ua826\\ua8c4\\ua8e0-\\ua8f1\\ua926-\\ua92d\\ua947-\\ua951\\ua980-\\ua982\\ua9b3\\ua9b6-\\ua9b9\\ua9bc\\uaa29-\\uaa2e\\uaa31\\uaa32\\uaa35\\uaa36\\uaa43\\uaa4c\\uaab0\\uaab2-\\uaab4\\uaab7\\uaab8\\uaabe\\uaabf\\uaac1\\uabe5\\uabe8\\uabed\\udc00-\\udfff\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe26\\uff9e\\uff9f]/;\n  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }\n\n  // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.\n  function skipExtendingChars(str, pos, dir) {\n    while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }\n    return pos\n  }\n\n  // Returns the value from the range [`from`; `to`] that satisfies\n  // `pred` and is closest to `from`. Assumes that at least `to`\n  // satisfies `pred`. Supports `from` being greater than `to`.\n  function findFirst(pred, from, to) {\n    // At any point we are certain `to` satisfies `pred`, don't know\n    // whether `from` does.\n    var dir = from > to ? -1 : 1;\n    for (;;) {\n      if (from == to) { return from }\n      var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);\n      if (mid == from) { return pred(mid) ? from : to }\n      if (pred(mid)) { to = mid; }\n      else { from = mid + dir; }\n    }\n  }\n\n  // BIDI HELPERS\n\n  function iterateBidiSections(order, from, to, f) {\n    if (!order) { return f(from, to, \"ltr\", 0) }\n    var found = false;\n    for (var i = 0; i < order.length; ++i) {\n      var part = order[i];\n      if (part.from < to && part.to > from || from == to && part.to == from) {\n        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? \"rtl\" : \"ltr\", i);\n        found = true;\n      }\n    }\n    if (!found) { f(from, to, \"ltr\"); }\n  }\n\n  var bidiOther = null;\n  function getBidiPartAt(order, ch, sticky) {\n    var found;\n    bidiOther = null;\n    for (var i = 0; i < order.length; ++i) {\n      var cur = order[i];\n      if (cur.from < ch && cur.to > ch) { return i }\n      if (cur.to == ch) {\n        if (cur.from != cur.to && sticky == \"before\") { found = i; }\n        else { bidiOther = i; }\n      }\n      if (cur.from == ch) {\n        if (cur.from != cur.to && sticky != \"before\") { found = i; }\n        else { bidiOther = i; }\n      }\n    }\n    return found != null ? found : bidiOther\n  }\n\n  // Bidirectional ordering algorithm\n  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm\n  // that this (partially) implements.\n\n  // One-char codes used for character types:\n  // L (L):   Left-to-Right\n  // R (R):   Right-to-Left\n  // r (AL):  Right-to-Left Arabic\n  // 1 (EN):  European Number\n  // + (ES):  European Number Separator\n  // % (ET):  European Number Terminator\n  // n (AN):  Arabic Number\n  // , (CS):  Common Number Separator\n  // m (NSM): Non-Spacing Mark\n  // b (BN):  Boundary Neutral\n  // s (B):   Paragraph Separator\n  // t (S):   Segment Separator\n  // w (WS):  Whitespace\n  // N (ON):  Other Neutrals\n\n  // Returns null if characters are ordered as they appear\n  // (left-to-right), or an array of sections ({from, to, level}\n  // objects) in the order in which they occur visually.\n  var bidiOrdering = (function() {\n    // Character types for codepoints 0 to 0xff\n    var lowTypes = \"bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN\";\n    // Character types for codepoints 0x600 to 0x6f9\n    var arabicTypes = \"nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111\";\n    function charType(code) {\n      if (code <= 0xf7) { return lowTypes.charAt(code) }\n      else if (0x590 <= code && code <= 0x5f4) { return \"R\" }\n      else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }\n      else if (0x6ee <= code && code <= 0x8ac) { return \"r\" }\n      else if (0x2000 <= code && code <= 0x200b) { return \"w\" }\n      else if (code == 0x200c) { return \"b\" }\n      else { return \"L\" }\n    }\n\n    var bidiRE = /[\\u0590-\\u05f4\\u0600-\\u06ff\\u0700-\\u08ac]/;\n    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;\n\n    function BidiSpan(level, from, to) {\n      this.level = level;\n      this.from = from; this.to = to;\n    }\n\n    return function(str, direction) {\n      var outerType = direction == \"ltr\" ? \"L\" : \"R\";\n\n      if (str.length == 0 || direction == \"ltr\" && !bidiRE.test(str)) { return false }\n      var len = str.length, types = [];\n      for (var i = 0; i < len; ++i)\n        { types.push(charType(str.charCodeAt(i))); }\n\n      // W1. Examine each non-spacing mark (NSM) in the level run, and\n      // change the type of the NSM to the type of the previous\n      // character. If the NSM is at the start of the level run, it will\n      // get the type of sor.\n      for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {\n        var type = types[i$1];\n        if (type == \"m\") { types[i$1] = prev; }\n        else { prev = type; }\n      }\n\n      // W2. Search backwards from each instance of a European number\n      // until the first strong type (R, L, AL, or sor) is found. If an\n      // AL is found, change the type of the European number to Arabic\n      // number.\n      // W3. Change all ALs to R.\n      for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {\n        var type$1 = types[i$2];\n        if (type$1 == \"1\" && cur == \"r\") { types[i$2] = \"n\"; }\n        else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == \"r\") { types[i$2] = \"R\"; } }\n      }\n\n      // W4. A single European separator between two European numbers\n      // changes to a European number. A single common separator between\n      // two numbers of the same type changes to that type.\n      for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {\n        var type$2 = types[i$3];\n        if (type$2 == \"+\" && prev$1 == \"1\" && types[i$3+1] == \"1\") { types[i$3] = \"1\"; }\n        else if (type$2 == \",\" && prev$1 == types[i$3+1] &&\n                 (prev$1 == \"1\" || prev$1 == \"n\")) { types[i$3] = prev$1; }\n        prev$1 = type$2;\n      }\n\n      // W5. A sequence of European terminators adjacent to European\n      // numbers changes to all European numbers.\n      // W6. Otherwise, separators and terminators change to Other\n      // Neutral.\n      for (var i$4 = 0; i$4 < len; ++i$4) {\n        var type$3 = types[i$4];\n        if (type$3 == \",\") { types[i$4] = \"N\"; }\n        else if (type$3 == \"%\") {\n          var end = (void 0);\n          for (end = i$4 + 1; end < len && types[end] == \"%\"; ++end) {}\n          var replace = (i$4 && types[i$4-1] == \"!\") || (end < len && types[end] == \"1\") ? \"1\" : \"N\";\n          for (var j = i$4; j < end; ++j) { types[j] = replace; }\n          i$4 = end - 1;\n        }\n      }\n\n      // W7. Search backwards from each instance of a European number\n      // until the first strong type (R, L, or sor) is found. If an L is\n      // found, then change the type of the European number to L.\n      for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {\n        var type$4 = types[i$5];\n        if (cur$1 == \"L\" && type$4 == \"1\") { types[i$5] = \"L\"; }\n        else if (isStrong.test(type$4)) { cur$1 = type$4; }\n      }\n\n      // N1. A sequence of neutrals takes the direction of the\n      // surrounding strong text if the text on both sides has the same\n      // direction. European and Arabic numbers act as if they were R in\n      // terms of their influence on neutrals. Start-of-level-run (sor)\n      // and end-of-level-run (eor) are used at level run boundaries.\n      // N2. Any remaining neutrals take the embedding direction.\n      for (var i$6 = 0; i$6 < len; ++i$6) {\n        if (isNeutral.test(types[i$6])) {\n          var end$1 = (void 0);\n          for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}\n          var before = (i$6 ? types[i$6-1] : outerType) == \"L\";\n          var after = (end$1 < len ? types[end$1] : outerType) == \"L\";\n          var replace$1 = before == after ? (before ? \"L\" : \"R\") : outerType;\n          for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }\n          i$6 = end$1 - 1;\n        }\n      }\n\n      // Here we depart from the documented algorithm, in order to avoid\n      // building up an actual levels array. Since there are only three\n      // levels (0, 1, 2) in an implementation that doesn't take\n      // explicit embedding into account, we can build up the order on\n      // the fly, without following the level-based algorithm.\n      var order = [], m;\n      for (var i$7 = 0; i$7 < len;) {\n        if (countsAsLeft.test(types[i$7])) {\n          var start = i$7;\n          for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}\n          order.push(new BidiSpan(0, start, i$7));\n        } else {\n          var pos = i$7, at = order.length, isRTL = direction == \"rtl\" ? 1 : 0;\n          for (++i$7; i$7 < len && types[i$7] != \"L\"; ++i$7) {}\n          for (var j$2 = pos; j$2 < i$7;) {\n            if (countsAsNum.test(types[j$2])) {\n              if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; }\n              var nstart = j$2;\n              for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}\n              order.splice(at, 0, new BidiSpan(2, nstart, j$2));\n              at += isRTL;\n              pos = j$2;\n            } else { ++j$2; }\n          }\n          if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }\n        }\n      }\n      if (direction == \"ltr\") {\n        if (order[0].level == 1 && (m = str.match(/^\\s+/))) {\n          order[0].from = m[0].length;\n          order.unshift(new BidiSpan(0, 0, m[0].length));\n        }\n        if (lst(order).level == 1 && (m = str.match(/\\s+$/))) {\n          lst(order).to -= m[0].length;\n          order.push(new BidiSpan(0, len - m[0].length, len));\n        }\n      }\n\n      return direction == \"rtl\" ? order.reverse() : order\n    }\n  })();\n\n  // Get the bidi ordering for the given line (and cache it). Returns\n  // false for lines that are fully left-to-right, and an array of\n  // BidiSpan objects otherwise.\n  function getOrder(line, direction) {\n    var order = line.order;\n    if (order == null) { order = line.order = bidiOrdering(line.text, direction); }\n    return order\n  }\n\n  // EVENT HANDLING\n\n  // Lightweight event framework. on/off also work on DOM nodes,\n  // registering native DOM handlers.\n\n  var noHandlers = [];\n\n  var on = function(emitter, type, f) {\n    if (emitter.addEventListener) {\n      emitter.addEventListener(type, f, false);\n    } else if (emitter.attachEvent) {\n      emitter.attachEvent(\"on\" + type, f);\n    } else {\n      var map = emitter._handlers || (emitter._handlers = {});\n      map[type] = (map[type] || noHandlers).concat(f);\n    }\n  };\n\n  function getHandlers(emitter, type) {\n    return emitter._handlers && emitter._handlers[type] || noHandlers\n  }\n\n  function off(emitter, type, f) {\n    if (emitter.removeEventListener) {\n      emitter.removeEventListener(type, f, false);\n    } else if (emitter.detachEvent) {\n      emitter.detachEvent(\"on\" + type, f);\n    } else {\n      var map = emitter._handlers, arr = map && map[type];\n      if (arr) {\n        var index = indexOf(arr, f);\n        if (index > -1)\n          { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }\n      }\n    }\n  }\n\n  function signal(emitter, type /*, values...*/) {\n    var handlers = getHandlers(emitter, type);\n    if (!handlers.length) { return }\n    var args = Array.prototype.slice.call(arguments, 2);\n    for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }\n  }\n\n  // The DOM events that CodeMirror handles can be overridden by\n  // registering a (non-DOM) handler on the editor for the event name,\n  // and preventDefault-ing the event in that handler.\n  function signalDOMEvent(cm, e, override) {\n    if (typeof e == \"string\")\n      { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }\n    signal(cm, override || e.type, cm, e);\n    return e_defaultPrevented(e) || e.codemirrorIgnore\n  }\n\n  function signalCursorActivity(cm) {\n    var arr = cm._handlers && cm._handlers.cursorActivity;\n    if (!arr) { return }\n    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);\n    for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)\n      { set.push(arr[i]); } }\n  }\n\n  function hasHandler(emitter, type) {\n    return getHandlers(emitter, type).length > 0\n  }\n\n  // Add on and off methods to a constructor's prototype, to make\n  // registering events on such objects more convenient.\n  function eventMixin(ctor) {\n    ctor.prototype.on = function(type, f) {on(this, type, f);};\n    ctor.prototype.off = function(type, f) {off(this, type, f);};\n  }\n\n  // Due to the fact that we still support jurassic IE versions, some\n  // compatibility wrappers are needed.\n\n  function e_preventDefault(e) {\n    if (e.preventDefault) { e.preventDefault(); }\n    else { e.returnValue = false; }\n  }\n  function e_stopPropagation(e) {\n    if (e.stopPropagation) { e.stopPropagation(); }\n    else { e.cancelBubble = true; }\n  }\n  function e_defaultPrevented(e) {\n    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false\n  }\n  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}\n\n  function e_target(e) {return e.target || e.srcElement}\n  function e_button(e) {\n    var b = e.which;\n    if (b == null) {\n      if (e.button & 1) { b = 1; }\n      else if (e.button & 2) { b = 3; }\n      else if (e.button & 4) { b = 2; }\n    }\n    if (mac && e.ctrlKey && b == 1) { b = 3; }\n    return b\n  }\n\n  // Detect drag-and-drop\n  var dragAndDrop = function() {\n    // There is *some* kind of drag-and-drop support in IE6-8, but I\n    // couldn't get it to work yet.\n    if (ie && ie_version < 9) { return false }\n    var div = elt('div');\n    return \"draggable\" in div || \"dragDrop\" in div\n  }();\n\n  var zwspSupported;\n  function zeroWidthElement(measure) {\n    if (zwspSupported == null) {\n      var test = elt(\"span\", \"\\u200b\");\n      removeChildrenAndAdd(measure, elt(\"span\", [test, document.createTextNode(\"x\")]));\n      if (measure.firstChild.offsetHeight != 0)\n        { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }\n    }\n    var node = zwspSupported ? elt(\"span\", \"\\u200b\") :\n      elt(\"span\", \"\\u00a0\", null, \"display: inline-block; width: 1px; margin-right: -1px\");\n    node.setAttribute(\"cm-text\", \"\");\n    return node\n  }\n\n  // Feature-detect IE's crummy client rect reporting for bidi text\n  var badBidiRects;\n  function hasBadBidiRects(measure) {\n    if (badBidiRects != null) { return badBidiRects }\n    var txt = removeChildrenAndAdd(measure, document.createTextNode(\"A\\u062eA\"));\n    var r0 = range(txt, 0, 1).getBoundingClientRect();\n    var r1 = range(txt, 1, 2).getBoundingClientRect();\n    removeChildren(measure);\n    if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)\n    return badBidiRects = (r1.right - r0.right < 3)\n  }\n\n  // See if \"\".split is the broken IE version, if so, provide an\n  // alternative way to split lines.\n  var splitLinesAuto = \"\\n\\nb\".split(/\\n/).length != 3 ? function (string) {\n    var pos = 0, result = [], l = string.length;\n    while (pos <= l) {\n      var nl = string.indexOf(\"\\n\", pos);\n      if (nl == -1) { nl = string.length; }\n      var line = string.slice(pos, string.charAt(nl - 1) == \"\\r\" ? nl - 1 : nl);\n      var rt = line.indexOf(\"\\r\");\n      if (rt != -1) {\n        result.push(line.slice(0, rt));\n        pos += rt + 1;\n      } else {\n        result.push(line);\n        pos = nl + 1;\n      }\n    }\n    return result\n  } : function (string) { return string.split(/\\r\\n?|\\n/); };\n\n  var hasSelection = window.getSelection ? function (te) {\n    try { return te.selectionStart != te.selectionEnd }\n    catch(e) { return false }\n  } : function (te) {\n    var range;\n    try {range = te.ownerDocument.selection.createRange();}\n    catch(e) {}\n    if (!range || range.parentElement() != te) { return false }\n    return range.compareEndPoints(\"StartToEnd\", range) != 0\n  };\n\n  var hasCopyEvent = (function () {\n    var e = elt(\"div\");\n    if (\"oncopy\" in e) { return true }\n    e.setAttribute(\"oncopy\", \"return;\");\n    return typeof e.oncopy == \"function\"\n  })();\n\n  var badZoomedRects = null;\n  function hasBadZoomedRects(measure) {\n    if (badZoomedRects != null) { return badZoomedRects }\n    var node = removeChildrenAndAdd(measure, elt(\"span\", \"x\"));\n    var normal = node.getBoundingClientRect();\n    var fromRange = range(node, 0, 1).getBoundingClientRect();\n    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1\n  }\n\n  // Known modes, by name and by MIME\n  var modes = {}, mimeModes = {};\n\n  // Extra arguments are stored as the mode's dependencies, which is\n  // used by (legacy) mechanisms like loadmode.js to automatically\n  // load a mode. (Preferred mechanism is the require/define calls.)\n  function defineMode(name, mode) {\n    if (arguments.length > 2)\n      { mode.dependencies = Array.prototype.slice.call(arguments, 2); }\n    modes[name] = mode;\n  }\n\n  function defineMIME(mime, spec) {\n    mimeModes[mime] = spec;\n  }\n\n  // Given a MIME type, a {name, ...options} config object, or a name\n  // string, return a mode config object.\n  function resolveMode(spec) {\n    if (typeof spec == \"string\" && mimeModes.hasOwnProperty(spec)) {\n      spec = mimeModes[spec];\n    } else if (spec && typeof spec.name == \"string\" && mimeModes.hasOwnProperty(spec.name)) {\n      var found = mimeModes[spec.name];\n      if (typeof found == \"string\") { found = {name: found}; }\n      spec = createObj(found, spec);\n      spec.name = found.name;\n    } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+xml$/.test(spec)) {\n      return resolveMode(\"application/xml\")\n    } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+json$/.test(spec)) {\n      return resolveMode(\"application/json\")\n    }\n    if (typeof spec == \"string\") { return {name: spec} }\n    else { return spec || {name: \"null\"} }\n  }\n\n  // Given a mode spec (anything that resolveMode accepts), find and\n  // initialize an actual mode object.\n  function getMode(options, spec) {\n    spec = resolveMode(spec);\n    var mfactory = modes[spec.name];\n    if (!mfactory) { return getMode(options, \"text/plain\") }\n    var modeObj = mfactory(options, spec);\n    if (modeExtensions.hasOwnProperty(spec.name)) {\n      var exts = modeExtensions[spec.name];\n      for (var prop in exts) {\n        if (!exts.hasOwnProperty(prop)) { continue }\n        if (modeObj.hasOwnProperty(prop)) { modeObj[\"_\" + prop] = modeObj[prop]; }\n        modeObj[prop] = exts[prop];\n      }\n    }\n    modeObj.name = spec.name;\n    if (spec.helperType) { modeObj.helperType = spec.helperType; }\n    if (spec.modeProps) { for (var prop$1 in spec.modeProps)\n      { modeObj[prop$1] = spec.modeProps[prop$1]; } }\n\n    return modeObj\n  }\n\n  // This can be used to attach properties to mode objects from\n  // outside the actual mode definition.\n  var modeExtensions = {};\n  function extendMode(mode, properties) {\n    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});\n    copyObj(properties, exts);\n  }\n\n  function copyState(mode, state) {\n    if (state === true) { return state }\n    if (mode.copyState) { return mode.copyState(state) }\n    var nstate = {};\n    for (var n in state) {\n      var val = state[n];\n      if (val instanceof Array) { val = val.concat([]); }\n      nstate[n] = val;\n    }\n    return nstate\n  }\n\n  // Given a mode and a state (for that mode), find the inner mode and\n  // state at the position that the state refers to.\n  function innerMode(mode, state) {\n    var info;\n    while (mode.innerMode) {\n      info = mode.innerMode(state);\n      if (!info || info.mode == mode) { break }\n      state = info.state;\n      mode = info.mode;\n    }\n    return info || {mode: mode, state: state}\n  }\n\n  function startState(mode, a1, a2) {\n    return mode.startState ? mode.startState(a1, a2) : true\n  }\n\n  // STRING STREAM\n\n  // Fed to the mode parsers, provides helper functions to make\n  // parsers more succinct.\n\n  var StringStream = function(string, tabSize, lineOracle) {\n    this.pos = this.start = 0;\n    this.string = string;\n    this.tabSize = tabSize || 8;\n    this.lastColumnPos = this.lastColumnValue = 0;\n    this.lineStart = 0;\n    this.lineOracle = lineOracle;\n  };\n\n  StringStream.prototype.eol = function () {return this.pos >= this.string.length};\n  StringStream.prototype.sol = function () {return this.pos == this.lineStart};\n  StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};\n  StringStream.prototype.next = function () {\n    if (this.pos < this.string.length)\n      { return this.string.charAt(this.pos++) }\n  };\n  StringStream.prototype.eat = function (match) {\n    var ch = this.string.charAt(this.pos);\n    var ok;\n    if (typeof match == \"string\") { ok = ch == match; }\n    else { ok = ch && (match.test ? match.test(ch) : match(ch)); }\n    if (ok) {++this.pos; return ch}\n  };\n  StringStream.prototype.eatWhile = function (match) {\n    var start = this.pos;\n    while (this.eat(match)){}\n    return this.pos > start\n  };\n  StringStream.prototype.eatSpace = function () {\n    var start = this.pos;\n    while (/[\\s\\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }\n    return this.pos > start\n  };\n  StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};\n  StringStream.prototype.skipTo = function (ch) {\n    var found = this.string.indexOf(ch, this.pos);\n    if (found > -1) {this.pos = found; return true}\n  };\n  StringStream.prototype.backUp = function (n) {this.pos -= n;};\n  StringStream.prototype.column = function () {\n    if (this.lastColumnPos < this.start) {\n      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);\n      this.lastColumnPos = this.start;\n    }\n    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n  };\n  StringStream.prototype.indentation = function () {\n    return countColumn(this.string, null, this.tabSize) -\n      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n  };\n  StringStream.prototype.match = function (pattern, consume, caseInsensitive) {\n    if (typeof pattern == \"string\") {\n      var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };\n      var substr = this.string.substr(this.pos, pattern.length);\n      if (cased(substr) == cased(pattern)) {\n        if (consume !== false) { this.pos += pattern.length; }\n        return true\n      }\n    } else {\n      var match = this.string.slice(this.pos).match(pattern);\n      if (match && match.index > 0) { return null }\n      if (match && consume !== false) { this.pos += match[0].length; }\n      return match\n    }\n  };\n  StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};\n  StringStream.prototype.hideFirstChars = function (n, inner) {\n    this.lineStart += n;\n    try { return inner() }\n    finally { this.lineStart -= n; }\n  };\n  StringStream.prototype.lookAhead = function (n) {\n    var oracle = this.lineOracle;\n    return oracle && oracle.lookAhead(n)\n  };\n  StringStream.prototype.baseToken = function () {\n    var oracle = this.lineOracle;\n    return oracle && oracle.baseToken(this.pos)\n  };\n\n  // Find the line object corresponding to the given line number.\n  function getLine(doc, n) {\n    n -= doc.first;\n    if (n < 0 || n >= doc.size) { throw new Error(\"There is no line \" + (n + doc.first) + \" in the document.\") }\n    var chunk = doc;\n    while (!chunk.lines) {\n      for (var i = 0;; ++i) {\n        var child = chunk.children[i], sz = child.chunkSize();\n        if (n < sz) { chunk = child; break }\n        n -= sz;\n      }\n    }\n    return chunk.lines[n]\n  }\n\n  // Get the part of a document between two positions, as an array of\n  // strings.\n  function getBetween(doc, start, end) {\n    var out = [], n = start.line;\n    doc.iter(start.line, end.line + 1, function (line) {\n      var text = line.text;\n      if (n == end.line) { text = text.slice(0, end.ch); }\n      if (n == start.line) { text = text.slice(start.ch); }\n      out.push(text);\n      ++n;\n    });\n    return out\n  }\n  // Get the lines between from and to, as array of strings.\n  function getLines(doc, from, to) {\n    var out = [];\n    doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value\n    return out\n  }\n\n  // Update the height of a line, propagating the height change\n  // upwards to parent nodes.\n  function updateLineHeight(line, height) {\n    var diff = height - line.height;\n    if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }\n  }\n\n  // Given a line object, find its line number by walking up through\n  // its parent links.\n  function lineNo(line) {\n    if (line.parent == null) { return null }\n    var cur = line.parent, no = indexOf(cur.lines, line);\n    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {\n      for (var i = 0;; ++i) {\n        if (chunk.children[i] == cur) { break }\n        no += chunk.children[i].chunkSize();\n      }\n    }\n    return no + cur.first\n  }\n\n  // Find the line at the given vertical position, using the height\n  // information in the document tree.\n  function lineAtHeight(chunk, h) {\n    var n = chunk.first;\n    outer: do {\n      for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {\n        var child = chunk.children[i$1], ch = child.height;\n        if (h < ch) { chunk = child; continue outer }\n        h -= ch;\n        n += child.chunkSize();\n      }\n      return n\n    } while (!chunk.lines)\n    var i = 0;\n    for (; i < chunk.lines.length; ++i) {\n      var line = chunk.lines[i], lh = line.height;\n      if (h < lh) { break }\n      h -= lh;\n    }\n    return n + i\n  }\n\n  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}\n\n  function lineNumberFor(options, i) {\n    return String(options.lineNumberFormatter(i + options.firstLineNumber))\n  }\n\n  // A Pos instance represents a position within the text.\n  function Pos(line, ch, sticky) {\n    if ( sticky === void 0 ) sticky = null;\n\n    if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }\n    this.line = line;\n    this.ch = ch;\n    this.sticky = sticky;\n  }\n\n  // Compare two positions, return 0 if they are the same, a negative\n  // number when a is less, and a positive number otherwise.\n  function cmp(a, b) { return a.line - b.line || a.ch - b.ch }\n\n  function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }\n\n  function copyPos(x) {return Pos(x.line, x.ch)}\n  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }\n  function minPos(a, b) { return cmp(a, b) < 0 ? a : b }\n\n  // Most of the external API clips given positions to make sure they\n  // actually exist within the document.\n  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}\n  function clipPos(doc, pos) {\n    if (pos.line < doc.first) { return Pos(doc.first, 0) }\n    var last = doc.first + doc.size - 1;\n    if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }\n    return clipToLen(pos, getLine(doc, pos.line).text.length)\n  }\n  function clipToLen(pos, linelen) {\n    var ch = pos.ch;\n    if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }\n    else if (ch < 0) { return Pos(pos.line, 0) }\n    else { return pos }\n  }\n  function clipPosArray(doc, array) {\n    var out = [];\n    for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }\n    return out\n  }\n\n  var SavedContext = function(state, lookAhead) {\n    this.state = state;\n    this.lookAhead = lookAhead;\n  };\n\n  var Context = function(doc, state, line, lookAhead) {\n    this.state = state;\n    this.doc = doc;\n    this.line = line;\n    this.maxLookAhead = lookAhead || 0;\n    this.baseTokens = null;\n    this.baseTokenPos = 1;\n  };\n\n  Context.prototype.lookAhead = function (n) {\n    var line = this.doc.getLine(this.line + n);\n    if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }\n    return line\n  };\n\n  Context.prototype.baseToken = function (n) {\n    if (!this.baseTokens) { return null }\n    while (this.baseTokens[this.baseTokenPos] <= n)\n      { this.baseTokenPos += 2; }\n    var type = this.baseTokens[this.baseTokenPos + 1];\n    return {type: type && type.replace(/( |^)overlay .*/, \"\"),\n            size: this.baseTokens[this.baseTokenPos] - n}\n  };\n\n  Context.prototype.nextLine = function () {\n    this.line++;\n    if (this.maxLookAhead > 0) { this.maxLookAhead--; }\n  };\n\n  Context.fromSaved = function (doc, saved, line) {\n    if (saved instanceof SavedContext)\n      { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }\n    else\n      { return new Context(doc, copyState(doc.mode, saved), line) }\n  };\n\n  Context.prototype.save = function (copy) {\n    var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;\n    return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state\n  };\n\n\n  // Compute a style array (an array starting with a mode generation\n  // -- for invalidation -- followed by pairs of end positions and\n  // style strings), which is used to highlight the tokens on the\n  // line.\n  function highlightLine(cm, line, context, forceToEnd) {\n    // A styles array always starts with a number identifying the\n    // mode/overlays that it is based on (for easy invalidation).\n    var st = [cm.state.modeGen], lineClasses = {};\n    // Compute the base array of styles\n    runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },\n            lineClasses, forceToEnd);\n    var state = context.state;\n\n    // Run overlays, adjust style array.\n    var loop = function ( o ) {\n      context.baseTokens = st;\n      var overlay = cm.state.overlays[o], i = 1, at = 0;\n      context.state = true;\n      runMode(cm, line.text, overlay.mode, context, function (end, style) {\n        var start = i;\n        // Ensure there's a token end at the current position, and that i points at it\n        while (at < end) {\n          var i_end = st[i];\n          if (i_end > end)\n            { st.splice(i, 1, end, st[i+1], i_end); }\n          i += 2;\n          at = Math.min(end, i_end);\n        }\n        if (!style) { return }\n        if (overlay.opaque) {\n          st.splice(start, i - start, end, \"overlay \" + style);\n          i = start + 2;\n        } else {\n          for (; start < i; start += 2) {\n            var cur = st[start+1];\n            st[start+1] = (cur ? cur + \" \" : \"\") + \"overlay \" + style;\n          }\n        }\n      }, lineClasses);\n      context.state = state;\n      context.baseTokens = null;\n      context.baseTokenPos = 1;\n    };\n\n    for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );\n\n    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}\n  }\n\n  function getLineStyles(cm, line, updateFrontier) {\n    if (!line.styles || line.styles[0] != cm.state.modeGen) {\n      var context = getContextBefore(cm, lineNo(line));\n      var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);\n      var result = highlightLine(cm, line, context);\n      if (resetState) { context.state = resetState; }\n      line.stateAfter = context.save(!resetState);\n      line.styles = result.styles;\n      if (result.classes) { line.styleClasses = result.classes; }\n      else if (line.styleClasses) { line.styleClasses = null; }\n      if (updateFrontier === cm.doc.highlightFrontier)\n        { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }\n    }\n    return line.styles\n  }\n\n  function getContextBefore(cm, n, precise) {\n    var doc = cm.doc, display = cm.display;\n    if (!doc.mode.startState) { return new Context(doc, true, n) }\n    var start = findStartLine(cm, n, precise);\n    var saved = start > doc.first && getLine(doc, start - 1).stateAfter;\n    var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);\n\n    doc.iter(start, n, function (line) {\n      processLine(cm, line.text, context);\n      var pos = context.line;\n      line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;\n      context.nextLine();\n    });\n    if (precise) { doc.modeFrontier = context.line; }\n    return context\n  }\n\n  // Lightweight form of highlight -- proceed over this line and\n  // update state, but don't save a style array. Used for lines that\n  // aren't currently visible.\n  function processLine(cm, text, context, startAt) {\n    var mode = cm.doc.mode;\n    var stream = new StringStream(text, cm.options.tabSize, context);\n    stream.start = stream.pos = startAt || 0;\n    if (text == \"\") { callBlankLine(mode, context.state); }\n    while (!stream.eol()) {\n      readToken(mode, stream, context.state);\n      stream.start = stream.pos;\n    }\n  }\n\n  function callBlankLine(mode, state) {\n    if (mode.blankLine) { return mode.blankLine(state) }\n    if (!mode.innerMode) { return }\n    var inner = innerMode(mode, state);\n    if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }\n  }\n\n  function readToken(mode, stream, state, inner) {\n    for (var i = 0; i < 10; i++) {\n      if (inner) { inner[0] = innerMode(mode, state).mode; }\n      var style = mode.token(stream, state);\n      if (stream.pos > stream.start) { return style }\n    }\n    throw new Error(\"Mode \" + mode.name + \" failed to advance stream.\")\n  }\n\n  var Token = function(stream, type, state) {\n    this.start = stream.start; this.end = stream.pos;\n    this.string = stream.current();\n    this.type = type || null;\n    this.state = state;\n  };\n\n  // Utility for getTokenAt and getLineTokens\n  function takeToken(cm, pos, precise, asArray) {\n    var doc = cm.doc, mode = doc.mode, style;\n    pos = clipPos(doc, pos);\n    var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);\n    var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;\n    if (asArray) { tokens = []; }\n    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {\n      stream.start = stream.pos;\n      style = readToken(mode, stream, context.state);\n      if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }\n    }\n    return asArray ? tokens : new Token(stream, style, context.state)\n  }\n\n  function extractLineClasses(type, output) {\n    if (type) { for (;;) {\n      var lineClass = type.match(/(?:^|\\s+)line-(background-)?(\\S+)/);\n      if (!lineClass) { break }\n      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);\n      var prop = lineClass[1] ? \"bgClass\" : \"textClass\";\n      if (output[prop] == null)\n        { output[prop] = lineClass[2]; }\n      else if (!(new RegExp(\"(?:^|\\\\s)\" + lineClass[2] + \"(?:$|\\\\s)\")).test(output[prop]))\n        { output[prop] += \" \" + lineClass[2]; }\n    } }\n    return type\n  }\n\n  // Run the given mode's parser over a line, calling f for each token.\n  function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {\n    var flattenSpans = mode.flattenSpans;\n    if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }\n    var curStart = 0, curStyle = null;\n    var stream = new StringStream(text, cm.options.tabSize, context), style;\n    var inner = cm.options.addModeClass && [null];\n    if (text == \"\") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }\n    while (!stream.eol()) {\n      if (stream.pos > cm.options.maxHighlightLength) {\n        flattenSpans = false;\n        if (forceToEnd) { processLine(cm, text, context, stream.pos); }\n        stream.pos = text.length;\n        style = null;\n      } else {\n        style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);\n      }\n      if (inner) {\n        var mName = inner[0].name;\n        if (mName) { style = \"m-\" + (style ? mName + \" \" + style : mName); }\n      }\n      if (!flattenSpans || curStyle != style) {\n        while (curStart < stream.start) {\n          curStart = Math.min(stream.start, curStart + 5000);\n          f(curStart, curStyle);\n        }\n        curStyle = style;\n      }\n      stream.start = stream.pos;\n    }\n    while (curStart < stream.pos) {\n      // Webkit seems to refuse to render text nodes longer than 57444\n      // characters, and returns inaccurate measurements in nodes\n      // starting around 5000 chars.\n      var pos = Math.min(stream.pos, curStart + 5000);\n      f(pos, curStyle);\n      curStart = pos;\n    }\n  }\n\n  // Finds the line to start with when starting a parse. Tries to\n  // find a line with a stateAfter, so that it can start with a\n  // valid state. If that fails, it returns the line with the\n  // smallest indentation, which tends to need the least context to\n  // parse correctly.\n  function findStartLine(cm, n, precise) {\n    var minindent, minline, doc = cm.doc;\n    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);\n    for (var search = n; search > lim; --search) {\n      if (search <= doc.first) { return doc.first }\n      var line = getLine(doc, search - 1), after = line.stateAfter;\n      if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))\n        { return search }\n      var indented = countColumn(line.text, null, cm.options.tabSize);\n      if (minline == null || minindent > indented) {\n        minline = search - 1;\n        minindent = indented;\n      }\n    }\n    return minline\n  }\n\n  function retreatFrontier(doc, n) {\n    doc.modeFrontier = Math.min(doc.modeFrontier, n);\n    if (doc.highlightFrontier < n - 10) { return }\n    var start = doc.first;\n    for (var line = n - 1; line > start; line--) {\n      var saved = getLine(doc, line).stateAfter;\n      // change is on 3\n      // state on line 1 looked ahead 2 -- so saw 3\n      // test 1 + 2 < 3 should cover this\n      if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {\n        start = line + 1;\n        break\n      }\n    }\n    doc.highlightFrontier = Math.min(doc.highlightFrontier, start);\n  }\n\n  // Optimize some code when these features are not used.\n  var sawReadOnlySpans = false, sawCollapsedSpans = false;\n\n  function seeReadOnlySpans() {\n    sawReadOnlySpans = true;\n  }\n\n  function seeCollapsedSpans() {\n    sawCollapsedSpans = true;\n  }\n\n  // TEXTMARKER SPANS\n\n  function MarkedSpan(marker, from, to) {\n    this.marker = marker;\n    this.from = from; this.to = to;\n  }\n\n  // Search an array of spans for a span matching the given marker.\n  function getMarkedSpanFor(spans, marker) {\n    if (spans) { for (var i = 0; i < spans.length; ++i) {\n      var span = spans[i];\n      if (span.marker == marker) { return span }\n    } }\n  }\n\n  // Remove a span from an array, returning undefined if no spans are\n  // left (we don't store arrays for lines without spans).\n  function removeMarkedSpan(spans, span) {\n    var r;\n    for (var i = 0; i < spans.length; ++i)\n      { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }\n    return r\n  }\n\n  // Add a span to a line.\n  function addMarkedSpan(line, span, op) {\n    var inThisOp = op && window.WeakSet && (op.markedSpans || (op.markedSpans = new WeakSet));\n    if (inThisOp && line.markedSpans && inThisOp.has(line.markedSpans)) {\n      line.markedSpans.push(span);\n    } else {\n      line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];\n      if (inThisOp) { inThisOp.add(line.markedSpans); }\n    }\n    span.marker.attachLine(line);\n  }\n\n  // Used for the algorithm that adjusts markers for a change in the\n  // document. These functions cut an array of spans at a given\n  // character position, returning an array of remaining chunks (or\n  // undefined if nothing remains).\n  function markedSpansBefore(old, startCh, isInsert) {\n    var nw;\n    if (old) { for (var i = 0; i < old.length; ++i) {\n      var span = old[i], marker = span.marker;\n      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);\n      if (startsBefore || span.from == startCh && marker.type == \"bookmark\" && (!isInsert || !span.marker.insertLeft)) {\n        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)\n        ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));\n      }\n    } }\n    return nw\n  }\n  function markedSpansAfter(old, endCh, isInsert) {\n    var nw;\n    if (old) { for (var i = 0; i < old.length; ++i) {\n      var span = old[i], marker = span.marker;\n      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);\n      if (endsAfter || span.from == endCh && marker.type == \"bookmark\" && (!isInsert || span.marker.insertLeft)) {\n        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)\n        ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,\n                                              span.to == null ? null : span.to - endCh));\n      }\n    } }\n    return nw\n  }\n\n  // Given a change object, compute the new set of marker spans that\n  // cover the line in which the change took place. Removes spans\n  // entirely within the change, reconnects spans belonging to the\n  // same marker that appear on both sides of the change, and cuts off\n  // spans partially within the change. Returns an array of span\n  // arrays with one element for each line in (after) the change.\n  function stretchSpansOverChange(doc, change) {\n    if (change.full) { return null }\n    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;\n    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;\n    if (!oldFirst && !oldLast) { return null }\n\n    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;\n    // Get the spans that 'stick out' on both sides\n    var first = markedSpansBefore(oldFirst, startCh, isInsert);\n    var last = markedSpansAfter(oldLast, endCh, isInsert);\n\n    // Next, merge those two ends\n    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);\n    if (first) {\n      // Fix up .to properties of first\n      for (var i = 0; i < first.length; ++i) {\n        var span = first[i];\n        if (span.to == null) {\n          var found = getMarkedSpanFor(last, span.marker);\n          if (!found) { span.to = startCh; }\n          else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }\n        }\n      }\n    }\n    if (last) {\n      // Fix up .from in last (or move them into first in case of sameLine)\n      for (var i$1 = 0; i$1 < last.length; ++i$1) {\n        var span$1 = last[i$1];\n        if (span$1.to != null) { span$1.to += offset; }\n        if (span$1.from == null) {\n          var found$1 = getMarkedSpanFor(first, span$1.marker);\n          if (!found$1) {\n            span$1.from = offset;\n            if (sameLine) { (first || (first = [])).push(span$1); }\n          }\n        } else {\n          span$1.from += offset;\n          if (sameLine) { (first || (first = [])).push(span$1); }\n        }\n      }\n    }\n    // Make sure we didn't create any zero-length spans\n    if (first) { first = clearEmptySpans(first); }\n    if (last && last != first) { last = clearEmptySpans(last); }\n\n    var newMarkers = [first];\n    if (!sameLine) {\n      // Fill gap with whole-line-spans\n      var gap = change.text.length - 2, gapMarkers;\n      if (gap > 0 && first)\n        { for (var i$2 = 0; i$2 < first.length; ++i$2)\n          { if (first[i$2].to == null)\n            { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }\n      for (var i$3 = 0; i$3 < gap; ++i$3)\n        { newMarkers.push(gapMarkers); }\n      newMarkers.push(last);\n    }\n    return newMarkers\n  }\n\n  // Remove spans that are empty and don't have a clearWhenEmpty\n  // option of false.\n  function clearEmptySpans(spans) {\n    for (var i = 0; i < spans.length; ++i) {\n      var span = spans[i];\n      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)\n        { spans.splice(i--, 1); }\n    }\n    if (!spans.length) { return null }\n    return spans\n  }\n\n  // Used to 'clip' out readOnly ranges when making a change.\n  function removeReadOnlyRanges(doc, from, to) {\n    var markers = null;\n    doc.iter(from.line, to.line + 1, function (line) {\n      if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {\n        var mark = line.markedSpans[i].marker;\n        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))\n          { (markers || (markers = [])).push(mark); }\n      } }\n    });\n    if (!markers) { return null }\n    var parts = [{from: from, to: to}];\n    for (var i = 0; i < markers.length; ++i) {\n      var mk = markers[i], m = mk.find(0);\n      for (var j = 0; j < parts.length; ++j) {\n        var p = parts[j];\n        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }\n        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);\n        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)\n          { newParts.push({from: p.from, to: m.from}); }\n        if (dto > 0 || !mk.inclusiveRight && !dto)\n          { newParts.push({from: m.to, to: p.to}); }\n        parts.splice.apply(parts, newParts);\n        j += newParts.length - 3;\n      }\n    }\n    return parts\n  }\n\n  // Connect or disconnect spans from a line.\n  function detachMarkedSpans(line) {\n    var spans = line.markedSpans;\n    if (!spans) { return }\n    for (var i = 0; i < spans.length; ++i)\n      { spans[i].marker.detachLine(line); }\n    line.markedSpans = null;\n  }\n  function attachMarkedSpans(line, spans) {\n    if (!spans) { return }\n    for (var i = 0; i < spans.length; ++i)\n      { spans[i].marker.attachLine(line); }\n    line.markedSpans = spans;\n  }\n\n  // Helpers used when computing which overlapping collapsed span\n  // counts as the larger one.\n  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }\n  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }\n\n  // Returns a number indicating which of two overlapping collapsed\n  // spans is larger (and thus includes the other). Falls back to\n  // comparing ids when the spans cover exactly the same range.\n  function compareCollapsedMarkers(a, b) {\n    var lenDiff = a.lines.length - b.lines.length;\n    if (lenDiff != 0) { return lenDiff }\n    var aPos = a.find(), bPos = b.find();\n    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);\n    if (fromCmp) { return -fromCmp }\n    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);\n    if (toCmp) { return toCmp }\n    return b.id - a.id\n  }\n\n  // Find out whether a line ends or starts in a collapsed span. If\n  // so, return the marker for that span.\n  function collapsedSpanAtSide(line, start) {\n    var sps = sawCollapsedSpans && line.markedSpans, found;\n    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {\n      sp = sps[i];\n      if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&\n          (!found || compareCollapsedMarkers(found, sp.marker) < 0))\n        { found = sp.marker; }\n    } }\n    return found\n  }\n  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }\n  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }\n\n  function collapsedSpanAround(line, ch) {\n    var sps = sawCollapsedSpans && line.markedSpans, found;\n    if (sps) { for (var i = 0; i < sps.length; ++i) {\n      var sp = sps[i];\n      if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&\n          (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }\n    } }\n    return found\n  }\n\n  // Test whether there exists a collapsed span that partially\n  // overlaps (covers the start or end, but not both) of a new span.\n  // Such overlap is not allowed.\n  function conflictingCollapsedRange(doc, lineNo, from, to, marker) {\n    var line = getLine(doc, lineNo);\n    var sps = sawCollapsedSpans && line.markedSpans;\n    if (sps) { for (var i = 0; i < sps.length; ++i) {\n      var sp = sps[i];\n      if (!sp.marker.collapsed) { continue }\n      var found = sp.marker.find(0);\n      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);\n      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);\n      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }\n      if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||\n          fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))\n        { return true }\n    } }\n  }\n\n  // A visual line is a line as drawn on the screen. Folding, for\n  // example, can cause multiple logical lines to appear on the same\n  // visual line. This finds the start of the visual line that the\n  // given line is part of (usually that is the line itself).\n  function visualLine(line) {\n    var merged;\n    while (merged = collapsedSpanAtStart(line))\n      { line = merged.find(-1, true).line; }\n    return line\n  }\n\n  function visualLineEnd(line) {\n    var merged;\n    while (merged = collapsedSpanAtEnd(line))\n      { line = merged.find(1, true).line; }\n    return line\n  }\n\n  // Returns an array of logical lines that continue the visual line\n  // started by the argument, or undefined if there are no such lines.\n  function visualLineContinued(line) {\n    var merged, lines;\n    while (merged = collapsedSpanAtEnd(line)) {\n      line = merged.find(1, true).line\n      ;(lines || (lines = [])).push(line);\n    }\n    return lines\n  }\n\n  // Get the line number of the start of the visual line that the\n  // given line number is part of.\n  function visualLineNo(doc, lineN) {\n    var line = getLine(doc, lineN), vis = visualLine(line);\n    if (line == vis) { return lineN }\n    return lineNo(vis)\n  }\n\n  // Get the line number of the start of the next visual line after\n  // the given line.\n  function visualLineEndNo(doc, lineN) {\n    if (lineN > doc.lastLine()) { return lineN }\n    var line = getLine(doc, lineN), merged;\n    if (!lineIsHidden(doc, line)) { return lineN }\n    while (merged = collapsedSpanAtEnd(line))\n      { line = merged.find(1, true).line; }\n    return lineNo(line) + 1\n  }\n\n  // Compute whether a line is hidden. Lines count as hidden when they\n  // are part of a visual line that starts with another line, or when\n  // they are entirely covered by collapsed, non-widget span.\n  function lineIsHidden(doc, line) {\n    var sps = sawCollapsedSpans && line.markedSpans;\n    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {\n      sp = sps[i];\n      if (!sp.marker.collapsed) { continue }\n      if (sp.from == null) { return true }\n      if (sp.marker.widgetNode) { continue }\n      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))\n        { return true }\n    } }\n  }\n  function lineIsHiddenInner(doc, line, span) {\n    if (span.to == null) {\n      var end = span.marker.find(1, true);\n      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))\n    }\n    if (span.marker.inclusiveRight && span.to == line.text.length)\n      { return true }\n    for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {\n      sp = line.markedSpans[i];\n      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&\n          (sp.to == null || sp.to != span.from) &&\n          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&\n          lineIsHiddenInner(doc, line, sp)) { return true }\n    }\n  }\n\n  // Find the height above the given line.\n  function heightAtLine(lineObj) {\n    lineObj = visualLine(lineObj);\n\n    var h = 0, chunk = lineObj.parent;\n    for (var i = 0; i < chunk.lines.length; ++i) {\n      var line = chunk.lines[i];\n      if (line == lineObj) { break }\n      else { h += line.height; }\n    }\n    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {\n      for (var i$1 = 0; i$1 < p.children.length; ++i$1) {\n        var cur = p.children[i$1];\n        if (cur == chunk) { break }\n        else { h += cur.height; }\n      }\n    }\n    return h\n  }\n\n  // Compute the character length of a line, taking into account\n  // collapsed ranges (see markText) that might hide parts, and join\n  // other lines onto it.\n  function lineLength(line) {\n    if (line.height == 0) { return 0 }\n    var len = line.text.length, merged, cur = line;\n    while (merged = collapsedSpanAtStart(cur)) {\n      var found = merged.find(0, true);\n      cur = found.from.line;\n      len += found.from.ch - found.to.ch;\n    }\n    cur = line;\n    while (merged = collapsedSpanAtEnd(cur)) {\n      var found$1 = merged.find(0, true);\n      len -= cur.text.length - found$1.from.ch;\n      cur = found$1.to.line;\n      len += cur.text.length - found$1.to.ch;\n    }\n    return len\n  }\n\n  // Find the longest line in the document.\n  function findMaxLine(cm) {\n    var d = cm.display, doc = cm.doc;\n    d.maxLine = getLine(doc, doc.first);\n    d.maxLineLength = lineLength(d.maxLine);\n    d.maxLineChanged = true;\n    doc.iter(function (line) {\n      var len = lineLength(line);\n      if (len > d.maxLineLength) {\n        d.maxLineLength = len;\n        d.maxLine = line;\n      }\n    });\n  }\n\n  // LINE DATA STRUCTURE\n\n  // Line objects. These hold state related to a line, including\n  // highlighting info (the styles array).\n  var Line = function(text, markedSpans, estimateHeight) {\n    this.text = text;\n    attachMarkedSpans(this, markedSpans);\n    this.height = estimateHeight ? estimateHeight(this) : 1;\n  };\n\n  Line.prototype.lineNo = function () { return lineNo(this) };\n  eventMixin(Line);\n\n  // Change the content (text, markers) of a line. Automatically\n  // invalidates cached information and tries to re-estimate the\n  // line's height.\n  function updateLine(line, text, markedSpans, estimateHeight) {\n    line.text = text;\n    if (line.stateAfter) { line.stateAfter = null; }\n    if (line.styles) { line.styles = null; }\n    if (line.order != null) { line.order = null; }\n    detachMarkedSpans(line);\n    attachMarkedSpans(line, markedSpans);\n    var estHeight = estimateHeight ? estimateHeight(line) : 1;\n    if (estHeight != line.height) { updateLineHeight(line, estHeight); }\n  }\n\n  // Detach a line from the document tree and its markers.\n  function cleanUpLine(line) {\n    line.parent = null;\n    detachMarkedSpans(line);\n  }\n\n  // Convert a style as returned by a mode (either null, or a string\n  // containing one or more styles) to a CSS style. This is cached,\n  // and also looks for line-wide styles.\n  var styleToClassCache = {}, styleToClassCacheWithMode = {};\n  function interpretTokenStyle(style, options) {\n    if (!style || /^\\s*$/.test(style)) { return null }\n    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;\n    return cache[style] ||\n      (cache[style] = style.replace(/\\S+/g, \"cm-$&\"))\n  }\n\n  // Render the DOM representation of the text of a line. Also builds\n  // up a 'line map', which points at the DOM nodes that represent\n  // specific stretches of text, and is used by the measuring code.\n  // The returned object contains the DOM node, this map, and\n  // information about line-wide styles that were set by the mode.\n  function buildLineContent(cm, lineView) {\n    // The padding-right forces the element to have a 'border', which\n    // is needed on Webkit to be able to get line-level bounding\n    // rectangles for it (in measureChar).\n    var content = eltP(\"span\", null, null, webkit ? \"padding-right: .1px\" : null);\n    var builder = {pre: eltP(\"pre\", [content], \"CodeMirror-line\"), content: content,\n                   col: 0, pos: 0, cm: cm,\n                   trailingSpace: false,\n                   splitSpaces: cm.getOption(\"lineWrapping\")};\n    lineView.measure = {};\n\n    // Iterate over the logical lines that make up this visual line.\n    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {\n      var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);\n      builder.pos = 0;\n      builder.addToken = buildToken;\n      // Optionally wire in some hacks into the token-rendering\n      // algorithm, to deal with browser quirks.\n      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))\n        { builder.addToken = buildTokenBadBidi(builder.addToken, order); }\n      builder.map = [];\n      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);\n      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));\n      if (line.styleClasses) {\n        if (line.styleClasses.bgClass)\n          { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || \"\"); }\n        if (line.styleClasses.textClass)\n          { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || \"\"); }\n      }\n\n      // Ensure at least a single node is present, for measuring.\n      if (builder.map.length == 0)\n        { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }\n\n      // Store the map and a cache object for the current logical line\n      if (i == 0) {\n        lineView.measure.map = builder.map;\n        lineView.measure.cache = {};\n      } else {\n  (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)\n        ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});\n      }\n    }\n\n    // See issue #2901\n    if (webkit) {\n      var last = builder.content.lastChild;\n      if (/\\bcm-tab\\b/.test(last.className) || (last.querySelector && last.querySelector(\".cm-tab\")))\n        { builder.content.className = \"cm-tab-wrap-hack\"; }\n    }\n\n    signal(cm, \"renderLine\", cm, lineView.line, builder.pre);\n    if (builder.pre.className)\n      { builder.textClass = joinClasses(builder.pre.className, builder.textClass || \"\"); }\n\n    return builder\n  }\n\n  function defaultSpecialCharPlaceholder(ch) {\n    var token = elt(\"span\", \"\\u2022\", \"cm-invalidchar\");\n    token.title = \"\\\\u\" + ch.charCodeAt(0).toString(16);\n    token.setAttribute(\"aria-label\", token.title);\n    return token\n  }\n\n  // Build up the DOM representation for a single token, and add it to\n  // the line map. Takes care to render special characters separately.\n  function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {\n    if (!text) { return }\n    var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;\n    var special = builder.cm.state.specialChars, mustWrap = false;\n    var content;\n    if (!special.test(text)) {\n      builder.col += text.length;\n      content = document.createTextNode(displayText);\n      builder.map.push(builder.pos, builder.pos + text.length, content);\n      if (ie && ie_version < 9) { mustWrap = true; }\n      builder.pos += text.length;\n    } else {\n      content = document.createDocumentFragment();\n      var pos = 0;\n      while (true) {\n        special.lastIndex = pos;\n        var m = special.exec(text);\n        var skipped = m ? m.index - pos : text.length - pos;\n        if (skipped) {\n          var txt = document.createTextNode(displayText.slice(pos, pos + skipped));\n          if (ie && ie_version < 9) { content.appendChild(elt(\"span\", [txt])); }\n          else { content.appendChild(txt); }\n          builder.map.push(builder.pos, builder.pos + skipped, txt);\n          builder.col += skipped;\n          builder.pos += skipped;\n        }\n        if (!m) { break }\n        pos += skipped + 1;\n        var txt$1 = (void 0);\n        if (m[0] == \"\\t\") {\n          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;\n          txt$1 = content.appendChild(elt(\"span\", spaceStr(tabWidth), \"cm-tab\"));\n          txt$1.setAttribute(\"role\", \"presentation\");\n          txt$1.setAttribute(\"cm-text\", \"\\t\");\n          builder.col += tabWidth;\n        } else if (m[0] == \"\\r\" || m[0] == \"\\n\") {\n          txt$1 = content.appendChild(elt(\"span\", m[0] == \"\\r\" ? \"\\u240d\" : \"\\u2424\", \"cm-invalidchar\"));\n          txt$1.setAttribute(\"cm-text\", m[0]);\n          builder.col += 1;\n        } else {\n          txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);\n          txt$1.setAttribute(\"cm-text\", m[0]);\n          if (ie && ie_version < 9) { content.appendChild(elt(\"span\", [txt$1])); }\n          else { content.appendChild(txt$1); }\n          builder.col += 1;\n        }\n        builder.map.push(builder.pos, builder.pos + 1, txt$1);\n        builder.pos++;\n      }\n    }\n    builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;\n    if (style || startStyle || endStyle || mustWrap || css || attributes) {\n      var fullStyle = style || \"\";\n      if (startStyle) { fullStyle += startStyle; }\n      if (endStyle) { fullStyle += endStyle; }\n      var token = elt(\"span\", [content], fullStyle, css);\n      if (attributes) {\n        for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != \"style\" && attr != \"class\")\n          { token.setAttribute(attr, attributes[attr]); } }\n      }\n      return builder.content.appendChild(token)\n    }\n    builder.content.appendChild(content);\n  }\n\n  // Change some spaces to NBSP to prevent the browser from collapsing\n  // trailing spaces at the end of a line when rendering text (issue #1362).\n  function splitSpaces(text, trailingBefore) {\n    if (text.length > 1 && !/  /.test(text)) { return text }\n    var spaceBefore = trailingBefore, result = \"\";\n    for (var i = 0; i < text.length; i++) {\n      var ch = text.charAt(i);\n      if (ch == \" \" && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))\n        { ch = \"\\u00a0\"; }\n      result += ch;\n      spaceBefore = ch == \" \";\n    }\n    return result\n  }\n\n  // Work around nonsense dimensions being reported for stretches of\n  // right-to-left text.\n  function buildTokenBadBidi(inner, order) {\n    return function (builder, text, style, startStyle, endStyle, css, attributes) {\n      style = style ? style + \" cm-force-border\" : \"cm-force-border\";\n      var start = builder.pos, end = start + text.length;\n      for (;;) {\n        // Find the part that overlaps with the start of this text\n        var part = (void 0);\n        for (var i = 0; i < order.length; i++) {\n          part = order[i];\n          if (part.to > start && part.from <= start) { break }\n        }\n        if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }\n        inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);\n        startStyle = null;\n        text = text.slice(part.to - start);\n        start = part.to;\n      }\n    }\n  }\n\n  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {\n    var widget = !ignoreWidget && marker.widgetNode;\n    if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }\n    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {\n      if (!widget)\n        { widget = builder.content.appendChild(document.createElement(\"span\")); }\n      widget.setAttribute(\"cm-marker\", marker.id);\n    }\n    if (widget) {\n      builder.cm.display.input.setUneditable(widget);\n      builder.content.appendChild(widget);\n    }\n    builder.pos += size;\n    builder.trailingSpace = false;\n  }\n\n  // Outputs a number of spans to make up a line, taking highlighting\n  // and marked text into account.\n  function insertLineContent(line, builder, styles) {\n    var spans = line.markedSpans, allText = line.text, at = 0;\n    if (!spans) {\n      for (var i$1 = 1; i$1 < styles.length; i$1+=2)\n        { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }\n      return\n    }\n\n    var len = allText.length, pos = 0, i = 1, text = \"\", style, css;\n    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;\n    for (;;) {\n      if (nextChange == pos) { // Update current marker set\n        spanStyle = spanEndStyle = spanStartStyle = css = \"\";\n        attributes = null;\n        collapsed = null; nextChange = Infinity;\n        var foundBookmarks = [], endStyles = (void 0);\n        for (var j = 0; j < spans.length; ++j) {\n          var sp = spans[j], m = sp.marker;\n          if (m.type == \"bookmark\" && sp.from == pos && m.widgetNode) {\n            foundBookmarks.push(m);\n          } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {\n            if (sp.to != null && sp.to != pos && nextChange > sp.to) {\n              nextChange = sp.to;\n              spanEndStyle = \"\";\n            }\n            if (m.className) { spanStyle += \" \" + m.className; }\n            if (m.css) { css = (css ? css + \";\" : \"\") + m.css; }\n            if (m.startStyle && sp.from == pos) { spanStartStyle += \" \" + m.startStyle; }\n            if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }\n            // support for the old title property\n            // https://github.com/codemirror/CodeMirror/pull/5673\n            if (m.title) { (attributes || (attributes = {})).title = m.title; }\n            if (m.attributes) {\n              for (var attr in m.attributes)\n                { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }\n            }\n            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))\n              { collapsed = sp; }\n          } else if (sp.from > pos && nextChange > sp.from) {\n            nextChange = sp.from;\n          }\n        }\n        if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)\n          { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += \" \" + endStyles[j$1]; } } }\n\n        if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)\n          { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }\n        if (collapsed && (collapsed.from || 0) == pos) {\n          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,\n                             collapsed.marker, collapsed.from == null);\n          if (collapsed.to == null) { return }\n          if (collapsed.to == pos) { collapsed = false; }\n        }\n      }\n      if (pos >= len) { break }\n\n      var upto = Math.min(len, nextChange);\n      while (true) {\n        if (text) {\n          var end = pos + text.length;\n          if (!collapsed) {\n            var tokenText = end > upto ? text.slice(0, upto - pos) : text;\n            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,\n                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : \"\", css, attributes);\n          }\n          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}\n          pos = end;\n          spanStartStyle = \"\";\n        }\n        text = allText.slice(at, at = styles[i++]);\n        style = interpretTokenStyle(styles[i++], builder.cm.options);\n      }\n    }\n  }\n\n\n  // These objects are used to represent the visible (currently drawn)\n  // part of the document. A LineView may correspond to multiple\n  // logical lines, if those are connected by collapsed ranges.\n  function LineView(doc, line, lineN) {\n    // The starting line\n    this.line = line;\n    // Continuing lines, if any\n    this.rest = visualLineContinued(line);\n    // Number of logical lines in this visual line\n    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;\n    this.node = this.text = null;\n    this.hidden = lineIsHidden(doc, line);\n  }\n\n  // Create a range of LineView objects for the given lines.\n  function buildViewArray(cm, from, to) {\n    var array = [], nextPos;\n    for (var pos = from; pos < to; pos = nextPos) {\n      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);\n      nextPos = pos + view.size;\n      array.push(view);\n    }\n    return array\n  }\n\n  var operationGroup = null;\n\n  function pushOperation(op) {\n    if (operationGroup) {\n      operationGroup.ops.push(op);\n    } else {\n      op.ownsGroup = operationGroup = {\n        ops: [op],\n        delayedCallbacks: []\n      };\n    }\n  }\n\n  function fireCallbacksForOps(group) {\n    // Calls delayed callbacks and cursorActivity handlers until no\n    // new ones appear\n    var callbacks = group.delayedCallbacks, i = 0;\n    do {\n      for (; i < callbacks.length; i++)\n        { callbacks[i].call(null); }\n      for (var j = 0; j < group.ops.length; j++) {\n        var op = group.ops[j];\n        if (op.cursorActivityHandlers)\n          { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)\n            { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }\n      }\n    } while (i < callbacks.length)\n  }\n\n  function finishOperation(op, endCb) {\n    var group = op.ownsGroup;\n    if (!group) { return }\n\n    try { fireCallbacksForOps(group); }\n    finally {\n      operationGroup = null;\n      endCb(group);\n    }\n  }\n\n  var orphanDelayedCallbacks = null;\n\n  // Often, we want to signal events at a point where we are in the\n  // middle of some work, but don't want the handler to start calling\n  // other methods on the editor, which might be in an inconsistent\n  // state or simply not expect any other events to happen.\n  // signalLater looks whether there are any handlers, and schedules\n  // them to be executed when the last operation ends, or, if no\n  // operation is active, when a timeout fires.\n  function signalLater(emitter, type /*, values...*/) {\n    var arr = getHandlers(emitter, type);\n    if (!arr.length) { return }\n    var args = Array.prototype.slice.call(arguments, 2), list;\n    if (operationGroup) {\n      list = operationGroup.delayedCallbacks;\n    } else if (orphanDelayedCallbacks) {\n      list = orphanDelayedCallbacks;\n    } else {\n      list = orphanDelayedCallbacks = [];\n      setTimeout(fireOrphanDelayed, 0);\n    }\n    var loop = function ( i ) {\n      list.push(function () { return arr[i].apply(null, args); });\n    };\n\n    for (var i = 0; i < arr.length; ++i)\n      loop( i );\n  }\n\n  function fireOrphanDelayed() {\n    var delayed = orphanDelayedCallbacks;\n    orphanDelayedCallbacks = null;\n    for (var i = 0; i < delayed.length; ++i) { delayed[i](); }\n  }\n\n  // When an aspect of a line changes, a string is added to\n  // lineView.changes. This updates the relevant part of the line's\n  // DOM structure.\n  function updateLineForChanges(cm, lineView, lineN, dims) {\n    for (var j = 0; j < lineView.changes.length; j++) {\n      var type = lineView.changes[j];\n      if (type == \"text\") { updateLineText(cm, lineView); }\n      else if (type == \"gutter\") { updateLineGutter(cm, lineView, lineN, dims); }\n      else if (type == \"class\") { updateLineClasses(cm, lineView); }\n      else if (type == \"widget\") { updateLineWidgets(cm, lineView, dims); }\n    }\n    lineView.changes = null;\n  }\n\n  // Lines with gutter elements, widgets or a background class need to\n  // be wrapped, and have the extra elements added to the wrapper div\n  function ensureLineWrapped(lineView) {\n    if (lineView.node == lineView.text) {\n      lineView.node = elt(\"div\", null, null, \"position: relative\");\n      if (lineView.text.parentNode)\n        { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }\n      lineView.node.appendChild(lineView.text);\n      if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }\n    }\n    return lineView.node\n  }\n\n  function updateLineBackground(cm, lineView) {\n    var cls = lineView.bgClass ? lineView.bgClass + \" \" + (lineView.line.bgClass || \"\") : lineView.line.bgClass;\n    if (cls) { cls += \" CodeMirror-linebackground\"; }\n    if (lineView.background) {\n      if (cls) { lineView.background.className = cls; }\n      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }\n    } else if (cls) {\n      var wrap = ensureLineWrapped(lineView);\n      lineView.background = wrap.insertBefore(elt(\"div\", null, cls), wrap.firstChild);\n      cm.display.input.setUneditable(lineView.background);\n    }\n  }\n\n  // Wrapper around buildLineContent which will reuse the structure\n  // in display.externalMeasured when possible.\n  function getLineContent(cm, lineView) {\n    var ext = cm.display.externalMeasured;\n    if (ext && ext.line == lineView.line) {\n      cm.display.externalMeasured = null;\n      lineView.measure = ext.measure;\n      return ext.built\n    }\n    return buildLineContent(cm, lineView)\n  }\n\n  // Redraw the line's text. Interacts with the background and text\n  // classes because the mode may output tokens that influence these\n  // classes.\n  function updateLineText(cm, lineView) {\n    var cls = lineView.text.className;\n    var built = getLineContent(cm, lineView);\n    if (lineView.text == lineView.node) { lineView.node = built.pre; }\n    lineView.text.parentNode.replaceChild(built.pre, lineView.text);\n    lineView.text = built.pre;\n    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {\n      lineView.bgClass = built.bgClass;\n      lineView.textClass = built.textClass;\n      updateLineClasses(cm, lineView);\n    } else if (cls) {\n      lineView.text.className = cls;\n    }\n  }\n\n  function updateLineClasses(cm, lineView) {\n    updateLineBackground(cm, lineView);\n    if (lineView.line.wrapClass)\n      { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }\n    else if (lineView.node != lineView.text)\n      { lineView.node.className = \"\"; }\n    var textClass = lineView.textClass ? lineView.textClass + \" \" + (lineView.line.textClass || \"\") : lineView.line.textClass;\n    lineView.text.className = textClass || \"\";\n  }\n\n  function updateLineGutter(cm, lineView, lineN, dims) {\n    if (lineView.gutter) {\n      lineView.node.removeChild(lineView.gutter);\n      lineView.gutter = null;\n    }\n    if (lineView.gutterBackground) {\n      lineView.node.removeChild(lineView.gutterBackground);\n      lineView.gutterBackground = null;\n    }\n    if (lineView.line.gutterClass) {\n      var wrap = ensureLineWrapped(lineView);\n      lineView.gutterBackground = elt(\"div\", null, \"CodeMirror-gutter-background \" + lineView.line.gutterClass,\n                                      (\"left: \" + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + \"px; width: \" + (dims.gutterTotalWidth) + \"px\"));\n      cm.display.input.setUneditable(lineView.gutterBackground);\n      wrap.insertBefore(lineView.gutterBackground, lineView.text);\n    }\n    var markers = lineView.line.gutterMarkers;\n    if (cm.options.lineNumbers || markers) {\n      var wrap$1 = ensureLineWrapped(lineView);\n      var gutterWrap = lineView.gutter = elt(\"div\", null, \"CodeMirror-gutter-wrapper\", (\"left: \" + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + \"px\"));\n      gutterWrap.setAttribute(\"aria-hidden\", \"true\");\n      cm.display.input.setUneditable(gutterWrap);\n      wrap$1.insertBefore(gutterWrap, lineView.text);\n      if (lineView.line.gutterClass)\n        { gutterWrap.className += \" \" + lineView.line.gutterClass; }\n      if (cm.options.lineNumbers && (!markers || !markers[\"CodeMirror-linenumbers\"]))\n        { lineView.lineNumber = gutterWrap.appendChild(\n          elt(\"div\", lineNumberFor(cm.options, lineN),\n              \"CodeMirror-linenumber CodeMirror-gutter-elt\",\n              (\"left: \" + (dims.gutterLeft[\"CodeMirror-linenumbers\"]) + \"px; width: \" + (cm.display.lineNumInnerWidth) + \"px\"))); }\n      if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {\n        var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];\n        if (found)\n          { gutterWrap.appendChild(elt(\"div\", [found], \"CodeMirror-gutter-elt\",\n                                     (\"left: \" + (dims.gutterLeft[id]) + \"px; width: \" + (dims.gutterWidth[id]) + \"px\"))); }\n      } }\n    }\n  }\n\n  function updateLineWidgets(cm, lineView, dims) {\n    if (lineView.alignable) { lineView.alignable = null; }\n    var isWidget = classTest(\"CodeMirror-linewidget\");\n    for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {\n      next = node.nextSibling;\n      if (isWidget.test(node.className)) { lineView.node.removeChild(node); }\n    }\n    insertLineWidgets(cm, lineView, dims);\n  }\n\n  // Build a line's DOM representation from scratch\n  function buildLineElement(cm, lineView, lineN, dims) {\n    var built = getLineContent(cm, lineView);\n    lineView.text = lineView.node = built.pre;\n    if (built.bgClass) { lineView.bgClass = built.bgClass; }\n    if (built.textClass) { lineView.textClass = built.textClass; }\n\n    updateLineClasses(cm, lineView);\n    updateLineGutter(cm, lineView, lineN, dims);\n    insertLineWidgets(cm, lineView, dims);\n    return lineView.node\n  }\n\n  // A lineView may contain multiple logical lines (when merged by\n  // collapsed spans). The widgets for all of them need to be drawn.\n  function insertLineWidgets(cm, lineView, dims) {\n    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);\n    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)\n      { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }\n  }\n\n  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {\n    if (!line.widgets) { return }\n    var wrap = ensureLineWrapped(lineView);\n    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {\n      var widget = ws[i], node = elt(\"div\", [widget.node], \"CodeMirror-linewidget\" + (widget.className ? \" \" + widget.className : \"\"));\n      if (!widget.handleMouseEvents) { node.setAttribute(\"cm-ignore-events\", \"true\"); }\n      positionLineWidget(widget, node, lineView, dims);\n      cm.display.input.setUneditable(node);\n      if (allowAbove && widget.above)\n        { wrap.insertBefore(node, lineView.gutter || lineView.text); }\n      else\n        { wrap.appendChild(node); }\n      signalLater(widget, \"redraw\");\n    }\n  }\n\n  function positionLineWidget(widget, node, lineView, dims) {\n    if (widget.noHScroll) {\n  (lineView.alignable || (lineView.alignable = [])).push(node);\n      var width = dims.wrapperWidth;\n      node.style.left = dims.fixedPos + \"px\";\n      if (!widget.coverGutter) {\n        width -= dims.gutterTotalWidth;\n        node.style.paddingLeft = dims.gutterTotalWidth + \"px\";\n      }\n      node.style.width = width + \"px\";\n    }\n    if (widget.coverGutter) {\n      node.style.zIndex = 5;\n      node.style.position = \"relative\";\n      if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + \"px\"; }\n    }\n  }\n\n  function widgetHeight(widget) {\n    if (widget.height != null) { return widget.height }\n    var cm = widget.doc.cm;\n    if (!cm) { return 0 }\n    if (!contains(document.body, widget.node)) {\n      var parentStyle = \"position: relative;\";\n      if (widget.coverGutter)\n        { parentStyle += \"margin-left: -\" + cm.display.gutters.offsetWidth + \"px;\"; }\n      if (widget.noHScroll)\n        { parentStyle += \"width: \" + cm.display.wrapper.clientWidth + \"px;\"; }\n      removeChildrenAndAdd(cm.display.measure, elt(\"div\", [widget.node], null, parentStyle));\n    }\n    return widget.height = widget.node.parentNode.offsetHeight\n  }\n\n  // Return true when the given mouse event happened in a widget\n  function eventInWidget(display, e) {\n    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {\n      if (!n || (n.nodeType == 1 && n.getAttribute(\"cm-ignore-events\") == \"true\") ||\n          (n.parentNode == display.sizer && n != display.mover))\n        { return true }\n    }\n  }\n\n  // POSITION MEASUREMENT\n\n  function paddingTop(display) {return display.lineSpace.offsetTop}\n  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}\n  function paddingH(display) {\n    if (display.cachedPaddingH) { return display.cachedPaddingH }\n    var e = removeChildrenAndAdd(display.measure, elt(\"pre\", \"x\", \"CodeMirror-line-like\"));\n    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;\n    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};\n    if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }\n    return data\n  }\n\n  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }\n  function displayWidth(cm) {\n    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth\n  }\n  function displayHeight(cm) {\n    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight\n  }\n\n  // Ensure the lineView.wrapping.heights array is populated. This is\n  // an array of bottom offsets for the lines that make up a drawn\n  // line. When lineWrapping is on, there might be more than one\n  // height.\n  function ensureLineHeights(cm, lineView, rect) {\n    var wrapping = cm.options.lineWrapping;\n    var curWidth = wrapping && displayWidth(cm);\n    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {\n      var heights = lineView.measure.heights = [];\n      if (wrapping) {\n        lineView.measure.width = curWidth;\n        var rects = lineView.text.firstChild.getClientRects();\n        for (var i = 0; i < rects.length - 1; i++) {\n          var cur = rects[i], next = rects[i + 1];\n          if (Math.abs(cur.bottom - next.bottom) > 2)\n            { heights.push((cur.bottom + next.top) / 2 - rect.top); }\n        }\n      }\n      heights.push(rect.bottom - rect.top);\n    }\n  }\n\n  // Find a line map (mapping character offsets to text nodes) and a\n  // measurement cache for the given line number. (A line view might\n  // contain multiple lines when collapsed ranges are present.)\n  function mapFromLineView(lineView, line, lineN) {\n    if (lineView.line == line)\n      { return {map: lineView.measure.map, cache: lineView.measure.cache} }\n    if (lineView.rest) {\n      for (var i = 0; i < lineView.rest.length; i++)\n        { if (lineView.rest[i] == line)\n          { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }\n      for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)\n        { if (lineNo(lineView.rest[i$1]) > lineN)\n          { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }\n    }\n  }\n\n  // Render a line into the hidden node display.externalMeasured. Used\n  // when measurement is needed for a line that's not in the viewport.\n  function updateExternalMeasurement(cm, line) {\n    line = visualLine(line);\n    var lineN = lineNo(line);\n    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);\n    view.lineN = lineN;\n    var built = view.built = buildLineContent(cm, view);\n    view.text = built.pre;\n    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);\n    return view\n  }\n\n  // Get a {top, bottom, left, right} box (in line-local coordinates)\n  // for a given character.\n  function measureChar(cm, line, ch, bias) {\n    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)\n  }\n\n  // Find a line view that corresponds to the given line number.\n  function findViewForLine(cm, lineN) {\n    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)\n      { return cm.display.view[findViewIndex(cm, lineN)] }\n    var ext = cm.display.externalMeasured;\n    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)\n      { return ext }\n  }\n\n  // Measurement can be split in two steps, the set-up work that\n  // applies to the whole line, and the measurement of the actual\n  // character. Functions like coordsChar, that need to do a lot of\n  // measurements in a row, can thus ensure that the set-up work is\n  // only done once.\n  function prepareMeasureForLine(cm, line) {\n    var lineN = lineNo(line);\n    var view = findViewForLine(cm, lineN);\n    if (view && !view.text) {\n      view = null;\n    } else if (view && view.changes) {\n      updateLineForChanges(cm, view, lineN, getDimensions(cm));\n      cm.curOp.forceUpdate = true;\n    }\n    if (!view)\n      { view = updateExternalMeasurement(cm, line); }\n\n    var info = mapFromLineView(view, line, lineN);\n    return {\n      line: line, view: view, rect: null,\n      map: info.map, cache: info.cache, before: info.before,\n      hasHeights: false\n    }\n  }\n\n  // Given a prepared measurement object, measures the position of an\n  // actual character (or fetches it from the cache).\n  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {\n    if (prepared.before) { ch = -1; }\n    var key = ch + (bias || \"\"), found;\n    if (prepared.cache.hasOwnProperty(key)) {\n      found = prepared.cache[key];\n    } else {\n      if (!prepared.rect)\n        { prepared.rect = prepared.view.text.getBoundingClientRect(); }\n      if (!prepared.hasHeights) {\n        ensureLineHeights(cm, prepared.view, prepared.rect);\n        prepared.hasHeights = true;\n      }\n      found = measureCharInner(cm, prepared, ch, bias);\n      if (!found.bogus) { prepared.cache[key] = found; }\n    }\n    return {left: found.left, right: found.right,\n            top: varHeight ? found.rtop : found.top,\n            bottom: varHeight ? found.rbottom : found.bottom}\n  }\n\n  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};\n\n  function nodeAndOffsetInLineMap(map, ch, bias) {\n    var node, start, end, collapse, mStart, mEnd;\n    // First, search the line map for the text node corresponding to,\n    // or closest to, the target character.\n    for (var i = 0; i < map.length; i += 3) {\n      mStart = map[i];\n      mEnd = map[i + 1];\n      if (ch < mStart) {\n        start = 0; end = 1;\n        collapse = \"left\";\n      } else if (ch < mEnd) {\n        start = ch - mStart;\n        end = start + 1;\n      } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {\n        end = mEnd - mStart;\n        start = end - 1;\n        if (ch >= mEnd) { collapse = \"right\"; }\n      }\n      if (start != null) {\n        node = map[i + 2];\n        if (mStart == mEnd && bias == (node.insertLeft ? \"left\" : \"right\"))\n          { collapse = bias; }\n        if (bias == \"left\" && start == 0)\n          { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {\n            node = map[(i -= 3) + 2];\n            collapse = \"left\";\n          } }\n        if (bias == \"right\" && start == mEnd - mStart)\n          { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {\n            node = map[(i += 3) + 2];\n            collapse = \"right\";\n          } }\n        break\n      }\n    }\n    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}\n  }\n\n  function getUsefulRect(rects, bias) {\n    var rect = nullRect;\n    if (bias == \"left\") { for (var i = 0; i < rects.length; i++) {\n      if ((rect = rects[i]).left != rect.right) { break }\n    } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {\n      if ((rect = rects[i$1]).left != rect.right) { break }\n    } }\n    return rect\n  }\n\n  function measureCharInner(cm, prepared, ch, bias) {\n    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);\n    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;\n\n    var rect;\n    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.\n      for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned\n        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }\n        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }\n        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)\n          { rect = node.parentNode.getBoundingClientRect(); }\n        else\n          { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }\n        if (rect.left || rect.right || start == 0) { break }\n        end = start;\n        start = start - 1;\n        collapse = \"right\";\n      }\n      if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }\n    } else { // If it is a widget, simply get the box for the whole widget.\n      if (start > 0) { collapse = bias = \"right\"; }\n      var rects;\n      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)\n        { rect = rects[bias == \"right\" ? rects.length - 1 : 0]; }\n      else\n        { rect = node.getBoundingClientRect(); }\n    }\n    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {\n      var rSpan = node.parentNode.getClientRects()[0];\n      if (rSpan)\n        { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }\n      else\n        { rect = nullRect; }\n    }\n\n    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;\n    var mid = (rtop + rbot) / 2;\n    var heights = prepared.view.measure.heights;\n    var i = 0;\n    for (; i < heights.length - 1; i++)\n      { if (mid < heights[i]) { break } }\n    var top = i ? heights[i - 1] : 0, bot = heights[i];\n    var result = {left: (collapse == \"right\" ? rect.right : rect.left) - prepared.rect.left,\n                  right: (collapse == \"left\" ? rect.left : rect.right) - prepared.rect.left,\n                  top: top, bottom: bot};\n    if (!rect.left && !rect.right) { result.bogus = true; }\n    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }\n\n    return result\n  }\n\n  // Work around problem with bounding client rects on ranges being\n  // returned incorrectly when zoomed on IE10 and below.\n  function maybeUpdateRectForZooming(measure, rect) {\n    if (!window.screen || screen.logicalXDPI == null ||\n        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))\n      { return rect }\n    var scaleX = screen.logicalXDPI / screen.deviceXDPI;\n    var scaleY = screen.logicalYDPI / screen.deviceYDPI;\n    return {left: rect.left * scaleX, right: rect.right * scaleX,\n            top: rect.top * scaleY, bottom: rect.bottom * scaleY}\n  }\n\n  function clearLineMeasurementCacheFor(lineView) {\n    if (lineView.measure) {\n      lineView.measure.cache = {};\n      lineView.measure.heights = null;\n      if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)\n        { lineView.measure.caches[i] = {}; } }\n    }\n  }\n\n  function clearLineMeasurementCache(cm) {\n    cm.display.externalMeasure = null;\n    removeChildren(cm.display.lineMeasure);\n    for (var i = 0; i < cm.display.view.length; i++)\n      { clearLineMeasurementCacheFor(cm.display.view[i]); }\n  }\n\n  function clearCaches(cm) {\n    clearLineMeasurementCache(cm);\n    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;\n    if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }\n    cm.display.lineNumChars = null;\n  }\n\n  function pageScrollX(doc) {\n    // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206\n    // which causes page_Offset and bounding client rects to use\n    // different reference viewports and invalidate our calculations.\n    if (chrome && android) { return -(doc.body.getBoundingClientRect().left - parseInt(getComputedStyle(doc.body).marginLeft)) }\n    return doc.defaultView.pageXOffset || (doc.documentElement || doc.body).scrollLeft\n  }\n  function pageScrollY(doc) {\n    if (chrome && android) { return -(doc.body.getBoundingClientRect().top - parseInt(getComputedStyle(doc.body).marginTop)) }\n    return doc.defaultView.pageYOffset || (doc.documentElement || doc.body).scrollTop\n  }\n\n  function widgetTopHeight(lineObj) {\n    var ref = visualLine(lineObj);\n    var widgets = ref.widgets;\n    var height = 0;\n    if (widgets) { for (var i = 0; i < widgets.length; ++i) { if (widgets[i].above)\n      { height += widgetHeight(widgets[i]); } } }\n    return height\n  }\n\n  // Converts a {top, bottom, left, right} box from line-local\n  // coordinates into another coordinate system. Context may be one of\n  // \"line\", \"div\" (display.lineDiv), \"local\"./null (editor), \"window\",\n  // or \"page\".\n  function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {\n    if (!includeWidgets) {\n      var height = widgetTopHeight(lineObj);\n      rect.top += height; rect.bottom += height;\n    }\n    if (context == \"line\") { return rect }\n    if (!context) { context = \"local\"; }\n    var yOff = heightAtLine(lineObj);\n    if (context == \"local\") { yOff += paddingTop(cm.display); }\n    else { yOff -= cm.display.viewOffset; }\n    if (context == \"page\" || context == \"window\") {\n      var lOff = cm.display.lineSpace.getBoundingClientRect();\n      yOff += lOff.top + (context == \"window\" ? 0 : pageScrollY(doc(cm)));\n      var xOff = lOff.left + (context == \"window\" ? 0 : pageScrollX(doc(cm)));\n      rect.left += xOff; rect.right += xOff;\n    }\n    rect.top += yOff; rect.bottom += yOff;\n    return rect\n  }\n\n  // Coverts a box from \"div\" coords to another coordinate system.\n  // Context may be \"window\", \"page\", \"div\", or \"local\"./null.\n  function fromCoordSystem(cm, coords, context) {\n    if (context == \"div\") { return coords }\n    var left = coords.left, top = coords.top;\n    // First move into \"page\" coordinate system\n    if (context == \"page\") {\n      left -= pageScrollX(doc(cm));\n      top -= pageScrollY(doc(cm));\n    } else if (context == \"local\" || !context) {\n      var localBox = cm.display.sizer.getBoundingClientRect();\n      left += localBox.left;\n      top += localBox.top;\n    }\n\n    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();\n    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}\n  }\n\n  function charCoords(cm, pos, context, lineObj, bias) {\n    if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }\n    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)\n  }\n\n  // Returns a box for a given cursor position, which may have an\n  // 'other' property containing the position of the secondary cursor\n  // on a bidi boundary.\n  // A cursor Pos(line, char, \"before\") is on the same visual line as `char - 1`\n  // and after `char - 1` in writing order of `char - 1`\n  // A cursor Pos(line, char, \"after\") is on the same visual line as `char`\n  // and before `char` in writing order of `char`\n  // Examples (upper-case letters are RTL, lower-case are LTR):\n  //     Pos(0, 1, ...)\n  //     before   after\n  // ab     a|b     a|b\n  // aB     a|B     aB|\n  // Ab     |Ab     A|b\n  // AB     B|A     B|A\n  // Every position after the last character on a line is considered to stick\n  // to the last character on the line.\n  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {\n    lineObj = lineObj || getLine(cm.doc, pos.line);\n    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }\n    function get(ch, right) {\n      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? \"right\" : \"left\", varHeight);\n      if (right) { m.left = m.right; } else { m.right = m.left; }\n      return intoCoordSystem(cm, lineObj, m, context)\n    }\n    var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;\n    if (ch >= lineObj.text.length) {\n      ch = lineObj.text.length;\n      sticky = \"before\";\n    } else if (ch <= 0) {\n      ch = 0;\n      sticky = \"after\";\n    }\n    if (!order) { return get(sticky == \"before\" ? ch - 1 : ch, sticky == \"before\") }\n\n    function getBidi(ch, partPos, invert) {\n      var part = order[partPos], right = part.level == 1;\n      return get(invert ? ch - 1 : ch, right != invert)\n    }\n    var partPos = getBidiPartAt(order, ch, sticky);\n    var other = bidiOther;\n    var val = getBidi(ch, partPos, sticky == \"before\");\n    if (other != null) { val.other = getBidi(ch, other, sticky != \"before\"); }\n    return val\n  }\n\n  // Used to cheaply estimate the coordinates for a position. Used for\n  // intermediate scroll updates.\n  function estimateCoords(cm, pos) {\n    var left = 0;\n    pos = clipPos(cm.doc, pos);\n    if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }\n    var lineObj = getLine(cm.doc, pos.line);\n    var top = heightAtLine(lineObj) + paddingTop(cm.display);\n    return {left: left, right: left, top: top, bottom: top + lineObj.height}\n  }\n\n  // Positions returned by coordsChar contain some extra information.\n  // xRel is the relative x position of the input coordinates compared\n  // to the found position (so xRel > 0 means the coordinates are to\n  // the right of the character position, for example). When outside\n  // is true, that means the coordinates lie outside the line's\n  // vertical range.\n  function PosWithInfo(line, ch, sticky, outside, xRel) {\n    var pos = Pos(line, ch, sticky);\n    pos.xRel = xRel;\n    if (outside) { pos.outside = outside; }\n    return pos\n  }\n\n  // Compute the character position closest to the given coordinates.\n  // Input must be lineSpace-local (\"div\" coordinate system).\n  function coordsChar(cm, x, y) {\n    var doc = cm.doc;\n    y += cm.display.viewOffset;\n    if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }\n    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;\n    if (lineN > last)\n      { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }\n    if (x < 0) { x = 0; }\n\n    var lineObj = getLine(doc, lineN);\n    for (;;) {\n      var found = coordsCharInner(cm, lineObj, lineN, x, y);\n      var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));\n      if (!collapsed) { return found }\n      var rangeEnd = collapsed.find(1);\n      if (rangeEnd.line == lineN) { return rangeEnd }\n      lineObj = getLine(doc, lineN = rangeEnd.line);\n    }\n  }\n\n  function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {\n    y -= widgetTopHeight(lineObj);\n    var end = lineObj.text.length;\n    var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);\n    end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);\n    return {begin: begin, end: end}\n  }\n\n  function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {\n    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }\n    var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), \"line\").top;\n    return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)\n  }\n\n  // Returns true if the given side of a box is after the given\n  // coordinates, in top-to-bottom, left-to-right order.\n  function boxIsAfter(box, x, y, left) {\n    return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x\n  }\n\n  function coordsCharInner(cm, lineObj, lineNo, x, y) {\n    // Move y into line-local coordinate space\n    y -= heightAtLine(lineObj);\n    var preparedMeasure = prepareMeasureForLine(cm, lineObj);\n    // When directly calling `measureCharPrepared`, we have to adjust\n    // for the widgets at this line.\n    var widgetHeight = widgetTopHeight(lineObj);\n    var begin = 0, end = lineObj.text.length, ltr = true;\n\n    var order = getOrder(lineObj, cm.doc.direction);\n    // If the line isn't plain left-to-right text, first figure out\n    // which bidi section the coordinates fall into.\n    if (order) {\n      var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)\n                   (cm, lineObj, lineNo, preparedMeasure, order, x, y);\n      ltr = part.level != 1;\n      // The awkward -1 offsets are needed because findFirst (called\n      // on these below) will treat its first bound as inclusive,\n      // second as exclusive, but we want to actually address the\n      // characters in the part's range\n      begin = ltr ? part.from : part.to - 1;\n      end = ltr ? part.to : part.from - 1;\n    }\n\n    // A binary search to find the first character whose bounding box\n    // starts after the coordinates. If we run across any whose box wrap\n    // the coordinates, store that.\n    var chAround = null, boxAround = null;\n    var ch = findFirst(function (ch) {\n      var box = measureCharPrepared(cm, preparedMeasure, ch);\n      box.top += widgetHeight; box.bottom += widgetHeight;\n      if (!boxIsAfter(box, x, y, false)) { return false }\n      if (box.top <= y && box.left <= x) {\n        chAround = ch;\n        boxAround = box;\n      }\n      return true\n    }, begin, end);\n\n    var baseX, sticky, outside = false;\n    // If a box around the coordinates was found, use that\n    if (boxAround) {\n      // Distinguish coordinates nearer to the left or right side of the box\n      var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;\n      ch = chAround + (atStart ? 0 : 1);\n      sticky = atStart ? \"after\" : \"before\";\n      baseX = atLeft ? boxAround.left : boxAround.right;\n    } else {\n      // (Adjust for extended bound, if necessary.)\n      if (!ltr && (ch == end || ch == begin)) { ch++; }\n      // To determine which side to associate with, get the box to the\n      // left of the character and compare it's vertical position to the\n      // coordinates\n      sticky = ch == 0 ? \"after\" : ch == lineObj.text.length ? \"before\" :\n        (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?\n        \"after\" : \"before\";\n      // Now get accurate coordinates for this place, in order to get a\n      // base X position\n      var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), \"line\", lineObj, preparedMeasure);\n      baseX = coords.left;\n      outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;\n    }\n\n    ch = skipExtendingChars(lineObj.text, ch, 1);\n    return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)\n  }\n\n  function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {\n    // Bidi parts are sorted left-to-right, and in a non-line-wrapping\n    // situation, we can take this ordering to correspond to the visual\n    // ordering. This finds the first part whose end is after the given\n    // coordinates.\n    var index = findFirst(function (i) {\n      var part = order[i], ltr = part.level != 1;\n      return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? \"before\" : \"after\"),\n                                     \"line\", lineObj, preparedMeasure), x, y, true)\n    }, 0, order.length - 1);\n    var part = order[index];\n    // If this isn't the first part, the part's start is also after\n    // the coordinates, and the coordinates aren't on the same line as\n    // that start, move one part back.\n    if (index > 0) {\n      var ltr = part.level != 1;\n      var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? \"after\" : \"before\"),\n                               \"line\", lineObj, preparedMeasure);\n      if (boxIsAfter(start, x, y, true) && start.top > y)\n        { part = order[index - 1]; }\n    }\n    return part\n  }\n\n  function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {\n    // In a wrapped line, rtl text on wrapping boundaries can do things\n    // that don't correspond to the ordering in our `order` array at\n    // all, so a binary search doesn't work, and we want to return a\n    // part that only spans one line so that the binary search in\n    // coordsCharInner is safe. As such, we first find the extent of the\n    // wrapped line, and then do a flat search in which we discard any\n    // spans that aren't on the line.\n    var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);\n    var begin = ref.begin;\n    var end = ref.end;\n    if (/\\s/.test(lineObj.text.charAt(end - 1))) { end--; }\n    var part = null, closestDist = null;\n    for (var i = 0; i < order.length; i++) {\n      var p = order[i];\n      if (p.from >= end || p.to <= begin) { continue }\n      var ltr = p.level != 1;\n      var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;\n      // Weigh against spans ending before this, so that they are only\n      // picked if nothing ends after\n      var dist = endX < x ? x - endX + 1e9 : endX - x;\n      if (!part || closestDist > dist) {\n        part = p;\n        closestDist = dist;\n      }\n    }\n    if (!part) { part = order[order.length - 1]; }\n    // Clip the part to the wrapped line.\n    if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }\n    if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }\n    return part\n  }\n\n  var measureText;\n  // Compute the default text height.\n  function textHeight(display) {\n    if (display.cachedTextHeight != null) { return display.cachedTextHeight }\n    if (measureText == null) {\n      measureText = elt(\"pre\", null, \"CodeMirror-line-like\");\n      // Measure a bunch of lines, for browsers that compute\n      // fractional heights.\n      for (var i = 0; i < 49; ++i) {\n        measureText.appendChild(document.createTextNode(\"x\"));\n        measureText.appendChild(elt(\"br\"));\n      }\n      measureText.appendChild(document.createTextNode(\"x\"));\n    }\n    removeChildrenAndAdd(display.measure, measureText);\n    var height = measureText.offsetHeight / 50;\n    if (height > 3) { display.cachedTextHeight = height; }\n    removeChildren(display.measure);\n    return height || 1\n  }\n\n  // Compute the default character width.\n  function charWidth(display) {\n    if (display.cachedCharWidth != null) { return display.cachedCharWidth }\n    var anchor = elt(\"span\", \"xxxxxxxxxx\");\n    var pre = elt(\"pre\", [anchor], \"CodeMirror-line-like\");\n    removeChildrenAndAdd(display.measure, pre);\n    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;\n    if (width > 2) { display.cachedCharWidth = width; }\n    return width || 10\n  }\n\n  // Do a bulk-read of the DOM positions and sizes needed to draw the\n  // view, so that we don't interleave reading and writing to the DOM.\n  function getDimensions(cm) {\n    var d = cm.display, left = {}, width = {};\n    var gutterLeft = d.gutters.clientLeft;\n    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {\n      var id = cm.display.gutterSpecs[i].className;\n      left[id] = n.offsetLeft + n.clientLeft + gutterLeft;\n      width[id] = n.clientWidth;\n    }\n    return {fixedPos: compensateForHScroll(d),\n            gutterTotalWidth: d.gutters.offsetWidth,\n            gutterLeft: left,\n            gutterWidth: width,\n            wrapperWidth: d.wrapper.clientWidth}\n  }\n\n  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,\n  // but using getBoundingClientRect to get a sub-pixel-accurate\n  // result.\n  function compensateForHScroll(display) {\n    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left\n  }\n\n  // Returns a function that estimates the height of a line, to use as\n  // first approximation until the line becomes visible (and is thus\n  // properly measurable).\n  function estimateHeight(cm) {\n    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;\n    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);\n    return function (line) {\n      if (lineIsHidden(cm.doc, line)) { return 0 }\n\n      var widgetsHeight = 0;\n      if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {\n        if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }\n      } }\n\n      if (wrapping)\n        { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }\n      else\n        { return widgetsHeight + th }\n    }\n  }\n\n  function estimateLineHeights(cm) {\n    var doc = cm.doc, est = estimateHeight(cm);\n    doc.iter(function (line) {\n      var estHeight = est(line);\n      if (estHeight != line.height) { updateLineHeight(line, estHeight); }\n    });\n  }\n\n  // Given a mouse event, find the corresponding position. If liberal\n  // is false, it checks whether a gutter or scrollbar was clicked,\n  // and returns null if it was. forRect is used by rectangular\n  // selections, and tries to estimate a character position even for\n  // coordinates beyond the right of the text.\n  function posFromMouse(cm, e, liberal, forRect) {\n    var display = cm.display;\n    if (!liberal && e_target(e).getAttribute(\"cm-not-content\") == \"true\") { return null }\n\n    var x, y, space = display.lineSpace.getBoundingClientRect();\n    // Fails unpredictably on IE[67] when mouse is dragged around quickly.\n    try { x = e.clientX - space.left; y = e.clientY - space.top; }\n    catch (e$1) { return null }\n    var coords = coordsChar(cm, x, y), line;\n    if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {\n      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;\n      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));\n    }\n    return coords\n  }\n\n  // Find the view element corresponding to a given line. Return null\n  // when the line isn't visible.\n  function findViewIndex(cm, n) {\n    if (n >= cm.display.viewTo) { return null }\n    n -= cm.display.viewFrom;\n    if (n < 0) { return null }\n    var view = cm.display.view;\n    for (var i = 0; i < view.length; i++) {\n      n -= view[i].size;\n      if (n < 0) { return i }\n    }\n  }\n\n  // Updates the display.view data structure for a given change to the\n  // document. From and to are in pre-change coordinates. Lendiff is\n  // the amount of lines added or subtracted by the change. This is\n  // used for changes that span multiple lines, or change the way\n  // lines are divided into visual lines. regLineChange (below)\n  // registers single-line changes.\n  function regChange(cm, from, to, lendiff) {\n    if (from == null) { from = cm.doc.first; }\n    if (to == null) { to = cm.doc.first + cm.doc.size; }\n    if (!lendiff) { lendiff = 0; }\n\n    var display = cm.display;\n    if (lendiff && to < display.viewTo &&\n        (display.updateLineNumbers == null || display.updateLineNumbers > from))\n      { display.updateLineNumbers = from; }\n\n    cm.curOp.viewChanged = true;\n\n    if (from >= display.viewTo) { // Change after\n      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)\n        { resetView(cm); }\n    } else if (to <= display.viewFrom) { // Change before\n      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {\n        resetView(cm);\n      } else {\n        display.viewFrom += lendiff;\n        display.viewTo += lendiff;\n      }\n    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap\n      resetView(cm);\n    } else if (from <= display.viewFrom) { // Top overlap\n      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);\n      if (cut) {\n        display.view = display.view.slice(cut.index);\n        display.viewFrom = cut.lineN;\n        display.viewTo += lendiff;\n      } else {\n        resetView(cm);\n      }\n    } else if (to >= display.viewTo) { // Bottom overlap\n      var cut$1 = viewCuttingPoint(cm, from, from, -1);\n      if (cut$1) {\n        display.view = display.view.slice(0, cut$1.index);\n        display.viewTo = cut$1.lineN;\n      } else {\n        resetView(cm);\n      }\n    } else { // Gap in the middle\n      var cutTop = viewCuttingPoint(cm, from, from, -1);\n      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);\n      if (cutTop && cutBot) {\n        display.view = display.view.slice(0, cutTop.index)\n          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))\n          .concat(display.view.slice(cutBot.index));\n        display.viewTo += lendiff;\n      } else {\n        resetView(cm);\n      }\n    }\n\n    var ext = display.externalMeasured;\n    if (ext) {\n      if (to < ext.lineN)\n        { ext.lineN += lendiff; }\n      else if (from < ext.lineN + ext.size)\n        { display.externalMeasured = null; }\n    }\n  }\n\n  // Register a change to a single line. Type must be one of \"text\",\n  // \"gutter\", \"class\", \"widget\"\n  function regLineChange(cm, line, type) {\n    cm.curOp.viewChanged = true;\n    var display = cm.display, ext = cm.display.externalMeasured;\n    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)\n      { display.externalMeasured = null; }\n\n    if (line < display.viewFrom || line >= display.viewTo) { return }\n    var lineView = display.view[findViewIndex(cm, line)];\n    if (lineView.node == null) { return }\n    var arr = lineView.changes || (lineView.changes = []);\n    if (indexOf(arr, type) == -1) { arr.push(type); }\n  }\n\n  // Clear the view.\n  function resetView(cm) {\n    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;\n    cm.display.view = [];\n    cm.display.viewOffset = 0;\n  }\n\n  function viewCuttingPoint(cm, oldN, newN, dir) {\n    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;\n    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)\n      { return {index: index, lineN: newN} }\n    var n = cm.display.viewFrom;\n    for (var i = 0; i < index; i++)\n      { n += view[i].size; }\n    if (n != oldN) {\n      if (dir > 0) {\n        if (index == view.length - 1) { return null }\n        diff = (n + view[index].size) - oldN;\n        index++;\n      } else {\n        diff = n - oldN;\n      }\n      oldN += diff; newN += diff;\n    }\n    while (visualLineNo(cm.doc, newN) != newN) {\n      if (index == (dir < 0 ? 0 : view.length - 1)) { return null }\n      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;\n      index += dir;\n    }\n    return {index: index, lineN: newN}\n  }\n\n  // Force the view to cover a given range, adding empty view element\n  // or clipping off existing ones as needed.\n  function adjustView(cm, from, to) {\n    var display = cm.display, view = display.view;\n    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {\n      display.view = buildViewArray(cm, from, to);\n      display.viewFrom = from;\n    } else {\n      if (display.viewFrom > from)\n        { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }\n      else if (display.viewFrom < from)\n        { display.view = display.view.slice(findViewIndex(cm, from)); }\n      display.viewFrom = from;\n      if (display.viewTo < to)\n        { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }\n      else if (display.viewTo > to)\n        { display.view = display.view.slice(0, findViewIndex(cm, to)); }\n    }\n    display.viewTo = to;\n  }\n\n  // Count the number of lines in the view whose DOM representation is\n  // out of date (or nonexistent).\n  function countDirtyView(cm) {\n    var view = cm.display.view, dirty = 0;\n    for (var i = 0; i < view.length; i++) {\n      var lineView = view[i];\n      if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }\n    }\n    return dirty\n  }\n\n  function updateSelection(cm) {\n    cm.display.input.showSelection(cm.display.input.prepareSelection());\n  }\n\n  function prepareSelection(cm, primary) {\n    if ( primary === void 0 ) primary = true;\n\n    var doc = cm.doc, result = {};\n    var curFragment = result.cursors = document.createDocumentFragment();\n    var selFragment = result.selection = document.createDocumentFragment();\n\n    var customCursor = cm.options.$customCursor;\n    if (customCursor) { primary = true; }\n    for (var i = 0; i < doc.sel.ranges.length; i++) {\n      if (!primary && i == doc.sel.primIndex) { continue }\n      var range = doc.sel.ranges[i];\n      if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }\n      var collapsed = range.empty();\n      if (customCursor) {\n        var head = customCursor(cm, range);\n        if (head) { drawSelectionCursor(cm, head, curFragment); }\n      } else if (collapsed || cm.options.showCursorWhenSelecting) {\n        drawSelectionCursor(cm, range.head, curFragment);\n      }\n      if (!collapsed)\n        { drawSelectionRange(cm, range, selFragment); }\n    }\n    return result\n  }\n\n  // Draws a cursor for the given range\n  function drawSelectionCursor(cm, head, output) {\n    var pos = cursorCoords(cm, head, \"div\", null, null, !cm.options.singleCursorHeightPerLine);\n\n    var cursor = output.appendChild(elt(\"div\", \"\\u00a0\", \"CodeMirror-cursor\"));\n    cursor.style.left = pos.left + \"px\";\n    cursor.style.top = pos.top + \"px\";\n    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + \"px\";\n\n    if (/\\bcm-fat-cursor\\b/.test(cm.getWrapperElement().className)) {\n      var charPos = charCoords(cm, head, \"div\", null, null);\n      var width = charPos.right - charPos.left;\n      cursor.style.width = (width > 0 ? width : cm.defaultCharWidth()) + \"px\";\n    }\n\n    if (pos.other) {\n      // Secondary cursor, shown when on a 'jump' in bi-directional text\n      var otherCursor = output.appendChild(elt(\"div\", \"\\u00a0\", \"CodeMirror-cursor CodeMirror-secondarycursor\"));\n      otherCursor.style.display = \"\";\n      otherCursor.style.left = pos.other.left + \"px\";\n      otherCursor.style.top = pos.other.top + \"px\";\n      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + \"px\";\n    }\n  }\n\n  function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }\n\n  // Draws the given range as a highlighted selection\n  function drawSelectionRange(cm, range, output) {\n    var display = cm.display, doc = cm.doc;\n    var fragment = document.createDocumentFragment();\n    var padding = paddingH(cm.display), leftSide = padding.left;\n    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;\n    var docLTR = doc.direction == \"ltr\";\n\n    function add(left, top, width, bottom) {\n      if (top < 0) { top = 0; }\n      top = Math.round(top);\n      bottom = Math.round(bottom);\n      fragment.appendChild(elt(\"div\", null, \"CodeMirror-selected\", (\"position: absolute; left: \" + left + \"px;\\n                             top: \" + top + \"px; width: \" + (width == null ? rightSide - left : width) + \"px;\\n                             height: \" + (bottom - top) + \"px\")));\n    }\n\n    function drawForLine(line, fromArg, toArg) {\n      var lineObj = getLine(doc, line);\n      var lineLen = lineObj.text.length;\n      var start, end;\n      function coords(ch, bias) {\n        return charCoords(cm, Pos(line, ch), \"div\", lineObj, bias)\n      }\n\n      function wrapX(pos, dir, side) {\n        var extent = wrappedLineExtentChar(cm, lineObj, null, pos);\n        var prop = (dir == \"ltr\") == (side == \"after\") ? \"left\" : \"right\";\n        var ch = side == \"after\" ? extent.begin : extent.end - (/\\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);\n        return coords(ch, prop)[prop]\n      }\n\n      var order = getOrder(lineObj, doc.direction);\n      iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {\n        var ltr = dir == \"ltr\";\n        var fromPos = coords(from, ltr ? \"left\" : \"right\");\n        var toPos = coords(to - 1, ltr ? \"right\" : \"left\");\n\n        var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;\n        var first = i == 0, last = !order || i == order.length - 1;\n        if (toPos.top - fromPos.top <= 3) { // Single line\n          var openLeft = (docLTR ? openStart : openEnd) && first;\n          var openRight = (docLTR ? openEnd : openStart) && last;\n          var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;\n          var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;\n          add(left, fromPos.top, right - left, fromPos.bottom);\n        } else { // Multiple lines\n          var topLeft, topRight, botLeft, botRight;\n          if (ltr) {\n            topLeft = docLTR && openStart && first ? leftSide : fromPos.left;\n            topRight = docLTR ? rightSide : wrapX(from, dir, \"before\");\n            botLeft = docLTR ? leftSide : wrapX(to, dir, \"after\");\n            botRight = docLTR && openEnd && last ? rightSide : toPos.right;\n          } else {\n            topLeft = !docLTR ? leftSide : wrapX(from, dir, \"before\");\n            topRight = !docLTR && openStart && first ? rightSide : fromPos.right;\n            botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;\n            botRight = !docLTR ? rightSide : wrapX(to, dir, \"after\");\n          }\n          add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);\n          if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }\n          add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);\n        }\n\n        if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }\n        if (cmpCoords(toPos, start) < 0) { start = toPos; }\n        if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }\n        if (cmpCoords(toPos, end) < 0) { end = toPos; }\n      });\n      return {start: start, end: end}\n    }\n\n    var sFrom = range.from(), sTo = range.to();\n    if (sFrom.line == sTo.line) {\n      drawForLine(sFrom.line, sFrom.ch, sTo.ch);\n    } else {\n      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);\n      var singleVLine = visualLine(fromLine) == visualLine(toLine);\n      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;\n      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;\n      if (singleVLine) {\n        if (leftEnd.top < rightStart.top - 2) {\n          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);\n          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);\n        } else {\n          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);\n        }\n      }\n      if (leftEnd.bottom < rightStart.top)\n        { add(leftSide, leftEnd.bottom, null, rightStart.top); }\n    }\n\n    output.appendChild(fragment);\n  }\n\n  // Cursor-blinking\n  function restartBlink(cm) {\n    if (!cm.state.focused) { return }\n    var display = cm.display;\n    clearInterval(display.blinker);\n    var on = true;\n    display.cursorDiv.style.visibility = \"\";\n    if (cm.options.cursorBlinkRate > 0)\n      { display.blinker = setInterval(function () {\n        if (!cm.hasFocus()) { onBlur(cm); }\n        display.cursorDiv.style.visibility = (on = !on) ? \"\" : \"hidden\";\n      }, cm.options.cursorBlinkRate); }\n    else if (cm.options.cursorBlinkRate < 0)\n      { display.cursorDiv.style.visibility = \"hidden\"; }\n  }\n\n  function ensureFocus(cm) {\n    if (!cm.hasFocus()) {\n      cm.display.input.focus();\n      if (!cm.state.focused) { onFocus(cm); }\n    }\n  }\n\n  function delayBlurEvent(cm) {\n    cm.state.delayingBlurEvent = true;\n    setTimeout(function () { if (cm.state.delayingBlurEvent) {\n      cm.state.delayingBlurEvent = false;\n      if (cm.state.focused) { onBlur(cm); }\n    } }, 100);\n  }\n\n  function onFocus(cm, e) {\n    if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; }\n\n    if (cm.options.readOnly == \"nocursor\") { return }\n    if (!cm.state.focused) {\n      signal(cm, \"focus\", cm, e);\n      cm.state.focused = true;\n      addClass(cm.display.wrapper, \"CodeMirror-focused\");\n      // This test prevents this from firing when a context\n      // menu is closed (since the input reset would kill the\n      // select-all detection hack)\n      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {\n        cm.display.input.reset();\n        if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730\n      }\n      cm.display.input.receivedFocus();\n    }\n    restartBlink(cm);\n  }\n  function onBlur(cm, e) {\n    if (cm.state.delayingBlurEvent) { return }\n\n    if (cm.state.focused) {\n      signal(cm, \"blur\", cm, e);\n      cm.state.focused = false;\n      rmClass(cm.display.wrapper, \"CodeMirror-focused\");\n    }\n    clearInterval(cm.display.blinker);\n    setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);\n  }\n\n  // Read the actual heights of the rendered lines, and update their\n  // stored heights to match.\n  function updateHeightsInViewport(cm) {\n    var display = cm.display;\n    var prevBottom = display.lineDiv.offsetTop;\n    var viewTop = Math.max(0, display.scroller.getBoundingClientRect().top);\n    var oldHeight = display.lineDiv.getBoundingClientRect().top;\n    var mustScroll = 0;\n    for (var i = 0; i < display.view.length; i++) {\n      var cur = display.view[i], wrapping = cm.options.lineWrapping;\n      var height = (void 0), width = 0;\n      if (cur.hidden) { continue }\n      oldHeight += cur.line.height;\n      if (ie && ie_version < 8) {\n        var bot = cur.node.offsetTop + cur.node.offsetHeight;\n        height = bot - prevBottom;\n        prevBottom = bot;\n      } else {\n        var box = cur.node.getBoundingClientRect();\n        height = box.bottom - box.top;\n        // Check that lines don't extend past the right of the current\n        // editor width\n        if (!wrapping && cur.text.firstChild)\n          { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }\n      }\n      var diff = cur.line.height - height;\n      if (diff > .005 || diff < -.005) {\n        if (oldHeight < viewTop) { mustScroll -= diff; }\n        updateLineHeight(cur.line, height);\n        updateWidgetHeight(cur.line);\n        if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)\n          { updateWidgetHeight(cur.rest[j]); } }\n      }\n      if (width > cm.display.sizerWidth) {\n        var chWidth = Math.ceil(width / charWidth(cm.display));\n        if (chWidth > cm.display.maxLineLength) {\n          cm.display.maxLineLength = chWidth;\n          cm.display.maxLine = cur.line;\n          cm.display.maxLineChanged = true;\n        }\n      }\n    }\n    if (Math.abs(mustScroll) > 2) { display.scroller.scrollTop += mustScroll; }\n  }\n\n  // Read and store the height of line widgets associated with the\n  // given line.\n  function updateWidgetHeight(line) {\n    if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {\n      var w = line.widgets[i], parent = w.node.parentNode;\n      if (parent) { w.height = parent.offsetHeight; }\n    } }\n  }\n\n  // Compute the lines that are visible in a given viewport (defaults\n  // the the current scroll position). viewport may contain top,\n  // height, and ensure (see op.scrollToPos) properties.\n  function visibleLines(display, doc, viewport) {\n    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;\n    top = Math.floor(top - paddingTop(display));\n    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;\n\n    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);\n    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and\n    // forces those lines into the viewport (if possible).\n    if (viewport && viewport.ensure) {\n      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;\n      if (ensureFrom < from) {\n        from = ensureFrom;\n        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);\n      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {\n        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);\n        to = ensureTo;\n      }\n    }\n    return {from: from, to: Math.max(to, from + 1)}\n  }\n\n  // SCROLLING THINGS INTO VIEW\n\n  // If an editor sits on the top or bottom of the window, partially\n  // scrolled out of view, this ensures that the cursor is visible.\n  function maybeScrollWindow(cm, rect) {\n    if (signalDOMEvent(cm, \"scrollCursorIntoView\")) { return }\n\n    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;\n    var doc = display.wrapper.ownerDocument;\n    if (rect.top + box.top < 0) { doScroll = true; }\n    else if (rect.bottom + box.top > (doc.defaultView.innerHeight || doc.documentElement.clientHeight)) { doScroll = false; }\n    if (doScroll != null && !phantom) {\n      var scrollNode = elt(\"div\", \"\\u200b\", null, (\"position: absolute;\\n                         top: \" + (rect.top - display.viewOffset - paddingTop(cm.display)) + \"px;\\n                         height: \" + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + \"px;\\n                         left: \" + (rect.left) + \"px; width: \" + (Math.max(2, rect.right - rect.left)) + \"px;\"));\n      cm.display.lineSpace.appendChild(scrollNode);\n      scrollNode.scrollIntoView(doScroll);\n      cm.display.lineSpace.removeChild(scrollNode);\n    }\n  }\n\n  // Scroll a given position into view (immediately), verifying that\n  // it actually became visible (as line heights are accurately\n  // measured, the position of something may 'drift' during drawing).\n  function scrollPosIntoView(cm, pos, end, margin) {\n    if (margin == null) { margin = 0; }\n    var rect;\n    if (!cm.options.lineWrapping && pos == end) {\n      // Set pos and end to the cursor positions around the character pos sticks to\n      // If pos.sticky == \"before\", that is around pos.ch - 1, otherwise around pos.ch\n      // If pos == Pos(_, 0, \"before\"), pos and end are unchanged\n      end = pos.sticky == \"before\" ? Pos(pos.line, pos.ch + 1, \"before\") : pos;\n      pos = pos.ch ? Pos(pos.line, pos.sticky == \"before\" ? pos.ch - 1 : pos.ch, \"after\") : pos;\n    }\n    for (var limit = 0; limit < 5; limit++) {\n      var changed = false;\n      var coords = cursorCoords(cm, pos);\n      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);\n      rect = {left: Math.min(coords.left, endCoords.left),\n              top: Math.min(coords.top, endCoords.top) - margin,\n              right: Math.max(coords.left, endCoords.left),\n              bottom: Math.max(coords.bottom, endCoords.bottom) + margin};\n      var scrollPos = calculateScrollPos(cm, rect);\n      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;\n      if (scrollPos.scrollTop != null) {\n        updateScrollTop(cm, scrollPos.scrollTop);\n        if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }\n      }\n      if (scrollPos.scrollLeft != null) {\n        setScrollLeft(cm, scrollPos.scrollLeft);\n        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }\n      }\n      if (!changed) { break }\n    }\n    return rect\n  }\n\n  // Scroll a given set of coordinates into view (immediately).\n  function scrollIntoView(cm, rect) {\n    var scrollPos = calculateScrollPos(cm, rect);\n    if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }\n    if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }\n  }\n\n  // Calculate a new scroll position needed to scroll the given\n  // rectangle into view. Returns an object with scrollTop and\n  // scrollLeft properties. When these are undefined, the\n  // vertical/horizontal position does not need to be adjusted.\n  function calculateScrollPos(cm, rect) {\n    var display = cm.display, snapMargin = textHeight(cm.display);\n    if (rect.top < 0) { rect.top = 0; }\n    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;\n    var screen = displayHeight(cm), result = {};\n    if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }\n    var docBottom = cm.doc.height + paddingVert(display);\n    var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;\n    if (rect.top < screentop) {\n      result.scrollTop = atTop ? 0 : rect.top;\n    } else if (rect.bottom > screentop + screen) {\n      var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);\n      if (newTop != screentop) { result.scrollTop = newTop; }\n    }\n\n    var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth;\n    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace;\n    var screenw = displayWidth(cm) - display.gutters.offsetWidth;\n    var tooWide = rect.right - rect.left > screenw;\n    if (tooWide) { rect.right = rect.left + screenw; }\n    if (rect.left < 10)\n      { result.scrollLeft = 0; }\n    else if (rect.left < screenleft)\n      { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); }\n    else if (rect.right > screenw + screenleft - 3)\n      { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }\n    return result\n  }\n\n  // Store a relative adjustment to the scroll position in the current\n  // operation (to be applied when the operation finishes).\n  function addToScrollTop(cm, top) {\n    if (top == null) { return }\n    resolveScrollToPos(cm);\n    cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;\n  }\n\n  // Make sure that at the end of the operation the current cursor is\n  // shown.\n  function ensureCursorVisible(cm) {\n    resolveScrollToPos(cm);\n    var cur = cm.getCursor();\n    cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};\n  }\n\n  function scrollToCoords(cm, x, y) {\n    if (x != null || y != null) { resolveScrollToPos(cm); }\n    if (x != null) { cm.curOp.scrollLeft = x; }\n    if (y != null) { cm.curOp.scrollTop = y; }\n  }\n\n  function scrollToRange(cm, range) {\n    resolveScrollToPos(cm);\n    cm.curOp.scrollToPos = range;\n  }\n\n  // When an operation has its scrollToPos property set, and another\n  // scroll action is applied before the end of the operation, this\n  // 'simulates' scrolling that position into view in a cheap way, so\n  // that the effect of intermediate scroll commands is not ignored.\n  function resolveScrollToPos(cm) {\n    var range = cm.curOp.scrollToPos;\n    if (range) {\n      cm.curOp.scrollToPos = null;\n      var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);\n      scrollToCoordsRange(cm, from, to, range.margin);\n    }\n  }\n\n  function scrollToCoordsRange(cm, from, to, margin) {\n    var sPos = calculateScrollPos(cm, {\n      left: Math.min(from.left, to.left),\n      top: Math.min(from.top, to.top) - margin,\n      right: Math.max(from.right, to.right),\n      bottom: Math.max(from.bottom, to.bottom) + margin\n    });\n    scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);\n  }\n\n  // Sync the scrollable area and scrollbars, ensure the viewport\n  // covers the visible area.\n  function updateScrollTop(cm, val) {\n    if (Math.abs(cm.doc.scrollTop - val) < 2) { return }\n    if (!gecko) { updateDisplaySimple(cm, {top: val}); }\n    setScrollTop(cm, val, true);\n    if (gecko) { updateDisplaySimple(cm); }\n    startWorker(cm, 100);\n  }\n\n  function setScrollTop(cm, val, forceScroll) {\n    val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val));\n    if (cm.display.scroller.scrollTop == val && !forceScroll) { return }\n    cm.doc.scrollTop = val;\n    cm.display.scrollbars.setScrollTop(val);\n    if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }\n  }\n\n  // Sync scroller and scrollbar, ensure the gutter elements are\n  // aligned.\n  function setScrollLeft(cm, val, isScroller, forceScroll) {\n    val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth));\n    if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }\n    cm.doc.scrollLeft = val;\n    alignHorizontally(cm);\n    if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }\n    cm.display.scrollbars.setScrollLeft(val);\n  }\n\n  // SCROLLBARS\n\n  // Prepare DOM reads needed to update the scrollbars. Done in one\n  // shot to minimize update/measure roundtrips.\n  function measureForScrollbars(cm) {\n    var d = cm.display, gutterW = d.gutters.offsetWidth;\n    var docH = Math.round(cm.doc.height + paddingVert(cm.display));\n    return {\n      clientHeight: d.scroller.clientHeight,\n      viewHeight: d.wrapper.clientHeight,\n      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,\n      viewWidth: d.wrapper.clientWidth,\n      barLeft: cm.options.fixedGutter ? gutterW : 0,\n      docHeight: docH,\n      scrollHeight: docH + scrollGap(cm) + d.barHeight,\n      nativeBarWidth: d.nativeBarWidth,\n      gutterWidth: gutterW\n    }\n  }\n\n  var NativeScrollbars = function(place, scroll, cm) {\n    this.cm = cm;\n    var vert = this.vert = elt(\"div\", [elt(\"div\", null, null, \"min-width: 1px\")], \"CodeMirror-vscrollbar\");\n    var horiz = this.horiz = elt(\"div\", [elt(\"div\", null, null, \"height: 100%; min-height: 1px\")], \"CodeMirror-hscrollbar\");\n    vert.tabIndex = horiz.tabIndex = -1;\n    place(vert); place(horiz);\n\n    on(vert, \"scroll\", function () {\n      if (vert.clientHeight) { scroll(vert.scrollTop, \"vertical\"); }\n    });\n    on(horiz, \"scroll\", function () {\n      if (horiz.clientWidth) { scroll(horiz.scrollLeft, \"horizontal\"); }\n    });\n\n    this.checkedZeroWidth = false;\n    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).\n    if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = \"18px\"; }\n  };\n\n  NativeScrollbars.prototype.update = function (measure) {\n    var needsH = measure.scrollWidth > measure.clientWidth + 1;\n    var needsV = measure.scrollHeight > measure.clientHeight + 1;\n    var sWidth = measure.nativeBarWidth;\n\n    if (needsV) {\n      this.vert.style.display = \"block\";\n      this.vert.style.bottom = needsH ? sWidth + \"px\" : \"0\";\n      var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);\n      // A bug in IE8 can cause this value to be negative, so guard it.\n      this.vert.firstChild.style.height =\n        Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + \"px\";\n    } else {\n      this.vert.scrollTop = 0;\n      this.vert.style.display = \"\";\n      this.vert.firstChild.style.height = \"0\";\n    }\n\n    if (needsH) {\n      this.horiz.style.display = \"block\";\n      this.horiz.style.right = needsV ? sWidth + \"px\" : \"0\";\n      this.horiz.style.left = measure.barLeft + \"px\";\n      var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);\n      this.horiz.firstChild.style.width =\n        Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + \"px\";\n    } else {\n      this.horiz.style.display = \"\";\n      this.horiz.firstChild.style.width = \"0\";\n    }\n\n    if (!this.checkedZeroWidth && measure.clientHeight > 0) {\n      if (sWidth == 0) { this.zeroWidthHack(); }\n      this.checkedZeroWidth = true;\n    }\n\n    return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}\n  };\n\n  NativeScrollbars.prototype.setScrollLeft = function (pos) {\n    if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }\n    if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, \"horiz\"); }\n  };\n\n  NativeScrollbars.prototype.setScrollTop = function (pos) {\n    if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }\n    if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, \"vert\"); }\n  };\n\n  NativeScrollbars.prototype.zeroWidthHack = function () {\n    var w = mac && !mac_geMountainLion ? \"12px\" : \"18px\";\n    this.horiz.style.height = this.vert.style.width = w;\n    this.horiz.style.visibility = this.vert.style.visibility = \"hidden\";\n    this.disableHoriz = new Delayed;\n    this.disableVert = new Delayed;\n  };\n\n  NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {\n    bar.style.visibility = \"\";\n    function maybeDisable() {\n      // To find out whether the scrollbar is still visible, we\n      // check whether the element under the pixel in the bottom\n      // right corner of the scrollbar box is the scrollbar box\n      // itself (when the bar is still visible) or its filler child\n      // (when the bar is hidden). If it is still visible, we keep\n      // it enabled, if it's hidden, we disable pointer events.\n      var box = bar.getBoundingClientRect();\n      var elt = type == \"vert\" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)\n          : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);\n      if (elt != bar) { bar.style.visibility = \"hidden\"; }\n      else { delay.set(1000, maybeDisable); }\n    }\n    delay.set(1000, maybeDisable);\n  };\n\n  NativeScrollbars.prototype.clear = function () {\n    var parent = this.horiz.parentNode;\n    parent.removeChild(this.horiz);\n    parent.removeChild(this.vert);\n  };\n\n  var NullScrollbars = function () {};\n\n  NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };\n  NullScrollbars.prototype.setScrollLeft = function () {};\n  NullScrollbars.prototype.setScrollTop = function () {};\n  NullScrollbars.prototype.clear = function () {};\n\n  function updateScrollbars(cm, measure) {\n    if (!measure) { measure = measureForScrollbars(cm); }\n    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;\n    updateScrollbarsInner(cm, measure);\n    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {\n      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)\n        { updateHeightsInViewport(cm); }\n      updateScrollbarsInner(cm, measureForScrollbars(cm));\n      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;\n    }\n  }\n\n  // Re-synchronize the fake scrollbars with the actual size of the\n  // content.\n  function updateScrollbarsInner(cm, measure) {\n    var d = cm.display;\n    var sizes = d.scrollbars.update(measure);\n\n    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + \"px\";\n    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + \"px\";\n    d.heightForcer.style.borderBottom = sizes.bottom + \"px solid transparent\";\n\n    if (sizes.right && sizes.bottom) {\n      d.scrollbarFiller.style.display = \"block\";\n      d.scrollbarFiller.style.height = sizes.bottom + \"px\";\n      d.scrollbarFiller.style.width = sizes.right + \"px\";\n    } else { d.scrollbarFiller.style.display = \"\"; }\n    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {\n      d.gutterFiller.style.display = \"block\";\n      d.gutterFiller.style.height = sizes.bottom + \"px\";\n      d.gutterFiller.style.width = measure.gutterWidth + \"px\";\n    } else { d.gutterFiller.style.display = \"\"; }\n  }\n\n  var scrollbarModel = {\"native\": NativeScrollbars, \"null\": NullScrollbars};\n\n  function initScrollbars(cm) {\n    if (cm.display.scrollbars) {\n      cm.display.scrollbars.clear();\n      if (cm.display.scrollbars.addClass)\n        { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }\n    }\n\n    cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {\n      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);\n      // Prevent clicks in the scrollbars from killing focus\n      on(node, \"mousedown\", function () {\n        if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }\n      });\n      node.setAttribute(\"cm-not-content\", \"true\");\n    }, function (pos, axis) {\n      if (axis == \"horizontal\") { setScrollLeft(cm, pos); }\n      else { updateScrollTop(cm, pos); }\n    }, cm);\n    if (cm.display.scrollbars.addClass)\n      { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }\n  }\n\n  // Operations are used to wrap a series of changes to the editor\n  // state in such a way that each change won't have to update the\n  // cursor and display (which would be awkward, slow, and\n  // error-prone). Instead, display updates are batched and then all\n  // combined and executed at once.\n\n  var nextOpId = 0;\n  // Start a new operation.\n  function startOperation(cm) {\n    cm.curOp = {\n      cm: cm,\n      viewChanged: false,      // Flag that indicates that lines might need to be redrawn\n      startHeight: cm.doc.height, // Used to detect need to update scrollbar\n      forceUpdate: false,      // Used to force a redraw\n      updateInput: 0,       // Whether to reset the input textarea\n      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)\n      changeObjs: null,        // Accumulated changes, for firing change events\n      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on\n      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already\n      selectionChanged: false, // Whether the selection needs to be redrawn\n      updateMaxLine: false,    // Set when the widest line needs to be determined anew\n      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet\n      scrollToPos: null,       // Used to scroll to a specific position\n      focus: false,\n      id: ++nextOpId,          // Unique ID\n      markArrays: null         // Used by addMarkedSpan\n    };\n    pushOperation(cm.curOp);\n  }\n\n  // Finish an operation, updating the display and signalling delayed events\n  function endOperation(cm) {\n    var op = cm.curOp;\n    if (op) { finishOperation(op, function (group) {\n      for (var i = 0; i < group.ops.length; i++)\n        { group.ops[i].cm.curOp = null; }\n      endOperations(group);\n    }); }\n  }\n\n  // The DOM updates done when an operation finishes are batched so\n  // that the minimum number of relayouts are required.\n  function endOperations(group) {\n    var ops = group.ops;\n    for (var i = 0; i < ops.length; i++) // Read DOM\n      { endOperation_R1(ops[i]); }\n    for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)\n      { endOperation_W1(ops[i$1]); }\n    for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM\n      { endOperation_R2(ops[i$2]); }\n    for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)\n      { endOperation_W2(ops[i$3]); }\n    for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM\n      { endOperation_finish(ops[i$4]); }\n  }\n\n  function endOperation_R1(op) {\n    var cm = op.cm, display = cm.display;\n    maybeClipScrollbars(cm);\n    if (op.updateMaxLine) { findMaxLine(cm); }\n\n    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||\n      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||\n                         op.scrollToPos.to.line >= display.viewTo) ||\n      display.maxLineChanged && cm.options.lineWrapping;\n    op.update = op.mustUpdate &&\n      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);\n  }\n\n  function endOperation_W1(op) {\n    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);\n  }\n\n  function endOperation_R2(op) {\n    var cm = op.cm, display = cm.display;\n    if (op.updatedDisplay) { updateHeightsInViewport(cm); }\n\n    op.barMeasure = measureForScrollbars(cm);\n\n    // If the max line changed since it was last measured, measure it,\n    // and ensure the document's width matches it.\n    // updateDisplay_W2 will use these properties to do the actual resizing\n    if (display.maxLineChanged && !cm.options.lineWrapping) {\n      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;\n      cm.display.sizerWidth = op.adjustWidthTo;\n      op.barMeasure.scrollWidth =\n        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);\n      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));\n    }\n\n    if (op.updatedDisplay || op.selectionChanged)\n      { op.preparedSelection = display.input.prepareSelection(); }\n  }\n\n  function endOperation_W2(op) {\n    var cm = op.cm;\n\n    if (op.adjustWidthTo != null) {\n      cm.display.sizer.style.minWidth = op.adjustWidthTo + \"px\";\n      if (op.maxScrollLeft < cm.doc.scrollLeft)\n        { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }\n      cm.display.maxLineChanged = false;\n    }\n\n    var takeFocus = op.focus && op.focus == activeElt(root(cm));\n    if (op.preparedSelection)\n      { cm.display.input.showSelection(op.preparedSelection, takeFocus); }\n    if (op.updatedDisplay || op.startHeight != cm.doc.height)\n      { updateScrollbars(cm, op.barMeasure); }\n    if (op.updatedDisplay)\n      { setDocumentHeight(cm, op.barMeasure); }\n\n    if (op.selectionChanged) { restartBlink(cm); }\n\n    if (cm.state.focused && op.updateInput)\n      { cm.display.input.reset(op.typing); }\n    if (takeFocus) { ensureFocus(op.cm); }\n  }\n\n  function endOperation_finish(op) {\n    var cm = op.cm, display = cm.display, doc = cm.doc;\n\n    if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }\n\n    // Abort mouse wheel delta measurement, when scrolling explicitly\n    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))\n      { display.wheelStartX = display.wheelStartY = null; }\n\n    // Propagate the scroll position to the actual DOM scroller\n    if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }\n\n    if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }\n    // If we need to scroll a specific position into view, do so.\n    if (op.scrollToPos) {\n      var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),\n                                   clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);\n      maybeScrollWindow(cm, rect);\n    }\n\n    // Fire events for markers that are hidden/unidden by editing or\n    // undoing\n    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;\n    if (hidden) { for (var i = 0; i < hidden.length; ++i)\n      { if (!hidden[i].lines.length) { signal(hidden[i], \"hide\"); } } }\n    if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)\n      { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], \"unhide\"); } } }\n\n    if (display.wrapper.offsetHeight)\n      { doc.scrollTop = cm.display.scroller.scrollTop; }\n\n    // Fire change events, and delayed event handlers\n    if (op.changeObjs)\n      { signal(cm, \"changes\", cm, op.changeObjs); }\n    if (op.update)\n      { op.update.finish(); }\n  }\n\n  // Run the given function in an operation\n  function runInOp(cm, f) {\n    if (cm.curOp) { return f() }\n    startOperation(cm);\n    try { return f() }\n    finally { endOperation(cm); }\n  }\n  // Wraps a function in an operation. Returns the wrapped function.\n  function operation(cm, f) {\n    return function() {\n      if (cm.curOp) { return f.apply(cm, arguments) }\n      startOperation(cm);\n      try { return f.apply(cm, arguments) }\n      finally { endOperation(cm); }\n    }\n  }\n  // Used to add methods to editor and doc instances, wrapping them in\n  // operations.\n  function methodOp(f) {\n    return function() {\n      if (this.curOp) { return f.apply(this, arguments) }\n      startOperation(this);\n      try { return f.apply(this, arguments) }\n      finally { endOperation(this); }\n    }\n  }\n  function docMethodOp(f) {\n    return function() {\n      var cm = this.cm;\n      if (!cm || cm.curOp) { return f.apply(this, arguments) }\n      startOperation(cm);\n      try { return f.apply(this, arguments) }\n      finally { endOperation(cm); }\n    }\n  }\n\n  // HIGHLIGHT WORKER\n\n  function startWorker(cm, time) {\n    if (cm.doc.highlightFrontier < cm.display.viewTo)\n      { cm.state.highlight.set(time, bind(highlightWorker, cm)); }\n  }\n\n  function highlightWorker(cm) {\n    var doc = cm.doc;\n    if (doc.highlightFrontier >= cm.display.viewTo) { return }\n    var end = +new Date + cm.options.workTime;\n    var context = getContextBefore(cm, doc.highlightFrontier);\n    var changedLines = [];\n\n    doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {\n      if (context.line >= cm.display.viewFrom) { // Visible\n        var oldStyles = line.styles;\n        var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;\n        var highlighted = highlightLine(cm, line, context, true);\n        if (resetState) { context.state = resetState; }\n        line.styles = highlighted.styles;\n        var oldCls = line.styleClasses, newCls = highlighted.classes;\n        if (newCls) { line.styleClasses = newCls; }\n        else if (oldCls) { line.styleClasses = null; }\n        var ischange = !oldStyles || oldStyles.length != line.styles.length ||\n          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);\n        for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }\n        if (ischange) { changedLines.push(context.line); }\n        line.stateAfter = context.save();\n        context.nextLine();\n      } else {\n        if (line.text.length <= cm.options.maxHighlightLength)\n          { processLine(cm, line.text, context); }\n        line.stateAfter = context.line % 5 == 0 ? context.save() : null;\n        context.nextLine();\n      }\n      if (+new Date > end) {\n        startWorker(cm, cm.options.workDelay);\n        return true\n      }\n    });\n    doc.highlightFrontier = context.line;\n    doc.modeFrontier = Math.max(doc.modeFrontier, context.line);\n    if (changedLines.length) { runInOp(cm, function () {\n      for (var i = 0; i < changedLines.length; i++)\n        { regLineChange(cm, changedLines[i], \"text\"); }\n    }); }\n  }\n\n  // DISPLAY DRAWING\n\n  var DisplayUpdate = function(cm, viewport, force) {\n    var display = cm.display;\n\n    this.viewport = viewport;\n    // Store some values that we'll need later (but don't want to force a relayout for)\n    this.visible = visibleLines(display, cm.doc, viewport);\n    this.editorIsHidden = !display.wrapper.offsetWidth;\n    this.wrapperHeight = display.wrapper.clientHeight;\n    this.wrapperWidth = display.wrapper.clientWidth;\n    this.oldDisplayWidth = displayWidth(cm);\n    this.force = force;\n    this.dims = getDimensions(cm);\n    this.events = [];\n  };\n\n  DisplayUpdate.prototype.signal = function (emitter, type) {\n    if (hasHandler(emitter, type))\n      { this.events.push(arguments); }\n  };\n  DisplayUpdate.prototype.finish = function () {\n    for (var i = 0; i < this.events.length; i++)\n      { signal.apply(null, this.events[i]); }\n  };\n\n  function maybeClipScrollbars(cm) {\n    var display = cm.display;\n    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {\n      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;\n      display.heightForcer.style.height = scrollGap(cm) + \"px\";\n      display.sizer.style.marginBottom = -display.nativeBarWidth + \"px\";\n      display.sizer.style.borderRightWidth = scrollGap(cm) + \"px\";\n      display.scrollbarsClipped = true;\n    }\n  }\n\n  function selectionSnapshot(cm) {\n    if (cm.hasFocus()) { return null }\n    var active = activeElt(root(cm));\n    if (!active || !contains(cm.display.lineDiv, active)) { return null }\n    var result = {activeElt: active};\n    if (window.getSelection) {\n      var sel = win(cm).getSelection();\n      if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {\n        result.anchorNode = sel.anchorNode;\n        result.anchorOffset = sel.anchorOffset;\n        result.focusNode = sel.focusNode;\n        result.focusOffset = sel.focusOffset;\n      }\n    }\n    return result\n  }\n\n  function restoreSelection(snapshot) {\n    if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt(rootNode(snapshot.activeElt))) { return }\n    snapshot.activeElt.focus();\n    if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) &&\n        snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {\n      var doc = snapshot.activeElt.ownerDocument;\n      var sel = doc.defaultView.getSelection(), range = doc.createRange();\n      range.setEnd(snapshot.anchorNode, snapshot.anchorOffset);\n      range.collapse(false);\n      sel.removeAllRanges();\n      sel.addRange(range);\n      sel.extend(snapshot.focusNode, snapshot.focusOffset);\n    }\n  }\n\n  // Does the actual updating of the line display. Bails out\n  // (returning false) when there is nothing to be done and forced is\n  // false.\n  function updateDisplayIfNeeded(cm, update) {\n    var display = cm.display, doc = cm.doc;\n\n    if (update.editorIsHidden) {\n      resetView(cm);\n      return false\n    }\n\n    // Bail out if the visible area is already rendered and nothing changed.\n    if (!update.force &&\n        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&\n        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&\n        display.renderedView == display.view && countDirtyView(cm) == 0)\n      { return false }\n\n    if (maybeUpdateLineNumberWidth(cm)) {\n      resetView(cm);\n      update.dims = getDimensions(cm);\n    }\n\n    // Compute a suitable new viewport (from & to)\n    var end = doc.first + doc.size;\n    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);\n    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);\n    if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }\n    if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }\n    if (sawCollapsedSpans) {\n      from = visualLineNo(cm.doc, from);\n      to = visualLineEndNo(cm.doc, to);\n    }\n\n    var different = from != display.viewFrom || to != display.viewTo ||\n      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;\n    adjustView(cm, from, to);\n\n    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));\n    // Position the mover div to align with the current scroll position\n    cm.display.mover.style.top = display.viewOffset + \"px\";\n\n    var toUpdate = countDirtyView(cm);\n    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&\n        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))\n      { return false }\n\n    // For big changes, we hide the enclosing element during the\n    // update, since that speeds up the operations on most browsers.\n    var selSnapshot = selectionSnapshot(cm);\n    if (toUpdate > 4) { display.lineDiv.style.display = \"none\"; }\n    patchDisplay(cm, display.updateLineNumbers, update.dims);\n    if (toUpdate > 4) { display.lineDiv.style.display = \"\"; }\n    display.renderedView = display.view;\n    // There might have been a widget with a focused element that got\n    // hidden or updated, if so re-focus it.\n    restoreSelection(selSnapshot);\n\n    // Prevent selection and cursors from interfering with the scroll\n    // width and height.\n    removeChildren(display.cursorDiv);\n    removeChildren(display.selectionDiv);\n    display.gutters.style.height = display.sizer.style.minHeight = 0;\n\n    if (different) {\n      display.lastWrapHeight = update.wrapperHeight;\n      display.lastWrapWidth = update.wrapperWidth;\n      startWorker(cm, 400);\n    }\n\n    display.updateLineNumbers = null;\n\n    return true\n  }\n\n  function postUpdateDisplay(cm, update) {\n    var viewport = update.viewport;\n\n    for (var first = true;; first = false) {\n      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {\n        // Clip forced viewport to actual scrollable area.\n        if (viewport && viewport.top != null)\n          { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }\n        // Updated line heights might result in the drawn area not\n        // actually covering the viewport. Keep looping until it does.\n        update.visible = visibleLines(cm.display, cm.doc, viewport);\n        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)\n          { break }\n      } else if (first) {\n        update.visible = visibleLines(cm.display, cm.doc, viewport);\n      }\n      if (!updateDisplayIfNeeded(cm, update)) { break }\n      updateHeightsInViewport(cm);\n      var barMeasure = measureForScrollbars(cm);\n      updateSelection(cm);\n      updateScrollbars(cm, barMeasure);\n      setDocumentHeight(cm, barMeasure);\n      update.force = false;\n    }\n\n    update.signal(cm, \"update\", cm);\n    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {\n      update.signal(cm, \"viewportChange\", cm, cm.display.viewFrom, cm.display.viewTo);\n      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;\n    }\n  }\n\n  function updateDisplaySimple(cm, viewport) {\n    var update = new DisplayUpdate(cm, viewport);\n    if (updateDisplayIfNeeded(cm, update)) {\n      updateHeightsInViewport(cm);\n      postUpdateDisplay(cm, update);\n      var barMeasure = measureForScrollbars(cm);\n      updateSelection(cm);\n      updateScrollbars(cm, barMeasure);\n      setDocumentHeight(cm, barMeasure);\n      update.finish();\n    }\n  }\n\n  // Sync the actual display DOM structure with display.view, removing\n  // nodes for lines that are no longer in view, and creating the ones\n  // that are not there yet, and updating the ones that are out of\n  // date.\n  function patchDisplay(cm, updateNumbersFrom, dims) {\n    var display = cm.display, lineNumbers = cm.options.lineNumbers;\n    var container = display.lineDiv, cur = container.firstChild;\n\n    function rm(node) {\n      var next = node.nextSibling;\n      // Works around a throw-scroll bug in OS X Webkit\n      if (webkit && mac && cm.display.currentWheelTarget == node)\n        { node.style.display = \"none\"; }\n      else\n        { node.parentNode.removeChild(node); }\n      return next\n    }\n\n    var view = display.view, lineN = display.viewFrom;\n    // Loop over the elements in the view, syncing cur (the DOM nodes\n    // in display.lineDiv) with the view as we go.\n    for (var i = 0; i < view.length; i++) {\n      var lineView = view[i];\n      if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet\n        var node = buildLineElement(cm, lineView, lineN, dims);\n        container.insertBefore(node, cur);\n      } else { // Already drawn\n        while (cur != lineView.node) { cur = rm(cur); }\n        var updateNumber = lineNumbers && updateNumbersFrom != null &&\n          updateNumbersFrom <= lineN && lineView.lineNumber;\n        if (lineView.changes) {\n          if (indexOf(lineView.changes, \"gutter\") > -1) { updateNumber = false; }\n          updateLineForChanges(cm, lineView, lineN, dims);\n        }\n        if (updateNumber) {\n          removeChildren(lineView.lineNumber);\n          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));\n        }\n        cur = lineView.node.nextSibling;\n      }\n      lineN += lineView.size;\n    }\n    while (cur) { cur = rm(cur); }\n  }\n\n  function updateGutterSpace(display) {\n    var width = display.gutters.offsetWidth;\n    display.sizer.style.marginLeft = width + \"px\";\n    // Send an event to consumers responding to changes in gutter width.\n    signalLater(display, \"gutterChanged\", display);\n  }\n\n  function setDocumentHeight(cm, measure) {\n    cm.display.sizer.style.minHeight = measure.docHeight + \"px\";\n    cm.display.heightForcer.style.top = measure.docHeight + \"px\";\n    cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + \"px\";\n  }\n\n  // Re-align line numbers and gutter marks to compensate for\n  // horizontal scrolling.\n  function alignHorizontally(cm) {\n    var display = cm.display, view = display.view;\n    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }\n    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;\n    var gutterW = display.gutters.offsetWidth, left = comp + \"px\";\n    for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {\n      if (cm.options.fixedGutter) {\n        if (view[i].gutter)\n          { view[i].gutter.style.left = left; }\n        if (view[i].gutterBackground)\n          { view[i].gutterBackground.style.left = left; }\n      }\n      var align = view[i].alignable;\n      if (align) { for (var j = 0; j < align.length; j++)\n        { align[j].style.left = left; } }\n    } }\n    if (cm.options.fixedGutter)\n      { display.gutters.style.left = (comp + gutterW) + \"px\"; }\n  }\n\n  // Used to ensure that the line number gutter is still the right\n  // size for the current document size. Returns true when an update\n  // is needed.\n  function maybeUpdateLineNumberWidth(cm) {\n    if (!cm.options.lineNumbers) { return false }\n    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;\n    if (last.length != display.lineNumChars) {\n      var test = display.measure.appendChild(elt(\"div\", [elt(\"div\", last)],\n                                                 \"CodeMirror-linenumber CodeMirror-gutter-elt\"));\n      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;\n      display.lineGutter.style.width = \"\";\n      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;\n      display.lineNumWidth = display.lineNumInnerWidth + padding;\n      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;\n      display.lineGutter.style.width = display.lineNumWidth + \"px\";\n      updateGutterSpace(cm.display);\n      return true\n    }\n    return false\n  }\n\n  function getGutters(gutters, lineNumbers) {\n    var result = [], sawLineNumbers = false;\n    for (var i = 0; i < gutters.length; i++) {\n      var name = gutters[i], style = null;\n      if (typeof name != \"string\") { style = name.style; name = name.className; }\n      if (name == \"CodeMirror-linenumbers\") {\n        if (!lineNumbers) { continue }\n        else { sawLineNumbers = true; }\n      }\n      result.push({className: name, style: style});\n    }\n    if (lineNumbers && !sawLineNumbers) { result.push({className: \"CodeMirror-linenumbers\", style: null}); }\n    return result\n  }\n\n  // Rebuild the gutter elements, ensure the margin to the left of the\n  // code matches their width.\n  function renderGutters(display) {\n    var gutters = display.gutters, specs = display.gutterSpecs;\n    removeChildren(gutters);\n    display.lineGutter = null;\n    for (var i = 0; i < specs.length; ++i) {\n      var ref = specs[i];\n      var className = ref.className;\n      var style = ref.style;\n      var gElt = gutters.appendChild(elt(\"div\", null, \"CodeMirror-gutter \" + className));\n      if (style) { gElt.style.cssText = style; }\n      if (className == \"CodeMirror-linenumbers\") {\n        display.lineGutter = gElt;\n        gElt.style.width = (display.lineNumWidth || 1) + \"px\";\n      }\n    }\n    gutters.style.display = specs.length ? \"\" : \"none\";\n    updateGutterSpace(display);\n  }\n\n  function updateGutters(cm) {\n    renderGutters(cm.display);\n    regChange(cm);\n    alignHorizontally(cm);\n  }\n\n  // The display handles the DOM integration, both for input reading\n  // and content drawing. It holds references to DOM nodes and\n  // display-related state.\n\n  function Display(place, doc, input, options) {\n    var d = this;\n    this.input = input;\n\n    // Covers bottom-right square when both scrollbars are present.\n    d.scrollbarFiller = elt(\"div\", null, \"CodeMirror-scrollbar-filler\");\n    d.scrollbarFiller.setAttribute(\"cm-not-content\", \"true\");\n    // Covers bottom of gutter when coverGutterNextToScrollbar is on\n    // and h scrollbar is present.\n    d.gutterFiller = elt(\"div\", null, \"CodeMirror-gutter-filler\");\n    d.gutterFiller.setAttribute(\"cm-not-content\", \"true\");\n    // Will contain the actual code, positioned to cover the viewport.\n    d.lineDiv = eltP(\"div\", null, \"CodeMirror-code\");\n    // Elements are added to these to represent selection and cursors.\n    d.selectionDiv = elt(\"div\", null, null, \"position: relative; z-index: 1\");\n    d.cursorDiv = elt(\"div\", null, \"CodeMirror-cursors\");\n    // A visibility: hidden element used to find the size of things.\n    d.measure = elt(\"div\", null, \"CodeMirror-measure\");\n    // When lines outside of the viewport are measured, they are drawn in this.\n    d.lineMeasure = elt(\"div\", null, \"CodeMirror-measure\");\n    // Wraps everything that needs to exist inside the vertically-padded coordinate system\n    d.lineSpace = eltP(\"div\", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],\n                      null, \"position: relative; outline: none\");\n    var lines = eltP(\"div\", [d.lineSpace], \"CodeMirror-lines\");\n    // Moved around its parent to cover visible view.\n    d.mover = elt(\"div\", [lines], null, \"position: relative\");\n    // Set to the height of the document, allowing scrolling.\n    d.sizer = elt(\"div\", [d.mover], \"CodeMirror-sizer\");\n    d.sizerWidth = null;\n    // Behavior of elts with overflow: auto and padding is\n    // inconsistent across browsers. This is used to ensure the\n    // scrollable area is big enough.\n    d.heightForcer = elt(\"div\", null, null, \"position: absolute; height: \" + scrollerGap + \"px; width: 1px;\");\n    // Will contain the gutters, if any.\n    d.gutters = elt(\"div\", null, \"CodeMirror-gutters\");\n    d.lineGutter = null;\n    // Actual scrollable element.\n    d.scroller = elt(\"div\", [d.sizer, d.heightForcer, d.gutters], \"CodeMirror-scroll\");\n    d.scroller.setAttribute(\"tabIndex\", \"-1\");\n    // The element in which the editor lives.\n    d.wrapper = elt(\"div\", [d.scrollbarFiller, d.gutterFiller, d.scroller], \"CodeMirror\");\n    // See #6982. FIXME remove when this has been fixed for a while in Chrome\n    if (chrome && chrome_version >= 105) { d.wrapper.style.clipPath = \"inset(0px)\"; }\n\n    // This attribute is respected by automatic translation systems such as Google Translate,\n    // and may also be respected by tools used by human translators.\n    d.wrapper.setAttribute('translate', 'no');\n\n    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)\n    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }\n    if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }\n\n    if (place) {\n      if (place.appendChild) { place.appendChild(d.wrapper); }\n      else { place(d.wrapper); }\n    }\n\n    // Current rendered range (may be bigger than the view window).\n    d.viewFrom = d.viewTo = doc.first;\n    d.reportedViewFrom = d.reportedViewTo = doc.first;\n    // Information about the rendered lines.\n    d.view = [];\n    d.renderedView = null;\n    // Holds info about a single rendered line when it was rendered\n    // for measurement, while not in view.\n    d.externalMeasured = null;\n    // Empty space (in pixels) above the view\n    d.viewOffset = 0;\n    d.lastWrapHeight = d.lastWrapWidth = 0;\n    d.updateLineNumbers = null;\n\n    d.nativeBarWidth = d.barHeight = d.barWidth = 0;\n    d.scrollbarsClipped = false;\n\n    // Used to only resize the line number gutter when necessary (when\n    // the amount of lines crosses a boundary that makes its width change)\n    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;\n    // Set to true when a non-horizontal-scrolling line widget is\n    // added. As an optimization, line widget aligning is skipped when\n    // this is false.\n    d.alignWidgets = false;\n\n    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;\n\n    // Tracks the maximum line length so that the horizontal scrollbar\n    // can be kept static when scrolling.\n    d.maxLine = null;\n    d.maxLineLength = 0;\n    d.maxLineChanged = false;\n\n    // Used for measuring wheel scrolling granularity\n    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;\n\n    // True when shift is held down.\n    d.shift = false;\n\n    // Used to track whether anything happened since the context menu\n    // was opened.\n    d.selForContextMenu = null;\n\n    d.activeTouch = null;\n\n    d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);\n    renderGutters(d);\n\n    input.init(d);\n  }\n\n  // Since the delta values reported on mouse wheel events are\n  // unstandardized between browsers and even browser versions, and\n  // generally horribly unpredictable, this code starts by measuring\n  // the scroll effect that the first few mouse wheel events have,\n  // and, from that, detects the way it can convert deltas to pixel\n  // offsets afterwards.\n  //\n  // The reason we want to know the amount a wheel event will scroll\n  // is that it gives us a chance to update the display before the\n  // actual scrolling happens, reducing flickering.\n\n  var wheelSamples = 0, wheelPixelsPerUnit = null;\n  // Fill in a browser-detected starting value on browsers where we\n  // know one. These don't have to be accurate -- the result of them\n  // being wrong would just be a slight flicker on the first wheel\n  // scroll (if it is large enough).\n  if (ie) { wheelPixelsPerUnit = -.53; }\n  else if (gecko) { wheelPixelsPerUnit = 15; }\n  else if (chrome) { wheelPixelsPerUnit = -.7; }\n  else if (safari) { wheelPixelsPerUnit = -1/3; }\n\n  function wheelEventDelta(e) {\n    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;\n    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }\n    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }\n    else if (dy == null) { dy = e.wheelDelta; }\n    return {x: dx, y: dy}\n  }\n  function wheelEventPixels(e) {\n    var delta = wheelEventDelta(e);\n    delta.x *= wheelPixelsPerUnit;\n    delta.y *= wheelPixelsPerUnit;\n    return delta\n  }\n\n  function onScrollWheel(cm, e) {\n    // On Chrome 102, viewport updates somehow stop wheel-based\n    // scrolling. Turning off pointer events during the scroll seems\n    // to avoid the issue.\n    if (chrome && chrome_version == 102) {\n      if (cm.display.chromeScrollHack == null) { cm.display.sizer.style.pointerEvents = \"none\"; }\n      else { clearTimeout(cm.display.chromeScrollHack); }\n      cm.display.chromeScrollHack = setTimeout(function () {\n        cm.display.chromeScrollHack = null;\n        cm.display.sizer.style.pointerEvents = \"\";\n      }, 100);\n    }\n    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;\n    var pixelsPerUnit = wheelPixelsPerUnit;\n    if (e.deltaMode === 0) {\n      dx = e.deltaX;\n      dy = e.deltaY;\n      pixelsPerUnit = 1;\n    }\n\n    var display = cm.display, scroll = display.scroller;\n    // Quit if there's nothing to scroll here\n    var canScrollX = scroll.scrollWidth > scroll.clientWidth;\n    var canScrollY = scroll.scrollHeight > scroll.clientHeight;\n    if (!(dx && canScrollX || dy && canScrollY)) { return }\n\n    // Webkit browsers on OS X abort momentum scrolls when the target\n    // of the scroll event is removed from the scrollable element.\n    // This hack (see related code in patchDisplay) makes sure the\n    // element is kept around.\n    if (dy && mac && webkit) {\n      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {\n        for (var i = 0; i < view.length; i++) {\n          if (view[i].node == cur) {\n            cm.display.currentWheelTarget = cur;\n            break outer\n          }\n        }\n      }\n    }\n\n    // On some browsers, horizontal scrolling will cause redraws to\n    // happen before the gutter has been realigned, causing it to\n    // wriggle around in a most unseemly way. When we have an\n    // estimated pixels/delta value, we just handle horizontal\n    // scrolling entirely here. It'll be slightly off from native, but\n    // better than glitching out.\n    if (dx && !gecko && !presto && pixelsPerUnit != null) {\n      if (dy && canScrollY)\n        { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * pixelsPerUnit)); }\n      setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * pixelsPerUnit));\n      // Only prevent default scrolling if vertical scrolling is\n      // actually possible. Otherwise, it causes vertical scroll\n      // jitter on OSX trackpads when deltaX is small and deltaY\n      // is large (issue #3579)\n      if (!dy || (dy && canScrollY))\n        { e_preventDefault(e); }\n      display.wheelStartX = null; // Abort measurement, if in progress\n      return\n    }\n\n    // 'Project' the visible viewport to cover the area that is being\n    // scrolled into view (if we know enough to estimate it).\n    if (dy && pixelsPerUnit != null) {\n      var pixels = dy * pixelsPerUnit;\n      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;\n      if (pixels < 0) { top = Math.max(0, top + pixels - 50); }\n      else { bot = Math.min(cm.doc.height, bot + pixels + 50); }\n      updateDisplaySimple(cm, {top: top, bottom: bot});\n    }\n\n    if (wheelSamples < 20 && e.deltaMode !== 0) {\n      if (display.wheelStartX == null) {\n        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;\n        display.wheelDX = dx; display.wheelDY = dy;\n        setTimeout(function () {\n          if (display.wheelStartX == null) { return }\n          var movedX = scroll.scrollLeft - display.wheelStartX;\n          var movedY = scroll.scrollTop - display.wheelStartY;\n          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||\n            (movedX && display.wheelDX && movedX / display.wheelDX);\n          display.wheelStartX = display.wheelStartY = null;\n          if (!sample) { return }\n          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);\n          ++wheelSamples;\n        }, 200);\n      } else {\n        display.wheelDX += dx; display.wheelDY += dy;\n      }\n    }\n  }\n\n  // Selection objects are immutable. A new one is created every time\n  // the selection changes. A selection is one or more non-overlapping\n  // (and non-touching) ranges, sorted, and an integer that indicates\n  // which one is the primary selection (the one that's scrolled into\n  // view, that getCursor returns, etc).\n  var Selection = function(ranges, primIndex) {\n    this.ranges = ranges;\n    this.primIndex = primIndex;\n  };\n\n  Selection.prototype.primary = function () { return this.ranges[this.primIndex] };\n\n  Selection.prototype.equals = function (other) {\n    if (other == this) { return true }\n    if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }\n    for (var i = 0; i < this.ranges.length; i++) {\n      var here = this.ranges[i], there = other.ranges[i];\n      if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }\n    }\n    return true\n  };\n\n  Selection.prototype.deepCopy = function () {\n    var out = [];\n    for (var i = 0; i < this.ranges.length; i++)\n      { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); }\n    return new Selection(out, this.primIndex)\n  };\n\n  Selection.prototype.somethingSelected = function () {\n    for (var i = 0; i < this.ranges.length; i++)\n      { if (!this.ranges[i].empty()) { return true } }\n    return false\n  };\n\n  Selection.prototype.contains = function (pos, end) {\n    if (!end) { end = pos; }\n    for (var i = 0; i < this.ranges.length; i++) {\n      var range = this.ranges[i];\n      if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)\n        { return i }\n    }\n    return -1\n  };\n\n  var Range = function(anchor, head) {\n    this.anchor = anchor; this.head = head;\n  };\n\n  Range.prototype.from = function () { return minPos(this.anchor, this.head) };\n  Range.prototype.to = function () { return maxPos(this.anchor, this.head) };\n  Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };\n\n  // Take an unsorted, potentially overlapping set of ranges, and\n  // build a selection out of it. 'Consumes' ranges array (modifying\n  // it).\n  function normalizeSelection(cm, ranges, primIndex) {\n    var mayTouch = cm && cm.options.selectionsMayTouch;\n    var prim = ranges[primIndex];\n    ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });\n    primIndex = indexOf(ranges, prim);\n    for (var i = 1; i < ranges.length; i++) {\n      var cur = ranges[i], prev = ranges[i - 1];\n      var diff = cmp(prev.to(), cur.from());\n      if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {\n        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());\n        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;\n        if (i <= primIndex) { --primIndex; }\n        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));\n      }\n    }\n    return new Selection(ranges, primIndex)\n  }\n\n  function simpleSelection(anchor, head) {\n    return new Selection([new Range(anchor, head || anchor)], 0)\n  }\n\n  // Compute the position of the end of a change (its 'to' property\n  // refers to the pre-change end).\n  function changeEnd(change) {\n    if (!change.text) { return change.to }\n    return Pos(change.from.line + change.text.length - 1,\n               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))\n  }\n\n  // Adjust a position to refer to the post-change position of the\n  // same text, or the end of the change if the change covers it.\n  function adjustForChange(pos, change) {\n    if (cmp(pos, change.from) < 0) { return pos }\n    if (cmp(pos, change.to) <= 0) { return changeEnd(change) }\n\n    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;\n    if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }\n    return Pos(line, ch)\n  }\n\n  function computeSelAfterChange(doc, change) {\n    var out = [];\n    for (var i = 0; i < doc.sel.ranges.length; i++) {\n      var range = doc.sel.ranges[i];\n      out.push(new Range(adjustForChange(range.anchor, change),\n                         adjustForChange(range.head, change)));\n    }\n    return normalizeSelection(doc.cm, out, doc.sel.primIndex)\n  }\n\n  function offsetPos(pos, old, nw) {\n    if (pos.line == old.line)\n      { return Pos(nw.line, pos.ch - old.ch + nw.ch) }\n    else\n      { return Pos(nw.line + (pos.line - old.line), pos.ch) }\n  }\n\n  // Used by replaceSelections to allow moving the selection to the\n  // start or around the replaced test. Hint may be \"start\" or \"around\".\n  function computeReplacedSel(doc, changes, hint) {\n    var out = [];\n    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;\n    for (var i = 0; i < changes.length; i++) {\n      var change = changes[i];\n      var from = offsetPos(change.from, oldPrev, newPrev);\n      var to = offsetPos(changeEnd(change), oldPrev, newPrev);\n      oldPrev = change.to;\n      newPrev = to;\n      if (hint == \"around\") {\n        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;\n        out[i] = new Range(inv ? to : from, inv ? from : to);\n      } else {\n        out[i] = new Range(from, from);\n      }\n    }\n    return new Selection(out, doc.sel.primIndex)\n  }\n\n  // Used to get the editor into a consistent state again when options change.\n\n  function loadMode(cm) {\n    cm.doc.mode = getMode(cm.options, cm.doc.modeOption);\n    resetModeState(cm);\n  }\n\n  function resetModeState(cm) {\n    cm.doc.iter(function (line) {\n      if (line.stateAfter) { line.stateAfter = null; }\n      if (line.styles) { line.styles = null; }\n    });\n    cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;\n    startWorker(cm, 100);\n    cm.state.modeGen++;\n    if (cm.curOp) { regChange(cm); }\n  }\n\n  // DOCUMENT DATA STRUCTURE\n\n  // By default, updates that start and end at the beginning of a line\n  // are treated specially, in order to make the association of line\n  // widgets and marker elements with the text behave more intuitive.\n  function isWholeLineUpdate(doc, change) {\n    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == \"\" &&\n      (!doc.cm || doc.cm.options.wholeLineUpdateBefore)\n  }\n\n  // Perform a change on the document data structure.\n  function updateDoc(doc, change, markedSpans, estimateHeight) {\n    function spansFor(n) {return markedSpans ? markedSpans[n] : null}\n    function update(line, text, spans) {\n      updateLine(line, text, spans, estimateHeight);\n      signalLater(line, \"change\", line, change);\n    }\n    function linesFor(start, end) {\n      var result = [];\n      for (var i = start; i < end; ++i)\n        { result.push(new Line(text[i], spansFor(i), estimateHeight)); }\n      return result\n    }\n\n    var from = change.from, to = change.to, text = change.text;\n    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);\n    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;\n\n    // Adjust the line structure\n    if (change.full) {\n      doc.insert(0, linesFor(0, text.length));\n      doc.remove(text.length, doc.size - text.length);\n    } else if (isWholeLineUpdate(doc, change)) {\n      // This is a whole-line replace. Treated specially to make\n      // sure line objects move the way they are supposed to.\n      var added = linesFor(0, text.length - 1);\n      update(lastLine, lastLine.text, lastSpans);\n      if (nlines) { doc.remove(from.line, nlines); }\n      if (added.length) { doc.insert(from.line, added); }\n    } else if (firstLine == lastLine) {\n      if (text.length == 1) {\n        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);\n      } else {\n        var added$1 = linesFor(1, text.length - 1);\n        added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));\n        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));\n        doc.insert(from.line + 1, added$1);\n      }\n    } else if (text.length == 1) {\n      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));\n      doc.remove(from.line + 1, nlines);\n    } else {\n      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));\n      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);\n      var added$2 = linesFor(1, text.length - 1);\n      if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }\n      doc.insert(from.line + 1, added$2);\n    }\n\n    signalLater(doc, \"change\", doc, change);\n  }\n\n  // Call f for all linked documents.\n  function linkedDocs(doc, f, sharedHistOnly) {\n    function propagate(doc, skip, sharedHist) {\n      if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {\n        var rel = doc.linked[i];\n        if (rel.doc == skip) { continue }\n        var shared = sharedHist && rel.sharedHist;\n        if (sharedHistOnly && !shared) { continue }\n        f(rel.doc, shared);\n        propagate(rel.doc, doc, shared);\n      } }\n    }\n    propagate(doc, null, true);\n  }\n\n  // Attach a document to an editor.\n  function attachDoc(cm, doc) {\n    if (doc.cm) { throw new Error(\"This document is already in use.\") }\n    cm.doc = doc;\n    doc.cm = cm;\n    estimateLineHeights(cm);\n    loadMode(cm);\n    setDirectionClass(cm);\n    cm.options.direction = doc.direction;\n    if (!cm.options.lineWrapping) { findMaxLine(cm); }\n    cm.options.mode = doc.modeOption;\n    regChange(cm);\n  }\n\n  function setDirectionClass(cm) {\n  (cm.doc.direction == \"rtl\" ? addClass : rmClass)(cm.display.lineDiv, \"CodeMirror-rtl\");\n  }\n\n  function directionChanged(cm) {\n    runInOp(cm, function () {\n      setDirectionClass(cm);\n      regChange(cm);\n    });\n  }\n\n  function History(prev) {\n    // Arrays of change events and selections. Doing something adds an\n    // event to done and clears undo. Undoing moves events from done\n    // to undone, redoing moves them in the other direction.\n    this.done = []; this.undone = [];\n    this.undoDepth = prev ? prev.undoDepth : Infinity;\n    // Used to track when changes can be merged into a single undo\n    // event\n    this.lastModTime = this.lastSelTime = 0;\n    this.lastOp = this.lastSelOp = null;\n    this.lastOrigin = this.lastSelOrigin = null;\n    // Used by the isClean() method\n    this.generation = this.maxGeneration = prev ? prev.maxGeneration : 1;\n  }\n\n  // Create a history change event from an updateDoc-style change\n  // object.\n  function historyChangeFromChange(doc, change) {\n    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};\n    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);\n    linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);\n    return histChange\n  }\n\n  // Pop all selection events off the end of a history array. Stop at\n  // a change event.\n  function clearSelectionEvents(array) {\n    while (array.length) {\n      var last = lst(array);\n      if (last.ranges) { array.pop(); }\n      else { break }\n    }\n  }\n\n  // Find the top change event in the history. Pop off selection\n  // events that are in the way.\n  function lastChangeEvent(hist, force) {\n    if (force) {\n      clearSelectionEvents(hist.done);\n      return lst(hist.done)\n    } else if (hist.done.length && !lst(hist.done).ranges) {\n      return lst(hist.done)\n    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {\n      hist.done.pop();\n      return lst(hist.done)\n    }\n  }\n\n  // Register a change in the history. Merges changes that are within\n  // a single operation, or are close together with an origin that\n  // allows merging (starting with \"+\") into a single event.\n  function addChangeToHistory(doc, change, selAfter, opId) {\n    var hist = doc.history;\n    hist.undone.length = 0;\n    var time = +new Date, cur;\n    var last;\n\n    if ((hist.lastOp == opId ||\n         hist.lastOrigin == change.origin && change.origin &&\n         ((change.origin.charAt(0) == \"+\" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||\n          change.origin.charAt(0) == \"*\")) &&\n        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {\n      // Merge this change into the last event\n      last = lst(cur.changes);\n      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {\n        // Optimized case for simple insertion -- don't want to add\n        // new changesets for every character typed\n        last.to = changeEnd(change);\n      } else {\n        // Add new sub-event\n        cur.changes.push(historyChangeFromChange(doc, change));\n      }\n    } else {\n      // Can not be merged, start a new event.\n      var before = lst(hist.done);\n      if (!before || !before.ranges)\n        { pushSelectionToHistory(doc.sel, hist.done); }\n      cur = {changes: [historyChangeFromChange(doc, change)],\n             generation: hist.generation};\n      hist.done.push(cur);\n      while (hist.done.length > hist.undoDepth) {\n        hist.done.shift();\n        if (!hist.done[0].ranges) { hist.done.shift(); }\n      }\n    }\n    hist.done.push(selAfter);\n    hist.generation = ++hist.maxGeneration;\n    hist.lastModTime = hist.lastSelTime = time;\n    hist.lastOp = hist.lastSelOp = opId;\n    hist.lastOrigin = hist.lastSelOrigin = change.origin;\n\n    if (!last) { signal(doc, \"historyAdded\"); }\n  }\n\n  function selectionEventCanBeMerged(doc, origin, prev, sel) {\n    var ch = origin.charAt(0);\n    return ch == \"*\" ||\n      ch == \"+\" &&\n      prev.ranges.length == sel.ranges.length &&\n      prev.somethingSelected() == sel.somethingSelected() &&\n      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)\n  }\n\n  // Called whenever the selection changes, sets the new selection as\n  // the pending selection in the history, and pushes the old pending\n  // selection into the 'done' array when it was significantly\n  // different (in number of selected ranges, emptiness, or time).\n  function addSelectionToHistory(doc, sel, opId, options) {\n    var hist = doc.history, origin = options && options.origin;\n\n    // A new event is started when the previous origin does not match\n    // the current, or the origins don't allow matching. Origins\n    // starting with * are always merged, those starting with + are\n    // merged when similar and close together in time.\n    if (opId == hist.lastSelOp ||\n        (origin && hist.lastSelOrigin == origin &&\n         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||\n          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))\n      { hist.done[hist.done.length - 1] = sel; }\n    else\n      { pushSelectionToHistory(sel, hist.done); }\n\n    hist.lastSelTime = +new Date;\n    hist.lastSelOrigin = origin;\n    hist.lastSelOp = opId;\n    if (options && options.clearRedo !== false)\n      { clearSelectionEvents(hist.undone); }\n  }\n\n  function pushSelectionToHistory(sel, dest) {\n    var top = lst(dest);\n    if (!(top && top.ranges && top.equals(sel)))\n      { dest.push(sel); }\n  }\n\n  // Used to store marked span information in the history.\n  function attachLocalSpans(doc, change, from, to) {\n    var existing = change[\"spans_\" + doc.id], n = 0;\n    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {\n      if (line.markedSpans)\n        { (existing || (existing = change[\"spans_\" + doc.id] = {}))[n] = line.markedSpans; }\n      ++n;\n    });\n  }\n\n  // When un/re-doing restores text containing marked spans, those\n  // that have been explicitly cleared should not be restored.\n  function removeClearedSpans(spans) {\n    if (!spans) { return null }\n    var out;\n    for (var i = 0; i < spans.length; ++i) {\n      if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }\n      else if (out) { out.push(spans[i]); }\n    }\n    return !out ? spans : out.length ? out : null\n  }\n\n  // Retrieve and filter the old marked spans stored in a change event.\n  function getOldSpans(doc, change) {\n    var found = change[\"spans_\" + doc.id];\n    if (!found) { return null }\n    var nw = [];\n    for (var i = 0; i < change.text.length; ++i)\n      { nw.push(removeClearedSpans(found[i])); }\n    return nw\n  }\n\n  // Used for un/re-doing changes from the history. Combines the\n  // result of computing the existing spans with the set of spans that\n  // existed in the history (so that deleting around a span and then\n  // undoing brings back the span).\n  function mergeOldSpans(doc, change) {\n    var old = getOldSpans(doc, change);\n    var stretched = stretchSpansOverChange(doc, change);\n    if (!old) { return stretched }\n    if (!stretched) { return old }\n\n    for (var i = 0; i < old.length; ++i) {\n      var oldCur = old[i], stretchCur = stretched[i];\n      if (oldCur && stretchCur) {\n        spans: for (var j = 0; j < stretchCur.length; ++j) {\n          var span = stretchCur[j];\n          for (var k = 0; k < oldCur.length; ++k)\n            { if (oldCur[k].marker == span.marker) { continue spans } }\n          oldCur.push(span);\n        }\n      } else if (stretchCur) {\n        old[i] = stretchCur;\n      }\n    }\n    return old\n  }\n\n  // Used both to provide a JSON-safe object in .getHistory, and, when\n  // detaching a document, to split the history in two\n  function copyHistoryArray(events, newGroup, instantiateSel) {\n    var copy = [];\n    for (var i = 0; i < events.length; ++i) {\n      var event = events[i];\n      if (event.ranges) {\n        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);\n        continue\n      }\n      var changes = event.changes, newChanges = [];\n      copy.push({changes: newChanges});\n      for (var j = 0; j < changes.length; ++j) {\n        var change = changes[j], m = (void 0);\n        newChanges.push({from: change.from, to: change.to, text: change.text});\n        if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\\d+)$/)) {\n          if (indexOf(newGroup, Number(m[1])) > -1) {\n            lst(newChanges)[prop] = change[prop];\n            delete change[prop];\n          }\n        } } }\n      }\n    }\n    return copy\n  }\n\n  // The 'scroll' parameter given to many of these indicated whether\n  // the new cursor position should be scrolled into view after\n  // modifying the selection.\n\n  // If shift is held or the extend flag is set, extends a range to\n  // include a given position (and optionally a second position).\n  // Otherwise, simply returns the range between the given positions.\n  // Used for cursor motion and such.\n  function extendRange(range, head, other, extend) {\n    if (extend) {\n      var anchor = range.anchor;\n      if (other) {\n        var posBefore = cmp(head, anchor) < 0;\n        if (posBefore != (cmp(other, anchor) < 0)) {\n          anchor = head;\n          head = other;\n        } else if (posBefore != (cmp(head, other) < 0)) {\n          head = other;\n        }\n      }\n      return new Range(anchor, head)\n    } else {\n      return new Range(other || head, head)\n    }\n  }\n\n  // Extend the primary selection range, discard the rest.\n  function extendSelection(doc, head, other, options, extend) {\n    if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }\n    setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);\n  }\n\n  // Extend all selections (pos is an array of selections with length\n  // equal the number of selections)\n  function extendSelections(doc, heads, options) {\n    var out = [];\n    var extend = doc.cm && (doc.cm.display.shift || doc.extend);\n    for (var i = 0; i < doc.sel.ranges.length; i++)\n      { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }\n    var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);\n    setSelection(doc, newSel, options);\n  }\n\n  // Updates a single range in the selection.\n  function replaceOneSelection(doc, i, range, options) {\n    var ranges = doc.sel.ranges.slice(0);\n    ranges[i] = range;\n    setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);\n  }\n\n  // Reset the selection to a single range.\n  function setSimpleSelection(doc, anchor, head, options) {\n    setSelection(doc, simpleSelection(anchor, head), options);\n  }\n\n  // Give beforeSelectionChange handlers a change to influence a\n  // selection update.\n  function filterSelectionChange(doc, sel, options) {\n    var obj = {\n      ranges: sel.ranges,\n      update: function(ranges) {\n        this.ranges = [];\n        for (var i = 0; i < ranges.length; i++)\n          { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),\n                                     clipPos(doc, ranges[i].head)); }\n      },\n      origin: options && options.origin\n    };\n    signal(doc, \"beforeSelectionChange\", doc, obj);\n    if (doc.cm) { signal(doc.cm, \"beforeSelectionChange\", doc.cm, obj); }\n    if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }\n    else { return sel }\n  }\n\n  function setSelectionReplaceHistory(doc, sel, options) {\n    var done = doc.history.done, last = lst(done);\n    if (last && last.ranges) {\n      done[done.length - 1] = sel;\n      setSelectionNoUndo(doc, sel, options);\n    } else {\n      setSelection(doc, sel, options);\n    }\n  }\n\n  // Set a new selection.\n  function setSelection(doc, sel, options) {\n    setSelectionNoUndo(doc, sel, options);\n    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);\n  }\n\n  function setSelectionNoUndo(doc, sel, options) {\n    if (hasHandler(doc, \"beforeSelectionChange\") || doc.cm && hasHandler(doc.cm, \"beforeSelectionChange\"))\n      { sel = filterSelectionChange(doc, sel, options); }\n\n    var bias = options && options.bias ||\n      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);\n    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));\n\n    if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption(\"readOnly\") != \"nocursor\")\n      { ensureCursorVisible(doc.cm); }\n  }\n\n  function setSelectionInner(doc, sel) {\n    if (sel.equals(doc.sel)) { return }\n\n    doc.sel = sel;\n\n    if (doc.cm) {\n      doc.cm.curOp.updateInput = 1;\n      doc.cm.curOp.selectionChanged = true;\n      signalCursorActivity(doc.cm);\n    }\n    signalLater(doc, \"cursorActivity\", doc);\n  }\n\n  // Verify that the selection does not partially select any atomic\n  // marked ranges.\n  function reCheckSelection(doc) {\n    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));\n  }\n\n  // Return a selection that does not partially select any atomic\n  // ranges.\n  function skipAtomicInSelection(doc, sel, bias, mayClear) {\n    var out;\n    for (var i = 0; i < sel.ranges.length; i++) {\n      var range = sel.ranges[i];\n      var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];\n      var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);\n      var newHead = range.head == range.anchor ? newAnchor : skipAtomic(doc, range.head, old && old.head, bias, mayClear);\n      if (out || newAnchor != range.anchor || newHead != range.head) {\n        if (!out) { out = sel.ranges.slice(0, i); }\n        out[i] = new Range(newAnchor, newHead);\n      }\n    }\n    return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel\n  }\n\n  function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {\n    var line = getLine(doc, pos.line);\n    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {\n      var sp = line.markedSpans[i], m = sp.marker;\n\n      // Determine if we should prevent the cursor being placed to the left/right of an atomic marker\n      // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it\n      // is with selectLeft/Right\n      var preventCursorLeft = (\"selectLeft\" in m) ? !m.selectLeft : m.inclusiveLeft;\n      var preventCursorRight = (\"selectRight\" in m) ? !m.selectRight : m.inclusiveRight;\n\n      if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&\n          (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {\n        if (mayClear) {\n          signal(m, \"beforeCursorEnter\");\n          if (m.explicitlyCleared) {\n            if (!line.markedSpans) { break }\n            else {--i; continue}\n          }\n        }\n        if (!m.atomic) { continue }\n\n        if (oldPos) {\n          var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);\n          if (dir < 0 ? preventCursorRight : preventCursorLeft)\n            { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }\n          if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))\n            { return skipAtomicInner(doc, near, pos, dir, mayClear) }\n        }\n\n        var far = m.find(dir < 0 ? -1 : 1);\n        if (dir < 0 ? preventCursorLeft : preventCursorRight)\n          { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }\n        return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null\n      }\n    } }\n    return pos\n  }\n\n  // Ensure a given position is not inside an atomic range.\n  function skipAtomic(doc, pos, oldPos, bias, mayClear) {\n    var dir = bias || 1;\n    var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||\n        (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||\n        skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||\n        (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));\n    if (!found) {\n      doc.cantEdit = true;\n      return Pos(doc.first, 0)\n    }\n    return found\n  }\n\n  function movePos(doc, pos, dir, line) {\n    if (dir < 0 && pos.ch == 0) {\n      if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }\n      else { return null }\n    } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {\n      if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }\n      else { return null }\n    } else {\n      return new Pos(pos.line, pos.ch + dir)\n    }\n  }\n\n  function selectAll(cm) {\n    cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);\n  }\n\n  // UPDATING\n\n  // Allow \"beforeChange\" event handlers to influence a change\n  function filterChange(doc, change, update) {\n    var obj = {\n      canceled: false,\n      from: change.from,\n      to: change.to,\n      text: change.text,\n      origin: change.origin,\n      cancel: function () { return obj.canceled = true; }\n    };\n    if (update) { obj.update = function (from, to, text, origin) {\n      if (from) { obj.from = clipPos(doc, from); }\n      if (to) { obj.to = clipPos(doc, to); }\n      if (text) { obj.text = text; }\n      if (origin !== undefined) { obj.origin = origin; }\n    }; }\n    signal(doc, \"beforeChange\", doc, obj);\n    if (doc.cm) { signal(doc.cm, \"beforeChange\", doc.cm, obj); }\n\n    if (obj.canceled) {\n      if (doc.cm) { doc.cm.curOp.updateInput = 2; }\n      return null\n    }\n    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}\n  }\n\n  // Apply a change to a document, and add it to the document's\n  // history, and propagating it to all linked documents.\n  function makeChange(doc, change, ignoreReadOnly) {\n    if (doc.cm) {\n      if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }\n      if (doc.cm.state.suppressEdits) { return }\n    }\n\n    if (hasHandler(doc, \"beforeChange\") || doc.cm && hasHandler(doc.cm, \"beforeChange\")) {\n      change = filterChange(doc, change, true);\n      if (!change) { return }\n    }\n\n    // Possibly split or suppress the update based on the presence\n    // of read-only spans in its range.\n    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);\n    if (split) {\n      for (var i = split.length - 1; i >= 0; --i)\n        { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [\"\"] : change.text, origin: change.origin}); }\n    } else {\n      makeChangeInner(doc, change);\n    }\n  }\n\n  function makeChangeInner(doc, change) {\n    if (change.text.length == 1 && change.text[0] == \"\" && cmp(change.from, change.to) == 0) { return }\n    var selAfter = computeSelAfterChange(doc, change);\n    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);\n\n    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));\n    var rebased = [];\n\n    linkedDocs(doc, function (doc, sharedHist) {\n      if (!sharedHist && indexOf(rebased, doc.history) == -1) {\n        rebaseHist(doc.history, change);\n        rebased.push(doc.history);\n      }\n      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));\n    });\n  }\n\n  // Revert a change stored in a document's history.\n  function makeChangeFromHistory(doc, type, allowSelectionOnly) {\n    var suppress = doc.cm && doc.cm.state.suppressEdits;\n    if (suppress && !allowSelectionOnly) { return }\n\n    var hist = doc.history, event, selAfter = doc.sel;\n    var source = type == \"undo\" ? hist.done : hist.undone, dest = type == \"undo\" ? hist.undone : hist.done;\n\n    // Verify that there is a useable event (so that ctrl-z won't\n    // needlessly clear selection events)\n    var i = 0;\n    for (; i < source.length; i++) {\n      event = source[i];\n      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)\n        { break }\n    }\n    if (i == source.length) { return }\n    hist.lastOrigin = hist.lastSelOrigin = null;\n\n    for (;;) {\n      event = source.pop();\n      if (event.ranges) {\n        pushSelectionToHistory(event, dest);\n        if (allowSelectionOnly && !event.equals(doc.sel)) {\n          setSelection(doc, event, {clearRedo: false});\n          return\n        }\n        selAfter = event;\n      } else if (suppress) {\n        source.push(event);\n        return\n      } else { break }\n    }\n\n    // Build up a reverse change object to add to the opposite history\n    // stack (redo when undoing, and vice versa).\n    var antiChanges = [];\n    pushSelectionToHistory(selAfter, dest);\n    dest.push({changes: antiChanges, generation: hist.generation});\n    hist.generation = event.generation || ++hist.maxGeneration;\n\n    var filter = hasHandler(doc, \"beforeChange\") || doc.cm && hasHandler(doc.cm, \"beforeChange\");\n\n    var loop = function ( i ) {\n      var change = event.changes[i];\n      change.origin = type;\n      if (filter && !filterChange(doc, change, false)) {\n        source.length = 0;\n        return {}\n      }\n\n      antiChanges.push(historyChangeFromChange(doc, change));\n\n      var after = i ? computeSelAfterChange(doc, change) : lst(source);\n      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));\n      if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }\n      var rebased = [];\n\n      // Propagate to the linked documents\n      linkedDocs(doc, function (doc, sharedHist) {\n        if (!sharedHist && indexOf(rebased, doc.history) == -1) {\n          rebaseHist(doc.history, change);\n          rebased.push(doc.history);\n        }\n        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));\n      });\n    };\n\n    for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {\n      var returned = loop( i$1 );\n\n      if ( returned ) return returned.v;\n    }\n  }\n\n  // Sub-views need their line numbers shifted when text is added\n  // above or below them in the parent document.\n  function shiftDoc(doc, distance) {\n    if (distance == 0) { return }\n    doc.first += distance;\n    doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(\n      Pos(range.anchor.line + distance, range.anchor.ch),\n      Pos(range.head.line + distance, range.head.ch)\n    ); }), doc.sel.primIndex);\n    if (doc.cm) {\n      regChange(doc.cm, doc.first, doc.first - distance, distance);\n      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)\n        { regLineChange(doc.cm, l, \"gutter\"); }\n    }\n  }\n\n  // More lower-level change function, handling only a single document\n  // (not linked ones).\n  function makeChangeSingleDoc(doc, change, selAfter, spans) {\n    if (doc.cm && !doc.cm.curOp)\n      { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }\n\n    if (change.to.line < doc.first) {\n      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));\n      return\n    }\n    if (change.from.line > doc.lastLine()) { return }\n\n    // Clip the change to the size of this doc\n    if (change.from.line < doc.first) {\n      var shift = change.text.length - 1 - (doc.first - change.from.line);\n      shiftDoc(doc, shift);\n      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),\n                text: [lst(change.text)], origin: change.origin};\n    }\n    var last = doc.lastLine();\n    if (change.to.line > last) {\n      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),\n                text: [change.text[0]], origin: change.origin};\n    }\n\n    change.removed = getBetween(doc, change.from, change.to);\n\n    if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }\n    if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }\n    else { updateDoc(doc, change, spans); }\n    setSelectionNoUndo(doc, selAfter, sel_dontScroll);\n\n    if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))\n      { doc.cantEdit = false; }\n  }\n\n  // Handle the interaction of a change to a document with the editor\n  // that this document is part of.\n  function makeChangeSingleDocInEditor(cm, change, spans) {\n    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;\n\n    var recomputeMaxLength = false, checkWidthStart = from.line;\n    if (!cm.options.lineWrapping) {\n      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));\n      doc.iter(checkWidthStart, to.line + 1, function (line) {\n        if (line == display.maxLine) {\n          recomputeMaxLength = true;\n          return true\n        }\n      });\n    }\n\n    if (doc.sel.contains(change.from, change.to) > -1)\n      { signalCursorActivity(cm); }\n\n    updateDoc(doc, change, spans, estimateHeight(cm));\n\n    if (!cm.options.lineWrapping) {\n      doc.iter(checkWidthStart, from.line + change.text.length, function (line) {\n        var len = lineLength(line);\n        if (len > display.maxLineLength) {\n          display.maxLine = line;\n          display.maxLineLength = len;\n          display.maxLineChanged = true;\n          recomputeMaxLength = false;\n        }\n      });\n      if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }\n    }\n\n    retreatFrontier(doc, from.line);\n    startWorker(cm, 400);\n\n    var lendiff = change.text.length - (to.line - from.line) - 1;\n    // Remember that these lines changed, for updating the display\n    if (change.full)\n      { regChange(cm); }\n    else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))\n      { regLineChange(cm, from.line, \"text\"); }\n    else\n      { regChange(cm, from.line, to.line + 1, lendiff); }\n\n    var changesHandler = hasHandler(cm, \"changes\"), changeHandler = hasHandler(cm, \"change\");\n    if (changeHandler || changesHandler) {\n      var obj = {\n        from: from, to: to,\n        text: change.text,\n        removed: change.removed,\n        origin: change.origin\n      };\n      if (changeHandler) { signalLater(cm, \"change\", cm, obj); }\n      if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }\n    }\n    cm.display.selForContextMenu = null;\n  }\n\n  function replaceRange(doc, code, from, to, origin) {\n    var assign;\n\n    if (!to) { to = from; }\n    if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }\n    if (typeof code == \"string\") { code = doc.splitLines(code); }\n    makeChange(doc, {from: from, to: to, text: code, origin: origin});\n  }\n\n  // Rebasing/resetting history to deal with externally-sourced changes\n\n  function rebaseHistSelSingle(pos, from, to, diff) {\n    if (to < pos.line) {\n      pos.line += diff;\n    } else if (from < pos.line) {\n      pos.line = from;\n      pos.ch = 0;\n    }\n  }\n\n  // Tries to rebase an array of history events given a change in the\n  // document. If the change touches the same lines as the event, the\n  // event, and everything 'behind' it, is discarded. If the change is\n  // before the event, the event's positions are updated. Uses a\n  // copy-on-write scheme for the positions, to avoid having to\n  // reallocate them all on every rebase, but also avoid problems with\n  // shared position objects being unsafely updated.\n  function rebaseHistArray(array, from, to, diff) {\n    for (var i = 0; i < array.length; ++i) {\n      var sub = array[i], ok = true;\n      if (sub.ranges) {\n        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }\n        for (var j = 0; j < sub.ranges.length; j++) {\n          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);\n          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);\n        }\n        continue\n      }\n      for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {\n        var cur = sub.changes[j$1];\n        if (to < cur.from.line) {\n          cur.from = Pos(cur.from.line + diff, cur.from.ch);\n          cur.to = Pos(cur.to.line + diff, cur.to.ch);\n        } else if (from <= cur.to.line) {\n          ok = false;\n          break\n        }\n      }\n      if (!ok) {\n        array.splice(0, i + 1);\n        i = 0;\n      }\n    }\n  }\n\n  function rebaseHist(hist, change) {\n    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;\n    rebaseHistArray(hist.done, from, to, diff);\n    rebaseHistArray(hist.undone, from, to, diff);\n  }\n\n  // Utility for applying a change to a line by handle or number,\n  // returning the number and optionally registering the line as\n  // changed.\n  function changeLine(doc, handle, changeType, op) {\n    var no = handle, line = handle;\n    if (typeof handle == \"number\") { line = getLine(doc, clipLine(doc, handle)); }\n    else { no = lineNo(handle); }\n    if (no == null) { return null }\n    if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }\n    return line\n  }\n\n  // The document is represented as a BTree consisting of leaves, with\n  // chunk of lines in them, and branches, with up to ten leaves or\n  // other branch nodes below them. The top node is always a branch\n  // node, and is the document object itself (meaning it has\n  // additional methods and properties).\n  //\n  // All nodes have parent links. The tree is used both to go from\n  // line numbers to line objects, and to go from objects to numbers.\n  // It also indexes by height, and is used to convert between height\n  // and line object, and to find the total height of the document.\n  //\n  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html\n\n  function LeafChunk(lines) {\n    this.lines = lines;\n    this.parent = null;\n    var height = 0;\n    for (var i = 0; i < lines.length; ++i) {\n      lines[i].parent = this;\n      height += lines[i].height;\n    }\n    this.height = height;\n  }\n\n  LeafChunk.prototype = {\n    chunkSize: function() { return this.lines.length },\n\n    // Remove the n lines at offset 'at'.\n    removeInner: function(at, n) {\n      for (var i = at, e = at + n; i < e; ++i) {\n        var line = this.lines[i];\n        this.height -= line.height;\n        cleanUpLine(line);\n        signalLater(line, \"delete\");\n      }\n      this.lines.splice(at, n);\n    },\n\n    // Helper used to collapse a small branch into a single leaf.\n    collapse: function(lines) {\n      lines.push.apply(lines, this.lines);\n    },\n\n    // Insert the given array of lines at offset 'at', count them as\n    // having the given height.\n    insertInner: function(at, lines, height) {\n      this.height += height;\n      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));\n      for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; }\n    },\n\n    // Used to iterate over a part of the tree.\n    iterN: function(at, n, op) {\n      for (var e = at + n; at < e; ++at)\n        { if (op(this.lines[at])) { return true } }\n    }\n  };\n\n  function BranchChunk(children) {\n    this.children = children;\n    var size = 0, height = 0;\n    for (var i = 0; i < children.length; ++i) {\n      var ch = children[i];\n      size += ch.chunkSize(); height += ch.height;\n      ch.parent = this;\n    }\n    this.size = size;\n    this.height = height;\n    this.parent = null;\n  }\n\n  BranchChunk.prototype = {\n    chunkSize: function() { return this.size },\n\n    removeInner: function(at, n) {\n      this.size -= n;\n      for (var i = 0; i < this.children.length; ++i) {\n        var child = this.children[i], sz = child.chunkSize();\n        if (at < sz) {\n          var rm = Math.min(n, sz - at), oldHeight = child.height;\n          child.removeInner(at, rm);\n          this.height -= oldHeight - child.height;\n          if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }\n          if ((n -= rm) == 0) { break }\n          at = 0;\n        } else { at -= sz; }\n      }\n      // If the result is smaller than 25 lines, ensure that it is a\n      // single leaf node.\n      if (this.size - n < 25 &&\n          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {\n        var lines = [];\n        this.collapse(lines);\n        this.children = [new LeafChunk(lines)];\n        this.children[0].parent = this;\n      }\n    },\n\n    collapse: function(lines) {\n      for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); }\n    },\n\n    insertInner: function(at, lines, height) {\n      this.size += lines.length;\n      this.height += height;\n      for (var i = 0; i < this.children.length; ++i) {\n        var child = this.children[i], sz = child.chunkSize();\n        if (at <= sz) {\n          child.insertInner(at, lines, height);\n          if (child.lines && child.lines.length > 50) {\n            // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.\n            // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.\n            var remaining = child.lines.length % 25 + 25;\n            for (var pos = remaining; pos < child.lines.length;) {\n              var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));\n              child.height -= leaf.height;\n              this.children.splice(++i, 0, leaf);\n              leaf.parent = this;\n            }\n            child.lines = child.lines.slice(0, remaining);\n            this.maybeSpill();\n          }\n          break\n        }\n        at -= sz;\n      }\n    },\n\n    // When a node has grown, check whether it should be split.\n    maybeSpill: function() {\n      if (this.children.length <= 10) { return }\n      var me = this;\n      do {\n        var spilled = me.children.splice(me.children.length - 5, 5);\n        var sibling = new BranchChunk(spilled);\n        if (!me.parent) { // Become the parent node\n          var copy = new BranchChunk(me.children);\n          copy.parent = me;\n          me.children = [copy, sibling];\n          me = copy;\n       } else {\n          me.size -= sibling.size;\n          me.height -= sibling.height;\n          var myIndex = indexOf(me.parent.children, me);\n          me.parent.children.splice(myIndex + 1, 0, sibling);\n        }\n        sibling.parent = me.parent;\n      } while (me.children.length > 10)\n      me.parent.maybeSpill();\n    },\n\n    iterN: function(at, n, op) {\n      for (var i = 0; i < this.children.length; ++i) {\n        var child = this.children[i], sz = child.chunkSize();\n        if (at < sz) {\n          var used = Math.min(n, sz - at);\n          if (child.iterN(at, used, op)) { return true }\n          if ((n -= used) == 0) { break }\n          at = 0;\n        } else { at -= sz; }\n      }\n    }\n  };\n\n  // Line widgets are block elements displayed above or below a line.\n\n  var LineWidget = function(doc, node, options) {\n    if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))\n      { this[opt] = options[opt]; } } }\n    this.doc = doc;\n    this.node = node;\n  };\n\n  LineWidget.prototype.clear = function () {\n    var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);\n    if (no == null || !ws) { return }\n    for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } }\n    if (!ws.length) { line.widgets = null; }\n    var height = widgetHeight(this);\n    updateLineHeight(line, Math.max(0, line.height - height));\n    if (cm) {\n      runInOp(cm, function () {\n        adjustScrollWhenAboveVisible(cm, line, -height);\n        regLineChange(cm, no, \"widget\");\n      });\n      signalLater(cm, \"lineWidgetCleared\", cm, this, no);\n    }\n  };\n\n  LineWidget.prototype.changed = function () {\n      var this$1 = this;\n\n    var oldH = this.height, cm = this.doc.cm, line = this.line;\n    this.height = null;\n    var diff = widgetHeight(this) - oldH;\n    if (!diff) { return }\n    if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }\n    if (cm) {\n      runInOp(cm, function () {\n        cm.curOp.forceUpdate = true;\n        adjustScrollWhenAboveVisible(cm, line, diff);\n        signalLater(cm, \"lineWidgetChanged\", cm, this$1, lineNo(line));\n      });\n    }\n  };\n  eventMixin(LineWidget);\n\n  function adjustScrollWhenAboveVisible(cm, line, diff) {\n    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))\n      { addToScrollTop(cm, diff); }\n  }\n\n  function addLineWidget(doc, handle, node, options) {\n    var widget = new LineWidget(doc, node, options);\n    var cm = doc.cm;\n    if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }\n    changeLine(doc, handle, \"widget\", function (line) {\n      var widgets = line.widgets || (line.widgets = []);\n      if (widget.insertAt == null) { widgets.push(widget); }\n      else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); }\n      widget.line = line;\n      if (cm && !lineIsHidden(doc, line)) {\n        var aboveVisible = heightAtLine(line) < doc.scrollTop;\n        updateLineHeight(line, line.height + widgetHeight(widget));\n        if (aboveVisible) { addToScrollTop(cm, widget.height); }\n        cm.curOp.forceUpdate = true;\n      }\n      return true\n    });\n    if (cm) { signalLater(cm, \"lineWidgetAdded\", cm, widget, typeof handle == \"number\" ? handle : lineNo(handle)); }\n    return widget\n  }\n\n  // TEXTMARKERS\n\n  // Created with markText and setBookmark methods. A TextMarker is a\n  // handle that can be used to clear or find a marked position in the\n  // document. Line objects hold arrays (markedSpans) containing\n  // {from, to, marker} object pointing to such marker objects, and\n  // indicating that such a marker is present on that line. Multiple\n  // lines may point to the same marker when it spans across lines.\n  // The spans will have null for their from/to properties when the\n  // marker continues beyond the start/end of the line. Markers have\n  // links back to the lines they currently touch.\n\n  // Collapsed markers have unique ids, in order to be able to order\n  // them, which is needed for uniquely determining an outer marker\n  // when they overlap (they may nest, but not partially overlap).\n  var nextMarkerId = 0;\n\n  var TextMarker = function(doc, type) {\n    this.lines = [];\n    this.type = type;\n    this.doc = doc;\n    this.id = ++nextMarkerId;\n  };\n\n  // Clear the marker.\n  TextMarker.prototype.clear = function () {\n    if (this.explicitlyCleared) { return }\n    var cm = this.doc.cm, withOp = cm && !cm.curOp;\n    if (withOp) { startOperation(cm); }\n    if (hasHandler(this, \"clear\")) {\n      var found = this.find();\n      if (found) { signalLater(this, \"clear\", found.from, found.to); }\n    }\n    var min = null, max = null;\n    for (var i = 0; i < this.lines.length; ++i) {\n      var line = this.lines[i];\n      var span = getMarkedSpanFor(line.markedSpans, this);\n      if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), \"text\"); }\n      else if (cm) {\n        if (span.to != null) { max = lineNo(line); }\n        if (span.from != null) { min = lineNo(line); }\n      }\n      line.markedSpans = removeMarkedSpan(line.markedSpans, span);\n      if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)\n        { updateLineHeight(line, textHeight(cm.display)); }\n    }\n    if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {\n      var visual = visualLine(this.lines[i$1]), len = lineLength(visual);\n      if (len > cm.display.maxLineLength) {\n        cm.display.maxLine = visual;\n        cm.display.maxLineLength = len;\n        cm.display.maxLineChanged = true;\n      }\n    } }\n\n    if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }\n    this.lines.length = 0;\n    this.explicitlyCleared = true;\n    if (this.atomic && this.doc.cantEdit) {\n      this.doc.cantEdit = false;\n      if (cm) { reCheckSelection(cm.doc); }\n    }\n    if (cm) { signalLater(cm, \"markerCleared\", cm, this, min, max); }\n    if (withOp) { endOperation(cm); }\n    if (this.parent) { this.parent.clear(); }\n  };\n\n  // Find the position of the marker in the document. Returns a {from,\n  // to} object by default. Side can be passed to get a specific side\n  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the\n  // Pos objects returned contain a line object, rather than a line\n  // number (used to prevent looking up the same line twice).\n  TextMarker.prototype.find = function (side, lineObj) {\n    if (side == null && this.type == \"bookmark\") { side = 1; }\n    var from, to;\n    for (var i = 0; i < this.lines.length; ++i) {\n      var line = this.lines[i];\n      var span = getMarkedSpanFor(line.markedSpans, this);\n      if (span.from != null) {\n        from = Pos(lineObj ? line : lineNo(line), span.from);\n        if (side == -1) { return from }\n      }\n      if (span.to != null) {\n        to = Pos(lineObj ? line : lineNo(line), span.to);\n        if (side == 1) { return to }\n      }\n    }\n    return from && {from: from, to: to}\n  };\n\n  // Signals that the marker's widget changed, and surrounding layout\n  // should be recomputed.\n  TextMarker.prototype.changed = function () {\n      var this$1 = this;\n\n    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;\n    if (!pos || !cm) { return }\n    runInOp(cm, function () {\n      var line = pos.line, lineN = lineNo(pos.line);\n      var view = findViewForLine(cm, lineN);\n      if (view) {\n        clearLineMeasurementCacheFor(view);\n        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;\n      }\n      cm.curOp.updateMaxLine = true;\n      if (!lineIsHidden(widget.doc, line) && widget.height != null) {\n        var oldHeight = widget.height;\n        widget.height = null;\n        var dHeight = widgetHeight(widget) - oldHeight;\n        if (dHeight)\n          { updateLineHeight(line, line.height + dHeight); }\n      }\n      signalLater(cm, \"markerChanged\", cm, this$1);\n    });\n  };\n\n  TextMarker.prototype.attachLine = function (line) {\n    if (!this.lines.length && this.doc.cm) {\n      var op = this.doc.cm.curOp;\n      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)\n        { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }\n    }\n    this.lines.push(line);\n  };\n\n  TextMarker.prototype.detachLine = function (line) {\n    this.lines.splice(indexOf(this.lines, line), 1);\n    if (!this.lines.length && this.doc.cm) {\n      var op = this.doc.cm.curOp\n      ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);\n    }\n  };\n  eventMixin(TextMarker);\n\n  // Create a marker, wire it up to the right lines, and\n  function markText(doc, from, to, options, type) {\n    // Shared markers (across linked documents) are handled separately\n    // (markTextShared will call out to this again, once per\n    // document).\n    if (options && options.shared) { return markTextShared(doc, from, to, options, type) }\n    // Ensure we are in an operation.\n    if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }\n\n    var marker = new TextMarker(doc, type), diff = cmp(from, to);\n    if (options) { copyObj(options, marker, false); }\n    // Don't connect empty markers unless clearWhenEmpty is false\n    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)\n      { return marker }\n    if (marker.replacedWith) {\n      // Showing up as a widget implies collapsed (widget replaces text)\n      marker.collapsed = true;\n      marker.widgetNode = eltP(\"span\", [marker.replacedWith], \"CodeMirror-widget\");\n      if (!options.handleMouseEvents) { marker.widgetNode.setAttribute(\"cm-ignore-events\", \"true\"); }\n      if (options.insertLeft) { marker.widgetNode.insertLeft = true; }\n    }\n    if (marker.collapsed) {\n      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||\n          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))\n        { throw new Error(\"Inserting collapsed marker partially overlapping an existing one\") }\n      seeCollapsedSpans();\n    }\n\n    if (marker.addToHistory)\n      { addChangeToHistory(doc, {from: from, to: to, origin: \"markText\"}, doc.sel, NaN); }\n\n    var curLine = from.line, cm = doc.cm, updateMaxLine;\n    doc.iter(curLine, to.line + 1, function (line) {\n      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)\n        { updateMaxLine = true; }\n      if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }\n      addMarkedSpan(line, new MarkedSpan(marker,\n                                         curLine == from.line ? from.ch : null,\n                                         curLine == to.line ? to.ch : null), doc.cm && doc.cm.curOp);\n      ++curLine;\n    });\n    // lineIsHidden depends on the presence of the spans, so needs a second pass\n    if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {\n      if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }\n    }); }\n\n    if (marker.clearOnEnter) { on(marker, \"beforeCursorEnter\", function () { return marker.clear(); }); }\n\n    if (marker.readOnly) {\n      seeReadOnlySpans();\n      if (doc.history.done.length || doc.history.undone.length)\n        { doc.clearHistory(); }\n    }\n    if (marker.collapsed) {\n      marker.id = ++nextMarkerId;\n      marker.atomic = true;\n    }\n    if (cm) {\n      // Sync editor state\n      if (updateMaxLine) { cm.curOp.updateMaxLine = true; }\n      if (marker.collapsed)\n        { regChange(cm, from.line, to.line + 1); }\n      else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||\n               marker.attributes || marker.title)\n        { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, \"text\"); } }\n      if (marker.atomic) { reCheckSelection(cm.doc); }\n      signalLater(cm, \"markerAdded\", cm, marker);\n    }\n    return marker\n  }\n\n  // SHARED TEXTMARKERS\n\n  // A shared marker spans multiple linked documents. It is\n  // implemented as a meta-marker-object controlling multiple normal\n  // markers.\n  var SharedTextMarker = function(markers, primary) {\n    this.markers = markers;\n    this.primary = primary;\n    for (var i = 0; i < markers.length; ++i)\n      { markers[i].parent = this; }\n  };\n\n  SharedTextMarker.prototype.clear = function () {\n    if (this.explicitlyCleared) { return }\n    this.explicitlyCleared = true;\n    for (var i = 0; i < this.markers.length; ++i)\n      { this.markers[i].clear(); }\n    signalLater(this, \"clear\");\n  };\n\n  SharedTextMarker.prototype.find = function (side, lineObj) {\n    return this.primary.find(side, lineObj)\n  };\n  eventMixin(SharedTextMarker);\n\n  function markTextShared(doc, from, to, options, type) {\n    options = copyObj(options);\n    options.shared = false;\n    var markers = [markText(doc, from, to, options, type)], primary = markers[0];\n    var widget = options.widgetNode;\n    linkedDocs(doc, function (doc) {\n      if (widget) { options.widgetNode = widget.cloneNode(true); }\n      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));\n      for (var i = 0; i < doc.linked.length; ++i)\n        { if (doc.linked[i].isParent) { return } }\n      primary = lst(markers);\n    });\n    return new SharedTextMarker(markers, primary)\n  }\n\n  function findSharedMarkers(doc) {\n    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })\n  }\n\n  function copySharedMarkers(doc, markers) {\n    for (var i = 0; i < markers.length; i++) {\n      var marker = markers[i], pos = marker.find();\n      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);\n      if (cmp(mFrom, mTo)) {\n        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);\n        marker.markers.push(subMark);\n        subMark.parent = marker;\n      }\n    }\n  }\n\n  function detachSharedMarkers(markers) {\n    var loop = function ( i ) {\n      var marker = markers[i], linked = [marker.primary.doc];\n      linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });\n      for (var j = 0; j < marker.markers.length; j++) {\n        var subMarker = marker.markers[j];\n        if (indexOf(linked, subMarker.doc) == -1) {\n          subMarker.parent = null;\n          marker.markers.splice(j--, 1);\n        }\n      }\n    };\n\n    for (var i = 0; i < markers.length; i++) loop( i );\n  }\n\n  var nextDocId = 0;\n  var Doc = function(text, mode, firstLine, lineSep, direction) {\n    if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }\n    if (firstLine == null) { firstLine = 0; }\n\n    BranchChunk.call(this, [new LeafChunk([new Line(\"\", null)])]);\n    this.first = firstLine;\n    this.scrollTop = this.scrollLeft = 0;\n    this.cantEdit = false;\n    this.cleanGeneration = 1;\n    this.modeFrontier = this.highlightFrontier = firstLine;\n    var start = Pos(firstLine, 0);\n    this.sel = simpleSelection(start);\n    this.history = new History(null);\n    this.id = ++nextDocId;\n    this.modeOption = mode;\n    this.lineSep = lineSep;\n    this.direction = (direction == \"rtl\") ? \"rtl\" : \"ltr\";\n    this.extend = false;\n\n    if (typeof text == \"string\") { text = this.splitLines(text); }\n    updateDoc(this, {from: start, to: start, text: text});\n    setSelection(this, simpleSelection(start), sel_dontScroll);\n  };\n\n  Doc.prototype = createObj(BranchChunk.prototype, {\n    constructor: Doc,\n    // Iterate over the document. Supports two forms -- with only one\n    // argument, it calls that for each line in the document. With\n    // three, it iterates over the range given by the first two (with\n    // the second being non-inclusive).\n    iter: function(from, to, op) {\n      if (op) { this.iterN(from - this.first, to - from, op); }\n      else { this.iterN(this.first, this.first + this.size, from); }\n    },\n\n    // Non-public interface for adding and removing lines.\n    insert: function(at, lines) {\n      var height = 0;\n      for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }\n      this.insertInner(at - this.first, lines, height);\n    },\n    remove: function(at, n) { this.removeInner(at - this.first, n); },\n\n    // From here, the methods are part of the public interface. Most\n    // are also available from CodeMirror (editor) instances.\n\n    getValue: function(lineSep) {\n      var lines = getLines(this, this.first, this.first + this.size);\n      if (lineSep === false) { return lines }\n      return lines.join(lineSep || this.lineSeparator())\n    },\n    setValue: docMethodOp(function(code) {\n      var top = Pos(this.first, 0), last = this.first + this.size - 1;\n      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),\n                        text: this.splitLines(code), origin: \"setValue\", full: true}, true);\n      if (this.cm) { scrollToCoords(this.cm, 0, 0); }\n      setSelection(this, simpleSelection(top), sel_dontScroll);\n    }),\n    replaceRange: function(code, from, to, origin) {\n      from = clipPos(this, from);\n      to = to ? clipPos(this, to) : from;\n      replaceRange(this, code, from, to, origin);\n    },\n    getRange: function(from, to, lineSep) {\n      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));\n      if (lineSep === false) { return lines }\n      if (lineSep === '') { return lines.join('') }\n      return lines.join(lineSep || this.lineSeparator())\n    },\n\n    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},\n\n    getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},\n    getLineNumber: function(line) {return lineNo(line)},\n\n    getLineHandleVisualStart: function(line) {\n      if (typeof line == \"number\") { line = getLine(this, line); }\n      return visualLine(line)\n    },\n\n    lineCount: function() {return this.size},\n    firstLine: function() {return this.first},\n    lastLine: function() {return this.first + this.size - 1},\n\n    clipPos: function(pos) {return clipPos(this, pos)},\n\n    getCursor: function(start) {\n      var range = this.sel.primary(), pos;\n      if (start == null || start == \"head\") { pos = range.head; }\n      else if (start == \"anchor\") { pos = range.anchor; }\n      else if (start == \"end\" || start == \"to\" || start === false) { pos = range.to(); }\n      else { pos = range.from(); }\n      return pos\n    },\n    listSelections: function() { return this.sel.ranges },\n    somethingSelected: function() {return this.sel.somethingSelected()},\n\n    setCursor: docMethodOp(function(line, ch, options) {\n      setSimpleSelection(this, clipPos(this, typeof line == \"number\" ? Pos(line, ch || 0) : line), null, options);\n    }),\n    setSelection: docMethodOp(function(anchor, head, options) {\n      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);\n    }),\n    extendSelection: docMethodOp(function(head, other, options) {\n      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);\n    }),\n    extendSelections: docMethodOp(function(heads, options) {\n      extendSelections(this, clipPosArray(this, heads), options);\n    }),\n    extendSelectionsBy: docMethodOp(function(f, options) {\n      var heads = map(this.sel.ranges, f);\n      extendSelections(this, clipPosArray(this, heads), options);\n    }),\n    setSelections: docMethodOp(function(ranges, primary, options) {\n      if (!ranges.length) { return }\n      var out = [];\n      for (var i = 0; i < ranges.length; i++)\n        { out[i] = new Range(clipPos(this, ranges[i].anchor),\n                           clipPos(this, ranges[i].head || ranges[i].anchor)); }\n      if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }\n      setSelection(this, normalizeSelection(this.cm, out, primary), options);\n    }),\n    addSelection: docMethodOp(function(anchor, head, options) {\n      var ranges = this.sel.ranges.slice(0);\n      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));\n      setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);\n    }),\n\n    getSelection: function(lineSep) {\n      var ranges = this.sel.ranges, lines;\n      for (var i = 0; i < ranges.length; i++) {\n        var sel = getBetween(this, ranges[i].from(), ranges[i].to());\n        lines = lines ? lines.concat(sel) : sel;\n      }\n      if (lineSep === false) { return lines }\n      else { return lines.join(lineSep || this.lineSeparator()) }\n    },\n    getSelections: function(lineSep) {\n      var parts = [], ranges = this.sel.ranges;\n      for (var i = 0; i < ranges.length; i++) {\n        var sel = getBetween(this, ranges[i].from(), ranges[i].to());\n        if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); }\n        parts[i] = sel;\n      }\n      return parts\n    },\n    replaceSelection: function(code, collapse, origin) {\n      var dup = [];\n      for (var i = 0; i < this.sel.ranges.length; i++)\n        { dup[i] = code; }\n      this.replaceSelections(dup, collapse, origin || \"+input\");\n    },\n    replaceSelections: docMethodOp(function(code, collapse, origin) {\n      var changes = [], sel = this.sel;\n      for (var i = 0; i < sel.ranges.length; i++) {\n        var range = sel.ranges[i];\n        changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};\n      }\n      var newSel = collapse && collapse != \"end\" && computeReplacedSel(this, changes, collapse);\n      for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)\n        { makeChange(this, changes[i$1]); }\n      if (newSel) { setSelectionReplaceHistory(this, newSel); }\n      else if (this.cm) { ensureCursorVisible(this.cm); }\n    }),\n    undo: docMethodOp(function() {makeChangeFromHistory(this, \"undo\");}),\n    redo: docMethodOp(function() {makeChangeFromHistory(this, \"redo\");}),\n    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, \"undo\", true);}),\n    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, \"redo\", true);}),\n\n    setExtending: function(val) {this.extend = val;},\n    getExtending: function() {return this.extend},\n\n    historySize: function() {\n      var hist = this.history, done = 0, undone = 0;\n      for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }\n      for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }\n      return {undo: done, redo: undone}\n    },\n    clearHistory: function() {\n      var this$1 = this;\n\n      this.history = new History(this.history);\n      linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true);\n    },\n\n    markClean: function() {\n      this.cleanGeneration = this.changeGeneration(true);\n    },\n    changeGeneration: function(forceSplit) {\n      if (forceSplit)\n        { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }\n      return this.history.generation\n    },\n    isClean: function (gen) {\n      return this.history.generation == (gen || this.cleanGeneration)\n    },\n\n    getHistory: function() {\n      return {done: copyHistoryArray(this.history.done),\n              undone: copyHistoryArray(this.history.undone)}\n    },\n    setHistory: function(histData) {\n      var hist = this.history = new History(this.history);\n      hist.done = copyHistoryArray(histData.done.slice(0), null, true);\n      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);\n    },\n\n    setGutterMarker: docMethodOp(function(line, gutterID, value) {\n      return changeLine(this, line, \"gutter\", function (line) {\n        var markers = line.gutterMarkers || (line.gutterMarkers = {});\n        markers[gutterID] = value;\n        if (!value && isEmpty(markers)) { line.gutterMarkers = null; }\n        return true\n      })\n    }),\n\n    clearGutter: docMethodOp(function(gutterID) {\n      var this$1 = this;\n\n      this.iter(function (line) {\n        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {\n          changeLine(this$1, line, \"gutter\", function () {\n            line.gutterMarkers[gutterID] = null;\n            if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }\n            return true\n          });\n        }\n      });\n    }),\n\n    lineInfo: function(line) {\n      var n;\n      if (typeof line == \"number\") {\n        if (!isLine(this, line)) { return null }\n        n = line;\n        line = getLine(this, line);\n        if (!line) { return null }\n      } else {\n        n = lineNo(line);\n        if (n == null) { return null }\n      }\n      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,\n              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,\n              widgets: line.widgets}\n    },\n\n    addLineClass: docMethodOp(function(handle, where, cls) {\n      return changeLine(this, handle, where == \"gutter\" ? \"gutter\" : \"class\", function (line) {\n        var prop = where == \"text\" ? \"textClass\"\n                 : where == \"background\" ? \"bgClass\"\n                 : where == \"gutter\" ? \"gutterClass\" : \"wrapClass\";\n        if (!line[prop]) { line[prop] = cls; }\n        else if (classTest(cls).test(line[prop])) { return false }\n        else { line[prop] += \" \" + cls; }\n        return true\n      })\n    }),\n    removeLineClass: docMethodOp(function(handle, where, cls) {\n      return changeLine(this, handle, where == \"gutter\" ? \"gutter\" : \"class\", function (line) {\n        var prop = where == \"text\" ? \"textClass\"\n                 : where == \"background\" ? \"bgClass\"\n                 : where == \"gutter\" ? \"gutterClass\" : \"wrapClass\";\n        var cur = line[prop];\n        if (!cur) { return false }\n        else if (cls == null) { line[prop] = null; }\n        else {\n          var found = cur.match(classTest(cls));\n          if (!found) { return false }\n          var end = found.index + found[0].length;\n          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? \"\" : \" \") + cur.slice(end) || null;\n        }\n        return true\n      })\n    }),\n\n    addLineWidget: docMethodOp(function(handle, node, options) {\n      return addLineWidget(this, handle, node, options)\n    }),\n    removeLineWidget: function(widget) { widget.clear(); },\n\n    markText: function(from, to, options) {\n      return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || \"range\")\n    },\n    setBookmark: function(pos, options) {\n      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),\n                      insertLeft: options && options.insertLeft,\n                      clearWhenEmpty: false, shared: options && options.shared,\n                      handleMouseEvents: options && options.handleMouseEvents};\n      pos = clipPos(this, pos);\n      return markText(this, pos, pos, realOpts, \"bookmark\")\n    },\n    findMarksAt: function(pos) {\n      pos = clipPos(this, pos);\n      var markers = [], spans = getLine(this, pos.line).markedSpans;\n      if (spans) { for (var i = 0; i < spans.length; ++i) {\n        var span = spans[i];\n        if ((span.from == null || span.from <= pos.ch) &&\n            (span.to == null || span.to >= pos.ch))\n          { markers.push(span.marker.parent || span.marker); }\n      } }\n      return markers\n    },\n    findMarks: function(from, to, filter) {\n      from = clipPos(this, from); to = clipPos(this, to);\n      var found = [], lineNo = from.line;\n      this.iter(from.line, to.line + 1, function (line) {\n        var spans = line.markedSpans;\n        if (spans) { for (var i = 0; i < spans.length; i++) {\n          var span = spans[i];\n          if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||\n                span.from == null && lineNo != from.line ||\n                span.from != null && lineNo == to.line && span.from >= to.ch) &&\n              (!filter || filter(span.marker)))\n            { found.push(span.marker.parent || span.marker); }\n        } }\n        ++lineNo;\n      });\n      return found\n    },\n    getAllMarks: function() {\n      var markers = [];\n      this.iter(function (line) {\n        var sps = line.markedSpans;\n        if (sps) { for (var i = 0; i < sps.length; ++i)\n          { if (sps[i].from != null) { markers.push(sps[i].marker); } } }\n      });\n      return markers\n    },\n\n    posFromIndex: function(off) {\n      var ch, lineNo = this.first, sepSize = this.lineSeparator().length;\n      this.iter(function (line) {\n        var sz = line.text.length + sepSize;\n        if (sz > off) { ch = off; return true }\n        off -= sz;\n        ++lineNo;\n      });\n      return clipPos(this, Pos(lineNo, ch))\n    },\n    indexFromPos: function (coords) {\n      coords = clipPos(this, coords);\n      var index = coords.ch;\n      if (coords.line < this.first || coords.ch < 0) { return 0 }\n      var sepSize = this.lineSeparator().length;\n      this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value\n        index += line.text.length + sepSize;\n      });\n      return index\n    },\n\n    copy: function(copyHistory) {\n      var doc = new Doc(getLines(this, this.first, this.first + this.size),\n                        this.modeOption, this.first, this.lineSep, this.direction);\n      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;\n      doc.sel = this.sel;\n      doc.extend = false;\n      if (copyHistory) {\n        doc.history.undoDepth = this.history.undoDepth;\n        doc.setHistory(this.getHistory());\n      }\n      return doc\n    },\n\n    linkedDoc: function(options) {\n      if (!options) { options = {}; }\n      var from = this.first, to = this.first + this.size;\n      if (options.from != null && options.from > from) { from = options.from; }\n      if (options.to != null && options.to < to) { to = options.to; }\n      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);\n      if (options.sharedHist) { copy.history = this.history\n      ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});\n      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];\n      copySharedMarkers(copy, findSharedMarkers(this));\n      return copy\n    },\n    unlinkDoc: function(other) {\n      if (other instanceof CodeMirror) { other = other.doc; }\n      if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {\n        var link = this.linked[i];\n        if (link.doc != other) { continue }\n        this.linked.splice(i, 1);\n        other.unlinkDoc(this);\n        detachSharedMarkers(findSharedMarkers(this));\n        break\n      } }\n      // If the histories were shared, split them again\n      if (other.history == this.history) {\n        var splitIds = [other.id];\n        linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);\n        other.history = new History(null);\n        other.history.done = copyHistoryArray(this.history.done, splitIds);\n        other.history.undone = copyHistoryArray(this.history.undone, splitIds);\n      }\n    },\n    iterLinkedDocs: function(f) {linkedDocs(this, f);},\n\n    getMode: function() {return this.mode},\n    getEditor: function() {return this.cm},\n\n    splitLines: function(str) {\n      if (this.lineSep) { return str.split(this.lineSep) }\n      return splitLinesAuto(str)\n    },\n    lineSeparator: function() { return this.lineSep || \"\\n\" },\n\n    setDirection: docMethodOp(function (dir) {\n      if (dir != \"rtl\") { dir = \"ltr\"; }\n      if (dir == this.direction) { return }\n      this.direction = dir;\n      this.iter(function (line) { return line.order = null; });\n      if (this.cm) { directionChanged(this.cm); }\n    })\n  });\n\n  // Public alias.\n  Doc.prototype.eachLine = Doc.prototype.iter;\n\n  // Kludge to work around strange IE behavior where it'll sometimes\n  // re-fire a series of drag-related events right after the drop (#1551)\n  var lastDrop = 0;\n\n  function onDrop(e) {\n    var cm = this;\n    clearDragCursor(cm);\n    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))\n      { return }\n    e_preventDefault(e);\n    if (ie) { lastDrop = +new Date; }\n    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;\n    if (!pos || cm.isReadOnly()) { return }\n    // Might be a file drop, in which case we simply extract the text\n    // and insert it.\n    if (files && files.length && window.FileReader && window.File) {\n      var n = files.length, text = Array(n), read = 0;\n      var markAsReadAndPasteIfAllFilesAreRead = function () {\n        if (++read == n) {\n          operation(cm, function () {\n            pos = clipPos(cm.doc, pos);\n            var change = {from: pos, to: pos,\n                          text: cm.doc.splitLines(\n                              text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())),\n                          origin: \"paste\"};\n            makeChange(cm.doc, change);\n            setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))));\n          })();\n        }\n      };\n      var readTextFromFile = function (file, i) {\n        if (cm.options.allowDropFileTypes &&\n            indexOf(cm.options.allowDropFileTypes, file.type) == -1) {\n          markAsReadAndPasteIfAllFilesAreRead();\n          return\n        }\n        var reader = new FileReader;\n        reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); };\n        reader.onload = function () {\n          var content = reader.result;\n          if (/[\\x00-\\x08\\x0e-\\x1f]{2}/.test(content)) {\n            markAsReadAndPasteIfAllFilesAreRead();\n            return\n          }\n          text[i] = content;\n          markAsReadAndPasteIfAllFilesAreRead();\n        };\n        reader.readAsText(file);\n      };\n      for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); }\n    } else { // Normal drop\n      // Don't do a replace if the drop happened inside of the selected text.\n      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {\n        cm.state.draggingText(e);\n        // Ensure the editor is re-focused\n        setTimeout(function () { return cm.display.input.focus(); }, 20);\n        return\n      }\n      try {\n        var text$1 = e.dataTransfer.getData(\"Text\");\n        if (text$1) {\n          var selected;\n          if (cm.state.draggingText && !cm.state.draggingText.copy)\n            { selected = cm.listSelections(); }\n          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));\n          if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)\n            { replaceRange(cm.doc, \"\", selected[i$1].anchor, selected[i$1].head, \"drag\"); } }\n          cm.replaceSelection(text$1, \"around\", \"paste\");\n          cm.display.input.focus();\n        }\n      }\n      catch(e$1){}\n    }\n  }\n\n  function onDragStart(cm, e) {\n    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }\n    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }\n\n    e.dataTransfer.setData(\"Text\", cm.getSelection());\n    e.dataTransfer.effectAllowed = \"copyMove\";\n\n    // Use dummy image instead of default browsers image.\n    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.\n    if (e.dataTransfer.setDragImage && !safari) {\n      var img = elt(\"img\", null, null, \"position: fixed; left: 0; top: 0;\");\n      img.src = \"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\";\n      if (presto) {\n        img.width = img.height = 1;\n        cm.display.wrapper.appendChild(img);\n        // Force a relayout, or Opera won't use our image for some obscure reason\n        img._top = img.offsetTop;\n      }\n      e.dataTransfer.setDragImage(img, 0, 0);\n      if (presto) { img.parentNode.removeChild(img); }\n    }\n  }\n\n  function onDragOver(cm, e) {\n    var pos = posFromMouse(cm, e);\n    if (!pos) { return }\n    var frag = document.createDocumentFragment();\n    drawSelectionCursor(cm, pos, frag);\n    if (!cm.display.dragCursor) {\n      cm.display.dragCursor = elt(\"div\", null, \"CodeMirror-cursors CodeMirror-dragcursors\");\n      cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);\n    }\n    removeChildrenAndAdd(cm.display.dragCursor, frag);\n  }\n\n  function clearDragCursor(cm) {\n    if (cm.display.dragCursor) {\n      cm.display.lineSpace.removeChild(cm.display.dragCursor);\n      cm.display.dragCursor = null;\n    }\n  }\n\n  // These must be handled carefully, because naively registering a\n  // handler for each editor will cause the editors to never be\n  // garbage collected.\n\n  function forEachCodeMirror(f) {\n    if (!document.getElementsByClassName) { return }\n    var byClass = document.getElementsByClassName(\"CodeMirror\"), editors = [];\n    for (var i = 0; i < byClass.length; i++) {\n      var cm = byClass[i].CodeMirror;\n      if (cm) { editors.push(cm); }\n    }\n    if (editors.length) { editors[0].operation(function () {\n      for (var i = 0; i < editors.length; i++) { f(editors[i]); }\n    }); }\n  }\n\n  var globalsRegistered = false;\n  function ensureGlobalHandlers() {\n    if (globalsRegistered) { return }\n    registerGlobalHandlers();\n    globalsRegistered = true;\n  }\n  function registerGlobalHandlers() {\n    // When the window resizes, we need to refresh active editors.\n    var resizeTimer;\n    on(window, \"resize\", function () {\n      if (resizeTimer == null) { resizeTimer = setTimeout(function () {\n        resizeTimer = null;\n        forEachCodeMirror(onResize);\n      }, 100); }\n    });\n    // When the window loses focus, we want to show the editor as blurred\n    on(window, \"blur\", function () { return forEachCodeMirror(onBlur); });\n  }\n  // Called when the window resizes\n  function onResize(cm) {\n    var d = cm.display;\n    // Might be a text scaling operation, clear size caches.\n    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;\n    d.scrollbarsClipped = false;\n    cm.setSize();\n  }\n\n  var keyNames = {\n    3: \"Pause\", 8: \"Backspace\", 9: \"Tab\", 13: \"Enter\", 16: \"Shift\", 17: \"Ctrl\", 18: \"Alt\",\n    19: \"Pause\", 20: \"CapsLock\", 27: \"Esc\", 32: \"Space\", 33: \"PageUp\", 34: \"PageDown\", 35: \"End\",\n    36: \"Home\", 37: \"Left\", 38: \"Up\", 39: \"Right\", 40: \"Down\", 44: \"PrintScrn\", 45: \"Insert\",\n    46: \"Delete\", 59: \";\", 61: \"=\", 91: \"Mod\", 92: \"Mod\", 93: \"Mod\",\n    106: \"*\", 107: \"=\", 109: \"-\", 110: \".\", 111: \"/\", 145: \"ScrollLock\",\n    173: \"-\", 186: \";\", 187: \"=\", 188: \",\", 189: \"-\", 190: \".\", 191: \"/\", 192: \"`\", 219: \"[\", 220: \"\\\\\",\n    221: \"]\", 222: \"'\", 224: \"Mod\", 63232: \"Up\", 63233: \"Down\", 63234: \"Left\", 63235: \"Right\", 63272: \"Delete\",\n    63273: \"Home\", 63275: \"End\", 63276: \"PageUp\", 63277: \"PageDown\", 63302: \"Insert\"\n  };\n\n  // Number keys\n  for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }\n  // Alphabetic keys\n  for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }\n  // Function keys\n  for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = \"F\" + i$2; }\n\n  var keyMap = {};\n\n  keyMap.basic = {\n    \"Left\": \"goCharLeft\", \"Right\": \"goCharRight\", \"Up\": \"goLineUp\", \"Down\": \"goLineDown\",\n    \"End\": \"goLineEnd\", \"Home\": \"goLineStartSmart\", \"PageUp\": \"goPageUp\", \"PageDown\": \"goPageDown\",\n    \"Delete\": \"delCharAfter\", \"Backspace\": \"delCharBefore\", \"Shift-Backspace\": \"delCharBefore\",\n    \"Tab\": \"defaultTab\", \"Shift-Tab\": \"indentAuto\",\n    \"Enter\": \"newlineAndIndent\", \"Insert\": \"toggleOverwrite\",\n    \"Esc\": \"singleSelection\"\n  };\n  // Note that the save and find-related commands aren't defined by\n  // default. User code or addons can define them. Unknown commands\n  // are simply ignored.\n  keyMap.pcDefault = {\n    \"Ctrl-A\": \"selectAll\", \"Ctrl-D\": \"deleteLine\", \"Ctrl-Z\": \"undo\", \"Shift-Ctrl-Z\": \"redo\", \"Ctrl-Y\": \"redo\",\n    \"Ctrl-Home\": \"goDocStart\", \"Ctrl-End\": \"goDocEnd\", \"Ctrl-Up\": \"goLineUp\", \"Ctrl-Down\": \"goLineDown\",\n    \"Ctrl-Left\": \"goGroupLeft\", \"Ctrl-Right\": \"goGroupRight\", \"Alt-Left\": \"goLineStart\", \"Alt-Right\": \"goLineEnd\",\n    \"Ctrl-Backspace\": \"delGroupBefore\", \"Ctrl-Delete\": \"delGroupAfter\", \"Ctrl-S\": \"save\", \"Ctrl-F\": \"find\",\n    \"Ctrl-G\": \"findNext\", \"Shift-Ctrl-G\": \"findPrev\", \"Shift-Ctrl-F\": \"replace\", \"Shift-Ctrl-R\": \"replaceAll\",\n    \"Ctrl-[\": \"indentLess\", \"Ctrl-]\": \"indentMore\",\n    \"Ctrl-U\": \"undoSelection\", \"Shift-Ctrl-U\": \"redoSelection\", \"Alt-U\": \"redoSelection\",\n    \"fallthrough\": \"basic\"\n  };\n  // Very basic readline/emacs-style bindings, which are standard on Mac.\n  keyMap.emacsy = {\n    \"Ctrl-F\": \"goCharRight\", \"Ctrl-B\": \"goCharLeft\", \"Ctrl-P\": \"goLineUp\", \"Ctrl-N\": \"goLineDown\",\n    \"Ctrl-A\": \"goLineStart\", \"Ctrl-E\": \"goLineEnd\", \"Ctrl-V\": \"goPageDown\", \"Shift-Ctrl-V\": \"goPageUp\",\n    \"Ctrl-D\": \"delCharAfter\", \"Ctrl-H\": \"delCharBefore\", \"Alt-Backspace\": \"delWordBefore\", \"Ctrl-K\": \"killLine\",\n    \"Ctrl-T\": \"transposeChars\", \"Ctrl-O\": \"openLine\"\n  };\n  keyMap.macDefault = {\n    \"Cmd-A\": \"selectAll\", \"Cmd-D\": \"deleteLine\", \"Cmd-Z\": \"undo\", \"Shift-Cmd-Z\": \"redo\", \"Cmd-Y\": \"redo\",\n    \"Cmd-Home\": \"goDocStart\", \"Cmd-Up\": \"goDocStart\", \"Cmd-End\": \"goDocEnd\", \"Cmd-Down\": \"goDocEnd\", \"Alt-Left\": \"goGroupLeft\",\n    \"Alt-Right\": \"goGroupRight\", \"Cmd-Left\": \"goLineLeft\", \"Cmd-Right\": \"goLineRight\", \"Alt-Backspace\": \"delGroupBefore\",\n    \"Ctrl-Alt-Backspace\": \"delGroupAfter\", \"Alt-Delete\": \"delGroupAfter\", \"Cmd-S\": \"save\", \"Cmd-F\": \"find\",\n    \"Cmd-G\": \"findNext\", \"Shift-Cmd-G\": \"findPrev\", \"Cmd-Alt-F\": \"replace\", \"Shift-Cmd-Alt-F\": \"replaceAll\",\n    \"Cmd-[\": \"indentLess\", \"Cmd-]\": \"indentMore\", \"Cmd-Backspace\": \"delWrappedLineLeft\", \"Cmd-Delete\": \"delWrappedLineRight\",\n    \"Cmd-U\": \"undoSelection\", \"Shift-Cmd-U\": \"redoSelection\", \"Ctrl-Up\": \"goDocStart\", \"Ctrl-Down\": \"goDocEnd\",\n    \"fallthrough\": [\"basic\", \"emacsy\"]\n  };\n  keyMap[\"default\"] = mac ? keyMap.macDefault : keyMap.pcDefault;\n\n  // KEYMAP DISPATCH\n\n  function normalizeKeyName(name) {\n    var parts = name.split(/-(?!$)/);\n    name = parts[parts.length - 1];\n    var alt, ctrl, shift, cmd;\n    for (var i = 0; i < parts.length - 1; i++) {\n      var mod = parts[i];\n      if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }\n      else if (/^a(lt)?$/i.test(mod)) { alt = true; }\n      else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }\n      else if (/^s(hift)?$/i.test(mod)) { shift = true; }\n      else { throw new Error(\"Unrecognized modifier name: \" + mod) }\n    }\n    if (alt) { name = \"Alt-\" + name; }\n    if (ctrl) { name = \"Ctrl-\" + name; }\n    if (cmd) { name = \"Cmd-\" + name; }\n    if (shift) { name = \"Shift-\" + name; }\n    return name\n  }\n\n  // This is a kludge to keep keymaps mostly working as raw objects\n  // (backwards compatibility) while at the same time support features\n  // like normalization and multi-stroke key bindings. It compiles a\n  // new normalized keymap, and then updates the old object to reflect\n  // this.\n  function normalizeKeyMap(keymap) {\n    var copy = {};\n    for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {\n      var value = keymap[keyname];\n      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }\n      if (value == \"...\") { delete keymap[keyname]; continue }\n\n      var keys = map(keyname.split(\" \"), normalizeKeyName);\n      for (var i = 0; i < keys.length; i++) {\n        var val = (void 0), name = (void 0);\n        if (i == keys.length - 1) {\n          name = keys.join(\" \");\n          val = value;\n        } else {\n          name = keys.slice(0, i + 1).join(\" \");\n          val = \"...\";\n        }\n        var prev = copy[name];\n        if (!prev) { copy[name] = val; }\n        else if (prev != val) { throw new Error(\"Inconsistent bindings for \" + name) }\n      }\n      delete keymap[keyname];\n    } }\n    for (var prop in copy) { keymap[prop] = copy[prop]; }\n    return keymap\n  }\n\n  function lookupKey(key, map, handle, context) {\n    map = getKeyMap(map);\n    var found = map.call ? map.call(key, context) : map[key];\n    if (found === false) { return \"nothing\" }\n    if (found === \"...\") { return \"multi\" }\n    if (found != null && handle(found)) { return \"handled\" }\n\n    if (map.fallthrough) {\n      if (Object.prototype.toString.call(map.fallthrough) != \"[object Array]\")\n        { return lookupKey(key, map.fallthrough, handle, context) }\n      for (var i = 0; i < map.fallthrough.length; i++) {\n        var result = lookupKey(key, map.fallthrough[i], handle, context);\n        if (result) { return result }\n      }\n    }\n  }\n\n  // Modifier key presses don't count as 'real' key presses for the\n  // purpose of keymap fallthrough.\n  function isModifierKey(value) {\n    var name = typeof value == \"string\" ? value : keyNames[value.keyCode];\n    return name == \"Ctrl\" || name == \"Alt\" || name == \"Shift\" || name == \"Mod\"\n  }\n\n  function addModifierNames(name, event, noShift) {\n    var base = name;\n    if (event.altKey && base != \"Alt\") { name = \"Alt-\" + name; }\n    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != \"Ctrl\") { name = \"Ctrl-\" + name; }\n    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != \"Mod\") { name = \"Cmd-\" + name; }\n    if (!noShift && event.shiftKey && base != \"Shift\") { name = \"Shift-\" + name; }\n    return name\n  }\n\n  // Look up the name of a key as indicated by an event object.\n  function keyName(event, noShift) {\n    if (presto && event.keyCode == 34 && event[\"char\"]) { return false }\n    var name = keyNames[event.keyCode];\n    if (name == null || event.altGraphKey) { return false }\n    // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,\n    // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)\n    if (event.keyCode == 3 && event.code) { name = event.code; }\n    return addModifierNames(name, event, noShift)\n  }\n\n  function getKeyMap(val) {\n    return typeof val == \"string\" ? keyMap[val] : val\n  }\n\n  // Helper for deleting text near the selection(s), used to implement\n  // backspace, delete, and similar functionality.\n  function deleteNearSelection(cm, compute) {\n    var ranges = cm.doc.sel.ranges, kill = [];\n    // Build up a set of ranges to kill first, merging overlapping\n    // ranges.\n    for (var i = 0; i < ranges.length; i++) {\n      var toKill = compute(ranges[i]);\n      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {\n        var replaced = kill.pop();\n        if (cmp(replaced.from, toKill.from) < 0) {\n          toKill.from = replaced.from;\n          break\n        }\n      }\n      kill.push(toKill);\n    }\n    // Next, remove those actual ranges.\n    runInOp(cm, function () {\n      for (var i = kill.length - 1; i >= 0; i--)\n        { replaceRange(cm.doc, \"\", kill[i].from, kill[i].to, \"+delete\"); }\n      ensureCursorVisible(cm);\n    });\n  }\n\n  function moveCharLogically(line, ch, dir) {\n    var target = skipExtendingChars(line.text, ch + dir, dir);\n    return target < 0 || target > line.text.length ? null : target\n  }\n\n  function moveLogically(line, start, dir) {\n    var ch = moveCharLogically(line, start.ch, dir);\n    return ch == null ? null : new Pos(start.line, ch, dir < 0 ? \"after\" : \"before\")\n  }\n\n  function endOfLine(visually, cm, lineObj, lineNo, dir) {\n    if (visually) {\n      if (cm.doc.direction == \"rtl\") { dir = -dir; }\n      var order = getOrder(lineObj, cm.doc.direction);\n      if (order) {\n        var part = dir < 0 ? lst(order) : order[0];\n        var moveInStorageOrder = (dir < 0) == (part.level == 1);\n        var sticky = moveInStorageOrder ? \"after\" : \"before\";\n        var ch;\n        // With a wrapped rtl chunk (possibly spanning multiple bidi parts),\n        // it could be that the last bidi part is not on the last visual line,\n        // since visual lines contain content order-consecutive chunks.\n        // Thus, in rtl, we are looking for the first (content-order) character\n        // in the rtl chunk that is on the last line (that is, the same line\n        // as the last (content-order) character).\n        if (part.level > 0 || cm.doc.direction == \"rtl\") {\n          var prep = prepareMeasureForLine(cm, lineObj);\n          ch = dir < 0 ? lineObj.text.length - 1 : 0;\n          var targetTop = measureCharPrepared(cm, prep, ch).top;\n          ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);\n          if (sticky == \"before\") { ch = moveCharLogically(lineObj, ch, 1); }\n        } else { ch = dir < 0 ? part.to : part.from; }\n        return new Pos(lineNo, ch, sticky)\n      }\n    }\n    return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? \"before\" : \"after\")\n  }\n\n  function moveVisually(cm, line, start, dir) {\n    var bidi = getOrder(line, cm.doc.direction);\n    if (!bidi) { return moveLogically(line, start, dir) }\n    if (start.ch >= line.text.length) {\n      start.ch = line.text.length;\n      start.sticky = \"before\";\n    } else if (start.ch <= 0) {\n      start.ch = 0;\n      start.sticky = \"after\";\n    }\n    var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];\n    if (cm.doc.direction == \"ltr\" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {\n      // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,\n      // nothing interesting happens.\n      return moveLogically(line, start, dir)\n    }\n\n    var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };\n    var prep;\n    var getWrappedLineExtent = function (ch) {\n      if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }\n      prep = prep || prepareMeasureForLine(cm, line);\n      return wrappedLineExtentChar(cm, line, prep, ch)\n    };\n    var wrappedLineExtent = getWrappedLineExtent(start.sticky == \"before\" ? mv(start, -1) : start.ch);\n\n    if (cm.doc.direction == \"rtl\" || part.level == 1) {\n      var moveInStorageOrder = (part.level == 1) == (dir < 0);\n      var ch = mv(start, moveInStorageOrder ? 1 : -1);\n      if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {\n        // Case 2: We move within an rtl part or in an rtl editor on the same visual line\n        var sticky = moveInStorageOrder ? \"before\" : \"after\";\n        return new Pos(start.line, ch, sticky)\n      }\n    }\n\n    // Case 3: Could not move within this bidi part in this visual line, so leave\n    // the current bidi part\n\n    var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {\n      var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder\n        ? new Pos(start.line, mv(ch, 1), \"before\")\n        : new Pos(start.line, ch, \"after\"); };\n\n      for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {\n        var part = bidi[partPos];\n        var moveInStorageOrder = (dir > 0) == (part.level != 1);\n        var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);\n        if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }\n        ch = moveInStorageOrder ? part.from : mv(part.to, -1);\n        if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }\n      }\n    };\n\n    // Case 3a: Look for other bidi parts on the same visual line\n    var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);\n    if (res) { return res }\n\n    // Case 3b: Look for other bidi parts on the next visual line\n    var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);\n    if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {\n      res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));\n      if (res) { return res }\n    }\n\n    // Case 4: Nowhere to move\n    return null\n  }\n\n  // Commands are parameter-less actions that can be performed on an\n  // editor, mostly used for keybindings.\n  var commands = {\n    selectAll: selectAll,\n    singleSelection: function (cm) { return cm.setSelection(cm.getCursor(\"anchor\"), cm.getCursor(\"head\"), sel_dontScroll); },\n    killLine: function (cm) { return deleteNearSelection(cm, function (range) {\n      if (range.empty()) {\n        var len = getLine(cm.doc, range.head.line).text.length;\n        if (range.head.ch == len && range.head.line < cm.lastLine())\n          { return {from: range.head, to: Pos(range.head.line + 1, 0)} }\n        else\n          { return {from: range.head, to: Pos(range.head.line, len)} }\n      } else {\n        return {from: range.from(), to: range.to()}\n      }\n    }); },\n    deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({\n      from: Pos(range.from().line, 0),\n      to: clipPos(cm.doc, Pos(range.to().line + 1, 0))\n    }); }); },\n    delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({\n      from: Pos(range.from().line, 0), to: range.from()\n    }); }); },\n    delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {\n      var top = cm.charCoords(range.head, \"div\").top + 5;\n      var leftPos = cm.coordsChar({left: 0, top: top}, \"div\");\n      return {from: leftPos, to: range.from()}\n    }); },\n    delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {\n      var top = cm.charCoords(range.head, \"div\").top + 5;\n      var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, \"div\");\n      return {from: range.from(), to: rightPos }\n    }); },\n    undo: function (cm) { return cm.undo(); },\n    redo: function (cm) { return cm.redo(); },\n    undoSelection: function (cm) { return cm.undoSelection(); },\n    redoSelection: function (cm) { return cm.redoSelection(); },\n    goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },\n    goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },\n    goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },\n      {origin: \"+move\", bias: 1}\n    ); },\n    goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },\n      {origin: \"+move\", bias: 1}\n    ); },\n    goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },\n      {origin: \"+move\", bias: -1}\n    ); },\n    goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {\n      var top = cm.cursorCoords(range.head, \"div\").top + 5;\n      return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, \"div\")\n    }, sel_move); },\n    goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {\n      var top = cm.cursorCoords(range.head, \"div\").top + 5;\n      return cm.coordsChar({left: 0, top: top}, \"div\")\n    }, sel_move); },\n    goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {\n      var top = cm.cursorCoords(range.head, \"div\").top + 5;\n      var pos = cm.coordsChar({left: 0, top: top}, \"div\");\n      if (pos.ch < cm.getLine(pos.line).search(/\\S/)) { return lineStartSmart(cm, range.head) }\n      return pos\n    }, sel_move); },\n    goLineUp: function (cm) { return cm.moveV(-1, \"line\"); },\n    goLineDown: function (cm) { return cm.moveV(1, \"line\"); },\n    goPageUp: function (cm) { return cm.moveV(-1, \"page\"); },\n    goPageDown: function (cm) { return cm.moveV(1, \"page\"); },\n    goCharLeft: function (cm) { return cm.moveH(-1, \"char\"); },\n    goCharRight: function (cm) { return cm.moveH(1, \"char\"); },\n    goColumnLeft: function (cm) { return cm.moveH(-1, \"column\"); },\n    goColumnRight: function (cm) { return cm.moveH(1, \"column\"); },\n    goWordLeft: function (cm) { return cm.moveH(-1, \"word\"); },\n    goGroupRight: function (cm) { return cm.moveH(1, \"group\"); },\n    goGroupLeft: function (cm) { return cm.moveH(-1, \"group\"); },\n    goWordRight: function (cm) { return cm.moveH(1, \"word\"); },\n    delCharBefore: function (cm) { return cm.deleteH(-1, \"codepoint\"); },\n    delCharAfter: function (cm) { return cm.deleteH(1, \"char\"); },\n    delWordBefore: function (cm) { return cm.deleteH(-1, \"word\"); },\n    delWordAfter: function (cm) { return cm.deleteH(1, \"word\"); },\n    delGroupBefore: function (cm) { return cm.deleteH(-1, \"group\"); },\n    delGroupAfter: function (cm) { return cm.deleteH(1, \"group\"); },\n    indentAuto: function (cm) { return cm.indentSelection(\"smart\"); },\n    indentMore: function (cm) { return cm.indentSelection(\"add\"); },\n    indentLess: function (cm) { return cm.indentSelection(\"subtract\"); },\n    insertTab: function (cm) { return cm.replaceSelection(\"\\t\"); },\n    insertSoftTab: function (cm) {\n      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;\n      for (var i = 0; i < ranges.length; i++) {\n        var pos = ranges[i].from();\n        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);\n        spaces.push(spaceStr(tabSize - col % tabSize));\n      }\n      cm.replaceSelections(spaces);\n    },\n    defaultTab: function (cm) {\n      if (cm.somethingSelected()) { cm.indentSelection(\"add\"); }\n      else { cm.execCommand(\"insertTab\"); }\n    },\n    // Swap the two chars left and right of each selection's head.\n    // Move cursor behind the two swapped characters afterwards.\n    //\n    // Doesn't consider line feeds a character.\n    // Doesn't scan more than one line above to find a character.\n    // Doesn't do anything on an empty line.\n    // Doesn't do anything with non-empty selections.\n    transposeChars: function (cm) { return runInOp(cm, function () {\n      var ranges = cm.listSelections(), newSel = [];\n      for (var i = 0; i < ranges.length; i++) {\n        if (!ranges[i].empty()) { continue }\n        var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;\n        if (line) {\n          if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }\n          if (cur.ch > 0) {\n            cur = new Pos(cur.line, cur.ch + 1);\n            cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),\n                            Pos(cur.line, cur.ch - 2), cur, \"+transpose\");\n          } else if (cur.line > cm.doc.first) {\n            var prev = getLine(cm.doc, cur.line - 1).text;\n            if (prev) {\n              cur = new Pos(cur.line, 1);\n              cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +\n                              prev.charAt(prev.length - 1),\n                              Pos(cur.line - 1, prev.length - 1), cur, \"+transpose\");\n            }\n          }\n        }\n        newSel.push(new Range(cur, cur));\n      }\n      cm.setSelections(newSel);\n    }); },\n    newlineAndIndent: function (cm) { return runInOp(cm, function () {\n      var sels = cm.listSelections();\n      for (var i = sels.length - 1; i >= 0; i--)\n        { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, \"+input\"); }\n      sels = cm.listSelections();\n      for (var i$1 = 0; i$1 < sels.length; i$1++)\n        { cm.indentLine(sels[i$1].from().line, null, true); }\n      ensureCursorVisible(cm);\n    }); },\n    openLine: function (cm) { return cm.replaceSelection(\"\\n\", \"start\"); },\n    toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }\n  };\n\n\n  function lineStart(cm, lineN) {\n    var line = getLine(cm.doc, lineN);\n    var visual = visualLine(line);\n    if (visual != line) { lineN = lineNo(visual); }\n    return endOfLine(true, cm, visual, lineN, 1)\n  }\n  function lineEnd(cm, lineN) {\n    var line = getLine(cm.doc, lineN);\n    var visual = visualLineEnd(line);\n    if (visual != line) { lineN = lineNo(visual); }\n    return endOfLine(true, cm, line, lineN, -1)\n  }\n  function lineStartSmart(cm, pos) {\n    var start = lineStart(cm, pos.line);\n    var line = getLine(cm.doc, start.line);\n    var order = getOrder(line, cm.doc.direction);\n    if (!order || order[0].level == 0) {\n      var firstNonWS = Math.max(start.ch, line.text.search(/\\S/));\n      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;\n      return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)\n    }\n    return start\n  }\n\n  // Run a handler that was bound to a key.\n  function doHandleBinding(cm, bound, dropShift) {\n    if (typeof bound == \"string\") {\n      bound = commands[bound];\n      if (!bound) { return false }\n    }\n    // Ensure previous input has been read, so that the handler sees a\n    // consistent view of the document\n    cm.display.input.ensurePolled();\n    var prevShift = cm.display.shift, done = false;\n    try {\n      if (cm.isReadOnly()) { cm.state.suppressEdits = true; }\n      if (dropShift) { cm.display.shift = false; }\n      done = bound(cm) != Pass;\n    } finally {\n      cm.display.shift = prevShift;\n      cm.state.suppressEdits = false;\n    }\n    return done\n  }\n\n  function lookupKeyForEditor(cm, name, handle) {\n    for (var i = 0; i < cm.state.keyMaps.length; i++) {\n      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);\n      if (result) { return result }\n    }\n    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))\n      || lookupKey(name, cm.options.keyMap, handle, cm)\n  }\n\n  // Note that, despite the name, this function is also used to check\n  // for bound mouse clicks.\n\n  var stopSeq = new Delayed;\n\n  function dispatchKey(cm, name, e, handle) {\n    var seq = cm.state.keySeq;\n    if (seq) {\n      if (isModifierKey(name)) { return \"handled\" }\n      if (/\\'$/.test(name))\n        { cm.state.keySeq = null; }\n      else\n        { stopSeq.set(50, function () {\n          if (cm.state.keySeq == seq) {\n            cm.state.keySeq = null;\n            cm.display.input.reset();\n          }\n        }); }\n      if (dispatchKeyInner(cm, seq + \" \" + name, e, handle)) { return true }\n    }\n    return dispatchKeyInner(cm, name, e, handle)\n  }\n\n  function dispatchKeyInner(cm, name, e, handle) {\n    var result = lookupKeyForEditor(cm, name, handle);\n\n    if (result == \"multi\")\n      { cm.state.keySeq = name; }\n    if (result == \"handled\")\n      { signalLater(cm, \"keyHandled\", cm, name, e); }\n\n    if (result == \"handled\" || result == \"multi\") {\n      e_preventDefault(e);\n      restartBlink(cm);\n    }\n\n    return !!result\n  }\n\n  // Handle a key from the keydown event.\n  function handleKeyBinding(cm, e) {\n    var name = keyName(e, true);\n    if (!name) { return false }\n\n    if (e.shiftKey && !cm.state.keySeq) {\n      // First try to resolve full name (including 'Shift-'). Failing\n      // that, see if there is a cursor-motion command (starting with\n      // 'go') bound to the keyname without 'Shift-'.\n      return dispatchKey(cm, \"Shift-\" + name, e, function (b) { return doHandleBinding(cm, b, true); })\n          || dispatchKey(cm, name, e, function (b) {\n               if (typeof b == \"string\" ? /^go[A-Z]/.test(b) : b.motion)\n                 { return doHandleBinding(cm, b) }\n             })\n    } else {\n      return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })\n    }\n  }\n\n  // Handle a key from the keypress event\n  function handleCharBinding(cm, e, ch) {\n    return dispatchKey(cm, \"'\" + ch + \"'\", e, function (b) { return doHandleBinding(cm, b, true); })\n  }\n\n  var lastStoppedKey = null;\n  function onKeyDown(e) {\n    var cm = this;\n    if (e.target && e.target != cm.display.input.getField()) { return }\n    cm.curOp.focus = activeElt(root(cm));\n    if (signalDOMEvent(cm, e)) { return }\n    // IE does strange things with escape.\n    if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }\n    var code = e.keyCode;\n    cm.display.shift = code == 16 || e.shiftKey;\n    var handled = handleKeyBinding(cm, e);\n    if (presto) {\n      lastStoppedKey = handled ? code : null;\n      // Opera has no cut event... we try to at least catch the key combo\n      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))\n        { cm.replaceSelection(\"\", null, \"cut\"); }\n    }\n    if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand)\n      { document.execCommand(\"cut\"); }\n\n    // Turn mouse into crosshair when Alt is held on Mac.\n    if (code == 18 && !/\\bCodeMirror-crosshair\\b/.test(cm.display.lineDiv.className))\n      { showCrossHair(cm); }\n  }\n\n  function showCrossHair(cm) {\n    var lineDiv = cm.display.lineDiv;\n    addClass(lineDiv, \"CodeMirror-crosshair\");\n\n    function up(e) {\n      if (e.keyCode == 18 || !e.altKey) {\n        rmClass(lineDiv, \"CodeMirror-crosshair\");\n        off(document, \"keyup\", up);\n        off(document, \"mouseover\", up);\n      }\n    }\n    on(document, \"keyup\", up);\n    on(document, \"mouseover\", up);\n  }\n\n  function onKeyUp(e) {\n    if (e.keyCode == 16) { this.doc.sel.shift = false; }\n    signalDOMEvent(this, e);\n  }\n\n  function onKeyPress(e) {\n    var cm = this;\n    if (e.target && e.target != cm.display.input.getField()) { return }\n    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }\n    var keyCode = e.keyCode, charCode = e.charCode;\n    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}\n    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }\n    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);\n    // Some browsers fire keypress events for backspace\n    if (ch == \"\\x08\") { return }\n    if (handleCharBinding(cm, e, ch)) { return }\n    cm.display.input.onKeyPress(e);\n  }\n\n  var DOUBLECLICK_DELAY = 400;\n\n  var PastClick = function(time, pos, button) {\n    this.time = time;\n    this.pos = pos;\n    this.button = button;\n  };\n\n  PastClick.prototype.compare = function (time, pos, button) {\n    return this.time + DOUBLECLICK_DELAY > time &&\n      cmp(pos, this.pos) == 0 && button == this.button\n  };\n\n  var lastClick, lastDoubleClick;\n  function clickRepeat(pos, button) {\n    var now = +new Date;\n    if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {\n      lastClick = lastDoubleClick = null;\n      return \"triple\"\n    } else if (lastClick && lastClick.compare(now, pos, button)) {\n      lastDoubleClick = new PastClick(now, pos, button);\n      lastClick = null;\n      return \"double\"\n    } else {\n      lastClick = new PastClick(now, pos, button);\n      lastDoubleClick = null;\n      return \"single\"\n    }\n  }\n\n  // A mouse down can be a single click, double click, triple click,\n  // start of selection drag, start of text drag, new cursor\n  // (ctrl-click), rectangle drag (alt-drag), or xwin\n  // middle-click-paste. Or it might be a click on something we should\n  // not interfere with, such as a scrollbar or widget.\n  function onMouseDown(e) {\n    var cm = this, display = cm.display;\n    if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }\n    display.input.ensurePolled();\n    display.shift = e.shiftKey;\n\n    if (eventInWidget(display, e)) {\n      if (!webkit) {\n        // Briefly turn off draggability, to allow widgets to do\n        // normal dragging things.\n        display.scroller.draggable = false;\n        setTimeout(function () { return display.scroller.draggable = true; }, 100);\n      }\n      return\n    }\n    if (clickInGutter(cm, e)) { return }\n    var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : \"single\";\n    win(cm).focus();\n\n    // #3261: make sure, that we're not starting a second selection\n    if (button == 1 && cm.state.selectingText)\n      { cm.state.selectingText(e); }\n\n    if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }\n\n    if (button == 1) {\n      if (pos) { leftButtonDown(cm, pos, repeat, e); }\n      else if (e_target(e) == display.scroller) { e_preventDefault(e); }\n    } else if (button == 2) {\n      if (pos) { extendSelection(cm.doc, pos); }\n      setTimeout(function () { return display.input.focus(); }, 20);\n    } else if (button == 3) {\n      if (captureRightClick) { cm.display.input.onContextMenu(e); }\n      else { delayBlurEvent(cm); }\n    }\n  }\n\n  function handleMappedButton(cm, button, pos, repeat, event) {\n    var name = \"Click\";\n    if (repeat == \"double\") { name = \"Double\" + name; }\n    else if (repeat == \"triple\") { name = \"Triple\" + name; }\n    name = (button == 1 ? \"Left\" : button == 2 ? \"Middle\" : \"Right\") + name;\n\n    return dispatchKey(cm,  addModifierNames(name, event), event, function (bound) {\n      if (typeof bound == \"string\") { bound = commands[bound]; }\n      if (!bound) { return false }\n      var done = false;\n      try {\n        if (cm.isReadOnly()) { cm.state.suppressEdits = true; }\n        done = bound(cm, pos) != Pass;\n      } finally {\n        cm.state.suppressEdits = false;\n      }\n      return done\n    })\n  }\n\n  function configureMouse(cm, repeat, event) {\n    var option = cm.getOption(\"configureMouse\");\n    var value = option ? option(cm, repeat, event) : {};\n    if (value.unit == null) {\n      var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;\n      value.unit = rect ? \"rectangle\" : repeat == \"single\" ? \"char\" : repeat == \"double\" ? \"word\" : \"line\";\n    }\n    if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }\n    if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }\n    if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }\n    return value\n  }\n\n  function leftButtonDown(cm, pos, repeat, event) {\n    if (ie) { setTimeout(bind(ensureFocus, cm), 0); }\n    else { cm.curOp.focus = activeElt(root(cm)); }\n\n    var behavior = configureMouse(cm, repeat, event);\n\n    var sel = cm.doc.sel, contained;\n    if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&\n        repeat == \"single\" && (contained = sel.contains(pos)) > -1 &&\n        (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&\n        (cmp(contained.to(), pos) > 0 || pos.xRel < 0))\n      { leftButtonStartDrag(cm, event, pos, behavior); }\n    else\n      { leftButtonSelect(cm, event, pos, behavior); }\n  }\n\n  // Start a text drag. When it ends, see if any dragging actually\n  // happen, and treat as a click if it didn't.\n  function leftButtonStartDrag(cm, event, pos, behavior) {\n    var display = cm.display, moved = false;\n    var dragEnd = operation(cm, function (e) {\n      if (webkit) { display.scroller.draggable = false; }\n      cm.state.draggingText = false;\n      if (cm.state.delayingBlurEvent) {\n        if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; }\n        else { delayBlurEvent(cm); }\n      }\n      off(display.wrapper.ownerDocument, \"mouseup\", dragEnd);\n      off(display.wrapper.ownerDocument, \"mousemove\", mouseMove);\n      off(display.scroller, \"dragstart\", dragStart);\n      off(display.scroller, \"drop\", dragEnd);\n      if (!moved) {\n        e_preventDefault(e);\n        if (!behavior.addNew)\n          { extendSelection(cm.doc, pos, null, null, behavior.extend); }\n        // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)\n        if ((webkit && !safari) || ie && ie_version == 9)\n          { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); }\n        else\n          { display.input.focus(); }\n      }\n    });\n    var mouseMove = function(e2) {\n      moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;\n    };\n    var dragStart = function () { return moved = true; };\n    // Let the drag handler handle this.\n    if (webkit) { display.scroller.draggable = true; }\n    cm.state.draggingText = dragEnd;\n    dragEnd.copy = !behavior.moveOnDrag;\n    on(display.wrapper.ownerDocument, \"mouseup\", dragEnd);\n    on(display.wrapper.ownerDocument, \"mousemove\", mouseMove);\n    on(display.scroller, \"dragstart\", dragStart);\n    on(display.scroller, \"drop\", dragEnd);\n\n    cm.state.delayingBlurEvent = true;\n    setTimeout(function () { return display.input.focus(); }, 20);\n    // IE's approach to draggable\n    if (display.scroller.dragDrop) { display.scroller.dragDrop(); }\n  }\n\n  function rangeForUnit(cm, pos, unit) {\n    if (unit == \"char\") { return new Range(pos, pos) }\n    if (unit == \"word\") { return cm.findWordAt(pos) }\n    if (unit == \"line\") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }\n    var result = unit(cm, pos);\n    return new Range(result.from, result.to)\n  }\n\n  // Normal selection, as opposed to text dragging.\n  function leftButtonSelect(cm, event, start, behavior) {\n    if (ie) { delayBlurEvent(cm); }\n    var display = cm.display, doc = cm.doc;\n    e_preventDefault(event);\n\n    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;\n    if (behavior.addNew && !behavior.extend) {\n      ourIndex = doc.sel.contains(start);\n      if (ourIndex > -1)\n        { ourRange = ranges[ourIndex]; }\n      else\n        { ourRange = new Range(start, start); }\n    } else {\n      ourRange = doc.sel.primary();\n      ourIndex = doc.sel.primIndex;\n    }\n\n    if (behavior.unit == \"rectangle\") {\n      if (!behavior.addNew) { ourRange = new Range(start, start); }\n      start = posFromMouse(cm, event, true, true);\n      ourIndex = -1;\n    } else {\n      var range = rangeForUnit(cm, start, behavior.unit);\n      if (behavior.extend)\n        { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); }\n      else\n        { ourRange = range; }\n    }\n\n    if (!behavior.addNew) {\n      ourIndex = 0;\n      setSelection(doc, new Selection([ourRange], 0), sel_mouse);\n      startSel = doc.sel;\n    } else if (ourIndex == -1) {\n      ourIndex = ranges.length;\n      setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),\n                   {scroll: false, origin: \"*mouse\"});\n    } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == \"char\" && !behavior.extend) {\n      setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),\n                   {scroll: false, origin: \"*mouse\"});\n      startSel = doc.sel;\n    } else {\n      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);\n    }\n\n    var lastPos = start;\n    function extendTo(pos) {\n      if (cmp(lastPos, pos) == 0) { return }\n      lastPos = pos;\n\n      if (behavior.unit == \"rectangle\") {\n        var ranges = [], tabSize = cm.options.tabSize;\n        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);\n        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);\n        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);\n        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));\n             line <= end; line++) {\n          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);\n          if (left == right)\n            { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }\n          else if (text.length > leftPos)\n            { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }\n        }\n        if (!ranges.length) { ranges.push(new Range(start, start)); }\n        setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),\n                     {origin: \"*mouse\", scroll: false});\n        cm.scrollIntoView(pos);\n      } else {\n        var oldRange = ourRange;\n        var range = rangeForUnit(cm, pos, behavior.unit);\n        var anchor = oldRange.anchor, head;\n        if (cmp(range.anchor, anchor) > 0) {\n          head = range.head;\n          anchor = minPos(oldRange.from(), range.anchor);\n        } else {\n          head = range.anchor;\n          anchor = maxPos(oldRange.to(), range.head);\n        }\n        var ranges$1 = startSel.ranges.slice(0);\n        ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));\n        setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);\n      }\n    }\n\n    var editorSize = display.wrapper.getBoundingClientRect();\n    // Used to ensure timeout re-tries don't fire when another extend\n    // happened in the meantime (clearTimeout isn't reliable -- at\n    // least on Chrome, the timeouts still happen even when cleared,\n    // if the clear happens after their scheduled firing time).\n    var counter = 0;\n\n    function extend(e) {\n      var curCount = ++counter;\n      var cur = posFromMouse(cm, e, true, behavior.unit == \"rectangle\");\n      if (!cur) { return }\n      if (cmp(cur, lastPos) != 0) {\n        cm.curOp.focus = activeElt(root(cm));\n        extendTo(cur);\n        var visible = visibleLines(display, doc);\n        if (cur.line >= visible.to || cur.line < visible.from)\n          { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }\n      } else {\n        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;\n        if (outside) { setTimeout(operation(cm, function () {\n          if (counter != curCount) { return }\n          display.scroller.scrollTop += outside;\n          extend(e);\n        }), 50); }\n      }\n    }\n\n    function done(e) {\n      cm.state.selectingText = false;\n      counter = Infinity;\n      // If e is null or undefined we interpret this as someone trying\n      // to explicitly cancel the selection rather than the user\n      // letting go of the mouse button.\n      if (e) {\n        e_preventDefault(e);\n        display.input.focus();\n      }\n      off(display.wrapper.ownerDocument, \"mousemove\", move);\n      off(display.wrapper.ownerDocument, \"mouseup\", up);\n      doc.history.lastSelOrigin = null;\n    }\n\n    var move = operation(cm, function (e) {\n      if (e.buttons === 0 || !e_button(e)) { done(e); }\n      else { extend(e); }\n    });\n    var up = operation(cm, done);\n    cm.state.selectingText = up;\n    on(display.wrapper.ownerDocument, \"mousemove\", move);\n    on(display.wrapper.ownerDocument, \"mouseup\", up);\n  }\n\n  // Used when mouse-selecting to adjust the anchor to the proper side\n  // of a bidi jump depending on the visual position of the head.\n  function bidiSimplify(cm, range) {\n    var anchor = range.anchor;\n    var head = range.head;\n    var anchorLine = getLine(cm.doc, anchor.line);\n    if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }\n    var order = getOrder(anchorLine);\n    if (!order) { return range }\n    var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];\n    if (part.from != anchor.ch && part.to != anchor.ch) { return range }\n    var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);\n    if (boundary == 0 || boundary == order.length) { return range }\n\n    // Compute the relative visual position of the head compared to the\n    // anchor (<0 is to the left, >0 to the right)\n    var leftSide;\n    if (head.line != anchor.line) {\n      leftSide = (head.line - anchor.line) * (cm.doc.direction == \"ltr\" ? 1 : -1) > 0;\n    } else {\n      var headIndex = getBidiPartAt(order, head.ch, head.sticky);\n      var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);\n      if (headIndex == boundary - 1 || headIndex == boundary)\n        { leftSide = dir < 0; }\n      else\n        { leftSide = dir > 0; }\n    }\n\n    var usePart = order[boundary + (leftSide ? -1 : 0)];\n    var from = leftSide == (usePart.level == 1);\n    var ch = from ? usePart.from : usePart.to, sticky = from ? \"after\" : \"before\";\n    return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)\n  }\n\n\n  // Determines whether an event happened in the gutter, and fires the\n  // handlers for the corresponding event.\n  function gutterEvent(cm, e, type, prevent) {\n    var mX, mY;\n    if (e.touches) {\n      mX = e.touches[0].clientX;\n      mY = e.touches[0].clientY;\n    } else {\n      try { mX = e.clientX; mY = e.clientY; }\n      catch(e$1) { return false }\n    }\n    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }\n    if (prevent) { e_preventDefault(e); }\n\n    var display = cm.display;\n    var lineBox = display.lineDiv.getBoundingClientRect();\n\n    if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }\n    mY -= lineBox.top - display.viewOffset;\n\n    for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {\n      var g = display.gutters.childNodes[i];\n      if (g && g.getBoundingClientRect().right >= mX) {\n        var line = lineAtHeight(cm.doc, mY);\n        var gutter = cm.display.gutterSpecs[i];\n        signal(cm, type, cm, line, gutter.className, e);\n        return e_defaultPrevented(e)\n      }\n    }\n  }\n\n  function clickInGutter(cm, e) {\n    return gutterEvent(cm, e, \"gutterClick\", true)\n  }\n\n  // CONTEXT MENU HANDLING\n\n  // To make the context menu work, we need to briefly unhide the\n  // textarea (making it as unobtrusive as possible) to let the\n  // right-click take effect on it.\n  function onContextMenu(cm, e) {\n    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }\n    if (signalDOMEvent(cm, e, \"contextmenu\")) { return }\n    if (!captureRightClick) { cm.display.input.onContextMenu(e); }\n  }\n\n  function contextMenuInGutter(cm, e) {\n    if (!hasHandler(cm, \"gutterContextMenu\")) { return false }\n    return gutterEvent(cm, e, \"gutterContextMenu\", false)\n  }\n\n  function themeChanged(cm) {\n    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\\s*cm-s-\\S+/g, \"\") +\n      cm.options.theme.replace(/(^|\\s)\\s*/g, \" cm-s-\");\n    clearCaches(cm);\n  }\n\n  var Init = {toString: function(){return \"CodeMirror.Init\"}};\n\n  var defaults = {};\n  var optionHandlers = {};\n\n  function defineOptions(CodeMirror) {\n    var optionHandlers = CodeMirror.optionHandlers;\n\n    function option(name, deflt, handle, notOnInit) {\n      CodeMirror.defaults[name] = deflt;\n      if (handle) { optionHandlers[name] =\n        notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }\n    }\n\n    CodeMirror.defineOption = option;\n\n    // Passed to option handlers when there is no old value.\n    CodeMirror.Init = Init;\n\n    // These two are, on init, called from the constructor because they\n    // have to be initialized before the editor can start at all.\n    option(\"value\", \"\", function (cm, val) { return cm.setValue(val); }, true);\n    option(\"mode\", null, function (cm, val) {\n      cm.doc.modeOption = val;\n      loadMode(cm);\n    }, true);\n\n    option(\"indentUnit\", 2, loadMode, true);\n    option(\"indentWithTabs\", false);\n    option(\"smartIndent\", true);\n    option(\"tabSize\", 4, function (cm) {\n      resetModeState(cm);\n      clearCaches(cm);\n      regChange(cm);\n    }, true);\n\n    option(\"lineSeparator\", null, function (cm, val) {\n      cm.doc.lineSep = val;\n      if (!val) { return }\n      var newBreaks = [], lineNo = cm.doc.first;\n      cm.doc.iter(function (line) {\n        for (var pos = 0;;) {\n          var found = line.text.indexOf(val, pos);\n          if (found == -1) { break }\n          pos = found + val.length;\n          newBreaks.push(Pos(lineNo, found));\n        }\n        lineNo++;\n      });\n      for (var i = newBreaks.length - 1; i >= 0; i--)\n        { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }\n    });\n    option(\"specialChars\", /[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u061c\\u200b\\u200e\\u200f\\u2028\\u2029\\u202d\\u202e\\u2066\\u2067\\u2069\\ufeff\\ufff9-\\ufffc]/g, function (cm, val, old) {\n      cm.state.specialChars = new RegExp(val.source + (val.test(\"\\t\") ? \"\" : \"|\\t\"), \"g\");\n      if (old != Init) { cm.refresh(); }\n    });\n    option(\"specialCharPlaceholder\", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);\n    option(\"electricChars\", true);\n    option(\"inputStyle\", mobile ? \"contenteditable\" : \"textarea\", function () {\n      throw new Error(\"inputStyle can not (yet) be changed in a running editor\") // FIXME\n    }, true);\n    option(\"spellcheck\", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);\n    option(\"autocorrect\", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);\n    option(\"autocapitalize\", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);\n    option(\"rtlMoveVisually\", !windows);\n    option(\"wholeLineUpdateBefore\", true);\n\n    option(\"theme\", \"default\", function (cm) {\n      themeChanged(cm);\n      updateGutters(cm);\n    }, true);\n    option(\"keyMap\", \"default\", function (cm, val, old) {\n      var next = getKeyMap(val);\n      var prev = old != Init && getKeyMap(old);\n      if (prev && prev.detach) { prev.detach(cm, next); }\n      if (next.attach) { next.attach(cm, prev || null); }\n    });\n    option(\"extraKeys\", null);\n    option(\"configureMouse\", null);\n\n    option(\"lineWrapping\", false, wrappingChanged, true);\n    option(\"gutters\", [], function (cm, val) {\n      cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);\n      updateGutters(cm);\n    }, true);\n    option(\"fixedGutter\", true, function (cm, val) {\n      cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + \"px\" : \"0\";\n      cm.refresh();\n    }, true);\n    option(\"coverGutterNextToScrollbar\", false, function (cm) { return updateScrollbars(cm); }, true);\n    option(\"scrollbarStyle\", \"native\", function (cm) {\n      initScrollbars(cm);\n      updateScrollbars(cm);\n      cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);\n      cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);\n    }, true);\n    option(\"lineNumbers\", false, function (cm, val) {\n      cm.display.gutterSpecs = getGutters(cm.options.gutters, val);\n      updateGutters(cm);\n    }, true);\n    option(\"firstLineNumber\", 1, updateGutters, true);\n    option(\"lineNumberFormatter\", function (integer) { return integer; }, updateGutters, true);\n    option(\"showCursorWhenSelecting\", false, updateSelection, true);\n\n    option(\"resetSelectionOnContextMenu\", true);\n    option(\"lineWiseCopyCut\", true);\n    option(\"pasteLinesPerSelection\", true);\n    option(\"selectionsMayTouch\", false);\n\n    option(\"readOnly\", false, function (cm, val) {\n      if (val == \"nocursor\") {\n        onBlur(cm);\n        cm.display.input.blur();\n      }\n      cm.display.input.readOnlyChanged(val);\n    });\n\n    option(\"screenReaderLabel\", null, function (cm, val) {\n      val = (val === '') ? null : val;\n      cm.display.input.screenReaderLabelChanged(val);\n    });\n\n    option(\"disableInput\", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);\n    option(\"dragDrop\", true, dragDropChanged);\n    option(\"allowDropFileTypes\", null);\n\n    option(\"cursorBlinkRate\", 530);\n    option(\"cursorScrollMargin\", 0);\n    option(\"cursorHeight\", 1, updateSelection, true);\n    option(\"singleCursorHeightPerLine\", true, updateSelection, true);\n    option(\"workTime\", 100);\n    option(\"workDelay\", 100);\n    option(\"flattenSpans\", true, resetModeState, true);\n    option(\"addModeClass\", false, resetModeState, true);\n    option(\"pollInterval\", 100);\n    option(\"undoDepth\", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });\n    option(\"historyEventDelay\", 1250);\n    option(\"viewportMargin\", 10, function (cm) { return cm.refresh(); }, true);\n    option(\"maxHighlightLength\", 10000, resetModeState, true);\n    option(\"moveInputWithCursor\", true, function (cm, val) {\n      if (!val) { cm.display.input.resetPosition(); }\n    });\n\n    option(\"tabindex\", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || \"\"; });\n    option(\"autofocus\", null);\n    option(\"direction\", \"ltr\", function (cm, val) { return cm.doc.setDirection(val); }, true);\n    option(\"phrases\", null);\n  }\n\n  function dragDropChanged(cm, value, old) {\n    var wasOn = old && old != Init;\n    if (!value != !wasOn) {\n      var funcs = cm.display.dragFunctions;\n      var toggle = value ? on : off;\n      toggle(cm.display.scroller, \"dragstart\", funcs.start);\n      toggle(cm.display.scroller, \"dragenter\", funcs.enter);\n      toggle(cm.display.scroller, \"dragover\", funcs.over);\n      toggle(cm.display.scroller, \"dragleave\", funcs.leave);\n      toggle(cm.display.scroller, \"drop\", funcs.drop);\n    }\n  }\n\n  function wrappingChanged(cm) {\n    if (cm.options.lineWrapping) {\n      addClass(cm.display.wrapper, \"CodeMirror-wrap\");\n      cm.display.sizer.style.minWidth = \"\";\n      cm.display.sizerWidth = null;\n    } else {\n      rmClass(cm.display.wrapper, \"CodeMirror-wrap\");\n      findMaxLine(cm);\n    }\n    estimateLineHeights(cm);\n    regChange(cm);\n    clearCaches(cm);\n    setTimeout(function () { return updateScrollbars(cm); }, 100);\n  }\n\n  // A CodeMirror instance represents an editor. This is the object\n  // that user code is usually dealing with.\n\n  function CodeMirror(place, options) {\n    var this$1 = this;\n\n    if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }\n\n    this.options = options = options ? copyObj(options) : {};\n    // Determine effective options based on given values and defaults.\n    copyObj(defaults, options, false);\n\n    var doc = options.value;\n    if (typeof doc == \"string\") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }\n    else if (options.mode) { doc.modeOption = options.mode; }\n    this.doc = doc;\n\n    var input = new CodeMirror.inputStyles[options.inputStyle](this);\n    var display = this.display = new Display(place, doc, input, options);\n    display.wrapper.CodeMirror = this;\n    themeChanged(this);\n    if (options.lineWrapping)\n      { this.display.wrapper.className += \" CodeMirror-wrap\"; }\n    initScrollbars(this);\n\n    this.state = {\n      keyMaps: [],  // stores maps added by addKeyMap\n      overlays: [], // highlighting overlays, as added by addOverlay\n      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info\n      overwrite: false,\n      delayingBlurEvent: false,\n      focused: false,\n      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode\n      pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll\n      selectingText: false,\n      draggingText: false,\n      highlight: new Delayed(), // stores highlight worker timeout\n      keySeq: null,  // Unfinished key sequence\n      specialChars: null\n    };\n\n    if (options.autofocus && !mobile) { display.input.focus(); }\n\n    // Override magic textarea content restore that IE sometimes does\n    // on our hidden textarea on reload\n    if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }\n\n    registerEventHandlers(this);\n    ensureGlobalHandlers();\n\n    startOperation(this);\n    this.curOp.forceUpdate = true;\n    attachDoc(this, doc);\n\n    if ((options.autofocus && !mobile) || this.hasFocus())\n      { setTimeout(function () {\n        if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); }\n      }, 20); }\n    else\n      { onBlur(this); }\n\n    for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))\n      { optionHandlers[opt](this, options[opt], Init); } }\n    maybeUpdateLineNumberWidth(this);\n    if (options.finishInit) { options.finishInit(this); }\n    for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); }\n    endOperation(this);\n    // Suppress optimizelegibility in Webkit, since it breaks text\n    // measuring on line wrapping boundaries.\n    if (webkit && options.lineWrapping &&\n        getComputedStyle(display.lineDiv).textRendering == \"optimizelegibility\")\n      { display.lineDiv.style.textRendering = \"auto\"; }\n  }\n\n  // The default configuration options.\n  CodeMirror.defaults = defaults;\n  // Functions to run when options are changed.\n  CodeMirror.optionHandlers = optionHandlers;\n\n  // Attach the necessary event handlers when initializing the editor\n  function registerEventHandlers(cm) {\n    var d = cm.display;\n    on(d.scroller, \"mousedown\", operation(cm, onMouseDown));\n    // Older IE's will not fire a second mousedown for a double click\n    if (ie && ie_version < 11)\n      { on(d.scroller, \"dblclick\", operation(cm, function (e) {\n        if (signalDOMEvent(cm, e)) { return }\n        var pos = posFromMouse(cm, e);\n        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }\n        e_preventDefault(e);\n        var word = cm.findWordAt(pos);\n        extendSelection(cm.doc, word.anchor, word.head);\n      })); }\n    else\n      { on(d.scroller, \"dblclick\", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }\n    // Some browsers fire contextmenu *after* opening the menu, at\n    // which point we can't mess with it anymore. Context menu is\n    // handled in onMouseDown for these browsers.\n    on(d.scroller, \"contextmenu\", function (e) { return onContextMenu(cm, e); });\n    on(d.input.getField(), \"contextmenu\", function (e) {\n      if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); }\n    });\n\n    // Used to suppress mouse event handling when a touch happens\n    var touchFinished, prevTouch = {end: 0};\n    function finishTouch() {\n      if (d.activeTouch) {\n        touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);\n        prevTouch = d.activeTouch;\n        prevTouch.end = +new Date;\n      }\n    }\n    function isMouseLikeTouchEvent(e) {\n      if (e.touches.length != 1) { return false }\n      var touch = e.touches[0];\n      return touch.radiusX <= 1 && touch.radiusY <= 1\n    }\n    function farAway(touch, other) {\n      if (other.left == null) { return true }\n      var dx = other.left - touch.left, dy = other.top - touch.top;\n      return dx * dx + dy * dy > 20 * 20\n    }\n    on(d.scroller, \"touchstart\", function (e) {\n      if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {\n        d.input.ensurePolled();\n        clearTimeout(touchFinished);\n        var now = +new Date;\n        d.activeTouch = {start: now, moved: false,\n                         prev: now - prevTouch.end <= 300 ? prevTouch : null};\n        if (e.touches.length == 1) {\n          d.activeTouch.left = e.touches[0].pageX;\n          d.activeTouch.top = e.touches[0].pageY;\n        }\n      }\n    });\n    on(d.scroller, \"touchmove\", function () {\n      if (d.activeTouch) { d.activeTouch.moved = true; }\n    });\n    on(d.scroller, \"touchend\", function (e) {\n      var touch = d.activeTouch;\n      if (touch && !eventInWidget(d, e) && touch.left != null &&\n          !touch.moved && new Date - touch.start < 300) {\n        var pos = cm.coordsChar(d.activeTouch, \"page\"), range;\n        if (!touch.prev || farAway(touch, touch.prev)) // Single tap\n          { range = new Range(pos, pos); }\n        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap\n          { range = cm.findWordAt(pos); }\n        else // Triple tap\n          { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }\n        cm.setSelection(range.anchor, range.head);\n        cm.focus();\n        e_preventDefault(e);\n      }\n      finishTouch();\n    });\n    on(d.scroller, \"touchcancel\", finishTouch);\n\n    // Sync scrolling between fake scrollbars and real scrollable\n    // area, ensure viewport is updated when scrolling.\n    on(d.scroller, \"scroll\", function () {\n      if (d.scroller.clientHeight) {\n        updateScrollTop(cm, d.scroller.scrollTop);\n        setScrollLeft(cm, d.scroller.scrollLeft, true);\n        signal(cm, \"scroll\", cm);\n      }\n    });\n\n    // Listen to wheel events in order to try and update the viewport on time.\n    on(d.scroller, \"mousewheel\", function (e) { return onScrollWheel(cm, e); });\n    on(d.scroller, \"DOMMouseScroll\", function (e) { return onScrollWheel(cm, e); });\n\n    // Prevent wrapper from ever scrolling\n    on(d.wrapper, \"scroll\", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });\n\n    d.dragFunctions = {\n      enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},\n      over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},\n      start: function (e) { return onDragStart(cm, e); },\n      drop: operation(cm, onDrop),\n      leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}\n    };\n\n    var inp = d.input.getField();\n    on(inp, \"keyup\", function (e) { return onKeyUp.call(cm, e); });\n    on(inp, \"keydown\", operation(cm, onKeyDown));\n    on(inp, \"keypress\", operation(cm, onKeyPress));\n    on(inp, \"focus\", function (e) { return onFocus(cm, e); });\n    on(inp, \"blur\", function (e) { return onBlur(cm, e); });\n  }\n\n  var initHooks = [];\n  CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };\n\n  // Indent the given line. The how parameter can be \"smart\",\n  // \"add\"/null, \"subtract\", or \"prev\". When aggressive is false\n  // (typically set to true for forced single-line indents), empty\n  // lines are not indented, and places where the mode returns Pass\n  // are left alone.\n  function indentLine(cm, n, how, aggressive) {\n    var doc = cm.doc, state;\n    if (how == null) { how = \"add\"; }\n    if (how == \"smart\") {\n      // Fall back to \"prev\" when the mode doesn't have an indentation\n      // method.\n      if (!doc.mode.indent) { how = \"prev\"; }\n      else { state = getContextBefore(cm, n).state; }\n    }\n\n    var tabSize = cm.options.tabSize;\n    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);\n    if (line.stateAfter) { line.stateAfter = null; }\n    var curSpaceString = line.text.match(/^\\s*/)[0], indentation;\n    if (!aggressive && !/\\S/.test(line.text)) {\n      indentation = 0;\n      how = \"not\";\n    } else if (how == \"smart\") {\n      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);\n      if (indentation == Pass || indentation > 150) {\n        if (!aggressive) { return }\n        how = \"prev\";\n      }\n    }\n    if (how == \"prev\") {\n      if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }\n      else { indentation = 0; }\n    } else if (how == \"add\") {\n      indentation = curSpace + cm.options.indentUnit;\n    } else if (how == \"subtract\") {\n      indentation = curSpace - cm.options.indentUnit;\n    } else if (typeof how == \"number\") {\n      indentation = curSpace + how;\n    }\n    indentation = Math.max(0, indentation);\n\n    var indentString = \"\", pos = 0;\n    if (cm.options.indentWithTabs)\n      { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += \"\\t\";} }\n    if (pos < indentation) { indentString += spaceStr(indentation - pos); }\n\n    if (indentString != curSpaceString) {\n      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), \"+input\");\n      line.stateAfter = null;\n      return true\n    } else {\n      // Ensure that, if the cursor was in the whitespace at the start\n      // of the line, it is moved to the end of that space.\n      for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {\n        var range = doc.sel.ranges[i$1];\n        if (range.head.line == n && range.head.ch < curSpaceString.length) {\n          var pos$1 = Pos(n, curSpaceString.length);\n          replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));\n          break\n        }\n      }\n    }\n  }\n\n  // This will be set to a {lineWise: bool, text: [string]} object, so\n  // that, when pasting, we know what kind of selections the copied\n  // text was made out of.\n  var lastCopied = null;\n\n  function setLastCopied(newLastCopied) {\n    lastCopied = newLastCopied;\n  }\n\n  function applyTextInput(cm, inserted, deleted, sel, origin) {\n    var doc = cm.doc;\n    cm.display.shift = false;\n    if (!sel) { sel = doc.sel; }\n\n    var recent = +new Date - 200;\n    var paste = origin == \"paste\" || cm.state.pasteIncoming > recent;\n    var textLines = splitLinesAuto(inserted), multiPaste = null;\n    // When pasting N lines into N selections, insert one line per selection\n    if (paste && sel.ranges.length > 1) {\n      if (lastCopied && lastCopied.text.join(\"\\n\") == inserted) {\n        if (sel.ranges.length % lastCopied.text.length == 0) {\n          multiPaste = [];\n          for (var i = 0; i < lastCopied.text.length; i++)\n            { multiPaste.push(doc.splitLines(lastCopied.text[i])); }\n        }\n      } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {\n        multiPaste = map(textLines, function (l) { return [l]; });\n      }\n    }\n\n    var updateInput = cm.curOp.updateInput;\n    // Normal behavior is to insert the new text into every selection\n    for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {\n      var range = sel.ranges[i$1];\n      var from = range.from(), to = range.to();\n      if (range.empty()) {\n        if (deleted && deleted > 0) // Handle deletion\n          { from = Pos(from.line, from.ch - deleted); }\n        else if (cm.state.overwrite && !paste) // Handle overwrite\n          { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }\n        else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join(\"\\n\") == textLines.join(\"\\n\"))\n          { from = to = Pos(from.line, 0); }\n      }\n      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,\n                         origin: origin || (paste ? \"paste\" : cm.state.cutIncoming > recent ? \"cut\" : \"+input\")};\n      makeChange(cm.doc, changeEvent);\n      signalLater(cm, \"inputRead\", cm, changeEvent);\n    }\n    if (inserted && !paste)\n      { triggerElectric(cm, inserted); }\n\n    ensureCursorVisible(cm);\n    if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }\n    cm.curOp.typing = true;\n    cm.state.pasteIncoming = cm.state.cutIncoming = -1;\n  }\n\n  function handlePaste(e, cm) {\n    var pasted = e.clipboardData && e.clipboardData.getData(\"Text\");\n    if (pasted) {\n      e.preventDefault();\n      if (!cm.isReadOnly() && !cm.options.disableInput && cm.hasFocus())\n        { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, \"paste\"); }); }\n      return true\n    }\n  }\n\n  function triggerElectric(cm, inserted) {\n    // When an 'electric' character is inserted, immediately trigger a reindent\n    if (!cm.options.electricChars || !cm.options.smartIndent) { return }\n    var sel = cm.doc.sel;\n\n    for (var i = sel.ranges.length - 1; i >= 0; i--) {\n      var range = sel.ranges[i];\n      if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }\n      var mode = cm.getModeAt(range.head);\n      var indented = false;\n      if (mode.electricChars) {\n        for (var j = 0; j < mode.electricChars.length; j++)\n          { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {\n            indented = indentLine(cm, range.head.line, \"smart\");\n            break\n          } }\n      } else if (mode.electricInput) {\n        if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))\n          { indented = indentLine(cm, range.head.line, \"smart\"); }\n      }\n      if (indented) { signalLater(cm, \"electricInput\", cm, range.head.line); }\n    }\n  }\n\n  function copyableRanges(cm) {\n    var text = [], ranges = [];\n    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {\n      var line = cm.doc.sel.ranges[i].head.line;\n      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};\n      ranges.push(lineRange);\n      text.push(cm.getRange(lineRange.anchor, lineRange.head));\n    }\n    return {text: text, ranges: ranges}\n  }\n\n  function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {\n    field.setAttribute(\"autocorrect\", autocorrect ? \"on\" : \"off\");\n    field.setAttribute(\"autocapitalize\", autocapitalize ? \"on\" : \"off\");\n    field.setAttribute(\"spellcheck\", !!spellcheck);\n  }\n\n  function hiddenTextarea() {\n    var te = elt(\"textarea\", null, null, \"position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; min-height: 1em; outline: none\");\n    var div = elt(\"div\", [te], null, \"overflow: hidden; position: relative; width: 3px; height: 0px;\");\n    // The textarea is kept positioned near the cursor to prevent the\n    // fact that it'll be scrolled into view on input from scrolling\n    // our fake cursor out of view. On webkit, when wrap=off, paste is\n    // very slow. So make the area wide instead.\n    if (webkit) { te.style.width = \"1000px\"; }\n    else { te.setAttribute(\"wrap\", \"off\"); }\n    // If border: 0; -- iOS fails to open keyboard (issue #1287)\n    if (ios) { te.style.border = \"1px solid black\"; }\n    return div\n  }\n\n  // The publicly visible API. Note that methodOp(f) means\n  // 'wrap f in an operation, performed on its `this` parameter'.\n\n  // This is not the complete set of editor methods. Most of the\n  // methods defined on the Doc type are also injected into\n  // CodeMirror.prototype, for backwards compatibility and\n  // convenience.\n\n  function addEditorMethods(CodeMirror) {\n    var optionHandlers = CodeMirror.optionHandlers;\n\n    var helpers = CodeMirror.helpers = {};\n\n    CodeMirror.prototype = {\n      constructor: CodeMirror,\n      focus: function(){win(this).focus(); this.display.input.focus();},\n\n      setOption: function(option, value) {\n        var options = this.options, old = options[option];\n        if (options[option] == value && option != \"mode\") { return }\n        options[option] = value;\n        if (optionHandlers.hasOwnProperty(option))\n          { operation(this, optionHandlers[option])(this, value, old); }\n        signal(this, \"optionChange\", this, option);\n      },\n\n      getOption: function(option) {return this.options[option]},\n      getDoc: function() {return this.doc},\n\n      addKeyMap: function(map, bottom) {\n        this.state.keyMaps[bottom ? \"push\" : \"unshift\"](getKeyMap(map));\n      },\n      removeKeyMap: function(map) {\n        var maps = this.state.keyMaps;\n        for (var i = 0; i < maps.length; ++i)\n          { if (maps[i] == map || maps[i].name == map) {\n            maps.splice(i, 1);\n            return true\n          } }\n      },\n\n      addOverlay: methodOp(function(spec, options) {\n        var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);\n        if (mode.startState) { throw new Error(\"Overlays may not be stateful.\") }\n        insertSorted(this.state.overlays,\n                     {mode: mode, modeSpec: spec, opaque: options && options.opaque,\n                      priority: (options && options.priority) || 0},\n                     function (overlay) { return overlay.priority; });\n        this.state.modeGen++;\n        regChange(this);\n      }),\n      removeOverlay: methodOp(function(spec) {\n        var overlays = this.state.overlays;\n        for (var i = 0; i < overlays.length; ++i) {\n          var cur = overlays[i].modeSpec;\n          if (cur == spec || typeof spec == \"string\" && cur.name == spec) {\n            overlays.splice(i, 1);\n            this.state.modeGen++;\n            regChange(this);\n            return\n          }\n        }\n      }),\n\n      indentLine: methodOp(function(n, dir, aggressive) {\n        if (typeof dir != \"string\" && typeof dir != \"number\") {\n          if (dir == null) { dir = this.options.smartIndent ? \"smart\" : \"prev\"; }\n          else { dir = dir ? \"add\" : \"subtract\"; }\n        }\n        if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }\n      }),\n      indentSelection: methodOp(function(how) {\n        var ranges = this.doc.sel.ranges, end = -1;\n        for (var i = 0; i < ranges.length; i++) {\n          var range = ranges[i];\n          if (!range.empty()) {\n            var from = range.from(), to = range.to();\n            var start = Math.max(end, from.line);\n            end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;\n            for (var j = start; j < end; ++j)\n              { indentLine(this, j, how); }\n            var newRanges = this.doc.sel.ranges;\n            if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)\n              { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }\n          } else if (range.head.line > end) {\n            indentLine(this, range.head.line, how, true);\n            end = range.head.line;\n            if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); }\n          }\n        }\n      }),\n\n      // Fetch the parser token for a given character. Useful for hacks\n      // that want to inspect the mode state (say, for completion).\n      getTokenAt: function(pos, precise) {\n        return takeToken(this, pos, precise)\n      },\n\n      getLineTokens: function(line, precise) {\n        return takeToken(this, Pos(line), precise, true)\n      },\n\n      getTokenTypeAt: function(pos) {\n        pos = clipPos(this.doc, pos);\n        var styles = getLineStyles(this, getLine(this.doc, pos.line));\n        var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;\n        var type;\n        if (ch == 0) { type = styles[2]; }\n        else { for (;;) {\n          var mid = (before + after) >> 1;\n          if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }\n          else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }\n          else { type = styles[mid * 2 + 2]; break }\n        } }\n        var cut = type ? type.indexOf(\"overlay \") : -1;\n        return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)\n      },\n\n      getModeAt: function(pos) {\n        var mode = this.doc.mode;\n        if (!mode.innerMode) { return mode }\n        return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode\n      },\n\n      getHelper: function(pos, type) {\n        return this.getHelpers(pos, type)[0]\n      },\n\n      getHelpers: function(pos, type) {\n        var found = [];\n        if (!helpers.hasOwnProperty(type)) { return found }\n        var help = helpers[type], mode = this.getModeAt(pos);\n        if (typeof mode[type] == \"string\") {\n          if (help[mode[type]]) { found.push(help[mode[type]]); }\n        } else if (mode[type]) {\n          for (var i = 0; i < mode[type].length; i++) {\n            var val = help[mode[type][i]];\n            if (val) { found.push(val); }\n          }\n        } else if (mode.helperType && help[mode.helperType]) {\n          found.push(help[mode.helperType]);\n        } else if (help[mode.name]) {\n          found.push(help[mode.name]);\n        }\n        for (var i$1 = 0; i$1 < help._global.length; i$1++) {\n          var cur = help._global[i$1];\n          if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)\n            { found.push(cur.val); }\n        }\n        return found\n      },\n\n      getStateAfter: function(line, precise) {\n        var doc = this.doc;\n        line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);\n        return getContextBefore(this, line + 1, precise).state\n      },\n\n      cursorCoords: function(start, mode) {\n        var pos, range = this.doc.sel.primary();\n        if (start == null) { pos = range.head; }\n        else if (typeof start == \"object\") { pos = clipPos(this.doc, start); }\n        else { pos = start ? range.from() : range.to(); }\n        return cursorCoords(this, pos, mode || \"page\")\n      },\n\n      charCoords: function(pos, mode) {\n        return charCoords(this, clipPos(this.doc, pos), mode || \"page\")\n      },\n\n      coordsChar: function(coords, mode) {\n        coords = fromCoordSystem(this, coords, mode || \"page\");\n        return coordsChar(this, coords.left, coords.top)\n      },\n\n      lineAtHeight: function(height, mode) {\n        height = fromCoordSystem(this, {top: height, left: 0}, mode || \"page\").top;\n        return lineAtHeight(this.doc, height + this.display.viewOffset)\n      },\n      heightAtLine: function(line, mode, includeWidgets) {\n        var end = false, lineObj;\n        if (typeof line == \"number\") {\n          var last = this.doc.first + this.doc.size - 1;\n          if (line < this.doc.first) { line = this.doc.first; }\n          else if (line > last) { line = last; end = true; }\n          lineObj = getLine(this.doc, line);\n        } else {\n          lineObj = line;\n        }\n        return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || \"page\", includeWidgets || end).top +\n          (end ? this.doc.height - heightAtLine(lineObj) : 0)\n      },\n\n      defaultTextHeight: function() { return textHeight(this.display) },\n      defaultCharWidth: function() { return charWidth(this.display) },\n\n      getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},\n\n      addWidget: function(pos, node, scroll, vert, horiz) {\n        var display = this.display;\n        pos = cursorCoords(this, clipPos(this.doc, pos));\n        var top = pos.bottom, left = pos.left;\n        node.style.position = \"absolute\";\n        node.setAttribute(\"cm-ignore-events\", \"true\");\n        this.display.input.setUneditable(node);\n        display.sizer.appendChild(node);\n        if (vert == \"over\") {\n          top = pos.top;\n        } else if (vert == \"above\" || vert == \"near\") {\n          var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),\n          hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);\n          // Default to positioning above (if specified and possible); otherwise default to positioning below\n          if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)\n            { top = pos.top - node.offsetHeight; }\n          else if (pos.bottom + node.offsetHeight <= vspace)\n            { top = pos.bottom; }\n          if (left + node.offsetWidth > hspace)\n            { left = hspace - node.offsetWidth; }\n        }\n        node.style.top = top + \"px\";\n        node.style.left = node.style.right = \"\";\n        if (horiz == \"right\") {\n          left = display.sizer.clientWidth - node.offsetWidth;\n          node.style.right = \"0px\";\n        } else {\n          if (horiz == \"left\") { left = 0; }\n          else if (horiz == \"middle\") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }\n          node.style.left = left + \"px\";\n        }\n        if (scroll)\n          { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }\n      },\n\n      triggerOnKeyDown: methodOp(onKeyDown),\n      triggerOnKeyPress: methodOp(onKeyPress),\n      triggerOnKeyUp: onKeyUp,\n      triggerOnMouseDown: methodOp(onMouseDown),\n\n      execCommand: function(cmd) {\n        if (commands.hasOwnProperty(cmd))\n          { return commands[cmd].call(null, this) }\n      },\n\n      triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),\n\n      findPosH: function(from, amount, unit, visually) {\n        var dir = 1;\n        if (amount < 0) { dir = -1; amount = -amount; }\n        var cur = clipPos(this.doc, from);\n        for (var i = 0; i < amount; ++i) {\n          cur = findPosH(this.doc, cur, dir, unit, visually);\n          if (cur.hitSide) { break }\n        }\n        return cur\n      },\n\n      moveH: methodOp(function(dir, unit) {\n        var this$1 = this;\n\n        this.extendSelectionsBy(function (range) {\n          if (this$1.display.shift || this$1.doc.extend || range.empty())\n            { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }\n          else\n            { return dir < 0 ? range.from() : range.to() }\n        }, sel_move);\n      }),\n\n      deleteH: methodOp(function(dir, unit) {\n        var sel = this.doc.sel, doc = this.doc;\n        if (sel.somethingSelected())\n          { doc.replaceSelection(\"\", null, \"+delete\"); }\n        else\n          { deleteNearSelection(this, function (range) {\n            var other = findPosH(doc, range.head, dir, unit, false);\n            return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}\n          }); }\n      }),\n\n      findPosV: function(from, amount, unit, goalColumn) {\n        var dir = 1, x = goalColumn;\n        if (amount < 0) { dir = -1; amount = -amount; }\n        var cur = clipPos(this.doc, from);\n        for (var i = 0; i < amount; ++i) {\n          var coords = cursorCoords(this, cur, \"div\");\n          if (x == null) { x = coords.left; }\n          else { coords.left = x; }\n          cur = findPosV(this, coords, dir, unit);\n          if (cur.hitSide) { break }\n        }\n        return cur\n      },\n\n      moveV: methodOp(function(dir, unit) {\n        var this$1 = this;\n\n        var doc = this.doc, goals = [];\n        var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();\n        doc.extendSelectionsBy(function (range) {\n          if (collapse)\n            { return dir < 0 ? range.from() : range.to() }\n          var headPos = cursorCoords(this$1, range.head, \"div\");\n          if (range.goalColumn != null) { headPos.left = range.goalColumn; }\n          goals.push(headPos.left);\n          var pos = findPosV(this$1, headPos, dir, unit);\n          if (unit == \"page\" && range == doc.sel.primary())\n            { addToScrollTop(this$1, charCoords(this$1, pos, \"div\").top - headPos.top); }\n          return pos\n        }, sel_move);\n        if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)\n          { doc.sel.ranges[i].goalColumn = goals[i]; } }\n      }),\n\n      // Find the word at the given position (as returned by coordsChar).\n      findWordAt: function(pos) {\n        var doc = this.doc, line = getLine(doc, pos.line).text;\n        var start = pos.ch, end = pos.ch;\n        if (line) {\n          var helper = this.getHelper(pos, \"wordChars\");\n          if ((pos.sticky == \"before\" || end == line.length) && start) { --start; } else { ++end; }\n          var startChar = line.charAt(start);\n          var check = isWordChar(startChar, helper)\n            ? function (ch) { return isWordChar(ch, helper); }\n            : /\\s/.test(startChar) ? function (ch) { return /\\s/.test(ch); }\n            : function (ch) { return (!/\\s/.test(ch) && !isWordChar(ch)); };\n          while (start > 0 && check(line.charAt(start - 1))) { --start; }\n          while (end < line.length && check(line.charAt(end))) { ++end; }\n        }\n        return new Range(Pos(pos.line, start), Pos(pos.line, end))\n      },\n\n      toggleOverwrite: function(value) {\n        if (value != null && value == this.state.overwrite) { return }\n        if (this.state.overwrite = !this.state.overwrite)\n          { addClass(this.display.cursorDiv, \"CodeMirror-overwrite\"); }\n        else\n          { rmClass(this.display.cursorDiv, \"CodeMirror-overwrite\"); }\n\n        signal(this, \"overwriteToggle\", this, this.state.overwrite);\n      },\n      hasFocus: function() { return this.display.input.getField() == activeElt(root(this)) },\n      isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },\n\n      scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),\n      getScrollInfo: function() {\n        var scroller = this.display.scroller;\n        return {left: scroller.scrollLeft, top: scroller.scrollTop,\n                height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,\n                width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,\n                clientHeight: displayHeight(this), clientWidth: displayWidth(this)}\n      },\n\n      scrollIntoView: methodOp(function(range, margin) {\n        if (range == null) {\n          range = {from: this.doc.sel.primary().head, to: null};\n          if (margin == null) { margin = this.options.cursorScrollMargin; }\n        } else if (typeof range == \"number\") {\n          range = {from: Pos(range, 0), to: null};\n        } else if (range.from == null) {\n          range = {from: range, to: null};\n        }\n        if (!range.to) { range.to = range.from; }\n        range.margin = margin || 0;\n\n        if (range.from.line != null) {\n          scrollToRange(this, range);\n        } else {\n          scrollToCoordsRange(this, range.from, range.to, range.margin);\n        }\n      }),\n\n      setSize: methodOp(function(width, height) {\n        var this$1 = this;\n\n        var interpret = function (val) { return typeof val == \"number\" || /^\\d+$/.test(String(val)) ? val + \"px\" : val; };\n        if (width != null) { this.display.wrapper.style.width = interpret(width); }\n        if (height != null) { this.display.wrapper.style.height = interpret(height); }\n        if (this.options.lineWrapping) { clearLineMeasurementCache(this); }\n        var lineNo = this.display.viewFrom;\n        this.doc.iter(lineNo, this.display.viewTo, function (line) {\n          if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)\n            { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, \"widget\"); break } } }\n          ++lineNo;\n        });\n        this.curOp.forceUpdate = true;\n        signal(this, \"refresh\", this);\n      }),\n\n      operation: function(f){return runInOp(this, f)},\n      startOperation: function(){return startOperation(this)},\n      endOperation: function(){return endOperation(this)},\n\n      refresh: methodOp(function() {\n        var oldHeight = this.display.cachedTextHeight;\n        regChange(this);\n        this.curOp.forceUpdate = true;\n        clearCaches(this);\n        scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);\n        updateGutterSpace(this.display);\n        if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping)\n          { estimateLineHeights(this); }\n        signal(this, \"refresh\", this);\n      }),\n\n      swapDoc: methodOp(function(doc) {\n        var old = this.doc;\n        old.cm = null;\n        // Cancel the current text selection if any (#5821)\n        if (this.state.selectingText) { this.state.selectingText(); }\n        attachDoc(this, doc);\n        clearCaches(this);\n        this.display.input.reset();\n        scrollToCoords(this, doc.scrollLeft, doc.scrollTop);\n        this.curOp.forceScroll = true;\n        signalLater(this, \"swapDoc\", this, old);\n        return old\n      }),\n\n      phrase: function(phraseText) {\n        var phrases = this.options.phrases;\n        return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText\n      },\n\n      getInputField: function(){return this.display.input.getField()},\n      getWrapperElement: function(){return this.display.wrapper},\n      getScrollerElement: function(){return this.display.scroller},\n      getGutterElement: function(){return this.display.gutters}\n    };\n    eventMixin(CodeMirror);\n\n    CodeMirror.registerHelper = function(type, name, value) {\n      if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }\n      helpers[type][name] = value;\n    };\n    CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {\n      CodeMirror.registerHelper(type, name, value);\n      helpers[type]._global.push({pred: predicate, val: value});\n    };\n  }\n\n  // Used for horizontal relative motion. Dir is -1 or 1 (left or\n  // right), unit can be \"codepoint\", \"char\", \"column\" (like char, but\n  // doesn't cross line boundaries), \"word\" (across next word), or\n  // \"group\" (to the start of next group of word or\n  // non-word-non-whitespace chars). The visually param controls\n  // whether, in right-to-left text, direction 1 means to move towards\n  // the next index in the string, or towards the character to the right\n  // of the current position. The resulting position will have a\n  // hitSide=true property if it reached the end of the document.\n  function findPosH(doc, pos, dir, unit, visually) {\n    var oldPos = pos;\n    var origDir = dir;\n    var lineObj = getLine(doc, pos.line);\n    var lineDir = visually && doc.direction == \"rtl\" ? -dir : dir;\n    function findNextLine() {\n      var l = pos.line + lineDir;\n      if (l < doc.first || l >= doc.first + doc.size) { return false }\n      pos = new Pos(l, pos.ch, pos.sticky);\n      return lineObj = getLine(doc, l)\n    }\n    function moveOnce(boundToLine) {\n      var next;\n      if (unit == \"codepoint\") {\n        var ch = lineObj.text.charCodeAt(pos.ch + (dir > 0 ? 0 : -1));\n        if (isNaN(ch)) {\n          next = null;\n        } else {\n          var astral = dir > 0 ? ch >= 0xD800 && ch < 0xDC00 : ch >= 0xDC00 && ch < 0xDFFF;\n          next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (astral ? 2 : 1))), -dir);\n        }\n      } else if (visually) {\n        next = moveVisually(doc.cm, lineObj, pos, dir);\n      } else {\n        next = moveLogically(lineObj, pos, dir);\n      }\n      if (next == null) {\n        if (!boundToLine && findNextLine())\n          { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); }\n        else\n          { return false }\n      } else {\n        pos = next;\n      }\n      return true\n    }\n\n    if (unit == \"char\" || unit == \"codepoint\") {\n      moveOnce();\n    } else if (unit == \"column\") {\n      moveOnce(true);\n    } else if (unit == \"word\" || unit == \"group\") {\n      var sawType = null, group = unit == \"group\";\n      var helper = doc.cm && doc.cm.getHelper(pos, \"wordChars\");\n      for (var first = true;; first = false) {\n        if (dir < 0 && !moveOnce(!first)) { break }\n        var cur = lineObj.text.charAt(pos.ch) || \"\\n\";\n        var type = isWordChar(cur, helper) ? \"w\"\n          : group && cur == \"\\n\" ? \"n\"\n          : !group || /\\s/.test(cur) ? null\n          : \"p\";\n        if (group && !first && !type) { type = \"s\"; }\n        if (sawType && sawType != type) {\n          if (dir < 0) {dir = 1; moveOnce(); pos.sticky = \"after\";}\n          break\n        }\n\n        if (type) { sawType = type; }\n        if (dir > 0 && !moveOnce(!first)) { break }\n      }\n    }\n    var result = skipAtomic(doc, pos, oldPos, origDir, true);\n    if (equalCursorPos(oldPos, result)) { result.hitSide = true; }\n    return result\n  }\n\n  // For relative vertical movement. Dir may be -1 or 1. Unit can be\n  // \"page\" or \"line\". The resulting position will have a hitSide=true\n  // property if it reached the end of the document.\n  function findPosV(cm, pos, dir, unit) {\n    var doc = cm.doc, x = pos.left, y;\n    if (unit == \"page\") {\n      var pageSize = Math.min(cm.display.wrapper.clientHeight, win(cm).innerHeight || doc(cm).documentElement.clientHeight);\n      var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);\n      y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;\n\n    } else if (unit == \"line\") {\n      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;\n    }\n    var target;\n    for (;;) {\n      target = coordsChar(cm, x, y);\n      if (!target.outside) { break }\n      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }\n      y += dir * 5;\n    }\n    return target\n  }\n\n  // CONTENTEDITABLE INPUT STYLE\n\n  var ContentEditableInput = function(cm) {\n    this.cm = cm;\n    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;\n    this.polling = new Delayed();\n    this.composing = null;\n    this.gracePeriod = false;\n    this.readDOMTimeout = null;\n  };\n\n  ContentEditableInput.prototype.init = function (display) {\n      var this$1 = this;\n\n    var input = this, cm = input.cm;\n    var div = input.div = display.lineDiv;\n    div.contentEditable = true;\n    disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);\n\n    function belongsToInput(e) {\n      for (var t = e.target; t; t = t.parentNode) {\n        if (t == div) { return true }\n        if (/\\bCodeMirror-(?:line)?widget\\b/.test(t.className)) { break }\n      }\n      return false\n    }\n\n    on(div, \"paste\", function (e) {\n      if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }\n      // IE doesn't fire input events, so we schedule a read for the pasted content in this way\n      if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }\n    });\n\n    on(div, \"compositionstart\", function (e) {\n      this$1.composing = {data: e.data, done: false};\n    });\n    on(div, \"compositionupdate\", function (e) {\n      if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }\n    });\n    on(div, \"compositionend\", function (e) {\n      if (this$1.composing) {\n        if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }\n        this$1.composing.done = true;\n      }\n    });\n\n    on(div, \"touchstart\", function () { return input.forceCompositionEnd(); });\n\n    on(div, \"input\", function () {\n      if (!this$1.composing) { this$1.readFromDOMSoon(); }\n    });\n\n    function onCopyCut(e) {\n      if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return }\n      if (cm.somethingSelected()) {\n        setLastCopied({lineWise: false, text: cm.getSelections()});\n        if (e.type == \"cut\") { cm.replaceSelection(\"\", null, \"cut\"); }\n      } else if (!cm.options.lineWiseCopyCut) {\n        return\n      } else {\n        var ranges = copyableRanges(cm);\n        setLastCopied({lineWise: true, text: ranges.text});\n        if (e.type == \"cut\") {\n          cm.operation(function () {\n            cm.setSelections(ranges.ranges, 0, sel_dontScroll);\n            cm.replaceSelection(\"\", null, \"cut\");\n          });\n        }\n      }\n      if (e.clipboardData) {\n        e.clipboardData.clearData();\n        var content = lastCopied.text.join(\"\\n\");\n        // iOS exposes the clipboard API, but seems to discard content inserted into it\n        e.clipboardData.setData(\"Text\", content);\n        if (e.clipboardData.getData(\"Text\") == content) {\n          e.preventDefault();\n          return\n        }\n      }\n      // Old-fashioned briefly-focus-a-textarea hack\n      var kludge = hiddenTextarea(), te = kludge.firstChild;\n      disableBrowserMagic(te);\n      cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);\n      te.value = lastCopied.text.join(\"\\n\");\n      var hadFocus = activeElt(rootNode(div));\n      selectInput(te);\n      setTimeout(function () {\n        cm.display.lineSpace.removeChild(kludge);\n        hadFocus.focus();\n        if (hadFocus == div) { input.showPrimarySelection(); }\n      }, 50);\n    }\n    on(div, \"copy\", onCopyCut);\n    on(div, \"cut\", onCopyCut);\n  };\n\n  ContentEditableInput.prototype.screenReaderLabelChanged = function (label) {\n    // Label for screenreaders, accessibility\n    if(label) {\n      this.div.setAttribute('aria-label', label);\n    } else {\n      this.div.removeAttribute('aria-label');\n    }\n  };\n\n  ContentEditableInput.prototype.prepareSelection = function () {\n    var result = prepareSelection(this.cm, false);\n    result.focus = activeElt(rootNode(this.div)) == this.div;\n    return result\n  };\n\n  ContentEditableInput.prototype.showSelection = function (info, takeFocus) {\n    if (!info || !this.cm.display.view.length) { return }\n    if (info.focus || takeFocus) { this.showPrimarySelection(); }\n    this.showMultipleSelections(info);\n  };\n\n  ContentEditableInput.prototype.getSelection = function () {\n    return this.cm.display.wrapper.ownerDocument.getSelection()\n  };\n\n  ContentEditableInput.prototype.showPrimarySelection = function () {\n    var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();\n    var from = prim.from(), to = prim.to();\n\n    if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {\n      sel.removeAllRanges();\n      return\n    }\n\n    var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);\n    var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);\n    if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&\n        cmp(minPos(curAnchor, curFocus), from) == 0 &&\n        cmp(maxPos(curAnchor, curFocus), to) == 0)\n      { return }\n\n    var view = cm.display.view;\n    var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||\n        {node: view[0].measure.map[2], offset: 0};\n    var end = to.line < cm.display.viewTo && posToDOM(cm, to);\n    if (!end) {\n      var measure = view[view.length - 1].measure;\n      var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;\n      end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};\n    }\n\n    if (!start || !end) {\n      sel.removeAllRanges();\n      return\n    }\n\n    var old = sel.rangeCount && sel.getRangeAt(0), rng;\n    try { rng = range(start.node, start.offset, end.offset, end.node); }\n    catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible\n    if (rng) {\n      if (!gecko && cm.state.focused) {\n        sel.collapse(start.node, start.offset);\n        if (!rng.collapsed) {\n          sel.removeAllRanges();\n          sel.addRange(rng);\n        }\n      } else {\n        sel.removeAllRanges();\n        sel.addRange(rng);\n      }\n      if (old && sel.anchorNode == null) { sel.addRange(old); }\n      else if (gecko) { this.startGracePeriod(); }\n    }\n    this.rememberSelection();\n  };\n\n  ContentEditableInput.prototype.startGracePeriod = function () {\n      var this$1 = this;\n\n    clearTimeout(this.gracePeriod);\n    this.gracePeriod = setTimeout(function () {\n      this$1.gracePeriod = false;\n      if (this$1.selectionChanged())\n        { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }\n    }, 20);\n  };\n\n  ContentEditableInput.prototype.showMultipleSelections = function (info) {\n    removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);\n    removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);\n  };\n\n  ContentEditableInput.prototype.rememberSelection = function () {\n    var sel = this.getSelection();\n    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;\n    this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;\n  };\n\n  ContentEditableInput.prototype.selectionInEditor = function () {\n    var sel = this.getSelection();\n    if (!sel.rangeCount) { return false }\n    var node = sel.getRangeAt(0).commonAncestorContainer;\n    return contains(this.div, node)\n  };\n\n  ContentEditableInput.prototype.focus = function () {\n    if (this.cm.options.readOnly != \"nocursor\") {\n      if (!this.selectionInEditor() || activeElt(rootNode(this.div)) != this.div)\n        { this.showSelection(this.prepareSelection(), true); }\n      this.div.focus();\n    }\n  };\n  ContentEditableInput.prototype.blur = function () { this.div.blur(); };\n  ContentEditableInput.prototype.getField = function () { return this.div };\n\n  ContentEditableInput.prototype.supportsTouch = function () { return true };\n\n  ContentEditableInput.prototype.receivedFocus = function () {\n      var this$1 = this;\n\n    var input = this;\n    if (this.selectionInEditor())\n      { setTimeout(function () { return this$1.pollSelection(); }, 20); }\n    else\n      { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }\n\n    function poll() {\n      if (input.cm.state.focused) {\n        input.pollSelection();\n        input.polling.set(input.cm.options.pollInterval, poll);\n      }\n    }\n    this.polling.set(this.cm.options.pollInterval, poll);\n  };\n\n  ContentEditableInput.prototype.selectionChanged = function () {\n    var sel = this.getSelection();\n    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||\n      sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset\n  };\n\n  ContentEditableInput.prototype.pollSelection = function () {\n    if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }\n    var sel = this.getSelection(), cm = this.cm;\n    // On Android Chrome (version 56, at least), backspacing into an\n    // uneditable block element will put the cursor in that element,\n    // and then, because it's not editable, hide the virtual keyboard.\n    // Because Android doesn't allow us to actually detect backspace\n    // presses in a sane way, this code checks for when that happens\n    // and simulates a backspace press in this case.\n    if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {\n      this.cm.triggerOnKeyDown({type: \"keydown\", keyCode: 8, preventDefault: Math.abs});\n      this.blur();\n      this.focus();\n      return\n    }\n    if (this.composing) { return }\n    this.rememberSelection();\n    var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);\n    var head = domToPos(cm, sel.focusNode, sel.focusOffset);\n    if (anchor && head) { runInOp(cm, function () {\n      setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);\n      if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }\n    }); }\n  };\n\n  ContentEditableInput.prototype.pollContent = function () {\n    if (this.readDOMTimeout != null) {\n      clearTimeout(this.readDOMTimeout);\n      this.readDOMTimeout = null;\n    }\n\n    var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();\n    var from = sel.from(), to = sel.to();\n    if (from.ch == 0 && from.line > cm.firstLine())\n      { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }\n    if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())\n      { to = Pos(to.line + 1, 0); }\n    if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }\n\n    var fromIndex, fromLine, fromNode;\n    if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {\n      fromLine = lineNo(display.view[0].line);\n      fromNode = display.view[0].node;\n    } else {\n      fromLine = lineNo(display.view[fromIndex].line);\n      fromNode = display.view[fromIndex - 1].node.nextSibling;\n    }\n    var toIndex = findViewIndex(cm, to.line);\n    var toLine, toNode;\n    if (toIndex == display.view.length - 1) {\n      toLine = display.viewTo - 1;\n      toNode = display.lineDiv.lastChild;\n    } else {\n      toLine = lineNo(display.view[toIndex + 1].line) - 1;\n      toNode = display.view[toIndex + 1].node.previousSibling;\n    }\n\n    if (!fromNode) { return false }\n    var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));\n    var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));\n    while (newText.length > 1 && oldText.length > 1) {\n      if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }\n      else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }\n      else { break }\n    }\n\n    var cutFront = 0, cutEnd = 0;\n    var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);\n    while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))\n      { ++cutFront; }\n    var newBot = lst(newText), oldBot = lst(oldText);\n    var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),\n                             oldBot.length - (oldText.length == 1 ? cutFront : 0));\n    while (cutEnd < maxCutEnd &&\n           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))\n      { ++cutEnd; }\n    // Try to move start of change to start of selection if ambiguous\n    if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {\n      while (cutFront && cutFront > from.ch &&\n             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {\n        cutFront--;\n        cutEnd++;\n      }\n    }\n\n    newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\\u200b+/, \"\");\n    newText[0] = newText[0].slice(cutFront).replace(/\\u200b+$/, \"\");\n\n    var chFrom = Pos(fromLine, cutFront);\n    var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);\n    if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {\n      replaceRange(cm.doc, newText, chFrom, chTo, \"+input\");\n      return true\n    }\n  };\n\n  ContentEditableInput.prototype.ensurePolled = function () {\n    this.forceCompositionEnd();\n  };\n  ContentEditableInput.prototype.reset = function () {\n    this.forceCompositionEnd();\n  };\n  ContentEditableInput.prototype.forceCompositionEnd = function () {\n    if (!this.composing) { return }\n    clearTimeout(this.readDOMTimeout);\n    this.composing = null;\n    this.updateFromDOM();\n    this.div.blur();\n    this.div.focus();\n  };\n  ContentEditableInput.prototype.readFromDOMSoon = function () {\n      var this$1 = this;\n\n    if (this.readDOMTimeout != null) { return }\n    this.readDOMTimeout = setTimeout(function () {\n      this$1.readDOMTimeout = null;\n      if (this$1.composing) {\n        if (this$1.composing.done) { this$1.composing = null; }\n        else { return }\n      }\n      this$1.updateFromDOM();\n    }, 80);\n  };\n\n  ContentEditableInput.prototype.updateFromDOM = function () {\n      var this$1 = this;\n\n    if (this.cm.isReadOnly() || !this.pollContent())\n      { runInOp(this.cm, function () { return regChange(this$1.cm); }); }\n  };\n\n  ContentEditableInput.prototype.setUneditable = function (node) {\n    node.contentEditable = \"false\";\n  };\n\n  ContentEditableInput.prototype.onKeyPress = function (e) {\n    if (e.charCode == 0 || this.composing) { return }\n    e.preventDefault();\n    if (!this.cm.isReadOnly())\n      { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }\n  };\n\n  ContentEditableInput.prototype.readOnlyChanged = function (val) {\n    this.div.contentEditable = String(val != \"nocursor\");\n  };\n\n  ContentEditableInput.prototype.onContextMenu = function () {};\n  ContentEditableInput.prototype.resetPosition = function () {};\n\n  ContentEditableInput.prototype.needsContentAttribute = true;\n\n  function posToDOM(cm, pos) {\n    var view = findViewForLine(cm, pos.line);\n    if (!view || view.hidden) { return null }\n    var line = getLine(cm.doc, pos.line);\n    var info = mapFromLineView(view, line, pos.line);\n\n    var order = getOrder(line, cm.doc.direction), side = \"left\";\n    if (order) {\n      var partPos = getBidiPartAt(order, pos.ch);\n      side = partPos % 2 ? \"right\" : \"left\";\n    }\n    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);\n    result.offset = result.collapse == \"right\" ? result.end : result.start;\n    return result\n  }\n\n  function isInGutter(node) {\n    for (var scan = node; scan; scan = scan.parentNode)\n      { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }\n    return false\n  }\n\n  function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }\n\n  function domTextBetween(cm, from, to, fromLine, toLine) {\n    var text = \"\", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;\n    function recognizeMarker(id) { return function (marker) { return marker.id == id; } }\n    function close() {\n      if (closing) {\n        text += lineSep;\n        if (extraLinebreak) { text += lineSep; }\n        closing = extraLinebreak = false;\n      }\n    }\n    function addText(str) {\n      if (str) {\n        close();\n        text += str;\n      }\n    }\n    function walk(node) {\n      if (node.nodeType == 1) {\n        var cmText = node.getAttribute(\"cm-text\");\n        if (cmText) {\n          addText(cmText);\n          return\n        }\n        var markerID = node.getAttribute(\"cm-marker\"), range;\n        if (markerID) {\n          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));\n          if (found.length && (range = found[0].find(0)))\n            { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); }\n          return\n        }\n        if (node.getAttribute(\"contenteditable\") == \"false\") { return }\n        var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);\n        if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }\n\n        if (isBlock) { close(); }\n        for (var i = 0; i < node.childNodes.length; i++)\n          { walk(node.childNodes[i]); }\n\n        if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }\n        if (isBlock) { closing = true; }\n      } else if (node.nodeType == 3) {\n        addText(node.nodeValue.replace(/\\u200b/g, \"\").replace(/\\u00a0/g, \" \"));\n      }\n    }\n    for (;;) {\n      walk(from);\n      if (from == to) { break }\n      from = from.nextSibling;\n      extraLinebreak = false;\n    }\n    return text\n  }\n\n  function domToPos(cm, node, offset) {\n    var lineNode;\n    if (node == cm.display.lineDiv) {\n      lineNode = cm.display.lineDiv.childNodes[offset];\n      if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }\n      node = null; offset = 0;\n    } else {\n      for (lineNode = node;; lineNode = lineNode.parentNode) {\n        if (!lineNode || lineNode == cm.display.lineDiv) { return null }\n        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }\n      }\n    }\n    for (var i = 0; i < cm.display.view.length; i++) {\n      var lineView = cm.display.view[i];\n      if (lineView.node == lineNode)\n        { return locateNodeInLineView(lineView, node, offset) }\n    }\n  }\n\n  function locateNodeInLineView(lineView, node, offset) {\n    var wrapper = lineView.text.firstChild, bad = false;\n    if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }\n    if (node == wrapper) {\n      bad = true;\n      node = wrapper.childNodes[offset];\n      offset = 0;\n      if (!node) {\n        var line = lineView.rest ? lst(lineView.rest) : lineView.line;\n        return badPos(Pos(lineNo(line), line.text.length), bad)\n      }\n    }\n\n    var textNode = node.nodeType == 3 ? node : null, topNode = node;\n    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {\n      textNode = node.firstChild;\n      if (offset) { offset = textNode.nodeValue.length; }\n    }\n    while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }\n    var measure = lineView.measure, maps = measure.maps;\n\n    function find(textNode, topNode, offset) {\n      for (var i = -1; i < (maps ? maps.length : 0); i++) {\n        var map = i < 0 ? measure.map : maps[i];\n        for (var j = 0; j < map.length; j += 3) {\n          var curNode = map[j + 2];\n          if (curNode == textNode || curNode == topNode) {\n            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);\n            var ch = map[j] + offset;\n            if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; }\n            return Pos(line, ch)\n          }\n        }\n      }\n    }\n    var found = find(textNode, topNode, offset);\n    if (found) { return badPos(found, bad) }\n\n    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems\n    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {\n      found = find(after, after.firstChild, 0);\n      if (found)\n        { return badPos(Pos(found.line, found.ch - dist), bad) }\n      else\n        { dist += after.textContent.length; }\n    }\n    for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {\n      found = find(before, before.firstChild, -1);\n      if (found)\n        { return badPos(Pos(found.line, found.ch + dist$1), bad) }\n      else\n        { dist$1 += before.textContent.length; }\n    }\n  }\n\n  // TEXTAREA INPUT STYLE\n\n  var TextareaInput = function(cm) {\n    this.cm = cm;\n    // See input.poll and input.reset\n    this.prevInput = \"\";\n\n    // Flag that indicates whether we expect input to appear real soon\n    // now (after some event like 'keypress' or 'input') and are\n    // polling intensively.\n    this.pollingFast = false;\n    // Self-resetting timeout for the poller\n    this.polling = new Delayed();\n    // Used to work around IE issue with selection being forgotten when focus moves away from textarea\n    this.hasSelection = false;\n    this.composing = null;\n    this.resetting = false;\n  };\n\n  TextareaInput.prototype.init = function (display) {\n      var this$1 = this;\n\n    var input = this, cm = this.cm;\n    this.createField(display);\n    var te = this.textarea;\n\n    display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);\n\n    // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)\n    if (ios) { te.style.width = \"0px\"; }\n\n    on(te, \"input\", function () {\n      if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }\n      input.poll();\n    });\n\n    on(te, \"paste\", function (e) {\n      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }\n\n      cm.state.pasteIncoming = +new Date;\n      input.fastPoll();\n    });\n\n    function prepareCopyCut(e) {\n      if (signalDOMEvent(cm, e)) { return }\n      if (cm.somethingSelected()) {\n        setLastCopied({lineWise: false, text: cm.getSelections()});\n      } else if (!cm.options.lineWiseCopyCut) {\n        return\n      } else {\n        var ranges = copyableRanges(cm);\n        setLastCopied({lineWise: true, text: ranges.text});\n        if (e.type == \"cut\") {\n          cm.setSelections(ranges.ranges, null, sel_dontScroll);\n        } else {\n          input.prevInput = \"\";\n          te.value = ranges.text.join(\"\\n\");\n          selectInput(te);\n        }\n      }\n      if (e.type == \"cut\") { cm.state.cutIncoming = +new Date; }\n    }\n    on(te, \"cut\", prepareCopyCut);\n    on(te, \"copy\", prepareCopyCut);\n\n    on(display.scroller, \"paste\", function (e) {\n      if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }\n      if (!te.dispatchEvent) {\n        cm.state.pasteIncoming = +new Date;\n        input.focus();\n        return\n      }\n\n      // Pass the `paste` event to the textarea so it's handled by its event listener.\n      var event = new Event(\"paste\");\n      event.clipboardData = e.clipboardData;\n      te.dispatchEvent(event);\n    });\n\n    // Prevent normal selection in the editor (we handle our own)\n    on(display.lineSpace, \"selectstart\", function (e) {\n      if (!eventInWidget(display, e)) { e_preventDefault(e); }\n    });\n\n    on(te, \"compositionstart\", function () {\n      var start = cm.getCursor(\"from\");\n      if (input.composing) { input.composing.range.clear(); }\n      input.composing = {\n        start: start,\n        range: cm.markText(start, cm.getCursor(\"to\"), {className: \"CodeMirror-composing\"})\n      };\n    });\n    on(te, \"compositionend\", function () {\n      if (input.composing) {\n        input.poll();\n        input.composing.range.clear();\n        input.composing = null;\n      }\n    });\n  };\n\n  TextareaInput.prototype.createField = function (_display) {\n    // Wraps and hides input textarea\n    this.wrapper = hiddenTextarea();\n    // The semihidden textarea that is focused when the editor is\n    // focused, and receives input.\n    this.textarea = this.wrapper.firstChild;\n    var opts = this.cm.options;\n    disableBrowserMagic(this.textarea, opts.spellcheck, opts.autocorrect, opts.autocapitalize);\n  };\n\n  TextareaInput.prototype.screenReaderLabelChanged = function (label) {\n    // Label for screenreaders, accessibility\n    if(label) {\n      this.textarea.setAttribute('aria-label', label);\n    } else {\n      this.textarea.removeAttribute('aria-label');\n    }\n  };\n\n  TextareaInput.prototype.prepareSelection = function () {\n    // Redraw the selection and/or cursor\n    var cm = this.cm, display = cm.display, doc = cm.doc;\n    var result = prepareSelection(cm);\n\n    // Move the hidden textarea near the cursor to prevent scrolling artifacts\n    if (cm.options.moveInputWithCursor) {\n      var headPos = cursorCoords(cm, doc.sel.primary().head, \"div\");\n      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();\n      result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,\n                                          headPos.top + lineOff.top - wrapOff.top));\n      result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,\n                                           headPos.left + lineOff.left - wrapOff.left));\n    }\n\n    return result\n  };\n\n  TextareaInput.prototype.showSelection = function (drawn) {\n    var cm = this.cm, display = cm.display;\n    removeChildrenAndAdd(display.cursorDiv, drawn.cursors);\n    removeChildrenAndAdd(display.selectionDiv, drawn.selection);\n    if (drawn.teTop != null) {\n      this.wrapper.style.top = drawn.teTop + \"px\";\n      this.wrapper.style.left = drawn.teLeft + \"px\";\n    }\n  };\n\n  // Reset the input to correspond to the selection (or to be empty,\n  // when not typing and nothing is selected)\n  TextareaInput.prototype.reset = function (typing) {\n    if (this.contextMenuPending || this.composing && typing) { return }\n    var cm = this.cm;\n    this.resetting = true;\n    if (cm.somethingSelected()) {\n      this.prevInput = \"\";\n      var content = cm.getSelection();\n      this.textarea.value = content;\n      if (cm.state.focused) { selectInput(this.textarea); }\n      if (ie && ie_version >= 9) { this.hasSelection = content; }\n    } else if (!typing) {\n      this.prevInput = this.textarea.value = \"\";\n      if (ie && ie_version >= 9) { this.hasSelection = null; }\n    }\n    this.resetting = false;\n  };\n\n  TextareaInput.prototype.getField = function () { return this.textarea };\n\n  TextareaInput.prototype.supportsTouch = function () { return false };\n\n  TextareaInput.prototype.focus = function () {\n    if (this.cm.options.readOnly != \"nocursor\" && (!mobile || activeElt(rootNode(this.textarea)) != this.textarea)) {\n      try { this.textarea.focus(); }\n      catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM\n    }\n  };\n\n  TextareaInput.prototype.blur = function () { this.textarea.blur(); };\n\n  TextareaInput.prototype.resetPosition = function () {\n    this.wrapper.style.top = this.wrapper.style.left = 0;\n  };\n\n  TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };\n\n  // Poll for input changes, using the normal rate of polling. This\n  // runs as long as the editor is focused.\n  TextareaInput.prototype.slowPoll = function () {\n      var this$1 = this;\n\n    if (this.pollingFast) { return }\n    this.polling.set(this.cm.options.pollInterval, function () {\n      this$1.poll();\n      if (this$1.cm.state.focused) { this$1.slowPoll(); }\n    });\n  };\n\n  // When an event has just come in that is likely to add or change\n  // something in the input textarea, we poll faster, to ensure that\n  // the change appears on the screen quickly.\n  TextareaInput.prototype.fastPoll = function () {\n    var missed = false, input = this;\n    input.pollingFast = true;\n    function p() {\n      var changed = input.poll();\n      if (!changed && !missed) {missed = true; input.polling.set(60, p);}\n      else {input.pollingFast = false; input.slowPoll();}\n    }\n    input.polling.set(20, p);\n  };\n\n  // Read input from the textarea, and update the document to match.\n  // When something is selected, it is present in the textarea, and\n  // selected (unless it is huge, in which case a placeholder is\n  // used). When nothing is selected, the cursor sits after previously\n  // seen text (can be empty), which is stored in prevInput (we must\n  // not reset the textarea when typing, because that breaks IME).\n  TextareaInput.prototype.poll = function () {\n      var this$1 = this;\n\n    var cm = this.cm, input = this.textarea, prevInput = this.prevInput;\n    // Since this is called a *lot*, try to bail out as cheaply as\n    // possible when it is clear that nothing happened. hasSelection\n    // will be the case when there is a lot of text in the textarea,\n    // in which case reading its value would be expensive.\n    if (this.contextMenuPending || this.resetting || !cm.state.focused ||\n        (hasSelection(input) && !prevInput && !this.composing) ||\n        cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)\n      { return false }\n\n    var text = input.value;\n    // If nothing changed, bail.\n    if (text == prevInput && !cm.somethingSelected()) { return false }\n    // Work around nonsensical selection resetting in IE9/10, and\n    // inexplicable appearance of private area unicode characters on\n    // some key combos in Mac (#2689).\n    if (ie && ie_version >= 9 && this.hasSelection === text ||\n        mac && /[\\uf700-\\uf7ff]/.test(text)) {\n      cm.display.input.reset();\n      return false\n    }\n\n    if (cm.doc.sel == cm.display.selForContextMenu) {\n      var first = text.charCodeAt(0);\n      if (first == 0x200b && !prevInput) { prevInput = \"\\u200b\"; }\n      if (first == 0x21da) { this.reset(); return this.cm.execCommand(\"undo\") }\n    }\n    // Find the part of the input that is actually new\n    var same = 0, l = Math.min(prevInput.length, text.length);\n    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }\n\n    runInOp(cm, function () {\n      applyTextInput(cm, text.slice(same), prevInput.length - same,\n                     null, this$1.composing ? \"*compose\" : null);\n\n      // Don't leave long text in the textarea, since it makes further polling slow\n      if (text.length > 1000 || text.indexOf(\"\\n\") > -1) { input.value = this$1.prevInput = \"\"; }\n      else { this$1.prevInput = text; }\n\n      if (this$1.composing) {\n        this$1.composing.range.clear();\n        this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor(\"to\"),\n                                           {className: \"CodeMirror-composing\"});\n      }\n    });\n    return true\n  };\n\n  TextareaInput.prototype.ensurePolled = function () {\n    if (this.pollingFast && this.poll()) { this.pollingFast = false; }\n  };\n\n  TextareaInput.prototype.onKeyPress = function () {\n    if (ie && ie_version >= 9) { this.hasSelection = null; }\n    this.fastPoll();\n  };\n\n  TextareaInput.prototype.onContextMenu = function (e) {\n    var input = this, cm = input.cm, display = cm.display, te = input.textarea;\n    if (input.contextMenuPending) { input.contextMenuPending(); }\n    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;\n    if (!pos || presto) { return } // Opera is difficult.\n\n    // Reset the current text selection only if the click is done outside of the selection\n    // and 'resetSelectionOnContextMenu' option is true.\n    var reset = cm.options.resetSelectionOnContextMenu;\n    if (reset && cm.doc.sel.contains(pos) == -1)\n      { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }\n\n    var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;\n    var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();\n    input.wrapper.style.cssText = \"position: static\";\n    te.style.cssText = \"position: absolute; width: 30px; height: 30px;\\n      top: \" + (e.clientY - wrapperBox.top - 5) + \"px; left: \" + (e.clientX - wrapperBox.left - 5) + \"px;\\n      z-index: 1000; background: \" + (ie ? \"rgba(255, 255, 255, .05)\" : \"transparent\") + \";\\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);\";\n    var oldScrollY;\n    if (webkit) { oldScrollY = te.ownerDocument.defaultView.scrollY; } // Work around Chrome issue (#2712)\n    display.input.focus();\n    if (webkit) { te.ownerDocument.defaultView.scrollTo(null, oldScrollY); }\n    display.input.reset();\n    // Adds \"Select all\" to context menu in FF\n    if (!cm.somethingSelected()) { te.value = input.prevInput = \" \"; }\n    input.contextMenuPending = rehide;\n    display.selForContextMenu = cm.doc.sel;\n    clearTimeout(display.detectingSelectAll);\n\n    // Select-all will be greyed out if there's nothing to select, so\n    // this adds a zero-width space so that we can later check whether\n    // it got selected.\n    function prepareSelectAllHack() {\n      if (te.selectionStart != null) {\n        var selected = cm.somethingSelected();\n        var extval = \"\\u200b\" + (selected ? te.value : \"\");\n        te.value = \"\\u21da\"; // Used to catch context-menu undo\n        te.value = extval;\n        input.prevInput = selected ? \"\" : \"\\u200b\";\n        te.selectionStart = 1; te.selectionEnd = extval.length;\n        // Re-set this, in case some other handler touched the\n        // selection in the meantime.\n        display.selForContextMenu = cm.doc.sel;\n      }\n    }\n    function rehide() {\n      if (input.contextMenuPending != rehide) { return }\n      input.contextMenuPending = false;\n      input.wrapper.style.cssText = oldWrapperCSS;\n      te.style.cssText = oldCSS;\n      if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }\n\n      // Try to detect the user choosing select-all\n      if (te.selectionStart != null) {\n        if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }\n        var i = 0, poll = function () {\n          if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&\n              te.selectionEnd > 0 && input.prevInput == \"\\u200b\") {\n            operation(cm, selectAll)(cm);\n          } else if (i++ < 10) {\n            display.detectingSelectAll = setTimeout(poll, 500);\n          } else {\n            display.selForContextMenu = null;\n            display.input.reset();\n          }\n        };\n        display.detectingSelectAll = setTimeout(poll, 200);\n      }\n    }\n\n    if (ie && ie_version >= 9) { prepareSelectAllHack(); }\n    if (captureRightClick) {\n      e_stop(e);\n      var mouseup = function () {\n        off(window, \"mouseup\", mouseup);\n        setTimeout(rehide, 20);\n      };\n      on(window, \"mouseup\", mouseup);\n    } else {\n      setTimeout(rehide, 50);\n    }\n  };\n\n  TextareaInput.prototype.readOnlyChanged = function (val) {\n    if (!val) { this.reset(); }\n    this.textarea.disabled = val == \"nocursor\";\n    this.textarea.readOnly = !!val;\n  };\n\n  TextareaInput.prototype.setUneditable = function () {};\n\n  TextareaInput.prototype.needsContentAttribute = false;\n\n  function fromTextArea(textarea, options) {\n    options = options ? copyObj(options) : {};\n    options.value = textarea.value;\n    if (!options.tabindex && textarea.tabIndex)\n      { options.tabindex = textarea.tabIndex; }\n    if (!options.placeholder && textarea.placeholder)\n      { options.placeholder = textarea.placeholder; }\n    // Set autofocus to true if this textarea is focused, or if it has\n    // autofocus and no other element is focused.\n    if (options.autofocus == null) {\n      var hasFocus = activeElt(rootNode(textarea));\n      options.autofocus = hasFocus == textarea ||\n        textarea.getAttribute(\"autofocus\") != null && hasFocus == document.body;\n    }\n\n    function save() {textarea.value = cm.getValue();}\n\n    var realSubmit;\n    if (textarea.form) {\n      on(textarea.form, \"submit\", save);\n      // Deplorable hack to make the submit method do the right thing.\n      if (!options.leaveSubmitMethodAlone) {\n        var form = textarea.form;\n        realSubmit = form.submit;\n        try {\n          var wrappedSubmit = form.submit = function () {\n            save();\n            form.submit = realSubmit;\n            form.submit();\n            form.submit = wrappedSubmit;\n          };\n        } catch(e) {}\n      }\n    }\n\n    options.finishInit = function (cm) {\n      cm.save = save;\n      cm.getTextArea = function () { return textarea; };\n      cm.toTextArea = function () {\n        cm.toTextArea = isNaN; // Prevent this from being ran twice\n        save();\n        textarea.parentNode.removeChild(cm.getWrapperElement());\n        textarea.style.display = \"\";\n        if (textarea.form) {\n          off(textarea.form, \"submit\", save);\n          if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == \"function\")\n            { textarea.form.submit = realSubmit; }\n        }\n      };\n    };\n\n    textarea.style.display = \"none\";\n    var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },\n      options);\n    return cm\n  }\n\n  function addLegacyProps(CodeMirror) {\n    CodeMirror.off = off;\n    CodeMirror.on = on;\n    CodeMirror.wheelEventPixels = wheelEventPixels;\n    CodeMirror.Doc = Doc;\n    CodeMirror.splitLines = splitLinesAuto;\n    CodeMirror.countColumn = countColumn;\n    CodeMirror.findColumn = findColumn;\n    CodeMirror.isWordChar = isWordCharBasic;\n    CodeMirror.Pass = Pass;\n    CodeMirror.signal = signal;\n    CodeMirror.Line = Line;\n    CodeMirror.changeEnd = changeEnd;\n    CodeMirror.scrollbarModel = scrollbarModel;\n    CodeMirror.Pos = Pos;\n    CodeMirror.cmpPos = cmp;\n    CodeMirror.modes = modes;\n    CodeMirror.mimeModes = mimeModes;\n    CodeMirror.resolveMode = resolveMode;\n    CodeMirror.getMode = getMode;\n    CodeMirror.modeExtensions = modeExtensions;\n    CodeMirror.extendMode = extendMode;\n    CodeMirror.copyState = copyState;\n    CodeMirror.startState = startState;\n    CodeMirror.innerMode = innerMode;\n    CodeMirror.commands = commands;\n    CodeMirror.keyMap = keyMap;\n    CodeMirror.keyName = keyName;\n    CodeMirror.isModifierKey = isModifierKey;\n    CodeMirror.lookupKey = lookupKey;\n    CodeMirror.normalizeKeyMap = normalizeKeyMap;\n    CodeMirror.StringStream = StringStream;\n    CodeMirror.SharedTextMarker = SharedTextMarker;\n    CodeMirror.TextMarker = TextMarker;\n    CodeMirror.LineWidget = LineWidget;\n    CodeMirror.e_preventDefault = e_preventDefault;\n    CodeMirror.e_stopPropagation = e_stopPropagation;\n    CodeMirror.e_stop = e_stop;\n    CodeMirror.addClass = addClass;\n    CodeMirror.contains = contains;\n    CodeMirror.rmClass = rmClass;\n    CodeMirror.keyNames = keyNames;\n  }\n\n  // EDITOR CONSTRUCTOR\n\n  defineOptions(CodeMirror);\n\n  addEditorMethods(CodeMirror);\n\n  // Set up methods on CodeMirror's prototype to redirect to the editor's document.\n  var dontDelegate = \"iter insert remove copy getEditor constructor\".split(\" \");\n  for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)\n    { CodeMirror.prototype[prop] = (function(method) {\n      return function() {return method.apply(this.doc, arguments)}\n    })(Doc.prototype[prop]); } }\n\n  eventMixin(Doc);\n  CodeMirror.inputStyles = {\"textarea\": TextareaInput, \"contenteditable\": ContentEditableInput};\n\n  // Extra arguments are stored as the mode's dependencies, which is\n  // used by (legacy) mechanisms like loadmode.js to automatically\n  // load a mode. (Preferred mechanism is the require/define calls.)\n  CodeMirror.defineMode = function(name/*, mode, …*/) {\n    if (!CodeMirror.defaults.mode && name != \"null\") { CodeMirror.defaults.mode = name; }\n    defineMode.apply(this, arguments);\n  };\n\n  CodeMirror.defineMIME = defineMIME;\n\n  // Minimal default mode.\n  CodeMirror.defineMode(\"null\", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });\n  CodeMirror.defineMIME(\"text/plain\", \"null\");\n\n  // EXTENSIONS\n\n  CodeMirror.defineExtension = function (name, func) {\n    CodeMirror.prototype[name] = func;\n  };\n  CodeMirror.defineDocExtension = function (name, func) {\n    Doc.prototype[name] = func;\n  };\n\n  CodeMirror.fromTextArea = fromTextArea;\n\n  addLegacyProps(CodeMirror);\n\n  CodeMirror.version = \"5.65.16\";\n\n  return CodeMirror;\n\n})));\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/apl/apl.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"apl\", function() {\n  var builtInOps = {\n    \".\": \"innerProduct\",\n    \"\\\\\": \"scan\",\n    \"/\": \"reduce\",\n    \"⌿\": \"reduce1Axis\",\n    \"⍀\": \"scan1Axis\",\n    \"¨\": \"each\",\n    \"⍣\": \"power\"\n  };\n  var builtInFuncs = {\n    \"+\": [\"conjugate\", \"add\"],\n    \"−\": [\"negate\", \"subtract\"],\n    \"×\": [\"signOf\", \"multiply\"],\n    \"÷\": [\"reciprocal\", \"divide\"],\n    \"⌈\": [\"ceiling\", \"greaterOf\"],\n    \"⌊\": [\"floor\", \"lesserOf\"],\n    \"∣\": [\"absolute\", \"residue\"],\n    \"⍳\": [\"indexGenerate\", \"indexOf\"],\n    \"?\": [\"roll\", \"deal\"],\n    \"⋆\": [\"exponentiate\", \"toThePowerOf\"],\n    \"⍟\": [\"naturalLog\", \"logToTheBase\"],\n    \"○\": [\"piTimes\", \"circularFuncs\"],\n    \"!\": [\"factorial\", \"binomial\"],\n    \"⌹\": [\"matrixInverse\", \"matrixDivide\"],\n    \"<\": [null, \"lessThan\"],\n    \"≤\": [null, \"lessThanOrEqual\"],\n    \"=\": [null, \"equals\"],\n    \">\": [null, \"greaterThan\"],\n    \"≥\": [null, \"greaterThanOrEqual\"],\n    \"≠\": [null, \"notEqual\"],\n    \"≡\": [\"depth\", \"match\"],\n    \"≢\": [null, \"notMatch\"],\n    \"∈\": [\"enlist\", \"membership\"],\n    \"⍷\": [null, \"find\"],\n    \"∪\": [\"unique\", \"union\"],\n    \"∩\": [null, \"intersection\"],\n    \"∼\": [\"not\", \"without\"],\n    \"∨\": [null, \"or\"],\n    \"∧\": [null, \"and\"],\n    \"⍱\": [null, \"nor\"],\n    \"⍲\": [null, \"nand\"],\n    \"⍴\": [\"shapeOf\", \"reshape\"],\n    \",\": [\"ravel\", \"catenate\"],\n    \"⍪\": [null, \"firstAxisCatenate\"],\n    \"⌽\": [\"reverse\", \"rotate\"],\n    \"⊖\": [\"axis1Reverse\", \"axis1Rotate\"],\n    \"⍉\": [\"transpose\", null],\n    \"↑\": [\"first\", \"take\"],\n    \"↓\": [null, \"drop\"],\n    \"⊂\": [\"enclose\", \"partitionWithAxis\"],\n    \"⊃\": [\"diclose\", \"pick\"],\n    \"⌷\": [null, \"index\"],\n    \"⍋\": [\"gradeUp\", null],\n    \"⍒\": [\"gradeDown\", null],\n    \"⊤\": [\"encode\", null],\n    \"⊥\": [\"decode\", null],\n    \"⍕\": [\"format\", \"formatByExample\"],\n    \"⍎\": [\"execute\", null],\n    \"⊣\": [\"stop\", \"left\"],\n    \"⊢\": [\"pass\", \"right\"]\n  };\n\n  var isOperator = /[\\.\\/⌿⍀¨⍣]/;\n  var isNiladic = /⍬/;\n  var isFunction = /[\\+−×÷⌈⌊∣⍳\\?⋆⍟○!⌹<≤=>≥≠≡≢∈⍷∪∩∼∨∧⍱⍲⍴,⍪⌽⊖⍉↑↓⊂⊃⌷⍋⍒⊤⊥⍕⍎⊣⊢]/;\n  var isArrow = /←/;\n  var isComment = /[⍝#].*$/;\n\n  var stringEater = function(type) {\n    var prev;\n    prev = false;\n    return function(c) {\n      prev = c;\n      if (c === type) {\n        return prev === \"\\\\\";\n      }\n      return true;\n    };\n  };\n  return {\n    startState: function() {\n      return {\n        prev: false,\n        func: false,\n        op: false,\n        string: false,\n        escape: false\n      };\n    },\n    token: function(stream, state) {\n      var ch, funcName;\n      if (stream.eatSpace()) {\n        return null;\n      }\n      ch = stream.next();\n      if (ch === '\"' || ch === \"'\") {\n        stream.eatWhile(stringEater(ch));\n        stream.next();\n        state.prev = true;\n        return \"string\";\n      }\n      if (/[\\[{\\(]/.test(ch)) {\n        state.prev = false;\n        return null;\n      }\n      if (/[\\]}\\)]/.test(ch)) {\n        state.prev = true;\n        return null;\n      }\n      if (isNiladic.test(ch)) {\n        state.prev = false;\n        return \"niladic\";\n      }\n      if (/[¯\\d]/.test(ch)) {\n        if (state.func) {\n          state.func = false;\n          state.prev = false;\n        } else {\n          state.prev = true;\n        }\n        stream.eatWhile(/[\\w\\.]/);\n        return \"number\";\n      }\n      if (isOperator.test(ch)) {\n        return \"operator apl-\" + builtInOps[ch];\n      }\n      if (isArrow.test(ch)) {\n        return \"apl-arrow\";\n      }\n      if (isFunction.test(ch)) {\n        funcName = \"apl-\";\n        if (builtInFuncs[ch] != null) {\n          if (state.prev) {\n            funcName += builtInFuncs[ch][1];\n          } else {\n            funcName += builtInFuncs[ch][0];\n          }\n        }\n        state.func = true;\n        state.prev = false;\n        return \"function \" + funcName;\n      }\n      if (isComment.test(ch)) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n      if (ch === \"∘\" && stream.peek() === \".\") {\n        stream.next();\n        return \"function jot-dot\";\n      }\n      stream.eatWhile(/[\\w\\$_]/);\n      state.prev = true;\n      return \"keyword\";\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/apl\", \"apl\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/apl/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: APL mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"./apl.js\"></script>\n<style>\n\t.CodeMirror { border: 2px inset #dee; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">APL</a>\n  </ul>\n</div>\n\n<article>\n<h2>APL mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n⍝ Conway's game of life\n\n⍝ This example was inspired by the impressive demo at\n⍝ http://www.youtube.com/watch?v=a9xAKttWgP4\n\n⍝ Create a matrix:\n⍝     0 1 1\n⍝     1 1 0\n⍝     0 1 0\ncreature ← (3 3 ⍴ ⍳ 9) ∈ 1 2 3 4 7   ⍝ Original creature from demo\ncreature ← (3 3 ⍴ ⍳ 9) ∈ 1 3 6 7 8   ⍝ Glider\n\n⍝ Place the creature on a larger board, near the centre\nboard ← ¯1 ⊖ ¯2 ⌽ 5 7 ↑ creature\n\n⍝ A function to move from one generation to the next\nlife ← {∨/ 1 ⍵ ∧ 3 4 = ⊂+/ +⌿ 1 0 ¯1 ∘.⊖ 1 0 ¯1 ⌽¨ ⊂⍵}\n\n⍝ Compute n-th generation and format it as a\n⍝ character matrix\ngen ← {' #'[(life ⍣ ⍵) board]}\n\n⍝ Show first three generations\n(gen 1) (gen 2) (gen 3)\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/apl\"\n      });\n    </script>\n\n    <p>Simple mode that tries to handle APL as well as it can.</p>\n    <p>It attempts to label functions/operators based upon\n    monadic/dyadic usage (but this is far from fully fleshed out).\n    This means there are meaningful classnames so hover states can\n    have popups etc.</p>\n\n    <p><strong>MIME types defined:</strong> <code>text/apl</code> (APL code)</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/asciiarmor/asciiarmor.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  function errorIfNotEmpty(stream) {\n    var nonWS = stream.match(/^\\s*\\S/);\n    stream.skipToEnd();\n    return nonWS ? \"error\" : null;\n  }\n\n  CodeMirror.defineMode(\"asciiarmor\", function() {\n    return {\n      token: function(stream, state) {\n        var m;\n        if (state.state == \"top\") {\n          if (stream.sol() && (m = stream.match(/^-----BEGIN (.*)?-----\\s*$/))) {\n            state.state = \"headers\";\n            state.type = m[1];\n            return \"tag\";\n          }\n          return errorIfNotEmpty(stream);\n        } else if (state.state == \"headers\") {\n          if (stream.sol() && stream.match(/^\\w+:/)) {\n            state.state = \"header\";\n            return \"atom\";\n          } else {\n            var result = errorIfNotEmpty(stream);\n            if (result) state.state = \"body\";\n            return result;\n          }\n        } else if (state.state == \"header\") {\n          stream.skipToEnd();\n          state.state = \"headers\";\n          return \"string\";\n        } else if (state.state == \"body\") {\n          if (stream.sol() && (m = stream.match(/^-----END (.*)?-----\\s*$/))) {\n            if (m[1] != state.type) return \"error\";\n            state.state = \"end\";\n            return \"tag\";\n          } else {\n            if (stream.eatWhile(/[A-Za-z0-9+\\/=]/)) {\n              return null;\n            } else {\n              stream.next();\n              return \"error\";\n            }\n          }\n        } else if (state.state == \"end\") {\n          return errorIfNotEmpty(stream);\n        }\n      },\n      blankLine: function(state) {\n        if (state.state == \"headers\") state.state = \"body\";\n      },\n      startState: function() {\n        return {state: \"top\", type: null};\n      }\n    };\n  });\n\n  CodeMirror.defineMIME(\"application/pgp\", \"asciiarmor\");\n  CodeMirror.defineMIME(\"application/pgp-encrypted\", \"asciiarmor\");\n  CodeMirror.defineMIME(\"application/pgp-keys\", \"asciiarmor\");\n  CodeMirror.defineMIME(\"application/pgp-signature\", \"asciiarmor\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/asciiarmor/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: ASCII Armor (PGP) mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"asciiarmor.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">ASCII Armor</a>\n  </ul>\n</div>\n\n<article>\n<h2>ASCII Armor (PGP) mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n-----BEGIN PGP MESSAGE-----\nVersion: OpenPrivacy 0.99\n\nyDgBO22WxBHv7O8X7O/jygAEzol56iUKiXmV+XmpCtmpqQUKiQrFqclFqUDBovzS\nvBSFjNSiVHsuAA==\n=njUN\n-----END PGP MESSAGE-----\n</textarea></form>\n\n<script>\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  lineNumbers: true\n});\n</script>\n\n<p><strong>MIME types\ndefined:</strong> <code>application/pgp</code>, <code>application/pgp-encrypted</code>, <code>application/pgp-keys</code>, <code>application/pgp-signature</code></p>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/asn.1/asn.1.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"asn.1\", function(config, parserConfig) {\n    var indentUnit = config.indentUnit,\n        keywords = parserConfig.keywords || {},\n        cmipVerbs = parserConfig.cmipVerbs || {},\n        compareTypes = parserConfig.compareTypes || {},\n        status = parserConfig.status || {},\n        tags = parserConfig.tags || {},\n        storage = parserConfig.storage || {},\n        modifier = parserConfig.modifier || {},\n        accessTypes = parserConfig.accessTypes|| {},\n        multiLineStrings = parserConfig.multiLineStrings,\n        indentStatements = parserConfig.indentStatements !== false;\n    var isOperatorChar = /[\\|\\^]/;\n    var curPunc;\n\n    function tokenBase(stream, state) {\n      var ch = stream.next();\n      if (ch == '\"' || ch == \"'\") {\n        state.tokenize = tokenString(ch);\n        return state.tokenize(stream, state);\n      }\n      if (/[\\[\\]\\(\\){}:=,;]/.test(ch)) {\n        curPunc = ch;\n        return \"punctuation\";\n      }\n      if (ch == \"-\"){\n        if (stream.eat(\"-\")) {\n          stream.skipToEnd();\n          return \"comment\";\n        }\n      }\n      if (/\\d/.test(ch)) {\n        stream.eatWhile(/[\\w\\.]/);\n        return \"number\";\n      }\n      if (isOperatorChar.test(ch)) {\n        stream.eatWhile(isOperatorChar);\n        return \"operator\";\n      }\n\n      stream.eatWhile(/[\\w\\-]/);\n      var cur = stream.current();\n      if (keywords.propertyIsEnumerable(cur)) return \"keyword\";\n      if (cmipVerbs.propertyIsEnumerable(cur)) return \"variable cmipVerbs\";\n      if (compareTypes.propertyIsEnumerable(cur)) return \"atom compareTypes\";\n      if (status.propertyIsEnumerable(cur)) return \"comment status\";\n      if (tags.propertyIsEnumerable(cur)) return \"variable-3 tags\";\n      if (storage.propertyIsEnumerable(cur)) return \"builtin storage\";\n      if (modifier.propertyIsEnumerable(cur)) return \"string-2 modifier\";\n      if (accessTypes.propertyIsEnumerable(cur)) return \"atom accessTypes\";\n\n      return \"variable\";\n    }\n\n    function tokenString(quote) {\n      return function(stream, state) {\n        var escaped = false, next, end = false;\n        while ((next = stream.next()) != null) {\n          if (next == quote && !escaped){\n            var afterNext = stream.peek();\n            //look if the character if the quote is like the B in '10100010'B\n            if (afterNext){\n              afterNext = afterNext.toLowerCase();\n              if(afterNext == \"b\" || afterNext == \"h\" || afterNext == \"o\")\n                stream.next();\n            }\n            end = true; break;\n          }\n          escaped = !escaped && next == \"\\\\\";\n        }\n        if (end || !(escaped || multiLineStrings))\n          state.tokenize = null;\n        return \"string\";\n      };\n    }\n\n    function Context(indented, column, type, align, prev) {\n      this.indented = indented;\n      this.column = column;\n      this.type = type;\n      this.align = align;\n      this.prev = prev;\n    }\n    function pushContext(state, col, type) {\n      var indent = state.indented;\n      if (state.context && state.context.type == \"statement\")\n        indent = state.context.indented;\n      return state.context = new Context(indent, col, type, null, state.context);\n    }\n    function popContext(state) {\n      var t = state.context.type;\n      if (t == \")\" || t == \"]\" || t == \"}\")\n        state.indented = state.context.indented;\n      return state.context = state.context.prev;\n    }\n\n    //Interface\n    return {\n      startState: function(basecolumn) {\n        return {\n          tokenize: null,\n          context: new Context((basecolumn || 0) - indentUnit, 0, \"top\", false),\n          indented: 0,\n          startOfLine: true\n        };\n      },\n\n      token: function(stream, state) {\n        var ctx = state.context;\n        if (stream.sol()) {\n          if (ctx.align == null) ctx.align = false;\n          state.indented = stream.indentation();\n          state.startOfLine = true;\n        }\n        if (stream.eatSpace()) return null;\n        curPunc = null;\n        var style = (state.tokenize || tokenBase)(stream, state);\n        if (style == \"comment\") return style;\n        if (ctx.align == null) ctx.align = true;\n\n        if ((curPunc == \";\" || curPunc == \":\" || curPunc == \",\")\n            && ctx.type == \"statement\"){\n          popContext(state);\n        }\n        else if (curPunc == \"{\") pushContext(state, stream.column(), \"}\");\n        else if (curPunc == \"[\") pushContext(state, stream.column(), \"]\");\n        else if (curPunc == \"(\") pushContext(state, stream.column(), \")\");\n        else if (curPunc == \"}\") {\n          while (ctx.type == \"statement\") ctx = popContext(state);\n          if (ctx.type == \"}\") ctx = popContext(state);\n          while (ctx.type == \"statement\") ctx = popContext(state);\n        }\n        else if (curPunc == ctx.type) popContext(state);\n        else if (indentStatements && (((ctx.type == \"}\" || ctx.type == \"top\")\n            && curPunc != ';') || (ctx.type == \"statement\"\n            && curPunc == \"newstatement\")))\n          pushContext(state, stream.column(), \"statement\");\n\n        state.startOfLine = false;\n        return style;\n      },\n\n      electricChars: \"{}\",\n      lineComment: \"--\",\n      fold: \"brace\"\n    };\n  });\n\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  CodeMirror.defineMIME(\"text/x-ttcn-asn\", {\n    name: \"asn.1\",\n    keywords: words(\"DEFINITIONS OBJECTS IF DERIVED INFORMATION ACTION\" +\n    \" REPLY ANY NAMED CHARACTERIZED BEHAVIOUR REGISTERED\" +\n    \" WITH AS IDENTIFIED CONSTRAINED BY PRESENT BEGIN\" +\n    \" IMPORTS FROM UNITS SYNTAX MIN-ACCESS MAX-ACCESS\" +\n    \" MINACCESS MAXACCESS REVISION STATUS DESCRIPTION\" +\n    \" SEQUENCE SET COMPONENTS OF CHOICE DistinguishedName\" +\n    \" ENUMERATED SIZE MODULE END INDEX AUGMENTS EXTENSIBILITY\" +\n    \" IMPLIED EXPORTS\"),\n    cmipVerbs: words(\"ACTIONS ADD GET NOTIFICATIONS REPLACE REMOVE\"),\n    compareTypes: words(\"OPTIONAL DEFAULT MANAGED MODULE-TYPE MODULE_IDENTITY\" +\n    \" MODULE-COMPLIANCE OBJECT-TYPE OBJECT-IDENTITY\" +\n    \" OBJECT-COMPLIANCE MODE CONFIRMED CONDITIONAL\" +\n    \" SUBORDINATE SUPERIOR CLASS TRUE FALSE NULL\" +\n    \" TEXTUAL-CONVENTION\"),\n    status: words(\"current deprecated mandatory obsolete\"),\n    tags: words(\"APPLICATION AUTOMATIC EXPLICIT IMPLICIT PRIVATE TAGS\" +\n    \" UNIVERSAL\"),\n    storage: words(\"BOOLEAN INTEGER OBJECT IDENTIFIER BIT OCTET STRING\" +\n    \" UTCTime InterfaceIndex IANAifType CMIP-Attribute\" +\n    \" REAL PACKAGE PACKAGES IpAddress PhysAddress\" +\n    \" NetworkAddress BITS BMPString TimeStamp TimeTicks\" +\n    \" TruthValue RowStatus DisplayString GeneralString\" +\n    \" GraphicString IA5String NumericString\" +\n    \" PrintableString SnmpAdminString TeletexString\" +\n    \" UTF8String VideotexString VisibleString StringStore\" +\n    \" ISO646String T61String UniversalString Unsigned32\" +\n    \" Integer32 Gauge Gauge32 Counter Counter32 Counter64\"),\n    modifier: words(\"ATTRIBUTE ATTRIBUTES MANDATORY-GROUP MANDATORY-GROUPS\" +\n    \" GROUP GROUPS ELEMENTS EQUALITY ORDERING SUBSTRINGS\" +\n    \" DEFINED\"),\n    accessTypes: words(\"not-accessible accessible-for-notify read-only\" +\n    \" read-create read-write\"),\n    multiLineStrings: true\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/asn.1/index.html",
    "content": "﻿<!doctype html>\n\n<title>CodeMirror: ASN.1 mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"asn.1.js\"></script>\n<style>\n    .CodeMirror {\n        border-top: 1px solid black;\n        border-bottom: 1px solid black;\n    }\n</style>\n<div id=nav>\n    <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1>\n        <img id=logo src=\"../../doc/logo.png\" alt=\"\">\n    </a>\n\n    <ul>\n        <li><a href=\"../../index.html\">Home</a>\n        <li><a href=\"../../doc/manual.html\">Manual</a>\n        <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n    </ul>\n    <ul>\n        <li><a href=\"../index.html\">Language modes</a>\n        <li><a class=active href=\"http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One\">ASN.1</a>\n    </ul>\n</div>\n<article>\n    <h2>ASN.1 example</h2>\n    <div>\n        <textarea id=\"ttcn-asn-code\">\n --\n -- Sample ASN.1 Code\n --\n MyModule DEFINITIONS ::=\n BEGIN\n\n MyTypes ::= SEQUENCE {\n     myObjectId   OBJECT IDENTIFIER,\n     mySeqOf      SEQUENCE OF MyInt,\n     myBitString  BIT STRING {\n                         muxToken(0),\n                         modemToken(1)\n                  }\n }\n\n MyInt ::= INTEGER (0..65535)\n\n END\n        </textarea>\n    </div>\n\n    <script>\n        var ttcnEditor = CodeMirror.fromTextArea(document.getElementById(\"ttcn-asn-code\"), {\n            lineNumbers: true,\n            matchBrackets: true,\n            mode: \"text/x-ttcn-asn\"\n        });\n        ttcnEditor.setSize(400, 400);\n        var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;\n        CodeMirror.keyMap.default[(mac ? \"Cmd\" : \"Ctrl\") + \"-Space\"] = \"autocomplete\";\n    </script>\n    <br/>\n    <p><strong>Language:</strong> Abstract Syntax Notation One\n        (<a href=\"http://www.itu.int/en/ITU-T/asn1/Pages/introduction.aspx\">ASN.1</a>)\n    </p>\n    <p><strong>MIME types defined:</strong> <code>text/x-ttcn-asn</code></p>\n\n    <br/>\n    <p>The development of this mode has been sponsored by <a href=\"http://www.ericsson.com/\">Ericsson\n    </a>.</p>\n    <p>Coded by Asmelash Tsegay Gebretsadkan </p>\n</article>\n\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/asterisk/asterisk.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/*\n * =====================================================================================\n *\n *       Filename:  mode/asterisk/asterisk.js\n *\n *    Description:  CodeMirror mode for Asterisk dialplan\n *\n *        Created:  05/17/2012 09:20:25 PM\n *       Revision:  08/05/2019 AstLinux Project: Support block-comments\n *\n *         Author:  Stas Kobzar (stas@modulis.ca),\n *        Company:  Modulis.ca Inc.\n *\n * =====================================================================================\n */\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"asterisk\", function() {\n  var atoms    = [\"exten\", \"same\", \"include\",\"ignorepat\",\"switch\"],\n      dpcmd    = [\"#include\",\"#exec\"],\n      apps     = [\n                  \"addqueuemember\",\"adsiprog\",\"aelsub\",\"agentlogin\",\"agentmonitoroutgoing\",\"agi\",\n                  \"alarmreceiver\",\"amd\",\"answer\",\"authenticate\",\"background\",\"backgrounddetect\",\n                  \"bridge\",\"busy\",\"callcompletioncancel\",\"callcompletionrequest\",\"celgenuserevent\",\n                  \"changemonitor\",\"chanisavail\",\"channelredirect\",\"chanspy\",\"clearhash\",\"confbridge\",\n                  \"congestion\",\"continuewhile\",\"controlplayback\",\"dahdiacceptr2call\",\"dahdibarge\",\n                  \"dahdiras\",\"dahdiscan\",\"dahdisendcallreroutingfacility\",\"dahdisendkeypadfacility\",\n                  \"datetime\",\"dbdel\",\"dbdeltree\",\"deadagi\",\"dial\",\"dictate\",\"directory\",\"disa\",\n                  \"dumpchan\",\"eagi\",\"echo\",\"endwhile\",\"exec\",\"execif\",\"execiftime\",\"exitwhile\",\"extenspy\",\n                  \"externalivr\",\"festival\",\"flash\",\"followme\",\"forkcdr\",\"getcpeid\",\"gosub\",\"gosubif\",\n                  \"goto\",\"gotoif\",\"gotoiftime\",\"hangup\",\"iax2provision\",\"ices\",\"importvar\",\"incomplete\",\n                  \"ivrdemo\",\"jabberjoin\",\"jabberleave\",\"jabbersend\",\"jabbersendgroup\",\"jabberstatus\",\n                  \"jack\",\"log\",\"macro\",\"macroexclusive\",\"macroexit\",\"macroif\",\"mailboxexists\",\"meetme\",\n                  \"meetmeadmin\",\"meetmechanneladmin\",\"meetmecount\",\"milliwatt\",\"minivmaccmess\",\"minivmdelete\",\n                  \"minivmgreet\",\"minivmmwi\",\"minivmnotify\",\"minivmrecord\",\"mixmonitor\",\"monitor\",\"morsecode\",\n                  \"mp3player\",\"mset\",\"musiconhold\",\"nbscat\",\"nocdr\",\"noop\",\"odbc\",\"odbc\",\"odbcfinish\",\n                  \"originate\",\"ospauth\",\"ospfinish\",\"osplookup\",\"ospnext\",\"page\",\"park\",\"parkandannounce\",\n                  \"parkedcall\",\"pausemonitor\",\"pausequeuemember\",\"pickup\",\"pickupchan\",\"playback\",\"playtones\",\n                  \"privacymanager\",\"proceeding\",\"progress\",\"queue\",\"queuelog\",\"raiseexception\",\"read\",\"readexten\",\n                  \"readfile\",\"receivefax\",\"receivefax\",\"receivefax\",\"record\",\"removequeuemember\",\n                  \"resetcdr\",\"retrydial\",\"return\",\"ringing\",\"sayalpha\",\"saycountedadj\",\"saycountednoun\",\n                  \"saycountpl\",\"saydigits\",\"saynumber\",\"sayphonetic\",\"sayunixtime\",\"senddtmf\",\"sendfax\",\n                  \"sendfax\",\"sendfax\",\"sendimage\",\"sendtext\",\"sendurl\",\"set\",\"setamaflags\",\n                  \"setcallerpres\",\"setmusiconhold\",\"sipaddheader\",\"sipdtmfmode\",\"sipremoveheader\",\"skel\",\n                  \"slastation\",\"slatrunk\",\"sms\",\"softhangup\",\"speechactivategrammar\",\"speechbackground\",\n                  \"speechcreate\",\"speechdeactivategrammar\",\"speechdestroy\",\"speechloadgrammar\",\"speechprocessingsound\",\n                  \"speechstart\",\"speechunloadgrammar\",\"stackpop\",\"startmusiconhold\",\"stopmixmonitor\",\"stopmonitor\",\n                  \"stopmusiconhold\",\"stopplaytones\",\"system\",\"testclient\",\"testserver\",\"transfer\",\"tryexec\",\n                  \"trysystem\",\"unpausemonitor\",\"unpausequeuemember\",\"userevent\",\"verbose\",\"vmauthenticate\",\n                  \"vmsayname\",\"voicemail\",\"voicemailmain\",\"wait\",\"waitexten\",\"waitfornoise\",\"waitforring\",\n                  \"waitforsilence\",\"waitmusiconhold\",\"waituntil\",\"while\",\"zapateller\"\n                 ];\n\n  function basicToken(stream,state){\n    var cur = '';\n    var ch = stream.next();\n    // comment\n    if (state.blockComment) {\n      if (ch == \"-\" && stream.match(\"-;\", true)) {\n        state.blockComment = false;\n      } else if (stream.skipTo(\"--;\")) {\n        stream.next();\n        stream.next();\n        stream.next();\n        state.blockComment = false;\n      } else {\n        stream.skipToEnd();\n      }\n      return \"comment\";\n    }\n    if(ch == \";\") {\n      if (stream.match(\"--\", true)) {\n        if (!stream.match(\"-\", false)) {  // Except ;--- is not a block comment\n          state.blockComment = true;\n          return \"comment\";\n        }\n      }\n      stream.skipToEnd();\n      return \"comment\";\n    }\n    // context\n    if(ch == '[') {\n      stream.skipTo(']');\n      stream.eat(']');\n      return \"header\";\n    }\n    // string\n    if(ch == '\"') {\n      stream.skipTo('\"');\n      return \"string\";\n    }\n    if(ch == \"'\") {\n      stream.skipTo(\"'\");\n      return \"string-2\";\n    }\n    // dialplan commands\n    if(ch == '#') {\n      stream.eatWhile(/\\w/);\n      cur = stream.current();\n      if(dpcmd.indexOf(cur) !== -1) {\n        stream.skipToEnd();\n        return \"strong\";\n      }\n    }\n    // application args\n    if(ch == '$'){\n      var ch1 = stream.peek();\n      if(ch1 == '{'){\n        stream.skipTo('}');\n        stream.eat('}');\n        return \"variable-3\";\n      }\n    }\n    // extension\n    stream.eatWhile(/\\w/);\n    cur = stream.current();\n    if(atoms.indexOf(cur) !== -1) {\n      state.extenStart = true;\n      switch(cur) {\n        case 'same': state.extenSame = true; break;\n        case 'include':\n        case 'switch':\n        case 'ignorepat':\n          state.extenInclude = true;break;\n        default:break;\n      }\n      return \"atom\";\n    }\n  }\n\n  return {\n    startState: function() {\n      return {\n        blockComment: false,\n        extenStart: false,\n        extenSame:  false,\n        extenInclude: false,\n        extenExten: false,\n        extenPriority: false,\n        extenApplication: false\n      };\n    },\n    token: function(stream, state) {\n\n      var cur = '';\n      if(stream.eatSpace()) return null;\n      // extension started\n      if(state.extenStart){\n        stream.eatWhile(/[^\\s]/);\n        cur = stream.current();\n        if(/^=>?$/.test(cur)){\n          state.extenExten = true;\n          state.extenStart = false;\n          return \"strong\";\n        } else {\n          state.extenStart = false;\n          stream.skipToEnd();\n          return \"error\";\n        }\n      } else if(state.extenExten) {\n        // set exten and priority\n        state.extenExten = false;\n        state.extenPriority = true;\n        stream.eatWhile(/[^,]/);\n        if(state.extenInclude) {\n          stream.skipToEnd();\n          state.extenPriority = false;\n          state.extenInclude = false;\n        }\n        if(state.extenSame) {\n          state.extenPriority = false;\n          state.extenSame = false;\n          state.extenApplication = true;\n        }\n        return \"tag\";\n      } else if(state.extenPriority) {\n        state.extenPriority = false;\n        state.extenApplication = true;\n        stream.next(); // get comma\n        if(state.extenSame) return null;\n        stream.eatWhile(/[^,]/);\n        return \"number\";\n      } else if(state.extenApplication) {\n        stream.eatWhile(/,/);\n        cur = stream.current();\n        if(cur === ',') return null;\n        stream.eatWhile(/\\w/);\n        cur = stream.current().toLowerCase();\n        state.extenApplication = false;\n        if(apps.indexOf(cur) !== -1){\n          return \"def strong\";\n        }\n      } else{\n        return basicToken(stream,state);\n      }\n\n      return null;\n    },\n\n    blockCommentStart: \";--\",\n    blockCommentEnd: \"--;\",\n    lineComment: \";\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-asterisk\", \"asterisk\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/asterisk/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Asterisk dialplan mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"asterisk.js\"></script>\n<style>\n      .CodeMirror {border: 1px solid #999;}\n      .cm-s-default span.cm-arrow { color: red; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Asterisk dialplan</a>\n  </ul>\n</div>\n\n<article>\n<h2>Asterisk dialplan mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n; extensions.conf - the Asterisk dial plan\n;\n\n[general]\n;\n; If static is set to no, or omitted, then the pbx_config will rewrite\n; this file when extensions are modified.  Remember that all comments\n; made in the file will be lost when that happens.\nstatic=yes\n\n#include \"/etc/asterisk/additional_general.conf\n\n[iaxprovider]\nswitch => IAX2/user:[key]@myserver/mycontext\n\n[dynamic]\n#exec /usr/bin/dynamic-peers.pl\n\n[trunkint]\n;\n; International long distance through trunk\n;\nexten => _9011.,1,Macro(dundi-e164,${EXTEN:4})\nexten => _9011.,n,Dial(${GLOBAL(TRUNK)}/${FILTER(0-9,${EXTEN:${GLOBAL(TRUNKMSD)}})})\n\n[local]\n;\n; Master context for local, toll-free, and iaxtel calls only\n;\nignorepat => 9\ninclude => default\n\n[demo]\ninclude => stdexten\n;\n; We start with what to do when a call first comes in.\n;\nexten => s,1,Wait(1)\t\t\t; Wait a second, just for fun\nsame  => n,Answer\t\t\t; Answer the line\nsame  => n,Set(TIMEOUT(digit)=5)\t; Set Digit Timeout to 5 seconds\nsame  => n,Set(TIMEOUT(response)=10)\t; Set Response Timeout to 10 seconds\nsame  => n(restart),BackGround(demo-congrats)\t; Play a congratulatory message\nsame  => n(instruct),BackGround(demo-instruct)\t; Play some instructions\nsame  => n,WaitExten\t\t\t; Wait for an extension to be dialed.\n\nexten => 2,1,BackGround(demo-moreinfo)\t; Give some more information.\nexten => 2,n,Goto(s,instruct)\n\nexten => 3,1,Set(LANGUAGE()=fr)\t\t; Set language to french\nexten => 3,n,Goto(s,restart)\t\t; Start with the congratulations\n\nexten => 1000,1,Goto(default,s,1)\n;\n; We also create an example user, 1234, who is on the console and has\n; voicemail, etc.\n;\nexten => 1234,1,Playback(transfer,skip)\t\t; \"Please hold while...\"\n\t\t\t\t\t; (but skip if channel is not up)\nexten => 1234,n,Gosub(${EXTEN},stdexten(${GLOBAL(CONSOLE)}))\nexten => 1234,n,Goto(default,s,1)\t\t; exited Voicemail\n\nexten => 1235,1,Voicemail(1234,u)\t\t; Right to voicemail\n\nexten => 1236,1,Dial(Console/dsp)\t\t; Ring forever\nexten => 1236,n,Voicemail(1234,b)\t\t; Unless busy\n\n;\n; # for when they're done with the demo\n;\nexten => #,1,Playback(demo-thanks)\t; \"Thanks for trying the demo\"\nexten => #,n,Hangup\t\t\t; Hang them up.\n\n;\n; A timeout and \"invalid extension rule\"\n;\nexten => t,1,Goto(#,1)\t\t\t; If they take too long, give up\nexten => i,1,Playback(invalid)\t\t; \"That's not valid, try again\"\n\n;\n; Create an extension, 500, for dialing the\n; Asterisk demo.\n;\nexten => 500,1,Playback(demo-abouttotry); Let them know what's going on\nexten => 500,n,Dial(IAX2/guest@pbx.digium.com/s@default)\t; Call the Asterisk demo\nexten => 500,n,Playback(demo-nogo)\t; Couldn't connect to the demo site\nexten => 500,n,Goto(s,6)\t\t; Return to the start over message.\n\n;\n; Create an extension, 600, for evaluating echo latency.\n;\nexten => 600,1,Playback(demo-echotest)\t; Let them know what's going on\nexten => 600,n,Echo\t\t\t; Do the echo test\nexten => 600,n,Playback(demo-echodone)\t; Let them know it's over\nexten => 600,n,Goto(s,6)\t\t; Start over\n\n;\n;\tYou can use the Macro Page to intercom a individual user\nexten => 76245,1,Macro(page,SIP/Grandstream1)\n; or if your peernames are the same as extensions\nexten => _7XXX,1,Macro(page,SIP/${EXTEN})\n;\n;\n; System Wide Page at extension 7999\n;\nexten => 7999,1,Set(TIMEOUT(absolute)=60)\nexten => 7999,2,Page(Local/Grandstream1@page&Local/Xlite1@page&Local/1234@page/n,d)\n\n; Give voicemail at extension 8500\n;\nexten => 8500,1,VoicemailMain\nexten => 8500,n,Goto(s,6)\n\n    </textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: \"text/x-asterisk\",\n        matchBrackets: true,\n        lineNumbers: true\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-asterisk</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/brainfuck/brainfuck.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Brainfuck mode created by Michael Kaminsky https://github.com/mkaminsky11\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\")\n    mod(require(\"../../lib/codemirror\"))\n  else if (typeof define == \"function\" && define.amd)\n    define([\"../../lib/codemirror\"], mod)\n  else\n    mod(CodeMirror)\n})(function(CodeMirror) {\n  \"use strict\"\n  var reserve = \"><+-.,[]\".split(\"\");\n  /*\n  comments can be either:\n  placed behind lines\n\n        +++    this is a comment\n\n  where reserved characters cannot be used\n  or in a loop\n  [\n    this is ok to use [ ] and stuff\n  ]\n  or preceded by #\n  */\n  CodeMirror.defineMode(\"brainfuck\", function() {\n    return {\n      startState: function() {\n        return {\n          commentLine: false,\n          left: 0,\n          right: 0,\n          commentLoop: false\n        }\n      },\n      token: function(stream, state) {\n        if (stream.eatSpace()) return null\n        if(stream.sol()){\n          state.commentLine = false;\n        }\n        var ch = stream.next().toString();\n        if(reserve.indexOf(ch) !== -1){\n          if(state.commentLine === true){\n            if(stream.eol()){\n              state.commentLine = false;\n            }\n            return \"comment\";\n          }\n          if(ch === \"]\" || ch === \"[\"){\n            if(ch === \"[\"){\n              state.left++;\n            }\n            else{\n              state.right++;\n            }\n            return \"bracket\";\n          }\n          else if(ch === \"+\" || ch === \"-\"){\n            return \"keyword\";\n          }\n          else if(ch === \"<\" || ch === \">\"){\n            return \"atom\";\n          }\n          else if(ch === \".\" || ch === \",\"){\n            return \"def\";\n          }\n        }\n        else{\n          state.commentLine = true;\n          if(stream.eol()){\n            state.commentLine = false;\n          }\n          return \"comment\";\n        }\n        if(stream.eol()){\n          state.commentLine = false;\n        }\n      }\n    };\n  });\nCodeMirror.defineMIME(\"text/x-brainfuck\",\"brainfuck\")\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/brainfuck/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Brainfuck mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"./brainfuck.js\"></script>\n<style>\n\t.CodeMirror { border: 2px inset #dee; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\"></a>\n  </ul>\n</div>\n\n<article>\n<h2>Brainfuck mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n[ This program prints \"Hello World!\" and a newline to the screen, its\n  length is 106 active command characters [it is not the shortest.]\n\n  This loop is a \"comment loop\", it's a simple way of adding a comment\n  to a BF program such that you don't have to worry about any command\n  characters. Any \".\", \",\", \"+\", \"-\", \"&lt;\" and \"&gt;\" characters are simply\n  ignored, the \"[\" and \"]\" characters just have to be balanced.\n]\n+++++ +++               Set Cell #0 to 8\n[\n    &gt;++++               Add 4 to Cell #1; this will always set Cell #1 to 4\n    [                   as the cell will be cleared by the loop\n        &gt;++             Add 2 to Cell #2\n        &gt;+++            Add 3 to Cell #3\n        &gt;+++            Add 3 to Cell #4\n        &gt;+              Add 1 to Cell #5\n        &lt;&lt;&lt;&lt;-           Decrement the loop counter in Cell #1\n    ]                   Loop till Cell #1 is zero; number of iterations is 4\n    &gt;+                  Add 1 to Cell #2\n    &gt;+                  Add 1 to Cell #3\n    &gt;-                  Subtract 1 from Cell #4\n    &gt;&gt;+                 Add 1 to Cell #6\n    [&lt;]                 Move back to the first zero cell you find; this will\n                        be Cell #1 which was cleared by the previous loop\n    &lt;-                  Decrement the loop Counter in Cell #0\n]                       Loop till Cell #0 is zero; number of iterations is 8\n\nThe result of this is:\nCell No :   0   1   2   3   4   5   6\nContents:   0   0  72 104  88  32   8\nPointer :   ^\n\n&gt;&gt;.                     Cell #2 has value 72 which is 'H'\n&gt;---.                   Subtract 3 from Cell #3 to get 101 which is 'e'\n+++++++..+++.           Likewise for 'llo' from Cell #3\n&gt;&gt;.                     Cell #5 is 32 for the space\n&lt;-.                     Subtract 1 from Cell #4 for 87 to give a 'W'\n&lt;.                      Cell #3 was set to 'o' from the end of 'Hello'\n+++.------.--------.    Cell #3 for 'rl' and 'd'\n&gt;&gt;+.                    Add 1 to Cell #5 gives us an exclamation point\n&gt;++.                    And finally a newline from Cell #6\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-brainfuck\"\n      });\n    </script>\n\n    <p>A mode for Brainfuck</p>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-brainfuck</code></p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/clike/clike.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nfunction Context(indented, column, type, info, align, prev) {\n  this.indented = indented;\n  this.column = column;\n  this.type = type;\n  this.info = info;\n  this.align = align;\n  this.prev = prev;\n}\nfunction pushContext(state, col, type, info) {\n  var indent = state.indented;\n  if (state.context && state.context.type == \"statement\" && type != \"statement\")\n    indent = state.context.indented;\n  return state.context = new Context(indent, col, type, info, null, state.context);\n}\nfunction popContext(state) {\n  var t = state.context.type;\n  if (t == \")\" || t == \"]\" || t == \"}\")\n    state.indented = state.context.indented;\n  return state.context = state.context.prev;\n}\n\nfunction typeBefore(stream, state, pos) {\n  if (state.prevToken == \"variable\" || state.prevToken == \"type\") return true;\n  if (/\\S(?:[^- ]>|[*\\]])\\s*$|\\*$/.test(stream.string.slice(0, pos))) return true;\n  if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true;\n}\n\nfunction isTopScope(context) {\n  for (;;) {\n    if (!context || context.type == \"top\") return true;\n    if (context.type == \"}\" && context.prev.info != \"namespace\") return false;\n    context = context.prev;\n  }\n}\n\nCodeMirror.defineMode(\"clike\", function(config, parserConfig) {\n  var indentUnit = config.indentUnit,\n      statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,\n      dontAlignCalls = parserConfig.dontAlignCalls,\n      keywords = parserConfig.keywords || {},\n      types = parserConfig.types || {},\n      builtin = parserConfig.builtin || {},\n      blockKeywords = parserConfig.blockKeywords || {},\n      defKeywords = parserConfig.defKeywords || {},\n      atoms = parserConfig.atoms || {},\n      hooks = parserConfig.hooks || {},\n      multiLineStrings = parserConfig.multiLineStrings,\n      indentStatements = parserConfig.indentStatements !== false,\n      indentSwitch = parserConfig.indentSwitch !== false,\n      namespaceSeparator = parserConfig.namespaceSeparator,\n      isPunctuationChar = parserConfig.isPunctuationChar || /[\\[\\]{}\\(\\),;\\:\\.]/,\n      numberStart = parserConfig.numberStart || /[\\d\\.]/,\n      number = parserConfig.number || /^(?:0x[a-f\\d]+|0b[01]+|(?:\\d+\\.?\\d*|\\.\\d+)(?:e[-+]?\\d+)?)(u|ll?|l|f)?/i,\n      isOperatorChar = parserConfig.isOperatorChar || /[+\\-*&%=<>!?|\\/]/,\n      isIdentifierChar = parserConfig.isIdentifierChar || /[\\w\\$_\\xa1-\\uffff]/,\n      // An optional function that takes a {string} token and returns true if it\n      // should be treated as a builtin.\n      isReservedIdentifier = parserConfig.isReservedIdentifier || false;\n\n  var curPunc, isDefKeyword;\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (hooks[ch]) {\n      var result = hooks[ch](stream, state);\n      if (result !== false) return result;\n    }\n    if (ch == '\"' || ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    }\n    if (numberStart.test(ch)) {\n      stream.backUp(1)\n      if (stream.match(number)) return \"number\"\n      stream.next()\n    }\n    if (isPunctuationChar.test(ch)) {\n      curPunc = ch;\n      return null;\n    }\n    if (ch == \"/\") {\n      if (stream.eat(\"*\")) {\n        state.tokenize = tokenComment;\n        return tokenComment(stream, state);\n      }\n      if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n    }\n    if (isOperatorChar.test(ch)) {\n      while (!stream.match(/^\\/[\\/*]/, false) && stream.eat(isOperatorChar)) {}\n      return \"operator\";\n    }\n    stream.eatWhile(isIdentifierChar);\n    if (namespaceSeparator) while (stream.match(namespaceSeparator))\n      stream.eatWhile(isIdentifierChar);\n\n    var cur = stream.current();\n    if (contains(keywords, cur)) {\n      if (contains(blockKeywords, cur)) curPunc = \"newstatement\";\n      if (contains(defKeywords, cur)) isDefKeyword = true;\n      return \"keyword\";\n    }\n    if (contains(types, cur)) return \"type\";\n    if (contains(builtin, cur)\n        || (isReservedIdentifier && isReservedIdentifier(cur))) {\n      if (contains(blockKeywords, cur)) curPunc = \"newstatement\";\n      return \"builtin\";\n    }\n    if (contains(atoms, cur)) return \"atom\";\n    return \"variable\";\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {end = true; break;}\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (end || !(escaped || multiLineStrings))\n        state.tokenize = null;\n      return \"string\";\n    };\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = null;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n\n  function maybeEOL(stream, state) {\n    if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context))\n      state.typeAtEndOfLine = typeBefore(stream, state, stream.pos)\n  }\n\n  // Interface\n\n  return {\n    startState: function(basecolumn) {\n      return {\n        tokenize: null,\n        context: new Context((basecolumn || 0) - indentUnit, 0, \"top\", null, false),\n        indented: 0,\n        startOfLine: true,\n        prevToken: null\n      };\n    },\n\n    token: function(stream, state) {\n      var ctx = state.context;\n      if (stream.sol()) {\n        if (ctx.align == null) ctx.align = false;\n        state.indented = stream.indentation();\n        state.startOfLine = true;\n      }\n      if (stream.eatSpace()) { maybeEOL(stream, state); return null; }\n      curPunc = isDefKeyword = null;\n      var style = (state.tokenize || tokenBase)(stream, state);\n      if (style == \"comment\" || style == \"meta\") return style;\n      if (ctx.align == null) ctx.align = true;\n\n      if (curPunc == \";\" || curPunc == \":\" || (curPunc == \",\" && stream.match(/^\\s*(?:\\/\\/.*)?$/, false)))\n        while (state.context.type == \"statement\") popContext(state);\n      else if (curPunc == \"{\") pushContext(state, stream.column(), \"}\");\n      else if (curPunc == \"[\") pushContext(state, stream.column(), \"]\");\n      else if (curPunc == \"(\") pushContext(state, stream.column(), \")\");\n      else if (curPunc == \"}\") {\n        while (ctx.type == \"statement\") ctx = popContext(state);\n        if (ctx.type == \"}\") ctx = popContext(state);\n        while (ctx.type == \"statement\") ctx = popContext(state);\n      }\n      else if (curPunc == ctx.type) popContext(state);\n      else if (indentStatements &&\n               (((ctx.type == \"}\" || ctx.type == \"top\") && curPunc != \";\") ||\n                (ctx.type == \"statement\" && curPunc == \"newstatement\"))) {\n        pushContext(state, stream.column(), \"statement\", stream.current());\n      }\n\n      if (style == \"variable\" &&\n          ((state.prevToken == \"def\" ||\n            (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) &&\n             isTopScope(state.context) && stream.match(/^\\s*\\(/, false)))))\n        style = \"def\";\n\n      if (hooks.token) {\n        var result = hooks.token(stream, state, style);\n        if (result !== undefined) style = result;\n      }\n\n      if (style == \"def\" && parserConfig.styleDefs === false) style = \"variable\";\n\n      state.startOfLine = false;\n      state.prevToken = isDefKeyword ? \"def\" : style || curPunc;\n      maybeEOL(stream, state);\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine && isTopScope(state.context))\n        return CodeMirror.Pass;\n      var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);\n      var closing = firstChar == ctx.type;\n      if (ctx.type == \"statement\" && firstChar == \"}\") ctx = ctx.prev;\n      if (parserConfig.dontIndentStatements)\n        while (ctx.type == \"statement\" && parserConfig.dontIndentStatements.test(ctx.info))\n          ctx = ctx.prev\n      if (hooks.indent) {\n        var hook = hooks.indent(state, ctx, textAfter, indentUnit);\n        if (typeof hook == \"number\") return hook\n      }\n      var switchBlock = ctx.prev && ctx.prev.info == \"switch\";\n      if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {\n        while (ctx.type != \"top\" && ctx.type != \"}\") ctx = ctx.prev\n        return ctx.indented\n      }\n      if (ctx.type == \"statement\")\n        return ctx.indented + (firstChar == \"{\" ? 0 : statementIndentUnit);\n      if (ctx.align && (!dontAlignCalls || ctx.type != \")\"))\n        return ctx.column + (closing ? 0 : 1);\n      if (ctx.type == \")\" && !closing)\n        return ctx.indented + statementIndentUnit;\n\n      return ctx.indented + (closing ? 0 : indentUnit) +\n        (!closing && switchBlock && !/^(?:case|default)\\b/.test(textAfter) ? indentUnit : 0);\n    },\n\n    electricInput: indentSwitch ? /^\\s*(?:case .*?:|default:|\\{\\}?|\\})$/ : /^\\s*[{}]$/,\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    blockCommentContinue: \" * \",\n    lineComment: \"//\",\n    fold: \"brace\"\n  };\n});\n\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n  function contains(words, word) {\n    if (typeof words === \"function\") {\n      return words(word);\n    } else {\n      return words.propertyIsEnumerable(word);\n    }\n  }\n  var cKeywords = \"auto if break case register continue return default do sizeof \" +\n    \"static else struct switch extern typedef union for goto while enum const \" +\n    \"volatile inline restrict asm fortran\";\n\n  // Keywords from https://en.cppreference.com/w/cpp/keyword includes C++20.\n  var cppKeywords = \"alignas alignof and and_eq audit axiom bitand bitor catch \" +\n  \"class compl concept constexpr const_cast decltype delete dynamic_cast \" +\n  \"explicit export final friend import module mutable namespace new noexcept \" +\n  \"not not_eq operator or or_eq override private protected public \" +\n  \"reinterpret_cast requires static_assert static_cast template this \" +\n  \"thread_local throw try typeid typename using virtual xor xor_eq\";\n\n  var objCKeywords = \"bycopy byref in inout oneway out self super atomic nonatomic retain copy \" +\n  \"readwrite readonly strong weak assign typeof nullable nonnull null_resettable _cmd \" +\n  \"@interface @implementation @end @protocol @encode @property @synthesize @dynamic @class \" +\n  \"@public @package @private @protected @required @optional @try @catch @finally @import \" +\n  \"@selector @encode @defs @synchronized @autoreleasepool @compatibility_alias @available\";\n\n  var objCBuiltins = \"FOUNDATION_EXPORT FOUNDATION_EXTERN NS_INLINE NS_FORMAT_FUNCTION \" +\n  \" NS_RETURNS_RETAINEDNS_ERROR_ENUM NS_RETURNS_NOT_RETAINED NS_RETURNS_INNER_POINTER \" +\n  \"NS_DESIGNATED_INITIALIZER NS_ENUM NS_OPTIONS NS_REQUIRES_NIL_TERMINATION \" +\n  \"NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_SWIFT_NAME NS_REFINED_FOR_SWIFT\"\n\n  // Do not use this. Use the cTypes function below. This is global just to avoid\n  // excessive calls when cTypes is being called multiple times during a parse.\n  var basicCTypes = words(\"int long char short double float unsigned signed \" +\n    \"void bool\");\n\n  // Do not use this. Use the objCTypes function below. This is global just to avoid\n  // excessive calls when objCTypes is being called multiple times during a parse.\n  var basicObjCTypes = words(\"SEL instancetype id Class Protocol BOOL\");\n\n  // Returns true if identifier is a \"C\" type.\n  // C type is defined as those that are reserved by the compiler (basicTypes),\n  // and those that end in _t (Reserved by POSIX for types)\n  // http://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html\n  function cTypes(identifier) {\n    return contains(basicCTypes, identifier) || /.+_t$/.test(identifier);\n  }\n\n  // Returns true if identifier is a \"Objective C\" type.\n  function objCTypes(identifier) {\n    return cTypes(identifier) || contains(basicObjCTypes, identifier);\n  }\n\n  var cBlockKeywords = \"case do else for if switch while struct enum union\";\n  var cDefKeywords = \"struct enum union\";\n\n  function cppHook(stream, state) {\n    if (!state.startOfLine) return false\n    for (var ch, next = null; ch = stream.peek();) {\n      if (ch == \"\\\\\" && stream.match(/^.$/)) {\n        next = cppHook\n        break\n      } else if (ch == \"/\" && stream.match(/^\\/[\\/\\*]/, false)) {\n        break\n      }\n      stream.next()\n    }\n    state.tokenize = next\n    return \"meta\"\n  }\n\n  function pointerHook(_stream, state) {\n    if (state.prevToken == \"type\") return \"type\";\n    return false;\n  }\n\n  // For C and C++ (and ObjC): identifiers starting with __\n  // or _ followed by a capital letter are reserved for the compiler.\n  function cIsReservedIdentifier(token) {\n    if (!token || token.length < 2) return false;\n    if (token[0] != '_') return false;\n    return (token[1] == '_') || (token[1] !== token[1].toLowerCase());\n  }\n\n  function cpp14Literal(stream) {\n    stream.eatWhile(/[\\w\\.']/);\n    return \"number\";\n  }\n\n  function cpp11StringHook(stream, state) {\n    stream.backUp(1);\n    // Raw strings.\n    if (stream.match(/^(?:R|u8R|uR|UR|LR)/)) {\n      var match = stream.match(/^\"([^\\s\\\\()]{0,16})\\(/);\n      if (!match) {\n        return false;\n      }\n      state.cpp11RawStringDelim = match[1];\n      state.tokenize = tokenRawString;\n      return tokenRawString(stream, state);\n    }\n    // Unicode strings/chars.\n    if (stream.match(/^(?:u8|u|U|L)/)) {\n      if (stream.match(/^[\"']/, /* eat */ false)) {\n        return \"string\";\n      }\n      return false;\n    }\n    // Ignore this hook.\n    stream.next();\n    return false;\n  }\n\n  function cppLooksLikeConstructor(word) {\n    var lastTwo = /(\\w+)::~?(\\w+)$/.exec(word);\n    return lastTwo && lastTwo[1] == lastTwo[2];\n  }\n\n  // C#-style strings where \"\" escapes a quote.\n  function tokenAtString(stream, state) {\n    var next;\n    while ((next = stream.next()) != null) {\n      if (next == '\"' && !stream.eat('\"')) {\n        state.tokenize = null;\n        break;\n      }\n    }\n    return \"string\";\n  }\n\n  // C++11 raw string literal is <prefix>\"<delim>( anything )<delim>\", where\n  // <delim> can be a string up to 16 characters long.\n  function tokenRawString(stream, state) {\n    // Escape characters that have special regex meanings.\n    var delim = state.cpp11RawStringDelim.replace(/[^\\w\\s]/g, '\\\\$&');\n    var match = stream.match(new RegExp(\".*?\\\\)\" + delim + '\"'));\n    if (match)\n      state.tokenize = null;\n    else\n      stream.skipToEnd();\n    return \"string\";\n  }\n\n  function def(mimes, mode) {\n    if (typeof mimes == \"string\") mimes = [mimes];\n    var words = [];\n    function add(obj) {\n      if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))\n        words.push(prop);\n    }\n    add(mode.keywords);\n    add(mode.types);\n    add(mode.builtin);\n    add(mode.atoms);\n    if (words.length) {\n      mode.helperType = mimes[0];\n      CodeMirror.registerHelper(\"hintWords\", mimes[0], words);\n    }\n\n    for (var i = 0; i < mimes.length; ++i)\n      CodeMirror.defineMIME(mimes[i], mode);\n  }\n\n  def([\"text/x-csrc\", \"text/x-c\", \"text/x-chdr\"], {\n    name: \"clike\",\n    keywords: words(cKeywords),\n    types: cTypes,\n    blockKeywords: words(cBlockKeywords),\n    defKeywords: words(cDefKeywords),\n    typeFirstDefinitions: true,\n    atoms: words(\"NULL true false\"),\n    isReservedIdentifier: cIsReservedIdentifier,\n    hooks: {\n      \"#\": cppHook,\n      \"*\": pointerHook,\n    },\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  def([\"text/x-c++src\", \"text/x-c++hdr\"], {\n    name: \"clike\",\n    keywords: words(cKeywords + \" \" + cppKeywords),\n    types: cTypes,\n    blockKeywords: words(cBlockKeywords + \" class try catch\"),\n    defKeywords: words(cDefKeywords + \" class namespace\"),\n    typeFirstDefinitions: true,\n    atoms: words(\"true false NULL nullptr\"),\n    dontIndentStatements: /^template$/,\n    isIdentifierChar: /[\\w\\$_~\\xa1-\\uffff]/,\n    isReservedIdentifier: cIsReservedIdentifier,\n    hooks: {\n      \"#\": cppHook,\n      \"*\": pointerHook,\n      \"u\": cpp11StringHook,\n      \"U\": cpp11StringHook,\n      \"L\": cpp11StringHook,\n      \"R\": cpp11StringHook,\n      \"0\": cpp14Literal,\n      \"1\": cpp14Literal,\n      \"2\": cpp14Literal,\n      \"3\": cpp14Literal,\n      \"4\": cpp14Literal,\n      \"5\": cpp14Literal,\n      \"6\": cpp14Literal,\n      \"7\": cpp14Literal,\n      \"8\": cpp14Literal,\n      \"9\": cpp14Literal,\n      token: function(stream, state, style) {\n        if (style == \"variable\" && stream.peek() == \"(\" &&\n            (state.prevToken == \";\" || state.prevToken == null ||\n             state.prevToken == \"}\") &&\n            cppLooksLikeConstructor(stream.current()))\n          return \"def\";\n      }\n    },\n    namespaceSeparator: \"::\",\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  def(\"text/x-java\", {\n    name: \"clike\",\n    keywords: words(\"abstract assert break case catch class const continue default \" +\n                    \"do else enum extends final finally for goto if implements import \" +\n                    \"instanceof interface native new package private protected public \" +\n                    \"return static strictfp super switch synchronized this throw throws transient \" +\n                    \"try volatile while @interface\"),\n    types: words(\"var byte short int long float double boolean char void Boolean Byte Character Double Float \" +\n                 \"Integer Long Number Object Short String StringBuffer StringBuilder Void\"),\n    blockKeywords: words(\"catch class do else finally for if switch try while\"),\n    defKeywords: words(\"class interface enum @interface\"),\n    typeFirstDefinitions: true,\n    atoms: words(\"true false null\"),\n    number: /^(?:0x[a-f\\d_]+|0b[01_]+|(?:[\\d_]+\\.?\\d*|\\.\\d+)(?:e[-+]?[\\d_]+)?)(u|ll?|l|f)?/i,\n    hooks: {\n      \"@\": function(stream) {\n        // Don't match the @interface keyword.\n        if (stream.match('interface', false)) return false;\n\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"meta\";\n      },\n      '\"': function(stream, state) {\n        if (!stream.match(/\"\"$/)) return false;\n        state.tokenize = tokenTripleString;\n        return state.tokenize(stream, state);\n      }\n    },\n    modeProps: {fold: [\"brace\", \"import\"]}\n  });\n\n  def(\"text/x-csharp\", {\n    name: \"clike\",\n    keywords: words(\"abstract as async await base break case catch checked class const continue\" +\n                    \" default delegate do else enum event explicit extern finally fixed for\" +\n                    \" foreach goto if implicit in init interface internal is lock namespace new\" +\n                    \" operator out override params private protected public readonly record ref required return sealed\" +\n                    \" sizeof stackalloc static struct switch this throw try typeof unchecked\" +\n                    \" unsafe using virtual void volatile while add alias ascending descending dynamic from get\" +\n                    \" global group into join let orderby partial remove select set value var yield\"),\n    types: words(\"Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func\" +\n                 \" Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32\" +\n                 \" UInt64 bool byte char decimal double short int long object\"  +\n                 \" sbyte float string ushort uint ulong\"),\n    blockKeywords: words(\"catch class do else finally for foreach if struct switch try while\"),\n    defKeywords: words(\"class interface namespace record struct var\"),\n    typeFirstDefinitions: true,\n    atoms: words(\"true false null\"),\n    hooks: {\n      \"@\": function(stream, state) {\n        if (stream.eat('\"')) {\n          state.tokenize = tokenAtString;\n          return tokenAtString(stream, state);\n        }\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"meta\";\n      }\n    }\n  });\n\n  function tokenTripleString(stream, state) {\n    var escaped = false;\n    while (!stream.eol()) {\n      if (!escaped && stream.match('\"\"\"')) {\n        state.tokenize = null;\n        break;\n      }\n      escaped = stream.next() == \"\\\\\" && !escaped;\n    }\n    return \"string\";\n  }\n\n  function tokenNestedComment(depth) {\n    return function (stream, state) {\n      var ch\n      while (ch = stream.next()) {\n        if (ch == \"*\" && stream.eat(\"/\")) {\n          if (depth == 1) {\n            state.tokenize = null\n            break\n          } else {\n            state.tokenize = tokenNestedComment(depth - 1)\n            return state.tokenize(stream, state)\n          }\n        } else if (ch == \"/\" && stream.eat(\"*\")) {\n          state.tokenize = tokenNestedComment(depth + 1)\n          return state.tokenize(stream, state)\n        }\n      }\n      return \"comment\"\n    }\n  }\n\n  def(\"text/x-scala\", {\n    name: \"clike\",\n    keywords: words(\n      /* scala */\n      \"abstract case catch class def do else extends final finally for forSome if \" +\n      \"implicit import lazy match new null object override package private protected return \" +\n      \"sealed super this throw trait try type val var while with yield _ \" +\n\n      /* package scala */\n      \"assert assume require print println printf readLine readBoolean readByte readShort \" +\n      \"readChar readInt readLong readFloat readDouble\"\n    ),\n    types: words(\n      \"AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either \" +\n      \"Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable \" +\n      \"Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering \" +\n      \"Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder \" +\n      \"StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector \" +\n\n      /* package java.lang */\n      \"Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable \" +\n      \"Compiler Double Exception Float Integer Long Math Number Object Package Pair Process \" +\n      \"Runtime Runnable SecurityManager Short StackTraceElement StrictMath String \" +\n      \"StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void\"\n    ),\n    multiLineStrings: true,\n    blockKeywords: words(\"catch class enum do else finally for forSome if match switch try while\"),\n    defKeywords: words(\"class enum def object package trait type val var\"),\n    atoms: words(\"true false null\"),\n    indentStatements: false,\n    indentSwitch: false,\n    isOperatorChar: /[+\\-*&%=<>!?|\\/#:@]/,\n    hooks: {\n      \"@\": function(stream) {\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"meta\";\n      },\n      '\"': function(stream, state) {\n        if (!stream.match('\"\"')) return false;\n        state.tokenize = tokenTripleString;\n        return state.tokenize(stream, state);\n      },\n      \"'\": function(stream) {\n        if (stream.match(/^(\\\\[^'\\s]+|[^\\\\'])'/)) return \"string-2\"\n        stream.eatWhile(/[\\w\\$_\\xa1-\\uffff]/);\n        return \"atom\";\n      },\n      \"=\": function(stream, state) {\n        var cx = state.context\n        if (cx.type == \"}\" && cx.align && stream.eat(\">\")) {\n          state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)\n          return \"operator\"\n        } else {\n          return false\n        }\n      },\n\n      \"/\": function(stream, state) {\n        if (!stream.eat(\"*\")) return false\n        state.tokenize = tokenNestedComment(1)\n        return state.tokenize(stream, state)\n      }\n    },\n    modeProps: {closeBrackets: {pairs: '()[]{}\"\"', triples: '\"'}}\n  });\n\n  function tokenKotlinString(tripleString){\n    return function (stream, state) {\n      var escaped = false, next, end = false;\n      while (!stream.eol()) {\n        if (!tripleString && !escaped && stream.match('\"') ) {end = true; break;}\n        if (tripleString && stream.match('\"\"\"')) {end = true; break;}\n        next = stream.next();\n        if(!escaped && next == \"$\" && stream.match('{'))\n          stream.skipTo(\"}\");\n        escaped = !escaped && next == \"\\\\\" && !tripleString;\n      }\n      if (end || !tripleString)\n        state.tokenize = null;\n      return \"string\";\n    }\n  }\n\n  def(\"text/x-kotlin\", {\n    name: \"clike\",\n    keywords: words(\n      /*keywords*/\n      \"package as typealias class interface this super val operator \" +\n      \"var fun for is in This throw return annotation \" +\n      \"break continue object if else while do try when !in !is as? \" +\n\n      /*soft keywords*/\n      \"file import where by get set abstract enum open inner override private public internal \" +\n      \"protected catch finally out final vararg reified dynamic companion constructor init \" +\n      \"sealed field property receiver param sparam lateinit data inline noinline tailrec \" +\n      \"external annotation crossinline const operator infix suspend actual expect setparam value\"\n    ),\n    types: words(\n      /* package java.lang */\n      \"Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable \" +\n      \"Compiler Double Exception Float Integer Long Math Number Object Package Pair Process \" +\n      \"Runtime Runnable SecurityManager Short StackTraceElement StrictMath String \" +\n      \"StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray \" +\n      \"ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy \" +\n      \"LazyThreadSafetyMode LongArray Nothing ShortArray Unit\"\n    ),\n    intendSwitch: false,\n    indentStatements: false,\n    multiLineStrings: true,\n    number: /^(?:0x[a-f\\d_]+|0b[01_]+|(?:[\\d_]+(\\.\\d+)?|\\.\\d+)(?:e[-+]?[\\d_]+)?)(u|ll?|l|f)?/i,\n    blockKeywords: words(\"catch class do else finally for if where try while enum\"),\n    defKeywords: words(\"class val var object interface fun\"),\n    atoms: words(\"true false null this\"),\n    hooks: {\n      \"@\": function(stream) {\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"meta\";\n      },\n      '*': function(_stream, state) {\n        return state.prevToken == '.' ? 'variable' : 'operator';\n      },\n      '\"': function(stream, state) {\n        state.tokenize = tokenKotlinString(stream.match('\"\"'));\n        return state.tokenize(stream, state);\n      },\n      \"/\": function(stream, state) {\n        if (!stream.eat(\"*\")) return false;\n        state.tokenize = tokenNestedComment(1);\n        return state.tokenize(stream, state)\n      },\n      indent: function(state, ctx, textAfter, indentUnit) {\n        var firstChar = textAfter && textAfter.charAt(0);\n        if ((state.prevToken == \"}\" || state.prevToken == \")\") && textAfter == \"\")\n          return state.indented;\n        if ((state.prevToken == \"operator\" && textAfter != \"}\" && state.context.type != \"}\") ||\n          state.prevToken == \"variable\" && firstChar == \".\" ||\n          (state.prevToken == \"}\" || state.prevToken == \")\") && firstChar == \".\")\n          return indentUnit * 2 + ctx.indented;\n        if (ctx.align && ctx.type == \"}\")\n          return ctx.indented + (state.context.type == (textAfter || \"\").charAt(0) ? 0 : indentUnit);\n      }\n    },\n    modeProps: {closeBrackets: {triples: '\"'}}\n  });\n\n  def([\"x-shader/x-vertex\", \"x-shader/x-fragment\"], {\n    name: \"clike\",\n    keywords: words(\"sampler1D sampler2D sampler3D samplerCube \" +\n                    \"sampler1DShadow sampler2DShadow \" +\n                    \"const attribute uniform varying \" +\n                    \"break continue discard return \" +\n                    \"for while do if else struct \" +\n                    \"in out inout\"),\n    types: words(\"float int bool void \" +\n                 \"vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 \" +\n                 \"mat2 mat3 mat4\"),\n    blockKeywords: words(\"for while do if else struct\"),\n    builtin: words(\"radians degrees sin cos tan asin acos atan \" +\n                    \"pow exp log exp2 sqrt inversesqrt \" +\n                    \"abs sign floor ceil fract mod min max clamp mix step smoothstep \" +\n                    \"length distance dot cross normalize ftransform faceforward \" +\n                    \"reflect refract matrixCompMult \" +\n                    \"lessThan lessThanEqual greaterThan greaterThanEqual \" +\n                    \"equal notEqual any all not \" +\n                    \"texture1D texture1DProj texture1DLod texture1DProjLod \" +\n                    \"texture2D texture2DProj texture2DLod texture2DProjLod \" +\n                    \"texture3D texture3DProj texture3DLod texture3DProjLod \" +\n                    \"textureCube textureCubeLod \" +\n                    \"shadow1D shadow2D shadow1DProj shadow2DProj \" +\n                    \"shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod \" +\n                    \"dFdx dFdy fwidth \" +\n                    \"noise1 noise2 noise3 noise4\"),\n    atoms: words(\"true false \" +\n                \"gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex \" +\n                \"gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 \" +\n                \"gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 \" +\n                \"gl_FogCoord gl_PointCoord \" +\n                \"gl_Position gl_PointSize gl_ClipVertex \" +\n                \"gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor \" +\n                \"gl_TexCoord gl_FogFragCoord \" +\n                \"gl_FragCoord gl_FrontFacing \" +\n                \"gl_FragData gl_FragDepth \" +\n                \"gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix \" +\n                \"gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse \" +\n                \"gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse \" +\n                \"gl_TextureMatrixTranspose gl_ModelViewMatrixInverseTranspose \" +\n                \"gl_ProjectionMatrixInverseTranspose \" +\n                \"gl_ModelViewProjectionMatrixInverseTranspose \" +\n                \"gl_TextureMatrixInverseTranspose \" +\n                \"gl_NormalScale gl_DepthRange gl_ClipPlane \" +\n                \"gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel \" +\n                \"gl_FrontLightModelProduct gl_BackLightModelProduct \" +\n                \"gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ \" +\n                \"gl_FogParameters \" +\n                \"gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords \" +\n                \"gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats \" +\n                \"gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits \" +\n                \"gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits \" +\n                \"gl_MaxDrawBuffers\"),\n    indentSwitch: false,\n    hooks: {\"#\": cppHook},\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  def(\"text/x-nesc\", {\n    name: \"clike\",\n    keywords: words(cKeywords + \" as atomic async call command component components configuration event generic \" +\n                    \"implementation includes interface module new norace nx_struct nx_union post provides \" +\n                    \"signal task uses abstract extends\"),\n    types: cTypes,\n    blockKeywords: words(cBlockKeywords),\n    atoms: words(\"null true false\"),\n    hooks: {\"#\": cppHook},\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  def(\"text/x-objectivec\", {\n    name: \"clike\",\n    keywords: words(cKeywords + \" \" + objCKeywords),\n    types: objCTypes,\n    builtin: words(objCBuiltins),\n    blockKeywords: words(cBlockKeywords + \" @synthesize @try @catch @finally @autoreleasepool @synchronized\"),\n    defKeywords: words(cDefKeywords + \" @interface @implementation @protocol @class\"),\n    dontIndentStatements: /^@.*$/,\n    typeFirstDefinitions: true,\n    atoms: words(\"YES NO NULL Nil nil true false nullptr\"),\n    isReservedIdentifier: cIsReservedIdentifier,\n    hooks: {\n      \"#\": cppHook,\n      \"*\": pointerHook,\n    },\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  def(\"text/x-objectivec++\", {\n    name: \"clike\",\n    keywords: words(cKeywords + \" \" + objCKeywords + \" \" + cppKeywords),\n    types: objCTypes,\n    builtin: words(objCBuiltins),\n    blockKeywords: words(cBlockKeywords + \" @synthesize @try @catch @finally @autoreleasepool @synchronized class try catch\"),\n    defKeywords: words(cDefKeywords + \" @interface @implementation @protocol @class class namespace\"),\n    dontIndentStatements: /^@.*$|^template$/,\n    typeFirstDefinitions: true,\n    atoms: words(\"YES NO NULL Nil nil true false nullptr\"),\n    isReservedIdentifier: cIsReservedIdentifier,\n    hooks: {\n      \"#\": cppHook,\n      \"*\": pointerHook,\n      \"u\": cpp11StringHook,\n      \"U\": cpp11StringHook,\n      \"L\": cpp11StringHook,\n      \"R\": cpp11StringHook,\n      \"0\": cpp14Literal,\n      \"1\": cpp14Literal,\n      \"2\": cpp14Literal,\n      \"3\": cpp14Literal,\n      \"4\": cpp14Literal,\n      \"5\": cpp14Literal,\n      \"6\": cpp14Literal,\n      \"7\": cpp14Literal,\n      \"8\": cpp14Literal,\n      \"9\": cpp14Literal,\n      token: function(stream, state, style) {\n        if (style == \"variable\" && stream.peek() == \"(\" &&\n            (state.prevToken == \";\" || state.prevToken == null ||\n             state.prevToken == \"}\") &&\n            cppLooksLikeConstructor(stream.current()))\n          return \"def\";\n      }\n    },\n    namespaceSeparator: \"::\",\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  def(\"text/x-squirrel\", {\n    name: \"clike\",\n    keywords: words(\"base break clone continue const default delete enum extends function in class\" +\n                    \" foreach local resume return this throw typeof yield constructor instanceof static\"),\n    types: cTypes,\n    blockKeywords: words(\"case catch class else for foreach if switch try while\"),\n    defKeywords: words(\"function local class\"),\n    typeFirstDefinitions: true,\n    atoms: words(\"true false null\"),\n    hooks: {\"#\": cppHook},\n    modeProps: {fold: [\"brace\", \"include\"]}\n  });\n\n  // Ceylon Strings need to deal with interpolation\n  var stringTokenizer = null;\n  function tokenCeylonString(type) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while (!stream.eol()) {\n        if (!escaped && stream.match('\"') &&\n              (type == \"single\" || stream.match('\"\"'))) {\n          end = true;\n          break;\n        }\n        if (!escaped && stream.match('``')) {\n          stringTokenizer = tokenCeylonString(type);\n          end = true;\n          break;\n        }\n        next = stream.next();\n        escaped = type == \"single\" && !escaped && next == \"\\\\\";\n      }\n      if (end)\n          state.tokenize = null;\n      return \"string\";\n    }\n  }\n\n  def(\"text/x-ceylon\", {\n    name: \"clike\",\n    keywords: words(\"abstracts alias assembly assert assign break case catch class continue dynamic else\" +\n                    \" exists extends finally for function given if import in interface is let module new\" +\n                    \" nonempty object of out outer package return satisfies super switch then this throw\" +\n                    \" try value void while\"),\n    types: function(word) {\n        // In Ceylon all identifiers that start with an uppercase are types\n        var first = word.charAt(0);\n        return (first === first.toUpperCase() && first !== first.toLowerCase());\n    },\n    blockKeywords: words(\"case catch class dynamic else finally for function if interface module new object switch try while\"),\n    defKeywords: words(\"class dynamic function interface module object package value\"),\n    builtin: words(\"abstract actual aliased annotation by default deprecated doc final formal late license\" +\n                   \" native optional sealed see serializable shared suppressWarnings tagged throws variable\"),\n    isPunctuationChar: /[\\[\\]{}\\(\\),;\\:\\.`]/,\n    isOperatorChar: /[+\\-*&%=<>!?|^~:\\/]/,\n    numberStart: /[\\d#$]/,\n    number: /^(?:#[\\da-fA-F_]+|\\$[01_]+|[\\d_]+[kMGTPmunpf]?|[\\d_]+\\.[\\d_]+(?:[eE][-+]?\\d+|[kMGTPmunpf]|)|)/i,\n    multiLineStrings: true,\n    typeFirstDefinitions: true,\n    atoms: words(\"true false null larger smaller equal empty finished\"),\n    indentSwitch: false,\n    styleDefs: false,\n    hooks: {\n      \"@\": function(stream) {\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"meta\";\n      },\n      '\"': function(stream, state) {\n          state.tokenize = tokenCeylonString(stream.match('\"\"') ? \"triple\" : \"single\");\n          return state.tokenize(stream, state);\n        },\n      '`': function(stream, state) {\n          if (!stringTokenizer || !stream.match('`')) return false;\n          state.tokenize = stringTokenizer;\n          stringTokenizer = null;\n          return state.tokenize(stream, state);\n        },\n      \"'\": function(stream) {\n        stream.eatWhile(/[\\w\\$_\\xa1-\\uffff]/);\n        return \"atom\";\n      },\n      token: function(_stream, state, style) {\n          if ((style == \"variable\" || style == \"type\") &&\n              state.prevToken == \".\") {\n            return \"variable-2\";\n          }\n        }\n    },\n    modeProps: {\n        fold: [\"brace\", \"import\"],\n        closeBrackets: {triples: '\"'}\n    }\n  });\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/clike/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: C-like mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<link rel=\"stylesheet\" href=\"../../addon/hint/show-hint.css\">\n<script src=\"../../addon/hint/show-hint.js\"></script>\n<script src=\"clike.js\"></script>\n<style>.CodeMirror {border: 2px inset #dee;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">C-like</a>\n  </ul>\n</div>\n\n<article>\n<h2>C-like mode</h2>\n\n<div><textarea id=\"c-code\">\n/* C demo code */\n\n#include <zmq.h>\n#include <pthread.h>\n#include <semaphore.h>\n#include <time.h>\n#include <stdio.h>\n#include <fcntl.h>\n#include <malloc.h>\n\ntypedef struct {\n  void* arg_socket;\n  zmq_msg_t* arg_msg;\n  char* arg_string;\n  unsigned long arg_len;\n  int arg_int, arg_command;\n\n  int signal_fd;\n  int pad;\n  void* context;\n  sem_t sem;\n} acl_zmq_context;\n\n#define p(X) (context->arg_##X)\n\nvoid* zmq_thread(void* context_pointer) {\n  acl_zmq_context* context = (acl_zmq_context*)context_pointer;\n  char ok = 'K', err = 'X';\n  int res;\n\n  while (1) {\n    while ((res = sem_wait(&amp;context->sem)) == EINTR);\n    if (res) {write(context->signal_fd, &amp;err, 1); goto cleanup;}\n    switch(p(command)) {\n    case 0: goto cleanup;\n    case 1: p(socket) = zmq_socket(context->context, p(int)); break;\n    case 2: p(int) = zmq_close(p(socket)); break;\n    case 3: p(int) = zmq_bind(p(socket), p(string)); break;\n    case 4: p(int) = zmq_connect(p(socket), p(string)); break;\n    case 5: p(int) = zmq_getsockopt(p(socket), p(int), (void*)p(string), &amp;p(len)); break;\n    case 6: p(int) = zmq_setsockopt(p(socket), p(int), (void*)p(string), p(len)); break;\n    case 7: p(int) = zmq_send(p(socket), p(msg), p(int)); break;\n    case 8: p(int) = zmq_recv(p(socket), p(msg), p(int)); break;\n    case 9: p(int) = zmq_poll(p(socket), p(int), p(len)); break;\n    }\n    p(command) = errno;\n    write(context->signal_fd, &amp;ok, 1);\n  }\n cleanup:\n  close(context->signal_fd);\n  free(context_pointer);\n  return 0;\n}\n\nvoid* zmq_thread_init(void* zmq_context, int signal_fd) {\n  acl_zmq_context* context = malloc(sizeof(acl_zmq_context));\n  pthread_t thread;\n\n  context->context = zmq_context;\n  context->signal_fd = signal_fd;\n  sem_init(&amp;context->sem, 1, 0);\n  pthread_create(&amp;thread, 0, &amp;zmq_thread, context);\n  pthread_detach(thread);\n  return context;\n}\n</textarea></div>\n\n<h2>C++ example</h2>\n\n<div><textarea id=\"cpp-code\">\n#include <iostream>\n#include \"mystuff/util.h\"\n\nnamespace {\nenum Enum {\n  VAL1, VAL2, VAL3\n};\n\nchar32_t unicode_string = U\"\\U0010FFFF\";\nstring raw_string = R\"delim(anything\nyou\nwant)delim\";\n\nint Helper(const MyType& param) {\n  return 0;\n}\n} // namespace\n\nclass ForwardDec;\n\ntemplate <class T, class V>\nclass Class : public BaseClass {\n  const MyType<T, V> member_;\n\n public:\n  const MyType<T, V>& Method() const {\n    return member_;\n  }\n\n  void Method2(MyType<T, V>* value);\n}\n\ntemplate <class T, class V>\nvoid Class::Method2(MyType<T, V>* value) {\n  std::out << 1 >> method();\n  value->Method3(member_);\n  member_ = value;\n}\n</textarea></div>\n\n<h2>Objective-C example</h2>\n\n<div><textarea id=\"objectivec-code\">\n/*\nThis is a longer comment\nThat spans two lines\n*/\n\n#import \"MyClass.h\"\n#import <AFramework/AFramework.h>\n@import BFrameworkModule;\n\nNS_ENUM(SomeValues) {\n  aValue = 1;\n};\n\n// A Class Extension with some properties\n@interface MyClass ()<AProtocol>\n@property(atomic, readwrite, assign) NSInteger anInt;\n@property(nonatomic, strong, nullable) NSString *aString;\n@end\n\n@implementation YourAppDelegate\n\n- (instancetype)initWithString:(NSString *)aStringVar {\n  if ((self = [super init])) {\n    aString = aStringVar;\n  }\n  return self;\n}\n\n- (BOOL)doSomething:(float)progress {\n  NSString *myString = @\"This is a ObjC string %f \";\n  myString = [[NSString stringWithFormat:myString, progress] stringByAppendingString:self.aString];\n  return myString.length > 100 ? NO : YES;\n}\n\n@end\n</textarea></div>\n\n<h2>Java example</h2>\n\n<div><textarea id=\"java-code\">\nimport com.demo.util.MyType;\nimport com.demo.util.MyInterface;\n\npublic enum Enum {\n  VAL1, VAL2, VAL3\n}\n\npublic class Class<T, V> implements MyInterface {\n  public static final MyType<T, V> member;\n  \n  private class InnerClass {\n    public int zero() {\n      return 0;\n    }\n  }\n\n  @Override\n  public MyType method() {\n    return member;\n  }\n\n  public void method2(MyType<T, V> value) {\n    method();\n    value.method3();\n    member = value;\n  }\n}\n</textarea></div>\n\n<h2>Scala example</h2>\n\n<div><textarea id=\"scala-code\">\nobject FilterTest extends App {\n  def filter(xs: List[Int], threshold: Int) = {\n    def process(ys: List[Int]): List[Int] =\n      if (ys.isEmpty) ys\n      else if (ys.head < threshold) ys.head :: process(ys.tail)\n      else process(ys.tail)\n    process(xs)\n  }\n  println(filter(List(1, 9, 2, 8, 3, 7, 4), 5))\n}\n</textarea></div>\n\n<h2>Kotlin mode</h2>\n\n<div><textarea id=\"kotlin-code\">\npackage org.wasabi.http\n\nimport java.util.concurrent.Executors\nimport java.net.InetSocketAddress\nimport org.wasabi.app.AppConfiguration\nimport io.netty.bootstrap.ServerBootstrap\nimport io.netty.channel.nio.NioEventLoopGroup\nimport io.netty.channel.socket.nio.NioServerSocketChannel\nimport org.wasabi.app.AppServer\n\npublic class HttpServer(private val appServer: AppServer) {\n\n    val bootstrap: ServerBootstrap\n    val primaryGroup: NioEventLoopGroup\n    val workerGroup:  NioEventLoopGroup\n\n    init {\n        // Define worker groups\n        primaryGroup = NioEventLoopGroup()\n        workerGroup = NioEventLoopGroup()\n\n        // Initialize bootstrap of server\n        bootstrap = ServerBootstrap()\n\n        bootstrap.group(primaryGroup, workerGroup)\n        bootstrap.channel(javaClass<NioServerSocketChannel>())\n        bootstrap.childHandler(NettyPipelineInitializer(appServer))\n    }\n\n    public fun start(wait: Boolean = true) {\n        val channel = bootstrap.bind(appServer.configuration.port)?.sync()?.channel()\n\n        if (wait) {\n            channel?.closeFuture()?.sync()\n        }\n    }\n\n    public fun stop() {\n        // Shutdown all event loops\n        primaryGroup.shutdownGracefully()\n        workerGroup.shutdownGracefully()\n\n        // Wait till all threads are terminated\n        primaryGroup.terminationFuture().sync()\n        workerGroup.terminationFuture().sync()\n    }\n}\n</textarea></div>\n\n<h2>Ceylon mode</h2>\n\n<div><textarea id=\"ceylon-code\">\n\"Produces the [[stream|Iterable]] that results from repeated\n application of the given [[function|next]] to the given\n [[first]] element of the stream, until the function first\n returns [[finished]]. If the given function never returns \n `finished`, the resulting stream is infinite.\n\n For example:\n\n     loop(0)(2.plus).takeWhile(10.largerThan)\n\n produces the stream `{ 0, 2, 4, 6, 8 }`.\"\ntagged(\"Streams\")\nshared {Element+} loop&lt;Element&gt;(\n        \"The first element of the resulting stream.\"\n        Element first)(\n        \"The function that produces the next element of the\n         stream, given the current element. The function may\n         return [[finished]] to indicate the end of the \n         stream.\"\n        Element|Finished next(Element element))\n    =&gt; let (start = first)\n    object satisfies {Element+} {\n        first =&gt; start;\n        empty =&gt; false;\n        function nextElement(Element element)\n                =&gt; next(element);\n        iterator()\n                =&gt; object satisfies Iterator&lt;Element&gt; {\n            variable Element|Finished current = start;\n            shared actual Element|Finished next() {\n                if (!is Finished result = current) {\n                    current = nextElement(result);\n                    return result;\n                }\n                else {\n                    return finished;\n                }\n            }\n        };\n    };\n</textarea></div>\n\n    <script>\n      var cEditor = CodeMirror.fromTextArea(document.getElementById(\"c-code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-csrc\"\n      });\n      var cppEditor = CodeMirror.fromTextArea(document.getElementById(\"cpp-code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-c++src\"\n      });\n      var javaEditor = CodeMirror.fromTextArea(document.getElementById(\"java-code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-java\"\n      });\n      var objectivecEditor = CodeMirror.fromTextArea(document.getElementById(\"objectivec-code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-objectivec\"\n      });\n      var scalaEditor = CodeMirror.fromTextArea(document.getElementById(\"scala-code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-scala\"\n      });\n      var kotlinEditor = CodeMirror.fromTextArea(document.getElementById(\"kotlin-code\"), {\n          lineNumbers: true,\n          matchBrackets: true,\n          mode: \"text/x-kotlin\"\n      });\n      var ceylonEditor = CodeMirror.fromTextArea(document.getElementById(\"ceylon-code\"), {\n          lineNumbers: true,\n          matchBrackets: true,\n          mode: \"text/x-ceylon\"\n      });\n      var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;\n      CodeMirror.keyMap.default[(mac ? \"Cmd\" : \"Ctrl\") + \"-Space\"] = \"autocomplete\";\n    </script>\n\n    <p>Simple mode that tries to handle C-like languages as well as it\n    can. Takes two configuration parameters: <code>keywords</code>, an\n    object whose property names are the keywords in the language,\n    and <code>useCPP</code>, which determines whether C preprocessor\n    directives are recognized.</p>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-csrc</code>\n    (C), <code>text/x-c++src</code> (C++), <code>text/x-java</code>\n    (Java), <code>text/x-csharp</code> (C#),\n    <code>text/x-objectivec</code> (Objective-C),\n    <code>text/x-scala</code> (Scala), <code>text/x-vertex</code>\n    <code>x-shader/x-fragment</code> (shader programs),\n    <code>text/x-squirrel</code> (Squirrel) and\n    <code>text/x-ceylon</code> (Ceylon)</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/clike/scala.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Scala mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/ambiance.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"clike.js\"></script>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Scala</a>\n  </ul>\n</div>\n\n<article>\n<h2>Scala mode</h2>\n<form>\n<textarea id=\"code\" name=\"code\">\n\n  /*                     __                                               *\\\n  **     ________ ___   / /  ___     Scala API                            **\n  **    / __/ __// _ | / /  / _ |    (c) 2003-2011, LAMP/EPFL             **\n  **  __\\ \\/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **\n  ** /____/\\___/_/ |_/____/_/ | |                                         **\n  **                          |/                                          **\n  \\*                                                                      */\n\n  package scala.collection\n\n  import generic._\n  import mutable.{ Builder, ListBuffer }\n  import annotation.{tailrec, migration, bridge}\n  import annotation.unchecked.{ uncheckedVariance => uV }\n  import parallel.ParIterable\n\n  /** A template trait for traversable collections of type `Traversable[A]`.\n   *  \n   *  $traversableInfo\n   *  @define mutability\n   *  @define traversableInfo\n   *  This is a base trait of all kinds of $mutability Scala collections. It\n   *  implements the behavior common to all collections, in terms of a method\n   *  `foreach` with signature:\n   * {{{\n   *     def foreach[U](f: Elem => U): Unit\n   * }}}\n   *  Collection classes mixing in this trait provide a concrete \n   *  `foreach` method which traverses all the\n   *  elements contained in the collection, applying a given function to each.\n   *  They also need to provide a method `newBuilder`\n   *  which creates a builder for collections of the same kind.\n   *  \n   *  A traversable class might or might not have two properties: strictness\n   *  and orderedness. Neither is represented as a type.\n   *  \n   *  The instances of a strict collection class have all their elements\n   *  computed before they can be used as values. By contrast, instances of\n   *  a non-strict collection class may defer computation of some of their\n   *  elements until after the instance is available as a value.\n   *  A typical example of a non-strict collection class is a\n   *  <a href=\"../immutable/Stream.html\" target=\"ContentFrame\">\n   *  `scala.collection.immutable.Stream`</a>.\n   *  A more general class of examples are `TraversableViews`.\n   *  \n   *  If a collection is an instance of an ordered collection class, traversing\n   *  its elements with `foreach` will always visit elements in the\n   *  same order, even for different runs of the program. If the class is not\n   *  ordered, `foreach` can visit elements in different orders for\n   *  different runs (but it will keep the same order in the same run).'\n   * \n   *  A typical example of a collection class which is not ordered is a\n   *  `HashMap` of objects. The traversal order for hash maps will\n   *  depend on the hash codes of its elements, and these hash codes might\n   *  differ from one run to the next. By contrast, a `LinkedHashMap`\n   *  is ordered because it's `foreach` method visits elements in the\n   *  order they were inserted into the `HashMap`.\n   *\n   *  @author Martin Odersky\n   *  @version 2.8\n   *  @since   2.8\n   *  @tparam A    the element type of the collection\n   *  @tparam Repr the type of the actual collection containing the elements.\n   *\n   *  @define Coll Traversable\n   *  @define coll traversable collection\n   */\n  trait TraversableLike[+A, +Repr] extends HasNewBuilder[A, Repr] \n                                      with FilterMonadic[A, Repr]\n                                      with TraversableOnce[A]\n                                      with GenTraversableLike[A, Repr]\n                                      with Parallelizable[A, ParIterable[A]]\n  {\n    self =>\n\n    import Traversable.breaks._\n\n    /** The type implementing this traversable */\n    protected type Self = Repr\n\n    /** The collection of type $coll underlying this `TraversableLike` object.\n     *  By default this is implemented as the `TraversableLike` object itself,\n     *  but this can be overridden.\n     */\n    def repr: Repr = this.asInstanceOf[Repr]\n\n    /** The underlying collection seen as an instance of `$Coll`.\n     *  By default this is implemented as the current collection object itself,\n     *  but this can be overridden.\n     */\n    protected[this] def thisCollection: Traversable[A] = this.asInstanceOf[Traversable[A]]\n\n    /** A conversion from collections of type `Repr` to `$Coll` objects.\n     *  By default this is implemented as just a cast, but this can be overridden.\n     */\n    protected[this] def toCollection(repr: Repr): Traversable[A] = repr.asInstanceOf[Traversable[A]]\n\n    /** Creates a new builder for this collection type.\n     */\n    protected[this] def newBuilder: Builder[A, Repr]\n\n    protected[this] def parCombiner = ParIterable.newCombiner[A]\n\n    /** Applies a function `f` to all elements of this $coll.\n     *  \n     *    Note: this method underlies the implementation of most other bulk operations.\n     *    It's important to implement this method in an efficient way.\n     *  \n     *\n     *  @param  f   the function that is applied for its side-effect to every element.\n     *              The result of function `f` is discarded.\n     *              \n     *  @tparam  U  the type parameter describing the result of function `f`. \n     *              This result will always be ignored. Typically `U` is `Unit`,\n     *              but this is not necessary.\n     *\n     *  @usecase def foreach(f: A => Unit): Unit\n     */\n    def foreach[U](f: A => U): Unit\n\n    /** Tests whether this $coll is empty.\n     *\n     *  @return    `true` if the $coll contain no elements, `false` otherwise.\n     */\n    def isEmpty: Boolean = {\n      var result = true\n      breakable {\n        for (x <- this) {\n          result = false\n          break\n        }\n      }\n      result\n    }\n\n    /** Tests whether this $coll is known to have a finite size.\n     *  All strict collections are known to have finite size. For a non-strict collection\n     *  such as `Stream`, the predicate returns `true` if all elements have been computed.\n     *  It returns `false` if the stream is not yet evaluated to the end.\n     *\n     *  Note: many collection methods will not work on collections of infinite sizes. \n     *\n     *  @return  `true` if this collection is known to have finite size, `false` otherwise.\n     */\n    def hasDefiniteSize = true\n\n    def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {\n      val b = bf(repr)\n      if (that.isInstanceOf[IndexedSeqLike[_, _]]) b.sizeHint(this, that.seq.size)\n      b ++= thisCollection\n      b ++= that.seq\n      b.result\n    }\n\n    @bridge\n    def ++[B >: A, That](that: TraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That =\n      ++(that: GenTraversableOnce[B])(bf)\n\n    /** Concatenates this $coll with the elements of a traversable collection.\n     *  It differs from ++ in that the right operand determines the type of the\n     *  resulting collection rather than the left one.\n     * \n     *  @param that   the traversable to append.\n     *  @tparam B     the element type of the returned collection. \n     *  @tparam That  $thatinfo\n     *  @param bf     $bfinfo\n     *  @return       a new collection of type `That` which contains all elements\n     *                of this $coll followed by all elements of `that`.\n     * \n     *  @usecase def ++:[B](that: TraversableOnce[B]): $Coll[B]\n     *  \n     *  @return       a new $coll which contains all elements of this $coll\n     *                followed by all elements of `that`.\n     */\n    def ++:[B >: A, That](that: TraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {\n      val b = bf(repr)\n      if (that.isInstanceOf[IndexedSeqLike[_, _]]) b.sizeHint(this, that.size)\n      b ++= that\n      b ++= thisCollection\n      b.result\n    }\n\n    /** This overload exists because: for the implementation of ++: we should reuse\n     *  that of ++ because many collections override it with more efficient versions.\n     *  Since TraversableOnce has no '++' method, we have to implement that directly,\n     *  but Traversable and down can use the overload.\n     */\n    def ++:[B >: A, That](that: Traversable[B])(implicit bf: CanBuildFrom[Repr, B, That]): That =\n      (that ++ seq)(breakOut)\n\n    def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {\n      val b = bf(repr)\n      b.sizeHint(this) \n      for (x <- this) b += f(x)\n      b.result\n    }\n\n    def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {\n      val b = bf(repr)\n      for (x <- this) b ++= f(x).seq\n      b.result\n    }\n\n    /** Selects all elements of this $coll which satisfy a predicate.\n     *\n     *  @param p     the predicate used to test elements.\n     *  @return      a new $coll consisting of all elements of this $coll that satisfy the given\n     *               predicate `p`. The order of the elements is preserved.\n     */\n    def filter(p: A => Boolean): Repr = {\n      val b = newBuilder\n      for (x <- this) \n        if (p(x)) b += x\n      b.result\n    }\n\n    /** Selects all elements of this $coll which do not satisfy a predicate.\n     *\n     *  @param p     the predicate used to test elements.\n     *  @return      a new $coll consisting of all elements of this $coll that do not satisfy the given\n     *               predicate `p`. The order of the elements is preserved.\n     */\n    def filterNot(p: A => Boolean): Repr = filter(!p(_))\n\n    def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {\n      val b = bf(repr)\n      for (x <- this) if (pf.isDefinedAt(x)) b += pf(x)\n      b.result\n    }\n\n    /** Builds a new collection by applying an option-valued function to all\n     *  elements of this $coll on which the function is defined.\n     *\n     *  @param f      the option-valued function which filters and maps the $coll.\n     *  @tparam B     the element type of the returned collection.\n     *  @tparam That  $thatinfo\n     *  @param bf     $bfinfo\n     *  @return       a new collection of type `That` resulting from applying the option-valued function\n     *                `f` to each element and collecting all defined results.\n     *                The order of the elements is preserved.\n     *\n     *  @usecase def filterMap[B](f: A => Option[B]): $Coll[B]\n     *  \n     *  @param pf     the partial function which filters and maps the $coll.\n     *  @return       a new $coll resulting from applying the given option-valued function\n     *                `f` to each element and collecting all defined results.\n     *                The order of the elements is preserved.\n    def filterMap[B, That](f: A => Option[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {\n      val b = bf(repr)\n      for (x <- this) \n        f(x) match {\n          case Some(y) => b += y\n          case _ =>\n        }\n      b.result\n    }\n     */\n\n    /** Partitions this $coll in two ${coll}s according to a predicate.\n     *\n     *  @param p the predicate on which to partition.\n     *  @return  a pair of ${coll}s: the first $coll consists of all elements that \n     *           satisfy the predicate `p` and the second $coll consists of all elements\n     *           that don't. The relative order of the elements in the resulting ${coll}s\n     *           is the same as in the original $coll.\n     */\n    def partition(p: A => Boolean): (Repr, Repr) = {\n      val l, r = newBuilder\n      for (x <- this) (if (p(x)) l else r) += x\n      (l.result, r.result)\n    }\n\n    def groupBy[K](f: A => K): immutable.Map[K, Repr] = {\n      val m = mutable.Map.empty[K, Builder[A, Repr]]\n      for (elem <- this) {\n        val key = f(elem)\n        val bldr = m.getOrElseUpdate(key, newBuilder)\n        bldr += elem\n      }\n      val b = immutable.Map.newBuilder[K, Repr]\n      for ((k, v) <- m)\n        b += ((k, v.result))\n\n      b.result\n    }\n\n    /** Tests whether a predicate holds for all elements of this $coll.\n     *\n     *  $mayNotTerminateInf\n     *\n     *  @param   p     the predicate used to test elements.\n     *  @return        `true` if the given predicate `p` holds for all elements\n     *                 of this $coll, otherwise `false`.\n     */\n    def forall(p: A => Boolean): Boolean = {\n      var result = true\n      breakable {\n        for (x <- this)\n          if (!p(x)) { result = false; break }\n      }\n      result\n    }\n\n    /** Tests whether a predicate holds for some of the elements of this $coll.\n     *\n     *  $mayNotTerminateInf\n     *\n     *  @param   p     the predicate used to test elements.\n     *  @return        `true` if the given predicate `p` holds for some of the\n     *                 elements of this $coll, otherwise `false`.\n     */\n    def exists(p: A => Boolean): Boolean = {\n      var result = false\n      breakable {\n        for (x <- this)\n          if (p(x)) { result = true; break }\n      }\n      result\n    }\n\n    /** Finds the first element of the $coll satisfying a predicate, if any.\n     * \n     *  $mayNotTerminateInf\n     *  $orderDependent\n     *\n     *  @param p    the predicate used to test elements.\n     *  @return     an option value containing the first element in the $coll\n     *              that satisfies `p`, or `None` if none exists.\n     */\n    def find(p: A => Boolean): Option[A] = {\n      var result: Option[A] = None\n      breakable {\n        for (x <- this)\n          if (p(x)) { result = Some(x); break }\n      }\n      result\n    }\n\n    def scan[B >: A, That](z: B)(op: (B, B) => B)(implicit cbf: CanBuildFrom[Repr, B, That]): That = scanLeft(z)(op)\n\n    def scanLeft[B, That](z: B)(op: (B, A) => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {\n      val b = bf(repr)\n      b.sizeHint(this, 1)\n      var acc = z\n      b += acc\n      for (x <- this) { acc = op(acc, x); b += acc }\n      b.result\n    }\n\n    @migration(2, 9,\n      \"This scanRight definition has changed in 2.9.\\n\" +\n      \"The previous behavior can be reproduced with scanRight.reverse.\"\n    )\n    def scanRight[B, That](z: B)(op: (A, B) => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {\n      var scanned = List(z)\n      var acc = z\n      for (x <- reversed) {\n        acc = op(x, acc)\n        scanned ::= acc\n      }\n      val b = bf(repr)\n      for (elem <- scanned) b += elem\n      b.result\n    }\n\n    /** Selects the first element of this $coll.\n     *  $orderDependent\n     *  @return  the first element of this $coll.\n     *  @throws `NoSuchElementException` if the $coll is empty.\n     */\n    def head: A = {\n      var result: () => A = () => throw new NoSuchElementException\n      breakable {\n        for (x <- this) {\n          result = () => x\n          break\n        }\n      }\n      result()\n    }\n\n    /** Optionally selects the first element.\n     *  $orderDependent\n     *  @return  the first element of this $coll if it is nonempty, `None` if it is empty.\n     */\n    def headOption: Option[A] = if (isEmpty) None else Some(head)\n\n    /** Selects all elements except the first.\n     *  $orderDependent\n     *  @return  a $coll consisting of all elements of this $coll\n     *           except the first one.\n     *  @throws `UnsupportedOperationException` if the $coll is empty.\n     */ \n    override def tail: Repr = {\n      if (isEmpty) throw new UnsupportedOperationException(\"empty.tail\")\n      drop(1)\n    }\n\n    /** Selects the last element.\n      * $orderDependent\n      * @return The last element of this $coll.\n      * @throws NoSuchElementException If the $coll is empty.\n      */\n    def last: A = {\n      var lst = head\n      for (x <- this)\n        lst = x\n      lst\n    }\n\n    /** Optionally selects the last element.\n     *  $orderDependent\n     *  @return  the last element of this $coll$ if it is nonempty, `None` if it is empty.\n     */\n    def lastOption: Option[A] = if (isEmpty) None else Some(last)\n\n    /** Selects all elements except the last.\n     *  $orderDependent\n     *  @return  a $coll consisting of all elements of this $coll\n     *           except the last one.\n     *  @throws `UnsupportedOperationException` if the $coll is empty.\n     */\n    def init: Repr = {\n      if (isEmpty) throw new UnsupportedOperationException(\"empty.init\")\n      var lst = head\n      var follow = false\n      val b = newBuilder\n      b.sizeHint(this, -1)\n      for (x <- this.seq) {\n        if (follow) b += lst\n        else follow = true\n        lst = x\n      }\n      b.result\n    }\n\n    def take(n: Int): Repr = slice(0, n)\n\n    def drop(n: Int): Repr = \n      if (n <= 0) {\n        val b = newBuilder\n        b.sizeHint(this)\n        b ++= thisCollection result\n      }\n      else sliceWithKnownDelta(n, Int.MaxValue, -n)\n\n    def slice(from: Int, until: Int): Repr = sliceWithKnownBound(math.max(from, 0), until)\n\n    // Precondition: from >= 0, until > 0, builder already configured for building.\n    private[this] def sliceInternal(from: Int, until: Int, b: Builder[A, Repr]): Repr = {\n      var i = 0\n      breakable {\n        for (x <- this.seq) {\n          if (i >= from) b += x\n          i += 1\n          if (i >= until) break\n        }\n      }\n      b.result\n    }\n    // Precondition: from >= 0\n    private[scala] def sliceWithKnownDelta(from: Int, until: Int, delta: Int): Repr = {\n      val b = newBuilder\n      if (until <= from) b.result\n      else {\n        b.sizeHint(this, delta)\n        sliceInternal(from, until, b)\n      }\n    }\n    // Precondition: from >= 0\n    private[scala] def sliceWithKnownBound(from: Int, until: Int): Repr = {\n      val b = newBuilder\n      if (until <= from) b.result\n      else {\n        b.sizeHintBounded(until - from, this)      \n        sliceInternal(from, until, b)\n      }\n    }\n\n    def takeWhile(p: A => Boolean): Repr = {\n      val b = newBuilder\n      breakable {\n        for (x <- this) {\n          if (!p(x)) break\n          b += x\n        }\n      }\n      b.result\n    }\n\n    def dropWhile(p: A => Boolean): Repr = {\n      val b = newBuilder\n      var go = false\n      for (x <- this) {\n        if (!p(x)) go = true\n        if (go) b += x\n      }\n      b.result\n    }\n\n    def span(p: A => Boolean): (Repr, Repr) = {\n      val l, r = newBuilder\n      var toLeft = true\n      for (x <- this) {\n        toLeft = toLeft && p(x)\n        (if (toLeft) l else r) += x\n      }\n      (l.result, r.result)\n    }\n\n    def splitAt(n: Int): (Repr, Repr) = {\n      val l, r = newBuilder\n      l.sizeHintBounded(n, this)\n      if (n >= 0) r.sizeHint(this, -n)\n      var i = 0\n      for (x <- this) {\n        (if (i < n) l else r) += x\n        i += 1\n      }\n      (l.result, r.result)\n    }\n\n    /** Iterates over the tails of this $coll. The first value will be this\n     *  $coll and the final one will be an empty $coll, with the intervening\n     *  values the results of successive applications of `tail`.\n     *\n     *  @return   an iterator over all the tails of this $coll\n     *  @example  `List(1,2,3).tails = Iterator(List(1,2,3), List(2,3), List(3), Nil)`\n     */  \n    def tails: Iterator[Repr] = iterateUntilEmpty(_.tail)\n\n    /** Iterates over the inits of this $coll. The first value will be this\n     *  $coll and the final one will be an empty $coll, with the intervening\n     *  values the results of successive applications of `init`.\n     *\n     *  @return  an iterator over all the inits of this $coll\n     *  @example  `List(1,2,3).inits = Iterator(List(1,2,3), List(1,2), List(1), Nil)`\n     */\n    def inits: Iterator[Repr] = iterateUntilEmpty(_.init)\n\n    /** Copies elements of this $coll to an array.\n     *  Fills the given array `xs` with at most `len` elements of\n     *  this $coll, starting at position `start`.\n     *  Copying will stop once either the end of the current $coll is reached,\n     *  or the end of the array is reached, or `len` elements have been copied.\n     *\n     *  $willNotTerminateInf\n     * \n     *  @param  xs     the array to fill.\n     *  @param  start  the starting index.\n     *  @param  len    the maximal number of elements to copy.\n     *  @tparam B      the type of the elements of the array. \n     * \n     *\n     *  @usecase def copyToArray(xs: Array[A], start: Int, len: Int): Unit\n     */\n    def copyToArray[B >: A](xs: Array[B], start: Int, len: Int) {\n      var i = start\n      val end = (start + len) min xs.length\n      breakable {\n        for (x <- this) {\n          if (i >= end) break\n          xs(i) = x\n          i += 1\n        }\n      }\n    }\n\n    def toTraversable: Traversable[A] = thisCollection\n    def toIterator: Iterator[A] = toStream.iterator\n    def toStream: Stream[A] = toBuffer.toStream\n\n    /** Converts this $coll to a string.\n     *\n     *  @return   a string representation of this collection. By default this\n     *            string consists of the `stringPrefix` of this $coll,\n     *            followed by all elements separated by commas and enclosed in parentheses.\n     */\n    override def toString = mkString(stringPrefix + \"(\", \", \", \")\")\n\n    /** Defines the prefix of this object's `toString` representation.\n     *\n     *  @return  a string representation which starts the result of `toString`\n     *           applied to this $coll. By default the string prefix is the\n     *           simple name of the collection class $coll.\n     */\n    def stringPrefix : String = {\n      var string = repr.asInstanceOf[AnyRef].getClass.getName\n      val idx1 = string.lastIndexOf('.' : Int)\n      if (idx1 != -1) string = string.substring(idx1 + 1)\n      val idx2 = string.indexOf('$')\n      if (idx2 != -1) string = string.substring(0, idx2)\n      string\n    }\n\n    /** Creates a non-strict view of this $coll.\n     * \n     *  @return a non-strict view of this $coll.\n     */\n    def view = new TraversableView[A, Repr] {\n      protected lazy val underlying = self.repr\n      override def foreach[U](f: A => U) = self foreach f\n    }\n\n    /** Creates a non-strict view of a slice of this $coll.\n     *\n     *  Note: the difference between `view` and `slice` is that `view` produces\n     *        a view of the current $coll, whereas `slice` produces a new $coll.\n     * \n     *  Note: `view(from, to)` is equivalent to `view.slice(from, to)`\n     *  $orderDependent\n     * \n     *  @param from   the index of the first element of the view\n     *  @param until  the index of the element following the view\n     *  @return a non-strict view of a slice of this $coll, starting at index `from`\n     *  and extending up to (but not including) index `until`.\n     */\n    def view(from: Int, until: Int): TraversableView[A, Repr] = view.slice(from, until)\n\n    /** Creates a non-strict filter of this $coll.\n     *\n     *  Note: the difference between `c filter p` and `c withFilter p` is that\n     *        the former creates a new collection, whereas the latter only\n     *        restricts the domain of subsequent `map`, `flatMap`, `foreach`,\n     *        and `withFilter` operations.\n     *  $orderDependent\n     * \n     *  @param p   the predicate used to test elements.\n     *  @return    an object of class `WithFilter`, which supports\n     *             `map`, `flatMap`, `foreach`, and `withFilter` operations.\n     *             All these operations apply to those elements of this $coll which\n     *             satisfy the predicate `p`.\n     */\n    def withFilter(p: A => Boolean): FilterMonadic[A, Repr] = new WithFilter(p)\n\n    /** A class supporting filtered operations. Instances of this class are\n     *  returned by method `withFilter`.\n     */\n    class WithFilter(p: A => Boolean) extends FilterMonadic[A, Repr] {\n\n      /** Builds a new collection by applying a function to all elements of the\n       *  outer $coll containing this `WithFilter` instance that satisfy predicate `p`.\n       *\n       *  @param f      the function to apply to each element.\n       *  @tparam B     the element type of the returned collection.\n       *  @tparam That  $thatinfo\n       *  @param bf     $bfinfo\n       *  @return       a new collection of type `That` resulting from applying\n       *                the given function `f` to each element of the outer $coll\n       *                that satisfies predicate `p` and collecting the results.\n       *\n       *  @usecase def map[B](f: A => B): $Coll[B] \n       *  \n       *  @return       a new $coll resulting from applying the given function\n       *                `f` to each element of the outer $coll that satisfies\n       *                predicate `p` and collecting the results.\n       */\n      def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {\n        val b = bf(repr)\n        for (x <- self) \n          if (p(x)) b += f(x)\n        b.result\n      }\n\n      /** Builds a new collection by applying a function to all elements of the\n       *  outer $coll containing this `WithFilter` instance that satisfy\n       *  predicate `p` and concatenating the results. \n       *\n       *  @param f      the function to apply to each element.\n       *  @tparam B     the element type of the returned collection.\n       *  @tparam That  $thatinfo\n       *  @param bf     $bfinfo\n       *  @return       a new collection of type `That` resulting from applying\n       *                the given collection-valued function `f` to each element\n       *                of the outer $coll that satisfies predicate `p` and\n       *                concatenating the results.\n       *\n       *  @usecase def flatMap[B](f: A => TraversableOnce[B]): $Coll[B]\n       * \n       *  @return       a new $coll resulting from applying the given collection-valued function\n       *                `f` to each element of the outer $coll that satisfies predicate `p` and concatenating the results.\n       */\n      def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {\n        val b = bf(repr)\n        for (x <- self) \n          if (p(x)) b ++= f(x).seq\n        b.result\n      }\n\n      /** Applies a function `f` to all elements of the outer $coll containing\n       *  this `WithFilter` instance that satisfy predicate `p`.\n       *\n       *  @param  f   the function that is applied for its side-effect to every element.\n       *              The result of function `f` is discarded.\n       *              \n       *  @tparam  U  the type parameter describing the result of function `f`. \n       *              This result will always be ignored. Typically `U` is `Unit`,\n       *              but this is not necessary.\n       *\n       *  @usecase def foreach(f: A => Unit): Unit\n       */   \n      def foreach[U](f: A => U): Unit = \n        for (x <- self) \n          if (p(x)) f(x)\n\n      /** Further refines the filter for this $coll.\n       *\n       *  @param q   the predicate used to test elements.\n       *  @return    an object of class `WithFilter`, which supports\n       *             `map`, `flatMap`, `foreach`, and `withFilter` operations.\n       *             All these operations apply to those elements of this $coll which\n       *             satisfy the predicate `q` in addition to the predicate `p`.\n       */\n      def withFilter(q: A => Boolean): WithFilter = \n        new WithFilter(x => p(x) && q(x))\n    }\n\n    // A helper for tails and inits.\n    private def iterateUntilEmpty(f: Traversable[A @uV] => Traversable[A @uV]): Iterator[Repr] = {\n      val it = Iterator.iterate(thisCollection)(f) takeWhile (x => !x.isEmpty)\n      it ++ Iterator(Nil) map (newBuilder ++= _ result)\n    }\n  }\n\n\n</textarea>\n</form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        theme: \"ambiance\",\n        mode: \"text/x-scala\"\n      });\n    </script>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/clike/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"text/x-c\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"indent\",\n     \"[type void] [def foo]([type void*] [variable a], [type int] [variable b]) {\",\n     \"  [type int] [variable c] [operator =] [variable b] [operator +]\",\n     \"    [number 1];\",\n     \"  [keyword return] [operator *][variable a];\",\n     \"}\");\n\n  MT(\"indent_switch\",\n     \"[keyword switch] ([variable x]) {\",\n     \"  [keyword case] [number 10]:\",\n     \"    [keyword return] [number 20];\",\n     \"  [keyword default]:\",\n     \"    [variable printf]([string \\\"foo %c\\\"], [variable x]);\",\n     \"}\");\n\n  MT(\"def\",\n     \"[type void] [def foo]() {}\",\n     \"[keyword struct] [def bar]{}\",\n     \"[keyword enum] [def zot]{}\",\n     \"[keyword union] [def ugh]{}\",\n     \"[type int] [type *][def baz]() {}\");\n\n  MT(\"def_new_line\",\n     \"::[variable std]::[variable SomeTerribleType][operator <][variable T][operator >]\",\n     \"[def SomeLongMethodNameThatDoesntFitIntoOneLine]([keyword const] [variable MyType][operator &] [variable param]) {}\")\n\n  MT(\"double_block\",\n     \"[keyword for] (;;)\",\n     \"  [keyword for] (;;)\",\n     \"    [variable x][operator ++];\",\n     \"[keyword return];\");\n\n  MT(\"preprocessor\",\n     \"[meta #define FOO 3]\",\n     \"[type int] [variable foo];\",\n     \"[meta #define BAR\\\\]\",\n     \"[meta 4]\",\n     \"[type unsigned] [type int] [variable bar] [operator =] [number 8];\",\n     \"[meta #include <baz> ][comment // comment]\")\n\n  MT(\"c_underscores\",\n     \"[builtin __FOO];\",\n     \"[builtin _Complex];\",\n     \"[builtin __aName];\",\n     \"[variable _aName];\");\n\n  MT(\"c_types\",\n    \"[type int];\",\n    \"[type long];\",\n    \"[type char];\",\n    \"[type short];\",\n    \"[type double];\",\n    \"[type float];\",\n    \"[type unsigned];\",\n    \"[type signed];\",\n    \"[type void];\",\n    \"[type bool];\",\n    \"[type foo_t];\",\n    \"[variable foo_T];\",\n    \"[variable _t];\");\n\n  var mode_cpp = CodeMirror.getMode({indentUnit: 2}, \"text/x-c++src\");\n  function MTCPP(name) { test.mode(name, mode_cpp, Array.prototype.slice.call(arguments, 1)); }\n\n  MTCPP(\"cpp14_literal\",\n    \"[number 10'000];\",\n    \"[number 0b10'000];\",\n    \"[number 0x10'000];\",\n    \"[string '100000'];\");\n\n  MTCPP(\"ctor_dtor\",\n     \"[def Foo::Foo]() {}\",\n     \"[def Foo::~Foo]() {}\");\n\n  MTCPP(\"cpp_underscores\",\n        \"[builtin __FOO];\",\n        \"[builtin _Complex];\",\n        \"[builtin __aName];\",\n        \"[variable _aName];\");\n\n  var mode_objc = CodeMirror.getMode({indentUnit: 2}, \"text/x-objectivec\");\n  function MTOBJC(name) { test.mode(name, mode_objc, Array.prototype.slice.call(arguments, 1)); }\n\n  MTOBJC(\"objc_underscores\",\n         \"[builtin __FOO];\",\n         \"[builtin _Complex];\",\n         \"[builtin __aName];\",\n         \"[variable _aName];\");\n\n  MTOBJC(\"objc_interface\",\n         \"[keyword @interface] [def foo] {\",\n         \"  [type int] [variable bar];\",\n         \"}\",\n         \"[keyword @property] ([keyword atomic], [keyword nullable]) [variable NSString][operator *] [variable a];\",\n         \"[keyword @property] ([keyword nonatomic], [keyword assign]) [type int] [variable b];\",\n         \"[operator -]([type instancetype])[variable initWithFoo]:([type int])[variable a] \" +\n           \"[builtin NS_DESIGNATED_INITIALIZER];\",\n         \"[keyword @end]\");\n\n  MTOBJC(\"objc_implementation\",\n         \"[keyword @implementation] [def foo] {\",\n         \"  [type int] [variable bar];\",\n         \"}\",\n         \"[keyword @property] ([keyword readwrite]) [type SEL] [variable a];\",\n         \"[operator -]([type instancetype])[variable initWithFoo]:([type int])[variable a] {\",\n         \"  [keyword if](([keyword self] [operator =] [[[keyword super] [variable init] ]])) {}\",\n         \"  [keyword return] [keyword self];\",\n         \"}\",\n         \"[keyword @end]\");\n\n  MTOBJC(\"objc_types\",\n         \"[type int];\",\n         \"[type foo_t];\",\n         \"[variable foo_T];\",\n         \"[type id];\",\n         \"[type SEL];\",\n         \"[type instancetype];\",\n         \"[type Class];\",\n         \"[type Protocol];\",\n         \"[type BOOL];\"\n         );\n\n  var mode_scala = CodeMirror.getMode({indentUnit: 2}, \"text/x-scala\");\n  function MTSCALA(name) { test.mode(\"scala_\" + name, mode_scala, Array.prototype.slice.call(arguments, 1)); }\n  MTSCALA(\"nested_comments\",\n     \"[comment /*]\",\n     \"[comment But wait /* this is a nested comment */ for real]\",\n     \"[comment /**** let * me * show * you ****/]\",\n     \"[comment ///// let / me / show / you /////]\",\n     \"[comment */]\");\n\n  var mode_java = CodeMirror.getMode({indentUnit: 2}, \"text/x-java\");\n  function MTJAVA(name) { test.mode(\"java_\" + name, mode_java, Array.prototype.slice.call(arguments, 1)); }\n  MTJAVA(\"types\",\n         \"[type byte];\",\n         \"[type short];\",\n         \"[type int];\",\n         \"[type long];\",\n         \"[type float];\",\n         \"[type double];\",\n         \"[type boolean];\",\n         \"[type char];\",\n         \"[type void];\",\n         \"[type Boolean];\",\n         \"[type Byte];\",\n         \"[type Character];\",\n         \"[type Double];\",\n         \"[type Float];\",\n         \"[type Integer];\",\n         \"[type Long];\",\n         \"[type Number];\",\n         \"[type Object];\",\n         \"[type Short];\",\n         \"[type String];\",\n         \"[type StringBuffer];\",\n         \"[type StringBuilder];\",\n         \"[type Void];\");\n\n  MTJAVA(\"indent\",\n         \"[keyword public] [keyword class] [def A] [keyword extends] [variable B]\",\n         \"{\",\n         \"  [variable c]()\")\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/clojure/clojure.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports === \"object\" && typeof module === \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define === \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"clojure\", function (options) {\n  var atoms = [\"false\", \"nil\", \"true\"];\n  var specialForms = [\".\", \"catch\", \"def\", \"do\", \"if\", \"monitor-enter\",\n      \"monitor-exit\", \"new\", \"quote\", \"recur\", \"set!\", \"throw\", \"try\", \"var\"];\n  var coreSymbols = [\"*\", \"*'\", \"*1\", \"*2\", \"*3\", \"*agent*\",\n      \"*allow-unresolved-vars*\", \"*assert*\", \"*clojure-version*\",\n      \"*command-line-args*\", \"*compile-files*\", \"*compile-path*\",\n      \"*compiler-options*\", \"*data-readers*\", \"*default-data-reader-fn*\", \"*e\",\n      \"*err*\", \"*file*\", \"*flush-on-newline*\", \"*fn-loader*\", \"*in*\",\n      \"*math-context*\", \"*ns*\", \"*out*\", \"*print-dup*\", \"*print-length*\",\n      \"*print-level*\", \"*print-meta*\", \"*print-namespace-maps*\",\n      \"*print-readably*\", \"*read-eval*\", \"*reader-resolver*\", \"*source-path*\",\n      \"*suppress-read*\", \"*unchecked-math*\", \"*use-context-classloader*\",\n      \"*verbose-defrecords*\", \"*warn-on-reflection*\", \"+\", \"+'\", \"-\", \"-'\",\n      \"->\", \"->>\", \"->ArrayChunk\", \"->Eduction\", \"->Vec\", \"->VecNode\",\n      \"->VecSeq\", \"-cache-protocol-fn\", \"-reset-methods\", \"..\", \"/\", \"<\", \"<=\",\n      \"=\", \"==\", \">\", \">=\", \"EMPTY-NODE\", \"Inst\", \"StackTraceElement->vec\",\n      \"Throwable->map\", \"accessor\", \"aclone\", \"add-classpath\", \"add-watch\",\n      \"agent\", \"agent-error\", \"agent-errors\", \"aget\", \"alength\", \"alias\",\n      \"all-ns\", \"alter\", \"alter-meta!\", \"alter-var-root\", \"amap\", \"ancestors\",\n      \"and\", \"any?\", \"apply\", \"areduce\", \"array-map\", \"as->\", \"aset\",\n      \"aset-boolean\", \"aset-byte\", \"aset-char\", \"aset-double\", \"aset-float\",\n      \"aset-int\", \"aset-long\", \"aset-short\", \"assert\", \"assoc\", \"assoc!\",\n      \"assoc-in\", \"associative?\", \"atom\", \"await\", \"await-for\", \"await1\",\n      \"bases\", \"bean\", \"bigdec\", \"bigint\", \"biginteger\", \"binding\", \"bit-and\",\n      \"bit-and-not\", \"bit-clear\", \"bit-flip\", \"bit-not\", \"bit-or\", \"bit-set\",\n      \"bit-shift-left\", \"bit-shift-right\", \"bit-test\", \"bit-xor\", \"boolean\",\n      \"boolean-array\", \"boolean?\", \"booleans\", \"bound-fn\", \"bound-fn*\",\n      \"bound?\", \"bounded-count\", \"butlast\", \"byte\", \"byte-array\", \"bytes\",\n      \"bytes?\", \"case\", \"cast\", \"cat\", \"char\", \"char-array\",\n      \"char-escape-string\", \"char-name-string\", \"char?\", \"chars\", \"chunk\",\n      \"chunk-append\", \"chunk-buffer\", \"chunk-cons\", \"chunk-first\", \"chunk-next\",\n      \"chunk-rest\", \"chunked-seq?\", \"class\", \"class?\", \"clear-agent-errors\",\n      \"clojure-version\", \"coll?\", \"comment\", \"commute\", \"comp\", \"comparator\",\n      \"compare\", \"compare-and-set!\", \"compile\", \"complement\", \"completing\",\n      \"concat\", \"cond\", \"cond->\", \"cond->>\", \"condp\", \"conj\", \"conj!\", \"cons\",\n      \"constantly\", \"construct-proxy\", \"contains?\", \"count\", \"counted?\",\n      \"create-ns\", \"create-struct\", \"cycle\", \"dec\", \"dec'\", \"decimal?\",\n      \"declare\", \"dedupe\", \"default-data-readers\", \"definline\", \"definterface\",\n      \"defmacro\", \"defmethod\", \"defmulti\", \"defn\", \"defn-\", \"defonce\",\n      \"defprotocol\", \"defrecord\", \"defstruct\", \"deftype\", \"delay\", \"delay?\",\n      \"deliver\", \"denominator\", \"deref\", \"derive\", \"descendants\", \"destructure\",\n      \"disj\", \"disj!\", \"dissoc\", \"dissoc!\", \"distinct\", \"distinct?\", \"doall\",\n      \"dorun\", \"doseq\", \"dosync\", \"dotimes\", \"doto\", \"double\", \"double-array\",\n      \"double?\", \"doubles\", \"drop\", \"drop-last\", \"drop-while\", \"eduction\",\n      \"empty\", \"empty?\", \"ensure\", \"ensure-reduced\", \"enumeration-seq\",\n      \"error-handler\", \"error-mode\", \"eval\", \"even?\", \"every-pred\", \"every?\",\n      \"ex-data\", \"ex-info\", \"extend\", \"extend-protocol\", \"extend-type\",\n      \"extenders\", \"extends?\", \"false?\", \"ffirst\", \"file-seq\", \"filter\",\n      \"filterv\", \"find\", \"find-keyword\", \"find-ns\", \"find-protocol-impl\",\n      \"find-protocol-method\", \"find-var\", \"first\", \"flatten\", \"float\",\n      \"float-array\", \"float?\", \"floats\", \"flush\", \"fn\", \"fn?\", \"fnext\", \"fnil\",\n      \"for\", \"force\", \"format\", \"frequencies\", \"future\", \"future-call\",\n      \"future-cancel\", \"future-cancelled?\", \"future-done?\", \"future?\",\n      \"gen-class\", \"gen-interface\", \"gensym\", \"get\", \"get-in\", \"get-method\",\n      \"get-proxy-class\", \"get-thread-bindings\", \"get-validator\", \"group-by\",\n      \"halt-when\", \"hash\", \"hash-combine\", \"hash-map\", \"hash-ordered-coll\",\n      \"hash-set\", \"hash-unordered-coll\", \"ident?\", \"identical?\", \"identity\",\n      \"if-let\", \"if-not\", \"if-some\", \"ifn?\", \"import\", \"in-ns\", \"inc\", \"inc'\",\n      \"indexed?\", \"init-proxy\", \"inst-ms\", \"inst-ms*\", \"inst?\", \"instance?\",\n      \"int\", \"int-array\", \"int?\", \"integer?\", \"interleave\", \"intern\",\n      \"interpose\", \"into\", \"into-array\", \"ints\", \"io!\", \"isa?\", \"iterate\",\n      \"iterator-seq\", \"juxt\", \"keep\", \"keep-indexed\", \"key\", \"keys\", \"keyword\",\n      \"keyword?\", \"last\", \"lazy-cat\", \"lazy-seq\", \"let\", \"letfn\", \"line-seq\",\n      \"list\", \"list*\", \"list?\", \"load\", \"load-file\", \"load-reader\",\n      \"load-string\", \"loaded-libs\", \"locking\", \"long\", \"long-array\", \"longs\",\n      \"loop\", \"macroexpand\", \"macroexpand-1\", \"make-array\", \"make-hierarchy\",\n      \"map\", \"map-entry?\", \"map-indexed\", \"map?\", \"mapcat\", \"mapv\", \"max\",\n      \"max-key\", \"memfn\", \"memoize\", \"merge\", \"merge-with\", \"meta\",\n      \"method-sig\", \"methods\", \"min\", \"min-key\", \"mix-collection-hash\", \"mod\",\n      \"munge\", \"name\", \"namespace\", \"namespace-munge\", \"nat-int?\", \"neg-int?\",\n      \"neg?\", \"newline\", \"next\", \"nfirst\", \"nil?\", \"nnext\", \"not\", \"not-any?\",\n      \"not-empty\", \"not-every?\", \"not=\", \"ns\", \"ns-aliases\", \"ns-imports\",\n      \"ns-interns\", \"ns-map\", \"ns-name\", \"ns-publics\", \"ns-refers\",\n      \"ns-resolve\", \"ns-unalias\", \"ns-unmap\", \"nth\", \"nthnext\", \"nthrest\",\n      \"num\", \"number?\", \"numerator\", \"object-array\", \"odd?\", \"or\", \"parents\",\n      \"partial\", \"partition\", \"partition-all\", \"partition-by\", \"pcalls\", \"peek\",\n      \"persistent!\", \"pmap\", \"pop\", \"pop!\", \"pop-thread-bindings\", \"pos-int?\",\n      \"pos?\", \"pr\", \"pr-str\", \"prefer-method\", \"prefers\",\n      \"primitives-classnames\", \"print\", \"print-ctor\", \"print-dup\",\n      \"print-method\", \"print-simple\", \"print-str\", \"printf\", \"println\",\n      \"println-str\", \"prn\", \"prn-str\", \"promise\", \"proxy\",\n      \"proxy-call-with-super\", \"proxy-mappings\", \"proxy-name\", \"proxy-super\",\n      \"push-thread-bindings\", \"pvalues\", \"qualified-ident?\",\n      \"qualified-keyword?\", \"qualified-symbol?\", \"quot\", \"rand\", \"rand-int\",\n      \"rand-nth\", \"random-sample\", \"range\", \"ratio?\", \"rational?\",\n      \"rationalize\", \"re-find\", \"re-groups\", \"re-matcher\", \"re-matches\",\n      \"re-pattern\", \"re-seq\", \"read\", \"read-line\", \"read-string\",\n      \"reader-conditional\", \"reader-conditional?\", \"realized?\", \"record?\",\n      \"reduce\", \"reduce-kv\", \"reduced\", \"reduced?\", \"reductions\", \"ref\",\n      \"ref-history-count\", \"ref-max-history\", \"ref-min-history\", \"ref-set\",\n      \"refer\", \"refer-clojure\", \"reify\", \"release-pending-sends\", \"rem\",\n      \"remove\", \"remove-all-methods\", \"remove-method\", \"remove-ns\",\n      \"remove-watch\", \"repeat\", \"repeatedly\", \"replace\", \"replicate\", \"require\",\n      \"reset!\", \"reset-meta!\", \"reset-vals!\", \"resolve\", \"rest\",\n      \"restart-agent\", \"resultset-seq\", \"reverse\", \"reversible?\", \"rseq\",\n      \"rsubseq\", \"run!\", \"satisfies?\", \"second\", \"select-keys\", \"send\",\n      \"send-off\", \"send-via\", \"seq\", \"seq?\", \"seqable?\", \"seque\", \"sequence\",\n      \"sequential?\", \"set\", \"set-agent-send-executor!\",\n      \"set-agent-send-off-executor!\", \"set-error-handler!\", \"set-error-mode!\",\n      \"set-validator!\", \"set?\", \"short\", \"short-array\", \"shorts\", \"shuffle\",\n      \"shutdown-agents\", \"simple-ident?\", \"simple-keyword?\", \"simple-symbol?\",\n      \"slurp\", \"some\", \"some->\", \"some->>\", \"some-fn\", \"some?\", \"sort\",\n      \"sort-by\", \"sorted-map\", \"sorted-map-by\", \"sorted-set\", \"sorted-set-by\",\n      \"sorted?\", \"special-symbol?\", \"spit\", \"split-at\", \"split-with\", \"str\",\n      \"string?\", \"struct\", \"struct-map\", \"subs\", \"subseq\", \"subvec\", \"supers\",\n      \"swap!\", \"swap-vals!\", \"symbol\", \"symbol?\", \"sync\", \"tagged-literal\",\n      \"tagged-literal?\", \"take\", \"take-last\", \"take-nth\", \"take-while\", \"test\",\n      \"the-ns\", \"thread-bound?\", \"time\", \"to-array\", \"to-array-2d\",\n      \"trampoline\", \"transduce\", \"transient\", \"tree-seq\", \"true?\", \"type\",\n      \"unchecked-add\", \"unchecked-add-int\", \"unchecked-byte\", \"unchecked-char\",\n      \"unchecked-dec\", \"unchecked-dec-int\", \"unchecked-divide-int\",\n      \"unchecked-double\", \"unchecked-float\", \"unchecked-inc\",\n      \"unchecked-inc-int\", \"unchecked-int\", \"unchecked-long\",\n      \"unchecked-multiply\", \"unchecked-multiply-int\", \"unchecked-negate\",\n      \"unchecked-negate-int\", \"unchecked-remainder-int\", \"unchecked-short\",\n      \"unchecked-subtract\", \"unchecked-subtract-int\", \"underive\", \"unquote\",\n      \"unquote-splicing\", \"unreduced\", \"unsigned-bit-shift-right\", \"update\",\n      \"update-in\", \"update-proxy\", \"uri?\", \"use\", \"uuid?\", \"val\", \"vals\",\n      \"var-get\", \"var-set\", \"var?\", \"vary-meta\", \"vec\", \"vector\", \"vector-of\",\n      \"vector?\", \"volatile!\", \"volatile?\", \"vreset!\", \"vswap!\", \"when\",\n      \"when-first\", \"when-let\", \"when-not\", \"when-some\", \"while\",\n      \"with-bindings\", \"with-bindings*\", \"with-in-str\", \"with-loading-context\",\n      \"with-local-vars\", \"with-meta\", \"with-open\", \"with-out-str\",\n      \"with-precision\", \"with-redefs\", \"with-redefs-fn\", \"xml-seq\", \"zero?\",\n      \"zipmap\"];\n  var haveBodyParameter = [\n      \"->\", \"->>\", \"as->\", \"binding\", \"bound-fn\", \"case\", \"catch\", \"comment\",\n      \"cond\", \"cond->\", \"cond->>\", \"condp\", \"def\", \"definterface\", \"defmethod\",\n      \"defn\", \"defmacro\", \"defprotocol\", \"defrecord\", \"defstruct\", \"deftype\",\n      \"do\", \"doseq\", \"dotimes\", \"doto\", \"extend\", \"extend-protocol\",\n      \"extend-type\", \"fn\", \"for\", \"future\", \"if\", \"if-let\", \"if-not\", \"if-some\",\n      \"let\", \"letfn\", \"locking\", \"loop\", \"ns\", \"proxy\", \"reify\", \"struct-map\",\n      \"some->\", \"some->>\", \"try\", \"when\", \"when-first\", \"when-let\", \"when-not\",\n      \"when-some\", \"while\", \"with-bindings\", \"with-bindings*\", \"with-in-str\",\n      \"with-loading-context\", \"with-local-vars\", \"with-meta\", \"with-open\",\n      \"with-out-str\", \"with-precision\", \"with-redefs\", \"with-redefs-fn\"];\n\n  CodeMirror.registerHelper(\"hintWords\", \"clojure\",\n    [].concat(atoms, specialForms, coreSymbols));\n\n  var atom = createLookupMap(atoms);\n  var specialForm = createLookupMap(specialForms);\n  var coreSymbol = createLookupMap(coreSymbols);\n  var hasBodyParameter = createLookupMap(haveBodyParameter);\n  var delimiter = /^(?:[\\\\\\[\\]\\s\"(),;@^`{}~]|$)/;\n  var numberLiteral = /^(?:[+\\-]?\\d+(?:(?:N|(?:[eE][+\\-]?\\d+))|(?:\\.?\\d*(?:M|(?:[eE][+\\-]?\\d+))?)|\\/\\d+|[xX][0-9a-fA-F]+|r[0-9a-zA-Z]+)?(?=[\\\\\\[\\]\\s\"#'(),;@^`{}~]|$))/;\n  var characterLiteral = /^(?:\\\\(?:backspace|formfeed|newline|return|space|tab|o[0-7]{3}|u[0-9A-Fa-f]{4}|x[0-9A-Fa-f]{4}|.)?(?=[\\\\\\[\\]\\s\"(),;@^`{}~]|$))/;\n\n  // simple-namespace := /^[^\\\\\\/\\[\\]\\d\\s\"#'(),;@^`{}~.][^\\\\\\[\\]\\s\"(),;@^`{}~.\\/]*/\n  // simple-symbol    := /^(?:\\/|[^\\\\\\/\\[\\]\\d\\s\"#'(),;@^`{}~][^\\\\\\[\\]\\s\"(),;@^`{}~]*)/\n  // qualified-symbol := (<simple-namespace>(<.><simple-namespace>)*</>)?<simple-symbol>\n  var qualifiedSymbol = /^(?:(?:[^\\\\\\/\\[\\]\\d\\s\"#'(),;@^`{}~.][^\\\\\\[\\]\\s\"(),;@^`{}~.\\/]*(?:\\.[^\\\\\\/\\[\\]\\d\\s\"#'(),;@^`{}~.][^\\\\\\[\\]\\s\"(),;@^`{}~.\\/]*)*\\/)?(?:\\/|[^\\\\\\/\\[\\]\\d\\s\"#'(),;@^`{}~][^\\\\\\[\\]\\s\"(),;@^`{}~]*)*(?=[\\\\\\[\\]\\s\"(),;@^`{}~]|$))/;\n\n  function base(stream, state) {\n    if (stream.eatSpace() || stream.eat(\",\")) return [\"space\", null];\n    if (stream.match(numberLiteral)) return [null, \"number\"];\n    if (stream.match(characterLiteral)) return [null, \"string-2\"];\n    if (stream.eat(/^\"/)) return (state.tokenize = inString)(stream, state);\n    if (stream.eat(/^[(\\[{]/)) return [\"open\", \"bracket\"];\n    if (stream.eat(/^[)\\]}]/)) return [\"close\", \"bracket\"];\n    if (stream.eat(/^;/)) {stream.skipToEnd(); return [\"space\", \"comment\"];}\n    if (stream.eat(/^[#'@^`~]/)) return [null, \"meta\"];\n\n    var matches = stream.match(qualifiedSymbol);\n    var symbol = matches && matches[0];\n\n    if (!symbol) {\n      // advance stream by at least one character so we don't get stuck.\n      stream.next();\n      stream.eatWhile(function (c) {return !is(c, delimiter);});\n      return [null, \"error\"];\n    }\n\n    if (symbol === \"comment\" && state.lastToken === \"(\")\n      return (state.tokenize = inComment)(stream, state);\n    if (is(symbol, atom) || symbol.charAt(0) === \":\") return [\"symbol\", \"atom\"];\n    if (is(symbol, specialForm) || is(symbol, coreSymbol)) return [\"symbol\", \"keyword\"];\n    if (state.lastToken === \"(\") return [\"symbol\", \"builtin\"]; // other operator\n\n    return [\"symbol\", \"variable\"];\n  }\n\n  function inString(stream, state) {\n    var escaped = false, next;\n\n    while (next = stream.next()) {\n      if (next === \"\\\"\" && !escaped) {state.tokenize = base; break;}\n      escaped = !escaped && next === \"\\\\\";\n    }\n\n    return [null, \"string\"];\n  }\n\n  function inComment(stream, state) {\n    var parenthesisCount = 1;\n    var next;\n\n    while (next = stream.next()) {\n      if (next === \")\") parenthesisCount--;\n      if (next === \"(\") parenthesisCount++;\n      if (parenthesisCount === 0) {\n        stream.backUp(1);\n        state.tokenize = base;\n        break;\n      }\n    }\n\n    return [\"space\", \"comment\"];\n  }\n\n  function createLookupMap(words) {\n    var obj = {};\n\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n\n    return obj;\n  }\n\n  function is(value, test) {\n    if (test instanceof RegExp) return test.test(value);\n    if (test instanceof Object) return test.propertyIsEnumerable(value);\n  }\n\n  return {\n    startState: function () {\n      return {\n        ctx: {prev: null, start: 0, indentTo: 0},\n        lastToken: null,\n        tokenize: base\n      };\n    },\n\n    token: function (stream, state) {\n      if (stream.sol() && (typeof state.ctx.indentTo !== \"number\"))\n        state.ctx.indentTo = state.ctx.start + 1;\n\n      var typeStylePair = state.tokenize(stream, state);\n      var type = typeStylePair[0];\n      var style = typeStylePair[1];\n      var current = stream.current();\n\n      if (type !== \"space\") {\n        if (state.lastToken === \"(\" && state.ctx.indentTo === null) {\n          if (type === \"symbol\" && is(current, hasBodyParameter))\n            state.ctx.indentTo = state.ctx.start + options.indentUnit;\n          else state.ctx.indentTo = \"next\";\n        } else if (state.ctx.indentTo === \"next\") {\n          state.ctx.indentTo = stream.column();\n        }\n\n        state.lastToken = current;\n      }\n\n      if (type === \"open\")\n        state.ctx = {prev: state.ctx, start: stream.column(), indentTo: null};\n      else if (type === \"close\") state.ctx = state.ctx.prev || state.ctx;\n\n      return style;\n    },\n\n    indent: function (state) {\n      var i = state.ctx.indentTo;\n\n      return (typeof i === \"number\") ?\n        i :\n        state.ctx.start + 1;\n    },\n\n    closeBrackets: {pairs: \"()[]{}\\\"\\\"\"},\n    lineComment: \";;\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-clojure\", \"clojure\");\nCodeMirror.defineMIME(\"text/x-clojurescript\", \"clojure\");\nCodeMirror.defineMIME(\"application/edn\", \"clojure\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/clojure/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Clojure mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/closebrackets.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"clojure.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Clojure</a>\n  </ul>\n</div>\n\n<article>\n<h2>Clojure mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n(ns game-of-life\n  \"Conway's Game of Life, based on the work of\n  Christophe Grand (http://clj-me.cgrand.net/2011/08/19/conways-game-of-life)\n  and Laurent Petit (https://gist.github.com/1200343).\")\n\n;;; Core game of life's algorithm functions\n\n(defn neighbors\n  \"Given a cell's coordinates `[x y]`, returns the coordinates of its\n  neighbors.\"\n  [[x y]]\n  (for [dx [-1 0 1]\n        dy (if (zero? dx)\n             [-1 1]\n             [-1 0 1])]\n    [(+ dx x) (+ dy y)]))\n\n(defn step\n  \"Given a set of living `cells`, computes the new set of living cells.\"\n  [cells]\n  (set (for [[cell n] (frequencies (mapcat neighbors cells))\n             :when (or (= n 3)\n                       (and (= n 2)\n                            (cells cell)))]\n         cell)))\n\n;;; Utility methods for displaying game on a text terminal\n\n(defn print-grid\n  \"Prints a `grid` of `w` columns and `h` rows, on *out*, representing a\n  step in the game.\"\n  [grid w h]\n  (doseq [x (range (inc w))\n          y (range (inc h))]\n    (when (= y 0) (println))\n    (print (if (grid [x y])\n             \"[X]\"\n             \" . \"))))\n\n(defn print-grids\n  \"Prints a sequence of `grids` of `w` columns and `h` rows on *out*,\n  representing several steps.\"\n  [grids w h]\n  (doseq [grid grids]\n    (print-grid grid w h)\n    (println)))\n\n;;; Launches an example grid\n\n(def grid\n  \"`grid` represents the initial set of living cells\"\n  #{[2 1] [2 2] [2 3]})\n\n(print-grids (take 3 (iterate step grid)) 5 5)</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n          autoCloseBrackets: true,\n          lineNumbers: true,\n          matchBrackets: true,\n          mode: 'text/x-clojure'\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-clojure</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/clojure/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function () {\n    var mode = CodeMirror.getMode({indentUnit: 2}, \"clojure\");\n\n    function MT(name) {\n        test.mode(name, mode, Array.prototype.slice.call(arguments, 1));\n    }\n\n    MT(\"atoms\",\n        \"[atom false]\",\n        \"[atom nil]\",\n        \"[atom true]\"\n    );\n\n    MT(\"keywords\",\n        \"[atom :foo]\",\n        \"[atom ::bar]\",\n        \"[atom :foo/bar]\",\n        \"[atom :foo.bar/baz]\"\n    );\n\n    MT(\"numbers\",\n        \"[number 42] [number +42] [number -421]\",\n        \"[number 42N] [number +42N] [number -42N]\",\n        \"[number 0.42] [number +0.42] [number -0.42]\",\n        \"[number 42M] [number +42M] [number -42M]\",\n        \"[number 42.42M] [number +42.42M] [number -42.42M]\",\n        \"[number 1/42] [number +1/42] [number -1/42]\",\n        \"[number 0x42af] [number +0x42af] [number -0x42af]\",\n        \"[number 0x42AF] [number +0x42AF] [number -0x42AF]\",\n        \"[number 1e2] [number 1e+2] [number 1e-2]\",\n        \"[number +1e2] [number +1e+2] [number +1e-2]\",\n        \"[number -1e2] [number -1e+2] [number -1e-2]\",\n        \"[number -1.0e2] [number -0.1e+2] [number -1.01e-2]\",\n        \"[number 1E2] [number 1E+2] [number 1E-2]\",\n        \"[number +1E2] [number +1E+2] [number +1E-2]\",\n        \"[number -1E2] [number -1E+2] [number -1E-2]\",\n        \"[number -1.0E2] [number -0.1E+2] [number -1.01E-2]\",\n        \"[number 2r101010] [number +2r101010] [number -2r101010]\",\n        \"[number 2r101010] [number +2r101010] [number -2r101010]\",\n        \"[number 8r52] [number +8r52] [number -8r52]\",\n        \"[number 36rhello] [number +36rhello] [number -36rhello]\",\n        \"[number 36rz] [number +36rz] [number -36rz]\",\n        \"[number 36rZ] [number +36rZ] [number -36rZ]\",\n\n        // invalid numbers\n        \"[error 42foo]\",\n        \"[error 42Nfoo]\",\n        \"[error 42Mfoo]\",\n        \"[error 42.42Mfoo]\",\n        \"[error 42.42M!]\",\n        \"[error 42!]\",\n        \"[error 0x42afm]\"\n    );\n\n    MT(\"characters\",\n        \"[string-2 \\\\1]\",\n        \"[string-2 \\\\a]\",\n        \"[string-2 \\\\a\\\\b\\\\c]\",\n        \"[string-2 \\\\#]\",\n        \"[string-2 \\\\\\\\]\",\n        \"[string-2 \\\\\\\"]\",\n        \"[string-2 \\\\(]\",\n        \"[string-2 \\\\A]\",\n        \"[string-2 \\\\backspace]\",\n        \"[string-2 \\\\formfeed]\",\n        \"[string-2 \\\\newline]\",\n        \"[string-2 \\\\space]\",\n        \"[string-2 \\\\return]\",\n        \"[string-2 \\\\tab]\",\n        \"[string-2 \\\\u1000]\",\n        \"[string-2 \\\\uAaAa]\",\n        \"[string-2 \\\\u9F9F]\",\n        \"[string-2 \\\\o123]\",\n        \"[string-2 \\\\符]\",\n        \"[string-2 \\\\シ]\",\n        \"[string-2 \\\\ۇ]\",\n        // FIXME\n        // \"[string-2 \\\\🙂]\",\n\n        // invalid character literals\n        \"[error \\\\abc]\",\n        \"[error \\\\a123]\",\n        \"[error \\\\a!]\",\n        \"[error \\\\newlines]\",\n        \"[error \\\\NEWLINE]\",\n        \"[error \\\\u9F9FF]\",\n        \"[error \\\\o1234]\"\n    );\n\n    MT(\"strings\",\n        \"[string \\\"I'm a teapot.\\\"]\",\n        \"[string \\\"I'm a \\\\\\\"teapot\\\\\\\".\\\"]\",\n        \"[string \\\"I'm]\",       // this is\n        \"[string a]\",           // a multi-line\n        \"[string teapot.\\\"]\"    // string\n\n        // TODO unterminated (multi-line) strings?\n    );\n\n    MT(\"comments\",\n        \"[comment ; this is an in-line comment.]\",\n        \"[comment ;; this is a line comment.]\",\n        \"[keyword comment]\",\n        \"[bracket (][comment comment (foo 1 2 3)][bracket )]\"\n    );\n\n    MT(\"reader macro characters\",\n        \"[meta #][variable _]\",\n        \"[meta #][variable -Inf]\",\n        \"[meta ##][variable Inf]\",\n        \"[meta ##][variable NaN]\",\n        \"[meta @][variable x]\",\n        \"[meta ^][bracket {][atom :tag] [variable String][bracket }]\",\n        \"[meta `][bracket (][builtin f] [variable x][bracket )]\",\n        \"[meta ~][variable foo#]\",\n        \"[meta '][number 1]\",\n        \"[meta '][atom :foo]\",\n        \"[meta '][string \\\"foo\\\"]\",\n        \"[meta '][variable x]\",\n        \"[meta '][bracket (][builtin a] [variable b] [variable c][bracket )]\",\n        \"[meta '][bracket [[][variable a] [variable b] [variable c][bracket ]]]\",\n        \"[meta '][bracket {][variable a] [number 1] [atom :foo] [number 2] [variable c] [number 3][bracket }]\",\n        \"[meta '#][bracket {][variable a] [number 1] [atom :foo][bracket }]\"\n    );\n\n    MT(\"symbols\",\n      \"[variable foo!]\",\n      \"[variable foo#]\",\n      \"[variable foo$]\",\n      \"[variable foo&]\",\n      \"[variable foo']\",\n      \"[variable foo*]\",\n      \"[variable foo+]\",\n      \"[variable foo-]\",\n      \"[variable foo.]\",\n      \"[variable foo/bar]\",\n      \"[variable foo:bar]\",\n      \"[variable foo<]\",\n      \"[variable foo=]\",\n      \"[variable foo>]\",\n      \"[variable foo?]\",\n      \"[variable foo_]\",\n      \"[variable foo|]\",\n      \"[variable foobarBaz]\",\n      \"[variable foo¡]\",\n      \"[variable 符号]\",\n      \"[variable シンボル]\",\n      \"[variable ئۇيغۇر]\",\n      \"[variable 🙂❤🇺🇸]\",\n\n      // invalid symbols\n      \"[error 3foo]\",\n      \"[error 3+]\",\n      \"[error 3|]\",\n      \"[error 3_]\"\n    );\n\n    MT(\"numbers and other forms\",\n      \"[number 42][bracket (][builtin foo][bracket )]\",\n      \"[number 42][bracket [[][variable foo][bracket ]]]\",\n      \"[number 42][meta #][bracket {][variable foo][bracket }]\",\n      \"[number 42][bracket {][atom :foo] [variable bar][bracket }]\",\n      \"[number 42][meta `][variable foo]\",\n      \"[number 42][meta ~][variable foo]\",\n      \"[number 42][meta #][variable foo]\"\n    );\n\n    var specialForms = [\".\", \"catch\", \"def\", \"do\", \"if\", \"monitor-enter\",\n        \"monitor-exit\", \"new\", \"quote\", \"recur\", \"set!\", \"throw\", \"try\", \"var\"];\n\n    MT(\"should highlight special forms as keywords\",\n        typeTokenPairs(\"keyword\", specialForms)\n    );\n\n    var coreSymbols1 = [\n        \"*\", \"*'\", \"*1\", \"*2\", \"*3\", \"*agent*\", \"*allow-unresolved-vars*\", \"*assert*\",\n        \"*clojure-version*\", \"*command-line-args*\", \"*compile-files*\", \"*compile-path*\", \"*compiler-options*\",\n        \"*data-readers*\", \"*default-data-reader-fn*\", \"*e\", \"*err*\", \"*file*\", \"*flush-on-newline*\", \"*fn-loader*\",\n        \"*in*\", \"*math-context*\", \"*ns*\", \"*out*\", \"*print-dup*\", \"*print-length*\", \"*print-level*\", \"*print-meta*\",\n        \"*print-namespace-maps*\", \"*print-readably*\", \"*read-eval*\", \"*reader-resolver*\", \"*source-path*\",\n        \"*suppress-read*\", \"*unchecked-math*\", \"*use-context-classloader*\", \"*verbose-defrecords*\",\n        \"*warn-on-reflection*\", \"+\", \"+'\", \"-\", \"-'\", \"->\", \"->>\", \"->ArrayChunk\", \"->Eduction\", \"->Vec\", \"->VecNode\",\n        \"->VecSeq\", \"-cache-protocol-fn\", \"-reset-methods\", \"..\", \"/\", \"<\", \"<=\", \"=\", \"==\", \">\", \">=\",\n        \"EMPTY-NODE\", \"Inst\", \"StackTraceElement->vec\", \"Throwable->map\", \"accessor\", \"aclone\", \"add-classpath\",\n        \"add-watch\", \"agent\", \"agent-error\", \"agent-errors\", \"aget\", \"alength\", \"alias\", \"all-ns\", \"alter\",\n        \"alter-meta!\", \"alter-var-root\", \"amap\", \"ancestors\", \"and\", \"any?\", \"apply\", \"areduce\", \"array-map\",\n        \"as->\", \"aset\", \"aset-boolean\", \"aset-byte\", \"aset-char\", \"aset-double\", \"aset-float\", \"aset-int\",\n        \"aset-long\", \"aset-short\", \"assert\", \"assoc\", \"assoc!\", \"assoc-in\", \"associative?\", \"atom\", \"await\",\n        \"await-for\", \"await1\", \"bases\", \"bean\", \"bigdec\", \"bigint\", \"biginteger\", \"binding\", \"bit-and\", \"bit-and-not\",\n        \"bit-clear\", \"bit-flip\", \"bit-not\", \"bit-or\", \"bit-set\", \"bit-shift-left\", \"bit-shift-right\", \"bit-test\",\n        \"bit-xor\", \"boolean\", \"boolean-array\", \"boolean?\", \"booleans\", \"bound-fn\", \"bound-fn*\", \"bound?\",\n        \"bounded-count\", \"butlast\", \"byte\", \"byte-array\", \"bytes\", \"bytes?\", \"case\", \"cast\", \"cat\", \"char\",\n        \"char-array\", \"char-escape-string\", \"char-name-string\", \"char?\", \"chars\", \"chunk\", \"chunk-append\",\n        \"chunk-buffer\", \"chunk-cons\", \"chunk-first\", \"chunk-next\", \"chunk-rest\", \"chunked-seq?\", \"class\", \"class?\",\n        \"clear-agent-errors\", \"clojure-version\", \"coll?\", \"comment\", \"commute\", \"comp\", \"comparator\", \"compare\",\n        \"compare-and-set!\", \"compile\", \"complement\", \"completing\", \"concat\", \"cond\", \"cond->\", \"cond->>\", \"condp\",\n        \"conj\", \"conj!\", \"cons\", \"constantly\", \"construct-proxy\", \"contains?\", \"count\", \"counted?\", \"create-ns\",\n        \"create-struct\", \"cycle\", \"dec\", \"dec'\", \"decimal?\", \"declare\", \"dedupe\", \"default-data-readers\", \"definline\",\n        \"definterface\", \"defmacro\", \"defmethod\", \"defmulti\", \"defn\", \"defn-\", \"defonce\", \"defprotocol\", \"defrecord\",\n        \"defstruct\", \"deftype\", \"delay\", \"delay?\", \"deliver\", \"denominator\", \"deref\", \"derive\", \"descendants\",\n        \"destructure\", \"disj\", \"disj!\", \"dissoc\", \"dissoc!\", \"distinct\", \"distinct?\", \"doall\", \"dorun\", \"doseq\",\n        \"dosync\", \"dotimes\", \"doto\", \"double\", \"double-array\", \"double?\", \"doubles\", \"drop\", \"drop-last\", \"drop-while\",\n        \"eduction\", \"empty\", \"empty?\", \"ensure\", \"ensure-reduced\", \"enumeration-seq\", \"error-handler\", \"error-mode\",\n        \"eval\", \"even?\", \"every-pred\", \"every?\", \"ex-data\", \"ex-info\", \"extend\", \"extend-protocol\", \"extend-type\",\n        \"extenders\", \"extends?\", \"false?\", \"ffirst\", \"file-seq\", \"filter\", \"filterv\", \"find\", \"find-keyword\", \"find-ns\",\n        \"find-protocol-impl\", \"find-protocol-method\", \"find-var\", \"first\", \"flatten\", \"float\", \"float-array\", \"float?\",\n        \"floats\", \"flush\", \"fn\", \"fn?\", \"fnext\", \"fnil\", \"for\", \"force\", \"format\", \"frequencies\", \"future\", \"future-call\",\n        \"future-cancel\", \"future-cancelled?\", \"future-done?\", \"future?\", \"gen-class\", \"gen-interface\", \"gensym\", \"get\",\n        \"get-in\", \"get-method\", \"get-proxy-class\", \"get-thread-bindings\", \"get-validator\", \"group-by\", \"halt-when\", \"hash\",\n        \"hash-combine\", \"hash-map\", \"hash-ordered-coll\", \"hash-set\", \"hash-unordered-coll\", \"ident?\", \"identical?\",\n        \"identity\", \"if-let\", \"if-not\", \"if-some\", \"ifn?\", \"import\", \"in-ns\", \"inc\", \"inc'\", \"indexed?\", \"init-proxy\",\n        \"inst-ms\", \"inst-ms*\", \"inst?\", \"instance?\", \"int\", \"int-array\", \"int?\", \"integer?\", \"interleave\", \"intern\",\n        \"interpose\", \"into\", \"into-array\", \"ints\", \"io!\", \"isa?\", \"iterate\", \"iterator-seq\", \"juxt\", \"keep\", \"keep-indexed\",\n        \"key\", \"keys\", \"keyword\", \"keyword?\", \"last\", \"lazy-cat\", \"lazy-seq\", \"let\", \"letfn\", \"line-seq\", \"list\", \"list*\",\n        \"list?\", \"load\", \"load-file\", \"load-reader\", \"load-string\", \"loaded-libs\", \"locking\", \"long\", \"long-array\", \"longs\",\n        \"loop\", \"macroexpand\", \"macroexpand-1\", \"make-array\", \"make-hierarchy\", \"map\", \"map-entry?\", \"map-indexed\", \"map?\",\n        \"mapcat\", \"mapv\", \"max\", \"max-key\", \"memfn\", \"memoize\", \"merge\", \"merge-with\", \"meta\", \"method-sig\", \"methods\"];\n\n    var coreSymbols2 = [\n        \"min\", \"min-key\", \"mix-collection-hash\", \"mod\", \"munge\", \"name\", \"namespace\", \"namespace-munge\", \"nat-int?\",\n        \"neg-int?\", \"neg?\", \"newline\", \"next\", \"nfirst\", \"nil?\", \"nnext\", \"not\", \"not-any?\", \"not-empty\", \"not-every?\",\n        \"not=\", \"ns\", \"ns-aliases\", \"ns-imports\", \"ns-interns\", \"ns-map\", \"ns-name\", \"ns-publics\", \"ns-refers\", \"ns-resolve\",\n        \"ns-unalias\", \"ns-unmap\", \"nth\", \"nthnext\", \"nthrest\", \"num\", \"number?\", \"numerator\", \"object-array\", \"odd?\", \"or\",\n        \"parents\", \"partial\", \"partition\", \"partition-all\", \"partition-by\", \"pcalls\", \"peek\", \"persistent!\", \"pmap\", \"pop\",\n        \"pop!\", \"pop-thread-bindings\", \"pos-int?\", \"pos?\", \"pr\", \"pr-str\", \"prefer-method\", \"prefers\",\n        \"primitives-classnames\", \"print\", \"print-ctor\", \"print-dup\", \"print-method\", \"print-simple\", \"print-str\", \"printf\",\n        \"println\", \"println-str\", \"prn\", \"prn-str\", \"promise\", \"proxy\", \"proxy-call-with-super\", \"proxy-mappings\",\n        \"proxy-name\", \"proxy-super\", \"push-thread-bindings\", \"pvalues\", \"qualified-ident?\", \"qualified-keyword?\",\n        \"qualified-symbol?\", \"quot\", \"rand\", \"rand-int\", \"rand-nth\", \"random-sample\", \"range\", \"ratio?\", \"rational?\",\n        \"rationalize\", \"re-find\", \"re-groups\", \"re-matcher\", \"re-matches\", \"re-pattern\", \"re-seq\", \"read\", \"read-line\",\n        \"read-string\", \"reader-conditional\", \"reader-conditional?\", \"realized?\", \"record?\", \"reduce\", \"reduce-kv\", \"reduced\",\n        \"reduced?\", \"reductions\", \"ref\", \"ref-history-count\", \"ref-max-history\", \"ref-min-history\", \"ref-set\", \"refer\",\n        \"refer-clojure\", \"reify\", \"release-pending-sends\", \"rem\", \"remove\", \"remove-all-methods\", \"remove-method\", \"remove-ns\",\n        \"remove-watch\", \"repeat\", \"repeatedly\", \"replace\", \"replicate\", \"require\", \"reset!\", \"reset-meta!\", \"reset-vals!\",\n        \"resolve\", \"rest\", \"restart-agent\", \"resultset-seq\", \"reverse\", \"reversible?\", \"rseq\", \"rsubseq\", \"run!\", \"satisfies?\",\n        \"second\", \"select-keys\", \"send\", \"send-off\", \"send-via\", \"seq\", \"seq?\", \"seqable?\", \"seque\", \"sequence\", \"sequential?\",\n        \"set\", \"set-agent-send-executor!\", \"set-agent-send-off-executor!\", \"set-error-handler!\", \"set-error-mode!\",\n        \"set-validator!\", \"set?\", \"short\", \"short-array\", \"shorts\", \"shuffle\", \"shutdown-agents\", \"simple-ident?\",\n        \"simple-keyword?\", \"simple-symbol?\", \"slurp\", \"some\", \"some->\", \"some->>\", \"some-fn\", \"some?\", \"sort\", \"sort-by\",\n        \"sorted-map\", \"sorted-map-by\", \"sorted-set\", \"sorted-set-by\", \"sorted?\", \"special-symbol?\", \"spit\", \"split-at\",\n        \"split-with\", \"str\", \"string?\", \"struct\", \"struct-map\", \"subs\", \"subseq\", \"subvec\", \"supers\", \"swap!\", \"swap-vals!\",\n        \"symbol\", \"symbol?\", \"sync\", \"tagged-literal\", \"tagged-literal?\", \"take\", \"take-last\", \"take-nth\", \"take-while\", \"test\",\n        \"the-ns\", \"thread-bound?\", \"time\", \"to-array\", \"to-array-2d\", \"trampoline\", \"transduce\", \"transient\", \"tree-seq\",\n        \"true?\", \"type\", \"unchecked-add\", \"unchecked-add-int\", \"unchecked-byte\", \"unchecked-char\", \"unchecked-dec\",\n        \"unchecked-dec-int\", \"unchecked-divide-int\", \"unchecked-double\", \"unchecked-float\", \"unchecked-inc\", \"unchecked-inc-int\",\n        \"unchecked-int\", \"unchecked-long\", \"unchecked-multiply\", \"unchecked-multiply-int\", \"unchecked-negate\",\n        \"unchecked-negate-int\", \"unchecked-remainder-int\", \"unchecked-short\", \"unchecked-subtract\", \"unchecked-subtract-int\",\n        \"underive\", \"unquote\", \"unquote-splicing\", \"unreduced\", \"unsigned-bit-shift-right\", \"update\", \"update-in\",\n        \"update-proxy\", \"uri?\", \"use\", \"uuid?\", \"val\", \"vals\", \"var-get\", \"var-set\", \"var?\", \"vary-meta\", \"vec\", \"vector\",\n        \"vector-of\", \"vector?\", \"volatile!\", \"volatile?\", \"vreset!\", \"vswap!\", \"when\", \"when-first\", \"when-let\", \"when-not\",\n        \"when-some\", \"while\", \"with-bindings\", \"with-bindings*\", \"with-in-str\", \"with-loading-context\", \"with-local-vars\",\n        \"with-meta\", \"with-open\", \"with-out-str\", \"with-precision\", \"with-redefs\", \"with-redefs-fn\", \"xml-seq\", \"zero?\",\n        \"zipmap\"\n    ];\n\n    MT(\"should highlight core symbols as keywords (part 1/2)\",\n        typeTokenPairs(\"keyword\", coreSymbols1)\n    );\n\n    MT(\"should highlight core symbols as keywords (part 2/2)\",\n        typeTokenPairs(\"keyword\", coreSymbols2)\n    );\n\n    MT(\"should properly indent forms in list literals\",\n        \"[bracket (][builtin foo] [atom :a] [number 1] [atom true] [atom nil][bracket )]\",\n        \"\",\n        \"[bracket (][builtin foo] [atom :a]\",\n        \"     [number 1]\",\n        \"     [atom true]\",\n        \"     [atom nil][bracket )]\",\n        \"\",\n        \"[bracket (][builtin foo] [atom :a] [number 1]\",\n        \"     [atom true]\",\n        \"     [atom nil][bracket )]\",\n        \"\",\n        \"[bracket (]\",\n        \" [builtin foo]\",\n        \" [atom :a]\",\n        \" [number 1]\",\n        \" [atom true]\",\n        \" [atom nil][bracket )]\",\n        \"\",\n        \"[bracket (][builtin foo] [bracket [[][atom :a][bracket ]]]\",\n        \"     [number 1]\",\n        \"     [atom true]\",\n        \"     [atom nil][bracket )]\"\n    );\n\n    MT(\"should properly indent forms in vector literals\",\n        \"[bracket [[][atom :a] [number 1] [atom true] [atom nil][bracket ]]]\",\n        \"\",\n        \"[bracket [[][atom :a]\",\n        \" [number 1]\",\n        \" [atom true]\",\n        \" [atom nil][bracket ]]]\",\n        \"\",\n        \"[bracket [[][atom :a] [number 1]\",\n        \" [atom true]\",\n        \" [atom nil][bracket ]]]\",\n        \"\",\n        \"[bracket [[]\",\n        \" [variable foo]\",\n        \" [atom :a]\",\n        \" [number 1]\",\n        \" [atom true]\",\n        \" [atom nil][bracket ]]]\"\n    );\n\n    MT(\"should properly indent forms in map literals\",\n        \"[bracket {][atom :a] [atom :a] [atom :b] [number 1] [atom :c] [atom true] [atom :d] [atom nil] [bracket }]\",\n        \"\",\n        \"[bracket {][atom :a] [atom :a]\",\n        \" [atom :b] [number 1]\",\n        \" [atom :c] [atom true]\",\n        \" [atom :d] [atom nil][bracket }]\",\n        \"\",\n        \"[bracket {][atom :a]\",\n        \" [atom :a]\",\n        \" [atom :b]\",\n        \" [number 1]\",\n        \" [atom :c]\",\n        \" [atom true]\",\n        \" [atom :d]\",\n        \" [atom nil][bracket }]\",\n        \"\",\n        \"[bracket {]\",\n        \" [atom :a] [atom :a]\",\n        \" [atom :b] [number 1]\",\n        \" [atom :c] [atom true]\",\n        \" [atom :d] [atom nil][bracket }]\"\n    );\n\n    MT(\"should properly indent forms in set literals\",\n        \"[meta #][bracket {][atom :a] [number 1] [atom true] [atom nil] [bracket }]\",\n        \"\",\n        \"[meta #][bracket {][atom :a]\",\n        \"  [number 1]\",\n        \"  [atom true]\",\n        \"  [atom nil][bracket }]\",\n        \"\",\n        \"[meta #][bracket {]\",\n        \"  [atom :a]\",\n        \"  [number 1]\",\n        \"  [atom true]\",\n        \"  [atom nil][bracket }]\"\n    );\n\n    var haveBodyParameter = [\n        \"->\", \"->>\", \"as->\", \"binding\", \"bound-fn\", \"case\", \"catch\", \"cond\",\n        \"cond->\", \"cond->>\", \"condp\", \"def\", \"definterface\", \"defmethod\", \"defn\",\n        \"defmacro\", \"defprotocol\", \"defrecord\", \"defstruct\", \"deftype\", \"do\",\n        \"doseq\", \"dotimes\", \"doto\", \"extend\", \"extend-protocol\", \"extend-type\",\n        \"fn\", \"for\", \"future\", \"if\", \"if-let\", \"if-not\", \"if-some\", \"let\",\n        \"letfn\", \"locking\", \"loop\", \"ns\", \"proxy\", \"reify\", \"some->\", \"some->>\",\n        \"struct-map\", \"try\", \"when\", \"when-first\", \"when-let\", \"when-not\",\n        \"when-some\", \"while\", \"with-bindings\", \"with-bindings*\", \"with-in-str\",\n        \"with-loading-context\", \"with-local-vars\", \"with-meta\", \"with-open\",\n        \"with-out-str\", \"with-precision\", \"with-redefs\", \"with-redefs-fn\"];\n\n    function testFormsThatHaveBodyParameter(forms) {\n        for (var i = 0; i < forms.length; i++) {\n            MT(\"should indent body argument of `\" + forms[i] + \"` by `options.indentUnit` spaces\",\n                \"[bracket (][keyword \" + forms[i] + \"] [variable foo] [variable bar]\",\n                \"  [variable baz]\",\n                \"  [variable qux][bracket )]\"\n            );\n        }\n    }\n\n    testFormsThatHaveBodyParameter(haveBodyParameter);\n\n    MT(\"should indent body argument of `comment` by `options.indentUnit` spaces\",\n        \"[bracket (][comment comment foo bar]\",\n        \"[comment  baz]\",\n        \"[comment  qux][bracket )]\"\n    );\n\n    function typeTokenPairs(type, tokens) {\n        return \"[\" + type + \" \" + tokens.join(\"] [\" + type + \" \") + \"]\";\n    }\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/cmake/cmake.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\")\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd)\n    define([\"../../lib/codemirror\"], mod);\n  else\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"cmake\", function () {\n  var variable_regex = /({)?[a-zA-Z0-9_]+(})?/;\n\n  function tokenString(stream, state) {\n    var current, prev, found_var = false;\n    while (!stream.eol() && (current = stream.next()) != state.pending) {\n      if (current === '$' && prev != '\\\\' && state.pending == '\"') {\n        found_var = true;\n        break;\n      }\n      prev = current;\n    }\n    if (found_var) {\n      stream.backUp(1);\n    }\n    if (current == state.pending) {\n      state.continueString = false;\n    } else {\n      state.continueString = true;\n    }\n    return \"string\";\n  }\n\n  function tokenize(stream, state) {\n    var ch = stream.next();\n\n    // Have we found a variable?\n    if (ch === '$') {\n      if (stream.match(variable_regex)) {\n        return 'variable-2';\n      }\n      return 'variable';\n    }\n    // Should we still be looking for the end of a string?\n    if (state.continueString) {\n      // If so, go through the loop again\n      stream.backUp(1);\n      return tokenString(stream, state);\n    }\n    // Do we just have a function on our hands?\n    // In 'cmake_minimum_required (VERSION 2.8.8)', 'cmake_minimum_required' is matched\n    if (stream.match(/(\\s+)?\\w+\\(/) || stream.match(/(\\s+)?\\w+\\ \\(/)) {\n      stream.backUp(1);\n      return 'def';\n    }\n    if (ch == \"#\") {\n      stream.skipToEnd();\n      return \"comment\";\n    }\n    // Have we found a string?\n    if (ch == \"'\" || ch == '\"') {\n      // Store the type (single or double)\n      state.pending = ch;\n      // Perform the looping function to find the end\n      return tokenString(stream, state);\n    }\n    if (ch == '(' || ch == ')') {\n      return 'bracket';\n    }\n    if (ch.match(/[0-9]/)) {\n      return 'number';\n    }\n    stream.eatWhile(/[\\w-]/);\n    return null;\n  }\n  return {\n    startState: function () {\n      var state = {};\n      state.inDefinition = false;\n      state.inInclude = false;\n      state.continueString = false;\n      state.pending = false;\n      return state;\n    },\n    token: function (stream, state) {\n      if (stream.eatSpace()) return null;\n      return tokenize(stream, state);\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-cmake\", \"cmake\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/cmake/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: CMake mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"cmake.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n      .cm-s-default span.cm-arrow { color: red; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">CMake</a>\n  </ul>\n</div>\n\n<article>\n<h2>CMake mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n# vim: syntax=cmake\nif(NOT CMAKE_BUILD_TYPE)\n    # default to Release build for GCC builds\n    set(CMAKE_BUILD_TYPE Release CACHE STRING\n        \"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.\"\n        FORCE)\nendif()\nmessage(STATUS \"cmake version ${CMAKE_VERSION}\")\nif(POLICY CMP0025)\n    cmake_policy(SET CMP0025 OLD) # report Apple's Clang as just Clang\nendif()\nif(POLICY CMP0042)\n    cmake_policy(SET CMP0042 NEW) # MACOSX_RPATH\nendif()\n\nproject (x265)\ncmake_minimum_required (VERSION 2.8.8) # OBJECT libraries require 2.8.8\ninclude(CheckIncludeFiles)\ninclude(CheckFunctionExists)\ninclude(CheckSymbolExists)\ninclude(CheckCXXCompilerFlag)\n\n# X265_BUILD must be incremented each time the public API is changed\nset(X265_BUILD 48)\nconfigure_file(\"${PROJECT_SOURCE_DIR}/x265.def.in\"\n               \"${PROJECT_BINARY_DIR}/x265.def\")\nconfigure_file(\"${PROJECT_SOURCE_DIR}/x265_config.h.in\"\n               \"${PROJECT_BINARY_DIR}/x265_config.h\")\n\nSET(CMAKE_MODULE_PATH \"${PROJECT_SOURCE_DIR}/cmake\" \"${CMAKE_MODULE_PATH}\")\n\n# System architecture detection\nstring(TOLOWER \"${CMAKE_SYSTEM_PROCESSOR}\" SYSPROC)\nset(X86_ALIASES x86 i386 i686 x86_64 amd64)\nlist(FIND X86_ALIASES \"${SYSPROC}\" X86MATCH)\nif(\"${SYSPROC}\" STREQUAL \"\" OR X86MATCH GREATER \"-1\")\n    message(STATUS \"Detected x86 target processor\")\n    set(X86 1)\n    add_definitions(-DX265_ARCH_X86=1)\n    if(\"${CMAKE_SIZEOF_VOID_P}\" MATCHES 8)\n        set(X64 1)\n        add_definitions(-DX86_64=1)\n    endif()\nelseif(${SYSPROC} STREQUAL \"armv6l\")\n    message(STATUS \"Detected ARM target processor\")\n    set(ARM 1)\n    add_definitions(-DX265_ARCH_ARM=1 -DHAVE_ARMV6=1)\nelse()\n    message(STATUS \"CMAKE_SYSTEM_PROCESSOR value `${CMAKE_SYSTEM_PROCESSOR}` is unknown\")\n    message(STATUS \"Please add this value near ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}\")\nendif()\n\nif(UNIX)\n    list(APPEND PLATFORM_LIBS pthread)\n    find_library(LIBRT rt)\n    if(LIBRT)\n        list(APPEND PLATFORM_LIBS rt)\n    endif()\n    find_package(Numa)\n    if(NUMA_FOUND)\n        list(APPEND CMAKE_REQUIRED_LIBRARIES ${NUMA_LIBRARY})\n        check_symbol_exists(numa_node_of_cpu numa.h NUMA_V2)\n        if(NUMA_V2)\n            add_definitions(-DHAVE_LIBNUMA)\n            message(STATUS \"libnuma found, building with support for NUMA nodes\")\n            list(APPEND PLATFORM_LIBS ${NUMA_LIBRARY})\n            link_directories(${NUMA_LIBRARY_DIR})\n            include_directories(${NUMA_INCLUDE_DIR})\n        endif()\n    endif()\n    mark_as_advanced(LIBRT NUMA_FOUND)\nendif(UNIX)\n\nif(X64 AND NOT WIN32)\n    option(ENABLE_PIC \"Enable Position Independent Code\" ON)\nelse()\n    option(ENABLE_PIC \"Enable Position Independent Code\" OFF)\nendif(X64 AND NOT WIN32)\n\n# Compiler detection\nif(CMAKE_GENERATOR STREQUAL \"Xcode\")\n  set(XCODE 1)\nendif()\nif (APPLE)\n  add_definitions(-DMACOS)\nendif()\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: \"text/x-cmake\",\n        matchBrackets: true,\n        indentUnit: 4\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-cmake</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/cobol/cobol.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/**\n * Author: Gautam Mehta\n * Branched from CodeMirror's Scheme mode\n */\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"cobol\", function () {\n  var BUILTIN = \"builtin\", COMMENT = \"comment\", STRING = \"string\",\n      ATOM = \"atom\", NUMBER = \"number\", KEYWORD = \"keyword\", MODTAG = \"header\",\n      COBOLLINENUM = \"def\", PERIOD = \"link\";\n  function makeKeywords(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n  var atoms = makeKeywords(\"TRUE FALSE ZEROES ZEROS ZERO SPACES SPACE LOW-VALUE LOW-VALUES \");\n  var keywords = makeKeywords(\n      \"ACCEPT ACCESS ACQUIRE ADD ADDRESS \" +\n      \"ADVANCING AFTER ALIAS ALL ALPHABET \" +\n      \"ALPHABETIC ALPHABETIC-LOWER ALPHABETIC-UPPER ALPHANUMERIC ALPHANUMERIC-EDITED \" +\n      \"ALSO ALTER ALTERNATE AND ANY \" +\n      \"ARE AREA AREAS ARITHMETIC ASCENDING \" +\n      \"ASSIGN AT ATTRIBUTE AUTHOR AUTO \" +\n      \"AUTO-SKIP AUTOMATIC B-AND B-EXOR B-LESS \" +\n      \"B-NOT B-OR BACKGROUND-COLOR BACKGROUND-COLOUR BEEP \" +\n      \"BEFORE BELL BINARY BIT BITS \" +\n      \"BLANK BLINK BLOCK BOOLEAN BOTTOM \" +\n      \"BY CALL CANCEL CD CF \" +\n      \"CH CHARACTER CHARACTERS CLASS CLOCK-UNITS \" +\n      \"CLOSE COBOL CODE CODE-SET COL \" +\n      \"COLLATING COLUMN COMMA COMMIT COMMITMENT \" +\n      \"COMMON COMMUNICATION COMP COMP-0 COMP-1 \" +\n      \"COMP-2 COMP-3 COMP-4 COMP-5 COMP-6 \" +\n      \"COMP-7 COMP-8 COMP-9 COMPUTATIONAL COMPUTATIONAL-0 \" +\n      \"COMPUTATIONAL-1 COMPUTATIONAL-2 COMPUTATIONAL-3 COMPUTATIONAL-4 COMPUTATIONAL-5 \" +\n      \"COMPUTATIONAL-6 COMPUTATIONAL-7 COMPUTATIONAL-8 COMPUTATIONAL-9 COMPUTE \" +\n      \"CONFIGURATION CONNECT CONSOLE CONTAINED CONTAINS \" +\n      \"CONTENT CONTINUE CONTROL CONTROL-AREA CONTROLS \" +\n      \"CONVERTING COPY CORR CORRESPONDING COUNT \" +\n      \"CRT CRT-UNDER CURRENCY CURRENT CURSOR \" +\n      \"DATA DATE DATE-COMPILED DATE-WRITTEN DAY \" +\n      \"DAY-OF-WEEK DB DB-ACCESS-CONTROL-KEY DB-DATA-NAME DB-EXCEPTION \" +\n      \"DB-FORMAT-NAME DB-RECORD-NAME DB-SET-NAME DB-STATUS DBCS \" +\n      \"DBCS-EDITED DE DEBUG-CONTENTS DEBUG-ITEM DEBUG-LINE \" +\n      \"DEBUG-NAME DEBUG-SUB-1 DEBUG-SUB-2 DEBUG-SUB-3 DEBUGGING \" +\n      \"DECIMAL-POINT DECLARATIVES DEFAULT DELETE DELIMITED \" +\n      \"DELIMITER DEPENDING DESCENDING DESCRIBED DESTINATION \" +\n      \"DETAIL DISABLE DISCONNECT DISPLAY DISPLAY-1 \" +\n      \"DISPLAY-2 DISPLAY-3 DISPLAY-4 DISPLAY-5 DISPLAY-6 \" +\n      \"DISPLAY-7 DISPLAY-8 DISPLAY-9 DIVIDE DIVISION \" +\n      \"DOWN DROP DUPLICATE DUPLICATES DYNAMIC \" +\n      \"EBCDIC EGI EJECT ELSE EMI \" +\n      \"EMPTY EMPTY-CHECK ENABLE END END. END-ACCEPT END-ACCEPT. \" +\n      \"END-ADD END-CALL END-COMPUTE END-DELETE END-DISPLAY \" +\n      \"END-DIVIDE END-EVALUATE END-IF END-INVOKE END-MULTIPLY \" +\n      \"END-OF-PAGE END-PERFORM END-READ END-RECEIVE END-RETURN \" +\n      \"END-REWRITE END-SEARCH END-START END-STRING END-SUBTRACT \" +\n      \"END-UNSTRING END-WRITE END-XML ENTER ENTRY \" +\n      \"ENVIRONMENT EOP EQUAL EQUALS ERASE \" +\n      \"ERROR ESI EVALUATE EVERY EXCEEDS \" +\n      \"EXCEPTION EXCLUSIVE EXIT EXTEND EXTERNAL \" +\n      \"EXTERNALLY-DESCRIBED-KEY FD FETCH FILE FILE-CONTROL \" +\n      \"FILE-STREAM FILES FILLER FINAL FIND \" +\n      \"FINISH FIRST FOOTING FOR FOREGROUND-COLOR \" +\n      \"FOREGROUND-COLOUR FORMAT FREE FROM FULL \" +\n      \"FUNCTION GENERATE GET GIVING GLOBAL \" +\n      \"GO GOBACK GREATER GROUP HEADING \" +\n      \"HIGH-VALUE HIGH-VALUES HIGHLIGHT I-O I-O-CONTROL \" +\n      \"ID IDENTIFICATION IF IN INDEX \" +\n      \"INDEX-1 INDEX-2 INDEX-3 INDEX-4 INDEX-5 \" +\n      \"INDEX-6 INDEX-7 INDEX-8 INDEX-9 INDEXED \" +\n      \"INDIC INDICATE INDICATOR INDICATORS INITIAL \" +\n      \"INITIALIZE INITIATE INPUT INPUT-OUTPUT INSPECT \" +\n      \"INSTALLATION INTO INVALID INVOKE IS \" +\n      \"JUST JUSTIFIED KANJI KEEP KEY \" +\n      \"LABEL LAST LD LEADING LEFT \" +\n      \"LEFT-JUSTIFY LENGTH LENGTH-CHECK LESS LIBRARY \" +\n      \"LIKE LIMIT LIMITS LINAGE LINAGE-COUNTER \" +\n      \"LINE LINE-COUNTER LINES LINKAGE LOCAL-STORAGE \" +\n      \"LOCALE LOCALLY LOCK \" +\n      \"MEMBER MEMORY MERGE MESSAGE METACLASS \" +\n      \"MODE MODIFIED MODIFY MODULES MOVE \" +\n      \"MULTIPLE MULTIPLY NATIONAL NATIVE NEGATIVE \" +\n      \"NEXT NO NO-ECHO NONE NOT \" +\n      \"NULL NULL-KEY-MAP NULL-MAP NULLS NUMBER \" +\n      \"NUMERIC NUMERIC-EDITED OBJECT OBJECT-COMPUTER OCCURS \" +\n      \"OF OFF OMITTED ON ONLY \" +\n      \"OPEN OPTIONAL OR ORDER ORGANIZATION \" +\n      \"OTHER OUTPUT OVERFLOW OWNER PACKED-DECIMAL \" +\n      \"PADDING PAGE PAGE-COUNTER PARSE PERFORM \" +\n      \"PF PH PIC PICTURE PLUS \" +\n      \"POINTER POSITION POSITIVE PREFIX PRESENT \" +\n      \"PRINTING PRIOR PROCEDURE PROCEDURE-POINTER PROCEDURES \" +\n      \"PROCEED PROCESS PROCESSING PROGRAM PROGRAM-ID \" +\n      \"PROMPT PROTECTED PURGE QUEUE QUOTE \" +\n      \"QUOTES RANDOM RD READ READY \" +\n      \"REALM RECEIVE RECONNECT RECORD RECORD-NAME \" +\n      \"RECORDS RECURSIVE REDEFINES REEL REFERENCE \" +\n      \"REFERENCE-MONITOR REFERENCES RELATION RELATIVE RELEASE \" +\n      \"REMAINDER REMOVAL RENAMES REPEATED REPLACE \" +\n      \"REPLACING REPORT REPORTING REPORTS REPOSITORY \" +\n      \"REQUIRED RERUN RESERVE RESET RETAINING \" +\n      \"RETRIEVAL RETURN RETURN-CODE RETURNING REVERSE-VIDEO \" +\n      \"REVERSED REWIND REWRITE RF RH \" +\n      \"RIGHT RIGHT-JUSTIFY ROLLBACK ROLLING ROUNDED \" +\n      \"RUN SAME SCREEN SD SEARCH \" +\n      \"SECTION SECURE SECURITY SEGMENT SEGMENT-LIMIT \" +\n      \"SELECT SEND SENTENCE SEPARATE SEQUENCE \" +\n      \"SEQUENTIAL SET SHARED SIGN SIZE \" +\n      \"SKIP1 SKIP2 SKIP3 SORT SORT-MERGE \" +\n      \"SORT-RETURN SOURCE SOURCE-COMPUTER SPACE-FILL \" +\n      \"SPECIAL-NAMES STANDARD STANDARD-1 STANDARD-2 \" +\n      \"START STARTING STATUS STOP STORE \" +\n      \"STRING SUB-QUEUE-1 SUB-QUEUE-2 SUB-QUEUE-3 SUB-SCHEMA \" +\n      \"SUBFILE SUBSTITUTE SUBTRACT SUM SUPPRESS \" +\n      \"SYMBOLIC SYNC SYNCHRONIZED SYSIN SYSOUT \" +\n      \"TABLE TALLYING TAPE TENANT TERMINAL \" +\n      \"TERMINATE TEST TEXT THAN THEN \" +\n      \"THROUGH THRU TIME TIMES TITLE \" +\n      \"TO TOP TRAILING TRAILING-SIGN TRANSACTION \" +\n      \"TYPE TYPEDEF UNDERLINE UNEQUAL UNIT \" +\n      \"UNSTRING UNTIL UP UPDATE UPON \" +\n      \"USAGE USAGE-MODE USE USING VALID \" +\n      \"VALIDATE VALUE VALUES VARYING VLR \" +\n      \"WAIT WHEN WHEN-COMPILED WITH WITHIN \" +\n      \"WORDS WORKING-STORAGE WRITE XML XML-CODE \" +\n      \"XML-EVENT XML-NTEXT XML-TEXT ZERO ZERO-FILL \" );\n\n  var builtins = makeKeywords(\"- * ** / + < <= = > >= \");\n  var tests = {\n    digit: /\\d/,\n    digit_or_colon: /[\\d:]/,\n    hex: /[0-9a-f]/i,\n    sign: /[+-]/,\n    exponent: /e/i,\n    keyword_char: /[^\\s\\(\\[\\;\\)\\]]/,\n    symbol: /[\\w*+\\-]/\n  };\n  function isNumber(ch, stream){\n    // hex\n    if ( ch === '0' && stream.eat(/x/i) ) {\n      stream.eatWhile(tests.hex);\n      return true;\n    }\n    // leading sign\n    if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) {\n      stream.eat(tests.sign);\n      ch = stream.next();\n    }\n    if ( tests.digit.test(ch) ) {\n      stream.eat(ch);\n      stream.eatWhile(tests.digit);\n      if ( '.' == stream.peek()) {\n        stream.eat('.');\n        stream.eatWhile(tests.digit);\n      }\n      if ( stream.eat(tests.exponent) ) {\n        stream.eat(tests.sign);\n        stream.eatWhile(tests.digit);\n      }\n      return true;\n    }\n    return false;\n  }\n  return {\n    startState: function () {\n      return {\n        indentStack: null,\n        indentation: 0,\n        mode: false\n      };\n    },\n    token: function (stream, state) {\n      if (state.indentStack == null && stream.sol()) {\n        // update indentation, but only if indentStack is empty\n        state.indentation = 6 ; //stream.indentation();\n      }\n      // skip spaces\n      if (stream.eatSpace()) {\n        return null;\n      }\n      var returnType = null;\n      switch(state.mode){\n      case \"string\": // multi-line string parsing mode\n        var next = false;\n        while ((next = stream.next()) != null) {\n          if ((next == \"\\\"\" || next == \"\\'\") && !stream.match(/['\"]/, false)) {\n            state.mode = false;\n            break;\n          }\n        }\n        returnType = STRING; // continue on in string mode\n        break;\n      default: // default parsing mode\n        var ch = stream.next();\n        var col = stream.column();\n        if (col >= 0 && col <= 5) {\n          returnType = COBOLLINENUM;\n        } else if (col >= 72 && col <= 79) {\n          stream.skipToEnd();\n          returnType = MODTAG;\n        } else if (ch == \"*\" && col == 6) { // comment\n          stream.skipToEnd(); // rest of the line is a comment\n          returnType = COMMENT;\n        } else if (ch == \"\\\"\" || ch == \"\\'\") {\n          state.mode = \"string\";\n          returnType = STRING;\n        } else if (ch == \"'\" && !( tests.digit_or_colon.test(stream.peek()) )) {\n          returnType = ATOM;\n        } else if (ch == \".\") {\n          returnType = PERIOD;\n        } else if (isNumber(ch,stream)){\n          returnType = NUMBER;\n        } else {\n          if (stream.current().match(tests.symbol)) {\n            while (col < 71) {\n              if (stream.eat(tests.symbol) === undefined) {\n                break;\n              } else {\n                col++;\n              }\n            }\n          }\n          if (keywords && keywords.propertyIsEnumerable(stream.current().toUpperCase())) {\n            returnType = KEYWORD;\n          } else if (builtins && builtins.propertyIsEnumerable(stream.current().toUpperCase())) {\n            returnType = BUILTIN;\n          } else if (atoms && atoms.propertyIsEnumerable(stream.current().toUpperCase())) {\n            returnType = ATOM;\n          } else returnType = null;\n        }\n      }\n      return returnType;\n    },\n    indent: function (state) {\n      if (state.indentStack == null) return state.indentation;\n      return state.indentStack.indent;\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-cobol\", \"cobol\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/cobol/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: COBOL mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/neat.css\">\n<link rel=\"stylesheet\" href=\"../../theme/elegant.css\">\n<link rel=\"stylesheet\" href=\"../../theme/erlang-dark.css\">\n<link rel=\"stylesheet\" href=\"../../theme/night.css\">\n<link rel=\"stylesheet\" href=\"../../theme/monokai.css\">\n<link rel=\"stylesheet\" href=\"../../theme/cobalt.css\">\n<link rel=\"stylesheet\" href=\"../../theme/eclipse.css\">\n<link rel=\"stylesheet\" href=\"../../theme/rubyblue.css\">\n<link rel=\"stylesheet\" href=\"../../theme/lesser-dark.css\">\n<link rel=\"stylesheet\" href=\"../../theme/xq-dark.css\">\n<link rel=\"stylesheet\" href=\"../../theme/xq-light.css\">\n<link rel=\"stylesheet\" href=\"../../theme/ambiance.css\">\n<link rel=\"stylesheet\" href=\"../../theme/blackboard.css\">\n<link rel=\"stylesheet\" href=\"../../theme/vibrant-ink.css\">\n<link rel=\"stylesheet\" href=\"../../theme/solarized.css\">\n<link rel=\"stylesheet\" href=\"../../theme/twilight.css\">\n<link rel=\"stylesheet\" href=\"../../theme/midnight.css\">\n<link rel=\"stylesheet\" href=\"../../addon/dialog/dialog.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"cobol.js\"></script>\n<script src=\"../../addon/selection/active-line.js\"></script>\n<script src=\"../../addon/search/search.js\"></script>\n<script src=\"../../addon/dialog/dialog.js\"></script>\n<script src=\"../../addon/search/searchcursor.js\"></script>\n<style>\n        .CodeMirror {\n          border: 1px solid #eee;\n          font-size : 20px;\n          height : auto !important;\n        }\n        .CodeMirror-activeline-background {background: #555555 !important;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">COBOL</a>\n  </ul>\n</div>\n\n<article>\n<h2>COBOL mode</h2>\n\n    <p> Select Theme <select onchange=\"selectTheme()\" id=\"selectTheme\">\n        <option>default</option>\n        <option>ambiance</option>\n        <option>blackboard</option>\n        <option>cobalt</option>\n        <option>eclipse</option>\n        <option>elegant</option>\n        <option>erlang-dark</option>\n        <option>lesser-dark</option>\n        <option>midnight</option>\n        <option>monokai</option>\n        <option>neat</option>\n        <option>night</option>\n        <option>rubyblue</option>\n        <option>solarized dark</option>\n        <option>solarized light</option>\n        <option selected>twilight</option>\n        <option>vibrant-ink</option>\n        <option>xq-dark</option>\n        <option>xq-light</option>\n    </select>    Select Font Size <select onchange=\"selectFontsize()\" id=\"selectFontSize\">\n          <option value=\"13px\">13px</option>\n          <option value=\"14px\">14px</option>\n          <option value=\"16px\">16px</option>\n          <option value=\"18px\">18px</option>\n          <option value=\"20px\" selected=\"selected\">20px</option>\n          <option value=\"24px\">24px</option>\n          <option value=\"26px\">26px</option>\n          <option value=\"28px\">28px</option>\n          <option value=\"30px\">30px</option>\n          <option value=\"32px\">32px</option>\n          <option value=\"34px\">34px</option>\n          <option value=\"36px\">36px</option>\n        </select>\n<label for=\"checkBoxReadOnly\">Read-only</label>\n<input type=\"checkbox\" id=\"checkBoxReadOnly\" onchange=\"selectReadOnly()\">\n<label for=\"id_tabToIndentSpace\">Insert Spaces on Tab</label>\n<input type=\"checkbox\" id=\"id_tabToIndentSpace\" onchange=\"tabToIndentSpace()\">\n</p>\n<textarea id=\"code\" name=\"code\">\n---------1---------2---------3---------4---------5---------6---------7---------8\n12345678911234567892123456789312345678941234567895123456789612345678971234567898\n000010 IDENTIFICATION DIVISION.                                        MODTGHERE\n000020 PROGRAM-ID.       SAMPLE.\n000030 AUTHOR.           TEST SAM. \n000040 DATE-WRITTEN.     5 February 2013\n000041\n000042* A sample program just to show the form.\n000043* The program copies its input to the output,\n000044* and counts the number of records.\n000045* At the end this number is printed.\n000046\n000050 ENVIRONMENT DIVISION.\n000060 INPUT-OUTPUT SECTION.\n000070 FILE-CONTROL.\n000080     SELECT STUDENT-FILE     ASSIGN TO SYSIN\n000090         ORGANIZATION IS LINE SEQUENTIAL.\n000100     SELECT PRINT-FILE       ASSIGN TO SYSOUT\n000110         ORGANIZATION IS LINE SEQUENTIAL.\n000120\n000130 DATA DIVISION.\n000140 FILE SECTION.\n000150 FD  STUDENT-FILE\n000160     RECORD CONTAINS 43 CHARACTERS\n000170     DATA RECORD IS STUDENT-IN.\n000180 01  STUDENT-IN              PIC X(43).\n000190\n000200 FD  PRINT-FILE\n000210     RECORD CONTAINS 80 CHARACTERS\n000220     DATA RECORD IS PRINT-LINE.\n000230 01  PRINT-LINE              PIC X(80).\n000240\n000250 WORKING-STORAGE SECTION.\n000260 01  DATA-REMAINS-SWITCH     PIC X(2)      VALUE SPACES.\n000261 01  RECORDS-WRITTEN         PIC 99.\n000270\n000280 01  DETAIL-LINE.\n000290     05  FILLER              PIC X(7)      VALUE SPACES.\n000300     05  RECORD-IMAGE        PIC X(43).\n000310     05  FILLER              PIC X(30)     VALUE SPACES.\n000311 \n000312 01  SUMMARY-LINE.\n000313     05  FILLER              PIC X(7)      VALUE SPACES.\n000314     05  TOTAL-READ          PIC 99.\n000315     05  FILLER              PIC X         VALUE SPACE.\n000316     05  FILLER              PIC X(17)     \n000317                 VALUE  'Records were read'.\n000318     05  FILLER              PIC X(53)     VALUE SPACES.\n000319\n000320 PROCEDURE DIVISION.\n000321\n000330 PREPARE-SENIOR-REPORT.\n000340     OPEN INPUT  STUDENT-FILE\n000350          OUTPUT PRINT-FILE.\n000351     MOVE ZERO TO RECORDS-WRITTEN.\n000360     READ STUDENT-FILE\n000370         AT END MOVE 'NO' TO DATA-REMAINS-SWITCH\n000380     END-READ.\n000390     PERFORM PROCESS-RECORDS\n000410         UNTIL DATA-REMAINS-SWITCH = 'NO'.\n000411     PERFORM PRINT-SUMMARY.\n000420     CLOSE STUDENT-FILE\n000430           PRINT-FILE.\n000440     STOP RUN.\n000450\n000460 PROCESS-RECORDS.\n000470     MOVE STUDENT-IN TO RECORD-IMAGE.\n000480     MOVE DETAIL-LINE TO PRINT-LINE.\n000490     WRITE PRINT-LINE.\n000500     ADD 1 TO RECORDS-WRITTEN.\n000510     READ STUDENT-FILE\n000520         AT END MOVE 'NO' TO DATA-REMAINS-SWITCH\n000530     END-READ. \n000540\n000550 PRINT-SUMMARY.\n000560     MOVE RECORDS-WRITTEN TO TOTAL-READ.\n000570     MOVE SUMMARY-LINE TO PRINT-LINE.\n000571     WRITE PRINT-LINE. \n000572\n000580\n</textarea>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-cobol\",\n        theme : \"twilight\",\n        styleActiveLine: true,\n        showCursorWhenSelecting : true,  \n      });\n      function selectTheme() {\n        var themeInput = document.getElementById(\"selectTheme\");\n        var theme = themeInput.options[themeInput.selectedIndex].innerHTML;\n        editor.setOption(\"theme\", theme);\n      }\n      function selectFontsize() {\n        var fontSizeInput = document.getElementById(\"selectFontSize\");\n        var fontSize = fontSizeInput.options[fontSizeInput.selectedIndex].innerHTML;\n        editor.getWrapperElement().style.fontSize = fontSize;\n        editor.refresh();\n      }\n      function selectReadOnly() {\n        editor.setOption(\"readOnly\", document.getElementById(\"checkBoxReadOnly\").checked);\n      }\n      function tabToIndentSpace() {\n        if (document.getElementById(\"id_tabToIndentSpace\").checked) {\n            editor.setOption(\"extraKeys\", {Tab: function(cm) { cm.replaceSelection(\"    \", \"end\"); }});\n        } else {\n            editor.setOption(\"extraKeys\", {Tab: function(cm) { cm.replaceSelection(\"    \", \"end\"); }});\n        }\n      }\n    </script>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/coffeescript/coffeescript.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/**\n * Link to the project's GitHub page:\n * https://github.com/pickhardt/coffeescript-codemirror-mode\n */\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"coffeescript\", function(conf, parserConf) {\n  var ERRORCLASS = \"error\";\n\n  function wordRegexp(words) {\n    return new RegExp(\"^((\" + words.join(\")|(\") + \"))\\\\b\");\n  }\n\n  var operators = /^(?:->|=>|\\+[+=]?|-[\\-=]?|\\*[\\*=]?|\\/[\\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\\|=?|\\^=?|\\~|!|\\?|(or|and|\\|\\||&&|\\?)=)/;\n  var delimiters = /^(?:[()\\[\\]{},:`=;]|\\.\\.?\\.?)/;\n  var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/;\n  var atProp = /^@[_A-Za-z$][_A-Za-z$0-9]*/;\n\n  var wordOperators = wordRegexp([\"and\", \"or\", \"not\",\n                                  \"is\", \"isnt\", \"in\",\n                                  \"instanceof\", \"typeof\"]);\n  var indentKeywords = [\"for\", \"while\", \"loop\", \"if\", \"unless\", \"else\",\n                        \"switch\", \"try\", \"catch\", \"finally\", \"class\"];\n  var commonKeywords = [\"break\", \"by\", \"continue\", \"debugger\", \"delete\",\n                        \"do\", \"in\", \"of\", \"new\", \"return\", \"then\",\n                        \"this\", \"@\", \"throw\", \"when\", \"until\", \"extends\"];\n\n  var keywords = wordRegexp(indentKeywords.concat(commonKeywords));\n\n  indentKeywords = wordRegexp(indentKeywords);\n\n\n  var stringPrefixes = /^('{3}|\\\"{3}|['\\\"])/;\n  var regexPrefixes = /^(\\/{3}|\\/)/;\n  var commonConstants = [\"Infinity\", \"NaN\", \"undefined\", \"null\", \"true\", \"false\", \"on\", \"off\", \"yes\", \"no\"];\n  var constants = wordRegexp(commonConstants);\n\n  // Tokenizers\n  function tokenBase(stream, state) {\n    // Handle scope changes\n    if (stream.sol()) {\n      if (state.scope.align === null) state.scope.align = false;\n      var scopeOffset = state.scope.offset;\n      if (stream.eatSpace()) {\n        var lineOffset = stream.indentation();\n        if (lineOffset > scopeOffset && state.scope.type == \"coffee\") {\n          return \"indent\";\n        } else if (lineOffset < scopeOffset) {\n          return \"dedent\";\n        }\n        return null;\n      } else {\n        if (scopeOffset > 0) {\n          dedent(stream, state);\n        }\n      }\n    }\n    if (stream.eatSpace()) {\n      return null;\n    }\n\n    var ch = stream.peek();\n\n    // Handle docco title comment (single line)\n    if (stream.match(\"####\")) {\n      stream.skipToEnd();\n      return \"comment\";\n    }\n\n    // Handle multi line comments\n    if (stream.match(\"###\")) {\n      state.tokenize = longComment;\n      return state.tokenize(stream, state);\n    }\n\n    // Single line comment\n    if (ch === \"#\") {\n      stream.skipToEnd();\n      return \"comment\";\n    }\n\n    // Handle number literals\n    if (stream.match(/^-?[0-9\\.]/, false)) {\n      var floatLiteral = false;\n      // Floats\n      if (stream.match(/^-?\\d*\\.\\d+(e[\\+\\-]?\\d+)?/i)) {\n        floatLiteral = true;\n      }\n      if (stream.match(/^-?\\d+\\.\\d*/)) {\n        floatLiteral = true;\n      }\n      if (stream.match(/^-?\\.\\d+/)) {\n        floatLiteral = true;\n      }\n\n      if (floatLiteral) {\n        // prevent from getting extra . on 1..\n        if (stream.peek() == \".\"){\n          stream.backUp(1);\n        }\n        return \"number\";\n      }\n      // Integers\n      var intLiteral = false;\n      // Hex\n      if (stream.match(/^-?0x[0-9a-f]+/i)) {\n        intLiteral = true;\n      }\n      // Decimal\n      if (stream.match(/^-?[1-9]\\d*(e[\\+\\-]?\\d+)?/)) {\n        intLiteral = true;\n      }\n      // Zero by itself with no other piece of number.\n      if (stream.match(/^-?0(?![\\dx])/i)) {\n        intLiteral = true;\n      }\n      if (intLiteral) {\n        return \"number\";\n      }\n    }\n\n    // Handle strings\n    if (stream.match(stringPrefixes)) {\n      state.tokenize = tokenFactory(stream.current(), false, \"string\");\n      return state.tokenize(stream, state);\n    }\n    // Handle regex literals\n    if (stream.match(regexPrefixes)) {\n      if (stream.current() != \"/\" || stream.match(/^.*\\//, false)) { // prevent highlight of division\n        state.tokenize = tokenFactory(stream.current(), true, \"string-2\");\n        return state.tokenize(stream, state);\n      } else {\n        stream.backUp(1);\n      }\n    }\n\n\n\n    // Handle operators and delimiters\n    if (stream.match(operators) || stream.match(wordOperators)) {\n      return \"operator\";\n    }\n    if (stream.match(delimiters)) {\n      return \"punctuation\";\n    }\n\n    if (stream.match(constants)) {\n      return \"atom\";\n    }\n\n    if (stream.match(atProp) || state.prop && stream.match(identifiers)) {\n      return \"property\";\n    }\n\n    if (stream.match(keywords)) {\n      return \"keyword\";\n    }\n\n    if (stream.match(identifiers)) {\n      return \"variable\";\n    }\n\n    // Handle non-detected items\n    stream.next();\n    return ERRORCLASS;\n  }\n\n  function tokenFactory(delimiter, singleline, outclass) {\n    return function(stream, state) {\n      while (!stream.eol()) {\n        stream.eatWhile(/[^'\"\\/\\\\]/);\n        if (stream.eat(\"\\\\\")) {\n          stream.next();\n          if (singleline && stream.eol()) {\n            return outclass;\n          }\n        } else if (stream.match(delimiter)) {\n          state.tokenize = tokenBase;\n          return outclass;\n        } else {\n          stream.eat(/['\"\\/]/);\n        }\n      }\n      if (singleline) {\n        if (parserConf.singleLineStringErrors) {\n          outclass = ERRORCLASS;\n        } else {\n          state.tokenize = tokenBase;\n        }\n      }\n      return outclass;\n    };\n  }\n\n  function longComment(stream, state) {\n    while (!stream.eol()) {\n      stream.eatWhile(/[^#]/);\n      if (stream.match(\"###\")) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      stream.eatWhile(\"#\");\n    }\n    return \"comment\";\n  }\n\n  function indent(stream, state, type) {\n    type = type || \"coffee\";\n    var offset = 0, align = false, alignOffset = null;\n    for (var scope = state.scope; scope; scope = scope.prev) {\n      if (scope.type === \"coffee\" || scope.type == \"}\") {\n        offset = scope.offset + conf.indentUnit;\n        break;\n      }\n    }\n    if (type !== \"coffee\") {\n      align = null;\n      alignOffset = stream.column() + stream.current().length;\n    } else if (state.scope.align) {\n      state.scope.align = false;\n    }\n    state.scope = {\n      offset: offset,\n      type: type,\n      prev: state.scope,\n      align: align,\n      alignOffset: alignOffset\n    };\n  }\n\n  function dedent(stream, state) {\n    if (!state.scope.prev) return;\n    if (state.scope.type === \"coffee\") {\n      var _indent = stream.indentation();\n      var matched = false;\n      for (var scope = state.scope; scope; scope = scope.prev) {\n        if (_indent === scope.offset) {\n          matched = true;\n          break;\n        }\n      }\n      if (!matched) {\n        return true;\n      }\n      while (state.scope.prev && state.scope.offset !== _indent) {\n        state.scope = state.scope.prev;\n      }\n      return false;\n    } else {\n      state.scope = state.scope.prev;\n      return false;\n    }\n  }\n\n  function tokenLexer(stream, state) {\n    var style = state.tokenize(stream, state);\n    var current = stream.current();\n\n    // Handle scope changes.\n    if (current === \"return\") {\n      state.dedent = true;\n    }\n    if (((current === \"->\" || current === \"=>\") && stream.eol())\n        || style === \"indent\") {\n      indent(stream, state);\n    }\n    var delimiter_index = \"[({\".indexOf(current);\n    if (delimiter_index !== -1) {\n      indent(stream, state, \"])}\".slice(delimiter_index, delimiter_index+1));\n    }\n    if (indentKeywords.exec(current)){\n      indent(stream, state);\n    }\n    if (current == \"then\"){\n      dedent(stream, state);\n    }\n\n\n    if (style === \"dedent\") {\n      if (dedent(stream, state)) {\n        return ERRORCLASS;\n      }\n    }\n    delimiter_index = \"])}\".indexOf(current);\n    if (delimiter_index !== -1) {\n      while (state.scope.type == \"coffee\" && state.scope.prev)\n        state.scope = state.scope.prev;\n      if (state.scope.type == current)\n        state.scope = state.scope.prev;\n    }\n    if (state.dedent && stream.eol()) {\n      if (state.scope.type == \"coffee\" && state.scope.prev)\n        state.scope = state.scope.prev;\n      state.dedent = false;\n    }\n\n    return style;\n  }\n\n  var external = {\n    startState: function(basecolumn) {\n      return {\n        tokenize: tokenBase,\n        scope: {offset:basecolumn || 0, type:\"coffee\", prev: null, align: false},\n        prop: false,\n        dedent: 0\n      };\n    },\n\n    token: function(stream, state) {\n      var fillAlign = state.scope.align === null && state.scope;\n      if (fillAlign && stream.sol()) fillAlign.align = false;\n\n      var style = tokenLexer(stream, state);\n      if (style && style != \"comment\") {\n        if (fillAlign) fillAlign.align = true;\n        state.prop = style == \"punctuation\" && stream.current() == \".\"\n      }\n\n      return style;\n    },\n\n    indent: function(state, text) {\n      if (state.tokenize != tokenBase) return 0;\n      var scope = state.scope;\n      var closer = text && \"])}\".indexOf(text.charAt(0)) > -1;\n      if (closer) while (scope.type == \"coffee\" && scope.prev) scope = scope.prev;\n      var closes = closer && scope.type === text.charAt(0);\n      if (scope.align)\n        return scope.alignOffset - (closes ? 1 : 0);\n      else\n        return (closes ? scope.prev : scope).offset;\n    },\n\n    lineComment: \"#\",\n    fold: \"indent\"\n  };\n  return external;\n});\n\n// IANA registered media type\n// https://www.iana.org/assignments/media-types/\nCodeMirror.defineMIME(\"application/vnd.coffeescript\", \"coffeescript\");\n\nCodeMirror.defineMIME(\"text/x-coffeescript\", \"coffeescript\");\nCodeMirror.defineMIME(\"text/coffeescript\", \"coffeescript\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/coffeescript/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: CoffeeScript mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"coffeescript.js\"></script>\n<style>.CodeMirror {border-top: 1px solid silver; border-bottom: 1px solid silver;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">CoffeeScript</a>\n  </ul>\n</div>\n\n<article>\n<h2>CoffeeScript mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n# CoffeeScript mode for CodeMirror\n# Copyright (c) 2011 Jeff Pickhardt, released under\n# the MIT License.\n#\n# Modified from the Python CodeMirror mode, which also is \n# under the MIT License Copyright (c) 2010 Timothy Farrell.\n#\n# The following script, Underscore.coffee, is used to \n# demonstrate CoffeeScript mode for CodeMirror.\n#\n# To download CoffeeScript mode for CodeMirror, go to:\n# https://github.com/pickhardt/coffeescript-codemirror-mode\n\n# **Underscore.coffee\n# (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.**\n# Underscore is freely distributable under the terms of the\n# [MIT license](http://en.wikipedia.org/wiki/MIT_License).\n# Portions of Underscore are inspired by or borrowed from\n# [Prototype.js](http://prototypejs.org/api), Oliver Steele's\n# [Functional](http://osteele.com), and John Resig's\n# [Micro-Templating](http://ejohn.org).\n# For all details and documentation:\n# http://documentcloud.github.com/underscore/\n\n\n# Baseline setup\n# --------------\n\n# Establish the root object, `window` in the browser, or `global` on the server.\nroot = this\n\n\n# Save the previous value of the `_` variable.\npreviousUnderscore = root._\n\n### Multiline\n    comment\n###\n\n# Establish the object that gets thrown to break out of a loop iteration.\n# `StopIteration` is SOP on Mozilla.\nbreaker = if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration\n\n\n#### Docco style single line comment (title)\n\n\n# Helper function to escape **RegExp** contents, because JS doesn't have one.\nescapeRegExp = (string) -> string.replace(/([.*+?^${}()|[\\]\\/\\\\])/g, '\\\\$1')\n\n\n# Save bytes in the minified (but not gzipped) version:\nArrayProto = Array.prototype\nObjProto = Object.prototype\n\n\n# Create quick reference variables for speed access to core prototypes.\nslice = ArrayProto.slice\nunshift = ArrayProto.unshift\ntoString = ObjProto.toString\nhasOwnProperty = ObjProto.hasOwnProperty\npropertyIsEnumerable = ObjProto.propertyIsEnumerable\n\n\n# All **ECMA5** native implementations we hope to use are declared here.\nnativeForEach = ArrayProto.forEach\nnativeMap = ArrayProto.map\nnativeReduce = ArrayProto.reduce\nnativeReduceRight = ArrayProto.reduceRight\nnativeFilter = ArrayProto.filter\nnativeEvery = ArrayProto.every\nnativeSome = ArrayProto.some\nnativeIndexOf = ArrayProto.indexOf\nnativeLastIndexOf = ArrayProto.lastIndexOf\nnativeIsArray = Array.isArray\nnativeKeys = Object.keys\n\n\n# Create a safe reference to the Underscore object for use below.\n_ = (obj) -> new wrapper(obj)\n\n\n# Export the Underscore object for **CommonJS**.\nif typeof(exports) != 'undefined' then exports._ = _\n\n\n# Export Underscore to global scope.\nroot._ = _\n\n\n# Current version.\n_.VERSION = '1.1.0'\n\n\n# Collection Functions\n# --------------------\n\n# The cornerstone, an **each** implementation.\n# Handles objects implementing **forEach**, arrays, and raw objects.\n_.each = (obj, iterator, context) ->\n  try\n    if nativeForEach and obj.forEach is nativeForEach\n      obj.forEach iterator, context\n    else if _.isNumber obj.length\n      iterator.call context, obj[i], i, obj for i in [0...obj.length]\n    else\n      iterator.call context, val, key, obj for own key, val of obj\n  catch e\n    throw e if e isnt breaker\n  obj\n\n\n# Return the results of applying the iterator to each element. Use JavaScript\n# 1.6's version of **map**, if possible.\n_.map = (obj, iterator, context) ->\n  return obj.map(iterator, context) if nativeMap and obj.map is nativeMap\n  results = []\n  _.each obj, (value, index, list) ->\n    results.push iterator.call context, value, index, list\n  results\n\n\n# **Reduce** builds up a single result from a list of values. Also known as\n# **inject**, or **foldl**. Uses JavaScript 1.8's version of **reduce**, if possible.\n_.reduce = (obj, iterator, memo, context) ->\n  if nativeReduce and obj.reduce is nativeReduce\n    iterator = _.bind iterator, context if context\n    return obj.reduce iterator, memo\n  _.each obj, (value, index, list) ->\n    memo = iterator.call context, memo, value, index, list\n  memo\n\n\n# The right-associative version of **reduce**, also known as **foldr**. Uses\n# JavaScript 1.8's version of **reduceRight**, if available.\n_.reduceRight = (obj, iterator, memo, context) ->\n  if nativeReduceRight and obj.reduceRight is nativeReduceRight\n    iterator = _.bind iterator, context if context\n    return obj.reduceRight iterator, memo\n  reversed = _.clone(_.toArray(obj)).reverse()\n  _.reduce reversed, iterator, memo, context\n\n\n# Return the first value which passes a truth test.\n_.detect = (obj, iterator, context) ->\n  result = null\n  _.each obj, (value, index, list) ->\n    if iterator.call context, value, index, list\n      result = value\n      _.breakLoop()\n  result\n\n\n# Return all the elements that pass a truth test. Use JavaScript 1.6's\n# **filter**, if it exists.\n_.filter = (obj, iterator, context) ->\n  return obj.filter iterator, context if nativeFilter and obj.filter is nativeFilter\n  results = []\n  _.each obj, (value, index, list) ->\n    results.push value if iterator.call context, value, index, list\n  results\n\n\n# Return all the elements for which a truth test fails.\n_.reject = (obj, iterator, context) ->\n  results = []\n  _.each obj, (value, index, list) ->\n    results.push value if not iterator.call context, value, index, list\n  results\n\n\n# Determine whether all of the elements match a truth test. Delegate to\n# JavaScript 1.6's **every**, if it is present.\n_.every = (obj, iterator, context) ->\n  iterator ||= _.identity\n  return obj.every iterator, context if nativeEvery and obj.every is nativeEvery\n  result = true\n  _.each obj, (value, index, list) ->\n    _.breakLoop() unless (result = result and iterator.call(context, value, index, list))\n  result\n\n\n# Determine if at least one element in the object matches a truth test. Use\n# JavaScript 1.6's **some**, if it exists.\n_.some = (obj, iterator, context) ->\n  iterator ||= _.identity\n  return obj.some iterator, context if nativeSome and obj.some is nativeSome\n  result = false\n  _.each obj, (value, index, list) ->\n    _.breakLoop() if (result = iterator.call(context, value, index, list))\n  result\n\n\n# Determine if a given value is included in the array or object,\n# based on `===`.\n_.include = (obj, target) ->\n  return _.indexOf(obj, target) isnt -1 if nativeIndexOf and obj.indexOf is nativeIndexOf\n  return true for own key, val of obj when val is target\n  false\n\n\n# Invoke a method with arguments on every item in a collection.\n_.invoke = (obj, method) ->\n  args = _.rest arguments, 2\n  (if method then val[method] else val).apply(val, args) for val in obj\n\n\n# Convenience version of a common use case of **map**: fetching a property.\n_.pluck = (obj, key) ->\n  _.map(obj, (val) -> val[key])\n\n\n# Return the maximum item or (item-based computation).\n_.max = (obj, iterator, context) ->\n  return Math.max.apply(Math, obj) if not iterator and _.isArray(obj)\n  result = computed: -Infinity\n  _.each obj, (value, index, list) ->\n    computed = if iterator then iterator.call(context, value, index, list) else value\n    computed >= result.computed and (result = {value: value, computed: computed})\n  result.value\n\n\n# Return the minimum element (or element-based computation).\n_.min = (obj, iterator, context) ->\n  return Math.min.apply(Math, obj) if not iterator and _.isArray(obj)\n  result = computed: Infinity\n  _.each obj, (value, index, list) ->\n    computed = if iterator then iterator.call(context, value, index, list) else value\n    computed < result.computed and (result = {value: value, computed: computed})\n  result.value\n\n\n# Sort the object's values by a criterion produced by an iterator.\n_.sortBy = (obj, iterator, context) ->\n  _.pluck(((_.map obj, (value, index, list) ->\n    {value: value, criteria: iterator.call(context, value, index, list)}\n  ).sort((left, right) ->\n    a = left.criteria; b = right.criteria\n    if a < b then -1 else if a > b then 1 else 0\n  )), 'value')\n\n\n# Use a comparator function to figure out at what index an object should\n# be inserted so as to maintain order. Uses binary search.\n_.sortedIndex = (array, obj, iterator) ->\n  iterator ||= _.identity\n  low = 0\n  high = array.length\n  while low < high\n    mid = (low + high) >> 1\n    if iterator(array[mid]) < iterator(obj) then low = mid + 1 else high = mid\n  low\n\n\n# Convert anything iterable into a real, live array.\n_.toArray = (iterable) ->\n  return [] if (!iterable)\n  return iterable.toArray() if (iterable.toArray)\n  return iterable if (_.isArray(iterable))\n  return slice.call(iterable) if (_.isArguments(iterable))\n  _.values(iterable)\n\n\n# Return the number of elements in an object.\n_.size = (obj) -> _.toArray(obj).length\n\n\n# Array Functions\n# ---------------\n\n# Get the first element of an array. Passing `n` will return the first N\n# values in the array. Aliased as **head**. The `guard` check allows it to work\n# with **map**.\n_.first = (array, n, guard) ->\n  if n and not guard then slice.call(array, 0, n) else array[0]\n\n\n# Returns everything but the first entry of the array. Aliased as **tail**.\n# Especially useful on the arguments object. Passing an `index` will return\n# the rest of the values in the array from that index onward. The `guard`\n# check allows it to work with **map**.\n_.rest = (array, index, guard) ->\n  slice.call(array, if _.isUndefined(index) or guard then 1 else index)\n\n\n# Get the last element of an array.\n_.last = (array) -> array[array.length - 1]\n\n\n# Trim out all falsy values from an array.\n_.compact = (array) -> item for item in array when item\n\n\n# Return a completely flattened version of an array.\n_.flatten = (array) ->\n  _.reduce array, (memo, value) ->\n    return memo.concat(_.flatten(value)) if _.isArray value\n    memo.push value\n    memo\n  , []\n\n\n# Return a version of the array that does not contain the specified value(s).\n_.without = (array) ->\n  values = _.rest arguments\n  val for val in _.toArray(array) when not _.include values, val\n\n\n# Produce a duplicate-free version of the array. If the array has already\n# been sorted, you have the option of using a faster algorithm.\n_.uniq = (array, isSorted) ->\n  memo = []\n  for el, i in _.toArray array\n    memo.push el if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))\n  memo\n\n\n# Produce an array that contains every item shared between all the\n# passed-in arrays.\n_.intersect = (array) ->\n  rest = _.rest arguments\n  _.select _.uniq(array), (item) ->\n    _.all rest, (other) ->\n      _.indexOf(other, item) >= 0\n\n\n# Zip together multiple lists into a single array -- elements that share\n# an index go together.\n_.zip = ->\n  length = _.max _.pluck arguments, 'length'\n  results = new Array length\n  for i in [0...length]\n    results[i] = _.pluck arguments, String i\n  results\n\n\n# If the browser doesn't supply us with **indexOf** (I'm looking at you, MSIE),\n# we need this function. Return the position of the first occurrence of an\n# item in an array, or -1 if the item is not included in the array.\n_.indexOf = (array, item) ->\n  return array.indexOf item if nativeIndexOf and array.indexOf is nativeIndexOf\n  i = 0; l = array.length\n  while l - i\n    if array[i] is item then return i else i++\n  -1\n\n\n# Provide JavaScript 1.6's **lastIndexOf**, delegating to the native function,\n# if possible.\n_.lastIndexOf = (array, item) ->\n  return array.lastIndexOf(item) if nativeLastIndexOf and array.lastIndexOf is nativeLastIndexOf\n  i = array.length\n  while i\n    if array[i] is item then return i else i--\n  -1\n\n\n# Generate an integer Array containing an arithmetic progression. A port of\n# [the native Python **range** function](http://docs.python.org/library/functions.html#range).\n_.range = (start, stop, step) ->\n  a = arguments\n  solo = a.length <= 1\n  i = start = if solo then 0 else a[0]\n  stop = if solo then a[0] else a[1]\n  step = a[2] or 1\n  len = Math.ceil((stop - start) / step)\n  return [] if len <= 0\n  range = new Array len\n  idx = 0\n  loop\n    return range if (if step > 0 then i - stop else stop - i) >= 0\n    range[idx] = i\n    idx++\n    i+= step\n\n\n# Function Functions\n# ------------------\n\n# Create a function bound to a given object (assigning `this`, and arguments,\n# optionally). Binding with arguments is also known as **curry**.\n_.bind = (func, obj) ->\n  args = _.rest arguments, 2\n  -> func.apply obj or root, args.concat arguments\n\n\n# Bind all of an object's methods to that object. Useful for ensuring that\n# all callbacks defined on an object belong to it.\n_.bindAll = (obj) ->\n  funcs = if arguments.length > 1 then _.rest(arguments) else _.functions(obj)\n  _.each funcs, (f) -> obj[f] = _.bind obj[f], obj\n  obj\n\n\n# Delays a function for the given number of milliseconds, and then calls\n# it with the arguments supplied.\n_.delay = (func, wait) ->\n  args = _.rest arguments, 2\n  setTimeout((-> func.apply(func, args)), wait)\n\n\n# Memoize an expensive function by storing its results.\n_.memoize = (func, hasher) ->\n  memo = {}\n  hasher or= _.identity\n  ->\n    key = hasher.apply this, arguments\n    return memo[key] if key of memo\n    memo[key] = func.apply this, arguments\n\n\n# Defers a function, scheduling it to run after the current call stack has\n# cleared.\n_.defer = (func) ->\n  _.delay.apply _, [func, 1].concat _.rest arguments\n\n\n# Returns the first function passed as an argument to the second,\n# allowing you to adjust arguments, run code before and after, and\n# conditionally execute the original function.\n_.wrap = (func, wrapper) ->\n  -> wrapper.apply wrapper, [func].concat arguments\n\n\n# Returns a function that is the composition of a list of functions, each\n# consuming the return value of the function that follows.\n_.compose = ->\n  funcs = arguments\n  ->\n    args = arguments\n    for i in [funcs.length - 1..0] by -1\n      args = [funcs[i].apply(this, args)]\n    args[0]\n\n\n# Object Functions\n# ----------------\n\n# Retrieve the names of an object's properties.\n_.keys = nativeKeys or (obj) ->\n  return _.range 0, obj.length if _.isArray(obj)\n  key for key, val of obj\n\n\n# Retrieve the values of an object's properties.\n_.values = (obj) ->\n  _.map obj, _.identity\n\n\n# Return a sorted list of the function names available in Underscore.\n_.functions = (obj) ->\n  _.filter(_.keys(obj), (key) -> _.isFunction(obj[key])).sort()\n\n\n# Extend a given object with all of the properties in a source object.\n_.extend = (obj) ->\n  for source in _.rest(arguments)\n    obj[key] = val for key, val of source\n  obj\n\n\n# Create a (shallow-cloned) duplicate of an object.\n_.clone = (obj) ->\n  return obj.slice 0 if _.isArray obj\n  _.extend {}, obj\n\n\n# Invokes interceptor with the obj, and then returns obj.\n# The primary purpose of this method is to \"tap into\" a method chain,\n# in order to perform operations on intermediate results within\n the chain.\n_.tap = (obj, interceptor) ->\n  interceptor obj\n  obj\n\n\n# Perform a deep comparison to check if two objects are equal.\n_.isEqual = (a, b) ->\n  # Check object identity.\n  return true if a is b\n  # Different types?\n  atype = typeof(a); btype = typeof(b)\n  return false if atype isnt btype\n  # Basic equality test (watch out for coercions).\n  return true if `a == b`\n  # One is falsy and the other truthy.\n  return false if (!a and b) or (a and !b)\n  # One of them implements an `isEqual()`?\n  return a.isEqual(b) if a.isEqual\n  # Check dates' integer values.\n  return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b)\n  # Both are NaN?\n  return false if _.isNaN(a) and _.isNaN(b)\n  # Compare regular expressions.\n  if _.isRegExp(a) and _.isRegExp(b)\n    return a.source is b.source and\n           a.global is b.global and\n           a.ignoreCase is b.ignoreCase and\n           a.multiline is b.multiline\n  # If a is not an object by this point, we can't handle it.\n  return false if atype isnt 'object'\n  # Check for different array lengths before comparing contents.\n  return false if a.length and (a.length isnt b.length)\n  # Nothing else worked, deep compare the contents.\n  aKeys = _.keys(a); bKeys = _.keys(b)\n  # Different object sizes?\n  return false if aKeys.length isnt bKeys.length\n  # Recursive comparison of contents.\n  return false for key, val of a when !(key of b) or !_.isEqual(val, b[key])\n  true\n\n\n# Is a given array or object empty?\n_.isEmpty = (obj) ->\n  return obj.length is 0 if _.isArray(obj) or _.isString(obj)\n  return false for own key of obj\n  true\n\n\n# Is a given value a DOM element?\n_.isElement = (obj) -> obj and obj.nodeType is 1\n\n\n# Is a given value an array?\n_.isArray = nativeIsArray or (obj) -> !!(obj and obj.concat and obj.unshift and not obj.callee)\n\n\n# Is a given variable an arguments object?\n_.isArguments = (obj) -> obj and obj.callee\n\n\n# Is the given value a function?\n_.isFunction = (obj) -> !!(obj and obj.constructor and obj.call and obj.apply)\n\n\n# Is the given value a string?\n_.isString = (obj) -> !!(obj is '' or (obj and obj.charCodeAt and obj.substr))\n\n\n# Is a given value a number?\n_.isNumber = (obj) -> (obj is +obj) or toString.call(obj) is '[object Number]'\n\n\n# Is a given value a boolean?\n_.isBoolean = (obj) -> obj is true or obj is false\n\n\n# Is a given value a Date?\n_.isDate = (obj) -> !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)\n\n\n# Is the given value a regular expression?\n_.isRegExp = (obj) -> !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false))\n\n\n# Is the given value NaN -- this one is interesting. `NaN != NaN`, and\n# `isNaN(undefined) == true`, so we make sure it's a number first.\n_.isNaN = (obj) -> _.isNumber(obj) and window.isNaN(obj)\n\n\n# Is a given value equal to null?\n_.isNull = (obj) -> obj is null\n\n\n# Is a given variable undefined?\n_.isUndefined = (obj) -> typeof obj is 'undefined'\n\n\n# Utility Functions\n# -----------------\n\n# Run Underscore.js in noConflict mode, returning the `_` variable to its\n# previous owner. Returns a reference to the Underscore object.\n_.noConflict = ->\n  root._ = previousUnderscore\n  this\n\n\n# Keep the identity function around for default iterators.\n_.identity = (value) -> value\n\n\n# Run a function `n` times.\n_.times = (n, iterator, context) ->\n  iterator.call context, i for i in [0...n]\n\n\n# Break out of the middle of an iteration.\n_.breakLoop = -> throw breaker\n\n\n# Add your own custom functions to the Underscore object, ensuring that\n# they're correctly added to the OOP wrapper as well.\n_.mixin = (obj) ->\n  for name in _.functions(obj)\n    addToWrapper name, _[name] = obj[name]\n\n\n# Generate a unique integer id (unique within the entire client session).\n# Useful for temporary DOM ids.\nidCounter = 0\n_.uniqueId = (prefix) ->\n  (prefix or '') + idCounter++\n\n\n# By default, Underscore uses **ERB**-style template delimiters, change the\n# following template settings to use alternative delimiters.\n_.templateSettings = {\n  start: '<%'\n  end: '%>'\n  interpolate: /<%=(.+?)%>/g\n}\n\n\n# JavaScript templating a-la **ERB**, pilfered from John Resig's\n# *Secrets of the JavaScript Ninja*, page 83.\n# Single-quote fix from Rick Strahl.\n# With alterations for arbitrary delimiters, and to preserve whitespace.\n_.template = (str, data) ->\n  c = _.templateSettings\n  endMatch = new RegExp(\"'(?=[^\"+c.end.substr(0, 1)+\"]*\"+escapeRegExp(c.end)+\")\",\"g\")\n  fn = new Function 'obj',\n    'var p=[],print=function(){p.push.apply(p,arguments);};' +\n    'with(obj||{}){p.push(\\'' +\n    str.replace(/\\r/g, '\\\\r')\n       .replace(/\\n/g, '\\\\n')\n       .replace(/\\t/g, '\\\\t')\n       .replace(endMatch,\"���\")\n       .split(\"'\").join(\"\\\\'\")\n       .split(\"���\").join(\"'\")\n       .replace(c.interpolate, \"',$1,'\")\n       .split(c.start).join(\"');\")\n       .split(c.end).join(\"p.push('\") +\n       \"');}return p.join('');\"\n  if data then fn(data) else fn\n\n\n# Aliases\n# -------\n\n_.forEach = _.each\n_.foldl = _.inject = _.reduce\n_.foldr = _.reduceRight\n_.select = _.filter\n_.all = _.every\n_.any = _.some\n_.contains = _.include\n_.head = _.first\n_.tail = _.rest\n_.methods = _.functions\n\n\n# Setup the OOP Wrapper\n# ---------------------\n\n# If Underscore is called as a function, it returns a wrapped object that\n# can be used OO-style. This wrapper holds altered versions of all the\n# underscore functions. Wrapped objects may be chained.\nwrapper = (obj) ->\n  this._wrapped = obj\n  this\n\n\n# Helper function to continue chaining intermediate results.\nresult = (obj, chain) ->\n  if chain then _(obj).chain() else obj\n\n\n# A method to easily add functions to the OOP wrapper.\naddToWrapper = (name, func) ->\n  wrapper.prototype[name] = ->\n    args = _.toArray arguments\n    unshift.call args, this._wrapped\n    result func.apply(_, args), this._chain\n\n\n# Add all ofthe Underscore functions to the wrapper object.\n_.mixin _\n\n\n# Add all mutator Array functions to the wrapper.\n_.each ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], (name) ->\n  method = Array.prototype[name]\n  wrapper.prototype[name] = ->\n    method.apply(this._wrapped, arguments)\n    result(this._wrapped, this._chain)\n\n\n# Add all accessor Array functions to the wrapper.\n_.each ['concat', 'join', 'slice'], (name) ->\n  method = Array.prototype[name]\n  wrapper.prototype[name] = ->\n    result(method.apply(this._wrapped, arguments), this._chain)\n\n\n# Start chaining a wrapped Underscore object.\nwrapper::chain = ->\n  this._chain = true\n  this\n\n\n# Extracts the result from a wrapped and chained object.\nwrapper::value = -> this._wrapped\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>application/vnd.coffeescript</code>, <code>text/coffeescript</code>, <code>text/x-coffeescript</code>.</p>\n\n    <p>The CoffeeScript mode was written by Jeff Pickhardt.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/commonlisp/commonlisp.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"commonlisp\", function (config) {\n  var specialForm = /^(block|let*|return-from|catch|load-time-value|setq|eval-when|locally|symbol-macrolet|flet|macrolet|tagbody|function|multiple-value-call|the|go|multiple-value-prog1|throw|if|progn|unwind-protect|labels|progv|let|quote)$/;\n  var assumeBody = /^with|^def|^do|^prog|case$|^cond$|bind$|when$|unless$/;\n  var numLiteral = /^(?:[+\\-]?(?:\\d+|\\d*\\.\\d+)(?:[efd][+\\-]?\\d+)?|[+\\-]?\\d+(?:\\/[+\\-]?\\d+)?|#b[+\\-]?[01]+|#o[+\\-]?[0-7]+|#x[+\\-]?[\\da-f]+)/;\n  var symbol = /[^\\s'`,@()\\[\\]\";]/;\n  var type;\n\n  function readSym(stream) {\n    var ch;\n    while (ch = stream.next()) {\n      if (ch == \"\\\\\") stream.next();\n      else if (!symbol.test(ch)) { stream.backUp(1); break; }\n    }\n    return stream.current();\n  }\n\n  function base(stream, state) {\n    if (stream.eatSpace()) {type = \"ws\"; return null;}\n    if (stream.match(numLiteral)) return \"number\";\n    var ch = stream.next();\n    if (ch == \"\\\\\") ch = stream.next();\n\n    if (ch == '\"') return (state.tokenize = inString)(stream, state);\n    else if (ch == \"(\") { type = \"open\"; return \"bracket\"; }\n    else if (ch == \")\" || ch == \"]\") { type = \"close\"; return \"bracket\"; }\n    else if (ch == \";\") { stream.skipToEnd(); type = \"ws\"; return \"comment\"; }\n    else if (/['`,@]/.test(ch)) return null;\n    else if (ch == \"|\") {\n      if (stream.skipTo(\"|\")) { stream.next(); return \"symbol\"; }\n      else { stream.skipToEnd(); return \"error\"; }\n    } else if (ch == \"#\") {\n      var ch = stream.next();\n      if (ch == \"(\") { type = \"open\"; return \"bracket\"; }\n      else if (/[+\\-=\\.']/.test(ch)) return null;\n      else if (/\\d/.test(ch) && stream.match(/^\\d*#/)) return null;\n      else if (ch == \"|\") return (state.tokenize = inComment)(stream, state);\n      else if (ch == \":\") { readSym(stream); return \"meta\"; }\n      else if (ch == \"\\\\\") { stream.next(); readSym(stream); return \"string-2\" }\n      else return \"error\";\n    } else {\n      var name = readSym(stream);\n      if (name == \".\") return null;\n      type = \"symbol\";\n      if (name == \"nil\" || name == \"t\" || name.charAt(0) == \":\") return \"atom\";\n      if (state.lastType == \"open\" && (specialForm.test(name) || assumeBody.test(name))) return \"keyword\";\n      if (name.charAt(0) == \"&\") return \"variable-2\";\n      return \"variable\";\n    }\n  }\n\n  function inString(stream, state) {\n    var escaped = false, next;\n    while (next = stream.next()) {\n      if (next == '\"' && !escaped) { state.tokenize = base; break; }\n      escaped = !escaped && next == \"\\\\\";\n    }\n    return \"string\";\n  }\n\n  function inComment(stream, state) {\n    var next, last;\n    while (next = stream.next()) {\n      if (next == \"#\" && last == \"|\") { state.tokenize = base; break; }\n      last = next;\n    }\n    type = \"ws\";\n    return \"comment\";\n  }\n\n  return {\n    startState: function () {\n      return {ctx: {prev: null, start: 0, indentTo: 0}, lastType: null, tokenize: base};\n    },\n\n    token: function (stream, state) {\n      if (stream.sol() && typeof state.ctx.indentTo != \"number\")\n        state.ctx.indentTo = state.ctx.start + 1;\n\n      type = null;\n      var style = state.tokenize(stream, state);\n      if (type != \"ws\") {\n        if (state.ctx.indentTo == null) {\n          if (type == \"symbol\" && assumeBody.test(stream.current()))\n            state.ctx.indentTo = state.ctx.start + config.indentUnit;\n          else\n            state.ctx.indentTo = \"next\";\n        } else if (state.ctx.indentTo == \"next\") {\n          state.ctx.indentTo = stream.column();\n        }\n        state.lastType = type;\n      }\n      if (type == \"open\") state.ctx = {prev: state.ctx, start: stream.column(), indentTo: null};\n      else if (type == \"close\") state.ctx = state.ctx.prev || state.ctx;\n      return style;\n    },\n\n    indent: function (state, _textAfter) {\n      var i = state.ctx.indentTo;\n      return typeof i == \"number\" ? i : state.ctx.start + 1;\n    },\n\n    closeBrackets: {pairs: \"()[]{}\\\"\\\"\"},\n    lineComment: \";;\",\n    fold: \"brace-paren\",\n    blockCommentStart: \"#|\",\n    blockCommentEnd: \"|#\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-common-lisp\", \"commonlisp\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/commonlisp/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Common Lisp mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"commonlisp.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Common Lisp</a>\n  </ul>\n</div>\n\n<article>\n<h2>Common Lisp mode</h2>\n<form><textarea id=\"code\" name=\"code\">(in-package :cl-postgres)\n\n;; These are used to synthesize reader and writer names for integer\n;; reading/writing functions when the amount of bytes and the\n;; signedness is known. Both the macro that creates the functions and\n;; some macros that use them create names this way.\n(eval-when (:compile-toplevel :load-toplevel :execute)\n  (defun integer-reader-name (bytes signed)\n    (intern (with-standard-io-syntax\n              (format nil \"~a~a~a~a\" '#:read- (if signed \"\" '#:u) '#:int bytes))))\n  (defun integer-writer-name (bytes signed)\n    (intern (with-standard-io-syntax\n              (format nil \"~a~a~a~a\" '#:write- (if signed \"\" '#:u) '#:int bytes)))))\n\n(defmacro integer-reader (bytes)\n  \"Create a function to read integers from a binary stream.\"\n  (let ((bits (* bytes 8)))\n    (labels ((return-form (signed)\n               (if signed\n                   `(if (logbitp ,(1- bits) result)\n                        (dpb result (byte ,(1- bits) 0) -1)\n                        result)\n                   `result))\n             (generate-reader (signed)\n               `(defun ,(integer-reader-name bytes signed) (socket)\n                  (declare (type stream socket)\n                           #.*optimize*)\n                  ,(if (= bytes 1)\n                       `(let ((result (the (unsigned-byte 8) (read-byte socket))))\n                          (declare (type (unsigned-byte 8) result))\n                          ,(return-form signed))\n                       `(let ((result 0))\n                          (declare (type (unsigned-byte ,bits) result))\n                          ,@(loop :for byte :from (1- bytes) :downto 0\n                                   :collect `(setf (ldb (byte 8 ,(* 8 byte)) result)\n                                                   (the (unsigned-byte 8) (read-byte socket))))\n                          ,(return-form signed))))))\n      `(progn\n;; This causes weird errors on SBCL in some circumstances. Disabled for now.\n;;         (declaim (inline ,(integer-reader-name bytes t)\n;;                          ,(integer-reader-name bytes nil)))\n         (declaim (ftype (function (t) (signed-byte ,bits))\n                         ,(integer-reader-name bytes t)))\n         ,(generate-reader t)\n         (declaim (ftype (function (t) (unsigned-byte ,bits))\n                         ,(integer-reader-name bytes nil)))\n         ,(generate-reader nil)))))\n\n(defmacro integer-writer (bytes)\n  \"Create a function to write integers to a binary stream.\"\n  (let ((bits (* 8 bytes)))\n    `(progn\n      (declaim (inline ,(integer-writer-name bytes t)\n                       ,(integer-writer-name bytes nil)))\n      (defun ,(integer-writer-name bytes nil) (socket value)\n        (declare (type stream socket)\n                 (type (unsigned-byte ,bits) value)\n                 #.*optimize*)\n        ,@(if (= bytes 1)\n              `((write-byte value socket))\n              (loop :for byte :from (1- bytes) :downto 0\n                    :collect `(write-byte (ldb (byte 8 ,(* byte 8)) value)\n                               socket)))\n        (values))\n      (defun ,(integer-writer-name bytes t) (socket value)\n        (declare (type stream socket)\n                 (type (signed-byte ,bits) value)\n                 #.*optimize*)\n        ,@(if (= bytes 1)\n              `((write-byte (ldb (byte 8 0) value) socket))\n              (loop :for byte :from (1- bytes) :downto 0\n                    :collect `(write-byte (ldb (byte 8 ,(* byte 8)) value)\n                               socket)))\n        (values)))))\n\n;; All the instances of the above that we need.\n\n(integer-reader 1)\n(integer-reader 2)\n(integer-reader 4)\n(integer-reader 8)\n\n(integer-writer 1)\n(integer-writer 2)\n(integer-writer 4)\n\n(defun write-bytes (socket bytes)\n  \"Write a byte-array to a stream.\"\n  (declare (type stream socket)\n           (type (simple-array (unsigned-byte 8)) bytes)\n           #.*optimize*)\n  (write-sequence bytes socket))\n\n(defun write-str (socket string)\n  \"Write a null-terminated string to a stream \\(encoding it when UTF-8\nsupport is enabled.).\"\n  (declare (type stream socket)\n           (type string string)\n           #.*optimize*)\n  (enc-write-string string socket)\n  (write-uint1 socket 0))\n\n(declaim (ftype (function (t unsigned-byte)\n                          (simple-array (unsigned-byte 8) (*)))\n                read-bytes))\n(defun read-bytes (socket length)\n  \"Read a byte array of the given length from a stream.\"\n  (declare (type stream socket)\n           (type fixnum length)\n           #.*optimize*)\n  (let ((result (make-array length :element-type '(unsigned-byte 8))))\n    (read-sequence result socket)\n    result))\n\n(declaim (ftype (function (t) string) read-str))\n(defun read-str (socket)\n  \"Read a null-terminated string from a stream. Takes care of encoding\nwhen UTF-8 support is enabled.\"\n  (declare (type stream socket)\n           #.*optimize*)\n  (enc-read-string socket :null-terminated t))\n\n(defun skip-bytes (socket length)\n  \"Skip a given number of bytes in a binary stream.\"\n  (declare (type stream socket)\n           (type (unsigned-byte 32) length)\n           #.*optimize*)\n  (dotimes (i length)\n    (read-byte socket)))\n\n(defun skip-str (socket)\n  \"Skip a null-terminated string.\"\n  (declare (type stream socket)\n           #.*optimize*)\n  (loop :for char :of-type fixnum = (read-byte socket)\n        :until (zerop char)))\n\n(defun ensure-socket-is-closed (socket &amp;key abort)\n  (when (open-stream-p socket)\n    (handler-case\n        (close socket :abort abort)\n      (error (error)\n        (warn \"Ignoring the error which happened while trying to close PostgreSQL socket: ~A\" error)))))\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {lineNumbers: true});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-common-lisp</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/crystal/crystal.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"crystal\", function(config) {\n    function wordRegExp(words, end) {\n      return new RegExp((end ? \"\" : \"^\") + \"(?:\" + words.join(\"|\") + \")\" + (end ? \"$\" : \"\\\\b\"));\n    }\n\n    function chain(tokenize, stream, state) {\n      state.tokenize.push(tokenize);\n      return tokenize(stream, state);\n    }\n\n    var operators = /^(?:[-+/%|&^]|\\*\\*?|[<>]{2})/;\n    var conditionalOperators = /^(?:[=!]~|===|<=>|[<>=!]=?|[|&]{2}|~)/;\n    var indexingOperators = /^(?:\\[\\][?=]?)/;\n    var anotherOperators = /^(?:\\.(?:\\.{2})?|->|[?:])/;\n    var idents = /^[a-z_\\u009F-\\uFFFF][a-zA-Z0-9_\\u009F-\\uFFFF]*/;\n    var types = /^[A-Z_\\u009F-\\uFFFF][a-zA-Z0-9_\\u009F-\\uFFFF]*/;\n    var keywords = wordRegExp([\n      \"abstract\", \"alias\", \"as\", \"asm\", \"begin\", \"break\", \"case\", \"class\", \"def\", \"do\",\n      \"else\", \"elsif\", \"end\", \"ensure\", \"enum\", \"extend\", \"for\", \"fun\", \"if\",\n      \"include\", \"instance_sizeof\", \"lib\", \"macro\", \"module\", \"next\", \"of\", \"out\", \"pointerof\",\n      \"private\", \"protected\", \"rescue\", \"return\", \"require\", \"select\", \"sizeof\", \"struct\",\n      \"super\", \"then\", \"type\", \"typeof\", \"uninitialized\", \"union\", \"unless\", \"until\", \"when\", \"while\", \"with\",\n      \"yield\", \"__DIR__\", \"__END_LINE__\", \"__FILE__\", \"__LINE__\"\n    ]);\n    var atomWords = wordRegExp([\"true\", \"false\", \"nil\", \"self\"]);\n    var indentKeywordsArray = [\n      \"def\", \"fun\", \"macro\",\n      \"class\", \"module\", \"struct\", \"lib\", \"enum\", \"union\",\n      \"do\", \"for\"\n    ];\n    var indentKeywords = wordRegExp(indentKeywordsArray);\n    var indentExpressionKeywordsArray = [\"if\", \"unless\", \"case\", \"while\", \"until\", \"begin\", \"then\"];\n    var indentExpressionKeywords = wordRegExp(indentExpressionKeywordsArray);\n    var dedentKeywordsArray = [\"end\", \"else\", \"elsif\", \"rescue\", \"ensure\"];\n    var dedentKeywords = wordRegExp(dedentKeywordsArray);\n    var dedentPunctualsArray = [\"\\\\)\", \"\\\\}\", \"\\\\]\"];\n    var dedentPunctuals = new RegExp(\"^(?:\" + dedentPunctualsArray.join(\"|\") + \")$\");\n    var nextTokenizer = {\n      \"def\": tokenFollowIdent, \"fun\": tokenFollowIdent, \"macro\": tokenMacroDef,\n      \"class\": tokenFollowType, \"module\": tokenFollowType, \"struct\": tokenFollowType,\n      \"lib\": tokenFollowType, \"enum\": tokenFollowType, \"union\": tokenFollowType\n    };\n    var matching = {\"[\": \"]\", \"{\": \"}\", \"(\": \")\", \"<\": \">\"};\n\n    function tokenBase(stream, state) {\n      if (stream.eatSpace()) {\n        return null;\n      }\n\n      // Macros\n      if (state.lastToken != \"\\\\\" && stream.match(\"{%\", false)) {\n        return chain(tokenMacro(\"%\", \"%\"), stream, state);\n      }\n\n      if (state.lastToken != \"\\\\\" && stream.match(\"{{\", false)) {\n        return chain(tokenMacro(\"{\", \"}\"), stream, state);\n      }\n\n      // Comments\n      if (stream.peek() == \"#\") {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n\n      // Variables and keywords\n      var matched;\n      if (stream.match(idents)) {\n        stream.eat(/[?!]/);\n\n        matched = stream.current();\n        if (stream.eat(\":\")) {\n          return \"atom\";\n        } else if (state.lastToken == \".\") {\n          return \"property\";\n        } else if (keywords.test(matched)) {\n          if (indentKeywords.test(matched)) {\n            if (!(matched == \"fun\" && state.blocks.indexOf(\"lib\") >= 0) && !(matched == \"def\" && state.lastToken == \"abstract\")) {\n              state.blocks.push(matched);\n              state.currentIndent += 1;\n            }\n          } else if ((state.lastStyle == \"operator\" || !state.lastStyle) && indentExpressionKeywords.test(matched)) {\n            state.blocks.push(matched);\n            state.currentIndent += 1;\n          } else if (matched == \"end\") {\n            state.blocks.pop();\n            state.currentIndent -= 1;\n          }\n\n          if (nextTokenizer.hasOwnProperty(matched)) {\n            state.tokenize.push(nextTokenizer[matched]);\n          }\n\n          return \"keyword\";\n        } else if (atomWords.test(matched)) {\n          return \"atom\";\n        }\n\n        return \"variable\";\n      }\n\n      // Class variables and instance variables\n      // or attributes\n      if (stream.eat(\"@\")) {\n        if (stream.peek() == \"[\") {\n          return chain(tokenNest(\"[\", \"]\", \"meta\"), stream, state);\n        }\n\n        stream.eat(\"@\");\n        stream.match(idents) || stream.match(types);\n        return \"variable-2\";\n      }\n\n      // Constants and types\n      if (stream.match(types)) {\n        return \"tag\";\n      }\n\n      // Symbols or ':' operator\n      if (stream.eat(\":\")) {\n        if (stream.eat(\"\\\"\")) {\n          return chain(tokenQuote(\"\\\"\", \"atom\", false), stream, state);\n        } else if (stream.match(idents) || stream.match(types) ||\n                   stream.match(operators) || stream.match(conditionalOperators) || stream.match(indexingOperators)) {\n          return \"atom\";\n        }\n        stream.eat(\":\");\n        return \"operator\";\n      }\n\n      // Strings\n      if (stream.eat(\"\\\"\")) {\n        return chain(tokenQuote(\"\\\"\", \"string\", true), stream, state);\n      }\n\n      // Strings or regexps or macro variables or '%' operator\n      if (stream.peek() == \"%\") {\n        var style = \"string\";\n        var embed = true;\n        var delim;\n\n        if (stream.match(\"%r\")) {\n          // Regexps\n          style = \"string-2\";\n          delim = stream.next();\n        } else if (stream.match(\"%w\")) {\n          embed = false;\n          delim = stream.next();\n        } else if (stream.match(\"%q\")) {\n          embed = false;\n          delim = stream.next();\n        } else {\n          if(delim = stream.match(/^%([^\\w\\s=])/)) {\n            delim = delim[1];\n          } else if (stream.match(/^%[a-zA-Z_\\u009F-\\uFFFF][\\w\\u009F-\\uFFFF]*/)) {\n            // Macro variables\n            return \"meta\";\n          } else if (stream.eat('%')) {\n            // '%' operator\n            return \"operator\";\n          }\n        }\n\n        if (matching.hasOwnProperty(delim)) {\n          delim = matching[delim];\n        }\n        return chain(tokenQuote(delim, style, embed), stream, state);\n      }\n\n      // Here Docs\n      if (matched = stream.match(/^<<-('?)([A-Z]\\w*)\\1/)) {\n        return chain(tokenHereDoc(matched[2], !matched[1]), stream, state)\n      }\n\n      // Characters\n      if (stream.eat(\"'\")) {\n        stream.match(/^(?:[^']|\\\\(?:[befnrtv0'\"]|[0-7]{3}|u(?:[0-9a-fA-F]{4}|\\{[0-9a-fA-F]{1,6}\\})))/);\n        stream.eat(\"'\");\n        return \"atom\";\n      }\n\n      // Numbers\n      if (stream.eat(\"0\")) {\n        if (stream.eat(\"x\")) {\n          stream.match(/^[0-9a-fA-F_]+/);\n        } else if (stream.eat(\"o\")) {\n          stream.match(/^[0-7_]+/);\n        } else if (stream.eat(\"b\")) {\n          stream.match(/^[01_]+/);\n        }\n        return \"number\";\n      }\n\n      if (stream.eat(/^\\d/)) {\n        stream.match(/^[\\d_]*(?:\\.[\\d_]+)?(?:[eE][+-]?\\d+)?/);\n        return \"number\";\n      }\n\n      // Operators\n      if (stream.match(operators)) {\n        stream.eat(\"=\"); // Operators can follow assign symbol.\n        return \"operator\";\n      }\n\n      if (stream.match(conditionalOperators) || stream.match(anotherOperators)) {\n        return \"operator\";\n      }\n\n      // Parens and braces\n      if (matched = stream.match(/[({[]/, false)) {\n        matched = matched[0];\n        return chain(tokenNest(matched, matching[matched], null), stream, state);\n      }\n\n      // Escapes\n      if (stream.eat(\"\\\\\")) {\n        stream.next();\n        return \"meta\";\n      }\n\n      stream.next();\n      return null;\n    }\n\n    function tokenNest(begin, end, style, started) {\n      return function (stream, state) {\n        if (!started && stream.match(begin)) {\n          state.tokenize[state.tokenize.length - 1] = tokenNest(begin, end, style, true);\n          state.currentIndent += 1;\n          return style;\n        }\n\n        var nextStyle = tokenBase(stream, state);\n        if (stream.current() === end) {\n          state.tokenize.pop();\n          state.currentIndent -= 1;\n          nextStyle = style;\n        }\n\n        return nextStyle;\n      };\n    }\n\n    function tokenMacro(begin, end, started) {\n      return function (stream, state) {\n        if (!started && stream.match(\"{\" + begin)) {\n          state.currentIndent += 1;\n          state.tokenize[state.tokenize.length - 1] = tokenMacro(begin, end, true);\n          return \"meta\";\n        }\n\n        if (stream.match(end + \"}\")) {\n          state.currentIndent -= 1;\n          state.tokenize.pop();\n          return \"meta\";\n        }\n\n        return tokenBase(stream, state);\n      };\n    }\n\n    function tokenMacroDef(stream, state) {\n      if (stream.eatSpace()) {\n        return null;\n      }\n\n      var matched;\n      if (matched = stream.match(idents)) {\n        if (matched == \"def\") {\n          return \"keyword\";\n        }\n        stream.eat(/[?!]/);\n      }\n\n      state.tokenize.pop();\n      return \"def\";\n    }\n\n    function tokenFollowIdent(stream, state) {\n      if (stream.eatSpace()) {\n        return null;\n      }\n\n      if (stream.match(idents)) {\n        stream.eat(/[!?]/);\n      } else {\n        stream.match(operators) || stream.match(conditionalOperators) || stream.match(indexingOperators);\n      }\n      state.tokenize.pop();\n      return \"def\";\n    }\n\n    function tokenFollowType(stream, state) {\n      if (stream.eatSpace()) {\n        return null;\n      }\n\n      stream.match(types);\n      state.tokenize.pop();\n      return \"def\";\n    }\n\n    function tokenQuote(end, style, embed) {\n      return function (stream, state) {\n        var escaped = false;\n\n        while (stream.peek()) {\n          if (!escaped) {\n            if (stream.match(\"{%\", false)) {\n              state.tokenize.push(tokenMacro(\"%\", \"%\"));\n              return style;\n            }\n\n            if (stream.match(\"{{\", false)) {\n              state.tokenize.push(tokenMacro(\"{\", \"}\"));\n              return style;\n            }\n\n            if (embed && stream.match(\"#{\", false)) {\n              state.tokenize.push(tokenNest(\"#{\", \"}\", \"meta\"));\n              return style;\n            }\n\n            var ch = stream.next();\n\n            if (ch == end) {\n              state.tokenize.pop();\n              return style;\n            }\n\n            escaped = embed && ch == \"\\\\\";\n          } else {\n            stream.next();\n            escaped = false;\n          }\n        }\n\n        return style;\n      };\n    }\n\n    function tokenHereDoc(phrase, embed) {\n      return function (stream, state) {\n        if (stream.sol()) {\n          stream.eatSpace()\n          if (stream.match(phrase)) {\n            state.tokenize.pop();\n            return \"string\";\n          }\n        }\n\n        var escaped = false;\n        while (stream.peek()) {\n          if (!escaped) {\n            if (stream.match(\"{%\", false)) {\n              state.tokenize.push(tokenMacro(\"%\", \"%\"));\n              return \"string\";\n            }\n\n            if (stream.match(\"{{\", false)) {\n              state.tokenize.push(tokenMacro(\"{\", \"}\"));\n              return \"string\";\n            }\n\n            if (embed && stream.match(\"#{\", false)) {\n              state.tokenize.push(tokenNest(\"#{\", \"}\", \"meta\"));\n              return \"string\";\n            }\n\n            escaped = embed && stream.next() == \"\\\\\";\n          } else {\n            stream.next();\n            escaped = false;\n          }\n        }\n\n        return \"string\";\n      }\n    }\n\n    return {\n      startState: function () {\n        return {\n          tokenize: [tokenBase],\n          currentIndent: 0,\n          lastToken: null,\n          lastStyle: null,\n          blocks: []\n        };\n      },\n\n      token: function (stream, state) {\n        var style = state.tokenize[state.tokenize.length - 1](stream, state);\n        var token = stream.current();\n\n        if (style && style != \"comment\") {\n          state.lastToken = token;\n          state.lastStyle = style;\n        }\n\n        return style;\n      },\n\n      indent: function (state, textAfter) {\n        textAfter = textAfter.replace(/^\\s*(?:\\{%)?\\s*|\\s*(?:%\\})?\\s*$/g, \"\");\n\n        if (dedentKeywords.test(textAfter) || dedentPunctuals.test(textAfter)) {\n          return config.indentUnit * (state.currentIndent - 1);\n        }\n\n        return config.indentUnit * state.currentIndent;\n      },\n\n      fold: \"indent\",\n      electricInput: wordRegExp(dedentPunctualsArray.concat(dedentKeywordsArray), true),\n      lineComment: '#'\n    };\n  });\n\n  CodeMirror.defineMIME(\"text/x-crystal\", \"crystal\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/crystal/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Crystal mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"crystal.js\"></script>\n<style>\n  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n  .cm-s-default span.cm-arrow { color: red; }\n</style>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Crystal</a>\n  </ul>\n</div>\n\n<article>\n<h2>Crystal mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n# Features of Crystal\n# - Ruby-inspired syntax.\n# - Statically type-checked but without having to specify the type of variables or method arguments.\n# - Be able to call C code by writing bindings to it in Crystal.\n# - Have compile-time evaluation and generation of code, to avoid boilerplate code.\n# - Compile to efficient native code.\n\n# A very basic HTTP server\nrequire \"http/server\"\n\nserver = HTTP::Server.new(8080) do |request|\n  HTTP::Response.ok \"text/plain\", \"Hello world, got #{request.path}!\"\nend\n\nputs \"Listening on http://0.0.0.0:8080\"\nserver.listen\n\nmodule Foo\n  abstract def abstract_method : String\n\n  @[AlwaysInline]\n  def with_foofoo\n    with Foo.new(self) yield\n  end\n\n  struct Foo\n    def initialize(@foo : ::Foo)\n    end\n\n    def hello_world\n      @foo.abstract_method\n    end\n  end\nend\n\nclass Bar\n  include Foo\n\n  @@foobar = 12345\n\n  def initialize(@bar : Int32)\n  end\n\n  macro alias_method(name, method)\n    def {{ name }}(*args)\n      {{ method }}(*args)\n    end\n  end\n\n  def a_method\n    \"Hello, World\"\n  end\n\n  alias_method abstract_method, a_method\n\n  def show_instance_vars : Nil\n    {% for var in @type.instance_vars %}\n      puts \"@{{ var }} = #{ @{{ var }} }\"\n    {% end %}\n  end\nend\n\nclass Baz &lt; Bar; end\n\nlib LibC\n  fun c_puts = \"puts\"(str : Char*) : Int\nend\n\nbaz = Baz.new(100)\nbaz.show_instance_vars\nbaz.with_foofoo do\n  LibC.c_puts hello_world\nend\n</textarea></form>\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    mode: \"text/x-crystal\",\n    matchBrackets: true,\n    indentUnit: 2\n  });\n</script>\n\n<p><strong>MIME types defined:</strong> <code>text/x-crystal</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/css/css.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"css\", function(config, parserConfig) {\n  var inline = parserConfig.inline\n  if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode(\"text/css\");\n\n  var indentUnit = config.indentUnit,\n      tokenHooks = parserConfig.tokenHooks,\n      documentTypes = parserConfig.documentTypes || {},\n      mediaTypes = parserConfig.mediaTypes || {},\n      mediaFeatures = parserConfig.mediaFeatures || {},\n      mediaValueKeywords = parserConfig.mediaValueKeywords || {},\n      propertyKeywords = parserConfig.propertyKeywords || {},\n      nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},\n      fontProperties = parserConfig.fontProperties || {},\n      counterDescriptors = parserConfig.counterDescriptors || {},\n      colorKeywords = parserConfig.colorKeywords || {},\n      valueKeywords = parserConfig.valueKeywords || {},\n      allowNested = parserConfig.allowNested,\n      lineComment = parserConfig.lineComment,\n      supportsAtComponent = parserConfig.supportsAtComponent === true,\n      highlightNonStandardPropertyKeywords = config.highlightNonStandardPropertyKeywords !== false;\n\n  var type, override;\n  function ret(style, tp) { type = tp; return style; }\n\n  // Tokenizers\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (tokenHooks[ch]) {\n      var result = tokenHooks[ch](stream, state);\n      if (result !== false) return result;\n    }\n    if (ch == \"@\") {\n      stream.eatWhile(/[\\w\\\\\\-]/);\n      return ret(\"def\", stream.current());\n    } else if (ch == \"=\" || (ch == \"~\" || ch == \"|\") && stream.eat(\"=\")) {\n      return ret(null, \"compare\");\n    } else if (ch == \"\\\"\" || ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    } else if (ch == \"#\") {\n      stream.eatWhile(/[\\w\\\\\\-]/);\n      return ret(\"atom\", \"hash\");\n    } else if (ch == \"!\") {\n      stream.match(/^\\s*\\w*/);\n      return ret(\"keyword\", \"important\");\n    } else if (/\\d/.test(ch) || ch == \".\" && stream.eat(/\\d/)) {\n      stream.eatWhile(/[\\w.%]/);\n      return ret(\"number\", \"unit\");\n    } else if (ch === \"-\") {\n      if (/[\\d.]/.test(stream.peek())) {\n        stream.eatWhile(/[\\w.%]/);\n        return ret(\"number\", \"unit\");\n      } else if (stream.match(/^-[\\w\\\\\\-]*/)) {\n        stream.eatWhile(/[\\w\\\\\\-]/);\n        if (stream.match(/^\\s*:/, false))\n          return ret(\"variable-2\", \"variable-definition\");\n        return ret(\"variable-2\", \"variable\");\n      } else if (stream.match(/^\\w+-/)) {\n        return ret(\"meta\", \"meta\");\n      }\n    } else if (/[,+>*\\/]/.test(ch)) {\n      return ret(null, \"select-op\");\n    } else if (ch == \".\" && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {\n      return ret(\"qualifier\", \"qualifier\");\n    } else if (/[:;{}\\[\\]\\(\\)]/.test(ch)) {\n      return ret(null, ch);\n    } else if (stream.match(/^[\\w-.]+(?=\\()/)) {\n      if (/^(url(-prefix)?|domain|regexp)$/i.test(stream.current())) {\n        state.tokenize = tokenParenthesized;\n      }\n      return ret(\"variable callee\", \"variable\");\n    } else if (/[\\w\\\\\\-]/.test(ch)) {\n      stream.eatWhile(/[\\w\\\\\\-]/);\n      return ret(\"property\", \"word\");\n    } else {\n      return ret(null, null);\n    }\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, ch;\n      while ((ch = stream.next()) != null) {\n        if (ch == quote && !escaped) {\n          if (quote == \")\") stream.backUp(1);\n          break;\n        }\n        escaped = !escaped && ch == \"\\\\\";\n      }\n      if (ch == quote || !escaped && quote != \")\") state.tokenize = null;\n      return ret(\"string\", \"string\");\n    };\n  }\n\n  function tokenParenthesized(stream, state) {\n    stream.next(); // Must be '('\n    if (!stream.match(/^\\s*[\\\"\\')]/, false))\n      state.tokenize = tokenString(\")\");\n    else\n      state.tokenize = null;\n    return ret(null, \"(\");\n  }\n\n  // Context management\n\n  function Context(type, indent, prev) {\n    this.type = type;\n    this.indent = indent;\n    this.prev = prev;\n  }\n\n  function pushContext(state, stream, type, indent) {\n    state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);\n    return type;\n  }\n\n  function popContext(state) {\n    if (state.context.prev)\n      state.context = state.context.prev;\n    return state.context.type;\n  }\n\n  function pass(type, stream, state) {\n    return states[state.context.type](type, stream, state);\n  }\n  function popAndPass(type, stream, state, n) {\n    for (var i = n || 1; i > 0; i--)\n      state.context = state.context.prev;\n    return pass(type, stream, state);\n  }\n\n  // Parser\n\n  function wordAsValue(stream) {\n    var word = stream.current().toLowerCase();\n    if (valueKeywords.hasOwnProperty(word))\n      override = \"atom\";\n    else if (colorKeywords.hasOwnProperty(word))\n      override = \"keyword\";\n    else\n      override = \"variable\";\n  }\n\n  var states = {};\n\n  states.top = function(type, stream, state) {\n    if (type == \"{\") {\n      return pushContext(state, stream, \"block\");\n    } else if (type == \"}\" && state.context.prev) {\n      return popContext(state);\n    } else if (supportsAtComponent && /@component/i.test(type)) {\n      return pushContext(state, stream, \"atComponentBlock\");\n    } else if (/^@(-moz-)?document$/i.test(type)) {\n      return pushContext(state, stream, \"documentTypes\");\n    } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {\n      return pushContext(state, stream, \"atBlock\");\n    } else if (/^@(font-face|counter-style)/i.test(type)) {\n      state.stateArg = type;\n      return \"restricted_atBlock_before\";\n    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {\n      return \"keyframes\";\n    } else if (type && type.charAt(0) == \"@\") {\n      return pushContext(state, stream, \"at\");\n    } else if (type == \"hash\") {\n      override = \"builtin\";\n    } else if (type == \"word\") {\n      override = \"tag\";\n    } else if (type == \"variable-definition\") {\n      return \"maybeprop\";\n    } else if (type == \"interpolation\") {\n      return pushContext(state, stream, \"interpolation\");\n    } else if (type == \":\") {\n      return \"pseudo\";\n    } else if (allowNested && type == \"(\") {\n      return pushContext(state, stream, \"parens\");\n    }\n    return state.context.type;\n  };\n\n  states.block = function(type, stream, state) {\n    if (type == \"word\") {\n      var word = stream.current().toLowerCase();\n      if (propertyKeywords.hasOwnProperty(word)) {\n        override = \"property\";\n        return \"maybeprop\";\n      } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {\n        override = highlightNonStandardPropertyKeywords ? \"string-2\" : \"property\";\n        return \"maybeprop\";\n      } else if (allowNested) {\n        override = stream.match(/^\\s*:(?:\\s|$)/, false) ? \"property\" : \"tag\";\n        return \"block\";\n      } else {\n        override += \" error\";\n        return \"maybeprop\";\n      }\n    } else if (type == \"meta\") {\n      return \"block\";\n    } else if (!allowNested && (type == \"hash\" || type == \"qualifier\")) {\n      override = \"error\";\n      return \"block\";\n    } else {\n      return states.top(type, stream, state);\n    }\n  };\n\n  states.maybeprop = function(type, stream, state) {\n    if (type == \":\") return pushContext(state, stream, \"prop\");\n    return pass(type, stream, state);\n  };\n\n  states.prop = function(type, stream, state) {\n    if (type == \";\") return popContext(state);\n    if (type == \"{\" && allowNested) return pushContext(state, stream, \"propBlock\");\n    if (type == \"}\" || type == \"{\") return popAndPass(type, stream, state);\n    if (type == \"(\") return pushContext(state, stream, \"parens\");\n\n    if (type == \"hash\" && !/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(stream.current())) {\n      override += \" error\";\n    } else if (type == \"word\") {\n      wordAsValue(stream);\n    } else if (type == \"interpolation\") {\n      return pushContext(state, stream, \"interpolation\");\n    }\n    return \"prop\";\n  };\n\n  states.propBlock = function(type, _stream, state) {\n    if (type == \"}\") return popContext(state);\n    if (type == \"word\") { override = \"property\"; return \"maybeprop\"; }\n    return state.context.type;\n  };\n\n  states.parens = function(type, stream, state) {\n    if (type == \"{\" || type == \"}\") return popAndPass(type, stream, state);\n    if (type == \")\") return popContext(state);\n    if (type == \"(\") return pushContext(state, stream, \"parens\");\n    if (type == \"interpolation\") return pushContext(state, stream, \"interpolation\");\n    if (type == \"word\") wordAsValue(stream);\n    return \"parens\";\n  };\n\n  states.pseudo = function(type, stream, state) {\n    if (type == \"meta\") return \"pseudo\";\n\n    if (type == \"word\") {\n      override = \"variable-3\";\n      return state.context.type;\n    }\n    return pass(type, stream, state);\n  };\n\n  states.documentTypes = function(type, stream, state) {\n    if (type == \"word\" && documentTypes.hasOwnProperty(stream.current())) {\n      override = \"tag\";\n      return state.context.type;\n    } else {\n      return states.atBlock(type, stream, state);\n    }\n  };\n\n  states.atBlock = function(type, stream, state) {\n    if (type == \"(\") return pushContext(state, stream, \"atBlock_parens\");\n    if (type == \"}\" || type == \";\") return popAndPass(type, stream, state);\n    if (type == \"{\") return popContext(state) && pushContext(state, stream, allowNested ? \"block\" : \"top\");\n\n    if (type == \"interpolation\") return pushContext(state, stream, \"interpolation\");\n\n    if (type == \"word\") {\n      var word = stream.current().toLowerCase();\n      if (word == \"only\" || word == \"not\" || word == \"and\" || word == \"or\")\n        override = \"keyword\";\n      else if (mediaTypes.hasOwnProperty(word))\n        override = \"attribute\";\n      else if (mediaFeatures.hasOwnProperty(word))\n        override = \"property\";\n      else if (mediaValueKeywords.hasOwnProperty(word))\n        override = \"keyword\";\n      else if (propertyKeywords.hasOwnProperty(word))\n        override = \"property\";\n      else if (nonStandardPropertyKeywords.hasOwnProperty(word))\n        override = highlightNonStandardPropertyKeywords ? \"string-2\" : \"property\";\n      else if (valueKeywords.hasOwnProperty(word))\n        override = \"atom\";\n      else if (colorKeywords.hasOwnProperty(word))\n        override = \"keyword\";\n      else\n        override = \"error\";\n    }\n    return state.context.type;\n  };\n\n  states.atComponentBlock = function(type, stream, state) {\n    if (type == \"}\")\n      return popAndPass(type, stream, state);\n    if (type == \"{\")\n      return popContext(state) && pushContext(state, stream, allowNested ? \"block\" : \"top\", false);\n    if (type == \"word\")\n      override = \"error\";\n    return state.context.type;\n  };\n\n  states.atBlock_parens = function(type, stream, state) {\n    if (type == \")\") return popContext(state);\n    if (type == \"{\" || type == \"}\") return popAndPass(type, stream, state, 2);\n    return states.atBlock(type, stream, state);\n  };\n\n  states.restricted_atBlock_before = function(type, stream, state) {\n    if (type == \"{\")\n      return pushContext(state, stream, \"restricted_atBlock\");\n    if (type == \"word\" && state.stateArg == \"@counter-style\") {\n      override = \"variable\";\n      return \"restricted_atBlock_before\";\n    }\n    return pass(type, stream, state);\n  };\n\n  states.restricted_atBlock = function(type, stream, state) {\n    if (type == \"}\") {\n      state.stateArg = null;\n      return popContext(state);\n    }\n    if (type == \"word\") {\n      if ((state.stateArg == \"@font-face\" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||\n          (state.stateArg == \"@counter-style\" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))\n        override = \"error\";\n      else\n        override = \"property\";\n      return \"maybeprop\";\n    }\n    return \"restricted_atBlock\";\n  };\n\n  states.keyframes = function(type, stream, state) {\n    if (type == \"word\") { override = \"variable\"; return \"keyframes\"; }\n    if (type == \"{\") return pushContext(state, stream, \"top\");\n    return pass(type, stream, state);\n  };\n\n  states.at = function(type, stream, state) {\n    if (type == \";\") return popContext(state);\n    if (type == \"{\" || type == \"}\") return popAndPass(type, stream, state);\n    if (type == \"word\") override = \"tag\";\n    else if (type == \"hash\") override = \"builtin\";\n    return \"at\";\n  };\n\n  states.interpolation = function(type, stream, state) {\n    if (type == \"}\") return popContext(state);\n    if (type == \"{\" || type == \";\") return popAndPass(type, stream, state);\n    if (type == \"word\") override = \"variable\";\n    else if (type != \"variable\" && type != \"(\" && type != \")\") override = \"error\";\n    return \"interpolation\";\n  };\n\n  return {\n    startState: function(base) {\n      return {tokenize: null,\n              state: inline ? \"block\" : \"top\",\n              stateArg: null,\n              context: new Context(inline ? \"block\" : \"top\", base || 0, null)};\n    },\n\n    token: function(stream, state) {\n      if (!state.tokenize && stream.eatSpace()) return null;\n      var style = (state.tokenize || tokenBase)(stream, state);\n      if (style && typeof style == \"object\") {\n        type = style[1];\n        style = style[0];\n      }\n      override = style;\n      if (type != \"comment\")\n        state.state = states[state.state](type, stream, state);\n      return override;\n    },\n\n    indent: function(state, textAfter) {\n      var cx = state.context, ch = textAfter && textAfter.charAt(0);\n      var indent = cx.indent;\n      if (cx.type == \"prop\" && (ch == \"}\" || ch == \")\")) cx = cx.prev;\n      if (cx.prev) {\n        if (ch == \"}\" && (cx.type == \"block\" || cx.type == \"top\" ||\n                          cx.type == \"interpolation\" || cx.type == \"restricted_atBlock\")) {\n          // Resume indentation from parent context.\n          cx = cx.prev;\n          indent = cx.indent;\n        } else if (ch == \")\" && (cx.type == \"parens\" || cx.type == \"atBlock_parens\") ||\n            ch == \"{\" && (cx.type == \"at\" || cx.type == \"atBlock\")) {\n          // Dedent relative to current context.\n          indent = Math.max(0, cx.indent - indentUnit);\n        }\n      }\n      return indent;\n    },\n\n    electricChars: \"}\",\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    blockCommentContinue: \" * \",\n    lineComment: lineComment,\n    fold: \"brace\"\n  };\n});\n\n  function keySet(array) {\n    var keys = {};\n    for (var i = 0; i < array.length; ++i) {\n      keys[array[i].toLowerCase()] = true;\n    }\n    return keys;\n  }\n\n  var documentTypes_ = [\n    \"domain\", \"regexp\", \"url\", \"url-prefix\"\n  ], documentTypes = keySet(documentTypes_);\n\n  var mediaTypes_ = [\n    \"all\", \"aural\", \"braille\", \"handheld\", \"print\", \"projection\", \"screen\",\n    \"tty\", \"tv\", \"embossed\"\n  ], mediaTypes = keySet(mediaTypes_);\n\n  var mediaFeatures_ = [\n    \"width\", \"min-width\", \"max-width\", \"height\", \"min-height\", \"max-height\",\n    \"device-width\", \"min-device-width\", \"max-device-width\", \"device-height\",\n    \"min-device-height\", \"max-device-height\", \"aspect-ratio\",\n    \"min-aspect-ratio\", \"max-aspect-ratio\", \"device-aspect-ratio\",\n    \"min-device-aspect-ratio\", \"max-device-aspect-ratio\", \"color\", \"min-color\",\n    \"max-color\", \"color-index\", \"min-color-index\", \"max-color-index\",\n    \"monochrome\", \"min-monochrome\", \"max-monochrome\", \"resolution\",\n    \"min-resolution\", \"max-resolution\", \"scan\", \"grid\", \"orientation\",\n    \"device-pixel-ratio\", \"min-device-pixel-ratio\", \"max-device-pixel-ratio\",\n    \"pointer\", \"any-pointer\", \"hover\", \"any-hover\", \"prefers-color-scheme\",\n    \"dynamic-range\", \"video-dynamic-range\"\n  ], mediaFeatures = keySet(mediaFeatures_);\n\n  var mediaValueKeywords_ = [\n    \"landscape\", \"portrait\", \"none\", \"coarse\", \"fine\", \"on-demand\", \"hover\",\n    \"interlace\", \"progressive\",\n    \"dark\", \"light\",\n    \"standard\", \"high\"\n  ], mediaValueKeywords = keySet(mediaValueKeywords_);\n\n  var propertyKeywords_ = [\n    \"align-content\", \"align-items\", \"align-self\", \"alignment-adjust\",\n    \"alignment-baseline\", \"all\", \"anchor-point\", \"animation\", \"animation-delay\",\n    \"animation-direction\", \"animation-duration\", \"animation-fill-mode\",\n    \"animation-iteration-count\", \"animation-name\", \"animation-play-state\",\n    \"animation-timing-function\", \"appearance\", \"azimuth\", \"backdrop-filter\",\n    \"backface-visibility\", \"background\", \"background-attachment\",\n    \"background-blend-mode\", \"background-clip\", \"background-color\",\n    \"background-image\", \"background-origin\", \"background-position\",\n    \"background-position-x\", \"background-position-y\", \"background-repeat\",\n    \"background-size\", \"baseline-shift\", \"binding\", \"bleed\", \"block-size\",\n    \"bookmark-label\", \"bookmark-level\", \"bookmark-state\", \"bookmark-target\",\n    \"border\", \"border-bottom\", \"border-bottom-color\", \"border-bottom-left-radius\",\n    \"border-bottom-right-radius\", \"border-bottom-style\", \"border-bottom-width\",\n    \"border-collapse\", \"border-color\", \"border-image\", \"border-image-outset\",\n    \"border-image-repeat\", \"border-image-slice\", \"border-image-source\",\n    \"border-image-width\", \"border-left\", \"border-left-color\", \"border-left-style\",\n    \"border-left-width\", \"border-radius\", \"border-right\", \"border-right-color\",\n    \"border-right-style\", \"border-right-width\", \"border-spacing\", \"border-style\",\n    \"border-top\", \"border-top-color\", \"border-top-left-radius\",\n    \"border-top-right-radius\", \"border-top-style\", \"border-top-width\",\n    \"border-width\", \"bottom\", \"box-decoration-break\", \"box-shadow\", \"box-sizing\",\n    \"break-after\", \"break-before\", \"break-inside\", \"caption-side\", \"caret-color\",\n    \"clear\", \"clip\", \"color\", \"color-profile\", \"column-count\", \"column-fill\",\n    \"column-gap\", \"column-rule\", \"column-rule-color\", \"column-rule-style\",\n    \"column-rule-width\", \"column-span\", \"column-width\", \"columns\", \"contain\",\n    \"content\", \"counter-increment\", \"counter-reset\", \"crop\", \"cue\", \"cue-after\",\n    \"cue-before\", \"cursor\", \"direction\", \"display\", \"dominant-baseline\",\n    \"drop-initial-after-adjust\", \"drop-initial-after-align\",\n    \"drop-initial-before-adjust\", \"drop-initial-before-align\", \"drop-initial-size\",\n    \"drop-initial-value\", \"elevation\", \"empty-cells\", \"fit\", \"fit-content\", \"fit-position\",\n    \"flex\", \"flex-basis\", \"flex-direction\", \"flex-flow\", \"flex-grow\",\n    \"flex-shrink\", \"flex-wrap\", \"float\", \"float-offset\", \"flow-from\", \"flow-into\",\n    \"font\", \"font-family\", \"font-feature-settings\", \"font-kerning\",\n    \"font-language-override\", \"font-optical-sizing\", \"font-size\",\n    \"font-size-adjust\", \"font-stretch\", \"font-style\", \"font-synthesis\",\n    \"font-variant\", \"font-variant-alternates\", \"font-variant-caps\",\n    \"font-variant-east-asian\", \"font-variant-ligatures\", \"font-variant-numeric\",\n    \"font-variant-position\", \"font-variation-settings\", \"font-weight\", \"gap\",\n    \"grid\", \"grid-area\", \"grid-auto-columns\", \"grid-auto-flow\", \"grid-auto-rows\",\n    \"grid-column\", \"grid-column-end\", \"grid-column-gap\", \"grid-column-start\",\n    \"grid-gap\", \"grid-row\", \"grid-row-end\", \"grid-row-gap\", \"grid-row-start\",\n    \"grid-template\", \"grid-template-areas\", \"grid-template-columns\",\n    \"grid-template-rows\", \"hanging-punctuation\", \"height\", \"hyphens\", \"icon\",\n    \"image-orientation\", \"image-rendering\", \"image-resolution\", \"inline-box-align\",\n    \"inset\", \"inset-block\", \"inset-block-end\", \"inset-block-start\", \"inset-inline\",\n    \"inset-inline-end\", \"inset-inline-start\", \"isolation\", \"justify-content\",\n    \"justify-items\", \"justify-self\", \"left\", \"letter-spacing\", \"line-break\",\n    \"line-height\", \"line-height-step\", \"line-stacking\", \"line-stacking-ruby\",\n    \"line-stacking-shift\", \"line-stacking-strategy\", \"list-style\",\n    \"list-style-image\", \"list-style-position\", \"list-style-type\", \"margin\",\n    \"margin-bottom\", \"margin-left\", \"margin-right\", \"margin-top\", \"marks\",\n    \"marquee-direction\", \"marquee-loop\", \"marquee-play-count\", \"marquee-speed\",\n    \"marquee-style\", \"mask-clip\", \"mask-composite\", \"mask-image\", \"mask-mode\",\n    \"mask-origin\", \"mask-position\", \"mask-repeat\", \"mask-size\",\"mask-type\",\n    \"max-block-size\", \"max-height\", \"max-inline-size\",\n    \"max-width\", \"min-block-size\", \"min-height\", \"min-inline-size\", \"min-width\",\n    \"mix-blend-mode\", \"move-to\", \"nav-down\", \"nav-index\", \"nav-left\", \"nav-right\",\n    \"nav-up\", \"object-fit\", \"object-position\", \"offset\", \"offset-anchor\",\n    \"offset-distance\", \"offset-path\", \"offset-position\", \"offset-rotate\",\n    \"opacity\", \"order\", \"orphans\", \"outline\", \"outline-color\", \"outline-offset\",\n    \"outline-style\", \"outline-width\", \"overflow\", \"overflow-style\",\n    \"overflow-wrap\", \"overflow-x\", \"overflow-y\", \"padding\", \"padding-bottom\",\n    \"padding-left\", \"padding-right\", \"padding-top\", \"page\", \"page-break-after\",\n    \"page-break-before\", \"page-break-inside\", \"page-policy\", \"pause\",\n    \"pause-after\", \"pause-before\", \"perspective\", \"perspective-origin\", \"pitch\",\n    \"pitch-range\", \"place-content\", \"place-items\", \"place-self\", \"play-during\",\n    \"position\", \"presentation-level\", \"punctuation-trim\", \"quotes\",\n    \"region-break-after\", \"region-break-before\", \"region-break-inside\",\n    \"region-fragment\", \"rendering-intent\", \"resize\", \"rest\", \"rest-after\",\n    \"rest-before\", \"richness\", \"right\", \"rotate\", \"rotation\", \"rotation-point\",\n    \"row-gap\", \"ruby-align\", \"ruby-overhang\", \"ruby-position\", \"ruby-span\",\n    \"scale\", \"scroll-behavior\", \"scroll-margin\", \"scroll-margin-block\",\n    \"scroll-margin-block-end\", \"scroll-margin-block-start\", \"scroll-margin-bottom\",\n    \"scroll-margin-inline\", \"scroll-margin-inline-end\",\n    \"scroll-margin-inline-start\", \"scroll-margin-left\", \"scroll-margin-right\",\n    \"scroll-margin-top\", \"scroll-padding\", \"scroll-padding-block\",\n    \"scroll-padding-block-end\", \"scroll-padding-block-start\",\n    \"scroll-padding-bottom\", \"scroll-padding-inline\", \"scroll-padding-inline-end\",\n    \"scroll-padding-inline-start\", \"scroll-padding-left\", \"scroll-padding-right\",\n    \"scroll-padding-top\", \"scroll-snap-align\", \"scroll-snap-type\",\n    \"shape-image-threshold\", \"shape-inside\", \"shape-margin\", \"shape-outside\",\n    \"size\", \"speak\", \"speak-as\", \"speak-header\", \"speak-numeral\",\n    \"speak-punctuation\", \"speech-rate\", \"stress\", \"string-set\", \"tab-size\",\n    \"table-layout\", \"target\", \"target-name\", \"target-new\", \"target-position\",\n    \"text-align\", \"text-align-last\", \"text-combine-upright\", \"text-decoration\",\n    \"text-decoration-color\", \"text-decoration-line\", \"text-decoration-skip\",\n    \"text-decoration-skip-ink\", \"text-decoration-style\", \"text-emphasis\",\n    \"text-emphasis-color\", \"text-emphasis-position\", \"text-emphasis-style\",\n    \"text-height\", \"text-indent\", \"text-justify\", \"text-orientation\",\n    \"text-outline\", \"text-overflow\", \"text-rendering\", \"text-shadow\",\n    \"text-size-adjust\", \"text-space-collapse\", \"text-transform\",\n    \"text-underline-position\", \"text-wrap\", \"top\", \"touch-action\", \"transform\", \"transform-origin\",\n    \"transform-style\", \"transition\", \"transition-delay\", \"transition-duration\",\n    \"transition-property\", \"transition-timing-function\", \"translate\",\n    \"unicode-bidi\", \"user-select\", \"vertical-align\", \"visibility\", \"voice-balance\",\n    \"voice-duration\", \"voice-family\", \"voice-pitch\", \"voice-range\", \"voice-rate\",\n    \"voice-stress\", \"voice-volume\", \"volume\", \"white-space\", \"widows\", \"width\",\n    \"will-change\", \"word-break\", \"word-spacing\", \"word-wrap\", \"writing-mode\", \"z-index\",\n    // SVG-specific\n    \"clip-path\", \"clip-rule\", \"mask\", \"enable-background\", \"filter\", \"flood-color\",\n    \"flood-opacity\", \"lighting-color\", \"stop-color\", \"stop-opacity\", \"pointer-events\",\n    \"color-interpolation\", \"color-interpolation-filters\",\n    \"color-rendering\", \"fill\", \"fill-opacity\", \"fill-rule\", \"image-rendering\",\n    \"marker\", \"marker-end\", \"marker-mid\", \"marker-start\", \"paint-order\", \"shape-rendering\", \"stroke\",\n    \"stroke-dasharray\", \"stroke-dashoffset\", \"stroke-linecap\", \"stroke-linejoin\",\n    \"stroke-miterlimit\", \"stroke-opacity\", \"stroke-width\", \"text-rendering\",\n    \"baseline-shift\", \"dominant-baseline\", \"glyph-orientation-horizontal\",\n    \"glyph-orientation-vertical\", \"text-anchor\", \"writing-mode\",\n  ], propertyKeywords = keySet(propertyKeywords_);\n\n  var nonStandardPropertyKeywords_ = [\n    \"accent-color\", \"aspect-ratio\", \"border-block\", \"border-block-color\", \"border-block-end\",\n    \"border-block-end-color\", \"border-block-end-style\", \"border-block-end-width\",\n    \"border-block-start\", \"border-block-start-color\", \"border-block-start-style\",\n    \"border-block-start-width\", \"border-block-style\", \"border-block-width\",\n    \"border-inline\", \"border-inline-color\", \"border-inline-end\",\n    \"border-inline-end-color\", \"border-inline-end-style\",\n    \"border-inline-end-width\", \"border-inline-start\", \"border-inline-start-color\",\n    \"border-inline-start-style\", \"border-inline-start-width\",\n    \"border-inline-style\", \"border-inline-width\", \"content-visibility\", \"margin-block\",\n    \"margin-block-end\", \"margin-block-start\", \"margin-inline\", \"margin-inline-end\",\n    \"margin-inline-start\", \"overflow-anchor\", \"overscroll-behavior\", \"padding-block\", \"padding-block-end\",\n    \"padding-block-start\", \"padding-inline\", \"padding-inline-end\",\n    \"padding-inline-start\", \"scroll-snap-stop\", \"scrollbar-3d-light-color\",\n    \"scrollbar-arrow-color\", \"scrollbar-base-color\", \"scrollbar-dark-shadow-color\",\n    \"scrollbar-face-color\", \"scrollbar-highlight-color\", \"scrollbar-shadow-color\",\n    \"scrollbar-track-color\", \"searchfield-cancel-button\", \"searchfield-decoration\",\n    \"searchfield-results-button\", \"searchfield-results-decoration\", \"shape-inside\", \"zoom\"\n  ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);\n\n  var fontProperties_ = [\n    \"font-display\", \"font-family\", \"src\", \"unicode-range\", \"font-variant\",\n     \"font-feature-settings\", \"font-stretch\", \"font-weight\", \"font-style\"\n  ], fontProperties = keySet(fontProperties_);\n\n  var counterDescriptors_ = [\n    \"additive-symbols\", \"fallback\", \"negative\", \"pad\", \"prefix\", \"range\",\n    \"speak-as\", \"suffix\", \"symbols\", \"system\"\n  ], counterDescriptors = keySet(counterDescriptors_);\n\n  var colorKeywords_ = [\n    \"aliceblue\", \"antiquewhite\", \"aqua\", \"aquamarine\", \"azure\", \"beige\",\n    \"bisque\", \"black\", \"blanchedalmond\", \"blue\", \"blueviolet\", \"brown\",\n    \"burlywood\", \"cadetblue\", \"chartreuse\", \"chocolate\", \"coral\", \"cornflowerblue\",\n    \"cornsilk\", \"crimson\", \"cyan\", \"darkblue\", \"darkcyan\", \"darkgoldenrod\",\n    \"darkgray\", \"darkgreen\", \"darkgrey\", \"darkkhaki\", \"darkmagenta\", \"darkolivegreen\",\n    \"darkorange\", \"darkorchid\", \"darkred\", \"darksalmon\", \"darkseagreen\",\n    \"darkslateblue\", \"darkslategray\", \"darkslategrey\", \"darkturquoise\", \"darkviolet\",\n    \"deeppink\", \"deepskyblue\", \"dimgray\", \"dimgrey\", \"dodgerblue\", \"firebrick\",\n    \"floralwhite\", \"forestgreen\", \"fuchsia\", \"gainsboro\", \"ghostwhite\",\n    \"gold\", \"goldenrod\", \"gray\", \"grey\", \"green\", \"greenyellow\", \"honeydew\",\n    \"hotpink\", \"indianred\", \"indigo\", \"ivory\", \"khaki\", \"lavender\",\n    \"lavenderblush\", \"lawngreen\", \"lemonchiffon\", \"lightblue\", \"lightcoral\",\n    \"lightcyan\", \"lightgoldenrodyellow\", \"lightgray\", \"lightgreen\", \"lightgrey\", \"lightpink\",\n    \"lightsalmon\", \"lightseagreen\", \"lightskyblue\", \"lightslategray\", \"lightslategrey\",\n    \"lightsteelblue\", \"lightyellow\", \"lime\", \"limegreen\", \"linen\", \"magenta\",\n    \"maroon\", \"mediumaquamarine\", \"mediumblue\", \"mediumorchid\", \"mediumpurple\",\n    \"mediumseagreen\", \"mediumslateblue\", \"mediumspringgreen\", \"mediumturquoise\",\n    \"mediumvioletred\", \"midnightblue\", \"mintcream\", \"mistyrose\", \"moccasin\",\n    \"navajowhite\", \"navy\", \"oldlace\", \"olive\", \"olivedrab\", \"orange\", \"orangered\",\n    \"orchid\", \"palegoldenrod\", \"palegreen\", \"paleturquoise\", \"palevioletred\",\n    \"papayawhip\", \"peachpuff\", \"peru\", \"pink\", \"plum\", \"powderblue\",\n    \"purple\", \"rebeccapurple\", \"red\", \"rosybrown\", \"royalblue\", \"saddlebrown\",\n    \"salmon\", \"sandybrown\", \"seagreen\", \"seashell\", \"sienna\", \"silver\", \"skyblue\",\n    \"slateblue\", \"slategray\", \"slategrey\", \"snow\", \"springgreen\", \"steelblue\", \"tan\",\n    \"teal\", \"thistle\", \"tomato\", \"turquoise\", \"violet\", \"wheat\", \"white\",\n    \"whitesmoke\", \"yellow\", \"yellowgreen\"\n  ], colorKeywords = keySet(colorKeywords_);\n\n  var valueKeywords_ = [\n    \"above\", \"absolute\", \"activeborder\", \"additive\", \"activecaption\", \"afar\",\n    \"after-white-space\", \"ahead\", \"alias\", \"all\", \"all-scroll\", \"alphabetic\", \"alternate\",\n    \"always\", \"amharic\", \"amharic-abegede\", \"antialiased\", \"appworkspace\",\n    \"arabic-indic\", \"armenian\", \"asterisks\", \"attr\", \"auto\", \"auto-flow\", \"avoid\", \"avoid-column\", \"avoid-page\",\n    \"avoid-region\", \"axis-pan\", \"background\", \"backwards\", \"baseline\", \"below\", \"bidi-override\", \"binary\",\n    \"bengali\", \"blink\", \"block\", \"block-axis\", \"blur\", \"bold\", \"bolder\", \"border\", \"border-box\",\n    \"both\", \"bottom\", \"break\", \"break-all\", \"break-word\", \"brightness\", \"bullets\", \"button\",\n    \"buttonface\", \"buttonhighlight\", \"buttonshadow\", \"buttontext\", \"calc\", \"cambodian\",\n    \"capitalize\", \"caps-lock-indicator\", \"caption\", \"captiontext\", \"caret\",\n    \"cell\", \"center\", \"checkbox\", \"circle\", \"cjk-decimal\", \"cjk-earthly-branch\",\n    \"cjk-heavenly-stem\", \"cjk-ideographic\", \"clear\", \"clip\", \"close-quote\",\n    \"col-resize\", \"collapse\", \"color\", \"color-burn\", \"color-dodge\", \"column\", \"column-reverse\",\n    \"compact\", \"condensed\", \"conic-gradient\", \"contain\", \"content\", \"contents\",\n    \"content-box\", \"context-menu\", \"continuous\", \"contrast\", \"copy\", \"counter\", \"counters\", \"cover\", \"crop\",\n    \"cross\", \"crosshair\", \"cubic-bezier\", \"currentcolor\", \"cursive\", \"cyclic\", \"darken\", \"dashed\", \"decimal\",\n    \"decimal-leading-zero\", \"default\", \"default-button\", \"dense\", \"destination-atop\",\n    \"destination-in\", \"destination-out\", \"destination-over\", \"devanagari\", \"difference\",\n    \"disc\", \"discard\", \"disclosure-closed\", \"disclosure-open\", \"document\",\n    \"dot-dash\", \"dot-dot-dash\",\n    \"dotted\", \"double\", \"down\", \"drop-shadow\", \"e-resize\", \"ease\", \"ease-in\", \"ease-in-out\", \"ease-out\",\n    \"element\", \"ellipse\", \"ellipsis\", \"embed\", \"end\", \"ethiopic\", \"ethiopic-abegede\",\n    \"ethiopic-abegede-am-et\", \"ethiopic-abegede-gez\", \"ethiopic-abegede-ti-er\",\n    \"ethiopic-abegede-ti-et\", \"ethiopic-halehame-aa-er\",\n    \"ethiopic-halehame-aa-et\", \"ethiopic-halehame-am-et\",\n    \"ethiopic-halehame-gez\", \"ethiopic-halehame-om-et\",\n    \"ethiopic-halehame-sid-et\", \"ethiopic-halehame-so-et\",\n    \"ethiopic-halehame-ti-er\", \"ethiopic-halehame-ti-et\", \"ethiopic-halehame-tig\",\n    \"ethiopic-numeric\", \"ew-resize\", \"exclusion\", \"expanded\", \"extends\", \"extra-condensed\",\n    \"extra-expanded\", \"fantasy\", \"fast\", \"fill\", \"fill-box\", \"fixed\", \"flat\", \"flex\", \"flex-end\", \"flex-start\", \"footnotes\",\n    \"forwards\", \"from\", \"geometricPrecision\", \"georgian\", \"grayscale\", \"graytext\", \"grid\", \"groove\",\n    \"gujarati\", \"gurmukhi\", \"hand\", \"hangul\", \"hangul-consonant\", \"hard-light\", \"hebrew\",\n    \"help\", \"hidden\", \"hide\", \"higher\", \"highlight\", \"highlighttext\",\n    \"hiragana\", \"hiragana-iroha\", \"horizontal\", \"hsl\", \"hsla\", \"hue\", \"hue-rotate\", \"icon\", \"ignore\",\n    \"inactiveborder\", \"inactivecaption\", \"inactivecaptiontext\", \"infinite\",\n    \"infobackground\", \"infotext\", \"inherit\", \"initial\", \"inline\", \"inline-axis\",\n    \"inline-block\", \"inline-flex\", \"inline-grid\", \"inline-table\", \"inset\", \"inside\", \"intrinsic\", \"invert\",\n    \"italic\", \"japanese-formal\", \"japanese-informal\", \"justify\", \"kannada\",\n    \"katakana\", \"katakana-iroha\", \"keep-all\", \"khmer\",\n    \"korean-hangul-formal\", \"korean-hanja-formal\", \"korean-hanja-informal\",\n    \"landscape\", \"lao\", \"large\", \"larger\", \"left\", \"level\", \"lighter\", \"lighten\",\n    \"line-through\", \"linear\", \"linear-gradient\", \"lines\", \"list-item\", \"listbox\", \"listitem\",\n    \"local\", \"logical\", \"loud\", \"lower\", \"lower-alpha\", \"lower-armenian\",\n    \"lower-greek\", \"lower-hexadecimal\", \"lower-latin\", \"lower-norwegian\",\n    \"lower-roman\", \"lowercase\", \"ltr\", \"luminosity\", \"malayalam\", \"manipulation\", \"match\", \"matrix\", \"matrix3d\",\n    \"media-play-button\", \"media-slider\", \"media-sliderthumb\",\n    \"media-volume-slider\", \"media-volume-sliderthumb\", \"medium\",\n    \"menu\", \"menulist\", \"menulist-button\",\n    \"menutext\", \"message-box\", \"middle\", \"min-intrinsic\",\n    \"mix\", \"mongolian\", \"monospace\", \"move\", \"multiple\", \"multiple_mask_images\", \"multiply\", \"myanmar\", \"n-resize\",\n    \"narrower\", \"ne-resize\", \"nesw-resize\", \"no-close-quote\", \"no-drop\",\n    \"no-open-quote\", \"no-repeat\", \"none\", \"normal\", \"not-allowed\", \"nowrap\",\n    \"ns-resize\", \"numbers\", \"numeric\", \"nw-resize\", \"nwse-resize\", \"oblique\", \"octal\", \"opacity\", \"open-quote\",\n    \"optimizeLegibility\", \"optimizeSpeed\", \"oriya\", \"oromo\", \"outset\",\n    \"outside\", \"outside-shape\", \"overlay\", \"overline\", \"padding\", \"padding-box\",\n    \"painted\", \"page\", \"paused\", \"persian\", \"perspective\", \"pinch-zoom\", \"plus-darker\", \"plus-lighter\",\n    \"pointer\", \"polygon\", \"portrait\", \"pre\", \"pre-line\", \"pre-wrap\", \"preserve-3d\",\n    \"progress\", \"push-button\", \"radial-gradient\", \"radio\", \"read-only\",\n    \"read-write\", \"read-write-plaintext-only\", \"rectangle\", \"region\",\n    \"relative\", \"repeat\", \"repeating-linear-gradient\", \"repeating-radial-gradient\",\n    \"repeating-conic-gradient\", \"repeat-x\", \"repeat-y\", \"reset\", \"reverse\",\n    \"rgb\", \"rgba\", \"ridge\", \"right\", \"rotate\", \"rotate3d\", \"rotateX\", \"rotateY\",\n    \"rotateZ\", \"round\", \"row\", \"row-resize\", \"row-reverse\", \"rtl\", \"run-in\", \"running\",\n    \"s-resize\", \"sans-serif\", \"saturate\", \"saturation\", \"scale\", \"scale3d\", \"scaleX\", \"scaleY\", \"scaleZ\", \"screen\",\n    \"scroll\", \"scrollbar\", \"scroll-position\", \"se-resize\", \"searchfield\",\n    \"searchfield-cancel-button\", \"searchfield-decoration\",\n    \"searchfield-results-button\", \"searchfield-results-decoration\", \"self-start\", \"self-end\",\n    \"semi-condensed\", \"semi-expanded\", \"separate\", \"sepia\", \"serif\", \"show\", \"sidama\",\n    \"simp-chinese-formal\", \"simp-chinese-informal\", \"single\",\n    \"skew\", \"skewX\", \"skewY\", \"skip-white-space\", \"slide\", \"slider-horizontal\",\n    \"slider-vertical\", \"sliderthumb-horizontal\", \"sliderthumb-vertical\", \"slow\",\n    \"small\", \"small-caps\", \"small-caption\", \"smaller\", \"soft-light\", \"solid\", \"somali\",\n    \"source-atop\", \"source-in\", \"source-out\", \"source-over\", \"space\", \"space-around\", \"space-between\", \"space-evenly\", \"spell-out\", \"square\",\n    \"square-button\", \"start\", \"static\", \"status-bar\", \"stretch\", \"stroke\", \"stroke-box\", \"sub\",\n    \"subpixel-antialiased\", \"svg_masks\", \"super\", \"sw-resize\", \"symbolic\", \"symbols\", \"system-ui\", \"table\",\n    \"table-caption\", \"table-cell\", \"table-column\", \"table-column-group\",\n    \"table-footer-group\", \"table-header-group\", \"table-row\", \"table-row-group\",\n    \"tamil\",\n    \"telugu\", \"text\", \"text-bottom\", \"text-top\", \"textarea\", \"textfield\", \"thai\",\n    \"thick\", \"thin\", \"threeddarkshadow\", \"threedface\", \"threedhighlight\",\n    \"threedlightshadow\", \"threedshadow\", \"tibetan\", \"tigre\", \"tigrinya-er\",\n    \"tigrinya-er-abegede\", \"tigrinya-et\", \"tigrinya-et-abegede\", \"to\", \"top\",\n    \"trad-chinese-formal\", \"trad-chinese-informal\", \"transform\",\n    \"translate\", \"translate3d\", \"translateX\", \"translateY\", \"translateZ\",\n    \"transparent\", \"ultra-condensed\", \"ultra-expanded\", \"underline\", \"unidirectional-pan\", \"unset\", \"up\",\n    \"upper-alpha\", \"upper-armenian\", \"upper-greek\", \"upper-hexadecimal\",\n    \"upper-latin\", \"upper-norwegian\", \"upper-roman\", \"uppercase\", \"urdu\", \"url\",\n    \"var\", \"vertical\", \"vertical-text\", \"view-box\", \"visible\", \"visibleFill\", \"visiblePainted\",\n    \"visibleStroke\", \"visual\", \"w-resize\", \"wait\", \"wave\", \"wider\",\n    \"window\", \"windowframe\", \"windowtext\", \"words\", \"wrap\", \"wrap-reverse\", \"x-large\", \"x-small\", \"xor\",\n    \"xx-large\", \"xx-small\"\n  ], valueKeywords = keySet(valueKeywords_);\n\n  var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)\n    .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)\n    .concat(valueKeywords_);\n  CodeMirror.registerHelper(\"hintWords\", \"css\", allWords);\n\n  function tokenCComment(stream, state) {\n    var maybeEnd = false, ch;\n    while ((ch = stream.next()) != null) {\n      if (maybeEnd && ch == \"/\") {\n        state.tokenize = null;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return [\"comment\", \"comment\"];\n  }\n\n  CodeMirror.defineMIME(\"text/css\", {\n    documentTypes: documentTypes,\n    mediaTypes: mediaTypes,\n    mediaFeatures: mediaFeatures,\n    mediaValueKeywords: mediaValueKeywords,\n    propertyKeywords: propertyKeywords,\n    nonStandardPropertyKeywords: nonStandardPropertyKeywords,\n    fontProperties: fontProperties,\n    counterDescriptors: counterDescriptors,\n    colorKeywords: colorKeywords,\n    valueKeywords: valueKeywords,\n    tokenHooks: {\n      \"/\": function(stream, state) {\n        if (!stream.eat(\"*\")) return false;\n        state.tokenize = tokenCComment;\n        return tokenCComment(stream, state);\n      }\n    },\n    name: \"css\"\n  });\n\n  CodeMirror.defineMIME(\"text/x-scss\", {\n    mediaTypes: mediaTypes,\n    mediaFeatures: mediaFeatures,\n    mediaValueKeywords: mediaValueKeywords,\n    propertyKeywords: propertyKeywords,\n    nonStandardPropertyKeywords: nonStandardPropertyKeywords,\n    colorKeywords: colorKeywords,\n    valueKeywords: valueKeywords,\n    fontProperties: fontProperties,\n    allowNested: true,\n    lineComment: \"//\",\n    tokenHooks: {\n      \"/\": function(stream, state) {\n        if (stream.eat(\"/\")) {\n          stream.skipToEnd();\n          return [\"comment\", \"comment\"];\n        } else if (stream.eat(\"*\")) {\n          state.tokenize = tokenCComment;\n          return tokenCComment(stream, state);\n        } else {\n          return [\"operator\", \"operator\"];\n        }\n      },\n      \":\": function(stream) {\n        if (stream.match(/^\\s*\\{/, false))\n          return [null, null]\n        return false;\n      },\n      \"$\": function(stream) {\n        stream.match(/^[\\w-]+/);\n        if (stream.match(/^\\s*:/, false))\n          return [\"variable-2\", \"variable-definition\"];\n        return [\"variable-2\", \"variable\"];\n      },\n      \"#\": function(stream) {\n        if (!stream.eat(\"{\")) return false;\n        return [null, \"interpolation\"];\n      }\n    },\n    name: \"css\",\n    helperType: \"scss\"\n  });\n\n  CodeMirror.defineMIME(\"text/x-less\", {\n    mediaTypes: mediaTypes,\n    mediaFeatures: mediaFeatures,\n    mediaValueKeywords: mediaValueKeywords,\n    propertyKeywords: propertyKeywords,\n    nonStandardPropertyKeywords: nonStandardPropertyKeywords,\n    colorKeywords: colorKeywords,\n    valueKeywords: valueKeywords,\n    fontProperties: fontProperties,\n    allowNested: true,\n    lineComment: \"//\",\n    tokenHooks: {\n      \"/\": function(stream, state) {\n        if (stream.eat(\"/\")) {\n          stream.skipToEnd();\n          return [\"comment\", \"comment\"];\n        } else if (stream.eat(\"*\")) {\n          state.tokenize = tokenCComment;\n          return tokenCComment(stream, state);\n        } else {\n          return [\"operator\", \"operator\"];\n        }\n      },\n      \"@\": function(stream) {\n        if (stream.eat(\"{\")) return [null, \"interpolation\"];\n        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\\b/i, false)) return false;\n        stream.eatWhile(/[\\w\\\\\\-]/);\n        if (stream.match(/^\\s*:/, false))\n          return [\"variable-2\", \"variable-definition\"];\n        return [\"variable-2\", \"variable\"];\n      },\n      \"&\": function() {\n        return [\"atom\", \"atom\"];\n      }\n    },\n    name: \"css\",\n    helperType: \"less\"\n  });\n\n  CodeMirror.defineMIME(\"text/x-gss\", {\n    documentTypes: documentTypes,\n    mediaTypes: mediaTypes,\n    mediaFeatures: mediaFeatures,\n    propertyKeywords: propertyKeywords,\n    nonStandardPropertyKeywords: nonStandardPropertyKeywords,\n    fontProperties: fontProperties,\n    counterDescriptors: counterDescriptors,\n    colorKeywords: colorKeywords,\n    valueKeywords: valueKeywords,\n    supportsAtComponent: true,\n    tokenHooks: {\n      \"/\": function(stream, state) {\n        if (!stream.eat(\"*\")) return false;\n        state.tokenize = tokenCComment;\n        return tokenCComment(stream, state);\n      }\n    },\n    name: \"css\",\n    helperType: \"gss\"\n  });\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/css/gss.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Closure Stylesheets (GSS) mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../addon/hint/show-hint.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"css.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"../../addon/hint/show-hint.js\"></script>\n<script src=\"../../addon/hint/css-hint.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Closure Stylesheets (GSS)</a>\n  </ul>\n</div>\n\n<article>\n<h2>Closure Stylesheets (GSS) mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n/* Some example Closure Stylesheets */\n\n@provide 'some.styles';\n\n@require 'other.styles';\n\n@component {\n\n@def FONT_FAMILY           \"Times New Roman\", Georgia, Serif;\n@def FONT_SIZE_NORMAL      15px;\n@def FONT_NORMAL           normal FONT_SIZE_NORMAL FONT_FAMILY;\n\n@def BG_COLOR              rgb(235, 239, 249);\n\n@def DIALOG_BORDER_COLOR   rgb(107, 144, 218);\n@def DIALOG_BG_COLOR       BG_COLOR;\n\n@def LEFT_HAND_NAV_WIDTH    180px;\n@def LEFT_HAND_NAV_PADDING  3px;\n\n@defmixin size(WIDTH, HEIGHT) {\n  width: WIDTH;\n  height: HEIGHT;\n}\n\nbody {\n  background-color: BG_COLOR;\n  margin: 0;\n  padding: 3em 6em;\n  font: FONT_NORMAL;\n  color: #000;\n}\n\n#navigation a {\n  font-weight: bold;\n  text-decoration: none !important;\n}\n\n.dialog {\n  background-color: DIALOG_BG_COLOR;\n  border: 1px solid DIALOG_BORDER_COLOR;\n}\n\n.content {\n  position: absolute;\n  margin-left: add(LEFT_HAND_NAV_PADDING,  /* padding left */\n                   LEFT_HAND_NAV_WIDTH,\n                   LEFT_HAND_NAV_PADDING); /* padding right */\n\n}\n\n.logo {\n  @mixin size(150px, 55px);\n  background-image: url('http://www.google.com/images/logo_sm.gif');\n}\n\n}\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        extraKeys: {\"Ctrl-Space\": \"autocomplete\"},\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-gss\"\n      });\n    </script>\n\n    <p>A mode for <a href=\"https://github.com/google/closure-stylesheets\">Closure Stylesheets</a> (GSS).</p>\n    <p><strong>MIME type defined:</strong> <code>text/x-gss</code>.</p>\n\n    <p><strong>Parsing/Highlighting Tests:</strong> <a href=\"../../test/index.html#gss_*\">normal</a>,  <a href=\"../../test/index.html#verbose,gss_*\">verbose</a>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/css/gss_test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  \"use strict\";\n\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"text/x-gss\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), \"gss\"); }\n\n  MT(\"atComponent\",\n     \"[def @component] {\",\n     \"[tag foo] {\",\n     \"  [property color]: [keyword black];\",\n     \"}\",\n     \"}\");\n\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/css/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: CSS mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../addon/hint/show-hint.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"css.js\"></script>\n<script src=\"../../addon/hint/show-hint.js\"></script>\n<script src=\"../../addon/hint/css-hint.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">CSS</a>\n  </ul>\n</div>\n\n<article>\n<h2>CSS mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n/* Some example CSS */\n\n@import url(\"something.css\");\n\nbody {\n  margin: 0;\n  padding: 3em 6em;\n  font-family: tahoma, arial, sans-serif;\n  color: #000;\n}\n\n#navigation a {\n  font-weight: bold;\n  text-decoration: none !important;\n}\n\nh1 {\n  font-size: 2.5em;\n}\n\nh2 {\n  font-size: 1.7em;\n}\n\nh1:before, h2:before {\n  content: \"::\";\n}\n\ncode {\n  font-family: courier, monospace;\n  font-size: 80%;\n  color: #418A8A;\n}\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        extraKeys: {\"Ctrl-Space\": \"autocomplete\"}\n      });\n    </script>\n\n  <p>CSS mode supports this option:</p>\n  <d1>\n    <dt><code><strong>highlightNonStandardPropertyKeywords</strong>: boolean</code></dt>\n    <dd>Whether to highlight non-standard CSS property keywords such as <code>margin-inline</code> or <code>zoom</code> (default: <code>true</code>).</dd>\n  </d1>\n\n    <p><strong>MIME types defined:</strong> <code>text/css</code>, <code>text/x-scss</code> (<a href=\"scss.html\">demo</a>), <code>text/x-less</code> (<a href=\"less.html\">demo</a>).</p>\n\n    <p><strong>Parsing/Highlighting Tests:</strong> <a href=\"../../test/index.html#css_*\">normal</a>,  <a href=\"../../test/index.html#verbose,css_*\">verbose</a>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/css/less.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: LESS mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"css.js\"></script>\n<style>.CodeMirror {border: 1px solid #ddd; line-height: 1.2;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">LESS</a>\n  </ul>\n</div>\n\n<article>\n<h2>LESS mode</h2>\n<form><textarea id=\"code\" name=\"code\">@media screen and (device-aspect-ratio: 16/9) { … }\n@media screen and (device-aspect-ratio: 1280/720) { … }\n@media screen and (device-aspect-ratio: 2560/1440) { … }\n\nhtml:lang(fr-be)\n\ntr:nth-child(2n+1) /* represents every odd row of an HTML table */\n\nimg:nth-of-type(2n+1) { float: right; }\nimg:nth-of-type(2n) { float: left; }\n\nbody > h2:not(:first-of-type):not(:last-of-type)\n\nhtml|*:not(:link):not(:visited)\n*|*:not(:hover)\np::first-line { text-transform: uppercase }\n\n@namespace foo url(http://www.example.com);\nfoo|h1 { color: blue }  /* first rule */\n\nspan[hello=\"Ocean\"][goodbye=\"Land\"]\n\nE[foo]{\n  padding:65px;\n}\n\ninput[type=\"search\"]::-webkit-search-decoration,\ninput[type=\"search\"]::-webkit-search-cancel-button {\n  -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner { // Inner padding and border oddities in FF3/4\n  padding: 0;\n  border: 0;\n}\n.btn {\n  // reset here as of 2.0.3 due to Recess property order\n  border-color: #ccc;\n  border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);\n}\nfieldset span button, fieldset span input[type=\"file\"] {\n  font-size:12px;\n\tfont-family:Arial, Helvetica, sans-serif;\n}\n\n.rounded-corners (@radius: 5px) {\n  border-radius: @radius;\n  -webkit-border-radius: @radius;\n  -moz-border-radius: @radius;\n}\n\n@import url(\"something.css\");\n\n@light-blue:   hsl(190, 50%, 65%);\n\n#menu {\n  position: absolute;\n  width: 100%;\n  z-index: 3;\n  clear: both;\n  display: block;\n  background-color: @blue;\n  height: 42px;\n  border-top: 2px solid lighten(@alpha-blue, 20%);\n  border-bottom: 2px solid darken(@alpha-blue, 25%);\n  .box-shadow(0, 1px, 8px, 0.6);\n  -moz-box-shadow: 0 0 0 #000; // Because firefox sucks.\n\n  &.docked {\n    background-color: hsla(210, 60%, 40%, 0.4);\n  }\n  &:hover {\n    background-color: @blue;\n  }\n\n  #dropdown {\n    margin: 0 0 0 117px;\n    padding: 0;\n    padding-top: 5px;\n    display: none;\n    width: 190px;\n    border-top: 2px solid @medium;\n    color: @highlight;\n    border: 2px solid darken(@medium, 25%);\n    border-left-color: darken(@medium, 15%);\n    border-right-color: darken(@medium, 15%);\n    border-top-width: 0;\n    background-color: darken(@medium, 10%);\n    ul {\n      padding: 0px;  \n    }\n    li {\n      font-size: 14px;\n      display: block;\n      text-align: left;\n      padding: 0;\n      border: 0;\n      a {\n        display: block;\n        padding: 0px 15px;  \n        text-decoration: none;\n        color: white;  \n        &:hover {\n          background-color: darken(@medium, 15%);\n          text-decoration: none;\n        }\n      }\n    }\n    .border-radius(5px, bottom);\n    .box-shadow(0, 6px, 8px, 0.5);\n  }\n}\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-less\"\n      });\n    </script>\n\n    <p>The LESS mode is a sub-mode of the <a href=\"index.html\">CSS mode</a> (defined in <code>css.js</code>).</p>\n\n    <p><strong>Parsing/Highlighting Tests:</strong> <a href=\"../../test/index.html#less_*\">normal</a>,  <a href=\"../../test/index.html#verbose,less_*\">verbose</a>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/css/less_test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  \"use strict\";\n\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"text/x-less\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), \"less\"); }\n\n  MT(\"variable\",\n     \"[variable-2 @base]: [atom #f04615];\",\n     \"[qualifier .class] {\",\n     \"  [property width]: [variable&callee percentage]([number 0.5]); [comment // returns `50%`]\",\n     \"  [property color]: [variable&callee saturate]([variable-2 @base], [number 5%]);\",\n     \"}\");\n\n  MT(\"amp\",\n     \"[qualifier .child], [qualifier .sibling] {\",\n     \"  [qualifier .parent] [atom &] {\",\n     \"    [property color]: [keyword black];\",\n     \"  }\",\n     \"  [atom &] + [atom &] {\",\n     \"    [property color]: [keyword red];\",\n     \"  }\",\n     \"}\");\n\n  MT(\"mixin\",\n     \"[qualifier .mixin] ([variable dark]; [variable-2 @color]) {\",\n     \"  [property color]: [variable&callee darken]([variable-2 @color], [number 10%]);\",\n     \"}\",\n     \"[qualifier .mixin] ([variable light]; [variable-2 @color]) {\",\n     \"  [property color]: [variable&callee lighten]([variable-2 @color], [number 10%]);\",\n     \"}\",\n     \"[qualifier .mixin] ([variable-2 @_]; [variable-2 @color]) {\",\n     \"  [property display]: [atom block];\",\n     \"}\",\n     \"[variable-2 @switch]: [variable light];\",\n     \"[qualifier .class] {\",\n     \"  [qualifier .mixin]([variable-2 @switch]; [atom #888]);\",\n     \"}\");\n\n  MT(\"nest\",\n     \"[qualifier .one] {\",\n     \"  [def @media] ([property width]: [number 400px]) {\",\n     \"    [property font-size]: [number 1.2em];\",\n     \"    [def @media] [attribute print] [keyword and] [property color] {\",\n     \"      [property color]: [keyword blue];\",\n     \"    }\",\n     \"  }\",\n     \"}\");\n\n\n  MT(\"interpolation\", \".@{[variable foo]} { [property font-weight]: [atom bold]; }\");\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/css/scss.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: SCSS mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"css.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">SCSS</a>\n  </ul>\n</div>\n\n<article>\n<h2>SCSS mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n/* Some example SCSS */\n\n@import \"compass/css3\";\n$variable: #333;\n\n$blue: #3bbfce;\n$margin: 16px;\n\n.content-navigation {\n  #nested {\n    background-color: black;\n  }\n  border-color: $blue;\n  color:\n    darken($blue, 9%);\n}\n\n.border {\n  padding: $margin / 2;\n  margin: $margin / 2;\n  border-color: $blue;\n}\n\n@mixin table-base {\n  th {\n    text-align: center;\n    font-weight: bold;\n  }\n  td, th {padding: 2px}\n}\n\ntable.hl {\n  margin: 2em 0;\n  td.ln {\n    text-align: right;\n  }\n}\n\nli {\n  font: {\n    family: serif;\n    weight: bold;\n    size: 1.2em;\n  }\n}\n\n@mixin left($dist) {\n  float: left;\n  margin-left: $dist;\n}\n\n#data {\n  @include left(10px);\n  @include table-base;\n}\n\n.source {\n  @include flow-into(target);\n  border: 10px solid green;\n  margin: 20px;\n  width: 200px; }\n\n.new-container {\n  @include flow-from(target);\n  border: 10px solid red;\n  margin: 20px;\n  width: 200px; }\n\nbody {\n  margin: 0;\n  padding: 3em 6em;\n  font-family: tahoma, arial, sans-serif;\n  color: #000;\n}\n\n@mixin yellow() {\n  background: yellow;\n}\n\n.big {\n  font-size: 14px;\n}\n\n.nested {\n  @include border-radius(3px);\n  @extend .big;\n  p {\n    background: whitesmoke;\n    a {\n      color: red;\n    }\n  }\n}\n\n#navigation a {\n  font-weight: bold;\n  text-decoration: none !important;\n}\n\nh1 {\n  font-size: 2.5em;\n}\n\nh2 {\n  font-size: 1.7em;\n}\n\nh1:before, h2:before {\n  content: \"::\";\n}\n\ncode {\n  font-family: courier, monospace;\n  font-size: 80%;\n  color: #418A8A;\n}\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-scss\"\n      });\n    </script>\n\n    <p>The SCSS mode is a sub-mode of the <a href=\"index.html\">CSS mode</a> (defined in <code>css.js</code>).</p>\n\n    <p><strong>Parsing/Highlighting Tests:</strong> <a href=\"../../test/index.html#scss_*\">normal</a>,  <a href=\"../../test/index.html#verbose,scss_*\">verbose</a>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/css/scss_test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"text/x-scss\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), \"scss\"); }\n\n  MT('url_with_quotation',\n    \"[tag foo] { [property background]:[variable&callee url]([string test.jpg]) }\");\n\n  MT('url_with_double_quotes',\n    \"[tag foo] { [property background]:[variable&callee url]([string \\\"test.jpg\\\"]) }\");\n\n  MT('url_with_single_quotes',\n    \"[tag foo] { [property background]:[variable&callee url]([string \\'test.jpg\\']) }\");\n\n  MT('string',\n    \"[def @import] [string \\\"compass/css3\\\"]\");\n\n  MT('important_keyword',\n    \"[tag foo] { [property background]:[variable&callee url]([string \\'test.jpg\\']) [keyword !important] }\");\n\n  MT('variable',\n    \"[variable-2 $blue]:[atom #333]\");\n\n  MT('variable_as_attribute',\n    \"[tag foo] { [property color]:[variable-2 $blue] }\");\n\n  MT('numbers',\n    \"[tag foo] { [property padding]:[number 10px] [number 10] [number 10em] [number 8in] }\");\n\n  MT('number_percentage',\n    \"[tag foo] { [property width]:[number 80%] }\");\n\n  MT('selector',\n    \"[builtin #hello][qualifier .world]{}\");\n\n  MT('singleline_comment',\n    \"[comment // this is a comment]\");\n\n  MT('multiline_comment',\n    \"[comment /*foobar*/]\");\n\n  MT('attribute_with_hyphen',\n    \"[tag foo] { [property font-size]:[number 10px] }\");\n\n  MT('string_after_attribute',\n    \"[tag foo] { [property content]:[string \\\"::\\\"] }\");\n\n  MT('directives',\n    \"[def @include] [qualifier .mixin]\");\n\n  MT('basic_structure',\n    \"[tag p] { [property background]:[keyword red]; }\");\n\n  MT('nested_structure',\n    \"[tag p] { [tag a] { [property color]:[keyword red]; } }\");\n\n  MT('mixin',\n    \"[def @mixin] [tag table-base] {}\");\n\n  MT('number_without_semicolon',\n    \"[tag p] {[property width]:[number 12]}\",\n    \"[tag a] {[property color]:[keyword red];}\");\n\n  MT('atom_in_nested_block',\n    \"[tag p] { [tag a] { [property color]:[atom #000]; } }\");\n\n  MT('interpolation_in_property',\n    \"[tag foo] { #{[variable-2 $hello]}:[number 2]; }\");\n\n  MT('interpolation_in_selector',\n    \"[tag foo]#{[variable-2 $hello]} { [property color]:[atom #000]; }\");\n\n  MT('interpolation_error',\n    \"[tag foo]#{[variable foo]} { [property color]:[atom #000]; }\");\n\n  MT(\"divide_operator\",\n    \"[tag foo] { [property width]:[number 4] [operator /] [number 2] }\");\n\n  MT('nested_structure_with_id_selector',\n    \"[tag p] { [builtin #hello] { [property color]:[keyword red]; } }\");\n\n  MT('indent_mixin',\n     \"[def @mixin] [tag container] (\",\n     \"  [variable-2 $a]: [number 10],\",\n     \"  [variable-2 $b]: [number 10])\",\n     \"{}\");\n\n  MT('indent_nested',\n     \"[tag foo] {\",\n     \"  [tag bar] {\",\n     \"  }\",\n     \"}\");\n\n  MT('indent_parentheses',\n     \"[tag foo] {\",\n     \"  [property color]: [variable&callee darken]([variable-2 $blue],\",\n     \"    [number 9%]);\",\n     \"}\");\n\n  MT('indent_vardef',\n     \"[variable-2 $name]:\",\n     \"  [string 'val'];\",\n     \"[tag tag] {\",\n     \"  [tag inner] {\",\n     \"    [property margin]: [number 3px];\",\n     \"  }\",\n     \"}\");\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/css/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"css\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  // Error, because \"foobarhello\" is neither a known type or property, but\n  // property was expected (after \"and\"), and it should be in parentheses.\n  MT(\"atMediaUnknownType\",\n     \"[def @media] [attribute screen] [keyword and] [error foobarhello] { }\");\n\n  // Soft error, because \"foobarhello\" is not a known property or type.\n  MT(\"atMediaUnknownProperty\",\n     \"[def @media] [attribute screen] [keyword and] ([error foobarhello]) { }\");\n\n  // Make sure nesting works with media queries\n  MT(\"atMediaMaxWidthNested\",\n     \"[def @media] [attribute screen] [keyword and] ([property max-width]: [number 25px]) { [tag foo] { } }\");\n\n  MT(\"atMediaFeatureValueKeyword\",\n     \"[def @media] ([property orientation]: [keyword landscape]) { }\");\n\n  MT(\"atMediaUnknownFeatureValueKeyword\",\n     \"[def @media] ([property orientation]: [error upsidedown]) { }\");\n\n  MT(\"atMediaUppercase\",\n     \"[def @MEDIA] ([property orienTAtion]: [keyword landScape]) { }\");\n\n  MT(\"tagSelector\",\n     \"[tag foo] { }\");\n\n  MT(\"classSelector\",\n     \"[qualifier .foo-bar_hello] { }\");\n\n  MT(\"idSelector\",\n     \"[builtin #foo] { [error #foo] }\");\n\n  MT(\"tagSelectorUnclosed\",\n     \"[tag foo] { [property margin]: [number 0] } [tag bar] { }\");\n\n  MT(\"tagStringNoQuotes\",\n     \"[tag foo] { [property font-family]: [variable hello] [variable world]; }\");\n\n  MT(\"tagStringDouble\",\n     \"[tag foo] { [property font-family]: [string \\\"hello world\\\"]; }\");\n\n  MT(\"tagStringSingle\",\n     \"[tag foo] { [property font-family]: [string 'hello world']; }\");\n\n  MT(\"tagColorKeyword\",\n     \"[tag foo] {\",\n     \"  [property color]: [keyword black];\",\n     \"  [property color]: [keyword navy];\",\n     \"  [property color]: [keyword yellow];\",\n     \"}\");\n\n  MT(\"tagColorHex3\",\n     \"[tag foo] { [property background]: [atom #fff]; }\");\n\n  MT(\"tagColorHex4\",\n     \"[tag foo] { [property background]: [atom #ffff]; }\");\n\n  MT(\"tagColorHex6\",\n     \"[tag foo] { [property background]: [atom #ffffff]; }\");\n\n  MT(\"tagColorHex8\",\n     \"[tag foo] { [property background]: [atom #ffffffff]; }\");\n\n  MT(\"tagColorHex5Invalid\",\n     \"[tag foo] { [property background]: [atom&error #fffff]; }\");\n\n  MT(\"tagColorHexInvalid\",\n     \"[tag foo] { [property background]: [atom&error #ffg]; }\");\n\n  MT(\"tagNegativeNumber\",\n     \"[tag foo] { [property margin]: [number -5px]; }\");\n\n  MT(\"tagPositiveNumber\",\n     \"[tag foo] { [property padding]: [number 5px]; }\");\n\n  MT(\"tagVendor\",\n     \"[tag foo] { [meta -foo-][property box-sizing]: [meta -foo-][atom border-box]; }\");\n\n  MT(\"tagBogusProperty\",\n     \"[tag foo] { [property&error barhelloworld]: [number 0]; }\");\n\n  MT(\"tagTwoProperties\",\n     \"[tag foo] { [property margin]: [number 0]; [property padding]: [number 0]; }\");\n\n  MT(\"tagTwoPropertiesURL\",\n     \"[tag foo] { [property background]: [variable&callee url]([string //example.com/foo.png]); [property padding]: [number 0]; }\");\n\n  MT(\"indent_tagSelector\",\n     \"[tag strong], [tag em] {\",\n     \"  [property background]: [variable&callee rgba](\",\n     \"    [number 255], [number 255], [number 0], [number .2]\",\n     \"  );\",\n     \"}\");\n\n  MT(\"indent_atMedia\",\n     \"[def @media] {\",\n     \"  [tag foo] {\",\n     \"    [property color]:\",\n     \"      [keyword yellow];\",\n     \"  }\",\n     \"}\");\n\n  MT(\"indent_comma\",\n     \"[tag foo] {\",\n     \"  [property font-family]: [variable verdana],\",\n     \"    [atom sans-serif];\",\n     \"}\");\n\n  MT(\"indent_parentheses\",\n     \"[tag foo]:[variable-3 before] {\",\n     \"  [property background]: [variable&callee url](\",\n     \"[string     blahblah]\",\n     \"[string     etc]\",\n     \"[string   ]) [keyword !important];\",\n     \"}\");\n\n  MT(\"font_face\",\n     \"[def @font-face] {\",\n     \"  [property font-family]: [string 'myfont'];\",\n     \"  [error nonsense]: [string 'abc'];\",\n     \"  [property src]: [variable&callee url]([string http://blah]),\",\n     \"    [variable&callee url]([string http://foo]);\",\n     \"}\");\n\n  MT(\"empty_url\",\n     \"[def @import] [variable&callee url]() [attribute screen];\");\n\n  MT(\"parens\",\n     \"[qualifier .foo] {\",\n     \"  [property background-image]: [variable&callee fade]([atom #000], [number 20%]);\",\n     \"  [property border-image]: [variable&callee linear-gradient](\",\n     \"    [atom to] [atom bottom],\",\n     \"    [variable&callee fade]([atom #000], [number 20%]) [number 0%],\",\n     \"    [variable&callee fade]([atom #000], [number 20%]) [number 100%]\",\n     \"  );\",\n     \"}\");\n\n  MT(\"css_variable\",\n     \":[variable-3 root] {\",\n     \"  [variable-2 --main-color]: [atom #06c];\",\n     \"}\",\n     \"[tag h1][builtin #foo] {\",\n     \"  [property color]: [variable&callee var]([variable-2 --main-color]);\",\n     \"}\");\n\n  MT(\"blank_css_variable\",\n     \":[variable-3 root] {\",\n     \"  [variable-2 --]: [atom #06c];\",\n     \"}\",\n     \"[tag h1][builtin #foo] {\",\n     \"  [property color]: [variable&callee var]([variable-2 --]);\",\n     \"}\");\n\n  MT(\"supports\",\n     \"[def @supports] ([keyword not] (([property text-align-last]: [atom justify]) [keyword or] ([meta -moz-][property text-align-last]: [atom justify])) {\",\n     \"  [property text-align-last]: [atom justify];\",\n     \"}\");\n\n   MT(\"document\",\n      \"[def @document] [variable&callee url]([string http://blah]),\",\n      \"  [variable&callee url-prefix]([string https://]),\",\n      \"  [variable&callee domain]([string blah.com]),\",\n      \"  [variable&callee regexp]([string \\\".*blah.+\\\"]) {\",\n      \"    [builtin #id] {\",\n      \"      [property background-color]: [keyword white];\",\n      \"    }\",\n      \"    [tag foo] {\",\n      \"      [property font-family]: [variable Verdana], [atom sans-serif];\",\n      \"    }\",\n      \"}\");\n\n   MT(\"document_url\",\n      \"[def @document] [variable&callee url]([string http://blah]) { [qualifier .class] { } }\");\n\n   MT(\"document_urlPrefix\",\n      \"[def @document] [variable&callee url-prefix]([string https://]) { [builtin #id] { } }\");\n\n   MT(\"document_domain\",\n      \"[def @document] [variable&callee domain]([string blah.com]) { [tag foo] { } }\");\n\n   MT(\"document_regexp\",\n      \"[def @document] [variable&callee regexp]([string \\\".*blah.+\\\"]) { [builtin #id] { } }\");\n\n   MT(\"counter-style\",\n      \"[def @counter-style] [variable binary] {\",\n      \"  [property system]: [atom numeric];\",\n      \"  [property symbols]: [number 0] [number 1];\",\n      \"  [property suffix]: [string \\\".\\\"];\",\n      \"  [property range]: [atom infinite];\",\n      \"  [property speak-as]: [atom numeric];\",\n      \"}\");\n\n   MT(\"counter-style-additive-symbols\",\n      \"[def @counter-style] [variable simple-roman] {\",\n      \"  [property system]: [atom additive];\",\n      \"  [property additive-symbols]: [number 10] [variable X], [number 5] [variable V], [number 1] [variable I];\",\n      \"  [property range]: [number 1] [number 49];\",\n      \"}\");\n\n   MT(\"counter-style-use\",\n      \"[tag ol][qualifier .roman] { [property list-style]: [variable simple-roman]; }\");\n\n   MT(\"counter-style-symbols\",\n      \"[tag ol] { [property list-style]: [variable&callee symbols]([atom cyclic] [string \\\"*\\\"] [string \\\"\\\\2020\\\"] [string \\\"\\\\2021\\\"] [string \\\"\\\\A7\\\"]); }\");\n\n  MT(\"comment-does-not-disrupt\",\n     \"[def @font-face] [comment /* foo */] {\",\n     \"  [property src]: [variable&callee url]([string x]);\",\n     \"  [property font-family]: [variable One];\",\n     \"}\")\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/cypher/cypher.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// By the Neo4j Team and contributors.\n// https://github.com/neo4j-contrib/CodeMirror\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n  var wordRegexp = function(words) {\n    return new RegExp(\"^(?:\" + words.join(\"|\") + \")$\", \"i\");\n  };\n\n  CodeMirror.defineMode(\"cypher\", function(config) {\n    var tokenBase = function(stream/*, state*/) {\n      curPunc = null\n      var ch = stream.next();\n      if (ch ==='\"') {\n        stream.match(/^[^\"]*\"/);\n        return \"string\";\n      }\n      if (ch === \"'\") {\n        stream.match(/^[^']*'/);\n        return \"string\";\n      }\n      if (/[{}\\(\\),\\.;\\[\\]]/.test(ch)) {\n        curPunc = ch;\n        return \"node\";\n      } else if (ch === \"/\" && stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      } else if (operatorChars.test(ch)) {\n        stream.eatWhile(operatorChars);\n        return null;\n      } else {\n        stream.eatWhile(/[_\\w\\d]/);\n        if (stream.eat(\":\")) {\n          stream.eatWhile(/[\\w\\d_\\-]/);\n          return \"atom\";\n        }\n        var word = stream.current();\n        if (funcs.test(word)) return \"builtin\";\n        if (preds.test(word)) return \"def\";\n        if (keywords.test(word) || systemKeywords.test(word)) return \"keyword\";\n        return \"variable\";\n      }\n    };\n    var pushContext = function(state, type, col) {\n      return state.context = {\n        prev: state.context,\n        indent: state.indent,\n        col: col,\n        type: type\n      };\n    };\n    var popContext = function(state) {\n      state.indent = state.context.indent;\n      return state.context = state.context.prev;\n    };\n    var indentUnit = config.indentUnit;\n    var curPunc;\n    var funcs = wordRegexp([\"abs\", \"acos\", \"allShortestPaths\", \"asin\", \"atan\", \"atan2\", \"avg\", \"ceil\", \"coalesce\", \"collect\", \"cos\", \"cot\", \"count\", \"degrees\", \"e\", \"endnode\", \"exp\", \"extract\", \"filter\", \"floor\", \"haversin\", \"head\", \"id\", \"keys\", \"labels\", \"last\", \"left\", \"length\", \"log\", \"log10\", \"lower\", \"ltrim\", \"max\", \"min\", \"node\", \"nodes\", \"percentileCont\", \"percentileDisc\", \"pi\", \"radians\", \"rand\", \"range\", \"reduce\", \"rel\", \"relationship\", \"relationships\", \"replace\", \"reverse\", \"right\", \"round\", \"rtrim\", \"shortestPath\", \"sign\", \"sin\", \"size\", \"split\", \"sqrt\", \"startnode\", \"stdev\", \"stdevp\", \"str\", \"substring\", \"sum\", \"tail\", \"tan\", \"timestamp\", \"toFloat\", \"toInt\", \"toString\", \"trim\", \"type\", \"upper\"]);\n    var preds = wordRegexp([\"all\", \"and\", \"any\", \"contains\", \"exists\", \"has\", \"in\", \"none\", \"not\", \"or\", \"single\", \"xor\"]);\n    var keywords = wordRegexp([\"as\", \"asc\", \"ascending\", \"assert\", \"by\", \"case\", \"commit\", \"constraint\", \"create\", \"csv\", \"cypher\", \"delete\", \"desc\", \"descending\", \"detach\", \"distinct\", \"drop\", \"else\", \"end\", \"ends\", \"explain\", \"false\", \"fieldterminator\", \"foreach\", \"from\", \"headers\", \"in\", \"index\", \"is\", \"join\", \"limit\", \"load\", \"match\", \"merge\", \"null\", \"on\", \"optional\", \"order\", \"periodic\", \"profile\", \"remove\", \"return\", \"scan\", \"set\", \"skip\", \"start\", \"starts\", \"then\", \"true\", \"union\", \"unique\", \"unwind\", \"using\", \"when\", \"where\", \"with\", \"call\", \"yield\"]);\n    var systemKeywords = wordRegexp([\"access\", \"active\", \"assign\", \"all\", \"alter\", \"as\", \"catalog\", \"change\", \"copy\", \"create\", \"constraint\", \"constraints\", \"current\", \"database\", \"databases\", \"dbms\", \"default\", \"deny\", \"drop\", \"element\", \"elements\", \"exists\", \"from\", \"grant\", \"graph\", \"graphs\", \"if\", \"index\", \"indexes\", \"label\", \"labels\", \"management\", \"match\", \"name\", \"names\", \"new\", \"node\", \"nodes\", \"not\", \"of\", \"on\", \"or\", \"password\", \"populated\", \"privileges\", \"property\", \"read\", \"relationship\", \"relationships\", \"remove\", \"replace\", \"required\", \"revoke\", \"role\", \"roles\", \"set\", \"show\", \"start\", \"status\", \"stop\", \"suspended\", \"to\", \"traverse\", \"type\", \"types\", \"user\", \"users\", \"with\", \"write\"]);\n    var operatorChars = /[*+\\-<>=&|~%^]/;\n\n    return {\n      startState: function(/*base*/) {\n        return {\n          tokenize: tokenBase,\n          context: null,\n          indent: 0,\n          col: 0\n        };\n      },\n      token: function(stream, state) {\n        if (stream.sol()) {\n          if (state.context && (state.context.align == null)) {\n            state.context.align = false;\n          }\n          state.indent = stream.indentation();\n        }\n        if (stream.eatSpace()) {\n          return null;\n        }\n        var style = state.tokenize(stream, state);\n        if (style !== \"comment\" && state.context && (state.context.align == null) && state.context.type !== \"pattern\") {\n          state.context.align = true;\n        }\n        if (curPunc === \"(\") {\n          pushContext(state, \")\", stream.column());\n        } else if (curPunc === \"[\") {\n          pushContext(state, \"]\", stream.column());\n        } else if (curPunc === \"{\") {\n          pushContext(state, \"}\", stream.column());\n        } else if (/[\\]\\}\\)]/.test(curPunc)) {\n          while (state.context && state.context.type === \"pattern\") {\n            popContext(state);\n          }\n          if (state.context && curPunc === state.context.type) {\n            popContext(state);\n          }\n        } else if (curPunc === \".\" && state.context && state.context.type === \"pattern\") {\n          popContext(state);\n        } else if (/atom|string|variable/.test(style) && state.context) {\n          if (/[\\}\\]]/.test(state.context.type)) {\n            pushContext(state, \"pattern\", stream.column());\n          } else if (state.context.type === \"pattern\" && !state.context.align) {\n            state.context.align = true;\n            state.context.col = stream.column();\n          }\n        }\n        return style;\n      },\n      indent: function(state, textAfter) {\n        var firstChar = textAfter && textAfter.charAt(0);\n        var context = state.context;\n        if (/[\\]\\}]/.test(firstChar)) {\n          while (context && context.type === \"pattern\") {\n            context = context.prev;\n          }\n        }\n        var closing = context && firstChar === context.type;\n        if (!context) return 0;\n        if (context.type === \"keywords\") return CodeMirror.commands.newlineAndIndent;\n        if (context.align) return context.col + (closing ? 0 : 1);\n        return context.indent + (closing ? 0 : indentUnit);\n      }\n    };\n  });\n\n  CodeMirror.modeExtensions[\"cypher\"] = {\n    autoFormatLineBreaks: function(text) {\n      var i, lines, reProcessedPortion;\n      var lines = text.split(\"\\n\");\n      var reProcessedPortion = /\\s+\\b(return|where|order by|match|with|skip|limit|create|delete|set)\\b\\s/g;\n      for (var i = 0; i < lines.length; i++)\n        lines[i] = lines[i].replace(reProcessedPortion, \" \\n$1 \").trim();\n      return lines.join(\"\\n\");\n    }\n  };\n\n  CodeMirror.defineMIME(\"application/x-cypher-query\", \"cypher\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/cypher/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Cypher Mode for CodeMirror</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\" />\n<link rel=\"stylesheet\" href=\"../../theme/neo.css\" />\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"cypher.js\"></script>\n<style>\n.CodeMirror {\n    border-top: 1px solid black;\n    border-bottom: 1px solid black;\n}\n        </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Cypher Mode for CodeMirror</a>\n  </ul>\n</div>\n\n<article>\n<h2>Cypher Mode for CodeMirror</h2>\n<form>\n            <textarea id=\"code\" name=\"code\">// Cypher Mode for CodeMirror, using the neo theme\nMATCH (joe { name: 'Joe' })-[:knows*2..2]-(friend_of_friend)\nWHERE NOT (joe)-[:knows]-(friend_of_friend)\nRETURN friend_of_friend.name, COUNT(*)\nORDER BY COUNT(*) DESC , friend_of_friend.name\n</textarea>\n            </form>\n            <p><strong>MIME types defined:</strong> \n            <code><a href=\"?mime=application/x-cypher-query\">application/x-cypher-query</a></code>\n        </p>\n<script>\nwindow.onload = function() {\n  var mime = 'application/x-cypher-query';\n  // get mime type\n  if (window.location.href.indexOf('mime=') > -1) {\n    mime = window.location.href.substr(window.location.href.indexOf('mime=') + 5);\n  }\n  window.editor = CodeMirror.fromTextArea(document.getElementById('code'), {\n    mode: mime,\n    indentWithTabs: true,\n    smartIndent: true,\n    lineNumbers: true,\n    matchBrackets: true,\n    autofocus: true,\n    theme: 'neo'\n  });\n};\n</script>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/cypher/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({tabSize: 4, indentUnit: 2}, \"cypher\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"unbalancedDoubledQuotedString\",\n      \"[string \\\"a'b\\\"][variable c]\");\n\n  MT(\"unbalancedSingleQuotedString\",\n      \"[string 'a\\\"b'][variable c]\");\n\n  MT(\"doubleQuotedString\",\n      \"[string \\\"a\\\"][variable b]\");\n\n  MT(\"singleQuotedString\",\n      \"[string 'a'][variable b]\");\n\n  MT(\"single attribute (with content)\",\n      \"[node {][atom a:][string 'a'][node }]\");\n\n  MT(\"multiple attribute, singleQuotedString (with content)\",\n      \"[node {][atom a:][string 'a'][node ,][atom b:][string 'b'][node }]\");\n\n  MT(\"multiple attribute, doubleQuotedString (with content)\",\n      \"[node {][atom a:][string \\\"a\\\"][node ,][atom b:][string \\\"b\\\"][node }]\");\n\n  MT(\"single attribute (without content)\",\n      \"[node {][atom a:][string 'a'][node }]\");\n\n  MT(\"multiple attribute, singleQuotedString (without content)\",\n      \"[node {][atom a:][string ''][node ,][atom b:][string ''][node }]\");\n\n  MT(\"multiple attribute, doubleQuotedString (without content)\",\n      \"[node {][atom a:][string \\\"\\\"][node ,][atom b:][string \\\"\\\"][node }]\");\n })();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/d/d.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"d\", function(config, parserConfig) {\n  var indentUnit = config.indentUnit,\n      statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,\n      keywords = parserConfig.keywords || {},\n      builtin = parserConfig.builtin || {},\n      blockKeywords = parserConfig.blockKeywords || {},\n      atoms = parserConfig.atoms || {},\n      hooks = parserConfig.hooks || {},\n      multiLineStrings = parserConfig.multiLineStrings;\n  var isOperatorChar = /[+\\-*&%=<>!?|\\/]/;\n\n  var curPunc;\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (hooks[ch]) {\n      var result = hooks[ch](stream, state);\n      if (result !== false) return result;\n    }\n    if (ch == '\"' || ch == \"'\" || ch == \"`\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    }\n    if (/[\\[\\]{}\\(\\),;\\:\\.]/.test(ch)) {\n      curPunc = ch;\n      return null;\n    }\n    if (/\\d/.test(ch)) {\n      stream.eatWhile(/[\\w\\.]/);\n      return \"number\";\n    }\n    if (ch == \"/\") {\n      if (stream.eat(\"+\")) {\n        state.tokenize = tokenNestedComment;\n        return tokenNestedComment(stream, state);\n      }\n      if (stream.eat(\"*\")) {\n        state.tokenize = tokenComment;\n        return tokenComment(stream, state);\n      }\n      if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n    }\n    if (isOperatorChar.test(ch)) {\n      stream.eatWhile(isOperatorChar);\n      return \"operator\";\n    }\n    stream.eatWhile(/[\\w\\$_\\xa1-\\uffff]/);\n    var cur = stream.current();\n    if (keywords.propertyIsEnumerable(cur)) {\n      if (blockKeywords.propertyIsEnumerable(cur)) curPunc = \"newstatement\";\n      return \"keyword\";\n    }\n    if (builtin.propertyIsEnumerable(cur)) {\n      if (blockKeywords.propertyIsEnumerable(cur)) curPunc = \"newstatement\";\n      return \"builtin\";\n    }\n    if (atoms.propertyIsEnumerable(cur)) return \"atom\";\n    return \"variable\";\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {end = true; break;}\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (end || !(escaped || multiLineStrings))\n        state.tokenize = null;\n      return \"string\";\n    };\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = null;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n\n  function tokenNestedComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = null;\n        break;\n      }\n      maybeEnd = (ch == \"+\");\n    }\n    return \"comment\";\n  }\n\n  function Context(indented, column, type, align, prev) {\n    this.indented = indented;\n    this.column = column;\n    this.type = type;\n    this.align = align;\n    this.prev = prev;\n  }\n  function pushContext(state, col, type) {\n    var indent = state.indented;\n    if (state.context && state.context.type == \"statement\")\n      indent = state.context.indented;\n    return state.context = new Context(indent, col, type, null, state.context);\n  }\n  function popContext(state) {\n    var t = state.context.type;\n    if (t == \")\" || t == \"]\" || t == \"}\")\n      state.indented = state.context.indented;\n    return state.context = state.context.prev;\n  }\n\n  // Interface\n\n  return {\n    startState: function(basecolumn) {\n      return {\n        tokenize: null,\n        context: new Context((basecolumn || 0) - indentUnit, 0, \"top\", false),\n        indented: 0,\n        startOfLine: true\n      };\n    },\n\n    token: function(stream, state) {\n      var ctx = state.context;\n      if (stream.sol()) {\n        if (ctx.align == null) ctx.align = false;\n        state.indented = stream.indentation();\n        state.startOfLine = true;\n      }\n      if (stream.eatSpace()) return null;\n      curPunc = null;\n      var style = (state.tokenize || tokenBase)(stream, state);\n      if (style == \"comment\" || style == \"meta\") return style;\n      if (ctx.align == null) ctx.align = true;\n\n      if ((curPunc == \";\" || curPunc == \":\" || curPunc == \",\") && ctx.type == \"statement\") popContext(state);\n      else if (curPunc == \"{\") pushContext(state, stream.column(), \"}\");\n      else if (curPunc == \"[\") pushContext(state, stream.column(), \"]\");\n      else if (curPunc == \"(\") pushContext(state, stream.column(), \")\");\n      else if (curPunc == \"}\") {\n        while (ctx.type == \"statement\") ctx = popContext(state);\n        if (ctx.type == \"}\") ctx = popContext(state);\n        while (ctx.type == \"statement\") ctx = popContext(state);\n      }\n      else if (curPunc == ctx.type) popContext(state);\n      else if (((ctx.type == \"}\" || ctx.type == \"top\") && curPunc != ';') || (ctx.type == \"statement\" && curPunc == \"newstatement\"))\n        pushContext(state, stream.column(), \"statement\");\n      state.startOfLine = false;\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;\n      var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);\n      if (ctx.type == \"statement\" && firstChar == \"}\") ctx = ctx.prev;\n      var closing = firstChar == ctx.type;\n      if (ctx.type == \"statement\") return ctx.indented + (firstChar == \"{\" ? 0 : statementIndentUnit);\n      else if (ctx.align) return ctx.column + (closing ? 0 : 1);\n      else return ctx.indented + (closing ? 0 : indentUnit);\n    },\n\n    electricChars: \"{}\",\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    blockCommentContinue: \" * \",\n    lineComment: \"//\",\n    fold: \"brace\"\n  };\n});\n\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  var blockKeywords = \"body catch class do else enum for foreach foreach_reverse if in interface mixin \" +\n                      \"out scope struct switch try union unittest version while with\";\n\n  CodeMirror.defineMIME(\"text/x-d\", {\n    name: \"d\",\n    keywords: words(\"abstract alias align asm assert auto break case cast cdouble cent cfloat const continue \" +\n                    \"debug default delegate delete deprecated export extern final finally function goto immutable \" +\n                    \"import inout invariant is lazy macro module new nothrow override package pragma private \" +\n                    \"protected public pure ref return shared short static super synchronized template this \" +\n                    \"throw typedef typeid typeof volatile __FILE__ __LINE__ __gshared __traits __vector __parameters \" +\n                    blockKeywords),\n    blockKeywords: words(blockKeywords),\n    builtin: words(\"bool byte char creal dchar double float idouble ifloat int ireal long real short ubyte \" +\n                   \"ucent uint ulong ushort wchar wstring void size_t sizediff_t\"),\n    atoms: words(\"exit failure success true false null\"),\n    hooks: {\n      \"@\": function(stream, _state) {\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"meta\";\n      }\n    }\n  });\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/d/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: D mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"d.js\"></script>\n<style>.CodeMirror {border: 2px inset #dee;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">D</a>\n  </ul>\n</div>\n\n<article>\n<h2>D mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n/* D demo code // copied from phobos/sd/metastrings.d */\n// Written in the D programming language.\n\n/**\nTemplates with which to do compile-time manipulation of strings.\n\nMacros:\n WIKI = Phobos/StdMetastrings\n\nCopyright: Copyright Digital Mars 2007 - 2009.\nLicense:   <a href=\"http://www.boost.org/LICENSE_1_0.txt\">Boost License 1.0</a>.\nAuthors:   $(WEB digitalmars.com, Walter Bright),\n           Don Clugston\nSource:    $(PHOBOSSRC std/_metastrings.d)\n*/\n/*\n         Copyright Digital Mars 2007 - 2009.\nDistributed under the Boost Software License, Version 1.0.\n   (See accompanying file LICENSE_1_0.txt or copy at\n         http://www.boost.org/LICENSE_1_0.txt)\n */\nmodule std.metastrings;\n\n/**\nFormats constants into a string at compile time.  Analogous to $(XREF\nstring,format).\n\nParameters:\n\nA = tuple of constants, which can be strings, characters, or integral\n    values.\n\nFormats:\n *    The formats supported are %s for strings, and %%\n *    for the % character.\nExample:\n---\nimport std.metastrings;\nimport std.stdio;\n\nvoid main()\n{\n  string s = Format!(\"Arg %s = %s\", \"foo\", 27);\n  writefln(s); // \"Arg foo = 27\"\n}\n * ---\n */\n\ntemplate Format(A...)\n{\n    static if (A.length == 0)\n        enum Format = \"\";\n    else static if (is(typeof(A[0]) : const(char)[]))\n        enum Format = FormatString!(A[0], A[1..$]);\n    else\n        enum Format = toStringNow!(A[0]) ~ Format!(A[1..$]);\n}\n\ntemplate FormatString(const(char)[] F, A...)\n{\n    static if (F.length == 0)\n        enum FormatString = Format!(A);\n    else static if (F.length == 1)\n        enum FormatString = F[0] ~ Format!(A);\n    else static if (F[0..2] == \"%s\")\n        enum FormatString\n            = toStringNow!(A[0]) ~ FormatString!(F[2..$],A[1..$]);\n    else static if (F[0..2] == \"%%\")\n        enum FormatString = \"%\" ~ FormatString!(F[2..$],A);\n    else\n    {\n        static assert(F[0] != '%', \"unrecognized format %\" ~ F[1]);\n        enum FormatString = F[0] ~ FormatString!(F[1..$],A);\n    }\n}\n\nunittest\n{\n    auto s = Format!(\"hel%slo\", \"world\", -138, 'c', true);\n    assert(s == \"helworldlo-138ctrue\", \"[\" ~ s ~ \"]\");\n}\n\n/**\n * Convert constant argument to a string.\n */\n\ntemplate toStringNow(ulong v)\n{\n    static if (v < 10)\n        enum toStringNow = \"\" ~ cast(char)(v + '0');\n    else\n        enum toStringNow = toStringNow!(v / 10) ~ toStringNow!(v % 10);\n}\n\nunittest\n{\n    static assert(toStringNow!(1uL << 62) == \"4611686018427387904\");\n}\n\n/// ditto\ntemplate toStringNow(long v)\n{\n    static if (v < 0)\n        enum toStringNow = \"-\" ~ toStringNow!(cast(ulong) -v);\n    else\n        enum toStringNow = toStringNow!(cast(ulong) v);\n}\n\nunittest\n{\n    static assert(toStringNow!(0x100000000) == \"4294967296\");\n    static assert(toStringNow!(-138L) == \"-138\");\n}\n\n/// ditto\ntemplate toStringNow(uint U)\n{\n    enum toStringNow = toStringNow!(cast(ulong)U);\n}\n\n/// ditto\ntemplate toStringNow(int I)\n{\n    enum toStringNow = toStringNow!(cast(long)I);\n}\n\n/// ditto\ntemplate toStringNow(bool B)\n{\n    enum toStringNow = B ? \"true\" : \"false\";\n}\n\n/// ditto\ntemplate toStringNow(string S)\n{\n    enum toStringNow = S;\n}\n\n/// ditto\ntemplate toStringNow(char C)\n{\n    enum toStringNow = \"\" ~ C;\n}\n\n\n/********\n * Parse unsigned integer literal from the start of string s.\n * returns:\n *    .value = the integer literal as a string,\n *    .rest = the string following the integer literal\n * Otherwise:\n *    .value = null,\n *    .rest = s\n */\n\ntemplate parseUinteger(const(char)[] s)\n{\n    static if (s.length == 0)\n    {\n        enum value = \"\";\n        enum rest = \"\";\n    }\n    else static if (s[0] >= '0' && s[0] <= '9')\n    {\n        enum value = s[0] ~ parseUinteger!(s[1..$]).value;\n        enum rest = parseUinteger!(s[1..$]).rest;\n    }\n    else\n    {\n        enum value = \"\";\n        enum rest = s;\n    }\n}\n\n/********\nParse integer literal optionally preceded by $(D '-') from the start\nof string $(D s).\n\nReturns:\n   .value = the integer literal as a string,\n   .rest = the string following the integer literal\n\nOtherwise:\n   .value = null,\n   .rest = s\n*/\n\ntemplate parseInteger(const(char)[] s)\n{\n    static if (s.length == 0)\n    {\n        enum value = \"\";\n        enum rest = \"\";\n    }\n    else static if (s[0] >= '0' && s[0] <= '9')\n    {\n        enum value = s[0] ~ parseUinteger!(s[1..$]).value;\n        enum rest = parseUinteger!(s[1..$]).rest;\n    }\n    else static if (s.length >= 2 &&\n            s[0] == '-' && s[1] >= '0' && s[1] <= '9')\n    {\n        enum value = s[0..2] ~ parseUinteger!(s[2..$]).value;\n        enum rest = parseUinteger!(s[2..$]).rest;\n    }\n    else\n    {\n        enum value = \"\";\n        enum rest = s;\n    }\n}\n\nunittest\n{\n    assert(parseUinteger!(\"1234abc\").value == \"1234\");\n    assert(parseUinteger!(\"1234abc\").rest == \"abc\");\n    assert(parseInteger!(\"-1234abc\").value == \"-1234\");\n    assert(parseInteger!(\"-1234abc\").rest == \"abc\");\n}\n\n/**\nDeprecated aliases held for backward compatibility.\n*/\ndeprecated alias toStringNow ToString;\n/// Ditto\ndeprecated alias parseUinteger ParseUinteger;\n/// Ditto\ndeprecated alias parseUinteger ParseInteger;\n\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        indentUnit: 4,\n        mode: \"text/x-d\"\n      });\n    </script>\n\n    <p>Simple mode that handle D-Syntax (<a href=\"http://www.dlang.org\">DLang Homepage</a>).</p>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-d</code>\n    .</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/d/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"d\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"nested_comments\",\n     \"[comment /+]\",\"[comment comment]\",\"[comment +/]\",\"[variable void] [variable main](){}\");\n\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/dart/dart.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../clike/clike\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../clike/clike\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var keywords = (\"this super static final const abstract class extends external factory \" +\n    \"implements mixin get native set typedef with enum throw rethrow assert break case \" +\n    \"continue default in return new deferred async await covariant try catch finally \" +\n    \"do else for if switch while import library export part of show hide is as extension \" +\n    \"on yield late required sealed base interface when\").split(\" \");\n  var blockKeywords = \"try catch finally do else for if switch while\".split(\" \");\n  var atoms = \"true false null\".split(\" \");\n  var builtins = \"void bool num int double dynamic var String Null Never\".split(\" \");\n\n  function set(words) {\n    var obj = {};\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  function pushInterpolationStack(state) {\n    (state.interpolationStack || (state.interpolationStack = [])).push(state.tokenize);\n  }\n\n  function popInterpolationStack(state) {\n    return (state.interpolationStack || (state.interpolationStack = [])).pop();\n  }\n\n  function sizeInterpolationStack(state) {\n    return state.interpolationStack ? state.interpolationStack.length : 0;\n  }\n\n  CodeMirror.defineMIME(\"application/dart\", {\n    name: \"clike\",\n    keywords: set(keywords),\n    blockKeywords: set(blockKeywords),\n    builtin: set(builtins),\n    atoms: set(atoms),\n    hooks: {\n      \"@\": function(stream) {\n        stream.eatWhile(/[\\w\\$_\\.]/);\n        return \"meta\";\n      },\n\n      // custom string handling to deal with triple-quoted strings and string interpolation\n      \"'\": function(stream, state) {\n        return tokenString(\"'\", stream, state, false);\n      },\n      \"\\\"\": function(stream, state) {\n        return tokenString(\"\\\"\", stream, state, false);\n      },\n      \"r\": function(stream, state) {\n        var peek = stream.peek();\n        if (peek == \"'\" || peek == \"\\\"\") {\n          return tokenString(stream.next(), stream, state, true);\n        }\n        return false;\n      },\n\n      \"}\": function(_stream, state) {\n        // \"}\" is end of interpolation, if interpolation stack is non-empty\n        if (sizeInterpolationStack(state) > 0) {\n          state.tokenize = popInterpolationStack(state);\n          return null;\n        }\n        return false;\n      },\n\n      \"/\": function(stream, state) {\n        if (!stream.eat(\"*\")) return false\n        state.tokenize = tokenNestedComment(1)\n        return state.tokenize(stream, state)\n      },\n      token: function(stream, _, style) {\n        if (style == \"variable\") {\n          // Assume uppercase symbols are classes using variable-2\n          var isUpper = RegExp('^[_$]*[A-Z][a-zA-Z0-9_$]*$','g');\n          if (isUpper.test(stream.current())) {\n            return 'variable-2';\n          }\n        }\n      }\n    }\n  });\n\n  function tokenString(quote, stream, state, raw) {\n    var tripleQuoted = false;\n    if (stream.eat(quote)) {\n      if (stream.eat(quote)) tripleQuoted = true;\n      else return \"string\"; //empty string\n    }\n    function tokenStringHelper(stream, state) {\n      var escaped = false;\n      while (!stream.eol()) {\n        if (!raw && !escaped && stream.peek() == \"$\") {\n          pushInterpolationStack(state);\n          state.tokenize = tokenInterpolation;\n          return \"string\";\n        }\n        var next = stream.next();\n        if (next == quote && !escaped && (!tripleQuoted || stream.match(quote + quote))) {\n          state.tokenize = null;\n          break;\n        }\n        escaped = !raw && !escaped && next == \"\\\\\";\n      }\n      return \"string\";\n    }\n    state.tokenize = tokenStringHelper;\n    return tokenStringHelper(stream, state);\n  }\n\n  function tokenInterpolation(stream, state) {\n    stream.eat(\"$\");\n    if (stream.eat(\"{\")) {\n      // let clike handle the content of ${...},\n      // we take over again when \"}\" appears (see hooks).\n      state.tokenize = null;\n    } else {\n      state.tokenize = tokenInterpolationIdentifier;\n    }\n    return null;\n  }\n\n  function tokenInterpolationIdentifier(stream, state) {\n    stream.eatWhile(/[\\w_]/);\n    state.tokenize = popInterpolationStack(state);\n    return \"variable\";\n  }\n\n  function tokenNestedComment(depth) {\n    return function (stream, state) {\n      var ch\n      while (ch = stream.next()) {\n        if (ch == \"*\" && stream.eat(\"/\")) {\n          if (depth == 1) {\n            state.tokenize = null\n            break\n          } else {\n            state.tokenize = tokenNestedComment(depth - 1)\n            return state.tokenize(stream, state)\n          }\n        } else if (ch == \"/\" && stream.eat(\"*\")) {\n          state.tokenize = tokenNestedComment(depth + 1)\n          return state.tokenize(stream, state)\n        }\n      }\n      return \"comment\"\n    }\n  }\n\n  CodeMirror.registerHelper(\"hintWords\", \"application/dart\", keywords.concat(atoms).concat(builtins));\n\n  // This is needed to make loading through meta.js work.\n  CodeMirror.defineMode(\"dart\", function(conf) {\n    return CodeMirror.getMode(conf, \"application/dart\");\n  }, \"clike\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/dart/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Dart mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../clike/clike.js\"></script>\n<script src=\"dart.js\"></script>\n<style>.CodeMirror {border: 1px solid #dee;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Dart</a>\n  </ul>\n</div>\n\n<article>\n<h2>Dart mode</h2>\n<form>\n<textarea id=\"code\" name=\"code\">\nimport 'dart:math' show Random;\n\nvoid main() {\n  print(Die(n: 12).roll());\n}\n\n// Define a class.\nclass Die {\n  // Define a class variable.\n  static final Random shaker = Random();\n\n  // Define instance variables.\n  final int sides;\n  int? lastRoll;\n\n  // Define a constructor.\n  Die({int n = 6}) : sides = n {\n    if (4 > n || n > 20) {\n      // Support for errors and exceptions.\n      throw ArgumentError(/* */);\n    }\n  }\n\n  // Define a method using shorthand syntax.\n  @override\n  String toString() => '$lastRoll';\n\n  // Define an instance method.\n  int roll() {\n    return lastRoll = shaker.nextInt(sides) + 1;\n  }\n}\n</textarea>\n</form>\n\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    lineNumbers: true,\n    mode: \"application/dart\"\n  });\n</script>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/diff/diff.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"diff\", function() {\n\n  var TOKEN_NAMES = {\n    '+': 'positive',\n    '-': 'negative',\n    '@': 'meta'\n  };\n\n  return {\n    token: function(stream) {\n      var tw_pos = stream.string.search(/[\\t ]+?$/);\n\n      if (!stream.sol() || tw_pos === 0) {\n        stream.skipToEnd();\n        return (\"error \" + (\n          TOKEN_NAMES[stream.string.charAt(0)] || '')).replace(/ $/, '');\n      }\n\n      var token_name = TOKEN_NAMES[stream.peek()] || stream.skipToEnd();\n\n      if (tw_pos === -1) {\n        stream.skipToEnd();\n      } else {\n        stream.pos = tw_pos;\n      }\n\n      return token_name;\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-diff\", \"diff\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/diff/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Diff mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"diff.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid #ddd; border-bottom: 1px solid #ddd;}\n      span.cm-meta {color: #a0b !important;}\n      span.cm-error { background-color: black; opacity: 0.4;}\n      span.cm-error.cm-string { background-color: red; }\n      span.cm-error.cm-tag { background-color: #2b2; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Diff</a>\n  </ul>\n</div>\n\n<article>\n<h2>Diff mode</h2>\n<form><textarea id=\"code\" name=\"code\">\ndiff --git a/index.html b/index.html\nindex c1d9156..7764744 100644\n--- a/index.html\n+++ b/index.html\n@@ -95,7 +95,8 @@ StringStream.prototype = {\n     <script>\n       var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n         lineNumbers: true,\n-        autoMatchBrackets: true\n+        autoMatchBrackets: true,\n+      onGutterClick: function(x){console.log(x);}\n       });\n     </script>\n   </body>\ndiff --git a/lib/codemirror.js b/lib/codemirror.js\nindex 04646a9..9a39cc7 100644\n--- a/lib/codemirror.js\n+++ b/lib/codemirror.js\n@@ -399,10 +399,16 @@ var CodeMirror = (function() {\n     }\n \n     function onMouseDown(e) {\n-      var start = posFromMouse(e), last = start;    \n+      var start = posFromMouse(e), last = start, target = e.target();\n       if (!start) return;\n       setCursor(start.line, start.ch, false);\n       if (e.button() != 1) return;\n+      if (target.parentNode == gutter) {    \n+        if (options.onGutterClick)\n+          options.onGutterClick(indexOf(gutter.childNodes, target) + showingFrom);\n+        return;\n+      }\n+\n       if (!focused) onFocus();\n \n       e.stop();\n@@ -808,7 +814,7 @@ var CodeMirror = (function() {\n       for (var i = showingFrom; i < showingTo; ++i) {\n         var marker = lines[i].gutterMarker;\n         if (marker) html.push('<div class=\"' + marker.style + '\">' + htmlEscape(marker.text) + '</div>');\n-        else html.push(\"<div>\" + (options.lineNumbers ? i + 1 : \"\\u00a0\") + \"</div>\");\n+        else html.push(\"<div>\" + (options.lineNumbers ? i + options.firstLineNumber : \"\\u00a0\") + \"</div>\");\n       }\n       gutter.style.display = \"none\"; // TODO test whether this actually helps\n       gutter.innerHTML = html.join(\"\");\n@@ -1371,10 +1377,8 @@ var CodeMirror = (function() {\n         if (option == \"parser\") setParser(value);\n         else if (option === \"lineNumbers\") setLineNumbers(value);\n         else if (option === \"gutter\") setGutter(value);\n-        else if (option === \"readOnly\") options.readOnly = value;\n-        else if (option === \"indentUnit\") {options.indentUnit = indentUnit = value; setParser(options.parser);}\n-        else if (/^(?:enterMode|tabMode|indentWithTabs|readOnly|autoMatchBrackets|undoDepth)$/.test(option)) options[option] = value;\n-        else throw new Error(\"Can't set option \" + option);\n+        else if (option === \"indentUnit\") {options.indentUnit = value; setParser(options.parser);}\n+        else options[option] = value;\n       },\n       cursorCoords: cursorCoords,\n       undo: operation(undo),\n@@ -1402,7 +1406,8 @@ var CodeMirror = (function() {\n       replaceRange: operation(replaceRange),\n \n       operation: function(f){return operation(f)();},\n-      refresh: function(){updateDisplay([{from: 0, to: lines.length}]);}\n+      refresh: function(){updateDisplay([{from: 0, to: lines.length}]);},\n+      getInputField: function(){return input;}\n     };\n     return instance;\n   }\n@@ -1420,6 +1425,7 @@ var CodeMirror = (function() {\n     readOnly: false,\n     onChange: null,\n     onCursorActivity: null,\n+    onGutterClick: null,\n     autoMatchBrackets: false,\n     workTime: 200,\n     workDelay: 300,\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-diff</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/django/django.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../htmlmixed/htmlmixed\"),\n        require(\"../../addon/mode/overlay\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../htmlmixed/htmlmixed\",\n            \"../../addon/mode/overlay\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"django:inner\", function() {\n    var keywords = [\"block\", \"endblock\", \"for\", \"endfor\", \"true\", \"false\", \"filter\", \"endfilter\",\n                    \"loop\", \"none\", \"self\", \"super\", \"if\", \"elif\", \"endif\", \"as\", \"else\", \"import\",\n                    \"with\", \"endwith\", \"without\", \"context\", \"ifequal\", \"endifequal\", \"ifnotequal\",\n                    \"endifnotequal\", \"extends\", \"include\", \"load\", \"comment\", \"endcomment\",\n                    \"empty\", \"url\", \"static\", \"trans\", \"blocktrans\", \"endblocktrans\", \"now\",\n                    \"regroup\", \"lorem\", \"ifchanged\", \"endifchanged\", \"firstof\", \"debug\", \"cycle\",\n                    \"csrf_token\", \"autoescape\", \"endautoescape\", \"spaceless\", \"endspaceless\",\n                    \"ssi\", \"templatetag\", \"verbatim\", \"endverbatim\", \"widthratio\"],\n        filters = [\"add\", \"addslashes\", \"capfirst\", \"center\", \"cut\", \"date\",\n                   \"default\", \"default_if_none\", \"dictsort\",\n                   \"dictsortreversed\", \"divisibleby\", \"escape\", \"escapejs\",\n                   \"filesizeformat\", \"first\", \"floatformat\", \"force_escape\",\n                   \"get_digit\", \"iriencode\", \"join\", \"last\", \"length\",\n                   \"length_is\", \"linebreaks\", \"linebreaksbr\", \"linenumbers\",\n                   \"ljust\", \"lower\", \"make_list\", \"phone2numeric\", \"pluralize\",\n                   \"pprint\", \"random\", \"removetags\", \"rjust\", \"safe\",\n                   \"safeseq\", \"slice\", \"slugify\", \"stringformat\", \"striptags\",\n                   \"time\", \"timesince\", \"timeuntil\", \"title\", \"truncatechars\",\n                   \"truncatechars_html\", \"truncatewords\", \"truncatewords_html\",\n                   \"unordered_list\", \"upper\", \"urlencode\", \"urlize\",\n                   \"urlizetrunc\", \"wordcount\", \"wordwrap\", \"yesno\"],\n        operators = [\"==\", \"!=\", \"<\", \">\", \"<=\", \">=\"],\n        wordOperators = [\"in\", \"not\", \"or\", \"and\"];\n\n    keywords = new RegExp(\"^\\\\b(\" + keywords.join(\"|\") + \")\\\\b\");\n    filters = new RegExp(\"^\\\\b(\" + filters.join(\"|\") + \")\\\\b\");\n    operators = new RegExp(\"^\\\\b(\" + operators.join(\"|\") + \")\\\\b\");\n    wordOperators = new RegExp(\"^\\\\b(\" + wordOperators.join(\"|\") + \")\\\\b\");\n\n    // We have to return \"null\" instead of null, in order to avoid string\n    // styling as the default, when using Django templates inside HTML\n    // element attributes\n    function tokenBase (stream, state) {\n      // Attempt to identify a variable, template or comment tag respectively\n      if (stream.match(\"{{\")) {\n        state.tokenize = inVariable;\n        return \"tag\";\n      } else if (stream.match(\"{%\")) {\n        state.tokenize = inTag;\n        return \"tag\";\n      } else if (stream.match(\"{#\")) {\n        state.tokenize = inComment;\n        return \"comment\";\n      }\n\n      // Ignore completely any stream series that do not match the\n      // Django template opening tags.\n      while (stream.next() != null && !stream.match(/\\{[{%#]/, false)) {}\n      return null;\n    }\n\n    // A string can be included in either single or double quotes (this is\n    // the delimiter). Mark everything as a string until the start delimiter\n    // occurs again.\n    function inString (delimiter, previousTokenizer) {\n      return function (stream, state) {\n        if (!state.escapeNext && stream.eat(delimiter)) {\n          state.tokenize = previousTokenizer;\n        } else {\n          if (state.escapeNext) {\n            state.escapeNext = false;\n          }\n\n          var ch = stream.next();\n\n          // Take into account the backslash for escaping characters, such as\n          // the string delimiter.\n          if (ch == \"\\\\\") {\n            state.escapeNext = true;\n          }\n        }\n\n        return \"string\";\n      };\n    }\n\n    // Apply Django template variable syntax highlighting\n    function inVariable (stream, state) {\n      // Attempt to match a dot that precedes a property\n      if (state.waitDot) {\n        state.waitDot = false;\n\n        if (stream.peek() != \".\") {\n          return \"null\";\n        }\n\n        // Dot followed by a non-word character should be considered an error.\n        if (stream.match(/\\.\\W+/)) {\n          return \"error\";\n        } else if (stream.eat(\".\")) {\n          state.waitProperty = true;\n          return \"null\";\n        } else {\n          throw Error (\"Unexpected error while waiting for property.\");\n        }\n      }\n\n      // Attempt to match a pipe that precedes a filter\n      if (state.waitPipe) {\n        state.waitPipe = false;\n\n        if (stream.peek() != \"|\") {\n          return \"null\";\n        }\n\n        // Pipe followed by a non-word character should be considered an error.\n        if (stream.match(/\\.\\W+/)) {\n          return \"error\";\n        } else if (stream.eat(\"|\")) {\n          state.waitFilter = true;\n          return \"null\";\n        } else {\n          throw Error (\"Unexpected error while waiting for filter.\");\n        }\n      }\n\n      // Highlight properties\n      if (state.waitProperty) {\n        state.waitProperty = false;\n        if (stream.match(/\\b(\\w+)\\b/)) {\n          state.waitDot = true;  // A property can be followed by another property\n          state.waitPipe = true;  // A property can be followed by a filter\n          return \"property\";\n        }\n      }\n\n      // Highlight filters\n      if (state.waitFilter) {\n          state.waitFilter = false;\n        if (stream.match(filters)) {\n          return \"variable-2\";\n        }\n      }\n\n      // Ignore all white spaces\n      if (stream.eatSpace()) {\n        state.waitProperty = false;\n        return \"null\";\n      }\n\n      // Identify numbers\n      if (stream.match(/\\b\\d+(\\.\\d+)?\\b/)) {\n        return \"number\";\n      }\n\n      // Identify strings\n      if (stream.match(\"'\")) {\n        state.tokenize = inString(\"'\", state.tokenize);\n        return \"string\";\n      } else if (stream.match('\"')) {\n        state.tokenize = inString('\"', state.tokenize);\n        return \"string\";\n      }\n\n      // Attempt to find the variable\n      if (stream.match(/\\b(\\w+)\\b/) && !state.foundVariable) {\n        state.waitDot = true;\n        state.waitPipe = true;  // A property can be followed by a filter\n        return \"variable\";\n      }\n\n      // If found closing tag reset\n      if (stream.match(\"}}\")) {\n        state.waitProperty = null;\n        state.waitFilter = null;\n        state.waitDot = null;\n        state.waitPipe = null;\n        state.tokenize = tokenBase;\n        return \"tag\";\n      }\n\n      // If nothing was found, advance to the next character\n      stream.next();\n      return \"null\";\n    }\n\n    function inTag (stream, state) {\n      // Attempt to match a dot that precedes a property\n      if (state.waitDot) {\n        state.waitDot = false;\n\n        if (stream.peek() != \".\") {\n          return \"null\";\n        }\n\n        // Dot followed by a non-word character should be considered an error.\n        if (stream.match(/\\.\\W+/)) {\n          return \"error\";\n        } else if (stream.eat(\".\")) {\n          state.waitProperty = true;\n          return \"null\";\n        } else {\n          throw Error (\"Unexpected error while waiting for property.\");\n        }\n      }\n\n      // Attempt to match a pipe that precedes a filter\n      if (state.waitPipe) {\n        state.waitPipe = false;\n\n        if (stream.peek() != \"|\") {\n          return \"null\";\n        }\n\n        // Pipe followed by a non-word character should be considered an error.\n        if (stream.match(/\\.\\W+/)) {\n          return \"error\";\n        } else if (stream.eat(\"|\")) {\n          state.waitFilter = true;\n          return \"null\";\n        } else {\n          throw Error (\"Unexpected error while waiting for filter.\");\n        }\n      }\n\n      // Highlight properties\n      if (state.waitProperty) {\n        state.waitProperty = false;\n        if (stream.match(/\\b(\\w+)\\b/)) {\n          state.waitDot = true;  // A property can be followed by another property\n          state.waitPipe = true;  // A property can be followed by a filter\n          return \"property\";\n        }\n      }\n\n      // Highlight filters\n      if (state.waitFilter) {\n          state.waitFilter = false;\n        if (stream.match(filters)) {\n          return \"variable-2\";\n        }\n      }\n\n      // Ignore all white spaces\n      if (stream.eatSpace()) {\n        state.waitProperty = false;\n        return \"null\";\n      }\n\n      // Identify numbers\n      if (stream.match(/\\b\\d+(\\.\\d+)?\\b/)) {\n        return \"number\";\n      }\n\n      // Identify strings\n      if (stream.match(\"'\")) {\n        state.tokenize = inString(\"'\", state.tokenize);\n        return \"string\";\n      } else if (stream.match('\"')) {\n        state.tokenize = inString('\"', state.tokenize);\n        return \"string\";\n      }\n\n      // Attempt to match an operator\n      if (stream.match(operators)) {\n        return \"operator\";\n      }\n\n      // Attempt to match a word operator\n      if (stream.match(wordOperators)) {\n        return \"keyword\";\n      }\n\n      // Attempt to match a keyword\n      var keywordMatch = stream.match(keywords);\n      if (keywordMatch) {\n        if (keywordMatch[0] == \"comment\") {\n          state.blockCommentTag = true;\n        }\n        return \"keyword\";\n      }\n\n      // Attempt to match a variable\n      if (stream.match(/\\b(\\w+)\\b/)) {\n        state.waitDot = true;\n        state.waitPipe = true;  // A property can be followed by a filter\n        return \"variable\";\n      }\n\n      // If found closing tag reset\n      if (stream.match(\"%}\")) {\n        state.waitProperty = null;\n        state.waitFilter = null;\n        state.waitDot = null;\n        state.waitPipe = null;\n        // If the tag that closes is a block comment tag, we want to mark the\n        // following code as comment, until the tag closes.\n        if (state.blockCommentTag) {\n          state.blockCommentTag = false;  // Release the \"lock\"\n          state.tokenize = inBlockComment;\n        } else {\n          state.tokenize = tokenBase;\n        }\n        return \"tag\";\n      }\n\n      // If nothing was found, advance to the next character\n      stream.next();\n      return \"null\";\n    }\n\n    // Mark everything as comment inside the tag and the tag itself.\n    function inComment (stream, state) {\n      if (stream.match(/^.*?#\\}/)) state.tokenize = tokenBase\n      else stream.skipToEnd()\n      return \"comment\";\n    }\n\n    // Mark everything as a comment until the `blockcomment` tag closes.\n    function inBlockComment (stream, state) {\n      if (stream.match(/\\{%\\s*endcomment\\s*%\\}/, false)) {\n        state.tokenize = inTag;\n        stream.match(\"{%\");\n        return \"tag\";\n      } else {\n        stream.next();\n        return \"comment\";\n      }\n    }\n\n    return {\n      startState: function () {\n        return {tokenize: tokenBase};\n      },\n      token: function (stream, state) {\n        return state.tokenize(stream, state);\n      },\n      blockCommentStart: \"{% comment %}\",\n      blockCommentEnd: \"{% endcomment %}\"\n    };\n  });\n\n  CodeMirror.defineMode(\"django\", function(config) {\n    var htmlBase = CodeMirror.getMode(config, \"text/html\");\n    var djangoInner = CodeMirror.getMode(config, \"django:inner\");\n    return CodeMirror.overlayMode(htmlBase, djangoInner);\n  });\n\n  CodeMirror.defineMIME(\"text/x-django\", \"django\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/django/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Django template mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/mdn-like.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/mode/overlay.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../htmlmixed/htmlmixed.js\"></script>\n<script src=\"django.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Django</a>\n  </ul>\n</div>\n\n<article>\n<h2>Django template mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n<!doctype html>\n<html>\n  <head>\n    <title>My Django web application</title>\n  </head>\n  <body>\n    <h1>\n      {{ page.title|capfirst }}\n    </h1>\n    <ul class=\"my-list\">\n      {# traverse a list of items and produce links to their views. #}\n      {% for item in items %}\n      <li>\n        <a href=\"{% url 'item_view' item.name|slugify %}\">\n          {{ item.name }}\n        </a>\n      </li>\n      {% empty %}\n      <li>You have no items in your list.</li>\n      {% endfor %}\n    </ul>\n    {% comment \"this is a forgotten footer\" %}\n    <footer></footer>\n    {% endcomment %}\n  </body>\n</html>\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        mode: \"django\",\n        indentUnit: 2,\n        indentWithTabs: true,\n        theme: \"mdn-like\"\n      });\n    </script>\n\n    <p>Mode for HTML with embedded Django template markup.</p>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-django</code></p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/dockerfile/dockerfile.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../../addon/mode/simple\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../../addon/mode/simple\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var from = \"from\";\n  var fromRegex = new RegExp(\"^(\\\\s*)\\\\b(\" + from + \")\\\\b\", \"i\");\n\n  var shells = [\"run\", \"cmd\", \"entrypoint\", \"shell\"];\n  var shellsAsArrayRegex = new RegExp(\"^(\\\\s*)(\" + shells.join('|') + \")(\\\\s+\\\\[)\", \"i\");\n\n  var expose = \"expose\";\n  var exposeRegex = new RegExp(\"^(\\\\s*)(\" + expose + \")(\\\\s+)\", \"i\");\n\n  var others = [\n    \"arg\", \"from\", \"maintainer\", \"label\", \"env\",\n    \"add\", \"copy\", \"volume\", \"user\",\n    \"workdir\", \"onbuild\", \"stopsignal\", \"healthcheck\", \"shell\"\n  ];\n\n  // Collect all Dockerfile directives\n  var instructions = [from, expose].concat(shells).concat(others),\n      instructionRegex = \"(\" + instructions.join('|') + \")\",\n      instructionOnlyLine = new RegExp(\"^(\\\\s*)\" + instructionRegex + \"(\\\\s*)(#.*)?$\", \"i\"),\n      instructionWithArguments = new RegExp(\"^(\\\\s*)\" + instructionRegex + \"(\\\\s+)\", \"i\");\n\n  CodeMirror.defineSimpleMode(\"dockerfile\", {\n    start: [\n      // Block comment: This is a line starting with a comment\n      {\n        regex: /^\\s*#.*$/,\n        sol: true,\n        token: \"comment\"\n      },\n      {\n        regex: fromRegex,\n        token: [null, \"keyword\"],\n        sol: true,\n        next: \"from\"\n      },\n      // Highlight an instruction without any arguments (for convenience)\n      {\n        regex: instructionOnlyLine,\n        token: [null, \"keyword\", null, \"error\"],\n        sol: true\n      },\n      {\n        regex: shellsAsArrayRegex,\n        token: [null, \"keyword\", null],\n        sol: true,\n        next: \"array\"\n      },\n      {\n        regex: exposeRegex,\n        token: [null, \"keyword\", null],\n        sol: true,\n        next: \"expose\"\n      },\n      // Highlight an instruction followed by arguments\n      {\n        regex: instructionWithArguments,\n        token: [null, \"keyword\", null],\n        sol: true,\n        next: \"arguments\"\n      },\n      {\n        regex: /./,\n        token: null\n      }\n    ],\n    from: [\n      {\n        regex: /\\s*$/,\n        token: null,\n        next: \"start\"\n      },\n      {\n        // Line comment without instruction arguments is an error\n        regex: /(\\s*)(#.*)$/,\n        token: [null, \"error\"],\n        next: \"start\"\n      },\n      {\n        regex: /(\\s*\\S+\\s+)(as)/i,\n        token: [null, \"keyword\"],\n        next: \"start\"\n      },\n      // Fail safe return to start\n      {\n        token: null,\n        next: \"start\"\n      }\n    ],\n    single: [\n      {\n        regex: /(?:[^\\\\']|\\\\.)/,\n        token: \"string\"\n      },\n      {\n        regex: /'/,\n        token: \"string\",\n        pop: true\n      }\n    ],\n    double: [\n      {\n        regex: /(?:[^\\\\\"]|\\\\.)/,\n        token: \"string\"\n      },\n      {\n        regex: /\"/,\n        token: \"string\",\n        pop: true\n      }\n    ],\n    array: [\n      {\n        regex: /\\]/,\n        token: null,\n        next: \"start\"\n      },\n      {\n        regex: /\"(?:[^\\\\\"]|\\\\.)*\"?/,\n        token: \"string\"\n      }\n    ],\n    expose: [\n      {\n        regex: /\\d+$/,\n        token: \"number\",\n        next: \"start\"\n      },\n      {\n        regex: /[^\\d]+$/,\n        token: null,\n        next: \"start\"\n      },\n      {\n        regex: /\\d+/,\n        token: \"number\"\n      },\n      {\n        regex: /[^\\d]+/,\n        token: null\n      },\n      // Fail safe return to start\n      {\n        token: null,\n        next: \"start\"\n      }\n    ],\n    arguments: [\n      {\n        regex: /^\\s*#.*$/,\n        sol: true,\n        token: \"comment\"\n      },\n      {\n        regex: /\"(?:[^\\\\\"]|\\\\.)*\"?$/,\n        token: \"string\",\n        next: \"start\"\n      },\n      {\n        regex: /\"/,\n        token: \"string\",\n        push: \"double\"\n      },\n      {\n        regex: /'(?:[^\\\\']|\\\\.)*'?$/,\n        token: \"string\",\n        next: \"start\"\n      },\n      {\n        regex: /'/,\n        token: \"string\",\n        push: \"single\"\n      },\n      {\n        regex: /[^#\"']+[\\\\`]$/,\n        token: null\n      },\n      {\n        regex: /[^#\"']+$/,\n        token: null,\n        next: \"start\"\n      },\n      {\n        regex: /[^#\"']+/,\n        token: null\n      },\n      // Fail safe return to start\n      {\n        token: null,\n        next: \"start\"\n      }\n    ],\n    meta: {\n      lineComment: \"#\"\n    }\n  });\n\n  CodeMirror.defineMIME(\"text/x-dockerfile\", \"dockerfile\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/dockerfile/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Dockerfile mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/mode/simple.js\"></script>\n<script src=\"dockerfile.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Dockerfile</a>\n  </ul>\n</div>\n\n<article>\n<h2>Dockerfile mode</h2>\n<form><textarea id=\"code\" name=\"code\"># Install Ghost blogging platform and run development environment\n#\n# VERSION 1.0.0\n\nFROM ubuntu:12.10\nMAINTAINER Amer Grgic \"amer@livebyt.es\"\nWORKDIR /data/ghost\n\n# Install dependencies for nginx installation\nRUN apt-get update\nRUN apt-get install -y python g++ make software-properties-common --force-yes\nRUN add-apt-repository ppa:chris-lea/node.js\nRUN apt-get update\n# Install unzip\nRUN apt-get install -y unzip\n# Install curl\nRUN apt-get install -y curl\n# Install nodejs & npm\nRUN apt-get install -y rlwrap\nRUN apt-get install -y nodejs \n# Download Ghost v0.4.1\nRUN curl -L https://ghost.org/zip/ghost-latest.zip -o /tmp/ghost.zip\n# Unzip Ghost zip to /data/ghost\nRUN unzip -uo /tmp/ghost.zip -d /data/ghost\n# Add custom config js to /data/ghost\nADD ./config.example.js /data/ghost/config.js\n# Install Ghost with NPM\nRUN cd /data/ghost/ && npm install --production\n# Expose port 2368\nEXPOSE 2368\n# Run Ghost\nCMD [\"npm\",\"start\"]\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        mode: \"dockerfile\"\n      });\n    </script>\n\n    <p>Dockerfile syntax highlighting for CodeMirror. Depends on\n    the <a href=\"../../demo/simplemode.html\">simplemode</a> addon.</p>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-dockerfile</code></p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/dockerfile/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"text/x-dockerfile\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"simple_nodejs_dockerfile\",\n     \"[keyword FROM] node:carbon\",\n     \"[comment # Create app directory]\",\n     \"[keyword WORKDIR] /usr/src/app\",\n     \"[comment # Install app dependencies]\",\n     \"[comment # A wildcard is used to ensure both package.json AND package-lock.json are copied]\",\n     \"[comment # where available (npm@5+)]\",\n     \"[keyword COPY] package*.json ./\",\n     \"[keyword RUN] npm install\",\n     \"[keyword COPY] . .\",\n     \"[keyword EXPOSE] [number 8080] [number 3000]\",\n     \"[keyword ENV] NODE_ENV development\",\n     \"[keyword CMD] [[ [string \\\"npm\\\"], [string \\\"start\\\"] ]]\");\n\n  // Ideally the last space should not be highlighted.\n  MT(\"instruction_without_args_1\",\n     \"[keyword CMD] \");\n\n  MT(\"instruction_without_args_2\",\n     \"[comment # An instruction without args...]\",\n     \"[keyword ARG] [error #...is an error]\");\n\n  MT(\"multiline\",\n     \"[keyword RUN] apt-get update && apt-get install -y \\\\\",\n     \"  mercurial \\\\\",\n     \"  subversion \\\\\",\n     \"  && apt-get clean \\\\\",\n     \"  && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*\");\n\n  MT(\"from_comment\",\n     \"  [keyword FROM] debian:stretch # I tend to use stable as that is more stable\",\n     \"  [keyword FROM] debian:stretch [keyword AS] stable # I am even more stable\",\n     \" [keyword FROM] [error # this is an error]\");\n\n  MT(\"from_as\",\n     \"[keyword FROM] golang:1.9.2-alpine3.6 [keyword AS] build\",\n     \"[keyword COPY] --from=build /bin/project /bin/project\",\n     \"[keyword ENTRYPOINT] [[ [string \\\"/bin/project\\\"] ]]\",\n     \"[keyword CMD] [[ [string \\\"--help\\\"] ]]\");\n\n  MT(\"arg\",\n     \"[keyword ARG] VERSION=latest\",\n     \"[keyword FROM] busybox:$VERSION\",\n     \"[keyword ARG] VERSION\",\n     \"[keyword RUN] echo $VERSION > image_version\");\n\n  MT(\"label\",\n     \"[keyword LABEL] com.example.label-with-value=[string \\\"foo\\\"]\");\n\n  MT(\"label_multiline\",\n     \"[keyword LABEL] description=[string \\\"This text illustrates ]\\\\\",\n     \"[string that label-values can span multiple lines.\\\"]\");\n\n  MT(\"maintainer\",\n     \"[keyword MAINTAINER] Foo Bar [string \\\"foo@bar.com\\\"] \",\n     \"[keyword MAINTAINER] Bar Baz <bar@baz.com>\");\n\n  MT(\"env\",\n     \"[keyword ENV] BUNDLE_PATH=[string \\\"$GEM_HOME\\\"] \\\\\",\n     \"  BUNDLE_APP_CONFIG=[string \\\"$GEM_HOME\\\"]\");\n\n  MT(\"verify_keyword\",\n     \"[keyword RUN] add-apt-repository ppa:chris-lea/node.js\");\n\n  MT(\"scripts\",\n     \"[comment # Set an entrypoint, to automatically install node modules]\",\n     \"[keyword ENTRYPOINT] [[ [string \\\"/bin/bash\\\"], [string \\\"-c\\\"], [string \\\"if [[ ! -d node_modules ]]; then npm install; fi; exec \\\\\\\"${@:0}\\\\\\\";\\\"] ]]\",\n     \"[keyword CMD] npm start\",\n     \"[keyword RUN] npm run build && \\\\\",\n     \"[comment # a comment between the shell commands]\",\n     \"  npm run test\");\n\n  MT(\"strings_single\",\n     \"[keyword FROM] buildpack-deps:stretch\",\n     \"[keyword RUN] { \\\\\",\n     \"        echo [string 'install: --no-document']; \\\\\",\n     \"        echo [string 'update: --no-document']; \\\\\",\n     \"    } >> /usr/local/etc/gemrc\");\n\n  MT(\"strings_single_multiline\",\n     \"[keyword RUN] set -ex \\\\\",\n     \"    \\\\\",\n     \"    && buildDeps=[string ' ]\\\\\",\n     \"[string        bison ]\\\\\",\n     \"[string        dpkg-dev ]\\\\\",\n     \"[string        libgdbm-dev ]\\\\\",\n     \"[string        ruby ]\\\\\",\n     \"[string    '] \\\\\",\n     \"    && apt-get update\");\n\n  MT(\"strings_single_multiline_2\",\n     \"[keyword RUN] echo [string 'say \\\\' ]\\\\\",\n     \"[string   it works'] \");\n\n  MT(\"strings_double\",\n     \"[keyword RUN] apt-get install -y --no-install-recommends $buildDeps \\\\\",\n     \" \\\\\",\n     \" && wget -O ruby.tar.xz [string \\\"https://cache.ruby-lang.org/pub/ruby/${RUBY_MAJOR%-rc}/ruby-$RUBY_VERSION.tar.xz\\\"] \\\\\",\n     \" && echo [string \\\"$RUBY_DOWNLOAD_SHA256 *ruby.tar.xz\\\"] | sha256sum -c - \");\n\n  MT(\"strings_double_multiline\",\n     \"[keyword RUN] echo [string \\\"say \\\\\\\" ]\\\\\",\n     \"[string   it works\\\"] \");\n\n  MT(\"escape\",\n     \"[comment # escape=`]\",\n     \"[keyword FROM] microsoft/windowsservercore\",\n     \"[keyword RUN] powershell.exe -Command `\",\n     \"    $ErrorActionPreference = [string 'Stop']; `\",\n     \"    wget https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile c:\\python-3.5.1.exe ; `\",\n     \"    Start-Process c:\\python-3.5.1.exe -ArgumentList [string '/quiet InstallAllUsers=1 PrependPath=1'] -Wait ; `\",\n     \"    Remove-Item c:\\python-3.5.1.exe -Force)\");\n\n  MT(\"escape_strings\",\n     \"[comment # escape=`]\",\n     \"[keyword FROM] python:3.6-windowsservercore [keyword AS] python\",\n     \"[keyword RUN] $env:PATH = [string 'C:\\\\Python;C:\\\\Python\\\\Scripts;{0}'] -f $env:PATH ; `\",\n     // It should not consider \\' as escaped.\n     // \"  Set-ItemProperty -Path [string 'HKLM:\\\\SYSTEM\\\\CurrentControlSet\\\\Control\\\\Session Manager\\\\Environment\\\\'] -Name Path -Value $env:PATH ;\");\n     \"  Set-ItemProperty -Path [string 'HKLM:\\\\SYSTEM\\\\CurrentControlSet\\\\Control\\\\Session Manager\\\\Environment\\\\' -Name Path -Value $env:PATH ;]\");\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/dtd/dtd.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/*\n  DTD mode\n  Ported to CodeMirror by Peter Kroon <plakroon@gmail.com>\n  Report bugs/issues here: https://github.com/codemirror/CodeMirror/issues\n  GitHub: @peterkroon\n*/\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"dtd\", function(config) {\n  var indentUnit = config.indentUnit, type;\n  function ret(style, tp) {type = tp; return style;}\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n\n    if (ch == \"<\" && stream.eat(\"!\") ) {\n      if (stream.eatWhile(/[\\-]/)) {\n        state.tokenize = tokenSGMLComment;\n        return tokenSGMLComment(stream, state);\n      } else if (stream.eatWhile(/[\\w]/)) return ret(\"keyword\", \"doindent\");\n    } else if (ch == \"<\" && stream.eat(\"?\")) { //xml declaration\n      state.tokenize = inBlock(\"meta\", \"?>\");\n      return ret(\"meta\", ch);\n    } else if (ch == \"#\" && stream.eatWhile(/[\\w]/)) return ret(\"atom\", \"tag\");\n    else if (ch == \"|\") return ret(\"keyword\", \"separator\");\n    else if (ch.match(/[\\(\\)\\[\\]\\-\\.,\\+\\?>]/)) return ret(null, ch);//if(ch === \">\") return ret(null, \"endtag\"); else\n    else if (ch.match(/[\\[\\]]/)) return ret(\"rule\", ch);\n    else if (ch == \"\\\"\" || ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    } else if (stream.eatWhile(/[a-zA-Z\\?\\+\\d]/)) {\n      var sc = stream.current();\n      if( sc.substr(sc.length-1,sc.length).match(/\\?|\\+/) !== null )stream.backUp(1);\n      return ret(\"tag\", \"tag\");\n    } else if (ch == \"%\" || ch == \"*\" ) return ret(\"number\", \"number\");\n    else {\n      stream.eatWhile(/[\\w\\\\\\-_%.{,]/);\n      return ret(null, null);\n    }\n  }\n\n  function tokenSGMLComment(stream, state) {\n    var dashes = 0, ch;\n    while ((ch = stream.next()) != null) {\n      if (dashes >= 2 && ch == \">\") {\n        state.tokenize = tokenBase;\n        break;\n      }\n      dashes = (ch == \"-\") ? dashes + 1 : 0;\n    }\n    return ret(\"comment\", \"comment\");\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, ch;\n      while ((ch = stream.next()) != null) {\n        if (ch == quote && !escaped) {\n          state.tokenize = tokenBase;\n          break;\n        }\n        escaped = !escaped && ch == \"\\\\\";\n      }\n      return ret(\"string\", \"tag\");\n    };\n  }\n\n  function inBlock(style, terminator) {\n    return function(stream, state) {\n      while (!stream.eol()) {\n        if (stream.match(terminator)) {\n          state.tokenize = tokenBase;\n          break;\n        }\n        stream.next();\n      }\n      return style;\n    };\n  }\n\n  return {\n    startState: function(base) {\n      return {tokenize: tokenBase,\n              baseIndent: base || 0,\n              stack: []};\n    },\n\n    token: function(stream, state) {\n      if (stream.eatSpace()) return null;\n      var style = state.tokenize(stream, state);\n\n      var context = state.stack[state.stack.length-1];\n      if (stream.current() == \"[\" || type === \"doindent\" || type == \"[\") state.stack.push(\"rule\");\n      else if (type === \"endtag\") state.stack[state.stack.length-1] = \"endtag\";\n      else if (stream.current() == \"]\" || type == \"]\" || (type == \">\" && context == \"rule\")) state.stack.pop();\n      else if (type == \"[\") state.stack.push(\"[\");\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      var n = state.stack.length;\n\n      if( textAfter.charAt(0) === ']' )n--;\n      else if(textAfter.substr(textAfter.length-1, textAfter.length) === \">\"){\n        if(textAfter.substr(0,1) === \"<\") {}\n        else if( type == \"doindent\" && textAfter.length > 1 ) {}\n        else if( type == \"doindent\")n--;\n        else if( type == \">\" && textAfter.length > 1) {}\n        else if( type == \"tag\" && textAfter !== \">\") {}\n        else if( type == \"tag\" && state.stack[state.stack.length-1] == \"rule\")n--;\n        else if( type == \"tag\")n++;\n        else if( textAfter === \">\" && state.stack[state.stack.length-1] == \"rule\" && type === \">\")n--;\n        else if( textAfter === \">\" && state.stack[state.stack.length-1] == \"rule\") {}\n        else if( textAfter.substr(0,1) !== \"<\" && textAfter.substr(0,1) === \">\" )n=n-1;\n        else if( textAfter === \">\") {}\n        else n=n-1;\n        //over rule them all\n        if(type == null || type == \"]\")n--;\n      }\n\n      return state.baseIndent + n * indentUnit;\n    },\n\n    electricChars: \"]>\"\n  };\n});\n\nCodeMirror.defineMIME(\"application/xml-dtd\", \"dtd\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/dtd/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: DTD mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"dtd.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">DTD</a>\n  </ul>\n</div>\n\n<article>\n<h2>DTD mode</h2>\n<form><textarea id=\"code\" name=\"code\"><?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!ATTLIST title\n  xmlns\tCDATA\t#FIXED\t\"http://docbook.org/ns/docbook\"\n  role\tCDATA\t#IMPLIED\n  %db.common.attributes;\n  %db.common.linking.attributes;\n>\n\n<!--\n  Try: http://docbook.org/xml/5.0/dtd/docbook.dtd\n-->\n\n<!DOCTYPE xsl:stylesheet\n  [\n    <!ENTITY nbsp   \"&amp;#160;\">\n    <!ENTITY copy   \"&amp;#169;\">\n    <!ENTITY reg    \"&amp;#174;\">\n    <!ENTITY trade  \"&amp;#8482;\">\n    <!ENTITY mdash  \"&amp;#8212;\">\n    <!ENTITY ldquo  \"&amp;#8220;\">\n    <!ENTITY rdquo  \"&amp;#8221;\">\n    <!ENTITY pound  \"&amp;#163;\">\n    <!ENTITY yen    \"&amp;#165;\">\n    <!ENTITY euro   \"&amp;#8364;\">\n    <!ENTITY mathml \"http://www.w3.org/1998/Math/MathML\">\n  ]\n>\n\n<!ELEMENT title (#PCDATA|inlinemediaobject|remark|superscript|subscript|xref|link|olink|anchor|biblioref|alt|annotation|indexterm|abbrev|acronym|date|emphasis|footnote|footnoteref|foreignphrase|phrase|quote|wordasword|firstterm|glossterm|coref|trademark|productnumber|productname|database|application|hardware|citation|citerefentry|citetitle|citebiblioid|author|person|personname|org|orgname|editor|jobtitle|replaceable|package|parameter|termdef|nonterminal|systemitem|option|optional|property|inlineequation|tag|markup|token|symbol|literal|code|constant|email|uri|guiicon|guibutton|guimenuitem|guimenu|guisubmenu|guilabel|menuchoice|mousebutton|keycombo|keycap|keycode|keysym|shortcut|accel|prompt|envar|filename|command|computeroutput|userinput|function|varname|returnvalue|type|classname|exceptionname|interfacename|methodname|modifier|initializer|ooclass|ooexception|oointerface|errorcode|errortext|errorname|errortype)*>\n\n<!ENTITY % db.common.attributes \"\n  xml:id\tID\t#IMPLIED\n  version\tCDATA\t#IMPLIED\n  xml:lang\tCDATA\t#IMPLIED\n  xml:base\tCDATA\t#IMPLIED\n  remap\tCDATA\t#IMPLIED\n  xreflabel\tCDATA\t#IMPLIED\n  revisionflag\t(changed|added|deleted|off)\t#IMPLIED\n  dir\t(ltr|rtl|lro|rlo)\t#IMPLIED\n  arch\tCDATA\t#IMPLIED\n  audience\tCDATA\t#IMPLIED\n  condition\tCDATA\t#IMPLIED\n  conformance\tCDATA\t#IMPLIED\n  os\tCDATA\t#IMPLIED\n  revision\tCDATA\t#IMPLIED\n  security\tCDATA\t#IMPLIED\n  userlevel\tCDATA\t#IMPLIED\n  vendor\tCDATA\t#IMPLIED\n  wordsize\tCDATA\t#IMPLIED\n  annotations\tCDATA\t#IMPLIED\n\n\"></textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: {name: \"dtd\", alignCDATA: true},\n        lineNumbers: true,\n        lineWrapping: true\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>application/xml-dtd</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/dylan/dylan.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nfunction forEach(arr, f) {\n  for (var i = 0; i < arr.length; i++) f(arr[i], i)\n}\nfunction some(arr, f) {\n  for (var i = 0; i < arr.length; i++) if (f(arr[i], i)) return true\n  return false\n}\n\nCodeMirror.defineMode(\"dylan\", function(_config) {\n  // Words\n  var words = {\n    // Words that introduce unnamed definitions like \"define interface\"\n    unnamedDefinition: [\"interface\"],\n\n    // Words that introduce simple named definitions like \"define library\"\n    namedDefinition: [\"module\", \"library\", \"macro\",\n                      \"C-struct\", \"C-union\",\n                      \"C-function\", \"C-callable-wrapper\"\n                     ],\n\n    // Words that introduce type definitions like \"define class\".\n    // These are also parameterized like \"define method\" and are\n    // appended to otherParameterizedDefinitionWords\n    typeParameterizedDefinition: [\"class\", \"C-subtype\", \"C-mapped-subtype\"],\n\n    // Words that introduce trickier definitions like \"define method\".\n    // These require special definitions to be added to startExpressions\n    otherParameterizedDefinition: [\"method\", \"function\",\n                                   \"C-variable\", \"C-address\"\n                                  ],\n\n    // Words that introduce module constant definitions.\n    // These must also be simple definitions and are\n    // appended to otherSimpleDefinitionWords\n    constantSimpleDefinition: [\"constant\"],\n\n    // Words that introduce module variable definitions.\n    // These must also be simple definitions and are\n    // appended to otherSimpleDefinitionWords\n    variableSimpleDefinition: [\"variable\"],\n\n    // Other words that introduce simple definitions\n    // (without implicit bodies).\n    otherSimpleDefinition: [\"generic\", \"domain\",\n                            \"C-pointer-type\",\n                            \"table\"\n                           ],\n\n    // Words that begin statements with implicit bodies.\n    statement: [\"if\", \"block\", \"begin\", \"method\", \"case\",\n                \"for\", \"select\", \"when\", \"unless\", \"until\",\n                \"while\", \"iterate\", \"profiling\", \"dynamic-bind\"\n               ],\n\n    // Patterns that act as separators in compound statements.\n    // This may include any general pattern that must be indented\n    // specially.\n    separator: [\"finally\", \"exception\", \"cleanup\", \"else\",\n                \"elseif\", \"afterwards\"\n               ],\n\n    // Keywords that do not require special indentation handling,\n    // but which should be highlighted\n    other: [\"above\", \"below\", \"by\", \"from\", \"handler\", \"in\",\n            \"instance\", \"let\", \"local\", \"otherwise\", \"slot\",\n            \"subclass\", \"then\", \"to\", \"keyed-by\", \"virtual\"\n           ],\n\n    // Condition signaling function calls\n    signalingCalls: [\"signal\", \"error\", \"cerror\",\n                     \"break\", \"check-type\", \"abort\"\n                    ]\n  };\n\n  words[\"otherDefinition\"] =\n    words[\"unnamedDefinition\"]\n    .concat(words[\"namedDefinition\"])\n    .concat(words[\"otherParameterizedDefinition\"]);\n\n  words[\"definition\"] =\n    words[\"typeParameterizedDefinition\"]\n    .concat(words[\"otherDefinition\"]);\n\n  words[\"parameterizedDefinition\"] =\n    words[\"typeParameterizedDefinition\"]\n    .concat(words[\"otherParameterizedDefinition\"]);\n\n  words[\"simpleDefinition\"] =\n    words[\"constantSimpleDefinition\"]\n    .concat(words[\"variableSimpleDefinition\"])\n    .concat(words[\"otherSimpleDefinition\"]);\n\n  words[\"keyword\"] =\n    words[\"statement\"]\n    .concat(words[\"separator\"])\n    .concat(words[\"other\"]);\n\n  // Patterns\n  var symbolPattern = \"[-_a-zA-Z?!*@<>$%]+\";\n  var symbol = new RegExp(\"^\" + symbolPattern);\n  var patterns = {\n    // Symbols with special syntax\n    symbolKeyword: symbolPattern + \":\",\n    symbolClass: \"<\" + symbolPattern + \">\",\n    symbolGlobal: \"\\\\*\" + symbolPattern + \"\\\\*\",\n    symbolConstant: \"\\\\$\" + symbolPattern\n  };\n  var patternStyles = {\n    symbolKeyword: \"atom\",\n    symbolClass: \"tag\",\n    symbolGlobal: \"variable-2\",\n    symbolConstant: \"variable-3\"\n  };\n\n  // Compile all patterns to regular expressions\n  for (var patternName in patterns)\n    if (patterns.hasOwnProperty(patternName))\n      patterns[patternName] = new RegExp(\"^\" + patterns[patternName]);\n\n  // Names beginning \"with-\" and \"without-\" are commonly\n  // used as statement macro\n  patterns[\"keyword\"] = [/^with(?:out)?-[-_a-zA-Z?!*@<>$%]+/];\n\n  var styles = {};\n  styles[\"keyword\"] = \"keyword\";\n  styles[\"definition\"] = \"def\";\n  styles[\"simpleDefinition\"] = \"def\";\n  styles[\"signalingCalls\"] = \"builtin\";\n\n  // protected words lookup table\n  var wordLookup = {};\n  var styleLookup = {};\n\n  forEach([\n    \"keyword\",\n    \"definition\",\n    \"simpleDefinition\",\n    \"signalingCalls\"\n  ], function(type) {\n    forEach(words[type], function(word) {\n      wordLookup[word] = type;\n      styleLookup[word] = styles[type];\n    });\n  });\n\n\n  function chain(stream, state, f) {\n    state.tokenize = f;\n    return f(stream, state);\n  }\n\n  function tokenBase(stream, state) {\n    // String\n    var ch = stream.peek();\n    if (ch == \"'\" || ch == '\"') {\n      stream.next();\n      return chain(stream, state, tokenString(ch, \"string\"));\n    }\n    // Comment\n    else if (ch == \"/\") {\n      stream.next();\n      if (stream.eat(\"*\")) {\n        return chain(stream, state, tokenComment);\n      } else if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n      stream.backUp(1);\n    }\n    // Decimal\n    else if (/[+\\-\\d\\.]/.test(ch)) {\n      if (stream.match(/^[+-]?[0-9]*\\.[0-9]*([esdx][+-]?[0-9]+)?/i) ||\n          stream.match(/^[+-]?[0-9]+([esdx][+-]?[0-9]+)/i) ||\n          stream.match(/^[+-]?\\d+/)) {\n        return \"number\";\n      }\n    }\n    // Hash\n    else if (ch == \"#\") {\n      stream.next();\n      // Symbol with string syntax\n      ch = stream.peek();\n      if (ch == '\"') {\n        stream.next();\n        return chain(stream, state, tokenString('\"', \"string\"));\n      }\n      // Binary number\n      else if (ch == \"b\") {\n        stream.next();\n        stream.eatWhile(/[01]/);\n        return \"number\";\n      }\n      // Hex number\n      else if (ch == \"x\") {\n        stream.next();\n        stream.eatWhile(/[\\da-f]/i);\n        return \"number\";\n      }\n      // Octal number\n      else if (ch == \"o\") {\n        stream.next();\n        stream.eatWhile(/[0-7]/);\n        return \"number\";\n      }\n      // Token concatenation in macros\n      else if (ch == '#') {\n        stream.next();\n        return \"punctuation\";\n      }\n      // Sequence literals\n      else if ((ch == '[') || (ch == '(')) {\n        stream.next();\n        return \"bracket\";\n      // Hash symbol\n      } else if (stream.match(/f|t|all-keys|include|key|next|rest/i)) {\n        return \"atom\";\n      } else {\n        stream.eatWhile(/[-a-zA-Z]/);\n        return \"error\";\n      }\n    } else if (ch == \"~\") {\n      stream.next();\n      ch = stream.peek();\n      if (ch == \"=\") {\n        stream.next();\n        ch = stream.peek();\n        if (ch == \"=\") {\n          stream.next();\n          return \"operator\";\n        }\n        return \"operator\";\n      }\n      return \"operator\";\n    } else if (ch == \":\") {\n      stream.next();\n      ch = stream.peek();\n      if (ch == \"=\") {\n        stream.next();\n        return \"operator\";\n      } else if (ch == \":\") {\n        stream.next();\n        return \"punctuation\";\n      }\n    } else if (\"[](){}\".indexOf(ch) != -1) {\n      stream.next();\n      return \"bracket\";\n    } else if (\".,\".indexOf(ch) != -1) {\n      stream.next();\n      return \"punctuation\";\n    } else if (stream.match(\"end\")) {\n      return \"keyword\";\n    }\n    for (var name in patterns) {\n      if (patterns.hasOwnProperty(name)) {\n        var pattern = patterns[name];\n        if ((pattern instanceof Array && some(pattern, function(p) {\n          return stream.match(p);\n        })) || stream.match(pattern))\n          return patternStyles[name];\n      }\n    }\n    if (/[+\\-*\\/^=<>&|]/.test(ch)) {\n      stream.next();\n      return \"operator\";\n    }\n    if (stream.match(\"define\")) {\n      return \"def\";\n    } else {\n      stream.eatWhile(/[\\w\\-]/);\n      // Keyword\n      if (wordLookup.hasOwnProperty(stream.current())) {\n        return styleLookup[stream.current()];\n      } else if (stream.current().match(symbol)) {\n        return \"variable\";\n      } else {\n        stream.next();\n        return \"variable-2\";\n      }\n    }\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, maybeNested = false, nestedCount = 0, ch;\n    while ((ch = stream.next())) {\n      if (ch == \"/\" && maybeEnd) {\n        if (nestedCount > 0) {\n          nestedCount--;\n        } else {\n          state.tokenize = tokenBase;\n          break;\n        }\n      } else if (ch == \"*\" && maybeNested) {\n        nestedCount++;\n      }\n      maybeEnd = (ch == \"*\");\n      maybeNested = (ch == \"/\");\n    }\n    return \"comment\";\n  }\n\n  function tokenString(quote, style) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {\n          end = true;\n          break;\n        }\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (end || !escaped) {\n        state.tokenize = tokenBase;\n      }\n      return style;\n    };\n  }\n\n  // Interface\n  return {\n    startState: function() {\n      return {\n        tokenize: tokenBase,\n        currentIndent: 0\n      };\n    },\n    token: function(stream, state) {\n      if (stream.eatSpace())\n        return null;\n      var style = state.tokenize(stream, state);\n      return style;\n    },\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-dylan\", \"dylan\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/dylan/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Dylan mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"../../addon/comment/continuecomment.js\"></script>\n<script src=\"../../addon/comment/comment.js\"></script>\n<script src=\"dylan.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Dylan</a>\n  </ul>\n</div>\n\n<article>\n<h2>Dylan mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\nModule:       locators-internals\nSynopsis:     Abstract modeling of locations\nAuthor:       Andy Armstrong\nCopyright:    Original Code is Copyright (c) 1995-2004 Functional Objects, Inc.\n              All rights reserved.\nLicense:      See License.txt in this distribution for details.\nWarranty:     Distributed WITHOUT WARRANTY OF ANY KIND\n\ndefine open generic locator-server\n    (locator :: <locator>) => (server :: false-or(<server-locator>));\ndefine open generic locator-host\n    (locator :: <locator>) => (host :: false-or(<string>));\ndefine open generic locator-volume\n    (locator :: <locator>) => (volume :: false-or(<string>));\ndefine open generic locator-directory\n    (locator :: <locator>) => (directory :: false-or(<directory-locator>));\ndefine open generic locator-relative?\n    (locator :: <locator>) => (relative? :: <boolean>);\ndefine open generic locator-path\n    (locator :: <locator>) => (path :: <sequence>);\ndefine open generic locator-base\n    (locator :: <locator>) => (base :: false-or(<string>));\ndefine open generic locator-extension\n    (locator :: <locator>) => (extension :: false-or(<string>));\n\n/// Locator classes\n\ndefine open abstract class <directory-locator> (<physical-locator>)\nend class <directory-locator>;\n\ndefine open abstract class <file-locator> (<physical-locator>)\nend class <file-locator>;\n\ndefine method as\n    (class == <directory-locator>, string :: <string>)\n => (locator :: <directory-locator>)\n  as(<native-directory-locator>, string)\nend method as;\n\ndefine method make\n    (class == <directory-locator>,\n     #key server :: false-or(<server-locator>) = #f,\n          path :: <sequence> = #[],\n          relative? :: <boolean> = #f,\n          name :: false-or(<string>) = #f)\n => (locator :: <directory-locator>)\n  make(<native-directory-locator>,\n       server:    server,\n       path:      path,\n       relative?: relative?,\n       name:      name)\nend method make;\n\ndefine method as\n    (class == <file-locator>, string :: <string>)\n => (locator :: <file-locator>)\n  as(<native-file-locator>, string)\nend method as;\n\ndefine method make\n    (class == <file-locator>,\n     #key directory :: false-or(<directory-locator>) = #f,\n          base :: false-or(<string>) = #f,\n          extension :: false-or(<string>) = #f,\n          name :: false-or(<string>) = #f)\n => (locator :: <file-locator>)\n  make(<native-file-locator>,\n       directory: directory,\n       base:      base,\n       extension: extension,\n       name:      name)\nend method make;\n\n/// Locator coercion\n\n//---*** andrewa: This caching scheme doesn't work yet, so disable it.\ndefine constant $cache-locators?        = #f;\ndefine constant $cache-locator-strings? = #f;\n\ndefine constant $locator-to-string-cache = make(<object-table>, weak: #\"key\");\ndefine constant $string-to-locator-cache = make(<string-table>, weak: #\"value\");\n\ndefine open generic locator-as-string\n    (class :: subclass(<string>), locator :: <locator>)\n => (string :: <string>);\n\ndefine open generic string-as-locator\n    (class :: subclass(<locator>), string :: <string>)\n => (locator :: <locator>);\n\ndefine sealed sideways method as\n    (class :: subclass(<string>), locator :: <locator>)\n => (string :: <string>)\n  let string = element($locator-to-string-cache, locator, default: #f);\n  if (string)\n    as(class, string)\n  else\n    let string = locator-as-string(class, locator);\n    if ($cache-locator-strings?)\n      element($locator-to-string-cache, locator) := string;\n    else\n      string\n    end\n  end\nend method as;\n\ndefine sealed sideways method as\n    (class :: subclass(<locator>), string :: <string>)\n => (locator :: <locator>)\n  let locator = element($string-to-locator-cache, string, default: #f);\n  if (instance?(locator, class))\n    locator\n  else\n    let locator = string-as-locator(class, string);\n    if ($cache-locators?)\n      element($string-to-locator-cache, string) := locator;\n    else\n      locator\n    end\n  end\nend method as;\n\n/// Locator conditions\n\ndefine class <locator-error> (<format-string-condition>, <error>)\nend class <locator-error>;\n\ndefine function locator-error\n    (format-string :: <string>, #rest format-arguments)\n  error(make(<locator-error>, \n             format-string:    format-string,\n             format-arguments: format-arguments))\nend function locator-error;\n\n/// Useful locator protocols\n\ndefine open generic locator-test\n    (locator :: <directory-locator>) => (test :: <function>);\n\ndefine method locator-test\n    (locator :: <directory-locator>) => (test :: <function>)\n  \\=\nend method locator-test;\n\ndefine open generic locator-might-have-links?\n    (locator :: <directory-locator>) => (links? :: <boolean>);\n\ndefine method locator-might-have-links?\n    (locator :: <directory-locator>) => (links? :: singleton(#f))\n  #f\nend method locator-might-have-links?;\n\ndefine method locator-relative?\n    (locator :: <file-locator>) => (relative? :: <boolean>)\n  let directory = locator.locator-directory;\n  ~directory | directory.locator-relative?\nend method locator-relative?;\n\ndefine method current-directory-locator?\n    (locator :: <directory-locator>) => (current-directory? :: <boolean>)\n  locator.locator-relative?\n    & locator.locator-path = #[#\"self\"]\nend method current-directory-locator?;\n\ndefine method locator-directory\n    (locator :: <directory-locator>) => (parent :: false-or(<directory-locator>))\n  let path = locator.locator-path;\n  unless (empty?(path))\n    make(object-class(locator),\n         server:    locator.locator-server,\n         path:      copy-sequence(path, end: path.size - 1),\n         relative?: locator.locator-relative?)\n  end\nend method locator-directory;\n\n/// Simplify locator\n\ndefine open generic simplify-locator\n    (locator :: <physical-locator>)\n => (simplified-locator :: <physical-locator>);\n\ndefine method simplify-locator\n    (locator :: <directory-locator>)\n => (simplified-locator :: <directory-locator>)\n  let path = locator.locator-path;\n  let relative? = locator.locator-relative?;\n  let resolve-parent? = ~locator.locator-might-have-links?;\n  let simplified-path\n    = simplify-path(path, \n                    resolve-parent?: resolve-parent?,\n                    relative?: relative?);\n  if (path ~= simplified-path)\n    make(object-class(locator),\n         server:    locator.locator-server,\n         path:      simplified-path,\n         relative?: locator.locator-relative?)\n  else\n    locator\n  end\nend method simplify-locator;\n\ndefine method simplify-locator\n    (locator :: <file-locator>) => (simplified-locator :: <file-locator>)\n  let directory = locator.locator-directory;\n  let simplified-directory = directory & simplify-locator(directory);\n  if (directory ~= simplified-directory)\n    make(object-class(locator),\n         directory: simplified-directory,\n         base:      locator.locator-base,\n         extension: locator.locator-extension)\n  else\n    locator\n  end\nend method simplify-locator;\n\n/// Subdirectory locator\n\ndefine open generic subdirectory-locator\n    (locator :: <directory-locator>, #rest sub-path)\n => (subdirectory :: <directory-locator>);\n\ndefine method subdirectory-locator\n    (locator :: <directory-locator>, #rest sub-path)\n => (subdirectory :: <directory-locator>)\n  let old-path = locator.locator-path;\n  let new-path = concatenate-as(<simple-object-vector>, old-path, sub-path);\n  make(object-class(locator),\n       server:    locator.locator-server,\n       path:      new-path,\n       relative?: locator.locator-relative?)\nend method subdirectory-locator;\n\n/// Relative locator\n\ndefine open generic relative-locator\n    (locator :: <physical-locator>, from-locator :: <physical-locator>)\n => (relative-locator :: <physical-locator>);\n\ndefine method relative-locator\n    (locator :: <directory-locator>, from-locator :: <directory-locator>)\n => (relative-locator :: <directory-locator>)\n  let path = locator.locator-path;\n  let from-path = from-locator.locator-path;\n  case\n    ~locator.locator-relative? & from-locator.locator-relative? =>\n      locator-error\n        (\"Cannot find relative path of absolute locator %= from relative locator %=\",\n         locator, from-locator);\n    locator.locator-server ~= from-locator.locator-server =>\n      locator;\n    path = from-path =>\n      make(object-class(locator),\n           path: vector(#\"self\"),\n           relative?: #t);\n    otherwise =>\n      make(object-class(locator),\n           path: relative-path(path, from-path, test: locator.locator-test),\n           relative?: #t);\n  end\nend method relative-locator;\n\ndefine method relative-locator\n    (locator :: <file-locator>, from-directory :: <directory-locator>)\n => (relative-locator :: <file-locator>)\n  let directory = locator.locator-directory;\n  let relative-directory = directory & relative-locator(directory, from-directory);\n  if (relative-directory ~= directory)\n    simplify-locator\n      (make(object-class(locator),\n            directory: relative-directory,\n            base:      locator.locator-base,\n            extension: locator.locator-extension))\n  else\n    locator\n  end\nend method relative-locator;\n\ndefine method relative-locator\n    (locator :: <physical-locator>, from-locator :: <file-locator>)\n => (relative-locator :: <physical-locator>)\n  let from-directory = from-locator.locator-directory;\n  case\n    from-directory =>\n      relative-locator(locator, from-directory);\n    ~locator.locator-relative? =>\n      locator-error\n        (\"Cannot find relative path of absolute locator %= from relative locator %=\",\n         locator, from-locator);\n    otherwise =>\n      locator;\n  end\nend method relative-locator;\n\n/// Merge locators\n\ndefine open generic merge-locators\n    (locator :: <physical-locator>, from-locator :: <physical-locator>)\n => (merged-locator :: <physical-locator>);\n\n/// Merge locators\n\ndefine method merge-locators\n    (locator :: <directory-locator>, from-locator :: <directory-locator>)\n => (merged-locator :: <directory-locator>)\n  if (locator.locator-relative?)\n    let path = concatenate(from-locator.locator-path, locator.locator-path);\n    simplify-locator\n      (make(object-class(locator),\n            server:    from-locator.locator-server,\n            path:      path,\n            relative?: from-locator.locator-relative?))\n  else\n    locator\n  end\nend method merge-locators;\n\ndefine method merge-locators\n    (locator :: <file-locator>, from-locator :: <directory-locator>)\n => (merged-locator :: <file-locator>)\n  let directory = locator.locator-directory;\n  let merged-directory \n    = if (directory)\n        merge-locators(directory, from-locator)\n      else\n        simplify-locator(from-locator)\n      end;\n  if (merged-directory ~= directory)\n    make(object-class(locator),\n         directory: merged-directory,\n         base:      locator.locator-base,\n         extension: locator.locator-extension)\n  else\n    locator\n  end\nend method merge-locators;\n\ndefine method merge-locators\n    (locator :: <physical-locator>, from-locator :: <file-locator>)\n => (merged-locator :: <physical-locator>)\n  let from-directory = from-locator.locator-directory;\n  if (from-directory)\n    merge-locators(locator, from-directory)\n  else\n    locator\n  end\nend method merge-locators;\n\n/// Locator protocols\n\ndefine sideways method supports-open-locator?\n    (locator :: <file-locator>) => (openable? :: <boolean>)\n  ~locator.locator-relative?\nend method supports-open-locator?;\n\ndefine sideways method open-locator\n    (locator :: <file-locator>, #rest keywords, #key, #all-keys)\n => (stream :: <stream>)\n  apply(open-file-stream, locator, keywords)\nend method open-locator;\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: \"text/x-dylan\",\n        lineNumbers: true,\n        matchBrackets: true,\n        continueComments: \"Enter\",\n        extraKeys: {\"Ctrl-Q\": \"toggleComment\"},\n        tabMode: \"indent\",\n        indentUnit: 2\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-dylan</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/dylan/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"dylan\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT('comments',\n     '[comment // This is a line comment]',\n     '[comment /* This is a block comment */]',\n     '[comment /* This is a multi]',\n     '[comment line comment]',\n     '[comment */]',\n     '[comment /* And this is a /*]',\n     '[comment /* nested */ comment */]');\n\n  MT('unary_operators',\n     '[operator -][variable a]',\n     '[operator -] [variable a]',\n     '[operator ~][variable a]',\n     '[operator ~] [variable a]');\n\n  MT('binary_operators',\n     '[variable a] [operator +] [variable b]',\n     '[variable a] [operator -] [variable b]',\n     '[variable a] [operator *] [variable b]',\n     '[variable a] [operator /] [variable b]',\n     '[variable a] [operator ^] [variable b]',\n     '[variable a] [operator =] [variable b]',\n     '[variable a] [operator ==] [variable b]',\n     '[variable a] [operator ~=] [variable b]',\n     '[variable a] [operator ~==] [variable b]',\n     '[variable a] [operator <] [variable b]',\n     '[variable a] [operator <=] [variable b]',\n     '[variable a] [operator >] [variable b]',\n     '[variable a] [operator >=] [variable b]',\n     '[variable a] [operator &] [variable b]',\n     '[variable a] [operator |] [variable b]',\n     '[variable a] [operator :=] [variable b]');\n\n  MT('integers',\n     '[number 1]',\n     '[number 123]',\n     '[number -123]',\n     '[number +456]',\n     '[number #b010]',\n     '[number #o073]',\n     '[number #xabcDEF123]');\n\n  MT('floats',\n     '[number .3]',\n     '[number -1.]',\n     '[number -2.335]',\n     '[number +3.78d1]',\n     '[number 3.78s-1]',\n     '[number -3.32e+5]');\n\n  MT('characters_and_strings',\n     \"[string 'a']\",\n     \"[string '\\\\\\\\'']\",\n     '[string \"\"]',\n     '[string \"a\"]',\n     '[string \"abc def\"]',\n     '[string \"More escaped characters: \\\\\\\\\\\\\\\\ \\\\\\\\a \\\\\\\\b \\\\\\\\e \\\\\\\\f \\\\\\\\n \\\\\\\\r \\\\\\\\t \\\\\\\\0 ...\"]');\n\n  MT('brackets',\n     '[bracket #[[]]]',\n     '[bracket #()]',\n     '[bracket #(][number 1][bracket )]',\n     '[bracket [[][number 1][punctuation ,] [number 3][bracket ]]]',\n     '[bracket ()]',\n     '[bracket {}]',\n     '[keyword if] [bracket (][variable foo][bracket )]',\n     '[bracket (][number 1][bracket )]',\n     '[bracket [[][number 1][bracket ]]]');\n\n  MT('hash_words',\n     '[punctuation ##]',\n     '[atom #f]', '[atom #F]',\n     '[atom #t]', '[atom #T]',\n     '[atom #all-keys]',\n     '[atom #include]',\n     '[atom #key]',\n     '[atom #next]',\n     '[atom #rest]',\n     '[string #\"foo\"]',\n     '[error #invalid]');\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ebnf/ebnf.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"ebnf\", function (config) {\n    var commentType = {slash: 0, parenthesis: 1};\n    var stateType = {comment: 0, _string: 1, characterClass: 2};\n    var bracesMode = null;\n\n    if (config.bracesMode)\n      bracesMode = CodeMirror.getMode(config, config.bracesMode);\n\n    return {\n      startState: function () {\n        return {\n          stringType: null,\n          commentType: null,\n          braced: 0,\n          lhs: true,\n          localState: null,\n          stack: [],\n          inDefinition: false\n        };\n      },\n      token: function (stream, state) {\n        if (!stream) return;\n\n        //check for state changes\n        if (state.stack.length === 0) {\n          //strings\n          if ((stream.peek() == '\"') || (stream.peek() == \"'\")) {\n            state.stringType = stream.peek();\n            stream.next(); // Skip quote\n            state.stack.unshift(stateType._string);\n          } else if (stream.match('/*')) { //comments starting with /*\n            state.stack.unshift(stateType.comment);\n            state.commentType = commentType.slash;\n          } else if (stream.match('(*')) { //comments starting with (*\n            state.stack.unshift(stateType.comment);\n            state.commentType = commentType.parenthesis;\n          }\n        }\n\n        //return state\n        //stack has\n        switch (state.stack[0]) {\n        case stateType._string:\n          while (state.stack[0] === stateType._string && !stream.eol()) {\n            if (stream.peek() === state.stringType) {\n              stream.next(); // Skip quote\n              state.stack.shift(); // Clear flag\n            } else if (stream.peek() === \"\\\\\") {\n              stream.next();\n              stream.next();\n            } else {\n              stream.match(/^.[^\\\\\\\"\\']*/);\n            }\n          }\n          return state.lhs ? \"property string\" : \"string\"; // Token style\n\n        case stateType.comment:\n          while (state.stack[0] === stateType.comment && !stream.eol()) {\n            if (state.commentType === commentType.slash && stream.match('*/')) {\n              state.stack.shift(); // Clear flag\n              state.commentType = null;\n            } else if (state.commentType === commentType.parenthesis && stream.match('*)')) {\n              state.stack.shift(); // Clear flag\n              state.commentType = null;\n            } else {\n              stream.match(/^.[^\\*]*/);\n            }\n          }\n          return \"comment\";\n\n        case stateType.characterClass:\n          while (state.stack[0] === stateType.characterClass && !stream.eol()) {\n            if (!(stream.match(/^[^\\]\\\\]+/) || stream.match('.'))) {\n              state.stack.shift();\n            }\n          }\n          return \"operator\";\n        }\n\n        var peek = stream.peek();\n\n        if (bracesMode !== null && (state.braced || peek === \"{\")) {\n          if (state.localState === null)\n            state.localState = CodeMirror.startState(bracesMode);\n\n          var token = bracesMode.token(stream, state.localState),\n          text = stream.current();\n\n          if (!token) {\n            for (var i = 0; i < text.length; i++) {\n              if (text[i] === \"{\") {\n                if (state.braced === 0) {\n                  token = \"matchingbracket\";\n                }\n                state.braced++;\n              } else if (text[i] === \"}\") {\n                state.braced--;\n                if (state.braced === 0) {\n                  token = \"matchingbracket\";\n                }\n              }\n            }\n          }\n          return token;\n        }\n\n        //no stack\n        switch (peek) {\n        case \"[\":\n          stream.next();\n          state.stack.unshift(stateType.characterClass);\n          return \"bracket\";\n        case \":\":\n        case \"|\":\n        case \";\":\n          stream.next();\n          return \"operator\";\n        case \"%\":\n          if (stream.match(\"%%\")) {\n            return \"header\";\n          } else if (stream.match(/[%][A-Za-z]+/)) {\n            return \"keyword\";\n          } else if (stream.match(/[%][}]/)) {\n            return \"matchingbracket\";\n          }\n          break;\n        case \"/\":\n          if (stream.match(/[\\/][A-Za-z]+/)) {\n          return \"keyword\";\n        }\n        case \"\\\\\":\n          if (stream.match(/[\\][a-z]+/)) {\n            return \"string-2\";\n          }\n        case \".\":\n          if (stream.match(\".\")) {\n            return \"atom\";\n          }\n        case \"*\":\n        case \"-\":\n        case \"+\":\n        case \"^\":\n          if (stream.match(peek)) {\n            return \"atom\";\n          }\n        case \"$\":\n          if (stream.match(\"$$\")) {\n            return \"builtin\";\n          } else if (stream.match(/[$][0-9]+/)) {\n            return \"variable-3\";\n          }\n        case \"<\":\n          if (stream.match(/<<[a-zA-Z_]+>>/)) {\n            return \"builtin\";\n          }\n        }\n\n        if (stream.match('//')) {\n          stream.skipToEnd();\n          return \"comment\";\n        } else if (stream.match('return')) {\n          return \"operator\";\n        } else if (stream.match(/^[a-zA-Z_][a-zA-Z0-9_]*/)) {\n          if (stream.match(/(?=[\\(.])/)) {\n            return \"variable\";\n          } else if (stream.match(/(?=[\\s\\n]*[:=])/)) {\n            return \"def\";\n          }\n          return \"variable-2\";\n        } else if ([\"[\", \"]\", \"(\", \")\"].indexOf(stream.peek()) != -1) {\n          stream.next();\n          return \"bracket\";\n        } else if (!stream.eatSpace()) {\n          stream.next();\n        }\n        return null;\n      }\n    };\n  });\n\n  CodeMirror.defineMIME(\"text/x-ebnf\", \"ebnf\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ebnf/index.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>CodeMirror: EBNF Mode</title>\n    <meta charset=\"utf-8\"/>\n    <link rel=stylesheet href=\"../../doc/docs.css\">\n\n    <link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n    <script src=\"../../lib/codemirror.js\"></script>\n    <script src=\"../javascript/javascript.js\"></script>\n    <script src=\"ebnf.js\"></script>\n    <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n  </head>\n  <body>\n    <div id=nav>\n      <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n      <ul>\n        <li><a href=\"../../index.html\">Home</a>\n        <li><a href=\"../../doc/manual.html\">Manual</a>\n        <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n      </ul>\n      <ul>\n        <li><a href=\"../index.html\">Language modes</a>\n        <li><a class=active href=\"#\">EBNF Mode</a>\n      </ul>\n    </div>\n\n    <article>\n      <h2>EBNF Mode (bracesMode setting = \"javascript\")</h2>\n      <form><textarea id=\"code\" name=\"code\">\n/* description: Parses end executes mathematical expressions. */\n\n/* lexical grammar */\n%lex\n\n%%\n\\s+                   /* skip whitespace */\n[0-9]+(\".\"[0-9]+)?\\b  return 'NUMBER';\n\"*\"                   return '*';\n\"/\"                   return '/';\n\"-\"                   return '-';\n\"+\"                   return '+';\n\"^\"                   return '^';\n\"(\"                   return '(';\n\")\"                   return ')';\n\"PI\"                  return 'PI';\n\"E\"                   return 'E';\n&lt;&lt;EOF&gt;&gt;               return 'EOF';\n\n/lex\n\n/* operator associations and precedence */\n\n%left '+' '-'\n%left '*' '/'\n%left '^'\n%left UMINUS\n\n%start expressions\n\n%% /* language grammar */\n\nexpressions\n: e EOF\n{print($1); return $1;}\n;\n\ne\n: e '+' e\n{$$ = $1+$3;}\n| e '-' e\n{$$ = $1-$3;}\n| e '*' e\n{$$ = $1*$3;}\n| e '/' e\n{$$ = $1/$3;}\n| e '^' e\n{$$ = Math.pow($1, $3);}\n| '-' e %prec UMINUS\n{$$ = -$2;}\n| '(' e ')'\n{$$ = $2;}\n| NUMBER\n{$$ = Number(yytext);}\n| E\n{$$ = Math.E;}\n| PI\n{$$ = Math.PI;}\n;</textarea></form>\n      <script>\n        var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n          mode: {name: \"ebnf\"},\n          lineNumbers: true,\n          bracesMode: 'javascript'\n        });\n      </script>\n      <h3>The EBNF Mode</h3>\n      <p> Created by <a href=\"https://github.com/robertleeplummerjr\">Robert Plummer</a></p>\n    </article>\n  </body>\n</html>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ecl/ecl.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"ecl\", function(config) {\n\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  function metaHook(stream, state) {\n    if (!state.startOfLine) return false;\n    stream.skipToEnd();\n    return \"meta\";\n  }\n\n  var indentUnit = config.indentUnit;\n  var keyword = words(\"abs acos allnodes ascii asin asstring atan atan2 ave case choose choosen choosesets clustersize combine correlation cos cosh count covariance cron dataset dedup define denormalize distribute distributed distribution ebcdic enth error evaluate event eventextra eventname exists exp failcode failmessage fetch fromunicode getisvalid global graph group hash hash32 hash64 hashcrc hashmd5 having if index intformat isvalid iterate join keyunicode length library limit ln local log loop map matched matchlength matchposition matchtext matchunicode max merge mergejoin min nolocal nonempty normalize parse pipe power preload process project pull random range rank ranked realformat recordof regexfind regexreplace regroup rejected rollup round roundup row rowdiff sample set sin sinh sizeof soapcall sort sorted sqrt stepped stored sum table tan tanh thisnode topn tounicode transfer trim truncate typeof ungroup unicodeorder variance which workunit xmldecode xmlencode xmltext xmlunicode\");\n  var variable = words(\"apply assert build buildindex evaluate fail keydiff keypatch loadxml nothor notify output parallel sequential soapcall wait\");\n  var variable_2 = words(\"__compressed__ all and any as atmost before beginc++ best between case const counter csv descend encrypt end endc++ endmacro except exclusive expire export extend false few first flat from full function group header heading hole ifblock import in interface joined keep keyed last left limit load local locale lookup macro many maxcount maxlength min skew module named nocase noroot noscan nosort not of only opt or outer overwrite packed partition penalty physicallength pipe quote record relationship repeat return right scan self separator service shared skew skip sql store terminator thor threshold token transform trim true type unicodeorder unsorted validate virtual whole wild within xml xpath\");\n  var variable_3 = words(\"ascii big_endian boolean data decimal ebcdic integer pattern qstring real record rule set of string token udecimal unicode unsigned varstring varunicode\");\n  var builtin = words(\"checkpoint deprecated failcode failmessage failure global independent onwarning persist priority recovery stored success wait when\");\n  var blockKeywords = words(\"catch class do else finally for if switch try while\");\n  var atoms = words(\"true false null\");\n  var hooks = {\"#\": metaHook};\n  var isOperatorChar = /[+\\-*&%=<>!?|\\/]/;\n\n  var curPunc;\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (hooks[ch]) {\n      var result = hooks[ch](stream, state);\n      if (result !== false) return result;\n    }\n    if (ch == '\"' || ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    }\n    if (/[\\[\\]{}\\(\\),;\\:\\.]/.test(ch)) {\n      curPunc = ch;\n      return null;\n    }\n    if (/\\d/.test(ch)) {\n      stream.eatWhile(/[\\w\\.]/);\n      return \"number\";\n    }\n    if (ch == \"/\") {\n      if (stream.eat(\"*\")) {\n        state.tokenize = tokenComment;\n        return tokenComment(stream, state);\n      }\n      if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n    }\n    if (isOperatorChar.test(ch)) {\n      stream.eatWhile(isOperatorChar);\n      return \"operator\";\n    }\n    stream.eatWhile(/[\\w\\$_]/);\n    var cur = stream.current().toLowerCase();\n    if (keyword.propertyIsEnumerable(cur)) {\n      if (blockKeywords.propertyIsEnumerable(cur)) curPunc = \"newstatement\";\n      return \"keyword\";\n    } else if (variable.propertyIsEnumerable(cur)) {\n      if (blockKeywords.propertyIsEnumerable(cur)) curPunc = \"newstatement\";\n      return \"variable\";\n    } else if (variable_2.propertyIsEnumerable(cur)) {\n      if (blockKeywords.propertyIsEnumerable(cur)) curPunc = \"newstatement\";\n      return \"variable-2\";\n    } else if (variable_3.propertyIsEnumerable(cur)) {\n      if (blockKeywords.propertyIsEnumerable(cur)) curPunc = \"newstatement\";\n      return \"variable-3\";\n    } else if (builtin.propertyIsEnumerable(cur)) {\n      if (blockKeywords.propertyIsEnumerable(cur)) curPunc = \"newstatement\";\n      return \"builtin\";\n    } else { //Data types are of from KEYWORD##\n                var i = cur.length - 1;\n                while(i >= 0 && (!isNaN(cur[i]) || cur[i] == '_'))\n                        --i;\n\n                if (i > 0) {\n                        var cur2 = cur.substr(0, i + 1);\n                if (variable_3.propertyIsEnumerable(cur2)) {\n                        if (blockKeywords.propertyIsEnumerable(cur2)) curPunc = \"newstatement\";\n                        return \"variable-3\";\n                }\n            }\n    }\n    if (atoms.propertyIsEnumerable(cur)) return \"atom\";\n    return null;\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {end = true; break;}\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (end || !escaped)\n        state.tokenize = tokenBase;\n      return \"string\";\n    };\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n\n  function Context(indented, column, type, align, prev) {\n    this.indented = indented;\n    this.column = column;\n    this.type = type;\n    this.align = align;\n    this.prev = prev;\n  }\n  function pushContext(state, col, type) {\n    return state.context = new Context(state.indented, col, type, null, state.context);\n  }\n  function popContext(state) {\n    var t = state.context.type;\n    if (t == \")\" || t == \"]\" || t == \"}\")\n      state.indented = state.context.indented;\n    return state.context = state.context.prev;\n  }\n\n  // Interface\n\n  return {\n    startState: function(basecolumn) {\n      return {\n        tokenize: null,\n        context: new Context((basecolumn || 0) - indentUnit, 0, \"top\", false),\n        indented: 0,\n        startOfLine: true\n      };\n    },\n\n    token: function(stream, state) {\n      var ctx = state.context;\n      if (stream.sol()) {\n        if (ctx.align == null) ctx.align = false;\n        state.indented = stream.indentation();\n        state.startOfLine = true;\n      }\n      if (stream.eatSpace()) return null;\n      curPunc = null;\n      var style = (state.tokenize || tokenBase)(stream, state);\n      if (style == \"comment\" || style == \"meta\") return style;\n      if (ctx.align == null) ctx.align = true;\n\n      if ((curPunc == \";\" || curPunc == \":\") && ctx.type == \"statement\") popContext(state);\n      else if (curPunc == \"{\") pushContext(state, stream.column(), \"}\");\n      else if (curPunc == \"[\") pushContext(state, stream.column(), \"]\");\n      else if (curPunc == \"(\") pushContext(state, stream.column(), \")\");\n      else if (curPunc == \"}\") {\n        while (ctx.type == \"statement\") ctx = popContext(state);\n        if (ctx.type == \"}\") ctx = popContext(state);\n        while (ctx.type == \"statement\") ctx = popContext(state);\n      }\n      else if (curPunc == ctx.type) popContext(state);\n      else if (ctx.type == \"}\" || ctx.type == \"top\" || (ctx.type == \"statement\" && curPunc == \"newstatement\"))\n        pushContext(state, stream.column(), \"statement\");\n      state.startOfLine = false;\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize != tokenBase && state.tokenize != null) return 0;\n      var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);\n      if (ctx.type == \"statement\" && firstChar == \"}\") ctx = ctx.prev;\n      var closing = firstChar == ctx.type;\n      if (ctx.type == \"statement\") return ctx.indented + (firstChar == \"{\" ? 0 : indentUnit);\n      else if (ctx.align) return ctx.column + (closing ? 0 : 1);\n      else return ctx.indented + (closing ? 0 : indentUnit);\n    },\n\n    electricChars: \"{}\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-ecl\", \"ecl\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ecl/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: ECL mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"ecl.js\"></script>\n<style>.CodeMirror {border: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">ECL</a>\n  </ul>\n</div>\n\n<article>\n<h2>ECL mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n/*\nsample useless code to demonstrate ecl syntax highlighting\nthis is a multiline comment!\n*/\n\n//  this is a singleline comment!\n\nimport ut;\nr := \n  record\n   string22 s1 := '123';\n   integer4 i1 := 123;\n  end;\n#option('tmp', true);\nd := dataset('tmp::qb', r, thor);\noutput(d);\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p>Based on CodeMirror's clike mode.  For more information see <a href=\"http://hpccsystems.com\">HPCC Systems</a> web site.</p>\n    <p><strong>MIME types defined:</strong> <code>text/x-ecl</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/eiffel/eiffel.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"eiffel\", function() {\n  function wordObj(words) {\n    var o = {};\n    for (var i = 0, e = words.length; i < e; ++i) o[words[i]] = true;\n    return o;\n  }\n  var keywords = wordObj([\n    'note',\n    'across',\n    'when',\n    'variant',\n    'until',\n    'unique',\n    'undefine',\n    'then',\n    'strip',\n    'select',\n    'retry',\n    'rescue',\n    'require',\n    'rename',\n    'reference',\n    'redefine',\n    'prefix',\n    'once',\n    'old',\n    'obsolete',\n    'loop',\n    'local',\n    'like',\n    'is',\n    'inspect',\n    'infix',\n    'include',\n    'if',\n    'frozen',\n    'from',\n    'external',\n    'export',\n    'ensure',\n    'end',\n    'elseif',\n    'else',\n    'do',\n    'creation',\n    'create',\n    'check',\n    'alias',\n    'agent',\n    'separate',\n    'invariant',\n    'inherit',\n    'indexing',\n    'feature',\n    'expanded',\n    'deferred',\n    'class',\n    'Void',\n    'True',\n    'Result',\n    'Precursor',\n    'False',\n    'Current',\n    'create',\n    'attached',\n    'detachable',\n    'as',\n    'and',\n    'implies',\n    'not',\n    'or'\n  ]);\n  var operators = wordObj([\":=\", \"and then\",\"and\", \"or\",\"<<\",\">>\"]);\n\n  function chain(newtok, stream, state) {\n    state.tokenize.push(newtok);\n    return newtok(stream, state);\n  }\n\n  function tokenBase(stream, state) {\n    if (stream.eatSpace()) return null;\n    var ch = stream.next();\n    if (ch == '\"'||ch == \"'\") {\n      return chain(readQuoted(ch, \"string\"), stream, state);\n    } else if (ch == \"-\"&&stream.eat(\"-\")) {\n      stream.skipToEnd();\n      return \"comment\";\n    } else if (ch == \":\"&&stream.eat(\"=\")) {\n      return \"operator\";\n    } else if (/[0-9]/.test(ch)) {\n      stream.eatWhile(/[xXbBCc0-9\\.]/);\n      stream.eat(/[\\?\\!]/);\n      return \"ident\";\n    } else if (/[a-zA-Z_0-9]/.test(ch)) {\n      stream.eatWhile(/[a-zA-Z_0-9]/);\n      stream.eat(/[\\?\\!]/);\n      return \"ident\";\n    } else if (/[=+\\-\\/*^%<>~]/.test(ch)) {\n      stream.eatWhile(/[=+\\-\\/*^%<>~]/);\n      return \"operator\";\n    } else {\n      return null;\n    }\n  }\n\n  function readQuoted(quote, style,  unescaped) {\n    return function(stream, state) {\n      var escaped = false, ch;\n      while ((ch = stream.next()) != null) {\n        if (ch == quote && (unescaped || !escaped)) {\n          state.tokenize.pop();\n          break;\n        }\n        escaped = !escaped && ch == \"%\";\n      }\n      return style;\n    };\n  }\n\n  return {\n    startState: function() {\n      return {tokenize: [tokenBase]};\n    },\n\n    token: function(stream, state) {\n      var style = state.tokenize[state.tokenize.length-1](stream, state);\n      if (style == \"ident\") {\n        var word = stream.current();\n        style = keywords.propertyIsEnumerable(stream.current()) ? \"keyword\"\n          : operators.propertyIsEnumerable(stream.current()) ? \"operator\"\n          : /^[A-Z][A-Z_0-9]*$/g.test(word) ? \"tag\"\n          : /^0[bB][0-1]+$/g.test(word) ? \"number\"\n          : /^0[cC][0-7]+$/g.test(word) ? \"number\"\n          : /^0[xX][a-fA-F0-9]+$/g.test(word) ? \"number\"\n          : /^([0-9]+\\.[0-9]*)|([0-9]*\\.[0-9]+)$/g.test(word) ? \"number\"\n          : /^[0-9]+$/g.test(word) ? \"number\"\n          : \"variable\";\n      }\n      return style;\n    },\n    lineComment: \"--\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-eiffel\", \"eiffel\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/eiffel/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Eiffel mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/neat.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"eiffel.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n      .cm-s-default span.cm-arrow { color: red; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Eiffel</a>\n  </ul>\n</div>\n\n<article>\n<h2>Eiffel mode</h2>\n<form><textarea id=\"code\" name=\"code\">\nnote\n    description: \"[\n        Project-wide universal properties.\n        This class is an ancestor to all developer-written classes.\n        ANY may be customized for individual projects or teams.\n        ]\"\n\n    library: \"Free implementation of ELKS library\"\n    status: \"See notice at end of class.\"\n    legal: \"See notice at end of class.\"\n    date: \"$Date: 2013-01-25 11:49:00 -0800 (Fri, 25 Jan 2013) $\"\n    revision: \"$Revision: 712 $\"\n\nclass\n    ANY\n\nfeature -- Customization\n\nfeature -- Access\n\n    generator: STRING\n            -- Name of current object's generating class\n            -- (base class of the type of which it is a direct instance)\n        external\n            \"built_in\"\n        ensure\n            generator_not_void: Result /= Void\n            generator_not_empty: not Result.is_empty\n        end\n\n    generating_type: TYPE [detachable like Current]\n            -- Type of current object\n            -- (type of which it is a direct instance)\n        do\n            Result := {detachable like Current}\n        ensure\n            generating_type_not_void: Result /= Void\n        end\n\nfeature -- Status report\n\n    conforms_to (other: ANY): BOOLEAN\n            -- Does type of current object conform to type\n            -- of `other' (as per Eiffel: The Language, chapter 13)?\n        require\n            other_not_void: other /= Void\n        external\n            \"built_in\"\n        end\n\n    same_type (other: ANY): BOOLEAN\n            -- Is type of current object identical to type of `other'?\n        require\n            other_not_void: other /= Void\n        external\n            \"built_in\"\n        ensure\n            definition: Result = (conforms_to (other) and\n                                        other.conforms_to (Current))\n        end\n\nfeature -- Comparison\n\n    is_equal (other: like Current): BOOLEAN\n            -- Is `other' attached to an object considered\n            -- equal to current object?\n        require\n            other_not_void: other /= Void\n        external\n            \"built_in\"\n        ensure\n            symmetric: Result implies other ~ Current\n            consistent: standard_is_equal (other) implies Result\n        end\n\n    frozen standard_is_equal (other: like Current): BOOLEAN\n            -- Is `other' attached to an object of the same type\n            -- as current object, and field-by-field identical to it?\n        require\n            other_not_void: other /= Void\n        external\n            \"built_in\"\n        ensure\n            same_type: Result implies same_type (other)\n            symmetric: Result implies other.standard_is_equal (Current)\n        end\n\n    frozen equal (a: detachable ANY; b: like a): BOOLEAN\n            -- Are `a' and `b' either both void or attached\n            -- to objects considered equal?\n        do\n            if a = Void then\n                Result := b = Void\n            else\n                Result := b /= Void and then\n                            a.is_equal (b)\n            end\n        ensure\n            definition: Result = (a = Void and b = Void) or else\n                        ((a /= Void and b /= Void) and then\n                        a.is_equal (b))\n        end\n\n    frozen standard_equal (a: detachable ANY; b: like a): BOOLEAN\n            -- Are `a' and `b' either both void or attached to\n            -- field-by-field identical objects of the same type?\n            -- Always uses default object comparison criterion.\n        do\n            if a = Void then\n                Result := b = Void\n            else\n                Result := b /= Void and then\n                            a.standard_is_equal (b)\n            end\n        ensure\n            definition: Result = (a = Void and b = Void) or else\n                        ((a /= Void and b /= Void) and then\n                        a.standard_is_equal (b))\n        end\n\n    frozen is_deep_equal (other: like Current): BOOLEAN\n            -- Are `Current' and `other' attached to isomorphic object structures?\n        require\n            other_not_void: other /= Void\n        external\n            \"built_in\"\n        ensure\n            shallow_implies_deep: standard_is_equal (other) implies Result\n            same_type: Result implies same_type (other)\n            symmetric: Result implies other.is_deep_equal (Current)\n        end\n\n    frozen deep_equal (a: detachable ANY; b: like a): BOOLEAN\n            -- Are `a' and `b' either both void\n            -- or attached to isomorphic object structures?\n        do\n            if a = Void then\n                Result := b = Void\n            else\n                Result := b /= Void and then a.is_deep_equal (b)\n            end\n        ensure\n            shallow_implies_deep: standard_equal (a, b) implies Result\n            both_or_none_void: (a = Void) implies (Result = (b = Void))\n            same_type: (Result and (a /= Void)) implies (b /= Void and then a.same_type (b))\n            symmetric: Result implies deep_equal (b, a)\n        end\n\nfeature -- Duplication\n\n    frozen twin: like Current\n            -- New object equal to `Current'\n            -- `twin' calls `copy'; to change copying/twinning semantics, redefine `copy'.\n        external\n            \"built_in\"\n        ensure\n            twin_not_void: Result /= Void\n            is_equal: Result ~ Current\n        end\n\n    copy (other: like Current)\n            -- Update current object using fields of object attached\n            -- to `other', so as to yield equal objects.\n        require\n            other_not_void: other /= Void\n            type_identity: same_type (other)\n        external\n            \"built_in\"\n        ensure\n            is_equal: Current ~ other\n        end\n\n    frozen standard_copy (other: like Current)\n            -- Copy every field of `other' onto corresponding field\n            -- of current object.\n        require\n            other_not_void: other /= Void\n            type_identity: same_type (other)\n        external\n            \"built_in\"\n        ensure\n            is_standard_equal: standard_is_equal (other)\n        end\n\n    frozen clone (other: detachable ANY): like other\n            -- Void if `other' is void; otherwise new object\n            -- equal to `other'\n            --\n            -- For non-void `other', `clone' calls `copy';\n            -- to change copying/cloning semantics, redefine `copy'.\n        obsolete\n            \"Use `twin' instead.\"\n        do\n            if other /= Void then\n                Result := other.twin\n            end\n        ensure\n            equal: Result ~ other\n        end\n\n    frozen standard_clone (other: detachable ANY): like other\n            -- Void if `other' is void; otherwise new object\n            -- field-by-field identical to `other'.\n            -- Always uses default copying semantics.\n        obsolete\n            \"Use `standard_twin' instead.\"\n        do\n            if other /= Void then\n                Result := other.standard_twin\n            end\n        ensure\n            equal: standard_equal (Result, other)\n        end\n\n    frozen standard_twin: like Current\n            -- New object field-by-field identical to `other'.\n            -- Always uses default copying semantics.\n        external\n            \"built_in\"\n        ensure\n            standard_twin_not_void: Result /= Void\n            equal: standard_equal (Result, Current)\n        end\n\n    frozen deep_twin: like Current\n            -- New object structure recursively duplicated from Current.\n        external\n            \"built_in\"\n        ensure\n            deep_twin_not_void: Result /= Void\n            deep_equal: deep_equal (Current, Result)\n        end\n\n    frozen deep_clone (other: detachable ANY): like other\n            -- Void if `other' is void: otherwise, new object structure\n            -- recursively duplicated from the one attached to `other'\n        obsolete\n            \"Use `deep_twin' instead.\"\n        do\n            if other /= Void then\n                Result := other.deep_twin\n            end\n        ensure\n            deep_equal: deep_equal (other, Result)\n        end\n\n    frozen deep_copy (other: like Current)\n            -- Effect equivalent to that of:\n            --      `copy' (`other' . `deep_twin')\n        require\n            other_not_void: other /= Void\n        do\n            copy (other.deep_twin)\n        ensure\n            deep_equal: deep_equal (Current, other)\n        end\n\nfeature {NONE} -- Retrieval\n\n    frozen internal_correct_mismatch\n            -- Called from runtime to perform a proper dynamic dispatch on `correct_mismatch'\n            -- from MISMATCH_CORRECTOR.\n        local\n            l_msg: STRING\n            l_exc: EXCEPTIONS\n        do\n            if attached {MISMATCH_CORRECTOR} Current as l_corrector then\n                l_corrector.correct_mismatch\n            else\n                create l_msg.make_from_string (\"Mismatch: \")\n                create l_exc\n                l_msg.append (generating_type.name)\n                l_exc.raise_retrieval_exception (l_msg)\n            end\n        end\n\nfeature -- Output\n\n    io: STD_FILES\n            -- Handle to standard file setup\n        once\n            create Result\n            Result.set_output_default\n        ensure\n            io_not_void: Result /= Void\n        end\n\n    out: STRING\n            -- New string containing terse printable representation\n            -- of current object\n        do\n            Result := tagged_out\n        ensure\n            out_not_void: Result /= Void\n        end\n\n    frozen tagged_out: STRING\n            -- New string containing terse printable representation\n            -- of current object\n        external\n            \"built_in\"\n        ensure\n            tagged_out_not_void: Result /= Void\n        end\n\n    print (o: detachable ANY)\n            -- Write terse external representation of `o'\n            -- on standard output.\n        do\n            if o /= Void then\n                io.put_string (o.out)\n            end\n        end\n\nfeature -- Platform\n\n    Operating_environment: OPERATING_ENVIRONMENT\n            -- Objects available from the operating system\n        once\n            create Result\n        ensure\n            operating_environment_not_void: Result /= Void\n        end\n\nfeature {NONE} -- Initialization\n\n    default_create\n            -- Process instances of classes with no creation clause.\n            -- (Default: do nothing.)\n        do\n        end\n\nfeature -- Basic operations\n\n    default_rescue\n            -- Process exception for routines with no Rescue clause.\n            -- (Default: do nothing.)\n        do\n        end\n\n    frozen do_nothing\n            -- Execute a null action.\n        do\n        end\n\n    frozen default: detachable like Current\n            -- Default value of object's type\n        do\n        end\n\n    frozen default_pointer: POINTER\n            -- Default value of type `POINTER'\n            -- (Avoid the need to write `p'.`default' for\n            -- some `p' of type `POINTER'.)\n        do\n        ensure\n            -- Result = Result.default\n        end\n\n    frozen as_attached: attached like Current\n            -- Attached version of Current\n            -- (Can be used during transitional period to convert\n            -- non-void-safe classes to void-safe ones.)\n        do\n            Result := Current\n        end\n\ninvariant\n    reflexive_equality: standard_is_equal (Current)\n    reflexive_conformance: conforms_to (Current)\n\nnote\n    copyright: \"Copyright (c) 1984-2012, Eiffel Software and others\"\n    license:   \"Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)\"\n    source: \"[\n            Eiffel Software\n            5949 Hollister Ave., Goleta, CA 93117 USA\n            Telephone 805-685-1006, Fax 805-685-6869\n            Website http://www.eiffel.com\n            Customer support http://support.eiffel.com\n        ]\"\n\nend\n\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: \"text/x-eiffel\",\n        indentUnit: 4,\n        lineNumbers: true,\n        theme: \"neat\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-eiffel</code>.</p>\n \n <p> Created by <a href=\"https://github.com/ynh\">YNH</a>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/elm/elm.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"elm\", function() {\n\n    function switchState(source, setState, f)\n    {\n      setState(f);\n      return f(source, setState);\n    }\n\n    var lowerRE = /[a-z]/;\n    var upperRE = /[A-Z]/;\n    var innerRE = /[a-zA-Z0-9_]/;\n\n    var digitRE = /[0-9]/;\n    var hexRE = /[0-9A-Fa-f]/;\n    var symbolRE = /[-&*+.\\\\/<>=?^|:]/;\n    var specialRE = /[(),[\\]{}]/;\n    var spacesRE = /[ \\v\\f]/; // newlines are handled in tokenizer\n\n    function normal()\n    {\n      return function(source, setState)\n      {\n        if (source.eatWhile(spacesRE))\n        {\n          return null;\n        }\n\n        var char = source.next();\n\n        if (specialRE.test(char))\n        {\n          return (char === '{' && source.eat('-'))\n            ? switchState(source, setState, chompMultiComment(1))\n            : (char === '[' && source.match('glsl|'))\n                ? switchState(source, setState, chompGlsl)\n                : 'builtin';\n        }\n\n        if (char === '\\'')\n        {\n          return switchState(source, setState, chompChar);\n        }\n\n        if (char === '\"')\n        {\n          return source.eat('\"')\n            ? source.eat('\"')\n                ? switchState(source, setState, chompMultiString)\n                : 'string'\n            : switchState(source, setState, chompSingleString);\n        }\n\n        if (upperRE.test(char))\n        {\n          source.eatWhile(innerRE);\n          return 'variable-2';\n        }\n\n        if (lowerRE.test(char))\n        {\n          var isDef = source.pos === 1;\n          source.eatWhile(innerRE);\n          return isDef ? \"def\" : \"variable\";\n        }\n\n        if (digitRE.test(char))\n        {\n          if (char === '0')\n          {\n            if (source.eat(/[xX]/))\n            {\n              source.eatWhile(hexRE); // should require at least 1\n              return \"number\";\n            }\n          }\n          else\n          {\n            source.eatWhile(digitRE);\n          }\n          if (source.eat('.'))\n          {\n            source.eatWhile(digitRE); // should require at least 1\n          }\n          if (source.eat(/[eE]/))\n          {\n            source.eat(/[-+]/);\n            source.eatWhile(digitRE); // should require at least 1\n          }\n          return \"number\";\n        }\n\n        if (symbolRE.test(char))\n        {\n          if (char === '-' && source.eat('-'))\n          {\n            source.skipToEnd();\n            return \"comment\";\n          }\n          source.eatWhile(symbolRE);\n          return \"keyword\";\n        }\n\n        if (char === '_')\n        {\n          return \"keyword\";\n        }\n\n        return \"error\";\n      }\n    }\n\n    function chompMultiComment(nest)\n    {\n      if (nest == 0)\n      {\n        return normal();\n      }\n      return function(source, setState)\n      {\n        while (!source.eol())\n        {\n          var char = source.next();\n          if (char == '{' && source.eat('-'))\n          {\n            ++nest;\n          }\n          else if (char == '-' && source.eat('}'))\n          {\n            --nest;\n            if (nest === 0)\n            {\n              setState(normal());\n              return 'comment';\n            }\n          }\n        }\n        setState(chompMultiComment(nest));\n        return 'comment';\n      }\n    }\n\n    function chompMultiString(source, setState)\n    {\n      while (!source.eol())\n      {\n        var char = source.next();\n        if (char === '\"' && source.eat('\"') && source.eat('\"'))\n        {\n          setState(normal());\n          return 'string';\n        }\n      }\n      return 'string';\n    }\n\n    function chompSingleString(source, setState)\n    {\n      while (source.skipTo('\\\\\"')) { source.next(); source.next(); }\n      if (source.skipTo('\"'))\n      {\n        source.next();\n        setState(normal());\n        return 'string';\n      }\n      source.skipToEnd();\n      setState(normal());\n      return 'error';\n    }\n\n    function chompChar(source, setState)\n    {\n      while (source.skipTo(\"\\\\'\")) { source.next(); source.next(); }\n      if (source.skipTo(\"'\"))\n      {\n        source.next();\n        setState(normal());\n        return 'string';\n      }\n      source.skipToEnd();\n      setState(normal());\n      return 'error';\n    }\n\n    function chompGlsl(source, setState)\n    {\n      while (!source.eol())\n      {\n        var char = source.next();\n        if (char === '|' && source.eat(']'))\n        {\n          setState(normal());\n          return 'string';\n        }\n      }\n      return 'string';\n    }\n\n    var wellKnownWords = {\n      case: 1,\n      of: 1,\n      as: 1,\n      if: 1,\n      then: 1,\n      else: 1,\n      let: 1,\n      in: 1,\n      type: 1,\n      alias: 1,\n      module: 1,\n      where: 1,\n      import: 1,\n      exposing: 1,\n      port: 1\n    };\n\n    return {\n      startState: function ()  { return { f: normal() }; },\n      copyState:  function (s) { return { f: s.f }; },\n\n      lineComment: '--',\n\n      token: function(stream, state) {\n        var type = state.f(stream, function(s) { state.f = s; });\n        var word = stream.current();\n        return (wellKnownWords.hasOwnProperty(word)) ? 'keyword' : type;\n      }\n    };\n\n  });\n\n  CodeMirror.defineMIME(\"text/x-elm\", \"elm\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/elm/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Elm mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"elm.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Elm</a>\n  </ul>\n</div>\n\n<article>\n<h2>Elm mode</h2>\n\n<div><textarea id=\"code\" name=\"code\">\nimport Color exposing (..)\nimport Graphics.Collage exposing (..)\nimport Graphics.Element exposing (..)\nimport Time exposing (..)\n\nmain =\n  Signal.map clock (every second)\n\nclock t =\n  collage 400 400\n    [ filled    lightGrey   (ngon 12 110)\n    , outlined (solid grey) (ngon 12 110)\n    , hand orange   100  t\n    , hand charcoal 100 (t/60)\n    , hand charcoal 60  (t/720)\n    ]\n\nhand clr len time =\n  let angle = degrees (90 - 6 * inSeconds time)\n  in\n      segment (0,0) (fromPolar (len,angle))\n        |> traced (solid clr)\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        mode: \"text/x-elm\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-elm</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/erlang/erlang.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/*jshint unused:true, eqnull:true, curly:true, bitwise:true */\n/*jshint undef:true, latedef:true, trailing:true */\n/*global CodeMirror:true */\n\n// erlang mode.\n// tokenizer -> token types -> CodeMirror styles\n// tokenizer maintains a parse stack\n// indenter uses the parse stack\n\n// TODO indenter:\n//   bit syntax\n//   old guard/bif/conversion clashes (e.g. \"float/1\")\n//   type/spec/opaque\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMIME(\"text/x-erlang\", \"erlang\");\n\nCodeMirror.defineMode(\"erlang\", function(cmCfg) {\n  \"use strict\";\n\n/////////////////////////////////////////////////////////////////////////////\n// constants\n\n  var typeWords = [\n    \"-type\", \"-spec\", \"-export_type\", \"-opaque\"];\n\n  var keywordWords = [\n    \"after\",\"begin\",\"catch\",\"case\",\"cond\",\"end\",\"fun\",\"if\",\n    \"let\",\"of\",\"query\",\"receive\",\"try\",\"when\"];\n\n  var separatorRE    = /[\\->,;]/;\n  var separatorWords = [\n    \"->\",\";\",\",\"];\n\n  var operatorAtomWords = [\n    \"and\",\"andalso\",\"band\",\"bnot\",\"bor\",\"bsl\",\"bsr\",\"bxor\",\n    \"div\",\"not\",\"or\",\"orelse\",\"rem\",\"xor\"];\n\n  var operatorSymbolRE    = /[\\+\\-\\*\\/<>=\\|:!]/;\n  var operatorSymbolWords = [\n    \"=\",\"+\",\"-\",\"*\",\"/\",\">\",\">=\",\"<\",\"=<\",\"=:=\",\"==\",\"=/=\",\"/=\",\"||\",\"<-\",\"!\"];\n\n  var openParenRE    = /[<\\(\\[\\{]/;\n  var openParenWords = [\n    \"<<\",\"(\",\"[\",\"{\"];\n\n  var closeParenRE    = /[>\\)\\]\\}]/;\n  var closeParenWords = [\n    \"}\",\"]\",\")\",\">>\"];\n\n  var guardWords = [\n    \"is_atom\",\"is_binary\",\"is_bitstring\",\"is_boolean\",\"is_float\",\n    \"is_function\",\"is_integer\",\"is_list\",\"is_number\",\"is_pid\",\n    \"is_port\",\"is_record\",\"is_reference\",\"is_tuple\",\n    \"atom\",\"binary\",\"bitstring\",\"boolean\",\"function\",\"integer\",\"list\",\n    \"number\",\"pid\",\"port\",\"record\",\"reference\",\"tuple\"];\n\n  var bifWords = [\n    \"abs\",\"adler32\",\"adler32_combine\",\"alive\",\"apply\",\"atom_to_binary\",\n    \"atom_to_list\",\"binary_to_atom\",\"binary_to_existing_atom\",\n    \"binary_to_list\",\"binary_to_term\",\"bit_size\",\"bitstring_to_list\",\n    \"byte_size\",\"check_process_code\",\"contact_binary\",\"crc32\",\n    \"crc32_combine\",\"date\",\"decode_packet\",\"delete_module\",\n    \"disconnect_node\",\"element\",\"erase\",\"exit\",\"float\",\"float_to_list\",\n    \"garbage_collect\",\"get\",\"get_keys\",\"group_leader\",\"halt\",\"hd\",\n    \"integer_to_list\",\"internal_bif\",\"iolist_size\",\"iolist_to_binary\",\n    \"is_alive\",\"is_atom\",\"is_binary\",\"is_bitstring\",\"is_boolean\",\n    \"is_float\",\"is_function\",\"is_integer\",\"is_list\",\"is_number\",\"is_pid\",\n    \"is_port\",\"is_process_alive\",\"is_record\",\"is_reference\",\"is_tuple\",\n    \"length\",\"link\",\"list_to_atom\",\"list_to_binary\",\"list_to_bitstring\",\n    \"list_to_existing_atom\",\"list_to_float\",\"list_to_integer\",\n    \"list_to_pid\",\"list_to_tuple\",\"load_module\",\"make_ref\",\"module_loaded\",\n    \"monitor_node\",\"node\",\"node_link\",\"node_unlink\",\"nodes\",\"notalive\",\n    \"now\",\"open_port\",\"pid_to_list\",\"port_close\",\"port_command\",\n    \"port_connect\",\"port_control\",\"pre_loaded\",\"process_flag\",\n    \"process_info\",\"processes\",\"purge_module\",\"put\",\"register\",\n    \"registered\",\"round\",\"self\",\"setelement\",\"size\",\"spawn\",\"spawn_link\",\n    \"spawn_monitor\",\"spawn_opt\",\"split_binary\",\"statistics\",\n    \"term_to_binary\",\"time\",\"throw\",\"tl\",\"trunc\",\"tuple_size\",\n    \"tuple_to_list\",\"unlink\",\"unregister\",\"whereis\"];\n\n// upper case: [A-Z] [Ø-Þ] [À-Ö]\n// lower case: [a-z] [ß-ö] [ø-ÿ]\n  var anumRE       = /[\\w@Ø-ÞÀ-Öß-öø-ÿ]/;\n  var escapesRE    =\n    /[0-7]{1,3}|[bdefnrstv\\\\\"']|\\^[a-zA-Z]|x[0-9a-zA-Z]{2}|x{[0-9a-zA-Z]+}/;\n\n/////////////////////////////////////////////////////////////////////////////\n// tokenizer\n\n  function tokenizer(stream,state) {\n    // in multi-line string\n    if (state.in_string) {\n      state.in_string = (!doubleQuote(stream));\n      return rval(state,stream,\"string\");\n    }\n\n    // in multi-line atom\n    if (state.in_atom) {\n      state.in_atom = (!singleQuote(stream));\n      return rval(state,stream,\"atom\");\n    }\n\n    // whitespace\n    if (stream.eatSpace()) {\n      return rval(state,stream,\"whitespace\");\n    }\n\n    // attributes and type specs\n    if (!peekToken(state) &&\n        stream.match(/-\\s*[a-zß-öø-ÿ][\\wØ-ÞÀ-Öß-öø-ÿ]*/)) {\n      if (is_member(stream.current(),typeWords)) {\n        return rval(state,stream,\"type\");\n      }else{\n        return rval(state,stream,\"attribute\");\n      }\n    }\n\n    var ch = stream.next();\n\n    // comment\n    if (ch == '%') {\n      stream.skipToEnd();\n      return rval(state,stream,\"comment\");\n    }\n\n    // colon\n    if (ch == \":\") {\n      return rval(state,stream,\"colon\");\n    }\n\n    // macro\n    if (ch == '?') {\n      stream.eatSpace();\n      stream.eatWhile(anumRE);\n      return rval(state,stream,\"macro\");\n    }\n\n    // record\n    if (ch == \"#\") {\n      stream.eatSpace();\n      stream.eatWhile(anumRE);\n      return rval(state,stream,\"record\");\n    }\n\n    // dollar escape\n    if (ch == \"$\") {\n      if (stream.next() == \"\\\\\" && !stream.match(escapesRE)) {\n        return rval(state,stream,\"error\");\n      }\n      return rval(state,stream,\"number\");\n    }\n\n    // dot\n    if (ch == \".\") {\n      return rval(state,stream,\"dot\");\n    }\n\n    // quoted atom\n    if (ch == '\\'') {\n      if (!(state.in_atom = (!singleQuote(stream)))) {\n        if (stream.match(/\\s*\\/\\s*[0-9]/,false)) {\n          stream.match(/\\s*\\/\\s*[0-9]/,true);\n          return rval(state,stream,\"fun\");      // 'f'/0 style fun\n        }\n        if (stream.match(/\\s*\\(/,false) || stream.match(/\\s*:/,false)) {\n          return rval(state,stream,\"function\");\n        }\n      }\n      return rval(state,stream,\"atom\");\n    }\n\n    // string\n    if (ch == '\"') {\n      state.in_string = (!doubleQuote(stream));\n      return rval(state,stream,\"string\");\n    }\n\n    // variable\n    if (/[A-Z_Ø-ÞÀ-Ö]/.test(ch)) {\n      stream.eatWhile(anumRE);\n      return rval(state,stream,\"variable\");\n    }\n\n    // atom/keyword/BIF/function\n    if (/[a-z_ß-öø-ÿ]/.test(ch)) {\n      stream.eatWhile(anumRE);\n\n      if (stream.match(/\\s*\\/\\s*[0-9]/,false)) {\n        stream.match(/\\s*\\/\\s*[0-9]/,true);\n        return rval(state,stream,\"fun\");      // f/0 style fun\n      }\n\n      var w = stream.current();\n\n      if (is_member(w,keywordWords)) {\n        return rval(state,stream,\"keyword\");\n      }else if (is_member(w,operatorAtomWords)) {\n        return rval(state,stream,\"operator\");\n      }else if (stream.match(/\\s*\\(/,false)) {\n        // 'put' and 'erlang:put' are bifs, 'foo:put' is not\n        if (is_member(w,bifWords) &&\n            ((peekToken(state).token != \":\") ||\n             (peekToken(state,2).token == \"erlang\"))) {\n          return rval(state,stream,\"builtin\");\n        }else if (is_member(w,guardWords)) {\n          return rval(state,stream,\"guard\");\n        }else{\n          return rval(state,stream,\"function\");\n        }\n      }else if (lookahead(stream) == \":\") {\n        if (w == \"erlang\") {\n          return rval(state,stream,\"builtin\");\n        } else {\n          return rval(state,stream,\"function\");\n        }\n      }else if (is_member(w,[\"true\",\"false\"])) {\n        return rval(state,stream,\"boolean\");\n      }else{\n        return rval(state,stream,\"atom\");\n      }\n    }\n\n    // number\n    var digitRE      = /[0-9]/;\n    var radixRE      = /[0-9a-zA-Z]/;         // 36#zZ style int\n    if (digitRE.test(ch)) {\n      stream.eatWhile(digitRE);\n      if (stream.eat('#')) {                // 36#aZ  style integer\n        if (!stream.eatWhile(radixRE)) {\n          stream.backUp(1);                 //\"36#\" - syntax error\n        }\n      } else if (stream.eat('.')) {       // float\n        if (!stream.eatWhile(digitRE)) {\n          stream.backUp(1);        // \"3.\" - probably end of function\n        } else {\n          if (stream.eat(/[eE]/)) {        // float with exponent\n            if (stream.eat(/[-+]/)) {\n              if (!stream.eatWhile(digitRE)) {\n                stream.backUp(2);            // \"2e-\" - syntax error\n              }\n            } else {\n              if (!stream.eatWhile(digitRE)) {\n                stream.backUp(1);            // \"2e\" - syntax error\n              }\n            }\n          }\n        }\n      }\n      return rval(state,stream,\"number\");   // normal integer\n    }\n\n    // open parens\n    if (nongreedy(stream,openParenRE,openParenWords)) {\n      return rval(state,stream,\"open_paren\");\n    }\n\n    // close parens\n    if (nongreedy(stream,closeParenRE,closeParenWords)) {\n      return rval(state,stream,\"close_paren\");\n    }\n\n    // separators\n    if (greedy(stream,separatorRE,separatorWords)) {\n      return rval(state,stream,\"separator\");\n    }\n\n    // operators\n    if (greedy(stream,operatorSymbolRE,operatorSymbolWords)) {\n      return rval(state,stream,\"operator\");\n    }\n\n    return rval(state,stream,null);\n  }\n\n/////////////////////////////////////////////////////////////////////////////\n// utilities\n  function nongreedy(stream,re,words) {\n    if (stream.current().length == 1 && re.test(stream.current())) {\n      stream.backUp(1);\n      while (re.test(stream.peek())) {\n        stream.next();\n        if (is_member(stream.current(),words)) {\n          return true;\n        }\n      }\n      stream.backUp(stream.current().length-1);\n    }\n    return false;\n  }\n\n  function greedy(stream,re,words) {\n    if (stream.current().length == 1 && re.test(stream.current())) {\n      while (re.test(stream.peek())) {\n        stream.next();\n      }\n      while (0 < stream.current().length) {\n        if (is_member(stream.current(),words)) {\n          return true;\n        }else{\n          stream.backUp(1);\n        }\n      }\n      stream.next();\n    }\n    return false;\n  }\n\n  function doubleQuote(stream) {\n    return quote(stream, '\"', '\\\\');\n  }\n\n  function singleQuote(stream) {\n    return quote(stream,'\\'','\\\\');\n  }\n\n  function quote(stream,quoteChar,escapeChar) {\n    while (!stream.eol()) {\n      var ch = stream.next();\n      if (ch == quoteChar) {\n        return true;\n      }else if (ch == escapeChar) {\n        stream.next();\n      }\n    }\n    return false;\n  }\n\n  function lookahead(stream) {\n    var m = stream.match(/^\\s*([^\\s%])/, false)\n    return m ? m[1] : \"\";\n  }\n\n  function is_member(element,list) {\n    return (-1 < list.indexOf(element));\n  }\n\n  function rval(state,stream,type) {\n\n    // parse stack\n    pushToken(state,realToken(type,stream));\n\n    // map erlang token type to CodeMirror style class\n    //     erlang             -> CodeMirror tag\n    switch (type) {\n      case \"atom\":        return \"atom\";\n      case \"attribute\":   return \"attribute\";\n      case \"boolean\":     return \"atom\";\n      case \"builtin\":     return \"builtin\";\n      case \"close_paren\": return null;\n      case \"colon\":       return null;\n      case \"comment\":     return \"comment\";\n      case \"dot\":         return null;\n      case \"error\":       return \"error\";\n      case \"fun\":         return \"meta\";\n      case \"function\":    return \"tag\";\n      case \"guard\":       return \"property\";\n      case \"keyword\":     return \"keyword\";\n      case \"macro\":       return \"variable-2\";\n      case \"number\":      return \"number\";\n      case \"open_paren\":  return null;\n      case \"operator\":    return \"operator\";\n      case \"record\":      return \"bracket\";\n      case \"separator\":   return null;\n      case \"string\":      return \"string\";\n      case \"type\":        return \"def\";\n      case \"variable\":    return \"variable\";\n      default:            return null;\n    }\n  }\n\n  function aToken(tok,col,ind,typ) {\n    return {token:  tok,\n            column: col,\n            indent: ind,\n            type:   typ};\n  }\n\n  function realToken(type,stream) {\n    return aToken(stream.current(),\n                 stream.column(),\n                 stream.indentation(),\n                 type);\n  }\n\n  function fakeToken(type) {\n    return aToken(type,0,0,type);\n  }\n\n  function peekToken(state,depth) {\n    var len = state.tokenStack.length;\n    var dep = (depth ? depth : 1);\n\n    if (len < dep) {\n      return false;\n    }else{\n      return state.tokenStack[len-dep];\n    }\n  }\n\n  function pushToken(state,token) {\n\n    if (!(token.type == \"comment\" || token.type == \"whitespace\")) {\n      state.tokenStack = maybe_drop_pre(state.tokenStack,token);\n      state.tokenStack = maybe_drop_post(state.tokenStack);\n    }\n  }\n\n  function maybe_drop_pre(s,token) {\n    var last = s.length-1;\n\n    if (0 < last && s[last].type === \"record\" && token.type === \"dot\") {\n      s.pop();\n    }else if (0 < last && s[last].type === \"group\") {\n      s.pop();\n      s.push(token);\n    }else{\n      s.push(token);\n    }\n    return s;\n  }\n\n  function maybe_drop_post(s) {\n    if (!s.length) return s\n    var last = s.length-1;\n\n    if (s[last].type === \"dot\") {\n      return [];\n    }\n    if (last > 1 && s[last].type === \"fun\" && s[last-1].token === \"fun\") {\n      return s.slice(0,last-1);\n    }\n    switch (s[last].token) {\n      case \"}\":    return d(s,{g:[\"{\"]});\n      case \"]\":    return d(s,{i:[\"[\"]});\n      case \")\":    return d(s,{i:[\"(\"]});\n      case \">>\":   return d(s,{i:[\"<<\"]});\n      case \"end\":  return d(s,{i:[\"begin\",\"case\",\"fun\",\"if\",\"receive\",\"try\"]});\n      case \",\":    return d(s,{e:[\"begin\",\"try\",\"when\",\"->\",\n                                  \",\",\"(\",\"[\",\"{\",\"<<\"]});\n      case \"->\":   return d(s,{r:[\"when\"],\n                               m:[\"try\",\"if\",\"case\",\"receive\"]});\n      case \";\":    return d(s,{E:[\"case\",\"fun\",\"if\",\"receive\",\"try\",\"when\"]});\n      case \"catch\":return d(s,{e:[\"try\"]});\n      case \"of\":   return d(s,{e:[\"case\"]});\n      case \"after\":return d(s,{e:[\"receive\",\"try\"]});\n      default:     return s;\n    }\n  }\n\n  function d(stack,tt) {\n    // stack is a stack of Token objects.\n    // tt is an object; {type:tokens}\n    // type is a char, tokens is a list of token strings.\n    // The function returns (possibly truncated) stack.\n    // It will descend the stack, looking for a Token such that Token.token\n    //  is a member of tokens. If it does not find that, it will normally (but\n    //  see \"E\" below) return stack. If it does find a match, it will remove\n    //  all the Tokens between the top and the matched Token.\n    // If type is \"m\", that is all it does.\n    // If type is \"i\", it will also remove the matched Token and the top Token.\n    // If type is \"g\", like \"i\", but add a fake \"group\" token at the top.\n    // If type is \"r\", it will remove the matched Token, but not the top Token.\n    // If type is \"e\", it will keep the matched Token but not the top Token.\n    // If type is \"E\", it behaves as for type \"e\", except if there is no match,\n    //  in which case it will return an empty stack.\n\n    for (var type in tt) {\n      var len = stack.length-1;\n      var tokens = tt[type];\n      for (var i = len-1; -1 < i ; i--) {\n        if (is_member(stack[i].token,tokens)) {\n          var ss = stack.slice(0,i);\n          switch (type) {\n              case \"m\": return ss.concat(stack[i]).concat(stack[len]);\n              case \"r\": return ss.concat(stack[len]);\n              case \"i\": return ss;\n              case \"g\": return ss.concat(fakeToken(\"group\"));\n              case \"E\": return ss.concat(stack[i]);\n              case \"e\": return ss.concat(stack[i]);\n          }\n        }\n      }\n    }\n    return (type == \"E\" ? [] : stack);\n  }\n\n/////////////////////////////////////////////////////////////////////////////\n// indenter\n\n  function indenter(state,textAfter) {\n    var t;\n    var unit = cmCfg.indentUnit;\n    var wordAfter = wordafter(textAfter);\n    var currT = peekToken(state,1);\n    var prevT = peekToken(state,2);\n\n    if (state.in_string || state.in_atom) {\n      return CodeMirror.Pass;\n    }else if (!prevT) {\n      return 0;\n    }else if (currT.token == \"when\") {\n      return currT.column+unit;\n    }else if (wordAfter === \"when\" && prevT.type === \"function\") {\n      return prevT.indent+unit;\n    }else if (wordAfter === \"(\" && currT.token === \"fun\") {\n      return  currT.column+3;\n    }else if (wordAfter === \"catch\" && (t = getToken(state,[\"try\"]))) {\n      return t.column;\n    }else if (is_member(wordAfter,[\"end\",\"after\",\"of\"])) {\n      t = getToken(state,[\"begin\",\"case\",\"fun\",\"if\",\"receive\",\"try\"]);\n      return t ? t.column : CodeMirror.Pass;\n    }else if (is_member(wordAfter,closeParenWords)) {\n      t = getToken(state,openParenWords);\n      return t ? t.column : CodeMirror.Pass;\n    }else if (is_member(currT.token,[\",\",\"|\",\"||\"]) ||\n              is_member(wordAfter,[\",\",\"|\",\"||\"])) {\n      t = postcommaToken(state);\n      return t ? t.column+t.token.length : unit;\n    }else if (currT.token == \"->\") {\n      if (is_member(prevT.token, [\"receive\",\"case\",\"if\",\"try\"])) {\n        return prevT.column+unit+unit;\n      }else{\n        return prevT.column+unit;\n      }\n    }else if (is_member(currT.token,openParenWords)) {\n      return currT.column+currT.token.length;\n    }else{\n      t = defaultToken(state);\n      return truthy(t) ? t.column+unit : 0;\n    }\n  }\n\n  function wordafter(str) {\n    var m = str.match(/,|[a-z]+|\\}|\\]|\\)|>>|\\|+|\\(/);\n\n    return truthy(m) && (m.index === 0) ? m[0] : \"\";\n  }\n\n  function postcommaToken(state) {\n    var objs = state.tokenStack.slice(0,-1);\n    var i = getTokenIndex(objs,\"type\",[\"open_paren\"]);\n\n    return truthy(objs[i]) ? objs[i] : false;\n  }\n\n  function defaultToken(state) {\n    var objs = state.tokenStack;\n    var stop = getTokenIndex(objs,\"type\",[\"open_paren\",\"separator\",\"keyword\"]);\n    var oper = getTokenIndex(objs,\"type\",[\"operator\"]);\n\n    if (truthy(stop) && truthy(oper) && stop < oper) {\n      return objs[stop+1];\n    } else if (truthy(stop)) {\n      return objs[stop];\n    } else {\n      return false;\n    }\n  }\n\n  function getToken(state,tokens) {\n    var objs = state.tokenStack;\n    var i = getTokenIndex(objs,\"token\",tokens);\n\n    return truthy(objs[i]) ? objs[i] : false;\n  }\n\n  function getTokenIndex(objs,propname,propvals) {\n\n    for (var i = objs.length-1; -1 < i ; i--) {\n      if (is_member(objs[i][propname],propvals)) {\n        return i;\n      }\n    }\n    return false;\n  }\n\n  function truthy(x) {\n    return (x !== false) && (x != null);\n  }\n\n/////////////////////////////////////////////////////////////////////////////\n// this object defines the mode\n\n  return {\n    startState:\n      function() {\n        return {tokenStack: [],\n                in_string:  false,\n                in_atom:    false};\n      },\n\n    token:\n      function(stream, state) {\n        return tokenizer(stream, state);\n      },\n\n    indent:\n      function(state, textAfter) {\n        return indenter(state,textAfter);\n      },\n\n    lineComment: \"%\"\n  };\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/erlang/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Erlang mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/erlang-dark.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"erlang.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Erlang</a>\n  </ul>\n</div>\n\n<article>\n<h2>Erlang mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n%% -*- mode: erlang; erlang-indent-level: 2 -*-\n%%% Created :  7 May 2012 by mats cronqvist <masse@klarna.com>\n\n%% @doc\n%% Demonstrates how to print a record.\n%% @end\n\n-module('ex').\n-author('mats cronqvist').\n-export([demo/0,\n         rec_info/1]).\n\n-record(demo,{a=\"One\",b=\"Two\",c=\"Three\",d=\"Four\"}).\n\nrec_info(demo) -> record_info(fields,demo).\n\ndemo() -> expand_recs(?MODULE,#demo{a=\"A\",b=\"BB\"}).\n\nexpand_recs(M,List) when is_list(List) ->\n  [expand_recs(M,L)||L<-List];\nexpand_recs(M,Tup) when is_tuple(Tup) ->\n  case tuple_size(Tup) of\n    L when L < 1 -> Tup;\n    L ->\n      try\n        Fields = M:rec_info(element(1,Tup)),\n        L = length(Fields)+1,\n        lists:zip(Fields,expand_recs(M,tl(tuple_to_list(Tup))))\n      catch\n        _:_ -> list_to_tuple(expand_recs(M,tuple_to_list(Tup)))\n      end\n  end;\nexpand_recs(_,Term) ->\n  Term.\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        extraKeys: {\"Tab\":  \"indentAuto\"},\n        theme: \"erlang-dark\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-erlang</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/factor/factor.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Factor syntax highlight - simple mode\n//\n// by Dimage Sapelkin (https://github.com/kerabromsmu)\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../../addon/mode/simple\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../../addon/mode/simple\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineSimpleMode(\"factor\", {\n    // The start state contains the rules that are initially used\n    start: [\n      // comments\n      {regex: /#?!.*/, token: \"comment\"},\n      // strings \"\"\", multiline --> state\n      {regex: /\"\"\"/, token: \"string\", next: \"string3\"},\n      {regex: /(STRING:)(\\s)/, token: [\"keyword\", null], next: \"string2\"},\n      {regex: /\\S*?\"/, token: \"string\", next: \"string\"},\n      // numbers: dec, hex, unicode, bin, fractional, complex\n      {regex: /(?:0x[\\d,a-f]+)|(?:0o[0-7]+)|(?:0b[0,1]+)|(?:\\-?\\d+.?\\d*)(?=\\s)/, token: \"number\"},\n      //{regex: /[+-]?/} //fractional\n      // definition: defining word, defined word, etc\n      {regex: /((?:GENERIC)|\\:?\\:)(\\s+)(\\S+)(\\s+)(\\()/, token: [\"keyword\", null, \"def\", null, \"bracket\"], next: \"stack\"},\n      // method definition: defining word, type, defined word, etc\n      {regex: /(M\\:)(\\s+)(\\S+)(\\s+)(\\S+)/, token: [\"keyword\", null, \"def\", null, \"tag\"]},\n      // vocabulary using --> state\n      {regex: /USING\\:/, token: \"keyword\", next: \"vocabulary\"},\n      // vocabulary definition/use\n      {regex: /(USE\\:|IN\\:)(\\s+)(\\S+)(?=\\s|$)/, token: [\"keyword\", null, \"tag\"]},\n      // definition: a defining word, defined word\n      {regex: /(\\S+\\:)(\\s+)(\\S+)(?=\\s|$)/, token: [\"keyword\", null, \"def\"]},\n      // \"keywords\", incl. ; t f . [ ] { } defining words\n      {regex: /(?:;|\\\\|t|f|if|loop|while|until|do|PRIVATE>|<PRIVATE|\\.|\\S*\\[|\\]|\\S*\\{|\\})(?=\\s|$)/, token: \"keyword\"},\n      // <constructors> and the like\n      {regex: /\\S+[\\)>\\.\\*\\?]+(?=\\s|$)/, token: \"builtin\"},\n      {regex: /[\\)><]+\\S+(?=\\s|$)/, token: \"builtin\"},\n      // operators\n      {regex: /(?:[\\+\\-\\=\\/\\*<>])(?=\\s|$)/, token: \"keyword\"},\n      // any id (?)\n      {regex: /\\S+/, token: \"variable\"},\n      {regex: /\\s+|./, token: null}\n    ],\n    vocabulary: [\n      {regex: /;/, token: \"keyword\", next: \"start\"},\n      {regex: /\\S+/, token: \"tag\"},\n      {regex: /\\s+|./, token: null}\n    ],\n    string: [\n      {regex: /(?:[^\\\\]|\\\\.)*?\"/, token: \"string\", next: \"start\"},\n      {regex: /.*/, token: \"string\"}\n    ],\n    string2: [\n      {regex: /^;/, token: \"keyword\", next: \"start\"},\n      {regex: /.*/, token: \"string\"}\n    ],\n    string3: [\n      {regex: /(?:[^\\\\]|\\\\.)*?\"\"\"/, token: \"string\", next: \"start\"},\n      {regex: /.*/, token: \"string\"}\n    ],\n    stack: [\n      {regex: /\\)/, token: \"bracket\", next: \"start\"},\n      {regex: /--/, token: \"bracket\"},\n      {regex: /\\S+/, token: \"meta\"},\n      {regex: /\\s+|./, token: null}\n    ],\n    // The meta property contains global information about the mode. It\n    // can contain properties like lineComment, which are supported by\n    // all modes, and also directives like dontIndentStates, which are\n    // specific to simple modes.\n    meta: {\n      dontIndentStates: [\"start\", \"vocabulary\", \"string\", \"string3\", \"stack\"],\n      lineComment: \"!\"\n    }\n  });\n\n  CodeMirror.defineMIME(\"text/x-factor\", \"factor\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/factor/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Factor mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/mode/simple.js\"></script>\n<script src=\"factor.js\"></script>\n<style>\n.CodeMirror {\n    font-family: 'Droid Sans Mono', monospace;\n    font-size: 14px;\n}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Factor</a>\n  </ul>\n</div>\n\n<article>\n\n<h2>Factor mode</h2>\n\n<form><textarea id=\"code\" name=\"code\">\n! Copyright (C) 2008 Slava Pestov.\n! See http://factorcode.org/license.txt for BSD license.\n\n! A simple time server\n\nUSING: accessors calendar calendar.format io io.encodings.ascii\nio.servers kernel threads ;\nIN: time-server\n\n: handle-time-client ( -- )\n    now timestamp>rfc822 print ;\n\n: <time-server> ( -- threaded-server )\n    ascii <threaded-server>\n        \"time-server\" >>name\n        1234 >>insecure\n        [ handle-time-client ] >>handler ;\n\n: start-time-server ( -- )\n    <time-server> start-server drop ;\n\nMAIN: start-time-server\n</textarea>\n  </form>\n\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    lineNumbers: true,\n    lineWrapping: true,\n    indentUnit: 2,\n    tabSize: 2,\n    autofocus: true,\n    mode: \"text/x-factor\"\n  });\n</script>\n<p/>\n<p>Simple mode that handles Factor Syntax (<a href=\"http://en.wikipedia.org/wiki/Factor_(programming_language)\">Factor on Wikipedia</a>).</p>\n\n<p><strong>MIME types defined:</strong> <code>text/x-factor</code>.</p>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/fcl/fcl.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"fcl\", function(config) {\n  var indentUnit = config.indentUnit;\n\n  var keywords = {\n      \"term\": true,\n      \"method\": true, \"accu\": true,\n      \"rule\": true, \"then\": true, \"is\": true, \"and\": true, \"or\": true,\n      \"if\": true, \"default\": true\n  };\n\n  var start_blocks = {\n      \"var_input\": true,\n      \"var_output\": true,\n      \"fuzzify\": true,\n      \"defuzzify\": true,\n      \"function_block\": true,\n      \"ruleblock\": true\n  };\n\n  var end_blocks = {\n      \"end_ruleblock\": true,\n      \"end_defuzzify\": true,\n      \"end_function_block\": true,\n      \"end_fuzzify\": true,\n      \"end_var\": true\n  };\n\n  var atoms = {\n      \"true\": true, \"false\": true, \"nan\": true,\n      \"real\": true, \"min\": true, \"max\": true, \"cog\": true, \"cogs\": true\n  };\n\n  var isOperatorChar = /[+\\-*&^%:=<>!|\\/]/;\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n\n    if (/[\\d\\.]/.test(ch)) {\n      if (ch == \".\") {\n        stream.match(/^[0-9]+([eE][\\-+]?[0-9]+)?/);\n      } else if (ch == \"0\") {\n        stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);\n      } else {\n        stream.match(/^[0-9]*\\.?[0-9]*([eE][\\-+]?[0-9]+)?/);\n      }\n      return \"number\";\n    }\n\n    if (ch == \"/\" || ch == \"(\") {\n      if (stream.eat(\"*\")) {\n        state.tokenize = tokenComment;\n        return tokenComment(stream, state);\n      }\n      if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n    }\n    if (isOperatorChar.test(ch)) {\n      stream.eatWhile(isOperatorChar);\n      return \"operator\";\n    }\n    stream.eatWhile(/[\\w\\$_\\xa1-\\uffff]/);\n\n    var cur = stream.current().toLowerCase();\n    if (keywords.propertyIsEnumerable(cur) ||\n        start_blocks.propertyIsEnumerable(cur) ||\n        end_blocks.propertyIsEnumerable(cur)) {\n      return \"keyword\";\n    }\n    if (atoms.propertyIsEnumerable(cur)) return \"atom\";\n    return \"variable\";\n  }\n\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if ((ch == \"/\" || ch == \")\") && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n\n  function Context(indented, column, type, align, prev) {\n    this.indented = indented;\n    this.column = column;\n    this.type = type;\n    this.align = align;\n    this.prev = prev;\n  }\n\n  function pushContext(state, col, type) {\n    return state.context = new Context(state.indented, col, type, null, state.context);\n  }\n\n  function popContext(state) {\n    if (!state.context.prev) return;\n    var t = state.context.type;\n    if (t == \"end_block\")\n      state.indented = state.context.indented;\n    return state.context = state.context.prev;\n  }\n\n  // Interface\n\n  return {\n    startState: function(basecolumn) {\n      return {\n        tokenize: null,\n        context: new Context((basecolumn || 0) - indentUnit, 0, \"top\", false),\n        indented: 0,\n        startOfLine: true\n      };\n    },\n\n    token: function(stream, state) {\n        var ctx = state.context;\n        if (stream.sol()) {\n            if (ctx.align == null) ctx.align = false;\n            state.indented = stream.indentation();\n            state.startOfLine = true;\n        }\n        if (stream.eatSpace()) return null;\n\n        var style = (state.tokenize || tokenBase)(stream, state);\n        if (style == \"comment\") return style;\n        if (ctx.align == null) ctx.align = true;\n\n        var cur = stream.current().toLowerCase();\n\n        if (start_blocks.propertyIsEnumerable(cur)) pushContext(state, stream.column(), \"end_block\");\n        else if (end_blocks.propertyIsEnumerable(cur))  popContext(state);\n\n        state.startOfLine = false;\n        return style;\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize != tokenBase && state.tokenize != null) return 0;\n      var ctx = state.context;\n\n      var closing = end_blocks.propertyIsEnumerable(textAfter);\n      if (ctx.align) return ctx.column + (closing ? 0 : 1);\n      else return ctx.indented + (closing ? 0 : indentUnit);\n    },\n\n    electricChars: \"ryk\",\n    fold: \"brace\",\n    blockCommentStart: \"(*\",\n    blockCommentEnd: \"*)\",\n    lineComment: \"//\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-fcl\", \"fcl\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/fcl/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: FCL mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/elegant.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"fcl.js\"></script>\n<style>.CodeMirror {border:1px solid #999; background:#ffc}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">FCL</a>\n  </ul>\n</div>\n\n<article>\n<h2>FCL mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n  FUNCTION_BLOCK Fuzzy_FB\n      VAR_INPUT\n          TimeDay : REAL; (* RANGE(0 .. 23) *)\n          ApplicateHost: REAL;\n          TimeConfiguration: REAL;\n          TimeRequirements: REAL;\n      END_VAR\n\n      VAR_OUTPUT\n          ProbabilityDistribution: REAL;\n          ProbabilityAccess: REAL;\n      END_VAR\n\n      FUZZIFY TimeDay\n          TERM inside := (0, 0) (8, 1) (22,0);\n          TERM outside := (0, 1) (8, 0) (22, 1);\n      END_FUZZIFY\n\n      FUZZIFY ApplicateHost\n          TERM few := (0, 1) (100, 0) (200, 0);\n          TERM many := (0, 0) (100, 0) (200, 1);\n      END_FUZZIFY\n\n      FUZZIFY TimeConfiguration\n          TERM recently := (0, 1) (30, 1) (120, 0);\n          TERM long := (0, 0) (30, 0) (120, 1);\n      END_FUZZIFY\n\n      FUZZIFY TimeRequirements\n          TERM recently := (0, 1) (30, 1) (365, 0);\n          TERM long := (0, 0) (30, 0) (365, 1);\n      END_FUZZIFY\n\n      DEFUZZIFY ProbabilityAccess\n          TERM height := 1;\n          TERM medium := 0.5;\n          TERM low := 0;\n          ACCU: MAX;\n          METHOD: COGS;\n          DEFAULT := 0;\n      END_DEFUZZIFY\n\n      DEFUZZIFY ProbabilityDistribution\n          TERM height := 1;\n          TERM medium := 0.5;\n          TERM low := 0;\n          ACCU: MAX;\n          METHOD: COGS;\n          DEFAULT := 0;\n      END_DEFUZZIFY\n\n      RULEBLOCK No1\n          AND : MIN;\n          RULE 1 : IF TimeDay IS outside AND ApplicateHost IS few THEN ProbabilityAccess IS height;\n          RULE 2 : IF ApplicateHost IS many THEN ProbabilityAccess IS height;\n          RULE 3 : IF TimeDay IS inside AND ApplicateHost IS few THEN ProbabilityAccess IS low;\n      END_RULEBLOCK\n\n      RULEBLOCK No2\n          AND : MIN;\n          RULE 1 : IF ApplicateHost IS many THEN ProbabilityDistribution IS height;\n      END_RULEBLOCK\n\n  END_FUNCTION_BLOCK\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        theme: \"elegant\",\n        matchBrackets: true,\n        indentUnit: 8,\n        tabSize: 8,\n        indentWithTabs: true,\n        mode: \"text/x-fcl\"\n      });\n    </script>\n\n    <p><strong>MIME type:</strong> <code>text/x-fcl</code></p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/forth/forth.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Author: Aliaksei Chapyzhenka\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  function toWordList(words) {\n    var ret = [];\n    words.split(' ').forEach(function(e){\n      ret.push({name: e});\n    });\n    return ret;\n  }\n\n  var coreWordList = toWordList(\n'INVERT AND OR XOR\\\n 2* 2/ LSHIFT RSHIFT\\\n 0= = 0< < > U< MIN MAX\\\n 2DROP 2DUP 2OVER 2SWAP ?DUP DEPTH DROP DUP OVER ROT SWAP\\\n >R R> R@\\\n + - 1+ 1- ABS NEGATE\\\n S>D * M* UM*\\\n FM/MOD SM/REM UM/MOD */ */MOD / /MOD MOD\\\n HERE , @ ! CELL+ CELLS C, C@ C! CHARS 2@ 2!\\\n ALIGN ALIGNED +! ALLOT\\\n CHAR [CHAR] [ ] BL\\\n FIND EXECUTE IMMEDIATE COUNT LITERAL STATE\\\n ; DOES> >BODY\\\n EVALUATE\\\n SOURCE >IN\\\n <# # #S #> HOLD SIGN BASE >NUMBER HEX DECIMAL\\\n FILL MOVE\\\n . CR EMIT SPACE SPACES TYPE U. .R U.R\\\n ACCEPT\\\n TRUE FALSE\\\n <> U> 0<> 0>\\\n NIP TUCK ROLL PICK\\\n 2>R 2R@ 2R>\\\n WITHIN UNUSED MARKER\\\n I J\\\n TO\\\n COMPILE, [COMPILE]\\\n SAVE-INPUT RESTORE-INPUT\\\n PAD ERASE\\\n 2LITERAL DNEGATE\\\n D- D+ D0< D0= D2* D2/ D< D= DMAX DMIN D>S DABS\\\n M+ M*/ D. D.R 2ROT DU<\\\n CATCH THROW\\\n FREE RESIZE ALLOCATE\\\n CS-PICK CS-ROLL\\\n GET-CURRENT SET-CURRENT FORTH-WORDLIST GET-ORDER SET-ORDER\\\n PREVIOUS SEARCH-WORDLIST WORDLIST FIND ALSO ONLY FORTH DEFINITIONS ORDER\\\n -TRAILING /STRING SEARCH COMPARE CMOVE CMOVE> BLANK SLITERAL');\n\n  var immediateWordList = toWordList('IF ELSE THEN BEGIN WHILE REPEAT UNTIL RECURSE [IF] [ELSE] [THEN] ?DO DO LOOP +LOOP UNLOOP LEAVE EXIT AGAIN CASE OF ENDOF ENDCASE');\n\n  CodeMirror.defineMode('forth', function() {\n    function searchWordList (wordList, word) {\n      var i;\n      for (i = wordList.length - 1; i >= 0; i--) {\n        if (wordList[i].name === word.toUpperCase()) {\n          return wordList[i];\n        }\n      }\n      return undefined;\n    }\n  return {\n    startState: function() {\n      return {\n        state: '',\n        base: 10,\n        coreWordList: coreWordList,\n        immediateWordList: immediateWordList,\n        wordList: []\n      };\n    },\n    token: function (stream, stt) {\n      var mat;\n      if (stream.eatSpace()) {\n        return null;\n      }\n      if (stt.state === '') { // interpretation\n        if (stream.match(/^(\\]|:NONAME)(\\s|$)/i)) {\n          stt.state = ' compilation';\n          return 'builtin compilation';\n        }\n        mat = stream.match(/^(\\:)\\s+(\\S+)(\\s|$)+/);\n        if (mat) {\n          stt.wordList.push({name: mat[2].toUpperCase()});\n          stt.state = ' compilation';\n          return 'def' + stt.state;\n        }\n        mat = stream.match(/^(VARIABLE|2VARIABLE|CONSTANT|2CONSTANT|CREATE|POSTPONE|VALUE|WORD)\\s+(\\S+)(\\s|$)+/i);\n        if (mat) {\n          stt.wordList.push({name: mat[2].toUpperCase()});\n          return 'def' + stt.state;\n        }\n        mat = stream.match(/^(\\'|\\[\\'\\])\\s+(\\S+)(\\s|$)+/);\n        if (mat) {\n          return 'builtin' + stt.state;\n        }\n        } else { // compilation\n        // ; [\n        if (stream.match(/^(\\;|\\[)(\\s)/)) {\n          stt.state = '';\n          stream.backUp(1);\n          return 'builtin compilation';\n        }\n        if (stream.match(/^(\\;|\\[)($)/)) {\n          stt.state = '';\n          return 'builtin compilation';\n        }\n        if (stream.match(/^(POSTPONE)\\s+\\S+(\\s|$)+/)) {\n          return 'builtin';\n        }\n      }\n\n      // dynamic wordlist\n      mat = stream.match(/^(\\S+)(\\s+|$)/);\n      if (mat) {\n        if (searchWordList(stt.wordList, mat[1]) !== undefined) {\n          return 'variable' + stt.state;\n        }\n\n        // comments\n        if (mat[1] === '\\\\') {\n          stream.skipToEnd();\n            return 'comment' + stt.state;\n          }\n\n          // core words\n          if (searchWordList(stt.coreWordList, mat[1]) !== undefined) {\n            return 'builtin' + stt.state;\n          }\n          if (searchWordList(stt.immediateWordList, mat[1]) !== undefined) {\n            return 'keyword' + stt.state;\n          }\n\n          if (mat[1] === '(') {\n            stream.eatWhile(function (s) { return s !== ')'; });\n            stream.eat(')');\n            return 'comment' + stt.state;\n          }\n\n          // // strings\n          if (mat[1] === '.(') {\n            stream.eatWhile(function (s) { return s !== ')'; });\n            stream.eat(')');\n            return 'string' + stt.state;\n          }\n          if (mat[1] === 'S\"' || mat[1] === '.\"' || mat[1] === 'C\"') {\n            stream.eatWhile(function (s) { return s !== '\"'; });\n            stream.eat('\"');\n            return 'string' + stt.state;\n          }\n\n          // numbers\n          if (mat[1] - 0xfffffffff) {\n            return 'number' + stt.state;\n          }\n          // if (mat[1].match(/^[-+]?[0-9]+\\.[0-9]*/)) {\n          //     return 'number' + stt.state;\n          // }\n\n          return 'atom' + stt.state;\n        }\n      }\n    };\n  });\n  CodeMirror.defineMIME(\"text/x-forth\", \"forth\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/forth/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Forth mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=stylesheet href=\"../../theme/colorforth.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"forth.js\"></script>\n<style>\n.CodeMirror {\n    font-family: 'Droid Sans Mono', monospace;\n    font-size: 14px;\n}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Forth</a>\n  </ul>\n</div>\n\n<article>\n\n<h2>Forth mode</h2>\n\n<form><textarea id=\"code\" name=\"code\">\n\\ Insertion sort\n\n: cell-  1 cells - ;\n\n: insert ( start end -- start )\n  dup @ >r ( r: v )\n  begin\n    2dup <\n  while\n    r@ over cell- @ <\n  while\n    cell-\n    dup @ over cell+ !\n  repeat then\n  r> swap ! ;\n\n: sort ( array len -- )\n  1 ?do\n    dup i cells + insert\n  loop drop ;</textarea>\n  </form>\n\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    lineNumbers: true,\n    lineWrapping: true,\n    indentUnit: 2,\n    tabSize: 2,\n    autofocus: true,\n    theme: \"colorforth\",\n    mode: \"text/x-forth\"\n  });\n</script>\n\n<p>Simple mode that handle Forth-Syntax (<a href=\"http://en.wikipedia.org/wiki/Forth_%28programming_language%29\">Forth on Wikipedia</a>).</p>\n\n<p><strong>MIME types defined:</strong> <code>text/x-forth</code>.</p>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/fortran/fortran.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"fortran\", function() {\n  function words(array) {\n    var keys = {};\n    for (var i = 0; i < array.length; ++i) {\n      keys[array[i]] = true;\n    }\n    return keys;\n  }\n\n  var keywords = words([\n                  \"abstract\", \"accept\", \"allocatable\", \"allocate\",\n                  \"array\", \"assign\", \"asynchronous\", \"backspace\",\n                  \"bind\", \"block\", \"byte\", \"call\", \"case\",\n                  \"class\", \"close\", \"common\", \"contains\",\n                  \"continue\", \"cycle\", \"data\", \"deallocate\",\n                  \"decode\", \"deferred\", \"dimension\", \"do\",\n                  \"elemental\", \"else\", \"encode\", \"end\",\n                  \"endif\", \"entry\", \"enumerator\", \"equivalence\",\n                  \"exit\", \"external\", \"extrinsic\", \"final\",\n                  \"forall\", \"format\", \"function\", \"generic\",\n                  \"go\", \"goto\", \"if\", \"implicit\", \"import\", \"include\",\n                  \"inquire\", \"intent\", \"interface\", \"intrinsic\",\n                  \"module\", \"namelist\", \"non_intrinsic\",\n                  \"non_overridable\", \"none\", \"nopass\",\n                  \"nullify\", \"open\", \"optional\", \"options\",\n                  \"parameter\", \"pass\", \"pause\", \"pointer\",\n                  \"print\", \"private\", \"program\", \"protected\",\n                  \"public\", \"pure\", \"read\", \"recursive\", \"result\",\n                  \"return\", \"rewind\", \"save\", \"select\", \"sequence\",\n                  \"stop\", \"subroutine\", \"target\", \"then\", \"to\", \"type\",\n                  \"use\", \"value\", \"volatile\", \"where\", \"while\",\n                  \"write\"]);\n  var builtins = words([\"abort\", \"abs\", \"access\", \"achar\", \"acos\",\n                          \"adjustl\", \"adjustr\", \"aimag\", \"aint\", \"alarm\",\n                          \"all\", \"allocated\", \"alog\", \"amax\", \"amin\",\n                          \"amod\", \"and\", \"anint\", \"any\", \"asin\",\n                          \"associated\", \"atan\", \"besj\", \"besjn\", \"besy\",\n                          \"besyn\", \"bit_size\", \"btest\", \"cabs\", \"ccos\",\n                          \"ceiling\", \"cexp\", \"char\", \"chdir\", \"chmod\",\n                          \"clog\", \"cmplx\", \"command_argument_count\",\n                          \"complex\", \"conjg\", \"cos\", \"cosh\", \"count\",\n                          \"cpu_time\", \"cshift\", \"csin\", \"csqrt\", \"ctime\",\n                          \"c_funloc\", \"c_loc\", \"c_associated\", \"c_null_ptr\",\n                          \"c_null_funptr\", \"c_f_pointer\", \"c_null_char\",\n                          \"c_alert\", \"c_backspace\", \"c_form_feed\",\n                          \"c_new_line\", \"c_carriage_return\",\n                          \"c_horizontal_tab\", \"c_vertical_tab\", \"dabs\",\n                          \"dacos\", \"dasin\", \"datan\", \"date_and_time\",\n                          \"dbesj\", \"dbesj\", \"dbesjn\", \"dbesy\", \"dbesy\",\n                          \"dbesyn\", \"dble\", \"dcos\", \"dcosh\", \"ddim\", \"derf\",\n                          \"derfc\", \"dexp\", \"digits\", \"dim\", \"dint\", \"dlog\",\n                          \"dlog\", \"dmax\", \"dmin\", \"dmod\", \"dnint\",\n                          \"dot_product\", \"dprod\", \"dsign\", \"dsinh\",\n                          \"dsin\", \"dsqrt\", \"dtanh\", \"dtan\", \"dtime\",\n                          \"eoshift\", \"epsilon\", \"erf\", \"erfc\", \"etime\",\n                          \"exit\", \"exp\", \"exponent\", \"extends_type_of\",\n                          \"fdate\", \"fget\", \"fgetc\", \"float\", \"floor\",\n                          \"flush\", \"fnum\", \"fputc\", \"fput\", \"fraction\",\n                          \"fseek\", \"fstat\", \"ftell\", \"gerror\", \"getarg\",\n                          \"get_command\", \"get_command_argument\",\n                          \"get_environment_variable\", \"getcwd\",\n                          \"getenv\", \"getgid\", \"getlog\", \"getpid\",\n                          \"getuid\", \"gmtime\", \"hostnm\", \"huge\", \"iabs\",\n                          \"iachar\", \"iand\", \"iargc\", \"ibclr\", \"ibits\",\n                          \"ibset\", \"ichar\", \"idate\", \"idim\", \"idint\",\n                          \"idnint\", \"ieor\", \"ierrno\", \"ifix\", \"imag\",\n                          \"imagpart\", \"index\", \"int\", \"ior\", \"irand\",\n                          \"isatty\", \"ishft\", \"ishftc\", \"isign\",\n                          \"iso_c_binding\", \"is_iostat_end\", \"is_iostat_eor\",\n                          \"itime\", \"kill\", \"kind\", \"lbound\", \"len\", \"len_trim\",\n                          \"lge\", \"lgt\", \"link\", \"lle\", \"llt\", \"lnblnk\", \"loc\",\n                          \"log\", \"logical\", \"long\", \"lshift\", \"lstat\", \"ltime\",\n                          \"matmul\", \"max\", \"maxexponent\", \"maxloc\", \"maxval\",\n                          \"mclock\", \"merge\", \"move_alloc\", \"min\", \"minexponent\",\n                          \"minloc\", \"minval\", \"mod\", \"modulo\", \"mvbits\",\n                          \"nearest\", \"new_line\", \"nint\", \"not\", \"or\", \"pack\",\n                          \"perror\", \"precision\", \"present\", \"product\", \"radix\",\n                          \"rand\", \"random_number\", \"random_seed\", \"range\",\n                          \"real\", \"realpart\", \"rename\", \"repeat\", \"reshape\",\n                          \"rrspacing\", \"rshift\", \"same_type_as\", \"scale\",\n                          \"scan\", \"second\", \"selected_int_kind\",\n                          \"selected_real_kind\", \"set_exponent\", \"shape\",\n                          \"short\", \"sign\", \"signal\", \"sinh\", \"sin\", \"sleep\",\n                          \"sngl\", \"spacing\", \"spread\", \"sqrt\", \"srand\", \"stat\",\n                          \"sum\", \"symlnk\", \"system\", \"system_clock\", \"tan\",\n                          \"tanh\", \"time\", \"tiny\", \"transfer\", \"transpose\",\n                          \"trim\", \"ttynam\", \"ubound\", \"umask\", \"unlink\",\n                          \"unpack\", \"verify\", \"xor\", \"zabs\", \"zcos\", \"zexp\",\n                          \"zlog\", \"zsin\", \"zsqrt\"]);\n\n    var dataTypes =  words([\"c_bool\", \"c_char\", \"c_double\", \"c_double_complex\",\n                     \"c_float\", \"c_float_complex\", \"c_funptr\", \"c_int\",\n                     \"c_int16_t\", \"c_int32_t\", \"c_int64_t\", \"c_int8_t\",\n                     \"c_int_fast16_t\", \"c_int_fast32_t\", \"c_int_fast64_t\",\n                     \"c_int_fast8_t\", \"c_int_least16_t\", \"c_int_least32_t\",\n                     \"c_int_least64_t\", \"c_int_least8_t\", \"c_intmax_t\",\n                     \"c_intptr_t\", \"c_long\", \"c_long_double\",\n                     \"c_long_double_complex\", \"c_long_long\", \"c_ptr\",\n                     \"c_short\", \"c_signed_char\", \"c_size_t\", \"character\",\n                     \"complex\", \"double\", \"integer\", \"logical\", \"real\"]);\n  var isOperatorChar = /[+\\-*&=<>\\/\\:]/;\n  var litOperator = /^\\.(and|or|eq|lt|le|gt|ge|ne|not|eqv|neqv)\\./i;\n\n  function tokenBase(stream, state) {\n\n    if (stream.match(litOperator)){\n        return 'operator';\n    }\n\n    var ch = stream.next();\n    if (ch == \"!\") {\n      stream.skipToEnd();\n      return \"comment\";\n    }\n    if (ch == '\"' || ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    }\n    if (/[\\[\\]\\(\\),]/.test(ch)) {\n      return null;\n    }\n    if (/\\d/.test(ch)) {\n      stream.eatWhile(/[\\w\\.]/);\n      return \"number\";\n    }\n    if (isOperatorChar.test(ch)) {\n      stream.eatWhile(isOperatorChar);\n      return \"operator\";\n    }\n    stream.eatWhile(/[\\w\\$_]/);\n    var word = stream.current().toLowerCase();\n\n    if (keywords.hasOwnProperty(word)){\n            return 'keyword';\n    }\n    if (builtins.hasOwnProperty(word) || dataTypes.hasOwnProperty(word)) {\n            return 'builtin';\n    }\n    return \"variable\";\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {\n            end = true;\n            break;\n        }\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (end || !escaped) state.tokenize = null;\n      return \"string\";\n    };\n  }\n\n  // Interface\n\n  return {\n    startState: function() {\n      return {tokenize: null};\n    },\n\n    token: function(stream, state) {\n      if (stream.eatSpace()) return null;\n      var style = (state.tokenize || tokenBase)(stream, state);\n      if (style == \"comment\" || style == \"meta\") return style;\n      return style;\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-fortran\", \"fortran\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/fortran/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Fortran mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"fortran.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Fortran</a>\n  </ul>\n</div>\n\n<article>\n<h2>Fortran mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n! Example Fortran code\n  program average\n\n  ! Read in some numbers and take the average\n  ! As written, if there are no data points, an average of zero is returned\n  ! While this may not be desired behavior, it keeps this example simple\n\n  implicit none\n\n  real, dimension(:), allocatable :: points\n  integer                         :: number_of_points\n  real                            :: average_points=0., positive_average=0., negative_average=0.\n\n  write (*,*) \"Input number of points to average:\"\n  read  (*,*) number_of_points\n\n  allocate (points(number_of_points))\n\n  write (*,*) \"Enter the points to average:\"\n  read  (*,*) points\n\n  ! Take the average by summing points and dividing by number_of_points\n  if (number_of_points > 0) average_points = sum(points) / number_of_points\n\n  ! Now form average over positive and negative points only\n  if (count(points > 0.) > 0) then\n     positive_average = sum(points, points > 0.) / count(points > 0.)\n  end if\n\n  if (count(points < 0.) > 0) then\n     negative_average = sum(points, points < 0.) / count(points < 0.)\n  end if\n\n  deallocate (points)\n\n  ! Print result to terminal\n  write (*,'(a,g12.4)') 'Average = ', average_points\n  write (*,'(a,g12.4)') 'Average of positive points = ', positive_average\n  write (*,'(a,g12.4)') 'Average of negative points = ', negative_average\n\n  end program average\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        mode: \"text/x-fortran\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-fortran</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/gas/gas.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"gas\", function(_config, parserConfig) {\n  'use strict';\n\n  // If an architecture is specified, its initialization function may\n  // populate this array with custom parsing functions which will be\n  // tried in the event that the standard functions do not find a match.\n  var custom = [];\n\n  // The symbol used to start a line comment changes based on the target\n  // architecture.\n  // If no architecture is pased in \"parserConfig\" then only multiline\n  // comments will have syntax support.\n  var lineCommentStartSymbol = \"\";\n\n  // These directives are architecture independent.\n  // Machine specific directives should go in their respective\n  // architecture initialization function.\n  // Reference:\n  // http://sourceware.org/binutils/docs/as/Pseudo-Ops.html#Pseudo-Ops\n  var directives = {\n    \".abort\" : \"builtin\",\n    \".align\" : \"builtin\",\n    \".altmacro\" : \"builtin\",\n    \".ascii\" : \"builtin\",\n    \".asciz\" : \"builtin\",\n    \".balign\" : \"builtin\",\n    \".balignw\" : \"builtin\",\n    \".balignl\" : \"builtin\",\n    \".bundle_align_mode\" : \"builtin\",\n    \".bundle_lock\" : \"builtin\",\n    \".bundle_unlock\" : \"builtin\",\n    \".byte\" : \"builtin\",\n    \".cfi_startproc\" : \"builtin\",\n    \".comm\" : \"builtin\",\n    \".data\" : \"builtin\",\n    \".def\" : \"builtin\",\n    \".desc\" : \"builtin\",\n    \".dim\" : \"builtin\",\n    \".double\" : \"builtin\",\n    \".eject\" : \"builtin\",\n    \".else\" : \"builtin\",\n    \".elseif\" : \"builtin\",\n    \".end\" : \"builtin\",\n    \".endef\" : \"builtin\",\n    \".endfunc\" : \"builtin\",\n    \".endif\" : \"builtin\",\n    \".equ\" : \"builtin\",\n    \".equiv\" : \"builtin\",\n    \".eqv\" : \"builtin\",\n    \".err\" : \"builtin\",\n    \".error\" : \"builtin\",\n    \".exitm\" : \"builtin\",\n    \".extern\" : \"builtin\",\n    \".fail\" : \"builtin\",\n    \".file\" : \"builtin\",\n    \".fill\" : \"builtin\",\n    \".float\" : \"builtin\",\n    \".func\" : \"builtin\",\n    \".global\" : \"builtin\",\n    \".gnu_attribute\" : \"builtin\",\n    \".hidden\" : \"builtin\",\n    \".hword\" : \"builtin\",\n    \".ident\" : \"builtin\",\n    \".if\" : \"builtin\",\n    \".incbin\" : \"builtin\",\n    \".include\" : \"builtin\",\n    \".int\" : \"builtin\",\n    \".internal\" : \"builtin\",\n    \".irp\" : \"builtin\",\n    \".irpc\" : \"builtin\",\n    \".lcomm\" : \"builtin\",\n    \".lflags\" : \"builtin\",\n    \".line\" : \"builtin\",\n    \".linkonce\" : \"builtin\",\n    \".list\" : \"builtin\",\n    \".ln\" : \"builtin\",\n    \".loc\" : \"builtin\",\n    \".loc_mark_labels\" : \"builtin\",\n    \".local\" : \"builtin\",\n    \".long\" : \"builtin\",\n    \".macro\" : \"builtin\",\n    \".mri\" : \"builtin\",\n    \".noaltmacro\" : \"builtin\",\n    \".nolist\" : \"builtin\",\n    \".octa\" : \"builtin\",\n    \".offset\" : \"builtin\",\n    \".org\" : \"builtin\",\n    \".p2align\" : \"builtin\",\n    \".popsection\" : \"builtin\",\n    \".previous\" : \"builtin\",\n    \".print\" : \"builtin\",\n    \".protected\" : \"builtin\",\n    \".psize\" : \"builtin\",\n    \".purgem\" : \"builtin\",\n    \".pushsection\" : \"builtin\",\n    \".quad\" : \"builtin\",\n    \".reloc\" : \"builtin\",\n    \".rept\" : \"builtin\",\n    \".sbttl\" : \"builtin\",\n    \".scl\" : \"builtin\",\n    \".section\" : \"builtin\",\n    \".set\" : \"builtin\",\n    \".short\" : \"builtin\",\n    \".single\" : \"builtin\",\n    \".size\" : \"builtin\",\n    \".skip\" : \"builtin\",\n    \".sleb128\" : \"builtin\",\n    \".space\" : \"builtin\",\n    \".stab\" : \"builtin\",\n    \".string\" : \"builtin\",\n    \".struct\" : \"builtin\",\n    \".subsection\" : \"builtin\",\n    \".symver\" : \"builtin\",\n    \".tag\" : \"builtin\",\n    \".text\" : \"builtin\",\n    \".title\" : \"builtin\",\n    \".type\" : \"builtin\",\n    \".uleb128\" : \"builtin\",\n    \".val\" : \"builtin\",\n    \".version\" : \"builtin\",\n    \".vtable_entry\" : \"builtin\",\n    \".vtable_inherit\" : \"builtin\",\n    \".warning\" : \"builtin\",\n    \".weak\" : \"builtin\",\n    \".weakref\" : \"builtin\",\n    \".word\" : \"builtin\"\n  };\n\n  var registers = {};\n\n  function x86(_parserConfig) {\n    lineCommentStartSymbol = \"#\";\n\n    registers.al  = \"variable\";\n    registers.ah  = \"variable\";\n    registers.ax  = \"variable\";\n    registers.eax = \"variable-2\";\n    registers.rax = \"variable-3\";\n\n    registers.bl  = \"variable\";\n    registers.bh  = \"variable\";\n    registers.bx  = \"variable\";\n    registers.ebx = \"variable-2\";\n    registers.rbx = \"variable-3\";\n\n    registers.cl  = \"variable\";\n    registers.ch  = \"variable\";\n    registers.cx  = \"variable\";\n    registers.ecx = \"variable-2\";\n    registers.rcx = \"variable-3\";\n\n    registers.dl  = \"variable\";\n    registers.dh  = \"variable\";\n    registers.dx  = \"variable\";\n    registers.edx = \"variable-2\";\n    registers.rdx = \"variable-3\";\n\n    registers.si  = \"variable\";\n    registers.esi = \"variable-2\";\n    registers.rsi = \"variable-3\";\n\n    registers.di  = \"variable\";\n    registers.edi = \"variable-2\";\n    registers.rdi = \"variable-3\";\n\n    registers.sp  = \"variable\";\n    registers.esp = \"variable-2\";\n    registers.rsp = \"variable-3\";\n\n    registers.bp  = \"variable\";\n    registers.ebp = \"variable-2\";\n    registers.rbp = \"variable-3\";\n\n    registers.ip  = \"variable\";\n    registers.eip = \"variable-2\";\n    registers.rip = \"variable-3\";\n\n    registers.cs  = \"keyword\";\n    registers.ds  = \"keyword\";\n    registers.ss  = \"keyword\";\n    registers.es  = \"keyword\";\n    registers.fs  = \"keyword\";\n    registers.gs  = \"keyword\";\n  }\n\n  function armv6(_parserConfig) {\n    // Reference:\n    // http://infocenter.arm.com/help/topic/com.arm.doc.qrc0001l/QRC0001_UAL.pdf\n    // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0301h/DDI0301H_arm1176jzfs_r0p7_trm.pdf\n    lineCommentStartSymbol = \"@\";\n    directives.syntax = \"builtin\";\n\n    registers.r0  = \"variable\";\n    registers.r1  = \"variable\";\n    registers.r2  = \"variable\";\n    registers.r3  = \"variable\";\n    registers.r4  = \"variable\";\n    registers.r5  = \"variable\";\n    registers.r6  = \"variable\";\n    registers.r7  = \"variable\";\n    registers.r8  = \"variable\";\n    registers.r9  = \"variable\";\n    registers.r10 = \"variable\";\n    registers.r11 = \"variable\";\n    registers.r12 = \"variable\";\n\n    registers.sp  = \"variable-2\";\n    registers.lr  = \"variable-2\";\n    registers.pc  = \"variable-2\";\n    registers.r13 = registers.sp;\n    registers.r14 = registers.lr;\n    registers.r15 = registers.pc;\n\n    custom.push(function(ch, stream) {\n      if (ch === '#') {\n        stream.eatWhile(/\\w/);\n        return \"number\";\n      }\n    });\n  }\n\n  var arch = (parserConfig.architecture || \"x86\").toLowerCase();\n  if (arch === \"x86\") {\n    x86(parserConfig);\n  } else if (arch === \"arm\" || arch === \"armv6\") {\n    armv6(parserConfig);\n  }\n\n  function nextUntilUnescaped(stream, end) {\n    var escaped = false, next;\n    while ((next = stream.next()) != null) {\n      if (next === end && !escaped) {\n        return false;\n      }\n      escaped = !escaped && next === \"\\\\\";\n    }\n    return escaped;\n  }\n\n  function clikeComment(stream, state) {\n    var maybeEnd = false, ch;\n    while ((ch = stream.next()) != null) {\n      if (ch === \"/\" && maybeEnd) {\n        state.tokenize = null;\n        break;\n      }\n      maybeEnd = (ch === \"*\");\n    }\n    return \"comment\";\n  }\n\n  return {\n    startState: function() {\n      return {\n        tokenize: null\n      };\n    },\n\n    token: function(stream, state) {\n      if (state.tokenize) {\n        return state.tokenize(stream, state);\n      }\n\n      if (stream.eatSpace()) {\n        return null;\n      }\n\n      var style, cur, ch = stream.next();\n\n      if (ch === \"/\") {\n        if (stream.eat(\"*\")) {\n          state.tokenize = clikeComment;\n          return clikeComment(stream, state);\n        }\n      }\n\n      if (ch === lineCommentStartSymbol) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n\n      if (ch === '\"') {\n        nextUntilUnescaped(stream, '\"');\n        return \"string\";\n      }\n\n      if (ch === '.') {\n        stream.eatWhile(/\\w/);\n        cur = stream.current().toLowerCase();\n        style = directives[cur];\n        return style || null;\n      }\n\n      if (ch === '=') {\n        stream.eatWhile(/\\w/);\n        return \"tag\";\n      }\n\n      if (ch === '{') {\n        return \"bracket\";\n      }\n\n      if (ch === '}') {\n        return \"bracket\";\n      }\n\n      if (/\\d/.test(ch)) {\n        if (ch === \"0\" && stream.eat(\"x\")) {\n          stream.eatWhile(/[0-9a-fA-F]/);\n          return \"number\";\n        }\n        stream.eatWhile(/\\d/);\n        return \"number\";\n      }\n\n      if (/\\w/.test(ch)) {\n        stream.eatWhile(/\\w/);\n        if (stream.eat(\":\")) {\n          return 'tag';\n        }\n        cur = stream.current().toLowerCase();\n        style = registers[cur];\n        return style || null;\n      }\n\n      for (var i = 0; i < custom.length; i++) {\n        style = custom[i](ch, stream, state);\n        if (style) {\n          return style;\n        }\n      }\n    },\n\n    lineComment: lineCommentStartSymbol,\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\"\n  };\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/gas/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Gas mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"gas.js\"></script>\n<style>.CodeMirror {border: 2px inset #dee;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Gas</a>\n  </ul>\n</div>\n\n<article>\n<h2>Gas mode</h2>\n<form>\n<textarea id=\"code\" name=\"code\">\n.syntax unified\n.global main\n\n/* \n *  A\n *  multi-line\n *  comment.\n */\n\n@ A single line comment.\n\nmain:\n        push    {sp, lr}\n        ldr     r0, =message\n        bl      puts\n        mov     r0, #0\n        pop     {sp, pc}\n\nmessage:\n        .asciz \"Hello world!<br />\"\n</textarea>\n        </form>\n\n        <script>\n            var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n                lineNumbers: true,\n                mode: {name: \"gas\", architecture: \"ARMv6\"},\n            });\n        </script>\n\n        <p>Handles AT&amp;T assembler syntax (more specifically this handles\n        the GNU Assembler (gas) syntax.)\n        It takes a single optional configuration parameter:\n        <code>architecture</code>, which can be one of <code>\"ARM\"</code>,\n        <code>\"ARMv6\"</code> or <code>\"x86\"</code>.\n        Including the parameter adds syntax for the registers and special\n        directives for the supplied architecture.\n\n        <p><strong>MIME types defined:</strong> <code>text/x-gas</code></p>\n    </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/gfm/gfm.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../markdown/markdown\"), require(\"../../addon/mode/overlay\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../markdown/markdown\", \"../../addon/mode/overlay\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nvar urlRE = /^((?:(?:aaas?|about|acap|adiumxtra|af[ps]|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|cap|chrome(?:-extension)?|cid|coap|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-(?:playcontainer|playsingle)|dns|doi|dtn|dvb|ed2k|facetime|feed|file|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|hcp|https?|iax|icap|icon|im|imap|info|ipn|ipp|irc[6s]?|iris(?:\\.beep|\\.lwz|\\.xpc|\\.xpcs)?|itms|jar|javascript|jms|keyparc|lastfm|ldaps?|magnet|mailto|maps|market|message|mid|mms|ms-help|msnim|msrps?|mtqp|mumble|mupdate|mvn|news|nfs|nih?|nntp|notes|oid|opaquelocktoken|palm|paparazzi|platform|pop|pres|proxy|psyc|query|res(?:ource)?|rmi|rsync|rtmp|rtsp|secondlife|service|session|sftp|sgn|shttp|sieve|sips?|skype|sm[bs]|snmp|soap\\.beeps?|soldat|spotify|ssh|steam|svn|tag|teamspeak|tel(?:net)?|tftp|things|thismessage|tip|tn3270|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|view-source|webcal|wss?|wtai|wyciwyg|xcon(?:-userid)?|xfire|xmlrpc\\.beeps?|xmpp|xri|ymsgr|z39\\.50[rs]?):(?:\\/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}\\/)(?:[^\\s()<>]|\\([^\\s()<>]*\\))+(?:\\([^\\s()<>]*\\)|[^\\s`*!()\\[\\]{};:'\".,<>?«»“”‘’]))/i\n\nCodeMirror.defineMode(\"gfm\", function(config, modeConfig) {\n  var codeDepth = 0;\n  function blankLine(state) {\n    state.code = false;\n    return null;\n  }\n  var gfmOverlay = {\n    startState: function() {\n      return {\n        code: false,\n        codeBlock: false,\n        ateSpace: false\n      };\n    },\n    copyState: function(s) {\n      return {\n        code: s.code,\n        codeBlock: s.codeBlock,\n        ateSpace: s.ateSpace\n      };\n    },\n    token: function(stream, state) {\n      state.combineTokens = null;\n\n      // Hack to prevent formatting override inside code blocks (block and inline)\n      if (state.codeBlock) {\n        if (stream.match(/^```+/)) {\n          state.codeBlock = false;\n          return null;\n        }\n        stream.skipToEnd();\n        return null;\n      }\n      if (stream.sol()) {\n        state.code = false;\n      }\n      if (stream.sol() && stream.match(/^```+/)) {\n        stream.skipToEnd();\n        state.codeBlock = true;\n        return null;\n      }\n      // If this block is changed, it may need to be updated in Markdown mode\n      if (stream.peek() === '`') {\n        stream.next();\n        var before = stream.pos;\n        stream.eatWhile('`');\n        var difference = 1 + stream.pos - before;\n        if (!state.code) {\n          codeDepth = difference;\n          state.code = true;\n        } else {\n          if (difference === codeDepth) { // Must be exact\n            state.code = false;\n          }\n        }\n        return null;\n      } else if (state.code) {\n        stream.next();\n        return null;\n      }\n      // Check if space. If so, links can be formatted later on\n      if (stream.eatSpace()) {\n        state.ateSpace = true;\n        return null;\n      }\n      if (stream.sol() || state.ateSpace) {\n        state.ateSpace = false;\n        if (modeConfig.gitHubSpice !== false) {\n          if(stream.match(/^(?:[a-zA-Z0-9\\-_]+\\/)?(?:[a-zA-Z0-9\\-_]+@)?(?=.{0,6}\\d)(?:[a-f0-9]{7,40}\\b)/)) {\n            // User/Project@SHA\n            // User@SHA\n            // SHA\n            state.combineTokens = true;\n            return \"link\";\n          } else if (stream.match(/^(?:[a-zA-Z0-9\\-_]+\\/)?(?:[a-zA-Z0-9\\-_]+)?#[0-9]+\\b/)) {\n            // User/Project#Num\n            // User#Num\n            // #Num\n            state.combineTokens = true;\n            return \"link\";\n          }\n        }\n      }\n      if (stream.match(urlRE) &&\n          stream.string.slice(stream.start - 2, stream.start) != \"](\" &&\n          (stream.start == 0 || /\\W/.test(stream.string.charAt(stream.start - 1)))) {\n        // URLs\n        // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls\n        // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine\n        // And then limited url schemes to the CommonMark list, so foo:bar isn't matched as a URL\n        state.combineTokens = true;\n        return \"link\";\n      }\n      stream.next();\n      return null;\n    },\n    blankLine: blankLine\n  };\n\n  var markdownConfig = {\n    taskLists: true,\n    strikethrough: true,\n    emoji: true\n  };\n  for (var attr in modeConfig) {\n    markdownConfig[attr] = modeConfig[attr];\n  }\n  markdownConfig.name = \"markdown\";\n  return CodeMirror.overlayMode(CodeMirror.getMode(config, markdownConfig), gfmOverlay);\n\n}, \"markdown\");\n\n  CodeMirror.defineMIME(\"text/x-gfm\", \"gfm\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/gfm/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: GFM mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/mode/overlay.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../markdown/markdown.js\"></script>\n<script src=\"gfm.js\"></script>\n<script src=\"../javascript/javascript.js\"></script>\n<script src=\"../css/css.js\"></script>\n<script src=\"../htmlmixed/htmlmixed.js\"></script>\n<script src=\"../clike/clike.js\"></script>\n<script src=\"../meta.js\"></script>\n<style>\n  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n  .cm-s-default .cm-emoji {color: #009688;}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">GFM</a>\n  </ul>\n</div>\n\n<article>\n<h2>GFM mode</h2>\n<form><textarea id=\"code\" name=\"code\">\nGitHub Flavored Markdown\n========================\n\nEverything from markdown plus GFM features:\n\n## URL autolinking\n\nUnderscores_are_allowed_between_words.\n\n## Strikethrough text\n\nGFM adds syntax to strikethrough text, which is missing from standard Markdown.\n\n~~Mistaken text.~~\n~~**works with other formatting**~~\n\n~~spans across\nlines~~\n\n## Fenced code blocks (and syntax highlighting)\n\n```javascript\nfor (var i = 0; i &lt; items.length; i++) {\n    console.log(items[i], i); // log them\n}\n```\n\n## Task Lists\n\n- [ ] Incomplete task list item\n- [x] **Completed** task list item\n\n## A bit of GitHub spice\n\nSee http://github.github.com/github-flavored-markdown/.\n\n(Set `gitHubSpice: false` in mode options to disable):\n\n* SHA: be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2\n* User@SHA ref: mojombo@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2\n* User/Project@SHA: mojombo/god@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2\n* \\#Num: #1\n* User/#Num: mojombo#1\n* User/Project#Num: mojombo/god#1\n\n(Set `emoji: false` in mode options to disable):\n\n* emoji: :smile:\n\n\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: {\n          name: \"gfm\",\n          tokenTypeOverrides: {\n            emoji: \"emoji\"\n          }\n        },\n        lineNumbers: true,\n        theme: \"default\"\n      });\n    </script>\n\n    <p>Optionally depends on other modes for properly highlighted code blocks.</p>\n\n    <p>Gfm mode supports these options (apart those from base Markdown mode):</p>\n    <ul>\n      <li>\n        <d1>\n          <dt><code>gitHubSpice: boolean</code></dt>\n          <dd>Hashes, issues... (default: <code>true</code>).</dd>\n        </d1>\n      </li>\n      <li>\n        <d1>\n          <dt><code>taskLists: boolean</code></dt>\n          <dd><code>- [ ]</code> syntax (default: <code>true</code>).</dd>\n        </d1>\n      </li>\n      <li>\n        <d1>\n          <dt><code>strikethrough: boolean</code></dt>\n          <dd><code>~~foo~~</code> syntax (default: <code>true</code>).</dd>\n        </d1>\n      </li>\n      <li>\n        <d1>\n          <dt><code>emoji: boolean</code></dt>\n          <dd><code>:emoji:</code> syntax (default: <code>true</code>).</dd>\n        </d1>\n      </li>\n    </ul>\n\n    <p><strong>Parsing/Highlighting Tests:</strong> <a href=\"../../test/index.html#gfm_*\">normal</a>,  <a href=\"../../test/index.html#verbose,gfm_*\">verbose</a>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/gfm/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var config = {tabSize: 4, indentUnit: 2}\n  var mode = CodeMirror.getMode(config, \"gfm\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n  var modeHighlightFormatting = CodeMirror.getMode(config, {name: \"gfm\", highlightFormatting: true});\n  function FT(name) { test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1)); }\n\n  FT(\"codeBackticks\",\n     \"[comment&formatting&formatting-code `][comment foo][comment&formatting&formatting-code `]\");\n\n  FT(\"doubleBackticks\",\n     \"[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]\");\n\n  FT(\"taskList\",\n     \"[variable-2&formatting&formatting-list&formatting-list-ul - ][meta&formatting&formatting-task [ ]]][variable-2  foo]\",\n     \"[variable-2&formatting&formatting-list&formatting-list-ul - ][property&formatting&formatting-task [x]]][variable-2  foo]\");\n\n  FT(\"formatting_strikethrough\",\n     \"[strikethrough&formatting&formatting-strikethrough ~~][strikethrough foo][strikethrough&formatting&formatting-strikethrough ~~]\");\n\n  FT(\"formatting_strikethrough\",\n     \"foo [strikethrough&formatting&formatting-strikethrough ~~][strikethrough bar][strikethrough&formatting&formatting-strikethrough ~~]\");\n\n  FT(\"formatting_emoji\",\n     \"foo [builtin&formatting&formatting-emoji :smile:] foo\");\n\n  MT(\"emInWordAsterisk\",\n     \"foo[em *bar*]hello\");\n\n  MT(\"emInWordUnderscore\",\n     \"foo_bar_hello\");\n\n  MT(\"emStrongUnderscore\",\n     \"[em&strong ___foo___] bar\");\n\n  MT(\"taskListAsterisk\",\n     \"[variable-2 * ][link&variable-2 [[]]][variable-2 foo]\", // Invalid; must have space or x between []\n     \"[variable-2 * ][link&variable-2 [[ ]]][variable-2 bar]\", // Invalid; must have space after ]\n     \"[variable-2 * ][link&variable-2 [[x]]][variable-2 hello]\", // Invalid; must have space after ]\n     \"[variable-2 * ][meta [ ]]][variable-2  ][link&variable-2 [[world]]]\", // Valid; tests reference style links\n     \"    [variable-3 * ][property [x]]][variable-3  foo]\"); // Valid; can be nested\n\n  MT(\"taskListPlus\",\n     \"[variable-2 + ][link&variable-2 [[]]][variable-2 foo]\", // Invalid; must have space or x between []\n     \"[variable-2 + ][link&variable-2 [[x]]][variable-2 hello]\", // Invalid; must have space after ]\n     \"[variable-2 + ][meta [ ]]][variable-2  ][link&variable-2 [[world]]]\", // Valid; tests reference style links\n     \"    [variable-3 + ][property [x]]][variable-3  foo]\"); // Valid; can be nested\n\n  MT(\"taskListDash\",\n     \"[variable-2 - ][link&variable-2 [[]]][variable-2 foo]\", // Invalid; must have space or x between []\n     \"[variable-2 - ][link&variable-2 [[x]]][variable-2 hello]\", // Invalid; must have space after ]\n     \"[variable-2 - ][meta [ ]]][variable-2  world]\", // Valid; tests reference style links\n     \"    [variable-3 - ][property [x]]][variable-3  foo]\"); // Valid; can be nested\n\n  MT(\"taskListNumber\",\n     \"[variable-2 1. ][link&variable-2 [[]]][variable-2 foo]\", // Invalid; must have space or x between []\n     \"[variable-2 2. ][link&variable-2 [[ ]]][variable-2 bar]\", // Invalid; must have space after ]\n     \"[variable-2 3. ][meta [ ]]][variable-2  world]\", // Valid; tests reference style links\n     \"    [variable-3 1. ][property [x]]][variable-3  foo]\"); // Valid; can be nested\n\n  MT(\"SHA\",\n     \"foo [link be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2] bar\");\n\n  MT(\"SHAEmphasis\",\n     \"[em *foo ][em&link be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2][em *]\");\n\n  MT(\"shortSHA\",\n     \"foo [link be6a8cc] bar\");\n\n  MT(\"tooShortSHA\",\n     \"foo be6a8c bar\");\n\n  MT(\"longSHA\",\n     \"foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd22 bar\");\n\n  MT(\"badSHA\",\n     \"foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cg2 bar\");\n\n  MT(\"userSHA\",\n     \"foo [link bar@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2] hello\");\n\n  MT(\"userSHAEmphasis\",\n     \"[em *foo ][em&link bar@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2][em *]\");\n\n  MT(\"userProjectSHA\",\n     \"foo [link bar/hello@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2] world\");\n\n  MT(\"userProjectSHAEmphasis\",\n     \"[em *foo ][em&link bar/hello@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2][em *]\");\n\n  MT(\"wordSHA\",\n     \"ask for feedback\")\n\n  MT(\"num\",\n     \"foo [link #1] bar\");\n\n  MT(\"numEmphasis\",\n     \"[em *foo ][em&link #1][em *]\");\n\n  MT(\"badNum\",\n     \"foo #1bar hello\");\n\n  MT(\"userNum\",\n     \"foo [link bar#1] hello\");\n\n  MT(\"userNumEmphasis\",\n     \"[em *foo ][em&link bar#1][em *]\");\n\n  MT(\"userProjectNum\",\n     \"foo [link bar/hello#1] world\");\n\n  MT(\"userProjectNumEmphasis\",\n     \"[em *foo ][em&link bar/hello#1][em *]\");\n\n  MT(\"vanillaLink\",\n     \"foo [link http://www.example.com/] bar\");\n\n  MT(\"vanillaLinkNoScheme\",\n     \"foo [link www.example.com] bar\");\n\n  MT(\"vanillaLinkHttps\",\n     \"foo [link https://www.example.com/] bar\");\n\n  MT(\"vanillaLinkDataSchema\",\n     \"foo [link data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==] bar\");\n\n  MT(\"vanillaLinkPunctuation\",\n     \"foo [link http://www.example.com/]. bar\");\n\n  MT(\"vanillaLinkExtension\",\n     \"foo [link http://www.example.com/index.html] bar\");\n\n  MT(\"vanillaLinkEmphasis\",\n     \"foo [em *][em&link http://www.example.com/index.html][em *] bar\");\n\n  MT(\"notALink\",\n     \"foo asfd:asdf bar\");\n\n  MT(\"notALink\",\n     \"[comment ``foo `bar` http://www.example.com/``] hello\");\n\n  MT(\"notALink\",\n     \"[comment `foo]\",\n     \"[comment&link http://www.example.com/]\",\n     \"[comment `] foo\",\n     \"\",\n     \"[link http://www.example.com/]\");\n\n  MT(\"strikethrough\",\n     \"[strikethrough ~~foo~~]\");\n\n  MT(\"strikethroughWithStartingSpace\",\n     \"~~ foo~~\");\n\n  MT(\"strikethroughUnclosedStrayTildes\",\n    \"[strikethrough ~~foo~~~]\");\n\n  MT(\"strikethroughUnclosedStrayTildes\",\n     \"[strikethrough ~~foo ~~]\");\n\n  MT(\"strikethroughUnclosedStrayTildes\",\n    \"[strikethrough ~~foo ~~ bar]\");\n\n  MT(\"strikethroughUnclosedStrayTildes\",\n    \"[strikethrough ~~foo ~~ bar~~]hello\");\n\n  MT(\"strikethroughOneLetter\",\n     \"[strikethrough ~~a~~]\");\n\n  MT(\"strikethroughWrapped\",\n     \"[strikethrough ~~foo]\",\n     \"[strikethrough foo~~]\");\n\n  MT(\"strikethroughParagraph\",\n     \"[strikethrough ~~foo]\",\n     \"\",\n     \"foo[strikethrough ~~bar]\");\n\n  MT(\"strikethroughEm\",\n     \"[strikethrough ~~foo][em&strikethrough *bar*][strikethrough ~~]\");\n\n  MT(\"strikethroughEm\",\n     \"[em *][em&strikethrough ~~foo~~][em *]\");\n\n  MT(\"strikethroughStrong\",\n     \"[strikethrough ~~][strong&strikethrough **foo**][strikethrough ~~]\");\n\n  MT(\"strikethroughStrong\",\n     \"[strong **][strong&strikethrough ~~foo~~][strong **]\");\n\n  MT(\"emoji\",\n     \"text [builtin :blush:] text [builtin :v:] text [builtin :+1:] text\",\n     \":text text: [builtin :smiley_cat:]\");\n\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/gherkin/gherkin.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/*\nGherkin mode - http://www.cukes.info/\nReport bugs/issues here: https://github.com/codemirror/CodeMirror/issues\n*/\n\n// Following Objs from Brackets implementation: https://github.com/tregusti/brackets-gherkin/blob/master/main.js\n//var Quotes = {\n//  SINGLE: 1,\n//  DOUBLE: 2\n//};\n\n//var regex = {\n//  keywords: /(Feature| {2}(Scenario|In order to|As|I)| {4}(Given|When|Then|And))/\n//};\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"gherkin\", function () {\n  return {\n    startState: function () {\n      return {\n        lineNumber: 0,\n        tableHeaderLine: false,\n        allowFeature: true,\n        allowBackground: false,\n        allowScenario: false,\n        allowSteps: false,\n        allowPlaceholders: false,\n        allowMultilineArgument: false,\n        inMultilineString: false,\n        inMultilineTable: false,\n        inKeywordLine: false\n      };\n    },\n    token: function (stream, state) {\n      if (stream.sol()) {\n        state.lineNumber++;\n        state.inKeywordLine = false;\n        if (state.inMultilineTable) {\n            state.tableHeaderLine = false;\n            if (!stream.match(/\\s*\\|/, false)) {\n              state.allowMultilineArgument = false;\n              state.inMultilineTable = false;\n            }\n        }\n      }\n\n      stream.eatSpace();\n\n      if (state.allowMultilineArgument) {\n\n        // STRING\n        if (state.inMultilineString) {\n          if (stream.match('\"\"\"')) {\n            state.inMultilineString = false;\n            state.allowMultilineArgument = false;\n          } else {\n            stream.match(/.*/);\n          }\n          return \"string\";\n        }\n\n        // TABLE\n        if (state.inMultilineTable) {\n          if (stream.match(/\\|\\s*/)) {\n            return \"bracket\";\n          } else {\n            stream.match(/[^\\|]*/);\n            return state.tableHeaderLine ? \"header\" : \"string\";\n          }\n        }\n\n        // DETECT START\n        if (stream.match('\"\"\"')) {\n          // String\n          state.inMultilineString = true;\n          return \"string\";\n        } else if (stream.match(\"|\")) {\n          // Table\n          state.inMultilineTable = true;\n          state.tableHeaderLine = true;\n          return \"bracket\";\n        }\n\n      }\n\n      // LINE COMMENT\n      if (stream.match(/#.*/)) {\n        return \"comment\";\n\n      // TAG\n      } else if (!state.inKeywordLine && stream.match(/@\\S+/)) {\n        return \"tag\";\n\n      // FEATURE\n      } else if (!state.inKeywordLine && state.allowFeature && stream.match(/(機能|功能|フィーチャ|기능|โครงหลัก|ความสามารถ|ความต้องการทางธุรกิจ|ಹೆಚ್ಚಳ|గుణము|ਮੁਹਾਂਦਰਾ|ਨਕਸ਼ ਨੁਹਾਰ|ਖਾਸੀਅਤ|रूप लेख|وِیژگی|خاصية|תכונה|Функціонал|Функция|Функционалност|Функционал|Үзенчәлеклелек|Свойство|Особина|Мөмкинлек|Могућност|Λειτουργία|Δυνατότητα|Właściwość|Vlastnosť|Trajto|Tính năng|Savybė|Pretty much|Požiadavka|Požadavek|Potrzeba biznesowa|Özellik|Osobina|Ominaisuus|Omadus|OH HAI|Mogućnost|Mogucnost|Jellemző|Hwæt|Hwaet|Funzionalità|Funktionalitéit|Funktionalität|Funkcja|Funkcionalnost|Funkcionalitāte|Funkcia|Fungsi|Functionaliteit|Funcționalitate|Funcţionalitate|Functionalitate|Funcionalitat|Funcionalidade|Fonctionnalité|Fitur|Fīča|Feature|Eiginleiki|Egenskap|Egenskab|Característica|Caracteristica|Business Need|Aspekt|Arwedd|Ahoy matey!|Ability):/)) {\n        state.allowScenario = true;\n        state.allowBackground = true;\n        state.allowPlaceholders = false;\n        state.allowSteps = false;\n        state.allowMultilineArgument = false;\n        state.inKeywordLine = true;\n        return \"keyword\";\n\n      // BACKGROUND\n      } else if (!state.inKeywordLine && state.allowBackground && stream.match(/(背景|배경|แนวคิด|ಹಿನ್ನೆಲೆ|నేపథ్యం|ਪਿਛੋਕੜ|पृष्ठभूमि|زمینه|الخلفية|רקע|Тарих|Предыстория|Предистория|Позадина|Передумова|Основа|Контекст|Кереш|Υπόβαθρο|Założenia|Yo\\-ho\\-ho|Tausta|Taust|Situācija|Rerefons|Pozadina|Pozadie|Pozadí|Osnova|Latar Belakang|Kontext|Konteksts|Kontekstas|Kontekst|Háttér|Hannergrond|Grundlage|Geçmiş|Fundo|Fono|First off|Dis is what went down|Dasar|Contexto|Contexte|Context|Contesto|Cenário de Fundo|Cenario de Fundo|Cefndir|Bối cảnh|Bakgrunnur|Bakgrunn|Bakgrund|Baggrund|Background|B4|Antecedents|Antecedentes|Ær|Aer|Achtergrond):/)) {\n        state.allowPlaceholders = false;\n        state.allowSteps = true;\n        state.allowBackground = false;\n        state.allowMultilineArgument = false;\n        state.inKeywordLine = true;\n        return \"keyword\";\n\n      // SCENARIO OUTLINE\n      } else if (!state.inKeywordLine && state.allowScenario && stream.match(/(場景大綱|场景大纲|劇本大綱|剧本大纲|テンプレ|シナリオテンプレート|シナリオテンプレ|シナリオアウトライン|시나리오 개요|สรุปเหตุการณ์|โครงสร้างของเหตุการณ์|ವಿವರಣೆ|కథనం|ਪਟਕਥਾ ਰੂਪ ਰੇਖਾ|ਪਟਕਥਾ ਢਾਂਚਾ|परिदृश्य रूपरेखा|سيناريو مخطط|الگوی سناریو|תבנית תרחיש|Сценарийның төзелеше|Сценарий структураси|Структура сценарію|Структура сценария|Структура сценарија|Скица|Рамка на сценарий|Концепт|Περιγραφή Σεναρίου|Wharrimean is|Template Situai|Template Senario|Template Keadaan|Tapausaihio|Szenariogrundriss|Szablon scenariusza|Swa hwær swa|Swa hwaer swa|Struktura scenarija|Structură scenariu|Structura scenariu|Skica|Skenario konsep|Shiver me timbers|Senaryo taslağı|Schema dello scenario|Scenariomall|Scenariomal|Scenario Template|Scenario Outline|Scenario Amlinellol|Scenārijs pēc parauga|Scenarijaus šablonas|Reckon it's like|Raamstsenaarium|Plang vum Szenario|Plan du Scénario|Plan du scénario|Osnova scénáře|Osnova Scenára|Náčrt Scenáru|Náčrt Scénáře|Náčrt Scenára|MISHUN SRSLY|Menggariskan Senario|Lýsing Dæma|Lýsing Atburðarásar|Konturo de la scenaro|Koncept|Khung tình huống|Khung kịch bản|Forgatókönyv vázlat|Esquema do Cenário|Esquema do Cenario|Esquema del escenario|Esquema de l'escenari|Esbozo do escenario|Delineação do Cenário|Delineacao do Cenario|All y'all|Abstrakt Scenario|Abstract Scenario):/)) {\n        state.allowPlaceholders = true;\n        state.allowSteps = true;\n        state.allowMultilineArgument = false;\n        state.inKeywordLine = true;\n        return \"keyword\";\n\n      // EXAMPLES\n      } else if (state.allowScenario && stream.match(/(例子|例|サンプル|예|ชุดของเหตุการณ์|ชุดของตัวอย่าง|ಉದಾಹರಣೆಗಳು|ఉదాహరణలు|ਉਦਾਹਰਨਾਂ|उदाहरण|نمونه ها|امثلة|דוגמאות|Үрнәкләр|Сценарији|Примеры|Примери|Приклади|Мисоллар|Мисаллар|Σενάρια|Παραδείγματα|You'll wanna|Voorbeelden|Variantai|Tapaukset|Se þe|Se the|Se ðe|Scenarios|Scenariji|Scenarijai|Przykłady|Primjeri|Primeri|Příklady|Príklady|Piemēri|Példák|Pavyzdžiai|Paraugs|Örnekler|Juhtumid|Exemplos|Exemples|Exemple|Exempel|EXAMPLZ|Examples|Esempi|Enghreifftiau|Ekzemploj|Eksempler|Ejemplos|Dữ liệu|Dead men tell no tales|Dæmi|Contoh|Cenários|Cenarios|Beispiller|Beispiele|Atburðarásir):/)) {\n        state.allowPlaceholders = false;\n        state.allowSteps = true;\n        state.allowBackground = false;\n        state.allowMultilineArgument = true;\n        return \"keyword\";\n\n      // SCENARIO\n      } else if (!state.inKeywordLine && state.allowScenario && stream.match(/(場景|场景|劇本|剧本|シナリオ|시나리오|เหตุการณ์|ಕಥಾಸಾರಾಂಶ|సన్నివేశం|ਪਟਕਥਾ|परिदृश्य|سيناريو|سناریو|תרחיש|Сценарій|Сценарио|Сценарий|Пример|Σενάριο|Tình huống|The thing of it is|Tapaus|Szenario|Swa|Stsenaarium|Skenario|Situai|Senaryo|Senario|Scenaro|Scenariusz|Scenariu|Scénario|Scenario|Scenarijus|Scenārijs|Scenarij|Scenarie|Scénář|Scenár|Primer|MISHUN|Kịch bản|Keadaan|Heave to|Forgatókönyv|Escenario|Escenari|Cenário|Cenario|Awww, look mate|Atburðarás):/)) {\n        state.allowPlaceholders = false;\n        state.allowSteps = true;\n        state.allowBackground = false;\n        state.allowMultilineArgument = false;\n        state.inKeywordLine = true;\n        return \"keyword\";\n\n      // STEPS\n      } else if (!state.inKeywordLine && state.allowSteps && stream.match(/(那麼|那么|而且|當|当|并且|同時|同时|前提|假设|假設|假定|假如|但是|但し|並且|もし|ならば|ただし|しかし|かつ|하지만|조건|먼저|만일|만약|단|그리고|그러면|และ |เมื่อ |แต่ |ดังนั้น |กำหนดให้ |ಸ್ಥಿತಿಯನ್ನು |ಮತ್ತು |ನೀಡಿದ |ನಂತರ |ಆದರೆ |మరియు |చెప్పబడినది |కాని |ఈ పరిస్థితిలో |అప్పుడు |ਪਰ |ਤਦ |ਜੇਕਰ |ਜਿਵੇਂ ਕਿ |ਜਦੋਂ |ਅਤੇ |यदि |परन्तु |पर |तब |तदा |तथा |जब |चूंकि |किन्तु |कदा |और |अगर |و |هنگامی |متى |لكن |عندما |ثم |بفرض |با فرض |اما |اذاً |آنگاه |כאשר |וגם |בהינתן |אזי |אז |אבל |Якщо |Һәм |Унда |Тоді |Тогда |То |Также |Та |Пусть |Припустимо, що |Припустимо |Онда |Но |Нехай |Нәтиҗәдә |Лекин |Ләкин |Коли |Когда |Когато |Када |Кад |К тому же |І |И |Задато |Задати |Задате |Если |Допустим |Дано |Дадено |Вә |Ва |Бирок |Әмма |Әйтик |Әгәр |Аммо |Али |Але |Агар |А також |А |Τότε |Όταν |Και |Δεδομένου |Αλλά |Þurh |Þegar |Þa þe |Þá |Þa |Zatati |Zakładając |Zadato |Zadate |Zadano |Zadani |Zadan |Za předpokladu |Za predpokladu |Youse know when youse got |Youse know like when |Yna |Yeah nah |Y'know |Y |Wun |Wtedy |When y'all |When |Wenn |WEN |wann |Ve |Và |Und |Un |ugeholl |Too right |Thurh |Thì |Then y'all |Then |Tha the |Tha |Tetapi |Tapi |Tak |Tada |Tad |Stel |Soit |Siis |Și |Şi |Si |Sed |Se |Så |Quando |Quand |Quan |Pryd |Potom |Pokud |Pokiaľ |Però |Pero |Pak |Oraz |Onda |Ond |Oletetaan |Og |Och |O zaman |Niin |Nhưng |När |Når |Mutta |Men |Mas |Maka |Majd |Mając |Mais |Maar |mä |Ma |Lorsque |Lorsqu'|Logo |Let go and haul |Kun |Kuid |Kui |Kiedy |Khi |Ketika |Kemudian |Keď |Když |Kaj |Kai |Kada |Kad |Jeżeli |Jeśli |Ja |It's just unbelievable |Ir |I CAN HAZ |I |Ha |Givun |Givet |Given y'all |Given |Gitt |Gegeven |Gegeben seien |Gegeben sei |Gdy |Gangway! |Fakat |Étant donnés |Etant donnés |Étant données |Etant données |Étant donnée |Etant donnée |Étant donné |Etant donné |Et |És |Entonces |Entón |Então |Entao |En |Eğer ki |Ef |Eeldades |E |Ðurh |Duota |Dun |Donitaĵo |Donat |Donada |Do |Diyelim ki |Diberi |Dengan |Den youse gotta |DEN |De |Dato |Dați fiind |Daţi fiind |Dati fiind |Dati |Date fiind |Date |Data |Dat fiind |Dar |Dann |dann |Dan |Dados |Dado |Dadas |Dada |Ða ðe |Ða |Cuando |Cho |Cando |Când |Cand |Cal |But y'all |But at the end of the day I reckon |BUT |But |Buh |Blimey! |Biết |Bet |Bagi |Aye |awer |Avast! |Atunci |Atesa |Atès |Apabila |Anrhegedig a |Angenommen |And y'all |And |AN |An |an |Amikor |Amennyiben |Ama |Als |Alors |Allora |Ali |Aleshores |Ale |Akkor |Ak |Adott |Ac |Aber |A zároveň |A tiež |A taktiež |A také |A |a |7 |\\* )/)) {\n        state.inStep = true;\n        state.allowPlaceholders = true;\n        state.allowMultilineArgument = true;\n        state.inKeywordLine = true;\n        return \"keyword\";\n\n      // INLINE STRING\n      } else if (stream.match(/\"[^\"]*\"?/)) {\n        return \"string\";\n\n      // PLACEHOLDER\n      } else if (state.allowPlaceholders && stream.match(/<[^>]*>?/)) {\n        return \"variable\";\n\n      // Fall through\n      } else {\n        stream.next();\n        stream.eatWhile(/[^@\"<#]/);\n        return null;\n      }\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-feature\", \"gherkin\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/gherkin/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Gherkin mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"gherkin.js\"></script>\n<style>.CodeMirror { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Gherkin</a>\n  </ul>\n</div>\n\n<article>\n<h2>Gherkin mode</h2>\n<form><textarea id=\"code\" name=\"code\">\nFeature: Using Google\n  Background: \n    Something something\n    Something else\n  Scenario: Has a homepage\n    When I navigate to the google home page\n    Then the home page should contain the menu and the search form\n  Scenario: Searching for a term \n    When I navigate to the google home page\n    When I search for Tofu\n    Then the search results page is displayed\n    Then the search results page contains 10 individual search results\n    Then the search results contain a link to the wikipedia tofu page\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-feature</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/go/go.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"go\", function(config) {\n  var indentUnit = config.indentUnit;\n\n  var keywords = {\n    \"break\":true, \"case\":true, \"chan\":true, \"const\":true, \"continue\":true,\n    \"default\":true, \"defer\":true, \"else\":true, \"fallthrough\":true, \"for\":true,\n    \"func\":true, \"go\":true, \"goto\":true, \"if\":true, \"import\":true,\n    \"interface\":true, \"map\":true, \"package\":true, \"range\":true, \"return\":true,\n    \"select\":true, \"struct\":true, \"switch\":true, \"type\":true, \"var\":true,\n    \"bool\":true, \"byte\":true, \"complex64\":true, \"complex128\":true,\n    \"float32\":true, \"float64\":true, \"int8\":true, \"int16\":true, \"int32\":true,\n    \"int64\":true, \"string\":true, \"uint8\":true, \"uint16\":true, \"uint32\":true,\n    \"uint64\":true, \"int\":true, \"uint\":true, \"uintptr\":true, \"error\": true,\n    \"rune\":true, \"any\":true, \"comparable\":true\n  };\n\n  var atoms = {\n    \"true\":true, \"false\":true, \"iota\":true, \"nil\":true, \"append\":true,\n    \"cap\":true, \"close\":true, \"complex\":true, \"copy\":true, \"delete\":true, \"imag\":true,\n    \"len\":true, \"make\":true, \"new\":true, \"panic\":true, \"print\":true,\n    \"println\":true, \"real\":true, \"recover\":true\n  };\n\n  var isOperatorChar = /[+\\-*&^%:=<>!|\\/]/;\n\n  var curPunc;\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (ch == '\"' || ch == \"'\" || ch == \"`\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    }\n    if (/[\\d\\.]/.test(ch)) {\n      if (ch == \".\") {\n        stream.match(/^[0-9_]+([eE][\\-+]?[0-9_]+)?/);\n      } else if (ch == \"0\") {\n        stream.match(/^[xX][0-9a-fA-F_]+/) || stream.match(/^[0-7_]+/);\n      } else {\n        stream.match(/^[0-9_]*\\.?[0-9_]*([eE][\\-+]?[0-9_]+)?/);\n      }\n      return \"number\";\n    }\n    if (/[\\[\\]{}\\(\\),;\\:\\.]/.test(ch)) {\n      curPunc = ch;\n      return null;\n    }\n    if (ch == \"/\") {\n      if (stream.eat(\"*\")) {\n        state.tokenize = tokenComment;\n        return tokenComment(stream, state);\n      }\n      if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n    }\n    if (isOperatorChar.test(ch)) {\n      stream.eatWhile(isOperatorChar);\n      return \"operator\";\n    }\n    stream.eatWhile(/[\\w\\$_\\xa1-\\uffff]/);\n    var cur = stream.current();\n    if (keywords.propertyIsEnumerable(cur)) {\n      if (cur == \"case\" || cur == \"default\") curPunc = \"case\";\n      return \"keyword\";\n    }\n    if (atoms.propertyIsEnumerable(cur)) return \"atom\";\n    return \"variable\";\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {end = true; break;}\n        escaped = !escaped && quote != \"`\" && next == \"\\\\\";\n      }\n      if (end || !(escaped || quote == \"`\"))\n        state.tokenize = tokenBase;\n      return \"string\";\n    };\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n\n  function Context(indented, column, type, align, prev) {\n    this.indented = indented;\n    this.column = column;\n    this.type = type;\n    this.align = align;\n    this.prev = prev;\n  }\n  function pushContext(state, col, type) {\n    return state.context = new Context(state.indented, col, type, null, state.context);\n  }\n  function popContext(state) {\n    if (!state.context.prev) return;\n    var t = state.context.type;\n    if (t == \")\" || t == \"]\" || t == \"}\")\n      state.indented = state.context.indented;\n    return state.context = state.context.prev;\n  }\n\n  // Interface\n\n  return {\n    startState: function(basecolumn) {\n      return {\n        tokenize: null,\n        context: new Context((basecolumn || 0) - indentUnit, 0, \"top\", false),\n        indented: 0,\n        startOfLine: true\n      };\n    },\n\n    token: function(stream, state) {\n      var ctx = state.context;\n      if (stream.sol()) {\n        if (ctx.align == null) ctx.align = false;\n        state.indented = stream.indentation();\n        state.startOfLine = true;\n        if (ctx.type == \"case\") ctx.type = \"}\";\n      }\n      if (stream.eatSpace()) return null;\n      curPunc = null;\n      var style = (state.tokenize || tokenBase)(stream, state);\n      if (style == \"comment\") return style;\n      if (ctx.align == null) ctx.align = true;\n\n      if (curPunc == \"{\") pushContext(state, stream.column(), \"}\");\n      else if (curPunc == \"[\") pushContext(state, stream.column(), \"]\");\n      else if (curPunc == \"(\") pushContext(state, stream.column(), \")\");\n      else if (curPunc == \"case\") ctx.type = \"case\";\n      else if (curPunc == \"}\" && ctx.type == \"}\") popContext(state);\n      else if (curPunc == ctx.type) popContext(state);\n      state.startOfLine = false;\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;\n      var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);\n      if (ctx.type == \"case\" && /^(?:case|default)\\b/.test(textAfter)) {\n        state.context.type = \"}\";\n        return ctx.indented;\n      }\n      var closing = firstChar == ctx.type;\n      if (ctx.align) return ctx.column + (closing ? 0 : 1);\n      else return ctx.indented + (closing ? 0 : indentUnit);\n    },\n\n    electricChars: \"{}):\",\n    closeBrackets: \"()[]{}''\\\"\\\"``\",\n    fold: \"brace\",\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    lineComment: \"//\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-go\", \"go\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/go/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Go mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/elegant.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"go.js\"></script>\n<style>.CodeMirror {border:1px solid #999; background:#ffc}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Go</a>\n  </ul>\n</div>\n\n<article>\n<h2>Go mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n// Prime Sieve in Go.\n// Taken from the Go specification.\n// Copyright © The Go Authors.\n\npackage main\n\nimport \"fmt\"\n\n// Send the sequence 2, 3, 4, ... to channel 'ch'.\nfunc generate(ch chan&lt;- int) {\n\tfor i := 2; ; i++ {\n\t\tch &lt;- i  // Send 'i' to channel 'ch'\n\t}\n}\n\n// Copy the values from channel 'src' to channel 'dst',\n// removing those divisible by 'prime'.\nfunc filter(src &lt;-chan int, dst chan&lt;- int, prime int) {\n\tfor i := range src {    // Loop over values received from 'src'.\n\t\tif i%prime != 0 {\n\t\t\tdst &lt;- i  // Send 'i' to channel 'dst'.\n\t\t}\n\t}\n}\n\n// The prime sieve: Daisy-chain filter processes together.\nfunc sieve() {\n\tch := make(chan int)  // Create a new channel.\n\tgo generate(ch)       // Start generate() as a subprocess.\n\tfor {\n\t\tprime := &lt;-ch\n\t\tfmt.Print(prime, \"\\n\")\n\t\tch1 := make(chan int)\n\t\tgo filter(ch, ch1, prime)\n\t\tch = ch1\n\t}\n}\n\nfunc main() {\n\tsieve()\n}\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        theme: \"elegant\",\n        matchBrackets: true,\n        indentUnit: 8,\n        tabSize: 8,\n        indentWithTabs: true,\n        mode: \"text/x-go\"\n      });\n    </script>\n\n    <p><strong>MIME type:</strong> <code>text/x-go</code></p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/groovy/groovy.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"groovy\", function(config) {\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n  var keywords = words(\n    \"abstract as assert boolean break byte case catch char class const continue def default \" +\n    \"do double else enum extends final finally float for goto if implements import in \" +\n    \"instanceof int interface long native new package private protected public return \" +\n    \"short static strictfp super switch synchronized threadsafe throw throws trait transient \" +\n    \"try void volatile while\");\n  var blockKeywords = words(\"catch class def do else enum finally for if interface switch trait try while\");\n  var standaloneKeywords = words(\"return break continue\");\n  var atoms = words(\"null true false this\");\n\n  var curPunc;\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (ch == '\"' || ch == \"'\") {\n      return startString(ch, stream, state);\n    }\n    if (/[\\[\\]{}\\(\\),;\\:\\.]/.test(ch)) {\n      curPunc = ch;\n      return null;\n    }\n    if (/\\d/.test(ch)) {\n      stream.eatWhile(/[\\w\\.]/);\n      if (stream.eat(/eE/)) { stream.eat(/\\+\\-/); stream.eatWhile(/\\d/); }\n      return \"number\";\n    }\n    if (ch == \"/\") {\n      if (stream.eat(\"*\")) {\n        state.tokenize.push(tokenComment);\n        return tokenComment(stream, state);\n      }\n      if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n      if (expectExpression(state.lastToken, false)) {\n        return startString(ch, stream, state);\n      }\n    }\n    if (ch == \"-\" && stream.eat(\">\")) {\n      curPunc = \"->\";\n      return null;\n    }\n    if (/[+\\-*&%=<>!?|\\/~]/.test(ch)) {\n      stream.eatWhile(/[+\\-*&%=<>|~]/);\n      return \"operator\";\n    }\n    stream.eatWhile(/[\\w\\$_]/);\n    if (ch == \"@\") { stream.eatWhile(/[\\w\\$_\\.]/); return \"meta\"; }\n    if (state.lastToken == \".\") return \"property\";\n    if (stream.eat(\":\")) { curPunc = \"proplabel\"; return \"property\"; }\n    var cur = stream.current();\n    if (atoms.propertyIsEnumerable(cur)) { return \"atom\"; }\n    if (keywords.propertyIsEnumerable(cur)) {\n      if (blockKeywords.propertyIsEnumerable(cur)) curPunc = \"newstatement\";\n      else if (standaloneKeywords.propertyIsEnumerable(cur)) curPunc = \"standalone\";\n      return \"keyword\";\n    }\n    return \"variable\";\n  }\n  tokenBase.isBase = true;\n\n  function startString(quote, stream, state) {\n    var tripleQuoted = false;\n    if (quote != \"/\" && stream.eat(quote)) {\n      if (stream.eat(quote)) tripleQuoted = true;\n      else return \"string\";\n    }\n    function t(stream, state) {\n      var escaped = false, next, end = !tripleQuoted;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {\n          if (!tripleQuoted) { break; }\n          if (stream.match(quote + quote)) { end = true; break; }\n        }\n        if (quote == '\"' && next == \"$\" && !escaped) {\n          if (stream.eat(\"{\")) {\n            state.tokenize.push(tokenBaseUntilBrace());\n            return \"string\";\n          } else if (stream.match(/^\\w/, false)) {\n            state.tokenize.push(tokenVariableDeref);\n            return \"string\";\n          }\n        }\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (end) state.tokenize.pop();\n      return \"string\";\n    }\n    state.tokenize.push(t);\n    return t(stream, state);\n  }\n\n  function tokenBaseUntilBrace() {\n    var depth = 1;\n    function t(stream, state) {\n      if (stream.peek() == \"}\") {\n        depth--;\n        if (depth == 0) {\n          state.tokenize.pop();\n          return state.tokenize[state.tokenize.length-1](stream, state);\n        }\n      } else if (stream.peek() == \"{\") {\n        depth++;\n      }\n      return tokenBase(stream, state);\n    }\n    t.isBase = true;\n    return t;\n  }\n\n  function tokenVariableDeref(stream, state) {\n    var next = stream.match(/^(\\.|[\\w\\$_]+)/)\n    if (!next) {\n      state.tokenize.pop()\n      return state.tokenize[state.tokenize.length-1](stream, state)\n    }\n    return next[0] == \".\" ? null : \"variable\"\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize.pop();\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n\n  function expectExpression(last, newline) {\n    return !last || last == \"operator\" || last == \"->\" || /[\\.\\[\\{\\(,;:]/.test(last) ||\n      last == \"newstatement\" || last == \"keyword\" || last == \"proplabel\" ||\n      (last == \"standalone\" && !newline);\n  }\n\n  function Context(indented, column, type, align, prev) {\n    this.indented = indented;\n    this.column = column;\n    this.type = type;\n    this.align = align;\n    this.prev = prev;\n  }\n  function pushContext(state, col, type) {\n    return state.context = new Context(state.indented, col, type, null, state.context);\n  }\n  function popContext(state) {\n    var t = state.context.type;\n    if (t == \")\" || t == \"]\" || t == \"}\")\n      state.indented = state.context.indented;\n    return state.context = state.context.prev;\n  }\n\n  // Interface\n\n  return {\n    startState: function(basecolumn) {\n      return {\n        tokenize: [tokenBase],\n        context: new Context((basecolumn || 0) - config.indentUnit, 0, \"top\", false),\n        indented: 0,\n        startOfLine: true,\n        lastToken: null\n      };\n    },\n\n    token: function(stream, state) {\n      var ctx = state.context;\n      if (stream.sol()) {\n        if (ctx.align == null) ctx.align = false;\n        state.indented = stream.indentation();\n        state.startOfLine = true;\n        // Automatic semicolon insertion\n        if (ctx.type == \"statement\" && !expectExpression(state.lastToken, true)) {\n          popContext(state); ctx = state.context;\n        }\n      }\n      if (stream.eatSpace()) return null;\n      curPunc = null;\n      var style = state.tokenize[state.tokenize.length-1](stream, state);\n      if (style == \"comment\") return style;\n      if (ctx.align == null) ctx.align = true;\n\n      if ((curPunc == \";\" || curPunc == \":\") && ctx.type == \"statement\") popContext(state);\n      // Handle indentation for {x -> \\n ... }\n      else if (curPunc == \"->\" && ctx.type == \"statement\" && ctx.prev.type == \"}\") {\n        popContext(state);\n        state.context.align = false;\n      }\n      else if (curPunc == \"{\") pushContext(state, stream.column(), \"}\");\n      else if (curPunc == \"[\") pushContext(state, stream.column(), \"]\");\n      else if (curPunc == \"(\") pushContext(state, stream.column(), \")\");\n      else if (curPunc == \"}\") {\n        while (ctx.type == \"statement\") ctx = popContext(state);\n        if (ctx.type == \"}\") ctx = popContext(state);\n        while (ctx.type == \"statement\") ctx = popContext(state);\n      }\n      else if (curPunc == ctx.type) popContext(state);\n      else if (ctx.type == \"}\" || ctx.type == \"top\" || (ctx.type == \"statement\" && curPunc == \"newstatement\"))\n        pushContext(state, stream.column(), \"statement\");\n      state.startOfLine = false;\n      state.lastToken = curPunc || style;\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      if (!state.tokenize[state.tokenize.length-1].isBase) return CodeMirror.Pass;\n      var firstChar = textAfter && textAfter.charAt(0), ctx = state.context;\n      if (ctx.type == \"statement\" && !expectExpression(state.lastToken, true)) ctx = ctx.prev;\n      var closing = firstChar == ctx.type;\n      if (ctx.type == \"statement\") return ctx.indented + (firstChar == \"{\" ? 0 : config.indentUnit);\n      else if (ctx.align) return ctx.column + (closing ? 0 : 1);\n      else return ctx.indented + (closing ? 0 : config.indentUnit);\n    },\n\n    electricChars: \"{}\",\n    closeBrackets: {triples: \"'\\\"\"},\n    fold: \"brace\",\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    lineComment: \"//\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-groovy\", \"groovy\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/groovy/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Groovy mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"groovy.js\"></script>\n<style>.CodeMirror {border-top: 1px solid #500; border-bottom: 1px solid #500;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Groovy</a>\n  </ul>\n</div>\n\n<article>\n<h2>Groovy mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n//Pattern for groovy script\ndef p = ~/.*\\.groovy/\nnew File( 'd:\\\\scripts' ).eachFileMatch(p) {f ->\n  // imports list\n  def imports = []\n  f.eachLine {\n    // condition to detect an import instruction\n    ln -> if ( ln =~ '^import .*' ) {\n      imports << \"${ln - 'import '}\"\n    }\n  }\n  // print thmen\n  if ( ! imports.empty ) {\n    println f\n    imports.each{ println \"   $it\" }\n  }\n}\n\n/* Coin changer demo code from http://groovy.codehaus.org */\n\nenum UsCoin {\n  quarter(25), dime(10), nickel(5), penny(1)\n  UsCoin(v) { value = v }\n  final value\n}\n\nenum OzzieCoin {\n  fifty(50), twenty(20), ten(10), five(5)\n  OzzieCoin(v) { value = v }\n  final value\n}\n\ndef plural(word, count) {\n  if (count == 1) return word\n  word[-1] == 'y' ? word[0..-2] + \"ies\" : word + \"s\"\n}\n\ndef change(currency, amount) {\n  currency.values().inject([]){ list, coin ->\n     int count = amount / coin.value\n     amount = amount % coin.value\n     list += \"$count ${plural(coin.toString(), count)}\"\n  }\n}\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-groovy\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-groovy</code></p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/haml/haml.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../htmlmixed/htmlmixed\"), require(\"../ruby/ruby\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../htmlmixed/htmlmixed\", \"../ruby/ruby\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\n  // full haml mode. This handled embedded ruby and html fragments too\n  CodeMirror.defineMode(\"haml\", function(config) {\n    var htmlMode = CodeMirror.getMode(config, {name: \"htmlmixed\"});\n    var rubyMode = CodeMirror.getMode(config, \"ruby\");\n\n    function rubyInQuote(endQuote) {\n      return function(stream, state) {\n        var ch = stream.peek();\n        if (ch == endQuote && state.rubyState.tokenize.length == 1) {\n          // step out of ruby context as it seems to complete processing all the braces\n          stream.next();\n          state.tokenize = html;\n          return \"closeAttributeTag\";\n        } else {\n          return ruby(stream, state);\n        }\n      };\n    }\n\n    function ruby(stream, state) {\n      if (stream.match(\"-#\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n      return rubyMode.token(stream, state.rubyState);\n    }\n\n    function html(stream, state) {\n      var ch = stream.peek();\n\n      // handle haml declarations. All declarations that cant be handled here\n      // will be passed to html mode\n      if (state.previousToken.style == \"comment\" ) {\n        if (state.indented > state.previousToken.indented) {\n          stream.skipToEnd();\n          return \"commentLine\";\n        }\n      }\n\n      if (state.startOfLine) {\n        if (ch == \"!\" && stream.match(\"!!\")) {\n          stream.skipToEnd();\n          return \"tag\";\n        } else if (stream.match(/^%[\\w:#\\.]+=/)) {\n          state.tokenize = ruby;\n          return \"hamlTag\";\n        } else if (stream.match(/^%[\\w:]+/)) {\n          return \"hamlTag\";\n        } else if (ch == \"/\" ) {\n          stream.skipToEnd();\n          return \"comment\";\n        }\n      }\n\n      if (state.startOfLine || state.previousToken.style == \"hamlTag\") {\n        if ( ch == \"#\" || ch == \".\") {\n          stream.match(/[\\w-#\\.]*/);\n          return \"hamlAttribute\";\n        }\n      }\n\n      // do not handle --> as valid ruby, make it HTML close comment instead\n      if (state.startOfLine && !stream.match(\"-->\", false) && (ch == \"=\" || ch == \"-\" )) {\n        state.tokenize = ruby;\n        return state.tokenize(stream, state);\n      }\n\n      if (state.previousToken.style == \"hamlTag\" ||\n          state.previousToken.style == \"closeAttributeTag\" ||\n          state.previousToken.style == \"hamlAttribute\") {\n        if (ch == \"(\") {\n          state.tokenize = rubyInQuote(\")\");\n          return state.tokenize(stream, state);\n        } else if (ch == \"{\") {\n          if (!stream.match(/^\\{%.*/)) {\n            state.tokenize = rubyInQuote(\"}\");\n            return state.tokenize(stream, state);\n          }\n        }\n      }\n\n      return htmlMode.token(stream, state.htmlState);\n    }\n\n    return {\n      // default to html mode\n      startState: function() {\n        var htmlState = CodeMirror.startState(htmlMode);\n        var rubyState = CodeMirror.startState(rubyMode);\n        return {\n          htmlState: htmlState,\n          rubyState: rubyState,\n          indented: 0,\n          previousToken: { style: null, indented: 0},\n          tokenize: html\n        };\n      },\n\n      copyState: function(state) {\n        return {\n          htmlState : CodeMirror.copyState(htmlMode, state.htmlState),\n          rubyState: CodeMirror.copyState(rubyMode, state.rubyState),\n          indented: state.indented,\n          previousToken: state.previousToken,\n          tokenize: state.tokenize\n        };\n      },\n\n      token: function(stream, state) {\n        if (stream.sol()) {\n          state.indented = stream.indentation();\n          state.startOfLine = true;\n        }\n        if (stream.eatSpace()) return null;\n        var style = state.tokenize(stream, state);\n        state.startOfLine = false;\n        // dont record comment line as we only want to measure comment line with\n        // the opening comment block\n        if (style && style != \"commentLine\") {\n          state.previousToken = { style: style, indented: state.indented };\n        }\n        // if current state is ruby and the previous token is not `,` reset the\n        // tokenize to html\n        if (stream.eol() && state.tokenize == ruby) {\n          stream.backUp(1);\n          var ch = stream.peek();\n          stream.next();\n          if (ch && ch != \",\") {\n            state.tokenize = html;\n          }\n        }\n        // reprocess some of the specific style tag when finish setting previousToken\n        if (style == \"hamlTag\") {\n          style = \"tag\";\n        } else if (style == \"commentLine\") {\n          style = \"comment\";\n        } else if (style == \"hamlAttribute\") {\n          style = \"attribute\";\n        } else if (style == \"closeAttributeTag\") {\n          style = null;\n        }\n        return style;\n      }\n    };\n  }, \"htmlmixed\", \"ruby\");\n\n  CodeMirror.defineMIME(\"text/x-haml\", \"haml\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/haml/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: HAML mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../htmlmixed/htmlmixed.js\"></script>\n<script src=\"../javascript/javascript.js\"></script>\n<script src=\"../ruby/ruby.js\"></script>\n<script src=\"haml.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">HAML</a>\n  </ul>\n</div>\n\n<article>\n<h2>HAML mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n!!!\n#content\n.left.column(title=\"title\"){:href => \"/hello\", :test => \"#{hello}_#{world}\"}\n    <!-- This is a comment -->\n    %h2 Welcome to our site!\n    %p= puts \"HAML MODE\"\n  .right.column\n    = render :partial => \"sidebar\"\n\n.container\n  .row\n    .span8\n      %h1.title= @page_title\n%p.title= @page_title\n%p\n  /\n    The same as HTML comment\n    Hello multiline comment\n\n  -# haml comment\n      This wont be displayed\n      nor will this\n  Date/Time:\n  - now = DateTime.now\n  %strong= now\n  - if now > DateTime.parse(\"December 31, 2006\")\n    = \"Happy new \" + \"year!\"\n\n%title\n  = @title\n  \\= @title\n  <h1>Title</h1>\n  <h1 title=\"HELLO\">\n    Title\n  </h1>\n    </textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        mode: \"text/x-haml\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-haml</code>.</p>\n\n    <p><strong>Parsing/Highlighting Tests:</strong> <a href=\"../../test/index.html#haml_*\">normal</a>,  <a href=\"../../test/index.html#verbose,haml_*\">verbose</a>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/haml/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({tabSize: 4, indentUnit: 2}, \"haml\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  // Requires at least one media query\n  MT(\"elementName\",\n     \"[tag %h1] Hey There\");\n\n  MT(\"oneElementPerLine\",\n     \"[tag %h1] Hey There %h2\");\n\n  MT(\"idSelector\",\n     \"[tag %h1][attribute #test] Hey There\");\n\n  MT(\"classSelector\",\n     \"[tag %h1][attribute .hello] Hey There\");\n\n  MT(\"docType\",\n     \"[tag !!! XML]\");\n\n  MT(\"comment\",\n     \"[comment / Hello WORLD]\");\n\n  MT(\"notComment\",\n     \"[tag %h1] This is not a / comment \");\n\n  MT(\"attributes\",\n     \"[tag %a]([variable title][operator =][string \\\"test\\\"]){[atom :title] [operator =>] [string \\\"test\\\"]}\");\n\n  MT(\"htmlCode\",\n     \"[tag&bracket <][tag h1][tag&bracket >]Title[tag&bracket </][tag h1][tag&bracket >]\");\n\n  MT(\"rubyBlock\",\n     \"[operator =][variable-2 @item]\");\n\n  MT(\"selectorRubyBlock\",\n     \"[tag %a.selector=] [variable-2 @item]\");\n\n  MT(\"nestedRubyBlock\",\n      \"[tag %a]\",\n      \"   [operator =][variable puts] [string \\\"test\\\"]\");\n\n  MT(\"multilinePlaintext\",\n      \"[tag %p]\",\n      \"  Hello,\",\n      \"  World\");\n\n  MT(\"multilineRuby\",\n      \"[tag %p]\",\n      \"  [comment -# this is a comment]\",\n      \"     [comment and this is a comment too]\",\n      \"  Date/Time\",\n      \"  [operator -] [variable now] [operator =] [tag DateTime][operator .][property now]\",\n      \"  [tag %strong=] [variable now]\",\n      \"  [operator -] [keyword if] [variable now] [operator >] [tag DateTime][operator .][property parse]([string \\\"December 31, 2006\\\"])\",\n      \"     [operator =][string \\\"Happy\\\"]\",\n      \"     [operator =][string \\\"Belated\\\"]\",\n      \"     [operator =][string \\\"Birthday\\\"]\");\n\n  MT(\"multilineComment\",\n      \"[comment /]\",\n      \"  [comment Multiline]\",\n      \"  [comment Comment]\");\n\n  MT(\"hamlComment\",\n     \"[comment -# this is a comment]\");\n\n  MT(\"multilineHamlComment\",\n     \"[comment -# this is a comment]\",\n     \"   [comment and this is a comment too]\");\n\n  MT(\"multilineHTMLComment\",\n    \"[comment <!--]\",\n    \"  [comment what a comment]\",\n    \"  [comment -->]\");\n\n  MT(\"hamlAfterRubyTag\",\n    \"[attribute .block]\",\n    \"  [tag %strong=] [variable now]\",\n    \"  [attribute .test]\",\n    \"     [operator =][variable now]\",\n    \"  [attribute .right]\");\n\n  MT(\"stretchedRuby\",\n     \"[operator =] [variable puts] [string \\\"Hello\\\"],\",\n     \"   [string \\\"World\\\"]\");\n\n  MT(\"interpolationInHashAttribute\",\n     //\"[tag %div]{[atom :id] [operator =>] [string \\\"#{][variable test][string }_#{][variable ting][string }\\\"]} test\");\n     \"[tag %div]{[atom :id] [operator =>] [string \\\"#{][variable test][string }_#{][variable ting][string }\\\"]} test\");\n\n  MT(\"interpolationInHTMLAttribute\",\n     \"[tag %div]([variable title][operator =][string \\\"#{][variable test][string }_#{][variable ting]()[string }\\\"]) Test\");\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/handlebars/handlebars.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../../addon/mode/simple\"), require(\"../../addon/mode/multiplex\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../../addon/mode/simple\", \"../../addon/mode/multiplex\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineSimpleMode(\"handlebars-tags\", {\n    start: [\n      { regex: /\\{\\{\\{/, push: \"handlebars_raw\", token: \"tag\" },\n      { regex: /\\{\\{!--/, push: \"dash_comment\", token: \"comment\" },\n      { regex: /\\{\\{!/,   push: \"comment\", token: \"comment\" },\n      { regex: /\\{\\{/,    push: \"handlebars\", token: \"tag\" }\n    ],\n    handlebars_raw: [\n      { regex: /\\}\\}\\}/, pop: true, token: \"tag\" },\n    ],\n    handlebars: [\n      { regex: /\\}\\}/, pop: true, token: \"tag\" },\n\n      // Double and single quotes\n      { regex: /\"(?:[^\\\\\"]|\\\\.)*\"?/, token: \"string\" },\n      { regex: /'(?:[^\\\\']|\\\\.)*'?/, token: \"string\" },\n\n      // Handlebars keywords\n      { regex: />|[#\\/]([A-Za-z_]\\w*)/, token: \"keyword\" },\n      { regex: /(?:else|this)\\b/, token: \"keyword\" },\n\n      // Numeral\n      { regex: /\\d+/i, token: \"number\" },\n\n      // Atoms like = and .\n      { regex: /=|~|@|true|false/, token: \"atom\" },\n\n      // Paths\n      { regex: /(?:\\.\\.\\/)*(?:[A-Za-z_][\\w\\.]*)+/, token: \"variable-2\" }\n    ],\n    dash_comment: [\n      { regex: /--\\}\\}/, pop: true, token: \"comment\" },\n\n      // Commented code\n      { regex: /./, token: \"comment\"}\n    ],\n    comment: [\n      { regex: /\\}\\}/, pop: true, token: \"comment\" },\n      { regex: /./, token: \"comment\" }\n    ],\n    meta: {\n      blockCommentStart: \"{{--\",\n      blockCommentEnd: \"--}}\"\n    }\n  });\n\n  CodeMirror.defineMode(\"handlebars\", function(config, parserConfig) {\n    var handlebars = CodeMirror.getMode(config, \"handlebars-tags\");\n    if (!parserConfig || !parserConfig.base) return handlebars;\n    return CodeMirror.multiplexingMode(\n      CodeMirror.getMode(config, parserConfig.base),\n      {open: \"{{\", close: /\\}\\}\\}?/, mode: handlebars, parseDelimiters: true}\n    );\n  });\n\n  CodeMirror.defineMIME(\"text/x-handlebars-template\", \"handlebars\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/handlebars/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Handlebars mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/mode/simple.js\"></script>\n<script src=\"../../addon/mode/multiplex.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"handlebars.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">HTML mixed</a>\n  </ul>\n</div>\n\n<article>\n<h2>Handlebars</h2>\n<form><textarea id=\"code\" name=\"code\">\n{{> breadcrumbs}}\n\n{{!--\n  You can use the t function to get\n  content translated to the current locale, es:\n  {{t 'article_list'}}\n--}}\n\n<h1>{{t 'article_list'}}</h1>\n\n{{! one line comment }}\n\n{{{propertyContainingRawHtml}}}\n\n{{#each articles}}\n  {{~title}}\n  <p>{{excerpt body size=120 ellipsis=true}}</p>\n\n  {{#with author}}\n    written by {{first_name}} {{last_name}}\n    from category: {{../category.title}}\n    {{#if @../last}}foobar!{{/if}}\n  {{/with~}}\n\n  {{#if promoted.latest}}Read this one! {{else}} This is ok! {{/if}}\n\n  {{#if @last}}<hr>{{/if}}\n{{/each}}\n\n{{#form new_comment}}\n  <input type=\"text\" name=\"body\">\n{{/form}}\n\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: {name: \"handlebars\", base: \"text/html\"}\n      });\n    </script>\n    \n    <p>Handlebars syntax highlighting for CodeMirror.</p>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-handlebars-template</code></p>\n\n    <p>Supported options: <code>base</code> to set the mode to\n    wrap. For example, use</p>\n    <pre>mode: {name: \"handlebars\", base: \"text/html\"}</pre>\n    <p>to highlight an HTML template.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/haskell/haskell.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"haskell\", function(_config, modeConfig) {\n\n  function switchState(source, setState, f) {\n    setState(f);\n    return f(source, setState);\n  }\n\n  // These should all be Unicode extended, as per the Haskell 2010 report\n  var smallRE = /[a-z_]/;\n  var largeRE = /[A-Z]/;\n  var digitRE = /\\d/;\n  var hexitRE = /[0-9A-Fa-f]/;\n  var octitRE = /[0-7]/;\n  var idRE = /[a-z_A-Z0-9'\\xa1-\\uffff]/;\n  var symbolRE = /[-!#$%&*+.\\/<=>?@\\\\^|~:]/;\n  var specialRE = /[(),;[\\]`{}]/;\n  var whiteCharRE = /[ \\t\\v\\f]/; // newlines are handled in tokenizer\n\n  function normal(source, setState) {\n    if (source.eatWhile(whiteCharRE)) {\n      return null;\n    }\n\n    var ch = source.next();\n    if (specialRE.test(ch)) {\n      if (ch == '{' && source.eat('-')) {\n        var t = \"comment\";\n        if (source.eat('#')) {\n          t = \"meta\";\n        }\n        return switchState(source, setState, ncomment(t, 1));\n      }\n      return null;\n    }\n\n    if (ch == '\\'') {\n      if (source.eat('\\\\')) {\n        source.next();  // should handle other escapes here\n      }\n      else {\n        source.next();\n      }\n      if (source.eat('\\'')) {\n        return \"string\";\n      }\n      return \"string error\";\n    }\n\n    if (ch == '\"') {\n      return switchState(source, setState, stringLiteral);\n    }\n\n    if (largeRE.test(ch)) {\n      source.eatWhile(idRE);\n      if (source.eat('.')) {\n        return \"qualifier\";\n      }\n      return \"variable-2\";\n    }\n\n    if (smallRE.test(ch)) {\n      source.eatWhile(idRE);\n      return \"variable\";\n    }\n\n    if (digitRE.test(ch)) {\n      if (ch == '0') {\n        if (source.eat(/[xX]/)) {\n          source.eatWhile(hexitRE); // should require at least 1\n          return \"integer\";\n        }\n        if (source.eat(/[oO]/)) {\n          source.eatWhile(octitRE); // should require at least 1\n          return \"number\";\n        }\n      }\n      source.eatWhile(digitRE);\n      var t = \"number\";\n      if (source.match(/^\\.\\d+/)) {\n        t = \"number\";\n      }\n      if (source.eat(/[eE]/)) {\n        t = \"number\";\n        source.eat(/[-+]/);\n        source.eatWhile(digitRE); // should require at least 1\n      }\n      return t;\n    }\n\n    if (ch == \".\" && source.eat(\".\"))\n      return \"keyword\";\n\n    if (symbolRE.test(ch)) {\n      if (ch == '-' && source.eat(/-/)) {\n        source.eatWhile(/-/);\n        if (!source.eat(symbolRE)) {\n          source.skipToEnd();\n          return \"comment\";\n        }\n      }\n      var t = \"variable\";\n      if (ch == ':') {\n        t = \"variable-2\";\n      }\n      source.eatWhile(symbolRE);\n      return t;\n    }\n\n    return \"error\";\n  }\n\n  function ncomment(type, nest) {\n    if (nest == 0) {\n      return normal;\n    }\n    return function(source, setState) {\n      var currNest = nest;\n      while (!source.eol()) {\n        var ch = source.next();\n        if (ch == '{' && source.eat('-')) {\n          ++currNest;\n        }\n        else if (ch == '-' && source.eat('}')) {\n          --currNest;\n          if (currNest == 0) {\n            setState(normal);\n            return type;\n          }\n        }\n      }\n      setState(ncomment(type, currNest));\n      return type;\n    };\n  }\n\n  function stringLiteral(source, setState) {\n    while (!source.eol()) {\n      var ch = source.next();\n      if (ch == '\"') {\n        setState(normal);\n        return \"string\";\n      }\n      if (ch == '\\\\') {\n        if (source.eol() || source.eat(whiteCharRE)) {\n          setState(stringGap);\n          return \"string\";\n        }\n        if (source.eat('&')) {\n        }\n        else {\n          source.next(); // should handle other escapes here\n        }\n      }\n    }\n    setState(normal);\n    return \"string error\";\n  }\n\n  function stringGap(source, setState) {\n    if (source.eat('\\\\')) {\n      return switchState(source, setState, stringLiteral);\n    }\n    source.next();\n    setState(normal);\n    return \"error\";\n  }\n\n\n  var wellKnownWords = (function() {\n    var wkw = {};\n    function setType(t) {\n      return function () {\n        for (var i = 0; i < arguments.length; i++)\n          wkw[arguments[i]] = t;\n      };\n    }\n\n    setType(\"keyword\")(\n      \"case\", \"class\", \"data\", \"default\", \"deriving\", \"do\", \"else\", \"foreign\",\n      \"if\", \"import\", \"in\", \"infix\", \"infixl\", \"infixr\", \"instance\", \"let\",\n      \"module\", \"newtype\", \"of\", \"then\", \"type\", \"where\", \"_\");\n\n    setType(\"keyword\")(\n      \"\\.\\.\", \":\", \"::\", \"=\", \"\\\\\", \"<-\", \"->\", \"@\", \"~\", \"=>\");\n\n    setType(\"builtin\")(\n      \"!!\", \"$!\", \"$\", \"&&\", \"+\", \"++\", \"-\", \".\", \"/\", \"/=\", \"<\", \"<*\", \"<=\",\n      \"<$>\", \"<*>\", \"=<<\", \"==\", \">\", \">=\", \">>\", \">>=\", \"^\", \"^^\", \"||\", \"*\",\n      \"*>\", \"**\");\n\n    setType(\"builtin\")(\n      \"Applicative\", \"Bool\", \"Bounded\", \"Char\", \"Double\", \"EQ\", \"Either\", \"Enum\",\n      \"Eq\", \"False\", \"FilePath\", \"Float\", \"Floating\", \"Fractional\", \"Functor\",\n      \"GT\", \"IO\", \"IOError\", \"Int\", \"Integer\", \"Integral\", \"Just\", \"LT\", \"Left\",\n      \"Maybe\", \"Monad\", \"Nothing\", \"Num\", \"Ord\", \"Ordering\", \"Rational\", \"Read\",\n      \"ReadS\", \"Real\", \"RealFloat\", \"RealFrac\", \"Right\", \"Show\", \"ShowS\",\n      \"String\", \"True\");\n\n    setType(\"builtin\")(\n      \"abs\", \"acos\", \"acosh\", \"all\", \"and\", \"any\", \"appendFile\", \"asTypeOf\",\n      \"asin\", \"asinh\", \"atan\", \"atan2\", \"atanh\", \"break\", \"catch\", \"ceiling\",\n      \"compare\", \"concat\", \"concatMap\", \"const\", \"cos\", \"cosh\", \"curry\",\n      \"cycle\", \"decodeFloat\", \"div\", \"divMod\", \"drop\", \"dropWhile\", \"either\",\n      \"elem\", \"encodeFloat\", \"enumFrom\", \"enumFromThen\", \"enumFromThenTo\",\n      \"enumFromTo\", \"error\", \"even\", \"exp\", \"exponent\", \"fail\", \"filter\",\n      \"flip\", \"floatDigits\", \"floatRadix\", \"floatRange\", \"floor\", \"fmap\",\n      \"foldl\", \"foldl1\", \"foldr\", \"foldr1\", \"fromEnum\", \"fromInteger\",\n      \"fromIntegral\", \"fromRational\", \"fst\", \"gcd\", \"getChar\", \"getContents\",\n      \"getLine\", \"head\", \"id\", \"init\", \"interact\", \"ioError\", \"isDenormalized\",\n      \"isIEEE\", \"isInfinite\", \"isNaN\", \"isNegativeZero\", \"iterate\", \"last\",\n      \"lcm\", \"length\", \"lex\", \"lines\", \"log\", \"logBase\", \"lookup\", \"map\",\n      \"mapM\", \"mapM_\", \"max\", \"maxBound\", \"maximum\", \"maybe\", \"min\", \"minBound\",\n      \"minimum\", \"mod\", \"negate\", \"not\", \"notElem\", \"null\", \"odd\", \"or\",\n      \"otherwise\", \"pi\", \"pred\", \"print\", \"product\", \"properFraction\", \"pure\",\n      \"putChar\", \"putStr\", \"putStrLn\", \"quot\", \"quotRem\", \"read\", \"readFile\",\n      \"readIO\", \"readList\", \"readLn\", \"readParen\", \"reads\", \"readsPrec\",\n      \"realToFrac\", \"recip\", \"rem\", \"repeat\", \"replicate\", \"return\", \"reverse\",\n      \"round\", \"scaleFloat\", \"scanl\", \"scanl1\", \"scanr\", \"scanr1\", \"seq\",\n      \"sequence\", \"sequence_\", \"show\", \"showChar\", \"showList\", \"showParen\",\n      \"showString\", \"shows\", \"showsPrec\", \"significand\", \"signum\", \"sin\",\n      \"sinh\", \"snd\", \"span\", \"splitAt\", \"sqrt\", \"subtract\", \"succ\", \"sum\",\n      \"tail\", \"take\", \"takeWhile\", \"tan\", \"tanh\", \"toEnum\", \"toInteger\",\n      \"toRational\", \"truncate\", \"uncurry\", \"undefined\", \"unlines\", \"until\",\n      \"unwords\", \"unzip\", \"unzip3\", \"userError\", \"words\", \"writeFile\", \"zip\",\n      \"zip3\", \"zipWith\", \"zipWith3\");\n\n    var override = modeConfig.overrideKeywords;\n    if (override) for (var word in override) if (override.hasOwnProperty(word))\n      wkw[word] = override[word];\n\n    return wkw;\n  })();\n\n\n\n  return {\n    startState: function ()  { return { f: normal }; },\n    copyState:  function (s) { return { f: s.f }; },\n\n    token: function(stream, state) {\n      var t = state.f(stream, function(s) { state.f = s; });\n      var w = stream.current();\n      return wellKnownWords.hasOwnProperty(w) ? wellKnownWords[w] : t;\n    },\n\n    blockCommentStart: \"{-\",\n    blockCommentEnd: \"-}\",\n    lineComment: \"--\"\n  };\n\n});\n\nCodeMirror.defineMIME(\"text/x-haskell\", \"haskell\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/haskell/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Haskell mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/elegant.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"haskell.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Haskell</a>\n  </ul>\n</div>\n\n<article>\n<h2>Haskell mode</h2>\n<form><textarea id=\"code\" name=\"code\">\nmodule UniquePerms (\n    uniquePerms\n    )\nwhere\n\n-- | Find all unique permutations of a list where there might be duplicates.\nuniquePerms :: (Eq a) => [a] -> [[a]]\nuniquePerms = permBag . makeBag\n\n-- | An unordered collection where duplicate values are allowed,\n-- but represented with a single value and a count.\ntype Bag a = [(a, Int)]\n\nmakeBag :: (Eq a) => [a] -> Bag a\nmakeBag [] = []\nmakeBag (a:as) = mix a $ makeBag as\n  where\n    mix a []                        = [(a,1)]\n    mix a (bn@(b,n):bs) | a == b    = (b,n+1):bs\n                        | otherwise = bn : mix a bs\n\npermBag :: Bag a -> [[a]]\npermBag [] = [[]]\npermBag bs = concatMap (\\(f,cs) -> map (f:) $ permBag cs) . oneOfEach $ bs\n  where\n    oneOfEach [] = []\n    oneOfEach (an@(a,n):bs) =\n        let bs' = if n == 1 then bs else (a,n-1):bs\n        in (a,bs') : mapSnd (an:) (oneOfEach bs)\n    \n    apSnd f (a,b) = (a, f b)\n    mapSnd = map . apSnd\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        theme: \"elegant\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-haskell</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/haskell-literate/haskell-literate.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function (mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../haskell/haskell\"))\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../haskell/haskell\"], mod)\n  else // Plain browser env\n    mod(CodeMirror)\n})(function (CodeMirror) {\n  \"use strict\"\n\n  CodeMirror.defineMode(\"haskell-literate\", function (config, parserConfig) {\n    var baseMode = CodeMirror.getMode(config, (parserConfig && parserConfig.base) || \"haskell\")\n\n    return {\n      startState: function () {\n        return {\n          inCode: false,\n          baseState: CodeMirror.startState(baseMode)\n        }\n      },\n      token: function (stream, state) {\n        if (stream.sol()) {\n          if (state.inCode = stream.eat(\">\"))\n            return \"meta\"\n        }\n        if (state.inCode) {\n          return baseMode.token(stream, state.baseState)\n        } else {\n          stream.skipToEnd()\n          return \"comment\"\n        }\n      },\n      innerMode: function (state) {\n        return state.inCode ? {state: state.baseState, mode: baseMode} : null\n      }\n    }\n  }, \"haskell\")\n\n  CodeMirror.defineMIME(\"text/x-literate-haskell\", \"haskell-literate\")\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/haskell-literate/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Haskell-literate mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"haskell-literate.js\"></script>\n<script src=\"../haskell/haskell.js\"></script>\n<style>.CodeMirror {\n  border-top    : 1px solid #DDDDDD;\n  border-bottom : 1px solid #DDDDDD;\n}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo\n                                                          src=\"../../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Haskell-literate</a>\n  </ul>\n</div>\n\n<article>\n  <h2>Haskell literate mode</h2>\n  <form>\n    <textarea id=\"code\" name=\"code\">\n> {-# LANGUAGE OverloadedStrings #-}\n> {-# OPTIONS_GHC -fno-warn-unused-do-bind #-}\n> import Control.Applicative ((<$>), (<*>))\n> import Data.Maybe (isJust)\n\n> import Data.Text (Text)\n> import Text.Blaze ((!))\n> import qualified Data.Text as T\n> import qualified Happstack.Server as Happstack\n> import qualified Text.Blaze.Html5 as H\n> import qualified Text.Blaze.Html5.Attributes as A\n\n> import Text.Digestive\n> import Text.Digestive.Blaze.Html5\n> import Text.Digestive.Happstack\n> import Text.Digestive.Util\n\nSimple forms and validation\n---------------------------\n\nLet's start by creating a very simple datatype to represent a user:\n\n> data User = User\n>     { userName :: Text\n>     , userMail :: Text\n>     } deriving (Show)\n\nAnd dive in immediately to create a `Form` for a user. The `Form v m a` type\nhas three parameters:\n\n- `v`: the type for messages and errors (usually a `String`-like type, `Text` in\n  this case);\n- `m`: the monad we are operating in, not specified here;\n- `a`: the return type of the `Form`, in this case, this is obviously `User`.\n\n> userForm :: Monad m => Form Text m User\n\nWe create forms by using the `Applicative` interface. A few form types are\nprovided in the `Text.Digestive.Form` module, such as `text`, `string`,\n`bool`...\n\nIn the `digestive-functors` library, the developer is required to label each\nfield using the `.:` operator. This might look like a bit of a burden, but it\nallows you to do some really useful stuff, like separating the `Form` from the\nactual HTML layout.\n\n> userForm = User\n>     <$> \"name\" .: text Nothing\n>     <*> \"mail\" .: check \"Not a valid email address\" checkEmail (text Nothing)\n\nThe `check` function enables you to validate the result of a form. For example,\nwe can validate the email address with a really naive `checkEmail` function.\n\n> checkEmail :: Text -> Bool\n> checkEmail = isJust . T.find (== '@')\n\nMore validation\n---------------\n\nFor our example, we also want descriptions of Haskell libraries, and in order to\ndo that, we need package versions...\n\n> type Version = [Int]\n\nWe want to let the user input a version number such as `0.1.0.0`. This means we\nneed to validate if the input `Text` is of this form, and then we need to parse\nit to a `Version` type. Fortunately, we can do this in a single function:\n`validate` allows conversion between values, which can optionally fail.\n\n`readMaybe :: Read a => String -> Maybe a` is a utility function imported from\n`Text.Digestive.Util`.\n\n> validateVersion :: Text -> Result Text Version\n> validateVersion = maybe (Error \"Cannot parse version\") Success .\n>     mapM (readMaybe . T.unpack) . T.split (== '.')\n\nA quick test in GHCi:\n\n    ghci> validateVersion (T.pack \"0.3.2.1\")\n    Success [0,3,2,1]\n    ghci> validateVersion (T.pack \"0.oops\")\n    Error \"Cannot parse version\"\n\nIt works! This means we can now easily add a `Package` type and a `Form` for it:\n\n> data Category = Web | Text | Math\n>     deriving (Bounded, Enum, Eq, Show)\n\n> data Package = Package Text Version Category\n>     deriving (Show)\n\n> packageForm :: Monad m => Form Text m Package\n> packageForm = Package\n>     <$> \"name\"     .: text Nothing\n>     <*> \"version\"  .: validate validateVersion (text (Just \"0.0.0.1\"))\n>     <*> \"category\" .: choice categories Nothing\n>   where\n>     categories = [(x, T.pack (show x)) | x <- [minBound .. maxBound]]\n\nComposing forms\n---------------\n\nA release has an author and a package. Let's use this to illustrate the\ncomposability of the digestive-functors library: we can reuse the forms we have\nwritten earlier on.\n\n> data Release = Release User Package\n>     deriving (Show)\n\n> releaseForm :: Monad m => Form Text m Release\n> releaseForm = Release\n>     <$> \"author\"  .: userForm\n>     <*> \"package\" .: packageForm\n\nViews\n-----\n\nAs mentioned before, one of the advantages of using digestive-functors is\nseparation of forms and their actual HTML layout. In order to do this, we have\nanother type, `View`.\n\nWe can get a `View` from a `Form` by supplying input. A `View` contains more\ninformation than a `Form`, it has:\n\n- the original form;\n- the input given by the user;\n- any errors that have occurred.\n\nIt is this view that we convert to HTML. For this tutorial, we use the\n[blaze-html] library, and some helpers from the `digestive-functors-blaze`\nlibrary.\n\n[blaze-html]: http://jaspervdj.be/blaze/\n\nLet's write a view for the `User` form. As you can see, we here refer to the\ndifferent fields in the `userForm`. The `errorList` will generate a list of\nerrors for the `\"mail\"` field.\n\n> userView :: View H.Html -> H.Html\n> userView view = do\n>     label     \"name\" view \"Name: \"\n>     inputText \"name\" view\n>     H.br\n>\n>     errorList \"mail\" view\n>     label     \"mail\" view \"Email address: \"\n>     inputText \"mail\" view\n>     H.br\n\nLike forms, views are also composable: let's illustrate that by adding a view\nfor the `releaseForm`, in which we reuse `userView`. In order to do this, we\ntake only the parts relevant to the author from the view by using `subView`. We\ncan then pass the resulting view to our own `userView`.\nWe have no special view code for `Package`, so we can just add that to\n`releaseView` as well. `childErrorList` will generate a list of errors for each\nchild of the specified form. In this case, this means a list of errors from\n`\"package.name\"` and `\"package.version\"`. Note how we use `foo.bar` to refer to\nnested forms.\n\n> releaseView :: View H.Html -> H.Html\n> releaseView view = do\n>     H.h2 \"Author\"\n>     userView $ subView \"author\" view\n>\n>     H.h2 \"Package\"\n>     childErrorList \"package\" view\n>\n>     label     \"package.name\" view \"Name: \"\n>     inputText \"package.name\" view\n>     H.br\n>\n>     label     \"package.version\" view \"Version: \"\n>     inputText \"package.version\" view\n>     H.br\n>\n>     label       \"package.category\" view \"Category: \"\n>     inputSelect \"package.category\" view\n>     H.br\n\nThe attentive reader might have wondered what the type parameter for `View` is:\nit is the `String`-like type used for e.g. error messages.\nBut wait! We have\n    releaseForm :: Monad m => Form Text m Release\n    releaseView :: View H.Html -> H.Html\n... doesn't this mean that we need a `View Text` rather than a `View Html`?  The\nanswer is yes -- but having `View Html` allows us to write these views more\neasily with the `digestive-functors-blaze` library. Fortunately, we will be able\nto fix this using the `Functor` instance of `View`.\n    fmap :: Monad m => (v -> w) -> View v -> View w\nA backend\n---------\nTo finish this tutorial, we need to be able to actually run this code. We need\nan HTTP server for that, and we use [Happstack] for this tutorial. The\n`digestive-functors-happstack` library gives about everything we need for this.\n[Happstack]: http://happstack.com/\n\n> site :: Happstack.ServerPart Happstack.Response\n> site = do\n>     Happstack.decodeBody $ Happstack.defaultBodyPolicy \"/tmp\" 4096 4096 4096\n>     r <- runForm \"test\" releaseForm\n>     case r of\n>         (view, Nothing) -> do\n>             let view' = fmap H.toHtml view\n>             Happstack.ok $ Happstack.toResponse $\n>                 template $\n>                     form view' \"/\" $ do\n>                         releaseView view'\n>                         H.br\n>                         inputSubmit \"Submit\"\n>         (_, Just release) -> Happstack.ok $ Happstack.toResponse $\n>             template $ do\n>                 css\n>                 H.h1 \"Release received\"\n>                 H.p $ H.toHtml $ show release\n>\n> main :: IO ()\n> main = Happstack.simpleHTTP Happstack.nullConf site\n\nUtilities\n---------\n\n> template :: H.Html -> H.Html\n> template body = H.docTypeHtml $ do\n>     H.head $ do\n>         H.title \"digestive-functors tutorial\"\n>         css\n>     H.body body\n> css :: H.Html\n> css = H.style ! A.type_ \"text/css\" $ do\n>     \"label {width: 130px; float: left; clear: both}\"\n>     \"ul.digestive-functors-error-list {\"\n>     \"    color: red;\"\n>     \"    list-style-type: none;\"\n>     \"    padding-left: 0px;\"\n>     \"}\"\n    </textarea>\n  </form>\n\n  <p><strong>MIME types\n  defined:</strong> <code>text/x-literate-haskell</code>.</p>\n\n  <p>Parser configuration parameters recognized: <code>base</code> to\n  set the base mode (defaults to <code>\"haskell\"</code>).</p>\n\n  <script>\n    var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {mode: \"haskell-literate\"});\n  </script>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/haxe/haxe.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"haxe\", function(config, parserConfig) {\n  var indentUnit = config.indentUnit;\n\n  // Tokenizer\n\n  function kw(type) {return {type: type, style: \"keyword\"};}\n  var A = kw(\"keyword a\"), B = kw(\"keyword b\"), C = kw(\"keyword c\");\n  var operator = kw(\"operator\"), atom = {type: \"atom\", style: \"atom\"}, attribute = {type:\"attribute\", style: \"attribute\"};\n  var type = kw(\"typedef\");\n  var keywords = {\n    \"if\": A, \"while\": A, \"else\": B, \"do\": B, \"try\": B,\n    \"return\": C, \"break\": C, \"continue\": C, \"new\": C, \"throw\": C,\n    \"var\": kw(\"var\"), \"inline\":attribute, \"static\": attribute, \"using\":kw(\"import\"),\n    \"public\": attribute, \"private\": attribute, \"cast\": kw(\"cast\"), \"import\": kw(\"import\"), \"macro\": kw(\"macro\"),\n    \"function\": kw(\"function\"), \"catch\": kw(\"catch\"), \"untyped\": kw(\"untyped\"), \"callback\": kw(\"cb\"),\n    \"for\": kw(\"for\"), \"switch\": kw(\"switch\"), \"case\": kw(\"case\"), \"default\": kw(\"default\"),\n    \"in\": operator, \"never\": kw(\"property_access\"), \"trace\":kw(\"trace\"),\n    \"class\": type, \"abstract\":type, \"enum\":type, \"interface\":type, \"typedef\":type, \"extends\":type, \"implements\":type, \"dynamic\":type,\n    \"true\": atom, \"false\": atom, \"null\": atom\n  };\n\n  var isOperatorChar = /[+\\-*&%=<>!?|]/;\n\n  function chain(stream, state, f) {\n    state.tokenize = f;\n    return f(stream, state);\n  }\n\n  function toUnescaped(stream, end) {\n    var escaped = false, next;\n    while ((next = stream.next()) != null) {\n      if (next == end && !escaped)\n        return true;\n      escaped = !escaped && next == \"\\\\\";\n    }\n  }\n\n  // Used as scratch variables to communicate multiple values without\n  // consing up tons of objects.\n  var type, content;\n  function ret(tp, style, cont) {\n    type = tp; content = cont;\n    return style;\n  }\n\n  function haxeTokenBase(stream, state) {\n    var ch = stream.next();\n    if (ch == '\"' || ch == \"'\") {\n      return chain(stream, state, haxeTokenString(ch));\n    } else if (/[\\[\\]{}\\(\\),;\\:\\.]/.test(ch)) {\n      return ret(ch);\n    } else if (ch == \"0\" && stream.eat(/x/i)) {\n      stream.eatWhile(/[\\da-f]/i);\n      return ret(\"number\", \"number\");\n    } else if (/\\d/.test(ch) || ch == \"-\" && stream.eat(/\\d/)) {\n      stream.match(/^\\d*(?:\\.\\d*(?!\\.))?(?:[eE][+\\-]?\\d+)?/);\n      return ret(\"number\", \"number\");\n    } else if (state.reAllowed && (ch == \"~\" && stream.eat(/\\//))) {\n      toUnescaped(stream, \"/\");\n      stream.eatWhile(/[gimsu]/);\n      return ret(\"regexp\", \"string-2\");\n    } else if (ch == \"/\") {\n      if (stream.eat(\"*\")) {\n        return chain(stream, state, haxeTokenComment);\n      } else if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return ret(\"comment\", \"comment\");\n      } else {\n        stream.eatWhile(isOperatorChar);\n        return ret(\"operator\", null, stream.current());\n      }\n    } else if (ch == \"#\") {\n        stream.skipToEnd();\n        return ret(\"conditional\", \"meta\");\n    } else if (ch == \"@\") {\n      stream.eat(/:/);\n      stream.eatWhile(/[\\w_]/);\n      return ret (\"metadata\", \"meta\");\n    } else if (isOperatorChar.test(ch)) {\n      stream.eatWhile(isOperatorChar);\n      return ret(\"operator\", null, stream.current());\n    } else {\n      var word;\n      if(/[A-Z]/.test(ch)) {\n        stream.eatWhile(/[\\w_<>]/);\n        word = stream.current();\n        return ret(\"type\", \"variable-3\", word);\n      } else {\n        stream.eatWhile(/[\\w_]/);\n        var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];\n        return (known && state.kwAllowed) ? ret(known.type, known.style, word) :\n                       ret(\"variable\", \"variable\", word);\n      }\n    }\n  }\n\n  function haxeTokenString(quote) {\n    return function(stream, state) {\n      if (toUnescaped(stream, quote))\n        state.tokenize = haxeTokenBase;\n      return ret(\"string\", \"string\");\n    };\n  }\n\n  function haxeTokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = haxeTokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return ret(\"comment\", \"comment\");\n  }\n\n  // Parser\n\n  var atomicTypes = {\"atom\": true, \"number\": true, \"variable\": true, \"string\": true, \"regexp\": true};\n\n  function HaxeLexical(indented, column, type, align, prev, info) {\n    this.indented = indented;\n    this.column = column;\n    this.type = type;\n    this.prev = prev;\n    this.info = info;\n    if (align != null) this.align = align;\n  }\n\n  function inScope(state, varname) {\n    for (var v = state.localVars; v; v = v.next)\n      if (v.name == varname) return true;\n  }\n\n  function parseHaxe(state, style, type, content, stream) {\n    var cc = state.cc;\n    // Communicate our context to the combinators.\n    // (Less wasteful than consing up a hundred closures on every call.)\n    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;\n\n    if (!state.lexical.hasOwnProperty(\"align\"))\n      state.lexical.align = true;\n\n    while(true) {\n      var combinator = cc.length ? cc.pop() : statement;\n      if (combinator(type, content)) {\n        while(cc.length && cc[cc.length - 1].lex)\n          cc.pop()();\n        if (cx.marked) return cx.marked;\n        if (type == \"variable\" && inScope(state, content)) return \"variable-2\";\n        if (type == \"variable\" && imported(state, content)) return \"variable-3\";\n        return style;\n      }\n    }\n  }\n\n  function imported(state, typename) {\n    if (/[a-z]/.test(typename.charAt(0)))\n      return false;\n    var len = state.importedtypes.length;\n    for (var i = 0; i<len; i++)\n      if(state.importedtypes[i]==typename) return true;\n  }\n\n  function registerimport(importname) {\n    var state = cx.state;\n    for (var t = state.importedtypes; t; t = t.next)\n      if(t.name == importname) return;\n    state.importedtypes = { name: importname, next: state.importedtypes };\n  }\n  // Combinator utils\n\n  var cx = {state: null, column: null, marked: null, cc: null};\n  function pass() {\n    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);\n  }\n  function cont() {\n    pass.apply(null, arguments);\n    return true;\n  }\n  function inList(name, list) {\n    for (var v = list; v; v = v.next)\n      if (v.name == name) return true;\n    return false;\n  }\n  function register(varname) {\n    var state = cx.state;\n    if (state.context) {\n      cx.marked = \"def\";\n      if (inList(varname, state.localVars)) return;\n      state.localVars = {name: varname, next: state.localVars};\n    } else if (state.globalVars) {\n      if (inList(varname, state.globalVars)) return;\n      state.globalVars = {name: varname, next: state.globalVars};\n    }\n  }\n\n  // Combinators\n\n  var defaultVars = {name: \"this\", next: null};\n  function pushcontext() {\n    if (!cx.state.context) cx.state.localVars = defaultVars;\n    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};\n  }\n  function popcontext() {\n    cx.state.localVars = cx.state.context.vars;\n    cx.state.context = cx.state.context.prev;\n  }\n  popcontext.lex = true;\n  function pushlex(type, info) {\n    var result = function() {\n      var state = cx.state;\n      state.lexical = new HaxeLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);\n    };\n    result.lex = true;\n    return result;\n  }\n  function poplex() {\n    var state = cx.state;\n    if (state.lexical.prev) {\n      if (state.lexical.type == \")\")\n        state.indented = state.lexical.indented;\n      state.lexical = state.lexical.prev;\n    }\n  }\n  poplex.lex = true;\n\n  function expect(wanted) {\n    function f(type) {\n      if (type == wanted) return cont();\n      else if (wanted == \";\") return pass();\n      else return cont(f);\n    }\n    return f;\n  }\n\n  function statement(type) {\n    if (type == \"@\") return cont(metadef);\n    if (type == \"var\") return cont(pushlex(\"vardef\"), vardef1, expect(\";\"), poplex);\n    if (type == \"keyword a\") return cont(pushlex(\"form\"), expression, statement, poplex);\n    if (type == \"keyword b\") return cont(pushlex(\"form\"), statement, poplex);\n    if (type == \"{\") return cont(pushlex(\"}\"), pushcontext, block, poplex, popcontext);\n    if (type == \";\") return cont();\n    if (type == \"attribute\") return cont(maybeattribute);\n    if (type == \"function\") return cont(functiondef);\n    if (type == \"for\") return cont(pushlex(\"form\"), expect(\"(\"), pushlex(\")\"), forspec1, expect(\")\"),\n                                   poplex, statement, poplex);\n    if (type == \"variable\") return cont(pushlex(\"stat\"), maybelabel);\n    if (type == \"switch\") return cont(pushlex(\"form\"), expression, pushlex(\"}\", \"switch\"), expect(\"{\"),\n                                      block, poplex, poplex);\n    if (type == \"case\") return cont(expression, expect(\":\"));\n    if (type == \"default\") return cont(expect(\":\"));\n    if (type == \"catch\") return cont(pushlex(\"form\"), pushcontext, expect(\"(\"), funarg, expect(\")\"),\n                                     statement, poplex, popcontext);\n    if (type == \"import\") return cont(importdef, expect(\";\"));\n    if (type == \"typedef\") return cont(typedef);\n    return pass(pushlex(\"stat\"), expression, expect(\";\"), poplex);\n  }\n  function expression(type) {\n    if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);\n    if (type == \"type\" ) return cont(maybeoperator);\n    if (type == \"function\") return cont(functiondef);\n    if (type == \"keyword c\") return cont(maybeexpression);\n    if (type == \"(\") return cont(pushlex(\")\"), maybeexpression, expect(\")\"), poplex, maybeoperator);\n    if (type == \"operator\") return cont(expression);\n    if (type == \"[\") return cont(pushlex(\"]\"), commasep(maybeexpression, \"]\"), poplex, maybeoperator);\n    if (type == \"{\") return cont(pushlex(\"}\"), commasep(objprop, \"}\"), poplex, maybeoperator);\n    return cont();\n  }\n  function maybeexpression(type) {\n    if (type.match(/[;\\}\\)\\],]/)) return pass();\n    return pass(expression);\n  }\n\n  function maybeoperator(type, value) {\n    if (type == \"operator\" && /\\+\\+|--/.test(value)) return cont(maybeoperator);\n    if (type == \"operator\" || type == \":\") return cont(expression);\n    if (type == \";\") return;\n    if (type == \"(\") return cont(pushlex(\")\"), commasep(expression, \")\"), poplex, maybeoperator);\n    if (type == \".\") return cont(property, maybeoperator);\n    if (type == \"[\") return cont(pushlex(\"]\"), expression, expect(\"]\"), poplex, maybeoperator);\n  }\n\n  function maybeattribute(type) {\n    if (type == \"attribute\") return cont(maybeattribute);\n    if (type == \"function\") return cont(functiondef);\n    if (type == \"var\") return cont(vardef1);\n  }\n\n  function metadef(type) {\n    if(type == \":\") return cont(metadef);\n    if(type == \"variable\") return cont(metadef);\n    if(type == \"(\") return cont(pushlex(\")\"), commasep(metaargs, \")\"), poplex, statement);\n  }\n  function metaargs(type) {\n    if(type == \"variable\") return cont();\n  }\n\n  function importdef (type, value) {\n    if(type == \"variable\" && /[A-Z]/.test(value.charAt(0))) { registerimport(value); return cont(); }\n    else if(type == \"variable\" || type == \"property\" || type == \".\" || value == \"*\") return cont(importdef);\n  }\n\n  function typedef (type, value)\n  {\n    if(type == \"variable\" && /[A-Z]/.test(value.charAt(0))) { registerimport(value); return cont(); }\n    else if (type == \"type\" && /[A-Z]/.test(value.charAt(0))) { return cont(); }\n  }\n\n  function maybelabel(type) {\n    if (type == \":\") return cont(poplex, statement);\n    return pass(maybeoperator, expect(\";\"), poplex);\n  }\n  function property(type) {\n    if (type == \"variable\") {cx.marked = \"property\"; return cont();}\n  }\n  function objprop(type) {\n    if (type == \"variable\") cx.marked = \"property\";\n    if (atomicTypes.hasOwnProperty(type)) return cont(expect(\":\"), expression);\n  }\n  function commasep(what, end) {\n    function proceed(type) {\n      if (type == \",\") return cont(what, proceed);\n      if (type == end) return cont();\n      return cont(expect(end));\n    }\n    return function(type) {\n      if (type == end) return cont();\n      else return pass(what, proceed);\n    };\n  }\n  function block(type) {\n    if (type == \"}\") return cont();\n    return pass(statement, block);\n  }\n  function vardef1(type, value) {\n    if (type == \"variable\"){register(value); return cont(typeuse, vardef2);}\n    return cont();\n  }\n  function vardef2(type, value) {\n    if (value == \"=\") return cont(expression, vardef2);\n    if (type == \",\") return cont(vardef1);\n  }\n  function forspec1(type, value) {\n    if (type == \"variable\") {\n      register(value);\n      return cont(forin, expression)\n    } else {\n      return pass()\n    }\n  }\n  function forin(_type, value) {\n    if (value == \"in\") return cont();\n  }\n  function functiondef(type, value) {\n    //function names starting with upper-case letters are recognised as types, so cludging them together here.\n    if (type == \"variable\" || type == \"type\") {register(value); return cont(functiondef);}\n    if (value == \"new\") return cont(functiondef);\n    if (type == \"(\") return cont(pushlex(\")\"), pushcontext, commasep(funarg, \")\"), poplex, typeuse, statement, popcontext);\n  }\n  function typeuse(type) {\n    if(type == \":\") return cont(typestring);\n  }\n  function typestring(type) {\n    if(type == \"type\") return cont();\n    if(type == \"variable\") return cont();\n    if(type == \"{\") return cont(pushlex(\"}\"), commasep(typeprop, \"}\"), poplex);\n  }\n  function typeprop(type) {\n    if(type == \"variable\") return cont(typeuse);\n  }\n  function funarg(type, value) {\n    if (type == \"variable\") {register(value); return cont(typeuse);}\n  }\n\n  // Interface\n  return {\n    startState: function(basecolumn) {\n      var defaulttypes = [\"Int\", \"Float\", \"String\", \"Void\", \"Std\", \"Bool\", \"Dynamic\", \"Array\"];\n      var state = {\n        tokenize: haxeTokenBase,\n        reAllowed: true,\n        kwAllowed: true,\n        cc: [],\n        lexical: new HaxeLexical((basecolumn || 0) - indentUnit, 0, \"block\", false),\n        localVars: parserConfig.localVars,\n        importedtypes: defaulttypes,\n        context: parserConfig.localVars && {vars: parserConfig.localVars},\n        indented: 0\n      };\n      if (parserConfig.globalVars && typeof parserConfig.globalVars == \"object\")\n        state.globalVars = parserConfig.globalVars;\n      return state;\n    },\n\n    token: function(stream, state) {\n      if (stream.sol()) {\n        if (!state.lexical.hasOwnProperty(\"align\"))\n          state.lexical.align = false;\n        state.indented = stream.indentation();\n      }\n      if (stream.eatSpace()) return null;\n      var style = state.tokenize(stream, state);\n      if (type == \"comment\") return style;\n      state.reAllowed = !!(type == \"operator\" || type == \"keyword c\" || type.match(/^[\\[{}\\(,;:]$/));\n      state.kwAllowed = type != '.';\n      return parseHaxe(state, style, type, content, stream);\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize != haxeTokenBase) return 0;\n      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;\n      if (lexical.type == \"stat\" && firstChar == \"}\") lexical = lexical.prev;\n      var type = lexical.type, closing = firstChar == type;\n      if (type == \"vardef\") return lexical.indented + 4;\n      else if (type == \"form\" && firstChar == \"{\") return lexical.indented;\n      else if (type == \"stat\" || type == \"form\") return lexical.indented + indentUnit;\n      else if (lexical.info == \"switch\" && !closing)\n        return lexical.indented + (/^(?:case|default)\\b/.test(textAfter) ? indentUnit : 2 * indentUnit);\n      else if (lexical.align) return lexical.column + (closing ? 0 : 1);\n      else return lexical.indented + (closing ? 0 : indentUnit);\n    },\n\n    electricChars: \"{}\",\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    lineComment: \"//\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-haxe\", \"haxe\");\n\nCodeMirror.defineMode(\"hxml\", function () {\n\n  return {\n    startState: function () {\n      return {\n        define: false,\n        inString: false\n      };\n    },\n    token: function (stream, state) {\n      var ch = stream.peek();\n      var sol = stream.sol();\n\n      ///* comments */\n      if (ch == \"#\") {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n      if (sol && ch == \"-\") {\n        var style = \"variable-2\";\n\n        stream.eat(/-/);\n\n        if (stream.peek() == \"-\") {\n          stream.eat(/-/);\n          style = \"keyword a\";\n        }\n\n        if (stream.peek() == \"D\") {\n          stream.eat(/[D]/);\n          style = \"keyword c\";\n          state.define = true;\n        }\n\n        stream.eatWhile(/[A-Z]/i);\n        return style;\n      }\n\n      var ch = stream.peek();\n\n      if (state.inString == false && ch == \"'\") {\n        state.inString = true;\n        stream.next();\n      }\n\n      if (state.inString == true) {\n        if (stream.skipTo(\"'\")) {\n\n        } else {\n          stream.skipToEnd();\n        }\n\n        if (stream.peek() == \"'\") {\n          stream.next();\n          state.inString = false;\n        }\n\n        return \"string\";\n      }\n\n      stream.next();\n      return null;\n    },\n    lineComment: \"#\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-hxml\", \"hxml\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/haxe/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Haxe mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"haxe.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Haxe</a>\n  </ul>\n</div>\n\n<article>\n<h2>Haxe mode</h2>\n\n\n<div><p><textarea id=\"code-haxe\" name=\"code\">\nimport one.two.Three;\n\n@attr(\"test\")\nclass Foo&lt;T&gt; extends Three\n{\n\tpublic function new()\n\t{\n\t\tnoFoo = 12;\n\t}\n\t\n\tpublic static inline function doFoo(obj:{k:Int, l:Float}):Int\n\t{\n\t\tfor(i in 0...10)\n\t\t{\n\t\t\tobj.k++;\n\t\t\ttrace(i);\n\t\t\tvar var1 = new Array();\n\t\t\tif(var1.length > 1)\n\t\t\t\tthrow \"Error\";\n\t\t}\n\t\t// The following line should not be colored, the variable is scoped out\n\t\tvar1;\n\t\t/* Multi line\n\t\t * Comment test\n\t\t */\n\t\treturn obj.k;\n\t}\n\tprivate function bar():Void\n\t{\n\t\t#if flash\n\t\tvar t1:String = \"1.21\";\n\t\t#end\n\t\ttry {\n\t\t\tdoFoo({k:3, l:1.2});\n\t\t}\n\t\tcatch (e : String) {\n\t\t\ttrace(e);\n\t\t}\n\t\tvar t2:Float = cast(3.2);\n\t\tvar t3:haxe.Timer = new haxe.Timer();\n\t\tvar t4 = {k:Std.int(t2), l:Std.parseFloat(t1)};\n\t\tvar t5 = ~/123+.*$/i;\n\t\tdoFoo(t4);\n\t\tuntyped t1 = 4;\n\t\tbob = new Foo&lt;Int&gt;\n\t}\n\tpublic var okFoo(default, never):Float;\n\tvar noFoo(getFoo, null):Int;\n\tfunction getFoo():Int {\n\t\treturn noFoo;\n\t}\n\t\n\tpublic var three:Int;\n}\nenum Color\n{\n\tred;\n\tgreen;\n\tblue;\n\tgrey( v : Int );\n\trgb (r:Int,g:Int,b:Int);\n}\n</textarea></p>\n\n<p>Hxml mode:</p>\n\n<p><textarea id=\"code-hxml\">\n-cp test\n-js path/to/file.js\n#-remap nme:flash\n--next\n-D source-map-content\n-cmd 'test'\n-lib lime\n</textarea></p>\n</div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code-haxe\"), {\n      \tmode: \"haxe\",\n        lineNumbers: true,\n        indentUnit: 4,\n        indentWithTabs: true\n      });\n      \n      editor = CodeMirror.fromTextArea(document.getElementById(\"code-hxml\"), {\n      \tmode: \"hxml\",\n        lineNumbers: true,\n        indentUnit: 4,\n        indentWithTabs: true\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-haxe, text/x-hxml</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/htmlembedded/htmlembedded.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../htmlmixed/htmlmixed\"),\n        require(\"../../addon/mode/multiplex\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../htmlmixed/htmlmixed\",\n            \"../../addon/mode/multiplex\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"htmlembedded\", function(config, parserConfig) {\n    var closeComment = parserConfig.closeComment || \"--%>\"\n    return CodeMirror.multiplexingMode(CodeMirror.getMode(config, \"htmlmixed\"), {\n      open: parserConfig.openComment || \"<%--\",\n      close: closeComment,\n      delimStyle: \"comment\",\n      mode: {token: function(stream) {\n        stream.skipTo(closeComment) || stream.skipToEnd()\n        return \"comment\"\n      }}\n    }, {\n      open: parserConfig.open || parserConfig.scriptStartRegex || \"<%\",\n      close: parserConfig.close || parserConfig.scriptEndRegex || \"%>\",\n      mode: CodeMirror.getMode(config, parserConfig.scriptingModeSpec)\n    });\n  }, \"htmlmixed\");\n\n  CodeMirror.defineMIME(\"application/x-ejs\", {name: \"htmlembedded\", scriptingModeSpec:\"javascript\"});\n  CodeMirror.defineMIME(\"application/x-aspx\", {name: \"htmlembedded\", scriptingModeSpec:\"text/x-csharp\"});\n  CodeMirror.defineMIME(\"application/x-jsp\", {name: \"htmlembedded\", scriptingModeSpec:\"text/x-java\"});\n  CodeMirror.defineMIME(\"application/x-erb\", {name: \"htmlembedded\", scriptingModeSpec:\"ruby\"});\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/htmlembedded/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Html Embedded Scripts mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../javascript/javascript.js\"></script>\n<script src=\"../css/css.js\"></script>\n<script src=\"../htmlmixed/htmlmixed.js\"></script>\n<script src=\"../../addon/mode/multiplex.js\"></script>\n<script src=\"htmlembedded.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Html Embedded Scripts</a>\n  </ul>\n</div>\n\n<article>\n<h2>Html Embedded Scripts mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n<%\nfunction hello(who) {\n\treturn \"Hello \" + who;\n}\n%>\nThis is an example of EJS (embedded javascript)\n<p>The program says <%= hello(\"world\") %>.</p>\n<script>\n\talert(\"And here is some normal JS code\"); // also colored\n</script>\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        mode: \"application/x-ejs\",\n        indentUnit: 4,\n        indentWithTabs: true\n      });\n    </script>\n\n    <p>Mode for html embedded scripts like JSP and ASP.NET. Depends on multiplex and HtmlMixed which in turn depends on\n    JavaScript, CSS and XML.<br />Other dependencies include those of the scripting language chosen.</p>\n\n    <p><strong>MIME types defined:</strong> <code>application/x-aspx</code> (ASP.NET),\n    <code>application/x-ejs</code> (Embedded JavaScript), <code>application/x-jsp</code> (JavaServer Pages)\n    and <code>application/x-erb</code></p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/htmlmixed/htmlmixed.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../xml/xml\"), require(\"../javascript/javascript\"), require(\"../css/css\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../xml/xml\", \"../javascript/javascript\", \"../css/css\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var defaultTags = {\n    script: [\n      [\"lang\", /(javascript|babel)/i, \"javascript\"],\n      [\"type\", /^(?:text|application)\\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, \"javascript\"],\n      [\"type\", /./, \"text/plain\"],\n      [null, null, \"javascript\"]\n    ],\n    style:  [\n      [\"lang\", /^css$/i, \"css\"],\n      [\"type\", /^(text\\/)?(x-)?(stylesheet|css)$/i, \"css\"],\n      [\"type\", /./, \"text/plain\"],\n      [null, null, \"css\"]\n    ]\n  };\n\n  function maybeBackup(stream, pat, style) {\n    var cur = stream.current(), close = cur.search(pat);\n    if (close > -1) {\n      stream.backUp(cur.length - close);\n    } else if (cur.match(/<\\/?$/)) {\n      stream.backUp(cur.length);\n      if (!stream.match(pat, false)) stream.match(cur);\n    }\n    return style;\n  }\n\n  var attrRegexpCache = {};\n  function getAttrRegexp(attr) {\n    var regexp = attrRegexpCache[attr];\n    if (regexp) return regexp;\n    return attrRegexpCache[attr] = new RegExp(\"\\\\s+\" + attr + \"\\\\s*=\\\\s*('|\\\")?([^'\\\"]+)('|\\\")?\\\\s*\");\n  }\n\n  function getAttrValue(text, attr) {\n    var match = text.match(getAttrRegexp(attr))\n    return match ? /^\\s*(.*?)\\s*$/.exec(match[2])[1] : \"\"\n  }\n\n  function getTagRegexp(tagName, anchored) {\n    return new RegExp((anchored ? \"^\" : \"\") + \"<\\/\\\\s*\" + tagName + \"\\\\s*>\", \"i\");\n  }\n\n  function addTags(from, to) {\n    for (var tag in from) {\n      var dest = to[tag] || (to[tag] = []);\n      var source = from[tag];\n      for (var i = source.length - 1; i >= 0; i--)\n        dest.unshift(source[i])\n    }\n  }\n\n  function findMatchingMode(tagInfo, tagText) {\n    for (var i = 0; i < tagInfo.length; i++) {\n      var spec = tagInfo[i];\n      if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];\n    }\n  }\n\n  CodeMirror.defineMode(\"htmlmixed\", function (config, parserConfig) {\n    var htmlMode = CodeMirror.getMode(config, {\n      name: \"xml\",\n      htmlMode: true,\n      multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,\n      multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag,\n      allowMissingTagName: parserConfig.allowMissingTagName,\n    });\n\n    var tags = {};\n    var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;\n    addTags(defaultTags, tags);\n    if (configTags) addTags(configTags, tags);\n    if (configScript) for (var i = configScript.length - 1; i >= 0; i--)\n      tags.script.unshift([\"type\", configScript[i].matches, configScript[i].mode])\n\n    function html(stream, state) {\n      var style = htmlMode.token(stream, state.htmlState), tag = /\\btag\\b/.test(style), tagName\n      if (tag && !/[<>\\s\\/]/.test(stream.current()) &&\n          (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&\n          tags.hasOwnProperty(tagName)) {\n        state.inTag = tagName + \" \"\n      } else if (state.inTag && tag && />$/.test(stream.current())) {\n        var inTag = /^([\\S]+) (.*)/.exec(state.inTag)\n        state.inTag = null\n        var modeSpec = stream.current() == \">\" && findMatchingMode(tags[inTag[1]], inTag[2])\n        var mode = CodeMirror.getMode(config, modeSpec)\n        var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);\n        state.token = function (stream, state) {\n          if (stream.match(endTagA, false)) {\n            state.token = html;\n            state.localState = state.localMode = null;\n            return null;\n          }\n          return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));\n        };\n        state.localMode = mode;\n        state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, \"\", \"\"));\n      } else if (state.inTag) {\n        state.inTag += stream.current()\n        if (stream.eol()) state.inTag += \" \"\n      }\n      return style;\n    };\n\n    return {\n      startState: function () {\n        var state = CodeMirror.startState(htmlMode);\n        return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};\n      },\n\n      copyState: function (state) {\n        var local;\n        if (state.localState) {\n          local = CodeMirror.copyState(state.localMode, state.localState);\n        }\n        return {token: state.token, inTag: state.inTag,\n                localMode: state.localMode, localState: local,\n                htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};\n      },\n\n      token: function (stream, state) {\n        return state.token(stream, state);\n      },\n\n      indent: function (state, textAfter, line) {\n        if (!state.localMode || /^\\s*<\\//.test(textAfter))\n          return htmlMode.indent(state.htmlState, textAfter, line);\n        else if (state.localMode.indent)\n          return state.localMode.indent(state.localState, textAfter, line);\n        else\n          return CodeMirror.Pass;\n      },\n\n      innerMode: function (state) {\n        return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};\n      }\n    };\n  }, \"xml\", \"javascript\", \"css\");\n\n  CodeMirror.defineMIME(\"text/html\", \"htmlmixed\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/htmlmixed/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: HTML mixed mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/selection/selection-pointer.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../javascript/javascript.js\"></script>\n<script src=\"../css/css.js\"></script>\n<script src=\"../vbscript/vbscript.js\"></script>\n<script src=\"htmlmixed.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">HTML mixed</a>\n  </ul>\n</div>\n\n<article>\n<h2>HTML mixed mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n<html style=\"color: green\">\n  <!-- this is a comment -->\n  <head>\n    <title>Mixed HTML Example</title>\n    <style>\n      h1 {font-family: comic sans; color: #f0f;}\n      div {background: yellow !important;}\n      body {\n        max-width: 50em;\n        margin: 1em 2em 1em 5em;\n      }\n    </style>\n  </head>\n  <body>\n    <h1>Mixed HTML Example</h1>\n    <script>\n      function jsFunc(arg1, arg2) {\n        if (arg1 && arg2) document.body.innerHTML = \"achoo\";\n      }\n    </script>\n  </body>\n</html>\n</textarea></form>\n    <script>\n      // Define an extended mixed-mode that understands vbscript and\n      // leaves mustache/handlebars embedded templates in html mode\n      var mixedMode = {\n        name: \"htmlmixed\",\n        scriptTypes: [{matches: /\\/x-handlebars-template|\\/x-mustache/i,\n                       mode: null},\n                      {matches: /(text|application)\\/(x-)?vb(a|script)/i,\n                       mode: \"vbscript\"}]\n      };\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: mixedMode,\n        selectionPointer: true\n      });\n    </script>\n\n    <p>The HTML mixed mode depends on the XML, JavaScript, and CSS modes.</p>\n\n    <p>It takes an optional mode configuration\n    option, <code>tags</code>, which can be used to add custom\n    behavior for specific tags. When given, it should be an object\n    mapping tag names (for example <code>script</code>) to arrays or\n    three-element arrays. Those inner arrays indicate [attributeName,\n    valueRegexp, <a href=\"../../doc/manual.html#option_mode\">modeSpec</a>]\n    specifications. For example, you could use <code>[\"type\", /^foo$/,\n    \"foo\"]</code> to map the attribute <code>type=\"foo\"</code> to\n    the <code>foo</code> mode. When the first two fields are null\n    (<code>[null, null, \"mode\"]</code>), the given mode is used for\n    any such tag that doesn't match any of the previously given\n    attributes. For example:</p>\n\n    <pre>var myModeSpec = {\n  name: \"htmlmixed\",\n  tags: {\n    style: [[\"type\", /^text\\/(x-)?scss$/, \"text/x-scss\"],\n            [null, null, \"css\"]],\n    custom: [[null, null, \"customMode\"]]\n  }\n}</pre>\n\n    <p><strong>MIME types defined:</strong> <code>text/html</code>\n    (redefined, only takes effect if you load this parser after the\n    XML parser).</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/http/http.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"http\", function() {\n  function failFirstLine(stream, state) {\n    stream.skipToEnd();\n    state.cur = header;\n    return \"error\";\n  }\n\n  function start(stream, state) {\n    if (stream.match(/^HTTP\\/\\d\\.\\d/)) {\n      state.cur = responseStatusCode;\n      return \"keyword\";\n    } else if (stream.match(/^[A-Z]+/) && /[ \\t]/.test(stream.peek())) {\n      state.cur = requestPath;\n      return \"keyword\";\n    } else {\n      return failFirstLine(stream, state);\n    }\n  }\n\n  function responseStatusCode(stream, state) {\n    var code = stream.match(/^\\d+/);\n    if (!code) return failFirstLine(stream, state);\n\n    state.cur = responseStatusText;\n    var status = Number(code[0]);\n    if (status >= 100 && status < 200) {\n      return \"positive informational\";\n    } else if (status >= 200 && status < 300) {\n      return \"positive success\";\n    } else if (status >= 300 && status < 400) {\n      return \"positive redirect\";\n    } else if (status >= 400 && status < 500) {\n      return \"negative client-error\";\n    } else if (status >= 500 && status < 600) {\n      return \"negative server-error\";\n    } else {\n      return \"error\";\n    }\n  }\n\n  function responseStatusText(stream, state) {\n    stream.skipToEnd();\n    state.cur = header;\n    return null;\n  }\n\n  function requestPath(stream, state) {\n    stream.eatWhile(/\\S/);\n    state.cur = requestProtocol;\n    return \"string-2\";\n  }\n\n  function requestProtocol(stream, state) {\n    if (stream.match(/^HTTP\\/\\d\\.\\d$/)) {\n      state.cur = header;\n      return \"keyword\";\n    } else {\n      return failFirstLine(stream, state);\n    }\n  }\n\n  function header(stream) {\n    if (stream.sol() && !stream.eat(/[ \\t]/)) {\n      if (stream.match(/^.*?:/)) {\n        return \"atom\";\n      } else {\n        stream.skipToEnd();\n        return \"error\";\n      }\n    } else {\n      stream.skipToEnd();\n      return \"string\";\n    }\n  }\n\n  function body(stream) {\n    stream.skipToEnd();\n    return null;\n  }\n\n  return {\n    token: function(stream, state) {\n      var cur = state.cur;\n      if (cur != header && cur != body && stream.eatSpace()) return null;\n      return cur(stream, state);\n    },\n\n    blankLine: function(state) {\n      state.cur = body;\n    },\n\n    startState: function() {\n      return {cur: start};\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"message/http\", \"http\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/http/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: HTTP mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"http.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">HTTP</a>\n  </ul>\n</div>\n\n<article>\n<h2>HTTP mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\nPOST /somewhere HTTP/1.1\nHost: example.com\nIf-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT\nContent-Type: application/x-www-form-urlencoded;\n\tcharset=utf-8\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.11 (KHTML, like Gecko) Ubuntu/12.04 Chromium/20.0.1132.47 Chrome/20.0.1132.47 Safari/536.11\n\nThis is the request body!\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>message/http</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/idl/idl.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  function wordRegexp(words) {\n    return new RegExp('^((' + words.join(')|(') + '))\\\\b', 'i');\n  };\n\n  var builtinArray = [\n    'a_correlate', 'abs', 'acos', 'adapt_hist_equal', 'alog',\n    'alog2', 'alog10', 'amoeba', 'annotate', 'app_user_dir',\n    'app_user_dir_query', 'arg_present', 'array_equal', 'array_indices',\n    'arrow', 'ascii_template', 'asin', 'assoc', 'atan',\n    'axis', 'axis', 'bandpass_filter', 'bandreject_filter', 'barplot',\n    'bar_plot', 'beseli', 'beselj', 'beselk', 'besely',\n    'beta', 'biginteger', 'bilinear', 'bin_date', 'binary_template',\n    'bindgen', 'binomial', 'bit_ffs', 'bit_population', 'blas_axpy',\n    'blk_con', 'boolarr', 'boolean', 'boxplot', 'box_cursor',\n    'breakpoint', 'broyden', 'bubbleplot', 'butterworth', 'bytarr',\n    'byte', 'byteorder', 'bytscl', 'c_correlate', 'calendar',\n    'caldat', 'call_external', 'call_function', 'call_method',\n    'call_procedure', 'canny', 'catch', 'cd', 'cdf', 'ceil',\n    'chebyshev', 'check_math', 'chisqr_cvf', 'chisqr_pdf', 'choldc',\n    'cholsol', 'cindgen', 'cir_3pnt', 'clipboard', 'close',\n    'clust_wts', 'cluster', 'cluster_tree', 'cmyk_convert', 'code_coverage',\n    'color_convert', 'color_exchange', 'color_quan', 'color_range_map',\n    'colorbar', 'colorize_sample', 'colormap_applicable',\n    'colormap_gradient', 'colormap_rotation', 'colortable',\n    'comfit', 'command_line_args', 'common', 'compile_opt', 'complex',\n    'complexarr', 'complexround', 'compute_mesh_normals', 'cond', 'congrid',\n    'conj', 'constrained_min', 'contour', 'contour', 'convert_coord',\n    'convol', 'convol_fft', 'coord2to3', 'copy_lun', 'correlate',\n    'cos', 'cosh', 'cpu', 'cramer', 'createboxplotdata',\n    'create_cursor', 'create_struct', 'create_view', 'crossp', 'crvlength',\n    'ct_luminance', 'cti_test', 'cursor', 'curvefit', 'cv_coord',\n    'cvttobm', 'cw_animate', 'cw_animate_getp', 'cw_animate_load',\n    'cw_animate_run', 'cw_arcball', 'cw_bgroup', 'cw_clr_index',\n    'cw_colorsel', 'cw_defroi', 'cw_field', 'cw_filesel', 'cw_form',\n    'cw_fslider', 'cw_light_editor', 'cw_light_editor_get',\n    'cw_light_editor_set', 'cw_orient', 'cw_palette_editor',\n    'cw_palette_editor_get', 'cw_palette_editor_set', 'cw_pdmenu',\n    'cw_rgbslider', 'cw_tmpl', 'cw_zoom', 'db_exists',\n    'dblarr', 'dcindgen', 'dcomplex', 'dcomplexarr', 'define_key',\n    'define_msgblk', 'define_msgblk_from_file', 'defroi', 'defsysv',\n    'delvar', 'dendro_plot', 'dendrogram', 'deriv', 'derivsig',\n    'determ', 'device', 'dfpmin', 'diag_matrix', 'dialog_dbconnect',\n    'dialog_message', 'dialog_pickfile', 'dialog_printersetup',\n    'dialog_printjob', 'dialog_read_image',\n    'dialog_write_image', 'dictionary', 'digital_filter', 'dilate', 'dindgen',\n    'dissolve', 'dist', 'distance_measure', 'dlm_load', 'dlm_register',\n    'doc_library', 'double', 'draw_roi', 'edge_dog', 'efont',\n    'eigenql', 'eigenvec', 'ellipse', 'elmhes', 'emboss',\n    'empty', 'enable_sysrtn', 'eof', 'eos', 'erase',\n    'erf', 'erfc', 'erfcx', 'erode', 'errorplot',\n    'errplot', 'estimator_filter', 'execute', 'exit', 'exp',\n    'expand', 'expand_path', 'expint', 'extract', 'extract_slice',\n    'f_cvf', 'f_pdf', 'factorial', 'fft', 'file_basename',\n    'file_chmod', 'file_copy', 'file_delete', 'file_dirname',\n    'file_expand_path', 'file_gunzip', 'file_gzip', 'file_info',\n    'file_lines', 'file_link', 'file_mkdir', 'file_move',\n    'file_poll_input', 'file_readlink', 'file_same',\n    'file_search', 'file_tar', 'file_test', 'file_untar', 'file_unzip',\n    'file_which', 'file_zip', 'filepath', 'findgen', 'finite',\n    'fix', 'flick', 'float', 'floor', 'flow3',\n    'fltarr', 'flush', 'format_axis_values', 'forward_function', 'free_lun',\n    'fstat', 'fulstr', 'funct', 'function', 'fv_test',\n    'fx_root', 'fz_roots', 'gamma', 'gamma_ct', 'gauss_cvf',\n    'gauss_pdf', 'gauss_smooth', 'gauss2dfit', 'gaussfit',\n    'gaussian_function', 'gaussint', 'get_drive_list', 'get_dxf_objects',\n    'get_kbrd', 'get_login_info',\n    'get_lun', 'get_screen_size', 'getenv', 'getwindows', 'greg2jul',\n    'grib', 'grid_input', 'grid_tps', 'grid3', 'griddata',\n    'gs_iter', 'h_eq_ct', 'h_eq_int', 'hanning', 'hash',\n    'hdf', 'hdf5', 'heap_free', 'heap_gc', 'heap_nosave',\n    'heap_refcount', 'heap_save', 'help', 'hilbert', 'hist_2d',\n    'hist_equal', 'histogram', 'hls', 'hough', 'hqr',\n    'hsv', 'i18n_multibytetoutf8',\n    'i18n_multibytetowidechar', 'i18n_utf8tomultibyte',\n    'i18n_widechartomultibyte',\n    'ibeta', 'icontour', 'iconvertcoord', 'idelete', 'identity',\n    'idl_base64', 'idl_container', 'idl_validname',\n    'idlexbr_assistant', 'idlitsys_createtool',\n    'idlunit', 'iellipse', 'igamma', 'igetcurrent', 'igetdata',\n    'igetid', 'igetproperty', 'iimage', 'image', 'image_cont',\n    'image_statistics', 'image_threshold', 'imaginary', 'imap', 'indgen',\n    'int_2d', 'int_3d', 'int_tabulated', 'intarr', 'interpol',\n    'interpolate', 'interval_volume', 'invert', 'ioctl', 'iopen',\n    'ir_filter', 'iplot', 'ipolygon', 'ipolyline', 'iputdata',\n    'iregister', 'ireset', 'iresolve', 'irotate', 'isa',\n    'isave', 'iscale', 'isetcurrent', 'isetproperty', 'ishft',\n    'isocontour', 'isosurface', 'isurface', 'itext', 'itranslate',\n    'ivector', 'ivolume', 'izoom', 'journal', 'json_parse',\n    'json_serialize', 'jul2greg', 'julday', 'keyword_set', 'krig2d',\n    'kurtosis', 'kw_test', 'l64indgen', 'la_choldc', 'la_cholmprove',\n    'la_cholsol', 'la_determ', 'la_eigenproblem', 'la_eigenql', 'la_eigenvec',\n    'la_elmhes', 'la_gm_linear_model', 'la_hqr', 'la_invert',\n    'la_least_square_equality', 'la_least_squares', 'la_linear_equation',\n    'la_ludc', 'la_lumprove', 'la_lusol',\n    'la_svd', 'la_tridc', 'la_trimprove', 'la_triql', 'la_trired',\n    'la_trisol', 'label_date', 'label_region', 'ladfit', 'laguerre',\n    'lambda', 'lambdap', 'lambertw', 'laplacian', 'least_squares_filter',\n    'leefilt', 'legend', 'legendre', 'linbcg', 'lindgen',\n    'linfit', 'linkimage', 'list', 'll_arc_distance', 'lmfit',\n    'lmgr', 'lngamma', 'lnp_test', 'loadct', 'locale_get',\n    'logical_and', 'logical_or', 'logical_true', 'lon64arr', 'lonarr',\n    'long', 'long64', 'lsode', 'lu_complex', 'ludc',\n    'lumprove', 'lusol', 'm_correlate', 'machar', 'make_array',\n    'make_dll', 'make_rt', 'map', 'mapcontinents', 'mapgrid',\n    'map_2points', 'map_continents', 'map_grid', 'map_image', 'map_patch',\n    'map_proj_forward', 'map_proj_image', 'map_proj_info',\n    'map_proj_init', 'map_proj_inverse',\n    'map_set', 'matrix_multiply', 'matrix_power', 'max', 'md_test',\n    'mean', 'meanabsdev', 'mean_filter', 'median', 'memory',\n    'mesh_clip', 'mesh_decimate', 'mesh_issolid',\n    'mesh_merge', 'mesh_numtriangles',\n    'mesh_obj', 'mesh_smooth', 'mesh_surfacearea',\n    'mesh_validate', 'mesh_volume',\n    'message', 'min', 'min_curve_surf', 'mk_html_help', 'modifyct',\n    'moment', 'morph_close', 'morph_distance',\n    'morph_gradient', 'morph_hitormiss',\n    'morph_open', 'morph_thin', 'morph_tophat', 'multi', 'n_elements',\n    'n_params', 'n_tags', 'ncdf', 'newton', 'noise_hurl',\n    'noise_pick', 'noise_scatter', 'noise_slur', 'norm', 'obj_class',\n    'obj_destroy', 'obj_hasmethod', 'obj_isa', 'obj_new', 'obj_valid',\n    'objarr', 'on_error', 'on_ioerror', 'online_help', 'openr',\n    'openu', 'openw', 'oplot', 'oploterr', 'orderedhash',\n    'p_correlate', 'parse_url', 'particle_trace', 'path_cache', 'path_sep',\n    'pcomp', 'plot', 'plot3d', 'plot', 'plot_3dbox',\n    'plot_field', 'ploterr', 'plots', 'polar_contour', 'polar_surface',\n    'polyfill', 'polyshade', 'pnt_line', 'point_lun', 'polarplot',\n    'poly', 'poly_2d', 'poly_area', 'poly_fit', 'polyfillv',\n    'polygon', 'polyline', 'polywarp', 'popd', 'powell',\n    'pref_commit', 'pref_get', 'pref_set', 'prewitt', 'primes',\n    'print', 'printf', 'printd', 'pro', 'product',\n    'profile', 'profiler', 'profiles', 'project_vol', 'ps_show_fonts',\n    'psafm', 'pseudo', 'ptr_free', 'ptr_new', 'ptr_valid',\n    'ptrarr', 'pushd', 'qgrid3', 'qhull', 'qromb',\n    'qromo', 'qsimp', 'query_*', 'query_ascii', 'query_bmp',\n    'query_csv', 'query_dicom', 'query_gif', 'query_image', 'query_jpeg',\n    'query_jpeg2000', 'query_mrsid', 'query_pict', 'query_png', 'query_ppm',\n    'query_srf', 'query_tiff', 'query_video', 'query_wav', 'r_correlate',\n    'r_test', 'radon', 'randomn', 'randomu', 'ranks',\n    'rdpix', 'read', 'readf', 'read_ascii', 'read_binary',\n    'read_bmp', 'read_csv', 'read_dicom', 'read_gif', 'read_image',\n    'read_interfile', 'read_jpeg', 'read_jpeg2000', 'read_mrsid', 'read_pict',\n    'read_png', 'read_ppm', 'read_spr', 'read_srf', 'read_sylk',\n    'read_tiff', 'read_video', 'read_wav', 'read_wave', 'read_x11_bitmap',\n    'read_xwd', 'reads', 'readu', 'real_part', 'rebin',\n    'recall_commands', 'recon3', 'reduce_colors', 'reform', 'region_grow',\n    'register_cursor', 'regress', 'replicate',\n    'replicate_inplace', 'resolve_all',\n    'resolve_routine', 'restore', 'retall', 'return', 'reverse',\n    'rk4', 'roberts', 'rot', 'rotate', 'round',\n    'routine_filepath', 'routine_info', 'rs_test', 's_test', 'save',\n    'savgol', 'scale3', 'scale3d', 'scatterplot', 'scatterplot3d',\n    'scope_level', 'scope_traceback', 'scope_varfetch',\n    'scope_varname', 'search2d',\n    'search3d', 'sem_create', 'sem_delete', 'sem_lock', 'sem_release',\n    'set_plot', 'set_shading', 'setenv', 'sfit', 'shade_surf',\n    'shade_surf_irr', 'shade_volume', 'shift', 'shift_diff', 'shmdebug',\n    'shmmap', 'shmunmap', 'shmvar', 'show3', 'showfont',\n    'signum', 'simplex', 'sin', 'sindgen', 'sinh',\n    'size', 'skewness', 'skip_lun', 'slicer3', 'slide_image',\n    'smooth', 'sobel', 'socket', 'sort', 'spawn',\n    'sph_4pnt', 'sph_scat', 'spher_harm', 'spl_init', 'spl_interp',\n    'spline', 'spline_p', 'sprsab', 'sprsax', 'sprsin',\n    'sprstp', 'sqrt', 'standardize', 'stddev', 'stop',\n    'strarr', 'strcmp', 'strcompress', 'streamline', 'streamline',\n    'stregex', 'stretch', 'string', 'strjoin', 'strlen',\n    'strlowcase', 'strmatch', 'strmessage', 'strmid', 'strpos',\n    'strput', 'strsplit', 'strtrim', 'struct_assign', 'struct_hide',\n    'strupcase', 'surface', 'surface', 'surfr', 'svdc',\n    'svdfit', 'svsol', 'swap_endian', 'swap_endian_inplace', 'symbol',\n    'systime', 't_cvf', 't_pdf', 't3d', 'tag_names',\n    'tan', 'tanh', 'tek_color', 'temporary', 'terminal_size',\n    'tetra_clip', 'tetra_surface', 'tetra_volume', 'text', 'thin',\n    'thread', 'threed', 'tic', 'time_test2', 'timegen',\n    'timer', 'timestamp', 'timestamptovalues', 'tm_test', 'toc',\n    'total', 'trace', 'transpose', 'tri_surf', 'triangulate',\n    'trigrid', 'triql', 'trired', 'trisol', 'truncate_lun',\n    'ts_coef', 'ts_diff', 'ts_fcast', 'ts_smooth', 'tv',\n    'tvcrs', 'tvlct', 'tvrd', 'tvscl', 'typename',\n    'uindgen', 'uint', 'uintarr', 'ul64indgen', 'ulindgen',\n    'ulon64arr', 'ulonarr', 'ulong', 'ulong64', 'uniq',\n    'unsharp_mask', 'usersym', 'value_locate', 'variance', 'vector',\n    'vector_field', 'vel', 'velovect', 'vert_t3d', 'voigt',\n    'volume', 'voronoi', 'voxel_proj', 'wait', 'warp_tri',\n    'watershed', 'wdelete', 'wf_draw', 'where', 'widget_base',\n    'widget_button', 'widget_combobox', 'widget_control',\n    'widget_displaycontextmenu', 'widget_draw',\n    'widget_droplist', 'widget_event', 'widget_info',\n    'widget_label', 'widget_list',\n    'widget_propertysheet', 'widget_slider', 'widget_tab',\n    'widget_table', 'widget_text',\n    'widget_tree', 'widget_tree_move', 'widget_window',\n    'wiener_filter', 'window',\n    'window', 'write_bmp', 'write_csv', 'write_gif', 'write_image',\n    'write_jpeg', 'write_jpeg2000', 'write_nrif', 'write_pict', 'write_png',\n    'write_ppm', 'write_spr', 'write_srf', 'write_sylk', 'write_tiff',\n    'write_video', 'write_wav', 'write_wave', 'writeu', 'wset',\n    'wshow', 'wtn', 'wv_applet', 'wv_cwt', 'wv_cw_wavelet',\n    'wv_denoise', 'wv_dwt', 'wv_fn_coiflet',\n    'wv_fn_daubechies', 'wv_fn_gaussian',\n    'wv_fn_haar', 'wv_fn_morlet', 'wv_fn_paul',\n    'wv_fn_symlet', 'wv_import_data',\n    'wv_import_wavelet', 'wv_plot3d_wps', 'wv_plot_multires',\n    'wv_pwt', 'wv_tool_denoise',\n    'xbm_edit', 'xdisplayfile', 'xdxf', 'xfont', 'xinteranimate',\n    'xloadct', 'xmanager', 'xmng_tmpl', 'xmtool', 'xobjview',\n    'xobjview_rotate', 'xobjview_write_image',\n    'xpalette', 'xpcolor', 'xplot3d',\n    'xregistered', 'xroi', 'xsq_test', 'xsurface', 'xvaredit',\n    'xvolume', 'xvolume_rotate', 'xvolume_write_image',\n    'xyouts', 'zlib_compress', 'zlib_uncompress', 'zoom', 'zoom_24'\n  ];\n  var builtins = wordRegexp(builtinArray);\n\n  var keywordArray = [\n    'begin', 'end', 'endcase', 'endfor',\n    'endwhile', 'endif', 'endrep', 'endforeach',\n    'break', 'case', 'continue', 'for',\n    'foreach', 'goto', 'if', 'then', 'else',\n    'repeat', 'until', 'switch', 'while',\n    'do', 'pro', 'function'\n  ];\n  var keywords = wordRegexp(keywordArray);\n\n  CodeMirror.registerHelper(\"hintWords\", \"idl\", builtinArray.concat(keywordArray));\n\n  var identifiers = new RegExp('^[_a-z\\xa1-\\uffff][_a-z0-9\\xa1-\\uffff]*', 'i');\n\n  var singleOperators = /[+\\-*&=<>\\/@#~$]/;\n  var boolOperators = new RegExp('(and|or|eq|lt|le|gt|ge|ne|not)', 'i');\n\n  function tokenBase(stream) {\n    // whitespaces\n    if (stream.eatSpace()) return null;\n\n    // Handle one line Comments\n    if (stream.match(';')) {\n      stream.skipToEnd();\n      return 'comment';\n    }\n\n    // Handle Number Literals\n    if (stream.match(/^[0-9\\.+-]/, false)) {\n      if (stream.match(/^[+-]?0x[0-9a-fA-F]+/))\n        return 'number';\n      if (stream.match(/^[+-]?\\d*\\.\\d+([EeDd][+-]?\\d+)?/))\n        return 'number';\n      if (stream.match(/^[+-]?\\d+([EeDd][+-]?\\d+)?/))\n        return 'number';\n    }\n\n    // Handle Strings\n    if (stream.match(/^\"([^\"]|(\"\"))*\"/)) { return 'string'; }\n    if (stream.match(/^'([^']|(''))*'/)) { return 'string'; }\n\n    // Handle words\n    if (stream.match(keywords)) { return 'keyword'; }\n    if (stream.match(builtins)) { return 'builtin'; }\n    if (stream.match(identifiers)) { return 'variable'; }\n\n    if (stream.match(singleOperators) || stream.match(boolOperators)) {\n      return 'operator'; }\n\n    // Handle non-detected items\n    stream.next();\n    return null;\n  };\n\n  CodeMirror.defineMode('idl', function() {\n    return {\n      token: function(stream) {\n        return tokenBase(stream);\n      }\n    };\n  });\n\n  CodeMirror.defineMIME('text/x-idl', 'idl');\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/idl/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: IDL mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"idl.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">IDL</a>\n  </ul>\n</div>\n\n<article>\n<h2>IDL mode</h2>\n\n    <div><textarea id=\"code\" name=\"code\">\n;; Example IDL code\nFUNCTION mean_and_stddev,array\n  ;; This program reads in an array of numbers\n  ;; and returns a structure containing the\n  ;; average and standard deviation\n\n  ave = 0.0\n  count = 0.0\n\n  for i=0,N_ELEMENTS(array)-1 do begin\n      ave = ave + array[i]\n      count = count + 1\n  endfor\n  \n  ave = ave/count\n\n  std = stddev(array)  \n\n  return, {average:ave,std:std}\n\nEND\n\n    </textarea></div>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: {name: \"idl\",\n               version: 1,\n               singleLineStringErrors: false},\n        lineNumbers: true,\n        indentUnit: 4,\n        matchBrackets: true\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-idl</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Language Modes</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../doc/docs.css\">\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../index.html\">Home</a>\n    <li><a href=\"../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a class=active href=\"#\">Language modes</a>\n  </ul>\n</div>\n\n<article>\n\n  <h2>Language modes</h2>\n\n  <p>This is a list of every mode in the distribution. Each mode lives\nin a subdirectory of the <code>mode/</code> directory, and typically\ndefines a single JavaScript file that implements the mode. Loading\nsuch file will make the language available to CodeMirror, through\nthe <a href=\"../doc/manual.html#option_mode\"><code>mode</code></a>\noption.</p>\n\n  <div style=\"-webkit-columns: 100px 2; -moz-columns: 100px 2; columns: 100px 2;\">\n    <ul style=\"margin-top: 0\">\n      <li><a href=\"apl/index.html\">APL</a></li>\n      <li><a href=\"asn.1/index.html\">ASN.1</a></li>\n      <li><a href=\"asterisk/index.html\">Asterisk dialplan</a></li>\n      <li><a href=\"brainfuck/index.html\">Brainfuck</a></li>\n      <li><a href=\"clike/index.html\">C, C++, C#</a></li>\n      <li><a href=\"clike/index.html\">Ceylon</a></li>\n      <li><a href=\"clojure/index.html\">Clojure</a></li>\n      <li><a href=\"css/gss.html\">Closure Stylesheets (GSS)</a></li>\n      <li><a href=\"cmake/index.html\">CMake</a></li>\n      <li><a href=\"cobol/index.html\">COBOL</a></li>\n      <li><a href=\"coffeescript/index.html\">CoffeeScript</a></li>\n      <li><a href=\"commonlisp/index.html\">Common Lisp</a></li>\n      <li><a href=\"crystal/index.html\">Crystal</a></li>\n      <li><a href=\"css/index.html\">CSS</a></li>\n      <li><a href=\"cypher/index.html\">Cypher</a></li>\n      <li><a href=\"python/index.html\">Cython</a></li>\n      <li><a href=\"d/index.html\">D</a></li>\n      <li><a href=\"dart/index.html\">Dart</a></li>\n      <li><a href=\"django/index.html\">Django</a> (templating language)</li>\n      <li><a href=\"dockerfile/index.html\">Dockerfile</a></li>\n      <li><a href=\"diff/index.html\">diff</a></li>\n      <li><a href=\"dtd/index.html\">DTD</a></li>\n      <li><a href=\"dylan/index.html\">Dylan</a></li>\n      <li><a href=\"ebnf/index.html\">EBNF</a></li>\n      <li><a href=\"ecl/index.html\">ECL</a></li>\n      <li><a href=\"eiffel/index.html\">Eiffel</a></li>\n      <li><a href=\"https://github.com/optick/codemirror-mode-elixir\">Elixir</a></li>\n      <li><a href=\"elm/index.html\">Elm</a></li>\n      <li><a href=\"erlang/index.html\">Erlang</a></li>\n      <li><a href=\"factor/index.html\">Factor</a></li>\n      <li><a href=\"fcl/index.html\">FCL</a></li>\n      <li><a href=\"forth/index.html\">Forth</a></li>\n      <li><a href=\"fortran/index.html\">Fortran</a></li>\n      <li><a href=\"mllike/index.html\">F#</a></li>\n      <li><a href=\"gas/index.html\">Gas</a> (AT&amp;T-style assembly)</li>\n      <li><a href=\"gherkin/index.html\">Gherkin</a></li>\n      <li><a href=\"go/index.html\">Go</a></li>\n      <li><a href=\"groovy/index.html\">Groovy</a></li>\n      <li><a href=\"haml/index.html\">HAML</a></li>\n      <li><a href=\"handlebars/index.html\">Handlebars</a></li>\n      <li><a href=\"haskell/index.html\">Haskell</a> (<a href=\"haskell-literate/index.html\">Literate</a>)</li>\n      <li><a href=\"haxe/index.html\">Haxe</a></li>\n      <li><a href=\"htmlembedded/index.html\">HTML embedded</a> (JSP, ASP.NET)</li>\n      <li><a href=\"htmlmixed/index.html\">HTML mixed-mode</a></li>\n      <li><a href=\"http/index.html\">HTTP</a></li>\n      <li><a href=\"idl/index.html\">IDL</a></li>\n      <li><a href=\"clike/index.html\">Java</a></li>\n      <li><a href=\"javascript/index.html\">JavaScript</a> (<a href=\"jsx/index.html\">JSX</a>)</li>\n      <li><a href=\"jinja2/index.html\">Jinja2</a></li>\n      <li><a href=\"julia/index.html\">Julia</a></li>\n      <li><a href=\"clike/index.html\">Kotlin</a></li>\n      <li><a href=\"css/less.html\">LESS</a></li>\n      <li><a href=\"livescript/index.html\">LiveScript</a></li>\n      <li><a href=\"lua/index.html\">Lua</a></li>\n      <li><a href=\"markdown/index.html\">Markdown</a> (<a href=\"gfm/index.html\">GitHub-flavour</a>)</li>\n      <li><a href=\"mathematica/index.html\">Mathematica</a></li>\n      <li><a href=\"mbox/index.html\">mbox</a></li>\n      <li><a href=\"mirc/index.html\">mIRC</a></li>\n      <li><a href=\"modelica/index.html\">Modelica</a></li>\n      <li><a href=\"mscgen/index.html\">MscGen</a></li>\n      <li><a href=\"mumps/index.html\">MUMPS</a></li>\n      <li><a href=\"nginx/index.html\">Nginx</a></li>\n      <li><a href=\"nsis/index.html\">NSIS</a></li>\n      <li><a href=\"ntriples/index.html\">N-Triples/N-Quads</a></li>\n      <li><a href=\"clike/index.html\">Objective C</a></li>\n      <li><a href=\"mllike/index.html\">OCaml</a></li>\n      <li><a href=\"octave/index.html\">Octave</a> (MATLAB)</li>\n      <li><a href=\"oz/index.html\">Oz</a></li>\n      <li><a href=\"pascal/index.html\">Pascal</a></li>\n      <li><a href=\"pegjs/index.html\">PEG.js</a></li>\n      <li><a href=\"perl/index.html\">Perl</a></li>\n      <li><a href=\"asciiarmor/index.html\">PGP (ASCII armor)</a></li>\n      <li><a href=\"php/index.html\">PHP</a></li>\n      <li><a href=\"pig/index.html\">Pig Latin</a></li>\n      <li><a href=\"powershell/index.html\">PowerShell</a></li>\n      <li><a href=\"properties/index.html\">Properties files</a></li>\n      <li><a href=\"protobuf/index.html\">ProtoBuf</a></li>\n      <li><a href=\"pug/index.html\">Pug</a></li>\n      <li><a href=\"puppet/index.html\">Puppet</a></li>\n      <li><a href=\"python/index.html\">Python</a></li>\n      <li><a href=\"q/index.html\">Q</a></li>\n      <li><a href=\"r/index.html\">R</a></li>\n      <li><a href=\"rpm/index.html\">RPM</a></li>\n      <li><a href=\"rst/index.html\">reStructuredText</a></li>\n      <li><a href=\"ruby/index.html\">Ruby</a></li>\n      <li><a href=\"rust/index.html\">Rust</a></li>\n      <li><a href=\"sas/index.html\">SAS</a></li>\n      <li><a href=\"sass/index.html\">Sass</a></li>\n      <li><a href=\"spreadsheet/index.html\">Spreadsheet</a></li>\n      <li><a href=\"clike/scala.html\">Scala</a></li>\n      <li><a href=\"scheme/index.html\">Scheme</a></li>\n      <li><a href=\"css/scss.html\">SCSS</a></li>\n      <li><a href=\"shell/index.html\">Shell</a></li>\n      <li><a href=\"sieve/index.html\">Sieve</a></li>\n      <li><a href=\"slim/index.html\">Slim</a></li>\n      <li><a href=\"smalltalk/index.html\">Smalltalk</a></li>\n      <li><a href=\"smarty/index.html\">Smarty</a></li>\n      <li><a href=\"solr/index.html\">Solr</a></li>\n      <li><a href=\"soy/index.html\">Soy</a></li>\n      <li><a href=\"stylus/index.html\">Stylus</a></li>\n      <li><a href=\"sql/index.html\">SQL</a> (several dialects)</li>\n      <li><a href=\"sparql/index.html\">SPARQL</a></li>\n      <li><a href=\"clike/index.html\">Squirrel</a></li>\n      <li><a href=\"swift/index.html\">Swift</a></li>\n      <li><a href=\"stex/index.html\">sTeX, LaTeX</a></li>\n      <li><a href=\"tcl/index.html\">Tcl</a></li>\n      <li><a href=\"textile/index.html\">Textile</a></li>\n      <li><a href=\"tiddlywiki/index.html\">Tiddlywiki</a></li>\n      <li><a href=\"tiki/index.html\">Tiki wiki</a></li>\n      <li><a href=\"toml/index.html\">TOML</a></li>\n      <li><a href=\"tornado/index.html\">Tornado</a> (templating language)</li>\n      <li><a href=\"troff/index.html\">troff</a> (for manpages)</li>\n      <li><a href=\"ttcn/index.html\">TTCN</a></li>\n      <li><a href=\"ttcn-cfg/index.html\">TTCN Configuration</a></li>\n      <li><a href=\"turtle/index.html\">Turtle</a></li>\n      <li><a href=\"twig/index.html\">Twig</a></li>\n      <li><a href=\"vb/index.html\">VB.NET</a></li>\n      <li><a href=\"vbscript/index.html\">VBScript</a></li>\n      <li><a href=\"velocity/index.html\">Velocity</a></li>\n      <li><a href=\"verilog/index.html\">Verilog/SystemVerilog</a></li>\n      <li><a href=\"vhdl/index.html\">VHDL</a></li>\n      <li><a href=\"vue/index.html\">Vue.js app</a></li>\n      <li><a href=\"webidl/index.html\">Web IDL</a></li>\n      <li><a href=\"wast/index.html\">WebAssembly Text Format</a></li>\n      <li><a href=\"xml/index.html\">XML/HTML</a></li>\n      <li><a href=\"xquery/index.html\">XQuery</a></li>\n      <li><a href=\"yacas/index.html\">Yacas</a></li>\n      <li><a href=\"yaml/index.html\">YAML</a></li>\n      <li><a href=\"yaml-frontmatter/index.html\">YAML frontmatter</a></li>\n      <li><a href=\"z80/index.html\">Z80</a></li>\n    </ul>\n  </div>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/javascript/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: JavaScript mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"../../addon/comment/continuecomment.js\"></script>\n<script src=\"../../addon/comment/comment.js\"></script>\n<script src=\"javascript.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">JavaScript</a>\n  </ul>\n</div>\n\n<article>\n<h2>JavaScript mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n// Demo code (the actual new parser character stream implementation)\n\nfunction StringStream(string) {\n  this.pos = 0;\n  this.string = string;\n}\n\nStringStream.prototype = {\n  done: function() {return this.pos >= this.string.length;},\n  peek: function() {return this.string.charAt(this.pos);},\n  next: function() {\n    if (this.pos &lt; this.string.length)\n      return this.string.charAt(this.pos++);\n  },\n  eat: function(match) {\n    var ch = this.string.charAt(this.pos);\n    if (typeof match == \"string\") var ok = ch == match;\n    else var ok = ch &amp;&amp; match.test ? match.test(ch) : match(ch);\n    if (ok) {this.pos++; return ch;}\n  },\n  eatWhile: function(match) {\n    var start = this.pos;\n    while (this.eat(match));\n    if (this.pos > start) return this.string.slice(start, this.pos);\n  },\n  backUp: function(n) {this.pos -= n;},\n  column: function() {return this.pos;},\n  eatSpace: function() {\n    var start = this.pos;\n    while (/\\s/.test(this.string.charAt(this.pos))) this.pos++;\n    return this.pos - start;\n  },\n  match: function(pattern, consume, caseInsensitive) {\n    if (typeof pattern == \"string\") {\n      function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}\n      if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {\n        if (consume !== false) this.pos += str.length;\n        return true;\n      }\n    }\n    else {\n      var match = this.string.slice(this.pos).match(pattern);\n      if (match &amp;&amp; consume !== false) this.pos += match[0].length;\n      return match;\n    }\n  }\n};\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        continueComments: \"Enter\",\n        extraKeys: {\"Ctrl-Q\": \"toggleComment\"}\n      });\n    </script>\n\n    <p>\n      JavaScript mode supports several configuration options:\n      <ul>\n        <li><code>json</code> which will set the mode to expect JSON\n        data rather than a JavaScript program.</li>\n        <li><code>jsonld</code> which will set the mode to expect\n        <a href=\"http://json-ld.org\">JSON-LD</a> linked data rather\n        than a JavaScript program (<a href=\"json-ld.html\">demo</a>).</li>\n        <li><code>typescript</code> which will activate additional\n        syntax highlighting and some other things for TypeScript code\n        (<a href=\"typescript.html\">demo</a>).</li>\n        <li><code>trackScope</code> can be set to false to turn off\n        tracking of local variables. This will prevent locals from\n        getting the <code>\"variable-2\"</code> token type, and will\n        break completion of locals with javascript-hint.</li>\n        <li><code>statementIndent</code> which (given a number) will\n        determine the amount of indentation to use for statements\n        continued on a new line.</li>\n        <li><code>wordCharacters</code>, a regexp that indicates which\n        characters should be considered part of an identifier.\n        Defaults to <code>/[\\w$]/</code>, which does not handle\n        non-ASCII identifiers. Can be set to something more elaborate\n        to improve Unicode support.</li>\n      </ul>\n    </p>\n\n    <p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/javascript</code>, <code>application/x-javascript</code>, <code>text/ecmascript</code>, <code>application/ecmascript</code>, <code>application/json</code>, <code>application/x-json</code>, <code>application/manifest+json</code>, <code>application/ld+json</code>, <code>text/typescript</code>, <code>application/typescript</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/javascript/javascript.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"javascript\", function(config, parserConfig) {\n  var indentUnit = config.indentUnit;\n  var statementIndent = parserConfig.statementIndent;\n  var jsonldMode = parserConfig.jsonld;\n  var jsonMode = parserConfig.json || jsonldMode;\n  var trackScope = parserConfig.trackScope !== false\n  var isTS = parserConfig.typescript;\n  var wordRE = parserConfig.wordCharacters || /[\\w$\\xa1-\\uffff]/;\n\n  // Tokenizer\n\n  var keywords = function(){\n    function kw(type) {return {type: type, style: \"keyword\"};}\n    var A = kw(\"keyword a\"), B = kw(\"keyword b\"), C = kw(\"keyword c\"), D = kw(\"keyword d\");\n    var operator = kw(\"operator\"), atom = {type: \"atom\", style: \"atom\"};\n\n    return {\n      \"if\": kw(\"if\"), \"while\": A, \"with\": A, \"else\": B, \"do\": B, \"try\": B, \"finally\": B,\n      \"return\": D, \"break\": D, \"continue\": D, \"new\": kw(\"new\"), \"delete\": C, \"void\": C, \"throw\": C,\n      \"debugger\": kw(\"debugger\"), \"var\": kw(\"var\"), \"const\": kw(\"var\"), \"let\": kw(\"var\"),\n      \"function\": kw(\"function\"), \"catch\": kw(\"catch\"),\n      \"for\": kw(\"for\"), \"switch\": kw(\"switch\"), \"case\": kw(\"case\"), \"default\": kw(\"default\"),\n      \"in\": operator, \"typeof\": operator, \"instanceof\": operator,\n      \"true\": atom, \"false\": atom, \"null\": atom, \"undefined\": atom, \"NaN\": atom, \"Infinity\": atom,\n      \"this\": kw(\"this\"), \"class\": kw(\"class\"), \"super\": kw(\"atom\"),\n      \"yield\": C, \"export\": kw(\"export\"), \"import\": kw(\"import\"), \"extends\": C,\n      \"await\": C\n    };\n  }();\n\n  var isOperatorChar = /[+\\-*&%=<>!?|~^@]/;\n  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)\"/;\n\n  function readRegexp(stream) {\n    var escaped = false, next, inSet = false;\n    while ((next = stream.next()) != null) {\n      if (!escaped) {\n        if (next == \"/\" && !inSet) return;\n        if (next == \"[\") inSet = true;\n        else if (inSet && next == \"]\") inSet = false;\n      }\n      escaped = !escaped && next == \"\\\\\";\n    }\n  }\n\n  // Used as scratch variables to communicate multiple values without\n  // consing up tons of objects.\n  var type, content;\n  function ret(tp, style, cont) {\n    type = tp; content = cont;\n    return style;\n  }\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (ch == '\"' || ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    } else if (ch == \".\" && stream.match(/^\\d[\\d_]*(?:[eE][+\\-]?[\\d_]+)?/)) {\n      return ret(\"number\", \"number\");\n    } else if (ch == \".\" && stream.match(\"..\")) {\n      return ret(\"spread\", \"meta\");\n    } else if (/[\\[\\]{}\\(\\),;\\:\\.]/.test(ch)) {\n      return ret(ch);\n    } else if (ch == \"=\" && stream.eat(\">\")) {\n      return ret(\"=>\", \"operator\");\n    } else if (ch == \"0\" && stream.match(/^(?:x[\\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) {\n      return ret(\"number\", \"number\");\n    } else if (/\\d/.test(ch)) {\n      stream.match(/^[\\d_]*(?:n|(?:\\.[\\d_]*)?(?:[eE][+\\-]?[\\d_]+)?)?/);\n      return ret(\"number\", \"number\");\n    } else if (ch == \"/\") {\n      if (stream.eat(\"*\")) {\n        state.tokenize = tokenComment;\n        return tokenComment(stream, state);\n      } else if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return ret(\"comment\", \"comment\");\n      } else if (expressionAllowed(stream, state, 1)) {\n        readRegexp(stream);\n        stream.match(/^\\b(([gimyus])(?![gimyus]*\\2))+\\b/);\n        return ret(\"regexp\", \"string-2\");\n      } else {\n        stream.eat(\"=\");\n        return ret(\"operator\", \"operator\", stream.current());\n      }\n    } else if (ch == \"`\") {\n      state.tokenize = tokenQuasi;\n      return tokenQuasi(stream, state);\n    } else if (ch == \"#\" && stream.peek() == \"!\") {\n      stream.skipToEnd();\n      return ret(\"meta\", \"meta\");\n    } else if (ch == \"#\" && stream.eatWhile(wordRE)) {\n      return ret(\"variable\", \"property\")\n    } else if (ch == \"<\" && stream.match(\"!--\") ||\n               (ch == \"-\" && stream.match(\"->\") && !/\\S/.test(stream.string.slice(0, stream.start)))) {\n      stream.skipToEnd()\n      return ret(\"comment\", \"comment\")\n    } else if (isOperatorChar.test(ch)) {\n      if (ch != \">\" || !state.lexical || state.lexical.type != \">\") {\n        if (stream.eat(\"=\")) {\n          if (ch == \"!\" || ch == \"=\") stream.eat(\"=\")\n        } else if (/[<>*+\\-|&?]/.test(ch)) {\n          stream.eat(ch)\n          if (ch == \">\") stream.eat(ch)\n        }\n      }\n      if (ch == \"?\" && stream.eat(\".\")) return ret(\".\")\n      return ret(\"operator\", \"operator\", stream.current());\n    } else if (wordRE.test(ch)) {\n      stream.eatWhile(wordRE);\n      var word = stream.current()\n      if (state.lastType != \".\") {\n        if (keywords.propertyIsEnumerable(word)) {\n          var kw = keywords[word]\n          return ret(kw.type, kw.style, word)\n        }\n        if (word == \"async\" && stream.match(/^(\\s|\\/\\*([^*]|\\*(?!\\/))*?\\*\\/)*[\\[\\(\\w]/, false))\n          return ret(\"async\", \"keyword\", word)\n      }\n      return ret(\"variable\", \"variable\", word)\n    }\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next;\n      if (jsonldMode && stream.peek() == \"@\" && stream.match(isJsonldKeyword)){\n        state.tokenize = tokenBase;\n        return ret(\"jsonld-keyword\", \"meta\");\n      }\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) break;\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (!escaped) state.tokenize = tokenBase;\n      return ret(\"string\", \"string\");\n    };\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return ret(\"comment\", \"comment\");\n  }\n\n  function tokenQuasi(stream, state) {\n    var escaped = false, next;\n    while ((next = stream.next()) != null) {\n      if (!escaped && (next == \"`\" || next == \"$\" && stream.eat(\"{\"))) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      escaped = !escaped && next == \"\\\\\";\n    }\n    return ret(\"quasi\", \"string-2\", stream.current());\n  }\n\n  var brackets = \"([{}])\";\n  // This is a crude lookahead trick to try and notice that we're\n  // parsing the argument patterns for a fat-arrow function before we\n  // actually hit the arrow token. It only works if the arrow is on\n  // the same line as the arguments and there's no strange noise\n  // (comments) in between. Fallback is to only notice when we hit the\n  // arrow, and not declare the arguments as locals for the arrow\n  // body.\n  function findFatArrow(stream, state) {\n    if (state.fatArrowAt) state.fatArrowAt = null;\n    var arrow = stream.string.indexOf(\"=>\", stream.start);\n    if (arrow < 0) return;\n\n    if (isTS) { // Try to skip TypeScript return type declarations after the arguments\n      var m = /:\\s*(?:\\w+(?:<[^>]*>|\\[\\])?|\\{[^}]*\\})\\s*$/.exec(stream.string.slice(stream.start, arrow))\n      if (m) arrow = m.index\n    }\n\n    var depth = 0, sawSomething = false;\n    for (var pos = arrow - 1; pos >= 0; --pos) {\n      var ch = stream.string.charAt(pos);\n      var bracket = brackets.indexOf(ch);\n      if (bracket >= 0 && bracket < 3) {\n        if (!depth) { ++pos; break; }\n        if (--depth == 0) { if (ch == \"(\") sawSomething = true; break; }\n      } else if (bracket >= 3 && bracket < 6) {\n        ++depth;\n      } else if (wordRE.test(ch)) {\n        sawSomething = true;\n      } else if (/[\"'\\/`]/.test(ch)) {\n        for (;; --pos) {\n          if (pos == 0) return\n          var next = stream.string.charAt(pos - 1)\n          if (next == ch && stream.string.charAt(pos - 2) != \"\\\\\") { pos--; break }\n        }\n      } else if (sawSomething && !depth) {\n        ++pos;\n        break;\n      }\n    }\n    if (sawSomething && !depth) state.fatArrowAt = pos;\n  }\n\n  // Parser\n\n  var atomicTypes = {\"atom\": true, \"number\": true, \"variable\": true, \"string\": true,\n                     \"regexp\": true, \"this\": true, \"import\": true, \"jsonld-keyword\": true};\n\n  function JSLexical(indented, column, type, align, prev, info) {\n    this.indented = indented;\n    this.column = column;\n    this.type = type;\n    this.prev = prev;\n    this.info = info;\n    if (align != null) this.align = align;\n  }\n\n  function inScope(state, varname) {\n    if (!trackScope) return false\n    for (var v = state.localVars; v; v = v.next)\n      if (v.name == varname) return true;\n    for (var cx = state.context; cx; cx = cx.prev) {\n      for (var v = cx.vars; v; v = v.next)\n        if (v.name == varname) return true;\n    }\n  }\n\n  function parseJS(state, style, type, content, stream) {\n    var cc = state.cc;\n    // Communicate our context to the combinators.\n    // (Less wasteful than consing up a hundred closures on every call.)\n    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;\n\n    if (!state.lexical.hasOwnProperty(\"align\"))\n      state.lexical.align = true;\n\n    while(true) {\n      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;\n      if (combinator(type, content)) {\n        while(cc.length && cc[cc.length - 1].lex)\n          cc.pop()();\n        if (cx.marked) return cx.marked;\n        if (type == \"variable\" && inScope(state, content)) return \"variable-2\";\n        return style;\n      }\n    }\n  }\n\n  // Combinator utils\n\n  var cx = {state: null, column: null, marked: null, cc: null};\n  function pass() {\n    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);\n  }\n  function cont() {\n    pass.apply(null, arguments);\n    return true;\n  }\n  function inList(name, list) {\n    for (var v = list; v; v = v.next) if (v.name == name) return true\n    return false;\n  }\n  function register(varname) {\n    var state = cx.state;\n    cx.marked = \"def\";\n    if (!trackScope) return\n    if (state.context) {\n      if (state.lexical.info == \"var\" && state.context && state.context.block) {\n        // FIXME function decls are also not block scoped\n        var newContext = registerVarScoped(varname, state.context)\n        if (newContext != null) {\n          state.context = newContext\n          return\n        }\n      } else if (!inList(varname, state.localVars)) {\n        state.localVars = new Var(varname, state.localVars)\n        return\n      }\n    }\n    // Fall through means this is global\n    if (parserConfig.globalVars && !inList(varname, state.globalVars))\n      state.globalVars = new Var(varname, state.globalVars)\n  }\n  function registerVarScoped(varname, context) {\n    if (!context) {\n      return null\n    } else if (context.block) {\n      var inner = registerVarScoped(varname, context.prev)\n      if (!inner) return null\n      if (inner == context.prev) return context\n      return new Context(inner, context.vars, true)\n    } else if (inList(varname, context.vars)) {\n      return context\n    } else {\n      return new Context(context.prev, new Var(varname, context.vars), false)\n    }\n  }\n\n  function isModifier(name) {\n    return name == \"public\" || name == \"private\" || name == \"protected\" || name == \"abstract\" || name == \"readonly\"\n  }\n\n  // Combinators\n\n  function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }\n  function Var(name, next) { this.name = name; this.next = next }\n\n  var defaultVars = new Var(\"this\", new Var(\"arguments\", null))\n  function pushcontext() {\n    cx.state.context = new Context(cx.state.context, cx.state.localVars, false)\n    cx.state.localVars = defaultVars\n  }\n  function pushblockcontext() {\n    cx.state.context = new Context(cx.state.context, cx.state.localVars, true)\n    cx.state.localVars = null\n  }\n  pushcontext.lex = pushblockcontext.lex = true\n  function popcontext() {\n    cx.state.localVars = cx.state.context.vars\n    cx.state.context = cx.state.context.prev\n  }\n  popcontext.lex = true\n  function pushlex(type, info) {\n    var result = function() {\n      var state = cx.state, indent = state.indented;\n      if (state.lexical.type == \"stat\") indent = state.lexical.indented;\n      else for (var outer = state.lexical; outer && outer.type == \")\" && outer.align; outer = outer.prev)\n        indent = outer.indented;\n      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);\n    };\n    result.lex = true;\n    return result;\n  }\n  function poplex() {\n    var state = cx.state;\n    if (state.lexical.prev) {\n      if (state.lexical.type == \")\")\n        state.indented = state.lexical.indented;\n      state.lexical = state.lexical.prev;\n    }\n  }\n  poplex.lex = true;\n\n  function expect(wanted) {\n    function exp(type) {\n      if (type == wanted) return cont();\n      else if (wanted == \";\" || type == \"}\" || type == \")\" || type == \"]\") return pass();\n      else return cont(exp);\n    };\n    return exp;\n  }\n\n  function statement(type, value) {\n    if (type == \"var\") return cont(pushlex(\"vardef\", value), vardef, expect(\";\"), poplex);\n    if (type == \"keyword a\") return cont(pushlex(\"form\"), parenExpr, statement, poplex);\n    if (type == \"keyword b\") return cont(pushlex(\"form\"), statement, poplex);\n    if (type == \"keyword d\") return cx.stream.match(/^\\s*$/, false) ? cont() : cont(pushlex(\"stat\"), maybeexpression, expect(\";\"), poplex);\n    if (type == \"debugger\") return cont(expect(\";\"));\n    if (type == \"{\") return cont(pushlex(\"}\"), pushblockcontext, block, poplex, popcontext);\n    if (type == \";\") return cont();\n    if (type == \"if\") {\n      if (cx.state.lexical.info == \"else\" && cx.state.cc[cx.state.cc.length - 1] == poplex)\n        cx.state.cc.pop()();\n      return cont(pushlex(\"form\"), parenExpr, statement, poplex, maybeelse);\n    }\n    if (type == \"function\") return cont(functiondef);\n    if (type == \"for\") return cont(pushlex(\"form\"), pushblockcontext, forspec, statement, popcontext, poplex);\n    if (type == \"class\" || (isTS && value == \"interface\")) {\n      cx.marked = \"keyword\"\n      return cont(pushlex(\"form\", type == \"class\" ? type : value), className, poplex)\n    }\n    if (type == \"variable\") {\n      if (isTS && value == \"declare\") {\n        cx.marked = \"keyword\"\n        return cont(statement)\n      } else if (isTS && (value == \"module\" || value == \"enum\" || value == \"type\") && cx.stream.match(/^\\s*\\w/, false)) {\n        cx.marked = \"keyword\"\n        if (value == \"enum\") return cont(enumdef);\n        else if (value == \"type\") return cont(typename, expect(\"operator\"), typeexpr, expect(\";\"));\n        else return cont(pushlex(\"form\"), pattern, expect(\"{\"), pushlex(\"}\"), block, poplex, poplex)\n      } else if (isTS && value == \"namespace\") {\n        cx.marked = \"keyword\"\n        return cont(pushlex(\"form\"), expression, statement, poplex)\n      } else if (isTS && value == \"abstract\") {\n        cx.marked = \"keyword\"\n        return cont(statement)\n      } else {\n        return cont(pushlex(\"stat\"), maybelabel);\n      }\n    }\n    if (type == \"switch\") return cont(pushlex(\"form\"), parenExpr, expect(\"{\"), pushlex(\"}\", \"switch\"), pushblockcontext,\n                                      block, poplex, poplex, popcontext);\n    if (type == \"case\") return cont(expression, expect(\":\"));\n    if (type == \"default\") return cont(expect(\":\"));\n    if (type == \"catch\") return cont(pushlex(\"form\"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);\n    if (type == \"export\") return cont(pushlex(\"stat\"), afterExport, poplex);\n    if (type == \"import\") return cont(pushlex(\"stat\"), afterImport, poplex);\n    if (type == \"async\") return cont(statement)\n    if (value == \"@\") return cont(expression, statement)\n    return pass(pushlex(\"stat\"), expression, expect(\";\"), poplex);\n  }\n  function maybeCatchBinding(type) {\n    if (type == \"(\") return cont(funarg, expect(\")\"))\n  }\n  function expression(type, value) {\n    return expressionInner(type, value, false);\n  }\n  function expressionNoComma(type, value) {\n    return expressionInner(type, value, true);\n  }\n  function parenExpr(type) {\n    if (type != \"(\") return pass()\n    return cont(pushlex(\")\"), maybeexpression, expect(\")\"), poplex)\n  }\n  function expressionInner(type, value, noComma) {\n    if (cx.state.fatArrowAt == cx.stream.start) {\n      var body = noComma ? arrowBodyNoComma : arrowBody;\n      if (type == \"(\") return cont(pushcontext, pushlex(\")\"), commasep(funarg, \")\"), poplex, expect(\"=>\"), body, popcontext);\n      else if (type == \"variable\") return pass(pushcontext, pattern, expect(\"=>\"), body, popcontext);\n    }\n\n    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;\n    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);\n    if (type == \"function\") return cont(functiondef, maybeop);\n    if (type == \"class\" || (isTS && value == \"interface\")) { cx.marked = \"keyword\"; return cont(pushlex(\"form\"), classExpression, poplex); }\n    if (type == \"keyword c\" || type == \"async\") return cont(noComma ? expressionNoComma : expression);\n    if (type == \"(\") return cont(pushlex(\")\"), maybeexpression, expect(\")\"), poplex, maybeop);\n    if (type == \"operator\" || type == \"spread\") return cont(noComma ? expressionNoComma : expression);\n    if (type == \"[\") return cont(pushlex(\"]\"), arrayLiteral, poplex, maybeop);\n    if (type == \"{\") return contCommasep(objprop, \"}\", null, maybeop);\n    if (type == \"quasi\") return pass(quasi, maybeop);\n    if (type == \"new\") return cont(maybeTarget(noComma));\n    return cont();\n  }\n  function maybeexpression(type) {\n    if (type.match(/[;\\}\\)\\],]/)) return pass();\n    return pass(expression);\n  }\n\n  function maybeoperatorComma(type, value) {\n    if (type == \",\") return cont(maybeexpression);\n    return maybeoperatorNoComma(type, value, false);\n  }\n  function maybeoperatorNoComma(type, value, noComma) {\n    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;\n    var expr = noComma == false ? expression : expressionNoComma;\n    if (type == \"=>\") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);\n    if (type == \"operator\") {\n      if (/\\+\\+|--/.test(value) || isTS && value == \"!\") return cont(me);\n      if (isTS && value == \"<\" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\\s*\\(/, false))\n        return cont(pushlex(\">\"), commasep(typeexpr, \">\"), poplex, me);\n      if (value == \"?\") return cont(expression, expect(\":\"), expr);\n      return cont(expr);\n    }\n    if (type == \"quasi\") { return pass(quasi, me); }\n    if (type == \";\") return;\n    if (type == \"(\") return contCommasep(expressionNoComma, \")\", \"call\", me);\n    if (type == \".\") return cont(property, me);\n    if (type == \"[\") return cont(pushlex(\"]\"), maybeexpression, expect(\"]\"), poplex, me);\n    if (isTS && value == \"as\") { cx.marked = \"keyword\"; return cont(typeexpr, me) }\n    if (type == \"regexp\") {\n      cx.state.lastType = cx.marked = \"operator\"\n      cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)\n      return cont(expr)\n    }\n  }\n  function quasi(type, value) {\n    if (type != \"quasi\") return pass();\n    if (value.slice(value.length - 2) != \"${\") return cont(quasi);\n    return cont(maybeexpression, continueQuasi);\n  }\n  function continueQuasi(type) {\n    if (type == \"}\") {\n      cx.marked = \"string-2\";\n      cx.state.tokenize = tokenQuasi;\n      return cont(quasi);\n    }\n  }\n  function arrowBody(type) {\n    findFatArrow(cx.stream, cx.state);\n    return pass(type == \"{\" ? statement : expression);\n  }\n  function arrowBodyNoComma(type) {\n    findFatArrow(cx.stream, cx.state);\n    return pass(type == \"{\" ? statement : expressionNoComma);\n  }\n  function maybeTarget(noComma) {\n    return function(type) {\n      if (type == \".\") return cont(noComma ? targetNoComma : target);\n      else if (type == \"variable\" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)\n      else return pass(noComma ? expressionNoComma : expression);\n    };\n  }\n  function target(_, value) {\n    if (value == \"target\") { cx.marked = \"keyword\"; return cont(maybeoperatorComma); }\n  }\n  function targetNoComma(_, value) {\n    if (value == \"target\") { cx.marked = \"keyword\"; return cont(maybeoperatorNoComma); }\n  }\n  function maybelabel(type) {\n    if (type == \":\") return cont(poplex, statement);\n    return pass(maybeoperatorComma, expect(\";\"), poplex);\n  }\n  function property(type) {\n    if (type == \"variable\") {cx.marked = \"property\"; return cont();}\n  }\n  function objprop(type, value) {\n    if (type == \"async\") {\n      cx.marked = \"property\";\n      return cont(objprop);\n    } else if (type == \"variable\" || cx.style == \"keyword\") {\n      cx.marked = \"property\";\n      if (value == \"get\" || value == \"set\") return cont(getterSetter);\n      var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params\n      if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\\s*:\\s*/, false)))\n        cx.state.fatArrowAt = cx.stream.pos + m[0].length\n      return cont(afterprop);\n    } else if (type == \"number\" || type == \"string\") {\n      cx.marked = jsonldMode ? \"property\" : (cx.style + \" property\");\n      return cont(afterprop);\n    } else if (type == \"jsonld-keyword\") {\n      return cont(afterprop);\n    } else if (isTS && isModifier(value)) {\n      cx.marked = \"keyword\"\n      return cont(objprop)\n    } else if (type == \"[\") {\n      return cont(expression, maybetype, expect(\"]\"), afterprop);\n    } else if (type == \"spread\") {\n      return cont(expressionNoComma, afterprop);\n    } else if (value == \"*\") {\n      cx.marked = \"keyword\";\n      return cont(objprop);\n    } else if (type == \":\") {\n      return pass(afterprop)\n    }\n  }\n  function getterSetter(type) {\n    if (type != \"variable\") return pass(afterprop);\n    cx.marked = \"property\";\n    return cont(functiondef);\n  }\n  function afterprop(type) {\n    if (type == \":\") return cont(expressionNoComma);\n    if (type == \"(\") return pass(functiondef);\n  }\n  function commasep(what, end, sep) {\n    function proceed(type, value) {\n      if (sep ? sep.indexOf(type) > -1 : type == \",\") {\n        var lex = cx.state.lexical;\n        if (lex.info == \"call\") lex.pos = (lex.pos || 0) + 1;\n        return cont(function(type, value) {\n          if (type == end || value == end) return pass()\n          return pass(what)\n        }, proceed);\n      }\n      if (type == end || value == end) return cont();\n      if (sep && sep.indexOf(\";\") > -1) return pass(what)\n      return cont(expect(end));\n    }\n    return function(type, value) {\n      if (type == end || value == end) return cont();\n      return pass(what, proceed);\n    };\n  }\n  function contCommasep(what, end, info) {\n    for (var i = 3; i < arguments.length; i++)\n      cx.cc.push(arguments[i]);\n    return cont(pushlex(end, info), commasep(what, end), poplex);\n  }\n  function block(type) {\n    if (type == \"}\") return cont();\n    return pass(statement, block);\n  }\n  function maybetype(type, value) {\n    if (isTS) {\n      if (type == \":\") return cont(typeexpr);\n      if (value == \"?\") return cont(maybetype);\n    }\n  }\n  function maybetypeOrIn(type, value) {\n    if (isTS && (type == \":\" || value == \"in\")) return cont(typeexpr)\n  }\n  function mayberettype(type) {\n    if (isTS && type == \":\") {\n      if (cx.stream.match(/^\\s*\\w+\\s+is\\b/, false)) return cont(expression, isKW, typeexpr)\n      else return cont(typeexpr)\n    }\n  }\n  function isKW(_, value) {\n    if (value == \"is\") {\n      cx.marked = \"keyword\"\n      return cont()\n    }\n  }\n  function typeexpr(type, value) {\n    if (value == \"keyof\" || value == \"typeof\" || value == \"infer\" || value == \"readonly\") {\n      cx.marked = \"keyword\"\n      return cont(value == \"typeof\" ? expressionNoComma : typeexpr)\n    }\n    if (type == \"variable\" || value == \"void\") {\n      cx.marked = \"type\"\n      return cont(afterType)\n    }\n    if (value == \"|\" || value == \"&\") return cont(typeexpr)\n    if (type == \"string\" || type == \"number\" || type == \"atom\") return cont(afterType);\n    if (type == \"[\") return cont(pushlex(\"]\"), commasep(typeexpr, \"]\", \",\"), poplex, afterType)\n    if (type == \"{\") return cont(pushlex(\"}\"), typeprops, poplex, afterType)\n    if (type == \"(\") return cont(commasep(typearg, \")\"), maybeReturnType, afterType)\n    if (type == \"<\") return cont(commasep(typeexpr, \">\"), typeexpr)\n    if (type == \"quasi\") { return pass(quasiType, afterType); }\n  }\n  function maybeReturnType(type) {\n    if (type == \"=>\") return cont(typeexpr)\n  }\n  function typeprops(type) {\n    if (type.match(/[\\}\\)\\]]/)) return cont()\n    if (type == \",\" || type == \";\") return cont(typeprops)\n    return pass(typeprop, typeprops)\n  }\n  function typeprop(type, value) {\n    if (type == \"variable\" || cx.style == \"keyword\") {\n      cx.marked = \"property\"\n      return cont(typeprop)\n    } else if (value == \"?\" || type == \"number\" || type == \"string\") {\n      return cont(typeprop)\n    } else if (type == \":\") {\n      return cont(typeexpr)\n    } else if (type == \"[\") {\n      return cont(expect(\"variable\"), maybetypeOrIn, expect(\"]\"), typeprop)\n    } else if (type == \"(\") {\n      return pass(functiondecl, typeprop)\n    } else if (!type.match(/[;\\}\\)\\],]/)) {\n      return cont()\n    }\n  }\n  function quasiType(type, value) {\n    if (type != \"quasi\") return pass();\n    if (value.slice(value.length - 2) != \"${\") return cont(quasiType);\n    return cont(typeexpr, continueQuasiType);\n  }\n  function continueQuasiType(type) {\n    if (type == \"}\") {\n      cx.marked = \"string-2\";\n      cx.state.tokenize = tokenQuasi;\n      return cont(quasiType);\n    }\n  }\n  function typearg(type, value) {\n    if (type == \"variable\" && cx.stream.match(/^\\s*[?:]/, false) || value == \"?\") return cont(typearg)\n    if (type == \":\") return cont(typeexpr)\n    if (type == \"spread\") return cont(typearg)\n    return pass(typeexpr)\n  }\n  function afterType(type, value) {\n    if (value == \"<\") return cont(pushlex(\">\"), commasep(typeexpr, \">\"), poplex, afterType)\n    if (value == \"|\" || type == \".\" || value == \"&\") return cont(typeexpr)\n    if (type == \"[\") return cont(typeexpr, expect(\"]\"), afterType)\n    if (value == \"extends\" || value == \"implements\") { cx.marked = \"keyword\"; return cont(typeexpr) }\n    if (value == \"?\") return cont(typeexpr, expect(\":\"), typeexpr)\n  }\n  function maybeTypeArgs(_, value) {\n    if (value == \"<\") return cont(pushlex(\">\"), commasep(typeexpr, \">\"), poplex, afterType)\n  }\n  function typeparam() {\n    return pass(typeexpr, maybeTypeDefault)\n  }\n  function maybeTypeDefault(_, value) {\n    if (value == \"=\") return cont(typeexpr)\n  }\n  function vardef(_, value) {\n    if (value == \"enum\") {cx.marked = \"keyword\"; return cont(enumdef)}\n    return pass(pattern, maybetype, maybeAssign, vardefCont);\n  }\n  function pattern(type, value) {\n    if (isTS && isModifier(value)) { cx.marked = \"keyword\"; return cont(pattern) }\n    if (type == \"variable\") { register(value); return cont(); }\n    if (type == \"spread\") return cont(pattern);\n    if (type == \"[\") return contCommasep(eltpattern, \"]\");\n    if (type == \"{\") return contCommasep(proppattern, \"}\");\n  }\n  function proppattern(type, value) {\n    if (type == \"variable\" && !cx.stream.match(/^\\s*:/, false)) {\n      register(value);\n      return cont(maybeAssign);\n    }\n    if (type == \"variable\") cx.marked = \"property\";\n    if (type == \"spread\") return cont(pattern);\n    if (type == \"}\") return pass();\n    if (type == \"[\") return cont(expression, expect(']'), expect(':'), proppattern);\n    return cont(expect(\":\"), pattern, maybeAssign);\n  }\n  function eltpattern() {\n    return pass(pattern, maybeAssign)\n  }\n  function maybeAssign(_type, value) {\n    if (value == \"=\") return cont(expressionNoComma);\n  }\n  function vardefCont(type) {\n    if (type == \",\") return cont(vardef);\n  }\n  function maybeelse(type, value) {\n    if (type == \"keyword b\" && value == \"else\") return cont(pushlex(\"form\", \"else\"), statement, poplex);\n  }\n  function forspec(type, value) {\n    if (value == \"await\") return cont(forspec);\n    if (type == \"(\") return cont(pushlex(\")\"), forspec1, poplex);\n  }\n  function forspec1(type) {\n    if (type == \"var\") return cont(vardef, forspec2);\n    if (type == \"variable\") return cont(forspec2);\n    return pass(forspec2)\n  }\n  function forspec2(type, value) {\n    if (type == \")\") return cont()\n    if (type == \";\") return cont(forspec2)\n    if (value == \"in\" || value == \"of\") { cx.marked = \"keyword\"; return cont(expression, forspec2) }\n    return pass(expression, forspec2)\n  }\n  function functiondef(type, value) {\n    if (value == \"*\") {cx.marked = \"keyword\"; return cont(functiondef);}\n    if (type == \"variable\") {register(value); return cont(functiondef);}\n    if (type == \"(\") return cont(pushcontext, pushlex(\")\"), commasep(funarg, \")\"), poplex, mayberettype, statement, popcontext);\n    if (isTS && value == \"<\") return cont(pushlex(\">\"), commasep(typeparam, \">\"), poplex, functiondef)\n  }\n  function functiondecl(type, value) {\n    if (value == \"*\") {cx.marked = \"keyword\"; return cont(functiondecl);}\n    if (type == \"variable\") {register(value); return cont(functiondecl);}\n    if (type == \"(\") return cont(pushcontext, pushlex(\")\"), commasep(funarg, \")\"), poplex, mayberettype, popcontext);\n    if (isTS && value == \"<\") return cont(pushlex(\">\"), commasep(typeparam, \">\"), poplex, functiondecl)\n  }\n  function typename(type, value) {\n    if (type == \"keyword\" || type == \"variable\") {\n      cx.marked = \"type\"\n      return cont(typename)\n    } else if (value == \"<\") {\n      return cont(pushlex(\">\"), commasep(typeparam, \">\"), poplex)\n    }\n  }\n  function funarg(type, value) {\n    if (value == \"@\") cont(expression, funarg)\n    if (type == \"spread\") return cont(funarg);\n    if (isTS && isModifier(value)) { cx.marked = \"keyword\"; return cont(funarg); }\n    if (isTS && type == \"this\") return cont(maybetype, maybeAssign)\n    return pass(pattern, maybetype, maybeAssign);\n  }\n  function classExpression(type, value) {\n    // Class expressions may have an optional name.\n    if (type == \"variable\") return className(type, value);\n    return classNameAfter(type, value);\n  }\n  function className(type, value) {\n    if (type == \"variable\") {register(value); return cont(classNameAfter);}\n  }\n  function classNameAfter(type, value) {\n    if (value == \"<\") return cont(pushlex(\">\"), commasep(typeparam, \">\"), poplex, classNameAfter)\n    if (value == \"extends\" || value == \"implements\" || (isTS && type == \",\")) {\n      if (value == \"implements\") cx.marked = \"keyword\";\n      return cont(isTS ? typeexpr : expression, classNameAfter);\n    }\n    if (type == \"{\") return cont(pushlex(\"}\"), classBody, poplex);\n  }\n  function classBody(type, value) {\n    if (type == \"async\" ||\n        (type == \"variable\" &&\n         (value == \"static\" || value == \"get\" || value == \"set\" || (isTS && isModifier(value))) &&\n         cx.stream.match(/^\\s+#?[\\w$\\xa1-\\uffff]/, false))) {\n      cx.marked = \"keyword\";\n      return cont(classBody);\n    }\n    if (type == \"variable\" || cx.style == \"keyword\") {\n      cx.marked = \"property\";\n      return cont(classfield, classBody);\n    }\n    if (type == \"number\" || type == \"string\") return cont(classfield, classBody);\n    if (type == \"[\")\n      return cont(expression, maybetype, expect(\"]\"), classfield, classBody)\n    if (value == \"*\") {\n      cx.marked = \"keyword\";\n      return cont(classBody);\n    }\n    if (isTS && type == \"(\") return pass(functiondecl, classBody)\n    if (type == \";\" || type == \",\") return cont(classBody);\n    if (type == \"}\") return cont();\n    if (value == \"@\") return cont(expression, classBody)\n  }\n  function classfield(type, value) {\n    if (value == \"!\") return cont(classfield)\n    if (value == \"?\") return cont(classfield)\n    if (type == \":\") return cont(typeexpr, maybeAssign)\n    if (value == \"=\") return cont(expressionNoComma)\n    var context = cx.state.lexical.prev, isInterface = context && context.info == \"interface\"\n    return pass(isInterface ? functiondecl : functiondef)\n  }\n  function afterExport(type, value) {\n    if (value == \"*\") { cx.marked = \"keyword\"; return cont(maybeFrom, expect(\";\")); }\n    if (value == \"default\") { cx.marked = \"keyword\"; return cont(expression, expect(\";\")); }\n    if (type == \"{\") return cont(commasep(exportField, \"}\"), maybeFrom, expect(\";\"));\n    return pass(statement);\n  }\n  function exportField(type, value) {\n    if (value == \"as\") { cx.marked = \"keyword\"; return cont(expect(\"variable\")); }\n    if (type == \"variable\") return pass(expressionNoComma, exportField);\n  }\n  function afterImport(type) {\n    if (type == \"string\") return cont();\n    if (type == \"(\") return pass(expression);\n    if (type == \".\") return pass(maybeoperatorComma);\n    return pass(importSpec, maybeMoreImports, maybeFrom);\n  }\n  function importSpec(type, value) {\n    if (type == \"{\") return contCommasep(importSpec, \"}\");\n    if (type == \"variable\") register(value);\n    if (value == \"*\") cx.marked = \"keyword\";\n    return cont(maybeAs);\n  }\n  function maybeMoreImports(type) {\n    if (type == \",\") return cont(importSpec, maybeMoreImports)\n  }\n  function maybeAs(_type, value) {\n    if (value == \"as\") { cx.marked = \"keyword\"; return cont(importSpec); }\n  }\n  function maybeFrom(_type, value) {\n    if (value == \"from\") { cx.marked = \"keyword\"; return cont(expression); }\n  }\n  function arrayLiteral(type) {\n    if (type == \"]\") return cont();\n    return pass(commasep(expressionNoComma, \"]\"));\n  }\n  function enumdef() {\n    return pass(pushlex(\"form\"), pattern, expect(\"{\"), pushlex(\"}\"), commasep(enummember, \"}\"), poplex, poplex)\n  }\n  function enummember() {\n    return pass(pattern, maybeAssign);\n  }\n\n  function isContinuedStatement(state, textAfter) {\n    return state.lastType == \"operator\" || state.lastType == \",\" ||\n      isOperatorChar.test(textAfter.charAt(0)) ||\n      /[,.]/.test(textAfter.charAt(0));\n  }\n\n  function expressionAllowed(stream, state, backUp) {\n    return state.tokenize == tokenBase &&\n      /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\\[{}\\(,;:]|=>)$/.test(state.lastType) ||\n      (state.lastType == \"quasi\" && /\\{\\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))\n  }\n\n  // Interface\n\n  return {\n    startState: function(basecolumn) {\n      var state = {\n        tokenize: tokenBase,\n        lastType: \"sof\",\n        cc: [],\n        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, \"block\", false),\n        localVars: parserConfig.localVars,\n        context: parserConfig.localVars && new Context(null, null, false),\n        indented: basecolumn || 0\n      };\n      if (parserConfig.globalVars && typeof parserConfig.globalVars == \"object\")\n        state.globalVars = parserConfig.globalVars;\n      return state;\n    },\n\n    token: function(stream, state) {\n      if (stream.sol()) {\n        if (!state.lexical.hasOwnProperty(\"align\"))\n          state.lexical.align = false;\n        state.indented = stream.indentation();\n        findFatArrow(stream, state);\n      }\n      if (state.tokenize != tokenComment && stream.eatSpace()) return null;\n      var style = state.tokenize(stream, state);\n      if (type == \"comment\") return style;\n      state.lastType = type == \"operator\" && (content == \"++\" || content == \"--\") ? \"incdec\" : type;\n      return parseJS(state, style, type, content, stream);\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass;\n      if (state.tokenize != tokenBase) return 0;\n      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top\n      // Kludge to prevent 'maybelse' from blocking lexical scope pops\n      if (!/^\\s*else\\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {\n        var c = state.cc[i];\n        if (c == poplex) lexical = lexical.prev;\n        else if (c != maybeelse && c != popcontext) break;\n      }\n      while ((lexical.type == \"stat\" || lexical.type == \"form\") &&\n             (firstChar == \"}\" || ((top = state.cc[state.cc.length - 1]) &&\n                                   (top == maybeoperatorComma || top == maybeoperatorNoComma) &&\n                                   !/^[,\\.=+\\-*:?[\\(]/.test(textAfter))))\n        lexical = lexical.prev;\n      if (statementIndent && lexical.type == \")\" && lexical.prev.type == \"stat\")\n        lexical = lexical.prev;\n      var type = lexical.type, closing = firstChar == type;\n\n      if (type == \"vardef\") return lexical.indented + (state.lastType == \"operator\" || state.lastType == \",\" ? lexical.info.length + 1 : 0);\n      else if (type == \"form\" && firstChar == \"{\") return lexical.indented;\n      else if (type == \"form\") return lexical.indented + indentUnit;\n      else if (type == \"stat\")\n        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);\n      else if (lexical.info == \"switch\" && !closing && parserConfig.doubleIndentSwitch != false)\n        return lexical.indented + (/^(?:case|default)\\b/.test(textAfter) ? indentUnit : 2 * indentUnit);\n      else if (lexical.align) return lexical.column + (closing ? 0 : 1);\n      else return lexical.indented + (closing ? 0 : indentUnit);\n    },\n\n    electricInput: /^\\s*(?:case .*?:|default:|\\{|\\})$/,\n    blockCommentStart: jsonMode ? null : \"/*\",\n    blockCommentEnd: jsonMode ? null : \"*/\",\n    blockCommentContinue: jsonMode ? null : \" * \",\n    lineComment: jsonMode ? null : \"//\",\n    fold: \"brace\",\n    closeBrackets: \"()[]{}''\\\"\\\"``\",\n\n    helperType: jsonMode ? \"json\" : \"javascript\",\n    jsonldMode: jsonldMode,\n    jsonMode: jsonMode,\n\n    expressionAllowed: expressionAllowed,\n\n    skipExpression: function(state) {\n      parseJS(state, \"atom\", \"atom\", \"true\", new CodeMirror.StringStream(\"\", 2, null))\n    }\n  };\n});\n\nCodeMirror.registerHelper(\"wordChars\", \"javascript\", /[\\w$]/);\n\nCodeMirror.defineMIME(\"text/javascript\", \"javascript\");\nCodeMirror.defineMIME(\"text/ecmascript\", \"javascript\");\nCodeMirror.defineMIME(\"application/javascript\", \"javascript\");\nCodeMirror.defineMIME(\"application/x-javascript\", \"javascript\");\nCodeMirror.defineMIME(\"application/ecmascript\", \"javascript\");\nCodeMirror.defineMIME(\"application/json\", { name: \"javascript\", json: true });\nCodeMirror.defineMIME(\"application/x-json\", { name: \"javascript\", json: true });\nCodeMirror.defineMIME(\"application/manifest+json\", { name: \"javascript\", json: true })\nCodeMirror.defineMIME(\"application/ld+json\", { name: \"javascript\", jsonld: true });\nCodeMirror.defineMIME(\"text/typescript\", { name: \"javascript\", typescript: true });\nCodeMirror.defineMIME(\"application/typescript\", { name: \"javascript\", typescript: true });\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/javascript/json-ld.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: JSON-LD mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"../../addon/comment/continuecomment.js\"></script>\n<script src=\"../../addon/comment/comment.js\"></script>\n<script src=\"javascript.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=\"nav\">\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\"/></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">JSON-LD</a>\n  </ul>\n</div>\n\n<article>\n<h2>JSON-LD mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n{\n  \"@context\": {\n    \"name\": \"http://schema.org/name\",\n    \"description\": \"http://schema.org/description\",\n    \"image\": {\n      \"@id\": \"http://schema.org/image\",\n      \"@type\": \"@id\"\n    },\n    \"geo\": \"http://schema.org/geo\",\n    \"latitude\": {\n      \"@id\": \"http://schema.org/latitude\",\n      \"@type\": \"xsd:float\"\n    },\n    \"longitude\": {\n      \"@id\": \"http://schema.org/longitude\",\n      \"@type\": \"xsd:float\"\n    },\n    \"xsd\": \"http://www.w3.org/2001/XMLSchema#\"\n  },\n  \"name\": \"The Empire State Building\",\n  \"description\": \"The Empire State Building is a 102-story landmark in New York City.\",\n  \"image\": \"http://www.civil.usherbrooke.ca/cours/gci215a/empire-state-building.jpg\",\n  \"geo\": {\n    \"latitude\": \"40.75\",\n    \"longitude\": \"73.98\"\n  }\n}\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        matchBrackets: true,\n        autoCloseBrackets: true,\n        mode: \"application/ld+json\",\n        lineWrapping: true\n      });\n    </script>\n    \n    <p>This is a specialization of the <a href=\"index.html\">JavaScript mode</a>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/javascript/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"javascript\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"locals\",\n     \"[keyword function] [def foo]([def a], [def b]) { [keyword var] [def c] [operator =] [number 10]; [keyword return] [variable-2 a] [operator +] [variable-2 c] [operator +] [variable d]; }\");\n\n  MT(\"comma-and-binop\",\n     \"[keyword function](){ [keyword var] [def x] [operator =] [number 1] [operator +] [number 2], [def y]; }\");\n\n  MT(\"destructuring\",\n     \"([keyword function]([def a], [[[def b], [def c] ]]) {\",\n     \"  [keyword let] {[def d], [property foo]: [def c][operator =][number 10], [def x]} [operator =] [variable foo]([variable-2 a]);\",\n     \"  [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];\",\n     \"})();\");\n\n  MT(\"destructure_trailing_comma\",\n    \"[keyword let] {[def a], [def b],} [operator =] [variable foo];\",\n    \"[keyword let] [def c];\"); // Parser still in good state?\n\n  MT(\"class_body\",\n     \"[keyword class] [def Foo] {\",\n     \"  [property constructor]() {}\",\n     \"  [property sayName]() {\",\n     \"    [keyword return] [string-2 `foo${][variable foo][string-2 }oo`];\",\n     \"  }\",\n     \"}\");\n\n  MT(\"class\",\n     \"[keyword class] [def Point] [keyword extends] [variable SuperThing] {\",\n     \"  [keyword get] [property prop]() { [keyword return] [number 24]; }\",\n     \"  [property constructor]([def x], [def y]) {\",\n     \"    [keyword super]([string 'something']);\",\n     \"    [keyword this].[property x] [operator =] [variable-2 x];\",\n     \"  }\",\n     \"}\");\n\n  MT(\"anonymous_class_expression\",\n     \"[keyword const] [def Adder] [operator =] [keyword class] [keyword extends] [variable Arithmetic] {\",\n     \"  [property add]([def a], [def b]) {}\",\n     \"};\");\n\n  MT(\"named_class_expression\",\n     \"[keyword const] [def Subber] [operator =] [keyword class] [def Subtract] {\",\n     \"  [property sub]([def a], [def b]) {}\",\n     \"};\");\n\n  MT(\"class_async_method\",\n     \"[keyword class] [def Foo] {\",\n     \"  [property sayName1]() {}\",\n     \"  [keyword async] [property sayName2]() {}\",\n     \"}\");\n\n  MT(\"import\",\n     \"[keyword function] [def foo]() {\",\n     \"  [keyword import] [def $] [keyword from] [string 'jquery'];\",\n     \"  [keyword import] { [def encrypt], [def decrypt] } [keyword from] [string 'crypto'];\",\n     \"}\");\n\n  MT(\"import_trailing_comma\",\n     \"[keyword import] {[def foo], [def bar],} [keyword from] [string 'baz']\")\n\n  MT(\"import_dynamic\",\n     \"[keyword import]([string 'baz']).[property then]\")\n\n  MT(\"import_dynamic\",\n     \"[keyword const] [def t] [operator =] [keyword import]([string 'baz']).[property then]\")\n\n  MT(\"const\",\n     \"[keyword function] [def f]() {\",\n     \"  [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];\",\n     \"}\");\n\n  MT(\"for/of\",\n     \"[keyword for]([keyword let] [def of] [keyword of] [variable something]) {}\");\n\n  MT(\"for await\",\n     \"[keyword for] [keyword await]([keyword let] [def of] [keyword of] [variable something]) {}\");\n\n  MT(\"generator\",\n     \"[keyword function*] [def repeat]([def n]) {\",\n     \"  [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])\",\n     \"    [keyword yield] [variable-2 i];\",\n     \"}\");\n\n  MT(\"let_scoping\",\n     \"[keyword function] [def scoped]([def n]) {\",\n     \"  { [keyword var] [def i]; } [variable-2 i];\",\n     \"  { [keyword let] [def j]; [variable-2 j]; } [variable j];\",\n     \"  [keyword if] ([atom true]) { [keyword const] [def k]; [variable-2 k]; } [variable k];\",\n     \"}\");\n\n  MT(\"switch_scoping\",\n     \"[keyword switch] ([variable x]) {\",\n     \"  [keyword default]:\",\n     \"    [keyword let] [def j];\",\n     \"    [keyword return] [variable-2 j]\",\n     \"}\",\n     \"[variable j];\")\n\n  MT(\"leaving_scope\",\n     \"[keyword function] [def a]() {\",\n     \"  {\",\n     \"    [keyword const] [def x] [operator =] [number 1]\",\n     \"    [keyword if] ([atom true]) {\",\n     \"      [keyword let] [def y] [operator =] [number 2]\",\n     \"      [keyword var] [def z] [operator =] [number 3]\",\n     \"      [variable console].[property log]([variable-2 x], [variable-2 y], [variable-2 z])\",\n     \"    }\",\n     \"    [variable console].[property log]([variable-2 x], [variable y], [variable-2 z])\",\n     \"  }\",\n     \"  [variable console].[property log]([variable x], [variable y], [variable-2 z])\",\n     \"}\")\n\n  MT(\"quotedStringAddition\",\n     \"[keyword let] [def f] [operator =] [variable a] [operator +] [string 'fatarrow'] [operator +] [variable c];\");\n\n  MT(\"quotedFatArrow\",\n     \"[keyword let] [def f] [operator =] [variable a] [operator +] [string '=>'] [operator +] [variable c];\");\n\n  MT(\"fatArrow\",\n     \"[variable array].[property filter]([def a] [operator =>] [variable-2 a] [operator +] [number 1]);\",\n     \"[variable a];\", // No longer in scope\n     \"[keyword let] [def f] [operator =] ([[ [def a], [def b] ]], [def c]) [operator =>] [variable-2 a] [operator +] [variable-2 c];\",\n     \"[variable c];\");\n\n  MT(\"fatArrow_stringDefault\",\n     \"([def a], [def b] [operator =] [string 'x\\\\'y']) [operator =>] [variable-2 a] [operator +] [variable-2 b]\")\n\n  MT(\"spread\",\n     \"[keyword function] [def f]([def a], [meta ...][def b]) {\",\n     \"  [variable something]([variable-2 a], [meta ...][variable-2 b]);\",\n     \"}\");\n\n  MT(\"quasi\",\n     \"[variable re][string-2 `fofdlakj${][variable x] [operator +] ([variable re][string-2 `foo`]) [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]\");\n\n  MT(\"quasi_no_function\",\n     \"[variable x] [operator =] [string-2 `fofdlakj${][variable x] [operator +] [string-2 `foo`] [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]\");\n\n  MT(\"indent_statement\",\n     \"[keyword var] [def x] [operator =] [number 10]\",\n     \"[variable x] [operator +=] [variable y] [operator +]\",\n     \"  [atom Infinity]\",\n     \"[keyword debugger];\");\n\n  MT(\"indent_if\",\n     \"[keyword if] ([number 1])\",\n     \"  [keyword break];\",\n     \"[keyword else] [keyword if] ([number 2])\",\n     \"  [keyword continue];\",\n     \"[keyword else]\",\n     \"  [number 10];\",\n     \"[keyword if] ([number 1]) {\",\n     \"  [keyword break];\",\n     \"} [keyword else] [keyword if] ([number 2]) {\",\n     \"  [keyword continue];\",\n     \"} [keyword else] {\",\n     \"  [number 10];\",\n     \"}\");\n\n  MT(\"indent_for\",\n     \"[keyword for] ([keyword var] [def i] [operator =] [number 0];\",\n     \"     [variable-2 i] [operator <] [number 100];\",\n     \"     [variable-2 i][operator ++])\",\n     \"  [variable doSomething]([variable-2 i]);\",\n     \"[keyword debugger];\");\n\n  MT(\"indent_c_style\",\n     \"[keyword function] [def foo]()\",\n     \"{\",\n     \"  [keyword debugger];\",\n     \"}\");\n\n  MT(\"indent_else\",\n     \"[keyword for] (;;)\",\n     \"  [keyword if] ([variable foo])\",\n     \"    [keyword if] ([variable bar])\",\n     \"      [number 1];\",\n     \"    [keyword else]\",\n     \"      [number 2];\",\n     \"  [keyword else]\",\n     \"    [number 3];\");\n\n  MT(\"indent_funarg\",\n     \"[variable foo]([number 10000],\",\n     \"    [keyword function]([def a]) {\",\n     \"  [keyword debugger];\",\n     \"};\");\n\n  MT(\"indent_below_if\",\n     \"[keyword for] (;;)\",\n     \"  [keyword if] ([variable foo])\",\n     \"    [number 1];\",\n     \"[number 2];\");\n\n  MT(\"indent_semicolonless_if\",\n     \"[keyword function] [def foo]() {\",\n     \"  [keyword if] ([variable x])\",\n     \"    [variable foo]()\",\n     \"}\")\n\n  MT(\"indent_semicolonless_if_with_statement\",\n     \"[keyword function] [def foo]() {\",\n     \"  [keyword if] ([variable x])\",\n     \"    [variable foo]()\",\n     \"  [variable bar]()\",\n     \"}\")\n\n  MT(\"multilinestring\",\n     \"[keyword var] [def x] [operator =] [string 'foo\\\\]\",\n     \"[string bar'];\");\n\n  MT(\"scary_regexp\",\n     \"[string-2 /foo[[/]]bar/];\");\n\n  MT(\"indent_strange_array\",\n     \"[keyword var] [def x] [operator =] [[\",\n     \"  [number 1],,\",\n     \"  [number 2],\",\n     \"]];\",\n     \"[number 10];\");\n\n  MT(\"param_default\",\n     \"[keyword function] [def foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {\",\n     \"  [keyword return] [variable-2 x];\",\n     \"}\");\n\n  MT(\n    \"param_destructuring\",\n    \"[keyword function] [def foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {\",\n    \"  [keyword return] [variable-2 x];\",\n    \"}\");\n\n  MT(\"new_target\",\n     \"[keyword function] [def F]([def target]) {\",\n     \"  [keyword if] ([variable-2 target] [operator &&] [keyword new].[keyword target].[property name]) {\",\n     \"    [keyword return] [keyword new]\",\n     \"      .[keyword target];\",\n     \"  }\",\n     \"}\");\n\n  MT(\"async\",\n     \"[keyword async] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }\");\n\n  MT(\"async_assignment\",\n     \"[keyword const] [def foo] [operator =] [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; };\");\n\n  MT(\"async_object\",\n     \"[keyword let] [def obj] [operator =] { [property async]: [atom false] };\");\n\n  // async be highlighted as keyword and foo as def, but it requires potentially expensive look-ahead. See #4173\n  MT(\"async_object_function\",\n     \"[keyword let] [def obj] [operator =] { [property async] [property foo]([def args]) { [keyword return] [atom true]; } };\");\n\n  MT(\"async_object_properties\",\n     \"[keyword let] [def obj] [operator =] {\",\n     \"  [property prop1]: [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; },\",\n     \"  [property prop2]: [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; },\",\n     \"  [property prop3]: [keyword async] [keyword function] [def prop3]([def args]) { [keyword return] [atom true]; },\",\n     \"};\");\n\n  MT(\"async_arrow\",\n     \"[keyword const] [def foo] [operator =] [keyword async] ([def args]) [operator =>] { [keyword return] [atom true]; };\");\n\n  MT(\"async_jquery\",\n     \"[variable $].[property ajax]({\",\n     \"  [property url]: [variable url],\",\n     \"  [property async]: [atom true],\",\n     \"  [property method]: [string 'GET']\",\n     \"});\");\n\n  MT(\"async_variable\",\n     \"[keyword const] [def async] [operator =] {[property a]: [number 1]};\",\n     \"[keyword const] [def foo] [operator =] [string-2 `bar ${][variable async].[property a][string-2 }`];\")\n\n  MT(\"bigint\", \"[number 1n] [operator +] [number 0x1afn] [operator +] [number 0o064n] [operator +] [number 0b100n];\")\n\n  MT(\"async_comment\",\n     \"[keyword async] [comment /**/] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }\");\n\n  MT(\"indent_switch\",\n     \"[keyword switch] ([variable x]) {\",\n     \"  [keyword default]:\",\n     \"    [keyword return] [number 2]\",\n     \"}\")\n\n  MT(\"regexp_corner_case\",\n     \"[operator +]{} [operator /] [atom undefined];\",\n     \"[[[meta ...][string-2 /\\\\//] ]];\",\n     \"[keyword void] [string-2 /\\\\//];\",\n     \"[keyword do] [string-2 /\\\\//]; [keyword while] ([number 0]);\",\n     \"[keyword if] ([number 0]) {} [keyword else] [string-2 /\\\\//];\",\n     \"[string-2 `${][variable async][operator ++][string-2 }//`];\",\n     \"[string-2 `${]{} [operator /] [string-2 /\\\\//}`];\")\n\n  MT(\"return_eol\",\n     \"[keyword return]\",\n     \"{} [string-2 /5/]\")\n\n  MT(\"numeric separator\",\n     \"[number 123_456];\",\n     \"[number 0xdead_c0de];\",\n     \"[number 0o123_456];\",\n     \"[number 0b1101_1101];\",\n     \"[number .123_456e0_1];\",\n     \"[number 1E+123_456];\",\n     \"[number 12_34_56n];\")\n\n  MT(\"underscore property\",\n     \"[variable something].[property _property];\",\n     \"[variable something].[property _123];\",\n     \"[variable something].[property _for];\",\n     \"[variable _for];\",\n     \"[variable _123];\")\n\n  MT(\"private properties\",\n     \"[keyword class] [def C] {\",\n     \"  [property #x] [operator =] [number 2];\",\n     \"  [property #read]() {\",\n     \"    [keyword return] [keyword this].[property #x]\",\n     \"  }\",\n     \"}\")\n\n  var ts_mode = CodeMirror.getMode({indentUnit: 2}, \"application/typescript\")\n  function TS(name) {\n    test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1))\n  }\n\n  TS(\"typescript_extend_type\",\n     \"[keyword class] [def Foo] [keyword extends] [type Some][operator <][type Type][operator >] {}\")\n\n  TS(\"typescript_arrow_type\",\n     \"[keyword let] [def x]: ([variable arg]: [type Type]) [operator =>] [type ReturnType]\")\n\n  TS(\"typescript_class\",\n     \"[keyword class] [def Foo] {\",\n     \"  [keyword public] [keyword static] [property main]() {}\",\n     \"  [keyword private] [property _foo]: [type string];\",\n     \"}\")\n\n  TS(\"typescript_literal_types\",\n     \"[keyword import] [keyword *] [keyword as] [def Sequelize] [keyword from] [string 'sequelize'];\",\n     \"[keyword interface] [def MyAttributes] {\",\n     \"  [property truthy]: [string 'true'] [operator |] [number 1] [operator |] [atom true];\",\n     \"  [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];\",\n     \"}\",\n     \"[keyword interface] [def MyInstance] [keyword extends] [type Sequelize].[type Instance] [operator <] [type MyAttributes] [operator >] {\",\n     \"  [property rawAttributes]: [type MyAttributes];\",\n     \"  [property truthy]: [string 'true'] [operator |] [number 1] [operator |] [atom true];\",\n     \"  [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];\",\n     \"}\")\n\n  TS(\"typescript_extend_operators\",\n     \"[keyword export] [keyword interface] [def UserModel] [keyword extends]\",\n     \"  [type Sequelize].[type Model] [operator <] [type UserInstance], [type UserAttributes] [operator >] {\",\n     \"    [property findById]: (\",\n     \"    [variable userId]: [type number]\",\n     \"    ) [operator =>] [type Promise] [operator <] [type Array] [operator <] { [property id], [property name] } [operator >>];\",\n     \"    [property updateById]: (\",\n     \"    [variable userId]: [type number],\",\n     \"    [variable isActive]: [type boolean]\",\n     \"    ) [operator =>] [type Promise] [operator <] [type AccountHolderNotificationPreferenceInstance] [operator >];\",\n     \"  }\")\n\n  TS(\"typescript_interface_with_const\",\n     \"[keyword const] [def hello]: {\",\n     \"  [property prop1][operator ?]: [type string];\",\n     \"  [property prop2][operator ?]: [type string];\",\n     \"} [operator =] {};\")\n\n  TS(\"typescript_double_extend\",\n     \"[keyword export] [keyword interface] [def UserAttributes] {\",\n     \"  [property id][operator ?]: [type number];\",\n     \"  [property createdAt][operator ?]: [type Date];\",\n     \"}\",\n     \"[keyword export] [keyword interface] [def UserInstance] [keyword extends] [type Sequelize].[type Instance][operator <][type UserAttributes][operator >], [type UserAttributes] {\",\n     \"  [property id]: [type number];\",\n     \"  [property createdAt]: [type Date];\",\n     \"}\");\n\n  TS(\"typescript_index_signature\",\n     \"[keyword interface] [def A] {\",\n     \"  [[ [variable prop]: [type string] ]]: [type any];\",\n     \"  [property prop1]: [type any];\",\n     \"}\");\n\n  TS(\"typescript_generic_class\",\n     \"[keyword class] [def Foo][operator <][type T][operator >] {\",\n     \"  [property bar]() {}\",\n     \"  [property foo](): [type Foo] {}\",\n     \"}\")\n\n  TS(\"typescript_type_when_keyword\",\n     \"[keyword export] [keyword type] [type AB] [operator =] [type A] [operator |] [type B];\",\n     \"[keyword type] [type Flags] [operator =] {\",\n     \"  [property p1]: [type string];\",\n     \"  [property p2]: [type boolean];\",\n     \"};\")\n\n  TS(\"typescript_type_when_not_keyword\",\n     \"[keyword class] [def HasType] {\",\n     \"  [property type]: [type string];\",\n     \"  [property constructor]([def type]: [type string]) {\",\n     \"    [keyword this].[property type] [operator =] [variable-2 type];\",\n     \"  }\",\n     \"  [property setType]({ [def type] }: { [property type]: [type string]; }) {\",\n     \"    [keyword this].[property type] [operator =] [variable-2 type];\",\n     \"  }\",\n     \"}\")\n\n  TS(\"typescript_function_generics\",\n     \"[keyword function] [def a]() {}\",\n     \"[keyword function] [def b][operator <][type IA] [keyword extends] [type object], [type IB] [keyword extends] [type object][operator >]() {}\",\n     \"[keyword function] [def c]() {}\")\n\n  TS(\"typescript_complex_return_type\",\n     \"[keyword function] [def A]() {\",\n     \"  [keyword return] [keyword this].[property property];\",\n     \"}\",\n     \"[keyword function] [def B](): [type Promise][operator <]{ [[ [variable key]: [type string] ]]: [type any] } [operator |] [atom null][operator >] {\",\n     \"  [keyword return] [keyword this].[property property];\",\n     \"}\")\n\n  TS(\"typescript_complex_type_casting\",\n     \"[keyword const] [def giftpay] [operator =] [variable config].[property get]([string 'giftpay']) [keyword as] { [[ [variable platformUuid]: [type string] ]]: { [property version]: [type number]; [property apiCode]: [type string]; } };\")\n\n  TS(\"typescript_keyof\",\n     \"[keyword function] [def x][operator <][type T] [keyword extends] [keyword keyof] [type X][operator >]([def a]: [type T]) {\",\n     \"  [keyword return]\")\n\n  TS(\"typescript_new_typeargs\",\n     \"[keyword let] [def x] [operator =] [keyword new] [variable Map][operator <][type string], [type Date][operator >]([string-2 `foo${][variable bar][string-2 }`])\")\n\n  TS(\"modifiers\",\n     \"[keyword class] [def Foo] {\",\n     \"  [keyword public] [keyword abstract] [property bar]() {}\",\n     \"  [property constructor]([keyword readonly] [keyword private] [def x]) {}\",\n     \"}\")\n\n  TS(\"arrow prop\",\n     \"({[property a]: [def p] [operator =>] [variable-2 p]})\")\n\n  TS(\"generic in function call\",\n     \"[keyword this].[property a][operator <][type Type][operator >]([variable foo]);\",\n     \"[keyword this].[property a][operator <][variable Type][operator >][variable foo];\")\n\n  TS(\"type guard\",\n     \"[keyword class] [def Appler] {\",\n     \"  [keyword static] [property assertApple]([def fruit]: [type Fruit]): [variable-2 fruit] [keyword is] [type Apple] {\",\n     \"    [keyword if] ([operator !]([variable-2 fruit] [keyword instanceof] [variable Apple]))\",\n     \"      [keyword throw] [keyword new] [variable Error]();\",\n     \"  }\",\n     \"}\")\n\n  TS(\"type as variable\",\n     \"[variable type] [operator =] [variable x] [keyword as] [type Bar];\");\n\n  TS(\"enum body\",\n     \"[keyword export] [keyword const] [keyword enum] [def CodeInspectionResultType] {\",\n     \"  [def ERROR] [operator =] [string 'problem_type_error'],\",\n     \"  [def WARNING] [operator =] [string 'problem_type_warning'],\",\n     \"  [def META],\",\n     \"}\")\n\n  TS(\"parenthesized type\",\n     \"[keyword class] [def Foo] {\",\n     \"  [property x] [operator =] [keyword new] [variable A][operator <][type B], [type string][operator |](() [operator =>] [type void])[operator >]();\",\n     \"  [keyword private] [property bar]();\",\n     \"}\")\n\n  TS(\"abstract class\",\n     \"[keyword export] [keyword abstract] [keyword class] [def Foo] {}\")\n\n  TS(\"interface without semicolons\",\n     \"[keyword interface] [def Foo] {\",\n     \"  [property greet]([def x]: [type int]): [type blah]\",\n     \"  [property bar]: [type void]\",\n     \"}\")\n\n  var jsonld_mode = CodeMirror.getMode(\n    {indentUnit: 2},\n    {name: \"javascript\", jsonld: true}\n  );\n  function LD(name) {\n    test.mode(name, jsonld_mode, Array.prototype.slice.call(arguments, 1));\n  }\n\n  LD(\"json_ld_keywords\",\n    '{',\n    '  [meta \"@context\"]: {',\n    '    [meta \"@base\"]: [string \"http://example.com\"],',\n    '    [meta \"@vocab\"]: [string \"http://xmlns.com/foaf/0.1/\"],',\n    '    [property \"likesFlavor\"]: {',\n    '      [meta \"@container\"]: [meta \"@list\"]',\n    '      [meta \"@reverse\"]: [string \"@beFavoriteOf\"]',\n    '    },',\n    '    [property \"nick\"]: { [meta \"@container\"]: [meta \"@set\"] },',\n    '    [property \"nick\"]: { [meta \"@container\"]: [meta \"@index\"] }',\n    '  },',\n    '  [meta \"@graph\"]: [[ {',\n    '    [meta \"@id\"]: [string \"http://dbpedia.org/resource/John_Lennon\"],',\n    '    [property \"name\"]: [string \"John Lennon\"],',\n    '    [property \"modified\"]: {',\n    '      [meta \"@value\"]: [string \"2010-05-29T14:17:39+02:00\"],',\n    '      [meta \"@type\"]: [string \"http://www.w3.org/2001/XMLSchema#dateTime\"]',\n    '    }',\n    '  } ]]',\n    '}');\n\n  LD(\"json_ld_fake\",\n    '{',\n    '  [property \"@fake\"]: [string \"@fake\"],',\n    '  [property \"@contextual\"]: [string \"@identifier\"],',\n    '  [property \"user@domain.com\"]: [string \"@graphical\"],',\n    '  [property \"@ID\"]: [string \"@@ID\"]',\n    '}');\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/javascript/typescript.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: TypeScript mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"javascript.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">TypeScript</a>\n  </ul>\n</div>\n\n<article>\n<h2>TypeScript mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\nclass Greeter {\n  greeting: string;\n  constructor (message: string) {\n    this.greeting = message;\n  }\n  greet() {\n    return \"Hello, \" + this.greeting;\n  }\n}   \n\nvar greeter = new Greeter(\"world\");\n\nvar button = document.createElement('button')\nbutton.innerText = \"Say Hello\"\nbutton.onclick = function() {\n  alert(greeter.greet())\n}\n\ndocument.body.appendChild(button)\n\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/typescript\"\n      });\n    </script>\n\n    <p>This is a specialization of the <a href=\"index.html\">JavaScript mode</a>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/jinja2/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Jinja2 mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"jinja2.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Jinja2</a>\n  </ul>\n</div>\n\n<article>\n<h2>Jinja2 mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n{# this is a comment #}\n{%- for item in li -%}\n  &lt;li&gt;{{ item.label }}&lt;/li&gt;\n{% endfor -%}\n{{ item.sand == true and item.keyword == false ? 1 : 0 }}\n{{ app.get(55, 1.2, true) }}\n{% if app.get(&#39;_route&#39;) == (&#39;_home&#39;) %}home{% endif %}\n{% if app.session.flashbag.has(&#39;message&#39;) %}\n  {% for message in app.session.flashbag.get(&#39;message&#39;) %}\n    {{ message.content }}\n  {% endfor %}\n{% endif %}\n{{ path(&#39;_home&#39;, {&#39;section&#39;: app.request.get(&#39;section&#39;)}) }}\n{{ path(&#39;_home&#39;, {\n    &#39;section&#39;: app.request.get(&#39;section&#39;),\n    &#39;boolean&#39;: true,\n    &#39;number&#39;: 55.33\n  })\n}}\n{% include (&#39;test.incl.html.twig&#39;) %}\n\n# for item in seq:\n    &lt;li&gt;{{ item }} &lt;/li&gt;    ## this comment is ignored\n# endfor\n# set text = \"Multiline\n\ttext\"\n# for href, caption in [('index.html', 'Index'),\n                        ('about.html', 'About')]:\n    &lt;li&gt;&lt;a href=\"{{ href }}\"&gt;{{ caption }}&lt;/a&gt;&lt;/li&gt;\n# endfor\n</textarea></form>\n    <script>\n      var editor =\n      CodeMirror.fromTextArea(document.getElementById(\"code\"), {mode:\n        {name: \"jinja2\", htmlMode: true}});\n    </script>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/jinja2/jinja2.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"jinja2\", function() {\n    var keywords = [\"and\", \"as\", \"block\", \"endblock\", \"by\", \"cycle\", \"debug\", \"else\", \"elif\",\n      \"extends\", \"filter\", \"endfilter\", \"firstof\", \"do\", \"for\",\n      \"endfor\", \"if\", \"endif\", \"ifchanged\", \"endifchanged\",\n      \"ifequal\", \"endifequal\", \"ifnotequal\", \"set\", \"raw\", \"endraw\",\n      \"endifnotequal\", \"in\", \"include\", \"load\", \"not\", \"now\", \"or\",\n      \"parsed\", \"regroup\", \"reversed\", \"spaceless\", \"call\", \"endcall\", \"macro\",\n      \"endmacro\", \"endspaceless\", \"ssi\", \"templatetag\", \"openblock\",\n      \"closeblock\", \"openvariable\", \"closevariable\", \"without\", \"context\",\n      \"openbrace\", \"closebrace\", \"opencomment\",\n      \"closecomment\", \"widthratio\", \"url\", \"with\", \"endwith\",\n      \"get_current_language\", \"trans\", \"endtrans\", \"noop\", \"blocktrans\",\n      \"endblocktrans\", \"get_available_languages\",\n      \"get_current_language_bidi\", \"pluralize\", \"autoescape\", \"endautoescape\"],\n    operator = /^[+\\-*&%=<>!?|~^]/,\n    sign = /^[:\\[\\(\\{]/,\n    atom = [\"true\", \"false\"],\n    number = /^(\\d[+\\-\\*\\/])?\\d+(\\.\\d+)?/;\n\n    keywords = new RegExp(\"((\" + keywords.join(\")|(\") + \"))\\\\b\");\n    atom = new RegExp(\"((\" + atom.join(\")|(\") + \"))\\\\b\");\n\n    function tokenBase (stream, state) {\n      var ch = stream.peek();\n\n      //Comment\n      if (state.incomment) {\n        if(!stream.skipTo(\"#}\")) {\n          stream.skipToEnd();\n        } else {\n          stream.eatWhile(/\\#|}/);\n          state.incomment = false;\n        }\n        return \"comment\";\n      //Tag\n      } else if (state.intag) {\n        //After operator\n        if(state.operator) {\n          state.operator = false;\n          if(stream.match(atom)) {\n            return \"atom\";\n          }\n          if(stream.match(number)) {\n            return \"number\";\n          }\n        }\n        //After sign\n        if(state.sign) {\n          state.sign = false;\n          if(stream.match(atom)) {\n            return \"atom\";\n          }\n          if(stream.match(number)) {\n            return \"number\";\n          }\n        }\n\n        if(state.instring) {\n          if(ch == state.instring) {\n            state.instring = false;\n          }\n          stream.next();\n          return \"string\";\n        } else if(ch == \"'\" || ch == '\"') {\n          state.instring = ch;\n          stream.next();\n          return \"string\";\n        }\n        else if (state.inbraces > 0 && ch ==\")\") {\n          stream.next()\n          state.inbraces--;\n        }\n        else if (ch == \"(\") {\n          stream.next()\n          state.inbraces++;\n        }\n        else if (state.inbrackets > 0 && ch ==\"]\") {\n          stream.next()\n          state.inbrackets--;\n        }\n        else if (ch == \"[\") {\n          stream.next()\n          state.inbrackets++;\n        }\n        else if (!state.lineTag && (stream.match(state.intag + \"}\") || stream.eat(\"-\") && stream.match(state.intag + \"}\"))) {\n          state.intag = false;\n          return \"tag\";\n        } else if(stream.match(operator)) {\n          state.operator = true;\n          return \"operator\";\n        } else if(stream.match(sign)) {\n          state.sign = true;\n        } else {\n          if (stream.column() == 1 && state.lineTag && stream.match(keywords)) {\n            //allow nospace after tag before the keyword\n            return \"keyword\";\n          }\n          if(stream.eat(\" \") || stream.sol()) {\n            if(stream.match(keywords)) {\n              return \"keyword\";\n            }\n            if(stream.match(atom)) {\n              return \"atom\";\n            }\n            if(stream.match(number)) {\n              return \"number\";\n            }\n            if(stream.sol()) {\n              stream.next();\n            }\n          } else {\n            stream.next();\n          }\n\n        }\n        return \"variable\";\n      } else if (stream.eat(\"{\")) {\n        if (stream.eat(\"#\")) {\n          state.incomment = true;\n          if(!stream.skipTo(\"#}\")) {\n            stream.skipToEnd();\n          } else {\n            stream.eatWhile(/\\#|}/);\n            state.incomment = false;\n          }\n          return \"comment\";\n        //Open tag\n        } else if (ch = stream.eat(/\\{|%/)) {\n          //Cache close tag\n          state.intag = ch;\n          state.inbraces = 0;\n          state.inbrackets = 0;\n          if(ch == \"{\") {\n            state.intag = \"}\";\n          }\n          stream.eat(\"-\");\n          return \"tag\";\n        }\n      //Line statements\n      } else if (stream.eat('#')) {\n        if (stream.peek() == '#') {\n          stream.skipToEnd();\n          return \"comment\"\n        }\n        else if (!stream.eol()) {\n          state.intag = true;\n          state.lineTag = true;\n          state.inbraces = 0;\n          state.inbrackets = 0;\n          return \"tag\";\n        }\n      }\n      stream.next();\n    };\n\n    return {\n      startState: function () {\n        return {\n          tokenize: tokenBase,\n          inbrackets:0,\n          inbraces:0\n        };\n      },\n      token: function(stream, state) {\n        var style = state.tokenize(stream, state);\n        if (stream.eol() && state.lineTag && !state.instring && state.inbraces == 0 && state.inbrackets == 0) {\n          //Close line statement at the EOL\n          state.intag = false\n          state.lineTag = false\n        }\n        return style;\n      },\n      blockCommentStart: \"{#\",\n      blockCommentEnd: \"#}\",\n      lineComment: \"##\",\n    };\n  });\n\n  CodeMirror.defineMIME(\"text/jinja2\", \"jinja2\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/jsx/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: JSX mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../javascript/javascript.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"jsx.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">JSX</a>\n  </ul>\n</div>\n\n<article>\n<h2>JSX mode</h2>\n\n<div><textarea id=\"code\" name=\"code\">// Code snippets from http://facebook.github.io/react/docs/jsx-in-depth.html\n\n// Rendering HTML tags\nvar myDivElement = <div className=\"foo\" />;\nReactDOM.render(myDivElement, document.getElementById('example'));\n\n// Rendering React components\nvar MyComponent = React.createClass({/*...*/});\nvar myElement = <MyComponent someProperty={true} />;\nReactDOM.render(myElement, document.getElementById('example'));\n\n// Namespaced components\nvar Form = MyFormComponent;\n\nvar App = (\n  <Form>\n    <Form.Row>\n      <Form.Label />\n      <Form.Input />\n    </Form.Row>\n  </Form>\n);\n\n// Attribute JavaScript expressions\nvar person = <Person name={window.isLoggedIn ? window.name : ''} />;\n\n// Boolean attributes\n<input type=\"button\" disabled />;\n<input type=\"button\" disabled={true} />;\n\n// Child JavaScript expressions\nvar content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;\n\n// Comments\nvar content = (\n  <Nav>\n    {/* child comment, put {} around */}\n    <Person\n      /* multi\n         line\n         comment */\n      name={window.isLoggedIn ? window.name : ''} // end of line comment\n    />\n  </Nav>\n);\n</textarea></div>\n\n<script>\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  lineNumbers: true,\n  mode: \"jsx\"\n})\n</script>\n\n<p>JSX Mode for <a href=\"http://facebook.github.io/react\">React</a>'s\nJavaScript syntax extension.</p>\n\n<p><strong>MIME types defined:</strong> <code>text/jsx</code>, <code>text/typescript-jsx</code>.</p>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/jsx/jsx.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../xml/xml\"), require(\"../javascript/javascript\"))\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../xml/xml\", \"../javascript/javascript\"], mod)\n  else // Plain browser env\n    mod(CodeMirror)\n})(function(CodeMirror) {\n  \"use strict\"\n\n  // Depth means the amount of open braces in JS context, in XML\n  // context 0 means not in tag, 1 means in tag, and 2 means in tag\n  // and js block comment.\n  function Context(state, mode, depth, prev) {\n    this.state = state; this.mode = mode; this.depth = depth; this.prev = prev\n  }\n\n  function copyContext(context) {\n    return new Context(CodeMirror.copyState(context.mode, context.state),\n                       context.mode,\n                       context.depth,\n                       context.prev && copyContext(context.prev))\n  }\n\n  CodeMirror.defineMode(\"jsx\", function(config, modeConfig) {\n    var xmlMode = CodeMirror.getMode(config, {name: \"xml\", allowMissing: true, multilineTagIndentPastTag: false, allowMissingTagName: true})\n    var jsMode = CodeMirror.getMode(config, modeConfig && modeConfig.base || \"javascript\")\n\n    function flatXMLIndent(state) {\n      var tagName = state.tagName\n      state.tagName = null\n      var result = xmlMode.indent(state, \"\", \"\")\n      state.tagName = tagName\n      return result\n    }\n\n    function token(stream, state) {\n      if (state.context.mode == xmlMode)\n        return xmlToken(stream, state, state.context)\n      else\n        return jsToken(stream, state, state.context)\n    }\n\n    function xmlToken(stream, state, cx) {\n      if (cx.depth == 2) { // Inside a JS /* */ comment\n        if (stream.match(/^.*?\\*\\//)) cx.depth = 1\n        else stream.skipToEnd()\n        return \"comment\"\n      }\n\n      if (stream.peek() == \"{\") {\n        xmlMode.skipAttribute(cx.state)\n\n        var indent = flatXMLIndent(cx.state), xmlContext = cx.state.context\n        // If JS starts on same line as tag\n        if (xmlContext && stream.match(/^[^>]*>\\s*$/, false)) {\n          while (xmlContext.prev && !xmlContext.startOfLine)\n            xmlContext = xmlContext.prev\n          // If tag starts the line, use XML indentation level\n          if (xmlContext.startOfLine) indent -= config.indentUnit\n          // Else use JS indentation level\n          else if (cx.prev.state.lexical) indent = cx.prev.state.lexical.indented\n        // Else if inside of tag\n        } else if (cx.depth == 1) {\n          indent += config.indentUnit\n        }\n\n        state.context = new Context(CodeMirror.startState(jsMode, indent),\n                                    jsMode, 0, state.context)\n        return null\n      }\n\n      if (cx.depth == 1) { // Inside of tag\n        if (stream.peek() == \"<\") { // Tag inside of tag\n          xmlMode.skipAttribute(cx.state)\n          state.context = new Context(CodeMirror.startState(xmlMode, flatXMLIndent(cx.state)),\n                                      xmlMode, 0, state.context)\n          return null\n        } else if (stream.match(\"//\")) {\n          stream.skipToEnd()\n          return \"comment\"\n        } else if (stream.match(\"/*\")) {\n          cx.depth = 2\n          return token(stream, state)\n        }\n      }\n\n      var style = xmlMode.token(stream, cx.state), cur = stream.current(), stop\n      if (/\\btag\\b/.test(style)) {\n        if (/>$/.test(cur)) {\n          if (cx.state.context) cx.depth = 0\n          else state.context = state.context.prev\n        } else if (/^</.test(cur)) {\n          cx.depth = 1\n        }\n      } else if (!style && (stop = cur.indexOf(\"{\")) > -1) {\n        stream.backUp(cur.length - stop)\n      }\n      return style\n    }\n\n    function jsToken(stream, state, cx) {\n      if (stream.peek() == \"<\" && !stream.match(/^<([^<>]|<[^>]*>)+,\\s*>/, false) &&\n          jsMode.expressionAllowed(stream, cx.state)) {\n        state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, \"\", \"\")),\n                                    xmlMode, 0, state.context)\n        jsMode.skipExpression(cx.state)\n        return null\n      }\n\n      var style = jsMode.token(stream, cx.state)\n      if (!style && cx.depth != null) {\n        var cur = stream.current()\n        if (cur == \"{\") {\n          cx.depth++\n        } else if (cur == \"}\") {\n          if (--cx.depth == 0) state.context = state.context.prev\n        }\n      }\n      return style\n    }\n\n    return {\n      startState: function() {\n        return {context: new Context(CodeMirror.startState(jsMode), jsMode)}\n      },\n\n      copyState: function(state) {\n        return {context: copyContext(state.context)}\n      },\n\n      token: token,\n\n      indent: function(state, textAfter, fullLine) {\n        return state.context.mode.indent(state.context.state, textAfter, fullLine)\n      },\n\n      innerMode: function(state) {\n        return state.context\n      }\n    }\n  }, \"xml\", \"javascript\")\n\n  CodeMirror.defineMIME(\"text/jsx\", \"jsx\")\n  CodeMirror.defineMIME(\"text/typescript-jsx\", {name: \"jsx\", base: {name: \"javascript\", typescript: true}})\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/jsx/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"jsx\")\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)) }\n\n  MT(\"selfclose\",\n     \"[keyword var] [def x] [operator =] [bracket&tag <] [tag foo] [bracket&tag />] [operator +] [number 1];\")\n\n  MT(\"openclose\",\n     \"([bracket&tag <][tag foo][bracket&tag >]hello [atom &amp;][bracket&tag </][tag foo][bracket&tag >][operator ++])\")\n\n  MT(\"openclosefragment\",\n     \"([bracket&tag <><][tag foo][bracket&tag >]hello [atom &amp;][bracket&tag </][tag foo][bracket&tag ></>][operator ++])\")\n\n  MT(\"attr\",\n     \"([bracket&tag <][tag foo] [attribute abc]=[string 'value'][bracket&tag >]hello [atom &amp;][bracket&tag </][tag foo][bracket&tag >][operator ++])\")\n\n  MT(\"braced_attr\",\n     \"([bracket&tag <][tag foo] [attribute abc]={[number 10]}[bracket&tag >]hello [atom &amp;][bracket&tag </][tag foo][bracket&tag >][operator ++])\")\n\n  MT(\"braced_text\",\n     \"([bracket&tag <][tag foo][bracket&tag >]hello {[number 10]} [atom &amp;][bracket&tag </][tag foo][bracket&tag >][operator ++])\")\n\n  MT(\"nested_tag\",\n     \"([bracket&tag <][tag foo][bracket&tag ><][tag bar][bracket&tag ></][tag bar][bracket&tag ></][tag foo][bracket&tag >][operator ++])\")\n\n  MT(\"nested_jsx\",\n     \"[keyword return] (\",\n     \"  [bracket&tag <][tag foo][bracket&tag >]\",\n     \"    say {[number 1] [operator +] [bracket&tag <][tag bar] [attribute attr]={[number 10]}[bracket&tag />]}!\",\n     \"  [bracket&tag </][tag foo][bracket&tag >][operator ++]\",\n     \")\")\n\n  MT(\"preserve_js_context\",\n     \"[variable x] [operator =] [string-2 `quasi${][bracket&tag <][tag foo][bracket&tag />][string-2 }quoted`]\")\n\n  MT(\"string_interpolation\",\n    \"[variable x] [operator =] [string-2 `quasi<code>${] [number 10] [string-2 }</code>`]\")\n\n  MT(\"line_comment\",\n     \"([bracket&tag <][tag foo] [comment // hello]\",\n     \"   [bracket&tag ></][tag foo][bracket&tag >][operator ++])\")\n\n  MT(\"line_comment_not_in_tag\",\n     \"([bracket&tag <][tag foo][bracket&tag >] // hello\",\n     \"  [bracket&tag </][tag foo][bracket&tag >][operator ++])\")\n\n  MT(\"block_comment\",\n     \"([bracket&tag <][tag foo] [comment /* hello]\",\n     \"[comment    line 2]\",\n     \"[comment    line 3 */] [bracket&tag ></][tag foo][bracket&tag >][operator ++])\")\n\n  MT(\"block_comment_not_in_tag\",\n     \"([bracket&tag <][tag foo][bracket&tag >]/* hello\",\n     \"    line 2\",\n     \"    line 3 */ [bracket&tag </][tag foo][bracket&tag >][operator ++])\")\n\n  MT(\"missing_attr\",\n     \"([bracket&tag <][tag foo] [attribute selected][bracket&tag />][operator ++])\")\n\n  MT(\"indent_js\",\n     \"([bracket&tag <][tag foo][bracket&tag >]\",\n     \"    [bracket&tag <][tag bar] [attribute baz]={[keyword function]() {\",\n     \"        [keyword return] [number 10]\",\n     \"      }}[bracket&tag />]\",\n     \"  [bracket&tag </][tag foo][bracket&tag >])\")\n\n  MT(\"spread\",\n     \"([bracket&tag <][tag foo] [attribute bar]={[meta ...][variable baz] [operator /][number 2]}[bracket&tag />])\")\n\n  MT(\"tag_attribute\",\n     \"([bracket&tag <][tag foo] [attribute bar]=[bracket&tag <][tag foo][bracket&tag />/>][operator ++])\")\n\n  MT(\"in_array\",\n     \"[[\",\n     \"  [bracket&tag <][tag Something][bracket&tag />],\",\n     \"  [string-2 `${][variable x][string-2 }`],\",\n     \"  [variable y]\",\n     \"]]\")\n\n  var ts_mode = CodeMirror.getMode({indentUnit: 2}, \"text/typescript-jsx\")\n  function TS(name) { test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1)) }\n\n  TS(\"tsx_react_integration\",\n     \"[keyword interface] [def Props] {\",\n     \"  [property foo]: [type string];\",\n     \"}\",\n     \"[keyword class] [def MyComponent] [keyword extends] [type React].[type Component] [operator <] [type Props], [type any] [operator >] {\",\n     \"  [property render]() {\",\n     \"    [keyword return] [bracket&tag <][tag span][bracket&tag >]{[keyword this].[property props].[property foo]}[bracket&tag </][tag span][bracket&tag >]\",\n     \"  }\",\n     \"}\",\n     \"[bracket&tag <][tag MyComponent] [attribute foo]=[string \\\"bar\\\"] [bracket&tag />]; [comment //ok]\",\n     \"[bracket&tag <][tag MyComponent] [attribute foo]={[number 0]} [bracket&tag />]; [comment //error]\")\n\n   TS(\"tsx_react_generics\",\n      \"[variable x] [operator =] [operator <] [variable T],[operator >] ([def v]: [type T]) [operator =>] [variable-2 v];\")\n})()\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/julia/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Julia mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"julia.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Julia</a>\n  </ul>\n</div>\n\n<article>\n<h2>Julia mode</h2>\n\n    <div><textarea id=\"code\" name=\"code\">\n#numbers\n1234\n1234im\n.234\n.234im\n2.23im\n2.3f3\n23e2\n0x234\n\n#strings\n'a'\n\"asdf\"\nr\"regex\"\nb\"bytestring\"\n\n\"\"\"\nmultiline string\n\"\"\"\n\n#identifiers\na\nas123\nfunction_name!\n\n#unicode identifiers\n# a = x\\ddot\na⃗ = ẍ\n# a = v\\dot\na⃗ = v̇\n#F\\vec = m \\cdotp a\\vec\nF⃗ = m·a⃗\n\n#literal identifier multiples\n3x\n4[1, 2, 3]\n\n#dicts and indexing\nx=[1, 2, 3]\nx[end-1]\nx={\"julia\"=>\"language of technical computing\"}\n\n\n#exception handling\ntry\n  f()\ncatch\n  @printf \"Error\"\nfinally\n  g()\nend\n\n#types\nimmutable Color{T<:Number}\n  r::T\n  g::T\n  b::T\nend\n\n#functions\nfunction change!(x::Vector{Float64})\n  for i = 1:length(x)\n    x[i] *= 2\n  end\nend\n\n#function invocation\nf('b', (2, 3)...)\n\n#operators\n|=\n&=\n^=\n\\-\n%=\n*=\n+=\n-=\n<=\n>=\n!=\n==\n%\n*\n+\n-\n<\n>\n!\n=\n|\n&\n^\n\\\n?\n~\n:\n$\n<:\n.<\n.>\n<<\n<<=\n>>\n>>>>\n>>=\n>>>=\n<<=\n<<<=\n.<=\n.>=\n.==\n->\n//\nin\n...\n//\n:=\n.//=\n.*=\n./=\n.^=\n.%=\n.+=\n.-=\n\\=\n\\\\=\n||\n===\n&&\n|=\n.|=\n<:\n>:\n|>\n<|\n::\nx ? y : z\n\n#macros\n@spawnat 2 1+1\n@eval(:x)\n\n#keywords and operators\nif else elseif while for\n begin let end do\ntry catch finally return break continue\nglobal local const \nexport import importall using\nfunction macro module baremodule \ntype immutable quote\ntrue false enumerate\n\n\n    </textarea></div>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: {name: \"julia\",\n               },\n        lineNumbers: true,\n        indentUnit: 4,\n        matchBrackets: true\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-julia</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/julia/julia.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"julia\", function(config, parserConf) {\n  function wordRegexp(words, end, pre) {\n    if (typeof pre === \"undefined\") { pre = \"\"; }\n    if (typeof end === \"undefined\") { end = \"\\\\b\"; }\n    return new RegExp(\"^\" + pre + \"((\" + words.join(\")|(\") + \"))\" + end);\n  }\n\n  var octChar = \"\\\\\\\\[0-7]{1,3}\";\n  var hexChar = \"\\\\\\\\x[A-Fa-f0-9]{1,2}\";\n  var sChar = \"\\\\\\\\[abefnrtv0%?'\\\"\\\\\\\\]\";\n  var uChar = \"([^\\\\u0027\\\\u005C\\\\uD800-\\\\uDFFF]|[\\\\uD800-\\\\uDFFF][\\\\uDC00-\\\\uDFFF])\";\n\n  var asciiOperatorsList = [\n    \"[<>]:\", \"[<>=]=\", \"<<=?\", \">>>?=?\", \"=>\", \"--?>\", \"<--[->]?\", \"\\\\/\\\\/\",\n    \"\\\\.{2,3}\", \"[\\\\.\\\\\\\\%*+\\\\-<>!\\\\/^|&]=?\", \"\\\\?\", \"\\\\$\", \"~\", \":\"\n  ];\n  var operators = parserConf.operators || wordRegexp([\n    \"[<>]:\", \"[<>=]=\", \"[!=]==\", \"<<=?\", \">>>?=?\", \"=>?\", \"--?>\", \"<--[->]?\", \"\\\\/\\\\/\",\n    \"[\\\\\\\\%*+\\\\-<>!\\\\/^|&\\\\u00F7\\\\u22BB]=?\", \"\\\\?\", \"\\\\$\", \"~\", \":\",\n    \"\\\\u00D7\", \"\\\\u2208\", \"\\\\u2209\", \"\\\\u220B\", \"\\\\u220C\", \"\\\\u2218\",\n    \"\\\\u221A\", \"\\\\u221B\", \"\\\\u2229\", \"\\\\u222A\", \"\\\\u2260\", \"\\\\u2264\",\n    \"\\\\u2265\", \"\\\\u2286\", \"\\\\u2288\", \"\\\\u228A\", \"\\\\u22C5\",\n    \"\\\\b(in|isa)\\\\b(?!\\.?\\\\()\"\n  ], \"\");\n  var delimiters = parserConf.delimiters || /^[;,()[\\]{}]/;\n  var identifiers = parserConf.identifiers ||\n        /^[_A-Za-z\\u00A1-\\u2217\\u2219-\\uFFFF][\\w\\u00A1-\\u2217\\u2219-\\uFFFF]*!*/;\n\n  var chars = wordRegexp([octChar, hexChar, sChar, uChar], \"'\");\n\n  var openersList = [\"begin\", \"function\", \"type\", \"struct\", \"immutable\", \"let\",\n        \"macro\", \"for\", \"while\", \"quote\", \"if\", \"else\", \"elseif\", \"try\",\n        \"finally\", \"catch\", \"do\"];\n\n  var closersList = [\"end\", \"else\", \"elseif\", \"catch\", \"finally\"];\n\n  var keywordsList = [\"if\", \"else\", \"elseif\", \"while\", \"for\", \"begin\", \"let\",\n        \"end\", \"do\", \"try\", \"catch\", \"finally\", \"return\", \"break\", \"continue\",\n        \"global\", \"local\", \"const\", \"export\", \"import\", \"importall\", \"using\",\n        \"function\", \"where\", \"macro\", \"module\", \"baremodule\", \"struct\", \"type\",\n        \"mutable\", \"immutable\", \"quote\", \"typealias\", \"abstract\", \"primitive\",\n        \"bitstype\"];\n\n  var builtinsList = [\"true\", \"false\", \"nothing\", \"NaN\", \"Inf\"];\n\n  CodeMirror.registerHelper(\"hintWords\", \"julia\", keywordsList.concat(builtinsList));\n\n  var openers = wordRegexp(openersList);\n  var closers = wordRegexp(closersList);\n  var keywords = wordRegexp(keywordsList);\n  var builtins = wordRegexp(builtinsList);\n\n  var macro = /^@[_A-Za-z\\u00A1-\\uFFFF][\\w\\u00A1-\\uFFFF]*!*/;\n  var symbol = /^:[_A-Za-z\\u00A1-\\uFFFF][\\w\\u00A1-\\uFFFF]*!*/;\n  var stringPrefixes = /^(`|([_A-Za-z\\u00A1-\\uFFFF]*\"(\"\")?))/;\n\n  var macroOperators = wordRegexp(asciiOperatorsList, \"\", \"@\");\n  var symbolOperators = wordRegexp(asciiOperatorsList, \"\", \":\");\n\n  function inArray(state) {\n    return (state.nestedArrays > 0);\n  }\n\n  function inGenerator(state) {\n    return (state.nestedGenerators > 0);\n  }\n\n  function currentScope(state, n) {\n    if (typeof(n) === \"undefined\") { n = 0; }\n    if (state.scopes.length <= n) {\n      return null;\n    }\n    return state.scopes[state.scopes.length - (n + 1)];\n  }\n\n  // tokenizers\n  function tokenBase(stream, state) {\n    // Handle multiline comments\n    if (stream.match('#=', false)) {\n      state.tokenize = tokenComment;\n      return state.tokenize(stream, state);\n    }\n\n    // Handle scope changes\n    var leavingExpr = state.leavingExpr;\n    if (stream.sol()) {\n      leavingExpr = false;\n    }\n    state.leavingExpr = false;\n\n    if (leavingExpr) {\n      if (stream.match(/^'+/)) {\n        return \"operator\";\n      }\n    }\n\n    if (stream.match(/\\.{4,}/)) {\n      return \"error\";\n    } else if (stream.match(/\\.{1,3}/)) {\n      return \"operator\";\n    }\n\n    if (stream.eatSpace()) {\n      return null;\n    }\n\n    var ch = stream.peek();\n\n    // Handle single line comments\n    if (ch === '#') {\n      stream.skipToEnd();\n      return \"comment\";\n    }\n\n    if (ch === '[') {\n      state.scopes.push('[');\n      state.nestedArrays++;\n    }\n\n    if (ch === '(') {\n      state.scopes.push('(');\n      state.nestedGenerators++;\n    }\n\n    if (inArray(state) && ch === ']') {\n      while (state.scopes.length && currentScope(state) !== \"[\") { state.scopes.pop(); }\n      state.scopes.pop();\n      state.nestedArrays--;\n      state.leavingExpr = true;\n    }\n\n    if (inGenerator(state) && ch === ')') {\n      while (state.scopes.length && currentScope(state) !== \"(\") { state.scopes.pop(); }\n      state.scopes.pop();\n      state.nestedGenerators--;\n      state.leavingExpr = true;\n    }\n\n    if (inArray(state)) {\n      if (state.lastToken == \"end\" && stream.match(':')) {\n        return \"operator\";\n      }\n      if (stream.match('end')) {\n        return \"number\";\n      }\n    }\n\n    var match;\n    if (match = stream.match(openers, false)) {\n      state.scopes.push(match[0]);\n    }\n\n    if (stream.match(closers, false)) {\n      state.scopes.pop();\n    }\n\n    // Handle type annotations\n    if (stream.match(/^::(?![:\\$])/)) {\n      state.tokenize = tokenAnnotation;\n      return state.tokenize(stream, state);\n    }\n\n    // Handle symbols\n    if (!leavingExpr && (stream.match(symbol) || stream.match(symbolOperators))) {\n      return \"builtin\";\n    }\n\n    // Handle parametric types\n    //if (stream.match(/^{[^}]*}(?=\\()/)) {\n    //  return \"builtin\";\n    //}\n\n    // Handle operators and Delimiters\n    if (stream.match(operators)) {\n      return \"operator\";\n    }\n\n    // Handle Number Literals\n    if (stream.match(/^\\.?\\d/, false)) {\n      var imMatcher = RegExp(/^im\\b/);\n      var numberLiteral = false;\n      if (stream.match(/^0x\\.[0-9a-f_]+p[\\+\\-]?[_\\d]+/i)) { numberLiteral = true; }\n      // Integers\n      if (stream.match(/^0x[0-9a-f_]+/i)) { numberLiteral = true; } // Hex\n      if (stream.match(/^0b[01_]+/i)) { numberLiteral = true; } // Binary\n      if (stream.match(/^0o[0-7_]+/i)) { numberLiteral = true; } // Octal\n      // Floats\n      if (stream.match(/^(?:(?:\\d[_\\d]*)?\\.(?!\\.)(?:\\d[_\\d]*)?|\\d[_\\d]*\\.(?!\\.)(?:\\d[_\\d]*))?([Eef][\\+\\-]?[_\\d]+)?/i)) { numberLiteral = true; }\n      if (stream.match(/^\\d[_\\d]*(e[\\+\\-]?\\d+)?/i)) { numberLiteral = true; } // Decimal\n      if (numberLiteral) {\n          // Integer literals may be \"long\"\n          stream.match(imMatcher);\n          state.leavingExpr = true;\n          return \"number\";\n      }\n    }\n\n    // Handle Chars\n    if (stream.match('\\'')) {\n      state.tokenize = tokenChar;\n      return state.tokenize(stream, state);\n    }\n\n    // Handle Strings\n    if (stream.match(stringPrefixes)) {\n      state.tokenize = tokenStringFactory(stream.current());\n      return state.tokenize(stream, state);\n    }\n\n    if (stream.match(macro) || stream.match(macroOperators)) {\n      return \"meta\";\n    }\n\n    if (stream.match(delimiters)) {\n      return null;\n    }\n\n    if (stream.match(keywords)) {\n      return \"keyword\";\n    }\n\n    if (stream.match(builtins)) {\n      return \"builtin\";\n    }\n\n    var isDefinition = state.isDefinition || state.lastToken == \"function\" ||\n                       state.lastToken == \"macro\" || state.lastToken == \"type\" ||\n                       state.lastToken == \"struct\" || state.lastToken == \"immutable\";\n\n    if (stream.match(identifiers)) {\n      if (isDefinition) {\n        if (stream.peek() === '.') {\n          state.isDefinition = true;\n          return \"variable\";\n        }\n        state.isDefinition = false;\n        return \"def\";\n      }\n      state.leavingExpr = true;\n      return \"variable\";\n    }\n\n    // Handle non-detected items\n    stream.next();\n    return \"error\";\n  }\n\n  function tokenAnnotation(stream, state) {\n    stream.match(/.*?(?=[,;{}()=\\s]|$)/);\n    if (stream.match('{')) {\n      state.nestedParameters++;\n    } else if (stream.match('}') && state.nestedParameters > 0) {\n      state.nestedParameters--;\n    }\n    if (state.nestedParameters > 0) {\n      stream.match(/.*?(?={|})/) || stream.next();\n    } else if (state.nestedParameters == 0) {\n      state.tokenize = tokenBase;\n    }\n    return \"builtin\";\n  }\n\n  function tokenComment(stream, state) {\n    if (stream.match('#=')) {\n      state.nestedComments++;\n    }\n    if (!stream.match(/.*?(?=(#=|=#))/)) {\n      stream.skipToEnd();\n    }\n    if (stream.match('=#')) {\n      state.nestedComments--;\n      if (state.nestedComments == 0)\n        state.tokenize = tokenBase;\n    }\n    return \"comment\";\n  }\n\n  function tokenChar(stream, state) {\n    var isChar = false, match;\n    if (stream.match(chars)) {\n      isChar = true;\n    } else if (match = stream.match(/\\\\u([a-f0-9]{1,4})(?=')/i)) {\n      var value = parseInt(match[1], 16);\n      if (value <= 55295 || value >= 57344) { // (U+0,U+D7FF), (U+E000,U+FFFF)\n        isChar = true;\n        stream.next();\n      }\n    } else if (match = stream.match(/\\\\U([A-Fa-f0-9]{5,8})(?=')/)) {\n      var value = parseInt(match[1], 16);\n      if (value <= 1114111) { // U+10FFFF\n        isChar = true;\n        stream.next();\n      }\n    }\n    if (isChar) {\n      state.leavingExpr = true;\n      state.tokenize = tokenBase;\n      return \"string\";\n    }\n    if (!stream.match(/^[^']+(?=')/)) { stream.skipToEnd(); }\n    if (stream.match('\\'')) { state.tokenize = tokenBase; }\n    return \"error\";\n  }\n\n  function tokenStringFactory(delimiter) {\n    if (delimiter.substr(-3) === '\"\"\"') {\n      delimiter = '\"\"\"';\n    } else if (delimiter.substr(-1) === '\"') {\n      delimiter = '\"';\n    }\n    function tokenString(stream, state) {\n      if (stream.eat('\\\\')) {\n        stream.next();\n      } else if (stream.match(delimiter)) {\n        state.tokenize = tokenBase;\n        state.leavingExpr = true;\n        return \"string\";\n      } else {\n        stream.eat(/[`\"]/);\n      }\n      stream.eatWhile(/[^\\\\`\"]/);\n      return \"string\";\n    }\n    return tokenString;\n  }\n\n  var external = {\n    startState: function() {\n      return {\n        tokenize: tokenBase,\n        scopes: [],\n        lastToken: null,\n        leavingExpr: false,\n        isDefinition: false,\n        nestedArrays: 0,\n        nestedComments: 0,\n        nestedGenerators: 0,\n        nestedParameters: 0,\n        firstParenPos: -1\n      };\n    },\n\n    token: function(stream, state) {\n      var style = state.tokenize(stream, state);\n      var current = stream.current();\n\n      if (current && style) {\n        state.lastToken = current;\n      }\n\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      var delta = 0;\n      if ( textAfter === ']' || textAfter === ')' || /^end\\b/.test(textAfter) ||\n           /^else/.test(textAfter) || /^catch\\b/.test(textAfter) || /^elseif\\b/.test(textAfter) ||\n           /^finally/.test(textAfter) ) {\n        delta = -1;\n      }\n      return (state.scopes.length + delta) * config.indentUnit;\n    },\n\n    electricInput: /\\b(end|else|catch|finally)\\b/,\n    blockCommentStart: \"#=\",\n    blockCommentEnd: \"=#\",\n    lineComment: \"#\",\n    closeBrackets: \"()[]{}\\\"\\\"\",\n    fold: \"indent\"\n  };\n  return external;\n});\n\n\nCodeMirror.defineMIME(\"text/x-julia\", \"julia\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/livescript/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: LiveScript mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/solarized.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"livescript.js\"></script>\n<style>.CodeMirror {font-size: 80%;border-top: 1px solid silver; border-bottom: 1px solid silver;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">LiveScript</a>\n  </ul>\n</div>\n\n<article>\n<h2>LiveScript mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n# LiveScript mode for CodeMirror\n# The following script, prelude.ls, is used to\n# demonstrate LiveScript mode for CodeMirror.\n#   https://github.com/gkz/prelude-ls\n\nexport objToFunc = objToFunc = (obj) ->\n  (key) -> obj[key]\n\nexport each = (f, xs) -->\n  if typeof! xs is \\Object\n    for , x of xs then f x\n  else\n    for x in xs then f x\n  xs\n\nexport map = (f, xs) -->\n  f = objToFunc f if typeof! f isnt \\Function\n  type = typeof! xs\n  if type is \\Object\n    {[key, f x] for key, x of xs}\n  else\n    result = [f x for x in xs]\n    if type is \\String then result * '' else result\n\nexport filter = (f, xs) -->\n  f = objToFunc f if typeof! f isnt \\Function\n  type = typeof! xs\n  if type is \\Object\n    {[key, x] for key, x of xs when f x}\n  else\n    result = [x for x in xs when f x]\n    if type is \\String then result * '' else result\n\nexport reject = (f, xs) -->\n  f = objToFunc f if typeof! f isnt \\Function\n  type = typeof! xs\n  if type is \\Object\n    {[key, x] for key, x of xs when not f x}\n  else\n    result = [x for x in xs when not f x]\n    if type is \\String then result * '' else result\n\nexport partition = (f, xs) -->\n  f = objToFunc f if typeof! f isnt \\Function\n  type = typeof! xs\n  if type is \\Object\n    passed = {}\n    failed = {}\n    for key, x of xs\n      (if f x then passed else failed)[key] = x\n  else\n    passed = []\n    failed = []\n    for x in xs\n      (if f x then passed else failed)push x\n    if type is \\String\n      passed *= ''\n      failed *= ''\n  [passed, failed]\n\nexport find = (f, xs) -->\n  f = objToFunc f if typeof! f isnt \\Function\n  if typeof! xs is \\Object\n    for , x of xs when f x then return x\n  else\n    for x in xs when f x then return x\n  void\n\nexport head = export first = (xs) ->\n  return void if not xs.length\n  xs.0\n\nexport tail = (xs) ->\n  return void if not xs.length\n  xs.slice 1\n\nexport last = (xs) ->\n  return void if not xs.length\n  xs[*-1]\n\nexport initial = (xs) ->\n  return void if not xs.length\n  xs.slice 0 xs.length - 1\n\nexport empty = (xs) ->\n  if typeof! xs is \\Object\n    for x of xs then return false\n    return yes\n  not xs.length\n\nexport values = (obj) ->\n  [x for , x of obj]\n\nexport keys = (obj) ->\n  [x for x of obj]\n\nexport len = (xs) ->\n  xs = values xs if typeof! xs is \\Object\n  xs.length\n\nexport cons = (x, xs) -->\n  if typeof! xs is \\String then x + xs else [x] ++ xs\n\nexport append = (xs, ys) -->\n  if typeof! ys is \\String then xs + ys else xs ++ ys\n\nexport join = (sep, xs) -->\n  xs = values xs if typeof! xs is \\Object\n  xs.join sep\n\nexport reverse = (xs) ->\n  if typeof! xs is \\String\n  then (xs / '')reverse! * ''\n  else xs.slice!reverse!\n\nexport fold = export foldl = (f, memo, xs) -->\n  if typeof! xs is \\Object\n    for , x of xs then memo = f memo, x\n  else\n    for x in xs then memo = f memo, x\n  memo\n\nexport fold1 = export foldl1 = (f, xs) --> fold f, xs.0, xs.slice 1\n\nexport foldr = (f, memo, xs) --> fold f, memo, xs.slice!reverse!\n\nexport foldr1 = (f, xs) -->\n  xs.=slice!reverse!\n  fold f, xs.0, xs.slice 1\n\nexport unfoldr = export unfold = (f, b) -->\n  if (f b)?\n    [that.0] ++ unfoldr f, that.1\n  else\n    []\n\nexport andList = (xs) ->\n  for x in xs when not x\n    return false\n  true\n\nexport orList = (xs) ->\n  for x in xs when x\n    return true\n  false\n\nexport any = (f, xs) -->\n  f = objToFunc f if typeof! f isnt \\Function\n  for x in xs when f x\n    return yes\n  no\n\nexport all = (f, xs) -->\n  f = objToFunc f if typeof! f isnt \\Function\n  for x in xs when not f x\n    return no\n  yes\n\nexport unique = (xs) ->\n  result = []\n  if typeof! xs is \\Object\n    for , x of xs when x not in result then result.push x\n  else\n    for x   in xs when x not in result then result.push x\n  if typeof! xs is \\String then result * '' else result\n\nexport sort = (xs) ->\n  xs.concat!sort (x, y) ->\n    | x > y =>  1\n    | x < y => -1\n    | _     =>  0\n\nexport sortBy = (f, xs) -->\n  return [] unless xs.length\n  xs.concat!sort f\n\nexport compare = (f, x, y) -->\n  | (f x) > (f y) =>  1\n  | (f x) < (f y) => -1\n  | otherwise     =>  0\n\nexport sum = (xs) ->\n  result = 0\n  if typeof! xs is \\Object\n    for , x of xs then result += x\n  else\n    for x   in xs then result += x\n  result\n\nexport product = (xs) ->\n  result = 1\n  if typeof! xs is \\Object\n    for , x of xs then result *= x\n  else\n    for x   in xs then result *= x\n  result\n\nexport mean = export average = (xs) -> (sum xs) / len xs\n\nexport concat = (xss) -> fold append, [], xss\n\nexport concatMap = (f, xs) --> fold ((memo, x) -> append memo, f x), [], xs\n\nexport listToObj = (xs) ->\n  {[x.0, x.1] for x in xs}\n\nexport maximum = (xs) -> fold1 (>?), xs\n\nexport minimum = (xs) -> fold1 (<?), xs\n\nexport scan = export scanl = (f, memo, xs) -->\n  last = memo\n  if typeof! xs is \\Object\n  then [memo] ++ [last = f last, x for , x of xs]\n  else [memo] ++ [last = f last, x for x in xs]\n\nexport scan1 = export scanl1 = (f, xs) --> scan f, xs.0, xs.slice 1\n\nexport scanr = (f, memo, xs) -->\n  xs.=slice!reverse!\n  scan f, memo, xs .reverse!\n\nexport scanr1 = (f, xs) -->\n  xs.=slice!reverse!\n  scan f, xs.0, xs.slice 1 .reverse!\n\nexport replicate = (n, x) -->\n  result = []\n  i = 0\n  while i < n, ++i then result.push x\n  result\n\nexport take = (n, xs) -->\n  | n <= 0\n    if typeof! xs is \\String then '' else []\n  | not xs.length => xs\n  | otherwise     => xs.slice 0, n\n\nexport drop = (n, xs) -->\n  | n <= 0        => xs\n  | not xs.length => xs\n  | otherwise     => xs.slice n\n\nexport splitAt = (n, xs) --> [(take n, xs), (drop n, xs)]\n\nexport takeWhile = (p, xs) -->\n  return xs if not xs.length\n  p = objToFunc p if typeof! p isnt \\Function\n  result = []\n  for x in xs\n    break if not p x\n    result.push x\n  if typeof! xs is \\String then result * '' else result\n\nexport dropWhile = (p, xs) -->\n  return xs if not xs.length\n  p = objToFunc p if typeof! p isnt \\Function\n  i = 0\n  for x in xs\n    break if not p x\n    ++i\n  drop i, xs\n\nexport span = (p, xs) --> [(takeWhile p, xs), (dropWhile p, xs)]\n\nexport breakIt = (p, xs) --> span (not) << p, xs\n\nexport zip = (xs, ys) -->\n  result = []\n  for zs, i in [xs, ys]\n    for z, j in zs\n      result.push [] if i is 0\n      result[j]?push z\n  result\n\nexport zipWith = (f,xs, ys) -->\n  f = objToFunc f if typeof! f isnt \\Function\n  if not xs.length or not ys.length\n    []\n  else\n    [f.apply this, zs for zs in zip.call this, xs, ys]\n\nexport zipAll = (...xss) ->\n  result = []\n  for xs, i in xss\n    for x, j in xs\n      result.push [] if i is 0\n      result[j]?push x\n  result\n\nexport zipAllWith = (f, ...xss) ->\n  f = objToFunc f if typeof! f isnt \\Function\n  if not xss.0.length or not xss.1.length\n    []\n  else\n    [f.apply this, xs for xs in zipAll.apply this, xss]\n\nexport compose = (...funcs) ->\n  ->\n    args = arguments\n    for f in funcs\n      args = [f.apply this, args]\n    args.0\n\nexport curry = (f) ->\n  curry$ f # using util method curry$ from livescript\n\nexport id = (x) -> x\n\nexport flip = (f, x, y) --> f y, x\n\nexport fix = (f) ->\n  ( (g, x) -> -> f(g g) ...arguments ) do\n    (g, x) -> -> f(g g) ...arguments\n\nexport lines = (str) ->\n  return [] if not str.length\n  str / \\\\n\n\nexport unlines = (strs) -> strs * \\\\n\n\nexport words = (str) ->\n  return [] if not str.length\n  str / /[ ]+/\n\nexport unwords = (strs) -> strs * ' '\n\nexport max = (>?)\n\nexport min = (<?)\n\nexport negate = (x) -> -x\n\nexport abs = Math.abs\n\nexport signum = (x) ->\n  | x < 0     => -1\n  | x > 0     =>  1\n  | otherwise =>  0\n\nexport quot = (x, y) --> ~~(x / y)\n\nexport rem = (%)\n\nexport div = (x, y) --> Math.floor x / y\n\nexport mod = (%%)\n\nexport recip = (1 /)\n\nexport pi = Math.PI\n\nexport tau = pi * 2\n\nexport exp = Math.exp\n\nexport sqrt = Math.sqrt\n\n# changed from log as log is a\n# common function for logging things\nexport ln = Math.log\n\nexport pow = (^)\n\nexport sin = Math.sin\n\nexport tan = Math.tan\n\nexport cos = Math.cos\n\nexport asin = Math.asin\n\nexport acos = Math.acos\n\nexport atan = Math.atan\n\nexport atan2 = (x, y) --> Math.atan2 x, y\n\n# sinh\n# tanh\n# cosh\n# asinh\n# atanh\n# acosh\n\nexport truncate = (x) -> ~~x\n\nexport round = Math.round\n\nexport ceiling = Math.ceil\n\nexport floor = Math.floor\n\nexport isItNaN = (x) -> x isnt x\n\nexport even = (x) -> x % 2 == 0\n\nexport odd = (x) -> x % 2 != 0\n\nexport gcd = (x, y) -->\n  x = Math.abs x\n  y = Math.abs y\n  until y is 0\n    z = x % y\n    x = y\n    y = z\n  x\n\nexport lcm = (x, y) -->\n  Math.abs Math.floor (x / (gcd x, y) * y)\n\n# meta\nexport installPrelude = !(target) ->\n  unless target.prelude?isInstalled\n    target <<< out$ # using out$ generated by livescript\n    target <<< target.prelude.isInstalled = true\n\nexport prelude = out$\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        theme: \"solarized light\",\n        lineNumbers: true\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-livescript</code>.</p>\n\n    <p>The LiveScript mode was written by Kenneth Bentley.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/livescript/livescript.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/**\n * Link to the project's GitHub page:\n * https://github.com/duralog/CodeMirror\n */\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode('livescript', function(){\n    var tokenBase = function(stream, state) {\n      var next_rule = state.next || \"start\";\n      if (next_rule) {\n        state.next = state.next;\n        var nr = Rules[next_rule];\n        if (nr.splice) {\n          for (var i$ = 0; i$ < nr.length; ++i$) {\n            var r = nr[i$];\n            if (r.regex && stream.match(r.regex)) {\n              state.next = r.next || state.next;\n              return r.token;\n            }\n          }\n          stream.next();\n          return 'error';\n        }\n        if (stream.match(r = Rules[next_rule])) {\n          if (r.regex && stream.match(r.regex)) {\n            state.next = r.next;\n            return r.token;\n          } else {\n            stream.next();\n            return 'error';\n          }\n        }\n      }\n      stream.next();\n      return 'error';\n    };\n    var external = {\n      startState: function(){\n        return {\n          next: 'start',\n          lastToken: {style: null, indent: 0, content: \"\"}\n        };\n      },\n      token: function(stream, state){\n        while (stream.pos == stream.start)\n          var style = tokenBase(stream, state);\n        state.lastToken = {\n          style: style,\n          indent: stream.indentation(),\n          content: stream.current()\n        };\n        return style.replace(/\\./g, ' ');\n      },\n      indent: function(state){\n        var indentation = state.lastToken.indent;\n        if (state.lastToken.content.match(indenter)) {\n          indentation += 2;\n        }\n        return indentation;\n      }\n    };\n    return external;\n  });\n\n  var identifier = '(?![\\\\d\\\\s])[$\\\\w\\\\xAA-\\\\uFFDC](?:(?!\\\\s)[$\\\\w\\\\xAA-\\\\uFFDC]|-[A-Za-z])*';\n  var indenter = RegExp('(?:[({[=:]|[-~]>|\\\\b(?:e(?:lse|xport)|d(?:o|efault)|t(?:ry|hen)|finally|import(?:\\\\s*all)?|const|var|let|new|catch(?:\\\\s*' + identifier + ')?))\\\\s*$');\n  var keywordend = '(?![$\\\\w]|-[A-Za-z]|\\\\s*:(?![:=]))';\n  var stringfill = {\n    token: 'string',\n    regex: '.+'\n  };\n  var Rules = {\n    start: [\n      {\n        token: 'comment.doc',\n        regex: '/\\\\*',\n        next: 'comment'\n      }, {\n        token: 'comment',\n        regex: '#.*'\n      }, {\n        token: 'keyword',\n        regex: '(?:t(?:h(?:is|row|en)|ry|ypeof!?)|c(?:on(?:tinue|st)|a(?:se|tch)|lass)|i(?:n(?:stanceof)?|mp(?:ort(?:\\\\s+all)?|lements)|[fs])|d(?:e(?:fault|lete|bugger)|o)|f(?:or(?:\\\\s+own)?|inally|unction)|s(?:uper|witch)|e(?:lse|x(?:tends|port)|val)|a(?:nd|rguments)|n(?:ew|ot)|un(?:less|til)|w(?:hile|ith)|o[fr]|return|break|let|var|loop)' + keywordend\n      }, {\n        token: 'constant.language',\n        regex: '(?:true|false|yes|no|on|off|null|void|undefined)' + keywordend\n      }, {\n        token: 'invalid.illegal',\n        regex: '(?:p(?:ackage|r(?:ivate|otected)|ublic)|i(?:mplements|nterface)|enum|static|yield)' + keywordend\n      }, {\n        token: 'language.support.class',\n        regex: '(?:R(?:e(?:gExp|ferenceError)|angeError)|S(?:tring|yntaxError)|E(?:rror|valError)|Array|Boolean|Date|Function|Number|Object|TypeError|URIError)' + keywordend\n      }, {\n        token: 'language.support.function',\n        regex: '(?:is(?:NaN|Finite)|parse(?:Int|Float)|Math|JSON|(?:en|de)codeURI(?:Component)?)' + keywordend\n      }, {\n        token: 'variable.language',\n        regex: '(?:t(?:hat|il|o)|f(?:rom|allthrough)|it|by|e)' + keywordend\n      }, {\n        token: 'identifier',\n        regex: identifier + '\\\\s*:(?![:=])'\n      }, {\n        token: 'variable',\n        regex: identifier\n      }, {\n        token: 'keyword.operator',\n        regex: '(?:\\\\.{3}|\\\\s+\\\\?)'\n      }, {\n        token: 'keyword.variable',\n        regex: '(?:@+|::|\\\\.\\\\.)',\n        next: 'key'\n      }, {\n        token: 'keyword.operator',\n        regex: '\\\\.\\\\s*',\n        next: 'key'\n      }, {\n        token: 'string',\n        regex: '\\\\\\\\\\\\S[^\\\\s,;)}\\\\]]*'\n      }, {\n        token: 'string.doc',\n        regex: '\\'\\'\\'',\n        next: 'qdoc'\n      }, {\n        token: 'string.doc',\n        regex: '\"\"\"',\n        next: 'qqdoc'\n      }, {\n        token: 'string',\n        regex: '\\'',\n        next: 'qstring'\n      }, {\n        token: 'string',\n        regex: '\"',\n        next: 'qqstring'\n      }, {\n        token: 'string',\n        regex: '`',\n        next: 'js'\n      }, {\n        token: 'string',\n        regex: '<\\\\[',\n        next: 'words'\n      }, {\n        token: 'string.regex',\n        regex: '//',\n        next: 'heregex'\n      }, {\n        token: 'string.regex',\n        regex: '\\\\/(?:[^[\\\\/\\\\n\\\\\\\\]*(?:(?:\\\\\\\\.|\\\\[[^\\\\]\\\\n\\\\\\\\]*(?:\\\\\\\\.[^\\\\]\\\\n\\\\\\\\]*)*\\\\])[^[\\\\/\\\\n\\\\\\\\]*)*)\\\\/[gimy$]{0,4}',\n        next: 'key'\n      }, {\n        token: 'constant.numeric',\n        regex: '(?:0x[\\\\da-fA-F][\\\\da-fA-F_]*|(?:[2-9]|[12]\\\\d|3[0-6])r[\\\\da-zA-Z][\\\\da-zA-Z_]*|(?:\\\\d[\\\\d_]*(?:\\\\.\\\\d[\\\\d_]*)?|\\\\.\\\\d[\\\\d_]*)(?:e[+-]?\\\\d[\\\\d_]*)?[\\\\w$]*)'\n      }, {\n        token: 'lparen',\n        regex: '[({[]'\n      }, {\n        token: 'rparen',\n        regex: '[)}\\\\]]',\n        next: 'key'\n      }, {\n        token: 'keyword.operator',\n        regex: '\\\\S+'\n      }, {\n        token: 'text',\n        regex: '\\\\s+'\n      }\n    ],\n    heregex: [\n      {\n        token: 'string.regex',\n        regex: '.*?//[gimy$?]{0,4}',\n        next: 'start'\n      }, {\n        token: 'string.regex',\n        regex: '\\\\s*#{'\n      }, {\n        token: 'comment.regex',\n        regex: '\\\\s+(?:#.*)?'\n      }, {\n        token: 'string.regex',\n        regex: '\\\\S+'\n      }\n    ],\n    key: [\n      {\n        token: 'keyword.operator',\n        regex: '[.?@!]+'\n      }, {\n        token: 'identifier',\n        regex: identifier,\n        next: 'start'\n      }, {\n        token: 'text',\n        regex: '',\n        next: 'start'\n      }\n    ],\n    comment: [\n      {\n        token: 'comment.doc',\n        regex: '.*?\\\\*/',\n        next: 'start'\n      }, {\n        token: 'comment.doc',\n        regex: '.+'\n      }\n    ],\n    qdoc: [\n      {\n        token: 'string',\n        regex: \".*?'''\",\n        next: 'key'\n      }, stringfill\n    ],\n    qqdoc: [\n      {\n        token: 'string',\n        regex: '.*?\"\"\"',\n        next: 'key'\n      }, stringfill\n    ],\n    qstring: [\n      {\n        token: 'string',\n        regex: '[^\\\\\\\\\\']*(?:\\\\\\\\.[^\\\\\\\\\\']*)*\\'',\n        next: 'key'\n      }, stringfill\n    ],\n    qqstring: [\n      {\n        token: 'string',\n        regex: '[^\\\\\\\\\"]*(?:\\\\\\\\.[^\\\\\\\\\"]*)*\"',\n        next: 'key'\n      }, stringfill\n    ],\n    js: [\n      {\n        token: 'string',\n        regex: '[^\\\\\\\\`]*(?:\\\\\\\\.[^\\\\\\\\`]*)*`',\n        next: 'key'\n      }, stringfill\n    ],\n    words: [\n      {\n        token: 'string',\n        regex: '.*?\\\\]>',\n        next: 'key'\n      }, stringfill\n    ]\n  };\n  for (var idx in Rules) {\n    var r = Rules[idx];\n    if (r.splice) {\n      for (var i = 0, len = r.length; i < len; ++i) {\n        var rr = r[i];\n        if (typeof rr.regex === 'string') {\n          Rules[idx][i].regex = new RegExp('^' + rr.regex);\n        }\n      }\n    } else if (typeof rr.regex === 'string') {\n      Rules[idx].regex = new RegExp('^' + r.regex);\n    }\n  }\n\n  CodeMirror.defineMIME('text/x-livescript', 'livescript');\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/lua/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Lua mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/neat.css\">\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"lua.js\"></script>\n<style>.CodeMirror {border: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Lua</a>\n  </ul>\n</div>\n\n<article>\n<h2>Lua mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n--[[\nexample useless code to show lua syntax highlighting\nthis is multiline comment\n]]\n\nfunction blahblahblah(x)\n\n  local table = {\n    \"asd\" = 123,\n    \"x\" = 0.34,  \n  }\n  if x ~= 3 then\n    print( x )\n  elseif x == \"string\"\n    my_custom_function( 0x34 )\n  else\n    unknown_function( \"some string\" )\n  end\n\n  --single line comment\n  \nend\n\nfunction blablabla3()\n\n  for k,v in ipairs( table ) do\n    --abcde..\n    y=[=[\n  x=[[\n      x is a multi line string\n   ]]\n  but its definition is iside a highest level string!\n  ]=]\n    print(\" \\\"\\\" \")\n\n    s = math.sin( x )\n  end\n\nend\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        matchBrackets: true,\n        theme: \"neat\"\n      });\n    </script>\n\n    <p>Loosely based on Franciszek\n    Wawrzak's <a href=\"https://codemirror.net/5/1/contrib/lua\">CodeMirror\n    1 mode</a>. One configuration parameter is\n    supported, <code>specials</code>, to which you can provide an\n    array of strings to have those identifiers highlighted with\n    the <code>lua-special</code> style.</p>\n    <p><strong>MIME types defined:</strong> <code>text/x-lua</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/lua/lua.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// LUA mode. Ported to CodeMirror 2 from Franciszek Wawrzak's\n// CodeMirror 1 mode.\n// highlights keywords, strings, comments (no leveling supported! (\"[==[\")), tokens, basic indenting\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"lua\", function(config, parserConfig) {\n  var indentUnit = config.indentUnit;\n\n  function prefixRE(words) {\n    return new RegExp(\"^(?:\" + words.join(\"|\") + \")\", \"i\");\n  }\n  function wordRE(words) {\n    return new RegExp(\"^(?:\" + words.join(\"|\") + \")$\", \"i\");\n  }\n  var specials = wordRE(parserConfig.specials || []);\n\n  // long list of standard functions from lua manual\n  var builtins = wordRE([\n    \"_G\",\"_VERSION\",\"assert\",\"collectgarbage\",\"dofile\",\"error\",\"getfenv\",\"getmetatable\",\"ipairs\",\"load\",\n    \"loadfile\",\"loadstring\",\"module\",\"next\",\"pairs\",\"pcall\",\"print\",\"rawequal\",\"rawget\",\"rawset\",\"require\",\n    \"select\",\"setfenv\",\"setmetatable\",\"tonumber\",\"tostring\",\"type\",\"unpack\",\"xpcall\",\n\n    \"coroutine.create\",\"coroutine.resume\",\"coroutine.running\",\"coroutine.status\",\"coroutine.wrap\",\"coroutine.yield\",\n\n    \"debug.debug\",\"debug.getfenv\",\"debug.gethook\",\"debug.getinfo\",\"debug.getlocal\",\"debug.getmetatable\",\n    \"debug.getregistry\",\"debug.getupvalue\",\"debug.setfenv\",\"debug.sethook\",\"debug.setlocal\",\"debug.setmetatable\",\n    \"debug.setupvalue\",\"debug.traceback\",\n\n    \"close\",\"flush\",\"lines\",\"read\",\"seek\",\"setvbuf\",\"write\",\n\n    \"io.close\",\"io.flush\",\"io.input\",\"io.lines\",\"io.open\",\"io.output\",\"io.popen\",\"io.read\",\"io.stderr\",\"io.stdin\",\n    \"io.stdout\",\"io.tmpfile\",\"io.type\",\"io.write\",\n\n    \"math.abs\",\"math.acos\",\"math.asin\",\"math.atan\",\"math.atan2\",\"math.ceil\",\"math.cos\",\"math.cosh\",\"math.deg\",\n    \"math.exp\",\"math.floor\",\"math.fmod\",\"math.frexp\",\"math.huge\",\"math.ldexp\",\"math.log\",\"math.log10\",\"math.max\",\n    \"math.min\",\"math.modf\",\"math.pi\",\"math.pow\",\"math.rad\",\"math.random\",\"math.randomseed\",\"math.sin\",\"math.sinh\",\n    \"math.sqrt\",\"math.tan\",\"math.tanh\",\n\n    \"os.clock\",\"os.date\",\"os.difftime\",\"os.execute\",\"os.exit\",\"os.getenv\",\"os.remove\",\"os.rename\",\"os.setlocale\",\n    \"os.time\",\"os.tmpname\",\n\n    \"package.cpath\",\"package.loaded\",\"package.loaders\",\"package.loadlib\",\"package.path\",\"package.preload\",\n    \"package.seeall\",\n\n    \"string.byte\",\"string.char\",\"string.dump\",\"string.find\",\"string.format\",\"string.gmatch\",\"string.gsub\",\n    \"string.len\",\"string.lower\",\"string.match\",\"string.rep\",\"string.reverse\",\"string.sub\",\"string.upper\",\n\n    \"table.concat\",\"table.insert\",\"table.maxn\",\"table.remove\",\"table.sort\"\n  ]);\n  var keywords = wordRE([\"and\",\"break\",\"elseif\",\"false\",\"nil\",\"not\",\"or\",\"return\",\n                         \"true\",\"function\", \"end\", \"if\", \"then\", \"else\", \"do\",\n                         \"while\", \"repeat\", \"until\", \"for\", \"in\", \"local\" ]);\n\n  var indentTokens = wordRE([\"function\", \"if\",\"repeat\",\"do\", \"\\\\(\", \"{\"]);\n  var dedentTokens = wordRE([\"end\", \"until\", \"\\\\)\", \"}\"]);\n  var dedentPartial = prefixRE([\"end\", \"until\", \"\\\\)\", \"}\", \"else\", \"elseif\"]);\n\n  function readBracket(stream) {\n    var level = 0;\n    while (stream.eat(\"=\")) ++level;\n    stream.eat(\"[\");\n    return level;\n  }\n\n  function normal(stream, state) {\n    var ch = stream.next();\n    if (ch == \"-\" && stream.eat(\"-\")) {\n      if (stream.eat(\"[\") && stream.eat(\"[\"))\n        return (state.cur = bracketed(readBracket(stream), \"comment\"))(stream, state);\n      stream.skipToEnd();\n      return \"comment\";\n    }\n    if (ch == \"\\\"\" || ch == \"'\")\n      return (state.cur = string(ch))(stream, state);\n    if (ch == \"[\" && /[\\[=]/.test(stream.peek()))\n      return (state.cur = bracketed(readBracket(stream), \"string\"))(stream, state);\n    if (/\\d/.test(ch)) {\n      stream.eatWhile(/[\\w.%]/);\n      return \"number\";\n    }\n    if (/[\\w_]/.test(ch)) {\n      stream.eatWhile(/[\\w\\\\\\-_.]/);\n      return \"variable\";\n    }\n    return null;\n  }\n\n  function bracketed(level, style) {\n    return function(stream, state) {\n      var curlev = null, ch;\n      while ((ch = stream.next()) != null) {\n        if (curlev == null) {if (ch == \"]\") curlev = 0;}\n        else if (ch == \"=\") ++curlev;\n        else if (ch == \"]\" && curlev == level) { state.cur = normal; break; }\n        else curlev = null;\n      }\n      return style;\n    };\n  }\n\n  function string(quote) {\n    return function(stream, state) {\n      var escaped = false, ch;\n      while ((ch = stream.next()) != null) {\n        if (ch == quote && !escaped) break;\n        escaped = !escaped && ch == \"\\\\\";\n      }\n      if (!escaped) state.cur = normal;\n      return \"string\";\n    };\n  }\n\n  return {\n    startState: function(basecol) {\n      return {basecol: basecol || 0, indentDepth: 0, cur: normal};\n    },\n\n    token: function(stream, state) {\n      if (stream.eatSpace()) return null;\n      var style = state.cur(stream, state);\n      var word = stream.current();\n      if (style == \"variable\") {\n        if (keywords.test(word)) style = \"keyword\";\n        else if (builtins.test(word)) style = \"builtin\";\n        else if (specials.test(word)) style = \"variable-2\";\n      }\n      if ((style != \"comment\") && (style != \"string\")){\n        if (indentTokens.test(word)) ++state.indentDepth;\n        else if (dedentTokens.test(word)) --state.indentDepth;\n      }\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      var closing = dedentPartial.test(textAfter);\n      return state.basecol + indentUnit * (state.indentDepth - (closing ? 1 : 0));\n    },\n\n    electricInput: /^\\s*(?:end|until|else|\\)|\\})$/,\n    lineComment: \"--\",\n    blockCommentStart: \"--[[\",\n    blockCommentEnd: \"]]\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-lua\", \"lua\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/markdown/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Markdown mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/continuelist.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../javascript/javascript.js\"></script>\n<script src=\"markdown.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n      .cm-s-default .cm-trailing-space-a:before,\n      .cm-s-default .cm-trailing-space-b:before {position: absolute; content: \"\\00B7\"; color: #777;}\n      .cm-s-default .cm-trailing-space-new-line:before {position: absolute; content: \"\\21B5\"; color: #777;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Markdown</a>\n  </ul>\n</div>\n\n<article>\n<h2>Markdown mode</h2>\n<form><textarea id=\"code\" name=\"code\">\nMarkdown: Basics\n================\n\n&lt;ul id=\"ProjectSubmenu\"&gt;\n    &lt;li&gt;&lt;a href=\"/projects/markdown/\" title=\"Markdown Project Page\"&gt;Main&lt;/a&gt;&lt;/li&gt;\n    &lt;li&gt;&lt;a class=\"selected\" title=\"Markdown Basics\"&gt;Basics&lt;/a&gt;&lt;/li&gt;\n    &lt;li&gt;&lt;a href=\"/projects/markdown/syntax\" title=\"Markdown Syntax Documentation\"&gt;Syntax&lt;/a&gt;&lt;/li&gt;\n    &lt;li&gt;&lt;a href=\"/projects/markdown/license\" title=\"Pricing and License Information\"&gt;License&lt;/a&gt;&lt;/li&gt;\n    &lt;li&gt;&lt;a href=\"/projects/markdown/dingus\" title=\"Online Markdown Web Form\"&gt;Dingus&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n\n\nGetting the Gist of Markdown's Formatting Syntax\n------------------------------------------------\n\nThis page offers a brief overview of what it's like to use Markdown.\nThe [syntax page] [s] provides complete, detailed documentation for\nevery feature, but Markdown should be very easy to pick up simply by\nlooking at a few examples of it in action. The examples on this page\nare written in a before/after style, showing example syntax and the\nHTML output produced by Markdown.\n\nIt's also helpful to simply try Markdown out; the [Dingus] [d] is a\nweb application that allows you type your own Markdown-formatted text\nand translate it to XHTML.\n\n**Note:** This document is itself written using Markdown; you\ncan [see the source for it by adding '.text' to the URL] [src].\n\n  [s]: /projects/markdown/syntax  \"Markdown Syntax\"\n  [d]: /projects/markdown/dingus  \"Markdown Dingus\"\n  [src]: /projects/markdown/basics.text\n\n\n## Paragraphs, Headers, Blockquotes ##\n\nA paragraph is simply one or more consecutive lines of text, separated\nby one or more blank lines. (A blank line is any line that looks like\na blank line -- a line containing nothing but spaces or tabs is\nconsidered blank.) Normal paragraphs should not be indented with\nspaces or tabs.\n\nMarkdown offers two styles of headers: *Setext* and *atx*.\nSetext-style headers for `&lt;h1&gt;` and `&lt;h2&gt;` are created by\n\"underlining\" with equal signs (`=`) and hyphens (`-`), respectively.\nTo create an atx-style header, you put 1-6 hash marks (`#`) at the\nbeginning of the line -- the number of hashes equals the resulting\nHTML header level.\n\nBlockquotes are indicated using email-style '`&gt;`' angle brackets.\n\nMarkdown:\n\n    A First Level Header\n    ====================\n\n    A Second Level Header\n    ---------------------\n\n    Now is the time for all good men to come to\n    the aid of their country. This is just a\n    regular paragraph.\n\n    The quick brown fox jumped over the lazy\n    dog's back.\n\n    ### Header 3\n\n    &gt; This is a blockquote.\n    &gt;\n    &gt; This is the second paragraph in the blockquote.\n    &gt;\n    &gt; ## This is an H2 in a blockquote\n\n\nOutput:\n\n    &lt;h1&gt;A First Level Header&lt;/h1&gt;\n\n    &lt;h2&gt;A Second Level Header&lt;/h2&gt;\n\n    &lt;p&gt;Now is the time for all good men to come to\n    the aid of their country. This is just a\n    regular paragraph.&lt;/p&gt;\n\n    &lt;p&gt;The quick brown fox jumped over the lazy\n    dog's back.&lt;/p&gt;\n\n    &lt;h3&gt;Header 3&lt;/h3&gt;\n\n    &lt;blockquote&gt;\n        &lt;p&gt;This is a blockquote.&lt;/p&gt;\n\n        &lt;p&gt;This is the second paragraph in the blockquote.&lt;/p&gt;\n\n        &lt;h2&gt;This is an H2 in a blockquote&lt;/h2&gt;\n    &lt;/blockquote&gt;\n\n\n\n### Phrase Emphasis ###\n\nMarkdown uses asterisks and underscores to indicate spans of emphasis.\n\nMarkdown:\n\n    Some of these words *are emphasized*.\n    Some of these words _are emphasized also_.\n\n    Use two asterisks for **strong emphasis**.\n    Or, if you prefer, __use two underscores instead__.\n\nOutput:\n\n    &lt;p&gt;Some of these words &lt;em&gt;are emphasized&lt;/em&gt;.\n    Some of these words &lt;em&gt;are emphasized also&lt;/em&gt;.&lt;/p&gt;\n\n    &lt;p&gt;Use two asterisks for &lt;strong&gt;strong emphasis&lt;/strong&gt;.\n    Or, if you prefer, &lt;strong&gt;use two underscores instead&lt;/strong&gt;.&lt;/p&gt;\n\n\n\n## Lists ##\n\nUnordered (bulleted) lists use asterisks, pluses, and hyphens (`*`,\n`+`, and `-`) as list markers. These three markers are\ninterchangeable; this:\n\n    *   Candy.\n    *   Gum.\n    *   Booze.\n\nthis:\n\n    +   Candy.\n    +   Gum.\n    +   Booze.\n\nand this:\n\n    -   Candy.\n    -   Gum.\n    -   Booze.\n\nall produce the same output:\n\n    &lt;ul&gt;\n    &lt;li&gt;Candy.&lt;/li&gt;\n    &lt;li&gt;Gum.&lt;/li&gt;\n    &lt;li&gt;Booze.&lt;/li&gt;\n    &lt;/ul&gt;\n\nOrdered (numbered) lists use regular numbers, followed by periods, as\nlist markers:\n\n    1.  Red\n    2.  Green\n    3.  Blue\n\nOutput:\n\n    &lt;ol&gt;\n    &lt;li&gt;Red&lt;/li&gt;\n    &lt;li&gt;Green&lt;/li&gt;\n    &lt;li&gt;Blue&lt;/li&gt;\n    &lt;/ol&gt;\n\nIf you put blank lines between items, you'll get `&lt;p&gt;` tags for the\nlist item text. You can create multi-paragraph list items by indenting\nthe paragraphs by 4 spaces or 1 tab:\n\n    *   A list item.\n\n        With multiple paragraphs.\n\n    *   Another item in the list.\n\nOutput:\n\n    &lt;ul&gt;\n    &lt;li&gt;&lt;p&gt;A list item.&lt;/p&gt;\n    &lt;p&gt;With multiple paragraphs.&lt;/p&gt;&lt;/li&gt;\n    &lt;li&gt;&lt;p&gt;Another item in the list.&lt;/p&gt;&lt;/li&gt;\n    &lt;/ul&gt;\n\n\n\n### Links ###\n\nMarkdown supports two styles for creating links: *inline* and\n*reference*. With both styles, you use square brackets to delimit the\ntext you want to turn into a link.\n\nInline-style links use parentheses immediately after the link text.\nFor example:\n\n    This is an [example link](http://example.com/).\n\nOutput:\n\n    &lt;p&gt;This is an &lt;a href=\"http://example.com/\"&gt;\n    example link&lt;/a&gt;.&lt;/p&gt;\n\nOptionally, you may include a title attribute in the parentheses:\n\n    This is an [example link](http://example.com/ \"With a Title\").\n\nOutput:\n\n    &lt;p&gt;This is an &lt;a href=\"http://example.com/\" title=\"With a Title\"&gt;\n    example link&lt;/a&gt;.&lt;/p&gt;\n\nReference-style links allow you to refer to your links by names, which\nyou define elsewhere in your document:\n\n    I get 10 times more traffic from [Google][1] than from\n    [Yahoo][2] or [MSN][3].\n\n    [1]: http://google.com/        \"Google\"\n    [2]: http://search.yahoo.com/  \"Yahoo Search\"\n    [3]: http://search.msn.com/    \"MSN Search\"\n\nOutput:\n\n    &lt;p&gt;I get 10 times more traffic from &lt;a href=\"http://google.com/\"\n    title=\"Google\"&gt;Google&lt;/a&gt; than from &lt;a href=\"http://search.yahoo.com/\"\n    title=\"Yahoo Search\"&gt;Yahoo&lt;/a&gt; or &lt;a href=\"http://search.msn.com/\"\n    title=\"MSN Search\"&gt;MSN&lt;/a&gt;.&lt;/p&gt;\n\nThe title attribute is optional. Link names may contain letters,\nnumbers and spaces, but are *not* case sensitive:\n\n    I start my morning with a cup of coffee and\n    [The New York Times][NY Times].\n\n    [ny times]: http://www.nytimes.com/\n\nOutput:\n\n    &lt;p&gt;I start my morning with a cup of coffee and\n    &lt;a href=\"http://www.nytimes.com/\"&gt;The New York Times&lt;/a&gt;.&lt;/p&gt;\n\n\n### Images ###\n\nImage syntax is very much like link syntax.\n\nInline (titles are optional):\n\n    ![alt text](/path/to/img.jpg \"Title\")\n\nReference-style:\n\n    ![alt text][id]\n\n    [id]: /path/to/img.jpg \"Title\"\n\nBoth of the above examples produce the same output:\n\n    &lt;img src=\"/path/to/img.jpg\" alt=\"alt text\" title=\"Title\" /&gt;\n\n\n\n### Code ###\n\nIn a regular paragraph, you can create code span by wrapping text in\nbacktick quotes. Any ampersands (`&amp;`) and angle brackets (`&lt;` or\n`&gt;`) will automatically be translated into HTML entities. This makes\nit easy to use Markdown to write about HTML example code:\n\n    I strongly recommend against using any `&lt;blink&gt;` tags.\n\n    I wish SmartyPants used named entities like `&amp;mdash;`\n    instead of decimal-encoded entities like `&amp;#8212;`.\n\nOutput:\n\n    &lt;p&gt;I strongly recommend against using any\n    &lt;code&gt;&amp;lt;blink&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;\n\n    &lt;p&gt;I wish SmartyPants used named entities like\n    &lt;code&gt;&amp;amp;mdash;&lt;/code&gt; instead of decimal-encoded\n    entities like &lt;code&gt;&amp;amp;#8212;&lt;/code&gt;.&lt;/p&gt;\n\n\nTo specify an entire block of pre-formatted code, indent every line of\nthe block by 4 spaces or 1 tab. Just like with code spans, `&amp;`, `&lt;`,\nand `&gt;` characters will be escaped automatically.\n\nMarkdown:\n\n    If you want your page to validate under XHTML 1.0 Strict,\n    you've got to put paragraph tags in your blockquotes:\n\n        &lt;blockquote&gt;\n            &lt;p&gt;For example.&lt;/p&gt;\n        &lt;/blockquote&gt;\n\nOutput:\n\n    &lt;p&gt;If you want your page to validate under XHTML 1.0 Strict,\n    you've got to put paragraph tags in your blockquotes:&lt;/p&gt;\n\n    &lt;pre&gt;&lt;code&gt;&amp;lt;blockquote&amp;gt;\n        &amp;lt;p&amp;gt;For example.&amp;lt;/p&amp;gt;\n    &amp;lt;/blockquote&amp;gt;\n    &lt;/code&gt;&lt;/pre&gt;\n\n## Fenced code blocks (and syntax highlighting)\n\n```javascript\nfor (var i = 0; i < items.length; i++) {\n    console.log(items[i], i); // log them\n}\n```\n\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: 'markdown',\n        lineNumbers: true,\n        theme: \"default\",\n        extraKeys: {\"Enter\": \"newlineAndIndentContinueMarkdownList\"}\n      });\n    </script>\n\n    <p>If you also want support <code>strikethrough</code>, <code>emoji</code> and few other goodies, check out <a href=\"../gfm/index.html\">GitHub-Flavored Markdown mode</a>.</p>\n\n    <p>Optionally depends on other modes for properly highlighted code blocks,\n      and XML mode for properly highlighted inline XML blocks.</p>\n\n    <p>Markdown mode supports these options:</p>\n    <ul>\n      <li>\n        <d1>\n          <dt><code>highlightFormatting: boolean</code></dt>\n          <dd>Whether to separately highlight markdown meta characters (<code>*[]()</code>etc.) (default: <code>false</code>).</dd>\n        </d1>\n      </li>\n      <li>\n        <d1>\n          <dt><code>maxBlockquoteDepth: boolean</code></dt>\n          <dd>Maximum allowed blockquote nesting (default: <code>0</code> - infinite nesting).</dd>\n        </d1>\n      </li>\n      <li>\n        <d1>\n          <dt><code>xml: boolean</code></dt>\n          <dd>Whether to highlight inline XML (default: <code>true</code>).</dd>\n        </d1>\n      </li>\n      <li>\n        <d1>\n          <dt><code>fencedCodeBlockHighlighting: boolean</code></dt>\n          <dd>Whether to syntax-highlight fenced code blocks, if given mode is included, or fencedCodeBlockDefaultMode is set (default: <code>true</code>).</dd>\n        </d1>\n      </li>\n      <li>\n        <d1>\n          <dt><code>fencedCodeBlockDefaultMode: string</code></dt>\n          <dd>Mode to use for fencedCodeBlockHighlighting, if given mode is not included.</dd>\n        </d1>\n      </li>\n      <li>\n        <d1>\n          <dt><code>tokenTypeOverrides: Object</code></dt>\n          <dd>When you want to override default token type names (e.g. <code>{code: \"code\"}</code>).</dd>\n        </d1>\n      </li>\n      <li>\n        <d1>\n          <dt><code>allowAtxHeaderWithoutSpace: boolean</code></dt>\n          <dd>Allow lazy headers without whitespace between hashtag and text (default: <code>false</code>).</dd>\n        </d1>\n      </li>\n    </ul>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-markdown</code>.</p>\n\n    <p><strong>Parsing/Highlighting Tests:</strong> <a href=\"../../test/index.html#markdown_*\">normal</a>,  <a href=\"../../test/index.html#verbose,markdown_*\">verbose</a>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/markdown/markdown.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../xml/xml\"), require(\"../meta\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../xml/xml\", \"../meta\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"markdown\", function(cmCfg, modeCfg) {\n\n  var htmlMode = CodeMirror.getMode(cmCfg, \"text/html\");\n  var htmlModeMissing = htmlMode.name == \"null\"\n\n  function getMode(name) {\n    if (CodeMirror.findModeByName) {\n      var found = CodeMirror.findModeByName(name);\n      if (found) name = found.mime || found.mimes[0];\n    }\n    var mode = CodeMirror.getMode(cmCfg, name);\n    return mode.name == \"null\" ? null : mode;\n  }\n\n  // Should characters that affect highlighting be highlighted separate?\n  // Does not include characters that will be output (such as `1.` and `-` for lists)\n  if (modeCfg.highlightFormatting === undefined)\n    modeCfg.highlightFormatting = false;\n\n  // Maximum number of nested blockquotes. Set to 0 for infinite nesting.\n  // Excess `>` will emit `error` token.\n  if (modeCfg.maxBlockquoteDepth === undefined)\n    modeCfg.maxBlockquoteDepth = 0;\n\n  // Turn on task lists? (\"- [ ] \" and \"- [x] \")\n  if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;\n\n  // Turn on strikethrough syntax\n  if (modeCfg.strikethrough === undefined)\n    modeCfg.strikethrough = false;\n\n  if (modeCfg.emoji === undefined)\n    modeCfg.emoji = false;\n\n  if (modeCfg.fencedCodeBlockHighlighting === undefined)\n    modeCfg.fencedCodeBlockHighlighting = true;\n\n  if (modeCfg.fencedCodeBlockDefaultMode === undefined)\n    modeCfg.fencedCodeBlockDefaultMode = 'text/plain';\n\n  if (modeCfg.xml === undefined)\n    modeCfg.xml = true;\n\n  // Allow token types to be overridden by user-provided token types.\n  if (modeCfg.tokenTypeOverrides === undefined)\n    modeCfg.tokenTypeOverrides = {};\n\n  var tokenTypes = {\n    header: \"header\",\n    code: \"comment\",\n    quote: \"quote\",\n    list1: \"variable-2\",\n    list2: \"variable-3\",\n    list3: \"keyword\",\n    hr: \"hr\",\n    image: \"image\",\n    imageAltText: \"image-alt-text\",\n    imageMarker: \"image-marker\",\n    formatting: \"formatting\",\n    linkInline: \"link\",\n    linkEmail: \"link\",\n    linkText: \"link\",\n    linkHref: \"string\",\n    em: \"em\",\n    strong: \"strong\",\n    strikethrough: \"strikethrough\",\n    emoji: \"builtin\"\n  };\n\n  for (var tokenType in tokenTypes) {\n    if (tokenTypes.hasOwnProperty(tokenType) && modeCfg.tokenTypeOverrides[tokenType]) {\n      tokenTypes[tokenType] = modeCfg.tokenTypeOverrides[tokenType];\n    }\n  }\n\n  var hrRE = /^([*\\-_])(?:\\s*\\1){2,}\\s*$/\n  ,   listRE = /^(?:[*\\-+]|^[0-9]+([.)]))\\s+/\n  ,   taskListRE = /^\\[(x| )\\](?=\\s)/i // Must follow listRE\n  ,   atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/\n  ,   setextHeaderRE = /^ {0,3}(?:\\={1,}|-{2,})\\s*$/\n  ,   textRE = /^[^#!\\[\\]*_\\\\<>` \"'(~:]+/\n  ,   fencedCodeRE = /^(~~~+|```+)[ \\t]*([\\w\\/+#-]*)[^\\n`]*$/\n  ,   linkDefRE = /^\\s*\\[[^\\]]+?\\]:.*$/ // naive link-definition\n  ,   punctuation = /[!\"#$%&'()*+,\\-.\\/:;<=>?@\\[\\\\\\]^_`{|}~\\xA1\\xA7\\xAB\\xB6\\xB7\\xBB\\xBF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2308-\\u230B\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E42\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA8FC\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]|\\uD800[\\uDD00-\\uDD02\\uDF9F\\uDFD0]|\\uD801\\uDD6F|\\uD802[\\uDC57\\uDD1F\\uDD3F\\uDE50-\\uDE58\\uDE7F\\uDEF0-\\uDEF6\\uDF39-\\uDF3F\\uDF99-\\uDF9C]|\\uD804[\\uDC47-\\uDC4D\\uDCBB\\uDCBC\\uDCBE-\\uDCC1\\uDD40-\\uDD43\\uDD74\\uDD75\\uDDC5-\\uDDC9\\uDDCD\\uDDDB\\uDDDD-\\uDDDF\\uDE38-\\uDE3D\\uDEA9]|\\uD805[\\uDCC6\\uDDC1-\\uDDD7\\uDE41-\\uDE43\\uDF3C-\\uDF3E]|\\uD809[\\uDC70-\\uDC74]|\\uD81A[\\uDE6E\\uDE6F\\uDEF5\\uDF37-\\uDF3B\\uDF44]|\\uD82F\\uDC9F|\\uD836[\\uDE87-\\uDE8B]/\n  ,   expandedTab = \"    \" // CommonMark specifies tab as 4 spaces\n\n  function switchInline(stream, state, f) {\n    state.f = state.inline = f;\n    return f(stream, state);\n  }\n\n  function switchBlock(stream, state, f) {\n    state.f = state.block = f;\n    return f(stream, state);\n  }\n\n  function lineIsEmpty(line) {\n    return !line || !/\\S/.test(line.string)\n  }\n\n  // Blocks\n\n  function blankLine(state) {\n    // Reset linkTitle state\n    state.linkTitle = false;\n    state.linkHref = false;\n    state.linkText = false;\n    // Reset EM state\n    state.em = false;\n    // Reset STRONG state\n    state.strong = false;\n    // Reset strikethrough state\n    state.strikethrough = false;\n    // Reset state.quote\n    state.quote = 0;\n    // Reset state.indentedCode\n    state.indentedCode = false;\n    if (state.f == htmlBlock) {\n      var exit = htmlModeMissing\n      if (!exit) {\n        var inner = CodeMirror.innerMode(htmlMode, state.htmlState)\n        exit = inner.mode.name == \"xml\" && inner.state.tagStart === null &&\n          (!inner.state.context && inner.state.tokenize.isInText)\n      }\n      if (exit) {\n        state.f = inlineNormal;\n        state.block = blockNormal;\n        state.htmlState = null;\n      }\n    }\n    // Reset state.trailingSpace\n    state.trailingSpace = 0;\n    state.trailingSpaceNewLine = false;\n    // Mark this line as blank\n    state.prevLine = state.thisLine\n    state.thisLine = {stream: null}\n    return null;\n  }\n\n  function blockNormal(stream, state) {\n    var firstTokenOnLine = stream.column() === state.indentation;\n    var prevLineLineIsEmpty = lineIsEmpty(state.prevLine.stream);\n    var prevLineIsIndentedCode = state.indentedCode;\n    var prevLineIsHr = state.prevLine.hr;\n    var prevLineIsList = state.list !== false;\n    var maxNonCodeIndentation = (state.listStack[state.listStack.length - 1] || 0) + 3;\n\n    state.indentedCode = false;\n\n    var lineIndentation = state.indentation;\n    // compute once per line (on first token)\n    if (state.indentationDiff === null) {\n      state.indentationDiff = state.indentation;\n      if (prevLineIsList) {\n        state.list = null;\n        // While this list item's marker's indentation is less than the deepest\n        //  list item's content's indentation,pop the deepest list item\n        //  indentation off the stack, and update block indentation state\n        while (lineIndentation < state.listStack[state.listStack.length - 1]) {\n          state.listStack.pop();\n          if (state.listStack.length) {\n            state.indentation = state.listStack[state.listStack.length - 1];\n          // less than the first list's indent -> the line is no longer a list\n          } else {\n            state.list = false;\n          }\n        }\n        if (state.list !== false) {\n          state.indentationDiff = lineIndentation - state.listStack[state.listStack.length - 1]\n        }\n      }\n    }\n\n    // not comprehensive (currently only for setext detection purposes)\n    var allowsInlineContinuation = (\n        !prevLineLineIsEmpty && !prevLineIsHr && !state.prevLine.header &&\n        (!prevLineIsList || !prevLineIsIndentedCode) &&\n        !state.prevLine.fencedCodeEnd\n    );\n\n    var isHr = (state.list === false || prevLineIsHr || prevLineLineIsEmpty) &&\n      state.indentation <= maxNonCodeIndentation && stream.match(hrRE);\n\n    var match = null;\n    if (state.indentationDiff >= 4 && (prevLineIsIndentedCode || state.prevLine.fencedCodeEnd ||\n         state.prevLine.header || prevLineLineIsEmpty)) {\n      stream.skipToEnd();\n      state.indentedCode = true;\n      return tokenTypes.code;\n    } else if (stream.eatSpace()) {\n      return null;\n    } else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(atxHeaderRE)) && match[1].length <= 6) {\n      state.quote = 0;\n      state.header = match[1].length;\n      state.thisLine.header = true;\n      if (modeCfg.highlightFormatting) state.formatting = \"header\";\n      state.f = state.inline;\n      return getType(state);\n    } else if (state.indentation <= maxNonCodeIndentation && stream.eat('>')) {\n      state.quote = firstTokenOnLine ? 1 : state.quote + 1;\n      if (modeCfg.highlightFormatting) state.formatting = \"quote\";\n      stream.eatSpace();\n      return getType(state);\n    } else if (!isHr && !state.setext && firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(listRE))) {\n      var listType = match[1] ? \"ol\" : \"ul\";\n\n      state.indentation = lineIndentation + stream.current().length;\n      state.list = true;\n      state.quote = 0;\n\n      // Add this list item's content's indentation to the stack\n      state.listStack.push(state.indentation);\n      // Reset inline styles which shouldn't propagate across list items\n      state.em = false;\n      state.strong = false;\n      state.code = false;\n      state.strikethrough = false;\n\n      if (modeCfg.taskLists && stream.match(taskListRE, false)) {\n        state.taskList = true;\n      }\n      state.f = state.inline;\n      if (modeCfg.highlightFormatting) state.formatting = [\"list\", \"list-\" + listType];\n      return getType(state);\n    } else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(fencedCodeRE, true))) {\n      state.quote = 0;\n      state.fencedEndRE = new RegExp(match[1] + \"+ *$\");\n      // try switching mode\n      state.localMode = modeCfg.fencedCodeBlockHighlighting && getMode(match[2] || modeCfg.fencedCodeBlockDefaultMode );\n      if (state.localMode) state.localState = CodeMirror.startState(state.localMode);\n      state.f = state.block = local;\n      if (modeCfg.highlightFormatting) state.formatting = \"code-block\";\n      state.code = -1\n      return getType(state);\n    // SETEXT has lowest block-scope precedence after HR, so check it after\n    //  the others (code, blockquote, list...)\n    } else if (\n      // if setext set, indicates line after ---/===\n      state.setext || (\n        // line before ---/===\n        (!allowsInlineContinuation || !prevLineIsList) && !state.quote && state.list === false &&\n        !state.code && !isHr && !linkDefRE.test(stream.string) &&\n        (match = stream.lookAhead(1)) && (match = match.match(setextHeaderRE))\n      )\n    ) {\n      if ( !state.setext ) {\n        state.header = match[0].charAt(0) == '=' ? 1 : 2;\n        state.setext = state.header;\n      } else {\n        state.header = state.setext;\n        // has no effect on type so we can reset it now\n        state.setext = 0;\n        stream.skipToEnd();\n        if (modeCfg.highlightFormatting) state.formatting = \"header\";\n      }\n      state.thisLine.header = true;\n      state.f = state.inline;\n      return getType(state);\n    } else if (isHr) {\n      stream.skipToEnd();\n      state.hr = true;\n      state.thisLine.hr = true;\n      return tokenTypes.hr;\n    } else if (stream.peek() === '[') {\n      return switchInline(stream, state, footnoteLink);\n    }\n\n    return switchInline(stream, state, state.inline);\n  }\n\n  function htmlBlock(stream, state) {\n    var style = htmlMode.token(stream, state.htmlState);\n    if (!htmlModeMissing) {\n      var inner = CodeMirror.innerMode(htmlMode, state.htmlState)\n      if ((inner.mode.name == \"xml\" && inner.state.tagStart === null &&\n           (!inner.state.context && inner.state.tokenize.isInText)) ||\n          (state.md_inside && stream.current().indexOf(\">\") > -1)) {\n        state.f = inlineNormal;\n        state.block = blockNormal;\n        state.htmlState = null;\n      }\n    }\n    return style;\n  }\n\n  function local(stream, state) {\n    var currListInd = state.listStack[state.listStack.length - 1] || 0;\n    var hasExitedList = state.indentation < currListInd;\n    var maxFencedEndInd = currListInd + 3;\n    if (state.fencedEndRE && state.indentation <= maxFencedEndInd && (hasExitedList || stream.match(state.fencedEndRE))) {\n      if (modeCfg.highlightFormatting) state.formatting = \"code-block\";\n      var returnType;\n      if (!hasExitedList) returnType = getType(state)\n      state.localMode = state.localState = null;\n      state.block = blockNormal;\n      state.f = inlineNormal;\n      state.fencedEndRE = null;\n      state.code = 0\n      state.thisLine.fencedCodeEnd = true;\n      if (hasExitedList) return switchBlock(stream, state, state.block);\n      return returnType;\n    } else if (state.localMode) {\n      return state.localMode.token(stream, state.localState);\n    } else {\n      stream.skipToEnd();\n      return tokenTypes.code;\n    }\n  }\n\n  // Inline\n  function getType(state) {\n    var styles = [];\n\n    if (state.formatting) {\n      styles.push(tokenTypes.formatting);\n\n      if (typeof state.formatting === \"string\") state.formatting = [state.formatting];\n\n      for (var i = 0; i < state.formatting.length; i++) {\n        styles.push(tokenTypes.formatting + \"-\" + state.formatting[i]);\n\n        if (state.formatting[i] === \"header\") {\n          styles.push(tokenTypes.formatting + \"-\" + state.formatting[i] + \"-\" + state.header);\n        }\n\n        // Add `formatting-quote` and `formatting-quote-#` for blockquotes\n        // Add `error` instead if the maximum blockquote nesting depth is passed\n        if (state.formatting[i] === \"quote\") {\n          if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {\n            styles.push(tokenTypes.formatting + \"-\" + state.formatting[i] + \"-\" + state.quote);\n          } else {\n            styles.push(\"error\");\n          }\n        }\n      }\n    }\n\n    if (state.taskOpen) {\n      styles.push(\"meta\");\n      return styles.length ? styles.join(' ') : null;\n    }\n    if (state.taskClosed) {\n      styles.push(\"property\");\n      return styles.length ? styles.join(' ') : null;\n    }\n\n    if (state.linkHref) {\n      styles.push(tokenTypes.linkHref, \"url\");\n    } else { // Only apply inline styles to non-url text\n      if (state.strong) { styles.push(tokenTypes.strong); }\n      if (state.em) { styles.push(tokenTypes.em); }\n      if (state.strikethrough) { styles.push(tokenTypes.strikethrough); }\n      if (state.emoji) { styles.push(tokenTypes.emoji); }\n      if (state.linkText) { styles.push(tokenTypes.linkText); }\n      if (state.code) { styles.push(tokenTypes.code); }\n      if (state.image) { styles.push(tokenTypes.image); }\n      if (state.imageAltText) { styles.push(tokenTypes.imageAltText, \"link\"); }\n      if (state.imageMarker) { styles.push(tokenTypes.imageMarker); }\n    }\n\n    if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + \"-\" + state.header); }\n\n    if (state.quote) {\n      styles.push(tokenTypes.quote);\n\n      // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth\n      if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {\n        styles.push(tokenTypes.quote + \"-\" + state.quote);\n      } else {\n        styles.push(tokenTypes.quote + \"-\" + modeCfg.maxBlockquoteDepth);\n      }\n    }\n\n    if (state.list !== false) {\n      var listMod = (state.listStack.length - 1) % 3;\n      if (!listMod) {\n        styles.push(tokenTypes.list1);\n      } else if (listMod === 1) {\n        styles.push(tokenTypes.list2);\n      } else {\n        styles.push(tokenTypes.list3);\n      }\n    }\n\n    if (state.trailingSpaceNewLine) {\n      styles.push(\"trailing-space-new-line\");\n    } else if (state.trailingSpace) {\n      styles.push(\"trailing-space-\" + (state.trailingSpace % 2 ? \"a\" : \"b\"));\n    }\n\n    return styles.length ? styles.join(' ') : null;\n  }\n\n  function handleText(stream, state) {\n    if (stream.match(textRE, true)) {\n      return getType(state);\n    }\n    return undefined;\n  }\n\n  function inlineNormal(stream, state) {\n    var style = state.text(stream, state);\n    if (typeof style !== 'undefined')\n      return style;\n\n    if (state.list) { // List marker (*, +, -, 1., etc)\n      state.list = null;\n      return getType(state);\n    }\n\n    if (state.taskList) {\n      var taskOpen = stream.match(taskListRE, true)[1] === \" \";\n      if (taskOpen) state.taskOpen = true;\n      else state.taskClosed = true;\n      if (modeCfg.highlightFormatting) state.formatting = \"task\";\n      state.taskList = false;\n      return getType(state);\n    }\n\n    state.taskOpen = false;\n    state.taskClosed = false;\n\n    if (state.header && stream.match(/^#+$/, true)) {\n      if (modeCfg.highlightFormatting) state.formatting = \"header\";\n      return getType(state);\n    }\n\n    var ch = stream.next();\n\n    // Matches link titles present on next line\n    if (state.linkTitle) {\n      state.linkTitle = false;\n      var matchCh = ch;\n      if (ch === '(') {\n        matchCh = ')';\n      }\n      matchCh = (matchCh+'').replace(/([.?*+^\\[\\]\\\\(){}|-])/g, \"\\\\$1\");\n      var regex = '^\\\\s*(?:[^' + matchCh + '\\\\\\\\]+|\\\\\\\\\\\\\\\\|\\\\\\\\.)' + matchCh;\n      if (stream.match(new RegExp(regex), true)) {\n        return tokenTypes.linkHref;\n      }\n    }\n\n    // If this block is changed, it may need to be updated in GFM mode\n    if (ch === '`') {\n      var previousFormatting = state.formatting;\n      if (modeCfg.highlightFormatting) state.formatting = \"code\";\n      stream.eatWhile('`');\n      var count = stream.current().length\n      if (state.code == 0 && (!state.quote || count == 1)) {\n        state.code = count\n        return getType(state)\n      } else if (count == state.code) { // Must be exact\n        var t = getType(state)\n        state.code = 0\n        return t\n      } else {\n        state.formatting = previousFormatting\n        return getType(state)\n      }\n    } else if (state.code) {\n      return getType(state);\n    }\n\n    if (ch === '\\\\') {\n      stream.next();\n      if (modeCfg.highlightFormatting) {\n        var type = getType(state);\n        var formattingEscape = tokenTypes.formatting + \"-escape\";\n        return type ? type + \" \" + formattingEscape : formattingEscape;\n      }\n    }\n\n    if (ch === '!' && stream.match(/\\[[^\\]]*\\] ?(?:\\(|\\[)/, false)) {\n      state.imageMarker = true;\n      state.image = true;\n      if (modeCfg.highlightFormatting) state.formatting = \"image\";\n      return getType(state);\n    }\n\n    if (ch === '[' && state.imageMarker && stream.match(/[^\\]]*\\](\\(.*?\\)| ?\\[.*?\\])/, false)) {\n      state.imageMarker = false;\n      state.imageAltText = true\n      if (modeCfg.highlightFormatting) state.formatting = \"image\";\n      return getType(state);\n    }\n\n    if (ch === ']' && state.imageAltText) {\n      if (modeCfg.highlightFormatting) state.formatting = \"image\";\n      var type = getType(state);\n      state.imageAltText = false;\n      state.image = false;\n      state.inline = state.f = linkHref;\n      return type;\n    }\n\n    if (ch === '[' && !state.image) {\n      if (state.linkText && stream.match(/^.*?\\]/)) return getType(state)\n      state.linkText = true;\n      if (modeCfg.highlightFormatting) state.formatting = \"link\";\n      return getType(state);\n    }\n\n    if (ch === ']' && state.linkText) {\n      if (modeCfg.highlightFormatting) state.formatting = \"link\";\n      var type = getType(state);\n      state.linkText = false;\n      state.inline = state.f = stream.match(/\\(.*?\\)| ?\\[.*?\\]/, false) ? linkHref : inlineNormal\n      return type;\n    }\n\n    if (ch === '<' && stream.match(/^(https?|ftps?):\\/\\/(?:[^\\\\>]|\\\\.)+>/, false)) {\n      state.f = state.inline = linkInline;\n      if (modeCfg.highlightFormatting) state.formatting = \"link\";\n      var type = getType(state);\n      if (type){\n        type += \" \";\n      } else {\n        type = \"\";\n      }\n      return type + tokenTypes.linkInline;\n    }\n\n    if (ch === '<' && stream.match(/^[^> \\\\]+@(?:[^\\\\>]|\\\\.)+>/, false)) {\n      state.f = state.inline = linkInline;\n      if (modeCfg.highlightFormatting) state.formatting = \"link\";\n      var type = getType(state);\n      if (type){\n        type += \" \";\n      } else {\n        type = \"\";\n      }\n      return type + tokenTypes.linkEmail;\n    }\n\n    if (modeCfg.xml && ch === '<' && stream.match(/^(!--|\\?|!\\[CDATA\\[|[a-z][a-z0-9-]*(?:\\s+[a-z_:.\\-]+(?:\\s*=\\s*[^>]+)?)*\\s*(?:>|$))/i, false)) {\n      var end = stream.string.indexOf(\">\", stream.pos);\n      if (end != -1) {\n        var atts = stream.string.substring(stream.start, end);\n        if (/markdown\\s*=\\s*('|\"){0,1}1('|\"){0,1}/.test(atts)) state.md_inside = true;\n      }\n      stream.backUp(1);\n      state.htmlState = CodeMirror.startState(htmlMode);\n      return switchBlock(stream, state, htmlBlock);\n    }\n\n    if (modeCfg.xml && ch === '<' && stream.match(/^\\/\\w*?>/)) {\n      state.md_inside = false;\n      return \"tag\";\n    } else if (ch === \"*\" || ch === \"_\") {\n      var len = 1, before = stream.pos == 1 ? \" \" : stream.string.charAt(stream.pos - 2)\n      while (len < 3 && stream.eat(ch)) len++\n      var after = stream.peek() || \" \"\n      // See http://spec.commonmark.org/0.27/#emphasis-and-strong-emphasis\n      var leftFlanking = !/\\s/.test(after) && (!punctuation.test(after) || /\\s/.test(before) || punctuation.test(before))\n      var rightFlanking = !/\\s/.test(before) && (!punctuation.test(before) || /\\s/.test(after) || punctuation.test(after))\n      var setEm = null, setStrong = null\n      if (len % 2) { // Em\n        if (!state.em && leftFlanking && (ch === \"*\" || !rightFlanking || punctuation.test(before)))\n          setEm = true\n        else if (state.em == ch && rightFlanking && (ch === \"*\" || !leftFlanking || punctuation.test(after)))\n          setEm = false\n      }\n      if (len > 1) { // Strong\n        if (!state.strong && leftFlanking && (ch === \"*\" || !rightFlanking || punctuation.test(before)))\n          setStrong = true\n        else if (state.strong == ch && rightFlanking && (ch === \"*\" || !leftFlanking || punctuation.test(after)))\n          setStrong = false\n      }\n      if (setStrong != null || setEm != null) {\n        if (modeCfg.highlightFormatting) state.formatting = setEm == null ? \"strong\" : setStrong == null ? \"em\" : \"strong em\"\n        if (setEm === true) state.em = ch\n        if (setStrong === true) state.strong = ch\n        var t = getType(state)\n        if (setEm === false) state.em = false\n        if (setStrong === false) state.strong = false\n        return t\n      }\n    } else if (ch === ' ') {\n      if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces\n        if (stream.peek() === ' ') { // Surrounded by spaces, ignore\n          return getType(state);\n        } else { // Not surrounded by spaces, back up pointer\n          stream.backUp(1);\n        }\n      }\n    }\n\n    if (modeCfg.strikethrough) {\n      if (ch === '~' && stream.eatWhile(ch)) {\n        if (state.strikethrough) {// Remove strikethrough\n          if (modeCfg.highlightFormatting) state.formatting = \"strikethrough\";\n          var t = getType(state);\n          state.strikethrough = false;\n          return t;\n        } else if (stream.match(/^[^\\s]/, false)) {// Add strikethrough\n          state.strikethrough = true;\n          if (modeCfg.highlightFormatting) state.formatting = \"strikethrough\";\n          return getType(state);\n        }\n      } else if (ch === ' ') {\n        if (stream.match('~~', true)) { // Probably surrounded by space\n          if (stream.peek() === ' ') { // Surrounded by spaces, ignore\n            return getType(state);\n          } else { // Not surrounded by spaces, back up pointer\n            stream.backUp(2);\n          }\n        }\n      }\n    }\n\n    if (modeCfg.emoji && ch === \":\" && stream.match(/^(?:[a-z_\\d+][a-z_\\d+-]*|\\-[a-z_\\d+][a-z_\\d+-]*):/)) {\n      state.emoji = true;\n      if (modeCfg.highlightFormatting) state.formatting = \"emoji\";\n      var retType = getType(state);\n      state.emoji = false;\n      return retType;\n    }\n\n    if (ch === ' ') {\n      if (stream.match(/^ +$/, false)) {\n        state.trailingSpace++;\n      } else if (state.trailingSpace) {\n        state.trailingSpaceNewLine = true;\n      }\n    }\n\n    return getType(state);\n  }\n\n  function linkInline(stream, state) {\n    var ch = stream.next();\n\n    if (ch === \">\") {\n      state.f = state.inline = inlineNormal;\n      if (modeCfg.highlightFormatting) state.formatting = \"link\";\n      var type = getType(state);\n      if (type){\n        type += \" \";\n      } else {\n        type = \"\";\n      }\n      return type + tokenTypes.linkInline;\n    }\n\n    stream.match(/^[^>]+/, true);\n\n    return tokenTypes.linkInline;\n  }\n\n  function linkHref(stream, state) {\n    // Check if space, and return NULL if so (to avoid marking the space)\n    if(stream.eatSpace()){\n      return null;\n    }\n    var ch = stream.next();\n    if (ch === '(' || ch === '[') {\n      state.f = state.inline = getLinkHrefInside(ch === \"(\" ? \")\" : \"]\");\n      if (modeCfg.highlightFormatting) state.formatting = \"link-string\";\n      state.linkHref = true;\n      return getType(state);\n    }\n    return 'error';\n  }\n\n  var linkRE = {\n    \")\": /^(?:[^\\\\\\(\\)]|\\\\.|\\((?:[^\\\\\\(\\)]|\\\\.)*\\))*?(?=\\))/,\n    \"]\": /^(?:[^\\\\\\[\\]]|\\\\.|\\[(?:[^\\\\\\[\\]]|\\\\.)*\\])*?(?=\\])/\n  }\n\n  function getLinkHrefInside(endChar) {\n    return function(stream, state) {\n      var ch = stream.next();\n\n      if (ch === endChar) {\n        state.f = state.inline = inlineNormal;\n        if (modeCfg.highlightFormatting) state.formatting = \"link-string\";\n        var returnState = getType(state);\n        state.linkHref = false;\n        return returnState;\n      }\n\n      stream.match(linkRE[endChar])\n      state.linkHref = true;\n      return getType(state);\n    };\n  }\n\n  function footnoteLink(stream, state) {\n    if (stream.match(/^([^\\]\\\\]|\\\\.)*\\]:/, false)) {\n      state.f = footnoteLinkInside;\n      stream.next(); // Consume [\n      if (modeCfg.highlightFormatting) state.formatting = \"link\";\n      state.linkText = true;\n      return getType(state);\n    }\n    return switchInline(stream, state, inlineNormal);\n  }\n\n  function footnoteLinkInside(stream, state) {\n    if (stream.match(']:', true)) {\n      state.f = state.inline = footnoteUrl;\n      if (modeCfg.highlightFormatting) state.formatting = \"link\";\n      var returnType = getType(state);\n      state.linkText = false;\n      return returnType;\n    }\n\n    stream.match(/^([^\\]\\\\]|\\\\.)+/, true);\n\n    return tokenTypes.linkText;\n  }\n\n  function footnoteUrl(stream, state) {\n    // Check if space, and return NULL if so (to avoid marking the space)\n    if(stream.eatSpace()){\n      return null;\n    }\n    // Match URL\n    stream.match(/^[^\\s]+/, true);\n    // Check for link title\n    if (stream.peek() === undefined) { // End of line, set flag to check next line\n      state.linkTitle = true;\n    } else { // More content on line, check if link title\n      stream.match(/^(?:\\s+(?:\"(?:[^\"\\\\]|\\\\.)+\"|'(?:[^'\\\\]|\\\\.)+'|\\((?:[^)\\\\]|\\\\.)+\\)))?/, true);\n    }\n    state.f = state.inline = inlineNormal;\n    return tokenTypes.linkHref + \" url\";\n  }\n\n  var mode = {\n    startState: function() {\n      return {\n        f: blockNormal,\n\n        prevLine: {stream: null},\n        thisLine: {stream: null},\n\n        block: blockNormal,\n        htmlState: null,\n        indentation: 0,\n\n        inline: inlineNormal,\n        text: handleText,\n\n        formatting: false,\n        linkText: false,\n        linkHref: false,\n        linkTitle: false,\n        code: 0,\n        em: false,\n        strong: false,\n        header: 0,\n        setext: 0,\n        hr: false,\n        taskList: false,\n        list: false,\n        listStack: [],\n        quote: 0,\n        trailingSpace: 0,\n        trailingSpaceNewLine: false,\n        strikethrough: false,\n        emoji: false,\n        fencedEndRE: null\n      };\n    },\n\n    copyState: function(s) {\n      return {\n        f: s.f,\n\n        prevLine: s.prevLine,\n        thisLine: s.thisLine,\n\n        block: s.block,\n        htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),\n        indentation: s.indentation,\n\n        localMode: s.localMode,\n        localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,\n\n        inline: s.inline,\n        text: s.text,\n        formatting: false,\n        linkText: s.linkText,\n        linkTitle: s.linkTitle,\n        linkHref: s.linkHref,\n        code: s.code,\n        em: s.em,\n        strong: s.strong,\n        strikethrough: s.strikethrough,\n        emoji: s.emoji,\n        header: s.header,\n        setext: s.setext,\n        hr: s.hr,\n        taskList: s.taskList,\n        list: s.list,\n        listStack: s.listStack.slice(0),\n        quote: s.quote,\n        indentedCode: s.indentedCode,\n        trailingSpace: s.trailingSpace,\n        trailingSpaceNewLine: s.trailingSpaceNewLine,\n        md_inside: s.md_inside,\n        fencedEndRE: s.fencedEndRE\n      };\n    },\n\n    token: function(stream, state) {\n\n      // Reset state.formatting\n      state.formatting = false;\n\n      if (stream != state.thisLine.stream) {\n        state.header = 0;\n        state.hr = false;\n\n        if (stream.match(/^\\s*$/, true)) {\n          blankLine(state);\n          return null;\n        }\n\n        state.prevLine = state.thisLine\n        state.thisLine = {stream: stream}\n\n        // Reset state.taskList\n        state.taskList = false;\n\n        // Reset state.trailingSpace\n        state.trailingSpace = 0;\n        state.trailingSpaceNewLine = false;\n\n        if (!state.localState) {\n          state.f = state.block;\n          if (state.f != htmlBlock) {\n            var indentation = stream.match(/^\\s*/, true)[0].replace(/\\t/g, expandedTab).length;\n            state.indentation = indentation;\n            state.indentationDiff = null;\n            if (indentation > 0) return null;\n          }\n        }\n      }\n      return state.f(stream, state);\n    },\n\n    innerMode: function(state) {\n      if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};\n      if (state.localState) return {state: state.localState, mode: state.localMode};\n      return {state: state, mode: mode};\n    },\n\n    indent: function(state, textAfter, line) {\n      if (state.block == htmlBlock && htmlMode.indent) return htmlMode.indent(state.htmlState, textAfter, line)\n      if (state.localState && state.localMode.indent) return state.localMode.indent(state.localState, textAfter, line)\n      return CodeMirror.Pass\n    },\n\n    blankLine: blankLine,\n\n    getType: getType,\n\n    blockCommentStart: \"<!--\",\n    blockCommentEnd: \"-->\",\n    closeBrackets: \"()[]{}''\\\"\\\"``\",\n    fold: \"markdown\"\n  };\n  return mode;\n}, \"xml\");\n\nCodeMirror.defineMIME(\"text/markdown\", \"markdown\");\n\nCodeMirror.defineMIME(\"text/x-markdown\", \"markdown\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/markdown/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var config = {tabSize: 4, indentUnit: 2}\n  var mode = CodeMirror.getMode(config, \"markdown\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n  var modeHighlightFormatting = CodeMirror.getMode(config, {name: \"markdown\", highlightFormatting: true});\n  function FT(name) { test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1)); }\n  var modeMT_noXml = CodeMirror.getMode(config, {name: \"markdown\", xml: false});\n  function MT_noXml(name) { test.mode(name, modeMT_noXml, Array.prototype.slice.call(arguments, 1)); }\n  var modeMT_noFencedHighlight = CodeMirror.getMode(config, {name: \"markdown\", fencedCodeBlockHighlighting: false});\n  function MT_noFencedHighlight(name) { test.mode(name, modeMT_noFencedHighlight, Array.prototype.slice.call(arguments, 1)); }\n  var modeAtxNoSpace = CodeMirror.getMode(config, {name: \"markdown\", allowAtxHeaderWithoutSpace: true});\n  function AtxNoSpaceTest(name) { test.mode(name, modeAtxNoSpace, Array.prototype.slice.call(arguments, 1)); }\n  var modeOverrideClasses = CodeMirror.getMode(config, {\n    name: \"markdown\",\n    strikethrough: true,\n    emoji: true,\n    tokenTypeOverrides: {\n      \"header\" : \"override-header\",\n      \"code\" : \"override-code\",\n      \"quote\" : \"override-quote\",\n      \"list1\" : \"override-list1\",\n      \"list2\" : \"override-list2\",\n      \"list3\" : \"override-list3\",\n      \"hr\" : \"override-hr\",\n      \"image\" : \"override-image\",\n      \"imageAltText\": \"override-image-alt-text\",\n      \"imageMarker\": \"override-image-marker\",\n      \"linkInline\" : \"override-link-inline\",\n      \"linkEmail\" : \"override-link-email\",\n      \"linkText\" : \"override-link-text\",\n      \"linkHref\" : \"override-link-href\",\n      \"em\" : \"override-em\",\n      \"strong\" : \"override-strong\",\n      \"strikethrough\" : \"override-strikethrough\",\n      \"emoji\" : \"override-emoji\"\n  }});\n  function TokenTypeOverrideTest(name) { test.mode(name, modeOverrideClasses, Array.prototype.slice.call(arguments, 1)); }\n  var modeFormattingOverride = CodeMirror.getMode(config, {\n    name: \"markdown\",\n    highlightFormatting: true,\n    tokenTypeOverrides: {\n      \"formatting\" : \"override-formatting\"\n  }});\n  function FormatTokenTypeOverrideTest(name) { test.mode(name, modeFormattingOverride, Array.prototype.slice.call(arguments, 1)); }\n  var modeET = CodeMirror.getMode(config, {name: \"markdown\", emoji: true});\n  function ET(name) { test.mode(name, modeET, Array.prototype.slice.call(arguments, 1)); }\n\n\n  FT(\"formatting_emAsterisk\",\n     \"[em&formatting&formatting-em *][em foo][em&formatting&formatting-em *]\");\n\n  FT(\"formatting_emUnderscore\",\n     \"[em&formatting&formatting-em _][em foo][em&formatting&formatting-em _]\");\n\n  FT(\"formatting_strongAsterisk\",\n     \"[strong&formatting&formatting-strong **][strong foo][strong&formatting&formatting-strong **]\");\n\n  FT(\"formatting_strongUnderscore\",\n     \"[strong&formatting&formatting-strong __][strong foo][strong&formatting&formatting-strong __]\");\n\n  FT(\"formatting_codeBackticks\",\n     \"[comment&formatting&formatting-code `][comment foo][comment&formatting&formatting-code `]\");\n\n  FT(\"formatting_doubleBackticks\",\n     \"[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]\");\n\n  FT(\"formatting_atxHeader\",\n     \"[header&header-1&formatting&formatting-header&formatting-header-1 # ][header&header-1 foo # bar ][header&header-1&formatting&formatting-header&formatting-header-1 #]\");\n\n  FT(\"formatting_setextHeader\",\n     \"[header&header-1 foo]\",\n     \"[header&header-1&formatting&formatting-header&formatting-header-1 =]\");\n\n  FT(\"formatting_blockquote\",\n     \"[quote&quote-1&formatting&formatting-quote&formatting-quote-1 > ][quote&quote-1 foo]\");\n\n  FT(\"formatting_list\",\n     \"[variable-2&formatting&formatting-list&formatting-list-ul - ][variable-2 foo]\");\n  FT(\"formatting_list\",\n     \"[variable-2&formatting&formatting-list&formatting-list-ol 1. ][variable-2 foo]\");\n\n  FT(\"formatting_link\",\n     \"[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string&url (][string&url http://example.com/][string&formatting&formatting-link-string&url )]\");\n\n  FT(\"formatting_linkReference\",\n     \"[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string&url [][string&url bar][string&formatting&formatting-link-string&url ]]]\",\n     \"[link&formatting&formatting-link [][link bar][link&formatting&formatting-link ]]:] [string&url http://example.com/]\");\n\n  FT(\"formatting_linkWeb\",\n     \"[link&formatting&formatting-link <][link http://example.com/][link&formatting&formatting-link >]\");\n\n  FT(\"formatting_linkEmail\",\n     \"[link&formatting&formatting-link <][link user@example.com][link&formatting&formatting-link >]\");\n\n  FT(\"formatting_escape\",\n     \"[formatting-escape \\\\*]\");\n\n  FT(\"formatting_image\",\n     \"[formatting&formatting-image&image&image-marker !][formatting&formatting-image&image&image-alt-text&link [[][image&image-alt-text&link alt text][formatting&formatting-image&image&image-alt-text&link ]]][formatting&formatting-link-string&string&url (][url&string http://link.to/image.jpg][formatting&formatting-link-string&string&url )]\");\n\n  FT(\"codeBlock\",\n     \"[comment&formatting&formatting-code-block ```css]\",\n     \"[tag foo]\",\n     \"[comment&formatting&formatting-code-block ```]\");\n\n  MT(\"plainText\",\n     \"foo\");\n\n  // Don't style single trailing space\n  MT(\"trailingSpace1\",\n     \"foo \");\n\n  // Two or more trailing spaces should be styled with line break character\n  MT(\"trailingSpace2\",\n     \"foo[trailing-space-a  ][trailing-space-new-line  ]\");\n\n  MT(\"trailingSpace3\",\n     \"foo[trailing-space-a  ][trailing-space-b  ][trailing-space-new-line  ]\");\n\n  MT(\"trailingSpace4\",\n     \"foo[trailing-space-a  ][trailing-space-b  ][trailing-space-a  ][trailing-space-new-line  ]\");\n\n  // Code blocks using 4 spaces (regardless of CodeMirror.tabSize value)\n  MT(\"codeBlocksUsing4Spaces\",\n     \"    [comment foo]\");\n\n  // Code blocks using 4 spaces with internal indentation\n  MT(\"codeBlocksUsing4SpacesIndentation\",\n     \"    [comment bar]\",\n     \"        [comment hello]\",\n     \"            [comment world]\",\n     \"    [comment foo]\",\n     \"bar\");\n\n  // Code blocks should end even after extra indented lines\n  MT(\"codeBlocksWithTrailingIndentedLine\",\n     \"    [comment foo]\",\n     \"        [comment bar]\",\n     \"    [comment baz]\",\n     \"    \",\n     \"hello\");\n\n  // Code blocks using 1 tab (regardless of CodeMirror.indentWithTabs value)\n  MT(\"codeBlocksUsing1Tab\",\n     \"\\t[comment foo]\");\n\n  // No code blocks directly after paragraph\n  // http://spec.commonmark.org/0.19/#example-65\n  MT(\"noCodeBlocksAfterParagraph\",\n     \"Foo\",\n     \"    Bar\");\n\n  MT(\"codeBlocksAfterATX\",\n     \"[header&header-1 # foo]\",\n     \"    [comment code]\");\n\n  MT(\"codeBlocksAfterSetext\",\n     \"[header&header-2 foo]\",\n     \"[header&header-2 ---]\",\n     \"    [comment code]\");\n\n  MT(\"codeBlocksAfterFencedCode\",\n     \"[comment ```]\",\n     \"[comment foo]\",\n     \"[comment ```]\",\n     \"    [comment code]\");\n\n  // Inline code using backticks\n  MT(\"inlineCodeUsingBackticks\",\n     \"foo [comment `bar`]\");\n\n  // Block code using single backtick (shouldn't work)\n  MT(\"blockCodeSingleBacktick\",\n     \"[comment `]\",\n     \"[comment foo]\",\n     \"[comment `]\");\n\n  // Unclosed backticks\n  // Instead of simply marking as CODE, it would be nice to have an\n  // incomplete flag for CODE, that is styled slightly different.\n  MT(\"unclosedBackticks\",\n     \"foo [comment `bar]\");\n\n  // Per documentation: \"To include a literal backtick character within a\n  // code span, you can use multiple backticks as the opening and closing\n  // delimiters\"\n  MT(\"doubleBackticks\",\n     \"[comment ``foo ` bar``]\");\n\n  // Tests based on Dingus\n  // http://daringfireball.net/projects/markdown/dingus\n  //\n  // Multiple backticks within an inline code block\n  MT(\"consecutiveBackticks\",\n     \"[comment `foo```bar`]\");\n\n  // Multiple backticks within an inline code block with a second code block\n  MT(\"consecutiveBackticks\",\n     \"[comment `foo```bar`] hello [comment `world`]\");\n\n  // Unclosed with several different groups of backticks\n  MT(\"unclosedBackticks\",\n     \"[comment ``foo ``` bar` hello]\");\n\n  // Closed with several different groups of backticks\n  MT(\"closedBackticks\",\n     \"[comment ``foo ``` bar` hello``] world\");\n\n  // info string cannot contain backtick, thus should result in inline code\n  MT(\"closingFencedMarksOnSameLine\",\n     \"[comment ``` code ```] foo\");\n\n  // atx headers\n  // http://daringfireball.net/projects/markdown/syntax#header\n\n  MT(\"atxH1\",\n     \"[header&header-1 # foo]\");\n\n  MT(\"atxH2\",\n     \"[header&header-2 ## foo]\");\n\n  MT(\"atxH3\",\n     \"[header&header-3 ### foo]\");\n\n  MT(\"atxH4\",\n     \"[header&header-4 #### foo]\");\n\n  MT(\"atxH5\",\n     \"[header&header-5 ##### foo]\");\n\n  MT(\"atxH6\",\n     \"[header&header-6 ###### foo]\");\n\n  // http://spec.commonmark.org/0.19/#example-24\n  MT(\"noAtxH7\",\n     \"####### foo\");\n\n  // http://spec.commonmark.org/0.19/#example-25\n  MT(\"noAtxH1WithoutSpace\",\n     \"#5 bolt\");\n\n  // CommonMark requires a space after # but most parsers don't\n  AtxNoSpaceTest(\"atxNoSpaceAllowed_H1NoSpace\",\n     \"[header&header-1 #foo]\");\n\n  AtxNoSpaceTest(\"atxNoSpaceAllowed_H4NoSpace\",\n     \"[header&header-4 ####foo]\");\n\n  AtxNoSpaceTest(\"atxNoSpaceAllowed_H1Space\",\n     \"[header&header-1 # foo]\");\n\n  // Inline styles should be parsed inside headers\n  MT(\"atxH1inline\",\n     \"[header&header-1 # foo ][header&header-1&em *bar*]\");\n\n  MT(\"atxIndentedTooMuch\",\n     \"[header&header-1 # foo]\",\n     \"    [comment # bar]\");\n\n  // disable atx inside blockquote until we implement proper blockquote inner mode\n  // TODO: fix to be CommonMark-compliant\n  MT(\"atxNestedInsideBlockquote\",\n     \"[quote&quote-1 > # foo]\");\n\n  MT(\"atxAfterBlockquote\",\n     \"[quote&quote-1 > foo]\",\n     \"[header&header-1 # bar]\");\n\n  // Setext headers - H1, H2\n  // Per documentation, \"Any number of underlining =’s or -’s will work.\"\n  // http://daringfireball.net/projects/markdown/syntax#header\n  // Ideally, the text would be marked as `header` as well, but this is\n  // not really feasible at the moment. So, instead, we're testing against\n  // what works today, to avoid any regressions.\n  //\n  // Check if single underlining = works\n  MT(\"setextH1\",\n     \"[header&header-1 foo]\",\n     \"[header&header-1 =]\");\n\n  // Check if 3+ ='s work\n  MT(\"setextH1\",\n     \"[header&header-1 foo]\",\n     \"[header&header-1 ===]\");\n\n  // Check if single underlining - should not be interpreted\n  // as it might lead to an empty list:\n  // https://spec.commonmark.org/0.28/#setext-heading-underline\n  MT(\"setextH2Single\",\n     \"foo\",\n     \"-\");\n\n  // Check if 3+ -'s work\n  MT(\"setextH2\",\n     \"[header&header-2 foo]\",\n     \"[header&header-2 ---]\");\n\n  // http://spec.commonmark.org/0.19/#example-45\n  MT(\"setextH2AllowSpaces\",\n     \"[header&header-2 foo]\",\n     \"   [header&header-2 ----      ]\");\n\n  // http://spec.commonmark.org/0.19/#example-44\n  MT(\"noSetextAfterIndentedCodeBlock\",\n     \"     [comment foo]\",\n     \"[hr ---]\");\n\n  MT(\"setextAfterFencedCode\",\n     \"[comment ```]\",\n     \"[comment foo]\",\n     \"[comment ```]\",\n     \"[header&header-2 bar]\",\n     \"[header&header-2 ---]\");\n\n  MT(\"setextAfterATX\",\n     \"[header&header-1 # foo]\",\n     \"[header&header-2 bar]\",\n     \"[header&header-2 ---]\");\n\n  // http://spec.commonmark.org/0.19/#example-51\n  MT(\"noSetextAfterQuote\",\n     \"[quote&quote-1 > foo]\",\n     \"[hr ---]\",\n     \"\",\n     \"[quote&quote-1 > foo]\",\n     \"[quote&quote-1 bar]\",\n     \"[hr ---]\");\n\n  MT(\"noSetextAfterList\",\n     \"[variable-2 - foo]\",\n     \"[hr ---]\");\n\n  MT(\"noSetextAfterList_listContinuation\",\n     \"[variable-2 - foo]\",\n     \"bar\",\n     \"[hr ---]\");\n\n  MT(\"setextAfterList_afterIndentedCode\",\n     \"[variable-2 - foo]\",\n     \"\",\n     \"      [comment bar]\",\n     \"[header&header-2 baz]\",\n     \"[header&header-2 ---]\");\n\n  MT(\"setextAfterList_afterFencedCodeBlocks\",\n     \"[variable-2 - foo]\",\n     \"\",\n     \"      [comment ```]\",\n     \"      [comment bar]\",\n     \"      [comment ```]\",\n     \"[header&header-2 baz]\",\n     \"[header&header-2 ---]\");\n\n  MT(\"setextAfterList_afterHeader\",\n     \"[variable-2 - foo]\",\n     \"  [variable-2&header&header-1 # bar]\",\n     \"[header&header-2 baz]\",\n     \"[header&header-2 ---]\");\n\n  MT(\"setextAfterList_afterHr\",\n     \"[variable-2 - foo]\",\n     \"\",\n     \"  [hr ---]\",\n     \"[header&header-2 bar]\",\n     \"[header&header-2 ---]\");\n\n  MT(\"setext_nestedInlineMarkup\",\n     \"[header&header-1 foo ][em&header&header-1 *bar*]\",\n     \"[header&header-1 =]\");\n\n  MT(\"setext_linkDef\",\n     \"[link [[aaa]]:] [string&url http://google.com 'title']\",\n     \"[hr ---]\");\n\n  // currently, looks max one line ahead, thus won't catch valid CommonMark\n  //  markup\n  MT(\"setext_oneLineLookahead\",\n     \"foo\",\n     \"[header&header-1 bar]\",\n     \"[header&header-1 =]\");\n\n  // ensure we regard space after a single dash as a list\n  MT(\"setext_emptyList\",\n     \"foo\",\n     \"[variable-2 - ]\",\n     \"foo\");\n\n  // Single-line blockquote with trailing space\n  MT(\"blockquoteSpace\",\n     \"[quote&quote-1 > foo]\");\n\n  // Single-line blockquote\n  MT(\"blockquoteNoSpace\",\n     \"[quote&quote-1 >foo]\");\n\n  // No blank line before blockquote\n  MT(\"blockquoteNoBlankLine\",\n     \"foo\",\n     \"[quote&quote-1 > bar]\");\n\n  MT(\"blockquoteNested\",\n     \"[quote&quote-1 > foo]\",\n     \"[quote&quote-1 >][quote&quote-2 > foo]\",\n     \"[quote&quote-1 >][quote&quote-2 >][quote&quote-3 > foo]\");\n\n  // ensure quote-level is inferred correctly even if indented\n  MT(\"blockquoteNestedIndented\",\n     \" [quote&quote-1 > foo]\",\n     \" [quote&quote-1 >][quote&quote-2 > foo]\",\n     \" [quote&quote-1 >][quote&quote-2 >][quote&quote-3 > foo]\");\n\n  // ensure quote-level is inferred correctly even if indented\n  MT(\"blockquoteIndentedTooMuch\",\n     \"foo\",\n     \"    > bar\");\n\n  // Single-line blockquote followed by normal paragraph\n  MT(\"blockquoteThenParagraph\",\n     \"[quote&quote-1 >foo]\",\n     \"\",\n     \"bar\");\n\n  // Multi-line blockquote (lazy mode)\n  MT(\"multiBlockquoteLazy\",\n     \"[quote&quote-1 >foo]\",\n     \"[quote&quote-1 bar]\");\n\n  // Multi-line blockquote followed by normal paragraph (lazy mode)\n  MT(\"multiBlockquoteLazyThenParagraph\",\n     \"[quote&quote-1 >foo]\",\n     \"[quote&quote-1 bar]\",\n     \"\",\n     \"hello\");\n\n  // Multi-line blockquote (non-lazy mode)\n  MT(\"multiBlockquote\",\n     \"[quote&quote-1 >foo]\",\n     \"[quote&quote-1 >bar]\");\n\n  // Multi-line blockquote followed by normal paragraph (non-lazy mode)\n  MT(\"multiBlockquoteThenParagraph\",\n     \"[quote&quote-1 >foo]\",\n     \"[quote&quote-1 >bar]\",\n     \"\",\n     \"hello\");\n\n  // disallow lists inside blockquote for now because it causes problems outside blockquote\n  // TODO: fix to be CommonMark-compliant\n  MT(\"listNestedInBlockquote\",\n     \"[quote&quote-1 > - foo]\");\n\n  // disallow fenced blocks inside blockquote because it causes problems outside blockquote\n  // TODO: fix to be CommonMark-compliant\n  MT(\"fencedBlockNestedInBlockquote\",\n     \"[quote&quote-1 > ```]\",\n     \"[quote&quote-1 > code]\",\n     \"[quote&quote-1 > ```]\",\n     // ensure we still allow inline code\n     \"[quote&quote-1 > ][quote&quote-1&comment `code`]\");\n\n  // Header with leading space after continued blockquote (#3287, negative indentation)\n  MT(\"headerAfterContinuedBlockquote\",\n     \"[quote&quote-1 > foo]\",\n     \"[quote&quote-1 bar]\",\n     \"\",\n     \" [header&header-1 # hello]\");\n\n  // Check list types\n\n  MT(\"listAsterisk\",\n     \"foo\",\n     \"bar\",\n     \"\",\n     \"[variable-2 * foo]\",\n     \"[variable-2 * bar]\");\n\n  MT(\"listPlus\",\n     \"foo\",\n     \"bar\",\n     \"\",\n     \"[variable-2 + foo]\",\n     \"[variable-2 + bar]\");\n\n  MT(\"listDash\",\n     \"foo\",\n     \"bar\",\n     \"\",\n     \"[variable-2 - foo]\",\n     \"[variable-2 - bar]\");\n\n  MT(\"listNumber\",\n     \"foo\",\n     \"bar\",\n     \"\",\n     \"[variable-2 1. foo]\",\n     \"[variable-2 2. bar]\");\n\n  MT(\"listFromParagraph\",\n     \"foo\",\n     \"[variable-2 1. bar]\",\n     \"[variable-2 2. hello]\");\n\n  // List after hr\n  MT(\"listAfterHr\",\n     \"[hr ---]\",\n     \"[variable-2 - bar]\");\n\n  // List after header\n  MT(\"listAfterHeader\",\n     \"[header&header-1 # foo]\",\n     \"[variable-2 - bar]\");\n\n  // hr after list\n  MT(\"hrAfterList\",\n     \"[variable-2 - foo]\",\n     \"[hr -----]\");\n\n  MT(\"hrAfterFencedCode\",\n     \"[comment ```]\",\n     \"[comment code]\",\n     \"[comment ```]\",\n     \"[hr ---]\");\n\n  // allow hr inside lists\n  // (require prev line to be empty or hr, TODO: non-CommonMark-compliant)\n  MT(\"hrInsideList\",\n     \"[variable-2 - foo]\",\n     \"\",\n     \"  [hr ---]\",\n     \"     [hr ---]\",\n     \"\",\n     \"      [comment ---]\");\n\n  MT(\"consecutiveHr\",\n     \"[hr ---]\",\n     \"[hr ---]\",\n     \"[hr ---]\");\n\n  // Formatting in lists (*)\n  MT(\"listAsteriskFormatting\",\n     \"[variable-2 * ][variable-2&em *foo*][variable-2  bar]\",\n     \"[variable-2 * ][variable-2&strong **foo**][variable-2  bar]\",\n     \"[variable-2 * ][variable-2&em&strong ***foo***][variable-2  bar]\",\n     \"[variable-2 * ][variable-2&comment `foo`][variable-2  bar]\");\n\n  // Formatting in lists (+)\n  MT(\"listPlusFormatting\",\n     \"[variable-2 + ][variable-2&em *foo*][variable-2  bar]\",\n     \"[variable-2 + ][variable-2&strong **foo**][variable-2  bar]\",\n     \"[variable-2 + ][variable-2&em&strong ***foo***][variable-2  bar]\",\n     \"[variable-2 + ][variable-2&comment `foo`][variable-2  bar]\");\n\n  // Formatting in lists (-)\n  MT(\"listDashFormatting\",\n     \"[variable-2 - ][variable-2&em *foo*][variable-2  bar]\",\n     \"[variable-2 - ][variable-2&strong **foo**][variable-2  bar]\",\n     \"[variable-2 - ][variable-2&em&strong ***foo***][variable-2  bar]\",\n     \"[variable-2 - ][variable-2&comment `foo`][variable-2  bar]\");\n\n  // Formatting in lists (1.)\n  MT(\"listNumberFormatting\",\n     \"[variable-2 1. ][variable-2&em *foo*][variable-2  bar]\",\n     \"[variable-2 2. ][variable-2&strong **foo**][variable-2  bar]\",\n     \"[variable-2 3. ][variable-2&em&strong ***foo***][variable-2  bar]\",\n     \"[variable-2 4. ][variable-2&comment `foo`][variable-2  bar]\");\n\n  // Paragraph lists\n  MT(\"listParagraph\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"[variable-2 * bar]\");\n\n  // Multi-paragraph lists\n  //\n  // 4 spaces\n  MT(\"listMultiParagraph\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"[variable-2 * bar]\",\n     \"\",\n     \"    [variable-2 hello]\");\n\n  // 4 spaces, extra blank lines (should still be list, per Dingus)\n  MT(\"listMultiParagraphExtra\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"[variable-2 * bar]\",\n     \"\",\n     \"\",\n     \"    [variable-2 hello]\");\n\n  // 4 spaces, plus 1 space (should still be list, per Dingus)\n  MT(\"listMultiParagraphExtraSpace\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"[variable-2 * bar]\",\n     \"\",\n     \"     [variable-2 hello]\",\n     \"\",\n     \"    [variable-2 world]\");\n\n  // 1 tab\n  MT(\"listTab\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"[variable-2 * bar]\",\n     \"\",\n     \"\\t[variable-2 hello]\");\n\n  // No indent\n  MT(\"listNoIndent\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"[variable-2 * bar]\",\n     \"\",\n     \"hello\");\n\n  MT(\"listCommonMarkIndentationCode\",\n     \"[variable-2 * Code blocks also affect]\",\n     \"  [variable-3 * The next level starts where the contents start.]\",\n     \"   [variable-3 *    Anything less than that will keep the item on the same level.]\",\n     \"       [variable-3 * Each list item can indent the first level further and further.]\",\n     \"  [variable-3 * For the most part, this makes sense while writing a list.]\",\n     \"    [keyword * This means two items with same indentation can be different levels.]\",\n     \"     [keyword *  Each level has an indent requirement that can change between items.]\",\n     \"       [keyword * A list item that meets this will be part of the next level.]\",\n     \"   [variable-3 * Otherwise, it will be part of the level where it does meet this.]\",\n     \" [variable-2 * World]\");\n\n  // should handle nested and un-nested lists\n  MT(\"listCommonMark_MixedIndents\",\n     \"[variable-2 * list1]\",\n     \"    [variable-2 list1]\",\n     \"  [variable-2&header&header-1 # heading still part of list1]\",\n     \"  [variable-2 text after heading still part of list1]\",\n     \"\",\n     \"      [comment indented codeblock]\",\n     \"  [variable-2 list1 after code block]\",\n     \"  [variable-3 * list2]\",\n     // amount of spaces on empty lines between lists doesn't matter\n     \"              \",\n     // extra empty lines irrelevant\n     \"\",\n     \"\",\n     \"    [variable-3 indented text part of list2]\",\n     \"    [keyword * list3]\",\n     \"\",\n     \"    [variable-3 text at level of list2]\",\n     \"\",\n     \"  [variable-2 de-indented text part of list1 again]\",\n     \"\",\n     \"  [variable-2&comment ```]\",\n     \"  [comment code]\",\n     \"  [variable-2&comment ```]\",\n     \"\",\n     \"  [variable-2 text after fenced code]\");\n\n  // should correctly parse numbered list content indentation\n  MT(\"listCommonMark_NumberedListIndent\",\n     \"[variable-2 1000. list with base indent of 6]\",\n     \"\",\n     \"      [variable-2 text must be indented 6 spaces at minimum]\",\n     \"\",\n     \"         [variable-2 9-spaces indented text still part of list]\",\n     \"\",\n     \"          [comment indented codeblock starts at 10 spaces]\",\n     \"\",\n     \"     [comment text indented by 5 spaces no longer belong to list]\");\n\n  // should consider tab as 4 spaces\n  MT(\"listCommonMark_TabIndented\",\n     \"[variable-2 * list]\",\n     \"\\t[variable-3 * list2]\",\n     \"\",\n     \"\\t\\t[variable-3 part of list2]\");\n\n  MT(\"listAfterBlockquote\",\n     \"[quote&quote-1 > foo]\",\n     \"[variable-2 - bar]\");\n\n  // shouldn't create sublist if it's indented more than allowed\n  MT(\"nestedListIndentedTooMuch\",\n     \"[variable-2 - foo]\",\n     \"          [variable-2 - bar]\");\n\n  MT(\"listIndentedTooMuchAfterParagraph\",\n     \"foo\",\n     \"    - bar\");\n\n  // Blockquote\n  MT(\"blockquote\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"[variable-2 * bar]\",\n     \"\",\n     \"    [variable-2&quote&quote-1 > hello]\");\n\n  // Code block\n  MT(\"blockquoteCode\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"[variable-2 * bar]\",\n     \"\",\n     \"        [comment > hello]\",\n     \"\",\n     \"    [variable-2 world]\");\n\n  // Code block followed by text\n  MT(\"blockquoteCodeText\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"    [variable-2 bar]\",\n     \"\",\n     \"        [comment hello]\",\n     \"\",\n     \"    [variable-2 world]\");\n\n  // Nested list\n\n  MT(\"listAsteriskNested\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"    [variable-3 * bar]\");\n\n  MT(\"listPlusNested\",\n     \"[variable-2 + foo]\",\n     \"\",\n     \"    [variable-3 + bar]\");\n\n  MT(\"listDashNested\",\n     \"[variable-2 - foo]\",\n     \"\",\n     \"    [variable-3 - bar]\");\n\n  MT(\"listNumberNested\",\n     \"[variable-2 1. foo]\",\n     \"\",\n     \"    [variable-3 2. bar]\");\n\n  MT(\"listMixed\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"    [variable-3 + bar]\",\n     \"\",\n     \"        [keyword - hello]\",\n     \"\",\n     \"            [variable-2 1. world]\");\n\n  MT(\"listBlockquote\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"    [variable-3 + bar]\",\n     \"\",\n     \"        [quote&quote-1&variable-3 > hello]\");\n\n  MT(\"listCode\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"    [variable-3 + bar]\",\n     \"\",\n     \"            [comment hello]\");\n\n  // Code with internal indentation\n  MT(\"listCodeIndentation\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"        [comment bar]\",\n     \"            [comment hello]\",\n     \"                [comment world]\",\n     \"        [comment foo]\",\n     \"    [variable-2 bar]\");\n\n  // List nesting edge cases\n  MT(\"listNested\",\n    \"[variable-2 * foo]\",\n    \"\",\n    \"    [variable-3 * bar]\",\n    \"\",\n    \"       [variable-3 hello]\"\n  );\n  MT(\"listNested\",\n    \"[variable-2 * foo]\",\n    \"\",\n    \"    [variable-3 * bar]\",\n    \"\",\n    \"      [keyword * foo]\"\n  );\n\n  // Code followed by text\n  MT(\"listCodeText\",\n     \"[variable-2 * foo]\",\n     \"\",\n     \"        [comment bar]\",\n     \"\",\n     \"hello\");\n\n  // Following tests directly from official Markdown documentation\n  // http://daringfireball.net/projects/markdown/syntax#hr\n\n  MT(\"hrSpace\",\n     \"[hr * * *]\");\n\n  MT(\"hr\",\n     \"[hr ***]\");\n\n  MT(\"hrLong\",\n     \"[hr *****]\");\n\n  MT(\"hrSpaceDash\",\n     \"[hr - - -]\");\n\n  MT(\"hrDashLong\",\n     \"[hr ---------------------------------------]\");\n\n  //Images\n  MT(\"Images\",\n     \"[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://link.to/image.jpg)]\")\n\n  //Images with highlight alt text\n  MT(\"imageEm\",\n     \"[image&image-marker !][image&image-alt-text&link [[][image-alt-text&em&image&link *alt text*][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]\");\n\n  MT(\"imageStrong\",\n     \"[image&image-marker !][image&image-alt-text&link [[][image-alt-text&strong&image&link **alt text**][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]\");\n\n  MT(\"imageEmStrong\",\n     \"[image&image-marker !][image&image-alt-text&link [[][image&image-alt-text&em&strong&link ***alt text***][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]\");\n\n  // Inline link with title\n  MT(\"linkTitle\",\n     \"[link [[foo]]][string&url (http://example.com/ \\\"bar\\\")] hello\");\n\n  // Inline link without title\n  MT(\"linkNoTitle\",\n     \"[link [[foo]]][string&url (http://example.com/)] bar\");\n\n  // Inline link with image\n  MT(\"linkImage\",\n     \"[link [[][link&image&image-marker !][link&image&image-alt-text&link [[alt text]]][string&url (http://link.to/image.jpg)][link ]]][string&url (http://example.com/)] bar\");\n\n  // Inline link with Em\n  MT(\"linkEm\",\n     \"[link [[][link&em *foo*][link ]]][string&url (http://example.com/)] bar\");\n\n  // Inline link with Strong\n  MT(\"linkStrong\",\n     \"[link [[][link&strong **foo**][link ]]][string&url (http://example.com/)] bar\");\n\n  // Inline link with EmStrong\n  MT(\"linkEmStrong\",\n     \"[link [[][link&em&strong ***foo***][link ]]][string&url (http://example.com/)] bar\");\n\n  MT(\"multilineLink\",\n     \"[link [[foo]\",\n     \"[link bar]]][string&url (https://foo#_a)]\",\n     \"should not be italics\")\n\n  // Image with title\n  MT(\"imageTitle\",\n     \"[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://example.com/ \\\"bar\\\")] hello\");\n\n  // Image without title\n  MT(\"imageNoTitle\",\n     \"[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://example.com/)] bar\");\n\n  // Image with asterisks\n  MT(\"imageAsterisks\",\n     \"[image&image-marker !][image&image-alt-text&link [[ ][image&image-alt-text&em&link *alt text*][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)] bar\");\n\n  // Not a link. Should be normal text due to square brackets being used\n  // regularly in text, especially in quoted material, and no space is allowed\n  // between square brackets and parentheses (per Dingus).\n  MT(\"notALink\",\n     \"[link [[foo]]] (bar)\");\n\n  // Reference-style links\n  MT(\"linkReference\",\n     \"[link [[foo]]][string&url [[bar]]] hello\");\n\n  // Reference-style links with Em\n  MT(\"linkReferenceEm\",\n     \"[link [[][link&em *foo*][link ]]][string&url [[bar]]] hello\");\n\n  // Reference-style links with Strong\n  MT(\"linkReferenceStrong\",\n     \"[link [[][link&strong **foo**][link ]]][string&url [[bar]]] hello\");\n\n  // Reference-style links with EmStrong\n  MT(\"linkReferenceEmStrong\",\n     \"[link [[][link&em&strong ***foo***][link ]]][string&url [[bar]]] hello\");\n\n  // Reference-style links with optional space separator (per documentation)\n  // \"You can optionally use a space to separate the sets of brackets\"\n  MT(\"linkReferenceSpace\",\n     \"[link [[foo]]] [string&url [[bar]]] hello\");\n\n  // Should only allow a single space (\"...use *a* space...\")\n  MT(\"linkReferenceDoubleSpace\",\n     \"[link [[foo]]]  [link [[bar]]] hello\");\n\n  // Reference-style links with implicit link name\n  MT(\"linkImplicit\",\n     \"[link [[foo]]][string&url [[]]] hello\");\n\n  // @todo It would be nice if, at some point, the document was actually\n  // checked to see if the referenced link exists\n\n  // Link label, for reference-style links (taken from documentation)\n\n  MT(\"labelNoTitle\",\n     \"[link [[foo]]:] [string&url http://example.com/]\");\n\n  MT(\"labelIndented\",\n     \"   [link [[foo]]:] [string&url http://example.com/]\");\n\n  MT(\"labelSpaceTitle\",\n     \"[link [[foo bar]]:] [string&url http://example.com/ \\\"hello\\\"]\");\n\n  MT(\"labelDoubleTitle\",\n     \"[link [[foo bar]]:] [string&url http://example.com/ \\\"hello\\\"] \\\"world\\\"\");\n\n  MT(\"labelTitleDoubleQuotes\",\n     \"[link [[foo]]:] [string&url http://example.com/  \\\"bar\\\"]\");\n\n  MT(\"labelTitleSingleQuotes\",\n     \"[link [[foo]]:] [string&url http://example.com/  'bar']\");\n\n  MT(\"labelTitleParentheses\",\n     \"[link [[foo]]:] [string&url http://example.com/  (bar)]\");\n\n  MT(\"labelTitleInvalid\",\n     \"[link [[foo]]:] [string&url http://example.com/] bar\");\n\n  MT(\"labelLinkAngleBrackets\",\n     \"[link [[foo]]:] [string&url <http://example.com/>  \\\"bar\\\"]\");\n\n  MT(\"labelTitleNextDoubleQuotes\",\n     \"[link [[foo]]:] [string&url http://example.com/]\",\n     \"[string \\\"bar\\\"] hello\");\n\n  MT(\"labelTitleNextSingleQuotes\",\n     \"[link [[foo]]:] [string&url http://example.com/]\",\n     \"[string 'bar'] hello\");\n\n  MT(\"labelTitleNextParentheses\",\n     \"[link [[foo]]:] [string&url http://example.com/]\",\n     \"[string (bar)] hello\");\n\n  MT(\"labelTitleNextMixed\",\n     \"[link [[foo]]:] [string&url http://example.com/]\",\n     \"(bar\\\" hello\");\n\n  MT(\"labelEscape\",\n     \"[link [[foo \\\\]] ]]:] [string&url http://example.com/]\");\n\n  MT(\"labelEscapeColon\",\n     \"[link [[foo \\\\]]: bar]]:] [string&url http://example.com/]\");\n\n  MT(\"labelEscapeEnd\",\n     \"\\\\[[foo\\\\]]: http://example.com/\");\n\n  MT(\"linkWeb\",\n     \"[link <http://example.com/>] foo\");\n\n  MT(\"linkWebDouble\",\n     \"[link <http://example.com/>] foo [link <http://example.com/>]\");\n\n  MT(\"linkEmail\",\n     \"[link <user@example.com>] foo\");\n\n  MT(\"linkEmailDouble\",\n     \"[link <user@example.com>] foo [link <user@example.com>]\");\n\n  MT(\"emAsterisk\",\n     \"[em *foo*] bar\");\n\n  MT(\"emUnderscore\",\n     \"[em _foo_] bar\");\n\n  MT(\"emInWordAsterisk\",\n     \"foo[em *bar*]hello\");\n\n  MT(\"emInWordUnderscore\",\n     \"foo_bar_hello\");\n\n  // Per documentation: \"...surround an * or _ with spaces, it’ll be\n  // treated as a literal asterisk or underscore.\"\n\n  MT(\"emEscapedBySpaceIn\",\n     \"foo [em _bar _ hello_] world\");\n\n  MT(\"emEscapedBySpaceOut\",\n     \"foo _ bar [em _hello_] world\");\n\n  MT(\"emEscapedByNewline\",\n     \"foo\",\n     \"_ bar [em _hello_] world\");\n\n  // Unclosed emphasis characters\n  // Instead of simply marking as EM / STRONG, it would be nice to have an\n  // incomplete flag for EM and STRONG, that is styled slightly different.\n  MT(\"emIncompleteAsterisk\",\n     \"foo [em *bar]\");\n\n  MT(\"emIncompleteUnderscore\",\n     \"foo [em _bar]\");\n\n  MT(\"strongAsterisk\",\n     \"[strong **foo**] bar\");\n\n  MT(\"strongUnderscore\",\n     \"[strong __foo__] bar\");\n\n  MT(\"emStrongAsterisk\",\n     \"[em *foo][em&strong **bar*][strong hello**] world\");\n\n  MT(\"emStrongUnderscore\",\n     \"[em _foo ][em&strong __bar_][strong  hello__] world\");\n\n  // \"...same character must be used to open and close an emphasis span.\"\"\n  MT(\"emStrongMixed\",\n     \"[em _foo][em&strong **bar*hello__ world]\");\n\n  MT(\"emStrongMixed\",\n     \"[em *foo ][em&strong __bar_hello** world]\");\n\n  MT(\"linkWithNestedParens\",\n     \"[link [[foo]]][string&url (bar(baz))]\")\n\n  // These characters should be escaped:\n  // \\   backslash\n  // `   backtick\n  // *   asterisk\n  // _   underscore\n  // {}  curly braces\n  // []  square brackets\n  // ()  parentheses\n  // #   hash mark\n  // +   plus sign\n  // -   minus sign (hyphen)\n  // .   dot\n  // !   exclamation mark\n\n  MT(\"escapeBacktick\",\n     \"foo \\\\`bar\\\\`\");\n\n  MT(\"doubleEscapeBacktick\",\n     \"foo \\\\\\\\[comment `bar\\\\\\\\`]\");\n\n  MT(\"escapeAsterisk\",\n     \"foo \\\\*bar\\\\*\");\n\n  MT(\"doubleEscapeAsterisk\",\n     \"foo \\\\\\\\[em *bar\\\\\\\\*]\");\n\n  MT(\"escapeUnderscore\",\n     \"foo \\\\_bar\\\\_\");\n\n  MT(\"doubleEscapeUnderscore\",\n     \"foo \\\\\\\\[em _bar\\\\\\\\_]\");\n\n  MT(\"escapeHash\",\n     \"\\\\# foo\");\n\n  MT(\"doubleEscapeHash\",\n     \"\\\\\\\\# foo\");\n\n  MT(\"escapeNewline\",\n     \"\\\\\",\n     \"[em *foo*]\");\n\n  // Class override tests\n  TokenTypeOverrideTest(\"overrideHeader1\",\n    \"[override-header&override-header-1 # Foo]\");\n\n  TokenTypeOverrideTest(\"overrideHeader2\",\n    \"[override-header&override-header-2 ## Foo]\");\n\n  TokenTypeOverrideTest(\"overrideHeader3\",\n    \"[override-header&override-header-3 ### Foo]\");\n\n  TokenTypeOverrideTest(\"overrideHeader4\",\n    \"[override-header&override-header-4 #### Foo]\");\n\n  TokenTypeOverrideTest(\"overrideHeader5\",\n    \"[override-header&override-header-5 ##### Foo]\");\n\n  TokenTypeOverrideTest(\"overrideHeader6\",\n    \"[override-header&override-header-6 ###### Foo]\");\n\n  TokenTypeOverrideTest(\"overrideCode\",\n    \"[override-code `foo`]\");\n\n  TokenTypeOverrideTest(\"overrideCodeBlock\",\n    \"[override-code ```]\",\n    \"[override-code foo]\",\n    \"[override-code ```]\");\n\n  TokenTypeOverrideTest(\"overrideQuote\",\n    \"[override-quote&override-quote-1 > foo]\",\n    \"[override-quote&override-quote-1 > bar]\");\n\n  TokenTypeOverrideTest(\"overrideQuoteNested\",\n    \"[override-quote&override-quote-1 > foo]\",\n    \"[override-quote&override-quote-1 >][override-quote&override-quote-2 > bar]\",\n    \"[override-quote&override-quote-1 >][override-quote&override-quote-2 >][override-quote&override-quote-3 > baz]\");\n\n  TokenTypeOverrideTest(\"overrideLists\",\n    \"[override-list1 - foo]\",\n    \"\",\n    \"    [override-list2 + bar]\",\n    \"\",\n    \"        [override-list3 * baz]\",\n    \"\",\n    \"            [override-list1 1. qux]\",\n    \"\",\n    \"                [override-list2 - quux]\");\n\n  TokenTypeOverrideTest(\"overrideHr\",\n    \"[override-hr * * *]\");\n\n  TokenTypeOverrideTest(\"overrideImage\",\n    \"[override-image&override-image-marker !][override-image&override-image-alt-text&link [[alt text]]][override-link-href&url (http://link.to/image.jpg)]\");\n\n  TokenTypeOverrideTest(\"overrideLinkText\",\n    \"[override-link-text [[foo]]][override-link-href&url (http://example.com)]\");\n\n  TokenTypeOverrideTest(\"overrideLinkEmailAndInline\",\n    \"[override-link-email <][override-link-inline foo@example.com>]\");\n\n  TokenTypeOverrideTest(\"overrideEm\",\n    \"[override-em *foo*]\");\n\n  TokenTypeOverrideTest(\"overrideStrong\",\n    \"[override-strong **foo**]\");\n\n  TokenTypeOverrideTest(\"overrideStrikethrough\",\n    \"[override-strikethrough ~~foo~~]\");\n\n  TokenTypeOverrideTest(\"overrideEmoji\",\n    \"[override-emoji :foo:]\");\n\n  FormatTokenTypeOverrideTest(\"overrideFormatting\",\n    \"[override-formatting-escape \\\\*]\");\n\n  // Tests to make sure GFM-specific things aren't getting through\n\n  MT(\"taskList\",\n     \"[variable-2 * ][link&variable-2 [[ ]]][variable-2 bar]\");\n\n  MT(\"fencedCodeBlocks\",\n     \"[comment ```]\",\n     \"[comment foo]\",\n     \"\",\n     \"[comment bar]\",\n     \"[comment ```]\",\n     \"baz\");\n\n  MT(\"fencedCodeBlocks_invalidClosingFence_trailingText\",\n     \"[comment ```]\",\n     \"[comment foo]\",\n     \"[comment ``` must not have trailing text]\",\n     \"[comment baz]\");\n\n  MT(\"fencedCodeBlocks_invalidClosingFence_trailingTabs\",\n     \"[comment ```]\",\n     \"[comment foo]\",\n     \"[comment ```\\t]\",\n     \"[comment baz]\");\n\n  MT(\"fencedCodeBlocks_validClosingFence\",\n     \"[comment ```]\",\n     \"[comment foo]\",\n     // may have trailing spaces\n     \"[comment ```     ]\",\n     \"baz\");\n\n  MT(\"fencedCodeBlocksInList_closingFenceIndented\",\n     \"[variable-2 - list]\",\n     \"    [variable-2&comment ```]\",\n     \"    [comment foo]\",\n     \"     [variable-2&comment ```]\",\n     \"    [variable-2 baz]\");\n\n  MT(\"fencedCodeBlocksInList_closingFenceIndentedTooMuch\",\n     \"[variable-2 - list]\",\n     \"    [variable-2&comment ```]\",\n     \"    [comment foo]\",\n     \"      [comment ```]\",\n     \"    [comment baz]\");\n\n  MT(\"fencedCodeBlockModeSwitching\",\n     \"[comment ```javascript]\",\n     \"[variable foo]\",\n     \"\",\n     \"[comment ```]\",\n     \"bar\");\n\n  MT_noFencedHighlight(\"fencedCodeBlock_noHighlight\",\n     \"[comment ```javascript]\",\n     \"[comment foo]\",\n     \"[comment ```]\");\n\n  MT(\"fencedCodeBlockModeSwitchingObjc\",\n     \"[comment ```objective-c]\",\n     \"[keyword @property] [variable NSString] [operator *] [variable foo];\",\n     \"[comment ```]\",\n     \"bar\");\n\n  MT(\"fencedCodeBlocksMultipleChars\",\n     \"[comment `````]\",\n     \"[comment foo]\",\n     \"[comment ```]\",\n     \"[comment foo]\",\n     \"[comment `````]\",\n     \"bar\");\n\n  MT(\"fencedCodeBlocksTildes\",\n     \"[comment ~~~]\",\n     \"[comment foo]\",\n     \"[comment ~~~]\",\n     \"bar\");\n\n  MT(\"fencedCodeBlocksTildesMultipleChars\",\n     \"[comment ~~~~~]\",\n     \"[comment ~~~]\",\n     \"[comment foo]\",\n     \"[comment ~~~~~]\",\n     \"bar\");\n\n  MT(\"fencedCodeBlocksMultipleChars\",\n     \"[comment `````]\",\n     \"[comment foo]\",\n     \"[comment ```]\",\n     \"[comment foo]\",\n     \"[comment `````]\",\n     \"bar\");\n\n  MT(\"fencedCodeBlocksMixed\",\n     \"[comment ~~~]\",\n     \"[comment ```]\",\n     \"[comment foo]\",\n     \"[comment ~~~]\",\n     \"bar\");\n\n  MT(\"fencedCodeBlocksAfterBlockquote\",\n     \"[quote&quote-1 > foo]\",\n     \"[comment ```]\",\n     \"[comment bar]\",\n     \"[comment ```]\");\n\n  // fencedCode indented too much should act as simple indentedCode\n  //  (hence has no highlight formatting)\n  FT(\"tooMuchIndentedFencedCode\",\n     \"    [comment ```]\",\n     \"    [comment code]\",\n     \"    [comment ```]\");\n\n  MT(\"autoTerminateFencedCodeWhenLeavingList\",\n     \"[variable-2 - list1]\",\n     \"  [variable-3 - list2]\",\n     \"    [variable-3&comment ```]\",\n     \"    [comment code]\",\n     \"  [variable-3 - list2]\",\n     \"  [variable-2&comment ```]\",\n     \"  [comment code]\",\n     \"[quote&quote-1 > foo]\");\n\n  // Tests that require XML mode\n\n  MT(\"xmlMode\",\n     \"[tag&bracket <][tag div][tag&bracket >]\",\n     \"  *foo*\",\n     \"  [tag&bracket <][tag http://github.com][tag&bracket />]\",\n     \"[tag&bracket </][tag div][tag&bracket >]\",\n     \"[link <http://github.com/>]\");\n\n  MT(\"xmlModeWithMarkdownInside\",\n     \"[tag&bracket <][tag div] [attribute markdown]=[string 1][tag&bracket >]\",\n     \"[em *foo*]\",\n     \"[link <http://github.com/>]\",\n     \"[tag </div>]\",\n     \"[link <http://github.com/>]\",\n     \"[tag&bracket <][tag div][tag&bracket >]\",\n     \"[tag&bracket </][tag div][tag&bracket >]\");\n\n  MT(\"xmlModeLineBreakInTags\",\n     \"[tag&bracket <][tag div] [attribute id]=[string \\\"1\\\"]\",\n     \"     [attribute class]=[string \\\"sth\\\"][tag&bracket >]xxx\",\n     \"[tag&bracket </][tag div][tag&bracket >]\");\n\n  MT(\"xmlModeCommentWithBlankLine\",\n     \"[comment <!-- Hello]\",\n     \"\",\n     \"[comment World -->]\");\n\n  MT(\"xmlModeCDATA\",\n     \"[atom <![CDATA[ Hello]\",\n     \"\",\n     \"[atom FooBar]\",\n     \"[atom Test ]]]]>]\");\n\n  MT(\"xmlModePreprocessor\",\n     \"[meta <?php] [meta echo '1234'; ?>]\");\n\n  MT_noXml(\"xmlHighlightDisabled\",\n     \"<div>foo</div>\");\n\n  // Tests Emojis\n\n  ET(\"emojiDefault\",\n    \"[builtin :foobar:]\");\n\n  ET(\"emojiTable\",\n    \" :--:\");\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mathematica/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Mathematica mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=stylesheet href=../../lib/codemirror.css>\n<script src=../../lib/codemirror.js></script>\n<script src=../../addon/edit/matchbrackets.js></script>\n<script src=mathematica.js></script>\n<style type=text/css>\n  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Mathematica</a>\n  </ul>\n</div>\n\n<article>\n<h2>Mathematica mode</h2>\n\n\n<textarea id=\"mathematicaCode\">\n(* example Mathematica code *)\n(* Dualisiert wird anhand einer Polarität an einer\n   Quadrik $x^t Q x = 0$ mit regulärer Matrix $Q$ (also\n   mit $det(Q) \\neq 0$), z.B. die Identitätsmatrix.\n   $p$ ist eine Liste von Polynomen - ein Ideal. *)\ndualize::\"singular\" = \"Q must be regular: found Det[Q]==0.\";\ndualize[ Q_, p_ ] := Block[\n    { m, n, xv, lv, uv, vars, polys, dual },\n    If[Det[Q] == 0,\n      Message[dualize::\"singular\"],\n      m = Length[p];\n      n = Length[Q] - 1;\n      xv = Table[Subscript[x, i], {i, 0, n}];\n      lv = Table[Subscript[l, i], {i, 1, m}];\n      uv = Table[Subscript[u, i], {i, 0, n}];\n      (* Konstruiere Ideal polys. *)\n      If[m == 0,\n        polys = Q.uv,\n        polys = Join[p, Q.uv - Transpose[Outer[D, p, xv]].lv]\n        ];\n      (* Eliminiere die ersten n + 1 + m Variablen xv und lv\n         aus dem Ideal polys. *)\n      vars = Join[xv, lv];\n      dual = GroebnerBasis[polys, uv, vars];\n      (* Ersetze u mit x im Ergebnis. *)\n      ReplaceAll[dual, Rule[u, x]]\n      ]\n    ]\n</textarea>\n\n<script>\n  var mathematicaEditor = CodeMirror.fromTextArea(document.getElementById('mathematicaCode'), {\n    mode: 'text/x-mathematica',\n    lineNumbers: true,\n    matchBrackets: true\n  });\n</script>\n\n<p><strong>MIME types defined:</strong> <code>text/x-mathematica</code> (Mathematica).</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mathematica/mathematica.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Mathematica mode copyright (c) 2015 by Calin Barbat\n// Based on code by Patrick Scheibe (halirutan)\n// See: https://github.com/halirutan/Mathematica-Source-Highlighting/tree/master/src/lang-mma.js\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode('mathematica', function(_config, _parserConfig) {\n\n  // used pattern building blocks\n  var Identifier = '[a-zA-Z\\\\$][a-zA-Z0-9\\\\$]*';\n  var pBase      = \"(?:\\\\d+)\";\n  var pFloat     = \"(?:\\\\.\\\\d+|\\\\d+\\\\.\\\\d*|\\\\d+)\";\n  var pFloatBase = \"(?:\\\\.\\\\w+|\\\\w+\\\\.\\\\w*|\\\\w+)\";\n  var pPrecision = \"(?:`(?:`?\"+pFloat+\")?)\";\n\n  // regular expressions\n  var reBaseForm        = new RegExp('(?:'+pBase+'(?:\\\\^\\\\^'+pFloatBase+pPrecision+'?(?:\\\\*\\\\^[+-]?\\\\d+)?))');\n  var reFloatForm       = new RegExp('(?:' + pFloat + pPrecision + '?(?:\\\\*\\\\^[+-]?\\\\d+)?)');\n  var reIdInContext     = new RegExp('(?:`?)(?:' + Identifier + ')(?:`(?:' + Identifier + '))*(?:`?)');\n\n  function tokenBase(stream, state) {\n    var ch;\n\n    // get next character\n    ch = stream.next();\n\n    // string\n    if (ch === '\"') {\n      state.tokenize = tokenString;\n      return state.tokenize(stream, state);\n    }\n\n    // comment\n    if (ch === '(') {\n      if (stream.eat('*')) {\n        state.commentLevel++;\n        state.tokenize = tokenComment;\n        return state.tokenize(stream, state);\n      }\n    }\n\n    // go back one character\n    stream.backUp(1);\n\n    // look for numbers\n    // Numbers in a baseform\n    if (stream.match(reBaseForm, true, false)) {\n      return 'number';\n    }\n\n    // Mathematica numbers. Floats (1.2, .2, 1.) can have optionally a precision (`float) or an accuracy definition\n    // (``float). Note: while 1.2` is possible 1.2`` is not. At the end an exponent (float*^+12) can follow.\n    if (stream.match(reFloatForm, true, false)) {\n      return 'number';\n    }\n\n    /* In[23] and Out[34] */\n    if (stream.match(/(?:In|Out)\\[[0-9]*\\]/, true, false)) {\n      return 'atom';\n    }\n\n    // usage\n    if (stream.match(/([a-zA-Z\\$][a-zA-Z0-9\\$]*(?:`[a-zA-Z0-9\\$]+)*::usage)/, true, false)) {\n      return 'meta';\n    }\n\n    // message\n    if (stream.match(/([a-zA-Z\\$][a-zA-Z0-9\\$]*(?:`[a-zA-Z0-9\\$]+)*::[a-zA-Z\\$][a-zA-Z0-9\\$]*):?/, true, false)) {\n      return 'string-2';\n    }\n\n    // this makes a look-ahead match for something like variable:{_Integer}\n    // the match is then forwarded to the mma-patterns tokenizer.\n    if (stream.match(/([a-zA-Z\\$][a-zA-Z0-9\\$]*\\s*:)(?:(?:[a-zA-Z\\$][a-zA-Z0-9\\$]*)|(?:[^:=>~@\\^\\&\\*\\)\\[\\]'\\?,\\|])).*/, true, false)) {\n      return 'variable-2';\n    }\n\n    // catch variables which are used together with Blank (_), BlankSequence (__) or BlankNullSequence (___)\n    // Cannot start with a number, but can have numbers at any other position. Examples\n    // blub__Integer, a1_, b34_Integer32\n    if (stream.match(/[a-zA-Z\\$][a-zA-Z0-9\\$]*_+[a-zA-Z\\$][a-zA-Z0-9\\$]*/, true, false)) {\n      return 'variable-2';\n    }\n    if (stream.match(/[a-zA-Z\\$][a-zA-Z0-9\\$]*_+/, true, false)) {\n      return 'variable-2';\n    }\n    if (stream.match(/_+[a-zA-Z\\$][a-zA-Z0-9\\$]*/, true, false)) {\n      return 'variable-2';\n    }\n\n    // Named characters in Mathematica, like \\[Gamma].\n    if (stream.match(/\\\\\\[[a-zA-Z\\$][a-zA-Z0-9\\$]*\\]/, true, false)) {\n      return 'variable-3';\n    }\n\n    // Match all braces separately\n    if (stream.match(/(?:\\[|\\]|{|}|\\(|\\))/, true, false)) {\n      return 'bracket';\n    }\n\n    // Catch Slots (#, ##, #3, ##9 and the V10 named slots #name). I have never seen someone using more than one digit after #, so we match\n    // only one.\n    if (stream.match(/(?:#[a-zA-Z\\$][a-zA-Z0-9\\$]*|#+[0-9]?)/, true, false)) {\n      return 'variable-2';\n    }\n\n    // Literals like variables, keywords, functions\n    if (stream.match(reIdInContext, true, false)) {\n      return 'keyword';\n    }\n\n    // operators. Note that operators like @@ or /; are matched separately for each symbol.\n    if (stream.match(/(?:\\\\|\\+|\\-|\\*|\\/|,|;|\\.|:|@|~|=|>|<|&|\\||_|`|'|\\^|\\?|!|%)/, true, false)) {\n      return 'operator';\n    }\n\n    // everything else is an error\n    stream.next(); // advance the stream.\n    return 'error';\n  }\n\n  function tokenString(stream, state) {\n    var next, end = false, escaped = false;\n    while ((next = stream.next()) != null) {\n      if (next === '\"' && !escaped) {\n        end = true;\n        break;\n      }\n      escaped = !escaped && next === '\\\\';\n    }\n    if (end && !escaped) {\n      state.tokenize = tokenBase;\n    }\n    return 'string';\n  };\n\n  function tokenComment(stream, state) {\n    var prev, next;\n    while(state.commentLevel > 0 && (next = stream.next()) != null) {\n      if (prev === '(' && next === '*') state.commentLevel++;\n      if (prev === '*' && next === ')') state.commentLevel--;\n      prev = next;\n    }\n    if (state.commentLevel <= 0) {\n      state.tokenize = tokenBase;\n    }\n    return 'comment';\n  }\n\n  return {\n    startState: function() {return {tokenize: tokenBase, commentLevel: 0};},\n    token: function(stream, state) {\n      if (stream.eatSpace()) return null;\n      return state.tokenize(stream, state);\n    },\n    blockCommentStart: \"(*\",\n    blockCommentEnd: \"*)\"\n  };\n});\n\nCodeMirror.defineMIME('text/x-mathematica', {\n  name: 'mathematica'\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mbox/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: mbox mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"mbox.js\"></script>\n<style>.CodeMirror { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">mbox</a>\n  </ul>\n</div>\n\n<article>\n<h2>mbox mode</h2>\n<form><textarea id=\"code\" name=\"code\">\nFrom timothygu99@gmail.com Sun Apr 17 01:40:43 2016\nFrom: Timothy Gu &lt;timothygu99@gmail.com&gt;\nDate: Sat, 16 Apr 2016 18:40:43 -0700\nSubject: mbox mode\nMessage-ID: &lt;Z8d+bTT50U/az94FZnyPkDjZmW0=@gmail.com&gt;\n\nmbox mode is working!\n\nTimothy\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>application/mbox</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mbox/mbox.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nvar rfc2822 = [\n  \"From\", \"Sender\", \"Reply-To\", \"To\", \"Cc\", \"Bcc\", \"Message-ID\",\n  \"In-Reply-To\", \"References\", \"Resent-From\", \"Resent-Sender\", \"Resent-To\",\n  \"Resent-Cc\", \"Resent-Bcc\", \"Resent-Message-ID\", \"Return-Path\", \"Received\"\n];\nvar rfc2822NoEmail = [\n  \"Date\", \"Subject\", \"Comments\", \"Keywords\", \"Resent-Date\"\n];\n\nCodeMirror.registerHelper(\"hintWords\", \"mbox\", rfc2822.concat(rfc2822NoEmail));\n\nvar whitespace = /^[ \\t]/;\nvar separator = /^From /; // See RFC 4155\nvar rfc2822Header = new RegExp(\"^(\" + rfc2822.join(\"|\") + \"): \");\nvar rfc2822HeaderNoEmail = new RegExp(\"^(\" + rfc2822NoEmail.join(\"|\") + \"): \");\nvar header = /^[^:]+:/; // Optional fields defined in RFC 2822\nvar email = /^[^ ]+@[^ ]+/;\nvar untilEmail = /^.*?(?=[^ ]+?@[^ ]+)/;\nvar bracketedEmail = /^<.*?>/;\nvar untilBracketedEmail = /^.*?(?=<.*>)/;\n\nfunction styleForHeader(header) {\n  if (header === \"Subject\") return \"header\";\n  return \"string\";\n}\n\nfunction readToken(stream, state) {\n  if (stream.sol()) {\n    // From last line\n    state.inSeparator = false;\n    if (state.inHeader && stream.match(whitespace)) {\n      // Header folding\n      return null;\n    } else {\n      state.inHeader = false;\n      state.header = null;\n    }\n\n    if (stream.match(separator)) {\n      state.inHeaders = true;\n      state.inSeparator = true;\n      return \"atom\";\n    }\n\n    var match;\n    var emailPermitted = false;\n    if ((match = stream.match(rfc2822HeaderNoEmail)) ||\n        (emailPermitted = true) && (match = stream.match(rfc2822Header))) {\n      state.inHeaders = true;\n      state.inHeader = true;\n      state.emailPermitted = emailPermitted;\n      state.header = match[1];\n      return \"atom\";\n    }\n\n    // Use vim's heuristics: recognize custom headers only if the line is in a\n    // block of legitimate headers.\n    if (state.inHeaders && (match = stream.match(header))) {\n      state.inHeader = true;\n      state.emailPermitted = true;\n      state.header = match[1];\n      return \"atom\";\n    }\n\n    state.inHeaders = false;\n    stream.skipToEnd();\n    return null;\n  }\n\n  if (state.inSeparator) {\n    if (stream.match(email)) return \"link\";\n    if (stream.match(untilEmail)) return \"atom\";\n    stream.skipToEnd();\n    return \"atom\";\n  }\n\n  if (state.inHeader) {\n    var style = styleForHeader(state.header);\n\n    if (state.emailPermitted) {\n      if (stream.match(bracketedEmail)) return style + \" link\";\n      if (stream.match(untilBracketedEmail)) return style;\n    }\n    stream.skipToEnd();\n    return style;\n  }\n\n  stream.skipToEnd();\n  return null;\n};\n\nCodeMirror.defineMode(\"mbox\", function() {\n  return {\n    startState: function() {\n      return {\n        // Is in a mbox separator\n        inSeparator: false,\n        // Is in a mail header\n        inHeader: false,\n        // If bracketed email is permitted. Only applicable when inHeader\n        emailPermitted: false,\n        // Name of current header\n        header: null,\n        // Is in a region of mail headers\n        inHeaders: false\n      };\n    },\n    token: readToken,\n    blankLine: function(state) {\n      state.inHeaders = state.inSeparator = state.inHeader = false;\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"application/mbox\", \"mbox\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/meta.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.modeInfo = [\n    {name: \"APL\", mime: \"text/apl\", mode: \"apl\", ext: [\"dyalog\", \"apl\"]},\n    {name: \"PGP\", mimes: [\"application/pgp\", \"application/pgp-encrypted\", \"application/pgp-keys\", \"application/pgp-signature\"], mode: \"asciiarmor\", ext: [\"asc\", \"pgp\", \"sig\"]},\n    {name: \"ASN.1\", mime: \"text/x-ttcn-asn\", mode: \"asn.1\", ext: [\"asn\", \"asn1\"]},\n    {name: \"Asterisk\", mime: \"text/x-asterisk\", mode: \"asterisk\", file: /^extensions\\.conf$/i},\n    {name: \"Brainfuck\", mime: \"text/x-brainfuck\", mode: \"brainfuck\", ext: [\"b\", \"bf\"]},\n    {name: \"C\", mime: \"text/x-csrc\", mode: \"clike\", ext: [\"c\", \"h\", \"ino\"]},\n    {name: \"C++\", mime: \"text/x-c++src\", mode: \"clike\", ext: [\"cpp\", \"c++\", \"cc\", \"cxx\", \"hpp\", \"h++\", \"hh\", \"hxx\"], alias: [\"cpp\"]},\n    {name: \"Cobol\", mime: \"text/x-cobol\", mode: \"cobol\", ext: [\"cob\", \"cpy\", \"cbl\"]},\n    {name: \"C#\", mime: \"text/x-csharp\", mode: \"clike\", ext: [\"cs\"], alias: [\"csharp\", \"cs\"]},\n    {name: \"Clojure\", mime: \"text/x-clojure\", mode: \"clojure\", ext: [\"clj\", \"cljc\", \"cljx\"]},\n    {name: \"ClojureScript\", mime: \"text/x-clojurescript\", mode: \"clojure\", ext: [\"cljs\"]},\n    {name: \"Closure Stylesheets (GSS)\", mime: \"text/x-gss\", mode: \"css\", ext: [\"gss\"]},\n    {name: \"CMake\", mime: \"text/x-cmake\", mode: \"cmake\", ext: [\"cmake\", \"cmake.in\"], file: /^CMakeLists\\.txt$/},\n    {name: \"CoffeeScript\", mimes: [\"application/vnd.coffeescript\", \"text/coffeescript\", \"text/x-coffeescript\"], mode: \"coffeescript\", ext: [\"coffee\"], alias: [\"coffee\", \"coffee-script\"]},\n    {name: \"Common Lisp\", mime: \"text/x-common-lisp\", mode: \"commonlisp\", ext: [\"cl\", \"lisp\", \"el\"], alias: [\"lisp\"]},\n    {name: \"Cypher\", mime: \"application/x-cypher-query\", mode: \"cypher\", ext: [\"cyp\", \"cypher\"]},\n    {name: \"Cython\", mime: \"text/x-cython\", mode: \"python\", ext: [\"pyx\", \"pxd\", \"pxi\"]},\n    {name: \"Crystal\", mime: \"text/x-crystal\", mode: \"crystal\", ext: [\"cr\"]},\n    {name: \"CSS\", mime: \"text/css\", mode: \"css\", ext: [\"css\"]},\n    {name: \"CQL\", mime: \"text/x-cassandra\", mode: \"sql\", ext: [\"cql\"]},\n    {name: \"D\", mime: \"text/x-d\", mode: \"d\", ext: [\"d\"]},\n    {name: \"Dart\", mimes: [\"application/dart\", \"text/x-dart\"], mode: \"dart\", ext: [\"dart\"]},\n    {name: \"diff\", mime: \"text/x-diff\", mode: \"diff\", ext: [\"diff\", \"patch\"]},\n    {name: \"Django\", mime: \"text/x-django\", mode: \"django\"},\n    {name: \"Dockerfile\", mime: \"text/x-dockerfile\", mode: \"dockerfile\", file: /^Dockerfile$/},\n    {name: \"DTD\", mime: \"application/xml-dtd\", mode: \"dtd\", ext: [\"dtd\"]},\n    {name: \"Dylan\", mime: \"text/x-dylan\", mode: \"dylan\", ext: [\"dylan\", \"dyl\", \"intr\"]},\n    {name: \"EBNF\", mime: \"text/x-ebnf\", mode: \"ebnf\"},\n    {name: \"ECL\", mime: \"text/x-ecl\", mode: \"ecl\", ext: [\"ecl\"]},\n    {name: \"edn\", mime: \"application/edn\", mode: \"clojure\", ext: [\"edn\"]},\n    {name: \"Eiffel\", mime: \"text/x-eiffel\", mode: \"eiffel\", ext: [\"e\"]},\n    {name: \"Elm\", mime: \"text/x-elm\", mode: \"elm\", ext: [\"elm\"]},\n    {name: \"Embedded JavaScript\", mime: \"application/x-ejs\", mode: \"htmlembedded\", ext: [\"ejs\"]},\n    {name: \"Embedded Ruby\", mime: \"application/x-erb\", mode: \"htmlembedded\", ext: [\"erb\"]},\n    {name: \"Erlang\", mime: \"text/x-erlang\", mode: \"erlang\", ext: [\"erl\"]},\n    {name: \"Esper\", mime: \"text/x-esper\", mode: \"sql\"},\n    {name: \"Factor\", mime: \"text/x-factor\", mode: \"factor\", ext: [\"factor\"]},\n    {name: \"FCL\", mime: \"text/x-fcl\", mode: \"fcl\"},\n    {name: \"Forth\", mime: \"text/x-forth\", mode: \"forth\", ext: [\"forth\", \"fth\", \"4th\"]},\n    {name: \"Fortran\", mime: \"text/x-fortran\", mode: \"fortran\", ext: [\"f\", \"for\", \"f77\", \"f90\", \"f95\"]},\n    {name: \"F#\", mime: \"text/x-fsharp\", mode: \"mllike\", ext: [\"fs\"], alias: [\"fsharp\"]},\n    {name: \"Gas\", mime: \"text/x-gas\", mode: \"gas\", ext: [\"s\"]},\n    {name: \"Gherkin\", mime: \"text/x-feature\", mode: \"gherkin\", ext: [\"feature\"]},\n    {name: \"GitHub Flavored Markdown\", mime: \"text/x-gfm\", mode: \"gfm\", file: /^(readme|contributing|history)\\.md$/i},\n    {name: \"Go\", mime: \"text/x-go\", mode: \"go\", ext: [\"go\"]},\n    {name: \"Groovy\", mime: \"text/x-groovy\", mode: \"groovy\", ext: [\"groovy\", \"gradle\"], file: /^Jenkinsfile$/},\n    {name: \"HAML\", mime: \"text/x-haml\", mode: \"haml\", ext: [\"haml\"]},\n    {name: \"Haskell\", mime: \"text/x-haskell\", mode: \"haskell\", ext: [\"hs\"]},\n    {name: \"Haskell (Literate)\", mime: \"text/x-literate-haskell\", mode: \"haskell-literate\", ext: [\"lhs\"]},\n    {name: \"Haxe\", mime: \"text/x-haxe\", mode: \"haxe\", ext: [\"hx\"]},\n    {name: \"HXML\", mime: \"text/x-hxml\", mode: \"haxe\", ext: [\"hxml\"]},\n    {name: \"ASP.NET\", mime: \"application/x-aspx\", mode: \"htmlembedded\", ext: [\"aspx\"], alias: [\"asp\", \"aspx\"]},\n    {name: \"HTML\", mime: \"text/html\", mode: \"htmlmixed\", ext: [\"html\", \"htm\", \"handlebars\", \"hbs\"], alias: [\"xhtml\"]},\n    {name: \"HTTP\", mime: \"message/http\", mode: \"http\"},\n    {name: \"IDL\", mime: \"text/x-idl\", mode: \"idl\", ext: [\"pro\"]},\n    {name: \"Pug\", mime: \"text/x-pug\", mode: \"pug\", ext: [\"jade\", \"pug\"], alias: [\"jade\"]},\n    {name: \"Java\", mime: \"text/x-java\", mode: \"clike\", ext: [\"java\"]},\n    {name: \"Java Server Pages\", mime: \"application/x-jsp\", mode: \"htmlembedded\", ext: [\"jsp\"], alias: [\"jsp\"]},\n    {name: \"JavaScript\", mimes: [\"text/javascript\", \"text/ecmascript\", \"application/javascript\", \"application/x-javascript\", \"application/ecmascript\"],\n     mode: \"javascript\", ext: [\"js\"], alias: [\"ecmascript\", \"js\", \"node\"]},\n    {name: \"JSON\", mimes: [\"application/json\", \"application/x-json\"], mode: \"javascript\", ext: [\"json\", \"map\"], alias: [\"json5\"]},\n    {name: \"JSON-LD\", mime: \"application/ld+json\", mode: \"javascript\", ext: [\"jsonld\"], alias: [\"jsonld\"]},\n    {name: \"JSX\", mime: \"text/jsx\", mode: \"jsx\", ext: [\"jsx\"]},\n    {name: \"Jinja2\", mime: \"text/jinja2\", mode: \"jinja2\", ext: [\"j2\", \"jinja\", \"jinja2\"]},\n    {name: \"Julia\", mime: \"text/x-julia\", mode: \"julia\", ext: [\"jl\"], alias: [\"jl\"]},\n    {name: \"Kotlin\", mime: \"text/x-kotlin\", mode: \"clike\", ext: [\"kt\"]},\n    {name: \"LESS\", mime: \"text/x-less\", mode: \"css\", ext: [\"less\"]},\n    {name: \"LiveScript\", mime: \"text/x-livescript\", mode: \"livescript\", ext: [\"ls\"], alias: [\"ls\"]},\n    {name: \"Lua\", mime: \"text/x-lua\", mode: \"lua\", ext: [\"lua\"]},\n    {name: \"Markdown\", mime: \"text/x-markdown\", mode: \"markdown\", ext: [\"markdown\", \"md\", \"mkd\"]},\n    {name: \"mIRC\", mime: \"text/mirc\", mode: \"mirc\"},\n    {name: \"MariaDB SQL\", mime: \"text/x-mariadb\", mode: \"sql\"},\n    {name: \"Mathematica\", mime: \"text/x-mathematica\", mode: \"mathematica\", ext: [\"m\", \"nb\", \"wl\", \"wls\"]},\n    {name: \"Modelica\", mime: \"text/x-modelica\", mode: \"modelica\", ext: [\"mo\"]},\n    {name: \"MUMPS\", mime: \"text/x-mumps\", mode: \"mumps\", ext: [\"mps\"]},\n    {name: \"MS SQL\", mime: \"text/x-mssql\", mode: \"sql\"},\n    {name: \"mbox\", mime: \"application/mbox\", mode: \"mbox\", ext: [\"mbox\"]},\n    {name: \"MySQL\", mime: \"text/x-mysql\", mode: \"sql\"},\n    {name: \"Nginx\", mime: \"text/x-nginx-conf\", mode: \"nginx\", file: /nginx.*\\.conf$/i},\n    {name: \"NSIS\", mime: \"text/x-nsis\", mode: \"nsis\", ext: [\"nsh\", \"nsi\"]},\n    {name: \"NTriples\", mimes: [\"application/n-triples\", \"application/n-quads\", \"text/n-triples\"],\n     mode: \"ntriples\", ext: [\"nt\", \"nq\"]},\n    {name: \"Objective-C\", mime: \"text/x-objectivec\", mode: \"clike\", ext: [\"m\"], alias: [\"objective-c\", \"objc\"]},\n    {name: \"Objective-C++\", mime: \"text/x-objectivec++\", mode: \"clike\", ext: [\"mm\"], alias: [\"objective-c++\", \"objc++\"]},\n    {name: \"OCaml\", mime: \"text/x-ocaml\", mode: \"mllike\", ext: [\"ml\", \"mli\", \"mll\", \"mly\"]},\n    {name: \"Octave\", mime: \"text/x-octave\", mode: \"octave\", ext: [\"m\"]},\n    {name: \"Oz\", mime: \"text/x-oz\", mode: \"oz\", ext: [\"oz\"]},\n    {name: \"Pascal\", mime: \"text/x-pascal\", mode: \"pascal\", ext: [\"p\", \"pas\"]},\n    {name: \"PEG.js\", mime: \"null\", mode: \"pegjs\", ext: [\"jsonld\"]},\n    {name: \"Perl\", mime: \"text/x-perl\", mode: \"perl\", ext: [\"pl\", \"pm\"]},\n    {name: \"PHP\", mimes: [\"text/x-php\", \"application/x-httpd-php\", \"application/x-httpd-php-open\"], mode: \"php\", ext: [\"php\", \"php3\", \"php4\", \"php5\", \"php7\", \"phtml\"]},\n    {name: \"Pig\", mime: \"text/x-pig\", mode: \"pig\", ext: [\"pig\"]},\n    {name: \"Plain Text\", mime: \"text/plain\", mode: \"null\", ext: [\"txt\", \"text\", \"conf\", \"def\", \"list\", \"log\"]},\n    {name: \"PLSQL\", mime: \"text/x-plsql\", mode: \"sql\", ext: [\"pls\"]},\n    {name: \"PostgreSQL\", mime: \"text/x-pgsql\", mode: \"sql\"},\n    {name: \"PowerShell\", mime: \"application/x-powershell\", mode: \"powershell\", ext: [\"ps1\", \"psd1\", \"psm1\"]},\n    {name: \"Properties files\", mime: \"text/x-properties\", mode: \"properties\", ext: [\"properties\", \"ini\", \"in\"], alias: [\"ini\", \"properties\"]},\n    {name: \"ProtoBuf\", mime: \"text/x-protobuf\", mode: \"protobuf\", ext: [\"proto\"]},\n    {name: \"Python\", mime: \"text/x-python\", mode: \"python\", ext: [\"BUILD\", \"bzl\", \"py\", \"pyw\"], file: /^(BUCK|BUILD)$/},\n    {name: \"Puppet\", mime: \"text/x-puppet\", mode: \"puppet\", ext: [\"pp\"]},\n    {name: \"Q\", mime: \"text/x-q\", mode: \"q\", ext: [\"q\"]},\n    {name: \"R\", mime: \"text/x-rsrc\", mode: \"r\", ext: [\"r\", \"R\"], alias: [\"rscript\"]},\n    {name: \"reStructuredText\", mime: \"text/x-rst\", mode: \"rst\", ext: [\"rst\"], alias: [\"rst\"]},\n    {name: \"RPM Changes\", mime: \"text/x-rpm-changes\", mode: \"rpm\"},\n    {name: \"RPM Spec\", mime: \"text/x-rpm-spec\", mode: \"rpm\", ext: [\"spec\"]},\n    {name: \"Ruby\", mime: \"text/x-ruby\", mode: \"ruby\", ext: [\"rb\"], alias: [\"jruby\", \"macruby\", \"rake\", \"rb\", \"rbx\"]},\n    {name: \"Rust\", mime: \"text/x-rustsrc\", mode: \"rust\", ext: [\"rs\"]},\n    {name: \"SAS\", mime: \"text/x-sas\", mode: \"sas\", ext: [\"sas\"]},\n    {name: \"Sass\", mime: \"text/x-sass\", mode: \"sass\", ext: [\"sass\"]},\n    {name: \"Scala\", mime: \"text/x-scala\", mode: \"clike\", ext: [\"scala\"]},\n    {name: \"Scheme\", mime: \"text/x-scheme\", mode: \"scheme\", ext: [\"scm\", \"ss\"]},\n    {name: \"SCSS\", mime: \"text/x-scss\", mode: \"css\", ext: [\"scss\"]},\n    {name: \"Shell\", mimes: [\"text/x-sh\", \"application/x-sh\"], mode: \"shell\", ext: [\"sh\", \"ksh\", \"bash\"], alias: [\"bash\", \"sh\", \"zsh\"], file: /^PKGBUILD$/},\n    {name: \"Sieve\", mime: \"application/sieve\", mode: \"sieve\", ext: [\"siv\", \"sieve\"]},\n    {name: \"Slim\", mimes: [\"text/x-slim\", \"application/x-slim\"], mode: \"slim\", ext: [\"slim\"]},\n    {name: \"Smalltalk\", mime: \"text/x-stsrc\", mode: \"smalltalk\", ext: [\"st\"]},\n    {name: \"Smarty\", mime: \"text/x-smarty\", mode: \"smarty\", ext: [\"tpl\"]},\n    {name: \"Solr\", mime: \"text/x-solr\", mode: \"solr\"},\n    {name: \"SML\", mime: \"text/x-sml\", mode: \"mllike\", ext: [\"sml\", \"sig\", \"fun\", \"smackspec\"]},\n    {name: \"Soy\", mime: \"text/x-soy\", mode: \"soy\", ext: [\"soy\"], alias: [\"closure template\"]},\n    {name: \"SPARQL\", mime: \"application/sparql-query\", mode: \"sparql\", ext: [\"rq\", \"sparql\"], alias: [\"sparul\"]},\n    {name: \"Spreadsheet\", mime: \"text/x-spreadsheet\", mode: \"spreadsheet\", alias: [\"excel\", \"formula\"]},\n    {name: \"SQL\", mime: \"text/x-sql\", mode: \"sql\", ext: [\"sql\"]},\n    {name: \"SQLite\", mime: \"text/x-sqlite\", mode: \"sql\"},\n    {name: \"Squirrel\", mime: \"text/x-squirrel\", mode: \"clike\", ext: [\"nut\"]},\n    {name: \"Stylus\", mime: \"text/x-styl\", mode: \"stylus\", ext: [\"styl\"]},\n    {name: \"Swift\", mime: \"text/x-swift\", mode: \"swift\", ext: [\"swift\"]},\n    {name: \"sTeX\", mime: \"text/x-stex\", mode: \"stex\"},\n    {name: \"LaTeX\", mime: \"text/x-latex\", mode: \"stex\", ext: [\"text\", \"ltx\", \"tex\"], alias: [\"tex\"]},\n    {name: \"SystemVerilog\", mime: \"text/x-systemverilog\", mode: \"verilog\", ext: [\"v\", \"sv\", \"svh\"]},\n    {name: \"Tcl\", mime: \"text/x-tcl\", mode: \"tcl\", ext: [\"tcl\"]},\n    {name: \"Textile\", mime: \"text/x-textile\", mode: \"textile\", ext: [\"textile\"]},\n    {name: \"TiddlyWiki\", mime: \"text/x-tiddlywiki\", mode: \"tiddlywiki\"},\n    {name: \"Tiki wiki\", mime: \"text/tiki\", mode: \"tiki\"},\n    {name: \"TOML\", mime: \"text/x-toml\", mode: \"toml\", ext: [\"toml\"]},\n    {name: \"Tornado\", mime: \"text/x-tornado\", mode: \"tornado\"},\n    {name: \"troff\", mime: \"text/troff\", mode: \"troff\", ext: [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\"]},\n    {name: \"TTCN\", mime: \"text/x-ttcn\", mode: \"ttcn\", ext: [\"ttcn\", \"ttcn3\", \"ttcnpp\"]},\n    {name: \"TTCN_CFG\", mime: \"text/x-ttcn-cfg\", mode: \"ttcn-cfg\", ext: [\"cfg\"]},\n    {name: \"Turtle\", mime: \"text/turtle\", mode: \"turtle\", ext: [\"ttl\"]},\n    {name: \"TypeScript\", mime: \"application/typescript\", mode: \"javascript\", ext: [\"ts\"], alias: [\"ts\"]},\n    {name: \"TypeScript-JSX\", mime: \"text/typescript-jsx\", mode: \"jsx\", ext: [\"tsx\"], alias: [\"tsx\"]},\n    {name: \"Twig\", mime: \"text/x-twig\", mode: \"twig\"},\n    {name: \"Web IDL\", mime: \"text/x-webidl\", mode: \"webidl\", ext: [\"webidl\"]},\n    {name: \"VB.NET\", mime: \"text/x-vb\", mode: \"vb\", ext: [\"vb\"]},\n    {name: \"VBScript\", mime: \"text/vbscript\", mode: \"vbscript\", ext: [\"vbs\"]},\n    {name: \"Velocity\", mime: \"text/velocity\", mode: \"velocity\", ext: [\"vtl\"]},\n    {name: \"Verilog\", mime: \"text/x-verilog\", mode: \"verilog\", ext: [\"v\"]},\n    {name: \"VHDL\", mime: \"text/x-vhdl\", mode: \"vhdl\", ext: [\"vhd\", \"vhdl\"]},\n    {name: \"Vue.js Component\", mimes: [\"script/x-vue\", \"text/x-vue\"], mode: \"vue\", ext: [\"vue\"]},\n    {name: \"XML\", mimes: [\"application/xml\", \"text/xml\"], mode: \"xml\", ext: [\"xml\", \"xsl\", \"xsd\", \"svg\"], alias: [\"rss\", \"wsdl\", \"xsd\"]},\n    {name: \"XQuery\", mime: \"application/xquery\", mode: \"xquery\", ext: [\"xy\", \"xquery\"]},\n    {name: \"Yacas\", mime: \"text/x-yacas\", mode: \"yacas\", ext: [\"ys\"]},\n    {name: \"YAML\", mimes: [\"text/x-yaml\", \"text/yaml\"], mode: \"yaml\", ext: [\"yaml\", \"yml\"], alias: [\"yml\"]},\n    {name: \"Z80\", mime: \"text/x-z80\", mode: \"z80\", ext: [\"z80\"]},\n    {name: \"mscgen\", mime: \"text/x-mscgen\", mode: \"mscgen\", ext: [\"mscgen\", \"mscin\", \"msc\"]},\n    {name: \"xu\", mime: \"text/x-xu\", mode: \"mscgen\", ext: [\"xu\"]},\n    {name: \"msgenny\", mime: \"text/x-msgenny\", mode: \"mscgen\", ext: [\"msgenny\"]},\n    {name: \"WebAssembly\", mime: \"text/webassembly\", mode: \"wast\", ext: [\"wat\", \"wast\"]},\n  ];\n  // Ensure all modes have a mime property for backwards compatibility\n  for (var i = 0; i < CodeMirror.modeInfo.length; i++) {\n    var info = CodeMirror.modeInfo[i];\n    if (info.mimes) info.mime = info.mimes[0];\n  }\n\n  CodeMirror.findModeByMIME = function(mime) {\n    mime = mime.toLowerCase();\n    for (var i = 0; i < CodeMirror.modeInfo.length; i++) {\n      var info = CodeMirror.modeInfo[i];\n      if (info.mime == mime) return info;\n      if (info.mimes) for (var j = 0; j < info.mimes.length; j++)\n        if (info.mimes[j] == mime) return info;\n    }\n    if (/\\+xml$/.test(mime)) return CodeMirror.findModeByMIME(\"application/xml\")\n    if (/\\+json$/.test(mime)) return CodeMirror.findModeByMIME(\"application/json\")\n  };\n\n  CodeMirror.findModeByExtension = function(ext) {\n    ext = ext.toLowerCase();\n    for (var i = 0; i < CodeMirror.modeInfo.length; i++) {\n      var info = CodeMirror.modeInfo[i];\n      if (info.ext) for (var j = 0; j < info.ext.length; j++)\n        if (info.ext[j] == ext) return info;\n    }\n  };\n\n  CodeMirror.findModeByFileName = function(filename) {\n    for (var i = 0; i < CodeMirror.modeInfo.length; i++) {\n      var info = CodeMirror.modeInfo[i];\n      if (info.file && info.file.test(filename)) return info;\n    }\n    var dot = filename.lastIndexOf(\".\");\n    var ext = dot > -1 && filename.substring(dot + 1, filename.length);\n    if (ext) return CodeMirror.findModeByExtension(ext);\n  };\n\n  CodeMirror.findModeByName = function(name) {\n    name = name.toLowerCase();\n    for (var i = 0; i < CodeMirror.modeInfo.length; i++) {\n      var info = CodeMirror.modeInfo[i];\n      if (info.name.toLowerCase() == name) return info;\n      if (info.alias) for (var j = 0; j < info.alias.length; j++)\n        if (info.alias[j].toLowerCase() == name) return info;\n    }\n  };\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mirc/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: mIRC mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/twilight.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"mirc.js\"></script>\n<style>.CodeMirror {border: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">mIRC</a>\n  </ul>\n</div>\n\n<article>\n<h2>mIRC mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n;AKA Nick Tracker by Ford_Lawnmower irc.GeekShed.net #Script-Help\n;*****************************************************************************;\n;**Start Setup\n;Change JoinDisplay, below, for On Join AKA Display. On = 1 - Off = 0\nalias -l JoinDisplay { return 1 }\n;Change MaxNicks, below, to the number of nicknames you want to store for each hostmask. I wouldn't go over 400 with this ;/\nalias -l MaxNicks { return 20 }\n;Change AKALogo, below, To the text you want displayed before each AKA result.\nalias -l AKALogo { return \u000306\u0007 \u000305A\u000306K\u000307A \u000306\u0007 }\n;**End Setup\n;*****************************************************************************;\nOn *:Join:#: {\n  if ($nick == $me) { .timer 1 1 ialupdateCheck $chan }\n  NickNamesAdd $nick $+($network,$wildsite)\n  if ($JoinDisplay) { .timerNickNames $+ $nick 1 2 NickNames.display $nick $chan $network $wildsite }\n}\non *:Nick: { NickNamesAdd $newnick $+($network,$wildsite) $nick }\nalias -l NickNames.display {\n  if ($gettok($hget(NickNames,$+($3,$4)),0,126) > 1) {\n    echo -g $2 $AKALogo $+(\u000309,$1) $AKALogo \u000307 $mid($replace($hget(NickNames,$+($3,$4)),$chr(126),$chr(44)),2,-1)\n  }\n}\nalias -l NickNamesAdd {\n  if ($hget(NickNames,$2)) {\n    if (!$regex($hget(NickNames,$2),/~\\Q $+ $replacecs($1,\\E,\\E\\\\E\\Q) $+ \\E~/i)) {\n      if ($gettok($hget(NickNames,$2),0,126) <= $MaxNicks) {\n        hadd NickNames $2 $+($hget(NickNames,$2),$1,~)\n      }\n      else {\n        hadd NickNames $2 $+($mid($hget(NickNames,$2),$pos($hget(NickNames,$2),~,2)),$1,~)\n      }\n    }\n  }\n  else {\n    hadd -m NickNames $2 $+(~,$1,~,$iif($3,$+($3,~)))\n  }\n}\nalias -l Fix.All.MindUser {\n  var %Fix.Count = $hfind(NickNames,/[^~]+[0-9]{4}~/,0,r).data\n  while (%Fix.Count) {\n    if ($Fix.MindUser($hget(NickNames,$hfind(NickNames,/[^~]+[0-9]{4}~/,%Fix.Count,r).data))) {\n      echo -ag Record %Fix.Count - $v1 - Was Cleaned\n      hadd NickNames $hfind(NickNames,/[^~]+[0-9]{4}~/,%Fix.Count,r).data $v1\n    }\n    dec %Fix.Count\n  }\n}\nalias -l Fix.MindUser { return $regsubex($1,/[^~]+[0-9]{4}~/g,$null) }\nmenu nicklist,query {\n  -\n  .AKA\n  ..Check $$1: {\n    if ($gettok($hget(NickNames,$+($network,$address($1,2))),0,126) > 1) {\n      NickNames.display $1 $active $network $address($1,2)\n    }\n    else { echo -ag $AKALogo $+(\u000309,$1) \u000307has not been known by any other nicknames while I have been watching. }\n  }\n  ..Cleanup $$1:hadd NickNames $+($network,$address($1,2)) $fix.minduser($hget(NickNames,$+($network,$address($1,2))))\n  ..Clear $$1:hadd NickNames $+($network,$address($1,2)) $+(~,$1,~)\n  ..AKA Search Dialog:dialog $iif($dialog(AKA_Search),-v,-m) AKA_Search AKA_Search\n  -\n}\nmenu status,channel {\n  -\n  .AKA\n  ..AKA Search Dialog:dialog $iif($dialog(AKA_Search),-v,-m) AKA_Search AKA_Search\n  ..Clean All Records:Fix.All.Minduser\n  -\n}\ndialog AKA_Search {\n  title \"AKA Search Engine\"\n  size -1 -1 206 221\n  option dbu\n  edit \"\", 1, 8 5 149 10, autohs\n  button \"Search\", 2, 163 4 32 12\n  radio \"Search HostMask\", 4, 61 22 55 10\n  radio \"Search Nicknames\", 5, 123 22 56 10\n  list 6, 8 38 190 169, sort extsel vsbar\n  button \"Check Selected\", 7, 67 206 40 12\n  button \"Close\", 8, 160 206 38 12, cancel\n  box \"Search Type\", 3, 11 17 183 18\n  button \"Copy to Clipboard\", 9, 111 206 46 12\n}\nOn *:Dialog:Aka_Search:init:*: { did -c $dname 5 }\nOn *:Dialog:Aka_Search:Sclick:2,7,9: {\n  if ($did == 2) && ($did($dname,1)) {\n    did -r $dname 6\n    var %search $+(*,$v1,*), %type $iif($did($dname,5).state,data,item), %matches = $hfind(NickNames,%search,0,w). [ $+ [ %type ] ]\n    while (%matches) {\n      did -a $dname 6 $hfind(NickNames,%search,%matches,w). [ $+ [ %type ] ]\n      dec %matches\n    }\n    did -c $dname 6 1\n  }\n  elseif ($did == 7) && ($did($dname,6).seltext) { echo -ga $AKALogo \u000307 $mid($replace($hget(NickNames,$v1),$chr(126),$chr(44)),2,-1) }\n  elseif ($did == 9) && ($did($dname,6).seltext) { clipboard $mid($v1,$pos($v1,*,1)) }\n}\nOn *:Start:{\n  if (!$hget(NickNames)) { hmake NickNames 10 }\n  if ($isfile(NickNames.hsh)) { hload  NickNames NickNames.hsh }\n}\nOn *:Exit: { if ($hget(NickNames)) { hsave NickNames NickNames.hsh } }\nOn *:Disconnect: { if ($hget(NickNames)) { hsave NickNames NickNames.hsh } }\nOn *:Unload: { hfree NickNames }\nalias -l ialupdateCheck {\n  inc -z $+(%,ialupdateCheck,$network) $calc($nick($1,0) / 4)\n  ;If your ial is already being updated on join .who $1 out.\n  ;If you are using /names to update ial you will still need this line.\n  .who $1\n}\nRaw 352:*: {\n  if ($($+(%,ialupdateCheck,$network),2)) haltdef\n  NickNamesAdd $6 $+($network,$address($6,2))\n}\nRaw 315:*: {\n  if ($($+(%,ialupdateCheck,$network),2)) haltdef\n}\n\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        theme: \"twilight\",\n        lineNumbers: true,\n        matchBrackets: true,\n        indentUnit: 4,\n        mode: \"text/mirc\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/mirc</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mirc/mirc.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n//mIRC mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMIME(\"text/mirc\", \"mirc\");\nCodeMirror.defineMode(\"mirc\", function() {\n  function parseWords(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n  var specials = parseWords(\"$! $$ $& $? $+ $abook $abs $active $activecid \" +\n                            \"$activewid $address $addtok $agent $agentname $agentstat $agentver \" +\n                            \"$alias $and $anick $ansi2mirc $aop $appactive $appstate $asc $asctime \" +\n                            \"$asin $atan $avoice $away $awaymsg $awaytime $banmask $base $bfind \" +\n                            \"$binoff $biton $bnick $bvar $bytes $calc $cb $cd $ceil $chan $chanmodes \" +\n                            \"$chantypes $chat $chr $cid $clevel $click $cmdbox $cmdline $cnick $color \" +\n                            \"$com $comcall $comchan $comerr $compact $compress $comval $cos $count \" +\n                            \"$cr $crc $creq $crlf $ctime $ctimer $ctrlenter $date $day $daylight \" +\n                            \"$dbuh $dbuw $dccignore $dccport $dde $ddename $debug $decode $decompress \" +\n                            \"$deltok $devent $dialog $did $didreg $didtok $didwm $disk $dlevel $dll \" +\n                            \"$dllcall $dname $dns $duration $ebeeps $editbox $emailaddr $encode $error \" +\n                            \"$eval $event $exist $feof $ferr $fgetc $file $filename $filtered $finddir \" +\n                            \"$finddirn $findfile $findfilen $findtok $fline $floor $fopen $fread $fserve \" +\n                            \"$fulladdress $fulldate $fullname $fullscreen $get $getdir $getdot $gettok $gmt \" +\n                            \"$group $halted $hash $height $hfind $hget $highlight $hnick $hotline \" +\n                            \"$hotlinepos $ial $ialchan $ibl $idle $iel $ifmatch $ignore $iif $iil \" +\n                            \"$inelipse $ini $inmidi $inpaste $inpoly $input $inrect $inroundrect \" +\n                            \"$insong $instok $int $inwave $ip $isalias $isbit $isdde $isdir $isfile \" +\n                            \"$isid $islower $istok $isupper $keychar $keyrpt $keyval $knick $lactive \" +\n                            \"$lactivecid $lactivewid $left $len $level $lf $line $lines $link $lock \" +\n                            \"$lock $locked $log $logstamp $logstampfmt $longfn $longip $lower $ltimer \" +\n                            \"$maddress $mask $matchkey $matchtok $md5 $me $menu $menubar $menucontext \" +\n                            \"$menutype $mid $middir $mircdir $mircexe $mircini $mklogfn $mnick $mode \" +\n                            \"$modefirst $modelast $modespl $mouse $msfile $network $newnick $nick $nofile \" +\n                            \"$nopath $noqt $not $notags $notify $null $numeric $numok $oline $onpoly \" +\n                            \"$opnick $or $ord $os $passivedcc $pic $play $pnick $port $portable $portfree \" +\n                            \"$pos $prefix $prop $protect $puttok $qt $query $rand $r $rawmsg $read $readomo \" +\n                            \"$readn $regex $regml $regsub $regsubex $remove $remtok $replace $replacex \" +\n                            \"$reptok $result $rgb $right $round $scid $scon $script $scriptdir $scriptline \" +\n                            \"$sdir $send $server $serverip $sfile $sha1 $shortfn $show $signal $sin \" +\n                            \"$site $sline $snick $snicks $snotify $sock $sockbr $sockerr $sockname \" +\n                            \"$sorttok $sound $sqrt $ssl $sreq $sslready $status $strip $str $stripped \" +\n                            \"$syle $submenu $switchbar $tan $target $ticks $time $timer $timestamp \" +\n                            \"$timestampfmt $timezone $tip $titlebar $toolbar $treebar $trust $ulevel \" +\n                            \"$ulist $upper $uptime $url $usermode $v1 $v2 $var $vcmd $vcmdstat $vcmdver \" +\n                            \"$version $vnick $vol $wid $width $wildsite $wildtok $window $wrap $xor\");\n  var keywords = parseWords(\"abook ajinvite alias aline ame amsg anick aop auser autojoin avoice \" +\n                            \"away background ban bcopy beep bread break breplace bset btrunc bunset bwrite \" +\n                            \"channel clear clearall cline clipboard close cnick color comclose comopen \" +\n                            \"comreg continue copy creq ctcpreply ctcps dcc dccserver dde ddeserver \" +\n                            \"debug dec describe dialog did didtok disable disconnect dlevel dline dll \" +\n                            \"dns dqwindow drawcopy drawdot drawfill drawline drawpic drawrect drawreplace \" +\n                            \"drawrot drawsave drawscroll drawtext ebeeps echo editbox emailaddr enable \" +\n                            \"events exit fclose filter findtext finger firewall flash flist flood flush \" +\n                            \"flushini font fopen fseek fsend fserve fullname fwrite ghide gload gmove \" +\n                            \"gopts goto gplay gpoint gqreq groups gshow gsize gstop gtalk gunload hadd \" +\n                            \"halt haltdef hdec hdel help hfree hinc hload hmake hop hsave ial ialclear \" +\n                            \"ialmark identd if ignore iline inc invite iuser join kick linesep links list \" +\n                            \"load loadbuf localinfo log mdi me menubar mkdir mnick mode msg nick noop notice \" +\n                            \"notify omsg onotice part partall pdcc perform play playctrl pop protect pvoice \" +\n                            \"qme qmsg query queryn quit raw reload remini remote remove rename renwin \" +\n                            \"reseterror resetidle return rlevel rline rmdir run ruser save savebuf saveini \" +\n                            \"say scid scon server set showmirc signam sline sockaccept sockclose socklist \" +\n                            \"socklisten sockmark sockopen sockpause sockread sockrename sockudp sockwrite \" +\n                            \"sound speak splay sreq strip switchbar timer timestamp titlebar tnick tokenize \" +\n                            \"toolbar topic tray treebar ulist unload unset unsetall updatenl url uwho \" +\n                            \"var vcadd vcmd vcrem vol while whois window winhelp write writeint if isalnum \" +\n                            \"isalpha isaop isavoice isban ischan ishop isignore isin isincs isletter islower \" +\n                            \"isnotify isnum ison isop isprotect isreg isupper isvoice iswm iswmcs \" +\n                            \"elseif else goto menu nicklist status title icon size option text edit \" +\n                            \"button check radio box scroll list combo link tab item\");\n  var functions = parseWords(\"if elseif else and not or eq ne in ni for foreach while switch\");\n  var isOperatorChar = /[+\\-*&%=<>!?^\\/\\|]/;\n  function chain(stream, state, f) {\n    state.tokenize = f;\n    return f(stream, state);\n  }\n  function tokenBase(stream, state) {\n    var beforeParams = state.beforeParams;\n    state.beforeParams = false;\n    var ch = stream.next();\n    if (/[\\[\\]{}\\(\\),\\.]/.test(ch)) {\n      if (ch == \"(\" && beforeParams) state.inParams = true;\n      else if (ch == \")\") state.inParams = false;\n      return null;\n    }\n    else if (/\\d/.test(ch)) {\n      stream.eatWhile(/[\\w\\.]/);\n      return \"number\";\n    }\n    else if (ch == \"\\\\\") {\n      stream.eat(\"\\\\\");\n      stream.eat(/./);\n      return \"number\";\n    }\n    else if (ch == \"/\" && stream.eat(\"*\")) {\n      return chain(stream, state, tokenComment);\n    }\n    else if (ch == \";\" && stream.match(/ *\\( *\\(/)) {\n      return chain(stream, state, tokenUnparsed);\n    }\n    else if (ch == \";\" && !state.inParams) {\n      stream.skipToEnd();\n      return \"comment\";\n    }\n    else if (ch == '\"') {\n      stream.eat(/\"/);\n      return \"keyword\";\n    }\n    else if (ch == \"$\") {\n      stream.eatWhile(/[$_a-z0-9A-Z\\.:]/);\n      if (specials && specials.propertyIsEnumerable(stream.current().toLowerCase())) {\n        return \"keyword\";\n      }\n      else {\n        state.beforeParams = true;\n        return \"builtin\";\n      }\n    }\n    else if (ch == \"%\") {\n      stream.eatWhile(/[^,\\s()]/);\n      state.beforeParams = true;\n      return \"string\";\n    }\n    else if (isOperatorChar.test(ch)) {\n      stream.eatWhile(isOperatorChar);\n      return \"operator\";\n    }\n    else {\n      stream.eatWhile(/[\\w\\$_{}]/);\n      var word = stream.current().toLowerCase();\n      if (keywords && keywords.propertyIsEnumerable(word))\n        return \"keyword\";\n      if (functions && functions.propertyIsEnumerable(word)) {\n        state.beforeParams = true;\n        return \"keyword\";\n      }\n      return null;\n    }\n  }\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n  function tokenUnparsed(stream, state) {\n    var maybeEnd = 0, ch;\n    while (ch = stream.next()) {\n      if (ch == \";\" && maybeEnd == 2) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      if (ch == \")\")\n        maybeEnd++;\n      else if (ch != \" \")\n        maybeEnd = 0;\n    }\n    return \"meta\";\n  }\n  return {\n    startState: function() {\n      return {\n        tokenize: tokenBase,\n        beforeParams: false,\n        inParams: false\n      };\n    },\n    token: function(stream, state) {\n      if (stream.eatSpace()) return null;\n      return state.tokenize(stream, state);\n    }\n  };\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mllike/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: ML-like mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=stylesheet href=../../lib/codemirror.css>\n<script src=../../lib/codemirror.js></script>\n<script src=../../addon/edit/matchbrackets.js></script>\n<script src=mllike.js></script>\n<style type=text/css>\n  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">ML-like</a>\n  </ul>\n</div>\n\n<article>\n<h2>OCaml mode</h2>\n\n\n<textarea id=\"ocamlCode\">\n(* Summing a list of integers *)\nlet rec sum xs =\n  match xs with\n    | []       -&gt; 0\n    | x :: xs' -&gt; x + sum xs'\n\n(* Quicksort *)\nlet rec qsort = function\n   | [] -&gt; []\n   | pivot :: rest -&gt;\n       let is_less x = x &lt; pivot in\n       let left, right = List.partition is_less rest in\n       qsort left @ [pivot] @ qsort right\n\n(* Fibonacci Sequence *)\nlet rec fib_aux n a b =\n  match n with\n  | 0 -&gt; a\n  | _ -&gt; fib_aux (n - 1) (a + b) a\nlet fib n = fib_aux n 0 1\n\n(* Birthday paradox *)\nlet year_size = 365.\n\nlet rec birthday_paradox prob people =\n    let prob' = (year_size -. float people) /. year_size *. prob  in\n    if prob' &lt; 0.5 then\n        Printf.printf \"answer = %d\\n\" (people+1)\n    else\n        birthday_paradox prob' (people+1) ;;\n\nbirthday_paradox 1.0 1\n\n(* Church numerals *)\nlet zero f x = x\nlet succ n f x = f (n f x)\nlet one = succ zero\nlet two = succ (succ zero)\nlet add n1 n2 f x = n1 f (n2 f x)\nlet to_string n = n (fun k -&gt; \"S\" ^ k) \"0\"\nlet _ = to_string (add (succ two) two)\n\n(* Elementary functions *)\nlet square x = x * x;;\nlet rec fact x =\n  if x &lt;= 1 then 1 else x * fact (x - 1);;\n\n(* Automatic memory management *)\nlet l = 1 :: 2 :: 3 :: [];;\n[1; 2; 3];;\n5 :: l;;\n\n(* Polymorphism: sorting lists *)\nlet rec sort = function\n  | [] -&gt; []\n  | x :: l -&gt; insert x (sort l)\n\nand insert elem = function\n  | [] -&gt; [elem]\n  | x :: l -&gt;\n      if elem &lt; x then elem :: x :: l else x :: insert elem l;;\n\n(* Imperative features *)\nlet add_polynom p1 p2 =\n  let n1 = Array.length p1\n  and n2 = Array.length p2 in\n  let result = Array.create (max n1 n2) 0 in\n  for i = 0 to n1 - 1 do result.(i) &lt;- p1.(i) done;\n  for i = 0 to n2 - 1 do result.(i) &lt;- result.(i) + p2.(i) done;\n  result;;\nadd_polynom [| 1; 2 |] [| 1; 2; 3 |];;\n\n(* We may redefine fact using a reference cell and a for loop *)\nlet fact n =\n  let result = ref 1 in\n  for i = 2 to n do\n    result := i * !result\n   done;\n   !result;;\nfact 5;;\n\n(* Triangle (graphics) *)\nlet () =\n  ignore( Glut.init Sys.argv );\n  Glut.initDisplayMode ~double_buffer:true ();\n  ignore (Glut.createWindow ~title:\"OpenGL Demo\");\n  let angle t = 10. *. t *. t in\n  let render () =\n    GlClear.clear [ `color ];\n    GlMat.load_identity ();\n    GlMat.rotate ~angle: (angle (Sys.time ())) ~z:1. ();\n    GlDraw.begins `triangles;\n    List.iter GlDraw.vertex2 [-1., -1.; 0., 1.; 1., -1.];\n    GlDraw.ends ();\n    Glut.swapBuffers () in\n  GlMat.mode `modelview;\n  Glut.displayFunc ~cb:render;\n  Glut.idleFunc ~cb:(Some Glut.postRedisplay);\n  Glut.mainLoop ()\n\n(* A Hundred Lines of Caml - http://caml.inria.fr/about/taste.en.html *)\n(* OCaml page on Wikipedia - http://en.wikipedia.org/wiki/OCaml *)\n\nmodule type S = sig type t end\n\nlet x = {| \n  this is a long string \n  with many lines and stuff\n  |}\n\nlet b = 0b00110\nlet h = 0x123abcd\nlet e = 1e-10\nlet i = 1.\nlet x = 30_000\nlet o = 0o1234\n\n[1; 2; 3] (* lists *)\n\n1 @ 2\n1. +. 2.\n</textarea>\n\n<h2>F# mode</h2>\n<textarea id=\"fsharpCode\">\nmodule CodeMirror.FSharp\n\nlet rec fib = function\n    | 0 -> 0\n    | 1 -> 1\n    | n -> fib (n - 1) + fib (n - 2)\n\ntype Point =\n    {\n        x : int\n        y : int\n    }\n\ntype Color =\n    | Red\n    | Green\n    | Blue\n\n[0 .. 10]\n|> List.map ((+) 2)\n|> List.fold (fun x y -> x + y) 0\n|> printf \"%i\"\n</textarea>\n\n\n<script>\n  var ocamlEditor = CodeMirror.fromTextArea(document.getElementById('ocamlCode'), {\n    mode: 'text/x-ocaml',\n    lineNumbers: true,\n    matchBrackets: true\n  });\n\n  var fsharpEditor = CodeMirror.fromTextArea(document.getElementById('fsharpCode'), {\n    mode: 'text/x-fsharp',\n    lineNumbers: true,\n    matchBrackets: true\n  });\n</script>\n\n<p><strong>MIME types defined:</strong> <code>text/x-ocaml</code> (OCaml) and <code>text/x-fsharp</code> (F#).</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mllike/mllike.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode('mllike', function(_config, parserConfig) {\n  var words = {\n    'as': 'keyword',\n    'do': 'keyword',\n    'else': 'keyword',\n    'end': 'keyword',\n    'exception': 'keyword',\n    'fun': 'keyword',\n    'functor': 'keyword',\n    'if': 'keyword',\n    'in': 'keyword',\n    'include': 'keyword',\n    'let': 'keyword',\n    'of': 'keyword',\n    'open': 'keyword',\n    'rec': 'keyword',\n    'struct': 'keyword',\n    'then': 'keyword',\n    'type': 'keyword',\n    'val': 'keyword',\n    'while': 'keyword',\n    'with': 'keyword'\n  };\n\n  var extraWords = parserConfig.extraWords || {};\n  for (var prop in extraWords) {\n    if (extraWords.hasOwnProperty(prop)) {\n      words[prop] = parserConfig.extraWords[prop];\n    }\n  }\n  var hintWords = [];\n  for (var k in words) { hintWords.push(k); }\n  CodeMirror.registerHelper(\"hintWords\", \"mllike\", hintWords);\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n\n    if (ch === '\"') {\n      state.tokenize = tokenString;\n      return state.tokenize(stream, state);\n    }\n    if (ch === '{') {\n      if (stream.eat('|')) {\n        state.longString = true;\n        state.tokenize = tokenLongString;\n        return state.tokenize(stream, state);\n      }\n    }\n    if (ch === '(') {\n      if (stream.match(/^\\*(?!\\))/)) {\n        state.commentLevel++;\n        state.tokenize = tokenComment;\n        return state.tokenize(stream, state);\n      }\n    }\n    if (ch === '~' || ch === '?') {\n      stream.eatWhile(/\\w/);\n      return 'variable-2';\n    }\n    if (ch === '`') {\n      stream.eatWhile(/\\w/);\n      return 'quote';\n    }\n    if (ch === '/' && parserConfig.slashComments && stream.eat('/')) {\n      stream.skipToEnd();\n      return 'comment';\n    }\n    if (/\\d/.test(ch)) {\n      if (ch === '0' && stream.eat(/[bB]/)) {\n        stream.eatWhile(/[01]/);\n      } if (ch === '0' && stream.eat(/[xX]/)) {\n        stream.eatWhile(/[0-9a-fA-F]/)\n      } if (ch === '0' && stream.eat(/[oO]/)) {\n        stream.eatWhile(/[0-7]/);\n      } else {\n        stream.eatWhile(/[\\d_]/);\n        if (stream.eat('.')) {\n          stream.eatWhile(/[\\d]/);\n        }\n        if (stream.eat(/[eE]/)) {\n          stream.eatWhile(/[\\d\\-+]/);\n        }\n      }\n      return 'number';\n    }\n    if ( /[+\\-*&%=<>!?|@\\.~:]/.test(ch)) {\n      return 'operator';\n    }\n    if (/[\\w\\xa1-\\uffff]/.test(ch)) {\n      stream.eatWhile(/[\\w\\xa1-\\uffff]/);\n      var cur = stream.current();\n      return words.hasOwnProperty(cur) ? words[cur] : 'variable';\n    }\n    return null\n  }\n\n  function tokenString(stream, state) {\n    var next, end = false, escaped = false;\n    while ((next = stream.next()) != null) {\n      if (next === '\"' && !escaped) {\n        end = true;\n        break;\n      }\n      escaped = !escaped && next === '\\\\';\n    }\n    if (end && !escaped) {\n      state.tokenize = tokenBase;\n    }\n    return 'string';\n  };\n\n  function tokenComment(stream, state) {\n    var prev, next;\n    while(state.commentLevel > 0 && (next = stream.next()) != null) {\n      if (prev === '(' && next === '*') state.commentLevel++;\n      if (prev === '*' && next === ')') state.commentLevel--;\n      prev = next;\n    }\n    if (state.commentLevel <= 0) {\n      state.tokenize = tokenBase;\n    }\n    return 'comment';\n  }\n\n  function tokenLongString(stream, state) {\n    var prev, next;\n    while (state.longString && (next = stream.next()) != null) {\n      if (prev === '|' && next === '}') state.longString = false;\n      prev = next;\n    }\n    if (!state.longString) {\n      state.tokenize = tokenBase;\n    }\n    return 'string';\n  }\n\n  return {\n    startState: function() {return {tokenize: tokenBase, commentLevel: 0, longString: false};},\n    token: function(stream, state) {\n      if (stream.eatSpace()) return null;\n      return state.tokenize(stream, state);\n    },\n\n    blockCommentStart: \"(*\",\n    blockCommentEnd: \"*)\",\n    lineComment: parserConfig.slashComments ? \"//\" : null\n  };\n});\n\nCodeMirror.defineMIME('text/x-ocaml', {\n  name: 'mllike',\n  extraWords: {\n    'and': 'keyword',\n    'assert': 'keyword',\n    'begin': 'keyword',\n    'class': 'keyword',\n    'constraint': 'keyword',\n    'done': 'keyword',\n    'downto': 'keyword',\n    'external': 'keyword',\n    'function': 'keyword',\n    'initializer': 'keyword',\n    'lazy': 'keyword',\n    'match': 'keyword',\n    'method': 'keyword',\n    'module': 'keyword',\n    'mutable': 'keyword',\n    'new': 'keyword',\n    'nonrec': 'keyword',\n    'object': 'keyword',\n    'private': 'keyword',\n    'sig': 'keyword',\n    'to': 'keyword',\n    'try': 'keyword',\n    'value': 'keyword',\n    'virtual': 'keyword',\n    'when': 'keyword',\n\n    // builtins\n    'raise': 'builtin',\n    'failwith': 'builtin',\n    'true': 'builtin',\n    'false': 'builtin',\n\n    // Pervasives builtins\n    'asr': 'builtin',\n    'land': 'builtin',\n    'lor': 'builtin',\n    'lsl': 'builtin',\n    'lsr': 'builtin',\n    'lxor': 'builtin',\n    'mod': 'builtin',\n    'or': 'builtin',\n\n    // More Pervasives\n    'raise_notrace': 'builtin',\n    'trace': 'builtin',\n    'exit': 'builtin',\n    'print_string': 'builtin',\n    'print_endline': 'builtin',\n\n     'int': 'type',\n     'float': 'type',\n     'bool': 'type',\n     'char': 'type',\n     'string': 'type',\n     'unit': 'type',\n\n     // Modules\n     'List': 'builtin'\n  }\n});\n\nCodeMirror.defineMIME('text/x-fsharp', {\n  name: 'mllike',\n  extraWords: {\n    'abstract': 'keyword',\n    'assert': 'keyword',\n    'base': 'keyword',\n    'begin': 'keyword',\n    'class': 'keyword',\n    'default': 'keyword',\n    'delegate': 'keyword',\n    'do!': 'keyword',\n    'done': 'keyword',\n    'downcast': 'keyword',\n    'downto': 'keyword',\n    'elif': 'keyword',\n    'extern': 'keyword',\n    'finally': 'keyword',\n    'for': 'keyword',\n    'function': 'keyword',\n    'global': 'keyword',\n    'inherit': 'keyword',\n    'inline': 'keyword',\n    'interface': 'keyword',\n    'internal': 'keyword',\n    'lazy': 'keyword',\n    'let!': 'keyword',\n    'match': 'keyword',\n    'member': 'keyword',\n    'module': 'keyword',\n    'mutable': 'keyword',\n    'namespace': 'keyword',\n    'new': 'keyword',\n    'null': 'keyword',\n    'override': 'keyword',\n    'private': 'keyword',\n    'public': 'keyword',\n    'return!': 'keyword',\n    'return': 'keyword',\n    'select': 'keyword',\n    'static': 'keyword',\n    'to': 'keyword',\n    'try': 'keyword',\n    'upcast': 'keyword',\n    'use!': 'keyword',\n    'use': 'keyword',\n    'void': 'keyword',\n    'when': 'keyword',\n    'yield!': 'keyword',\n    'yield': 'keyword',\n\n    // Reserved words\n    'atomic': 'keyword',\n    'break': 'keyword',\n    'checked': 'keyword',\n    'component': 'keyword',\n    'const': 'keyword',\n    'constraint': 'keyword',\n    'constructor': 'keyword',\n    'continue': 'keyword',\n    'eager': 'keyword',\n    'event': 'keyword',\n    'external': 'keyword',\n    'fixed': 'keyword',\n    'method': 'keyword',\n    'mixin': 'keyword',\n    'object': 'keyword',\n    'parallel': 'keyword',\n    'process': 'keyword',\n    'protected': 'keyword',\n    'pure': 'keyword',\n    'sealed': 'keyword',\n    'tailcall': 'keyword',\n    'trait': 'keyword',\n    'virtual': 'keyword',\n    'volatile': 'keyword',\n\n    // builtins\n    'List': 'builtin',\n    'Seq': 'builtin',\n    'Map': 'builtin',\n    'Set': 'builtin',\n    'Option': 'builtin',\n    'int': 'builtin',\n    'string': 'builtin',\n    'not': 'builtin',\n    'true': 'builtin',\n    'false': 'builtin',\n\n    'raise': 'builtin',\n    'failwith': 'builtin'\n  },\n  slashComments: true\n});\n\n\nCodeMirror.defineMIME('text/x-sml', {\n  name: 'mllike',\n  extraWords: {\n    'abstype': 'keyword',\n    'and': 'keyword',\n    'andalso': 'keyword',\n    'case': 'keyword',\n    'datatype': 'keyword',\n    'fn': 'keyword',\n    'handle': 'keyword',\n    'infix': 'keyword',\n    'infixr': 'keyword',\n    'local': 'keyword',\n    'nonfix': 'keyword',\n    'op': 'keyword',\n    'orelse': 'keyword',\n    'raise': 'keyword',\n    'withtype': 'keyword',\n    'eqtype': 'keyword',\n    'sharing': 'keyword',\n    'sig': 'keyword',\n    'signature': 'keyword',\n    'structure': 'keyword',\n    'where': 'keyword',\n    'true': 'keyword',\n    'false': 'keyword',\n\n    // types\n    'int': 'builtin',\n    'real': 'builtin',\n    'string': 'builtin',\n    'char': 'builtin',\n    'bool': 'builtin'\n  },\n  slashComments: true\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/modelica/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Modelica mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<link rel=\"stylesheet\" href=\"../../addon/hint/show-hint.css\">\n<script src=\"../../addon/hint/show-hint.js\"></script>\n<script src=\"modelica.js\"></script>\n<style>.CodeMirror {border: 2px inset #dee;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Modelica</a>\n  </ul>\n</div>\n\n<article>\n<h2>Modelica mode</h2>\n\n<div><textarea id=\"modelica\">\nmodel BouncingBall\n  parameter Real e = 0.7;\n  parameter Real g = 9.81;\n  Real h(start=1);\n  Real v;\n  Boolean flying(start=true);\n  Boolean impact;\n  Real v_new;\nequation\n  impact = h <= 0.0;\n  der(v) = if flying then -g else 0;\n  der(h) = v;\n  when {h <= 0.0 and v <= 0.0, impact} then\n    v_new = if edge(impact) then -e*pre(v) else 0;\n    flying = v_new > 0;\n    reinit(v, v_new);\n  end when;\n  annotation (uses(Modelica(version=\"3.2\")));\nend BouncingBall;\n</textarea></div>\n\n    <script>\n      var modelicaEditor = CodeMirror.fromTextArea(document.getElementById(\"modelica\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-modelica\"\n      });\n      var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;\n      CodeMirror.keyMap.default[(mac ? \"Cmd\" : \"Ctrl\") + \"-Space\"] = \"autocomplete\";\n    </script>\n\n    <p>Simple mode that tries to handle Modelica as well as it can.</p>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-modelica</code>\n    (Modlica code).</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/modelica/modelica.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Modelica support for CodeMirror, copyright (c) by Lennart Ochel\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})\n\n(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"modelica\", function(config, parserConfig) {\n\n    var indentUnit = config.indentUnit;\n    var keywords = parserConfig.keywords || {};\n    var builtin = parserConfig.builtin || {};\n    var atoms = parserConfig.atoms || {};\n\n    var isSingleOperatorChar = /[;=\\(:\\),{}.*<>+\\-\\/^\\[\\]]/;\n    var isDoubleOperatorChar = /(:=|<=|>=|==|<>|\\.\\+|\\.\\-|\\.\\*|\\.\\/|\\.\\^)/;\n    var isDigit = /[0-9]/;\n    var isNonDigit = /[_a-zA-Z]/;\n\n    function tokenLineComment(stream, state) {\n      stream.skipToEnd();\n      state.tokenize = null;\n      return \"comment\";\n    }\n\n    function tokenBlockComment(stream, state) {\n      var maybeEnd = false, ch;\n      while (ch = stream.next()) {\n        if (maybeEnd && ch == \"/\") {\n          state.tokenize = null;\n          break;\n        }\n        maybeEnd = (ch == \"*\");\n      }\n      return \"comment\";\n    }\n\n    function tokenString(stream, state) {\n      var escaped = false, ch;\n      while ((ch = stream.next()) != null) {\n        if (ch == '\"' && !escaped) {\n          state.tokenize = null;\n          state.sol = false;\n          break;\n        }\n        escaped = !escaped && ch == \"\\\\\";\n      }\n\n      return \"string\";\n    }\n\n    function tokenIdent(stream, state) {\n      stream.eatWhile(isDigit);\n      while (stream.eat(isDigit) || stream.eat(isNonDigit)) { }\n\n\n      var cur = stream.current();\n\n      if(state.sol && (cur == \"package\" || cur == \"model\" || cur == \"when\" || cur == \"connector\")) state.level++;\n      else if(state.sol && cur == \"end\" && state.level > 0) state.level--;\n\n      state.tokenize = null;\n      state.sol = false;\n\n      if (keywords.propertyIsEnumerable(cur)) return \"keyword\";\n      else if (builtin.propertyIsEnumerable(cur)) return \"builtin\";\n      else if (atoms.propertyIsEnumerable(cur)) return \"atom\";\n      else return \"variable\";\n    }\n\n    function tokenQIdent(stream, state) {\n      while (stream.eat(/[^']/)) { }\n\n      state.tokenize = null;\n      state.sol = false;\n\n      if(stream.eat(\"'\"))\n        return \"variable\";\n      else\n        return \"error\";\n    }\n\n    function tokenUnsignedNumber(stream, state) {\n      stream.eatWhile(isDigit);\n      if (stream.eat('.')) {\n        stream.eatWhile(isDigit);\n      }\n      if (stream.eat('e') || stream.eat('E')) {\n        if (!stream.eat('-'))\n          stream.eat('+');\n        stream.eatWhile(isDigit);\n      }\n\n      state.tokenize = null;\n      state.sol = false;\n      return \"number\";\n    }\n\n    // Interface\n    return {\n      startState: function() {\n        return {\n          tokenize: null,\n          level: 0,\n          sol: true\n        };\n      },\n\n      token: function(stream, state) {\n        if(state.tokenize != null) {\n          return state.tokenize(stream, state);\n        }\n\n        if(stream.sol()) {\n          state.sol = true;\n        }\n\n        // WHITESPACE\n        if(stream.eatSpace()) {\n          state.tokenize = null;\n          return null;\n        }\n\n        var ch = stream.next();\n\n        // LINECOMMENT\n        if(ch == '/' && stream.eat('/')) {\n          state.tokenize = tokenLineComment;\n        }\n        // BLOCKCOMMENT\n        else if(ch == '/' && stream.eat('*')) {\n          state.tokenize = tokenBlockComment;\n        }\n        // TWO SYMBOL TOKENS\n        else if(isDoubleOperatorChar.test(ch+stream.peek())) {\n          stream.next();\n          state.tokenize = null;\n          return \"operator\";\n        }\n        // SINGLE SYMBOL TOKENS\n        else if(isSingleOperatorChar.test(ch)) {\n          state.tokenize = null;\n          return \"operator\";\n        }\n        // IDENT\n        else if(isNonDigit.test(ch)) {\n          state.tokenize = tokenIdent;\n        }\n        // Q-IDENT\n        else if(ch == \"'\" && stream.peek() && stream.peek() != \"'\") {\n          state.tokenize = tokenQIdent;\n        }\n        // STRING\n        else if(ch == '\"') {\n          state.tokenize = tokenString;\n        }\n        // UNSIGNED_NUMBER\n        else if(isDigit.test(ch)) {\n          state.tokenize = tokenUnsignedNumber;\n        }\n        // ERROR\n        else {\n          state.tokenize = null;\n          return \"error\";\n        }\n\n        return state.tokenize(stream, state);\n      },\n\n      indent: function(state, textAfter) {\n        if (state.tokenize != null) return CodeMirror.Pass;\n\n        var level = state.level;\n        if(/(algorithm)/.test(textAfter)) level--;\n        if(/(equation)/.test(textAfter)) level--;\n        if(/(initial algorithm)/.test(textAfter)) level--;\n        if(/(initial equation)/.test(textAfter)) level--;\n        if(/(end)/.test(textAfter)) level--;\n\n        if(level > 0)\n          return indentUnit*level;\n        else\n          return 0;\n      },\n\n      blockCommentStart: \"/*\",\n      blockCommentEnd: \"*/\",\n      lineComment: \"//\"\n    };\n  });\n\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i=0; i<words.length; ++i)\n      obj[words[i]] = true;\n    return obj;\n  }\n\n  var modelicaKeywords = \"algorithm and annotation assert block break class connect connector constant constrainedby der discrete each else elseif elsewhen encapsulated end enumeration equation expandable extends external false final flow for function if import impure in initial inner input loop model not operator or outer output package parameter partial protected public pure record redeclare replaceable return stream then true type when while within\";\n  var modelicaBuiltin = \"abs acos actualStream asin atan atan2 cardinality ceil cos cosh delay div edge exp floor getInstanceName homotopy inStream integer log log10 mod pre reinit rem semiLinear sign sin sinh spatialDistribution sqrt tan tanh\";\n  var modelicaAtoms = \"Real Boolean Integer String\";\n\n  function def(mimes, mode) {\n    if (typeof mimes == \"string\")\n      mimes = [mimes];\n\n    var words = [];\n\n    function add(obj) {\n      if (obj)\n        for (var prop in obj)\n          if (obj.hasOwnProperty(prop))\n            words.push(prop);\n    }\n\n    add(mode.keywords);\n    add(mode.builtin);\n    add(mode.atoms);\n\n    if (words.length) {\n      mode.helperType = mimes[0];\n      CodeMirror.registerHelper(\"hintWords\", mimes[0], words);\n    }\n\n    for (var i=0; i<mimes.length; ++i)\n      CodeMirror.defineMIME(mimes[i], mode);\n  }\n\n  def([\"text/x-modelica\"], {\n    name: \"modelica\",\n    keywords: words(modelicaKeywords),\n    builtin: words(modelicaBuiltin),\n    atoms: words(modelicaAtoms)\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mscgen/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: MscGen mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"mscgen.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">MscGen</a>\n  </ul>\n</div>\n\n<article>\n<h2>MscGen mode</h2>\n\n<div><textarea id=\"mscgen-code\">\n# Sample mscgen program\n# See http://www.mcternan.me.uk/mscgen or\n# https://sverweij.github.io/mscgen_js for more samples\nmsc {\n  # options\n  hscale=\"1.2\";\n\n  # entities/ lifelines\n  a [label=\"Entity A\"],\n  b [label=\"Entity B\", linecolor=\"red\", arclinecolor=\"red\", textbgcolor=\"pink\"],\n  c [label=\"Entity C\"];\n\n  # arcs/ messages\n  a => c [label=\"doSomething(args)\"];\n  b => c [label=\"doSomething(args)\"];\n  c >> * [label=\"everyone asked me\", arcskip=\"1\"];\n  c =>> c [label=\"doing something\"];\n  c -x * [label=\"report back\", arcskip=\"1\"];\n  |||;\n  --- [label=\"shows's over, however ...\"];\n  b => a [label=\"did you see c doing something?\"];\n  a -> b [label=\"nope\"];\n  b :> a [label=\"shall we ask again?\"];\n  a => b [label=\"naah\"];\n  ...;\n}\n</textarea></div>\n\n<h2>Xù mode</h2>\n\n<div><textarea id=\"xu-code\">\n# Xù - expansions to MscGen to support inline expressions\n#      https://github.com/sverweij/mscgen_js/blob/master/wikum/xu.md\n# More samples: https://sverweij.github.io/mscgen_js\nxu {\n  hscale=\"0.8\",\n  width=\"700\";\n\n  a,\n  b [label=\"change store\"],\n  c,\n  d [label=\"necro queue\"],\n  e [label=\"natalis queue\"],\n  f;\n\n  a =>> b [label=\"get change list()\"];\n  a alt f [label=\"changes found\"] { /* alt is a xu specific keyword*/\n    b >> a [label=\"list of changes\"];\n    a =>> c [label=\"cull old stuff (list of changes)\"];\n    b loop e [label=\"for each change\"] { // loop is xu specific as well...\n      /*\n       * Interesting stuff happens.\n       */\n      c =>> b [label=\"get change()\"];\n      b >> c [label=\"change\"];\n      c alt e [label=\"change too old\"] {\n        c =>> d [label=\"queue(change)\"];\n        --- [label=\"change newer than latest run\"];\n        c =>> e [label=\"queue(change)\"];\n        --- [label=\"all other cases\"];\n        ||| [label=\"leave well alone\"];\n      };\n    };\n\n    c >> a [label=\"done\n    processing\"];\n\n    /* shucks! nothing found ...*/\n    --- [label=\"nothing found\"];\n    b >> a [label=\"nothing\"];\n    a note a [label=\"silent exit\"];\n  };\n}\n</textarea></div>\n\n<h2>MsGenny mode</h2>\n<div><textarea id=\"msgenny-code\">\n# MsGenny - simplified version of MscGen / Xù\n#           https://github.com/sverweij/mscgen_js/blob/master/wikum/msgenny.md\n# More samples: https://sverweij.github.io/mscgen_js\na -> b   : a -> b  (signal);\na => b   : a => b  (method);\nb >> a   : b >> a  (return value);\na =>> b  : a =>> b (callback);\na -x b   : a -x b  (lost);\na :> b   : a :> b  (emphasis);\na .. b   : a .. b  (dotted);\na -- b   : \"a -- b straight line\";\na note a : a note a\\n(note),\nb box b  : b box b\\n(action);\na rbox a : a rbox a\\n(reference),\nb abox b : b abox b\\n(state/ condition);\n|||      : ||| (empty row);\n...      : ... (omitted row);\n---      : --- (comment);\n</textarea></div>\n\n    <p>\n      Simple mode for highlighting MscGen and two derived sequence\n      chart languages.\n    </p>\n\n    <script>\n      var mscgenEditor = CodeMirror.fromTextArea(document.getElementById(\"mscgen-code\"), {\n        lineNumbers: true,\n        mode: \"text/x-mscgen\",\n      });\n      var xuEditor = CodeMirror.fromTextArea(document.getElementById(\"xu-code\"), {\n        lineNumbers: true,\n        mode: \"text/x-xu\",\n      });\n      var msgennyEditor = CodeMirror.fromTextArea(document.getElementById(\"msgenny-code\"), {\n        lineNumbers: true,\n        mode: \"text/x-msgenny\",\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong>\n      <code>text/x-mscgen</code>\n      <code>text/x-xu</code>\n      <code>text/x-msgenny</code>\n    </p>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mscgen/mscgen.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// mode(s) for the sequence chart dsl's mscgen, xù and msgenny\n// For more information on mscgen, see the site of the original author:\n// http://www.mcternan.me.uk/mscgen\n//\n// This mode for mscgen and the two derivative languages were\n// originally made for use in the mscgen_js interpreter\n// (https://sverweij.github.io/mscgen_js)\n\n(function(mod) {\n  if ( typeof exports == \"object\" && typeof module == \"object\")// CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if ( typeof define == \"function\" && define.amd)// AMD\n    define([\"../../lib/codemirror\"], mod);\n  else// Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var languages = {\n    mscgen: {\n      \"keywords\" : [\"msc\"],\n      \"options\" : [\"hscale\", \"width\", \"arcgradient\", \"wordwraparcs\"],\n      \"constants\" : [\"true\", \"false\", \"on\", \"off\"],\n      \"attributes\" : [\"label\", \"idurl\", \"id\", \"url\", \"linecolor\", \"linecolour\", \"textcolor\", \"textcolour\", \"textbgcolor\", \"textbgcolour\", \"arclinecolor\", \"arclinecolour\", \"arctextcolor\", \"arctextcolour\", \"arctextbgcolor\", \"arctextbgcolour\", \"arcskip\"],\n      \"brackets\" : [\"\\\\{\", \"\\\\}\"], // [ and  ] are brackets too, but these get handled in with lists\n      \"arcsWords\" : [\"note\", \"abox\", \"rbox\", \"box\"],\n      \"arcsOthers\" : [\"\\\\|\\\\|\\\\|\", \"\\\\.\\\\.\\\\.\", \"---\", \"--\", \"<->\", \"==\", \"<<=>>\", \"<=>\", \"\\\\.\\\\.\", \"<<>>\", \"::\", \"<:>\", \"->\", \"=>>\", \"=>\", \">>\", \":>\", \"<-\", \"<<=\", \"<=\", \"<<\", \"<:\", \"x-\", \"-x\"],\n      \"singlecomment\" : [\"//\", \"#\"],\n      \"operators\" : [\"=\"]\n    },\n    xu: {\n      \"keywords\" : [\"msc\", \"xu\"],\n      \"options\" : [\"hscale\", \"width\", \"arcgradient\", \"wordwraparcs\", \"wordwrapentities\", \"watermark\"],\n      \"constants\" : [\"true\", \"false\", \"on\", \"off\", \"auto\"],\n      \"attributes\" : [\"label\", \"idurl\", \"id\", \"url\", \"linecolor\", \"linecolour\", \"textcolor\", \"textcolour\", \"textbgcolor\", \"textbgcolour\", \"arclinecolor\", \"arclinecolour\", \"arctextcolor\", \"arctextcolour\", \"arctextbgcolor\", \"arctextbgcolour\", \"arcskip\", \"title\", \"deactivate\", \"activate\", \"activation\"],\n      \"brackets\" : [\"\\\\{\", \"\\\\}\"],  // [ and  ] are brackets too, but these get handled in with lists\n      \"arcsWords\" : [\"note\", \"abox\", \"rbox\", \"box\", \"alt\", \"else\", \"opt\", \"break\", \"par\", \"seq\", \"strict\", \"neg\", \"critical\", \"ignore\", \"consider\", \"assert\", \"loop\", \"ref\", \"exc\"],\n      \"arcsOthers\" : [\"\\\\|\\\\|\\\\|\", \"\\\\.\\\\.\\\\.\", \"---\", \"--\", \"<->\", \"==\", \"<<=>>\", \"<=>\", \"\\\\.\\\\.\", \"<<>>\", \"::\", \"<:>\", \"->\", \"=>>\", \"=>\", \">>\", \":>\", \"<-\", \"<<=\", \"<=\", \"<<\", \"<:\", \"x-\", \"-x\"],\n      \"singlecomment\" : [\"//\", \"#\"],\n      \"operators\" : [\"=\"]\n    },\n    msgenny: {\n      \"keywords\" : null,\n      \"options\" : [\"hscale\", \"width\", \"arcgradient\", \"wordwraparcs\", \"wordwrapentities\", \"watermark\"],\n      \"constants\" : [\"true\", \"false\", \"on\", \"off\", \"auto\"],\n      \"attributes\" : null,\n      \"brackets\" : [\"\\\\{\", \"\\\\}\"],\n      \"arcsWords\" : [\"note\", \"abox\", \"rbox\", \"box\", \"alt\", \"else\", \"opt\", \"break\", \"par\", \"seq\", \"strict\", \"neg\", \"critical\", \"ignore\", \"consider\", \"assert\", \"loop\", \"ref\", \"exc\"],\n      \"arcsOthers\" : [\"\\\\|\\\\|\\\\|\", \"\\\\.\\\\.\\\\.\", \"---\", \"--\", \"<->\", \"==\", \"<<=>>\", \"<=>\", \"\\\\.\\\\.\", \"<<>>\", \"::\", \"<:>\", \"->\", \"=>>\", \"=>\", \">>\", \":>\", \"<-\", \"<<=\", \"<=\", \"<<\", \"<:\", \"x-\", \"-x\"],\n      \"singlecomment\" : [\"//\", \"#\"],\n      \"operators\" : [\"=\"]\n    }\n  }\n\n  CodeMirror.defineMode(\"mscgen\", function(_, modeConfig) {\n    var language = languages[modeConfig && modeConfig.language || \"mscgen\"]\n    return {\n      startState: startStateFn,\n      copyState: copyStateFn,\n      token: produceTokenFunction(language),\n      lineComment : \"#\",\n      blockCommentStart : \"/*\",\n      blockCommentEnd : \"*/\"\n    };\n  });\n\n  CodeMirror.defineMIME(\"text/x-mscgen\", \"mscgen\");\n  CodeMirror.defineMIME(\"text/x-xu\", {name: \"mscgen\", language: \"xu\"});\n  CodeMirror.defineMIME(\"text/x-msgenny\", {name: \"mscgen\", language: \"msgenny\"});\n\n  function wordRegexpBoundary(pWords) {\n    return new RegExp(\"^\\\\b(?:\" + pWords.join(\"|\") + \")\\\\b\", \"i\");\n  }\n\n  function wordRegexp(pWords) {\n    return new RegExp(\"^(?:\" + pWords.join(\"|\") + \")\", \"i\");\n  }\n\n  function startStateFn() {\n    return {\n      inComment : false,\n      inString : false,\n      inAttributeList : false,\n      inScript : false\n    };\n  }\n\n  function copyStateFn(pState) {\n    return {\n      inComment : pState.inComment,\n      inString : pState.inString,\n      inAttributeList : pState.inAttributeList,\n      inScript : pState.inScript\n    };\n  }\n\n  function produceTokenFunction(pConfig) {\n\n    return function(pStream, pState) {\n      if (pStream.match(wordRegexp(pConfig.brackets), true, true)) {\n        return \"bracket\";\n      }\n      /* comments */\n      if (!pState.inComment) {\n        if (pStream.match(/\\/\\*[^\\*\\/]*/, true, true)) {\n          pState.inComment = true;\n          return \"comment\";\n        }\n        if (pStream.match(wordRegexp(pConfig.singlecomment), true, true)) {\n          pStream.skipToEnd();\n          return \"comment\";\n        }\n      }\n      if (pState.inComment) {\n        if (pStream.match(/[^\\*\\/]*\\*\\//, true, true))\n          pState.inComment = false;\n        else\n          pStream.skipToEnd();\n        return \"comment\";\n      }\n      /* strings */\n      if (!pState.inString && pStream.match(/\\\"(\\\\\\\"|[^\\\"])*/, true, true)) {\n        pState.inString = true;\n        return \"string\";\n      }\n      if (pState.inString) {\n        if (pStream.match(/[^\\\"]*\\\"/, true, true))\n          pState.inString = false;\n        else\n          pStream.skipToEnd();\n        return \"string\";\n      }\n      /* keywords & operators */\n      if (!!pConfig.keywords && pStream.match(wordRegexpBoundary(pConfig.keywords), true, true))\n        return \"keyword\";\n\n      if (pStream.match(wordRegexpBoundary(pConfig.options), true, true))\n        return \"keyword\";\n\n      if (pStream.match(wordRegexpBoundary(pConfig.arcsWords), true, true))\n        return \"keyword\";\n\n      if (pStream.match(wordRegexp(pConfig.arcsOthers), true, true))\n        return \"keyword\";\n\n      if (!!pConfig.operators && pStream.match(wordRegexp(pConfig.operators), true, true))\n        return \"operator\";\n\n      if (!!pConfig.constants && pStream.match(wordRegexp(pConfig.constants), true, true))\n        return \"variable\";\n\n      /* attribute lists */\n      if (!pConfig.inAttributeList && !!pConfig.attributes && pStream.match('[', true, true)) {\n        pConfig.inAttributeList = true;\n        return \"bracket\";\n      }\n      if (pConfig.inAttributeList) {\n        if (pConfig.attributes !== null && pStream.match(wordRegexpBoundary(pConfig.attributes), true, true)) {\n          return \"attribute\";\n        }\n        if (pStream.match(']', true, true)) {\n          pConfig.inAttributeList = false;\n          return \"bracket\";\n        }\n      }\n\n      pStream.next();\n      return \"base\";\n    };\n  }\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mscgen/mscgen_test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"mscgen\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"empty chart\",\n     \"[keyword msc][bracket {]\",\n     \"[base   ]\",\n     \"[bracket }]\"\n   );\n\n  MT(\"comments\",\n    \"[comment // a single line comment]\",\n    \"[comment # another  single line comment /* and */ ignored here]\",\n    \"[comment /* A multi-line comment even though it contains]\",\n    \"[comment msc keywords and \\\"quoted text\\\"*/]\");\n\n  MT(\"strings\",\n    \"[string \\\"// a string\\\"]\",\n    \"[string \\\"a string running over]\",\n    \"[string two lines\\\"]\",\n    \"[string \\\"with \\\\\\\"escaped quote\\\"]\"\n  );\n\n  MT(\"xù/ msgenny keywords classify as 'base'\",\n    \"[base watermark]\",\n    \"[base wordwrapentities]\",\n    \"[base alt loop opt ref else break par seq assert]\"\n  );\n\n  MT(\"xù/ msgenny constants classify as 'base'\",\n    \"[base auto]\"\n  );\n\n  MT(\"mscgen constants classify as 'variable'\",\n    \"[variable true]\", \"[variable false]\", \"[variable on]\", \"[variable off]\"\n  );\n\n  MT(\"mscgen options classify as keyword\",\n    \"[keyword hscale]\", \"[keyword width]\", \"[keyword arcgradient]\", \"[keyword wordwraparcs]\"\n  );\n\n  MT(\"mscgen arcs classify as keyword\",\n    \"[keyword note]\",\"[keyword abox]\",\"[keyword rbox]\",\"[keyword box]\",\n    \"[keyword |||...---]\", \"[keyword ..--==::]\",\n    \"[keyword ->]\", \"[keyword <-]\", \"[keyword <->]\",\n    \"[keyword =>]\", \"[keyword <=]\", \"[keyword <=>]\",\n    \"[keyword =>>]\", \"[keyword <<=]\", \"[keyword <<=>>]\",\n    \"[keyword >>]\", \"[keyword <<]\", \"[keyword <<>>]\",\n    \"[keyword -x]\", \"[keyword x-]\", \"[keyword -X]\", \"[keyword X-]\",\n    \"[keyword :>]\", \"[keyword <:]\", \"[keyword <:>]\"\n  );\n\n  MT(\"within an attribute list, attributes classify as attribute\",\n    \"[bracket [[][attribute label]\",\n    \"[attribute id]\",\"[attribute url]\",\"[attribute idurl]\",\n    \"[attribute linecolor]\",\"[attribute linecolour]\",\"[attribute textcolor]\",\"[attribute textcolour]\",\"[attribute textbgcolor]\",\"[attribute textbgcolour]\",\n    \"[attribute arclinecolor]\",\"[attribute arclinecolour]\",\"[attribute arctextcolor]\",\"[attribute arctextcolour]\",\"[attribute arctextbgcolor]\",\"[attribute arctextbgcolour]\",\n    \"[attribute arcskip][bracket ]]]\"\n  );\n\n  MT(\"outside an attribute list, attributes classify as base\",\n    \"[base label]\",\n    \"[base id]\",\"[base url]\",\"[base idurl]\",\n    \"[base linecolor]\",\"[base linecolour]\",\"[base textcolor]\",\"[base textcolour]\",\"[base textbgcolor]\",\"[base textbgcolour]\",\n    \"[base arclinecolor]\",\"[base arclinecolour]\",\"[base arctextcolor]\",\"[base arctextcolour]\",\"[base arctextbgcolor]\",\"[base arctextbgcolour]\",\n    \"[base arcskip]\"\n  );\n\n  MT(\"a typical program\",\n    \"[comment # typical mscgen program]\",\n    \"[keyword msc][base  ][bracket {]\",\n    \"[keyword wordwraparcs][operator =][variable true][base , ][keyword hscale][operator =][string \\\"0.8\\\"][base , ][keyword arcgradient][operator =][base 30;]\",\n    \"[base   a][bracket [[][attribute label][operator =][string \\\"Entity A\\\"][bracket ]]][base ,]\",\n    \"[base   b][bracket [[][attribute label][operator =][string \\\"Entity B\\\"][bracket ]]][base ,]\",\n    \"[base   c][bracket [[][attribute label][operator =][string \\\"Entity C\\\"][bracket ]]][base ;]\",\n    \"[base   a ][keyword =>>][base  b][bracket [[][attribute label][operator =][string \\\"Hello entity B\\\"][bracket ]]][base ;]\",\n    \"[base   a ][keyword <<][base  b][bracket [[][attribute label][operator =][string \\\"Here's an answer dude!\\\"][bracket ]]][base ;]\",\n    \"[base   c ][keyword :>][base  *][bracket [[][attribute label][operator =][string \\\"What about me?\\\"][base , ][attribute textcolor][operator =][base red][bracket ]]][base ;]\",\n    \"[bracket }]\"\n  );\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mscgen/msgenny_test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"text/x-msgenny\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), \"msgenny\"); }\n\n  MT(\"comments\",\n    \"[comment // a single line comment]\",\n    \"[comment # another  single line comment /* and */ ignored here]\",\n    \"[comment /* A multi-line comment even though it contains]\",\n    \"[comment msc keywords and \\\"quoted text\\\"*/]\");\n\n  MT(\"strings\",\n    \"[string \\\"// a string\\\"]\",\n    \"[string \\\"a string running over]\",\n    \"[string two lines\\\"]\",\n    \"[string \\\"with \\\\\\\"escaped quote\\\"]\"\n  );\n\n  MT(\"xù/ msgenny keywords classify as 'keyword'\",\n    \"[keyword watermark]\",\n    \"[keyword wordwrapentities]\",\n    \"[keyword alt]\",\"[keyword loop]\",\"[keyword opt]\",\"[keyword ref]\",\"[keyword else]\",\"[keyword break]\",\"[keyword par]\",\"[keyword seq]\",\"[keyword assert]\"\n  );\n\n  MT(\"xù/ msgenny constants classify as 'variable'\",\n    \"[variable auto]\",\n    \"[variable true]\", \"[variable false]\", \"[variable on]\", \"[variable off]\"\n  );\n\n  MT(\"mscgen options classify as keyword\",\n    \"[keyword hscale]\", \"[keyword width]\", \"[keyword arcgradient]\", \"[keyword wordwraparcs]\"\n  );\n\n  MT(\"mscgen arcs classify as keyword\",\n    \"[keyword note]\",\"[keyword abox]\",\"[keyword rbox]\",\"[keyword box]\",\n    \"[keyword |||...---]\", \"[keyword ..--==::]\",\n    \"[keyword ->]\", \"[keyword <-]\", \"[keyword <->]\",\n    \"[keyword =>]\", \"[keyword <=]\", \"[keyword <=>]\",\n    \"[keyword =>>]\", \"[keyword <<=]\", \"[keyword <<=>>]\",\n    \"[keyword >>]\", \"[keyword <<]\", \"[keyword <<>>]\",\n    \"[keyword -x]\", \"[keyword x-]\", \"[keyword -X]\", \"[keyword X-]\",\n    \"[keyword :>]\", \"[keyword <:]\", \"[keyword <:>]\"\n  );\n\n  MT(\"within an attribute list, mscgen/ xù attributes classify as base\",\n    \"[base [[label]\",\n    \"[base idurl id url]\",\n    \"[base linecolor linecolour textcolor textcolour textbgcolor textbgcolour]\",\n    \"[base arclinecolor arclinecolour arctextcolor arctextcolour arctextbgcolor arctextbgcolour]\",\n    \"[base arcskip]]]\"\n  );\n\n  MT(\"outside an attribute list, mscgen/ xù attributes classify as base\",\n    \"[base label]\",\n    \"[base idurl id url]\",\n    \"[base linecolor linecolour textcolor textcolour textbgcolor textbgcolour]\",\n    \"[base arclinecolor arclinecolour arctextcolor arctextcolour arctextbgcolor arctextbgcolour]\",\n    \"[base arcskip]\"\n  );\n\n  MT(\"a typical program\",\n    \"[comment # typical msgenny program]\",\n    \"[keyword wordwraparcs][operator =][variable true][base , ][keyword hscale][operator =][string \\\"0.8\\\"][base , ][keyword arcgradient][operator =][base 30;]\",\n    \"[base   a : ][string \\\"Entity A\\\"][base ,]\",\n    \"[base   b : Entity B,]\",\n    \"[base   c : Entity C;]\",\n    \"[base   a ][keyword =>>][base  b: ][string \\\"Hello entity B\\\"][base ;]\",\n    \"[base   a ][keyword alt][base  c][bracket {]\",\n    \"[base     a ][keyword <<][base  b: ][string \\\"Here's an answer dude!\\\"][base ;]\",\n    \"[keyword ---][base : ][string \\\"sorry, won't march - comm glitch\\\"]\",\n    \"[base     a ][keyword x-][base  b: ][string \\\"Here's an answer dude! (won't arrive...)\\\"][base ;]\",\n    \"[bracket }]\",\n    \"[base   c ][keyword :>][base  *: What about me?;]\"\n  );\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mscgen/xu_test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"text/x-xu\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), \"xu\"); }\n\n  MT(\"empty chart\",\n     \"[keyword msc][bracket {]\",\n     \"[base   ]\",\n     \"[bracket }]\"\n  );\n\n  MT(\"empty chart\",\n     \"[keyword xu][bracket {]\",\n     \"[base   ]\",\n     \"[bracket }]\"\n  );\n\n  MT(\"comments\",\n    \"[comment // a single line comment]\",\n    \"[comment # another  single line comment /* and */ ignored here]\",\n    \"[comment /* A multi-line comment even though it contains]\",\n    \"[comment msc keywords and \\\"quoted text\\\"*/]\");\n\n  MT(\"strings\",\n    \"[string \\\"// a string\\\"]\",\n    \"[string \\\"a string running over]\",\n    \"[string two lines\\\"]\",\n    \"[string \\\"with \\\\\\\"escaped quote\\\"]\"\n  );\n\n  MT(\"xù/ msgenny keywords classify as 'keyword'\",\n    \"[keyword watermark]\",\n    \"[keyword alt]\",\"[keyword loop]\",\"[keyword opt]\",\"[keyword ref]\",\"[keyword else]\",\"[keyword break]\",\"[keyword par]\",\"[keyword seq]\",\"[keyword assert]\"\n  );\n\n  MT(\"xù/ msgenny constants classify as 'variable'\",\n    \"[variable auto]\",\n    \"[variable true]\", \"[variable false]\", \"[variable on]\", \"[variable off]\"\n  );\n\n  MT(\"mscgen options classify as keyword\",\n    \"[keyword hscale]\", \"[keyword width]\", \"[keyword arcgradient]\", \"[keyword wordwraparcs]\"\n  );\n\n  MT(\"mscgen arcs classify as keyword\",\n    \"[keyword note]\",\"[keyword abox]\",\"[keyword rbox]\",\"[keyword box]\",\n    \"[keyword |||...---]\", \"[keyword ..--==::]\",\n    \"[keyword ->]\", \"[keyword <-]\", \"[keyword <->]\",\n    \"[keyword =>]\", \"[keyword <=]\", \"[keyword <=>]\",\n    \"[keyword =>>]\", \"[keyword <<=]\", \"[keyword <<=>>]\",\n    \"[keyword >>]\", \"[keyword <<]\", \"[keyword <<>>]\",\n    \"[keyword -x]\", \"[keyword x-]\", \"[keyword -X]\", \"[keyword X-]\",\n    \"[keyword :>]\", \"[keyword <:]\", \"[keyword <:>]\"\n  );\n\n  MT(\"within an attribute list, attributes classify as attribute\",\n    \"[bracket [[][attribute label]\",\n    \"[attribute id]\",\"[attribute url]\",\"[attribute idurl]\",\n    \"[attribute linecolor]\",\"[attribute linecolour]\",\"[attribute textcolor]\",\"[attribute textcolour]\",\"[attribute textbgcolor]\",\"[attribute textbgcolour]\",\n    \"[attribute arclinecolor]\",\"[attribute arclinecolour]\",\"[attribute arctextcolor]\",\"[attribute arctextcolour]\",\"[attribute arctextbgcolor]\",\"[attribute arctextbgcolour]\",\n    \"[attribute arcskip]\",\"[attribute title]\",\n    \"[attribute activate]\",\"[attribute deactivate]\",\"[attribute activation][bracket ]]]\"\n  );\n\n  MT(\"outside an attribute list, attributes classify as base\",\n    \"[base label]\",\n    \"[base id]\",\"[base url]\",\"[base idurl]\",\n    \"[base linecolor]\",\"[base linecolour]\",\"[base textcolor]\",\"[base textcolour]\",\"[base textbgcolor]\",\"[base textbgcolour]\",\n    \"[base arclinecolor]\",\"[base arclinecolour]\",\"[base arctextcolor]\",\"[base arctextcolour]\",\"[base arctextbgcolor]\",\"[base arctextbgcolour]\",\n    \"[base arcskip]\", \"[base title]\"\n  );\n\n  MT(\"a typical program\",\n    \"[comment # typical xu program]\",\n    \"[keyword xu][base  ][bracket {]\",\n    \"[keyword wordwraparcs][operator =][string \\\"true\\\"][base , ][keyword hscale][operator =][string \\\"0.8\\\"][base , ][keyword arcgradient][operator =][base 30, ][keyword width][operator =][variable auto][base ;]\",\n    \"[base   a][bracket [[][attribute label][operator =][string \\\"Entity A\\\"][bracket ]]][base ,]\",\n    \"[base   b][bracket [[][attribute label][operator =][string \\\"Entity B\\\"][bracket ]]][base ,]\",\n    \"[base   c][bracket [[][attribute label][operator =][string \\\"Entity C\\\"][bracket ]]][base ;]\",\n    \"[base   a ][keyword =>>][base  b][bracket [[][attribute label][operator =][string \\\"Hello entity B\\\"][bracket ]]][base ;]\",\n    \"[base   a ][keyword <<][base  b][bracket [[][attribute label][operator =][string \\\"Here's an answer dude!\\\"][base , ][attribute title][operator =][string \\\"This is a title for this message\\\"][bracket ]]][base ;]\",\n    \"[base   c ][keyword :>][base  *][bracket [[][attribute label][operator =][string \\\"What about me?\\\"][base , ][attribute textcolor][operator =][base red][bracket ]]][base ;]\",\n    \"[bracket }]\"\n  );\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mumps/index.html",
    "content": "﻿<!doctype html>\n\n<title>CodeMirror: MUMPS mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"mumps.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">MUMPS</a>\n  </ul>\n</div>\n\n<article>\n<h2>MUMPS mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n ; Lloyd Milligan\n ; 03-30-2015\n ;\n ; MUMPS support for Code Mirror - Excerpts below from routine ^XUS\n ;\nCHECKAV(X1) ;Check A/V code return DUZ or Zero. (Called from XUSRB)\n N %,%1,X,Y,IEN,DA,DIK\n S IEN=0\n ;Start CCOW\n I $E(X1,1,7)=\"~~TOK~~\" D  Q:IEN>0 IEN\n . I $E(X1,8,9)=\"~1\" S IEN=$$CHKASH^XUSRB4($E(X1,8,255))\n . I $E(X1,8,9)=\"~2\" S IEN=$$CHKCCOW^XUSRB4($E(X1,8,255))\n . Q\n ;End CCOW\n S X1=$$UP(X1) S:X1[\":\" XUTT=1,X1=$TR(X1,\":\")\n S X=$P(X1,\";\") Q:X=\"^\" -1 S:XUF %1=\"Access: \"_X\n Q:X'?1.20ANP 0\n S X=$$EN^XUSHSH(X) I '$D(^VA(200,\"A\",X)) D LBAV Q 0\n S %1=\"\",IEN=$O(^VA(200,\"A\",X,0)),XUF(.3)=IEN D USER(IEN)\n S X=$P(X1,\";\",2) S:XUF %1=\"Verify: \"_X S X=$$EN^XUSHSH(X)\n I $P(XUSER(1),\"^\",2)'=X D LBAV Q 0\n I $G(XUFAC(1)) S DIK=\"^XUSEC(4,\",DA=XUFAC(1) D ^DIK\n Q IEN\n ;\n ; Spell out commands\n ;\nSET2() ;EF. Return error code (also called from XUSRB)\n NEW %,X\n SET XUNOW=$$HTFM^XLFDT($H),DT=$P(XUNOW,\".\")\n KILL DUZ,XUSER\n SET (DUZ,DUZ(2))=0,(DUZ(0),DUZ(\"AG\"),XUSER(0),XUSER(1),XUTT,%UCI)=\"\"\n SET %=$$INHIBIT^XUSRB() IF %>0 QUIT %\n SET X=$G(^%ZIS(1,XUDEV,\"XUS\")),XU1=$G(^(1))\n IF $L(X) FOR I=1:1:15 IF $L($P(X,U,I)) SET $P(XOPT,U,I)=$P(X,U,I)\n SET DTIME=600\n IF '$P(XOPT,U,11),$D(^%ZIS(1,XUDEV,90)),^(90)>2800000,^(90)'>DT QUIT 8\n QUIT 0\n ;\n ; Spell out commands and functions\n ;\n IF $PIECE(XUSER(0),U,11),$PIECE(XUSER(0),U,11)'>DT QUIT 11 ;Terminated\n IF $DATA(DUZ(\"ASH\")) QUIT 0 ;If auto handle, Allow to sign-on p434\n IF $PIECE(XUSER(0),U,7) QUIT 5 ;Disuser flag set\n IF '$LENGTH($PIECE(XUSER(1),U,2)) QUIT 21 ;p419, p434\n Q 0\n ;\n  </textarea></div>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n         mode: \"mumps\",\n         lineNumbers: true,\n         lineWrapping: true\n      });\n    </script>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/mumps/mumps.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/*\n  This MUMPS Language script was constructed using vbscript.js as a template.\n*/\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"mumps\", function() {\n    function wordRegexp(words) {\n      return new RegExp(\"^((\" + words.join(\")|(\") + \"))\\\\b\", \"i\");\n    }\n\n    var singleOperators = new RegExp(\"^[\\\\+\\\\-\\\\*/&#!_?\\\\\\\\<>=\\\\'\\\\[\\\\]]\");\n    var doubleOperators = new RegExp(\"^(('=)|(<=)|(>=)|('>)|('<)|([[)|(]])|(^$))\");\n    var singleDelimiters = new RegExp(\"^[\\\\.,:]\");\n    var brackets = new RegExp(\"[()]\");\n    var identifiers = new RegExp(\"^[%A-Za-z][A-Za-z0-9]*\");\n    var commandKeywords = [\"break\",\"close\",\"do\",\"else\",\"for\",\"goto\", \"halt\", \"hang\", \"if\", \"job\",\"kill\",\"lock\",\"merge\",\"new\",\"open\", \"quit\", \"read\", \"set\", \"tcommit\", \"trollback\", \"tstart\", \"use\", \"view\", \"write\", \"xecute\", \"b\",\"c\",\"d\",\"e\",\"f\",\"g\", \"h\", \"i\", \"j\",\"k\",\"l\",\"m\",\"n\",\"o\", \"q\", \"r\", \"s\", \"tc\", \"tro\", \"ts\", \"u\", \"v\", \"w\", \"x\"];\n    // The following list includes intrinsic functions _and_ special variables\n    var intrinsicFuncsWords = [\"\\\\$ascii\", \"\\\\$char\", \"\\\\$data\", \"\\\\$ecode\", \"\\\\$estack\", \"\\\\$etrap\", \"\\\\$extract\", \"\\\\$find\", \"\\\\$fnumber\", \"\\\\$get\", \"\\\\$horolog\", \"\\\\$io\", \"\\\\$increment\", \"\\\\$job\", \"\\\\$justify\", \"\\\\$length\", \"\\\\$name\", \"\\\\$next\", \"\\\\$order\", \"\\\\$piece\", \"\\\\$qlength\", \"\\\\$qsubscript\", \"\\\\$query\", \"\\\\$quit\", \"\\\\$random\", \"\\\\$reverse\", \"\\\\$select\", \"\\\\$stack\", \"\\\\$test\", \"\\\\$text\", \"\\\\$translate\", \"\\\\$view\", \"\\\\$x\", \"\\\\$y\", \"\\\\$a\", \"\\\\$c\", \"\\\\$d\", \"\\\\$e\", \"\\\\$ec\", \"\\\\$es\", \"\\\\$et\", \"\\\\$f\", \"\\\\$fn\", \"\\\\$g\", \"\\\\$h\", \"\\\\$i\", \"\\\\$j\", \"\\\\$l\", \"\\\\$n\", \"\\\\$na\", \"\\\\$o\", \"\\\\$p\", \"\\\\$q\", \"\\\\$ql\", \"\\\\$qs\", \"\\\\$r\", \"\\\\$re\", \"\\\\$s\", \"\\\\$st\", \"\\\\$t\", \"\\\\$tr\", \"\\\\$v\", \"\\\\$z\"];\n    var intrinsicFuncs = wordRegexp(intrinsicFuncsWords);\n    var command = wordRegexp(commandKeywords);\n\n    function tokenBase(stream, state) {\n      if (stream.sol()) {\n        state.label = true;\n        state.commandMode = 0;\n      }\n\n      // The <space> character has meaning in MUMPS. Ignoring consecutive\n      // spaces would interfere with interpreting whether the next non-space\n      // character belongs to the command or argument context.\n\n      // Examine each character and update a mode variable whose interpretation is:\n      //   >0 => command    0 => argument    <0 => command post-conditional\n      var ch = stream.peek();\n\n      if (ch == \" \" || ch == \"\\t\") { // Pre-process <space>\n        state.label = false;\n        if (state.commandMode == 0)\n          state.commandMode = 1;\n        else if ((state.commandMode < 0) || (state.commandMode == 2))\n          state.commandMode = 0;\n      } else if ((ch != \".\") && (state.commandMode > 0)) {\n        if (ch == \":\")\n          state.commandMode = -1;   // SIS - Command post-conditional\n        else\n          state.commandMode = 2;\n      }\n\n      // Do not color parameter list as line tag\n      if ((ch === \"(\") || (ch === \"\\u0009\"))\n        state.label = false;\n\n      // MUMPS comment starts with \";\"\n      if (ch === \";\") {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n\n      // Number Literals // SIS/RLM - MUMPS permits canonic number followed by concatenate operator\n      if (stream.match(/^[-+]?\\d+(\\.\\d+)?([eE][-+]?\\d+)?/))\n        return \"number\";\n\n      // Handle Strings\n      if (ch == '\"') {\n        if (stream.skipTo('\"')) {\n          stream.next();\n          return \"string\";\n        } else {\n          stream.skipToEnd();\n          return \"error\";\n        }\n      }\n\n      // Handle operators and Delimiters\n      if (stream.match(doubleOperators) || stream.match(singleOperators))\n        return \"operator\";\n\n      // Prevents leading \".\" in DO block from falling through to error\n      if (stream.match(singleDelimiters))\n        return null;\n\n      if (brackets.test(ch)) {\n        stream.next();\n        return \"bracket\";\n      }\n\n      if (state.commandMode > 0 && stream.match(command))\n        return \"variable-2\";\n\n      if (stream.match(intrinsicFuncs))\n        return \"builtin\";\n\n      if (stream.match(identifiers))\n        return \"variable\";\n\n      // Detect dollar-sign when not a documented intrinsic function\n      // \"^\" may introduce a GVN or SSVN - Color same as function\n      if (ch === \"$\" || ch === \"^\") {\n        stream.next();\n        return \"builtin\";\n      }\n\n      // MUMPS Indirection\n      if (ch === \"@\") {\n        stream.next();\n        return \"string-2\";\n      }\n\n      if (/[\\w%]/.test(ch)) {\n        stream.eatWhile(/[\\w%]/);\n        return \"variable\";\n      }\n\n      // Handle non-detected items\n      stream.next();\n      return \"error\";\n    }\n\n    return {\n      startState: function() {\n        return {\n          label: false,\n          commandMode: 0\n        };\n      },\n\n      token: function(stream, state) {\n        var style = tokenBase(stream, state);\n        if (state.label) return \"tag\";\n        return style;\n      }\n    };\n  });\n\n  CodeMirror.defineMIME(\"text/x-mumps\", \"mumps\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/nginx/index.html",
    "content": "<!doctype html>\n<head>\n<title>CodeMirror: NGINX mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"nginx.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n    <link rel=\"stylesheet\" href=\"../../doc/docs.css\">\n  </head>\n\n  <style>\n    body {\n      margin: 0em auto;\n    }\n\n    .CodeMirror, .CodeMirror-scroll {\n      height: 600px;\n    }\n  </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">NGINX</a>\n  </ul>\n</div>\n\n<article>\n<h2>NGINX mode</h2>\n<form><textarea id=\"code\" name=\"code\" style=\"height: 800px;\">\nserver {\n  listen 173.255.219.235:80;\n  server_name website.com.au;\n  rewrite / $scheme://www.$host$request_uri permanent; ## Forcibly prepend a www\n}\n\nserver {\n  listen 173.255.219.235:443;\n  server_name website.com.au;\n  rewrite / $scheme://www.$host$request_uri permanent; ## Forcibly prepend a www\n}\n\nserver {\n\n  listen      173.255.219.235:80;\n  server_name www.website.com.au;\n\n\n\n  root        /data/www;\n  index       index.html index.php;\n\n  location / {\n    index index.html index.php;     ## Allow a static html file to be shown first\n    try_files $uri $uri/ @handler;  ## If missing pass the URI to Magento's front handler\n    expires 30d;                    ## Assume all files are cacheable\n  }\n\n  ## These locations would be hidden by .htaccess normally\n  location /app/                { deny all; }\n  location /includes/           { deny all; }\n  location /lib/                { deny all; }\n  location /media/downloadable/ { deny all; }\n  location /pkginfo/            { deny all; }\n  location /report/config.xml   { deny all; }\n  location /var/                { deny all; }\n\n  location /var/export/ { ## Allow admins only to view export folder\n    auth_basic           \"Restricted\"; ## Message shown in login window\n    auth_basic_user_file /rs/passwords/testfile; ## See /etc/nginx/htpassword\n    autoindex            on;\n  }\n\n  location  /. { ## Disable .htaccess and other hidden files\n    return 404;\n  }\n\n  location @handler { ## Magento uses a common front handler\n    rewrite / /index.php;\n  }\n\n  location ~ .php/ { ## Forward paths like /js/index.php/x.js to relevant handler\n    rewrite ^/(.*.php)/ /$1 last;\n  }\n\n  location ~ \\.php$ {\n    if (!-e $request_filename) { rewrite / /index.php last; } ## Catch 404s that try_files miss\n\n    fastcgi_pass   127.0.0.1:9000;\n    fastcgi_index  index.php;\n    fastcgi_param PATH_INFO $fastcgi_script_name;\n    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;\n    include        /rs/confs/nginx/fastcgi_params;\n  }\n\n}\n\n\nserver {\n\n  listen              173.255.219.235:443;\n  server_name         website.com.au www.website.com.au;\n\n  root   /data/www;\n  index index.html index.php;\n\n  ssl                 on;\n  ssl_certificate     /rs/ssl/ssl.crt;\n  ssl_certificate_key /rs/ssl/ssl.key;\n\n  ssl_session_timeout  5m;\n\n  ssl_protocols  SSLv2 SSLv3 TLSv1;\n  ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;\n  ssl_prefer_server_ciphers   on;\n\n\n\n  location / {\n    index index.html index.php; ## Allow a static html file to be shown first\n    try_files $uri $uri/ @handler; ## If missing pass the URI to Magento's front handler\n    expires 30d; ## Assume all files are cacheable\n  }\n\n  ## These locations would be hidden by .htaccess normally\n  location /app/                { deny all; }\n  location /includes/           { deny all; }\n  location /lib/                { deny all; }\n  location /media/downloadable/ { deny all; }\n  location /pkginfo/            { deny all; }\n  location /report/config.xml   { deny all; }\n  location /var/                { deny all; }\n\n  location /var/export/ { ## Allow admins only to view export folder\n    auth_basic           \"Restricted\"; ## Message shown in login window\n    auth_basic_user_file htpasswd; ## See /etc/nginx/htpassword\n    autoindex            on;\n  }\n\n  location  /. { ## Disable .htaccess and other hidden files\n    return 404;\n  }\n\n  location @handler { ## Magento uses a common front handler\n    rewrite / /index.php;\n  }\n\n  location ~ .php/ { ## Forward paths like /js/index.php/x.js to relevant handler\n    rewrite ^/(.*.php)/ /$1 last;\n  }\n\n  location ~ .php$ { ## Execute PHP scripts\n    if (!-e $request_filename) { rewrite  /index.php last; } ## Catch 404s that try_files miss\n\n    fastcgi_pass 127.0.0.1:9000;\n    fastcgi_index  index.php;\n    fastcgi_param PATH_INFO $fastcgi_script_name;\n    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;\n    include        /rs/confs/nginx/fastcgi_params;\n\n    fastcgi_param HTTPS on;\n  }\n\n}\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-nginx-conf</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/nginx/nginx.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"nginx\", function(config) {\n\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  var keywords = words(\n    /* ngxDirectiveControl */ \"break return rewrite set\" +\n    /* ngxDirective */ \" accept_mutex accept_mutex_delay access_log add_after_body add_before_body add_header addition_types aio alias allow ancient_browser ancient_browser_value auth_basic auth_basic_user_file auth_http auth_http_header auth_http_timeout autoindex autoindex_exact_size autoindex_localtime charset charset_types client_body_buffer_size client_body_in_file_only client_body_in_single_buffer client_body_temp_path client_body_timeout client_header_buffer_size client_header_timeout client_max_body_size connection_pool_size create_full_put_path daemon dav_access dav_methods debug_connection debug_points default_type degradation degrade deny devpoll_changes devpoll_events directio directio_alignment empty_gif env epoll_events error_log eventport_events expires fastcgi_bind fastcgi_buffer_size fastcgi_buffers fastcgi_busy_buffers_size fastcgi_cache fastcgi_cache_key fastcgi_cache_methods fastcgi_cache_min_uses fastcgi_cache_path fastcgi_cache_use_stale fastcgi_cache_valid fastcgi_catch_stderr fastcgi_connect_timeout fastcgi_hide_header fastcgi_ignore_client_abort fastcgi_ignore_headers fastcgi_index fastcgi_intercept_errors fastcgi_max_temp_file_size fastcgi_next_upstream fastcgi_param fastcgi_pass_header fastcgi_pass_request_body fastcgi_pass_request_headers fastcgi_read_timeout fastcgi_send_lowat fastcgi_send_timeout fastcgi_split_path_info fastcgi_store fastcgi_store_access fastcgi_temp_file_write_size fastcgi_temp_path fastcgi_upstream_fail_timeout fastcgi_upstream_max_fails flv geoip_city geoip_country google_perftools_profiles gzip gzip_buffers gzip_comp_level gzip_disable gzip_hash gzip_http_version gzip_min_length gzip_no_buffer gzip_proxied gzip_static gzip_types gzip_vary gzip_window if_modified_since ignore_invalid_headers image_filter image_filter_buffer image_filter_jpeg_quality image_filter_transparency imap_auth imap_capabilities imap_client_buffer index ip_hash keepalive_requests keepalive_timeout kqueue_changes kqueue_events large_client_header_buffers limit_conn limit_conn_log_level limit_rate limit_rate_after limit_req limit_req_log_level limit_req_zone limit_zone lingering_time lingering_timeout lock_file log_format log_not_found log_subrequest map_hash_bucket_size map_hash_max_size master_process memcached_bind memcached_buffer_size memcached_connect_timeout memcached_next_upstream memcached_read_timeout memcached_send_timeout memcached_upstream_fail_timeout memcached_upstream_max_fails merge_slashes min_delete_depth modern_browser modern_browser_value msie_padding msie_refresh multi_accept open_file_cache open_file_cache_errors open_file_cache_events open_file_cache_min_uses open_file_cache_valid open_log_file_cache output_buffers override_charset perl perl_modules perl_require perl_set pid pop3_auth pop3_capabilities port_in_redirect postpone_gzipping postpone_output protocol proxy proxy_bind proxy_buffer proxy_buffer_size proxy_buffering proxy_buffers proxy_busy_buffers_size proxy_cache proxy_cache_key proxy_cache_methods proxy_cache_min_uses proxy_cache_path proxy_cache_use_stale proxy_cache_valid proxy_connect_timeout proxy_headers_hash_bucket_size proxy_headers_hash_max_size proxy_hide_header proxy_ignore_client_abort proxy_ignore_headers proxy_intercept_errors proxy_max_temp_file_size proxy_method proxy_next_upstream proxy_pass_error_message proxy_pass_header proxy_pass_request_body proxy_pass_request_headers proxy_read_timeout proxy_redirect proxy_send_lowat proxy_send_timeout proxy_set_body proxy_set_header proxy_ssl_session_reuse proxy_store proxy_store_access proxy_temp_file_write_size proxy_temp_path proxy_timeout proxy_upstream_fail_timeout proxy_upstream_max_fails random_index read_ahead real_ip_header recursive_error_pages request_pool_size reset_timedout_connection resolver resolver_timeout rewrite_log rtsig_overflow_events rtsig_overflow_test rtsig_overflow_threshold rtsig_signo satisfy secure_link_secret send_lowat send_timeout sendfile sendfile_max_chunk server_name_in_redirect server_names_hash_bucket_size server_names_hash_max_size server_tokens set_real_ip_from smtp_auth smtp_capabilities smtp_client_buffer smtp_greeting_delay so_keepalive source_charset ssi ssi_ignore_recycled_buffers ssi_min_file_chunk ssi_silent_errors ssi_types ssi_value_length ssl ssl_certificate ssl_certificate_key ssl_ciphers ssl_client_certificate ssl_crl ssl_dhparam ssl_engine ssl_prefer_server_ciphers ssl_protocols ssl_session_cache ssl_session_timeout ssl_verify_client ssl_verify_depth starttls stub_status sub_filter sub_filter_once sub_filter_types tcp_nodelay tcp_nopush thread_stack_size timeout timer_resolution types_hash_bucket_size types_hash_max_size underscores_in_headers uninitialized_variable_warn use user userid userid_domain userid_expires userid_mark userid_name userid_p3p userid_path userid_service valid_referers variables_hash_bucket_size variables_hash_max_size worker_connections worker_cpu_affinity worker_priority worker_processes worker_rlimit_core worker_rlimit_nofile worker_rlimit_sigpending worker_threads working_directory xclient xml_entities xslt_stylesheet xslt_typesdrew@li229-23\"\n    );\n\n  var keywords_block = words(\n    /* ngxDirectiveBlock */ \"http mail events server types location upstream charset_map limit_except if geo map\"\n    );\n\n  var keywords_important = words(\n    /* ngxDirectiveImportant */ \"include root server server_name listen internal proxy_pass memcached_pass fastcgi_pass try_files\"\n    );\n\n  var indentUnit = config.indentUnit, type;\n  function ret(style, tp) {type = tp; return style;}\n\n  function tokenBase(stream, state) {\n\n\n    stream.eatWhile(/[\\w\\$_]/);\n\n    var cur = stream.current();\n\n\n    if (keywords.propertyIsEnumerable(cur)) {\n      return \"keyword\";\n    }\n    else if (keywords_block.propertyIsEnumerable(cur)) {\n      return \"variable-2\";\n    }\n    else if (keywords_important.propertyIsEnumerable(cur)) {\n      return \"string-2\";\n    }\n    /**/\n\n    var ch = stream.next();\n    if (ch == \"@\") {stream.eatWhile(/[\\w\\\\\\-]/); return ret(\"meta\", stream.current());}\n    else if (ch == \"/\" && stream.eat(\"*\")) {\n      state.tokenize = tokenCComment;\n      return tokenCComment(stream, state);\n    }\n    else if (ch == \"<\" && stream.eat(\"!\")) {\n      state.tokenize = tokenSGMLComment;\n      return tokenSGMLComment(stream, state);\n    }\n    else if (ch == \"=\") ret(null, \"compare\");\n    else if ((ch == \"~\" || ch == \"|\") && stream.eat(\"=\")) return ret(null, \"compare\");\n    else if (ch == \"\\\"\" || ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    }\n    else if (ch == \"#\") {\n      stream.skipToEnd();\n      return ret(\"comment\", \"comment\");\n    }\n    else if (ch == \"!\") {\n      stream.match(/^\\s*\\w*/);\n      return ret(\"keyword\", \"important\");\n    }\n    else if (/\\d/.test(ch)) {\n      stream.eatWhile(/[\\w.%]/);\n      return ret(\"number\", \"unit\");\n    }\n    else if (/[,.+>*\\/]/.test(ch)) {\n      return ret(null, \"select-op\");\n    }\n    else if (/[;{}:\\[\\]]/.test(ch)) {\n      return ret(null, ch);\n    }\n    else {\n      stream.eatWhile(/[\\w\\\\\\-]/);\n      return ret(\"variable\", \"variable\");\n    }\n  }\n\n  function tokenCComment(stream, state) {\n    var maybeEnd = false, ch;\n    while ((ch = stream.next()) != null) {\n      if (maybeEnd && ch == \"/\") {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return ret(\"comment\", \"comment\");\n  }\n\n  function tokenSGMLComment(stream, state) {\n    var dashes = 0, ch;\n    while ((ch = stream.next()) != null) {\n      if (dashes >= 2 && ch == \">\") {\n        state.tokenize = tokenBase;\n        break;\n      }\n      dashes = (ch == \"-\") ? dashes + 1 : 0;\n    }\n    return ret(\"comment\", \"comment\");\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, ch;\n      while ((ch = stream.next()) != null) {\n        if (ch == quote && !escaped)\n          break;\n        escaped = !escaped && ch == \"\\\\\";\n      }\n      if (!escaped) state.tokenize = tokenBase;\n      return ret(\"string\", \"string\");\n    };\n  }\n\n  return {\n    startState: function(base) {\n      return {tokenize: tokenBase,\n              baseIndent: base || 0,\n              stack: []};\n    },\n\n    token: function(stream, state) {\n      if (stream.eatSpace()) return null;\n      type = null;\n      var style = state.tokenize(stream, state);\n\n      var context = state.stack[state.stack.length-1];\n      if (type == \"hash\" && context == \"rule\") style = \"atom\";\n      else if (style == \"variable\") {\n        if (context == \"rule\") style = \"number\";\n        else if (!context || context == \"@media{\") style = \"tag\";\n      }\n\n      if (context == \"rule\" && /^[\\{\\};]$/.test(type))\n        state.stack.pop();\n      if (type == \"{\") {\n        if (context == \"@media\") state.stack[state.stack.length-1] = \"@media{\";\n        else state.stack.push(\"{\");\n      }\n      else if (type == \"}\") state.stack.pop();\n      else if (type == \"@media\") state.stack.push(\"@media\");\n      else if (context == \"{\" && type != \"comment\") state.stack.push(\"rule\");\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      var n = state.stack.length;\n      if (/^\\}/.test(textAfter))\n        n -= state.stack[state.stack.length-1] == \"rule\" ? 2 : 1;\n      return state.baseIndent + n * indentUnit;\n    },\n\n    electricChars: \"}\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-nginx-conf\", \"nginx\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/nsis/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: NSIS mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=stylesheet href=../../lib/codemirror.css>\n<script src=../../lib/codemirror.js></script>\n<script src=\"../../addon/mode/simple.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=nsis.js></script>\n<style type=text/css>\n  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">NSIS</a>\n  </ul>\n</div>\n\n<article>\n<h2>NSIS mode</h2>\n\n\n<textarea id=code>\n; This is a comment\n!ifdef ERROR\n    !error \"Something went wrong\"\n!endif\n\nOutFile \"demo.exe\"\nRequestExecutionLevel user\nSetDetailsPrint listonly\n\n!include \"LogicLib.nsh\"\n!include \"WinVer.nsh\"\n\nSection -mandatory\n\n    Call logWinVer\n\n    ${If} 1 > 0\n      MessageBox MB_OK \"Hello world\"\n    ${EndIf}\n\nSectionEnd\n\nFunction logWinVer\n\n    ${If} ${IsWin10}\n        DetailPrint \"Windows 10!\"\n    ${ElseIf} ${AtLeastWinVista}\n        DetailPrint \"We're post-XP\"\n    ${Else}\n        DetailPrint \"Legacy system\"\n    ${EndIf}\n\nFunctionEnd\n</textarea>\n\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById('code'), {\n    mode: 'nsis',\n    indentWithTabs: true,\n    smartIndent: true,\n    lineNumbers: true,\n    matchBrackets: true\n  });\n</script>\n\n<p><strong>MIME types defined:</strong> <code>text/x-nsis</code>.</p>\n</article>"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/nsis/nsis.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Author: Jan T. Sott (http://github.com/idleberg)\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../../addon/mode/simple\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../../addon/mode/simple\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineSimpleMode(\"nsis\",{\n  start:[\n    // Numbers\n    {regex: /(?:[+-]?)(?:0x[\\d,a-f]+)|(?:0o[0-7]+)|(?:0b[0,1]+)|(?:\\d+.?\\d*)/, token: \"number\"},\n\n    // Strings\n    { regex: /\"(?:[^\\\\\"]|\\\\.)*\"?/, token: \"string\" },\n    { regex: /'(?:[^\\\\']|\\\\.)*'?/, token: \"string\" },\n    { regex: /`(?:[^\\\\`]|\\\\.)*`?/, token: \"string\" },\n\n    // Compile Time Commands\n    {regex: /^\\s*(?:\\!(addincludedir|addplugindir|appendfile|assert|cd|define|delfile|echo|error|execute|finalize|getdllversion|gettlbversion|include|insertmacro|macro|macroend|makensis|packhdr|pragma|searchparse|searchreplace|system|tempfile|undef|uninstfinalize|verbose|warning))\\b/i, token: \"keyword\"},\n\n    // Conditional Compilation\n    {regex: /^\\s*(?:\\!(if(?:n?def)?|ifmacron?def|macro))\\b/i, token: \"keyword\", indent: true},\n    {regex: /^\\s*(?:\\!(else|endif|macroend))\\b/i, token: \"keyword\", dedent: true},\n\n    // Runtime Commands\n    {regex: /^\\s*(?:Abort|AddBrandingImage|AddSize|AllowRootDirInstall|AllowSkipFiles|AutoCloseWindow|BGFont|BGGradient|BrandingText|BringToFront|Call|CallInstDLL|Caption|ChangeUI|CheckBitmap|ClearErrors|CompletedText|ComponentText|CopyFiles|CRCCheck|CreateDirectory|CreateFont|CreateShortCut|Delete|DeleteINISec|DeleteINIStr|DeleteRegKey|DeleteRegValue|DetailPrint|DetailsButtonText|DirText|DirVar|DirVerify|EnableWindow|EnumRegKey|EnumRegValue|Exch|Exec|ExecShell|ExecShellWait|ExecWait|ExpandEnvStrings|File|FileBufSize|FileClose|FileErrorText|FileOpen|FileRead|FileReadByte|FileReadUTF16LE|FileReadWord|FileWriteUTF16LE|FileSeek|FileWrite|FileWriteByte|FileWriteWord|FindClose|FindFirst|FindNext|FindWindow|FlushINI|GetCurInstType|GetCurrentAddress|GetDlgItem|GetDLLVersion|GetDLLVersionLocal|GetErrorLevel|GetFileTime|GetFileTimeLocal|GetFullPathName|GetFunctionAddress|GetInstDirError|GetKnownFolderPath|GetLabelAddress|GetTempFileName|GetWinVer|Goto|HideWindow|Icon|IfAbort|IfErrors|IfFileExists|IfRebootFlag|IfRtlLanguage|IfShellVarContextAll|IfSilent|InitPluginsDir|InstallButtonText|InstallColors|InstallDir|InstallDirRegKey|InstProgressFlags|InstType|InstTypeGetText|InstTypeSetText|Int64Cmp|Int64CmpU|Int64Fmt|IntCmp|IntCmpU|IntFmt|IntOp|IntPtrCmp|IntPtrCmpU|IntPtrOp|IsWindow|LangString|LicenseBkColor|LicenseData|LicenseForceSelection|LicenseLangString|LicenseText|LoadAndSetImage|LoadLanguageFile|LockWindow|LogSet|LogText|ManifestDPIAware|ManifestLongPathAware|ManifestMaxVersionTested|ManifestSupportedOS|MessageBox|MiscButtonText|Name|Nop|OutFile|Page|PageCallbacks|PEAddResource|PEDllCharacteristics|PERemoveResource|PESubsysVer|Pop|Push|Quit|ReadEnvStr|ReadINIStr|ReadRegDWORD|ReadRegStr|Reboot|RegDLL|Rename|RequestExecutionLevel|ReserveFile|Return|RMDir|SearchPath|SectionGetFlags|SectionGetInstTypes|SectionGetSize|SectionGetText|SectionIn|SectionSetFlags|SectionSetInstTypes|SectionSetSize|SectionSetText|SendMessage|SetAutoClose|SetBrandingImage|SetCompress|SetCompressor|SetCompressorDictSize|SetCtlColors|SetCurInstType|SetDatablockOptimize|SetDateSave|SetDetailsPrint|SetDetailsView|SetErrorLevel|SetErrors|SetFileAttributes|SetFont|SetOutPath|SetOverwrite|SetRebootFlag|SetRegView|SetShellVarContext|SetSilent|ShowInstDetails|ShowUninstDetails|ShowWindow|SilentInstall|SilentUnInstall|Sleep|SpaceTexts|StrCmp|StrCmpS|StrCpy|StrLen|SubCaption|Target|Unicode|UninstallButtonText|UninstallCaption|UninstallIcon|UninstallSubCaption|UninstallText|UninstPage|UnRegDLL|Var|VIAddVersionKey|VIFileVersion|VIProductVersion|WindowIcon|WriteINIStr|WriteRegBin|WriteRegDWORD|WriteRegExpandStr|WriteRegMultiStr|WriteRegNone|WriteRegStr|WriteUninstaller|XPStyle)\\b/i, token: \"keyword\"},\n    {regex: /^\\s*(?:Function|PageEx|Section(?:Group)?)\\b/i, token: \"keyword\", indent: true},\n    {regex: /^\\s*(?:(Function|PageEx|Section(?:Group)?)End)\\b/i, token: \"keyword\", dedent: true},\n\n    // Command Options\n    {regex: /\\b(?:ARCHIVE|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY|HIDDEN|HKCC|HKCR(32|64)?|HKCU(32|64)?|HKDD|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_DYN_DATA|HKEY_LOCAL_MACHINE|HKEY_PERFORMANCE_DATA|HKEY_USERS|HKLM(32|64)?|HKPD|HKU|IDABORT|IDCANCEL|IDD_DIR|IDD_INST|IDD_INSTFILES|IDD_LICENSE|IDD_SELCOM|IDD_UNINST|IDD_VERIFY|IDIGNORE|IDNO|IDOK|IDRETRY|IDYES|MB_ABORTRETRYIGNORE|MB_DEFBUTTON1|MB_DEFBUTTON2|MB_DEFBUTTON3|MB_DEFBUTTON4|MB_ICONEXCLAMATION|MB_ICONINFORMATION|MB_ICONQUESTION|MB_ICONSTOP|MB_OK|MB_OKCANCEL|MB_RETRYCANCEL|MB_RIGHT|MB_RTLREADING|MB_SETFOREGROUND|MB_TOPMOST|MB_USERICON|MB_YESNO|MB_YESNOCANCEL|NORMAL|OFFLINE|READONLY|SHCTX|SHELL_CONTEXT|SW_HIDE|SW_SHOWDEFAULT|SW_SHOWMAXIMIZED|SW_SHOWMINIMIZED|SW_SHOWNORMAL|SYSTEM|TEMPORARY)\\b/i, token: \"atom\"},\n    {regex: /\\b(?:admin|all|amd64-unicode|auto|both|bottom|bzip2|components|current|custom|directory|false|force|hide|highest|ifdiff|ifnewer|instfiles|lastused|leave|left|license|listonly|lzma|nevershow|none|normal|notset|off|on|right|show|silent|silentlog|textonly|top|true|try|un\\.components|un\\.custom|un\\.directory|un\\.instfiles|un\\.license|uninstConfirm|user|Win10|Win7|Win8|WinVista|x-86-(ansi|unicode)|zlib)\\b/i, token: \"builtin\"},\n\n    // LogicLib.nsh\n    {regex: /\\$\\{(?:And(?:If(?:Not)?|Unless)|Break|Case(?:2|3|4|5|Else)?|Continue|Default|Do(?:Until|While)?|Else(?:If(?:Not)?|Unless)?|End(?:If|Select|Switch)|Exit(?:Do|For|While)|For(?:Each)?|If(?:Cmd|Not(?:Then)?|Then)?|Loop(?:Until|While)?|Or(?:If(?:Not)?|Unless)|Select|Switch|Unless|While)\\}/i, token: \"variable-2\", indent: true},\n\n    // FileFunc.nsh\n    {regex: /\\$\\{(?:BannerTrimPath|DirState|DriveSpace|Get(BaseName|Drives|ExeName|ExePath|FileAttributes|FileExt|FileName|FileVersion|Options|OptionsS|Parameters|Parent|Root|Size|Time)|Locate|RefreshShellIcons)\\}/i, token: \"variable-2\", dedent: true},\n\n    // Memento.nsh\n    {regex: /\\$\\{(?:Memento(?:Section(?:Done|End|Restore|Save)?|UnselectedSection))\\}/i, token: \"variable-2\", dedent: true},\n\n    // TextFunc.nsh\n    {regex: /\\$\\{(?:Config(?:Read|ReadS|Write|WriteS)|File(?:Join|ReadFromEnd|Recode)|Line(?:Find|Read|Sum)|Text(?:Compare|CompareS)|TrimNewLines)\\}/i, token: \"variable-2\", dedent: true},\n\n    // WinVer.nsh\n    {regex: /\\$\\{(?:(?:At(?:Least|Most)|Is)(?:ServicePack|Win(?:7|8|10|95|98|200(?:0|3|8(?:R2)?)|ME|NT4|Vista|XP))|Is(?:NT|Server))\\}/i, token: \"variable\", dedent: true},\n\n    // WordFunc.nsh\n    {regex: /\\$\\{(?:StrFilterS?|Version(?:Compare|Convert)|Word(?:AddS?|Find(?:(?:2|3)X)?S?|InsertS?|ReplaceS?))\\}/i, token: \"variable-2\", dedent: true},\n\n    // x64.nsh\n    {regex: /\\$\\{(?:RunningX64)\\}/i, token: \"variable\", dedent: true},\n    {regex: /\\$\\{(?:Disable|Enable)X64FSRedirection\\}/i, token: \"variable-2\", dedent: true},\n\n    // Line Comment\n    {regex: /(#|;).*/, token: \"comment\"},\n\n    // Block Comment\n    {regex: /\\/\\*/, token: \"comment\", next: \"comment\"},\n\n    // Operator\n    {regex: /[-+\\/*=<>!]+/, token: \"operator\"},\n\n    // Variable\n    {regex: /\\$\\w[\\w\\.]*/, token: \"variable\"},\n\n    // Constant\n    {regex: /\\${[\\!\\w\\.:-]+}/, token: \"variable-2\"},\n\n    // Language String\n    {regex: /\\$\\([\\!\\w\\.:-]+\\)/, token: \"variable-3\"}\n  ],\n  comment: [\n    {regex: /.*?\\*\\//, token: \"comment\", next: \"start\"},\n    {regex: /.*/, token: \"comment\"}\n  ],\n  meta: {\n    electricInput: /^\\s*((Function|PageEx|Section|Section(Group)?)End|(\\!(endif|macroend))|\\$\\{(End(If|Unless|While)|Loop(Until)|Next)\\})$/i,\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    lineComment: [\"#\", \";\"]\n  }\n});\n\nCodeMirror.defineMIME(\"text/x-nsis\", \"nsis\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ntriples/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: N-Triples mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"ntriples.js\"></script>\n<style>\n      .CodeMirror {\n        border: 1px solid #eee;\n        height: auto;\n      }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">N-Triples/N-Quads</a>\n  </ul>\n</div>\n\n<article>\n  <h2><a href=\"https://www.w3.org/TR/n-triples/\">N-Triples</a> mode</h2>\n  <p>The N-Triples mode also works well with on\n    <a href=\"https://www.w3.org/TR/n-quads/\">N-Quad</a> documents.\n  </p>\n<form>\n<textarea id=\"ntriples\" name=\"ntriples\">    \n<http://Sub1>     <http://pred1>     <http://obj> .\n<http://Sub2>     <http://pred2#an2> \"literal 1\" .\n<http://Sub3#an3> <http://pred3>     _:bnode3 .\n_:bnode4          <http://pred4>     \"literal 2\"@lang .\n_:bnode5          <http://pred5>     \"literal 3\"^^<http://type> .\n</textarea>\n</form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"ntriples\"), {});\n    </script>\n    <p><strong>MIME types defined:</strong> <code>application/n-triples</code>.</p>\n\n    <hr />\n    <p><a href=\"https://www.w3.org/TR/n-quads/\">N-Quads</a> add a fourth\n    element to the statement to track which graph the statement is from.\n    Otherwise, it's identical to N-Triples.</p>\n    <form>\n    <textarea id=\"nquads\" name=\"nquads\">\n\n    <http://Sub1>     <http://pred1>     <http://obj>   <http://graph3> .\n    <http://Sub2>     <http://pred2#an2> \"literal 1\"    <http://graph2> .\n    <http://Sub3#an3> <http://pred3>     _:bnode3     <http://graph2> .\n    _:bnode4          <http://pred4>     \"literal 2\"@lang     <http://graph2> .\n    # if a graph label\n    _:bnode5          <http://pred5>     \"literal 3\"^^<http://type> .\n    </textarea>\n    </form>\n\n    <script>\n      var nquads_editor = CodeMirror.fromTextArea(document.getElementById(\"nquads\"), {});\n    </script>\n    <p><strong>MIME types defined:</strong> <code>application/n-quads</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ntriples/ntriples.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/**********************************************************\n* This script provides syntax highlighting support for\n* the N-Triples format.\n* N-Triples format specification:\n*     https://www.w3.org/TR/n-triples/\n***********************************************************/\n\n/*\n    The following expression defines the defined ASF grammar transitions.\n\n    pre_subject ->\n        {\n        ( writing_subject_uri | writing_bnode_uri )\n            -> pre_predicate\n                -> writing_predicate_uri\n                    -> pre_object\n                        -> writing_object_uri | writing_object_bnode |\n                          (\n                            writing_object_literal\n                                -> writing_literal_lang | writing_literal_type\n                          )\n                            -> post_object\n                                -> BEGIN\n         } otherwise {\n             -> ERROR\n         }\n*/\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"ntriples\", function() {\n\n  var Location = {\n    PRE_SUBJECT         : 0,\n    WRITING_SUB_URI     : 1,\n    WRITING_BNODE_URI   : 2,\n    PRE_PRED            : 3,\n    WRITING_PRED_URI    : 4,\n    PRE_OBJ             : 5,\n    WRITING_OBJ_URI     : 6,\n    WRITING_OBJ_BNODE   : 7,\n    WRITING_OBJ_LITERAL : 8,\n    WRITING_LIT_LANG    : 9,\n    WRITING_LIT_TYPE    : 10,\n    POST_OBJ            : 11,\n    ERROR               : 12\n  };\n  function transitState(currState, c) {\n    var currLocation = currState.location;\n    var ret;\n\n    // Opening.\n    if     (currLocation == Location.PRE_SUBJECT && c == '<') ret = Location.WRITING_SUB_URI;\n    else if(currLocation == Location.PRE_SUBJECT && c == '_') ret = Location.WRITING_BNODE_URI;\n    else if(currLocation == Location.PRE_PRED    && c == '<') ret = Location.WRITING_PRED_URI;\n    else if(currLocation == Location.PRE_OBJ     && c == '<') ret = Location.WRITING_OBJ_URI;\n    else if(currLocation == Location.PRE_OBJ     && c == '_') ret = Location.WRITING_OBJ_BNODE;\n    else if(currLocation == Location.PRE_OBJ     && c == '\"') ret = Location.WRITING_OBJ_LITERAL;\n\n    // Closing.\n    else if(currLocation == Location.WRITING_SUB_URI     && c == '>') ret = Location.PRE_PRED;\n    else if(currLocation == Location.WRITING_BNODE_URI   && c == ' ') ret = Location.PRE_PRED;\n    else if(currLocation == Location.WRITING_PRED_URI    && c == '>') ret = Location.PRE_OBJ;\n    else if(currLocation == Location.WRITING_OBJ_URI     && c == '>') ret = Location.POST_OBJ;\n    else if(currLocation == Location.WRITING_OBJ_BNODE   && c == ' ') ret = Location.POST_OBJ;\n    else if(currLocation == Location.WRITING_OBJ_LITERAL && c == '\"') ret = Location.POST_OBJ;\n    else if(currLocation == Location.WRITING_LIT_LANG && c == ' ') ret = Location.POST_OBJ;\n    else if(currLocation == Location.WRITING_LIT_TYPE && c == '>') ret = Location.POST_OBJ;\n\n    // Closing typed and language literal.\n    else if(currLocation == Location.WRITING_OBJ_LITERAL && c == '@') ret = Location.WRITING_LIT_LANG;\n    else if(currLocation == Location.WRITING_OBJ_LITERAL && c == '^') ret = Location.WRITING_LIT_TYPE;\n\n    // Spaces.\n    else if( c == ' ' &&\n             (\n               currLocation == Location.PRE_SUBJECT ||\n               currLocation == Location.PRE_PRED    ||\n               currLocation == Location.PRE_OBJ     ||\n               currLocation == Location.POST_OBJ\n             )\n           ) ret = currLocation;\n\n    // Reset.\n    else if(currLocation == Location.POST_OBJ && c == '.') ret = Location.PRE_SUBJECT;\n\n    // Error\n    else ret = Location.ERROR;\n\n    currState.location=ret;\n  }\n\n  return {\n    startState: function() {\n       return {\n           location : Location.PRE_SUBJECT,\n           uris     : [],\n           anchors  : [],\n           bnodes   : [],\n           langs    : [],\n           types    : []\n       };\n    },\n    token: function(stream, state) {\n      var ch = stream.next();\n      if(ch == '<') {\n         transitState(state, ch);\n         var parsedURI = '';\n         stream.eatWhile( function(c) { if( c != '#' && c != '>' ) { parsedURI += c; return true; } return false;} );\n         state.uris.push(parsedURI);\n         if( stream.match('#', false) ) return 'variable';\n         stream.next();\n         transitState(state, '>');\n         return 'variable';\n      }\n      if(ch == '#') {\n        var parsedAnchor = '';\n        stream.eatWhile(function(c) { if(c != '>' && c != ' ') { parsedAnchor+= c; return true; } return false;});\n        state.anchors.push(parsedAnchor);\n        return 'variable-2';\n      }\n      if(ch == '>') {\n          transitState(state, '>');\n          return 'variable';\n      }\n      if(ch == '_') {\n          transitState(state, ch);\n          var parsedBNode = '';\n          stream.eatWhile(function(c) { if( c != ' ' ) { parsedBNode += c; return true; } return false;});\n          state.bnodes.push(parsedBNode);\n          stream.next();\n          transitState(state, ' ');\n          return 'builtin';\n      }\n      if(ch == '\"') {\n          transitState(state, ch);\n          stream.eatWhile( function(c) { return c != '\"'; } );\n          stream.next();\n          if( stream.peek() != '@' && stream.peek() != '^' ) {\n              transitState(state, '\"');\n          }\n          return 'string';\n      }\n      if( ch == '@' ) {\n          transitState(state, '@');\n          var parsedLang = '';\n          stream.eatWhile(function(c) { if( c != ' ' ) { parsedLang += c; return true; } return false;});\n          state.langs.push(parsedLang);\n          stream.next();\n          transitState(state, ' ');\n          return 'string-2';\n      }\n      if( ch == '^' ) {\n          stream.next();\n          transitState(state, '^');\n          var parsedType = '';\n          stream.eatWhile(function(c) { if( c != '>' ) { parsedType += c; return true; } return false;} );\n          state.types.push(parsedType);\n          stream.next();\n          transitState(state, '>');\n          return 'variable';\n      }\n      if( ch == ' ' ) {\n          transitState(state, ch);\n      }\n      if( ch == '.' ) {\n          transitState(state, ch);\n      }\n    }\n  };\n});\n\n// define the registered Media Type for n-triples:\n// https://www.w3.org/TR/n-triples/#n-triples-mediatype\nCodeMirror.defineMIME(\"application/n-triples\", \"ntriples\");\n\n// N-Quads is based on the N-Triples format (so same highlighting works)\n// https://www.w3.org/TR/n-quads/\nCodeMirror.defineMIME(\"application/n-quads\", \"ntriples\");\n\n// previously used, though technically incorrect media type for n-triples\nCodeMirror.defineMIME(\"text/n-triples\", \"ntriples\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/octave/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Octave mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"octave.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Octave</a>\n  </ul>\n</div>\n\n<article>\n<h2>Octave mode</h2>\n\n    <div><textarea id=\"code\" name=\"code\">\n%numbers\n[1234 1234i 1234j]\n[.234 .234j 2.23i]\n[23e2 12E1j 123D-4 0x234]\n\n%strings\n'asda''a'\n\"asda\"\"a\"\n\n%identifiers\na + as123 - __asd__\n\n%operators\n-\n+\n=\n==\n>\n<\n>=\n<=\n&\n~\n...\nbreak zeros default margin round ones rand\nceil floor size clear zeros eye mean std cov\nerror eval function\nabs acos atan asin cos cosh exp log prod sum\nlog10 max min sign sin sinh sqrt tan reshape\nreturn\ncase switch\nelse elseif end if otherwise\ndo for while\ntry catch\nclassdef properties events methods\nglobal persistent\n\n%one line comment\n%{ multi \nline comment %}\n\n    </textarea></div>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: {name: \"octave\",\n               version: 2,\n               singleLineStringErrors: false},\n        lineNumbers: true,\n        indentUnit: 4,\n        matchBrackets: true\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-octave</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/octave/octave.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"octave\", function() {\n  function wordRegexp(words) {\n    return new RegExp(\"^((\" + words.join(\")|(\") + \"))\\\\b\");\n  }\n\n  var singleOperators = new RegExp(\"^[\\\\+\\\\-\\\\*/&|\\\\^~<>!@'\\\\\\\\]\");\n  var singleDelimiters = new RegExp('^[\\\\(\\\\[\\\\{\\\\},:=;\\\\.]');\n  var doubleOperators = new RegExp(\"^((==)|(~=)|(<=)|(>=)|(<<)|(>>)|(\\\\.[\\\\+\\\\-\\\\*/\\\\^\\\\\\\\]))\");\n  var doubleDelimiters = new RegExp(\"^((!=)|(\\\\+=)|(\\\\-=)|(\\\\*=)|(/=)|(&=)|(\\\\|=)|(\\\\^=))\");\n  var tripleDelimiters = new RegExp(\"^((>>=)|(<<=))\");\n  var expressionEnd = new RegExp(\"^[\\\\]\\\\)]\");\n  var identifiers = new RegExp(\"^[_A-Za-z\\xa1-\\uffff][_A-Za-z0-9\\xa1-\\uffff]*\");\n\n  var builtins = wordRegexp([\n    'error', 'eval', 'function', 'abs', 'acos', 'atan', 'asin', 'cos',\n    'cosh', 'exp', 'log', 'prod', 'sum', 'log10', 'max', 'min', 'sign', 'sin', 'sinh',\n    'sqrt', 'tan', 'reshape', 'break', 'zeros', 'default', 'margin', 'round', 'ones',\n    'rand', 'syn', 'ceil', 'floor', 'size', 'clear', 'zeros', 'eye', 'mean', 'std', 'cov',\n    'det', 'eig', 'inv', 'norm', 'rank', 'trace', 'expm', 'logm', 'sqrtm', 'linspace', 'plot',\n    'title', 'xlabel', 'ylabel', 'legend', 'text', 'grid', 'meshgrid', 'mesh', 'num2str',\n    'fft', 'ifft', 'arrayfun', 'cellfun', 'input', 'fliplr', 'flipud', 'ismember'\n  ]);\n\n  var keywords = wordRegexp([\n    'return', 'case', 'switch', 'else', 'elseif', 'end', 'endif', 'endfunction',\n    'if', 'otherwise', 'do', 'for', 'while', 'try', 'catch', 'classdef', 'properties', 'events',\n    'methods', 'global', 'persistent', 'endfor', 'endwhile', 'printf', 'sprintf', 'disp', 'until',\n    'continue', 'pkg'\n  ]);\n\n\n  // tokenizers\n  function tokenTranspose(stream, state) {\n    if (!stream.sol() && stream.peek() === '\\'') {\n      stream.next();\n      state.tokenize = tokenBase;\n      return 'operator';\n    }\n    state.tokenize = tokenBase;\n    return tokenBase(stream, state);\n  }\n\n\n  function tokenComment(stream, state) {\n    if (stream.match(/^.*%}/)) {\n      state.tokenize = tokenBase;\n      return 'comment';\n    };\n    stream.skipToEnd();\n    return 'comment';\n  }\n\n  function tokenBase(stream, state) {\n    // whitespaces\n    if (stream.eatSpace()) return null;\n\n    // Handle one line Comments\n    if (stream.match('%{')){\n      state.tokenize = tokenComment;\n      stream.skipToEnd();\n      return 'comment';\n    }\n\n    if (stream.match(/^[%#]/)){\n      stream.skipToEnd();\n      return 'comment';\n    }\n\n    // Handle Number Literals\n    if (stream.match(/^[0-9\\.+-]/, false)) {\n      if (stream.match(/^[+-]?0x[0-9a-fA-F]+[ij]?/)) {\n        stream.tokenize = tokenBase;\n        return 'number'; };\n      if (stream.match(/^[+-]?\\d*\\.\\d+([EeDd][+-]?\\d+)?[ij]?/)) { return 'number'; };\n      if (stream.match(/^[+-]?\\d+([EeDd][+-]?\\d+)?[ij]?/)) { return 'number'; };\n    }\n    if (stream.match(wordRegexp(['nan','NaN','inf','Inf']))) { return 'number'; };\n\n    // Handle Strings\n    var m = stream.match(/^\"(?:[^\"]|\"\")*(\"|$)/) || stream.match(/^'(?:[^']|'')*('|$)/)\n    if (m) { return m[1] ? 'string' : \"string error\"; }\n\n    // Handle words\n    if (stream.match(keywords)) { return 'keyword'; } ;\n    if (stream.match(builtins)) { return 'builtin'; } ;\n    if (stream.match(identifiers)) { return 'variable'; } ;\n\n    if (stream.match(singleOperators) || stream.match(doubleOperators)) { return 'operator'; };\n    if (stream.match(singleDelimiters) || stream.match(doubleDelimiters) || stream.match(tripleDelimiters)) { return null; };\n\n    if (stream.match(expressionEnd)) {\n      state.tokenize = tokenTranspose;\n      return null;\n    };\n\n\n    // Handle non-detected items\n    stream.next();\n    return 'error';\n  };\n\n\n  return {\n    startState: function() {\n      return {\n        tokenize: tokenBase\n      };\n    },\n\n    token: function(stream, state) {\n      var style = state.tokenize(stream, state);\n      if (style === 'number' || style === 'variable'){\n        state.tokenize = tokenTranspose;\n      }\n      return style;\n    },\n\n    lineComment: '%',\n\n    fold: 'indent'\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-octave\", \"octave\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/oz/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Oz mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"oz.js\"></script>\n<script src=\"../../addon/runmode/runmode.js\"></script>\n<style>\n  .CodeMirror {border: 1px solid #aaa;}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Oz</a>\n  </ul>\n</div>\n\n<article>\n<h2>Oz mode</h2>\n<textarea id=\"code\" name=\"code\">\ndeclare\nfun {Ints N Max}\n  if N == Max then nil\n  else\n    {Delay 1000}\n    N|{Ints N+1 Max}\n  end\nend\n\nfun {Sum S Stream}\n  case Stream of nil then S\n  [] H|T then S|{Sum H+S T} end\nend\n\nlocal X Y in\n  thread X = {Ints 0 1000} end\n  thread Y = {Sum 0 X} end\n  {Browse Y}\nend\n</textarea>\n<p>MIME type defined: <code>text/x-oz</code>.</p>\n\n<script>\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    lineNumbers: true,\n    mode: \"text/x-oz\",\n    readOnly: false\n});\n</script>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/oz/oz.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"oz\", function (conf) {\n\n  function wordRegexp(words) {\n    return new RegExp(\"^((\" + words.join(\")|(\") + \"))\\\\b\");\n  }\n\n  var singleOperators = /[\\^@!\\|<>#~\\.\\*\\-\\+\\\\/,=]/;\n  var doubleOperators = /(<-)|(:=)|(=<)|(>=)|(<=)|(<:)|(>:)|(=:)|(\\\\=)|(\\\\=:)|(!!)|(==)|(::)/;\n  var tripleOperators = /(:::)|(\\.\\.\\.)|(=<:)|(>=:)/;\n\n  var middle = [\"in\", \"then\", \"else\", \"of\", \"elseof\", \"elsecase\", \"elseif\", \"catch\",\n    \"finally\", \"with\", \"require\", \"prepare\", \"import\", \"export\", \"define\", \"do\"];\n  var end = [\"end\"];\n\n  var atoms = wordRegexp([\"true\", \"false\", \"nil\", \"unit\"]);\n  var commonKeywords = wordRegexp([\"andthen\", \"at\", \"attr\", \"declare\", \"feat\", \"from\", \"lex\",\n    \"mod\", \"div\", \"mode\", \"orelse\", \"parser\", \"prod\", \"prop\", \"scanner\", \"self\", \"syn\", \"token\"]);\n  var openingKeywords = wordRegexp([\"local\", \"proc\", \"fun\", \"case\", \"class\", \"if\", \"cond\", \"or\", \"dis\",\n    \"choice\", \"not\", \"thread\", \"try\", \"raise\", \"lock\", \"for\", \"suchthat\", \"meth\", \"functor\"]);\n  var middleKeywords = wordRegexp(middle);\n  var endKeywords = wordRegexp(end);\n\n  // Tokenizers\n  function tokenBase(stream, state) {\n    if (stream.eatSpace()) {\n      return null;\n    }\n\n    // Brackets\n    if(stream.match(/[{}]/)) {\n      return \"bracket\";\n    }\n\n    // Special [] keyword\n    if (stream.match('[]')) {\n        return \"keyword\"\n    }\n\n    // Operators\n    if (stream.match(tripleOperators) || stream.match(doubleOperators)) {\n      return \"operator\";\n    }\n\n    // Atoms\n    if(stream.match(atoms)) {\n      return 'atom';\n    }\n\n    // Opening keywords\n    var matched = stream.match(openingKeywords);\n    if (matched) {\n      if (!state.doInCurrentLine)\n        state.currentIndent++;\n      else\n        state.doInCurrentLine = false;\n\n      // Special matching for signatures\n      if(matched[0] == \"proc\" || matched[0] == \"fun\")\n        state.tokenize = tokenFunProc;\n      else if(matched[0] == \"class\")\n        state.tokenize = tokenClass;\n      else if(matched[0] == \"meth\")\n        state.tokenize = tokenMeth;\n\n      return 'keyword';\n    }\n\n    // Middle and other keywords\n    if (stream.match(middleKeywords) || stream.match(commonKeywords)) {\n      return \"keyword\"\n    }\n\n    // End keywords\n    if (stream.match(endKeywords)) {\n      state.currentIndent--;\n      return 'keyword';\n    }\n\n    // Eat the next char for next comparisons\n    var ch = stream.next();\n\n    // Strings\n    if (ch == '\"' || ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    }\n\n    // Numbers\n    if (/[~\\d]/.test(ch)) {\n      if (ch == \"~\") {\n        if(! /^[0-9]/.test(stream.peek()))\n          return null;\n        else if (( stream.next() == \"0\" && stream.match(/^[xX][0-9a-fA-F]+/)) || stream.match(/^[0-9]*(\\.[0-9]+)?([eE][~+]?[0-9]+)?/))\n          return \"number\";\n      }\n\n      if ((ch == \"0\" && stream.match(/^[xX][0-9a-fA-F]+/)) || stream.match(/^[0-9]*(\\.[0-9]+)?([eE][~+]?[0-9]+)?/))\n        return \"number\";\n\n      return null;\n    }\n\n    // Comments\n    if (ch == \"%\") {\n      stream.skipToEnd();\n      return 'comment';\n    }\n    else if (ch == \"/\") {\n      if (stream.eat(\"*\")) {\n        state.tokenize = tokenComment;\n        return tokenComment(stream, state);\n      }\n    }\n\n    // Single operators\n    if(singleOperators.test(ch)) {\n      return \"operator\";\n    }\n\n    // If nothing match, we skip the entire alphanumeric block\n    stream.eatWhile(/\\w/);\n\n    return \"variable\";\n  }\n\n  function tokenClass(stream, state) {\n    if (stream.eatSpace()) {\n      return null;\n    }\n    stream.match(/([A-Z][A-Za-z0-9_]*)|(`.+`)/);\n    state.tokenize = tokenBase;\n    return \"variable-3\"\n  }\n\n  function tokenMeth(stream, state) {\n    if (stream.eatSpace()) {\n      return null;\n    }\n    stream.match(/([a-zA-Z][A-Za-z0-9_]*)|(`.+`)/);\n    state.tokenize = tokenBase;\n    return \"def\"\n  }\n\n  function tokenFunProc(stream, state) {\n    if (stream.eatSpace()) {\n      return null;\n    }\n\n    if(!state.hasPassedFirstStage && stream.eat(\"{\")) {\n      state.hasPassedFirstStage = true;\n      return \"bracket\";\n    }\n    else if(state.hasPassedFirstStage) {\n      stream.match(/([A-Z][A-Za-z0-9_]*)|(`.+`)|\\$/);\n      state.hasPassedFirstStage = false;\n      state.tokenize = tokenBase;\n      return \"def\"\n    }\n    else {\n      state.tokenize = tokenBase;\n      return null;\n    }\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n\n  function tokenString(quote) {\n    return function (stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {\n          end = true;\n          break;\n        }\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (end || !escaped)\n        state.tokenize = tokenBase;\n      return \"string\";\n    };\n  }\n\n  function buildElectricInputRegEx() {\n    // Reindentation should occur on [] or on a match of any of\n    // the block closing keywords, at the end of a line.\n    var allClosings = middle.concat(end);\n    return new RegExp(\"[\\\\[\\\\]]|(\" + allClosings.join(\"|\") + \")$\");\n  }\n\n  return {\n\n    startState: function () {\n      return {\n        tokenize: tokenBase,\n        currentIndent: 0,\n        doInCurrentLine: false,\n        hasPassedFirstStage: false\n      };\n    },\n\n    token: function (stream, state) {\n      if (stream.sol())\n        state.doInCurrentLine = 0;\n\n      return state.tokenize(stream, state);\n    },\n\n    indent: function (state, textAfter) {\n      var trueText = textAfter.replace(/^\\s+|\\s+$/g, '');\n\n      if (trueText.match(endKeywords) || trueText.match(middleKeywords) || trueText.match(/(\\[])/))\n        return conf.indentUnit * (state.currentIndent - 1);\n\n      if (state.currentIndent < 0)\n        return 0;\n\n      return state.currentIndent * conf.indentUnit;\n    },\n    fold: \"indent\",\n    electricInput: buildElectricInputRegEx(),\n    lineComment: \"%\",\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-oz\", \"oz\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/pascal/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Pascal mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"pascal.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Pascal</a>\n  </ul>\n</div>\n\n<article>\n<h2>Pascal mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n(* Example Pascal code *)\n\nwhile a <> b do writeln('Waiting');\n \nif a > b then \n  writeln('Condition met')\nelse \n  writeln('Condition not met');\n \nfor i := 1 to 10 do \n  writeln('Iteration: ', i:1);\n \nrepeat\n  a := a + 1\nuntil a = 10;\n \ncase i of\n  0: write('zero');\n  1: write('one');\n  2: write('two')\nend;\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        mode: \"text/x-pascal\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-pascal</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/pascal/pascal.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"pascal\", function() {\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n  var keywords = words(\n    \"absolute and array asm begin case const constructor destructor div do \" +\n    \"downto else end file for function goto if implementation in inherited \" +\n    \"inline interface label mod nil not object of operator or packed procedure \" +\n    \"program record reintroduce repeat self set shl shr string then to type \" +\n    \"unit until uses var while with xor as class dispinterface except exports \" +\n    \"finalization finally initialization inline is library on out packed \" +\n    \"property raise resourcestring threadvar try absolute abstract alias \" +\n    \"assembler bitpacked break cdecl continue cppdecl cvar default deprecated \" +\n    \"dynamic enumerator experimental export external far far16 forward generic \" +\n    \"helper implements index interrupt iocheck local message name near \" +\n    \"nodefault noreturn nostackframe oldfpccall otherwise overload override \" +\n    \"pascal platform private protected public published read register \" +\n    \"reintroduce result safecall saveregisters softfloat specialize static \" +\n    \"stdcall stored strict unaligned unimplemented varargs virtual write\");\n  var atoms = {\"null\": true};\n\n  var isOperatorChar = /[+\\-*&%=<>!?|\\/]/;\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (ch == \"#\" && state.startOfLine) {\n      stream.skipToEnd();\n      return \"meta\";\n    }\n    if (ch == '\"' || ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    }\n    if (ch == \"(\" && stream.eat(\"*\")) {\n      state.tokenize = tokenComment;\n      return tokenComment(stream, state);\n    }\n    if (ch == \"{\") {\n      state.tokenize = tokenCommentBraces;\n      return tokenCommentBraces(stream, state);\n    }\n    if (/[\\[\\]\\(\\),;\\:\\.]/.test(ch)) {\n      return null;\n    }\n    if (/\\d/.test(ch)) {\n      stream.eatWhile(/[\\w\\.]/);\n      return \"number\";\n    }\n    if (ch == \"/\") {\n      if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n    }\n    if (isOperatorChar.test(ch)) {\n      stream.eatWhile(isOperatorChar);\n      return \"operator\";\n    }\n    stream.eatWhile(/[\\w\\$_]/);\n    var cur = stream.current();\n    if (keywords.propertyIsEnumerable(cur)) return \"keyword\";\n    if (atoms.propertyIsEnumerable(cur)) return \"atom\";\n    return \"variable\";\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {end = true; break;}\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (end || !escaped) state.tokenize = null;\n      return \"string\";\n    };\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \")\" && maybeEnd) {\n        state.tokenize = null;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n\n  function tokenCommentBraces(stream, state) {\n    var ch;\n    while (ch = stream.next()) {\n      if (ch == \"}\") {\n        state.tokenize = null;\n        break;\n      }\n    }\n    return \"comment\";\n  }\n\n  // Interface\n\n  return {\n    startState: function() {\n      return {tokenize: null};\n    },\n\n    token: function(stream, state) {\n      if (stream.eatSpace()) return null;\n      var style = (state.tokenize || tokenBase)(stream, state);\n      if (style == \"comment\" || style == \"meta\") return style;\n      return style;\n    },\n\n    electricChars: \"{}\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-pascal\", \"pascal\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/pegjs/index.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>CodeMirror: PEG.js Mode</title>\n    <meta charset=\"utf-8\"/>\n    <link rel=stylesheet href=\"../../doc/docs.css\">\n\n    <link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n    <script src=\"../../lib/codemirror.js\"></script>\n    <script src=\"../javascript/javascript.js\"></script>\n    <script src=\"pegjs.js\"></script>\n    <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n  </head>\n  <body>\n    <div id=nav>\n      <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n      <ul>\n        <li><a href=\"../../index.html\">Home</a>\n        <li><a href=\"../../doc/manual.html\">Manual</a>\n        <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n      </ul>\n      <ul>\n        <li><a href=\"../index.html\">Language modes</a>\n        <li><a class=active href=\"#\">PEG.js Mode</a>\n      </ul>\n    </div>\n\n    <article>\n      <h2>PEG.js Mode</h2>\n      <form><textarea id=\"code\" name=\"code\">\n/*\n * Classic example grammar, which recognizes simple arithmetic expressions like\n * \"2*(3+4)\". The parser generated from this grammar then computes their value.\n */\n\nstart\n  = additive\n\nadditive\n  = left:multiplicative \"+\" right:additive { return left + right; }\n  / multiplicative\n\nmultiplicative\n  = left:primary \"*\" right:multiplicative { return left * right; }\n  / primary\n\nprimary\n  = integer\n  / \"(\" additive:additive \")\" { return additive; }\n\ninteger \"integer\"\n  = digits:[0-9]+ { return parseInt(digits.join(\"\"), 10); }\n\nletter = [a-z]+</textarea></form>\n      <script>\n        var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n          mode: {name: \"pegjs\"},\n          lineNumbers: true\n        });\n      </script>\n      <h3>The PEG.js Mode</h3>\n      <p> Created by Forbes Lindesay.</p>\n    </article>\n  </body>\n</html>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/pegjs/pegjs.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../javascript/javascript\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../javascript/javascript\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"pegjs\", function (config) {\n  var jsMode = CodeMirror.getMode(config, \"javascript\");\n\n  function identifier(stream) {\n    return stream.match(/^[a-zA-Z_][a-zA-Z0-9_]*/);\n  }\n\n  return {\n    startState: function () {\n      return {\n        inString: false,\n        stringType: null,\n        inComment: false,\n        inCharacterClass: false,\n        braced: 0,\n        lhs: true,\n        localState: null\n      };\n    },\n    token: function (stream, state) {\n      //check for state changes\n      if (!state.inString && !state.inComment && ((stream.peek() == '\"') || (stream.peek() == \"'\"))) {\n        state.stringType = stream.peek();\n        stream.next(); // Skip quote\n        state.inString = true; // Update state\n      }\n      if (!state.inString && !state.inComment && stream.match('/*')) {\n        state.inComment = true;\n      }\n\n      if (state.inString) {\n        while (state.inString && !stream.eol()) {\n          if (stream.peek() === state.stringType) {\n            stream.next(); // Skip quote\n            state.inString = false; // Clear flag\n          } else if (stream.peek() === '\\\\') {\n            stream.next();\n            stream.next();\n          } else {\n            stream.match(/^.[^\\\\\\\"\\']*/);\n          }\n        }\n        return state.lhs ? \"property string\" : \"string\"; // Token style\n      } else if (state.inComment) {\n        while (state.inComment && !stream.eol()) {\n          if (stream.match('*/')) {\n            state.inComment = false; // Clear flag\n          } else {\n            stream.match(/^.[^\\*]*/);\n          }\n        }\n        return \"comment\";\n      } else if (state.inCharacterClass) {\n          while (state.inCharacterClass && !stream.eol()) {\n            if (!(stream.match(/^[^\\]\\\\]+/) || stream.match(/^\\\\./))) {\n              state.inCharacterClass = false;\n            }\n          }\n      } else if (stream.peek() === '[') {\n        stream.next();\n        state.inCharacterClass = true;\n        return 'bracket';\n      } else if (stream.match('//')) {\n        stream.skipToEnd();\n        return \"comment\";\n      } else if (state.braced || stream.peek() === '{') {\n        if (state.localState === null) {\n          state.localState = CodeMirror.startState(jsMode);\n        }\n        var token = jsMode.token(stream, state.localState);\n        var text = stream.current();\n        if (!token) {\n          for (var i = 0; i < text.length; i++) {\n            if (text[i] === '{') {\n              state.braced++;\n            } else if (text[i] === '}') {\n              state.braced--;\n            }\n          };\n        }\n        return token;\n      } else if (identifier(stream)) {\n        if (stream.peek() === ':') {\n          return 'variable';\n        }\n        return 'variable-2';\n      } else if (['[', ']', '(', ')'].indexOf(stream.peek()) != -1) {\n        stream.next();\n        return 'bracket';\n      } else if (!stream.eatSpace()) {\n        stream.next();\n      }\n      return null;\n    }\n  };\n}, \"javascript\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/perl/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Perl mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"perl.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Perl</a>\n  </ul>\n</div>\n\n<article>\n<h2>Perl mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n#!/usr/bin/perl\n\nuse Something qw(func1 func2);\n\n# strings\nmy $s1 = qq'single line';\nour $s2 = q(multi-\n              line);\n\n=item Something\n\tExample.\n=cut\n\nmy $html=<<'HTML'\n<html>\n<title>hi!</title>\n</html>\nHTML\n\nprint \"first,\".join(',', 'second', qq~third~);\n\nif($s1 =~ m[(?<!\\s)(l.ne)\\z]o) {\n\t$h->{$1}=$$.' predefined variables';\n\t$s2 =~ s/\\-line//ox;\n\t$s1 =~ s[\n\t\t  line ]\n\t\t[\n\t\t  block\n\t\t]ox;\n}\n\n1; # numbers and comments\n\n__END__\nsomething...\n\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-perl</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/perl/perl.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// CodeMirror2 mode/perl/perl.js (text/x-perl) beta 0.10 (2011-11-08)\n// This is a part of CodeMirror from https://github.com/sabaca/CodeMirror_mode_perl (mail@sabaca.com)\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"perl\",function(){\n        // http://perldoc.perl.org\n        var PERL={                                      //   null - magic touch\n                                                        //   1 - keyword\n                                                        //   2 - def\n                                                        //   3 - atom\n                                                        //   4 - operator\n                                                        //   5 - variable-2 (predefined)\n                                                        //   [x,y] - x=1,2,3; y=must be defined if x{...}\n                                                //      PERL operators\n                '->'                            :   4,\n                '++'                            :   4,\n                '--'                            :   4,\n                '**'                            :   4,\n                                                        //   ! ~ \\ and unary + and -\n                '=~'                            :   4,\n                '!~'                            :   4,\n                '*'                             :   4,\n                '/'                             :   4,\n                '%'                             :   4,\n                'x'                             :   4,\n                '+'                             :   4,\n                '-'                             :   4,\n                '.'                             :   4,\n                '<<'                            :   4,\n                '>>'                            :   4,\n                                                        //   named unary operators\n                '<'                             :   4,\n                '>'                             :   4,\n                '<='                            :   4,\n                '>='                            :   4,\n                'lt'                            :   4,\n                'gt'                            :   4,\n                'le'                            :   4,\n                'ge'                            :   4,\n                '=='                            :   4,\n                '!='                            :   4,\n                '<=>'                           :   4,\n                'eq'                            :   4,\n                'ne'                            :   4,\n                'cmp'                           :   4,\n                '~~'                            :   4,\n                '&'                             :   4,\n                '|'                             :   4,\n                '^'                             :   4,\n                '&&'                            :   4,\n                '||'                            :   4,\n                '//'                            :   4,\n                '..'                            :   4,\n                '...'                           :   4,\n                '?'                             :   4,\n                ':'                             :   4,\n                '='                             :   4,\n                '+='                            :   4,\n                '-='                            :   4,\n                '*='                            :   4,  //   etc. ???\n                ','                             :   4,\n                '=>'                            :   4,\n                '::'                            :   4,\n                                                        //   list operators (rightward)\n                'not'                           :   4,\n                'and'                           :   4,\n                'or'                            :   4,\n                'xor'                           :   4,\n                                                //      PERL predefined variables (I know, what this is a paranoid idea, but may be needed for people, who learn PERL, and for me as well, ...and may be for you?;)\n                'BEGIN'                         :   [5,1],\n                'END'                           :   [5,1],\n                'PRINT'                         :   [5,1],\n                'PRINTF'                        :   [5,1],\n                'GETC'                          :   [5,1],\n                'READ'                          :   [5,1],\n                'READLINE'                      :   [5,1],\n                'DESTROY'                       :   [5,1],\n                'TIE'                           :   [5,1],\n                'TIEHANDLE'                     :   [5,1],\n                'UNTIE'                         :   [5,1],\n                'STDIN'                         :    5,\n                'STDIN_TOP'                     :    5,\n                'STDOUT'                        :    5,\n                'STDOUT_TOP'                    :    5,\n                'STDERR'                        :    5,\n                'STDERR_TOP'                    :    5,\n                '$ARG'                          :    5,\n                '$_'                            :    5,\n                '@ARG'                          :    5,\n                '@_'                            :    5,\n                '$LIST_SEPARATOR'               :    5,\n                '$\"'                            :    5,\n                '$PROCESS_ID'                   :    5,\n                '$PID'                          :    5,\n                '$$'                            :    5,\n                '$REAL_GROUP_ID'                :    5,\n                '$GID'                          :    5,\n                '$('                            :    5,\n                '$EFFECTIVE_GROUP_ID'           :    5,\n                '$EGID'                         :    5,\n                '$)'                            :    5,\n                '$PROGRAM_NAME'                 :    5,\n                '$0'                            :    5,\n                '$SUBSCRIPT_SEPARATOR'          :    5,\n                '$SUBSEP'                       :    5,\n                '$;'                            :    5,\n                '$REAL_USER_ID'                 :    5,\n                '$UID'                          :    5,\n                '$<'                            :    5,\n                '$EFFECTIVE_USER_ID'            :    5,\n                '$EUID'                         :    5,\n                '$>'                            :    5,\n                '$a'                            :    5,\n                '$b'                            :    5,\n                '$COMPILING'                    :    5,\n                '$^C'                           :    5,\n                '$DEBUGGING'                    :    5,\n                '$^D'                           :    5,\n                '${^ENCODING}'                  :    5,\n                '$ENV'                          :    5,\n                '%ENV'                          :    5,\n                '$SYSTEM_FD_MAX'                :    5,\n                '$^F'                           :    5,\n                '@F'                            :    5,\n                '${^GLOBAL_PHASE}'              :    5,\n                '$^H'                           :    5,\n                '%^H'                           :    5,\n                '@INC'                          :    5,\n                '%INC'                          :    5,\n                '$INPLACE_EDIT'                 :    5,\n                '$^I'                           :    5,\n                '$^M'                           :    5,\n                '$OSNAME'                       :    5,\n                '$^O'                           :    5,\n                '${^OPEN}'                      :    5,\n                '$PERLDB'                       :    5,\n                '$^P'                           :    5,\n                '$SIG'                          :    5,\n                '%SIG'                          :    5,\n                '$BASETIME'                     :    5,\n                '$^T'                           :    5,\n                '${^TAINT}'                     :    5,\n                '${^UNICODE}'                   :    5,\n                '${^UTF8CACHE}'                 :    5,\n                '${^UTF8LOCALE}'                :    5,\n                '$PERL_VERSION'                 :    5,\n                '$^V'                           :    5,\n                '${^WIN32_SLOPPY_STAT}'         :    5,\n                '$EXECUTABLE_NAME'              :    5,\n                '$^X'                           :    5,\n                '$1'                            :    5, // - regexp $1, $2...\n                '$MATCH'                        :    5,\n                '$&'                            :    5,\n                '${^MATCH}'                     :    5,\n                '$PREMATCH'                     :    5,\n                '$`'                            :    5,\n                '${^PREMATCH}'                  :    5,\n                '$POSTMATCH'                    :    5,\n                \"$'\"                            :    5,\n                '${^POSTMATCH}'                 :    5,\n                '$LAST_PAREN_MATCH'             :    5,\n                '$+'                            :    5,\n                '$LAST_SUBMATCH_RESULT'         :    5,\n                '$^N'                           :    5,\n                '@LAST_MATCH_END'               :    5,\n                '@+'                            :    5,\n                '%LAST_PAREN_MATCH'             :    5,\n                '%+'                            :    5,\n                '@LAST_MATCH_START'             :    5,\n                '@-'                            :    5,\n                '%LAST_MATCH_START'             :    5,\n                '%-'                            :    5,\n                '$LAST_REGEXP_CODE_RESULT'      :    5,\n                '$^R'                           :    5,\n                '${^RE_DEBUG_FLAGS}'            :    5,\n                '${^RE_TRIE_MAXBUF}'            :    5,\n                '$ARGV'                         :    5,\n                '@ARGV'                         :    5,\n                'ARGV'                          :    5,\n                'ARGVOUT'                       :    5,\n                '$OUTPUT_FIELD_SEPARATOR'       :    5,\n                '$OFS'                          :    5,\n                '$,'                            :    5,\n                '$INPUT_LINE_NUMBER'            :    5,\n                '$NR'                           :    5,\n                '$.'                            :    5,\n                '$INPUT_RECORD_SEPARATOR'       :    5,\n                '$RS'                           :    5,\n                '$/'                            :    5,\n                '$OUTPUT_RECORD_SEPARATOR'      :    5,\n                '$ORS'                          :    5,\n                '$\\\\'                           :    5,\n                '$OUTPUT_AUTOFLUSH'             :    5,\n                '$|'                            :    5,\n                '$ACCUMULATOR'                  :    5,\n                '$^A'                           :    5,\n                '$FORMAT_FORMFEED'              :    5,\n                '$^L'                           :    5,\n                '$FORMAT_PAGE_NUMBER'           :    5,\n                '$%'                            :    5,\n                '$FORMAT_LINES_LEFT'            :    5,\n                '$-'                            :    5,\n                '$FORMAT_LINE_BREAK_CHARACTERS' :    5,\n                '$:'                            :    5,\n                '$FORMAT_LINES_PER_PAGE'        :    5,\n                '$='                            :    5,\n                '$FORMAT_TOP_NAME'              :    5,\n                '$^'                            :    5,\n                '$FORMAT_NAME'                  :    5,\n                '$~'                            :    5,\n                '${^CHILD_ERROR_NATIVE}'        :    5,\n                '$EXTENDED_OS_ERROR'            :    5,\n                '$^E'                           :    5,\n                '$EXCEPTIONS_BEING_CAUGHT'      :    5,\n                '$^S'                           :    5,\n                '$WARNING'                      :    5,\n                '$^W'                           :    5,\n                '${^WARNING_BITS}'              :    5,\n                '$OS_ERROR'                     :    5,\n                '$ERRNO'                        :    5,\n                '$!'                            :    5,\n                '%OS_ERROR'                     :    5,\n                '%ERRNO'                        :    5,\n                '%!'                            :    5,\n                '$CHILD_ERROR'                  :    5,\n                '$?'                            :    5,\n                '$EVAL_ERROR'                   :    5,\n                '$@'                            :    5,\n                '$OFMT'                         :    5,\n                '$#'                            :    5,\n                '$*'                            :    5,\n                '$ARRAY_BASE'                   :    5,\n                '$['                            :    5,\n                '$OLD_PERL_VERSION'             :    5,\n                '$]'                            :    5,\n                                                //      PERL blocks\n                'if'                            :[1,1],\n                elsif                           :[1,1],\n                'else'                          :[1,1],\n                'while'                         :[1,1],\n                unless                          :[1,1],\n                'for'                           :[1,1],\n                foreach                         :[1,1],\n                                                //      PERL functions\n                'abs'                           :1,     // - absolute value function\n                accept                          :1,     // - accept an incoming socket connect\n                alarm                           :1,     // - schedule a SIGALRM\n                'atan2'                         :1,     // - arctangent of Y/X in the range -PI to PI\n                bind                            :1,     // - binds an address to a socket\n                binmode                         :1,     // - prepare binary files for I/O\n                bless                           :1,     // - create an object\n                bootstrap                       :1,     //\n                'break'                         :1,     // - break out of a \"given\" block\n                caller                          :1,     // - get context of the current subroutine call\n                chdir                           :1,     // - change your current working directory\n                chmod                           :1,     // - changes the permissions on a list of files\n                chomp                           :1,     // - remove a trailing record separator from a string\n                chop                            :1,     // - remove the last character from a string\n                chown                           :1,     // - change the ownership on a list of files\n                chr                             :1,     // - get character this number represents\n                chroot                          :1,     // - make directory new root for path lookups\n                close                           :1,     // - close file (or pipe or socket) handle\n                closedir                        :1,     // - close directory handle\n                connect                         :1,     // - connect to a remote socket\n                'continue'                      :[1,1], // - optional trailing block in a while or foreach\n                'cos'                           :1,     // - cosine function\n                crypt                           :1,     // - one-way passwd-style encryption\n                dbmclose                        :1,     // - breaks binding on a tied dbm file\n                dbmopen                         :1,     // - create binding on a tied dbm file\n                'default'                       :1,     //\n                defined                         :1,     // - test whether a value, variable, or function is defined\n                'delete'                        :1,     // - deletes a value from a hash\n                die                             :1,     // - raise an exception or bail out\n                'do'                            :1,     // - turn a BLOCK into a TERM\n                dump                            :1,     // - create an immediate core dump\n                each                            :1,     // - retrieve the next key/value pair from a hash\n                endgrent                        :1,     // - be done using group file\n                endhostent                      :1,     // - be done using hosts file\n                endnetent                       :1,     // - be done using networks file\n                endprotoent                     :1,     // - be done using protocols file\n                endpwent                        :1,     // - be done using passwd file\n                endservent                      :1,     // - be done using services file\n                eof                             :1,     // - test a filehandle for its end\n                'eval'                          :1,     // - catch exceptions or compile and run code\n                'exec'                          :1,     // - abandon this program to run another\n                exists                          :1,     // - test whether a hash key is present\n                exit                            :1,     // - terminate this program\n                'exp'                           :1,     // - raise I to a power\n                fcntl                           :1,     // - file control system call\n                fileno                          :1,     // - return file descriptor from filehandle\n                flock                           :1,     // - lock an entire file with an advisory lock\n                fork                            :1,     // - create a new process just like this one\n                format                          :1,     // - declare a picture format with use by the write() function\n                formline                        :1,     // - internal function used for formats\n                getc                            :1,     // - get the next character from the filehandle\n                getgrent                        :1,     // - get next group record\n                getgrgid                        :1,     // - get group record given group user ID\n                getgrnam                        :1,     // - get group record given group name\n                gethostbyaddr                   :1,     // - get host record given its address\n                gethostbyname                   :1,     // - get host record given name\n                gethostent                      :1,     // - get next hosts record\n                getlogin                        :1,     // - return who logged in at this tty\n                getnetbyaddr                    :1,     // - get network record given its address\n                getnetbyname                    :1,     // - get networks record given name\n                getnetent                       :1,     // - get next networks record\n                getpeername                     :1,     // - find the other end of a socket connection\n                getpgrp                         :1,     // - get process group\n                getppid                         :1,     // - get parent process ID\n                getpriority                     :1,     // - get current nice value\n                getprotobyname                  :1,     // - get protocol record given name\n                getprotobynumber                :1,     // - get protocol record numeric protocol\n                getprotoent                     :1,     // - get next protocols record\n                getpwent                        :1,     // - get next passwd record\n                getpwnam                        :1,     // - get passwd record given user login name\n                getpwuid                        :1,     // - get passwd record given user ID\n                getservbyname                   :1,     // - get services record given its name\n                getservbyport                   :1,     // - get services record given numeric port\n                getservent                      :1,     // - get next services record\n                getsockname                     :1,     // - retrieve the sockaddr for a given socket\n                getsockopt                      :1,     // - get socket options on a given socket\n                given                           :1,     //\n                glob                            :1,     // - expand filenames using wildcards\n                gmtime                          :1,     // - convert UNIX time into record or string using Greenwich time\n                'goto'                          :1,     // - create spaghetti code\n                grep                            :1,     // - locate elements in a list test true against a given criterion\n                hex                             :1,     // - convert a string to a hexadecimal number\n                'import'                        :1,     // - patch a module's namespace into your own\n                index                           :1,     // - find a substring within a string\n                'int'                           :1,     // - get the integer portion of a number\n                ioctl                           :1,     // - system-dependent device control system call\n                'join'                          :1,     // - join a list into a string using a separator\n                keys                            :1,     // - retrieve list of indices from a hash\n                kill                            :1,     // - send a signal to a process or process group\n                last                            :1,     // - exit a block prematurely\n                lc                              :1,     // - return lower-case version of a string\n                lcfirst                         :1,     // - return a string with just the next letter in lower case\n                length                          :1,     // - return the number of bytes in a string\n                'link'                          :1,     // - create a hard link in the filesystem\n                listen                          :1,     // - register your socket as a server\n                local                           : 2,    // - create a temporary value for a global variable (dynamic scoping)\n                localtime                       :1,     // - convert UNIX time into record or string using local time\n                lock                            :1,     // - get a thread lock on a variable, subroutine, or method\n                'log'                           :1,     // - retrieve the natural logarithm for a number\n                lstat                           :1,     // - stat a symbolic link\n                m                               :null,  // - match a string with a regular expression pattern\n                map                             :1,     // - apply a change to a list to get back a new list with the changes\n                mkdir                           :1,     // - create a directory\n                msgctl                          :1,     // - SysV IPC message control operations\n                msgget                          :1,     // - get SysV IPC message queue\n                msgrcv                          :1,     // - receive a SysV IPC message from a message queue\n                msgsnd                          :1,     // - send a SysV IPC message to a message queue\n                my                              : 2,    // - declare and assign a local variable (lexical scoping)\n                'new'                           :1,     //\n                next                            :1,     // - iterate a block prematurely\n                no                              :1,     // - unimport some module symbols or semantics at compile time\n                oct                             :1,     // - convert a string to an octal number\n                open                            :1,     // - open a file, pipe, or descriptor\n                opendir                         :1,     // - open a directory\n                ord                             :1,     // - find a character's numeric representation\n                our                             : 2,    // - declare and assign a package variable (lexical scoping)\n                pack                            :1,     // - convert a list into a binary representation\n                'package'                       :1,     // - declare a separate global namespace\n                pipe                            :1,     // - open a pair of connected filehandles\n                pop                             :1,     // - remove the last element from an array and return it\n                pos                             :1,     // - find or set the offset for the last/next m//g search\n                print                           :1,     // - output a list to a filehandle\n                printf                          :1,     // - output a formatted list to a filehandle\n                prototype                       :1,     // - get the prototype (if any) of a subroutine\n                push                            :1,     // - append one or more elements to an array\n                q                               :null,  // - singly quote a string\n                qq                              :null,  // - doubly quote a string\n                qr                              :null,  // - Compile pattern\n                quotemeta                       :null,  // - quote regular expression magic characters\n                qw                              :null,  // - quote a list of words\n                qx                              :null,  // - backquote quote a string\n                rand                            :1,     // - retrieve the next pseudorandom number\n                read                            :1,     // - fixed-length buffered input from a filehandle\n                readdir                         :1,     // - get a directory from a directory handle\n                readline                        :1,     // - fetch a record from a file\n                readlink                        :1,     // - determine where a symbolic link is pointing\n                readpipe                        :1,     // - execute a system command and collect standard output\n                recv                            :1,     // - receive a message over a Socket\n                redo                            :1,     // - start this loop iteration over again\n                ref                             :1,     // - find out the type of thing being referenced\n                rename                          :1,     // - change a filename\n                require                         :1,     // - load in external functions from a library at runtime\n                reset                           :1,     // - clear all variables of a given name\n                'return'                        :1,     // - get out of a function early\n                reverse                         :1,     // - flip a string or a list\n                rewinddir                       :1,     // - reset directory handle\n                rindex                          :1,     // - right-to-left substring search\n                rmdir                           :1,     // - remove a directory\n                s                               :null,  // - replace a pattern with a string\n                say                             :1,     // - print with newline\n                scalar                          :1,     // - force a scalar context\n                seek                            :1,     // - reposition file pointer for random-access I/O\n                seekdir                         :1,     // - reposition directory pointer\n                select                          :1,     // - reset default output or do I/O multiplexing\n                semctl                          :1,     // - SysV semaphore control operations\n                semget                          :1,     // - get set of SysV semaphores\n                semop                           :1,     // - SysV semaphore operations\n                send                            :1,     // - send a message over a socket\n                setgrent                        :1,     // - prepare group file for use\n                sethostent                      :1,     // - prepare hosts file for use\n                setnetent                       :1,     // - prepare networks file for use\n                setpgrp                         :1,     // - set the process group of a process\n                setpriority                     :1,     // - set a process's nice value\n                setprotoent                     :1,     // - prepare protocols file for use\n                setpwent                        :1,     // - prepare passwd file for use\n                setservent                      :1,     // - prepare services file for use\n                setsockopt                      :1,     // - set some socket options\n                shift                           :1,     // - remove the first element of an array, and return it\n                shmctl                          :1,     // - SysV shared memory operations\n                shmget                          :1,     // - get SysV shared memory segment identifier\n                shmread                         :1,     // - read SysV shared memory\n                shmwrite                        :1,     // - write SysV shared memory\n                shutdown                        :1,     // - close down just half of a socket connection\n                'sin'                           :1,     // - return the sine of a number\n                sleep                           :1,     // - block for some number of seconds\n                socket                          :1,     // - create a socket\n                socketpair                      :1,     // - create a pair of sockets\n                'sort'                          :1,     // - sort a list of values\n                splice                          :1,     // - add or remove elements anywhere in an array\n                'split'                         :1,     // - split up a string using a regexp delimiter\n                sprintf                         :1,     // - formatted print into a string\n                'sqrt'                          :1,     // - square root function\n                srand                           :1,     // - seed the random number generator\n                stat                            :1,     // - get a file's status information\n                state                           :1,     // - declare and assign a state variable (persistent lexical scoping)\n                study                           :1,     // - optimize input data for repeated searches\n                'sub'                           :1,     // - declare a subroutine, possibly anonymously\n                'substr'                        :1,     // - get or alter a portion of a string\n                symlink                         :1,     // - create a symbolic link to a file\n                syscall                         :1,     // - execute an arbitrary system call\n                sysopen                         :1,     // - open a file, pipe, or descriptor\n                sysread                         :1,     // - fixed-length unbuffered input from a filehandle\n                sysseek                         :1,     // - position I/O pointer on handle used with sysread and syswrite\n                system                          :1,     // - run a separate program\n                syswrite                        :1,     // - fixed-length unbuffered output to a filehandle\n                tell                            :1,     // - get current seekpointer on a filehandle\n                telldir                         :1,     // - get current seekpointer on a directory handle\n                tie                             :1,     // - bind a variable to an object class\n                tied                            :1,     // - get a reference to the object underlying a tied variable\n                time                            :1,     // - return number of seconds since 1970\n                times                           :1,     // - return elapsed time for self and child processes\n                tr                              :null,  // - transliterate a string\n                truncate                        :1,     // - shorten a file\n                uc                              :1,     // - return upper-case version of a string\n                ucfirst                         :1,     // - return a string with just the next letter in upper case\n                umask                           :1,     // - set file creation mode mask\n                undef                           :1,     // - remove a variable or function definition\n                unlink                          :1,     // - remove one link to a file\n                unpack                          :1,     // - convert binary structure into normal perl variables\n                unshift                         :1,     // - prepend more elements to the beginning of a list\n                untie                           :1,     // - break a tie binding to a variable\n                use                             :1,     // - load in a module at compile time\n                utime                           :1,     // - set a file's last access and modify times\n                values                          :1,     // - return a list of the values in a hash\n                vec                             :1,     // - test or set particular bits in a string\n                wait                            :1,     // - wait for any child process to die\n                waitpid                         :1,     // - wait for a particular child process to die\n                wantarray                       :1,     // - get void vs scalar vs list context of current subroutine call\n                warn                            :1,     // - print debugging info\n                when                            :1,     //\n                write                           :1,     // - print a picture record\n                y                               :null}; // - transliterate a string\n\n        var RXstyle=\"string-2\";\n        var RXmodifiers=/[goseximacplud]/;              // NOTE: \"m\", \"s\", \"y\" and \"tr\" need to correct real modifiers for each regexp type\n\n        function tokenChain(stream,state,chain,style,tail){     // NOTE: chain.length > 2 is not working now (it's for s[...][...]geos;)\n                state.chain=null;                               //                                                          12   3tail\n                state.style=null;\n                state.tail=null;\n                state.tokenize=function(stream,state){\n                        var e=false,c,i=0;\n                        while(c=stream.next()){\n                                if(c===chain[i]&&!e){\n                                        if(chain[++i]!==undefined){\n                                                state.chain=chain[i];\n                                                state.style=style;\n                                                state.tail=tail;}\n                                        else if(tail)\n                                                stream.eatWhile(tail);\n                                        state.tokenize=tokenPerl;\n                                        return style;}\n                                e=!e&&c==\"\\\\\";}\n                        return style;};\n                return state.tokenize(stream,state);}\n\n        function tokenSOMETHING(stream,state,string){\n                state.tokenize=function(stream,state){\n                        if(stream.string==string)\n                                state.tokenize=tokenPerl;\n                        stream.skipToEnd();\n                        return \"string\";};\n                return state.tokenize(stream,state);}\n\n        function tokenPerl(stream,state){\n                if(stream.eatSpace())\n                        return null;\n                if(state.chain)\n                        return tokenChain(stream,state,state.chain,state.style,state.tail);\n                if(stream.match(/^(\\-?((\\d[\\d_]*)?\\.\\d+(e[+-]?\\d+)?|\\d+\\.\\d*)|0x[\\da-fA-F_]+|0b[01_]+|\\d[\\d_]*(e[+-]?\\d+)?)/))\n                        return 'number';\n                if(stream.match(/^<<(?=[_a-zA-Z])/)){                  // NOTE: <<SOMETHING\\n...\\nSOMETHING\\n\n                        stream.eatWhile(/\\w/);\n                        return tokenSOMETHING(stream,state,stream.current().substr(2));}\n                if(stream.sol()&&stream.match(/^\\=item(?!\\w)/)){// NOTE: \\n=item...\\n=cut\\n\n                        return tokenSOMETHING(stream,state,'=cut');}\n                var ch=stream.next();\n                if(ch=='\"'||ch==\"'\"){                           // NOTE: ' or \" or <<'SOMETHING'\\n...\\nSOMETHING\\n or <<\"SOMETHING\"\\n...\\nSOMETHING\\n\n                        if(prefix(stream, 3)==\"<<\"+ch){\n                                var p=stream.pos;\n                                stream.eatWhile(/\\w/);\n                                var n=stream.current().substr(1);\n                                if(n&&stream.eat(ch))\n                                        return tokenSOMETHING(stream,state,n);\n                                stream.pos=p;}\n                        return tokenChain(stream,state,[ch],\"string\");}\n                if(ch==\"q\"){\n                        var c=look(stream, -2);\n                        if(!(c&&/\\w/.test(c))){\n                                c=look(stream, 0);\n                                if(c==\"x\"){\n                                        c=look(stream, 1);\n                                        if(c==\"(\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\")\"],RXstyle,RXmodifiers);}\n                                        if(c==\"[\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\"]\"],RXstyle,RXmodifiers);}\n                                        if(c==\"{\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\"}\"],RXstyle,RXmodifiers);}\n                                        if(c==\"<\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\">\"],RXstyle,RXmodifiers);}\n                                        if(/[\\^'\"!~\\/]/.test(c)){\n                                                eatSuffix(stream, 1);\n                                                return tokenChain(stream,state,[stream.eat(c)],RXstyle,RXmodifiers);}}\n                                else if(c==\"q\"){\n                                        c=look(stream, 1);\n                                        if(c==\"(\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\")\"],\"string\");}\n                                        if(c==\"[\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\"]\"],\"string\");}\n                                        if(c==\"{\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\"}\"],\"string\");}\n                                        if(c==\"<\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\">\"],\"string\");}\n                                        if(/[\\^'\"!~\\/]/.test(c)){\n                                                eatSuffix(stream, 1);\n                                                return tokenChain(stream,state,[stream.eat(c)],\"string\");}}\n                                else if(c==\"w\"){\n                                        c=look(stream, 1);\n                                        if(c==\"(\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\")\"],\"bracket\");}\n                                        if(c==\"[\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\"]\"],\"bracket\");}\n                                        if(c==\"{\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\"}\"],\"bracket\");}\n                                        if(c==\"<\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\">\"],\"bracket\");}\n                                        if(/[\\^'\"!~\\/]/.test(c)){\n                                                eatSuffix(stream, 1);\n                                                return tokenChain(stream,state,[stream.eat(c)],\"bracket\");}}\n                                else if(c==\"r\"){\n                                        c=look(stream, 1);\n                                        if(c==\"(\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\")\"],RXstyle,RXmodifiers);}\n                                        if(c==\"[\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\"]\"],RXstyle,RXmodifiers);}\n                                        if(c==\"{\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\"}\"],RXstyle,RXmodifiers);}\n                                        if(c==\"<\"){\n                                                eatSuffix(stream, 2);\n                                                return tokenChain(stream,state,[\">\"],RXstyle,RXmodifiers);}\n                                        if(/[\\^'\"!~\\/]/.test(c)){\n                                                eatSuffix(stream, 1);\n                                                return tokenChain(stream,state,[stream.eat(c)],RXstyle,RXmodifiers);}}\n                                else if(/[\\^'\"!~\\/(\\[{<]/.test(c)){\n                                        if(c==\"(\"){\n                                                eatSuffix(stream, 1);\n                                                return tokenChain(stream,state,[\")\"],\"string\");}\n                                        if(c==\"[\"){\n                                                eatSuffix(stream, 1);\n                                                return tokenChain(stream,state,[\"]\"],\"string\");}\n                                        if(c==\"{\"){\n                                                eatSuffix(stream, 1);\n                                                return tokenChain(stream,state,[\"}\"],\"string\");}\n                                        if(c==\"<\"){\n                                                eatSuffix(stream, 1);\n                                                return tokenChain(stream,state,[\">\"],\"string\");}\n                                        if(/[\\^'\"!~\\/]/.test(c)){\n                                                return tokenChain(stream,state,[stream.eat(c)],\"string\");}}}}\n                if(ch==\"m\"){\n                        var c=look(stream, -2);\n                        if(!(c&&/\\w/.test(c))){\n                                c=stream.eat(/[(\\[{<\\^'\"!~\\/]/);\n                                if(c){\n                                        if(/[\\^'\"!~\\/]/.test(c)){\n                                                return tokenChain(stream,state,[c],RXstyle,RXmodifiers);}\n                                        if(c==\"(\"){\n                                                return tokenChain(stream,state,[\")\"],RXstyle,RXmodifiers);}\n                                        if(c==\"[\"){\n                                                return tokenChain(stream,state,[\"]\"],RXstyle,RXmodifiers);}\n                                        if(c==\"{\"){\n                                                return tokenChain(stream,state,[\"}\"],RXstyle,RXmodifiers);}\n                                        if(c==\"<\"){\n                                                return tokenChain(stream,state,[\">\"],RXstyle,RXmodifiers);}}}}\n                if(ch==\"s\"){\n                        var c=/[\\/>\\]})\\w]/.test(look(stream, -2));\n                        if(!c){\n                                c=stream.eat(/[(\\[{<\\^'\"!~\\/]/);\n                                if(c){\n                                        if(c==\"[\")\n                                                return tokenChain(stream,state,[\"]\",\"]\"],RXstyle,RXmodifiers);\n                                        if(c==\"{\")\n                                                return tokenChain(stream,state,[\"}\",\"}\"],RXstyle,RXmodifiers);\n                                        if(c==\"<\")\n                                                return tokenChain(stream,state,[\">\",\">\"],RXstyle,RXmodifiers);\n                                        if(c==\"(\")\n                                                return tokenChain(stream,state,[\")\",\")\"],RXstyle,RXmodifiers);\n                                        return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers);}}}\n                if(ch==\"y\"){\n                        var c=/[\\/>\\]})\\w]/.test(look(stream, -2));\n                        if(!c){\n                                c=stream.eat(/[(\\[{<\\^'\"!~\\/]/);\n                                if(c){\n                                        if(c==\"[\")\n                                                return tokenChain(stream,state,[\"]\",\"]\"],RXstyle,RXmodifiers);\n                                        if(c==\"{\")\n                                                return tokenChain(stream,state,[\"}\",\"}\"],RXstyle,RXmodifiers);\n                                        if(c==\"<\")\n                                                return tokenChain(stream,state,[\">\",\">\"],RXstyle,RXmodifiers);\n                                        if(c==\"(\")\n                                                return tokenChain(stream,state,[\")\",\")\"],RXstyle,RXmodifiers);\n                                        return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers);}}}\n                if(ch==\"t\"){\n                        var c=/[\\/>\\]})\\w]/.test(look(stream, -2));\n                        if(!c){\n                                c=stream.eat(\"r\");if(c){\n                                c=stream.eat(/[(\\[{<\\^'\"!~\\/]/);\n                                if(c){\n                                        if(c==\"[\")\n                                                return tokenChain(stream,state,[\"]\",\"]\"],RXstyle,RXmodifiers);\n                                        if(c==\"{\")\n                                                return tokenChain(stream,state,[\"}\",\"}\"],RXstyle,RXmodifiers);\n                                        if(c==\"<\")\n                                                return tokenChain(stream,state,[\">\",\">\"],RXstyle,RXmodifiers);\n                                        if(c==\"(\")\n                                                return tokenChain(stream,state,[\")\",\")\"],RXstyle,RXmodifiers);\n                                        return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers);}}}}\n                if(ch==\"`\"){\n                        return tokenChain(stream,state,[ch],\"variable-2\");}\n                if(ch==\"/\"){\n                        if(!/~\\s*$/.test(prefix(stream)))\n                                return \"operator\";\n                        else\n                                return tokenChain(stream,state,[ch],RXstyle,RXmodifiers);}\n                if(ch==\"$\"){\n                        var p=stream.pos;\n                        if(stream.eatWhile(/\\d/)||stream.eat(\"{\")&&stream.eatWhile(/\\d/)&&stream.eat(\"}\"))\n                                return \"variable-2\";\n                        else\n                                stream.pos=p;}\n                if(/[$@%]/.test(ch)){\n                        var p=stream.pos;\n                        if(stream.eat(\"^\")&&stream.eat(/[A-Z]/)||!/[@$%&]/.test(look(stream, -2))&&stream.eat(/[=|\\\\\\-#?@;:&`~\\^!\\[\\]*'\"$+.,\\/<>()]/)){\n                                var c=stream.current();\n                                if(PERL[c])\n                                        return \"variable-2\";}\n                        stream.pos=p;}\n                if(/[$@%&]/.test(ch)){\n                        if(stream.eatWhile(/[\\w$]/)||stream.eat(\"{\")&&stream.eatWhile(/[\\w$]/)&&stream.eat(\"}\")){\n                                var c=stream.current();\n                                if(PERL[c])\n                                        return \"variable-2\";\n                                else\n                                        return \"variable\";}}\n                if(ch==\"#\"){\n                        if(look(stream, -2)!=\"$\"){\n                                stream.skipToEnd();\n                                return \"comment\";}}\n                if(/[:+\\-\\^*$&%@=<>!?|\\/~\\.]/.test(ch)){\n                        var p=stream.pos;\n                        stream.eatWhile(/[:+\\-\\^*$&%@=<>!?|\\/~\\.]/);\n                        if(PERL[stream.current()])\n                                return \"operator\";\n                        else\n                                stream.pos=p;}\n                if(ch==\"_\"){\n                        if(stream.pos==1){\n                                if(suffix(stream, 6)==\"_END__\"){\n                                        return tokenChain(stream,state,['\\0'],\"comment\");}\n                                else if(suffix(stream, 7)==\"_DATA__\"){\n                                        return tokenChain(stream,state,['\\0'],\"variable-2\");}\n                                else if(suffix(stream, 7)==\"_C__\"){\n                                        return tokenChain(stream,state,['\\0'],\"string\");}}}\n                if(/\\w/.test(ch)){\n                        var p=stream.pos;\n                        if(look(stream, -2)==\"{\"&&(look(stream, 0)==\"}\"||stream.eatWhile(/\\w/)&&look(stream, 0)==\"}\"))\n                                return \"string\";\n                        else\n                                stream.pos=p;}\n                if(/[A-Z]/.test(ch)){\n                        var l=look(stream, -2);\n                        var p=stream.pos;\n                        stream.eatWhile(/[A-Z_]/);\n                        if(/[\\da-z]/.test(look(stream, 0))){\n                                stream.pos=p;}\n                        else{\n                                var c=PERL[stream.current()];\n                                if(!c)\n                                        return \"meta\";\n                                if(c[1])\n                                        c=c[0];\n                                if(l!=\":\"){\n                                        if(c==1)\n                                                return \"keyword\";\n                                        else if(c==2)\n                                                return \"def\";\n                                        else if(c==3)\n                                                return \"atom\";\n                                        else if(c==4)\n                                                return \"operator\";\n                                        else if(c==5)\n                                                return \"variable-2\";\n                                        else\n                                                return \"meta\";}\n                                else\n                                        return \"meta\";}}\n                if(/[a-zA-Z_]/.test(ch)){\n                        var l=look(stream, -2);\n                        stream.eatWhile(/\\w/);\n                        var c=PERL[stream.current()];\n                        if(!c)\n                                return \"meta\";\n                        if(c[1])\n                                c=c[0];\n                        if(l!=\":\"){\n                                if(c==1)\n                                        return \"keyword\";\n                                else if(c==2)\n                                        return \"def\";\n                                else if(c==3)\n                                        return \"atom\";\n                                else if(c==4)\n                                        return \"operator\";\n                                else if(c==5)\n                                        return \"variable-2\";\n                                else\n                                        return \"meta\";}\n                        else\n                                return \"meta\";}\n                return null;}\n\n        return {\n            startState: function() {\n                return {\n                    tokenize: tokenPerl,\n                    chain: null,\n                    style: null,\n                    tail: null\n                };\n            },\n            token: function(stream, state) {\n                return (state.tokenize || tokenPerl)(stream, state);\n            },\n            lineComment: '#'\n        };\n});\n\nCodeMirror.registerHelper(\"wordChars\", \"perl\", /[\\w$]/);\n\nCodeMirror.defineMIME(\"text/x-perl\", \"perl\");\n\n// it's like \"peek\", but need for look-ahead or look-behind if index < 0\nfunction look(stream, c){\n  return stream.string.charAt(stream.pos+(c||0));\n}\n\n// return a part of prefix of current stream from current position\nfunction prefix(stream, c){\n  if(c){\n    var x=stream.pos-c;\n    return stream.string.substr((x>=0?x:0),c);}\n  else{\n    return stream.string.substr(0,stream.pos-1);\n  }\n}\n\n// return a part of suffix of current stream from current position\nfunction suffix(stream, c){\n  var y=stream.string.length;\n  var x=y-stream.pos+1;\n  return stream.string.substr(stream.pos,(c&&c<y?c:x));\n}\n\n// eating and vomiting a part of stream from current position\nfunction eatSuffix(stream, c){\n  var x=stream.pos+c;\n  var y;\n  if(x<=0)\n    stream.pos=0;\n  else if(x>=(y=stream.string.length-1))\n    stream.pos=y;\n  else\n    stream.pos=x;\n}\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/php/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: PHP mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"../htmlmixed/htmlmixed.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../javascript/javascript.js\"></script>\n<script src=\"../css/css.js\"></script>\n<script src=\"../clike/clike.js\"></script>\n<script src=\"php.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">PHP</a>\n  </ul>\n</div>\n\n<article>\n<h2>PHP mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n<?php\n$a = array('a' => 1, 'b' => 2, 3 => 'c');\n\necho \"$a[a] ${a[3] /* } comment */} {$a[b]} \\$a[a]\";\n\nfunction hello($who) {\n\treturn \"Hello $who!\";\n}\n?>\n<p>The program says <?= hello(\"World\") ?>.</p>\n<script>\n\talert(\"And here is some JS code\"); // also colored\n</script>\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"application/x-httpd-php\",\n        indentUnit: 4,\n        indentWithTabs: true\n      });\n    </script>\n\n    <p>Simple HTML/PHP mode based on\n    the <a href=\"../clike/\">C-like</a> mode. Depends on XML,\n    JavaScript, CSS, HTMLMixed, and C-like modes.</p>\n\n    <p><strong>MIME types defined:</strong> <code>application/x-httpd-php</code> (HTML with PHP code), <code>text/x-php</code> (plain, non-wrapped PHP code).</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/php/php.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../htmlmixed/htmlmixed\"), require(\"../clike/clike\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../htmlmixed/htmlmixed\", \"../clike/clike\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  function keywords(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  // Helper for phpString\n  function matchSequence(list, end, escapes) {\n    if (list.length == 0) return phpString(end);\n    return function (stream, state) {\n      var patterns = list[0];\n      for (var i = 0; i < patterns.length; i++) if (stream.match(patterns[i][0])) {\n        state.tokenize = matchSequence(list.slice(1), end);\n        return patterns[i][1];\n      }\n      state.tokenize = phpString(end, escapes);\n      return \"string\";\n    };\n  }\n  function phpString(closing, escapes) {\n    return function(stream, state) { return phpString_(stream, state, closing, escapes); };\n  }\n  function phpString_(stream, state, closing, escapes) {\n    // \"Complex\" syntax\n    if (escapes !== false && stream.match(\"${\", false) || stream.match(\"{$\", false)) {\n      state.tokenize = null;\n      return \"string\";\n    }\n\n    // Simple syntax\n    if (escapes !== false && stream.match(/^\\$[a-zA-Z_][a-zA-Z0-9_]*/)) {\n      // After the variable name there may appear array or object operator.\n      if (stream.match(\"[\", false)) {\n        // Match array operator\n        state.tokenize = matchSequence([\n          [[\"[\", null]],\n          [[/\\d[\\w\\.]*/, \"number\"],\n           [/\\$[a-zA-Z_][a-zA-Z0-9_]*/, \"variable-2\"],\n           [/[\\w\\$]+/, \"variable\"]],\n          [[\"]\", null]]\n        ], closing, escapes);\n      }\n      if (stream.match(/^->\\w/, false)) {\n        // Match object operator\n        state.tokenize = matchSequence([\n          [[\"->\", null]],\n          [[/[\\w]+/, \"variable\"]]\n        ], closing, escapes);\n      }\n      return \"variable-2\";\n    }\n\n    var escaped = false;\n    // Normal string\n    while (!stream.eol() &&\n           (escaped || escapes === false ||\n            (!stream.match(\"{$\", false) &&\n             !stream.match(/^(\\$[a-zA-Z_][a-zA-Z0-9_]*|\\$\\{)/, false)))) {\n      if (!escaped && stream.match(closing)) {\n        state.tokenize = null;\n        state.tokStack.pop(); state.tokStack.pop();\n        break;\n      }\n      escaped = stream.next() == \"\\\\\" && !escaped;\n    }\n    return \"string\";\n  }\n\n  var phpKeywords = \"abstract and array as break case catch class clone const continue declare default \" +\n    \"do else elseif enddeclare endfor endforeach endif endswitch endwhile enum extends final \" +\n    \"for foreach function global goto if implements interface instanceof namespace \" +\n    \"new or private protected public static switch throw trait try use var while xor \" +\n    \"die echo empty exit eval include include_once isset list require require_once return \" +\n    \"print unset __halt_compiler self static parent yield insteadof finally readonly match\";\n  var phpAtoms = \"true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__\";\n  var phpBuiltin = \"func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage memory_get_peak_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists array_intersect_key array_combine array_column pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count\";\n  CodeMirror.registerHelper(\"hintWords\", \"php\", [phpKeywords, phpAtoms, phpBuiltin].join(\" \").split(\" \"));\n  CodeMirror.registerHelper(\"wordChars\", \"php\", /[\\w$]/);\n\n  var phpConfig = {\n    name: \"clike\",\n    helperType: \"php\",\n    keywords: keywords(phpKeywords),\n    blockKeywords: keywords(\"catch do else elseif for foreach if switch try while finally\"),\n    defKeywords: keywords(\"class enum function interface namespace trait\"),\n    atoms: keywords(phpAtoms),\n    builtin: keywords(phpBuiltin),\n    multiLineStrings: true,\n    hooks: {\n      \"$\": function(stream) {\n        stream.eatWhile(/[\\w\\$_]/);\n        return \"variable-2\";\n      },\n      \"<\": function(stream, state) {\n        var before;\n        if (before = stream.match(/^<<\\s*/)) {\n          var quoted = stream.eat(/['\"]/);\n          stream.eatWhile(/[\\w\\.]/);\n          var delim = stream.current().slice(before[0].length + (quoted ? 2 : 1));\n          if (quoted) stream.eat(quoted);\n          if (delim) {\n            (state.tokStack || (state.tokStack = [])).push(delim, 0);\n            state.tokenize = phpString(delim, quoted != \"'\");\n            return \"string\";\n          }\n        }\n        return false;\n      },\n      \"#\": function(stream) {\n        while (!stream.eol() && !stream.match(\"?>\", false)) stream.next();\n        return \"comment\";\n      },\n      \"/\": function(stream) {\n        if (stream.eat(\"/\")) {\n          while (!stream.eol() && !stream.match(\"?>\", false)) stream.next();\n          return \"comment\";\n        }\n        return false;\n      },\n      '\"': function(_stream, state) {\n        (state.tokStack || (state.tokStack = [])).push('\"', 0);\n        state.tokenize = phpString('\"');\n        return \"string\";\n      },\n      \"{\": function(_stream, state) {\n        if (state.tokStack && state.tokStack.length)\n          state.tokStack[state.tokStack.length - 1]++;\n        return false;\n      },\n      \"}\": function(_stream, state) {\n        if (state.tokStack && state.tokStack.length > 0 &&\n            !--state.tokStack[state.tokStack.length - 1]) {\n          state.tokenize = phpString(state.tokStack[state.tokStack.length - 2]);\n        }\n        return false;\n      }\n    }\n  };\n\n  CodeMirror.defineMode(\"php\", function(config, parserConfig) {\n    var htmlMode = CodeMirror.getMode(config, (parserConfig && parserConfig.htmlMode) || \"text/html\");\n    var phpMode = CodeMirror.getMode(config, phpConfig);\n\n    function dispatch(stream, state) {\n      var isPHP = state.curMode == phpMode;\n      if (stream.sol() && state.pending && state.pending != '\"' && state.pending != \"'\") state.pending = null;\n      if (!isPHP) {\n        if (stream.match(/^<\\?\\w*/)) {\n          state.curMode = phpMode;\n          if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, \"\", \"\"))\n          state.curState = state.php;\n          return \"meta\";\n        }\n        if (state.pending == '\"' || state.pending == \"'\") {\n          while (!stream.eol() && stream.next() != state.pending) {}\n          var style = \"string\";\n        } else if (state.pending && stream.pos < state.pending.end) {\n          stream.pos = state.pending.end;\n          var style = state.pending.style;\n        } else {\n          var style = htmlMode.token(stream, state.curState);\n        }\n        if (state.pending) state.pending = null;\n        var cur = stream.current(), openPHP = cur.search(/<\\?/), m;\n        if (openPHP != -1) {\n          if (style == \"string\" && (m = cur.match(/[\\'\\\"]$/)) && !/\\?>/.test(cur)) state.pending = m[0];\n          else state.pending = {end: stream.pos, style: style};\n          stream.backUp(cur.length - openPHP);\n        }\n        return style;\n      } else if (isPHP && state.php.tokenize == null && stream.match(\"?>\")) {\n        state.curMode = htmlMode;\n        state.curState = state.html;\n        if (!state.php.context.prev) state.php = null;\n        return \"meta\";\n      } else {\n        return phpMode.token(stream, state.curState);\n      }\n    }\n\n    return {\n      startState: function() {\n        var html = CodeMirror.startState(htmlMode)\n        var php = parserConfig.startOpen ? CodeMirror.startState(phpMode) : null\n        return {html: html,\n                php: php,\n                curMode: parserConfig.startOpen ? phpMode : htmlMode,\n                curState: parserConfig.startOpen ? php : html,\n                pending: null};\n      },\n\n      copyState: function(state) {\n        var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),\n            php = state.php, phpNew = php && CodeMirror.copyState(phpMode, php), cur;\n        if (state.curMode == htmlMode) cur = htmlNew;\n        else cur = phpNew;\n        return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur,\n                pending: state.pending};\n      },\n\n      token: dispatch,\n\n      indent: function(state, textAfter, line) {\n        if ((state.curMode != phpMode && /^\\s*<\\//.test(textAfter)) ||\n            (state.curMode == phpMode && /^\\?>/.test(textAfter)))\n          return htmlMode.indent(state.html, textAfter, line);\n        return state.curMode.indent(state.curState, textAfter, line);\n      },\n\n      blockCommentStart: \"/*\",\n      blockCommentEnd: \"*/\",\n      lineComment: \"//\",\n\n      innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }\n    };\n  }, \"htmlmixed\", \"clike\");\n\n  CodeMirror.defineMIME(\"application/x-httpd-php\", \"php\");\n  CodeMirror.defineMIME(\"application/x-httpd-php-open\", {name: \"php\", startOpen: true});\n  CodeMirror.defineMIME(\"text/x-php\", phpConfig);\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/php/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"php\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT('simple_test',\n     '[meta <?php] ' +\n     '[keyword echo] [string \"aaa\"]; ' +\n     '[meta ?>]');\n\n  MT('variable_interpolation_non_alphanumeric',\n     '[meta <?php]',\n     '[keyword echo] [string \"aaa$~$!$@$#$$$%$^$&$*$($)$.$<$>$/$\\\\$}$\\\\\\\"$:$;$?$|$[[$]]$+$=aaa\"]',\n     '[meta ?>]');\n\n  MT('variable_interpolation_digits',\n     '[meta <?php]',\n     '[keyword echo] [string \"aaa$1$2$3$4$5$6$7$8$9$0aaa\"]',\n     '[meta ?>]');\n\n  MT('variable_interpolation_simple_syntax_1',\n     '[meta <?php]',\n     '[keyword echo] [string \"aaa][variable-2 $aaa][string .aaa\"];',\n     '[meta ?>]');\n\n  MT('variable_interpolation_simple_syntax_2',\n     '[meta <?php]',\n     '[keyword echo] [string \"][variable-2 $aaaa][[','[number 2]',         ']][string aa\"];',\n     '[keyword echo] [string \"][variable-2 $aaaa][[','[number 2345]',      ']][string aa\"];',\n     '[keyword echo] [string \"][variable-2 $aaaa][[','[number 2.3]',       ']][string aa\"];',\n     '[keyword echo] [string \"][variable-2 $aaaa][[','[variable aaaaa]',   ']][string aa\"];',\n     '[keyword echo] [string \"][variable-2 $aaaa][[','[variable-2 $aaaaa]',']][string aa\"];',\n\n     '[keyword echo] [string \"1aaa][variable-2 $aaaa][[','[number 2]',         ']][string aa\"];',\n     '[keyword echo] [string \"aaa][variable-2 $aaaa][[','[number 2345]',      ']][string aa\"];',\n     '[keyword echo] [string \"aaa][variable-2 $aaaa][[','[number 2.3]',       ']][string aa\"];',\n     '[keyword echo] [string \"aaa][variable-2 $aaaa][[','[variable aaaaa]',   ']][string aa\"];',\n     '[keyword echo] [string \"aaa][variable-2 $aaaa][[','[variable-2 $aaaaa]',']][string aa\"];',\n     '[meta ?>]');\n\n  MT('variable_interpolation_simple_syntax_3',\n     '[meta <?php]',\n     '[keyword echo] [string \"aaa][variable-2 $aaaa]->[variable aaaaa][string .aaaaaa\"];',\n     '[keyword echo] [string \"aaa][variable-2 $aaaa][string ->][variable-2 $aaaaa][string .aaaaaa\"];',\n     '[keyword echo] [string \"aaa][variable-2 $aaaa]->[variable aaaaa][string [[2]].aaaaaa\"];',\n     '[keyword echo] [string \"aaa][variable-2 $aaaa]->[variable aaaaa][string ->aaaa2.aaaaaa\"];',\n     '[meta ?>]');\n\n  MT('variable_interpolation_escaping',\n     '[meta <?php] [comment /* Escaping */]',\n     '[keyword echo] [string \"aaa\\\\$aaaa->aaa.aaa\"];',\n     '[keyword echo] [string \"aaa\\\\$aaaa[[2]]aaa.aaa\"];',\n     '[keyword echo] [string \"aaa\\\\$aaaa[[asd]]aaa.aaa\"];',\n     '[keyword echo] [string \"aaa{\\\\$aaaa->aaa.aaa\"];',\n     '[keyword echo] [string \"aaa{\\\\$aaaa[[2]]aaa.aaa\"];',\n     '[keyword echo] [string \"aaa{\\\\aaaaa[[asd]]aaa.aaa\"];',\n     '[keyword echo] [string \"aaa\\\\${aaaa->aaa.aaa\"];',\n     '[keyword echo] [string \"aaa\\\\${aaaa[[2]]aaa.aaa\"];',\n     '[keyword echo] [string \"aaa\\\\${aaaa[[asd]]aaa.aaa\"];',\n     '[meta ?>]');\n\n  MT('variable_interpolation_complex_syntax_1',\n     '[meta <?php]',\n     '[keyword echo] [string \"aaa][variable-2 $]{[variable aaaa]}[string ->aaa.aaa\"];',\n     '[keyword echo] [string \"aaa][variable-2 $]{[variable-2 $aaaa]}[string ->aaa.aaa\"];',\n     '[keyword echo] [string \"aaa][variable-2 $]{[variable-2 $aaaa][[','  [number 42]',']]}[string ->aaa.aaa\"];',\n     '[keyword echo] [string \"aaa][variable-2 $]{[variable aaaa][meta ?>]aaaaaa');\n\n  MT('variable_interpolation_complex_syntax_2',\n     '[meta <?php] [comment /* Monsters */]',\n     '[keyword echo] [string \"][variable-2 $]{[variable aaa][comment /*}?>} $aaa<?php } */]}[string ->aaa.aaa\"];',\n     '[keyword echo] [string \"][variable-2 $]{[variable aaa][comment /*}?>*/][[','  [string \"aaa][variable-2 $aaa][string {}][variable-2 $]{[variable aaa]}[string \"]',']]}[string ->aaa.aaa\"];',\n     '[keyword echo] [string \"][variable-2 $]{[variable aaa][comment /*} } $aaa } */]}[string ->aaa.aaa\"];');\n\n\n  function build_recursive_monsters(nt, t, n){\n    var monsters = [t];\n    for (var i = 1; i <= n; ++i)\n      monsters[i] = nt.join(monsters[i - 1]);\n    return monsters;\n  }\n\n  var m1 = build_recursive_monsters(\n    ['[string \"][variable-2 $]{[variable aaa] [operator +] ', '}[string \"]'],\n    '[comment /* }?>} */] [string \"aaa][variable-2 $aaa][string .aaa\"]',\n    10\n  );\n\n  MT('variable_interpolation_complex_syntax_3_1',\n     '[meta <?php] [comment /* Recursive monsters */]',\n     '[keyword echo] ' + m1[4] + ';',\n     '[keyword echo] ' + m1[7] + ';',\n     '[keyword echo] ' + m1[8] + ';',\n     '[keyword echo] ' + m1[5] + ';',\n     '[keyword echo] ' + m1[1] + ';',\n     '[keyword echo] ' + m1[6] + ';',\n     '[keyword echo] ' + m1[9] + ';',\n     '[keyword echo] ' + m1[0] + ';',\n     '[keyword echo] ' + m1[10] + ';',\n     '[keyword echo] ' + m1[2] + ';',\n     '[keyword echo] ' + m1[3] + ';',\n     '[keyword echo] [string \"end\"];',\n     '[meta ?>]');\n\n  var m2 = build_recursive_monsters(\n    ['[string \"a][variable-2 $]{[variable aaa] [operator +] ', ' [operator +] ', '}[string .a\"]'],\n    '[comment /* }?>{{ */] [string \"a?>}{{aa][variable-2 $aaa][string .a}a?>a\"]',\n    5\n  );\n\n  MT('variable_interpolation_complex_syntax_3_2',\n     '[meta <?php] [comment /* Recursive monsters 2 */]',\n     '[keyword echo] ' + m2[0] + ';',\n     '[keyword echo] ' + m2[1] + ';',\n     '[keyword echo] ' + m2[5] + ';',\n     '[keyword echo] ' + m2[4] + ';',\n     '[keyword echo] ' + m2[2] + ';',\n     '[keyword echo] ' + m2[3] + ';',\n     '[keyword echo] [string \"end\"];',\n     '[meta ?>]');\n\n  function build_recursive_monsters_2(mf1, mf2, nt, t, n){\n    var monsters = [t];\n    for (var i = 1; i <= n; ++i)\n      monsters[i] = nt[0] + mf1[i - 1] + nt[1] + mf2[i - 1] + nt[2] + monsters[i - 1] + nt[3];\n    return monsters;\n  }\n\n  var m3 = build_recursive_monsters_2(\n    m1,\n    m2,\n    ['[string \"a][variable-2 $]{[variable aaa] [operator +] ', ' [operator +] ', ' [operator +] ', '}[string .a\"]'],\n    '[comment /* }?>{{ */] [string \"a?>}{{aa][variable-2 $aaa][string .a}a?>a\"]',\n    4\n  );\n\n  MT('variable_interpolation_complex_syntax_3_3',\n     '[meta <?php] [comment /* Recursive monsters 2 */]',\n     '[keyword echo] ' + m3[4] + ';',\n     '[keyword echo] ' + m3[0] + ';',\n     '[keyword echo] ' + m3[3] + ';',\n     '[keyword echo] ' + m3[1] + ';',\n     '[keyword echo] ' + m3[2] + ';',\n     '[keyword echo] [string \"end\"];',\n     '[meta ?>]');\n\n  MT(\"variable_interpolation_heredoc\",\n     \"[meta <?php]\",\n     \"[string <<<here]\",\n     \"[string doc ][variable-2 $]{[variable yay]}[string more]\",\n     \"[string here]; [comment // normal]\");\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/pig/index.html",
    "content": "﻿<!doctype html>\n<title>CodeMirror: Pig Latin mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"pig.js\"></script>\n<style>.CodeMirror {border: 2px inset #dee;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Pig Latin</a>\n  </ul>\n</div>\n\n<article>\n<h2>Pig Latin mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n-- Apache Pig (Pig Latin Language) Demo\n/* \nThis is a multiline comment.\n*/\na = LOAD \"\\path\\to\\input\" USING PigStorage('\\t') AS (x:long, y:chararray, z:bytearray);\nb = GROUP a BY (x,y,3+4);\nc = FOREACH b GENERATE flatten(group) as (x,y), SUM(group.$2) as z;\nSTORE c INTO \"\\path\\to\\output\";\n\n--\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        indentUnit: 4,\n        mode: \"text/x-pig\"\n      });\n    </script>\n\n    <p>\n        Simple mode that handles Pig Latin language.\n    </p>\n\n    <p><strong>MIME type defined:</strong> <code>text/x-pig</code>\n    (PIG code)\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/pig/pig.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/*\n *      Pig Latin Mode for CodeMirror 2\n *      @author Prasanth Jayachandran\n *      @link   https://github.com/prasanthj/pig-codemirror-2\n *  This implementation is adapted from PL/SQL mode in CodeMirror 2.\n */\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"pig\", function(_config, parserConfig) {\n  var keywords = parserConfig.keywords,\n  builtins = parserConfig.builtins,\n  types = parserConfig.types,\n  multiLineStrings = parserConfig.multiLineStrings;\n\n  var isOperatorChar = /[*+\\-%<>=&?:\\/!|]/;\n\n  function chain(stream, state, f) {\n    state.tokenize = f;\n    return f(stream, state);\n  }\n\n  function tokenComment(stream, state) {\n    var isEnd = false;\n    var ch;\n    while(ch = stream.next()) {\n      if(ch == \"/\" && isEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      isEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while((next = stream.next()) != null) {\n        if (next == quote && !escaped) {\n          end = true; break;\n        }\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (end || !(escaped || multiLineStrings))\n        state.tokenize = tokenBase;\n      return \"error\";\n    };\n  }\n\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n\n    // is a start of string?\n    if (ch == '\"' || ch == \"'\")\n      return chain(stream, state, tokenString(ch));\n    // is it one of the special chars\n    else if(/[\\[\\]{}\\(\\),;\\.]/.test(ch))\n      return null;\n    // is it a number?\n    else if(/\\d/.test(ch)) {\n      stream.eatWhile(/[\\w\\.]/);\n      return \"number\";\n    }\n    // multi line comment or operator\n    else if (ch == \"/\") {\n      if (stream.eat(\"*\")) {\n        return chain(stream, state, tokenComment);\n      }\n      else {\n        stream.eatWhile(isOperatorChar);\n        return \"operator\";\n      }\n    }\n    // single line comment or operator\n    else if (ch==\"-\") {\n      if(stream.eat(\"-\")){\n        stream.skipToEnd();\n        return \"comment\";\n      }\n      else {\n        stream.eatWhile(isOperatorChar);\n        return \"operator\";\n      }\n    }\n    // is it an operator\n    else if (isOperatorChar.test(ch)) {\n      stream.eatWhile(isOperatorChar);\n      return \"operator\";\n    }\n    else {\n      // get the while word\n      stream.eatWhile(/[\\w\\$_]/);\n      // is it one of the listed keywords?\n      if (keywords && keywords.propertyIsEnumerable(stream.current().toUpperCase())) {\n        //keywords can be used as variables like flatten(group), group.$0 etc..\n        if (!stream.eat(\")\") && !stream.eat(\".\"))\n          return \"keyword\";\n      }\n      // is it one of the builtin functions?\n      if (builtins && builtins.propertyIsEnumerable(stream.current().toUpperCase()))\n        return \"variable-2\";\n      // is it one of the listed types?\n      if (types && types.propertyIsEnumerable(stream.current().toUpperCase()))\n        return \"variable-3\";\n      // default is a 'variable'\n      return \"variable\";\n    }\n  }\n\n  // Interface\n  return {\n    startState: function() {\n      return {\n        tokenize: tokenBase,\n        startOfLine: true\n      };\n    },\n\n    token: function(stream, state) {\n      if(stream.eatSpace()) return null;\n      var style = state.tokenize(stream, state);\n      return style;\n    }\n  };\n});\n\n(function() {\n  function keywords(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  // builtin funcs taken from trunk revision 1303237\n  var pBuiltins = \"ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL \"\n    + \"CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS \"\n    + \"DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG \"\n    + \"FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN \"\n    + \"INTSUM INVOKEFORDOUBLE INVOKEFORFLOAT INVOKEFORINT INVOKEFORLONG INVOKEFORSTRING INVOKER \"\n    + \"ISEMPTY JSONLOADER JSONMETADATA JSONSTORAGE LAST_INDEX_OF LCFIRST LOG LOG10 LOWER LONGABS \"\n    + \"LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA  \"\n    + \"PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE \"\n    + \"SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG \"\n    + \"TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER \";\n\n  // taken from QueryLexer.g\n  var pKeywords = \"VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP \"\n    + \"JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL \"\n    + \"PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE \"\n    + \"SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE \"\n    + \"NEQ MATCHES TRUE FALSE DUMP\";\n\n  // data types\n  var pTypes = \"BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP \";\n\n  CodeMirror.defineMIME(\"text/x-pig\", {\n    name: \"pig\",\n    builtins: keywords(pBuiltins),\n    keywords: keywords(pKeywords),\n    types: keywords(pTypes)\n  });\n\n  CodeMirror.registerHelper(\"hintWords\", \"pig\", (pBuiltins + pTypes + pKeywords).split(\" \"));\n}());\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/powershell/index.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>CodeMirror: Powershell mode</title>\n    <link rel=\"stylesheet\" href=\"../../doc/docs.css\">\n    <link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n    <script src=\"../../lib/codemirror.js\"></script>\n    <script src=\"../../addon/edit/matchbrackets.js\"></script>\n    <script src=\"powershell.js\"></script>\n    <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n  </head>\n  <body>\n    <div id=nav>\n      <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n      <ul>\n        <li><a href=\"../../index.html\">Home</a>\n        <li><a href=\"../../doc/manual.html\">Manual</a>\n        <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n      </ul>\n      <ul>\n        <li><a href=\"../index.html\">Language modes</a>\n        <li><a class=active href=\"#\">JavaScript</a>\n      </ul>\n    </div>\n    <article>\n      <h2>PowerShell mode</h2>\n\n      <div><textarea id=\"code\" name=\"code\">\n# Paths\ncd c:\\\nc:\\windows\\calc.exe\n\n# Number Literals\n0 12345\n12kb 12mb 12gB 12Tb 12PB 12L 12D 12lkb 12dtb\n1.234 1.234e56 1. 1.e2 .2 .2e34\n1.2MB 1.kb .1dTb 1.e1gb\n0x1 0xabcdef 0x3tb 0xelmb\n\n# String Literals\n'Literal escaping'''\n'Literal $variable'\n\"Escaping 1`\"\"\n\"Escaping 2\"\"\"\n\"Escaped `$variable\"\n\"Text, $variable and more text\"\n\"Text, ${variable with spaces} and more text.\"\n\"Text, $($expression + 3) and more text.\"\n\"Text, $(\"interpolation $(\"inception\")\") and more text.\"\n\n@\"\nMultiline\nstring\n\"@\n# --\n@\"\nMultiline\nstring with quotes \"'\n\"@\n# --\n@'\nMultiline literal\nstring with quotes \"'\n'@\n\n# Array and Hash literals\n@( 'a','b','c' )\n@{ 'key': 'value' }\n\n# Variables\n$Variable = 5\n$global:variable = 5\n${Variable with spaces} = 5\n\n# Operators\n= += -= *= /= %=\n++ -- .. -f * / % + -\n-not ! -bnot\n-split -isplit -csplit\n-join\n-is -isnot -as\n-eq -ieq -ceq -ne -ine -cne\n-gt -igt -cgt -ge -ige -cge\n-lt -ilt -clt -le -ile -cle\n-like -ilike -clike -notlike -inotlike -cnotlike\n-match -imatch -cmatch -notmatch -inotmatch -cnotmatch\n-contains -icontains -ccontains -notcontains -inotcontains -cnotcontains\n-replace -ireplace -creplace\n-band\t-bor -bxor\n-and -or -xor\n\n# Punctuation\n() [] {} , : ` = ; .\n\n# Keywords\nelseif begin function for foreach return else trap while do data dynamicparam\nuntil end break if throw param continue finally in switch exit filter from try\nprocess catch\n\n# Built-in variables\n$$ $? $^ $_\n$args $ConfirmPreference $ConsoleFileName $DebugPreference $Error\n$ErrorActionPreference $ErrorView $ExecutionContext $false $FormatEnumerationLimit\n$HOME $Host $input $MaximumAliasCount $MaximumDriveCount $MaximumErrorCount\n$MaximumFunctionCount $MaximumHistoryCount $MaximumVariableCount $MyInvocation\n$NestedPromptLevel $null $OutputEncoding $PID $PROFILE $ProgressPreference\n$PSBoundParameters $PSCommandPath $PSCulture $PSDefaultParameterValues\n$PSEmailServer $PSHOME $PSScriptRoot $PSSessionApplicationName\n$PSSessionConfigurationName $PSSessionOption $PSUICulture $PSVersionTable $PWD\n$ShellId $StackTrace $true $VerbosePreference $WarningPreference $WhatIfPreference\n$true $false $null\n\n# Built-in functions\nA:\nAdd-Computer Add-Content Add-History Add-Member Add-PSSnapin Add-Type\nB:\nC:\nCheckpoint-Computer Clear-Content Clear-EventLog Clear-History Clear-Host Clear-Item\nClear-ItemProperty Clear-Variable Compare-Object Complete-Transaction Connect-PSSession\nConvertFrom-Csv ConvertFrom-Json ConvertFrom-SecureString ConvertFrom-StringData\nConvert-Path ConvertTo-Csv ConvertTo-Html ConvertTo-Json ConvertTo-SecureString\nConvertTo-Xml Copy-Item Copy-ItemProperty\nD:\nDebug-Process Disable-ComputerRestore Disable-PSBreakpoint Disable-PSRemoting\nDisable-PSSessionConfiguration Disconnect-PSSession\nE:\nEnable-ComputerRestore Enable-PSBreakpoint Enable-PSRemoting Enable-PSSessionConfiguration\nEnter-PSSession Exit-PSSession Export-Alias Export-Clixml Export-Console Export-Counter\nExport-Csv Export-FormatData Export-ModuleMember Export-PSSession\nF:\nForEach-Object Format-Custom Format-List Format-Table Format-Wide\nG:\nGet-Acl Get-Alias Get-AuthenticodeSignature Get-ChildItem Get-Command Get-ComputerRestorePoint\nGet-Content Get-ControlPanelItem Get-Counter Get-Credential Get-Culture Get-Date\nGet-Event Get-EventLog Get-EventSubscriber Get-ExecutionPolicy Get-FormatData Get-Help\nGet-History Get-Host Get-HotFix Get-Item Get-ItemProperty Get-Job Get-Location Get-Member\nGet-Module Get-PfxCertificate Get-Process Get-PSBreakpoint Get-PSCallStack Get-PSDrive\nGet-PSProvider Get-PSSession Get-PSSessionConfiguration Get-PSSnapin Get-Random Get-Service\nGet-TraceSource Get-Transaction Get-TypeData Get-UICulture  Get-Unique Get-Variable Get-Verb\nGet-WinEvent Get-WmiObject Group-Object\nH:\nhelp\nI:\nImport-Alias Import-Clixml Import-Counter Import-Csv Import-LocalizedData Import-Module\nImport-PSSession ImportSystemModules Invoke-Command Invoke-Expression Invoke-History\nInvoke-Item Invoke-RestMethod Invoke-WebRequest Invoke-WmiMethod\nJ:\nJoin-Path\nK:\nL:\nLimit-EventLog\nM:\nMeasure-Command Measure-Object mkdir more Move-Item Move-ItemProperty\nN:\nNew-Alias New-Event New-EventLog New-Item New-ItemProperty New-Module New-ModuleManifest\nNew-Object New-PSDrive New-PSSession New-PSSessionConfigurationFile New-PSSessionOption\nNew-PSTransportOption New-Service New-TimeSpan New-Variable New-WebServiceProxy\nNew-WinEvent\nO:\noss Out-Default Out-File Out-GridView Out-Host Out-Null Out-Printer Out-String\nP:\nPause Pop-Location prompt Push-Location\nQ:\nR:\nRead-Host Receive-Job Receive-PSSession Register-EngineEvent Register-ObjectEvent\nRegister-PSSessionConfiguration Register-WmiEvent Remove-Computer Remove-Event\nRemove-EventLog Remove-Item Remove-ItemProperty Remove-Job Remove-Module\nRemove-PSBreakpoint Remove-PSDrive Remove-PSSession Remove-PSSnapin Remove-TypeData\nRemove-Variable Remove-WmiObject Rename-Computer Rename-Item Rename-ItemProperty\nReset-ComputerMachinePassword Resolve-Path Restart-Computer Restart-Service\nRestore-Computer Resume-Job Resume-Service\nS:\nSave-Help Select-Object Select-String Select-Xml Send-MailMessage Set-Acl Set-Alias\nSet-AuthenticodeSignature Set-Content Set-Date Set-ExecutionPolicy Set-Item\nSet-ItemProperty Set-Location Set-PSBreakpoint Set-PSDebug\nSet-PSSessionConfiguration Set-Service Set-StrictMode Set-TraceSource Set-Variable\nSet-WmiInstance Show-Command Show-ControlPanelItem Show-EventLog Sort-Object\nSplit-Path Start-Job Start-Process Start-Service Start-Sleep Start-Transaction\nStart-Transcript Stop-Computer Stop-Job Stop-Process Stop-Service Stop-Transcript\nSuspend-Job Suspend-Service\nT:\nTabExpansion2 Tee-Object Test-ComputerSecureChannel Test-Connection\nTest-ModuleManifest Test-Path Test-PSSessionConfigurationFile Trace-Command\nU:\nUnblock-File Undo-Transaction Unregister-Event Unregister-PSSessionConfiguration\nUpdate-FormatData Update-Help Update-List Update-TypeData Use-Transaction\nV:\nW:\nWait-Event Wait-Job Wait-Process Where-Object Write-Debug Write-Error Write-EventLog\nWrite-Host Write-Output Write-Progress Write-Verbose Write-Warning\nX:\nY:\nZ:</textarea></div>\n      <script>\n        var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n          mode: \"powershell\",\n          lineNumbers: true,\n          indentUnit: 4,\n          tabMode: \"shift\",\n          matchBrackets: true\n        });\n      </script>\n\n      <p><strong>MIME types defined:</strong> <code>application/x-powershell</code>.</p>\n    </article>\n  </body>\n</html>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/powershell/powershell.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  'use strict';\n  if (typeof exports == 'object' && typeof module == 'object') // CommonJS\n    mod(require('../../lib/codemirror'));\n  else if (typeof define == 'function' && define.amd) // AMD\n    define(['../../lib/codemirror'], mod);\n  else // Plain browser env\n    mod(window.CodeMirror);\n})(function(CodeMirror) {\n'use strict';\n\nCodeMirror.defineMode('powershell', function() {\n  function buildRegexp(patterns, options) {\n    options = options || {};\n    var prefix = options.prefix !== undefined ? options.prefix : '^';\n    var suffix = options.suffix !== undefined ? options.suffix : '\\\\b';\n\n    for (var i = 0; i < patterns.length; i++) {\n      if (patterns[i] instanceof RegExp) {\n        patterns[i] = patterns[i].source;\n      }\n      else {\n        patterns[i] = patterns[i].replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n      }\n    }\n\n    return new RegExp(prefix + '(' + patterns.join('|') + ')' + suffix, 'i');\n  }\n\n  var notCharacterOrDash = '(?=[^A-Za-z\\\\d\\\\-_]|$)';\n  var varNames = /[\\w\\-:]/\n  var keywords = buildRegexp([\n    /begin|break|catch|continue|data|default|do|dynamicparam/,\n    /else|elseif|end|exit|filter|finally|for|foreach|from|function|if|in/,\n    /param|process|return|switch|throw|trap|try|until|where|while/\n  ], { suffix: notCharacterOrDash });\n\n  var punctuation = /[\\[\\]{},;`\\\\\\.]|@[({]/;\n  var wordOperators = buildRegexp([\n    'f',\n    /b?not/,\n    /[ic]?split/, 'join',\n    /is(not)?/, 'as',\n    /[ic]?(eq|ne|[gl][te])/,\n    /[ic]?(not)?(like|match|contains)/,\n    /[ic]?replace/,\n    /b?(and|or|xor)/\n  ], { prefix: '-' });\n  var symbolOperators = /[+\\-*\\/%]=|\\+\\+|--|\\.\\.|[+\\-*&^%:=!|\\/]|<(?!#)|(?!#)>/;\n  var operators = buildRegexp([wordOperators, symbolOperators], { suffix: '' });\n\n  var numbers = /^((0x[\\da-f]+)|((\\d+\\.\\d+|\\d\\.|\\.\\d+|\\d+)(e[\\+\\-]?\\d+)?))[ld]?([kmgtp]b)?/i;\n\n  var identifiers = /^[A-Za-z\\_][A-Za-z\\-\\_\\d]*\\b/;\n\n  var symbolBuiltins = /[A-Z]:|%|\\?/i;\n  var namedBuiltins = buildRegexp([\n    /Add-(Computer|Content|History|Member|PSSnapin|Type)/,\n    /Checkpoint-Computer/,\n    /Clear-(Content|EventLog|History|Host|Item(Property)?|Variable)/,\n    /Compare-Object/,\n    /Complete-Transaction/,\n    /Connect-PSSession/,\n    /ConvertFrom-(Csv|Json|SecureString|StringData)/,\n    /Convert-Path/,\n    /ConvertTo-(Csv|Html|Json|SecureString|Xml)/,\n    /Copy-Item(Property)?/,\n    /Debug-Process/,\n    /Disable-(ComputerRestore|PSBreakpoint|PSRemoting|PSSessionConfiguration)/,\n    /Disconnect-PSSession/,\n    /Enable-(ComputerRestore|PSBreakpoint|PSRemoting|PSSessionConfiguration)/,\n    /(Enter|Exit)-PSSession/,\n    /Export-(Alias|Clixml|Console|Counter|Csv|FormatData|ModuleMember|PSSession)/,\n    /ForEach-Object/,\n    /Format-(Custom|List|Table|Wide)/,\n    new RegExp('Get-(Acl|Alias|AuthenticodeSignature|ChildItem|Command|ComputerRestorePoint|Content|ControlPanelItem|Counter|Credential'\n      + '|Culture|Date|Event|EventLog|EventSubscriber|ExecutionPolicy|FormatData|Help|History|Host|HotFix|Item|ItemProperty|Job'\n      + '|Location|Member|Module|PfxCertificate|Process|PSBreakpoint|PSCallStack|PSDrive|PSProvider|PSSession|PSSessionConfiguration'\n      + '|PSSnapin|Random|Service|TraceSource|Transaction|TypeData|UICulture|Unique|Variable|Verb|WinEvent|WmiObject)'),\n    /Group-Object/,\n    /Import-(Alias|Clixml|Counter|Csv|LocalizedData|Module|PSSession)/,\n    /ImportSystemModules/,\n    /Invoke-(Command|Expression|History|Item|RestMethod|WebRequest|WmiMethod)/,\n    /Join-Path/,\n    /Limit-EventLog/,\n    /Measure-(Command|Object)/,\n    /Move-Item(Property)?/,\n    new RegExp('New-(Alias|Event|EventLog|Item(Property)?|Module|ModuleManifest|Object|PSDrive|PSSession|PSSessionConfigurationFile'\n      + '|PSSessionOption|PSTransportOption|Service|TimeSpan|Variable|WebServiceProxy|WinEvent)'),\n    /Out-(Default|File|GridView|Host|Null|Printer|String)/,\n    /Pause/,\n    /(Pop|Push)-Location/,\n    /Read-Host/,\n    /Receive-(Job|PSSession)/,\n    /Register-(EngineEvent|ObjectEvent|PSSessionConfiguration|WmiEvent)/,\n    /Remove-(Computer|Event|EventLog|Item(Property)?|Job|Module|PSBreakpoint|PSDrive|PSSession|PSSnapin|TypeData|Variable|WmiObject)/,\n    /Rename-(Computer|Item(Property)?)/,\n    /Reset-ComputerMachinePassword/,\n    /Resolve-Path/,\n    /Restart-(Computer|Service)/,\n    /Restore-Computer/,\n    /Resume-(Job|Service)/,\n    /Save-Help/,\n    /Select-(Object|String|Xml)/,\n    /Send-MailMessage/,\n    new RegExp('Set-(Acl|Alias|AuthenticodeSignature|Content|Date|ExecutionPolicy|Item(Property)?|Location|PSBreakpoint|PSDebug' +\n               '|PSSessionConfiguration|Service|StrictMode|TraceSource|Variable|WmiInstance)'),\n    /Show-(Command|ControlPanelItem|EventLog)/,\n    /Sort-Object/,\n    /Split-Path/,\n    /Start-(Job|Process|Service|Sleep|Transaction|Transcript)/,\n    /Stop-(Computer|Job|Process|Service|Transcript)/,\n    /Suspend-(Job|Service)/,\n    /TabExpansion2/,\n    /Tee-Object/,\n    /Test-(ComputerSecureChannel|Connection|ModuleManifest|Path|PSSessionConfigurationFile)/,\n    /Trace-Command/,\n    /Unblock-File/,\n    /Undo-Transaction/,\n    /Unregister-(Event|PSSessionConfiguration)/,\n    /Update-(FormatData|Help|List|TypeData)/,\n    /Use-Transaction/,\n    /Wait-(Event|Job|Process)/,\n    /Where-Object/,\n    /Write-(Debug|Error|EventLog|Host|Output|Progress|Verbose|Warning)/,\n    /cd|help|mkdir|more|oss|prompt/,\n    /ac|asnp|cat|cd|chdir|clc|clear|clhy|cli|clp|cls|clv|cnsn|compare|copy|cp|cpi|cpp|cvpa|dbp|del|diff|dir|dnsn|ebp/,\n    /echo|epal|epcsv|epsn|erase|etsn|exsn|fc|fl|foreach|ft|fw|gal|gbp|gc|gci|gcm|gcs|gdr|ghy|gi|gjb|gl|gm|gmo|gp|gps/,\n    /group|gsn|gsnp|gsv|gu|gv|gwmi|h|history|icm|iex|ihy|ii|ipal|ipcsv|ipmo|ipsn|irm|ise|iwmi|iwr|kill|lp|ls|man|md/,\n    /measure|mi|mount|move|mp|mv|nal|ndr|ni|nmo|npssc|nsn|nv|ogv|oh|popd|ps|pushd|pwd|r|rbp|rcjb|rcsn|rd|rdr|ren|ri/,\n    /rjb|rm|rmdir|rmo|rni|rnp|rp|rsn|rsnp|rujb|rv|rvpa|rwmi|sajb|sal|saps|sasv|sbp|sc|select|set|shcm|si|sl|sleep|sls/,\n    /sort|sp|spjb|spps|spsv|start|sujb|sv|swmi|tee|trcm|type|where|wjb|write/\n  ], { prefix: '', suffix: '' });\n  var variableBuiltins = buildRegexp([\n    /[$?^_]|Args|ConfirmPreference|ConsoleFileName|DebugPreference|Error|ErrorActionPreference|ErrorView|ExecutionContext/,\n    /FormatEnumerationLimit|Home|Host|Input|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount/,\n    /MaximumHistoryCount|MaximumVariableCount|MyInvocation|NestedPromptLevel|OutputEncoding|Pid|Profile|ProgressPreference/,\n    /PSBoundParameters|PSCommandPath|PSCulture|PSDefaultParameterValues|PSEmailServer|PSHome|PSScriptRoot|PSSessionApplicationName/,\n    /PSSessionConfigurationName|PSSessionOption|PSUICulture|PSVersionTable|Pwd|ShellId|StackTrace|VerbosePreference/,\n    /WarningPreference|WhatIfPreference/,\n\n    /Event|EventArgs|EventSubscriber|Sender/,\n    /Matches|Ofs|ForEach|LastExitCode|PSCmdlet|PSItem|PSSenderInfo|This/,\n    /true|false|null/\n  ], { prefix: '\\\\$', suffix: '' });\n\n  var builtins = buildRegexp([symbolBuiltins, namedBuiltins, variableBuiltins], { suffix: notCharacterOrDash });\n\n  var grammar = {\n    keyword: keywords,\n    number: numbers,\n    operator: operators,\n    builtin: builtins,\n    punctuation: punctuation,\n    identifier: identifiers\n  };\n\n  // tokenizers\n  function tokenBase(stream, state) {\n    // Handle Comments\n    //var ch = stream.peek();\n\n    var parent = state.returnStack[state.returnStack.length - 1];\n    if (parent && parent.shouldReturnFrom(state)) {\n      state.tokenize = parent.tokenize;\n      state.returnStack.pop();\n      return state.tokenize(stream, state);\n    }\n\n    if (stream.eatSpace()) {\n      return null;\n    }\n\n    if (stream.eat('(')) {\n      state.bracketNesting += 1;\n      return 'punctuation';\n    }\n\n    if (stream.eat(')')) {\n      state.bracketNesting -= 1;\n      return 'punctuation';\n    }\n\n    for (var key in grammar) {\n      if (stream.match(grammar[key])) {\n        return key;\n      }\n    }\n\n    var ch = stream.next();\n\n    // single-quote string\n    if (ch === \"'\") {\n      return tokenSingleQuoteString(stream, state);\n    }\n\n    if (ch === '$') {\n      return tokenVariable(stream, state);\n    }\n\n    // double-quote string\n    if (ch === '\"') {\n      return tokenDoubleQuoteString(stream, state);\n    }\n\n    if (ch === '<' && stream.eat('#')) {\n      state.tokenize = tokenComment;\n      return tokenComment(stream, state);\n    }\n\n    if (ch === '#') {\n      stream.skipToEnd();\n      return 'comment';\n    }\n\n    if (ch === '@') {\n      var quoteMatch = stream.eat(/[\"']/);\n      if (quoteMatch && stream.eol()) {\n        state.tokenize = tokenMultiString;\n        state.startQuote = quoteMatch[0];\n        return tokenMultiString(stream, state);\n      } else if (stream.eol()) {\n        return 'error';\n      } else if (stream.peek().match(/[({]/)) {\n        return 'punctuation';\n      } else if (stream.peek().match(varNames)) {\n        // splatted variable\n        return tokenVariable(stream, state);\n      }\n    }\n    return 'error';\n  }\n\n  function tokenSingleQuoteString(stream, state) {\n    var ch;\n    while ((ch = stream.peek()) != null) {\n      stream.next();\n\n      if (ch === \"'\" && !stream.eat(\"'\")) {\n        state.tokenize = tokenBase;\n        return 'string';\n      }\n    }\n\n    return 'error';\n  }\n\n  function tokenDoubleQuoteString(stream, state) {\n    var ch;\n    while ((ch = stream.peek()) != null) {\n      if (ch === '$') {\n        state.tokenize = tokenStringInterpolation;\n        return 'string';\n      }\n\n      stream.next();\n      if (ch === '`') {\n        stream.next();\n        continue;\n      }\n\n      if (ch === '\"' && !stream.eat('\"')) {\n        state.tokenize = tokenBase;\n        return 'string';\n      }\n    }\n\n    return 'error';\n  }\n\n  function tokenStringInterpolation(stream, state) {\n    return tokenInterpolation(stream, state, tokenDoubleQuoteString);\n  }\n\n  function tokenMultiStringReturn(stream, state) {\n    state.tokenize = tokenMultiString;\n    state.startQuote = '\"'\n    return tokenMultiString(stream, state);\n  }\n\n  function tokenHereStringInterpolation(stream, state) {\n    return tokenInterpolation(stream, state, tokenMultiStringReturn);\n  }\n\n  function tokenInterpolation(stream, state, parentTokenize) {\n    if (stream.match('$(')) {\n      var savedBracketNesting = state.bracketNesting;\n      state.returnStack.push({\n        /*jshint loopfunc:true */\n        shouldReturnFrom: function(state) {\n          return state.bracketNesting === savedBracketNesting;\n        },\n        tokenize: parentTokenize\n      });\n      state.tokenize = tokenBase;\n      state.bracketNesting += 1;\n      return 'punctuation';\n    } else {\n      stream.next();\n      state.returnStack.push({\n        shouldReturnFrom: function() { return true; },\n        tokenize: parentTokenize\n      });\n      state.tokenize = tokenVariable;\n      return state.tokenize(stream, state);\n    }\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while ((ch = stream.next()) != null) {\n      if (maybeEnd && ch == '>') {\n          state.tokenize = tokenBase;\n          break;\n      }\n      maybeEnd = (ch === '#');\n    }\n    return 'comment';\n  }\n\n  function tokenVariable(stream, state) {\n    var ch = stream.peek();\n    if (stream.eat('{')) {\n      state.tokenize = tokenVariableWithBraces;\n      return tokenVariableWithBraces(stream, state);\n    } else if (ch != undefined && ch.match(varNames)) {\n      stream.eatWhile(varNames);\n      state.tokenize = tokenBase;\n      return 'variable-2';\n    } else {\n      state.tokenize = tokenBase;\n      return 'error';\n    }\n  }\n\n  function tokenVariableWithBraces(stream, state) {\n    var ch;\n    while ((ch = stream.next()) != null) {\n      if (ch === '}') {\n        state.tokenize = tokenBase;\n        break;\n      }\n    }\n    return 'variable-2';\n  }\n\n  function tokenMultiString(stream, state) {\n    var quote = state.startQuote;\n    if (stream.sol() && stream.match(new RegExp(quote + '@'))) {\n      state.tokenize = tokenBase;\n    }\n    else if (quote === '\"') {\n      while (!stream.eol()) {\n        var ch = stream.peek();\n        if (ch === '$') {\n          state.tokenize = tokenHereStringInterpolation;\n          return 'string';\n        }\n\n        stream.next();\n        if (ch === '`') {\n          stream.next();\n        }\n      }\n    }\n    else {\n      stream.skipToEnd();\n    }\n\n    return 'string';\n  }\n\n  var external = {\n    startState: function() {\n      return {\n        returnStack: [],\n        bracketNesting: 0,\n        tokenize: tokenBase\n      };\n    },\n\n    token: function(stream, state) {\n      return state.tokenize(stream, state);\n    },\n\n    blockCommentStart: '<#',\n    blockCommentEnd: '#>',\n    lineComment: '#',\n    fold: 'brace'\n  };\n  return external;\n});\n\nCodeMirror.defineMIME('application/x-powershell', 'powershell');\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/powershell/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"powershell\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  function forEach(arr, f) { for (var i = 0; i < arr.length; i++) f(arr[i], i) }\n\n  MT('comment', '[number 1][comment # A]');\n  MT('comment_multiline', '[number 1][comment <#]',\n    '[comment ABC]',\n  '[comment #>][number 2]');\n\n  forEach([\n    '0', '1234',\n    '12kb', '12mb', '12Gb', '12Tb', '12PB', '12L', '12D', '12lkb', '12dtb',\n    '1.234', '1.234e56', '1.', '1.e2', '.2', '.2e34',\n    '1.2MB', '1.kb', '.1dTB', '1.e1gb', '.2', '.2e34',\n    '0x1', '0xabcdef', '0x3tb', '0xelmb'\n  ], function(number) {\n    MT(\"number_\" + number, \"[number \" + number + \"]\");\n  });\n\n  MT('string_literal_escaping', \"[string 'a''']\");\n  MT('string_literal_variable', \"[string 'a $x']\");\n  MT('string_escaping_1', '[string \"a `\"\"]');\n  MT('string_escaping_2', '[string \"a \"\"\"]');\n  MT('string_variable_escaping', '[string \"a `$x\"]');\n  MT('string_variable', '[string \"a ][variable-2 $x][string  b\"]');\n  MT('string_variable_spaces', '[string \"a ][variable-2 ${x y}][string  b\"]');\n  MT('string_expression', '[string \"a ][punctuation $(][variable-2 $x][operator +][number 3][punctuation )][string  b\"]');\n  MT('string_expression_nested', '[string \"A][punctuation $(][string \"a][punctuation $(][string \"w\"][punctuation )][string b\"][punctuation )][string B\"]');\n\n  MT('string_heredoc', '[string @\"]',\n    '[string abc]',\n  '[string \"@]');\n  MT('string_heredoc_quotes', '[string @\"]',\n    '[string abc \"\\']',\n  '[string \"@]');\n  MT('string_heredoc_variable', '[string @\"]',\n    '[string a ][variable-2 $x][string  b]',\n  '[string \"@]');\n  MT('string_heredoc_nested_string', '[string @\"]',\n    '[string a][punctuation $(][string \"w\"][punctuation )][string b]',\n  '[string \"@]');\n  MT('string_heredoc_literal_quotes', \"[string @']\",\n    '[string abc \"\\']',\n  \"[string '@]\");\n\n  MT('array', \"[punctuation @(][string 'a'][punctuation ,][string 'b'][punctuation )]\");\n  MT('hash', \"[punctuation @{][string 'key'][operator :][string 'value'][punctuation }]\");\n\n  MT('variable', \"[variable-2 $test]\");\n  MT('variable_global',  \"[variable-2 $global:test]\");\n  MT('variable_spaces',  \"[variable-2 ${test test}]\");\n  MT('operator_splat',   \"[variable-2 @x]\");\n  MT('variable_builtin', \"[builtin $ErrorActionPreference]\");\n  MT('variable_builtin_symbols', \"[builtin $$]\");\n\n  MT('operator', \"[operator +]\");\n  MT('operator_unary', \"[operator +][number 3]\");\n  MT('operator_long', \"[operator -match]\");\n\n  forEach([\n    '(', ')', '[[', ']]', '{', '}', ',', '`', ';', '.', '\\\\'\n  ], function(punctuation) {\n    MT(\"punctuation_\" + punctuation.replace(/^[\\[\\]]/,''), \"[punctuation \" + punctuation + \"]\");\n  });\n\n  MT('keyword', \"[keyword if]\");\n\n  MT('call_builtin', \"[builtin Get-ChildItem]\");\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/properties/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Properties files mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"properties.js\"></script>\n<style>.CodeMirror {border-top: 1px solid #ddd; border-bottom: 1px solid #ddd;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Properties files</a>\n  </ul>\n</div>\n\n<article>\n<h2>Properties files mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n# This is a properties file\na.key = A value\nanother.key = http://example.com\n! Exclamation mark as comment\nbut.not=Within ! A value # indeed\n   # Spaces at the beginning of a line\n   spaces.before.key=value\nbackslash=Used for multi\\\n          line entries,\\\n          that's convenient.\n# Unicode sequences\nunicode.key=This is \\u0020 Unicode\nno.multiline=here\n# Colons\ncolons : can be used too\n# Spaces\nspaces\\ in\\ keys=Not very common...\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-properties</code>,\n    <code>text/x-ini</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/properties/properties.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"properties\", function() {\n  return {\n    token: function(stream, state) {\n      var sol = stream.sol() || state.afterSection;\n      var eol = stream.eol();\n\n      state.afterSection = false;\n\n      if (sol) {\n        if (state.nextMultiline) {\n          state.inMultiline = true;\n          state.nextMultiline = false;\n        } else {\n          state.position = \"def\";\n        }\n      }\n\n      if (eol && ! state.nextMultiline) {\n        state.inMultiline = false;\n        state.position = \"def\";\n      }\n\n      if (sol) {\n        while(stream.eatSpace()) {}\n      }\n\n      var ch = stream.next();\n\n      if (sol && (ch === \"#\" || ch === \"!\" || ch === \";\")) {\n        state.position = \"comment\";\n        stream.skipToEnd();\n        return \"comment\";\n      } else if (sol && ch === \"[\") {\n        state.afterSection = true;\n        stream.skipTo(\"]\"); stream.eat(\"]\");\n        return \"header\";\n      } else if (ch === \"=\" || ch === \":\") {\n        state.position = \"quote\";\n        return null;\n      } else if (ch === \"\\\\\" && state.position === \"quote\") {\n        if (stream.eol()) {  // end of line?\n          // Multiline value\n          state.nextMultiline = true;\n        }\n      }\n\n      return state.position;\n    },\n\n    startState: function() {\n      return {\n        position : \"def\",       // Current position, \"def\", \"quote\" or \"comment\"\n        nextMultiline : false,  // Is the next line multiline value\n        inMultiline : false,    // Is the current line a multiline value\n        afterSection : false    // Did we just open a section\n      };\n    }\n\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-properties\", \"properties\");\nCodeMirror.defineMIME(\"text/x-ini\", \"properties\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/protobuf/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: ProtoBuf mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"protobuf.js\"></script>\n<style>.CodeMirror { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">ProtoBuf</a>\n  </ul>\n</div>\n\n<article>\n<h2>ProtoBuf mode</h2>\n<form><textarea id=\"code\" name=\"code\">\npackage addressbook;\n\nmessage Address {\n   required string street = 1;\n   required string postCode = 2;\n}\n\nmessage PhoneNumber {\n   required string number = 1;\n}\n\nmessage Person {\n   optional int32 id = 1;\n   required string name = 2;\n   required string surname = 3;\n   optional Address address = 4;\n   repeated PhoneNumber phoneNumbers = 5;\n   optional uint32 age = 6;\n   repeated uint32 favouriteNumbers = 7;\n   optional string license = 8;\n   enum Gender {\n      MALE = 0;\n      FEMALE = 1;\n   }\n   optional Gender gender = 9;\n   optional fixed64 lastUpdate = 10;\n   required bool deleted = 11 [default = false];\n}\n\n</textarea>\n  <textarea id=\"code2\" name=\"code2\">\nsyntax = \"proto3\";\npackage tutorial;\n\nimport \"google/protobuf/timestamp.proto\";\noption java_package = \"com.example.tutorial\";\noption java_outer_classname = \"AddressBookProtos\";\noption csharp_namespace = \"Google.Protobuf.Examples.AddressBook\";\n\nmessage Person {\n  string name = 1;\n  int32 id = 2;  // Unique ID number for this person.\n  string email = 3;\n\n  enum PhoneType {\n    MOBILE = 0;\n    HOME = 1;\n    WORK = 2;\n  }\n\n  message PhoneNumber {\n    string number = 1;\n    PhoneType type = 2;\n  }\n\n  repeated PhoneNumber phones = 4;\n\n  google.protobuf.Timestamp last_updated = 5;\n}\n\n// Our address book file is just one of these.\nmessage AddressBook {\n  repeated Person people = 1;\n}\nservice Test {\n  rpc SayHello (HelloRequest) returns (HelloReply) {}\n  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}\n}</textarea>\n</form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code2\"), {});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-protobuf</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/protobuf/protobuf.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  function wordRegexp(words) {\n    return new RegExp(\"^((\" + words.join(\")|(\") + \"))\\\\b\", \"i\");\n  };\n\n  var keywordArray = [\n    \"package\", \"message\", \"import\", \"syntax\",\n    \"required\", \"optional\", \"repeated\", \"reserved\", \"default\", \"extensions\", \"packed\",\n    \"bool\", \"bytes\", \"double\", \"enum\", \"float\", \"string\",\n    \"int32\", \"int64\", \"uint32\", \"uint64\", \"sint32\", \"sint64\", \"fixed32\", \"fixed64\", \"sfixed32\", \"sfixed64\",\n    \"option\", \"service\", \"rpc\", \"returns\"\n  ];\n  var keywords = wordRegexp(keywordArray);\n\n  CodeMirror.registerHelper(\"hintWords\", \"protobuf\", keywordArray);\n\n  var identifiers = new RegExp(\"^[_A-Za-z\\xa1-\\uffff][_A-Za-z0-9\\xa1-\\uffff]*\");\n\n  function tokenBase(stream) {\n    // whitespaces\n    if (stream.eatSpace()) return null;\n\n    // Handle one line Comments\n    if (stream.match(\"//\")) {\n      stream.skipToEnd();\n      return \"comment\";\n    }\n\n    // Handle Number Literals\n    if (stream.match(/^[0-9\\.+-]/, false)) {\n      if (stream.match(/^[+-]?0x[0-9a-fA-F]+/))\n        return \"number\";\n      if (stream.match(/^[+-]?\\d*\\.\\d+([EeDd][+-]?\\d+)?/))\n        return \"number\";\n      if (stream.match(/^[+-]?\\d+([EeDd][+-]?\\d+)?/))\n        return \"number\";\n    }\n\n    // Handle Strings\n    if (stream.match(/^\"([^\"]|(\"\"))*\"/)) { return \"string\"; }\n    if (stream.match(/^'([^']|(''))*'/)) { return \"string\"; }\n\n    // Handle words\n    if (stream.match(keywords)) { return \"keyword\"; }\n    if (stream.match(identifiers)) { return \"variable\"; } ;\n\n    // Handle non-detected items\n    stream.next();\n    return null;\n  };\n\n  CodeMirror.defineMode(\"protobuf\", function() {\n    return {\n      token: tokenBase,\n      fold: \"brace\"\n    };\n  });\n\n  CodeMirror.defineMIME(\"text/x-protobuf\", \"protobuf\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/pug/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Pug Templating Mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../javascript/javascript.js\"></script>\n<script src=\"../css/css.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../htmlmixed/htmlmixed.js\"></script>\n<script src=\"pug.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Pug Templating Mode</a>\n  </ul>\n</div>\n\n<article>\n<h2>Pug Templating Mode</h2>\n<form><textarea id=\"code\" name=\"code\">\ndoctype html\n  html\n    head\n      title= \"Pug Templating CodeMirror Mode Example\"\n      link(rel='stylesheet', href='/css/bootstrap.min.css')\n      link(rel='stylesheet', href='/css/index.css')\n      script(type='text/javascript', src='/js/jquery-1.9.1.min.js')\n      script(type='text/javascript', src='/js/bootstrap.min.js')\n    body\n      div.header\n        h1 Welcome to this Example\n      div.spots\n        if locals.spots\n          each spot in spots\n            div.spot.well\n         div\n           if spot.logo\n             img.img-rounded.logo(src=spot.logo)\n           else\n             img.img-rounded.logo(src=\"img/placeholder.png\")\n         h3\n           a(href=spot.hash) ##{spot.hash}\n           if spot.title\n             span.title #{spot.title}\n           if spot.desc\n             div #{spot.desc}\n        else\n          h3 There are no spots currently available.\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: {name: \"pug\", alignCDATA: true},\n        lineNumbers: true\n      });\n    </script>\n    <h3>The Pug Templating Mode</h3>\n      <p> Created by Forbes Lindesay. Managed as part of a Brackets extension at <a href=\"https://github.com/ForbesLindesay/jade-brackets\">https://github.com/ForbesLindesay/jade-brackets</a>.</p>\n    <p><strong>MIME type defined:</strong> <code>text/x-pug</code>, <code>text/x-jade</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/pug/pug.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../javascript/javascript\"), require(\"../css/css\"), require(\"../htmlmixed/htmlmixed\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../javascript/javascript\", \"../css/css\", \"../htmlmixed/htmlmixed\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"pug\", function (config) {\n  // token types\n  var KEYWORD = 'keyword';\n  var DOCTYPE = 'meta';\n  var ID = 'builtin';\n  var CLASS = 'qualifier';\n\n  var ATTRS_NEST = {\n    '{': '}',\n    '(': ')',\n    '[': ']'\n  };\n\n  var jsMode = CodeMirror.getMode(config, 'javascript');\n\n  function State() {\n    this.javaScriptLine = false;\n    this.javaScriptLineExcludesColon = false;\n\n    this.javaScriptArguments = false;\n    this.javaScriptArgumentsDepth = 0;\n\n    this.isInterpolating = false;\n    this.interpolationNesting = 0;\n\n    this.jsState = CodeMirror.startState(jsMode);\n\n    this.restOfLine = '';\n\n    this.isIncludeFiltered = false;\n    this.isEach = false;\n\n    this.lastTag = '';\n    this.scriptType = '';\n\n    // Attributes Mode\n    this.isAttrs = false;\n    this.attrsNest = [];\n    this.inAttributeName = true;\n    this.attributeIsType = false;\n    this.attrValue = '';\n\n    // Indented Mode\n    this.indentOf = Infinity;\n    this.indentToken = '';\n\n    this.innerMode = null;\n    this.innerState = null;\n\n    this.innerModeForLine = false;\n  }\n  /**\n   * Safely copy a state\n   *\n   * @return {State}\n   */\n  State.prototype.copy = function () {\n    var res = new State();\n    res.javaScriptLine = this.javaScriptLine;\n    res.javaScriptLineExcludesColon = this.javaScriptLineExcludesColon;\n    res.javaScriptArguments = this.javaScriptArguments;\n    res.javaScriptArgumentsDepth = this.javaScriptArgumentsDepth;\n    res.isInterpolating = this.isInterpolating;\n    res.interpolationNesting = this.interpolationNesting;\n\n    res.jsState = CodeMirror.copyState(jsMode, this.jsState);\n\n    res.innerMode = this.innerMode;\n    if (this.innerMode && this.innerState) {\n      res.innerState = CodeMirror.copyState(this.innerMode, this.innerState);\n    }\n\n    res.restOfLine = this.restOfLine;\n\n    res.isIncludeFiltered = this.isIncludeFiltered;\n    res.isEach = this.isEach;\n    res.lastTag = this.lastTag;\n    res.scriptType = this.scriptType;\n    res.isAttrs = this.isAttrs;\n    res.attrsNest = this.attrsNest.slice();\n    res.inAttributeName = this.inAttributeName;\n    res.attributeIsType = this.attributeIsType;\n    res.attrValue = this.attrValue;\n    res.indentOf = this.indentOf;\n    res.indentToken = this.indentToken;\n\n    res.innerModeForLine = this.innerModeForLine;\n\n    return res;\n  };\n\n  function javaScript(stream, state) {\n    if (stream.sol()) {\n      // if javaScriptLine was set at end of line, ignore it\n      state.javaScriptLine = false;\n      state.javaScriptLineExcludesColon = false;\n    }\n    if (state.javaScriptLine) {\n      if (state.javaScriptLineExcludesColon && stream.peek() === ':') {\n        state.javaScriptLine = false;\n        state.javaScriptLineExcludesColon = false;\n        return;\n      }\n      var tok = jsMode.token(stream, state.jsState);\n      if (stream.eol()) state.javaScriptLine = false;\n      return tok || true;\n    }\n  }\n  function javaScriptArguments(stream, state) {\n    if (state.javaScriptArguments) {\n      if (state.javaScriptArgumentsDepth === 0 && stream.peek() !== '(') {\n        state.javaScriptArguments = false;\n        return;\n      }\n      if (stream.peek() === '(') {\n        state.javaScriptArgumentsDepth++;\n      } else if (stream.peek() === ')') {\n        state.javaScriptArgumentsDepth--;\n      }\n      if (state.javaScriptArgumentsDepth === 0) {\n        state.javaScriptArguments = false;\n        return;\n      }\n\n      var tok = jsMode.token(stream, state.jsState);\n      return tok || true;\n    }\n  }\n\n  function yieldStatement(stream) {\n    if (stream.match(/^yield\\b/)) {\n        return 'keyword';\n    }\n  }\n\n  function doctype(stream) {\n    if (stream.match(/^(?:doctype) *([^\\n]+)?/)) {\n        return DOCTYPE;\n    }\n  }\n\n  function interpolation(stream, state) {\n    if (stream.match('#{')) {\n      state.isInterpolating = true;\n      state.interpolationNesting = 0;\n      return 'punctuation';\n    }\n  }\n\n  function interpolationContinued(stream, state) {\n    if (state.isInterpolating) {\n      if (stream.peek() === '}') {\n        state.interpolationNesting--;\n        if (state.interpolationNesting < 0) {\n          stream.next();\n          state.isInterpolating = false;\n          return 'punctuation';\n        }\n      } else if (stream.peek() === '{') {\n        state.interpolationNesting++;\n      }\n      return jsMode.token(stream, state.jsState) || true;\n    }\n  }\n\n  function caseStatement(stream, state) {\n    if (stream.match(/^case\\b/)) {\n      state.javaScriptLine = true;\n      return KEYWORD;\n    }\n  }\n\n  function when(stream, state) {\n    if (stream.match(/^when\\b/)) {\n      state.javaScriptLine = true;\n      state.javaScriptLineExcludesColon = true;\n      return KEYWORD;\n    }\n  }\n\n  function defaultStatement(stream) {\n    if (stream.match(/^default\\b/)) {\n      return KEYWORD;\n    }\n  }\n\n  function extendsStatement(stream, state) {\n    if (stream.match(/^extends?\\b/)) {\n      state.restOfLine = 'string';\n      return KEYWORD;\n    }\n  }\n\n  function append(stream, state) {\n    if (stream.match(/^append\\b/)) {\n      state.restOfLine = 'variable';\n      return KEYWORD;\n    }\n  }\n  function prepend(stream, state) {\n    if (stream.match(/^prepend\\b/)) {\n      state.restOfLine = 'variable';\n      return KEYWORD;\n    }\n  }\n  function block(stream, state) {\n    if (stream.match(/^block\\b *(?:(prepend|append)\\b)?/)) {\n      state.restOfLine = 'variable';\n      return KEYWORD;\n    }\n  }\n\n  function include(stream, state) {\n    if (stream.match(/^include\\b/)) {\n      state.restOfLine = 'string';\n      return KEYWORD;\n    }\n  }\n\n  function includeFiltered(stream, state) {\n    if (stream.match(/^include:([a-zA-Z0-9\\-]+)/, false) && stream.match('include')) {\n      state.isIncludeFiltered = true;\n      return KEYWORD;\n    }\n  }\n\n  function includeFilteredContinued(stream, state) {\n    if (state.isIncludeFiltered) {\n      var tok = filter(stream, state);\n      state.isIncludeFiltered = false;\n      state.restOfLine = 'string';\n      return tok;\n    }\n  }\n\n  function mixin(stream, state) {\n    if (stream.match(/^mixin\\b/)) {\n      state.javaScriptLine = true;\n      return KEYWORD;\n    }\n  }\n\n  function call(stream, state) {\n    if (stream.match(/^\\+([-\\w]+)/)) {\n      if (!stream.match(/^\\( *[-\\w]+ *=/, false)) {\n        state.javaScriptArguments = true;\n        state.javaScriptArgumentsDepth = 0;\n      }\n      return 'variable';\n    }\n    if (stream.match('+#{', false)) {\n      stream.next();\n      state.mixinCallAfter = true;\n      return interpolation(stream, state);\n    }\n  }\n  function callArguments(stream, state) {\n    if (state.mixinCallAfter) {\n      state.mixinCallAfter = false;\n      if (!stream.match(/^\\( *[-\\w]+ *=/, false)) {\n        state.javaScriptArguments = true;\n        state.javaScriptArgumentsDepth = 0;\n      }\n      return true;\n    }\n  }\n\n  function conditional(stream, state) {\n    if (stream.match(/^(if|unless|else if|else)\\b/)) {\n      state.javaScriptLine = true;\n      return KEYWORD;\n    }\n  }\n\n  function each(stream, state) {\n    if (stream.match(/^(- *)?(each|for)\\b/)) {\n      state.isEach = true;\n      return KEYWORD;\n    }\n  }\n  function eachContinued(stream, state) {\n    if (state.isEach) {\n      if (stream.match(/^ in\\b/)) {\n        state.javaScriptLine = true;\n        state.isEach = false;\n        return KEYWORD;\n      } else if (stream.sol() || stream.eol()) {\n        state.isEach = false;\n      } else if (stream.next()) {\n        while (!stream.match(/^ in\\b/, false) && stream.next());\n        return 'variable';\n      }\n    }\n  }\n\n  function whileStatement(stream, state) {\n    if (stream.match(/^while\\b/)) {\n      state.javaScriptLine = true;\n      return KEYWORD;\n    }\n  }\n\n  function tag(stream, state) {\n    var captures;\n    if (captures = stream.match(/^(\\w(?:[-:\\w]*\\w)?)\\/?/)) {\n      state.lastTag = captures[1].toLowerCase();\n      if (state.lastTag === 'script') {\n        state.scriptType = 'application/javascript';\n      }\n      return 'tag';\n    }\n  }\n\n  function filter(stream, state) {\n    if (stream.match(/^:([\\w\\-]+)/)) {\n      var innerMode;\n      if (config && config.innerModes) {\n        innerMode = config.innerModes(stream.current().substring(1));\n      }\n      if (!innerMode) {\n        innerMode = stream.current().substring(1);\n      }\n      if (typeof innerMode === 'string') {\n        innerMode = CodeMirror.getMode(config, innerMode);\n      }\n      setInnerMode(stream, state, innerMode);\n      return 'atom';\n    }\n  }\n\n  function code(stream, state) {\n    if (stream.match(/^(!?=|-)/)) {\n      state.javaScriptLine = true;\n      return 'punctuation';\n    }\n  }\n\n  function id(stream) {\n    if (stream.match(/^#([\\w-]+)/)) {\n      return ID;\n    }\n  }\n\n  function className(stream) {\n    if (stream.match(/^\\.([\\w-]+)/)) {\n      return CLASS;\n    }\n  }\n\n  function attrs(stream, state) {\n    if (stream.peek() == '(') {\n      stream.next();\n      state.isAttrs = true;\n      state.attrsNest = [];\n      state.inAttributeName = true;\n      state.attrValue = '';\n      state.attributeIsType = false;\n      return 'punctuation';\n    }\n  }\n\n  function attrsContinued(stream, state) {\n    if (state.isAttrs) {\n      if (ATTRS_NEST[stream.peek()]) {\n        state.attrsNest.push(ATTRS_NEST[stream.peek()]);\n      }\n      if (state.attrsNest[state.attrsNest.length - 1] === stream.peek()) {\n        state.attrsNest.pop();\n      } else  if (stream.eat(')')) {\n        state.isAttrs = false;\n        return 'punctuation';\n      }\n      if (state.inAttributeName && stream.match(/^[^=,\\)!]+/)) {\n        if (stream.peek() === '=' || stream.peek() === '!') {\n          state.inAttributeName = false;\n          state.jsState = CodeMirror.startState(jsMode);\n          if (state.lastTag === 'script' && stream.current().trim().toLowerCase() === 'type') {\n            state.attributeIsType = true;\n          } else {\n            state.attributeIsType = false;\n          }\n        }\n        return 'attribute';\n      }\n\n      var tok = jsMode.token(stream, state.jsState);\n      if (state.attributeIsType && tok === 'string') {\n        state.scriptType = stream.current().toString();\n      }\n      if (state.attrsNest.length === 0 && (tok === 'string' || tok === 'variable' || tok === 'keyword')) {\n        try {\n          Function('', 'var x ' + state.attrValue.replace(/,\\s*$/, '').replace(/^!/, ''));\n          state.inAttributeName = true;\n          state.attrValue = '';\n          stream.backUp(stream.current().length);\n          return attrsContinued(stream, state);\n        } catch (ex) {\n          //not the end of an attribute\n        }\n      }\n      state.attrValue += stream.current();\n      return tok || true;\n    }\n  }\n\n  function attributesBlock(stream, state) {\n    if (stream.match(/^&attributes\\b/)) {\n      state.javaScriptArguments = true;\n      state.javaScriptArgumentsDepth = 0;\n      return 'keyword';\n    }\n  }\n\n  function indent(stream) {\n    if (stream.sol() && stream.eatSpace()) {\n      return 'indent';\n    }\n  }\n\n  function comment(stream, state) {\n    if (stream.match(/^ *\\/\\/(-)?([^\\n]*)/)) {\n      state.indentOf = stream.indentation();\n      state.indentToken = 'comment';\n      return 'comment';\n    }\n  }\n\n  function colon(stream) {\n    if (stream.match(/^: */)) {\n      return 'colon';\n    }\n  }\n\n  function text(stream, state) {\n    if (stream.match(/^(?:\\| ?| )([^\\n]+)/)) {\n      return 'string';\n    }\n    if (stream.match(/^(<[^\\n]*)/, false)) {\n      // html string\n      setInnerMode(stream, state, 'htmlmixed');\n      state.innerModeForLine = true;\n      return innerMode(stream, state, true);\n    }\n  }\n\n  function dot(stream, state) {\n    if (stream.eat('.')) {\n      var innerMode = null;\n      if (state.lastTag === 'script' && state.scriptType.toLowerCase().indexOf('javascript') != -1) {\n        innerMode = state.scriptType.toLowerCase().replace(/\"|'/g, '');\n      } else if (state.lastTag === 'style') {\n        innerMode = 'css';\n      }\n      setInnerMode(stream, state, innerMode);\n      return 'dot';\n    }\n  }\n\n  function fail(stream) {\n    stream.next();\n    return null;\n  }\n\n\n  function setInnerMode(stream, state, mode) {\n    mode = CodeMirror.mimeModes[mode] || mode;\n    mode = config.innerModes ? config.innerModes(mode) || mode : mode;\n    mode = CodeMirror.mimeModes[mode] || mode;\n    mode = CodeMirror.getMode(config, mode);\n    state.indentOf = stream.indentation();\n\n    if (mode && mode.name !== 'null') {\n      state.innerMode = mode;\n    } else {\n      state.indentToken = 'string';\n    }\n  }\n  function innerMode(stream, state, force) {\n    if (stream.indentation() > state.indentOf || (state.innerModeForLine && !stream.sol()) || force) {\n      if (state.innerMode) {\n        if (!state.innerState) {\n          state.innerState = state.innerMode.startState ? CodeMirror.startState(state.innerMode, stream.indentation()) : {};\n        }\n        return stream.hideFirstChars(state.indentOf + 2, function () {\n          return state.innerMode.token(stream, state.innerState) || true;\n        });\n      } else {\n        stream.skipToEnd();\n        return state.indentToken;\n      }\n    } else if (stream.sol()) {\n      state.indentOf = Infinity;\n      state.indentToken = null;\n      state.innerMode = null;\n      state.innerState = null;\n    }\n  }\n  function restOfLine(stream, state) {\n    if (stream.sol()) {\n      // if restOfLine was set at end of line, ignore it\n      state.restOfLine = '';\n    }\n    if (state.restOfLine) {\n      stream.skipToEnd();\n      var tok = state.restOfLine;\n      state.restOfLine = '';\n      return tok;\n    }\n  }\n\n\n  function startState() {\n    return new State();\n  }\n  function copyState(state) {\n    return state.copy();\n  }\n  /**\n   * Get the next token in the stream\n   *\n   * @param {Stream} stream\n   * @param {State} state\n   */\n  function nextToken(stream, state) {\n    var tok = innerMode(stream, state)\n      || restOfLine(stream, state)\n      || interpolationContinued(stream, state)\n      || includeFilteredContinued(stream, state)\n      || eachContinued(stream, state)\n      || attrsContinued(stream, state)\n      || javaScript(stream, state)\n      || javaScriptArguments(stream, state)\n      || callArguments(stream, state)\n\n      || yieldStatement(stream)\n      || doctype(stream)\n      || interpolation(stream, state)\n      || caseStatement(stream, state)\n      || when(stream, state)\n      || defaultStatement(stream)\n      || extendsStatement(stream, state)\n      || append(stream, state)\n      || prepend(stream, state)\n      || block(stream, state)\n      || include(stream, state)\n      || includeFiltered(stream, state)\n      || mixin(stream, state)\n      || call(stream, state)\n      || conditional(stream, state)\n      || each(stream, state)\n      || whileStatement(stream, state)\n      || tag(stream, state)\n      || filter(stream, state)\n      || code(stream, state)\n      || id(stream)\n      || className(stream)\n      || attrs(stream, state)\n      || attributesBlock(stream, state)\n      || indent(stream)\n      || text(stream, state)\n      || comment(stream, state)\n      || colon(stream)\n      || dot(stream, state)\n      || fail(stream);\n\n    return tok === true ? null : tok;\n  }\n  return {\n    startState: startState,\n    copyState: copyState,\n    token: nextToken\n  };\n}, 'javascript', 'css', 'htmlmixed');\n\nCodeMirror.defineMIME('text/x-pug', 'pug');\nCodeMirror.defineMIME('text/x-jade', 'pug');\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/puppet/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Puppet mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"puppet.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n      .cm-s-default span.cm-arrow { color: red; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Puppet</a>\n  </ul>\n</div>\n\n<article>\n<h2>Puppet mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n# == Class: automysqlbackup\n#\n# Puppet module to install AutoMySQLBackup for periodic MySQL backups.\n#\n# class { 'automysqlbackup':\n#   backup_dir => '/mnt/backups',\n# }\n#\n\nclass automysqlbackup (\n  $bin_dir = $automysqlbackup::params::bin_dir,\n  $etc_dir = $automysqlbackup::params::etc_dir,\n  $backup_dir = $automysqlbackup::params::backup_dir,\n  $install_multicore = undef,\n  $config = {},\n  $config_defaults = {},\n) inherits automysqlbackup::params {\n\n# Ensure valid paths are assigned\n  validate_absolute_path($bin_dir)\n  validate_absolute_path($etc_dir)\n  validate_absolute_path($backup_dir)\n\n# Create a subdirectory in /etc for config files\n  file { $etc_dir:\n    ensure => directory,\n    owner => 'root',\n    group => 'root',\n    mode => '0750',\n  }\n\n# Create an example backup file, useful for reference\n  file { \"${etc_dir}/automysqlbackup.conf.example\":\n    ensure => file,\n    owner => 'root',\n    group => 'root',\n    mode => '0660',\n    source => 'puppet:///modules/automysqlbackup/automysqlbackup.conf',\n  }\n\n# Add files from the developer\n  file { \"${etc_dir}/AMB_README\":\n    ensure => file,\n    source => 'puppet:///modules/automysqlbackup/AMB_README',\n  }\n  file { \"${etc_dir}/AMB_LICENSE\":\n    ensure => file,\n    source => 'puppet:///modules/automysqlbackup/AMB_LICENSE',\n  }\n\n# Install the actual binary file\n  file { \"${bin_dir}/automysqlbackup\":\n    ensure => file,\n    owner => 'root',\n    group => 'root',\n    mode => '0755',\n    source => 'puppet:///modules/automysqlbackup/automysqlbackup',\n  }\n\n# Create the base backup directory\n  file { $backup_dir:\n    ensure => directory,\n    owner => 'root',\n    group => 'root',\n    mode => '0755',\n  }\n\n# If you'd like to keep your config in hiera and pass it to this class\n  if !empty($config) {\n    create_resources('automysqlbackup::backup', $config, $config_defaults)\n  }\n\n# If using RedHat family, must have the RPMforge repo's enabled\n  if $install_multicore {\n    package { ['pigz', 'pbzip2']: ensure => installed }\n  }\n\n}\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: \"text/x-puppet\",\n        matchBrackets: true,\n        indentUnit: 4\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-puppet</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/puppet/puppet.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"puppet\", function () {\n  // Stores the words from the define method\n  var words = {};\n  // Taken, mostly, from the Puppet official variable standards regex\n  var variable_regex = /({)?([a-z][a-z0-9_]*)?((::[a-z][a-z0-9_]*)*::)?[a-zA-Z0-9_]+(})?/;\n\n  // Takes a string of words separated by spaces and adds them as\n  // keys with the value of the first argument 'style'\n  function define(style, string) {\n    var split = string.split(' ');\n    for (var i = 0; i < split.length; i++) {\n      words[split[i]] = style;\n    }\n  }\n\n  // Takes commonly known puppet types/words and classifies them to a style\n  define('keyword', 'class define site node include import inherits');\n  define('keyword', 'case if else in and elsif default or');\n  define('atom', 'false true running present absent file directory undef');\n  define('builtin', 'action augeas burst chain computer cron destination dport exec ' +\n    'file filebucket group host icmp iniface interface jump k5login limit log_level ' +\n    'log_prefix macauthorization mailalias maillist mcx mount nagios_command ' +\n    'nagios_contact nagios_contactgroup nagios_host nagios_hostdependency ' +\n    'nagios_hostescalation nagios_hostextinfo nagios_hostgroup nagios_service ' +\n    'nagios_servicedependency nagios_serviceescalation nagios_serviceextinfo ' +\n    'nagios_servicegroup nagios_timeperiod name notify outiface package proto reject ' +\n    'resources router schedule scheduled_task selboolean selmodule service source ' +\n    'sport ssh_authorized_key sshkey stage state table tidy todest toports tosource ' +\n    'user vlan yumrepo zfs zone zpool');\n\n  // After finding a start of a string ('|\") this function attempts to find the end;\n  // If a variable is encountered along the way, we display it differently when it\n  // is encapsulated in a double-quoted string.\n  function tokenString(stream, state) {\n    var current, prev, found_var = false;\n    while (!stream.eol() && (current = stream.next()) != state.pending) {\n      if (current === '$' && prev != '\\\\' && state.pending == '\"') {\n        found_var = true;\n        break;\n      }\n      prev = current;\n    }\n    if (found_var) {\n      stream.backUp(1);\n    }\n    if (current == state.pending) {\n      state.continueString = false;\n    } else {\n      state.continueString = true;\n    }\n    return \"string\";\n  }\n\n  // Main function\n  function tokenize(stream, state) {\n    // Matches one whole word\n    var word = stream.match(/[\\w]+/, false);\n    // Matches attributes (i.e. ensure => present ; 'ensure' would be matched)\n    var attribute = stream.match(/(\\s+)?\\w+\\s+=>.*/, false);\n    // Matches non-builtin resource declarations\n    // (i.e. \"apache::vhost {\" or \"mycustomclasss {\" would be matched)\n    var resource = stream.match(/(\\s+)?[\\w:_]+(\\s+)?{/, false);\n    // Matches virtual and exported resources (i.e. @@user { ; and the like)\n    var special_resource = stream.match(/(\\s+)?[@]{1,2}[\\w:_]+(\\s+)?{/, false);\n\n    // Finally advance the stream\n    var ch = stream.next();\n\n    // Have we found a variable?\n    if (ch === '$') {\n      if (stream.match(variable_regex)) {\n        // If so, and its in a string, assign it a different color\n        return state.continueString ? 'variable-2' : 'variable';\n      }\n      // Otherwise return an invalid variable\n      return \"error\";\n    }\n    // Should we still be looking for the end of a string?\n    if (state.continueString) {\n      // If so, go through the loop again\n      stream.backUp(1);\n      return tokenString(stream, state);\n    }\n    // Are we in a definition (class, node, define)?\n    if (state.inDefinition) {\n      // If so, return def (i.e. for 'class myclass {' ; 'myclass' would be matched)\n      if (stream.match(/(\\s+)?[\\w:_]+(\\s+)?/)) {\n        return 'def';\n      }\n      // Match the rest it the next time around\n      stream.match(/\\s+{/);\n      state.inDefinition = false;\n    }\n    // Are we in an 'include' statement?\n    if (state.inInclude) {\n      // Match and return the included class\n      stream.match(/(\\s+)?\\S+(\\s+)?/);\n      state.inInclude = false;\n      return 'def';\n    }\n    // Do we just have a function on our hands?\n    // In 'ensure_resource(\"myclass\")', 'ensure_resource' is matched\n    if (stream.match(/(\\s+)?\\w+\\(/)) {\n      stream.backUp(1);\n      return 'def';\n    }\n    // Have we matched the prior attribute regex?\n    if (attribute) {\n      stream.match(/(\\s+)?\\w+/);\n      return 'tag';\n    }\n    // Do we have Puppet specific words?\n    if (word && words.hasOwnProperty(word)) {\n      // Negates the initial next()\n      stream.backUp(1);\n      // rs move the stream\n      stream.match(/[\\w]+/);\n      // We want to process these words differently\n      // do to the importance they have in Puppet\n      if (stream.match(/\\s+\\S+\\s+{/, false)) {\n        state.inDefinition = true;\n      }\n      if (word == 'include') {\n        state.inInclude = true;\n      }\n      // Returns their value as state in the prior define methods\n      return words[word];\n    }\n    // Is there a match on a reference?\n    if (/(^|\\s+)[A-Z][\\w:_]+/.test(word)) {\n      // Negate the next()\n      stream.backUp(1);\n      // Match the full reference\n      stream.match(/(^|\\s+)[A-Z][\\w:_]+/);\n      return 'def';\n    }\n    // Have we matched the prior resource regex?\n    if (resource) {\n      stream.match(/(\\s+)?[\\w:_]+/);\n      return 'def';\n    }\n    // Have we matched the prior special_resource regex?\n    if (special_resource) {\n      stream.match(/(\\s+)?[@]{1,2}/);\n      return 'special';\n    }\n    // Match all the comments. All of them.\n    if (ch == \"#\") {\n      stream.skipToEnd();\n      return \"comment\";\n    }\n    // Have we found a string?\n    if (ch == \"'\" || ch == '\"') {\n      // Store the type (single or double)\n      state.pending = ch;\n      // Perform the looping function to find the end\n      return tokenString(stream, state);\n    }\n    // Match all the brackets\n    if (ch == '{' || ch == '}') {\n      return 'bracket';\n    }\n    // Match characters that we are going to assume\n    // are trying to be regex\n    if (ch == '/') {\n      stream.match(/^[^\\/]*\\//);\n      return 'variable-3';\n    }\n    // Match all the numbers\n    if (ch.match(/[0-9]/)) {\n      stream.eatWhile(/[0-9]+/);\n      return 'number';\n    }\n    // Match the '=' and '=>' operators\n    if (ch == '=') {\n      if (stream.peek() == '>') {\n          stream.next();\n      }\n      return \"operator\";\n    }\n    // Keep advancing through all the rest\n    stream.eatWhile(/[\\w-]/);\n    // Return a blank line for everything else\n    return null;\n  }\n  // Start it all\n  return {\n    startState: function () {\n      var state = {};\n      state.inDefinition = false;\n      state.inInclude = false;\n      state.continueString = false;\n      state.pending = false;\n      return state;\n    },\n    token: function (stream, state) {\n      // Strip the spaces, but regex will account for them eitherway\n      if (stream.eatSpace()) return null;\n      // Go through the main process\n      return tokenize(stream, state);\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-puppet\", \"puppet\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/python/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Python mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"python.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Python</a>\n  </ul>\n</div>\n\n<article>\n<h2>Python mode</h2>\n\n    <div><textarea id=\"code\" name=\"code\">\n# Literals\n1234\n0.0e101\n.123\n0b01010011100\n0o01234567\n0x0987654321abcdef\n7\n2147483647\n3L\n79228162514264337593543950336L\n0x100000000L\n79228162514264337593543950336\n0xdeadbeef\n3.14j\n10.j\n10j\n.001j\n1e100j\n3.14e-10j\n\n\n# String Literals\n'For\\''\n\"God\\\"\"\n\"\"\"so loved\nthe world\"\"\"\n'''that he gave\nhis only begotten\\' '''\n'that whosoever believeth \\\nin him'\n''\n\n# Identifiers\n__a__\na.b\na.b.c\n\n#Unicode identifiers on Python3\n# a = x\\ddot\na⃗ = ẍ\n# a = v\\dot\na⃗ = v̇\n\n#F\\vec = m \\cdot a\\vec\nF⃗ = m•a⃗ \n\n# Operators\n+ - * / % & | ^ ~ < >\n== != <= >= <> << >> // **\nand or not in is\n\n#infix matrix multiplication operator (PEP 465)\nA @ B\n\n# Delimiters\n() [] {} , : ` = ; @ .  # Note that @ and . require the proper context on Python 2.\n+= -= *= /= %= &= |= ^=\n//= >>= <<= **=\n\n# Keywords\nas assert break class continue def del elif else except\nfinally for from global if import lambda pass raise\nreturn try while with yield\n\n# Python 2 Keywords (otherwise Identifiers)\nexec print\n\n# Python 3 Keywords (otherwise Identifiers)\nnonlocal\n\n# Types\nbool classmethod complex dict enumerate float frozenset int list object\nproperty reversed set slice staticmethod str super tuple type\n\n# Python 2 Types (otherwise Identifiers)\nbasestring buffer file long unicode xrange\n\n# Python 3 Types (otherwise Identifiers)\nbytearray bytes filter map memoryview open range zip\n\n# Some Example code\nimport os\nfrom package import ParentClass\n\n@nonsenseDecorator\ndef doesNothing():\n    pass\n\nclass ExampleClass(ParentClass):\n    @staticmethod\n    def example(inputStr):\n        a = list(inputStr)\n        a.reverse()\n        return ''.join(a)\n\n    def __init__(self, mixin = 'Hello'):\n        self.mixin = mixin\n\n# Python 3.6 f-strings (https://www.python.org/dev/peps/pep-0498/)\nf'My name is {name}, my age next year is {age+1}, my anniversary is {anniversary:%A, %B %d, %Y}.'\nf'He said his name is {name!r}.'\nf\"\"\"He said his name is {name!r}.\"\"\"\nf'{\"quoted string\"}'\nf'{{ {4*10} }}'\nf'This is an error }'\nf'This is ok }}'\nfr'x={4*10}\\n'\n</textarea></div>\n\n\n<h2>Cython mode</h2>\n\n<div><textarea id=\"code-cython\" name=\"code-cython\">\n\nimport numpy as np\ncimport cython\nfrom libc.math cimport sqrt\n\n@cython.boundscheck(False)\n@cython.wraparound(False)\ndef pairwise_cython(double[:, ::1] X):\n    cdef int M = X.shape[0]\n    cdef int N = X.shape[1]\n    cdef double tmp, d\n    cdef double[:, ::1] D = np.empty((M, M), dtype=np.float64)\n    for i in range(M):\n        for j in range(M):\n            d = 0.0\n            for k in range(N):\n                tmp = X[i, k] - X[j, k]\n                d += tmp * tmp\n            D[i, j] = sqrt(d)\n    return np.asarray(D)\n\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: {name: \"python\",\n               version: 3,\n               singleLineStringErrors: false},\n        lineNumbers: true,\n        indentUnit: 4,\n        matchBrackets: true\n    });\n\n    CodeMirror.fromTextArea(document.getElementById(\"code-cython\"), {\n        mode: {name: \"text/x-cython\",\n               version: 2,\n               singleLineStringErrors: false},\n        lineNumbers: true,\n        indentUnit: 4,\n        matchBrackets: true\n      });\n    </script>\n    <h2>Configuration Options for Python mode:</h2>\n    <ul>\n      <li>version - 2/3 - The version of Python to recognize.  Default is 3.</li>\n      <li>singleLineStringErrors - true/false - If you have a single-line string that is not terminated at the end of the line, this will show subsequent lines as errors if true, otherwise it will consider the newline as the end of the string. Default is false.</li>\n      <li>hangingIndent - int - If you want to write long arguments to a function starting on a new line, how much that line should be indented. Defaults to one normal indentation unit.</li>\n    </ul>\n    <h2>Advanced Configuration Options:</h2>\n    <p>Useful for superset of python syntax like Enthought enaml, IPython magics and  questionmark help</p>\n    <ul>\n      <li>singleOperators - RegEx - Regular Expression for single operator matching,  default : <pre>^[\\\\+\\\\-\\\\*/%&amp;|\\\\^~&lt;&gt;!]</pre> including <pre>@</pre> on Python 3</li>\n      <li>singleDelimiters - RegEx - Regular Expression for single delimiter matching, default :  <pre>^[\\\\(\\\\)\\\\[\\\\]\\\\{\\\\}@,:`=;\\\\.]</pre></li>\n      <li>doubleOperators - RegEx - Regular Expression for double operators matching, default : <pre>^((==)|(!=)|(&lt;=)|(&gt;=)|(&lt;&gt;)|(&lt;&lt;)|(&gt;&gt;)|(//)|(\\\\*\\\\*))</pre></li>\n      <li>doubleDelimiters - RegEx - Regular Expression for double delimiters matching, default : <pre>^((\\\\+=)|(\\\\-=)|(\\\\*=)|(%=)|(/=)|(&amp;=)|(\\\\|=)|(\\\\^=))</pre></li>\n      <li>tripleDelimiters - RegEx - Regular Expression for triple delimiters matching, default : <pre>^((//=)|(&gt;&gt;=)|(&lt;&lt;=)|(\\\\*\\\\*=))</pre></li>\n      <li>identifiers - RegEx - Regular Expression for identifier, default : <pre>^[_A-Za-z][_A-Za-z0-9]*</pre> on Python 2 and <pre>^[_A-Za-z\\u00A1-\\uFFFF][_A-Za-z0-9\\u00A1-\\uFFFF]*</pre> on Python 3.</li>\n      <li>extra_keywords - list of string - List of extra words ton consider as keywords</li>\n      <li>extra_builtins - list of string - List of extra words ton consider as builtins</li>\n    </ul>\n\n\n    <p><strong>MIME types defined:</strong> <code>text/x-python</code> and <code>text/x-cython</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/python/python.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  function wordRegexp(words) {\n    return new RegExp(\"^((\" + words.join(\")|(\") + \"))\\\\b\");\n  }\n\n  var wordOperators = wordRegexp([\"and\", \"or\", \"not\", \"is\"]);\n  var commonKeywords = [\"as\", \"assert\", \"break\", \"class\", \"continue\",\n                        \"def\", \"del\", \"elif\", \"else\", \"except\", \"finally\",\n                        \"for\", \"from\", \"global\", \"if\", \"import\",\n                        \"lambda\", \"pass\", \"raise\", \"return\",\n                        \"try\", \"while\", \"with\", \"yield\", \"in\", \"False\", \"True\"];\n  var commonBuiltins = [\"abs\", \"all\", \"any\", \"bin\", \"bool\", \"bytearray\", \"callable\", \"chr\",\n                        \"classmethod\", \"compile\", \"complex\", \"delattr\", \"dict\", \"dir\", \"divmod\",\n                        \"enumerate\", \"eval\", \"filter\", \"float\", \"format\", \"frozenset\",\n                        \"getattr\", \"globals\", \"hasattr\", \"hash\", \"help\", \"hex\", \"id\",\n                        \"input\", \"int\", \"isinstance\", \"issubclass\", \"iter\", \"len\",\n                        \"list\", \"locals\", \"map\", \"max\", \"memoryview\", \"min\", \"next\",\n                        \"object\", \"oct\", \"open\", \"ord\", \"pow\", \"property\", \"range\",\n                        \"repr\", \"reversed\", \"round\", \"set\", \"setattr\", \"slice\",\n                        \"sorted\", \"staticmethod\", \"str\", \"sum\", \"super\", \"tuple\",\n                        \"type\", \"vars\", \"zip\", \"__import__\", \"NotImplemented\",\n                        \"Ellipsis\", \"__debug__\"];\n  CodeMirror.registerHelper(\"hintWords\", \"python\", commonKeywords.concat(commonBuiltins).concat([\"exec\", \"print\"]));\n\n  function top(state) {\n    return state.scopes[state.scopes.length - 1];\n  }\n\n  CodeMirror.defineMode(\"python\", function(conf, parserConf) {\n    var ERRORCLASS = \"error\";\n\n    var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\\(\\)\\[\\]\\{\\}@,:`=;\\.\\\\]/;\n    //               (Backwards-compatibility with old, cumbersome config system)\n    var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters,\n                     parserConf.operators || /^([-+*/%\\/&|^]=?|[<>=]+|\\/\\/=?|\\*\\*=?|!=|[~!@]|\\.\\.\\.)/]\n    for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1)\n\n    var hangingIndent = parserConf.hangingIndent || conf.indentUnit;\n\n    var myKeywords = commonKeywords, myBuiltins = commonBuiltins;\n    if (parserConf.extra_keywords != undefined)\n      myKeywords = myKeywords.concat(parserConf.extra_keywords);\n\n    if (parserConf.extra_builtins != undefined)\n      myBuiltins = myBuiltins.concat(parserConf.extra_builtins);\n\n    var py3 = !(parserConf.version && Number(parserConf.version) < 3)\n    if (py3) {\n      // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator\n      var identifiers = parserConf.identifiers|| /^[_A-Za-z\\u00A1-\\uFFFF][_A-Za-z0-9\\u00A1-\\uFFFF]*/;\n      myKeywords = myKeywords.concat([\"nonlocal\", \"None\", \"aiter\", \"anext\", \"async\", \"await\", \"breakpoint\", \"match\", \"case\"]);\n      myBuiltins = myBuiltins.concat([\"ascii\", \"bytes\", \"exec\", \"print\"]);\n      var stringPrefixes = new RegExp(\"^(([rbuf]|(br)|(rb)|(fr)|(rf))?('{3}|\\\"{3}|['\\\"]))\", \"i\");\n    } else {\n      var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/;\n      myKeywords = myKeywords.concat([\"exec\", \"print\"]);\n      myBuiltins = myBuiltins.concat([\"apply\", \"basestring\", \"buffer\", \"cmp\", \"coerce\", \"execfile\",\n                                      \"file\", \"intern\", \"long\", \"raw_input\", \"reduce\", \"reload\",\n                                      \"unichr\", \"unicode\", \"xrange\", \"None\"]);\n      var stringPrefixes = new RegExp(\"^(([rubf]|(ur)|(br))?('{3}|\\\"{3}|['\\\"]))\", \"i\");\n    }\n    var keywords = wordRegexp(myKeywords);\n    var builtins = wordRegexp(myBuiltins);\n\n    // tokenizers\n    function tokenBase(stream, state) {\n      var sol = stream.sol() && state.lastToken != \"\\\\\"\n      if (sol) state.indent = stream.indentation()\n      // Handle scope changes\n      if (sol && top(state).type == \"py\") {\n        var scopeOffset = top(state).offset;\n        if (stream.eatSpace()) {\n          var lineOffset = stream.indentation();\n          if (lineOffset > scopeOffset)\n            pushPyScope(state);\n          else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != \"#\")\n            state.errorToken = true;\n          return null;\n        } else {\n          var style = tokenBaseInner(stream, state);\n          if (scopeOffset > 0 && dedent(stream, state))\n            style += \" \" + ERRORCLASS;\n          return style;\n        }\n      }\n      return tokenBaseInner(stream, state);\n    }\n\n    function tokenBaseInner(stream, state, inFormat) {\n      if (stream.eatSpace()) return null;\n\n      // Handle Comments\n      if (!inFormat && stream.match(/^#.*/)) return \"comment\";\n\n      // Handle Number Literals\n      if (stream.match(/^[0-9\\.]/, false)) {\n        var floatLiteral = false;\n        // Floats\n        if (stream.match(/^[\\d_]*\\.\\d+(e[\\+\\-]?\\d+)?/i)) { floatLiteral = true; }\n        if (stream.match(/^[\\d_]+\\.\\d*/)) { floatLiteral = true; }\n        if (stream.match(/^\\.\\d+/)) { floatLiteral = true; }\n        if (floatLiteral) {\n          // Float literals may be \"imaginary\"\n          stream.eat(/J/i);\n          return \"number\";\n        }\n        // Integers\n        var intLiteral = false;\n        // Hex\n        if (stream.match(/^0x[0-9a-f_]+/i)) intLiteral = true;\n        // Binary\n        if (stream.match(/^0b[01_]+/i)) intLiteral = true;\n        // Octal\n        if (stream.match(/^0o[0-7_]+/i)) intLiteral = true;\n        // Decimal\n        if (stream.match(/^[1-9][\\d_]*(e[\\+\\-]?[\\d_]+)?/)) {\n          // Decimal literals may be \"imaginary\"\n          stream.eat(/J/i);\n          // TODO - Can you have imaginary longs?\n          intLiteral = true;\n        }\n        // Zero by itself with no other piece of number.\n        if (stream.match(/^0(?![\\dx])/i)) intLiteral = true;\n        if (intLiteral) {\n          // Integer literals may be \"long\"\n          stream.eat(/L/i);\n          return \"number\";\n        }\n      }\n\n      // Handle Strings\n      if (stream.match(stringPrefixes)) {\n        var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1;\n        if (!isFmtString) {\n          state.tokenize = tokenStringFactory(stream.current(), state.tokenize);\n          return state.tokenize(stream, state);\n        } else {\n          state.tokenize = formatStringFactory(stream.current(), state.tokenize);\n          return state.tokenize(stream, state);\n        }\n      }\n\n      for (var i = 0; i < operators.length; i++)\n        if (stream.match(operators[i])) return \"operator\"\n\n      if (stream.match(delimiters)) return \"punctuation\";\n\n      if (state.lastToken == \".\" && stream.match(identifiers))\n        return \"property\";\n\n      if (stream.match(keywords) || stream.match(wordOperators))\n        return \"keyword\";\n\n      if (stream.match(builtins))\n        return \"builtin\";\n\n      if (stream.match(/^(self|cls)\\b/))\n        return \"variable-2\";\n\n      if (stream.match(identifiers)) {\n        if (state.lastToken == \"def\" || state.lastToken == \"class\")\n          return \"def\";\n        return \"variable\";\n      }\n\n      // Handle non-detected items\n      stream.next();\n      return inFormat ? null :ERRORCLASS;\n    }\n\n    function formatStringFactory(delimiter, tokenOuter) {\n      while (\"rubf\".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)\n        delimiter = delimiter.substr(1);\n\n      var singleline = delimiter.length == 1;\n      var OUTCLASS = \"string\";\n\n      function tokenNestedExpr(depth) {\n        return function(stream, state) {\n          var inner = tokenBaseInner(stream, state, true)\n          if (inner == \"punctuation\") {\n            if (stream.current() == \"{\") {\n              state.tokenize = tokenNestedExpr(depth + 1)\n            } else if (stream.current() == \"}\") {\n              if (depth > 1) state.tokenize = tokenNestedExpr(depth - 1)\n              else state.tokenize = tokenString\n            }\n          }\n          return inner\n        }\n      }\n\n      function tokenString(stream, state) {\n        while (!stream.eol()) {\n          stream.eatWhile(/[^'\"\\{\\}\\\\]/);\n          if (stream.eat(\"\\\\\")) {\n            stream.next();\n            if (singleline && stream.eol())\n              return OUTCLASS;\n          } else if (stream.match(delimiter)) {\n            state.tokenize = tokenOuter;\n            return OUTCLASS;\n          } else if (stream.match('{{')) {\n            // ignore {{ in f-str\n            return OUTCLASS;\n          } else if (stream.match('{', false)) {\n            // switch to nested mode\n            state.tokenize = tokenNestedExpr(0)\n            if (stream.current()) return OUTCLASS;\n            else return state.tokenize(stream, state)\n          } else if (stream.match('}}')) {\n            return OUTCLASS;\n          } else if (stream.match('}')) {\n            // single } in f-string is an error\n            return ERRORCLASS;\n          } else {\n            stream.eat(/['\"]/);\n          }\n        }\n        if (singleline) {\n          if (parserConf.singleLineStringErrors)\n            return ERRORCLASS;\n          else\n            state.tokenize = tokenOuter;\n        }\n        return OUTCLASS;\n      }\n      tokenString.isString = true;\n      return tokenString;\n    }\n\n    function tokenStringFactory(delimiter, tokenOuter) {\n      while (\"rubf\".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)\n        delimiter = delimiter.substr(1);\n\n      var singleline = delimiter.length == 1;\n      var OUTCLASS = \"string\";\n\n      function tokenString(stream, state) {\n        while (!stream.eol()) {\n          stream.eatWhile(/[^'\"\\\\]/);\n          if (stream.eat(\"\\\\\")) {\n            stream.next();\n            if (singleline && stream.eol())\n              return OUTCLASS;\n          } else if (stream.match(delimiter)) {\n            state.tokenize = tokenOuter;\n            return OUTCLASS;\n          } else {\n            stream.eat(/['\"]/);\n          }\n        }\n        if (singleline) {\n          if (parserConf.singleLineStringErrors)\n            return ERRORCLASS;\n          else\n            state.tokenize = tokenOuter;\n        }\n        return OUTCLASS;\n      }\n      tokenString.isString = true;\n      return tokenString;\n    }\n\n    function pushPyScope(state) {\n      while (top(state).type != \"py\") state.scopes.pop()\n      state.scopes.push({offset: top(state).offset + conf.indentUnit,\n                         type: \"py\",\n                         align: null})\n    }\n\n    function pushBracketScope(stream, state, type) {\n      var align = stream.match(/^[\\s\\[\\{\\(]*(?:#|$)/, false) ? null : stream.column() + 1\n      state.scopes.push({offset: state.indent + hangingIndent,\n                         type: type,\n                         align: align})\n    }\n\n    function dedent(stream, state) {\n      var indented = stream.indentation();\n      while (state.scopes.length > 1 && top(state).offset > indented) {\n        if (top(state).type != \"py\") return true;\n        state.scopes.pop();\n      }\n      return top(state).offset != indented;\n    }\n\n    function tokenLexer(stream, state) {\n      if (stream.sol()) {\n        state.beginningOfLine = true;\n        state.dedent = false;\n      }\n\n      var style = state.tokenize(stream, state);\n      var current = stream.current();\n\n      // Handle decorators\n      if (state.beginningOfLine && current == \"@\")\n        return stream.match(identifiers, false) ? \"meta\" : py3 ? \"operator\" : ERRORCLASS;\n\n      if (/\\S/.test(current)) state.beginningOfLine = false;\n\n      if ((style == \"variable\" || style == \"builtin\")\n          && state.lastToken == \"meta\")\n        style = \"meta\";\n\n      // Handle scope changes.\n      if (current == \"pass\" || current == \"return\")\n        state.dedent = true;\n\n      if (current == \"lambda\") state.lambda = true;\n      if (current == \":\" && !state.lambda && top(state).type == \"py\" && stream.match(/^\\s*(?:#|$)/, false))\n        pushPyScope(state);\n\n      if (current.length == 1 && !/string|comment/.test(style)) {\n        var delimiter_index = \"[({\".indexOf(current);\n        if (delimiter_index != -1)\n          pushBracketScope(stream, state, \"])}\".slice(delimiter_index, delimiter_index+1));\n\n        delimiter_index = \"])}\".indexOf(current);\n        if (delimiter_index != -1) {\n          if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent\n          else return ERRORCLASS;\n        }\n      }\n      if (state.dedent && stream.eol() && top(state).type == \"py\" && state.scopes.length > 1)\n        state.scopes.pop();\n\n      return style;\n    }\n\n    var external = {\n      startState: function(basecolumn) {\n        return {\n          tokenize: tokenBase,\n          scopes: [{offset: basecolumn || 0, type: \"py\", align: null}],\n          indent: basecolumn || 0,\n          lastToken: null,\n          lambda: false,\n          dedent: 0\n        };\n      },\n\n      token: function(stream, state) {\n        var addErr = state.errorToken;\n        if (addErr) state.errorToken = false;\n        var style = tokenLexer(stream, state);\n\n        if (style && style != \"comment\")\n          state.lastToken = (style == \"keyword\" || style == \"punctuation\") ? stream.current() : style;\n        if (style == \"punctuation\") style = null;\n\n        if (stream.eol() && state.lambda)\n          state.lambda = false;\n        return addErr ? style + \" \" + ERRORCLASS : style;\n      },\n\n      indent: function(state, textAfter) {\n        if (state.tokenize != tokenBase)\n          return state.tokenize.isString ? CodeMirror.Pass : 0;\n\n        var scope = top(state)\n        var closing = scope.type == textAfter.charAt(0) ||\n            scope.type == \"py\" && !state.dedent && /^(else:|elif |except |finally:)/.test(textAfter)\n        if (scope.align != null)\n          return scope.align - (closing ? 1 : 0)\n        else\n          return scope.offset - (closing ? hangingIndent : 0)\n      },\n\n      electricInput: /^\\s*([\\}\\]\\)]|else:|elif |except |finally:)$/,\n      closeBrackets: {triples: \"'\\\"\"},\n      lineComment: \"#\",\n      fold: \"indent\"\n    };\n    return external;\n  });\n\n  CodeMirror.defineMIME(\"text/x-python\", \"python\");\n\n  var words = function(str) { return str.split(\" \"); };\n\n  CodeMirror.defineMIME(\"text/x-cython\", {\n    name: \"python\",\n    extra_keywords: words(\"by cdef cimport cpdef ctypedef enum except \"+\n                          \"extern gil include nogil property public \"+\n                          \"readonly struct union DEF IF ELIF ELSE\")\n  });\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/python/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 4},\n              {name: \"python\",\n               version: 3,\n               singleLineStringErrors: false});\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  // Error, because \"foobarhello\" is neither a known type or property, but\n  // property was expected (after \"and\"), and it should be in parentheses.\n  MT(\"decoratorStartOfLine\",\n     \"[meta @dec]\",\n     \"[keyword def] [def function]():\",\n     \"    [keyword pass]\");\n\n  MT(\"decoratorIndented\",\n     \"[keyword class] [def Foo]:\",\n     \"    [meta @dec]\",\n     \"    [keyword def] [def function]():\",\n     \"        [keyword pass]\");\n\n  MT(\"matmulWithSpace:\", \"[variable a] [operator @] [variable b]\");\n  MT(\"matmulWithoutSpace:\", \"[variable a][operator @][variable b]\");\n  MT(\"matmulSpaceBefore:\", \"[variable a] [operator @][variable b]\");\n  var before_equal_sign = [\"+\", \"-\", \"*\", \"/\", \"=\", \"!\", \">\", \"<\"];\n  for (var i = 0; i < before_equal_sign.length; ++i) {\n    var c = before_equal_sign[i]\n    MT(\"before_equal_sign_\" + c, \"[variable a] [operator \" + c + \"=] [variable b]\");\n  }\n\n  MT(\"fValidStringPrefix\", \"[string f'this is a]{[variable formatted]}[string string']\");\n  MT(\"fValidExpressionInFString\", \"[string f'expression ]{[number 100][operator *][number 5]}[string string']\");\n  MT(\"fInvalidFString\", \"[error f'this is wrong}]\");\n  MT(\"fNestedFString\", \"[string f'expression ]{[number 100] [operator +] [string f'inner]{[number 5]}[string ']}[string string']\");\n  MT(\"uValidStringPrefix\", \"[string u'this is an unicode string']\");\n\n  MT(\"nestedString\", \"[string f']{[variable b][[ [string \\\"c\\\"] ]]}[string f'] [comment # oops]\")\n\n  MT(\"bracesInFString\", \"[string f']{[variable x] [operator +] {}}[string !']\")\n\n  MT(\"nestedFString\", \"[string f']{[variable b][[ [string f\\\"c\\\"] ]]}[string f'] [comment # oops]\")\n\n  MT(\"dontIndentTypeDecl\",\n     \"[variable i]: [builtin int] [operator =] [number 32]\",\n     \"[builtin print]([variable i])\")\n\n  MT(\"dedentElse\",\n     \"[keyword if] [variable x]:\",\n     \"    [variable foo]()\",\n     \"[keyword elif] [variable y]:\",\n     \"    [variable bar]()\",\n     \"[keyword else]:\",\n     \"    [variable baz]()\")\n\n  MT(\"dedentElsePass\",\n     \"[keyword if] [variable x]:\",\n     \"    [keyword pass]\",\n     \"[keyword elif] [variable y]:\",\n     \"    [keyword pass]\",\n     \"[keyword else]:\",\n     \"    [keyword pass]\")\n\n  MT(\"dedentElseInFunction\",\n     \"[keyword def] [def foo]():\",\n     \"    [keyword if] [variable x]:\",\n     \"        [variable foo]()\",\n     \"    [keyword elif] [variable y]:\",\n     \"        [variable bar]()\",\n     \"        [keyword pass]\",\n     \"    [keyword else]:\",\n     \"        [variable baz]()\")\n\n  MT(\"dedentCase\",\n     \"[keyword match] [variable x]:\",\n     \"    [keyword case] [variable y]:\",\n     \"        [variable foo]()\")\n  MT(\"dedentCasePass\",\n     \"[keyword match] [variable x]:\",\n     \"    [keyword case] [variable y]:\",\n     \"        [keyword pass]\")\n\n  MT(\"dedentCaseInFunction\",\n     \"[keyword def] [def foo]():\",\n     \"    [keyword match] [variable x]:\",\n     \"        [keyword case] [variable y]:\",\n     \"            [variable foo]()\")\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/q/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Q mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"q.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Q</a>\n  </ul>\n</div>\n\n<article>\n<h2>Q mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n/ utilities to quickly load a csv file - for more exhaustive analysis of the csv contents see csvguess.q\n/ 2009.09.20 - updated to match latest csvguess.q \n\n/ .csv.colhdrs[file] - return a list of colhdrs from file\n/ info:.csv.info[file] - return a table of information about the file\n/ columns are: \n/\tc - column name; ci - column index; t - load type; mw - max width; \n/\tdchar - distinct characters in values; rule - rule that caught the type\n/\tmaybe - needs checking, _could_ be say a date, but perhaps just a float?\n/ .csv.info0[file;onlycols] - like .csv.info except that it only analyses <onlycols>\n/ example:\n/\tinfo:.csv.info0[file;(.csv.colhdrs file)like\"*price\"]\n/\tinfo:.csv.infolike[file;\"*price\"]\n/\tshow delete from info where t=\" \"\n/ .csv.data[file;info] - use the info from .csv.info to read the data\n/ .csv.data10[file;info] - like .csv.data but only returns the first 10 rows\n/ bulkload[file;info] - bulk loads file into table DATA (which must be already defined :: DATA:() )\n/ .csv.read[file]/read10[file] - for when you don't care about checking/tweaking the <info> before reading \n\n\\d .csv\nDELIM:\",\"\nZAPHDRS:0b / lowercase and remove _ from colhdrs (junk characters are always removed)\nWIDTHHDR:25000 / number of characters read to get the header\nREADLINES:222 / number of lines read and used to guess the types\nSYMMAXWIDTH:11 / character columns narrower than this are stored as symbols\nSYMMAXGR:10 / max symbol granularity% before we give up and keep as a * string\nFORCECHARWIDTH:30 / every field (of any type) with values this wide or more is forced to character \"*\"\nDISCARDEMPTY:0b / completely ignore empty columns if true else set them to \"C\"\nCHUNKSIZE:50000000 / used in fs2 (modified .Q.fs)\n\nk)nameltrim:{$[~@x;.z.s'x;~(*x)in aA:.Q.a,.Q.A;(+/&\\~x in aA)_x;x]}\nk)fs2:{[f;s]((-7!s)>){[f;s;x]i:1+last@&0xa=r:1:(s;x;CHUNKSIZE);f@`\\:i#r;x+i}[f;s]/0j}\ncleanhdrs:{{$[ZAPHDRS;lower x except\"_\";x]}x where x in DELIM,.Q.an}\ncancast:{nw:x$\"\";if[not x in\"BXCS\";nw:(min 0#;max 0#;::)@\\:nw];$[not any nw in x$(11&count y)#y;$[11<count y;not any nw in x$y;1b];0b]}\n\nread:{[file]data[file;info[file]]}  \nread10:{[file]data10[file;info[file]]}  \n\ncolhdrs:{[file]\n\t`$nameltrim DELIM vs cleanhdrs first read0(file;0;1+first where 0xa=read1(file;0;WIDTHHDR))}\ndata:{[file;info]\n\t(exec c from info where not t=\" \")xcol(exec t from info;enlist DELIM)0:file}\ndata10:{[file;info]\n\tdata[;info](file;0;1+last 11#where 0xa=read1(file;0;15*WIDTHHDR))}\ninfo0:{[file;onlycols]\n\tcolhdrs:`$nameltrim DELIM vs cleanhdrs first head:read0(file;0;1+last where 0xa=read1(file;0;WIDTHHDR));\n\tloadfmts:(count colhdrs)#\"S\";if[count onlycols;loadfmts[where not colhdrs in onlycols]:\"C\"];\n\tbreaks:where 0xa=read1(file;0;floor(10+READLINES)*WIDTHHDR%count head);\n\tnas:count as:colhdrs xcol(loadfmts;enlist DELIM)0:(file;0;1+last((1+READLINES)&count breaks)#breaks);\n\tinfo:([]c:key flip as;v:value flip as);as:();\n\treserved:key`.q;reserved,:.Q.res;reserved,:`i;\n\tinfo:update res:c in reserved from info;\n\tinfo:update ci:i,t:\"?\",ipa:0b,mdot:0,mw:0,rule:0,gr:0,ndv:0,maybe:0b,empty:0b,j10:0b,j12:0b from info;\n\tinfo:update ci:`s#ci from info;\n\tif[count onlycols;info:update t:\" \",rule:10 from info where not c in onlycols];\n\tinfo:update sdv:{string(distinct x)except`}peach v from info; \n\tinfo:update ndv:count each sdv from info;\n\tinfo:update gr:floor 0.5+100*ndv%nas,mw:{max count each x}peach sdv from info where 0<ndv;\n\tinfo:update t:\"*\",rule:20 from info where mw>.csv.FORCECHARWIDTH; / long values\n\tinfo:update t:\"C \"[.csv.DISCARDEMPTY],rule:30,empty:1b from info where t=\"?\",mw=0; / empty columns\n\tinfo:update dchar:{asc distinct raze x}peach sdv from info where t=\"?\";\n\tinfo:update mdot:{max sum each\".\"=x}peach sdv from info where t=\"?\",{\".\"in x}each dchar;\n\tinfo:update t:\"n\",rule:40 from info where t=\"?\",{any x in\"0123456789\"}each dchar; / vaguely numeric..\n\tinfo:update t:\"I\",rule:50,ipa:1b from info where t=\"n\",mw within 7 15,mdot=3,{all x in\".0123456789\"}each dchar,.csv.cancast[\"I\"]peach sdv; / ip-address\n\tinfo:update t:\"J\",rule:60 from info where t=\"n\",mdot=0,{all x in\"+-0123456789\"}each dchar,.csv.cancast[\"J\"]peach sdv;\n\tinfo:update t:\"I\",rule:70 from info where t=\"J\",mw<12,.csv.cancast[\"I\"]peach sdv;\n\tinfo:update t:\"H\",rule:80 from info where t=\"I\",mw<7,.csv.cancast[\"H\"]peach sdv;\n\tinfo:update t:\"F\",rule:90 from info where t=\"n\",mdot<2,mw>1,.csv.cancast[\"F\"]peach sdv;\n\tinfo:update t:\"E\",rule:100,maybe:1b from info where t=\"F\",mw<9;\n\tinfo:update t:\"M\",rule:110,maybe:1b from info where t in\"nIHEF\",mdot<2,mw within 4 7,.csv.cancast[\"M\"]peach sdv; \n\tinfo:update t:\"D\",rule:120,maybe:1b from info where t in\"nI\",mdot in 0 2,mw within 6 11,.csv.cancast[\"D\"]peach sdv; \n\tinfo:update t:\"V\",rule:130,maybe:1b from info where t=\"I\",mw in 5 6,7<count each dchar,{all x like\"*[0-9][0-5][0-9][0-5][0-9]\"}peach sdv,.csv.cancast[\"V\"]peach sdv; / 235959 12345        \n\tinfo:update t:\"U\",rule:140,maybe:1b from info where t=\"H\",mw in 3 4,7<count each dchar,{all x like\"*[0-9][0-5][0-9]\"}peach sdv,.csv.cancast[\"U\"]peach sdv; /2359\n\tinfo:update t:\"U\",rule:150,maybe:0b from info where t=\"n\",mw in 4 5,mdot=0,{all x like\"*[0-9]:[0-5][0-9]\"}peach sdv,.csv.cancast[\"U\"]peach sdv;\n\tinfo:update t:\"T\",rule:160,maybe:0b from info where t=\"n\",mw within 7 12,mdot<2,{all x like\"*[0-9]:[0-5][0-9]:[0-5][0-9]*\"}peach sdv,.csv.cancast[\"T\"]peach sdv;\n\tinfo:update t:\"V\",rule:170,maybe:0b from info where t=\"T\",mw in 7 8,mdot=0,.csv.cancast[\"V\"]peach sdv;\n\tinfo:update t:\"T\",rule:180,maybe:1b from info where t in\"EF\",mw within 7 10,mdot=1,{all x like\"*[0-9][0-5][0-9][0-5][0-9].*\"}peach sdv,.csv.cancast[\"T\"]peach sdv;\n\tinfo:update t:\"Z\",rule:190,maybe:0b from info where t=\"n\",mw within 11 24,mdot<4,.csv.cancast[\"Z\"]peach sdv;\n\tinfo:update t:\"P\",rule:200,maybe:1b from info where t=\"n\",mw within 12 29,mdot<4,{all x like\"[12]*\"}peach sdv,.csv.cancast[\"P\"]peach sdv;\n\tinfo:update t:\"N\",rule:210,maybe:1b from info where t=\"n\",mw within 3 28,mdot=1,.csv.cancast[\"N\"]peach sdv;\n\tinfo:update t:\"?\",rule:220,maybe:0b from info where t=\"n\"; / reset remaining maybe numeric\n\tinfo:update t:\"C\",rule:230,maybe:0b from info where t=\"?\",mw=1; / char\n\tinfo:update t:\"B\",rule:240,maybe:0b from info where t in\"HC\",mw=1,mdot=0,{$[all x in\"01tTfFyYnN\";(any\"0fFnN\"in x)and any\"1tTyY\"in x;0b]}each dchar; / boolean\n\tinfo:update t:\"B\",rule:250,maybe:1b from info where t in\"HC\",mw=1,mdot=0,{all x in\"01tTfFyYnN\"}each dchar; / boolean\n\tinfo:update t:\"X\",rule:260,maybe:0b from info where t=\"?\",mw=2,{$[all x in\"0123456789abcdefABCDEF\";(any .Q.n in x)and any\"abcdefABCDEF\"in x;0b]}each dchar; /hex\n\tinfo:update t:\"S\",rule:270,maybe:1b from info where t=\"?\",mw<.csv.SYMMAXWIDTH,mw>1,gr<.csv.SYMMAXGR; / symbols (max width permitting)\n\tinfo:update t:\"*\",rule:280,maybe:0b from info where t=\"?\"; / the rest as strings\n\t/ flag those S/* columns which could be encoded to integers (.Q.j10/x10/j12/x12) to avoid symbols\n\tinfo:update j12:1b from info where t in\"S*\",mw<13,{all x in .Q.nA}each dchar;\n\tinfo:update j10:1b from info where t in\"S*\",mw<11,{all x in .Q.b6}each dchar; \n\tselect c,ci,t,maybe,empty,res,j10,j12,ipa,mw,mdot,rule,gr,ndv,dchar from info}\ninfo:info0[;()] / by default don't restrict columns\ninfolike:{[file;pattern] info0[file;{x where x like y}[lower colhdrs[file];pattern]]} / .csv.infolike[file;\"*time\"]\n\n\\d .\n/ DATA:()\nbulkload:{[file;info]\n\tif[not`DATA in system\"v\";'`DATA.not.defined];\n\tif[count DATA;'`DATA.not.empty];\n\tloadhdrs:exec c from info where not t=\" \";loadfmts:exec t from info;\n\t.csv.fs2[{[file;loadhdrs;loadfmts] `DATA insert $[count DATA;flip loadhdrs!(loadfmts;.csv.DELIM)0:file;loadhdrs xcol(loadfmts;enlist .csv.DELIM)0:file]}[file;loadhdrs;loadfmts]];\n\tcount DATA}\n@[.:;\"\\\\l csvutil.custom.q\";::]; / save your custom settings in csvutil.custom.q to override those set at the beginning of the file \n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true\n      });\n    </script>\n\n    <p><strong>MIME type defined:</strong> <code>text/x-q</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/q/q.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"q\",function(config){\n  var indentUnit=config.indentUnit,\n      curPunc,\n      keywords=buildRE([\"abs\",\"acos\",\"aj\",\"aj0\",\"all\",\"and\",\"any\",\"asc\",\"asin\",\"asof\",\"atan\",\"attr\",\"avg\",\"avgs\",\"bin\",\"by\",\"ceiling\",\"cols\",\"cor\",\"cos\",\"count\",\"cov\",\"cross\",\"csv\",\"cut\",\"delete\",\"deltas\",\"desc\",\"dev\",\"differ\",\"distinct\",\"div\",\"do\",\"each\",\"ej\",\"enlist\",\"eval\",\"except\",\"exec\",\"exit\",\"exp\",\"fby\",\"fills\",\"first\",\"fkeys\",\"flip\",\"floor\",\"from\",\"get\",\"getenv\",\"group\",\"gtime\",\"hclose\",\"hcount\",\"hdel\",\"hopen\",\"hsym\",\"iasc\",\"idesc\",\"if\",\"ij\",\"in\",\"insert\",\"inter\",\"inv\",\"key\",\"keys\",\"last\",\"like\",\"list\",\"lj\",\"load\",\"log\",\"lower\",\"lsq\",\"ltime\",\"ltrim\",\"mavg\",\"max\",\"maxs\",\"mcount\",\"md5\",\"mdev\",\"med\",\"meta\",\"min\",\"mins\",\"mmax\",\"mmin\",\"mmu\",\"mod\",\"msum\",\"neg\",\"next\",\"not\",\"null\",\"or\",\"over\",\"parse\",\"peach\",\"pj\",\"plist\",\"prd\",\"prds\",\"prev\",\"prior\",\"rand\",\"rank\",\"ratios\",\"raze\",\"read0\",\"read1\",\"reciprocal\",\"reverse\",\"rload\",\"rotate\",\"rsave\",\"rtrim\",\"save\",\"scan\",\"select\",\"set\",\"setenv\",\"show\",\"signum\",\"sin\",\"sqrt\",\"ss\",\"ssr\",\"string\",\"sublist\",\"sum\",\"sums\",\"sv\",\"system\",\"tables\",\"tan\",\"til\",\"trim\",\"txf\",\"type\",\"uj\",\"ungroup\",\"union\",\"update\",\"upper\",\"upsert\",\"value\",\"var\",\"view\",\"views\",\"vs\",\"wavg\",\"where\",\"where\",\"while\",\"within\",\"wj\",\"wj1\",\"wsum\",\"xasc\",\"xbar\",\"xcol\",\"xcols\",\"xdesc\",\"xexp\",\"xgroup\",\"xkey\",\"xlog\",\"xprev\",\"xrank\"]),\n      E=/[|/&^!+:\\\\\\-*%$=~#;@><,?_\\'\\\"\\[\\(\\]\\)\\s{}]/;\n  function buildRE(w){return new RegExp(\"^(\"+w.join(\"|\")+\")$\");}\n  function tokenBase(stream,state){\n    var sol=stream.sol(),c=stream.next();\n    curPunc=null;\n    if(sol)\n      if(c==\"/\")\n        return(state.tokenize=tokenLineComment)(stream,state);\n      else if(c==\"\\\\\"){\n        if(stream.eol()||/\\s/.test(stream.peek()))\n          return stream.skipToEnd(),/^\\\\\\s*$/.test(stream.current())?(state.tokenize=tokenCommentToEOF)(stream):state.tokenize=tokenBase,\"comment\";\n        else\n          return state.tokenize=tokenBase,\"builtin\";\n      }\n    if(/\\s/.test(c))\n      return stream.peek()==\"/\"?(stream.skipToEnd(),\"comment\"):\"whitespace\";\n    if(c=='\"')\n      return(state.tokenize=tokenString)(stream,state);\n    if(c=='`')\n      return stream.eatWhile(/[A-Za-z\\d_:\\/.]/),\"symbol\";\n    if((\".\"==c&&/\\d/.test(stream.peek()))||/\\d/.test(c)){\n      var t=null;\n      stream.backUp(1);\n      if(stream.match(/^\\d{4}\\.\\d{2}(m|\\.\\d{2}([DT](\\d{2}(:\\d{2}(:\\d{2}(\\.\\d{1,9})?)?)?)?)?)/)\n      || stream.match(/^\\d+D(\\d{2}(:\\d{2}(:\\d{2}(\\.\\d{1,9})?)?)?)/)\n      || stream.match(/^\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,9})?)?/)\n      || stream.match(/^\\d+[ptuv]{1}/))\n        t=\"temporal\";\n      else if(stream.match(/^0[NwW]{1}/)\n      || stream.match(/^0x[\\da-fA-F]*/)\n      || stream.match(/^[01]+[b]{1}/)\n      || stream.match(/^\\d+[chijn]{1}/)\n      || stream.match(/-?\\d*(\\.\\d*)?(e[+\\-]?\\d+)?(e|f)?/))\n        t=\"number\";\n      return(t&&(!(c=stream.peek())||E.test(c)))?t:(stream.next(),\"error\");\n    }\n    if(/[A-Za-z]|\\./.test(c))\n      return stream.eatWhile(/[A-Za-z._\\d]/),keywords.test(stream.current())?\"keyword\":\"variable\";\n    if(/[|/&^!+:\\\\\\-*%$=~#;@><\\.,?_\\']/.test(c))\n      return null;\n    if(/[{}\\(\\[\\]\\)]/.test(c))\n      return null;\n    return\"error\";\n  }\n  function tokenLineComment(stream,state){\n    return stream.skipToEnd(),/\\/\\s*$/.test(stream.current())?(state.tokenize=tokenBlockComment)(stream,state):(state.tokenize=tokenBase),\"comment\";\n  }\n  function tokenBlockComment(stream,state){\n    var f=stream.sol()&&stream.peek()==\"\\\\\";\n    stream.skipToEnd();\n    if(f&&/^\\\\\\s*$/.test(stream.current()))\n      state.tokenize=tokenBase;\n    return\"comment\";\n  }\n  function tokenCommentToEOF(stream){return stream.skipToEnd(),\"comment\";}\n  function tokenString(stream,state){\n    var escaped=false,next,end=false;\n    while((next=stream.next())){\n      if(next==\"\\\"\"&&!escaped){end=true;break;}\n      escaped=!escaped&&next==\"\\\\\";\n    }\n    if(end)state.tokenize=tokenBase;\n    return\"string\";\n  }\n  function pushContext(state,type,col){state.context={prev:state.context,indent:state.indent,col:col,type:type};}\n  function popContext(state){state.indent=state.context.indent;state.context=state.context.prev;}\n  return{\n    startState:function(){\n      return{tokenize:tokenBase,\n             context:null,\n             indent:0,\n             col:0};\n    },\n    token:function(stream,state){\n      if(stream.sol()){\n        if(state.context&&state.context.align==null)\n          state.context.align=false;\n        state.indent=stream.indentation();\n      }\n      //if (stream.eatSpace()) return null;\n      var style=state.tokenize(stream,state);\n      if(style!=\"comment\"&&state.context&&state.context.align==null&&state.context.type!=\"pattern\"){\n        state.context.align=true;\n      }\n      if(curPunc==\"(\")pushContext(state,\")\",stream.column());\n      else if(curPunc==\"[\")pushContext(state,\"]\",stream.column());\n      else if(curPunc==\"{\")pushContext(state,\"}\",stream.column());\n      else if(/[\\]\\}\\)]/.test(curPunc)){\n        while(state.context&&state.context.type==\"pattern\")popContext(state);\n        if(state.context&&curPunc==state.context.type)popContext(state);\n      }\n      else if(curPunc==\".\"&&state.context&&state.context.type==\"pattern\")popContext(state);\n      else if(/atom|string|variable/.test(style)&&state.context){\n        if(/[\\}\\]]/.test(state.context.type))\n          pushContext(state,\"pattern\",stream.column());\n        else if(state.context.type==\"pattern\"&&!state.context.align){\n          state.context.align=true;\n          state.context.col=stream.column();\n        }\n      }\n      return style;\n    },\n    indent:function(state,textAfter){\n      var firstChar=textAfter&&textAfter.charAt(0);\n      var context=state.context;\n      if(/[\\]\\}]/.test(firstChar))\n        while (context&&context.type==\"pattern\")context=context.prev;\n      var closing=context&&firstChar==context.type;\n      if(!context)\n        return 0;\n      else if(context.type==\"pattern\")\n        return context.col;\n      else if(context.align)\n        return context.col+(closing?0:1);\n      else\n        return context.indent+(closing?0:indentUnit);\n    }\n  };\n});\nCodeMirror.defineMIME(\"text/x-q\",\"q\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/r/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: R mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"r.js\"></script>\n<style>\n      .CodeMirror { border-top: 1px solid silver; border-bottom: 1px solid silver; }\n      .cm-s-default span.cm-semi { color: blue; font-weight: bold; }\n      .cm-s-default span.cm-dollar { color: orange; font-weight: bold; }\n      .cm-s-default span.cm-arrow { color: brown; }\n      .cm-s-default span.cm-arg-is { color: brown; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">R</a>\n  </ul>\n</div>\n\n<article>\n<h2>R mode</h2>\n<form><textarea id=\"code\" name=\"code\">\nX <- list(height = 5.4, weight = 54)\ncat(\"Printing objects: \"); print(X)\nprint(\"Accessing individual elements:\")\ncat(sprintf(\"Your height is %s and your weight is %s\\n\", X$height, X$weight))\n\n# Functions:\nsquare <- function(x) {\n  return(x * x)\n}\ncat(sprintf(\"The square of 3 is %s\\n\", square(3)))\n\n# In R, the last expression in a function is, by default, what is\n# returned. The idiomatic way to write the function is:\nsquare <- function(x) {\n  x * x\n}\n# or, for functions with short content:\nsquare <- function(x) x * x\n\n# Function arguments with default values:\ncube <- function(x = 5) x * x * x\ncat(sprintf(\"Calling cube with 2 : %s\\n\", cube(2))  # will give 2^3\ncat(sprintf(\"Calling cube        : %s\\n\", cube())   # will default to 5^3.\n\npowers <- function(x) list(x2 = x*x, x3 = x*x*x, x4 = x*x*x*x)\n\ncat(\"Powers of 3: \"); print(powers(3))\n\n# Data frames\ndf <- data.frame(letters = letters[1:5], '#letter' = 1:5)\nprint(df$letters)\nprint(df$`#letter`)\n\n# Operators:\nm1 <- matrix(1:6, 2, 3)\nm2 <- m1 %*% t(m1)\ncat(\"Matrix product: \"); print(m2)\n\n# Assignments:\na <- 1\nb <<- 2\nc = 3\n4 -> d\n5 ->> e\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-rsrc</code>.</p>\n\n    <p>Development of the CodeMirror R mode was kindly sponsored\n    by <a href=\"https://twitter.com/ubalo\">Ubalo</a>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/r/r.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.registerHelper(\"wordChars\", \"r\", /[\\w.]/);\n\nCodeMirror.defineMode(\"r\", function(config) {\n  function wordObj(words) {\n    var res = {};\n    for (var i = 0; i < words.length; ++i) res[words[i]] = true;\n    return res;\n  }\n  var commonAtoms = [\"NULL\", \"NA\", \"Inf\", \"NaN\", \"NA_integer_\", \"NA_real_\", \"NA_complex_\", \"NA_character_\", \"TRUE\", \"FALSE\"];\n  var commonBuiltins = [\"list\", \"quote\", \"bquote\", \"eval\", \"return\", \"call\", \"parse\", \"deparse\"];\n  var commonKeywords = [\"if\", \"else\", \"repeat\", \"while\", \"function\", \"for\", \"in\", \"next\", \"break\"];\n  var commonBlockKeywords = [\"if\", \"else\", \"repeat\", \"while\", \"function\", \"for\"];\n\n  CodeMirror.registerHelper(\"hintWords\", \"r\", commonAtoms.concat(commonBuiltins, commonKeywords));\n\n  var atoms = wordObj(commonAtoms);\n  var builtins = wordObj(commonBuiltins);\n  var keywords = wordObj(commonKeywords);\n  var blockkeywords = wordObj(commonBlockKeywords);\n  var opChars = /[+\\-*\\/^<>=!&|~$:]/;\n  var curPunc;\n\n  function tokenBase(stream, state) {\n    curPunc = null;\n    var ch = stream.next();\n    if (ch == \"#\") {\n      stream.skipToEnd();\n      return \"comment\";\n    } else if (ch == \"0\" && stream.eat(\"x\")) {\n      stream.eatWhile(/[\\da-f]/i);\n      return \"number\";\n    } else if (ch == \".\" && stream.eat(/\\d/)) {\n      stream.match(/\\d*(?:e[+\\-]?\\d+)?/);\n      return \"number\";\n    } else if (/\\d/.test(ch)) {\n      stream.match(/\\d*(?:\\.\\d+)?(?:e[+\\-]\\d+)?L?/);\n      return \"number\";\n    } else if (ch == \"'\" || ch == '\"') {\n      state.tokenize = tokenString(ch);\n      return \"string\";\n    } else if (ch == \"`\") {\n      stream.match(/[^`]+`/);\n      return \"variable-3\";\n    } else if (ch == \".\" && stream.match(/.(?:[.]|\\d+)/)) {\n      return \"keyword\";\n    } else if (/[a-zA-Z\\.]/.test(ch)) {\n      stream.eatWhile(/[\\w\\.]/);\n      var word = stream.current();\n      if (atoms.propertyIsEnumerable(word)) return \"atom\";\n      if (keywords.propertyIsEnumerable(word)) {\n        // Block keywords start new blocks, except 'else if', which only starts\n        // one new block for the 'if', no block for the 'else'.\n        if (blockkeywords.propertyIsEnumerable(word) &&\n            !stream.match(/\\s*if(\\s+|$)/, false))\n          curPunc = \"block\";\n        return \"keyword\";\n      }\n      if (builtins.propertyIsEnumerable(word)) return \"builtin\";\n      return \"variable\";\n    } else if (ch == \"%\") {\n      if (stream.skipTo(\"%\")) stream.next();\n      return \"operator variable-2\";\n    } else if (\n        (ch == \"<\" && stream.eat(\"-\")) ||\n        (ch == \"<\" && stream.match(\"<-\")) ||\n        (ch == \"-\" && stream.match(/>>?/))\n      ) {\n      return \"operator arrow\";\n    } else if (ch == \"=\" && state.ctx.argList) {\n      return \"arg-is\";\n    } else if (opChars.test(ch)) {\n      if (ch == \"$\") return \"operator dollar\";\n      stream.eatWhile(opChars);\n      return \"operator\";\n    } else if (/[\\(\\){}\\[\\];]/.test(ch)) {\n      curPunc = ch;\n      if (ch == \";\") return \"semi\";\n      return null;\n    } else {\n      return null;\n    }\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      if (stream.eat(\"\\\\\")) {\n        var ch = stream.next();\n        if (ch == \"x\") stream.match(/^[a-f0-9]{2}/i);\n        else if ((ch == \"u\" || ch == \"U\") && stream.eat(\"{\") && stream.skipTo(\"}\")) stream.next();\n        else if (ch == \"u\") stream.match(/^[a-f0-9]{4}/i);\n        else if (ch == \"U\") stream.match(/^[a-f0-9]{8}/i);\n        else if (/[0-7]/.test(ch)) stream.match(/^[0-7]{1,2}/);\n        return \"string-2\";\n      } else {\n        var next;\n        while ((next = stream.next()) != null) {\n          if (next == quote) { state.tokenize = tokenBase; break; }\n          if (next == \"\\\\\") { stream.backUp(1); break; }\n        }\n        return \"string\";\n      }\n    };\n  }\n\n  var ALIGN_YES = 1, ALIGN_NO = 2, BRACELESS = 4\n\n  function push(state, type, stream) {\n    state.ctx = {type: type,\n                 indent: state.indent,\n                 flags: 0,\n                 column: stream.column(),\n                 prev: state.ctx};\n  }\n  function setFlag(state, flag) {\n    var ctx = state.ctx\n    state.ctx = {type: ctx.type,\n                 indent: ctx.indent,\n                 flags: ctx.flags | flag,\n                 column: ctx.column,\n                 prev: ctx.prev}\n  }\n  function pop(state) {\n    state.indent = state.ctx.indent;\n    state.ctx = state.ctx.prev;\n  }\n\n  return {\n    startState: function() {\n      return {tokenize: tokenBase,\n              ctx: {type: \"top\",\n                    indent: -config.indentUnit,\n                    flags: ALIGN_NO},\n              indent: 0,\n              afterIdent: false};\n    },\n\n    token: function(stream, state) {\n      if (stream.sol()) {\n        if ((state.ctx.flags & 3) == 0) state.ctx.flags |= ALIGN_NO\n        if (state.ctx.flags & BRACELESS) pop(state)\n        state.indent = stream.indentation();\n      }\n      if (stream.eatSpace()) return null;\n      var style = state.tokenize(stream, state);\n      if (style != \"comment\" && (state.ctx.flags & ALIGN_NO) == 0) setFlag(state, ALIGN_YES)\n\n      if ((curPunc == \";\" || curPunc == \"{\" || curPunc == \"}\") && state.ctx.type == \"block\") pop(state);\n      if (curPunc == \"{\") push(state, \"}\", stream);\n      else if (curPunc == \"(\") {\n        push(state, \")\", stream);\n        if (state.afterIdent) state.ctx.argList = true;\n      }\n      else if (curPunc == \"[\") push(state, \"]\", stream);\n      else if (curPunc == \"block\") push(state, \"block\", stream);\n      else if (curPunc == state.ctx.type) pop(state);\n      else if (state.ctx.type == \"block\" && style != \"comment\") setFlag(state, BRACELESS)\n      state.afterIdent = style == \"variable\" || style == \"keyword\";\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize != tokenBase) return 0;\n      var firstChar = textAfter && textAfter.charAt(0), ctx = state.ctx,\n          closing = firstChar == ctx.type;\n      if (ctx.flags & BRACELESS) ctx = ctx.prev\n      if (ctx.type == \"block\") return ctx.indent + (firstChar == \"{\" ? 0 : config.indentUnit);\n      else if (ctx.flags & ALIGN_YES) return ctx.column + (closing ? 0 : 1);\n      else return ctx.indent + (closing ? 0 : config.indentUnit);\n    },\n\n    lineComment: \"#\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-rsrc\", \"r\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/rpm/changes/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: RPM changes mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n    <link rel=\"stylesheet\" href=\"../../../lib/codemirror.css\">\n    <script src=\"../../../lib/codemirror.js\"></script>\n    <script src=\"changes.js\"></script>\n    <link rel=\"stylesheet\" href=\"../../../doc/docs.css\">\n    <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../../../index.html\">Home</a>\n    <li><a href=\"../../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">RPM changes</a>\n  </ul>\n</div>\n\n<article>\n<h2>RPM changes mode</h2>\n\n    <div><textarea id=\"code\" name=\"code\">\n-------------------------------------------------------------------\nTue Oct 18 13:58:40 UTC 2011 - misterx@example.com\n\n- Update to r60.3\n- Fixes bug in the reflect package\n  * disallow Interface method on Value obtained via unexported name\n\n-------------------------------------------------------------------\nThu Oct  6 08:14:24 UTC 2011 - misterx@example.com\n\n- Update to r60.2\n- Fixes memory leak in certain map types\n\n-------------------------------------------------------------------\nWed Oct  5 14:34:10 UTC 2011 - misterx@example.com\n\n- Tweaks for gdb debugging\n- go.spec changes:\n  - move %go_arch definition to %prep section\n  - pass correct location of go specific gdb pretty printer and\n    functions to cpp as HOST_EXTRA_CFLAGS macro\n  - install go gdb functions & printer\n- gdb-printer.patch\n  - patch linker (src/cmd/ld/dwarf.c) to emit correct location of go\n    gdb functions and pretty printer\n</textarea></div>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: {name: \"changes\"},\n        lineNumbers: true,\n        indentUnit: 4\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-rpm-changes</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/rpm/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: RPM changes mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n    <link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n    <script src=\"../../lib/codemirror.js\"></script>\n    <script src=\"rpm.js\"></script>\n    <link rel=\"stylesheet\" href=\"../../doc/docs.css\">\n    <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">RPM</a>\n  </ul>\n</div>\n\n<article>\n<h2>RPM changes mode</h2>\n\n    <div><textarea id=\"code\" name=\"code\">\n-------------------------------------------------------------------\nTue Oct 18 13:58:40 UTC 2011 - misterx@example.com\n\n- Update to r60.3\n- Fixes bug in the reflect package\n  * disallow Interface method on Value obtained via unexported name\n\n-------------------------------------------------------------------\nThu Oct  6 08:14:24 UTC 2011 - misterx@example.com\n\n- Update to r60.2\n- Fixes memory leak in certain map types\n\n-------------------------------------------------------------------\nWed Oct  5 14:34:10 UTC 2011 - misterx@example.com\n\n- Tweaks for gdb debugging\n- go.spec changes:\n  - move %go_arch definition to %prep section\n  - pass correct location of go specific gdb pretty printer and\n    functions to cpp as HOST_EXTRA_CFLAGS macro\n  - install go gdb functions & printer\n- gdb-printer.patch\n  - patch linker (src/cmd/ld/dwarf.c) to emit correct location of go\n    gdb functions and pretty printer\n</textarea></div>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: {name: \"rpm-changes\"},\n        lineNumbers: true,\n        indentUnit: 4\n      });\n    </script>\n\n<h2>RPM spec mode</h2>\n    \n    <div><textarea id=\"code2\" name=\"code2\">\n#\n# spec file for package minidlna\n#\n# Copyright (c) 2011, Sascha Peilicke <saschpe@gmx.de>\n#\n# All modifications and additions to the file contributed by third parties\n# remain the property of their copyright owners, unless otherwise agreed\n# upon. The license for this file, and modifications and additions to the\n# file, is the same license as for the pristine package itself (unless the\n# license for the pristine package is not an Open Source License, in which\n# case the license is the MIT License). An \"Open Source License\" is a\n# license that conforms to the Open Source Definition (Version 1.9)\n# published by the Open Source Initiative.\n\n\nName:           libupnp6\nVersion:        1.6.13\nRelease:        0\nSummary:        Portable Universal Plug and Play (UPnP) SDK\nGroup:          System/Libraries\nLicense:        BSD-3-Clause\nUrl:            http://sourceforge.net/projects/pupnp/\nSource0:        http://downloads.sourceforge.net/pupnp/libupnp-%{version}.tar.bz2\nBuildRoot:      %{_tmppath}/%{name}-%{version}-build\n\n%description\nThe portable Universal Plug and Play (UPnP) SDK provides support for building\nUPnP-compliant control points, devices, and bridges on several operating\nsystems.\n\n%package -n libupnp-devel\nSummary:        Portable Universal Plug and Play (UPnP) SDK\nGroup:          Development/Libraries/C and C++\nProvides:       pkgconfig(libupnp)\nRequires:       %{name} = %{version}\n\n%description -n libupnp-devel\nThe portable Universal Plug and Play (UPnP) SDK provides support for building\nUPnP-compliant control points, devices, and bridges on several operating\nsystems.\n\n%prep\n%setup -n libupnp-%{version}\n\n%build\n%configure --disable-static\nmake %{?_smp_mflags}\n\n%install\n%makeinstall\nfind %{buildroot} -type f -name '*.la' -exec rm -f {} ';'\n\n%post -p /sbin/ldconfig\n\n%postun -p /sbin/ldconfig\n\n%files\n%defattr(-,root,root,-)\n%doc ChangeLog NEWS README TODO\n%{_libdir}/libixml.so.*\n%{_libdir}/libthreadutil.so.*\n%{_libdir}/libupnp.so.*\n\n%files -n libupnp-devel\n%defattr(-,root,root,-)\n%{_libdir}/pkgconfig/libupnp.pc\n%{_libdir}/libixml.so\n%{_libdir}/libthreadutil.so\n%{_libdir}/libupnp.so\n%{_includedir}/upnp/\n\n%changelog</textarea></div>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code2\"), {\n        mode: {name: \"rpm-spec\"},\n        lineNumbers: true,\n        indentUnit: 4\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-rpm-spec</code>, <code>text/x-rpm-changes</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/rpm/rpm.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"rpm-changes\", function() {\n  var headerSeparator = /^-+$/;\n  var headerLine = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)  ?\\d{1,2} \\d{2}:\\d{2}(:\\d{2})? [A-Z]{3,4} \\d{4} - /;\n  var simpleEmail = /^[\\w+.-]+@[\\w.-]+/;\n\n  return {\n    token: function(stream) {\n      if (stream.sol()) {\n        if (stream.match(headerSeparator)) { return 'tag'; }\n        if (stream.match(headerLine)) { return 'tag'; }\n      }\n      if (stream.match(simpleEmail)) { return 'string'; }\n      stream.next();\n      return null;\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-rpm-changes\", \"rpm-changes\");\n\n// Quick and dirty spec file highlighting\n\nCodeMirror.defineMode(\"rpm-spec\", function() {\n  var arch = /^(i386|i586|i686|x86_64|ppc64le|ppc64|ppc|ia64|s390x|s390|sparc64|sparcv9|sparc|noarch|alphaev6|alpha|hppa|mipsel)/;\n\n  var preamble = /^[a-zA-Z0-9()]+:/;\n  var section = /^%(debug_package|package|description|prep|build|install|files|clean|changelog|preinstall|preun|postinstall|postun|pretrans|posttrans|pre|post|triggerin|triggerun|verifyscript|check|triggerpostun|triggerprein|trigger)/;\n  var control_flow_complex = /^%(ifnarch|ifarch|if)/; // rpm control flow macros\n  var control_flow_simple = /^%(else|endif)/; // rpm control flow macros\n  var operators = /^(\\!|\\?|\\<\\=|\\<|\\>\\=|\\>|\\=\\=|\\&\\&|\\|\\|)/; // operators in control flow macros\n\n  return {\n    startState: function () {\n        return {\n          controlFlow: false,\n          macroParameters: false,\n          section: false\n        };\n    },\n    token: function (stream, state) {\n      var ch = stream.peek();\n      if (ch == \"#\") { stream.skipToEnd(); return \"comment\"; }\n\n      if (stream.sol()) {\n        if (stream.match(preamble)) { return \"header\"; }\n        if (stream.match(section)) { return \"atom\"; }\n      }\n\n      if (stream.match(/^\\$\\w+/)) { return \"def\"; } // Variables like '$RPM_BUILD_ROOT'\n      if (stream.match(/^\\$\\{\\w+\\}/)) { return \"def\"; } // Variables like '${RPM_BUILD_ROOT}'\n\n      if (stream.match(control_flow_simple)) { return \"keyword\"; }\n      if (stream.match(control_flow_complex)) {\n        state.controlFlow = true;\n        return \"keyword\";\n      }\n      if (state.controlFlow) {\n        if (stream.match(operators)) { return \"operator\"; }\n        if (stream.match(/^(\\d+)/)) { return \"number\"; }\n        if (stream.eol()) { state.controlFlow = false; }\n      }\n\n      if (stream.match(arch)) {\n        if (stream.eol()) { state.controlFlow = false; }\n        return \"number\";\n      }\n\n      // Macros like '%make_install' or '%attr(0775,root,root)'\n      if (stream.match(/^%[\\w]+/)) {\n        if (stream.match('(')) { state.macroParameters = true; }\n        return \"keyword\";\n      }\n      if (state.macroParameters) {\n        if (stream.match(/^\\d+/)) { return \"number\";}\n        if (stream.match(')')) {\n          state.macroParameters = false;\n          return \"keyword\";\n        }\n      }\n\n      // Macros like '%{defined fedora}'\n      if (stream.match(/^%\\{\\??[\\w \\-\\:\\!]+\\}/)) {\n        if (stream.eol()) { state.controlFlow = false; }\n        return \"def\";\n      }\n\n      //TODO: Include bash script sub-parser (CodeMirror supports that)\n      stream.next();\n      return null;\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-rpm-spec\", \"rpm-spec\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/rst/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: reStructuredText mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/mode/overlay.js\"></script>\n<script src=\"rst.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">reStructuredText</a>\n  </ul>\n</div>\n\n<article>\n<h2>reStructuredText mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n.. This is an excerpt from Sphinx documentation: http://sphinx.pocoo.org/_sources/rest.txt\n\n.. highlightlang:: rest\n\n.. _rst-primer:\n\nreStructuredText Primer\n=======================\n\nThis section is a brief introduction to reStructuredText (reST) concepts and\nsyntax, intended to provide authors with enough information to author documents\nproductively.  Since reST was designed to be a simple, unobtrusive markup\nlanguage, this will not take too long.\n\n.. seealso::\n\n   The authoritative `reStructuredText User Documentation\n   &lt;http://docutils.sourceforge.net/rst.html&gt;`_.  The \"ref\" links in this\n   document link to the description of the individual constructs in the reST\n   reference.\n\n\nParagraphs\n----------\n\nThe paragraph (:duref:`ref &lt;paragraphs&gt;`) is the most basic block in a reST\ndocument.  Paragraphs are simply chunks of text separated by one or more blank\nlines.  As in Python, indentation is significant in reST, so all lines of the\nsame paragraph must be left-aligned to the same level of indentation.\n\n\n.. _inlinemarkup:\n\nInline markup\n-------------\n\nThe standard reST inline markup is quite simple: use\n\n* one asterisk: ``*text*`` for emphasis (italics),\n* two asterisks: ``**text**`` for strong emphasis (boldface), and\n* backquotes: ````text```` for code samples.\n\nIf asterisks or backquotes appear in running text and could be confused with\ninline markup delimiters, they have to be escaped with a backslash.\n\nBe aware of some restrictions of this markup:\n\n* it may not be nested,\n* content may not start or end with whitespace: ``* text*`` is wrong,\n* it must be separated from surrounding text by non-word characters.  Use a\n  backslash escaped space to work around that: ``thisis\\ *one*\\ word``.\n\nThese restrictions may be lifted in future versions of the docutils.\n\nreST also allows for custom \"interpreted text roles\"', which signify that the\nenclosed text should be interpreted in a specific way.  Sphinx uses this to\nprovide semantic markup and cross-referencing of identifiers, as described in\nthe appropriate section.  The general syntax is ``:rolename:`content```.\n\nStandard reST provides the following roles:\n\n* :durole:`emphasis` -- alternate spelling for ``*emphasis*``\n* :durole:`strong` -- alternate spelling for ``**strong**``\n* :durole:`literal` -- alternate spelling for ````literal````\n* :durole:`subscript` -- subscript text\n* :durole:`superscript` -- superscript text\n* :durole:`title-reference` -- for titles of books, periodicals, and other\n  materials\n\nSee :ref:`inline-markup` for roles added by Sphinx.\n\n\nLists and Quote-like blocks\n---------------------------\n\nList markup (:duref:`ref &lt;bullet-lists&gt;`) is natural: just place an asterisk at\nthe start of a paragraph and indent properly.  The same goes for numbered lists;\nthey can also be autonumbered using a ``#`` sign::\n\n   * This is a bulleted list.\n   * It has two items, the second\n     item uses two lines.\n\n   1. This is a numbered list.\n   2. It has two items too.\n\n   #. This is a numbered list.\n   #. It has two items too.\n\n\nNested lists are possible, but be aware that they must be separated from the\nparent list items by blank lines::\n\n   * this is\n   * a list\n\n     * with a nested list\n     * and some subitems\n\n   * and here the parent list continues\n\nDefinition lists (:duref:`ref &lt;definition-lists&gt;`) are created as follows::\n\n   term (up to a line of text)\n      Definition of the term, which must be indented\n\n      and can even consist of multiple paragraphs\n\n   next term\n      Description.\n\nNote that the term cannot have more than one line of text.\n\nQuoted paragraphs (:duref:`ref &lt;block-quotes&gt;`) are created by just indenting\nthem more than the surrounding paragraphs.\n\nLine blocks (:duref:`ref &lt;line-blocks&gt;`) are a way of preserving line breaks::\n\n   | These lines are\n   | broken exactly like in\n   | the source file.\n\nThere are also several more special blocks available:\n\n* field lists (:duref:`ref &lt;field-lists&gt;`)\n* option lists (:duref:`ref &lt;option-lists&gt;`)\n* quoted literal blocks (:duref:`ref &lt;quoted-literal-blocks&gt;`)\n* doctest blocks (:duref:`ref &lt;doctest-blocks&gt;`)\n\n\nSource Code\n-----------\n\nLiteral code blocks (:duref:`ref &lt;literal-blocks&gt;`) are introduced by ending a\nparagraph with the special marker ``::``.  The literal block must be indented\n(and, like all paragraphs, separated from the surrounding ones by blank lines)::\n\n   This is a normal text paragraph. The next paragraph is a code sample::\n\n      It is not processed in any way, except\n      that the indentation is removed.\n\n      It can span multiple lines.\n\n   This is a normal text paragraph again.\n\nThe handling of the ``::`` marker is smart:\n\n* If it occurs as a paragraph of its own, that paragraph is completely left\n  out of the document.\n* If it is preceded by whitespace, the marker is removed.\n* If it is preceded by non-whitespace, the marker is replaced by a single\n  colon.\n\nThat way, the second sentence in the above example's first paragraph would be\nrendered as \"The next paragraph is a code sample:\".\n\n\n.. _rst-tables:\n\nTables\n------\n\nTwo forms of tables are supported.  For *grid tables* (:duref:`ref\n&lt;grid-tables&gt;`), you have to \"paint\" the cell grid yourself.  They look like\nthis::\n\n   +------------------------+------------+----------+----------+\n   | Header row, column 1   | Header 2   | Header 3 | Header 4 |\n   | (header rows optional) |            |          |          |\n   +========================+============+==========+==========+\n   | body row 1, column 1   | column 2   | column 3 | column 4 |\n   +------------------------+------------+----------+----------+\n   | body row 2             | ...        | ...      |          |\n   +------------------------+------------+----------+----------+\n\n*Simple tables* (:duref:`ref &lt;simple-tables&gt;`) are easier to write, but\nlimited: they must contain more than one row, and the first column cannot\ncontain multiple lines.  They look like this::\n\n   =====  =====  =======\n   A      B      A and B\n   =====  =====  =======\n   False  False  False\n   True   False  False\n   False  True   False\n   True   True   True\n   =====  =====  =======\n\n\nHyperlinks\n----------\n\nExternal links\n^^^^^^^^^^^^^^\n\nUse ```Link text &lt;http://example.com/&gt;`_`` for inline web links.  If the link\ntext should be the web address, you don't need special markup at all, the parser\nfinds links and mail addresses in ordinary text.\n\nYou can also separate the link and the target definition (:duref:`ref\n&lt;hyperlink-targets&gt;`), like this::\n\n   This is a paragraph that contains `a link`_.\n\n   .. _a link: http://example.com/\n\n\nInternal links\n^^^^^^^^^^^^^^\n\nInternal linking is done via a special reST role provided by Sphinx, see the\nsection on specific markup, :ref:`ref-role`.\n\n\nSections\n--------\n\nSection headers (:duref:`ref &lt;sections&gt;`) are created by underlining (and\noptionally overlining) the section title with a punctuation character, at least\nas long as the text::\n\n   =================\n   This is a heading\n   =================\n\nNormally, there are no heading levels assigned to certain characters as the\nstructure is determined from the succession of headings.  However, for the\nPython documentation, this convention is used which you may follow:\n\n* ``#`` with overline, for parts\n* ``*`` with overline, for chapters\n* ``=``, for sections\n* ``-``, for subsections\n* ``^``, for subsubsections\n* ``\"``, for paragraphs\n\nOf course, you are free to use your own marker characters (see the reST\ndocumentation), and use a deeper nesting level, but keep in mind that most\ntarget formats (HTML, LaTeX) have a limited supported nesting depth.\n\n\nExplicit Markup\n---------------\n\n\"Explicit markup\" (:duref:`ref &lt;explicit-markup-blocks&gt;`) is used in reST for\nmost constructs that need special handling, such as footnotes,\nspecially-highlighted paragraphs, comments, and generic directives.\n\nAn explicit markup block begins with a line starting with ``..`` followed by\nwhitespace and is terminated by the next paragraph at the same level of\nindentation.  (There needs to be a blank line between explicit markup and normal\nparagraphs.  This may all sound a bit complicated, but it is intuitive enough\nwhen you write it.)\n\n\n.. _directives:\n\nDirectives\n----------\n\nA directive (:duref:`ref &lt;directives&gt;`) is a generic block of explicit markup.\nBesides roles, it is one of the extension mechanisms of reST, and Sphinx makes\nheavy use of it.\n\nDocutils supports the following directives:\n\n* Admonitions: :dudir:`attention`, :dudir:`caution`, :dudir:`danger`,\n  :dudir:`error`, :dudir:`hint`, :dudir:`important`, :dudir:`note`,\n  :dudir:`tip`, :dudir:`warning` and the generic :dudir:`admonition`.\n  (Most themes style only \"note\" and \"warning\" specially.)\n\n* Images:\n\n  - :dudir:`image` (see also Images_ below)\n  - :dudir:`figure` (an image with caption and optional legend)\n\n* Additional body elements:\n\n  - :dudir:`contents` (a local, i.e. for the current file only, table of\n    contents)\n  - :dudir:`container` (a container with a custom class, useful to generate an\n    outer ``&lt;div&gt;`` in HTML)\n  - :dudir:`rubric` (a heading without relation to the document sectioning)\n  - :dudir:`topic`, :dudir:`sidebar` (special highlighted body elements)\n  - :dudir:`parsed-literal` (literal block that supports inline markup)\n  - :dudir:`epigraph` (a block quote with optional attribution line)\n  - :dudir:`highlights`, :dudir:`pull-quote` (block quotes with their own\n    class attribute)\n  - :dudir:`compound` (a compound paragraph)\n\n* Special tables:\n\n  - :dudir:`table` (a table with title)\n  - :dudir:`csv-table` (a table generated from comma-separated values)\n  - :dudir:`list-table` (a table generated from a list of lists)\n\n* Special directives:\n\n  - :dudir:`raw` (include raw target-format markup)\n  - :dudir:`include` (include reStructuredText from another file)\n    -- in Sphinx, when given an absolute include file path, this directive takes\n    it as relative to the source directory\n  - :dudir:`class` (assign a class attribute to the next element) [1]_\n\n* HTML specifics:\n\n  - :dudir:`meta` (generation of HTML ``&lt;meta&gt;`` tags)\n  - :dudir:`title` (override document title)\n\n* Influencing markup:\n\n  - :dudir:`default-role` (set a new default role)\n  - :dudir:`role` (create a new role)\n\n  Since these are only per-file, better use Sphinx' facilities for setting the\n  :confval:`default_role`.\n\nDo *not* use the directives :dudir:`sectnum`, :dudir:`header` and\n:dudir:`footer`.\n\nDirectives added by Sphinx are described in :ref:`sphinxmarkup`.\n\nBasically, a directive consists of a name, arguments, options and content. (Keep\nthis terminology in mind, it is used in the next chapter describing custom\ndirectives.)  Looking at this example, ::\n\n   .. function:: foo(x)\n                 foo(y, z)\n      :module: some.module.name\n\n      Return a line of text input from the user.\n\n``function`` is the directive name.  It is given two arguments here, the\nremainder of the first line and the second line, as well as one option\n``module`` (as you can see, options are given in the lines immediately following\nthe arguments and indicated by the colons).  Options must be indented to the\nsame level as the directive content.\n\nThe directive content follows after a blank line and is indented relative to the\ndirective start.\n\n\nImages\n------\n\nreST supports an image directive (:dudir:`ref &lt;image&gt;`), used like so::\n\n   .. image:: gnu.png\n      (options)\n\nWhen used within Sphinx, the file name given (here ``gnu.png``) must either be\nrelative to the source file, or absolute which means that they are relative to\nthe top source directory.  For example, the file ``sketch/spam.rst`` could refer\nto the image ``images/spam.png`` as ``../images/spam.png`` or\n``/images/spam.png``.\n\nSphinx will automatically copy image files over to a subdirectory of the output\ndirectory on building (e.g. the ``_static`` directory for HTML output.)\n\nInterpretation of image size options (``width`` and ``height``) is as follows:\nif the size has no unit or the unit is pixels, the given size will only be\nrespected for output channels that support pixels (i.e. not in LaTeX output).\nOther units (like ``pt`` for points) will be used for HTML and LaTeX output.\n\nSphinx extends the standard docutils behavior by allowing an asterisk for the\nextension::\n\n   .. image:: gnu.*\n\nSphinx then searches for all images matching the provided pattern and determines\ntheir type.  Each builder then chooses the best image out of these candidates.\nFor instance, if the file name ``gnu.*`` was given and two files :file:`gnu.pdf`\nand :file:`gnu.png` existed in the source tree, the LaTeX builder would choose\nthe former, while the HTML builder would prefer the latter.\n\n.. versionchanged:: 0.4\n   Added the support for file names ending in an asterisk.\n\n.. versionchanged:: 0.6\n   Image paths can now be absolute.\n\n\nFootnotes\n---------\n\nFor footnotes (:duref:`ref &lt;footnotes&gt;`), use ``[#name]_`` to mark the footnote\nlocation, and add the footnote body at the bottom of the document after a\n\"Footnotes\" rubric heading, like so::\n\n   Lorem ipsum [#f1]_ dolor sit amet ... [#f2]_\n\n   .. rubric:: Footnotes\n\n   .. [#f1] Text of the first footnote.\n   .. [#f2] Text of the second footnote.\n\nYou can also explicitly number the footnotes (``[1]_``) or use auto-numbered\nfootnotes without names (``[#]_``).\n\n\nCitations\n---------\n\nStandard reST citations (:duref:`ref &lt;citations&gt;`) are supported, with the\nadditional feature that they are \"global\", i.e. all citations can be referenced\nfrom all files.  Use them like so::\n\n   Lorem ipsum [Ref]_ dolor sit amet.\n\n   .. [Ref] Book or article reference, URL or whatever.\n\nCitation usage is similar to footnote usage, but with a label that is not\nnumeric or begins with ``#``.\n\n\nSubstitutions\n-------------\n\nreST supports \"substitutions\" (:duref:`ref &lt;substitution-definitions&gt;`), which\nare pieces of text and/or markup referred to in the text by ``|name|``.  They\nare defined like footnotes with explicit markup blocks, like this::\n\n   .. |name| replace:: replacement *text*\n\nor this::\n\n   .. |caution| image:: warning.png\n                :alt: Warning!\n\nSee the :duref:`reST reference for substitutions &lt;substitution-definitions&gt;`\nfor details.\n\nIf you want to use some substitutions for all documents, put them into\n:confval:`rst_prolog` or put them into a separate file and include it into all\ndocuments you want to use them in, using the :rst:dir:`include` directive.  (Be\nsure to give the include file a file name extension differing from that of other\nsource files, to avoid Sphinx finding it as a standalone document.)\n\nSphinx defines some default substitutions, see :ref:`default-substitutions`.\n\n\nComments\n--------\n\nEvery explicit markup block which isn't a valid markup construct (like the\nfootnotes above) is regarded as a comment (:duref:`ref &lt;comments&gt;`).  For\nexample::\n\n   .. This is a comment.\n\nYou can indent text after a comment start to form multiline comments::\n\n   ..\n      This whole indented block\n      is a comment.\n\n      Still in the comment.\n\n\nSource encoding\n---------------\n\nSince the easiest way to include special characters like em dashes or copyright\nsigns in reST is to directly write them as Unicode characters, one has to\nspecify an encoding.  Sphinx assumes source files to be encoded in UTF-8 by\ndefault; you can change this with the :confval:`source_encoding` config value.\n\n\nGotchas\n-------\n\nThere are some problems one commonly runs into while authoring reST documents:\n\n* **Separation of inline markup:** As said above, inline markup spans must be\n  separated from the surrounding text by non-word characters, you have to use a\n  backslash-escaped space to get around that.  See `the reference\n  &lt;http://docutils.sf.net/docs/ref/rst/restructuredtext.html#inline-markup&gt;`_\n  for the details.\n\n* **No nested inline markup:** Something like ``*see :func:`foo`*`` is not\n  possible.\n\n\n.. rubric:: Footnotes\n\n.. [1] When the default domain contains a :rst:dir:`class` directive, this directive\n       will be shadowed.  Therefore, Sphinx re-exports it as :rst:dir:`rst-class`.\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n      });\n    </script>\n    <p>\n        The <code>python</code> mode will be used for highlighting blocks\n        containing Python/IPython terminal sessions: blocks starting with\n        <code>&gt;&gt;&gt;</code> (for Python) or <code>In [num]:</code> (for\n        IPython).\n\n        Further, the <code>stex</code> mode will be used for highlighting\n        blocks containing LaTex code.\n    </p>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-rst</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/rst/rst.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../python/python\"), require(\"../stex/stex\"), require(\"../../addon/mode/overlay\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../python/python\", \"../stex/stex\", \"../../addon/mode/overlay\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode('rst', function (config, options) {\n\n  var rx_strong = /^\\*\\*[^\\*\\s](?:[^\\*]*[^\\*\\s])?\\*\\*/;\n  var rx_emphasis = /^\\*[^\\*\\s](?:[^\\*]*[^\\*\\s])?\\*/;\n  var rx_literal = /^``[^`\\s](?:[^`]*[^`\\s])``/;\n\n  var rx_number = /^(?:[\\d]+(?:[\\.,]\\d+)*)/;\n  var rx_positive = /^(?:\\s\\+[\\d]+(?:[\\.,]\\d+)*)/;\n  var rx_negative = /^(?:\\s\\-[\\d]+(?:[\\.,]\\d+)*)/;\n\n  var rx_uri_protocol = \"[Hh][Tt][Tt][Pp][Ss]?://\";\n  var rx_uri_domain = \"(?:[\\\\d\\\\w.-]+)\\\\.(?:\\\\w{2,6})\";\n  var rx_uri_path = \"(?:/[\\\\d\\\\w\\\\#\\\\%\\\\&\\\\-\\\\.\\\\,\\\\/\\\\:\\\\=\\\\?\\\\~]+)*\";\n  var rx_uri = new RegExp(\"^\" + rx_uri_protocol + rx_uri_domain + rx_uri_path);\n\n  var overlay = {\n    token: function (stream) {\n\n      if (stream.match(rx_strong) && stream.match (/\\W+|$/, false))\n        return 'strong';\n      if (stream.match(rx_emphasis) && stream.match (/\\W+|$/, false))\n        return 'em';\n      if (stream.match(rx_literal) && stream.match (/\\W+|$/, false))\n        return 'string-2';\n      if (stream.match(rx_number))\n        return 'number';\n      if (stream.match(rx_positive))\n        return 'positive';\n      if (stream.match(rx_negative))\n        return 'negative';\n      if (stream.match(rx_uri))\n        return 'link';\n\n      while (stream.next() != null) {\n        if (stream.match(rx_strong, false)) break;\n        if (stream.match(rx_emphasis, false)) break;\n        if (stream.match(rx_literal, false)) break;\n        if (stream.match(rx_number, false)) break;\n        if (stream.match(rx_positive, false)) break;\n        if (stream.match(rx_negative, false)) break;\n        if (stream.match(rx_uri, false)) break;\n      }\n\n      return null;\n    }\n  };\n\n  var mode = CodeMirror.getMode(\n    config, options.backdrop || 'rst-base'\n  );\n\n  return CodeMirror.overlayMode(mode, overlay, true); // combine\n}, 'python', 'stex');\n\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n\nCodeMirror.defineMode('rst-base', function (config) {\n\n  ///////////////////////////////////////////////////////////////////////////\n  ///////////////////////////////////////////////////////////////////////////\n\n  function format(string) {\n    var args = Array.prototype.slice.call(arguments, 1);\n    return string.replace(/{(\\d+)}/g, function (match, n) {\n      return typeof args[n] != 'undefined' ? args[n] : match;\n    });\n  }\n\n  ///////////////////////////////////////////////////////////////////////////\n  ///////////////////////////////////////////////////////////////////////////\n\n  var mode_python = CodeMirror.getMode(config, 'python');\n  var mode_stex = CodeMirror.getMode(config, 'stex');\n\n  ///////////////////////////////////////////////////////////////////////////\n  ///////////////////////////////////////////////////////////////////////////\n\n  var SEPA = \"\\\\s+\";\n  var TAIL = \"(?:\\\\s*|\\\\W|$)\",\n  rx_TAIL = new RegExp(format('^{0}', TAIL));\n\n  var NAME =\n    \"(?:[^\\\\W\\\\d_](?:[\\\\w!\\\"#$%&'()\\\\*\\\\+,\\\\-\\\\.\\/:;<=>\\\\?]*[^\\\\W_])?)\",\n  rx_NAME = new RegExp(format('^{0}', NAME));\n  var NAME_WWS =\n    \"(?:[^\\\\W\\\\d_](?:[\\\\w\\\\s!\\\"#$%&'()\\\\*\\\\+,\\\\-\\\\.\\/:;<=>\\\\?]*[^\\\\W_])?)\";\n  var REF_NAME = format('(?:{0}|`{1}`)', NAME, NAME_WWS);\n\n  var TEXT1 = \"(?:[^\\\\s\\\\|](?:[^\\\\|]*[^\\\\s\\\\|])?)\";\n  var TEXT2 = \"(?:[^\\\\`]+)\",\n  rx_TEXT2 = new RegExp(format('^{0}', TEXT2));\n\n  var rx_section = new RegExp(\n    \"^([!'#$%&\\\"()*+,-./:;<=>?@\\\\[\\\\\\\\\\\\]^_`{|}~])\\\\1{3,}\\\\s*$\");\n  var rx_explicit = new RegExp(\n    format('^\\\\.\\\\.{0}', SEPA));\n  var rx_link = new RegExp(\n    format('^_{0}:{1}|^__:{1}', REF_NAME, TAIL));\n  var rx_directive = new RegExp(\n    format('^{0}::{1}', REF_NAME, TAIL));\n  var rx_substitution = new RegExp(\n    format('^\\\\|{0}\\\\|{1}{2}::{3}', TEXT1, SEPA, REF_NAME, TAIL));\n  var rx_footnote = new RegExp(\n    format('^\\\\[(?:\\\\d+|#{0}?|\\\\*)]{1}', REF_NAME, TAIL));\n  var rx_citation = new RegExp(\n    format('^\\\\[{0}\\\\]{1}', REF_NAME, TAIL));\n\n  var rx_substitution_ref = new RegExp(\n    format('^\\\\|{0}\\\\|', TEXT1));\n  var rx_footnote_ref = new RegExp(\n    format('^\\\\[(?:\\\\d+|#{0}?|\\\\*)]_', REF_NAME));\n  var rx_citation_ref = new RegExp(\n    format('^\\\\[{0}\\\\]_', REF_NAME));\n  var rx_link_ref1 = new RegExp(\n    format('^{0}__?', REF_NAME));\n  var rx_link_ref2 = new RegExp(\n    format('^`{0}`_', TEXT2));\n\n  var rx_role_pre = new RegExp(\n    format('^:{0}:`{1}`{2}', NAME, TEXT2, TAIL));\n  var rx_role_suf = new RegExp(\n    format('^`{1}`:{0}:{2}', NAME, TEXT2, TAIL));\n  var rx_role = new RegExp(\n    format('^:{0}:{1}', NAME, TAIL));\n\n  var rx_directive_name = new RegExp(format('^{0}', REF_NAME));\n  var rx_directive_tail = new RegExp(format('^::{0}', TAIL));\n  var rx_substitution_text = new RegExp(format('^\\\\|{0}\\\\|', TEXT1));\n  var rx_substitution_sepa = new RegExp(format('^{0}', SEPA));\n  var rx_substitution_name = new RegExp(format('^{0}', REF_NAME));\n  var rx_substitution_tail = new RegExp(format('^::{0}', TAIL));\n  var rx_link_head = new RegExp(\"^_\");\n  var rx_link_name = new RegExp(format('^{0}|_', REF_NAME));\n  var rx_link_tail = new RegExp(format('^:{0}', TAIL));\n\n  var rx_verbatim = new RegExp('^::\\\\s*$');\n  var rx_examples = new RegExp('^\\\\s+(?:>>>|In \\\\[\\\\d+\\\\]:)\\\\s');\n\n  ///////////////////////////////////////////////////////////////////////////\n  ///////////////////////////////////////////////////////////////////////////\n\n  function to_normal(stream, state) {\n    var token = null;\n\n    if (stream.sol() && stream.match(rx_examples, false)) {\n      change(state, to_mode, {\n        mode: mode_python, local: CodeMirror.startState(mode_python)\n      });\n    } else if (stream.sol() && stream.match(rx_explicit)) {\n      change(state, to_explicit);\n      token = 'meta';\n    } else if (stream.sol() && stream.match(rx_section)) {\n      change(state, to_normal);\n      token = 'header';\n    } else if (phase(state) == rx_role_pre ||\n               stream.match(rx_role_pre, false)) {\n\n      switch (stage(state)) {\n      case 0:\n        change(state, to_normal, context(rx_role_pre, 1));\n        stream.match(/^:/);\n        token = 'meta';\n        break;\n      case 1:\n        change(state, to_normal, context(rx_role_pre, 2));\n        stream.match(rx_NAME);\n        token = 'keyword';\n\n        if (stream.current().match(/^(?:math|latex)/)) {\n          state.tmp_stex = true;\n        }\n        break;\n      case 2:\n        change(state, to_normal, context(rx_role_pre, 3));\n        stream.match(/^:`/);\n        token = 'meta';\n        break;\n      case 3:\n        if (state.tmp_stex) {\n          state.tmp_stex = undefined; state.tmp = {\n            mode: mode_stex, local: CodeMirror.startState(mode_stex)\n          };\n        }\n\n        if (state.tmp) {\n          if (stream.peek() == '`') {\n            change(state, to_normal, context(rx_role_pre, 4));\n            state.tmp = undefined;\n            break;\n          }\n\n          token = state.tmp.mode.token(stream, state.tmp.local);\n          break;\n        }\n\n        change(state, to_normal, context(rx_role_pre, 4));\n        stream.match(rx_TEXT2);\n        token = 'string';\n        break;\n      case 4:\n        change(state, to_normal, context(rx_role_pre, 5));\n        stream.match(/^`/);\n        token = 'meta';\n        break;\n      case 5:\n        change(state, to_normal, context(rx_role_pre, 6));\n        stream.match(rx_TAIL);\n        break;\n      default:\n        change(state, to_normal);\n      }\n    } else if (phase(state) == rx_role_suf ||\n               stream.match(rx_role_suf, false)) {\n\n      switch (stage(state)) {\n      case 0:\n        change(state, to_normal, context(rx_role_suf, 1));\n        stream.match(/^`/);\n        token = 'meta';\n        break;\n      case 1:\n        change(state, to_normal, context(rx_role_suf, 2));\n        stream.match(rx_TEXT2);\n        token = 'string';\n        break;\n      case 2:\n        change(state, to_normal, context(rx_role_suf, 3));\n        stream.match(/^`:/);\n        token = 'meta';\n        break;\n      case 3:\n        change(state, to_normal, context(rx_role_suf, 4));\n        stream.match(rx_NAME);\n        token = 'keyword';\n        break;\n      case 4:\n        change(state, to_normal, context(rx_role_suf, 5));\n        stream.match(/^:/);\n        token = 'meta';\n        break;\n      case 5:\n        change(state, to_normal, context(rx_role_suf, 6));\n        stream.match(rx_TAIL);\n        break;\n      default:\n        change(state, to_normal);\n      }\n    } else if (phase(state) == rx_role || stream.match(rx_role, false)) {\n\n      switch (stage(state)) {\n      case 0:\n        change(state, to_normal, context(rx_role, 1));\n        stream.match(/^:/);\n        token = 'meta';\n        break;\n      case 1:\n        change(state, to_normal, context(rx_role, 2));\n        stream.match(rx_NAME);\n        token = 'keyword';\n        break;\n      case 2:\n        change(state, to_normal, context(rx_role, 3));\n        stream.match(/^:/);\n        token = 'meta';\n        break;\n      case 3:\n        change(state, to_normal, context(rx_role, 4));\n        stream.match(rx_TAIL);\n        break;\n      default:\n        change(state, to_normal);\n      }\n    } else if (phase(state) == rx_substitution_ref ||\n               stream.match(rx_substitution_ref, false)) {\n\n      switch (stage(state)) {\n      case 0:\n        change(state, to_normal, context(rx_substitution_ref, 1));\n        stream.match(rx_substitution_text);\n        token = 'variable-2';\n        break;\n      case 1:\n        change(state, to_normal, context(rx_substitution_ref, 2));\n        if (stream.match(/^_?_?/)) token = 'link';\n        break;\n      default:\n        change(state, to_normal);\n      }\n    } else if (stream.match(rx_footnote_ref)) {\n      change(state, to_normal);\n      token = 'quote';\n    } else if (stream.match(rx_citation_ref)) {\n      change(state, to_normal);\n      token = 'quote';\n    } else if (stream.match(rx_link_ref1)) {\n      change(state, to_normal);\n      if (!stream.peek() || stream.peek().match(/^\\W$/)) {\n        token = 'link';\n      }\n    } else if (phase(state) == rx_link_ref2 ||\n               stream.match(rx_link_ref2, false)) {\n\n      switch (stage(state)) {\n      case 0:\n        if (!stream.peek() || stream.peek().match(/^\\W$/)) {\n          change(state, to_normal, context(rx_link_ref2, 1));\n        } else {\n          stream.match(rx_link_ref2);\n        }\n        break;\n      case 1:\n        change(state, to_normal, context(rx_link_ref2, 2));\n        stream.match(/^`/);\n        token = 'link';\n        break;\n      case 2:\n        change(state, to_normal, context(rx_link_ref2, 3));\n        stream.match(rx_TEXT2);\n        break;\n      case 3:\n        change(state, to_normal, context(rx_link_ref2, 4));\n        stream.match(/^`_/);\n        token = 'link';\n        break;\n      default:\n        change(state, to_normal);\n      }\n    } else if (stream.match(rx_verbatim)) {\n      change(state, to_verbatim);\n    }\n\n    else {\n      if (stream.next()) change(state, to_normal);\n    }\n\n    return token;\n  }\n\n  ///////////////////////////////////////////////////////////////////////////\n  ///////////////////////////////////////////////////////////////////////////\n\n  function to_explicit(stream, state) {\n    var token = null;\n\n    if (phase(state) == rx_substitution ||\n        stream.match(rx_substitution, false)) {\n\n      switch (stage(state)) {\n      case 0:\n        change(state, to_explicit, context(rx_substitution, 1));\n        stream.match(rx_substitution_text);\n        token = 'variable-2';\n        break;\n      case 1:\n        change(state, to_explicit, context(rx_substitution, 2));\n        stream.match(rx_substitution_sepa);\n        break;\n      case 2:\n        change(state, to_explicit, context(rx_substitution, 3));\n        stream.match(rx_substitution_name);\n        token = 'keyword';\n        break;\n      case 3:\n        change(state, to_explicit, context(rx_substitution, 4));\n        stream.match(rx_substitution_tail);\n        token = 'meta';\n        break;\n      default:\n        change(state, to_normal);\n      }\n    } else if (phase(state) == rx_directive ||\n               stream.match(rx_directive, false)) {\n\n      switch (stage(state)) {\n      case 0:\n        change(state, to_explicit, context(rx_directive, 1));\n        stream.match(rx_directive_name);\n        token = 'keyword';\n\n        if (stream.current().match(/^(?:math|latex)/))\n          state.tmp_stex = true;\n        else if (stream.current().match(/^python/))\n          state.tmp_py = true;\n        break;\n      case 1:\n        change(state, to_explicit, context(rx_directive, 2));\n        stream.match(rx_directive_tail);\n        token = 'meta';\n\n        if (stream.match(/^latex\\s*$/) || state.tmp_stex) {\n          state.tmp_stex = undefined; change(state, to_mode, {\n            mode: mode_stex, local: CodeMirror.startState(mode_stex)\n          });\n        }\n        break;\n      case 2:\n        change(state, to_explicit, context(rx_directive, 3));\n        if (stream.match(/^python\\s*$/) || state.tmp_py) {\n          state.tmp_py = undefined; change(state, to_mode, {\n            mode: mode_python, local: CodeMirror.startState(mode_python)\n          });\n        }\n        break;\n      default:\n        change(state, to_normal);\n      }\n    } else if (phase(state) == rx_link || stream.match(rx_link, false)) {\n\n      switch (stage(state)) {\n      case 0:\n        change(state, to_explicit, context(rx_link, 1));\n        stream.match(rx_link_head);\n        stream.match(rx_link_name);\n        token = 'link';\n        break;\n      case 1:\n        change(state, to_explicit, context(rx_link, 2));\n        stream.match(rx_link_tail);\n        token = 'meta';\n        break;\n      default:\n        change(state, to_normal);\n      }\n    } else if (stream.match(rx_footnote)) {\n      change(state, to_normal);\n      token = 'quote';\n    } else if (stream.match(rx_citation)) {\n      change(state, to_normal);\n      token = 'quote';\n    }\n\n    else {\n      stream.eatSpace();\n      if (stream.eol()) {\n        change(state, to_normal);\n      } else {\n        stream.skipToEnd();\n        change(state, to_comment);\n        token = 'comment';\n      }\n    }\n\n    return token;\n  }\n\n  ///////////////////////////////////////////////////////////////////////////\n  ///////////////////////////////////////////////////////////////////////////\n\n  function to_comment(stream, state) {\n    return as_block(stream, state, 'comment');\n  }\n\n  function to_verbatim(stream, state) {\n    return as_block(stream, state, 'meta');\n  }\n\n  function as_block(stream, state, token) {\n    if (stream.eol() || stream.eatSpace()) {\n      stream.skipToEnd();\n      return token;\n    } else {\n      change(state, to_normal);\n      return null;\n    }\n  }\n\n  ///////////////////////////////////////////////////////////////////////////\n  ///////////////////////////////////////////////////////////////////////////\n\n  function to_mode(stream, state) {\n\n    if (state.ctx.mode && state.ctx.local) {\n\n      if (stream.sol()) {\n        if (!stream.eatSpace()) change(state, to_normal);\n        return null;\n      }\n\n      return state.ctx.mode.token(stream, state.ctx.local);\n    }\n\n    change(state, to_normal);\n    return null;\n  }\n\n  ///////////////////////////////////////////////////////////////////////////\n  ///////////////////////////////////////////////////////////////////////////\n\n  function context(phase, stage, mode, local) {\n    return {phase: phase, stage: stage, mode: mode, local: local};\n  }\n\n  function change(state, tok, ctx) {\n    state.tok = tok;\n    state.ctx = ctx || {};\n  }\n\n  function stage(state) {\n    return state.ctx.stage || 0;\n  }\n\n  function phase(state) {\n    return state.ctx.phase;\n  }\n\n  ///////////////////////////////////////////////////////////////////////////\n  ///////////////////////////////////////////////////////////////////////////\n\n  return {\n    startState: function () {\n      return {tok: to_normal, ctx: context(undefined, 0)};\n    },\n\n    copyState: function (state) {\n      var ctx = state.ctx, tmp = state.tmp;\n      if (ctx.local)\n        ctx = {mode: ctx.mode, local: CodeMirror.copyState(ctx.mode, ctx.local)};\n      if (tmp)\n        tmp = {mode: tmp.mode, local: CodeMirror.copyState(tmp.mode, tmp.local)};\n      return {tok: state.tok, ctx: ctx, tmp: tmp};\n    },\n\n    innerMode: function (state) {\n      return state.tmp      ? {state: state.tmp.local, mode: state.tmp.mode}\n      : state.ctx.mode ? {state: state.ctx.local, mode: state.ctx.mode}\n      : null;\n    },\n\n    token: function (stream, state) {\n      return state.tok(stream, state);\n    }\n  };\n}, 'python', 'stex');\n\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n\nCodeMirror.defineMIME('text/x-rst', 'rst');\n\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ruby/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Ruby mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"ruby.js\"></script>\n<style>\n      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n      .cm-s-default span.cm-arrow { color: red; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Ruby</a>\n  </ul>\n</div>\n\n<article>\n<h2>Ruby mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n# Code from http://sandbox.mc.edu/~bennet/ruby/code/poly_rb.html\n#\n# This program evaluates polynomials.  It first asks for the coefficients\n# of a polynomial, which must be entered on one line, highest-order first.\n# It then requests values of x and will compute the value of the poly for\n# each x.  It will repeatedly ask for x values, unless you the user enters\n# a blank line.  It that case, it will ask for another polynomial.  If the\n# user types quit for either input, the program immediately exits.\n#\n\n#\n# Function to evaluate a polynomial at x.  The polynomial is given\n# as a list of coefficients, from the greatest to the least.\ndef polyval(x, coef)\n    sum = 0\n    coef = coef.clone           # Don't want to destroy the original\n    while true\n        sum += coef.shift       # Add and remove the next coef\n        break if coef.empty?    # If no more, done entirely.\n        sum *= x                # This happens the right number of times.\n    end\n    return sum\nend\n\n#\n# Function to read a line containing a list of integers and return\n# them as an array of integers.  If the string conversion fails, it\n# throws TypeError.  If the input line is the word 'quit', then it\n# converts it to an end-of-file exception\ndef readints(prompt)\n    # Read a line\n    print prompt\n    line = readline.chomp\n    raise EOFError.new if line == 'quit' # You can also use a real EOF.\n            \n    # Go through each item on the line, converting each one and adding it\n    # to retval.\n    retval = [ ]\n    for str in line.split(/\\s+/)\n        if str =~ /^\\-?\\d+$/\n            retval.push(str.to_i)\n        else\n            raise TypeError.new\n        end\n    end\n\n    return retval\nend\n\n#\n# Take a coeff and an exponent and return the string representation, ignoring\n# the sign of the coefficient.\ndef term_to_str(coef, exp)\n    ret = \"\"\n\n    # Show coeff, unless it's 1 or at the right\n    coef = coef.abs\n    ret = coef.to_s     unless coef == 1 && exp > 0\n    ret += \"x\" if exp > 0                               # x if exponent not 0\n    ret += \"^\" + exp.to_s if exp > 1                    # ^exponent, if > 1.\n\n    return ret\nend\n\n#\n# Create a string of the polynomial in sort-of-readable form.\ndef polystr(p)\n    # Get the exponent of first coefficient, plus 1.\n    exp = p.length\n\n    # Assign exponents to each term, making pairs of coeff and exponent,\n    # Then get rid of the zero terms.\n    p = (p.map { |c| exp -= 1; [ c, exp ] }).select { |p| p[0] != 0 }\n\n    # If there's nothing left, it's a zero\n    return \"0\" if p.empty?\n\n    # *** Now p is a non-empty list of [ coef, exponent ] pairs. ***\n\n    # Convert the first term, preceded by a \"-\" if it's negative.\n    result = (if p[0][0] < 0 then \"-\" else \"\" end) + term_to_str(*p[0])\n\n    # Convert the rest of the terms, in each case adding the appropriate\n    # + or - separating them.  \n    for term in p[1...p.length]\n        # Add the separator then the rep. of the term.\n        result += (if term[0] < 0 then \" - \" else \" + \" end) + \n                term_to_str(*term)\n    end\n\n    return result\nend\n        \n#\n# Run until some kind of endfile.\nbegin\n    # Repeat until an exception or quit gets us out.\n    while true\n        # Read a poly until it works.  An EOF will except out of the\n        # program.\n        print \"\\n\"\n        begin\n            poly = readints(\"Enter a polynomial coefficients: \")\n        rescue TypeError\n            print \"Try again.\\n\"\n            retry\n        end\n        break if poly.empty?\n\n        # Read and evaluate x values until the user types a blank line.\n        # Again, an EOF will except out of the pgm.\n        while true\n            # Request an integer.\n            print \"Enter x value or blank line: \"\n            x = readline.chomp\n            break if x == ''\n            raise EOFError.new if x == 'quit'\n\n            # If it looks bad, let's try again.\n            if x !~ /^\\-?\\d+$/\n                print \"That doesn't look like an integer.  Please try again.\\n\"\n                next\n            end\n\n            # Convert to an integer and print the result.\n            x = x.to_i\n            print \"p(x) = \", polystr(poly), \"\\n\"\n            print \"p(\", x, \") = \", polyval(x, poly), \"\\n\"\n        end\n    end\nrescue EOFError\n    print \"\\n=== EOF ===\\n\"\nrescue Interrupt, SignalException\n    print \"\\n=== Interrupted ===\\n\"\nelse\n    print \"--- Bye ---\\n\"\nend\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: \"text/x-ruby\",\n        matchBrackets: true,\n        indentUnit: 4\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-ruby</code>.</p>\n\n    <p>Development of the CodeMirror Ruby mode was kindly sponsored\n    by <a href=\"http://ubalo.com/\">Ubalo</a>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ruby/ruby.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nfunction wordObj(words) {\n  var o = {};\n  for (var i = 0, e = words.length; i < e; ++i) o[words[i]] = true;\n  return o;\n}\n\nvar keywordList = [\n  \"alias\", \"and\", \"BEGIN\", \"begin\", \"break\", \"case\", \"class\", \"def\", \"defined?\", \"do\", \"else\",\n  \"elsif\", \"END\", \"end\", \"ensure\", \"false\", \"for\", \"if\", \"in\", \"module\", \"next\", \"not\", \"or\",\n  \"redo\", \"rescue\", \"retry\", \"return\", \"self\", \"super\", \"then\", \"true\", \"undef\", \"unless\",\n  \"until\", \"when\", \"while\", \"yield\", \"nil\", \"raise\", \"throw\", \"catch\", \"fail\", \"loop\", \"callcc\",\n  \"caller\", \"lambda\", \"proc\", \"public\", \"protected\", \"private\", \"require\", \"load\",\n  \"require_relative\", \"extend\", \"autoload\", \"__END__\", \"__FILE__\", \"__LINE__\", \"__dir__\"\n], keywords = wordObj(keywordList);\n\nvar indentWords = wordObj([\"def\", \"class\", \"case\", \"for\", \"while\", \"until\", \"module\",\n                           \"catch\", \"loop\", \"proc\", \"begin\"]);\nvar dedentWords = wordObj([\"end\", \"until\"]);\nvar opening = {\"[\": \"]\", \"{\": \"}\", \"(\": \")\"};\nvar closing = {\"]\": \"[\", \"}\": \"{\", \")\": \"(\"};\n\nCodeMirror.defineMode(\"ruby\", function(config) {\n  var curPunc;\n\n  function chain(newtok, stream, state) {\n    state.tokenize.push(newtok);\n    return newtok(stream, state);\n  }\n\n  function tokenBase(stream, state) {\n    if (stream.sol() && stream.match(\"=begin\") && stream.eol()) {\n      state.tokenize.push(readBlockComment);\n      return \"comment\";\n    }\n    if (stream.eatSpace()) return null;\n    var ch = stream.next(), m;\n    if (ch == \"`\" || ch == \"'\" || ch == '\"') {\n      return chain(readQuoted(ch, \"string\", ch == '\"' || ch == \"`\"), stream, state);\n    } else if (ch == \"/\") {\n      if (regexpAhead(stream))\n        return chain(readQuoted(ch, \"string-2\", true), stream, state);\n      else\n        return \"operator\";\n    } else if (ch == \"%\") {\n      var style = \"string\", embed = true;\n      if (stream.eat(\"s\")) style = \"atom\";\n      else if (stream.eat(/[WQ]/)) style = \"string\";\n      else if (stream.eat(/[r]/)) style = \"string-2\";\n      else if (stream.eat(/[wxq]/)) { style = \"string\"; embed = false; }\n      var delim = stream.eat(/[^\\w\\s=]/);\n      if (!delim) return \"operator\";\n      if (opening.propertyIsEnumerable(delim)) delim = opening[delim];\n      return chain(readQuoted(delim, style, embed, true), stream, state);\n    } else if (ch == \"#\") {\n      stream.skipToEnd();\n      return \"comment\";\n    } else if (ch == \"<\" && (m = stream.match(/^<([-~])[\\`\\\"\\']?([a-zA-Z_?]\\w*)[\\`\\\"\\']?(?:;|$)/))) {\n      return chain(readHereDoc(m[2], m[1]), stream, state);\n    } else if (ch == \"0\") {\n      if (stream.eat(\"x\")) stream.eatWhile(/[\\da-fA-F]/);\n      else if (stream.eat(\"b\")) stream.eatWhile(/[01]/);\n      else stream.eatWhile(/[0-7]/);\n      return \"number\";\n    } else if (/\\d/.test(ch)) {\n      stream.match(/^[\\d_]*(?:\\.[\\d_]+)?(?:[eE][+\\-]?[\\d_]+)?/);\n      return \"number\";\n    } else if (ch == \"?\") {\n      while (stream.match(/^\\\\[CM]-/)) {}\n      if (stream.eat(\"\\\\\")) stream.eatWhile(/\\w/);\n      else stream.next();\n      return \"string\";\n    } else if (ch == \":\") {\n      if (stream.eat(\"'\")) return chain(readQuoted(\"'\", \"atom\", false), stream, state);\n      if (stream.eat('\"')) return chain(readQuoted('\"', \"atom\", true), stream, state);\n\n      // :> :>> :< :<< are valid symbols\n      if (stream.eat(/[\\<\\>]/)) {\n        stream.eat(/[\\<\\>]/);\n        return \"atom\";\n      }\n\n      // :+ :- :/ :* :| :& :! are valid symbols\n      if (stream.eat(/[\\+\\-\\*\\/\\&\\|\\:\\!]/)) {\n        return \"atom\";\n      }\n\n      // Symbols can't start by a digit\n      if (stream.eat(/[a-zA-Z$@_\\xa1-\\uffff]/)) {\n        stream.eatWhile(/[\\w$\\xa1-\\uffff]/);\n        // Only one ? ! = is allowed and only as the last character\n        stream.eat(/[\\?\\!\\=]/);\n        return \"atom\";\n      }\n      return \"operator\";\n    } else if (ch == \"@\" && stream.match(/^@?[a-zA-Z_\\xa1-\\uffff]/)) {\n      stream.eat(\"@\");\n      stream.eatWhile(/[\\w\\xa1-\\uffff]/);\n      return \"variable-2\";\n    } else if (ch == \"$\") {\n      if (stream.eat(/[a-zA-Z_]/)) {\n        stream.eatWhile(/[\\w]/);\n      } else if (stream.eat(/\\d/)) {\n        stream.eat(/\\d/);\n      } else {\n        stream.next(); // Must be a special global like $: or $!\n      }\n      return \"variable-3\";\n    } else if (/[a-zA-Z_\\xa1-\\uffff]/.test(ch)) {\n      stream.eatWhile(/[\\w\\xa1-\\uffff]/);\n      stream.eat(/[\\?\\!]/);\n      if (stream.eat(\":\")) return \"atom\";\n      return \"ident\";\n    } else if (ch == \"|\" && (state.varList || state.lastTok == \"{\" || state.lastTok == \"do\")) {\n      curPunc = \"|\";\n      return null;\n    } else if (/[\\(\\)\\[\\]{}\\\\;]/.test(ch)) {\n      curPunc = ch;\n      return null;\n    } else if (ch == \"-\" && stream.eat(\">\")) {\n      return \"arrow\";\n    } else if (/[=+\\-\\/*:\\.^%<>~|]/.test(ch)) {\n      var more = stream.eatWhile(/[=+\\-\\/*:\\.^%<>~|]/);\n      if (ch == \".\" && !more) curPunc = \".\";\n      return \"operator\";\n    } else {\n      return null;\n    }\n  }\n\n  function regexpAhead(stream) {\n    var start = stream.pos, depth = 0, next, found = false, escaped = false\n    while ((next = stream.next()) != null) {\n      if (!escaped) {\n        if (\"[{(\".indexOf(next) > -1) {\n          depth++\n        } else if (\"]})\".indexOf(next) > -1) {\n          depth--\n          if (depth < 0) break\n        } else if (next == \"/\" && depth == 0) {\n          found = true\n          break\n        }\n        escaped = next == \"\\\\\"\n      } else {\n        escaped = false\n      }\n    }\n    stream.backUp(stream.pos - start)\n    return found\n  }\n\n  function tokenBaseUntilBrace(depth) {\n    if (!depth) depth = 1;\n    return function(stream, state) {\n      if (stream.peek() == \"}\") {\n        if (depth == 1) {\n          state.tokenize.pop();\n          return state.tokenize[state.tokenize.length-1](stream, state);\n        } else {\n          state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth - 1);\n        }\n      } else if (stream.peek() == \"{\") {\n        state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth + 1);\n      }\n      return tokenBase(stream, state);\n    };\n  }\n  function tokenBaseOnce() {\n    var alreadyCalled = false;\n    return function(stream, state) {\n      if (alreadyCalled) {\n        state.tokenize.pop();\n        return state.tokenize[state.tokenize.length-1](stream, state);\n      }\n      alreadyCalled = true;\n      return tokenBase(stream, state);\n    };\n  }\n  function readQuoted(quote, style, embed, unescaped) {\n    return function(stream, state) {\n      var escaped = false, ch;\n\n      if (state.context.type === 'read-quoted-paused') {\n        state.context = state.context.prev;\n        stream.eat(\"}\");\n      }\n\n      while ((ch = stream.next()) != null) {\n        if (ch == quote && (unescaped || !escaped)) {\n          state.tokenize.pop();\n          break;\n        }\n        if (embed && ch == \"#\" && !escaped) {\n          if (stream.eat(\"{\")) {\n            if (quote == \"}\") {\n              state.context = {prev: state.context, type: 'read-quoted-paused'};\n            }\n            state.tokenize.push(tokenBaseUntilBrace());\n            break;\n          } else if (/[@\\$]/.test(stream.peek())) {\n            state.tokenize.push(tokenBaseOnce());\n            break;\n          }\n        }\n        escaped = !escaped && ch == \"\\\\\";\n      }\n      return style;\n    };\n  }\n  function readHereDoc(phrase, mayIndent) {\n    return function(stream, state) {\n      if (mayIndent) stream.eatSpace()\n      if (stream.match(phrase)) state.tokenize.pop();\n      else stream.skipToEnd();\n      return \"string\";\n    };\n  }\n  function readBlockComment(stream, state) {\n    if (stream.sol() && stream.match(\"=end\") && stream.eol())\n      state.tokenize.pop();\n    stream.skipToEnd();\n    return \"comment\";\n  }\n\n  return {\n    startState: function() {\n      return {tokenize: [tokenBase],\n              indented: 0,\n              context: {type: \"top\", indented: -config.indentUnit},\n              continuedLine: false,\n              lastTok: null,\n              varList: false};\n    },\n\n    token: function(stream, state) {\n      curPunc = null;\n      if (stream.sol()) state.indented = stream.indentation();\n      var style = state.tokenize[state.tokenize.length-1](stream, state), kwtype;\n      var thisTok = curPunc;\n      if (style == \"ident\") {\n        var word = stream.current();\n        style = state.lastTok == \".\" ? \"property\"\n          : keywords.propertyIsEnumerable(stream.current()) ? \"keyword\"\n          : /^[A-Z]/.test(word) ? \"tag\"\n          : (state.lastTok == \"def\" || state.lastTok == \"class\" || state.varList) ? \"def\"\n          : \"variable\";\n        if (style == \"keyword\") {\n          thisTok = word;\n          if (indentWords.propertyIsEnumerable(word)) kwtype = \"indent\";\n          else if (dedentWords.propertyIsEnumerable(word)) kwtype = \"dedent\";\n          else if ((word == \"if\" || word == \"unless\") && stream.column() == stream.indentation())\n            kwtype = \"indent\";\n          else if (word == \"do\" && state.context.indented < state.indented)\n            kwtype = \"indent\";\n        }\n      }\n      if (curPunc || (style && style != \"comment\")) state.lastTok = thisTok;\n      if (curPunc == \"|\") state.varList = !state.varList;\n\n      if (kwtype == \"indent\" || /[\\(\\[\\{]/.test(curPunc))\n        state.context = {prev: state.context, type: curPunc || style, indented: state.indented};\n      else if ((kwtype == \"dedent\" || /[\\)\\]\\}]/.test(curPunc)) && state.context.prev)\n        state.context = state.context.prev;\n\n      if (stream.eol())\n        state.continuedLine = (curPunc == \"\\\\\" || style == \"operator\");\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize[state.tokenize.length-1] != tokenBase) return CodeMirror.Pass;\n      var firstChar = textAfter && textAfter.charAt(0);\n      var ct = state.context;\n      var closed = ct.type == closing[firstChar] ||\n        ct.type == \"keyword\" && /^(?:end|until|else|elsif|when|rescue)\\b/.test(textAfter);\n      return ct.indented + (closed ? 0 : config.indentUnit) +\n        (state.continuedLine ? config.indentUnit : 0);\n    },\n\n    electricInput: /^\\s*(?:end|rescue|elsif|else|\\})$/,\n    lineComment: \"#\",\n    fold: \"indent\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-ruby\", \"ruby\");\n\nCodeMirror.registerHelper(\"hintWords\", \"ruby\", keywordList);\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ruby/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"ruby\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"divide_equal_operator\",\n     \"[variable bar] [operator /=] [variable foo]\");\n\n  MT(\"divide_equal_operator_no_spacing\",\n     \"[variable foo][operator /=][number 42]\");\n\n  MT(\"complex_regexp\",\n     \"[keyword if] [variable cr] [operator =~] [string-2 /(?: \\\\( #{][tag RE_NOT][string-2 }\\\\( | #{][tag RE_NOT_PAR_OR][string-2 }* #{][tag RE_OPA_OR][string-2 } )/][variable x]\")\n\n  MT(\"indented_heredoc\",\n     \"[keyword def] [def x]\",\n     \"  [variable y] [operator =] [string <<-FOO]\",\n     \"[string     bar]\",\n     \"[string   FOO]\",\n     \"[keyword end]\")\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/rust/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Rust mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/mode/simple.js\"></script>\n<script src=\"rust.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Rust</a>\n  </ul>\n</div>\n\n<article>\n<h2>Rust mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n// Demo code.\n\ntype foo<T> = i32;\nenum bar {\n    Some(i32, foo<f32>),\n    None\n}\n\nfn check_crate(x: i32) {\n    let v = 10;\n    match foo {\n        1 ... 3 {\n            print_foo();\n            if x {\n                blah().to_string();\n            }\n        }\n        (x, y) { \"bye\" }\n        _ { \"hi\" }\n    }\n}\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        lineWrapping: true,\n        indentUnit: 4,\n        mode: \"rust\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-rustsrc</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/rust/rust.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../../addon/mode/simple\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../../addon/mode/simple\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineSimpleMode(\"rust\",{\n  start: [\n    // string and byte string\n    {regex: /b?\"/, token: \"string\", next: \"string\"},\n    // raw string and raw byte string\n    {regex: /b?r\"/, token: \"string\", next: \"string_raw\"},\n    {regex: /b?r#+\"/, token: \"string\", next: \"string_raw_hash\"},\n    // character\n    {regex: /'(?:[^'\\\\]|\\\\(?:[nrt0'\"]|x[\\da-fA-F]{2}|u\\{[\\da-fA-F]{6}\\}))'/, token: \"string-2\"},\n    // byte\n    {regex: /b'(?:[^']|\\\\(?:['\\\\nrt0]|x[\\da-fA-F]{2}))'/, token: \"string-2\"},\n\n    {regex: /(?:(?:[0-9][0-9_]*)(?:(?:[Ee][+-]?[0-9_]+)|\\.[0-9_]+(?:[Ee][+-]?[0-9_]+)?)(?:f32|f64)?)|(?:0(?:b[01_]+|(?:o[0-7_]+)|(?:x[0-9a-fA-F_]+))|(?:[0-9][0-9_]*))(?:u8|u16|u32|u64|i8|i16|i32|i64|isize|usize)?/,\n     token: \"number\"},\n    {regex: /(let(?:\\s+mut)?|fn|enum|mod|struct|type|union)(\\s+)([a-zA-Z_][a-zA-Z0-9_]*)/, token: [\"keyword\", null, \"def\"]},\n    {regex: /(?:abstract|alignof|as|async|await|box|break|continue|const|crate|do|dyn|else|enum|extern|fn|for|final|if|impl|in|loop|macro|match|mod|move|offsetof|override|priv|proc|pub|pure|ref|return|self|sizeof|static|struct|super|trait|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\\b/, token: \"keyword\"},\n    {regex: /\\b(?:Self|isize|usize|char|bool|u8|u16|u32|u64|f16|f32|f64|i8|i16|i32|i64|str|Option)\\b/, token: \"atom\"},\n    {regex: /\\b(?:true|false|Some|None|Ok|Err)\\b/, token: \"builtin\"},\n    {regex: /\\b(fn)(\\s+)([a-zA-Z_][a-zA-Z0-9_]*)/,\n     token: [\"keyword\", null ,\"def\"]},\n    {regex: /#!?\\[.*\\]/, token: \"meta\"},\n    {regex: /\\/\\/.*/, token: \"comment\"},\n    {regex: /\\/\\*/, token: \"comment\", next: \"comment\"},\n    {regex: /[-+\\/*=<>!]+/, token: \"operator\"},\n    {regex: /[a-zA-Z_]\\w*!/,token: \"variable-3\"},\n    {regex: /[a-zA-Z_]\\w*/, token: \"variable\"},\n    {regex: /[\\{\\[\\(]/, indent: true},\n    {regex: /[\\}\\]\\)]/, dedent: true}\n  ],\n  string: [\n    {regex: /\"/, token: \"string\", next: \"start\"},\n    {regex: /(?:[^\\\\\"]|\\\\(?:.|$))*/, token: \"string\"}\n  ],\n  string_raw: [\n    {regex: /\"/, token: \"string\", next: \"start\"},\n    {regex: /[^\"]*/, token: \"string\"}\n  ],\n  string_raw_hash: [\n    {regex: /\"#+/, token: \"string\", next: \"start\"},\n    {regex: /(?:[^\"]|\"(?!#))*/, token: \"string\"}\n  ],\n  comment: [\n    {regex: /.*?\\*\\//, token: \"comment\", next: \"start\"},\n    {regex: /.*/, token: \"comment\"}\n  ],\n  meta: {\n    dontIndentStates: [\"comment\"],\n    electricInput: /^\\s*\\}$/,\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    lineComment: \"//\",\n    fold: \"brace\"\n  }\n});\n\n\nCodeMirror.defineMIME(\"text/x-rustsrc\", \"rust\");\nCodeMirror.defineMIME(\"text/rust\", \"rust\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/rust/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 4}, \"rust\");\n  function MT(name) {test.mode(name, mode, Array.prototype.slice.call(arguments, 1));}\n\n  MT('integer_test',\n     '[number 123i32]',\n     '[number 123u32]',\n     '[number 123_u32]',\n     '[number 0xff_u8]',\n     '[number 0o70_i16]',\n     '[number 0b1111_1111_1001_0000_i32]',\n     '[number 0usize]');\n\n  MT('float_test',\n     '[number 123.0f64]',\n     '[number 0.1f64]',\n     '[number 0.1f32]',\n     '[number 12E+99_f64]');\n\n  MT('string-literals-test',\n     '[string \"foo\"]',\n     '[string r\"foo\"]',\n     '[string \"\\\\\"foo\\\\\"\"]',\n     '[string r#\"\"foo\"\"#]',\n     '[string \"foo #\\\\\"# bar\"]',\n\n     '[string b\"foo\"]',\n     '[string br\"foo\"]',\n     '[string b\"\\\\\"foo\\\\\"\"]',\n     '[string br#\"\"foo\"\"#]',\n     '[string br##\"foo #\" bar\"##]',\n\n     \"[string-2 'h']\",\n     \"[string-2 b'h']\");\n\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/sas/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: SAS mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"sas.js\"></script>\n<style>\n  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n  .cm-s-default .cm-trailing-space-a:before,\n  .cm-s-default .cm-trailing-space-b:before {position: absolute; content: \"\\00B7\"; color: #777;}\n  .cm-s-default .cm-trailing-space-new-line:before {position: absolute; content: \"\\21B5\"; color: #777;}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">SAS</a>\n  </ul>\n</div>\n\n<article>\n<h2>SAS mode</h2>\n<form><textarea id=\"code\" name=\"code\">\nlibname foo \"/tmp/foobar\";\n%let count=1;\n\n/* Multi line\nComment\n*/\ndata _null_;\n    x=ranuni();\n    * single comment;\n    x2=x**2;\n    sx=sqrt(x);\n    if x=x2 then put \"x must be 1\";\n    else do;\n        put x=;\n    end;\nrun;\n\n/* embedded comment\n* comment;\n*/\n\nproc glm data=sashelp.class;\n    class sex;\n    model weight = height sex;\nrun;\n\nproc sql;\n    select count(*)\n    from sashelp.class;\n\n    create table foo as\n    select * from sashelp.class;\n\n    select *\n    from foo;\nquit;\n</textarea></form>\n\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    mode: 'sas',\n    lineNumbers: true\n  });\n</script>\n\n<p><strong>MIME types defined:</strong> <code>text/x-sas</code>.</p>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/sas/sas.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n\n// SAS mode copyright (c) 2016 Jared Dean, SAS Institute\n// Created by Jared Dean\n\n// TODO\n// indent and de-indent\n// identify macro variables\n\n\n//Definitions\n//  comment -- text within * ; or /* */\n//  keyword -- SAS language variable\n//  variable -- macro variables starts with '&' or variable formats\n//  variable-2 -- DATA Step, proc, or macro names\n//  string -- text within ' ' or \" \"\n//  operator -- numeric operator + / - * ** le eq ge ... and so on\n//  builtin -- proc %macro data run mend\n//  atom\n//  def\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"sas\", function () {\n    var words = {};\n    var isDoubleOperatorSym = {\n      eq: 'operator',\n      lt: 'operator',\n      le: 'operator',\n      gt: 'operator',\n      ge: 'operator',\n      \"in\": 'operator',\n      ne: 'operator',\n      or: 'operator'\n    };\n    var isDoubleOperatorChar = /(<=|>=|!=|<>)/;\n    var isSingleOperatorChar = /[=\\(:\\),{}.*<>+\\-\\/^\\[\\]]/;\n\n    // Takes a string of words separated by spaces and adds them as\n    // keys with the value of the first argument 'style'\n    function define(style, string, context) {\n      if (context) {\n        var split = string.split(' ');\n        for (var i = 0; i < split.length; i++) {\n          words[split[i]] = {style: style, state: context};\n        }\n      }\n    }\n    //datastep\n    define('def', 'stack pgm view source debug nesting nolist', ['inDataStep']);\n    define('def', 'if while until for do do; end end; then else cancel', ['inDataStep']);\n    define('def', 'label format _n_ _error_', ['inDataStep']);\n    define('def', 'ALTER BUFNO BUFSIZE CNTLLEV COMPRESS DLDMGACTION ENCRYPT ENCRYPTKEY EXTENDOBSCOUNTER GENMAX GENNUM INDEX LABEL OBSBUF OUTREP PW PWREQ READ REPEMPTY REPLACE REUSE ROLE SORTEDBY SPILL TOBSNO TYPE WRITE FILECLOSE FIRSTOBS IN OBS POINTOBS WHERE WHEREUP IDXNAME IDXWHERE DROP KEEP RENAME', ['inDataStep']);\n    define('def', 'filevar finfo finv fipname fipnamel fipstate first firstobs floor', ['inDataStep']);\n    define('def', 'varfmt varinfmt varlabel varlen varname varnum varray varrayx vartype verify vformat vformatd vformatdx vformatn vformatnx vformatw vformatwx vformatx vinarray vinarrayx vinformat vinformatd vinformatdx vinformatn vinformatnx vinformatw vinformatwx vinformatx vlabel vlabelx vlength vlengthx vname vnamex vnferr vtype vtypex weekday', ['inDataStep']);\n    define('def', 'zipfips zipname zipnamel zipstate', ['inDataStep']);\n    define('def', 'put putc putn', ['inDataStep']);\n    define('builtin', 'data run', ['inDataStep']);\n\n\n    //proc\n    define('def', 'data', ['inProc']);\n\n    // flow control for macros\n    define('def', '%if %end %end; %else %else; %do %do; %then', ['inMacro']);\n\n    //everywhere\n    define('builtin', 'proc run; quit; libname filename %macro %mend option options', ['ALL']);\n\n    define('def', 'footnote title libname ods', ['ALL']);\n    define('def', '%let %put %global %sysfunc %eval ', ['ALL']);\n    // automatic macro variables http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#a003167023.htm\n    define('variable', '&sysbuffr &syscc &syscharwidth &syscmd &sysdate &sysdate9 &sysday &sysdevic &sysdmg &sysdsn &sysencoding &sysenv &syserr &syserrortext &sysfilrc &syshostname &sysindex &sysinfo &sysjobid &syslast &syslckrc &syslibrc &syslogapplname &sysmacroname &sysmenv &sysmsg &sysncpu &sysodspath &sysparm &syspbuff &sysprocessid &sysprocessname &sysprocname &sysrc &sysscp &sysscpl &sysscpl &syssite &sysstartid &sysstartname &systcpiphostname &systime &sysuserid &sysver &sysvlong &sysvlong4 &syswarningtext', ['ALL']);\n\n    //footnote[1-9]? title[1-9]?\n\n    //options statement\n    define('def', 'source2 nosource2 page pageno pagesize', ['ALL']);\n\n    //proc and datastep\n    define('def', '_all_ _character_ _cmd_ _freq_ _i_ _infile_ _last_ _msg_ _null_ _numeric_ _temporary_ _type_ abort abs addr adjrsq airy alpha alter altlog altprint and arcos array arsin as atan attrc attrib attrn authserver autoexec awscontrol awsdef awsmenu awsmenumerge awstitle backward band base betainv between blocksize blshift bnot bor brshift bufno bufsize bxor by byerr byline byte calculated call cards cards4 catcache cbufno cdf ceil center cexist change chisq cinv class cleanup close cnonct cntllev coalesce codegen col collate collin column comamid comaux1 comaux2 comdef compbl compound compress config continue convert cos cosh cpuid create cross crosstab css curobs cv daccdb daccdbsl daccsl daccsyd dacctab dairy datalines datalines4 datejul datepart datetime day dbcslang dbcstype dclose ddfm ddm delete delimiter depdb depdbsl depsl depsyd deptab dequote descending descript design= device dflang dhms dif digamma dim dinfo display distinct dkricond dkrocond dlm dnum do dopen doptname doptnum dread drop dropnote dsname dsnferr echo else emaildlg emailid emailpw emailserver emailsys encrypt end endsas engine eof eov erf erfc error errorcheck errors exist exp fappend fclose fcol fdelete feedback fetch fetchobs fexist fget file fileclose fileexist filefmt filename fileref  fmterr fmtsearch fnonct fnote font fontalias  fopen foptname foptnum force formatted formchar formdelim formdlim forward fpoint fpos fput fread frewind frlen from fsep fuzz fwrite gaminv gamma getoption getvarc getvarn go goto group gwindow hbar hbound helpenv helploc hms honorappearance hosthelp hostprint hour hpct html hvar ibessel ibr id if index indexc indexw initcmd initstmt inner input inputc inputn inr insert int intck intnx into intrr invaliddata irr is jbessel join juldate keep kentb kurtosis label lag last lbound leave left length levels lgamma lib  library libref line linesize link list log log10 log2 logpdf logpmf logsdf lostcard lowcase lrecl ls macro macrogen maps mautosource max maxdec maxr mdy mean measures median memtype merge merror min minute missing missover mlogic mod mode model modify month mopen mort mprint mrecall msglevel msymtabmax mvarsize myy n nest netpv new news nmiss no nobatch nobs nocaps nocardimage nocenter nocharcode nocmdmac nocol nocum nodate nodbcs nodetails nodmr nodms nodmsbatch nodup nodupkey noduplicates noechoauto noequals noerrorabend noexitwindows nofullstimer noicon noimplmac noint nolist noloadlist nomiss nomlogic nomprint nomrecall nomsgcase nomstored nomultenvappl nonotes nonumber noobs noovp nopad nopercent noprint noprintinit normal norow norsasuser nosetinit  nosplash nosymbolgen note notes notitle notitles notsorted noverbose noxsync noxwait npv null number numkeys nummousekeys nway obs  on open     order ordinal otherwise out outer outp= output over ovp p(1 5 10 25 50 75 90 95 99) pad pad2  paired parm parmcards path pathdll pathname pdf peek peekc pfkey pmf point poisson poke position printer probbeta probbnml probchi probf probgam probhypr probit probnegb probnorm probsig probt procleave prt ps  pw pwreq qtr quote r ranbin rancau random ranexp rangam range ranks rannor ranpoi rantbl rantri ranuni rcorr read recfm register regr remote remove rename repeat repeated replace resolve retain return reuse reverse rewind right round rsquare rtf rtrace rtraceloc s s2 samploc sasautos sascontrol sasfrscr sasmsg sasmstore sasscript sasuser saving scan sdf second select selection separated seq serror set setcomm setot sign simple sin sinh siteinfo skewness skip sle sls sortedby sortpgm sortseq sortsize soundex  spedis splashlocation split spool sqrt start std stderr stdin stfips stimer stname stnamel stop stopover sub subgroup subpopn substr sum sumwgt symbol symbolgen symget symput sysget sysin sysleave sysmsg sysparm sysprint sysprintfont sysprod sysrc system t table tables tan tanh tapeclose tbufsize terminal test then timepart tinv  tnonct to today tol tooldef totper transformout translate trantab tranwrd trigamma trim trimn trunc truncover type unformatted uniform union until upcase update user usericon uss validate value var  weight when where while wincharset window work workinit workterm write wsum xsync xwait yearcutoff yes yyq  min max', ['inDataStep', 'inProc']);\n    define('operator', 'and not ', ['inDataStep', 'inProc']);\n\n    // Main function\n    function tokenize(stream, state) {\n      // Finally advance the stream\n      var ch = stream.next();\n\n      // BLOCKCOMMENT\n      if (ch === '/' && stream.eat('*')) {\n        state.continueComment = true;\n        return \"comment\";\n      } else if (state.continueComment === true) { // in comment block\n        //comment ends at the beginning of the line\n        if (ch === '*' && stream.peek() === '/') {\n          stream.next();\n          state.continueComment = false;\n        } else if (stream.skipTo('*')) { //comment is potentially later in line\n          stream.skipTo('*');\n          stream.next();\n          if (stream.eat('/'))\n            state.continueComment = false;\n        } else {\n          stream.skipToEnd();\n        }\n        return \"comment\";\n      }\n\n      if (ch == \"*\" && stream.column() == stream.indentation()) {\n        stream.skipToEnd()\n        return \"comment\"\n      }\n\n      // DoubleOperator match\n      var doubleOperator = ch + stream.peek();\n\n      if ((ch === '\"' || ch === \"'\") && !state.continueString) {\n        state.continueString = ch\n        return \"string\"\n      } else if (state.continueString) {\n        if (state.continueString == ch) {\n          state.continueString = null;\n        } else if (stream.skipTo(state.continueString)) {\n          // quote found on this line\n          stream.next();\n          state.continueString = null;\n        } else {\n          stream.skipToEnd();\n        }\n        return \"string\";\n      } else if (state.continueString !== null && stream.eol()) {\n        stream.skipTo(state.continueString) || stream.skipToEnd();\n        return \"string\";\n      } else if (/[\\d\\.]/.test(ch)) { //find numbers\n        if (ch === \".\")\n          stream.match(/^[0-9]+([eE][\\-+]?[0-9]+)?/);\n        else if (ch === \"0\")\n          stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);\n        else\n          stream.match(/^[0-9]*\\.?[0-9]*([eE][\\-+]?[0-9]+)?/);\n        return \"number\";\n      } else if (isDoubleOperatorChar.test(ch + stream.peek())) { // TWO SYMBOL TOKENS\n        stream.next();\n        return \"operator\";\n      } else if (isDoubleOperatorSym.hasOwnProperty(doubleOperator)) {\n        stream.next();\n        if (stream.peek() === ' ')\n          return isDoubleOperatorSym[doubleOperator.toLowerCase()];\n      } else if (isSingleOperatorChar.test(ch)) { // SINGLE SYMBOL TOKENS\n        return \"operator\";\n      }\n\n      // Matches one whole word -- even if the word is a character\n      var word;\n      if (stream.match(/[%&;\\w]+/, false) != null) {\n        word = ch + stream.match(/[%&;\\w]+/, true);\n        if (/&/.test(word)) return 'variable'\n      } else {\n        word = ch;\n      }\n      // the word after DATA PROC or MACRO\n      if (state.nextword) {\n        stream.match(/[\\w]+/);\n        // match memname.libname\n        if (stream.peek() === '.') stream.skipTo(' ');\n        state.nextword = false;\n        return 'variable-2';\n      }\n\n      word = word.toLowerCase()\n      // Are we in a DATA Step?\n      if (state.inDataStep) {\n        if (word === 'run;' || stream.match(/run\\s;/)) {\n          state.inDataStep = false;\n          return 'builtin';\n        }\n        // variable formats\n        if ((word) && stream.next() === '.') {\n          //either a format or libname.memname\n          if (/\\w/.test(stream.peek())) return 'variable-2';\n          else return 'variable';\n        }\n        // do we have a DATA Step keyword\n        if (word && words.hasOwnProperty(word) &&\n            (words[word].state.indexOf(\"inDataStep\") !== -1 ||\n             words[word].state.indexOf(\"ALL\") !== -1)) {\n          //backup to the start of the word\n          if (stream.start < stream.pos)\n            stream.backUp(stream.pos - stream.start);\n          //advance the length of the word and return\n          for (var i = 0; i < word.length; ++i) stream.next();\n          return words[word].style;\n        }\n      }\n      // Are we in an Proc statement?\n      if (state.inProc) {\n        if (word === 'run;' || word === 'quit;') {\n          state.inProc = false;\n          return 'builtin';\n        }\n        // do we have a proc keyword\n        if (word && words.hasOwnProperty(word) &&\n            (words[word].state.indexOf(\"inProc\") !== -1 ||\n             words[word].state.indexOf(\"ALL\") !== -1)) {\n          stream.match(/[\\w]+/);\n          return words[word].style;\n        }\n      }\n      // Are we in a Macro statement?\n      if (state.inMacro) {\n        if (word === '%mend') {\n          if (stream.peek() === ';') stream.next();\n          state.inMacro = false;\n          return 'builtin';\n        }\n        if (word && words.hasOwnProperty(word) &&\n            (words[word].state.indexOf(\"inMacro\") !== -1 ||\n             words[word].state.indexOf(\"ALL\") !== -1)) {\n          stream.match(/[\\w]+/);\n          return words[word].style;\n        }\n\n        return 'atom';\n      }\n      // Do we have Keywords specific words?\n      if (word && words.hasOwnProperty(word)) {\n        // Negates the initial next()\n        stream.backUp(1);\n        // Actually move the stream\n        stream.match(/[\\w]+/);\n        if (word === 'data' && /=/.test(stream.peek()) === false) {\n          state.inDataStep = true;\n          state.nextword = true;\n          return 'builtin';\n        }\n        if (word === 'proc') {\n          state.inProc = true;\n          state.nextword = true;\n          return 'builtin';\n        }\n        if (word === '%macro') {\n          state.inMacro = true;\n          state.nextword = true;\n          return 'builtin';\n        }\n        if (/title[1-9]/.test(word)) return 'def';\n\n        if (word === 'footnote') {\n          stream.eat(/[1-9]/);\n          return 'def';\n        }\n\n        // Returns their value as state in the prior define methods\n        if (state.inDataStep === true && words[word].state.indexOf(\"inDataStep\") !== -1)\n          return words[word].style;\n        if (state.inProc === true && words[word].state.indexOf(\"inProc\") !== -1)\n          return words[word].style;\n        if (state.inMacro === true && words[word].state.indexOf(\"inMacro\") !== -1)\n          return words[word].style;\n        if (words[word].state.indexOf(\"ALL\") !== -1)\n          return words[word].style;\n        return null;\n      }\n      // Unrecognized syntax\n      return null;\n    }\n\n    return {\n      startState: function () {\n        return {\n          inDataStep: false,\n          inProc: false,\n          inMacro: false,\n          nextword: false,\n          continueString: null,\n          continueComment: false\n        };\n      },\n      token: function (stream, state) {\n        // Strip the spaces, but regex will account for them either way\n        if (stream.eatSpace()) return null;\n        // Go through the main process\n        return tokenize(stream, state);\n      },\n\n      blockCommentStart: \"/*\",\n      blockCommentEnd: \"*/\"\n    };\n\n  });\n\n  CodeMirror.defineMIME(\"text/x-sas\", \"sas\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/sass/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Sass mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"../css/css.js\"></script>\n<script src=\"sass.js\"></script>\n<style>.CodeMirror {border: 1px solid #ddd; font-size:12px; height: 400px}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Sass</a>\n  </ul>\n</div>\n\n<article>\n<h2>Sass mode</h2>\n<form><textarea id=\"code\" name=\"code\">// Variable Definitions\n\n$page-width:    800px\n$sidebar-width: 200px\n$primary-color: #eeeeee\n\n// Global Attributes\n\nbody\n  font:\n    family: sans-serif\n    size: 30em\n    weight: bold\n\n// Scoped Styles\n\n#contents\n  width: $page-width\n  #sidebar\n    float: right\n    width: $sidebar-width\n  #main\n    width: $page-width - $sidebar-width\n    background: $primary-color\n    h2\n      color: blue\n\n#footer\n  height: 200px\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers : true,\n        matchBrackets : true,\n        mode: \"sass\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-sass</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/sass/sass.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../css/css\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../css/css\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"sass\", function(config) {\n  var cssMode = CodeMirror.mimeModes[\"text/css\"];\n  var propertyKeywords = cssMode.propertyKeywords || {},\n      colorKeywords = cssMode.colorKeywords || {},\n      valueKeywords = cssMode.valueKeywords || {},\n      fontProperties = cssMode.fontProperties || {};\n\n  function tokenRegexp(words) {\n    return new RegExp(\"^\" + words.join(\"|\"));\n  }\n\n  var keywords = [\"true\", \"false\", \"null\", \"auto\"];\n  var keywordsRegexp = new RegExp(\"^\" + keywords.join(\"|\"));\n\n  var operators = [\"\\\\(\", \"\\\\)\", \"=\", \">\", \"<\", \"==\", \">=\", \"<=\", \"\\\\+\", \"-\",\n                   \"\\\\!=\", \"/\", \"\\\\*\", \"%\", \"and\", \"or\", \"not\", \";\",\"\\\\{\",\"\\\\}\",\":\"];\n  var opRegexp = tokenRegexp(operators);\n\n  var pseudoElementsRegexp = /^::?[a-zA-Z_][\\w\\-]*/;\n\n  var word;\n\n  function isEndLine(stream) {\n    return !stream.peek() || stream.match(/\\s+$/, false);\n  }\n\n  function urlTokens(stream, state) {\n    var ch = stream.peek();\n\n    if (ch === \")\") {\n      stream.next();\n      state.tokenizer = tokenBase;\n      return \"operator\";\n    } else if (ch === \"(\") {\n      stream.next();\n      stream.eatSpace();\n\n      return \"operator\";\n    } else if (ch === \"'\" || ch === '\"') {\n      state.tokenizer = buildStringTokenizer(stream.next());\n      return \"string\";\n    } else {\n      state.tokenizer = buildStringTokenizer(\")\", false);\n      return \"string\";\n    }\n  }\n  function comment(indentation, multiLine) {\n    return function(stream, state) {\n      if (stream.sol() && stream.indentation() <= indentation) {\n        state.tokenizer = tokenBase;\n        return tokenBase(stream, state);\n      }\n\n      if (multiLine && stream.skipTo(\"*/\")) {\n        stream.next();\n        stream.next();\n        state.tokenizer = tokenBase;\n      } else {\n        stream.skipToEnd();\n      }\n\n      return \"comment\";\n    };\n  }\n\n  function buildStringTokenizer(quote, greedy) {\n    if (greedy == null) { greedy = true; }\n\n    function stringTokenizer(stream, state) {\n      var nextChar = stream.next();\n      var peekChar = stream.peek();\n      var previousChar = stream.string.charAt(stream.pos-2);\n\n      var endingString = ((nextChar !== \"\\\\\" && peekChar === quote) || (nextChar === quote && previousChar !== \"\\\\\"));\n\n      if (endingString) {\n        if (nextChar !== quote && greedy) { stream.next(); }\n        if (isEndLine(stream)) {\n          state.cursorHalf = 0;\n        }\n        state.tokenizer = tokenBase;\n        return \"string\";\n      } else if (nextChar === \"#\" && peekChar === \"{\") {\n        state.tokenizer = buildInterpolationTokenizer(stringTokenizer);\n        stream.next();\n        return \"operator\";\n      } else {\n        return \"string\";\n      }\n    }\n\n    return stringTokenizer;\n  }\n\n  function buildInterpolationTokenizer(currentTokenizer) {\n    return function(stream, state) {\n      if (stream.peek() === \"}\") {\n        stream.next();\n        state.tokenizer = currentTokenizer;\n        return \"operator\";\n      } else {\n        return tokenBase(stream, state);\n      }\n    };\n  }\n\n  function indent(state) {\n    if (state.indentCount == 0) {\n      state.indentCount++;\n      var lastScopeOffset = state.scopes[0].offset;\n      var currentOffset = lastScopeOffset + config.indentUnit;\n      state.scopes.unshift({ offset:currentOffset });\n    }\n  }\n\n  function dedent(state) {\n    if (state.scopes.length == 1) return;\n\n    state.scopes.shift();\n  }\n\n  function tokenBase(stream, state) {\n    var ch = stream.peek();\n\n    // Comment\n    if (stream.match(\"/*\")) {\n      state.tokenizer = comment(stream.indentation(), true);\n      return state.tokenizer(stream, state);\n    }\n    if (stream.match(\"//\")) {\n      state.tokenizer = comment(stream.indentation(), false);\n      return state.tokenizer(stream, state);\n    }\n\n    // Interpolation\n    if (stream.match(\"#{\")) {\n      state.tokenizer = buildInterpolationTokenizer(tokenBase);\n      return \"operator\";\n    }\n\n    // Strings\n    if (ch === '\"' || ch === \"'\") {\n      stream.next();\n      state.tokenizer = buildStringTokenizer(ch);\n      return \"string\";\n    }\n\n    if(!state.cursorHalf){// state.cursorHalf === 0\n    // first half i.e. before : for key-value pairs\n    // including selectors\n\n      if (ch === \"-\") {\n        if (stream.match(/^-\\w+-/)) {\n          return \"meta\";\n        }\n      }\n\n      if (ch === \".\") {\n        stream.next();\n        if (stream.match(/^[\\w-]+/)) {\n          indent(state);\n          return \"qualifier\";\n        } else if (stream.peek() === \"#\") {\n          indent(state);\n          return \"tag\";\n        }\n      }\n\n      if (ch === \"#\") {\n        stream.next();\n        // ID selectors\n        if (stream.match(/^[\\w-]+/)) {\n          indent(state);\n          return \"builtin\";\n        }\n        if (stream.peek() === \"#\") {\n          indent(state);\n          return \"tag\";\n        }\n      }\n\n      // Variables\n      if (ch === \"$\") {\n        stream.next();\n        stream.eatWhile(/[\\w-]/);\n        return \"variable-2\";\n      }\n\n      // Numbers\n      if (stream.match(/^-?[0-9\\.]+/))\n        return \"number\";\n\n      // Units\n      if (stream.match(/^(px|em|in)\\b/))\n        return \"unit\";\n\n      if (stream.match(keywordsRegexp))\n        return \"keyword\";\n\n      if (stream.match(/^url/) && stream.peek() === \"(\") {\n        state.tokenizer = urlTokens;\n        return \"atom\";\n      }\n\n      if (ch === \"=\") {\n        // Match shortcut mixin definition\n        if (stream.match(/^=[\\w-]+/)) {\n          indent(state);\n          return \"meta\";\n        }\n      }\n\n      if (ch === \"+\") {\n        // Match shortcut mixin definition\n        if (stream.match(/^\\+[\\w-]+/)){\n          return \"variable-3\";\n        }\n      }\n\n      if(ch === \"@\"){\n        if(stream.match('@extend')){\n          if(!stream.match(/\\s*[\\w]/))\n            dedent(state);\n        }\n      }\n\n\n      // Indent Directives\n      if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)) {\n        indent(state);\n        return \"def\";\n      }\n\n      // Other Directives\n      if (ch === \"@\") {\n        stream.next();\n        stream.eatWhile(/[\\w-]/);\n        return \"def\";\n      }\n\n      if (stream.eatWhile(/[\\w-]/)){\n        if(stream.match(/ *: *[\\w-\\+\\$#!\\(\"']/,false)){\n          word = stream.current().toLowerCase();\n          var prop = state.prevProp + \"-\" + word;\n          if (propertyKeywords.hasOwnProperty(prop)) {\n            return \"property\";\n          } else if (propertyKeywords.hasOwnProperty(word)) {\n            state.prevProp = word;\n            return \"property\";\n          } else if (fontProperties.hasOwnProperty(word)) {\n            return \"property\";\n          }\n          return \"tag\";\n        }\n        else if(stream.match(/ *:/,false)){\n          indent(state);\n          state.cursorHalf = 1;\n          state.prevProp = stream.current().toLowerCase();\n          return \"property\";\n        }\n        else if(stream.match(/ *,/,false)){\n          return \"tag\";\n        }\n        else{\n          indent(state);\n          return \"tag\";\n        }\n      }\n\n      if(ch === \":\"){\n        if (stream.match(pseudoElementsRegexp)){ // could be a pseudo-element\n          return \"variable-3\";\n        }\n        stream.next();\n        state.cursorHalf=1;\n        return \"operator\";\n      }\n\n    } // cursorHalf===0 ends here\n    else{\n\n      if (ch === \"#\") {\n        stream.next();\n        // Hex numbers\n        if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){\n          if (isEndLine(stream)) {\n            state.cursorHalf = 0;\n          }\n          return \"number\";\n        }\n      }\n\n      // Numbers\n      if (stream.match(/^-?[0-9\\.]+/)){\n        if (isEndLine(stream)) {\n          state.cursorHalf = 0;\n        }\n        return \"number\";\n      }\n\n      // Units\n      if (stream.match(/^(px|em|in)\\b/)){\n        if (isEndLine(stream)) {\n          state.cursorHalf = 0;\n        }\n        return \"unit\";\n      }\n\n      if (stream.match(keywordsRegexp)){\n        if (isEndLine(stream)) {\n          state.cursorHalf = 0;\n        }\n        return \"keyword\";\n      }\n\n      if (stream.match(/^url/) && stream.peek() === \"(\") {\n        state.tokenizer = urlTokens;\n        if (isEndLine(stream)) {\n          state.cursorHalf = 0;\n        }\n        return \"atom\";\n      }\n\n      // Variables\n      if (ch === \"$\") {\n        stream.next();\n        stream.eatWhile(/[\\w-]/);\n        if (isEndLine(stream)) {\n          state.cursorHalf = 0;\n        }\n        return \"variable-2\";\n      }\n\n      // bang character for !important, !default, etc.\n      if (ch === \"!\") {\n        stream.next();\n        state.cursorHalf = 0;\n        return stream.match(/^[\\w]+/) ? \"keyword\": \"operator\";\n      }\n\n      if (stream.match(opRegexp)){\n        if (isEndLine(stream)) {\n          state.cursorHalf = 0;\n        }\n        return \"operator\";\n      }\n\n      // attributes\n      if (stream.eatWhile(/[\\w-]/)) {\n        if (isEndLine(stream)) {\n          state.cursorHalf = 0;\n        }\n        word = stream.current().toLowerCase();\n        if (valueKeywords.hasOwnProperty(word)) {\n          return \"atom\";\n        } else if (colorKeywords.hasOwnProperty(word)) {\n          return \"keyword\";\n        } else if (propertyKeywords.hasOwnProperty(word)) {\n          state.prevProp = stream.current().toLowerCase();\n          return \"property\";\n        } else {\n          return \"tag\";\n        }\n      }\n\n      //stream.eatSpace();\n      if (isEndLine(stream)) {\n        state.cursorHalf = 0;\n        return null;\n      }\n\n    } // else ends here\n\n    if (stream.match(opRegexp))\n      return \"operator\";\n\n    // If we haven't returned by now, we move 1 character\n    // and return an error\n    stream.next();\n    return null;\n  }\n\n  function tokenLexer(stream, state) {\n    if (stream.sol()) state.indentCount = 0;\n    var style = state.tokenizer(stream, state);\n    var current = stream.current();\n\n    if (current === \"@return\" || current === \"}\"){\n      dedent(state);\n    }\n\n    if (style !== null) {\n      var startOfToken = stream.pos - current.length;\n\n      var withCurrentIndent = startOfToken + (config.indentUnit * state.indentCount);\n\n      var newScopes = [];\n\n      for (var i = 0; i < state.scopes.length; i++) {\n        var scope = state.scopes[i];\n\n        if (scope.offset <= withCurrentIndent)\n          newScopes.push(scope);\n      }\n\n      state.scopes = newScopes;\n    }\n\n\n    return style;\n  }\n\n  return {\n    startState: function() {\n      return {\n        tokenizer: tokenBase,\n        scopes: [{offset: 0, type: \"sass\"}],\n        indentCount: 0,\n        cursorHalf: 0,  // cursor half tells us if cursor lies after (1)\n                        // or before (0) colon (well... more or less)\n        definedVars: [],\n        definedMixins: []\n      };\n    },\n    token: function(stream, state) {\n      var style = tokenLexer(stream, state);\n\n      state.lastToken = { style: style, content: stream.current() };\n\n      return style;\n    },\n\n    indent: function(state) {\n      return state.scopes[0].offset;\n    },\n\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    lineComment: \"//\",\n    fold: \"indent\"\n  };\n}, \"css\");\n\nCodeMirror.defineMIME(\"text/x-sass\", \"sass\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/sass/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"sass\");\n  // Since Sass has an indent-based syntax, is almost impossible to test correctly the indentation in all cases.\n  // So disable it for tests.\n  mode.indent = undefined;\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"comment\",\n     \"[comment // this is a comment]\",\n     \"[comment   also this is a comment]\")\n\n  MT(\"comment_multiline\",\n     \"[comment /* this is a comment]\",\n     \"[comment   also this is a comment]\")\n\n  MT(\"variable\",\n     \"[variable-2 $page-width][operator :] [number 800][unit px]\")\n\n  MT(\"global_attributes\",\n     \"[tag body]\",\n     \"  [property font][operator :]\",\n     \"    [property family][operator :] [atom sans-serif]\",\n     \"    [property size][operator :] [number 30][unit em]\",\n     \"    [property weight][operator :] [atom bold]\")\n\n  MT(\"scoped_styles\",\n     \"[builtin #contents]\",\n     \"  [property width][operator :] [variable-2 $page-width]\",\n     \"  [builtin #sidebar]\",\n     \"    [property float][operator :] [atom right]\",\n     \"    [property width][operator :] [variable-2 $sidebar-width]\",\n     \"  [builtin #main]\",\n     \"    [property width][operator :] [variable-2 $page-width] [operator -] [variable-2 $sidebar-width]\",\n     \"    [property background][operator :] [variable-2 $primary-color]\",\n     \"    [tag h2]\",\n     \"      [property color][operator :] [keyword blue]\")\n\n  // Sass allows to write the colon as first char instead of a \"separator\".\n  //   :color red\n  // Not supported\n  // MT(\"property_syntax\",\n  //    \"[qualifier .foo]\",\n  //    \"  [operator :][property color] [keyword red]\")\n\n  MT(\"import\",\n     \"[def @import] [string \\\"sass/variables\\\"]\",\n     // Probably it should parsed as above: as a string even without the \" or '\n     // \"[def @import] [string sass/baz]\"\n     \"[def @import] [tag sass][operator /][tag baz]\")\n\n  MT(\"def\",\n     \"[def @if] [variable-2 $foo] [def @else]\")\n\n  MT(\"tag_on_more_lines\",\n    \"[tag td],\",\n    \"[tag th]\",\n    \"  [property font-family][operator :] [string \\\"Arial\\\"], [atom serif]\")\n\n  MT(\"important\",\n     \"[qualifier .foo]\",\n     \"  [property text-decoration][operator :] [atom none] [keyword !important]\",\n     \"[tag h1]\",\n     \"  [property font-size][operator :] [number 2.5][unit em]\")\n\n  MT(\"selector\",\n     // SCSS doesn't highlight the :\n     // \"[tag h1]:[variable-3 before],\",\n     // \"[tag h2]:[variable-3 before]\",\n     \"[tag h1][variable-3 :before],\",\n     \"[tag h2][variable-3 :before]\",\n     \"  [property content][operator :] [string \\\"::\\\"]\")\n\n  MT(\"definition_mixin_equal\",\n     \"[variable-2 $defined-bs-type][operator :] [atom border-box] [keyword !default]\",\n     \"[meta =bs][operator (][variable-2 $bs-type][operator :] [variable-2 $defined-bs-type][operator )]\",\n     \"  [meta -webkit-][property box-sizing][operator :] [variable-2 $bs-type]\",\n     \"  [property box-sizing][operator :] [variable-2 $bs-type]\")\n\n  MT(\"definition_mixin_with_space\",\n     \"[variable-2 $defined-bs-type][operator :] [atom border-box] [keyword !default]\",\n     \"[def @mixin] [tag bs][operator (][variable-2 $bs-type][operator :] [variable-2 $defined-bs-type][operator )] \",\n     \"  [meta -moz-][property box-sizing][operator :] [variable-2 $bs-type]\",\n     \"  [property box-sizing][operator :] [variable-2 $bs-type]\")\n\n  MT(\"numbers_start_dot_include_plus\",\n     // The % is not highlighted correctly\n     // \"[meta =button-links][operator (][variable-2 $button-base][operator :] [atom darken][operator (][variable-2 $color11], [number 10][unit %][operator )][operator )]\",\n     \"[meta =button-links][operator (][variable-2 $button-base][operator :] [atom darken][operator (][variable-2 $color11], [number 10][operator %))]\",\n     \"  [property padding][operator :] [number .3][unit em] [number .6][unit em]\",\n     \"  [variable-3 +border-radius][operator (][number 8][unit px][operator )]\",\n     \"  [property background-color][operator :] [variable-2 $button-base]\")\n\n  MT(\"include\",\n     \"[qualifier .bar]\",\n     \"  [def @include] [tag border-radius][operator (][number 8][unit px][operator )]\")\n\n  MT(\"reference_parent\",\n     \"[qualifier .col]\",\n     \"  [property clear][operator :] [atom both]\",\n     // SCSS doesn't highlight the :\n     // \"  &:[variable-3 after]\",\n     \"  &[variable-3 :after]\",\n     \"    [property content][operator :] [string '']\",\n     \"    [property clear][operator :] [atom both]\")\n\n  MT(\"reference_parent_with_spaces\",\n     \"[tag section]\",\n     \"  [property border-left][operator :]  [number 20][unit px] [atom transparent] [atom solid] \",\n     \"  &[qualifier .section3]\",\n     \"    [qualifier .title]\",\n     \"      [property color][operator :] [keyword white] \",\n     \"    [qualifier .vermas]\",\n     \"      [property display][operator :] [atom none]\")\n\n  MT(\"font_face\",\n     \"[def @font-face]\",\n     \"  [property font-family][operator :] [string 'icomoon']\",\n     \"  [property src][operator :] [atom url][operator (][string fonts/icomoon.ttf][operator )]\")\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/scheme/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Scheme mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"scheme.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Scheme</a>\n  </ul>\n</div>\n\n<article>\n<h2>Scheme mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n; See if the input starts with a given symbol.\n(define (match-symbol input pattern)\n  (cond ((null? (remain input)) #f)\n\t((eqv? (car (remain input)) pattern) (r-cdr input))\n\t(else #f)))\n\n; Allow the input to start with one of a list of patterns.\n(define (match-or input pattern)\n  (cond ((null? pattern) #f)\n\t((match-pattern input (car pattern)))\n\t(else (match-or input (cdr pattern)))))\n\n; Allow a sequence of patterns.\n(define (match-seq input pattern)\n  (if (null? pattern)\n      input\n      (let ((match (match-pattern input (car pattern))))\n\t(if match (match-seq match (cdr pattern)) #f))))\n\n; Match with the pattern but no problem if it does not match.\n(define (match-opt input pattern)\n  (let ((match (match-pattern input (car pattern))))\n    (if match match input)))\n\n; Match anything (other than '()), until pattern is found. The rather\n; clumsy form of requiring an ending pattern is needed to decide where\n; the end of the match is. If none is given, this will match the rest\n; of the sentence.\n(define (match-any input pattern)\n  (cond ((null? (remain input)) #f)\n\t((null? pattern) (f-cons (remain input) (clear-remain input)))\n\t(else\n\t (let ((accum-any (collector)))\n\t   (define (match-pattern-any input pattern)\n\t     (cond ((null? (remain input)) #f)\n\t\t   (else (accum-any (car (remain input)))\n\t\t\t (cond ((match-pattern (r-cdr input) pattern))\n\t\t\t       (else (match-pattern-any (r-cdr input) pattern))))))\n\t   (let ((retval (match-pattern-any input (car pattern))))\n\t     (if retval\n\t\t (f-cons (accum-any) retval)\n\t\t #f))))))\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-scheme</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/scheme/scheme.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/**\n * Author: Koh Zi Han, based on implementation by Koh Zi Chun\n * Improved by: Jakub T. Jankiewicz\n */\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"scheme\", function () {\n    var BUILTIN = \"builtin\", COMMENT = \"comment\", STRING = \"string\",\n        SYMBOL = \"symbol\", ATOM = \"atom\", NUMBER = \"number\", BRACKET = \"bracket\";\n    var INDENT_WORD_SKIP = 2;\n\n    function makeKeywords(str) {\n        var obj = {}, words = str.split(\" \");\n        for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n        return obj;\n    }\n\n    var keywords = makeKeywords(\"λ case-lambda call/cc class cond-expand define-class define-values exit-handler field import inherit init-field interface let*-values let-values let/ec mixin opt-lambda override protect provide public rename require require-for-syntax syntax syntax-case syntax-error unit/sig unless when with-syntax and begin call-with-current-continuation call-with-input-file call-with-output-file case cond define define-syntax define-macro defmacro delay do dynamic-wind else for-each if lambda let let* let-syntax letrec letrec-syntax map or syntax-rules abs acos angle append apply asin assoc assq assv atan boolean? caar cadr call-with-input-file call-with-output-file call-with-values car cdddar cddddr cdr ceiling char->integer char-alphabetic? char-ci<=? char-ci<? char-ci=? char-ci>=? char-ci>? char-downcase char-lower-case? char-numeric? char-ready? char-upcase char-upper-case? char-whitespace? char<=? char<? char=? char>=? char>? char? close-input-port close-output-port complex? cons cos current-input-port current-output-port denominator display eof-object? eq? equal? eqv? eval even? exact->inexact exact? exp expt #f floor force gcd imag-part inexact->exact inexact? input-port? integer->char integer? interaction-environment lcm length list list->string list->vector list-ref list-tail list? load log magnitude make-polar make-rectangular make-string make-vector max member memq memv min modulo negative? newline not null-environment null? number->string number? numerator odd? open-input-file open-output-file output-port? pair? peek-char port? positive? procedure? quasiquote quote quotient rational? rationalize read read-char real-part real? remainder reverse round scheme-report-environment set! set-car! set-cdr! sin sqrt string string->list string->number string->symbol string-append string-ci<=? string-ci<? string-ci=? string-ci>=? string-ci>? string-copy string-fill! string-length string-ref string-set! string<=? string<? string=? string>=? string>? string? substring symbol->string symbol? #t tan transcript-off transcript-on truncate values vector vector->list vector-fill! vector-length vector-ref vector-set! with-input-from-file with-output-to-file write write-char zero?\");\n    var indentKeys = makeKeywords(\"define let letrec let* lambda define-macro defmacro let-syntax letrec-syntax let-values let*-values define-syntax syntax-rules define-values when unless\");\n\n    function stateStack(indent, type, prev) { // represents a state stack object\n        this.indent = indent;\n        this.type = type;\n        this.prev = prev;\n    }\n\n    function pushStack(state, indent, type) {\n        state.indentStack = new stateStack(indent, type, state.indentStack);\n    }\n\n    function popStack(state) {\n        state.indentStack = state.indentStack.prev;\n    }\n\n    var binaryMatcher = new RegExp(/^(?:[-+]i|[-+][01]+#*(?:\\/[01]+#*)?i|[-+]?[01]+#*(?:\\/[01]+#*)?@[-+]?[01]+#*(?:\\/[01]+#*)?|[-+]?[01]+#*(?:\\/[01]+#*)?[-+](?:[01]+#*(?:\\/[01]+#*)?)?i|[-+]?[01]+#*(?:\\/[01]+#*)?)(?=[()\\s;\"]|$)/i);\n    var octalMatcher = new RegExp(/^(?:[-+]i|[-+][0-7]+#*(?:\\/[0-7]+#*)?i|[-+]?[0-7]+#*(?:\\/[0-7]+#*)?@[-+]?[0-7]+#*(?:\\/[0-7]+#*)?|[-+]?[0-7]+#*(?:\\/[0-7]+#*)?[-+](?:[0-7]+#*(?:\\/[0-7]+#*)?)?i|[-+]?[0-7]+#*(?:\\/[0-7]+#*)?)(?=[()\\s;\"]|$)/i);\n    var hexMatcher = new RegExp(/^(?:[-+]i|[-+][\\da-f]+#*(?:\\/[\\da-f]+#*)?i|[-+]?[\\da-f]+#*(?:\\/[\\da-f]+#*)?@[-+]?[\\da-f]+#*(?:\\/[\\da-f]+#*)?|[-+]?[\\da-f]+#*(?:\\/[\\da-f]+#*)?[-+](?:[\\da-f]+#*(?:\\/[\\da-f]+#*)?)?i|[-+]?[\\da-f]+#*(?:\\/[\\da-f]+#*)?)(?=[()\\s;\"]|$)/i);\n    var decimalMatcher = new RegExp(/^(?:[-+]i|[-+](?:(?:(?:\\d+#+\\.?#*|\\d+\\.\\d*#*|\\.\\d+#*|\\d+)(?:[esfdl][-+]?\\d+)?)|\\d+#*\\/\\d+#*)i|[-+]?(?:(?:(?:\\d+#+\\.?#*|\\d+\\.\\d*#*|\\.\\d+#*|\\d+)(?:[esfdl][-+]?\\d+)?)|\\d+#*\\/\\d+#*)@[-+]?(?:(?:(?:\\d+#+\\.?#*|\\d+\\.\\d*#*|\\.\\d+#*|\\d+)(?:[esfdl][-+]?\\d+)?)|\\d+#*\\/\\d+#*)|[-+]?(?:(?:(?:\\d+#+\\.?#*|\\d+\\.\\d*#*|\\.\\d+#*|\\d+)(?:[esfdl][-+]?\\d+)?)|\\d+#*\\/\\d+#*)[-+](?:(?:(?:\\d+#+\\.?#*|\\d+\\.\\d*#*|\\.\\d+#*|\\d+)(?:[esfdl][-+]?\\d+)?)|\\d+#*\\/\\d+#*)?i|(?:(?:(?:\\d+#+\\.?#*|\\d+\\.\\d*#*|\\.\\d+#*|\\d+)(?:[esfdl][-+]?\\d+)?)|\\d+#*\\/\\d+#*))(?=[()\\s;\"]|$)/i);\n\n    function isBinaryNumber (stream) {\n        return stream.match(binaryMatcher);\n    }\n\n    function isOctalNumber (stream) {\n        return stream.match(octalMatcher);\n    }\n\n    function isDecimalNumber (stream, backup) {\n        if (backup === true) {\n            stream.backUp(1);\n        }\n        return stream.match(decimalMatcher);\n    }\n\n    function isHexNumber (stream) {\n        return stream.match(hexMatcher);\n    }\n\n    function processEscapedSequence(stream, options) {\n        var next, escaped = false;\n        while ((next = stream.next()) != null) {\n            if (next == options.token && !escaped) {\n\n                options.state.mode = false;\n                break;\n            }\n            escaped = !escaped && next == \"\\\\\";\n        }\n    }\n\n    return {\n        startState: function () {\n            return {\n                indentStack: null,\n                indentation: 0,\n                mode: false,\n                sExprComment: false,\n                sExprQuote: false\n            };\n        },\n\n        token: function (stream, state) {\n            if (state.indentStack == null && stream.sol()) {\n                // update indentation, but only if indentStack is empty\n                state.indentation = stream.indentation();\n            }\n\n            // skip spaces\n            if (stream.eatSpace()) {\n                return null;\n            }\n            var returnType = null;\n\n            switch(state.mode){\n                case \"string\": // multi-line string parsing mode\n                    processEscapedSequence(stream, {\n                        token: \"\\\"\",\n                        state: state\n                    });\n                    returnType = STRING; // continue on in scheme-string mode\n                    break;\n                case \"symbol\": // escape symbol\n                    processEscapedSequence(stream, {\n                        token: \"|\",\n                        state: state\n                    });\n                    returnType = SYMBOL; // continue on in scheme-symbol mode\n                    break;\n                case \"comment\": // comment parsing mode\n                    var next, maybeEnd = false;\n                    while ((next = stream.next()) != null) {\n                        if (next == \"#\" && maybeEnd) {\n\n                            state.mode = false;\n                            break;\n                        }\n                        maybeEnd = (next == \"|\");\n                    }\n                    returnType = COMMENT;\n                    break;\n                case \"s-expr-comment\": // s-expr commenting mode\n                    state.mode = false;\n                    if(stream.peek() == \"(\" || stream.peek() == \"[\"){\n                        // actually start scheme s-expr commenting mode\n                        state.sExprComment = 0;\n                    }else{\n                        // if not we just comment the entire of the next token\n                        stream.eatWhile(/[^\\s\\(\\)\\[\\]]/); // eat symbol atom\n                        returnType = COMMENT;\n                        break;\n                    }\n                default: // default parsing mode\n                    var ch = stream.next();\n\n                    if (ch == \"\\\"\") {\n                        state.mode = \"string\";\n                        returnType = STRING;\n\n                    } else if (ch == \"'\") {\n                        if (stream.peek() == \"(\" || stream.peek() == \"[\"){\n                            if (typeof state.sExprQuote != \"number\") {\n                                state.sExprQuote = 0;\n                            } // else already in a quoted expression\n                            returnType = ATOM;\n                        } else {\n                            stream.eatWhile(/[\\w_\\-!$%&*+\\.\\/:<=>?@\\^~]/);\n                            returnType = ATOM;\n                        }\n                    } else if (ch == '|') {\n                        state.mode = \"symbol\";\n                        returnType = SYMBOL;\n                    } else if (ch == '#') {\n                        if (stream.eat(\"|\")) {                    // Multi-line comment\n                            state.mode = \"comment\"; // toggle to comment mode\n                            returnType = COMMENT;\n                        } else if (stream.eat(/[tf]/i)) {            // #t/#f (atom)\n                            returnType = ATOM;\n                        } else if (stream.eat(';')) {                // S-Expr comment\n                            state.mode = \"s-expr-comment\";\n                            returnType = COMMENT;\n                        } else {\n                            var numTest = null, hasExactness = false, hasRadix = true;\n                            if (stream.eat(/[ei]/i)) {\n                                hasExactness = true;\n                            } else {\n                                stream.backUp(1);       // must be radix specifier\n                            }\n                            if (stream.match(/^#b/i)) {\n                                numTest = isBinaryNumber;\n                            } else if (stream.match(/^#o/i)) {\n                                numTest = isOctalNumber;\n                            } else if (stream.match(/^#x/i)) {\n                                numTest = isHexNumber;\n                            } else if (stream.match(/^#d/i)) {\n                                numTest = isDecimalNumber;\n                            } else if (stream.match(/^[-+0-9.]/, false)) {\n                                hasRadix = false;\n                                numTest = isDecimalNumber;\n                            // re-consume the initial # if all matches failed\n                            } else if (!hasExactness) {\n                                stream.eat('#');\n                            }\n                            if (numTest != null) {\n                                if (hasRadix && !hasExactness) {\n                                    // consume optional exactness after radix\n                                    stream.match(/^#[ei]/i);\n                                }\n                                if (numTest(stream))\n                                    returnType = NUMBER;\n                            }\n                        }\n                    } else if (/^[-+0-9.]/.test(ch) && isDecimalNumber(stream, true)) { // match non-prefixed number, must be decimal\n                        returnType = NUMBER;\n                    } else if (ch == \";\") { // comment\n                        stream.skipToEnd(); // rest of the line is a comment\n                        returnType = COMMENT;\n                    } else if (ch == \"(\" || ch == \"[\") {\n                      var keyWord = ''; var indentTemp = stream.column(), letter;\n                        /**\n                        Either\n                        (indent-word ..\n                        (non-indent-word ..\n                        (;something else, bracket, etc.\n                        */\n\n                        while ((letter = stream.eat(/[^\\s\\(\\[\\;\\)\\]]/)) != null) {\n                            keyWord += letter;\n                        }\n\n                        if (keyWord.length > 0 && indentKeys.propertyIsEnumerable(keyWord)) { // indent-word\n\n                            pushStack(state, indentTemp + INDENT_WORD_SKIP, ch);\n                        } else { // non-indent word\n                            // we continue eating the spaces\n                            stream.eatSpace();\n                            if (stream.eol() || stream.peek() == \";\") {\n                                // nothing significant after\n                                // we restart indentation 1 space after\n                                pushStack(state, indentTemp + 1, ch);\n                            } else {\n                                pushStack(state, indentTemp + stream.current().length, ch); // else we match\n                            }\n                        }\n                        stream.backUp(stream.current().length - 1); // undo all the eating\n\n                        if(typeof state.sExprComment == \"number\") state.sExprComment++;\n                        if(typeof state.sExprQuote == \"number\") state.sExprQuote++;\n\n                        returnType = BRACKET;\n                    } else if (ch == \")\" || ch == \"]\") {\n                        returnType = BRACKET;\n                        if (state.indentStack != null && state.indentStack.type == (ch == \")\" ? \"(\" : \"[\")) {\n                            popStack(state);\n\n                            if(typeof state.sExprComment == \"number\"){\n                                if(--state.sExprComment == 0){\n                                    returnType = COMMENT; // final closing bracket\n                                    state.sExprComment = false; // turn off s-expr commenting mode\n                                }\n                            }\n                            if(typeof state.sExprQuote == \"number\"){\n                                if(--state.sExprQuote == 0){\n                                    returnType = ATOM; // final closing bracket\n                                    state.sExprQuote = false; // turn off s-expr quote mode\n                                }\n                            }\n                        }\n                    } else {\n                        stream.eatWhile(/[\\w_\\-!$%&*+\\.\\/:<=>?@\\^~]/);\n\n                        if (keywords && keywords.propertyIsEnumerable(stream.current())) {\n                            returnType = BUILTIN;\n                        } else returnType = \"variable\";\n                    }\n            }\n            return (typeof state.sExprComment == \"number\") ? COMMENT : ((typeof state.sExprQuote == \"number\") ? ATOM : returnType);\n        },\n\n        indent: function (state) {\n            if (state.indentStack == null) return state.indentation;\n            return state.indentStack.indent;\n        },\n\n        fold: \"brace-paren\",\n        closeBrackets: {pairs: \"()[]{}\\\"\\\"\"},\n        lineComment: \";;\"\n    };\n});\n\nCodeMirror.defineMIME(\"text/x-scheme\", \"scheme\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/shell/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Shell mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=stylesheet href=../../lib/codemirror.css>\n<script src=../../lib/codemirror.js></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=shell.js></script>\n<style type=text/css>\n  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Shell</a>\n  </ul>\n</div>\n\n<article>\n<h2>Shell mode</h2>\n\n\n<textarea id=code>\n#!/bin/bash\n\n# clone the repository\ngit clone http://github.com/garden/tree\n\n# generate HTTPS credentials\ncd tree\nopenssl genrsa -aes256 -out https.key 1024\nopenssl req -new -nodes -key https.key -out https.csr\nopenssl x509 -req -days 365 -in https.csr -signkey https.key -out https.crt\ncp https.key{,.orig}\nopenssl rsa -in https.key.orig -out https.key\n\n# start the server in HTTPS mode\ncd web\nsudo node ../server.js 443 'yes' &gt;&gt; ../node.log &amp;\n\n# here is how to stop the server\nfor pid in `ps aux | grep 'node ../server.js' | awk '{print $2}'` ; do\n  sudo kill -9 $pid 2&gt; /dev/null\ndone\n\nexit 0</textarea>\n\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById('code'), {\n    mode: 'shell',\n    lineNumbers: true,\n    matchBrackets: true\n  });\n</script>\n\n<p><strong>MIME types defined:</strong> <code>text/x-sh</code>, <code>application/x-sh</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/shell/shell.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode('shell', function() {\n\n  var words = {};\n  function define(style, dict) {\n    for(var i = 0; i < dict.length; i++) {\n      words[dict[i]] = style;\n    }\n  };\n\n  var commonAtoms = [\"true\", \"false\"];\n  var commonKeywords = [\"if\", \"then\", \"do\", \"else\", \"elif\", \"while\", \"until\", \"for\", \"in\", \"esac\", \"fi\",\n    \"fin\", \"fil\", \"done\", \"exit\", \"set\", \"unset\", \"export\", \"function\"];\n  var commonCommands = [\"ab\", \"awk\", \"bash\", \"beep\", \"cat\", \"cc\", \"cd\", \"chown\", \"chmod\", \"chroot\", \"clear\",\n    \"cp\", \"curl\", \"cut\", \"diff\", \"echo\", \"find\", \"gawk\", \"gcc\", \"get\", \"git\", \"grep\", \"hg\", \"kill\", \"killall\",\n    \"ln\", \"ls\", \"make\", \"mkdir\", \"openssl\", \"mv\", \"nc\", \"nl\", \"node\", \"npm\", \"ping\", \"ps\", \"restart\", \"rm\",\n    \"rmdir\", \"sed\", \"service\", \"sh\", \"shopt\", \"shred\", \"source\", \"sort\", \"sleep\", \"ssh\", \"start\", \"stop\",\n    \"su\", \"sudo\", \"svn\", \"tee\", \"telnet\", \"top\", \"touch\", \"vi\", \"vim\", \"wall\", \"wc\", \"wget\", \"who\", \"write\",\n    \"yes\", \"zsh\"];\n\n  CodeMirror.registerHelper(\"hintWords\", \"shell\", commonAtoms.concat(commonKeywords, commonCommands));\n\n  define('atom', commonAtoms);\n  define('keyword', commonKeywords);\n  define('builtin', commonCommands);\n\n  function tokenBase(stream, state) {\n    if (stream.eatSpace()) return null;\n\n    var sol = stream.sol();\n    var ch = stream.next();\n\n    if (ch === '\\\\') {\n      stream.next();\n      return null;\n    }\n    if (ch === '\\'' || ch === '\"' || ch === '`') {\n      state.tokens.unshift(tokenString(ch, ch === \"`\" ? \"quote\" : \"string\"));\n      return tokenize(stream, state);\n    }\n    if (ch === '#') {\n      if (sol && stream.eat('!')) {\n        stream.skipToEnd();\n        return 'meta'; // 'comment'?\n      }\n      stream.skipToEnd();\n      return 'comment';\n    }\n    if (ch === '$') {\n      state.tokens.unshift(tokenDollar);\n      return tokenize(stream, state);\n    }\n    if (ch === '+' || ch === '=') {\n      return 'operator';\n    }\n    if (ch === '-') {\n      stream.eat('-');\n      stream.eatWhile(/\\w/);\n      return 'attribute';\n    }\n    if (ch == \"<\") {\n      if (stream.match(\"<<\")) return \"operator\"\n      var heredoc = stream.match(/^<-?\\s*['\"]?([^'\"]*)['\"]?/)\n      if (heredoc) {\n        state.tokens.unshift(tokenHeredoc(heredoc[1]))\n        return 'string-2'\n      }\n    }\n    if (/\\d/.test(ch)) {\n      stream.eatWhile(/\\d/);\n      if(stream.eol() || !/\\w/.test(stream.peek())) {\n        return 'number';\n      }\n    }\n    stream.eatWhile(/[\\w-]/);\n    var cur = stream.current();\n    if (stream.peek() === '=' && /\\w+/.test(cur)) return 'def';\n    return words.hasOwnProperty(cur) ? words[cur] : null;\n  }\n\n  function tokenString(quote, style) {\n    var close = quote == \"(\" ? \")\" : quote == \"{\" ? \"}\" : quote\n    return function(stream, state) {\n      var next, escaped = false;\n      while ((next = stream.next()) != null) {\n        if (next === close && !escaped) {\n          state.tokens.shift();\n          break;\n        } else if (next === '$' && !escaped && quote !== \"'\" && stream.peek() != close) {\n          escaped = true;\n          stream.backUp(1);\n          state.tokens.unshift(tokenDollar);\n          break;\n        } else if (!escaped && quote !== close && next === quote) {\n          state.tokens.unshift(tokenString(quote, style))\n          return tokenize(stream, state)\n        } else if (!escaped && /['\"]/.test(next) && !/['\"]/.test(quote)) {\n          state.tokens.unshift(tokenStringStart(next, \"string\"));\n          stream.backUp(1);\n          break;\n        }\n        escaped = !escaped && next === '\\\\';\n      }\n      return style;\n    };\n  };\n\n  function tokenStringStart(quote, style) {\n    return function(stream, state) {\n      state.tokens[0] = tokenString(quote, style)\n      stream.next()\n      return tokenize(stream, state)\n    }\n  }\n\n  var tokenDollar = function(stream, state) {\n    if (state.tokens.length > 1) stream.eat('$');\n    var ch = stream.next()\n    if (/['\"({]/.test(ch)) {\n      state.tokens[0] = tokenString(ch, ch == \"(\" ? \"quote\" : ch == \"{\" ? \"def\" : \"string\");\n      return tokenize(stream, state);\n    }\n    if (!/\\d/.test(ch)) stream.eatWhile(/\\w/);\n    state.tokens.shift();\n    return 'def';\n  };\n\n  function tokenHeredoc(delim) {\n    return function(stream, state) {\n      if (stream.sol() && stream.string == delim) state.tokens.shift()\n      stream.skipToEnd()\n      return \"string-2\"\n    }\n  }\n\n  function tokenize(stream, state) {\n    return (state.tokens[0] || tokenBase) (stream, state);\n  };\n\n  return {\n    startState: function() {return {tokens:[]};},\n    token: function(stream, state) {\n      return tokenize(stream, state);\n    },\n    closeBrackets: \"()[]{}''\\\"\\\"``\",\n    lineComment: '#',\n    fold: \"brace\"\n  };\n});\n\nCodeMirror.defineMIME('text/x-sh', 'shell');\n// Apache uses a slightly different Media Type for Shell scripts\n// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types\nCodeMirror.defineMIME('application/x-sh', 'shell');\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/shell/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({}, \"shell\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"var\",\n     \"text [def $var] text\");\n  MT(\"varBraces\",\n     \"text[def ${var}]text\");\n  MT(\"varVar\",\n     \"text [def $a$b] text\");\n  MT(\"varBracesVarBraces\",\n     \"text[def ${a}${b}]text\");\n\n  MT(\"singleQuotedVar\",\n     \"[string 'text $var text']\");\n  MT(\"singleQuotedVarBraces\",\n     \"[string 'text ${var} text']\");\n\n  MT(\"doubleQuotedVar\",\n     '[string \"text ][def $var][string  text\"]');\n  MT(\"doubleQuotedVarBraces\",\n     '[string \"text][def ${var}][string text\"]');\n  MT(\"doubleQuotedVarPunct\",\n     '[string \"text ][def $@][string  text\"]');\n  MT(\"doubleQuotedVarVar\",\n     '[string \"][def $a$b][string \"]');\n  MT(\"doubleQuotedVarBracesVarBraces\",\n     '[string \"][def ${a}${b}][string \"]');\n\n  MT(\"notAString\",\n     \"text\\\\'text\");\n  MT(\"escapes\",\n     \"outside\\\\'\\\\\\\"\\\\`\\\\\\\\[string \\\"inside\\\\`\\\\'\\\\\\\"\\\\\\\\`\\\\$notAVar\\\"]outside\\\\$\\\\(notASubShell\\\\)\");\n\n  MT(\"subshell\",\n     \"[builtin echo] [quote $(whoami)] s log, stardate [quote `date`].\");\n  MT(\"doubleQuotedSubshell\",\n     \"[builtin echo] [string \\\"][quote $(whoami)][string 's log, stardate `date`.\\\"]\");\n\n  MT(\"hashbang\",\n     \"[meta #!/bin/bash]\");\n  MT(\"comment\",\n     \"text [comment # Blurb]\");\n\n  MT(\"numbers\",\n     \"[number 0] [number 1] [number 2]\");\n  MT(\"keywords\",\n     \"[keyword while] [atom true]; [keyword do]\",\n     \"  [builtin sleep] [number 3]\",\n     \"[keyword done]\");\n  MT(\"options\",\n     \"[builtin ls] [attribute -l] [attribute --human-readable]\");\n  MT(\"operator\",\n     \"[def var][operator =]value\");\n\n  MT(\"doubleParens\",\n     \"foo [quote $((bar))]\")\n\n  MT(\"nested braces\",\n     \"[builtin echo] [def ${A[${B}]]}]\")\n\n  MT(\"strings in parens\",\n     \"[def FOO][operator =]([quote $(<][string \\\"][def $MYDIR][string \\\"][quote /myfile grep ][string 'hello$'][quote )])\")\n\n  MT(\"string ending in dollar\",\n    '[def a][operator =][string \"xyz$\"]; [def b][operator =][string \"y\"]')\n\n  MT(\"quote ending in dollar\",\n    \"[quote $(echo a$)]\")\n\n  MT(\"heredoc\",\n     \"[builtin cat] [string-2 <<- end]\",\n     \"[string-2 content one]\",\n     \"[string-2 content two end]\",\n     \"[string-2 end]\",\n     \"[builtin echo]\")\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/sieve/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Sieve (RFC5228) mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"sieve.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Sieve (RFC5228)</a>\n  </ul>\n</div>\n\n<article>\n<h2>Sieve (RFC5228) mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n#\n# Example Sieve Filter\n# Declare any optional features or extension used by the script\n#\n\nrequire [\"fileinto\", \"reject\"];\n\n#\n# Reject any large messages (note that the four leading dots get\n# \"stuffed\" to three)\n#\nif size :over 1M\n{\n  reject text:\nPlease do not send me large attachments.\nPut your file on a server and send me the URL.\nThank you.\n.... Fred\n.\n;\n  stop;\n}\n\n#\n# Handle messages from known mailing lists\n# Move messages from IETF filter discussion list to filter folder\n#\nif header :is \"Sender\" \"owner-ietf-mta-filters@imc.org\"\n{\n  fileinto \"filter\";  # move to \"filter\" folder\n}\n#\n# Keep all messages to or from people in my company\n#\nelsif address :domain :is [\"From\", \"To\"] \"example.com\"\n{\n  keep;               # keep in \"In\" folder\n}\n\n#\n# Try and catch unsolicited email.  If a message is not to me,\n# or it contains a subject known to be spam, file it away.\n#\nelsif anyof (not address :all :contains\n               [\"To\", \"Cc\", \"Bcc\"] \"me@example.com\",\n             header :matches \"subject\"\n               [\"*make*money*fast*\", \"*university*dipl*mas*\"])\n{\n  # If message header does not contain my address,\n  # it's from a list.\n  fileinto \"spam\";   # move to \"spam\" folder\n}\nelse\n{\n  # Move all other (non-company) mail to \"personal\"\n  # folder.\n  fileinto \"personal\";\n}\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>application/sieve</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/sieve/sieve.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"sieve\", function(config) {\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  var keywords = words(\"if elsif else stop require\");\n  var atoms = words(\"true false not\");\n  var indentUnit = config.indentUnit;\n\n  function tokenBase(stream, state) {\n\n    var ch = stream.next();\n    if (ch == \"/\" && stream.eat(\"*\")) {\n      state.tokenize = tokenCComment;\n      return tokenCComment(stream, state);\n    }\n\n    if (ch === '#') {\n      stream.skipToEnd();\n      return \"comment\";\n    }\n\n    if (ch == \"\\\"\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    }\n\n    if (ch == \"(\") {\n      state._indent.push(\"(\");\n      // add virtual angel wings so that editor behaves...\n      // ...more sane in case of broken brackets\n      state._indent.push(\"{\");\n      return null;\n    }\n\n    if (ch === \"{\") {\n      state._indent.push(\"{\");\n      return null;\n    }\n\n    if (ch == \")\")  {\n      state._indent.pop();\n      state._indent.pop();\n    }\n\n    if (ch === \"}\") {\n      state._indent.pop();\n      return null;\n    }\n\n    if (ch == \",\")\n      return null;\n\n    if (ch == \";\")\n      return null;\n\n\n    if (/[{}\\(\\),;]/.test(ch))\n      return null;\n\n    // 1*DIGIT \"K\" / \"M\" / \"G\"\n    if (/\\d/.test(ch)) {\n      stream.eatWhile(/[\\d]/);\n      stream.eat(/[KkMmGg]/);\n      return \"number\";\n    }\n\n    // \":\" (ALPHA / \"_\") *(ALPHA / DIGIT / \"_\")\n    if (ch == \":\") {\n      stream.eatWhile(/[a-zA-Z_]/);\n      stream.eatWhile(/[a-zA-Z0-9_]/);\n\n      return \"operator\";\n    }\n\n    stream.eatWhile(/\\w/);\n    var cur = stream.current();\n\n    // \"text:\" *(SP / HTAB) (hash-comment / CRLF)\n    // *(multiline-literal / multiline-dotstart)\n    // \".\" CRLF\n    if ((cur == \"text\") && stream.eat(\":\"))\n    {\n      state.tokenize = tokenMultiLineString;\n      return \"string\";\n    }\n\n    if (keywords.propertyIsEnumerable(cur))\n      return \"keyword\";\n\n    if (atoms.propertyIsEnumerable(cur))\n      return \"atom\";\n\n    return null;\n  }\n\n  function tokenMultiLineString(stream, state)\n  {\n    state._multiLineString = true;\n    // the first line is special it may contain a comment\n    if (!stream.sol()) {\n      stream.eatSpace();\n\n      if (stream.peek() == \"#\") {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n\n      stream.skipToEnd();\n      return \"string\";\n    }\n\n    if ((stream.next() == \".\")  && (stream.eol()))\n    {\n      state._multiLineString = false;\n      state.tokenize = tokenBase;\n    }\n\n    return \"string\";\n  }\n\n  function tokenCComment(stream, state) {\n    var maybeEnd = false, ch;\n    while ((ch = stream.next()) != null) {\n      if (maybeEnd && ch == \"/\") {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, ch;\n      while ((ch = stream.next()) != null) {\n        if (ch == quote && !escaped)\n          break;\n        escaped = !escaped && ch == \"\\\\\";\n      }\n      if (!escaped) state.tokenize = tokenBase;\n      return \"string\";\n    };\n  }\n\n  return {\n    startState: function(base) {\n      return {tokenize: tokenBase,\n              baseIndent: base || 0,\n              _indent: []};\n    },\n\n    token: function(stream, state) {\n      if (stream.eatSpace())\n        return null;\n\n      return (state.tokenize || tokenBase)(stream, state);\n    },\n\n    indent: function(state, _textAfter) {\n      var length = state._indent.length;\n      if (_textAfter && (_textAfter[0] == \"}\"))\n        length--;\n\n      if (length <0)\n        length = 0;\n\n      return length * indentUnit;\n    },\n\n    electricChars: \"}\"\n  };\n});\n\nCodeMirror.defineMIME(\"application/sieve\", \"sieve\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/slim/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: SLIM mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/ambiance.css\">\n<script src=\"https://code.jquery.com/jquery-1.11.1.min.js\"></script>\n<script src=\"https://code.jquery.com/ui/1.11.0/jquery-ui.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../htmlembedded/htmlembedded.js\"></script>\n<script src=\"../htmlmixed/htmlmixed.js\"></script>\n<script src=\"../coffeescript/coffeescript.js\"></script>\n<script src=\"../javascript/javascript.js\"></script>\n<script src=\"../ruby/ruby.js\"></script>\n<script src=\"../markdown/markdown.js\"></script>\n<script src=\"slim.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">SLIM</a>\n  </ul>\n</div>\n\n<article>\n  <h2>SLIM mode</h2>\n  <form><textarea id=\"code\" name=\"code\">\nbody\n  table\n    - for user in users\n      td id=\"user_#{user.id}\" class=user.role\n        a href=user_action(user, :edit) Edit #{user.name}\n        a href=(path_to_user user) = user.name\nbody\n  h1(id=\"logo\") = page_logo\n  h2[id=\"tagline\" class=\"small tagline\"] = page_tagline\n\nh2[id=\"tagline\"\n   class=\"small tagline\"] = page_tagline\n\nh1 id = \"logo\" = page_logo\nh2 [ id = \"tagline\" ] = page_tagline\n\n/ comment\n  second line\n/! html comment\n   second line\n<!-- html comment -->\n<a href=\"#{'hello' if set}\">link</a>\na.slim href=\"work\" disabled=false running==:atom Text <b>bold</b>\n.clazz data-id=\"test\" == 'hello' unless quark\n | Text mode #{12}\n   Second line\n= x ||= :ruby_atom\n#menu.left\n  - @env.each do |x|\n    li: a = x\n*@dyntag attr=\"val\"\n.first *{:class => [:second, :third]} Text\n.second class=[\"text\",\"more\"]\n.third class=:text,:symbol\n\n  </textarea></form>\n  <script>\n    var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n      lineNumbers: true,\n      theme: \"ambiance\",\n      mode: \"application/x-slim\"\n    });\n    $('.CodeMirror').resizable({\n      resize: function() {\n        editor.setSize($(this).width(), $(this).height());\n        //editor.refresh();\n      }\n    });\n  </script>\n\n  <p><strong>MIME types defined:</strong> <code>application/x-slim</code>.</p>\n\n  <p>\n    <strong>Parsing/Highlighting Tests:</strong>\n    <a href=\"../../test/index.html#slim_*\">normal</a>,\n    <a href=\"../../test/index.html#verbose,slim_*\">verbose</a>.\n  </p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/slim/slim.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Slim Highlighting for CodeMirror copyright (c) HicknHack Software Gmbh\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../htmlmixed/htmlmixed\"), require(\"../ruby/ruby\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../htmlmixed/htmlmixed\", \"../ruby/ruby\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\n  CodeMirror.defineMode(\"slim\", function(config) {\n    var htmlMode = CodeMirror.getMode(config, {name: \"htmlmixed\"});\n    var rubyMode = CodeMirror.getMode(config, \"ruby\");\n    var modes = { html: htmlMode, ruby: rubyMode };\n    var embedded = {\n      ruby: \"ruby\",\n      javascript: \"javascript\",\n      css: \"text/css\",\n      sass: \"text/x-sass\",\n      scss: \"text/x-scss\",\n      less: \"text/x-less\",\n      styl: \"text/x-styl\", // no highlighting so far\n      coffee: \"coffeescript\",\n      asciidoc: \"text/x-asciidoc\",\n      markdown: \"text/x-markdown\",\n      textile: \"text/x-textile\", // no highlighting so far\n      creole: \"text/x-creole\", // no highlighting so far\n      wiki: \"text/x-wiki\", // no highlighting so far\n      mediawiki: \"text/x-mediawiki\", // no highlighting so far\n      rdoc: \"text/x-rdoc\", // no highlighting so far\n      builder: \"text/x-builder\", // no highlighting so far\n      nokogiri: \"text/x-nokogiri\", // no highlighting so far\n      erb: \"application/x-erb\"\n    };\n    var embeddedRegexp = function(map){\n      var arr = [];\n      for(var key in map) arr.push(key);\n      return new RegExp(\"^(\"+arr.join('|')+\"):\");\n    }(embedded);\n\n    var styleMap = {\n      \"commentLine\": \"comment\",\n      \"slimSwitch\": \"operator special\",\n      \"slimTag\": \"tag\",\n      \"slimId\": \"attribute def\",\n      \"slimClass\": \"attribute qualifier\",\n      \"slimAttribute\": \"attribute\",\n      \"slimSubmode\": \"keyword special\",\n      \"closeAttributeTag\": null,\n      \"slimDoctype\": null,\n      \"lineContinuation\": null\n    };\n    var closing = {\n      \"{\": \"}\",\n      \"[\": \"]\",\n      \"(\": \")\"\n    };\n\n    var nameStartChar = \"_a-zA-Z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\";\n    var nameChar = nameStartChar + \"\\\\-0-9\\xB7\\u0300-\\u036F\\u203F-\\u2040\";\n    var nameRegexp = new RegExp(\"^[:\"+nameStartChar+\"](?::[\"+nameChar+\"]|[\"+nameChar+\"]*)\");\n    var attributeNameRegexp = new RegExp(\"^[:\"+nameStartChar+\"][:\\\\.\"+nameChar+\"]*(?=\\\\s*=)\");\n    var wrappedAttributeNameRegexp = new RegExp(\"^[:\"+nameStartChar+\"][:\\\\.\"+nameChar+\"]*\");\n    var classNameRegexp = /^\\.-?[_a-zA-Z]+[\\w\\-]*/;\n    var classIdRegexp = /^#[_a-zA-Z]+[\\w\\-]*/;\n\n    function backup(pos, tokenize, style) {\n      var restore = function(stream, state) {\n        state.tokenize = tokenize;\n        if (stream.pos < pos) {\n          stream.pos = pos;\n          return style;\n        }\n        return state.tokenize(stream, state);\n      };\n      return function(stream, state) {\n        state.tokenize = restore;\n        return tokenize(stream, state);\n      };\n    }\n\n    function maybeBackup(stream, state, pat, offset, style) {\n      var cur = stream.current();\n      var idx = cur.search(pat);\n      if (idx > -1) {\n        state.tokenize = backup(stream.pos, state.tokenize, style);\n        stream.backUp(cur.length - idx - offset);\n      }\n      return style;\n    }\n\n    function continueLine(state, column) {\n      state.stack = {\n        parent: state.stack,\n        style: \"continuation\",\n        indented: column,\n        tokenize: state.line\n      };\n      state.line = state.tokenize;\n    }\n    function finishContinue(state) {\n      if (state.line == state.tokenize) {\n        state.line = state.stack.tokenize;\n        state.stack = state.stack.parent;\n      }\n    }\n\n    function lineContinuable(column, tokenize) {\n      return function(stream, state) {\n        finishContinue(state);\n        if (stream.match(/^\\\\$/)) {\n          continueLine(state, column);\n          return \"lineContinuation\";\n        }\n        var style = tokenize(stream, state);\n        if (stream.eol() && stream.current().match(/(?:^|[^\\\\])(?:\\\\\\\\)*\\\\$/)) {\n          stream.backUp(1);\n        }\n        return style;\n      };\n    }\n    function commaContinuable(column, tokenize) {\n      return function(stream, state) {\n        finishContinue(state);\n        var style = tokenize(stream, state);\n        if (stream.eol() && stream.current().match(/,$/)) {\n          continueLine(state, column);\n        }\n        return style;\n      };\n    }\n\n    function rubyInQuote(endQuote, tokenize) {\n      // TODO: add multi line support\n      return function(stream, state) {\n        var ch = stream.peek();\n        if (ch == endQuote && state.rubyState.tokenize.length == 1) {\n          // step out of ruby context as it seems to complete processing all the braces\n          stream.next();\n          state.tokenize = tokenize;\n          return \"closeAttributeTag\";\n        } else {\n          return ruby(stream, state);\n        }\n      };\n    }\n    function startRubySplat(tokenize) {\n      var rubyState;\n      var runSplat = function(stream, state) {\n        if (state.rubyState.tokenize.length == 1 && !state.rubyState.context.prev) {\n          stream.backUp(1);\n          if (stream.eatSpace()) {\n            state.rubyState = rubyState;\n            state.tokenize = tokenize;\n            return tokenize(stream, state);\n          }\n          stream.next();\n        }\n        return ruby(stream, state);\n      };\n      return function(stream, state) {\n        rubyState = state.rubyState;\n        state.rubyState = CodeMirror.startState(rubyMode);\n        state.tokenize = runSplat;\n        return ruby(stream, state);\n      };\n    }\n\n    function ruby(stream, state) {\n      return rubyMode.token(stream, state.rubyState);\n    }\n\n    function htmlLine(stream, state) {\n      if (stream.match(/^\\\\$/)) {\n        return \"lineContinuation\";\n      }\n      return html(stream, state);\n    }\n    function html(stream, state) {\n      if (stream.match(/^#\\{/)) {\n        state.tokenize = rubyInQuote(\"}\", state.tokenize);\n        return null;\n      }\n      return maybeBackup(stream, state, /[^\\\\]#\\{/, 1, htmlMode.token(stream, state.htmlState));\n    }\n\n    function startHtmlLine(lastTokenize) {\n      return function(stream, state) {\n        var style = htmlLine(stream, state);\n        if (stream.eol()) state.tokenize = lastTokenize;\n        return style;\n      };\n    }\n\n    function startHtmlMode(stream, state, offset) {\n      state.stack = {\n        parent: state.stack,\n        style: \"html\",\n        indented: stream.column() + offset, // pipe + space\n        tokenize: state.line\n      };\n      state.line = state.tokenize = html;\n      return null;\n    }\n\n    function comment(stream, state) {\n      stream.skipToEnd();\n      return state.stack.style;\n    }\n\n    function commentMode(stream, state) {\n      state.stack = {\n        parent: state.stack,\n        style: \"comment\",\n        indented: state.indented + 1,\n        tokenize: state.line\n      };\n      state.line = comment;\n      return comment(stream, state);\n    }\n\n    function attributeWrapper(stream, state) {\n      if (stream.eat(state.stack.endQuote)) {\n        state.line = state.stack.line;\n        state.tokenize = state.stack.tokenize;\n        state.stack = state.stack.parent;\n        return null;\n      }\n      if (stream.match(wrappedAttributeNameRegexp)) {\n        state.tokenize = attributeWrapperAssign;\n        return \"slimAttribute\";\n      }\n      stream.next();\n      return null;\n    }\n    function attributeWrapperAssign(stream, state) {\n      if (stream.match(/^==?/)) {\n        state.tokenize = attributeWrapperValue;\n        return null;\n      }\n      return attributeWrapper(stream, state);\n    }\n    function attributeWrapperValue(stream, state) {\n      var ch = stream.peek();\n      if (ch == '\"' || ch == \"\\'\") {\n        state.tokenize = readQuoted(ch, \"string\", true, false, attributeWrapper);\n        stream.next();\n        return state.tokenize(stream, state);\n      }\n      if (ch == '[') {\n        return startRubySplat(attributeWrapper)(stream, state);\n      }\n      if (stream.match(/^(true|false|nil)\\b/)) {\n        state.tokenize = attributeWrapper;\n        return \"keyword\";\n      }\n      return startRubySplat(attributeWrapper)(stream, state);\n    }\n\n    function startAttributeWrapperMode(state, endQuote, tokenize) {\n      state.stack = {\n        parent: state.stack,\n        style: \"wrapper\",\n        indented: state.indented + 1,\n        tokenize: tokenize,\n        line: state.line,\n        endQuote: endQuote\n      };\n      state.line = state.tokenize = attributeWrapper;\n      return null;\n    }\n\n    function sub(stream, state) {\n      if (stream.match(/^#\\{/)) {\n        state.tokenize = rubyInQuote(\"}\", state.tokenize);\n        return null;\n      }\n      var subStream = new CodeMirror.StringStream(stream.string.slice(state.stack.indented), stream.tabSize);\n      subStream.pos = stream.pos - state.stack.indented;\n      subStream.start = stream.start - state.stack.indented;\n      subStream.lastColumnPos = stream.lastColumnPos - state.stack.indented;\n      subStream.lastColumnValue = stream.lastColumnValue - state.stack.indented;\n      var style = state.subMode.token(subStream, state.subState);\n      stream.pos = subStream.pos + state.stack.indented;\n      return style;\n    }\n    function firstSub(stream, state) {\n      state.stack.indented = stream.column();\n      state.line = state.tokenize = sub;\n      return state.tokenize(stream, state);\n    }\n\n    function createMode(mode) {\n      var query = embedded[mode];\n      var spec = CodeMirror.mimeModes[query];\n      if (spec) {\n        return CodeMirror.getMode(config, spec);\n      }\n      var factory = CodeMirror.modes[query];\n      if (factory) {\n        return factory(config, {name: query});\n      }\n      return CodeMirror.getMode(config, \"null\");\n    }\n\n    function getMode(mode) {\n      if (!modes.hasOwnProperty(mode)) {\n        return modes[mode] = createMode(mode);\n      }\n      return modes[mode];\n    }\n\n    function startSubMode(mode, state) {\n      var subMode = getMode(mode);\n      var subState = CodeMirror.startState(subMode);\n\n      state.subMode = subMode;\n      state.subState = subState;\n\n      state.stack = {\n        parent: state.stack,\n        style: \"sub\",\n        indented: state.indented + 1,\n        tokenize: state.line\n      };\n      state.line = state.tokenize = firstSub;\n      return \"slimSubmode\";\n    }\n\n    function doctypeLine(stream, _state) {\n      stream.skipToEnd();\n      return \"slimDoctype\";\n    }\n\n    function startLine(stream, state) {\n      var ch = stream.peek();\n      if (ch == '<') {\n        return (state.tokenize = startHtmlLine(state.tokenize))(stream, state);\n      }\n      if (stream.match(/^[|']/)) {\n        return startHtmlMode(stream, state, 1);\n      }\n      if (stream.match(/^\\/(!|\\[\\w+])?/)) {\n        return commentMode(stream, state);\n      }\n      if (stream.match(/^(-|==?[<>]?)/)) {\n        state.tokenize = lineContinuable(stream.column(), commaContinuable(stream.column(), ruby));\n        return \"slimSwitch\";\n      }\n      if (stream.match(/^doctype\\b/)) {\n        state.tokenize = doctypeLine;\n        return \"keyword\";\n      }\n\n      var m = stream.match(embeddedRegexp);\n      if (m) {\n        return startSubMode(m[1], state);\n      }\n\n      return slimTag(stream, state);\n    }\n\n    function slim(stream, state) {\n      if (state.startOfLine) {\n        return startLine(stream, state);\n      }\n      return slimTag(stream, state);\n    }\n\n    function slimTag(stream, state) {\n      if (stream.eat('*')) {\n        state.tokenize = startRubySplat(slimTagExtras);\n        return null;\n      }\n      if (stream.match(nameRegexp)) {\n        state.tokenize = slimTagExtras;\n        return \"slimTag\";\n      }\n      return slimClass(stream, state);\n    }\n    function slimTagExtras(stream, state) {\n      if (stream.match(/^(<>?|><?)/)) {\n        state.tokenize = slimClass;\n        return null;\n      }\n      return slimClass(stream, state);\n    }\n    function slimClass(stream, state) {\n      if (stream.match(classIdRegexp)) {\n        state.tokenize = slimClass;\n        return \"slimId\";\n      }\n      if (stream.match(classNameRegexp)) {\n        state.tokenize = slimClass;\n        return \"slimClass\";\n      }\n      return slimAttribute(stream, state);\n    }\n    function slimAttribute(stream, state) {\n      if (stream.match(/^([\\[\\{\\(])/)) {\n        return startAttributeWrapperMode(state, closing[RegExp.$1], slimAttribute);\n      }\n      if (stream.match(attributeNameRegexp)) {\n        state.tokenize = slimAttributeAssign;\n        return \"slimAttribute\";\n      }\n      if (stream.peek() == '*') {\n        stream.next();\n        state.tokenize = startRubySplat(slimContent);\n        return null;\n      }\n      return slimContent(stream, state);\n    }\n    function slimAttributeAssign(stream, state) {\n      if (stream.match(/^==?/)) {\n        state.tokenize = slimAttributeValue;\n        return null;\n      }\n      // should never happen, because of forward lookup\n      return slimAttribute(stream, state);\n    }\n\n    function slimAttributeValue(stream, state) {\n      var ch = stream.peek();\n      if (ch == '\"' || ch == \"\\'\") {\n        state.tokenize = readQuoted(ch, \"string\", true, false, slimAttribute);\n        stream.next();\n        return state.tokenize(stream, state);\n      }\n      if (ch == '[') {\n        return startRubySplat(slimAttribute)(stream, state);\n      }\n      if (ch == ':') {\n        return startRubySplat(slimAttributeSymbols)(stream, state);\n      }\n      if (stream.match(/^(true|false|nil)\\b/)) {\n        state.tokenize = slimAttribute;\n        return \"keyword\";\n      }\n      return startRubySplat(slimAttribute)(stream, state);\n    }\n    function slimAttributeSymbols(stream, state) {\n      stream.backUp(1);\n      if (stream.match(/^[^\\s],(?=:)/)) {\n        state.tokenize = startRubySplat(slimAttributeSymbols);\n        return null;\n      }\n      stream.next();\n      return slimAttribute(stream, state);\n    }\n    function readQuoted(quote, style, embed, unescaped, nextTokenize) {\n      return function(stream, state) {\n        finishContinue(state);\n        var fresh = stream.current().length == 0;\n        if (stream.match(/^\\\\$/, fresh)) {\n          if (!fresh) return style;\n          continueLine(state, state.indented);\n          return \"lineContinuation\";\n        }\n        if (stream.match(/^#\\{/, fresh)) {\n          if (!fresh) return style;\n          state.tokenize = rubyInQuote(\"}\", state.tokenize);\n          return null;\n        }\n        var escaped = false, ch;\n        while ((ch = stream.next()) != null) {\n          if (ch == quote && (unescaped || !escaped)) {\n            state.tokenize = nextTokenize;\n            break;\n          }\n          if (embed && ch == \"#\" && !escaped) {\n            if (stream.eat(\"{\")) {\n              stream.backUp(2);\n              break;\n            }\n          }\n          escaped = !escaped && ch == \"\\\\\";\n        }\n        if (stream.eol() && escaped) {\n          stream.backUp(1);\n        }\n        return style;\n      };\n    }\n    function slimContent(stream, state) {\n      if (stream.match(/^==?/)) {\n        state.tokenize = ruby;\n        return \"slimSwitch\";\n      }\n      if (stream.match(/^\\/$/)) { // tag close hint\n        state.tokenize = slim;\n        return null;\n      }\n      if (stream.match(/^:/)) { // inline tag\n        state.tokenize = slimTag;\n        return \"slimSwitch\";\n      }\n      startHtmlMode(stream, state, 0);\n      return state.tokenize(stream, state);\n    }\n\n    var mode = {\n      // default to html mode\n      startState: function() {\n        var htmlState = CodeMirror.startState(htmlMode);\n        var rubyState = CodeMirror.startState(rubyMode);\n        return {\n          htmlState: htmlState,\n          rubyState: rubyState,\n          stack: null,\n          last: null,\n          tokenize: slim,\n          line: slim,\n          indented: 0\n        };\n      },\n\n      copyState: function(state) {\n        return {\n          htmlState : CodeMirror.copyState(htmlMode, state.htmlState),\n          rubyState: CodeMirror.copyState(rubyMode, state.rubyState),\n          subMode: state.subMode,\n          subState: state.subMode && CodeMirror.copyState(state.subMode, state.subState),\n          stack: state.stack,\n          last: state.last,\n          tokenize: state.tokenize,\n          line: state.line\n        };\n      },\n\n      token: function(stream, state) {\n        if (stream.sol()) {\n          state.indented = stream.indentation();\n          state.startOfLine = true;\n          state.tokenize = state.line;\n          while (state.stack && state.stack.indented > state.indented && state.last != \"slimSubmode\") {\n            state.line = state.tokenize = state.stack.tokenize;\n            state.stack = state.stack.parent;\n            state.subMode = null;\n            state.subState = null;\n          }\n        }\n        if (stream.eatSpace()) return null;\n        var style = state.tokenize(stream, state);\n        state.startOfLine = false;\n        if (style) state.last = style;\n        return styleMap.hasOwnProperty(style) ? styleMap[style] : style;\n      },\n\n      blankLine: function(state) {\n        if (state.subMode && state.subMode.blankLine) {\n          return state.subMode.blankLine(state.subState);\n        }\n      },\n\n      innerMode: function(state) {\n        if (state.subMode) return {state: state.subState, mode: state.subMode};\n        return {state: state, mode: mode};\n      }\n\n      //indent: function(state) {\n      //  return state.indented;\n      //}\n    };\n    return mode;\n  }, \"htmlmixed\", \"ruby\");\n\n  CodeMirror.defineMIME(\"text/x-slim\", \"slim\");\n  CodeMirror.defineMIME(\"application/x-slim\", \"slim\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/slim/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Slim Highlighting for CodeMirror copyright (c) HicknHack Software Gmbh\n\n(function() {\n  var mode = CodeMirror.getMode({tabSize: 4, indentUnit: 2}, \"slim\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  // Requires at least one media query\n  MT(\"elementName\",\n     \"[tag h1] Hey There\");\n\n  MT(\"oneElementPerLine\",\n     \"[tag h1] Hey There .h2\");\n\n  MT(\"idShortcut\",\n     \"[attribute&def #test] Hey There\");\n\n  MT(\"tagWithIdShortcuts\",\n     \"[tag h1][attribute&def #test] Hey There\");\n\n  MT(\"classShortcut\",\n     \"[attribute&qualifier .hello] Hey There\");\n\n  MT(\"tagWithIdAndClassShortcuts\",\n     \"[tag h1][attribute&def #test][attribute&qualifier .hello] Hey There\");\n\n  MT(\"docType\",\n     \"[keyword doctype] xml\");\n\n  MT(\"comment\",\n     \"[comment / Hello WORLD]\");\n\n  MT(\"notComment\",\n     \"[tag h1] This is not a / comment \");\n\n  MT(\"attributes\",\n     \"[tag a]([attribute title]=[string \\\"test\\\"]) [attribute href]=[string \\\"link\\\"]}\");\n\n  MT(\"multiLineAttributes\",\n     \"[tag a]([attribute title]=[string \\\"test\\\"]\",\n     \"  ) [attribute href]=[string \\\"link\\\"]}\");\n\n  MT(\"htmlCode\",\n     \"[tag&bracket <][tag h1][tag&bracket >]Title[tag&bracket </][tag h1][tag&bracket >]\");\n\n  MT(\"rubyBlock\",\n     \"[operator&special =][variable-2 @item]\");\n\n  MT(\"selectorRubyBlock\",\n     \"[tag a][attribute&qualifier .test][operator&special =] [variable-2 @item]\");\n\n  MT(\"nestedRubyBlock\",\n      \"[tag a]\",\n      \"  [operator&special =][variable puts] [string \\\"test\\\"]\");\n\n  MT(\"multilinePlaintext\",\n      \"[tag p]\",\n      \"  | Hello,\",\n      \"    World\");\n\n  MT(\"multilineRuby\",\n      \"[tag p]\",\n      \"  [comment /# this is a comment]\",\n      \"     [comment and this is a comment too]\",\n      \"  | Date/Time\",\n      \"  [operator&special -] [variable now] [operator =] [tag DateTime][operator .][property now]\",\n      \"  [tag strong][operator&special =] [variable now]\",\n      \"  [operator&special -] [keyword if] [variable now] [operator >] [tag DateTime][operator .][property parse]([string \\\"December 31, 2006\\\"])\",\n      \"     [operator&special =][string \\\"Happy\\\"]\",\n      \"     [operator&special =][string \\\"Belated\\\"]\",\n      \"     [operator&special =][string \\\"Birthday\\\"]\");\n\n  MT(\"multilineComment\",\n      \"[comment /]\",\n      \"  [comment Multiline]\",\n      \"  [comment Comment]\");\n\n  MT(\"hamlAfterRubyTag\",\n    \"[attribute&qualifier .block]\",\n    \"  [tag strong][operator&special =] [variable now]\",\n    \"  [attribute&qualifier .test]\",\n    \"     [operator&special =][variable now]\",\n    \"  [attribute&qualifier .right]\");\n\n  MT(\"stretchedRuby\",\n     \"[operator&special =] [variable puts] [string \\\"Hello\\\"],\",\n     \"   [string \\\"World\\\"]\");\n\n  MT(\"interpolationInHashAttribute\",\n     \"[tag div]{[attribute id] = [string \\\"]#{[variable test]}[string _]#{[variable ting]}[string \\\"]} test\");\n\n  MT(\"interpolationInHTMLAttribute\",\n     \"[tag div]([attribute title]=[string \\\"]#{[variable test]}[string _]#{[variable ting]()}[string \\\"]) Test\");\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/smalltalk/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Smalltalk mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"smalltalk.js\"></script>\n<style>\n      .CodeMirror {border: 2px solid #dee; border-right-width: 10px;}\n      .CodeMirror-gutter {border: none; background: #dee;}\n      .CodeMirror-gutter pre {color: white; font-weight: bold;}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Smalltalk</a>\n  </ul>\n</div>\n\n<article>\n<h2>Smalltalk mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n\" \n    This is a test of the Smalltalk code\n\"\nSeaside.WAComponent subclass: #MyCounter [\n    | count |\n    MyCounter class &gt;&gt; canBeRoot [ ^true ]\n\n    initialize [\n        super initialize.\n        count := 0.\n    ]\n    states [ ^{ self } ]\n    renderContentOn: html [\n        html heading: count.\n        html anchor callback: [ count := count + 1 ]; with: '++'.\n        html space.\n        html anchor callback: [ count := count - 1 ]; with: '--'.\n    ]\n]\n\nMyCounter registerAsApplication: 'mycounter'\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-stsrc\",\n        indentUnit: 4\n      });\n    </script>\n\n    <p>Simple Smalltalk mode.</p>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-stsrc</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/smalltalk/smalltalk.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode('smalltalk', function(config) {\n\n  var specialChars = /[+\\-\\/\\\\*~<>=@%|&?!.,:;^]/;\n  var keywords = /true|false|nil|self|super|thisContext/;\n\n  var Context = function(tokenizer, parent) {\n    this.next = tokenizer;\n    this.parent = parent;\n  };\n\n  var Token = function(name, context, eos) {\n    this.name = name;\n    this.context = context;\n    this.eos = eos;\n  };\n\n  var State = function() {\n    this.context = new Context(next, null);\n    this.expectVariable = true;\n    this.indentation = 0;\n    this.userIndentationDelta = 0;\n  };\n\n  State.prototype.userIndent = function(indentation) {\n    this.userIndentationDelta = indentation > 0 ? (indentation / config.indentUnit - this.indentation) : 0;\n  };\n\n  var next = function(stream, context, state) {\n    var token = new Token(null, context, false);\n    var aChar = stream.next();\n\n    if (aChar === '\"') {\n      token = nextComment(stream, new Context(nextComment, context));\n\n    } else if (aChar === '\\'') {\n      token = nextString(stream, new Context(nextString, context));\n\n    } else if (aChar === '#') {\n      if (stream.peek() === '\\'') {\n        stream.next();\n        token = nextSymbol(stream, new Context(nextSymbol, context));\n      } else {\n        if (stream.eatWhile(/[^\\s.{}\\[\\]()]/))\n          token.name = 'string-2';\n        else\n          token.name = 'meta';\n      }\n\n    } else if (aChar === '$') {\n      if (stream.next() === '<') {\n        stream.eatWhile(/[^\\s>]/);\n        stream.next();\n      }\n      token.name = 'string-2';\n\n    } else if (aChar === '|' && state.expectVariable) {\n      token.context = new Context(nextTemporaries, context);\n\n    } else if (/[\\[\\]{}()]/.test(aChar)) {\n      token.name = 'bracket';\n      token.eos = /[\\[{(]/.test(aChar);\n\n      if (aChar === '[') {\n        state.indentation++;\n      } else if (aChar === ']') {\n        state.indentation = Math.max(0, state.indentation - 1);\n      }\n\n    } else if (specialChars.test(aChar)) {\n      stream.eatWhile(specialChars);\n      token.name = 'operator';\n      token.eos = aChar !== ';'; // ; cascaded message expression\n\n    } else if (/\\d/.test(aChar)) {\n      stream.eatWhile(/[\\w\\d]/);\n      token.name = 'number';\n\n    } else if (/[\\w_]/.test(aChar)) {\n      stream.eatWhile(/[\\w\\d_]/);\n      token.name = state.expectVariable ? (keywords.test(stream.current()) ? 'keyword' : 'variable') : null;\n\n    } else {\n      token.eos = state.expectVariable;\n    }\n\n    return token;\n  };\n\n  var nextComment = function(stream, context) {\n    stream.eatWhile(/[^\"]/);\n    return new Token('comment', stream.eat('\"') ? context.parent : context, true);\n  };\n\n  var nextString = function(stream, context) {\n    stream.eatWhile(/[^']/);\n    return new Token('string', stream.eat('\\'') ? context.parent : context, false);\n  };\n\n  var nextSymbol = function(stream, context) {\n    stream.eatWhile(/[^']/);\n    return new Token('string-2', stream.eat('\\'') ? context.parent : context, false);\n  };\n\n  var nextTemporaries = function(stream, context) {\n    var token = new Token(null, context, false);\n    var aChar = stream.next();\n\n    if (aChar === '|') {\n      token.context = context.parent;\n      token.eos = true;\n\n    } else {\n      stream.eatWhile(/[^|]/);\n      token.name = 'variable';\n    }\n\n    return token;\n  };\n\n  return {\n    startState: function() {\n      return new State;\n    },\n\n    token: function(stream, state) {\n      state.userIndent(stream.indentation());\n\n      if (stream.eatSpace()) {\n        return null;\n      }\n\n      var token = state.context.next(stream, state.context, state);\n      state.context = token.context;\n      state.expectVariable = token.eos;\n\n      return token.name;\n    },\n\n    blankLine: function(state) {\n      state.userIndent(0);\n    },\n\n    indent: function(state, textAfter) {\n      var i = state.context.next === next && textAfter && textAfter.charAt(0) === ']' ? -1 : state.userIndentationDelta;\n      return (state.indentation + i) * config.indentUnit;\n    },\n\n    electricChars: ']'\n  };\n\n});\n\nCodeMirror.defineMIME('text/x-stsrc', {name: 'smalltalk'});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/smarty/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Smarty mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"smarty.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Smarty</a>\n  </ul>\n</div>\n\n<article>\n<h2>Smarty mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n{extends file=\"parent.tpl\"}\n{include file=\"template.tpl\"}\n\n{* some example Smarty content *}\n{if isset($name) && $name == 'Blog'}\n  This is a {$var}.\n  {$integer = 451}, {$array[] = \"a\"}, {$stringvar = \"string\"}\n  {assign var='bob' value=$var.prop}\n{elseif $name == $foo}\n  {function name=menu level=0}\n    {foreach $data as $entry}\n      {if is_array($entry)}\n        - {$entry@key}\n        {menu data=$entry level=$level+1}\n      {else}\n        {$entry}\n      {/if}\n    {/foreach}\n  {/function}\n{/if}</textarea></form>\n\n<p>Mode for Smarty version 2 or 3, which allows for custom delimiter tags.</p>\n\n<p>Several configuration parameters are supported:</p>\n\n<ul>\n  <li><code>leftDelimiter</code> and <code>rightDelimiter</code>,\n  which should be strings that determine where the Smarty syntax\n  starts and ends.</li>\n  <li><code>version</code>, which should be 2 or 3.</li>\n  <li><code>baseMode</code>, which can be a mode spec\n  like <code>\"text/html\"</code> to set a different background mode.</li>\n</ul>\n\n<p><strong>MIME types defined:</strong> <code>text/x-smarty</code></p>\n\n<h3>Smarty 2, custom delimiters</h3>\n\n<form><textarea id=\"code2\" name=\"code2\">\n{--extends file=\"parent.tpl\"--}\n{--include file=\"template.tpl\"--}\n\n{--* some example Smarty content *--}\n{--if isset($name) && $name == 'Blog'--}\n  This is a {--$var--}.\n  {--$integer = 451--}, {--$array[] = \"a\"--}, {--$stringvar = \"string\"--}\n  {--assign var='bob' value=$var.prop--}\n{--elseif $name == $foo--}\n  {--function name=menu level=0--}\n    {--foreach $data as $entry--}\n      {--if is_array($entry)--}\n        - {--$entry@key--}\n        {--menu data=$entry level=$level+1--}\n      {--else--}\n        {--$entry--}\n      {--/if--}\n    {--/foreach--}\n  {--/function--}\n{--/if--}</textarea></form>\n\n<h3>Smarty 3</h3>\n\n<textarea id=\"code3\" name=\"code3\">\nNested tags {$foo={counter one=1 two={inception}}+3} are now valid in Smarty 3.\n\n<script>\nfunction test() {\n  console.log(\"Smarty 3 permits single curly braces followed by whitespace to NOT slip into Smarty mode.\");\n}\n</script>\n\n{assign var=foo value=[1,2,3]}\n{assign var=foo value=['y'=>'yellow','b'=>'blue']}\n{assign var=foo value=[1,[9,8],3]}\n\n{$foo=$bar+2} {* a comment *}\n{$foo.bar=1}  {* another comment *}\n{$foo = myfunct(($x+$y)*3)}\n{$foo = strlen($bar)}\n{$foo.bar.baz=1}, {$foo[]=1}\n\nSmarty \"dot\" syntax (note: embedded {} are used to address ambiguities):\n\n{$foo.a.b.c}      => $foo['a']['b']['c']\n{$foo.a.$b.c}     => $foo['a'][$b]['c']\n{$foo.a.{$b+4}.c} => $foo['a'][$b+4]['c']\n{$foo.a.{$b.c}}   => $foo['a'][$b['c']]\n\n{$object->method1($x)->method2($y)}</textarea>\n\n<script>\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n  lineNumbers: true,\n  mode: \"smarty\"\n});\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code2\"), {\n  lineNumbers: true,\n  mode: {\n    name: \"smarty\",\n    leftDelimiter: \"{--\",\n    rightDelimiter: \"--}\"\n  }\n});\nvar editor = CodeMirror.fromTextArea(document.getElementById(\"code3\"), {\n  lineNumbers: true,\n  mode: {name: \"smarty\", version: 3, baseMode: \"text/html\"}\n});\n</script>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/smarty/smarty.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/**\n * Smarty 2 and 3 mode.\n */\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"smarty\", function(config, parserConf) {\n    var rightDelimiter = parserConf.rightDelimiter || \"}\";\n    var leftDelimiter = parserConf.leftDelimiter || \"{\";\n    var version = parserConf.version || 2;\n    var baseMode = CodeMirror.getMode(config, parserConf.baseMode || \"null\");\n\n    var keyFunctions = [\"debug\", \"extends\", \"function\", \"include\", \"literal\"];\n    var regs = {\n      operatorChars: /[+\\-*&%=<>!?]/,\n      validIdentifier: /[a-zA-Z0-9_]/,\n      stringChar: /['\"]/\n    };\n\n    var last;\n    function cont(style, lastType) {\n      last = lastType;\n      return style;\n    }\n\n    function chain(stream, state, parser) {\n      state.tokenize = parser;\n      return parser(stream, state);\n    }\n\n    // Smarty 3 allows { and } surrounded by whitespace to NOT slip into Smarty mode\n    function doesNotCount(stream, pos) {\n      if (pos == null) pos = stream.pos;\n      return version === 3 && leftDelimiter == \"{\" &&\n        (pos == stream.string.length || /\\s/.test(stream.string.charAt(pos)));\n    }\n\n    function tokenTop(stream, state) {\n      var string = stream.string;\n      for (var scan = stream.pos;;) {\n        var nextMatch = string.indexOf(leftDelimiter, scan);\n        scan = nextMatch + leftDelimiter.length;\n        if (nextMatch == -1 || !doesNotCount(stream, nextMatch + leftDelimiter.length)) break;\n      }\n      if (nextMatch == stream.pos) {\n        stream.match(leftDelimiter);\n        if (stream.eat(\"*\")) {\n          return chain(stream, state, tokenBlock(\"comment\", \"*\" + rightDelimiter));\n        } else {\n          state.depth++;\n          state.tokenize = tokenSmarty;\n          last = \"startTag\";\n          return \"tag\";\n        }\n      }\n\n      if (nextMatch > -1) stream.string = string.slice(0, nextMatch);\n      var token = baseMode.token(stream, state.base);\n      if (nextMatch > -1) stream.string = string;\n      return token;\n    }\n\n    // parsing Smarty content\n    function tokenSmarty(stream, state) {\n      if (stream.match(rightDelimiter, true)) {\n        if (version === 3) {\n          state.depth--;\n          if (state.depth <= 0) {\n            state.tokenize = tokenTop;\n          }\n        } else {\n          state.tokenize = tokenTop;\n        }\n        return cont(\"tag\", null);\n      }\n\n      if (stream.match(leftDelimiter, true)) {\n        state.depth++;\n        return cont(\"tag\", \"startTag\");\n      }\n\n      var ch = stream.next();\n      if (ch == \"$\") {\n        stream.eatWhile(regs.validIdentifier);\n        return cont(\"variable-2\", \"variable\");\n      } else if (ch == \"|\") {\n        return cont(\"operator\", \"pipe\");\n      } else if (ch == \".\") {\n        return cont(\"operator\", \"property\");\n      } else if (regs.stringChar.test(ch)) {\n        state.tokenize = tokenAttribute(ch);\n        return cont(\"string\", \"string\");\n      } else if (regs.operatorChars.test(ch)) {\n        stream.eatWhile(regs.operatorChars);\n        return cont(\"operator\", \"operator\");\n      } else if (ch == \"[\" || ch == \"]\") {\n        return cont(\"bracket\", \"bracket\");\n      } else if (ch == \"(\" || ch == \")\") {\n        return cont(\"bracket\", \"operator\");\n      } else if (/\\d/.test(ch)) {\n        stream.eatWhile(/\\d/);\n        return cont(\"number\", \"number\");\n      } else {\n\n        if (state.last == \"variable\") {\n          if (ch == \"@\") {\n            stream.eatWhile(regs.validIdentifier);\n            return cont(\"property\", \"property\");\n          } else if (ch == \"|\") {\n            stream.eatWhile(regs.validIdentifier);\n            return cont(\"qualifier\", \"modifier\");\n          }\n        } else if (state.last == \"pipe\") {\n          stream.eatWhile(regs.validIdentifier);\n          return cont(\"qualifier\", \"modifier\");\n        } else if (state.last == \"whitespace\") {\n          stream.eatWhile(regs.validIdentifier);\n          return cont(\"attribute\", \"modifier\");\n        } if (state.last == \"property\") {\n          stream.eatWhile(regs.validIdentifier);\n          return cont(\"property\", null);\n        } else if (/\\s/.test(ch)) {\n          last = \"whitespace\";\n          return null;\n        }\n\n        var str = \"\";\n        if (ch != \"/\") {\n          str += ch;\n        }\n        var c = null;\n        while (c = stream.eat(regs.validIdentifier)) {\n          str += c;\n        }\n        for (var i=0, j=keyFunctions.length; i<j; i++) {\n          if (keyFunctions[i] == str) {\n            return cont(\"keyword\", \"keyword\");\n          }\n        }\n        if (/\\s/.test(ch)) {\n          return null;\n        }\n        return cont(\"tag\", \"tag\");\n      }\n    }\n\n    function tokenAttribute(quote) {\n      return function(stream, state) {\n        var prevChar = null;\n        var currChar = null;\n        while (!stream.eol()) {\n          currChar = stream.peek();\n          if (stream.next() == quote && prevChar !== '\\\\') {\n            state.tokenize = tokenSmarty;\n            break;\n          }\n          prevChar = currChar;\n        }\n        return \"string\";\n      };\n    }\n\n    function tokenBlock(style, terminator) {\n      return function(stream, state) {\n        while (!stream.eol()) {\n          if (stream.match(terminator)) {\n            state.tokenize = tokenTop;\n            break;\n          }\n          stream.next();\n        }\n        return style;\n      };\n    }\n\n    return {\n      startState: function() {\n        return {\n          base: CodeMirror.startState(baseMode),\n          tokenize: tokenTop,\n          last: null,\n          depth: 0\n        };\n      },\n      copyState: function(state) {\n        return {\n          base: CodeMirror.copyState(baseMode, state.base),\n          tokenize: state.tokenize,\n          last: state.last,\n          depth: state.depth\n        };\n      },\n      innerMode: function(state) {\n        if (state.tokenize == tokenTop)\n          return {mode: baseMode, state: state.base};\n      },\n      token: function(stream, state) {\n        var style = state.tokenize(stream, state);\n        state.last = last;\n        return style;\n      },\n      indent: function(state, text, line) {\n        if (state.tokenize == tokenTop && baseMode.indent)\n          return baseMode.indent(state.base, text, line);\n        else\n          return CodeMirror.Pass;\n      },\n      blockCommentStart: leftDelimiter + \"*\",\n      blockCommentEnd: \"*\" + rightDelimiter\n    };\n  });\n\n  CodeMirror.defineMIME(\"text/x-smarty\", \"smarty\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/solr/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Solr mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"solr.js\"></script>\n<style>\n  .CodeMirror {\n    border-top: 1px solid black;\n    border-bottom: 1px solid black;\n  }\n\n  .CodeMirror .cm-operator {\n    color: orange;\n  }\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Solr</a>\n  </ul>\n</div>\n\n<article>\n  <h2>Solr mode</h2>\n\n  <div>\n    <textarea id=\"code\" name=\"code\">author:Camus\n\ntitle:\"The Rebel\" and author:Camus\n\nphilosophy:Existentialism -author:Kierkegaard\n\nhardToSpell:Dostoevsky~\n\npublished:[194* TO 1960] and author:(Sartre or \"Simone de Beauvoir\")</textarea>\n  </div>\n\n  <script>\n    var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n      mode: 'solr',\n      lineNumbers: true\n    });\n  </script>\n\n  <p><strong>MIME types defined:</strong> <code>text/x-solr</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/solr/solr.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"solr\", function() {\n  \"use strict\";\n\n  var isStringChar = /[^\\s\\|\\!\\+\\-\\*\\?\\~\\^\\&\\:\\(\\)\\[\\]\\{\\}\\\"\\\\]/;\n  var isOperatorChar = /[\\|\\!\\+\\-\\*\\?\\~\\^\\&]/;\n  var isOperatorString = /^(OR|AND|NOT|TO)$/i;\n\n  function isNumber(word) {\n    return parseFloat(word).toString() === word;\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) break;\n        escaped = !escaped && next == \"\\\\\";\n      }\n\n      if (!escaped) state.tokenize = tokenBase;\n      return \"string\";\n    };\n  }\n\n  function tokenOperator(operator) {\n    return function(stream, state) {\n      var style = \"operator\";\n      if (operator == \"+\")\n        style += \" positive\";\n      else if (operator == \"-\")\n        style += \" negative\";\n      else if (operator == \"|\")\n        stream.eat(/\\|/);\n      else if (operator == \"&\")\n        stream.eat(/\\&/);\n      else if (operator == \"^\")\n        style += \" boost\";\n\n      state.tokenize = tokenBase;\n      return style;\n    };\n  }\n\n  function tokenWord(ch) {\n    return function(stream, state) {\n      var word = ch;\n      while ((ch = stream.peek()) && ch.match(isStringChar) != null) {\n        word += stream.next();\n      }\n\n      state.tokenize = tokenBase;\n      if (isOperatorString.test(word))\n        return \"operator\";\n      else if (isNumber(word))\n        return \"number\";\n      else if (stream.peek() == \":\")\n        return \"field\";\n      else\n        return \"string\";\n    };\n  }\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (ch == '\"')\n      state.tokenize = tokenString(ch);\n    else if (isOperatorChar.test(ch))\n      state.tokenize = tokenOperator(ch);\n    else if (isStringChar.test(ch))\n      state.tokenize = tokenWord(ch);\n\n    return (state.tokenize != tokenBase) ? state.tokenize(stream, state) : null;\n  }\n\n  return {\n    startState: function() {\n      return {\n        tokenize: tokenBase\n      };\n    },\n\n    token: function(stream, state) {\n      if (stream.eatSpace()) return null;\n      return state.tokenize(stream, state);\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-solr\", \"solr\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/soy/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Soy (Closure Template) mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"../htmlmixed/htmlmixed.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../javascript/javascript.js\"></script>\n<script src=\"../css/css.js\"></script>\n<script src=\"soy.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Soy (Closure Template)</a>\n  </ul>\n</div>\n\n<article>\n<h2>Soy (Closure Template) mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n{namespace example}\n\n/**\n * Says hello to the world.\n */\n{template .helloWorld}\n  {@param name: string}\n  {@param? score: number}\n  Hello <b>{$name}</b>!\n  <div>\n    {if $score}\n      <em>{$score} points</em>\n    {else}\n      no score\n    {/if}\n  </div>\n{/template}\n\n{template .alertHelloWorld kind=\"js\"}\n  alert('Hello World');\n{/template}\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-soy\",\n        indentUnit: 2,\n        indentWithTabs: false\n      });\n    </script>\n\n    <p>A mode for <a href=\"https://developers.google.com/closure/templates/\">Closure Templates</a> (Soy).</p>\n    <p><strong>MIME type defined:</strong> <code>text/x-soy</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/soy/soy.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../htmlmixed/htmlmixed\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../htmlmixed/htmlmixed\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var paramData = { noEndTag: true, soyState: \"param-def\" };\n  var tags = {\n    \"alias\": { noEndTag: true },\n    \"delpackage\": { noEndTag: true },\n    \"namespace\": { noEndTag: true, soyState: \"namespace-def\" },\n    \"@attribute\": paramData,\n    \"@attribute?\": paramData,\n    \"@param\": paramData,\n    \"@param?\": paramData,\n    \"@inject\": paramData,\n    \"@inject?\": paramData,\n    \"@state\": paramData,\n    \"template\": { soyState: \"templ-def\", variableScope: true},\n    \"extern\": {soyState: \"param-def\"},\n    \"export\": {soyState: \"export\"},\n    \"literal\": { },\n    \"msg\": {},\n    \"fallbackmsg\": { noEndTag: true, reduceIndent: true},\n    \"select\": {},\n    \"plural\": {},\n    \"let\": { soyState: \"var-def\" },\n    \"if\": {},\n    \"javaimpl\": {},\n    \"jsimpl\": {},\n    \"elseif\": { noEndTag: true, reduceIndent: true},\n    \"else\": { noEndTag: true, reduceIndent: true},\n    \"switch\": {},\n    \"case\": { noEndTag: true, reduceIndent: true},\n    \"default\": { noEndTag: true, reduceIndent: true},\n    \"foreach\": { variableScope: true, soyState: \"for-loop\" },\n    \"ifempty\": { noEndTag: true, reduceIndent: true},\n    \"for\": { variableScope: true, soyState: \"for-loop\" },\n    \"call\": { soyState: \"templ-ref\" },\n    \"param\": { soyState: \"param-ref\"},\n    \"print\": { noEndTag: true },\n    \"deltemplate\": { soyState: \"templ-def\", variableScope: true},\n    \"delcall\": { soyState: \"templ-ref\" },\n    \"log\": {},\n    \"element\": { variableScope: true },\n    \"velog\": {},\n    \"const\": { soyState: \"const-def\"},\n  };\n\n  var indentingTags = Object.keys(tags).filter(function(tag) {\n    return !tags[tag].noEndTag || tags[tag].reduceIndent;\n  });\n\n  CodeMirror.defineMode(\"soy\", function(config) {\n    var textMode = CodeMirror.getMode(config, \"text/plain\");\n    var modes = {\n      html: CodeMirror.getMode(config, {name: \"text/html\", multilineTagIndentFactor: 2, multilineTagIndentPastTag: false, allowMissingTagName: true}),\n      attributes: textMode,\n      text: textMode,\n      uri: textMode,\n      trusted_resource_uri: textMode,\n      css: CodeMirror.getMode(config, \"text/css\"),\n      js: CodeMirror.getMode(config, {name: \"text/javascript\", statementIndent: 2 * config.indentUnit})\n    };\n\n    function last(array) {\n      return array[array.length - 1];\n    }\n\n    function tokenUntil(stream, state, untilRegExp) {\n      if (stream.sol()) {\n        for (var indent = 0; indent < state.indent; indent++) {\n          if (!stream.eat(/\\s/)) break;\n        }\n        if (indent) return null;\n      }\n      var oldString = stream.string;\n      var match = untilRegExp.exec(oldString.substr(stream.pos));\n      if (match) {\n        // We don't use backUp because it backs up just the position, not the state.\n        // This uses an undocumented API.\n        stream.string = oldString.substr(0, stream.pos + match.index);\n      }\n      var result = stream.hideFirstChars(state.indent, function() {\n        var localState = last(state.localStates);\n        return localState.mode.token(stream, localState.state);\n      });\n      stream.string = oldString;\n      return result;\n    }\n\n    function contains(list, element) {\n      while (list) {\n        if (list.element === element) return true;\n        list = list.next;\n      }\n      return false;\n    }\n\n    function prepend(list, element) {\n      return {\n        element: element,\n        next: list\n      };\n    }\n\n    function popcontext(state) {\n      if (!state.context) return;\n      if (state.context.scope) {\n        state.variables = state.context.scope;\n      }\n      state.context = state.context.previousContext;\n    }\n\n    // Reference a variable `name` in `list`.\n    // Let `loose` be truthy to ignore missing identifiers.\n    function ref(list, name, loose) {\n      return contains(list, name) ? \"variable-2\" : (loose ? \"variable\" : \"variable-2 error\");\n    }\n\n    // Data for an open soy tag.\n    function Context(previousContext, tag, scope) {\n      this.previousContext = previousContext;\n      this.tag = tag;\n      this.kind = null;\n      this.scope = scope;\n    }\n\n    function expression(stream, state) {\n      var match;\n      if (stream.match(/[[]/)) {\n        state.soyState.push(\"list-literal\");\n        state.context = new Context(state.context, \"list-literal\", state.variables);\n        state.lookupVariables = false;\n        return null;\n      } else if (stream.match(/\\bmap(?=\\()/)) {\n        state.soyState.push(\"map-literal\");\n        return \"keyword\";\n      } else if (stream.match(/\\brecord(?=\\()/)) {\n        state.soyState.push(\"record-literal\");\n        return \"keyword\";\n      } else if (stream.match(/([\\w]+)(?=\\()/)) {\n        return \"variable callee\";\n      } else if (match = stream.match(/^[\"']/)) {\n        state.soyState.push(\"string\");\n        state.quoteKind = match[0];\n        return \"string\";\n      } else if (stream.match(/^[(]/)) {\n        state.soyState.push(\"open-parentheses\");\n        return null;\n      } else if (stream.match(/(null|true|false)(?!\\w)/) ||\n          stream.match(/0x([0-9a-fA-F]{2,})/) ||\n          stream.match(/-?([0-9]*[.])?[0-9]+(e[0-9]*)?/)) {\n        return \"atom\";\n      } else if (stream.match(/(\\||[+\\-*\\/%]|[=!]=|\\?:|[<>]=?)/)) {\n        // Tokenize filter, binary, null propagator, and equality operators.\n        return \"operator\";\n      } else if (match = stream.match(/^\\$([\\w]+)/)) {\n        return ref(state.variables, match[1], !state.lookupVariables);\n      } else if (match = stream.match(/^\\w+/)) {\n        return /^(?:as|and|or|not|in|if)$/.test(match[0]) ? \"keyword\" : null;\n      }\n\n      stream.next();\n      return null;\n    }\n\n    return {\n      startState: function() {\n        return {\n          soyState: [],\n          variables: prepend(null, 'ij'),\n          scopes: null,\n          indent: 0,\n          quoteKind: null,\n          context: null,\n          lookupVariables: true, // Is unknown variables considered an error\n          localStates: [{\n            mode: modes.html,\n            state: CodeMirror.startState(modes.html)\n          }]\n        };\n      },\n\n      copyState: function(state) {\n        return {\n          tag: state.tag, // Last seen Soy tag.\n          soyState: state.soyState.concat([]),\n          variables: state.variables,\n          context: state.context,\n          indent: state.indent, // Indentation of the following line.\n          quoteKind: state.quoteKind,\n          lookupVariables: state.lookupVariables,\n          localStates: state.localStates.map(function(localState) {\n            return {\n              mode: localState.mode,\n              state: CodeMirror.copyState(localState.mode, localState.state)\n            };\n          })\n        };\n      },\n\n      token: function(stream, state) {\n        var match;\n\n        switch (last(state.soyState)) {\n          case \"comment\":\n            if (stream.match(/^.*?\\*\\//)) {\n              state.soyState.pop();\n            } else {\n              stream.skipToEnd();\n            }\n            if (!state.context || !state.context.scope) {\n              var paramRe = /@param\\??\\s+(\\S+)/g;\n              var current = stream.current();\n              for (var match; (match = paramRe.exec(current)); ) {\n                state.variables = prepend(state.variables, match[1]);\n              }\n            }\n            return \"comment\";\n\n          case \"string\":\n            var match = stream.match(/^.*?([\"']|\\\\[\\s\\S])/);\n            if (!match) {\n              stream.skipToEnd();\n            } else if (match[1] == state.quoteKind) {\n              state.quoteKind = null;\n              state.soyState.pop();\n            }\n            return \"string\";\n        }\n\n        if (!state.soyState.length || last(state.soyState) != \"literal\") {\n          if (stream.match(/^\\/\\*/)) {\n            state.soyState.push(\"comment\");\n            return \"comment\";\n          } else if (stream.match(stream.sol() ? /^\\s*\\/\\/.*/ : /^\\s+\\/\\/.*/)) {\n            return \"comment\";\n          }\n        }\n\n        switch (last(state.soyState)) {\n          case \"templ-def\":\n            if (match = stream.match(/^\\.?([\\w]+(?!\\.[\\w]+)*)/)) {\n              state.soyState.pop();\n              return \"def\";\n            }\n            stream.next();\n            return null;\n\n          case \"templ-ref\":\n            if (match = stream.match(/(\\.?[a-zA-Z_][a-zA-Z_0-9]+)+/)) {\n              state.soyState.pop();\n              // If the first character is '.', it can only be a local template.\n              if (match[0][0] == '.') {\n                return \"variable-2\"\n              }\n              // Otherwise\n              return \"variable\";\n            }\n            if (match = stream.match(/^\\$([\\w]+)/)) {\n              state.soyState.pop();\n              return ref(state.variables, match[1], !state.lookupVariables);\n            }\n\n            stream.next();\n            return null;\n\n          case \"namespace-def\":\n            if (match = stream.match(/^\\.?([\\w\\.]+)/)) {\n              state.soyState.pop();\n              return \"variable\";\n            }\n            stream.next();\n            return null;\n\n          case \"param-def\":\n            if (match = stream.match(/^\\*/)) {\n              state.soyState.pop();\n              state.soyState.push(\"param-type\");\n              return \"type\";\n            }\n            if (match = stream.match(/^\\w+/)) {\n              state.variables = prepend(state.variables, match[0]);\n              state.soyState.pop();\n              state.soyState.push(\"param-type\");\n              return \"def\";\n            }\n            stream.next();\n            return null;\n\n          case \"param-ref\":\n            if (match = stream.match(/^\\w+/)) {\n              state.soyState.pop();\n              return \"property\";\n            }\n            stream.next();\n            return null;\n\n          case \"open-parentheses\":\n            if (stream.match(/[)]/)) {\n              state.soyState.pop();\n              return null;\n            }\n            return expression(stream, state);\n\n          case \"param-type\":\n            var peekChar = stream.peek();\n            if (\"}]=>,\".indexOf(peekChar) != -1) {\n              state.soyState.pop();\n              return null;\n            } else if (peekChar == \"[\") {\n              state.soyState.push('param-type-record');\n              return null;\n            } else if (peekChar == \"(\") {\n              state.soyState.push('param-type-template');\n              return null;\n            } else if (peekChar == \"<\") {\n              state.soyState.push('param-type-parameter');\n              return null;\n            } else if (match = stream.match(/^([\\w]+|[?])/)) {\n              return \"type\";\n            }\n            stream.next();\n            return null;\n\n          case \"param-type-record\":\n            var peekChar = stream.peek();\n            if (peekChar == \"]\") {\n              state.soyState.pop();\n              return null;\n            }\n            if (stream.match(/^\\w+/)) {\n              state.soyState.push('param-type');\n              return \"property\";\n            }\n            stream.next();\n            return null;\n\n          case \"param-type-parameter\":\n            if (stream.match(/^[>]/)) {\n              state.soyState.pop();\n              return null;\n            }\n            if (stream.match(/^[<,]/)) {\n              state.soyState.push('param-type');\n              return null;\n            }\n            stream.next();\n            return null;\n\n          case \"param-type-template\":\n            if (stream.match(/[>]/)) {\n              state.soyState.pop();\n              state.soyState.push('param-type');\n              return null;\n            }\n            if (stream.match(/^\\w+/)) {\n              state.soyState.push('param-type');\n              return \"def\";\n            }\n            stream.next();\n            return null;\n\n          case \"var-def\":\n            if (match = stream.match(/^\\$([\\w]+)/)) {\n              state.variables = prepend(state.variables, match[1]);\n              state.soyState.pop();\n              return \"def\";\n            }\n            stream.next();\n            return null;\n\n          case \"for-loop\":\n            if (stream.match(/\\bin\\b/)) {\n              state.soyState.pop();\n              return \"keyword\";\n            }\n            if (stream.peek() == \"$\") {\n              state.soyState.push('var-def');\n              return null;\n            }\n            stream.next();\n            return null;\n\n          case \"record-literal\":\n            if (stream.match(/^[)]/)) {\n              state.soyState.pop();\n              return null;\n            }\n            if (stream.match(/[(,]/)) {\n              state.soyState.push(\"map-value\")\n              state.soyState.push(\"record-key\")\n              return null;\n            }\n            stream.next()\n            return null;\n\n          case \"map-literal\":\n            if (stream.match(/^[)]/)) {\n              state.soyState.pop();\n              return null;\n            }\n            if (stream.match(/[(,]/)) {\n              state.soyState.push(\"map-value\")\n              state.soyState.push(\"map-value\")\n              return null;\n            }\n            stream.next()\n            return null;\n\n          case \"list-literal\":\n            if (stream.match(']')) {\n              state.soyState.pop();\n              state.lookupVariables = true;\n              popcontext(state);\n              return null;\n            }\n            if (stream.match(/\\bfor\\b/)) {\n              state.lookupVariables = true;\n              state.soyState.push('for-loop');\n              return \"keyword\";\n            }\n            return expression(stream, state);\n\n          case \"record-key\":\n            if (stream.match(/[\\w]+/)) {\n              return \"property\";\n            }\n            if (stream.match(/^[:]/)) {\n              state.soyState.pop();\n              return null;\n            }\n            stream.next();\n            return null;\n\n          case \"map-value\":\n            if (stream.peek() == \")\" || stream.peek() == \",\" || stream.match(/^[:)]/)) {\n              state.soyState.pop();\n              return null;\n            }\n            return expression(stream, state);\n\n          case \"import\":\n            if (stream.eat(\";\")) {\n              state.soyState.pop();\n              state.indent -= 2 * config.indentUnit;\n              return null;\n            }\n            if (stream.match(/\\w+(?=\\s+as\\b)/)) {\n              return \"variable\";\n            }\n            if (match = stream.match(/\\w+/)) {\n              return /\\b(from|as)\\b/.test(match[0]) ? \"keyword\" : \"def\";\n            }\n            if (match = stream.match(/^[\"']/)) {\n              state.soyState.push(\"string\");\n              state.quoteKind = match[0];\n              return \"string\";\n            }\n            stream.next();\n            return null;\n\n          case \"tag\":\n            var endTag;\n            var tagName;\n            if (state.tag === undefined) {\n              endTag = true;\n              tagName = '';\n            } else {\n              endTag = state.tag[0] == \"/\";\n              tagName = endTag ? state.tag.substring(1) : state.tag;\n            }\n            var tag = tags[tagName];\n            if (stream.match(/^\\/?}/)) {\n              var selfClosed = stream.current() == \"/}\";\n              if (selfClosed && !endTag) {\n                popcontext(state);\n              }\n              if (state.tag == \"/template\" || state.tag == \"/deltemplate\") {\n                state.variables = prepend(null, 'ij');\n                state.indent = 0;\n              } else {\n                state.indent -= config.indentUnit *\n                    (selfClosed || indentingTags.indexOf(state.tag) == -1 ? 2 : 1);\n              }\n              state.soyState.pop();\n              return \"keyword\";\n            } else if (stream.match(/^([\\w?]+)(?==)/)) {\n              if (state.context && state.context.tag == tagName && stream.current() == \"kind\" && (match = stream.match(/^=\"([^\"]+)/, false))) {\n                var kind = match[1];\n                state.context.kind = kind;\n                var mode = modes[kind] || modes.html;\n                var localState = last(state.localStates);\n                if (localState.mode.indent) {\n                  state.indent += localState.mode.indent(localState.state, \"\", \"\");\n                }\n                state.localStates.push({\n                  mode: mode,\n                  state: CodeMirror.startState(mode)\n                });\n              }\n              return \"attribute\";\n            }\n            return expression(stream, state);\n\n          case \"template-call-expression\":\n            if (stream.match(/^([\\w-?]+)(?==)/)) {\n              return \"attribute\";\n            } else if (stream.eat('>')) {\n              state.soyState.pop();\n              return \"keyword\";\n            } else if (stream.eat('/>')) {\n              state.soyState.pop();\n              return \"keyword\";\n            }\n            return expression(stream, state);\n          case \"literal\":\n            if (stream.match('{/literal}', false)) {\n              state.soyState.pop();\n              return this.token(stream, state);\n            }\n            return tokenUntil(stream, state, /\\{\\/literal}/);\n          case \"export\":\n            if (match = stream.match(/\\w+/)) {\n              state.soyState.pop();\n              if (match == \"const\") {\n                state.soyState.push(\"const-def\")\n                return \"keyword\";\n              } else if (match == \"extern\") {\n                state.soyState.push(\"param-def\")\n                return \"keyword\";\n              }\n            } else {\n              stream.next();\n            }\n            return null;\n          case \"const-def\":\n            if (stream.match(/^\\w+/)) {\n              state.soyState.pop();\n              return \"def\";\n            }\n            stream.next();\n            return null;\n        }\n\n        if (stream.match('{literal}')) {\n          state.indent += config.indentUnit;\n          state.soyState.push(\"literal\");\n          state.context = new Context(state.context, \"literal\", state.variables);\n          return \"keyword\";\n\n        // A tag-keyword must be followed by whitespace, comment or a closing tag.\n        } else if (match = stream.match(/^\\{([/@\\\\]?\\w+\\??)(?=$|[\\s}]|\\/[/*])/)) {\n          var prevTag = state.tag;\n          state.tag = match[1];\n          var endTag = state.tag[0] == \"/\";\n          var indentingTag = !!tags[state.tag];\n          var tagName = endTag ? state.tag.substring(1) : state.tag;\n          var tag = tags[tagName];\n          if (state.tag != \"/switch\")\n            state.indent += ((endTag || tag && tag.reduceIndent) && prevTag != \"switch\" ? 1 : 2) * config.indentUnit;\n\n          state.soyState.push(\"tag\");\n          var tagError = false;\n          if (tag) {\n            if (!endTag) {\n              if (tag.soyState) state.soyState.push(tag.soyState);\n            }\n            // If a new tag, open a new context.\n            if (!tag.noEndTag && (indentingTag || !endTag)) {\n              state.context = new Context(state.context, state.tag, tag.variableScope ? state.variables : null);\n            // Otherwise close the current context.\n            } else if (endTag) {\n              var isBalancedForExtern = tagName == 'extern' && (state.context && state.context.tag == 'export');\n              if (!state.context || ((state.context.tag != tagName) && !isBalancedForExtern)) {\n                tagError = true;\n              } else if (state.context) {\n                if (state.context.kind) {\n                  state.localStates.pop();\n                  var localState = last(state.localStates);\n                  if (localState.mode.indent) {\n                    state.indent -= localState.mode.indent(localState.state, \"\", \"\");\n                  }\n                }\n                popcontext(state);\n              }\n            }\n          } else if (endTag) {\n            // Assume all tags with a closing tag are defined in the config.\n            tagError = true;\n          }\n          return (tagError ? \"error \" : \"\") + \"keyword\";\n\n        // Not a tag-keyword; it's an implicit print tag.\n        } else if (stream.eat('{')) {\n          state.tag = \"print\";\n          state.indent += 2 * config.indentUnit;\n          state.soyState.push(\"tag\");\n          return \"keyword\";\n        } else if (!state.context && stream.sol() && stream.match(/import\\b/)) {\n          state.soyState.push(\"import\");\n          state.indent += 2 * config.indentUnit;\n          return \"keyword\";\n        } else if (match = stream.match('<{')) {\n          state.soyState.push(\"template-call-expression\");\n          state.indent += 2 * config.indentUnit;\n          state.soyState.push(\"tag\");\n          return \"keyword\";\n        } else if (match = stream.match('</>')) {\n          state.indent -= 1 * config.indentUnit;\n          return \"keyword\";\n        }\n\n        return tokenUntil(stream, state, /\\{|\\s+\\/\\/|\\/\\*/);\n      },\n\n      indent: function(state, textAfter, line) {\n        var indent = state.indent, top = last(state.soyState);\n        if (top == \"comment\") return CodeMirror.Pass;\n\n        if (top == \"literal\") {\n          if (/^\\{\\/literal}/.test(textAfter)) indent -= config.indentUnit;\n        } else {\n          if (/^\\s*\\{\\/(template|deltemplate)\\b/.test(textAfter)) return 0;\n          if (/^\\{(\\/|(fallbackmsg|elseif|else|ifempty)\\b)/.test(textAfter)) indent -= config.indentUnit;\n          if (state.tag != \"switch\" && /^\\{(case|default)\\b/.test(textAfter)) indent -= config.indentUnit;\n          if (/^\\{\\/switch\\b/.test(textAfter)) indent -= config.indentUnit;\n        }\n        var localState = last(state.localStates);\n        if (indent && localState.mode.indent) {\n          indent += localState.mode.indent(localState.state, textAfter, line);\n        }\n        return indent;\n      },\n\n      innerMode: function(state) {\n        if (state.soyState.length && last(state.soyState) != \"literal\") return null;\n        else return last(state.localStates);\n      },\n\n      electricInput: /^\\s*\\{(\\/|\\/template|\\/deltemplate|\\/switch|fallbackmsg|elseif|else|case|default|ifempty|\\/literal\\})$/,\n      lineComment: \"//\",\n      blockCommentStart: \"/*\",\n      blockCommentEnd: \"*/\",\n      blockCommentContinue: \" * \",\n      useInnerComments: false,\n      fold: \"indent\"\n    };\n  }, \"htmlmixed\");\n\n  CodeMirror.registerHelper(\"wordChars\", \"soy\", /[\\w$]/);\n\n  CodeMirror.registerHelper(\"hintWords\", \"soy\", Object.keys(tags).concat(\n      [\"css\", \"debugger\"]));\n\n  CodeMirror.defineMIME(\"text/x-soy\", \"soy\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/soy/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"soy\");\n  function MT(name) {test.mode(name, mode, Array.prototype.slice.call(arguments, 1));}\n\n  // Test of small keywords and words containing them.\n  MT('keywords-test',\n     '[keyword {] [keyword as] worrying [keyword and] notorious [keyword as]',\n     '    the Fandor[operator -]alias assassin, [keyword or]',\n     '    Corcand cannot fit [keyword in] [keyword }]');\n\n  MT('let-test',\n     '[keyword {template] [def .name][keyword }]',\n     '  [keyword {let] [def $name]: [string \"world\"][keyword /}]',\n     '  [tag&bracket <][tag h1][tag&bracket >]',\n     '    Hello, [keyword {][variable-2 $name][keyword }]',\n     '  [tag&bracket </][tag h1][tag&bracket >]',\n     '[keyword {/template}]',\n     '');\n\n  MT('function-test',\n     '[keyword {] [callee&variable css]([string \"MyClass\"])[keyword }]',\n     '[tag&bracket <][tag input] [attribute value]=[string \"]' +\n     '[keyword {] [callee&variable index]([variable-2&error $list])[keyword }]' +\n        '[string \"][tag&bracket />]');\n\n  MT('soy-element-composition-test',\n     '[keyword <{][callee&variable foo]()[keyword }]',\n     '[keyword ></>]');\n\n  MT('soy-element-composition-attribute-test',\n     '[keyword <{][callee&variable foo]()[keyword }]',\n     '[attribute class]=[string \"Foo\"]',\n     '[keyword ></>]');\n\n  MT('namespace-test',\n     '[keyword {namespace] [variable namespace][keyword }]')\n\n  MT('namespace-with-attribute-test',\n     '[keyword {namespace] [variable my.namespace.templates] ' +\n         '[attribute requirecss]=[string \"my.namespace\"][keyword }]');\n\n  MT('operators-test',\n     '[keyword {] [atom 1] [operator ==] [atom 1] [keyword }]',\n     '[keyword {] [atom 1] [operator !=] [atom 2] [keyword }]',\n     '[keyword {] [atom 2] [operator +] [atom 2] [keyword }]',\n     '[keyword {] [atom 2] [operator -] [atom 2] [keyword }]',\n     '[keyword {] [atom 2] [operator *] [atom 2] [keyword }]',\n     '[keyword {] [atom 2] [operator /] [atom 2] [keyword }]',\n     '[keyword {] [atom 2] [operator %] [atom 2] [keyword }]',\n     '[keyword {] [atom 2] [operator <=] [atom 2] [keyword }]',\n     '[keyword {] [atom 2] [operator >=] [atom 2] [keyword }]',\n     '[keyword {] [atom 3] [operator >] [atom 2] [keyword }]',\n     '[keyword {] [atom 2] [operator >] [atom 3] [keyword }]',\n     '[keyword {] [atom null] [operator ?:] [string \"\"] [keyword }]',\n     '[keyword {] [variable-2&error $variable] [operator |] safeHtml [keyword }]')\n\n  MT('primitive-test',\n     '[keyword {] [atom true] [keyword }]',\n     '[keyword {] [atom false] [keyword }]',\n     '[keyword {] truethy [keyword }]',\n     '[keyword {] falsey [keyword }]',\n     '[keyword {] [atom 42] [keyword }]',\n     '[keyword {] [atom .42] [keyword }]',\n     '[keyword {] [atom 0.42] [keyword }]',\n     '[keyword {] [atom -0.42] [keyword }]',\n     '[keyword {] [atom -.2] [keyword }]',\n     '[keyword {] [atom 6.03e23] [keyword }]',\n     '[keyword {] [atom -0.03e0] [keyword }]',\n     '[keyword {] [atom 0x1F] [keyword }]',\n     '[keyword {] [atom 0x1F00BBEA] [keyword }]');\n\n  MT('param-type-record',\n     '[keyword {@param] [def record]: [[[property foo]: [type bool], [property bar]: [type int] ]][keyword }]'\n  );\n\n  MT('param-type-map',\n     '[keyword {@param] [def unknown]: [type map]<[type string], [type bool]>[keyword }]'\n  );\n\n  MT('param-type-list',\n     '[keyword {@param] [def list]: [type list]<[type ?]>[keyword }]'\n  );\n\n  MT('param-type-any',\n     '[keyword {@param] [def unknown]: [type ?][keyword }]'\n  );\n\n  MT('param-type-nested',\n     '[keyword {@param] [def a]: ' +\n         '[type list]<[[[property a]: [type int], ' +\n         '[property b]: [type map]<[type string], ' +\n         '[type bool]>]]>][keyword }]');\n\n  MT('undefined-var',\n     '[keyword {][variable-2&error $var]');\n\n  MT('param-scope-test',\n     '[keyword {template] [def .a][keyword }]',\n     '  [keyword {@param] [def x]: [type string][keyword }]',\n     '  [keyword {][variable-2 $x][keyword }]',\n     '[keyword {/template}]',\n     '',\n     '[keyword {template] [def .b][keyword }]',\n     '  [keyword {][variable-2&error $x][keyword }]',\n     '[keyword {/template}]',\n     '');\n\n  MT('if-variable-test',\n     '[keyword {if] [variable-2&error $showThing][keyword }]',\n     '  Yo!',\n     '[keyword {/if}]',\n     '');\n\n  MT('defined-if-variable-test',\n     '[keyword {template] [def .foo][keyword }]',\n     '  [keyword {@param?] [def showThing]: [type bool][keyword }]',\n     '  [keyword {if] [variable-2 $showThing][keyword }]',\n     '    Yo!',\n     '  [keyword {/if}]',\n     '[keyword {/template}]',\n     '');\n\n  MT('template-calls-test',\n     '[keyword {call] [variable-2 .foo][keyword /}]',\n     '[keyword {call] [variable foo][keyword /}]',\n     '[keyword {call] [variable foo][keyword }] [keyword {/call}]',\n     '[keyword {call] [variable first1.second.third_3][keyword /}]',\n     '[keyword {call] [variable first1.second.third_3] [keyword }] [keyword {/call}]',\n     '');\n\n  MT('foreach-scope-test',\n     '[keyword {@param] [def bar]: [type string][keyword }]',\n     '[keyword {foreach] [def $foo] [keyword in] [variable-2&error $foos][keyword }]',\n     '  [keyword {][variable-2 $foo][keyword }]',\n     '[keyword {/foreach}]',\n     '[keyword {][variable-2&error $foo][keyword }]',\n     '[keyword {][variable-2 $bar][keyword }]');\n\n  MT('foreach-ifempty-indent-test',\n     '[keyword {foreach] [def $foo] [keyword in] [variable-2&error $foos][keyword }]',\n     '  something',\n     '[keyword {ifempty}]',\n     '  nothing',\n     '[keyword {/foreach}]',\n     '');\n\n   MT('foreach-index',\n      '[keyword {foreach] [def $foo],[def $index] [keyword in] [[]] [keyword }]',\n      '  [keyword {][variable-2 $foo][keyword }] [keyword {][variable-2 $index][keyword }]',\n      '[keyword {/foreach}]');\n\n  MT('nested-kind-test',\n     '[keyword {template] [def .foo] [attribute kind]=[string \"html\"][keyword }]',\n     '  [tag&bracket <][tag div][tag&bracket >]',\n     '    [keyword {call] [variable-2 .bar][keyword }]',\n     '      [keyword {param] [property propertyName] [attribute kind]=[string \"js\"][keyword }]',\n     '        [keyword var] [def bar] [operator =] [number 5];',\n     '      [keyword {/param}]',\n     '    [keyword {/call}]',\n     '  [tag&bracket </][tag div][tag&bracket >]',\n     '[keyword {/template}]',\n     '');\n\n  MT('tag-starting-with-function-call-is-not-a-keyword',\n     '[keyword {][callee&variable index]([variable-2&error $foo])[keyword }]',\n     '[keyword {css] [string \"some-class\"][keyword }]',\n     '[keyword {][callee&variable css]([string \"some-class\"])[keyword }]',\n     '');\n\n  MT('allow-missing-colon-in-@param',\n     '[keyword {template] [def .foo][keyword }]',\n     '  [keyword {@param] [def showThing] [type bool][keyword }]',\n     '  [keyword {if] [variable-2 $showThing][keyword }]',\n     '    Yo!',\n     '  [keyword {/if}]',\n     '[keyword {/template}]',\n     '');\n\n  MT('param-type-and-default-value',\n     '[keyword {template] [def .foo][keyword }]',\n     '  [keyword {@param] [def bar]: [type bool] = [atom true][keyword }]',\n     '[keyword {/template}]',\n     '');\n\n  MT('attribute-type',\n     '[keyword {template] [def .foo][keyword }]',\n     '  [keyword {@attribute] [def bar]: [type string][keyword }]',\n     '[keyword {/template}]',\n     '');\n\n  MT('attribute-type-optional',\n     '[keyword {template] [def .foo][keyword }]',\n     '  [keyword {@attribute] [def bar]: [type string][keyword }]',\n     '[keyword {/template}]',\n     '');\n\n  MT('attribute-type-all',\n     '[keyword {template] [def .foo][keyword }]',\n     '  [keyword {@attribute] [type *][keyword }]',\n     '[keyword {/template}]',\n     '');\n\n   MT('state-variable-reference',\n     '[keyword {template] [def .foo][keyword }]',\n     '  [keyword {@param] [def bar]:= [atom true][keyword }]',\n     '  [keyword {@state] [def foobar]:= [variable-2 $bar][keyword }]',\n     '[keyword {/template}]',\n     '');\n\n   MT('param-type-template',\n     '[keyword {template] [def .foo][keyword }]',\n     '  [keyword {@param] [def renderer]: ([def s]:[type string])=>[type html][keyword }]',\n     '  [keyword {call] [variable-2 $renderer] [keyword /}]',\n     '[keyword {/template}]',\n     '');\n\n  MT('single-quote-strings',\n     '[keyword {][string \"foo\"] [string \\'bar\\'][keyword }]',\n     '');\n\n  MT('literal-comments',\n     '[keyword {literal}]/* comment */ // comment[keyword {/literal}]');\n\n  MT('highlight-command-at-eol',\n     '[keyword {msg]',\n     '    [keyword }]');\n\n  MT('switch-indent-test',\n     '[keyword {let] [def $marbles]: [atom 5] [keyword /}]',\n     '[keyword {switch] [variable-2 $marbles][keyword }]',\n     '  [keyword {case] [atom 0][keyword }]',\n     '    No marbles',\n     '  [keyword {default}]',\n     '    At least 1 marble',\n     '[keyword {/switch}]',\n     '');\n\n  MT('if-elseif-else-indent',\n     '[keyword {if] [atom true][keyword }]',\n     '  [keyword {let] [def $a]: [atom 5] [keyword /}]',\n     '[keyword {elseif] [atom false][keyword }]',\n     '  [keyword {let] [def $bar]: [atom 5] [keyword /}]',\n     '[keyword {else}]',\n     '  [keyword {let] [def $bar]: [atom 5] [keyword /}]',\n     '[keyword {/if}]');\n\n  MT('msg-fallbackmsg-indent',\n     '[keyword {msg] [attribute desc]=[string \"A message\"][keyword }]',\n     '  A message',\n     '[keyword {fallbackmsg] [attribute desc]=[string \"A message\"][keyword }]',\n     '  Old message',\n     '[keyword {/msg}]');\n\n  MT('literal-indent',\n     '[keyword {template] [def .name][keyword }]',\n     '  [keyword {literal}]',\n     '    Lerum',\n     '  [keyword {/literal}]',\n     '  Ipsum',\n     '[keyword {/template}]');\n\n  MT('special-chars',\n     '[keyword {sp}]',\n     '[keyword {nil}]',\n     '[keyword {\\\\r}]',\n     '[keyword {\\\\n}]',\n     '[keyword {\\\\t}]',\n     '[keyword {lb}]',\n     '[keyword {rb}]');\n\n  MT('let-list-literal',\n     '[keyword {let] [def $test]: [[[[[string \\'a\\'] ], [[[string \\'b\\'] ] ] [keyword /}]');\n\n  MT('let-record-literal',\n     '[keyword {let] [def $test]: [keyword record]([property test]: [callee&variable bidiGlobalDir](), ' +\n         '[property foo]: [atom 5]) [keyword /}]');\n\n  MT('let-map-literal',\n     '[keyword {let] [def $test]: [keyword map]([string \\'outer\\']: [keyword map]([atom 5]: [atom false]), ' +\n         '[string \\'foo\\']: [string \\'bar\\']) [keyword /}]');\n\n  MT('wrong-closing-tag',\n     '[keyword {if] [atom true][keyword }]',\n     '  Optional',\n     '[keyword&error {/badend][keyword }]');\n\n  MT('list-comprehension',\n     '[keyword {let] [def $myList]: [[[[[string \\'a\\'] ] ] [keyword /}] ' +\n     '[keyword {let] [def $test]: [[[variable $a] [operator +] [atom 1] [keyword for] ' +\n         '[def $a] [keyword in] [variable-2 $myList] [keyword if] [variable-2 $a] [operator >=] [atom 3] ] [keyword /}]');\n\n  MT('list-comprehension-index',\n     '[keyword {let] [def $test]: [[[variable $a] [operator +] [variable $index] [keyword for] ' +\n         '[def $a],[def $index] [keyword in] [[]] [keyword if] [variable-2 $a] [operator >=] [variable-2 $index] ] [keyword /}]');\n\n\n  MT('list-comprehension-variable-scope',\n     '[keyword {let] [def $name]: [string \"world\"][keyword /}]',\n     '[keyword {let] [def $test]: [[[variable $a] [operator +] [variable $index] [keyword for] ' +\n         '[def $a],[def $index] [keyword in] [[]] [keyword if] [variable-2 $a] [operator >=] [variable-2 $index] ] [keyword /}]',\n     '[keyword {][variable-2&error $a][keyword }]',\n     '[keyword {][variable-2&error $index][keyword }]',\n     '[keyword {][variable-2 $test][keyword }]',\n     '[keyword {][variable-2 $name][keyword }]');\n\n  MT('import',\n   '[keyword import] {[def Name], [variable Person] [keyword as] [def P]} [keyword from] [string \\'examples/proto/example.proto\\'];');\n\n  MT('velog',\n    '[keyword {velog] [variable-2&error $data][keyword }] Logged [keyword {/velog}]');\n\n  MT('extern', '[keyword {extern] [def renderer]: ([def s]:[type string])=>[type string][keyword }] [keyword {/extern}]');\n\n  MT('export extern', '[keyword {export] [keyword extern] [def renderer]: ([def s]:[type string])=>[type string][keyword }] [keyword {/extern}]');\n\n  MT('const',\n    '[keyword {const] [def FOO] = [atom 5] [keyword /}]',\n    '[keyword {export] [keyword const] [def FOO] = [atom 5] [keyword /}]');\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/sparql/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: SPARQL mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"sparql.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">SPARQL</a>\n  </ul>\n</div>\n\n<article>\n<h2>SPARQL mode</h2>\n<form><textarea id=\"code\" name=\"code\">\nPREFIX a: &lt;http://www.w3.org/2000/10/annotation-ns#>\nPREFIX dc: &lt;http://purl.org/dc/elements/1.1/>\nPREFIX foaf: &lt;http://xmlns.com/foaf/0.1/>\nPREFIX rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#>\n\n# Comment!\n\nSELECT ?given ?family\nWHERE {\n  {\n    ?annot a:annotates &lt;http://www.w3.org/TR/rdf-sparql-query/> .\n    ?annot dc:creator ?c .\n    OPTIONAL {?c foaf:givenName ?given ;\n                 foaf:familyName ?family }\n  } UNION {\n    ?c !foaf:knows/foaf:knows? ?thing.\n    ?thing rdfs\n  } MINUS {\n    ?thing rdfs:label \"剛柔流\"@jp\n  }\n  FILTER isBlank(?c)\n}\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: \"application/sparql-query\",\n        matchBrackets: true\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>application/sparql-query</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/sparql/sparql.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"sparql\", function(config) {\n  var indentUnit = config.indentUnit;\n  var curPunc;\n\n  function wordRegexp(words) {\n    return new RegExp(\"^(?:\" + words.join(\"|\") + \")$\", \"i\");\n  }\n  var ops = wordRegexp([\"str\", \"lang\", \"langmatches\", \"datatype\", \"bound\", \"sameterm\", \"isiri\", \"isuri\",\n                        \"iri\", \"uri\", \"bnode\", \"count\", \"sum\", \"min\", \"max\", \"avg\", \"sample\",\n                        \"group_concat\", \"rand\", \"abs\", \"ceil\", \"floor\", \"round\", \"concat\", \"substr\", \"strlen\",\n                        \"replace\", \"ucase\", \"lcase\", \"encode_for_uri\", \"contains\", \"strstarts\", \"strends\",\n                        \"strbefore\", \"strafter\", \"year\", \"month\", \"day\", \"hours\", \"minutes\", \"seconds\",\n                        \"timezone\", \"tz\", \"now\", \"uuid\", \"struuid\", \"md5\", \"sha1\", \"sha256\", \"sha384\",\n                        \"sha512\", \"coalesce\", \"if\", \"strlang\", \"strdt\", \"isnumeric\", \"regex\", \"exists\",\n                        \"isblank\", \"isliteral\", \"a\", \"bind\"]);\n  var keywords = wordRegexp([\"base\", \"prefix\", \"select\", \"distinct\", \"reduced\", \"construct\", \"describe\",\n                             \"ask\", \"from\", \"named\", \"where\", \"order\", \"limit\", \"offset\", \"filter\", \"optional\",\n                             \"graph\", \"by\", \"asc\", \"desc\", \"as\", \"having\", \"undef\", \"values\", \"group\",\n                             \"minus\", \"in\", \"not\", \"service\", \"silent\", \"using\", \"insert\", \"delete\", \"union\",\n                             \"true\", \"false\", \"with\",\n                             \"data\", \"copy\", \"to\", \"move\", \"add\", \"create\", \"drop\", \"clear\", \"load\", \"into\"]);\n  var operatorChars = /[*+\\-<>=&|\\^\\/!\\?]/;\n  var PN_CHARS = \"[A-Za-z_\\\\-0-9]\";\n  var PREFIX_START = new RegExp(\"[A-Za-z]\");\n  var PREFIX_REMAINDER = new RegExp(\"((\" + PN_CHARS + \"|\\\\.)*(\" + PN_CHARS + \"))?:\");\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    curPunc = null;\n    if (ch == \"$\" || ch == \"?\") {\n      if(ch == \"?\" && stream.match(/\\s/, false)){\n        return \"operator\";\n      }\n      stream.match(/^[A-Za-z0-9_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD][A-Za-z0-9_\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u203F-\\u2040\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD]*/);\n      return \"variable-2\";\n    }\n    else if (ch == \"<\" && !stream.match(/^[\\s\\u00a0=]/, false)) {\n      stream.match(/^[^\\s\\u00a0>]*>?/);\n      return \"atom\";\n    }\n    else if (ch == \"\\\"\" || ch == \"'\") {\n      state.tokenize = tokenLiteral(ch);\n      return state.tokenize(stream, state);\n    }\n    else if (/[{}\\(\\),\\.;\\[\\]]/.test(ch)) {\n      curPunc = ch;\n      return \"bracket\";\n    }\n    else if (ch == \"#\") {\n      stream.skipToEnd();\n      return \"comment\";\n    }\n    else if (operatorChars.test(ch)) {\n      return \"operator\";\n    }\n    else if (ch == \":\") {\n      eatPnLocal(stream);\n      return \"atom\";\n    }\n    else if (ch == \"@\") {\n      stream.eatWhile(/[a-z\\d\\-]/i);\n      return \"meta\";\n    }\n    else if (PREFIX_START.test(ch) && stream.match(PREFIX_REMAINDER)) {\n        eatPnLocal(stream);\n        return \"atom\";\n    }\n    stream.eatWhile(/[_\\w\\d]/);\n    var word = stream.current();\n    if (ops.test(word))\n      return \"builtin\";\n    else if (keywords.test(word))\n      return \"keyword\";\n    else\n      return \"variable\";\n  }\n\n  function eatPnLocal(stream) {\n    stream.match(/(\\.(?=[\\w_\\-\\\\%])|[:\\w_-]|\\\\[-\\\\_~.!$&'()*+,;=/?#@%]|%[a-f\\d][a-f\\d])+/i);\n  }\n\n  function tokenLiteral(quote) {\n    return function(stream, state) {\n      var escaped = false, ch;\n      while ((ch = stream.next()) != null) {\n        if (ch == quote && !escaped) {\n          state.tokenize = tokenBase;\n          break;\n        }\n        escaped = !escaped && ch == \"\\\\\";\n      }\n      return \"string\";\n    };\n  }\n\n  function pushContext(state, type, col) {\n    state.context = {prev: state.context, indent: state.indent, col: col, type: type};\n  }\n  function popContext(state) {\n    state.indent = state.context.indent;\n    state.context = state.context.prev;\n  }\n\n  return {\n    startState: function() {\n      return {tokenize: tokenBase,\n              context: null,\n              indent: 0,\n              col: 0};\n    },\n\n    token: function(stream, state) {\n      if (stream.sol()) {\n        if (state.context && state.context.align == null) state.context.align = false;\n        state.indent = stream.indentation();\n      }\n      if (stream.eatSpace()) return null;\n      var style = state.tokenize(stream, state);\n\n      if (style != \"comment\" && state.context && state.context.align == null && state.context.type != \"pattern\") {\n        state.context.align = true;\n      }\n\n      if (curPunc == \"(\") pushContext(state, \")\", stream.column());\n      else if (curPunc == \"[\") pushContext(state, \"]\", stream.column());\n      else if (curPunc == \"{\") pushContext(state, \"}\", stream.column());\n      else if (/[\\]\\}\\)]/.test(curPunc)) {\n        while (state.context && state.context.type == \"pattern\") popContext(state);\n        if (state.context && curPunc == state.context.type) {\n          popContext(state);\n          if (curPunc == \"}\" && state.context && state.context.type == \"pattern\")\n            popContext(state);\n        }\n      }\n      else if (curPunc == \".\" && state.context && state.context.type == \"pattern\") popContext(state);\n      else if (/atom|string|variable/.test(style) && state.context) {\n        if (/[\\}\\]]/.test(state.context.type))\n          pushContext(state, \"pattern\", stream.column());\n        else if (state.context.type == \"pattern\" && !state.context.align) {\n          state.context.align = true;\n          state.context.col = stream.column();\n        }\n      }\n\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      var firstChar = textAfter && textAfter.charAt(0);\n      var context = state.context;\n      if (/[\\]\\}]/.test(firstChar))\n        while (context && context.type == \"pattern\") context = context.prev;\n\n      var closing = context && firstChar == context.type;\n      if (!context)\n        return 0;\n      else if (context.type == \"pattern\")\n        return context.col;\n      else if (context.align)\n        return context.col + (closing ? 0 : 1);\n      else\n        return context.indent + (closing ? 0 : indentUnit);\n    },\n\n    lineComment: \"#\"\n  };\n});\n\nCodeMirror.defineMIME(\"application/sparql-query\", \"sparql\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/spreadsheet/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Spreadsheet mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"spreadsheet.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Spreadsheet</a>\n  </ul>\n</div>\n\n<article>\n  <h2>Spreadsheet mode</h2>\n  <form><textarea id=\"code\" name=\"code\">=IF(A1:B2, TRUE, FALSE) / 100</textarea></form>\n\n  <script>\n    var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n      lineNumbers: true,\n      matchBrackets: true,\n      extraKeys: {\"Tab\":  \"indentAuto\"}\n    });\n  </script>\n\n  <p><strong>MIME types defined:</strong> <code>text/x-spreadsheet</code>.</p>\n  \n  <h3>The Spreadsheet Mode</h3>\n  <p> Created by <a href=\"https://github.com/robertleeplummerjr\">Robert Plummer</a></p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/spreadsheet/spreadsheet.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"spreadsheet\", function () {\n    return {\n      startState: function () {\n        return {\n          stringType: null,\n          stack: []\n        };\n      },\n      token: function (stream, state) {\n        if (!stream) return;\n\n        //check for state changes\n        if (state.stack.length === 0) {\n          //strings\n          if ((stream.peek() == '\"') || (stream.peek() == \"'\")) {\n            state.stringType = stream.peek();\n            stream.next(); // Skip quote\n            state.stack.unshift(\"string\");\n          }\n        }\n\n        //return state\n        //stack has\n        switch (state.stack[0]) {\n        case \"string\":\n          while (state.stack[0] === \"string\" && !stream.eol()) {\n            if (stream.peek() === state.stringType) {\n              stream.next(); // Skip quote\n              state.stack.shift(); // Clear flag\n            } else if (stream.peek() === \"\\\\\") {\n              stream.next();\n              stream.next();\n            } else {\n              stream.match(/^.[^\\\\\\\"\\']*/);\n            }\n          }\n          return \"string\";\n\n        case \"characterClass\":\n          while (state.stack[0] === \"characterClass\" && !stream.eol()) {\n            if (!(stream.match(/^[^\\]\\\\]+/) || stream.match(/^\\\\./)))\n              state.stack.shift();\n          }\n          return \"operator\";\n        }\n\n        var peek = stream.peek();\n\n        //no stack\n        switch (peek) {\n        case \"[\":\n          stream.next();\n          state.stack.unshift(\"characterClass\");\n          return \"bracket\";\n        case \":\":\n          stream.next();\n          return \"operator\";\n        case \"\\\\\":\n          if (stream.match(/\\\\[a-z]+/)) return \"string-2\";\n          else {\n            stream.next();\n            return \"atom\";\n          }\n        case \".\":\n        case \",\":\n        case \";\":\n        case \"*\":\n        case \"-\":\n        case \"+\":\n        case \"^\":\n        case \"<\":\n        case \"/\":\n        case \"=\":\n          stream.next();\n          return \"atom\";\n        case \"$\":\n          stream.next();\n          return \"builtin\";\n        }\n\n        if (stream.match(/\\d+/)) {\n          if (stream.match(/^\\w+/)) return \"error\";\n          return \"number\";\n        } else if (stream.match(/^[a-zA-Z_]\\w*/)) {\n          if (stream.match(/(?=[\\(.])/, false)) return \"keyword\";\n          return \"variable-2\";\n        } else if ([\"[\", \"]\", \"(\", \")\", \"{\", \"}\"].indexOf(peek) != -1) {\n          stream.next();\n          return \"bracket\";\n        } else if (!stream.eatSpace()) {\n          stream.next();\n        }\n        return null;\n      }\n    };\n  });\n\n  CodeMirror.defineMIME(\"text/x-spreadsheet\", \"spreadsheet\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/sql/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: SQL Mode for CodeMirror</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\" />\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"sql.js\"></script>\n<link rel=\"stylesheet\" href=\"../../addon/hint/show-hint.css\" />\n<script src=\"../../addon/hint/show-hint.js\"></script>\n<script src=\"../../addon/hint/sql-hint.js\"></script>\n<style>\n.CodeMirror {\n    border-top: 1px solid black;\n    border-bottom: 1px solid black;\n}\n        </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">SQL Mode for CodeMirror</a>\n  </ul>\n</div>\n\n<article>\n<h2>SQL Mode for CodeMirror</h2>\n<form>\n            <textarea id=\"code\" name=\"code\">-- SQL Mode for CodeMirror\nSELECT SQL_NO_CACHE DISTINCT\n\t\t@var1 AS `val1`, @'val2', @global.'sql_mode',\n\t\t1.1 AS `float_val`, .14 AS `another_float`, 0.09e3 AS `int_with_esp`,\n\t\t0xFA5 AS `hex`, x'fa5' AS `hex2`, 0b101 AS `bin`, b'101' AS `bin2`,\n\t\tDATE '1994-01-01' AS `sql_date`, { T \"1994-01-01\" } AS `odbc_date`,\n\t\t'my string', _utf8'your string', N'her string',\n        TRUE, FALSE, UNKNOWN\n\tFROM DUAL\n\t-- space needed after '--'\n\t# 1 line comment\n\t/* multiline\n\tcomment! */\n\tLIMIT 1 OFFSET 0;\n</textarea>\n            </form>\n            <p><strong>MIME types defined:</strong>\n            <code><a href=\"?mime=text/x-sql\">text/x-sql</a></code>,\n            <code><a href=\"?mime=text/x-mysql\">text/x-mysql</a></code>,\n            <code><a href=\"?mime=text/x-mariadb\">text/x-mariadb</a></code>,\n            <code><a href=\"?mime=text/x-cassandra\">text/x-cassandra</a></code>,\n            <code><a href=\"?mime=text/x-plsql\">text/x-plsql</a></code>,\n            <code><a href=\"?mime=text/x-mssql\">text/x-mssql</a></code>,\n            <code><a href=\"?mime=text/x-hive\">text/x-hive</a></code>,\n            <code><a href=\"?mime=text/x-pgsql\">text/x-pgsql</a></code>,\n            <code><a href=\"?mime=text/x-gql\">text/x-gql</a></code>,\n            <code><a href=\"?mime=text/x-gpsql\">text/x-gpsql</a></code>.\n            <code><a href=\"?mime=text/x-esper\">text/x-esper</a></code>.\n            <code><a href=\"?mime=text/x-sqlite\">text/x-sqlite</a></code>.\n            <code><a href=\"?mime=text/x-sparksql\">text/x-sparksql</a></code>.\n            <code><a href=\"?mime=text/x-trino\">text/x-trino</a></code>.\n        </p>\n<script>\nwindow.onload = function() {\n  var mime = 'text/x-mariadb';\n  // get mime type\n  if (window.location.href.indexOf('mime=') > -1) {\n    mime = window.location.href.substr(window.location.href.indexOf('mime=') + 5);\n  }\n  window.editor = CodeMirror.fromTextArea(document.getElementById('code'), {\n    mode: mime,\n    indentWithTabs: true,\n    smartIndent: true,\n    lineNumbers: true,\n    matchBrackets : true,\n    autofocus: true,\n    extraKeys: {\"Ctrl-Space\": \"autocomplete\"},\n    hintOptions: {tables: {\n      users: [\"name\", \"score\", \"birthDate\"],\n      countries: [\"name\", \"population\", \"size\"]\n    }}\n  });\n};\n</script>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/sql/sql.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"sql\", function(config, parserConfig) {\n  var client         = parserConfig.client || {},\n      atoms          = parserConfig.atoms || {\"false\": true, \"true\": true, \"null\": true},\n      builtin        = parserConfig.builtin || set(defaultBuiltin),\n      keywords       = parserConfig.keywords || set(sqlKeywords),\n      operatorChars  = parserConfig.operatorChars || /^[*+\\-%<>!=&|~^\\/]/,\n      support        = parserConfig.support || {},\n      hooks          = parserConfig.hooks || {},\n      dateSQL        = parserConfig.dateSQL || {\"date\" : true, \"time\" : true, \"timestamp\" : true},\n      backslashStringEscapes = parserConfig.backslashStringEscapes !== false,\n      brackets       = parserConfig.brackets || /^[\\{}\\(\\)\\[\\]]/,\n      punctuation    = parserConfig.punctuation || /^[;.,:]/\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n\n    // call hooks from the mime type\n    if (hooks[ch]) {\n      var result = hooks[ch](stream, state);\n      if (result !== false) return result;\n    }\n\n    if (support.hexNumber &&\n      ((ch == \"0\" && stream.match(/^[xX][0-9a-fA-F]+/))\n      || (ch == \"x\" || ch == \"X\") && stream.match(/^'[0-9a-fA-F]*'/))) {\n      // hex\n      // ref: https://dev.mysql.com/doc/refman/8.0/en/hexadecimal-literals.html\n      return \"number\";\n    } else if (support.binaryNumber &&\n      (((ch == \"b\" || ch == \"B\") && stream.match(/^'[01]*'/))\n      || (ch == \"0\" && stream.match(/^b[01]+/)))) {\n      // bitstring\n      // ref: https://dev.mysql.com/doc/refman/8.0/en/bit-value-literals.html\n      return \"number\";\n    } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {\n      // numbers\n      // ref: https://dev.mysql.com/doc/refman/8.0/en/number-literals.html\n      stream.match(/^[0-9]*(\\.[0-9]+)?([eE][-+]?[0-9]+)?/);\n      support.decimallessFloat && stream.match(/^\\.(?!\\.)/);\n      return \"number\";\n    } else if (ch == \"?\" && (stream.eatSpace() || stream.eol() || stream.eat(\";\"))) {\n      // placeholders\n      return \"variable-3\";\n    } else if (ch == \"'\" || (ch == '\"' && support.doubleQuote)) {\n      // strings\n      // ref: https://dev.mysql.com/doc/refman/8.0/en/string-literals.html\n      state.tokenize = tokenLiteral(ch);\n      return state.tokenize(stream, state);\n    } else if ((((support.nCharCast && (ch == \"n\" || ch == \"N\"))\n        || (support.charsetCast && ch == \"_\" && stream.match(/[a-z][a-z0-9]*/i)))\n        && (stream.peek() == \"'\" || stream.peek() == '\"'))) {\n      // charset casting: _utf8'str', N'str', n'str'\n      // ref: https://dev.mysql.com/doc/refman/8.0/en/string-literals.html\n      return \"keyword\";\n    } else if (support.escapeConstant && (ch == \"e\" || ch == \"E\")\n        && (stream.peek() == \"'\" || (stream.peek() == '\"' && support.doubleQuote))) {\n      // escape constant: E'str', e'str'\n      // ref: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-ESCAPE\n      state.tokenize = function(stream, state) {\n        return (state.tokenize = tokenLiteral(stream.next(), true))(stream, state);\n      }\n      return \"keyword\";\n    } else if (support.commentSlashSlash && ch == \"/\" && stream.eat(\"/\")) {\n      // 1-line comment\n      stream.skipToEnd();\n      return \"comment\";\n    } else if ((support.commentHash && ch == \"#\")\n        || (ch == \"-\" && stream.eat(\"-\") && (!support.commentSpaceRequired || stream.eat(\" \")))) {\n      // 1-line comments\n      // ref: https://kb.askmonty.org/en/comment-syntax/\n      stream.skipToEnd();\n      return \"comment\";\n    } else if (ch == \"/\" && stream.eat(\"*\")) {\n      // multi-line comments\n      // ref: https://kb.askmonty.org/en/comment-syntax/\n      state.tokenize = tokenComment(1);\n      return state.tokenize(stream, state);\n    } else if (ch == \".\") {\n      // .1 for 0.1\n      if (support.zerolessFloat && stream.match(/^(?:\\d+(?:e[+-]?\\d+)?)/i))\n        return \"number\";\n      if (stream.match(/^\\.+/))\n        return null\n      if (stream.match(/^[\\w\\d_$#]+/))\n        return \"variable-2\";\n    } else if (operatorChars.test(ch)) {\n      // operators\n      stream.eatWhile(operatorChars);\n      return \"operator\";\n    } else if (brackets.test(ch)) {\n      // brackets\n      return \"bracket\";\n    } else if (punctuation.test(ch)) {\n      // punctuation\n      stream.eatWhile(punctuation);\n      return \"punctuation\";\n    } else if (ch == '{' &&\n        (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*\"[^\"]*\"( )*}/))) {\n      // dates (weird ODBC syntax)\n      // ref: https://dev.mysql.com/doc/refman/8.0/en/date-and-time-literals.html\n      return \"number\";\n    } else {\n      stream.eatWhile(/^[_\\w\\d]/);\n      var word = stream.current().toLowerCase();\n      // dates (standard SQL syntax)\n      // ref: https://dev.mysql.com/doc/refman/8.0/en/date-and-time-literals.html\n      if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+\"[^\"]*\"/)))\n        return \"number\";\n      if (atoms.hasOwnProperty(word)) return \"atom\";\n      if (builtin.hasOwnProperty(word)) return \"type\";\n      if (keywords.hasOwnProperty(word)) return \"keyword\";\n      if (client.hasOwnProperty(word)) return \"builtin\";\n      return null;\n    }\n  }\n\n  // 'string', with char specified in quote escaped by '\\'\n  function tokenLiteral(quote, backslashEscapes) {\n    return function(stream, state) {\n      var escaped = false, ch;\n      while ((ch = stream.next()) != null) {\n        if (ch == quote && !escaped) {\n          state.tokenize = tokenBase;\n          break;\n        }\n        escaped = (backslashStringEscapes || backslashEscapes) && !escaped && ch == \"\\\\\";\n      }\n      return \"string\";\n    };\n  }\n  function tokenComment(depth) {\n    return function(stream, state) {\n      var m = stream.match(/^.*?(\\/\\*|\\*\\/)/)\n      if (!m) stream.skipToEnd()\n      else if (m[1] == \"/*\") state.tokenize = tokenComment(depth + 1)\n      else if (depth > 1) state.tokenize = tokenComment(depth - 1)\n      else state.tokenize = tokenBase\n      return \"comment\"\n    }\n  }\n\n  function pushContext(stream, state, type) {\n    state.context = {\n      prev: state.context,\n      indent: stream.indentation(),\n      col: stream.column(),\n      type: type\n    };\n  }\n\n  function popContext(state) {\n    state.indent = state.context.indent;\n    state.context = state.context.prev;\n  }\n\n  return {\n    startState: function() {\n      return {tokenize: tokenBase, context: null};\n    },\n\n    token: function(stream, state) {\n      if (stream.sol()) {\n        if (state.context && state.context.align == null)\n          state.context.align = false;\n      }\n      if (state.tokenize == tokenBase && stream.eatSpace()) return null;\n\n      var style = state.tokenize(stream, state);\n      if (style == \"comment\") return style;\n\n      if (state.context && state.context.align == null)\n        state.context.align = true;\n\n      var tok = stream.current();\n      if (tok == \"(\")\n        pushContext(stream, state, \")\");\n      else if (tok == \"[\")\n        pushContext(stream, state, \"]\");\n      else if (state.context && state.context.type == tok)\n        popContext(state);\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      var cx = state.context;\n      if (!cx) return CodeMirror.Pass;\n      var closing = textAfter.charAt(0) == cx.type;\n      if (cx.align) return cx.col + (closing ? 0 : 1);\n      else return cx.indent + (closing ? 0 : config.indentUnit);\n    },\n\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    lineComment: support.commentSlashSlash ? \"//\" : support.commentHash ? \"#\" : \"--\",\n    closeBrackets: \"()[]{}''\\\"\\\"``\",\n    config: parserConfig\n  };\n});\n\n  // `identifier`\n  function hookIdentifier(stream) {\n    // MySQL/MariaDB identifiers\n    // ref: https://dev.mysql.com/doc/refman/8.0/en/identifier-qualifiers.html\n    var ch;\n    while ((ch = stream.next()) != null) {\n      if (ch == \"`\" && !stream.eat(\"`\")) return \"variable-2\";\n    }\n    stream.backUp(stream.current().length - 1);\n    return stream.eatWhile(/\\w/) ? \"variable-2\" : null;\n  }\n\n  // \"identifier\"\n  function hookIdentifierDoublequote(stream) {\n    // Standard SQL /SQLite identifiers\n    // ref: http://web.archive.org/web/20160813185132/http://savage.net.au/SQL/sql-99.bnf.html#delimited%20identifier\n    // ref: http://sqlite.org/lang_keywords.html\n    var ch;\n    while ((ch = stream.next()) != null) {\n      if (ch == \"\\\"\" && !stream.eat(\"\\\"\")) return \"variable-2\";\n    }\n    stream.backUp(stream.current().length - 1);\n    return stream.eatWhile(/\\w/) ? \"variable-2\" : null;\n  }\n\n  // variable token\n  function hookVar(stream) {\n    // variables\n    // @@prefix.varName @varName\n    // varName can be quoted with ` or ' or \"\n    // ref: https://dev.mysql.com/doc/refman/8.0/en/user-variables.html\n    if (stream.eat(\"@\")) {\n      stream.match('session.');\n      stream.match('local.');\n      stream.match('global.');\n    }\n\n    if (stream.eat(\"'\")) {\n      stream.match(/^.*'/);\n      return \"variable-2\";\n    } else if (stream.eat('\"')) {\n      stream.match(/^.*\"/);\n      return \"variable-2\";\n    } else if (stream.eat(\"`\")) {\n      stream.match(/^.*`/);\n      return \"variable-2\";\n    } else if (stream.match(/^[0-9a-zA-Z$\\.\\_]+/)) {\n      return \"variable-2\";\n    }\n    return null;\n  };\n\n  // short client keyword token\n  function hookClient(stream) {\n    // \\N means NULL\n    // ref: https://dev.mysql.com/doc/refman/8.0/en/null-values.html\n    if (stream.eat(\"N\")) {\n        return \"atom\";\n    }\n    // \\g, etc\n    // ref: https://dev.mysql.com/doc/refman/8.0/en/mysql-commands.html\n    return stream.match(/^[a-zA-Z.#!?]/) ? \"variable-2\" : null;\n  }\n\n  // these keywords are used by all SQL dialects (however, a mode can still overwrite it)\n  var sqlKeywords = \"alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit \";\n\n  // turn a space-separated list into an array\n  function set(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  var defaultBuiltin = \"bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric\"\n\n  // A generic SQL Mode. It's not a standard, it just tries to support what is generally supported\n  CodeMirror.defineMIME(\"text/x-sql\", {\n    name: \"sql\",\n    keywords: set(sqlKeywords + \"begin\"),\n    builtin: set(defaultBuiltin),\n    atoms: set(\"false true null unknown\"),\n    dateSQL: set(\"date time timestamp\"),\n    support: set(\"doubleQuote binaryNumber hexNumber\")\n  });\n\n  CodeMirror.defineMIME(\"text/x-mssql\", {\n    name: \"sql\",\n    client: set(\"$partition binary_checksum checksum connectionproperty context_info current_request_id error_line error_message error_number error_procedure error_severity error_state formatmessage get_filestream_transaction_context getansinull host_id host_name isnull isnumeric min_active_rowversion newid newsequentialid rowcount_big xact_state object_id\"),\n    keywords: set(sqlKeywords + \"begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare exec go if use index holdlock nolock nowait paglock readcommitted readcommittedlock readpast readuncommitted repeatableread rowlock serializable snapshot tablock tablockx updlock with\"),\n    builtin: set(\"bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table \"),\n    atoms: set(\"is not null like and or in left right between inner outer join all any some cross unpivot pivot exists\"),\n    operatorChars: /^[*+\\-%<>!=^\\&|\\/]/,\n    brackets: /^[\\{}\\(\\)]/,\n    punctuation: /^[;.,:/]/,\n    backslashStringEscapes: false,\n    dateSQL: set(\"date datetimeoffset datetime2 smalldatetime datetime time\"),\n    hooks: {\n      \"@\":   hookVar\n    }\n  });\n\n  CodeMirror.defineMIME(\"text/x-mysql\", {\n    name: \"sql\",\n    client: set(\"charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee\"),\n    keywords: set(sqlKeywords + \"accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group group_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat\"),\n    builtin: set(\"bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric\"),\n    atoms: set(\"false true null unknown\"),\n    operatorChars: /^[*+\\-%<>!=&|^]/,\n    dateSQL: set(\"date time timestamp\"),\n    support: set(\"decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired\"),\n    hooks: {\n      \"@\":   hookVar,\n      \"`\":   hookIdentifier,\n      \"\\\\\":  hookClient\n    }\n  });\n\n  CodeMirror.defineMIME(\"text/x-mariadb\", {\n    name: \"sql\",\n    client: set(\"charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee\"),\n    keywords: set(sqlKeywords + \"accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group group_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat\"),\n    builtin: set(\"bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric\"),\n    atoms: set(\"false true null unknown\"),\n    operatorChars: /^[*+\\-%<>!=&|^]/,\n    dateSQL: set(\"date time timestamp\"),\n    support: set(\"decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired\"),\n    hooks: {\n      \"@\":   hookVar,\n      \"`\":   hookIdentifier,\n      \"\\\\\":  hookClient\n    }\n  });\n\n  // provided by the phpLiteAdmin project - phpliteadmin.org\n  CodeMirror.defineMIME(\"text/x-sqlite\", {\n    name: \"sql\",\n    // commands of the official SQLite client, ref: https://www.sqlite.org/cli.html#dotcmd\n    client: set(\"auth backup bail binary changes check clone databases dbinfo dump echo eqp exit explain fullschema headers help import imposter indexes iotrace limit lint load log mode nullvalue once open output print prompt quit read restore save scanstats schema separator session shell show stats system tables testcase timeout timer trace vfsinfo vfslist vfsname width\"),\n    // ref: http://sqlite.org/lang_keywords.html\n    keywords: set(sqlKeywords + \"abort action add after all analyze attach autoincrement before begin cascade case cast check collate column commit conflict constraint cross current_date current_time current_timestamp database default deferrable deferred detach each else end escape except exclusive exists explain fail for foreign full glob if ignore immediate index indexed initially inner instead intersect isnull key left limit match natural no notnull null of offset outer plan pragma primary query raise recursive references regexp reindex release rename replace restrict right rollback row savepoint temp temporary then to transaction trigger unique using vacuum view virtual when with without\"),\n    // SQLite is weakly typed, ref: http://sqlite.org/datatype3.html. This is just a list of some common types.\n    builtin: set(\"bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text clob bigint int int2 int8 integer float double char varchar date datetime year unsigned signed numeric real\"),\n    // ref: http://sqlite.org/syntax/literal-value.html\n    atoms: set(\"null current_date current_time current_timestamp\"),\n    // ref: http://sqlite.org/lang_expr.html#binaryops\n    operatorChars: /^[*+\\-%<>!=&|/~]/,\n    // SQLite is weakly typed, ref: http://sqlite.org/datatype3.html. This is just a list of some common types.\n    dateSQL: set(\"date time timestamp datetime\"),\n    support: set(\"decimallessFloat zerolessFloat\"),\n    identifierQuote: \"\\\"\",  //ref: http://sqlite.org/lang_keywords.html\n    hooks: {\n      // bind-parameters ref:http://sqlite.org/lang_expr.html#varparam\n      \"@\":   hookVar,\n      \":\":   hookVar,\n      \"?\":   hookVar,\n      \"$\":   hookVar,\n      // The preferred way to escape Identifiers is using double quotes, ref: http://sqlite.org/lang_keywords.html\n      \"\\\"\":   hookIdentifierDoublequote,\n      // there is also support for backticks, ref: http://sqlite.org/lang_keywords.html\n      \"`\":   hookIdentifier\n    }\n  });\n\n  // the query language used by Apache Cassandra is called CQL, but this mime type\n  // is called Cassandra to avoid confusion with Contextual Query Language\n  CodeMirror.defineMIME(\"text/x-cassandra\", {\n    name: \"sql\",\n    client: { },\n    keywords: set(\"add all allow alter and any apply as asc authorize batch begin by clustering columnfamily compact consistency count create custom delete desc distinct drop each_quorum exists filtering from grant if in index insert into key keyspace keyspaces level limit local_one local_quorum modify nan norecursive nosuperuser not of on one order password permission permissions primary quorum rename revoke schema select set storage superuser table three to token truncate ttl two type unlogged update use user users using values where with writetime\"),\n    builtin: set(\"ascii bigint blob boolean counter decimal double float frozen inet int list map static text timestamp timeuuid tuple uuid varchar varint\"),\n    atoms: set(\"false true infinity NaN\"),\n    operatorChars: /^[<>=]/,\n    dateSQL: { },\n    support: set(\"commentSlashSlash decimallessFloat\"),\n    hooks: { }\n  });\n\n  // this is based on Peter Raganitsch's 'plsql' mode\n  CodeMirror.defineMIME(\"text/x-plsql\", {\n    name:       \"sql\",\n    client:     set(\"appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap\"),\n    keywords:   set(\"abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work\"),\n    builtin:    set(\"abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least length lengthb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml\"),\n    operatorChars: /^[*\\/+\\-%<>!=~]/,\n    dateSQL:    set(\"date time timestamp\"),\n    support:    set(\"doubleQuote nCharCast zerolessFloat binaryNumber hexNumber\")\n  });\n\n  // Created to support specific hive keywords\n  CodeMirror.defineMIME(\"text/x-hive\", {\n    name: \"sql\",\n    keywords: set(\"select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with admin authorization char compact compactions conf cube current current_date current_timestamp day decimal defined dependency directories elem_type exchange file following for grouping hour ignore inner interval jar less logical macro minute month more none noscan over owner partialscan preceding pretty principals protection reload rewrite role roles rollup rows second server sets skewed transactions truncate unbounded unset uri user values window year\"),\n    builtin: set(\"bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype key_type utctimestamp value_type varchar\"),\n    atoms: set(\"false true null unknown\"),\n    operatorChars: /^[*+\\-%<>!=]/,\n    dateSQL: set(\"date timestamp\"),\n    support: set(\"doubleQuote binaryNumber hexNumber\")\n  });\n\n  CodeMirror.defineMIME(\"text/x-pgsql\", {\n    name: \"sql\",\n    client: set(\"source\"),\n    // For PostgreSQL - https://www.postgresql.org/docs/11/sql-keywords-appendix.html\n    // For pl/pgsql lang - https://github.com/postgres/postgres/blob/REL_11_2/src/pl/plpgsql/src/pl_scanner.c\n    keywords: set(sqlKeywords + \"a abort abs absent absolute access according action ada add admin after aggregate alias all allocate also alter always analyse analyze and any are array array_agg array_max_cardinality as asc asensitive assert assertion assignment asymmetric at atomic attach attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli between bigint binary bit bit_length blob blocked bom boolean both breadth by c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain char char_length character character_length character_set_catalog character_set_name character_set_schema characteristics characters check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column column_name columns command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constant constraint constraint_catalog constraint_name constraint_schema constraints constructor contains content continue control conversion convert copy corr corresponding cost count covar_pop covar_samp create cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datatype date datetime_interval_code datetime_interval_precision day db deallocate debug dec decimal declare default defaults deferrable deferred defined definer degree delete delimiter delimiters dense_rank depends depth deref derived desc describe descriptor detach detail deterministic diagnostics dictionary disable discard disconnect dispatch distinct dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain double drop dump dynamic dynamic_function dynamic_function_code each element else elseif elsif empty enable encoding encrypted end end_frame end_partition endexec enforced enum equals errcode error escape event every except exception exclude excluding exclusive exec execute exists exit exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreach foreign fortran forward found frame_row free freeze from fs full function functions fusion g general generated get global go goto grant granted greatest group grouping groups handler having header hex hierarchy hint hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import in include including increment indent index indexes indicator info inherit inherits initially inline inner inout input insensitive insert instance instantiable instead int integer integrity intersect intersection interval into invoker is isnull isolation join k key key_member key_type label lag language large last last_value lateral lead leading leakproof least left length level library like like_regex limit link listen ln load local localtime localtimestamp location locator lock locked log logged loop lower m map mapping match matched materialized max max_cardinality maxvalue member merge message message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized not nothing notice notify notnull nowait nth_value ntile null nullable nullif nulls number numeric object occurrences_regex octet_length octets of off offset oids old on only open operator option options or order ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password path percent percent_rank percentile_cont percentile_disc perform period permission pg_context pg_datatype_name pg_exception_context pg_exception_detail pg_exception_hint placing plans pli policy portion position position_regex power precedes preceding precision prepare prepared preserve primary print_strict_params prior privileges procedural procedure procedures program public publication query quote raise range rank read reads real reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict result result_oid return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns reverse revoke right role rollback rollup routine routine_catalog routine_name routine_schema routines row row_count row_number rows rowtype rule savepoint scale schema schema_name schemas scope scope_catalog scope_name scope_schema scroll search second section security select selective self sensitive sequence sequences serializable server server_name session session_user set setof sets share show similar simple size skip slice smallint snapshot some source space specific specific_name specifictype sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable stacked standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset subscription substring substring_regex succeeds sum symmetric sysid system system_time system_user t table table_name tables tablesample tablespace temp template temporary text then ties time timestamp timezone_hour timezone_minute to token top_level_count trailing transaction transaction_active transactions_committed transactions_rolled_back transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted union unique unknown unlink unlisten unlogged unnamed unnest until untyped update upper uri usage use_column use_variable user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of values var_pop var_samp varbinary varchar variable_conflict variadic varying verbose version versioning view views volatile warning when whenever where while whitespace width_bucket window with within without work wrapper write xml xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes zone\"),\n    // https://www.postgresql.org/docs/11/datatype.html\n    builtin: set(\"bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time zone timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml\"),\n    atoms: set(\"false true null unknown\"),\n    operatorChars: /^[*\\/+\\-%<>!=&|^\\/#@?~]/,\n    backslashStringEscapes: false,\n    dateSQL: set(\"date time timestamp\"),\n    support: set(\"decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast escapeConstant\")\n  });\n\n  // Google's SQL-like query language, GQL\n  CodeMirror.defineMIME(\"text/x-gql\", {\n    name: \"sql\",\n    keywords: set(\"ancestor and asc by contains desc descendant distinct from group has in is limit offset on order select superset where\"),\n    atoms: set(\"false true\"),\n    builtin: set(\"blob datetime first key __key__ string integer double boolean null\"),\n    operatorChars: /^[*+\\-%<>!=]/\n  });\n\n  // Greenplum\n  CodeMirror.defineMIME(\"text/x-gpsql\", {\n    name: \"sql\",\n    client: set(\"source\"),\n    //https://github.com/greenplum-db/gpdb/blob/master/src/include/parser/kwlist.h\n    keywords: set(\"abort absolute access action active add admin after aggregate all also alter always analyse analyze and any array as asc assertion assignment asymmetric at authorization backward before begin between bigint binary bit boolean both by cache called cascade cascaded case cast chain char character characteristics check checkpoint class close cluster coalesce codegen collate column comment commit committed concurrency concurrently configuration connection constraint constraints contains content continue conversion copy cost cpu_rate_limit create createdb createexttable createrole createuser cross csv cube current current_catalog current_date current_role current_schema current_time current_timestamp current_user cursor cycle data database day deallocate dec decimal declare decode default defaults deferrable deferred definer delete delimiter delimiters deny desc dictionary disable discard distinct distributed do document domain double drop dxl each else enable encoding encrypted end enum errors escape every except exchange exclude excluding exclusive execute exists explain extension external extract false family fetch fields filespace fill filter first float following for force foreign format forward freeze from full function global grant granted greatest group group_id grouping handler hash having header hold host hour identity if ignore ilike immediate immutable implicit in including inclusive increment index indexes inherit inherits initially inline inner inout input insensitive insert instead int integer intersect interval into invoker is isnull isolation join key language large last leading least left level like limit list listen load local localtime localtimestamp location lock log login mapping master match maxvalue median merge minute minvalue missing mode modifies modify month move name names national natural nchar new newline next no nocreatedb nocreateexttable nocreaterole nocreateuser noinherit nologin none noovercommit nosuperuser not nothing notify notnull nowait null nullif nulls numeric object of off offset oids old on only operator option options or order ordered others out outer over overcommit overlaps overlay owned owner parser partial partition partitions passing password percent percentile_cont percentile_disc placing plans position preceding precision prepare prepared preserve primary prior privileges procedural procedure protocol queue quote randomly range read readable reads real reassign recheck recursive ref references reindex reject relative release rename repeatable replace replica reset resource restart restrict returning returns revoke right role rollback rollup rootpartition row rows rule savepoint scatter schema scroll search second security segment select sequence serializable session session_user set setof sets share show similar simple smallint some split sql stable standalone start statement statistics stdin stdout storage strict strip subpartition subpartitions substring superuser symmetric sysid system table tablespace temp template temporary text then threshold ties time timestamp to trailing transaction treat trigger trim true truncate trusted type unbounded uncommitted unencrypted union unique unknown unlisten until update user using vacuum valid validation validator value values varchar variadic varying verbose version view volatile web when where whitespace window with within without work writable write xml xmlattributes xmlconcat xmlelement xmlexists xmlforest xmlparse xmlpi xmlroot xmlserialize year yes zone\"),\n    builtin: set(\"bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml\"),\n    atoms: set(\"false true null unknown\"),\n    operatorChars: /^[*+\\-%<>!=&|^\\/#@?~]/,\n    dateSQL: set(\"date time timestamp\"),\n    support: set(\"decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast\")\n  });\n\n  // Spark SQL\n  CodeMirror.defineMIME(\"text/x-sparksql\", {\n    name: \"sql\",\n    keywords: set(\"add after all alter analyze and anti archive array as asc at between bucket buckets by cache cascade case cast change clear cluster clustered codegen collection column columns comment commit compact compactions compute concatenate cost create cross cube current current_date current_timestamp database databases data dbproperties defined delete delimited deny desc describe dfs directories distinct distribute drop else end escaped except exchange exists explain export extended external false fields fileformat first following for format formatted from full function functions global grant group grouping having if ignore import in index indexes inner inpath inputformat insert intersect interval into is items join keys last lateral lazy left like limit lines list load local location lock locks logical macro map minus msck natural no not null nulls of on optimize option options or order out outer outputformat over overwrite partition partitioned partitions percent preceding principals purge range recordreader recordwriter recover reduce refresh regexp rename repair replace reset restrict revoke right rlike role roles rollback rollup row rows schema schemas select semi separated serde serdeproperties set sets show skewed sort sorted start statistics stored stratify struct table tables tablesample tblproperties temp temporary terminated then to touch transaction transactions transform true truncate unarchive unbounded uncache union unlock unset use using values view when where window with\"),\n    builtin: set(\"abs acos acosh add_months aggregate and any approx_count_distinct approx_percentile array array_contains array_distinct array_except array_intersect array_join array_max array_min array_position array_remove array_repeat array_sort array_union arrays_overlap arrays_zip ascii asin asinh assert_true atan atan2 atanh avg base64 between bigint bin binary bit_and bit_count bit_get bit_length bit_or bit_xor bool_and bool_or boolean bround btrim cardinality case cast cbrt ceil ceiling char char_length character_length chr coalesce collect_list collect_set concat concat_ws conv corr cos cosh cot count count_if count_min_sketch covar_pop covar_samp crc32 cume_dist current_catalog current_database current_date current_timestamp current_timezone current_user date date_add date_format date_from_unix_date date_part date_sub date_trunc datediff day dayofmonth dayofweek dayofyear decimal decode degrees delimited dense_rank div double e element_at elt encode every exists exp explode explode_outer expm1 extract factorial filter find_in_set first first_value flatten float floor forall format_number format_string from_csv from_json from_unixtime from_utc_timestamp get_json_object getbit greatest grouping grouping_id hash hex hour hypot if ifnull in initcap inline inline_outer input_file_block_length input_file_block_start input_file_name inputformat instr int isnan isnotnull isnull java_method json_array_length json_object_keys json_tuple kurtosis lag last last_day last_value lcase lead least left length levenshtein like ln locate log log10 log1p log2 lower lpad ltrim make_date make_dt_interval make_interval make_timestamp make_ym_interval map map_concat map_entries map_filter map_from_arrays map_from_entries map_keys map_values map_zip_with max max_by md5 mean min min_by minute mod monotonically_increasing_id month months_between named_struct nanvl negative next_day not now nth_value ntile nullif nvl nvl2 octet_length or outputformat overlay parse_url percent_rank percentile percentile_approx pi pmod posexplode posexplode_outer position positive pow power printf quarter radians raise_error rand randn random rank rcfile reflect regexp regexp_extract regexp_extract_all regexp_like regexp_replace repeat replace reverse right rint rlike round row_number rpad rtrim schema_of_csv schema_of_json second sentences sequence sequencefile serde session_window sha sha1 sha2 shiftleft shiftright shiftrightunsigned shuffle sign signum sin sinh size skewness slice smallint some sort_array soundex space spark_partition_id split sqrt stack std stddev stddev_pop stddev_samp str_to_map string struct substr substring substring_index sum tan tanh textfile timestamp timestamp_micros timestamp_millis timestamp_seconds tinyint to_csv to_date to_json to_timestamp to_unix_timestamp to_utc_timestamp transform transform_keys transform_values translate trim trunc try_add try_divide typeof ucase unbase64 unhex uniontype unix_date unix_micros unix_millis unix_seconds unix_timestamp upper uuid var_pop var_samp variance version weekday weekofyear when width_bucket window xpath xpath_boolean xpath_double xpath_float xpath_int xpath_long xpath_number xpath_short xpath_string xxhash64 year zip_with\"),\n    atoms: set(\"false true null\"),\n    operatorChars: /^[*\\/+\\-%<>!=~&|^]/,\n    dateSQL: set(\"date time timestamp\"),\n    support: set(\"doubleQuote zerolessFloat\")\n  });\n\n  // Esper\n  CodeMirror.defineMIME(\"text/x-esper\", {\n    name: \"sql\",\n    client: set(\"source\"),\n    // http://www.espertech.com/esper/release-5.5.0/esper-reference/html/appendix_keywords.html\n    keywords: set(\"alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit after all and as at asc avedev avg between by case cast coalesce count create current_timestamp day days delete define desc distinct else end escape events every exists false first from full group having hour hours in inner insert instanceof into irstream is istream join last lastweekday left limit like max match_recognize matches median measures metadatasql min minute minutes msec millisecond milliseconds not null offset on or order outer output partition pattern prev prior regexp retain-union retain-intersection right rstream sec second seconds select set some snapshot sql stddev sum then true unidirectional until update variable weekday when where window\"),\n    builtin: {},\n    atoms: set(\"false true null\"),\n    operatorChars: /^[*+\\-%<>!=&|^\\/#@?~]/,\n    dateSQL: set(\"time\"),\n    support: set(\"decimallessFloat zerolessFloat binaryNumber hexNumber\")\n  });\n\n  // Trino (formerly known as Presto)\n  CodeMirror.defineMIME(\"text/x-trino\", {\n    name: \"sql\",\n    // https://github.com/trinodb/trino/blob/bc7a4eeedde28684c7ae6f74cefcaf7c6e782174/core/trino-parser/src/main/antlr4/io/trino/sql/parser/SqlBase.g4#L859-L1129\n    // https://github.com/trinodb/trino/blob/bc7a4eeedde28684c7ae6f74cefcaf7c6e782174/docs/src/main/sphinx/functions/list.rst\n    keywords: set(\"abs absent acos add admin after all all_match alter analyze and any any_match approx_distinct approx_most_frequent approx_percentile approx_set arbitrary array_agg array_distinct array_except array_intersect array_join array_max array_min array_position array_remove array_sort array_union arrays_overlap as asc asin at at_timezone atan atan2 authorization avg bar bernoulli beta_cdf between bing_tile bing_tile_at bing_tile_coordinates bing_tile_polygon bing_tile_quadkey bing_tile_zoom_level bing_tiles_around bit_count bitwise_and bitwise_and_agg bitwise_left_shift bitwise_not bitwise_or bitwise_or_agg bitwise_right_shift bitwise_right_shift_arithmetic bitwise_xor bool_and bool_or both by call cardinality cascade case cast catalogs cbrt ceil ceiling char2hexint checksum chr classify coalesce codepoint column columns combinations comment commit committed concat concat_ws conditional constraint contains contains_sequence convex_hull_agg copartition corr cos cosh cosine_similarity count count_if covar_pop covar_samp crc32 create cross cube cume_dist current current_catalog current_date current_groups current_path current_role current_schema current_time current_timestamp current_timezone current_user data date_add date_diff date_format date_parse date_trunc day day_of_month day_of_week day_of_year deallocate default define definer degrees delete dense_rank deny desc describe descriptor distinct distributed dow doy drop e element_at else empty empty_approx_set encoding end error escape evaluate_classifier_predictions every except excluding execute exists exp explain extract false features fetch filter final first first_value flatten floor following for format format_datetime format_number from from_base from_base32 from_base64 from_base64url from_big_endian_32 from_big_endian_64 from_encoded_polyline from_geojson_geometry from_hex from_ieee754_32 from_ieee754_64 from_iso8601_date from_iso8601_timestamp from_iso8601_timestamp_nanos from_unixtime from_unixtime_nanos from_utf8 full functions geometric_mean geometry_from_hadoop_shape geometry_invalid_reason geometry_nearest_points geometry_to_bing_tiles geometry_union geometry_union_agg grant granted grants graphviz great_circle_distance greatest group grouping groups hamming_distance hash_counts having histogram hmac_md5 hmac_sha1 hmac_sha256 hmac_sha512 hour human_readable_seconds if ignore in including index infinity initial inner input insert intersect intersection_cardinality into inverse_beta_cdf inverse_normal_cdf invoker io is is_finite is_infinite is_json_scalar is_nan isolation jaccard_index join json_array json_array_contains json_array_get json_array_length json_exists json_extract json_extract_scalar json_format json_object json_parse json_query json_size json_value keep key keys kurtosis lag last last_day_of_month last_value lateral lead leading learn_classifier learn_libsvm_classifier learn_libsvm_regressor learn_regressor least left length level levenshtein_distance like limit line_interpolate_point line_interpolate_points line_locate_point listagg ln local localtime localtimestamp log log10 log2 logical lower lpad ltrim luhn_check make_set_digest map_agg map_concat map_entries map_filter map_from_entries map_keys map_union map_values map_zip_with match match_recognize matched matches materialized max max_by md5 measures merge merge_set_digest millisecond min min_by minute mod month multimap_agg multimap_from_entries murmur3 nan natural next nfc nfd nfkc nfkd ngrams no none none_match normal_cdf normalize not now nth_value ntile null nullif nulls numeric_histogram object objectid_timestamp of offset omit on one only option or order ordinality outer output over overflow parse_data_size parse_datetime parse_duration partition partitions passing past path pattern per percent_rank permute pi position pow power preceding prepare privileges properties prune qdigest_agg quarter quotes radians rand random range rank read recursive reduce reduce_agg refresh regexp_count regexp_extract regexp_extract_all regexp_like regexp_position regexp_replace regexp_split regr_intercept regr_slope regress rename render repeat repeatable replace reset respect restrict returning reverse revoke rgb right role roles rollback rollup round row_number rows rpad rtrim running scalar schema schemas second security seek select sequence serializable session set sets sha1 sha256 sha512 show shuffle sign simplify_geometry sin skewness skip slice some soundex spatial_partitioning spatial_partitions split split_part split_to_map split_to_multimap spooky_hash_v2_32 spooky_hash_v2_64 sqrt st_area st_asbinary st_astext st_boundary st_buffer st_centroid st_contains st_convexhull st_coorddim st_crosses st_difference st_dimension st_disjoint st_distance st_endpoint st_envelope st_envelopeaspts st_equals st_exteriorring st_geometries st_geometryfromtext st_geometryn st_geometrytype st_geomfrombinary st_interiorringn st_interiorrings st_intersection st_intersects st_isclosed st_isempty st_isring st_issimple st_isvalid st_length st_linefromtext st_linestring st_multipoint st_numgeometries st_numinteriorring st_numpoints st_overlaps st_point st_pointn st_points st_polygon st_relate st_startpoint st_symdifference st_touches st_union st_within st_x st_xmax st_xmin st_y st_ymax st_ymin start starts_with stats stddev stddev_pop stddev_samp string strpos subset substr substring sum system table tables tablesample tan tanh tdigest_agg text then ties timestamp_objectid timezone_hour timezone_minute to to_base to_base32 to_base64 to_base64url to_big_endian_32 to_big_endian_64 to_char to_date to_encoded_polyline to_geojson_geometry to_geometry to_hex to_ieee754_32 to_ieee754_64 to_iso8601 to_milliseconds to_spherical_geography to_timestamp to_unixtime to_utf8 trailing transaction transform transform_keys transform_values translate trim trim_array true truncate try try_cast type typeof uescape unbounded uncommitted unconditional union unique unknown unmatched unnest update upper url_decode url_encode url_extract_fragment url_extract_host url_extract_parameter url_extract_path url_extract_port url_extract_protocol url_extract_query use user using utf16 utf32 utf8 validate value value_at_quantile values values_at_quantiles var_pop var_samp variance verbose version view week week_of_year when where width_bucket wilson_interval_lower wilson_interval_upper window with with_timezone within without word_stem work wrapper write xxhash64 year year_of_week yow zip zip_with\"),\n    // https://github.com/trinodb/trino/blob/bc7a4eeedde28684c7ae6f74cefcaf7c6e782174/core/trino-main/src/main/java/io/trino/metadata/TypeRegistry.java#L131-L168\n    // https://github.com/trinodb/trino/blob/bc7a4eeedde28684c7ae6f74cefcaf7c6e782174/plugin/trino-ml/src/main/java/io/trino/plugin/ml/MLPlugin.java#L35\n    // https://github.com/trinodb/trino/blob/bc7a4eeedde28684c7ae6f74cefcaf7c6e782174/plugin/trino-mongodb/src/main/java/io/trino/plugin/mongodb/MongoPlugin.java#L32\n    // https://github.com/trinodb/trino/blob/bc7a4eeedde28684c7ae6f74cefcaf7c6e782174/plugin/trino-geospatial/src/main/java/io/trino/plugin/geospatial/GeoPlugin.java#L37\n    builtin: set(\"array bigint bingtile boolean char codepoints color date decimal double function geometry hyperloglog int integer interval ipaddress joniregexp json json2016 jsonpath kdbtree likepattern map model objectid p4hyperloglog precision qdigest re2jregexp real regressor row setdigest smallint sphericalgeography tdigest time timestamp tinyint uuid varbinary varchar zone\"),\n    atoms: set(\"false true null unknown\"),\n    // https://trino.io/docs/current/functions/list.html#id1\n    operatorChars: /^[[\\]|<>=!\\-+*/%]/,\n    dateSQL: set(\"date time timestamp zone\"),\n    // hexNumber is necessary for VARBINARY literals, e.g. X'65683F'\n    // but it also enables 0xFF hex numbers, which Trino doesn't support.\n    support: set(\"decimallessFloat zerolessFloat hexNumber\")\n  });\n});\n\n/*\n  How Properties of Mime Types are used by SQL Mode\n  =================================================\n\n  keywords:\n    A list of keywords you want to be highlighted.\n  builtin:\n    A list of builtin types you want to be highlighted (if you want types to be of class \"builtin\" instead of \"keyword\").\n  operatorChars:\n    All characters that must be handled as operators.\n  client:\n    Commands parsed and executed by the client (not the server).\n  support:\n    A list of supported syntaxes which are not common, but are supported by more than 1 DBMS.\n    * zerolessFloat: .1\n    * decimallessFloat: 1.\n    * hexNumber: X'01AF' X'01af' x'01AF' x'01af' 0x01AF 0x01af\n    * binaryNumber: b'01' B'01' 0b01\n    * doubleQuote: \"string\"\n    * escapeConstant: E''\n    * nCharCast: N'string'\n    * charsetCast: _utf8'string'\n    * commentHash: use # char for comments\n    * commentSlashSlash: use // for comments\n    * commentSpaceRequired: require a space after -- for comments\n  atoms:\n    Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others:\n    UNKNOWN, INFINITY, UNDERFLOW, NaN...\n  dateSQL:\n    Used for date/time SQL standard syntax, because not all DBMS's support same temporal types.\n*/\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/stex/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: sTeX mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"stex.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">sTeX</a>\n  </ul>\n</div>\n\n<article>\n<h2>sTeX mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n\\begin{module}[id=bbt-size]\n\\importmodule[balanced-binary-trees]{balanced-binary-trees}\n\\importmodule[\\KWARCslides{dmath/en/cardinality}]{cardinality}\n\n\\begin{frame}\n  \\frametitle{Size Lemma for Balanced Trees}\n  \\begin{itemize}\n  \\item\n    \\begin{assertion}[id=size-lemma,type=lemma] \n    Let $G=\\tup{V,E}$ be a \\termref[cd=binary-trees]{balanced binary tree} \n    of \\termref[cd=graph-depth,name=vertex-depth]{depth}$n>i$, then the set\n     $\\defeq{\\livar{V}i}{\\setst{\\inset{v}{V}}{\\gdepth{v} = i}}$ of\n    \\termref[cd=graphs-intro,name=node]{nodes} at \n    \\termref[cd=graph-depth,name=vertex-depth]{depth} $i$ has\n    \\termref[cd=cardinality,name=cardinality]{cardinality} $\\power2i$.\n   \\end{assertion}\n  \\item\n    \\begin{sproof}[id=size-lemma-pf,proofend=,for=size-lemma]{via induction over the depth $i$.}\n      \\begin{spfcases}{We have to consider two cases}\n        \\begin{spfcase}{$i=0$}\n          \\begin{spfstep}[display=flow]\n            then $\\livar{V}i=\\set{\\livar{v}r}$, where $\\livar{v}r$ is the root, so\n            $\\eq{\\card{\\livar{V}0},\\card{\\set{\\livar{v}r}},1,\\power20}$.\n          \\end{spfstep}\n        \\end{spfcase}\n        \\begin{spfcase}{$i>0$}\n          \\begin{spfstep}[display=flow]\n           then $\\livar{V}{i-1}$ contains $\\power2{i-1}$ vertexes \n           \\begin{justification}[method=byIH](IH)\\end{justification}\n          \\end{spfstep}\n          \\begin{spfstep}\n           By the \\begin{justification}[method=byDef]definition of a binary\n              tree\\end{justification}, each $\\inset{v}{\\livar{V}{i-1}}$ is a leaf or has\n            two children that are at depth $i$.\n          \\end{spfstep}\n          \\begin{spfstep}\n           As $G$ is \\termref[cd=balanced-binary-trees,name=balanced-binary-tree]{balanced} and $\\gdepth{G}=n>i$, $\\livar{V}{i-1}$ cannot contain\n            leaves.\n          \\end{spfstep}\n          \\begin{spfstep}[type=conclusion]\n           Thus $\\eq{\\card{\\livar{V}i},{\\atimes[cdot]{2,\\card{\\livar{V}{i-1}}}},{\\atimes[cdot]{2,\\power2{i-1}}},\\power2i}$.\n          \\end{spfstep}\n        \\end{spfcase}\n      \\end{spfcases}\n    \\end{sproof}\n  \\item \n    \\begin{assertion}[id=fbbt,type=corollary]\t\n      A fully balanced tree of depth $d$ has $\\power2{d+1}-1$ nodes.\n    \\end{assertion}\n  \\item\n      \\begin{sproof}[for=fbbt,id=fbbt-pf]{}\n        \\begin{spfstep}\n          Let $\\defeq{G}{\\tup{V,E}}$ be a fully balanced tree\n        \\end{spfstep}\n        \\begin{spfstep}\n          Then $\\card{V}=\\Sumfromto{i}1d{\\power2i}= \\power2{d+1}-1$.\n        \\end{spfstep}\n      \\end{sproof}\n    \\end{itemize}\n  \\end{frame}\n\\begin{note}\n  \\begin{omtext}[type=conclusion,for=binary-tree]\n    This shows that balanced binary trees grow in breadth very quickly, a consequence of\n    this is that they are very shallow (and this compute very fast), which is the essence of\n    the next result.\n  \\end{omtext}\n\\end{note}\n\\end{module}\n\n%%% Local Variables: \n%%% mode: LaTeX\n%%% TeX-master: \"all\"\n%%% End: \\end{document}\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p>sTeX mode supports this option:</p>\n    <d1>\n      <dt><code>inMathMode: boolean</code></dt>\n      <dd>Whether to start parsing in math mode (default: <code>false</code>).</dd>\n    </d1>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-stex</code>.</p>\n\n    <p><strong>Parsing/Highlighting Tests:</strong> <a href=\"../../test/index.html#stex_*\">normal</a>,  <a href=\"../../test/index.html#verbose,stex_*\">verbose</a>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/stex/stex.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/*\n * Author: Constantin Jucovschi (c.jucovschi@jacobs-university.de)\n * Licence: MIT\n */\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"stex\", function(_config, parserConfig) {\n    \"use strict\";\n\n    function pushCommand(state, command) {\n      state.cmdState.push(command);\n    }\n\n    function peekCommand(state) {\n      if (state.cmdState.length > 0) {\n        return state.cmdState[state.cmdState.length - 1];\n      } else {\n        return null;\n      }\n    }\n\n    function popCommand(state) {\n      var plug = state.cmdState.pop();\n      if (plug) {\n        plug.closeBracket();\n      }\n    }\n\n    // returns the non-default plugin closest to the end of the list\n    function getMostPowerful(state) {\n      var context = state.cmdState;\n      for (var i = context.length - 1; i >= 0; i--) {\n        var plug = context[i];\n        if (plug.name == \"DEFAULT\") {\n          continue;\n        }\n        return plug;\n      }\n      return { styleIdentifier: function() { return null; } };\n    }\n\n    function addPluginPattern(pluginName, cmdStyle, styles) {\n      return function () {\n        this.name = pluginName;\n        this.bracketNo = 0;\n        this.style = cmdStyle;\n        this.styles = styles;\n        this.argument = null;   // \\begin and \\end have arguments that follow. These are stored in the plugin\n\n        this.styleIdentifier = function() {\n          return this.styles[this.bracketNo - 1] || null;\n        };\n        this.openBracket = function() {\n          this.bracketNo++;\n          return \"bracket\";\n        };\n        this.closeBracket = function() {};\n      };\n    }\n\n    var plugins = {};\n\n    plugins[\"importmodule\"] = addPluginPattern(\"importmodule\", \"tag\", [\"string\", \"builtin\"]);\n    plugins[\"documentclass\"] = addPluginPattern(\"documentclass\", \"tag\", [\"\", \"atom\"]);\n    plugins[\"usepackage\"] = addPluginPattern(\"usepackage\", \"tag\", [\"atom\"]);\n    plugins[\"begin\"] = addPluginPattern(\"begin\", \"tag\", [\"atom\"]);\n    plugins[\"end\"] = addPluginPattern(\"end\", \"tag\", [\"atom\"]);\n\n    plugins[\"label\"    ] = addPluginPattern(\"label\"    , \"tag\", [\"atom\"]);\n    plugins[\"ref\"      ] = addPluginPattern(\"ref\"      , \"tag\", [\"atom\"]);\n    plugins[\"eqref\"    ] = addPluginPattern(\"eqref\"    , \"tag\", [\"atom\"]);\n    plugins[\"cite\"     ] = addPluginPattern(\"cite\"     , \"tag\", [\"atom\"]);\n    plugins[\"bibitem\"  ] = addPluginPattern(\"bibitem\"  , \"tag\", [\"atom\"]);\n    plugins[\"Bibitem\"  ] = addPluginPattern(\"Bibitem\"  , \"tag\", [\"atom\"]);\n    plugins[\"RBibitem\" ] = addPluginPattern(\"RBibitem\" , \"tag\", [\"atom\"]);\n\n    plugins[\"DEFAULT\"] = function () {\n      this.name = \"DEFAULT\";\n      this.style = \"tag\";\n\n      this.styleIdentifier = this.openBracket = this.closeBracket = function() {};\n    };\n\n    function setState(state, f) {\n      state.f = f;\n    }\n\n    // called when in a normal (no environment) context\n    function normal(source, state) {\n      var plug;\n      // Do we look like '\\command' ?  If so, attempt to apply the plugin 'command'\n      if (source.match(/^\\\\[a-zA-Z@]+/)) {\n        var cmdName = source.current().slice(1);\n        plug = plugins.hasOwnProperty(cmdName) ? plugins[cmdName] : plugins[\"DEFAULT\"];\n        plug = new plug();\n        pushCommand(state, plug);\n        setState(state, beginParams);\n        return plug.style;\n      }\n\n      // escape characters\n      if (source.match(/^\\\\[$&%#{}_]/)) {\n        return \"tag\";\n      }\n\n      // white space control characters\n      if (source.match(/^\\\\[,;!\\/\\\\]/)) {\n        return \"tag\";\n      }\n\n      // find if we're starting various math modes\n      if (source.match(\"\\\\[\")) {\n        setState(state, function(source, state){ return inMathMode(source, state, \"\\\\]\"); });\n        return \"keyword\";\n      }\n      if (source.match(\"\\\\(\")) {\n        setState(state, function(source, state){ return inMathMode(source, state, \"\\\\)\"); });\n        return \"keyword\";\n      }\n      if (source.match(\"$$\")) {\n        setState(state, function(source, state){ return inMathMode(source, state, \"$$\"); });\n        return \"keyword\";\n      }\n      if (source.match(\"$\")) {\n        setState(state, function(source, state){ return inMathMode(source, state, \"$\"); });\n        return \"keyword\";\n      }\n\n      var ch = source.next();\n      if (ch == \"%\") {\n        source.skipToEnd();\n        return \"comment\";\n      } else if (ch == '}' || ch == ']') {\n        plug = peekCommand(state);\n        if (plug) {\n          plug.closeBracket(ch);\n          setState(state, beginParams);\n        } else {\n          return \"error\";\n        }\n        return \"bracket\";\n      } else if (ch == '{' || ch == '[') {\n        plug = plugins[\"DEFAULT\"];\n        plug = new plug();\n        pushCommand(state, plug);\n        return \"bracket\";\n      } else if (/\\d/.test(ch)) {\n        source.eatWhile(/[\\w.%]/);\n        return \"atom\";\n      } else {\n        source.eatWhile(/[\\w\\-_]/);\n        plug = getMostPowerful(state);\n        if (plug.name == 'begin') {\n          plug.argument = source.current();\n        }\n        return plug.styleIdentifier();\n      }\n    }\n\n    function inMathMode(source, state, endModeSeq) {\n      if (source.eatSpace()) {\n        return null;\n      }\n      if (endModeSeq && source.match(endModeSeq)) {\n        setState(state, normal);\n        return \"keyword\";\n      }\n      if (source.match(/^\\\\[a-zA-Z@]+/)) {\n        return \"tag\";\n      }\n      if (source.match(/^[a-zA-Z]+/)) {\n        return \"variable-2\";\n      }\n      // escape characters\n      if (source.match(/^\\\\[$&%#{}_]/)) {\n        return \"tag\";\n      }\n      // white space control characters\n      if (source.match(/^\\\\[,;!\\/]/)) {\n        return \"tag\";\n      }\n      // special math-mode characters\n      if (source.match(/^[\\^_&]/)) {\n        return \"tag\";\n      }\n      // non-special characters\n      if (source.match(/^[+\\-<>|=,\\/@!*:;'\"`~#?]/)) {\n        return null;\n      }\n      if (source.match(/^(\\d+\\.\\d*|\\d*\\.\\d+|\\d+)/)) {\n        return \"number\";\n      }\n      var ch = source.next();\n      if (ch == \"{\" || ch == \"}\" || ch == \"[\" || ch == \"]\" || ch == \"(\" || ch == \")\") {\n        return \"bracket\";\n      }\n\n      if (ch == \"%\") {\n        source.skipToEnd();\n        return \"comment\";\n      }\n      return \"error\";\n    }\n\n    function beginParams(source, state) {\n      var ch = source.peek(), lastPlug;\n      if (ch == '{' || ch == '[') {\n        lastPlug = peekCommand(state);\n        lastPlug.openBracket(ch);\n        source.eat(ch);\n        setState(state, normal);\n        return \"bracket\";\n      }\n      if (/[ \\t\\r]/.test(ch)) {\n        source.eat(ch);\n        return null;\n      }\n      setState(state, normal);\n      popCommand(state);\n\n      return normal(source, state);\n    }\n\n    return {\n      startState: function() {\n        var f = parserConfig.inMathMode ? function(source, state){ return inMathMode(source, state); } : normal;\n        return {\n          cmdState: [],\n          f: f\n        };\n      },\n      copyState: function(s) {\n        return {\n          cmdState: s.cmdState.slice(),\n          f: s.f\n        };\n      },\n      token: function(stream, state) {\n        return state.f(stream, state);\n      },\n      blankLine: function(state) {\n        state.f = normal;\n        state.cmdState.length = 0;\n      },\n      lineComment: \"%\"\n    };\n  });\n\n  CodeMirror.defineMIME(\"text/x-stex\", \"stex\");\n  CodeMirror.defineMIME(\"text/x-latex\", \"stex\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/stex/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({tabSize: 4}, \"stex\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"word\",\n     \"foo\");\n\n  MT(\"twoWords\",\n     \"foo bar\");\n\n  MT(\"beginEndDocument\",\n     \"[tag \\\\begin][bracket {][atom document][bracket }]\",\n     \"[tag \\\\end][bracket {][atom document][bracket }]\");\n\n  MT(\"beginEndEquation\",\n     \"[tag \\\\begin][bracket {][atom equation][bracket }]\",\n     \"  E=mc^2\",\n     \"[tag \\\\end][bracket {][atom equation][bracket }]\");\n\n  MT(\"beginModule\",\n     \"[tag \\\\begin][bracket {][atom module][bracket }[[]]]\");\n\n  MT(\"beginModuleId\",\n     \"[tag \\\\begin][bracket {][atom module][bracket }[[]id=bbt-size[bracket ]]]\");\n\n  MT(\"importModule\",\n     \"[tag \\\\importmodule][bracket [[][string b-b-t][bracket ]]{][builtin b-b-t][bracket }]\");\n\n  MT(\"importModulePath\",\n     \"[tag \\\\importmodule][bracket [[][tag \\\\KWARCslides][bracket {][string dmath/en/cardinality][bracket }]]{][builtin card][bracket }]\");\n\n  MT(\"psForPDF\",\n     \"[tag \\\\PSforPDF][bracket [[][atom 1][bracket ]]{]#1[bracket }]\");\n\n  MT(\"comment\",\n     \"[comment % foo]\");\n\n  MT(\"tagComment\",\n     \"[tag \\\\item][comment % bar]\");\n\n  MT(\"commentTag\",\n     \" [comment % \\\\item]\");\n\n  MT(\"commentLineBreak\",\n     \"[comment %]\",\n     \"foo\");\n\n  MT(\"tagErrorCurly\",\n     \"[tag \\\\begin][error }][bracket {]\");\n\n  MT(\"tagErrorSquare\",\n     \"[tag \\\\item][error ]]][bracket {]\");\n\n  MT(\"commentCurly\",\n     \"[comment % }]\");\n\n  MT(\"tagHash\",\n     \"the [tag \\\\#] key\");\n\n  MT(\"tagNumber\",\n     \"a [tag \\\\$][atom 5] stetson\");\n\n  MT(\"tagPercent\",\n     \"[atom 100][tag \\\\%] beef\");\n\n  MT(\"tagAmpersand\",\n     \"L [tag \\\\&] N\");\n\n  MT(\"tagUnderscore\",\n     \"foo[tag \\\\_]bar\");\n\n  MT(\"tagBracketOpen\",\n     \"[tag \\\\emph][bracket {][tag \\\\{][bracket }]\");\n\n  MT(\"tagBracketClose\",\n     \"[tag \\\\emph][bracket {][tag \\\\}][bracket }]\");\n\n  MT(\"tagLetterNumber\",\n     \"section [tag \\\\S][atom 1]\");\n\n  MT(\"textTagNumber\",\n     \"para [tag \\\\P][atom 2]\");\n\n  MT(\"thinspace\",\n     \"x[tag \\\\,]y\");\n\n  MT(\"thickspace\",\n     \"x[tag \\\\;]y\");\n\n  MT(\"negativeThinspace\",\n     \"x[tag \\\\!]y\");\n\n  MT(\"periodNotSentence\",\n     \"J.\\\\ L.\\\\ is\");\n\n  MT(\"periodSentence\",\n     \"X[tag \\\\@]. The\");\n\n  MT(\"italicCorrection\",\n     \"[bracket {][tag \\\\em] If[tag \\\\/][bracket }] I\");\n\n  MT(\"tagBracket\",\n     \"[tag \\\\newcommand][bracket {][tag \\\\pop][bracket }]\");\n\n  MT(\"inlineMathTagFollowedByNumber\",\n     \"[keyword $][tag \\\\pi][number 2][keyword $]\");\n\n  MT(\"inlineMath\",\n     \"[keyword $][number 3][variable-2 x][tag ^][number 2.45]-[tag \\\\sqrt][bracket {][tag \\\\$\\\\alpha][bracket }] = [number 2][keyword $] other text\");\n\n  MT(\"inlineMathLatexStyle\",\n     \"[keyword \\\\(][number 3][variable-2 x][tag ^][number 2.45]-[tag \\\\sqrt][bracket {][tag \\\\$\\\\alpha][bracket }] = [number 2][keyword \\\\)] other text\");\n\n  MT(\"displayMath\",\n     \"More [keyword $$]\\t[variable-2 S][tag ^][variable-2 n][tag \\\\sum] [variable-2 i][keyword $$] other text\");\n\n  MT(\"displayMath environment\",\n     \"[tag \\\\begin][bracket {][atom equation][bracket }] x [tag \\\\end][bracket {][atom equation][bracket }] other text\");\n\n  MT(\"displayMath environment with label\",\n     \"[tag \\\\begin][bracket {][atom equation][bracket }][tag \\\\label][bracket {][atom eq1][bracket }] x [tag \\\\end][bracket {][atom equation][bracket }] other text~[tag \\\\ref][bracket {][atom eq1][bracket }]\");\n\n  MT(\"mathWithComment\",\n     \"[keyword $][variable-2 x] [comment % $]\",\n     \"[variable-2 y][keyword $] other text\");\n\n  MT(\"lineBreakArgument\",\n    \"[tag \\\\\\\\][bracket [[][atom 1cm][bracket ]]]\");\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/stylus/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Stylus mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../addon/hint/show-hint.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"stylus.js\"></script>\n<script src=\"../../addon/hint/show-hint.js\"></script>\n<script src=\"../../addon/hint/css-hint.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;} form{margin-bottom: .7em;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Stylus</a>\n  </ul>\n</div>\n\n<article>\n<h2>Stylus mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n/* Stylus mode */\n\n#id,\n.class,\narticle\n  font-family Arial, sans-serif\n\n#id,\n.class,\narticle {\n  font-family: Arial, sans-serif;\n}\n\n// Variables\nfont-size-base = 16px\nline-height-base = 1.5\nfont-family-base = \"Helvetica Neue\", Helvetica, Arial, sans-serif\ntext-color = lighten(#000, 20%)\n\nbody\n  font font-size-base/line-height-base font-family-base\n  color text-color\n\nbody {\n  font: 400 16px/1.5 \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  color: #333;\n}\n\n// Variables\nlink-color = darken(#428bca, 6.5%)\nlink-hover-color = darken(link-color, 15%)\nlink-decoration = none\nlink-hover-decoration = false\n\n// Mixin\ntab-focus()\n  outline thin dotted\n  outline 5px auto -webkit-focus-ring-color\n  outline-offset -2px\n\na\n  color link-color\n  if link-decoration\n    text-decoration link-decoration\n  &:hover\n  &:focus\n    color link-hover-color\n    if link-hover-decoration\n      text-decoration link-hover-decoration\n  &:focus\n    tab-focus()\n\na {\n  color: #3782c4;\n  text-decoration: none;\n}\na:hover,\na:focus {\n  color: #2f6ea7;\n}\na:focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n</textarea>\n</form>\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    extraKeys: {\"Ctrl-Space\": \"autocomplete\"},\n    tabSize: 2\n  });\n</script>\n\n<p><strong>MIME types defined:</strong> <code>text/x-styl</code>.</p>\n<p>Created by <a href=\"https://github.com/dmitrykiselyov\">Dmitry Kiselyov</a></p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/stylus/stylus.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Stylus mode created by Dmitry Kiselyov http://git.io/AaRB\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"stylus\", function(config) {\n    var indentUnit = config.indentUnit,\n        indentUnitString = '',\n        tagKeywords = keySet(tagKeywords_),\n        tagVariablesRegexp = /^(a|b|i|s|col|em)$/i,\n        propertyKeywords = keySet(propertyKeywords_),\n        nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_),\n        valueKeywords = keySet(valueKeywords_),\n        colorKeywords = keySet(colorKeywords_),\n        documentTypes = keySet(documentTypes_),\n        documentTypesRegexp = wordRegexp(documentTypes_),\n        mediaFeatures = keySet(mediaFeatures_),\n        mediaTypes = keySet(mediaTypes_),\n        fontProperties = keySet(fontProperties_),\n        operatorsRegexp = /^\\s*([.]{2,3}|&&|\\|\\||\\*\\*|[?!=:]?=|[-+*\\/%<>]=?|\\?:|\\~)/,\n        wordOperatorKeywordsRegexp = wordRegexp(wordOperatorKeywords_),\n        blockKeywords = keySet(blockKeywords_),\n        vendorPrefixesRegexp = new RegExp(/^\\-(moz|ms|o|webkit)-/i),\n        commonAtoms = keySet(commonAtoms_),\n        firstWordMatch = \"\",\n        states = {},\n        ch,\n        style,\n        type,\n        override;\n\n    while (indentUnitString.length < indentUnit) indentUnitString += ' ';\n\n    /**\n     * Tokenizers\n     */\n    function tokenBase(stream, state) {\n      firstWordMatch = stream.string.match(/(^[\\w-]+\\s*=\\s*$)|(^\\s*[\\w-]+\\s*=\\s*[\\w-])|(^\\s*(\\.|#|@|\\$|\\&|\\[|\\d|\\+|::?|\\{|\\>|~|\\/)?\\s*[\\w-]*([a-z0-9-]|\\*|\\/\\*)(\\(|,)?)/);\n      state.context.line.firstWord = firstWordMatch ? firstWordMatch[0].replace(/^\\s*/, \"\") : \"\";\n      state.context.line.indent = stream.indentation();\n      ch = stream.peek();\n\n      // Line comment\n      if (stream.match(\"//\")) {\n        stream.skipToEnd();\n        return [\"comment\", \"comment\"];\n      }\n      // Block comment\n      if (stream.match(\"/*\")) {\n        state.tokenize = tokenCComment;\n        return tokenCComment(stream, state);\n      }\n      // String\n      if (ch == \"\\\"\" || ch == \"'\") {\n        stream.next();\n        state.tokenize = tokenString(ch);\n        return state.tokenize(stream, state);\n      }\n      // Def\n      if (ch == \"@\") {\n        stream.next();\n        stream.eatWhile(/[\\w\\\\-]/);\n        return [\"def\", stream.current()];\n      }\n      // ID selector or Hex color\n      if (ch == \"#\") {\n        stream.next();\n        // Hex color\n        if (stream.match(/^[0-9a-f]{3}([0-9a-f]([0-9a-f]{2}){0,2})?\\b(?!-)/i)) {\n          return [\"atom\", \"atom\"];\n        }\n        // ID selector\n        if (stream.match(/^[a-z][\\w-]*/i)) {\n          return [\"builtin\", \"hash\"];\n        }\n      }\n      // Vendor prefixes\n      if (stream.match(vendorPrefixesRegexp)) {\n        return [\"meta\", \"vendor-prefixes\"];\n      }\n      // Numbers\n      if (stream.match(/^-?[0-9]?\\.?[0-9]/)) {\n        stream.eatWhile(/[a-z%]/i);\n        return [\"number\", \"unit\"];\n      }\n      // !important|optional\n      if (ch == \"!\") {\n        stream.next();\n        return [stream.match(/^(important|optional)/i) ? \"keyword\": \"operator\", \"important\"];\n      }\n      // Class\n      if (ch == \".\" && stream.match(/^\\.[a-z][\\w-]*/i)) {\n        return [\"qualifier\", \"qualifier\"];\n      }\n      // url url-prefix domain regexp\n      if (stream.match(documentTypesRegexp)) {\n        if (stream.peek() == \"(\") state.tokenize = tokenParenthesized;\n        return [\"property\", \"word\"];\n      }\n      // Mixins / Functions\n      if (stream.match(/^[a-z][\\w-]*\\(/i)) {\n        stream.backUp(1);\n        return [\"keyword\", \"mixin\"];\n      }\n      // Block mixins\n      if (stream.match(/^(\\+|-)[a-z][\\w-]*\\(/i)) {\n        stream.backUp(1);\n        return [\"keyword\", \"block-mixin\"];\n      }\n      // Parent Reference BEM naming\n      if (stream.string.match(/^\\s*&/) && stream.match(/^[-_]+[a-z][\\w-]*/)) {\n        return [\"qualifier\", \"qualifier\"];\n      }\n      // / Root Reference & Parent Reference\n      if (stream.match(/^(\\/|&)(-|_|:|\\.|#|[a-z])/)) {\n        stream.backUp(1);\n        return [\"variable-3\", \"reference\"];\n      }\n      if (stream.match(/^&{1}\\s*$/)) {\n        return [\"variable-3\", \"reference\"];\n      }\n      // Word operator\n      if (stream.match(wordOperatorKeywordsRegexp)) {\n        return [\"operator\", \"operator\"];\n      }\n      // Word\n      if (stream.match(/^\\$?[-_]*[a-z0-9]+[\\w-]*/i)) {\n        // Variable\n        if (stream.match(/^(\\.|\\[)[\\w-\\'\\\"\\]]+/i, false)) {\n          if (!wordIsTag(stream.current())) {\n            stream.match('.');\n            return [\"variable-2\", \"variable-name\"];\n          }\n        }\n        return [\"variable-2\", \"word\"];\n      }\n      // Operators\n      if (stream.match(operatorsRegexp)) {\n        return [\"operator\", stream.current()];\n      }\n      // Delimiters\n      if (/[:;,{}\\[\\]\\(\\)]/.test(ch)) {\n        stream.next();\n        return [null, ch];\n      }\n      // Non-detected items\n      stream.next();\n      return [null, null];\n    }\n\n    /**\n     * Token comment\n     */\n    function tokenCComment(stream, state) {\n      var maybeEnd = false, ch;\n      while ((ch = stream.next()) != null) {\n        if (maybeEnd && ch == \"/\") {\n          state.tokenize = null;\n          break;\n        }\n        maybeEnd = (ch == \"*\");\n      }\n      return [\"comment\", \"comment\"];\n    }\n\n    /**\n     * Token string\n     */\n    function tokenString(quote) {\n      return function(stream, state) {\n        var escaped = false, ch;\n        while ((ch = stream.next()) != null) {\n          if (ch == quote && !escaped) {\n            if (quote == \")\") stream.backUp(1);\n            break;\n          }\n          escaped = !escaped && ch == \"\\\\\";\n        }\n        if (ch == quote || !escaped && quote != \")\") state.tokenize = null;\n        return [\"string\", \"string\"];\n      };\n    }\n\n    /**\n     * Token parenthesized\n     */\n    function tokenParenthesized(stream, state) {\n      stream.next(); // Must be \"(\"\n      if (!stream.match(/\\s*[\\\"\\')]/, false))\n        state.tokenize = tokenString(\")\");\n      else\n        state.tokenize = null;\n      return [null, \"(\"];\n    }\n\n    /**\n     * Context management\n     */\n    function Context(type, indent, prev, line) {\n      this.type = type;\n      this.indent = indent;\n      this.prev = prev;\n      this.line = line || {firstWord: \"\", indent: 0};\n    }\n\n    function pushContext(state, stream, type, indent) {\n      indent = indent >= 0 ? indent : indentUnit;\n      state.context = new Context(type, stream.indentation() + indent, state.context);\n      return type;\n    }\n\n    function popContext(state, currentIndent) {\n      var contextIndent = state.context.indent - indentUnit;\n      currentIndent = currentIndent || false;\n      state.context = state.context.prev;\n      if (currentIndent) state.context.indent = contextIndent;\n      return state.context.type;\n    }\n\n    function pass(type, stream, state) {\n      return states[state.context.type](type, stream, state);\n    }\n\n    function popAndPass(type, stream, state, n) {\n      for (var i = n || 1; i > 0; i--)\n        state.context = state.context.prev;\n      return pass(type, stream, state);\n    }\n\n\n    /**\n     * Parser\n     */\n    function wordIsTag(word) {\n      return word.toLowerCase() in tagKeywords;\n    }\n\n    function wordIsProperty(word) {\n      word = word.toLowerCase();\n      return word in propertyKeywords || word in fontProperties;\n    }\n\n    function wordIsBlock(word) {\n      return word.toLowerCase() in blockKeywords;\n    }\n\n    function wordIsVendorPrefix(word) {\n      return word.toLowerCase().match(vendorPrefixesRegexp);\n    }\n\n    function wordAsValue(word) {\n      var wordLC = word.toLowerCase();\n      var override = \"variable-2\";\n      if (wordIsTag(word)) override = \"tag\";\n      else if (wordIsBlock(word)) override = \"block-keyword\";\n      else if (wordIsProperty(word)) override = \"property\";\n      else if (wordLC in valueKeywords || wordLC in commonAtoms) override = \"atom\";\n      else if (wordLC == \"return\" || wordLC in colorKeywords) override = \"keyword\";\n\n      // Font family\n      else if (word.match(/^[A-Z]/)) override = \"string\";\n      return override;\n    }\n\n    function typeIsBlock(type, stream) {\n      return ((endOfLine(stream) && (type == \"{\" || type == \"]\" || type == \"hash\" || type == \"qualifier\")) || type == \"block-mixin\");\n    }\n\n    function typeIsInterpolation(type, stream) {\n      return type == \"{\" && stream.match(/^\\s*\\$?[\\w-]+/i, false);\n    }\n\n    function typeIsPseudo(type, stream) {\n      return type == \":\" && stream.match(/^[a-z-]+/, false);\n    }\n\n    function startOfLine(stream) {\n      return stream.sol() || stream.string.match(new RegExp(\"^\\\\s*\" + escapeRegExp(stream.current())));\n    }\n\n    function endOfLine(stream) {\n      return stream.eol() || stream.match(/^\\s*$/, false);\n    }\n\n    function firstWordOfLine(line) {\n      var re = /^\\s*[-_]*[a-z0-9]+[\\w-]*/i;\n      var result = typeof line == \"string\" ? line.match(re) : line.string.match(re);\n      return result ? result[0].replace(/^\\s*/, \"\") : \"\";\n    }\n\n\n    /**\n     * Block\n     */\n    states.block = function(type, stream, state) {\n      if ((type == \"comment\" && startOfLine(stream)) ||\n          (type == \",\" && endOfLine(stream)) ||\n          type == \"mixin\") {\n        return pushContext(state, stream, \"block\", 0);\n      }\n      if (typeIsInterpolation(type, stream)) {\n        return pushContext(state, stream, \"interpolation\");\n      }\n      if (endOfLine(stream) && type == \"]\") {\n        if (!/^\\s*(\\.|#|:|\\[|\\*|&)/.test(stream.string) && !wordIsTag(firstWordOfLine(stream))) {\n          return pushContext(state, stream, \"block\", 0);\n        }\n      }\n      if (typeIsBlock(type, stream)) {\n        return pushContext(state, stream, \"block\");\n      }\n      if (type == \"}\" && endOfLine(stream)) {\n        return pushContext(state, stream, \"block\", 0);\n      }\n      if (type == \"variable-name\") {\n        if (stream.string.match(/^\\s?\\$[\\w-\\.\\[\\]\\'\\\"]+$/) || wordIsBlock(firstWordOfLine(stream))) {\n          return pushContext(state, stream, \"variableName\");\n        }\n        else {\n          return pushContext(state, stream, \"variableName\", 0);\n        }\n      }\n      if (type == \"=\") {\n        if (!endOfLine(stream) && !wordIsBlock(firstWordOfLine(stream))) {\n          return pushContext(state, stream, \"block\", 0);\n        }\n        return pushContext(state, stream, \"block\");\n      }\n      if (type == \"*\") {\n        if (endOfLine(stream) || stream.match(/\\s*(,|\\.|#|\\[|:|{)/,false)) {\n          override = \"tag\";\n          return pushContext(state, stream, \"block\");\n        }\n      }\n      if (typeIsPseudo(type, stream)) {\n        return pushContext(state, stream, \"pseudo\");\n      }\n      if (/@(font-face|media|supports|(-moz-)?document)/.test(type)) {\n        return pushContext(state, stream, endOfLine(stream) ? \"block\" : \"atBlock\");\n      }\n      if (/@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {\n        return pushContext(state, stream, \"keyframes\");\n      }\n      if (/@extends?/.test(type)) {\n        return pushContext(state, stream, \"extend\", 0);\n      }\n      if (type && type.charAt(0) == \"@\") {\n\n        // Property Lookup\n        if (stream.indentation() > 0 && wordIsProperty(stream.current().slice(1))) {\n          override = \"variable-2\";\n          return \"block\";\n        }\n        if (/(@import|@require|@charset)/.test(type)) {\n          return pushContext(state, stream, \"block\", 0);\n        }\n        return pushContext(state, stream, \"block\");\n      }\n      if (type == \"reference\" && endOfLine(stream)) {\n        return pushContext(state, stream, \"block\");\n      }\n      if (type == \"(\") {\n        return pushContext(state, stream, \"parens\");\n      }\n\n      if (type == \"vendor-prefixes\") {\n        return pushContext(state, stream, \"vendorPrefixes\");\n      }\n      if (type == \"word\") {\n        var word = stream.current();\n        override = wordAsValue(word);\n\n        if (override == \"property\") {\n          if (startOfLine(stream)) {\n            return pushContext(state, stream, \"block\", 0);\n          } else {\n            override = \"atom\";\n            return \"block\";\n          }\n        }\n\n        if (override == \"tag\") {\n\n          // tag is a css value\n          if (/embed|menu|pre|progress|sub|table/.test(word)) {\n            if (wordIsProperty(firstWordOfLine(stream))) {\n              override = \"atom\";\n              return \"block\";\n            }\n          }\n\n          // tag is an attribute\n          if (stream.string.match(new RegExp(\"\\\\[\\\\s*\" + word + \"|\" + word +\"\\\\s*\\\\]\"))) {\n            override = \"atom\";\n            return \"block\";\n          }\n\n          // tag is a variable\n          if (tagVariablesRegexp.test(word)) {\n            if ((startOfLine(stream) && stream.string.match(/=/)) ||\n                (!startOfLine(stream) &&\n                 !stream.string.match(/^(\\s*\\.|#|\\&|\\[|\\/|>|\\*)/) &&\n                 !wordIsTag(firstWordOfLine(stream)))) {\n              override = \"variable-2\";\n              if (wordIsBlock(firstWordOfLine(stream)))  return \"block\";\n              return pushContext(state, stream, \"block\", 0);\n            }\n          }\n\n          if (endOfLine(stream)) return pushContext(state, stream, \"block\");\n        }\n        if (override == \"block-keyword\") {\n          override = \"keyword\";\n\n          // Postfix conditionals\n          if (stream.current(/(if|unless)/) && !startOfLine(stream)) {\n            return \"block\";\n          }\n          return pushContext(state, stream, \"block\");\n        }\n        if (word == \"return\") return pushContext(state, stream, \"block\", 0);\n\n        // Placeholder selector\n        if (override == \"variable-2\" && stream.string.match(/^\\s?\\$[\\w-\\.\\[\\]\\'\\\"]+$/)) {\n          return pushContext(state, stream, \"block\");\n        }\n      }\n      return state.context.type;\n    };\n\n\n    /**\n     * Parens\n     */\n    states.parens = function(type, stream, state) {\n      if (type == \"(\") return pushContext(state, stream, \"parens\");\n      if (type == \")\") {\n        if (state.context.prev.type == \"parens\") {\n          return popContext(state);\n        }\n        if ((stream.string.match(/^[a-z][\\w-]*\\(/i) && endOfLine(stream)) ||\n            wordIsBlock(firstWordOfLine(stream)) ||\n            /(\\.|#|:|\\[|\\*|&|>|~|\\+|\\/)/.test(firstWordOfLine(stream)) ||\n            (!stream.string.match(/^-?[a-z][\\w-\\.\\[\\]\\'\\\"]*\\s*=/) &&\n             wordIsTag(firstWordOfLine(stream)))) {\n          return pushContext(state, stream, \"block\");\n        }\n        if (stream.string.match(/^[\\$-]?[a-z][\\w-\\.\\[\\]\\'\\\"]*\\s*=/) ||\n            stream.string.match(/^\\s*(\\(|\\)|[0-9])/) ||\n            stream.string.match(/^\\s+[a-z][\\w-]*\\(/i) ||\n            stream.string.match(/^\\s+[\\$-]?[a-z]/i)) {\n          return pushContext(state, stream, \"block\", 0);\n        }\n        if (endOfLine(stream)) return pushContext(state, stream, \"block\");\n        else return pushContext(state, stream, \"block\", 0);\n      }\n      if (type && type.charAt(0) == \"@\" && wordIsProperty(stream.current().slice(1))) {\n        override = \"variable-2\";\n      }\n      if (type == \"word\") {\n        var word = stream.current();\n        override = wordAsValue(word);\n        if (override == \"tag\" && tagVariablesRegexp.test(word)) {\n          override = \"variable-2\";\n        }\n        if (override == \"property\" || word == \"to\") override = \"atom\";\n      }\n      if (type == \"variable-name\") {\n        return pushContext(state, stream, \"variableName\");\n      }\n      if (typeIsPseudo(type, stream)) {\n        return pushContext(state, stream, \"pseudo\");\n      }\n      return state.context.type;\n    };\n\n\n    /**\n     * Vendor prefixes\n     */\n    states.vendorPrefixes = function(type, stream, state) {\n      if (type == \"word\") {\n        override = \"property\";\n        return pushContext(state, stream, \"block\", 0);\n      }\n      return popContext(state);\n    };\n\n\n    /**\n     * Pseudo\n     */\n    states.pseudo = function(type, stream, state) {\n      if (!wordIsProperty(firstWordOfLine(stream.string))) {\n        stream.match(/^[a-z-]+/);\n        override = \"variable-3\";\n        if (endOfLine(stream)) return pushContext(state, stream, \"block\");\n        return popContext(state);\n      }\n      return popAndPass(type, stream, state);\n    };\n\n\n    /**\n     * atBlock\n     */\n    states.atBlock = function(type, stream, state) {\n      if (type == \"(\") return pushContext(state, stream, \"atBlock_parens\");\n      if (typeIsBlock(type, stream)) {\n        return pushContext(state, stream, \"block\");\n      }\n      if (typeIsInterpolation(type, stream)) {\n        return pushContext(state, stream, \"interpolation\");\n      }\n      if (type == \"word\") {\n        var word = stream.current().toLowerCase();\n        if (/^(only|not|and|or)$/.test(word))\n          override = \"keyword\";\n        else if (documentTypes.hasOwnProperty(word))\n          override = \"tag\";\n        else if (mediaTypes.hasOwnProperty(word))\n          override = \"attribute\";\n        else if (mediaFeatures.hasOwnProperty(word))\n          override = \"property\";\n        else if (nonStandardPropertyKeywords.hasOwnProperty(word))\n          override = \"string-2\";\n        else override = wordAsValue(stream.current());\n        if (override == \"tag\" && endOfLine(stream)) {\n          return pushContext(state, stream, \"block\");\n        }\n      }\n      if (type == \"operator\" && /^(not|and|or)$/.test(stream.current())) {\n        override = \"keyword\";\n      }\n      return state.context.type;\n    };\n\n    states.atBlock_parens = function(type, stream, state) {\n      if (type == \"{\" || type == \"}\") return state.context.type;\n      if (type == \")\") {\n        if (endOfLine(stream)) return pushContext(state, stream, \"block\");\n        else return pushContext(state, stream, \"atBlock\");\n      }\n      if (type == \"word\") {\n        var word = stream.current().toLowerCase();\n        override = wordAsValue(word);\n        if (/^(max|min)/.test(word)) override = \"property\";\n        if (override == \"tag\") {\n          tagVariablesRegexp.test(word) ? override = \"variable-2\" : override = \"atom\";\n        }\n        return state.context.type;\n      }\n      return states.atBlock(type, stream, state);\n    };\n\n\n    /**\n     * Keyframes\n     */\n    states.keyframes = function(type, stream, state) {\n      if (stream.indentation() == \"0\" && ((type == \"}\" && startOfLine(stream)) || type == \"]\" || type == \"hash\"\n                                          || type == \"qualifier\" || wordIsTag(stream.current()))) {\n        return popAndPass(type, stream, state);\n      }\n      if (type == \"{\") return pushContext(state, stream, \"keyframes\");\n      if (type == \"}\") {\n        if (startOfLine(stream)) return popContext(state, true);\n        else return pushContext(state, stream, \"keyframes\");\n      }\n      if (type == \"unit\" && /^[0-9]+\\%$/.test(stream.current())) {\n        return pushContext(state, stream, \"keyframes\");\n      }\n      if (type == \"word\") {\n        override = wordAsValue(stream.current());\n        if (override == \"block-keyword\") {\n          override = \"keyword\";\n          return pushContext(state, stream, \"keyframes\");\n        }\n      }\n      if (/@(font-face|media|supports|(-moz-)?document)/.test(type)) {\n        return pushContext(state, stream, endOfLine(stream) ? \"block\" : \"atBlock\");\n      }\n      if (type == \"mixin\") {\n        return pushContext(state, stream, \"block\", 0);\n      }\n      return state.context.type;\n    };\n\n\n    /**\n     * Interpolation\n     */\n    states.interpolation = function(type, stream, state) {\n      if (type == \"{\") popContext(state) && pushContext(state, stream, \"block\");\n      if (type == \"}\") {\n        if (stream.string.match(/^\\s*(\\.|#|:|\\[|\\*|&|>|~|\\+|\\/)/i) ||\n            (stream.string.match(/^\\s*[a-z]/i) && wordIsTag(firstWordOfLine(stream)))) {\n          return pushContext(state, stream, \"block\");\n        }\n        if (!stream.string.match(/^(\\{|\\s*\\&)/) ||\n            stream.match(/\\s*[\\w-]/,false)) {\n          return pushContext(state, stream, \"block\", 0);\n        }\n        return pushContext(state, stream, \"block\");\n      }\n      if (type == \"variable-name\") {\n        return pushContext(state, stream, \"variableName\", 0);\n      }\n      if (type == \"word\") {\n        override = wordAsValue(stream.current());\n        if (override == \"tag\") override = \"atom\";\n      }\n      return state.context.type;\n    };\n\n\n    /**\n     * Extend/s\n     */\n    states.extend = function(type, stream, state) {\n      if (type == \"[\" || type == \"=\") return \"extend\";\n      if (type == \"]\") return popContext(state);\n      if (type == \"word\") {\n        override = wordAsValue(stream.current());\n        return \"extend\";\n      }\n      return popContext(state);\n    };\n\n\n    /**\n     * Variable name\n     */\n    states.variableName = function(type, stream, state) {\n      if (type == \"string\" || type == \"[\" || type == \"]\" || stream.current().match(/^(\\.|\\$)/)) {\n        if (stream.current().match(/^\\.[\\w-]+/i)) override = \"variable-2\";\n        return \"variableName\";\n      }\n      return popAndPass(type, stream, state);\n    };\n\n\n    return {\n      startState: function(base) {\n        return {\n          tokenize: null,\n          state: \"block\",\n          context: new Context(\"block\", base || 0, null)\n        };\n      },\n      token: function(stream, state) {\n        if (!state.tokenize && stream.eatSpace()) return null;\n        style = (state.tokenize || tokenBase)(stream, state);\n        if (style && typeof style == \"object\") {\n          type = style[1];\n          style = style[0];\n        }\n        override = style;\n        state.state = states[state.state](type, stream, state);\n        return override;\n      },\n      indent: function(state, textAfter, line) {\n\n        var cx = state.context,\n            ch = textAfter && textAfter.charAt(0),\n            indent = cx.indent,\n            lineFirstWord = firstWordOfLine(textAfter),\n            lineIndent = line.match(/^\\s*/)[0].replace(/\\t/g, indentUnitString).length,\n            prevLineFirstWord = state.context.prev ? state.context.prev.line.firstWord : \"\",\n            prevLineIndent = state.context.prev ? state.context.prev.line.indent : lineIndent;\n\n        if (cx.prev &&\n            (ch == \"}\" && (cx.type == \"block\" || cx.type == \"atBlock\" || cx.type == \"keyframes\") ||\n             ch == \")\" && (cx.type == \"parens\" || cx.type == \"atBlock_parens\") ||\n             ch == \"{\" && (cx.type == \"at\"))) {\n          indent = cx.indent - indentUnit;\n        } else if (!(/(\\})/.test(ch))) {\n          if (/@|\\$|\\d/.test(ch) ||\n              /^\\{/.test(textAfter) ||\n/^\\s*\\/(\\/|\\*)/.test(textAfter) ||\n              /^\\s*\\/\\*/.test(prevLineFirstWord) ||\n              /^\\s*[\\w-\\.\\[\\]\\'\\\"]+\\s*(\\?|:|\\+)?=/i.test(textAfter) ||\n/^(\\+|-)?[a-z][\\w-]*\\(/i.test(textAfter) ||\n/^return/.test(textAfter) ||\n              wordIsBlock(lineFirstWord)) {\n            indent = lineIndent;\n          } else if (/(\\.|#|:|\\[|\\*|&|>|~|\\+|\\/)/.test(ch) || wordIsTag(lineFirstWord)) {\n            if (/\\,\\s*$/.test(prevLineFirstWord)) {\n              indent = prevLineIndent;\n            } else if (/^\\s+/.test(line) && (/(\\.|#|:|\\[|\\*|&|>|~|\\+|\\/)/.test(prevLineFirstWord) || wordIsTag(prevLineFirstWord))) {\n              indent = lineIndent <= prevLineIndent ? prevLineIndent : prevLineIndent + indentUnit;\n            } else {\n              indent = lineIndent;\n            }\n          } else if (!/,\\s*$/.test(line) && (wordIsVendorPrefix(lineFirstWord) || wordIsProperty(lineFirstWord))) {\n            if (wordIsBlock(prevLineFirstWord)) {\n              indent = lineIndent <= prevLineIndent ? prevLineIndent : prevLineIndent + indentUnit;\n            } else if (/^\\{/.test(prevLineFirstWord)) {\n              indent = lineIndent <= prevLineIndent ? lineIndent : prevLineIndent + indentUnit;\n            } else if (wordIsVendorPrefix(prevLineFirstWord) || wordIsProperty(prevLineFirstWord)) {\n              indent = lineIndent >= prevLineIndent ? prevLineIndent : lineIndent;\n            } else if (/^(\\.|#|:|\\[|\\*|&|@|\\+|\\-|>|~|\\/)/.test(prevLineFirstWord) ||\n                      /=\\s*$/.test(prevLineFirstWord) ||\n                      wordIsTag(prevLineFirstWord) ||\n                      /^\\$[\\w-\\.\\[\\]\\'\\\"]/.test(prevLineFirstWord)) {\n              indent = prevLineIndent + indentUnit;\n            } else {\n              indent = lineIndent;\n            }\n          }\n        }\n        return indent;\n      },\n      electricChars: \"}\",\n      blockCommentStart: \"/*\",\n      blockCommentEnd: \"*/\",\n      blockCommentContinue: \" * \",\n      lineComment: \"//\",\n      fold: \"indent\"\n    };\n  });\n\n  // developer.mozilla.org/en-US/docs/Web/HTML/Element\n  var tagKeywords_ = [\"a\",\"abbr\",\"address\",\"area\",\"article\",\"aside\",\"audio\", \"b\", \"base\",\"bdi\", \"bdo\",\"bgsound\",\"blockquote\",\"body\",\"br\",\"button\",\"canvas\",\"caption\",\"cite\", \"code\",\"col\",\"colgroup\",\"data\",\"datalist\",\"dd\",\"del\",\"details\",\"dfn\",\"div\", \"dl\",\"dt\",\"em\",\"embed\",\"fieldset\",\"figcaption\",\"figure\",\"footer\",\"form\",\"h1\", \"h2\",\"h3\",\"h4\",\"h5\",\"h6\",\"head\",\"header\",\"hgroup\",\"hr\",\"html\",\"i\",\"iframe\", \"img\",\"input\",\"ins\",\"kbd\",\"keygen\",\"label\",\"legend\",\"li\",\"link\",\"main\",\"map\", \"mark\",\"marquee\",\"menu\",\"menuitem\",\"meta\",\"meter\",\"nav\",\"nobr\",\"noframes\", \"noscript\",\"object\",\"ol\",\"optgroup\",\"option\",\"output\",\"p\",\"param\",\"pre\", \"progress\",\"q\",\"rp\",\"rt\",\"ruby\",\"s\",\"samp\",\"script\",\"section\",\"select\", \"small\",\"source\",\"span\",\"strong\",\"style\",\"sub\",\"summary\",\"sup\",\"table\",\"tbody\",\"td\",\"textarea\",\"tfoot\",\"th\",\"thead\",\"time\",\"tr\",\"track\", \"u\",\"ul\",\"var\",\"video\"];\n\n  // github.com/codemirror/CodeMirror/blob/master/mode/css/css.js\n  // Note, \"url-prefix\" should precede \"url\" in order to match correctly in documentTypesRegexp\n  var documentTypes_ = [\"domain\", \"regexp\", \"url-prefix\", \"url\"];\n  var mediaTypes_ = [\"all\",\"aural\",\"braille\",\"handheld\",\"print\",\"projection\",\"screen\",\"tty\",\"tv\",\"embossed\"];\n  var mediaFeatures_ = [\"width\",\"min-width\",\"max-width\",\"height\",\"min-height\",\"max-height\",\"device-width\",\"min-device-width\",\"max-device-width\",\"device-height\",\"min-device-height\",\"max-device-height\",\"aspect-ratio\",\"min-aspect-ratio\",\"max-aspect-ratio\",\"device-aspect-ratio\",\"min-device-aspect-ratio\",\"max-device-aspect-ratio\",\"color\",\"min-color\",\"max-color\",\"color-index\",\"min-color-index\",\"max-color-index\",\"monochrome\",\"min-monochrome\",\"max-monochrome\",\"resolution\",\"min-resolution\",\"max-resolution\",\"scan\",\"grid\",\"dynamic-range\",\"video-dynamic-range\"];\n  var propertyKeywords_ = [\"align-content\",\"align-items\",\"align-self\",\"alignment-adjust\",\"alignment-baseline\",\"anchor-point\",\"animation\",\"animation-delay\",\"animation-direction\",\"animation-duration\",\"animation-fill-mode\",\"animation-iteration-count\",\"animation-name\",\"animation-play-state\",\"animation-timing-function\",\"appearance\",\"azimuth\",\"backface-visibility\",\"background\",\"background-attachment\",\"background-clip\",\"background-color\",\"background-image\",\"background-origin\",\"background-position\",\"background-repeat\",\"background-size\",\"baseline-shift\",\"binding\",\"bleed\",\"bookmark-label\",\"bookmark-level\",\"bookmark-state\",\"bookmark-target\",\"border\",\"border-bottom\",\"border-bottom-color\",\"border-bottom-left-radius\",\"border-bottom-right-radius\",\"border-bottom-style\",\"border-bottom-width\",\"border-collapse\",\"border-color\",\"border-image\",\"border-image-outset\",\"border-image-repeat\",\"border-image-slice\",\"border-image-source\",\"border-image-width\",\"border-left\",\"border-left-color\",\"border-left-style\",\"border-left-width\",\"border-radius\",\"border-right\",\"border-right-color\",\"border-right-style\",\"border-right-width\",\"border-spacing\",\"border-style\",\"border-top\",\"border-top-color\",\"border-top-left-radius\",\"border-top-right-radius\",\"border-top-style\",\"border-top-width\",\"border-width\",\"bottom\",\"box-decoration-break\",\"box-shadow\",\"box-sizing\",\"break-after\",\"break-before\",\"break-inside\",\"caption-side\",\"clear\",\"clip\",\"color\",\"color-profile\",\"column-count\",\"column-fill\",\"column-gap\",\"column-rule\",\"column-rule-color\",\"column-rule-style\",\"column-rule-width\",\"column-span\",\"column-width\",\"columns\",\"content\",\"counter-increment\",\"counter-reset\",\"crop\",\"cue\",\"cue-after\",\"cue-before\",\"cursor\",\"direction\",\"display\",\"dominant-baseline\",\"drop-initial-after-adjust\",\"drop-initial-after-align\",\"drop-initial-before-adjust\",\"drop-initial-before-align\",\"drop-initial-size\",\"drop-initial-value\",\"elevation\",\"empty-cells\",\"fit\",\"fit-position\",\"flex\",\"flex-basis\",\"flex-direction\",\"flex-flow\",\"flex-grow\",\"flex-shrink\",\"flex-wrap\",\"float\",\"float-offset\",\"flow-from\",\"flow-into\",\"font\",\"font-feature-settings\",\"font-family\",\"font-kerning\",\"font-language-override\",\"font-size\",\"font-size-adjust\",\"font-stretch\",\"font-style\",\"font-synthesis\",\"font-variant\",\"font-variant-alternates\",\"font-variant-caps\",\"font-variant-east-asian\",\"font-variant-ligatures\",\"font-variant-numeric\",\"font-variant-position\",\"font-weight\",\"grid\",\"grid-area\",\"grid-auto-columns\",\"grid-auto-flow\",\"grid-auto-position\",\"grid-auto-rows\",\"grid-column\",\"grid-column-end\",\"grid-column-start\",\"grid-row\",\"grid-row-end\",\"grid-row-start\",\"grid-template\",\"grid-template-areas\",\"grid-template-columns\",\"grid-template-rows\",\"hanging-punctuation\",\"height\",\"hyphens\",\"icon\",\"image-orientation\",\"image-rendering\",\"image-resolution\",\"inline-box-align\",\"justify-content\",\"left\",\"letter-spacing\",\"line-break\",\"line-height\",\"line-stacking\",\"line-stacking-ruby\",\"line-stacking-shift\",\"line-stacking-strategy\",\"list-style\",\"list-style-image\",\"list-style-position\",\"list-style-type\",\"margin\",\"margin-bottom\",\"margin-left\",\"margin-right\",\"margin-top\",\"marker-offset\",\"marks\",\"marquee-direction\",\"marquee-loop\",\"marquee-play-count\",\"marquee-speed\",\"marquee-style\",\"max-height\",\"max-width\",\"min-height\",\"min-width\",\"move-to\",\"nav-down\",\"nav-index\",\"nav-left\",\"nav-right\",\"nav-up\",\"object-fit\",\"object-position\",\"opacity\",\"order\",\"orphans\",\"outline\",\"outline-color\",\"outline-offset\",\"outline-style\",\"outline-width\",\"overflow\",\"overflow-style\",\"overflow-wrap\",\"overflow-x\",\"overflow-y\",\"padding\",\"padding-bottom\",\"padding-left\",\"padding-right\",\"padding-top\",\"page\",\"page-break-after\",\"page-break-before\",\"page-break-inside\",\"page-policy\",\"pause\",\"pause-after\",\"pause-before\",\"perspective\",\"perspective-origin\",\"pitch\",\"pitch-range\",\"play-during\",\"position\",\"presentation-level\",\"punctuation-trim\",\"quotes\",\"region-break-after\",\"region-break-before\",\"region-break-inside\",\"region-fragment\",\"rendering-intent\",\"resize\",\"rest\",\"rest-after\",\"rest-before\",\"richness\",\"right\",\"rotation\",\"rotation-point\",\"ruby-align\",\"ruby-overhang\",\"ruby-position\",\"ruby-span\",\"shape-image-threshold\",\"shape-inside\",\"shape-margin\",\"shape-outside\",\"size\",\"speak\",\"speak-as\",\"speak-header\",\"speak-numeral\",\"speak-punctuation\",\"speech-rate\",\"stress\",\"string-set\",\"tab-size\",\"table-layout\",\"target\",\"target-name\",\"target-new\",\"target-position\",\"text-align\",\"text-align-last\",\"text-decoration\",\"text-decoration-color\",\"text-decoration-line\",\"text-decoration-skip\",\"text-decoration-style\",\"text-emphasis\",\"text-emphasis-color\",\"text-emphasis-position\",\"text-emphasis-style\",\"text-height\",\"text-indent\",\"text-justify\",\"text-outline\",\"text-overflow\",\"text-shadow\",\"text-size-adjust\",\"text-space-collapse\",\"text-transform\",\"text-underline-position\",\"text-wrap\",\"top\",\"transform\",\"transform-origin\",\"transform-style\",\"transition\",\"transition-delay\",\"transition-duration\",\"transition-property\",\"transition-timing-function\",\"unicode-bidi\",\"vertical-align\",\"visibility\",\"voice-balance\",\"voice-duration\",\"voice-family\",\"voice-pitch\",\"voice-range\",\"voice-rate\",\"voice-stress\",\"voice-volume\",\"volume\",\"white-space\",\"widows\",\"width\",\"will-change\",\"word-break\",\"word-spacing\",\"word-wrap\",\"z-index\",\"clip-path\",\"clip-rule\",\"mask\",\"enable-background\",\"filter\",\"flood-color\",\"flood-opacity\",\"lighting-color\",\"stop-color\",\"stop-opacity\",\"pointer-events\",\"color-interpolation\",\"color-interpolation-filters\",\"color-rendering\",\"fill\",\"fill-opacity\",\"fill-rule\",\"image-rendering\",\"marker\",\"marker-end\",\"marker-mid\",\"marker-start\",\"shape-rendering\",\"stroke\",\"stroke-dasharray\",\"stroke-dashoffset\",\"stroke-linecap\",\"stroke-linejoin\",\"stroke-miterlimit\",\"stroke-opacity\",\"stroke-width\",\"text-rendering\",\"baseline-shift\",\"dominant-baseline\",\"glyph-orientation-horizontal\",\"glyph-orientation-vertical\",\"text-anchor\",\"writing-mode\",\"font-smoothing\",\"osx-font-smoothing\"];\n  var nonStandardPropertyKeywords_ = [\"scrollbar-arrow-color\",\"scrollbar-base-color\",\"scrollbar-dark-shadow-color\",\"scrollbar-face-color\",\"scrollbar-highlight-color\",\"scrollbar-shadow-color\",\"scrollbar-3d-light-color\",\"scrollbar-track-color\",\"shape-inside\",\"searchfield-cancel-button\",\"searchfield-decoration\",\"searchfield-results-button\",\"searchfield-results-decoration\",\"zoom\"];\n  var fontProperties_ = [\"font-family\",\"src\",\"unicode-range\",\"font-variant\",\"font-feature-settings\",\"font-stretch\",\"font-weight\",\"font-style\"];\n  var colorKeywords_ = [\"aliceblue\",\"antiquewhite\",\"aqua\",\"aquamarine\",\"azure\",\"beige\",\"bisque\",\"black\",\"blanchedalmond\",\"blue\",\"blueviolet\",\"brown\",\"burlywood\",\"cadetblue\",\"chartreuse\",\"chocolate\",\"coral\",\"cornflowerblue\",\"cornsilk\",\"crimson\",\"cyan\",\"darkblue\",\"darkcyan\",\"darkgoldenrod\",\"darkgray\",\"darkgreen\",\"darkkhaki\",\"darkmagenta\",\"darkolivegreen\",\"darkorange\",\"darkorchid\",\"darkred\",\"darksalmon\",\"darkseagreen\",\"darkslateblue\",\"darkslategray\",\"darkturquoise\",\"darkviolet\",\"deeppink\",\"deepskyblue\",\"dimgray\",\"dodgerblue\",\"firebrick\",\"floralwhite\",\"forestgreen\",\"fuchsia\",\"gainsboro\",\"ghostwhite\",\"gold\",\"goldenrod\",\"gray\",\"grey\",\"green\",\"greenyellow\",\"honeydew\",\"hotpink\",\"indianred\",\"indigo\",\"ivory\",\"khaki\",\"lavender\",\"lavenderblush\",\"lawngreen\",\"lemonchiffon\",\"lightblue\",\"lightcoral\",\"lightcyan\",\"lightgoldenrodyellow\",\"lightgray\",\"lightgreen\",\"lightpink\",\"lightsalmon\",\"lightseagreen\",\"lightskyblue\",\"lightslategray\",\"lightsteelblue\",\"lightyellow\",\"lime\",\"limegreen\",\"linen\",\"magenta\",\"maroon\",\"mediumaquamarine\",\"mediumblue\",\"mediumorchid\",\"mediumpurple\",\"mediumseagreen\",\"mediumslateblue\",\"mediumspringgreen\",\"mediumturquoise\",\"mediumvioletred\",\"midnightblue\",\"mintcream\",\"mistyrose\",\"moccasin\",\"navajowhite\",\"navy\",\"oldlace\",\"olive\",\"olivedrab\",\"orange\",\"orangered\",\"orchid\",\"palegoldenrod\",\"palegreen\",\"paleturquoise\",\"palevioletred\",\"papayawhip\",\"peachpuff\",\"peru\",\"pink\",\"plum\",\"powderblue\",\"purple\",\"rebeccapurple\",\"red\",\"rosybrown\",\"royalblue\",\"saddlebrown\",\"salmon\",\"sandybrown\",\"seagreen\",\"seashell\",\"sienna\",\"silver\",\"skyblue\",\"slateblue\",\"slategray\",\"snow\",\"springgreen\",\"steelblue\",\"tan\",\"teal\",\"thistle\",\"tomato\",\"turquoise\",\"violet\",\"wheat\",\"white\",\"whitesmoke\",\"yellow\",\"yellowgreen\"];\n  var valueKeywords_ = [\"above\",\"absolute\",\"activeborder\",\"additive\",\"activecaption\",\"afar\",\"after-white-space\",\"ahead\",\"alias\",\"all\",\"all-scroll\",\"alphabetic\",\"alternate\",\"always\",\"amharic\",\"amharic-abegede\",\"antialiased\",\"appworkspace\",\"arabic-indic\",\"armenian\",\"asterisks\",\"attr\",\"auto\",\"avoid\",\"avoid-column\",\"avoid-page\",\"avoid-region\",\"background\",\"backwards\",\"baseline\",\"below\",\"bidi-override\",\"binary\",\"bengali\",\"blink\",\"block\",\"block-axis\",\"bold\",\"bolder\",\"border\",\"border-box\",\"both\",\"bottom\",\"break\",\"break-all\",\"break-word\",\"bullets\",\"button\",\"buttonface\",\"buttonhighlight\",\"buttonshadow\",\"buttontext\",\"calc\",\"cambodian\",\"capitalize\",\"caps-lock-indicator\",\"caption\",\"captiontext\",\"caret\",\"cell\",\"center\",\"checkbox\",\"circle\",\"cjk-decimal\",\"cjk-earthly-branch\",\"cjk-heavenly-stem\",\"cjk-ideographic\",\"clear\",\"clip\",\"close-quote\",\"col-resize\",\"collapse\",\"column\",\"compact\",\"condensed\",\"conic-gradient\",\"contain\",\"content\",\"contents\",\"content-box\",\"context-menu\",\"continuous\",\"copy\",\"counter\",\"counters\",\"cover\",\"crop\",\"cross\",\"crosshair\",\"currentcolor\",\"cursive\",\"cyclic\",\"dashed\",\"decimal\",\"decimal-leading-zero\",\"default\",\"default-button\",\"destination-atop\",\"destination-in\",\"destination-out\",\"destination-over\",\"devanagari\",\"disc\",\"discard\",\"disclosure-closed\",\"disclosure-open\",\"document\",\"dot-dash\",\"dot-dot-dash\",\"dotted\",\"double\",\"down\",\"e-resize\",\"ease\",\"ease-in\",\"ease-in-out\",\"ease-out\",\"element\",\"ellipse\",\"ellipsis\",\"embed\",\"end\",\"ethiopic\",\"ethiopic-abegede\",\"ethiopic-abegede-am-et\",\"ethiopic-abegede-gez\",\"ethiopic-abegede-ti-er\",\"ethiopic-abegede-ti-et\",\"ethiopic-halehame-aa-er\",\"ethiopic-halehame-aa-et\",\"ethiopic-halehame-am-et\",\"ethiopic-halehame-gez\",\"ethiopic-halehame-om-et\",\"ethiopic-halehame-sid-et\",\"ethiopic-halehame-so-et\",\"ethiopic-halehame-ti-er\",\"ethiopic-halehame-ti-et\",\"ethiopic-halehame-tig\",\"ethiopic-numeric\",\"ew-resize\",\"expanded\",\"extends\",\"extra-condensed\",\"extra-expanded\",\"fantasy\",\"fast\",\"fill\",\"fixed\",\"flat\",\"flex\",\"footnotes\",\"forwards\",\"from\",\"geometricPrecision\",\"georgian\",\"graytext\",\"groove\",\"gujarati\",\"gurmukhi\",\"hand\",\"hangul\",\"hangul-consonant\",\"hebrew\",\"help\",\"hidden\",\"hide\",\"high\",\"higher\",\"highlight\",\"highlighttext\",\"hiragana\",\"hiragana-iroha\",\"horizontal\",\"hsl\",\"hsla\",\"icon\",\"ignore\",\"inactiveborder\",\"inactivecaption\",\"inactivecaptiontext\",\"infinite\",\"infobackground\",\"infotext\",\"inherit\",\"initial\",\"inline\",\"inline-axis\",\"inline-block\",\"inline-flex\",\"inline-table\",\"inset\",\"inside\",\"intrinsic\",\"invert\",\"italic\",\"japanese-formal\",\"japanese-informal\",\"justify\",\"kannada\",\"katakana\",\"katakana-iroha\",\"keep-all\",\"khmer\",\"korean-hangul-formal\",\"korean-hanja-formal\",\"korean-hanja-informal\",\"landscape\",\"lao\",\"large\",\"larger\",\"left\",\"level\",\"lighter\",\"line-through\",\"linear\",\"linear-gradient\",\"lines\",\"list-item\",\"listbox\",\"listitem\",\"local\",\"logical\",\"loud\",\"lower\",\"lower-alpha\",\"lower-armenian\",\"lower-greek\",\"lower-hexadecimal\",\"lower-latin\",\"lower-norwegian\",\"lower-roman\",\"lowercase\",\"ltr\",\"malayalam\",\"match\",\"matrix\",\"matrix3d\",\"media-play-button\",\"media-slider\",\"media-sliderthumb\",\"media-volume-slider\",\"media-volume-sliderthumb\",\"medium\",\"menu\",\"menulist\",\"menulist-button\",\"menutext\",\"message-box\",\"middle\",\"min-intrinsic\",\"mix\",\"mongolian\",\"monospace\",\"move\",\"multiple\",\"myanmar\",\"n-resize\",\"narrower\",\"ne-resize\",\"nesw-resize\",\"no-close-quote\",\"no-drop\",\"no-open-quote\",\"no-repeat\",\"none\",\"normal\",\"not-allowed\",\"nowrap\",\"ns-resize\",\"numbers\",\"numeric\",\"nw-resize\",\"nwse-resize\",\"oblique\",\"octal\",\"open-quote\",\"optimizeLegibility\",\"optimizeSpeed\",\"oriya\",\"oromo\",\"outset\",\"outside\",\"outside-shape\",\"overlay\",\"overline\",\"padding\",\"padding-box\",\"painted\",\"page\",\"paused\",\"persian\",\"perspective\",\"plus-darker\",\"plus-lighter\",\"pointer\",\"polygon\",\"portrait\",\"pre\",\"pre-line\",\"pre-wrap\",\"preserve-3d\",\"progress\",\"push-button\",\"radial-gradient\",\"radio\",\"read-only\",\"read-write\",\"read-write-plaintext-only\",\"rectangle\",\"region\",\"relative\",\"repeat\",\"repeating-linear-gradient\",\"repeating-radial-gradient\",\"repeating-conic-gradient\",\"repeat-x\",\"repeat-y\",\"reset\",\"reverse\",\"rgb\",\"rgba\",\"ridge\",\"right\",\"rotate\",\"rotate3d\",\"rotateX\",\"rotateY\",\"rotateZ\",\"round\",\"row-resize\",\"rtl\",\"run-in\",\"running\",\"s-resize\",\"sans-serif\",\"scale\",\"scale3d\",\"scaleX\",\"scaleY\",\"scaleZ\",\"scroll\",\"scrollbar\",\"scroll-position\",\"se-resize\",\"searchfield\",\"searchfield-cancel-button\",\"searchfield-decoration\",\"searchfield-results-button\",\"searchfield-results-decoration\",\"semi-condensed\",\"semi-expanded\",\"separate\",\"serif\",\"show\",\"sidama\",\"simp-chinese-formal\",\"simp-chinese-informal\",\"single\",\"skew\",\"skewX\",\"skewY\",\"skip-white-space\",\"slide\",\"slider-horizontal\",\"slider-vertical\",\"sliderthumb-horizontal\",\"sliderthumb-vertical\",\"slow\",\"small\",\"small-caps\",\"small-caption\",\"smaller\",\"solid\",\"somali\",\"source-atop\",\"source-in\",\"source-out\",\"source-over\",\"space\",\"spell-out\",\"square\",\"square-button\",\"standard\",\"start\",\"static\",\"status-bar\",\"stretch\",\"stroke\",\"sub\",\"subpixel-antialiased\",\"super\",\"sw-resize\",\"symbolic\",\"symbols\",\"table\",\"table-caption\",\"table-cell\",\"table-column\",\"table-column-group\",\"table-footer-group\",\"table-header-group\",\"table-row\",\"table-row-group\",\"tamil\",\"telugu\",\"text\",\"text-bottom\",\"text-top\",\"textarea\",\"textfield\",\"thai\",\"thick\",\"thin\",\"threeddarkshadow\",\"threedface\",\"threedhighlight\",\"threedlightshadow\",\"threedshadow\",\"tibetan\",\"tigre\",\"tigrinya-er\",\"tigrinya-er-abegede\",\"tigrinya-et\",\"tigrinya-et-abegede\",\"to\",\"top\",\"trad-chinese-formal\",\"trad-chinese-informal\",\"translate\",\"translate3d\",\"translateX\",\"translateY\",\"translateZ\",\"transparent\",\"ultra-condensed\",\"ultra-expanded\",\"underline\",\"up\",\"upper-alpha\",\"upper-armenian\",\"upper-greek\",\"upper-hexadecimal\",\"upper-latin\",\"upper-norwegian\",\"upper-roman\",\"uppercase\",\"urdu\",\"url\",\"var\",\"vertical\",\"vertical-text\",\"visible\",\"visibleFill\",\"visiblePainted\",\"visibleStroke\",\"visual\",\"w-resize\",\"wait\",\"wave\",\"wider\",\"window\",\"windowframe\",\"windowtext\",\"words\",\"x-large\",\"x-small\",\"xor\",\"xx-large\",\"xx-small\",\"bicubic\",\"optimizespeed\",\"grayscale\",\"row\",\"row-reverse\",\"wrap\",\"wrap-reverse\",\"column-reverse\",\"flex-start\",\"flex-end\",\"space-between\",\"space-around\", \"unset\"];\n\n  var wordOperatorKeywords_ = [\"in\",\"and\",\"or\",\"not\",\"is not\",\"is a\",\"is\",\"isnt\",\"defined\",\"if unless\"],\n      blockKeywords_ = [\"for\",\"if\",\"else\",\"unless\", \"from\", \"to\"],\n      commonAtoms_ = [\"null\",\"true\",\"false\",\"href\",\"title\",\"type\",\"not-allowed\",\"readonly\",\"disabled\"],\n      commonDef_ = [\"@font-face\", \"@keyframes\", \"@media\", \"@viewport\", \"@page\", \"@host\", \"@supports\", \"@block\", \"@css\"];\n\n  var hintWords = tagKeywords_.concat(documentTypes_,mediaTypes_,mediaFeatures_,\n                                      propertyKeywords_,nonStandardPropertyKeywords_,\n                                      colorKeywords_,valueKeywords_,fontProperties_,\n                                      wordOperatorKeywords_,blockKeywords_,\n                                      commonAtoms_,commonDef_);\n\n  function wordRegexp(words) {\n    words = words.sort(function(a,b){return b > a;});\n    return new RegExp(\"^((\" + words.join(\")|(\") + \"))\\\\b\");\n  }\n\n  function keySet(array) {\n    var keys = {};\n    for (var i = 0; i < array.length; ++i) keys[array[i]] = true;\n    return keys;\n  }\n\n  function escapeRegExp(text) {\n    return text.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, \"\\\\$&\");\n  }\n\n  CodeMirror.registerHelper(\"hintWords\", \"stylus\", hintWords);\n  CodeMirror.defineMIME(\"text/x-styl\", \"stylus\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/swift/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Swift mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"./swift.js\"></script>\n<style>\n\t.CodeMirror { border: 2px inset #dee; }\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Swift</a>\n  </ul>\n</div>\n\n<article>\n<h2>Swift mode</h2>\n<form><textarea id=\"code\" name=\"code\">\nprotocol HeaderViewProtocol {\n    func setTitle(_ string: String)\n}\n\nstruct AnyHeaderView {\n    let view: UIView\n    let headerView: HeaderViewProtocol\n    init<T: UIView>(view: T) where T: HeaderViewProtocol {\n        self.view = view\n        self.headerView = view\n    }\n}\n\nlet header = AnyHeaderView(view: myView)\nheader.headerView.setTitle(\"hi\")\n\nstruct HeaderView {\n    let view: UIView\n    let setTitle: (String) -> ()\n}\n\nvar label = UILabel()\nlet header = HeaderView(view: label) { str in\n    label.text = str\n}\nheader.setTitle(\"hello\")\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-swift\"\n      });\n    </script>\n\n    <p>A simple mode for Swift</p>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-swift</code> (Swift code)</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/swift/swift.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Swift mode created by Michael Kaminsky https://github.com/mkaminsky11\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\")\n    mod(require(\"../../lib/codemirror\"))\n  else if (typeof define == \"function\" && define.amd)\n    define([\"../../lib/codemirror\"], mod)\n  else\n    mod(CodeMirror)\n})(function(CodeMirror) {\n  \"use strict\"\n\n  function wordSet(words) {\n    var set = {}\n    for (var i = 0; i < words.length; i++) set[words[i]] = true\n    return set\n  }\n\n  var keywords = wordSet([\"_\",\"var\",\"let\",\"actor\",\"class\",\"enum\",\"extension\",\"import\",\"protocol\",\"struct\",\"func\",\"typealias\",\"associatedtype\",\n                          \"open\",\"public\",\"internal\",\"fileprivate\",\"private\",\"deinit\",\"init\",\"new\",\"override\",\"self\",\"subscript\",\"super\",\n                          \"convenience\",\"dynamic\",\"final\",\"indirect\",\"lazy\",\"required\",\"static\",\"unowned\",\"unowned(safe)\",\"unowned(unsafe)\",\"weak\",\"as\",\"is\",\n                          \"break\",\"case\",\"continue\",\"default\",\"else\",\"fallthrough\",\"for\",\"guard\",\"if\",\"in\",\"repeat\",\"switch\",\"where\",\"while\",\n                          \"defer\",\"return\",\"inout\",\"mutating\",\"nonmutating\",\"isolated\",\"nonisolated\",\"catch\",\"do\",\"rethrows\",\"throw\",\"throws\",\"async\",\"await\",\"try\",\"didSet\",\"get\",\"set\",\"willSet\",\n                          \"assignment\",\"associativity\",\"infix\",\"left\",\"none\",\"operator\",\"postfix\",\"precedence\",\"precedencegroup\",\"prefix\",\"right\",\n                          \"Any\",\"AnyObject\",\"Type\",\"dynamicType\",\"Self\",\"Protocol\",\"__COLUMN__\",\"__FILE__\",\"__FUNCTION__\",\"__LINE__\"])\n  var definingKeywords = wordSet([\"var\",\"let\",\"actor\",\"class\",\"enum\",\"extension\",\"import\",\"protocol\",\"struct\",\"func\",\"typealias\",\"associatedtype\",\"for\"])\n  var atoms = wordSet([\"true\",\"false\",\"nil\",\"self\",\"super\",\"_\"])\n  var types = wordSet([\"Array\",\"Bool\",\"Character\",\"Dictionary\",\"Double\",\"Float\",\"Int\",\"Int8\",\"Int16\",\"Int32\",\"Int64\",\"Never\",\"Optional\",\"Set\",\"String\",\n                       \"UInt8\",\"UInt16\",\"UInt32\",\"UInt64\",\"Void\"])\n  var operators = \"+-/*%=|&<>~^?!\"\n  var punc = \":;,.(){}[]\"\n  var binary = /^\\-?0b[01][01_]*/\n  var octal = /^\\-?0o[0-7][0-7_]*/\n  var hexadecimal = /^\\-?0x[\\dA-Fa-f][\\dA-Fa-f_]*(?:(?:\\.[\\dA-Fa-f][\\dA-Fa-f_]*)?[Pp]\\-?\\d[\\d_]*)?/\n  var decimal = /^\\-?\\d[\\d_]*(?:\\.\\d[\\d_]*)?(?:[Ee]\\-?\\d[\\d_]*)?/\n  var identifier = /^\\$\\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\\1/\n  var property = /^\\.(?:\\$\\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\\1)/\n  var instruction = /^\\#[A-Za-z]+/\n  var attribute = /^@(?:\\$\\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\\1)/\n  //var regexp = /^\\/(?!\\s)(?:\\/\\/)?(?:\\\\.|[^\\/])+\\//\n\n  function tokenBase(stream, state, prev) {\n    if (stream.sol()) state.indented = stream.indentation()\n    if (stream.eatSpace()) return null\n\n    var ch = stream.peek()\n    if (ch == \"/\") {\n      if (stream.match(\"//\")) {\n        stream.skipToEnd()\n        return \"comment\"\n      }\n      if (stream.match(\"/*\")) {\n        state.tokenize.push(tokenComment)\n        return tokenComment(stream, state)\n      }\n    }\n    if (stream.match(instruction)) return \"builtin\"\n    if (stream.match(attribute)) return \"attribute\"\n    if (stream.match(binary)) return \"number\"\n    if (stream.match(octal)) return \"number\"\n    if (stream.match(hexadecimal)) return \"number\"\n    if (stream.match(decimal)) return \"number\"\n    if (stream.match(property)) return \"property\"\n    if (operators.indexOf(ch) > -1) {\n      stream.next()\n      return \"operator\"\n    }\n    if (punc.indexOf(ch) > -1) {\n      stream.next()\n      stream.match(\"..\")\n      return \"punctuation\"\n    }\n    var stringMatch\n    if (stringMatch = stream.match(/(\"\"\"|\"|')/)) {\n      var tokenize = tokenString.bind(null, stringMatch[0])\n      state.tokenize.push(tokenize)\n      return tokenize(stream, state)\n    }\n\n    if (stream.match(identifier)) {\n      var ident = stream.current()\n      if (types.hasOwnProperty(ident)) return \"variable-2\"\n      if (atoms.hasOwnProperty(ident)) return \"atom\"\n      if (keywords.hasOwnProperty(ident)) {\n        if (definingKeywords.hasOwnProperty(ident))\n          state.prev = \"define\"\n        return \"keyword\"\n      }\n      if (prev == \"define\") return \"def\"\n      return \"variable\"\n    }\n\n    stream.next()\n    return null\n  }\n\n  function tokenUntilClosingParen() {\n    var depth = 0\n    return function(stream, state, prev) {\n      var inner = tokenBase(stream, state, prev)\n      if (inner == \"punctuation\") {\n        if (stream.current() == \"(\") ++depth\n        else if (stream.current() == \")\") {\n          if (depth == 0) {\n            stream.backUp(1)\n            state.tokenize.pop()\n            return state.tokenize[state.tokenize.length - 1](stream, state)\n          }\n          else --depth\n        }\n      }\n      return inner\n    }\n  }\n\n  function tokenString(openQuote, stream, state) {\n    var singleLine = openQuote.length == 1\n    var ch, escaped = false\n    while (ch = stream.peek()) {\n      if (escaped) {\n        stream.next()\n        if (ch == \"(\") {\n          state.tokenize.push(tokenUntilClosingParen())\n          return \"string\"\n        }\n        escaped = false\n      } else if (stream.match(openQuote)) {\n        state.tokenize.pop()\n        return \"string\"\n      } else {\n        stream.next()\n        escaped = ch == \"\\\\\"\n      }\n    }\n    if (singleLine) {\n      state.tokenize.pop()\n    }\n    return \"string\"\n  }\n\n  function tokenComment(stream, state) {\n    var ch\n    while (ch = stream.next()) {\n      if (ch === \"/\" && stream.eat(\"*\")) {\n        state.tokenize.push(tokenComment)\n      } else if (ch === \"*\" && stream.eat(\"/\")) {\n        state.tokenize.pop()\n        break\n      }\n    }\n    return \"comment\"\n  }\n\n  function Context(prev, align, indented) {\n    this.prev = prev\n    this.align = align\n    this.indented = indented\n  }\n\n  function pushContext(state, stream) {\n    var align = stream.match(/^\\s*($|\\/[\\/\\*])/, false) ? null : stream.column() + 1\n    state.context = new Context(state.context, align, state.indented)\n  }\n\n  function popContext(state) {\n    if (state.context) {\n      state.indented = state.context.indented\n      state.context = state.context.prev\n    }\n  }\n\n  CodeMirror.defineMode(\"swift\", function(config) {\n    return {\n      startState: function() {\n        return {\n          prev: null,\n          context: null,\n          indented: 0,\n          tokenize: []\n        }\n      },\n\n      token: function(stream, state) {\n        var prev = state.prev\n        state.prev = null\n        var tokenize = state.tokenize[state.tokenize.length - 1] || tokenBase\n        var style = tokenize(stream, state, prev)\n        if (!style || style == \"comment\") state.prev = prev\n        else if (!state.prev) state.prev = style\n\n        if (style == \"punctuation\") {\n          var bracket = /[\\(\\[\\{]|([\\]\\)\\}])/.exec(stream.current())\n          if (bracket) (bracket[1] ? popContext : pushContext)(state, stream)\n        }\n\n        return style\n      },\n\n      indent: function(state, textAfter) {\n        var cx = state.context\n        if (!cx) return 0\n        var closing = /^[\\]\\}\\)]/.test(textAfter)\n        if (cx.align != null) return cx.align - (closing ? 1 : 0)\n        return cx.indented + (closing ? 0 : config.indentUnit)\n      },\n\n      electricInput: /^\\s*[\\)\\}\\]]$/,\n\n      lineComment: \"//\",\n      blockCommentStart: \"/*\",\n      blockCommentEnd: \"*/\",\n      fold: \"brace\",\n      closeBrackets: \"()[]{}''\\\"\\\"``\"\n    }\n  })\n\n  CodeMirror.defineMIME(\"text/x-swift\",\"swift\")\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/swift/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"swift\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  // Ensure all number types are properly represented.\n  MT(\"numbers\",\n     \"[keyword var] [def a] [operator =] [number 17]\",\n     \"[keyword var] [def b] [operator =] [number -0.5]\",\n     \"[keyword var] [def c] [operator =] [number 0.3456e-4]\",\n     \"[keyword var] [def d] [operator =] [number 345e2]\",\n     \"[keyword var] [def e] [operator =] [number 0o7324]\",\n     \"[keyword var] [def f] [operator =] [number 0b10010]\",\n     \"[keyword var] [def g] [operator =] [number -0x35ade]\",\n     \"[keyword var] [def h] [operator =] [number 0xaea.ep-13]\",\n     \"[keyword var] [def i] [operator =] [number 0x13ep6]\");\n\n  // Variable/class/etc definition.\n  MT(\"definition\",\n     \"[keyword var] [def a] [operator =] [number 5]\",\n     \"[keyword let] [def b][punctuation :] [variable-2 Int] [operator =] [number 10]\",\n     \"[keyword class] [def C] [punctuation {] [punctuation }]\",\n     \"[keyword struct] [def D] [punctuation {] [punctuation }]\",\n     \"[keyword enum] [def E] [punctuation {] [punctuation }]\",\n     \"[keyword extension] [def F] [punctuation {] [punctuation }]\",\n     \"[keyword protocol] [def G] [punctuation {] [punctuation }]\",\n     \"[keyword func] [def h][punctuation ()] [punctuation {] [punctuation }]\",\n     \"[keyword import] [def Foundation]\",\n     \"[keyword typealias] [def NewString] [operator =] [variable-2 String]\",\n     \"[keyword associatedtype] [def I]\",\n     \"[keyword for] [def j] [keyword in] [number 0][punctuation ..][operator <][number 3] [punctuation {] [punctuation }]\");\n\n  // Strings and string interpolation.\n  MT(\"strings\",\n     \"[keyword var] [def a][punctuation :] [variable-2 String] [operator =] [string \\\"test\\\"]\",\n     \"[keyword var] [def b][punctuation :] [variable-2 String] [operator =] [string \\\"\\\\(][variable a][string )\\\"]\",\n     \"[keyword var] [def c] [operator =] [string \\\"\\\"\\\"]\",\n     \"[string multi]\",\n     \"[string line]\",\n     \"[string \\\"test\\\"]\",\n     \"[string \\\"\\\"\\\"]\",\n     \"[variable print][punctuation (][string \\\"\\\"][punctuation )]\");\n\n  // Comments.\n  MT(\"comments\",\n     \"[comment // This is a comment]\",\n     \"[comment /* This is another comment */]\",\n     \"[keyword var] [def a] [operator =] [number 5] [comment // Third comment]\");\n\n  // Atoms.\n  MT(\"atoms\",\n     \"[keyword class] [def FooClass] [punctuation {]\",\n     \"  [keyword let] [def fooBool][punctuation :] [variable-2 Bool][operator ?]\",\n     \"  [keyword let] [def fooInt][punctuation :] [variable-2 Int][operator ?]\",\n     \"  [keyword func] [keyword init][punctuation (][variable fooBool][punctuation :] [variable-2 Bool][punctuation ,] [variable barBool][punctuation :] [variable-2 Bool][punctuation )] [punctuation {]\",\n     \"    [atom super][property .init][punctuation ()]\",\n     \"    [atom self][property .fooBool] [operator =] [variable fooBool]\",\n     \"    [variable fooInt] [operator =] [atom nil]\",\n     \"    [keyword if] [variable barBool] [operator ==] [atom true] [punctuation {]\",\n     \"      [variable print][punctuation (][string \\\"True!\\\"][punctuation )]\",\n     \"    [punctuation }] [keyword else] [keyword if] [variable barBool] [operator ==] [atom false] [punctuation {]\",\n     \"      [keyword for] [atom _] [keyword in] [number 0][punctuation ...][number 5] [punctuation {]\",\n     \"        [variable print][punctuation (][string \\\"False!\\\"][punctuation )]\",\n     \"      [punctuation }]\",\n     \"    [punctuation }]\",\n     \"  [punctuation }]\",\n     \"[punctuation }]\");\n\n  // Types.\n  MT(\"types\",\n     \"[keyword var] [def a] [operator =] [variable-2 Array][operator <][variable-2 Int][operator >]\",\n     \"[keyword var] [def b] [operator =] [variable-2 Set][operator <][variable-2 Bool][operator >]\",\n     \"[keyword var] [def c] [operator =] [variable-2 Dictionary][operator <][variable-2 String][punctuation ,][variable-2 Character][operator >]\",\n     \"[keyword var] [def d][punctuation :] [variable-2 Int64][operator ?] [operator =] [variable-2 Optional][punctuation (][number 8][punctuation )]\",\n     \"[keyword func] [def e][punctuation ()] [operator ->] [variable-2 Void] [punctuation {]\",\n     \"  [keyword var] [def e1][punctuation :] [variable-2 Float] [operator =] [number 1.2]\",\n     \"[punctuation }]\",\n     \"[keyword func] [def f][punctuation ()] [operator ->] [variable-2 Never] [punctuation {]\",\n     \"  [keyword var] [def f1][punctuation :] [variable-2 Double] [operator =] [number 2.4]\",\n     \"[punctuation }]\");\n\n  // Operators.\n  MT(\"operators\",\n     \"[keyword var] [def a] [operator =] [number 1] [operator +] [number 2]\",\n     \"[keyword var] [def b] [operator =] [number 1] [operator -] [number 2]\",\n     \"[keyword var] [def c] [operator =] [number 1] [operator *] [number 2]\",\n     \"[keyword var] [def d] [operator =] [number 1] [operator /] [number 2]\",\n     \"[keyword var] [def e] [operator =] [number 1] [operator %] [number 2]\",\n     \"[keyword var] [def f] [operator =] [number 1] [operator |] [number 2]\",\n     \"[keyword var] [def g] [operator =] [number 1] [operator &] [number 2]\",\n     \"[keyword var] [def h] [operator =] [number 1] [operator <<] [number 2]\",\n     \"[keyword var] [def i] [operator =] [number 1] [operator >>] [number 2]\",\n     \"[keyword var] [def j] [operator =] [number 1] [operator ^] [number 2]\",\n     \"[keyword var] [def k] [operator =] [operator ~][number 1]\",\n     \"[keyword var] [def l] [operator =] [variable foo] [operator ?] [number 1] [punctuation :] [number 2]\",\n     \"[keyword var] [def m][punctuation :] [variable-2 Int] [operator =] [variable-2 Optional][punctuation (][number 8][punctuation )][operator !]\");\n\n  // Punctuation.\n  MT(\"punctuation\",\n     \"[keyword let] [def a] [operator =] [number 1][punctuation ;] [keyword let] [def b] [operator =] [number 2]\",\n     \"[keyword let] [def testArr][punctuation :] [punctuation [[][variable-2 Int][punctuation ]]] [operator =] [punctuation [[][variable a][punctuation ,] [variable b][punctuation ]]]\",\n     \"[keyword for] [def i] [keyword in] [number 0][punctuation ..][operator <][variable testArr][property .count] [punctuation {]\",\n     \"  [variable print][punctuation (][variable testArr][punctuation [[][variable i][punctuation ]])]\",\n     \"[punctuation }]\");\n\n  // Identifiers.\n  MT(\"identifiers\",\n     \"[keyword let] [def abc] [operator =] [number 1]\",\n     \"[keyword let] [def ABC] [operator =] [number 2]\",\n     \"[keyword let] [def _123] [operator =] [number 3]\",\n     \"[keyword let] [def _$1$2$3] [operator =] [number 4]\",\n     \"[keyword let] [def A1$_c32_$_] [operator =] [number 5]\",\n     \"[keyword let] [def `var`] [operator =] [punctuation [[][number 1][punctuation ,] [number 2][punctuation ,] [number 3][punctuation ]]]\",\n     \"[keyword let] [def square$] [operator =] [variable `var`][property .map] [punctuation {][variable $0] [operator *] [variable $0][punctuation }]\",\n     \"$$ [number 1][variable a] $[atom _] [variable _$] [variable __] `[variable a] [variable b]`\");\n\n  // Properties.\n  MT(\"properties\",\n     \"[variable print][punctuation (][variable foo][property .abc][punctuation )]\",\n     \"[variable print][punctuation (][variable foo][property .ABC][punctuation )]\",\n     \"[variable print][punctuation (][variable foo][property ._123][punctuation )]\",\n     \"[variable print][punctuation (][variable foo][property ._$1$2$3][punctuation )]\",\n     \"[variable print][punctuation (][variable foo][property .A1$_c32_$_][punctuation )]\",\n     \"[variable print][punctuation (][variable foo][property .`var`][punctuation )]\",\n     \"[variable print][punctuation (][variable foo][property .__][punctuation )]\");\n\n  // Instructions or other things that start with #.\n  MT(\"instructions\",\n     \"[keyword if] [builtin #available][punctuation (][variable iOS] [number 9][punctuation ,] [operator *][punctuation )] [punctuation {}]\",\n     \"[variable print][punctuation (][builtin #file][punctuation ,] [builtin #function][punctuation )]\",\n     \"[variable print][punctuation (][builtin #line][punctuation ,] [builtin #column][punctuation )]\",\n     \"[builtin #if] [atom true]\",\n     \"[keyword import] [def A]\",\n     \"[builtin #elseif] [atom false]\",\n     \"[keyword import] [def B]\",\n     \"[builtin #endif]\",\n     \"[builtin #sourceLocation][punctuation (][variable file][punctuation :] [string \\\"file.swift\\\"][punctuation ,] [variable line][punctuation :] [number 2][punctuation )]\");\n\n  // Attributes; things that start with @.\n  MT(\"attributes\",\n     \"[attribute @objc][punctuation (][variable objcFoo][punctuation :)]\",\n     \"[attribute @available][punctuation (][variable iOS][punctuation )]\");\n\n  // Property/number edge case.\n  MT(\"property_number\",\n     \"[variable print][punctuation (][variable foo][property ._123][punctuation )]\",\n     \"[variable print][punctuation (]\")\n\n  MT(\"nested_comments\",\n     \"[comment /*]\",\n     \"[comment But wait /* this is a nested comment */ for real]\",\n     \"[comment /**** let * me * show * you ****/]\",\n     \"[comment ///// let / me / show / you /////]\",\n     \"[comment */]\");\n\n  // TODO: correctly identify when multiple variables are being declared\n  // by use of a comma-separated list.\n  // TODO: correctly identify when variables are being declared in a tuple.\n  // TODO: identify protocols as types when used before an extension?\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/tcl/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Tcl mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/night.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"tcl.js\"></script>\n<script src=\"../../addon/scroll/scrollpastend.js\"></script>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Tcl</a>\n  </ul>\n</div>\n\n<article>\n<h2>Tcl mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n##############################################################################################\n##  ##     whois.tcl for eggdrop by Ford_Lawnmower irc.geekshed.net #Script-Help        ##  ##\n##############################################################################################\n## To use this script you must set channel flag +whois (ie .chanset #chan +whois)           ##\n##############################################################################################\n##      ____                __                 ###########################################  ##\n##     / __/___ _ ___ _ ___/ /____ ___   ___   ###########################################  ##\n##    / _/ / _ `// _ `// _  // __// _ \\ / _ \\  ###########################################  ##\n##   /___/ \\_, / \\_, / \\_,_//_/   \\___// .__/  ###########################################  ##\n##        /___/ /___/                 /_/      ###########################################  ##\n##                                             ###########################################  ##\n##############################################################################################\n##  ##                             Start Setup.                                         ##  ##\n##############################################################################################\nnamespace eval whois {\n## change cmdchar to the trigger you want to use                                        ##  ##\n  variable cmdchar \"!\"\n## change command to the word trigger you would like to use.                            ##  ##\n## Keep in mind, This will also change the .chanset +/-command                          ##  ##\n  variable command \"whois\"\n## change textf to the colors you want for the text.                                    ##  ##\n  variable textf \"\\017\\00304\"\n## change tagf to the colors you want for tags:                                         ##  ##\n  variable tagf \"\\017\\002\"\n## Change logo to the logo you want at the start of the line.                           ##  ##\n  variable logo \"\\017\\00304\\002\\[\\00306W\\003hois\\00304\\]\\017\"\n## Change lineout to the results you want. Valid results are channel users modes topic  ##  ##\n  variable lineout \"channel users modes topic\"\n##############################################################################################\n##  ##                           End Setup.                                              ## ##\n##############################################################################################\n  variable channel \"\"\n  setudef flag $whois::command\n  bind pub -|- [string trimleft $whois::cmdchar]${whois::command} whois::list\n  bind raw -|- \"311\" whois::311\n  bind raw -|- \"312\" whois::312\n  bind raw -|- \"319\" whois::319\n  bind raw -|- \"317\" whois::317\n  bind raw -|- \"313\" whois::multi\n  bind raw -|- \"310\" whois::multi\n  bind raw -|- \"335\" whois::multi\n  bind raw -|- \"301\" whois::301\n  bind raw -|- \"671\" whois::multi\n  bind raw -|- \"320\" whois::multi\n  bind raw -|- \"401\" whois::multi\n  bind raw -|- \"318\" whois::318\n  bind raw -|- \"307\" whois::307\n}\nproc whois::311 {from key text} {\n  if {[regexp -- {^[^\\s]+\\s(.+?)\\s(.+?)\\s(.+?)\\s\\*\\s\\:(.+)$} $text wholematch nick ident host realname]} {\n    putserv \"PRIVMSG $whois::channel :${whois::logo} ${whois::tagf}Host:${whois::textf} \\\n        $nick \\(${ident}@${host}\\) ${whois::tagf}Realname:${whois::textf} $realname\"\n  }\n}\nproc whois::multi {from key text} {\n  if {[regexp {\\:(.*)$} $text match $key]} {\n    putserv \"PRIVMSG $whois::channel :${whois::logo} ${whois::tagf}Note:${whois::textf} [subst $$key]\"\n        return 1\n  }\n}\nproc whois::312 {from key text} {\n  regexp {([^\\s]+)\\s\\:} $text match server\n  putserv \"PRIVMSG $whois::channel :${whois::logo} ${whois::tagf}Server:${whois::textf} $server\"\n}\nproc whois::319 {from key text} {\n  if {[regexp {.+\\:(.+)$} $text match channels]} {\n    putserv \"PRIVMSG $whois::channel :${whois::logo} ${whois::tagf}Channels:${whois::textf} $channels\"\n  }\n}\nproc whois::317 {from key text} {\n  if {[regexp -- {.*\\s(\\d+)\\s(\\d+)\\s\\:} $text wholematch idle signon]} {\n    putserv \"PRIVMSG $whois::channel :${whois::logo} ${whois::tagf}Connected:${whois::textf} \\\n        [ctime $signon] ${whois::tagf}Idle:${whois::textf} [duration $idle]\"\n  }\n}\nproc whois::301 {from key text} {\n  if {[regexp {^.+\\s[^\\s]+\\s\\:(.*)$} $text match awaymsg]} {\n    putserv \"PRIVMSG $whois::channel :${whois::logo} ${whois::tagf}Away:${whois::textf} $awaymsg\"\n  }\n}\nproc whois::318 {from key text} {\n  namespace eval whois {\n        variable channel \"\"\n  }\n  variable whois::channel \"\"\n}\nproc whois::307 {from key text} {\n  putserv \"PRIVMSG $whois::channel :${whois::logo} ${whois::tagf}Services:${whois::textf} Registered Nick\"\n}\nproc whois::list {nick host hand chan text} {\n  if {[lsearch -exact [channel info $chan] \"+${whois::command}\"] != -1} {\n    namespace eval whois {\n          variable channel \"\"\n        }\n    variable whois::channel $chan\n    putserv \"WHOIS $text\"\n  }\n}\nputlog \"\\002*Loaded* \\017\\00304\\002\\[\\00306W\\003hois\\00304\\]\\017 \\002by \\\nFord_Lawnmower irc.GeekShed.net #Script-Help\"\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        theme: \"night\",\n        lineNumbers: true,\n        indentUnit: 2,\n        scrollPastEnd: true,\n        mode: \"text/x-tcl\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-tcl</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/tcl/tcl.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n//tcl mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"tcl\", function() {\n  function parseWords(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n  var keywords = parseWords(\"Tcl safe after append array auto_execok auto_import auto_load \" +\n        \"auto_mkindex auto_mkindex_old auto_qualify auto_reset bgerror \" +\n        \"binary break catch cd close concat continue dde eof encoding error \" +\n        \"eval exec exit expr fblocked fconfigure fcopy file fileevent filename \" +\n        \"filename flush for foreach format gets glob global history http if \" +\n        \"incr info interp join lappend lindex linsert list llength load lrange \" +\n        \"lreplace lsearch lset lsort memory msgcat namespace open package parray \" +\n        \"pid pkg::create pkg_mkIndex proc puts pwd re_syntax read regex regexp \" +\n        \"registry regsub rename resource return scan seek set socket source split \" +\n        \"string subst switch tcl_endOfWord tcl_findLibrary tcl_startOfNextWord \" +\n        \"tcl_wordBreakAfter tcl_startOfPreviousWord tcl_wordBreakBefore tcltest \" +\n        \"tclvars tell time trace unknown unset update uplevel upvar variable \" +\n    \"vwait\");\n    var functions = parseWords(\"if elseif else and not or eq ne in ni for foreach while switch\");\n    var isOperatorChar = /[+\\-*&%=<>!?^\\/\\|]/;\n    function chain(stream, state, f) {\n      state.tokenize = f;\n      return f(stream, state);\n    }\n    function tokenBase(stream, state) {\n      var beforeParams = state.beforeParams;\n      state.beforeParams = false;\n      var ch = stream.next();\n      if ((ch == '\"' || ch == \"'\") && state.inParams) {\n        return chain(stream, state, tokenString(ch));\n      } else if (/[\\[\\]{}\\(\\),;\\.]/.test(ch)) {\n        if (ch == \"(\" && beforeParams) state.inParams = true;\n        else if (ch == \")\") state.inParams = false;\n          return null;\n      } else if (/\\d/.test(ch)) {\n        stream.eatWhile(/[\\w\\.]/);\n        return \"number\";\n      } else if (ch == \"#\") {\n        if (stream.eat(\"*\"))\n          return chain(stream, state, tokenComment);\n        if (ch == \"#\" && stream.match(/ *\\[ *\\[/))\n          return chain(stream, state, tokenUnparsed);\n        stream.skipToEnd();\n        return \"comment\";\n      } else if (ch == '\"') {\n        stream.skipTo(/\"/);\n        return \"comment\";\n      } else if (ch == \"$\") {\n        stream.eatWhile(/[$_a-z0-9A-Z\\.{:]/);\n        stream.eatWhile(/}/);\n        state.beforeParams = true;\n        return \"builtin\";\n      } else if (isOperatorChar.test(ch)) {\n        stream.eatWhile(isOperatorChar);\n        return \"comment\";\n      } else {\n        stream.eatWhile(/[\\w\\$_{}\\xa1-\\uffff]/);\n        var word = stream.current().toLowerCase();\n        if (keywords && keywords.propertyIsEnumerable(word))\n          return \"keyword\";\n        if (functions && functions.propertyIsEnumerable(word)) {\n          state.beforeParams = true;\n          return \"keyword\";\n        }\n        return null;\n      }\n    }\n    function tokenString(quote) {\n      return function(stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {\n          end = true;\n          break;\n        }\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (end) state.tokenize = tokenBase;\n        return \"string\";\n      };\n    }\n    function tokenComment(stream, state) {\n      var maybeEnd = false, ch;\n      while (ch = stream.next()) {\n        if (ch == \"#\" && maybeEnd) {\n          state.tokenize = tokenBase;\n          break;\n        }\n        maybeEnd = (ch == \"*\");\n      }\n      return \"comment\";\n    }\n    function tokenUnparsed(stream, state) {\n      var maybeEnd = 0, ch;\n      while (ch = stream.next()) {\n        if (ch == \"#\" && maybeEnd == 2) {\n          state.tokenize = tokenBase;\n          break;\n        }\n        if (ch == \"]\")\n          maybeEnd++;\n        else if (ch != \" \")\n          maybeEnd = 0;\n      }\n      return \"meta\";\n    }\n    return {\n      startState: function() {\n        return {\n          tokenize: tokenBase,\n          beforeParams: false,\n          inParams: false\n        };\n      },\n      token: function(stream, state) {\n        if (stream.eatSpace()) return null;\n        return state.tokenize(stream, state);\n      },\n      lineComment: \"#\"\n    };\n});\nCodeMirror.defineMIME(\"text/x-tcl\", \"tcl\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/textile/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Textile mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"textile.js\"></script>\n<style>.CodeMirror {background: #f8f8f8;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/marijnh/codemirror\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=\"active\" href=\"#\">Textile</a>\n  </ul>\n</div>\n\n<article>\n    <h2>Textile mode</h2>\n    <form><textarea id=\"code\" name=\"code\">\nh1. Textile Mode\n\nA paragraph without formatting.\n\np. A simple Paragraph.\n\n\nh2. Phrase Modifiers\n\nHere are some simple phrase modifiers: *strong*, _emphasis_, **bold**, and __italic__.\n\nA ??citation??, -deleted text-, +inserted text+, some ^superscript^, and some ~subscript~.\n\nA %span element% and @code element@\n\nA \"link\":http://example.com, a \"link with (alt text)\":urlAlias\n\n[urlAlias]http://example.com/\n\nAn image: !http://example.com/image.png! and an image with a link: !http://example.com/image.png!:http://example.com\n\nA sentence with a footnote.[123]\n\nfn123. The footnote is defined here.\n\nRegistered(r), Trademark(tm), and Copyright(c)\n\n\nh2. Headers\n\nh1. Top level\nh2. Second level\nh3. Third level\nh4. Fourth level\nh5. Fifth level\nh6. Lowest level\n\n\nh2.  Lists\n\n* An unordered list\n** foo bar\n*** foo bar\n**** foo bar\n** foo bar\n\n# An ordered list\n## foo bar\n### foo bar\n#### foo bar\n## foo bar\n\n- definition list := description\n- another item    := foo bar\n- spanning ines   :=\n                     foo bar\n\n                     foo bar =:\n\n\nh2. Attributes\n\nLayouts and phrase modifiers can be modified with various kinds of attributes: alignment, CSS ID, CSS class names, language, padding, and CSS styles.\n\nh3. Alignment\n\ndiv<. left align\ndiv>. right align\n\nh3. CSS ID and class name\n\nYou are a %(my-id#my-classname) rad% person.\n\nh3. Language\n\np[en_CA]. Strange weather, eh?\n\nh3. Horizontal Padding\n\np(())). 2em left padding, 3em right padding\n\nh3. CSS styling\n\np{background: red}. Fire!\n\n\nh2. Table\n\n|_.              Header 1               |_.      Header 2        |\n|{background:#ddd}. Cell with background|         Normal         |\n|\\2.         Cell spanning 2 columns                             |\n|/2.         Cell spanning 2 rows       |(cell-class). one       |\n|                                                two             |\n|>.                  Right aligned cell |<. Left aligned cell    |\n\n\nh3. A table with attributes:\n\ntable(#prices).\n|Adults|$5|\n|Children|$2|\n\n\nh2. Code blocks\n\nbc.\nfunction factorial(n) {\n    if (n === 0) {\n        return 1;\n    }\n    return n * factorial(n - 1);\n}\n\npre..\n                ,,,,,,\n            o#'9MMHb':'-,o,\n         .oH\":HH$' \"' ' -*R&o,\n        dMMM*\"\"'`'      .oM\"HM?.\n       ,MMM'          \"HLbd< ?&H\\\n      .:MH .\"\\          ` MM  MM&b\n     . \"*H    -        &MMMMMMMMMH:\n     .    dboo        MMMMMMMMMMMM.\n     .   dMMMMMMb      *MMMMMMMMMP.\n     .    MMMMMMMP        *MMMMMP .\n          `#MMMMM           MM6P ,\n       '    `MMMP\"           HM*`,\n        '    :MM             .- ,\n         '.   `#?..  .       ..'\n            -.   .         .-\n              ''-.oo,oo.-''\n\n\\. _(9>\n \\==_)\n  -'=\n\nh2. Temporarily disabling textile markup\n\nnotextile. Don't __touch this!__\n\nSurround text with double-equals to disable textile inline. Example: Use ==*asterisks*== for *strong* text.\n\n\nh2. HTML\n\nSome block layouts are simply textile versions of HTML tags with the same name, like @div@, @pre@, and @p@. HTML tags can also exist on their own line:\n\n<section>\n  <h1>Title</h1>\n  <p>Hello!</p>\n</section>\n\n</textarea></form>\n    <script>\n        var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n            lineNumbers: true,\n            mode: \"text/x-textile\"\n        });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-textile</code>.</p>\n\n    <p><strong>Parsing/Highlighting Tests:</strong> <a href=\"../../test/index.html#textile_*\">normal</a>,  <a href=\"../../test/index.html#verbose,textile_*\">verbose</a>.</p>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/textile/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({tabSize: 4}, 'textile');\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT('simpleParagraphs',\n      'Some text.',\n      '',\n      'Some more text.');\n\n  /*\n   * Phrase Modifiers\n   */\n\n  MT('em',\n      'foo [em _bar_]');\n\n  MT('emBoogus',\n      'code_mirror');\n\n  MT('strong',\n      'foo [strong *bar*]');\n\n  MT('strongBogus',\n      '3 * 3 = 9');\n\n  MT('italic',\n      'foo [em __bar__]');\n\n  MT('italicBogus',\n      'code__mirror');\n\n  MT('bold',\n      'foo [strong **bar**]');\n\n  MT('boldBogus',\n      '3 ** 3 = 27');\n\n  MT('simpleLink',\n      '[link \"CodeMirror\":https://codemirror.net]');\n\n  MT('referenceLink',\n      '[link \"CodeMirror\":code_mirror]',\n      'Normal Text.',\n      '[link [[code_mirror]]https://codemirror.net]');\n\n  MT('footCite',\n      'foo bar[qualifier [[1]]]');\n\n  MT('footCiteBogus',\n      'foo bar[[1a2]]');\n\n  MT('special-characters',\n          'Registered [tag (r)], ' +\n          'Trademark [tag (tm)], and ' +\n          'Copyright [tag (c)] 2008');\n\n  MT('cite',\n      \"A book is [keyword ??The Count of Monte Cristo??] by Dumas.\");\n\n  MT('additionAndDeletion',\n      'The news networks declared [negative -Al Gore-] ' +\n        '[positive +George W. Bush+] the winner in Florida.');\n\n  MT('subAndSup',\n      'f(x, n) = log [builtin ~4~] x [builtin ^n^]');\n\n  MT('spanAndCode',\n      'A [quote %span element%] and [atom @code element@]');\n\n  MT('spanBogus',\n      'Percentage 25% is not a span.');\n\n  MT('citeBogus',\n      'Question? is not a citation.');\n\n  MT('codeBogus',\n      'user@example.com');\n\n  MT('subBogus',\n      '~username');\n\n  MT('supBogus',\n      'foo ^ bar');\n\n  MT('deletionBogus',\n      '3 - 3 = 0');\n\n  MT('additionBogus',\n      '3 + 3 = 6');\n\n  MT('image',\n      'An image: [string !http://www.example.com/image.png!]');\n\n  MT('imageWithAltText',\n      'An image: [string !http://www.example.com/image.png (Alt Text)!]');\n\n  MT('imageWithUrl',\n      'An image: [string !http://www.example.com/image.png!:http://www.example.com/]');\n\n  /*\n   * Headers\n   */\n\n  MT('h1',\n      '[header&header-1 h1. foo]');\n\n  MT('h2',\n      '[header&header-2 h2. foo]');\n\n  MT('h3',\n      '[header&header-3 h3. foo]');\n\n  MT('h4',\n      '[header&header-4 h4. foo]');\n\n  MT('h5',\n      '[header&header-5 h5. foo]');\n\n  MT('h6',\n      '[header&header-6 h6. foo]');\n\n  MT('h7Bogus',\n      'h7. foo');\n\n  MT('multipleHeaders',\n      '[header&header-1 h1. Heading 1]',\n      '',\n      'Some text.',\n      '',\n      '[header&header-2 h2. Heading 2]',\n      '',\n      'More text.');\n\n  MT('h1inline',\n      '[header&header-1 h1. foo ][header&header-1&em _bar_][header&header-1  baz]');\n\n  /*\n   * Lists\n   */\n\n  MT('ul',\n      'foo',\n      'bar',\n      '',\n      '[variable-2 * foo]',\n      '[variable-2 * bar]');\n\n  MT('ulNoBlank',\n      'foo',\n      'bar',\n      '[variable-2 * foo]',\n      '[variable-2 * bar]');\n\n  MT('ol',\n      'foo',\n      'bar',\n      '',\n      '[variable-2 # foo]',\n      '[variable-2 # bar]');\n\n  MT('olNoBlank',\n      'foo',\n      'bar',\n      '[variable-2 # foo]',\n      '[variable-2 # bar]');\n\n  MT('ulFormatting',\n      '[variable-2 * ][variable-2&em _foo_][variable-2  bar]',\n      '[variable-2 * ][variable-2&strong *][variable-2&em&strong _foo_]' +\n        '[variable-2&strong *][variable-2  bar]',\n      '[variable-2 * ][variable-2&strong *foo*][variable-2  bar]');\n\n  MT('olFormatting',\n      '[variable-2 # ][variable-2&em _foo_][variable-2  bar]',\n      '[variable-2 # ][variable-2&strong *][variable-2&em&strong _foo_]' +\n        '[variable-2&strong *][variable-2  bar]',\n      '[variable-2 # ][variable-2&strong *foo*][variable-2  bar]');\n\n  MT('ulNested',\n      '[variable-2 * foo]',\n      '[variable-3 ** bar]',\n      '[keyword *** bar]',\n      '[variable-2 **** bar]',\n      '[variable-3 ** bar]');\n\n  MT('olNested',\n      '[variable-2 # foo]',\n      '[variable-3 ## bar]',\n      '[keyword ### bar]',\n      '[variable-2 #### bar]',\n      '[variable-3 ## bar]');\n\n  MT('ulNestedWithOl',\n      '[variable-2 * foo]',\n      '[variable-3 ## bar]',\n      '[keyword *** bar]',\n      '[variable-2 #### bar]',\n      '[variable-3 ** bar]');\n\n  MT('olNestedWithUl',\n      '[variable-2 # foo]',\n      '[variable-3 ** bar]',\n      '[keyword ### bar]',\n      '[variable-2 **** bar]',\n      '[variable-3 ## bar]');\n\n  MT('definitionList',\n      '[number - coffee := Hot ][number&em _and_][number  black]',\n      '',\n      'Normal text.');\n\n  MT('definitionListSpan',\n      '[number - coffee :=]',\n      '',\n      '[number Hot ][number&em _and_][number  black =:]',\n      '',\n      'Normal text.');\n\n  MT('boo',\n      '[number - dog := woof woof]',\n      '[number - cat := meow meow]',\n      '[number - whale :=]',\n      '[number Whale noises.]',\n      '',\n      '[number Also, ][number&em _splashing_][number . =:]');\n\n  /*\n   * Attributes\n   */\n\n  MT('divWithAttribute',\n      '[punctuation div][punctuation&attribute (#my-id)][punctuation . foo bar]');\n\n  MT('divWithAttributeAnd2emRightPadding',\n      '[punctuation div][punctuation&attribute (#my-id)((][punctuation . foo bar]');\n\n  MT('divWithClassAndId',\n      '[punctuation div][punctuation&attribute (my-class#my-id)][punctuation . foo bar]');\n\n  MT('paragraphWithCss',\n      'p[attribute {color:red;}]. foo bar');\n\n  MT('paragraphNestedStyles',\n      'p. [strong *foo ][strong&em _bar_][strong *]');\n\n  MT('paragraphWithLanguage',\n      'p[attribute [[fr]]]. Parlez-vous français?');\n\n  MT('paragraphLeftAlign',\n      'p[attribute <]. Left');\n\n  MT('paragraphRightAlign',\n      'p[attribute >]. Right');\n\n  MT('paragraphRightAlign',\n      'p[attribute =]. Center');\n\n  MT('paragraphJustified',\n      'p[attribute <>]. Justified');\n\n  MT('paragraphWithLeftIndent1em',\n      'p[attribute (]. Left');\n\n  MT('paragraphWithRightIndent1em',\n      'p[attribute )]. Right');\n\n  MT('paragraphWithLeftIndent2em',\n      'p[attribute ((]. Left');\n\n  MT('paragraphWithRightIndent2em',\n      'p[attribute ))]. Right');\n\n  MT('paragraphWithLeftIndent3emRightIndent2em',\n      'p[attribute ((())]. Right');\n\n  MT('divFormatting',\n      '[punctuation div. ][punctuation&strong *foo ]' +\n        '[punctuation&strong&em _bar_][punctuation&strong *]');\n\n  MT('phraseModifierAttributes',\n      'p[attribute (my-class)]. This is a paragraph that has a class and' +\n      ' this [em _][em&attribute (#special-phrase)][em emphasized phrase_]' +\n      ' has an id.');\n\n  MT('linkWithClass',\n      '[link \"(my-class). This is a link with class\":http://redcloth.org]');\n\n  /*\n   * Layouts\n   */\n\n  MT('paragraphLayouts',\n      'p. This is one paragraph.',\n      '',\n      'p. This is another.');\n\n  MT('div',\n      '[punctuation div. foo bar]');\n\n  MT('pre',\n      '[operator pre. Text]');\n\n  MT('bq.',\n      '[bracket bq. foo bar]',\n      '',\n      'Normal text.');\n\n  MT('footnote',\n      '[variable fn123. foo ][variable&strong *bar*]');\n\n  /*\n   * Spanning Layouts\n   */\n\n  MT('bq..ThenParagraph',\n      '[bracket bq.. foo bar]',\n      '',\n      '[bracket More quote.]',\n      'p. Normal Text');\n\n  MT('bq..ThenH1',\n      '[bracket bq.. foo bar]',\n      '',\n      '[bracket More quote.]',\n      '[header&header-1 h1. Header Text]');\n\n  MT('bc..ThenParagraph',\n      '[atom bc.. # Some ruby code]',\n      '[atom obj = {foo: :bar}]',\n      '[atom puts obj]',\n      '',\n      '[atom obj[[:love]] = \"*love*\"]',\n      '[atom puts obj.love.upcase]',\n      '',\n      'p. Normal text.');\n\n  MT('fn1..ThenParagraph',\n      '[variable fn1.. foo bar]',\n      '',\n      '[variable More.]',\n      'p. Normal Text');\n\n  MT('pre..ThenParagraph',\n      '[operator pre.. foo bar]',\n      '',\n      '[operator More.]',\n      'p. Normal Text');\n\n  /*\n   * Tables\n   */\n\n  MT('table',\n      '[variable-3&operator |_. name |_. age|]',\n      '[variable-3 |][variable-3&strong *Walter*][variable-3 |   5  |]',\n      '[variable-3 |Florence|   6  |]',\n      '',\n      'p. Normal text.');\n\n  MT('tableWithAttributes',\n      '[variable-3&operator |_. name |_. age|]',\n      '[variable-3 |][variable-3&attribute /2.][variable-3  Jim |]',\n      '[variable-3 |][variable-3&attribute \\\\2{color: red}.][variable-3  Sam |]');\n\n  /*\n   * HTML\n   */\n\n  MT('html',\n      '[comment <div id=\"wrapper\">]',\n      '[comment <section id=\"introduction\">]',\n      '',\n      '[header&header-1 h1. Welcome]',\n      '',\n      '[variable-2 * Item one]',\n      '[variable-2 * Item two]',\n      '',\n      '[comment <a href=\"http://example.com\">Example</a>]',\n      '',\n      '[comment </section>]',\n      '[comment </div>]');\n\n  MT('inlineHtml',\n      'I can use HTML directly in my [comment <span class=\"youbetcha\">Textile</span>].');\n\n  /*\n   * No-Textile\n   */\n\n  MT('notextile',\n    '[string-2 notextile. *No* formatting]');\n\n  MT('notextileInline',\n      'Use [string-2 ==*asterisks*==] for [strong *strong*] text.');\n\n  MT('notextileWithPre',\n      '[operator pre. *No* formatting]');\n\n  MT('notextileWithSpanningPre',\n      '[operator pre.. *No* formatting]',\n      '',\n      '[operator *No* formatting]');\n\n  /* Only toggling phrases between non-word chars. */\n\n  MT('phrase-in-word',\n     'foo_bar_baz');\n\n  MT('phrase-non-word',\n     '[negative -x-] aaa-bbb ccc-ddd [negative -eee-] fff [negative -ggg-]');\n\n  MT('phrase-lone-dash',\n     'foo - bar - baz');\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/textile/textile.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") { // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  } else if (typeof define == \"function\" && define.amd) { // AMD\n    define([\"../../lib/codemirror\"], mod);\n  } else { // Plain browser env\n    mod(CodeMirror);\n  }\n})(function(CodeMirror) {\n  \"use strict\";\n\n  var TOKEN_STYLES = {\n    addition: \"positive\",\n    attributes: \"attribute\",\n    bold: \"strong\",\n    cite: \"keyword\",\n    code: \"atom\",\n    definitionList: \"number\",\n    deletion: \"negative\",\n    div: \"punctuation\",\n    em: \"em\",\n    footnote: \"variable\",\n    footCite: \"qualifier\",\n    header: \"header\",\n    html: \"comment\",\n    image: \"string\",\n    italic: \"em\",\n    link: \"link\",\n    linkDefinition: \"link\",\n    list1: \"variable-2\",\n    list2: \"variable-3\",\n    list3: \"keyword\",\n    notextile: \"string-2\",\n    pre: \"operator\",\n    p: \"property\",\n    quote: \"bracket\",\n    span: \"quote\",\n    specialChar: \"tag\",\n    strong: \"strong\",\n    sub: \"builtin\",\n    sup: \"builtin\",\n    table: \"variable-3\",\n    tableHeading: \"operator\"\n  };\n\n  function startNewLine(stream, state) {\n    state.mode = Modes.newLayout;\n    state.tableHeading = false;\n\n    if (state.layoutType === \"definitionList\" && state.spanningLayout &&\n        stream.match(RE(\"definitionListEnd\"), false))\n      state.spanningLayout = false;\n  }\n\n  function handlePhraseModifier(stream, state, ch) {\n    if (ch === \"_\") {\n      if (stream.eat(\"_\"))\n        return togglePhraseModifier(stream, state, \"italic\", /__/, 2);\n      else\n        return togglePhraseModifier(stream, state, \"em\", /_/, 1);\n    }\n\n    if (ch === \"*\") {\n      if (stream.eat(\"*\")) {\n        return togglePhraseModifier(stream, state, \"bold\", /\\*\\*/, 2);\n      }\n      return togglePhraseModifier(stream, state, \"strong\", /\\*/, 1);\n    }\n\n    if (ch === \"[\") {\n      if (stream.match(/\\d+\\]/)) state.footCite = true;\n      return tokenStyles(state);\n    }\n\n    if (ch === \"(\") {\n      var spec = stream.match(/^(r|tm|c)\\)/);\n      if (spec)\n        return tokenStylesWith(state, TOKEN_STYLES.specialChar);\n    }\n\n    if (ch === \"<\" && stream.match(/(\\w+)[^>]+>[^<]+<\\/\\1>/))\n      return tokenStylesWith(state, TOKEN_STYLES.html);\n\n    if (ch === \"?\" && stream.eat(\"?\"))\n      return togglePhraseModifier(stream, state, \"cite\", /\\?\\?/, 2);\n\n    if (ch === \"=\" && stream.eat(\"=\"))\n      return togglePhraseModifier(stream, state, \"notextile\", /==/, 2);\n\n    if (ch === \"-\" && !stream.eat(\"-\"))\n      return togglePhraseModifier(stream, state, \"deletion\", /-/, 1);\n\n    if (ch === \"+\")\n      return togglePhraseModifier(stream, state, \"addition\", /\\+/, 1);\n\n    if (ch === \"~\")\n      return togglePhraseModifier(stream, state, \"sub\", /~/, 1);\n\n    if (ch === \"^\")\n      return togglePhraseModifier(stream, state, \"sup\", /\\^/, 1);\n\n    if (ch === \"%\")\n      return togglePhraseModifier(stream, state, \"span\", /%/, 1);\n\n    if (ch === \"@\")\n      return togglePhraseModifier(stream, state, \"code\", /@/, 1);\n\n    if (ch === \"!\") {\n      var type = togglePhraseModifier(stream, state, \"image\", /(?:\\([^\\)]+\\))?!/, 1);\n      stream.match(/^:\\S+/); // optional Url portion\n      return type;\n    }\n    return tokenStyles(state);\n  }\n\n  function togglePhraseModifier(stream, state, phraseModifier, closeRE, openSize) {\n    var charBefore = stream.pos > openSize ? stream.string.charAt(stream.pos - openSize - 1) : null;\n    var charAfter = stream.peek();\n    if (state[phraseModifier]) {\n      if ((!charAfter || /\\W/.test(charAfter)) && charBefore && /\\S/.test(charBefore)) {\n        var type = tokenStyles(state);\n        state[phraseModifier] = false;\n        return type;\n      }\n    } else if ((!charBefore || /\\W/.test(charBefore)) && charAfter && /\\S/.test(charAfter) &&\n               stream.match(new RegExp(\"^.*\\\\S\" + closeRE.source + \"(?:\\\\W|$)\"), false)) {\n      state[phraseModifier] = true;\n      state.mode = Modes.attributes;\n    }\n    return tokenStyles(state);\n  };\n\n  function tokenStyles(state) {\n    var disabled = textileDisabled(state);\n    if (disabled) return disabled;\n\n    var styles = [];\n    if (state.layoutType) styles.push(TOKEN_STYLES[state.layoutType]);\n\n    styles = styles.concat(activeStyles(\n      state, \"addition\", \"bold\", \"cite\", \"code\", \"deletion\", \"em\", \"footCite\",\n      \"image\", \"italic\", \"link\", \"span\", \"strong\", \"sub\", \"sup\", \"table\", \"tableHeading\"));\n\n    if (state.layoutType === \"header\")\n      styles.push(TOKEN_STYLES.header + \"-\" + state.header);\n\n    return styles.length ? styles.join(\" \") : null;\n  }\n\n  function textileDisabled(state) {\n    var type = state.layoutType;\n\n    switch(type) {\n    case \"notextile\":\n    case \"code\":\n    case \"pre\":\n      return TOKEN_STYLES[type];\n    default:\n      if (state.notextile)\n        return TOKEN_STYLES.notextile + (type ? (\" \" + TOKEN_STYLES[type]) : \"\");\n      return null;\n    }\n  }\n\n  function tokenStylesWith(state, extraStyles) {\n    var disabled = textileDisabled(state);\n    if (disabled) return disabled;\n\n    var type = tokenStyles(state);\n    if (extraStyles)\n      return type ? (type + \" \" + extraStyles) : extraStyles;\n    else\n      return type;\n  }\n\n  function activeStyles(state) {\n    var styles = [];\n    for (var i = 1; i < arguments.length; ++i) {\n      if (state[arguments[i]])\n        styles.push(TOKEN_STYLES[arguments[i]]);\n    }\n    return styles;\n  }\n\n  function blankLine(state) {\n    var spanningLayout = state.spanningLayout, type = state.layoutType;\n\n    for (var key in state) if (state.hasOwnProperty(key))\n      delete state[key];\n\n    state.mode = Modes.newLayout;\n    if (spanningLayout) {\n      state.layoutType = type;\n      state.spanningLayout = true;\n    }\n  }\n\n  var REs = {\n    cache: {},\n    single: {\n      bc: \"bc\",\n      bq: \"bq\",\n      definitionList: /- .*?:=+/,\n      definitionListEnd: /.*=:\\s*$/,\n      div: \"div\",\n      drawTable: /\\|.*\\|/,\n      foot: /fn\\d+/,\n      header: /h[1-6]/,\n      html: /\\s*<(?:\\/)?(\\w+)(?:[^>]+)?>(?:[^<]+<\\/\\1>)?/,\n      link: /[^\"]+\":\\S/,\n      linkDefinition: /\\[[^\\s\\]]+\\]\\S+/,\n      list: /(?:#+|\\*+)/,\n      notextile: \"notextile\",\n      para: \"p\",\n      pre: \"pre\",\n      table: \"table\",\n      tableCellAttributes: /[\\/\\\\]\\d+/,\n      tableHeading: /\\|_\\./,\n      tableText: /[^\"_\\*\\[\\(\\?\\+~\\^%@|-]+/,\n      text: /[^!\"_=\\*\\[\\(<\\?\\+~\\^%@-]+/\n    },\n    attributes: {\n      align: /(?:<>|<|>|=)/,\n      selector: /\\([^\\(][^\\)]+\\)/,\n      lang: /\\[[^\\[\\]]+\\]/,\n      pad: /(?:\\(+|\\)+){1,2}/,\n      css: /\\{[^\\}]+\\}/\n    },\n    createRe: function(name) {\n      switch (name) {\n      case \"drawTable\":\n        return REs.makeRe(\"^\", REs.single.drawTable, \"$\");\n      case \"html\":\n        return REs.makeRe(\"^\", REs.single.html, \"(?:\", REs.single.html, \")*\", \"$\");\n      case \"linkDefinition\":\n        return REs.makeRe(\"^\", REs.single.linkDefinition, \"$\");\n      case \"listLayout\":\n        return REs.makeRe(\"^\", REs.single.list, RE(\"allAttributes\"), \"*\\\\s+\");\n      case \"tableCellAttributes\":\n        return REs.makeRe(\"^\", REs.choiceRe(REs.single.tableCellAttributes,\n                                            RE(\"allAttributes\")), \"+\\\\.\");\n      case \"type\":\n        return REs.makeRe(\"^\", RE(\"allTypes\"));\n      case \"typeLayout\":\n        return REs.makeRe(\"^\", RE(\"allTypes\"), RE(\"allAttributes\"),\n                          \"*\\\\.\\\\.?\", \"(\\\\s+|$)\");\n      case \"attributes\":\n        return REs.makeRe(\"^\", RE(\"allAttributes\"), \"+\");\n\n      case \"allTypes\":\n        return REs.choiceRe(REs.single.div, REs.single.foot,\n                            REs.single.header, REs.single.bc, REs.single.bq,\n                            REs.single.notextile, REs.single.pre, REs.single.table,\n                            REs.single.para);\n\n      case \"allAttributes\":\n        return REs.choiceRe(REs.attributes.selector, REs.attributes.css,\n                            REs.attributes.lang, REs.attributes.align, REs.attributes.pad);\n\n      default:\n        return REs.makeRe(\"^\", REs.single[name]);\n      }\n    },\n    makeRe: function() {\n      var pattern = \"\";\n      for (var i = 0; i < arguments.length; ++i) {\n        var arg = arguments[i];\n        pattern += (typeof arg === \"string\") ? arg : arg.source;\n      }\n      return new RegExp(pattern);\n    },\n    choiceRe: function() {\n      var parts = [arguments[0]];\n      for (var i = 1; i < arguments.length; ++i) {\n        parts[i * 2 - 1] = \"|\";\n        parts[i * 2] = arguments[i];\n      }\n\n      parts.unshift(\"(?:\");\n      parts.push(\")\");\n      return REs.makeRe.apply(null, parts);\n    }\n  };\n\n  function RE(name) {\n    return (REs.cache[name] || (REs.cache[name] = REs.createRe(name)));\n  }\n\n  var Modes = {\n    newLayout: function(stream, state) {\n      if (stream.match(RE(\"typeLayout\"), false)) {\n        state.spanningLayout = false;\n        return (state.mode = Modes.blockType)(stream, state);\n      }\n      var newMode;\n      if (!textileDisabled(state)) {\n        if (stream.match(RE(\"listLayout\"), false))\n          newMode = Modes.list;\n        else if (stream.match(RE(\"drawTable\"), false))\n          newMode = Modes.table;\n        else if (stream.match(RE(\"linkDefinition\"), false))\n          newMode = Modes.linkDefinition;\n        else if (stream.match(RE(\"definitionList\")))\n          newMode = Modes.definitionList;\n        else if (stream.match(RE(\"html\"), false))\n          newMode = Modes.html;\n      }\n      return (state.mode = (newMode || Modes.text))(stream, state);\n    },\n\n    blockType: function(stream, state) {\n      var match, type;\n      state.layoutType = null;\n\n      if (match = stream.match(RE(\"type\")))\n        type = match[0];\n      else\n        return (state.mode = Modes.text)(stream, state);\n\n      if (match = type.match(RE(\"header\"))) {\n        state.layoutType = \"header\";\n        state.header = parseInt(match[0][1]);\n      } else if (type.match(RE(\"bq\"))) {\n        state.layoutType = \"quote\";\n      } else if (type.match(RE(\"bc\"))) {\n        state.layoutType = \"code\";\n      } else if (type.match(RE(\"foot\"))) {\n        state.layoutType = \"footnote\";\n      } else if (type.match(RE(\"notextile\"))) {\n        state.layoutType = \"notextile\";\n      } else if (type.match(RE(\"pre\"))) {\n        state.layoutType = \"pre\";\n      } else if (type.match(RE(\"div\"))) {\n        state.layoutType = \"div\";\n      } else if (type.match(RE(\"table\"))) {\n        state.layoutType = \"table\";\n      }\n\n      state.mode = Modes.attributes;\n      return tokenStyles(state);\n    },\n\n    text: function(stream, state) {\n      if (stream.match(RE(\"text\"))) return tokenStyles(state);\n\n      var ch = stream.next();\n      if (ch === '\"')\n        return (state.mode = Modes.link)(stream, state);\n      return handlePhraseModifier(stream, state, ch);\n    },\n\n    attributes: function(stream, state) {\n      state.mode = Modes.layoutLength;\n\n      if (stream.match(RE(\"attributes\")))\n        return tokenStylesWith(state, TOKEN_STYLES.attributes);\n      else\n        return tokenStyles(state);\n    },\n\n    layoutLength: function(stream, state) {\n      if (stream.eat(\".\") && stream.eat(\".\"))\n        state.spanningLayout = true;\n\n      state.mode = Modes.text;\n      return tokenStyles(state);\n    },\n\n    list: function(stream, state) {\n      var match = stream.match(RE(\"list\"));\n      state.listDepth = match[0].length;\n      var listMod = (state.listDepth - 1) % 3;\n      if (!listMod)\n        state.layoutType = \"list1\";\n      else if (listMod === 1)\n        state.layoutType = \"list2\";\n      else\n        state.layoutType = \"list3\";\n\n      state.mode = Modes.attributes;\n      return tokenStyles(state);\n    },\n\n    link: function(stream, state) {\n      state.mode = Modes.text;\n      if (stream.match(RE(\"link\"))) {\n        stream.match(/\\S+/);\n        return tokenStylesWith(state, TOKEN_STYLES.link);\n      }\n      return tokenStyles(state);\n    },\n\n    linkDefinition: function(stream, state) {\n      stream.skipToEnd();\n      return tokenStylesWith(state, TOKEN_STYLES.linkDefinition);\n    },\n\n    definitionList: function(stream, state) {\n      stream.match(RE(\"definitionList\"));\n\n      state.layoutType = \"definitionList\";\n\n      if (stream.match(/\\s*$/))\n        state.spanningLayout = true;\n      else\n        state.mode = Modes.attributes;\n\n      return tokenStyles(state);\n    },\n\n    html: function(stream, state) {\n      stream.skipToEnd();\n      return tokenStylesWith(state, TOKEN_STYLES.html);\n    },\n\n    table: function(stream, state) {\n      state.layoutType = \"table\";\n      return (state.mode = Modes.tableCell)(stream, state);\n    },\n\n    tableCell: function(stream, state) {\n      if (stream.match(RE(\"tableHeading\")))\n        state.tableHeading = true;\n      else\n        stream.eat(\"|\");\n\n      state.mode = Modes.tableCellAttributes;\n      return tokenStyles(state);\n    },\n\n    tableCellAttributes: function(stream, state) {\n      state.mode = Modes.tableText;\n\n      if (stream.match(RE(\"tableCellAttributes\")))\n        return tokenStylesWith(state, TOKEN_STYLES.attributes);\n      else\n        return tokenStyles(state);\n    },\n\n    tableText: function(stream, state) {\n      if (stream.match(RE(\"tableText\")))\n        return tokenStyles(state);\n\n      if (stream.peek() === \"|\") { // end of cell\n        state.mode = Modes.tableCell;\n        return tokenStyles(state);\n      }\n      return handlePhraseModifier(stream, state, stream.next());\n    }\n  };\n\n  CodeMirror.defineMode(\"textile\", function() {\n    return {\n      startState: function() {\n        return { mode: Modes.newLayout };\n      },\n      token: function(stream, state) {\n        if (stream.sol()) startNewLine(stream, state);\n        return state.mode(stream, state);\n      },\n      blankLine: blankLine\n    };\n  });\n\n  CodeMirror.defineMIME(\"text/x-textile\", \"textile\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/tiddlywiki/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: TiddlyWiki mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"tiddlywiki.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"tiddlywiki.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">TiddlyWiki</a>\n  </ul>\n</div>\n\n<article>\n<h2>TiddlyWiki mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n!TiddlyWiki Formatting\n* Rendered versions can be found at: http://www.tiddlywiki.com/#Reference\n\n|!Option            | !Syntax            |\n|bold font          | ''bold''           |\n|italic type        | //italic//         |\n|underlined text    | __underlined__     |\n|strikethrough text | --strikethrough--  |\n|superscript text   | super^^script^^    |\n|subscript text     | sub~~script~~      |\n|highlighted text   | @@highlighted@@    |\n|preformatted text  | {{{preformatted}}} |\n\n!Block Elements\n<<<\n!Heading 1\n\n!!Heading 2\n\n!!!Heading 3\n\n!!!!Heading 4\n\n!!!!!Heading 5\n<<<\n\n!!Lists\n<<<\n* unordered list, level 1\n** unordered list, level 2\n*** unordered list, level 3\n\n# ordered list, level 1\n## ordered list, level 2\n### unordered list, level 3\n\n; definition list, term\n: definition list, description\n<<<\n\n!!Blockquotes\n<<<\n> blockquote, level 1\n>> blockquote, level 2\n>>> blockquote, level 3\n\n> blockquote\n<<<\n\n!!Preformatted Text\n<<<\n{{{\npreformatted (e.g. code)\n}}}\n<<<\n\n!!Code Sections\n<<<\n{{{\nText style code\n}}}\n\n//{{{\nJS styled code. TiddlyWiki mixed mode should support highlighter switching in the future.\n//}}}\n\n<!--{{{-->\nXML styled code. TiddlyWiki mixed mode should support highlighter switching in the future.\n<!--}}}-->\n<<<\n\n!!Tables\n<<<\n|CssClass|k\n|!heading column 1|!heading column 2|\n|row 1, column 1|row 1, column 2|\n|row 2, column 1|row 2, column 2|\n|>|COLSPAN|\n|ROWSPAN| ... |\n|~| ... |\n|CssProperty:value;...| ... |\n|caption|c\n\n''Annotation:''\n* The {{{>}}} marker creates a \"colspan\", causing the current cell to merge with the one to the right.\n* The {{{~}}} marker creates a \"rowspan\", causing the current cell to merge with the one above.\n<<<\n!!Images /% TODO %/\ncf. [[TiddlyWiki.com|http://www.tiddlywiki.com/#EmbeddedImages]]\n\n!Hyperlinks\n* [[WikiWords|WikiWord]] are automatically transformed to hyperlinks to the respective tiddler\n** the automatic transformation can be suppressed by preceding the respective WikiWord with a tilde ({{{~}}}): {{{~WikiWord}}}\n* [[PrettyLinks]] are enclosed in square brackets and contain the desired tiddler name: {{{[[tiddler name]]}}}\n** optionally, a custom title or description can be added, separated by a pipe character ({{{|}}}): {{{[[title|target]]}}}<br>'''N.B.:''' In this case, the target can also be any website (i.e. URL).\n\n!Custom Styling\n* {{{@@CssProperty:value;CssProperty:value;...@@}}}<br>''N.B.:'' CSS color definitions should use lowercase letters to prevent the inadvertent creation of WikiWords.\n* <html><code>{{customCssClass{...}}}</code></html>\n* raw HTML can be inserted by enclosing the respective code in HTML tags: {{{<html> ... </html>}}}\n\n!Special Markers\n* {{{<br>}}} forces a manual line break\n* {{{----}}} creates a horizontal ruler\n* [[HTML entities|http://www.tiddlywiki.com/#HtmlEntities]]\n* [[HTML entities local|HtmlEntities]]\n* {{{<<macroName>>}}} calls the respective [[macro|Macros]]\n* To hide text within a tiddler so that it is not displayed, it can be wrapped in {{{/%}}} and {{{%/}}}.<br/>This can be a useful trick for hiding drafts or annotating complex markup.\n* To prevent wiki markup from taking effect for a particular section, that section can be enclosed in three double quotes: e.g. {{{\"\"\"WikiWord\"\"\"}}}.\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: 'tiddlywiki',      \n        lineNumbers: true,\n        matchBrackets: true\n      });\n    </script>\n\n    <p>TiddlyWiki mode supports a single configuration.</p>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-tiddlywiki</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/tiddlywiki/tiddlywiki.css",
    "content": "span.cm-underlined {\n  text-decoration: underline;\n}\nspan.cm-strikethrough {\n  text-decoration: line-through;\n}\nspan.cm-brace {\n  color: #170;\n  font-weight: bold;\n}\nspan.cm-table {\n  color: blue;\n  font-weight: bold;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/tiddlywiki/tiddlywiki.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/***\n    |''Name''|tiddlywiki.js|\n    |''Description''|Enables TiddlyWikiy syntax highlighting using CodeMirror|\n    |''Author''|PMario|\n    |''Version''|0.1.7|\n    |''Status''|''stable''|\n    |''Source''|[[GitHub|https://github.com/pmario/CodeMirror2/blob/tw-syntax/mode/tiddlywiki]]|\n    |''Documentation''|https://codemirror.tiddlyspace.com/|\n    |''License''|[[MIT License|http://www.opensource.org/licenses/mit-license.php]]|\n    |''CoreVersion''|2.5.0|\n    |''Requires''|codemirror.js|\n    |''Keywords''|syntax highlighting color code mirror codemirror|\n    ! Info\n    CoreVersion parameter is needed for TiddlyWiki only!\n***/\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"tiddlywiki\", function () {\n  // Tokenizer\n  var textwords = {};\n\n  var keywords = {\n    \"allTags\": true, \"closeAll\": true, \"list\": true,\n    \"newJournal\": true, \"newTiddler\": true,\n    \"permaview\": true, \"saveChanges\": true,\n    \"search\": true, \"slider\": true, \"tabs\": true,\n    \"tag\": true, \"tagging\": true, \"tags\": true,\n    \"tiddler\": true, \"timeline\": true,\n    \"today\": true, \"version\": true, \"option\": true,\n    \"with\": true, \"filter\": true\n  };\n\n  var isSpaceName = /[\\w_\\-]/i,\n      reHR = /^\\-\\-\\-\\-+$/,                                 // <hr>\n      reWikiCommentStart = /^\\/\\*\\*\\*$/,            // /***\n      reWikiCommentStop = /^\\*\\*\\*\\/$/,             // ***/\n      reBlockQuote = /^<<<$/,\n\n      reJsCodeStart = /^\\/\\/\\{\\{\\{$/,                       // //{{{ js block start\n      reJsCodeStop = /^\\/\\/\\}\\}\\}$/,                        // //}}} js stop\n      reXmlCodeStart = /^<!--\\{\\{\\{-->$/,           // xml block start\n      reXmlCodeStop = /^<!--\\}\\}\\}-->$/,            // xml stop\n\n      reCodeBlockStart = /^\\{\\{\\{$/,                        // {{{ TW text div block start\n      reCodeBlockStop = /^\\}\\}\\}$/,                 // }}} TW text stop\n\n      reUntilCodeStop = /.*?\\}\\}\\}/;\n\n  function chain(stream, state, f) {\n    state.tokenize = f;\n    return f(stream, state);\n  }\n\n  function tokenBase(stream, state) {\n    var sol = stream.sol(), ch = stream.peek();\n\n    state.block = false;        // indicates the start of a code block.\n\n    // check start of  blocks\n    if (sol && /[<\\/\\*{}\\-]/.test(ch)) {\n      if (stream.match(reCodeBlockStart)) {\n        state.block = true;\n        return chain(stream, state, twTokenCode);\n      }\n      if (stream.match(reBlockQuote))\n        return 'quote';\n      if (stream.match(reWikiCommentStart) || stream.match(reWikiCommentStop))\n        return 'comment';\n      if (stream.match(reJsCodeStart) || stream.match(reJsCodeStop) || stream.match(reXmlCodeStart) || stream.match(reXmlCodeStop))\n        return 'comment';\n      if (stream.match(reHR))\n        return 'hr';\n    }\n\n    stream.next();\n    if (sol && /[\\/\\*!#;:>|]/.test(ch)) {\n      if (ch == \"!\") { // tw header\n        stream.skipToEnd();\n        return \"header\";\n      }\n      if (ch == \"*\") { // tw list\n        stream.eatWhile('*');\n        return \"comment\";\n      }\n      if (ch == \"#\") { // tw numbered list\n        stream.eatWhile('#');\n        return \"comment\";\n      }\n      if (ch == \";\") { // definition list, term\n        stream.eatWhile(';');\n        return \"comment\";\n      }\n      if (ch == \":\") { // definition list, description\n        stream.eatWhile(':');\n        return \"comment\";\n      }\n      if (ch == \">\") { // single line quote\n        stream.eatWhile(\">\");\n        return \"quote\";\n      }\n      if (ch == '|')\n        return 'header';\n    }\n\n    if (ch == '{' && stream.match('{{'))\n      return chain(stream, state, twTokenCode);\n\n    // rudimentary html:// file:// link matching. TW knows much more ...\n    if (/[hf]/i.test(ch) &&\n        /[ti]/i.test(stream.peek()) &&\n        stream.match(/\\b(ttps?|tp|ile):\\/\\/[\\-A-Z0-9+&@#\\/%?=~_|$!:,.;]*[A-Z0-9+&@#\\/%=~_|$]/i))\n      return \"link\";\n\n    // just a little string indicator, don't want to have the whole string covered\n    if (ch == '\"')\n      return 'string';\n\n    if (ch == '~')    // _no_ CamelCase indicator should be bold\n      return 'brace';\n\n    if (/[\\[\\]]/.test(ch) && stream.match(ch)) // check for [[..]]\n      return 'brace';\n\n    if (ch == \"@\") {    // check for space link. TODO fix @@...@@ highlighting\n      stream.eatWhile(isSpaceName);\n      return \"link\";\n    }\n\n    if (/\\d/.test(ch)) {        // numbers\n      stream.eatWhile(/\\d/);\n      return \"number\";\n    }\n\n    if (ch == \"/\") { // tw invisible comment\n      if (stream.eat(\"%\")) {\n        return chain(stream, state, twTokenComment);\n      } else if (stream.eat(\"/\")) { //\n        return chain(stream, state, twTokenEm);\n      }\n    }\n\n    if (ch == \"_\" && stream.eat(\"_\")) // tw underline\n        return chain(stream, state, twTokenUnderline);\n\n    // strikethrough and mdash handling\n    if (ch == \"-\" && stream.eat(\"-\")) {\n      // if strikethrough looks ugly, change CSS.\n      if (stream.peek() != ' ')\n        return chain(stream, state, twTokenStrike);\n      // mdash\n      if (stream.peek() == ' ')\n        return 'brace';\n    }\n\n    if (ch == \"'\" && stream.eat(\"'\")) // tw bold\n      return chain(stream, state, twTokenStrong);\n\n    if (ch == \"<\" && stream.eat(\"<\")) // tw macro\n      return chain(stream, state, twTokenMacro);\n\n    // core macro handling\n    stream.eatWhile(/[\\w\\$_]/);\n    return textwords.propertyIsEnumerable(stream.current()) ? \"keyword\" : null\n  }\n\n  // tw invisible comment\n  function twTokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"%\");\n    }\n    return \"comment\";\n  }\n\n  // tw strong / bold\n  function twTokenStrong(stream, state) {\n    var maybeEnd = false,\n    ch;\n    while (ch = stream.next()) {\n      if (ch == \"'\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"'\");\n    }\n    return \"strong\";\n  }\n\n  // tw code\n  function twTokenCode(stream, state) {\n    var sb = state.block;\n\n    if (sb && stream.current()) {\n      return \"comment\";\n    }\n\n    if (!sb && stream.match(reUntilCodeStop)) {\n      state.tokenize = tokenBase;\n      return \"comment\";\n    }\n\n    if (sb && stream.sol() && stream.match(reCodeBlockStop)) {\n      state.tokenize = tokenBase;\n      return \"comment\";\n    }\n\n    stream.next();\n    return \"comment\";\n  }\n\n  // tw em / italic\n  function twTokenEm(stream, state) {\n    var maybeEnd = false,\n    ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"/\");\n    }\n    return \"em\";\n  }\n\n  // tw underlined text\n  function twTokenUnderline(stream, state) {\n    var maybeEnd = false,\n    ch;\n    while (ch = stream.next()) {\n      if (ch == \"_\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"_\");\n    }\n    return \"underlined\";\n  }\n\n  // tw strike through text looks ugly\n  // change CSS if needed\n  function twTokenStrike(stream, state) {\n    var maybeEnd = false, ch;\n\n    while (ch = stream.next()) {\n      if (ch == \"-\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"-\");\n    }\n    return \"strikethrough\";\n  }\n\n  // macro\n  function twTokenMacro(stream, state) {\n    if (stream.current() == '<<') {\n      return 'macro';\n    }\n\n    var ch = stream.next();\n    if (!ch) {\n      state.tokenize = tokenBase;\n      return null;\n    }\n    if (ch == \">\") {\n      if (stream.peek() == '>') {\n        stream.next();\n        state.tokenize = tokenBase;\n        return \"macro\";\n      }\n    }\n\n    stream.eatWhile(/[\\w\\$_]/);\n    return keywords.propertyIsEnumerable(stream.current()) ? \"keyword\" : null\n  }\n\n  // Interface\n  return {\n    startState: function () {\n      return {tokenize: tokenBase};\n    },\n\n    token: function (stream, state) {\n      if (stream.eatSpace()) return null;\n      var style = state.tokenize(stream, state);\n      return style;\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-tiddlywiki\", \"tiddlywiki\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/tiki/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Tiki wiki mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"tiki.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"tiki.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Tiki wiki</a>\n  </ul>\n</div>\n\n<article>\n<h2>Tiki wiki mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\nHeadings\n!Header 1\n!!Header 2\n!!!Header 3\n!!!!Header 4\n!!!!!Header 5\n!!!!!!Header 6\n\nStyling\n-=titlebar=-\n^^ Box on multi\nlines\nof content^^\n__bold__\n''italic''\n===underline===\n::center::\n--Line Through--\n\nOperators\n~np~No parse~/np~\n\nLink\n[link|desc|nocache]\n\nWiki\n((Wiki))\n((Wiki|desc))\n((Wiki|desc|timeout))\n\nTable\n||row1 col1|row1 col2|row1 col3\nrow2 col1|row2 col2|row2 col3\nrow3 col1|row3 col2|row3 col3||\n\nLists:\n*bla\n**bla-1\n++continue-bla-1\n***bla-2\n++continue-bla-1\n*bla\n+continue-bla\n#bla\n** tra-la-la\n+continue-bla\n#bla\n\nPlugin (standard):\n{PLUGIN(attr=\"my attr\")}\nPlugin Body\n{PLUGIN}\n\nPlugin (inline):\n{plugin attr=\"my attr\"}\n</textarea></div>\n\n<script>\n\tvar editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: 'tiki',      \n        lineNumbers: true\n    });\n</script>\n\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/tiki/tiki.css",
    "content": ".cm-tw-syntaxerror {\n\tcolor: #FFF;\n\tbackground-color: #900;\n}\n\n.cm-tw-deleted {\n\ttext-decoration: line-through;\n}\n\n.cm-tw-header5 {\n\tfont-weight: bold;\n}\n.cm-tw-listitem:first-child { /*Added first child to fix duplicate padding when highlighting*/\n\tpadding-left: 10px;\n}\n\n.cm-tw-box {\n\tborder-top-width: 0px !important;\n\tborder-style: solid;\n\tborder-width: 1px;\n\tborder-color: inherit;\n}\n\n.cm-tw-underline {\n\ttext-decoration: underline;\n}"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/tiki/tiki.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode('tiki', function(config) {\n  function inBlock(style, terminator, returnTokenizer) {\n    return function(stream, state) {\n      while (!stream.eol()) {\n        if (stream.match(terminator)) {\n          state.tokenize = inText;\n          break;\n        }\n        stream.next();\n      }\n\n      if (returnTokenizer) state.tokenize = returnTokenizer;\n\n      return style;\n    };\n  }\n\n  function inLine(style) {\n    return function(stream, state) {\n      while(!stream.eol()) {\n        stream.next();\n      }\n      state.tokenize = inText;\n      return style;\n    };\n  }\n\n  function inText(stream, state) {\n    function chain(parser) {\n      state.tokenize = parser;\n      return parser(stream, state);\n    }\n\n    var sol = stream.sol();\n    var ch = stream.next();\n\n    //non start of line\n    switch (ch) { //switch is generally much faster than if, so it is used here\n    case \"{\": //plugin\n      stream.eat(\"/\");\n      stream.eatSpace();\n      stream.eatWhile(/[^\\s\\u00a0=\\\"\\'\\/?(}]/);\n      state.tokenize = inPlugin;\n      return \"tag\";\n    case \"_\": //bold\n      if (stream.eat(\"_\"))\n        return chain(inBlock(\"strong\", \"__\", inText));\n      break;\n    case \"'\": //italics\n      if (stream.eat(\"'\"))\n        return chain(inBlock(\"em\", \"''\", inText));\n      break;\n    case \"(\":// Wiki Link\n      if (stream.eat(\"(\"))\n        return chain(inBlock(\"variable-2\", \"))\", inText));\n      break;\n    case \"[\":// Weblink\n      return chain(inBlock(\"variable-3\", \"]\", inText));\n      break;\n    case \"|\": //table\n      if (stream.eat(\"|\"))\n        return chain(inBlock(\"comment\", \"||\"));\n      break;\n    case \"-\":\n      if (stream.eat(\"=\")) {//titleBar\n        return chain(inBlock(\"header string\", \"=-\", inText));\n      } else if (stream.eat(\"-\")) {//deleted\n        return chain(inBlock(\"error tw-deleted\", \"--\", inText));\n      }\n      break;\n    case \"=\": //underline\n      if (stream.match(\"==\"))\n        return chain(inBlock(\"tw-underline\", \"===\", inText));\n      break;\n    case \":\":\n      if (stream.eat(\":\"))\n        return chain(inBlock(\"comment\", \"::\"));\n      break;\n    case \"^\": //box\n      return chain(inBlock(\"tw-box\", \"^\"));\n      break;\n    case \"~\": //np\n      if (stream.match(\"np~\"))\n        return chain(inBlock(\"meta\", \"~/np~\"));\n      break;\n    }\n\n    //start of line types\n    if (sol) {\n      switch (ch) {\n      case \"!\": //header at start of line\n        if (stream.match('!!!!!')) {\n          return chain(inLine(\"header string\"));\n        } else if (stream.match('!!!!')) {\n          return chain(inLine(\"header string\"));\n        } else if (stream.match('!!!')) {\n          return chain(inLine(\"header string\"));\n        } else if (stream.match('!!')) {\n          return chain(inLine(\"header string\"));\n        } else {\n          return chain(inLine(\"header string\"));\n        }\n        break;\n      case \"*\": //unordered list line item, or <li /> at start of line\n      case \"#\": //ordered list line item, or <li /> at start of line\n      case \"+\": //ordered list line item, or <li /> at start of line\n        return chain(inLine(\"tw-listitem bracket\"));\n        break;\n      }\n    }\n\n    //stream.eatWhile(/[&{]/); was eating up plugins, turned off to act less like html and more like tiki\n    return null;\n  }\n\n  var indentUnit = config.indentUnit;\n\n  // Return variables for tokenizers\n  var pluginName, type;\n  function inPlugin(stream, state) {\n    var ch = stream.next();\n    var peek = stream.peek();\n\n    if (ch == \"}\") {\n      state.tokenize = inText;\n      //type = ch == \")\" ? \"endPlugin\" : \"selfclosePlugin\"; inPlugin\n      return \"tag\";\n    } else if (ch == \"(\" || ch == \")\") {\n      return \"bracket\";\n    } else if (ch == \"=\") {\n      type = \"equals\";\n\n      if (peek == \">\") {\n        stream.next();\n        peek = stream.peek();\n      }\n\n      //here we detect values directly after equal character with no quotes\n      if (!/[\\'\\\"]/.test(peek)) {\n        state.tokenize = inAttributeNoQuote();\n      }\n      //end detect values\n\n      return \"operator\";\n    } else if (/[\\'\\\"]/.test(ch)) {\n      state.tokenize = inAttribute(ch);\n      return state.tokenize(stream, state);\n    } else {\n      stream.eatWhile(/[^\\s\\u00a0=\\\"\\'\\/?]/);\n      return \"keyword\";\n    }\n  }\n\n  function inAttribute(quote) {\n    return function(stream, state) {\n      while (!stream.eol()) {\n        if (stream.next() == quote) {\n          state.tokenize = inPlugin;\n          break;\n        }\n      }\n      return \"string\";\n    };\n  }\n\n  function inAttributeNoQuote() {\n    return function(stream, state) {\n      while (!stream.eol()) {\n        var ch = stream.next();\n        var peek = stream.peek();\n        if (ch == \" \" || ch == \",\" || /[ )}]/.test(peek)) {\n      state.tokenize = inPlugin;\n      break;\n    }\n  }\n  return \"string\";\n};\n                     }\n\nvar curState, setStyle;\nfunction pass() {\n  for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);\n}\n\nfunction cont() {\n  pass.apply(null, arguments);\n  return true;\n}\n\nfunction pushContext(pluginName, startOfLine) {\n  var noIndent = curState.context && curState.context.noIndent;\n  curState.context = {\n    prev: curState.context,\n    pluginName: pluginName,\n    indent: curState.indented,\n    startOfLine: startOfLine,\n    noIndent: noIndent\n  };\n}\n\nfunction popContext() {\n  if (curState.context) curState.context = curState.context.prev;\n}\n\nfunction element(type) {\n  if (type == \"openPlugin\") {curState.pluginName = pluginName; return cont(attributes, endplugin(curState.startOfLine));}\n  else if (type == \"closePlugin\") {\n    var err = false;\n    if (curState.context) {\n      err = curState.context.pluginName != pluginName;\n      popContext();\n    } else {\n      err = true;\n    }\n    if (err) setStyle = \"error\";\n    return cont(endcloseplugin(err));\n  }\n  else if (type == \"string\") {\n    if (!curState.context || curState.context.name != \"!cdata\") pushContext(\"!cdata\");\n    if (curState.tokenize == inText) popContext();\n    return cont();\n  }\n  else return cont();\n}\n\nfunction endplugin(startOfLine) {\n  return function(type) {\n    if (\n      type == \"selfclosePlugin\" ||\n        type == \"endPlugin\"\n    )\n      return cont();\n    if (type == \"endPlugin\") {pushContext(curState.pluginName, startOfLine); return cont();}\n    return cont();\n  };\n}\n\nfunction endcloseplugin(err) {\n  return function(type) {\n    if (err) setStyle = \"error\";\n    if (type == \"endPlugin\") return cont();\n    return pass();\n  };\n}\n\nfunction attributes(type) {\n  if (type == \"keyword\") {setStyle = \"attribute\"; return cont(attributes);}\n  if (type == \"equals\") return cont(attvalue, attributes);\n  return pass();\n}\nfunction attvalue(type) {\n  if (type == \"keyword\") {setStyle = \"string\"; return cont();}\n  if (type == \"string\") return cont(attvaluemaybe);\n  return pass();\n}\nfunction attvaluemaybe(type) {\n  if (type == \"string\") return cont(attvaluemaybe);\n  else return pass();\n}\nreturn {\n  startState: function() {\n    return {tokenize: inText, cc: [], indented: 0, startOfLine: true, pluginName: null, context: null};\n  },\n  token: function(stream, state) {\n    if (stream.sol()) {\n      state.startOfLine = true;\n      state.indented = stream.indentation();\n    }\n    if (stream.eatSpace()) return null;\n\n    setStyle = type = pluginName = null;\n    var style = state.tokenize(stream, state);\n    if ((style || type) && style != \"comment\") {\n      curState = state;\n      while (true) {\n        var comb = state.cc.pop() || element;\n        if (comb(type || style)) break;\n      }\n    }\n    state.startOfLine = false;\n    return setStyle || style;\n  },\n  indent: function(state, textAfter) {\n    var context = state.context;\n    if (context && context.noIndent) return 0;\n    if (context && /^{\\//.test(textAfter))\n        context = context.prev;\n    while (context && !context.startOfLine)\n        context = context.prev;\n    if (context) return context.indent + indentUnit;\n    else return 0;\n  },\n  electricChars: \"/\"\n};\n});\n\nCodeMirror.defineMIME(\"text/tiki\", \"tiki\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/toml/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: TOML Mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"toml.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">TOML Mode</a>\n  </ul>\n</div>\n\n<article>\n<h2>TOML Mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n# This is a TOML document. Boom.\n\ntitle = \"TOML Example\"\n\n[owner]\nname = \"Tom Preston-Werner\"\norganization = \"GitHub\"\nbio = \"GitHub Cofounder &amp; CEO\\nLikes tater tots and beer.\"\ndob = 1979-05-27T07:32:00Z # First class dates? Why not?\n\n[database]\nserver = \"192.168.1.1\"\nports = [ 8001, 8001, 8002 ]\nconnection_max = 5000\nenabled = true\n\n[servers]\n\n  # You can indent as you please. Tabs or spaces. TOML don't care.\n  [servers.alpha]\n  ip = \"10.0.0.1\"\n  dc = \"eqdc10\"\n  \n  [servers.beta]\n  ip = \"10.0.0.2\"\n  dc = \"eqdc10\"\n  \n[clients]\ndata = [ [\"gamma\", \"delta\"], [1, 2] ]\n\n# Line breaks are OK when inside arrays\nhosts = [\n  \"alpha\",\n  \"omega\"\n]\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: {name: \"toml\"},\n        lineNumbers: true\n      });\n    </script>\n    <h3>The TOML Mode</h3>\n      <p> Created by Forbes Lindesay.</p>\n    <p><strong>MIME type defined:</strong> <code>text/x-toml</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/toml/toml.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"toml\", function () {\n  return {\n    startState: function () {\n      return {\n        inString: false,\n        stringType: \"\",\n        lhs: true,\n        inArray: 0\n      };\n    },\n    token: function (stream, state) {\n      //check for state changes\n      if (!state.inString && ((stream.peek() == '\"') || (stream.peek() == \"'\"))) {\n        state.stringType = stream.peek();\n        stream.next(); // Skip quote\n        state.inString = true; // Update state\n      }\n      if (stream.sol() && state.inArray === 0) {\n        state.lhs = true;\n      }\n      //return state\n      if (state.inString) {\n        while (state.inString && !stream.eol()) {\n          if (stream.peek() === state.stringType) {\n            stream.next(); // Skip quote\n            state.inString = false; // Clear flag\n          } else if (stream.peek() === '\\\\') {\n            stream.next();\n            stream.next();\n          } else {\n            stream.match(/^.[^\\\\\\\"\\']*/);\n          }\n        }\n        return state.lhs ? \"property string\" : \"string\"; // Token style\n      } else if (state.inArray && stream.peek() === ']') {\n        stream.next();\n        state.inArray--;\n        return 'bracket';\n      } else if (state.lhs && stream.peek() === '[' && stream.skipTo(']')) {\n        stream.next();//skip closing ]\n        // array of objects has an extra open & close []\n        if (stream.peek() === ']') stream.next();\n        return \"atom\";\n      } else if (stream.peek() === \"#\") {\n        stream.skipToEnd();\n        return \"comment\";\n      } else if (stream.eatSpace()) {\n        return null;\n      } else if (state.lhs && stream.eatWhile(function (c) { return c != '=' && c != ' '; })) {\n        return \"property\";\n      } else if (state.lhs && stream.peek() === \"=\") {\n        stream.next();\n        state.lhs = false;\n        return null;\n      } else if (!state.lhs && stream.match(/^\\d\\d\\d\\d[\\d\\-\\:\\.T]*Z/)) {\n        return 'atom'; //date\n      } else if (!state.lhs && (stream.match('true') || stream.match('false'))) {\n        return 'atom';\n      } else if (!state.lhs && stream.peek() === '[') {\n        state.inArray++;\n        stream.next();\n        return 'bracket';\n      } else if (!state.lhs && stream.match(/^\\-?\\d+(?:\\.\\d+)?/)) {\n        return 'number';\n      } else if (!stream.eatSpace()) {\n        stream.next();\n      }\n      return null;\n    }\n  };\n});\n\nCodeMirror.defineMIME('text/x-toml', 'toml');\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/tornado/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Tornado template mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/mode/overlay.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../htmlmixed/htmlmixed.js\"></script>\n<script src=\"tornado.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/marijnh/codemirror\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Tornado</a>\n  </ul>\n</div>\n\n<article>\n<h2>Tornado template mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n<!doctype html>\n<html>\n    <head>\n        <title>My Tornado web application</title>\n    </head>\n    <body>\n        <h1>\n            {{ title }}\n        </h1>\n        <ul class=\"my-list\">\n            {% for item in items %}\n                <li>{% item.name %}</li>\n            {% empty %}\n                <li>You have no items in your list.</li>\n            {% end %}\n        </ul>\n    </body>\n</html>\n</textarea></form>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        mode: \"tornado\",\n        indentUnit: 4,\n        indentWithTabs: true\n      });\n    </script>\n\n    <p>Mode for HTML with embedded Tornado template markup.</p>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-tornado</code></p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/tornado/tornado.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../htmlmixed/htmlmixed\"),\n        require(\"../../addon/mode/overlay\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../htmlmixed/htmlmixed\",\n            \"../../addon/mode/overlay\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"tornado:inner\", function() {\n    var keywords = [\"and\",\"as\",\"assert\",\"autoescape\",\"block\",\"break\",\"class\",\"comment\",\"context\",\n                    \"continue\",\"datetime\",\"def\",\"del\",\"elif\",\"else\",\"end\",\"escape\",\"except\",\n                    \"exec\",\"extends\",\"false\",\"finally\",\"for\",\"from\",\"global\",\"if\",\"import\",\"in\",\n                    \"include\",\"is\",\"json_encode\",\"lambda\",\"length\",\"linkify\",\"load\",\"module\",\n                    \"none\",\"not\",\"or\",\"pass\",\"print\",\"put\",\"raise\",\"raw\",\"return\",\"self\",\"set\",\n                    \"squeeze\",\"super\",\"true\",\"try\",\"url_escape\",\"while\",\"with\",\"without\",\"xhtml_escape\",\"yield\"];\n    keywords = new RegExp(\"^((\" + keywords.join(\")|(\") + \"))\\\\b\");\n\n    function tokenBase (stream, state) {\n      stream.eatWhile(/[^\\{]/);\n      var ch = stream.next();\n      if (ch == \"{\") {\n        if (ch = stream.eat(/\\{|%|#/)) {\n          state.tokenize = inTag(ch);\n          return \"tag\";\n        }\n      }\n    }\n    function inTag (close) {\n      if (close == \"{\") {\n        close = \"}\";\n      }\n      return function (stream, state) {\n        var ch = stream.next();\n        if ((ch == close) && stream.eat(\"}\")) {\n          state.tokenize = tokenBase;\n          return \"tag\";\n        }\n        if (stream.match(keywords)) {\n          return \"keyword\";\n        }\n        return close == \"#\" ? \"comment\" : \"string\";\n      };\n    }\n    return {\n      startState: function () {\n        return {tokenize: tokenBase};\n      },\n      token: function (stream, state) {\n        return state.tokenize(stream, state);\n      }\n    };\n  });\n\n  CodeMirror.defineMode(\"tornado\", function(config) {\n    var htmlBase = CodeMirror.getMode(config, \"text/html\");\n    var tornadoInner = CodeMirror.getMode(config, \"tornado:inner\");\n    return CodeMirror.overlayMode(htmlBase, tornadoInner);\n  });\n\n  CodeMirror.defineMIME(\"text/x-tornado\", \"tornado\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/troff/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: troff mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=stylesheet href=../../lib/codemirror.css>\n<script src=../../lib/codemirror.js></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=troff.js></script>\n<style type=text/css>\n  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">troff</a>\n  </ul>\n</div>\n\n<article>\n<h2>troff</h2>\n\n\n<textarea id=code>\n'\\\" t\n.\\\"     Title: mkvextract\n.TH \"MKVEXTRACT\" \"1\" \"2015\\-02\\-28\" \"MKVToolNix 7\\&.7\\&.0\" \"User Commands\"\n.\\\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n.ie \\n(.g .ds Aq \\(aq\n.el       .ds Aq '\n.\\\" -----------------------------------------------------------------\n.nh\n.\\\" disable justification (adjust text to left margin only)\n.ad l\n.\\\" -----------------------------------------------------------------\n.SH \"NAME\"\nmkvextract \\- extract tracks from Matroska(TM) files into other files\n.SH \"SYNOPSIS\"\n.HP \\w'\\fBmkvextract\\fR\\ 'u\n\\fBmkvextract\\fR {mode} {source\\-filename} [options] [extraction\\-spec]\n.SH \"DESCRIPTION\"\n.PP\n.B mkvextract\nextracts specific parts from a\n.I Matroska(TM)\nfile to other useful formats\\&. The first argument,\n\\fBmode\\fR, tells\n\\fBmkvextract\\fR(1)\nwhat to extract\\&. Currently supported is the extraction of\ntracks,\ntags,\nattachments,\nchapters,\nCUE sheets,\ntimecodes\nand\ncues\\&. The second argument is the name of the source file\\&. It must be a\nMatroska(TM)\nfile\\&. All following arguments are options and extraction specifications; both of which depend on the selected mode\\&.\n.SS \"Common options\"\n.PP\nThe following options are available in all modes and only described once in this section\\&.\n.PP\n\\fB\\-f\\fR, \\fB\\-\\-parse\\-fully\\fR\n.RS 4\nSets the parse mode to \\*(Aqfull\\*(Aq\\&. The default mode does not parse the whole file but uses the meta seek elements for locating the required elements of a source file\\&. In 99% of all cases this is enough\\&. But for files that do not contain meta seek elements or which are damaged the user might have to use this mode\\&. A full scan of a file can take a couple of minutes while a fast scan only takes seconds\\&.\n.RE\n.PP\n\\fB\\-\\-command\\-line\\-charset\\fR \\fIcharacter\\-set\\fR\n.RS 4\nSets the character set to convert strings given on the command line from\\&. It defaults to the character set given by system\\*(Aqs current locale\\&.\n.RE\n.PP\n\\fB\\-\\-output\\-charset\\fR \\fIcharacter\\-set\\fR\n.RS 4\nSets the character set to which strings are converted that are to be output\\&. It defaults to the character set given by system\\*(Aqs current locale\\&.\n.RE\n.PP\n\\fB\\-r\\fR, \\fB\\-\\-redirect\\-output\\fR \\fIfile\\-name\\fR\n.RS 4\nWrites all messages to the file\n\\fIfile\\-name\\fR\ninstead of to the console\\&. While this can be done easily with output redirection there are cases in which this option is needed: when the terminal reinterprets the output before writing it to a file\\&. The character set set with\n\\fB\\-\\-output\\-charset\\fR\nis honored\\&.\n.RE\n.PP\n\\fB\\-\\-ui\\-language\\fR \\fIcode\\fR\n.RS 4\nForces the translations for the language\n\\fIcode\\fR\nto be used (e\\&.g\\&. \\*(Aqde_DE\\*(Aq for the German translations)\\&. It is preferable to use the environment variables\n\\fILANG\\fR,\n\\fILC_MESSAGES\\fR\nand\n\\fILC_ALL\\fR\nthough\\&. Entering \\*(Aqlist\\*(Aq as the\n\\fIcode\\fR\nwill cause\n\\fBmkvextract\\fR(1)\nto output a list of available translations\\&.\n\n.\\\" [...]\n\n.SH \"SEE ALSO\"\n.PP\n\\fBmkvmerge\\fR(1),\n\\fBmkvinfo\\fR(1),\n\\fBmkvpropedit\\fR(1),\n\\fBmmg\\fR(1)\n.SH \"WWW\"\n.PP\nThe latest version can always be found at\n\\m[blue]\\fBthe MKVToolNix homepage\\fR\\m[]\\&\\s-2\\u[1]\\d\\s+2\\&.\n.SH \"AUTHOR\"\n.PP\n\\(co \\fBMoritz Bunkus\\fR <\\&moritz@bunkus\\&.org\\&>\n.RS 4\nDeveloper\n.RE\n.SH \"NOTES\"\n.IP \" 1.\" 4\nthe MKVToolNix homepage\n.RS 4\n\\%https://www.bunkus.org/videotools/mkvtoolnix/\n.RE\n</textarea>\n\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById('code'), {\n    mode: 'troff',\n    lineNumbers: true,\n    matchBrackets: false\n  });\n</script>\n\n<p><strong>MIME types defined:</strong> <code>troff</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/troff/troff.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\")\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd)\n    define([\"../../lib/codemirror\"], mod);\n  else\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode('troff', function() {\n\n  var words = {};\n\n  function tokenBase(stream) {\n    if (stream.eatSpace()) return null;\n\n    var sol = stream.sol();\n    var ch = stream.next();\n\n    if (ch === '\\\\') {\n      if (stream.match('fB') || stream.match('fR') || stream.match('fI') ||\n          stream.match('u')  || stream.match('d')  ||\n          stream.match('%')  || stream.match('&')) {\n        return 'string';\n      }\n      if (stream.match('m[')) {\n        stream.skipTo(']');\n        stream.next();\n        return 'string';\n      }\n      if (stream.match('s+') || stream.match('s-')) {\n        stream.eatWhile(/[\\d-]/);\n        return 'string';\n      }\n      if (stream.match('\\(') || stream.match('*\\(')) {\n        stream.eatWhile(/[\\w-]/);\n        return 'string';\n      }\n      return 'string';\n    }\n    if (sol && (ch === '.' || ch === '\\'')) {\n      if (stream.eat('\\\\') && stream.eat('\\\"')) {\n        stream.skipToEnd();\n        return 'comment';\n      }\n    }\n    if (sol && ch === '.') {\n      if (stream.match('B ') || stream.match('I ') || stream.match('R ')) {\n        return 'attribute';\n      }\n      if (stream.match('TH ') || stream.match('SH ') || stream.match('SS ') || stream.match('HP ')) {\n        stream.skipToEnd();\n        return 'quote';\n      }\n      if ((stream.match(/[A-Z]/) && stream.match(/[A-Z]/)) || (stream.match(/[a-z]/) && stream.match(/[a-z]/))) {\n        return 'attribute';\n      }\n    }\n    stream.eatWhile(/[\\w-]/);\n    var cur = stream.current();\n    return words.hasOwnProperty(cur) ? words[cur] : null;\n  }\n\n  function tokenize(stream, state) {\n    return (state.tokens[0] || tokenBase) (stream, state);\n  };\n\n  return {\n    startState: function() {return {tokens:[]};},\n    token: function(stream, state) {\n      return tokenize(stream, state);\n    }\n  };\n});\n\nCodeMirror.defineMIME('text/troff', 'troff');\nCodeMirror.defineMIME('text/x-troff', 'troff');\nCodeMirror.defineMIME('application/x-troff', 'troff');\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ttcn/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: TTCN mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"ttcn.js\"></script>\n<style>\n    .CodeMirror {\n        border-top: 1px solid black;\n        border-bottom: 1px solid black;\n    }\n</style>\n<div id=nav>\n    <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1>\n        <img id=logo src=\"../../doc/logo.png\" alt=\"\">\n    </a>\n\n    <ul>\n        <li><a href=\"../../index.html\">Home</a>\n        <li><a href=\"../../doc/manual.html\">Manual</a>\n        <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n    </ul>\n    <ul>\n        <li><a href=\"../index.html\">Language modes</a>\n        <li><a class=active href=\"http://en.wikipedia.org/wiki/TTCN\">TTCN</a>\n    </ul>\n</div>\n<article>\n    <h2>TTCN example</h2>\n    <div>\n        <textarea id=\"ttcn-code\">\nmodule Templates {\n  /* import types from ASN.1 */\n  import from Types language \"ASN.1:1997\" all;\n\n  /* During the conversion phase from ASN.1 to TTCN-3 */\n  /* - the minus sign (Message-Type) within the identifiers will be replaced by underscore (Message_Type)*/\n  /* - the ASN.1 identifiers matching a TTCN-3 keyword (objid) will be postfixed with an underscore (objid_)*/\n\n  // simple types\n\n  template SenderID localObjid := objid {itu_t(0) identified_organization(4) etsi(0)};\n\n  // complex types\n\n  /* ASN.1 Message-Type mapped to TTCN-3 Message_Type */\n  template Message receiveMsg(template (present) Message_Type p_messageType) := {\n    header := p_messageType,\n    body := ?\n  }\n\n  /* ASN.1 objid mapped to TTCN-3 objid_ */\n  template Message sendInviteMsg := {\n      header := inviteType,\n      body := {\n        /* optional fields may be assigned by omit or may be ignored/skipped */\n        description := \"Invite Message\",\n        data := 'FF'O,\n        objid_ := localObjid\n      }\n  }\n\n  template Message sendAcceptMsg modifies sendInviteMsg := {\n      header := acceptType,\n      body := {\n        description := \"Accept Message\"\n      }\n    };\n\n  template Message sendErrorMsg modifies sendInviteMsg := {\n      header := errorType,\n      body := {\n        description := \"Error Message\"\n      }\n    };\n\n  template Message expectedErrorMsg := {\n      header := errorType,\n      body := ?\n    };\n\n  template Message expectedInviteMsg modifies expectedErrorMsg := {\n      header := inviteType\n    };\n\n  template Message expectedAcceptMsg modifies expectedErrorMsg := {\n      header := acceptType\n    };\n\n} with { encode \"BER:1997\" }\n        </textarea>\n    </div>\n\n    <script> \n      var ttcnEditor = CodeMirror.fromTextArea(document.getElementById(\"ttcn-code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-ttcn\"\n      });\n      ttcnEditor.setSize(600, 860);\n      var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;\n      CodeMirror.keyMap.default[(mac ? \"Cmd\" : \"Ctrl\") + \"-Space\"] = \"autocomplete\";\n    </script>\n    <br/>\n    <p><strong>Language:</strong> Testing and Test Control Notation\n        (<a href=\"http://en.wikipedia.org/wiki/TTCN\">TTCN</a>)\n    </p>\n    <p><strong>MIME types defined:</strong> <code>text/x-ttcn,\n        text/x-ttcn3, text/x-ttcnpp</code>.</p>\n    <br/>\n    <p>The development of this mode has been sponsored by <a href=\"http://www.ericsson.com/\">Ericsson\n    </a>.</p>\n    <p>Coded by Asmelash Tsegay Gebretsadkan </p>\n</article>\n\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ttcn/ttcn.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"ttcn\", function(config, parserConfig) {\n    var indentUnit = config.indentUnit,\n        keywords = parserConfig.keywords || {},\n        builtin = parserConfig.builtin || {},\n        timerOps = parserConfig.timerOps || {},\n        portOps  = parserConfig.portOps || {},\n        configOps = parserConfig.configOps || {},\n        verdictOps = parserConfig.verdictOps || {},\n        sutOps = parserConfig.sutOps || {},\n        functionOps = parserConfig.functionOps || {},\n\n        verdictConsts = parserConfig.verdictConsts || {},\n        booleanConsts = parserConfig.booleanConsts || {},\n        otherConsts   = parserConfig.otherConsts || {},\n\n        types = parserConfig.types || {},\n        visibilityModifiers = parserConfig.visibilityModifiers || {},\n        templateMatch = parserConfig.templateMatch || {},\n        multiLineStrings = parserConfig.multiLineStrings,\n        indentStatements = parserConfig.indentStatements !== false;\n    var isOperatorChar = /[+\\-*&@=<>!\\/]/;\n    var curPunc;\n\n    function tokenBase(stream, state) {\n      var ch = stream.next();\n\n      if (ch == '\"' || ch == \"'\") {\n        state.tokenize = tokenString(ch);\n        return state.tokenize(stream, state);\n      }\n      if (/[\\[\\]{}\\(\\),;\\\\:\\?\\.]/.test(ch)) {\n        curPunc = ch;\n        return \"punctuation\";\n      }\n      if (ch == \"#\"){\n        stream.skipToEnd();\n        return \"atom preprocessor\";\n      }\n      if (ch == \"%\"){\n        stream.eatWhile(/\\b/);\n        return \"atom ttcn3Macros\";\n      }\n      if (/\\d/.test(ch)) {\n        stream.eatWhile(/[\\w\\.]/);\n        return \"number\";\n      }\n      if (ch == \"/\") {\n        if (stream.eat(\"*\")) {\n          state.tokenize = tokenComment;\n          return tokenComment(stream, state);\n        }\n        if (stream.eat(\"/\")) {\n          stream.skipToEnd();\n          return \"comment\";\n        }\n      }\n      if (isOperatorChar.test(ch)) {\n        if(ch == \"@\"){\n          if(stream.match(\"try\") || stream.match(\"catch\")\n              || stream.match(\"lazy\")){\n            return \"keyword\";\n          }\n        }\n        stream.eatWhile(isOperatorChar);\n        return \"operator\";\n      }\n      stream.eatWhile(/[\\w\\$_\\xa1-\\uffff]/);\n      var cur = stream.current();\n\n      if (keywords.propertyIsEnumerable(cur)) return \"keyword\";\n      if (builtin.propertyIsEnumerable(cur)) return \"builtin\";\n\n      if (timerOps.propertyIsEnumerable(cur)) return \"def timerOps\";\n      if (configOps.propertyIsEnumerable(cur)) return \"def configOps\";\n      if (verdictOps.propertyIsEnumerable(cur)) return \"def verdictOps\";\n      if (portOps.propertyIsEnumerable(cur)) return \"def portOps\";\n      if (sutOps.propertyIsEnumerable(cur)) return \"def sutOps\";\n      if (functionOps.propertyIsEnumerable(cur)) return \"def functionOps\";\n\n      if (verdictConsts.propertyIsEnumerable(cur)) return \"string verdictConsts\";\n      if (booleanConsts.propertyIsEnumerable(cur)) return \"string booleanConsts\";\n      if (otherConsts.propertyIsEnumerable(cur)) return \"string otherConsts\";\n\n      if (types.propertyIsEnumerable(cur)) return \"builtin types\";\n      if (visibilityModifiers.propertyIsEnumerable(cur))\n        return \"builtin visibilityModifiers\";\n      if (templateMatch.propertyIsEnumerable(cur)) return \"atom templateMatch\";\n\n      return \"variable\";\n    }\n\n    function tokenString(quote) {\n      return function(stream, state) {\n        var escaped = false, next, end = false;\n        while ((next = stream.next()) != null) {\n          if (next == quote && !escaped){\n            var afterQuote = stream.peek();\n            //look if the character after the quote is like the B in '10100010'B\n            if (afterQuote){\n              afterQuote = afterQuote.toLowerCase();\n              if(afterQuote == \"b\" || afterQuote == \"h\" || afterQuote == \"o\")\n                stream.next();\n            }\n            end = true; break;\n          }\n          escaped = !escaped && next == \"\\\\\";\n        }\n        if (end || !(escaped || multiLineStrings))\n          state.tokenize = null;\n        return \"string\";\n      };\n    }\n\n    function tokenComment(stream, state) {\n      var maybeEnd = false, ch;\n      while (ch = stream.next()) {\n        if (ch == \"/\" && maybeEnd) {\n          state.tokenize = null;\n          break;\n        }\n        maybeEnd = (ch == \"*\");\n      }\n      return \"comment\";\n    }\n\n    function Context(indented, column, type, align, prev) {\n      this.indented = indented;\n      this.column = column;\n      this.type = type;\n      this.align = align;\n      this.prev = prev;\n    }\n\n    function pushContext(state, col, type) {\n      var indent = state.indented;\n      if (state.context && state.context.type == \"statement\")\n        indent = state.context.indented;\n      return state.context = new Context(indent, col, type, null, state.context);\n    }\n\n    function popContext(state) {\n      var t = state.context.type;\n      if (t == \")\" || t == \"]\" || t == \"}\")\n        state.indented = state.context.indented;\n      return state.context = state.context.prev;\n    }\n\n    //Interface\n    return {\n      startState: function(basecolumn) {\n        return {\n          tokenize: null,\n          context: new Context((basecolumn || 0) - indentUnit, 0, \"top\", false),\n          indented: 0,\n          startOfLine: true\n        };\n      },\n\n      token: function(stream, state) {\n        var ctx = state.context;\n        if (stream.sol()) {\n          if (ctx.align == null) ctx.align = false;\n          state.indented = stream.indentation();\n          state.startOfLine = true;\n        }\n        if (stream.eatSpace()) return null;\n        curPunc = null;\n        var style = (state.tokenize || tokenBase)(stream, state);\n        if (style == \"comment\") return style;\n        if (ctx.align == null) ctx.align = true;\n\n        if ((curPunc == \";\" || curPunc == \":\" || curPunc == \",\")\n            && ctx.type == \"statement\"){\n          popContext(state);\n        }\n        else if (curPunc == \"{\") pushContext(state, stream.column(), \"}\");\n        else if (curPunc == \"[\") pushContext(state, stream.column(), \"]\");\n        else if (curPunc == \"(\") pushContext(state, stream.column(), \")\");\n        else if (curPunc == \"}\") {\n          while (ctx.type == \"statement\") ctx = popContext(state);\n          if (ctx.type == \"}\") ctx = popContext(state);\n          while (ctx.type == \"statement\") ctx = popContext(state);\n        }\n        else if (curPunc == ctx.type) popContext(state);\n        else if (indentStatements &&\n            (((ctx.type == \"}\" || ctx.type == \"top\") && curPunc != ';') ||\n            (ctx.type == \"statement\" && curPunc == \"newstatement\")))\n          pushContext(state, stream.column(), \"statement\");\n\n        state.startOfLine = false;\n\n        return style;\n      },\n\n      electricChars: \"{}\",\n      blockCommentStart: \"/*\",\n      blockCommentEnd: \"*/\",\n      lineComment: \"//\",\n      fold: \"brace\"\n    };\n  });\n\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  function def(mimes, mode) {\n    if (typeof mimes == \"string\") mimes = [mimes];\n    var words = [];\n    function add(obj) {\n      if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))\n        words.push(prop);\n    }\n\n    add(mode.keywords);\n    add(mode.builtin);\n    add(mode.timerOps);\n    add(mode.portOps);\n\n    if (words.length) {\n      mode.helperType = mimes[0];\n      CodeMirror.registerHelper(\"hintWords\", mimes[0], words);\n    }\n\n    for (var i = 0; i < mimes.length; ++i)\n      CodeMirror.defineMIME(mimes[i], mode);\n  }\n\n  def([\"text/x-ttcn\", \"text/x-ttcn3\", \"text/x-ttcnpp\"], {\n    name: \"ttcn\",\n    keywords: words(\"activate address alive all alt altstep and and4b any\" +\n    \" break case component const continue control deactivate\" +\n    \" display do else encode enumerated except exception\" +\n    \" execute extends extension external for from function\" +\n    \" goto group if import in infinity inout interleave\" +\n    \" label language length log match message mixed mod\" +\n    \" modifies module modulepar mtc noblock not not4b nowait\" +\n    \" of on optional or or4b out override param pattern port\" +\n    \" procedure record recursive rem repeat return runs select\" +\n    \" self sender set signature system template testcase to\" +\n    \" type union value valueof var variant while with xor xor4b\"),\n    builtin: words(\"bit2hex bit2int bit2oct bit2str char2int char2oct encvalue\" +\n    \" decomp decvalue float2int float2str hex2bit hex2int\" +\n    \" hex2oct hex2str int2bit int2char int2float int2hex\" +\n    \" int2oct int2str int2unichar isbound ischosen ispresent\" +\n    \" isvalue lengthof log2str oct2bit oct2char oct2hex oct2int\" +\n    \" oct2str regexp replace rnd sizeof str2bit str2float\" +\n    \" str2hex str2int str2oct substr unichar2int unichar2char\" +\n    \" enum2int\"),\n    types: words(\"anytype bitstring boolean char charstring default float\" +\n    \" hexstring integer objid octetstring universal verdicttype timer\"),\n    timerOps: words(\"read running start stop timeout\"),\n    portOps: words(\"call catch check clear getcall getreply halt raise receive\" +\n    \" reply send trigger\"),\n    configOps: words(\"create connect disconnect done kill killed map unmap\"),\n    verdictOps: words(\"getverdict setverdict\"),\n    sutOps: words(\"action\"),\n    functionOps: words(\"apply derefers refers\"),\n\n    verdictConsts: words(\"error fail inconc none pass\"),\n    booleanConsts: words(\"true false\"),\n    otherConsts: words(\"null NULL omit\"),\n\n    visibilityModifiers: words(\"private public friend\"),\n    templateMatch: words(\"complement ifpresent subset superset permutation\"),\n    multiLineStrings: true\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ttcn-cfg/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: TTCN-CFG mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"ttcn-cfg.js\"></script>\n<style>\n    .CodeMirror {\n        border-top: 1px solid black;\n        border-bottom: 1px solid black;\n    }\n</style>\n<div id=nav>\n    <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1>\n        <img id=logo src=\"../../doc/logo.png\" alt=\"\">\n    </a>\n\n    <ul>\n        <li><a href=\"../../index.html\">Home</a>\n        <li><a href=\"../../doc/manual.html\">Manual</a>\n        <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n    </ul>\n    <ul>\n        <li><a href=\"../index.html\">Language modes</a>\n        <li><a class=active href=\"http://en.wikipedia.org/wiki/TTCN\">TTCN-CFG</a>\n    </ul>\n</div>\n<article>\n    <h2>TTCN-CFG example</h2>\n    <div>\n        <textarea id=\"ttcn-cfg-code\">\n[MODULE_PARAMETERS]\n# This section shall contain the values of all parameters that are defined in your TTCN-3 modules.\n\n[LOGGING]\n# In this section you can specify the name of the log file and the classes of events\n# you want to log into the file or display on console (standard error).\n\nLogFile := \"logs/%e.%h-%r.%s\"\nFileMask := LOG_ALL | DEBUG | MATCHING\nConsoleMask := ERROR | WARNING | TESTCASE | STATISTICS | PORTEVENT\n\nLogSourceInfo := Yes\nAppendFile := No\nTimeStampFormat := DateTime\nLogEventTypes := Yes\nSourceInfoFormat := Single\nLogEntityName := Yes\n\n[TESTPORT_PARAMETERS]\n# In this section you can specify parameters that are passed to Test Ports.\n\n[DEFINE]\n# In this section you can create macro definitions,\n# that can be used in other configuration file sections except [INCLUDE].\n\n[INCLUDE]\n# To use configuration settings given in other configuration files,\n# the configuration files just need to be listed in this section, with their full or relative pathnames.\n\n[EXTERNAL_COMMANDS]\n# This section can define external commands (shell scripts) to be executed by the ETS\n# whenever a control part or test case is started or terminated.\n\nBeginTestCase := \"\"\nEndTestCase := \"\"\nBeginControlPart := \"\"\nEndControlPart := \"\"\n\n[EXECUTE]\n# In this section you can specify what parts of your test suite you want to execute.\n\n[GROUPS]\n# In this section you can specify groups of hosts. These groups can be used inside the\n# [COMPONENTS] section to restrict the creation of certain PTCs to a given set of hosts.\n\n[COMPONENTS]\n# This section consists of rules restricting the location of created PTCs.\n\n[MAIN_CONTROLLER]\n# The options herein control the behavior of MC.\n\nTCPPort := 0\nKillTimer := 10.0\nNumHCs := 0\nLocalAddress :=\n        </textarea>\n    </div>\n\n    <script> \n      var ttcnEditor = CodeMirror.fromTextArea(document.getElementById(\"ttcn-cfg-code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/x-ttcn-cfg\"\n      });\n      ttcnEditor.setSize(600, 860);\n      var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;\n      CodeMirror.keyMap.default[(mac ? \"Cmd\" : \"Ctrl\") + \"-Space\"] = \"autocomplete\";\n    </script>\n    <br/>\n    <p><strong>Language:</strong> Testing and Test Control Notation -\n        Configuration files\n        (<a href=\"http://en.wikipedia.org/wiki/TTCN\">TTCN-CFG</a>)\n    </p>\n    <p><strong>MIME types defined:</strong> <code>text/x-ttcn-cfg</code>.</p>\n\n    <br/>\n    <p>The development of this mode has been sponsored by <a href=\"http://www.ericsson.com/\">Ericsson\n    </a>.</p>\n    <p>Coded by Asmelash Tsegay Gebretsadkan </p>\n</article>\n\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/ttcn-cfg/ttcn-cfg.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"ttcn-cfg\", function(config, parserConfig) {\n    var indentUnit = config.indentUnit,\n        keywords = parserConfig.keywords || {},\n        fileNCtrlMaskOptions = parserConfig.fileNCtrlMaskOptions || {},\n        externalCommands = parserConfig.externalCommands || {},\n        multiLineStrings = parserConfig.multiLineStrings,\n        indentStatements = parserConfig.indentStatements !== false;\n    var isOperatorChar = /[\\|]/;\n    var curPunc;\n\n    function tokenBase(stream, state) {\n      var ch = stream.next();\n      if (ch == '\"' || ch == \"'\") {\n        state.tokenize = tokenString(ch);\n        return state.tokenize(stream, state);\n      }\n      if (/[:=]/.test(ch)) {\n        curPunc = ch;\n        return \"punctuation\";\n      }\n      if (ch == \"#\"){\n        stream.skipToEnd();\n        return \"comment\";\n      }\n      if (/\\d/.test(ch)) {\n        stream.eatWhile(/[\\w\\.]/);\n        return \"number\";\n      }\n      if (isOperatorChar.test(ch)) {\n        stream.eatWhile(isOperatorChar);\n        return \"operator\";\n      }\n      if (ch == \"[\"){\n        stream.eatWhile(/[\\w_\\]]/);\n        return \"number sectionTitle\";\n      }\n\n      stream.eatWhile(/[\\w\\$_]/);\n      var cur = stream.current();\n      if (keywords.propertyIsEnumerable(cur)) return \"keyword\";\n      if (fileNCtrlMaskOptions.propertyIsEnumerable(cur))\n        return \"negative fileNCtrlMaskOptions\";\n      if (externalCommands.propertyIsEnumerable(cur)) return \"negative externalCommands\";\n\n      return \"variable\";\n    }\n\n    function tokenString(quote) {\n      return function(stream, state) {\n        var escaped = false, next, end = false;\n        while ((next = stream.next()) != null) {\n          if (next == quote && !escaped){\n            var afterNext = stream.peek();\n            //look if the character if the quote is like the B in '10100010'B\n            if (afterNext){\n              afterNext = afterNext.toLowerCase();\n              if(afterNext == \"b\" || afterNext == \"h\" || afterNext == \"o\")\n                stream.next();\n            }\n            end = true; break;\n          }\n          escaped = !escaped && next == \"\\\\\";\n        }\n        if (end || !(escaped || multiLineStrings))\n          state.tokenize = null;\n        return \"string\";\n      };\n    }\n\n    function Context(indented, column, type, align, prev) {\n      this.indented = indented;\n      this.column = column;\n      this.type = type;\n      this.align = align;\n      this.prev = prev;\n    }\n    function pushContext(state, col, type) {\n      var indent = state.indented;\n      if (state.context && state.context.type == \"statement\")\n        indent = state.context.indented;\n      return state.context = new Context(indent, col, type, null, state.context);\n    }\n    function popContext(state) {\n      var t = state.context.type;\n      if (t == \")\" || t == \"]\" || t == \"}\")\n        state.indented = state.context.indented;\n      return state.context = state.context.prev;\n    }\n\n    //Interface\n    return {\n      startState: function(basecolumn) {\n        return {\n          tokenize: null,\n          context: new Context((basecolumn || 0) - indentUnit, 0, \"top\", false),\n          indented: 0,\n          startOfLine: true\n        };\n      },\n\n      token: function(stream, state) {\n        var ctx = state.context;\n        if (stream.sol()) {\n          if (ctx.align == null) ctx.align = false;\n          state.indented = stream.indentation();\n          state.startOfLine = true;\n        }\n        if (stream.eatSpace()) return null;\n        curPunc = null;\n        var style = (state.tokenize || tokenBase)(stream, state);\n        if (style == \"comment\") return style;\n        if (ctx.align == null) ctx.align = true;\n\n        if ((curPunc == \";\" || curPunc == \":\" || curPunc == \",\")\n            && ctx.type == \"statement\"){\n          popContext(state);\n        }\n        else if (curPunc == \"{\") pushContext(state, stream.column(), \"}\");\n        else if (curPunc == \"[\") pushContext(state, stream.column(), \"]\");\n        else if (curPunc == \"(\") pushContext(state, stream.column(), \")\");\n        else if (curPunc == \"}\") {\n          while (ctx.type == \"statement\") ctx = popContext(state);\n          if (ctx.type == \"}\") ctx = popContext(state);\n          while (ctx.type == \"statement\") ctx = popContext(state);\n        }\n        else if (curPunc == ctx.type) popContext(state);\n        else if (indentStatements && (((ctx.type == \"}\" || ctx.type == \"top\")\n            && curPunc != ';') || (ctx.type == \"statement\"\n            && curPunc == \"newstatement\")))\n          pushContext(state, stream.column(), \"statement\");\n        state.startOfLine = false;\n        return style;\n      },\n\n      electricChars: \"{}\",\n      lineComment: \"#\",\n      fold: \"brace\"\n    };\n  });\n\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i)\n      obj[words[i]] = true;\n    return obj;\n  }\n\n  CodeMirror.defineMIME(\"text/x-ttcn-cfg\", {\n    name: \"ttcn-cfg\",\n    keywords: words(\"Yes No LogFile FileMask ConsoleMask AppendFile\" +\n    \" TimeStampFormat LogEventTypes SourceInfoFormat\" +\n    \" LogEntityName LogSourceInfo DiskFullAction\" +\n    \" LogFileNumber LogFileSize MatchingHints Detailed\" +\n    \" Compact SubCategories Stack Single None Seconds\" +\n    \" DateTime Time Stop Error Retry Delete TCPPort KillTimer\" +\n    \" NumHCs UnixSocketsEnabled LocalAddress\"),\n    fileNCtrlMaskOptions: words(\"TTCN_EXECUTOR TTCN_ERROR TTCN_WARNING\" +\n    \" TTCN_PORTEVENT TTCN_TIMEROP TTCN_VERDICTOP\" +\n    \" TTCN_DEFAULTOP TTCN_TESTCASE TTCN_ACTION\" +\n    \" TTCN_USER TTCN_FUNCTION TTCN_STATISTICS\" +\n    \" TTCN_PARALLEL TTCN_MATCHING TTCN_DEBUG\" +\n    \" EXECUTOR ERROR WARNING PORTEVENT TIMEROP\" +\n    \" VERDICTOP DEFAULTOP TESTCASE ACTION USER\" +\n    \" FUNCTION STATISTICS PARALLEL MATCHING DEBUG\" +\n    \" LOG_ALL LOG_NOTHING ACTION_UNQUALIFIED\" +\n    \" DEBUG_ENCDEC DEBUG_TESTPORT\" +\n    \" DEBUG_UNQUALIFIED DEFAULTOP_ACTIVATE\" +\n    \" DEFAULTOP_DEACTIVATE DEFAULTOP_EXIT\" +\n    \" DEFAULTOP_UNQUALIFIED ERROR_UNQUALIFIED\" +\n    \" EXECUTOR_COMPONENT EXECUTOR_CONFIGDATA\" +\n    \" EXECUTOR_EXTCOMMAND EXECUTOR_LOGOPTIONS\" +\n    \" EXECUTOR_RUNTIME EXECUTOR_UNQUALIFIED\" +\n    \" FUNCTION_RND FUNCTION_UNQUALIFIED\" +\n    \" MATCHING_DONE MATCHING_MCSUCCESS\" +\n    \" MATCHING_MCUNSUCC MATCHING_MMSUCCESS\" +\n    \" MATCHING_MMUNSUCC MATCHING_PCSUCCESS\" +\n    \" MATCHING_PCUNSUCC MATCHING_PMSUCCESS\" +\n    \" MATCHING_PMUNSUCC MATCHING_PROBLEM\" +\n    \" MATCHING_TIMEOUT MATCHING_UNQUALIFIED\" +\n    \" PARALLEL_PORTCONN PARALLEL_PORTMAP\" +\n    \" PARALLEL_PTC PARALLEL_UNQUALIFIED\" +\n    \" PORTEVENT_DUALRECV PORTEVENT_DUALSEND\" +\n    \" PORTEVENT_MCRECV PORTEVENT_MCSEND\" +\n    \" PORTEVENT_MMRECV PORTEVENT_MMSEND\" +\n    \" PORTEVENT_MQUEUE PORTEVENT_PCIN\" +\n    \" PORTEVENT_PCOUT PORTEVENT_PMIN\" +\n    \" PORTEVENT_PMOUT PORTEVENT_PQUEUE\" +\n    \" PORTEVENT_STATE PORTEVENT_UNQUALIFIED\" +\n    \" STATISTICS_UNQUALIFIED STATISTICS_VERDICT\" +\n    \" TESTCASE_FINISH TESTCASE_START\" +\n    \" TESTCASE_UNQUALIFIED TIMEROP_GUARD\" +\n    \" TIMEROP_READ TIMEROP_START TIMEROP_STOP\" +\n    \" TIMEROP_TIMEOUT TIMEROP_UNQUALIFIED\" +\n    \" USER_UNQUALIFIED VERDICTOP_FINAL\" +\n    \" VERDICTOP_GETVERDICT VERDICTOP_SETVERDICT\" +\n    \" VERDICTOP_UNQUALIFIED WARNING_UNQUALIFIED\"),\n    externalCommands: words(\"BeginControlPart EndControlPart BeginTestCase\" +\n    \" EndTestCase\"),\n    multiLineStrings: true\n  });\n});"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/turtle/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Turtle mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"turtle.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Turtle</a>\n  </ul>\n</div>\n\n<article>\n<h2>Turtle mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n@prefix geo: <http://www.w3.org/2003/01/geo/wgs84_pos#> .\n@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n\n<http://purl.org/net/bsletten> \n    a foaf:Person;\n    foaf:interest <http://www.w3.org/2000/01/sw/>;\n    foaf:based_near [\n        geo:lat \"34.0736111\" ;\n        geo:lon \"-118.3994444\"\n   ]\n\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: \"text/turtle\",\n        matchBrackets: true\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/turtle</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/turtle/turtle.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"turtle\", function(config) {\n  var indentUnit = config.indentUnit;\n  var curPunc;\n\n  function wordRegexp(words) {\n    return new RegExp(\"^(?:\" + words.join(\"|\") + \")$\", \"i\");\n  }\n  var ops = wordRegexp([]);\n  var keywords = wordRegexp([\"@prefix\", \"@base\", \"a\"]);\n  var operatorChars = /[*+\\-<>=&|]/;\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    curPunc = null;\n    if (ch == \"<\" && !stream.match(/^[\\s\\u00a0=]/, false)) {\n      stream.match(/^[^\\s\\u00a0>]*>?/);\n      return \"atom\";\n    }\n    else if (ch == \"\\\"\" || ch == \"'\") {\n      state.tokenize = tokenLiteral(ch);\n      return state.tokenize(stream, state);\n    }\n    else if (/[{}\\(\\),\\.;\\[\\]]/.test(ch)) {\n      curPunc = ch;\n      return null;\n    }\n    else if (ch == \"#\") {\n      stream.skipToEnd();\n      return \"comment\";\n    }\n    else if (operatorChars.test(ch)) {\n      stream.eatWhile(operatorChars);\n      return null;\n    }\n    else if (ch == \":\") {\n          return \"operator\";\n        } else {\n      stream.eatWhile(/[_\\w\\d]/);\n      if(stream.peek() == \":\") {\n        return \"variable-3\";\n      } else {\n             var word = stream.current();\n\n             if(keywords.test(word)) {\n                        return \"meta\";\n             }\n\n             if(ch >= \"A\" && ch <= \"Z\") {\n                    return \"comment\";\n                 } else {\n                        return \"keyword\";\n                 }\n      }\n      var word = stream.current();\n      if (ops.test(word))\n        return null;\n      else if (keywords.test(word))\n        return \"meta\";\n      else\n        return \"variable\";\n    }\n  }\n\n  function tokenLiteral(quote) {\n    return function(stream, state) {\n      var escaped = false, ch;\n      while ((ch = stream.next()) != null) {\n        if (ch == quote && !escaped) {\n          state.tokenize = tokenBase;\n          break;\n        }\n        escaped = !escaped && ch == \"\\\\\";\n      }\n      return \"string\";\n    };\n  }\n\n  function pushContext(state, type, col) {\n    state.context = {prev: state.context, indent: state.indent, col: col, type: type};\n  }\n  function popContext(state) {\n    state.indent = state.context.indent;\n    state.context = state.context.prev;\n  }\n\n  return {\n    startState: function() {\n      return {tokenize: tokenBase,\n              context: null,\n              indent: 0,\n              col: 0};\n    },\n\n    token: function(stream, state) {\n      if (stream.sol()) {\n        if (state.context && state.context.align == null) state.context.align = false;\n        state.indent = stream.indentation();\n      }\n      if (stream.eatSpace()) return null;\n      var style = state.tokenize(stream, state);\n\n      if (style != \"comment\" && state.context && state.context.align == null && state.context.type != \"pattern\") {\n        state.context.align = true;\n      }\n\n      if (curPunc == \"(\") pushContext(state, \")\", stream.column());\n      else if (curPunc == \"[\") pushContext(state, \"]\", stream.column());\n      else if (curPunc == \"{\") pushContext(state, \"}\", stream.column());\n      else if (/[\\]\\}\\)]/.test(curPunc)) {\n        while (state.context && state.context.type == \"pattern\") popContext(state);\n        if (state.context && curPunc == state.context.type) popContext(state);\n      }\n      else if (curPunc == \".\" && state.context && state.context.type == \"pattern\") popContext(state);\n      else if (/atom|string|variable/.test(style) && state.context) {\n        if (/[\\}\\]]/.test(state.context.type))\n          pushContext(state, \"pattern\", stream.column());\n        else if (state.context.type == \"pattern\" && !state.context.align) {\n          state.context.align = true;\n          state.context.col = stream.column();\n        }\n      }\n\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      var firstChar = textAfter && textAfter.charAt(0);\n      var context = state.context;\n      if (/[\\]\\}]/.test(firstChar))\n        while (context && context.type == \"pattern\") context = context.prev;\n\n      var closing = context && firstChar == context.type;\n      if (!context)\n        return 0;\n      else if (context.type == \"pattern\")\n        return context.col;\n      else if (context.align)\n        return context.col + (closing ? 0 : 1);\n      else\n        return context.indent + (closing ? 0 : indentUnit);\n    },\n\n    lineComment: \"#\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/turtle\", \"turtle\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/twig/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Twig mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"twig.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../../addon/mode/multiplex.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Twig</a>\n  </ul>\n</div>\n\n<article>\n<h2>Twig mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n{% extends \"layout.twig\" %}\n{% block title %}CodeMirror: Twig mode{% endblock %}\n{# this is a comment #}\n{% block content %}\n  {% for foo in bar if foo.baz is divisible by(3) %}\n    Hello {{ foo.world }}\n  {% else %}\n    {% set msg = \"Result not found\" %}\n    {% include \"empty.twig\" with { message: msg } %}\n  {% endfor %}\n{% endblock %}\n</textarea></form>\n    <script>\n      var editor =\n      CodeMirror.fromTextArea(document.getElementById(\"code\"), {mode:\n        {name: \"twig\", base: \"text/html\"}});\n    </script>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/twig/twig.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"),  require(\"../../addon/mode/multiplex\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../../addon/mode/multiplex\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n  \"use strict\";\n\n  CodeMirror.defineMode(\"twig:inner\", function() {\n    var keywords = [\"and\", \"as\", \"autoescape\", \"endautoescape\", \"block\", \"do\", \"endblock\", \"else\", \"elseif\", \"extends\", \"for\", \"endfor\", \"embed\", \"endembed\", \"filter\", \"endfilter\", \"flush\", \"from\", \"if\", \"endif\", \"in\", \"is\", \"include\", \"import\", \"not\", \"or\", \"set\", \"spaceless\", \"endspaceless\", \"with\", \"endwith\", \"trans\", \"endtrans\", \"blocktrans\", \"endblocktrans\", \"macro\", \"endmacro\", \"use\", \"verbatim\", \"endverbatim\"],\n        operator = /^[+\\-*&%=<>!?|~^]/,\n        sign = /^[:\\[\\(\\{]/,\n        atom = [\"true\", \"false\", \"null\", \"empty\", \"defined\", \"divisibleby\", \"divisible by\", \"even\", \"odd\", \"iterable\", \"sameas\", \"same as\"],\n        number = /^(\\d[+\\-\\*\\/])?\\d+(\\.\\d+)?/;\n\n    keywords = new RegExp(\"((\" + keywords.join(\")|(\") + \"))\\\\b\");\n    atom = new RegExp(\"((\" + atom.join(\")|(\") + \"))\\\\b\");\n\n    function tokenBase (stream, state) {\n      var ch = stream.peek();\n\n      //Comment\n      if (state.incomment) {\n        if (!stream.skipTo(\"#}\")) {\n          stream.skipToEnd();\n        } else {\n          stream.eatWhile(/\\#|}/);\n          state.incomment = false;\n        }\n        return \"comment\";\n      //Tag\n      } else if (state.intag) {\n        //After operator\n        if (state.operator) {\n          state.operator = false;\n          if (stream.match(atom)) {\n            return \"atom\";\n          }\n          if (stream.match(number)) {\n            return \"number\";\n          }\n        }\n        //After sign\n        if (state.sign) {\n          state.sign = false;\n          if (stream.match(atom)) {\n            return \"atom\";\n          }\n          if (stream.match(number)) {\n            return \"number\";\n          }\n        }\n\n        if (state.instring) {\n          if (ch == state.instring) {\n            state.instring = false;\n          }\n          stream.next();\n          return \"string\";\n        } else if (ch == \"'\" || ch == '\"') {\n          state.instring = ch;\n          stream.next();\n          return \"string\";\n        } else if (stream.match(state.intag + \"}\") || stream.eat(\"-\") && stream.match(state.intag + \"}\")) {\n          state.intag = false;\n          return \"tag\";\n        } else if (stream.match(operator)) {\n          state.operator = true;\n          return \"operator\";\n        } else if (stream.match(sign)) {\n          state.sign = true;\n        } else {\n          if (stream.eat(\" \") || stream.sol()) {\n            if (stream.match(keywords)) {\n              return \"keyword\";\n            }\n            if (stream.match(atom)) {\n              return \"atom\";\n            }\n            if (stream.match(number)) {\n              return \"number\";\n            }\n            if (stream.sol()) {\n              stream.next();\n            }\n          } else {\n            stream.next();\n          }\n\n        }\n        return \"variable\";\n      } else if (stream.eat(\"{\")) {\n        if (stream.eat(\"#\")) {\n          state.incomment = true;\n          if (!stream.skipTo(\"#}\")) {\n            stream.skipToEnd();\n          } else {\n            stream.eatWhile(/\\#|}/);\n            state.incomment = false;\n          }\n          return \"comment\";\n        //Open tag\n        } else if (ch = stream.eat(/\\{|%/)) {\n          //Cache close tag\n          state.intag = ch;\n          if (ch == \"{\") {\n            state.intag = \"}\";\n          }\n          stream.eat(\"-\");\n          return \"tag\";\n        }\n      }\n      stream.next();\n    };\n\n    return {\n      startState: function () {\n        return {};\n      },\n      token: function (stream, state) {\n        return tokenBase(stream, state);\n      }\n    };\n  });\n\n  CodeMirror.defineMode(\"twig\", function(config, parserConfig) {\n    var twigInner = CodeMirror.getMode(config, \"twig:inner\");\n    if (!parserConfig || !parserConfig.base) return twigInner;\n    return CodeMirror.multiplexingMode(\n      CodeMirror.getMode(config, parserConfig.base), {\n        open: /\\{[{#%]/, close: /[}#%]\\}/, mode: twigInner, parseDelimiters: true\n      }\n    );\n  });\n  CodeMirror.defineMIME(\"text/x-twig\", \"twig\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/vb/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: VB.NET mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link href=\"http://fonts.googleapis.com/css?family=Inconsolata\" rel=\"stylesheet\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"vb.js\"></script>\n<script src=\"../../addon/runmode/runmode.js\"></script>\n<style>\n      .CodeMirror {border: 1px solid #aaa; height:210px; height: auto;}\n      .CodeMirror-scroll { overflow-x: auto; overflow-y: hidden;}\n      .CodeMirror pre { font-family: Inconsolata; font-size: 14px}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">VB.NET</a>\n  </ul>\n</div>\n\n<article>\n<h2>VB.NET mode</h2>\n  <div id=\"edit\">\n  <textarea name=\"code\" id=\"code\" >\nClass rocket\n  Private quality as Double\n  Public Sub launch() as String\n    If quality > 0.8\n      launch = \"Successful\"\n    Else\n      launch = \"Failed\"\n    End If\n  End sub\nEnd class\n</textarea>\n  </div>\n  <p>MIME type defined: <code>text/x-vb</code>.</p>\n<script>CodeMirror.fromTextArea(document.getElementById(\"code\"))</script>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/vb/vb.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"vb\", function(conf, parserConf) {\n    var ERRORCLASS = 'error';\n\n    function wordRegexp(words) {\n        return new RegExp(\"^((\" + words.join(\")|(\") + \"))\\\\b\", \"i\");\n    }\n\n    var singleOperators = new RegExp(\"^[\\\\+\\\\-\\\\*/%&\\\\\\\\|\\\\^~<>!]\");\n    var singleDelimiters = new RegExp('^[\\\\(\\\\)\\\\[\\\\]\\\\{\\\\}@,:`=;\\\\.]');\n    var doubleOperators = new RegExp(\"^((==)|(<>)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\\\*\\\\*))\");\n    var doubleDelimiters = new RegExp(\"^((\\\\+=)|(\\\\-=)|(\\\\*=)|(%=)|(/=)|(&=)|(\\\\|=)|(\\\\^=))\");\n    var tripleDelimiters = new RegExp(\"^((//=)|(>>=)|(<<=)|(\\\\*\\\\*=))\");\n    var identifiers = new RegExp(\"^[_A-Za-z][_A-Za-z0-9]*\");\n\n    var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property', 'try', 'structure', 'synclock', 'using', 'with'];\n    var middleKeywords = ['else','elseif','case', 'catch', 'finally'];\n    var endKeywords = ['next','loop'];\n\n    var operatorKeywords = ['and', \"andalso\", 'or', 'orelse', 'xor', 'in', 'not', 'is', 'isnot', 'like'];\n    var wordOperators = wordRegexp(operatorKeywords);\n\n    var commonKeywords = [\"#const\", \"#else\", \"#elseif\", \"#end\", \"#if\", \"#region\", \"addhandler\", \"addressof\", \"alias\", \"as\", \"byref\", \"byval\", \"cbool\", \"cbyte\", \"cchar\", \"cdate\", \"cdbl\", \"cdec\", \"cint\", \"clng\", \"cobj\", \"compare\", \"const\", \"continue\", \"csbyte\", \"cshort\", \"csng\", \"cstr\", \"cuint\", \"culng\", \"cushort\", \"declare\", \"default\", \"delegate\", \"dim\", \"directcast\", \"each\", \"erase\", \"error\", \"event\", \"exit\", \"explicit\", \"false\", \"for\", \"friend\", \"gettype\", \"goto\", \"handles\", \"implements\", \"imports\", \"infer\", \"inherits\", \"interface\", \"isfalse\", \"istrue\", \"lib\", \"me\", \"mod\", \"mustinherit\", \"mustoverride\", \"my\", \"mybase\", \"myclass\", \"namespace\", \"narrowing\", \"new\", \"nothing\", \"notinheritable\", \"notoverridable\", \"of\", \"off\", \"on\", \"operator\", \"option\", \"optional\", \"out\", \"overloads\", \"overridable\", \"overrides\", \"paramarray\", \"partial\", \"private\", \"protected\", \"public\", \"raiseevent\", \"readonly\", \"redim\", \"removehandler\", \"resume\", \"return\", \"shadows\", \"shared\", \"static\", \"step\", \"stop\", \"strict\", \"then\", \"throw\", \"to\", \"true\", \"trycast\", \"typeof\", \"until\", \"until\", \"when\", \"widening\", \"withevents\", \"writeonly\"];\n\n    var commontypes = ['object', 'boolean', 'char', 'string', 'byte', 'sbyte', 'short', 'ushort', 'int16', 'uint16', 'integer', 'uinteger', 'int32', 'uint32', 'long', 'ulong', 'int64', 'uint64', 'decimal', 'single', 'double', 'float', 'date', 'datetime', 'intptr', 'uintptr'];\n\n    var keywords = wordRegexp(commonKeywords);\n    var types = wordRegexp(commontypes);\n    var stringPrefixes = '\"';\n\n    var opening = wordRegexp(openingKeywords);\n    var middle = wordRegexp(middleKeywords);\n    var closing = wordRegexp(endKeywords);\n    var doubleClosing = wordRegexp(['end']);\n    var doOpening = wordRegexp(['do']);\n\n    var indentInfo = null;\n\n    CodeMirror.registerHelper(\"hintWords\", \"vb\", openingKeywords.concat(middleKeywords).concat(endKeywords)\n                                .concat(operatorKeywords).concat(commonKeywords).concat(commontypes));\n\n    function indent(_stream, state) {\n      state.currentIndent++;\n    }\n\n    function dedent(_stream, state) {\n      state.currentIndent--;\n    }\n    // tokenizers\n    function tokenBase(stream, state) {\n        if (stream.eatSpace()) {\n            return null;\n        }\n\n        var ch = stream.peek();\n\n        // Handle Comments\n        if (ch === \"'\") {\n            stream.skipToEnd();\n            return 'comment';\n        }\n\n\n        // Handle Number Literals\n        if (stream.match(/^((&H)|(&O))?[0-9\\.a-f]/i, false)) {\n            var floatLiteral = false;\n            // Floats\n            if (stream.match(/^\\d*\\.\\d+F?/i)) { floatLiteral = true; }\n            else if (stream.match(/^\\d+\\.\\d*F?/)) { floatLiteral = true; }\n            else if (stream.match(/^\\.\\d+F?/)) { floatLiteral = true; }\n\n            if (floatLiteral) {\n                // Float literals may be \"imaginary\"\n                stream.eat(/J/i);\n                return 'number';\n            }\n            // Integers\n            var intLiteral = false;\n            // Hex\n            if (stream.match(/^&H[0-9a-f]+/i)) { intLiteral = true; }\n            // Octal\n            else if (stream.match(/^&O[0-7]+/i)) { intLiteral = true; }\n            // Decimal\n            else if (stream.match(/^[1-9]\\d*F?/)) {\n                // Decimal literals may be \"imaginary\"\n                stream.eat(/J/i);\n                // TODO - Can you have imaginary longs?\n                intLiteral = true;\n            }\n            // Zero by itself with no other piece of number.\n            else if (stream.match(/^0(?![\\dx])/i)) { intLiteral = true; }\n            if (intLiteral) {\n                // Integer literals may be \"long\"\n                stream.eat(/L/i);\n                return 'number';\n            }\n        }\n\n        // Handle Strings\n        if (stream.match(stringPrefixes)) {\n            state.tokenize = tokenStringFactory(stream.current());\n            return state.tokenize(stream, state);\n        }\n\n        // Handle operators and Delimiters\n        if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {\n            return null;\n        }\n        if (stream.match(doubleOperators)\n            || stream.match(singleOperators)\n            || stream.match(wordOperators)) {\n            return 'operator';\n        }\n        if (stream.match(singleDelimiters)) {\n            return null;\n        }\n        if (stream.match(doOpening)) {\n            indent(stream,state);\n            state.doInCurrentLine = true;\n            return 'keyword';\n        }\n        if (stream.match(opening)) {\n            if (! state.doInCurrentLine)\n              indent(stream,state);\n            else\n              state.doInCurrentLine = false;\n            return 'keyword';\n        }\n        if (stream.match(middle)) {\n            return 'keyword';\n        }\n\n        if (stream.match(doubleClosing)) {\n            dedent(stream,state);\n            dedent(stream,state);\n            return 'keyword';\n        }\n        if (stream.match(closing)) {\n            dedent(stream,state);\n            return 'keyword';\n        }\n\n        if (stream.match(types)) {\n            return 'keyword';\n        }\n\n        if (stream.match(keywords)) {\n            return 'keyword';\n        }\n\n        if (stream.match(identifiers)) {\n            return 'variable';\n        }\n\n        // Handle non-detected items\n        stream.next();\n        return ERRORCLASS;\n    }\n\n    function tokenStringFactory(delimiter) {\n        var singleline = delimiter.length == 1;\n        var OUTCLASS = 'string';\n\n        return function(stream, state) {\n            while (!stream.eol()) {\n                stream.eatWhile(/[^'\"]/);\n                if (stream.match(delimiter)) {\n                    state.tokenize = tokenBase;\n                    return OUTCLASS;\n                } else {\n                    stream.eat(/['\"]/);\n                }\n            }\n            if (singleline) {\n                if (parserConf.singleLineStringErrors) {\n                    return ERRORCLASS;\n                } else {\n                    state.tokenize = tokenBase;\n                }\n            }\n            return OUTCLASS;\n        };\n    }\n\n\n    function tokenLexer(stream, state) {\n        var style = state.tokenize(stream, state);\n        var current = stream.current();\n\n        // Handle '.' connected identifiers\n        if (current === '.') {\n            style = state.tokenize(stream, state);\n            if (style === 'variable') {\n                return 'variable';\n            } else {\n                return ERRORCLASS;\n            }\n        }\n\n\n        var delimiter_index = '[({'.indexOf(current);\n        if (delimiter_index !== -1) {\n            indent(stream, state );\n        }\n        if (indentInfo === 'dedent') {\n            if (dedent(stream, state)) {\n                return ERRORCLASS;\n            }\n        }\n        delimiter_index = '])}'.indexOf(current);\n        if (delimiter_index !== -1) {\n            if (dedent(stream, state)) {\n                return ERRORCLASS;\n            }\n        }\n\n        return style;\n    }\n\n    var external = {\n        electricChars:\"dDpPtTfFeE \",\n        startState: function() {\n            return {\n              tokenize: tokenBase,\n              lastToken: null,\n              currentIndent: 0,\n              nextLineIndent: 0,\n              doInCurrentLine: false\n\n\n          };\n        },\n\n        token: function(stream, state) {\n            if (stream.sol()) {\n              state.currentIndent += state.nextLineIndent;\n              state.nextLineIndent = 0;\n              state.doInCurrentLine = 0;\n            }\n            var style = tokenLexer(stream, state);\n\n            state.lastToken = {style:style, content: stream.current()};\n\n\n\n            return style;\n        },\n\n        indent: function(state, textAfter) {\n            var trueText = textAfter.replace(/^\\s+|\\s+$/g, '') ;\n            if (trueText.match(closing) || trueText.match(doubleClosing) || trueText.match(middle)) return conf.indentUnit*(state.currentIndent-1);\n            if(state.currentIndent < 0) return 0;\n            return state.currentIndent * conf.indentUnit;\n        },\n\n        lineComment: \"'\"\n    };\n    return external;\n});\n\nCodeMirror.defineMIME(\"text/x-vb\", \"vb\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/vbscript/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: VBScript mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"vbscript.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">VBScript</a>\n  </ul>\n</div>\n\n<article>\n<h2>VBScript mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n' Pete Guhl\n' 03-04-2012\n'\n' Basic VBScript support for codemirror2\n\nConst ForReading = 1, ForWriting = 2, ForAppending = 8\n\nCall Sub020_PostBroadcastToUrbanAirship(strUserName, strPassword, intTransmitID, strResponse)\n\nIf Not IsNull(strResponse) AND Len(strResponse) = 0 Then\n\tboolTransmitOkYN = False\nElse\n\t' WScript.Echo \"Oh Happy Day! Oh Happy DAY!\"\n\tboolTransmitOkYN = True\nEnd If\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        indentUnit: 4\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/vbscript</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/vbscript/vbscript.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n/*\nFor extra ASP classic objects, initialize CodeMirror instance with this option:\n    isASP: true\n\nE.G.:\n    var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        isASP: true\n      });\n*/\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"vbscript\", function(conf, parserConf) {\n    var ERRORCLASS = 'error';\n\n    function wordRegexp(words) {\n        return new RegExp(\"^((\" + words.join(\")|(\") + \"))\\\\b\", \"i\");\n    }\n\n    var singleOperators = new RegExp(\"^[\\\\+\\\\-\\\\*/&\\\\\\\\\\\\^<>=]\");\n    var doubleOperators = new RegExp(\"^((<>)|(<=)|(>=))\");\n    var singleDelimiters = new RegExp('^[\\\\.,]');\n    var brackets = new RegExp('^[\\\\(\\\\)]');\n    var identifiers = new RegExp(\"^[A-Za-z][_A-Za-z0-9]*\");\n\n    var openingKeywords = ['class','sub','select','while','if','function', 'property', 'with', 'for'];\n    var middleKeywords = ['else','elseif','case'];\n    var endKeywords = ['next','loop','wend'];\n\n    var wordOperators = wordRegexp(['and', 'or', 'not', 'xor', 'is', 'mod', 'eqv', 'imp']);\n    var commonkeywords = ['dim', 'redim', 'then',  'until', 'randomize',\n                          'byval','byref','new','property', 'exit', 'in',\n                          'const','private', 'public',\n                          'get','set','let', 'stop', 'on error resume next', 'on error goto 0', 'option explicit', 'call', 'me'];\n\n    //This list was from: http://msdn.microsoft.com/en-us/library/f8tbc79x(v=vs.84).aspx\n    var atomWords = ['true', 'false', 'nothing', 'empty', 'null'];\n    //This list was from: http://msdn.microsoft.com/en-us/library/3ca8tfek(v=vs.84).aspx\n    var builtinFuncsWords = ['abs', 'array', 'asc', 'atn', 'cbool', 'cbyte', 'ccur', 'cdate', 'cdbl', 'chr', 'cint', 'clng', 'cos', 'csng', 'cstr', 'date', 'dateadd', 'datediff', 'datepart',\n                        'dateserial', 'datevalue', 'day', 'escape', 'eval', 'execute', 'exp', 'filter', 'formatcurrency', 'formatdatetime', 'formatnumber', 'formatpercent', 'getlocale', 'getobject',\n                        'getref', 'hex', 'hour', 'inputbox', 'instr', 'instrrev', 'int', 'fix', 'isarray', 'isdate', 'isempty', 'isnull', 'isnumeric', 'isobject', 'join', 'lbound', 'lcase', 'left',\n                        'len', 'loadpicture', 'log', 'ltrim', 'rtrim', 'trim', 'maths', 'mid', 'minute', 'month', 'monthname', 'msgbox', 'now', 'oct', 'replace', 'rgb', 'right', 'rnd', 'round',\n                        'scriptengine', 'scriptenginebuildversion', 'scriptenginemajorversion', 'scriptengineminorversion', 'second', 'setlocale', 'sgn', 'sin', 'space', 'split', 'sqr', 'strcomp',\n                        'string', 'strreverse', 'tan', 'time', 'timer', 'timeserial', 'timevalue', 'typename', 'ubound', 'ucase', 'unescape', 'vartype', 'weekday', 'weekdayname', 'year'];\n\n    //This list was from: http://msdn.microsoft.com/en-us/library/ydz4cfk3(v=vs.84).aspx\n    var builtinConsts = ['vbBlack', 'vbRed', 'vbGreen', 'vbYellow', 'vbBlue', 'vbMagenta', 'vbCyan', 'vbWhite', 'vbBinaryCompare', 'vbTextCompare',\n                         'vbSunday', 'vbMonday', 'vbTuesday', 'vbWednesday', 'vbThursday', 'vbFriday', 'vbSaturday', 'vbUseSystemDayOfWeek', 'vbFirstJan1', 'vbFirstFourDays', 'vbFirstFullWeek',\n                         'vbGeneralDate', 'vbLongDate', 'vbShortDate', 'vbLongTime', 'vbShortTime', 'vbObjectError',\n                         'vbOKOnly', 'vbOKCancel', 'vbAbortRetryIgnore', 'vbYesNoCancel', 'vbYesNo', 'vbRetryCancel', 'vbCritical', 'vbQuestion', 'vbExclamation', 'vbInformation', 'vbDefaultButton1', 'vbDefaultButton2',\n                         'vbDefaultButton3', 'vbDefaultButton4', 'vbApplicationModal', 'vbSystemModal', 'vbOK', 'vbCancel', 'vbAbort', 'vbRetry', 'vbIgnore', 'vbYes', 'vbNo',\n                         'vbCr', 'VbCrLf', 'vbFormFeed', 'vbLf', 'vbNewLine', 'vbNullChar', 'vbNullString', 'vbTab', 'vbVerticalTab', 'vbUseDefault', 'vbTrue', 'vbFalse',\n                         'vbEmpty', 'vbNull', 'vbInteger', 'vbLong', 'vbSingle', 'vbDouble', 'vbCurrency', 'vbDate', 'vbString', 'vbObject', 'vbError', 'vbBoolean', 'vbVariant', 'vbDataObject', 'vbDecimal', 'vbByte', 'vbArray'];\n    //This list was from: http://msdn.microsoft.com/en-us/library/hkc375ea(v=vs.84).aspx\n    var builtinObjsWords = ['WScript', 'err', 'debug', 'RegExp'];\n    var knownProperties = ['description', 'firstindex', 'global', 'helpcontext', 'helpfile', 'ignorecase', 'length', 'number', 'pattern', 'source', 'value', 'count'];\n    var knownMethods = ['clear', 'execute', 'raise', 'replace', 'test', 'write', 'writeline', 'close', 'open', 'state', 'eof', 'update', 'addnew', 'end', 'createobject', 'quit'];\n\n    var aspBuiltinObjsWords = ['server', 'response', 'request', 'session', 'application'];\n    var aspKnownProperties = ['buffer', 'cachecontrol', 'charset', 'contenttype', 'expires', 'expiresabsolute', 'isclientconnected', 'pics', 'status', //response\n                              'clientcertificate', 'cookies', 'form', 'querystring', 'servervariables', 'totalbytes', //request\n                              'contents', 'staticobjects', //application\n                              'codepage', 'lcid', 'sessionid', 'timeout', //session\n                              'scripttimeout']; //server\n    var aspKnownMethods = ['addheader', 'appendtolog', 'binarywrite', 'end', 'flush', 'redirect', //response\n                           'binaryread', //request\n                           'remove', 'removeall', 'lock', 'unlock', //application\n                           'abandon', //session\n                           'getlasterror', 'htmlencode', 'mappath', 'transfer', 'urlencode']; //server\n\n    var knownWords = knownMethods.concat(knownProperties);\n\n    builtinObjsWords = builtinObjsWords.concat(builtinConsts);\n\n    if (conf.isASP){\n        builtinObjsWords = builtinObjsWords.concat(aspBuiltinObjsWords);\n        knownWords = knownWords.concat(aspKnownMethods, aspKnownProperties);\n    };\n\n    var keywords = wordRegexp(commonkeywords);\n    var atoms = wordRegexp(atomWords);\n    var builtinFuncs = wordRegexp(builtinFuncsWords);\n    var builtinObjs = wordRegexp(builtinObjsWords);\n    var known = wordRegexp(knownWords);\n    var stringPrefixes = '\"';\n\n    var opening = wordRegexp(openingKeywords);\n    var middle = wordRegexp(middleKeywords);\n    var closing = wordRegexp(endKeywords);\n    var doubleClosing = wordRegexp(['end']);\n    var doOpening = wordRegexp(['do']);\n    var noIndentWords = wordRegexp(['on error resume next', 'exit']);\n    var comment = wordRegexp(['rem']);\n\n\n    function indent(_stream, state) {\n      state.currentIndent++;\n    }\n\n    function dedent(_stream, state) {\n      state.currentIndent--;\n    }\n    // tokenizers\n    function tokenBase(stream, state) {\n        if (stream.eatSpace()) {\n            return 'space';\n            //return null;\n        }\n\n        var ch = stream.peek();\n\n        // Handle Comments\n        if (ch === \"'\") {\n            stream.skipToEnd();\n            return 'comment';\n        }\n        if (stream.match(comment)){\n            stream.skipToEnd();\n            return 'comment';\n        }\n\n\n        // Handle Number Literals\n        if (stream.match(/^((&H)|(&O))?[0-9\\.]/i, false) && !stream.match(/^((&H)|(&O))?[0-9\\.]+[a-z_]/i, false)) {\n            var floatLiteral = false;\n            // Floats\n            if (stream.match(/^\\d*\\.\\d+/i)) { floatLiteral = true; }\n            else if (stream.match(/^\\d+\\.\\d*/)) { floatLiteral = true; }\n            else if (stream.match(/^\\.\\d+/)) { floatLiteral = true; }\n\n            if (floatLiteral) {\n                // Float literals may be \"imaginary\"\n                stream.eat(/J/i);\n                return 'number';\n            }\n            // Integers\n            var intLiteral = false;\n            // Hex\n            if (stream.match(/^&H[0-9a-f]+/i)) { intLiteral = true; }\n            // Octal\n            else if (stream.match(/^&O[0-7]+/i)) { intLiteral = true; }\n            // Decimal\n            else if (stream.match(/^[1-9]\\d*F?/)) {\n                // Decimal literals may be \"imaginary\"\n                stream.eat(/J/i);\n                // TODO - Can you have imaginary longs?\n                intLiteral = true;\n            }\n            // Zero by itself with no other piece of number.\n            else if (stream.match(/^0(?![\\dx])/i)) { intLiteral = true; }\n            if (intLiteral) {\n                // Integer literals may be \"long\"\n                stream.eat(/L/i);\n                return 'number';\n            }\n        }\n\n        // Handle Strings\n        if (stream.match(stringPrefixes)) {\n            state.tokenize = tokenStringFactory(stream.current());\n            return state.tokenize(stream, state);\n        }\n\n        // Handle operators and Delimiters\n        if (stream.match(doubleOperators)\n            || stream.match(singleOperators)\n            || stream.match(wordOperators)) {\n            return 'operator';\n        }\n        if (stream.match(singleDelimiters)) {\n            return null;\n        }\n\n        if (stream.match(brackets)) {\n            return \"bracket\";\n        }\n\n        if (stream.match(noIndentWords)) {\n            state.doInCurrentLine = true;\n\n            return 'keyword';\n        }\n\n        if (stream.match(doOpening)) {\n            indent(stream,state);\n            state.doInCurrentLine = true;\n\n            return 'keyword';\n        }\n        if (stream.match(opening)) {\n            if (! state.doInCurrentLine)\n              indent(stream,state);\n            else\n              state.doInCurrentLine = false;\n\n            return 'keyword';\n        }\n        if (stream.match(middle)) {\n            return 'keyword';\n        }\n\n\n        if (stream.match(doubleClosing)) {\n            dedent(stream,state);\n            dedent(stream,state);\n\n            return 'keyword';\n        }\n        if (stream.match(closing)) {\n            if (! state.doInCurrentLine)\n              dedent(stream,state);\n            else\n              state.doInCurrentLine = false;\n\n            return 'keyword';\n        }\n\n        if (stream.match(keywords)) {\n            return 'keyword';\n        }\n\n        if (stream.match(atoms)) {\n            return 'atom';\n        }\n\n        if (stream.match(known)) {\n            return 'variable-2';\n        }\n\n        if (stream.match(builtinFuncs)) {\n            return 'builtin';\n        }\n\n        if (stream.match(builtinObjs)){\n            return 'variable-2';\n        }\n\n        if (stream.match(identifiers)) {\n            return 'variable';\n        }\n\n        // Handle non-detected items\n        stream.next();\n        return ERRORCLASS;\n    }\n\n    function tokenStringFactory(delimiter) {\n        var singleline = delimiter.length == 1;\n        var OUTCLASS = 'string';\n\n        return function(stream, state) {\n            while (!stream.eol()) {\n                stream.eatWhile(/[^'\"]/);\n                if (stream.match(delimiter)) {\n                    state.tokenize = tokenBase;\n                    return OUTCLASS;\n                } else {\n                    stream.eat(/['\"]/);\n                }\n            }\n            if (singleline) {\n                if (parserConf.singleLineStringErrors) {\n                    return ERRORCLASS;\n                } else {\n                    state.tokenize = tokenBase;\n                }\n            }\n            return OUTCLASS;\n        };\n    }\n\n\n    function tokenLexer(stream, state) {\n        var style = state.tokenize(stream, state);\n        var current = stream.current();\n\n        // Handle '.' connected identifiers\n        if (current === '.') {\n            style = state.tokenize(stream, state);\n\n            current = stream.current();\n            if (style && (style.substr(0, 8) === 'variable' || style==='builtin' || style==='keyword')){//|| knownWords.indexOf(current.substring(1)) > -1) {\n                if (style === 'builtin' || style === 'keyword') style='variable';\n                if (knownWords.indexOf(current.substr(1)) > -1) style='variable-2';\n\n                return style;\n            } else {\n                return ERRORCLASS;\n            }\n        }\n\n        return style;\n    }\n\n    var external = {\n        electricChars:\"dDpPtTfFeE \",\n        startState: function() {\n            return {\n              tokenize: tokenBase,\n              lastToken: null,\n              currentIndent: 0,\n              nextLineIndent: 0,\n              doInCurrentLine: false,\n              ignoreKeyword: false\n\n\n          };\n        },\n\n        token: function(stream, state) {\n            if (stream.sol()) {\n              state.currentIndent += state.nextLineIndent;\n              state.nextLineIndent = 0;\n              state.doInCurrentLine = 0;\n            }\n            var style = tokenLexer(stream, state);\n\n            state.lastToken = {style:style, content: stream.current()};\n\n            if (style==='space') style=null;\n\n            return style;\n        },\n\n        indent: function(state, textAfter) {\n            var trueText = textAfter.replace(/^\\s+|\\s+$/g, '') ;\n            if (trueText.match(closing) || trueText.match(doubleClosing) || trueText.match(middle)) return conf.indentUnit*(state.currentIndent-1);\n            if(state.currentIndent < 0) return 0;\n            return state.currentIndent * conf.indentUnit;\n        }\n\n    };\n    return external;\n});\n\nCodeMirror.defineMIME(\"text/vbscript\", \"vbscript\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/velocity/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Velocity mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/night.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"velocity.js\"></script>\n<style>.CodeMirror {border: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Velocity</a>\n  </ul>\n</div>\n\n<article>\n<h2>Velocity mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n## Velocity Code Demo\n#*\n   based on PL/SQL mode by Peter Raganitsch, adapted to Velocity by Steve O'Hara ( http://www.pivotal-solutions.co.uk )\n   August 2011\n*#\n\n#*\n   This is a multiline comment.\n   This is the second line\n*#\n\n#[[ hello steve\n   This has invalid syntax that would normally need \"poor man's escaping\" like:\n\n   #define()\n\n   ${blah\n]]#\n\n#include( \"disclaimer.txt\" \"opinion.txt\" )\n#include( $foo $bar )\n\n#parse( \"lecorbusier.vm\" )\n#parse( $foo )\n\n#evaluate( 'string with VTL #if(true)will be displayed#end' )\n\n#define( $hello ) Hello $who #end #set( $who = \"World!\") $hello ## displays Hello World!\n\n#foreach( $customer in $customerList )\n\n    $foreach.count $customer.Name\n\n    #if( $foo == ${bar})\n        it's true!\n        #break\n    #{else}\n        it's not!\n        #stop\n    #end\n\n    #if ($foreach.parent.hasNext)\n        $velocityCount\n    #end\n#end\n\n$someObject.getValues(\"this is a string split\n        across lines\")\n\n$someObject(\"This plus $something in the middle\").method(7567).property\n\n#set($something = \"Parseable string with '$quotes'!\")\n\n#macro( tablerows $color $somelist )\n    #foreach( $something in $somelist )\n        <tr><td bgcolor=$color>$something</td></tr>\n        <tr><td bgcolor=$color>$bodyContent</td></tr>\n    #end\n#end\n\n#tablerows(\"red\" [\"dadsdf\",\"dsa\"])\n#@tablerows(\"red\" [\"dadsdf\",\"dsa\"]) some body content #end\n\n   Variable reference: #set( $monkey = $bill )\n   String literal: #set( $monkey.Friend = 'monica' )\n   Property reference: #set( $monkey.Blame = $whitehouse.Leak )\n   Method reference: #set( $monkey.Plan = $spindoctor.weave($web) )\n   Number literal: #set( $monkey.Number = 123 )\n   Range operator: #set( $monkey.Numbers = [1..3] )\n   Object list: #set( $monkey.Say = [\"Not\", $my, \"fault\"] )\n   Object map: #set( $monkey.Map = {\"banana\" : \"good\", \"roast beef\" : \"bad\"})\n\nThe RHS can also be a simple arithmetic expression, such as:\nAddition: #set( $value = $foo + 1 )\n   Subtraction: #set( $value = $bar - 1 )\n   Multiplication: #set( $value = $foo * $bar )\n   Division: #set( $value = $foo / $bar )\n   Remainder: #set( $value = $foo % $bar )\n\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        theme: \"night\",\n        lineNumbers: true,\n        indentUnit: 4,\n        mode: \"text/velocity\"\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/velocity</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/velocity/velocity.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"velocity\", function() {\n    function parseWords(str) {\n        var obj = {}, words = str.split(\" \");\n        for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n        return obj;\n    }\n\n    var keywords = parseWords(\"#end #else #break #stop #[[ #]] \" +\n                              \"#{end} #{else} #{break} #{stop}\");\n    var functions = parseWords(\"#if #elseif #foreach #set #include #parse #macro #define #evaluate \" +\n                               \"#{if} #{elseif} #{foreach} #{set} #{include} #{parse} #{macro} #{define} #{evaluate}\");\n    var specials = parseWords(\"$foreach.count $foreach.hasNext $foreach.first $foreach.last $foreach.topmost $foreach.parent.count $foreach.parent.hasNext $foreach.parent.first $foreach.parent.last $foreach.parent $velocityCount $!bodyContent $bodyContent\");\n    var isOperatorChar = /[+\\-*&%=<>!?:\\/|]/;\n\n    function chain(stream, state, f) {\n        state.tokenize = f;\n        return f(stream, state);\n    }\n    function tokenBase(stream, state) {\n        var beforeParams = state.beforeParams;\n        state.beforeParams = false;\n        var ch = stream.next();\n        // start of unparsed string?\n        if ((ch == \"'\") && !state.inString && state.inParams) {\n            state.lastTokenWasBuiltin = false;\n            return chain(stream, state, tokenString(ch));\n        }\n        // start of parsed string?\n        else if ((ch == '\"')) {\n            state.lastTokenWasBuiltin = false;\n            if (state.inString) {\n                state.inString = false;\n                return \"string\";\n            }\n            else if (state.inParams)\n                return chain(stream, state, tokenString(ch));\n        }\n        // is it one of the special signs []{}().,;? Separator?\n        else if (/[\\[\\]{}\\(\\),;\\.]/.test(ch)) {\n            if (ch == \"(\" && beforeParams)\n                state.inParams = true;\n            else if (ch == \")\") {\n                state.inParams = false;\n                state.lastTokenWasBuiltin = true;\n            }\n            return null;\n        }\n        // start of a number value?\n        else if (/\\d/.test(ch)) {\n            state.lastTokenWasBuiltin = false;\n            stream.eatWhile(/[\\w\\.]/);\n            return \"number\";\n        }\n        // multi line comment?\n        else if (ch == \"#\" && stream.eat(\"*\")) {\n            state.lastTokenWasBuiltin = false;\n            return chain(stream, state, tokenComment);\n        }\n        // unparsed content?\n        else if (ch == \"#\" && stream.match(/ *\\[ *\\[/)) {\n            state.lastTokenWasBuiltin = false;\n            return chain(stream, state, tokenUnparsed);\n        }\n        // single line comment?\n        else if (ch == \"#\" && stream.eat(\"#\")) {\n            state.lastTokenWasBuiltin = false;\n            stream.skipToEnd();\n            return \"comment\";\n        }\n        // variable?\n        else if (ch == \"$\") {\n            stream.eat(\"!\");\n            stream.eatWhile(/[\\w\\d\\$_\\.{}-]/);\n            // is it one of the specials?\n            if (specials && specials.propertyIsEnumerable(stream.current())) {\n                return \"keyword\";\n            }\n            else {\n                state.lastTokenWasBuiltin = true;\n                state.beforeParams = true;\n                return \"builtin\";\n            }\n        }\n        // is it a operator?\n        else if (isOperatorChar.test(ch)) {\n            state.lastTokenWasBuiltin = false;\n            stream.eatWhile(isOperatorChar);\n            return \"operator\";\n        }\n        else {\n            // get the whole word\n            stream.eatWhile(/[\\w\\$_{}@]/);\n            var word = stream.current();\n            // is it one of the listed keywords?\n            if (keywords && keywords.propertyIsEnumerable(word))\n                return \"keyword\";\n            // is it one of the listed functions?\n            if (functions && functions.propertyIsEnumerable(word) ||\n                    (stream.current().match(/^#@?[a-z0-9_]+ *$/i) && stream.peek()==\"(\") &&\n                     !(functions && functions.propertyIsEnumerable(word.toLowerCase()))) {\n                state.beforeParams = true;\n                state.lastTokenWasBuiltin = false;\n                return \"keyword\";\n            }\n            if (state.inString) {\n                state.lastTokenWasBuiltin = false;\n                return \"string\";\n            }\n            if (stream.pos > word.length && stream.string.charAt(stream.pos-word.length-1)==\".\" && state.lastTokenWasBuiltin)\n                return \"builtin\";\n            // default: just a \"word\"\n            state.lastTokenWasBuiltin = false;\n            return null;\n        }\n    }\n\n    function tokenString(quote) {\n        return function(stream, state) {\n            var escaped = false, next, end = false;\n            while ((next = stream.next()) != null) {\n                if ((next == quote) && !escaped) {\n                    end = true;\n                    break;\n                }\n                if (quote=='\"' && stream.peek() == '$' && !escaped) {\n                    state.inString = true;\n                    end = true;\n                    break;\n                }\n                escaped = !escaped && next == \"\\\\\";\n            }\n            if (end) state.tokenize = tokenBase;\n            return \"string\";\n        };\n    }\n\n    function tokenComment(stream, state) {\n        var maybeEnd = false, ch;\n        while (ch = stream.next()) {\n            if (ch == \"#\" && maybeEnd) {\n                state.tokenize = tokenBase;\n                break;\n            }\n            maybeEnd = (ch == \"*\");\n        }\n        return \"comment\";\n    }\n\n    function tokenUnparsed(stream, state) {\n        var maybeEnd = 0, ch;\n        while (ch = stream.next()) {\n            if (ch == \"#\" && maybeEnd == 2) {\n                state.tokenize = tokenBase;\n                break;\n            }\n            if (ch == \"]\")\n                maybeEnd++;\n            else if (ch != \" \")\n                maybeEnd = 0;\n        }\n        return \"meta\";\n    }\n    // Interface\n\n    return {\n        startState: function() {\n            return {\n                tokenize: tokenBase,\n                beforeParams: false,\n                inParams: false,\n                inString: false,\n                lastTokenWasBuiltin: false\n            };\n        },\n\n        token: function(stream, state) {\n            if (stream.eatSpace()) return null;\n            return state.tokenize(stream, state);\n        },\n        blockCommentStart: \"#*\",\n        blockCommentEnd: \"*#\",\n        lineComment: \"##\",\n        fold: \"velocity\"\n    };\n});\n\nCodeMirror.defineMIME(\"text/velocity\", \"velocity\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/verilog/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Verilog/SystemVerilog mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"verilog.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Verilog/SystemVerilog</a>\n  </ul>\n</div>\n\n<article>\n<h2>SystemVerilog mode</h2>\n\n<div><textarea id=\"code\" name=\"code\">\n// Literals\n1'b0\n1'bx\n1'bz\n16'hDC78\n'hdeadbeef\n'b0011xxzz\n1234\n32'd5678\n3.4e6\n-128.7\n\n// Macro definition\n`define BUS_WIDTH = 8;\n\n// Module definition\nmodule block(\n  input                   clk,\n  input                   rst_n,\n  input  [`BUS_WIDTH-1:0] data_in,\n  output [`BUS_WIDTH-1:0] data_out\n);\n  \n  always @(posedge clk or negedge rst_n) begin\n\n    if (~rst_n) begin\n      data_out <= 8'b0;\n    end else begin\n      data_out <= data_in;\n    end\n    \n    if (~rst_n)\n      data_out <= 8'b0;\n    else\n      data_out <= data_in;\n    \n    if (~rst_n)\n      begin\n        data_out <= 8'b0;\n      end\n    else\n      begin\n        data_out <= data_in;\n      end\n\n  end\n  \nendmodule\n\n// Class definition\nclass test;\n\n  /**\n   * Sum two integers\n   */\n  function int sum(int a, int b);\n    int result = a + b;\n    string msg = $sformatf(\"%d + %d = %d\", a, b, result);\n    $display(msg);\n    return result;\n  endfunction\n  \n  task delay(int num_cycles);\n    repeat(num_cycles) #1;\n  endtask\n  \nendclass\n\n</textarea></div>\n\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    lineNumbers: true,\n    matchBrackets: true,\n    mode: {\n      name: \"verilog\",\n      noIndentKeywords: [\"package\"]\n    }\n  });\n</script>\n\n<p>\nSyntax highlighting and indentation for the Verilog and SystemVerilog languages (IEEE 1800).\n<h2>Configuration options:</h2>\n  <ul>\n    <li><strong>noIndentKeywords</strong> - List of keywords which should not cause indentation to increase. E.g. [\"package\", \"module\"]. Default: None</li>\n  </ul>\n</p>\n\n<p><strong>MIME types defined:</strong> <code>text/x-verilog</code> and <code>text/x-systemverilog</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/verilog/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 4}, \"verilog\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"binary_literals\",\n     \"[number 1'b0]\",\n     \"[number 1'b1]\",\n     \"[number 1'bx]\",\n     \"[number 1'bz]\",\n     \"[number 1'bX]\",\n     \"[number 1'bZ]\",\n     \"[number 1'B0]\",\n     \"[number 1'B1]\",\n     \"[number 1'Bx]\",\n     \"[number 1'Bz]\",\n     \"[number 1'BX]\",\n     \"[number 1'BZ]\",\n     \"[number 1'b0]\",\n     \"[number 1'b1]\",\n     \"[number 2'b01]\",\n     \"[number 2'bxz]\",\n     \"[number 2'b11]\",\n     \"[number 2'b10]\",\n     \"[number 2'b1Z]\",\n     \"[number 12'b0101_0101_0101]\",\n     \"[number 1'b 0]\",\n     \"[number 'b0101]\"\n  );\n\n  MT(\"octal_literals\",\n     \"[number 3'o7]\",\n     \"[number 3'O7]\",\n     \"[number 3'so7]\",\n     \"[number 3'SO7]\"\n  );\n\n  MT(\"decimal_literals\",\n     \"[number 0]\",\n     \"[number 1]\",\n     \"[number 7]\",\n     \"[number 123_456]\",\n     \"[number 'd33]\",\n     \"[number 8'd255]\",\n     \"[number 8'D255]\",\n     \"[number 8'sd255]\",\n     \"[number 8'SD255]\",\n     \"[number 32'd123]\",\n     \"[number 32 'd123]\",\n     \"[number 32 'd 123]\"\n  );\n\n  MT(\"hex_literals\",\n     \"[number 4'h0]\",\n     \"[number 4'ha]\",\n     \"[number 4'hF]\",\n     \"[number 4'hx]\",\n     \"[number 4'hz]\",\n     \"[number 4'hX]\",\n     \"[number 4'hZ]\",\n     \"[number 32'hdc78]\",\n     \"[number 32'hDC78]\",\n     \"[number 32 'hDC78]\",\n     \"[number 32'h DC78]\",\n     \"[number 32 'h DC78]\",\n     \"[number 32'h44x7]\",\n     \"[number 32'hFFF?]\"\n  );\n\n  MT(\"real_number_literals\",\n     \"[number 1.2]\",\n     \"[number 0.1]\",\n     \"[number 2394.26331]\",\n     \"[number 1.2E12]\",\n     \"[number 1.2e12]\",\n     \"[number 1.30e-2]\",\n     \"[number 0.1e-0]\",\n     \"[number 23E10]\",\n     \"[number 29E-2]\",\n     \"[number 236.123_763_e-12]\"\n  );\n\n  MT(\"operators\",\n     \"[meta ^]\"\n  );\n\n  MT(\"keywords\",\n     \"[keyword logic]\",\n     \"[keyword logic] [variable foo]\",\n     \"[keyword reg] [variable abc]\"\n  );\n\n  MT(\"variables\",\n     \"[variable _leading_underscore]\",\n     \"[variable _if]\",\n     \"[number 12] [variable foo]\",\n     \"[variable foo] [number 14]\"\n  );\n\n  MT(\"tick_defines\",\n     \"[def `FOO]\",\n     \"[def `foo]\",\n     \"[def `FOO_bar]\"\n  );\n\n  MT(\"system_calls\",\n     \"[meta $display]\",\n     \"[meta $vpi_printf]\"\n  );\n\n  MT(\"line_comment\", \"[comment // Hello world]\");\n\n  // Alignment tests\n  MT(\"align_port_map_style1\",\n     /**\n      * mod mod(.a(a),\n      *         .b(b)\n      *        );\n      */\n     \"[variable mod] [variable mod][bracket (].[variable a][bracket (][variable a][bracket )],\",\n     \"        .[variable b][bracket (][variable b][bracket )]\",\n     \"       [bracket )];\",\n     \"\"\n  );\n\n  MT(\"align_port_map_style2\",\n     /**\n      * mod mod(\n      *     .a(a),\n      *     .b(b)\n      * );\n      */\n     \"[variable mod] [variable mod][bracket (]\",\n     \"    .[variable a][bracket (][variable a][bracket )],\",\n     \"    .[variable b][bracket (][variable b][bracket )]\",\n     \"[bracket )];\",\n     \"\"\n  );\n\n  MT(\"align_assignments\",\n     /**\n      * always @(posedge clk) begin\n      *    if (rst)\n      *       data_out <= 8'b0 +\n      *                   8'b1;\n      *    else\n      *       data_out = 8'b0 +\n      *                  8'b1;\n      *    data_out =\n      *       8'b0 + 8'b1;\n      * end\n      */\n     \"[keyword always] [def @][bracket (][keyword posedge] [variable clk][bracket )] [keyword begin]\",\n     \"    [keyword if] [bracket (][variable rst][bracket )]\",\n     \"        [variable data_out] [meta <=] [number 8'b0] [meta +]\",\n     \"                    [number 8'b1];\",\n     \"    [keyword else]\",\n     \"        [variable data_out] [meta =] [number 8'b0] [meta +]\",\n     \"                   [number 8'b1];\",\n     \"    [variable data_out] [meta =] [number 8'b0] [meta +]\",\n     \"               [number 8'b1];\",\n     \"[keyword end]\",\n     \"\"\n  );\n\n  // Indentation tests\n  MT(\"indent_single_statement_if\",\n      \"[keyword if] [bracket (][variable foo][bracket )]\",\n      \"    [keyword break];\",\n      \"\"\n  );\n\n  MT(\"no_indent_after_single_line_if\",\n      \"[keyword if] [bracket (][variable foo][bracket )] [keyword break];\",\n      \"\"\n  );\n\n  MT(\"indent_after_if_begin_same_line\",\n      \"[keyword if] [bracket (][variable foo][bracket )] [keyword begin]\",\n      \"    [keyword break];\",\n      \"    [keyword break];\",\n      \"[keyword end]\",\n      \"\"\n  );\n\n  MT(\"indent_after_if_begin_next_line\",\n      \"[keyword if] [bracket (][variable foo][bracket )]\",\n      \"    [keyword begin]\",\n      \"        [keyword break];\",\n      \"        [keyword break];\",\n      \"    [keyword end]\",\n      \"\"\n  );\n\n  MT(\"indent_single_statement_if_else\",\n      \"[keyword if] [bracket (][variable foo][bracket )]\",\n      \"    [keyword break];\",\n      \"[keyword else]\",\n      \"    [keyword break];\",\n      \"\"\n  );\n\n  MT(\"indent_if_else_begin_same_line\",\n      \"[keyword if] [bracket (][variable foo][bracket )] [keyword begin]\",\n      \"    [keyword break];\",\n      \"    [keyword break];\",\n      \"[keyword end] [keyword else] [keyword begin]\",\n      \"    [keyword break];\",\n      \"    [keyword break];\",\n      \"[keyword end]\",\n      \"\"\n  );\n\n  MT(\"indent_if_else_begin_next_line\",\n      \"[keyword if] [bracket (][variable foo][bracket )]\",\n      \"    [keyword begin]\",\n      \"        [keyword break];\",\n      \"        [keyword break];\",\n      \"    [keyword end]\",\n      \"[keyword else]\",\n      \"    [keyword begin]\",\n      \"        [keyword break];\",\n      \"        [keyword break];\",\n      \"    [keyword end]\",\n      \"\"\n  );\n\n  MT(\"indent_if_nested_without_begin\",\n      \"[keyword if] [bracket (][variable foo][bracket )]\",\n      \"    [keyword if] [bracket (][variable foo][bracket )]\",\n      \"        [keyword if] [bracket (][variable foo][bracket )]\",\n      \"            [keyword break];\",\n      \"\"\n  );\n\n  MT(\"indent_case\",\n      \"[keyword case] [bracket (][variable state][bracket )]\",\n      \"    [variable FOO]:\",\n      \"        [keyword break];\",\n      \"    [variable BAR]:\",\n      \"        [keyword break];\",\n      \"[keyword endcase]\",\n      \"\"\n  );\n\n  MT(\"unindent_after_end_with_preceding_text\",\n      \"[keyword begin]\",\n      \"    [keyword break]; [keyword end]\",\n      \"\"\n  );\n\n  MT(\"export_function_one_line_does_not_indent\",\n     \"[keyword export] [string \\\"DPI-C\\\"] [keyword function] [variable helloFromSV];\",\n     \"\"\n  );\n\n  MT(\"export_task_one_line_does_not_indent\",\n     \"[keyword export] [string \\\"DPI-C\\\"] [keyword task] [variable helloFromSV];\",\n     \"\"\n  );\n\n  MT(\"export_function_two_lines_indents_properly\",\n    \"[keyword export]\",\n    \"    [string \\\"DPI-C\\\"] [keyword function] [variable helloFromSV];\",\n    \"\"\n  );\n\n  MT(\"export_task_two_lines_indents_properly\",\n    \"[keyword export]\",\n    \"    [string \\\"DPI-C\\\"] [keyword task] [variable helloFromSV];\",\n    \"\"\n  );\n\n  MT(\"import_function_one_line_does_not_indent\",\n    \"[keyword import] [string \\\"DPI-C\\\"] [keyword function] [variable helloFromC];\",\n    \"\"\n  );\n\n  MT(\"import_task_one_line_does_not_indent\",\n    \"[keyword import] [string \\\"DPI-C\\\"] [keyword task] [variable helloFromC];\",\n    \"\"\n  );\n\n  MT(\"import_package_single_line_does_not_indent\",\n    \"[keyword import] [variable p]::[variable x];\",\n    \"[keyword import] [variable p]::[variable y];\",\n    \"\"\n  );\n\n  MT(\"covergroup_with_function_indents_properly\",\n    \"[keyword covergroup] [variable cg] [keyword with] [keyword function] [variable sample][bracket (][keyword bit] [variable b][bracket )];\",\n    \"    [variable c] : [keyword coverpoint] [variable c];\",\n    \"[keyword endgroup]: [variable cg]\",\n    \"\"\n  );\n\n  MT(\"indent_uvm_macros\",\n     /**\n      *  `uvm_object_utils_begin(foo)\n      *    `uvm_field_event(foo, UVM_ALL_ON)\n      *  `uvm_object_utils_end\n      */\n     \"[def `uvm_object_utils_begin][bracket (][variable foo][bracket )]\",\n     \"    [def `uvm_field_event][bracket (][variable foo], [variable UVM_ALL_ON][bracket )]\",\n     \"[def `uvm_object_utils_end]\",\n     \"\"\n  );\n\n  MT(\"indent_uvm_macros2\",\n     /**\n      * `uvm_do_with(mem_read,{\n      *    bar_nb == 0;\n      * })\n      */\n     \"[def `uvm_do_with][bracket (][variable mem_read],[bracket {]\",\n     \"    [variable bar_nb] [meta ==] [number 0];\",\n     \"[bracket })]\",\n     \"\"\n  );\n\n  MT(\"indent_wait_disable_fork\",\n     /**\n      * virtual task body();\n      *    repeat (20) begin\n      *       fork\n      *          `uvm_create_on(t,p_seq)\n      *       join_none\n      *    end\n      *    wait fork;\n      *    disable fork;\n      * endtask : body\n      */\n     \"[keyword virtual] [keyword task] [variable body][bracket ()];\",\n     \"    [keyword repeat] [bracket (][number 20][bracket )] [keyword begin]\",\n     \"        [keyword fork]\",\n     \"            [def `uvm_create_on][bracket (][variable t],[variable p_seq][bracket )]\",\n     \"        [keyword join_none]\",\n     \"    [keyword end]\",\n     \"    [keyword wait] [keyword fork];\",\n     \"    [keyword disable] [keyword fork];\",\n     \"[keyword endtask] : [variable body]\",\n     \"\"\n  );\n\n  MT(\"indent_typedef_class\",\n     /**\n      * typedef class asdf;\n      * typedef p p_t[];\n      * typedef enum {\n      *    ASDF\n      * } t;\n      */\n     \"[keyword typedef] [keyword class] [variable asdf];\",\n     \"[keyword typedef] [variable p] [variable p_t][bracket [[]]];\",\n     \"[keyword typedef] [keyword enum] [bracket {]\",\n     \"    [variable ASDF]\",\n     \"[bracket }] [variable t];\",\n     \"\"\n  );\n\n  MT(\"indent_case_with_macro\",\n     /**\n      * // It should be assumed that Macros can have ';' inside, or 'begin'/'end' blocks.\n      * // As such, 'case' statement should indent correctly with macros inside.\n      * case(foo)\n      *    ASDF : this.foo = seqNum;\n      *    ABCD : `update(f)\n      *    EFGH : `update(g)\n      * endcase\n      */\n     \"[keyword case][bracket (][variable foo][bracket )]\",\n     \"    [variable ASDF] : [keyword this].[variable foo] [meta =] [variable seqNum];\",\n     \"    [variable ABCD] : [def `update][bracket (][variable f][bracket )]\",\n     \"    [variable EFGH] : [def `update][bracket (][variable g][bracket )]\",\n     \"[keyword endcase]\",\n     \"\"\n  );\n\n  MT(\"indent_extern_function\",\n     /**\n      * extern virtual function void do(ref packet trans);\n      * extern virtual function void do2(ref packet trans);\n      */\n     \"[keyword extern] [keyword virtual] [keyword function] [keyword void] [variable do1][bracket (][keyword ref] [variable packet] [variable trans][bracket )];\",\n     \"[keyword extern] [keyword virtual] [keyword function] [keyword void] [variable do2][bracket (][keyword ref] [variable packet] [variable trans][bracket )];\",\n     \"\"\n  );\n\n  MT(\"indent_assignment\",\n     /**\n      * for (int i=1;i < fun;i++) begin\n      *    foo = 2 << asdf || 11'h35 >> abcd\n      *          && 8'h6 | 1'b1;\n      * end\n      */\n     \"[keyword for] [bracket (][keyword int] [variable i][meta =][number 1];[variable i] [meta <] [variable fun];[variable i][meta ++][bracket )] [keyword begin]\",\n     \"    [variable foo] [meta =] [number 2] [meta <<] [variable asdf] [meta ||] [number 11'h35] [meta >>] [variable abcd]\",\n     \"          [meta &&] [number 8'h6] [meta |] [number 1'b1];\",\n     \"[keyword end]\",\n     \"\"\n  );\n\n  MT(\"indent_foreach_constraint\",\n     /**\n      * `uvm_rand_send_with(wrTlp, {\n      *    length ==1;\n      *    foreach (Data[i]) {\n      *       payload[i] == Data[i];\n      *    }\n      * })\n      */\n     \"[def `uvm_rand_send_with][bracket (][variable wrTlp], [bracket {]\",\n     \"    [variable length] [meta ==][number 1];\",\n     \"    [keyword foreach] [bracket (][variable Data][bracket [[][variable i][bracket ]])] [bracket {]\",\n     \"        [variable payload][bracket [[][variable i][bracket ]]] [meta ==] [variable Data][bracket [[][variable i][bracket ]]];\",\n     \"    [bracket }]\",\n     \"[bracket })]\",\n     \"\"\n  );\n\n  MT(\"indent_compiler_directives\",\n     /**\n      * `ifdef DUT\n      * `else\n      *     `ifndef FOO\n      *         `define FOO\n      *     `endif\n      * `endif\n      * `timescale 1ns/1ns\n      */\n     \"[def `ifdef] [variable DUT]\",\n     \"[def `else]\",\n     \"    [def `ifndef] [variable FOO]\",\n     \"        [def `define] [variable FOO]\",\n     \"    [def `endif]\",\n     \"[def `endif]\",\n     \"[def `timescale] [number 1][variable ns][meta /][number 1][variable ns]\",\n     \"\"\n  );\n\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/verilog/verilog.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"verilog\", function(config, parserConfig) {\n\n  var indentUnit = config.indentUnit,\n      statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,\n      dontAlignCalls = parserConfig.dontAlignCalls,\n      // compilerDirectivesUseRegularIndentation - If set, Compiler directive\n      // indentation follows the same rules as everything else. Otherwise if\n      // false, compiler directives will track their own indentation.\n      // For example, `ifdef nested inside another `ifndef will be indented,\n      // but a `ifdef inside a function block may not be indented.\n      compilerDirectivesUseRegularIndentation = parserConfig.compilerDirectivesUseRegularIndentation,\n      noIndentKeywords = parserConfig.noIndentKeywords || [],\n      multiLineStrings = parserConfig.multiLineStrings,\n      hooks = parserConfig.hooks || {};\n\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  /**\n   * Keywords from IEEE 1800-2012\n   */\n  var keywords = words(\n    \"accept_on alias always always_comb always_ff always_latch and assert assign assume automatic before begin bind \" +\n    \"bins binsof bit break buf bufif0 bufif1 byte case casex casez cell chandle checker class clocking cmos config \" +\n    \"const constraint context continue cover covergroup coverpoint cross deassign default defparam design disable \" +\n    \"dist do edge else end endcase endchecker endclass endclocking endconfig endfunction endgenerate endgroup \" +\n    \"endinterface endmodule endpackage endprimitive endprogram endproperty endspecify endsequence endtable endtask \" +\n    \"enum event eventually expect export extends extern final first_match for force foreach forever fork forkjoin \" +\n    \"function generate genvar global highz0 highz1 if iff ifnone ignore_bins illegal_bins implements implies import \" +\n    \"incdir include initial inout input inside instance int integer interconnect interface intersect join join_any \" +\n    \"join_none large let liblist library local localparam logic longint macromodule matches medium modport module \" +\n    \"nand negedge nettype new nexttime nmos nor noshowcancelled not notif0 notif1 null or output package packed \" +\n    \"parameter pmos posedge primitive priority program property protected pull0 pull1 pulldown pullup \" +\n    \"pulsestyle_ondetect pulsestyle_onevent pure rand randc randcase randsequence rcmos real realtime ref reg \" +\n    \"reject_on release repeat restrict return rnmos rpmos rtran rtranif0 rtranif1 s_always s_eventually s_nexttime \" +\n    \"s_until s_until_with scalared sequence shortint shortreal showcancelled signed small soft solve specify \" +\n    \"specparam static string strong strong0 strong1 struct super supply0 supply1 sync_accept_on sync_reject_on \" +\n    \"table tagged task this throughout time timeprecision timeunit tran tranif0 tranif1 tri tri0 tri1 triand trior \" +\n    \"trireg type typedef union unique unique0 unsigned until until_with untyped use uwire var vectored virtual void \" +\n    \"wait wait_order wand weak weak0 weak1 while wildcard wire with within wor xnor xor\");\n\n  /** Operators from IEEE 1800-2012\n     unary_operator ::=\n       + | - | ! | ~ | & | ~& | | | ~| | ^ | ~^ | ^~\n     binary_operator ::=\n       + | - | * | / | % | == | != | === | !== | ==? | !=? | && | || | **\n       | < | <= | > | >= | & | | | ^ | ^~ | ~^ | >> | << | >>> | <<<\n       | -> | <->\n     inc_or_dec_operator ::= ++ | --\n     unary_module_path_operator ::=\n       ! | ~ | & | ~& | | | ~| | ^ | ~^ | ^~\n     binary_module_path_operator ::=\n       == | != | && | || | & | | | ^ | ^~ | ~^\n  */\n  var isOperatorChar = /[\\+\\-\\*\\/!~&|^%=?:<>]/;\n  var isBracketChar = /[\\[\\]{}()]/;\n\n  var unsignedNumber = /\\d[0-9_]*/;\n  var decimalLiteral = /\\d*\\s*'s?d\\s*\\d[0-9_]*/i;\n  var binaryLiteral = /\\d*\\s*'s?b\\s*[xz01][xz01_]*/i;\n  var octLiteral = /\\d*\\s*'s?o\\s*[xz0-7][xz0-7_]*/i;\n  var hexLiteral = /\\d*\\s*'s?h\\s*[0-9a-fxz?][0-9a-fxz?_]*/i;\n  var realLiteral = /(\\d[\\d_]*(\\.\\d[\\d_]*)?E-?[\\d_]+)|(\\d[\\d_]*\\.\\d[\\d_]*)/i;\n\n  var closingBracketOrWord = /^((`?\\w+)|[)}\\]])/;\n  var closingBracket = /[)}\\]]/;\n  var compilerDirectiveRegex      = new RegExp(\n    \"^(`(?:ifdef|ifndef|elsif|else|endif|undef|undefineall|define|include|begin_keywords|celldefine|default|\" +\n    \"nettype|end_keywords|endcelldefine|line|nounconnected_drive|pragma|resetall|timescale|unconnected_drive))\\\\b\");\n  var compilerDirectiveBeginRegex = /^(`(?:ifdef|ifndef|elsif|else))\\b/;\n  var compilerDirectiveEndRegex   = /^(`(?:elsif|else|endif))\\b/;\n\n  var curPunc;\n  var curKeyword;\n\n  // Block openings which are closed by a matching keyword in the form of (\"end\" + keyword)\n  // E.g. \"task\" => \"endtask\"\n  var blockKeywords = words(\n    \"case checker class clocking config function generate interface module package \" +\n    \"primitive program property specify sequence table task\"\n  );\n\n  // Opening/closing pairs\n  var openClose = {};\n  for (var keyword in blockKeywords) {\n    openClose[keyword] = \"end\" + keyword;\n  }\n  openClose[\"begin\"] = \"end\";\n  openClose[\"casex\"] = \"endcase\";\n  openClose[\"casez\"] = \"endcase\";\n  openClose[\"do\"   ] = \"while\";\n  openClose[\"fork\" ] = \"join;join_any;join_none\";\n  openClose[\"covergroup\"] = \"endgroup\";\n  openClose[\"macro_begin\"] = \"macro_end\";\n\n  for (var i in noIndentKeywords) {\n    var keyword = noIndentKeywords[i];\n    if (openClose[keyword]) {\n      openClose[keyword] = undefined;\n    }\n  }\n\n  // Keywords which open statements that are ended with a semi-colon\n  var statementKeywords = words(\"always always_comb always_ff always_latch assert assign assume else export for foreach forever if import initial repeat while extern typedef\");\n\n  function tokenBase(stream, state) {\n    var ch = stream.peek(), style;\n    if (hooks[ch] && (style = hooks[ch](stream, state)) != false) return style;\n    if (hooks.tokenBase && (style = hooks.tokenBase(stream, state)) != false)\n      return style;\n\n    if (/[,;:\\.]/.test(ch)) {\n      curPunc = stream.next();\n      return null;\n    }\n    if (isBracketChar.test(ch)) {\n      curPunc = stream.next();\n      return \"bracket\";\n    }\n    // Macros (tick-defines)\n    if (ch == '`') {\n      stream.next();\n      if (stream.eatWhile(/[\\w\\$_]/)) {\n        var cur = stream.current();\n        curKeyword = cur;\n        // Macros that end in _begin, are start of block and end with _end\n        if (cur.startsWith(\"`uvm_\") && cur.endsWith(\"_begin\")) {\n          var keywordClose = curKeyword.substr(0,curKeyword.length - 5) + \"end\";\n          openClose[cur] = keywordClose;\n          curPunc = \"newblock\";\n        } else {\n          stream.eatSpace();\n          if (stream.peek() == '(') {\n            // Check if this is a block\n            curPunc = \"newmacro\";\n          }\n          var withSpace = stream.current();\n          // Move the stream back before the spaces\n          stream.backUp(withSpace.length - cur.length);\n        }\n        return \"def\";\n      } else {\n        return null;\n      }\n    }\n    // System calls\n    if (ch == '$') {\n      stream.next();\n      if (stream.eatWhile(/[\\w\\$_]/)) {\n        return \"meta\";\n      } else {\n        return null;\n      }\n    }\n    // Time literals\n    if (ch == '#') {\n      stream.next();\n      stream.eatWhile(/[\\d_.]/);\n      return \"def\";\n    }\n    // Event\n    if (ch == '@') {\n      stream.next();\n      stream.eatWhile(/[@]/);\n      return \"def\";\n    }\n    // Strings\n    if (ch == '\"') {\n      stream.next();\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    }\n    // Comments\n    if (ch == \"/\") {\n      stream.next();\n      if (stream.eat(\"*\")) {\n        state.tokenize = tokenComment;\n        return tokenComment(stream, state);\n      }\n      if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n      stream.backUp(1);\n    }\n\n    // Numeric literals\n    if (stream.match(realLiteral) ||\n        stream.match(decimalLiteral) ||\n        stream.match(binaryLiteral) ||\n        stream.match(octLiteral) ||\n        stream.match(hexLiteral) ||\n        stream.match(unsignedNumber) ||\n        stream.match(realLiteral)) {\n      return \"number\";\n    }\n\n    // Operators\n    if (stream.eatWhile(isOperatorChar)) {\n      curPunc = stream.current();\n      return \"meta\";\n    }\n\n    // Keywords / plain variables\n    if (stream.eatWhile(/[\\w\\$_]/)) {\n      var cur = stream.current();\n      if (keywords[cur]) {\n        if (openClose[cur]) {\n          curPunc = \"newblock\";\n          if (cur === \"fork\") {\n            // Fork can be a statement instead of block in cases of:\n            // \"disable fork;\" and \"wait fork;\" (trailing semicolon)\n            stream.eatSpace()\n            if (stream.peek() == ';') {\n              curPunc = \"newstatement\";\n            }\n            stream.backUp(stream.current().length - cur.length);\n          }\n        }\n        if (statementKeywords[cur]) {\n          curPunc = \"newstatement\";\n        }\n        curKeyword = cur;\n        return \"keyword\";\n      }\n      return \"variable\";\n    }\n\n    stream.next();\n    return null;\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {end = true; break;}\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (end || !(escaped || multiLineStrings))\n        state.tokenize = tokenBase;\n      return \"string\";\n    };\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return \"comment\";\n  }\n\n  function Context(indented, column, type, scopekind, align, prev) {\n    this.indented = indented;\n    this.column = column;\n    this.type = type;\n    this.scopekind = scopekind;\n    this.align = align;\n    this.prev = prev;\n  }\n  function pushContext(state, col, type, scopekind) {\n    var indent = state.indented;\n    var c = new Context(indent, col, type, scopekind ? scopekind : \"\", null, state.context);\n    return state.context = c;\n  }\n  function popContext(state) {\n    var t = state.context.type;\n    if (t == \")\" || t == \"]\" || t == \"}\") {\n      state.indented = state.context.indented;\n    }\n    return state.context = state.context.prev;\n  }\n\n  function isClosing(text, contextClosing) {\n    if (text == contextClosing) {\n      return true;\n    } else {\n      // contextClosing may be multiple keywords separated by ;\n      var closingKeywords = contextClosing.split(\";\");\n      for (var i in closingKeywords) {\n        if (text == closingKeywords[i]) {\n          return true;\n        }\n      }\n      return false;\n    }\n  }\n\n  function isInsideScopeKind(ctx, scopekind) {\n    if (ctx == null) {\n      return false;\n    }\n    if (ctx.scopekind === scopekind) {\n      return true;\n    }\n    return isInsideScopeKind(ctx.prev, scopekind);\n  }\n\n  function buildElectricInputRegEx() {\n    // Reindentation should occur on any bracket char: {}()[]\n    // or on a match of any of the block closing keywords, at\n    // the end of a line\n    var allClosings = [];\n    for (var i in openClose) {\n      if (openClose[i]) {\n        var closings = openClose[i].split(\";\");\n        for (var j in closings) {\n          allClosings.push(closings[j]);\n        }\n      }\n    }\n    var re = new RegExp(\"[{}()\\\\[\\\\]]|(\" + allClosings.join(\"|\") + \")$\");\n    return re;\n  }\n\n  // Interface\n  return {\n\n    // Regex to force current line to reindent\n    electricInput: buildElectricInputRegEx(),\n\n    startState: function(basecolumn) {\n      var state = {\n        tokenize: null,\n        context: new Context((basecolumn || 0) - indentUnit, 0, \"top\", \"top\", false),\n        indented: 0,\n        compilerDirectiveIndented: 0,\n        startOfLine: true\n      };\n      if (hooks.startState) hooks.startState(state);\n      return state;\n    },\n\n    token: function(stream, state) {\n      var ctx = state.context;\n      if (stream.sol()) {\n        if (ctx.align == null) ctx.align = false;\n        state.indented = stream.indentation();\n        state.startOfLine = true;\n      }\n      if (hooks.token) {\n        // Call hook, with an optional return value of a style to override verilog styling.\n        var style = hooks.token(stream, state);\n        if (style !== undefined) {\n          return style;\n        }\n      }\n      if (stream.eatSpace()) return null;\n      curPunc = null;\n      curKeyword = null;\n      var style = (state.tokenize || tokenBase)(stream, state);\n      if (style == \"comment\" || style == \"meta\" || style == \"variable\") {\n        if (((curPunc === \"=\") || (curPunc === \"<=\")) && !isInsideScopeKind(ctx, \"assignment\")) {\n          // '<=' could be nonblocking assignment or lessthan-equals (which shouldn't cause indent)\n          //      Search through the context to see if we are already in an assignment.\n          // '=' could be inside port declaration with comma or ')' afterward, or inside for(;;) block.\n          pushContext(state, stream.column() + curPunc.length, \"assignment\", \"assignment\");\n          if (ctx.align == null) ctx.align = true;\n        }\n        return style;\n      }\n      if (ctx.align == null) ctx.align = true;\n\n      var isClosingAssignment = ctx.type == \"assignment\" &&\n        closingBracket.test(curPunc) && ctx.prev && ctx.prev.type === curPunc;\n      if (curPunc == ctx.type || isClosingAssignment) {\n        if (isClosingAssignment) {\n          ctx = popContext(state);\n        }\n        ctx = popContext(state);\n        if (curPunc == \")\") {\n          // Handle closing macros, assuming they could have a semicolon or begin/end block inside.\n          if (ctx && (ctx.type === \"macro\")) {\n            ctx = popContext(state);\n            while (ctx && (ctx.type == \"statement\" || ctx.type == \"assignment\")) ctx = popContext(state);\n          }\n        } else if (curPunc == \"}\") {\n          // Handle closing statements like constraint block: \"foreach () {}\" which\n          // do not have semicolon at end.\n          if (ctx && (ctx.type === \"statement\")) {\n            while (ctx && (ctx.type == \"statement\")) ctx = popContext(state);\n          }\n        }\n      } else if (((curPunc == \";\" || curPunc == \",\") && (ctx.type == \"statement\" || ctx.type == \"assignment\")) ||\n               (ctx.type && isClosing(curKeyword, ctx.type))) {\n        ctx = popContext(state);\n        while (ctx && (ctx.type == \"statement\" || ctx.type == \"assignment\")) ctx = popContext(state);\n      } else if (curPunc == \"{\") {\n        pushContext(state, stream.column(), \"}\");\n      } else if (curPunc == \"[\") {\n        pushContext(state, stream.column(), \"]\");\n      } else if (curPunc == \"(\") {\n        pushContext(state, stream.column(), \")\");\n      } else if (ctx && ctx.type == \"endcase\" && curPunc == \":\") {\n        pushContext(state, stream.column(), \"statement\", \"case\");\n      } else if (curPunc == \"newstatement\") {\n        pushContext(state, stream.column(), \"statement\", curKeyword);\n      } else if (curPunc == \"newblock\") {\n        if (curKeyword == \"function\" && ctx && (ctx.type == \"statement\" || ctx.type == \"endgroup\")) {\n          // The 'function' keyword can appear in some other contexts where it actually does not\n          // indicate a function (import/export DPI and covergroup definitions).\n          // Do nothing in this case\n        } else if (curKeyword == \"task\" && ctx && ctx.type == \"statement\") {\n          // Same thing for task\n        } else if (curKeyword == \"class\" && ctx && ctx.type == \"statement\") {\n          // Same thing for class (e.g. typedef)\n        } else {\n          var close = openClose[curKeyword];\n          pushContext(state, stream.column(), close, curKeyword);\n        }\n      } else if (curPunc == \"newmacro\" || (curKeyword && curKeyword.match(compilerDirectiveRegex))) {\n        if (curPunc == \"newmacro\") {\n          // Macros (especially if they have parenthesis) potentially have a semicolon\n          // or complete statement/block inside, and should be treated as such.\n          pushContext(state, stream.column(), \"macro\", \"macro\");\n        }\n        if (curKeyword.match(compilerDirectiveEndRegex)) {\n          state.compilerDirectiveIndented -= statementIndentUnit;\n        }\n        if (curKeyword.match(compilerDirectiveBeginRegex)) {\n          state.compilerDirectiveIndented += statementIndentUnit;\n        }\n      }\n\n      state.startOfLine = false;\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;\n      if (hooks.indent) {\n        var fromHook = hooks.indent(state);\n        if (fromHook >= 0) return fromHook;\n      }\n      var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);\n      if (ctx.type == \"statement\" && firstChar == \"}\") ctx = ctx.prev;\n      var closing = false;\n      var possibleClosing = textAfter.match(closingBracketOrWord);\n      if (possibleClosing)\n        closing = isClosing(possibleClosing[0], ctx.type);\n      if (!compilerDirectivesUseRegularIndentation && textAfter.match(compilerDirectiveRegex)) {\n        if (textAfter.match(compilerDirectiveEndRegex)) {\n          return state.compilerDirectiveIndented - statementIndentUnit;\n        }\n        return state.compilerDirectiveIndented;\n      }\n      if (ctx.type == \"statement\") return ctx.indented + (firstChar == \"{\" ? 0 : statementIndentUnit);\n      else if ((closingBracket.test(ctx.type) || ctx.type == \"assignment\")\n        && ctx.align && !dontAlignCalls) return ctx.column + (closing ? 0 : 1);\n      else if (ctx.type == \")\" && !closing) return ctx.indented + statementIndentUnit;\n      else return ctx.indented + (closing ? 0 : indentUnit);\n    },\n\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    lineComment: \"//\",\n    fold: \"indent\"\n  };\n});\n\n  CodeMirror.defineMIME(\"text/x-verilog\", {\n    name: \"verilog\"\n  });\n\n  CodeMirror.defineMIME(\"text/x-systemverilog\", {\n    name: \"verilog\"\n  });\n\n\n\n  // TL-Verilog mode.\n  // See tl-x.org for language spec.\n  // See the mode in action at makerchip.com.\n  // Contact: steve.hoover@redwoodeda.com\n\n  // TLV Identifier prefixes.\n  // Note that sign is not treated separately, so \"+/-\" versions of numeric identifiers\n  // are included.\n  var tlvIdentifierStyle = {\n    \"|\": \"link\",\n    \">\": \"property\",  // Should condition this off for > TLV 1c.\n    \"$\": \"variable\",\n    \"$$\": \"variable\",\n    \"?$\": \"qualifier\",\n    \"?*\": \"qualifier\",\n    \"-\": \"hr\",\n    \"/\": \"property\",\n    \"/-\": \"property\",\n    \"@\": \"variable-3\",\n    \"@-\": \"variable-3\",\n    \"@++\": \"variable-3\",\n    \"@+=\": \"variable-3\",\n    \"@+=-\": \"variable-3\",\n    \"@--\": \"variable-3\",\n    \"@-=\": \"variable-3\",\n    \"%+\": \"tag\",\n    \"%-\": \"tag\",\n    \"%\": \"tag\",\n    \">>\": \"tag\",\n    \"<<\": \"tag\",\n    \"<>\": \"tag\",\n    \"#\": \"tag\",  // Need to choose a style for this.\n    \"^\": \"attribute\",\n    \"^^\": \"attribute\",\n    \"^!\": \"attribute\",\n    \"*\": \"variable-2\",\n    \"**\": \"variable-2\",\n    \"\\\\\": \"keyword\",\n    \"\\\"\": \"comment\"\n  };\n\n  // Lines starting with these characters define scope (result in indentation).\n  var tlvScopePrefixChars = {\n    \"/\": \"beh-hier\",\n    \">\": \"beh-hier\",\n    \"-\": \"phys-hier\",\n    \"|\": \"pipe\",\n    \"?\": \"when\",\n    \"@\": \"stage\",\n    \"\\\\\": \"keyword\"\n  };\n  var tlvIndentUnit = 3;\n  var tlvTrackStatements = false;\n  var tlvIdentMatch = /^([~!@#\\$%\\^&\\*-\\+=\\?\\/\\\\\\|'\"<>]+)([\\d\\w_]*)/;  // Matches an identifier.\n  // Note that ':' is excluded, because of it's use in [:].\n  var tlvFirstLevelIndentMatch = /^[! ]  /;\n  var tlvLineIndentationMatch = /^[! ] */;\n  var tlvCommentMatch = /^\\/[\\/\\*]/;\n\n\n  // Returns a style specific to the scope at the given indentation column.\n  // Type is one of: \"indent\", \"scope-ident\", \"before-scope-ident\".\n  function tlvScopeStyle(state, indentation, type) {\n    // Begin scope.\n    var depth = indentation / tlvIndentUnit;  // TODO: Pass this in instead.\n    return \"tlv-\" + state.tlvIndentationStyle[depth] + \"-\" + type;\n  }\n\n  // Return true if the next thing in the stream is an identifier with a mnemonic.\n  function tlvIdentNext(stream) {\n    var match;\n    return (match = stream.match(tlvIdentMatch, false)) && match[2].length > 0;\n  }\n\n  CodeMirror.defineMIME(\"text/x-tlv\", {\n    name: \"verilog\",\n\n    hooks: {\n\n      electricInput: false,\n\n\n      // Return undefined for verilog tokenizing, or style for TLV token (null not used).\n      // Standard CM styles are used for most formatting, but some TL-Verilog-specific highlighting\n      // can be enabled with the definition of cm-tlv-* styles, including highlighting for:\n      //   - M4 tokens\n      //   - TLV scope indentation\n      //   - Statement delimitation (enabled by tlvTrackStatements)\n      token: function(stream, state) {\n        var style = undefined;\n        var match;  // Return value of pattern matches.\n\n        // Set highlighting mode based on code region (TLV or SV).\n        if (stream.sol() && ! state.tlvInBlockComment) {\n          // Process region.\n          if (stream.peek() == '\\\\') {\n            style = \"def\";\n            stream.skipToEnd();\n            if (stream.string.match(/\\\\SV/)) {\n              state.tlvCodeActive = false;\n            } else if (stream.string.match(/\\\\TLV/)){\n              state.tlvCodeActive = true;\n            }\n          }\n          // Correct indentation in the face of a line prefix char.\n          if (state.tlvCodeActive && stream.pos == 0 &&\n              (state.indented == 0) && (match = stream.match(tlvLineIndentationMatch, false))) {\n            state.indented = match[0].length;\n          }\n\n          // Compute indentation state:\n          //   o Auto indentation on next line\n          //   o Indentation scope styles\n          var indented = state.indented;\n          var depth = indented / tlvIndentUnit;\n          if (depth <= state.tlvIndentationStyle.length) {\n            // not deeper than current scope\n\n            var blankline = stream.string.length == indented;\n            var chPos = depth * tlvIndentUnit;\n            if (chPos < stream.string.length) {\n              var bodyString = stream.string.slice(chPos);\n              var ch = bodyString[0];\n              if (tlvScopePrefixChars[ch] && ((match = bodyString.match(tlvIdentMatch)) &&\n                  tlvIdentifierStyle[match[1]])) {\n                // This line begins scope.\n                // Next line gets indented one level.\n                indented += tlvIndentUnit;\n                // Style the next level of indentation (except non-region keyword identifiers,\n                //   which are statements themselves)\n                if (!(ch == \"\\\\\" && chPos > 0)) {\n                  state.tlvIndentationStyle[depth] = tlvScopePrefixChars[ch];\n                  if (tlvTrackStatements) {state.statementComment = false;}\n                  depth++;\n                }\n              }\n            }\n            // Clear out deeper indentation levels unless line is blank.\n            if (!blankline) {\n              while (state.tlvIndentationStyle.length > depth) {\n                state.tlvIndentationStyle.pop();\n              }\n            }\n          }\n          // Set next level of indentation.\n          state.tlvNextIndent = indented;\n        }\n\n        if (state.tlvCodeActive) {\n          // Highlight as TLV.\n\n          var beginStatement = false;\n          if (tlvTrackStatements) {\n            // This starts a statement if the position is at the scope level\n            // and we're not within a statement leading comment.\n            beginStatement =\n                   (stream.peek() != \" \") &&   // not a space\n                   (style === undefined) &&    // not a region identifier\n                   !state.tlvInBlockComment && // not in block comment\n                   //!stream.match(tlvCommentMatch, false) && // not comment start\n                   (stream.column() == state.tlvIndentationStyle.length * tlvIndentUnit);  // at scope level\n            if (beginStatement) {\n              if (state.statementComment) {\n                // statement already started by comment\n                beginStatement = false;\n              }\n              state.statementComment =\n                   stream.match(tlvCommentMatch, false); // comment start\n            }\n          }\n\n          var match;\n          if (style !== undefined) {\n            // Region line.\n            style += \" \" + tlvScopeStyle(state, 0, \"scope-ident\")\n          } else if (((stream.pos / tlvIndentUnit) < state.tlvIndentationStyle.length) &&\n                     (match = stream.match(stream.sol() ? tlvFirstLevelIndentMatch : /^   /))) {\n            // Indentation\n            style = // make this style distinct from the previous one to prevent\n                    // codemirror from combining spans\n                    \"tlv-indent-\" + (((stream.pos % 2) == 0) ? \"even\" : \"odd\") +\n                    // and style it\n                    \" \" + tlvScopeStyle(state, stream.pos - tlvIndentUnit, \"indent\");\n            // Style the line prefix character.\n            if (match[0].charAt(0) == \"!\") {\n              style += \" tlv-alert-line-prefix\";\n            }\n            // Place a class before a scope identifier.\n            if (tlvIdentNext(stream)) {\n              style += \" \" + tlvScopeStyle(state, stream.pos, \"before-scope-ident\");\n            }\n          } else if (state.tlvInBlockComment) {\n            // In a block comment.\n            if (stream.match(/^.*?\\*\\//)) {\n              // Exit block comment.\n              state.tlvInBlockComment = false;\n              if (tlvTrackStatements && !stream.eol()) {\n                // Anything after comment is assumed to be real statement content.\n                state.statementComment = false;\n              }\n            } else {\n              stream.skipToEnd();\n            }\n            style = \"comment\";\n          } else if ((match = stream.match(tlvCommentMatch)) && !state.tlvInBlockComment) {\n            // Start comment.\n            if (match[0] == \"//\") {\n              // Line comment.\n              stream.skipToEnd();\n            } else {\n              // Block comment.\n              state.tlvInBlockComment = true;\n            }\n            style = \"comment\";\n          } else if (match = stream.match(tlvIdentMatch)) {\n            // looks like an identifier (or identifier prefix)\n            var prefix = match[1];\n            var mnemonic = match[2];\n            if (// is identifier prefix\n                tlvIdentifierStyle.hasOwnProperty(prefix) &&\n                // has mnemonic or we're at the end of the line (maybe it hasn't been typed yet)\n                (mnemonic.length > 0 || stream.eol())) {\n              style = tlvIdentifierStyle[prefix];\n              if (stream.column() == state.indented) {\n                // Begin scope.\n                style += \" \" + tlvScopeStyle(state, stream.column(), \"scope-ident\")\n              }\n            } else {\n              // Just swallow one character and try again.\n              // This enables subsequent identifier match with preceding symbol character, which\n              //   is legal within a statement.  (E.g., !$reset).  It also enables detection of\n              //   comment start with preceding symbols.\n              stream.backUp(stream.current().length - 1);\n              style = \"tlv-default\";\n            }\n          } else if (stream.match(/^\\t+/)) {\n            // Highlight tabs, which are illegal.\n            style = \"tlv-tab\";\n          } else if (stream.match(/^[\\[\\]{}\\(\\);\\:]+/)) {\n            // [:], (), {}, ;.\n            style = \"meta\";\n          } else if (match = stream.match(/^[mM]4([\\+_])?[\\w\\d_]*/)) {\n            // m4 pre proc\n            style = (match[1] == \"+\") ? \"tlv-m4-plus\" : \"tlv-m4\";\n          } else if (stream.match(/^ +/)){\n            // Skip over spaces.\n            if (stream.eol()) {\n              // Trailing spaces.\n              style = \"error\";\n            } else {\n              // Non-trailing spaces.\n              style = \"tlv-default\";\n            }\n          } else if (stream.match(/^[\\w\\d_]+/)) {\n            // alpha-numeric token.\n            style = \"number\";\n          } else {\n            // Eat the next char w/ no formatting.\n            stream.next();\n            style = \"tlv-default\";\n          }\n          if (beginStatement) {\n            style += \" tlv-statement\";\n          }\n        } else {\n          if (stream.match(/^[mM]4([\\w\\d_]*)/)) {\n            // m4 pre proc\n            style = \"tlv-m4\";\n          }\n        }\n        return style;\n      },\n\n      indent: function(state) {\n        return (state.tlvCodeActive == true) ? state.tlvNextIndent : -1;\n      },\n\n      startState: function(state) {\n        state.tlvIndentationStyle = [];  // Styles to use for each level of indentation.\n        state.tlvCodeActive = true;  // True when we're in a TLV region (and at beginning of file).\n        state.tlvNextIndent = -1;    // The number of spaces to autoindent the next line if tlvCodeActive.\n        state.tlvInBlockComment = false;  // True inside /**/ comment.\n        if (tlvTrackStatements) {\n          state.statementComment = false;  // True inside a statement's header comment.\n        }\n      }\n\n    }\n  });\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/vhdl/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: VHDL mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"vhdl.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">VHDL</a>\n  </ul>\n</div>\n\n<article>\n<h2>VHDL mode</h2>\n\n<div><textarea id=\"code\" name=\"code\">\nLIBRARY ieee;\nUSE ieee.std_logic_1164.ALL;\nUSE ieee.numeric_std.ALL;\n\nENTITY tb IS\nEND tb;\n\nARCHITECTURE behavior OF tb IS\n   --Inputs\n   signal a : unsigned(2 downto 0) := (others => '0');\n   signal b : unsigned(2 downto 0) := (others => '0');\n    --Outputs\n   signal a_eq_b : std_logic;\n   signal a_le_b : std_logic;\n   signal a_gt_b : std_logic;\n\n    signal i,j : integer;\n\nBEGIN\n\n    -- Instantiate the Unit Under Test (UUT)\n   uut: entity work.comparator PORT MAP (\n          a => a,\n          b => b,\n          a_eq_b => a_eq_b,\n          a_le_b => a_le_b,\n          a_gt_b => a_gt_b\n        );\n\n   -- Stimulus process\n   stim_proc: process\n   begin\n        for i in 0 to 8 loop\n            for j in 0 to 8 loop\n                a <= to_unsigned(i,3); --integer to unsigned type conversion\n                b <= to_unsigned(j,3);\n                wait for 10 ns;\n            end loop;\n        end loop;\n   end process;\n\nEND;\n</textarea></div>\n\n<script>\n  var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n    lineNumbers: true,\n    matchBrackets: true,\n    mode: {\n      name: \"vhdl\",\n    }\n  });\n</script>\n\n<p>\nSyntax highlighting and indentation for the VHDL language.\n<h2>Configuration options:</h2>\n  <ul>\n    <li><strong>atoms</strong> - List of atom words. Default: \"null\"</li>\n    <li><strong>hooks</strong> - List of meta hooks. Default: [\"`\", \"$\"]</li>\n    <li><strong>multiLineStrings</strong> - Whether multi-line strings are accepted. Default: false</li>\n  </ul>\n</p>\n\n<p><strong>MIME types defined:</strong> <code>text/x-vhdl</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/vhdl/vhdl.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Originally written by Alf Nielsen, re-written by Michael Zhou\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nfunction words(str) {\n  var obj = {}, words = str.split(\",\");\n  for (var i = 0; i < words.length; ++i) {\n    var allCaps = words[i].toUpperCase();\n    var firstCap = words[i].charAt(0).toUpperCase() + words[i].slice(1);\n    obj[words[i]] = true;\n    obj[allCaps] = true;\n    obj[firstCap] = true;\n  }\n  return obj;\n}\n\nfunction metaHook(stream) {\n  stream.eatWhile(/[\\w\\$_]/);\n  return \"meta\";\n}\n\nCodeMirror.defineMode(\"vhdl\", function(config, parserConfig) {\n  var indentUnit = config.indentUnit,\n      atoms = parserConfig.atoms || words(\"null\"),\n      hooks = parserConfig.hooks || {\"`\": metaHook, \"$\": metaHook},\n      multiLineStrings = parserConfig.multiLineStrings;\n\n  var keywords = words(\"abs,access,after,alias,all,and,architecture,array,assert,attribute,begin,block,\" +\n      \"body,buffer,bus,case,component,configuration,constant,disconnect,downto,else,elsif,end,end block,end case,\" +\n      \"end component,end for,end generate,end if,end loop,end process,end record,end units,entity,exit,file,for,\" +\n      \"function,generate,generic,generic map,group,guarded,if,impure,in,inertial,inout,is,label,library,linkage,\" +\n      \"literal,loop,map,mod,nand,new,next,nor,null,of,on,open,or,others,out,package,package body,port,port map,\" +\n      \"postponed,procedure,process,pure,range,record,register,reject,rem,report,return,rol,ror,select,severity,signal,\" +\n      \"sla,sll,sra,srl,subtype,then,to,transport,type,unaffected,units,until,use,variable,wait,when,while,with,xnor,xor\");\n\n  var blockKeywords = words(\"architecture,entity,begin,case,port,else,elsif,end,for,function,if\");\n\n  var isOperatorChar = /[&|~><!\\)\\(*#%@+\\/=?\\:;}{,\\.\\^\\-\\[\\]]/;\n  var curPunc;\n\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (hooks[ch]) {\n      var result = hooks[ch](stream, state);\n      if (result !== false) return result;\n    }\n    if (ch == '\"') {\n      state.tokenize = tokenString2(ch);\n      return state.tokenize(stream, state);\n    }\n    if (ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    }\n    if (/[\\[\\]{}\\(\\),;\\:\\.]/.test(ch)) {\n      curPunc = ch;\n      return null;\n    }\n    if (/[\\d']/.test(ch)) {\n      stream.eatWhile(/[\\w\\.']/);\n      return \"number\";\n    }\n    if (ch == \"-\") {\n      if (stream.eat(\"-\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n    }\n    if (isOperatorChar.test(ch)) {\n      stream.eatWhile(isOperatorChar);\n      return \"operator\";\n    }\n    stream.eatWhile(/[\\w\\$_]/);\n    var cur = stream.current();\n    if (keywords.propertyIsEnumerable(cur.toLowerCase())) {\n      if (blockKeywords.propertyIsEnumerable(cur)) curPunc = \"newstatement\";\n      return \"keyword\";\n    }\n    if (atoms.propertyIsEnumerable(cur)) return \"atom\";\n    return \"variable\";\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {end = true; break;}\n        escaped = !escaped && next == \"--\";\n      }\n      if (end || !(escaped || multiLineStrings))\n        state.tokenize = tokenBase;\n      return \"string\";\n    };\n  }\n  function tokenString2(quote) {\n    return function(stream, state) {\n      var escaped = false, next, end = false;\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) {end = true; break;}\n        escaped = !escaped && next == \"--\";\n      }\n      if (end || !(escaped || multiLineStrings))\n        state.tokenize = tokenBase;\n      return \"string-2\";\n    };\n  }\n\n  function Context(indented, column, type, align, prev) {\n    this.indented = indented;\n    this.column = column;\n    this.type = type;\n    this.align = align;\n    this.prev = prev;\n  }\n  function pushContext(state, col, type) {\n    return state.context = new Context(state.indented, col, type, null, state.context);\n  }\n  function popContext(state) {\n    var t = state.context.type;\n    if (t == \")\" || t == \"]\" || t == \"}\")\n      state.indented = state.context.indented;\n    return state.context = state.context.prev;\n  }\n\n  // Interface\n  return {\n    startState: function(basecolumn) {\n      return {\n        tokenize: null,\n        context: new Context((basecolumn || 0) - indentUnit, 0, \"top\", false),\n        indented: 0,\n        startOfLine: true\n      };\n    },\n\n    token: function(stream, state) {\n      var ctx = state.context;\n      if (stream.sol()) {\n        if (ctx.align == null) ctx.align = false;\n        state.indented = stream.indentation();\n        state.startOfLine = true;\n      }\n      if (stream.eatSpace()) return null;\n      curPunc = null;\n      var style = (state.tokenize || tokenBase)(stream, state);\n      if (style == \"comment\" || style == \"meta\") return style;\n      if (ctx.align == null) ctx.align = true;\n\n      if ((curPunc == \";\" || curPunc == \":\") && ctx.type == \"statement\") popContext(state);\n      else if (curPunc == \"{\") pushContext(state, stream.column(), \"}\");\n      else if (curPunc == \"[\") pushContext(state, stream.column(), \"]\");\n      else if (curPunc == \"(\") pushContext(state, stream.column(), \")\");\n      else if (curPunc == \"}\") {\n        while (ctx.type == \"statement\") ctx = popContext(state);\n        if (ctx.type == \"}\") ctx = popContext(state);\n        while (ctx.type == \"statement\") ctx = popContext(state);\n      }\n      else if (curPunc == ctx.type) popContext(state);\n      else if (ctx.type == \"}\" || ctx.type == \"top\" || (ctx.type == \"statement\" && curPunc == \"newstatement\"))\n        pushContext(state, stream.column(), \"statement\");\n      state.startOfLine = false;\n      return style;\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize != tokenBase && state.tokenize != null) return 0;\n      var firstChar = textAfter && textAfter.charAt(0), ctx = state.context, closing = firstChar == ctx.type;\n      if (ctx.type == \"statement\") return ctx.indented + (firstChar == \"{\" ? 0 : indentUnit);\n      else if (ctx.align) return ctx.column + (closing ? 0 : 1);\n      else return ctx.indented + (closing ? 0 : indentUnit);\n    },\n\n    electricChars: \"{}\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-vhdl\", \"vhdl\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/vue/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Vue.js mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/mode/overlay.js\"></script>\n<script src=\"../../addon/mode/simple.js\"></script>\n<script src=\"../../addon/selection/selection-pointer.js\"></script>\n<script src=\"../xml/xml.js\"></script>\n<script src=\"../javascript/javascript.js\"></script>\n<script src=\"../css/css.js\"></script>\n<script src=\"../coffeescript/coffeescript.js\"></script>\n<script src=\"../sass/sass.js\"></script>\n<script src=\"../pug/pug.js\"></script>\n\n<script src=\"../handlebars/handlebars.js\"></script>\n<script src=\"../htmlmixed/htmlmixed.js\"></script>\n<script src=\"vue.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Vue.js mode</a>\n  </ul>\n</div>\n\n<article>\n<h2>Vue.js mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n<template>\n  <div class=\"sass\">I'm a {{mustache-like}} template</div>\n</template>\n\n<script lang=\"coffee\">\n  module.exports =\n    props: ['one', 'two', 'three']\n</script>\n\n<style lang=\"sass\">\n.sass\n  font-size: 18px\n</style>\n\n</textarea></form>\n    <script>\n      // Define an extended mixed-mode that understands vbscript and\n      // leaves mustache/handlebars embedded templates in html mode\n      var mixedMode = {\n        name: \"vue\"\n      };\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: mixedMode,\n        selectionPointer: true\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-vue</code></p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/vue/vue.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function (mod) {\n  \"use strict\";\n  if (typeof exports === \"object\" && typeof module === \"object\") {// CommonJS\n    mod(require(\"../../lib/codemirror\"),\n        require(\"../../addon/mode/overlay\"),\n        require(\"../xml/xml\"),\n        require(\"../javascript/javascript\"),\n        require(\"../coffeescript/coffeescript\"),\n        require(\"../css/css\"),\n        require(\"../sass/sass\"),\n        require(\"../stylus/stylus\"),\n        require(\"../pug/pug\"),\n        require(\"../handlebars/handlebars\"));\n  } else if (typeof define === \"function\" && define.amd) { // AMD\n    define([\"../../lib/codemirror\",\n            \"../../addon/mode/overlay\",\n            \"../xml/xml\",\n            \"../javascript/javascript\",\n            \"../coffeescript/coffeescript\",\n            \"../css/css\",\n            \"../sass/sass\",\n            \"../stylus/stylus\",\n            \"../pug/pug\",\n            \"../handlebars/handlebars\"], mod);\n  } else { // Plain browser env\n    mod(CodeMirror);\n  }\n})(function (CodeMirror) {\n  var tagLanguages = {\n    script: [\n      [\"lang\", /coffee(script)?/, \"coffeescript\"],\n      [\"type\", /^(?:text|application)\\/(?:x-)?coffee(?:script)?$/, \"coffeescript\"],\n      [\"lang\", /^babel$/, \"javascript\"],\n      [\"type\", /^text\\/babel$/, \"javascript\"],\n      [\"type\", /^text\\/ecmascript-\\d+$/, \"javascript\"]\n    ],\n    style: [\n      [\"lang\", /^stylus$/i, \"stylus\"],\n      [\"lang\", /^sass$/i, \"sass\"],\n      [\"lang\", /^less$/i, \"text/x-less\"],\n      [\"lang\", /^scss$/i, \"text/x-scss\"],\n      [\"type\", /^(text\\/)?(x-)?styl(us)?$/i, \"stylus\"],\n      [\"type\", /^text\\/sass/i, \"sass\"],\n      [\"type\", /^(text\\/)?(x-)?scss$/i, \"text/x-scss\"],\n      [\"type\", /^(text\\/)?(x-)?less$/i, \"text/x-less\"]\n    ],\n    template: [\n      [\"lang\", /^vue-template$/i, \"vue\"],\n      [\"lang\", /^pug$/i, \"pug\"],\n      [\"lang\", /^handlebars$/i, \"handlebars\"],\n      [\"type\", /^(text\\/)?(x-)?pug$/i, \"pug\"],\n      [\"type\", /^text\\/x-handlebars-template$/i, \"handlebars\"],\n      [null, null, \"vue-template\"]\n    ]\n  };\n\n  CodeMirror.defineMode(\"vue-template\", function (config, parserConfig) {\n    var mustacheOverlay = {\n      token: function (stream) {\n        if (stream.match(/^\\{\\{.*?\\}\\}/)) return \"meta mustache\";\n        while (stream.next() && !stream.match(\"{{\", false)) {}\n        return null;\n      }\n    };\n    return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || \"text/html\"), mustacheOverlay);\n  });\n\n  CodeMirror.defineMode(\"vue\", function (config) {\n    return CodeMirror.getMode(config, {name: \"htmlmixed\", tags: tagLanguages});\n  }, \"htmlmixed\", \"xml\", \"javascript\", \"coffeescript\", \"css\", \"sass\", \"stylus\", \"pug\", \"handlebars\");\n\n  CodeMirror.defineMIME(\"script/x-vue\", \"vue\");\n  CodeMirror.defineMIME(\"text/x-vue\", \"vue\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/wast/index.html",
    "content": "<!DOCTYPE html>\n\n<title>CodeMirror: WebAssembly mode</title>\n<meta charset=\"utf-8\" />\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/mode/simple.js\"></script>\n<script src=\"wast.js\"></script>\n<style>\n  .CodeMirror {\n    border-top: 1px solid black;\n    border-bottom: 1px solid black;\n  }\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\">\n    <h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\">\n  </a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">WebAssembly</a>\n  </ul>\n</div>\n\n<article>\n  <h2>WebAssembly mode</h2>\n\n\n  <div><textarea id=\"code\" name=\"code\">\n/* Example WebAssembly */\n(module $foo\n (export \"fac\" (func $fac))\n (export \"plus\" (func $plus))\n\n (func $fac (type $t0) (param $p0 i64) (result i64)\n    (if $I0 (result i64)\n      (i64.lt_s\n        (local.get $p0)\n        (i64.const 1))\n      (then\n        (i64.const 1))\n      (else\n        (i64.mul\n          (local.get $p0)\n          (call $fac\n            (i64.sub\n              (local.get $p0)\n              (i64.const 1)))))))\n\n (func $plus (param $x i32) (param $y i32) (result i32)\n  (i32.add\n   (local.get $x)\n   (local.get $y))))</textarea></div>\n\n  <script>\n    var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n      lineNumbers: true,\n      lineWrapping: true,\n      indentUnit: 4,\n      mode: \"wast\"\n    });\n  </script>\n\n  <p><strong>MIME types defined:</strong> <code>text/webassembly</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/wast/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 4}, \"wast\");\n  function MT(name) {test.mode(name, mode, Array.prototype.slice.call(arguments, 1));}\n\n  MT('number-test',\n     '[number 0]',\n     '[number 123]',\n     '[number nan]',\n     '[number inf]',\n     '[number infinity]',\n     '[number 0.1]',\n     '[number 123.0]',\n     '[number 12E+99]');\n\n  MT('string-literals-test',\n     '[string \"foo\"]',\n     '[string \"\\\\\"foo\\\\\"\"]',\n     '[string \"foo #\\\\\"# bar\"]');\n\n  MT('atom-test',\n     '[atom funcref]',\n     '[atom externref]',\n     '[atom i32]',\n     '[atom i64]',\n     '[atom f32]',\n     '[atom f64]');\n\n  MT('keyword-test',\n     '[keyword br]',\n     '[keyword if]',\n     '[keyword loop]',\n     '[keyword i32.add]',\n     '[keyword local.get]');\n\n  MT('control-instructions',\n     '[keyword unreachable]',\n     '[keyword nop]',\n     '[keyword br] [variable-2 $label0]',\n     '[keyword br_if] [variable-2 $label0]',\n     '[keyword br_table] [variable-2 $label0] [variable-2 $label1] [variable-2 $label3]',\n     '[keyword return]',\n     '[keyword call] [variable-2 $func0]',\n     '[keyword call_indirect] [variable-2 $table] ([keyword param] [atom f32] [atom f64]) ([keyword result] [atom i32] [atom i64])',\n     '[keyword return_call] [variable-2 $func0]',\n     '[keyword return_call_indirect] ([keyword param] [atom f32] [atom f64]) ([keyword result] [atom i32] [atom i64])',\n     '[keyword select] ([keyword local.get] [number 1]) ([keyword local.get] [number 2]) ([keyword local.get] [number 3])',\n     '[keyword try] ([keyword result] [atom i32])',\n     '[keyword throw] [number 0]',\n     '[keyword rethrow] [number 0]',\n     '[keyword catch] [number 0]',\n     '[keyword catch_all]',\n     '[keyword delegate] [number 0]',\n     '[keyword unwind]');\n\n\n  MT('memory-instructions',\n     '[keyword i32.load] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i32.load8_s] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i32.load8_u] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i32.load16_s] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i32.load16_u] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i32.store] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i32.store8] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i32.store16] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i64.store] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i64.load] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i64.load8_s] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i64.load8_u] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i64.load16_s] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i64.load16_u] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i64.load32_s] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i64.load32_u] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i64.store8] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i64.store16] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword i64.store32] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword f32.load] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword f32.store] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword f64.load] [keyword offset]=[number 4] [keyword align]=[number 4]',\n     '[keyword f64.store] [keyword offset]=[number 4] [keyword align]=[number 4]');\n\n  MT('atomic-memory-instructions',\n     '[keyword memory.atomic.notify] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword memory.atomic.wait32] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword memory.atomic.wait64] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.load] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.load8_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.load16_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.store] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.store8] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.store16] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.load] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.load8_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.load16_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.load32_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.store] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.store8] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.store16] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.store32] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw.add] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw8.add_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw16.add_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw.add] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw8.add_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw16.add_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw32.add_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw.sub] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw8.sub_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw16.sub_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw.sub] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw8.sub_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw16.sub_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw32.sub_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw.and] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw8.and_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw16.and_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw.and] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw8.and_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw16.and_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw32.and_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw.or] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw8.or_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw16.or_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw.or] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw8.or_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw16.or_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw32.or_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw.xor] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw8.xor_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw16.xor_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw.xor] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw8.xor_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw16.xor_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw32.xor_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw.xchg] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw8.xchg_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw16.xchg_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw.xchg] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw8.xchg_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw16.xchg_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw32.xchg_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw.cmpxchg] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw8.cmpxchg_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i32.atomic.rmw16.cmpxchg_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw.cmpxchg] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw8.cmpxchg_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw16.cmpxchg_u] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword i64.atomic.rmw32.cmpxchg_u] [keyword offset]=[number 32] [keyword align]=[number 4]');\n\n  MT('simd-instructions',\n     '[keyword v128.load] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword v128.load8x8_s] [keyword offset]=[number 64] [keyword align]=[number 0]',\n     '[keyword v128.load8x8_u] [keyword offset]=[number 64] [keyword align]=[number 0]',\n     '[keyword v128.load16x4_s] [keyword offset]=[number 64] [keyword align]=[number 0]',\n     '[keyword v128.load16x4_u] [keyword offset]=[number 64] [keyword align]=[number 0]',\n     '[keyword v128.load32x2_s] [keyword offset]=[number 64] [keyword align]=[number 0]',\n     '[keyword v128.load32x2_u] [keyword offset]=[number 64] [keyword align]=[number 0]',\n     '[keyword v128.load8_splat] [keyword offset]=[number 64] [keyword align]=[number 0]',\n     '[keyword v128.load16_splat] [keyword offset]=[number 64] [keyword align]=[number 0]',\n     '[keyword v128.load32_splat] [keyword offset]=[number 64] [keyword align]=[number 0]',\n     '[keyword v128.load64_splat] [keyword offset]=[number 64] [keyword align]=[number 0]',\n     '[keyword v128.store] [keyword offset]=[number 32] [keyword align]=[number 4]',\n     '[keyword v128.const] [number 0] [number 1] [number 2] [number 3] [number 4] [number 5] [number 6] [number 7] [number 8] [number 9] [number 10] [number 11] [number 12] [number 13] [number 14] [number 15]',\n     '[keyword i8x16.shuffle] [number 0] [number 1] [number 2] [number 3] [number 4] [number 5] [number 6] [number 7] [number 8] [number 9] [number 10] [number 11] [number 12] [number 13] [number 14] [number 15]',\n     '[keyword i8x16.swizzle]',\n     '[keyword i8x16.splat]',\n     '[keyword i16x8.splat]',\n     '[keyword i32x4.splat]',\n     '[keyword i64x2.splat]',\n     '[keyword f32x4.splat]',\n     '[keyword f64x2.splat]',\n     '[keyword i8x16.extract_lane_s] [number 1]',\n     '[keyword i8x16.extract_lane_u] [number 1]',\n     '[keyword i8x16.replace_lane] [number 1]',\n     '[keyword i16x8.extract_lane_s] [number 1]',\n     '[keyword i16x8.extract_lane_u] [number 1]',\n     '[keyword i16x8.replace_lane] [number 1]',\n     '[keyword i32x4.extract_lane] [number 1]',\n     '[keyword i32x4.replace_lane] [number 1]',\n     '[keyword i64x2.extract_lane] [number 1]',\n     '[keyword i64x2.replace_lane] [number 1]',\n     '[keyword f32x4.extract_lane] [number 1]',\n     '[keyword f32x4.replace_lane] [number 1]',\n     '[keyword f64x2.extract_lane] [number 1]',\n     '[keyword f64x2.replace_lane] [number 1]',\n     '[keyword i8x16.eq]',\n     '[keyword i8x16.ne]',\n     '[keyword i8x16.lt_s]',\n     '[keyword i8x16.lt_u]',\n     '[keyword i8x16.gt_s]',\n     '[keyword i8x16.gt_u]',\n     '[keyword i8x16.le_s]',\n     '[keyword i8x16.le_u]',\n     '[keyword i8x16.ge_s]',\n     '[keyword i8x16.ge_u]',\n     '[keyword i16x8.eq]',\n     '[keyword i16x8.ne]',\n     '[keyword i16x8.lt_s]',\n     '[keyword i16x8.lt_u]',\n     '[keyword i16x8.gt_s]',\n     '[keyword i16x8.gt_u]',\n     '[keyword i16x8.le_s]',\n     '[keyword i16x8.le_u]',\n     '[keyword i16x8.ge_s]',\n     '[keyword i16x8.ge_u]',\n     '[keyword i32x4.eq]',\n     '[keyword i32x4.ne]',\n     '[keyword i32x4.lt_s]',\n     '[keyword i32x4.lt_u]',\n     '[keyword i32x4.gt_s]',\n     '[keyword i32x4.gt_u]',\n     '[keyword i32x4.le_s]',\n     '[keyword i32x4.le_u]',\n     '[keyword i32x4.ge_s]',\n     '[keyword i32x4.ge_u]',\n     '[keyword f32x4.eq]',\n     '[keyword f32x4.ne]',\n     '[keyword f32x4.lt]',\n     '[keyword f32x4.gt]',\n     '[keyword f32x4.le]',\n     '[keyword f32x4.ge]',\n     '[keyword f64x2.eq]',\n     '[keyword f64x2.ne]',\n     '[keyword f64x2.lt]',\n     '[keyword f64x2.gt]',\n     '[keyword f64x2.le]',\n     '[keyword f64x2.ge]',\n     '[keyword v128.not]',\n     '[keyword v128.and]',\n     '[keyword v128.andnot]',\n     '[keyword v128.or]',\n     '[keyword v128.xor]',\n     '[keyword v128.bitselect]',\n     '[keyword v128.any_true]',\n     '[keyword v128.load8_lane] [keyword offset]=[number 64] [keyword align]=[number 0] [number 1]',\n     '[keyword v128.load16_lane] [keyword offset]=[number 64] [keyword align]=[number 0] [number 1]',\n     '[keyword v128.load32_lane] [keyword offset]=[number 64] [keyword align]=[number 0] [number 1]',\n     '[keyword v128.load64_lane] [keyword offset]=[number 64] [keyword align]=[number 0] [number 1]',\n     '[keyword v128.store8_lane] [keyword offset]=[number 64] [keyword align]=[number 0] [number 1]',\n     '[keyword v128.store16_lane] [keyword offset]=[number 64] [keyword align]=[number 0] [number 1]',\n     '[keyword v128.store32_lane] [keyword offset]=[number 64] [keyword align]=[number 0] [number 1]',\n     '[keyword v128.store64_lane] [keyword offset]=[number 64] [keyword align]=[number 0] [number 1]',\n     '[keyword v128.load32_zero] [keyword offset]=[number 64] [keyword align]=[number 0]',\n     '[keyword v128.load64_zero] [keyword offset]=[number 64] [keyword align]=[number 0]',\n     '[keyword f32x4.demote_f64x2_zero]',\n     '[keyword f64x2.promote_low_f32x4]',\n     '[keyword i8x16.abs]',\n     '[keyword i8x16.neg]',\n     '[keyword i8x16.popcnt]',\n     '[keyword i8x16.all_true]',\n     '[keyword i8x16.bitmask]',\n     '[keyword i8x16.narrow_i16x8_s]',\n     '[keyword i8x16.narrow_i16x8_u]',\n     '[keyword f32x4.ceil]',\n     '[keyword f32x4.floor]',\n     '[keyword f32x4.trunc]',\n     '[keyword f32x4.nearest]',\n     '[keyword i8x16.shl]',\n     '[keyword i8x16.shr_s]',\n     '[keyword i8x16.shr_u]',\n     '[keyword i8x16.add]',\n     '[keyword i8x16.add_sat_s]',\n     '[keyword i8x16.add_sat_u]',\n     '[keyword i8x16.sub]',\n     '[keyword i8x16.sub_sat_s]',\n     '[keyword i8x16.sub_sat_u]',\n     '[keyword f64x2.ceil]',\n     '[keyword f64x2.floor]',\n     '[keyword i8x16.min_s]',\n     '[keyword i8x16.min_u]',\n     '[keyword i8x16.max_s]',\n     '[keyword i8x16.max_u]',\n     '[keyword f64x2.trunc]',\n     '[keyword i8x16.avgr_u]',\n     '[keyword i16x8.extadd_pairwise_i8x16_s]',\n     '[keyword i16x8.extadd_pairwise_i8x16_u]',\n     '[keyword i32x4.extadd_pairwise_i16x8_s]',\n     '[keyword i32x4.extadd_pairwise_i16x8_u]',\n     '[keyword i16x8.abs]',\n     '[keyword i16x8.neg]',\n     '[keyword i16x8.q15mulr_sat_s]',\n     '[keyword i16x8.all_true]',\n     '[keyword i16x8.bitmask]',\n     '[keyword i16x8.narrow_i32x4_s]',\n     '[keyword i16x8.narrow_i32x4_u]',\n     '[keyword i16x8.extend_low_i8x16_s]',\n     '[keyword i16x8.extend_high_i8x16_s]',\n     '[keyword i16x8.extend_low_i8x16_u]',\n     '[keyword i16x8.extend_high_i8x16_u]',\n     '[keyword i16x8.shl]',\n     '[keyword i16x8.shr_s]',\n     '[keyword i16x8.shr_u]',\n     '[keyword i16x8.add]',\n     '[keyword i16x8.add_sat_s]',\n     '[keyword i16x8.add_sat_u]',\n     '[keyword i16x8.sub]',\n     '[keyword i16x8.sub_sat_s]',\n     '[keyword i16x8.sub_sat_u]',\n     '[keyword f64x2.nearest]',\n     '[keyword i16x8.mul]',\n     '[keyword i16x8.min_s]',\n     '[keyword i16x8.min_u]',\n     '[keyword i16x8.max_s]',\n     '[keyword i16x8.max_u]',\n     '[keyword i16x8.avgr_u]',\n     '[keyword i16x8.extmul_low_i8x16_s]',\n     '[keyword i16x8.extmul_high_i8x16_s]',\n     '[keyword i16x8.extmul_low_i8x16_u]',\n     '[keyword i16x8.extmul_high_i8x16_u]',\n     '[keyword i32x4.abs]',\n     '[keyword i32x4.neg]',\n     '[keyword i32x4.all_true]',\n     '[keyword i32x4.bitmask]',\n     '[keyword i32x4.extend_low_i16x8_s]',\n     '[keyword i32x4.extend_high_i16x8_s]',\n     '[keyword i32x4.extend_low_i16x8_u]',\n     '[keyword i32x4.extend_high_i16x8_u]',\n     '[keyword i32x4.shl]',\n     '[keyword i32x4.shr_s]',\n     '[keyword i32x4.shr_u]',\n     '[keyword i32x4.add]',\n     '[keyword i32x4.sub]',\n     '[keyword i32x4.mul]',\n     '[keyword i32x4.min_s]',\n     '[keyword i32x4.min_u]',\n     '[keyword i32x4.max_s]',\n     '[keyword i32x4.max_u]',\n     '[keyword i32x4.dot_i16x8_s]',\n     '[keyword i32x4.extmul_low_i16x8_s]',\n     '[keyword i32x4.extmul_high_i16x8_s]',\n     '[keyword i32x4.extmul_low_i16x8_u]',\n     '[keyword i32x4.extmul_high_i16x8_u]',\n     '[keyword i64x2.abs]',\n     '[keyword i64x2.neg]',\n     '[keyword i64x2.all_true]',\n     '[keyword i64x2.bitmask]',\n     '[keyword i64x2.extend_low_i32x4_s]',\n     '[keyword i64x2.extend_high_i32x4_s]',\n     '[keyword i64x2.extend_low_i32x4_u]',\n     '[keyword i64x2.extend_high_i32x4_u]',\n     '[keyword i64x2.shl]',\n     '[keyword i64x2.shr_s]',\n     '[keyword i64x2.shr_u]',\n     '[keyword i64x2.add]',\n     '[keyword i64x2.sub]',\n     '[keyword i64x2.mul]',\n     '[keyword i64x2.eq]',\n     '[keyword i64x2.ne]',\n     '[keyword i64x2.lt_s]',\n     '[keyword i64x2.gt_s]',\n     '[keyword i64x2.le_s]',\n     '[keyword i64x2.ge_s]',\n     '[keyword i64x2.extmul_low_i32x4_s]',\n     '[keyword i64x2.extmul_high_i32x4_s]',\n     '[keyword i64x2.extmul_low_i32x4_u]',\n     '[keyword i64x2.extmul_high_i32x4_u]',\n     '[keyword f32x4.abs]',\n     '[keyword f32x4.neg]',\n     '[keyword f32x4.sqrt]',\n     '[keyword f32x4.add]',\n     '[keyword f32x4.sub]',\n     '[keyword f32x4.mul]',\n     '[keyword f32x4.div]',\n     '[keyword f32x4.min]',\n     '[keyword f32x4.max]',\n     '[keyword f64x2.abs]',\n     '[keyword f64x2.neg]',\n     '[keyword f64x2.sqrt]',\n     '[keyword f64x2.add]',\n     '[keyword f64x2.sub]',\n     '[keyword f64x2.mul]',\n     '[keyword f64x2.div]',\n     '[keyword f64x2.min]',\n     '[keyword f64x2.max]',\n     '[keyword i32x4.trunc_sat_f32x4_s]',\n     '[keyword i32x4.trunc_sat_f32x4_u]',\n     '[keyword f32x4.convert_i32x4_s]',\n     '[keyword f32x4.convert_i32x4_u]',\n     '[keyword i32x4.trunc_sat_f64x2_s_zero]',\n     '[keyword i32x4.trunc_sat_f64x2_u_zero]',\n     '[keyword f64x2.convert_low_i32x4_s]',\n     '[keyword f64x2.convert_low_i32x4_u]');\n\n     MT('reference-type-instructions',\n     '[keyword ref.null] [keyword extern]',\n     '[keyword ref.null] [keyword func]',\n     '[keyword ref.is_null] ([keyword ref.func] [variable-2 $f])',\n     '[keyword ref.func] [variable-2 $f]');\n\n     MT('table-instructions',\n     '[keyword table.get] [variable-2 $t] ([keyword i32.const] [number 5])',\n     '[keyword table.set] [variable-2 $t] ([keyword i32.const] [number 5]) ([keyword ref.func] [variable-2 $f])',\n     '[keyword table.size] [variable-2 $t]',\n     '[keyword table.grow] [variable-2 $t] ([keyword ref.null] [keyword extern]) ([keyword i32.const] [number 5])',\n     '[keyword table.fill] [variable-2 $t] ([keyword i32.const] [number 5]) ([keyword param] [variable-2 $r] [atom externref]) ([keyword i32.const] [number 5])',\n     '[keyword table.init] [variable-2 $t] [number 1] ([keyword i32.const] [number 5]) ([keyword i32.const] [number 10]) ([keyword i32.const] [number 15])',\n     '[keyword table.copy] [variable-2 $t] [variable-2 $t2] ([keyword i32.const] [number 5]) ([keyword i32.const] [number 10]) ([keyword i32.const] [number 15])'\n     );\n     MT('gc-proposal',\n     '[keyword call_ref] [keyword return_call_ref]',\n     '[keyword ref.as_non_null] [keyword br_on_null] [keyword ref.eq]');\n     MT('gc-proposal-structs',\n     '[keyword struct.new_with_rtt] [keyword struct.new_default_with_rtt]',\n     '[keyword struct.get] [keyword struct.get_s] [keyword struct.get_u]',\n     '[keyword struct.set]');\n     MT('gc-proposal-arrays',\n     '[keyword array.new_with_rtt] [keyword array.new_default_with_rtt]',\n     '[keyword array.get] [keyword array.get_s] [keyword array.get_u]',\n     '[keyword array.len] [keyword array.set]');\n     MT('gc-proposal-i31',\n     '[keyword i31.new] [keyword i31.get_s] [keyword i31.get_u]');\n     MT('gc-proposal-rtt',\n     '[keyword rtt.canon] [keyword rtt.sub]');\n     MT('gc-proposal-typechecks',\n     '[keyword ref.test] [keyword ref.cast] [keyword br_on_cast]',\n     '[keyword ref.is_func] [keyword ref.is_data] [keyword ref.is_i31]',\n     '[keyword ref.as_func] [keyword ref.as_data] [keyword ref.as_i31]',\n     '[keyword br_on_func] [keyword br_on_data] [keyword br_on_i31]');\n     MT('gc-proposal-types',\n     '[atom i8] [atom i16]',\n     '[atom anyref] [atom dataref] [atom eqref] [atom i31ref]');\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/wast/wast.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../../addon/mode/simple\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../../addon/mode/simple\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nvar kKeywords = [\n    \"align\",\n    \"block\",\n    \"br(_if|_table|_on_(cast|data|func|i31|null))?\",\n    \"call(_indirect|_ref)?\",\n    \"current_memory\",\n    \"\\\\bdata\\\\b\",\n    \"catch(_all)?\",\n    \"delegate\",\n    \"drop\",\n    \"elem\",\n    \"else\",\n    \"end\",\n    \"export\",\n    \"\\\\bextern\\\\b\",\n    \"\\\\bfunc\\\\b\",\n    \"global(\\\\.(get|set))?\",\n    \"if\",\n    \"import\",\n    \"local(\\\\.(get|set|tee))?\",\n    \"loop\",\n    \"module\",\n    \"mut\",\n    \"nop\",\n    \"offset\",\n    \"param\",\n    \"result\",\n    \"rethrow\",\n    \"return(_call(_indirect|_ref)?)?\",\n    \"select\",\n    \"start\",\n    \"table(\\\\.(size|get|set|size|grow|fill|init|copy))?\",\n    \"then\",\n    \"throw\",\n    \"try\",\n    \"type\",\n    \"unreachable\",\n    \"unwind\",\n\n    // Numeric opcodes.\n    \"i(32|64)\\\\.(store(8|16)|(load(8|16)_[su]))\",\n    \"i64\\\\.(load32_[su]|store32)\",\n    \"[fi](32|64)\\\\.(const|load|store)\",\n    \"f(32|64)\\\\.(abs|add|ceil|copysign|div|eq|floor|[gl][et]|max|min|mul|nearest|neg?|sqrt|sub|trunc)\",\n    \"i(32|64)\\\\.(a[dn]d|c[lt]z|(div|rem)_[su]|eqz?|[gl][te]_[su]|mul|ne|popcnt|rot[lr]|sh(l|r_[su])|sub|x?or)\",\n    \"i64\\\\.extend_[su]_i32\",\n    \"i32\\\\.wrap_i64\",\n    \"i(32|64)\\\\.trunc_f(32|64)_[su]\",\n    \"f(32|64)\\\\.convert_i(32|64)_[su]\",\n    \"f64\\\\.promote_f32\",\n    \"f32\\\\.demote_f64\",\n    \"f32\\\\.reinterpret_i32\",\n    \"i32\\\\.reinterpret_f32\",\n    \"f64\\\\.reinterpret_i64\",\n    \"i64\\\\.reinterpret_f64\",\n    // Atomics.\n    \"memory(\\\\.((atomic\\\\.(notify|wait(32|64)))|grow|size))?\",\n    \"i64\\.atomic\\\\.(load32_u|store32|rmw32\\\\.(a[dn]d|sub|x?or|(cmp)?xchg)_u)\",\n    \"i(32|64)\\\\.atomic\\\\.(load((8|16)_u)?|store(8|16)?|rmw(\\\\.(a[dn]d|sub|x?or|(cmp)?xchg)|(8|16)\\\\.(a[dn]d|sub|x?or|(cmp)?xchg)_u))\",\n    // SIMD.\n    \"v128\\\\.load(8x8|16x4|32x2)_[su]\",\n    \"v128\\\\.load(8|16|32|64)_splat\",\n    \"v128\\\\.(load|store)(8|16|32|64)_lane\",\n    \"v128\\\\.load(32|64)_zero\",\n    \"v128\\.(load|store|const|not|andnot|and|or|xor|bitselect|any_true)\",\n    \"i(8x16|16x8)\\\\.(extract_lane_[su]|(add|sub)_sat_[su]|avgr_u)\",\n    \"i(8x16|16x8|32x4|64x2)\\\\.(neg|add|sub|abs|shl|shr_[su]|all_true|bitmask|eq|ne|[lg][te]_s)\",\n    \"(i(8x16|16x8|32x4|64x2)|f(32x4|64x2))\\.(splat|replace_lane)\",\n    \"i(8x16|16x8|32x4)\\\\.(([lg][te]_u)|((min|max)_[su]))\",\n    \"f(32x4|64x2)\\\\.(neg|add|sub|abs|nearest|eq|ne|[lg][te]|sqrt|mul|div|min|max|ceil|floor|trunc)\",\n    \"[fi](32x4|64x2)\\\\.extract_lane\",\n    \"i8x16\\\\.(shuffle|swizzle|popcnt|narrow_i16x8_[su])\",\n    \"i16x8\\\\.(narrow_i32x4_[su]|mul|extadd_pairwise_i8x16_[su]|q15mulr_sat_s)\",\n    \"i16x8\\\\.(extend|extmul)_(low|high)_i8x16_[su]\",\n    \"i32x4\\\\.(mul|dot_i16x8_s|trunc_sat_f64x2_[su]_zero)\",\n    \"i32x4\\\\.((extend|extmul)_(low|high)_i16x8_|trunc_sat_f32x4_|extadd_pairwise_i16x8_)[su]\",\n    \"i64x2\\\\.(mul|(extend|extmul)_(low|high)_i32x4_[su])\",\n    \"f32x4\\\\.(convert_i32x4_[su]|demote_f64x2_zero)\",\n    \"f64x2\\\\.(promote_low_f32x4|convert_low_i32x4_[su])\",\n    // Reference types, function references, and GC.\n    \"\\\\bany\\\\b\",\n    \"array\\\\.len\",\n    \"(array|struct)(\\\\.(new_(default_)?with_rtt|get(_[su])?|set))?\",\n    \"\\\\beq\\\\b\",\n    \"field\",\n    \"i31\\\\.(new|get_[su])\",\n    \"\\\\bnull\\\\b\",\n    \"ref(\\\\.(([ai]s_(data|func|i31))|cast|eq|func|(is_|as_non_)?null|test))?\",\n    \"rtt(\\\\.(canon|sub))?\",\n];\n\nCodeMirror.defineSimpleMode('wast', {\n  start: [\n    {regex: /[+\\-]?(?:nan(?::0x[0-9a-fA-F]+)?|infinity|inf|0x[0-9a-fA-F]+\\.?[0-9a-fA-F]*p[+\\/-]?\\d+|\\d+(?:\\.\\d*)?[eE][+\\-]?\\d*|\\d+\\.\\d*|0x[0-9a-fA-F]+|\\d+)/, token: \"number\"},\n    {regex: new RegExp(kKeywords.join('|')), token: \"keyword\"},\n    {regex: /\\b((any|data|eq|extern|i31|func)ref|[fi](32|64)|i(8|16))\\b/, token: \"atom\"},\n    {regex: /\\$([a-zA-Z0-9_`\\+\\-\\*\\/\\\\\\^~=<>!\\?@#$%&|:\\.]+)/, token: \"variable-2\"},\n    {regex: /\"(?:[^\"\\\\\\x00-\\x1f\\x7f]|\\\\[nt\\\\'\"]|\\\\[0-9a-fA-F][0-9a-fA-F])*\"/, token: \"string\"},\n    {regex: /\\(;.*?/, token: \"comment\", next: \"comment\"},\n    {regex: /;;.*$/, token: \"comment\"},\n    {regex: /\\(/, indent: true},\n    {regex: /\\)/, dedent: true},\n  ],\n\n  comment: [\n    {regex: /.*?;\\)/, token: \"comment\", next: \"start\"},\n    {regex: /.*/, token: \"comment\"},\n  ],\n\n  meta: {\n    dontIndentStates: ['comment'],\n  },\n});\n\n// https://github.com/WebAssembly/design/issues/981 mentions text/webassembly,\n// which seems like a reasonable choice, although it's not standard right now.\nCodeMirror.defineMIME(\"text/webassembly\", \"wast\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/webidl/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Web IDL mode</title>\n<meta charset=\"utf-8\">\n<link rel=\"stylesheet\" href=\"../../doc/docs.css\">\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"webidl.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n\n<div id=\"nav\">\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=\"logo\" src=\"../../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=\"active\" href=\"#\">Web IDL</a>\n  </ul>\n</div>\n\n<article>\n  <h2>Web IDL mode</h2>\n\n  <div>\n<textarea id=\"code\" name=\"code\">\n[NamedConstructor=Image(optional unsigned long width, optional unsigned long height)]\ninterface HTMLImageElement : HTMLElement {\n           attribute DOMString alt;\n           attribute DOMString src;\n           attribute DOMString srcset;\n           attribute DOMString sizes;\n           attribute DOMString? crossOrigin;\n           attribute DOMString useMap;\n           attribute boolean isMap;\n           attribute unsigned long width;\n           attribute unsigned long height;\n  readonly attribute unsigned long naturalWidth;\n  readonly attribute unsigned long naturalHeight;\n  readonly attribute boolean complete;\n  readonly attribute DOMString currentSrc;\n\n  // also has obsolete members\n};\n\npartial interface HTMLImageElement {\n  attribute DOMString name;\n  attribute DOMString lowsrc;\n  attribute DOMString align;\n  attribute unsigned long hspace;\n  attribute unsigned long vspace;\n  attribute DOMString longDesc;\n\n  [TreatNullAs=EmptyString] attribute DOMString border;\n};\n</textarea>\n  </div>\n\n  <script>\n    var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n      lineNumbers: true,\n      matchBrackets: true\n    });\n  </script>\n\n  <p><strong>MIME type defined:</strong> <code>text/x-webidl</code>.</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/webidl/webidl.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nfunction wordRegexp(words) {\n  return new RegExp(\"^((\" + words.join(\")|(\") + \"))\\\\b\");\n};\n\nvar builtinArray = [\n  \"Clamp\",\n  \"Constructor\",\n  \"EnforceRange\",\n  \"Exposed\",\n  \"ImplicitThis\",\n  \"Global\", \"PrimaryGlobal\",\n  \"LegacyArrayClass\",\n  \"LegacyUnenumerableNamedProperties\",\n  \"LenientThis\",\n  \"NamedConstructor\",\n  \"NewObject\",\n  \"NoInterfaceObject\",\n  \"OverrideBuiltins\",\n  \"PutForwards\",\n  \"Replaceable\",\n  \"SameObject\",\n  \"TreatNonObjectAsNull\",\n  \"TreatNullAs\",\n    \"EmptyString\",\n  \"Unforgeable\",\n  \"Unscopeable\"\n];\nvar builtins = wordRegexp(builtinArray);\n\nvar typeArray = [\n  \"unsigned\", \"short\", \"long\",                  // UnsignedIntegerType\n  \"unrestricted\", \"float\", \"double\",            // UnrestrictedFloatType\n  \"boolean\", \"byte\", \"octet\",                   // Rest of PrimitiveType\n  \"Promise\",                                    // PromiseType\n  \"ArrayBuffer\", \"DataView\", \"Int8Array\", \"Int16Array\", \"Int32Array\",\n  \"Uint8Array\", \"Uint16Array\", \"Uint32Array\", \"Uint8ClampedArray\",\n  \"Float32Array\", \"Float64Array\",               // BufferRelatedType\n  \"ByteString\", \"DOMString\", \"USVString\", \"sequence\", \"object\", \"RegExp\",\n  \"Error\", \"DOMException\", \"FrozenArray\",       // Rest of NonAnyType\n  \"any\",                                        // Rest of SingleType\n  \"void\"                                        // Rest of ReturnType\n];\nvar types = wordRegexp(typeArray);\n\nvar keywordArray = [\n  \"attribute\", \"callback\", \"const\", \"deleter\", \"dictionary\", \"enum\", \"getter\",\n  \"implements\", \"inherit\", \"interface\", \"iterable\", \"legacycaller\", \"maplike\",\n  \"partial\", \"required\", \"serializer\", \"setlike\", \"setter\", \"static\",\n  \"stringifier\", \"typedef\",                     // ArgumentNameKeyword except\n                                                // \"unrestricted\"\n  \"optional\", \"readonly\", \"or\"\n];\nvar keywords = wordRegexp(keywordArray);\n\nvar atomArray = [\n  \"true\", \"false\",                              // BooleanLiteral\n  \"Infinity\", \"NaN\",                            // FloatLiteral\n  \"null\"                                        // Rest of ConstValue\n];\nvar atoms = wordRegexp(atomArray);\n\nCodeMirror.registerHelper(\"hintWords\", \"webidl\",\n    builtinArray.concat(typeArray).concat(keywordArray).concat(atomArray));\n\nvar startDefArray = [\"callback\", \"dictionary\", \"enum\", \"interface\"];\nvar startDefs = wordRegexp(startDefArray);\n\nvar endDefArray = [\"typedef\"];\nvar endDefs = wordRegexp(endDefArray);\n\nvar singleOperators = /^[:<=>?]/;\nvar integers = /^-?([1-9][0-9]*|0[Xx][0-9A-Fa-f]+|0[0-7]*)/;\nvar floats = /^-?(([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)/;\nvar identifiers = /^_?[A-Za-z][0-9A-Z_a-z-]*/;\nvar identifiersEnd = /^_?[A-Za-z][0-9A-Z_a-z-]*(?=\\s*;)/;\nvar strings = /^\"[^\"]*\"/;\nvar multilineComments = /^\\/\\*.*?\\*\\//;\nvar multilineCommentsStart = /^\\/\\*.*/;\nvar multilineCommentsEnd = /^.*?\\*\\//;\n\nfunction readToken(stream, state) {\n  // whitespace\n  if (stream.eatSpace()) return null;\n\n  // comment\n  if (state.inComment) {\n    if (stream.match(multilineCommentsEnd)) {\n      state.inComment = false;\n      return \"comment\";\n    }\n    stream.skipToEnd();\n    return \"comment\";\n  }\n  if (stream.match(\"//\")) {\n    stream.skipToEnd();\n    return \"comment\";\n  }\n  if (stream.match(multilineComments)) return \"comment\";\n  if (stream.match(multilineCommentsStart)) {\n    state.inComment = true;\n    return \"comment\";\n  }\n\n  // integer and float\n  if (stream.match(/^-?[0-9\\.]/, false)) {\n    if (stream.match(integers) || stream.match(floats)) return \"number\";\n  }\n\n  // string\n  if (stream.match(strings)) return \"string\";\n\n  // identifier\n  if (state.startDef && stream.match(identifiers)) return \"def\";\n\n  if (state.endDef && stream.match(identifiersEnd)) {\n    state.endDef = false;\n    return \"def\";\n  }\n\n  if (stream.match(keywords)) return \"keyword\";\n\n  if (stream.match(types)) {\n    var lastToken = state.lastToken;\n    var nextToken = (stream.match(/^\\s*(.+?)\\b/, false) || [])[1];\n\n    if (lastToken === \":\" || lastToken === \"implements\" ||\n        nextToken === \"implements\" || nextToken === \"=\") {\n      // Used as identifier\n      return \"builtin\";\n    } else {\n      // Used as type\n      return \"variable-3\";\n    }\n  }\n\n  if (stream.match(builtins)) return \"builtin\";\n  if (stream.match(atoms)) return \"atom\";\n  if (stream.match(identifiers)) return \"variable\";\n\n  // other\n  if (stream.match(singleOperators)) return \"operator\";\n\n  // unrecognized\n  stream.next();\n  return null;\n};\n\nCodeMirror.defineMode(\"webidl\", function() {\n  return {\n    startState: function() {\n      return {\n        // Is in multiline comment\n        inComment: false,\n        // Last non-whitespace, matched token\n        lastToken: \"\",\n        // Next token is a definition\n        startDef: false,\n        // Last token of the statement is a definition\n        endDef: false\n      };\n    },\n    token: function(stream, state) {\n      var style = readToken(stream, state);\n\n      if (style) {\n        var cur = stream.current();\n        state.lastToken = cur;\n        if (style === \"keyword\") {\n          state.startDef = startDefs.test(cur);\n          state.endDef = state.endDef || endDefs.test(cur);\n        } else {\n          state.startDef = false;\n        }\n      }\n\n      return style;\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-webidl\", \"webidl\");\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/xml/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: XML mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"xml.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">XML</a>\n  </ul>\n</div>\n\n<article>\n<h2>XML mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n&lt;html style=\"color: green\"&gt;\n  &lt;!-- this is a comment --&gt;\n  &lt;head&gt;\n    &lt;title&gt;HTML Example&lt;/title&gt;\n  &lt;/head&gt;\n  &lt;body&gt;\n    The indentation tries to be &lt;em&gt;somewhat &amp;quot;do what\n    I mean&amp;quot;&lt;/em&gt;... but might not match your style.\n  &lt;/body&gt;\n&lt;/html&gt;\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        mode: \"text/html\",\n        lineNumbers: true\n      });\n    </script>\n    <p>The XML mode supports these configuration parameters:</p>\n    <dl>\n      <dt><code>htmlMode (boolean)</code></dt>\n      <dd>This switches the mode to parse HTML instead of XML. This\n      means attributes do not have to be quoted, and some elements\n      (such as <code>br</code>) do not require a closing tag.</dd>\n      <dt><code>matchClosing (boolean)</code></dt>\n      <dd>Controls whether the mode checks that close tags match the\n      corresponding opening tag, and highlights mismatches as errors.\n      Defaults to true.</dd>\n      <dt><code>alignCDATA (boolean)</code></dt>\n      <dd>Setting this to true will force the opening tag of CDATA\n      blocks to not be indented.</dd>\n    </dl>\n\n    <p><strong>MIME types defined:</strong> <code>application/xml</code>, <code>text/html</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/xml/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"xml\"), mname = \"xml\";\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), mname); }\n\n  MT(\"matching\",\n     \"[tag&bracket <][tag top][tag&bracket >]\",\n     \"  text\",\n     \"  [tag&bracket <][tag inner][tag&bracket />]\",\n     \"[tag&bracket </][tag top][tag&bracket >]\");\n\n  MT(\"nonmatching\",\n     \"[tag&bracket <][tag top][tag&bracket >]\",\n     \"  [tag&bracket <][tag inner][tag&bracket />]\",\n     \"  [tag&bracket </][tag&error tip][tag&bracket&error >]\");\n\n  MT(\"doctype\",\n     \"[meta <!doctype foobar>]\",\n     \"[tag&bracket <][tag top][tag&bracket />]\");\n\n  MT(\"cdata\",\n     \"[tag&bracket <][tag top][tag&bracket >]\",\n     \"  [atom <![CDATA[foo]\",\n     \"[atom barbazguh]]]]>]\",\n     \"[tag&bracket </][tag top][tag&bracket >]\");\n\n  // HTML tests\n  mode = CodeMirror.getMode({indentUnit: 2}, \"text/html\");\n\n  MT(\"selfclose\",\n     \"[tag&bracket <][tag html][tag&bracket >]\",\n     \"  [tag&bracket <][tag link] [attribute rel]=[string stylesheet] [attribute href]=[string \\\"/foobar\\\"][tag&bracket >]\",\n     \"[tag&bracket </][tag html][tag&bracket >]\");\n\n  MT(\"list\",\n     \"[tag&bracket <][tag ol][tag&bracket >]\",\n     \"  [tag&bracket <][tag li][tag&bracket >]one\",\n     \"  [tag&bracket <][tag li][tag&bracket >]two\",\n     \"[tag&bracket </][tag ol][tag&bracket >]\");\n\n  MT(\"valueless\",\n     \"[tag&bracket <][tag input] [attribute type]=[string checkbox] [attribute checked][tag&bracket />]\");\n\n  MT(\"pThenArticle\",\n     \"[tag&bracket <][tag p][tag&bracket >]\",\n     \"  foo\",\n     \"[tag&bracket <][tag article][tag&bracket >]bar\");\n\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/xml/xml.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nvar htmlConfig = {\n  autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,\n                    'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,\n                    'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,\n                    'track': true, 'wbr': true, 'menuitem': true},\n  implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,\n                     'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,\n                     'th': true, 'tr': true},\n  contextGrabbers: {\n    'dd': {'dd': true, 'dt': true},\n    'dt': {'dd': true, 'dt': true},\n    'li': {'li': true},\n    'option': {'option': true, 'optgroup': true},\n    'optgroup': {'optgroup': true},\n    'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,\n          'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,\n          'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,\n          'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,\n          'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},\n    'rp': {'rp': true, 'rt': true},\n    'rt': {'rp': true, 'rt': true},\n    'tbody': {'tbody': true, 'tfoot': true},\n    'td': {'td': true, 'th': true},\n    'tfoot': {'tbody': true},\n    'th': {'td': true, 'th': true},\n    'thead': {'tbody': true, 'tfoot': true},\n    'tr': {'tr': true}\n  },\n  doNotIndent: {\"pre\": true},\n  allowUnquoted: true,\n  allowMissing: true,\n  caseFold: true\n}\n\nvar xmlConfig = {\n  autoSelfClosers: {},\n  implicitlyClosed: {},\n  contextGrabbers: {},\n  doNotIndent: {},\n  allowUnquoted: false,\n  allowMissing: false,\n  allowMissingTagName: false,\n  caseFold: false\n}\n\nCodeMirror.defineMode(\"xml\", function(editorConf, config_) {\n  var indentUnit = editorConf.indentUnit\n  var config = {}\n  var defaults = config_.htmlMode ? htmlConfig : xmlConfig\n  for (var prop in defaults) config[prop] = defaults[prop]\n  for (var prop in config_) config[prop] = config_[prop]\n\n  // Return variables for tokenizers\n  var type, setStyle;\n\n  function inText(stream, state) {\n    function chain(parser) {\n      state.tokenize = parser;\n      return parser(stream, state);\n    }\n\n    var ch = stream.next();\n    if (ch == \"<\") {\n      if (stream.eat(\"!\")) {\n        if (stream.eat(\"[\")) {\n          if (stream.match(\"CDATA[\")) return chain(inBlock(\"atom\", \"]]>\"));\n          else return null;\n        } else if (stream.match(\"--\")) {\n          return chain(inBlock(\"comment\", \"-->\"));\n        } else if (stream.match(\"DOCTYPE\", true, true)) {\n          stream.eatWhile(/[\\w\\._\\-]/);\n          return chain(doctype(1));\n        } else {\n          return null;\n        }\n      } else if (stream.eat(\"?\")) {\n        stream.eatWhile(/[\\w\\._\\-]/);\n        state.tokenize = inBlock(\"meta\", \"?>\");\n        return \"meta\";\n      } else {\n        type = stream.eat(\"/\") ? \"closeTag\" : \"openTag\";\n        state.tokenize = inTag;\n        return \"tag bracket\";\n      }\n    } else if (ch == \"&\") {\n      var ok;\n      if (stream.eat(\"#\")) {\n        if (stream.eat(\"x\")) {\n          ok = stream.eatWhile(/[a-fA-F\\d]/) && stream.eat(\";\");\n        } else {\n          ok = stream.eatWhile(/[\\d]/) && stream.eat(\";\");\n        }\n      } else {\n        ok = stream.eatWhile(/[\\w\\.\\-:]/) && stream.eat(\";\");\n      }\n      return ok ? \"atom\" : \"error\";\n    } else {\n      stream.eatWhile(/[^&<]/);\n      return null;\n    }\n  }\n  inText.isInText = true;\n\n  function inTag(stream, state) {\n    var ch = stream.next();\n    if (ch == \">\" || (ch == \"/\" && stream.eat(\">\"))) {\n      state.tokenize = inText;\n      type = ch == \">\" ? \"endTag\" : \"selfcloseTag\";\n      return \"tag bracket\";\n    } else if (ch == \"=\") {\n      type = \"equals\";\n      return null;\n    } else if (ch == \"<\") {\n      state.tokenize = inText;\n      state.state = baseState;\n      state.tagName = state.tagStart = null;\n      var next = state.tokenize(stream, state);\n      return next ? next + \" tag error\" : \"tag error\";\n    } else if (/[\\'\\\"]/.test(ch)) {\n      state.tokenize = inAttribute(ch);\n      state.stringStartCol = stream.column();\n      return state.tokenize(stream, state);\n    } else {\n      stream.match(/^[^\\s\\u00a0=<>\\\"\\']*[^\\s\\u00a0=<>\\\"\\'\\/]/);\n      return \"word\";\n    }\n  }\n\n  function inAttribute(quote) {\n    var closure = function(stream, state) {\n      while (!stream.eol()) {\n        if (stream.next() == quote) {\n          state.tokenize = inTag;\n          break;\n        }\n      }\n      return \"string\";\n    };\n    closure.isInAttribute = true;\n    return closure;\n  }\n\n  function inBlock(style, terminator) {\n    return function(stream, state) {\n      while (!stream.eol()) {\n        if (stream.match(terminator)) {\n          state.tokenize = inText;\n          break;\n        }\n        stream.next();\n      }\n      return style;\n    }\n  }\n\n  function doctype(depth) {\n    return function(stream, state) {\n      var ch;\n      while ((ch = stream.next()) != null) {\n        if (ch == \"<\") {\n          state.tokenize = doctype(depth + 1);\n          return state.tokenize(stream, state);\n        } else if (ch == \">\") {\n          if (depth == 1) {\n            state.tokenize = inText;\n            break;\n          } else {\n            state.tokenize = doctype(depth - 1);\n            return state.tokenize(stream, state);\n          }\n        }\n      }\n      return \"meta\";\n    };\n  }\n\n  function lower(tagName) {\n    return tagName && tagName.toLowerCase();\n  }\n\n  function Context(state, tagName, startOfLine) {\n    this.prev = state.context;\n    this.tagName = tagName || \"\";\n    this.indent = state.indented;\n    this.startOfLine = startOfLine;\n    if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))\n      this.noIndent = true;\n  }\n  function popContext(state) {\n    if (state.context) state.context = state.context.prev;\n  }\n  function maybePopContext(state, nextTagName) {\n    var parentTagName;\n    while (true) {\n      if (!state.context) {\n        return;\n      }\n      parentTagName = state.context.tagName;\n      if (!config.contextGrabbers.hasOwnProperty(lower(parentTagName)) ||\n          !config.contextGrabbers[lower(parentTagName)].hasOwnProperty(lower(nextTagName))) {\n        return;\n      }\n      popContext(state);\n    }\n  }\n\n  function baseState(type, stream, state) {\n    if (type == \"openTag\") {\n      state.tagStart = stream.column();\n      return tagNameState;\n    } else if (type == \"closeTag\") {\n      return closeTagNameState;\n    } else {\n      return baseState;\n    }\n  }\n  function tagNameState(type, stream, state) {\n    if (type == \"word\") {\n      state.tagName = stream.current();\n      setStyle = \"tag\";\n      return attrState;\n    } else if (config.allowMissingTagName && type == \"endTag\") {\n      setStyle = \"tag bracket\";\n      return attrState(type, stream, state);\n    } else {\n      setStyle = \"error\";\n      return tagNameState;\n    }\n  }\n  function closeTagNameState(type, stream, state) {\n    if (type == \"word\") {\n      var tagName = stream.current();\n      if (state.context && state.context.tagName != tagName &&\n          config.implicitlyClosed.hasOwnProperty(lower(state.context.tagName)))\n        popContext(state);\n      if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {\n        setStyle = \"tag\";\n        return closeState;\n      } else {\n        setStyle = \"tag error\";\n        return closeStateErr;\n      }\n    } else if (config.allowMissingTagName && type == \"endTag\") {\n      setStyle = \"tag bracket\";\n      return closeState(type, stream, state);\n    } else {\n      setStyle = \"error\";\n      return closeStateErr;\n    }\n  }\n\n  function closeState(type, _stream, state) {\n    if (type != \"endTag\") {\n      setStyle = \"error\";\n      return closeState;\n    }\n    popContext(state);\n    return baseState;\n  }\n  function closeStateErr(type, stream, state) {\n    setStyle = \"error\";\n    return closeState(type, stream, state);\n  }\n\n  function attrState(type, _stream, state) {\n    if (type == \"word\") {\n      setStyle = \"attribute\";\n      return attrEqState;\n    } else if (type == \"endTag\" || type == \"selfcloseTag\") {\n      var tagName = state.tagName, tagStart = state.tagStart;\n      state.tagName = state.tagStart = null;\n      if (type == \"selfcloseTag\" ||\n          config.autoSelfClosers.hasOwnProperty(lower(tagName))) {\n        maybePopContext(state, tagName);\n      } else {\n        maybePopContext(state, tagName);\n        state.context = new Context(state, tagName, tagStart == state.indented);\n      }\n      return baseState;\n    }\n    setStyle = \"error\";\n    return attrState;\n  }\n  function attrEqState(type, stream, state) {\n    if (type == \"equals\") return attrValueState;\n    if (!config.allowMissing) setStyle = \"error\";\n    return attrState(type, stream, state);\n  }\n  function attrValueState(type, stream, state) {\n    if (type == \"string\") return attrContinuedState;\n    if (type == \"word\" && config.allowUnquoted) {setStyle = \"string\"; return attrState;}\n    setStyle = \"error\";\n    return attrState(type, stream, state);\n  }\n  function attrContinuedState(type, stream, state) {\n    if (type == \"string\") return attrContinuedState;\n    return attrState(type, stream, state);\n  }\n\n  return {\n    startState: function(baseIndent) {\n      var state = {tokenize: inText,\n                   state: baseState,\n                   indented: baseIndent || 0,\n                   tagName: null, tagStart: null,\n                   context: null}\n      if (baseIndent != null) state.baseIndent = baseIndent\n      return state\n    },\n\n    token: function(stream, state) {\n      if (!state.tagName && stream.sol())\n        state.indented = stream.indentation();\n\n      if (stream.eatSpace()) return null;\n      type = null;\n      var style = state.tokenize(stream, state);\n      if ((style || type) && style != \"comment\") {\n        setStyle = null;\n        state.state = state.state(type || style, stream, state);\n        if (setStyle)\n          style = setStyle == \"error\" ? style + \" error\" : setStyle;\n      }\n      return style;\n    },\n\n    indent: function(state, textAfter, fullLine) {\n      var context = state.context;\n      // Indent multi-line strings (e.g. css).\n      if (state.tokenize.isInAttribute) {\n        if (state.tagStart == state.indented)\n          return state.stringStartCol + 1;\n        else\n          return state.indented + indentUnit;\n      }\n      if (context && context.noIndent) return CodeMirror.Pass;\n      if (state.tokenize != inTag && state.tokenize != inText)\n        return fullLine ? fullLine.match(/^(\\s*)/)[0].length : 0;\n      // Indent the starts of attribute names.\n      if (state.tagName) {\n        if (config.multilineTagIndentPastTag !== false)\n          return state.tagStart + state.tagName.length + 2;\n        else\n          return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);\n      }\n      if (config.alignCDATA && /<!\\[CDATA\\[/.test(textAfter)) return 0;\n      var tagAfter = textAfter && /^<(\\/)?([\\w_:\\.-]*)/.exec(textAfter);\n      if (tagAfter && tagAfter[1]) { // Closing tag spotted\n        while (context) {\n          if (context.tagName == tagAfter[2]) {\n            context = context.prev;\n            break;\n          } else if (config.implicitlyClosed.hasOwnProperty(lower(context.tagName))) {\n            context = context.prev;\n          } else {\n            break;\n          }\n        }\n      } else if (tagAfter) { // Opening tag spotted\n        while (context) {\n          var grabbers = config.contextGrabbers[lower(context.tagName)];\n          if (grabbers && grabbers.hasOwnProperty(lower(tagAfter[2])))\n            context = context.prev;\n          else\n            break;\n        }\n      }\n      while (context && context.prev && !context.startOfLine)\n        context = context.prev;\n      if (context) return context.indent + indentUnit;\n      else return state.baseIndent || 0;\n    },\n\n    electricInput: /<\\/[\\s\\w:]+>$/,\n    blockCommentStart: \"<!--\",\n    blockCommentEnd: \"-->\",\n\n    configuration: config.htmlMode ? \"html\" : \"xml\",\n    helperType: config.htmlMode ? \"html\" : \"xml\",\n\n    skipAttribute: function(state) {\n      if (state.state == attrValueState)\n        state.state = attrState\n    },\n\n    xmlCurrentTag: function(state) {\n      return state.tagName ? {name: state.tagName, close: state.type == \"closeTag\"} : null\n    },\n\n    xmlCurrentContext: function(state) {\n      var context = []\n      for (var cx = state.context; cx; cx = cx.prev)\n        context.push(cx.tagName)\n      return context.reverse()\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/xml\", \"xml\");\nCodeMirror.defineMIME(\"application/xml\", \"xml\");\nif (!CodeMirror.mimeModes.hasOwnProperty(\"text/html\"))\n  CodeMirror.defineMIME(\"text/html\", {name: \"xml\", htmlMode: true});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/xquery/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: XQuery mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<link rel=\"stylesheet\" href=\"../../theme/xq-dark.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"xquery.js\"></script>\n<style>\n\t.CodeMirror {\n\t  border-top: 1px solid black; border-bottom: 1px solid black;\n\t  height:400px;\n\t}\n    </style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">XQuery</a>\n  </ul>\n</div>\n\n<article>\n<h2>XQuery mode</h2>\n \n \n<div class=\"cm-s-default\"> \n\t<textarea id=\"code\" name=\"code\"> \nxquery version &quot;1.0-ml&quot;;\n(: this is\n : a \n   \"comment\" :)\nlet $let := &lt;x attr=&quot;value&quot;&gt;&quot;test&quot;&lt;func&gt;function() $var {function()} {$var}&lt;/func&gt;&lt;/x&gt;\nlet $joe:=1\nreturn element element {\n\tattribute attribute { 1 },\n\telement test { &#39;a&#39; }, \n\tattribute foo { &quot;bar&quot; },\n\tfn:doc()[ foo/@bar eq $let ],\n\t//x }    \n \n(: a more 'evil' test :)\n(: Modified Blakeley example (: with nested comment :) ... :)\ndeclare private function local:declare() {()};\ndeclare private function local:private() {()};\ndeclare private function local:function() {()};\ndeclare private function local:local() {()};\nlet $let := &lt;let&gt;let $let := &quot;let&quot;&lt;/let&gt;\nreturn element element {\n\tattribute attribute { try { xdmp:version() } catch($e) { xdmp:log($e) } },\n\tattribute fn:doc { &quot;bar&quot; castable as xs:string },\n\telement text { text { &quot;text&quot; } },\n\tfn:doc()[ child::eq/(@bar | attribute::attribute) eq $let ],\n\t//fn:doc\n}\n\n\n\nxquery version &quot;1.0-ml&quot;;\n\n(: Copyright 2006-2010 Mark Logic Corporation. :)\n\n(:\n : Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);\n : you may not use this file except in compliance with the License.\n : You may obtain a copy of the License at\n :\n :     http://www.apache.org/licenses/LICENSE-2.0\n :\n : Unless required by applicable law or agreed to in writing, software\n : distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n : See the License for the specific language governing permissions and\n : limitations under the License.\n :)\n\nmodule namespace json = &quot;http://marklogic.com/json&quot;;\ndeclare default function namespace &quot;http://www.w3.org/2005/xpath-functions&quot;;\n\n(: Need to backslash escape any double quotes, backslashes, and newlines :)\ndeclare function json:escape($s as xs:string) as xs:string {\n  let $s := replace($s, &quot;\\\\&quot;, &quot;\\\\\\\\&quot;)\n  let $s := replace($s, &quot;&quot;&quot;&quot;, &quot;\\\\&quot;&quot;&quot;)\n  let $s := replace($s, codepoints-to-string((13, 10)), &quot;\\\\n&quot;)\n  let $s := replace($s, codepoints-to-string(13), &quot;\\\\n&quot;)\n  let $s := replace($s, codepoints-to-string(10), &quot;\\\\n&quot;)\n  return $s\n};\n\ndeclare function json:atomize($x as element()) as xs:string {\n  if (count($x/node()) = 0) then 'null'\n  else if ($x/@type = &quot;number&quot;) then\n    let $castable := $x castable as xs:float or\n                     $x castable as xs:double or\n                     $x castable as xs:decimal\n    return\n    if ($castable) then xs:string($x)\n    else error(concat(&quot;Not a number: &quot;, xdmp:describe($x)))\n  else if ($x/@type = &quot;boolean&quot;) then\n    let $castable := $x castable as xs:boolean\n    return\n    if ($castable) then xs:string(xs:boolean($x))\n    else error(concat(&quot;Not a boolean: &quot;, xdmp:describe($x)))\n  else concat('&quot;', json:escape($x), '&quot;')\n};\n\n(: Print the thing that comes after the colon :)\ndeclare function json:print-value($x as element()) as xs:string {\n  if (count($x/*) = 0) then\n    json:atomize($x)\n  else if ($x/@quote = &quot;true&quot;) then\n    concat('&quot;', json:escape(xdmp:quote($x/node())), '&quot;')\n  else\n    string-join(('{',\n      string-join(for $i in $x/* return json:print-name-value($i), &quot;,&quot;),\n    '}'), &quot;&quot;)\n};\n\n(: Print the name and value both :)\ndeclare function json:print-name-value($x as element()) as xs:string? {\n  let $name := name($x)\n  let $first-in-array :=\n    count($x/preceding-sibling::*[name(.) = $name]) = 0 and\n    (count($x/following-sibling::*[name(.) = $name]) &gt; 0 or $x/@array = &quot;true&quot;)\n  let $later-in-array := count($x/preceding-sibling::*[name(.) = $name]) &gt; 0\n  return\n\n  if ($later-in-array) then\n    ()  (: I was handled previously :)\n  else if ($first-in-array) then\n    string-join(('&quot;', json:escape($name), '&quot;:[',\n      string-join((for $i in ($x, $x/following-sibling::*[name(.) = $name]) return json:print-value($i)), &quot;,&quot;),\n    ']'), &quot;&quot;)\n   else\n     string-join(('&quot;', json:escape($name), '&quot;:', json:print-value($x)), &quot;&quot;)\n};\n\n(:~\n  Transforms an XML element into a JSON string representation.  See http://json.org.\n  &lt;p/&gt;\n  Sample usage:\n  &lt;pre&gt;\n    xquery version &quot;1.0-ml&quot;;\n    import module namespace json=&quot;http://marklogic.com/json&quot; at &quot;json.xqy&quot;;\n    json:serialize(&amp;lt;foo&amp;gt;&amp;lt;bar&amp;gt;kid&amp;lt;/bar&amp;gt;&amp;lt;/foo&amp;gt;)\n  &lt;/pre&gt;\n  Sample transformations:\n  &lt;pre&gt;\n  &amp;lt;e/&amp;gt; becomes {&quot;e&quot;:null}\n  &amp;lt;e&amp;gt;text&amp;lt;/e&amp;gt; becomes {&quot;e&quot;:&quot;text&quot;}\n  &amp;lt;e&amp;gt;quote &quot; escaping&amp;lt;/e&amp;gt; becomes {&quot;e&quot;:&quot;quote \\&quot; escaping&quot;}\n  &amp;lt;e&amp;gt;backslash \\ escaping&amp;lt;/e&amp;gt; becomes {&quot;e&quot;:&quot;backslash \\\\ escaping&quot;}\n  &amp;lt;e&amp;gt;&amp;lt;a&amp;gt;text1&amp;lt;/a&amp;gt;&amp;lt;b&amp;gt;text2&amp;lt;/b&amp;gt;&amp;lt;/e&amp;gt; becomes {&quot;e&quot;:{&quot;a&quot;:&quot;text1&quot;,&quot;b&quot;:&quot;text2&quot;}}\n  &amp;lt;e&amp;gt;&amp;lt;a&amp;gt;text1&amp;lt;/a&amp;gt;&amp;lt;a&amp;gt;text2&amp;lt;/a&amp;gt;&amp;lt;/e&amp;gt; becomes {&quot;e&quot;:{&quot;a&quot;:[&quot;text1&quot;,&quot;text2&quot;]}}\n  &amp;lt;e&amp;gt;&amp;lt;a array=&quot;true&quot;&amp;gt;text1&amp;lt;/a&amp;gt;&amp;lt;/e&amp;gt; becomes {&quot;e&quot;:{&quot;a&quot;:[&quot;text1&quot;]}}\n  &amp;lt;e&amp;gt;&amp;lt;a type=&quot;boolean&quot;&amp;gt;false&amp;lt;/a&amp;gt;&amp;lt;/e&amp;gt; becomes {&quot;e&quot;:{&quot;a&quot;:false}}\n  &amp;lt;e&amp;gt;&amp;lt;a type=&quot;number&quot;&amp;gt;123.5&amp;lt;/a&amp;gt;&amp;lt;/e&amp;gt; becomes {&quot;e&quot;:{&quot;a&quot;:123.5}}\n  &amp;lt;e quote=&quot;true&quot;&amp;gt;&amp;lt;div attrib=&quot;value&quot;/&amp;gt;&amp;lt;/e&amp;gt; becomes {&quot;e&quot;:&quot;&amp;lt;div attrib=\\&quot;value\\&quot;/&amp;gt;&quot;}\n  &lt;/pre&gt;\n  &lt;p/&gt;\n  Namespace URIs are ignored.  Namespace prefixes are included in the JSON name.\n  &lt;p/&gt;\n  Attributes are ignored, except for the special attribute @array=&quot;true&quot; that\n  indicates the JSON serialization should write the node, even if single, as an\n  array, and the attribute @type that can be set to &quot;boolean&quot; or &quot;number&quot; to\n  dictate the value should be written as that type (unquoted).  There's also\n  an @quote attribute that when set to true writes the inner content as text\n  rather than as structured JSON, useful for sending some XHTML over the\n  wire.\n  &lt;p/&gt;\n  Text nodes within mixed content are ignored.\n\n  @param $x Element node to convert\n  @return String holding JSON serialized representation of $x\n\n  @author Jason Hunter\n  @version 1.0.1\n  \n  Ported to xquery 1.0-ml; double escaped backslashes in json:escape\n:)\ndeclare function json:serialize($x as element())  as xs:string {\n  string-join(('{', json:print-name-value($x), '}'), &quot;&quot;)\n};\n  </textarea> \n</div> \n \n    <script> \n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        theme: \"xq-dark\"\n      });\n    </script> \n \n    <p><strong>MIME types defined:</strong> <code>application/xquery</code>.</p> \n \n    <p>Development of the CodeMirror XQuery mode was sponsored by \n      <a href=\"http://marklogic.com\">MarkLogic</a> and developed by \n      <a href=\"https://twitter.com/mbrevoort\">Mike Brevoort</a>.\n    </p>\n \n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/xquery/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Don't take these too seriously -- the expected results appear to be\n// based on the results of actual runs without any serious manual\n// verification. If a change you made causes them to fail, the test is\n// as likely to wrong as the code.\n\n(function() {\n  var mode = CodeMirror.getMode({tabSize: 4}, \"xquery\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"eviltest\",\n     \"[keyword xquery] [keyword version] [variable &quot;1][keyword .][atom 0][keyword -][variable ml&quot;][def&variable ;]      [comment (: this is       : a          \\\"comment\\\" :)]\",\n     \"      [keyword let] [variable $let] [keyword :=] [variable &lt;x] [variable attr][keyword =][variable &quot;value&quot;&gt;&quot;test&quot;&lt;func&gt][def&variable ;function]() [variable $var] {[keyword function]()} {[variable $var]}[variable &lt;][keyword /][variable func&gt;&lt;][keyword /][variable x&gt;]\",\n     \"      [keyword let] [variable $joe][keyword :=][atom 1]\",\n     \"      [keyword return] [keyword element] [variable element] {\",\n     \"          [keyword attribute] [variable attribute] { [atom 1] },\",\n     \"          [keyword element] [variable test] { [variable &#39;a&#39;] },           [keyword attribute] [variable foo] { [variable &quot;bar&quot;] },\",\n     \"          [def&variable fn:doc]()[[ [variable foo][keyword /][variable @bar] [keyword eq] [variable $let] ]],\",\n     \"          [keyword //][variable x] }                 [comment (: a more 'evil' test :)]\",\n     \"      [comment (: Modified Blakeley example (: with nested comment :) ... :)]\",\n     \"      [keyword declare] [variable private] [keyword function] [def&variable local:declare]() {()}[variable ;]\",\n     \"      [keyword declare] [variable private] [keyword function] [def&variable local:private]() {()}[variable ;]\",\n     \"      [keyword declare] [variable private] [keyword function] [def&variable local:function]() {()}[variable ;]\",\n     \"      [keyword declare] [variable private] [keyword function] [def&variable local:local]() {()}[variable ;]\",\n     \"      [keyword let] [variable $let] [keyword :=] [variable &lt;let&gt;let] [variable $let] [keyword :=] [variable &quot;let&quot;&lt;][keyword /let][variable &gt;]\",\n     \"      [keyword return] [keyword element] [variable element] {\",\n     \"          [keyword attribute] [variable attribute] { [keyword try] { [def&variable xdmp:version]() } [keyword catch]([variable $e]) { [def&variable xdmp:log]([variable $e]) } },\",\n     \"          [keyword attribute] [variable fn:doc] { [variable &quot;bar&quot;] [keyword castable] [keyword as] [atom xs:string] },\",\n     \"          [keyword element] [variable text] { [keyword text] { [variable &quot;text&quot;] } },\",\n     \"          [def&variable fn:doc]()[[ [qualifier child::][variable eq][keyword /]([variable @bar] [keyword |] [qualifier attribute::][variable attribute]) [keyword eq] [variable $let] ]],\",\n     \"          [keyword //][variable fn:doc]\",\n     \"      }\");\n\n  MT(\"testEmptySequenceKeyword\",\n     \"[string \\\"foo\\\"] [keyword instance] [keyword of] [keyword empty-sequence]()\");\n\n  MT(\"testMultiAttr\",\n     \"[tag <p ][attribute a1]=[string \\\"foo\\\"] [attribute a2]=[string \\\"bar\\\"][tag >][variable hello] [variable world][tag </p>]\");\n\n  MT(\"test namespaced variable\",\n     \"[keyword declare] [keyword namespace] [variable e] [keyword =] [string \\\"http://example.com/ANamespace\\\"][variable ;declare] [keyword variable] [variable $e:exampleComThisVarIsNotRecognized] [keyword as] [keyword element]([keyword *]) [variable external;]\");\n\n  MT(\"test EQName variable\",\n     \"[keyword declare] [keyword variable] [variable $\\\"http://www.example.com/ns/my\\\":var] [keyword :=] [atom 12][variable ;]\",\n     \"[tag <out>]{[variable $\\\"http://www.example.com/ns/my\\\":var]}[tag </out>]\");\n\n  MT(\"test EQName function\",\n     \"[keyword declare] [keyword function] [def&variable \\\"http://www.example.com/ns/my\\\":fn] ([variable $a] [keyword as] [atom xs:integer]) [keyword as] [atom xs:integer] {\",\n     \"   [variable $a] [keyword +] [atom 2]\",\n     \"}[variable ;]\",\n     \"[tag <out>]{[def&variable \\\"http://www.example.com/ns/my\\\":fn]([atom 12])}[tag </out>]\");\n\n  MT(\"test EQName function with single quotes\",\n     \"[keyword declare] [keyword function] [def&variable 'http://www.example.com/ns/my':fn] ([variable $a] [keyword as] [atom xs:integer]) [keyword as] [atom xs:integer] {\",\n     \"   [variable $a] [keyword +] [atom 2]\",\n     \"}[variable ;]\",\n     \"[tag <out>]{[def&variable 'http://www.example.com/ns/my':fn]([atom 12])}[tag </out>]\");\n\n  MT(\"testProcessingInstructions\",\n     \"[def&variable data]([comment&meta <?target content?>]) [keyword instance] [keyword of] [atom xs:string]\");\n\n  MT(\"testQuoteEscapeDouble\",\n     \"[keyword let] [variable $rootfolder] [keyword :=] [string \\\"c:\\\\builds\\\\winnt\\\\HEAD\\\\qa\\\\scripts\\\\\\\"]\",\n     \"[keyword let] [variable $keysfolder] [keyword :=] [def&variable concat]([variable $rootfolder], [string \\\"keys\\\\\\\"])\");\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/xquery/xquery.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"xquery\", function() {\n\n  // The keywords object is set to the result of this self executing\n  // function. Each keyword is a property of the keywords object whose\n  // value is {type: atype, style: astyle}\n  var keywords = function(){\n    // convenience functions used to build keywords object\n    function kw(type) {return {type: type, style: \"keyword\"};}\n    var operator = kw(\"operator\")\n      , atom = {type: \"atom\", style: \"atom\"}\n      , punctuation = {type: \"punctuation\", style: null}\n      , qualifier = {type: \"axis_specifier\", style: \"qualifier\"};\n\n    // kwObj is what is return from this function at the end\n    var kwObj = {\n      ',': punctuation\n    };\n\n    // a list of 'basic' keywords. For each add a property to kwObj with the value of\n    // {type: basic[i], style: \"keyword\"} e.g. 'after' --> {type: \"after\", style: \"keyword\"}\n    var basic = ['after', 'all', 'allowing', 'ancestor', 'ancestor-or-self', 'any', 'array', 'as',\n    'ascending', 'at', 'attribute', 'base-uri', 'before', 'boundary-space', 'by', 'case', 'cast',\n    'castable', 'catch', 'child', 'collation', 'comment', 'construction', 'contains', 'content',\n    'context', 'copy', 'copy-namespaces', 'count', 'decimal-format', 'declare', 'default', 'delete',\n    'descendant', 'descendant-or-self', 'descending', 'diacritics', 'different', 'distance',\n    'document', 'document-node', 'element', 'else', 'empty', 'empty-sequence', 'encoding', 'end',\n    'entire', 'every', 'exactly', 'except', 'external', 'first', 'following', 'following-sibling',\n    'for', 'from', 'ftand', 'ftnot', 'ft-option', 'ftor', 'function', 'fuzzy', 'greatest', 'group',\n    'if', 'import', 'in', 'inherit', 'insensitive', 'insert', 'instance', 'intersect', 'into',\n    'invoke', 'is', 'item', 'language', 'last', 'lax', 'least', 'let', 'levels', 'lowercase', 'map',\n    'modify', 'module', 'most', 'namespace', 'next', 'no', 'node', 'nodes', 'no-inherit',\n    'no-preserve', 'not', 'occurs', 'of', 'only', 'option', 'order', 'ordered', 'ordering',\n    'paragraph', 'paragraphs', 'parent', 'phrase', 'preceding', 'preceding-sibling', 'preserve',\n    'previous', 'processing-instruction', 'relationship', 'rename', 'replace', 'return',\n    'revalidation', 'same', 'satisfies', 'schema', 'schema-attribute', 'schema-element', 'score',\n    'self', 'sensitive', 'sentence', 'sentences', 'sequence', 'skip', 'sliding', 'some', 'stable',\n    'start', 'stemming', 'stop', 'strict', 'strip', 'switch', 'text', 'then', 'thesaurus', 'times',\n    'to', 'transform', 'treat', 'try', 'tumbling', 'type', 'typeswitch', 'union', 'unordered',\n    'update', 'updating', 'uppercase', 'using', 'validate', 'value', 'variable', 'version',\n    'weight', 'when', 'where', 'wildcards', 'window', 'with', 'without', 'word', 'words', 'xquery'];\n    for(var i=0, l=basic.length; i < l; i++) { kwObj[basic[i]] = kw(basic[i]);};\n\n    // a list of types. For each add a property to kwObj with the value of\n    // {type: \"atom\", style: \"atom\"}\n    var types = ['xs:anyAtomicType', 'xs:anySimpleType', 'xs:anyType', 'xs:anyURI',\n    'xs:base64Binary', 'xs:boolean', 'xs:byte', 'xs:date', 'xs:dateTime', 'xs:dateTimeStamp',\n    'xs:dayTimeDuration', 'xs:decimal', 'xs:double', 'xs:duration', 'xs:ENTITIES', 'xs:ENTITY',\n    'xs:float', 'xs:gDay', 'xs:gMonth', 'xs:gMonthDay', 'xs:gYear', 'xs:gYearMonth', 'xs:hexBinary',\n    'xs:ID', 'xs:IDREF', 'xs:IDREFS', 'xs:int', 'xs:integer', 'xs:item', 'xs:java', 'xs:language',\n    'xs:long', 'xs:Name', 'xs:NCName', 'xs:negativeInteger', 'xs:NMTOKEN', 'xs:NMTOKENS',\n    'xs:nonNegativeInteger', 'xs:nonPositiveInteger', 'xs:normalizedString', 'xs:NOTATION',\n    'xs:numeric', 'xs:positiveInteger', 'xs:precisionDecimal', 'xs:QName', 'xs:short', 'xs:string',\n    'xs:time', 'xs:token', 'xs:unsignedByte', 'xs:unsignedInt', 'xs:unsignedLong',\n    'xs:unsignedShort', 'xs:untyped', 'xs:untypedAtomic', 'xs:yearMonthDuration'];\n    for(var i=0, l=types.length; i < l; i++) { kwObj[types[i]] = atom;};\n\n    // each operator will add a property to kwObj with value of {type: \"operator\", style: \"keyword\"}\n    var operators = ['eq', 'ne', 'lt', 'le', 'gt', 'ge', ':=', '=', '>', '>=', '<', '<=', '.', '|', '?', 'and', 'or', 'div', 'idiv', 'mod', '*', '/', '+', '-'];\n    for(var i=0, l=operators.length; i < l; i++) { kwObj[operators[i]] = operator;};\n\n    // each axis_specifiers will add a property to kwObj with value of {type: \"axis_specifier\", style: \"qualifier\"}\n    var axis_specifiers = [\"self::\", \"attribute::\", \"child::\", \"descendant::\", \"descendant-or-self::\", \"parent::\",\n    \"ancestor::\", \"ancestor-or-self::\", \"following::\", \"preceding::\", \"following-sibling::\", \"preceding-sibling::\"];\n    for(var i=0, l=axis_specifiers.length; i < l; i++) { kwObj[axis_specifiers[i]] = qualifier; };\n\n    return kwObj;\n  }();\n\n  function chain(stream, state, f) {\n    state.tokenize = f;\n    return f(stream, state);\n  }\n\n  // the primary mode tokenizer\n  function tokenBase(stream, state) {\n    var ch = stream.next(),\n        mightBeFunction = false,\n        isEQName = isEQNameAhead(stream);\n\n    // an XML tag (if not in some sub, chained tokenizer)\n    if (ch == \"<\") {\n      if(stream.match(\"!--\", true))\n        return chain(stream, state, tokenXMLComment);\n\n      if(stream.match(\"![CDATA\", false)) {\n        state.tokenize = tokenCDATA;\n        return \"tag\";\n      }\n\n      if(stream.match(\"?\", false)) {\n        return chain(stream, state, tokenPreProcessing);\n      }\n\n      var isclose = stream.eat(\"/\");\n      stream.eatSpace();\n      var tagName = \"\", c;\n      while ((c = stream.eat(/[^\\s\\u00a0=<>\\\"\\'\\/?]/))) tagName += c;\n\n      return chain(stream, state, tokenTag(tagName, isclose));\n    }\n    // start code block\n    else if(ch == \"{\") {\n      pushStateStack(state, { type: \"codeblock\"});\n      return null;\n    }\n    // end code block\n    else if(ch == \"}\") {\n      popStateStack(state);\n      return null;\n    }\n    // if we're in an XML block\n    else if(isInXmlBlock(state)) {\n      if(ch == \">\")\n        return \"tag\";\n      else if(ch == \"/\" && stream.eat(\">\")) {\n        popStateStack(state);\n        return \"tag\";\n      }\n      else\n        return \"variable\";\n    }\n    // if a number\n    else if (/\\d/.test(ch)) {\n      stream.match(/^\\d*(?:\\.\\d*)?(?:E[+\\-]?\\d+)?/);\n      return \"atom\";\n    }\n    // comment start\n    else if (ch === \"(\" && stream.eat(\":\")) {\n      pushStateStack(state, { type: \"comment\"});\n      return chain(stream, state, tokenComment);\n    }\n    // quoted string\n    else if (!isEQName && (ch === '\"' || ch === \"'\"))\n      return chain(stream, state, tokenString(ch));\n    // variable\n    else if(ch === \"$\") {\n      return chain(stream, state, tokenVariable);\n    }\n    // assignment\n    else if(ch ===\":\" && stream.eat(\"=\")) {\n      return \"keyword\";\n    }\n    // open paren\n    else if(ch === \"(\") {\n      pushStateStack(state, { type: \"paren\"});\n      return null;\n    }\n    // close paren\n    else if(ch === \")\") {\n      popStateStack(state);\n      return null;\n    }\n    // open paren\n    else if(ch === \"[\") {\n      pushStateStack(state, { type: \"bracket\"});\n      return null;\n    }\n    // close paren\n    else if(ch === \"]\") {\n      popStateStack(state);\n      return null;\n    }\n    else {\n      var known = keywords.propertyIsEnumerable(ch) && keywords[ch];\n\n      // if there's a EQName ahead, consume the rest of the string portion, it's likely a function\n      if(isEQName && ch === '\\\"') while(stream.next() !== '\"'){}\n      if(isEQName && ch === '\\'') while(stream.next() !== '\\''){}\n\n      // gobble up a word if the character is not known\n      if(!known) stream.eatWhile(/[\\w\\$_-]/);\n\n      // gobble a colon in the case that is a lib func type call fn:doc\n      var foundColon = stream.eat(\":\");\n\n      // if there's not a second colon, gobble another word. Otherwise, it's probably an axis specifier\n      // which should get matched as a keyword\n      if(!stream.eat(\":\") && foundColon) {\n        stream.eatWhile(/[\\w\\$_-]/);\n      }\n      // if the next non whitespace character is an open paren, this is probably a function (if not a keyword of other sort)\n      if(stream.match(/^[ \\t]*\\(/, false)) {\n        mightBeFunction = true;\n      }\n      // is the word a keyword?\n      var word = stream.current();\n      known = keywords.propertyIsEnumerable(word) && keywords[word];\n\n      // if we think it's a function call but not yet known,\n      // set style to variable for now for lack of something better\n      if(mightBeFunction && !known) known = {type: \"function_call\", style: \"variable def\"};\n\n      // if the previous word was element, attribute, axis specifier, this word should be the name of that\n      if(isInXmlConstructor(state)) {\n        popStateStack(state);\n        return \"variable\";\n      }\n      // as previously checked, if the word is element,attribute, axis specifier, call it an \"xmlconstructor\" and\n      // push the stack so we know to look for it on the next word\n      if(word == \"element\" || word == \"attribute\" || known.type == \"axis_specifier\") pushStateStack(state, {type: \"xmlconstructor\"});\n\n      // if the word is known, return the details of that else just call this a generic 'word'\n      return known ? known.style : \"variable\";\n    }\n  }\n\n  // handle comments, including nested\n  function tokenComment(stream, state) {\n    var maybeEnd = false, maybeNested = false, nestedCount = 0, ch;\n    while (ch = stream.next()) {\n      if (ch == \")\" && maybeEnd) {\n        if(nestedCount > 0)\n          nestedCount--;\n        else {\n          popStateStack(state);\n          break;\n        }\n      }\n      else if(ch == \":\" && maybeNested) {\n        nestedCount++;\n      }\n      maybeEnd = (ch == \":\");\n      maybeNested = (ch == \"(\");\n    }\n\n    return \"comment\";\n  }\n\n  // tokenizer for string literals\n  // optionally pass a tokenizer function to set state.tokenize back to when finished\n  function tokenString(quote, f) {\n    return function(stream, state) {\n      var ch;\n\n      if(isInString(state) && stream.current() == quote) {\n        popStateStack(state);\n        if(f) state.tokenize = f;\n        return \"string\";\n      }\n\n      pushStateStack(state, { type: \"string\", name: quote, tokenize: tokenString(quote, f) });\n\n      // if we're in a string and in an XML block, allow an embedded code block\n      if(stream.match(\"{\", false) && isInXmlAttributeBlock(state)) {\n        state.tokenize = tokenBase;\n        return \"string\";\n      }\n\n\n      while (ch = stream.next()) {\n        if (ch ==  quote) {\n          popStateStack(state);\n          if(f) state.tokenize = f;\n          break;\n        }\n        else {\n          // if we're in a string and in an XML block, allow an embedded code block in an attribute\n          if(stream.match(\"{\", false) && isInXmlAttributeBlock(state)) {\n            state.tokenize = tokenBase;\n            return \"string\";\n          }\n\n        }\n      }\n\n      return \"string\";\n    };\n  }\n\n  // tokenizer for variables\n  function tokenVariable(stream, state) {\n    var isVariableChar = /[\\w\\$_-]/;\n\n    // a variable may start with a quoted EQName so if the next character is quote, consume to the next quote\n    if(stream.eat(\"\\\"\")) {\n      while(stream.next() !== '\\\"'){};\n      stream.eat(\":\");\n    } else {\n      stream.eatWhile(isVariableChar);\n      if(!stream.match(\":=\", false)) stream.eat(\":\");\n    }\n    stream.eatWhile(isVariableChar);\n    state.tokenize = tokenBase;\n    return \"variable\";\n  }\n\n  // tokenizer for XML tags\n  function tokenTag(name, isclose) {\n    return function(stream, state) {\n      stream.eatSpace();\n      if(isclose && stream.eat(\">\")) {\n        popStateStack(state);\n        state.tokenize = tokenBase;\n        return \"tag\";\n      }\n      // self closing tag without attributes?\n      if(!stream.eat(\"/\"))\n        pushStateStack(state, { type: \"tag\", name: name, tokenize: tokenBase});\n      if(!stream.eat(\">\")) {\n        state.tokenize = tokenAttribute;\n        return \"tag\";\n      }\n      else {\n        state.tokenize = tokenBase;\n      }\n      return \"tag\";\n    };\n  }\n\n  // tokenizer for XML attributes\n  function tokenAttribute(stream, state) {\n    var ch = stream.next();\n\n    if(ch == \"/\" && stream.eat(\">\")) {\n      if(isInXmlAttributeBlock(state)) popStateStack(state);\n      if(isInXmlBlock(state)) popStateStack(state);\n      return \"tag\";\n    }\n    if(ch == \">\") {\n      if(isInXmlAttributeBlock(state)) popStateStack(state);\n      return \"tag\";\n    }\n    if(ch == \"=\")\n      return null;\n    // quoted string\n    if (ch == '\"' || ch == \"'\")\n      return chain(stream, state, tokenString(ch, tokenAttribute));\n\n    if(!isInXmlAttributeBlock(state))\n      pushStateStack(state, { type: \"attribute\", tokenize: tokenAttribute});\n\n    stream.eat(/[a-zA-Z_:]/);\n    stream.eatWhile(/[-a-zA-Z0-9_:.]/);\n    stream.eatSpace();\n\n    // the case where the attribute has not value and the tag was closed\n    if(stream.match(\">\", false) || stream.match(\"/\", false)) {\n      popStateStack(state);\n      state.tokenize = tokenBase;\n    }\n\n    return \"attribute\";\n  }\n\n  // handle comments, including nested\n  function tokenXMLComment(stream, state) {\n    var ch;\n    while (ch = stream.next()) {\n      if (ch == \"-\" && stream.match(\"->\", true)) {\n        state.tokenize = tokenBase;\n        return \"comment\";\n      }\n    }\n  }\n\n\n  // handle CDATA\n  function tokenCDATA(stream, state) {\n    var ch;\n    while (ch = stream.next()) {\n      if (ch == \"]\" && stream.match(\"]\", true)) {\n        state.tokenize = tokenBase;\n        return \"comment\";\n      }\n    }\n  }\n\n  // handle preprocessing instructions\n  function tokenPreProcessing(stream, state) {\n    var ch;\n    while (ch = stream.next()) {\n      if (ch == \"?\" && stream.match(\">\", true)) {\n        state.tokenize = tokenBase;\n        return \"comment meta\";\n      }\n    }\n  }\n\n\n  // functions to test the current context of the state\n  function isInXmlBlock(state) { return isIn(state, \"tag\"); }\n  function isInXmlAttributeBlock(state) { return isIn(state, \"attribute\"); }\n  function isInXmlConstructor(state) { return isIn(state, \"xmlconstructor\"); }\n  function isInString(state) { return isIn(state, \"string\"); }\n\n  function isEQNameAhead(stream) {\n    // assume we've already eaten a quote (\")\n    if(stream.current() === '\"')\n      return stream.match(/^[^\\\"]+\\\"\\:/, false);\n    else if(stream.current() === '\\'')\n      return stream.match(/^[^\\\"]+\\'\\:/, false);\n    else\n      return false;\n  }\n\n  function isIn(state, type) {\n    return (state.stack.length && state.stack[state.stack.length - 1].type == type);\n  }\n\n  function pushStateStack(state, newState) {\n    state.stack.push(newState);\n  }\n\n  function popStateStack(state) {\n    state.stack.pop();\n    var reinstateTokenize = state.stack.length && state.stack[state.stack.length-1].tokenize;\n    state.tokenize = reinstateTokenize || tokenBase;\n  }\n\n  // the interface for the mode API\n  return {\n    startState: function() {\n      return {\n        tokenize: tokenBase,\n        cc: [],\n        stack: []\n      };\n    },\n\n    token: function(stream, state) {\n      if (stream.eatSpace()) return null;\n      var style = state.tokenize(stream, state);\n      return style;\n    },\n\n    blockCommentStart: \"(:\",\n    blockCommentEnd: \":)\"\n\n  };\n\n});\n\nCodeMirror.defineMIME(\"application/xquery\", \"xquery\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/yacas/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: yacas mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=stylesheet href=../../lib/codemirror.css>\n<script src=../../lib/codemirror.js></script>\n<script src=../../addon/edit/matchbrackets.js></script>\n<script src=yacas.js></script>\n<style type=text/css>\n  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\n</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">yacas</a>\n  </ul>\n</div>\n\n<article>\n<h2>yacas mode</h2>\n\n\n<textarea id=\"yacasCode\">\n// example yacas code\nGraph(edges_IsList) <-- [\n    Local(v, e, f, t);\n\n    vertices := {};\n\n    ForEach (e, edges) [\n        If (IsList(e), e := Head(e));\n        {f, t} := Tail(Listify(e));\n\n        DestructiveAppend(vertices, f);\n        DestructiveAppend(vertices, t);\n    ];\n\n    Graph(RemoveDuplicates(vertices), edges);\n];\n\n10 # IsGraph(Graph(vertices_IsList, edges_IsList)) <-- True;\n20 # IsGraph(_x) <-- False;\n\nEdges(Graph(vertices_IsList, edges_IsList)) <-- edges;\nVertices(Graph(vertices_IsList, edges_IsList)) <-- vertices;\n\nAdjacencyList(g_IsGraph) <-- [\n    Local(l, vertices, edges, e, op, f, t);\n\n    l := Association'Create();\n\n    vertices := Vertices(g);\n    ForEach (v, vertices)\n        Association'Set(l, v, {});\n\n    edges := Edges(g);\n\n    ForEach(e, edges) [\n        If (IsList(e), e := Head(e));\n        {op, f, t} := Listify(e);\n        DestructiveAppend(Association'Get(l, f), t);\n        If (String(op) = \"<->\", DestructiveAppend(Association'Get(l, t), f));\n    ];\n\n    l;\n];\n</textarea>\n\n<script>\n  var yacasEditor = CodeMirror.fromTextArea(document.getElementById('yacasCode'), {\n    mode: 'text/x-yacas',\n    lineNumbers: true,\n    matchBrackets: true\n  });\n</script>\n\n<p><strong>MIME types defined:</strong> <code>text/x-yacas</code> (yacas).</p>\n</article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/yacas/yacas.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// Yacas mode copyright (c) 2015 by Grzegorz Mazur\n// Loosely based on mathematica mode by Calin Barbat\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode('yacas', function(_config, _parserConfig) {\n\n  function words(str) {\n    var obj = {}, words = str.split(\" \");\n    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n  }\n\n  var bodiedOps = words(\"Assert BackQuote D Defun Deriv For ForEach FromFile \" +\n                        \"FromString Function Integrate InverseTaylor Limit \" +\n                        \"LocalSymbols Macro MacroRule MacroRulePattern \" +\n                        \"NIntegrate Rule RulePattern Subst TD TExplicitSum \" +\n                        \"TSum Taylor Taylor1 Taylor2 Taylor3 ToFile \" +\n                        \"ToStdout ToString TraceRule Until While\");\n\n  // patterns\n  var pFloatForm  = \"(?:(?:\\\\.\\\\d+|\\\\d+\\\\.\\\\d*|\\\\d+)(?:[eE][+-]?\\\\d+)?)\";\n  var pIdentifier = \"(?:[a-zA-Z\\\\$'][a-zA-Z0-9\\\\$']*)\";\n\n  // regular expressions\n  var reFloatForm    = new RegExp(pFloatForm);\n  var reIdentifier   = new RegExp(pIdentifier);\n  var rePattern      = new RegExp(pIdentifier + \"?_\" + pIdentifier);\n  var reFunctionLike = new RegExp(pIdentifier + \"\\\\s*\\\\(\");\n\n  function tokenBase(stream, state) {\n    var ch;\n\n    // get next character\n    ch = stream.next();\n\n    // string\n    if (ch === '\"') {\n      state.tokenize = tokenString;\n      return state.tokenize(stream, state);\n    }\n\n    // comment\n    if (ch === '/') {\n      if (stream.eat('*')) {\n        state.tokenize = tokenComment;\n        return state.tokenize(stream, state);\n      }\n      if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n    }\n\n    // go back one character\n    stream.backUp(1);\n\n    // update scope info\n    var m = stream.match(/^(\\w+)\\s*\\(/, false);\n    if (m !== null && bodiedOps.hasOwnProperty(m[1]))\n      state.scopes.push('bodied');\n\n    var scope = currentScope(state);\n\n    if (scope === 'bodied' && ch === '[')\n      state.scopes.pop();\n\n    if (ch === '[' || ch === '{' || ch === '(')\n      state.scopes.push(ch);\n\n    scope = currentScope(state);\n\n    if (scope === '[' && ch === ']' ||\n        scope === '{' && ch === '}' ||\n        scope === '(' && ch === ')')\n      state.scopes.pop();\n\n    if (ch === ';') {\n      while (scope === 'bodied') {\n        state.scopes.pop();\n        scope = currentScope(state);\n      }\n    }\n\n    // look for ordered rules\n    if (stream.match(/\\d+ *#/, true, false)) {\n      return 'qualifier';\n    }\n\n    // look for numbers\n    if (stream.match(reFloatForm, true, false)) {\n      return 'number';\n    }\n\n    // look for placeholders\n    if (stream.match(rePattern, true, false)) {\n      return 'variable-3';\n    }\n\n    // match all braces separately\n    if (stream.match(/(?:\\[|\\]|{|}|\\(|\\))/, true, false)) {\n      return 'bracket';\n    }\n\n    // literals looking like function calls\n    if (stream.match(reFunctionLike, true, false)) {\n      stream.backUp(1);\n      return 'variable';\n    }\n\n    // all other identifiers\n    if (stream.match(reIdentifier, true, false)) {\n      return 'variable-2';\n    }\n\n    // operators; note that operators like @@ or /; are matched separately for each symbol.\n    if (stream.match(/(?:\\\\|\\+|\\-|\\*|\\/|,|;|\\.|:|@|~|=|>|<|&|\\||_|`|'|\\^|\\?|!|%|#)/, true, false)) {\n      return 'operator';\n    }\n\n    // everything else is an error\n    return 'error';\n  }\n\n  function tokenString(stream, state) {\n    var next, end = false, escaped = false;\n    while ((next = stream.next()) != null) {\n      if (next === '\"' && !escaped) {\n        end = true;\n        break;\n      }\n      escaped = !escaped && next === '\\\\';\n    }\n    if (end && !escaped) {\n      state.tokenize = tokenBase;\n    }\n    return 'string';\n  };\n\n  function tokenComment(stream, state) {\n    var prev, next;\n    while((next = stream.next()) != null) {\n      if (prev === '*' && next === '/') {\n        state.tokenize = tokenBase;\n        break;\n      }\n      prev = next;\n    }\n    return 'comment';\n  }\n\n  function currentScope(state) {\n    var scope = null;\n    if (state.scopes.length > 0)\n      scope = state.scopes[state.scopes.length - 1];\n    return scope;\n  }\n\n  return {\n    startState: function() {\n      return {\n        tokenize: tokenBase,\n        scopes: []\n      };\n    },\n    token: function(stream, state) {\n      if (stream.eatSpace()) return null;\n      return state.tokenize(stream, state);\n    },\n    indent: function(state, textAfter) {\n      if (state.tokenize !== tokenBase && state.tokenize !== null)\n        return CodeMirror.Pass;\n\n      var delta = 0;\n      if (textAfter === ']' || textAfter === '];' ||\n          textAfter === '}' || textAfter === '};' ||\n          textAfter === ');')\n        delta = -1;\n\n      return (state.scopes.length + delta) * _config.indentUnit;\n    },\n    electricChars: \"{}[]();\",\n    blockCommentStart: \"/*\",\n    blockCommentEnd: \"*/\",\n    lineComment: \"//\"\n  };\n});\n\nCodeMirror.defineMIME('text/x-yacas', {\n  name: 'yacas'\n});\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/yaml/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: YAML mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"yaml.js\"></script>\n<style>.CodeMirror { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">YAML</a>\n  </ul>\n</div>\n\n<article>\n<h2>YAML mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n--- # Favorite movies\n- Casablanca\n- North by Northwest\n- The Man Who Wasn't There\n--- # Shopping list\n[milk, pumpkin pie, eggs, juice]\n--- # Indented Blocks, common in YAML data files, use indentation and new lines to separate the key: value pairs\n  name: John Smith\n  age: 33\n--- # Inline Blocks, common in YAML data streams, use commas to separate the key: value pairs between braces\n{name: John Smith, age: 33}\n---\nreceipt:     Oz-Ware Purchase Invoice\ndate:        2007-08-06\ncustomer:\n    given:   Dorothy\n    family:  Gale\n\nitems:\n    - part_no:   A4786\n      descrip:   Water Bucket (Filled)\n      price:     1.47\n      quantity:  4\n\n    - part_no:   E1628\n      descrip:   High Heeled \"Ruby\" Slippers\n      size:       8\n      price:     100.27\n      quantity:  1\n\nbill-to:  &id001\n    street: |\n            123 Tornado Alley\n            Suite 16\n    city:   East Centerville\n    state:  KS\n\nship-to:  *id001\n\nspecialDelivery:  >\n    Follow the Yellow Brick\n    Road to the Emerald City.\n    Pay no attention to the\n    man behind the curtain.\n...\n</textarea></form>\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {});\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-yaml</code>.</p>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/yaml/yaml.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"yaml\", function() {\n\n  var cons = ['true', 'false', 'on', 'off', 'yes', 'no'];\n  var keywordRegex = new RegExp(\"\\\\b((\"+cons.join(\")|(\")+\"))$\", 'i');\n\n  return {\n    token: function(stream, state) {\n      var ch = stream.peek();\n      var esc = state.escaped;\n      state.escaped = false;\n      /* comments */\n      if (ch == \"#\" && (stream.pos == 0 || /\\s/.test(stream.string.charAt(stream.pos - 1)))) {\n        stream.skipToEnd();\n        return \"comment\";\n      }\n\n      if (stream.match(/^('([^']|\\\\.)*'?|\"([^\"]|\\\\.)*\"?)/))\n        return \"string\";\n\n      if (state.literal && stream.indentation() > state.keyCol) {\n        stream.skipToEnd(); return \"string\";\n      } else if (state.literal) { state.literal = false; }\n      if (stream.sol()) {\n        state.keyCol = 0;\n        state.pair = false;\n        state.pairStart = false;\n        /* document start */\n        if(stream.match('---')) { return \"def\"; }\n        /* document end */\n        if (stream.match('...')) { return \"def\"; }\n        /* array list item */\n        if (stream.match(/\\s*-\\s+/)) { return 'meta'; }\n      }\n      /* inline pairs/lists */\n      if (stream.match(/^(\\{|\\}|\\[|\\])/)) {\n        if (ch == '{')\n          state.inlinePairs++;\n        else if (ch == '}')\n          state.inlinePairs--;\n        else if (ch == '[')\n          state.inlineList++;\n        else\n          state.inlineList--;\n        return 'meta';\n      }\n\n      /* list separator */\n      if (state.inlineList > 0 && !esc && ch == ',') {\n        stream.next();\n        return 'meta';\n      }\n      /* pairs separator */\n      if (state.inlinePairs > 0 && !esc && ch == ',') {\n        state.keyCol = 0;\n        state.pair = false;\n        state.pairStart = false;\n        stream.next();\n        return 'meta';\n      }\n\n      /* start of value of a pair */\n      if (state.pairStart) {\n        /* block literals */\n        if (stream.match(/^\\s*(\\||\\>)\\s*/)) { state.literal = true; return 'meta'; };\n        /* references */\n        if (stream.match(/^\\s*(\\&|\\*)[a-z0-9\\._-]+\\b/i)) { return 'variable-2'; }\n        /* numbers */\n        if (state.inlinePairs == 0 && stream.match(/^\\s*-?[0-9\\.\\,]+\\s?$/)) { return 'number'; }\n        if (state.inlinePairs > 0 && stream.match(/^\\s*-?[0-9\\.\\,]+\\s?(?=(,|}))/)) { return 'number'; }\n        /* keywords */\n        if (stream.match(keywordRegex)) { return 'keyword'; }\n      }\n\n      /* pairs (associative arrays) -> key */\n      if (!state.pair && stream.match(/^\\s*(?:[,\\[\\]{}&*!|>'\"%@`][^\\s'\":]|[^\\s,\\[\\]{}#&*!|>'\"%@`])[^#:]*(?=:($|\\s))/)) {\n        state.pair = true;\n        state.keyCol = stream.indentation();\n        return \"atom\";\n      }\n      if (state.pair && stream.match(/^:\\s*/)) { state.pairStart = true; return 'meta'; }\n\n      /* nothing found, continue */\n      state.pairStart = false;\n      state.escaped = (ch == '\\\\');\n      stream.next();\n      return null;\n    },\n    startState: function() {\n      return {\n        pair: false,\n        pairStart: false,\n        keyCol: 0,\n        inlinePairs: 0,\n        inlineList: 0,\n        literal: false,\n        escaped: false\n      };\n    },\n    lineComment: \"#\",\n    fold: \"indent\"\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-yaml\", \"yaml\");\nCodeMirror.defineMIME(\"text/yaml\", \"yaml\");\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/yaml-frontmatter/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: YAML front matter mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/mode/overlay.js\"></script>\n<script src=\"../markdown/markdown.js\"></script>\n<script src=\"../gfm/gfm.js\"></script>\n<script src=\"../yaml/yaml.js\"></script>\n<script src=\"yaml-frontmatter.js\"></script>\n<style>.CodeMirror { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">YAML-Frontmatter</a>\n  </ul>\n</div>\n\n<article>\n<h2>YAML front matter mode</h2>\n<form><textarea id=\"code\" name=\"code\">\n---\nreceipt:     Oz-Ware Purchase Invoice\ndate:        2007-08-06\ncustomer:\n    given:   Dorothy\n    family:  Gale\n\nitems:\n    - part_no:   A4786\n      descrip:   Water Bucket (Filled)\n      price:     1.47\n      quantity:  4\n\n    - part_no:   E1628\n      descrip:   High Heeled \"Ruby\" Slippers\n      size:       8\n      price:     100.27\n      quantity:  1\n\nbill-to:  &id001\n    street: |\n            123 Tornado Alley\n            Suite 16\n    city:   East Centerville\n    state:  KS\n\nship-to:  *id001\n\nspecialDelivery:  >\n    Follow the Yellow Brick\n    Road to the Emerald City.\n    Pay no attention to the\n    man behind the curtain.\n---\n\nGitHub Flavored Markdown\n========================\n\nEverything from markdown plus GFM features:\n\n## URL autolinking\n\nUnderscores_are_allowed_between_words.\n\n## Strikethrough text\n\nGFM adds syntax to strikethrough text, which is missing from standard Markdown.\n\n~~Mistaken text.~~\n~~**works with other formatting**~~\n\n~~spans across\nlines~~\n\n## Fenced code blocks (and syntax highlighting)\n\n```javascript\nfor (var i = 0; i &lt; items.length; i++) {\n    console.log(items[i], i); // log them\n}\n```\n\n## Task Lists\n\n- [ ] Incomplete task list item\n- [x] **Completed** task list item\n\n## A bit of GitHub spice\n\n* SHA: be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2\n* User@SHA ref: mojombo@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2\n* User/Project@SHA: mojombo/god@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2\n* \\#Num: #1\n* User/#Num: mojombo#1\n* User/Project#Num: mojombo/god#1\n\nSee http://github.github.com/github-flavored-markdown/.\n</textarea></form>\n\n<p>Defines a mode that parses\na <a href=\"http://jekyllrb.com/docs/frontmatter/\">YAML frontmatter</a>\nat the start of a file, switching to a base mode at the end of that.\nTakes a mode configuration option <code>base</code> to configure the\nbase mode, which defaults to <code>\"gfm\"</code>.</p>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {mode: \"yaml-frontmatter\"});\n    </script>\n\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/yaml-frontmatter/yaml-frontmatter.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function (mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"), require(\"../yaml/yaml\"))\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\", \"../yaml/yaml\"], mod)\n  else // Plain browser env\n    mod(CodeMirror)\n})(function (CodeMirror) {\n\n  var START = 0, FRONTMATTER = 1, BODY = 2\n\n  // a mixed mode for Markdown text with an optional YAML front matter\n  CodeMirror.defineMode(\"yaml-frontmatter\", function (config, parserConfig) {\n    var yamlMode = CodeMirror.getMode(config, \"yaml\")\n    var innerMode = CodeMirror.getMode(config, parserConfig && parserConfig.base || \"gfm\")\n\n    function localMode(state) {\n      return state.state == FRONTMATTER ? {mode: yamlMode, state: state.yaml} : {mode: innerMode, state: state.inner}\n    }\n\n    return {\n      startState: function () {\n        return {\n          state: START,\n          yaml: null,\n          inner: CodeMirror.startState(innerMode)\n        }\n      },\n      copyState: function (state) {\n        return {\n          state: state.state,\n          yaml: state.yaml && CodeMirror.copyState(yamlMode, state.yaml),\n          inner: CodeMirror.copyState(innerMode, state.inner)\n        }\n      },\n      token: function (stream, state) {\n        if (state.state == START) {\n          if (stream.match('---', false)) {\n            state.state = FRONTMATTER\n            state.yaml = CodeMirror.startState(yamlMode)\n            return yamlMode.token(stream, state.yaml)\n          } else {\n            state.state = BODY\n            return innerMode.token(stream, state.inner)\n          }\n        } else if (state.state == FRONTMATTER) {\n          var end = stream.sol() && stream.match(/(---|\\.\\.\\.)/, false)\n          var style = yamlMode.token(stream, state.yaml)\n          if (end) {\n            state.state = BODY\n            state.yaml = null\n          }\n          return style\n        } else {\n          return innerMode.token(stream, state.inner)\n        }\n      },\n      innerMode: localMode,\n      indent: function(state, a, b) {\n        var m = localMode(state)\n        return m.mode.indent ? m.mode.indent(m.state, a, b) : CodeMirror.Pass\n      },\n      blankLine: function (state) {\n        var m = localMode(state)\n        if (m.mode.blankLine) return m.mode.blankLine(m.state)\n      }\n    }\n  })\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/z80/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: Z80 assembly mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"z80.js\"></script>\n<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"https://codemirror.net/5\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\" alt=\"\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror5\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">Z80 assembly</a>\n  </ul>\n</div>\n\n<article>\n<h2>Z80 assembly mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n#include    \"ti83plus.inc\"\n#define     progStart   $9D95\n    .org progStart-2\n    .db $BB,$6D\n\n    bcall(_ClrLCDFull)\n    ld hl,0\n    ld (CurCol),hl\n    ld hl,Message\n    bcall(_PutS) ; Displays the string\n    bcall(_NewLine)\n    ret\nMessage:\n    .db \"Hello world!\",0\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true\n      });\n    </script>\n\n    <p><strong>MIME types defined:</strong> <code>text/x-z80</code>, <code>text/x-ez80</code>.</p>\n  </article>\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/mode/z80/z80.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n  mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n  define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n  mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode('z80', function(_config, parserConfig) {\n  var ez80 = parserConfig.ez80;\n  var keywords1, keywords2;\n  if (ez80) {\n    keywords1 = /^(exx?|(ld|cp)([di]r?)?|[lp]ea|pop|push|ad[cd]|cpl|daa|dec|inc|neg|sbc|sub|and|bit|[cs]cf|x?or|res|set|r[lr]c?a?|r[lr]d|s[lr]a|srl|djnz|nop|[de]i|halt|im|in([di]mr?|ir?|irx|2r?)|ot(dmr?|[id]rx|imr?)|out(0?|[di]r?|[di]2r?)|tst(io)?|slp)(\\.([sl]?i)?[sl])?\\b/i;\n    keywords2 = /^(((call|j[pr]|rst|ret[in]?)(\\.([sl]?i)?[sl])?)|(rs|st)mix)\\b/i;\n  } else {\n    keywords1 = /^(exx?|(ld|cp|in)([di]r?)?|pop|push|ad[cd]|cpl|daa|dec|inc|neg|sbc|sub|and|bit|[cs]cf|x?or|res|set|r[lr]c?a?|r[lr]d|s[lr]a|srl|djnz|nop|rst|[de]i|halt|im|ot[di]r|out[di]?)\\b/i;\n    keywords2 = /^(call|j[pr]|ret[in]?|b_?(call|jump))\\b/i;\n  }\n\n  var variables1 = /^(af?|bc?|c|de?|e|hl?|l|i[xy]?|r|sp)\\b/i;\n  var variables2 = /^(n?[zc]|p[oe]?|m)\\b/i;\n  var errors = /^([hl][xy]|i[xy][hl]|slia|sll)\\b/i;\n  var numbers = /^([\\da-f]+h|[0-7]+o|[01]+b|\\d+d?)\\b/i;\n\n  return {\n    startState: function() {\n      return {\n        context: 0\n      };\n    },\n    token: function(stream, state) {\n      if (!stream.column())\n        state.context = 0;\n\n      if (stream.eatSpace())\n        return null;\n\n      var w;\n\n      if (stream.eatWhile(/\\w/)) {\n        if (ez80 && stream.eat('.')) {\n          stream.eatWhile(/\\w/);\n        }\n        w = stream.current();\n\n        if (stream.indentation()) {\n          if ((state.context == 1 || state.context == 4) && variables1.test(w)) {\n            state.context = 4;\n            return 'var2';\n          }\n\n          if (state.context == 2 && variables2.test(w)) {\n            state.context = 4;\n            return 'var3';\n          }\n\n          if (keywords1.test(w)) {\n            state.context = 1;\n            return 'keyword';\n          } else if (keywords2.test(w)) {\n            state.context = 2;\n            return 'keyword';\n          } else if (state.context == 4 && numbers.test(w)) {\n            return 'number';\n          }\n\n          if (errors.test(w))\n            return 'error';\n        } else if (stream.match(numbers)) {\n          return 'number';\n        } else {\n          return null;\n        }\n      } else if (stream.eat(';')) {\n        stream.skipToEnd();\n        return 'comment';\n      } else if (stream.eat('\"')) {\n        while (w = stream.next()) {\n          if (w == '\"')\n            break;\n\n          if (w == '\\\\')\n            stream.next();\n        }\n        return 'string';\n      } else if (stream.eat('\\'')) {\n        if (stream.match(/\\\\?.'/))\n          return 'number';\n      } else if (stream.eat('.') || stream.sol() && stream.eat('#')) {\n        state.context = 5;\n\n        if (stream.eatWhile(/\\w/))\n          return 'def';\n      } else if (stream.eat('$')) {\n        if (stream.eatWhile(/[\\da-f]/i))\n          return 'number';\n      } else if (stream.eat('%')) {\n        if (stream.eatWhile(/[01]/))\n          return 'number';\n      } else {\n        stream.next();\n      }\n      return null;\n    }\n  };\n});\n\nCodeMirror.defineMIME(\"text/x-z80\", \"z80\");\nCodeMirror.defineMIME(\"text/x-ez80\", { name: \"z80\", ez80: true });\n\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/package.json",
    "content": "{\n  \"name\": \"codemirror\",\n  \"version\": \"5.65.16\",\n  \"main\": \"lib/codemirror.js\",\n  \"style\": \"lib/codemirror.css\",\n  \"author\": {\n    \"name\": \"Marijn Haverbeke\",\n    \"email\": \"marijn@haverbeke.berlin\",\n    \"url\": \"http://marijnhaverbeke.nl\"\n  },\n  \"description\": \"Full-featured in-browser code editor\",\n  \"license\": \"MIT\",\n  \"directories\": {\n    \"lib\": \"./lib\"\n  },\n  \"scripts\": {\n    \"build\": \"rollup -c\",\n    \"watch\": \"rollup -w -c\",\n    \"prepare\": \"npm run-script build\",\n    \"test\": \"node ./test/run.js\",\n    \"lint\": \"bin/lint\"\n  },\n  \"devDependencies\": {\n    \"@rollup/plugin-buble\": \"^0.21.3\",\n    \"blint\": \"^1.1.2\",\n    \"cm5-vim\": \"^0.0.5\",\n    \"node-static\": \"0.7.11\",\n    \"puppeteer\": \"^1.20.0\",\n    \"rollup\": \"^1.26.3\",\n    \"rollup-plugin-copy\": \"^3.4.0\"\n  },\n  \"bugs\": \"http://github.com/codemirror/CodeMirror/issues\",\n  \"keywords\": [\n    \"JavaScript\",\n    \"CodeMirror\",\n    \"Editor\"\n  ],\n  \"homepage\": \"https://codemirror.net/5/\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/codemirror/CodeMirror.git\"\n  },\n  \"jspm\": {\n    \"directories\": {},\n    \"dependencies\": {},\n    \"devDependencies\": {}\n  },\n  \"dependencies\": {},\n  \"publishConfig\": {\n    \"tag\": \"version5\"\n  }\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/rollup.config.js",
    "content": "import buble from '@rollup/plugin-buble';\nimport copy from 'rollup-plugin-copy'\n\nlet copyVim = copy({\n  targets: [\n    { \n      src: require.resolve(\"cm5-vim/vim.js\").replace(/\\\\/g,  \"/\"), \n      dest: \"./keymap\" \n    }\n  ]\n});\n\nexport default [\n  {\n    input: \"src/codemirror.js\",\n    output: {\n      banner: `// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n// This is CodeMirror (https://codemirror.net/5), a code editor\n// implemented in JavaScript on top of the browser's DOM.\n//\n// You can find some technical background for some of the code below\n// at http://marijnhaverbeke.nl/blog/#cm-internals .\n`,\n      format: \"umd\",\n      file: \"lib/codemirror.js\",\n      name: \"CodeMirror\"\n    },\n    plugins: [ buble({namedFunctionExpressions: false}), copyVim ]\n  },\n  {\n    input: [\"src/addon/runmode/runmode-standalone.js\"],\n    output: {\n      format: \"iife\",\n      file: \"addon/runmode/runmode-standalone.js\",\n      name: \"CodeMirror\",\n      freeze: false, // IE8 doesn't support Object.freeze.\n    },\n    plugins: [ buble({namedFunctionExpressions: false}) ]\n  },\n  {\n    input: [\"src/addon/runmode/runmode.node.js\"],\n    output: {\n      format: \"cjs\",\n      file: \"addon/runmode/runmode.node.js\",\n      name: \"CodeMirror\",\n      freeze: false, // IE8 doesn't support Object.freeze.\n    },\n    plugins: [ buble({namedFunctionExpressions: false}) ]\n  },\n];\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/addon/runmode/codemirror-standalone.js",
    "content": "import StringStream from \"../../util/StringStream.js\"\nimport { countColumn } from \"../../util/misc.js\"\nimport * as modeMethods from \"../../modes.js\"\n\n// declare global: globalThis, CodeMirror\n\n// Create a minimal CodeMirror needed to use runMode, and assign to root.\nvar root = typeof globalThis !== 'undefined' ? globalThis : window\nroot.CodeMirror = {}\n\n// Copy StringStream and mode methods into CodeMirror object.\nCodeMirror.StringStream = StringStream\nfor (var exported in modeMethods) CodeMirror[exported] = modeMethods[exported]\n\n// Minimal default mode.\nCodeMirror.defineMode(\"null\", () => ({token: stream => stream.skipToEnd()}))\nCodeMirror.defineMIME(\"text/plain\", \"null\")\n\nCodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min\nCodeMirror.splitLines = function(string) { return string.split(/\\r?\\n|\\r/) }\nCodeMirror.countColumn = countColumn\n\nCodeMirror.defaults = { indentUnit: 2 }\nexport default CodeMirror\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/addon/runmode/codemirror.node.js",
    "content": "import StringStream from \"../../util/StringStream.js\"\nimport * as modeMethods from \"../../modes.js\"\nimport {countColumn} from \"../../util/misc.js\"\n\n// Copy StringStream and mode methods into exports (CodeMirror) object.\nexports.StringStream = StringStream\nexports.countColumn = countColumn\nfor (var exported in modeMethods) exports[exported] = modeMethods[exported]\n\n// Shim library CodeMirror with the minimal CodeMirror defined above.\nrequire.cache[require.resolve(\"../../lib/codemirror\")] = require.cache[require.resolve(\"./runmode.node\")]\nrequire.cache[require.resolve(\"../../addon/runmode/runmode\")] = require.cache[require.resolve(\"./runmode.node\")]\n\n// Minimal default mode.\nexports.defineMode(\"null\", () => ({token: stream => stream.skipToEnd()}))\nexports.defineMIME(\"text/plain\", \"null\")\n\nexports.registerHelper = exports.registerGlobalHelper = Math.min\nexports.splitLines = function(string) { return string.split(/\\r?\\n|\\r/) }\n\nexports.defaults = { indentUnit: 2 }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/addon/runmode/runmode-standalone.js",
    "content": "import \"./codemirror-standalone.js\"\nimport \"../../../addon/runmode/runmode.js\""
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/addon/runmode/runmode.node.js",
    "content": "import \"./codemirror.node.js\"\nimport \"../../../addon/runmode/runmode.js\""
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/codemirror.js",
    "content": "import { CodeMirror } from \"./edit/main.js\"\n\nexport default CodeMirror\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/Display.js",
    "content": "import { gecko, ie, ie_version, mobile, webkit, chrome, chrome_version } from \"../util/browser.js\"\nimport { elt, eltP } from \"../util/dom.js\"\nimport { scrollerGap } from \"../util/misc.js\"\nimport { getGutters, renderGutters } from \"./gutters.js\"\n\n// The display handles the DOM integration, both for input reading\n// and content drawing. It holds references to DOM nodes and\n// display-related state.\n\nexport function Display(place, doc, input, options) {\n  let d = this\n  this.input = input\n\n  // Covers bottom-right square when both scrollbars are present.\n  d.scrollbarFiller = elt(\"div\", null, \"CodeMirror-scrollbar-filler\")\n  d.scrollbarFiller.setAttribute(\"cm-not-content\", \"true\")\n  // Covers bottom of gutter when coverGutterNextToScrollbar is on\n  // and h scrollbar is present.\n  d.gutterFiller = elt(\"div\", null, \"CodeMirror-gutter-filler\")\n  d.gutterFiller.setAttribute(\"cm-not-content\", \"true\")\n  // Will contain the actual code, positioned to cover the viewport.\n  d.lineDiv = eltP(\"div\", null, \"CodeMirror-code\")\n  // Elements are added to these to represent selection and cursors.\n  d.selectionDiv = elt(\"div\", null, null, \"position: relative; z-index: 1\")\n  d.cursorDiv = elt(\"div\", null, \"CodeMirror-cursors\")\n  // A visibility: hidden element used to find the size of things.\n  d.measure = elt(\"div\", null, \"CodeMirror-measure\")\n  // When lines outside of the viewport are measured, they are drawn in this.\n  d.lineMeasure = elt(\"div\", null, \"CodeMirror-measure\")\n  // Wraps everything that needs to exist inside the vertically-padded coordinate system\n  d.lineSpace = eltP(\"div\", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],\n                    null, \"position: relative; outline: none\")\n  let lines = eltP(\"div\", [d.lineSpace], \"CodeMirror-lines\")\n  // Moved around its parent to cover visible view.\n  d.mover = elt(\"div\", [lines], null, \"position: relative\")\n  // Set to the height of the document, allowing scrolling.\n  d.sizer = elt(\"div\", [d.mover], \"CodeMirror-sizer\")\n  d.sizerWidth = null\n  // Behavior of elts with overflow: auto and padding is\n  // inconsistent across browsers. This is used to ensure the\n  // scrollable area is big enough.\n  d.heightForcer = elt(\"div\", null, null, \"position: absolute; height: \" + scrollerGap + \"px; width: 1px;\")\n  // Will contain the gutters, if any.\n  d.gutters = elt(\"div\", null, \"CodeMirror-gutters\")\n  d.lineGutter = null\n  // Actual scrollable element.\n  d.scroller = elt(\"div\", [d.sizer, d.heightForcer, d.gutters], \"CodeMirror-scroll\")\n  d.scroller.setAttribute(\"tabIndex\", \"-1\")\n  // The element in which the editor lives.\n  d.wrapper = elt(\"div\", [d.scrollbarFiller, d.gutterFiller, d.scroller], \"CodeMirror\")\n  // See #6982. FIXME remove when this has been fixed for a while in Chrome\n  if (chrome && chrome_version >= 105) d.wrapper.style.clipPath = \"inset(0px)\"\n\n  // This attribute is respected by automatic translation systems such as Google Translate,\n  // and may also be respected by tools used by human translators.\n  d.wrapper.setAttribute('translate', 'no')\n\n  // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)\n  if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 }\n  if (!webkit && !(gecko && mobile)) d.scroller.draggable = true\n\n  if (place) {\n    if (place.appendChild) place.appendChild(d.wrapper)\n    else place(d.wrapper)\n  }\n\n  // Current rendered range (may be bigger than the view window).\n  d.viewFrom = d.viewTo = doc.first\n  d.reportedViewFrom = d.reportedViewTo = doc.first\n  // Information about the rendered lines.\n  d.view = []\n  d.renderedView = null\n  // Holds info about a single rendered line when it was rendered\n  // for measurement, while not in view.\n  d.externalMeasured = null\n  // Empty space (in pixels) above the view\n  d.viewOffset = 0\n  d.lastWrapHeight = d.lastWrapWidth = 0\n  d.updateLineNumbers = null\n\n  d.nativeBarWidth = d.barHeight = d.barWidth = 0\n  d.scrollbarsClipped = false\n\n  // Used to only resize the line number gutter when necessary (when\n  // the amount of lines crosses a boundary that makes its width change)\n  d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null\n  // Set to true when a non-horizontal-scrolling line widget is\n  // added. As an optimization, line widget aligning is skipped when\n  // this is false.\n  d.alignWidgets = false\n\n  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null\n\n  // Tracks the maximum line length so that the horizontal scrollbar\n  // can be kept static when scrolling.\n  d.maxLine = null\n  d.maxLineLength = 0\n  d.maxLineChanged = false\n\n  // Used for measuring wheel scrolling granularity\n  d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null\n\n  // True when shift is held down.\n  d.shift = false\n\n  // Used to track whether anything happened since the context menu\n  // was opened.\n  d.selForContextMenu = null\n\n  d.activeTouch = null\n\n  d.gutterSpecs = getGutters(options.gutters, options.lineNumbers)\n  renderGutters(d)\n\n  input.init(d)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/focus.js",
    "content": "import { restartBlink } from \"./selection.js\"\nimport { webkit } from \"../util/browser.js\"\nimport { addClass, rmClass } from \"../util/dom.js\"\nimport { signal } from \"../util/event.js\"\n\nexport function ensureFocus(cm) {\n  if (!cm.hasFocus()) {\n    cm.display.input.focus()\n    if (!cm.state.focused) onFocus(cm)\n  }\n}\n\nexport function delayBlurEvent(cm) {\n  cm.state.delayingBlurEvent = true\n  setTimeout(() => { if (cm.state.delayingBlurEvent) {\n    cm.state.delayingBlurEvent = false\n    if (cm.state.focused) onBlur(cm)\n  } }, 100)\n}\n\nexport function onFocus(cm, e) {\n  if (cm.state.delayingBlurEvent && !cm.state.draggingText) cm.state.delayingBlurEvent = false\n\n  if (cm.options.readOnly == \"nocursor\") return\n  if (!cm.state.focused) {\n    signal(cm, \"focus\", cm, e)\n    cm.state.focused = true\n    addClass(cm.display.wrapper, \"CodeMirror-focused\")\n    // This test prevents this from firing when a context\n    // menu is closed (since the input reset would kill the\n    // select-all detection hack)\n    if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {\n      cm.display.input.reset()\n      if (webkit) setTimeout(() => cm.display.input.reset(true), 20) // Issue #1730\n    }\n    cm.display.input.receivedFocus()\n  }\n  restartBlink(cm)\n}\nexport function onBlur(cm, e) {\n  if (cm.state.delayingBlurEvent) return\n\n  if (cm.state.focused) {\n    signal(cm, \"blur\", cm, e)\n    cm.state.focused = false\n    rmClass(cm.display.wrapper, \"CodeMirror-focused\")\n  }\n  clearInterval(cm.display.blinker)\n  setTimeout(() => { if (!cm.state.focused) cm.display.shift = false }, 150)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/gutters.js",
    "content": "import { elt, removeChildren } from \"../util/dom.js\"\nimport { regChange } from \"./view_tracking.js\"\nimport { alignHorizontally } from \"./line_numbers.js\"\nimport { updateGutterSpace } from \"./update_display.js\"\n\nexport function getGutters(gutters, lineNumbers) {\n  let result = [], sawLineNumbers = false\n  for (let i = 0; i < gutters.length; i++) {\n    let name = gutters[i], style = null\n    if (typeof name != \"string\") { style = name.style; name = name.className }\n    if (name == \"CodeMirror-linenumbers\") {\n      if (!lineNumbers) continue\n      else sawLineNumbers = true\n    }\n    result.push({className: name, style})\n  }\n  if (lineNumbers && !sawLineNumbers) result.push({className: \"CodeMirror-linenumbers\", style: null})\n  return result\n}\n\n// Rebuild the gutter elements, ensure the margin to the left of the\n// code matches their width.\nexport function renderGutters(display) {\n  let gutters = display.gutters, specs = display.gutterSpecs\n  removeChildren(gutters)\n  display.lineGutter = null\n  for (let i = 0; i < specs.length; ++i) {\n    let {className, style} = specs[i]\n    let gElt = gutters.appendChild(elt(\"div\", null, \"CodeMirror-gutter \" + className))\n    if (style) gElt.style.cssText = style\n    if (className == \"CodeMirror-linenumbers\") {\n      display.lineGutter = gElt\n      gElt.style.width = (display.lineNumWidth || 1) + \"px\"\n    }\n  }\n  gutters.style.display = specs.length ? \"\" : \"none\"\n  updateGutterSpace(display)\n}\n\nexport function updateGutters(cm) {\n  renderGutters(cm.display)\n  regChange(cm)\n  alignHorizontally(cm)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/highlight_worker.js",
    "content": "import { getContextBefore, highlightLine, processLine } from \"../line/highlight.js\"\nimport { copyState } from \"../modes.js\"\nimport { bind } from \"../util/misc.js\"\n\nimport { runInOp } from \"./operations.js\"\nimport { regLineChange } from \"./view_tracking.js\"\n\n// HIGHLIGHT WORKER\n\nexport function startWorker(cm, time) {\n  if (cm.doc.highlightFrontier < cm.display.viewTo)\n    cm.state.highlight.set(time, bind(highlightWorker, cm))\n}\n\nfunction highlightWorker(cm) {\n  let doc = cm.doc\n  if (doc.highlightFrontier >= cm.display.viewTo) return\n  let end = +new Date + cm.options.workTime\n  let context = getContextBefore(cm, doc.highlightFrontier)\n  let changedLines = []\n\n  doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), line => {\n    if (context.line >= cm.display.viewFrom) { // Visible\n      let oldStyles = line.styles\n      let resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null\n      let highlighted = highlightLine(cm, line, context, true)\n      if (resetState) context.state = resetState\n      line.styles = highlighted.styles\n      let oldCls = line.styleClasses, newCls = highlighted.classes\n      if (newCls) line.styleClasses = newCls\n      else if (oldCls) line.styleClasses = null\n      let ischange = !oldStyles || oldStyles.length != line.styles.length ||\n        oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass)\n      for (let i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i]\n      if (ischange) changedLines.push(context.line)\n      line.stateAfter = context.save()\n      context.nextLine()\n    } else {\n      if (line.text.length <= cm.options.maxHighlightLength)\n        processLine(cm, line.text, context)\n      line.stateAfter = context.line % 5 == 0 ? context.save() : null\n      context.nextLine()\n    }\n    if (+new Date > end) {\n      startWorker(cm, cm.options.workDelay)\n      return true\n    }\n  })\n  doc.highlightFrontier = context.line\n  doc.modeFrontier = Math.max(doc.modeFrontier, context.line)\n  if (changedLines.length) runInOp(cm, () => {\n    for (let i = 0; i < changedLines.length; i++)\n      regLineChange(cm, changedLines[i], \"text\")\n  })\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/line_numbers.js",
    "content": "import { lineNumberFor } from \"../line/utils_line.js\"\nimport { compensateForHScroll } from \"../measurement/position_measurement.js\"\nimport { elt } from \"../util/dom.js\"\n\nimport { updateGutterSpace } from \"./update_display.js\"\n\n// Re-align line numbers and gutter marks to compensate for\n// horizontal scrolling.\nexport function alignHorizontally(cm) {\n  let display = cm.display, view = display.view\n  if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return\n  let comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft\n  let gutterW = display.gutters.offsetWidth, left = comp + \"px\"\n  for (let i = 0; i < view.length; i++) if (!view[i].hidden) {\n    if (cm.options.fixedGutter) {\n      if (view[i].gutter)\n        view[i].gutter.style.left = left\n      if (view[i].gutterBackground)\n        view[i].gutterBackground.style.left = left\n    }\n    let align = view[i].alignable\n    if (align) for (let j = 0; j < align.length; j++)\n      align[j].style.left = left\n  }\n  if (cm.options.fixedGutter)\n    display.gutters.style.left = (comp + gutterW) + \"px\"\n}\n\n// Used to ensure that the line number gutter is still the right\n// size for the current document size. Returns true when an update\n// is needed.\nexport function maybeUpdateLineNumberWidth(cm) {\n  if (!cm.options.lineNumbers) return false\n  let doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display\n  if (last.length != display.lineNumChars) {\n    let test = display.measure.appendChild(elt(\"div\", [elt(\"div\", last)],\n                                               \"CodeMirror-linenumber CodeMirror-gutter-elt\"))\n    let innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW\n    display.lineGutter.style.width = \"\"\n    display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1\n    display.lineNumWidth = display.lineNumInnerWidth + padding\n    display.lineNumChars = display.lineNumInnerWidth ? last.length : -1\n    display.lineGutter.style.width = display.lineNumWidth + \"px\"\n    updateGutterSpace(cm.display)\n    return true\n  }\n  return false\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/mode_state.js",
    "content": "import { getMode } from \"../modes.js\"\n\nimport { startWorker } from \"./highlight_worker.js\"\nimport { regChange } from \"./view_tracking.js\"\n\n// Used to get the editor into a consistent state again when options change.\n\nexport function loadMode(cm) {\n  cm.doc.mode = getMode(cm.options, cm.doc.modeOption)\n  resetModeState(cm)\n}\n\nexport function resetModeState(cm) {\n  cm.doc.iter(line => {\n    if (line.stateAfter) line.stateAfter = null\n    if (line.styles) line.styles = null\n  })\n  cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first\n  startWorker(cm, 100)\n  cm.state.modeGen++\n  if (cm.curOp) regChange(cm)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/operations.js",
    "content": "import { clipPos } from \"../line/pos.js\"\nimport { findMaxLine } from \"../line/spans.js\"\nimport { displayWidth, measureChar, scrollGap } from \"../measurement/position_measurement.js\"\nimport { signal } from \"../util/event.js\"\nimport { activeElt, root } from \"../util/dom.js\"\nimport { finishOperation, pushOperation } from \"../util/operation_group.js\"\n\nimport { ensureFocus } from \"./focus.js\"\nimport { measureForScrollbars, updateScrollbars } from \"./scrollbars.js\"\nimport { restartBlink } from \"./selection.js\"\nimport { maybeScrollWindow, scrollPosIntoView, setScrollLeft, setScrollTop } from \"./scrolling.js\"\nimport { DisplayUpdate, maybeClipScrollbars, postUpdateDisplay, setDocumentHeight, updateDisplayIfNeeded } from \"./update_display.js\"\nimport { updateHeightsInViewport } from \"./update_lines.js\"\n\n// Operations are used to wrap a series of changes to the editor\n// state in such a way that each change won't have to update the\n// cursor and display (which would be awkward, slow, and\n// error-prone). Instead, display updates are batched and then all\n// combined and executed at once.\n\nlet nextOpId = 0\n// Start a new operation.\nexport function startOperation(cm) {\n  cm.curOp = {\n    cm: cm,\n    viewChanged: false,      // Flag that indicates that lines might need to be redrawn\n    startHeight: cm.doc.height, // Used to detect need to update scrollbar\n    forceUpdate: false,      // Used to force a redraw\n    updateInput: 0,       // Whether to reset the input textarea\n    typing: false,           // Whether this reset should be careful to leave existing text (for compositing)\n    changeObjs: null,        // Accumulated changes, for firing change events\n    cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on\n    cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already\n    selectionChanged: false, // Whether the selection needs to be redrawn\n    updateMaxLine: false,    // Set when the widest line needs to be determined anew\n    scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet\n    scrollToPos: null,       // Used to scroll to a specific position\n    focus: false,\n    id: ++nextOpId,          // Unique ID\n    markArrays: null         // Used by addMarkedSpan\n  }\n  pushOperation(cm.curOp)\n}\n\n// Finish an operation, updating the display and signalling delayed events\nexport function endOperation(cm) {\n  let op = cm.curOp\n  if (op) finishOperation(op, group => {\n    for (let i = 0; i < group.ops.length; i++)\n      group.ops[i].cm.curOp = null\n    endOperations(group)\n  })\n}\n\n// The DOM updates done when an operation finishes are batched so\n// that the minimum number of relayouts are required.\nfunction endOperations(group) {\n  let ops = group.ops\n  for (let i = 0; i < ops.length; i++) // Read DOM\n    endOperation_R1(ops[i])\n  for (let i = 0; i < ops.length; i++) // Write DOM (maybe)\n    endOperation_W1(ops[i])\n  for (let i = 0; i < ops.length; i++) // Read DOM\n    endOperation_R2(ops[i])\n  for (let i = 0; i < ops.length; i++) // Write DOM (maybe)\n    endOperation_W2(ops[i])\n  for (let i = 0; i < ops.length; i++) // Read DOM\n    endOperation_finish(ops[i])\n}\n\nfunction endOperation_R1(op) {\n  let cm = op.cm, display = cm.display\n  maybeClipScrollbars(cm)\n  if (op.updateMaxLine) findMaxLine(cm)\n\n  op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||\n    op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||\n                       op.scrollToPos.to.line >= display.viewTo) ||\n    display.maxLineChanged && cm.options.lineWrapping\n  op.update = op.mustUpdate &&\n    new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate)\n}\n\nfunction endOperation_W1(op) {\n  op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)\n}\n\nfunction endOperation_R2(op) {\n  let cm = op.cm, display = cm.display\n  if (op.updatedDisplay) updateHeightsInViewport(cm)\n\n  op.barMeasure = measureForScrollbars(cm)\n\n  // If the max line changed since it was last measured, measure it,\n  // and ensure the document's width matches it.\n  // updateDisplay_W2 will use these properties to do the actual resizing\n  if (display.maxLineChanged && !cm.options.lineWrapping) {\n    op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3\n    cm.display.sizerWidth = op.adjustWidthTo\n    op.barMeasure.scrollWidth =\n      Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth)\n    op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))\n  }\n\n  if (op.updatedDisplay || op.selectionChanged)\n    op.preparedSelection = display.input.prepareSelection()\n}\n\nfunction endOperation_W2(op) {\n  let cm = op.cm\n\n  if (op.adjustWidthTo != null) {\n    cm.display.sizer.style.minWidth = op.adjustWidthTo + \"px\"\n    if (op.maxScrollLeft < cm.doc.scrollLeft)\n      setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true)\n    cm.display.maxLineChanged = false\n  }\n\n  let takeFocus = op.focus && op.focus == activeElt(root(cm))\n  if (op.preparedSelection)\n    cm.display.input.showSelection(op.preparedSelection, takeFocus)\n  if (op.updatedDisplay || op.startHeight != cm.doc.height)\n    updateScrollbars(cm, op.barMeasure)\n  if (op.updatedDisplay)\n    setDocumentHeight(cm, op.barMeasure)\n\n  if (op.selectionChanged) restartBlink(cm)\n\n  if (cm.state.focused && op.updateInput)\n    cm.display.input.reset(op.typing)\n  if (takeFocus) ensureFocus(op.cm)\n}\n\nfunction endOperation_finish(op) {\n  let cm = op.cm, display = cm.display, doc = cm.doc\n\n  if (op.updatedDisplay) postUpdateDisplay(cm, op.update)\n\n  // Abort mouse wheel delta measurement, when scrolling explicitly\n  if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))\n    display.wheelStartX = display.wheelStartY = null\n\n  // Propagate the scroll position to the actual DOM scroller\n  if (op.scrollTop != null) setScrollTop(cm, op.scrollTop, op.forceScroll)\n\n  if (op.scrollLeft != null) setScrollLeft(cm, op.scrollLeft, true, true)\n  // If we need to scroll a specific position into view, do so.\n  if (op.scrollToPos) {\n    let rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),\n                                 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)\n    maybeScrollWindow(cm, rect)\n  }\n\n  // Fire events for markers that are hidden/unidden by editing or\n  // undoing\n  let hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers\n  if (hidden) for (let i = 0; i < hidden.length; ++i)\n    if (!hidden[i].lines.length) signal(hidden[i], \"hide\")\n  if (unhidden) for (let i = 0; i < unhidden.length; ++i)\n    if (unhidden[i].lines.length) signal(unhidden[i], \"unhide\")\n\n  if (display.wrapper.offsetHeight)\n    doc.scrollTop = cm.display.scroller.scrollTop\n\n  // Fire change events, and delayed event handlers\n  if (op.changeObjs)\n    signal(cm, \"changes\", cm, op.changeObjs)\n  if (op.update)\n    op.update.finish()\n}\n\n// Run the given function in an operation\nexport function runInOp(cm, f) {\n  if (cm.curOp) return f()\n  startOperation(cm)\n  try { return f() }\n  finally { endOperation(cm) }\n}\n// Wraps a function in an operation. Returns the wrapped function.\nexport function operation(cm, f) {\n  return function() {\n    if (cm.curOp) return f.apply(cm, arguments)\n    startOperation(cm)\n    try { return f.apply(cm, arguments) }\n    finally { endOperation(cm) }\n  }\n}\n// Used to add methods to editor and doc instances, wrapping them in\n// operations.\nexport function methodOp(f) {\n  return function() {\n    if (this.curOp) return f.apply(this, arguments)\n    startOperation(this)\n    try { return f.apply(this, arguments) }\n    finally { endOperation(this) }\n  }\n}\nexport function docMethodOp(f) {\n  return function() {\n    let cm = this.cm\n    if (!cm || cm.curOp) return f.apply(this, arguments)\n    startOperation(cm)\n    try { return f.apply(this, arguments) }\n    finally { endOperation(cm) }\n  }\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/scroll_events.js",
    "content": "import { chrome, chrome_version, gecko, ie, mac, presto, safari, webkit } from \"../util/browser.js\"\nimport { e_preventDefault } from \"../util/event.js\"\n\nimport { updateDisplaySimple } from \"./update_display.js\"\nimport { setScrollLeft, updateScrollTop } from \"./scrolling.js\"\n\n// Since the delta values reported on mouse wheel events are\n// unstandardized between browsers and even browser versions, and\n// generally horribly unpredictable, this code starts by measuring\n// the scroll effect that the first few mouse wheel events have,\n// and, from that, detects the way it can convert deltas to pixel\n// offsets afterwards.\n//\n// The reason we want to know the amount a wheel event will scroll\n// is that it gives us a chance to update the display before the\n// actual scrolling happens, reducing flickering.\n\nlet wheelSamples = 0, wheelPixelsPerUnit = null\n// Fill in a browser-detected starting value on browsers where we\n// know one. These don't have to be accurate -- the result of them\n// being wrong would just be a slight flicker on the first wheel\n// scroll (if it is large enough).\nif (ie) wheelPixelsPerUnit = -.53\nelse if (gecko) wheelPixelsPerUnit = 15\nelse if (chrome) wheelPixelsPerUnit = -.7\nelse if (safari) wheelPixelsPerUnit = -1/3\n\nfunction wheelEventDelta(e) {\n  let dx = e.wheelDeltaX, dy = e.wheelDeltaY\n  if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail\n  if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail\n  else if (dy == null) dy = e.wheelDelta\n  return {x: dx, y: dy}\n}\nexport function wheelEventPixels(e) {\n  let delta = wheelEventDelta(e)\n  delta.x *= wheelPixelsPerUnit\n  delta.y *= wheelPixelsPerUnit\n  return delta\n}\n\nexport function onScrollWheel(cm, e) {\n  // On Chrome 102, viewport updates somehow stop wheel-based\n  // scrolling. Turning off pointer events during the scroll seems\n  // to avoid the issue.\n  if (chrome && chrome_version == 102) {\n    if (cm.display.chromeScrollHack == null) cm.display.sizer.style.pointerEvents = \"none\"\n    else clearTimeout(cm.display.chromeScrollHack)\n    cm.display.chromeScrollHack = setTimeout(() => {\n      cm.display.chromeScrollHack = null\n      cm.display.sizer.style.pointerEvents = \"\"\n    }, 100)\n  }\n  let delta = wheelEventDelta(e), dx = delta.x, dy = delta.y\n  let pixelsPerUnit = wheelPixelsPerUnit\n  if (e.deltaMode === 0) {\n    dx = e.deltaX\n    dy = e.deltaY\n    pixelsPerUnit = 1\n  }\n\n  let display = cm.display, scroll = display.scroller\n  // Quit if there's nothing to scroll here\n  let canScrollX = scroll.scrollWidth > scroll.clientWidth\n  let canScrollY = scroll.scrollHeight > scroll.clientHeight\n  if (!(dx && canScrollX || dy && canScrollY)) return\n\n  // Webkit browsers on OS X abort momentum scrolls when the target\n  // of the scroll event is removed from the scrollable element.\n  // This hack (see related code in patchDisplay) makes sure the\n  // element is kept around.\n  if (dy && mac && webkit) {\n    outer: for (let cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {\n      for (let i = 0; i < view.length; i++) {\n        if (view[i].node == cur) {\n          cm.display.currentWheelTarget = cur\n          break outer\n        }\n      }\n    }\n  }\n\n  // On some browsers, horizontal scrolling will cause redraws to\n  // happen before the gutter has been realigned, causing it to\n  // wriggle around in a most unseemly way. When we have an\n  // estimated pixels/delta value, we just handle horizontal\n  // scrolling entirely here. It'll be slightly off from native, but\n  // better than glitching out.\n  if (dx && !gecko && !presto && pixelsPerUnit != null) {\n    if (dy && canScrollY)\n      updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * pixelsPerUnit))\n    setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * pixelsPerUnit))\n    // Only prevent default scrolling if vertical scrolling is\n    // actually possible. Otherwise, it causes vertical scroll\n    // jitter on OSX trackpads when deltaX is small and deltaY\n    // is large (issue #3579)\n    if (!dy || (dy && canScrollY))\n      e_preventDefault(e)\n    display.wheelStartX = null // Abort measurement, if in progress\n    return\n  }\n\n  // 'Project' the visible viewport to cover the area that is being\n  // scrolled into view (if we know enough to estimate it).\n  if (dy && pixelsPerUnit != null) {\n    let pixels = dy * pixelsPerUnit\n    let top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight\n    if (pixels < 0) top = Math.max(0, top + pixels - 50)\n    else bot = Math.min(cm.doc.height, bot + pixels + 50)\n    updateDisplaySimple(cm, {top: top, bottom: bot})\n  }\n\n  if (wheelSamples < 20 && e.deltaMode !== 0) {\n    if (display.wheelStartX == null) {\n      display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop\n      display.wheelDX = dx; display.wheelDY = dy\n      setTimeout(() => {\n        if (display.wheelStartX == null) return\n        let movedX = scroll.scrollLeft - display.wheelStartX\n        let movedY = scroll.scrollTop - display.wheelStartY\n        let sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||\n          (movedX && display.wheelDX && movedX / display.wheelDX)\n        display.wheelStartX = display.wheelStartY = null\n        if (!sample) return\n        wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)\n        ++wheelSamples\n      }, 200)\n    } else {\n      display.wheelDX += dx; display.wheelDY += dy\n    }\n  }\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/scrollbars.js",
    "content": "import { addClass, elt, rmClass } from \"../util/dom.js\"\nimport { on } from \"../util/event.js\"\nimport { scrollGap, paddingVert } from \"../measurement/position_measurement.js\"\nimport { ie, ie_version, mac, mac_geMountainLion } from \"../util/browser.js\"\nimport { updateHeightsInViewport } from \"./update_lines.js\"\nimport { Delayed } from \"../util/misc.js\"\n\nimport { setScrollLeft, updateScrollTop } from \"./scrolling.js\"\n\n// SCROLLBARS\n\n// Prepare DOM reads needed to update the scrollbars. Done in one\n// shot to minimize update/measure roundtrips.\nexport function measureForScrollbars(cm) {\n  let d = cm.display, gutterW = d.gutters.offsetWidth\n  let docH = Math.round(cm.doc.height + paddingVert(cm.display))\n  return {\n    clientHeight: d.scroller.clientHeight,\n    viewHeight: d.wrapper.clientHeight,\n    scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,\n    viewWidth: d.wrapper.clientWidth,\n    barLeft: cm.options.fixedGutter ? gutterW : 0,\n    docHeight: docH,\n    scrollHeight: docH + scrollGap(cm) + d.barHeight,\n    nativeBarWidth: d.nativeBarWidth,\n    gutterWidth: gutterW\n  }\n}\n\nclass NativeScrollbars {\n  constructor(place, scroll, cm) {\n    this.cm = cm\n    let vert = this.vert = elt(\"div\", [elt(\"div\", null, null, \"min-width: 1px\")], \"CodeMirror-vscrollbar\")\n    let horiz = this.horiz = elt(\"div\", [elt(\"div\", null, null, \"height: 100%; min-height: 1px\")], \"CodeMirror-hscrollbar\")\n    vert.tabIndex = horiz.tabIndex = -1\n    place(vert); place(horiz)\n\n    on(vert, \"scroll\", () => {\n      if (vert.clientHeight) scroll(vert.scrollTop, \"vertical\")\n    })\n    on(horiz, \"scroll\", () => {\n      if (horiz.clientWidth) scroll(horiz.scrollLeft, \"horizontal\")\n    })\n\n    this.checkedZeroWidth = false\n    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).\n    if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = \"18px\"\n  }\n\n  update(measure) {\n    let needsH = measure.scrollWidth > measure.clientWidth + 1\n    let needsV = measure.scrollHeight > measure.clientHeight + 1\n    let sWidth = measure.nativeBarWidth\n\n    if (needsV) {\n      this.vert.style.display = \"block\"\n      this.vert.style.bottom = needsH ? sWidth + \"px\" : \"0\"\n      let totalHeight = measure.viewHeight - (needsH ? sWidth : 0)\n      // A bug in IE8 can cause this value to be negative, so guard it.\n      this.vert.firstChild.style.height =\n        Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + \"px\"\n    } else {\n      this.vert.scrollTop = 0\n      this.vert.style.display = \"\"\n      this.vert.firstChild.style.height = \"0\"\n    }\n\n    if (needsH) {\n      this.horiz.style.display = \"block\"\n      this.horiz.style.right = needsV ? sWidth + \"px\" : \"0\"\n      this.horiz.style.left = measure.barLeft + \"px\"\n      let totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)\n      this.horiz.firstChild.style.width =\n        Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + \"px\"\n    } else {\n      this.horiz.style.display = \"\"\n      this.horiz.firstChild.style.width = \"0\"\n    }\n\n    if (!this.checkedZeroWidth && measure.clientHeight > 0) {\n      if (sWidth == 0) this.zeroWidthHack()\n      this.checkedZeroWidth = true\n    }\n\n    return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}\n  }\n\n  setScrollLeft(pos) {\n    if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos\n    if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz, \"horiz\")\n  }\n\n  setScrollTop(pos) {\n    if (this.vert.scrollTop != pos) this.vert.scrollTop = pos\n    if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert, \"vert\")\n  }\n\n  zeroWidthHack() {\n    let w = mac && !mac_geMountainLion ? \"12px\" : \"18px\"\n    this.horiz.style.height = this.vert.style.width = w\n    this.horiz.style.visibility = this.vert.style.visibility = \"hidden\"\n    this.disableHoriz = new Delayed\n    this.disableVert = new Delayed\n  }\n\n  enableZeroWidthBar(bar, delay, type) {\n    bar.style.visibility = \"\"\n    function maybeDisable() {\n      // To find out whether the scrollbar is still visible, we\n      // check whether the element under the pixel in the bottom\n      // right corner of the scrollbar box is the scrollbar box\n      // itself (when the bar is still visible) or its filler child\n      // (when the bar is hidden). If it is still visible, we keep\n      // it enabled, if it's hidden, we disable pointer events.\n      let box = bar.getBoundingClientRect()\n      let elt = type == \"vert\" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)\n          : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1)\n      if (elt != bar) bar.style.visibility = \"hidden\"\n      else delay.set(1000, maybeDisable)\n    }\n    delay.set(1000, maybeDisable)\n  }\n\n  clear() {\n    let parent = this.horiz.parentNode\n    parent.removeChild(this.horiz)\n    parent.removeChild(this.vert)\n  }\n}\n\nclass NullScrollbars {\n  update() { return {bottom: 0, right: 0} }\n  setScrollLeft() {}\n  setScrollTop() {}\n  clear() {}\n}\n\nexport function updateScrollbars(cm, measure) {\n  if (!measure) measure = measureForScrollbars(cm)\n  let startWidth = cm.display.barWidth, startHeight = cm.display.barHeight\n  updateScrollbarsInner(cm, measure)\n  for (let i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {\n    if (startWidth != cm.display.barWidth && cm.options.lineWrapping)\n      updateHeightsInViewport(cm)\n    updateScrollbarsInner(cm, measureForScrollbars(cm))\n    startWidth = cm.display.barWidth; startHeight = cm.display.barHeight\n  }\n}\n\n// Re-synchronize the fake scrollbars with the actual size of the\n// content.\nfunction updateScrollbarsInner(cm, measure) {\n  let d = cm.display\n  let sizes = d.scrollbars.update(measure)\n\n  d.sizer.style.paddingRight = (d.barWidth = sizes.right) + \"px\"\n  d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + \"px\"\n  d.heightForcer.style.borderBottom = sizes.bottom + \"px solid transparent\"\n\n  if (sizes.right && sizes.bottom) {\n    d.scrollbarFiller.style.display = \"block\"\n    d.scrollbarFiller.style.height = sizes.bottom + \"px\"\n    d.scrollbarFiller.style.width = sizes.right + \"px\"\n  } else d.scrollbarFiller.style.display = \"\"\n  if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {\n    d.gutterFiller.style.display = \"block\"\n    d.gutterFiller.style.height = sizes.bottom + \"px\"\n    d.gutterFiller.style.width = measure.gutterWidth + \"px\"\n  } else d.gutterFiller.style.display = \"\"\n}\n\nexport let scrollbarModel = {\"native\": NativeScrollbars, \"null\": NullScrollbars}\n\nexport function initScrollbars(cm) {\n  if (cm.display.scrollbars) {\n    cm.display.scrollbars.clear()\n    if (cm.display.scrollbars.addClass)\n      rmClass(cm.display.wrapper, cm.display.scrollbars.addClass)\n  }\n\n  cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](node => {\n    cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)\n    // Prevent clicks in the scrollbars from killing focus\n    on(node, \"mousedown\", () => {\n      if (cm.state.focused) setTimeout(() => cm.display.input.focus(), 0)\n    })\n    node.setAttribute(\"cm-not-content\", \"true\")\n  }, (pos, axis) => {\n    if (axis == \"horizontal\") setScrollLeft(cm, pos)\n    else updateScrollTop(cm, pos)\n  }, cm)\n  if (cm.display.scrollbars.addClass)\n    addClass(cm.display.wrapper, cm.display.scrollbars.addClass)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/scrolling.js",
    "content": "import { Pos } from \"../line/pos.js\"\nimport { cursorCoords, displayHeight, displayWidth, estimateCoords, paddingTop, paddingVert, scrollGap, textHeight } from \"../measurement/position_measurement.js\"\nimport { gecko, phantom } from \"../util/browser.js\"\nimport { elt } from \"../util/dom.js\"\nimport { signalDOMEvent } from \"../util/event.js\"\n\nimport { startWorker } from \"./highlight_worker.js\"\nimport { alignHorizontally } from \"./line_numbers.js\"\nimport { updateDisplaySimple } from \"./update_display.js\"\n\n// SCROLLING THINGS INTO VIEW\n\n// If an editor sits on the top or bottom of the window, partially\n// scrolled out of view, this ensures that the cursor is visible.\nexport function maybeScrollWindow(cm, rect) {\n  if (signalDOMEvent(cm, \"scrollCursorIntoView\")) return\n\n  let display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null\n  let doc = display.wrapper.ownerDocument\n  if (rect.top + box.top < 0) doScroll = true\n  else if (rect.bottom + box.top > (doc.defaultView.innerHeight || doc.documentElement.clientHeight)) doScroll = false\n  if (doScroll != null && !phantom) {\n    let scrollNode = elt(\"div\", \"\\u200b\", null, `position: absolute;\n                         top: ${rect.top - display.viewOffset - paddingTop(cm.display)}px;\n                         height: ${rect.bottom - rect.top + scrollGap(cm) + display.barHeight}px;\n                         left: ${rect.left}px; width: ${Math.max(2, rect.right - rect.left)}px;`)\n    cm.display.lineSpace.appendChild(scrollNode)\n    scrollNode.scrollIntoView(doScroll)\n    cm.display.lineSpace.removeChild(scrollNode)\n  }\n}\n\n// Scroll a given position into view (immediately), verifying that\n// it actually became visible (as line heights are accurately\n// measured, the position of something may 'drift' during drawing).\nexport function scrollPosIntoView(cm, pos, end, margin) {\n  if (margin == null) margin = 0\n  let rect\n  if (!cm.options.lineWrapping && pos == end) {\n    // Set pos and end to the cursor positions around the character pos sticks to\n    // If pos.sticky == \"before\", that is around pos.ch - 1, otherwise around pos.ch\n    // If pos == Pos(_, 0, \"before\"), pos and end are unchanged\n    end = pos.sticky == \"before\" ? Pos(pos.line, pos.ch + 1, \"before\") : pos\n    pos = pos.ch ? Pos(pos.line, pos.sticky == \"before\" ? pos.ch - 1 : pos.ch, \"after\") : pos\n  }\n  for (let limit = 0; limit < 5; limit++) {\n    let changed = false\n    let coords = cursorCoords(cm, pos)\n    let endCoords = !end || end == pos ? coords : cursorCoords(cm, end)\n    rect = {left: Math.min(coords.left, endCoords.left),\n            top: Math.min(coords.top, endCoords.top) - margin,\n            right: Math.max(coords.left, endCoords.left),\n            bottom: Math.max(coords.bottom, endCoords.bottom) + margin}\n    let scrollPos = calculateScrollPos(cm, rect)\n    let startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft\n    if (scrollPos.scrollTop != null) {\n      updateScrollTop(cm, scrollPos.scrollTop)\n      if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true\n    }\n    if (scrollPos.scrollLeft != null) {\n      setScrollLeft(cm, scrollPos.scrollLeft)\n      if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true\n    }\n    if (!changed) break\n  }\n  return rect\n}\n\n// Scroll a given set of coordinates into view (immediately).\nexport function scrollIntoView(cm, rect) {\n  let scrollPos = calculateScrollPos(cm, rect)\n  if (scrollPos.scrollTop != null) updateScrollTop(cm, scrollPos.scrollTop)\n  if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft)\n}\n\n// Calculate a new scroll position needed to scroll the given\n// rectangle into view. Returns an object with scrollTop and\n// scrollLeft properties. When these are undefined, the\n// vertical/horizontal position does not need to be adjusted.\nfunction calculateScrollPos(cm, rect) {\n  let display = cm.display, snapMargin = textHeight(cm.display)\n  if (rect.top < 0) rect.top = 0\n  let screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop\n  let screen = displayHeight(cm), result = {}\n  if (rect.bottom - rect.top > screen) rect.bottom = rect.top + screen\n  let docBottom = cm.doc.height + paddingVert(display)\n  let atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin\n  if (rect.top < screentop) {\n    result.scrollTop = atTop ? 0 : rect.top\n  } else if (rect.bottom > screentop + screen) {\n    let newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen)\n    if (newTop != screentop) result.scrollTop = newTop\n  }\n\n  let gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth\n  let screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace\n  let screenw = displayWidth(cm) - display.gutters.offsetWidth\n  let tooWide = rect.right - rect.left > screenw\n  if (tooWide) rect.right = rect.left + screenw\n  if (rect.left < 10)\n    result.scrollLeft = 0\n  else if (rect.left < screenleft)\n    result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10))\n  else if (rect.right > screenw + screenleft - 3)\n    result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw\n  return result\n}\n\n// Store a relative adjustment to the scroll position in the current\n// operation (to be applied when the operation finishes).\nexport function addToScrollTop(cm, top) {\n  if (top == null) return\n  resolveScrollToPos(cm)\n  cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top\n}\n\n// Make sure that at the end of the operation the current cursor is\n// shown.\nexport function ensureCursorVisible(cm) {\n  resolveScrollToPos(cm)\n  let cur = cm.getCursor()\n  cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}\n}\n\nexport function scrollToCoords(cm, x, y) {\n  if (x != null || y != null) resolveScrollToPos(cm)\n  if (x != null) cm.curOp.scrollLeft = x\n  if (y != null) cm.curOp.scrollTop = y\n}\n\nexport function scrollToRange(cm, range) {\n  resolveScrollToPos(cm)\n  cm.curOp.scrollToPos = range\n}\n\n// When an operation has its scrollToPos property set, and another\n// scroll action is applied before the end of the operation, this\n// 'simulates' scrolling that position into view in a cheap way, so\n// that the effect of intermediate scroll commands is not ignored.\nfunction resolveScrollToPos(cm) {\n  let range = cm.curOp.scrollToPos\n  if (range) {\n    cm.curOp.scrollToPos = null\n    let from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to)\n    scrollToCoordsRange(cm, from, to, range.margin)\n  }\n}\n\nexport function scrollToCoordsRange(cm, from, to, margin) {\n  let sPos = calculateScrollPos(cm, {\n    left: Math.min(from.left, to.left),\n    top: Math.min(from.top, to.top) - margin,\n    right: Math.max(from.right, to.right),\n    bottom: Math.max(from.bottom, to.bottom) + margin\n  })\n  scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop)\n}\n\n// Sync the scrollable area and scrollbars, ensure the viewport\n// covers the visible area.\nexport function updateScrollTop(cm, val) {\n  if (Math.abs(cm.doc.scrollTop - val) < 2) return\n  if (!gecko) updateDisplaySimple(cm, {top: val})\n  setScrollTop(cm, val, true)\n  if (gecko) updateDisplaySimple(cm)\n  startWorker(cm, 100)\n}\n\nexport function setScrollTop(cm, val, forceScroll) {\n  val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val))\n  if (cm.display.scroller.scrollTop == val && !forceScroll) return\n  cm.doc.scrollTop = val\n  cm.display.scrollbars.setScrollTop(val)\n  if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val\n}\n\n// Sync scroller and scrollbar, ensure the gutter elements are\n// aligned.\nexport function setScrollLeft(cm, val, isScroller, forceScroll) {\n  val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth))\n  if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) return\n  cm.doc.scrollLeft = val\n  alignHorizontally(cm)\n  if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val\n  cm.display.scrollbars.setScrollLeft(val)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/selection.js",
    "content": "import { Pos } from \"../line/pos.js\"\nimport { visualLine } from \"../line/spans.js\"\nimport { getLine } from \"../line/utils_line.js\"\nimport { charCoords, cursorCoords, displayWidth, paddingH, wrappedLineExtentChar } from \"../measurement/position_measurement.js\"\nimport { getOrder, iterateBidiSections } from \"../util/bidi.js\"\nimport { elt } from \"../util/dom.js\"\nimport { onBlur } from \"./focus.js\"\n\nexport function updateSelection(cm) {\n  cm.display.input.showSelection(cm.display.input.prepareSelection())\n}\n\nexport function prepareSelection(cm, primary = true) {\n  let doc = cm.doc, result = {}\n  let curFragment = result.cursors = document.createDocumentFragment()\n  let selFragment = result.selection = document.createDocumentFragment()\n\n  let customCursor = cm.options.$customCursor\n  if (customCursor) primary = true\n  for (let i = 0; i < doc.sel.ranges.length; i++) {\n    if (!primary && i == doc.sel.primIndex) continue\n    let range = doc.sel.ranges[i]\n    if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) continue\n    let collapsed = range.empty()\n    if (customCursor) {\n      let head = customCursor(cm, range)\n      if (head) drawSelectionCursor(cm, head, curFragment)\n    } else if (collapsed || cm.options.showCursorWhenSelecting) {\n      drawSelectionCursor(cm, range.head, curFragment)\n    }\n    if (!collapsed)\n      drawSelectionRange(cm, range, selFragment)\n  }\n  return result\n}\n\n// Draws a cursor for the given range\nexport function drawSelectionCursor(cm, head, output) {\n  let pos = cursorCoords(cm, head, \"div\", null, null, !cm.options.singleCursorHeightPerLine)\n\n  let cursor = output.appendChild(elt(\"div\", \"\\u00a0\", \"CodeMirror-cursor\"))\n  cursor.style.left = pos.left + \"px\"\n  cursor.style.top = pos.top + \"px\"\n  cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + \"px\"\n\n  if (/\\bcm-fat-cursor\\b/.test(cm.getWrapperElement().className)) {\n    let charPos = charCoords(cm, head, \"div\", null, null)\n    let width = charPos.right - charPos.left\n    cursor.style.width = (width > 0 ? width : cm.defaultCharWidth()) + \"px\"\n  }\n\n  if (pos.other) {\n    // Secondary cursor, shown when on a 'jump' in bi-directional text\n    let otherCursor = output.appendChild(elt(\"div\", \"\\u00a0\", \"CodeMirror-cursor CodeMirror-secondarycursor\"))\n    otherCursor.style.display = \"\"\n    otherCursor.style.left = pos.other.left + \"px\"\n    otherCursor.style.top = pos.other.top + \"px\"\n    otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + \"px\"\n  }\n}\n\nfunction cmpCoords(a, b) { return a.top - b.top || a.left - b.left }\n\n// Draws the given range as a highlighted selection\nfunction drawSelectionRange(cm, range, output) {\n  let display = cm.display, doc = cm.doc\n  let fragment = document.createDocumentFragment()\n  let padding = paddingH(cm.display), leftSide = padding.left\n  let rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right\n  let docLTR = doc.direction == \"ltr\"\n\n  function add(left, top, width, bottom) {\n    if (top < 0) top = 0\n    top = Math.round(top)\n    bottom = Math.round(bottom)\n    fragment.appendChild(elt(\"div\", null, \"CodeMirror-selected\", `position: absolute; left: ${left}px;\n                             top: ${top}px; width: ${width == null ? rightSide - left : width}px;\n                             height: ${bottom - top}px`))\n  }\n\n  function drawForLine(line, fromArg, toArg) {\n    let lineObj = getLine(doc, line)\n    let lineLen = lineObj.text.length\n    let start, end\n    function coords(ch, bias) {\n      return charCoords(cm, Pos(line, ch), \"div\", lineObj, bias)\n    }\n\n    function wrapX(pos, dir, side) {\n      let extent = wrappedLineExtentChar(cm, lineObj, null, pos)\n      let prop = (dir == \"ltr\") == (side == \"after\") ? \"left\" : \"right\"\n      let ch = side == \"after\" ? extent.begin : extent.end - (/\\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1)\n      return coords(ch, prop)[prop]\n    }\n\n    let order = getOrder(lineObj, doc.direction)\n    iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, (from, to, dir, i) => {\n      let ltr = dir == \"ltr\"\n      let fromPos = coords(from, ltr ? \"left\" : \"right\")\n      let toPos = coords(to - 1, ltr ? \"right\" : \"left\")\n\n      let openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen\n      let first = i == 0, last = !order || i == order.length - 1\n      if (toPos.top - fromPos.top <= 3) { // Single line\n        let openLeft = (docLTR ? openStart : openEnd) && first\n        let openRight = (docLTR ? openEnd : openStart) && last\n        let left = openLeft ? leftSide : (ltr ? fromPos : toPos).left\n        let right = openRight ? rightSide : (ltr ? toPos : fromPos).right\n        add(left, fromPos.top, right - left, fromPos.bottom)\n      } else { // Multiple lines\n        let topLeft, topRight, botLeft, botRight\n        if (ltr) {\n          topLeft = docLTR && openStart && first ? leftSide : fromPos.left\n          topRight = docLTR ? rightSide : wrapX(from, dir, \"before\")\n          botLeft = docLTR ? leftSide : wrapX(to, dir, \"after\")\n          botRight = docLTR && openEnd && last ? rightSide : toPos.right\n        } else {\n          topLeft = !docLTR ? leftSide : wrapX(from, dir, \"before\")\n          topRight = !docLTR && openStart && first ? rightSide : fromPos.right\n          botLeft = !docLTR && openEnd && last ? leftSide : toPos.left\n          botRight = !docLTR ? rightSide : wrapX(to, dir, \"after\")\n        }\n        add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom)\n        if (fromPos.bottom < toPos.top) add(leftSide, fromPos.bottom, null, toPos.top)\n        add(botLeft, toPos.top, botRight - botLeft, toPos.bottom)\n      }\n\n      if (!start || cmpCoords(fromPos, start) < 0) start = fromPos\n      if (cmpCoords(toPos, start) < 0) start = toPos\n      if (!end || cmpCoords(fromPos, end) < 0) end = fromPos\n      if (cmpCoords(toPos, end) < 0) end = toPos\n    })\n    return {start: start, end: end}\n  }\n\n  let sFrom = range.from(), sTo = range.to()\n  if (sFrom.line == sTo.line) {\n    drawForLine(sFrom.line, sFrom.ch, sTo.ch)\n  } else {\n    let fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)\n    let singleVLine = visualLine(fromLine) == visualLine(toLine)\n    let leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end\n    let rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start\n    if (singleVLine) {\n      if (leftEnd.top < rightStart.top - 2) {\n        add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)\n        add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)\n      } else {\n        add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)\n      }\n    }\n    if (leftEnd.bottom < rightStart.top)\n      add(leftSide, leftEnd.bottom, null, rightStart.top)\n  }\n\n  output.appendChild(fragment)\n}\n\n// Cursor-blinking\nexport function restartBlink(cm) {\n  if (!cm.state.focused) return\n  let display = cm.display\n  clearInterval(display.blinker)\n  let on = true\n  display.cursorDiv.style.visibility = \"\"\n  if (cm.options.cursorBlinkRate > 0)\n    display.blinker = setInterval(() => {\n      if (!cm.hasFocus()) onBlur(cm)\n      display.cursorDiv.style.visibility = (on = !on) ? \"\" : \"hidden\"\n    }, cm.options.cursorBlinkRate)\n  else if (cm.options.cursorBlinkRate < 0)\n    display.cursorDiv.style.visibility = \"hidden\"\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/update_display.js",
    "content": "import { sawCollapsedSpans } from \"../line/saw_special_spans.js\"\nimport { heightAtLine, visualLineEndNo, visualLineNo } from \"../line/spans.js\"\nimport { getLine, lineNumberFor } from \"../line/utils_line.js\"\nimport { displayHeight, displayWidth, getDimensions, paddingVert, scrollGap } from \"../measurement/position_measurement.js\"\nimport { mac, webkit } from \"../util/browser.js\"\nimport { activeElt, removeChildren, contains, win, root, rootNode } from \"../util/dom.js\"\nimport { hasHandler, signal } from \"../util/event.js\"\nimport { signalLater } from \"../util/operation_group.js\"\nimport { indexOf } from \"../util/misc.js\"\n\nimport { buildLineElement, updateLineForChanges } from \"./update_line.js\"\nimport { startWorker } from \"./highlight_worker.js\"\nimport { maybeUpdateLineNumberWidth } from \"./line_numbers.js\"\nimport { measureForScrollbars, updateScrollbars } from \"./scrollbars.js\"\nimport { updateSelection } from \"./selection.js\"\nimport { updateHeightsInViewport, visibleLines } from \"./update_lines.js\"\nimport { adjustView, countDirtyView, resetView } from \"./view_tracking.js\"\n\n// DISPLAY DRAWING\n\nexport class DisplayUpdate {\n  constructor(cm, viewport, force) {\n    let display = cm.display\n\n    this.viewport = viewport\n    // Store some values that we'll need later (but don't want to force a relayout for)\n    this.visible = visibleLines(display, cm.doc, viewport)\n    this.editorIsHidden = !display.wrapper.offsetWidth\n    this.wrapperHeight = display.wrapper.clientHeight\n    this.wrapperWidth = display.wrapper.clientWidth\n    this.oldDisplayWidth = displayWidth(cm)\n    this.force = force\n    this.dims = getDimensions(cm)\n    this.events = []\n  }\n\n  signal(emitter, type) {\n    if (hasHandler(emitter, type))\n      this.events.push(arguments)\n  }\n  finish() {\n    for (let i = 0; i < this.events.length; i++)\n      signal.apply(null, this.events[i])\n  }\n}\n\nexport function maybeClipScrollbars(cm) {\n  let display = cm.display\n  if (!display.scrollbarsClipped && display.scroller.offsetWidth) {\n    display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth\n    display.heightForcer.style.height = scrollGap(cm) + \"px\"\n    display.sizer.style.marginBottom = -display.nativeBarWidth + \"px\"\n    display.sizer.style.borderRightWidth = scrollGap(cm) + \"px\"\n    display.scrollbarsClipped = true\n  }\n}\n\nfunction selectionSnapshot(cm) {\n  if (cm.hasFocus()) return null\n  let active = activeElt(root(cm))\n  if (!active || !contains(cm.display.lineDiv, active)) return null\n  let result = {activeElt: active}\n  if (window.getSelection) {\n    let sel = win(cm).getSelection()\n    if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {\n      result.anchorNode = sel.anchorNode\n      result.anchorOffset = sel.anchorOffset\n      result.focusNode = sel.focusNode\n      result.focusOffset = sel.focusOffset\n    }\n  }\n  return result\n}\n\nfunction restoreSelection(snapshot) {\n  if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt(rootNode(snapshot.activeElt))) return\n  snapshot.activeElt.focus()\n  if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) &&\n      snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {\n    let doc = snapshot.activeElt.ownerDocument\n    let sel = doc.defaultView.getSelection(), range = doc.createRange()\n    range.setEnd(snapshot.anchorNode, snapshot.anchorOffset)\n    range.collapse(false)\n    sel.removeAllRanges()\n    sel.addRange(range)\n    sel.extend(snapshot.focusNode, snapshot.focusOffset)\n  }\n}\n\n// Does the actual updating of the line display. Bails out\n// (returning false) when there is nothing to be done and forced is\n// false.\nexport function updateDisplayIfNeeded(cm, update) {\n  let display = cm.display, doc = cm.doc\n\n  if (update.editorIsHidden) {\n    resetView(cm)\n    return false\n  }\n\n  // Bail out if the visible area is already rendered and nothing changed.\n  if (!update.force &&\n      update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&\n      (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&\n      display.renderedView == display.view && countDirtyView(cm) == 0)\n    return false\n\n  if (maybeUpdateLineNumberWidth(cm)) {\n    resetView(cm)\n    update.dims = getDimensions(cm)\n  }\n\n  // Compute a suitable new viewport (from & to)\n  let end = doc.first + doc.size\n  let from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first)\n  let to = Math.min(end, update.visible.to + cm.options.viewportMargin)\n  if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom)\n  if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo)\n  if (sawCollapsedSpans) {\n    from = visualLineNo(cm.doc, from)\n    to = visualLineEndNo(cm.doc, to)\n  }\n\n  let different = from != display.viewFrom || to != display.viewTo ||\n    display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth\n  adjustView(cm, from, to)\n\n  display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom))\n  // Position the mover div to align with the current scroll position\n  cm.display.mover.style.top = display.viewOffset + \"px\"\n\n  let toUpdate = countDirtyView(cm)\n  if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&\n      (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))\n    return false\n\n  // For big changes, we hide the enclosing element during the\n  // update, since that speeds up the operations on most browsers.\n  let selSnapshot = selectionSnapshot(cm)\n  if (toUpdate > 4) display.lineDiv.style.display = \"none\"\n  patchDisplay(cm, display.updateLineNumbers, update.dims)\n  if (toUpdate > 4) display.lineDiv.style.display = \"\"\n  display.renderedView = display.view\n  // There might have been a widget with a focused element that got\n  // hidden or updated, if so re-focus it.\n  restoreSelection(selSnapshot)\n\n  // Prevent selection and cursors from interfering with the scroll\n  // width and height.\n  removeChildren(display.cursorDiv)\n  removeChildren(display.selectionDiv)\n  display.gutters.style.height = display.sizer.style.minHeight = 0\n\n  if (different) {\n    display.lastWrapHeight = update.wrapperHeight\n    display.lastWrapWidth = update.wrapperWidth\n    startWorker(cm, 400)\n  }\n\n  display.updateLineNumbers = null\n\n  return true\n}\n\nexport function postUpdateDisplay(cm, update) {\n  let viewport = update.viewport\n\n  for (let first = true;; first = false) {\n    if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {\n      // Clip forced viewport to actual scrollable area.\n      if (viewport && viewport.top != null)\n        viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}\n      // Updated line heights might result in the drawn area not\n      // actually covering the viewport. Keep looping until it does.\n      update.visible = visibleLines(cm.display, cm.doc, viewport)\n      if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)\n        break\n    } else if (first) {\n      update.visible = visibleLines(cm.display, cm.doc, viewport)\n    }\n    if (!updateDisplayIfNeeded(cm, update)) break\n    updateHeightsInViewport(cm)\n    let barMeasure = measureForScrollbars(cm)\n    updateSelection(cm)\n    updateScrollbars(cm, barMeasure)\n    setDocumentHeight(cm, barMeasure)\n    update.force = false\n  }\n\n  update.signal(cm, \"update\", cm)\n  if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {\n    update.signal(cm, \"viewportChange\", cm, cm.display.viewFrom, cm.display.viewTo)\n    cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo\n  }\n}\n\nexport function updateDisplaySimple(cm, viewport) {\n  let update = new DisplayUpdate(cm, viewport)\n  if (updateDisplayIfNeeded(cm, update)) {\n    updateHeightsInViewport(cm)\n    postUpdateDisplay(cm, update)\n    let barMeasure = measureForScrollbars(cm)\n    updateSelection(cm)\n    updateScrollbars(cm, barMeasure)\n    setDocumentHeight(cm, barMeasure)\n    update.finish()\n  }\n}\n\n// Sync the actual display DOM structure with display.view, removing\n// nodes for lines that are no longer in view, and creating the ones\n// that are not there yet, and updating the ones that are out of\n// date.\nfunction patchDisplay(cm, updateNumbersFrom, dims) {\n  let display = cm.display, lineNumbers = cm.options.lineNumbers\n  let container = display.lineDiv, cur = container.firstChild\n\n  function rm(node) {\n    let next = node.nextSibling\n    // Works around a throw-scroll bug in OS X Webkit\n    if (webkit && mac && cm.display.currentWheelTarget == node)\n      node.style.display = \"none\"\n    else\n      node.parentNode.removeChild(node)\n    return next\n  }\n\n  let view = display.view, lineN = display.viewFrom\n  // Loop over the elements in the view, syncing cur (the DOM nodes\n  // in display.lineDiv) with the view as we go.\n  for (let i = 0; i < view.length; i++) {\n    let lineView = view[i]\n    if (lineView.hidden) {\n    } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet\n      let node = buildLineElement(cm, lineView, lineN, dims)\n      container.insertBefore(node, cur)\n    } else { // Already drawn\n      while (cur != lineView.node) cur = rm(cur)\n      let updateNumber = lineNumbers && updateNumbersFrom != null &&\n        updateNumbersFrom <= lineN && lineView.lineNumber\n      if (lineView.changes) {\n        if (indexOf(lineView.changes, \"gutter\") > -1) updateNumber = false\n        updateLineForChanges(cm, lineView, lineN, dims)\n      }\n      if (updateNumber) {\n        removeChildren(lineView.lineNumber)\n        lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)))\n      }\n      cur = lineView.node.nextSibling\n    }\n    lineN += lineView.size\n  }\n  while (cur) cur = rm(cur)\n}\n\nexport function updateGutterSpace(display) {\n  let width = display.gutters.offsetWidth\n  display.sizer.style.marginLeft = width + \"px\"\n  // Send an event to consumers responding to changes in gutter width.\n  signalLater(display, \"gutterChanged\", display)\n}\n\nexport function setDocumentHeight(cm, measure) {\n  cm.display.sizer.style.minHeight = measure.docHeight + \"px\"\n  cm.display.heightForcer.style.top = measure.docHeight + \"px\"\n  cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + \"px\"\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/update_line.js",
    "content": "import { buildLineContent } from \"../line/line_data.js\"\nimport { lineNumberFor } from \"../line/utils_line.js\"\nimport { ie, ie_version } from \"../util/browser.js\"\nimport { elt, classTest } from \"../util/dom.js\"\nimport { signalLater } from \"../util/operation_group.js\"\n\n// When an aspect of a line changes, a string is added to\n// lineView.changes. This updates the relevant part of the line's\n// DOM structure.\nexport function updateLineForChanges(cm, lineView, lineN, dims) {\n  for (let j = 0; j < lineView.changes.length; j++) {\n    let type = lineView.changes[j]\n    if (type == \"text\") updateLineText(cm, lineView)\n    else if (type == \"gutter\") updateLineGutter(cm, lineView, lineN, dims)\n    else if (type == \"class\") updateLineClasses(cm, lineView)\n    else if (type == \"widget\") updateLineWidgets(cm, lineView, dims)\n  }\n  lineView.changes = null\n}\n\n// Lines with gutter elements, widgets or a background class need to\n// be wrapped, and have the extra elements added to the wrapper div\nfunction ensureLineWrapped(lineView) {\n  if (lineView.node == lineView.text) {\n    lineView.node = elt(\"div\", null, null, \"position: relative\")\n    if (lineView.text.parentNode)\n      lineView.text.parentNode.replaceChild(lineView.node, lineView.text)\n    lineView.node.appendChild(lineView.text)\n    if (ie && ie_version < 8) lineView.node.style.zIndex = 2\n  }\n  return lineView.node\n}\n\nfunction updateLineBackground(cm, lineView) {\n  let cls = lineView.bgClass ? lineView.bgClass + \" \" + (lineView.line.bgClass || \"\") : lineView.line.bgClass\n  if (cls) cls += \" CodeMirror-linebackground\"\n  if (lineView.background) {\n    if (cls) lineView.background.className = cls\n    else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }\n  } else if (cls) {\n    let wrap = ensureLineWrapped(lineView)\n    lineView.background = wrap.insertBefore(elt(\"div\", null, cls), wrap.firstChild)\n    cm.display.input.setUneditable(lineView.background)\n  }\n}\n\n// Wrapper around buildLineContent which will reuse the structure\n// in display.externalMeasured when possible.\nfunction getLineContent(cm, lineView) {\n  let ext = cm.display.externalMeasured\n  if (ext && ext.line == lineView.line) {\n    cm.display.externalMeasured = null\n    lineView.measure = ext.measure\n    return ext.built\n  }\n  return buildLineContent(cm, lineView)\n}\n\n// Redraw the line's text. Interacts with the background and text\n// classes because the mode may output tokens that influence these\n// classes.\nfunction updateLineText(cm, lineView) {\n  let cls = lineView.text.className\n  let built = getLineContent(cm, lineView)\n  if (lineView.text == lineView.node) lineView.node = built.pre\n  lineView.text.parentNode.replaceChild(built.pre, lineView.text)\n  lineView.text = built.pre\n  if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {\n    lineView.bgClass = built.bgClass\n    lineView.textClass = built.textClass\n    updateLineClasses(cm, lineView)\n  } else if (cls) {\n    lineView.text.className = cls\n  }\n}\n\nfunction updateLineClasses(cm, lineView) {\n  updateLineBackground(cm, lineView)\n  if (lineView.line.wrapClass)\n    ensureLineWrapped(lineView).className = lineView.line.wrapClass\n  else if (lineView.node != lineView.text)\n    lineView.node.className = \"\"\n  let textClass = lineView.textClass ? lineView.textClass + \" \" + (lineView.line.textClass || \"\") : lineView.line.textClass\n  lineView.text.className = textClass || \"\"\n}\n\nfunction updateLineGutter(cm, lineView, lineN, dims) {\n  if (lineView.gutter) {\n    lineView.node.removeChild(lineView.gutter)\n    lineView.gutter = null\n  }\n  if (lineView.gutterBackground) {\n    lineView.node.removeChild(lineView.gutterBackground)\n    lineView.gutterBackground = null\n  }\n  if (lineView.line.gutterClass) {\n    let wrap = ensureLineWrapped(lineView)\n    lineView.gutterBackground = elt(\"div\", null, \"CodeMirror-gutter-background \" + lineView.line.gutterClass,\n                                    `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px; width: ${dims.gutterTotalWidth}px`)\n    cm.display.input.setUneditable(lineView.gutterBackground)\n    wrap.insertBefore(lineView.gutterBackground, lineView.text)\n  }\n  let markers = lineView.line.gutterMarkers\n  if (cm.options.lineNumbers || markers) {\n    let wrap = ensureLineWrapped(lineView)\n    let gutterWrap = lineView.gutter = elt(\"div\", null, \"CodeMirror-gutter-wrapper\", `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px`)\n    gutterWrap.setAttribute(\"aria-hidden\", \"true\")\n    cm.display.input.setUneditable(gutterWrap)\n    wrap.insertBefore(gutterWrap, lineView.text)\n    if (lineView.line.gutterClass)\n      gutterWrap.className += \" \" + lineView.line.gutterClass\n    if (cm.options.lineNumbers && (!markers || !markers[\"CodeMirror-linenumbers\"]))\n      lineView.lineNumber = gutterWrap.appendChild(\n        elt(\"div\", lineNumberFor(cm.options, lineN),\n            \"CodeMirror-linenumber CodeMirror-gutter-elt\",\n            `left: ${dims.gutterLeft[\"CodeMirror-linenumbers\"]}px; width: ${cm.display.lineNumInnerWidth}px`))\n    if (markers) for (let k = 0; k < cm.display.gutterSpecs.length; ++k) {\n      let id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]\n      if (found)\n        gutterWrap.appendChild(elt(\"div\", [found], \"CodeMirror-gutter-elt\",\n                                   `left: ${dims.gutterLeft[id]}px; width: ${dims.gutterWidth[id]}px`))\n    }\n  }\n}\n\nfunction updateLineWidgets(cm, lineView, dims) {\n  if (lineView.alignable) lineView.alignable = null\n  let isWidget = classTest(\"CodeMirror-linewidget\")\n  for (let node = lineView.node.firstChild, next; node; node = next) {\n    next = node.nextSibling\n    if (isWidget.test(node.className)) lineView.node.removeChild(node)\n  }\n  insertLineWidgets(cm, lineView, dims)\n}\n\n// Build a line's DOM representation from scratch\nexport function buildLineElement(cm, lineView, lineN, dims) {\n  let built = getLineContent(cm, lineView)\n  lineView.text = lineView.node = built.pre\n  if (built.bgClass) lineView.bgClass = built.bgClass\n  if (built.textClass) lineView.textClass = built.textClass\n\n  updateLineClasses(cm, lineView)\n  updateLineGutter(cm, lineView, lineN, dims)\n  insertLineWidgets(cm, lineView, dims)\n  return lineView.node\n}\n\n// A lineView may contain multiple logical lines (when merged by\n// collapsed spans). The widgets for all of them need to be drawn.\nfunction insertLineWidgets(cm, lineView, dims) {\n  insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)\n  if (lineView.rest) for (let i = 0; i < lineView.rest.length; i++)\n    insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false)\n}\n\nfunction insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {\n  if (!line.widgets) return\n  let wrap = ensureLineWrapped(lineView)\n  for (let i = 0, ws = line.widgets; i < ws.length; ++i) {\n    let widget = ws[i], node = elt(\"div\", [widget.node], \"CodeMirror-linewidget\" + (widget.className ? \" \" + widget.className : \"\"))\n    if (!widget.handleMouseEvents) node.setAttribute(\"cm-ignore-events\", \"true\")\n    positionLineWidget(widget, node, lineView, dims)\n    cm.display.input.setUneditable(node)\n    if (allowAbove && widget.above)\n      wrap.insertBefore(node, lineView.gutter || lineView.text)\n    else\n      wrap.appendChild(node)\n    signalLater(widget, \"redraw\")\n  }\n}\n\nfunction positionLineWidget(widget, node, lineView, dims) {\n  if (widget.noHScroll) {\n    ;(lineView.alignable || (lineView.alignable = [])).push(node)\n    let width = dims.wrapperWidth\n    node.style.left = dims.fixedPos + \"px\"\n    if (!widget.coverGutter) {\n      width -= dims.gutterTotalWidth\n      node.style.paddingLeft = dims.gutterTotalWidth + \"px\"\n    }\n    node.style.width = width + \"px\"\n  }\n  if (widget.coverGutter) {\n    node.style.zIndex = 5\n    node.style.position = \"relative\"\n    if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + \"px\"\n  }\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/update_lines.js",
    "content": "import { heightAtLine } from \"../line/spans.js\"\nimport { getLine, lineAtHeight, updateLineHeight } from \"../line/utils_line.js\"\nimport { paddingTop, charWidth } from \"../measurement/position_measurement.js\"\nimport { ie, ie_version } from \"../util/browser.js\"\n\n// Read the actual heights of the rendered lines, and update their\n// stored heights to match.\nexport function updateHeightsInViewport(cm) {\n  let display = cm.display\n  let prevBottom = display.lineDiv.offsetTop\n  let viewTop = Math.max(0, display.scroller.getBoundingClientRect().top)\n  let oldHeight = display.lineDiv.getBoundingClientRect().top\n  let mustScroll = 0\n  for (let i = 0; i < display.view.length; i++) {\n    let cur = display.view[i], wrapping = cm.options.lineWrapping\n    let height, width = 0\n    if (cur.hidden) continue\n    oldHeight += cur.line.height\n    if (ie && ie_version < 8) {\n      let bot = cur.node.offsetTop + cur.node.offsetHeight\n      height = bot - prevBottom\n      prevBottom = bot\n    } else {\n      let box = cur.node.getBoundingClientRect()\n      height = box.bottom - box.top\n      // Check that lines don't extend past the right of the current\n      // editor width\n      if (!wrapping && cur.text.firstChild)\n        width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1\n    }\n    let diff = cur.line.height - height\n    if (diff > .005 || diff < -.005) {\n      if (oldHeight < viewTop) mustScroll -= diff\n      updateLineHeight(cur.line, height)\n      updateWidgetHeight(cur.line)\n      if (cur.rest) for (let j = 0; j < cur.rest.length; j++)\n        updateWidgetHeight(cur.rest[j])\n    }\n    if (width > cm.display.sizerWidth) {\n      let chWidth = Math.ceil(width / charWidth(cm.display))\n      if (chWidth > cm.display.maxLineLength) {\n        cm.display.maxLineLength = chWidth\n        cm.display.maxLine = cur.line\n        cm.display.maxLineChanged = true\n      }\n    }\n  }\n  if (Math.abs(mustScroll) > 2) display.scroller.scrollTop += mustScroll\n}\n\n// Read and store the height of line widgets associated with the\n// given line.\nfunction updateWidgetHeight(line) {\n  if (line.widgets) for (let i = 0; i < line.widgets.length; ++i) {\n    let w = line.widgets[i], parent = w.node.parentNode\n    if (parent) w.height = parent.offsetHeight\n  }\n}\n\n// Compute the lines that are visible in a given viewport (defaults\n// the the current scroll position). viewport may contain top,\n// height, and ensure (see op.scrollToPos) properties.\nexport function visibleLines(display, doc, viewport) {\n  let top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop\n  top = Math.floor(top - paddingTop(display))\n  let bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight\n\n  let from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom)\n  // Ensure is a {from: {line, ch}, to: {line, ch}} object, and\n  // forces those lines into the viewport (if possible).\n  if (viewport && viewport.ensure) {\n    let ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line\n    if (ensureFrom < from) {\n      from = ensureFrom\n      to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)\n    } else if (Math.min(ensureTo, doc.lastLine()) >= to) {\n      from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight)\n      to = ensureTo\n    }\n  }\n  return {from: from, to: Math.max(to, from + 1)}\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/display/view_tracking.js",
    "content": "import { buildViewArray } from \"../line/line_data.js\"\nimport { sawCollapsedSpans } from \"../line/saw_special_spans.js\"\nimport { visualLineEndNo, visualLineNo } from \"../line/spans.js\"\nimport { findViewIndex } from \"../measurement/position_measurement.js\"\nimport { indexOf } from \"../util/misc.js\"\n\n// Updates the display.view data structure for a given change to the\n// document. From and to are in pre-change coordinates. Lendiff is\n// the amount of lines added or subtracted by the change. This is\n// used for changes that span multiple lines, or change the way\n// lines are divided into visual lines. regLineChange (below)\n// registers single-line changes.\nexport function regChange(cm, from, to, lendiff) {\n  if (from == null) from = cm.doc.first\n  if (to == null) to = cm.doc.first + cm.doc.size\n  if (!lendiff) lendiff = 0\n\n  let display = cm.display\n  if (lendiff && to < display.viewTo &&\n      (display.updateLineNumbers == null || display.updateLineNumbers > from))\n    display.updateLineNumbers = from\n\n  cm.curOp.viewChanged = true\n\n  if (from >= display.viewTo) { // Change after\n    if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)\n      resetView(cm)\n  } else if (to <= display.viewFrom) { // Change before\n    if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {\n      resetView(cm)\n    } else {\n      display.viewFrom += lendiff\n      display.viewTo += lendiff\n    }\n  } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap\n    resetView(cm)\n  } else if (from <= display.viewFrom) { // Top overlap\n    let cut = viewCuttingPoint(cm, to, to + lendiff, 1)\n    if (cut) {\n      display.view = display.view.slice(cut.index)\n      display.viewFrom = cut.lineN\n      display.viewTo += lendiff\n    } else {\n      resetView(cm)\n    }\n  } else if (to >= display.viewTo) { // Bottom overlap\n    let cut = viewCuttingPoint(cm, from, from, -1)\n    if (cut) {\n      display.view = display.view.slice(0, cut.index)\n      display.viewTo = cut.lineN\n    } else {\n      resetView(cm)\n    }\n  } else { // Gap in the middle\n    let cutTop = viewCuttingPoint(cm, from, from, -1)\n    let cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)\n    if (cutTop && cutBot) {\n      display.view = display.view.slice(0, cutTop.index)\n        .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))\n        .concat(display.view.slice(cutBot.index))\n      display.viewTo += lendiff\n    } else {\n      resetView(cm)\n    }\n  }\n\n  let ext = display.externalMeasured\n  if (ext) {\n    if (to < ext.lineN)\n      ext.lineN += lendiff\n    else if (from < ext.lineN + ext.size)\n      display.externalMeasured = null\n  }\n}\n\n// Register a change to a single line. Type must be one of \"text\",\n// \"gutter\", \"class\", \"widget\"\nexport function regLineChange(cm, line, type) {\n  cm.curOp.viewChanged = true\n  let display = cm.display, ext = cm.display.externalMeasured\n  if (ext && line >= ext.lineN && line < ext.lineN + ext.size)\n    display.externalMeasured = null\n\n  if (line < display.viewFrom || line >= display.viewTo) return\n  let lineView = display.view[findViewIndex(cm, line)]\n  if (lineView.node == null) return\n  let arr = lineView.changes || (lineView.changes = [])\n  if (indexOf(arr, type) == -1) arr.push(type)\n}\n\n// Clear the view.\nexport function resetView(cm) {\n  cm.display.viewFrom = cm.display.viewTo = cm.doc.first\n  cm.display.view = []\n  cm.display.viewOffset = 0\n}\n\nfunction viewCuttingPoint(cm, oldN, newN, dir) {\n  let index = findViewIndex(cm, oldN), diff, view = cm.display.view\n  if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)\n    return {index: index, lineN: newN}\n  let n = cm.display.viewFrom\n  for (let i = 0; i < index; i++)\n    n += view[i].size\n  if (n != oldN) {\n    if (dir > 0) {\n      if (index == view.length - 1) return null\n      diff = (n + view[index].size) - oldN\n      index++\n    } else {\n      diff = n - oldN\n    }\n    oldN += diff; newN += diff\n  }\n  while (visualLineNo(cm.doc, newN) != newN) {\n    if (index == (dir < 0 ? 0 : view.length - 1)) return null\n    newN += dir * view[index - (dir < 0 ? 1 : 0)].size\n    index += dir\n  }\n  return {index: index, lineN: newN}\n}\n\n// Force the view to cover a given range, adding empty view element\n// or clipping off existing ones as needed.\nexport function adjustView(cm, from, to) {\n  let display = cm.display, view = display.view\n  if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {\n    display.view = buildViewArray(cm, from, to)\n    display.viewFrom = from\n  } else {\n    if (display.viewFrom > from)\n      display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view)\n    else if (display.viewFrom < from)\n      display.view = display.view.slice(findViewIndex(cm, from))\n    display.viewFrom = from\n    if (display.viewTo < to)\n      display.view = display.view.concat(buildViewArray(cm, display.viewTo, to))\n    else if (display.viewTo > to)\n      display.view = display.view.slice(0, findViewIndex(cm, to))\n  }\n  display.viewTo = to\n}\n\n// Count the number of lines in the view whose DOM representation is\n// out of date (or nonexistent).\nexport function countDirtyView(cm) {\n  let view = cm.display.view, dirty = 0\n  for (let i = 0; i < view.length; i++) {\n    let lineView = view[i]\n    if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty\n  }\n  return dirty\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/CodeMirror.js",
    "content": "import { Display } from \"../display/Display.js\"\nimport { onFocus, onBlur } from \"../display/focus.js\"\nimport { maybeUpdateLineNumberWidth } from \"../display/line_numbers.js\"\nimport { endOperation, operation, startOperation } from \"../display/operations.js\"\nimport { initScrollbars } from \"../display/scrollbars.js\"\nimport { onScrollWheel } from \"../display/scroll_events.js\"\nimport { setScrollLeft, updateScrollTop } from \"../display/scrolling.js\"\nimport { clipPos, Pos } from \"../line/pos.js\"\nimport { posFromMouse } from \"../measurement/position_measurement.js\"\nimport { eventInWidget } from \"../measurement/widgets.js\"\nimport Doc from \"../model/Doc.js\"\nimport { attachDoc } from \"../model/document_data.js\"\nimport { Range } from \"../model/selection.js\"\nimport { extendSelection } from \"../model/selection_updates.js\"\nimport { ie, ie_version, mobile, webkit } from \"../util/browser.js\"\nimport { e_preventDefault, e_stop, on, signal, signalDOMEvent } from \"../util/event.js\"\nimport { copyObj, Delayed } from \"../util/misc.js\"\n\nimport { clearDragCursor, onDragOver, onDragStart, onDrop } from \"./drop_events.js\"\nimport { ensureGlobalHandlers } from \"./global_events.js\"\nimport { onKeyDown, onKeyPress, onKeyUp } from \"./key_events.js\"\nimport { clickInGutter, onContextMenu, onMouseDown } from \"./mouse_events.js\"\nimport { themeChanged } from \"./utils.js\"\nimport { defaults, optionHandlers, Init } from \"./options.js\"\n\n// A CodeMirror instance represents an editor. This is the object\n// that user code is usually dealing with.\n\nexport function CodeMirror(place, options) {\n  if (!(this instanceof CodeMirror)) return new CodeMirror(place, options)\n\n  this.options = options = options ? copyObj(options) : {}\n  // Determine effective options based on given values and defaults.\n  copyObj(defaults, options, false)\n\n  let doc = options.value\n  if (typeof doc == \"string\") doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction)\n  else if (options.mode) doc.modeOption = options.mode\n  this.doc = doc\n\n  let input = new CodeMirror.inputStyles[options.inputStyle](this)\n  let display = this.display = new Display(place, doc, input, options)\n  display.wrapper.CodeMirror = this\n  themeChanged(this)\n  if (options.lineWrapping)\n    this.display.wrapper.className += \" CodeMirror-wrap\"\n  initScrollbars(this)\n\n  this.state = {\n    keyMaps: [],  // stores maps added by addKeyMap\n    overlays: [], // highlighting overlays, as added by addOverlay\n    modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info\n    overwrite: false,\n    delayingBlurEvent: false,\n    focused: false,\n    suppressEdits: false, // used to disable editing during key handlers when in readOnly mode\n    pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll\n    selectingText: false,\n    draggingText: false,\n    highlight: new Delayed(), // stores highlight worker timeout\n    keySeq: null,  // Unfinished key sequence\n    specialChars: null\n  }\n\n  if (options.autofocus && !mobile) display.input.focus()\n\n  // Override magic textarea content restore that IE sometimes does\n  // on our hidden textarea on reload\n  if (ie && ie_version < 11) setTimeout(() => this.display.input.reset(true), 20)\n\n  registerEventHandlers(this)\n  ensureGlobalHandlers()\n\n  startOperation(this)\n  this.curOp.forceUpdate = true\n  attachDoc(this, doc)\n\n  if ((options.autofocus && !mobile) || this.hasFocus())\n    setTimeout(() => {\n      if (this.hasFocus() && !this.state.focused) onFocus(this)\n    }, 20)\n  else\n    onBlur(this)\n\n  for (let opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))\n    optionHandlers[opt](this, options[opt], Init)\n  maybeUpdateLineNumberWidth(this)\n  if (options.finishInit) options.finishInit(this)\n  for (let i = 0; i < initHooks.length; ++i) initHooks[i](this)\n  endOperation(this)\n  // Suppress optimizelegibility in Webkit, since it breaks text\n  // measuring on line wrapping boundaries.\n  if (webkit && options.lineWrapping &&\n      getComputedStyle(display.lineDiv).textRendering == \"optimizelegibility\")\n    display.lineDiv.style.textRendering = \"auto\"\n}\n\n// The default configuration options.\nCodeMirror.defaults = defaults\n// Functions to run when options are changed.\nCodeMirror.optionHandlers = optionHandlers\n\nexport default CodeMirror\n\n// Attach the necessary event handlers when initializing the editor\nfunction registerEventHandlers(cm) {\n  let d = cm.display\n  on(d.scroller, \"mousedown\", operation(cm, onMouseDown))\n  // Older IE's will not fire a second mousedown for a double click\n  if (ie && ie_version < 11)\n    on(d.scroller, \"dblclick\", operation(cm, e => {\n      if (signalDOMEvent(cm, e)) return\n      let pos = posFromMouse(cm, e)\n      if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return\n      e_preventDefault(e)\n      let word = cm.findWordAt(pos)\n      extendSelection(cm.doc, word.anchor, word.head)\n    }))\n  else\n    on(d.scroller, \"dblclick\", e => signalDOMEvent(cm, e) || e_preventDefault(e))\n  // Some browsers fire contextmenu *after* opening the menu, at\n  // which point we can't mess with it anymore. Context menu is\n  // handled in onMouseDown for these browsers.\n  on(d.scroller, \"contextmenu\", e => onContextMenu(cm, e))\n  on(d.input.getField(), \"contextmenu\", e => {\n    if (!d.scroller.contains(e.target)) onContextMenu(cm, e)\n  })\n\n  // Used to suppress mouse event handling when a touch happens\n  let touchFinished, prevTouch = {end: 0}\n  function finishTouch() {\n    if (d.activeTouch) {\n      touchFinished = setTimeout(() => d.activeTouch = null, 1000)\n      prevTouch = d.activeTouch\n      prevTouch.end = +new Date\n    }\n  }\n  function isMouseLikeTouchEvent(e) {\n    if (e.touches.length != 1) return false\n    let touch = e.touches[0]\n    return touch.radiusX <= 1 && touch.radiusY <= 1\n  }\n  function farAway(touch, other) {\n    if (other.left == null) return true\n    let dx = other.left - touch.left, dy = other.top - touch.top\n    return dx * dx + dy * dy > 20 * 20\n  }\n  on(d.scroller, \"touchstart\", e => {\n    if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {\n      d.input.ensurePolled()\n      clearTimeout(touchFinished)\n      let now = +new Date\n      d.activeTouch = {start: now, moved: false,\n                       prev: now - prevTouch.end <= 300 ? prevTouch : null}\n      if (e.touches.length == 1) {\n        d.activeTouch.left = e.touches[0].pageX\n        d.activeTouch.top = e.touches[0].pageY\n      }\n    }\n  })\n  on(d.scroller, \"touchmove\", () => {\n    if (d.activeTouch) d.activeTouch.moved = true\n  })\n  on(d.scroller, \"touchend\", e => {\n    let touch = d.activeTouch\n    if (touch && !eventInWidget(d, e) && touch.left != null &&\n        !touch.moved && new Date - touch.start < 300) {\n      let pos = cm.coordsChar(d.activeTouch, \"page\"), range\n      if (!touch.prev || farAway(touch, touch.prev)) // Single tap\n        range = new Range(pos, pos)\n      else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap\n        range = cm.findWordAt(pos)\n      else // Triple tap\n        range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)))\n      cm.setSelection(range.anchor, range.head)\n      cm.focus()\n      e_preventDefault(e)\n    }\n    finishTouch()\n  })\n  on(d.scroller, \"touchcancel\", finishTouch)\n\n  // Sync scrolling between fake scrollbars and real scrollable\n  // area, ensure viewport is updated when scrolling.\n  on(d.scroller, \"scroll\", () => {\n    if (d.scroller.clientHeight) {\n      updateScrollTop(cm, d.scroller.scrollTop)\n      setScrollLeft(cm, d.scroller.scrollLeft, true)\n      signal(cm, \"scroll\", cm)\n    }\n  })\n\n  // Listen to wheel events in order to try and update the viewport on time.\n  on(d.scroller, \"mousewheel\", e => onScrollWheel(cm, e))\n  on(d.scroller, \"DOMMouseScroll\", e => onScrollWheel(cm, e))\n\n  // Prevent wrapper from ever scrolling\n  on(d.wrapper, \"scroll\", () => d.wrapper.scrollTop = d.wrapper.scrollLeft = 0)\n\n  d.dragFunctions = {\n    enter: e => {if (!signalDOMEvent(cm, e)) e_stop(e)},\n    over: e => {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }},\n    start: e => onDragStart(cm, e),\n    drop: operation(cm, onDrop),\n    leave: e => {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }}\n  }\n\n  let inp = d.input.getField()\n  on(inp, \"keyup\", e => onKeyUp.call(cm, e))\n  on(inp, \"keydown\", operation(cm, onKeyDown))\n  on(inp, \"keypress\", operation(cm, onKeyPress))\n  on(inp, \"focus\", e => onFocus(cm, e))\n  on(inp, \"blur\", e => onBlur(cm, e))\n}\n\nlet initHooks = []\nCodeMirror.defineInitHook = f => initHooks.push(f)\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/commands.js",
    "content": "import { deleteNearSelection } from \"./deleteNearSelection.js\"\nimport { runInOp } from \"../display/operations.js\"\nimport { ensureCursorVisible } from \"../display/scrolling.js\"\nimport { endOfLine } from \"../input/movement.js\"\nimport { clipPos, Pos } from \"../line/pos.js\"\nimport { visualLine, visualLineEnd } from \"../line/spans.js\"\nimport { getLine, lineNo } from \"../line/utils_line.js\"\nimport { Range } from \"../model/selection.js\"\nimport { selectAll } from \"../model/selection_updates.js\"\nimport { countColumn, sel_dontScroll, sel_move, spaceStr } from \"../util/misc.js\"\nimport { getOrder } from \"../util/bidi.js\"\n\n// Commands are parameter-less actions that can be performed on an\n// editor, mostly used for keybindings.\nexport let commands = {\n  selectAll: selectAll,\n  singleSelection: cm => cm.setSelection(cm.getCursor(\"anchor\"), cm.getCursor(\"head\"), sel_dontScroll),\n  killLine: cm => deleteNearSelection(cm, range => {\n    if (range.empty()) {\n      let len = getLine(cm.doc, range.head.line).text.length\n      if (range.head.ch == len && range.head.line < cm.lastLine())\n        return {from: range.head, to: Pos(range.head.line + 1, 0)}\n      else\n        return {from: range.head, to: Pos(range.head.line, len)}\n    } else {\n      return {from: range.from(), to: range.to()}\n    }\n  }),\n  deleteLine: cm => deleteNearSelection(cm, range => ({\n    from: Pos(range.from().line, 0),\n    to: clipPos(cm.doc, Pos(range.to().line + 1, 0))\n  })),\n  delLineLeft: cm => deleteNearSelection(cm, range => ({\n    from: Pos(range.from().line, 0), to: range.from()\n  })),\n  delWrappedLineLeft: cm => deleteNearSelection(cm, range => {\n    let top = cm.charCoords(range.head, \"div\").top + 5\n    let leftPos = cm.coordsChar({left: 0, top: top}, \"div\")\n    return {from: leftPos, to: range.from()}\n  }),\n  delWrappedLineRight: cm => deleteNearSelection(cm, range => {\n    let top = cm.charCoords(range.head, \"div\").top + 5\n    let rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, \"div\")\n    return {from: range.from(), to: rightPos }\n  }),\n  undo: cm => cm.undo(),\n  redo: cm => cm.redo(),\n  undoSelection: cm => cm.undoSelection(),\n  redoSelection: cm => cm.redoSelection(),\n  goDocStart: cm => cm.extendSelection(Pos(cm.firstLine(), 0)),\n  goDocEnd: cm => cm.extendSelection(Pos(cm.lastLine())),\n  goLineStart: cm => cm.extendSelectionsBy(range => lineStart(cm, range.head.line),\n    {origin: \"+move\", bias: 1}\n  ),\n  goLineStartSmart: cm => cm.extendSelectionsBy(range => lineStartSmart(cm, range.head),\n    {origin: \"+move\", bias: 1}\n  ),\n  goLineEnd: cm => cm.extendSelectionsBy(range => lineEnd(cm, range.head.line),\n    {origin: \"+move\", bias: -1}\n  ),\n  goLineRight: cm => cm.extendSelectionsBy(range => {\n    let top = cm.cursorCoords(range.head, \"div\").top + 5\n    return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, \"div\")\n  }, sel_move),\n  goLineLeft: cm => cm.extendSelectionsBy(range => {\n    let top = cm.cursorCoords(range.head, \"div\").top + 5\n    return cm.coordsChar({left: 0, top: top}, \"div\")\n  }, sel_move),\n  goLineLeftSmart: cm => cm.extendSelectionsBy(range => {\n    let top = cm.cursorCoords(range.head, \"div\").top + 5\n    let pos = cm.coordsChar({left: 0, top: top}, \"div\")\n    if (pos.ch < cm.getLine(pos.line).search(/\\S/)) return lineStartSmart(cm, range.head)\n    return pos\n  }, sel_move),\n  goLineUp: cm => cm.moveV(-1, \"line\"),\n  goLineDown: cm => cm.moveV(1, \"line\"),\n  goPageUp: cm => cm.moveV(-1, \"page\"),\n  goPageDown: cm => cm.moveV(1, \"page\"),\n  goCharLeft: cm => cm.moveH(-1, \"char\"),\n  goCharRight: cm => cm.moveH(1, \"char\"),\n  goColumnLeft: cm => cm.moveH(-1, \"column\"),\n  goColumnRight: cm => cm.moveH(1, \"column\"),\n  goWordLeft: cm => cm.moveH(-1, \"word\"),\n  goGroupRight: cm => cm.moveH(1, \"group\"),\n  goGroupLeft: cm => cm.moveH(-1, \"group\"),\n  goWordRight: cm => cm.moveH(1, \"word\"),\n  delCharBefore: cm => cm.deleteH(-1, \"codepoint\"),\n  delCharAfter: cm => cm.deleteH(1, \"char\"),\n  delWordBefore: cm => cm.deleteH(-1, \"word\"),\n  delWordAfter: cm => cm.deleteH(1, \"word\"),\n  delGroupBefore: cm => cm.deleteH(-1, \"group\"),\n  delGroupAfter: cm => cm.deleteH(1, \"group\"),\n  indentAuto: cm => cm.indentSelection(\"smart\"),\n  indentMore: cm => cm.indentSelection(\"add\"),\n  indentLess: cm => cm.indentSelection(\"subtract\"),\n  insertTab: cm => cm.replaceSelection(\"\\t\"),\n  insertSoftTab: cm => {\n    let spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize\n    for (let i = 0; i < ranges.length; i++) {\n      let pos = ranges[i].from()\n      let col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)\n      spaces.push(spaceStr(tabSize - col % tabSize))\n    }\n    cm.replaceSelections(spaces)\n  },\n  defaultTab: cm => {\n    if (cm.somethingSelected()) cm.indentSelection(\"add\")\n    else cm.execCommand(\"insertTab\")\n  },\n  // Swap the two chars left and right of each selection's head.\n  // Move cursor behind the two swapped characters afterwards.\n  //\n  // Doesn't consider line feeds a character.\n  // Doesn't scan more than one line above to find a character.\n  // Doesn't do anything on an empty line.\n  // Doesn't do anything with non-empty selections.\n  transposeChars: cm => runInOp(cm, () => {\n    let ranges = cm.listSelections(), newSel = []\n    for (let i = 0; i < ranges.length; i++) {\n      if (!ranges[i].empty()) continue\n      let cur = ranges[i].head, line = getLine(cm.doc, cur.line).text\n      if (line) {\n        if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1)\n        if (cur.ch > 0) {\n          cur = new Pos(cur.line, cur.ch + 1)\n          cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),\n                          Pos(cur.line, cur.ch - 2), cur, \"+transpose\")\n        } else if (cur.line > cm.doc.first) {\n          let prev = getLine(cm.doc, cur.line - 1).text\n          if (prev) {\n            cur = new Pos(cur.line, 1)\n            cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +\n                            prev.charAt(prev.length - 1),\n                            Pos(cur.line - 1, prev.length - 1), cur, \"+transpose\")\n          }\n        }\n      }\n      newSel.push(new Range(cur, cur))\n    }\n    cm.setSelections(newSel)\n  }),\n  newlineAndIndent: cm => runInOp(cm, () => {\n    let sels = cm.listSelections()\n    for (let i = sels.length - 1; i >= 0; i--)\n      cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, \"+input\")\n    sels = cm.listSelections()\n    for (let i = 0; i < sels.length; i++)\n      cm.indentLine(sels[i].from().line, null, true)\n    ensureCursorVisible(cm)\n  }),\n  openLine: cm => cm.replaceSelection(\"\\n\", \"start\"),\n  toggleOverwrite: cm => cm.toggleOverwrite()\n}\n\n\nfunction lineStart(cm, lineN) {\n  let line = getLine(cm.doc, lineN)\n  let visual = visualLine(line)\n  if (visual != line) lineN = lineNo(visual)\n  return endOfLine(true, cm, visual, lineN, 1)\n}\nfunction lineEnd(cm, lineN) {\n  let line = getLine(cm.doc, lineN)\n  let visual = visualLineEnd(line)\n  if (visual != line) lineN = lineNo(visual)\n  return endOfLine(true, cm, line, lineN, -1)\n}\nfunction lineStartSmart(cm, pos) {\n  let start = lineStart(cm, pos.line)\n  let line = getLine(cm.doc, start.line)\n  let order = getOrder(line, cm.doc.direction)\n  if (!order || order[0].level == 0) {\n    let firstNonWS = Math.max(start.ch, line.text.search(/\\S/))\n    let inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch\n    return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)\n  }\n  return start\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/deleteNearSelection.js",
    "content": "import { runInOp } from \"../display/operations.js\"\nimport { ensureCursorVisible } from \"../display/scrolling.js\"\nimport { cmp } from \"../line/pos.js\"\nimport { replaceRange } from \"../model/changes.js\"\nimport { lst } from \"../util/misc.js\"\n\n// Helper for deleting text near the selection(s), used to implement\n// backspace, delete, and similar functionality.\nexport function deleteNearSelection(cm, compute) {\n  let ranges = cm.doc.sel.ranges, kill = []\n  // Build up a set of ranges to kill first, merging overlapping\n  // ranges.\n  for (let i = 0; i < ranges.length; i++) {\n    let toKill = compute(ranges[i])\n    while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {\n      let replaced = kill.pop()\n      if (cmp(replaced.from, toKill.from) < 0) {\n        toKill.from = replaced.from\n        break\n      }\n    }\n    kill.push(toKill)\n  }\n  // Next, remove those actual ranges.\n  runInOp(cm, () => {\n    for (let i = kill.length - 1; i >= 0; i--)\n      replaceRange(cm.doc, \"\", kill[i].from, kill[i].to, \"+delete\")\n    ensureCursorVisible(cm)\n  })\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/drop_events.js",
    "content": "import { drawSelectionCursor } from \"../display/selection.js\"\nimport { operation } from \"../display/operations.js\"\nimport { clipPos } from \"../line/pos.js\"\nimport { posFromMouse } from \"../measurement/position_measurement.js\"\nimport { eventInWidget } from \"../measurement/widgets.js\"\nimport { makeChange, replaceRange } from \"../model/changes.js\"\nimport { changeEnd } from \"../model/change_measurement.js\"\nimport { simpleSelection } from \"../model/selection.js\"\nimport { setSelectionNoUndo, setSelectionReplaceHistory } from \"../model/selection_updates.js\"\nimport { ie, presto, safari } from \"../util/browser.js\"\nimport { elt, removeChildrenAndAdd } from \"../util/dom.js\"\nimport { e_preventDefault, e_stop, signalDOMEvent } from \"../util/event.js\"\nimport { indexOf } from \"../util/misc.js\"\n\n// Kludge to work around strange IE behavior where it'll sometimes\n// re-fire a series of drag-related events right after the drop (#1551)\nlet lastDrop = 0\n\nexport function onDrop(e) {\n  let cm = this\n  clearDragCursor(cm)\n  if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))\n    return\n  e_preventDefault(e)\n  if (ie) lastDrop = +new Date\n  let pos = posFromMouse(cm, e, true), files = e.dataTransfer.files\n  if (!pos || cm.isReadOnly()) return\n  // Might be a file drop, in which case we simply extract the text\n  // and insert it.\n  if (files && files.length && window.FileReader && window.File) {\n    let n = files.length, text = Array(n), read = 0\n    const markAsReadAndPasteIfAllFilesAreRead = () => {\n      if (++read == n) {\n        operation(cm, () => {\n          pos = clipPos(cm.doc, pos)\n          let change = {from: pos, to: pos,\n                        text: cm.doc.splitLines(\n                            text.filter(t => t != null).join(cm.doc.lineSeparator())),\n                        origin: \"paste\"}\n          makeChange(cm.doc, change)\n          setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))))\n        })()\n      }\n    }\n    const readTextFromFile = (file, i) => {\n      if (cm.options.allowDropFileTypes &&\n          indexOf(cm.options.allowDropFileTypes, file.type) == -1) {\n        markAsReadAndPasteIfAllFilesAreRead()\n        return\n      }\n      let reader = new FileReader\n      reader.onerror = () => markAsReadAndPasteIfAllFilesAreRead()\n      reader.onload = () => {\n        let content = reader.result\n        if (/[\\x00-\\x08\\x0e-\\x1f]{2}/.test(content)) {\n          markAsReadAndPasteIfAllFilesAreRead()\n          return\n        }\n        text[i] = content\n        markAsReadAndPasteIfAllFilesAreRead()\n      }\n      reader.readAsText(file)\n    }\n    for (let i = 0; i < files.length; i++) readTextFromFile(files[i], i)\n  } else { // Normal drop\n    // Don't do a replace if the drop happened inside of the selected text.\n    if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {\n      cm.state.draggingText(e)\n      // Ensure the editor is re-focused\n      setTimeout(() => cm.display.input.focus(), 20)\n      return\n    }\n    try {\n      let text = e.dataTransfer.getData(\"Text\")\n      if (text) {\n        let selected\n        if (cm.state.draggingText && !cm.state.draggingText.copy)\n          selected = cm.listSelections()\n        setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))\n        if (selected) for (let i = 0; i < selected.length; ++i)\n          replaceRange(cm.doc, \"\", selected[i].anchor, selected[i].head, \"drag\")\n        cm.replaceSelection(text, \"around\", \"paste\")\n        cm.display.input.focus()\n      }\n    }\n    catch(e){}\n  }\n}\n\nexport function onDragStart(cm, e) {\n  if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }\n  if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return\n\n  e.dataTransfer.setData(\"Text\", cm.getSelection())\n  e.dataTransfer.effectAllowed = \"copyMove\"\n\n  // Use dummy image instead of default browsers image.\n  // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.\n  if (e.dataTransfer.setDragImage && !safari) {\n    let img = elt(\"img\", null, null, \"position: fixed; left: 0; top: 0;\")\n    img.src = \"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\"\n    if (presto) {\n      img.width = img.height = 1\n      cm.display.wrapper.appendChild(img)\n      // Force a relayout, or Opera won't use our image for some obscure reason\n      img._top = img.offsetTop\n    }\n    e.dataTransfer.setDragImage(img, 0, 0)\n    if (presto) img.parentNode.removeChild(img)\n  }\n}\n\nexport function onDragOver(cm, e) {\n  let pos = posFromMouse(cm, e)\n  if (!pos) return\n  let frag = document.createDocumentFragment()\n  drawSelectionCursor(cm, pos, frag)\n  if (!cm.display.dragCursor) {\n    cm.display.dragCursor = elt(\"div\", null, \"CodeMirror-cursors CodeMirror-dragcursors\")\n    cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv)\n  }\n  removeChildrenAndAdd(cm.display.dragCursor, frag)\n}\n\nexport function clearDragCursor(cm) {\n  if (cm.display.dragCursor) {\n    cm.display.lineSpace.removeChild(cm.display.dragCursor)\n    cm.display.dragCursor = null\n  }\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/fromTextArea.js",
    "content": "import { CodeMirror } from \"./CodeMirror.js\"\nimport { activeElt, rootNode } from \"../util/dom.js\"\nimport { off, on } from \"../util/event.js\"\nimport { copyObj } from \"../util/misc.js\"\n\nexport function fromTextArea(textarea, options) {\n  options = options ? copyObj(options) : {}\n  options.value = textarea.value\n  if (!options.tabindex && textarea.tabIndex)\n    options.tabindex = textarea.tabIndex\n  if (!options.placeholder && textarea.placeholder)\n    options.placeholder = textarea.placeholder\n  // Set autofocus to true if this textarea is focused, or if it has\n  // autofocus and no other element is focused.\n  if (options.autofocus == null) {\n    let hasFocus = activeElt(rootNode(textarea))\n    options.autofocus = hasFocus == textarea ||\n      textarea.getAttribute(\"autofocus\") != null && hasFocus == document.body\n  }\n\n  function save() {textarea.value = cm.getValue()}\n\n  let realSubmit\n  if (textarea.form) {\n    on(textarea.form, \"submit\", save)\n    // Deplorable hack to make the submit method do the right thing.\n    if (!options.leaveSubmitMethodAlone) {\n      let form = textarea.form\n      realSubmit = form.submit\n      try {\n        let wrappedSubmit = form.submit = () => {\n          save()\n          form.submit = realSubmit\n          form.submit()\n          form.submit = wrappedSubmit\n        }\n      } catch(e) {}\n    }\n  }\n\n  options.finishInit = cm => {\n    cm.save = save\n    cm.getTextArea = () => textarea\n    cm.toTextArea = () => {\n      cm.toTextArea = isNaN // Prevent this from being ran twice\n      save()\n      textarea.parentNode.removeChild(cm.getWrapperElement())\n      textarea.style.display = \"\"\n      if (textarea.form) {\n        off(textarea.form, \"submit\", save)\n        if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == \"function\")\n          textarea.form.submit = realSubmit\n      }\n    }\n  }\n\n  textarea.style.display = \"none\"\n  let cm = CodeMirror(node => textarea.parentNode.insertBefore(node, textarea.nextSibling),\n    options)\n  return cm\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/global_events.js",
    "content": "import { onBlur } from \"../display/focus.js\"\nimport { on } from \"../util/event.js\"\n\n// These must be handled carefully, because naively registering a\n// handler for each editor will cause the editors to never be\n// garbage collected.\n\nfunction forEachCodeMirror(f) {\n  if (!document.getElementsByClassName) return\n  let byClass = document.getElementsByClassName(\"CodeMirror\"), editors = []\n  for (let i = 0; i < byClass.length; i++) {\n    let cm = byClass[i].CodeMirror\n    if (cm) editors.push(cm)\n  }\n  if (editors.length) editors[0].operation(() => {\n    for (let i = 0; i < editors.length; i++) f(editors[i])\n  })\n}\n\nlet globalsRegistered = false\nexport function ensureGlobalHandlers() {\n  if (globalsRegistered) return\n  registerGlobalHandlers()\n  globalsRegistered = true\n}\nfunction registerGlobalHandlers() {\n  // When the window resizes, we need to refresh active editors.\n  let resizeTimer\n  on(window, \"resize\", () => {\n    if (resizeTimer == null) resizeTimer = setTimeout(() => {\n      resizeTimer = null\n      forEachCodeMirror(onResize)\n    }, 100)\n  })\n  // When the window loses focus, we want to show the editor as blurred\n  on(window, \"blur\", () => forEachCodeMirror(onBlur))\n}\n// Called when the window resizes\nfunction onResize(cm) {\n  let d = cm.display\n  // Might be a text scaling operation, clear size caches.\n  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null\n  d.scrollbarsClipped = false\n  cm.setSize()\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/key_events.js",
    "content": "import { signalLater } from \"../util/operation_group.js\"\nimport { restartBlink } from \"../display/selection.js\"\nimport { isModifierKey, keyName, lookupKey } from \"../input/keymap.js\"\nimport { eventInWidget } from \"../measurement/widgets.js\"\nimport { ie, ie_version, mac, presto, gecko } from \"../util/browser.js\"\nimport { activeElt, addClass, rmClass, root } from \"../util/dom.js\"\nimport { e_preventDefault, off, on, signalDOMEvent } from \"../util/event.js\"\nimport { hasCopyEvent } from \"../util/feature_detection.js\"\nimport { Delayed, Pass } from \"../util/misc.js\"\n\nimport { commands } from \"./commands.js\"\n\n// Run a handler that was bound to a key.\nfunction doHandleBinding(cm, bound, dropShift) {\n  if (typeof bound == \"string\") {\n    bound = commands[bound]\n    if (!bound) return false\n  }\n  // Ensure previous input has been read, so that the handler sees a\n  // consistent view of the document\n  cm.display.input.ensurePolled()\n  let prevShift = cm.display.shift, done = false\n  try {\n    if (cm.isReadOnly()) cm.state.suppressEdits = true\n    if (dropShift) cm.display.shift = false\n    done = bound(cm) != Pass\n  } finally {\n    cm.display.shift = prevShift\n    cm.state.suppressEdits = false\n  }\n  return done\n}\n\nfunction lookupKeyForEditor(cm, name, handle) {\n  for (let i = 0; i < cm.state.keyMaps.length; i++) {\n    let result = lookupKey(name, cm.state.keyMaps[i], handle, cm)\n    if (result) return result\n  }\n  return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))\n    || lookupKey(name, cm.options.keyMap, handle, cm)\n}\n\n// Note that, despite the name, this function is also used to check\n// for bound mouse clicks.\n\nlet stopSeq = new Delayed\n\nexport function dispatchKey(cm, name, e, handle) {\n  let seq = cm.state.keySeq\n  if (seq) {\n    if (isModifierKey(name)) return \"handled\"\n    if (/\\'$/.test(name))\n      cm.state.keySeq = null\n    else\n      stopSeq.set(50, () => {\n        if (cm.state.keySeq == seq) {\n          cm.state.keySeq = null\n          cm.display.input.reset()\n        }\n      })\n    if (dispatchKeyInner(cm, seq + \" \" + name, e, handle)) return true\n  }\n  return dispatchKeyInner(cm, name, e, handle)\n}\n\nfunction dispatchKeyInner(cm, name, e, handle) {\n  let result = lookupKeyForEditor(cm, name, handle)\n\n  if (result == \"multi\")\n    cm.state.keySeq = name\n  if (result == \"handled\")\n    signalLater(cm, \"keyHandled\", cm, name, e)\n\n  if (result == \"handled\" || result == \"multi\") {\n    e_preventDefault(e)\n    restartBlink(cm)\n  }\n\n  return !!result\n}\n\n// Handle a key from the keydown event.\nfunction handleKeyBinding(cm, e) {\n  let name = keyName(e, true)\n  if (!name) return false\n\n  if (e.shiftKey && !cm.state.keySeq) {\n    // First try to resolve full name (including 'Shift-'). Failing\n    // that, see if there is a cursor-motion command (starting with\n    // 'go') bound to the keyname without 'Shift-'.\n    return dispatchKey(cm, \"Shift-\" + name, e, b => doHandleBinding(cm, b, true))\n        || dispatchKey(cm, name, e, b => {\n             if (typeof b == \"string\" ? /^go[A-Z]/.test(b) : b.motion)\n               return doHandleBinding(cm, b)\n           })\n  } else {\n    return dispatchKey(cm, name, e, b => doHandleBinding(cm, b))\n  }\n}\n\n// Handle a key from the keypress event\nfunction handleCharBinding(cm, e, ch) {\n  return dispatchKey(cm, \"'\" + ch + \"'\", e, b => doHandleBinding(cm, b, true))\n}\n\nlet lastStoppedKey = null\nexport function onKeyDown(e) {\n  let cm = this\n  if (e.target && e.target != cm.display.input.getField()) return\n  cm.curOp.focus = activeElt(root(cm))\n  if (signalDOMEvent(cm, e)) return\n  // IE does strange things with escape.\n  if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false\n  let code = e.keyCode\n  cm.display.shift = code == 16 || e.shiftKey\n  let handled = handleKeyBinding(cm, e)\n  if (presto) {\n    lastStoppedKey = handled ? code : null\n    // Opera has no cut event... we try to at least catch the key combo\n    if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))\n      cm.replaceSelection(\"\", null, \"cut\")\n  }\n  if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand)\n    document.execCommand(\"cut\")\n\n  // Turn mouse into crosshair when Alt is held on Mac.\n  if (code == 18 && !/\\bCodeMirror-crosshair\\b/.test(cm.display.lineDiv.className))\n    showCrossHair(cm)\n}\n\nfunction showCrossHair(cm) {\n  let lineDiv = cm.display.lineDiv\n  addClass(lineDiv, \"CodeMirror-crosshair\")\n\n  function up(e) {\n    if (e.keyCode == 18 || !e.altKey) {\n      rmClass(lineDiv, \"CodeMirror-crosshair\")\n      off(document, \"keyup\", up)\n      off(document, \"mouseover\", up)\n    }\n  }\n  on(document, \"keyup\", up)\n  on(document, \"mouseover\", up)\n}\n\nexport function onKeyUp(e) {\n  if (e.keyCode == 16) this.doc.sel.shift = false\n  signalDOMEvent(this, e)\n}\n\nexport function onKeyPress(e) {\n  let cm = this\n  if (e.target && e.target != cm.display.input.getField()) return\n  if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return\n  let keyCode = e.keyCode, charCode = e.charCode\n  if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}\n  if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return\n  let ch = String.fromCharCode(charCode == null ? keyCode : charCode)\n  // Some browsers fire keypress events for backspace\n  if (ch == \"\\x08\") return\n  if (handleCharBinding(cm, e, ch)) return\n  cm.display.input.onKeyPress(e)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/legacy.js",
    "content": "import { scrollbarModel } from \"../display/scrollbars.js\"\nimport { wheelEventPixels } from \"../display/scroll_events.js\"\nimport { keyMap, keyName, isModifierKey, lookupKey, normalizeKeyMap } from \"../input/keymap.js\"\nimport { keyNames } from \"../input/keynames.js\"\nimport { Line } from \"../line/line_data.js\"\nimport { cmp, Pos } from \"../line/pos.js\"\nimport { changeEnd } from \"../model/change_measurement.js\"\nimport Doc from \"../model/Doc.js\"\nimport { LineWidget } from \"../model/line_widget.js\"\nimport { SharedTextMarker, TextMarker } from \"../model/mark_text.js\"\nimport { copyState, extendMode, getMode, innerMode, mimeModes, modeExtensions, modes, resolveMode, startState } from \"../modes.js\"\nimport { addClass, contains, rmClass } from \"../util/dom.js\"\nimport { e_preventDefault, e_stop, e_stopPropagation, off, on, signal } from \"../util/event.js\"\nimport { splitLinesAuto } from \"../util/feature_detection.js\"\nimport { countColumn, findColumn, isWordCharBasic, Pass } from \"../util/misc.js\"\nimport StringStream from \"../util/StringStream.js\"\n\nimport { commands } from \"./commands.js\"\n\nexport function addLegacyProps(CodeMirror) {\n  CodeMirror.off = off\n  CodeMirror.on = on\n  CodeMirror.wheelEventPixels = wheelEventPixels\n  CodeMirror.Doc = Doc\n  CodeMirror.splitLines = splitLinesAuto\n  CodeMirror.countColumn = countColumn\n  CodeMirror.findColumn = findColumn\n  CodeMirror.isWordChar = isWordCharBasic\n  CodeMirror.Pass = Pass\n  CodeMirror.signal = signal\n  CodeMirror.Line = Line\n  CodeMirror.changeEnd = changeEnd\n  CodeMirror.scrollbarModel = scrollbarModel\n  CodeMirror.Pos = Pos\n  CodeMirror.cmpPos = cmp\n  CodeMirror.modes = modes\n  CodeMirror.mimeModes = mimeModes\n  CodeMirror.resolveMode = resolveMode\n  CodeMirror.getMode = getMode\n  CodeMirror.modeExtensions = modeExtensions\n  CodeMirror.extendMode = extendMode\n  CodeMirror.copyState = copyState\n  CodeMirror.startState = startState\n  CodeMirror.innerMode = innerMode\n  CodeMirror.commands = commands\n  CodeMirror.keyMap = keyMap\n  CodeMirror.keyName = keyName\n  CodeMirror.isModifierKey = isModifierKey\n  CodeMirror.lookupKey = lookupKey\n  CodeMirror.normalizeKeyMap = normalizeKeyMap\n  CodeMirror.StringStream = StringStream\n  CodeMirror.SharedTextMarker = SharedTextMarker\n  CodeMirror.TextMarker = TextMarker\n  CodeMirror.LineWidget = LineWidget\n  CodeMirror.e_preventDefault = e_preventDefault\n  CodeMirror.e_stopPropagation = e_stopPropagation\n  CodeMirror.e_stop = e_stop\n  CodeMirror.addClass = addClass\n  CodeMirror.contains = contains\n  CodeMirror.rmClass = rmClass\n  CodeMirror.keyNames = keyNames\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/main.js",
    "content": "// EDITOR CONSTRUCTOR\n\nimport { CodeMirror } from \"./CodeMirror.js\"\nexport { CodeMirror } from \"./CodeMirror.js\"\n\nimport { eventMixin } from \"../util/event.js\"\nimport { indexOf } from \"../util/misc.js\"\n\nimport { defineOptions } from \"./options.js\"\n\ndefineOptions(CodeMirror)\n\nimport addEditorMethods from \"./methods.js\"\n\naddEditorMethods(CodeMirror)\n\nimport Doc from \"../model/Doc.js\"\n\n// Set up methods on CodeMirror's prototype to redirect to the editor's document.\nlet dontDelegate = \"iter insert remove copy getEditor constructor\".split(\" \")\nfor (let prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)\n  CodeMirror.prototype[prop] = (function(method) {\n    return function() {return method.apply(this.doc, arguments)}\n  })(Doc.prototype[prop])\n\neventMixin(Doc)\n\n// INPUT HANDLING\n\nimport ContentEditableInput from \"../input/ContentEditableInput.js\"\nimport TextareaInput from \"../input/TextareaInput.js\"\nCodeMirror.inputStyles = {\"textarea\": TextareaInput, \"contenteditable\": ContentEditableInput}\n\n// MODE DEFINITION AND QUERYING\n\nimport { defineMIME, defineMode } from \"../modes.js\"\n\n// Extra arguments are stored as the mode's dependencies, which is\n// used by (legacy) mechanisms like loadmode.js to automatically\n// load a mode. (Preferred mechanism is the require/define calls.)\nCodeMirror.defineMode = function(name/*, mode, …*/) {\n  if (!CodeMirror.defaults.mode && name != \"null\") CodeMirror.defaults.mode = name\n  defineMode.apply(this, arguments)\n}\n\nCodeMirror.defineMIME = defineMIME\n\n// Minimal default mode.\nCodeMirror.defineMode(\"null\", () => ({token: stream => stream.skipToEnd()}))\nCodeMirror.defineMIME(\"text/plain\", \"null\")\n\n// EXTENSIONS\n\nCodeMirror.defineExtension = (name, func) => {\n  CodeMirror.prototype[name] = func\n}\nCodeMirror.defineDocExtension = (name, func) => {\n  Doc.prototype[name] = func\n}\n\nimport { fromTextArea } from \"./fromTextArea.js\"\n\nCodeMirror.fromTextArea = fromTextArea\n\nimport { addLegacyProps } from \"./legacy.js\"\n\naddLegacyProps(CodeMirror)\n\nCodeMirror.version = \"5.65.16\"\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/methods.js",
    "content": "import { deleteNearSelection } from \"./deleteNearSelection.js\"\nimport { commands } from \"./commands.js\"\nimport { attachDoc } from \"../model/document_data.js\"\nimport { activeElt, addClass, rmClass, root, win } from \"../util/dom.js\"\nimport { eventMixin, signal } from \"../util/event.js\"\nimport { getLineStyles, getContextBefore, takeToken } from \"../line/highlight.js\"\nimport { indentLine } from \"../input/indent.js\"\nimport { triggerElectric } from \"../input/input.js\"\nimport { onKeyDown, onKeyPress, onKeyUp } from \"./key_events.js\"\nimport { onMouseDown } from \"./mouse_events.js\"\nimport { getKeyMap } from \"../input/keymap.js\"\nimport { endOfLine, moveLogically, moveVisually } from \"../input/movement.js\"\nimport { endOperation, methodOp, operation, runInOp, startOperation } from \"../display/operations.js\"\nimport { clipLine, clipPos, equalCursorPos, Pos } from \"../line/pos.js\"\nimport { charCoords, charWidth, clearCaches, clearLineMeasurementCache, coordsChar, cursorCoords, displayHeight, displayWidth, estimateLineHeights, fromCoordSystem, intoCoordSystem, scrollGap, textHeight } from \"../measurement/position_measurement.js\"\nimport { Range } from \"../model/selection.js\"\nimport { replaceOneSelection, skipAtomic } from \"../model/selection_updates.js\"\nimport { addToScrollTop, ensureCursorVisible, scrollIntoView, scrollToCoords, scrollToCoordsRange, scrollToRange } from \"../display/scrolling.js\"\nimport { heightAtLine } from \"../line/spans.js\"\nimport { updateGutterSpace } from \"../display/update_display.js\"\nimport { indexOf, insertSorted, isWordChar, sel_dontScroll, sel_move } from \"../util/misc.js\"\nimport { signalLater } from \"../util/operation_group.js\"\nimport { getLine, isLine, lineAtHeight } from \"../line/utils_line.js\"\nimport { regChange, regLineChange } from \"../display/view_tracking.js\"\n\n// The publicly visible API. Note that methodOp(f) means\n// 'wrap f in an operation, performed on its `this` parameter'.\n\n// This is not the complete set of editor methods. Most of the\n// methods defined on the Doc type are also injected into\n// CodeMirror.prototype, for backwards compatibility and\n// convenience.\n\nexport default function(CodeMirror) {\n  let optionHandlers = CodeMirror.optionHandlers\n\n  let helpers = CodeMirror.helpers = {}\n\n  CodeMirror.prototype = {\n    constructor: CodeMirror,\n    focus: function(){win(this).focus(); this.display.input.focus()},\n\n    setOption: function(option, value) {\n      let options = this.options, old = options[option]\n      if (options[option] == value && option != \"mode\") return\n      options[option] = value\n      if (optionHandlers.hasOwnProperty(option))\n        operation(this, optionHandlers[option])(this, value, old)\n      signal(this, \"optionChange\", this, option)\n    },\n\n    getOption: function(option) {return this.options[option]},\n    getDoc: function() {return this.doc},\n\n    addKeyMap: function(map, bottom) {\n      this.state.keyMaps[bottom ? \"push\" : \"unshift\"](getKeyMap(map))\n    },\n    removeKeyMap: function(map) {\n      let maps = this.state.keyMaps\n      for (let i = 0; i < maps.length; ++i)\n        if (maps[i] == map || maps[i].name == map) {\n          maps.splice(i, 1)\n          return true\n        }\n    },\n\n    addOverlay: methodOp(function(spec, options) {\n      let mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)\n      if (mode.startState) throw new Error(\"Overlays may not be stateful.\")\n      insertSorted(this.state.overlays,\n                   {mode: mode, modeSpec: spec, opaque: options && options.opaque,\n                    priority: (options && options.priority) || 0},\n                   overlay => overlay.priority)\n      this.state.modeGen++\n      regChange(this)\n    }),\n    removeOverlay: methodOp(function(spec) {\n      let overlays = this.state.overlays\n      for (let i = 0; i < overlays.length; ++i) {\n        let cur = overlays[i].modeSpec\n        if (cur == spec || typeof spec == \"string\" && cur.name == spec) {\n          overlays.splice(i, 1)\n          this.state.modeGen++\n          regChange(this)\n          return\n        }\n      }\n    }),\n\n    indentLine: methodOp(function(n, dir, aggressive) {\n      if (typeof dir != \"string\" && typeof dir != \"number\") {\n        if (dir == null) dir = this.options.smartIndent ? \"smart\" : \"prev\"\n        else dir = dir ? \"add\" : \"subtract\"\n      }\n      if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive)\n    }),\n    indentSelection: methodOp(function(how) {\n      let ranges = this.doc.sel.ranges, end = -1\n      for (let i = 0; i < ranges.length; i++) {\n        let range = ranges[i]\n        if (!range.empty()) {\n          let from = range.from(), to = range.to()\n          let start = Math.max(end, from.line)\n          end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1\n          for (let j = start; j < end; ++j)\n            indentLine(this, j, how)\n          let newRanges = this.doc.sel.ranges\n          if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)\n            replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll)\n        } else if (range.head.line > end) {\n          indentLine(this, range.head.line, how, true)\n          end = range.head.line\n          if (i == this.doc.sel.primIndex) ensureCursorVisible(this)\n        }\n      }\n    }),\n\n    // Fetch the parser token for a given character. Useful for hacks\n    // that want to inspect the mode state (say, for completion).\n    getTokenAt: function(pos, precise) {\n      return takeToken(this, pos, precise)\n    },\n\n    getLineTokens: function(line, precise) {\n      return takeToken(this, Pos(line), precise, true)\n    },\n\n    getTokenTypeAt: function(pos) {\n      pos = clipPos(this.doc, pos)\n      let styles = getLineStyles(this, getLine(this.doc, pos.line))\n      let before = 0, after = (styles.length - 1) / 2, ch = pos.ch\n      let type\n      if (ch == 0) type = styles[2]\n      else for (;;) {\n        let mid = (before + after) >> 1\n        if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid\n        else if (styles[mid * 2 + 1] < ch) before = mid + 1\n        else { type = styles[mid * 2 + 2]; break }\n      }\n      let cut = type ? type.indexOf(\"overlay \") : -1\n      return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)\n    },\n\n    getModeAt: function(pos) {\n      let mode = this.doc.mode\n      if (!mode.innerMode) return mode\n      return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode\n    },\n\n    getHelper: function(pos, type) {\n      return this.getHelpers(pos, type)[0]\n    },\n\n    getHelpers: function(pos, type) {\n      let found = []\n      if (!helpers.hasOwnProperty(type)) return found\n      let help = helpers[type], mode = this.getModeAt(pos)\n      if (typeof mode[type] == \"string\") {\n        if (help[mode[type]]) found.push(help[mode[type]])\n      } else if (mode[type]) {\n        for (let i = 0; i < mode[type].length; i++) {\n          let val = help[mode[type][i]]\n          if (val) found.push(val)\n        }\n      } else if (mode.helperType && help[mode.helperType]) {\n        found.push(help[mode.helperType])\n      } else if (help[mode.name]) {\n        found.push(help[mode.name])\n      }\n      for (let i = 0; i < help._global.length; i++) {\n        let cur = help._global[i]\n        if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)\n          found.push(cur.val)\n      }\n      return found\n    },\n\n    getStateAfter: function(line, precise) {\n      let doc = this.doc\n      line = clipLine(doc, line == null ? doc.first + doc.size - 1: line)\n      return getContextBefore(this, line + 1, precise).state\n    },\n\n    cursorCoords: function(start, mode) {\n      let pos, range = this.doc.sel.primary()\n      if (start == null) pos = range.head\n      else if (typeof start == \"object\") pos = clipPos(this.doc, start)\n      else pos = start ? range.from() : range.to()\n      return cursorCoords(this, pos, mode || \"page\")\n    },\n\n    charCoords: function(pos, mode) {\n      return charCoords(this, clipPos(this.doc, pos), mode || \"page\")\n    },\n\n    coordsChar: function(coords, mode) {\n      coords = fromCoordSystem(this, coords, mode || \"page\")\n      return coordsChar(this, coords.left, coords.top)\n    },\n\n    lineAtHeight: function(height, mode) {\n      height = fromCoordSystem(this, {top: height, left: 0}, mode || \"page\").top\n      return lineAtHeight(this.doc, height + this.display.viewOffset)\n    },\n    heightAtLine: function(line, mode, includeWidgets) {\n      let end = false, lineObj\n      if (typeof line == \"number\") {\n        let last = this.doc.first + this.doc.size - 1\n        if (line < this.doc.first) line = this.doc.first\n        else if (line > last) { line = last; end = true }\n        lineObj = getLine(this.doc, line)\n      } else {\n        lineObj = line\n      }\n      return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || \"page\", includeWidgets || end).top +\n        (end ? this.doc.height - heightAtLine(lineObj) : 0)\n    },\n\n    defaultTextHeight: function() { return textHeight(this.display) },\n    defaultCharWidth: function() { return charWidth(this.display) },\n\n    getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},\n\n    addWidget: function(pos, node, scroll, vert, horiz) {\n      let display = this.display\n      pos = cursorCoords(this, clipPos(this.doc, pos))\n      let top = pos.bottom, left = pos.left\n      node.style.position = \"absolute\"\n      node.setAttribute(\"cm-ignore-events\", \"true\")\n      this.display.input.setUneditable(node)\n      display.sizer.appendChild(node)\n      if (vert == \"over\") {\n        top = pos.top\n      } else if (vert == \"above\" || vert == \"near\") {\n        let vspace = Math.max(display.wrapper.clientHeight, this.doc.height),\n        hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth)\n        // Default to positioning above (if specified and possible); otherwise default to positioning below\n        if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)\n          top = pos.top - node.offsetHeight\n        else if (pos.bottom + node.offsetHeight <= vspace)\n          top = pos.bottom\n        if (left + node.offsetWidth > hspace)\n          left = hspace - node.offsetWidth\n      }\n      node.style.top = top + \"px\"\n      node.style.left = node.style.right = \"\"\n      if (horiz == \"right\") {\n        left = display.sizer.clientWidth - node.offsetWidth\n        node.style.right = \"0px\"\n      } else {\n        if (horiz == \"left\") left = 0\n        else if (horiz == \"middle\") left = (display.sizer.clientWidth - node.offsetWidth) / 2\n        node.style.left = left + \"px\"\n      }\n      if (scroll)\n        scrollIntoView(this, {left, top, right: left + node.offsetWidth, bottom: top + node.offsetHeight})\n    },\n\n    triggerOnKeyDown: methodOp(onKeyDown),\n    triggerOnKeyPress: methodOp(onKeyPress),\n    triggerOnKeyUp: onKeyUp,\n    triggerOnMouseDown: methodOp(onMouseDown),\n\n    execCommand: function(cmd) {\n      if (commands.hasOwnProperty(cmd))\n        return commands[cmd].call(null, this)\n    },\n\n    triggerElectric: methodOp(function(text) { triggerElectric(this, text) }),\n\n    findPosH: function(from, amount, unit, visually) {\n      let dir = 1\n      if (amount < 0) { dir = -1; amount = -amount }\n      let cur = clipPos(this.doc, from)\n      for (let i = 0; i < amount; ++i) {\n        cur = findPosH(this.doc, cur, dir, unit, visually)\n        if (cur.hitSide) break\n      }\n      return cur\n    },\n\n    moveH: methodOp(function(dir, unit) {\n      this.extendSelectionsBy(range => {\n        if (this.display.shift || this.doc.extend || range.empty())\n          return findPosH(this.doc, range.head, dir, unit, this.options.rtlMoveVisually)\n        else\n          return dir < 0 ? range.from() : range.to()\n      }, sel_move)\n    }),\n\n    deleteH: methodOp(function(dir, unit) {\n      let sel = this.doc.sel, doc = this.doc\n      if (sel.somethingSelected())\n        doc.replaceSelection(\"\", null, \"+delete\")\n      else\n        deleteNearSelection(this, range => {\n          let other = findPosH(doc, range.head, dir, unit, false)\n          return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}\n        })\n    }),\n\n    findPosV: function(from, amount, unit, goalColumn) {\n      let dir = 1, x = goalColumn\n      if (amount < 0) { dir = -1; amount = -amount }\n      let cur = clipPos(this.doc, from)\n      for (let i = 0; i < amount; ++i) {\n        let coords = cursorCoords(this, cur, \"div\")\n        if (x == null) x = coords.left\n        else coords.left = x\n        cur = findPosV(this, coords, dir, unit)\n        if (cur.hitSide) break\n      }\n      return cur\n    },\n\n    moveV: methodOp(function(dir, unit) {\n      let doc = this.doc, goals = []\n      let collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected()\n      doc.extendSelectionsBy(range => {\n        if (collapse)\n          return dir < 0 ? range.from() : range.to()\n        let headPos = cursorCoords(this, range.head, \"div\")\n        if (range.goalColumn != null) headPos.left = range.goalColumn\n        goals.push(headPos.left)\n        let pos = findPosV(this, headPos, dir, unit)\n        if (unit == \"page\" && range == doc.sel.primary())\n          addToScrollTop(this, charCoords(this, pos, \"div\").top - headPos.top)\n        return pos\n      }, sel_move)\n      if (goals.length) for (let i = 0; i < doc.sel.ranges.length; i++)\n        doc.sel.ranges[i].goalColumn = goals[i]\n    }),\n\n    // Find the word at the given position (as returned by coordsChar).\n    findWordAt: function(pos) {\n      let doc = this.doc, line = getLine(doc, pos.line).text\n      let start = pos.ch, end = pos.ch\n      if (line) {\n        let helper = this.getHelper(pos, \"wordChars\")\n        if ((pos.sticky == \"before\" || end == line.length) && start) --start; else ++end\n        let startChar = line.charAt(start)\n        let check = isWordChar(startChar, helper)\n          ? ch => isWordChar(ch, helper)\n          : /\\s/.test(startChar) ? ch => /\\s/.test(ch)\n          : ch => (!/\\s/.test(ch) && !isWordChar(ch))\n        while (start > 0 && check(line.charAt(start - 1))) --start\n        while (end < line.length && check(line.charAt(end))) ++end\n      }\n      return new Range(Pos(pos.line, start), Pos(pos.line, end))\n    },\n\n    toggleOverwrite: function(value) {\n      if (value != null && value == this.state.overwrite) return\n      if (this.state.overwrite = !this.state.overwrite)\n        addClass(this.display.cursorDiv, \"CodeMirror-overwrite\")\n      else\n        rmClass(this.display.cursorDiv, \"CodeMirror-overwrite\")\n\n      signal(this, \"overwriteToggle\", this, this.state.overwrite)\n    },\n    hasFocus: function() { return this.display.input.getField() == activeElt(root(this)) },\n    isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },\n\n    scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y) }),\n    getScrollInfo: function() {\n      let scroller = this.display.scroller\n      return {left: scroller.scrollLeft, top: scroller.scrollTop,\n              height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,\n              width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,\n              clientHeight: displayHeight(this), clientWidth: displayWidth(this)}\n    },\n\n    scrollIntoView: methodOp(function(range, margin) {\n      if (range == null) {\n        range = {from: this.doc.sel.primary().head, to: null}\n        if (margin == null) margin = this.options.cursorScrollMargin\n      } else if (typeof range == \"number\") {\n        range = {from: Pos(range, 0), to: null}\n      } else if (range.from == null) {\n        range = {from: range, to: null}\n      }\n      if (!range.to) range.to = range.from\n      range.margin = margin || 0\n\n      if (range.from.line != null) {\n        scrollToRange(this, range)\n      } else {\n        scrollToCoordsRange(this, range.from, range.to, range.margin)\n      }\n    }),\n\n    setSize: methodOp(function(width, height) {\n      let interpret = val => typeof val == \"number\" || /^\\d+$/.test(String(val)) ? val + \"px\" : val\n      if (width != null) this.display.wrapper.style.width = interpret(width)\n      if (height != null) this.display.wrapper.style.height = interpret(height)\n      if (this.options.lineWrapping) clearLineMeasurementCache(this)\n      let lineNo = this.display.viewFrom\n      this.doc.iter(lineNo, this.display.viewTo, line => {\n        if (line.widgets) for (let i = 0; i < line.widgets.length; i++)\n          if (line.widgets[i].noHScroll) { regLineChange(this, lineNo, \"widget\"); break }\n        ++lineNo\n      })\n      this.curOp.forceUpdate = true\n      signal(this, \"refresh\", this)\n    }),\n\n    operation: function(f){return runInOp(this, f)},\n    startOperation: function(){return startOperation(this)},\n    endOperation: function(){return endOperation(this)},\n\n    refresh: methodOp(function() {\n      let oldHeight = this.display.cachedTextHeight\n      regChange(this)\n      this.curOp.forceUpdate = true\n      clearCaches(this)\n      scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop)\n      updateGutterSpace(this.display)\n      if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping)\n        estimateLineHeights(this)\n      signal(this, \"refresh\", this)\n    }),\n\n    swapDoc: methodOp(function(doc) {\n      let old = this.doc\n      old.cm = null\n      // Cancel the current text selection if any (#5821)\n      if (this.state.selectingText) this.state.selectingText()\n      attachDoc(this, doc)\n      clearCaches(this)\n      this.display.input.reset()\n      scrollToCoords(this, doc.scrollLeft, doc.scrollTop)\n      this.curOp.forceScroll = true\n      signalLater(this, \"swapDoc\", this, old)\n      return old\n    }),\n\n    phrase: function(phraseText) {\n      let phrases = this.options.phrases\n      return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText\n    },\n\n    getInputField: function(){return this.display.input.getField()},\n    getWrapperElement: function(){return this.display.wrapper},\n    getScrollerElement: function(){return this.display.scroller},\n    getGutterElement: function(){return this.display.gutters}\n  }\n  eventMixin(CodeMirror)\n\n  CodeMirror.registerHelper = function(type, name, value) {\n    if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []}\n    helpers[type][name] = value\n  }\n  CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {\n    CodeMirror.registerHelper(type, name, value)\n    helpers[type]._global.push({pred: predicate, val: value})\n  }\n}\n\n// Used for horizontal relative motion. Dir is -1 or 1 (left or\n// right), unit can be \"codepoint\", \"char\", \"column\" (like char, but\n// doesn't cross line boundaries), \"word\" (across next word), or\n// \"group\" (to the start of next group of word or\n// non-word-non-whitespace chars). The visually param controls\n// whether, in right-to-left text, direction 1 means to move towards\n// the next index in the string, or towards the character to the right\n// of the current position. The resulting position will have a\n// hitSide=true property if it reached the end of the document.\nfunction findPosH(doc, pos, dir, unit, visually) {\n  let oldPos = pos\n  let origDir = dir\n  let lineObj = getLine(doc, pos.line)\n  let lineDir = visually && doc.direction == \"rtl\" ? -dir : dir\n  function findNextLine() {\n    let l = pos.line + lineDir\n    if (l < doc.first || l >= doc.first + doc.size) return false\n    pos = new Pos(l, pos.ch, pos.sticky)\n    return lineObj = getLine(doc, l)\n  }\n  function moveOnce(boundToLine) {\n    let next\n    if (unit == \"codepoint\") {\n      let ch = lineObj.text.charCodeAt(pos.ch + (dir > 0 ? 0 : -1))\n      if (isNaN(ch)) {\n        next = null\n      } else {\n        let astral = dir > 0 ? ch >= 0xD800 && ch < 0xDC00 : ch >= 0xDC00 && ch < 0xDFFF\n        next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (astral ? 2 : 1))), -dir)\n      }\n    } else if (visually) {\n      next = moveVisually(doc.cm, lineObj, pos, dir)\n    } else {\n      next = moveLogically(lineObj, pos, dir)\n    }\n    if (next == null) {\n      if (!boundToLine && findNextLine())\n        pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir)\n      else\n        return false\n    } else {\n      pos = next\n    }\n    return true\n  }\n\n  if (unit == \"char\" || unit == \"codepoint\") {\n    moveOnce()\n  } else if (unit == \"column\") {\n    moveOnce(true)\n  } else if (unit == \"word\" || unit == \"group\") {\n    let sawType = null, group = unit == \"group\"\n    let helper = doc.cm && doc.cm.getHelper(pos, \"wordChars\")\n    for (let first = true;; first = false) {\n      if (dir < 0 && !moveOnce(!first)) break\n      let cur = lineObj.text.charAt(pos.ch) || \"\\n\"\n      let type = isWordChar(cur, helper) ? \"w\"\n        : group && cur == \"\\n\" ? \"n\"\n        : !group || /\\s/.test(cur) ? null\n        : \"p\"\n      if (group && !first && !type) type = \"s\"\n      if (sawType && sawType != type) {\n        if (dir < 0) {dir = 1; moveOnce(); pos.sticky = \"after\"}\n        break\n      }\n\n      if (type) sawType = type\n      if (dir > 0 && !moveOnce(!first)) break\n    }\n  }\n  let result = skipAtomic(doc, pos, oldPos, origDir, true)\n  if (equalCursorPos(oldPos, result)) result.hitSide = true\n  return result\n}\n\n// For relative vertical movement. Dir may be -1 or 1. Unit can be\n// \"page\" or \"line\". The resulting position will have a hitSide=true\n// property if it reached the end of the document.\nfunction findPosV(cm, pos, dir, unit) {\n  let doc = cm.doc, x = pos.left, y\n  if (unit == \"page\") {\n    let pageSize = Math.min(cm.display.wrapper.clientHeight, win(cm).innerHeight || doc(cm).documentElement.clientHeight)\n    let moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3)\n    y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount\n\n  } else if (unit == \"line\") {\n    y = dir > 0 ? pos.bottom + 3 : pos.top - 3\n  }\n  let target\n  for (;;) {\n    target = coordsChar(cm, x, y)\n    if (!target.outside) break\n    if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }\n    y += dir * 5\n  }\n  return target\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/mouse_events.js",
    "content": "import { delayBlurEvent, ensureFocus } from \"../display/focus.js\"\nimport { operation } from \"../display/operations.js\"\nimport { visibleLines } from \"../display/update_lines.js\"\nimport { clipPos, cmp, maxPos, minPos, Pos } from \"../line/pos.js\"\nimport { getLine, lineAtHeight } from \"../line/utils_line.js\"\nimport { posFromMouse } from \"../measurement/position_measurement.js\"\nimport { eventInWidget } from \"../measurement/widgets.js\"\nimport { normalizeSelection, Range, Selection } from \"../model/selection.js\"\nimport { extendRange, extendSelection, replaceOneSelection, setSelection } from \"../model/selection_updates.js\"\nimport { captureRightClick, chromeOS, ie, ie_version, mac, webkit, safari } from \"../util/browser.js\"\nimport { getOrder, getBidiPartAt } from \"../util/bidi.js\"\nimport { activeElt, root, win } from \"../util/dom.js\"\nimport { e_button, e_defaultPrevented, e_preventDefault, e_target, hasHandler, off, on, signal, signalDOMEvent } from \"../util/event.js\"\nimport { dragAndDrop } from \"../util/feature_detection.js\"\nimport { bind, countColumn, findColumn, sel_mouse } from \"../util/misc.js\"\nimport { addModifierNames } from \"../input/keymap.js\"\nimport { Pass } from \"../util/misc.js\"\n\nimport { dispatchKey } from \"./key_events.js\"\nimport { commands } from \"./commands.js\"\n\nconst DOUBLECLICK_DELAY = 400\n\nclass PastClick {\n  constructor(time, pos, button) {\n    this.time = time\n    this.pos = pos\n    this.button = button\n  }\n\n  compare(time, pos, button) {\n    return this.time + DOUBLECLICK_DELAY > time &&\n      cmp(pos, this.pos) == 0 && button == this.button\n  }\n}\n\nlet lastClick, lastDoubleClick\nfunction clickRepeat(pos, button) {\n  let now = +new Date\n  if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {\n    lastClick = lastDoubleClick = null\n    return \"triple\"\n  } else if (lastClick && lastClick.compare(now, pos, button)) {\n    lastDoubleClick = new PastClick(now, pos, button)\n    lastClick = null\n    return \"double\"\n  } else {\n    lastClick = new PastClick(now, pos, button)\n    lastDoubleClick = null\n    return \"single\"\n  }\n}\n\n// A mouse down can be a single click, double click, triple click,\n// start of selection drag, start of text drag, new cursor\n// (ctrl-click), rectangle drag (alt-drag), or xwin\n// middle-click-paste. Or it might be a click on something we should\n// not interfere with, such as a scrollbar or widget.\nexport function onMouseDown(e) {\n  let cm = this, display = cm.display\n  if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return\n  display.input.ensurePolled()\n  display.shift = e.shiftKey\n\n  if (eventInWidget(display, e)) {\n    if (!webkit) {\n      // Briefly turn off draggability, to allow widgets to do\n      // normal dragging things.\n      display.scroller.draggable = false\n      setTimeout(() => display.scroller.draggable = true, 100)\n    }\n    return\n  }\n  if (clickInGutter(cm, e)) return\n  let pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : \"single\"\n  win(cm).focus()\n\n  // #3261: make sure, that we're not starting a second selection\n  if (button == 1 && cm.state.selectingText)\n    cm.state.selectingText(e)\n\n  if (pos && handleMappedButton(cm, button, pos, repeat, e)) return\n\n  if (button == 1) {\n    if (pos) leftButtonDown(cm, pos, repeat, e)\n    else if (e_target(e) == display.scroller) e_preventDefault(e)\n  } else if (button == 2) {\n    if (pos) extendSelection(cm.doc, pos)\n    setTimeout(() => display.input.focus(), 20)\n  } else if (button == 3) {\n    if (captureRightClick) cm.display.input.onContextMenu(e)\n    else delayBlurEvent(cm)\n  }\n}\n\nfunction handleMappedButton(cm, button, pos, repeat, event) {\n  let name = \"Click\"\n  if (repeat == \"double\") name = \"Double\" + name\n  else if (repeat == \"triple\") name = \"Triple\" + name\n  name = (button == 1 ? \"Left\" : button == 2 ? \"Middle\" : \"Right\") + name\n\n  return dispatchKey(cm,  addModifierNames(name, event), event, bound => {\n    if (typeof bound == \"string\") bound = commands[bound]\n    if (!bound) return false\n    let done = false\n    try {\n      if (cm.isReadOnly()) cm.state.suppressEdits = true\n      done = bound(cm, pos) != Pass\n    } finally {\n      cm.state.suppressEdits = false\n    }\n    return done\n  })\n}\n\nfunction configureMouse(cm, repeat, event) {\n  let option = cm.getOption(\"configureMouse\")\n  let value = option ? option(cm, repeat, event) : {}\n  if (value.unit == null) {\n    let rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey\n    value.unit = rect ? \"rectangle\" : repeat == \"single\" ? \"char\" : repeat == \"double\" ? \"word\" : \"line\"\n  }\n  if (value.extend == null || cm.doc.extend) value.extend = cm.doc.extend || event.shiftKey\n  if (value.addNew == null) value.addNew = mac ? event.metaKey : event.ctrlKey\n  if (value.moveOnDrag == null) value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey)\n  return value\n}\n\nfunction leftButtonDown(cm, pos, repeat, event) {\n  if (ie) setTimeout(bind(ensureFocus, cm), 0)\n  else cm.curOp.focus = activeElt(root(cm))\n\n  let behavior = configureMouse(cm, repeat, event)\n\n  let sel = cm.doc.sel, contained\n  if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&\n      repeat == \"single\" && (contained = sel.contains(pos)) > -1 &&\n      (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&\n      (cmp(contained.to(), pos) > 0 || pos.xRel < 0))\n    leftButtonStartDrag(cm, event, pos, behavior)\n  else\n    leftButtonSelect(cm, event, pos, behavior)\n}\n\n// Start a text drag. When it ends, see if any dragging actually\n// happen, and treat as a click if it didn't.\nfunction leftButtonStartDrag(cm, event, pos, behavior) {\n  let display = cm.display, moved = false\n  let dragEnd = operation(cm, e => {\n    if (webkit) display.scroller.draggable = false\n    cm.state.draggingText = false\n    if (cm.state.delayingBlurEvent) {\n      if (cm.hasFocus()) cm.state.delayingBlurEvent = false\n      else delayBlurEvent(cm)\n    }\n    off(display.wrapper.ownerDocument, \"mouseup\", dragEnd)\n    off(display.wrapper.ownerDocument, \"mousemove\", mouseMove)\n    off(display.scroller, \"dragstart\", dragStart)\n    off(display.scroller, \"drop\", dragEnd)\n    if (!moved) {\n      e_preventDefault(e)\n      if (!behavior.addNew)\n        extendSelection(cm.doc, pos, null, null, behavior.extend)\n      // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)\n      if ((webkit && !safari) || ie && ie_version == 9)\n        setTimeout(() => {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus()}, 20)\n      else\n        display.input.focus()\n    }\n  })\n  let mouseMove = function(e2) {\n    moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10\n  }\n  let dragStart = () => moved = true\n  // Let the drag handler handle this.\n  if (webkit) display.scroller.draggable = true\n  cm.state.draggingText = dragEnd\n  dragEnd.copy = !behavior.moveOnDrag\n  on(display.wrapper.ownerDocument, \"mouseup\", dragEnd)\n  on(display.wrapper.ownerDocument, \"mousemove\", mouseMove)\n  on(display.scroller, \"dragstart\", dragStart)\n  on(display.scroller, \"drop\", dragEnd)\n\n  cm.state.delayingBlurEvent = true\n  setTimeout(() => display.input.focus(), 20)\n  // IE's approach to draggable\n  if (display.scroller.dragDrop) display.scroller.dragDrop()\n}\n\nfunction rangeForUnit(cm, pos, unit) {\n  if (unit == \"char\") return new Range(pos, pos)\n  if (unit == \"word\") return cm.findWordAt(pos)\n  if (unit == \"line\") return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)))\n  let result = unit(cm, pos)\n  return new Range(result.from, result.to)\n}\n\n// Normal selection, as opposed to text dragging.\nfunction leftButtonSelect(cm, event, start, behavior) {\n  if (ie) delayBlurEvent(cm)\n  let display = cm.display, doc = cm.doc\n  e_preventDefault(event)\n\n  let ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges\n  if (behavior.addNew && !behavior.extend) {\n    ourIndex = doc.sel.contains(start)\n    if (ourIndex > -1)\n      ourRange = ranges[ourIndex]\n    else\n      ourRange = new Range(start, start)\n  } else {\n    ourRange = doc.sel.primary()\n    ourIndex = doc.sel.primIndex\n  }\n\n  if (behavior.unit == \"rectangle\") {\n    if (!behavior.addNew) ourRange = new Range(start, start)\n    start = posFromMouse(cm, event, true, true)\n    ourIndex = -1\n  } else {\n    let range = rangeForUnit(cm, start, behavior.unit)\n    if (behavior.extend)\n      ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend)\n    else\n      ourRange = range\n  }\n\n  if (!behavior.addNew) {\n    ourIndex = 0\n    setSelection(doc, new Selection([ourRange], 0), sel_mouse)\n    startSel = doc.sel\n  } else if (ourIndex == -1) {\n    ourIndex = ranges.length\n    setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),\n                 {scroll: false, origin: \"*mouse\"})\n  } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == \"char\" && !behavior.extend) {\n    setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),\n                 {scroll: false, origin: \"*mouse\"})\n    startSel = doc.sel\n  } else {\n    replaceOneSelection(doc, ourIndex, ourRange, sel_mouse)\n  }\n\n  let lastPos = start\n  function extendTo(pos) {\n    if (cmp(lastPos, pos) == 0) return\n    lastPos = pos\n\n    if (behavior.unit == \"rectangle\") {\n      let ranges = [], tabSize = cm.options.tabSize\n      let startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize)\n      let posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize)\n      let left = Math.min(startCol, posCol), right = Math.max(startCol, posCol)\n      for (let line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));\n           line <= end; line++) {\n        let text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize)\n        if (left == right)\n          ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)))\n        else if (text.length > leftPos)\n          ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))))\n      }\n      if (!ranges.length) ranges.push(new Range(start, start))\n      setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),\n                   {origin: \"*mouse\", scroll: false})\n      cm.scrollIntoView(pos)\n    } else {\n      let oldRange = ourRange\n      let range = rangeForUnit(cm, pos, behavior.unit)\n      let anchor = oldRange.anchor, head\n      if (cmp(range.anchor, anchor) > 0) {\n        head = range.head\n        anchor = minPos(oldRange.from(), range.anchor)\n      } else {\n        head = range.anchor\n        anchor = maxPos(oldRange.to(), range.head)\n      }\n      let ranges = startSel.ranges.slice(0)\n      ranges[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head))\n      setSelection(doc, normalizeSelection(cm, ranges, ourIndex), sel_mouse)\n    }\n  }\n\n  let editorSize = display.wrapper.getBoundingClientRect()\n  // Used to ensure timeout re-tries don't fire when another extend\n  // happened in the meantime (clearTimeout isn't reliable -- at\n  // least on Chrome, the timeouts still happen even when cleared,\n  // if the clear happens after their scheduled firing time).\n  let counter = 0\n\n  function extend(e) {\n    let curCount = ++counter\n    let cur = posFromMouse(cm, e, true, behavior.unit == \"rectangle\")\n    if (!cur) return\n    if (cmp(cur, lastPos) != 0) {\n      cm.curOp.focus = activeElt(root(cm))\n      extendTo(cur)\n      let visible = visibleLines(display, doc)\n      if (cur.line >= visible.to || cur.line < visible.from)\n        setTimeout(operation(cm, () => {if (counter == curCount) extend(e)}), 150)\n    } else {\n      let outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0\n      if (outside) setTimeout(operation(cm, () => {\n        if (counter != curCount) return\n        display.scroller.scrollTop += outside\n        extend(e)\n      }), 50)\n    }\n  }\n\n  function done(e) {\n    cm.state.selectingText = false\n    counter = Infinity\n    // If e is null or undefined we interpret this as someone trying\n    // to explicitly cancel the selection rather than the user\n    // letting go of the mouse button.\n    if (e) {\n      e_preventDefault(e)\n      display.input.focus()\n    }\n    off(display.wrapper.ownerDocument, \"mousemove\", move)\n    off(display.wrapper.ownerDocument, \"mouseup\", up)\n    doc.history.lastSelOrigin = null\n  }\n\n  let move = operation(cm, e => {\n    if (e.buttons === 0 || !e_button(e)) done(e)\n    else extend(e)\n  })\n  let up = operation(cm, done)\n  cm.state.selectingText = up\n  on(display.wrapper.ownerDocument, \"mousemove\", move)\n  on(display.wrapper.ownerDocument, \"mouseup\", up)\n}\n\n// Used when mouse-selecting to adjust the anchor to the proper side\n// of a bidi jump depending on the visual position of the head.\nfunction bidiSimplify(cm, range) {\n  let {anchor, head} = range, anchorLine = getLine(cm.doc, anchor.line)\n  if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) return range\n  let order = getOrder(anchorLine)\n  if (!order) return range\n  let index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]\n  if (part.from != anchor.ch && part.to != anchor.ch) return range\n  let boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1)\n  if (boundary == 0 || boundary == order.length) return range\n\n  // Compute the relative visual position of the head compared to the\n  // anchor (<0 is to the left, >0 to the right)\n  let leftSide\n  if (head.line != anchor.line) {\n    leftSide = (head.line - anchor.line) * (cm.doc.direction == \"ltr\" ? 1 : -1) > 0\n  } else {\n    let headIndex = getBidiPartAt(order, head.ch, head.sticky)\n    let dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)\n    if (headIndex == boundary - 1 || headIndex == boundary)\n      leftSide = dir < 0\n    else\n      leftSide = dir > 0\n  }\n\n  let usePart = order[boundary + (leftSide ? -1 : 0)]\n  let from = leftSide == (usePart.level == 1)\n  let ch = from ? usePart.from : usePart.to, sticky = from ? \"after\" : \"before\"\n  return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)\n}\n\n\n// Determines whether an event happened in the gutter, and fires the\n// handlers for the corresponding event.\nfunction gutterEvent(cm, e, type, prevent) {\n  let mX, mY\n  if (e.touches) {\n    mX = e.touches[0].clientX\n    mY = e.touches[0].clientY\n  } else {\n    try { mX = e.clientX; mY = e.clientY }\n    catch(e) { return false }\n  }\n  if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false\n  if (prevent) e_preventDefault(e)\n\n  let display = cm.display\n  let lineBox = display.lineDiv.getBoundingClientRect()\n\n  if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e)\n  mY -= lineBox.top - display.viewOffset\n\n  for (let i = 0; i < cm.display.gutterSpecs.length; ++i) {\n    let g = display.gutters.childNodes[i]\n    if (g && g.getBoundingClientRect().right >= mX) {\n      let line = lineAtHeight(cm.doc, mY)\n      let gutter = cm.display.gutterSpecs[i]\n      signal(cm, type, cm, line, gutter.className, e)\n      return e_defaultPrevented(e)\n    }\n  }\n}\n\nexport function clickInGutter(cm, e) {\n  return gutterEvent(cm, e, \"gutterClick\", true)\n}\n\n// CONTEXT MENU HANDLING\n\n// To make the context menu work, we need to briefly unhide the\n// textarea (making it as unobtrusive as possible) to let the\n// right-click take effect on it.\nexport function onContextMenu(cm, e) {\n  if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return\n  if (signalDOMEvent(cm, e, \"contextmenu\")) return\n  if (!captureRightClick) cm.display.input.onContextMenu(e)\n}\n\nfunction contextMenuInGutter(cm, e) {\n  if (!hasHandler(cm, \"gutterContextMenu\")) return false\n  return gutterEvent(cm, e, \"gutterContextMenu\", false)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/options.js",
    "content": "import { onBlur } from \"../display/focus.js\"\nimport { getGutters, updateGutters } from \"../display/gutters.js\"\nimport { loadMode, resetModeState } from \"../display/mode_state.js\"\nimport { initScrollbars, updateScrollbars } from \"../display/scrollbars.js\"\nimport { updateSelection } from \"../display/selection.js\"\nimport { regChange } from \"../display/view_tracking.js\"\nimport { getKeyMap } from \"../input/keymap.js\"\nimport { defaultSpecialCharPlaceholder } from \"../line/line_data.js\"\nimport { Pos } from \"../line/pos.js\"\nimport { findMaxLine } from \"../line/spans.js\"\nimport { clearCaches, compensateForHScroll, estimateLineHeights } from \"../measurement/position_measurement.js\"\nimport { replaceRange } from \"../model/changes.js\"\nimport { mobile, windows } from \"../util/browser.js\"\nimport { addClass, rmClass } from \"../util/dom.js\"\nimport { off, on } from \"../util/event.js\"\n\nimport { themeChanged } from \"./utils.js\"\n\nexport let Init = {toString: function(){return \"CodeMirror.Init\"}}\n\nexport let defaults = {}\nexport let optionHandlers = {}\n\nexport function defineOptions(CodeMirror) {\n  let optionHandlers = CodeMirror.optionHandlers\n\n  function option(name, deflt, handle, notOnInit) {\n    CodeMirror.defaults[name] = deflt\n    if (handle) optionHandlers[name] =\n      notOnInit ? (cm, val, old) => {if (old != Init) handle(cm, val, old)} : handle\n  }\n\n  CodeMirror.defineOption = option\n\n  // Passed to option handlers when there is no old value.\n  CodeMirror.Init = Init\n\n  // These two are, on init, called from the constructor because they\n  // have to be initialized before the editor can start at all.\n  option(\"value\", \"\", (cm, val) => cm.setValue(val), true)\n  option(\"mode\", null, (cm, val) => {\n    cm.doc.modeOption = val\n    loadMode(cm)\n  }, true)\n\n  option(\"indentUnit\", 2, loadMode, true)\n  option(\"indentWithTabs\", false)\n  option(\"smartIndent\", true)\n  option(\"tabSize\", 4, cm => {\n    resetModeState(cm)\n    clearCaches(cm)\n    regChange(cm)\n  }, true)\n\n  option(\"lineSeparator\", null, (cm, val) => {\n    cm.doc.lineSep = val\n    if (!val) return\n    let newBreaks = [], lineNo = cm.doc.first\n    cm.doc.iter(line => {\n      for (let pos = 0;;) {\n        let found = line.text.indexOf(val, pos)\n        if (found == -1) break\n        pos = found + val.length\n        newBreaks.push(Pos(lineNo, found))\n      }\n      lineNo++\n    })\n    for (let i = newBreaks.length - 1; i >= 0; i--)\n      replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length))\n  })\n  option(\"specialChars\", /[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u061c\\u200b\\u200e\\u200f\\u2028\\u2029\\u202d\\u202e\\u2066\\u2067\\u2069\\ufeff\\ufff9-\\ufffc]/g, (cm, val, old) => {\n    cm.state.specialChars = new RegExp(val.source + (val.test(\"\\t\") ? \"\" : \"|\\t\"), \"g\")\n    if (old != Init) cm.refresh()\n  })\n  option(\"specialCharPlaceholder\", defaultSpecialCharPlaceholder, cm => cm.refresh(), true)\n  option(\"electricChars\", true)\n  option(\"inputStyle\", mobile ? \"contenteditable\" : \"textarea\", () => {\n    throw new Error(\"inputStyle can not (yet) be changed in a running editor\") // FIXME\n  }, true)\n  option(\"spellcheck\", false, (cm, val) => cm.getInputField().spellcheck = val, true)\n  option(\"autocorrect\", false, (cm, val) => cm.getInputField().autocorrect = val, true)\n  option(\"autocapitalize\", false, (cm, val) => cm.getInputField().autocapitalize = val, true)\n  option(\"rtlMoveVisually\", !windows)\n  option(\"wholeLineUpdateBefore\", true)\n\n  option(\"theme\", \"default\", cm => {\n    themeChanged(cm)\n    updateGutters(cm)\n  }, true)\n  option(\"keyMap\", \"default\", (cm, val, old) => {\n    let next = getKeyMap(val)\n    let prev = old != Init && getKeyMap(old)\n    if (prev && prev.detach) prev.detach(cm, next)\n    if (next.attach) next.attach(cm, prev || null)\n  })\n  option(\"extraKeys\", null)\n  option(\"configureMouse\", null)\n\n  option(\"lineWrapping\", false, wrappingChanged, true)\n  option(\"gutters\", [], (cm, val) => {\n    cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers)\n    updateGutters(cm)\n  }, true)\n  option(\"fixedGutter\", true, (cm, val) => {\n    cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + \"px\" : \"0\"\n    cm.refresh()\n  }, true)\n  option(\"coverGutterNextToScrollbar\", false, cm => updateScrollbars(cm), true)\n  option(\"scrollbarStyle\", \"native\", cm => {\n    initScrollbars(cm)\n    updateScrollbars(cm)\n    cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)\n    cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)\n  }, true)\n  option(\"lineNumbers\", false, (cm, val) => {\n    cm.display.gutterSpecs = getGutters(cm.options.gutters, val)\n    updateGutters(cm)\n  }, true)\n  option(\"firstLineNumber\", 1, updateGutters, true)\n  option(\"lineNumberFormatter\", integer => integer, updateGutters, true)\n  option(\"showCursorWhenSelecting\", false, updateSelection, true)\n\n  option(\"resetSelectionOnContextMenu\", true)\n  option(\"lineWiseCopyCut\", true)\n  option(\"pasteLinesPerSelection\", true)\n  option(\"selectionsMayTouch\", false)\n\n  option(\"readOnly\", false, (cm, val) => {\n    if (val == \"nocursor\") {\n      onBlur(cm)\n      cm.display.input.blur()\n    }\n    cm.display.input.readOnlyChanged(val)\n  })\n\n  option(\"screenReaderLabel\", null, (cm, val) => {\n    val = (val === '') ? null : val\n    cm.display.input.screenReaderLabelChanged(val)\n  })\n\n  option(\"disableInput\", false, (cm, val) => {if (!val) cm.display.input.reset()}, true)\n  option(\"dragDrop\", true, dragDropChanged)\n  option(\"allowDropFileTypes\", null)\n\n  option(\"cursorBlinkRate\", 530)\n  option(\"cursorScrollMargin\", 0)\n  option(\"cursorHeight\", 1, updateSelection, true)\n  option(\"singleCursorHeightPerLine\", true, updateSelection, true)\n  option(\"workTime\", 100)\n  option(\"workDelay\", 100)\n  option(\"flattenSpans\", true, resetModeState, true)\n  option(\"addModeClass\", false, resetModeState, true)\n  option(\"pollInterval\", 100)\n  option(\"undoDepth\", 200, (cm, val) => cm.doc.history.undoDepth = val)\n  option(\"historyEventDelay\", 1250)\n  option(\"viewportMargin\", 10, cm => cm.refresh(), true)\n  option(\"maxHighlightLength\", 10000, resetModeState, true)\n  option(\"moveInputWithCursor\", true, (cm, val) => {\n    if (!val) cm.display.input.resetPosition()\n  })\n\n  option(\"tabindex\", null, (cm, val) => cm.display.input.getField().tabIndex = val || \"\")\n  option(\"autofocus\", null)\n  option(\"direction\", \"ltr\", (cm, val) => cm.doc.setDirection(val), true)\n  option(\"phrases\", null)\n}\n\nfunction dragDropChanged(cm, value, old) {\n  let wasOn = old && old != Init\n  if (!value != !wasOn) {\n    let funcs = cm.display.dragFunctions\n    let toggle = value ? on : off\n    toggle(cm.display.scroller, \"dragstart\", funcs.start)\n    toggle(cm.display.scroller, \"dragenter\", funcs.enter)\n    toggle(cm.display.scroller, \"dragover\", funcs.over)\n    toggle(cm.display.scroller, \"dragleave\", funcs.leave)\n    toggle(cm.display.scroller, \"drop\", funcs.drop)\n  }\n}\n\nfunction wrappingChanged(cm) {\n  if (cm.options.lineWrapping) {\n    addClass(cm.display.wrapper, \"CodeMirror-wrap\")\n    cm.display.sizer.style.minWidth = \"\"\n    cm.display.sizerWidth = null\n  } else {\n    rmClass(cm.display.wrapper, \"CodeMirror-wrap\")\n    findMaxLine(cm)\n  }\n  estimateLineHeights(cm)\n  regChange(cm)\n  clearCaches(cm)\n  setTimeout(() => updateScrollbars(cm), 100)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/edit/utils.js",
    "content": "import { clearCaches } from \"../measurement/position_measurement.js\"\n\nexport function themeChanged(cm) {\n  cm.display.wrapper.className = cm.display.wrapper.className.replace(/\\s*cm-s-\\S+/g, \"\") +\n    cm.options.theme.replace(/(^|\\s)\\s*/g, \" cm-s-\")\n  clearCaches(cm)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/input/ContentEditableInput.js",
    "content": "import { operation, runInOp } from \"../display/operations.js\"\nimport { prepareSelection } from \"../display/selection.js\"\nimport { regChange } from \"../display/view_tracking.js\"\nimport { applyTextInput, copyableRanges, disableBrowserMagic, handlePaste, hiddenTextarea, lastCopied, setLastCopied } from \"./input.js\"\nimport { cmp, maxPos, minPos, Pos } from \"../line/pos.js\"\nimport { getBetween, getLine, lineNo } from \"../line/utils_line.js\"\nimport { findViewForLine, findViewIndex, mapFromLineView, nodeAndOffsetInLineMap } from \"../measurement/position_measurement.js\"\nimport { replaceRange } from \"../model/changes.js\"\nimport { simpleSelection } from \"../model/selection.js\"\nimport { setSelection } from \"../model/selection_updates.js\"\nimport { getBidiPartAt, getOrder } from \"../util/bidi.js\"\nimport { android, chrome, gecko, ie_version } from \"../util/browser.js\"\nimport { activeElt, contains, range, removeChildrenAndAdd, selectInput, rootNode } from \"../util/dom.js\"\nimport { on, signalDOMEvent } from \"../util/event.js\"\nimport { Delayed, lst, sel_dontScroll } from \"../util/misc.js\"\n\n// CONTENTEDITABLE INPUT STYLE\n\nexport default class ContentEditableInput {\n  constructor(cm) {\n    this.cm = cm\n    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null\n    this.polling = new Delayed()\n    this.composing = null\n    this.gracePeriod = false\n    this.readDOMTimeout = null\n  }\n\n  init(display) {\n    let input = this, cm = input.cm\n    let div = input.div = display.lineDiv\n    div.contentEditable = true\n    disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize)\n\n    function belongsToInput(e) {\n      for (let t = e.target; t; t = t.parentNode) {\n        if (t == div) return true\n        if (/\\bCodeMirror-(?:line)?widget\\b/.test(t.className)) break\n      }\n      return false\n    }\n\n    on(div, \"paste\", e => {\n      if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) return\n      // IE doesn't fire input events, so we schedule a read for the pasted content in this way\n      if (ie_version <= 11) setTimeout(operation(cm, () => this.updateFromDOM()), 20)\n    })\n\n    on(div, \"compositionstart\", e => {\n      this.composing = {data: e.data, done: false}\n    })\n    on(div, \"compositionupdate\", e => {\n      if (!this.composing) this.composing = {data: e.data, done: false}\n    })\n    on(div, \"compositionend\", e => {\n      if (this.composing) {\n        if (e.data != this.composing.data) this.readFromDOMSoon()\n        this.composing.done = true\n      }\n    })\n\n    on(div, \"touchstart\", () => input.forceCompositionEnd())\n\n    on(div, \"input\", () => {\n      if (!this.composing) this.readFromDOMSoon()\n    })\n\n    function onCopyCut(e) {\n      if (!belongsToInput(e) || signalDOMEvent(cm, e)) return\n      if (cm.somethingSelected()) {\n        setLastCopied({lineWise: false, text: cm.getSelections()})\n        if (e.type == \"cut\") cm.replaceSelection(\"\", null, \"cut\")\n      } else if (!cm.options.lineWiseCopyCut) {\n        return\n      } else {\n        let ranges = copyableRanges(cm)\n        setLastCopied({lineWise: true, text: ranges.text})\n        if (e.type == \"cut\") {\n          cm.operation(() => {\n            cm.setSelections(ranges.ranges, 0, sel_dontScroll)\n            cm.replaceSelection(\"\", null, \"cut\")\n          })\n        }\n      }\n      if (e.clipboardData) {\n        e.clipboardData.clearData()\n        let content = lastCopied.text.join(\"\\n\")\n        // iOS exposes the clipboard API, but seems to discard content inserted into it\n        e.clipboardData.setData(\"Text\", content)\n        if (e.clipboardData.getData(\"Text\") == content) {\n          e.preventDefault()\n          return\n        }\n      }\n      // Old-fashioned briefly-focus-a-textarea hack\n      let kludge = hiddenTextarea(), te = kludge.firstChild\n      disableBrowserMagic(te)\n      cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)\n      te.value = lastCopied.text.join(\"\\n\")\n      let hadFocus = activeElt(rootNode(div))\n      selectInput(te)\n      setTimeout(() => {\n        cm.display.lineSpace.removeChild(kludge)\n        hadFocus.focus()\n        if (hadFocus == div) input.showPrimarySelection()\n      }, 50)\n    }\n    on(div, \"copy\", onCopyCut)\n    on(div, \"cut\", onCopyCut)\n  }\n\n  screenReaderLabelChanged(label) {\n    // Label for screenreaders, accessibility\n    if(label) {\n      this.div.setAttribute('aria-label', label)\n    } else {\n      this.div.removeAttribute('aria-label')\n    }\n  }\n\n  prepareSelection() {\n    let result = prepareSelection(this.cm, false)\n    result.focus = activeElt(rootNode(this.div)) == this.div\n    return result\n  }\n\n  showSelection(info, takeFocus) {\n    if (!info || !this.cm.display.view.length) return\n    if (info.focus || takeFocus) this.showPrimarySelection()\n    this.showMultipleSelections(info)\n  }\n\n  getSelection() {\n    return this.cm.display.wrapper.ownerDocument.getSelection()\n  }\n\n  showPrimarySelection() {\n    let sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary()\n    let from = prim.from(), to = prim.to()\n\n    if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {\n      sel.removeAllRanges()\n      return\n    }\n\n    let curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)\n    let curFocus = domToPos(cm, sel.focusNode, sel.focusOffset)\n    if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&\n        cmp(minPos(curAnchor, curFocus), from) == 0 &&\n        cmp(maxPos(curAnchor, curFocus), to) == 0)\n      return\n\n    let view = cm.display.view\n    let start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||\n        {node: view[0].measure.map[2], offset: 0}\n    let end = to.line < cm.display.viewTo && posToDOM(cm, to)\n    if (!end) {\n      let measure = view[view.length - 1].measure\n      let map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map\n      end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}\n    }\n\n    if (!start || !end) {\n      sel.removeAllRanges()\n      return\n    }\n\n    let old = sel.rangeCount && sel.getRangeAt(0), rng\n    try { rng = range(start.node, start.offset, end.offset, end.node) }\n    catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible\n    if (rng) {\n      if (!gecko && cm.state.focused) {\n        sel.collapse(start.node, start.offset)\n        if (!rng.collapsed) {\n          sel.removeAllRanges()\n          sel.addRange(rng)\n        }\n      } else {\n        sel.removeAllRanges()\n        sel.addRange(rng)\n      }\n      if (old && sel.anchorNode == null) sel.addRange(old)\n      else if (gecko) this.startGracePeriod()\n    }\n    this.rememberSelection()\n  }\n\n  startGracePeriod() {\n    clearTimeout(this.gracePeriod)\n    this.gracePeriod = setTimeout(() => {\n      this.gracePeriod = false\n      if (this.selectionChanged())\n        this.cm.operation(() => this.cm.curOp.selectionChanged = true)\n    }, 20)\n  }\n\n  showMultipleSelections(info) {\n    removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)\n    removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)\n  }\n\n  rememberSelection() {\n    let sel = this.getSelection()\n    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset\n    this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset\n  }\n\n  selectionInEditor() {\n    let sel = this.getSelection()\n    if (!sel.rangeCount) return false\n    let node = sel.getRangeAt(0).commonAncestorContainer\n    return contains(this.div, node)\n  }\n\n  focus() {\n    if (this.cm.options.readOnly != \"nocursor\") {\n      if (!this.selectionInEditor() || activeElt(rootNode(this.div)) != this.div)\n        this.showSelection(this.prepareSelection(), true)\n      this.div.focus()\n    }\n  }\n  blur() { this.div.blur() }\n  getField() { return this.div }\n\n  supportsTouch() { return true }\n\n  receivedFocus() {\n    let input = this\n    if (this.selectionInEditor())\n      setTimeout(() => this.pollSelection(), 20)\n    else\n      runInOp(this.cm, () => input.cm.curOp.selectionChanged = true)\n\n    function poll() {\n      if (input.cm.state.focused) {\n        input.pollSelection()\n        input.polling.set(input.cm.options.pollInterval, poll)\n      }\n    }\n    this.polling.set(this.cm.options.pollInterval, poll)\n  }\n\n  selectionChanged() {\n    let sel = this.getSelection()\n    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||\n      sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset\n  }\n\n  pollSelection() {\n    if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) return\n    let sel = this.getSelection(), cm = this.cm\n    // On Android Chrome (version 56, at least), backspacing into an\n    // uneditable block element will put the cursor in that element,\n    // and then, because it's not editable, hide the virtual keyboard.\n    // Because Android doesn't allow us to actually detect backspace\n    // presses in a sane way, this code checks for when that happens\n    // and simulates a backspace press in this case.\n    if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {\n      this.cm.triggerOnKeyDown({type: \"keydown\", keyCode: 8, preventDefault: Math.abs})\n      this.blur()\n      this.focus()\n      return\n    }\n    if (this.composing) return\n    this.rememberSelection()\n    let anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)\n    let head = domToPos(cm, sel.focusNode, sel.focusOffset)\n    if (anchor && head) runInOp(cm, () => {\n      setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)\n      if (anchor.bad || head.bad) cm.curOp.selectionChanged = true\n    })\n  }\n\n  pollContent() {\n    if (this.readDOMTimeout != null) {\n      clearTimeout(this.readDOMTimeout)\n      this.readDOMTimeout = null\n    }\n\n    let cm = this.cm, display = cm.display, sel = cm.doc.sel.primary()\n    let from = sel.from(), to = sel.to()\n    if (from.ch == 0 && from.line > cm.firstLine())\n      from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length)\n    if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())\n      to = Pos(to.line + 1, 0)\n    if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false\n\n    let fromIndex, fromLine, fromNode\n    if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {\n      fromLine = lineNo(display.view[0].line)\n      fromNode = display.view[0].node\n    } else {\n      fromLine = lineNo(display.view[fromIndex].line)\n      fromNode = display.view[fromIndex - 1].node.nextSibling\n    }\n    let toIndex = findViewIndex(cm, to.line)\n    let toLine, toNode\n    if (toIndex == display.view.length - 1) {\n      toLine = display.viewTo - 1\n      toNode = display.lineDiv.lastChild\n    } else {\n      toLine = lineNo(display.view[toIndex + 1].line) - 1\n      toNode = display.view[toIndex + 1].node.previousSibling\n    }\n\n    if (!fromNode) return false\n    let newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))\n    let oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))\n    while (newText.length > 1 && oldText.length > 1) {\n      if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }\n      else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }\n      else break\n    }\n\n    let cutFront = 0, cutEnd = 0\n    let newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length)\n    while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))\n      ++cutFront\n    let newBot = lst(newText), oldBot = lst(oldText)\n    let maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),\n                             oldBot.length - (oldText.length == 1 ? cutFront : 0))\n    while (cutEnd < maxCutEnd &&\n           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))\n      ++cutEnd\n    // Try to move start of change to start of selection if ambiguous\n    if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {\n      while (cutFront && cutFront > from.ch &&\n             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {\n        cutFront--\n        cutEnd++\n      }\n    }\n\n    newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\\u200b+/, \"\")\n    newText[0] = newText[0].slice(cutFront).replace(/\\u200b+$/, \"\")\n\n    let chFrom = Pos(fromLine, cutFront)\n    let chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)\n    if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {\n      replaceRange(cm.doc, newText, chFrom, chTo, \"+input\")\n      return true\n    }\n  }\n\n  ensurePolled() {\n    this.forceCompositionEnd()\n  }\n  reset() {\n    this.forceCompositionEnd()\n  }\n  forceCompositionEnd() {\n    if (!this.composing) return\n    clearTimeout(this.readDOMTimeout)\n    this.composing = null\n    this.updateFromDOM()\n    this.div.blur()\n    this.div.focus()\n  }\n  readFromDOMSoon() {\n    if (this.readDOMTimeout != null) return\n    this.readDOMTimeout = setTimeout(() => {\n      this.readDOMTimeout = null\n      if (this.composing) {\n        if (this.composing.done) this.composing = null\n        else return\n      }\n      this.updateFromDOM()\n    }, 80)\n  }\n\n  updateFromDOM() {\n    if (this.cm.isReadOnly() || !this.pollContent())\n      runInOp(this.cm, () => regChange(this.cm))\n  }\n\n  setUneditable(node) {\n    node.contentEditable = \"false\"\n  }\n\n  onKeyPress(e) {\n    if (e.charCode == 0 || this.composing) return\n    e.preventDefault()\n    if (!this.cm.isReadOnly())\n      operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0)\n  }\n\n  readOnlyChanged(val) {\n    this.div.contentEditable = String(val != \"nocursor\")\n  }\n\n  onContextMenu() {}\n  resetPosition() {}\n}\n\nContentEditableInput.prototype.needsContentAttribute = true\n\nfunction posToDOM(cm, pos) {\n  let view = findViewForLine(cm, pos.line)\n  if (!view || view.hidden) return null\n  let line = getLine(cm.doc, pos.line)\n  let info = mapFromLineView(view, line, pos.line)\n\n  let order = getOrder(line, cm.doc.direction), side = \"left\"\n  if (order) {\n    let partPos = getBidiPartAt(order, pos.ch)\n    side = partPos % 2 ? \"right\" : \"left\"\n  }\n  let result = nodeAndOffsetInLineMap(info.map, pos.ch, side)\n  result.offset = result.collapse == \"right\" ? result.end : result.start\n  return result\n}\n\nfunction isInGutter(node) {\n  for (let scan = node; scan; scan = scan.parentNode)\n    if (/CodeMirror-gutter-wrapper/.test(scan.className)) return true\n  return false\n}\n\nfunction badPos(pos, bad) { if (bad) pos.bad = true; return pos }\n\nfunction domTextBetween(cm, from, to, fromLine, toLine) {\n  let text = \"\", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false\n  function recognizeMarker(id) { return marker => marker.id == id }\n  function close() {\n    if (closing) {\n      text += lineSep\n      if (extraLinebreak) text += lineSep\n      closing = extraLinebreak = false\n    }\n  }\n  function addText(str) {\n    if (str) {\n      close()\n      text += str\n    }\n  }\n  function walk(node) {\n    if (node.nodeType == 1) {\n      let cmText = node.getAttribute(\"cm-text\")\n      if (cmText) {\n        addText(cmText)\n        return\n      }\n      let markerID = node.getAttribute(\"cm-marker\"), range\n      if (markerID) {\n        let found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID))\n        if (found.length && (range = found[0].find(0)))\n          addText(getBetween(cm.doc, range.from, range.to).join(lineSep))\n        return\n      }\n      if (node.getAttribute(\"contenteditable\") == \"false\") return\n      let isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName)\n      if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) return\n\n      if (isBlock) close()\n      for (let i = 0; i < node.childNodes.length; i++)\n        walk(node.childNodes[i])\n\n      if (/^(pre|p)$/i.test(node.nodeName)) extraLinebreak = true\n      if (isBlock) closing = true\n    } else if (node.nodeType == 3) {\n      addText(node.nodeValue.replace(/\\u200b/g, \"\").replace(/\\u00a0/g, \" \"))\n    }\n  }\n  for (;;) {\n    walk(from)\n    if (from == to) break\n    from = from.nextSibling\n    extraLinebreak = false\n  }\n  return text\n}\n\nfunction domToPos(cm, node, offset) {\n  let lineNode\n  if (node == cm.display.lineDiv) {\n    lineNode = cm.display.lineDiv.childNodes[offset]\n    if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true)\n    node = null; offset = 0\n  } else {\n    for (lineNode = node;; lineNode = lineNode.parentNode) {\n      if (!lineNode || lineNode == cm.display.lineDiv) return null\n      if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break\n    }\n  }\n  for (let i = 0; i < cm.display.view.length; i++) {\n    let lineView = cm.display.view[i]\n    if (lineView.node == lineNode)\n      return locateNodeInLineView(lineView, node, offset)\n  }\n}\n\nfunction locateNodeInLineView(lineView, node, offset) {\n  let wrapper = lineView.text.firstChild, bad = false\n  if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true)\n  if (node == wrapper) {\n    bad = true\n    node = wrapper.childNodes[offset]\n    offset = 0\n    if (!node) {\n      let line = lineView.rest ? lst(lineView.rest) : lineView.line\n      return badPos(Pos(lineNo(line), line.text.length), bad)\n    }\n  }\n\n  let textNode = node.nodeType == 3 ? node : null, topNode = node\n  if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {\n    textNode = node.firstChild\n    if (offset) offset = textNode.nodeValue.length\n  }\n  while (topNode.parentNode != wrapper) topNode = topNode.parentNode\n  let measure = lineView.measure, maps = measure.maps\n\n  function find(textNode, topNode, offset) {\n    for (let i = -1; i < (maps ? maps.length : 0); i++) {\n      let map = i < 0 ? measure.map : maps[i]\n      for (let j = 0; j < map.length; j += 3) {\n        let curNode = map[j + 2]\n        if (curNode == textNode || curNode == topNode) {\n          let line = lineNo(i < 0 ? lineView.line : lineView.rest[i])\n          let ch = map[j] + offset\n          if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)]\n          return Pos(line, ch)\n        }\n      }\n    }\n  }\n  let found = find(textNode, topNode, offset)\n  if (found) return badPos(found, bad)\n\n  // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems\n  for (let after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {\n    found = find(after, after.firstChild, 0)\n    if (found)\n      return badPos(Pos(found.line, found.ch - dist), bad)\n    else\n      dist += after.textContent.length\n  }\n  for (let before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) {\n    found = find(before, before.firstChild, -1)\n    if (found)\n      return badPos(Pos(found.line, found.ch + dist), bad)\n    else\n      dist += before.textContent.length\n  }\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/input/TextareaInput.js",
    "content": "import { operation, runInOp } from \"../display/operations.js\"\nimport { prepareSelection } from \"../display/selection.js\"\nimport { applyTextInput, copyableRanges, handlePaste, hiddenTextarea, disableBrowserMagic, setLastCopied } from \"./input.js\"\nimport { cursorCoords, posFromMouse } from \"../measurement/position_measurement.js\"\nimport { eventInWidget } from \"../measurement/widgets.js\"\nimport { simpleSelection } from \"../model/selection.js\"\nimport { selectAll, setSelection } from \"../model/selection_updates.js\"\nimport { captureRightClick, ie, ie_version, ios, mac, mobile, presto, webkit } from \"../util/browser.js\"\nimport { activeElt, removeChildrenAndAdd, selectInput, rootNode } from \"../util/dom.js\"\nimport { e_preventDefault, e_stop, off, on, signalDOMEvent } from \"../util/event.js\"\nimport { hasSelection } from \"../util/feature_detection.js\"\nimport { Delayed, sel_dontScroll } from \"../util/misc.js\"\n\n// TEXTAREA INPUT STYLE\n\nexport default class TextareaInput {\n  constructor(cm) {\n    this.cm = cm\n    // See input.poll and input.reset\n    this.prevInput = \"\"\n\n    // Flag that indicates whether we expect input to appear real soon\n    // now (after some event like 'keypress' or 'input') and are\n    // polling intensively.\n    this.pollingFast = false\n    // Self-resetting timeout for the poller\n    this.polling = new Delayed()\n    // Used to work around IE issue with selection being forgotten when focus moves away from textarea\n    this.hasSelection = false\n    this.composing = null\n    this.resetting = false\n  }\n\n  init(display) {\n    let input = this, cm = this.cm\n    this.createField(display)\n    const te = this.textarea\n\n    display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild)\n\n    // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)\n    if (ios) te.style.width = \"0px\"\n\n    on(te, \"input\", () => {\n      if (ie && ie_version >= 9 && this.hasSelection) this.hasSelection = null\n      input.poll()\n    })\n\n    on(te, \"paste\", e => {\n      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return\n\n      cm.state.pasteIncoming = +new Date\n      input.fastPoll()\n    })\n\n    function prepareCopyCut(e) {\n      if (signalDOMEvent(cm, e)) return\n      if (cm.somethingSelected()) {\n        setLastCopied({lineWise: false, text: cm.getSelections()})\n      } else if (!cm.options.lineWiseCopyCut) {\n        return\n      } else {\n        let ranges = copyableRanges(cm)\n        setLastCopied({lineWise: true, text: ranges.text})\n        if (e.type == \"cut\") {\n          cm.setSelections(ranges.ranges, null, sel_dontScroll)\n        } else {\n          input.prevInput = \"\"\n          te.value = ranges.text.join(\"\\n\")\n          selectInput(te)\n        }\n      }\n      if (e.type == \"cut\") cm.state.cutIncoming = +new Date\n    }\n    on(te, \"cut\", prepareCopyCut)\n    on(te, \"copy\", prepareCopyCut)\n\n    on(display.scroller, \"paste\", e => {\n      if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return\n      if (!te.dispatchEvent) {\n        cm.state.pasteIncoming = +new Date\n        input.focus()\n        return\n      }\n\n      // Pass the `paste` event to the textarea so it's handled by its event listener.\n      const event = new Event(\"paste\")\n      event.clipboardData = e.clipboardData\n      te.dispatchEvent(event)\n    })\n\n    // Prevent normal selection in the editor (we handle our own)\n    on(display.lineSpace, \"selectstart\", e => {\n      if (!eventInWidget(display, e)) e_preventDefault(e)\n    })\n\n    on(te, \"compositionstart\", () => {\n      let start = cm.getCursor(\"from\")\n      if (input.composing) input.composing.range.clear()\n      input.composing = {\n        start: start,\n        range: cm.markText(start, cm.getCursor(\"to\"), {className: \"CodeMirror-composing\"})\n      }\n    })\n    on(te, \"compositionend\", () => {\n      if (input.composing) {\n        input.poll()\n        input.composing.range.clear()\n        input.composing = null\n      }\n    })\n  }\n\n  createField(_display) {\n    // Wraps and hides input textarea\n    this.wrapper = hiddenTextarea()\n    // The semihidden textarea that is focused when the editor is\n    // focused, and receives input.\n    this.textarea = this.wrapper.firstChild\n    let opts = this.cm.options\n    disableBrowserMagic(this.textarea, opts.spellcheck, opts.autocorrect, opts.autocapitalize)\n  }\n\n  screenReaderLabelChanged(label) {\n    // Label for screenreaders, accessibility\n    if(label) {\n      this.textarea.setAttribute('aria-label', label)\n    } else {\n      this.textarea.removeAttribute('aria-label')\n    }\n  }\n\n  prepareSelection() {\n    // Redraw the selection and/or cursor\n    let cm = this.cm, display = cm.display, doc = cm.doc\n    let result = prepareSelection(cm)\n\n    // Move the hidden textarea near the cursor to prevent scrolling artifacts\n    if (cm.options.moveInputWithCursor) {\n      let headPos = cursorCoords(cm, doc.sel.primary().head, \"div\")\n      let wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect()\n      result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,\n                                          headPos.top + lineOff.top - wrapOff.top))\n      result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,\n                                           headPos.left + lineOff.left - wrapOff.left))\n    }\n\n    return result\n  }\n\n  showSelection(drawn) {\n    let cm = this.cm, display = cm.display\n    removeChildrenAndAdd(display.cursorDiv, drawn.cursors)\n    removeChildrenAndAdd(display.selectionDiv, drawn.selection)\n    if (drawn.teTop != null) {\n      this.wrapper.style.top = drawn.teTop + \"px\"\n      this.wrapper.style.left = drawn.teLeft + \"px\"\n    }\n  }\n\n  // Reset the input to correspond to the selection (or to be empty,\n  // when not typing and nothing is selected)\n  reset(typing) {\n    if (this.contextMenuPending || this.composing && typing) return\n    let cm = this.cm\n    this.resetting = true\n    if (cm.somethingSelected()) {\n      this.prevInput = \"\"\n      let content = cm.getSelection()\n      this.textarea.value = content\n      if (cm.state.focused) selectInput(this.textarea)\n      if (ie && ie_version >= 9) this.hasSelection = content\n    } else if (!typing) {\n      this.prevInput = this.textarea.value = \"\"\n      if (ie && ie_version >= 9) this.hasSelection = null\n    }\n    this.resetting = false\n  }\n\n  getField() { return this.textarea }\n\n  supportsTouch() { return false }\n\n  focus() {\n    if (this.cm.options.readOnly != \"nocursor\" && (!mobile || activeElt(rootNode(this.textarea)) != this.textarea)) {\n      try { this.textarea.focus() }\n      catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM\n    }\n  }\n\n  blur() { this.textarea.blur() }\n\n  resetPosition() {\n    this.wrapper.style.top = this.wrapper.style.left = 0\n  }\n\n  receivedFocus() { this.slowPoll() }\n\n  // Poll for input changes, using the normal rate of polling. This\n  // runs as long as the editor is focused.\n  slowPoll() {\n    if (this.pollingFast) return\n    this.polling.set(this.cm.options.pollInterval, () => {\n      this.poll()\n      if (this.cm.state.focused) this.slowPoll()\n    })\n  }\n\n  // When an event has just come in that is likely to add or change\n  // something in the input textarea, we poll faster, to ensure that\n  // the change appears on the screen quickly.\n  fastPoll() {\n    let missed = false, input = this\n    input.pollingFast = true\n    function p() {\n      let changed = input.poll()\n      if (!changed && !missed) {missed = true; input.polling.set(60, p)}\n      else {input.pollingFast = false; input.slowPoll()}\n    }\n    input.polling.set(20, p)\n  }\n\n  // Read input from the textarea, and update the document to match.\n  // When something is selected, it is present in the textarea, and\n  // selected (unless it is huge, in which case a placeholder is\n  // used). When nothing is selected, the cursor sits after previously\n  // seen text (can be empty), which is stored in prevInput (we must\n  // not reset the textarea when typing, because that breaks IME).\n  poll() {\n    let cm = this.cm, input = this.textarea, prevInput = this.prevInput\n    // Since this is called a *lot*, try to bail out as cheaply as\n    // possible when it is clear that nothing happened. hasSelection\n    // will be the case when there is a lot of text in the textarea,\n    // in which case reading its value would be expensive.\n    if (this.contextMenuPending || this.resetting || !cm.state.focused ||\n        (hasSelection(input) && !prevInput && !this.composing) ||\n        cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)\n      return false\n\n    let text = input.value\n    // If nothing changed, bail.\n    if (text == prevInput && !cm.somethingSelected()) return false\n    // Work around nonsensical selection resetting in IE9/10, and\n    // inexplicable appearance of private area unicode characters on\n    // some key combos in Mac (#2689).\n    if (ie && ie_version >= 9 && this.hasSelection === text ||\n        mac && /[\\uf700-\\uf7ff]/.test(text)) {\n      cm.display.input.reset()\n      return false\n    }\n\n    if (cm.doc.sel == cm.display.selForContextMenu) {\n      let first = text.charCodeAt(0)\n      if (first == 0x200b && !prevInput) prevInput = \"\\u200b\"\n      if (first == 0x21da) { this.reset(); return this.cm.execCommand(\"undo\") }\n    }\n    // Find the part of the input that is actually new\n    let same = 0, l = Math.min(prevInput.length, text.length)\n    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same\n\n    runInOp(cm, () => {\n      applyTextInput(cm, text.slice(same), prevInput.length - same,\n                     null, this.composing ? \"*compose\" : null)\n\n      // Don't leave long text in the textarea, since it makes further polling slow\n      if (text.length > 1000 || text.indexOf(\"\\n\") > -1) input.value = this.prevInput = \"\"\n      else this.prevInput = text\n\n      if (this.composing) {\n        this.composing.range.clear()\n        this.composing.range = cm.markText(this.composing.start, cm.getCursor(\"to\"),\n                                           {className: \"CodeMirror-composing\"})\n      }\n    })\n    return true\n  }\n\n  ensurePolled() {\n    if (this.pollingFast && this.poll()) this.pollingFast = false\n  }\n\n  onKeyPress() {\n    if (ie && ie_version >= 9) this.hasSelection = null\n    this.fastPoll()\n  }\n\n  onContextMenu(e) {\n    let input = this, cm = input.cm, display = cm.display, te = input.textarea\n    if (input.contextMenuPending) input.contextMenuPending()\n    let pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop\n    if (!pos || presto) return // Opera is difficult.\n\n    // Reset the current text selection only if the click is done outside of the selection\n    // and 'resetSelectionOnContextMenu' option is true.\n    let reset = cm.options.resetSelectionOnContextMenu\n    if (reset && cm.doc.sel.contains(pos) == -1)\n      operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll)\n\n    let oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText\n    let wrapperBox = input.wrapper.offsetParent.getBoundingClientRect()\n    input.wrapper.style.cssText = \"position: static\"\n    te.style.cssText = `position: absolute; width: 30px; height: 30px;\n      top: ${e.clientY - wrapperBox.top - 5}px; left: ${e.clientX - wrapperBox.left - 5}px;\n      z-index: 1000; background: ${ie ? \"rgba(255, 255, 255, .05)\" : \"transparent\"};\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);`\n    let oldScrollY\n    if (webkit) oldScrollY = te.ownerDocument.defaultView.scrollY // Work around Chrome issue (#2712)\n    display.input.focus()\n    if (webkit) te.ownerDocument.defaultView.scrollTo(null, oldScrollY)\n    display.input.reset()\n    // Adds \"Select all\" to context menu in FF\n    if (!cm.somethingSelected()) te.value = input.prevInput = \" \"\n    input.contextMenuPending = rehide\n    display.selForContextMenu = cm.doc.sel\n    clearTimeout(display.detectingSelectAll)\n\n    // Select-all will be greyed out if there's nothing to select, so\n    // this adds a zero-width space so that we can later check whether\n    // it got selected.\n    function prepareSelectAllHack() {\n      if (te.selectionStart != null) {\n        let selected = cm.somethingSelected()\n        let extval = \"\\u200b\" + (selected ? te.value : \"\")\n        te.value = \"\\u21da\" // Used to catch context-menu undo\n        te.value = extval\n        input.prevInput = selected ? \"\" : \"\\u200b\"\n        te.selectionStart = 1; te.selectionEnd = extval.length\n        // Re-set this, in case some other handler touched the\n        // selection in the meantime.\n        display.selForContextMenu = cm.doc.sel\n      }\n    }\n    function rehide() {\n      if (input.contextMenuPending != rehide) return\n      input.contextMenuPending = false\n      input.wrapper.style.cssText = oldWrapperCSS\n      te.style.cssText = oldCSS\n      if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos)\n\n      // Try to detect the user choosing select-all\n      if (te.selectionStart != null) {\n        if (!ie || (ie && ie_version < 9)) prepareSelectAllHack()\n        let i = 0, poll = () => {\n          if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&\n              te.selectionEnd > 0 && input.prevInput == \"\\u200b\") {\n            operation(cm, selectAll)(cm)\n          } else if (i++ < 10) {\n            display.detectingSelectAll = setTimeout(poll, 500)\n          } else {\n            display.selForContextMenu = null\n            display.input.reset()\n          }\n        }\n        display.detectingSelectAll = setTimeout(poll, 200)\n      }\n    }\n\n    if (ie && ie_version >= 9) prepareSelectAllHack()\n    if (captureRightClick) {\n      e_stop(e)\n      let mouseup = () => {\n        off(window, \"mouseup\", mouseup)\n        setTimeout(rehide, 20)\n      }\n      on(window, \"mouseup\", mouseup)\n    } else {\n      setTimeout(rehide, 50)\n    }\n  }\n\n  readOnlyChanged(val) {\n    if (!val) this.reset()\n    this.textarea.disabled = val == \"nocursor\"\n    this.textarea.readOnly = !!val\n  }\n\n  setUneditable() {}\n}\n\nTextareaInput.prototype.needsContentAttribute = false\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/input/indent.js",
    "content": "import { getContextBefore } from \"../line/highlight.js\"\nimport { Pos } from \"../line/pos.js\"\nimport { getLine } from \"../line/utils_line.js\"\nimport { replaceRange } from \"../model/changes.js\"\nimport { Range } from \"../model/selection.js\"\nimport { replaceOneSelection } from \"../model/selection_updates.js\"\nimport { countColumn, Pass, spaceStr } from \"../util/misc.js\"\n\n// Indent the given line. The how parameter can be \"smart\",\n// \"add\"/null, \"subtract\", or \"prev\". When aggressive is false\n// (typically set to true for forced single-line indents), empty\n// lines are not indented, and places where the mode returns Pass\n// are left alone.\nexport function indentLine(cm, n, how, aggressive) {\n  let doc = cm.doc, state\n  if (how == null) how = \"add\"\n  if (how == \"smart\") {\n    // Fall back to \"prev\" when the mode doesn't have an indentation\n    // method.\n    if (!doc.mode.indent) how = \"prev\"\n    else state = getContextBefore(cm, n).state\n  }\n\n  let tabSize = cm.options.tabSize\n  let line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)\n  if (line.stateAfter) line.stateAfter = null\n  let curSpaceString = line.text.match(/^\\s*/)[0], indentation\n  if (!aggressive && !/\\S/.test(line.text)) {\n    indentation = 0\n    how = \"not\"\n  } else if (how == \"smart\") {\n    indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text)\n    if (indentation == Pass || indentation > 150) {\n      if (!aggressive) return\n      how = \"prev\"\n    }\n  }\n  if (how == \"prev\") {\n    if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize)\n    else indentation = 0\n  } else if (how == \"add\") {\n    indentation = curSpace + cm.options.indentUnit\n  } else if (how == \"subtract\") {\n    indentation = curSpace - cm.options.indentUnit\n  } else if (typeof how == \"number\") {\n    indentation = curSpace + how\n  }\n  indentation = Math.max(0, indentation)\n\n  let indentString = \"\", pos = 0\n  if (cm.options.indentWithTabs)\n    for (let i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += \"\\t\"}\n  if (pos < indentation) indentString += spaceStr(indentation - pos)\n\n  if (indentString != curSpaceString) {\n    replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), \"+input\")\n    line.stateAfter = null\n    return true\n  } else {\n    // Ensure that, if the cursor was in the whitespace at the start\n    // of the line, it is moved to the end of that space.\n    for (let i = 0; i < doc.sel.ranges.length; i++) {\n      let range = doc.sel.ranges[i]\n      if (range.head.line == n && range.head.ch < curSpaceString.length) {\n        let pos = Pos(n, curSpaceString.length)\n        replaceOneSelection(doc, i, new Range(pos, pos))\n        break\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/input/input.js",
    "content": "import { runInOp } from \"../display/operations.js\"\nimport { ensureCursorVisible } from \"../display/scrolling.js\"\nimport { Pos } from \"../line/pos.js\"\nimport { getLine } from \"../line/utils_line.js\"\nimport { makeChange } from \"../model/changes.js\"\nimport { ios, webkit } from \"../util/browser.js\"\nimport { elt } from \"../util/dom.js\"\nimport { lst, map } from \"../util/misc.js\"\nimport { signalLater } from \"../util/operation_group.js\"\nimport { splitLinesAuto } from \"../util/feature_detection.js\"\n\nimport { indentLine } from \"./indent.js\"\n\n// This will be set to a {lineWise: bool, text: [string]} object, so\n// that, when pasting, we know what kind of selections the copied\n// text was made out of.\nexport let lastCopied = null\n\nexport function setLastCopied(newLastCopied) {\n  lastCopied = newLastCopied\n}\n\nexport function applyTextInput(cm, inserted, deleted, sel, origin) {\n  let doc = cm.doc\n  cm.display.shift = false\n  if (!sel) sel = doc.sel\n\n  let recent = +new Date - 200\n  let paste = origin == \"paste\" || cm.state.pasteIncoming > recent\n  let textLines = splitLinesAuto(inserted), multiPaste = null\n  // When pasting N lines into N selections, insert one line per selection\n  if (paste && sel.ranges.length > 1) {\n    if (lastCopied && lastCopied.text.join(\"\\n\") == inserted) {\n      if (sel.ranges.length % lastCopied.text.length == 0) {\n        multiPaste = []\n        for (let i = 0; i < lastCopied.text.length; i++)\n          multiPaste.push(doc.splitLines(lastCopied.text[i]))\n      }\n    } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {\n      multiPaste = map(textLines, l => [l])\n    }\n  }\n\n  let updateInput = cm.curOp.updateInput\n  // Normal behavior is to insert the new text into every selection\n  for (let i = sel.ranges.length - 1; i >= 0; i--) {\n    let range = sel.ranges[i]\n    let from = range.from(), to = range.to()\n    if (range.empty()) {\n      if (deleted && deleted > 0) // Handle deletion\n        from = Pos(from.line, from.ch - deleted)\n      else if (cm.state.overwrite && !paste) // Handle overwrite\n        to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length))\n      else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join(\"\\n\") == textLines.join(\"\\n\"))\n        from = to = Pos(from.line, 0)\n    }\n    let changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,\n                       origin: origin || (paste ? \"paste\" : cm.state.cutIncoming > recent ? \"cut\" : \"+input\")}\n    makeChange(cm.doc, changeEvent)\n    signalLater(cm, \"inputRead\", cm, changeEvent)\n  }\n  if (inserted && !paste)\n    triggerElectric(cm, inserted)\n\n  ensureCursorVisible(cm)\n  if (cm.curOp.updateInput < 2) cm.curOp.updateInput = updateInput\n  cm.curOp.typing = true\n  cm.state.pasteIncoming = cm.state.cutIncoming = -1\n}\n\nexport function handlePaste(e, cm) {\n  let pasted = e.clipboardData && e.clipboardData.getData(\"Text\")\n  if (pasted) {\n    e.preventDefault()\n    if (!cm.isReadOnly() && !cm.options.disableInput && cm.hasFocus())\n      runInOp(cm, () => applyTextInput(cm, pasted, 0, null, \"paste\"))\n    return true\n  }\n}\n\nexport function triggerElectric(cm, inserted) {\n  // When an 'electric' character is inserted, immediately trigger a reindent\n  if (!cm.options.electricChars || !cm.options.smartIndent) return\n  let sel = cm.doc.sel\n\n  for (let i = sel.ranges.length - 1; i >= 0; i--) {\n    let range = sel.ranges[i]\n    if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue\n    let mode = cm.getModeAt(range.head)\n    let indented = false\n    if (mode.electricChars) {\n      for (let j = 0; j < mode.electricChars.length; j++)\n        if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {\n          indented = indentLine(cm, range.head.line, \"smart\")\n          break\n        }\n    } else if (mode.electricInput) {\n      if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))\n        indented = indentLine(cm, range.head.line, \"smart\")\n    }\n    if (indented) signalLater(cm, \"electricInput\", cm, range.head.line)\n  }\n}\n\nexport function copyableRanges(cm) {\n  let text = [], ranges = []\n  for (let i = 0; i < cm.doc.sel.ranges.length; i++) {\n    let line = cm.doc.sel.ranges[i].head.line\n    let lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}\n    ranges.push(lineRange)\n    text.push(cm.getRange(lineRange.anchor, lineRange.head))\n  }\n  return {text: text, ranges: ranges}\n}\n\nexport function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {\n  field.setAttribute(\"autocorrect\", autocorrect ? \"on\" : \"off\")\n  field.setAttribute(\"autocapitalize\", autocapitalize ? \"on\" : \"off\")\n  field.setAttribute(\"spellcheck\", !!spellcheck)\n}\n\nexport function hiddenTextarea() {\n  let te = elt(\"textarea\", null, null, \"position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; min-height: 1em; outline: none\")\n  let div = elt(\"div\", [te], null, \"overflow: hidden; position: relative; width: 3px; height: 0px;\")\n  // The textarea is kept positioned near the cursor to prevent the\n  // fact that it'll be scrolled into view on input from scrolling\n  // our fake cursor out of view. On webkit, when wrap=off, paste is\n  // very slow. So make the area wide instead.\n  if (webkit) te.style.width = \"1000px\"\n  else te.setAttribute(\"wrap\", \"off\")\n  // If border: 0; -- iOS fails to open keyboard (issue #1287)\n  if (ios) te.style.border = \"1px solid black\"\n  return div\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/input/keymap.js",
    "content": "import { flipCtrlCmd, mac, presto } from \"../util/browser.js\"\nimport { map } from \"../util/misc.js\"\n\nimport { keyNames } from \"./keynames.js\"\n\nexport let keyMap = {}\n\nkeyMap.basic = {\n  \"Left\": \"goCharLeft\", \"Right\": \"goCharRight\", \"Up\": \"goLineUp\", \"Down\": \"goLineDown\",\n  \"End\": \"goLineEnd\", \"Home\": \"goLineStartSmart\", \"PageUp\": \"goPageUp\", \"PageDown\": \"goPageDown\",\n  \"Delete\": \"delCharAfter\", \"Backspace\": \"delCharBefore\", \"Shift-Backspace\": \"delCharBefore\",\n  \"Tab\": \"defaultTab\", \"Shift-Tab\": \"indentAuto\",\n  \"Enter\": \"newlineAndIndent\", \"Insert\": \"toggleOverwrite\",\n  \"Esc\": \"singleSelection\"\n}\n// Note that the save and find-related commands aren't defined by\n// default. User code or addons can define them. Unknown commands\n// are simply ignored.\nkeyMap.pcDefault = {\n  \"Ctrl-A\": \"selectAll\", \"Ctrl-D\": \"deleteLine\", \"Ctrl-Z\": \"undo\", \"Shift-Ctrl-Z\": \"redo\", \"Ctrl-Y\": \"redo\",\n  \"Ctrl-Home\": \"goDocStart\", \"Ctrl-End\": \"goDocEnd\", \"Ctrl-Up\": \"goLineUp\", \"Ctrl-Down\": \"goLineDown\",\n  \"Ctrl-Left\": \"goGroupLeft\", \"Ctrl-Right\": \"goGroupRight\", \"Alt-Left\": \"goLineStart\", \"Alt-Right\": \"goLineEnd\",\n  \"Ctrl-Backspace\": \"delGroupBefore\", \"Ctrl-Delete\": \"delGroupAfter\", \"Ctrl-S\": \"save\", \"Ctrl-F\": \"find\",\n  \"Ctrl-G\": \"findNext\", \"Shift-Ctrl-G\": \"findPrev\", \"Shift-Ctrl-F\": \"replace\", \"Shift-Ctrl-R\": \"replaceAll\",\n  \"Ctrl-[\": \"indentLess\", \"Ctrl-]\": \"indentMore\",\n  \"Ctrl-U\": \"undoSelection\", \"Shift-Ctrl-U\": \"redoSelection\", \"Alt-U\": \"redoSelection\",\n  \"fallthrough\": \"basic\"\n}\n// Very basic readline/emacs-style bindings, which are standard on Mac.\nkeyMap.emacsy = {\n  \"Ctrl-F\": \"goCharRight\", \"Ctrl-B\": \"goCharLeft\", \"Ctrl-P\": \"goLineUp\", \"Ctrl-N\": \"goLineDown\",\n  \"Ctrl-A\": \"goLineStart\", \"Ctrl-E\": \"goLineEnd\", \"Ctrl-V\": \"goPageDown\", \"Shift-Ctrl-V\": \"goPageUp\",\n  \"Ctrl-D\": \"delCharAfter\", \"Ctrl-H\": \"delCharBefore\", \"Alt-Backspace\": \"delWordBefore\", \"Ctrl-K\": \"killLine\",\n  \"Ctrl-T\": \"transposeChars\", \"Ctrl-O\": \"openLine\"\n}\nkeyMap.macDefault = {\n  \"Cmd-A\": \"selectAll\", \"Cmd-D\": \"deleteLine\", \"Cmd-Z\": \"undo\", \"Shift-Cmd-Z\": \"redo\", \"Cmd-Y\": \"redo\",\n  \"Cmd-Home\": \"goDocStart\", \"Cmd-Up\": \"goDocStart\", \"Cmd-End\": \"goDocEnd\", \"Cmd-Down\": \"goDocEnd\", \"Alt-Left\": \"goGroupLeft\",\n  \"Alt-Right\": \"goGroupRight\", \"Cmd-Left\": \"goLineLeft\", \"Cmd-Right\": \"goLineRight\", \"Alt-Backspace\": \"delGroupBefore\",\n  \"Ctrl-Alt-Backspace\": \"delGroupAfter\", \"Alt-Delete\": \"delGroupAfter\", \"Cmd-S\": \"save\", \"Cmd-F\": \"find\",\n  \"Cmd-G\": \"findNext\", \"Shift-Cmd-G\": \"findPrev\", \"Cmd-Alt-F\": \"replace\", \"Shift-Cmd-Alt-F\": \"replaceAll\",\n  \"Cmd-[\": \"indentLess\", \"Cmd-]\": \"indentMore\", \"Cmd-Backspace\": \"delWrappedLineLeft\", \"Cmd-Delete\": \"delWrappedLineRight\",\n  \"Cmd-U\": \"undoSelection\", \"Shift-Cmd-U\": \"redoSelection\", \"Ctrl-Up\": \"goDocStart\", \"Ctrl-Down\": \"goDocEnd\",\n  \"fallthrough\": [\"basic\", \"emacsy\"]\n}\nkeyMap[\"default\"] = mac ? keyMap.macDefault : keyMap.pcDefault\n\n// KEYMAP DISPATCH\n\nfunction normalizeKeyName(name) {\n  let parts = name.split(/-(?!$)/)\n  name = parts[parts.length - 1]\n  let alt, ctrl, shift, cmd\n  for (let i = 0; i < parts.length - 1; i++) {\n    let mod = parts[i]\n    if (/^(cmd|meta|m)$/i.test(mod)) cmd = true\n    else if (/^a(lt)?$/i.test(mod)) alt = true\n    else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true\n    else if (/^s(hift)?$/i.test(mod)) shift = true\n    else throw new Error(\"Unrecognized modifier name: \" + mod)\n  }\n  if (alt) name = \"Alt-\" + name\n  if (ctrl) name = \"Ctrl-\" + name\n  if (cmd) name = \"Cmd-\" + name\n  if (shift) name = \"Shift-\" + name\n  return name\n}\n\n// This is a kludge to keep keymaps mostly working as raw objects\n// (backwards compatibility) while at the same time support features\n// like normalization and multi-stroke key bindings. It compiles a\n// new normalized keymap, and then updates the old object to reflect\n// this.\nexport function normalizeKeyMap(keymap) {\n  let copy = {}\n  for (let keyname in keymap) if (keymap.hasOwnProperty(keyname)) {\n    let value = keymap[keyname]\n    if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue\n    if (value == \"...\") { delete keymap[keyname]; continue }\n\n    let keys = map(keyname.split(\" \"), normalizeKeyName)\n    for (let i = 0; i < keys.length; i++) {\n      let val, name\n      if (i == keys.length - 1) {\n        name = keys.join(\" \")\n        val = value\n      } else {\n        name = keys.slice(0, i + 1).join(\" \")\n        val = \"...\"\n      }\n      let prev = copy[name]\n      if (!prev) copy[name] = val\n      else if (prev != val) throw new Error(\"Inconsistent bindings for \" + name)\n    }\n    delete keymap[keyname]\n  }\n  for (let prop in copy) keymap[prop] = copy[prop]\n  return keymap\n}\n\nexport function lookupKey(key, map, handle, context) {\n  map = getKeyMap(map)\n  let found = map.call ? map.call(key, context) : map[key]\n  if (found === false) return \"nothing\"\n  if (found === \"...\") return \"multi\"\n  if (found != null && handle(found)) return \"handled\"\n\n  if (map.fallthrough) {\n    if (Object.prototype.toString.call(map.fallthrough) != \"[object Array]\")\n      return lookupKey(key, map.fallthrough, handle, context)\n    for (let i = 0; i < map.fallthrough.length; i++) {\n      let result = lookupKey(key, map.fallthrough[i], handle, context)\n      if (result) return result\n    }\n  }\n}\n\n// Modifier key presses don't count as 'real' key presses for the\n// purpose of keymap fallthrough.\nexport function isModifierKey(value) {\n  let name = typeof value == \"string\" ? value : keyNames[value.keyCode]\n  return name == \"Ctrl\" || name == \"Alt\" || name == \"Shift\" || name == \"Mod\"\n}\n\nexport function addModifierNames(name, event, noShift) {\n  let base = name\n  if (event.altKey && base != \"Alt\") name = \"Alt-\" + name\n  if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != \"Ctrl\") name = \"Ctrl-\" + name\n  if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != \"Mod\") name = \"Cmd-\" + name\n  if (!noShift && event.shiftKey && base != \"Shift\") name = \"Shift-\" + name\n  return name\n}\n\n// Look up the name of a key as indicated by an event object.\nexport function keyName(event, noShift) {\n  if (presto && event.keyCode == 34 && event[\"char\"]) return false\n  let name = keyNames[event.keyCode]\n  if (name == null || event.altGraphKey) return false\n  // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,\n  // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)\n  if (event.keyCode == 3 && event.code) name = event.code\n  return addModifierNames(name, event, noShift)\n}\n\nexport function getKeyMap(val) {\n  return typeof val == \"string\" ? keyMap[val] : val\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/input/keynames.js",
    "content": "export let keyNames = {\n  3: \"Pause\", 8: \"Backspace\", 9: \"Tab\", 13: \"Enter\", 16: \"Shift\", 17: \"Ctrl\", 18: \"Alt\",\n  19: \"Pause\", 20: \"CapsLock\", 27: \"Esc\", 32: \"Space\", 33: \"PageUp\", 34: \"PageDown\", 35: \"End\",\n  36: \"Home\", 37: \"Left\", 38: \"Up\", 39: \"Right\", 40: \"Down\", 44: \"PrintScrn\", 45: \"Insert\",\n  46: \"Delete\", 59: \";\", 61: \"=\", 91: \"Mod\", 92: \"Mod\", 93: \"Mod\",\n  106: \"*\", 107: \"=\", 109: \"-\", 110: \".\", 111: \"/\", 145: \"ScrollLock\",\n  173: \"-\", 186: \";\", 187: \"=\", 188: \",\", 189: \"-\", 190: \".\", 191: \"/\", 192: \"`\", 219: \"[\", 220: \"\\\\\",\n  221: \"]\", 222: \"'\", 224: \"Mod\", 63232: \"Up\", 63233: \"Down\", 63234: \"Left\", 63235: \"Right\", 63272: \"Delete\",\n  63273: \"Home\", 63275: \"End\", 63276: \"PageUp\", 63277: \"PageDown\", 63302: \"Insert\"\n}\n\n// Number keys\nfor (let i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i)\n// Alphabetic keys\nfor (let i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i)\n// Function keys\nfor (let i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = \"F\" + i\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/input/movement.js",
    "content": "import { Pos } from \"../line/pos.js\"\nimport { prepareMeasureForLine, measureCharPrepared, wrappedLineExtentChar } from \"../measurement/position_measurement.js\"\nimport { getBidiPartAt, getOrder } from \"../util/bidi.js\"\nimport { findFirst, lst, skipExtendingChars } from \"../util/misc.js\"\n\nfunction moveCharLogically(line, ch, dir) {\n  let target = skipExtendingChars(line.text, ch + dir, dir)\n  return target < 0 || target > line.text.length ? null : target\n}\n\nexport function moveLogically(line, start, dir) {\n  let ch = moveCharLogically(line, start.ch, dir)\n  return ch == null ? null : new Pos(start.line, ch, dir < 0 ? \"after\" : \"before\")\n}\n\nexport function endOfLine(visually, cm, lineObj, lineNo, dir) {\n  if (visually) {\n    if (cm.doc.direction == \"rtl\") dir = -dir\n    let order = getOrder(lineObj, cm.doc.direction)\n    if (order) {\n      let part = dir < 0 ? lst(order) : order[0]\n      let moveInStorageOrder = (dir < 0) == (part.level == 1)\n      let sticky = moveInStorageOrder ? \"after\" : \"before\"\n      let ch\n      // With a wrapped rtl chunk (possibly spanning multiple bidi parts),\n      // it could be that the last bidi part is not on the last visual line,\n      // since visual lines contain content order-consecutive chunks.\n      // Thus, in rtl, we are looking for the first (content-order) character\n      // in the rtl chunk that is on the last line (that is, the same line\n      // as the last (content-order) character).\n      if (part.level > 0 || cm.doc.direction == \"rtl\") {\n        let prep = prepareMeasureForLine(cm, lineObj)\n        ch = dir < 0 ? lineObj.text.length - 1 : 0\n        let targetTop = measureCharPrepared(cm, prep, ch).top\n        ch = findFirst(ch => measureCharPrepared(cm, prep, ch).top == targetTop, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)\n        if (sticky == \"before\") ch = moveCharLogically(lineObj, ch, 1)\n      } else ch = dir < 0 ? part.to : part.from\n      return new Pos(lineNo, ch, sticky)\n    }\n  }\n  return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? \"before\" : \"after\")\n}\n\nexport function moveVisually(cm, line, start, dir) {\n  let bidi = getOrder(line, cm.doc.direction)\n  if (!bidi) return moveLogically(line, start, dir)\n  if (start.ch >= line.text.length) {\n    start.ch = line.text.length\n    start.sticky = \"before\"\n  } else if (start.ch <= 0) {\n    start.ch = 0\n    start.sticky = \"after\"\n  }\n  let partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]\n  if (cm.doc.direction == \"ltr\" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {\n    // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,\n    // nothing interesting happens.\n    return moveLogically(line, start, dir)\n  }\n\n  let mv = (pos, dir) => moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir)\n  let prep\n  let getWrappedLineExtent = ch => {\n    if (!cm.options.lineWrapping) return {begin: 0, end: line.text.length}\n    prep = prep || prepareMeasureForLine(cm, line)\n    return wrappedLineExtentChar(cm, line, prep, ch)\n  }\n  let wrappedLineExtent = getWrappedLineExtent(start.sticky == \"before\" ? mv(start, -1) : start.ch)\n\n  if (cm.doc.direction == \"rtl\" || part.level == 1) {\n    let moveInStorageOrder = (part.level == 1) == (dir < 0)\n    let ch = mv(start, moveInStorageOrder ? 1 : -1)\n    if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {\n      // Case 2: We move within an rtl part or in an rtl editor on the same visual line\n      let sticky = moveInStorageOrder ? \"before\" : \"after\"\n      return new Pos(start.line, ch, sticky)\n    }\n  }\n\n  // Case 3: Could not move within this bidi part in this visual line, so leave\n  // the current bidi part\n\n  let searchInVisualLine = (partPos, dir, wrappedLineExtent) => {\n    let getRes = (ch, moveInStorageOrder) => moveInStorageOrder\n      ? new Pos(start.line, mv(ch, 1), \"before\")\n      : new Pos(start.line, ch, \"after\")\n\n    for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {\n      let part = bidi[partPos]\n      let moveInStorageOrder = (dir > 0) == (part.level != 1)\n      let ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)\n      if (part.from <= ch && ch < part.to) return getRes(ch, moveInStorageOrder)\n      ch = moveInStorageOrder ? part.from : mv(part.to, -1)\n      if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) return getRes(ch, moveInStorageOrder)\n    }\n  }\n\n  // Case 3a: Look for other bidi parts on the same visual line\n  let res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)\n  if (res) return res\n\n  // Case 3b: Look for other bidi parts on the next visual line\n  let nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)\n  if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {\n    res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))\n    if (res) return res\n  }\n\n  // Case 4: Nowhere to move\n  return null\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/line/highlight.js",
    "content": "import { countColumn } from \"../util/misc.js\"\nimport { copyState, innerMode, startState } from \"../modes.js\"\nimport StringStream from \"../util/StringStream.js\"\n\nimport { getLine, lineNo } from \"./utils_line.js\"\nimport { clipPos } from \"./pos.js\"\n\nclass SavedContext {\n  constructor(state, lookAhead) {\n    this.state = state\n    this.lookAhead = lookAhead\n  }\n}\n\nclass Context {\n  constructor(doc, state, line, lookAhead) {\n    this.state = state\n    this.doc = doc\n    this.line = line\n    this.maxLookAhead = lookAhead || 0\n    this.baseTokens = null\n    this.baseTokenPos = 1\n  }\n\n  lookAhead(n) {\n    let line = this.doc.getLine(this.line + n)\n    if (line != null && n > this.maxLookAhead) this.maxLookAhead = n\n    return line\n  }\n\n  baseToken(n) {\n    if (!this.baseTokens) return null\n    while (this.baseTokens[this.baseTokenPos] <= n)\n      this.baseTokenPos += 2\n    let type = this.baseTokens[this.baseTokenPos + 1]\n    return {type: type && type.replace(/( |^)overlay .*/, \"\"),\n            size: this.baseTokens[this.baseTokenPos] - n}\n  }\n\n  nextLine() {\n    this.line++\n    if (this.maxLookAhead > 0) this.maxLookAhead--\n  }\n\n  static fromSaved(doc, saved, line) {\n    if (saved instanceof SavedContext)\n      return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead)\n    else\n      return new Context(doc, copyState(doc.mode, saved), line)\n  }\n\n  save(copy) {\n    let state = copy !== false ? copyState(this.doc.mode, this.state) : this.state\n    return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state\n  }\n}\n\n\n// Compute a style array (an array starting with a mode generation\n// -- for invalidation -- followed by pairs of end positions and\n// style strings), which is used to highlight the tokens on the\n// line.\nexport function highlightLine(cm, line, context, forceToEnd) {\n  // A styles array always starts with a number identifying the\n  // mode/overlays that it is based on (for easy invalidation).\n  let st = [cm.state.modeGen], lineClasses = {}\n  // Compute the base array of styles\n  runMode(cm, line.text, cm.doc.mode, context, (end, style) => st.push(end, style),\n          lineClasses, forceToEnd)\n  let state = context.state\n\n  // Run overlays, adjust style array.\n  for (let o = 0; o < cm.state.overlays.length; ++o) {\n    context.baseTokens = st\n    let overlay = cm.state.overlays[o], i = 1, at = 0\n    context.state = true\n    runMode(cm, line.text, overlay.mode, context, (end, style) => {\n      let start = i\n      // Ensure there's a token end at the current position, and that i points at it\n      while (at < end) {\n        let i_end = st[i]\n        if (i_end > end)\n          st.splice(i, 1, end, st[i+1], i_end)\n        i += 2\n        at = Math.min(end, i_end)\n      }\n      if (!style) return\n      if (overlay.opaque) {\n        st.splice(start, i - start, end, \"overlay \" + style)\n        i = start + 2\n      } else {\n        for (; start < i; start += 2) {\n          let cur = st[start+1]\n          st[start+1] = (cur ? cur + \" \" : \"\") + \"overlay \" + style\n        }\n      }\n    }, lineClasses)\n    context.state = state\n    context.baseTokens = null\n    context.baseTokenPos = 1\n  }\n\n  return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}\n}\n\nexport function getLineStyles(cm, line, updateFrontier) {\n  if (!line.styles || line.styles[0] != cm.state.modeGen) {\n    let context = getContextBefore(cm, lineNo(line))\n    let resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state)\n    let result = highlightLine(cm, line, context)\n    if (resetState) context.state = resetState\n    line.stateAfter = context.save(!resetState)\n    line.styles = result.styles\n    if (result.classes) line.styleClasses = result.classes\n    else if (line.styleClasses) line.styleClasses = null\n    if (updateFrontier === cm.doc.highlightFrontier)\n      cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier)\n  }\n  return line.styles\n}\n\nexport function getContextBefore(cm, n, precise) {\n  let doc = cm.doc, display = cm.display\n  if (!doc.mode.startState) return new Context(doc, true, n)\n  let start = findStartLine(cm, n, precise)\n  let saved = start > doc.first && getLine(doc, start - 1).stateAfter\n  let context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start)\n\n  doc.iter(start, n, line => {\n    processLine(cm, line.text, context)\n    let pos = context.line\n    line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null\n    context.nextLine()\n  })\n  if (precise) doc.modeFrontier = context.line\n  return context\n}\n\n// Lightweight form of highlight -- proceed over this line and\n// update state, but don't save a style array. Used for lines that\n// aren't currently visible.\nexport function processLine(cm, text, context, startAt) {\n  let mode = cm.doc.mode\n  let stream = new StringStream(text, cm.options.tabSize, context)\n  stream.start = stream.pos = startAt || 0\n  if (text == \"\") callBlankLine(mode, context.state)\n  while (!stream.eol()) {\n    readToken(mode, stream, context.state)\n    stream.start = stream.pos\n  }\n}\n\nfunction callBlankLine(mode, state) {\n  if (mode.blankLine) return mode.blankLine(state)\n  if (!mode.innerMode) return\n  let inner = innerMode(mode, state)\n  if (inner.mode.blankLine) return inner.mode.blankLine(inner.state)\n}\n\nfunction readToken(mode, stream, state, inner) {\n  for (let i = 0; i < 10; i++) {\n    if (inner) inner[0] = innerMode(mode, state).mode\n    let style = mode.token(stream, state)\n    if (stream.pos > stream.start) return style\n  }\n  throw new Error(\"Mode \" + mode.name + \" failed to advance stream.\")\n}\n\nclass Token {\n  constructor(stream, type, state) {\n    this.start = stream.start; this.end = stream.pos\n    this.string = stream.current()\n    this.type = type || null\n    this.state = state\n  }\n}\n\n// Utility for getTokenAt and getLineTokens\nexport function takeToken(cm, pos, precise, asArray) {\n  let doc = cm.doc, mode = doc.mode, style\n  pos = clipPos(doc, pos)\n  let line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise)\n  let stream = new StringStream(line.text, cm.options.tabSize, context), tokens\n  if (asArray) tokens = []\n  while ((asArray || stream.pos < pos.ch) && !stream.eol()) {\n    stream.start = stream.pos\n    style = readToken(mode, stream, context.state)\n    if (asArray) tokens.push(new Token(stream, style, copyState(doc.mode, context.state)))\n  }\n  return asArray ? tokens : new Token(stream, style, context.state)\n}\n\nfunction extractLineClasses(type, output) {\n  if (type) for (;;) {\n    let lineClass = type.match(/(?:^|\\s+)line-(background-)?(\\S+)/)\n    if (!lineClass) break\n    type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length)\n    let prop = lineClass[1] ? \"bgClass\" : \"textClass\"\n    if (output[prop] == null)\n      output[prop] = lineClass[2]\n    else if (!(new RegExp(\"(?:^|\\\\s)\" + lineClass[2] + \"(?:$|\\\\s)\")).test(output[prop]))\n      output[prop] += \" \" + lineClass[2]\n  }\n  return type\n}\n\n// Run the given mode's parser over a line, calling f for each token.\nfunction runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {\n  let flattenSpans = mode.flattenSpans\n  if (flattenSpans == null) flattenSpans = cm.options.flattenSpans\n  let curStart = 0, curStyle = null\n  let stream = new StringStream(text, cm.options.tabSize, context), style\n  let inner = cm.options.addModeClass && [null]\n  if (text == \"\") extractLineClasses(callBlankLine(mode, context.state), lineClasses)\n  while (!stream.eol()) {\n    if (stream.pos > cm.options.maxHighlightLength) {\n      flattenSpans = false\n      if (forceToEnd) processLine(cm, text, context, stream.pos)\n      stream.pos = text.length\n      style = null\n    } else {\n      style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses)\n    }\n    if (inner) {\n      let mName = inner[0].name\n      if (mName) style = \"m-\" + (style ? mName + \" \" + style : mName)\n    }\n    if (!flattenSpans || curStyle != style) {\n      while (curStart < stream.start) {\n        curStart = Math.min(stream.start, curStart + 5000)\n        f(curStart, curStyle)\n      }\n      curStyle = style\n    }\n    stream.start = stream.pos\n  }\n  while (curStart < stream.pos) {\n    // Webkit seems to refuse to render text nodes longer than 57444\n    // characters, and returns inaccurate measurements in nodes\n    // starting around 5000 chars.\n    let pos = Math.min(stream.pos, curStart + 5000)\n    f(pos, curStyle)\n    curStart = pos\n  }\n}\n\n// Finds the line to start with when starting a parse. Tries to\n// find a line with a stateAfter, so that it can start with a\n// valid state. If that fails, it returns the line with the\n// smallest indentation, which tends to need the least context to\n// parse correctly.\nfunction findStartLine(cm, n, precise) {\n  let minindent, minline, doc = cm.doc\n  let lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100)\n  for (let search = n; search > lim; --search) {\n    if (search <= doc.first) return doc.first\n    let line = getLine(doc, search - 1), after = line.stateAfter\n    if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))\n      return search\n    let indented = countColumn(line.text, null, cm.options.tabSize)\n    if (minline == null || minindent > indented) {\n      minline = search - 1\n      minindent = indented\n    }\n  }\n  return minline\n}\n\nexport function retreatFrontier(doc, n) {\n  doc.modeFrontier = Math.min(doc.modeFrontier, n)\n  if (doc.highlightFrontier < n - 10) return\n  let start = doc.first\n  for (let line = n - 1; line > start; line--) {\n    let saved = getLine(doc, line).stateAfter\n    // change is on 3\n    // state on line 1 looked ahead 2 -- so saw 3\n    // test 1 + 2 < 3 should cover this\n    if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {\n      start = line + 1\n      break\n    }\n  }\n  doc.highlightFrontier = Math.min(doc.highlightFrontier, start)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/line/line_data.js",
    "content": "import { getOrder } from \"../util/bidi.js\"\nimport { ie, ie_version, webkit } from \"../util/browser.js\"\nimport { elt, eltP, joinClasses } from \"../util/dom.js\"\nimport { eventMixin, signal } from \"../util/event.js\"\nimport { hasBadBidiRects, zeroWidthElement } from \"../util/feature_detection.js\"\nimport { lst, spaceStr } from \"../util/misc.js\"\n\nimport { getLineStyles } from \"./highlight.js\"\nimport { attachMarkedSpans, compareCollapsedMarkers, detachMarkedSpans, lineIsHidden, visualLineContinued } from \"./spans.js\"\nimport { getLine, lineNo, updateLineHeight } from \"./utils_line.js\"\n\n// LINE DATA STRUCTURE\n\n// Line objects. These hold state related to a line, including\n// highlighting info (the styles array).\nexport class Line {\n  constructor(text, markedSpans, estimateHeight) {\n    this.text = text\n    attachMarkedSpans(this, markedSpans)\n    this.height = estimateHeight ? estimateHeight(this) : 1\n  }\n\n  lineNo() { return lineNo(this) }\n}\neventMixin(Line)\n\n// Change the content (text, markers) of a line. Automatically\n// invalidates cached information and tries to re-estimate the\n// line's height.\nexport function updateLine(line, text, markedSpans, estimateHeight) {\n  line.text = text\n  if (line.stateAfter) line.stateAfter = null\n  if (line.styles) line.styles = null\n  if (line.order != null) line.order = null\n  detachMarkedSpans(line)\n  attachMarkedSpans(line, markedSpans)\n  let estHeight = estimateHeight ? estimateHeight(line) : 1\n  if (estHeight != line.height) updateLineHeight(line, estHeight)\n}\n\n// Detach a line from the document tree and its markers.\nexport function cleanUpLine(line) {\n  line.parent = null\n  detachMarkedSpans(line)\n}\n\n// Convert a style as returned by a mode (either null, or a string\n// containing one or more styles) to a CSS style. This is cached,\n// and also looks for line-wide styles.\nlet styleToClassCache = {}, styleToClassCacheWithMode = {}\nfunction interpretTokenStyle(style, options) {\n  if (!style || /^\\s*$/.test(style)) return null\n  let cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache\n  return cache[style] ||\n    (cache[style] = style.replace(/\\S+/g, \"cm-$&\"))\n}\n\n// Render the DOM representation of the text of a line. Also builds\n// up a 'line map', which points at the DOM nodes that represent\n// specific stretches of text, and is used by the measuring code.\n// The returned object contains the DOM node, this map, and\n// information about line-wide styles that were set by the mode.\nexport function buildLineContent(cm, lineView) {\n  // The padding-right forces the element to have a 'border', which\n  // is needed on Webkit to be able to get line-level bounding\n  // rectangles for it (in measureChar).\n  let content = eltP(\"span\", null, null, webkit ? \"padding-right: .1px\" : null)\n  let builder = {pre: eltP(\"pre\", [content], \"CodeMirror-line\"), content: content,\n                 col: 0, pos: 0, cm: cm,\n                 trailingSpace: false,\n                 splitSpaces: cm.getOption(\"lineWrapping\")}\n  lineView.measure = {}\n\n  // Iterate over the logical lines that make up this visual line.\n  for (let i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {\n    let line = i ? lineView.rest[i - 1] : lineView.line, order\n    builder.pos = 0\n    builder.addToken = buildToken\n    // Optionally wire in some hacks into the token-rendering\n    // algorithm, to deal with browser quirks.\n    if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))\n      builder.addToken = buildTokenBadBidi(builder.addToken, order)\n    builder.map = []\n    let allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line)\n    insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate))\n    if (line.styleClasses) {\n      if (line.styleClasses.bgClass)\n        builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || \"\")\n      if (line.styleClasses.textClass)\n        builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || \"\")\n    }\n\n    // Ensure at least a single node is present, for measuring.\n    if (builder.map.length == 0)\n      builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)))\n\n    // Store the map and a cache object for the current logical line\n    if (i == 0) {\n      lineView.measure.map = builder.map\n      lineView.measure.cache = {}\n    } else {\n      ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)\n      ;(lineView.measure.caches || (lineView.measure.caches = [])).push({})\n    }\n  }\n\n  // See issue #2901\n  if (webkit) {\n    let last = builder.content.lastChild\n    if (/\\bcm-tab\\b/.test(last.className) || (last.querySelector && last.querySelector(\".cm-tab\")))\n      builder.content.className = \"cm-tab-wrap-hack\"\n  }\n\n  signal(cm, \"renderLine\", cm, lineView.line, builder.pre)\n  if (builder.pre.className)\n    builder.textClass = joinClasses(builder.pre.className, builder.textClass || \"\")\n\n  return builder\n}\n\nexport function defaultSpecialCharPlaceholder(ch) {\n  let token = elt(\"span\", \"\\u2022\", \"cm-invalidchar\")\n  token.title = \"\\\\u\" + ch.charCodeAt(0).toString(16)\n  token.setAttribute(\"aria-label\", token.title)\n  return token\n}\n\n// Build up the DOM representation for a single token, and add it to\n// the line map. Takes care to render special characters separately.\nfunction buildToken(builder, text, style, startStyle, endStyle, css, attributes) {\n  if (!text) return\n  let displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text\n  let special = builder.cm.state.specialChars, mustWrap = false\n  let content\n  if (!special.test(text)) {\n    builder.col += text.length\n    content = document.createTextNode(displayText)\n    builder.map.push(builder.pos, builder.pos + text.length, content)\n    if (ie && ie_version < 9) mustWrap = true\n    builder.pos += text.length\n  } else {\n    content = document.createDocumentFragment()\n    let pos = 0\n    while (true) {\n      special.lastIndex = pos\n      let m = special.exec(text)\n      let skipped = m ? m.index - pos : text.length - pos\n      if (skipped) {\n        let txt = document.createTextNode(displayText.slice(pos, pos + skipped))\n        if (ie && ie_version < 9) content.appendChild(elt(\"span\", [txt]))\n        else content.appendChild(txt)\n        builder.map.push(builder.pos, builder.pos + skipped, txt)\n        builder.col += skipped\n        builder.pos += skipped\n      }\n      if (!m) break\n      pos += skipped + 1\n      let txt\n      if (m[0] == \"\\t\") {\n        let tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize\n        txt = content.appendChild(elt(\"span\", spaceStr(tabWidth), \"cm-tab\"))\n        txt.setAttribute(\"role\", \"presentation\")\n        txt.setAttribute(\"cm-text\", \"\\t\")\n        builder.col += tabWidth\n      } else if (m[0] == \"\\r\" || m[0] == \"\\n\") {\n        txt = content.appendChild(elt(\"span\", m[0] == \"\\r\" ? \"\\u240d\" : \"\\u2424\", \"cm-invalidchar\"))\n        txt.setAttribute(\"cm-text\", m[0])\n        builder.col += 1\n      } else {\n        txt = builder.cm.options.specialCharPlaceholder(m[0])\n        txt.setAttribute(\"cm-text\", m[0])\n        if (ie && ie_version < 9) content.appendChild(elt(\"span\", [txt]))\n        else content.appendChild(txt)\n        builder.col += 1\n      }\n      builder.map.push(builder.pos, builder.pos + 1, txt)\n      builder.pos++\n    }\n  }\n  builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32\n  if (style || startStyle || endStyle || mustWrap || css || attributes) {\n    let fullStyle = style || \"\"\n    if (startStyle) fullStyle += startStyle\n    if (endStyle) fullStyle += endStyle\n    let token = elt(\"span\", [content], fullStyle, css)\n    if (attributes) {\n      for (let attr in attributes) if (attributes.hasOwnProperty(attr) && attr != \"style\" && attr != \"class\")\n        token.setAttribute(attr, attributes[attr])\n    }\n    return builder.content.appendChild(token)\n  }\n  builder.content.appendChild(content)\n}\n\n// Change some spaces to NBSP to prevent the browser from collapsing\n// trailing spaces at the end of a line when rendering text (issue #1362).\nfunction splitSpaces(text, trailingBefore) {\n  if (text.length > 1 && !/  /.test(text)) return text\n  let spaceBefore = trailingBefore, result = \"\"\n  for (let i = 0; i < text.length; i++) {\n    let ch = text.charAt(i)\n    if (ch == \" \" && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))\n      ch = \"\\u00a0\"\n    result += ch\n    spaceBefore = ch == \" \"\n  }\n  return result\n}\n\n// Work around nonsense dimensions being reported for stretches of\n// right-to-left text.\nfunction buildTokenBadBidi(inner, order) {\n  return (builder, text, style, startStyle, endStyle, css, attributes) => {\n    style = style ? style + \" cm-force-border\" : \"cm-force-border\"\n    let start = builder.pos, end = start + text.length\n    for (;;) {\n      // Find the part that overlaps with the start of this text\n      let part\n      for (let i = 0; i < order.length; i++) {\n        part = order[i]\n        if (part.to > start && part.from <= start) break\n      }\n      if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, css, attributes)\n      inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes)\n      startStyle = null\n      text = text.slice(part.to - start)\n      start = part.to\n    }\n  }\n}\n\nfunction buildCollapsedSpan(builder, size, marker, ignoreWidget) {\n  let widget = !ignoreWidget && marker.widgetNode\n  if (widget) builder.map.push(builder.pos, builder.pos + size, widget)\n  if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {\n    if (!widget)\n      widget = builder.content.appendChild(document.createElement(\"span\"))\n    widget.setAttribute(\"cm-marker\", marker.id)\n  }\n  if (widget) {\n    builder.cm.display.input.setUneditable(widget)\n    builder.content.appendChild(widget)\n  }\n  builder.pos += size\n  builder.trailingSpace = false\n}\n\n// Outputs a number of spans to make up a line, taking highlighting\n// and marked text into account.\nfunction insertLineContent(line, builder, styles) {\n  let spans = line.markedSpans, allText = line.text, at = 0\n  if (!spans) {\n    for (let i = 1; i < styles.length; i+=2)\n      builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options))\n    return\n  }\n\n  let len = allText.length, pos = 0, i = 1, text = \"\", style, css\n  let nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes\n  for (;;) {\n    if (nextChange == pos) { // Update current marker set\n      spanStyle = spanEndStyle = spanStartStyle = css = \"\"\n      attributes = null\n      collapsed = null; nextChange = Infinity\n      let foundBookmarks = [], endStyles\n      for (let j = 0; j < spans.length; ++j) {\n        let sp = spans[j], m = sp.marker\n        if (m.type == \"bookmark\" && sp.from == pos && m.widgetNode) {\n          foundBookmarks.push(m)\n        } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {\n          if (sp.to != null && sp.to != pos && nextChange > sp.to) {\n            nextChange = sp.to\n            spanEndStyle = \"\"\n          }\n          if (m.className) spanStyle += \" \" + m.className\n          if (m.css) css = (css ? css + \";\" : \"\") + m.css\n          if (m.startStyle && sp.from == pos) spanStartStyle += \" \" + m.startStyle\n          if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to)\n          // support for the old title property\n          // https://github.com/codemirror/CodeMirror/pull/5673\n          if (m.title) (attributes || (attributes = {})).title = m.title\n          if (m.attributes) {\n            for (let attr in m.attributes)\n              (attributes || (attributes = {}))[attr] = m.attributes[attr]\n          }\n          if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))\n            collapsed = sp\n        } else if (sp.from > pos && nextChange > sp.from) {\n          nextChange = sp.from\n        }\n      }\n      if (endStyles) for (let j = 0; j < endStyles.length; j += 2)\n        if (endStyles[j + 1] == nextChange) spanEndStyle += \" \" + endStyles[j]\n\n      if (!collapsed || collapsed.from == pos) for (let j = 0; j < foundBookmarks.length; ++j)\n        buildCollapsedSpan(builder, 0, foundBookmarks[j])\n      if (collapsed && (collapsed.from || 0) == pos) {\n        buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,\n                           collapsed.marker, collapsed.from == null)\n        if (collapsed.to == null) return\n        if (collapsed.to == pos) collapsed = false\n      }\n    }\n    if (pos >= len) break\n\n    let upto = Math.min(len, nextChange)\n    while (true) {\n      if (text) {\n        let end = pos + text.length\n        if (!collapsed) {\n          let tokenText = end > upto ? text.slice(0, upto - pos) : text\n          builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,\n                           spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : \"\", css, attributes)\n        }\n        if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}\n        pos = end\n        spanStartStyle = \"\"\n      }\n      text = allText.slice(at, at = styles[i++])\n      style = interpretTokenStyle(styles[i++], builder.cm.options)\n    }\n  }\n}\n\n\n// These objects are used to represent the visible (currently drawn)\n// part of the document. A LineView may correspond to multiple\n// logical lines, if those are connected by collapsed ranges.\nexport function LineView(doc, line, lineN) {\n  // The starting line\n  this.line = line\n  // Continuing lines, if any\n  this.rest = visualLineContinued(line)\n  // Number of logical lines in this visual line\n  this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1\n  this.node = this.text = null\n  this.hidden = lineIsHidden(doc, line)\n}\n\n// Create a range of LineView objects for the given lines.\nexport function buildViewArray(cm, from, to) {\n  let array = [], nextPos\n  for (let pos = from; pos < to; pos = nextPos) {\n    let view = new LineView(cm.doc, getLine(cm.doc, pos), pos)\n    nextPos = pos + view.size\n    array.push(view)\n  }\n  return array\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/line/pos.js",
    "content": "import { getLine } from \"./utils_line.js\"\n\n// A Pos instance represents a position within the text.\nexport function Pos(line, ch, sticky = null) {\n  if (!(this instanceof Pos)) return new Pos(line, ch, sticky)\n  this.line = line\n  this.ch = ch\n  this.sticky = sticky\n}\n\n// Compare two positions, return 0 if they are the same, a negative\n// number when a is less, and a positive number otherwise.\nexport function cmp(a, b) { return a.line - b.line || a.ch - b.ch }\n\nexport function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }\n\nexport function copyPos(x) {return Pos(x.line, x.ch)}\nexport function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }\nexport function minPos(a, b) { return cmp(a, b) < 0 ? a : b }\n\n// Most of the external API clips given positions to make sure they\n// actually exist within the document.\nexport function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}\nexport function clipPos(doc, pos) {\n  if (pos.line < doc.first) return Pos(doc.first, 0)\n  let last = doc.first + doc.size - 1\n  if (pos.line > last) return Pos(last, getLine(doc, last).text.length)\n  return clipToLen(pos, getLine(doc, pos.line).text.length)\n}\nfunction clipToLen(pos, linelen) {\n  let ch = pos.ch\n  if (ch == null || ch > linelen) return Pos(pos.line, linelen)\n  else if (ch < 0) return Pos(pos.line, 0)\n  else return pos\n}\nexport function clipPosArray(doc, array) {\n  let out = []\n  for (let i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i])\n  return out\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/line/saw_special_spans.js",
    "content": "// Optimize some code when these features are not used.\nexport let sawReadOnlySpans = false, sawCollapsedSpans = false\n\nexport function seeReadOnlySpans() {\n  sawReadOnlySpans = true\n}\n\nexport function seeCollapsedSpans() {\n  sawCollapsedSpans = true\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/line/spans.js",
    "content": "import { indexOf, lst } from \"../util/misc.js\"\n\nimport { cmp } from \"./pos.js\"\nimport { sawCollapsedSpans } from \"./saw_special_spans.js\"\nimport { getLine, isLine, lineNo } from \"./utils_line.js\"\n\n// TEXTMARKER SPANS\n\nexport function MarkedSpan(marker, from, to) {\n  this.marker = marker\n  this.from = from; this.to = to\n}\n\n// Search an array of spans for a span matching the given marker.\nexport function getMarkedSpanFor(spans, marker) {\n  if (spans) for (let i = 0; i < spans.length; ++i) {\n    let span = spans[i]\n    if (span.marker == marker) return span\n  }\n}\n\n// Remove a span from an array, returning undefined if no spans are\n// left (we don't store arrays for lines without spans).\nexport function removeMarkedSpan(spans, span) {\n  let r\n  for (let i = 0; i < spans.length; ++i)\n    if (spans[i] != span) (r || (r = [])).push(spans[i])\n  return r\n}\n\n// Add a span to a line.\nexport function addMarkedSpan(line, span, op) {\n  let inThisOp = op && window.WeakSet && (op.markedSpans || (op.markedSpans = new WeakSet))\n  if (inThisOp && line.markedSpans && inThisOp.has(line.markedSpans)) {\n    line.markedSpans.push(span)\n  } else {\n    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]\n    if (inThisOp) inThisOp.add(line.markedSpans)\n  }\n  span.marker.attachLine(line)\n}\n\n// Used for the algorithm that adjusts markers for a change in the\n// document. These functions cut an array of spans at a given\n// character position, returning an array of remaining chunks (or\n// undefined if nothing remains).\nfunction markedSpansBefore(old, startCh, isInsert) {\n  let nw\n  if (old) for (let i = 0; i < old.length; ++i) {\n    let span = old[i], marker = span.marker\n    let startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh)\n    if (startsBefore || span.from == startCh && marker.type == \"bookmark\" && (!isInsert || !span.marker.insertLeft)) {\n      let endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)\n      ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to))\n    }\n  }\n  return nw\n}\nfunction markedSpansAfter(old, endCh, isInsert) {\n  let nw\n  if (old) for (let i = 0; i < old.length; ++i) {\n    let span = old[i], marker = span.marker\n    let endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh)\n    if (endsAfter || span.from == endCh && marker.type == \"bookmark\" && (!isInsert || span.marker.insertLeft)) {\n      let startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)\n      ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,\n                                            span.to == null ? null : span.to - endCh))\n    }\n  }\n  return nw\n}\n\n// Given a change object, compute the new set of marker spans that\n// cover the line in which the change took place. Removes spans\n// entirely within the change, reconnects spans belonging to the\n// same marker that appear on both sides of the change, and cuts off\n// spans partially within the change. Returns an array of span\n// arrays with one element for each line in (after) the change.\nexport function stretchSpansOverChange(doc, change) {\n  if (change.full) return null\n  let oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans\n  let oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans\n  if (!oldFirst && !oldLast) return null\n\n  let startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0\n  // Get the spans that 'stick out' on both sides\n  let first = markedSpansBefore(oldFirst, startCh, isInsert)\n  let last = markedSpansAfter(oldLast, endCh, isInsert)\n\n  // Next, merge those two ends\n  let sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0)\n  if (first) {\n    // Fix up .to properties of first\n    for (let i = 0; i < first.length; ++i) {\n      let span = first[i]\n      if (span.to == null) {\n        let found = getMarkedSpanFor(last, span.marker)\n        if (!found) span.to = startCh\n        else if (sameLine) span.to = found.to == null ? null : found.to + offset\n      }\n    }\n  }\n  if (last) {\n    // Fix up .from in last (or move them into first in case of sameLine)\n    for (let i = 0; i < last.length; ++i) {\n      let span = last[i]\n      if (span.to != null) span.to += offset\n      if (span.from == null) {\n        let found = getMarkedSpanFor(first, span.marker)\n        if (!found) {\n          span.from = offset\n          if (sameLine) (first || (first = [])).push(span)\n        }\n      } else {\n        span.from += offset\n        if (sameLine) (first || (first = [])).push(span)\n      }\n    }\n  }\n  // Make sure we didn't create any zero-length spans\n  if (first) first = clearEmptySpans(first)\n  if (last && last != first) last = clearEmptySpans(last)\n\n  let newMarkers = [first]\n  if (!sameLine) {\n    // Fill gap with whole-line-spans\n    let gap = change.text.length - 2, gapMarkers\n    if (gap > 0 && first)\n      for (let i = 0; i < first.length; ++i)\n        if (first[i].to == null)\n          (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null))\n    for (let i = 0; i < gap; ++i)\n      newMarkers.push(gapMarkers)\n    newMarkers.push(last)\n  }\n  return newMarkers\n}\n\n// Remove spans that are empty and don't have a clearWhenEmpty\n// option of false.\nfunction clearEmptySpans(spans) {\n  for (let i = 0; i < spans.length; ++i) {\n    let span = spans[i]\n    if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)\n      spans.splice(i--, 1)\n  }\n  if (!spans.length) return null\n  return spans\n}\n\n// Used to 'clip' out readOnly ranges when making a change.\nexport function removeReadOnlyRanges(doc, from, to) {\n  let markers = null\n  doc.iter(from.line, to.line + 1, line => {\n    if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) {\n      let mark = line.markedSpans[i].marker\n      if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))\n        (markers || (markers = [])).push(mark)\n    }\n  })\n  if (!markers) return null\n  let parts = [{from: from, to: to}]\n  for (let i = 0; i < markers.length; ++i) {\n    let mk = markers[i], m = mk.find(0)\n    for (let j = 0; j < parts.length; ++j) {\n      let p = parts[j]\n      if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue\n      let newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to)\n      if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)\n        newParts.push({from: p.from, to: m.from})\n      if (dto > 0 || !mk.inclusiveRight && !dto)\n        newParts.push({from: m.to, to: p.to})\n      parts.splice.apply(parts, newParts)\n      j += newParts.length - 3\n    }\n  }\n  return parts\n}\n\n// Connect or disconnect spans from a line.\nexport function detachMarkedSpans(line) {\n  let spans = line.markedSpans\n  if (!spans) return\n  for (let i = 0; i < spans.length; ++i)\n    spans[i].marker.detachLine(line)\n  line.markedSpans = null\n}\nexport function attachMarkedSpans(line, spans) {\n  if (!spans) return\n  for (let i = 0; i < spans.length; ++i)\n    spans[i].marker.attachLine(line)\n  line.markedSpans = spans\n}\n\n// Helpers used when computing which overlapping collapsed span\n// counts as the larger one.\nfunction extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }\nfunction extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }\n\n// Returns a number indicating which of two overlapping collapsed\n// spans is larger (and thus includes the other). Falls back to\n// comparing ids when the spans cover exactly the same range.\nexport function compareCollapsedMarkers(a, b) {\n  let lenDiff = a.lines.length - b.lines.length\n  if (lenDiff != 0) return lenDiff\n  let aPos = a.find(), bPos = b.find()\n  let fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b)\n  if (fromCmp) return -fromCmp\n  let toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b)\n  if (toCmp) return toCmp\n  return b.id - a.id\n}\n\n// Find out whether a line ends or starts in a collapsed span. If\n// so, return the marker for that span.\nfunction collapsedSpanAtSide(line, start) {\n  let sps = sawCollapsedSpans && line.markedSpans, found\n  if (sps) for (let sp, i = 0; i < sps.length; ++i) {\n    sp = sps[i]\n    if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&\n        (!found || compareCollapsedMarkers(found, sp.marker) < 0))\n      found = sp.marker\n  }\n  return found\n}\nexport function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }\nexport function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }\n\nexport function collapsedSpanAround(line, ch) {\n  let sps = sawCollapsedSpans && line.markedSpans, found\n  if (sps) for (let i = 0; i < sps.length; ++i) {\n    let sp = sps[i]\n    if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&\n        (!found || compareCollapsedMarkers(found, sp.marker) < 0)) found = sp.marker\n  }\n  return found\n}\n\n// Test whether there exists a collapsed span that partially\n// overlaps (covers the start or end, but not both) of a new span.\n// Such overlap is not allowed.\nexport function conflictingCollapsedRange(doc, lineNo, from, to, marker) {\n  let line = getLine(doc, lineNo)\n  let sps = sawCollapsedSpans && line.markedSpans\n  if (sps) for (let i = 0; i < sps.length; ++i) {\n    let sp = sps[i]\n    if (!sp.marker.collapsed) continue\n    let found = sp.marker.find(0)\n    let fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker)\n    let toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker)\n    if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue\n    if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||\n        fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))\n      return true\n  }\n}\n\n// A visual line is a line as drawn on the screen. Folding, for\n// example, can cause multiple logical lines to appear on the same\n// visual line. This finds the start of the visual line that the\n// given line is part of (usually that is the line itself).\nexport function visualLine(line) {\n  let merged\n  while (merged = collapsedSpanAtStart(line))\n    line = merged.find(-1, true).line\n  return line\n}\n\nexport function visualLineEnd(line) {\n  let merged\n  while (merged = collapsedSpanAtEnd(line))\n    line = merged.find(1, true).line\n  return line\n}\n\n// Returns an array of logical lines that continue the visual line\n// started by the argument, or undefined if there are no such lines.\nexport function visualLineContinued(line) {\n  let merged, lines\n  while (merged = collapsedSpanAtEnd(line)) {\n    line = merged.find(1, true).line\n    ;(lines || (lines = [])).push(line)\n  }\n  return lines\n}\n\n// Get the line number of the start of the visual line that the\n// given line number is part of.\nexport function visualLineNo(doc, lineN) {\n  let line = getLine(doc, lineN), vis = visualLine(line)\n  if (line == vis) return lineN\n  return lineNo(vis)\n}\n\n// Get the line number of the start of the next visual line after\n// the given line.\nexport function visualLineEndNo(doc, lineN) {\n  if (lineN > doc.lastLine()) return lineN\n  let line = getLine(doc, lineN), merged\n  if (!lineIsHidden(doc, line)) return lineN\n  while (merged = collapsedSpanAtEnd(line))\n    line = merged.find(1, true).line\n  return lineNo(line) + 1\n}\n\n// Compute whether a line is hidden. Lines count as hidden when they\n// are part of a visual line that starts with another line, or when\n// they are entirely covered by collapsed, non-widget span.\nexport function lineIsHidden(doc, line) {\n  let sps = sawCollapsedSpans && line.markedSpans\n  if (sps) for (let sp, i = 0; i < sps.length; ++i) {\n    sp = sps[i]\n    if (!sp.marker.collapsed) continue\n    if (sp.from == null) return true\n    if (sp.marker.widgetNode) continue\n    if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))\n      return true\n  }\n}\nfunction lineIsHiddenInner(doc, line, span) {\n  if (span.to == null) {\n    let end = span.marker.find(1, true)\n    return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))\n  }\n  if (span.marker.inclusiveRight && span.to == line.text.length)\n    return true\n  for (let sp, i = 0; i < line.markedSpans.length; ++i) {\n    sp = line.markedSpans[i]\n    if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&\n        (sp.to == null || sp.to != span.from) &&\n        (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&\n        lineIsHiddenInner(doc, line, sp)) return true\n  }\n}\n\n// Find the height above the given line.\nexport function heightAtLine(lineObj) {\n  lineObj = visualLine(lineObj)\n\n  let h = 0, chunk = lineObj.parent\n  for (let i = 0; i < chunk.lines.length; ++i) {\n    let line = chunk.lines[i]\n    if (line == lineObj) break\n    else h += line.height\n  }\n  for (let p = chunk.parent; p; chunk = p, p = chunk.parent) {\n    for (let i = 0; i < p.children.length; ++i) {\n      let cur = p.children[i]\n      if (cur == chunk) break\n      else h += cur.height\n    }\n  }\n  return h\n}\n\n// Compute the character length of a line, taking into account\n// collapsed ranges (see markText) that might hide parts, and join\n// other lines onto it.\nexport function lineLength(line) {\n  if (line.height == 0) return 0\n  let len = line.text.length, merged, cur = line\n  while (merged = collapsedSpanAtStart(cur)) {\n    let found = merged.find(0, true)\n    cur = found.from.line\n    len += found.from.ch - found.to.ch\n  }\n  cur = line\n  while (merged = collapsedSpanAtEnd(cur)) {\n    let found = merged.find(0, true)\n    len -= cur.text.length - found.from.ch\n    cur = found.to.line\n    len += cur.text.length - found.to.ch\n  }\n  return len\n}\n\n// Find the longest line in the document.\nexport function findMaxLine(cm) {\n  let d = cm.display, doc = cm.doc\n  d.maxLine = getLine(doc, doc.first)\n  d.maxLineLength = lineLength(d.maxLine)\n  d.maxLineChanged = true\n  doc.iter(line => {\n    let len = lineLength(line)\n    if (len > d.maxLineLength) {\n      d.maxLineLength = len\n      d.maxLine = line\n    }\n  })\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/line/utils_line.js",
    "content": "import { indexOf } from \"../util/misc.js\"\n\n// Find the line object corresponding to the given line number.\nexport function getLine(doc, n) {\n  n -= doc.first\n  if (n < 0 || n >= doc.size) throw new Error(\"There is no line \" + (n + doc.first) + \" in the document.\")\n  let chunk = doc\n  while (!chunk.lines) {\n    for (let i = 0;; ++i) {\n      let child = chunk.children[i], sz = child.chunkSize()\n      if (n < sz) { chunk = child; break }\n      n -= sz\n    }\n  }\n  return chunk.lines[n]\n}\n\n// Get the part of a document between two positions, as an array of\n// strings.\nexport function getBetween(doc, start, end) {\n  let out = [], n = start.line\n  doc.iter(start.line, end.line + 1, line => {\n    let text = line.text\n    if (n == end.line) text = text.slice(0, end.ch)\n    if (n == start.line) text = text.slice(start.ch)\n    out.push(text)\n    ++n\n  })\n  return out\n}\n// Get the lines between from and to, as array of strings.\nexport function getLines(doc, from, to) {\n  let out = []\n  doc.iter(from, to, line => { out.push(line.text) }) // iter aborts when callback returns truthy value\n  return out\n}\n\n// Update the height of a line, propagating the height change\n// upwards to parent nodes.\nexport function updateLineHeight(line, height) {\n  let diff = height - line.height\n  if (diff) for (let n = line; n; n = n.parent) n.height += diff\n}\n\n// Given a line object, find its line number by walking up through\n// its parent links.\nexport function lineNo(line) {\n  if (line.parent == null) return null\n  let cur = line.parent, no = indexOf(cur.lines, line)\n  for (let chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {\n    for (let i = 0;; ++i) {\n      if (chunk.children[i] == cur) break\n      no += chunk.children[i].chunkSize()\n    }\n  }\n  return no + cur.first\n}\n\n// Find the line at the given vertical position, using the height\n// information in the document tree.\nexport function lineAtHeight(chunk, h) {\n  let n = chunk.first\n  outer: do {\n    for (let i = 0; i < chunk.children.length; ++i) {\n      let child = chunk.children[i], ch = child.height\n      if (h < ch) { chunk = child; continue outer }\n      h -= ch\n      n += child.chunkSize()\n    }\n    return n\n  } while (!chunk.lines)\n  let i = 0\n  for (; i < chunk.lines.length; ++i) {\n    let line = chunk.lines[i], lh = line.height\n    if (h < lh) break\n    h -= lh\n  }\n  return n + i\n}\n\nexport function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}\n\nexport function lineNumberFor(options, i) {\n  return String(options.lineNumberFormatter(i + options.firstLineNumber))\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/measurement/position_measurement.js",
    "content": "import { buildLineContent, LineView } from \"../line/line_data.js\"\nimport { clipPos, Pos } from \"../line/pos.js\"\nimport { collapsedSpanAround, heightAtLine, lineIsHidden, visualLine } from \"../line/spans.js\"\nimport { getLine, lineAtHeight, lineNo, updateLineHeight } from \"../line/utils_line.js\"\nimport { bidiOther, getBidiPartAt, getOrder } from \"../util/bidi.js\"\nimport { chrome, android, ie, ie_version } from \"../util/browser.js\"\nimport { elt, removeChildren, range, removeChildrenAndAdd, doc } from \"../util/dom.js\"\nimport { e_target } from \"../util/event.js\"\nimport { hasBadZoomedRects } from \"../util/feature_detection.js\"\nimport { countColumn, findFirst, isExtendingChar, scrollerGap, skipExtendingChars } from \"../util/misc.js\"\nimport { updateLineForChanges } from \"../display/update_line.js\"\n\nimport { widgetHeight } from \"./widgets.js\"\n\n// POSITION MEASUREMENT\n\nexport function paddingTop(display) {return display.lineSpace.offsetTop}\nexport function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}\nexport function paddingH(display) {\n  if (display.cachedPaddingH) return display.cachedPaddingH\n  let e = removeChildrenAndAdd(display.measure, elt(\"pre\", \"x\", \"CodeMirror-line-like\"))\n  let style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle\n  let data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}\n  if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data\n  return data\n}\n\nexport function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }\nexport function displayWidth(cm) {\n  return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth\n}\nexport function displayHeight(cm) {\n  return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight\n}\n\n// Ensure the lineView.wrapping.heights array is populated. This is\n// an array of bottom offsets for the lines that make up a drawn\n// line. When lineWrapping is on, there might be more than one\n// height.\nfunction ensureLineHeights(cm, lineView, rect) {\n  let wrapping = cm.options.lineWrapping\n  let curWidth = wrapping && displayWidth(cm)\n  if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {\n    let heights = lineView.measure.heights = []\n    if (wrapping) {\n      lineView.measure.width = curWidth\n      let rects = lineView.text.firstChild.getClientRects()\n      for (let i = 0; i < rects.length - 1; i++) {\n        let cur = rects[i], next = rects[i + 1]\n        if (Math.abs(cur.bottom - next.bottom) > 2)\n          heights.push((cur.bottom + next.top) / 2 - rect.top)\n      }\n    }\n    heights.push(rect.bottom - rect.top)\n  }\n}\n\n// Find a line map (mapping character offsets to text nodes) and a\n// measurement cache for the given line number. (A line view might\n// contain multiple lines when collapsed ranges are present.)\nexport function mapFromLineView(lineView, line, lineN) {\n  if (lineView.line == line)\n    return {map: lineView.measure.map, cache: lineView.measure.cache}\n  if (lineView.rest) {\n    for (let i = 0; i < lineView.rest.length; i++)\n      if (lineView.rest[i] == line)\n        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]}\n    for (let i = 0; i < lineView.rest.length; i++)\n      if (lineNo(lineView.rest[i]) > lineN)\n        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true}\n  }\n}\n\n// Render a line into the hidden node display.externalMeasured. Used\n// when measurement is needed for a line that's not in the viewport.\nfunction updateExternalMeasurement(cm, line) {\n  line = visualLine(line)\n  let lineN = lineNo(line)\n  let view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN)\n  view.lineN = lineN\n  let built = view.built = buildLineContent(cm, view)\n  view.text = built.pre\n  removeChildrenAndAdd(cm.display.lineMeasure, built.pre)\n  return view\n}\n\n// Get a {top, bottom, left, right} box (in line-local coordinates)\n// for a given character.\nexport function measureChar(cm, line, ch, bias) {\n  return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)\n}\n\n// Find a line view that corresponds to the given line number.\nexport function findViewForLine(cm, lineN) {\n  if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)\n    return cm.display.view[findViewIndex(cm, lineN)]\n  let ext = cm.display.externalMeasured\n  if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)\n    return ext\n}\n\n// Measurement can be split in two steps, the set-up work that\n// applies to the whole line, and the measurement of the actual\n// character. Functions like coordsChar, that need to do a lot of\n// measurements in a row, can thus ensure that the set-up work is\n// only done once.\nexport function prepareMeasureForLine(cm, line) {\n  let lineN = lineNo(line)\n  let view = findViewForLine(cm, lineN)\n  if (view && !view.text) {\n    view = null\n  } else if (view && view.changes) {\n    updateLineForChanges(cm, view, lineN, getDimensions(cm))\n    cm.curOp.forceUpdate = true\n  }\n  if (!view)\n    view = updateExternalMeasurement(cm, line)\n\n  let info = mapFromLineView(view, line, lineN)\n  return {\n    line: line, view: view, rect: null,\n    map: info.map, cache: info.cache, before: info.before,\n    hasHeights: false\n  }\n}\n\n// Given a prepared measurement object, measures the position of an\n// actual character (or fetches it from the cache).\nexport function measureCharPrepared(cm, prepared, ch, bias, varHeight) {\n  if (prepared.before) ch = -1\n  let key = ch + (bias || \"\"), found\n  if (prepared.cache.hasOwnProperty(key)) {\n    found = prepared.cache[key]\n  } else {\n    if (!prepared.rect)\n      prepared.rect = prepared.view.text.getBoundingClientRect()\n    if (!prepared.hasHeights) {\n      ensureLineHeights(cm, prepared.view, prepared.rect)\n      prepared.hasHeights = true\n    }\n    found = measureCharInner(cm, prepared, ch, bias)\n    if (!found.bogus) prepared.cache[key] = found\n  }\n  return {left: found.left, right: found.right,\n          top: varHeight ? found.rtop : found.top,\n          bottom: varHeight ? found.rbottom : found.bottom}\n}\n\nlet nullRect = {left: 0, right: 0, top: 0, bottom: 0}\n\nexport function nodeAndOffsetInLineMap(map, ch, bias) {\n  let node, start, end, collapse, mStart, mEnd\n  // First, search the line map for the text node corresponding to,\n  // or closest to, the target character.\n  for (let i = 0; i < map.length; i += 3) {\n    mStart = map[i]\n    mEnd = map[i + 1]\n    if (ch < mStart) {\n      start = 0; end = 1\n      collapse = \"left\"\n    } else if (ch < mEnd) {\n      start = ch - mStart\n      end = start + 1\n    } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {\n      end = mEnd - mStart\n      start = end - 1\n      if (ch >= mEnd) collapse = \"right\"\n    }\n    if (start != null) {\n      node = map[i + 2]\n      if (mStart == mEnd && bias == (node.insertLeft ? \"left\" : \"right\"))\n        collapse = bias\n      if (bias == \"left\" && start == 0)\n        while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {\n          node = map[(i -= 3) + 2]\n          collapse = \"left\"\n        }\n      if (bias == \"right\" && start == mEnd - mStart)\n        while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {\n          node = map[(i += 3) + 2]\n          collapse = \"right\"\n        }\n      break\n    }\n  }\n  return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}\n}\n\nfunction getUsefulRect(rects, bias) {\n  let rect = nullRect\n  if (bias == \"left\") for (let i = 0; i < rects.length; i++) {\n    if ((rect = rects[i]).left != rect.right) break\n  } else for (let i = rects.length - 1; i >= 0; i--) {\n    if ((rect = rects[i]).left != rect.right) break\n  }\n  return rect\n}\n\nfunction measureCharInner(cm, prepared, ch, bias) {\n  let place = nodeAndOffsetInLineMap(prepared.map, ch, bias)\n  let node = place.node, start = place.start, end = place.end, collapse = place.collapse\n\n  let rect\n  if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.\n    for (let i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned\n      while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start\n      while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end\n      if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)\n        rect = node.parentNode.getBoundingClientRect()\n      else\n        rect = getUsefulRect(range(node, start, end).getClientRects(), bias)\n      if (rect.left || rect.right || start == 0) break\n      end = start\n      start = start - 1\n      collapse = \"right\"\n    }\n    if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect)\n  } else { // If it is a widget, simply get the box for the whole widget.\n    if (start > 0) collapse = bias = \"right\"\n    let rects\n    if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)\n      rect = rects[bias == \"right\" ? rects.length - 1 : 0]\n    else\n      rect = node.getBoundingClientRect()\n  }\n  if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {\n    let rSpan = node.parentNode.getClientRects()[0]\n    if (rSpan)\n      rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}\n    else\n      rect = nullRect\n  }\n\n  let rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top\n  let mid = (rtop + rbot) / 2\n  let heights = prepared.view.measure.heights\n  let i = 0\n  for (; i < heights.length - 1; i++)\n    if (mid < heights[i]) break\n  let top = i ? heights[i - 1] : 0, bot = heights[i]\n  let result = {left: (collapse == \"right\" ? rect.right : rect.left) - prepared.rect.left,\n                right: (collapse == \"left\" ? rect.left : rect.right) - prepared.rect.left,\n                top: top, bottom: bot}\n  if (!rect.left && !rect.right) result.bogus = true\n  if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot }\n\n  return result\n}\n\n// Work around problem with bounding client rects on ranges being\n// returned incorrectly when zoomed on IE10 and below.\nfunction maybeUpdateRectForZooming(measure, rect) {\n  if (!window.screen || screen.logicalXDPI == null ||\n      screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))\n    return rect\n  let scaleX = screen.logicalXDPI / screen.deviceXDPI\n  let scaleY = screen.logicalYDPI / screen.deviceYDPI\n  return {left: rect.left * scaleX, right: rect.right * scaleX,\n          top: rect.top * scaleY, bottom: rect.bottom * scaleY}\n}\n\nexport function clearLineMeasurementCacheFor(lineView) {\n  if (lineView.measure) {\n    lineView.measure.cache = {}\n    lineView.measure.heights = null\n    if (lineView.rest) for (let i = 0; i < lineView.rest.length; i++)\n      lineView.measure.caches[i] = {}\n  }\n}\n\nexport function clearLineMeasurementCache(cm) {\n  cm.display.externalMeasure = null\n  removeChildren(cm.display.lineMeasure)\n  for (let i = 0; i < cm.display.view.length; i++)\n    clearLineMeasurementCacheFor(cm.display.view[i])\n}\n\nexport function clearCaches(cm) {\n  clearLineMeasurementCache(cm)\n  cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null\n  if (!cm.options.lineWrapping) cm.display.maxLineChanged = true\n  cm.display.lineNumChars = null\n}\n\nfunction pageScrollX(doc) {\n  // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206\n  // which causes page_Offset and bounding client rects to use\n  // different reference viewports and invalidate our calculations.\n  if (chrome && android) return -(doc.body.getBoundingClientRect().left - parseInt(getComputedStyle(doc.body).marginLeft))\n  return doc.defaultView.pageXOffset || (doc.documentElement || doc.body).scrollLeft\n}\nfunction pageScrollY(doc) {\n  if (chrome && android) return -(doc.body.getBoundingClientRect().top - parseInt(getComputedStyle(doc.body).marginTop))\n  return doc.defaultView.pageYOffset || (doc.documentElement || doc.body).scrollTop\n}\n\nfunction widgetTopHeight(lineObj) {\n  let {widgets} = visualLine(lineObj), height = 0\n  if (widgets) for (let i = 0; i < widgets.length; ++i) if (widgets[i].above)\n    height += widgetHeight(widgets[i])\n  return height\n}\n\n// Converts a {top, bottom, left, right} box from line-local\n// coordinates into another coordinate system. Context may be one of\n// \"line\", \"div\" (display.lineDiv), \"local\"./null (editor), \"window\",\n// or \"page\".\nexport function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {\n  if (!includeWidgets) {\n    let height = widgetTopHeight(lineObj)\n    rect.top += height; rect.bottom += height\n  }\n  if (context == \"line\") return rect\n  if (!context) context = \"local\"\n  let yOff = heightAtLine(lineObj)\n  if (context == \"local\") yOff += paddingTop(cm.display)\n  else yOff -= cm.display.viewOffset\n  if (context == \"page\" || context == \"window\") {\n    let lOff = cm.display.lineSpace.getBoundingClientRect()\n    yOff += lOff.top + (context == \"window\" ? 0 : pageScrollY(doc(cm)))\n    let xOff = lOff.left + (context == \"window\" ? 0 : pageScrollX(doc(cm)))\n    rect.left += xOff; rect.right += xOff\n  }\n  rect.top += yOff; rect.bottom += yOff\n  return rect\n}\n\n// Coverts a box from \"div\" coords to another coordinate system.\n// Context may be \"window\", \"page\", \"div\", or \"local\"./null.\nexport function fromCoordSystem(cm, coords, context) {\n  if (context == \"div\") return coords\n  let left = coords.left, top = coords.top\n  // First move into \"page\" coordinate system\n  if (context == \"page\") {\n    left -= pageScrollX(doc(cm))\n    top -= pageScrollY(doc(cm))\n  } else if (context == \"local\" || !context) {\n    let localBox = cm.display.sizer.getBoundingClientRect()\n    left += localBox.left\n    top += localBox.top\n  }\n\n  let lineSpaceBox = cm.display.lineSpace.getBoundingClientRect()\n  return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}\n}\n\nexport function charCoords(cm, pos, context, lineObj, bias) {\n  if (!lineObj) lineObj = getLine(cm.doc, pos.line)\n  return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)\n}\n\n// Returns a box for a given cursor position, which may have an\n// 'other' property containing the position of the secondary cursor\n// on a bidi boundary.\n// A cursor Pos(line, char, \"before\") is on the same visual line as `char - 1`\n// and after `char - 1` in writing order of `char - 1`\n// A cursor Pos(line, char, \"after\") is on the same visual line as `char`\n// and before `char` in writing order of `char`\n// Examples (upper-case letters are RTL, lower-case are LTR):\n//     Pos(0, 1, ...)\n//     before   after\n// ab     a|b     a|b\n// aB     a|B     aB|\n// Ab     |Ab     A|b\n// AB     B|A     B|A\n// Every position after the last character on a line is considered to stick\n// to the last character on the line.\nexport function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {\n  lineObj = lineObj || getLine(cm.doc, pos.line)\n  if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj)\n  function get(ch, right) {\n    let m = measureCharPrepared(cm, preparedMeasure, ch, right ? \"right\" : \"left\", varHeight)\n    if (right) m.left = m.right; else m.right = m.left\n    return intoCoordSystem(cm, lineObj, m, context)\n  }\n  let order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky\n  if (ch >= lineObj.text.length) {\n    ch = lineObj.text.length\n    sticky = \"before\"\n  } else if (ch <= 0) {\n    ch = 0\n    sticky = \"after\"\n  }\n  if (!order) return get(sticky == \"before\" ? ch - 1 : ch, sticky == \"before\")\n\n  function getBidi(ch, partPos, invert) {\n    let part = order[partPos], right = part.level == 1\n    return get(invert ? ch - 1 : ch, right != invert)\n  }\n  let partPos = getBidiPartAt(order, ch, sticky)\n  let other = bidiOther\n  let val = getBidi(ch, partPos, sticky == \"before\")\n  if (other != null) val.other = getBidi(ch, other, sticky != \"before\")\n  return val\n}\n\n// Used to cheaply estimate the coordinates for a position. Used for\n// intermediate scroll updates.\nexport function estimateCoords(cm, pos) {\n  let left = 0\n  pos = clipPos(cm.doc, pos)\n  if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch\n  let lineObj = getLine(cm.doc, pos.line)\n  let top = heightAtLine(lineObj) + paddingTop(cm.display)\n  return {left: left, right: left, top: top, bottom: top + lineObj.height}\n}\n\n// Positions returned by coordsChar contain some extra information.\n// xRel is the relative x position of the input coordinates compared\n// to the found position (so xRel > 0 means the coordinates are to\n// the right of the character position, for example). When outside\n// is true, that means the coordinates lie outside the line's\n// vertical range.\nfunction PosWithInfo(line, ch, sticky, outside, xRel) {\n  let pos = Pos(line, ch, sticky)\n  pos.xRel = xRel\n  if (outside) pos.outside = outside\n  return pos\n}\n\n// Compute the character position closest to the given coordinates.\n// Input must be lineSpace-local (\"div\" coordinate system).\nexport function coordsChar(cm, x, y) {\n  let doc = cm.doc\n  y += cm.display.viewOffset\n  if (y < 0) return PosWithInfo(doc.first, 0, null, -1, -1)\n  let lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1\n  if (lineN > last)\n    return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1)\n  if (x < 0) x = 0\n\n  let lineObj = getLine(doc, lineN)\n  for (;;) {\n    let found = coordsCharInner(cm, lineObj, lineN, x, y)\n    let collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0))\n    if (!collapsed) return found\n    let rangeEnd = collapsed.find(1)\n    if (rangeEnd.line == lineN) return rangeEnd\n    lineObj = getLine(doc, lineN = rangeEnd.line)\n  }\n}\n\nfunction wrappedLineExtent(cm, lineObj, preparedMeasure, y) {\n  y -= widgetTopHeight(lineObj)\n  let end = lineObj.text.length\n  let begin = findFirst(ch => measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y, end, 0)\n  end = findFirst(ch => measureCharPrepared(cm, preparedMeasure, ch).top > y, begin, end)\n  return {begin, end}\n}\n\nexport function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {\n  if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj)\n  let targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), \"line\").top\n  return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)\n}\n\n// Returns true if the given side of a box is after the given\n// coordinates, in top-to-bottom, left-to-right order.\nfunction boxIsAfter(box, x, y, left) {\n  return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x\n}\n\nfunction coordsCharInner(cm, lineObj, lineNo, x, y) {\n  // Move y into line-local coordinate space\n  y -= heightAtLine(lineObj)\n  let preparedMeasure = prepareMeasureForLine(cm, lineObj)\n  // When directly calling `measureCharPrepared`, we have to adjust\n  // for the widgets at this line.\n  let widgetHeight = widgetTopHeight(lineObj)\n  let begin = 0, end = lineObj.text.length, ltr = true\n\n  let order = getOrder(lineObj, cm.doc.direction)\n  // If the line isn't plain left-to-right text, first figure out\n  // which bidi section the coordinates fall into.\n  if (order) {\n    let part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)\n                 (cm, lineObj, lineNo, preparedMeasure, order, x, y)\n    ltr = part.level != 1\n    // The awkward -1 offsets are needed because findFirst (called\n    // on these below) will treat its first bound as inclusive,\n    // second as exclusive, but we want to actually address the\n    // characters in the part's range\n    begin = ltr ? part.from : part.to - 1\n    end = ltr ? part.to : part.from - 1\n  }\n\n  // A binary search to find the first character whose bounding box\n  // starts after the coordinates. If we run across any whose box wrap\n  // the coordinates, store that.\n  let chAround = null, boxAround = null\n  let ch = findFirst(ch => {\n    let box = measureCharPrepared(cm, preparedMeasure, ch)\n    box.top += widgetHeight; box.bottom += widgetHeight\n    if (!boxIsAfter(box, x, y, false)) return false\n    if (box.top <= y && box.left <= x) {\n      chAround = ch\n      boxAround = box\n    }\n    return true\n  }, begin, end)\n\n  let baseX, sticky, outside = false\n  // If a box around the coordinates was found, use that\n  if (boxAround) {\n    // Distinguish coordinates nearer to the left or right side of the box\n    let atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr\n    ch = chAround + (atStart ? 0 : 1)\n    sticky = atStart ? \"after\" : \"before\"\n    baseX = atLeft ? boxAround.left : boxAround.right\n  } else {\n    // (Adjust for extended bound, if necessary.)\n    if (!ltr && (ch == end || ch == begin)) ch++\n    // To determine which side to associate with, get the box to the\n    // left of the character and compare it's vertical position to the\n    // coordinates\n    sticky = ch == 0 ? \"after\" : ch == lineObj.text.length ? \"before\" :\n      (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?\n      \"after\" : \"before\"\n    // Now get accurate coordinates for this place, in order to get a\n    // base X position\n    let coords = cursorCoords(cm, Pos(lineNo, ch, sticky), \"line\", lineObj, preparedMeasure)\n    baseX = coords.left\n    outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0\n  }\n\n  ch = skipExtendingChars(lineObj.text, ch, 1)\n  return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)\n}\n\nfunction coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {\n  // Bidi parts are sorted left-to-right, and in a non-line-wrapping\n  // situation, we can take this ordering to correspond to the visual\n  // ordering. This finds the first part whose end is after the given\n  // coordinates.\n  let index = findFirst(i => {\n    let part = order[i], ltr = part.level != 1\n    return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? \"before\" : \"after\"),\n                                   \"line\", lineObj, preparedMeasure), x, y, true)\n  }, 0, order.length - 1)\n  let part = order[index]\n  // If this isn't the first part, the part's start is also after\n  // the coordinates, and the coordinates aren't on the same line as\n  // that start, move one part back.\n  if (index > 0) {\n    let ltr = part.level != 1\n    let start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? \"after\" : \"before\"),\n                             \"line\", lineObj, preparedMeasure)\n    if (boxIsAfter(start, x, y, true) && start.top > y)\n      part = order[index - 1]\n  }\n  return part\n}\n\nfunction coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {\n  // In a wrapped line, rtl text on wrapping boundaries can do things\n  // that don't correspond to the ordering in our `order` array at\n  // all, so a binary search doesn't work, and we want to return a\n  // part that only spans one line so that the binary search in\n  // coordsCharInner is safe. As such, we first find the extent of the\n  // wrapped line, and then do a flat search in which we discard any\n  // spans that aren't on the line.\n  let {begin, end} = wrappedLineExtent(cm, lineObj, preparedMeasure, y)\n  if (/\\s/.test(lineObj.text.charAt(end - 1))) end--\n  let part = null, closestDist = null\n  for (let i = 0; i < order.length; i++) {\n    let p = order[i]\n    if (p.from >= end || p.to <= begin) continue\n    let ltr = p.level != 1\n    let endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right\n    // Weigh against spans ending before this, so that they are only\n    // picked if nothing ends after\n    let dist = endX < x ? x - endX + 1e9 : endX - x\n    if (!part || closestDist > dist) {\n      part = p\n      closestDist = dist\n    }\n  }\n  if (!part) part = order[order.length - 1]\n  // Clip the part to the wrapped line.\n  if (part.from < begin) part = {from: begin, to: part.to, level: part.level}\n  if (part.to > end) part = {from: part.from, to: end, level: part.level}\n  return part\n}\n\nlet measureText\n// Compute the default text height.\nexport function textHeight(display) {\n  if (display.cachedTextHeight != null) return display.cachedTextHeight\n  if (measureText == null) {\n    measureText = elt(\"pre\", null, \"CodeMirror-line-like\")\n    // Measure a bunch of lines, for browsers that compute\n    // fractional heights.\n    for (let i = 0; i < 49; ++i) {\n      measureText.appendChild(document.createTextNode(\"x\"))\n      measureText.appendChild(elt(\"br\"))\n    }\n    measureText.appendChild(document.createTextNode(\"x\"))\n  }\n  removeChildrenAndAdd(display.measure, measureText)\n  let height = measureText.offsetHeight / 50\n  if (height > 3) display.cachedTextHeight = height\n  removeChildren(display.measure)\n  return height || 1\n}\n\n// Compute the default character width.\nexport function charWidth(display) {\n  if (display.cachedCharWidth != null) return display.cachedCharWidth\n  let anchor = elt(\"span\", \"xxxxxxxxxx\")\n  let pre = elt(\"pre\", [anchor], \"CodeMirror-line-like\")\n  removeChildrenAndAdd(display.measure, pre)\n  let rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10\n  if (width > 2) display.cachedCharWidth = width\n  return width || 10\n}\n\n// Do a bulk-read of the DOM positions and sizes needed to draw the\n// view, so that we don't interleave reading and writing to the DOM.\nexport function getDimensions(cm) {\n  let d = cm.display, left = {}, width = {}\n  let gutterLeft = d.gutters.clientLeft\n  for (let n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {\n    let id = cm.display.gutterSpecs[i].className\n    left[id] = n.offsetLeft + n.clientLeft + gutterLeft\n    width[id] = n.clientWidth\n  }\n  return {fixedPos: compensateForHScroll(d),\n          gutterTotalWidth: d.gutters.offsetWidth,\n          gutterLeft: left,\n          gutterWidth: width,\n          wrapperWidth: d.wrapper.clientWidth}\n}\n\n// Computes display.scroller.scrollLeft + display.gutters.offsetWidth,\n// but using getBoundingClientRect to get a sub-pixel-accurate\n// result.\nexport function compensateForHScroll(display) {\n  return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left\n}\n\n// Returns a function that estimates the height of a line, to use as\n// first approximation until the line becomes visible (and is thus\n// properly measurable).\nexport function estimateHeight(cm) {\n  let th = textHeight(cm.display), wrapping = cm.options.lineWrapping\n  let perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3)\n  return line => {\n    if (lineIsHidden(cm.doc, line)) return 0\n\n    let widgetsHeight = 0\n    if (line.widgets) for (let i = 0; i < line.widgets.length; i++) {\n      if (line.widgets[i].height) widgetsHeight += line.widgets[i].height\n    }\n\n    if (wrapping)\n      return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th\n    else\n      return widgetsHeight + th\n  }\n}\n\nexport function estimateLineHeights(cm) {\n  let doc = cm.doc, est = estimateHeight(cm)\n  doc.iter(line => {\n    let estHeight = est(line)\n    if (estHeight != line.height) updateLineHeight(line, estHeight)\n  })\n}\n\n// Given a mouse event, find the corresponding position. If liberal\n// is false, it checks whether a gutter or scrollbar was clicked,\n// and returns null if it was. forRect is used by rectangular\n// selections, and tries to estimate a character position even for\n// coordinates beyond the right of the text.\nexport function posFromMouse(cm, e, liberal, forRect) {\n  let display = cm.display\n  if (!liberal && e_target(e).getAttribute(\"cm-not-content\") == \"true\") return null\n\n  let x, y, space = display.lineSpace.getBoundingClientRect()\n  // Fails unpredictably on IE[67] when mouse is dragged around quickly.\n  try { x = e.clientX - space.left; y = e.clientY - space.top }\n  catch (e) { return null }\n  let coords = coordsChar(cm, x, y), line\n  if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {\n    let colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length\n    coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff))\n  }\n  return coords\n}\n\n// Find the view element corresponding to a given line. Return null\n// when the line isn't visible.\nexport function findViewIndex(cm, n) {\n  if (n >= cm.display.viewTo) return null\n  n -= cm.display.viewFrom\n  if (n < 0) return null\n  let view = cm.display.view\n  for (let i = 0; i < view.length; i++) {\n    n -= view[i].size\n    if (n < 0) return i\n  }\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/measurement/widgets.js",
    "content": "import { contains, elt, removeChildrenAndAdd } from \"../util/dom.js\"\nimport { e_target } from \"../util/event.js\"\n\nexport function widgetHeight(widget) {\n  if (widget.height != null) return widget.height\n  let cm = widget.doc.cm\n  if (!cm) return 0\n  if (!contains(document.body, widget.node)) {\n    let parentStyle = \"position: relative;\"\n    if (widget.coverGutter)\n      parentStyle += \"margin-left: -\" + cm.display.gutters.offsetWidth + \"px;\"\n    if (widget.noHScroll)\n      parentStyle += \"width: \" + cm.display.wrapper.clientWidth + \"px;\"\n    removeChildrenAndAdd(cm.display.measure, elt(\"div\", [widget.node], null, parentStyle))\n  }\n  return widget.height = widget.node.parentNode.offsetHeight\n}\n\n// Return true when the given mouse event happened in a widget\nexport function eventInWidget(display, e) {\n  for (let n = e_target(e); n != display.wrapper; n = n.parentNode) {\n    if (!n || (n.nodeType == 1 && n.getAttribute(\"cm-ignore-events\") == \"true\") ||\n        (n.parentNode == display.sizer && n != display.mover))\n      return true\n  }\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/model/Doc.js",
    "content": "import CodeMirror from \"../edit/CodeMirror.js\"\nimport { docMethodOp } from \"../display/operations.js\"\nimport { Line } from \"../line/line_data.js\"\nimport { clipPos, clipPosArray, Pos } from \"../line/pos.js\"\nimport { visualLine } from \"../line/spans.js\"\nimport { getBetween, getLine, getLines, isLine, lineNo } from \"../line/utils_line.js\"\nimport { classTest } from \"../util/dom.js\"\nimport { splitLinesAuto } from \"../util/feature_detection.js\"\nimport { createObj, map, isEmpty, sel_dontScroll } from \"../util/misc.js\"\nimport { ensureCursorVisible, scrollToCoords } from \"../display/scrolling.js\"\n\nimport { changeLine, makeChange, makeChangeFromHistory, replaceRange } from \"./changes.js\"\nimport { computeReplacedSel } from \"./change_measurement.js\"\nimport { BranchChunk, LeafChunk } from \"./chunk.js\"\nimport { directionChanged, linkedDocs, updateDoc } from \"./document_data.js\"\nimport { copyHistoryArray, History } from \"./history.js\"\nimport { addLineWidget } from \"./line_widget.js\"\nimport { copySharedMarkers, detachSharedMarkers, findSharedMarkers, markText } from \"./mark_text.js\"\nimport { normalizeSelection, Range, simpleSelection } from \"./selection.js\"\nimport { extendSelection, extendSelections, setSelection, setSelectionReplaceHistory, setSimpleSelection } from \"./selection_updates.js\"\n\nlet nextDocId = 0\nlet Doc = function(text, mode, firstLine, lineSep, direction) {\n  if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep, direction)\n  if (firstLine == null) firstLine = 0\n\n  BranchChunk.call(this, [new LeafChunk([new Line(\"\", null)])])\n  this.first = firstLine\n  this.scrollTop = this.scrollLeft = 0\n  this.cantEdit = false\n  this.cleanGeneration = 1\n  this.modeFrontier = this.highlightFrontier = firstLine\n  let start = Pos(firstLine, 0)\n  this.sel = simpleSelection(start)\n  this.history = new History(null)\n  this.id = ++nextDocId\n  this.modeOption = mode\n  this.lineSep = lineSep\n  this.direction = (direction == \"rtl\") ? \"rtl\" : \"ltr\"\n  this.extend = false\n\n  if (typeof text == \"string\") text = this.splitLines(text)\n  updateDoc(this, {from: start, to: start, text: text})\n  setSelection(this, simpleSelection(start), sel_dontScroll)\n}\n\nDoc.prototype = createObj(BranchChunk.prototype, {\n  constructor: Doc,\n  // Iterate over the document. Supports two forms -- with only one\n  // argument, it calls that for each line in the document. With\n  // three, it iterates over the range given by the first two (with\n  // the second being non-inclusive).\n  iter: function(from, to, op) {\n    if (op) this.iterN(from - this.first, to - from, op)\n    else this.iterN(this.first, this.first + this.size, from)\n  },\n\n  // Non-public interface for adding and removing lines.\n  insert: function(at, lines) {\n    let height = 0\n    for (let i = 0; i < lines.length; ++i) height += lines[i].height\n    this.insertInner(at - this.first, lines, height)\n  },\n  remove: function(at, n) { this.removeInner(at - this.first, n) },\n\n  // From here, the methods are part of the public interface. Most\n  // are also available from CodeMirror (editor) instances.\n\n  getValue: function(lineSep) {\n    let lines = getLines(this, this.first, this.first + this.size)\n    if (lineSep === false) return lines\n    return lines.join(lineSep || this.lineSeparator())\n  },\n  setValue: docMethodOp(function(code) {\n    let top = Pos(this.first, 0), last = this.first + this.size - 1\n    makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),\n                      text: this.splitLines(code), origin: \"setValue\", full: true}, true)\n    if (this.cm) scrollToCoords(this.cm, 0, 0)\n    setSelection(this, simpleSelection(top), sel_dontScroll)\n  }),\n  replaceRange: function(code, from, to, origin) {\n    from = clipPos(this, from)\n    to = to ? clipPos(this, to) : from\n    replaceRange(this, code, from, to, origin)\n  },\n  getRange: function(from, to, lineSep) {\n    let lines = getBetween(this, clipPos(this, from), clipPos(this, to))\n    if (lineSep === false) return lines\n    if (lineSep === '') return lines.join('')\n    return lines.join(lineSep || this.lineSeparator())\n  },\n\n  getLine: function(line) {let l = this.getLineHandle(line); return l && l.text},\n\n  getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line)},\n  getLineNumber: function(line) {return lineNo(line)},\n\n  getLineHandleVisualStart: function(line) {\n    if (typeof line == \"number\") line = getLine(this, line)\n    return visualLine(line)\n  },\n\n  lineCount: function() {return this.size},\n  firstLine: function() {return this.first},\n  lastLine: function() {return this.first + this.size - 1},\n\n  clipPos: function(pos) {return clipPos(this, pos)},\n\n  getCursor: function(start) {\n    let range = this.sel.primary(), pos\n    if (start == null || start == \"head\") pos = range.head\n    else if (start == \"anchor\") pos = range.anchor\n    else if (start == \"end\" || start == \"to\" || start === false) pos = range.to()\n    else pos = range.from()\n    return pos\n  },\n  listSelections: function() { return this.sel.ranges },\n  somethingSelected: function() {return this.sel.somethingSelected()},\n\n  setCursor: docMethodOp(function(line, ch, options) {\n    setSimpleSelection(this, clipPos(this, typeof line == \"number\" ? Pos(line, ch || 0) : line), null, options)\n  }),\n  setSelection: docMethodOp(function(anchor, head, options) {\n    setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)\n  }),\n  extendSelection: docMethodOp(function(head, other, options) {\n    extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)\n  }),\n  extendSelections: docMethodOp(function(heads, options) {\n    extendSelections(this, clipPosArray(this, heads), options)\n  }),\n  extendSelectionsBy: docMethodOp(function(f, options) {\n    let heads = map(this.sel.ranges, f)\n    extendSelections(this, clipPosArray(this, heads), options)\n  }),\n  setSelections: docMethodOp(function(ranges, primary, options) {\n    if (!ranges.length) return\n    let out = []\n    for (let i = 0; i < ranges.length; i++)\n      out[i] = new Range(clipPos(this, ranges[i].anchor),\n                         clipPos(this, ranges[i].head || ranges[i].anchor))\n    if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex)\n    setSelection(this, normalizeSelection(this.cm, out, primary), options)\n  }),\n  addSelection: docMethodOp(function(anchor, head, options) {\n    let ranges = this.sel.ranges.slice(0)\n    ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))\n    setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options)\n  }),\n\n  getSelection: function(lineSep) {\n    let ranges = this.sel.ranges, lines\n    for (let i = 0; i < ranges.length; i++) {\n      let sel = getBetween(this, ranges[i].from(), ranges[i].to())\n      lines = lines ? lines.concat(sel) : sel\n    }\n    if (lineSep === false) return lines\n    else return lines.join(lineSep || this.lineSeparator())\n  },\n  getSelections: function(lineSep) {\n    let parts = [], ranges = this.sel.ranges\n    for (let i = 0; i < ranges.length; i++) {\n      let sel = getBetween(this, ranges[i].from(), ranges[i].to())\n      if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator())\n      parts[i] = sel\n    }\n    return parts\n  },\n  replaceSelection: function(code, collapse, origin) {\n    let dup = []\n    for (let i = 0; i < this.sel.ranges.length; i++)\n      dup[i] = code\n    this.replaceSelections(dup, collapse, origin || \"+input\")\n  },\n  replaceSelections: docMethodOp(function(code, collapse, origin) {\n    let changes = [], sel = this.sel\n    for (let i = 0; i < sel.ranges.length; i++) {\n      let range = sel.ranges[i]\n      changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}\n    }\n    let newSel = collapse && collapse != \"end\" && computeReplacedSel(this, changes, collapse)\n    for (let i = changes.length - 1; i >= 0; i--)\n      makeChange(this, changes[i])\n    if (newSel) setSelectionReplaceHistory(this, newSel)\n    else if (this.cm) ensureCursorVisible(this.cm)\n  }),\n  undo: docMethodOp(function() {makeChangeFromHistory(this, \"undo\")}),\n  redo: docMethodOp(function() {makeChangeFromHistory(this, \"redo\")}),\n  undoSelection: docMethodOp(function() {makeChangeFromHistory(this, \"undo\", true)}),\n  redoSelection: docMethodOp(function() {makeChangeFromHistory(this, \"redo\", true)}),\n\n  setExtending: function(val) {this.extend = val},\n  getExtending: function() {return this.extend},\n\n  historySize: function() {\n    let hist = this.history, done = 0, undone = 0\n    for (let i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done\n    for (let i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone\n    return {undo: done, redo: undone}\n  },\n  clearHistory: function() {\n    this.history = new History(this.history)\n    linkedDocs(this, doc => doc.history = this.history, true)\n  },\n\n  markClean: function() {\n    this.cleanGeneration = this.changeGeneration(true)\n  },\n  changeGeneration: function(forceSplit) {\n    if (forceSplit)\n      this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null\n    return this.history.generation\n  },\n  isClean: function (gen) {\n    return this.history.generation == (gen || this.cleanGeneration)\n  },\n\n  getHistory: function() {\n    return {done: copyHistoryArray(this.history.done),\n            undone: copyHistoryArray(this.history.undone)}\n  },\n  setHistory: function(histData) {\n    let hist = this.history = new History(this.history)\n    hist.done = copyHistoryArray(histData.done.slice(0), null, true)\n    hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)\n  },\n\n  setGutterMarker: docMethodOp(function(line, gutterID, value) {\n    return changeLine(this, line, \"gutter\", line => {\n      let markers = line.gutterMarkers || (line.gutterMarkers = {})\n      markers[gutterID] = value\n      if (!value && isEmpty(markers)) line.gutterMarkers = null\n      return true\n    })\n  }),\n\n  clearGutter: docMethodOp(function(gutterID) {\n    this.iter(line => {\n      if (line.gutterMarkers && line.gutterMarkers[gutterID]) {\n        changeLine(this, line, \"gutter\", () => {\n          line.gutterMarkers[gutterID] = null\n          if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null\n          return true\n        })\n      }\n    })\n  }),\n\n  lineInfo: function(line) {\n    let n\n    if (typeof line == \"number\") {\n      if (!isLine(this, line)) return null\n      n = line\n      line = getLine(this, line)\n      if (!line) return null\n    } else {\n      n = lineNo(line)\n      if (n == null) return null\n    }\n    return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,\n            textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,\n            widgets: line.widgets}\n  },\n\n  addLineClass: docMethodOp(function(handle, where, cls) {\n    return changeLine(this, handle, where == \"gutter\" ? \"gutter\" : \"class\", line => {\n      let prop = where == \"text\" ? \"textClass\"\n               : where == \"background\" ? \"bgClass\"\n               : where == \"gutter\" ? \"gutterClass\" : \"wrapClass\"\n      if (!line[prop]) line[prop] = cls\n      else if (classTest(cls).test(line[prop])) return false\n      else line[prop] += \" \" + cls\n      return true\n    })\n  }),\n  removeLineClass: docMethodOp(function(handle, where, cls) {\n    return changeLine(this, handle, where == \"gutter\" ? \"gutter\" : \"class\", line => {\n      let prop = where == \"text\" ? \"textClass\"\n               : where == \"background\" ? \"bgClass\"\n               : where == \"gutter\" ? \"gutterClass\" : \"wrapClass\"\n      let cur = line[prop]\n      if (!cur) return false\n      else if (cls == null) line[prop] = null\n      else {\n        let found = cur.match(classTest(cls))\n        if (!found) return false\n        let end = found.index + found[0].length\n        line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? \"\" : \" \") + cur.slice(end) || null\n      }\n      return true\n    })\n  }),\n\n  addLineWidget: docMethodOp(function(handle, node, options) {\n    return addLineWidget(this, handle, node, options)\n  }),\n  removeLineWidget: function(widget) { widget.clear() },\n\n  markText: function(from, to, options) {\n    return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || \"range\")\n  },\n  setBookmark: function(pos, options) {\n    let realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),\n                    insertLeft: options && options.insertLeft,\n                    clearWhenEmpty: false, shared: options && options.shared,\n                    handleMouseEvents: options && options.handleMouseEvents}\n    pos = clipPos(this, pos)\n    return markText(this, pos, pos, realOpts, \"bookmark\")\n  },\n  findMarksAt: function(pos) {\n    pos = clipPos(this, pos)\n    let markers = [], spans = getLine(this, pos.line).markedSpans\n    if (spans) for (let i = 0; i < spans.length; ++i) {\n      let span = spans[i]\n      if ((span.from == null || span.from <= pos.ch) &&\n          (span.to == null || span.to >= pos.ch))\n        markers.push(span.marker.parent || span.marker)\n    }\n    return markers\n  },\n  findMarks: function(from, to, filter) {\n    from = clipPos(this, from); to = clipPos(this, to)\n    let found = [], lineNo = from.line\n    this.iter(from.line, to.line + 1, line => {\n      let spans = line.markedSpans\n      if (spans) for (let i = 0; i < spans.length; i++) {\n        let span = spans[i]\n        if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||\n              span.from == null && lineNo != from.line ||\n              span.from != null && lineNo == to.line && span.from >= to.ch) &&\n            (!filter || filter(span.marker)))\n          found.push(span.marker.parent || span.marker)\n      }\n      ++lineNo\n    })\n    return found\n  },\n  getAllMarks: function() {\n    let markers = []\n    this.iter(line => {\n      let sps = line.markedSpans\n      if (sps) for (let i = 0; i < sps.length; ++i)\n        if (sps[i].from != null) markers.push(sps[i].marker)\n    })\n    return markers\n  },\n\n  posFromIndex: function(off) {\n    let ch, lineNo = this.first, sepSize = this.lineSeparator().length\n    this.iter(line => {\n      let sz = line.text.length + sepSize\n      if (sz > off) { ch = off; return true }\n      off -= sz\n      ++lineNo\n    })\n    return clipPos(this, Pos(lineNo, ch))\n  },\n  indexFromPos: function (coords) {\n    coords = clipPos(this, coords)\n    let index = coords.ch\n    if (coords.line < this.first || coords.ch < 0) return 0\n    let sepSize = this.lineSeparator().length\n    this.iter(this.first, coords.line, line => { // iter aborts when callback returns a truthy value\n      index += line.text.length + sepSize\n    })\n    return index\n  },\n\n  copy: function(copyHistory) {\n    let doc = new Doc(getLines(this, this.first, this.first + this.size),\n                      this.modeOption, this.first, this.lineSep, this.direction)\n    doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft\n    doc.sel = this.sel\n    doc.extend = false\n    if (copyHistory) {\n      doc.history.undoDepth = this.history.undoDepth\n      doc.setHistory(this.getHistory())\n    }\n    return doc\n  },\n\n  linkedDoc: function(options) {\n    if (!options) options = {}\n    let from = this.first, to = this.first + this.size\n    if (options.from != null && options.from > from) from = options.from\n    if (options.to != null && options.to < to) to = options.to\n    let copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction)\n    if (options.sharedHist) copy.history = this.history\n    ;(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})\n    copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]\n    copySharedMarkers(copy, findSharedMarkers(this))\n    return copy\n  },\n  unlinkDoc: function(other) {\n    if (other instanceof CodeMirror) other = other.doc\n    if (this.linked) for (let i = 0; i < this.linked.length; ++i) {\n      let link = this.linked[i]\n      if (link.doc != other) continue\n      this.linked.splice(i, 1)\n      other.unlinkDoc(this)\n      detachSharedMarkers(findSharedMarkers(this))\n      break\n    }\n    // If the histories were shared, split them again\n    if (other.history == this.history) {\n      let splitIds = [other.id]\n      linkedDocs(other, doc => splitIds.push(doc.id), true)\n      other.history = new History(null)\n      other.history.done = copyHistoryArray(this.history.done, splitIds)\n      other.history.undone = copyHistoryArray(this.history.undone, splitIds)\n    }\n  },\n  iterLinkedDocs: function(f) {linkedDocs(this, f)},\n\n  getMode: function() {return this.mode},\n  getEditor: function() {return this.cm},\n\n  splitLines: function(str) {\n    if (this.lineSep) return str.split(this.lineSep)\n    return splitLinesAuto(str)\n  },\n  lineSeparator: function() { return this.lineSep || \"\\n\" },\n\n  setDirection: docMethodOp(function (dir) {\n    if (dir != \"rtl\") dir = \"ltr\"\n    if (dir == this.direction) return\n    this.direction = dir\n    this.iter(line => line.order = null)\n    if (this.cm) directionChanged(this.cm)\n  })\n})\n\n// Public alias.\nDoc.prototype.eachLine = Doc.prototype.iter\n\nexport default Doc\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/model/change_measurement.js",
    "content": "import { cmp, Pos } from \"../line/pos.js\"\nimport { lst } from \"../util/misc.js\"\n\nimport { normalizeSelection, Range, Selection } from \"./selection.js\"\n\n// Compute the position of the end of a change (its 'to' property\n// refers to the pre-change end).\nexport function changeEnd(change) {\n  if (!change.text) return change.to\n  return Pos(change.from.line + change.text.length - 1,\n             lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))\n}\n\n// Adjust a position to refer to the post-change position of the\n// same text, or the end of the change if the change covers it.\nfunction adjustForChange(pos, change) {\n  if (cmp(pos, change.from) < 0) return pos\n  if (cmp(pos, change.to) <= 0) return changeEnd(change)\n\n  let line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch\n  if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch\n  return Pos(line, ch)\n}\n\nexport function computeSelAfterChange(doc, change) {\n  let out = []\n  for (let i = 0; i < doc.sel.ranges.length; i++) {\n    let range = doc.sel.ranges[i]\n    out.push(new Range(adjustForChange(range.anchor, change),\n                       adjustForChange(range.head, change)))\n  }\n  return normalizeSelection(doc.cm, out, doc.sel.primIndex)\n}\n\nfunction offsetPos(pos, old, nw) {\n  if (pos.line == old.line)\n    return Pos(nw.line, pos.ch - old.ch + nw.ch)\n  else\n    return Pos(nw.line + (pos.line - old.line), pos.ch)\n}\n\n// Used by replaceSelections to allow moving the selection to the\n// start or around the replaced test. Hint may be \"start\" or \"around\".\nexport function computeReplacedSel(doc, changes, hint) {\n  let out = []\n  let oldPrev = Pos(doc.first, 0), newPrev = oldPrev\n  for (let i = 0; i < changes.length; i++) {\n    let change = changes[i]\n    let from = offsetPos(change.from, oldPrev, newPrev)\n    let to = offsetPos(changeEnd(change), oldPrev, newPrev)\n    oldPrev = change.to\n    newPrev = to\n    if (hint == \"around\") {\n      let range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0\n      out[i] = new Range(inv ? to : from, inv ? from : to)\n    } else {\n      out[i] = new Range(from, from)\n    }\n  }\n  return new Selection(out, doc.sel.primIndex)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/model/changes.js",
    "content": "import { retreatFrontier } from \"../line/highlight.js\"\nimport { startWorker } from \"../display/highlight_worker.js\"\nimport { operation } from \"../display/operations.js\"\nimport { regChange, regLineChange } from \"../display/view_tracking.js\"\nimport { clipLine, clipPos, cmp, Pos } from \"../line/pos.js\"\nimport { sawReadOnlySpans } from \"../line/saw_special_spans.js\"\nimport { lineLength, removeReadOnlyRanges, stretchSpansOverChange, visualLine } from \"../line/spans.js\"\nimport { getBetween, getLine, lineNo } from \"../line/utils_line.js\"\nimport { estimateHeight } from \"../measurement/position_measurement.js\"\nimport { hasHandler, signal, signalCursorActivity } from \"../util/event.js\"\nimport { indexOf, lst, map, sel_dontScroll } from \"../util/misc.js\"\nimport { signalLater } from \"../util/operation_group.js\"\n\nimport { changeEnd, computeSelAfterChange } from \"./change_measurement.js\"\nimport { isWholeLineUpdate, linkedDocs, updateDoc } from \"./document_data.js\"\nimport { addChangeToHistory, historyChangeFromChange, mergeOldSpans, pushSelectionToHistory } from \"./history.js\"\nimport { Range, Selection } from \"./selection.js\"\nimport { setSelection, setSelectionNoUndo, skipAtomic } from \"./selection_updates.js\"\n\n// UPDATING\n\n// Allow \"beforeChange\" event handlers to influence a change\nfunction filterChange(doc, change, update) {\n  let obj = {\n    canceled: false,\n    from: change.from,\n    to: change.to,\n    text: change.text,\n    origin: change.origin,\n    cancel: () => obj.canceled = true\n  }\n  if (update) obj.update = (from, to, text, origin) => {\n    if (from) obj.from = clipPos(doc, from)\n    if (to) obj.to = clipPos(doc, to)\n    if (text) obj.text = text\n    if (origin !== undefined) obj.origin = origin\n  }\n  signal(doc, \"beforeChange\", doc, obj)\n  if (doc.cm) signal(doc.cm, \"beforeChange\", doc.cm, obj)\n\n  if (obj.canceled) {\n    if (doc.cm) doc.cm.curOp.updateInput = 2\n    return null\n  }\n  return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}\n}\n\n// Apply a change to a document, and add it to the document's\n// history, and propagating it to all linked documents.\nexport function makeChange(doc, change, ignoreReadOnly) {\n  if (doc.cm) {\n    if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly)\n    if (doc.cm.state.suppressEdits) return\n  }\n\n  if (hasHandler(doc, \"beforeChange\") || doc.cm && hasHandler(doc.cm, \"beforeChange\")) {\n    change = filterChange(doc, change, true)\n    if (!change) return\n  }\n\n  // Possibly split or suppress the update based on the presence\n  // of read-only spans in its range.\n  let split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to)\n  if (split) {\n    for (let i = split.length - 1; i >= 0; --i)\n      makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [\"\"] : change.text, origin: change.origin})\n  } else {\n    makeChangeInner(doc, change)\n  }\n}\n\nfunction makeChangeInner(doc, change) {\n  if (change.text.length == 1 && change.text[0] == \"\" && cmp(change.from, change.to) == 0) return\n  let selAfter = computeSelAfterChange(doc, change)\n  addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)\n\n  makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change))\n  let rebased = []\n\n  linkedDocs(doc, (doc, sharedHist) => {\n    if (!sharedHist && indexOf(rebased, doc.history) == -1) {\n      rebaseHist(doc.history, change)\n      rebased.push(doc.history)\n    }\n    makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))\n  })\n}\n\n// Revert a change stored in a document's history.\nexport function makeChangeFromHistory(doc, type, allowSelectionOnly) {\n  let suppress = doc.cm && doc.cm.state.suppressEdits\n  if (suppress && !allowSelectionOnly) return\n\n  let hist = doc.history, event, selAfter = doc.sel\n  let source = type == \"undo\" ? hist.done : hist.undone, dest = type == \"undo\" ? hist.undone : hist.done\n\n  // Verify that there is a useable event (so that ctrl-z won't\n  // needlessly clear selection events)\n  let i = 0\n  for (; i < source.length; i++) {\n    event = source[i]\n    if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)\n      break\n  }\n  if (i == source.length) return\n  hist.lastOrigin = hist.lastSelOrigin = null\n\n  for (;;) {\n    event = source.pop()\n    if (event.ranges) {\n      pushSelectionToHistory(event, dest)\n      if (allowSelectionOnly && !event.equals(doc.sel)) {\n        setSelection(doc, event, {clearRedo: false})\n        return\n      }\n      selAfter = event\n    } else if (suppress) {\n      source.push(event)\n      return\n    } else break\n  }\n\n  // Build up a reverse change object to add to the opposite history\n  // stack (redo when undoing, and vice versa).\n  let antiChanges = []\n  pushSelectionToHistory(selAfter, dest)\n  dest.push({changes: antiChanges, generation: hist.generation})\n  hist.generation = event.generation || ++hist.maxGeneration\n\n  let filter = hasHandler(doc, \"beforeChange\") || doc.cm && hasHandler(doc.cm, \"beforeChange\")\n\n  for (let i = event.changes.length - 1; i >= 0; --i) {\n    let change = event.changes[i]\n    change.origin = type\n    if (filter && !filterChange(doc, change, false)) {\n      source.length = 0\n      return\n    }\n\n    antiChanges.push(historyChangeFromChange(doc, change))\n\n    let after = i ? computeSelAfterChange(doc, change) : lst(source)\n    makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))\n    if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)})\n    let rebased = []\n\n    // Propagate to the linked documents\n    linkedDocs(doc, (doc, sharedHist) => {\n      if (!sharedHist && indexOf(rebased, doc.history) == -1) {\n        rebaseHist(doc.history, change)\n        rebased.push(doc.history)\n      }\n      makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))\n    })\n  }\n}\n\n// Sub-views need their line numbers shifted when text is added\n// above or below them in the parent document.\nfunction shiftDoc(doc, distance) {\n  if (distance == 0) return\n  doc.first += distance\n  doc.sel = new Selection(map(doc.sel.ranges, range => new Range(\n    Pos(range.anchor.line + distance, range.anchor.ch),\n    Pos(range.head.line + distance, range.head.ch)\n  )), doc.sel.primIndex)\n  if (doc.cm) {\n    regChange(doc.cm, doc.first, doc.first - distance, distance)\n    for (let d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)\n      regLineChange(doc.cm, l, \"gutter\")\n  }\n}\n\n// More lower-level change function, handling only a single document\n// (not linked ones).\nfunction makeChangeSingleDoc(doc, change, selAfter, spans) {\n  if (doc.cm && !doc.cm.curOp)\n    return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans)\n\n  if (change.to.line < doc.first) {\n    shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))\n    return\n  }\n  if (change.from.line > doc.lastLine()) return\n\n  // Clip the change to the size of this doc\n  if (change.from.line < doc.first) {\n    let shift = change.text.length - 1 - (doc.first - change.from.line)\n    shiftDoc(doc, shift)\n    change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),\n              text: [lst(change.text)], origin: change.origin}\n  }\n  let last = doc.lastLine()\n  if (change.to.line > last) {\n    change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),\n              text: [change.text[0]], origin: change.origin}\n  }\n\n  change.removed = getBetween(doc, change.from, change.to)\n\n  if (!selAfter) selAfter = computeSelAfterChange(doc, change)\n  if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans)\n  else updateDoc(doc, change, spans)\n  setSelectionNoUndo(doc, selAfter, sel_dontScroll)\n\n  if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))\n    doc.cantEdit = false\n}\n\n// Handle the interaction of a change to a document with the editor\n// that this document is part of.\nfunction makeChangeSingleDocInEditor(cm, change, spans) {\n  let doc = cm.doc, display = cm.display, from = change.from, to = change.to\n\n  let recomputeMaxLength = false, checkWidthStart = from.line\n  if (!cm.options.lineWrapping) {\n    checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))\n    doc.iter(checkWidthStart, to.line + 1, line => {\n      if (line == display.maxLine) {\n        recomputeMaxLength = true\n        return true\n      }\n    })\n  }\n\n  if (doc.sel.contains(change.from, change.to) > -1)\n    signalCursorActivity(cm)\n\n  updateDoc(doc, change, spans, estimateHeight(cm))\n\n  if (!cm.options.lineWrapping) {\n    doc.iter(checkWidthStart, from.line + change.text.length, line => {\n      let len = lineLength(line)\n      if (len > display.maxLineLength) {\n        display.maxLine = line\n        display.maxLineLength = len\n        display.maxLineChanged = true\n        recomputeMaxLength = false\n      }\n    })\n    if (recomputeMaxLength) cm.curOp.updateMaxLine = true\n  }\n\n  retreatFrontier(doc, from.line)\n  startWorker(cm, 400)\n\n  let lendiff = change.text.length - (to.line - from.line) - 1\n  // Remember that these lines changed, for updating the display\n  if (change.full)\n    regChange(cm)\n  else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))\n    regLineChange(cm, from.line, \"text\")\n  else\n    regChange(cm, from.line, to.line + 1, lendiff)\n\n  let changesHandler = hasHandler(cm, \"changes\"), changeHandler = hasHandler(cm, \"change\")\n  if (changeHandler || changesHandler) {\n    let obj = {\n      from: from, to: to,\n      text: change.text,\n      removed: change.removed,\n      origin: change.origin\n    }\n    if (changeHandler) signalLater(cm, \"change\", cm, obj)\n    if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj)\n  }\n  cm.display.selForContextMenu = null\n}\n\nexport function replaceRange(doc, code, from, to, origin) {\n  if (!to) to = from\n  if (cmp(to, from) < 0) [from, to] = [to, from]\n  if (typeof code == \"string\") code = doc.splitLines(code)\n  makeChange(doc, {from, to, text: code, origin})\n}\n\n// Rebasing/resetting history to deal with externally-sourced changes\n\nfunction rebaseHistSelSingle(pos, from, to, diff) {\n  if (to < pos.line) {\n    pos.line += diff\n  } else if (from < pos.line) {\n    pos.line = from\n    pos.ch = 0\n  }\n}\n\n// Tries to rebase an array of history events given a change in the\n// document. If the change touches the same lines as the event, the\n// event, and everything 'behind' it, is discarded. If the change is\n// before the event, the event's positions are updated. Uses a\n// copy-on-write scheme for the positions, to avoid having to\n// reallocate them all on every rebase, but also avoid problems with\n// shared position objects being unsafely updated.\nfunction rebaseHistArray(array, from, to, diff) {\n  for (let i = 0; i < array.length; ++i) {\n    let sub = array[i], ok = true\n    if (sub.ranges) {\n      if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }\n      for (let j = 0; j < sub.ranges.length; j++) {\n        rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)\n        rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)\n      }\n      continue\n    }\n    for (let j = 0; j < sub.changes.length; ++j) {\n      let cur = sub.changes[j]\n      if (to < cur.from.line) {\n        cur.from = Pos(cur.from.line + diff, cur.from.ch)\n        cur.to = Pos(cur.to.line + diff, cur.to.ch)\n      } else if (from <= cur.to.line) {\n        ok = false\n        break\n      }\n    }\n    if (!ok) {\n      array.splice(0, i + 1)\n      i = 0\n    }\n  }\n}\n\nfunction rebaseHist(hist, change) {\n  let from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1\n  rebaseHistArray(hist.done, from, to, diff)\n  rebaseHistArray(hist.undone, from, to, diff)\n}\n\n// Utility for applying a change to a line by handle or number,\n// returning the number and optionally registering the line as\n// changed.\nexport function changeLine(doc, handle, changeType, op) {\n  let no = handle, line = handle\n  if (typeof handle == \"number\") line = getLine(doc, clipLine(doc, handle))\n  else no = lineNo(handle)\n  if (no == null) return null\n  if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType)\n  return line\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/model/chunk.js",
    "content": "import { cleanUpLine } from \"../line/line_data.js\"\nimport { indexOf } from \"../util/misc.js\"\nimport { signalLater } from \"../util/operation_group.js\"\n\n// The document is represented as a BTree consisting of leaves, with\n// chunk of lines in them, and branches, with up to ten leaves or\n// other branch nodes below them. The top node is always a branch\n// node, and is the document object itself (meaning it has\n// additional methods and properties).\n//\n// All nodes have parent links. The tree is used both to go from\n// line numbers to line objects, and to go from objects to numbers.\n// It also indexes by height, and is used to convert between height\n// and line object, and to find the total height of the document.\n//\n// See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html\n\nexport function LeafChunk(lines) {\n  this.lines = lines\n  this.parent = null\n  let height = 0\n  for (let i = 0; i < lines.length; ++i) {\n    lines[i].parent = this\n    height += lines[i].height\n  }\n  this.height = height\n}\n\nLeafChunk.prototype = {\n  chunkSize() { return this.lines.length },\n\n  // Remove the n lines at offset 'at'.\n  removeInner(at, n) {\n    for (let i = at, e = at + n; i < e; ++i) {\n      let line = this.lines[i]\n      this.height -= line.height\n      cleanUpLine(line)\n      signalLater(line, \"delete\")\n    }\n    this.lines.splice(at, n)\n  },\n\n  // Helper used to collapse a small branch into a single leaf.\n  collapse(lines) {\n    lines.push.apply(lines, this.lines)\n  },\n\n  // Insert the given array of lines at offset 'at', count them as\n  // having the given height.\n  insertInner(at, lines, height) {\n    this.height += height\n    this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at))\n    for (let i = 0; i < lines.length; ++i) lines[i].parent = this\n  },\n\n  // Used to iterate over a part of the tree.\n  iterN(at, n, op) {\n    for (let e = at + n; at < e; ++at)\n      if (op(this.lines[at])) return true\n  }\n}\n\nexport function BranchChunk(children) {\n  this.children = children\n  let size = 0, height = 0\n  for (let i = 0; i < children.length; ++i) {\n    let ch = children[i]\n    size += ch.chunkSize(); height += ch.height\n    ch.parent = this\n  }\n  this.size = size\n  this.height = height\n  this.parent = null\n}\n\nBranchChunk.prototype = {\n  chunkSize() { return this.size },\n\n  removeInner(at, n) {\n    this.size -= n\n    for (let i = 0; i < this.children.length; ++i) {\n      let child = this.children[i], sz = child.chunkSize()\n      if (at < sz) {\n        let rm = Math.min(n, sz - at), oldHeight = child.height\n        child.removeInner(at, rm)\n        this.height -= oldHeight - child.height\n        if (sz == rm) { this.children.splice(i--, 1); child.parent = null }\n        if ((n -= rm) == 0) break\n        at = 0\n      } else at -= sz\n    }\n    // If the result is smaller than 25 lines, ensure that it is a\n    // single leaf node.\n    if (this.size - n < 25 &&\n        (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {\n      let lines = []\n      this.collapse(lines)\n      this.children = [new LeafChunk(lines)]\n      this.children[0].parent = this\n    }\n  },\n\n  collapse(lines) {\n    for (let i = 0; i < this.children.length; ++i) this.children[i].collapse(lines)\n  },\n\n  insertInner(at, lines, height) {\n    this.size += lines.length\n    this.height += height\n    for (let i = 0; i < this.children.length; ++i) {\n      let child = this.children[i], sz = child.chunkSize()\n      if (at <= sz) {\n        child.insertInner(at, lines, height)\n        if (child.lines && child.lines.length > 50) {\n          // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.\n          // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.\n          let remaining = child.lines.length % 25 + 25\n          for (let pos = remaining; pos < child.lines.length;) {\n            let leaf = new LeafChunk(child.lines.slice(pos, pos += 25))\n            child.height -= leaf.height\n            this.children.splice(++i, 0, leaf)\n            leaf.parent = this\n          }\n          child.lines = child.lines.slice(0, remaining)\n          this.maybeSpill()\n        }\n        break\n      }\n      at -= sz\n    }\n  },\n\n  // When a node has grown, check whether it should be split.\n  maybeSpill() {\n    if (this.children.length <= 10) return\n    let me = this\n    do {\n      let spilled = me.children.splice(me.children.length - 5, 5)\n      let sibling = new BranchChunk(spilled)\n      if (!me.parent) { // Become the parent node\n        let copy = new BranchChunk(me.children)\n        copy.parent = me\n        me.children = [copy, sibling]\n        me = copy\n     } else {\n        me.size -= sibling.size\n        me.height -= sibling.height\n        let myIndex = indexOf(me.parent.children, me)\n        me.parent.children.splice(myIndex + 1, 0, sibling)\n      }\n      sibling.parent = me.parent\n    } while (me.children.length > 10)\n    me.parent.maybeSpill()\n  },\n\n  iterN(at, n, op) {\n    for (let i = 0; i < this.children.length; ++i) {\n      let child = this.children[i], sz = child.chunkSize()\n      if (at < sz) {\n        let used = Math.min(n, sz - at)\n        if (child.iterN(at, used, op)) return true\n        if ((n -= used) == 0) break\n        at = 0\n      } else at -= sz\n    }\n  }\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/model/document_data.js",
    "content": "import { loadMode } from \"../display/mode_state.js\"\nimport { runInOp } from \"../display/operations.js\"\nimport { regChange } from \"../display/view_tracking.js\"\nimport { Line, updateLine } from \"../line/line_data.js\"\nimport { findMaxLine } from \"../line/spans.js\"\nimport { getLine } from \"../line/utils_line.js\"\nimport { estimateLineHeights } from \"../measurement/position_measurement.js\"\nimport { addClass, rmClass } from \"../util/dom.js\"\nimport { lst } from \"../util/misc.js\"\nimport { signalLater } from \"../util/operation_group.js\"\n\n// DOCUMENT DATA STRUCTURE\n\n// By default, updates that start and end at the beginning of a line\n// are treated specially, in order to make the association of line\n// widgets and marker elements with the text behave more intuitive.\nexport function isWholeLineUpdate(doc, change) {\n  return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == \"\" &&\n    (!doc.cm || doc.cm.options.wholeLineUpdateBefore)\n}\n\n// Perform a change on the document data structure.\nexport function updateDoc(doc, change, markedSpans, estimateHeight) {\n  function spansFor(n) {return markedSpans ? markedSpans[n] : null}\n  function update(line, text, spans) {\n    updateLine(line, text, spans, estimateHeight)\n    signalLater(line, \"change\", line, change)\n  }\n  function linesFor(start, end) {\n    let result = []\n    for (let i = start; i < end; ++i)\n      result.push(new Line(text[i], spansFor(i), estimateHeight))\n    return result\n  }\n\n  let from = change.from, to = change.to, text = change.text\n  let firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line)\n  let lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line\n\n  // Adjust the line structure\n  if (change.full) {\n    doc.insert(0, linesFor(0, text.length))\n    doc.remove(text.length, doc.size - text.length)\n  } else if (isWholeLineUpdate(doc, change)) {\n    // This is a whole-line replace. Treated specially to make\n    // sure line objects move the way they are supposed to.\n    let added = linesFor(0, text.length - 1)\n    update(lastLine, lastLine.text, lastSpans)\n    if (nlines) doc.remove(from.line, nlines)\n    if (added.length) doc.insert(from.line, added)\n  } else if (firstLine == lastLine) {\n    if (text.length == 1) {\n      update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans)\n    } else {\n      let added = linesFor(1, text.length - 1)\n      added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight))\n      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))\n      doc.insert(from.line + 1, added)\n    }\n  } else if (text.length == 1) {\n    update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0))\n    doc.remove(from.line + 1, nlines)\n  } else {\n    update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))\n    update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)\n    let added = linesFor(1, text.length - 1)\n    if (nlines > 1) doc.remove(from.line + 1, nlines - 1)\n    doc.insert(from.line + 1, added)\n  }\n\n  signalLater(doc, \"change\", doc, change)\n}\n\n// Call f for all linked documents.\nexport function linkedDocs(doc, f, sharedHistOnly) {\n  function propagate(doc, skip, sharedHist) {\n    if (doc.linked) for (let i = 0; i < doc.linked.length; ++i) {\n      let rel = doc.linked[i]\n      if (rel.doc == skip) continue\n      let shared = sharedHist && rel.sharedHist\n      if (sharedHistOnly && !shared) continue\n      f(rel.doc, shared)\n      propagate(rel.doc, doc, shared)\n    }\n  }\n  propagate(doc, null, true)\n}\n\n// Attach a document to an editor.\nexport function attachDoc(cm, doc) {\n  if (doc.cm) throw new Error(\"This document is already in use.\")\n  cm.doc = doc\n  doc.cm = cm\n  estimateLineHeights(cm)\n  loadMode(cm)\n  setDirectionClass(cm)\n  cm.options.direction = doc.direction\n  if (!cm.options.lineWrapping) findMaxLine(cm)\n  cm.options.mode = doc.modeOption\n  regChange(cm)\n}\n\nfunction setDirectionClass(cm) {\n  ;(cm.doc.direction == \"rtl\" ? addClass : rmClass)(cm.display.lineDiv, \"CodeMirror-rtl\")\n}\n\nexport function directionChanged(cm) {\n  runInOp(cm, () => {\n    setDirectionClass(cm)\n    regChange(cm)\n  })\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/model/history.js",
    "content": "import { cmp, copyPos } from \"../line/pos.js\"\nimport { stretchSpansOverChange } from \"../line/spans.js\"\nimport { getBetween } from \"../line/utils_line.js\"\nimport { signal } from \"../util/event.js\"\nimport { indexOf, lst } from \"../util/misc.js\"\n\nimport { changeEnd } from \"./change_measurement.js\"\nimport { linkedDocs } from \"./document_data.js\"\nimport { Selection } from \"./selection.js\"\n\nexport function History(prev) {\n  // Arrays of change events and selections. Doing something adds an\n  // event to done and clears undo. Undoing moves events from done\n  // to undone, redoing moves them in the other direction.\n  this.done = []; this.undone = []\n  this.undoDepth = prev ? prev.undoDepth : Infinity\n  // Used to track when changes can be merged into a single undo\n  // event\n  this.lastModTime = this.lastSelTime = 0\n  this.lastOp = this.lastSelOp = null\n  this.lastOrigin = this.lastSelOrigin = null\n  // Used by the isClean() method\n  this.generation = this.maxGeneration = prev ? prev.maxGeneration : 1\n}\n\n// Create a history change event from an updateDoc-style change\n// object.\nexport function historyChangeFromChange(doc, change) {\n  let histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}\n  attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)\n  linkedDocs(doc, doc => attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1), true)\n  return histChange\n}\n\n// Pop all selection events off the end of a history array. Stop at\n// a change event.\nfunction clearSelectionEvents(array) {\n  while (array.length) {\n    let last = lst(array)\n    if (last.ranges) array.pop()\n    else break\n  }\n}\n\n// Find the top change event in the history. Pop off selection\n// events that are in the way.\nfunction lastChangeEvent(hist, force) {\n  if (force) {\n    clearSelectionEvents(hist.done)\n    return lst(hist.done)\n  } else if (hist.done.length && !lst(hist.done).ranges) {\n    return lst(hist.done)\n  } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {\n    hist.done.pop()\n    return lst(hist.done)\n  }\n}\n\n// Register a change in the history. Merges changes that are within\n// a single operation, or are close together with an origin that\n// allows merging (starting with \"+\") into a single event.\nexport function addChangeToHistory(doc, change, selAfter, opId) {\n  let hist = doc.history\n  hist.undone.length = 0\n  let time = +new Date, cur\n  let last\n\n  if ((hist.lastOp == opId ||\n       hist.lastOrigin == change.origin && change.origin &&\n       ((change.origin.charAt(0) == \"+\" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||\n        change.origin.charAt(0) == \"*\")) &&\n      (cur = lastChangeEvent(hist, hist.lastOp == opId))) {\n    // Merge this change into the last event\n    last = lst(cur.changes)\n    if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {\n      // Optimized case for simple insertion -- don't want to add\n      // new changesets for every character typed\n      last.to = changeEnd(change)\n    } else {\n      // Add new sub-event\n      cur.changes.push(historyChangeFromChange(doc, change))\n    }\n  } else {\n    // Can not be merged, start a new event.\n    let before = lst(hist.done)\n    if (!before || !before.ranges)\n      pushSelectionToHistory(doc.sel, hist.done)\n    cur = {changes: [historyChangeFromChange(doc, change)],\n           generation: hist.generation}\n    hist.done.push(cur)\n    while (hist.done.length > hist.undoDepth) {\n      hist.done.shift()\n      if (!hist.done[0].ranges) hist.done.shift()\n    }\n  }\n  hist.done.push(selAfter)\n  hist.generation = ++hist.maxGeneration\n  hist.lastModTime = hist.lastSelTime = time\n  hist.lastOp = hist.lastSelOp = opId\n  hist.lastOrigin = hist.lastSelOrigin = change.origin\n\n  if (!last) signal(doc, \"historyAdded\")\n}\n\nfunction selectionEventCanBeMerged(doc, origin, prev, sel) {\n  let ch = origin.charAt(0)\n  return ch == \"*\" ||\n    ch == \"+\" &&\n    prev.ranges.length == sel.ranges.length &&\n    prev.somethingSelected() == sel.somethingSelected() &&\n    new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)\n}\n\n// Called whenever the selection changes, sets the new selection as\n// the pending selection in the history, and pushes the old pending\n// selection into the 'done' array when it was significantly\n// different (in number of selected ranges, emptiness, or time).\nexport function addSelectionToHistory(doc, sel, opId, options) {\n  let hist = doc.history, origin = options && options.origin\n\n  // A new event is started when the previous origin does not match\n  // the current, or the origins don't allow matching. Origins\n  // starting with * are always merged, those starting with + are\n  // merged when similar and close together in time.\n  if (opId == hist.lastSelOp ||\n      (origin && hist.lastSelOrigin == origin &&\n       (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||\n        selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))\n    hist.done[hist.done.length - 1] = sel\n  else\n    pushSelectionToHistory(sel, hist.done)\n\n  hist.lastSelTime = +new Date\n  hist.lastSelOrigin = origin\n  hist.lastSelOp = opId\n  if (options && options.clearRedo !== false)\n    clearSelectionEvents(hist.undone)\n}\n\nexport function pushSelectionToHistory(sel, dest) {\n  let top = lst(dest)\n  if (!(top && top.ranges && top.equals(sel)))\n    dest.push(sel)\n}\n\n// Used to store marked span information in the history.\nfunction attachLocalSpans(doc, change, from, to) {\n  let existing = change[\"spans_\" + doc.id], n = 0\n  doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), line => {\n    if (line.markedSpans)\n      (existing || (existing = change[\"spans_\" + doc.id] = {}))[n] = line.markedSpans\n    ++n\n  })\n}\n\n// When un/re-doing restores text containing marked spans, those\n// that have been explicitly cleared should not be restored.\nfunction removeClearedSpans(spans) {\n  if (!spans) return null\n  let out\n  for (let i = 0; i < spans.length; ++i) {\n    if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i) }\n    else if (out) out.push(spans[i])\n  }\n  return !out ? spans : out.length ? out : null\n}\n\n// Retrieve and filter the old marked spans stored in a change event.\nfunction getOldSpans(doc, change) {\n  let found = change[\"spans_\" + doc.id]\n  if (!found) return null\n  let nw = []\n  for (let i = 0; i < change.text.length; ++i)\n    nw.push(removeClearedSpans(found[i]))\n  return nw\n}\n\n// Used for un/re-doing changes from the history. Combines the\n// result of computing the existing spans with the set of spans that\n// existed in the history (so that deleting around a span and then\n// undoing brings back the span).\nexport function mergeOldSpans(doc, change) {\n  let old = getOldSpans(doc, change)\n  let stretched = stretchSpansOverChange(doc, change)\n  if (!old) return stretched\n  if (!stretched) return old\n\n  for (let i = 0; i < old.length; ++i) {\n    let oldCur = old[i], stretchCur = stretched[i]\n    if (oldCur && stretchCur) {\n      spans: for (let j = 0; j < stretchCur.length; ++j) {\n        let span = stretchCur[j]\n        for (let k = 0; k < oldCur.length; ++k)\n          if (oldCur[k].marker == span.marker) continue spans\n        oldCur.push(span)\n      }\n    } else if (stretchCur) {\n      old[i] = stretchCur\n    }\n  }\n  return old\n}\n\n// Used both to provide a JSON-safe object in .getHistory, and, when\n// detaching a document, to split the history in two\nexport function copyHistoryArray(events, newGroup, instantiateSel) {\n  let copy = []\n  for (let i = 0; i < events.length; ++i) {\n    let event = events[i]\n    if (event.ranges) {\n      copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event)\n      continue\n    }\n    let changes = event.changes, newChanges = []\n    copy.push({changes: newChanges})\n    for (let j = 0; j < changes.length; ++j) {\n      let change = changes[j], m\n      newChanges.push({from: change.from, to: change.to, text: change.text})\n      if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\\d+)$/)) {\n        if (indexOf(newGroup, Number(m[1])) > -1) {\n          lst(newChanges)[prop] = change[prop]\n          delete change[prop]\n        }\n      }\n    }\n  }\n  return copy\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/model/line_widget.js",
    "content": "import { runInOp } from \"../display/operations.js\"\nimport { addToScrollTop } from \"../display/scrolling.js\"\nimport { regLineChange } from \"../display/view_tracking.js\"\nimport { heightAtLine, lineIsHidden } from \"../line/spans.js\"\nimport { lineNo, updateLineHeight } from \"../line/utils_line.js\"\nimport { widgetHeight } from \"../measurement/widgets.js\"\nimport { changeLine } from \"./changes.js\"\nimport { eventMixin } from \"../util/event.js\"\nimport { signalLater } from \"../util/operation_group.js\"\n\n// Line widgets are block elements displayed above or below a line.\n\nexport class LineWidget {\n  constructor(doc, node, options) {\n    if (options) for (let opt in options) if (options.hasOwnProperty(opt))\n      this[opt] = options[opt]\n    this.doc = doc\n    this.node = node\n  }\n\n  clear() {\n    let cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line)\n    if (no == null || !ws) return\n    for (let i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1)\n    if (!ws.length) line.widgets = null\n    let height = widgetHeight(this)\n    updateLineHeight(line, Math.max(0, line.height - height))\n    if (cm) {\n      runInOp(cm, () => {\n        adjustScrollWhenAboveVisible(cm, line, -height)\n        regLineChange(cm, no, \"widget\")\n      })\n      signalLater(cm, \"lineWidgetCleared\", cm, this, no)\n    }\n  }\n\n  changed() {\n    let oldH = this.height, cm = this.doc.cm, line = this.line\n    this.height = null\n    let diff = widgetHeight(this) - oldH\n    if (!diff) return\n    if (!lineIsHidden(this.doc, line)) updateLineHeight(line, line.height + diff)\n    if (cm) {\n      runInOp(cm, () => {\n        cm.curOp.forceUpdate = true\n        adjustScrollWhenAboveVisible(cm, line, diff)\n        signalLater(cm, \"lineWidgetChanged\", cm, this, lineNo(line))\n      })\n    }\n  }\n}\neventMixin(LineWidget)\n\nfunction adjustScrollWhenAboveVisible(cm, line, diff) {\n  if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))\n    addToScrollTop(cm, diff)\n}\n\nexport function addLineWidget(doc, handle, node, options) {\n  let widget = new LineWidget(doc, node, options)\n  let cm = doc.cm\n  if (cm && widget.noHScroll) cm.display.alignWidgets = true\n  changeLine(doc, handle, \"widget\", line => {\n    let widgets = line.widgets || (line.widgets = [])\n    if (widget.insertAt == null) widgets.push(widget)\n    else widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget)\n    widget.line = line\n    if (cm && !lineIsHidden(doc, line)) {\n      let aboveVisible = heightAtLine(line) < doc.scrollTop\n      updateLineHeight(line, line.height + widgetHeight(widget))\n      if (aboveVisible) addToScrollTop(cm, widget.height)\n      cm.curOp.forceUpdate = true\n    }\n    return true\n  })\n  if (cm) signalLater(cm, \"lineWidgetAdded\", cm, widget, typeof handle == \"number\" ? handle : lineNo(handle))\n  return widget\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/model/mark_text.js",
    "content": "import { eltP } from \"../util/dom.js\"\nimport { eventMixin, hasHandler, on } from \"../util/event.js\"\nimport { endOperation, operation, runInOp, startOperation } from \"../display/operations.js\"\nimport { clipPos, cmp, Pos } from \"../line/pos.js\"\nimport { lineNo, updateLineHeight } from \"../line/utils_line.js\"\nimport { clearLineMeasurementCacheFor, findViewForLine, textHeight } from \"../measurement/position_measurement.js\"\nimport { seeReadOnlySpans, seeCollapsedSpans } from \"../line/saw_special_spans.js\"\nimport { addMarkedSpan, conflictingCollapsedRange, getMarkedSpanFor, lineIsHidden, lineLength, MarkedSpan, removeMarkedSpan, visualLine } from \"../line/spans.js\"\nimport { copyObj, indexOf, lst } from \"../util/misc.js\"\nimport { signalLater } from \"../util/operation_group.js\"\nimport { widgetHeight } from \"../measurement/widgets.js\"\nimport { regChange, regLineChange } from \"../display/view_tracking.js\"\n\nimport { linkedDocs } from \"./document_data.js\"\nimport { addChangeToHistory } from \"./history.js\"\nimport { reCheckSelection } from \"./selection_updates.js\"\n\n// TEXTMARKERS\n\n// Created with markText and setBookmark methods. A TextMarker is a\n// handle that can be used to clear or find a marked position in the\n// document. Line objects hold arrays (markedSpans) containing\n// {from, to, marker} object pointing to such marker objects, and\n// indicating that such a marker is present on that line. Multiple\n// lines may point to the same marker when it spans across lines.\n// The spans will have null for their from/to properties when the\n// marker continues beyond the start/end of the line. Markers have\n// links back to the lines they currently touch.\n\n// Collapsed markers have unique ids, in order to be able to order\n// them, which is needed for uniquely determining an outer marker\n// when they overlap (they may nest, but not partially overlap).\nlet nextMarkerId = 0\n\nexport class TextMarker {\n  constructor(doc, type) {\n    this.lines = []\n    this.type = type\n    this.doc = doc\n    this.id = ++nextMarkerId\n  }\n\n  // Clear the marker.\n  clear() {\n    if (this.explicitlyCleared) return\n    let cm = this.doc.cm, withOp = cm && !cm.curOp\n    if (withOp) startOperation(cm)\n    if (hasHandler(this, \"clear\")) {\n      let found = this.find()\n      if (found) signalLater(this, \"clear\", found.from, found.to)\n    }\n    let min = null, max = null\n    for (let i = 0; i < this.lines.length; ++i) {\n      let line = this.lines[i]\n      let span = getMarkedSpanFor(line.markedSpans, this)\n      if (cm && !this.collapsed) regLineChange(cm, lineNo(line), \"text\")\n      else if (cm) {\n        if (span.to != null) max = lineNo(line)\n        if (span.from != null) min = lineNo(line)\n      }\n      line.markedSpans = removeMarkedSpan(line.markedSpans, span)\n      if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)\n        updateLineHeight(line, textHeight(cm.display))\n    }\n    if (cm && this.collapsed && !cm.options.lineWrapping) for (let i = 0; i < this.lines.length; ++i) {\n      let visual = visualLine(this.lines[i]), len = lineLength(visual)\n      if (len > cm.display.maxLineLength) {\n        cm.display.maxLine = visual\n        cm.display.maxLineLength = len\n        cm.display.maxLineChanged = true\n      }\n    }\n\n    if (min != null && cm && this.collapsed) regChange(cm, min, max + 1)\n    this.lines.length = 0\n    this.explicitlyCleared = true\n    if (this.atomic && this.doc.cantEdit) {\n      this.doc.cantEdit = false\n      if (cm) reCheckSelection(cm.doc)\n    }\n    if (cm) signalLater(cm, \"markerCleared\", cm, this, min, max)\n    if (withOp) endOperation(cm)\n    if (this.parent) this.parent.clear()\n  }\n\n  // Find the position of the marker in the document. Returns a {from,\n  // to} object by default. Side can be passed to get a specific side\n  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the\n  // Pos objects returned contain a line object, rather than a line\n  // number (used to prevent looking up the same line twice).\n  find(side, lineObj) {\n    if (side == null && this.type == \"bookmark\") side = 1\n    let from, to\n    for (let i = 0; i < this.lines.length; ++i) {\n      let line = this.lines[i]\n      let span = getMarkedSpanFor(line.markedSpans, this)\n      if (span.from != null) {\n        from = Pos(lineObj ? line : lineNo(line), span.from)\n        if (side == -1) return from\n      }\n      if (span.to != null) {\n        to = Pos(lineObj ? line : lineNo(line), span.to)\n        if (side == 1) return to\n      }\n    }\n    return from && {from: from, to: to}\n  }\n\n  // Signals that the marker's widget changed, and surrounding layout\n  // should be recomputed.\n  changed() {\n    let pos = this.find(-1, true), widget = this, cm = this.doc.cm\n    if (!pos || !cm) return\n    runInOp(cm, () => {\n      let line = pos.line, lineN = lineNo(pos.line)\n      let view = findViewForLine(cm, lineN)\n      if (view) {\n        clearLineMeasurementCacheFor(view)\n        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true\n      }\n      cm.curOp.updateMaxLine = true\n      if (!lineIsHidden(widget.doc, line) && widget.height != null) {\n        let oldHeight = widget.height\n        widget.height = null\n        let dHeight = widgetHeight(widget) - oldHeight\n        if (dHeight)\n          updateLineHeight(line, line.height + dHeight)\n      }\n      signalLater(cm, \"markerChanged\", cm, this)\n    })\n  }\n\n  attachLine(line) {\n    if (!this.lines.length && this.doc.cm) {\n      let op = this.doc.cm.curOp\n      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)\n        (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this)\n    }\n    this.lines.push(line)\n  }\n\n  detachLine(line) {\n    this.lines.splice(indexOf(this.lines, line), 1)\n    if (!this.lines.length && this.doc.cm) {\n      let op = this.doc.cm.curOp\n      ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)\n    }\n  }\n}\neventMixin(TextMarker)\n\n// Create a marker, wire it up to the right lines, and\nexport function markText(doc, from, to, options, type) {\n  // Shared markers (across linked documents) are handled separately\n  // (markTextShared will call out to this again, once per\n  // document).\n  if (options && options.shared) return markTextShared(doc, from, to, options, type)\n  // Ensure we are in an operation.\n  if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type)\n\n  let marker = new TextMarker(doc, type), diff = cmp(from, to)\n  if (options) copyObj(options, marker, false)\n  // Don't connect empty markers unless clearWhenEmpty is false\n  if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)\n    return marker\n  if (marker.replacedWith) {\n    // Showing up as a widget implies collapsed (widget replaces text)\n    marker.collapsed = true\n    marker.widgetNode = eltP(\"span\", [marker.replacedWith], \"CodeMirror-widget\")\n    if (!options.handleMouseEvents) marker.widgetNode.setAttribute(\"cm-ignore-events\", \"true\")\n    if (options.insertLeft) marker.widgetNode.insertLeft = true\n  }\n  if (marker.collapsed) {\n    if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||\n        from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))\n      throw new Error(\"Inserting collapsed marker partially overlapping an existing one\")\n    seeCollapsedSpans()\n  }\n\n  if (marker.addToHistory)\n    addChangeToHistory(doc, {from: from, to: to, origin: \"markText\"}, doc.sel, NaN)\n\n  let curLine = from.line, cm = doc.cm, updateMaxLine\n  doc.iter(curLine, to.line + 1, line => {\n    if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)\n      updateMaxLine = true\n    if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0)\n    addMarkedSpan(line, new MarkedSpan(marker,\n                                       curLine == from.line ? from.ch : null,\n                                       curLine == to.line ? to.ch : null), doc.cm && doc.cm.curOp)\n    ++curLine\n  })\n  // lineIsHidden depends on the presence of the spans, so needs a second pass\n  if (marker.collapsed) doc.iter(from.line, to.line + 1, line => {\n    if (lineIsHidden(doc, line)) updateLineHeight(line, 0)\n  })\n\n  if (marker.clearOnEnter) on(marker, \"beforeCursorEnter\", () => marker.clear())\n\n  if (marker.readOnly) {\n    seeReadOnlySpans()\n    if (doc.history.done.length || doc.history.undone.length)\n      doc.clearHistory()\n  }\n  if (marker.collapsed) {\n    marker.id = ++nextMarkerId\n    marker.atomic = true\n  }\n  if (cm) {\n    // Sync editor state\n    if (updateMaxLine) cm.curOp.updateMaxLine = true\n    if (marker.collapsed)\n      regChange(cm, from.line, to.line + 1)\n    else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||\n             marker.attributes || marker.title)\n      for (let i = from.line; i <= to.line; i++) regLineChange(cm, i, \"text\")\n    if (marker.atomic) reCheckSelection(cm.doc)\n    signalLater(cm, \"markerAdded\", cm, marker)\n  }\n  return marker\n}\n\n// SHARED TEXTMARKERS\n\n// A shared marker spans multiple linked documents. It is\n// implemented as a meta-marker-object controlling multiple normal\n// markers.\nexport class SharedTextMarker {\n  constructor(markers, primary) {\n    this.markers = markers\n    this.primary = primary\n    for (let i = 0; i < markers.length; ++i)\n      markers[i].parent = this\n  }\n\n  clear() {\n    if (this.explicitlyCleared) return\n    this.explicitlyCleared = true\n    for (let i = 0; i < this.markers.length; ++i)\n      this.markers[i].clear()\n    signalLater(this, \"clear\")\n  }\n\n  find(side, lineObj) {\n    return this.primary.find(side, lineObj)\n  }\n}\neventMixin(SharedTextMarker)\n\nfunction markTextShared(doc, from, to, options, type) {\n  options = copyObj(options)\n  options.shared = false\n  let markers = [markText(doc, from, to, options, type)], primary = markers[0]\n  let widget = options.widgetNode\n  linkedDocs(doc, doc => {\n    if (widget) options.widgetNode = widget.cloneNode(true)\n    markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type))\n    for (let i = 0; i < doc.linked.length; ++i)\n      if (doc.linked[i].isParent) return\n    primary = lst(markers)\n  })\n  return new SharedTextMarker(markers, primary)\n}\n\nexport function findSharedMarkers(doc) {\n  return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), m => m.parent)\n}\n\nexport function copySharedMarkers(doc, markers) {\n  for (let i = 0; i < markers.length; i++) {\n    let marker = markers[i], pos = marker.find()\n    let mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to)\n    if (cmp(mFrom, mTo)) {\n      let subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type)\n      marker.markers.push(subMark)\n      subMark.parent = marker\n    }\n  }\n}\n\nexport function detachSharedMarkers(markers) {\n  for (let i = 0; i < markers.length; i++) {\n    let marker = markers[i], linked = [marker.primary.doc]\n    linkedDocs(marker.primary.doc, d => linked.push(d))\n    for (let j = 0; j < marker.markers.length; j++) {\n      let subMarker = marker.markers[j]\n      if (indexOf(linked, subMarker.doc) == -1) {\n        subMarker.parent = null\n        marker.markers.splice(j--, 1)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/model/selection.js",
    "content": "import { cmp, copyPos, equalCursorPos, maxPos, minPos } from \"../line/pos.js\"\nimport { indexOf } from \"../util/misc.js\"\n\n// Selection objects are immutable. A new one is created every time\n// the selection changes. A selection is one or more non-overlapping\n// (and non-touching) ranges, sorted, and an integer that indicates\n// which one is the primary selection (the one that's scrolled into\n// view, that getCursor returns, etc).\nexport class Selection {\n  constructor(ranges, primIndex) {\n    this.ranges = ranges\n    this.primIndex = primIndex\n  }\n\n  primary() { return this.ranges[this.primIndex] }\n\n  equals(other) {\n    if (other == this) return true\n    if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false\n    for (let i = 0; i < this.ranges.length; i++) {\n      let here = this.ranges[i], there = other.ranges[i]\n      if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) return false\n    }\n    return true\n  }\n\n  deepCopy() {\n    let out = []\n    for (let i = 0; i < this.ranges.length; i++)\n      out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head))\n    return new Selection(out, this.primIndex)\n  }\n\n  somethingSelected() {\n    for (let i = 0; i < this.ranges.length; i++)\n      if (!this.ranges[i].empty()) return true\n    return false\n  }\n\n  contains(pos, end) {\n    if (!end) end = pos\n    for (let i = 0; i < this.ranges.length; i++) {\n      let range = this.ranges[i]\n      if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)\n        return i\n    }\n    return -1\n  }\n}\n\nexport class Range {\n  constructor(anchor, head) {\n    this.anchor = anchor; this.head = head\n  }\n\n  from() { return minPos(this.anchor, this.head) }\n  to() { return maxPos(this.anchor, this.head) }\n  empty() { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }\n}\n\n// Take an unsorted, potentially overlapping set of ranges, and\n// build a selection out of it. 'Consumes' ranges array (modifying\n// it).\nexport function normalizeSelection(cm, ranges, primIndex) {\n  let mayTouch = cm && cm.options.selectionsMayTouch\n  let prim = ranges[primIndex]\n  ranges.sort((a, b) => cmp(a.from(), b.from()))\n  primIndex = indexOf(ranges, prim)\n  for (let i = 1; i < ranges.length; i++) {\n    let cur = ranges[i], prev = ranges[i - 1]\n    let diff = cmp(prev.to(), cur.from())\n    if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {\n      let from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to())\n      let inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head\n      if (i <= primIndex) --primIndex\n      ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))\n    }\n  }\n  return new Selection(ranges, primIndex)\n}\n\nexport function simpleSelection(anchor, head) {\n  return new Selection([new Range(anchor, head || anchor)], 0)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/model/selection_updates.js",
    "content": "import { signalLater } from \"../util/operation_group.js\"\nimport { ensureCursorVisible } from \"../display/scrolling.js\"\nimport { clipPos, cmp, Pos } from \"../line/pos.js\"\nimport { getLine } from \"../line/utils_line.js\"\nimport { hasHandler, signal, signalCursorActivity } from \"../util/event.js\"\nimport { lst, sel_dontScroll } from \"../util/misc.js\"\n\nimport { addSelectionToHistory } from \"./history.js\"\nimport { normalizeSelection, Range, Selection, simpleSelection } from \"./selection.js\"\n\n// The 'scroll' parameter given to many of these indicated whether\n// the new cursor position should be scrolled into view after\n// modifying the selection.\n\n// If shift is held or the extend flag is set, extends a range to\n// include a given position (and optionally a second position).\n// Otherwise, simply returns the range between the given positions.\n// Used for cursor motion and such.\nexport function extendRange(range, head, other, extend) {\n  if (extend) {\n    let anchor = range.anchor\n    if (other) {\n      let posBefore = cmp(head, anchor) < 0\n      if (posBefore != (cmp(other, anchor) < 0)) {\n        anchor = head\n        head = other\n      } else if (posBefore != (cmp(head, other) < 0)) {\n        head = other\n      }\n    }\n    return new Range(anchor, head)\n  } else {\n    return new Range(other || head, head)\n  }\n}\n\n// Extend the primary selection range, discard the rest.\nexport function extendSelection(doc, head, other, options, extend) {\n  if (extend == null) extend = doc.cm && (doc.cm.display.shift || doc.extend)\n  setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options)\n}\n\n// Extend all selections (pos is an array of selections with length\n// equal the number of selections)\nexport function extendSelections(doc, heads, options) {\n  let out = []\n  let extend = doc.cm && (doc.cm.display.shift || doc.extend)\n  for (let i = 0; i < doc.sel.ranges.length; i++)\n    out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend)\n  let newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex)\n  setSelection(doc, newSel, options)\n}\n\n// Updates a single range in the selection.\nexport function replaceOneSelection(doc, i, range, options) {\n  let ranges = doc.sel.ranges.slice(0)\n  ranges[i] = range\n  setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options)\n}\n\n// Reset the selection to a single range.\nexport function setSimpleSelection(doc, anchor, head, options) {\n  setSelection(doc, simpleSelection(anchor, head), options)\n}\n\n// Give beforeSelectionChange handlers a change to influence a\n// selection update.\nfunction filterSelectionChange(doc, sel, options) {\n  let obj = {\n    ranges: sel.ranges,\n    update: function(ranges) {\n      this.ranges = []\n      for (let i = 0; i < ranges.length; i++)\n        this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),\n                                   clipPos(doc, ranges[i].head))\n    },\n    origin: options && options.origin\n  }\n  signal(doc, \"beforeSelectionChange\", doc, obj)\n  if (doc.cm) signal(doc.cm, \"beforeSelectionChange\", doc.cm, obj)\n  if (obj.ranges != sel.ranges) return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1)\n  else return sel\n}\n\nexport function setSelectionReplaceHistory(doc, sel, options) {\n  let done = doc.history.done, last = lst(done)\n  if (last && last.ranges) {\n    done[done.length - 1] = sel\n    setSelectionNoUndo(doc, sel, options)\n  } else {\n    setSelection(doc, sel, options)\n  }\n}\n\n// Set a new selection.\nexport function setSelection(doc, sel, options) {\n  setSelectionNoUndo(doc, sel, options)\n  addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)\n}\n\nexport function setSelectionNoUndo(doc, sel, options) {\n  if (hasHandler(doc, \"beforeSelectionChange\") || doc.cm && hasHandler(doc.cm, \"beforeSelectionChange\"))\n    sel = filterSelectionChange(doc, sel, options)\n\n  let bias = options && options.bias ||\n    (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)\n  setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))\n\n  if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption(\"readOnly\") != \"nocursor\")\n    ensureCursorVisible(doc.cm)\n}\n\nfunction setSelectionInner(doc, sel) {\n  if (sel.equals(doc.sel)) return\n\n  doc.sel = sel\n\n  if (doc.cm) {\n    doc.cm.curOp.updateInput = 1\n    doc.cm.curOp.selectionChanged = true\n    signalCursorActivity(doc.cm)\n  }\n  signalLater(doc, \"cursorActivity\", doc)\n}\n\n// Verify that the selection does not partially select any atomic\n// marked ranges.\nexport function reCheckSelection(doc) {\n  setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false))\n}\n\n// Return a selection that does not partially select any atomic\n// ranges.\nfunction skipAtomicInSelection(doc, sel, bias, mayClear) {\n  let out\n  for (let i = 0; i < sel.ranges.length; i++) {\n    let range = sel.ranges[i]\n    let old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]\n    let newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear)\n    let newHead = range.head == range.anchor ? newAnchor : skipAtomic(doc, range.head, old && old.head, bias, mayClear)\n    if (out || newAnchor != range.anchor || newHead != range.head) {\n      if (!out) out = sel.ranges.slice(0, i)\n      out[i] = new Range(newAnchor, newHead)\n    }\n  }\n  return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel\n}\n\nfunction skipAtomicInner(doc, pos, oldPos, dir, mayClear) {\n  let line = getLine(doc, pos.line)\n  if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) {\n    let sp = line.markedSpans[i], m = sp.marker\n\n    // Determine if we should prevent the cursor being placed to the left/right of an atomic marker\n    // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it\n    // is with selectLeft/Right\n    let preventCursorLeft = (\"selectLeft\" in m) ? !m.selectLeft : m.inclusiveLeft\n    let preventCursorRight = (\"selectRight\" in m) ? !m.selectRight : m.inclusiveRight\n\n    if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&\n        (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {\n      if (mayClear) {\n        signal(m, \"beforeCursorEnter\")\n        if (m.explicitlyCleared) {\n          if (!line.markedSpans) break\n          else {--i; continue}\n        }\n      }\n      if (!m.atomic) continue\n\n      if (oldPos) {\n        let near = m.find(dir < 0 ? 1 : -1), diff\n        if (dir < 0 ? preventCursorRight : preventCursorLeft)\n          near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null)\n        if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))\n          return skipAtomicInner(doc, near, pos, dir, mayClear)\n      }\n\n      let far = m.find(dir < 0 ? -1 : 1)\n      if (dir < 0 ? preventCursorLeft : preventCursorRight)\n        far = movePos(doc, far, dir, far.line == pos.line ? line : null)\n      return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null\n    }\n  }\n  return pos\n}\n\n// Ensure a given position is not inside an atomic range.\nexport function skipAtomic(doc, pos, oldPos, bias, mayClear) {\n  let dir = bias || 1\n  let found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||\n      (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||\n      skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||\n      (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))\n  if (!found) {\n    doc.cantEdit = true\n    return Pos(doc.first, 0)\n  }\n  return found\n}\n\nfunction movePos(doc, pos, dir, line) {\n  if (dir < 0 && pos.ch == 0) {\n    if (pos.line > doc.first) return clipPos(doc, Pos(pos.line - 1))\n    else return null\n  } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {\n    if (pos.line < doc.first + doc.size - 1) return Pos(pos.line + 1, 0)\n    else return null\n  } else {\n    return new Pos(pos.line, pos.ch + dir)\n  }\n}\n\nexport function selectAll(cm) {\n  cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/modes.js",
    "content": "import { copyObj, createObj } from \"./util/misc.js\"\n\n// Known modes, by name and by MIME\nexport let modes = {}, mimeModes = {}\n\n// Extra arguments are stored as the mode's dependencies, which is\n// used by (legacy) mechanisms like loadmode.js to automatically\n// load a mode. (Preferred mechanism is the require/define calls.)\nexport function defineMode(name, mode) {\n  if (arguments.length > 2)\n    mode.dependencies = Array.prototype.slice.call(arguments, 2)\n  modes[name] = mode\n}\n\nexport function defineMIME(mime, spec) {\n  mimeModes[mime] = spec\n}\n\n// Given a MIME type, a {name, ...options} config object, or a name\n// string, return a mode config object.\nexport function resolveMode(spec) {\n  if (typeof spec == \"string\" && mimeModes.hasOwnProperty(spec)) {\n    spec = mimeModes[spec]\n  } else if (spec && typeof spec.name == \"string\" && mimeModes.hasOwnProperty(spec.name)) {\n    let found = mimeModes[spec.name]\n    if (typeof found == \"string\") found = {name: found}\n    spec = createObj(found, spec)\n    spec.name = found.name\n  } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+xml$/.test(spec)) {\n    return resolveMode(\"application/xml\")\n  } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+json$/.test(spec)) {\n    return resolveMode(\"application/json\")\n  }\n  if (typeof spec == \"string\") return {name: spec}\n  else return spec || {name: \"null\"}\n}\n\n// Given a mode spec (anything that resolveMode accepts), find and\n// initialize an actual mode object.\nexport function getMode(options, spec) {\n  spec = resolveMode(spec)\n  let mfactory = modes[spec.name]\n  if (!mfactory) return getMode(options, \"text/plain\")\n  let modeObj = mfactory(options, spec)\n  if (modeExtensions.hasOwnProperty(spec.name)) {\n    let exts = modeExtensions[spec.name]\n    for (let prop in exts) {\n      if (!exts.hasOwnProperty(prop)) continue\n      if (modeObj.hasOwnProperty(prop)) modeObj[\"_\" + prop] = modeObj[prop]\n      modeObj[prop] = exts[prop]\n    }\n  }\n  modeObj.name = spec.name\n  if (spec.helperType) modeObj.helperType = spec.helperType\n  if (spec.modeProps) for (let prop in spec.modeProps)\n    modeObj[prop] = spec.modeProps[prop]\n\n  return modeObj\n}\n\n// This can be used to attach properties to mode objects from\n// outside the actual mode definition.\nexport let modeExtensions = {}\nexport function extendMode(mode, properties) {\n  let exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})\n  copyObj(properties, exts)\n}\n\nexport function copyState(mode, state) {\n  if (state === true) return state\n  if (mode.copyState) return mode.copyState(state)\n  let nstate = {}\n  for (let n in state) {\n    let val = state[n]\n    if (val instanceof Array) val = val.concat([])\n    nstate[n] = val\n  }\n  return nstate\n}\n\n// Given a mode and a state (for that mode), find the inner mode and\n// state at the position that the state refers to.\nexport function innerMode(mode, state) {\n  let info\n  while (mode.innerMode) {\n    info = mode.innerMode(state)\n    if (!info || info.mode == mode) break\n    state = info.state\n    mode = info.mode\n  }\n  return info || {mode: mode, state: state}\n}\n\nexport function startState(mode, a1, a2) {\n  return mode.startState ? mode.startState(a1, a2) : true\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/util/StringStream.js",
    "content": "import { countColumn } from \"./misc.js\"\n\n// STRING STREAM\n\n// Fed to the mode parsers, provides helper functions to make\n// parsers more succinct.\n\nclass StringStream {\n  constructor(string, tabSize, lineOracle) {\n    this.pos = this.start = 0\n    this.string = string\n    this.tabSize = tabSize || 8\n    this.lastColumnPos = this.lastColumnValue = 0\n    this.lineStart = 0\n    this.lineOracle = lineOracle\n  }\n\n  eol() {return this.pos >= this.string.length}\n  sol() {return this.pos == this.lineStart}\n  peek() {return this.string.charAt(this.pos) || undefined}\n  next() {\n    if (this.pos < this.string.length)\n      return this.string.charAt(this.pos++)\n  }\n  eat(match) {\n    let ch = this.string.charAt(this.pos)\n    let ok\n    if (typeof match == \"string\") ok = ch == match\n    else ok = ch && (match.test ? match.test(ch) : match(ch))\n    if (ok) {++this.pos; return ch}\n  }\n  eatWhile(match) {\n    let start = this.pos\n    while (this.eat(match)){}\n    return this.pos > start\n  }\n  eatSpace() {\n    let start = this.pos\n    while (/[\\s\\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos\n    return this.pos > start\n  }\n  skipToEnd() {this.pos = this.string.length}\n  skipTo(ch) {\n    let found = this.string.indexOf(ch, this.pos)\n    if (found > -1) {this.pos = found; return true}\n  }\n  backUp(n) {this.pos -= n}\n  column() {\n    if (this.lastColumnPos < this.start) {\n      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)\n      this.lastColumnPos = this.start\n    }\n    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n  }\n  indentation() {\n    return countColumn(this.string, null, this.tabSize) -\n      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n  }\n  match(pattern, consume, caseInsensitive) {\n    if (typeof pattern == \"string\") {\n      let cased = str => caseInsensitive ? str.toLowerCase() : str\n      let substr = this.string.substr(this.pos, pattern.length)\n      if (cased(substr) == cased(pattern)) {\n        if (consume !== false) this.pos += pattern.length\n        return true\n      }\n    } else {\n      let match = this.string.slice(this.pos).match(pattern)\n      if (match && match.index > 0) return null\n      if (match && consume !== false) this.pos += match[0].length\n      return match\n    }\n  }\n  current(){return this.string.slice(this.start, this.pos)}\n  hideFirstChars(n, inner) {\n    this.lineStart += n\n    try { return inner() }\n    finally { this.lineStart -= n }\n  }\n  lookAhead(n) {\n    let oracle = this.lineOracle\n    return oracle && oracle.lookAhead(n)\n  }\n  baseToken() {\n    let oracle = this.lineOracle\n    return oracle && oracle.baseToken(this.pos)\n  }\n}\n\nexport default StringStream\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/util/bidi.js",
    "content": "import { lst } from \"./misc.js\"\n\n// BIDI HELPERS\n\nexport function iterateBidiSections(order, from, to, f) {\n  if (!order) return f(from, to, \"ltr\", 0)\n  let found = false\n  for (let i = 0; i < order.length; ++i) {\n    let part = order[i]\n    if (part.from < to && part.to > from || from == to && part.to == from) {\n      f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? \"rtl\" : \"ltr\", i)\n      found = true\n    }\n  }\n  if (!found) f(from, to, \"ltr\")\n}\n\nexport let bidiOther = null\nexport function getBidiPartAt(order, ch, sticky) {\n  let found\n  bidiOther = null\n  for (let i = 0; i < order.length; ++i) {\n    let cur = order[i]\n    if (cur.from < ch && cur.to > ch) return i\n    if (cur.to == ch) {\n      if (cur.from != cur.to && sticky == \"before\") found = i\n      else bidiOther = i\n    }\n    if (cur.from == ch) {\n      if (cur.from != cur.to && sticky != \"before\") found = i\n      else bidiOther = i\n    }\n  }\n  return found != null ? found : bidiOther\n}\n\n// Bidirectional ordering algorithm\n// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm\n// that this (partially) implements.\n\n// One-char codes used for character types:\n// L (L):   Left-to-Right\n// R (R):   Right-to-Left\n// r (AL):  Right-to-Left Arabic\n// 1 (EN):  European Number\n// + (ES):  European Number Separator\n// % (ET):  European Number Terminator\n// n (AN):  Arabic Number\n// , (CS):  Common Number Separator\n// m (NSM): Non-Spacing Mark\n// b (BN):  Boundary Neutral\n// s (B):   Paragraph Separator\n// t (S):   Segment Separator\n// w (WS):  Whitespace\n// N (ON):  Other Neutrals\n\n// Returns null if characters are ordered as they appear\n// (left-to-right), or an array of sections ({from, to, level}\n// objects) in the order in which they occur visually.\nlet bidiOrdering = (function() {\n  // Character types for codepoints 0 to 0xff\n  let lowTypes = \"bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN\"\n  // Character types for codepoints 0x600 to 0x6f9\n  let arabicTypes = \"nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111\"\n  function charType(code) {\n    if (code <= 0xf7) return lowTypes.charAt(code)\n    else if (0x590 <= code && code <= 0x5f4) return \"R\"\n    else if (0x600 <= code && code <= 0x6f9) return arabicTypes.charAt(code - 0x600)\n    else if (0x6ee <= code && code <= 0x8ac) return \"r\"\n    else if (0x2000 <= code && code <= 0x200b) return \"w\"\n    else if (code == 0x200c) return \"b\"\n    else return \"L\"\n  }\n\n  let bidiRE = /[\\u0590-\\u05f4\\u0600-\\u06ff\\u0700-\\u08ac]/\n  let isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/\n\n  function BidiSpan(level, from, to) {\n    this.level = level\n    this.from = from; this.to = to\n  }\n\n  return function(str, direction) {\n    let outerType = direction == \"ltr\" ? \"L\" : \"R\"\n\n    if (str.length == 0 || direction == \"ltr\" && !bidiRE.test(str)) return false\n    let len = str.length, types = []\n    for (let i = 0; i < len; ++i)\n      types.push(charType(str.charCodeAt(i)))\n\n    // W1. Examine each non-spacing mark (NSM) in the level run, and\n    // change the type of the NSM to the type of the previous\n    // character. If the NSM is at the start of the level run, it will\n    // get the type of sor.\n    for (let i = 0, prev = outerType; i < len; ++i) {\n      let type = types[i]\n      if (type == \"m\") types[i] = prev\n      else prev = type\n    }\n\n    // W2. Search backwards from each instance of a European number\n    // until the first strong type (R, L, AL, or sor) is found. If an\n    // AL is found, change the type of the European number to Arabic\n    // number.\n    // W3. Change all ALs to R.\n    for (let i = 0, cur = outerType; i < len; ++i) {\n      let type = types[i]\n      if (type == \"1\" && cur == \"r\") types[i] = \"n\"\n      else if (isStrong.test(type)) { cur = type; if (type == \"r\") types[i] = \"R\" }\n    }\n\n    // W4. A single European separator between two European numbers\n    // changes to a European number. A single common separator between\n    // two numbers of the same type changes to that type.\n    for (let i = 1, prev = types[0]; i < len - 1; ++i) {\n      let type = types[i]\n      if (type == \"+\" && prev == \"1\" && types[i+1] == \"1\") types[i] = \"1\"\n      else if (type == \",\" && prev == types[i+1] &&\n               (prev == \"1\" || prev == \"n\")) types[i] = prev\n      prev = type\n    }\n\n    // W5. A sequence of European terminators adjacent to European\n    // numbers changes to all European numbers.\n    // W6. Otherwise, separators and terminators change to Other\n    // Neutral.\n    for (let i = 0; i < len; ++i) {\n      let type = types[i]\n      if (type == \",\") types[i] = \"N\"\n      else if (type == \"%\") {\n        let end\n        for (end = i + 1; end < len && types[end] == \"%\"; ++end) {}\n        let replace = (i && types[i-1] == \"!\") || (end < len && types[end] == \"1\") ? \"1\" : \"N\"\n        for (let j = i; j < end; ++j) types[j] = replace\n        i = end - 1\n      }\n    }\n\n    // W7. Search backwards from each instance of a European number\n    // until the first strong type (R, L, or sor) is found. If an L is\n    // found, then change the type of the European number to L.\n    for (let i = 0, cur = outerType; i < len; ++i) {\n      let type = types[i]\n      if (cur == \"L\" && type == \"1\") types[i] = \"L\"\n      else if (isStrong.test(type)) cur = type\n    }\n\n    // N1. A sequence of neutrals takes the direction of the\n    // surrounding strong text if the text on both sides has the same\n    // direction. European and Arabic numbers act as if they were R in\n    // terms of their influence on neutrals. Start-of-level-run (sor)\n    // and end-of-level-run (eor) are used at level run boundaries.\n    // N2. Any remaining neutrals take the embedding direction.\n    for (let i = 0; i < len; ++i) {\n      if (isNeutral.test(types[i])) {\n        let end\n        for (end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}\n        let before = (i ? types[i-1] : outerType) == \"L\"\n        let after = (end < len ? types[end] : outerType) == \"L\"\n        let replace = before == after ? (before ? \"L\" : \"R\") : outerType\n        for (let j = i; j < end; ++j) types[j] = replace\n        i = end - 1\n      }\n    }\n\n    // Here we depart from the documented algorithm, in order to avoid\n    // building up an actual levels array. Since there are only three\n    // levels (0, 1, 2) in an implementation that doesn't take\n    // explicit embedding into account, we can build up the order on\n    // the fly, without following the level-based algorithm.\n    let order = [], m\n    for (let i = 0; i < len;) {\n      if (countsAsLeft.test(types[i])) {\n        let start = i\n        for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}\n        order.push(new BidiSpan(0, start, i))\n      } else {\n        let pos = i, at = order.length, isRTL = direction == \"rtl\" ? 1 : 0\n        for (++i; i < len && types[i] != \"L\"; ++i) {}\n        for (let j = pos; j < i;) {\n          if (countsAsNum.test(types[j])) {\n            if (pos < j) { order.splice(at, 0, new BidiSpan(1, pos, j)); at += isRTL }\n            let nstart = j\n            for (++j; j < i && countsAsNum.test(types[j]); ++j) {}\n            order.splice(at, 0, new BidiSpan(2, nstart, j))\n            at += isRTL\n            pos = j\n          } else ++j\n        }\n        if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i))\n      }\n    }\n    if (direction == \"ltr\") {\n      if (order[0].level == 1 && (m = str.match(/^\\s+/))) {\n        order[0].from = m[0].length\n        order.unshift(new BidiSpan(0, 0, m[0].length))\n      }\n      if (lst(order).level == 1 && (m = str.match(/\\s+$/))) {\n        lst(order).to -= m[0].length\n        order.push(new BidiSpan(0, len - m[0].length, len))\n      }\n    }\n\n    return direction == \"rtl\" ? order.reverse() : order\n  }\n})()\n\n// Get the bidi ordering for the given line (and cache it). Returns\n// false for lines that are fully left-to-right, and an array of\n// BidiSpan objects otherwise.\nexport function getOrder(line, direction) {\n  let order = line.order\n  if (order == null) order = line.order = bidiOrdering(line.text, direction)\n  return order\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/util/browser.js",
    "content": "// Kludges for bugs and behavior differences that can't be feature\n// detected are enabled based on userAgent etc sniffing.\nlet userAgent = navigator.userAgent\nlet platform = navigator.platform\n\nexport let gecko = /gecko\\/\\d/i.test(userAgent)\nlet ie_upto10 = /MSIE \\d/.test(userAgent)\nlet ie_11up = /Trident\\/(?:[7-9]|\\d{2,})\\..*rv:(\\d+)/.exec(userAgent)\nlet edge = /Edge\\/(\\d+)/.exec(userAgent)\nexport let ie = ie_upto10 || ie_11up || edge\nexport let ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1])\nexport let webkit = !edge && /WebKit\\//.test(userAgent)\nlet qtwebkit = webkit && /Qt\\/\\d+\\.\\d+/.test(userAgent)\nexport let chrome = !edge && /Chrome\\/(\\d+)/.exec(userAgent)\nexport let chrome_version = chrome && +chrome[1]\nexport let presto = /Opera\\//.test(userAgent)\nexport let safari = /Apple Computer/.test(navigator.vendor)\nexport let mac_geMountainLion = /Mac OS X 1\\d\\D([8-9]|\\d\\d)\\D/.test(userAgent)\nexport let phantom = /PhantomJS/.test(userAgent)\n\nexport let ios = safari && (/Mobile\\/\\w+/.test(userAgent) || navigator.maxTouchPoints > 2)\nexport let android = /Android/.test(userAgent)\n// This is woefully incomplete. Suggestions for alternative methods welcome.\nexport let mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent)\nexport let mac = ios || /Mac/.test(platform)\nexport let chromeOS = /\\bCrOS\\b/.test(userAgent)\nexport let windows = /win/i.test(platform)\n\nlet presto_version = presto && userAgent.match(/Version\\/(\\d*\\.\\d*)/)\nif (presto_version) presto_version = Number(presto_version[1])\nif (presto_version && presto_version >= 15) { presto = false; webkit = true }\n// Some browsers use the wrong event properties to signal cmd/ctrl on OS X\nexport let flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11))\nexport let captureRightClick = gecko || (ie && ie_version >= 9)\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/util/dom.js",
    "content": "import { ie, ios } from \"./browser.js\"\n\nexport function classTest(cls) { return new RegExp(\"(^|\\\\s)\" + cls + \"(?:$|\\\\s)\\\\s*\") }\n\nexport let rmClass = function(node, cls) {\n  let current = node.className\n  let match = classTest(cls).exec(current)\n  if (match) {\n    let after = current.slice(match.index + match[0].length)\n    node.className = current.slice(0, match.index) + (after ? match[1] + after : \"\")\n  }\n}\n\nexport function removeChildren(e) {\n  for (let count = e.childNodes.length; count > 0; --count)\n    e.removeChild(e.firstChild)\n  return e\n}\n\nexport function removeChildrenAndAdd(parent, e) {\n  return removeChildren(parent).appendChild(e)\n}\n\nexport function elt(tag, content, className, style) {\n  let e = document.createElement(tag)\n  if (className) e.className = className\n  if (style) e.style.cssText = style\n  if (typeof content == \"string\") e.appendChild(document.createTextNode(content))\n  else if (content) for (let i = 0; i < content.length; ++i) e.appendChild(content[i])\n  return e\n}\n// wrapper for elt, which removes the elt from the accessibility tree\nexport function eltP(tag, content, className, style) {\n  let e = elt(tag, content, className, style)\n  e.setAttribute(\"role\", \"presentation\")\n  return e\n}\n\nexport let range\nif (document.createRange) range = function(node, start, end, endNode) {\n  let r = document.createRange()\n  r.setEnd(endNode || node, end)\n  r.setStart(node, start)\n  return r\n}\nelse range = function(node, start, end) {\n  let r = document.body.createTextRange()\n  try { r.moveToElementText(node.parentNode) }\n  catch(e) { return r }\n  r.collapse(true)\n  r.moveEnd(\"character\", end)\n  r.moveStart(\"character\", start)\n  return r\n}\n\nexport function contains(parent, child) {\n  if (child.nodeType == 3) // Android browser always returns false when child is a textnode\n    child = child.parentNode\n  if (parent.contains)\n    return parent.contains(child)\n  do {\n    if (child.nodeType == 11) child = child.host\n    if (child == parent) return true\n  } while (child = child.parentNode)\n}\n\nexport function activeElt(rootNode) {\n  // IE and Edge may throw an \"Unspecified Error\" when accessing document.activeElement.\n  // IE < 10 will throw when accessed while the page is loading or in an iframe.\n  // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.\n  let doc = rootNode.ownerDocument || rootNode\n  let activeElement\n  try {\n    activeElement = rootNode.activeElement\n  } catch(e) {\n    activeElement = doc.body || null\n  }\n  while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)\n    activeElement = activeElement.shadowRoot.activeElement\n  return activeElement\n}\n\nexport function addClass(node, cls) {\n  let current = node.className\n  if (!classTest(cls).test(current)) node.className += (current ? \" \" : \"\") + cls\n}\nexport function joinClasses(a, b) {\n  let as = a.split(\" \")\n  for (let i = 0; i < as.length; i++)\n    if (as[i] && !classTest(as[i]).test(b)) b += \" \" + as[i]\n  return b\n}\n\nexport let selectInput = function(node) { node.select() }\nif (ios) // Mobile Safari apparently has a bug where select() is broken.\n  selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length }\nelse if (ie) // Suppress mysterious IE10 errors\n  selectInput = function(node) { try { node.select() } catch(_e) {} }\n\nexport function doc(cm) { return cm.display.wrapper.ownerDocument }\n\nexport function root(cm) {\n  return rootNode(cm.display.wrapper)\n}\n\nexport function rootNode(element) {\n  // Detect modern browsers (2017+).\n  return element.getRootNode ? element.getRootNode() : element.ownerDocument\n}\n\nexport function win(cm) { return doc(cm).defaultView }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/util/event.js",
    "content": "import { mac } from \"./browser.js\"\nimport { indexOf } from \"./misc.js\"\n\n// EVENT HANDLING\n\n// Lightweight event framework. on/off also work on DOM nodes,\n// registering native DOM handlers.\n\nconst noHandlers = []\n\nexport let on = function(emitter, type, f) {\n  if (emitter.addEventListener) {\n    emitter.addEventListener(type, f, false)\n  } else if (emitter.attachEvent) {\n    emitter.attachEvent(\"on\" + type, f)\n  } else {\n    let map = emitter._handlers || (emitter._handlers = {})\n    map[type] = (map[type] || noHandlers).concat(f)\n  }\n}\n\nexport function getHandlers(emitter, type) {\n  return emitter._handlers && emitter._handlers[type] || noHandlers\n}\n\nexport function off(emitter, type, f) {\n  if (emitter.removeEventListener) {\n    emitter.removeEventListener(type, f, false)\n  } else if (emitter.detachEvent) {\n    emitter.detachEvent(\"on\" + type, f)\n  } else {\n    let map = emitter._handlers, arr = map && map[type]\n    if (arr) {\n      let index = indexOf(arr, f)\n      if (index > -1)\n        map[type] = arr.slice(0, index).concat(arr.slice(index + 1))\n    }\n  }\n}\n\nexport function signal(emitter, type /*, values...*/) {\n  let handlers = getHandlers(emitter, type)\n  if (!handlers.length) return\n  let args = Array.prototype.slice.call(arguments, 2)\n  for (let i = 0; i < handlers.length; ++i) handlers[i].apply(null, args)\n}\n\n// The DOM events that CodeMirror handles can be overridden by\n// registering a (non-DOM) handler on the editor for the event name,\n// and preventDefault-ing the event in that handler.\nexport function signalDOMEvent(cm, e, override) {\n  if (typeof e == \"string\")\n    e = {type: e, preventDefault: function() { this.defaultPrevented = true }}\n  signal(cm, override || e.type, cm, e)\n  return e_defaultPrevented(e) || e.codemirrorIgnore\n}\n\nexport function signalCursorActivity(cm) {\n  let arr = cm._handlers && cm._handlers.cursorActivity\n  if (!arr) return\n  let set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = [])\n  for (let i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)\n    set.push(arr[i])\n}\n\nexport function hasHandler(emitter, type) {\n  return getHandlers(emitter, type).length > 0\n}\n\n// Add on and off methods to a constructor's prototype, to make\n// registering events on such objects more convenient.\nexport function eventMixin(ctor) {\n  ctor.prototype.on = function(type, f) {on(this, type, f)}\n  ctor.prototype.off = function(type, f) {off(this, type, f)}\n}\n\n// Due to the fact that we still support jurassic IE versions, some\n// compatibility wrappers are needed.\n\nexport function e_preventDefault(e) {\n  if (e.preventDefault) e.preventDefault()\n  else e.returnValue = false\n}\nexport function e_stopPropagation(e) {\n  if (e.stopPropagation) e.stopPropagation()\n  else e.cancelBubble = true\n}\nexport function e_defaultPrevented(e) {\n  return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false\n}\nexport function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}\n\nexport function e_target(e) {return e.target || e.srcElement}\nexport function e_button(e) {\n  let b = e.which\n  if (b == null) {\n    if (e.button & 1) b = 1\n    else if (e.button & 2) b = 3\n    else if (e.button & 4) b = 2\n  }\n  if (mac && e.ctrlKey && b == 1) b = 3\n  return b\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/util/feature_detection.js",
    "content": "import { elt, range, removeChildren, removeChildrenAndAdd } from \"./dom.js\"\nimport { ie, ie_version } from \"./browser.js\"\n\n// Detect drag-and-drop\nexport let dragAndDrop = function() {\n  // There is *some* kind of drag-and-drop support in IE6-8, but I\n  // couldn't get it to work yet.\n  if (ie && ie_version < 9) return false\n  let div = elt('div')\n  return \"draggable\" in div || \"dragDrop\" in div\n}()\n\nlet zwspSupported\nexport function zeroWidthElement(measure) {\n  if (zwspSupported == null) {\n    let test = elt(\"span\", \"\\u200b\")\n    removeChildrenAndAdd(measure, elt(\"span\", [test, document.createTextNode(\"x\")]))\n    if (measure.firstChild.offsetHeight != 0)\n      zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8)\n  }\n  let node = zwspSupported ? elt(\"span\", \"\\u200b\") :\n    elt(\"span\", \"\\u00a0\", null, \"display: inline-block; width: 1px; margin-right: -1px\")\n  node.setAttribute(\"cm-text\", \"\")\n  return node\n}\n\n// Feature-detect IE's crummy client rect reporting for bidi text\nlet badBidiRects\nexport function hasBadBidiRects(measure) {\n  if (badBidiRects != null) return badBidiRects\n  let txt = removeChildrenAndAdd(measure, document.createTextNode(\"A\\u062eA\"))\n  let r0 = range(txt, 0, 1).getBoundingClientRect()\n  let r1 = range(txt, 1, 2).getBoundingClientRect()\n  removeChildren(measure)\n  if (!r0 || r0.left == r0.right) return false // Safari returns null in some cases (#2780)\n  return badBidiRects = (r1.right - r0.right < 3)\n}\n\n// See if \"\".split is the broken IE version, if so, provide an\n// alternative way to split lines.\nexport let splitLinesAuto = \"\\n\\nb\".split(/\\n/).length != 3 ? string => {\n  let pos = 0, result = [], l = string.length\n  while (pos <= l) {\n    let nl = string.indexOf(\"\\n\", pos)\n    if (nl == -1) nl = string.length\n    let line = string.slice(pos, string.charAt(nl - 1) == \"\\r\" ? nl - 1 : nl)\n    let rt = line.indexOf(\"\\r\")\n    if (rt != -1) {\n      result.push(line.slice(0, rt))\n      pos += rt + 1\n    } else {\n      result.push(line)\n      pos = nl + 1\n    }\n  }\n  return result\n} : string => string.split(/\\r\\n?|\\n/)\n\nexport let hasSelection = window.getSelection ? te => {\n  try { return te.selectionStart != te.selectionEnd }\n  catch(e) { return false }\n} : te => {\n  let range\n  try {range = te.ownerDocument.selection.createRange()}\n  catch(e) {}\n  if (!range || range.parentElement() != te) return false\n  return range.compareEndPoints(\"StartToEnd\", range) != 0\n}\n\nexport let hasCopyEvent = (() => {\n  let e = elt(\"div\")\n  if (\"oncopy\" in e) return true\n  e.setAttribute(\"oncopy\", \"return;\")\n  return typeof e.oncopy == \"function\"\n})()\n\nlet badZoomedRects = null\nexport function hasBadZoomedRects(measure) {\n  if (badZoomedRects != null) return badZoomedRects\n  let node = removeChildrenAndAdd(measure, elt(\"span\", \"x\"))\n  let normal = node.getBoundingClientRect()\n  let fromRange = range(node, 0, 1).getBoundingClientRect()\n  return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/util/misc.js",
    "content": "export function bind(f) {\n  let args = Array.prototype.slice.call(arguments, 1)\n  return function(){return f.apply(null, args)}\n}\n\nexport function copyObj(obj, target, overwrite) {\n  if (!target) target = {}\n  for (let prop in obj)\n    if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))\n      target[prop] = obj[prop]\n  return target\n}\n\n// Counts the column offset in a string, taking tabs into account.\n// Used mostly to find indentation.\nexport function countColumn(string, end, tabSize, startIndex, startValue) {\n  if (end == null) {\n    end = string.search(/[^\\s\\u00a0]/)\n    if (end == -1) end = string.length\n  }\n  for (let i = startIndex || 0, n = startValue || 0;;) {\n    let nextTab = string.indexOf(\"\\t\", i)\n    if (nextTab < 0 || nextTab >= end)\n      return n + (end - i)\n    n += nextTab - i\n    n += tabSize - (n % tabSize)\n    i = nextTab + 1\n  }\n}\n\nexport class Delayed {\n  constructor() {\n    this.id = null\n    this.f = null\n    this.time = 0\n    this.handler = bind(this.onTimeout, this)\n  }\n  onTimeout(self) {\n    self.id = 0\n    if (self.time <= +new Date) {\n      self.f()\n    } else {\n      setTimeout(self.handler, self.time - +new Date)\n    }\n  }\n  set(ms, f) {\n    this.f = f\n    const time = +new Date + ms\n    if (!this.id || time < this.time) {\n      clearTimeout(this.id)\n      this.id = setTimeout(this.handler, ms)\n      this.time = time\n    }\n  }\n}\n\nexport function indexOf(array, elt) {\n  for (let i = 0; i < array.length; ++i)\n    if (array[i] == elt) return i\n  return -1\n}\n\n// Number of pixels added to scroller and sizer to hide scrollbar\nexport let scrollerGap = 50\n\n// Returned or thrown by various protocols to signal 'I'm not\n// handling this'.\nexport let Pass = {toString: function(){return \"CodeMirror.Pass\"}}\n\n// Reused option objects for setSelection & friends\nexport let sel_dontScroll = {scroll: false}, sel_mouse = {origin: \"*mouse\"}, sel_move = {origin: \"+move\"}\n\n// The inverse of countColumn -- find the offset that corresponds to\n// a particular column.\nexport function findColumn(string, goal, tabSize) {\n  for (let pos = 0, col = 0;;) {\n    let nextTab = string.indexOf(\"\\t\", pos)\n    if (nextTab == -1) nextTab = string.length\n    let skipped = nextTab - pos\n    if (nextTab == string.length || col + skipped >= goal)\n      return pos + Math.min(skipped, goal - col)\n    col += nextTab - pos\n    col += tabSize - (col % tabSize)\n    pos = nextTab + 1\n    if (col >= goal) return pos\n  }\n}\n\nlet spaceStrs = [\"\"]\nexport function spaceStr(n) {\n  while (spaceStrs.length <= n)\n    spaceStrs.push(lst(spaceStrs) + \" \")\n  return spaceStrs[n]\n}\n\nexport function lst(arr) { return arr[arr.length-1] }\n\nexport function map(array, f) {\n  let out = []\n  for (let i = 0; i < array.length; i++) out[i] = f(array[i], i)\n  return out\n}\n\nexport function insertSorted(array, value, score) {\n  let pos = 0, priority = score(value)\n  while (pos < array.length && score(array[pos]) <= priority) pos++\n  array.splice(pos, 0, value)\n}\n\nfunction nothing() {}\n\nexport function createObj(base, props) {\n  let inst\n  if (Object.create) {\n    inst = Object.create(base)\n  } else {\n    nothing.prototype = base\n    inst = new nothing()\n  }\n  if (props) copyObj(props, inst)\n  return inst\n}\n\nlet nonASCIISingleCaseWordChar = /[\\u00df\\u0587\\u0590-\\u05f4\\u0600-\\u06ff\\u3040-\\u309f\\u30a0-\\u30ff\\u3400-\\u4db5\\u4e00-\\u9fcc\\uac00-\\ud7af]/\nexport function isWordCharBasic(ch) {\n  return /\\w/.test(ch) || ch > \"\\x80\" &&\n    (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))\n}\nexport function isWordChar(ch, helper) {\n  if (!helper) return isWordCharBasic(ch)\n  if (helper.source.indexOf(\"\\\\w\") > -1 && isWordCharBasic(ch)) return true\n  return helper.test(ch)\n}\n\nexport function isEmpty(obj) {\n  for (let n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false\n  return true\n}\n\n// Extending unicode characters. A series of a non-extending char +\n// any number of extending chars is treated as a single unit as far\n// as editing and measuring is concerned. This is not fully correct,\n// since some scripts/fonts/browsers also treat other configurations\n// of code points as a group.\nlet extendingChars = /[\\u0300-\\u036f\\u0483-\\u0489\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u064b-\\u065e\\u0670\\u06d6-\\u06dc\\u06de-\\u06e4\\u06e7\\u06e8\\u06ea-\\u06ed\\u0711\\u0730-\\u074a\\u07a6-\\u07b0\\u07eb-\\u07f3\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0900-\\u0902\\u093c\\u0941-\\u0948\\u094d\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09bc\\u09be\\u09c1-\\u09c4\\u09cd\\u09d7\\u09e2\\u09e3\\u0a01\\u0a02\\u0a3c\\u0a41\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a70\\u0a71\\u0a75\\u0a81\\u0a82\\u0abc\\u0ac1-\\u0ac5\\u0ac7\\u0ac8\\u0acd\\u0ae2\\u0ae3\\u0b01\\u0b3c\\u0b3e\\u0b3f\\u0b41-\\u0b44\\u0b4d\\u0b56\\u0b57\\u0b62\\u0b63\\u0b82\\u0bbe\\u0bc0\\u0bcd\\u0bd7\\u0c3e-\\u0c40\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62\\u0c63\\u0cbc\\u0cbf\\u0cc2\\u0cc6\\u0ccc\\u0ccd\\u0cd5\\u0cd6\\u0ce2\\u0ce3\\u0d3e\\u0d41-\\u0d44\\u0d4d\\u0d57\\u0d62\\u0d63\\u0dca\\u0dcf\\u0dd2-\\u0dd4\\u0dd6\\u0ddf\\u0e31\\u0e34-\\u0e3a\\u0e47-\\u0e4e\\u0eb1\\u0eb4-\\u0eb9\\u0ebb\\u0ebc\\u0ec8-\\u0ecd\\u0f18\\u0f19\\u0f35\\u0f37\\u0f39\\u0f71-\\u0f7e\\u0f80-\\u0f84\\u0f86\\u0f87\\u0f90-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u102d-\\u1030\\u1032-\\u1037\\u1039\\u103a\\u103d\\u103e\\u1058\\u1059\\u105e-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108d\\u109d\\u135f\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17b7-\\u17bd\\u17c6\\u17c9-\\u17d3\\u17dd\\u180b-\\u180d\\u18a9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193b\\u1a17\\u1a18\\u1a56\\u1a58-\\u1a5e\\u1a60\\u1a62\\u1a65-\\u1a6c\\u1a73-\\u1a7c\\u1a7f\\u1b00-\\u1b03\\u1b34\\u1b36-\\u1b3a\\u1b3c\\u1b42\\u1b6b-\\u1b73\\u1b80\\u1b81\\u1ba2-\\u1ba5\\u1ba8\\u1ba9\\u1c2c-\\u1c33\\u1c36\\u1c37\\u1cd0-\\u1cd2\\u1cd4-\\u1ce0\\u1ce2-\\u1ce8\\u1ced\\u1dc0-\\u1de6\\u1dfd-\\u1dff\\u200c\\u200d\\u20d0-\\u20f0\\u2cef-\\u2cf1\\u2de0-\\u2dff\\u302a-\\u302f\\u3099\\u309a\\ua66f-\\ua672\\ua67c\\ua67d\\ua6f0\\ua6f1\\ua802\\ua806\\ua80b\\ua825\\ua826\\ua8c4\\ua8e0-\\ua8f1\\ua926-\\ua92d\\ua947-\\ua951\\ua980-\\ua982\\ua9b3\\ua9b6-\\ua9b9\\ua9bc\\uaa29-\\uaa2e\\uaa31\\uaa32\\uaa35\\uaa36\\uaa43\\uaa4c\\uaab0\\uaab2-\\uaab4\\uaab7\\uaab8\\uaabe\\uaabf\\uaac1\\uabe5\\uabe8\\uabed\\udc00-\\udfff\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe26\\uff9e\\uff9f]/\nexport function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }\n\n// Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.\nexport function skipExtendingChars(str, pos, dir) {\n  while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) pos += dir\n  return pos\n}\n\n// Returns the value from the range [`from`; `to`] that satisfies\n// `pred` and is closest to `from`. Assumes that at least `to`\n// satisfies `pred`. Supports `from` being greater than `to`.\nexport function findFirst(pred, from, to) {\n  // At any point we are certain `to` satisfies `pred`, don't know\n  // whether `from` does.\n  let dir = from > to ? -1 : 1\n  for (;;) {\n    if (from == to) return from\n    let midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF)\n    if (mid == from) return pred(mid) ? from : to\n    if (pred(mid)) to = mid\n    else from = mid + dir\n  }\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/src/util/operation_group.js",
    "content": "import { getHandlers } from \"./event.js\"\n\nlet operationGroup = null\n\nexport function pushOperation(op) {\n  if (operationGroup) {\n    operationGroup.ops.push(op)\n  } else {\n    op.ownsGroup = operationGroup = {\n      ops: [op],\n      delayedCallbacks: []\n    }\n  }\n}\n\nfunction fireCallbacksForOps(group) {\n  // Calls delayed callbacks and cursorActivity handlers until no\n  // new ones appear\n  let callbacks = group.delayedCallbacks, i = 0\n  do {\n    for (; i < callbacks.length; i++)\n      callbacks[i].call(null)\n    for (let j = 0; j < group.ops.length; j++) {\n      let op = group.ops[j]\n      if (op.cursorActivityHandlers)\n        while (op.cursorActivityCalled < op.cursorActivityHandlers.length)\n          op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm)\n    }\n  } while (i < callbacks.length)\n}\n\nexport function finishOperation(op, endCb) {\n  let group = op.ownsGroup\n  if (!group) return\n\n  try { fireCallbacksForOps(group) }\n  finally {\n    operationGroup = null\n    endCb(group)\n  }\n}\n\nlet orphanDelayedCallbacks = null\n\n// Often, we want to signal events at a point where we are in the\n// middle of some work, but don't want the handler to start calling\n// other methods on the editor, which might be in an inconsistent\n// state or simply not expect any other events to happen.\n// signalLater looks whether there are any handlers, and schedules\n// them to be executed when the last operation ends, or, if no\n// operation is active, when a timeout fires.\nexport function signalLater(emitter, type /*, values...*/) {\n  let arr = getHandlers(emitter, type)\n  if (!arr.length) return\n  let args = Array.prototype.slice.call(arguments, 2), list\n  if (operationGroup) {\n    list = operationGroup.delayedCallbacks\n  } else if (orphanDelayedCallbacks) {\n    list = orphanDelayedCallbacks\n  } else {\n    list = orphanDelayedCallbacks = []\n    setTimeout(fireOrphanDelayed, 0)\n  }\n  for (let i = 0; i < arr.length; ++i)\n    list.push(() => arr[i].apply(null, args))\n}\n\nfunction fireOrphanDelayed() {\n  let delayed = orphanDelayedCallbacks\n  orphanDelayedCallbacks = null\n  for (let i = 0; i < delayed.length; ++i) delayed[i]()\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/3024-day.css",
    "content": "/*\n\n    Name:       3024 day\n    Author:     Jan T. Sott (http://github.com/idleberg)\n\n    CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)\n    Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)\n\n*/\n\n.cm-s-3024-day.CodeMirror { background: #f7f7f7; color: #3a3432; }\n.cm-s-3024-day div.CodeMirror-selected { background: #d6d5d4; }\n\n.cm-s-3024-day .CodeMirror-line::selection, .cm-s-3024-day .CodeMirror-line > span::selection, .cm-s-3024-day .CodeMirror-line > span > span::selection { background: #d6d5d4; }\n.cm-s-3024-day .CodeMirror-line::-moz-selection, .cm-s-3024-day .CodeMirror-line > span::-moz-selection, .cm-s-3024-day .CodeMirror-line > span > span::selection { background: #d9d9d9; }\n\n.cm-s-3024-day .CodeMirror-gutters { background: #f7f7f7; border-right: 0px; }\n.cm-s-3024-day .CodeMirror-guttermarker { color: #db2d20; }\n.cm-s-3024-day .CodeMirror-guttermarker-subtle { color: #807d7c; }\n.cm-s-3024-day .CodeMirror-linenumber { color: #807d7c; }\n\n.cm-s-3024-day .CodeMirror-cursor { border-left: 1px solid #5c5855; }\n\n.cm-s-3024-day span.cm-comment { color: #cdab53; }\n.cm-s-3024-day span.cm-atom { color: #a16a94; }\n.cm-s-3024-day span.cm-number { color: #a16a94; }\n\n.cm-s-3024-day span.cm-property, .cm-s-3024-day span.cm-attribute { color: #01a252; }\n.cm-s-3024-day span.cm-keyword { color: #db2d20; }\n.cm-s-3024-day span.cm-string { color: #fded02; }\n\n.cm-s-3024-day span.cm-variable { color: #01a252; }\n.cm-s-3024-day span.cm-variable-2 { color: #01a0e4; }\n.cm-s-3024-day span.cm-def { color: #e8bbd0; }\n.cm-s-3024-day span.cm-bracket { color: #3a3432; }\n.cm-s-3024-day span.cm-tag { color: #db2d20; }\n.cm-s-3024-day span.cm-link { color: #a16a94; }\n.cm-s-3024-day span.cm-error { background: #db2d20; color: #5c5855; }\n\n.cm-s-3024-day .CodeMirror-activeline-background { background: #e8f2ff; }\n.cm-s-3024-day .CodeMirror-matchingbracket { text-decoration: underline; color: #a16a94 !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/3024-night.css",
    "content": "/*\n\n    Name:       3024 night\n    Author:     Jan T. Sott (http://github.com/idleberg)\n\n    CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)\n    Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)\n\n*/\n\n.cm-s-3024-night.CodeMirror { background: #090300; color: #d6d5d4; }\n.cm-s-3024-night div.CodeMirror-selected { background: #3a3432; }\n.cm-s-3024-night .CodeMirror-line::selection, .cm-s-3024-night .CodeMirror-line > span::selection, .cm-s-3024-night .CodeMirror-line > span > span::selection { background: rgba(58, 52, 50, .99); }\n.cm-s-3024-night .CodeMirror-line::-moz-selection, .cm-s-3024-night .CodeMirror-line > span::-moz-selection, .cm-s-3024-night .CodeMirror-line > span > span::-moz-selection { background: rgba(58, 52, 50, .99); }\n.cm-s-3024-night .CodeMirror-gutters { background: #090300; border-right: 0px; }\n.cm-s-3024-night .CodeMirror-guttermarker { color: #db2d20; }\n.cm-s-3024-night .CodeMirror-guttermarker-subtle { color: #5c5855; }\n.cm-s-3024-night .CodeMirror-linenumber { color: #5c5855; }\n\n.cm-s-3024-night .CodeMirror-cursor { border-left: 1px solid #807d7c; }\n\n.cm-s-3024-night span.cm-comment { color: #cdab53; }\n.cm-s-3024-night span.cm-atom { color: #a16a94; }\n.cm-s-3024-night span.cm-number { color: #a16a94; }\n\n.cm-s-3024-night span.cm-property, .cm-s-3024-night span.cm-attribute { color: #01a252; }\n.cm-s-3024-night span.cm-keyword { color: #db2d20; }\n.cm-s-3024-night span.cm-string { color: #fded02; }\n\n.cm-s-3024-night span.cm-variable { color: #01a252; }\n.cm-s-3024-night span.cm-variable-2 { color: #01a0e4; }\n.cm-s-3024-night span.cm-def { color: #e8bbd0; }\n.cm-s-3024-night span.cm-bracket { color: #d6d5d4; }\n.cm-s-3024-night span.cm-tag { color: #db2d20; }\n.cm-s-3024-night span.cm-link { color: #a16a94; }\n.cm-s-3024-night span.cm-error { background: #db2d20; color: #807d7c; }\n\n.cm-s-3024-night .CodeMirror-activeline-background { background: #2F2F2F; }\n.cm-s-3024-night .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/abbott.css",
    "content": "/*\n * abbott.css\n * A warm, dark theme for prose and code, with pastels and pretty greens.\n *\n * Ported from abbott.vim (https://github.com/bcat/abbott.vim) version 2.1.\n * Original design and CodeMirror port by Jonathan Rascher.\n *\n * This theme shares the following color palette with the Vim color scheme.\n *\n * Brown shades:\n *   bistre: #231c14\n *   chocolate: #3c3022\n *   cocoa: #745d42\n *   vanilla_cream: #fef3b4\n *\n * Red shades:\n *   crimson: #d80450\n *   cinnabar: #f63f05\n *\n * Green shades:\n *   dark_olive: #273900\n *   forest_green: #24a507\n *   chartreuse: #a0ea00\n *   pastel_chartreuse: #d8ff84\n *\n * Yellow shades:\n *   marigold: #fbb32f\n *   lemon_meringue: #fbec5d\n *\n * Blue shades:\n *   cornflower_blue: #3f91f1\n *   periwinkle_blue: #8ccdf0\n *\n * Magenta shades:\n *   french_pink: #ec6c99\n *   lavender: #e6a2f3\n *\n * Cyan shades:\n *   zomp: #39a78d\n *   seafoam_green: #00ff7f\n */\n\n/* Style the UI: */\n\n/* Equivalent to Vim's Normal group. */\n.cm-s-abbott.CodeMirror {\n  background: #231c14 /* bistre */;\n  color: #d8ff84 /* pastel_chartreuse */;\n}\n\n/* Roughly equivalent to Vim's LineNr group. */\n.cm-s-abbott .CodeMirror-gutters {\n  background: #231c14 /* bistre */;\n  border: none;\n}\n.cm-s-abbott .CodeMirror-linenumber { color: #fbec5d /* lemon_meringue */; }\n\n.cm-s-abbott .CodeMirror-guttermarker { color: #f63f05 /* cinnabar */; }\n\n/* Roughly equivalent to Vim's FoldColumn group. */\n.cm-s-abbott .CodeMirror-guttermarker-subtle { color: #fbb32f /* marigold */; }\n\n/*\n * Roughly equivalent to Vim's CursorColumn group. (We use a brighter color\n * since Vim's cursorcolumn option highlights a whole column, whereas\n * CodeMirror's rule just highlights a thin line.)\n */\n.cm-s-abbott .CodeMirror-ruler { border-color: #745d42 /* cocoa */; }\n\n/* Equivalent to Vim's Cursor group in insert mode. */\n.cm-s-abbott .CodeMirror-cursor { border-color: #a0ea00 /* chartreuse */; }\n\n/* Equivalent to Vim's Cursor group in normal mode. */\n.cm-s-abbott.cm-fat-cursor .CodeMirror-cursor,\n.cm-s-abbott .cm-animate-fat-cursor {\n  /*\n   * CodeMirror doesn't allow changing the foreground color of the character\n   * under the cursor, so we can't use a reverse video effect for the cursor.\n   * Instead, make it semitransparent.\n   */\n  background: rgba(160, 234, 0, 0.5) /* chartreuse */;\n}\n.cm-s-abbott.cm-fat-cursor .CodeMirror-cursors {\n  /*\n   * Boost the z-index so the fat cursor shows up on top of text and\n   * matchingbracket/matchingtag highlights.\n   */\n  z-index: 3;\n}\n\n/* Equivalent to Vim's Cursor group in replace mode. */\n.cm-s-abbott .CodeMirror-overwrite .CodeMirror-cursor {\n  border-bottom: 1px solid #a0ea00 /* chartreuse */;\n  border-left: none;\n  width: auto;\n}\n\n/* Roughly equivalent to Vim's CursorIM group. */\n.cm-s-abbott .CodeMirror-secondarycursor {\n  border-color: #00ff7f /* seafoam_green */;\n}\n\n/* Roughly equivalent to Vim's Visual group. */\n.cm-s-abbott .CodeMirror-selected,\n.cm-s-abbott.CodeMirror-focused .CodeMirror-selected {\n  background: #273900 /* dark_olive */;\n}\n.cm-s-abbott .CodeMirror-line::selection,\n.cm-s-abbott .CodeMirror-line > span::selection,\n.cm-s-abbott .CodeMirror-line > span > span::selection {\n  background: #273900 /* dark_olive */;\n}\n.cm-s-abbott .CodeMirror-line::-moz-selection,\n.cm-s-abbott .CodeMirror-line > span::-moz-selection,\n.cm-s-abbott .CodeMirror-line > span > span::-moz-selection {\n  background: #273900 /* dark_olive */;\n}\n\n/* Roughly equivalent to Vim's SpecialKey group. */\n.cm-s-abbott .cm-tab { color: #00ff7f /* seafoam_green */; }\n\n/* Equivalent to Vim's Search group. */\n.cm-s-abbott .cm-searching {\n  background: #fef3b4 /* vanilla_cream */ !important;\n  color: #231c14 /* bistre */ !important;\n}\n\n/* Style syntax highlighting modes: */\n\n/* Equivalent to Vim's Comment group. */\n.cm-s-abbott span.cm-comment {\n  color: #fbb32f /* marigold */;\n  font-style: italic;\n}\n\n/* Equivalent to Vim's String group. */\n.cm-s-abbott span.cm-string,\n.cm-s-abbott span.cm-string-2 {\n  color: #e6a2f3 /* lavender */;\n}\n\n/* Equivalent to Vim's Constant group. */\n.cm-s-abbott span.cm-number,\n.cm-s-abbott span.cm-string.cm-url { color: #f63f05 /* cinnabar */; }\n\n/* Roughly equivalent to Vim's SpecialKey group. */\n.cm-s-abbott span.cm-invalidchar { color: #00ff7f /* seafoam_green */; }\n\n/* Equivalent to Vim's Special group. */\n.cm-s-abbott span.cm-atom { color: #fef3b4 /* vanilla_cream */; }\n\n/* Equivalent to Vim's Delimiter group. */\n.cm-s-abbott span.cm-bracket,\n.cm-s-abbott span.cm-punctuation {\n  color: #fef3b4 /* vanilla_cream */;\n}\n\n/* Equivalent Vim's Operator group. */\n.cm-s-abbott span.cm-operator { font-weight: bold; }\n\n/* Roughly equivalent to Vim's Identifier group. */\n.cm-s-abbott span.cm-def,\n.cm-s-abbott span.cm-variable,\n.cm-s-abbott span.cm-variable-2,\n.cm-s-abbott span.cm-variable-3 {\n  color: #8ccdf0 /* periwinkle_blue */;\n}\n\n/* Roughly equivalent to Vim's Function group. */\n.cm-s-abbott span.cm-builtin,\n.cm-s-abbott span.cm-property,\n.cm-s-abbott span.cm-qualifier {\n  color: #3f91f1 /* cornflower_blue */;\n}\n\n/* Equivalent to Vim's Type group. */\n.cm-s-abbott span.cm-type { color: #24a507 /* forest_green */; }\n\n/* Equivalent to Vim's Keyword group. */\n.cm-s-abbott span.cm-keyword {\n  color: #d80450 /* crimson */;\n  font-weight: bold;\n}\n\n/* Equivalent to Vim's PreProc group. */\n.cm-s-abbott span.cm-meta { color: #ec6c99 /* french_pink */; }\n\n/* Equivalent to Vim's htmlTagName group (linked to Statement). */\n.cm-s-abbott span.cm-tag {\n  color: #d80450 /* crimson */;\n  font-weight: bold;\n}\n\n/* Equivalent to Vim's htmlArg group (linked to Type). */\n.cm-s-abbott span.cm-attribute { color: #24a507 /* forest_green */; }\n\n/* Equivalent to Vim's htmlH1, markdownH1, etc. groups (linked to Title). */\n.cm-s-abbott span.cm-header {\n  color: #d80450 /* crimson */;\n  font-weight: bold;\n}\n\n/* Equivalent to Vim's markdownRule group (linked to PreProc). */\n.cm-s-abbott span.cm-hr { color: #ec6c99 /* french_pink */; }\n\n/* Roughly equivalent to Vim's Underlined group. */\n.cm-s-abbott span.cm-link { color: #e6a2f3 /* lavender */; }\n\n/* Equivalent to Vim's diffRemoved group. */\n.cm-s-abbott span.cm-negative {\n  background: #d80450 /* crimson */;\n  color: #231c14 /* bistre */;\n}\n\n/* Equivalent to Vim's diffAdded group. */\n.cm-s-abbott span.cm-positive {\n  background: #a0ea00 /* chartreuse */;\n  color: #231c14 /* bistre */;\n  font-weight: bold;\n}\n\n/* Equivalent to Vim's Error group. */\n.cm-s-abbott span.cm-error {\n  background: #d80450 /* crimson */;\n  color: #231c14 /* bistre */;\n}\n\n/* Style addons: */\n\n/* Equivalent to Vim's MatchParen group. */\n.cm-s-abbott span.CodeMirror-matchingbracket {\n  background: #745d42 /* cocoa */ !important;\n  color: #231c14 /* bistre */ !important;\n  font-weight: bold;\n}\n\n/*\n * Roughly equivalent to Vim's Error group. (Vim doesn't seem to have a direct\n * equivalent in its own matchparen plugin, but many syntax highlighting plugins\n * mark mismatched brackets as Error.)\n */\n.cm-s-abbott span.CodeMirror-nonmatchingbracket {\n  background: #d80450 /* crimson */ !important;\n  color: #231c14 /* bistre */ !important;\n}\n\n.cm-s-abbott .CodeMirror-matchingtag,\n.cm-s-abbott .cm-matchhighlight {\n  outline: 1px solid #39a78d /* zomp */;\n}\n\n/* Equivalent to Vim's CursorLine group. */\n.cm-s-abbott .CodeMirror-activeline-background,\n.cm-s-abbott .CodeMirror-activeline-gutter {\n  background: #3c3022 /* chocolate */;\n}\n\n/* Equivalent to Vim's CursorLineNr group. */\n.cm-s-abbott .CodeMirror-activeline-gutter .CodeMirror-linenumber {\n  color: #d8ff84 /* pastel_chartreuse */;\n  font-weight: bold;\n}\n\n/* Roughly equivalent to Vim's Folded group. */\n.cm-s-abbott .CodeMirror-foldmarker {\n  color: #f63f05 /* cinnabar */;\n  text-shadow: none;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/abcdef.css",
    "content": ".cm-s-abcdef.CodeMirror { background: #0f0f0f; color: #defdef; }\n.cm-s-abcdef div.CodeMirror-selected { background: #515151; }\n.cm-s-abcdef .CodeMirror-line::selection, .cm-s-abcdef .CodeMirror-line > span::selection, .cm-s-abcdef .CodeMirror-line > span > span::selection { background: rgba(56, 56, 56, 0.99); }\n.cm-s-abcdef .CodeMirror-line::-moz-selection, .cm-s-abcdef .CodeMirror-line > span::-moz-selection, .cm-s-abcdef .CodeMirror-line > span > span::-moz-selection { background: rgba(56, 56, 56, 0.99); }\n.cm-s-abcdef .CodeMirror-gutters { background: #555; border-right: 2px solid #314151; }\n.cm-s-abcdef .CodeMirror-guttermarker { color: #222; }\n.cm-s-abcdef .CodeMirror-guttermarker-subtle { color: azure; }\n.cm-s-abcdef .CodeMirror-linenumber { color: #FFFFFF; }\n.cm-s-abcdef .CodeMirror-cursor { border-left: 1px solid #00FF00; }\n\n.cm-s-abcdef span.cm-keyword { color: darkgoldenrod; font-weight: bold; }\n.cm-s-abcdef span.cm-atom { color: #77F; }\n.cm-s-abcdef span.cm-number { color: violet; }\n.cm-s-abcdef span.cm-def { color: #fffabc; }\n.cm-s-abcdef span.cm-variable { color: #abcdef; }\n.cm-s-abcdef span.cm-variable-2 { color: #cacbcc; }\n.cm-s-abcdef span.cm-variable-3, .cm-s-abcdef span.cm-type { color: #def; }\n.cm-s-abcdef span.cm-property { color: #fedcba; }\n.cm-s-abcdef span.cm-operator { color: #ff0; }\n.cm-s-abcdef span.cm-comment { color: #7a7b7c; font-style: italic;}\n.cm-s-abcdef span.cm-string { color: #2b4; }\n.cm-s-abcdef span.cm-meta { color: #C9F; }\n.cm-s-abcdef span.cm-qualifier { color: #FFF700; }\n.cm-s-abcdef span.cm-builtin { color: #30aabc; }\n.cm-s-abcdef span.cm-bracket { color: #8a8a8a; }\n.cm-s-abcdef span.cm-tag { color: #FFDD44; }\n.cm-s-abcdef span.cm-attribute { color: #DDFF00; }\n.cm-s-abcdef span.cm-error { color: #FF0000; }\n.cm-s-abcdef span.cm-header { color: aquamarine; font-weight: bold; }\n.cm-s-abcdef span.cm-link { color: blueviolet; }\n\n.cm-s-abcdef .CodeMirror-activeline-background { background: #314151; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/ambiance-mobile.css",
    "content": ".cm-s-ambiance.CodeMirror {\n  -webkit-box-shadow: none;\n  -moz-box-shadow: none;\n  box-shadow: none;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/ambiance.css",
    "content": "/* ambiance theme for codemirror */\n\n/* Color scheme */\n\n.cm-s-ambiance .cm-header { color: blue; }\n.cm-s-ambiance .cm-quote { color: #24C2C7; }\n\n.cm-s-ambiance .cm-keyword { color: #cda869; }\n.cm-s-ambiance .cm-atom { color: #CF7EA9; }\n.cm-s-ambiance .cm-number { color: #78CF8A; }\n.cm-s-ambiance .cm-def { color: #aac6e3; }\n.cm-s-ambiance .cm-variable { color: #ffb795; }\n.cm-s-ambiance .cm-variable-2 { color: #eed1b3; }\n.cm-s-ambiance .cm-variable-3, .cm-s-ambiance .cm-type { color: #faded3; }\n.cm-s-ambiance .cm-property { color: #eed1b3; }\n.cm-s-ambiance .cm-operator { color: #fa8d6a; }\n.cm-s-ambiance .cm-comment { color: #555; font-style:italic; }\n.cm-s-ambiance .cm-string { color: #8f9d6a; }\n.cm-s-ambiance .cm-string-2 { color: #9d937c; }\n.cm-s-ambiance .cm-meta { color: #D2A8A1; }\n.cm-s-ambiance .cm-qualifier { color: yellow; }\n.cm-s-ambiance .cm-builtin { color: #9999cc; }\n.cm-s-ambiance .cm-bracket { color: #24C2C7; }\n.cm-s-ambiance .cm-tag { color: #fee4ff; }\n.cm-s-ambiance .cm-attribute { color: #9B859D; }\n.cm-s-ambiance .cm-hr { color: pink; }\n.cm-s-ambiance .cm-link { color: #F4C20B; }\n.cm-s-ambiance .cm-special { color: #FF9D00; }\n.cm-s-ambiance .cm-error { color: #AF2018; }\n\n.cm-s-ambiance .CodeMirror-matchingbracket { color: #0f0; }\n.cm-s-ambiance .CodeMirror-nonmatchingbracket { color: #f22; }\n\n.cm-s-ambiance div.CodeMirror-selected { background: rgba(255, 255, 255, 0.15); }\n.cm-s-ambiance.CodeMirror-focused div.CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-ambiance .CodeMirror-line::selection, .cm-s-ambiance .CodeMirror-line > span::selection, .cm-s-ambiance .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-ambiance .CodeMirror-line::-moz-selection, .cm-s-ambiance .CodeMirror-line > span::-moz-selection, .cm-s-ambiance .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n\n/* Editor styling */\n\n.cm-s-ambiance.CodeMirror {\n  line-height: 1.40em;\n  color: #E6E1DC;\n  background-color: #202020;\n  -webkit-box-shadow: inset 0 0 10px black;\n  -moz-box-shadow: inset 0 0 10px black;\n  box-shadow: inset 0 0 10px black;\n}\n\n.cm-s-ambiance .CodeMirror-gutters {\n  background: #3D3D3D;\n  border-right: 1px solid #4D4D4D;\n  box-shadow: 0 10px 20px black;\n}\n\n.cm-s-ambiance .CodeMirror-linenumber {\n  text-shadow: 0px 1px 1px #4d4d4d;\n  color: #111;\n  padding: 0 5px;\n}\n\n.cm-s-ambiance .CodeMirror-guttermarker { color: #aaa; }\n.cm-s-ambiance .CodeMirror-guttermarker-subtle { color: #111; }\n\n.cm-s-ambiance .CodeMirror-cursor { border-left: 1px solid #7991E8; }\n\n.cm-s-ambiance .CodeMirror-activeline-background {\n  background: none repeat scroll 0% 0% rgba(255, 255, 255, 0.031);\n}\n\n.cm-s-ambiance.CodeMirror,\n.cm-s-ambiance .CodeMirror-gutters {\n  background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAQAAAAHUWYVAABFFUlEQVQYGbzBCeDVU/74/6fj9HIcx/FRHx9JCFmzMyGRURhLZIkUsoeRfUjS2FNDtr6WkMhO9sm+S8maJfu+Jcsg+/o/c+Z4z/t97/vezy3z+z8ekGlnYICG/o7gdk+wmSHZ1z4pJItqapjoKXWahm8NmV6eOTbWUOp6/6a/XIg6GQqmenJ2lDHyvCFZ2cBDbmtHA043VFhHwXxClWmeYAdLhV00Bd85go8VmaFCkbVkzlQENzfBDZ5gtN7HwF0KDrTwJ0dypSOzpaKCMwQHKTIreYIxlmhXTzTWkVm+LTynZhiSBT3RZQ7aGfjGEd3qyXQ1FDymqbKxpspERQN2MiRjNZlFFQXfCNFm9nM1zpAsoYjmtRTc5ajwuaXc5xrWskT97RaKzAGe5ARHhVUsDbjKklziiX5WROcJwSNCNI+9w1Jwv4Zb2r7lCMZ4oq5C0EdTx+2GzNuKpJ+iFf38JEWkHJn9DNF7mmBDITrWEg0VWL3pHU20tSZnuqWu+R3BtYa8XxV1HO7GyD32UkOpL/yDloINFTmvtId+nmAjxRw40VMwVKiwrKLE4bK5UOVntYwhOcSSXKrJHKPJedocpGjVz/ZMIbnYUPB10/eKCrs5apqpgVmWzBYWpmtKHecJPjaUuEgRDDaU0oZghCJ6zNMQ5ZhDYx05r5v2muQdM0EILtXUsaKiQX9WMEUotagQzFbUNN6NUPC2nm5pxEWGCjMc3GdJHjSU2kORLK/JGSrkfGEIjncU/CYUnOipoYemwj8tST9NsJmB7TUVXtbUtXATJVZXBMvYeTXJfobgJUPmGMP/yFaWonaa6BcFO3nqcIqCozSZoZoSr1g4zJOzuyGnxTEX3lUEJ7WcZgme8ddaWvWJo2AJR9DZU3CUIbhCSG6ybSwN6qtJVnCU2svDTP2ZInOw2cBTrqtQahtNZn9NcJ4l2NaSmSkkP1noZWnVwkLmdUPOwLZEwy2Z3S3R+4rIG9hcbpPXHFVWcQdZkn2FOta3cKWQnNRC5g1LsJah4GCzSVsKnCOY5OAFRTBekyyryeyilhFKva75r4Mc0aWanGEaThcy31s439KKxTzJYY5WTHPU1FtIHjQU3Oip4xlNzj/lBw23dYZVliQa7WAXf4shetcQfatI+jWRDBPmyNeW6A1P5kdDgyYJlba0BIM8BZu1JfrFwItyjcAMR3K0BWOIrtMEXyhyrlVEx3ui5dUBjmB/Q3CXW85R4mBD0s7B+4q5tKUjOlb9qqmhi5AZ6GFIC5HXtOobdYGlVdMVbNJ8toNTFcHxnoL+muBagcctjWnbNMuR00uI7nQESwg5q2qqrKWIfrNUmeQocY6HuyxJV02wj36w00yhpmUFenv4p6fUkZYqLyuinx2RGOjhCXYyJF84oiU00YMOOhhquNdfbOB7gU88pY4xJO8LVdp6/q2voeB4R04vIdhSE40xZObx1HGGJ/ja0LBthFInKaLPPFzuCaYaoj8JjPME8yoyxo6zlBqkiUZYgq00OYMswbWO5NGmq+xhipxHLRW29ARjNKXO0wRnear8XSg4XFPLKEPUS1GqvyLwiuBUoa7zpZ0l5xxFwWmWZC1H5h5FwU8eQ7K+g8UcVY6TMQreVQT/8uQ8Z+ALIXnSEa2pYZQneE9RZbSBNYXfWYJzW/h/4j4Dp1tYVcFIC5019Vyi4ThPqSFCzjGWaHQTBU8q6vrVwgxP9Lkm840imWKpcLCjYTtrKuwvsKSnrvHCXGkSMk9p6lhckfRpIeis+N2PiszT+mFLspyGleUhDwcLrZqmyeylxwjBcKHEapqkmyangyLZRVOijwOtCY5SsG5zL0OwlCJ4y5KznF3EUNDDrinwiyLZRzOXtlBbK5ITHFGLp8Q0R6ab6mS7enI2cFrxOyHvOCFaT1HThS1krjCwqWeurCkk+willhCC+RSZnRXBiZaC5RXRIZYKp2lyfrHwiKPKR0JDzrdU2EFgpidawlFDR6FgXUMNa+g1FY3bUQh2cLCwosRdnuQTS/S+JVrGLeWIvtQUvONJxlqSQYYKpwoN2kaocLjdVsis4Mk80ESF2YpSkzwldjHkjFCUutI/r+EHDU8oCs6yzL3PhWiEooZdFMkymlas4AcI3KmoMMNSQ3tHzjGWCrcJJdYyZC7QFGwjRL9p+MrRkAGWzIaWCn9W0F3TsK01c2ZvQw0byvxuQU0r1lM0qJO7wW0kRIMdDTtXEdzi4VIh+EoIHm0mWtAtpCixlabgn83fKTI7anJe9ST7WIK1DMGpQmYeA58ImV6ezOGOzK2Kgq01pd60cKWiUi9Lievb/0vIDPHQ05Kzt4ddPckQBQtoaurjyHnek/nKzpQLrVgKPjIkh2v4uyezpv+Xoo7fPFXaGFp1vaLKxQ4uUpQQS5VuQs7BCq4xRJv7fwpVvvFEB3j+620haOuocqMhWd6TTPAEx+mdFNGHdranFe95WrWmIvlY4F1Dle2ECgc6cto7SryuqGGGha0tFQ5V53migUKmg6XKAo4qS3mik+0OZpAhOLeZKicacgaYcyx5hypYQE02ZA4xi/pNhOQxR4klNKyqacj+mpxnLTnnGSo85++3ZCZq6lrZkXlGEX3o+C9FieccJbZWVFjC0Yo1FZnJhoYMFoI1hEZ9r6hwg75HwzBNhbZCdJEfJwTPGzJvaKImw1yYX1HDAmpXR+ZJQ/SmgqMNVQb5vgamGwLtt7VwvP7Qk1xpiM5x5Cyv93E06MZmgs0Nya2azIKOYKCGBQQW97RmhKNKF02JZqHEJ4o58qp7X5EcZmc56trXEqzjCBZ1MFGR87Ql2tSTs6CGxS05PTzRQorkbw7aKoKXFDXsYW42VJih/q+FP2BdTzDTwVqOYB13liM50vG7wy28qagyuIXMeQI/Oqq8bcn5wJI50xH00CRntyfpL1T4hydYpoXgNiFzoIUTDZnLNRzh4TBHwbYGDvZkxmlyJloyr6tRihpeUG94GnKtIznREF0tzJG/OOr73JBcrSh1k6WuTprgLU+mnSGnv6Zge0NNz+kTDdH8nuAuTdJDCNb21LCiIuqlYbqGzT3RAoZofQfjFazkqeNWdYaGvYTM001EW2oKPvVk1ldUGSgUtHFwjKM1h9jnFcmy5lChoLNaQMGGDsYbKixlaMBmmsx1QjCfflwTfO/gckW0ruZ3jugKR3R5W9hGUWqCgxuFgsuaCHorotGKzGaeZB9DMsaTnKCpMtwTvOzhYk0rdrArKCqcaWmVk1+F372ur1YkKxgatI8Qfe1gIX9wE9FgS8ESmuABIXnRUbCapcKe+nO7slClSZFzpV/LkLncEb1qiO42fS3R855Su2mCLh62t1SYZZYVmKwIHjREF2uihTzB20JOkz7dkxzYQnK0UOU494wh+VWRc6Un2kpTaVgLDFEkJ/uhzRcI0YKGgpGWOlocBU/a4fKoJ/pEaNV6jip3+Es9VXY078rGnmAdf7t9ylPXS34RBSuYPs1UecZTU78WanhBCHpZ5sAoTz0LGZKjPf9TRypqWEiTvOFglL1fCEY3wY/++rbk7C8bWebA6p6om6PgOL2kp44TFJlVNBXae2rqqdZztOJpT87GQsE9jqCPIe9VReZuQ/CIgacsyZdCpIScSYqcZk8r+nsyCzhyfhOqHGOIvrLknC8wTpFcaYiGC/RU1NRbUeUpocQOnkRpGOrIOcNRx+1uA0UrzhSSt+VyS3SJpnFWkzNDqOFGIWcfR86DnmARTQ1HKIL33ExPiemeOhYSSjzlSUZZuE4TveoJLnBUOFof6KiysCbnAEcZgcUNTDOwkqWu3RWtmGpZwlHhJENdZ3miGz0lJlsKnjbwqSHQjpxnFDlTLLwqJPMZMjd7KrzkSG7VsxXBZE+F8YZkb01Oe00yyRK9psh5SYh29ySPKBo2ylNht7ZkZnsKenjKNJu9PNEyZpaCHv4Kt6RQsLvAVp7M9kIimmCUwGeWqLMmGuIotYMmWNpSahkhZw9FqZsVnKJhsjAHvtHMsTM9fCI06Dx/u3vfUXCqfsKRc4oFY2jMsoo/7DJDwZ1CsIKnJu+J9ldkpmiCxQx1rWjI+T9FwcWWzOuaYH0Hj7klNRVWEQpmaqosakiGNTFHdjS/qnUdmf0NJW5xsL0HhimCCZZSRzmSPTXJQ4aaztAwtZnoabebJ+htCaZ7Cm535ByoqXKbX1WRc4Eh2MkRXWzImVc96Cj4VdOKVxR84VdQsIUM8Psoou2byVHyZFuq7O8otbSQ2UAoeEWTudATLGSpZzVLlXVkPU2Jc+27lsw2jmg5T5VhbeE3BT083K9WsTTkFU/Osi0rC5lRlpwRHUiesNS0sOvmqGML1aRbPAxTJD9ZKtxuob+hhl8cwYGWpJ8nub7t5p6coYbMovZ1BTdaKn1jYD6h4GFDNFyT/Kqe1XCXphXHOKLZmuRSRdBPEfVUXQzJm5YGPGGJdvAEr7hHNdGZnuBvrpciGmopOLf5N0uVMy0FfYToJk90uUCbJupaVpO53UJXR2bVpoU00V2KOo4zMFrBd0Jtz2pa0clT5Q5L8IpQ177mWQejPMEJhuQjS10ref6HHjdEhy1P1EYR7GtO0uSsKJQYLiTnG1rVScj5lyazpqWGl5uBbRWl7m6ixGOOnEsMJR7z8J0n6KMnCdxhiNYQCoZ6CmYLnO8omC3MkW3bktlPmEt/VQQHejL3+dOE5FlPdK/Mq8hZxxJtLyRrepLThYKbLZxkSb5W52vYxNOaOxUF0yxMUPwBTYqCzy01XayYK0sJyWBLqX0MwU5CzoymRzV0EjjeUeLgDpTo6ij42ZAzvD01dHUUTPLU96MdLbBME8nFBn7zJCMtJcZokn8YoqU0FS5WFKyniHobguMcmW8N0XkWZjkyN3hqOMtS08r+/xTBwpZSZ3qiVRX8SzMHHjfUNFjgHEPmY9PL3ykEzxkSre/1ZD6z/NuznuB0RcE1TWTm9zRgfUWVJiG6yrzgmWPXC8EAR4Wxhlad0ZbgQyEz3pG5RVEwwDJH2mgKpjcTiCOzn1lfUWANFbZ2BA8balnEweJC9J0iuaeZoI+ippFCztEKVvckR2iice1JvhVytrQwUAZpgsubCPaU7xUe9vWnaOpaSBEspalykhC9bUlOMpT42ZHca6hyrqKmw/wMR8H5ZmdFoBVJb03O4UL0tSNnvIeRmkrLWqrs78gcrEn2tpcboh0UPOW3UUR9PMk4T4nnNKWmCjlrefhCwxRNztfmIQVdDElvS4m1/WuOujoZCs5XVOjtKPGokJzsYCtFYoWonSPT21DheU/wWhM19FcElwqNGOsp9Q8N/cwXaiND1MmeL1Q5XROtYYgGeFq1aTMsoMmcrKjQrOFQTQ1fmBYhmW6o8Jkjc7iDJRTBIo5kgJD5yMEYA3srCg7VFKwiVJkmRCc5ohGOKhsYMn/XBLdo5taZjlb9YAlGWRimqbCsoY7HFAXLa5I1HPRxMMsQDHFkWtRNniqT9UEeNjcE7RUlrCJ4R2CSJuqlKHWvJXjAUNcITYkenuBRB84TbeepcqTj3zZyFJzgYQdHnqfgI0ddUwS6GqWpsKWhjq9cV0vBAEMN2znq+EBfIWT+pClYw5xsTlJU6GeIBsjGmmANTzJZiIYpgrM0Oa8ZMjd7NP87jxhqGOhJlnQtjuQpB+8aEE00wZFznSJPyHxgH3HkPOsJFvYk8zqCHzTs1BYOa4J3PFU+UVRZxlHDM4YavlNUuMoRveiZA2d7grMNc2g+RbSCEKzmgYsUmWmazFJyoiOZ4KnyhKOGRzWJa0+moyV4TVHDzn51Awtqaphfk/lRQ08FX1iiqxTB/kLwd0VynKfEvI6cd4XMV5bMhZ7gZUWVzYQ6Nm2BYzxJbw3bGthEUUMfgbGeorae6DxHtJoZ6alhZ0+ytiVoK1R4z5PTrOECT/SugseEOlb1MMNR4VRNcJy+V1Hg9ONClSZFZjdHlc6W6FBLdJja2MC5hhpu0DBYEY1TFGwiFAxRRCsYkiM9JRb0JNMVkW6CZYT/2EiTGWmo8k+h4FhDNE7BvppoTSFnmCV5xZKzvcCdDo7VVPnIU+I+Rc68juApC90MwcFCsJ5hDqxgScYKreruyQwTqrzoqDCmhWi4IbhB0Yrt3RGa6GfDv52rKXWhh28dyZaWUvcZeMTBaZoSGyiCtRU5J8iviioHaErs7Jkj61syVzTTgOcUOQ8buFBTYWdL5g3T4qlpe0+wvD63heAXRfCCIed9RbCsp2CiI7raUOYOTU13N8PNHvpaGvayo4a3LLT1lDrVEPT2zLUlheB1R+ZTRfKWJ+dcocLJfi11vyJ51lLqJ0WD7tRwryezjiV5W28uJO9qykzX8JDe2lHl/9oyBwa2UMfOngpXCixvKdXTk3wrsKmiVYdZIqsoWEERjbcUNDuiaQomGoIbFdEHmsyWnuR+IeriKDVLnlawlyNHKwKlSU631PKep8J4Q+ayjkSLKYLhalNHlYvttb6fHm0p6OApsZ4l2VfdqZkjuysy6ysKLlckf1KUutCTs39bmCgEyyoasIWlVaMF7mgmWtBT8Kol5xpH9IGllo8cJdopcvZ2sImlDmMIbtDk3KIpeNiS08lQw11NFPTwVFlPP6pJ2gvRfI7gQUfmNAtf6Gs0wQxDsKGlVBdF8rCa3jzdwMaGHOsItrZk7hAyOzpK9VS06j5F49b0VNGOOfKs3lDToMsMBe9ZWtHFEgxTJLs7qrygKZjUnmCYoeAqeU6jqWuLJup4WghOdvCYJnrSkSzoyRkm5M2StQwVltPkfCAk58tET/CSg+8MUecmotMEnhBKfWBIZsg2ihruMJQaoIm+tkTLKEqspMh00w95gvFCQRtDwTT1gVDDSEVdlwqZfxoQRbK0g+tbiBZxzKlpnpypejdDwTaeOvorMk/IJE10h9CqRe28hhLbe0pMsdSwv4ZbhKivo2BjDWfL8UKJgeavwlwb5KlwhyE4u4XkGE2ytZCznKLCDZZq42VzT8HLCrpruFbIfOIINmh/qCdZ1ZBc65kLHR1Bkyf5zn6pN3SvGKIlFNGplhrO9QSXanLOMQTLCa0YJCRrCZm/CZmrLTm7WzCK4GJDiWUdFeYx1LCFg3NMd0XmCuF3Y5rITLDUsYS9zoHVzwnJoYpSTQoObyEzr4cFBNqYTopoaU/wkyLZ2lPhX/5Y95ulxGTV7KjhWrOZgl8MyUUafjYraNjNU1N3IWcjT5WzWqjwtoarHSUObGYO3GCJZpsBlnJGPd6ZYLyl1GdCA2625IwwJDP8GUKymbzuyPlZlvTUsaUh5zFDhRWFzPKKZLAlWdcQbObgF9tOqOsmB1dqcqYJmWstFbZRRI9poolmqiLnU0POvxScpah2iSL5UJNzgScY5+AuIbpO0YD3NCW+dLMszFSdFCWGqG6eVq2uYVNDdICGD6W7EPRWZEY5gpsE9rUkS3mijzzJnm6UpUFXG1hCUeVoS5WfNcFpblELL2qqrCvMvRfd45oalvKU2tiQ6ePJOVMRXase9iTtLJztPxJKLWpo2CRDcJwn2sWSLKIO1WQWNTCvpVUvOZhgSC40JD0dOctaSqzkCRbXsKlb11Oip6PCJ0IwSJM31j3akRxlP7Rwn6aGaUL0qiLnJkvB3xWZ2+Q1TfCwpQH3G0o92UzmX4o/oJNQMMSQc547wVHhdk+VCw01DFYEnTxzZKAm74QmeNNR1w6WzEhNK15VJzuCdxQ53dRUDws5KvwgBMOEgpcVNe0hZI6RXT1Jd0cyj5nsaEAHgVmGaJIlWdsc5Ui2ElrRR6jrRAttNMEAIWrTDFubkZaok7/AkzfIwfuWVq0jHzuCK4QabtLUMVPB3kJ0oyHTSVFlqMALilJf2Rf8k5aaHtMfayocLBS8L89oKoxpJvnAkDPa0qp5DAUTHKWmCcnthlou8iCKaFFLHWcINd1nyIwXqrSxMNmSs6KmoL2QrKuWtlQ5V0120xQ5vRyZS1rgFkWwhiOwiuQbR0OOVhQM9iS3tiXp4RawRPMp5tDletOOBL95MpM01dZTBM9pkn5qF010rIeHFcFZhmSGpYpTsI6nwhqe5C9ynhlpp5ophuRb6WcJFldkVnVEwwxVfrVkvnWUuNLCg5bgboFHPDlDPDmnK7hUrWiIbjadDclujlZcaokOFup4Ri1kacV6jmrrK1hN9bGwpKEBQ4Q6DvIUXOmo6U5LqQM6EPyiKNjVkPnJkDPNEaxhiFay5ExW1NXVUGqcpYYdPcGiCq7z/TSlbhL4pplWXKd7NZO5QQFrefhRQW/NHOsqcIglc4UhWklR8K0QzbAw08CBDnpbgqXdeD/QUsM4RZXDFBW6WJKe/mFPdH0LtBgiq57wFLzlyQzz82qYx5D5WJP5yVJDW01BfyHnS6HKO/reZqId1WGa4Hkh2kWodJ8i6KoIPlAj2hPt76CzXsVR6koPRzWTfKqIentatYpQw2me4AA3y1Kind3SwoOKZDcFXTwl9tWU6mfgRk9d71sKtlNwrjnYw5tC5n5LdKiGry3JKNlHEd3oaMCFHrazBPMp/uNJ+V7IudcSbeOIdjUEdwl0VHCOZo5t6YluEuaC9mQeMgSfOyKnYGFHcIeQ84yQWbuJYJpZw5CzglDH7gKnWqqM9ZTaXcN0TeYhR84eQtJT76JJ1lREe7WnnvsMmRc9FQ7SBBM9mV3lCUdmHk/S2RAMt0QjFNFqQpWjDPQ01DXWUdDBkXziKPjGEP3VP+zIWU2t7im41FOloyWzn/L6dkUy3VLDaZ6appgDLHPjJEsyvJngWEPUyVBiAaHCTEXwrLvSEbV1e1gKJniicWorC1MUrVjB3uDhJE/wgSOzk1DXpk0k73qCM8xw2UvD5kJmDUfOomqMpWCkJRlvKXGmoeBm18USjVIk04SClxTB6YrgLAPLWYK9HLUt5cmc0vYES8GnTeRc6skZbQkWdxRsIcyBRzx1DbTk9FbU0caTPOgJHhJKnOGIVhQqvKmo0llRw9sabrZkDtdg3PqaKi9oatjY8B+G371paMg6+mZFNNtQ04mWBq3rYLOmtWWQp8KJnpy9DdFensyjdqZ+yY40VJlH8wcdLzC8PZnvHMFUTZUrDTkLyQaGus5X5LzpYAf3i+e/ZlhqGqWhh6Ou6xTR9Z6oi5AZZtp7Mj2EEm8oSpxiYZCHU/1fbGdNNNRRoZMhmilEb2gqHOEJDtXkHK/JnG6IrvbPCwV3NhONVdS1thBMs1T4QOBcTWa2IzhMk2nW5Kyn9tXUtpv9RsG2msxk+ZsQzRQacJncpgke0+T8y5Fzj8BiGo7XlJjaTIlpQs7KFjpqGnKuoyEPeIKnFMkZHvopgh81ySxNFWvJWcKRs70j2FOT012IllEEO1n4pD1513Yg2ssQPOThOkvyrqHUdEXOSEsihmBbTbKX1kLBPWqWkLOqJbjB3GBIZmoa8qWl4CG/iZ7oiA72ZL7TJNeZUY7kFQftDcHHluBzRbCegzMtrRjVQpX2lgoPKKLJAkcbMl01XK2p7yhL8pCBbQ3BN2avJgKvttcrWDK3CiUOVxQ8ZP+pqXKyIxnmBymCg5vJjNfkPK4+c8cIfK8ocVt7kmfd/I5SR1hKvCzUtb+lhgc00ZaO6CyhIQP1Uv4yIZjload72PXX0OIJvnFU+0Zf6MhsJwTfW0r0UwQfW4LNLZl5HK261JCZ4qnBaAreVAS3WrjV0LBnNDUNNDToCEeFfwgcb4gOEqLRhirWkexrCEYKVV711DLYEE1XBEsp5tpTGjorkomKYF9FDXv7fR3BGwbettSxnyL53MBPjsxDZjMh+VUW9NRxq1DhVk+FSxQcaGjV9Pawv6eGByw5qzoy7xk4RsOShqjJwWKe/1pEEfzkobeD/dQJmpqedcyBTy2sr4nGNRH0c0SPWTLrqAc0OQcb/gemKgqucQT7ySWKCn2EUotoCvpZct7RO2sy/QW0IWcXd7pQRQyZVwT2USRO87uhjioTLKV2brpMUcMQRbKH/N2T+UlTpaMls6cmc6CCNy3JdYYSUzzJQ4oSD3oKLncULOiJvjBEC2oqnCJkJluCYy2ZQ5so9YYlZ1VLlQU1mXEW1jZERwj/MUSRc24TdexlqLKfQBtDTScJUV8FszXBEY5ktpD5Ur9hYB4Nb1iikw3JoYpkKX+RodRKFt53MMuRnKSpY31PwYaGaILh3wxJGz9TkTPEETxoCWZrgvOlmyMzxFEwVJE5xZKzvyJ4WxEc16Gd4Xe3Weq4XH2jKRikqOkGQ87hQnC7wBmGYLAnesX3M+S87eFATauuN+Qcrh7xIxXJbUIdMw3JGE3ylCWzrieaqCn4zhGM19TQ3z1oH1AX+pWEqIc7wNGAkULBo/ZxRaV9NNyh4Br3rCHZzbzmSfawBL0dNRwpW1kK9mxPXR9povcdrGSZK9c2k0xwFGzjuniCtRSZCZ6ccZ7gaktmgAOtKbG/JnOkJrjcQTdFMsxRQ2cLY3WTIrlCw1eWKn8R6pvt4GFDso3QoL4a3nLk3G6JrtME3dSenpx7PNFTmga0EaJTLQ061sEeQoWXhSo9LTXsaSjoJQRXeZLtDclbCrYzfzHHeaKjHCVOUkQHO3JeEepr56mhiyaYYKjjNU+Fed1wS5VlhWSqI/hYUdDOkaxiKehoyOnrCV5yBHtbWFqTHCCwtpDcYolesVR5yUzTZBb3RNMd0d6WP+SvhuBmRcGxnuQzT95IC285cr41cLGQ6aJJhmi4TMGempxeimBRQw1tFKV+8jd6KuzoSTqqDxzRtpZkurvKEHxlqXKRIjjfUNNXQsNOsRScoWFLT+YeRZVD3GRN0MdQcKqQjHDMrdGGVu3iYJpQx3WGUvfbmxwFfR20WBq0oYY7LMFhhgYtr8jpaEnaOzjawWWaTP8mMr0t/EPDPoqcnxTBI5o58L7uoWnMrpoqPwgVrlAUWE+V+TQl9rawoyP6QGAlQw2TPRX+YSkxyBC8Z6jhHkXBgQL7WII3DVFnRfCrBfxewv9D6xsyjys4VkhWb9pUU627JllV0YDNHMku/ldNMMXDEo4aFnAkk4U6frNEU4XgZUPmEKHUl44KrzmYamjAbh0JFvGnaTLPu1s9jPCwjFpYiN7z1DTOk/nc07CfDFzmCf7i+bfNHXhDtLeBXzTBT5rkMvWOIxpl4EMh2LGJBu2syDnAEx2naEhHDWMMzPZEhygyS1mS5RTJr5ZkoKbEUoYqr2kqdDUE8ztK7OaIntJkFrIECwv8LJTaVx5XJE86go8dFeZ3FN3rjabCAYpoYEeC9zzJVULBbmZhDyd7ko09ydpNZ3nm2Kee4FPPXHnYEF1nqOFEC08LUVcDvYXkJHW8gTaKCk9YGOeIJhqiE4ToPEepdp7IWFjdwnWaufGMwJJCMtUTTBBK9BGCOy2tGGrJTHIwyEOzp6aPzNMOtlZkDvcEWpP5SVNhfkvDxhmSazTJXYrM9U1E0xwFVwqZQwzJxw6+kGGGUj2FglGGmnb1/G51udRSMNlTw6GGnCcUwVcOpmsqTHa06o72sw1RL02p9z0VbnMLOaIX3QKaYKSCFQzBKEUNHTSc48k53RH9wxGMtpQa5KjjW0W0n6XCCCG4yxNNdhQ4R4l1Ff+2sSd6UFHiIEOyqqFgT01mEUMD+joy75jPhOA+oVVLm309FR4yVOlp4RhLiScNmSmaYF5Pw0STrOIoWMSR2UkRXOMp+M4SHW8o8Zoi6OZgjKOaFar8zZDzkWzvKOjkKBjmCXby8JahhjXULY4KlzgKLvAwxVGhvyd4zxB1d9T0piazmKLCVZY5sKiD0y2ZSYrkUEPUbIk+dlQ4SJHTR50k1DPaUWIdTZW9NJwnJMOECgd7ou/MnppMJ02O1VT4Wsh85MnZzcFTngpXGKo84qmwgKbCL/orR/SzJ2crA+t6Mp94KvxJUeIbT3CQu1uIdlQEOzlKfS3UMcrTiFmOuroocrZrT2AcmamOKg8YomeEKm/rlT2sociMaybaUlFhuqHCM2qIJ+rg4EcDFymiDSxzaHdPcpE62pD5kyM5SBMoA1PaUtfIthS85ig1VPiPPYXgYEMNk4Qq7TXBgo7oT57gPUdwgCHzhIVFPFU6OYJzHAX9m5oNrVjeE61miDrqQ4VSa1oiURTsKHC0IfjNwU2WzK6eqK8jWln4g15TVBnqmDteCJ501PGAocJhhqjZdtBEB6lnhLreFJKxmlKbeGrqLiSThVIbCdGzloasa6lpMQXHCME2boLpJgT7yWaemu6wBONbqGNVRS0PKIL7LckbjmQtR7K8I5qtqel+T/ChJTNIKLjdUMNIRyvOEko9YYl2cwQveBikCNawJKcLBbc7+JM92mysNvd/Fqp8a0k6CNEe7cnZrxlW0wQXaXjaktnRwNOGZKYiONwS7a1JVheq3WgJHlQUGKHKmp4KAxXR/ULURcNgoa4zhKSLpZR3kxRRb0NmD0OFn+UCS7CzI1nbP6+o4x47QZE5xRCt3ZagnYcvmpYQktXdk5YKXTzBC57kKEe0VVuiSYqapssMS3C9p2CKkHOg8B8Pa8p5atrIw3qezIWanMGa5HRDNF6RM9wcacl0N+Q8Z8hsIkSnaIIdHRUOEebAPy1zbCkhM062FCJtif7PU+UtoVXzWKqM1PxXO8cfdruhFQ/a6x3JKYagvVDhQEtNiyiiSQ7OsuRsZUku0CRNDs4Sog6KKjsZgk2bYJqijgsEenoKeniinRXBn/U3lgpPdyDZynQx8IiioMnCep5Ky8mjGs6Wty0l1hUQTcNWswS3WRp2kCNZwJG8omG8JphPUaFbC8lEfabwP7VtM9yoaNCAjpR41VNhrD9LkbN722v0CoZMByFzhaW+MyzRYEWFDQwN2M4/JiT76PuljT3VU/A36eaIThb+R9oZGOAJ9tewkgGvqOMNRWYjT/Cwu99Q8LqDE4TgbLWxJ1jaDDAERsFOFrobgjUsBScaguXU8kKm2RL19tRypSHnHNlHiIZqgufs4opgQdVdwxBNNFBR6kVFqb8ogimOzB6a6HTzrlDHEpYaxjiiA4TMQobkDg2vejjfwJGWmnbVFAw3H3hq2NyQfG7hz4aC+w3BbwbesG0swYayvpAs6++Ri1Vfzx93mFChvyN5xVHTS+0p9aqCAxyZ6ZacZyw5+7uuQkFPR9DDk9NOiE7X1PCYJVjVUqq7JlrHwWALF5nfHNGjApdpqgzx5OwilDhCiDYTgnc9waGW4BdLNNUQvOtpzDOWHDH8D7TR/A/85KljEQu3NREc4Pl/6B1Hhc8Umb5CsKMmGC9EPcxoT2amwHNCmeOEnOPbklnMkbOgIvO5UMOpQrS9UGVdt6iH/fURjhI/WOpaW9OKLYRod6HCUEdOX000wpDZQ6hwg6LgZfOqo1RfT/CrJzjekXOGhpc1VW71ZLbXyyp+93ILbC1kPtIEYx0FIx1VDrLoVzXRKRYWk809yYlC9ImcrinxtabKnzRJk3lAU1OLEN1j2zrYzr2myHRXJFf4h4QKT1qSTzTB5+ZNTzTRkAxX8FcLV2uS8eoQQ2aAkFzvCM72sJIcJET3WPjRk5wi32uSS9rfZajpWEvj9hW42F4o5NytSXYy8IKHay10VYdrcl4SkqscrXpMwyGOgtkajheSxdQqmpxP1L3t4R5PqasFnrQEjytq6qgp9Y09Qx9o4S1FzhUCn1kyHSzBWLemoSGvOqLNhZyBjmCaAUYpMgt4Ck7wBBMMwWKWgjsUwTaGVsxWC1mYoKiyqqeGKYqonSIRQ3KIkHO0pmAxTdBHkbOvfllfr+AA+7gnc50huVKYK393FOyg7rbPO/izI7hE4CnHHHnJ0ogNPRUGeUpsrZZTBJcrovUcJe51BPsr6GkJdhCCsZ6aTtMEb2pqWkqeVtDXE/QVggsU/Nl86d9RMF3DxvZTA58agu810RWawCiSzzXBeU3MMW9oyJUedvNEvQyNu1f10BSMddR1vaLCYpYa/mGocLSiYDcLbQz8aMn5iyF4xBNMs1P0QEOV7o5gaWGuzSeLue4tt3ro7y4Tgm4G/mopdZgl6q0o6KzJWE3mMksNr3r+a6CbT8g5wZNzT9O7fi/zpaOmnz3BRoqos+tv9zMbdpxsqDBOEewtJLt7cg5wtKKbvldpSzRRCD43VFheCI7yZLppggMVBS/KMAdHODJvOwq2NQSbKKKPLdFWQs7Fqo+mpl01JXYRgq8dnGLhTiFzqmWsUMdpllZdbKlyvSdYxhI9YghOtxR8LgSLWHK62mGGVoxzBE8LNWzqH9CUesQzFy5RQzTc56mhi6fgXEWwpKfE5Z7M05ZgZUPmo6auiv8YKzDYwWBLMErIbKHJvOwIrvEdhOBcQ9JdU1NHQ7CXn2XIDFBKU2WAgcX9UAUzDXWd5alwuyJ41Z9rjKLCL4aCp4WarhPm2rH+SaHUYE001JDZ2ZAzXPjdMpZWvC9wmqIB2lLhQ01D5jO06hghWMndbM7yRJMsoCj1vYbnFQVrW9jak3OlEJ3s/96+p33dEPRV5GxiqaGjIthUU6FFEZyqCa5qJrpBdzSw95IUnOPIrCUUjRZQFrbw5PR0R1qiYx3cb6nrWUMrBmmiBQxVHtTew5ICP/ip6g4hed/Akob/32wvBHsIOX83cI8hGeNeNPCIkPmXe8fPKx84OMSRM1MTdXSwjCZ4S30jVGhvqTRak/OVhgGazHuOCud5onEO1lJr6ecVyaOK6H7zqlBlIaHE0oroCgfvGJIdPcmfLNGLjpz7hZwZQpUbFME0A1cIJa7VNORkgfsMBatbKgwwJM9bSvQXeNOvbIjelg6WWvo5kvbKaJJNHexkKNHL9xRyFlH8Ti2riB5wVPhUk7nGkJnoCe428LR/wRGdYIlmWebCyxou1rCk4g/ShugBDX0V0ZQWkh0dOVsagkM0yV6OoLd5ye+pRlsCr0n+KiQrGuq5yJDzrTAXHtLUMduTDBVKrSm3eHL+6ijxhFDX9Z5gVU/wliHYTMiMFpKLNMEywu80wd3meoFmt6VbRMPenhrOc6DVe4pgXU8DnnHakLOIIrlF4FZPIw6R+zxBP0dyq6OOZ4Q5sLKCcz084ok+VsMMyQhNZmmBgX5xIXOEJTmi7VsGTvMTNdHHhpzdbE8Du2oKxgvBqQKdDDnTFOylCFaxR1syz2iqrOI/FEpNc3C6f11/7+ASS6l2inq2ciTrCCzgyemrCL5SVPjQkdPZUmGy2c9Sw9FtR1sS30RmsKPCS4rkIC/2U0MduwucYolGaPjKEyhzmiPYXagyWbYz8LWBDdzRimAXzxx4z8K9hpzlhLq+NiQ97HuKorMUfK/OVvC2JfiHUPCQI/q7J2gjK+tTDNxkCc4TMssqCs4TGtLVwQihyoAWgj9bosU80XGW6Ac9TJGziaUh5+hnFcHOnlaM1iRn29NaqGENTTTSUHCH2tWTeV0osUhH6psuVLjRUmGWhm6OZEshGeNowABHcJ2Bpy2ZszRcKkRXd2QuKVEeXnbfaEq825FguqfgfE2whlChSRMdron+LATTPQ2Z369t4B9C5gs/ylzv+CMmepIDPclFQl13W0rspPd1JOcbghGOEutqCv5qacURQl3dDKyvyJlqKXGPgcM9FfawJAMVmdcspcYKOZc4GjDYkFlK05olNMHyHn4zFNykyOxt99RkHlfwmiHo60l2EKI+mhreEKp080Tbug08BVPcgoqC5zWt+NLDTZ7oNSF51N1qie7Va3uCCwyZbkINf/NED6jzOsBdZjFN8oqG3wxVunqCSYYKf3EdhJyf9YWGf7tRU2oH3VHgPr1fe5J9hOgHd7xQ0y7qBwXr23aGErP0cm64JVjZwsOGqL+mhNgZmhJLW2oY4UhedsyBgzrCKrq7BmcpNVhR6jBPq64Vgi+kn6XE68pp8J5/+0wRHGOpsKenQn9DZntPzjRLZpDAdD2fnSgkG9tmIXnUwQ6WVighs7Yi2MxQ0N3CqYaCXkJ0oyOztMDJjmSSpcpvlrk0RMMOjmArQ04PRV1DO1FwhCVaUVPpKUM03JK5SxPsIWRu8/CGHi8UHChiqGFDTbSRJWeYUDDcH6vJWUxR4k1FXbMUwV6e4AJFXS8oMqsZKqzvYQ9DDQdZckY4aGsIhtlubbd2r3j4QBMoTamdPZk7O/Bf62lacZwneNjQoGcdVU7zJOd7ghsUHOkosagic6cnWc8+4gg285R6zZP5s1/LUbCKIznTwK36PkdwlOrl4U1LwfdCCa+IrvFkmgw1PCAUXKWo0sURXWcI2muKJlgyFzhynCY4RBOsqCjoI1R5zREco0n2Vt09BQtYSizgKNHfUmUrQ5UOCh51BFcLmY7umhYqXKQomOop8bUnWNNQcIiBcYaC6xzMNOS8JQQfeqKBmmglB+97ok/lfk3ygaHSyZaCRTzRxQo6GzLfa2jWBPepw+UmT7SQEJyiyRkhBLMVOfcoMjcK0eZChfUNzFAUzCsEN5vP/X1uP/n/aoMX+K+nw/Hjr/9xOo7j7Pju61tLcgvJpTWXNbfN5jLpi6VfCOviTktKlFusQixdEKWmEBUKNaIpjZRSSOXSgzaaKLdabrm1/9nZ+/f+vd/vz/v9+Xy+zZ7PRorYoZqyLrCwQdEAixxVOEXNNnjX2nUSRlkqGmWowk8lxR50JPy9Bo6qJXaXwNvREBvnThPEPrewryLhcAnj5WE15Fqi8W7R1sAuEu86S4ENikItFN4xkv9Af4nXSnUVcLiA9xzesFpivRRVeFKtsMRaKBhuSbjOELnAUtlSQUpXgdfB4Z1oSbnFEetbQ0IrAe+Y+pqnDcEJFj6S8LDZzZHwY4e3XONNlARraomNEt2bkvGsosA3ioyHm+6jCMbI59wqt4eeara28IzEmyPgoRaUOEDhTVdEJhmCoTWfC0p8aNkCp0oYqih2iqGi4yXeMkOsn4LdLLnmKfh/YogjNsPebeFGR4m9BJHLzB61XQ3BtpISfS2FugsK9FAtLWX1dCRcrCnUp44CNzuCowUZmxSRgYaE6Za0W2u/E7CVXCiI/UOR8aAm1+OSyE3mOUcwyc1zBBeoX1kiKy0Zfxck1Gsyulti11i83QTBF5Kg3pDQThFMVHiPSlK+0cSedng/VaS8bOZbtsBcTcZAR8JP5KeqQ1OYKAi20njdNNRpgnsU//K+JnaXJaGTomr7aYIphoRn9aeShJWKEq9LcozSF7QleEfDI5LYm5bgVkFkRwVDBCVu0DDIkGupo8TZBq+/pMQURYErJQmPKGKjNDkWOLx7Jd5QizdUweIaKrlP7SwJDhZvONjLkOsBBX9UpGxnydhXkfBLQ8IxgojQbLFnJf81JytSljclYYyEFyx0kVBvKWOFJmONpshGAcsduQY5giVNCV51eOdJYo/pLhbvM0uDHSevNKRcrKZIqnCtJeEsO95RoqcgGK4ocZcho1tTYtcZvH41pNQ7vA0WrhIfOSraIIntIAi+NXWCErdbkvrWwjRLrt0NKUdL6KSOscTOdMSOUtBHwL6OLA0vNSdynaWQEnCpIvKaIrJJEbvHkmuNhn6OjM8VkSGSqn1uYJCGHnq9I3aLhNME3t6GjIkO7xrNFumpyTNX/NrwX7CrIRiqqWijI9JO4d1iieykyfiposQIQ8YjjsjlBh6oHWbwRjgYJQn2NgSnNycmJAk3NiXhx44Sxykihxm8ybUwT1OVKySc7vi3OXVkdBJ4AyXBeksDXG0IhgtYY0lY5ahCD0ehborIk5aUWRJviMA7Xt5kyRjonrXENkm8yYqgs8VzgrJmClK20uMM3jRJ0FiQICQF9hdETlLQWRIb5ki6WDfWRPobvO6a4GP5mcOrNzDFELtTkONLh9dXE8xypEg7z8A9jkhrQ6Fhjlg/QVktJXxt4WXzT/03Q8IaQWSqIuEvloQ2mqC9Jfi7wRul4RX3pSPlzpoVlmCtI2jvKHCFhjcM3sN6lqF6HxnKelLjXWbwrpR4xzuCrTUZx2qq9oAh8p6ixCUGr78g8oyjRAtB5CZFwi80VerVpI0h+IeBxa6Zg6kWvpDHaioYYuEsRbDC3eOmC2JvGYLeioxGknL2UATNJN6hmtj1DlpLvDVmocYbrGCVJKOrg4X6DgddLA203BKMFngdJJFtFd7vJLm6KEpc5yjQrkk7M80SGe34X24nSex1Ra5Omgb71JKyg8SrU3i/kARKwWpH0kOGhKkObyfd0ZGjvyXlAkVZ4xRbYJ2irFMkFY1SwyWxr2oo4zlNiV+7zmaweFpT4kR3kaDAFW6xpSqzJay05FtYR4HmZhc9UxKbbfF2V8RG1MBmSaE+kmC6JnaRXK9gsiXhJHl/U0qM0WTcbyhwkYIvFGwjSbjfwhiJt8ZSQU+Bd5+marPMOkVkD0muxYLIfEuhh60x/J92itguihJSEMySVPQnTewnEm+620rTQEMsOfo4/kP/0ARvWjitlpSX7GxBgcMEsd3EEeYWvdytd+Saawi6aCIj1CkGb6Aj9rwhx16Cf3vAwFy5pyLhVonXzy51FDpdEblbkdJbUcEPDEFzQ8qNmhzzLTmmKWKbFCXeEuRabp6rxbvAtLF442QjQ+wEA9eL1xSR7Q0JXzlSHjJ4exq89yR0laScJ/FW6z4a73pFMEfDiRZvuvijIt86RaSFOl01riV2mD1UEvxGk/Geg5aWwGki1zgKPG9J2U8PEg8qYvMsZeytiTRXBMslCU8JSlxi8EabjwUldlDNLfzTUmCgxWsjqWCOHavYAqsknKFIO0yQ61VL5AVFxk6WhEaCAkdJgt9aSkzXlKNX2jEa79waYuc7gq0N3GDJGCBhoiTXUEPsdknCUE1CK0fwsiaylSF2uiDyO4XX3pFhNd7R4itFGc0k/ElBZwWvq+GC6szVeEoS/MZ+qylwpKNKv9Z469UOjqCjwlusicyTxG6VpNxcQ8IncoR4RhLbR+NdpGGmJWOcIzJGUuKPGpQg8rrG21dOMqQssJQ4RxH5jaUqnZuQ0F4Q+cjxLwPtpZbIAk3QTJHQWBE5S1BokoVtDd6lhqr9UpHSUxMcIYl9pojsb8h4SBOsMQcqvOWC2E8EVehqiJ1hrrAEbQxeK0NGZ0Gkq+guSRgniM23bIHVkqwx4hiHd7smaOyglyIyQuM978j4VS08J/A2G1KeMBRo4fBaSNhKUEZfQewVQ/C1I+MgfbEleEzCUw7mKXI0M3hd1EESVji8x5uQ41nxs1q4RMJCCXs7Iq9acpxn22oSDnQ/sJTxsCbHIYZiLyhY05TY0ZLIOQrGaSJDDN4t8pVaIrsqqFdEegtizc1iTew5Q4ayBDMUsQMkXocaYkc0hZua412siZ1rSXlR460zRJ5SlHGe5j801RLMlJTxtaOM3Q1pvxJ45zUlWFD7rsAbpfEm1JHxG0eh8w2R7QQVzBUw28FhFp5QZzq8t2rx2joqulYTWSuJdTYfWwqMFMcovFmSyJPNyLhE4E10pHzYjOC3huArRa571ZsGajQpQx38SBP5pyZB6lMU3khDnp0MBV51BE9o2E+TY5Ml2E8S7C0o6w1xvCZjf0HkVEHCzFoyNmqC+9wdcqN+Tp7jSDheE9ws8Y5V0NJCn2bk2tqSY4okdrEhx1iDN8cSudwepWmAGXKcJXK65H9to8jYQRH7SBF01ESUJdd0TayVInaWhLkOjlXE5irKGOnI6GSWGCJa482zBI9rCr0jyTVcEuzriC1vcr6mwFGSiqy5zMwxBH/TJHwjSPhL8+01kaaSUuMFKTcLEvaUePcrSmwn8DZrgikWb7CGPxkSjhQwrRk57tctmxLsb9sZvL9LSlyuSLlWkqOjwduo8b6Uv1DkmudIeFF2dHCgxVtk8dpIvHpBxhEOdhKk7OLIUSdJ+cSRY57B+0DgGUUlNfpthTfGkauzxrvTsUUaCVhlKeteTXCoJDCa2NOKhOmC4G1H8JBd4OBZReSRGkqcb/CO1PyLJTLB4j1q8JYaIutEjSLX8YKM+a6phdMsdLFUoV5RTm9JSkuDN8WcIon0NZMNZWh1q8C7SJEwV5HxrmnnTrf3KoJBlmCYI2ilSLlfEvlE4011NNgjgthzEua0oKK7JLE7HZHlEl60BLMVFewg4EWNt0ThrVNEVkkiTwpKXSWJzdRENgvKGq4IhjsiezgSFtsfCUq8qki5S1LRQeYQQ4nemmCkImWMw3tFUoUBZk4NOeZYEp4XRKTGa6wJjrWNHBVJR4m3FCnbuD6aak2WsMTh3SZImGCIPKNgsDpVwnsa70K31lCFJZYcwwSMFcQulGTsZuEaSdBXkPGZhu0FsdUO73RHjq8MPGGIfaGIbVTk6iuI3GFgucHrIQkmWSJdBd7BBu+uOryWAhY7+Lki9rK5wtEQzWwvtbqGhIMFwWRJsElsY4m9IIg9L6lCX0VklaPAYkfkZEGDnOWowlBJjtMUkcGK4Lg6EtoZInMUBVYLgn0UsdmCyCz7gIGHFfk+k1QwTh5We7A9x+IdJ6CvIkEagms0hR50eH9UnTQJ+2oiKyVlLFUE+8gBGu8MQ3CppUHesnjTHN4QB/UGPhCTHLFPHMFrCqa73gqObUJGa03wgbhHkrCfpEpzNLE7JDS25FMKhlhKKWKfCgqstLCPu1zBXy0J2ztwjtixBu8UTRn9LVtkmCN2iyFhtME70JHRQ1KVZXqKI/KNIKYMCYs1GUMEKbM1bKOI9LDXC7zbHS+bt+1MTWS9odA9DtrYtpbImQJ2VHh/lisEwaHqUk1kjKTAKknkBEXkbkdMGwq0dnhzLJF3NJH3JVwrqOB4Sca2hti75nmJN0WzxS6UxDYoEpxpa4htVlRjkYE7DZGzJVU72uC9IyhQL4i8YfGWSYLLNcHXloyz7QhNifmKSE9JgfGmuyLhc403Xm9vqcp6gXe3xuuv8F6VJNxkyTHEkHG2g0aKXL0MsXc1bGfgas2//dCONXiNLCX+5mB7eZIl1kHh7ajwpikyzlUUWOVOsjSQlsS+M0R+pPje/dzBXRZGO0rMtgQrLLG9VSu9n6CMXS3BhwYmSoIBhsjNBmZbgusE9BCPCP5triU4VhNbJfE+swSP27aayE8tuTpYYjtrYjMVGZdp2NpS1s6aBnKSHDsbKuplKbHM4a0wMFd/5/DmGyKrJSUaW4IBrqUhx0vyfzTBBLPIUcnZdrAkNsKR0sWRspumSns6Ch0v/qqIbBYUWKvPU/CFoyrDJGwSNFhbA/MlzKqjrO80hRbpKx0Jewsi/STftwGSlKc1JZyAzx05dhLEdnfQvhZOqiHWWEAHC7+30FuRcZUgaO5gpaIK+xsiHRUsqaPElTV40xQZQ107Q9BZE1nryDVGU9ZSQ47bmhBpLcYpUt7S+xuK/FiT8qKjwXYw5ypS2iuCv7q1gtgjhuBuB8LCFY5cUuCNtsQOFcT+4Ih9JX+k8Ea6v0iCIRZOtCT0Et00JW5UeC85Cg0ScK0k411HcG1zKtre3SeITBRk7WfwDhEvaYLTHP9le0m8By0JDwn4TlLW/aJOvGHxdjYUes+ScZigCkYQdNdEOhkiezgShqkx8ueKjI8lDfK2oNiOFvrZH1hS+tk7NV7nOmLHicGWEgubkXKdwdtZknCLJXaCpkrjZBtLZFsDP9CdxWsSr05Sxl6CMmoFbCOgryX40uDtamB7SVmXW4Ihlgpmq+00tBKUUa83WbjLUNkzDmY7cow1JDygyPGlhgGKYKz4vcV7QBNbJIgM11TUqZaMdwTeSguH6rOaw1JRKzaaGyxVm2EJ/uCIrVWUcZUkcp2grMsEjK+DMwS59jQk3Kd6SEq1d0S6uVmO4Bc1lDXTUcHjluCXEq+1OlBDj1pi9zgiXxnKuE0SqTXwhqbETW6RggMEnGl/q49UT2iCzgJvRwVXS2K/d6+ZkyUl7jawSVLit46EwxVljDZwoSQ20sDBihztHfk2yA8NVZghiXwrYHQdfKAOtzsayjhY9bY0yE2CWEeJ9xfzO423xhL5syS2TFJofO2pboHob0nY4GiAgRrvGQEDa/FWSsoaaYl0syRsEt3kWoH3B01shCXhTUWe9w3Bt44SC9QCh3eShQctwbaK2ApLroGCMlZrYqvlY3qYhM0aXpFkPOuoqJ3Dm6fxXrGwVF9gCWZagjPqznfkuMKQ8DPTQRO8ZqG1hPGKEm9IgpGW4DZDgTNriTxvFiq+Lz+0cKfp4wj6OCK9JSnzNSn9LFU7UhKZZMnYwcJ8s8yRsECScK4j5UOB95HFO0CzhY4xJxuCix0lDlEUeMdS6EZBkTsUkZ4K74dugyTXS7aNgL8aqjDfkCE0ZbwkCXpaWCKhl8P7VD5jxykivSyxyZrYERbe168LYu9ZYh86IkscgVLE7tWPKmJv11CgoyJltMEbrohtVAQfO4ImltiHEroYEs7RxAarVpY8AwXMcMReFOTYWe5iiLRQxJ5Q8DtJ8LQhWOhIeFESPGsILhbNDRljNbHzNRlTFbk2S3L0NOS6V1KFJYKUbSTcIIhM0wQ/s2TM0SRMNcQmSap3jCH4yhJZKSkwyRHpYYgsFeQ4U7xoCB7VVOExhXepo9ABBsYbvGWKXPME3lyH95YioZ0gssQRWWbI+FaSMkXijZXwgiTlYdPdkNLaETxlyDVIwqeaEus0aTcYcg0RVOkpR3CSJqIddK+90JCxzsDVloyrFd5ZAr4TBKfaWa6boEA7C7s6EpYaeFPjveooY72mjIccLHJ9HUwVlDhKkmutJDJBwnp1rvulJZggKDRfbXAkvC/4l3ozQOG9a8lxjx0i7nV4jSXc7vhe3OwIxjgSHjdEhhsif9YkPGlus3iLFDnWOFhtCZbJg0UbQcIaR67JjthoCyMEZRwhiXWyxO5QxI6w5NhT4U1WsJvDO60J34fW9hwzwlKij6ZAW9ne4L0s8C6XeBMEkd/LQy1VucBRot6QMlbivaBhoBgjqGiCJNhsqVp/S2SsG6DIONCR0dXhvWbJ+MRRZJkkuEjgDXJjFQW6SSL7GXK8Z2CZg7cVsbWGoKmEpzQ5elpiy8Ryg7dMkLLUEauzeO86CuwlSOlgYLojZWeJ9xM3S1PWfEfKl5ISLQ0MEKR8YOB2QfCxJBjrKPCN4f9MkaSsqoVXJBmP7EpFZ9UQfOoOFwSzBN4MQ8LsGrymlipcJQhmy0GaQjPqCHaXRwuCZwRbqK2Fg9wlClZqYicrIgMdZfxTQ0c7TBIbrChxmuzoKG8XRaSrIhhiyNFJkrC7oIAWMEOQa5aBekPCRknCo4IKPrYkvCDI8aYmY7WFtprgekcJZ3oLIqssCSMtFbQTJKwXYy3BY5oCh2iKPCpJOE+zRdpYgi6O2KmOAgvVCYaU4ySRek1sgyFhJ403QFHiVEmJHwtybO1gs8Hr5+BETQX3War0qZngYGgtVZtoqd6vFSk/UwdZElYqyjrF4HXUeFspIi9IGKf4j92pKGAdCYMVsbcV3kRF0N+R8LUd5PCsIGWoxDtBkCI0nKofdJQxT+LtZflvuc8Q3CjwWkq8KwUpHzkK/NmSsclCL0nseQdj5FRH5CNHSgtLiW80Of5HU9Hhlsga9bnBq3fEVltKfO5IaSTmGjjc4J0otcP7QsJUSQM8pEj5/wCuUuC2DWz8AAAAAElFTkSuQmCC\");\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/ayu-dark.css",
    "content": "/* Based on https://github.com/dempfi/ayu */\n\n.cm-s-ayu-dark.CodeMirror { background: #0a0e14; color: #b3b1ad; }\n.cm-s-ayu-dark div.CodeMirror-selected { background: #273747; }\n.cm-s-ayu-dark .CodeMirror-line::selection, .cm-s-ayu-dark .CodeMirror-line > span::selection, .cm-s-ayu-dark .CodeMirror-line > span > span::selection { background: rgba(39, 55, 71, 99); }\n.cm-s-ayu-dark .CodeMirror-line::-moz-selection, .cm-s-ayu-dark .CodeMirror-line > span::-moz-selection, .cm-s-ayu-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(39, 55, 71, 99); }\n.cm-s-ayu-dark .CodeMirror-gutters { background: #0a0e14; border-right: 0px; }\n.cm-s-ayu-dark .CodeMirror-guttermarker { color: white; }\n.cm-s-ayu-dark .CodeMirror-guttermarker-subtle { color: #3d424d; }\n.cm-s-ayu-dark .CodeMirror-linenumber { color: #3d424d; }\n.cm-s-ayu-dark .CodeMirror-cursor { border-left: 1px solid #e6b450; }\n.cm-s-ayu-dark.cm-fat-cursor .CodeMirror-cursor { background-color: #a2a8a175 !important; }\n.cm-s-ayu-dark .cm-animate-fat-cursor { background-color: #a2a8a175 !important; }\n\n.cm-s-ayu-dark span.cm-comment { color: #626a73; }\n.cm-s-ayu-dark span.cm-atom { color: #ae81ff; }\n.cm-s-ayu-dark span.cm-number { color: #e6b450; }\n\n.cm-s-ayu-dark span.cm-comment.cm-attribute { color: #ffb454; }\n.cm-s-ayu-dark span.cm-comment.cm-def { color: rgba(57, 186, 230, 80); }\n.cm-s-ayu-dark span.cm-comment.cm-tag { color: #39bae6; }\n.cm-s-ayu-dark span.cm-comment.cm-type { color: #5998a6; }\n\n.cm-s-ayu-dark span.cm-property, .cm-s-ayu-dark span.cm-attribute { color: #ffb454; }  \n.cm-s-ayu-dark span.cm-keyword { color: #ff8f40; } \n.cm-s-ayu-dark span.cm-builtin { color: #e6b450; }\n.cm-s-ayu-dark span.cm-string { color: #c2d94c; }\n\n.cm-s-ayu-dark span.cm-variable { color: #b3b1ad; }\n.cm-s-ayu-dark span.cm-variable-2 { color: #f07178; }\n.cm-s-ayu-dark span.cm-variable-3 { color: #39bae6; }\n.cm-s-ayu-dark span.cm-type { color: #ff8f40; }\n.cm-s-ayu-dark span.cm-def { color: #ffee99; }\n.cm-s-ayu-dark span.cm-bracket { color: #f8f8f2; }\n.cm-s-ayu-dark span.cm-tag { color: rgba(57, 186, 230, 80); }\n.cm-s-ayu-dark span.cm-header { color: #c2d94c; }\n.cm-s-ayu-dark span.cm-link { color: #39bae6; }\n.cm-s-ayu-dark span.cm-error { color: #ff3333; } \n\n.cm-s-ayu-dark .CodeMirror-activeline-background { background: #01060e; }\n.cm-s-ayu-dark .CodeMirror-matchingbracket {\n  text-decoration: underline;\n  color: white !important;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/ayu-mirage.css",
    "content": "/* Based on https://github.com/dempfi/ayu */\n\n.cm-s-ayu-mirage.CodeMirror { background: #1f2430; color: #cbccc6; }\n.cm-s-ayu-mirage div.CodeMirror-selected { background: #34455a; }\n.cm-s-ayu-mirage .CodeMirror-line::selection, .cm-s-ayu-mirage .CodeMirror-line > span::selection, .cm-s-ayu-mirage .CodeMirror-line > span > span::selection { background: #34455a; }\n.cm-s-ayu-mirage .CodeMirror-line::-moz-selection, .cm-s-ayu-mirage .CodeMirror-line > span::-moz-selection, .cm-s-ayu-mirage .CodeMirror-line > span > span::-moz-selection { background: rgba(25, 30, 42, 99); }\n.cm-s-ayu-mirage .CodeMirror-gutters { background: #1f2430; border-right: 0px; }\n.cm-s-ayu-mirage .CodeMirror-guttermarker { color: white; }\n.cm-s-ayu-mirage .CodeMirror-guttermarker-subtle { color:  rgba(112, 122, 140, 66); }\n.cm-s-ayu-mirage .CodeMirror-linenumber { color: rgba(61, 66, 77, 99); }\n.cm-s-ayu-mirage .CodeMirror-cursor { border-left: 1px solid #ffcc66;  }\n.cm-s-ayu-mirage.cm-fat-cursor .CodeMirror-cursor {background-color: #a2a8a175 !important;}\n.cm-s-ayu-mirage .cm-animate-fat-cursor { background-color: #a2a8a175 !important; }\n\n.cm-s-ayu-mirage span.cm-comment { color: #5c6773; font-style:italic; }\n.cm-s-ayu-mirage span.cm-atom { color: #ae81ff; }\n.cm-s-ayu-mirage span.cm-number { color: #ffcc66; }\n\n.cm-s-ayu-mirage span.cm-comment.cm-attribute { color: #ffd580; }\n.cm-s-ayu-mirage span.cm-comment.cm-def { color: #d4bfff; }\n.cm-s-ayu-mirage span.cm-comment.cm-tag { color: #5ccfe6; }\n.cm-s-ayu-mirage span.cm-comment.cm-type { color: #5998a6; }\n\n.cm-s-ayu-mirage span.cm-property { color: #f29e74; }\n.cm-s-ayu-mirage span.cm-attribute { color: #ffd580; }  \n.cm-s-ayu-mirage span.cm-keyword { color: #ffa759; } \n.cm-s-ayu-mirage span.cm-builtin { color: #ffcc66; }\n.cm-s-ayu-mirage span.cm-string { color: #bae67e; }\n\n.cm-s-ayu-mirage span.cm-variable { color: #cbccc6; }\n.cm-s-ayu-mirage span.cm-variable-2 { color: #f28779; }\n.cm-s-ayu-mirage span.cm-variable-3 { color: #5ccfe6; }\n.cm-s-ayu-mirage span.cm-type { color: #ffa759; }\n.cm-s-ayu-mirage span.cm-def { color: #ffd580; }\n.cm-s-ayu-mirage span.cm-bracket { color: rgba(92, 207, 230, 80); }\n.cm-s-ayu-mirage span.cm-tag { color: #5ccfe6; }\n.cm-s-ayu-mirage span.cm-header { color: #bae67e; }\n.cm-s-ayu-mirage span.cm-link { color: #5ccfe6; }\n.cm-s-ayu-mirage span.cm-error { color: #ff3333; } \n\n.cm-s-ayu-mirage .CodeMirror-activeline-background { background: #191e2a; }\n.cm-s-ayu-mirage .CodeMirror-matchingbracket {\n  text-decoration: underline;\n  color: white !important;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/base16-dark.css",
    "content": "/*\n\n    Name:       Base16 Default Dark\n    Author:     Chris Kempson (http://chriskempson.com)\n\n    CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)\n    Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)\n\n*/\n\n.cm-s-base16-dark.CodeMirror { background: #151515; color: #e0e0e0; }\n.cm-s-base16-dark div.CodeMirror-selected { background: #303030; }\n.cm-s-base16-dark .CodeMirror-line::selection, .cm-s-base16-dark .CodeMirror-line > span::selection, .cm-s-base16-dark .CodeMirror-line > span > span::selection { background: rgba(48, 48, 48, .99); }\n.cm-s-base16-dark .CodeMirror-line::-moz-selection, .cm-s-base16-dark .CodeMirror-line > span::-moz-selection, .cm-s-base16-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(48, 48, 48, .99); }\n.cm-s-base16-dark .CodeMirror-gutters { background: #151515; border-right: 0px; }\n.cm-s-base16-dark .CodeMirror-guttermarker { color: #ac4142; }\n.cm-s-base16-dark .CodeMirror-guttermarker-subtle { color: #505050; }\n.cm-s-base16-dark .CodeMirror-linenumber { color: #505050; }\n.cm-s-base16-dark .CodeMirror-cursor { border-left: 1px solid #b0b0b0; }\n.cm-s-base16-dark.cm-fat-cursor .CodeMirror-cursor { background-color: #8e8d8875 !important; }\n.cm-s-base16-dark .cm-animate-fat-cursor { background-color: #8e8d8875 !important; }\n\n.cm-s-base16-dark span.cm-comment { color: #8f5536; }\n.cm-s-base16-dark span.cm-atom { color: #aa759f; }\n.cm-s-base16-dark span.cm-number { color: #aa759f; }\n\n.cm-s-base16-dark span.cm-property, .cm-s-base16-dark span.cm-attribute { color: #90a959; }\n.cm-s-base16-dark span.cm-keyword { color: #ac4142; }\n.cm-s-base16-dark span.cm-string { color: #f4bf75; }\n\n.cm-s-base16-dark span.cm-variable { color: #90a959; }\n.cm-s-base16-dark span.cm-variable-2 { color: #6a9fb5; }\n.cm-s-base16-dark span.cm-def { color: #d28445; }\n.cm-s-base16-dark span.cm-bracket { color: #e0e0e0; }\n.cm-s-base16-dark span.cm-tag { color: #ac4142; }\n.cm-s-base16-dark span.cm-link { color: #aa759f; }\n.cm-s-base16-dark span.cm-error { background: #ac4142; color: #b0b0b0; }\n\n.cm-s-base16-dark .CodeMirror-activeline-background { background: #202020; }\n.cm-s-base16-dark .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/base16-light.css",
    "content": "/*\n\n    Name:       Base16 Default Light\n    Author:     Chris Kempson (http://chriskempson.com)\n\n    CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)\n    Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)\n\n*/\n\n.cm-s-base16-light.CodeMirror { background: #f5f5f5; color: #202020; }\n.cm-s-base16-light div.CodeMirror-selected { background: #e0e0e0; }\n.cm-s-base16-light .CodeMirror-line::selection, .cm-s-base16-light .CodeMirror-line > span::selection, .cm-s-base16-light .CodeMirror-line > span > span::selection { background: #e0e0e0; }\n.cm-s-base16-light .CodeMirror-line::-moz-selection, .cm-s-base16-light .CodeMirror-line > span::-moz-selection, .cm-s-base16-light .CodeMirror-line > span > span::-moz-selection { background: #e0e0e0; }\n.cm-s-base16-light .CodeMirror-gutters { background: #f5f5f5; border-right: 0px; }\n.cm-s-base16-light .CodeMirror-guttermarker { color: #ac4142; }\n.cm-s-base16-light .CodeMirror-guttermarker-subtle { color: #b0b0b0; }\n.cm-s-base16-light .CodeMirror-linenumber { color: #b0b0b0; }\n.cm-s-base16-light .CodeMirror-cursor { border-left: 1px solid #505050; }\n\n.cm-s-base16-light span.cm-comment { color: #8f5536; }\n.cm-s-base16-light span.cm-atom { color: #aa759f; }\n.cm-s-base16-light span.cm-number { color: #aa759f; }\n\n.cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute { color: #90a959; }\n.cm-s-base16-light span.cm-keyword { color: #ac4142; }\n.cm-s-base16-light span.cm-string { color: #f4bf75; }\n\n.cm-s-base16-light span.cm-variable { color: #90a959; }\n.cm-s-base16-light span.cm-variable-2 { color: #6a9fb5; }\n.cm-s-base16-light span.cm-def { color: #d28445; }\n.cm-s-base16-light span.cm-bracket { color: #202020; }\n.cm-s-base16-light span.cm-tag { color: #ac4142; }\n.cm-s-base16-light span.cm-link { color: #aa759f; }\n.cm-s-base16-light span.cm-error { background: #ac4142; color: #505050; }\n\n.cm-s-base16-light .CodeMirror-activeline-background { background: #DDDCDC; }\n.cm-s-base16-light .CodeMirror-matchingbracket { color: #f5f5f5 !important; background-color: #6A9FB5 !important}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/bespin.css",
    "content": "/*\n\n    Name:       Bespin\n    Author:     Mozilla / Jan T. Sott\n\n    CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)\n    Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)\n\n*/\n\n.cm-s-bespin.CodeMirror {background: #28211c; color: #9d9b97;}\n.cm-s-bespin div.CodeMirror-selected {background: #59554f !important;}\n.cm-s-bespin .CodeMirror-gutters {background: #28211c; border-right: 0px;}\n.cm-s-bespin .CodeMirror-linenumber {color: #666666;}\n.cm-s-bespin .CodeMirror-cursor {border-left: 1px solid #797977 !important;}\n\n.cm-s-bespin span.cm-comment {color: #937121;}\n.cm-s-bespin span.cm-atom {color: #9b859d;}\n.cm-s-bespin span.cm-number {color: #9b859d;}\n\n.cm-s-bespin span.cm-property, .cm-s-bespin span.cm-attribute {color: #54be0d;}\n.cm-s-bespin span.cm-keyword {color: #cf6a4c;}\n.cm-s-bespin span.cm-string {color: #f9ee98;}\n\n.cm-s-bespin span.cm-variable {color: #54be0d;}\n.cm-s-bespin span.cm-variable-2 {color: #5ea6ea;}\n.cm-s-bespin span.cm-def {color: #cf7d34;}\n.cm-s-bespin span.cm-error {background: #cf6a4c; color: #797977;}\n.cm-s-bespin span.cm-bracket {color: #9d9b97;}\n.cm-s-bespin span.cm-tag {color: #cf6a4c;}\n.cm-s-bespin span.cm-link {color: #9b859d;}\n\n.cm-s-bespin .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;}\n.cm-s-bespin .CodeMirror-activeline-background { background: #404040; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/blackboard.css",
    "content": "/* Port of TextMate's Blackboard theme */\n\n.cm-s-blackboard.CodeMirror { background: #0C1021; color: #F8F8F8; }\n.cm-s-blackboard div.CodeMirror-selected { background: #253B76; }\n.cm-s-blackboard .CodeMirror-line::selection, .cm-s-blackboard .CodeMirror-line > span::selection, .cm-s-blackboard .CodeMirror-line > span > span::selection { background: rgba(37, 59, 118, .99); }\n.cm-s-blackboard .CodeMirror-line::-moz-selection, .cm-s-blackboard .CodeMirror-line > span::-moz-selection, .cm-s-blackboard .CodeMirror-line > span > span::-moz-selection { background: rgba(37, 59, 118, .99); }\n.cm-s-blackboard .CodeMirror-gutters { background: #0C1021; border-right: 0; }\n.cm-s-blackboard .CodeMirror-guttermarker { color: #FBDE2D; }\n.cm-s-blackboard .CodeMirror-guttermarker-subtle { color: #888; }\n.cm-s-blackboard .CodeMirror-linenumber { color: #888; }\n.cm-s-blackboard .CodeMirror-cursor { border-left: 1px solid #A7A7A7; }\n\n.cm-s-blackboard .cm-keyword { color: #FBDE2D; }\n.cm-s-blackboard .cm-atom { color: #D8FA3C; }\n.cm-s-blackboard .cm-number { color: #D8FA3C; }\n.cm-s-blackboard .cm-def { color: #8DA6CE; }\n.cm-s-blackboard .cm-variable { color: #FF6400; }\n.cm-s-blackboard .cm-operator { color: #FBDE2D; }\n.cm-s-blackboard .cm-comment { color: #AEAEAE; }\n.cm-s-blackboard .cm-string { color: #61CE3C; }\n.cm-s-blackboard .cm-string-2 { color: #61CE3C; }\n.cm-s-blackboard .cm-meta { color: #D8FA3C; }\n.cm-s-blackboard .cm-builtin { color: #8DA6CE; }\n.cm-s-blackboard .cm-tag { color: #8DA6CE; }\n.cm-s-blackboard .cm-attribute { color: #8DA6CE; }\n.cm-s-blackboard .cm-header { color: #FF6400; }\n.cm-s-blackboard .cm-hr { color: #AEAEAE; }\n.cm-s-blackboard .cm-link { color: #8DA6CE; }\n.cm-s-blackboard .cm-error { background: #9D1E15; color: #F8F8F8; }\n\n.cm-s-blackboard .CodeMirror-activeline-background { background: #3C3636; }\n.cm-s-blackboard .CodeMirror-matchingbracket { outline:1px solid grey;color:white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/cobalt.css",
    "content": ".cm-s-cobalt.CodeMirror { background: #002240; color: white; }\n.cm-s-cobalt div.CodeMirror-selected { background: #b36539; }\n.cm-s-cobalt .CodeMirror-line::selection, .cm-s-cobalt .CodeMirror-line > span::selection, .cm-s-cobalt .CodeMirror-line > span > span::selection { background: rgba(179, 101, 57, .99); }\n.cm-s-cobalt .CodeMirror-line::-moz-selection, .cm-s-cobalt .CodeMirror-line > span::-moz-selection, .cm-s-cobalt .CodeMirror-line > span > span::-moz-selection { background: rgba(179, 101, 57, .99); }\n.cm-s-cobalt .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; }\n.cm-s-cobalt .CodeMirror-guttermarker { color: #ffee80; }\n.cm-s-cobalt .CodeMirror-guttermarker-subtle { color: #d0d0d0; }\n.cm-s-cobalt .CodeMirror-linenumber { color: #d0d0d0; }\n.cm-s-cobalt .CodeMirror-cursor { border-left: 1px solid white; }\n\n.cm-s-cobalt span.cm-comment { color: #08f; }\n.cm-s-cobalt span.cm-atom { color: #845dc4; }\n.cm-s-cobalt span.cm-number, .cm-s-cobalt span.cm-attribute { color: #ff80e1; }\n.cm-s-cobalt span.cm-keyword { color: #ffee80; }\n.cm-s-cobalt span.cm-string { color: #3ad900; }\n.cm-s-cobalt span.cm-meta { color: #ff9d00; }\n.cm-s-cobalt span.cm-variable-2, .cm-s-cobalt span.cm-tag { color: #9effff; }\n.cm-s-cobalt span.cm-variable-3, .cm-s-cobalt span.cm-def, .cm-s-cobalt .cm-type { color: white; }\n.cm-s-cobalt span.cm-bracket { color: #d8d8d8; }\n.cm-s-cobalt span.cm-builtin, .cm-s-cobalt span.cm-special { color: #ff9e59; }\n.cm-s-cobalt span.cm-link { color: #845dc4; }\n.cm-s-cobalt span.cm-error { color: #9d1e15; }\n\n.cm-s-cobalt .CodeMirror-activeline-background { background: #002D57; }\n.cm-s-cobalt .CodeMirror-matchingbracket { outline:1px solid grey;color:white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/colorforth.css",
    "content": ".cm-s-colorforth.CodeMirror { background: #000000; color: #f8f8f8; }\n.cm-s-colorforth .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; }\n.cm-s-colorforth .CodeMirror-guttermarker { color: #FFBD40; }\n.cm-s-colorforth .CodeMirror-guttermarker-subtle { color: #78846f; }\n.cm-s-colorforth .CodeMirror-linenumber { color: #bababa; }\n.cm-s-colorforth .CodeMirror-cursor { border-left: 1px solid white; }\n\n.cm-s-colorforth span.cm-comment     { color: #ededed; }\n.cm-s-colorforth span.cm-def         { color: #ff1c1c; font-weight:bold; }\n.cm-s-colorforth span.cm-keyword     { color: #ffd900; }\n.cm-s-colorforth span.cm-builtin     { color: #00d95a; }\n.cm-s-colorforth span.cm-variable    { color: #73ff00; }\n.cm-s-colorforth span.cm-string      { color: #007bff; }\n.cm-s-colorforth span.cm-number      { color: #00c4ff; }\n.cm-s-colorforth span.cm-atom        { color: #606060; }\n\n.cm-s-colorforth span.cm-variable-2  { color: #EEE; }\n.cm-s-colorforth span.cm-variable-3, .cm-s-colorforth span.cm-type { color: #DDD; }\n.cm-s-colorforth span.cm-property    {}\n.cm-s-colorforth span.cm-operator    {}\n\n.cm-s-colorforth span.cm-meta        { color: yellow; }\n.cm-s-colorforth span.cm-qualifier   { color: #FFF700; }\n.cm-s-colorforth span.cm-bracket     { color: #cc7; }\n.cm-s-colorforth span.cm-tag         { color: #FFBD40; }\n.cm-s-colorforth span.cm-attribute   { color: #FFF700; }\n.cm-s-colorforth span.cm-error       { color: #f00; }\n\n.cm-s-colorforth div.CodeMirror-selected { background: #333d53; }\n\n.cm-s-colorforth span.cm-compilation { background: rgba(255, 255, 255, 0.12); }\n\n.cm-s-colorforth .CodeMirror-activeline-background { background: #253540; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/darcula.css",
    "content": "/**\n    Name: IntelliJ IDEA darcula theme\n    From IntelliJ IDEA by JetBrains\n */\n\n.cm-s-darcula  { font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif;}\n.cm-s-darcula.CodeMirror { background: #2B2B2B; color: #A9B7C6; }\n\n.cm-s-darcula span.cm-meta { color: #BBB529; }\n.cm-s-darcula span.cm-number { color: #6897BB; }\n.cm-s-darcula span.cm-keyword { color: #CC7832; line-height: 1em; font-weight: bold; }\n.cm-s-darcula span.cm-def { color: #A9B7C6; font-style: italic; }\n.cm-s-darcula span.cm-variable { color: #A9B7C6; }\n.cm-s-darcula span.cm-variable-2 { color: #A9B7C6; }\n.cm-s-darcula span.cm-variable-3 { color: #9876AA; }\n.cm-s-darcula span.cm-type { color: #AABBCC; font-weight: bold; }\n.cm-s-darcula span.cm-property { color: #FFC66D; }\n.cm-s-darcula span.cm-operator { color: #A9B7C6; }\n.cm-s-darcula span.cm-string { color: #6A8759; }\n.cm-s-darcula span.cm-string-2 { color: #6A8759; }\n.cm-s-darcula span.cm-comment { color: #61A151; font-style: italic; }\n.cm-s-darcula span.cm-link { color: #CC7832; }\n.cm-s-darcula span.cm-atom { color: #CC7832; }\n.cm-s-darcula span.cm-error { color: #BC3F3C; }\n.cm-s-darcula span.cm-tag { color: #629755; font-weight: bold; font-style: italic; text-decoration: underline; }\n.cm-s-darcula span.cm-attribute { color: #6897bb; }\n.cm-s-darcula span.cm-qualifier { color: #6A8759; }\n.cm-s-darcula span.cm-bracket { color: #A9B7C6; }\n.cm-s-darcula span.cm-builtin { color: #FF9E59; }\n.cm-s-darcula span.cm-special { color: #FF9E59; }\n.cm-s-darcula span.cm-matchhighlight { color: #FFFFFF; background-color: rgba(50, 89, 48, .7); font-weight: normal;}\n.cm-s-darcula span.cm-searching { color: #FFFFFF; background-color: rgba(61, 115, 59, .7); font-weight: normal;}\n\n.cm-s-darcula .CodeMirror-cursor { border-left: 1px solid #A9B7C6; }\n.cm-s-darcula .CodeMirror-activeline-background { background: #323232; }\n.cm-s-darcula .CodeMirror-gutters { background: #313335; border-right: 1px solid #313335; }\n.cm-s-darcula .CodeMirror-guttermarker { color: #FFEE80; }\n.cm-s-darcula .CodeMirror-guttermarker-subtle { color: #D0D0D0; }\n.cm-s-darcula .CodeMirrir-linenumber { color: #606366; }\n.cm-s-darcula .CodeMirror-matchingbracket { background-color: #3B514D; color: #FFEF28 !important; font-weight: bold; }\n\n.cm-s-darcula div.CodeMirror-selected { background: #214283; }\n\n.CodeMirror-hints.darcula {\n  font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;\n  color: #9C9E9E;\n  background-color: #3B3E3F !important;\n}\n\n.CodeMirror-hints.darcula .CodeMirror-hint-active {\n  background-color: #494D4E !important;\n  color: #9C9E9E !important;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/dracula.css",
    "content": "/*\n\n    Name:       dracula\n    Author:     Michael Kaminsky (http://github.com/mkaminsky11)\n\n    Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n  background-color: #282a36 !important;\n  color: #f8f8f2 !important;\n  border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/duotone-dark.css",
    "content": "/*\nName:   DuoTone-Dark\nAuthor: by Bram de Haan, adapted from DuoTone themes by Simurai (http://simurai.com/projects/2016/01/01/duotone-themes)\n\nCodeMirror template by Jan T. Sott (https://github.com/idleberg), adapted by Bram de Haan (https://github.com/atelierbram/)\n*/\n\n.cm-s-duotone-dark.CodeMirror { background: #2a2734; color: #6c6783; }\n.cm-s-duotone-dark div.CodeMirror-selected { background: #545167!important; }\n.cm-s-duotone-dark .CodeMirror-gutters { background: #2a2734; border-right: 0px; }\n.cm-s-duotone-dark .CodeMirror-linenumber { color: #545167; }\n\n/* begin cursor */\n.cm-s-duotone-dark .CodeMirror-cursor { border-left: 1px solid #ffad5c; /* border-left: 1px solid #ffad5c80; */ border-right: .5em solid #ffad5c; /* border-right: .5em solid #ffad5c80; */ opacity: .5; }\n.cm-s-duotone-dark .CodeMirror-activeline-background { background: #363342; /* background: #36334280;  */ opacity: .5;}\n.cm-s-duotone-dark .cm-fat-cursor .CodeMirror-cursor { background: #ffad5c; /* background: #ffad5c80; */ opacity: .5;}\n/* end cursor */\n\n.cm-s-duotone-dark span.cm-atom, .cm-s-duotone-dark span.cm-number, .cm-s-duotone-dark span.cm-keyword, .cm-s-duotone-dark span.cm-variable, .cm-s-duotone-dark span.cm-attribute, .cm-s-duotone-dark span.cm-quote, .cm-s-duotone-dark span.cm-hr, .cm-s-duotone-dark span.cm-link { color: #ffcc99; }\n\n.cm-s-duotone-dark span.cm-property { color: #9a86fd; }\n.cm-s-duotone-dark span.cm-punctuation, .cm-s-duotone-dark span.cm-unit, .cm-s-duotone-dark span.cm-negative { color: #e09142; }\n.cm-s-duotone-dark span.cm-string { color: #ffb870; }\n.cm-s-duotone-dark span.cm-operator { color: #ffad5c; }\n.cm-s-duotone-dark span.cm-positive { color: #6a51e6; }\n\n.cm-s-duotone-dark span.cm-variable-2, .cm-s-duotone-dark span.cm-variable-3, .cm-s-duotone-dark span.cm-type, .cm-s-duotone-dark span.cm-string-2, .cm-s-duotone-dark span.cm-url { color: #7a63ee; }\n.cm-s-duotone-dark span.cm-def, .cm-s-duotone-dark span.cm-tag, .cm-s-duotone-dark span.cm-builtin, .cm-s-duotone-dark span.cm-qualifier, .cm-s-duotone-dark span.cm-header, .cm-s-duotone-dark span.cm-em { color: #eeebff; }\n.cm-s-duotone-dark span.cm-bracket, .cm-s-duotone-dark span.cm-comment { color: #6c6783; }\n\n/* using #f00 red for errors, don't think any of the colorscheme variables will stand out enough, ... maybe by giving it a background-color ... */\n.cm-s-duotone-dark span.cm-error, .cm-s-duotone-dark span.cm-invalidchar { color: #f00; }\n\n.cm-s-duotone-dark span.cm-header { font-weight: normal; }\n.cm-s-duotone-dark .CodeMirror-matchingbracket { text-decoration: underline; color: #eeebff !important; } \n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/duotone-light.css",
    "content": "/*\nName:   DuoTone-Light\nAuthor: by Bram de Haan, adapted from DuoTone themes by Simurai (http://simurai.com/projects/2016/01/01/duotone-themes)\n\nCodeMirror template by Jan T. Sott (https://github.com/idleberg), adapted by Bram de Haan (https://github.com/atelierbram/)\n*/\n\n.cm-s-duotone-light.CodeMirror { background: #faf8f5; color: #b29762; }\n.cm-s-duotone-light div.CodeMirror-selected { background: #e3dcce !important; }\n.cm-s-duotone-light .CodeMirror-gutters { background: #faf8f5; border-right: 0px; }\n.cm-s-duotone-light .CodeMirror-linenumber { color: #cdc4b1; }\n\n/* begin cursor */\n.cm-s-duotone-light .CodeMirror-cursor { border-left: 1px solid #93abdc; /* border-left: 1px solid #93abdc80; */ border-right: .5em solid #93abdc; /* border-right: .5em solid #93abdc80; */ opacity: .5; }\n.cm-s-duotone-light .CodeMirror-activeline-background { background: #e3dcce;  /* background: #e3dcce80; */ opacity: .5; }\n.cm-s-duotone-light .cm-fat-cursor .CodeMirror-cursor { background: #93abdc; /* #93abdc80; */ opacity: .5; }\n/* end cursor */\n\n.cm-s-duotone-light span.cm-atom, .cm-s-duotone-light span.cm-number, .cm-s-duotone-light span.cm-keyword, .cm-s-duotone-light span.cm-variable, .cm-s-duotone-light span.cm-attribute, .cm-s-duotone-light span.cm-quote, .cm-s-duotone-light-light span.cm-hr, .cm-s-duotone-light-light span.cm-link { color: #063289; }\n\n.cm-s-duotone-light span.cm-property { color: #b29762; }\n.cm-s-duotone-light span.cm-punctuation, .cm-s-duotone-light span.cm-unit, .cm-s-duotone-light span.cm-negative { color: #063289; }\n.cm-s-duotone-light span.cm-string, .cm-s-duotone-light span.cm-operator { color: #1659df; }\n.cm-s-duotone-light span.cm-positive { color: #896724; }\n\n.cm-s-duotone-light span.cm-variable-2, .cm-s-duotone-light span.cm-variable-3, .cm-s-duotone-light span.cm-type, .cm-s-duotone-light span.cm-string-2, .cm-s-duotone-light span.cm-url { color: #896724; }\n.cm-s-duotone-light span.cm-def, .cm-s-duotone-light span.cm-tag, .cm-s-duotone-light span.cm-builtin, .cm-s-duotone-light span.cm-qualifier, .cm-s-duotone-light span.cm-header, .cm-s-duotone-light span.cm-em { color: #2d2006; }\n.cm-s-duotone-light span.cm-bracket, .cm-s-duotone-light span.cm-comment { color: #b6ad9a; }\n\n/* using #f00 red for errors, don't think any of the colorscheme variables will stand out enough, ... maybe by giving it a background-color ... */\n/* .cm-s-duotone-light span.cm-error { background: #896724; color: #728fcb; } */\n.cm-s-duotone-light span.cm-error, .cm-s-duotone-light span.cm-invalidchar { color: #f00; }\n\n.cm-s-duotone-light span.cm-header { font-weight: normal; }\n.cm-s-duotone-light .CodeMirror-matchingbracket { text-decoration: underline; color: #faf8f5 !important; }\n\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/eclipse.css",
    "content": ".cm-s-eclipse span.cm-meta { color: #FF1717; }\n.cm-s-eclipse span.cm-keyword { line-height: 1em; font-weight: bold; color: #7F0055; }\n.cm-s-eclipse span.cm-atom { color: #219; }\n.cm-s-eclipse span.cm-number { color: #164; }\n.cm-s-eclipse span.cm-def { color: #00f; }\n.cm-s-eclipse span.cm-variable { color: black; }\n.cm-s-eclipse span.cm-variable-2 { color: #0000C0; }\n.cm-s-eclipse span.cm-variable-3, .cm-s-eclipse span.cm-type { color: #0000C0; }\n.cm-s-eclipse span.cm-property { color: black; }\n.cm-s-eclipse span.cm-operator { color: black; }\n.cm-s-eclipse span.cm-comment { color: #3F7F5F; }\n.cm-s-eclipse span.cm-string { color: #2A00FF; }\n.cm-s-eclipse span.cm-string-2 { color: #f50; }\n.cm-s-eclipse span.cm-qualifier { color: #555; }\n.cm-s-eclipse span.cm-builtin { color: #30a; }\n.cm-s-eclipse span.cm-bracket { color: #cc7; }\n.cm-s-eclipse span.cm-tag { color: #170; }\n.cm-s-eclipse span.cm-attribute { color: #00c; }\n.cm-s-eclipse span.cm-link { color: #219; }\n.cm-s-eclipse span.cm-error { color: #f00; }\n\n.cm-s-eclipse .CodeMirror-activeline-background { background: #e8f2ff; }\n.cm-s-eclipse .CodeMirror-matchingbracket { outline:1px solid grey; color:black !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/elegant.css",
    "content": ".cm-s-elegant span.cm-number, .cm-s-elegant span.cm-string, .cm-s-elegant span.cm-atom { color: #762; }\n.cm-s-elegant span.cm-comment { color: #262; font-style: italic; line-height: 1em; }\n.cm-s-elegant span.cm-meta { color: #555; font-style: italic; line-height: 1em; }\n.cm-s-elegant span.cm-variable { color: black; }\n.cm-s-elegant span.cm-variable-2 { color: #b11; }\n.cm-s-elegant span.cm-qualifier { color: #555; }\n.cm-s-elegant span.cm-keyword { color: #730; }\n.cm-s-elegant span.cm-builtin { color: #30a; }\n.cm-s-elegant span.cm-link { color: #762; }\n.cm-s-elegant span.cm-error { background-color: #fdd; }\n\n.cm-s-elegant .CodeMirror-activeline-background { background: #e8f2ff; }\n.cm-s-elegant .CodeMirror-matchingbracket { outline:1px solid grey; color:black !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/erlang-dark.css",
    "content": ".cm-s-erlang-dark.CodeMirror { background: #002240; color: white; }\n.cm-s-erlang-dark div.CodeMirror-selected { background: #b36539; }\n.cm-s-erlang-dark .CodeMirror-line::selection, .cm-s-erlang-dark .CodeMirror-line > span::selection, .cm-s-erlang-dark .CodeMirror-line > span > span::selection { background: rgba(179, 101, 57, .99); }\n.cm-s-erlang-dark .CodeMirror-line::-moz-selection, .cm-s-erlang-dark .CodeMirror-line > span::-moz-selection, .cm-s-erlang-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(179, 101, 57, .99); }\n.cm-s-erlang-dark .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; }\n.cm-s-erlang-dark .CodeMirror-guttermarker { color: white; }\n.cm-s-erlang-dark .CodeMirror-guttermarker-subtle { color: #d0d0d0; }\n.cm-s-erlang-dark .CodeMirror-linenumber { color: #d0d0d0; }\n.cm-s-erlang-dark .CodeMirror-cursor { border-left: 1px solid white; }\n\n.cm-s-erlang-dark span.cm-quote      { color: #ccc; }\n.cm-s-erlang-dark span.cm-atom       { color: #f133f1; }\n.cm-s-erlang-dark span.cm-attribute  { color: #ff80e1; }\n.cm-s-erlang-dark span.cm-bracket    { color: #ff9d00; }\n.cm-s-erlang-dark span.cm-builtin    { color: #eaa; }\n.cm-s-erlang-dark span.cm-comment    { color: #77f; }\n.cm-s-erlang-dark span.cm-def        { color: #e7a; }\n.cm-s-erlang-dark span.cm-keyword    { color: #ffee80; }\n.cm-s-erlang-dark span.cm-meta       { color: #50fefe; }\n.cm-s-erlang-dark span.cm-number     { color: #ffd0d0; }\n.cm-s-erlang-dark span.cm-operator   { color: #d55; }\n.cm-s-erlang-dark span.cm-property   { color: #ccc; }\n.cm-s-erlang-dark span.cm-qualifier  { color: #ccc; }\n.cm-s-erlang-dark span.cm-special    { color: #ffbbbb; }\n.cm-s-erlang-dark span.cm-string     { color: #3ad900; }\n.cm-s-erlang-dark span.cm-string-2   { color: #ccc; }\n.cm-s-erlang-dark span.cm-tag        { color: #9effff; }\n.cm-s-erlang-dark span.cm-variable   { color: #50fe50; }\n.cm-s-erlang-dark span.cm-variable-2 { color: #e0e; }\n.cm-s-erlang-dark span.cm-variable-3, .cm-s-erlang-dark span.cm-type { color: #ccc; }\n.cm-s-erlang-dark span.cm-error      { color: #9d1e15; }\n\n.cm-s-erlang-dark .CodeMirror-activeline-background { background: #013461; }\n.cm-s-erlang-dark .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/gruvbox-dark.css",
    "content": "/*\n\n    Name:       gruvbox-dark\n    Author:     kRkk (https://github.com/krkk)\n\n    Original gruvbox color scheme by Pavel Pertsev (https://github.com/morhetz/gruvbox)\n\n*/\n\n.cm-s-gruvbox-dark.CodeMirror, .cm-s-gruvbox-dark .CodeMirror-gutters { background-color: #282828; color: #bdae93; }\n.cm-s-gruvbox-dark .CodeMirror-gutters {background: #282828; border-right: 0px;}\n.cm-s-gruvbox-dark .CodeMirror-linenumber {color: #7c6f64;}\n.cm-s-gruvbox-dark .CodeMirror-cursor { border-left: 1px solid #ebdbb2; }\n.cm-s-gruvbox-dark.cm-fat-cursor .CodeMirror-cursor { background-color: #8e8d8875 !important; }\n.cm-s-gruvbox-dark .cm-animate-fat-cursor { background-color: #8e8d8875 !important; }\n.cm-s-gruvbox-dark div.CodeMirror-selected { background: #928374; }\n.cm-s-gruvbox-dark span.cm-meta { color: #83a598; }\n\n.cm-s-gruvbox-dark span.cm-comment { color: #928374; }\n.cm-s-gruvbox-dark span.cm-number, span.cm-atom { color: #d3869b; }\n.cm-s-gruvbox-dark span.cm-keyword { color: #f84934; }\n\n.cm-s-gruvbox-dark span.cm-variable { color: #ebdbb2; }\n.cm-s-gruvbox-dark span.cm-variable-2 { color: #ebdbb2; }\n.cm-s-gruvbox-dark span.cm-variable-3, .cm-s-gruvbox-dark span.cm-type { color: #fabd2f; }\n.cm-s-gruvbox-dark span.cm-operator { color: #ebdbb2; }\n.cm-s-gruvbox-dark span.cm-callee { color: #ebdbb2; }\n.cm-s-gruvbox-dark span.cm-def { color: #ebdbb2; }\n.cm-s-gruvbox-dark span.cm-property { color: #ebdbb2; }\n.cm-s-gruvbox-dark span.cm-string { color: #b8bb26; }\n.cm-s-gruvbox-dark span.cm-string-2 { color: #8ec07c; }\n.cm-s-gruvbox-dark span.cm-qualifier { color: #8ec07c; }\n.cm-s-gruvbox-dark span.cm-attribute { color: #8ec07c; }\n\n.cm-s-gruvbox-dark .CodeMirror-activeline-background { background: #3c3836; }\n.cm-s-gruvbox-dark .CodeMirror-matchingbracket { background: #928374; color:#282828 !important; }\n\n.cm-s-gruvbox-dark span.cm-builtin { color: #fe8019; }\n.cm-s-gruvbox-dark span.cm-tag { color: #fe8019; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/hopscotch.css",
    "content": "/*\n\n    Name:       Hopscotch\n    Author:     Jan T. Sott\n\n    CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)\n    Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)\n\n*/\n\n.cm-s-hopscotch.CodeMirror {background: #322931; color: #d5d3d5;}\n.cm-s-hopscotch div.CodeMirror-selected {background: #433b42 !important;}\n.cm-s-hopscotch .CodeMirror-gutters {background: #322931; border-right: 0px;}\n.cm-s-hopscotch .CodeMirror-linenumber {color: #797379;}\n.cm-s-hopscotch .CodeMirror-cursor {border-left: 1px solid #989498 !important;}\n\n.cm-s-hopscotch span.cm-comment {color: #b33508;}\n.cm-s-hopscotch span.cm-atom {color: #c85e7c;}\n.cm-s-hopscotch span.cm-number {color: #c85e7c;}\n\n.cm-s-hopscotch span.cm-property, .cm-s-hopscotch span.cm-attribute {color: #8fc13e;}\n.cm-s-hopscotch span.cm-keyword {color: #dd464c;}\n.cm-s-hopscotch span.cm-string {color: #fdcc59;}\n\n.cm-s-hopscotch span.cm-variable {color: #8fc13e;}\n.cm-s-hopscotch span.cm-variable-2 {color: #1290bf;}\n.cm-s-hopscotch span.cm-def {color: #fd8b19;}\n.cm-s-hopscotch span.cm-error {background: #dd464c; color: #989498;}\n.cm-s-hopscotch span.cm-bracket {color: #d5d3d5;}\n.cm-s-hopscotch span.cm-tag {color: #dd464c;}\n.cm-s-hopscotch span.cm-link {color: #c85e7c;}\n\n.cm-s-hopscotch .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;}\n.cm-s-hopscotch .CodeMirror-activeline-background { background: #302020; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/icecoder.css",
    "content": "/*\nICEcoder default theme by Matt Pass, used in code editor available at https://icecoder.net\n*/\n\n.cm-s-icecoder { color: #666; background: #1d1d1b; }\n\n.cm-s-icecoder span.cm-keyword { color: #eee; font-weight:bold; }  /* off-white 1 */\n.cm-s-icecoder span.cm-atom { color: #e1c76e; }                    /* yellow */\n.cm-s-icecoder span.cm-number { color: #6cb5d9; }                  /* blue */\n.cm-s-icecoder span.cm-def { color: #b9ca4a; }                     /* green */\n\n.cm-s-icecoder span.cm-variable { color: #6cb5d9; }                /* blue */\n.cm-s-icecoder span.cm-variable-2 { color: #cc1e5c; }              /* pink */\n.cm-s-icecoder span.cm-variable-3, .cm-s-icecoder span.cm-type { color: #f9602c; } /* orange */\n\n.cm-s-icecoder span.cm-property { color: #eee; }                   /* off-white 1 */\n.cm-s-icecoder span.cm-operator { color: #9179bb; }                /* purple */\n.cm-s-icecoder span.cm-comment { color: #97a3aa; }                 /* grey-blue */\n\n.cm-s-icecoder span.cm-string { color: #b9ca4a; }                  /* green */\n.cm-s-icecoder span.cm-string-2 { color: #6cb5d9; }                /* blue */\n\n.cm-s-icecoder span.cm-meta { color: #555; }                       /* grey */\n\n.cm-s-icecoder span.cm-qualifier { color: #555; }                  /* grey */\n.cm-s-icecoder span.cm-builtin { color: #214e7b; }                 /* bright blue */\n.cm-s-icecoder span.cm-bracket { color: #cc7; }                    /* grey-yellow */\n\n.cm-s-icecoder span.cm-tag { color: #e8e8e8; }                     /* off-white 2 */\n.cm-s-icecoder span.cm-attribute { color: #099; }                  /* teal */\n\n.cm-s-icecoder span.cm-header { color: #6a0d6a; }                  /* purple-pink */\n.cm-s-icecoder span.cm-quote { color: #186718; }                   /* dark green */\n.cm-s-icecoder span.cm-hr { color: #888; }                         /* mid-grey */\n.cm-s-icecoder span.cm-link { color: #e1c76e; }                    /* yellow */\n.cm-s-icecoder span.cm-error { color: #d00; }                      /* red */\n\n.cm-s-icecoder .CodeMirror-cursor { border-left: 1px solid white; }\n.cm-s-icecoder div.CodeMirror-selected { color: #fff; background: #037; }\n.cm-s-icecoder .CodeMirror-gutters { background: #1d1d1b; min-width: 41px; border-right: 0; }\n.cm-s-icecoder .CodeMirror-linenumber { color: #555; cursor: default; }\n.cm-s-icecoder .CodeMirror-matchingbracket { color: #fff !important; background: #555 !important; }\n.cm-s-icecoder .CodeMirror-activeline-background { background: #000; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/idea.css",
    "content": "/**\n    Name:       IDEA default theme\n    From IntelliJ IDEA by JetBrains\n */\n\n.cm-s-idea span.cm-meta { color: #808000; }\n.cm-s-idea span.cm-number { color: #0000FF; }\n.cm-s-idea span.cm-keyword { line-height: 1em; font-weight: bold; color: #000080; }\n.cm-s-idea span.cm-atom { font-weight: bold; color: #000080; }\n.cm-s-idea span.cm-def { color: #000000; }\n.cm-s-idea span.cm-variable { color: black; }\n.cm-s-idea span.cm-variable-2 { color: black; }\n.cm-s-idea span.cm-variable-3, .cm-s-idea span.cm-type { color: black; }\n.cm-s-idea span.cm-property { color: black; }\n.cm-s-idea span.cm-operator { color: black; }\n.cm-s-idea span.cm-comment { color: #808080; }\n.cm-s-idea span.cm-string { color: #008000; }\n.cm-s-idea span.cm-string-2 { color: #008000; }\n.cm-s-idea span.cm-qualifier { color: #555; }\n.cm-s-idea span.cm-error { color: #FF0000; }\n.cm-s-idea span.cm-attribute { color: #0000FF; }\n.cm-s-idea span.cm-tag { color: #000080; }\n.cm-s-idea span.cm-link { color: #0000FF; }\n.cm-s-idea .CodeMirror-activeline-background { background: #FFFAE3; }\n\n.cm-s-idea span.cm-builtin { color: #30a; }\n.cm-s-idea span.cm-bracket { color: #cc7; }\n.cm-s-idea  { font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;}\n\n\n.cm-s-idea .CodeMirror-matchingbracket { outline:1px solid grey; color:black !important; }\n\n.CodeMirror-hints.idea {\n  font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;\n  color: #616569;\n  background-color: #ebf3fd !important;\n}\n\n.CodeMirror-hints.idea .CodeMirror-hint-active {\n  background-color: #a2b8c9 !important;\n  color: #5c6065 !important;\n}"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/isotope.css",
    "content": "/*\n\n    Name:       Isotope\n    Author:     David Desandro / Jan T. Sott\n\n    CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)\n    Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)\n\n*/\n\n.cm-s-isotope.CodeMirror {background: #000000; color: #e0e0e0;}\n.cm-s-isotope div.CodeMirror-selected {background: #404040 !important;}\n.cm-s-isotope .CodeMirror-gutters {background: #000000; border-right: 0px;}\n.cm-s-isotope .CodeMirror-linenumber {color: #808080;}\n.cm-s-isotope .CodeMirror-cursor {border-left: 1px solid #c0c0c0 !important;}\n\n.cm-s-isotope span.cm-comment {color: #3300ff;}\n.cm-s-isotope span.cm-atom {color: #cc00ff;}\n.cm-s-isotope span.cm-number {color: #cc00ff;}\n\n.cm-s-isotope span.cm-property, .cm-s-isotope span.cm-attribute {color: #33ff00;}\n.cm-s-isotope span.cm-keyword {color: #ff0000;}\n.cm-s-isotope span.cm-string {color: #ff0099;}\n\n.cm-s-isotope span.cm-variable {color: #33ff00;}\n.cm-s-isotope span.cm-variable-2 {color: #0066ff;}\n.cm-s-isotope span.cm-def {color: #ff9900;}\n.cm-s-isotope span.cm-error {background: #ff0000; color: #c0c0c0;}\n.cm-s-isotope span.cm-bracket {color: #e0e0e0;}\n.cm-s-isotope span.cm-tag {color: #ff0000;}\n.cm-s-isotope span.cm-link {color: #cc00ff;}\n\n.cm-s-isotope .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;}\n.cm-s-isotope .CodeMirror-activeline-background { background: #202020; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/juejin.css",
    "content": ".cm-s-juejin.CodeMirror {\n  background: #f8f9fa;\n}\n.cm-s-juejin .cm-header,\n.cm-s-juejin .cm-def {\n  color: #1ba2f0;\n}\n.cm-s-juejin .cm-comment {\n  color: #009e9d;\n}\n.cm-s-juejin .cm-quote,\n.cm-s-juejin .cm-link,\n.cm-s-juejin .cm-strong,\n.cm-s-juejin .cm-attribute {\n  color: #fd7741;\n}\n.cm-s-juejin .cm-url,\n.cm-s-juejin .cm-keyword,\n.cm-s-juejin .cm-builtin {\n  color: #bb51b8;\n}\n.cm-s-juejin .cm-hr {\n  color: #909090;\n}\n.cm-s-juejin .cm-tag {\n  color: #107000;\n}\n.cm-s-juejin .cm-variable-2 {\n  color: #0050a0;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/lesser-dark.css",
    "content": "/*\nhttp://lesscss.org/ dark theme\nPorted to CodeMirror by Peter Kroon\n*/\n.cm-s-lesser-dark {\n  line-height: 1.3em;\n}\n.cm-s-lesser-dark.CodeMirror { background: #262626; color: #EBEFE7; text-shadow: 0 -1px 1px #262626; }\n.cm-s-lesser-dark div.CodeMirror-selected { background: #45443B; } /* 33322B*/\n.cm-s-lesser-dark .CodeMirror-line::selection, .cm-s-lesser-dark .CodeMirror-line > span::selection, .cm-s-lesser-dark .CodeMirror-line > span > span::selection { background: rgba(69, 68, 59, .99); }\n.cm-s-lesser-dark .CodeMirror-line::-moz-selection, .cm-s-lesser-dark .CodeMirror-line > span::-moz-selection, .cm-s-lesser-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(69, 68, 59, .99); }\n.cm-s-lesser-dark .CodeMirror-cursor { border-left: 1px solid white; }\n.cm-s-lesser-dark pre { padding: 0 8px; }/*editable code holder*/\n\n.cm-s-lesser-dark.CodeMirror span.CodeMirror-matchingbracket { color: #7EFC7E; }/*65FC65*/\n\n.cm-s-lesser-dark .CodeMirror-gutters { background: #262626; border-right:1px solid #aaa; }\n.cm-s-lesser-dark .CodeMirror-guttermarker { color: #599eff; }\n.cm-s-lesser-dark .CodeMirror-guttermarker-subtle { color: #777; }\n.cm-s-lesser-dark .CodeMirror-linenumber { color: #777; }\n\n.cm-s-lesser-dark span.cm-header { color: #a0a; }\n.cm-s-lesser-dark span.cm-quote { color: #090; }\n.cm-s-lesser-dark span.cm-keyword { color: #599eff; }\n.cm-s-lesser-dark span.cm-atom { color: #C2B470; }\n.cm-s-lesser-dark span.cm-number { color: #B35E4D; }\n.cm-s-lesser-dark span.cm-def { color: white; }\n.cm-s-lesser-dark span.cm-variable { color:#D9BF8C; }\n.cm-s-lesser-dark span.cm-variable-2 { color: #669199; }\n.cm-s-lesser-dark span.cm-variable-3, .cm-s-lesser-dark span.cm-type { color: white; }\n.cm-s-lesser-dark span.cm-property { color: #92A75C; }\n.cm-s-lesser-dark span.cm-operator { color: #92A75C; }\n.cm-s-lesser-dark span.cm-comment { color: #666; }\n.cm-s-lesser-dark span.cm-string { color: #BCD279; }\n.cm-s-lesser-dark span.cm-string-2 { color: #f50; }\n.cm-s-lesser-dark span.cm-meta { color: #738C73; }\n.cm-s-lesser-dark span.cm-qualifier { color: #555; }\n.cm-s-lesser-dark span.cm-builtin { color: #ff9e59; }\n.cm-s-lesser-dark span.cm-bracket { color: #EBEFE7; }\n.cm-s-lesser-dark span.cm-tag { color: #669199; }\n.cm-s-lesser-dark span.cm-attribute { color: #81a4d5; }\n.cm-s-lesser-dark span.cm-hr { color: #999; }\n.cm-s-lesser-dark span.cm-link { color: #7070E6; }\n.cm-s-lesser-dark span.cm-error { color: #9d1e15; }\n\n.cm-s-lesser-dark .CodeMirror-activeline-background { background: #3C3A3A; }\n.cm-s-lesser-dark .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/liquibyte.css",
    "content": ".cm-s-liquibyte.CodeMirror {\n\tbackground-color: #000;\n\tcolor: #fff;\n\tline-height: 1.2em;\n\tfont-size: 1em;\n}\n.cm-s-liquibyte .CodeMirror-focused .cm-matchhighlight {\n\ttext-decoration: underline;\n\ttext-decoration-color: #0f0;\n\ttext-decoration-style: wavy;\n}\n.cm-s-liquibyte .cm-trailingspace {\n\ttext-decoration: line-through;\n\ttext-decoration-color: #f00;\n\ttext-decoration-style: dotted;\n}\n.cm-s-liquibyte .cm-tab {\n\ttext-decoration: line-through;\n\ttext-decoration-color: #404040;\n\ttext-decoration-style: dotted;\n}\n.cm-s-liquibyte .CodeMirror-gutters { background-color: #262626; border-right: 1px solid #505050; padding-right: 0.8em; }\n.cm-s-liquibyte .CodeMirror-gutter-elt div { font-size: 1.2em; }\n.cm-s-liquibyte .CodeMirror-guttermarker {  }\n.cm-s-liquibyte .CodeMirror-guttermarker-subtle {  }\n.cm-s-liquibyte .CodeMirror-linenumber { color: #606060; padding-left: 0; }\n.cm-s-liquibyte .CodeMirror-cursor { border-left: 1px solid #eee; }\n\n.cm-s-liquibyte span.cm-comment     { color: #008000; }\n.cm-s-liquibyte span.cm-def         { color: #ffaf40; font-weight: bold; }\n.cm-s-liquibyte span.cm-keyword     { color: #c080ff; font-weight: bold; }\n.cm-s-liquibyte span.cm-builtin     { color: #ffaf40; font-weight: bold; }\n.cm-s-liquibyte span.cm-variable    { color: #5967ff; font-weight: bold; }\n.cm-s-liquibyte span.cm-string      { color: #ff8000; }\n.cm-s-liquibyte span.cm-number      { color: #0f0; font-weight: bold; }\n.cm-s-liquibyte span.cm-atom        { color: #bf3030; font-weight: bold; }\n\n.cm-s-liquibyte span.cm-variable-2  { color: #007f7f; font-weight: bold; }\n.cm-s-liquibyte span.cm-variable-3, .cm-s-liquibyte span.cm-type { color: #c080ff; font-weight: bold; }\n.cm-s-liquibyte span.cm-property    { color: #999; font-weight: bold; }\n.cm-s-liquibyte span.cm-operator    { color: #fff; }\n\n.cm-s-liquibyte span.cm-meta        { color: #0f0; }\n.cm-s-liquibyte span.cm-qualifier   { color: #fff700; font-weight: bold; }\n.cm-s-liquibyte span.cm-bracket     { color: #cc7; }\n.cm-s-liquibyte span.cm-tag         { color: #ff0; font-weight: bold; }\n.cm-s-liquibyte span.cm-attribute   { color: #c080ff; font-weight: bold; }\n.cm-s-liquibyte span.cm-error       { color: #f00; }\n\n.cm-s-liquibyte div.CodeMirror-selected { background-color: rgba(255, 0, 0, 0.25); }\n\n.cm-s-liquibyte span.cm-compilation { background-color: rgba(255, 255, 255, 0.12); }\n\n.cm-s-liquibyte .CodeMirror-activeline-background { background-color: rgba(0, 255, 0, 0.15); }\n\n/* Default styles for common addons */\n.cm-s-liquibyte .CodeMirror span.CodeMirror-matchingbracket { color: #0f0; font-weight: bold; }\n.cm-s-liquibyte .CodeMirror span.CodeMirror-nonmatchingbracket { color: #f00; font-weight: bold; }\n.CodeMirror-matchingtag { background-color: rgba(150, 255, 0, .3); }\n/* Scrollbars */\n/* Simple */\n.cm-s-liquibyte div.CodeMirror-simplescroll-horizontal div:hover, .cm-s-liquibyte div.CodeMirror-simplescroll-vertical div:hover {\n\tbackground-color: rgba(80, 80, 80, .7);\n}\n.cm-s-liquibyte div.CodeMirror-simplescroll-horizontal div, .cm-s-liquibyte div.CodeMirror-simplescroll-vertical div {\n\tbackground-color: rgba(80, 80, 80, .3);\n\tborder: 1px solid #404040;\n\tborder-radius: 5px;\n}\n.cm-s-liquibyte div.CodeMirror-simplescroll-vertical div {\n\tborder-top: 1px solid #404040;\n\tborder-bottom: 1px solid #404040;\n}\n.cm-s-liquibyte div.CodeMirror-simplescroll-horizontal div {\n\tborder-left: 1px solid #404040;\n\tborder-right: 1px solid #404040;\n}\n.cm-s-liquibyte div.CodeMirror-simplescroll-vertical {\n\tbackground-color: #262626;\n}\n.cm-s-liquibyte div.CodeMirror-simplescroll-horizontal {\n\tbackground-color: #262626;\n\tborder-top: 1px solid #404040;\n}\n/* Overlay */\n.cm-s-liquibyte div.CodeMirror-overlayscroll-horizontal div, div.CodeMirror-overlayscroll-vertical div {\n\tbackground-color: #404040;\n\tborder-radius: 5px;\n}\n.cm-s-liquibyte div.CodeMirror-overlayscroll-vertical div {\n\tborder: 1px solid #404040;\n}\n.cm-s-liquibyte div.CodeMirror-overlayscroll-horizontal div {\n\tborder: 1px solid #404040;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/lucario.css",
    "content": "/*\n  Name:       lucario\n  Author:     Raphael Amorim\n\n  Original Lucario color scheme (https://github.com/raphamorim/lucario)\n*/\n\n.cm-s-lucario.CodeMirror, .cm-s-lucario .CodeMirror-gutters {\n  background-color: #2b3e50 !important;\n  color: #f8f8f2 !important;\n  border: none;\n}\n.cm-s-lucario .CodeMirror-gutters { color: #2b3e50; }\n.cm-s-lucario .CodeMirror-cursor { border-left: solid thin #E6C845; }\n.cm-s-lucario .CodeMirror-linenumber { color: #f8f8f2; }\n.cm-s-lucario .CodeMirror-selected { background: #243443; }\n.cm-s-lucario .CodeMirror-line::selection, .cm-s-lucario .CodeMirror-line > span::selection, .cm-s-lucario .CodeMirror-line > span > span::selection { background: #243443; }\n.cm-s-lucario .CodeMirror-line::-moz-selection, .cm-s-lucario .CodeMirror-line > span::-moz-selection, .cm-s-lucario .CodeMirror-line > span > span::-moz-selection { background: #243443; }\n.cm-s-lucario span.cm-comment { color: #5c98cd; }\n.cm-s-lucario span.cm-string, .cm-s-lucario span.cm-string-2 { color: #E6DB74; }\n.cm-s-lucario span.cm-number { color: #ca94ff; }\n.cm-s-lucario span.cm-variable { color: #f8f8f2; }\n.cm-s-lucario span.cm-variable-2 { color: #f8f8f2; }\n.cm-s-lucario span.cm-def { color: #72C05D; }\n.cm-s-lucario span.cm-operator { color: #66D9EF; }\n.cm-s-lucario span.cm-keyword { color: #ff6541; }\n.cm-s-lucario span.cm-atom { color: #bd93f9; }\n.cm-s-lucario span.cm-meta { color: #f8f8f2; }\n.cm-s-lucario span.cm-tag { color: #ff6541; }\n.cm-s-lucario span.cm-attribute { color: #66D9EF; }\n.cm-s-lucario span.cm-qualifier { color: #72C05D; }\n.cm-s-lucario span.cm-property { color: #f8f8f2; }\n.cm-s-lucario span.cm-builtin { color: #72C05D; }\n.cm-s-lucario span.cm-variable-3, .cm-s-lucario span.cm-type { color: #ffb86c; }\n\n.cm-s-lucario .CodeMirror-activeline-background { background: #243443; }\n.cm-s-lucario .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/material-darker.css",
    "content": "/*\n  Name:       material\n  Author:     Mattia Astorino (http://github.com/equinusocio)\n  Website:    https://material-theme.site/\n*/\n\n.cm-s-material-darker.CodeMirror {\n  background-color: #212121;\n  color: #EEFFFF;\n}\n\n.cm-s-material-darker .CodeMirror-gutters {\n  background: #212121;\n  color: #545454;\n  border: none;\n}\n\n.cm-s-material-darker .CodeMirror-guttermarker,\n.cm-s-material-darker .CodeMirror-guttermarker-subtle,\n.cm-s-material-darker .CodeMirror-linenumber {\n  color: #545454;\n}\n\n.cm-s-material-darker .CodeMirror-cursor {\n  border-left: 1px solid #FFCC00;\n}\n\n.cm-s-material-darker div.CodeMirror-selected {\n  background: rgba(97, 97, 97, 0.2);\n}\n\n.cm-s-material-darker.CodeMirror-focused div.CodeMirror-selected {\n  background: rgba(97, 97, 97, 0.2);\n}\n\n.cm-s-material-darker .CodeMirror-line::selection,\n.cm-s-material-darker .CodeMirror-line>span::selection,\n.cm-s-material-darker .CodeMirror-line>span>span::selection {\n  background: rgba(128, 203, 196, 0.2);\n}\n\n.cm-s-material-darker .CodeMirror-line::-moz-selection,\n.cm-s-material-darker .CodeMirror-line>span::-moz-selection,\n.cm-s-material-darker .CodeMirror-line>span>span::-moz-selection {\n  background: rgba(128, 203, 196, 0.2);\n}\n\n.cm-s-material-darker .CodeMirror-activeline-background {\n  background: rgba(0, 0, 0, 0.5);\n}\n\n.cm-s-material-darker .cm-keyword {\n  color: #C792EA;\n}\n\n.cm-s-material-darker .cm-operator {\n  color: #89DDFF;\n}\n\n.cm-s-material-darker .cm-variable-2 {\n  color: #EEFFFF;\n}\n\n.cm-s-material-darker .cm-variable-3,\n.cm-s-material-darker .cm-type {\n  color: #f07178;\n}\n\n.cm-s-material-darker .cm-builtin {\n  color: #FFCB6B;\n}\n\n.cm-s-material-darker .cm-atom {\n  color: #F78C6C;\n}\n\n.cm-s-material-darker .cm-number {\n  color: #FF5370;\n}\n\n.cm-s-material-darker .cm-def {\n  color: #82AAFF;\n}\n\n.cm-s-material-darker .cm-string {\n  color: #C3E88D;\n}\n\n.cm-s-material-darker .cm-string-2 {\n  color: #f07178;\n}\n\n.cm-s-material-darker .cm-comment {\n  color: #545454;\n}\n\n.cm-s-material-darker .cm-variable {\n  color: #f07178;\n}\n\n.cm-s-material-darker .cm-tag {\n  color: #FF5370;\n}\n\n.cm-s-material-darker .cm-meta {\n  color: #FFCB6B;\n}\n\n.cm-s-material-darker .cm-attribute {\n  color: #C792EA;\n}\n\n.cm-s-material-darker .cm-property {\n  color: #C792EA;\n}\n\n.cm-s-material-darker .cm-qualifier {\n  color: #DECB6B;\n}\n\n.cm-s-material-darker .cm-variable-3,\n.cm-s-material-darker .cm-type {\n  color: #DECB6B;\n}\n\n\n.cm-s-material-darker .cm-error {\n  color: rgba(255, 255, 255, 1.0);\n  background-color: #FF5370;\n}\n\n.cm-s-material-darker .CodeMirror-matchingbracket {\n  text-decoration: underline;\n  color: white !important;\n}"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/material-ocean.css",
    "content": "/*\n  Name:       material\n  Author:     Mattia Astorino (http://github.com/equinusocio)\n  Website:    https://material-theme.site/\n*/\n\n.cm-s-material-ocean.CodeMirror {\n  background-color: #0F111A;\n  color: #8F93A2;\n}\n\n.cm-s-material-ocean .CodeMirror-gutters {\n  background: #0F111A;\n  color: #464B5D;\n  border: none;\n}\n\n.cm-s-material-ocean .CodeMirror-guttermarker,\n.cm-s-material-ocean .CodeMirror-guttermarker-subtle,\n.cm-s-material-ocean .CodeMirror-linenumber {\n  color: #464B5D;\n}\n\n.cm-s-material-ocean .CodeMirror-cursor {\n  border-left: 1px solid #FFCC00;\n}\n.cm-s-material-ocean.cm-fat-cursor .CodeMirror-cursor {\n  background-color: #a2a8a175 !important;\n}\n.cm-s-material-ocean .cm-animate-fat-cursor {\n  background-color: #a2a8a175 !important;\n}\n\n.cm-s-material-ocean div.CodeMirror-selected {\n  background: rgba(113, 124, 180, 0.2);\n}\n\n.cm-s-material-ocean.CodeMirror-focused div.CodeMirror-selected {\n  background: rgba(113, 124, 180, 0.2);\n}\n\n.cm-s-material-ocean .CodeMirror-line::selection,\n.cm-s-material-ocean .CodeMirror-line>span::selection,\n.cm-s-material-ocean .CodeMirror-line>span>span::selection {\n  background: rgba(128, 203, 196, 0.2);\n}\n\n.cm-s-material-ocean .CodeMirror-line::-moz-selection,\n.cm-s-material-ocean .CodeMirror-line>span::-moz-selection,\n.cm-s-material-ocean .CodeMirror-line>span>span::-moz-selection {\n  background: rgba(128, 203, 196, 0.2);\n}\n\n.cm-s-material-ocean .CodeMirror-activeline-background {\n  background: rgba(0, 0, 0, 0.5);\n}\n\n.cm-s-material-ocean .cm-keyword {\n  color: #C792EA;\n}\n\n.cm-s-material-ocean .cm-operator {\n  color: #89DDFF;\n}\n\n.cm-s-material-ocean .cm-variable-2 {\n  color: #EEFFFF;\n}\n\n.cm-s-material-ocean .cm-variable-3,\n.cm-s-material-ocean .cm-type {\n  color: #f07178;\n}\n\n.cm-s-material-ocean .cm-builtin {\n  color: #FFCB6B;\n}\n\n.cm-s-material-ocean .cm-atom {\n  color: #F78C6C;\n}\n\n.cm-s-material-ocean .cm-number {\n  color: #FF5370;\n}\n\n.cm-s-material-ocean .cm-def {\n  color: #82AAFF;\n}\n\n.cm-s-material-ocean .cm-string {\n  color: #C3E88D;\n}\n\n.cm-s-material-ocean .cm-string-2 {\n  color: #f07178;\n}\n\n.cm-s-material-ocean .cm-comment {\n  color: #464B5D;\n}\n\n.cm-s-material-ocean .cm-variable {\n  color: #f07178;\n}\n\n.cm-s-material-ocean .cm-tag {\n  color: #FF5370;\n}\n\n.cm-s-material-ocean .cm-meta {\n  color: #FFCB6B;\n}\n\n.cm-s-material-ocean .cm-attribute {\n  color: #C792EA;\n}\n\n.cm-s-material-ocean .cm-property {\n  color: #C792EA;\n}\n\n.cm-s-material-ocean .cm-qualifier {\n  color: #DECB6B;\n}\n\n.cm-s-material-ocean .cm-variable-3,\n.cm-s-material-ocean .cm-type {\n  color: #DECB6B;\n}\n\n\n.cm-s-material-ocean .cm-error {\n  color: rgba(255, 255, 255, 1.0);\n  background-color: #FF5370;\n}\n\n.cm-s-material-ocean .CodeMirror-matchingbracket {\n  text-decoration: underline;\n  color: white !important;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/material-palenight.css",
    "content": "/*\n  Name:       material\n  Author:     Mattia Astorino (http://github.com/equinusocio)\n  Website:    https://material-theme.site/\n*/\n\n.cm-s-material-palenight.CodeMirror {\n  background-color: #292D3E;\n  color: #A6ACCD;\n}\n\n.cm-s-material-palenight .CodeMirror-gutters {\n  background: #292D3E;\n  color: #676E95;\n  border: none;\n}\n\n.cm-s-material-palenight .CodeMirror-guttermarker,\n.cm-s-material-palenight .CodeMirror-guttermarker-subtle,\n.cm-s-material-palenight .CodeMirror-linenumber {\n  color: #676E95;\n}\n\n.cm-s-material-palenight .CodeMirror-cursor {\n  border-left: 1px solid #FFCC00;\n}\n.cm-s-material-palenight.cm-fat-cursor .CodeMirror-cursor {\n  background-color: #607c8b80 !important;\n}\n.cm-s-material-palenight .cm-animate-fat-cursor {\n  background-color: #607c8b80 !important;\n}\n\n.cm-s-material-palenight div.CodeMirror-selected {\n  background: rgba(113, 124, 180, 0.2);\n}\n\n.cm-s-material-palenight.CodeMirror-focused div.CodeMirror-selected {\n  background: rgba(113, 124, 180, 0.2);\n}\n\n.cm-s-material-palenight .CodeMirror-line::selection,\n.cm-s-material-palenight .CodeMirror-line>span::selection,\n.cm-s-material-palenight .CodeMirror-line>span>span::selection {\n  background: rgba(128, 203, 196, 0.2);\n}\n\n.cm-s-material-palenight .CodeMirror-line::-moz-selection,\n.cm-s-material-palenight .CodeMirror-line>span::-moz-selection,\n.cm-s-material-palenight .CodeMirror-line>span>span::-moz-selection {\n  background: rgba(128, 203, 196, 0.2);\n}\n\n.cm-s-material-palenight .CodeMirror-activeline-background {\n  background: rgba(0, 0, 0, 0.5);\n}\n\n.cm-s-material-palenight .cm-keyword {\n  color: #C792EA;\n}\n\n.cm-s-material-palenight .cm-operator {\n  color: #89DDFF;\n}\n\n.cm-s-material-palenight .cm-variable-2 {\n  color: #EEFFFF;\n}\n\n.cm-s-material-palenight .cm-variable-3,\n.cm-s-material-palenight .cm-type {\n  color: #f07178;\n}\n\n.cm-s-material-palenight .cm-builtin {\n  color: #FFCB6B;\n}\n\n.cm-s-material-palenight .cm-atom {\n  color: #F78C6C;\n}\n\n.cm-s-material-palenight .cm-number {\n  color: #FF5370;\n}\n\n.cm-s-material-palenight .cm-def {\n  color: #82AAFF;\n}\n\n.cm-s-material-palenight .cm-string {\n  color: #C3E88D;\n}\n\n.cm-s-material-palenight .cm-string-2 {\n  color: #f07178;\n}\n\n.cm-s-material-palenight .cm-comment {\n  color: #676E95;\n}\n\n.cm-s-material-palenight .cm-variable {\n  color: #f07178;\n}\n\n.cm-s-material-palenight .cm-tag {\n  color: #FF5370;\n}\n\n.cm-s-material-palenight .cm-meta {\n  color: #FFCB6B;\n}\n\n.cm-s-material-palenight .cm-attribute {\n  color: #C792EA;\n}\n\n.cm-s-material-palenight .cm-property {\n  color: #C792EA;\n}\n\n.cm-s-material-palenight .cm-qualifier {\n  color: #DECB6B;\n}\n\n.cm-s-material-palenight .cm-variable-3,\n.cm-s-material-palenight .cm-type {\n  color: #DECB6B;\n}\n\n\n.cm-s-material-palenight .cm-error {\n  color: rgba(255, 255, 255, 1.0);\n  background-color: #FF5370;\n}\n\n.cm-s-material-palenight .CodeMirror-matchingbracket {\n  text-decoration: underline;\n  color: white !important;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/material.css",
    "content": "/*\n  Name:       material\n  Author:     Mattia Astorino (http://github.com/equinusocio)\n  Website:    https://material-theme.site/\n*/\n\n.cm-s-material.CodeMirror {\n  background-color: #263238;\n  color: #EEFFFF;\n}\n\n.cm-s-material .CodeMirror-gutters {\n  background: #263238;\n  color: #546E7A;\n  border: none;\n}\n\n.cm-s-material .CodeMirror-guttermarker,\n.cm-s-material .CodeMirror-guttermarker-subtle,\n.cm-s-material .CodeMirror-linenumber {\n  color: #546E7A;\n}\n\n.cm-s-material .CodeMirror-cursor {\n  border-left: 1px solid #FFCC00;\n}\n.cm-s-material.cm-fat-cursor .CodeMirror-cursor {\n  background-color: #5d6d5c80 !important;\n}\n.cm-s-material .cm-animate-fat-cursor {\n  background-color: #5d6d5c80 !important;\n}\n\n.cm-s-material div.CodeMirror-selected {\n  background: rgba(128, 203, 196, 0.2);\n}\n\n.cm-s-material.CodeMirror-focused div.CodeMirror-selected {\n  background: rgba(128, 203, 196, 0.2);\n}\n\n.cm-s-material .CodeMirror-line::selection,\n.cm-s-material .CodeMirror-line>span::selection,\n.cm-s-material .CodeMirror-line>span>span::selection {\n  background: rgba(128, 203, 196, 0.2);\n}\n\n.cm-s-material .CodeMirror-line::-moz-selection,\n.cm-s-material .CodeMirror-line>span::-moz-selection,\n.cm-s-material .CodeMirror-line>span>span::-moz-selection {\n  background: rgba(128, 203, 196, 0.2);\n}\n\n.cm-s-material .CodeMirror-activeline-background {\n  background: rgba(0, 0, 0, 0.5);\n}\n\n.cm-s-material .cm-keyword {\n  color: #C792EA;\n}\n\n.cm-s-material .cm-operator {\n  color: #89DDFF;\n}\n\n.cm-s-material .cm-variable-2 {\n  color: #EEFFFF;\n}\n\n.cm-s-material .cm-variable-3,\n.cm-s-material .cm-type {\n  color: #f07178;\n}\n\n.cm-s-material .cm-builtin {\n  color: #FFCB6B;\n}\n\n.cm-s-material .cm-atom {\n  color: #F78C6C;\n}\n\n.cm-s-material .cm-number {\n  color: #FF5370;\n}\n\n.cm-s-material .cm-def {\n  color: #82AAFF;\n}\n\n.cm-s-material .cm-string {\n  color: #C3E88D;\n}\n\n.cm-s-material .cm-string-2 {\n  color: #f07178;\n}\n\n.cm-s-material .cm-comment {\n  color: #546E7A;\n}\n\n.cm-s-material .cm-variable {\n  color: #f07178;\n}\n\n.cm-s-material .cm-tag {\n  color: #FF5370;\n}\n\n.cm-s-material .cm-meta {\n  color: #FFCB6B;\n}\n\n.cm-s-material .cm-attribute {\n  color: #C792EA;\n}\n\n.cm-s-material .cm-property {\n  color: #C792EA;\n}\n\n.cm-s-material .cm-qualifier {\n  color: #DECB6B;\n}\n\n.cm-s-material .cm-variable-3,\n.cm-s-material .cm-type {\n  color: #DECB6B;\n}\n\n\n.cm-s-material .cm-error {\n  color: rgba(255, 255, 255, 1.0);\n  background-color: #FF5370;\n}\n\n.cm-s-material .CodeMirror-matchingbracket {\n  text-decoration: underline;\n  color: white !important;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/mbo.css",
    "content": "/****************************************************************/\n/*   Based on mbonaci's Brackets mbo theme                      */\n/*   https://github.com/mbonaci/global/blob/master/Mbo.tmTheme  */\n/*   Create your own: http://tmtheme-editor.herokuapp.com       */\n/****************************************************************/\n\n.cm-s-mbo.CodeMirror { background: #2c2c2c; color: #ffffec; }\n.cm-s-mbo div.CodeMirror-selected { background: #716C62; }\n.cm-s-mbo .CodeMirror-line::selection, .cm-s-mbo .CodeMirror-line > span::selection, .cm-s-mbo .CodeMirror-line > span > span::selection { background: rgba(113, 108, 98, .99); }\n.cm-s-mbo .CodeMirror-line::-moz-selection, .cm-s-mbo .CodeMirror-line > span::-moz-selection, .cm-s-mbo .CodeMirror-line > span > span::-moz-selection { background: rgba(113, 108, 98, .99); }\n.cm-s-mbo .CodeMirror-gutters { background: #4e4e4e; border-right: 0px; }\n.cm-s-mbo .CodeMirror-guttermarker { color: white; }\n.cm-s-mbo .CodeMirror-guttermarker-subtle { color: grey; }\n.cm-s-mbo .CodeMirror-linenumber { color: #dadada; }\n.cm-s-mbo .CodeMirror-cursor { border-left: 1px solid #ffffec; }\n\n.cm-s-mbo span.cm-comment { color: #95958a; }\n.cm-s-mbo span.cm-atom { color: #00a8c6; }\n.cm-s-mbo span.cm-number { color: #00a8c6; }\n\n.cm-s-mbo span.cm-property, .cm-s-mbo span.cm-attribute { color: #9ddfe9; }\n.cm-s-mbo span.cm-keyword { color: #ffb928; }\n.cm-s-mbo span.cm-string { color: #ffcf6c; }\n.cm-s-mbo span.cm-string.cm-property { color: #ffffec; }\n\n.cm-s-mbo span.cm-variable { color: #ffffec; }\n.cm-s-mbo span.cm-variable-2 { color: #00a8c6; }\n.cm-s-mbo span.cm-def { color: #ffffec; }\n.cm-s-mbo span.cm-bracket { color: #fffffc; font-weight: bold; }\n.cm-s-mbo span.cm-tag { color: #9ddfe9; }\n.cm-s-mbo span.cm-link { color: #f54b07; }\n.cm-s-mbo span.cm-error { border-bottom: #636363; color: #ffffec; }\n.cm-s-mbo span.cm-qualifier { color: #ffffec; }\n\n.cm-s-mbo .CodeMirror-activeline-background { background: #494b41; }\n.cm-s-mbo .CodeMirror-matchingbracket { color: #ffb928 !important; }\n.cm-s-mbo .CodeMirror-matchingtag { background: rgba(255, 255, 255, .37); }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/mdn-like.css",
    "content": "/*\n  MDN-LIKE Theme - Mozilla\n  Ported to CodeMirror by Peter Kroon <plakroon@gmail.com>\n  Report bugs/issues here: https://github.com/codemirror/CodeMirror/issues\n  GitHub: @peterkroon\n\n  The mdn-like theme is inspired on the displayed code examples at: https://developer.mozilla.org/en-US/docs/Web/CSS/animation\n\n*/\n.cm-s-mdn-like.CodeMirror { color: #999; background-color: #fff; }\n.cm-s-mdn-like div.CodeMirror-selected { background: #cfc; }\n.cm-s-mdn-like .CodeMirror-line::selection, .cm-s-mdn-like .CodeMirror-line > span::selection, .cm-s-mdn-like .CodeMirror-line > span > span::selection { background: #cfc; }\n.cm-s-mdn-like .CodeMirror-line::-moz-selection, .cm-s-mdn-like .CodeMirror-line > span::-moz-selection, .cm-s-mdn-like .CodeMirror-line > span > span::-moz-selection { background: #cfc; }\n\n.cm-s-mdn-like .CodeMirror-gutters { background: #f8f8f8; border-left: 6px solid rgba(0,83,159,0.65); color: #333; }\n.cm-s-mdn-like .CodeMirror-linenumber { color: #aaa; padding-left: 8px; }\n.cm-s-mdn-like .CodeMirror-cursor { border-left: 2px solid #222; }\n\n.cm-s-mdn-like .cm-keyword { color: #6262FF; }\n.cm-s-mdn-like .cm-atom { color: #F90; }\n.cm-s-mdn-like .cm-number { color:  #ca7841; }\n.cm-s-mdn-like .cm-def { color: #8DA6CE; }\n.cm-s-mdn-like span.cm-variable-2, .cm-s-mdn-like span.cm-tag { color: #690; }\n.cm-s-mdn-like span.cm-variable-3, .cm-s-mdn-like span.cm-def, .cm-s-mdn-like span.cm-type { color: #07a; }\n\n.cm-s-mdn-like .cm-variable { color: #07a; }\n.cm-s-mdn-like .cm-property { color: #905; }\n.cm-s-mdn-like .cm-qualifier { color: #690; }\n\n.cm-s-mdn-like .cm-operator { color: #cda869; }\n.cm-s-mdn-like .cm-comment { color:#777; font-weight:normal; }\n.cm-s-mdn-like .cm-string { color:#07a; font-style:italic; }\n.cm-s-mdn-like .cm-string-2 { color:#bd6b18; } /*?*/\n.cm-s-mdn-like .cm-meta { color: #000; } /*?*/\n.cm-s-mdn-like .cm-builtin { color: #9B7536; } /*?*/\n.cm-s-mdn-like .cm-tag { color: #997643; }\n.cm-s-mdn-like .cm-attribute { color: #d6bb6d; } /*?*/\n.cm-s-mdn-like .cm-header { color: #FF6400; }\n.cm-s-mdn-like .cm-hr { color: #AEAEAE; }\n.cm-s-mdn-like .cm-link { color:#ad9361; font-style:italic; text-decoration:none; }\n.cm-s-mdn-like .cm-error { border-bottom: 1px solid red; }\n\ndiv.cm-s-mdn-like .CodeMirror-activeline-background { background: #efefff; }\ndiv.cm-s-mdn-like span.CodeMirror-matchingbracket { outline:1px solid grey; color: inherit; }\n\n.cm-s-mdn-like.CodeMirror { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFcAAAAyCAYAAAAp8UeFAAAHvklEQVR42s2b63bcNgyEQZCSHCdt2vd/0tWF7I+Q6XgMXiTtuvU5Pl57ZQKkKHzEAOtF5KeIJBGJ8uvL599FRFREZhFx8DeXv8trn68RuGaC8TRfo3SNp9dlDDHedyLyTUTeRWStXKPZrjtpZxaRw5hPqozRs1N8/enzIiQRWcCgy4MUA0f+XWliDhyL8Lfyvx7ei/Ae3iQFHyw7U/59pQVIMEEPEz0G7XiwdRjzSfC3UTtz9vchIntxvry5iMgfIhJoEflOz2CQr3F5h/HfeFe+GTdLaKcu9L8LTeQb/R/7GgbsfKedyNdoHsN31uRPWrfZ5wsj/NzzRQHuToIdU3ahwnsKPxXCjJITuOsi7XLc7SG/v5GdALs7wf8JjTFiB5+QvTEfRyGOfX3Lrx8wxyQi3sNq46O7QahQiCsRFgqddjBouVEHOKDgXAQHD9gJCr5sMKkEdjwsarG/ww3BMHBU7OBjXnzdyY7SfCxf5/z6ATccrwlKuwC/jhznnPF4CgVzhhVf4xp2EixcBActO75iZ8/fM9zAs2OMzKdslgXWJ9XG8PQoOAMA5fGcsvORgv0doBXyHrCwfLJAOwo71QLNkb8n2Pl6EWiR7OCibtkPaz4Kc/0NNAze2gju3zOwekALDaCFPI5vjPFmgGY5AZqyGEvH1x7QfIb8YtxMnA/b+QQ0aQDAwc6JMFg8CbQZ4qoYEEHbRwNojuK3EHwd7VALSgq+MNDKzfT58T8qdpADrgW0GmgcAS1lhzztJmkAzcPNOQbsWEALBDSlMKUG0Eq4CLAQWvEVQ9WU57gZJwZtgPO3r9oBTQ9WO8TjqXINx8R0EYpiZEUWOF3FxkbJkgU9B2f41YBrIj5ZfsQa0M5kTgiAAqM3ShXLgu8XMqcrQBvJ0CL5pnTsfMB13oB8athpAq2XOQmcGmoACCLydx7nToa23ATaSIY2ichfOdPTGxlasXMLaL0MLZAOwAKIM+y8CmicobGdCcbbK9DzN+yYGVoNNI5iUKTMyYOjPse4A8SM1MmcXgU0toOq1yO/v8FOxlASyc7TgeYaAMBJHcY1CcCwGI/TK4AmDbDyKYBBtFUkRwto8gygiQEaByFgJ00BH2M8JWwQS1nafDXQCidWyOI8AcjDCSjCLk8ngObuAm3JAHAdubAmOaK06V8MNEsKPJOhobSprwQa6gD7DclRQdqcwL4zxqgBrQcabUiBLclRDKAlWp+etPkBaNMA0AKlrHwTdEByZAA4GM+SNluSY6wAzcMNewxmgig5Ks0nkrSpBvSaQHMdKTBAnLojOdYyGpQ254602ZILPdTD1hdlggdIm74jbTp8vDwF5ZYUeLWGJpWsh6XNyXgcYwVoJQTEhhTYkxzZjiU5npU2TaB979TQehlaAVq4kaGpiPwwwLkYUuBbQwocyQTv1tA0+1UFWoJF3iv1oq+qoSk8EQdJmwHkziIF7oOZk14EGitibAdjLYYK78H5vZOhtWpoI0ATGHs0Q8OMb4Ey+2bU2UYztCtA0wFAs7TplGLRVQCcqaFdGSPCeTI1QNIC52iWNzof6Uib7xjEp07mNNoUYmVosVItHrHzRlLgBn9LFyRHaQCtVUMbtTNhoXWiTOO9k/V8BdAc1Oq0ArSQs6/5SU0hckNy9NnXqQY0PGYo5dWJ7nINaN6o958FWin27aBaWRka1r5myvLOAm0j30eBJqCxHLReVclxhxOEN2JfDWjxBtAC7MIH1fVaGdoOp4qJYDgKtKPSFNID2gSnGldrCqkFZ+5UeQXQBIRrSwocbdZYQT/2LwRahBPBXoHrB8nxaGROST62DKUbQOMMzZIC9abkuELfQzQALWTnDNAm8KHWFOJgJ5+SHIvTPcmx1xQyZRhNL5Qci689aXMEaN/uNIWkEwDAvFpOZmgsBaaGnbs1NPa1Jm32gBZAIh1pCtG7TSH4aE0y1uVY4uqoFPisGlpP2rSA5qTecWn5agK6BzSpgAyD+wFaqhnYoSZ1Vwr8CmlTQbrcO3ZaX0NAEyMbYaAlyquFoLKK3SPby9CeVUPThrSJmkCAE0CrKUQadi4DrdSlWhmah0YL9z9vClH59YGbHx1J8VZTyAjQepJjmXwAKTDQI3omc3p1U4gDUf6RfcdYfrUp5ClAi2J3Ba6UOXGo+K+bQrjjssitG2SJzshaLwMtXgRagUNpYYoVkMSBLM+9GGiJZMvduG6DRZ4qc04DMPtQQxOjEtACmhO7K1AbNbQDEggZyJwscFpAGwENhoBeUwh3bWolhe8BTYVKxQEWrSUn/uhcM5KhvUu/+eQu0Lzhi+VrK0PrZZNDQKs9cpYUuFYgMVpD4/NxenJTiMCNqdUEUf1qZWjppLT5qSkkUZbCwkbZMSuVnu80hfSkzRbQeqCZSAh6huR4VtoM2gHAlLf72smuWgE+VV7XpE25Ab2WFDgyhnSuKbs4GuGzCjR+tIoUuMFg3kgcWKLTwRqanJQ2W00hAsenfaApRC42hbCvK1SlE0HtE9BGgneJO+ELamitD1YjjOYnNYVcraGhtKkW0EqVVeDx733I2NH581k1NNxNLG0i0IJ8/NjVaOZ0tYZ2Vtr0Xv7tPV3hkWp9EFkgS/J0vosngTaSoaG06WHi+xObQkaAdlbanP8B2+2l0f90LmUAAAAASUVORK5CYII=); }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/midnight.css",
    "content": "/* Based on the theme at http://bonsaiden.github.com/JavaScript-Garden */\n\n/*<!--activeline-->*/\n.cm-s-midnight .CodeMirror-activeline-background { background: #253540; }\n\n.cm-s-midnight.CodeMirror {\n    background: #0F192A;\n    color: #D1EDFF;\n}\n\n.cm-s-midnight div.CodeMirror-selected { background: #314D67; }\n.cm-s-midnight .CodeMirror-line::selection, .cm-s-midnight .CodeMirror-line > span::selection, .cm-s-midnight .CodeMirror-line > span > span::selection { background: rgba(49, 77, 103, .99); }\n.cm-s-midnight .CodeMirror-line::-moz-selection, .cm-s-midnight .CodeMirror-line > span::-moz-selection, .cm-s-midnight .CodeMirror-line > span > span::-moz-selection { background: rgba(49, 77, 103, .99); }\n.cm-s-midnight .CodeMirror-gutters { background: #0F192A; border-right: 1px solid; }\n.cm-s-midnight .CodeMirror-guttermarker { color: white; }\n.cm-s-midnight .CodeMirror-guttermarker-subtle { color: #d0d0d0; }\n.cm-s-midnight .CodeMirror-linenumber { color: #D0D0D0; }\n.cm-s-midnight .CodeMirror-cursor { border-left: 1px solid #F8F8F0; }\n\n.cm-s-midnight span.cm-comment { color: #428BDD; }\n.cm-s-midnight span.cm-atom { color: #AE81FF; }\n.cm-s-midnight span.cm-number { color: #D1EDFF; }\n\n.cm-s-midnight span.cm-property, .cm-s-midnight span.cm-attribute { color: #A6E22E; }\n.cm-s-midnight span.cm-keyword { color: #E83737; }\n.cm-s-midnight span.cm-string { color: #1DC116; }\n\n.cm-s-midnight span.cm-variable { color: #FFAA3E; }\n.cm-s-midnight span.cm-variable-2 { color: #FFAA3E; }\n.cm-s-midnight span.cm-def { color: #4DD; }\n.cm-s-midnight span.cm-bracket { color: #D1EDFF; }\n.cm-s-midnight span.cm-tag { color: #449; }\n.cm-s-midnight span.cm-link { color: #AE81FF; }\n.cm-s-midnight span.cm-error { background: #F92672; color: #F8F8F0; }\n\n.cm-s-midnight .CodeMirror-matchingbracket {\n  text-decoration: underline;\n  color: white !important;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/monokai.css",
    "content": "/* Based on Sublime Text's Monokai theme */\n\n.cm-s-monokai.CodeMirror { background: #272822; color: #f8f8f2; }\n.cm-s-monokai div.CodeMirror-selected { background: #49483E; }\n.cm-s-monokai .CodeMirror-line::selection, .cm-s-monokai .CodeMirror-line > span::selection, .cm-s-monokai .CodeMirror-line > span > span::selection { background: rgba(73, 72, 62, .99); }\n.cm-s-monokai .CodeMirror-line::-moz-selection, .cm-s-monokai .CodeMirror-line > span::-moz-selection, .cm-s-monokai .CodeMirror-line > span > span::-moz-selection { background: rgba(73, 72, 62, .99); }\n.cm-s-monokai .CodeMirror-gutters { background: #272822; border-right: 0px; }\n.cm-s-monokai .CodeMirror-guttermarker { color: white; }\n.cm-s-monokai .CodeMirror-guttermarker-subtle { color: #d0d0d0; }\n.cm-s-monokai .CodeMirror-linenumber { color: #d0d0d0; }\n.cm-s-monokai .CodeMirror-cursor { border-left: 1px solid #f8f8f0; }\n\n.cm-s-monokai span.cm-comment { color: #75715e; }\n.cm-s-monokai span.cm-atom { color: #ae81ff; }\n.cm-s-monokai span.cm-number { color: #ae81ff; }\n\n.cm-s-monokai span.cm-comment.cm-attribute { color: #97b757; }\n.cm-s-monokai span.cm-comment.cm-def { color: #bc9262; }\n.cm-s-monokai span.cm-comment.cm-tag { color: #bc6283; }\n.cm-s-monokai span.cm-comment.cm-type { color: #5998a6; }\n\n.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute { color: #a6e22e; }\n.cm-s-monokai span.cm-keyword { color: #f92672; }\n.cm-s-monokai span.cm-builtin { color: #66d9ef; }\n.cm-s-monokai span.cm-string { color: #e6db74; }\n\n.cm-s-monokai span.cm-variable { color: #f8f8f2; }\n.cm-s-monokai span.cm-variable-2 { color: #9effff; }\n.cm-s-monokai span.cm-variable-3, .cm-s-monokai span.cm-type { color: #66d9ef; }\n.cm-s-monokai span.cm-def { color: #fd971f; }\n.cm-s-monokai span.cm-bracket { color: #f8f8f2; }\n.cm-s-monokai span.cm-tag { color: #f92672; }\n.cm-s-monokai span.cm-header { color: #ae81ff; }\n.cm-s-monokai span.cm-link { color: #ae81ff; }\n.cm-s-monokai span.cm-error { background: #f92672; color: #f8f8f0; }\n\n.cm-s-monokai .CodeMirror-activeline-background { background: #373831; }\n.cm-s-monokai .CodeMirror-matchingbracket {\n  text-decoration: underline;\n  color: white !important;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/moxer.css",
    "content": "/*\n  Name:       Moxer Theme\n  Author:     Mattia Astorino (http://github.com/equinusocio)\n  Website:    https://github.com/moxer-theme/moxer-code\n*/\n\n.cm-s-moxer.CodeMirror {\n  background-color: #090A0F;\n  color: #8E95B4;\n  line-height: 1.8;\n}\n\n.cm-s-moxer .CodeMirror-gutters {\n  background: #090A0F;\n  color: #35394B;\n  border: none;\n}\n\n.cm-s-moxer .CodeMirror-guttermarker,\n.cm-s-moxer .CodeMirror-guttermarker-subtle,\n.cm-s-moxer .CodeMirror-linenumber {\n  color: #35394B;\n}\n\n\n.cm-s-moxer .CodeMirror-cursor {\n  border-left: 1px solid #FFCC00;\n}\n\n.cm-s-moxer div.CodeMirror-selected {\n  background: rgba(128, 203, 196, 0.2);\n}\n\n.cm-s-moxer.CodeMirror-focused div.CodeMirror-selected {\n  background: #212431;\n}\n\n.cm-s-moxer .CodeMirror-line::selection,\n.cm-s-moxer .CodeMirror-line>span::selection,\n.cm-s-moxer .CodeMirror-line>span>span::selection {\n  background: #212431;\n}\n\n.cm-s-moxer .CodeMirror-line::-moz-selection,\n.cm-s-moxer .CodeMirror-line>span::-moz-selection,\n.cm-s-moxer .CodeMirror-line>span>span::-moz-selection {\n  background: #212431;\n}\n\n.cm-s-moxer .CodeMirror-activeline-background,\n.cm-s-moxer .CodeMirror-activeline-gutter .CodeMirror-linenumber {\n  background: rgba(33, 36, 49, 0.5);\n}\n\n.cm-s-moxer .cm-keyword {\n  color: #D46C6C;\n}\n\n.cm-s-moxer .cm-operator {\n  color: #D46C6C;\n}\n\n.cm-s-moxer .cm-variable-2 {\n  color: #81C5DA;\n}\n\n\n.cm-s-moxer .cm-variable-3,\n.cm-s-moxer .cm-type {\n  color: #f07178;\n}\n\n.cm-s-moxer .cm-builtin {\n  color: #FFCB6B;\n}\n\n.cm-s-moxer .cm-atom {\n  color: #A99BE2;\n}\n\n.cm-s-moxer .cm-number {\n  color: #7CA4C0;\n}\n\n.cm-s-moxer .cm-def {\n  color: #F5DFA5;\n}\n\n.cm-s-moxer .CodeMirror-line .cm-def ~ .cm-def {\n  color: #81C5DA;\n}\n\n.cm-s-moxer .cm-string {\n  color: #B2E4AE;\n}\n\n.cm-s-moxer .cm-string-2 {\n  color: #f07178;\n}\n\n.cm-s-moxer .cm-comment {\n  color: #3F445A;\n}\n\n.cm-s-moxer .cm-variable {\n  color: #8E95B4;\n}\n\n.cm-s-moxer .cm-tag {\n  color: #FF5370;\n}\n\n.cm-s-moxer .cm-meta {\n  color: #FFCB6B;\n}\n\n.cm-s-moxer .cm-attribute {\n  color: #C792EA;\n}\n\n.cm-s-moxer .cm-property {\n  color: #81C5DA;\n}\n\n.cm-s-moxer .cm-qualifier {\n  color: #DECB6B;\n}\n\n.cm-s-moxer .cm-variable-3,\n.cm-s-moxer .cm-type {\n  color: #DECB6B;\n}\n\n\n.cm-s-moxer .cm-error {\n  color: rgba(255, 255, 255, 1.0);\n  background-color: #FF5370;\n}\n\n.cm-s-moxer .CodeMirror-matchingbracket {\n  text-decoration: underline;\n  color: white !important;\n}"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/neat.css",
    "content": ".cm-s-neat span.cm-comment { color: #a86; }\n.cm-s-neat span.cm-keyword { line-height: 1em; font-weight: bold; color: blue; }\n.cm-s-neat span.cm-string { color: #a22; }\n.cm-s-neat span.cm-builtin { line-height: 1em; font-weight: bold; color: #077; }\n.cm-s-neat span.cm-special { line-height: 1em; font-weight: bold; color: #0aa; }\n.cm-s-neat span.cm-variable { color: black; }\n.cm-s-neat span.cm-number, .cm-s-neat span.cm-atom { color: #3a3; }\n.cm-s-neat span.cm-meta { color: #555; }\n.cm-s-neat span.cm-link { color: #3a3; }\n\n.cm-s-neat .CodeMirror-activeline-background { background: #e8f2ff; }\n.cm-s-neat .CodeMirror-matchingbracket { outline:1px solid grey; color:black !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/neo.css",
    "content": "/* neo theme for codemirror */\n\n/* Color scheme */\n\n.cm-s-neo.CodeMirror {\n  background-color:#ffffff;\n  color:#2e383c;\n  line-height:1.4375;\n}\n.cm-s-neo .cm-comment { color:#75787b; }\n.cm-s-neo .cm-keyword, .cm-s-neo .cm-property { color:#1d75b3; }\n.cm-s-neo .cm-atom,.cm-s-neo .cm-number { color:#75438a; }\n.cm-s-neo .cm-node,.cm-s-neo .cm-tag { color:#9c3328; }\n.cm-s-neo .cm-string { color:#b35e14; }\n.cm-s-neo .cm-variable,.cm-s-neo .cm-qualifier { color:#047d65; }\n\n\n/* Editor styling */\n\n.cm-s-neo pre {\n  padding:0;\n}\n\n.cm-s-neo .CodeMirror-gutters {\n  border:none;\n  border-right:10px solid transparent;\n  background-color:transparent;\n}\n\n.cm-s-neo .CodeMirror-linenumber {\n  padding:0;\n  color:#e0e2e5;\n}\n\n.cm-s-neo .CodeMirror-guttermarker { color: #1d75b3; }\n.cm-s-neo .CodeMirror-guttermarker-subtle { color: #e0e2e5; }\n\n.cm-s-neo .CodeMirror-cursor {\n  width: auto;\n  border: 0;\n  background: rgba(155,157,162,0.37);\n  z-index: 1;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/night.css",
    "content": "/* Loosely based on the Midnight Textmate theme */\n\n.cm-s-night.CodeMirror { background: #0a001f; color: #f8f8f8; }\n.cm-s-night div.CodeMirror-selected { background: #447; }\n.cm-s-night .CodeMirror-line::selection, .cm-s-night .CodeMirror-line > span::selection, .cm-s-night .CodeMirror-line > span > span::selection { background: rgba(68, 68, 119, .99); }\n.cm-s-night .CodeMirror-line::-moz-selection, .cm-s-night .CodeMirror-line > span::-moz-selection, .cm-s-night .CodeMirror-line > span > span::-moz-selection { background: rgba(68, 68, 119, .99); }\n.cm-s-night .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; }\n.cm-s-night .CodeMirror-guttermarker { color: white; }\n.cm-s-night .CodeMirror-guttermarker-subtle { color: #bbb; }\n.cm-s-night .CodeMirror-linenumber { color: #f8f8f8; }\n.cm-s-night .CodeMirror-cursor { border-left: 1px solid white; }\n\n.cm-s-night span.cm-comment { color: #8900d1; }\n.cm-s-night span.cm-atom { color: #845dc4; }\n.cm-s-night span.cm-number, .cm-s-night span.cm-attribute { color: #ffd500; }\n.cm-s-night span.cm-keyword { color: #599eff; }\n.cm-s-night span.cm-string { color: #37f14a; }\n.cm-s-night span.cm-meta { color: #7678e2; }\n.cm-s-night span.cm-variable-2, .cm-s-night span.cm-tag { color: #99b2ff; }\n.cm-s-night span.cm-variable-3, .cm-s-night span.cm-def, .cm-s-night span.cm-type { color: white; }\n.cm-s-night span.cm-bracket { color: #8da6ce; }\n.cm-s-night span.cm-builtin, .cm-s-night span.cm-special { color: #ff9e59; }\n.cm-s-night span.cm-link { color: #845dc4; }\n.cm-s-night span.cm-error { color: #9d1e15; }\n\n.cm-s-night .CodeMirror-activeline-background { background: #1C005A; }\n.cm-s-night .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/nord.css",
    "content": "/* Based on arcticicestudio's Nord theme */\n/* https://github.com/arcticicestudio/nord */\n\n.cm-s-nord.CodeMirror { background: #2e3440; color: #d8dee9; }\n.cm-s-nord div.CodeMirror-selected { background: #434c5e; }\n.cm-s-nord .CodeMirror-line::selection, .cm-s-nord .CodeMirror-line > span::selection, .cm-s-nord .CodeMirror-line > span > span::selection { background: #3b4252; }\n.cm-s-nord .CodeMirror-line::-moz-selection, .cm-s-nord .CodeMirror-line > span::-moz-selection, .cm-s-nord .CodeMirror-line > span > span::-moz-selection { background: #3b4252; }\n.cm-s-nord .CodeMirror-gutters { background: #2e3440; border-right: 0px; }\n.cm-s-nord .CodeMirror-guttermarker { color: #4c566a; }\n.cm-s-nord .CodeMirror-guttermarker-subtle { color: #4c566a; }\n.cm-s-nord .CodeMirror-linenumber { color: #4c566a; }\n.cm-s-nord .CodeMirror-cursor { border-left: 1px solid #f8f8f0; }\n\n.cm-s-nord span.cm-comment { color: #4c566a; }\n.cm-s-nord span.cm-atom { color: #b48ead; }\n.cm-s-nord span.cm-number { color: #b48ead; }\n\n.cm-s-nord span.cm-comment.cm-attribute { color: #97b757; }\n.cm-s-nord span.cm-comment.cm-def { color: #bc9262; }\n.cm-s-nord span.cm-comment.cm-tag { color: #bc6283; }\n.cm-s-nord span.cm-comment.cm-type { color: #5998a6; }\n\n.cm-s-nord span.cm-property, .cm-s-nord span.cm-attribute { color: #8FBCBB; }\n.cm-s-nord span.cm-keyword { color: #81A1C1; }\n.cm-s-nord span.cm-builtin { color: #81A1C1; }\n.cm-s-nord span.cm-string { color: #A3BE8C; }\n\n.cm-s-nord span.cm-variable { color: #d8dee9; }\n.cm-s-nord span.cm-variable-2 { color: #d8dee9; }\n.cm-s-nord span.cm-variable-3, .cm-s-nord span.cm-type { color: #d8dee9; }\n.cm-s-nord span.cm-def { color: #8FBCBB; }\n.cm-s-nord span.cm-bracket { color: #81A1C1; }\n.cm-s-nord span.cm-tag { color: #bf616a; }\n.cm-s-nord span.cm-header { color: #b48ead; }\n.cm-s-nord span.cm-link { color: #b48ead; }\n.cm-s-nord span.cm-error { background: #bf616a; color: #f8f8f0; }\n\n.cm-s-nord .CodeMirror-activeline-background { background: #3b4252; }\n.cm-s-nord .CodeMirror-matchingbracket {\n  text-decoration: underline;\n  color: white !important;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/oceanic-next.css",
    "content": "/*\n\n    Name:       oceanic-next\n    Author:     Filype Pereira (https://github.com/fpereira1)\n\n    Original oceanic-next color scheme by Dmitri Voronianski (https://github.com/voronianski/oceanic-next-color-scheme)\n\n*/\n\n.cm-s-oceanic-next.CodeMirror { background: #304148; color: #f8f8f2; }\n.cm-s-oceanic-next div.CodeMirror-selected { background: rgba(101, 115, 126, 0.33); }\n.cm-s-oceanic-next .CodeMirror-line::selection, .cm-s-oceanic-next .CodeMirror-line > span::selection, .cm-s-oceanic-next .CodeMirror-line > span > span::selection { background: rgba(101, 115, 126, 0.33); }\n.cm-s-oceanic-next .CodeMirror-line::-moz-selection, .cm-s-oceanic-next .CodeMirror-line > span::-moz-selection, .cm-s-oceanic-next .CodeMirror-line > span > span::-moz-selection { background: rgba(101, 115, 126, 0.33); }\n.cm-s-oceanic-next .CodeMirror-gutters { background: #304148; border-right: 10px; }\n.cm-s-oceanic-next .CodeMirror-guttermarker { color: white; }\n.cm-s-oceanic-next .CodeMirror-guttermarker-subtle { color: #d0d0d0; }\n.cm-s-oceanic-next .CodeMirror-linenumber { color: #d0d0d0; }\n.cm-s-oceanic-next .CodeMirror-cursor { border-left: 1px solid #f8f8f0; }\n.cm-s-oceanic-next.cm-fat-cursor .CodeMirror-cursor { background-color: #a2a8a175 !important; }\n.cm-s-oceanic-next .cm-animate-fat-cursor { background-color: #a2a8a175 !important; }\n\n.cm-s-oceanic-next span.cm-comment { color: #65737E; }\n.cm-s-oceanic-next span.cm-atom { color: #C594C5; }\n.cm-s-oceanic-next span.cm-number { color: #F99157; }\n\n.cm-s-oceanic-next span.cm-property { color: #99C794; }\n.cm-s-oceanic-next span.cm-attribute,\n.cm-s-oceanic-next span.cm-keyword { color: #C594C5; }\n.cm-s-oceanic-next span.cm-builtin { color: #66d9ef; }\n.cm-s-oceanic-next span.cm-string { color: #99C794; }\n\n.cm-s-oceanic-next span.cm-variable,\n.cm-s-oceanic-next span.cm-variable-2,\n.cm-s-oceanic-next span.cm-variable-3 { color: #f8f8f2; }\n.cm-s-oceanic-next span.cm-def { color: #6699CC; }\n.cm-s-oceanic-next span.cm-bracket { color: #5FB3B3; }\n.cm-s-oceanic-next span.cm-tag { color: #C594C5; }\n.cm-s-oceanic-next span.cm-header { color: #C594C5; }\n.cm-s-oceanic-next span.cm-link { color: #C594C5; }\n.cm-s-oceanic-next span.cm-error { background: #C594C5; color: #f8f8f0; }\n\n.cm-s-oceanic-next .CodeMirror-activeline-background { background: rgba(101, 115, 126, 0.33); }\n.cm-s-oceanic-next .CodeMirror-matchingbracket {\n  text-decoration: underline;\n  color: white !important;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/panda-syntax.css",
    "content": "/*\n\tName:       Panda Syntax\n\tAuthor:     Siamak Mokhtari (http://github.com/siamak/)\n\tCodeMirror template by Siamak Mokhtari (https://github.com/siamak/atom-panda-syntax)\n*/\n.cm-s-panda-syntax {\n\tbackground: #292A2B;\n\tcolor: #E6E6E6;\n\tline-height: 1.5;\n\tfont-family: 'Operator Mono', 'Source Code Pro', Menlo, Monaco, Consolas, Courier New, monospace;\n}\n.cm-s-panda-syntax .CodeMirror-cursor { border-color: #ff2c6d; }\n.cm-s-panda-syntax .CodeMirror-activeline-background {\n\tbackground: rgba(99, 123, 156, 0.1);\n}\n.cm-s-panda-syntax .CodeMirror-selected {\n\tbackground: #FFF;\n}\n.cm-s-panda-syntax .cm-comment {\n\tfont-style: italic;\n\tcolor: #676B79;\n}\n.cm-s-panda-syntax .cm-operator {\n\tcolor: #f3f3f3;\n}\n.cm-s-panda-syntax .cm-string {\n\tcolor: #19F9D8;\n}\n.cm-s-panda-syntax .cm-string-2 {\n    color: #FFB86C;\n}\n\n.cm-s-panda-syntax .cm-tag {\n\tcolor: #ff2c6d;\n}\n.cm-s-panda-syntax .cm-meta {\n\tcolor: #b084eb;\n}\n\n.cm-s-panda-syntax .cm-number {\n\tcolor: #FFB86C;\n}\n.cm-s-panda-syntax .cm-atom {\n\tcolor: #ff2c6d;\n}\n.cm-s-panda-syntax .cm-keyword {\n\tcolor: #FF75B5;\n}\n.cm-s-panda-syntax .cm-variable {\n\tcolor: #ffb86c;\n}\n.cm-s-panda-syntax .cm-variable-2 {\n\tcolor: #ff9ac1;\n}\n.cm-s-panda-syntax .cm-variable-3, .cm-s-panda-syntax .cm-type {\n\tcolor: #ff9ac1;\n}\n\n.cm-s-panda-syntax .cm-def {\n\tcolor: #e6e6e6;\n}\n.cm-s-panda-syntax .cm-property {\n\tcolor: #f3f3f3;\n}\n.cm-s-panda-syntax .cm-unit {\n    color: #ffb86c;\n}\n\n.cm-s-panda-syntax .cm-attribute {\n    color: #ffb86c;\n}\n\n.cm-s-panda-syntax .CodeMirror-matchingbracket {\n    border-bottom: 1px dotted #19F9D8;\n    padding-bottom: 2px;\n    color: #e6e6e6;\n}\n.cm-s-panda-syntax .CodeMirror-gutters {\n    background: #292a2b;\n    border-right-color: rgba(255, 255, 255, 0.1);\n}\n.cm-s-panda-syntax .CodeMirror-linenumber {\n    color: #e6e6e6;\n    opacity: 0.6;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/paraiso-dark.css",
    "content": "/*\n\n    Name:       Paraíso (Dark)\n    Author:     Jan T. Sott\n\n    Color scheme by Jan T. Sott (https://github.com/idleberg/Paraiso-CodeMirror)\n    Inspired by the art of Rubens LP (http://www.rubenslp.com.br)\n\n*/\n\n.cm-s-paraiso-dark.CodeMirror { background: #2f1e2e; color: #b9b6b0; }\n.cm-s-paraiso-dark div.CodeMirror-selected { background: #41323f; }\n.cm-s-paraiso-dark .CodeMirror-line::selection, .cm-s-paraiso-dark .CodeMirror-line > span::selection, .cm-s-paraiso-dark .CodeMirror-line > span > span::selection { background: rgba(65, 50, 63, .99); }\n.cm-s-paraiso-dark .CodeMirror-line::-moz-selection, .cm-s-paraiso-dark .CodeMirror-line > span::-moz-selection, .cm-s-paraiso-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(65, 50, 63, .99); }\n.cm-s-paraiso-dark .CodeMirror-gutters { background: #2f1e2e; border-right: 0px; }\n.cm-s-paraiso-dark .CodeMirror-guttermarker { color: #ef6155; }\n.cm-s-paraiso-dark .CodeMirror-guttermarker-subtle { color: #776e71; }\n.cm-s-paraiso-dark .CodeMirror-linenumber { color: #776e71; }\n.cm-s-paraiso-dark .CodeMirror-cursor { border-left: 1px solid #8d8687; }\n\n.cm-s-paraiso-dark span.cm-comment { color: #e96ba8; }\n.cm-s-paraiso-dark span.cm-atom { color: #815ba4; }\n.cm-s-paraiso-dark span.cm-number { color: #815ba4; }\n\n.cm-s-paraiso-dark span.cm-property, .cm-s-paraiso-dark span.cm-attribute { color: #48b685; }\n.cm-s-paraiso-dark span.cm-keyword { color: #ef6155; }\n.cm-s-paraiso-dark span.cm-string { color: #fec418; }\n\n.cm-s-paraiso-dark span.cm-variable { color: #48b685; }\n.cm-s-paraiso-dark span.cm-variable-2 { color: #06b6ef; }\n.cm-s-paraiso-dark span.cm-def { color: #f99b15; }\n.cm-s-paraiso-dark span.cm-bracket { color: #b9b6b0; }\n.cm-s-paraiso-dark span.cm-tag { color: #ef6155; }\n.cm-s-paraiso-dark span.cm-link { color: #815ba4; }\n.cm-s-paraiso-dark span.cm-error { background: #ef6155; color: #8d8687; }\n\n.cm-s-paraiso-dark .CodeMirror-activeline-background { background: #4D344A; }\n.cm-s-paraiso-dark .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/paraiso-light.css",
    "content": "/*\n\n    Name:       Paraíso (Light)\n    Author:     Jan T. Sott\n\n    Color scheme by Jan T. Sott (https://github.com/idleberg/Paraiso-CodeMirror)\n    Inspired by the art of Rubens LP (http://www.rubenslp.com.br)\n\n*/\n\n.cm-s-paraiso-light.CodeMirror { background: #e7e9db; color: #41323f; }\n.cm-s-paraiso-light div.CodeMirror-selected { background: #b9b6b0; }\n.cm-s-paraiso-light .CodeMirror-line::selection, .cm-s-paraiso-light .CodeMirror-line > span::selection, .cm-s-paraiso-light .CodeMirror-line > span > span::selection { background: #b9b6b0; }\n.cm-s-paraiso-light .CodeMirror-line::-moz-selection, .cm-s-paraiso-light .CodeMirror-line > span::-moz-selection, .cm-s-paraiso-light .CodeMirror-line > span > span::-moz-selection { background: #b9b6b0; }\n.cm-s-paraiso-light .CodeMirror-gutters { background: #e7e9db; border-right: 0px; }\n.cm-s-paraiso-light .CodeMirror-guttermarker { color: black; }\n.cm-s-paraiso-light .CodeMirror-guttermarker-subtle { color: #8d8687; }\n.cm-s-paraiso-light .CodeMirror-linenumber { color: #8d8687; }\n.cm-s-paraiso-light .CodeMirror-cursor { border-left: 1px solid #776e71; }\n\n.cm-s-paraiso-light span.cm-comment { color: #e96ba8; }\n.cm-s-paraiso-light span.cm-atom { color: #815ba4; }\n.cm-s-paraiso-light span.cm-number { color: #815ba4; }\n\n.cm-s-paraiso-light span.cm-property, .cm-s-paraiso-light span.cm-attribute { color: #48b685; }\n.cm-s-paraiso-light span.cm-keyword { color: #ef6155; }\n.cm-s-paraiso-light span.cm-string { color: #fec418; }\n\n.cm-s-paraiso-light span.cm-variable { color: #48b685; }\n.cm-s-paraiso-light span.cm-variable-2 { color: #06b6ef; }\n.cm-s-paraiso-light span.cm-def { color: #f99b15; }\n.cm-s-paraiso-light span.cm-bracket { color: #41323f; }\n.cm-s-paraiso-light span.cm-tag { color: #ef6155; }\n.cm-s-paraiso-light span.cm-link { color: #815ba4; }\n.cm-s-paraiso-light span.cm-error { background: #ef6155; color: #776e71; }\n\n.cm-s-paraiso-light .CodeMirror-activeline-background { background: #CFD1C4; }\n.cm-s-paraiso-light .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/pastel-on-dark.css",
    "content": "/**\n * Pastel On Dark theme ported from ACE editor\n * @license MIT\n * @copyright AtomicPages LLC 2014\n * @author Dennis Thompson, AtomicPages LLC\n * @version 1.1\n * @source https://github.com/atomicpages/codemirror-pastel-on-dark-theme\n */\n\n.cm-s-pastel-on-dark.CodeMirror {\n\tbackground: #2c2827;\n\tcolor: #8F938F;\n\tline-height: 1.5;\n}\n.cm-s-pastel-on-dark div.CodeMirror-selected { background: rgba(221,240,255,0.2); }\n.cm-s-pastel-on-dark .CodeMirror-line::selection, .cm-s-pastel-on-dark .CodeMirror-line > span::selection, .cm-s-pastel-on-dark .CodeMirror-line > span > span::selection { background: rgba(221,240,255,0.2); }\n.cm-s-pastel-on-dark .CodeMirror-line::-moz-selection, .cm-s-pastel-on-dark .CodeMirror-line > span::-moz-selection, .cm-s-pastel-on-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(221,240,255,0.2); }\n\n.cm-s-pastel-on-dark .CodeMirror-gutters {\n\tbackground: #34302f;\n\tborder-right: 0px;\n\tpadding: 0 3px;\n}\n.cm-s-pastel-on-dark .CodeMirror-guttermarker { color: white; }\n.cm-s-pastel-on-dark .CodeMirror-guttermarker-subtle { color: #8F938F; }\n.cm-s-pastel-on-dark .CodeMirror-linenumber { color: #8F938F; }\n.cm-s-pastel-on-dark .CodeMirror-cursor { border-left: 1px solid #A7A7A7; }\n.cm-s-pastel-on-dark span.cm-comment { color: #A6C6FF; }\n.cm-s-pastel-on-dark span.cm-atom { color: #DE8E30; }\n.cm-s-pastel-on-dark span.cm-number { color: #CCCCCC; }\n.cm-s-pastel-on-dark span.cm-property { color: #8F938F; }\n.cm-s-pastel-on-dark span.cm-attribute { color: #a6e22e; }\n.cm-s-pastel-on-dark span.cm-keyword { color: #AEB2F8; }\n.cm-s-pastel-on-dark span.cm-string { color: #66A968; }\n.cm-s-pastel-on-dark span.cm-variable { color: #AEB2F8; }\n.cm-s-pastel-on-dark span.cm-variable-2 { color: #BEBF55; }\n.cm-s-pastel-on-dark span.cm-variable-3, .cm-s-pastel-on-dark span.cm-type { color: #DE8E30; }\n.cm-s-pastel-on-dark span.cm-def { color: #757aD8; }\n.cm-s-pastel-on-dark span.cm-bracket { color: #f8f8f2; }\n.cm-s-pastel-on-dark span.cm-tag { color: #C1C144; }\n.cm-s-pastel-on-dark span.cm-link { color: #ae81ff; }\n.cm-s-pastel-on-dark span.cm-qualifier,.cm-s-pastel-on-dark span.cm-builtin { color: #C1C144; }\n.cm-s-pastel-on-dark span.cm-error {\n\tbackground: #757aD8;\n\tcolor: #f8f8f0;\n}\n.cm-s-pastel-on-dark .CodeMirror-activeline-background { background: rgba(255, 255, 255, 0.031); }\n.cm-s-pastel-on-dark .CodeMirror-matchingbracket {\n\tborder: 1px solid rgba(255,255,255,0.25);\n\tcolor: #8F938F !important;\n\tmargin: -1px -1px 0 -1px;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/railscasts.css",
    "content": "/*\n\n    Name:       Railscasts\n    Author:     Ryan Bates (http://railscasts.com)\n\n    CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)\n    Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)\n\n*/\n\n.cm-s-railscasts.CodeMirror {background: #2b2b2b; color: #f4f1ed;}\n.cm-s-railscasts div.CodeMirror-selected {background: #272935 !important;}\n.cm-s-railscasts .CodeMirror-gutters {background: #2b2b2b; border-right: 0px;}\n.cm-s-railscasts .CodeMirror-linenumber {color: #5a647e;}\n.cm-s-railscasts .CodeMirror-cursor {border-left: 1px solid #d4cfc9 !important;}\n\n.cm-s-railscasts span.cm-comment {color: #bc9458;}\n.cm-s-railscasts span.cm-atom {color: #b6b3eb;}\n.cm-s-railscasts span.cm-number {color: #b6b3eb;}\n\n.cm-s-railscasts span.cm-property, .cm-s-railscasts span.cm-attribute {color: #a5c261;}\n.cm-s-railscasts span.cm-keyword {color: #da4939;}\n.cm-s-railscasts span.cm-string {color: #ffc66d;}\n\n.cm-s-railscasts span.cm-variable {color: #a5c261;}\n.cm-s-railscasts span.cm-variable-2 {color: #6d9cbe;}\n.cm-s-railscasts span.cm-def {color: #cc7833;}\n.cm-s-railscasts span.cm-error {background: #da4939; color: #d4cfc9;}\n.cm-s-railscasts span.cm-bracket {color: #f4f1ed;}\n.cm-s-railscasts span.cm-tag {color: #da4939;}\n.cm-s-railscasts span.cm-link {color: #b6b3eb;}\n\n.cm-s-railscasts .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;}\n.cm-s-railscasts .CodeMirror-activeline-background { background: #303040; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/rubyblue.css",
    "content": ".cm-s-rubyblue.CodeMirror { background: #112435; color: white; }\n.cm-s-rubyblue div.CodeMirror-selected { background: #38566F; }\n.cm-s-rubyblue .CodeMirror-line::selection, .cm-s-rubyblue .CodeMirror-line > span::selection, .cm-s-rubyblue .CodeMirror-line > span > span::selection { background: rgba(56, 86, 111, 0.99); }\n.cm-s-rubyblue .CodeMirror-line::-moz-selection, .cm-s-rubyblue .CodeMirror-line > span::-moz-selection, .cm-s-rubyblue .CodeMirror-line > span > span::-moz-selection { background: rgba(56, 86, 111, 0.99); }\n.cm-s-rubyblue .CodeMirror-gutters { background: #1F4661; border-right: 7px solid #3E7087; }\n.cm-s-rubyblue .CodeMirror-guttermarker { color: white; }\n.cm-s-rubyblue .CodeMirror-guttermarker-subtle { color: #3E7087; }\n.cm-s-rubyblue .CodeMirror-linenumber { color: white; }\n.cm-s-rubyblue .CodeMirror-cursor { border-left: 1px solid white; }\n\n.cm-s-rubyblue span.cm-comment { color: #999; font-style:italic; line-height: 1em; }\n.cm-s-rubyblue span.cm-atom { color: #F4C20B; }\n.cm-s-rubyblue span.cm-number, .cm-s-rubyblue span.cm-attribute { color: #82C6E0; }\n.cm-s-rubyblue span.cm-keyword { color: #F0F; }\n.cm-s-rubyblue span.cm-string { color: #F08047; }\n.cm-s-rubyblue span.cm-meta { color: #F0F; }\n.cm-s-rubyblue span.cm-variable-2, .cm-s-rubyblue span.cm-tag { color: #7BD827; }\n.cm-s-rubyblue span.cm-variable-3, .cm-s-rubyblue span.cm-def, .cm-s-rubyblue span.cm-type { color: white; }\n.cm-s-rubyblue span.cm-bracket { color: #F0F; }\n.cm-s-rubyblue span.cm-link { color: #F4C20B; }\n.cm-s-rubyblue span.CodeMirror-matchingbracket { color:#F0F !important; }\n.cm-s-rubyblue span.cm-builtin, .cm-s-rubyblue span.cm-special { color: #FF9D00; }\n.cm-s-rubyblue span.cm-error { color: #AF2018; }\n\n.cm-s-rubyblue .CodeMirror-activeline-background { background: #173047; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/seti.css",
    "content": "/*\n\n    Name:       seti\n    Author:     Michael Kaminsky (http://github.com/mkaminsky11)\n\n    Original seti color scheme by Jesse Weed (https://github.com/jesseweed/seti-syntax)\n\n*/\n\n\n.cm-s-seti.CodeMirror {\n  background-color: #151718 !important;\n  color: #CFD2D1 !important;\n  border: none;\n}\n.cm-s-seti .CodeMirror-gutters {\n  color: #404b53;\n  background-color: #0E1112;\n  border: none;\n}\n.cm-s-seti .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-seti .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-seti.CodeMirror-focused div.CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-seti .CodeMirror-line::selection, .cm-s-seti .CodeMirror-line > span::selection, .cm-s-seti .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-seti .CodeMirror-line::-moz-selection, .cm-s-seti .CodeMirror-line > span::-moz-selection, .cm-s-seti .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-seti span.cm-comment { color: #41535b; }\n.cm-s-seti span.cm-string, .cm-s-seti span.cm-string-2 { color: #55b5db; }\n.cm-s-seti span.cm-number { color: #cd3f45; }\n.cm-s-seti span.cm-variable { color: #55b5db; }\n.cm-s-seti span.cm-variable-2 { color: #a074c4; }\n.cm-s-seti span.cm-def { color: #55b5db; }\n.cm-s-seti span.cm-keyword { color: #ff79c6; }\n.cm-s-seti span.cm-operator { color: #9fca56; }\n.cm-s-seti span.cm-keyword { color: #e6cd69; }\n.cm-s-seti span.cm-atom { color: #cd3f45; }\n.cm-s-seti span.cm-meta { color: #55b5db; }\n.cm-s-seti span.cm-tag { color: #55b5db; }\n.cm-s-seti span.cm-attribute { color: #9fca56; }\n.cm-s-seti span.cm-qualifier { color: #9fca56; }\n.cm-s-seti span.cm-property { color: #a074c4; }\n.cm-s-seti span.cm-variable-3, .cm-s-seti span.cm-type { color: #9fca56; }\n.cm-s-seti span.cm-builtin { color: #9fca56; }\n.cm-s-seti .CodeMirror-activeline-background { background: #101213; }\n.cm-s-seti .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/shadowfox.css",
    "content": "/*\n\n    Name:       shadowfox\n    Author:     overdodactyl (http://github.com/overdodactyl)\n\n    Original shadowfox color scheme by Firefox\n\n*/\n\n.cm-s-shadowfox.CodeMirror { background: #2a2a2e; color: #b1b1b3; }\n.cm-s-shadowfox div.CodeMirror-selected { background: #353B48; }\n.cm-s-shadowfox .CodeMirror-line::selection, .cm-s-shadowfox .CodeMirror-line > span::selection, .cm-s-shadowfox .CodeMirror-line > span > span::selection { background: #353B48; }\n.cm-s-shadowfox .CodeMirror-line::-moz-selection, .cm-s-shadowfox .CodeMirror-line > span::-moz-selection, .cm-s-shadowfox .CodeMirror-line > span > span::-moz-selection { background: #353B48; }\n.cm-s-shadowfox .CodeMirror-gutters { background: #0c0c0d ; border-right: 1px solid #0c0c0d; }\n.cm-s-shadowfox .CodeMirror-guttermarker { color: #555; }\n.cm-s-shadowfox .CodeMirror-linenumber { color: #939393; }\n.cm-s-shadowfox .CodeMirror-cursor { border-left: 1px solid #fff; }\n\n.cm-s-shadowfox span.cm-comment { color: #939393; }\n.cm-s-shadowfox span.cm-atom { color: #FF7DE9; }\n.cm-s-shadowfox span.cm-quote { color: #FF7DE9; }\n.cm-s-shadowfox span.cm-builtin { color: #FF7DE9; }\n.cm-s-shadowfox span.cm-attribute { color: #FF7DE9; }\n.cm-s-shadowfox span.cm-keyword { color: #FF7DE9; }\n.cm-s-shadowfox span.cm-error { color: #FF7DE9; }\n\n.cm-s-shadowfox span.cm-number { color: #6B89FF; }\n.cm-s-shadowfox span.cm-string { color: #6B89FF; }\n.cm-s-shadowfox span.cm-string-2 { color: #6B89FF; }\n\n.cm-s-shadowfox span.cm-meta { color: #939393; }\n.cm-s-shadowfox span.cm-hr { color: #939393; }\n\n.cm-s-shadowfox span.cm-header { color: #75BFFF; }\n.cm-s-shadowfox span.cm-qualifier { color: #75BFFF; }\n.cm-s-shadowfox span.cm-variable-2 { color: #75BFFF; }\n\n.cm-s-shadowfox span.cm-property { color: #86DE74; }\n\n.cm-s-shadowfox span.cm-def { color: #75BFFF; }\n.cm-s-shadowfox span.cm-bracket { color: #75BFFF; }\n.cm-s-shadowfox span.cm-tag { color: #75BFFF; }\n.cm-s-shadowfox span.cm-link:visited { color: #75BFFF; }\n\n.cm-s-shadowfox span.cm-variable { color: #B98EFF; }\n.cm-s-shadowfox span.cm-variable-3 { color: #d7d7db; }\n.cm-s-shadowfox span.cm-link { color: #737373; }\n.cm-s-shadowfox span.cm-operator { color: #b1b1b3; }\n.cm-s-shadowfox span.cm-special { color: #d7d7db; }\n\n.cm-s-shadowfox .CodeMirror-activeline-background { background: rgba(185, 215, 253, .15) }\n.cm-s-shadowfox .CodeMirror-matchingbracket { outline: solid 1px rgba(255, 255, 255, .25); color: white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/solarized.css",
    "content": "/*\nSolarized theme for code-mirror\nhttp://ethanschoonover.com/solarized\n*/\n\n/*\nSolarized color palette\nhttp://ethanschoonover.com/solarized/img/solarized-palette.png\n*/\n\n.solarized.base03 { color: #002b36; }\n.solarized.base02 { color: #073642; }\n.solarized.base01 { color: #586e75; }\n.solarized.base00 { color: #657b83; }\n.solarized.base0 { color: #839496; }\n.solarized.base1 { color: #93a1a1; }\n.solarized.base2 { color: #eee8d5; }\n.solarized.base3  { color: #fdf6e3; }\n.solarized.solar-yellow  { color: #b58900; }\n.solarized.solar-orange  { color: #cb4b16; }\n.solarized.solar-red { color: #dc322f; }\n.solarized.solar-magenta { color: #d33682; }\n.solarized.solar-violet  { color: #6c71c4; }\n.solarized.solar-blue { color: #268bd2; }\n.solarized.solar-cyan { color: #2aa198; }\n.solarized.solar-green { color: #859900; }\n\n/* Color scheme for code-mirror */\n\n.cm-s-solarized {\n  line-height: 1.45em;\n  color-profile: sRGB;\n  rendering-intent: auto;\n}\n.cm-s-solarized.cm-s-dark {\n  color: #839496;\n  background-color: #002b36;\n}\n.cm-s-solarized.cm-s-light {\n  background-color: #fdf6e3;\n  color: #657b83;\n}\n\n.cm-s-solarized .CodeMirror-widget {\n  text-shadow: none;\n}\n\n.cm-s-solarized .cm-header { color: #586e75; }\n.cm-s-solarized .cm-quote { color: #93a1a1; }\n\n.cm-s-solarized .cm-keyword { color: #cb4b16; }\n.cm-s-solarized .cm-atom { color: #d33682; }\n.cm-s-solarized .cm-number { color: #d33682; }\n.cm-s-solarized .cm-def { color: #2aa198; }\n\n.cm-s-solarized .cm-variable { color: #839496; }\n.cm-s-solarized .cm-variable-2 { color: #b58900; }\n.cm-s-solarized .cm-variable-3, .cm-s-solarized .cm-type { color: #6c71c4; }\n\n.cm-s-solarized .cm-property { color: #2aa198; }\n.cm-s-solarized .cm-operator { color: #6c71c4; }\n\n.cm-s-solarized .cm-comment { color: #586e75; font-style:italic; }\n\n.cm-s-solarized .cm-string { color: #859900; }\n.cm-s-solarized .cm-string-2 { color: #b58900; }\n\n.cm-s-solarized .cm-meta { color: #859900; }\n.cm-s-solarized .cm-qualifier { color: #b58900; }\n.cm-s-solarized .cm-builtin { color: #d33682; }\n.cm-s-solarized .cm-bracket { color: #cb4b16; }\n.cm-s-solarized .CodeMirror-matchingbracket { color: #859900; }\n.cm-s-solarized .CodeMirror-nonmatchingbracket { color: #dc322f; }\n.cm-s-solarized .cm-tag { color: #93a1a1; }\n.cm-s-solarized .cm-attribute { color: #2aa198; }\n.cm-s-solarized .cm-hr {\n  color: transparent;\n  border-top: 1px solid #586e75;\n  display: block;\n}\n.cm-s-solarized .cm-link { color: #93a1a1; cursor: pointer; }\n.cm-s-solarized .cm-special { color: #6c71c4; }\n.cm-s-solarized .cm-em {\n  color: #999;\n  text-decoration: underline;\n  text-decoration-style: dotted;\n}\n.cm-s-solarized .cm-error,\n.cm-s-solarized .cm-invalidchar {\n  color: #586e75;\n  border-bottom: 1px dotted #dc322f;\n}\n\n.cm-s-solarized.cm-s-dark div.CodeMirror-selected { background: #073642; }\n.cm-s-solarized.cm-s-dark.CodeMirror ::selection { background: rgba(7, 54, 66, 0.99); }\n.cm-s-solarized.cm-s-dark .CodeMirror-line::-moz-selection, .cm-s-dark .CodeMirror-line > span::-moz-selection, .cm-s-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(7, 54, 66, 0.99); }\n\n.cm-s-solarized.cm-s-light div.CodeMirror-selected { background: #eee8d5; }\n.cm-s-solarized.cm-s-light .CodeMirror-line::selection, .cm-s-light .CodeMirror-line > span::selection, .cm-s-light .CodeMirror-line > span > span::selection { background: #eee8d5; }\n.cm-s-solarized.cm-s-light .CodeMirror-line::-moz-selection, .cm-s-light .CodeMirror-line > span::-moz-selection, .cm-s-light .CodeMirror-line > span > span::-moz-selection { background: #eee8d5; }\n\n/* Editor styling */\n\n\n\n/* Little shadow on the view-port of the buffer view */\n.cm-s-solarized.CodeMirror {\n  -moz-box-shadow: inset 7px 0 12px -6px #000;\n  -webkit-box-shadow: inset 7px 0 12px -6px #000;\n  box-shadow: inset 7px 0 12px -6px #000;\n}\n\n/* Remove gutter border */\n.cm-s-solarized .CodeMirror-gutters {\n  border-right: 0;\n}\n\n/* Gutter colors and line number styling based of color scheme (dark / light) */\n\n/* Dark */\n.cm-s-solarized.cm-s-dark .CodeMirror-gutters {\n  background-color: #073642;\n}\n\n.cm-s-solarized.cm-s-dark .CodeMirror-linenumber {\n  color: #586e75;\n}\n\n/* Light */\n.cm-s-solarized.cm-s-light .CodeMirror-gutters {\n  background-color: #eee8d5;\n}\n\n.cm-s-solarized.cm-s-light .CodeMirror-linenumber {\n  color: #839496;\n}\n\n/* Common */\n.cm-s-solarized .CodeMirror-linenumber {\n  padding: 0 5px;\n}\n.cm-s-solarized .CodeMirror-guttermarker-subtle { color: #586e75; }\n.cm-s-solarized.cm-s-dark .CodeMirror-guttermarker { color: #ddd; }\n.cm-s-solarized.cm-s-light .CodeMirror-guttermarker { color: #cb4b16; }\n\n.cm-s-solarized .CodeMirror-gutter .CodeMirror-gutter-text {\n  color: #586e75;\n}\n\n/* Cursor */\n.cm-s-solarized .CodeMirror-cursor { border-left: 1px solid #819090; }\n\n/* Fat cursor */\n.cm-s-solarized.cm-s-light.cm-fat-cursor .CodeMirror-cursor { background: #77ee77; }\n.cm-s-solarized.cm-s-light .cm-animate-fat-cursor { background-color: #77ee77; }\n.cm-s-solarized.cm-s-dark.cm-fat-cursor .CodeMirror-cursor { background: #586e75; }\n.cm-s-solarized.cm-s-dark .cm-animate-fat-cursor { background-color: #586e75; }\n\n/* Active line */\n.cm-s-solarized.cm-s-dark .CodeMirror-activeline-background {\n  background: rgba(255, 255, 255, 0.06);\n}\n.cm-s-solarized.cm-s-light .CodeMirror-activeline-background {\n  background: rgba(0, 0, 0, 0.06);\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/ssms.css",
    "content": ".cm-s-ssms span.cm-keyword { color: blue; }\n.cm-s-ssms span.cm-comment { color: darkgreen; }\n.cm-s-ssms span.cm-string { color: red; }\n.cm-s-ssms span.cm-def { color: black; }\n.cm-s-ssms span.cm-variable { color: black; }\n.cm-s-ssms span.cm-variable-2 { color: black; }\n.cm-s-ssms span.cm-atom { color: darkgray; }\n.cm-s-ssms .CodeMirror-linenumber { color: teal; }\n.cm-s-ssms .CodeMirror-activeline-background { background: #ffffff; }\n.cm-s-ssms span.cm-string-2 { color: #FF00FF; }\n.cm-s-ssms span.cm-operator, \n.cm-s-ssms span.cm-bracket, \n.cm-s-ssms span.cm-punctuation { color: darkgray; }\n.cm-s-ssms .CodeMirror-gutters { border-right: 3px solid #ffee62; background-color: #ffffff; }\n.cm-s-ssms div.CodeMirror-selected { background: #ADD6FF; }\n\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/the-matrix.css",
    "content": ".cm-s-the-matrix.CodeMirror { background: #000000; color: #00FF00; }\n.cm-s-the-matrix div.CodeMirror-selected { background: #2D2D2D; }\n.cm-s-the-matrix .CodeMirror-line::selection, .cm-s-the-matrix .CodeMirror-line > span::selection, .cm-s-the-matrix .CodeMirror-line > span > span::selection { background: rgba(45, 45, 45, 0.99); }\n.cm-s-the-matrix .CodeMirror-line::-moz-selection, .cm-s-the-matrix .CodeMirror-line > span::-moz-selection, .cm-s-the-matrix .CodeMirror-line > span > span::-moz-selection { background: rgba(45, 45, 45, 0.99); }\n.cm-s-the-matrix .CodeMirror-gutters { background: #060; border-right: 2px solid #00FF00; }\n.cm-s-the-matrix .CodeMirror-guttermarker { color: #0f0; }\n.cm-s-the-matrix .CodeMirror-guttermarker-subtle { color: white; }\n.cm-s-the-matrix .CodeMirror-linenumber { color: #FFFFFF; }\n.cm-s-the-matrix .CodeMirror-cursor { border-left: 1px solid #00FF00; }\n\n.cm-s-the-matrix span.cm-keyword { color: #008803; font-weight: bold; }\n.cm-s-the-matrix span.cm-atom { color: #3FF; }\n.cm-s-the-matrix span.cm-number { color: #FFB94F; }\n.cm-s-the-matrix span.cm-def { color: #99C; }\n.cm-s-the-matrix span.cm-variable { color: #F6C; }\n.cm-s-the-matrix span.cm-variable-2 { color: #C6F; }\n.cm-s-the-matrix span.cm-variable-3, .cm-s-the-matrix span.cm-type { color: #96F; }\n.cm-s-the-matrix span.cm-property { color: #62FFA0; }\n.cm-s-the-matrix span.cm-operator { color: #999; }\n.cm-s-the-matrix span.cm-comment { color: #CCCCCC; }\n.cm-s-the-matrix span.cm-string { color: #39C; }\n.cm-s-the-matrix span.cm-meta { color: #C9F; }\n.cm-s-the-matrix span.cm-qualifier { color: #FFF700; }\n.cm-s-the-matrix span.cm-builtin { color: #30a; }\n.cm-s-the-matrix span.cm-bracket { color: #cc7; }\n.cm-s-the-matrix span.cm-tag { color: #FFBD40; }\n.cm-s-the-matrix span.cm-attribute { color: #FFF700; }\n.cm-s-the-matrix span.cm-error { color: #FF0000; }\n\n.cm-s-the-matrix .CodeMirror-activeline-background { background: #040; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/tomorrow-night-bright.css",
    "content": "/*\n\n    Name:       Tomorrow Night - Bright\n    Author:     Chris Kempson\n\n    Port done by Gerard Braad <me@gbraad.nl>\n\n*/\n\n.cm-s-tomorrow-night-bright.CodeMirror { background: #000000; color: #eaeaea; }\n.cm-s-tomorrow-night-bright div.CodeMirror-selected { background: #424242; }\n.cm-s-tomorrow-night-bright .CodeMirror-gutters { background: #000000; border-right: 0px; }\n.cm-s-tomorrow-night-bright .CodeMirror-guttermarker { color: #e78c45; }\n.cm-s-tomorrow-night-bright .CodeMirror-guttermarker-subtle { color: #777; }\n.cm-s-tomorrow-night-bright .CodeMirror-linenumber { color: #424242; }\n.cm-s-tomorrow-night-bright .CodeMirror-cursor { border-left: 1px solid #6A6A6A; }\n\n.cm-s-tomorrow-night-bright span.cm-comment { color: #d27b53; }\n.cm-s-tomorrow-night-bright span.cm-atom { color: #a16a94; }\n.cm-s-tomorrow-night-bright span.cm-number { color: #a16a94; }\n\n.cm-s-tomorrow-night-bright span.cm-property, .cm-s-tomorrow-night-bright span.cm-attribute { color: #99cc99; }\n.cm-s-tomorrow-night-bright span.cm-keyword { color: #d54e53; }\n.cm-s-tomorrow-night-bright span.cm-string { color: #e7c547; }\n\n.cm-s-tomorrow-night-bright span.cm-variable { color: #b9ca4a; }\n.cm-s-tomorrow-night-bright span.cm-variable-2 { color: #7aa6da; }\n.cm-s-tomorrow-night-bright span.cm-def { color: #e78c45; }\n.cm-s-tomorrow-night-bright span.cm-bracket { color: #eaeaea; }\n.cm-s-tomorrow-night-bright span.cm-tag { color: #d54e53; }\n.cm-s-tomorrow-night-bright span.cm-link { color: #a16a94; }\n.cm-s-tomorrow-night-bright span.cm-error { background: #d54e53; color: #6A6A6A; }\n\n.cm-s-tomorrow-night-bright .CodeMirror-activeline-background { background: #2a2a2a; }\n.cm-s-tomorrow-night-bright .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/tomorrow-night-eighties.css",
    "content": "/*\n\n    Name:       Tomorrow Night - Eighties\n    Author:     Chris Kempson\n\n    CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)\n    Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)\n\n*/\n\n.cm-s-tomorrow-night-eighties.CodeMirror { background: #000000; color: #CCCCCC; }\n.cm-s-tomorrow-night-eighties div.CodeMirror-selected { background: #2D2D2D; }\n.cm-s-tomorrow-night-eighties .CodeMirror-line::selection, .cm-s-tomorrow-night-eighties .CodeMirror-line > span::selection, .cm-s-tomorrow-night-eighties .CodeMirror-line > span > span::selection { background: rgba(45, 45, 45, 0.99); }\n.cm-s-tomorrow-night-eighties .CodeMirror-line::-moz-selection, .cm-s-tomorrow-night-eighties .CodeMirror-line > span::-moz-selection, .cm-s-tomorrow-night-eighties .CodeMirror-line > span > span::-moz-selection { background: rgba(45, 45, 45, 0.99); }\n.cm-s-tomorrow-night-eighties .CodeMirror-gutters { background: #000000; border-right: 0px; }\n.cm-s-tomorrow-night-eighties .CodeMirror-guttermarker { color: #f2777a; }\n.cm-s-tomorrow-night-eighties .CodeMirror-guttermarker-subtle { color: #777; }\n.cm-s-tomorrow-night-eighties .CodeMirror-linenumber { color: #515151; }\n.cm-s-tomorrow-night-eighties .CodeMirror-cursor { border-left: 1px solid #6A6A6A; }\n\n.cm-s-tomorrow-night-eighties span.cm-comment { color: #d27b53; }\n.cm-s-tomorrow-night-eighties span.cm-atom { color: #a16a94; }\n.cm-s-tomorrow-night-eighties span.cm-number { color: #a16a94; }\n\n.cm-s-tomorrow-night-eighties span.cm-property, .cm-s-tomorrow-night-eighties span.cm-attribute { color: #99cc99; }\n.cm-s-tomorrow-night-eighties span.cm-keyword { color: #f2777a; }\n.cm-s-tomorrow-night-eighties span.cm-string { color: #ffcc66; }\n\n.cm-s-tomorrow-night-eighties span.cm-variable { color: #99cc99; }\n.cm-s-tomorrow-night-eighties span.cm-variable-2 { color: #6699cc; }\n.cm-s-tomorrow-night-eighties span.cm-def { color: #f99157; }\n.cm-s-tomorrow-night-eighties span.cm-bracket { color: #CCCCCC; }\n.cm-s-tomorrow-night-eighties span.cm-tag { color: #f2777a; }\n.cm-s-tomorrow-night-eighties span.cm-link { color: #a16a94; }\n.cm-s-tomorrow-night-eighties span.cm-error { background: #f2777a; color: #6A6A6A; }\n\n.cm-s-tomorrow-night-eighties .CodeMirror-activeline-background { background: #343600; }\n.cm-s-tomorrow-night-eighties .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/ttcn.css",
    "content": ".cm-s-ttcn .cm-quote { color: #090; }\n.cm-s-ttcn .cm-negative { color: #d44; }\n.cm-s-ttcn .cm-positive { color: #292; }\n.cm-s-ttcn .cm-header, .cm-strong { font-weight: bold; }\n.cm-s-ttcn .cm-em { font-style: italic; }\n.cm-s-ttcn .cm-link { text-decoration: underline; }\n.cm-s-ttcn .cm-strikethrough { text-decoration: line-through; }\n.cm-s-ttcn .cm-header { color: #00f; font-weight: bold; }\n\n.cm-s-ttcn .cm-atom { color: #219; }\n.cm-s-ttcn .cm-attribute { color: #00c; }\n.cm-s-ttcn .cm-bracket { color: #997; }\n.cm-s-ttcn .cm-comment { color: #333333; }\n.cm-s-ttcn .cm-def { color: #00f; }\n.cm-s-ttcn .cm-em { font-style: italic; }\n.cm-s-ttcn .cm-error { color: #f00; }\n.cm-s-ttcn .cm-hr { color: #999; }\n.cm-s-ttcn .cm-invalidchar { color: #f00; }\n.cm-s-ttcn .cm-keyword { font-weight:bold; }\n.cm-s-ttcn .cm-link { color: #00c; text-decoration: underline; }\n.cm-s-ttcn .cm-meta { color: #555; }\n.cm-s-ttcn .cm-negative { color: #d44; }\n.cm-s-ttcn .cm-positive { color: #292; }\n.cm-s-ttcn .cm-qualifier { color: #555; }\n.cm-s-ttcn .cm-strikethrough { text-decoration: line-through; }\n.cm-s-ttcn .cm-string { color: #006400; }\n.cm-s-ttcn .cm-string-2 { color: #f50; }\n.cm-s-ttcn .cm-strong { font-weight: bold; }\n.cm-s-ttcn .cm-tag { color: #170; }\n.cm-s-ttcn .cm-variable { color: #8B2252; }\n.cm-s-ttcn .cm-variable-2 { color: #05a; }\n.cm-s-ttcn .cm-variable-3, .cm-s-ttcn .cm-type { color: #085; }\n\n.cm-s-ttcn .cm-invalidchar { color: #f00; }\n\n/* ASN */\n.cm-s-ttcn .cm-accessTypes,\n.cm-s-ttcn .cm-compareTypes { color: #27408B; }\n.cm-s-ttcn .cm-cmipVerbs { color: #8B2252; }\n.cm-s-ttcn .cm-modifier { color:#D2691E; }\n.cm-s-ttcn .cm-status { color:#8B4545; }\n.cm-s-ttcn .cm-storage { color:#A020F0; }\n.cm-s-ttcn .cm-tags { color:#006400; }\n\n/* CFG */\n.cm-s-ttcn .cm-externalCommands { color: #8B4545; font-weight:bold; }\n.cm-s-ttcn .cm-fileNCtrlMaskOptions,\n.cm-s-ttcn .cm-sectionTitle { color: #2E8B57; font-weight:bold; }\n\n/* TTCN */\n.cm-s-ttcn .cm-booleanConsts,\n.cm-s-ttcn .cm-otherConsts,\n.cm-s-ttcn .cm-verdictConsts { color: #006400; }\n.cm-s-ttcn .cm-configOps,\n.cm-s-ttcn .cm-functionOps,\n.cm-s-ttcn .cm-portOps,\n.cm-s-ttcn .cm-sutOps,\n.cm-s-ttcn .cm-timerOps,\n.cm-s-ttcn .cm-verdictOps { color: #0000FF; }\n.cm-s-ttcn .cm-preprocessor,\n.cm-s-ttcn .cm-templateMatch,\n.cm-s-ttcn .cm-ttcn3Macros { color: #27408B; }\n.cm-s-ttcn .cm-types { color: #A52A2A; font-weight:bold; }\n.cm-s-ttcn .cm-visibilityModifiers { font-weight:bold; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/twilight.css",
    "content": ".cm-s-twilight.CodeMirror { background: #141414; color: #f7f7f7; } /**/\n.cm-s-twilight div.CodeMirror-selected { background: #323232; } /**/\n.cm-s-twilight .CodeMirror-line::selection, .cm-s-twilight .CodeMirror-line > span::selection, .cm-s-twilight .CodeMirror-line > span > span::selection { background: rgba(50, 50, 50, 0.99); }\n.cm-s-twilight .CodeMirror-line::-moz-selection, .cm-s-twilight .CodeMirror-line > span::-moz-selection, .cm-s-twilight .CodeMirror-line > span > span::-moz-selection { background: rgba(50, 50, 50, 0.99); }\n\n.cm-s-twilight .CodeMirror-gutters { background: #222; border-right: 1px solid #aaa; }\n.cm-s-twilight .CodeMirror-guttermarker { color: white; }\n.cm-s-twilight .CodeMirror-guttermarker-subtle { color: #aaa; }\n.cm-s-twilight .CodeMirror-linenumber { color: #aaa; }\n.cm-s-twilight .CodeMirror-cursor { border-left: 1px solid white; }\n\n.cm-s-twilight .cm-keyword { color: #f9ee98; } /**/\n.cm-s-twilight .cm-atom { color: #FC0; }\n.cm-s-twilight .cm-number { color:  #ca7841; } /**/\n.cm-s-twilight .cm-def { color: #8DA6CE; }\n.cm-s-twilight span.cm-variable-2, .cm-s-twilight span.cm-tag { color: #607392; } /**/\n.cm-s-twilight span.cm-variable-3, .cm-s-twilight span.cm-def, .cm-s-twilight span.cm-type { color: #607392; } /**/\n.cm-s-twilight .cm-operator { color: #cda869; } /**/\n.cm-s-twilight .cm-comment { color:#777; font-style:italic; font-weight:normal; } /**/\n.cm-s-twilight .cm-string { color:#8f9d6a; font-style:italic; } /**/\n.cm-s-twilight .cm-string-2 { color:#bd6b18; } /*?*/\n.cm-s-twilight .cm-meta { background-color:#141414; color:#f7f7f7; } /*?*/\n.cm-s-twilight .cm-builtin { color: #cda869; } /*?*/\n.cm-s-twilight .cm-tag { color: #997643; } /**/\n.cm-s-twilight .cm-attribute { color: #d6bb6d; } /*?*/\n.cm-s-twilight .cm-header { color: #FF6400; }\n.cm-s-twilight .cm-hr { color: #AEAEAE; }\n.cm-s-twilight .cm-link { color:#ad9361; font-style:italic; text-decoration:none; } /**/\n.cm-s-twilight .cm-error { border-bottom: 1px solid red; }\n\n.cm-s-twilight .CodeMirror-activeline-background { background: #27282E; }\n.cm-s-twilight .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/vibrant-ink.css",
    "content": "/* Taken from the popular Visual Studio Vibrant Ink Schema */\n\n.cm-s-vibrant-ink.CodeMirror { background: black; color: white; }\n.cm-s-vibrant-ink div.CodeMirror-selected { background: #35493c; }\n.cm-s-vibrant-ink .CodeMirror-line::selection, .cm-s-vibrant-ink .CodeMirror-line > span::selection, .cm-s-vibrant-ink .CodeMirror-line > span > span::selection { background: rgba(53, 73, 60, 0.99); }\n.cm-s-vibrant-ink .CodeMirror-line::-moz-selection, .cm-s-vibrant-ink .CodeMirror-line > span::-moz-selection, .cm-s-vibrant-ink .CodeMirror-line > span > span::-moz-selection { background: rgba(53, 73, 60, 0.99); }\n\n.cm-s-vibrant-ink .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; }\n.cm-s-vibrant-ink .CodeMirror-guttermarker { color: white; }\n.cm-s-vibrant-ink .CodeMirror-guttermarker-subtle { color: #d0d0d0; }\n.cm-s-vibrant-ink .CodeMirror-linenumber { color: #d0d0d0; }\n.cm-s-vibrant-ink .CodeMirror-cursor { border-left: 1px solid white; }\n\n.cm-s-vibrant-ink .cm-keyword { color: #CC7832; }\n.cm-s-vibrant-ink .cm-atom { color: #FC0; }\n.cm-s-vibrant-ink .cm-number { color:  #FFEE98; }\n.cm-s-vibrant-ink .cm-def { color: #8DA6CE; }\n.cm-s-vibrant-ink span.cm-variable-2, .cm-s-vibrant span.cm-tag { color: #FFC66D; }\n.cm-s-vibrant-ink span.cm-variable-3, .cm-s-vibrant span.cm-def, .cm-s-vibrant span.cm-type { color: #FFC66D; }\n.cm-s-vibrant-ink .cm-operator { color: #888; }\n.cm-s-vibrant-ink .cm-comment { color: gray; font-weight: bold; }\n.cm-s-vibrant-ink .cm-string { color:  #A5C25C; }\n.cm-s-vibrant-ink .cm-string-2 { color: red; }\n.cm-s-vibrant-ink .cm-meta { color: #D8FA3C; }\n.cm-s-vibrant-ink .cm-builtin { color: #8DA6CE; }\n.cm-s-vibrant-ink .cm-tag { color: #8DA6CE; }\n.cm-s-vibrant-ink .cm-attribute { color: #8DA6CE; }\n.cm-s-vibrant-ink .cm-header { color: #FF6400; }\n.cm-s-vibrant-ink .cm-hr { color: #AEAEAE; }\n.cm-s-vibrant-ink .cm-link { color: #5656F3; }\n.cm-s-vibrant-ink .cm-error { border-bottom: 1px solid red; }\n\n.cm-s-vibrant-ink .CodeMirror-activeline-background { background: #27282E; }\n.cm-s-vibrant-ink .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/xq-dark.css",
    "content": "/*\nCopyright (C) 2011 by MarkLogic Corporation\nAuthor: Mike Brevoort <mike@brevoort.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n.cm-s-xq-dark.CodeMirror { background: #0a001f; color: #f8f8f8; }\n.cm-s-xq-dark div.CodeMirror-selected { background: #27007A; }\n.cm-s-xq-dark .CodeMirror-line::selection, .cm-s-xq-dark .CodeMirror-line > span::selection, .cm-s-xq-dark .CodeMirror-line > span > span::selection { background: rgba(39, 0, 122, 0.99); }\n.cm-s-xq-dark .CodeMirror-line::-moz-selection, .cm-s-xq-dark .CodeMirror-line > span::-moz-selection, .cm-s-xq-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(39, 0, 122, 0.99); }\n.cm-s-xq-dark .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; }\n.cm-s-xq-dark .CodeMirror-guttermarker { color: #FFBD40; }\n.cm-s-xq-dark .CodeMirror-guttermarker-subtle { color: #f8f8f8; }\n.cm-s-xq-dark .CodeMirror-linenumber { color: #f8f8f8; }\n.cm-s-xq-dark .CodeMirror-cursor { border-left: 1px solid white; }\n\n.cm-s-xq-dark span.cm-keyword { color: #FFBD40; }\n.cm-s-xq-dark span.cm-atom { color: #6C8CD5; }\n.cm-s-xq-dark span.cm-number { color: #164; }\n.cm-s-xq-dark span.cm-def { color: #FFF; text-decoration:underline; }\n.cm-s-xq-dark span.cm-variable { color: #FFF; }\n.cm-s-xq-dark span.cm-variable-2 { color: #EEE; }\n.cm-s-xq-dark span.cm-variable-3, .cm-s-xq-dark span.cm-type { color: #DDD; }\n.cm-s-xq-dark span.cm-property {}\n.cm-s-xq-dark span.cm-operator {}\n.cm-s-xq-dark span.cm-comment { color: gray; }\n.cm-s-xq-dark span.cm-string { color: #9FEE00; }\n.cm-s-xq-dark span.cm-meta { color: yellow; }\n.cm-s-xq-dark span.cm-qualifier { color: #FFF700; }\n.cm-s-xq-dark span.cm-builtin { color: #30a; }\n.cm-s-xq-dark span.cm-bracket { color: #cc7; }\n.cm-s-xq-dark span.cm-tag { color: #FFBD40; }\n.cm-s-xq-dark span.cm-attribute { color: #FFF700; }\n.cm-s-xq-dark span.cm-error { color: #f00; }\n\n.cm-s-xq-dark .CodeMirror-activeline-background { background: #27282E; }\n.cm-s-xq-dark .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/xq-light.css",
    "content": "/*\nCopyright (C) 2011 by MarkLogic Corporation\nAuthor: Mike Brevoort <mike@brevoort.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n.cm-s-xq-light span.cm-keyword { line-height: 1em; font-weight: bold; color: #5A5CAD; }\n.cm-s-xq-light span.cm-atom { color: #6C8CD5; }\n.cm-s-xq-light span.cm-number { color: #164; }\n.cm-s-xq-light span.cm-def { text-decoration:underline; }\n.cm-s-xq-light span.cm-variable { color: black; }\n.cm-s-xq-light span.cm-variable-2 { color:black; }\n.cm-s-xq-light span.cm-variable-3, .cm-s-xq-light span.cm-type { color: black; }\n.cm-s-xq-light span.cm-property {}\n.cm-s-xq-light span.cm-operator {}\n.cm-s-xq-light span.cm-comment { color: #0080FF; font-style: italic; }\n.cm-s-xq-light span.cm-string { color: red; }\n.cm-s-xq-light span.cm-meta { color: yellow; }\n.cm-s-xq-light span.cm-qualifier { color: grey; }\n.cm-s-xq-light span.cm-builtin { color: #7EA656; }\n.cm-s-xq-light span.cm-bracket { color: #cc7; }\n.cm-s-xq-light span.cm-tag { color: #3F7F7F; }\n.cm-s-xq-light span.cm-attribute { color: #7F007F; }\n.cm-s-xq-light span.cm-error { color: #f00; }\n\n.cm-s-xq-light .CodeMirror-activeline-background { background: #e8f2ff; }\n.cm-s-xq-light .CodeMirror-matchingbracket { outline:1px solid grey;color:black !important;background:yellow; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/yeti.css",
    "content": "/*\n\n    Name:       yeti\n    Author:     Michael Kaminsky (http://github.com/mkaminsky11)\n\n    Original yeti color scheme by Jesse Weed (https://github.com/jesseweed/yeti-syntax)\n\n*/\n\n\n.cm-s-yeti.CodeMirror {\n  background-color: #ECEAE8 !important;\n  color: #d1c9c0 !important;\n  border: none;\n}\n\n.cm-s-yeti .CodeMirror-gutters {\n  color: #adaba6;\n  background-color: #E5E1DB;\n  border: none;\n}\n.cm-s-yeti .CodeMirror-cursor { border-left: solid thin #d1c9c0; }\n.cm-s-yeti .CodeMirror-linenumber { color: #adaba6; }\n.cm-s-yeti.CodeMirror-focused div.CodeMirror-selected { background: #DCD8D2; }\n.cm-s-yeti .CodeMirror-line::selection, .cm-s-yeti .CodeMirror-line > span::selection, .cm-s-yeti .CodeMirror-line > span > span::selection { background: #DCD8D2; }\n.cm-s-yeti .CodeMirror-line::-moz-selection, .cm-s-yeti .CodeMirror-line > span::-moz-selection, .cm-s-yeti .CodeMirror-line > span > span::-moz-selection { background: #DCD8D2; }\n.cm-s-yeti span.cm-comment { color: #d4c8be; }\n.cm-s-yeti span.cm-string, .cm-s-yeti span.cm-string-2 { color: #96c0d8; }\n.cm-s-yeti span.cm-number { color: #a074c4; }\n.cm-s-yeti span.cm-variable { color: #55b5db; }\n.cm-s-yeti span.cm-variable-2 { color: #a074c4; }\n.cm-s-yeti span.cm-def { color: #55b5db; }\n.cm-s-yeti span.cm-operator { color: #9fb96e; }\n.cm-s-yeti span.cm-keyword { color: #9fb96e; }\n.cm-s-yeti span.cm-atom { color: #a074c4; }\n.cm-s-yeti span.cm-meta { color: #96c0d8; }\n.cm-s-yeti span.cm-tag { color: #96c0d8; }\n.cm-s-yeti span.cm-attribute { color: #9fb96e; }\n.cm-s-yeti span.cm-qualifier { color: #96c0d8; }\n.cm-s-yeti span.cm-property { color: #a074c4; }\n.cm-s-yeti span.cm-builtin { color: #a074c4; }\n.cm-s-yeti span.cm-variable-3, .cm-s-yeti span.cm-type { color: #96c0d8; }\n.cm-s-yeti .CodeMirror-activeline-background { background: #E7E4E0; }\n.cm-s-yeti .CodeMirror-matchingbracket { text-decoration: underline; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/yonce.css",
    "content": "/*\n\n    Name:       yoncé\n    Author:     Thomas MacLean (http://github.com/thomasmaclean)\n\n    Original yoncé color scheme by Mina Markham (https://github.com/minamarkham)\n\n*/\n\n.cm-s-yonce.CodeMirror { background: #1C1C1C; color: #d4d4d4; } /**/\n.cm-s-yonce div.CodeMirror-selected { background: rgba(252, 69, 133, 0.478); } /**/\n.cm-s-yonce .CodeMirror-selectedtext,\n.cm-s-yonce .CodeMirror-selected,\n.cm-s-yonce .CodeMirror-line::selection,\n.cm-s-yonce .CodeMirror-line > span::selection,\n.cm-s-yonce .CodeMirror-line > span > span::selection,\n.cm-s-yonce .CodeMirror-line::-moz-selection,\n.cm-s-yonce .CodeMirror-line > span::-moz-selection,\n.cm-s-yonce .CodeMirror-line > span > span::-moz-selection { background: rgba(252, 67, 132, 0.47); }\n\n.cm-s-yonce.CodeMirror pre { padding-left: 0px; }\n.cm-s-yonce .CodeMirror-gutters {background: #1C1C1C; border-right: 0px;}\n.cm-s-yonce .CodeMirror-linenumber {color: #777777;  padding-right: 10px; }\n.cm-s-yonce .CodeMirror-activeline .CodeMirror-linenumber.CodeMirror-gutter-elt { background: #1C1C1C; color: #fc4384; }\n.cm-s-yonce .CodeMirror-linenumber { color: #777; }\n.cm-s-yonce .CodeMirror-cursor { border-left: 2px solid #FC4384; }\n.cm-s-yonce .cm-searching { background: rgba(243, 155, 53, .3) !important; outline: 1px solid #F39B35; }\n.cm-s-yonce .cm-searching.CodeMirror-selectedtext { background: rgba(243, 155, 53, .7) !important; color: white; }\n\n.cm-s-yonce .cm-keyword { color: #00A7AA; } /**/\n.cm-s-yonce .cm-atom { color: #F39B35; }\n.cm-s-yonce .cm-number, .cm-s-yonce span.cm-type { color:  #A06FCA; } /**/\n.cm-s-yonce .cm-def { color: #98E342; }\n.cm-s-yonce .cm-property,\n.cm-s-yonce span.cm-variable { color: #D4D4D4; font-style: italic; }\n.cm-s-yonce span.cm-variable-2 { color: #da7dae; font-style: italic; }\n.cm-s-yonce span.cm-variable-3 { color: #A06FCA; }\n.cm-s-yonce .cm-type.cm-def { color: #FC4384; font-style: normal; text-decoration: underline; }\n.cm-s-yonce .cm-property.cm-def { color: #FC4384; font-style: normal; }\n.cm-s-yonce .cm-callee { color: #FC4384; font-style: normal; }\n.cm-s-yonce .cm-operator { color: #FC4384; } /**/\n.cm-s-yonce .cm-qualifier,\n.cm-s-yonce .cm-tag { color: #FC4384; }\n.cm-s-yonce .cm-tag.cm-bracket { color: #D4D4D4; }\n.cm-s-yonce .cm-attribute { color: #A06FCA; }\n.cm-s-yonce .cm-comment { color:#696d70; font-style:italic; font-weight:normal; } /**/\n.cm-s-yonce .cm-comment.cm-tag { color: #FC4384 }\n.cm-s-yonce .cm-comment.cm-attribute { color: #D4D4D4; }\n.cm-s-yonce .cm-string { color:#E6DB74; } /**/\n.cm-s-yonce .cm-string-2 { color:#F39B35; } /*?*/\n.cm-s-yonce .cm-meta { color: #D4D4D4; background: inherit; }\n.cm-s-yonce .cm-builtin { color: #FC4384; } /*?*/\n.cm-s-yonce .cm-header { color: #da7dae; }\n.cm-s-yonce .cm-hr { color: #98E342; }\n.cm-s-yonce .cm-link { color:#696d70; font-style:italic; text-decoration:none; } /**/\n.cm-s-yonce .cm-error { border-bottom: 1px solid #C42412; }\n\n.cm-s-yonce .CodeMirror-activeline-background { background: #272727; }\n.cm-s-yonce .CodeMirror-matchingbracket { outline:1px solid grey; color:#D4D4D4 !important; }\n"
  },
  {
    "path": "public/assets/lib/vendor/codemirror/theme/zenburn.css",
    "content": "/**\n * \"\n *  Using Zenburn color palette from the Emacs Zenburn Theme\n *  https://github.com/bbatsov/zenburn-emacs/blob/master/zenburn-theme.el\n *\n *  Also using parts of https://github.com/xavi/coderay-lighttable-theme\n * \"\n * From: https://github.com/wisenomad/zenburn-lighttable-theme/blob/master/zenburn.css\n */\n\n.cm-s-zenburn .CodeMirror-gutters { background: #3f3f3f !important; }\n.cm-s-zenburn .CodeMirror-foldgutter-open, .CodeMirror-foldgutter-folded { color: #999; }\n.cm-s-zenburn .CodeMirror-cursor { border-left: 1px solid white; }\n.cm-s-zenburn.CodeMirror { background-color: #3f3f3f; color: #dcdccc; }\n.cm-s-zenburn span.cm-builtin { color: #dcdccc; font-weight: bold; }\n.cm-s-zenburn span.cm-comment { color: #7f9f7f; }\n.cm-s-zenburn span.cm-keyword { color: #f0dfaf; font-weight: bold; }\n.cm-s-zenburn span.cm-atom { color: #bfebbf; }\n.cm-s-zenburn span.cm-def { color: #dcdccc; }\n.cm-s-zenburn span.cm-variable { color: #dfaf8f; }\n.cm-s-zenburn span.cm-variable-2 { color: #dcdccc; }\n.cm-s-zenburn span.cm-string { color: #cc9393; }\n.cm-s-zenburn span.cm-string-2 { color: #cc9393; }\n.cm-s-zenburn span.cm-number { color: #dcdccc; }\n.cm-s-zenburn span.cm-tag { color: #93e0e3; }\n.cm-s-zenburn span.cm-property { color: #dfaf8f; }\n.cm-s-zenburn span.cm-attribute { color: #dfaf8f; }\n.cm-s-zenburn span.cm-qualifier { color: #7cb8bb; }\n.cm-s-zenburn span.cm-meta { color: #f0dfaf; }\n.cm-s-zenburn span.cm-header { color: #f0efd0; }\n.cm-s-zenburn span.cm-operator { color: #f0efd0; }\n.cm-s-zenburn span.CodeMirror-matchingbracket { box-sizing: border-box; background: transparent; border-bottom: 1px solid; }\n.cm-s-zenburn span.CodeMirror-nonmatchingbracket { border-bottom: 1px solid; background: none; }\n.cm-s-zenburn .CodeMirror-activeline { background: #000000; }\n.cm-s-zenburn .CodeMirror-activeline-background { background: #000000; }\n.cm-s-zenburn div.CodeMirror-selected { background: #545454; }\n.cm-s-zenburn .CodeMirror-focused div.CodeMirror-selected { background: #4f4f4f; }\n"
  },
  {
    "path": "public/assets/lib/vendor/exif-js.js",
    "content": "/**\n * Minified by jsDelivr using UglifyJS v3.3.25.\n * Original file: /npm/exif-js@2.3.0/exif.js\n * \n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n(function(){var d=!1,l=function(e){return e instanceof l?e:this instanceof l?void(this.EXIFwrapped=e):new l(e)};\"undefined\"!=typeof exports?(\"undefined\"!=typeof module&&module.exports&&(exports=module.exports=l),exports.EXIF=l):this.EXIF=l;var u=l.Tags={36864:\"ExifVersion\",40960:\"FlashpixVersion\",40961:\"ColorSpace\",40962:\"PixelXDimension\",40963:\"PixelYDimension\",37121:\"ComponentsConfiguration\",37122:\"CompressedBitsPerPixel\",37500:\"MakerNote\",37510:\"UserComment\",40964:\"RelatedSoundFile\",36867:\"DateTimeOriginal\",36868:\"DateTimeDigitized\",37520:\"SubsecTime\",37521:\"SubsecTimeOriginal\",37522:\"SubsecTimeDigitized\",33434:\"ExposureTime\",33437:\"FNumber\",34850:\"ExposureProgram\",34852:\"SpectralSensitivity\",34855:\"ISOSpeedRatings\",34856:\"OECF\",37377:\"ShutterSpeedValue\",37378:\"ApertureValue\",37379:\"BrightnessValue\",37380:\"ExposureBias\",37381:\"MaxApertureValue\",37382:\"SubjectDistance\",37383:\"MeteringMode\",37384:\"LightSource\",37385:\"Flash\",37396:\"SubjectArea\",37386:\"FocalLength\",41483:\"FlashEnergy\",41484:\"SpatialFrequencyResponse\",41486:\"FocalPlaneXResolution\",41487:\"FocalPlaneYResolution\",41488:\"FocalPlaneResolutionUnit\",41492:\"SubjectLocation\",41493:\"ExposureIndex\",41495:\"SensingMethod\",41728:\"FileSource\",41729:\"SceneType\",41730:\"CFAPattern\",41985:\"CustomRendered\",41986:\"ExposureMode\",41987:\"WhiteBalance\",41988:\"DigitalZoomRation\",41989:\"FocalLengthIn35mmFilm\",41990:\"SceneCaptureType\",41991:\"GainControl\",41992:\"Contrast\",41993:\"Saturation\",41994:\"Sharpness\",41995:\"DeviceSettingDescription\",41996:\"SubjectDistanceRange\",40965:\"InteroperabilityIFDPointer\",42016:\"ImageUniqueID\"},c=l.TiffTags={256:\"ImageWidth\",257:\"ImageHeight\",34665:\"ExifIFDPointer\",34853:\"GPSInfoIFDPointer\",40965:\"InteroperabilityIFDPointer\",258:\"BitsPerSample\",259:\"Compression\",262:\"PhotometricInterpretation\",274:\"Orientation\",277:\"SamplesPerPixel\",284:\"PlanarConfiguration\",530:\"YCbCrSubSampling\",531:\"YCbCrPositioning\",282:\"XResolution\",283:\"YResolution\",296:\"ResolutionUnit\",273:\"StripOffsets\",278:\"RowsPerStrip\",279:\"StripByteCounts\",513:\"JPEGInterchangeFormat\",514:\"JPEGInterchangeFormatLength\",301:\"TransferFunction\",318:\"WhitePoint\",319:\"PrimaryChromaticities\",529:\"YCbCrCoefficients\",532:\"ReferenceBlackWhite\",306:\"DateTime\",270:\"ImageDescription\",271:\"Make\",272:\"Model\",305:\"Software\",315:\"Artist\",33432:\"Copyright\"},f=l.GPSTags={0:\"GPSVersionID\",1:\"GPSLatitudeRef\",2:\"GPSLatitude\",3:\"GPSLongitudeRef\",4:\"GPSLongitude\",5:\"GPSAltitudeRef\",6:\"GPSAltitude\",7:\"GPSTimeStamp\",8:\"GPSSatellites\",9:\"GPSStatus\",10:\"GPSMeasureMode\",11:\"GPSDOP\",12:\"GPSSpeedRef\",13:\"GPSSpeed\",14:\"GPSTrackRef\",15:\"GPSTrack\",16:\"GPSImgDirectionRef\",17:\"GPSImgDirection\",18:\"GPSMapDatum\",19:\"GPSDestLatitudeRef\",20:\"GPSDestLatitude\",21:\"GPSDestLongitudeRef\",22:\"GPSDestLongitude\",23:\"GPSDestBearingRef\",24:\"GPSDestBearing\",25:\"GPSDestDistanceRef\",26:\"GPSDestDistance\",27:\"GPSProcessingMethod\",28:\"GPSAreaInformation\",29:\"GPSDateStamp\",30:\"GPSDifferential\"},g=l.IFD1Tags={256:\"ImageWidth\",257:\"ImageHeight\",258:\"BitsPerSample\",259:\"Compression\",262:\"PhotometricInterpretation\",273:\"StripOffsets\",274:\"Orientation\",277:\"SamplesPerPixel\",278:\"RowsPerStrip\",279:\"StripByteCounts\",282:\"XResolution\",283:\"YResolution\",284:\"PlanarConfiguration\",296:\"ResolutionUnit\",513:\"JpegIFOffset\",514:\"JpegIFByteCount\",529:\"YCbCrCoefficients\",530:\"YCbCrSubSampling\",531:\"YCbCrPositioning\",532:\"ReferenceBlackWhite\"},m=l.StringValues={ExposureProgram:{0:\"Not defined\",1:\"Manual\",2:\"Normal program\",3:\"Aperture priority\",4:\"Shutter priority\",5:\"Creative program\",6:\"Action program\",7:\"Portrait mode\",8:\"Landscape mode\"},MeteringMode:{0:\"Unknown\",1:\"Average\",2:\"CenterWeightedAverage\",3:\"Spot\",4:\"MultiSpot\",5:\"Pattern\",6:\"Partial\",255:\"Other\"},LightSource:{0:\"Unknown\",1:\"Daylight\",2:\"Fluorescent\",3:\"Tungsten (incandescent light)\",4:\"Flash\",9:\"Fine weather\",10:\"Cloudy weather\",11:\"Shade\",12:\"Daylight fluorescent (D 5700 - 7100K)\",13:\"Day white fluorescent (N 4600 - 5400K)\",14:\"Cool white fluorescent (W 3900 - 4500K)\",15:\"White fluorescent (WW 3200 - 3700K)\",17:\"Standard light A\",18:\"Standard light B\",19:\"Standard light C\",20:\"D55\",21:\"D65\",22:\"D75\",23:\"D50\",24:\"ISO studio tungsten\",255:\"Other\"},Flash:{0:\"Flash did not fire\",1:\"Flash fired\",5:\"Strobe return light not detected\",7:\"Strobe return light detected\",9:\"Flash fired, compulsory flash mode\",13:\"Flash fired, compulsory flash mode, return light not detected\",15:\"Flash fired, compulsory flash mode, return light detected\",16:\"Flash did not fire, compulsory flash mode\",24:\"Flash did not fire, auto mode\",25:\"Flash fired, auto mode\",29:\"Flash fired, auto mode, return light not detected\",31:\"Flash fired, auto mode, return light detected\",32:\"No flash function\",65:\"Flash fired, red-eye reduction mode\",69:\"Flash fired, red-eye reduction mode, return light not detected\",71:\"Flash fired, red-eye reduction mode, return light detected\",73:\"Flash fired, compulsory flash mode, red-eye reduction mode\",77:\"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected\",79:\"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected\",89:\"Flash fired, auto mode, red-eye reduction mode\",93:\"Flash fired, auto mode, return light not detected, red-eye reduction mode\",95:\"Flash fired, auto mode, return light detected, red-eye reduction mode\"},SensingMethod:{1:\"Not defined\",2:\"One-chip color area sensor\",3:\"Two-chip color area sensor\",4:\"Three-chip color area sensor\",5:\"Color sequential area sensor\",7:\"Trilinear sensor\",8:\"Color sequential linear sensor\"},SceneCaptureType:{0:\"Standard\",1:\"Landscape\",2:\"Portrait\",3:\"Night scene\"},SceneType:{1:\"Directly photographed\"},CustomRendered:{0:\"Normal process\",1:\"Custom process\"},WhiteBalance:{0:\"Auto white balance\",1:\"Manual white balance\"},GainControl:{0:\"None\",1:\"Low gain up\",2:\"High gain up\",3:\"Low gain down\",4:\"High gain down\"},Contrast:{0:\"Normal\",1:\"Soft\",2:\"Hard\"},Saturation:{0:\"Normal\",1:\"Low saturation\",2:\"High saturation\"},Sharpness:{0:\"Normal\",1:\"Soft\",2:\"Hard\"},SubjectDistanceRange:{0:\"Unknown\",1:\"Macro\",2:\"Close view\",3:\"Distant view\"},FileSource:{3:\"DSC\"},Components:{0:\"\",1:\"Y\",2:\"Cb\",3:\"Cr\",4:\"R\",5:\"G\",6:\"B\"}};function i(e){return!!e.exifdata}function r(i,o){function t(e){var t=p(e);i.exifdata=t||{};var n=function(e){var t=new DataView(e);d&&console.log(\"Got file of length \"+e.byteLength);if(255!=t.getUint8(0)||216!=t.getUint8(1))return d&&console.log(\"Not a valid JPEG\"),!1;var n=2,r=e.byteLength;for(;n<r;){if(l=n,56===(s=t).getUint8(l)&&66===s.getUint8(l+1)&&73===s.getUint8(l+2)&&77===s.getUint8(l+3)&&4===s.getUint8(l+4)&&4===s.getUint8(l+5)){var i=t.getUint8(n+7);i%2!=0&&(i+=1),0===i&&(i=4);var o=n+8+i,a=t.getUint16(n+6+i);return S(e,o,a)}n++}var s,l}(e);if(i.iptcdata=n||{},l.isXmpEnabled){var r=function(e){if(!(\"DOMParser\"in self))return;var t=new DataView(e);d&&console.log(\"Got file of length \"+e.byteLength);if(255!=t.getUint8(0)||216!=t.getUint8(1))return d&&console.log(\"Not a valid JPEG\"),!1;var n=2,r=e.byteLength,i=new DOMParser;for(;n<r-4;){if(\"http\"==y(t,n,4)){var o=n-1,a=t.getUint16(n-2)-1,s=y(t,o,a),l=s.indexOf(\"xmpmeta>\")+8,u=(s=s.substring(s.indexOf(\"<x:xmpmeta\"),l)).indexOf(\"x:xmpmeta\")+10;s=s.slice(0,u)+'xmlns:Iptc4xmpCore=\"http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:tiff=\"http://ns.adobe.com/tiff/1.0/\" xmlns:plus=\"http://schemas.android.com/apk/lib/com.google.android.gms.plus\" xmlns:ext=\"http://www.gettyimages.com/xsltExtension/1.0\" xmlns:exif=\"http://ns.adobe.com/exif/1.0/\" xmlns:stEvt=\"http://ns.adobe.com/xap/1.0/sType/ResourceEvent#\" xmlns:stRef=\"http://ns.adobe.com/xap/1.0/sType/ResourceRef#\" xmlns:crs=\"http://ns.adobe.com/camera-raw-settings/1.0/\" xmlns:xapGImg=\"http://ns.adobe.com/xap/1.0/g/img/\" xmlns:Iptc4xmpExt=\"http://iptc.org/std/Iptc4xmpExt/2008-02-29/\" '+s.slice(u);var c=i.parseFromString(s,\"text/xml\");return x(c)}n++}}(e);i.xmpdata=r||{}}o&&o.call(i)}var e,n,r;if(i.src)if(/^data\\:/i.test(i.src))t(function(e,t){t=t||e.match(/^data\\:([^\\;]+)\\;base64,/im)[1]||\"\",e=e.replace(/^data\\:([^\\;]+)\\;base64,/gim,\"\");for(var n=atob(e),r=n.length,i=new ArrayBuffer(r),o=new Uint8Array(i),a=0;a<r;a++)o[a]=n.charCodeAt(a);return i}(i.src));else if(/^blob\\:/i.test(i.src)){(s=new FileReader).onload=function(e){t(e.target.result)},e=i.src,n=function(e){s.readAsArrayBuffer(e)},(r=new XMLHttpRequest).open(\"GET\",e,!0),r.responseType=\"blob\",r.onload=function(e){200!=this.status&&0!==this.status||n(this.response)},r.send()}else{var a=new XMLHttpRequest;a.onload=function(){if(200!=this.status&&0!==this.status)throw\"Could not load image\";t(a.response),a=null},a.open(\"GET\",i.src,!0),a.responseType=\"arraybuffer\",a.send(null)}else if(self.FileReader&&(i instanceof self.Blob||i instanceof self.File)){var s;(s=new FileReader).onload=function(e){d&&console.log(\"Got file of length \"+e.target.result.byteLength),t(e.target.result)},s.readAsArrayBuffer(i)}}function p(e){var t=new DataView(e);if(d&&console.log(\"Got file of length \"+e.byteLength),255!=t.getUint8(0)||216!=t.getUint8(1))return d&&console.log(\"Not a valid JPEG\"),!1;for(var n,r=2,i=e.byteLength;r<i;){if(255!=t.getUint8(r))return d&&console.log(\"Not a valid marker at offset \"+r+\", found: \"+t.getUint8(r)),!1;if(n=t.getUint8(r+1),d&&console.log(n),225==n)return d&&console.log(\"Found 0xFFE1 marker\"),o(t,r+4,t.getUint16(r+2));r+=2+t.getUint16(r+2)}}var h={120:\"caption\",110:\"credit\",25:\"keywords\",55:\"dateCreated\",80:\"byline\",85:\"bylineTitle\",122:\"captionWriter\",105:\"headline\",116:\"copyright\",15:\"category\"};function S(e,t,n){for(var r,i,o,a,s=new DataView(e),l={},u=t;u<t+n;)28===s.getUint8(u)&&2===s.getUint8(u+1)&&(a=s.getUint8(u+2))in h&&((o=s.getInt16(u+3))+5,i=h[a],r=y(s,u+5,o),l.hasOwnProperty(i)?l[i]instanceof Array?l[i].push(r):l[i]=[l[i],r]:l[i]=r),u++;return l}function P(e,t,n,r,i){var o,a,s,l=e.getUint16(n,!i),u={};for(s=0;s<l;s++)o=n+12*s+2,!(a=r[e.getUint16(o,!i)])&&d&&console.log(\"Unknown tag: \"+e.getUint16(o,!i)),u[a]=F(e,o,t,n,i);return u}function F(e,t,n,r,i){var o,a,s,l,u,c,d=e.getUint16(t+2,!i),f=e.getUint32(t+4,!i),g=e.getUint32(t+8,!i)+n;switch(d){case 1:case 7:if(1==f)return e.getUint8(t+8,!i);for(o=4<f?g:t+8,a=[],l=0;l<f;l++)a[l]=e.getUint8(o+l);return a;case 2:return y(e,o=4<f?g:t+8,f-1);case 3:if(1==f)return e.getUint16(t+8,!i);for(o=2<f?g:t+8,a=[],l=0;l<f;l++)a[l]=e.getUint16(o+2*l,!i);return a;case 4:if(1==f)return e.getUint32(t+8,!i);for(a=[],l=0;l<f;l++)a[l]=e.getUint32(g+4*l,!i);return a;case 5:if(1==f)return u=e.getUint32(g,!i),c=e.getUint32(g+4,!i),(s=new Number(u/c)).numerator=u,s.denominator=c,s;for(a=[],l=0;l<f;l++)u=e.getUint32(g+8*l,!i),c=e.getUint32(g+4+8*l,!i),a[l]=new Number(u/c),a[l].numerator=u,a[l].denominator=c;return a;case 9:if(1==f)return e.getInt32(t+8,!i);for(a=[],l=0;l<f;l++)a[l]=e.getInt32(g+4*l,!i);return a;case 10:if(1==f)return e.getInt32(g,!i)/e.getInt32(g+4,!i);for(a=[],l=0;l<f;l++)a[l]=e.getInt32(g+8*l,!i)/e.getInt32(g+4+8*l,!i);return a}}function y(e,t,r){var i=\"\";for(n=t;n<t+r;n++)i+=String.fromCharCode(e.getUint8(n));return i}function o(e,t){if(\"Exif\"!=y(e,t,4))return d&&console.log(\"Not valid EXIF data! \"+y(e,t,4)),!1;var n,r,i,o,a,s=t+6;if(18761==e.getUint16(s))n=!1;else{if(19789!=e.getUint16(s))return d&&console.log(\"Not valid TIFF data! (no 0x4949 or 0x4D4D)\"),!1;n=!0}if(42!=e.getUint16(s+2,!n))return d&&console.log(\"Not valid TIFF data! (no 0x002A)\"),!1;var l=e.getUint32(s+4,!n);if(l<8)return d&&console.log(\"Not valid TIFF data! (First offset less than 8)\",e.getUint32(s+4,!n)),!1;if((r=P(e,s,s+l,c,n)).ExifIFDPointer)for(i in o=P(e,s,s+r.ExifIFDPointer,u,n)){switch(i){case\"LightSource\":case\"Flash\":case\"MeteringMode\":case\"ExposureProgram\":case\"SensingMethod\":case\"SceneCaptureType\":case\"SceneType\":case\"CustomRendered\":case\"WhiteBalance\":case\"GainControl\":case\"Contrast\":case\"Saturation\":case\"Sharpness\":case\"SubjectDistanceRange\":case\"FileSource\":o[i]=m[i][o[i]];break;case\"ExifVersion\":case\"FlashpixVersion\":o[i]=String.fromCharCode(o[i][0],o[i][1],o[i][2],o[i][3]);break;case\"ComponentsConfiguration\":o[i]=m.Components[o[i][0]]+m.Components[o[i][1]]+m.Components[o[i][2]]+m.Components[o[i][3]]}r[i]=o[i]}if(r.GPSInfoIFDPointer)for(i in a=P(e,s,s+r.GPSInfoIFDPointer,f,n)){switch(i){case\"GPSVersionID\":a[i]=a[i][0]+\".\"+a[i][1]+\".\"+a[i][2]+\".\"+a[i][3]}r[i]=a[i]}return r.thumbnail=function(e,t,n,r){var i,o,a,s,l=(o=t+n,a=r,s=(i=e).getUint16(o,!a),i.getUint32(o+2+12*s,!a));if(!l)return{};if(l>e.byteLength)return{};var u=P(e,t,t+l,g,r);if(u.Compression)switch(u.Compression){case 6:if(u.JpegIFOffset&&u.JpegIFByteCount){var c=t+u.JpegIFOffset,d=u.JpegIFByteCount;u.blob=new Blob([new Uint8Array(e.buffer,c,d)],{type:\"image/jpeg\"})}break;case 1:console.log(\"Thumbnail image format is TIFF, which is not implemented.\");break;default:console.log(\"Unknown thumbnail image format '%s'\",u.Compression)}else 2==u.PhotometricInterpretation&&console.log(\"Thumbnail image format is RGB, which is not implemented.\");return u}(e,s,l,n),r}function b(e){var t={};if(1==e.nodeType){if(0<e.attributes.length){t[\"@attributes\"]={};for(var n=0;n<e.attributes.length;n++){var r=e.attributes.item(n);t[\"@attributes\"][r.nodeName]=r.nodeValue}}}else if(3==e.nodeType)return e.nodeValue;if(e.hasChildNodes())for(var i=0;i<e.childNodes.length;i++){var o=e.childNodes.item(i),a=o.nodeName;if(null==t[a])t[a]=b(o);else{if(null==t[a].push){var s=t[a];t[a]=[],t[a].push(s)}t[a].push(b(o))}}return t}function x(e){try{var t={};if(0<e.children.length)for(var n=0;n<e.children.length;n++){var r=e.children.item(n),i=r.attributes;for(var o in i){var a=i[o],s=a.nodeName,l=a.nodeValue;void 0!==s&&(t[s]=l)}var u=r.nodeName;if(void 0===t[u])t[u]=b(r);else{if(void 0===t[u].push){var c=t[u];t[u]=[],t[u].push(c)}t[u].push(b(r))}}else t=e.textContent;return t}catch(e){console.log(e.message)}}l.enableXmp=function(){l.isXmpEnabled=!0},l.disableXmp=function(){l.isXmpEnabled=!1},l.getData=function(e,t){return!((self.Image&&e instanceof self.Image||self.HTMLImageElement&&e instanceof self.HTMLImageElement)&&!e.complete)&&(i(e)?t&&t.call(e):r(e,t),!0)},l.getTag=function(e,t){if(i(e))return e.exifdata[t]},l.getIptcTag=function(e,t){if(i(e))return e.iptcdata[t]},l.getAllTags=function(e){if(!i(e))return{};var t,n=e.exifdata,r={};for(t in n)n.hasOwnProperty(t)&&(r[t]=n[t]);return r},l.getAllIptcTags=function(e){if(!i(e))return{};var t,n=e.iptcdata,r={};for(t in n)n.hasOwnProperty(t)&&(r[t]=n[t]);return r},l.pretty=function(e){if(!i(e))return\"\";var t,n=e.exifdata,r=\"\";for(t in n)n.hasOwnProperty(t)&&(\"object\"==typeof n[t]?n[t]instanceof Number?r+=t+\" : \"+n[t]+\" [\"+n[t].numerator+\"/\"+n[t].denominator+\"]\\r\\n\":r+=t+\" : [\"+n[t].length+\" values]\\r\\n\":r+=t+\" : \"+n[t]+\"\\r\\n\");return r},l.readFromBinaryFile=function(e){return p(e)},\"function\"==typeof define&&define.amd&&define(\"exif-js\",[],function(){return l})}).call(this);\n//# sourceMappingURL=/sm/4f4f84956292156efc04b79e1131dc7c3a51794b7b3d62b43ed5eaca1b36d71e.map"
  },
  {
    "path": "public/assets/lib/vendor/hlsjs/hls.js",
    "content": "// @ts-nocheck\nfunction getDefaultExportFromCjs (x) {\n\treturn x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;\n}\n\nvar urlToolkit = {exports: {}};\n\n(function (module, exports) {\n\t// see https://tools.ietf.org/html/rfc1808\n\n\t(function (root) {\n\t  var URL_REGEX =\n\t    /^(?=((?:[a-zA-Z0-9+\\-.]+:)?))\\1(?=((?:\\/\\/[^\\/?#]*)?))\\2(?=((?:(?:[^?#\\/]*\\/)*[^;?#\\/]*)?))\\3((?:;[^?#]*)?)(\\?[^#]*)?(#[^]*)?$/;\n\t  var FIRST_SEGMENT_REGEX = /^(?=([^\\/?#]*))\\1([^]*)$/;\n\t  var SLASH_DOT_REGEX = /(?:\\/|^)\\.(?=\\/)/g;\n\t  var SLASH_DOT_DOT_REGEX = /(?:\\/|^)\\.\\.\\/(?!\\.\\.\\/)[^\\/]*(?=\\/)/g;\n\n\t  var URLToolkit = {\n\t    // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or //\n\t    // E.g\n\t    // With opts.alwaysNormalize = false (default, spec compliant)\n\t    // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g\n\t    // With opts.alwaysNormalize = true (not spec compliant)\n\t    // http://a.com/b/cd + /e/f/../g => http://a.com/e/g\n\t    buildAbsoluteURL: function (baseURL, relativeURL, opts) {\n\t      opts = opts || {};\n\t      // remove any remaining space and CRLF\n\t      baseURL = baseURL.trim();\n\t      relativeURL = relativeURL.trim();\n\t      if (!relativeURL) {\n\t        // 2a) If the embedded URL is entirely empty, it inherits the\n\t        // entire base URL (i.e., is set equal to the base URL)\n\t        // and we are done.\n\t        if (!opts.alwaysNormalize) {\n\t          return baseURL;\n\t        }\n\t        var basePartsForNormalise = URLToolkit.parseURL(baseURL);\n\t        if (!basePartsForNormalise) {\n\t          throw new Error('Error trying to parse base URL.');\n\t        }\n\t        basePartsForNormalise.path = URLToolkit.normalizePath(\n\t          basePartsForNormalise.path\n\t        );\n\t        return URLToolkit.buildURLFromParts(basePartsForNormalise);\n\t      }\n\t      var relativeParts = URLToolkit.parseURL(relativeURL);\n\t      if (!relativeParts) {\n\t        throw new Error('Error trying to parse relative URL.');\n\t      }\n\t      if (relativeParts.scheme) {\n\t        // 2b) If the embedded URL starts with a scheme name, it is\n\t        // interpreted as an absolute URL and we are done.\n\t        if (!opts.alwaysNormalize) {\n\t          return relativeURL;\n\t        }\n\t        relativeParts.path = URLToolkit.normalizePath(relativeParts.path);\n\t        return URLToolkit.buildURLFromParts(relativeParts);\n\t      }\n\t      var baseParts = URLToolkit.parseURL(baseURL);\n\t      if (!baseParts) {\n\t        throw new Error('Error trying to parse base URL.');\n\t      }\n\t      if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {\n\t        // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc\n\t        // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'\n\t        var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);\n\t        baseParts.netLoc = pathParts[1];\n\t        baseParts.path = pathParts[2];\n\t      }\n\t      if (baseParts.netLoc && !baseParts.path) {\n\t        baseParts.path = '/';\n\t      }\n\t      var builtParts = {\n\t        // 2c) Otherwise, the embedded URL inherits the scheme of\n\t        // the base URL.\n\t        scheme: baseParts.scheme,\n\t        netLoc: relativeParts.netLoc,\n\t        path: null,\n\t        params: relativeParts.params,\n\t        query: relativeParts.query,\n\t        fragment: relativeParts.fragment,\n\t      };\n\t      if (!relativeParts.netLoc) {\n\t        // 3) If the embedded URL's <net_loc> is non-empty, we skip to\n\t        // Step 7.  Otherwise, the embedded URL inherits the <net_loc>\n\t        // (if any) of the base URL.\n\t        builtParts.netLoc = baseParts.netLoc;\n\t        // 4) If the embedded URL path is preceded by a slash \"/\", the\n\t        // path is not relative and we skip to Step 7.\n\t        if (relativeParts.path[0] !== '/') {\n\t          if (!relativeParts.path) {\n\t            // 5) If the embedded URL path is empty (and not preceded by a\n\t            // slash), then the embedded URL inherits the base URL path\n\t            builtParts.path = baseParts.path;\n\t            // 5a) if the embedded URL's <params> is non-empty, we skip to\n\t            // step 7; otherwise, it inherits the <params> of the base\n\t            // URL (if any) and\n\t            if (!relativeParts.params) {\n\t              builtParts.params = baseParts.params;\n\t              // 5b) if the embedded URL's <query> is non-empty, we skip to\n\t              // step 7; otherwise, it inherits the <query> of the base\n\t              // URL (if any) and we skip to step 7.\n\t              if (!relativeParts.query) {\n\t                builtParts.query = baseParts.query;\n\t              }\n\t            }\n\t          } else {\n\t            // 6) The last segment of the base URL's path (anything\n\t            // following the rightmost slash \"/\", or the entire path if no\n\t            // slash is present) is removed and the embedded URL's path is\n\t            // appended in its place.\n\t            var baseURLPath = baseParts.path;\n\t            var newPath =\n\t              baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) +\n\t              relativeParts.path;\n\t            builtParts.path = URLToolkit.normalizePath(newPath);\n\t          }\n\t        }\n\t      }\n\t      if (builtParts.path === null) {\n\t        builtParts.path = opts.alwaysNormalize\n\t          ? URLToolkit.normalizePath(relativeParts.path)\n\t          : relativeParts.path;\n\t      }\n\t      return URLToolkit.buildURLFromParts(builtParts);\n\t    },\n\t    parseURL: function (url) {\n\t      var parts = URL_REGEX.exec(url);\n\t      if (!parts) {\n\t        return null;\n\t      }\n\t      return {\n\t        scheme: parts[1] || '',\n\t        netLoc: parts[2] || '',\n\t        path: parts[3] || '',\n\t        params: parts[4] || '',\n\t        query: parts[5] || '',\n\t        fragment: parts[6] || '',\n\t      };\n\t    },\n\t    normalizePath: function (path) {\n\t      // The following operations are\n\t      // then applied, in order, to the new path:\n\t      // 6a) All occurrences of \"./\", where \".\" is a complete path\n\t      // segment, are removed.\n\t      // 6b) If the path ends with \".\" as a complete path segment,\n\t      // that \".\" is removed.\n\t      path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, '');\n\t      // 6c) All occurrences of \"<segment>/../\", where <segment> is a\n\t      // complete path segment not equal to \"..\", are removed.\n\t      // Removal of these path segments is performed iteratively,\n\t      // removing the leftmost matching pattern on each iteration,\n\t      // until no matching pattern remains.\n\t      // 6d) If the path ends with \"<segment>/..\", where <segment> is a\n\t      // complete path segment not equal to \"..\", that\n\t      // \"<segment>/..\" is removed.\n\t      while (\n\t        path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length\n\t      ) {}\n\t      return path.split('').reverse().join('');\n\t    },\n\t    buildURLFromParts: function (parts) {\n\t      return (\n\t        parts.scheme +\n\t        parts.netLoc +\n\t        parts.path +\n\t        parts.params +\n\t        parts.query +\n\t        parts.fragment\n\t      );\n\t    },\n\t  };\n\n\t  module.exports = URLToolkit;\n\t})();\n} (urlToolkit));\n\nvar urlToolkitExports = urlToolkit.exports;\n\nfunction ownKeys(object, enumerableOnly) {\n  var keys = Object.keys(object);\n  if (Object.getOwnPropertySymbols) {\n    var symbols = Object.getOwnPropertySymbols(object);\n    enumerableOnly && (symbols = symbols.filter(function (sym) {\n      return Object.getOwnPropertyDescriptor(object, sym).enumerable;\n    })), keys.push.apply(keys, symbols);\n  }\n  return keys;\n}\nfunction _objectSpread2(target) {\n  for (var i = 1; i < arguments.length; i++) {\n    var source = null != arguments[i] ? arguments[i] : {};\n    i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {\n      _defineProperty(target, key, source[key]);\n    }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {\n      Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));\n    });\n  }\n  return target;\n}\nfunction _defineProperty(obj, key, value) {\n  key = _toPropertyKey(key);\n  if (key in obj) {\n    Object.defineProperty(obj, key, {\n      value: value,\n      enumerable: true,\n      configurable: true,\n      writable: true\n    });\n  } else {\n    obj[key] = value;\n  }\n  return obj;\n}\nfunction _extends() {\n  _extends = Object.assign ? Object.assign.bind() : function (target) {\n    for (var i = 1; i < arguments.length; i++) {\n      var source = arguments[i];\n      for (var key in source) {\n        if (Object.prototype.hasOwnProperty.call(source, key)) {\n          target[key] = source[key];\n        }\n      }\n    }\n    return target;\n  };\n  return _extends.apply(this, arguments);\n}\nfunction _toPrimitive(input, hint) {\n  if (typeof input !== \"object\" || input === null) return input;\n  var prim = input[Symbol.toPrimitive];\n  if (prim !== undefined) {\n    var res = prim.call(input, hint || \"default\");\n    if (typeof res !== \"object\") return res;\n    throw new TypeError(\"@@toPrimitive must return a primitive value.\");\n  }\n  return (hint === \"string\" ? String : Number)(input);\n}\nfunction _toPropertyKey(arg) {\n  var key = _toPrimitive(arg, \"string\");\n  return typeof key === \"symbol\" ? key : String(key);\n}\n\n// https://caniuse.com/mdn-javascript_builtins_number_isfinite\nconst isFiniteNumber = Number.isFinite || function (value) {\n  return typeof value === 'number' && isFinite(value);\n};\n\n// https://caniuse.com/mdn-javascript_builtins_number_issafeinteger\nconst isSafeInteger = Number.isSafeInteger || function (value) {\n  return typeof value === 'number' && Math.abs(value) <= MAX_SAFE_INTEGER;\n};\nconst MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;\n\nlet Events = /*#__PURE__*/function (Events) {\n  Events[\"MEDIA_ATTACHING\"] = \"hlsMediaAttaching\";\n  Events[\"MEDIA_ATTACHED\"] = \"hlsMediaAttached\";\n  Events[\"MEDIA_DETACHING\"] = \"hlsMediaDetaching\";\n  Events[\"MEDIA_DETACHED\"] = \"hlsMediaDetached\";\n  Events[\"BUFFER_RESET\"] = \"hlsBufferReset\";\n  Events[\"BUFFER_CODECS\"] = \"hlsBufferCodecs\";\n  Events[\"BUFFER_CREATED\"] = \"hlsBufferCreated\";\n  Events[\"BUFFER_APPENDING\"] = \"hlsBufferAppending\";\n  Events[\"BUFFER_APPENDED\"] = \"hlsBufferAppended\";\n  Events[\"BUFFER_EOS\"] = \"hlsBufferEos\";\n  Events[\"BUFFER_FLUSHING\"] = \"hlsBufferFlushing\";\n  Events[\"BUFFER_FLUSHED\"] = \"hlsBufferFlushed\";\n  Events[\"MANIFEST_LOADING\"] = \"hlsManifestLoading\";\n  Events[\"MANIFEST_LOADED\"] = \"hlsManifestLoaded\";\n  Events[\"MANIFEST_PARSED\"] = \"hlsManifestParsed\";\n  Events[\"LEVEL_SWITCHING\"] = \"hlsLevelSwitching\";\n  Events[\"LEVEL_SWITCHED\"] = \"hlsLevelSwitched\";\n  Events[\"LEVEL_LOADING\"] = \"hlsLevelLoading\";\n  Events[\"LEVEL_LOADED\"] = \"hlsLevelLoaded\";\n  Events[\"LEVEL_UPDATED\"] = \"hlsLevelUpdated\";\n  Events[\"LEVEL_PTS_UPDATED\"] = \"hlsLevelPtsUpdated\";\n  Events[\"LEVELS_UPDATED\"] = \"hlsLevelsUpdated\";\n  Events[\"AUDIO_TRACKS_UPDATED\"] = \"hlsAudioTracksUpdated\";\n  Events[\"AUDIO_TRACK_SWITCHING\"] = \"hlsAudioTrackSwitching\";\n  Events[\"AUDIO_TRACK_SWITCHED\"] = \"hlsAudioTrackSwitched\";\n  Events[\"AUDIO_TRACK_LOADING\"] = \"hlsAudioTrackLoading\";\n  Events[\"AUDIO_TRACK_LOADED\"] = \"hlsAudioTrackLoaded\";\n  Events[\"SUBTITLE_TRACKS_UPDATED\"] = \"hlsSubtitleTracksUpdated\";\n  Events[\"SUBTITLE_TRACKS_CLEARED\"] = \"hlsSubtitleTracksCleared\";\n  Events[\"SUBTITLE_TRACK_SWITCH\"] = \"hlsSubtitleTrackSwitch\";\n  Events[\"SUBTITLE_TRACK_LOADING\"] = \"hlsSubtitleTrackLoading\";\n  Events[\"SUBTITLE_TRACK_LOADED\"] = \"hlsSubtitleTrackLoaded\";\n  Events[\"SUBTITLE_FRAG_PROCESSED\"] = \"hlsSubtitleFragProcessed\";\n  Events[\"CUES_PARSED\"] = \"hlsCuesParsed\";\n  Events[\"NON_NATIVE_TEXT_TRACKS_FOUND\"] = \"hlsNonNativeTextTracksFound\";\n  Events[\"INIT_PTS_FOUND\"] = \"hlsInitPtsFound\";\n  Events[\"FRAG_LOADING\"] = \"hlsFragLoading\";\n  Events[\"FRAG_LOAD_EMERGENCY_ABORTED\"] = \"hlsFragLoadEmergencyAborted\";\n  Events[\"FRAG_LOADED\"] = \"hlsFragLoaded\";\n  Events[\"FRAG_DECRYPTED\"] = \"hlsFragDecrypted\";\n  Events[\"FRAG_PARSING_INIT_SEGMENT\"] = \"hlsFragParsingInitSegment\";\n  Events[\"FRAG_PARSING_USERDATA\"] = \"hlsFragParsingUserdata\";\n  Events[\"FRAG_PARSING_METADATA\"] = \"hlsFragParsingMetadata\";\n  Events[\"FRAG_PARSED\"] = \"hlsFragParsed\";\n  Events[\"FRAG_BUFFERED\"] = \"hlsFragBuffered\";\n  Events[\"FRAG_CHANGED\"] = \"hlsFragChanged\";\n  Events[\"FPS_DROP\"] = \"hlsFpsDrop\";\n  Events[\"FPS_DROP_LEVEL_CAPPING\"] = \"hlsFpsDropLevelCapping\";\n  Events[\"ERROR\"] = \"hlsError\";\n  Events[\"DESTROYING\"] = \"hlsDestroying\";\n  Events[\"KEY_LOADING\"] = \"hlsKeyLoading\";\n  Events[\"KEY_LOADED\"] = \"hlsKeyLoaded\";\n  Events[\"LIVE_BACK_BUFFER_REACHED\"] = \"hlsLiveBackBufferReached\";\n  Events[\"BACK_BUFFER_REACHED\"] = \"hlsBackBufferReached\";\n  return Events;\n}({});\n\n/**\n * Defines each Event type and payload by Event name. Used in {@link hls.js#HlsEventEmitter} to strongly type the event listener API.\n */\n\nlet ErrorTypes = /*#__PURE__*/function (ErrorTypes) {\n  ErrorTypes[\"NETWORK_ERROR\"] = \"networkError\";\n  ErrorTypes[\"MEDIA_ERROR\"] = \"mediaError\";\n  ErrorTypes[\"KEY_SYSTEM_ERROR\"] = \"keySystemError\";\n  ErrorTypes[\"MUX_ERROR\"] = \"muxError\";\n  ErrorTypes[\"OTHER_ERROR\"] = \"otherError\";\n  return ErrorTypes;\n}({});\nlet ErrorDetails = /*#__PURE__*/function (ErrorDetails) {\n  ErrorDetails[\"KEY_SYSTEM_NO_KEYS\"] = \"keySystemNoKeys\";\n  ErrorDetails[\"KEY_SYSTEM_NO_ACCESS\"] = \"keySystemNoAccess\";\n  ErrorDetails[\"KEY_SYSTEM_NO_SESSION\"] = \"keySystemNoSession\";\n  ErrorDetails[\"KEY_SYSTEM_NO_CONFIGURED_LICENSE\"] = \"keySystemNoConfiguredLicense\";\n  ErrorDetails[\"KEY_SYSTEM_LICENSE_REQUEST_FAILED\"] = \"keySystemLicenseRequestFailed\";\n  ErrorDetails[\"KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED\"] = \"keySystemServerCertificateRequestFailed\";\n  ErrorDetails[\"KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED\"] = \"keySystemServerCertificateUpdateFailed\";\n  ErrorDetails[\"KEY_SYSTEM_SESSION_UPDATE_FAILED\"] = \"keySystemSessionUpdateFailed\";\n  ErrorDetails[\"KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED\"] = \"keySystemStatusOutputRestricted\";\n  ErrorDetails[\"KEY_SYSTEM_STATUS_INTERNAL_ERROR\"] = \"keySystemStatusInternalError\";\n  ErrorDetails[\"MANIFEST_LOAD_ERROR\"] = \"manifestLoadError\";\n  ErrorDetails[\"MANIFEST_LOAD_TIMEOUT\"] = \"manifestLoadTimeOut\";\n  ErrorDetails[\"MANIFEST_PARSING_ERROR\"] = \"manifestParsingError\";\n  ErrorDetails[\"MANIFEST_INCOMPATIBLE_CODECS_ERROR\"] = \"manifestIncompatibleCodecsError\";\n  ErrorDetails[\"LEVEL_EMPTY_ERROR\"] = \"levelEmptyError\";\n  ErrorDetails[\"LEVEL_LOAD_ERROR\"] = \"levelLoadError\";\n  ErrorDetails[\"LEVEL_LOAD_TIMEOUT\"] = \"levelLoadTimeOut\";\n  ErrorDetails[\"LEVEL_PARSING_ERROR\"] = \"levelParsingError\";\n  ErrorDetails[\"LEVEL_SWITCH_ERROR\"] = \"levelSwitchError\";\n  ErrorDetails[\"AUDIO_TRACK_LOAD_ERROR\"] = \"audioTrackLoadError\";\n  ErrorDetails[\"AUDIO_TRACK_LOAD_TIMEOUT\"] = \"audioTrackLoadTimeOut\";\n  ErrorDetails[\"SUBTITLE_LOAD_ERROR\"] = \"subtitleTrackLoadError\";\n  ErrorDetails[\"SUBTITLE_TRACK_LOAD_TIMEOUT\"] = \"subtitleTrackLoadTimeOut\";\n  ErrorDetails[\"FRAG_LOAD_ERROR\"] = \"fragLoadError\";\n  ErrorDetails[\"FRAG_LOAD_TIMEOUT\"] = \"fragLoadTimeOut\";\n  ErrorDetails[\"FRAG_DECRYPT_ERROR\"] = \"fragDecryptError\";\n  ErrorDetails[\"FRAG_PARSING_ERROR\"] = \"fragParsingError\";\n  ErrorDetails[\"FRAG_GAP\"] = \"fragGap\";\n  ErrorDetails[\"REMUX_ALLOC_ERROR\"] = \"remuxAllocError\";\n  ErrorDetails[\"KEY_LOAD_ERROR\"] = \"keyLoadError\";\n  ErrorDetails[\"KEY_LOAD_TIMEOUT\"] = \"keyLoadTimeOut\";\n  ErrorDetails[\"BUFFER_ADD_CODEC_ERROR\"] = \"bufferAddCodecError\";\n  ErrorDetails[\"BUFFER_INCOMPATIBLE_CODECS_ERROR\"] = \"bufferIncompatibleCodecsError\";\n  ErrorDetails[\"BUFFER_APPEND_ERROR\"] = \"bufferAppendError\";\n  ErrorDetails[\"BUFFER_APPENDING_ERROR\"] = \"bufferAppendingError\";\n  ErrorDetails[\"BUFFER_STALLED_ERROR\"] = \"bufferStalledError\";\n  ErrorDetails[\"BUFFER_FULL_ERROR\"] = \"bufferFullError\";\n  ErrorDetails[\"BUFFER_SEEK_OVER_HOLE\"] = \"bufferSeekOverHole\";\n  ErrorDetails[\"BUFFER_NUDGE_ON_STALL\"] = \"bufferNudgeOnStall\";\n  ErrorDetails[\"INTERNAL_EXCEPTION\"] = \"internalException\";\n  ErrorDetails[\"INTERNAL_ABORTED\"] = \"aborted\";\n  ErrorDetails[\"UNKNOWN\"] = \"unknown\";\n  return ErrorDetails;\n}({});\n\nconst noop = function noop() {};\nconst fakeLogger = {\n  trace: noop,\n  debug: noop,\n  log: noop,\n  warn: noop,\n  info: noop,\n  error: noop\n};\nlet exportedLogger = fakeLogger;\n\n// let lastCallTime;\n// function formatMsgWithTimeInfo(type, msg) {\n//   const now = Date.now();\n//   const diff = lastCallTime ? '+' + (now - lastCallTime) : '0';\n//   lastCallTime = now;\n//   msg = (new Date(now)).toISOString() + ' | [' +  type + '] > ' + msg + ' ( ' + diff + ' ms )';\n//   return msg;\n// }\n\nfunction consolePrintFn(type) {\n  const func = self.console[type];\n  if (func) {\n    return func.bind(self.console, `[${type}] >`);\n  }\n  return noop;\n}\nfunction exportLoggerFunctions(debugConfig, ...functions) {\n  functions.forEach(function (type) {\n    exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type);\n  });\n}\nfunction enableLogs(debugConfig, id) {\n  // check that console is available\n  if (self.console && debugConfig === true || typeof debugConfig === 'object') {\n    exportLoggerFunctions(debugConfig,\n    // Remove out from list here to hard-disable a log-level\n    // 'trace',\n    'debug', 'log', 'info', 'warn', 'error');\n    // Some browsers don't allow to use bind on console object anyway\n    // fallback to default if needed\n    try {\n      exportedLogger.log(`Debug logs enabled for \"${id}\" in hls.js version ${\"1.4.13\"}`);\n    } catch (e) {\n      exportedLogger = fakeLogger;\n    }\n  } else {\n    exportedLogger = fakeLogger;\n  }\n}\nconst logger = exportedLogger;\n\nconst DECIMAL_RESOLUTION_REGEX = /^(\\d+)x(\\d+)$/;\nconst ATTR_LIST_REGEX = /(.+?)=(\".*?\"|.*?)(?:,|$)/g;\n\n// adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js\nclass AttrList {\n  constructor(attrs) {\n    if (typeof attrs === 'string') {\n      attrs = AttrList.parseAttrList(attrs);\n    }\n    for (const attr in attrs) {\n      if (attrs.hasOwnProperty(attr)) {\n        if (attr.substring(0, 2) === 'X-') {\n          this.clientAttrs = this.clientAttrs || [];\n          this.clientAttrs.push(attr);\n        }\n        this[attr] = attrs[attr];\n      }\n    }\n  }\n  decimalInteger(attrName) {\n    const intValue = parseInt(this[attrName], 10);\n    if (intValue > Number.MAX_SAFE_INTEGER) {\n      return Infinity;\n    }\n    return intValue;\n  }\n  hexadecimalInteger(attrName) {\n    if (this[attrName]) {\n      let stringValue = (this[attrName] || '0x').slice(2);\n      stringValue = (stringValue.length & 1 ? '0' : '') + stringValue;\n      const value = new Uint8Array(stringValue.length / 2);\n      for (let i = 0; i < stringValue.length / 2; i++) {\n        value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16);\n      }\n      return value;\n    } else {\n      return null;\n    }\n  }\n  hexadecimalIntegerAsNumber(attrName) {\n    const intValue = parseInt(this[attrName], 16);\n    if (intValue > Number.MAX_SAFE_INTEGER) {\n      return Infinity;\n    }\n    return intValue;\n  }\n  decimalFloatingPoint(attrName) {\n    return parseFloat(this[attrName]);\n  }\n  optionalFloat(attrName, defaultValue) {\n    const value = this[attrName];\n    return value ? parseFloat(value) : defaultValue;\n  }\n  enumeratedString(attrName) {\n    return this[attrName];\n  }\n  bool(attrName) {\n    return this[attrName] === 'YES';\n  }\n  decimalResolution(attrName) {\n    const res = DECIMAL_RESOLUTION_REGEX.exec(this[attrName]);\n    if (res === null) {\n      return undefined;\n    }\n    return {\n      width: parseInt(res[1], 10),\n      height: parseInt(res[2], 10)\n    };\n  }\n  static parseAttrList(input) {\n    let match;\n    const attrs = {};\n    const quote = '\"';\n    ATTR_LIST_REGEX.lastIndex = 0;\n    while ((match = ATTR_LIST_REGEX.exec(input)) !== null) {\n      let value = match[2];\n      if (value.indexOf(quote) === 0 && value.lastIndexOf(quote) === value.length - 1) {\n        value = value.slice(1, -1);\n      }\n      const name = match[1].trim();\n      attrs[name] = value;\n    }\n    return attrs;\n  }\n}\n\n// Avoid exporting const enum so that these values can be inlined\n\nfunction isDateRangeCueAttribute(attrName) {\n  return attrName !== \"ID\" && attrName !== \"CLASS\" && attrName !== \"START-DATE\" && attrName !== \"DURATION\" && attrName !== \"END-DATE\" && attrName !== \"END-ON-NEXT\";\n}\nfunction isSCTE35Attribute(attrName) {\n  return attrName === \"SCTE35-OUT\" || attrName === \"SCTE35-IN\";\n}\nclass DateRange {\n  constructor(dateRangeAttr, dateRangeWithSameId) {\n    this.attr = void 0;\n    this._startDate = void 0;\n    this._endDate = void 0;\n    this._badValueForSameId = void 0;\n    if (dateRangeWithSameId) {\n      const previousAttr = dateRangeWithSameId.attr;\n      for (const key in previousAttr) {\n        if (Object.prototype.hasOwnProperty.call(dateRangeAttr, key) && dateRangeAttr[key] !== previousAttr[key]) {\n          logger.warn(`DATERANGE tag attribute: \"${key}\" does not match for tags with ID: \"${dateRangeAttr.ID}\"`);\n          this._badValueForSameId = key;\n          break;\n        }\n      }\n      // Merge DateRange tags with the same ID\n      dateRangeAttr = _extends(new AttrList({}), previousAttr, dateRangeAttr);\n    }\n    this.attr = dateRangeAttr;\n    this._startDate = new Date(dateRangeAttr[\"START-DATE\"]);\n    if (\"END-DATE\" in this.attr) {\n      const endDate = new Date(this.attr[\"END-DATE\"]);\n      if (isFiniteNumber(endDate.getTime())) {\n        this._endDate = endDate;\n      }\n    }\n  }\n  get id() {\n    return this.attr.ID;\n  }\n  get class() {\n    return this.attr.CLASS;\n  }\n  get startDate() {\n    return this._startDate;\n  }\n  get endDate() {\n    if (this._endDate) {\n      return this._endDate;\n    }\n    const duration = this.duration;\n    if (duration !== null) {\n      return new Date(this._startDate.getTime() + duration * 1000);\n    }\n    return null;\n  }\n  get duration() {\n    if (\"DURATION\" in this.attr) {\n      const duration = this.attr.decimalFloatingPoint(\"DURATION\");\n      if (isFiniteNumber(duration)) {\n        return duration;\n      }\n    } else if (this._endDate) {\n      return (this._endDate.getTime() - this._startDate.getTime()) / 1000;\n    }\n    return null;\n  }\n  get plannedDuration() {\n    if (\"PLANNED-DURATION\" in this.attr) {\n      return this.attr.decimalFloatingPoint(\"PLANNED-DURATION\");\n    }\n    return null;\n  }\n  get endOnNext() {\n    return this.attr.bool(\"END-ON-NEXT\");\n  }\n  get isValid() {\n    return !!this.id && !this._badValueForSameId && isFiniteNumber(this.startDate.getTime()) && (this.duration === null || this.duration >= 0) && (!this.endOnNext || !!this.class);\n  }\n}\n\nclass LoadStats {\n  constructor() {\n    this.aborted = false;\n    this.loaded = 0;\n    this.retry = 0;\n    this.total = 0;\n    this.chunkCount = 0;\n    this.bwEstimate = 0;\n    this.loading = {\n      start: 0,\n      first: 0,\n      end: 0\n    };\n    this.parsing = {\n      start: 0,\n      end: 0\n    };\n    this.buffering = {\n      start: 0,\n      first: 0,\n      end: 0\n    };\n  }\n}\n\nvar ElementaryStreamTypes = {\n  AUDIO: \"audio\",\n  VIDEO: \"video\",\n  AUDIOVIDEO: \"audiovideo\"\n};\nclass BaseSegment {\n  // baseurl is the URL to the playlist\n\n  // relurl is the portion of the URL that comes from inside the playlist.\n\n  // Holds the types of data this fragment supports\n\n  constructor(baseurl) {\n    this._byteRange = null;\n    this._url = null;\n    this.baseurl = void 0;\n    this.relurl = void 0;\n    this.elementaryStreams = {\n      [ElementaryStreamTypes.AUDIO]: null,\n      [ElementaryStreamTypes.VIDEO]: null,\n      [ElementaryStreamTypes.AUDIOVIDEO]: null\n    };\n    this.baseurl = baseurl;\n  }\n\n  // setByteRange converts a EXT-X-BYTERANGE attribute into a two element array\n  setByteRange(value, previous) {\n    const params = value.split('@', 2);\n    const byteRange = [];\n    if (params.length === 1) {\n      byteRange[0] = previous ? previous.byteRangeEndOffset : 0;\n    } else {\n      byteRange[0] = parseInt(params[1]);\n    }\n    byteRange[1] = parseInt(params[0]) + byteRange[0];\n    this._byteRange = byteRange;\n  }\n  get byteRange() {\n    if (!this._byteRange) {\n      return [];\n    }\n    return this._byteRange;\n  }\n  get byteRangeStartOffset() {\n    return this.byteRange[0];\n  }\n  get byteRangeEndOffset() {\n    return this.byteRange[1];\n  }\n  get url() {\n    if (!this._url && this.baseurl && this.relurl) {\n      this._url = urlToolkitExports.buildAbsoluteURL(this.baseurl, this.relurl, {\n        alwaysNormalize: true\n      });\n    }\n    return this._url || '';\n  }\n  set url(value) {\n    this._url = value;\n  }\n}\n\n/**\n * Object representing parsed data from an HLS Segment. Found in {@link hls.js#LevelDetails.fragments}.\n */\nclass Fragment extends BaseSegment {\n  // EXTINF has to be present for a m3u8 to be considered valid\n\n  // sn notates the sequence number for a segment, and if set to a string can be 'initSegment'\n\n  // levelkeys are the EXT-X-KEY tags that apply to this segment for decryption\n  // core difference from the private field _decryptdata is the lack of the initialized IV\n  // _decryptdata will set the IV for this segment based on the segment number in the fragment\n  // A string representing the fragment type\n  // A reference to the loader. Set while the fragment is loading, and removed afterwards. Used to abort fragment loading\n  // A reference to the key loader. Set while the key is loading, and removed afterwards. Used to abort key loading\n  // The level/track index to which the fragment belongs\n  // The continuity counter of the fragment\n  // The starting Presentation Time Stamp (PTS) of the fragment. Set after transmux complete.\n  // The ending Presentation Time Stamp (PTS) of the fragment. Set after transmux complete.\n  // The starting Decode Time Stamp (DTS) of the fragment. Set after transmux complete.\n  // The ending Decode Time Stamp (DTS) of the fragment. Set after transmux complete.\n  // The start time of the fragment, as listed in the manifest. Updated after transmux complete.\n  // Set by `updateFragPTSDTS` in level-helper\n  // The maximum starting Presentation Time Stamp (audio/video PTS) of the fragment. Set after transmux complete.\n  // The minimum ending Presentation Time Stamp (audio/video PTS) of the fragment. Set after transmux complete.\n  // Load/parse timing information\n  // A flag indicating whether the segment was downloaded in order to test bitrate, and was not buffered\n  // #EXTINF  segment title\n  // The Media Initialization Section for this segment\n  // Fragment is the last fragment in the media playlist\n  // Fragment is marked by an EXT-X-GAP tag indicating that it does not contain media data and should not be loaded\n  constructor(type, baseurl) {\n    super(baseurl);\n    this._decryptdata = null;\n    this.rawProgramDateTime = null;\n    this.programDateTime = null;\n    this.tagList = [];\n    this.duration = 0;\n    this.sn = 0;\n    this.levelkeys = void 0;\n    this.type = void 0;\n    this.loader = null;\n    this.keyLoader = null;\n    this.level = -1;\n    this.cc = 0;\n    this.startPTS = void 0;\n    this.endPTS = void 0;\n    this.startDTS = void 0;\n    this.endDTS = void 0;\n    this.start = 0;\n    this.deltaPTS = void 0;\n    this.maxStartPTS = void 0;\n    this.minEndPTS = void 0;\n    this.stats = new LoadStats();\n    this.urlId = 0;\n    this.data = void 0;\n    this.bitrateTest = false;\n    this.title = null;\n    this.initSegment = null;\n    this.endList = void 0;\n    this.gap = void 0;\n    this.type = type;\n  }\n  get decryptdata() {\n    const {\n      levelkeys\n    } = this;\n    if (!levelkeys && !this._decryptdata) {\n      return null;\n    }\n    if (!this._decryptdata && this.levelkeys && !this.levelkeys.NONE) {\n      const key = this.levelkeys.identity;\n      if (key) {\n        this._decryptdata = key.getDecryptData(this.sn);\n      } else {\n        const keyFormats = Object.keys(this.levelkeys);\n        if (keyFormats.length === 1) {\n          return this._decryptdata = this.levelkeys[keyFormats[0]].getDecryptData(this.sn);\n        }\n      }\n    }\n    return this._decryptdata;\n  }\n  get end() {\n    return this.start + this.duration;\n  }\n  get endProgramDateTime() {\n    if (this.programDateTime === null) {\n      return null;\n    }\n    if (!isFiniteNumber(this.programDateTime)) {\n      return null;\n    }\n    const duration = !isFiniteNumber(this.duration) ? 0 : this.duration;\n    return this.programDateTime + duration * 1000;\n  }\n  get encrypted() {\n    var _this$_decryptdata;\n    // At the m3u8-parser level we need to add support for manifest signalled keyformats\n    // when we want the fragment to start reporting that it is encrypted.\n    // Currently, keyFormat will only be set for identity keys\n    if ((_this$_decryptdata = this._decryptdata) != null && _this$_decryptdata.encrypted) {\n      return true;\n    } else if (this.levelkeys) {\n      const keyFormats = Object.keys(this.levelkeys);\n      const len = keyFormats.length;\n      if (len > 1 || len === 1 && this.levelkeys[keyFormats[0]].encrypted) {\n        return true;\n      }\n    }\n    return false;\n  }\n  setKeyFormat(keyFormat) {\n    if (this.levelkeys) {\n      const key = this.levelkeys[keyFormat];\n      if (key && !this._decryptdata) {\n        this._decryptdata = key.getDecryptData(this.sn);\n      }\n    }\n  }\n  abortRequests() {\n    var _this$loader, _this$keyLoader;\n    (_this$loader = this.loader) == null ? void 0 : _this$loader.abort();\n    (_this$keyLoader = this.keyLoader) == null ? void 0 : _this$keyLoader.abort();\n  }\n  setElementaryStreamInfo(type, startPTS, endPTS, startDTS, endDTS, partial = false) {\n    const {\n      elementaryStreams\n    } = this;\n    const info = elementaryStreams[type];\n    if (!info) {\n      elementaryStreams[type] = {\n        startPTS,\n        endPTS,\n        startDTS,\n        endDTS,\n        partial\n      };\n      return;\n    }\n    info.startPTS = Math.min(info.startPTS, startPTS);\n    info.endPTS = Math.max(info.endPTS, endPTS);\n    info.startDTS = Math.min(info.startDTS, startDTS);\n    info.endDTS = Math.max(info.endDTS, endDTS);\n  }\n  clearElementaryStreamInfo() {\n    const {\n      elementaryStreams\n    } = this;\n    elementaryStreams[ElementaryStreamTypes.AUDIO] = null;\n    elementaryStreams[ElementaryStreamTypes.VIDEO] = null;\n    elementaryStreams[ElementaryStreamTypes.AUDIOVIDEO] = null;\n  }\n}\n\n/**\n * Object representing parsed data from an HLS Partial Segment. Found in {@link hls.js#LevelDetails.partList}.\n */\nclass Part extends BaseSegment {\n  constructor(partAttrs, frag, baseurl, index, previous) {\n    super(baseurl);\n    this.fragOffset = 0;\n    this.duration = 0;\n    this.gap = false;\n    this.independent = false;\n    this.relurl = void 0;\n    this.fragment = void 0;\n    this.index = void 0;\n    this.stats = new LoadStats();\n    this.duration = partAttrs.decimalFloatingPoint('DURATION');\n    this.gap = partAttrs.bool('GAP');\n    this.independent = partAttrs.bool('INDEPENDENT');\n    this.relurl = partAttrs.enumeratedString('URI');\n    this.fragment = frag;\n    this.index = index;\n    const byteRange = partAttrs.enumeratedString('BYTERANGE');\n    if (byteRange) {\n      this.setByteRange(byteRange, previous);\n    }\n    if (previous) {\n      this.fragOffset = previous.fragOffset + previous.duration;\n    }\n  }\n  get start() {\n    return this.fragment.start + this.fragOffset;\n  }\n  get end() {\n    return this.start + this.duration;\n  }\n  get loaded() {\n    const {\n      elementaryStreams\n    } = this;\n    return !!(elementaryStreams.audio || elementaryStreams.video || elementaryStreams.audiovideo);\n  }\n}\n\nconst DEFAULT_TARGET_DURATION = 10;\n\n/**\n * Object representing parsed data from an HLS Media Playlist. Found in {@link hls.js#Level.details}.\n */\nclass LevelDetails {\n  // Manifest reload synchronization\n\n  constructor(baseUrl) {\n    this.PTSKnown = false;\n    this.alignedSliding = false;\n    this.averagetargetduration = void 0;\n    this.endCC = 0;\n    this.endSN = 0;\n    this.fragments = void 0;\n    this.fragmentHint = void 0;\n    this.partList = null;\n    this.dateRanges = void 0;\n    this.live = true;\n    this.ageHeader = 0;\n    this.advancedDateTime = void 0;\n    this.updated = true;\n    this.advanced = true;\n    this.availabilityDelay = void 0;\n    this.misses = 0;\n    this.startCC = 0;\n    this.startSN = 0;\n    this.startTimeOffset = null;\n    this.targetduration = 0;\n    this.totalduration = 0;\n    this.type = null;\n    this.url = void 0;\n    this.m3u8 = '';\n    this.version = null;\n    this.canBlockReload = false;\n    this.canSkipUntil = 0;\n    this.canSkipDateRanges = false;\n    this.skippedSegments = 0;\n    this.recentlyRemovedDateranges = void 0;\n    this.partHoldBack = 0;\n    this.holdBack = 0;\n    this.partTarget = 0;\n    this.preloadHint = void 0;\n    this.renditionReports = void 0;\n    this.tuneInGoal = 0;\n    this.deltaUpdateFailed = void 0;\n    this.driftStartTime = 0;\n    this.driftEndTime = 0;\n    this.driftStart = 0;\n    this.driftEnd = 0;\n    this.encryptedFragments = void 0;\n    this.playlistParsingError = null;\n    this.variableList = null;\n    this.hasVariableRefs = false;\n    this.fragments = [];\n    this.encryptedFragments = [];\n    this.dateRanges = {};\n    this.url = baseUrl;\n  }\n  reloaded(previous) {\n    if (!previous) {\n      this.advanced = true;\n      this.updated = true;\n      return;\n    }\n    const partSnDiff = this.lastPartSn - previous.lastPartSn;\n    const partIndexDiff = this.lastPartIndex - previous.lastPartIndex;\n    this.updated = this.endSN !== previous.endSN || !!partIndexDiff || !!partSnDiff || !this.live;\n    this.advanced = this.endSN > previous.endSN || partSnDiff > 0 || partSnDiff === 0 && partIndexDiff > 0;\n    if (this.updated || this.advanced) {\n      this.misses = Math.floor(previous.misses * 0.6);\n    } else {\n      this.misses = previous.misses + 1;\n    }\n    this.availabilityDelay = previous.availabilityDelay;\n  }\n  get hasProgramDateTime() {\n    if (this.fragments.length) {\n      return isFiniteNumber(this.fragments[this.fragments.length - 1].programDateTime);\n    }\n    return false;\n  }\n  get levelTargetDuration() {\n    return this.averagetargetduration || this.targetduration || DEFAULT_TARGET_DURATION;\n  }\n  get drift() {\n    const runTime = this.driftEndTime - this.driftStartTime;\n    if (runTime > 0) {\n      const runDuration = this.driftEnd - this.driftStart;\n      return runDuration * 1000 / runTime;\n    }\n    return 1;\n  }\n  get edge() {\n    return this.partEnd || this.fragmentEnd;\n  }\n  get partEnd() {\n    var _this$partList;\n    if ((_this$partList = this.partList) != null && _this$partList.length) {\n      return this.partList[this.partList.length - 1].end;\n    }\n    return this.fragmentEnd;\n  }\n  get fragmentEnd() {\n    var _this$fragments;\n    if ((_this$fragments = this.fragments) != null && _this$fragments.length) {\n      return this.fragments[this.fragments.length - 1].end;\n    }\n    return 0;\n  }\n  get age() {\n    if (this.advancedDateTime) {\n      return Math.max(Date.now() - this.advancedDateTime, 0) / 1000;\n    }\n    return 0;\n  }\n  get lastPartIndex() {\n    var _this$partList2;\n    if ((_this$partList2 = this.partList) != null && _this$partList2.length) {\n      return this.partList[this.partList.length - 1].index;\n    }\n    return -1;\n  }\n  get lastPartSn() {\n    var _this$partList3;\n    if ((_this$partList3 = this.partList) != null && _this$partList3.length) {\n      return this.partList[this.partList.length - 1].fragment.sn;\n    }\n    return this.endSN;\n  }\n}\n\nfunction base64Decode(base64encodedStr) {\n  return Uint8Array.from(atob(base64encodedStr), c => c.charCodeAt(0));\n}\n\nfunction getKeyIdBytes(str) {\n  const keyIdbytes = strToUtf8array(str).subarray(0, 16);\n  const paddedkeyIdbytes = new Uint8Array(16);\n  paddedkeyIdbytes.set(keyIdbytes, 16 - keyIdbytes.length);\n  return paddedkeyIdbytes;\n}\nfunction changeEndianness(keyId) {\n  const swap = function swap(array, from, to) {\n    const cur = array[from];\n    array[from] = array[to];\n    array[to] = cur;\n  };\n  swap(keyId, 0, 3);\n  swap(keyId, 1, 2);\n  swap(keyId, 4, 5);\n  swap(keyId, 6, 7);\n}\nfunction convertDataUriToArrayBytes(uri) {\n  // data:[<media type][;attribute=value][;base64],<data>\n  const colonsplit = uri.split(':');\n  let keydata = null;\n  if (colonsplit[0] === 'data' && colonsplit.length === 2) {\n    const semicolonsplit = colonsplit[1].split(';');\n    const commasplit = semicolonsplit[semicolonsplit.length - 1].split(',');\n    if (commasplit.length === 2) {\n      const isbase64 = commasplit[0] === 'base64';\n      const data = commasplit[1];\n      if (isbase64) {\n        semicolonsplit.splice(-1, 1); // remove from processing\n        keydata = base64Decode(data);\n      } else {\n        keydata = getKeyIdBytes(data);\n      }\n    }\n  }\n  return keydata;\n}\nfunction strToUtf8array(str) {\n  return Uint8Array.from(unescape(encodeURIComponent(str)), c => c.charCodeAt(0));\n}\n\n/**\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMediaKeySystemAccess\n */\nvar KeySystems = {\n  CLEARKEY: \"org.w3.clearkey\",\n  FAIRPLAY: \"com.apple.fps\",\n  PLAYREADY: \"com.microsoft.playready\",\n  WIDEVINE: \"com.widevine.alpha\"\n};\n\n// Playlist #EXT-X-KEY KEYFORMAT values\nvar KeySystemFormats = {\n  CLEARKEY: \"org.w3.clearkey\",\n  FAIRPLAY: \"com.apple.streamingkeydelivery\",\n  PLAYREADY: \"com.microsoft.playready\",\n  WIDEVINE: \"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\"\n};\nfunction keySystemFormatToKeySystemDomain(format) {\n  switch (format) {\n    case KeySystemFormats.FAIRPLAY:\n      return KeySystems.FAIRPLAY;\n    case KeySystemFormats.PLAYREADY:\n      return KeySystems.PLAYREADY;\n    case KeySystemFormats.WIDEVINE:\n      return KeySystems.WIDEVINE;\n    case KeySystemFormats.CLEARKEY:\n      return KeySystems.CLEARKEY;\n  }\n}\n\n// System IDs for which we can extract a key ID from \"encrypted\" event PSSH\nvar KeySystemIds = {\n  WIDEVINE: \"edef8ba979d64acea3c827dcd51d21ed\"\n};\nfunction keySystemIdToKeySystemDomain(systemId) {\n  if (systemId === KeySystemIds.WIDEVINE) {\n    return KeySystems.WIDEVINE;\n    // } else if (systemId === KeySystemIds.PLAYREADY) {\n    //   return KeySystems.PLAYREADY;\n    // } else if (systemId === KeySystemIds.CENC || systemId === KeySystemIds.CLEARKEY) {\n    //   return KeySystems.CLEARKEY;\n  }\n}\n\nfunction keySystemDomainToKeySystemFormat(keySystem) {\n  switch (keySystem) {\n    case KeySystems.FAIRPLAY:\n      return KeySystemFormats.FAIRPLAY;\n    case KeySystems.PLAYREADY:\n      return KeySystemFormats.PLAYREADY;\n    case KeySystems.WIDEVINE:\n      return KeySystemFormats.WIDEVINE;\n    case KeySystems.CLEARKEY:\n      return KeySystemFormats.CLEARKEY;\n  }\n}\nfunction getKeySystemsForConfig(config) {\n  const {\n    drmSystems,\n    widevineLicenseUrl\n  } = config;\n  const keySystemsToAttempt = drmSystems ? [KeySystems.FAIRPLAY, KeySystems.WIDEVINE, KeySystems.PLAYREADY, KeySystems.CLEARKEY].filter(keySystem => !!drmSystems[keySystem]) : [];\n  if (!keySystemsToAttempt[KeySystems.WIDEVINE] && widevineLicenseUrl) {\n    keySystemsToAttempt.push(KeySystems.WIDEVINE);\n  }\n  return keySystemsToAttempt;\n}\nconst requestMediaKeySystemAccess = function () {\n  if (typeof self !== 'undefined' && self.navigator && self.navigator.requestMediaKeySystemAccess) {\n    return self.navigator.requestMediaKeySystemAccess.bind(self.navigator);\n  } else {\n    return null;\n  }\n}();\n\n/**\n * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaKeySystemConfiguration\n */\nfunction getSupportedMediaKeySystemConfigurations(keySystem, audioCodecs, videoCodecs, drmSystemOptions) {\n  let initDataTypes;\n  switch (keySystem) {\n    case KeySystems.FAIRPLAY:\n      initDataTypes = ['cenc', 'sinf'];\n      break;\n    case KeySystems.WIDEVINE:\n    case KeySystems.PLAYREADY:\n      initDataTypes = ['cenc'];\n      break;\n    case KeySystems.CLEARKEY:\n      initDataTypes = ['cenc', 'keyids'];\n      break;\n    default:\n      throw new Error(`Unknown key-system: ${keySystem}`);\n  }\n  return createMediaKeySystemConfigurations(initDataTypes, audioCodecs, videoCodecs, drmSystemOptions);\n}\nfunction createMediaKeySystemConfigurations(initDataTypes, audioCodecs, videoCodecs, drmSystemOptions) {\n  const baseConfig = {\n    initDataTypes: initDataTypes,\n    persistentState: drmSystemOptions.persistentState || 'not-allowed',\n    distinctiveIdentifier: drmSystemOptions.distinctiveIdentifier || 'not-allowed',\n    sessionTypes: drmSystemOptions.sessionTypes || [drmSystemOptions.sessionType || 'temporary'],\n    audioCapabilities: audioCodecs.map(codec => ({\n      contentType: `audio/mp4; codecs=\"${codec}\"`,\n      robustness: drmSystemOptions.audioRobustness || '',\n      encryptionScheme: drmSystemOptions.audioEncryptionScheme || null\n    })),\n    videoCapabilities: videoCodecs.map(codec => ({\n      contentType: `video/mp4; codecs=\"${codec}\"`,\n      robustness: drmSystemOptions.videoRobustness || '',\n      encryptionScheme: drmSystemOptions.videoEncryptionScheme || null\n    }))\n  };\n  return [baseConfig];\n}\n\nfunction sliceUint8(array, start, end) {\n  // @ts-expect-error This polyfills IE11 usage of Uint8Array slice.\n  // It always exists in the TypeScript definition so fails, but it fails at runtime on IE11.\n  return Uint8Array.prototype.slice ? array.slice(start, end) : new Uint8Array(Array.prototype.slice.call(array, start, end));\n}\n\n// breaking up those two types in order to clarify what is happening in the decoding path.\n\n/**\n * Returns true if an ID3 header can be found at offset in data\n * @param data - The data to search\n * @param offset - The offset at which to start searching\n */\nconst isHeader$2 = (data, offset) => {\n  /*\n   * http://id3.org/id3v2.3.0\n   * [0]     = 'I'\n   * [1]     = 'D'\n   * [2]     = '3'\n   * [3,4]   = {Version}\n   * [5]     = {Flags}\n   * [6-9]   = {ID3 Size}\n   *\n   * An ID3v2 tag can be detected with the following pattern:\n   *  $49 44 33 yy yy xx zz zz zz zz\n   * Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80\n   */\n  if (offset + 10 <= data.length) {\n    // look for 'ID3' identifier\n    if (data[offset] === 0x49 && data[offset + 1] === 0x44 && data[offset + 2] === 0x33) {\n      // check version is within range\n      if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) {\n        // check size is within range\n        if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) {\n          return true;\n        }\n      }\n    }\n  }\n  return false;\n};\n\n/**\n * Returns true if an ID3 footer can be found at offset in data\n * @param data - The data to search\n * @param offset - The offset at which to start searching\n */\nconst isFooter = (data, offset) => {\n  /*\n   * The footer is a copy of the header, but with a different identifier\n   */\n  if (offset + 10 <= data.length) {\n    // look for '3DI' identifier\n    if (data[offset] === 0x33 && data[offset + 1] === 0x44 && data[offset + 2] === 0x49) {\n      // check version is within range\n      if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) {\n        // check size is within range\n        if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) {\n          return true;\n        }\n      }\n    }\n  }\n  return false;\n};\n\n/**\n * Returns any adjacent ID3 tags found in data starting at offset, as one block of data\n * @param data - The data to search in\n * @param offset - The offset at which to start searching\n * @returns the block of data containing any ID3 tags found\n * or *undefined* if no header is found at the starting offset\n */\nconst getID3Data = (data, offset) => {\n  const front = offset;\n  let length = 0;\n  while (isHeader$2(data, offset)) {\n    // ID3 header is 10 bytes\n    length += 10;\n    const size = readSize(data, offset + 6);\n    length += size;\n    if (isFooter(data, offset + 10)) {\n      // ID3 footer is 10 bytes\n      length += 10;\n    }\n    offset += length;\n  }\n  if (length > 0) {\n    return data.subarray(front, front + length);\n  }\n  return undefined;\n};\nconst readSize = (data, offset) => {\n  let size = 0;\n  size = (data[offset] & 0x7f) << 21;\n  size |= (data[offset + 1] & 0x7f) << 14;\n  size |= (data[offset + 2] & 0x7f) << 7;\n  size |= data[offset + 3] & 0x7f;\n  return size;\n};\nconst canParse$2 = (data, offset) => {\n  return isHeader$2(data, offset) && readSize(data, offset + 6) + 10 <= data.length - offset;\n};\n\n/**\n * Searches for the Elementary Stream timestamp found in the ID3 data chunk\n * @param data - Block of data containing one or more ID3 tags\n */\nconst getTimeStamp = data => {\n  const frames = getID3Frames(data);\n  for (let i = 0; i < frames.length; i++) {\n    const frame = frames[i];\n    if (isTimeStampFrame(frame)) {\n      return readTimeStamp(frame);\n    }\n  }\n  return undefined;\n};\n\n/**\n * Returns true if the ID3 frame is an Elementary Stream timestamp frame\n */\nconst isTimeStampFrame = frame => {\n  return frame && frame.key === 'PRIV' && frame.info === 'com.apple.streaming.transportStreamTimestamp';\n};\nconst getFrameData = data => {\n  /*\n  Frame ID       $xx xx xx xx (four characters)\n  Size           $xx xx xx xx\n  Flags          $xx xx\n  */\n  const type = String.fromCharCode(data[0], data[1], data[2], data[3]);\n  const size = readSize(data, 4);\n\n  // skip frame id, size, and flags\n  const offset = 10;\n  return {\n    type,\n    size,\n    data: data.subarray(offset, offset + size)\n  };\n};\n\n/**\n * Returns an array of ID3 frames found in all the ID3 tags in the id3Data\n * @param id3Data - The ID3 data containing one or more ID3 tags\n */\nconst getID3Frames = id3Data => {\n  let offset = 0;\n  const frames = [];\n  while (isHeader$2(id3Data, offset)) {\n    const size = readSize(id3Data, offset + 6);\n    // skip past ID3 header\n    offset += 10;\n    const end = offset + size;\n    // loop through frames in the ID3 tag\n    while (offset + 8 < end) {\n      const frameData = getFrameData(id3Data.subarray(offset));\n      const frame = decodeFrame(frameData);\n      if (frame) {\n        frames.push(frame);\n      }\n\n      // skip frame header and frame data\n      offset += frameData.size + 10;\n    }\n    if (isFooter(id3Data, offset)) {\n      offset += 10;\n    }\n  }\n  return frames;\n};\nconst decodeFrame = frame => {\n  if (frame.type === 'PRIV') {\n    return decodePrivFrame(frame);\n  } else if (frame.type[0] === 'W') {\n    return decodeURLFrame(frame);\n  }\n  return decodeTextFrame(frame);\n};\nconst decodePrivFrame = frame => {\n  /*\n  Format: <text string>\\0<binary data>\n  */\n  if (frame.size < 2) {\n    return undefined;\n  }\n  const owner = utf8ArrayToStr(frame.data, true);\n  const privateData = new Uint8Array(frame.data.subarray(owner.length + 1));\n  return {\n    key: frame.type,\n    info: owner,\n    data: privateData.buffer\n  };\n};\nconst decodeTextFrame = frame => {\n  if (frame.size < 2) {\n    return undefined;\n  }\n  if (frame.type === 'TXXX') {\n    /*\n    Format:\n    [0]   = {Text Encoding}\n    [1-?] = {Description}\\0{Value}\n    */\n    let index = 1;\n    const description = utf8ArrayToStr(frame.data.subarray(index), true);\n    index += description.length + 1;\n    const value = utf8ArrayToStr(frame.data.subarray(index));\n    return {\n      key: frame.type,\n      info: description,\n      data: value\n    };\n  }\n  /*\n  Format:\n  [0]   = {Text Encoding}\n  [1-?] = {Value}\n  */\n  const text = utf8ArrayToStr(frame.data.subarray(1));\n  return {\n    key: frame.type,\n    data: text\n  };\n};\nconst decodeURLFrame = frame => {\n  if (frame.type === 'WXXX') {\n    /*\n    Format:\n    [0]   = {Text Encoding}\n    [1-?] = {Description}\\0{URL}\n    */\n    if (frame.size < 2) {\n      return undefined;\n    }\n    let index = 1;\n    const description = utf8ArrayToStr(frame.data.subarray(index), true);\n    index += description.length + 1;\n    const value = utf8ArrayToStr(frame.data.subarray(index));\n    return {\n      key: frame.type,\n      info: description,\n      data: value\n    };\n  }\n  /*\n  Format:\n  [0-?] = {URL}\n  */\n  const url = utf8ArrayToStr(frame.data);\n  return {\n    key: frame.type,\n    data: url\n  };\n};\nconst readTimeStamp = timeStampFrame => {\n  if (timeStampFrame.data.byteLength === 8) {\n    const data = new Uint8Array(timeStampFrame.data);\n    // timestamp is 33 bit expressed as a big-endian eight-octet number,\n    // with the upper 31 bits set to zero.\n    const pts33Bit = data[3] & 0x1;\n    let timestamp = (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7];\n    timestamp /= 45;\n    if (pts33Bit) {\n      timestamp += 47721858.84;\n    } // 2^32 / 90\n\n    return Math.round(timestamp);\n  }\n  return undefined;\n};\n\n// http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197\n// http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt\n/* utf.js - UTF-8 <=> UTF-16 convertion\n *\n * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>\n * Version: 1.0\n * LastModified: Dec 25 1999\n * This library is free.  You can redistribute it and/or modify it.\n */\nconst utf8ArrayToStr = (array, exitOnNull = false) => {\n  const decoder = getTextDecoder();\n  if (decoder) {\n    const decoded = decoder.decode(array);\n    if (exitOnNull) {\n      // grab up to the first null\n      const idx = decoded.indexOf('\\0');\n      return idx !== -1 ? decoded.substring(0, idx) : decoded;\n    }\n\n    // remove any null characters\n    return decoded.replace(/\\0/g, '');\n  }\n  const len = array.length;\n  let c;\n  let char2;\n  let char3;\n  let out = '';\n  let i = 0;\n  while (i < len) {\n    c = array[i++];\n    if (c === 0x00 && exitOnNull) {\n      return out;\n    } else if (c === 0x00 || c === 0x03) {\n      // If the character is 3 (END_OF_TEXT) or 0 (NULL) then skip it\n      continue;\n    }\n    switch (c >> 4) {\n      case 0:\n      case 1:\n      case 2:\n      case 3:\n      case 4:\n      case 5:\n      case 6:\n      case 7:\n        // 0xxxxxxx\n        out += String.fromCharCode(c);\n        break;\n      case 12:\n      case 13:\n        // 110x xxxx   10xx xxxx\n        char2 = array[i++];\n        out += String.fromCharCode((c & 0x1f) << 6 | char2 & 0x3f);\n        break;\n      case 14:\n        // 1110 xxxx  10xx xxxx  10xx xxxx\n        char2 = array[i++];\n        char3 = array[i++];\n        out += String.fromCharCode((c & 0x0f) << 12 | (char2 & 0x3f) << 6 | (char3 & 0x3f) << 0);\n        break;\n    }\n  }\n  return out;\n};\nlet decoder;\nfunction getTextDecoder() {\n  if (!decoder && typeof self.TextDecoder !== 'undefined') {\n    decoder = new self.TextDecoder('utf-8');\n  }\n  return decoder;\n}\n\n/**\n *  hex dump helper class\n */\n\nconst Hex = {\n  hexDump: function (array) {\n    let str = '';\n    for (let i = 0; i < array.length; i++) {\n      let h = array[i].toString(16);\n      if (h.length < 2) {\n        h = '0' + h;\n      }\n      str += h;\n    }\n    return str;\n  }\n};\n\nconst UINT32_MAX$1 = Math.pow(2, 32) - 1;\nconst push = [].push;\n\n// We are using fixed track IDs for driving the MP4 remuxer\n// instead of following the TS PIDs.\n// There is no reason not to do this and some browsers/SourceBuffer-demuxers\n// may not like if there are TrackID \"switches\"\n// See https://github.com/video-dev/hls.js/issues/1331\n// Here we are mapping our internal track types to constant MP4 track IDs\n// With MSE currently one can only have one track of each, and we are muxing\n// whatever video/audio rendition in them.\nconst RemuxerTrackIdConfig = {\n  video: 1,\n  audio: 2,\n  id3: 3,\n  text: 4\n};\nfunction bin2str(data) {\n  return String.fromCharCode.apply(null, data);\n}\nfunction readUint16(buffer, offset) {\n  const val = buffer[offset] << 8 | buffer[offset + 1];\n  return val < 0 ? 65536 + val : val;\n}\nfunction readUint32(buffer, offset) {\n  const val = readSint32(buffer, offset);\n  return val < 0 ? 4294967296 + val : val;\n}\nfunction readSint32(buffer, offset) {\n  return buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3];\n}\nfunction writeUint32(buffer, offset, value) {\n  buffer[offset] = value >> 24;\n  buffer[offset + 1] = value >> 16 & 0xff;\n  buffer[offset + 2] = value >> 8 & 0xff;\n  buffer[offset + 3] = value & 0xff;\n}\n\n// Find the data for a box specified by its path\nfunction findBox(data, path) {\n  const results = [];\n  if (!path.length) {\n    // short-circuit the search for empty paths\n    return results;\n  }\n  const end = data.byteLength;\n  for (let i = 0; i < end;) {\n    const size = readUint32(data, i);\n    const type = bin2str(data.subarray(i + 4, i + 8));\n    const endbox = size > 1 ? i + size : end;\n    if (type === path[0]) {\n      if (path.length === 1) {\n        // this is the end of the path and we've found the box we were\n        // looking for\n        results.push(data.subarray(i + 8, endbox));\n      } else {\n        // recursively search for the next box along the path\n        const subresults = findBox(data.subarray(i + 8, endbox), path.slice(1));\n        if (subresults.length) {\n          push.apply(results, subresults);\n        }\n      }\n    }\n    i = endbox;\n  }\n\n  // we've finished searching all of data\n  return results;\n}\nfunction parseSegmentIndex(sidx) {\n  const references = [];\n  const version = sidx[0];\n\n  // set initial offset, we skip the reference ID (not needed)\n  let index = 8;\n  const timescale = readUint32(sidx, index);\n  index += 4;\n\n  // TODO: parse earliestPresentationTime and firstOffset\n  // usually zero in our case\n  const earliestPresentationTime = 0;\n  const firstOffset = 0;\n  if (version === 0) {\n    index += 8;\n  } else {\n    index += 16;\n  }\n\n  // skip reserved\n  index += 2;\n  let startByte = sidx.length + firstOffset;\n  const referencesCount = readUint16(sidx, index);\n  index += 2;\n  for (let i = 0; i < referencesCount; i++) {\n    let referenceIndex = index;\n    const referenceInfo = readUint32(sidx, referenceIndex);\n    referenceIndex += 4;\n    const referenceSize = referenceInfo & 0x7fffffff;\n    const referenceType = (referenceInfo & 0x80000000) >>> 31;\n    if (referenceType === 1) {\n      logger.warn('SIDX has hierarchical references (not supported)');\n      return null;\n    }\n    const subsegmentDuration = readUint32(sidx, referenceIndex);\n    referenceIndex += 4;\n    references.push({\n      referenceSize,\n      subsegmentDuration,\n      // unscaled\n      info: {\n        duration: subsegmentDuration / timescale,\n        start: startByte,\n        end: startByte + referenceSize - 1\n      }\n    });\n    startByte += referenceSize;\n\n    // Skipping 1 bit for |startsWithSap|, 3 bits for |sapType|, and 28 bits\n    // for |sapDelta|.\n    referenceIndex += 4;\n\n    // skip to next ref\n    index = referenceIndex;\n  }\n  return {\n    earliestPresentationTime,\n    timescale,\n    version,\n    referencesCount,\n    references\n  };\n}\n\n/**\n * Parses an MP4 initialization segment and extracts stream type and\n * timescale values for any declared tracks. Timescale values indicate the\n * number of clock ticks per second to assume for time-based values\n * elsewhere in the MP4.\n *\n * To determine the start time of an MP4, you need two pieces of\n * information: the timescale unit and the earliest base media decode\n * time. Multiple timescales can be specified within an MP4 but the\n * base media decode time is always expressed in the timescale from\n * the media header box for the track:\n * ```\n * moov > trak > mdia > mdhd.timescale\n * moov > trak > mdia > hdlr\n * ```\n * @param initSegment the bytes of the init segment\n * @returns a hash of track type to timescale values or null if\n * the init segment is malformed.\n */\n\nfunction parseInitSegment(initSegment) {\n  const result = [];\n  const traks = findBox(initSegment, ['moov', 'trak']);\n  for (let i = 0; i < traks.length; i++) {\n    const trak = traks[i];\n    const tkhd = findBox(trak, ['tkhd'])[0];\n    if (tkhd) {\n      let version = tkhd[0];\n      let index = version === 0 ? 12 : 20;\n      const trackId = readUint32(tkhd, index);\n      const mdhd = findBox(trak, ['mdia', 'mdhd'])[0];\n      if (mdhd) {\n        version = mdhd[0];\n        index = version === 0 ? 12 : 20;\n        const timescale = readUint32(mdhd, index);\n        const hdlr = findBox(trak, ['mdia', 'hdlr'])[0];\n        if (hdlr) {\n          const hdlrType = bin2str(hdlr.subarray(8, 12));\n          const type = {\n            soun: ElementaryStreamTypes.AUDIO,\n            vide: ElementaryStreamTypes.VIDEO\n          }[hdlrType];\n          if (type) {\n            // Parse codec details\n            const stsd = findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0];\n            let codec;\n            if (stsd) {\n              codec = bin2str(stsd.subarray(12, 16));\n              // TODO: Parse codec details to be able to build MIME type.\n              // stsd.start += 8;\n              // const codecBox = findBox(stsd, [codec])[0];\n              // if (codecBox) {\n              //   TODO: Codec parsing support for avc1, mp4a, hevc, av01...\n              // }\n            }\n\n            result[trackId] = {\n              timescale,\n              type\n            };\n            result[type] = {\n              timescale,\n              id: trackId,\n              codec\n            };\n          }\n        }\n      }\n    }\n  }\n  const trex = findBox(initSegment, ['moov', 'mvex', 'trex']);\n  trex.forEach(trex => {\n    const trackId = readUint32(trex, 4);\n    const track = result[trackId];\n    if (track) {\n      track.default = {\n        duration: readUint32(trex, 12),\n        flags: readUint32(trex, 20)\n      };\n    }\n  });\n  return result;\n}\nfunction patchEncyptionData(initSegment, decryptdata) {\n  if (!initSegment || !decryptdata) {\n    return initSegment;\n  }\n  const keyId = decryptdata.keyId;\n  if (keyId && decryptdata.isCommonEncryption) {\n    const traks = findBox(initSegment, ['moov', 'trak']);\n    traks.forEach(trak => {\n      const stsd = findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0];\n\n      // skip the sample entry count\n      const sampleEntries = stsd.subarray(8);\n      let encBoxes = findBox(sampleEntries, ['enca']);\n      const isAudio = encBoxes.length > 0;\n      if (!isAudio) {\n        encBoxes = findBox(sampleEntries, ['encv']);\n      }\n      encBoxes.forEach(enc => {\n        const encBoxChildren = isAudio ? enc.subarray(28) : enc.subarray(78);\n        const sinfBoxes = findBox(encBoxChildren, ['sinf']);\n        sinfBoxes.forEach(sinf => {\n          const tenc = parseSinf(sinf);\n          if (tenc) {\n            // Look for default key id (keyID offset is always 8 within the tenc box):\n            const tencKeyId = tenc.subarray(8, 24);\n            if (!tencKeyId.some(b => b !== 0)) {\n              logger.log(`[eme] Patching keyId in 'enc${isAudio ? 'a' : 'v'}>sinf>>tenc' box: ${Hex.hexDump(tencKeyId)} -> ${Hex.hexDump(keyId)}`);\n              tenc.set(keyId, 8);\n            }\n          }\n        });\n      });\n    });\n  }\n  return initSegment;\n}\nfunction parseSinf(sinf) {\n  const schm = findBox(sinf, ['schm'])[0];\n  if (schm) {\n    const scheme = bin2str(schm.subarray(4, 8));\n    if (scheme === 'cbcs' || scheme === 'cenc') {\n      return findBox(sinf, ['schi', 'tenc'])[0];\n    }\n  }\n  logger.error(`[eme] missing 'schm' box`);\n  return null;\n}\n\n/**\n * Determine the base media decode start time, in seconds, for an MP4\n * fragment. If multiple fragments are specified, the earliest time is\n * returned.\n *\n * The base media decode time can be parsed from track fragment\n * metadata:\n * ```\n * moof > traf > tfdt.baseMediaDecodeTime\n * ```\n * It requires the timescale value from the mdhd to interpret.\n *\n * @param initData - a hash of track type to timescale values\n * @param fmp4 - the bytes of the mp4 fragment\n * @returns the earliest base media decode start time for the\n * fragment, in seconds\n */\nfunction getStartDTS(initData, fmp4) {\n  // we need info from two children of each track fragment box\n  return findBox(fmp4, ['moof', 'traf']).reduce((result, traf) => {\n    const tfdt = findBox(traf, ['tfdt'])[0];\n    const version = tfdt[0];\n    const start = findBox(traf, ['tfhd']).reduce((result, tfhd) => {\n      // get the track id from the tfhd\n      const id = readUint32(tfhd, 4);\n      const track = initData[id];\n      if (track) {\n        let baseTime = readUint32(tfdt, 4);\n        if (version === 1) {\n          // If value is too large, assume signed 64-bit. Negative track fragment decode times are invalid, but they exist in the wild.\n          // This prevents large values from being used for initPTS, which can cause playlist sync issues.\n          // https://github.com/video-dev/hls.js/issues/5303\n          if (baseTime === UINT32_MAX$1) {\n            logger.warn(`[mp4-demuxer]: Ignoring assumed invalid signed 64-bit track fragment decode time`);\n            return result;\n          }\n          baseTime *= UINT32_MAX$1 + 1;\n          baseTime += readUint32(tfdt, 8);\n        }\n        // assume a 90kHz clock if no timescale was specified\n        const scale = track.timescale || 90e3;\n        // convert base time to seconds\n        const startTime = baseTime / scale;\n        if (isFiniteNumber(startTime) && (result === null || startTime < result)) {\n          return startTime;\n        }\n      }\n      return result;\n    }, null);\n    if (start !== null && isFiniteNumber(start) && (result === null || start < result)) {\n      return start;\n    }\n    return result;\n  }, null);\n}\n\n/*\n  For Reference:\n  aligned(8) class TrackFragmentHeaderBox\n           extends FullBox(‘tfhd’, 0, tf_flags){\n     unsigned int(32)  track_ID;\n     // all the following are optional fields\n     unsigned int(64)  base_data_offset;\n     unsigned int(32)  sample_description_index;\n     unsigned int(32)  default_sample_duration;\n     unsigned int(32)  default_sample_size;\n     unsigned int(32)  default_sample_flags\n  }\n */\nfunction getDuration(data, initData) {\n  let rawDuration = 0;\n  let videoDuration = 0;\n  let audioDuration = 0;\n  const trafs = findBox(data, ['moof', 'traf']);\n  for (let i = 0; i < trafs.length; i++) {\n    const traf = trafs[i];\n    // There is only one tfhd & trun per traf\n    // This is true for CMAF style content, and we should perhaps check the ftyp\n    // and only look for a single trun then, but for ISOBMFF we should check\n    // for multiple track runs.\n    const tfhd = findBox(traf, ['tfhd'])[0];\n    // get the track id from the tfhd\n    const id = readUint32(tfhd, 4);\n    const track = initData[id];\n    if (!track) {\n      continue;\n    }\n    const trackDefault = track.default;\n    const tfhdFlags = readUint32(tfhd, 0) | (trackDefault == null ? void 0 : trackDefault.flags);\n    let sampleDuration = trackDefault == null ? void 0 : trackDefault.duration;\n    if (tfhdFlags & 0x000008) {\n      // 0x000008 indicates the presence of the default_sample_duration field\n      if (tfhdFlags & 0x000002) {\n        // 0x000002 indicates the presence of the sample_description_index field, which precedes default_sample_duration\n        // If present, the default_sample_duration exists at byte offset 12\n        sampleDuration = readUint32(tfhd, 12);\n      } else {\n        // Otherwise, the duration is at byte offset 8\n        sampleDuration = readUint32(tfhd, 8);\n      }\n    }\n    // assume a 90kHz clock if no timescale was specified\n    const timescale = track.timescale || 90e3;\n    const truns = findBox(traf, ['trun']);\n    for (let j = 0; j < truns.length; j++) {\n      rawDuration = computeRawDurationFromSamples(truns[j]);\n      if (!rawDuration && sampleDuration) {\n        const sampleCount = readUint32(truns[j], 4);\n        rawDuration = sampleDuration * sampleCount;\n      }\n      if (track.type === ElementaryStreamTypes.VIDEO) {\n        videoDuration += rawDuration / timescale;\n      } else if (track.type === ElementaryStreamTypes.AUDIO) {\n        audioDuration += rawDuration / timescale;\n      }\n    }\n  }\n  if (videoDuration === 0 && audioDuration === 0) {\n    // If duration samples are not available in the traf use sidx subsegment_duration\n    let sidxDuration = 0;\n    const sidxs = findBox(data, ['sidx']);\n    for (let i = 0; i < sidxs.length; i++) {\n      const sidx = parseSegmentIndex(sidxs[i]);\n      if (sidx != null && sidx.references) {\n        sidxDuration += sidx.references.reduce((dur, ref) => dur + ref.info.duration || 0, 0);\n      }\n    }\n    return sidxDuration;\n  }\n  if (videoDuration) {\n    return videoDuration;\n  }\n  return audioDuration;\n}\n\n/*\n  For Reference:\n  aligned(8) class TrackRunBox\n           extends FullBox(‘trun’, version, tr_flags) {\n     unsigned int(32)  sample_count;\n     // the following are optional fields\n     signed int(32) data_offset;\n     unsigned int(32)  first_sample_flags;\n     // all fields in the following array are optional\n     {\n        unsigned int(32)  sample_duration;\n        unsigned int(32)  sample_size;\n        unsigned int(32)  sample_flags\n        if (version == 0)\n           { unsigned int(32)\n        else\n           { signed int(32)\n     }[ sample_count ]\n  }\n */\nfunction computeRawDurationFromSamples(trun) {\n  const flags = readUint32(trun, 0);\n  // Flags are at offset 0, non-optional sample_count is at offset 4. Therefore we start 8 bytes in.\n  // Each field is an int32, which is 4 bytes\n  let offset = 8;\n  // data-offset-present flag\n  if (flags & 0x000001) {\n    offset += 4;\n  }\n  // first-sample-flags-present flag\n  if (flags & 0x000004) {\n    offset += 4;\n  }\n  let duration = 0;\n  const sampleCount = readUint32(trun, 4);\n  for (let i = 0; i < sampleCount; i++) {\n    // sample-duration-present flag\n    if (flags & 0x000100) {\n      const sampleDuration = readUint32(trun, offset);\n      duration += sampleDuration;\n      offset += 4;\n    }\n    // sample-size-present flag\n    if (flags & 0x000200) {\n      offset += 4;\n    }\n    // sample-flags-present flag\n    if (flags & 0x000400) {\n      offset += 4;\n    }\n    // sample-composition-time-offsets-present flag\n    if (flags & 0x000800) {\n      offset += 4;\n    }\n  }\n  return duration;\n}\nfunction offsetStartDTS(initData, fmp4, timeOffset) {\n  findBox(fmp4, ['moof', 'traf']).forEach(traf => {\n    findBox(traf, ['tfhd']).forEach(tfhd => {\n      // get the track id from the tfhd\n      const id = readUint32(tfhd, 4);\n      const track = initData[id];\n      if (!track) {\n        return;\n      }\n      // assume a 90kHz clock if no timescale was specified\n      const timescale = track.timescale || 90e3;\n      // get the base media decode time from the tfdt\n      findBox(traf, ['tfdt']).forEach(tfdt => {\n        const version = tfdt[0];\n        let baseMediaDecodeTime = readUint32(tfdt, 4);\n        if (version === 0) {\n          baseMediaDecodeTime -= timeOffset * timescale;\n          baseMediaDecodeTime = Math.max(baseMediaDecodeTime, 0);\n          writeUint32(tfdt, 4, baseMediaDecodeTime);\n        } else {\n          baseMediaDecodeTime *= Math.pow(2, 32);\n          baseMediaDecodeTime += readUint32(tfdt, 8);\n          baseMediaDecodeTime -= timeOffset * timescale;\n          baseMediaDecodeTime = Math.max(baseMediaDecodeTime, 0);\n          const upper = Math.floor(baseMediaDecodeTime / (UINT32_MAX$1 + 1));\n          const lower = Math.floor(baseMediaDecodeTime % (UINT32_MAX$1 + 1));\n          writeUint32(tfdt, 4, upper);\n          writeUint32(tfdt, 8, lower);\n        }\n      });\n    });\n  });\n}\n\n// TODO: Check if the last moof+mdat pair is part of the valid range\nfunction segmentValidRange(data) {\n  const segmentedRange = {\n    valid: null,\n    remainder: null\n  };\n  const moofs = findBox(data, ['moof']);\n  if (!moofs) {\n    return segmentedRange;\n  } else if (moofs.length < 2) {\n    segmentedRange.remainder = data;\n    return segmentedRange;\n  }\n  const last = moofs[moofs.length - 1];\n  // Offset by 8 bytes; findBox offsets the start by as much\n  segmentedRange.valid = sliceUint8(data, 0, last.byteOffset - 8);\n  segmentedRange.remainder = sliceUint8(data, last.byteOffset - 8);\n  return segmentedRange;\n}\nfunction appendUint8Array(data1, data2) {\n  const temp = new Uint8Array(data1.length + data2.length);\n  temp.set(data1);\n  temp.set(data2, data1.length);\n  return temp;\n}\nfunction parseSamples(timeOffset, track) {\n  const seiSamples = [];\n  const videoData = track.samples;\n  const timescale = track.timescale;\n  const trackId = track.id;\n  let isHEVCFlavor = false;\n  const moofs = findBox(videoData, ['moof']);\n  moofs.map(moof => {\n    const moofOffset = moof.byteOffset - 8;\n    const trafs = findBox(moof, ['traf']);\n    trafs.map(traf => {\n      // get the base media decode time from the tfdt\n      const baseTime = findBox(traf, ['tfdt']).map(tfdt => {\n        const version = tfdt[0];\n        let result = readUint32(tfdt, 4);\n        if (version === 1) {\n          result *= Math.pow(2, 32);\n          result += readUint32(tfdt, 8);\n        }\n        return result / timescale;\n      })[0];\n      if (baseTime !== undefined) {\n        timeOffset = baseTime;\n      }\n      return findBox(traf, ['tfhd']).map(tfhd => {\n        const id = readUint32(tfhd, 4);\n        const tfhdFlags = readUint32(tfhd, 0) & 0xffffff;\n        const baseDataOffsetPresent = (tfhdFlags & 0x000001) !== 0;\n        const sampleDescriptionIndexPresent = (tfhdFlags & 0x000002) !== 0;\n        const defaultSampleDurationPresent = (tfhdFlags & 0x000008) !== 0;\n        let defaultSampleDuration = 0;\n        const defaultSampleSizePresent = (tfhdFlags & 0x000010) !== 0;\n        let defaultSampleSize = 0;\n        const defaultSampleFlagsPresent = (tfhdFlags & 0x000020) !== 0;\n        let tfhdOffset = 8;\n        if (id === trackId) {\n          if (baseDataOffsetPresent) {\n            tfhdOffset += 8;\n          }\n          if (sampleDescriptionIndexPresent) {\n            tfhdOffset += 4;\n          }\n          if (defaultSampleDurationPresent) {\n            defaultSampleDuration = readUint32(tfhd, tfhdOffset);\n            tfhdOffset += 4;\n          }\n          if (defaultSampleSizePresent) {\n            defaultSampleSize = readUint32(tfhd, tfhdOffset);\n            tfhdOffset += 4;\n          }\n          if (defaultSampleFlagsPresent) {\n            tfhdOffset += 4;\n          }\n          if (track.type === 'video') {\n            isHEVCFlavor = isHEVC(track.codec);\n          }\n          findBox(traf, ['trun']).map(trun => {\n            const version = trun[0];\n            const flags = readUint32(trun, 0) & 0xffffff;\n            const dataOffsetPresent = (flags & 0x000001) !== 0;\n            let dataOffset = 0;\n            const firstSampleFlagsPresent = (flags & 0x000004) !== 0;\n            const sampleDurationPresent = (flags & 0x000100) !== 0;\n            let sampleDuration = 0;\n            const sampleSizePresent = (flags & 0x000200) !== 0;\n            let sampleSize = 0;\n            const sampleFlagsPresent = (flags & 0x000400) !== 0;\n            const sampleCompositionOffsetsPresent = (flags & 0x000800) !== 0;\n            let compositionOffset = 0;\n            const sampleCount = readUint32(trun, 4);\n            let trunOffset = 8; // past version, flags, and sample count\n\n            if (dataOffsetPresent) {\n              dataOffset = readUint32(trun, trunOffset);\n              trunOffset += 4;\n            }\n            if (firstSampleFlagsPresent) {\n              trunOffset += 4;\n            }\n            let sampleOffset = dataOffset + moofOffset;\n            for (let ix = 0; ix < sampleCount; ix++) {\n              if (sampleDurationPresent) {\n                sampleDuration = readUint32(trun, trunOffset);\n                trunOffset += 4;\n              } else {\n                sampleDuration = defaultSampleDuration;\n              }\n              if (sampleSizePresent) {\n                sampleSize = readUint32(trun, trunOffset);\n                trunOffset += 4;\n              } else {\n                sampleSize = defaultSampleSize;\n              }\n              if (sampleFlagsPresent) {\n                trunOffset += 4;\n              }\n              if (sampleCompositionOffsetsPresent) {\n                if (version === 0) {\n                  compositionOffset = readUint32(trun, trunOffset);\n                } else {\n                  compositionOffset = readSint32(trun, trunOffset);\n                }\n                trunOffset += 4;\n              }\n              if (track.type === ElementaryStreamTypes.VIDEO) {\n                let naluTotalSize = 0;\n                while (naluTotalSize < sampleSize) {\n                  const naluSize = readUint32(videoData, sampleOffset);\n                  sampleOffset += 4;\n                  if (isSEIMessage(isHEVCFlavor, videoData[sampleOffset])) {\n                    const data = videoData.subarray(sampleOffset, sampleOffset + naluSize);\n                    parseSEIMessageFromNALu(data, isHEVCFlavor ? 2 : 1, timeOffset + compositionOffset / timescale, seiSamples);\n                  }\n                  sampleOffset += naluSize;\n                  naluTotalSize += naluSize + 4;\n                }\n              }\n              timeOffset += sampleDuration / timescale;\n            }\n          });\n        }\n      });\n    });\n  });\n  return seiSamples;\n}\nfunction isHEVC(codec) {\n  if (!codec) {\n    return false;\n  }\n  const delimit = codec.indexOf('.');\n  const baseCodec = delimit < 0 ? codec : codec.substring(0, delimit);\n  return baseCodec === 'hvc1' || baseCodec === 'hev1' ||\n  // Dolby Vision\n  baseCodec === 'dvh1' || baseCodec === 'dvhe';\n}\nfunction isSEIMessage(isHEVCFlavor, naluHeader) {\n  if (isHEVCFlavor) {\n    const naluType = naluHeader >> 1 & 0x3f;\n    return naluType === 39 || naluType === 40;\n  } else {\n    const naluType = naluHeader & 0x1f;\n    return naluType === 6;\n  }\n}\nfunction parseSEIMessageFromNALu(unescapedData, headerSize, pts, samples) {\n  const data = discardEPB(unescapedData);\n  let seiPtr = 0;\n  // skip nal header\n  seiPtr += headerSize;\n  let payloadType = 0;\n  let payloadSize = 0;\n  let endOfCaptions = false;\n  let b = 0;\n  while (seiPtr < data.length) {\n    payloadType = 0;\n    do {\n      if (seiPtr >= data.length) {\n        break;\n      }\n      b = data[seiPtr++];\n      payloadType += b;\n    } while (b === 0xff);\n\n    // Parse payload size.\n    payloadSize = 0;\n    do {\n      if (seiPtr >= data.length) {\n        break;\n      }\n      b = data[seiPtr++];\n      payloadSize += b;\n    } while (b === 0xff);\n    const leftOver = data.length - seiPtr;\n    if (!endOfCaptions && payloadType === 4 && seiPtr < data.length) {\n      endOfCaptions = true;\n      const countryCode = data[seiPtr++];\n      if (countryCode === 181) {\n        const providerCode = readUint16(data, seiPtr);\n        seiPtr += 2;\n        if (providerCode === 49) {\n          const userStructure = readUint32(data, seiPtr);\n          seiPtr += 4;\n          if (userStructure === 0x47413934) {\n            const userDataType = data[seiPtr++];\n\n            // Raw CEA-608 bytes wrapped in CEA-708 packet\n            if (userDataType === 3) {\n              const firstByte = data[seiPtr++];\n              const totalCCs = 0x1f & firstByte;\n              const enabled = 0x40 & firstByte;\n              const totalBytes = enabled ? 2 + totalCCs * 3 : 0;\n              const byteArray = new Uint8Array(totalBytes);\n              if (enabled) {\n                byteArray[0] = firstByte;\n                for (let i = 1; i < totalBytes; i++) {\n                  byteArray[i] = data[seiPtr++];\n                }\n              }\n              samples.push({\n                type: userDataType,\n                payloadType,\n                pts,\n                bytes: byteArray\n              });\n            }\n          }\n        }\n      }\n    } else if (payloadType === 5 && payloadSize < leftOver) {\n      endOfCaptions = true;\n      if (payloadSize > 16) {\n        const uuidStrArray = [];\n        for (let i = 0; i < 16; i++) {\n          const _b = data[seiPtr++].toString(16);\n          uuidStrArray.push(_b.length == 1 ? '0' + _b : _b);\n          if (i === 3 || i === 5 || i === 7 || i === 9) {\n            uuidStrArray.push('-');\n          }\n        }\n        const length = payloadSize - 16;\n        const userDataBytes = new Uint8Array(length);\n        for (let i = 0; i < length; i++) {\n          userDataBytes[i] = data[seiPtr++];\n        }\n        samples.push({\n          payloadType,\n          pts,\n          uuid: uuidStrArray.join(''),\n          userData: utf8ArrayToStr(userDataBytes),\n          userDataBytes\n        });\n      }\n    } else if (payloadSize < leftOver) {\n      seiPtr += payloadSize;\n    } else if (payloadSize > leftOver) {\n      break;\n    }\n  }\n}\n\n/**\n * remove Emulation Prevention bytes from a RBSP\n */\nfunction discardEPB(data) {\n  const length = data.byteLength;\n  const EPBPositions = [];\n  let i = 1;\n\n  // Find all `Emulation Prevention Bytes`\n  while (i < length - 2) {\n    if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {\n      EPBPositions.push(i + 2);\n      i += 2;\n    } else {\n      i++;\n    }\n  }\n\n  // If no Emulation Prevention Bytes were found just return the original\n  // array\n  if (EPBPositions.length === 0) {\n    return data;\n  }\n\n  // Create a new array to hold the NAL unit data\n  const newLength = length - EPBPositions.length;\n  const newData = new Uint8Array(newLength);\n  let sourceIndex = 0;\n  for (i = 0; i < newLength; sourceIndex++, i++) {\n    if (sourceIndex === EPBPositions[0]) {\n      // Skip this byte\n      sourceIndex++;\n      // Remove this position index\n      EPBPositions.shift();\n    }\n    newData[i] = data[sourceIndex];\n  }\n  return newData;\n}\nfunction parseEmsg(data) {\n  const version = data[0];\n  let schemeIdUri = '';\n  let value = '';\n  let timeScale = 0;\n  let presentationTimeDelta = 0;\n  let presentationTime = 0;\n  let eventDuration = 0;\n  let id = 0;\n  let offset = 0;\n  if (version === 0) {\n    while (bin2str(data.subarray(offset, offset + 1)) !== '\\0') {\n      schemeIdUri += bin2str(data.subarray(offset, offset + 1));\n      offset += 1;\n    }\n    schemeIdUri += bin2str(data.subarray(offset, offset + 1));\n    offset += 1;\n    while (bin2str(data.subarray(offset, offset + 1)) !== '\\0') {\n      value += bin2str(data.subarray(offset, offset + 1));\n      offset += 1;\n    }\n    value += bin2str(data.subarray(offset, offset + 1));\n    offset += 1;\n    timeScale = readUint32(data, 12);\n    presentationTimeDelta = readUint32(data, 16);\n    eventDuration = readUint32(data, 20);\n    id = readUint32(data, 24);\n    offset = 28;\n  } else if (version === 1) {\n    offset += 4;\n    timeScale = readUint32(data, offset);\n    offset += 4;\n    const leftPresentationTime = readUint32(data, offset);\n    offset += 4;\n    const rightPresentationTime = readUint32(data, offset);\n    offset += 4;\n    presentationTime = 2 ** 32 * leftPresentationTime + rightPresentationTime;\n    if (!isSafeInteger(presentationTime)) {\n      presentationTime = Number.MAX_SAFE_INTEGER;\n      logger.warn('Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box');\n    }\n    eventDuration = readUint32(data, offset);\n    offset += 4;\n    id = readUint32(data, offset);\n    offset += 4;\n    while (bin2str(data.subarray(offset, offset + 1)) !== '\\0') {\n      schemeIdUri += bin2str(data.subarray(offset, offset + 1));\n      offset += 1;\n    }\n    schemeIdUri += bin2str(data.subarray(offset, offset + 1));\n    offset += 1;\n    while (bin2str(data.subarray(offset, offset + 1)) !== '\\0') {\n      value += bin2str(data.subarray(offset, offset + 1));\n      offset += 1;\n    }\n    value += bin2str(data.subarray(offset, offset + 1));\n    offset += 1;\n  }\n  const payload = data.subarray(offset, data.byteLength);\n  return {\n    schemeIdUri,\n    value,\n    timeScale,\n    presentationTime,\n    presentationTimeDelta,\n    eventDuration,\n    id,\n    payload\n  };\n}\nfunction mp4Box(type, ...payload) {\n  const len = payload.length;\n  let size = 8;\n  let i = len;\n  while (i--) {\n    size += payload[i].byteLength;\n  }\n  const result = new Uint8Array(size);\n  result[0] = size >> 24 & 0xff;\n  result[1] = size >> 16 & 0xff;\n  result[2] = size >> 8 & 0xff;\n  result[3] = size & 0xff;\n  result.set(type, 4);\n  for (i = 0, size = 8; i < len; i++) {\n    result.set(payload[i], size);\n    size += payload[i].byteLength;\n  }\n  return result;\n}\nfunction mp4pssh(systemId, keyids, data) {\n  if (systemId.byteLength !== 16) {\n    throw new RangeError('Invalid system id');\n  }\n  let version;\n  let kids;\n  if (keyids) {\n    version = 1;\n    kids = new Uint8Array(keyids.length * 16);\n    for (let ix = 0; ix < keyids.length; ix++) {\n      const k = keyids[ix]; // uint8array\n      if (k.byteLength !== 16) {\n        throw new RangeError('Invalid key');\n      }\n      kids.set(k, ix * 16);\n    }\n  } else {\n    version = 0;\n    kids = new Uint8Array();\n  }\n  let kidCount;\n  if (version > 0) {\n    kidCount = new Uint8Array(4);\n    if (keyids.length > 0) {\n      new DataView(kidCount.buffer).setUint32(0, keyids.length, false);\n    }\n  } else {\n    kidCount = new Uint8Array();\n  }\n  const dataSize = new Uint8Array(4);\n  if (data && data.byteLength > 0) {\n    new DataView(dataSize.buffer).setUint32(0, data.byteLength, false);\n  }\n  return mp4Box([112, 115, 115, 104], new Uint8Array([version, 0x00, 0x00, 0x00 // Flags\n  ]), systemId,\n  // 16 bytes\n  kidCount, kids, dataSize, data || new Uint8Array());\n}\nfunction parsePssh(initData) {\n  if (!(initData instanceof ArrayBuffer) || initData.byteLength < 32) {\n    return null;\n  }\n  const result = {\n    version: 0,\n    systemId: '',\n    kids: null,\n    data: null\n  };\n  const view = new DataView(initData);\n  const boxSize = view.getUint32(0);\n  if (initData.byteLength !== boxSize && boxSize > 44) {\n    return null;\n  }\n  const type = view.getUint32(4);\n  if (type !== 0x70737368) {\n    return null;\n  }\n  result.version = view.getUint32(8) >>> 24;\n  if (result.version > 1) {\n    return null;\n  }\n  result.systemId = Hex.hexDump(new Uint8Array(initData, 12, 16));\n  const dataSizeOrKidCount = view.getUint32(28);\n  if (result.version === 0) {\n    if (boxSize - 32 < dataSizeOrKidCount) {\n      return null;\n    }\n    result.data = new Uint8Array(initData, 32, dataSizeOrKidCount);\n  } else if (result.version === 1) {\n    result.kids = [];\n    for (let i = 0; i < dataSizeOrKidCount; i++) {\n      result.kids.push(new Uint8Array(initData, 32 + i * 16, 16));\n    }\n  }\n  return result;\n}\n\nlet keyUriToKeyIdMap = {};\nclass LevelKey {\n  static clearKeyUriToKeyIdMap() {\n    keyUriToKeyIdMap = {};\n  }\n  constructor(method, uri, format, formatversions = [1], iv = null) {\n    this.uri = void 0;\n    this.method = void 0;\n    this.keyFormat = void 0;\n    this.keyFormatVersions = void 0;\n    this.encrypted = void 0;\n    this.isCommonEncryption = void 0;\n    this.iv = null;\n    this.key = null;\n    this.keyId = null;\n    this.pssh = null;\n    this.method = method;\n    this.uri = uri;\n    this.keyFormat = format;\n    this.keyFormatVersions = formatversions;\n    this.iv = iv;\n    this.encrypted = method ? method !== 'NONE' : false;\n    this.isCommonEncryption = this.encrypted && method !== 'AES-128';\n  }\n  isSupported() {\n    // If it's Segment encryption or No encryption, just select that key system\n    if (this.method) {\n      if (this.method === 'AES-128' || this.method === 'NONE') {\n        return true;\n      }\n      if (this.keyFormat === 'identity') {\n        // Maintain support for clear SAMPLE-AES with MPEG-3 TS\n        return this.method === 'SAMPLE-AES';\n      } else {\n        switch (this.keyFormat) {\n          case KeySystemFormats.FAIRPLAY:\n          case KeySystemFormats.WIDEVINE:\n          case KeySystemFormats.PLAYREADY:\n          case KeySystemFormats.CLEARKEY:\n            return ['ISO-23001-7', 'SAMPLE-AES', 'SAMPLE-AES-CENC', 'SAMPLE-AES-CTR'].indexOf(this.method) !== -1;\n        }\n      }\n    }\n    return false;\n  }\n  getDecryptData(sn) {\n    if (!this.encrypted || !this.uri) {\n      return null;\n    }\n    if (this.method === 'AES-128' && this.uri && !this.iv) {\n      if (typeof sn !== 'number') {\n        // We are fetching decryption data for a initialization segment\n        // If the segment was encrypted with AES-128\n        // It must have an IV defined. We cannot substitute the Segment Number in.\n        if (this.method === 'AES-128' && !this.iv) {\n          logger.warn(`missing IV for initialization segment with method=\"${this.method}\" - compliance issue`);\n        }\n        // Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation.\n        sn = 0;\n      }\n      const iv = createInitializationVector(sn);\n      const decryptdata = new LevelKey(this.method, this.uri, 'identity', this.keyFormatVersions, iv);\n      return decryptdata;\n    }\n\n    // Initialize keyId if possible\n    const keyBytes = convertDataUriToArrayBytes(this.uri);\n    if (keyBytes) {\n      switch (this.keyFormat) {\n        case KeySystemFormats.WIDEVINE:\n          this.pssh = keyBytes;\n          // In case of widevine keyID is embedded in PSSH box. Read Key ID.\n          if (keyBytes.length >= 22) {\n            this.keyId = keyBytes.subarray(keyBytes.length - 22, keyBytes.length - 6);\n          }\n          break;\n        case KeySystemFormats.PLAYREADY:\n          {\n            const PlayReadyKeySystemUUID = new Uint8Array([0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95]);\n            this.pssh = mp4pssh(PlayReadyKeySystemUUID, null, keyBytes);\n            const keyBytesUtf16 = new Uint16Array(keyBytes.buffer, keyBytes.byteOffset, keyBytes.byteLength / 2);\n            const keyByteStr = String.fromCharCode.apply(null, Array.from(keyBytesUtf16));\n\n            // Parse Playready WRMHeader XML\n            const xmlKeyBytes = keyByteStr.substring(keyByteStr.indexOf('<'), keyByteStr.length);\n            const parser = new DOMParser();\n            const xmlDoc = parser.parseFromString(xmlKeyBytes, 'text/xml');\n            const keyData = xmlDoc.getElementsByTagName('KID')[0];\n            if (keyData) {\n              const keyId = keyData.childNodes[0] ? keyData.childNodes[0].nodeValue : keyData.getAttribute('VALUE');\n              if (keyId) {\n                const keyIdArray = base64Decode(keyId).subarray(0, 16);\n                // KID value in PRO is a base64-encoded little endian GUID interpretation of UUID\n                // KID value in ‘tenc’ is a big endian UUID GUID interpretation of UUID\n                changeEndianness(keyIdArray);\n                this.keyId = keyIdArray;\n              }\n            }\n            break;\n          }\n        default:\n          {\n            let keydata = keyBytes.subarray(0, 16);\n            if (keydata.length !== 16) {\n              const padded = new Uint8Array(16);\n              padded.set(keydata, 16 - keydata.length);\n              keydata = padded;\n            }\n            this.keyId = keydata;\n            break;\n          }\n      }\n    }\n\n    // Default behavior: assign a new keyId for each uri\n    if (!this.keyId || this.keyId.byteLength !== 16) {\n      let keyId = keyUriToKeyIdMap[this.uri];\n      if (!keyId) {\n        const val = Object.keys(keyUriToKeyIdMap).length % Number.MAX_SAFE_INTEGER;\n        keyId = new Uint8Array(16);\n        const dv = new DataView(keyId.buffer, 12, 4); // Just set the last 4 bytes\n        dv.setUint32(0, val);\n        keyUriToKeyIdMap[this.uri] = keyId;\n      }\n      this.keyId = keyId;\n    }\n    return this;\n  }\n}\nfunction createInitializationVector(segmentNumber) {\n  const uint8View = new Uint8Array(16);\n  for (let i = 12; i < 16; i++) {\n    uint8View[i] = segmentNumber >> 8 * (15 - i) & 0xff;\n  }\n  return uint8View;\n}\n\nconst VARIABLE_REPLACEMENT_REGEX = /\\{\\$([a-zA-Z0-9-_]+)\\}/g;\nfunction hasVariableReferences(str) {\n  return VARIABLE_REPLACEMENT_REGEX.test(str);\n}\nfunction substituteVariablesInAttributes(parsed, attr, attributeNames) {\n  if (parsed.variableList !== null || parsed.hasVariableRefs) {\n    for (let i = attributeNames.length; i--;) {\n      const name = attributeNames[i];\n      const value = attr[name];\n      if (value) {\n        attr[name] = substituteVariables(parsed, value);\n      }\n    }\n  }\n}\nfunction substituteVariables(parsed, value) {\n  if (parsed.variableList !== null || parsed.hasVariableRefs) {\n    const variableList = parsed.variableList;\n    return value.replace(VARIABLE_REPLACEMENT_REGEX, variableReference => {\n      const variableName = variableReference.substring(2, variableReference.length - 1);\n      const variableValue = variableList == null ? void 0 : variableList[variableName];\n      if (variableValue === undefined) {\n        parsed.playlistParsingError || (parsed.playlistParsingError = new Error(`Missing preceding EXT-X-DEFINE tag for Variable Reference: \"${variableName}\"`));\n        return variableReference;\n      }\n      return variableValue;\n    });\n  }\n  return value;\n}\nfunction addVariableDefinition(parsed, attr, parentUrl) {\n  let variableList = parsed.variableList;\n  if (!variableList) {\n    parsed.variableList = variableList = {};\n  }\n  let NAME;\n  let VALUE;\n  if ('QUERYPARAM' in attr) {\n    NAME = attr.QUERYPARAM;\n    try {\n      const searchParams = new self.URL(parentUrl).searchParams;\n      if (searchParams.has(NAME)) {\n        VALUE = searchParams.get(NAME);\n      } else {\n        throw new Error(`\"${NAME}\" does not match any query parameter in URI: \"${parentUrl}\"`);\n      }\n    } catch (error) {\n      parsed.playlistParsingError || (parsed.playlistParsingError = new Error(`EXT-X-DEFINE QUERYPARAM: ${error.message}`));\n    }\n  } else {\n    NAME = attr.NAME;\n    VALUE = attr.VALUE;\n  }\n  if (NAME in variableList) {\n    parsed.playlistParsingError || (parsed.playlistParsingError = new Error(`EXT-X-DEFINE duplicate Variable Name declarations: \"${NAME}\"`));\n  } else {\n    variableList[NAME] = VALUE || '';\n  }\n}\nfunction importVariableDefinition(parsed, attr, sourceVariableList) {\n  const IMPORT = attr.IMPORT;\n  if (sourceVariableList && IMPORT in sourceVariableList) {\n    let variableList = parsed.variableList;\n    if (!variableList) {\n      parsed.variableList = variableList = {};\n    }\n    variableList[IMPORT] = sourceVariableList[IMPORT];\n  } else {\n    parsed.playlistParsingError || (parsed.playlistParsingError = new Error(`EXT-X-DEFINE IMPORT attribute not found in Multivariant Playlist: \"${IMPORT}\"`));\n  }\n}\n\n/**\n * MediaSource helper\n */\n\nfunction getMediaSource() {\n  if (typeof self === 'undefined') return undefined;\n  return self.MediaSource || self.WebKitMediaSource;\n}\n\n// from http://mp4ra.org/codecs.html\nconst sampleEntryCodesISO = {\n  audio: {\n    a3ds: true,\n    'ac-3': true,\n    'ac-4': true,\n    alac: true,\n    alaw: true,\n    dra1: true,\n    'dts+': true,\n    'dts-': true,\n    dtsc: true,\n    dtse: true,\n    dtsh: true,\n    'ec-3': true,\n    enca: true,\n    g719: true,\n    g726: true,\n    m4ae: true,\n    mha1: true,\n    mha2: true,\n    mhm1: true,\n    mhm2: true,\n    mlpa: true,\n    mp4a: true,\n    'raw ': true,\n    Opus: true,\n    opus: true,\n    // browsers expect this to be lowercase despite MP4RA says 'Opus'\n    samr: true,\n    sawb: true,\n    sawp: true,\n    sevc: true,\n    sqcp: true,\n    ssmv: true,\n    twos: true,\n    ulaw: true\n  },\n  video: {\n    avc1: true,\n    avc2: true,\n    avc3: true,\n    avc4: true,\n    avcp: true,\n    av01: true,\n    drac: true,\n    dva1: true,\n    dvav: true,\n    dvh1: true,\n    dvhe: true,\n    encv: true,\n    hev1: true,\n    hvc1: true,\n    mjp2: true,\n    mp4v: true,\n    mvc1: true,\n    mvc2: true,\n    mvc3: true,\n    mvc4: true,\n    resv: true,\n    rv60: true,\n    s263: true,\n    svc1: true,\n    svc2: true,\n    'vc-1': true,\n    vp08: true,\n    vp09: true\n  },\n  text: {\n    stpp: true,\n    wvtt: true\n  }\n};\nconst MediaSource$2 = getMediaSource();\nfunction isCodecType(codec, type) {\n  const typeCodes = sampleEntryCodesISO[type];\n  return !!typeCodes && typeCodes[codec.slice(0, 4)] === true;\n}\nfunction isCodecSupportedInMp4(codec, type) {\n  var _MediaSource$isTypeSu;\n  return (_MediaSource$isTypeSu = MediaSource$2 == null ? void 0 : MediaSource$2.isTypeSupported(`${type || 'video'}/mp4;codecs=\"${codec}\"`)) != null ? _MediaSource$isTypeSu : false;\n}\n\nconst MASTER_PLAYLIST_REGEX = /#EXT-X-STREAM-INF:([^\\r\\n]*)(?:[\\r\\n](?:#[^\\r\\n]*)?)*([^\\r\\n]+)|#EXT-X-(SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\\r\\n]*)[\\r\\n]+/g;\nconst MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g;\nconst IS_MEDIA_PLAYLIST = /^#EXT(?:INF|-X-TARGETDURATION):/m; // Handle empty Media Playlist (first EXTINF not signaled, but TARGETDURATION present)\n\nconst LEVEL_PLAYLIST_REGEX_FAST = new RegExp([/#EXTINF:\\s*(\\d*(?:\\.\\d+)?)(?:,(.*)\\s+)?/.source,\n// duration (#EXTINF:<duration>,<title>), group 1 => duration, group 2 => title\n/(?!#) *(\\S[\\S ]*)/.source,\n// segment URI, group 3 => the URI (note newline is not eaten)\n/#EXT-X-BYTERANGE:*(.+)/.source,\n// next segment's byterange, group 4 => range spec (x@y)\n/#EXT-X-PROGRAM-DATE-TIME:(.+)/.source,\n// next segment's program date/time group 5 => the datetime spec\n/#.*/.source // All other non-segment oriented tags will match with all groups empty\n].join('|'), 'g');\nconst LEVEL_PLAYLIST_REGEX_SLOW = new RegExp([/#(EXTM3U)/.source, /#EXT-X-(DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source, /#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\\d+)/.source, /#EXT-X-(DISCONTINUITY|ENDLIST|GAP)/.source, /(#)([^:]*):(.*)/.source, /(#)(.*)(?:.*)\\r?\\n?/.source].join('|'));\nclass M3U8Parser {\n  static findGroup(groups, mediaGroupId) {\n    for (let i = 0; i < groups.length; i++) {\n      const group = groups[i];\n      if (group.id === mediaGroupId) {\n        return group;\n      }\n    }\n  }\n  static convertAVC1ToAVCOTI(codec) {\n    // Convert avc1 codec string from RFC-4281 to RFC-6381 for MediaSource.isTypeSupported\n    const avcdata = codec.split('.');\n    if (avcdata.length > 2) {\n      let result = avcdata.shift() + '.';\n      result += parseInt(avcdata.shift()).toString(16);\n      result += ('000' + parseInt(avcdata.shift()).toString(16)).slice(-4);\n      return result;\n    }\n    return codec;\n  }\n  static resolve(url, baseUrl) {\n    return urlToolkitExports.buildAbsoluteURL(baseUrl, url, {\n      alwaysNormalize: true\n    });\n  }\n  static isMediaPlaylist(str) {\n    return IS_MEDIA_PLAYLIST.test(str);\n  }\n  static parseMasterPlaylist(string, baseurl) {\n    const hasVariableRefs = hasVariableReferences(string) ;\n    const parsed = {\n      contentSteering: null,\n      levels: [],\n      playlistParsingError: null,\n      sessionData: null,\n      sessionKeys: null,\n      startTimeOffset: null,\n      variableList: null,\n      hasVariableRefs\n    };\n    const levelsWithKnownCodecs = [];\n    MASTER_PLAYLIST_REGEX.lastIndex = 0;\n    let result;\n    while ((result = MASTER_PLAYLIST_REGEX.exec(string)) != null) {\n      if (result[1]) {\n        var _level$unknownCodecs;\n        // '#EXT-X-STREAM-INF' is found, parse level tag  in group 1\n        const attrs = new AttrList(result[1]);\n        {\n          substituteVariablesInAttributes(parsed, attrs, ['CODECS', 'SUPPLEMENTAL-CODECS', 'ALLOWED-CPC', 'PATHWAY-ID', 'STABLE-VARIANT-ID', 'AUDIO', 'VIDEO', 'SUBTITLES', 'CLOSED-CAPTIONS', 'NAME']);\n        }\n        const uri = substituteVariables(parsed, result[2]) ;\n        const level = {\n          attrs,\n          bitrate: attrs.decimalInteger('AVERAGE-BANDWIDTH') || attrs.decimalInteger('BANDWIDTH'),\n          name: attrs.NAME,\n          url: M3U8Parser.resolve(uri, baseurl)\n        };\n        const resolution = attrs.decimalResolution('RESOLUTION');\n        if (resolution) {\n          level.width = resolution.width;\n          level.height = resolution.height;\n        }\n        setCodecs((attrs.CODECS || '').split(/[ ,]+/).filter(c => c), level);\n        if (level.videoCodec && level.videoCodec.indexOf('avc1') !== -1) {\n          level.videoCodec = M3U8Parser.convertAVC1ToAVCOTI(level.videoCodec);\n        }\n        if (!((_level$unknownCodecs = level.unknownCodecs) != null && _level$unknownCodecs.length)) {\n          levelsWithKnownCodecs.push(level);\n        }\n        parsed.levels.push(level);\n      } else if (result[3]) {\n        const tag = result[3];\n        const attributes = result[4];\n        switch (tag) {\n          case 'SESSION-DATA':\n            {\n              // #EXT-X-SESSION-DATA\n              const sessionAttrs = new AttrList(attributes);\n              {\n                substituteVariablesInAttributes(parsed, sessionAttrs, ['DATA-ID', 'LANGUAGE', 'VALUE', 'URI']);\n              }\n              const dataId = sessionAttrs['DATA-ID'];\n              if (dataId) {\n                if (parsed.sessionData === null) {\n                  parsed.sessionData = {};\n                }\n                parsed.sessionData[dataId] = sessionAttrs;\n              }\n              break;\n            }\n          case 'SESSION-KEY':\n            {\n              // #EXT-X-SESSION-KEY\n              const sessionKey = parseKey(attributes, baseurl, parsed);\n              if (sessionKey.encrypted && sessionKey.isSupported()) {\n                if (parsed.sessionKeys === null) {\n                  parsed.sessionKeys = [];\n                }\n                parsed.sessionKeys.push(sessionKey);\n              } else {\n                logger.warn(`[Keys] Ignoring invalid EXT-X-SESSION-KEY tag: \"${attributes}\"`);\n              }\n              break;\n            }\n          case 'DEFINE':\n            {\n              // #EXT-X-DEFINE\n              {\n                const variableAttributes = new AttrList(attributes);\n                substituteVariablesInAttributes(parsed, variableAttributes, ['NAME', 'VALUE', 'QUERYPARAM']);\n                addVariableDefinition(parsed, variableAttributes, baseurl);\n              }\n              break;\n            }\n          case 'CONTENT-STEERING':\n            {\n              // #EXT-X-CONTENT-STEERING\n              const contentSteeringAttributes = new AttrList(attributes);\n              {\n                substituteVariablesInAttributes(parsed, contentSteeringAttributes, ['SERVER-URI', 'PATHWAY-ID']);\n              }\n              parsed.contentSteering = {\n                uri: M3U8Parser.resolve(contentSteeringAttributes['SERVER-URI'], baseurl),\n                pathwayId: contentSteeringAttributes['PATHWAY-ID'] || '.'\n              };\n              break;\n            }\n          case 'START':\n            {\n              // #EXT-X-START\n              parsed.startTimeOffset = parseStartTimeOffset(attributes);\n              break;\n            }\n        }\n      }\n    }\n    // Filter out levels with unknown codecs if it does not remove all levels\n    const stripUnknownCodecLevels = levelsWithKnownCodecs.length > 0 && levelsWithKnownCodecs.length < parsed.levels.length;\n    parsed.levels = stripUnknownCodecLevels ? levelsWithKnownCodecs : parsed.levels;\n    if (parsed.levels.length === 0) {\n      parsed.playlistParsingError = new Error('no levels found in manifest');\n    }\n    return parsed;\n  }\n  static parseMasterPlaylistMedia(string, baseurl, parsed) {\n    let result;\n    const results = {};\n    const levels = parsed.levels;\n    const groupsByType = {\n      AUDIO: levels.map(level => ({\n        id: level.attrs.AUDIO,\n        audioCodec: level.audioCodec\n      })),\n      SUBTITLES: levels.map(level => ({\n        id: level.attrs.SUBTITLES,\n        textCodec: level.textCodec\n      })),\n      'CLOSED-CAPTIONS': []\n    };\n    let id = 0;\n    MASTER_PLAYLIST_MEDIA_REGEX.lastIndex = 0;\n    while ((result = MASTER_PLAYLIST_MEDIA_REGEX.exec(string)) !== null) {\n      const attrs = new AttrList(result[1]);\n      const type = attrs.TYPE;\n      if (type) {\n        const groups = groupsByType[type];\n        const medias = results[type] || [];\n        results[type] = medias;\n        {\n          substituteVariablesInAttributes(parsed, attrs, ['URI', 'GROUP-ID', 'LANGUAGE', 'ASSOC-LANGUAGE', 'STABLE-RENDITION-ID', 'NAME', 'INSTREAM-ID', 'CHARACTERISTICS', 'CHANNELS']);\n        }\n        const media = {\n          attrs,\n          bitrate: 0,\n          id: id++,\n          groupId: attrs['GROUP-ID'] || '',\n          instreamId: attrs['INSTREAM-ID'],\n          name: attrs.NAME || attrs.LANGUAGE || '',\n          type,\n          default: attrs.bool('DEFAULT'),\n          autoselect: attrs.bool('AUTOSELECT'),\n          forced: attrs.bool('FORCED'),\n          lang: attrs.LANGUAGE,\n          url: attrs.URI ? M3U8Parser.resolve(attrs.URI, baseurl) : ''\n        };\n        if (groups != null && groups.length) {\n          // If there are audio or text groups signalled in the manifest, let's look for a matching codec string for this track\n          // If we don't find the track signalled, lets use the first audio groups codec we have\n          // Acting as a best guess\n          const groupCodec = M3U8Parser.findGroup(groups, media.groupId) || groups[0];\n          assignCodec(media, groupCodec, 'audioCodec');\n          assignCodec(media, groupCodec, 'textCodec');\n        }\n        medias.push(media);\n      }\n    }\n    return results;\n  }\n  static parseLevelPlaylist(string, baseurl, id, type, levelUrlId, multivariantVariableList) {\n    const level = new LevelDetails(baseurl);\n    const fragments = level.fragments;\n    // The most recent init segment seen (applies to all subsequent segments)\n    let currentInitSegment = null;\n    let currentSN = 0;\n    let currentPart = 0;\n    let totalduration = 0;\n    let discontinuityCounter = 0;\n    let prevFrag = null;\n    let frag = new Fragment(type, baseurl);\n    let result;\n    let i;\n    let levelkeys;\n    let firstPdtIndex = -1;\n    let createNextFrag = false;\n    LEVEL_PLAYLIST_REGEX_FAST.lastIndex = 0;\n    level.m3u8 = string;\n    level.hasVariableRefs = hasVariableReferences(string) ;\n    while ((result = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) !== null) {\n      if (createNextFrag) {\n        createNextFrag = false;\n        frag = new Fragment(type, baseurl);\n        // setup the next fragment for part loading\n        frag.start = totalduration;\n        frag.sn = currentSN;\n        frag.cc = discontinuityCounter;\n        frag.level = id;\n        if (currentInitSegment) {\n          frag.initSegment = currentInitSegment;\n          frag.rawProgramDateTime = currentInitSegment.rawProgramDateTime;\n          currentInitSegment.rawProgramDateTime = null;\n        }\n      }\n      const duration = result[1];\n      if (duration) {\n        // INF\n        frag.duration = parseFloat(duration);\n        // avoid sliced strings    https://github.com/video-dev/hls.js/issues/939\n        const title = (' ' + result[2]).slice(1);\n        frag.title = title || null;\n        frag.tagList.push(title ? ['INF', duration, title] : ['INF', duration]);\n      } else if (result[3]) {\n        // url\n        if (isFiniteNumber(frag.duration)) {\n          frag.start = totalduration;\n          if (levelkeys) {\n            setFragLevelKeys(frag, levelkeys, level);\n          }\n          frag.sn = currentSN;\n          frag.level = id;\n          frag.cc = discontinuityCounter;\n          frag.urlId = levelUrlId;\n          fragments.push(frag);\n          // avoid sliced strings    https://github.com/video-dev/hls.js/issues/939\n          const uri = (' ' + result[3]).slice(1);\n          frag.relurl = substituteVariables(level, uri) ;\n          assignProgramDateTime(frag, prevFrag);\n          prevFrag = frag;\n          totalduration += frag.duration;\n          currentSN++;\n          currentPart = 0;\n          createNextFrag = true;\n        }\n      } else if (result[4]) {\n        // X-BYTERANGE\n        const data = (' ' + result[4]).slice(1);\n        if (prevFrag) {\n          frag.setByteRange(data, prevFrag);\n        } else {\n          frag.setByteRange(data);\n        }\n      } else if (result[5]) {\n        // PROGRAM-DATE-TIME\n        // avoid sliced strings    https://github.com/video-dev/hls.js/issues/939\n        frag.rawProgramDateTime = (' ' + result[5]).slice(1);\n        frag.tagList.push(['PROGRAM-DATE-TIME', frag.rawProgramDateTime]);\n        if (firstPdtIndex === -1) {\n          firstPdtIndex = fragments.length;\n        }\n      } else {\n        result = result[0].match(LEVEL_PLAYLIST_REGEX_SLOW);\n        if (!result) {\n          logger.warn('No matches on slow regex match for level playlist!');\n          continue;\n        }\n        for (i = 1; i < result.length; i++) {\n          if (typeof result[i] !== 'undefined') {\n            break;\n          }\n        }\n\n        // avoid sliced strings    https://github.com/video-dev/hls.js/issues/939\n        const tag = (' ' + result[i]).slice(1);\n        const value1 = (' ' + result[i + 1]).slice(1);\n        const value2 = result[i + 2] ? (' ' + result[i + 2]).slice(1) : '';\n        switch (tag) {\n          case 'PLAYLIST-TYPE':\n            level.type = value1.toUpperCase();\n            break;\n          case 'MEDIA-SEQUENCE':\n            currentSN = level.startSN = parseInt(value1);\n            break;\n          case 'SKIP':\n            {\n              const skipAttrs = new AttrList(value1);\n              {\n                substituteVariablesInAttributes(level, skipAttrs, ['RECENTLY-REMOVED-DATERANGES']);\n              }\n              const skippedSegments = skipAttrs.decimalInteger('SKIPPED-SEGMENTS');\n              if (isFiniteNumber(skippedSegments)) {\n                level.skippedSegments = skippedSegments;\n                // This will result in fragments[] containing undefined values, which we will fill in with `mergeDetails`\n                for (let _i = skippedSegments; _i--;) {\n                  fragments.unshift(null);\n                }\n                currentSN += skippedSegments;\n              }\n              const recentlyRemovedDateranges = skipAttrs.enumeratedString('RECENTLY-REMOVED-DATERANGES');\n              if (recentlyRemovedDateranges) {\n                level.recentlyRemovedDateranges = recentlyRemovedDateranges.split('\\t');\n              }\n              break;\n            }\n          case 'TARGETDURATION':\n            level.targetduration = Math.max(parseInt(value1), 1);\n            break;\n          case 'VERSION':\n            level.version = parseInt(value1);\n            break;\n          case 'EXTM3U':\n            break;\n          case 'ENDLIST':\n            level.live = false;\n            break;\n          case '#':\n            if (value1 || value2) {\n              frag.tagList.push(value2 ? [value1, value2] : [value1]);\n            }\n            break;\n          case 'DISCONTINUITY':\n            discontinuityCounter++;\n            frag.tagList.push(['DIS']);\n            break;\n          case 'GAP':\n            frag.gap = true;\n            frag.tagList.push([tag]);\n            break;\n          case 'BITRATE':\n            frag.tagList.push([tag, value1]);\n            break;\n          case 'DATERANGE':\n            {\n              const dateRangeAttr = new AttrList(value1);\n              {\n                substituteVariablesInAttributes(level, dateRangeAttr, ['ID', 'CLASS', 'START-DATE', 'END-DATE', 'SCTE35-CMD', 'SCTE35-OUT', 'SCTE35-IN']);\n                substituteVariablesInAttributes(level, dateRangeAttr, dateRangeAttr.clientAttrs);\n              }\n              const dateRange = new DateRange(dateRangeAttr, level.dateRanges[dateRangeAttr.ID]);\n              if (dateRange.isValid || level.skippedSegments) {\n                level.dateRanges[dateRange.id] = dateRange;\n              } else {\n                logger.warn(`Ignoring invalid DATERANGE tag: \"${value1}\"`);\n              }\n              // Add to fragment tag list for backwards compatibility (< v1.2.0)\n              frag.tagList.push(['EXT-X-DATERANGE', value1]);\n              break;\n            }\n          case 'DEFINE':\n            {\n              {\n                const variableAttributes = new AttrList(value1);\n                substituteVariablesInAttributes(level, variableAttributes, ['NAME', 'VALUE', 'IMPORT', 'QUERYPARAM']);\n                if ('IMPORT' in variableAttributes) {\n                  importVariableDefinition(level, variableAttributes, multivariantVariableList);\n                } else {\n                  addVariableDefinition(level, variableAttributes, baseurl);\n                }\n              }\n              break;\n            }\n          case 'DISCONTINUITY-SEQUENCE':\n            discontinuityCounter = parseInt(value1);\n            break;\n          case 'KEY':\n            {\n              const levelKey = parseKey(value1, baseurl, level);\n              if (levelKey.isSupported()) {\n                if (levelKey.method === 'NONE') {\n                  levelkeys = undefined;\n                  break;\n                }\n                if (!levelkeys) {\n                  levelkeys = {};\n                }\n                if (levelkeys[levelKey.keyFormat]) {\n                  levelkeys = _extends({}, levelkeys);\n                }\n                levelkeys[levelKey.keyFormat] = levelKey;\n              } else {\n                logger.warn(`[Keys] Ignoring invalid EXT-X-KEY tag: \"${value1}\"`);\n              }\n              break;\n            }\n          case 'START':\n            level.startTimeOffset = parseStartTimeOffset(value1);\n            break;\n          case 'MAP':\n            {\n              const mapAttrs = new AttrList(value1);\n              {\n                substituteVariablesInAttributes(level, mapAttrs, ['BYTERANGE', 'URI']);\n              }\n              if (frag.duration) {\n                // Initial segment tag is after segment duration tag.\n                //   #EXTINF: 6.0\n                //   #EXT-X-MAP:URI=\"init.mp4\n                const init = new Fragment(type, baseurl);\n                setInitSegment(init, mapAttrs, id, levelkeys);\n                currentInitSegment = init;\n                frag.initSegment = currentInitSegment;\n                if (currentInitSegment.rawProgramDateTime && !frag.rawProgramDateTime) {\n                  frag.rawProgramDateTime = currentInitSegment.rawProgramDateTime;\n                }\n              } else {\n                // Initial segment tag is before segment duration tag\n                setInitSegment(frag, mapAttrs, id, levelkeys);\n                currentInitSegment = frag;\n                createNextFrag = true;\n              }\n              break;\n            }\n          case 'SERVER-CONTROL':\n            {\n              const serverControlAttrs = new AttrList(value1);\n              level.canBlockReload = serverControlAttrs.bool('CAN-BLOCK-RELOAD');\n              level.canSkipUntil = serverControlAttrs.optionalFloat('CAN-SKIP-UNTIL', 0);\n              level.canSkipDateRanges = level.canSkipUntil > 0 && serverControlAttrs.bool('CAN-SKIP-DATERANGES');\n              level.partHoldBack = serverControlAttrs.optionalFloat('PART-HOLD-BACK', 0);\n              level.holdBack = serverControlAttrs.optionalFloat('HOLD-BACK', 0);\n              break;\n            }\n          case 'PART-INF':\n            {\n              const partInfAttrs = new AttrList(value1);\n              level.partTarget = partInfAttrs.decimalFloatingPoint('PART-TARGET');\n              break;\n            }\n          case 'PART':\n            {\n              let partList = level.partList;\n              if (!partList) {\n                partList = level.partList = [];\n              }\n              const previousFragmentPart = currentPart > 0 ? partList[partList.length - 1] : undefined;\n              const index = currentPart++;\n              const partAttrs = new AttrList(value1);\n              {\n                substituteVariablesInAttributes(level, partAttrs, ['BYTERANGE', 'URI']);\n              }\n              const part = new Part(partAttrs, frag, baseurl, index, previousFragmentPart);\n              partList.push(part);\n              frag.duration += part.duration;\n              break;\n            }\n          case 'PRELOAD-HINT':\n            {\n              const preloadHintAttrs = new AttrList(value1);\n              {\n                substituteVariablesInAttributes(level, preloadHintAttrs, ['URI']);\n              }\n              level.preloadHint = preloadHintAttrs;\n              break;\n            }\n          case 'RENDITION-REPORT':\n            {\n              const renditionReportAttrs = new AttrList(value1);\n              {\n                substituteVariablesInAttributes(level, renditionReportAttrs, ['URI']);\n              }\n              level.renditionReports = level.renditionReports || [];\n              level.renditionReports.push(renditionReportAttrs);\n              break;\n            }\n          default:\n            logger.warn(`line parsed but not handled: ${result}`);\n            break;\n        }\n      }\n    }\n    if (prevFrag && !prevFrag.relurl) {\n      fragments.pop();\n      totalduration -= prevFrag.duration;\n      if (level.partList) {\n        level.fragmentHint = prevFrag;\n      }\n    } else if (level.partList) {\n      assignProgramDateTime(frag, prevFrag);\n      frag.cc = discontinuityCounter;\n      level.fragmentHint = frag;\n      if (levelkeys) {\n        setFragLevelKeys(frag, levelkeys, level);\n      }\n    }\n    const fragmentLength = fragments.length;\n    const firstFragment = fragments[0];\n    const lastFragment = fragments[fragmentLength - 1];\n    totalduration += level.skippedSegments * level.targetduration;\n    if (totalduration > 0 && fragmentLength && lastFragment) {\n      level.averagetargetduration = totalduration / fragmentLength;\n      const lastSn = lastFragment.sn;\n      level.endSN = lastSn !== 'initSegment' ? lastSn : 0;\n      if (!level.live) {\n        lastFragment.endList = true;\n      }\n      if (firstFragment) {\n        level.startCC = firstFragment.cc;\n      }\n    } else {\n      level.endSN = 0;\n      level.startCC = 0;\n    }\n    if (level.fragmentHint) {\n      totalduration += level.fragmentHint.duration;\n    }\n    level.totalduration = totalduration;\n    level.endCC = discontinuityCounter;\n\n    /**\n     * Backfill any missing PDT values\n     * \"If the first EXT-X-PROGRAM-DATE-TIME tag in a Playlist appears after\n     * one or more Media Segment URIs, the client SHOULD extrapolate\n     * backward from that tag (using EXTINF durations and/or media\n     * timestamps) to associate dates with those segments.\"\n     * We have already extrapolated forward, but all fragments up to the first instance of PDT do not have their PDTs\n     * computed.\n     */\n    if (firstPdtIndex > 0) {\n      backfillProgramDateTimes(fragments, firstPdtIndex);\n    }\n    return level;\n  }\n}\nfunction parseKey(keyTagAttributes, baseurl, parsed) {\n  var _keyAttrs$METHOD, _keyAttrs$KEYFORMAT;\n  // https://tools.ietf.org/html/rfc8216#section-4.3.2.4\n  const keyAttrs = new AttrList(keyTagAttributes);\n  {\n    substituteVariablesInAttributes(parsed, keyAttrs, ['KEYFORMAT', 'KEYFORMATVERSIONS', 'URI', 'IV', 'URI']);\n  }\n  const decryptmethod = (_keyAttrs$METHOD = keyAttrs.METHOD) != null ? _keyAttrs$METHOD : '';\n  const decrypturi = keyAttrs.URI;\n  const decryptiv = keyAttrs.hexadecimalInteger('IV');\n  const decryptkeyformatversions = keyAttrs.KEYFORMATVERSIONS;\n  // From RFC: This attribute is OPTIONAL; its absence indicates an implicit value of \"identity\".\n  const decryptkeyformat = (_keyAttrs$KEYFORMAT = keyAttrs.KEYFORMAT) != null ? _keyAttrs$KEYFORMAT : 'identity';\n  if (decrypturi && keyAttrs.IV && !decryptiv) {\n    logger.error(`Invalid IV: ${keyAttrs.IV}`);\n  }\n  // If decrypturi is a URI with a scheme, then baseurl will be ignored\n  // No uri is allowed when METHOD is NONE\n  const resolvedUri = decrypturi ? M3U8Parser.resolve(decrypturi, baseurl) : '';\n  const keyFormatVersions = (decryptkeyformatversions ? decryptkeyformatversions : '1').split('/').map(Number).filter(Number.isFinite);\n  return new LevelKey(decryptmethod, resolvedUri, decryptkeyformat, keyFormatVersions, decryptiv);\n}\nfunction parseStartTimeOffset(startAttributes) {\n  const startAttrs = new AttrList(startAttributes);\n  const startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET');\n  if (isFiniteNumber(startTimeOffset)) {\n    return startTimeOffset;\n  }\n  return null;\n}\nfunction setCodecs(codecs, level) {\n  ['video', 'audio', 'text'].forEach(type => {\n    const filtered = codecs.filter(codec => isCodecType(codec, type));\n    if (filtered.length) {\n      const preferred = filtered.filter(codec => {\n        return codec.lastIndexOf('avc1', 0) === 0 || codec.lastIndexOf('mp4a', 0) === 0;\n      });\n      level[`${type}Codec`] = preferred.length > 0 ? preferred[0] : filtered[0];\n\n      // remove from list\n      codecs = codecs.filter(codec => filtered.indexOf(codec) === -1);\n    }\n  });\n  level.unknownCodecs = codecs;\n}\nfunction assignCodec(media, groupItem, codecProperty) {\n  const codecValue = groupItem[codecProperty];\n  if (codecValue) {\n    media[codecProperty] = codecValue;\n  }\n}\nfunction backfillProgramDateTimes(fragments, firstPdtIndex) {\n  let fragPrev = fragments[firstPdtIndex];\n  for (let i = firstPdtIndex; i--;) {\n    const frag = fragments[i];\n    // Exit on delta-playlist skipped segments\n    if (!frag) {\n      return;\n    }\n    frag.programDateTime = fragPrev.programDateTime - frag.duration * 1000;\n    fragPrev = frag;\n  }\n}\nfunction assignProgramDateTime(frag, prevFrag) {\n  if (frag.rawProgramDateTime) {\n    frag.programDateTime = Date.parse(frag.rawProgramDateTime);\n  } else if (prevFrag != null && prevFrag.programDateTime) {\n    frag.programDateTime = prevFrag.endProgramDateTime;\n  }\n  if (!isFiniteNumber(frag.programDateTime)) {\n    frag.programDateTime = null;\n    frag.rawProgramDateTime = null;\n  }\n}\nfunction setInitSegment(frag, mapAttrs, id, levelkeys) {\n  frag.relurl = mapAttrs.URI;\n  if (mapAttrs.BYTERANGE) {\n    frag.setByteRange(mapAttrs.BYTERANGE);\n  }\n  frag.level = id;\n  frag.sn = 'initSegment';\n  if (levelkeys) {\n    frag.levelkeys = levelkeys;\n  }\n  frag.initSegment = null;\n}\nfunction setFragLevelKeys(frag, levelkeys, level) {\n  frag.levelkeys = levelkeys;\n  const {\n    encryptedFragments\n  } = level;\n  if ((!encryptedFragments.length || encryptedFragments[encryptedFragments.length - 1].levelkeys !== levelkeys) && Object.keys(levelkeys).some(format => levelkeys[format].isCommonEncryption)) {\n    encryptedFragments.push(frag);\n  }\n}\n\nvar PlaylistContextType = {\n  MANIFEST: \"manifest\",\n  LEVEL: \"level\",\n  AUDIO_TRACK: \"audioTrack\",\n  SUBTITLE_TRACK: \"subtitleTrack\"\n};\nvar PlaylistLevelType = {\n  MAIN: \"main\",\n  AUDIO: \"audio\",\n  SUBTITLE: \"subtitle\"\n};\n\nfunction mapContextToLevelType(context) {\n  const {\n    type\n  } = context;\n  switch (type) {\n    case PlaylistContextType.AUDIO_TRACK:\n      return PlaylistLevelType.AUDIO;\n    case PlaylistContextType.SUBTITLE_TRACK:\n      return PlaylistLevelType.SUBTITLE;\n    default:\n      return PlaylistLevelType.MAIN;\n  }\n}\nfunction getResponseUrl(response, context) {\n  let url = response.url;\n  // responseURL not supported on some browsers (it is used to detect URL redirection)\n  // data-uri mode also not supported (but no need to detect redirection)\n  if (url === undefined || url.indexOf('data:') === 0) {\n    // fallback to initial URL\n    url = context.url;\n  }\n  return url;\n}\nclass PlaylistLoader {\n  constructor(hls) {\n    this.hls = void 0;\n    this.loaders = Object.create(null);\n    this.variableList = null;\n    this.hls = hls;\n    this.registerListeners();\n  }\n  startLoad(startPosition) {}\n  stopLoad() {\n    this.destroyInternalLoaders();\n  }\n  registerListeners() {\n    const {\n      hls\n    } = this;\n    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);\n    hls.on(Events.AUDIO_TRACK_LOADING, this.onAudioTrackLoading, this);\n    hls.on(Events.SUBTITLE_TRACK_LOADING, this.onSubtitleTrackLoading, this);\n  }\n  unregisterListeners() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.off(Events.LEVEL_LOADING, this.onLevelLoading, this);\n    hls.off(Events.AUDIO_TRACK_LOADING, this.onAudioTrackLoading, this);\n    hls.off(Events.SUBTITLE_TRACK_LOADING, this.onSubtitleTrackLoading, this);\n  }\n\n  /**\n   * Returns defaults or configured loader-type overloads (pLoader and loader config params)\n   */\n  createInternalLoader(context) {\n    const config = this.hls.config;\n    const PLoader = config.pLoader;\n    const Loader = config.loader;\n    const InternalLoader = PLoader || Loader;\n    const loader = new InternalLoader(config);\n    this.loaders[context.type] = loader;\n    return loader;\n  }\n  getInternalLoader(context) {\n    return this.loaders[context.type];\n  }\n  resetInternalLoader(contextType) {\n    if (this.loaders[contextType]) {\n      delete this.loaders[contextType];\n    }\n  }\n\n  /**\n   * Call `destroy` on all internal loader instances mapped (one per context type)\n   */\n  destroyInternalLoaders() {\n    for (const contextType in this.loaders) {\n      const loader = this.loaders[contextType];\n      if (loader) {\n        loader.destroy();\n      }\n      this.resetInternalLoader(contextType);\n    }\n  }\n  destroy() {\n    this.variableList = null;\n    this.unregisterListeners();\n    this.destroyInternalLoaders();\n  }\n  onManifestLoading(event, data) {\n    const {\n      url\n    } = data;\n    this.variableList = null;\n    this.load({\n      id: null,\n      level: 0,\n      responseType: 'text',\n      type: PlaylistContextType.MANIFEST,\n      url,\n      deliveryDirectives: null\n    });\n  }\n  onLevelLoading(event, data) {\n    const {\n      id,\n      level,\n      url,\n      deliveryDirectives\n    } = data;\n    this.load({\n      id,\n      level,\n      responseType: 'text',\n      type: PlaylistContextType.LEVEL,\n      url,\n      deliveryDirectives\n    });\n  }\n  onAudioTrackLoading(event, data) {\n    const {\n      id,\n      groupId,\n      url,\n      deliveryDirectives\n    } = data;\n    this.load({\n      id,\n      groupId,\n      level: null,\n      responseType: 'text',\n      type: PlaylistContextType.AUDIO_TRACK,\n      url,\n      deliveryDirectives\n    });\n  }\n  onSubtitleTrackLoading(event, data) {\n    const {\n      id,\n      groupId,\n      url,\n      deliveryDirectives\n    } = data;\n    this.load({\n      id,\n      groupId,\n      level: null,\n      responseType: 'text',\n      type: PlaylistContextType.SUBTITLE_TRACK,\n      url,\n      deliveryDirectives\n    });\n  }\n  load(context) {\n    var _context$deliveryDire;\n    const config = this.hls.config;\n\n    // logger.debug(`[playlist-loader]: Loading playlist of type ${context.type}, level: ${context.level}, id: ${context.id}`);\n\n    // Check if a loader for this context already exists\n    let loader = this.getInternalLoader(context);\n    if (loader) {\n      const loaderContext = loader.context;\n      if (loaderContext && loaderContext.url === context.url) {\n        // same URL can't overlap\n        logger.trace('[playlist-loader]: playlist request ongoing');\n        return;\n      }\n      logger.log(`[playlist-loader]: aborting previous loader for type: ${context.type}`);\n      loader.abort();\n    }\n\n    // apply different configs for retries depending on\n    // context (manifest, level, audio/subs playlist)\n    let loadPolicy;\n    if (context.type === PlaylistContextType.MANIFEST) {\n      loadPolicy = config.manifestLoadPolicy.default;\n    } else {\n      loadPolicy = _extends({}, config.playlistLoadPolicy.default, {\n        timeoutRetry: null,\n        errorRetry: null\n      });\n    }\n    loader = this.createInternalLoader(context);\n\n    // Override level/track timeout for LL-HLS requests\n    // (the default of 10000ms is counter productive to blocking playlist reload requests)\n    if ((_context$deliveryDire = context.deliveryDirectives) != null && _context$deliveryDire.part) {\n      let levelDetails;\n      if (context.type === PlaylistContextType.LEVEL && context.level !== null) {\n        levelDetails = this.hls.levels[context.level].details;\n      } else if (context.type === PlaylistContextType.AUDIO_TRACK && context.id !== null) {\n        levelDetails = this.hls.audioTracks[context.id].details;\n      } else if (context.type === PlaylistContextType.SUBTITLE_TRACK && context.id !== null) {\n        levelDetails = this.hls.subtitleTracks[context.id].details;\n      }\n      if (levelDetails) {\n        const partTarget = levelDetails.partTarget;\n        const targetDuration = levelDetails.targetduration;\n        if (partTarget && targetDuration) {\n          const maxLowLatencyPlaylistRefresh = Math.max(partTarget * 3, targetDuration * 0.8) * 1000;\n          loadPolicy = _extends({}, loadPolicy, {\n            maxTimeToFirstByteMs: Math.min(maxLowLatencyPlaylistRefresh, loadPolicy.maxTimeToFirstByteMs),\n            maxLoadTimeMs: Math.min(maxLowLatencyPlaylistRefresh, loadPolicy.maxTimeToFirstByteMs)\n          });\n        }\n      }\n    }\n    const legacyRetryCompatibility = loadPolicy.errorRetry || loadPolicy.timeoutRetry || {};\n    const loaderConfig = {\n      loadPolicy,\n      timeout: loadPolicy.maxLoadTimeMs,\n      maxRetry: legacyRetryCompatibility.maxNumRetry || 0,\n      retryDelay: legacyRetryCompatibility.retryDelayMs || 0,\n      maxRetryDelay: legacyRetryCompatibility.maxRetryDelayMs || 0\n    };\n    const loaderCallbacks = {\n      onSuccess: (response, stats, context, networkDetails) => {\n        const loader = this.getInternalLoader(context);\n        this.resetInternalLoader(context.type);\n        const string = response.data;\n\n        // Validate if it is an M3U8 at all\n        if (string.indexOf('#EXTM3U') !== 0) {\n          this.handleManifestParsingError(response, context, new Error('no EXTM3U delimiter'), networkDetails || null, stats);\n          return;\n        }\n        stats.parsing.start = performance.now();\n        if (M3U8Parser.isMediaPlaylist(string)) {\n          this.handleTrackOrLevelPlaylist(response, stats, context, networkDetails || null, loader);\n        } else {\n          this.handleMasterPlaylist(response, stats, context, networkDetails);\n        }\n      },\n      onError: (response, context, networkDetails, stats) => {\n        this.handleNetworkError(context, networkDetails, false, response, stats);\n      },\n      onTimeout: (stats, context, networkDetails) => {\n        this.handleNetworkError(context, networkDetails, true, undefined, stats);\n      }\n    };\n\n    // logger.debug(`[playlist-loader]: Calling internal loader delegate for URL: ${context.url}`);\n\n    loader.load(context, loaderConfig, loaderCallbacks);\n  }\n  handleMasterPlaylist(response, stats, context, networkDetails) {\n    const hls = this.hls;\n    const string = response.data;\n    const url = getResponseUrl(response, context);\n    const parsedResult = M3U8Parser.parseMasterPlaylist(string, url);\n    if (parsedResult.playlistParsingError) {\n      this.handleManifestParsingError(response, context, parsedResult.playlistParsingError, networkDetails, stats);\n      return;\n    }\n    const {\n      contentSteering,\n      levels,\n      sessionData,\n      sessionKeys,\n      startTimeOffset,\n      variableList\n    } = parsedResult;\n    this.variableList = variableList;\n    const {\n      AUDIO: audioTracks = [],\n      SUBTITLES: subtitles,\n      'CLOSED-CAPTIONS': captions\n    } = M3U8Parser.parseMasterPlaylistMedia(string, url, parsedResult);\n    if (audioTracks.length) {\n      // check if we have found an audio track embedded in main playlist (audio track without URI attribute)\n      const embeddedAudioFound = audioTracks.some(audioTrack => !audioTrack.url);\n\n      // if no embedded audio track defined, but audio codec signaled in quality level,\n      // we need to signal this main audio track this could happen with playlists with\n      // alt audio rendition in which quality levels (main)\n      // contains both audio+video. but with mixed audio track not signaled\n      if (!embeddedAudioFound && levels[0].audioCodec && !levels[0].attrs.AUDIO) {\n        logger.log('[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one');\n        audioTracks.unshift({\n          type: 'main',\n          name: 'main',\n          groupId: 'main',\n          default: false,\n          autoselect: false,\n          forced: false,\n          id: -1,\n          attrs: new AttrList({}),\n          bitrate: 0,\n          url: ''\n        });\n      }\n    }\n    hls.trigger(Events.MANIFEST_LOADED, {\n      levels,\n      audioTracks,\n      subtitles,\n      captions,\n      contentSteering,\n      url,\n      stats,\n      networkDetails,\n      sessionData,\n      sessionKeys,\n      startTimeOffset,\n      variableList\n    });\n  }\n  handleTrackOrLevelPlaylist(response, stats, context, networkDetails, loader) {\n    const hls = this.hls;\n    const {\n      id,\n      level,\n      type\n    } = context;\n    const url = getResponseUrl(response, context);\n    const levelUrlId = isFiniteNumber(id) ? id : 0;\n    const levelId = isFiniteNumber(level) ? level : levelUrlId;\n    const levelType = mapContextToLevelType(context);\n    const levelDetails = M3U8Parser.parseLevelPlaylist(response.data, url, levelId, levelType, levelUrlId, this.variableList);\n\n    // We have done our first request (Manifest-type) and receive\n    // not a master playlist but a chunk-list (track/level)\n    // We fire the manifest-loaded event anyway with the parsed level-details\n    // by creating a single-level structure for it.\n    if (type === PlaylistContextType.MANIFEST) {\n      const singleLevel = {\n        attrs: new AttrList({}),\n        bitrate: 0,\n        details: levelDetails,\n        name: '',\n        url\n      };\n      hls.trigger(Events.MANIFEST_LOADED, {\n        levels: [singleLevel],\n        audioTracks: [],\n        url,\n        stats,\n        networkDetails,\n        sessionData: null,\n        sessionKeys: null,\n        contentSteering: null,\n        startTimeOffset: null,\n        variableList: null\n      });\n    }\n\n    // save parsing time\n    stats.parsing.end = performance.now();\n\n    // extend the context with the new levelDetails property\n    context.levelDetails = levelDetails;\n    this.handlePlaylistLoaded(levelDetails, response, stats, context, networkDetails, loader);\n  }\n  handleManifestParsingError(response, context, error, networkDetails, stats) {\n    this.hls.trigger(Events.ERROR, {\n      type: ErrorTypes.NETWORK_ERROR,\n      details: ErrorDetails.MANIFEST_PARSING_ERROR,\n      fatal: context.type === PlaylistContextType.MANIFEST,\n      url: response.url,\n      err: error,\n      error,\n      reason: error.message,\n      response,\n      context,\n      networkDetails,\n      stats\n    });\n  }\n  handleNetworkError(context, networkDetails, timeout = false, response, stats) {\n    let message = `A network ${timeout ? 'timeout' : 'error' + (response ? ' (status ' + response.code + ')' : '')} occurred while loading ${context.type}`;\n    if (context.type === PlaylistContextType.LEVEL) {\n      message += `: ${context.level} id: ${context.id}`;\n    } else if (context.type === PlaylistContextType.AUDIO_TRACK || context.type === PlaylistContextType.SUBTITLE_TRACK) {\n      message += ` id: ${context.id} group-id: \"${context.groupId}\"`;\n    }\n    const error = new Error(message);\n    logger.warn(`[playlist-loader]: ${message}`);\n    let details = ErrorDetails.UNKNOWN;\n    let fatal = false;\n    const loader = this.getInternalLoader(context);\n    switch (context.type) {\n      case PlaylistContextType.MANIFEST:\n        details = timeout ? ErrorDetails.MANIFEST_LOAD_TIMEOUT : ErrorDetails.MANIFEST_LOAD_ERROR;\n        fatal = true;\n        break;\n      case PlaylistContextType.LEVEL:\n        details = timeout ? ErrorDetails.LEVEL_LOAD_TIMEOUT : ErrorDetails.LEVEL_LOAD_ERROR;\n        fatal = false;\n        break;\n      case PlaylistContextType.AUDIO_TRACK:\n        details = timeout ? ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT : ErrorDetails.AUDIO_TRACK_LOAD_ERROR;\n        fatal = false;\n        break;\n      case PlaylistContextType.SUBTITLE_TRACK:\n        details = timeout ? ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT : ErrorDetails.SUBTITLE_LOAD_ERROR;\n        fatal = false;\n        break;\n    }\n    if (loader) {\n      this.resetInternalLoader(context.type);\n    }\n    const errorData = {\n      type: ErrorTypes.NETWORK_ERROR,\n      details,\n      fatal,\n      url: context.url,\n      loader,\n      context,\n      error,\n      networkDetails,\n      stats\n    };\n    if (response) {\n      const url = (networkDetails == null ? void 0 : networkDetails.url) || context.url;\n      errorData.response = _objectSpread2({\n        url,\n        data: undefined\n      }, response);\n    }\n    this.hls.trigger(Events.ERROR, errorData);\n  }\n  handlePlaylistLoaded(levelDetails, response, stats, context, networkDetails, loader) {\n    const hls = this.hls;\n    const {\n      type,\n      level,\n      id,\n      groupId,\n      deliveryDirectives\n    } = context;\n    const url = getResponseUrl(response, context);\n    const parent = mapContextToLevelType(context);\n    const levelIndex = typeof context.level === 'number' && parent === PlaylistLevelType.MAIN ? level : undefined;\n    if (!levelDetails.fragments.length) {\n      const _error = new Error('No Segments found in Playlist');\n      hls.trigger(Events.ERROR, {\n        type: ErrorTypes.NETWORK_ERROR,\n        details: ErrorDetails.LEVEL_EMPTY_ERROR,\n        fatal: false,\n        url,\n        error: _error,\n        reason: _error.message,\n        response,\n        context,\n        level: levelIndex,\n        parent,\n        networkDetails,\n        stats\n      });\n      return;\n    }\n    if (!levelDetails.targetduration) {\n      levelDetails.playlistParsingError = new Error('Missing Target Duration');\n    }\n    const error = levelDetails.playlistParsingError;\n    if (error) {\n      hls.trigger(Events.ERROR, {\n        type: ErrorTypes.NETWORK_ERROR,\n        details: ErrorDetails.LEVEL_PARSING_ERROR,\n        fatal: false,\n        url,\n        error,\n        reason: error.message,\n        response,\n        context,\n        level: levelIndex,\n        parent,\n        networkDetails,\n        stats\n      });\n      return;\n    }\n    if (levelDetails.live && loader) {\n      if (loader.getCacheAge) {\n        levelDetails.ageHeader = loader.getCacheAge() || 0;\n      }\n      if (!loader.getCacheAge || isNaN(levelDetails.ageHeader)) {\n        levelDetails.ageHeader = 0;\n      }\n    }\n    switch (type) {\n      case PlaylistContextType.MANIFEST:\n      case PlaylistContextType.LEVEL:\n        hls.trigger(Events.LEVEL_LOADED, {\n          details: levelDetails,\n          level: levelIndex || 0,\n          id: id || 0,\n          stats,\n          networkDetails,\n          deliveryDirectives\n        });\n        break;\n      case PlaylistContextType.AUDIO_TRACK:\n        hls.trigger(Events.AUDIO_TRACK_LOADED, {\n          details: levelDetails,\n          id: id || 0,\n          groupId: groupId || '',\n          stats,\n          networkDetails,\n          deliveryDirectives\n        });\n        break;\n      case PlaylistContextType.SUBTITLE_TRACK:\n        hls.trigger(Events.SUBTITLE_TRACK_LOADED, {\n          details: levelDetails,\n          id: id || 0,\n          groupId: groupId || '',\n          stats,\n          networkDetails,\n          deliveryDirectives\n        });\n        break;\n    }\n  }\n}\n\nfunction sendAddTrackEvent(track, videoEl) {\n  let event;\n  try {\n    event = new Event('addtrack');\n  } catch (err) {\n    // for IE11\n    event = document.createEvent('Event');\n    event.initEvent('addtrack', false, false);\n  }\n  event.track = track;\n  videoEl.dispatchEvent(event);\n}\nfunction addCueToTrack(track, cue) {\n  // Sometimes there are cue overlaps on segmented vtts so the same\n  // cue can appear more than once in different vtt files.\n  // This avoid showing duplicated cues with same timecode and text.\n  const mode = track.mode;\n  if (mode === 'disabled') {\n    track.mode = 'hidden';\n  }\n  if (track.cues && !track.cues.getCueById(cue.id)) {\n    try {\n      track.addCue(cue);\n      if (!track.cues.getCueById(cue.id)) {\n        throw new Error(`addCue is failed for: ${cue}`);\n      }\n    } catch (err) {\n      logger.debug(`[texttrack-utils]: ${err}`);\n      try {\n        const textTrackCue = new self.TextTrackCue(cue.startTime, cue.endTime, cue.text);\n        textTrackCue.id = cue.id;\n        track.addCue(textTrackCue);\n      } catch (err2) {\n        logger.debug(`[texttrack-utils]: Legacy TextTrackCue fallback failed: ${err2}`);\n      }\n    }\n  }\n  if (mode === 'disabled') {\n    track.mode = mode;\n  }\n}\nfunction clearCurrentCues(track) {\n  // When track.mode is disabled, track.cues will be null.\n  // To guarantee the removal of cues, we need to temporarily\n  // change the mode to hidden\n  const mode = track.mode;\n  if (mode === 'disabled') {\n    track.mode = 'hidden';\n  }\n  if (track.cues) {\n    for (let i = track.cues.length; i--;) {\n      track.removeCue(track.cues[i]);\n    }\n  }\n  if (mode === 'disabled') {\n    track.mode = mode;\n  }\n}\nfunction removeCuesInRange(track, start, end, predicate) {\n  const mode = track.mode;\n  if (mode === 'disabled') {\n    track.mode = 'hidden';\n  }\n  if (track.cues && track.cues.length > 0) {\n    const cues = getCuesInRange(track.cues, start, end);\n    for (let i = 0; i < cues.length; i++) {\n      if (!predicate || predicate(cues[i])) {\n        track.removeCue(cues[i]);\n      }\n    }\n  }\n  if (mode === 'disabled') {\n    track.mode = mode;\n  }\n}\n\n// Find first cue starting after given time.\n// Modified version of binary search O(log(n)).\nfunction getFirstCueIndexAfterTime(cues, time) {\n  // If first cue starts after time, start there\n  if (time < cues[0].startTime) {\n    return 0;\n  }\n  // If the last cue ends before time there is no overlap\n  const len = cues.length - 1;\n  if (time > cues[len].endTime) {\n    return -1;\n  }\n  let left = 0;\n  let right = len;\n  while (left <= right) {\n    const mid = Math.floor((right + left) / 2);\n    if (time < cues[mid].startTime) {\n      right = mid - 1;\n    } else if (time > cues[mid].startTime && left < len) {\n      left = mid + 1;\n    } else {\n      // If it's not lower or higher, it must be equal.\n      return mid;\n    }\n  }\n  // At this point, left and right have swapped.\n  // No direct match was found, left or right element must be the closest. Check which one has the smallest diff.\n  return cues[left].startTime - time < time - cues[right].startTime ? left : right;\n}\nfunction getCuesInRange(cues, start, end) {\n  const cuesFound = [];\n  const firstCueInRange = getFirstCueIndexAfterTime(cues, start);\n  if (firstCueInRange > -1) {\n    for (let i = firstCueInRange, len = cues.length; i < len; i++) {\n      const cue = cues[i];\n      if (cue.startTime >= start && cue.endTime <= end) {\n        cuesFound.push(cue);\n      } else if (cue.startTime > end) {\n        return cuesFound;\n      }\n    }\n  }\n  return cuesFound;\n}\n\nvar MetadataSchema = {\n  audioId3: \"org.id3\",\n  dateRange: \"com.apple.quicktime.HLS\",\n  emsg: \"https://aomedia.org/emsg/ID3\"\n};\n\nconst MIN_CUE_DURATION = 0.25;\nfunction getCueClass() {\n  if (typeof self === 'undefined') return undefined;\n  return self.VTTCue || self.TextTrackCue;\n}\nfunction createCueWithDataFields(Cue, startTime, endTime, data, type) {\n  let cue = new Cue(startTime, endTime, '');\n  try {\n    cue.value = data;\n    if (type) {\n      cue.type = type;\n    }\n  } catch (e) {\n    cue = new Cue(startTime, endTime, JSON.stringify(type ? _objectSpread2({\n      type\n    }, data) : data));\n  }\n  return cue;\n}\n\n// VTTCue latest draft allows an infinite duration, fallback\n// to MAX_VALUE if necessary\nconst MAX_CUE_ENDTIME = (() => {\n  const Cue = getCueClass();\n  try {\n    Cue && new Cue(0, Number.POSITIVE_INFINITY, '');\n  } catch (e) {\n    return Number.MAX_VALUE;\n  }\n  return Number.POSITIVE_INFINITY;\n})();\nfunction dateRangeDateToTimelineSeconds(date, offset) {\n  return date.getTime() / 1000 - offset;\n}\nfunction hexToArrayBuffer(str) {\n  return Uint8Array.from(str.replace(/^0x/, '').replace(/([\\da-fA-F]{2}) ?/g, '0x$1 ').replace(/ +$/, '').split(' ')).buffer;\n}\nclass ID3TrackController {\n  constructor(hls) {\n    this.hls = void 0;\n    this.id3Track = null;\n    this.media = null;\n    this.dateRangeCuesAppended = {};\n    this.hls = hls;\n    this._registerListeners();\n  }\n  destroy() {\n    this._unregisterListeners();\n    this.id3Track = null;\n    this.media = null;\n    this.dateRangeCuesAppended = {};\n    // @ts-ignore\n    this.hls = null;\n  }\n  _registerListeners() {\n    const {\n      hls\n    } = this;\n    hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.on(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this);\n    hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);\n    hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this);\n  }\n  _unregisterListeners() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.off(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this);\n    hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);\n    hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this);\n  }\n\n  // Add ID3 metatadata text track.\n  onMediaAttached(event, data) {\n    this.media = data.media;\n  }\n  onMediaDetaching() {\n    if (!this.id3Track) {\n      return;\n    }\n    clearCurrentCues(this.id3Track);\n    this.id3Track = null;\n    this.media = null;\n    this.dateRangeCuesAppended = {};\n  }\n  onManifestLoading() {\n    this.dateRangeCuesAppended = {};\n  }\n  createTrack(media) {\n    const track = this.getID3Track(media.textTracks);\n    track.mode = 'hidden';\n    return track;\n  }\n  getID3Track(textTracks) {\n    if (!this.media) {\n      return;\n    }\n    for (let i = 0; i < textTracks.length; i++) {\n      const textTrack = textTracks[i];\n      if (textTrack.kind === 'metadata' && textTrack.label === 'id3') {\n        // send 'addtrack' when reusing the textTrack for metadata,\n        // same as what we do for captions\n        sendAddTrackEvent(textTrack, this.media);\n        return textTrack;\n      }\n    }\n    return this.media.addTextTrack('metadata', 'id3');\n  }\n  onFragParsingMetadata(event, data) {\n    if (!this.media) {\n      return;\n    }\n    const {\n      hls: {\n        config: {\n          enableEmsgMetadataCues,\n          enableID3MetadataCues\n        }\n      }\n    } = this;\n    if (!enableEmsgMetadataCues && !enableID3MetadataCues) {\n      return;\n    }\n    const {\n      samples\n    } = data;\n\n    // create track dynamically\n    if (!this.id3Track) {\n      this.id3Track = this.createTrack(this.media);\n    }\n    const Cue = getCueClass();\n    if (!Cue) {\n      return;\n    }\n    for (let i = 0; i < samples.length; i++) {\n      const type = samples[i].type;\n      if (type === MetadataSchema.emsg && !enableEmsgMetadataCues || !enableID3MetadataCues) {\n        continue;\n      }\n      const frames = getID3Frames(samples[i].data);\n      if (frames) {\n        const startTime = samples[i].pts;\n        let endTime = startTime + samples[i].duration;\n        if (endTime > MAX_CUE_ENDTIME) {\n          endTime = MAX_CUE_ENDTIME;\n        }\n        const timeDiff = endTime - startTime;\n        if (timeDiff <= 0) {\n          endTime = startTime + MIN_CUE_DURATION;\n        }\n        for (let j = 0; j < frames.length; j++) {\n          const frame = frames[j];\n          // Safari doesn't put the timestamp frame in the TextTrack\n          if (!isTimeStampFrame(frame)) {\n            // add a bounds to any unbounded cues\n            this.updateId3CueEnds(startTime, type);\n            const cue = createCueWithDataFields(Cue, startTime, endTime, frame, type);\n            if (cue) {\n              this.id3Track.addCue(cue);\n            }\n          }\n        }\n      }\n    }\n  }\n  updateId3CueEnds(startTime, type) {\n    var _this$id3Track;\n    const cues = (_this$id3Track = this.id3Track) == null ? void 0 : _this$id3Track.cues;\n    if (cues) {\n      for (let i = cues.length; i--;) {\n        const cue = cues[i];\n        if (cue.type === type && cue.startTime < startTime && cue.endTime === MAX_CUE_ENDTIME) {\n          cue.endTime = startTime;\n        }\n      }\n    }\n  }\n  onBufferFlushing(event, {\n    startOffset,\n    endOffset,\n    type\n  }) {\n    const {\n      id3Track,\n      hls\n    } = this;\n    if (!hls) {\n      return;\n    }\n    const {\n      config: {\n        enableEmsgMetadataCues,\n        enableID3MetadataCues\n      }\n    } = hls;\n    if (id3Track && (enableEmsgMetadataCues || enableID3MetadataCues)) {\n      let predicate;\n      if (type === 'audio') {\n        predicate = cue => cue.type === MetadataSchema.audioId3 && enableID3MetadataCues;\n      } else if (type === 'video') {\n        predicate = cue => cue.type === MetadataSchema.emsg && enableEmsgMetadataCues;\n      } else {\n        predicate = cue => cue.type === MetadataSchema.audioId3 && enableID3MetadataCues || cue.type === MetadataSchema.emsg && enableEmsgMetadataCues;\n      }\n      removeCuesInRange(id3Track, startOffset, endOffset, predicate);\n    }\n  }\n  onLevelUpdated(event, {\n    details\n  }) {\n    if (!this.media || !details.hasProgramDateTime || !this.hls.config.enableDateRangeMetadataCues) {\n      return;\n    }\n    const {\n      dateRangeCuesAppended,\n      id3Track\n    } = this;\n    const {\n      dateRanges\n    } = details;\n    const ids = Object.keys(dateRanges);\n    // Remove cues from track not found in details.dateRanges\n    if (id3Track) {\n      const idsToRemove = Object.keys(dateRangeCuesAppended).filter(id => !ids.includes(id));\n      for (let i = idsToRemove.length; i--;) {\n        const id = idsToRemove[i];\n        Object.keys(dateRangeCuesAppended[id].cues).forEach(key => {\n          id3Track.removeCue(dateRangeCuesAppended[id].cues[key]);\n        });\n        delete dateRangeCuesAppended[id];\n      }\n    }\n    // Exit if the playlist does not have Date Ranges or does not have Program Date Time\n    const lastFragment = details.fragments[details.fragments.length - 1];\n    if (ids.length === 0 || !isFiniteNumber(lastFragment == null ? void 0 : lastFragment.programDateTime)) {\n      return;\n    }\n    if (!this.id3Track) {\n      this.id3Track = this.createTrack(this.media);\n    }\n    const dateTimeOffset = lastFragment.programDateTime / 1000 - lastFragment.start;\n    const Cue = getCueClass();\n    for (let i = 0; i < ids.length; i++) {\n      const id = ids[i];\n      const dateRange = dateRanges[id];\n      const appendedDateRangeCues = dateRangeCuesAppended[id];\n      const cues = (appendedDateRangeCues == null ? void 0 : appendedDateRangeCues.cues) || {};\n      let durationKnown = (appendedDateRangeCues == null ? void 0 : appendedDateRangeCues.durationKnown) || false;\n      const startTime = dateRangeDateToTimelineSeconds(dateRange.startDate, dateTimeOffset);\n      let endTime = MAX_CUE_ENDTIME;\n      const endDate = dateRange.endDate;\n      if (endDate) {\n        endTime = dateRangeDateToTimelineSeconds(endDate, dateTimeOffset);\n        durationKnown = true;\n      } else if (dateRange.endOnNext && !durationKnown) {\n        const nextDateRangeWithSameClass = ids.reduce((filterMapArray, id) => {\n          const candidate = dateRanges[id];\n          if (candidate.class === dateRange.class && candidate.id !== id && candidate.startDate > dateRange.startDate) {\n            filterMapArray.push(candidate);\n          }\n          return filterMapArray;\n        }, []).sort((a, b) => a.startDate.getTime() - b.startDate.getTime())[0];\n        if (nextDateRangeWithSameClass) {\n          endTime = dateRangeDateToTimelineSeconds(nextDateRangeWithSameClass.startDate, dateTimeOffset);\n          durationKnown = true;\n        }\n      }\n      const attributes = Object.keys(dateRange.attr);\n      for (let j = 0; j < attributes.length; j++) {\n        const key = attributes[j];\n        if (!isDateRangeCueAttribute(key)) {\n          continue;\n        }\n        const cue = cues[key];\n        if (cue) {\n          if (durationKnown && !appendedDateRangeCues.durationKnown) {\n            cue.endTime = endTime;\n          }\n        } else if (Cue) {\n          let data = dateRange.attr[key];\n          if (isSCTE35Attribute(key)) {\n            data = hexToArrayBuffer(data);\n          }\n          const _cue = createCueWithDataFields(Cue, startTime, endTime, {\n            key,\n            data\n          }, MetadataSchema.dateRange);\n          if (_cue) {\n            _cue.id = id;\n            this.id3Track.addCue(_cue);\n            cues[key] = _cue;\n          }\n        }\n      }\n      dateRangeCuesAppended[id] = {\n        cues,\n        dateRange,\n        durationKnown\n      };\n    }\n  }\n}\n\nclass LatencyController {\n  constructor(hls) {\n    this.hls = void 0;\n    this.config = void 0;\n    this.media = null;\n    this.levelDetails = null;\n    this.currentTime = 0;\n    this.stallCount = 0;\n    this._latency = null;\n    this.timeupdateHandler = () => this.timeupdate();\n    this.hls = hls;\n    this.config = hls.config;\n    this.registerListeners();\n  }\n  get latency() {\n    return this._latency || 0;\n  }\n  get maxLatency() {\n    const {\n      config,\n      levelDetails\n    } = this;\n    if (config.liveMaxLatencyDuration !== undefined) {\n      return config.liveMaxLatencyDuration;\n    }\n    return levelDetails ? config.liveMaxLatencyDurationCount * levelDetails.targetduration : 0;\n  }\n  get targetLatency() {\n    const {\n      levelDetails\n    } = this;\n    if (levelDetails === null) {\n      return null;\n    }\n    const {\n      holdBack,\n      partHoldBack,\n      targetduration\n    } = levelDetails;\n    const {\n      liveSyncDuration,\n      liveSyncDurationCount,\n      lowLatencyMode\n    } = this.config;\n    const userConfig = this.hls.userConfig;\n    let targetLatency = lowLatencyMode ? partHoldBack || holdBack : holdBack;\n    if (userConfig.liveSyncDuration || userConfig.liveSyncDurationCount || targetLatency === 0) {\n      targetLatency = liveSyncDuration !== undefined ? liveSyncDuration : liveSyncDurationCount * targetduration;\n    }\n    const maxLiveSyncOnStallIncrease = targetduration;\n    const liveSyncOnStallIncrease = 1.0;\n    return targetLatency + Math.min(this.stallCount * liveSyncOnStallIncrease, maxLiveSyncOnStallIncrease);\n  }\n  get liveSyncPosition() {\n    const liveEdge = this.estimateLiveEdge();\n    const targetLatency = this.targetLatency;\n    const levelDetails = this.levelDetails;\n    if (liveEdge === null || targetLatency === null || levelDetails === null) {\n      return null;\n    }\n    const edge = levelDetails.edge;\n    const syncPosition = liveEdge - targetLatency - this.edgeStalled;\n    const min = edge - levelDetails.totalduration;\n    const max = edge - (this.config.lowLatencyMode && levelDetails.partTarget || levelDetails.targetduration);\n    return Math.min(Math.max(min, syncPosition), max);\n  }\n  get drift() {\n    const {\n      levelDetails\n    } = this;\n    if (levelDetails === null) {\n      return 1;\n    }\n    return levelDetails.drift;\n  }\n  get edgeStalled() {\n    const {\n      levelDetails\n    } = this;\n    if (levelDetails === null) {\n      return 0;\n    }\n    const maxLevelUpdateAge = (this.config.lowLatencyMode && levelDetails.partTarget || levelDetails.targetduration) * 3;\n    return Math.max(levelDetails.age - maxLevelUpdateAge, 0);\n  }\n  get forwardBufferLength() {\n    const {\n      media,\n      levelDetails\n    } = this;\n    if (!media || !levelDetails) {\n      return 0;\n    }\n    const bufferedRanges = media.buffered.length;\n    return (bufferedRanges ? media.buffered.end(bufferedRanges - 1) : levelDetails.edge) - this.currentTime;\n  }\n  destroy() {\n    this.unregisterListeners();\n    this.onMediaDetaching();\n    this.levelDetails = null;\n    // @ts-ignore\n    this.hls = this.timeupdateHandler = null;\n  }\n  registerListeners() {\n    this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    this.hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    this.hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    this.hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this);\n    this.hls.on(Events.ERROR, this.onError, this);\n  }\n  unregisterListeners() {\n    this.hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    this.hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    this.hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    this.hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this);\n    this.hls.off(Events.ERROR, this.onError, this);\n  }\n  onMediaAttached(event, data) {\n    this.media = data.media;\n    this.media.addEventListener('timeupdate', this.timeupdateHandler);\n  }\n  onMediaDetaching() {\n    if (this.media) {\n      this.media.removeEventListener('timeupdate', this.timeupdateHandler);\n      this.media = null;\n    }\n  }\n  onManifestLoading() {\n    this.levelDetails = null;\n    this._latency = null;\n    this.stallCount = 0;\n  }\n  onLevelUpdated(event, {\n    details\n  }) {\n    this.levelDetails = details;\n    if (details.advanced) {\n      this.timeupdate();\n    }\n    if (!details.live && this.media) {\n      this.media.removeEventListener('timeupdate', this.timeupdateHandler);\n    }\n  }\n  onError(event, data) {\n    var _this$levelDetails;\n    if (data.details !== ErrorDetails.BUFFER_STALLED_ERROR) {\n      return;\n    }\n    this.stallCount++;\n    if ((_this$levelDetails = this.levelDetails) != null && _this$levelDetails.live) {\n      logger.warn('[playback-rate-controller]: Stall detected, adjusting target latency');\n    }\n  }\n  timeupdate() {\n    const {\n      media,\n      levelDetails\n    } = this;\n    if (!media || !levelDetails) {\n      return;\n    }\n    this.currentTime = media.currentTime;\n    const latency = this.computeLatency();\n    if (latency === null) {\n      return;\n    }\n    this._latency = latency;\n\n    // Adapt playbackRate to meet target latency in low-latency mode\n    const {\n      lowLatencyMode,\n      maxLiveSyncPlaybackRate\n    } = this.config;\n    if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1) {\n      return;\n    }\n    const targetLatency = this.targetLatency;\n    if (targetLatency === null) {\n      return;\n    }\n    const distanceFromTarget = latency - targetLatency;\n    // Only adjust playbackRate when within one target duration of targetLatency\n    // and more than one second from under-buffering.\n    // Playback further than one target duration from target can be considered DVR playback.\n    const liveMinLatencyDuration = Math.min(this.maxLatency, targetLatency + levelDetails.targetduration);\n    const inLiveRange = distanceFromTarget < liveMinLatencyDuration;\n    if (levelDetails.live && inLiveRange && distanceFromTarget > 0.05 && this.forwardBufferLength > 1) {\n      const max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate));\n      const rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - this.edgeStalled)) * 20) / 20;\n      media.playbackRate = Math.min(max, Math.max(1, rate));\n    } else if (media.playbackRate !== 1 && media.playbackRate !== 0) {\n      media.playbackRate = 1;\n    }\n  }\n  estimateLiveEdge() {\n    const {\n      levelDetails\n    } = this;\n    if (levelDetails === null) {\n      return null;\n    }\n    return levelDetails.edge + levelDetails.age;\n  }\n  computeLatency() {\n    const liveEdge = this.estimateLiveEdge();\n    if (liveEdge === null) {\n      return null;\n    }\n    return liveEdge - this.currentTime;\n  }\n}\n\nconst HdcpLevels = ['NONE', 'TYPE-0', 'TYPE-1', null];\nvar HlsSkip = {\n  No: \"\",\n  Yes: \"YES\",\n  v2: \"v2\"\n};\nfunction getSkipValue(details, msn) {\n  const {\n    canSkipUntil,\n    canSkipDateRanges,\n    endSN\n  } = details;\n  const snChangeGoal = msn !== undefined ? msn - endSN : 0;\n  if (canSkipUntil && snChangeGoal < canSkipUntil) {\n    if (canSkipDateRanges) {\n      return HlsSkip.v2;\n    }\n    return HlsSkip.Yes;\n  }\n  return HlsSkip.No;\n}\nclass HlsUrlParameters {\n  constructor(msn, part, skip) {\n    this.msn = void 0;\n    this.part = void 0;\n    this.skip = void 0;\n    this.msn = msn;\n    this.part = part;\n    this.skip = skip;\n  }\n  addDirectives(uri) {\n    const url = new self.URL(uri);\n    if (this.msn !== undefined) {\n      url.searchParams.set('_HLS_msn', this.msn.toString());\n    }\n    if (this.part !== undefined) {\n      url.searchParams.set('_HLS_part', this.part.toString());\n    }\n    if (this.skip) {\n      url.searchParams.set('_HLS_skip', this.skip);\n    }\n    return url.href;\n  }\n}\nclass Level {\n  constructor(data) {\n    this._attrs = void 0;\n    this.audioCodec = void 0;\n    this.bitrate = void 0;\n    this.codecSet = void 0;\n    this.height = void 0;\n    this.id = void 0;\n    this.name = void 0;\n    this.videoCodec = void 0;\n    this.width = void 0;\n    this.unknownCodecs = void 0;\n    this.audioGroupIds = void 0;\n    this.details = void 0;\n    this.fragmentError = 0;\n    this.loadError = 0;\n    this.loaded = void 0;\n    this.realBitrate = 0;\n    this.textGroupIds = void 0;\n    this.url = void 0;\n    this._urlId = 0;\n    this.url = [data.url];\n    this._attrs = [data.attrs];\n    this.bitrate = data.bitrate;\n    if (data.details) {\n      this.details = data.details;\n    }\n    this.id = data.id || 0;\n    this.name = data.name;\n    this.width = data.width || 0;\n    this.height = data.height || 0;\n    this.audioCodec = data.audioCodec;\n    this.videoCodec = data.videoCodec;\n    this.unknownCodecs = data.unknownCodecs;\n    this.codecSet = [data.videoCodec, data.audioCodec].filter(c => c).join(',').replace(/\\.[^.,]+/g, '');\n  }\n  get maxBitrate() {\n    return Math.max(this.realBitrate, this.bitrate);\n  }\n  get attrs() {\n    return this._attrs[this._urlId];\n  }\n  get pathwayId() {\n    return this.attrs['PATHWAY-ID'] || '.';\n  }\n  get uri() {\n    return this.url[this._urlId] || '';\n  }\n  get urlId() {\n    return this._urlId;\n  }\n  set urlId(value) {\n    const newValue = value % this.url.length;\n    if (this._urlId !== newValue) {\n      this.fragmentError = 0;\n      this.loadError = 0;\n      this.details = undefined;\n      this._urlId = newValue;\n    }\n  }\n  get audioGroupId() {\n    var _this$audioGroupIds;\n    return (_this$audioGroupIds = this.audioGroupIds) == null ? void 0 : _this$audioGroupIds[this.urlId];\n  }\n  get textGroupId() {\n    var _this$textGroupIds;\n    return (_this$textGroupIds = this.textGroupIds) == null ? void 0 : _this$textGroupIds[this.urlId];\n  }\n  addFallback(data) {\n    this.url.push(data.url);\n    this._attrs.push(data.attrs);\n  }\n}\n\nfunction updateFromToPTS(fragFrom, fragTo) {\n  const fragToPTS = fragTo.startPTS;\n  // if we know startPTS[toIdx]\n  if (isFiniteNumber(fragToPTS)) {\n    // update fragment duration.\n    // it helps to fix drifts between playlist reported duration and fragment real duration\n    let duration = 0;\n    let frag;\n    if (fragTo.sn > fragFrom.sn) {\n      duration = fragToPTS - fragFrom.start;\n      frag = fragFrom;\n    } else {\n      duration = fragFrom.start - fragToPTS;\n      frag = fragTo;\n    }\n    if (frag.duration !== duration) {\n      frag.duration = duration;\n    }\n    // we dont know startPTS[toIdx]\n  } else if (fragTo.sn > fragFrom.sn) {\n    const contiguous = fragFrom.cc === fragTo.cc;\n    // TODO: With part-loading end/durations we need to confirm the whole fragment is loaded before using (or setting) minEndPTS\n    if (contiguous && fragFrom.minEndPTS) {\n      fragTo.start = fragFrom.start + (fragFrom.minEndPTS - fragFrom.start);\n    } else {\n      fragTo.start = fragFrom.start + fragFrom.duration;\n    }\n  } else {\n    fragTo.start = Math.max(fragFrom.start - fragTo.duration, 0);\n  }\n}\nfunction updateFragPTSDTS(details, frag, startPTS, endPTS, startDTS, endDTS) {\n  const parsedMediaDuration = endPTS - startPTS;\n  if (parsedMediaDuration <= 0) {\n    logger.warn('Fragment should have a positive duration', frag);\n    endPTS = startPTS + frag.duration;\n    endDTS = startDTS + frag.duration;\n  }\n  let maxStartPTS = startPTS;\n  let minEndPTS = endPTS;\n  const fragStartPts = frag.startPTS;\n  const fragEndPts = frag.endPTS;\n  if (isFiniteNumber(fragStartPts)) {\n    // delta PTS between audio and video\n    const deltaPTS = Math.abs(fragStartPts - startPTS);\n    if (!isFiniteNumber(frag.deltaPTS)) {\n      frag.deltaPTS = deltaPTS;\n    } else {\n      frag.deltaPTS = Math.max(deltaPTS, frag.deltaPTS);\n    }\n    maxStartPTS = Math.max(startPTS, fragStartPts);\n    startPTS = Math.min(startPTS, fragStartPts);\n    startDTS = Math.min(startDTS, frag.startDTS);\n    minEndPTS = Math.min(endPTS, fragEndPts);\n    endPTS = Math.max(endPTS, fragEndPts);\n    endDTS = Math.max(endDTS, frag.endDTS);\n  }\n  const drift = startPTS - frag.start;\n  if (frag.start !== 0) {\n    frag.start = startPTS;\n  }\n  frag.duration = endPTS - frag.start;\n  frag.startPTS = startPTS;\n  frag.maxStartPTS = maxStartPTS;\n  frag.startDTS = startDTS;\n  frag.endPTS = endPTS;\n  frag.minEndPTS = minEndPTS;\n  frag.endDTS = endDTS;\n  const sn = frag.sn; // 'initSegment'\n  // exit if sn out of range\n  if (!details || sn < details.startSN || sn > details.endSN) {\n    return 0;\n  }\n  let i;\n  const fragIdx = sn - details.startSN;\n  const fragments = details.fragments;\n  // update frag reference in fragments array\n  // rationale is that fragments array might not contain this frag object.\n  // this will happen if playlist has been refreshed between frag loading and call to updateFragPTSDTS()\n  // if we don't update frag, we won't be able to propagate PTS info on the playlist\n  // resulting in invalid sliding computation\n  fragments[fragIdx] = frag;\n  // adjust fragment PTS/duration from seqnum-1 to frag 0\n  for (i = fragIdx; i > 0; i--) {\n    updateFromToPTS(fragments[i], fragments[i - 1]);\n  }\n\n  // adjust fragment PTS/duration from seqnum to last frag\n  for (i = fragIdx; i < fragments.length - 1; i++) {\n    updateFromToPTS(fragments[i], fragments[i + 1]);\n  }\n  if (details.fragmentHint) {\n    updateFromToPTS(fragments[fragments.length - 1], details.fragmentHint);\n  }\n  details.PTSKnown = details.alignedSliding = true;\n  return drift;\n}\nfunction mergeDetails(oldDetails, newDetails) {\n  // Track the last initSegment processed. Initialize it to the last one on the timeline.\n  let currentInitSegment = null;\n  const oldFragments = oldDetails.fragments;\n  for (let i = oldFragments.length - 1; i >= 0; i--) {\n    const oldInit = oldFragments[i].initSegment;\n    if (oldInit) {\n      currentInitSegment = oldInit;\n      break;\n    }\n  }\n  if (oldDetails.fragmentHint) {\n    // prevent PTS and duration from being adjusted on the next hint\n    delete oldDetails.fragmentHint.endPTS;\n  }\n  // check if old/new playlists have fragments in common\n  // loop through overlapping SN and update startPTS , cc, and duration if any found\n  let ccOffset = 0;\n  let PTSFrag;\n  mapFragmentIntersection(oldDetails, newDetails, (oldFrag, newFrag) => {\n    if (oldFrag.relurl) {\n      // Do not compare CC if the old fragment has no url. This is a level.fragmentHint used by LL-HLS parts.\n      // It maybe be off by 1 if it was created before any parts or discontinuity tags were appended to the end\n      // of the playlist.\n      ccOffset = oldFrag.cc - newFrag.cc;\n    }\n    if (isFiniteNumber(oldFrag.startPTS) && isFiniteNumber(oldFrag.endPTS)) {\n      newFrag.start = newFrag.startPTS = oldFrag.startPTS;\n      newFrag.startDTS = oldFrag.startDTS;\n      newFrag.maxStartPTS = oldFrag.maxStartPTS;\n      newFrag.endPTS = oldFrag.endPTS;\n      newFrag.endDTS = oldFrag.endDTS;\n      newFrag.minEndPTS = oldFrag.minEndPTS;\n      newFrag.duration = oldFrag.endPTS - oldFrag.startPTS;\n      if (newFrag.duration) {\n        PTSFrag = newFrag;\n      }\n\n      // PTS is known when any segment has startPTS and endPTS\n      newDetails.PTSKnown = newDetails.alignedSliding = true;\n    }\n    newFrag.elementaryStreams = oldFrag.elementaryStreams;\n    newFrag.loader = oldFrag.loader;\n    newFrag.stats = oldFrag.stats;\n    newFrag.urlId = oldFrag.urlId;\n    if (oldFrag.initSegment) {\n      newFrag.initSegment = oldFrag.initSegment;\n      currentInitSegment = oldFrag.initSegment;\n    }\n  });\n  if (currentInitSegment) {\n    const fragmentsToCheck = newDetails.fragmentHint ? newDetails.fragments.concat(newDetails.fragmentHint) : newDetails.fragments;\n    fragmentsToCheck.forEach(frag => {\n      var _currentInitSegment;\n      if (!frag.initSegment || frag.initSegment.relurl === ((_currentInitSegment = currentInitSegment) == null ? void 0 : _currentInitSegment.relurl)) {\n        frag.initSegment = currentInitSegment;\n      }\n    });\n  }\n  if (newDetails.skippedSegments) {\n    newDetails.deltaUpdateFailed = newDetails.fragments.some(frag => !frag);\n    if (newDetails.deltaUpdateFailed) {\n      logger.warn('[level-helper] Previous playlist missing segments skipped in delta playlist');\n      for (let i = newDetails.skippedSegments; i--;) {\n        newDetails.fragments.shift();\n      }\n      newDetails.startSN = newDetails.fragments[0].sn;\n      newDetails.startCC = newDetails.fragments[0].cc;\n    } else if (newDetails.canSkipDateRanges) {\n      newDetails.dateRanges = mergeDateRanges(oldDetails.dateRanges, newDetails.dateRanges, newDetails.recentlyRemovedDateranges);\n    }\n  }\n  const newFragments = newDetails.fragments;\n  if (ccOffset) {\n    logger.warn('discontinuity sliding from playlist, take drift into account');\n    for (let i = 0; i < newFragments.length; i++) {\n      newFragments[i].cc += ccOffset;\n    }\n  }\n  if (newDetails.skippedSegments) {\n    newDetails.startCC = newDetails.fragments[0].cc;\n  }\n\n  // Merge parts\n  mapPartIntersection(oldDetails.partList, newDetails.partList, (oldPart, newPart) => {\n    newPart.elementaryStreams = oldPart.elementaryStreams;\n    newPart.stats = oldPart.stats;\n  });\n\n  // if at least one fragment contains PTS info, recompute PTS information for all fragments\n  if (PTSFrag) {\n    updateFragPTSDTS(newDetails, PTSFrag, PTSFrag.startPTS, PTSFrag.endPTS, PTSFrag.startDTS, PTSFrag.endDTS);\n  } else {\n    // ensure that delta is within oldFragments range\n    // also adjust sliding in case delta is 0 (we could have old=[50-60] and new=old=[50-61])\n    // in that case we also need to adjust start offset of all fragments\n    adjustSliding(oldDetails, newDetails);\n  }\n  if (newFragments.length) {\n    newDetails.totalduration = newDetails.edge - newFragments[0].start;\n  }\n  newDetails.driftStartTime = oldDetails.driftStartTime;\n  newDetails.driftStart = oldDetails.driftStart;\n  const advancedDateTime = newDetails.advancedDateTime;\n  if (newDetails.advanced && advancedDateTime) {\n    const edge = newDetails.edge;\n    if (!newDetails.driftStart) {\n      newDetails.driftStartTime = advancedDateTime;\n      newDetails.driftStart = edge;\n    }\n    newDetails.driftEndTime = advancedDateTime;\n    newDetails.driftEnd = edge;\n  } else {\n    newDetails.driftEndTime = oldDetails.driftEndTime;\n    newDetails.driftEnd = oldDetails.driftEnd;\n    newDetails.advancedDateTime = oldDetails.advancedDateTime;\n  }\n}\nfunction mergeDateRanges(oldDateRanges, deltaDateRanges, recentlyRemovedDateranges) {\n  const dateRanges = _extends({}, oldDateRanges);\n  if (recentlyRemovedDateranges) {\n    recentlyRemovedDateranges.forEach(id => {\n      delete dateRanges[id];\n    });\n  }\n  Object.keys(deltaDateRanges).forEach(id => {\n    const dateRange = new DateRange(deltaDateRanges[id].attr, dateRanges[id]);\n    if (dateRange.isValid) {\n      dateRanges[id] = dateRange;\n    } else {\n      logger.warn(`Ignoring invalid Playlist Delta Update DATERANGE tag: \"${JSON.stringify(deltaDateRanges[id].attr)}\"`);\n    }\n  });\n  return dateRanges;\n}\nfunction mapPartIntersection(oldParts, newParts, intersectionFn) {\n  if (oldParts && newParts) {\n    let delta = 0;\n    for (let i = 0, len = oldParts.length; i <= len; i++) {\n      const oldPart = oldParts[i];\n      const newPart = newParts[i + delta];\n      if (oldPart && newPart && oldPart.index === newPart.index && oldPart.fragment.sn === newPart.fragment.sn) {\n        intersectionFn(oldPart, newPart);\n      } else {\n        delta--;\n      }\n    }\n  }\n}\nfunction mapFragmentIntersection(oldDetails, newDetails, intersectionFn) {\n  const skippedSegments = newDetails.skippedSegments;\n  const start = Math.max(oldDetails.startSN, newDetails.startSN) - newDetails.startSN;\n  const end = (oldDetails.fragmentHint ? 1 : 0) + (skippedSegments ? newDetails.endSN : Math.min(oldDetails.endSN, newDetails.endSN)) - newDetails.startSN;\n  const delta = newDetails.startSN - oldDetails.startSN;\n  const newFrags = newDetails.fragmentHint ? newDetails.fragments.concat(newDetails.fragmentHint) : newDetails.fragments;\n  const oldFrags = oldDetails.fragmentHint ? oldDetails.fragments.concat(oldDetails.fragmentHint) : oldDetails.fragments;\n  for (let i = start; i <= end; i++) {\n    const oldFrag = oldFrags[delta + i];\n    let newFrag = newFrags[i];\n    if (skippedSegments && !newFrag && i < skippedSegments) {\n      // Fill in skipped segments in delta playlist\n      newFrag = newDetails.fragments[i] = oldFrag;\n    }\n    if (oldFrag && newFrag) {\n      intersectionFn(oldFrag, newFrag);\n    }\n  }\n}\nfunction adjustSliding(oldDetails, newDetails) {\n  const delta = newDetails.startSN + newDetails.skippedSegments - oldDetails.startSN;\n  const oldFragments = oldDetails.fragments;\n  if (delta < 0 || delta >= oldFragments.length) {\n    return;\n  }\n  addSliding(newDetails, oldFragments[delta].start);\n}\nfunction addSliding(details, start) {\n  if (start) {\n    const fragments = details.fragments;\n    for (let i = details.skippedSegments; i < fragments.length; i++) {\n      fragments[i].start += start;\n    }\n    if (details.fragmentHint) {\n      details.fragmentHint.start += start;\n    }\n  }\n}\nfunction computeReloadInterval(newDetails, distanceToLiveEdgeMs = Infinity) {\n  let reloadInterval = 1000 * newDetails.targetduration;\n  if (newDetails.updated) {\n    // Use last segment duration when shorter than target duration and near live edge\n    const fragments = newDetails.fragments;\n    const liveEdgeMaxTargetDurations = 4;\n    if (fragments.length && reloadInterval * liveEdgeMaxTargetDurations > distanceToLiveEdgeMs) {\n      const lastSegmentDuration = fragments[fragments.length - 1].duration * 1000;\n      if (lastSegmentDuration < reloadInterval) {\n        reloadInterval = lastSegmentDuration;\n      }\n    }\n  } else {\n    // estimate = 'miss half average';\n    // follow HLS Spec, If the client reloads a Playlist file and finds that it has not\n    // changed then it MUST wait for a period of one-half the target\n    // duration before retrying.\n    reloadInterval /= 2;\n  }\n  return Math.round(reloadInterval);\n}\nfunction getFragmentWithSN(level, sn, fragCurrent) {\n  if (!(level != null && level.details)) {\n    return null;\n  }\n  const levelDetails = level.details;\n  let fragment = levelDetails.fragments[sn - levelDetails.startSN];\n  if (fragment) {\n    return fragment;\n  }\n  fragment = levelDetails.fragmentHint;\n  if (fragment && fragment.sn === sn) {\n    return fragment;\n  }\n  if (sn < levelDetails.startSN && fragCurrent && fragCurrent.sn === sn) {\n    return fragCurrent;\n  }\n  return null;\n}\nfunction getPartWith(level, sn, partIndex) {\n  var _level$details;\n  if (!(level != null && level.details)) {\n    return null;\n  }\n  return findPart((_level$details = level.details) == null ? void 0 : _level$details.partList, sn, partIndex);\n}\nfunction findPart(partList, sn, partIndex) {\n  if (partList) {\n    for (let i = partList.length; i--;) {\n      const part = partList[i];\n      if (part.index === partIndex && part.fragment.sn === sn) {\n        return part;\n      }\n    }\n  }\n  return null;\n}\n\nfunction isTimeoutError(error) {\n  switch (error.details) {\n    case ErrorDetails.FRAG_LOAD_TIMEOUT:\n    case ErrorDetails.KEY_LOAD_TIMEOUT:\n    case ErrorDetails.LEVEL_LOAD_TIMEOUT:\n    case ErrorDetails.MANIFEST_LOAD_TIMEOUT:\n      return true;\n  }\n  return false;\n}\nfunction getRetryConfig(loadPolicy, error) {\n  const isTimeout = isTimeoutError(error);\n  return loadPolicy.default[`${isTimeout ? 'timeout' : 'error'}Retry`];\n}\nfunction getRetryDelay(retryConfig, retryCount) {\n  // exponential backoff capped to max retry delay\n  const backoffFactor = retryConfig.backoff === 'linear' ? 1 : Math.pow(2, retryCount);\n  return Math.min(backoffFactor * retryConfig.retryDelayMs, retryConfig.maxRetryDelayMs);\n}\nfunction getLoaderConfigWithoutReties(loderConfig) {\n  return _objectSpread2(_objectSpread2({}, loderConfig), {\n    errorRetry: null,\n    timeoutRetry: null\n  });\n}\nfunction shouldRetry(retryConfig, retryCount, isTimeout, httpStatus) {\n  return !!retryConfig && retryCount < retryConfig.maxNumRetry && (retryForHttpStatus(httpStatus) || !!isTimeout);\n}\nfunction retryForHttpStatus(httpStatus) {\n  // Do not retry on status 4xx, status 0 (CORS error), or undefined (decrypt/gap/parse error)\n  return httpStatus === 0 && navigator.onLine === false || !!httpStatus && (httpStatus < 400 || httpStatus > 499);\n}\n\nconst BinarySearch = {\n  /**\n   * Searches for an item in an array which matches a certain condition.\n   * This requires the condition to only match one item in the array,\n   * and for the array to be ordered.\n   *\n   * @param list The array to search.\n   * @param comparisonFn\n   *      Called and provided a candidate item as the first argument.\n   *      Should return:\n   *          > -1 if the item should be located at a lower index than the provided item.\n   *          > 1 if the item should be located at a higher index than the provided item.\n   *          > 0 if the item is the item you're looking for.\n   *\n   * @returns the object if found, otherwise returns null\n   */\n  search: function (list, comparisonFn) {\n    let minIndex = 0;\n    let maxIndex = list.length - 1;\n    let currentIndex = null;\n    let currentElement = null;\n    while (minIndex <= maxIndex) {\n      currentIndex = (minIndex + maxIndex) / 2 | 0;\n      currentElement = list[currentIndex];\n      const comparisonResult = comparisonFn(currentElement);\n      if (comparisonResult > 0) {\n        minIndex = currentIndex + 1;\n      } else if (comparisonResult < 0) {\n        maxIndex = currentIndex - 1;\n      } else {\n        return currentElement;\n      }\n    }\n    return null;\n  }\n};\n\n/**\n * Returns first fragment whose endPdt value exceeds the given PDT, or null.\n * @param fragments - The array of candidate fragments\n * @param PDTValue - The PDT value which must be exceeded\n * @param maxFragLookUpTolerance - The amount of time that a fragment's start/end can be within in order to be considered contiguous\n */\nfunction findFragmentByPDT(fragments, PDTValue, maxFragLookUpTolerance) {\n  if (PDTValue === null || !Array.isArray(fragments) || !fragments.length || !isFiniteNumber(PDTValue)) {\n    return null;\n  }\n\n  // if less than start\n  const startPDT = fragments[0].programDateTime;\n  if (PDTValue < (startPDT || 0)) {\n    return null;\n  }\n  const endPDT = fragments[fragments.length - 1].endProgramDateTime;\n  if (PDTValue >= (endPDT || 0)) {\n    return null;\n  }\n  maxFragLookUpTolerance = maxFragLookUpTolerance || 0;\n  for (let seg = 0; seg < fragments.length; ++seg) {\n    const frag = fragments[seg];\n    if (pdtWithinToleranceTest(PDTValue, maxFragLookUpTolerance, frag)) {\n      return frag;\n    }\n  }\n  return null;\n}\n\n/**\n * Finds a fragment based on the SN of the previous fragment; or based on the needs of the current buffer.\n * This method compensates for small buffer gaps by applying a tolerance to the start of any candidate fragment, thus\n * breaking any traps which would cause the same fragment to be continuously selected within a small range.\n * @param fragPrevious - The last frag successfully appended\n * @param fragments - The array of candidate fragments\n * @param bufferEnd - The end of the contiguous buffered range the playhead is currently within\n * @param maxFragLookUpTolerance - The amount of time that a fragment's start/end can be within in order to be considered contiguous\n * @returns a matching fragment or null\n */\nfunction findFragmentByPTS(fragPrevious, fragments, bufferEnd = 0, maxFragLookUpTolerance = 0) {\n  let fragNext = null;\n  if (fragPrevious) {\n    fragNext = fragments[fragPrevious.sn - fragments[0].sn + 1] || null;\n  } else if (bufferEnd === 0 && fragments[0].start === 0) {\n    fragNext = fragments[0];\n  }\n  // Prefer the next fragment if it's within tolerance\n  if (fragNext && fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, fragNext) === 0) {\n    return fragNext;\n  }\n  // We might be seeking past the tolerance so find the best match\n  const foundFragment = BinarySearch.search(fragments, fragmentWithinToleranceTest.bind(null, bufferEnd, maxFragLookUpTolerance));\n  if (foundFragment && (foundFragment !== fragPrevious || !fragNext)) {\n    return foundFragment;\n  }\n  // If no match was found return the next fragment after fragPrevious, or null\n  return fragNext;\n}\n\n/**\n * The test function used by the findFragmentBySn's BinarySearch to look for the best match to the current buffer conditions.\n * @param candidate - The fragment to test\n * @param bufferEnd - The end of the current buffered range the playhead is currently within\n * @param maxFragLookUpTolerance - The amount of time that a fragment's start can be within in order to be considered contiguous\n * @returns 0 if it matches, 1 if too low, -1 if too high\n */\nfunction fragmentWithinToleranceTest(bufferEnd = 0, maxFragLookUpTolerance = 0, candidate) {\n  // eagerly accept an accurate match (no tolerance)\n  if (candidate.start <= bufferEnd && candidate.start + candidate.duration > bufferEnd) {\n    return 0;\n  }\n  // offset should be within fragment boundary - config.maxFragLookUpTolerance\n  // this is to cope with situations like\n  // bufferEnd = 9.991\n  // frag[Ø] : [0,10]\n  // frag[1] : [10,20]\n  // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here\n  //              frag start               frag start+duration\n  //                  |-----------------------------|\n  //              <--->                         <--->\n  //  ...--------><-----------------------------><---------....\n  // previous frag         matching fragment         next frag\n  //  return -1             return 0                 return 1\n  // logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`);\n  // Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments\n  const candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0));\n  if (candidate.start + candidate.duration - candidateLookupTolerance <= bufferEnd) {\n    return 1;\n  } else if (candidate.start - candidateLookupTolerance > bufferEnd && candidate.start) {\n    // if maxFragLookUpTolerance will have negative value then don't return -1 for first element\n    return -1;\n  }\n  return 0;\n}\n\n/**\n * The test function used by the findFragmentByPdt's BinarySearch to look for the best match to the current buffer conditions.\n * This function tests the candidate's program date time values, as represented in Unix time\n * @param candidate - The fragment to test\n * @param pdtBufferEnd - The Unix time representing the end of the current buffered range\n * @param maxFragLookUpTolerance - The amount of time that a fragment's start can be within in order to be considered contiguous\n * @returns true if contiguous, false otherwise\n */\nfunction pdtWithinToleranceTest(pdtBufferEnd, maxFragLookUpTolerance, candidate) {\n  const candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)) * 1000;\n\n  // endProgramDateTime can be null, default to zero\n  const endProgramDateTime = candidate.endProgramDateTime || 0;\n  return endProgramDateTime - candidateLookupTolerance > pdtBufferEnd;\n}\nfunction findFragWithCC(fragments, cc) {\n  return BinarySearch.search(fragments, candidate => {\n    if (candidate.cc < cc) {\n      return 1;\n    } else if (candidate.cc > cc) {\n      return -1;\n    } else {\n      return 0;\n    }\n  });\n}\n\nconst RENDITION_PENALTY_DURATION_MS = 300000;\nvar NetworkErrorAction = {\n  DoNothing: 0,\n  SendEndCallback: 1,\n  SendAlternateToPenaltyBox: 2,\n  RemoveAlternatePermanently: 3,\n  InsertDiscontinuity: 4,\n  RetryRequest: 5\n};\nvar ErrorActionFlags = {\n  None: 0,\n  MoveAllAlternatesMatchingHost: 1,\n  MoveAllAlternatesMatchingHDCP: 2,\n  SwitchToSDR: 4\n}; // Reserved for future use\nclass ErrorController {\n  constructor(hls) {\n    this.hls = void 0;\n    this.playlistError = 0;\n    this.penalizedRenditions = {};\n    this.log = void 0;\n    this.warn = void 0;\n    this.error = void 0;\n    this.hls = hls;\n    this.log = logger.log.bind(logger, `[info]:`);\n    this.warn = logger.warn.bind(logger, `[warning]:`);\n    this.error = logger.error.bind(logger, `[error]:`);\n    this.registerListeners();\n  }\n  registerListeners() {\n    const hls = this.hls;\n    hls.on(Events.ERROR, this.onError, this);\n    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this);\n  }\n  unregisterListeners() {\n    const hls = this.hls;\n    if (!hls) {\n      return;\n    }\n    hls.off(Events.ERROR, this.onError, this);\n    hls.off(Events.ERROR, this.onErrorOut, this);\n    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this);\n  }\n  destroy() {\n    this.unregisterListeners();\n    // @ts-ignore\n    this.hls = null;\n    this.penalizedRenditions = {};\n  }\n  startLoad(startPosition) {\n    this.playlistError = 0;\n  }\n  stopLoad() {}\n  getVariantLevelIndex(frag) {\n    return (frag == null ? void 0 : frag.type) === PlaylistLevelType.MAIN ? frag.level : this.hls.loadLevel;\n  }\n  onManifestLoading() {\n    this.playlistError = 0;\n    this.penalizedRenditions = {};\n  }\n  onLevelUpdated() {\n    this.playlistError = 0;\n  }\n  onError(event, data) {\n    var _data$frag, _data$level;\n    if (data.fatal) {\n      return;\n    }\n    const hls = this.hls;\n    const context = data.context;\n    switch (data.details) {\n      case ErrorDetails.FRAG_LOAD_ERROR:\n      case ErrorDetails.FRAG_LOAD_TIMEOUT:\n      case ErrorDetails.KEY_LOAD_ERROR:\n      case ErrorDetails.KEY_LOAD_TIMEOUT:\n        data.errorAction = this.getFragRetryOrSwitchAction(data);\n        return;\n      case ErrorDetails.FRAG_PARSING_ERROR:\n        // ignore empty segment errors marked as gap\n        if ((_data$frag = data.frag) != null && _data$frag.gap) {\n          data.errorAction = {\n            action: NetworkErrorAction.DoNothing,\n            flags: ErrorActionFlags.None\n          };\n          return;\n        }\n      // falls through\n      case ErrorDetails.FRAG_GAP:\n      case ErrorDetails.FRAG_DECRYPT_ERROR:\n        {\n          // Switch level if possible, otherwise allow retry count to reach max error retries\n          data.errorAction = this.getFragRetryOrSwitchAction(data);\n          data.errorAction.action = NetworkErrorAction.SendAlternateToPenaltyBox;\n          return;\n        }\n      case ErrorDetails.LEVEL_EMPTY_ERROR:\n      case ErrorDetails.LEVEL_PARSING_ERROR:\n        {\n          var _data$context, _data$context$levelDe;\n          // Only retry when empty and live\n          const levelIndex = data.parent === PlaylistLevelType.MAIN ? data.level : hls.loadLevel;\n          if (data.details === ErrorDetails.LEVEL_EMPTY_ERROR && !!((_data$context = data.context) != null && (_data$context$levelDe = _data$context.levelDetails) != null && _data$context$levelDe.live)) {\n            data.errorAction = this.getPlaylistRetryOrSwitchAction(data, levelIndex);\n          } else {\n            // Escalate to fatal if not retrying or switching\n            data.levelRetry = false;\n            data.errorAction = this.getLevelSwitchAction(data, levelIndex);\n          }\n        }\n        return;\n      case ErrorDetails.LEVEL_LOAD_ERROR:\n      case ErrorDetails.LEVEL_LOAD_TIMEOUT:\n        if (typeof (context == null ? void 0 : context.level) === 'number') {\n          data.errorAction = this.getPlaylistRetryOrSwitchAction(data, context.level);\n        }\n        return;\n      case ErrorDetails.AUDIO_TRACK_LOAD_ERROR:\n      case ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT:\n      case ErrorDetails.SUBTITLE_LOAD_ERROR:\n      case ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT:\n        if (context) {\n          const level = hls.levels[hls.loadLevel];\n          if (level && (context.type === PlaylistContextType.AUDIO_TRACK && context.groupId === level.audioGroupId || context.type === PlaylistContextType.SUBTITLE_TRACK && context.groupId === level.textGroupId)) {\n            // Perform Pathway switch or Redundant failover if possible for fastest recovery\n            // otherwise allow playlist retry count to reach max error retries\n            data.errorAction = this.getPlaylistRetryOrSwitchAction(data, hls.loadLevel);\n            data.errorAction.action = NetworkErrorAction.SendAlternateToPenaltyBox;\n            data.errorAction.flags = ErrorActionFlags.MoveAllAlternatesMatchingHost;\n            return;\n          }\n        }\n        return;\n      case ErrorDetails.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED:\n        {\n          const level = hls.levels[hls.loadLevel];\n          const restrictedHdcpLevel = level == null ? void 0 : level.attrs['HDCP-LEVEL'];\n          if (restrictedHdcpLevel) {\n            data.errorAction = {\n              action: NetworkErrorAction.SendAlternateToPenaltyBox,\n              flags: ErrorActionFlags.MoveAllAlternatesMatchingHDCP,\n              hdcpLevel: restrictedHdcpLevel\n            };\n          }\n        }\n        return;\n      case ErrorDetails.BUFFER_ADD_CODEC_ERROR:\n      case ErrorDetails.REMUX_ALLOC_ERROR:\n        data.errorAction = this.getLevelSwitchAction(data, (_data$level = data.level) != null ? _data$level : hls.loadLevel);\n        return;\n      case ErrorDetails.INTERNAL_EXCEPTION:\n      case ErrorDetails.BUFFER_APPENDING_ERROR:\n      case ErrorDetails.BUFFER_APPEND_ERROR:\n      case ErrorDetails.BUFFER_FULL_ERROR:\n      case ErrorDetails.LEVEL_SWITCH_ERROR:\n      case ErrorDetails.BUFFER_STALLED_ERROR:\n      case ErrorDetails.BUFFER_SEEK_OVER_HOLE:\n      case ErrorDetails.BUFFER_NUDGE_ON_STALL:\n        data.errorAction = {\n          action: NetworkErrorAction.DoNothing,\n          flags: ErrorActionFlags.None\n        };\n        return;\n    }\n    if (data.type === ErrorTypes.KEY_SYSTEM_ERROR) {\n      const levelIndex = this.getVariantLevelIndex(data.frag);\n      // Do not retry level. Escalate to fatal if switching levels fails.\n      data.levelRetry = false;\n      data.errorAction = this.getLevelSwitchAction(data, levelIndex);\n      return;\n    }\n  }\n  getPlaylistRetryOrSwitchAction(data, levelIndex) {\n    var _data$response;\n    const hls = this.hls;\n    const retryConfig = getRetryConfig(hls.config.playlistLoadPolicy, data);\n    const retryCount = this.playlistError++;\n    const httpStatus = (_data$response = data.response) == null ? void 0 : _data$response.code;\n    const retry = shouldRetry(retryConfig, retryCount, isTimeoutError(data), httpStatus);\n    if (retry) {\n      return {\n        action: NetworkErrorAction.RetryRequest,\n        flags: ErrorActionFlags.None,\n        retryConfig,\n        retryCount\n      };\n    }\n    const errorAction = this.getLevelSwitchAction(data, levelIndex);\n    if (retryConfig) {\n      errorAction.retryConfig = retryConfig;\n      errorAction.retryCount = retryCount;\n    }\n    return errorAction;\n  }\n  getFragRetryOrSwitchAction(data) {\n    const hls = this.hls;\n    // Share fragment error count accross media options (main, audio, subs)\n    // This allows for level based rendition switching when media option assets fail\n    const variantLevelIndex = this.getVariantLevelIndex(data.frag);\n    const level = hls.levels[variantLevelIndex];\n    const {\n      fragLoadPolicy,\n      keyLoadPolicy\n    } = hls.config;\n    const retryConfig = getRetryConfig(data.details.startsWith('key') ? keyLoadPolicy : fragLoadPolicy, data);\n    const fragmentErrors = hls.levels.reduce((acc, level) => acc + level.fragmentError, 0);\n    // Switch levels when out of retried or level index out of bounds\n    if (level) {\n      var _data$response2;\n      if (data.details !== ErrorDetails.FRAG_GAP) {\n        level.fragmentError++;\n      }\n      const httpStatus = (_data$response2 = data.response) == null ? void 0 : _data$response2.code;\n      const retry = shouldRetry(retryConfig, fragmentErrors, isTimeoutError(data), httpStatus);\n      if (retry) {\n        return {\n          action: NetworkErrorAction.RetryRequest,\n          flags: ErrorActionFlags.None,\n          retryConfig,\n          retryCount: fragmentErrors\n        };\n      }\n    }\n    // Reach max retry count, or Missing level reference\n    // Switch to valid index\n    const errorAction = this.getLevelSwitchAction(data, variantLevelIndex);\n    // Add retry details to allow skipping of FRAG_PARSING_ERROR\n    if (retryConfig) {\n      errorAction.retryConfig = retryConfig;\n      errorAction.retryCount = fragmentErrors;\n    }\n    return errorAction;\n  }\n  getLevelSwitchAction(data, levelIndex) {\n    const hls = this.hls;\n    if (levelIndex === null || levelIndex === undefined) {\n      levelIndex = hls.loadLevel;\n    }\n    const level = this.hls.levels[levelIndex];\n    if (level) {\n      level.loadError++;\n      if (hls.autoLevelEnabled) {\n        var _data$frag2, _data$context2;\n        // Search for next level to retry\n        let nextLevel = -1;\n        const {\n          levels,\n          loadLevel,\n          minAutoLevel,\n          maxAutoLevel\n        } = hls;\n        const fragErrorType = (_data$frag2 = data.frag) == null ? void 0 : _data$frag2.type;\n        const {\n          type: playlistErrorType,\n          groupId: playlistErrorGroupId\n        } = (_data$context2 = data.context) != null ? _data$context2 : {};\n        for (let i = levels.length; i--;) {\n          const candidate = (i + loadLevel) % levels.length;\n          if (candidate !== loadLevel && candidate >= minAutoLevel && candidate <= maxAutoLevel && levels[candidate].loadError === 0) {\n            const levelCandidate = levels[candidate];\n            // Skip level switch if GAP tag is found in next level at same position\n            if (data.details === ErrorDetails.FRAG_GAP && data.frag) {\n              const levelDetails = levels[candidate].details;\n              if (levelDetails) {\n                const fragCandidate = findFragmentByPTS(data.frag, levelDetails.fragments, data.frag.start);\n                if (fragCandidate != null && fragCandidate.gap) {\n                  continue;\n                }\n              }\n            } else if (playlistErrorType === PlaylistContextType.AUDIO_TRACK && playlistErrorGroupId === levelCandidate.audioGroupId || playlistErrorType === PlaylistContextType.SUBTITLE_TRACK && playlistErrorGroupId === levelCandidate.textGroupId) {\n              // For audio/subs playlist errors find another group ID or fallthrough to redundant fail-over\n              continue;\n            } else if (fragErrorType === PlaylistLevelType.AUDIO && level.audioGroupId === levelCandidate.audioGroupId || fragErrorType === PlaylistLevelType.SUBTITLE && level.textGroupId === levelCandidate.textGroupId) {\n              // For audio/subs frag errors find another group ID or fallthrough to redundant fail-over\n              continue;\n            }\n            nextLevel = candidate;\n            break;\n          }\n        }\n        if (nextLevel > -1 && hls.loadLevel !== nextLevel) {\n          data.levelRetry = true;\n          this.playlistError = 0;\n          return {\n            action: NetworkErrorAction.SendAlternateToPenaltyBox,\n            flags: ErrorActionFlags.None,\n            nextAutoLevel: nextLevel\n          };\n        }\n      }\n    }\n    // No levels to switch / Manual level selection / Level not found\n    // Resolve with Pathway switch, Redundant fail-over, or stay on lowest Level\n    return {\n      action: NetworkErrorAction.SendAlternateToPenaltyBox,\n      flags: ErrorActionFlags.MoveAllAlternatesMatchingHost\n    };\n  }\n  onErrorOut(event, data) {\n    var _data$errorAction;\n    switch ((_data$errorAction = data.errorAction) == null ? void 0 : _data$errorAction.action) {\n      case NetworkErrorAction.DoNothing:\n        break;\n      case NetworkErrorAction.SendAlternateToPenaltyBox:\n        this.sendAlternateToPenaltyBox(data);\n        if (!data.errorAction.resolved && data.details !== ErrorDetails.FRAG_GAP) {\n          data.fatal = true;\n        }\n        break;\n    }\n    if (data.fatal) {\n      this.hls.stopLoad();\n      return;\n    }\n  }\n  sendAlternateToPenaltyBox(data) {\n    const hls = this.hls;\n    const errorAction = data.errorAction;\n    if (!errorAction) {\n      return;\n    }\n    const {\n      flags,\n      hdcpLevel,\n      nextAutoLevel\n    } = errorAction;\n    switch (flags) {\n      case ErrorActionFlags.None:\n        this.switchLevel(data, nextAutoLevel);\n        break;\n      case ErrorActionFlags.MoveAllAlternatesMatchingHost:\n        {\n          // Handle Redundant Levels here. Pathway switching is handled by content-steering-controller\n          if (!errorAction.resolved) {\n            errorAction.resolved = this.redundantFailover(data);\n          }\n        }\n        break;\n      case ErrorActionFlags.MoveAllAlternatesMatchingHDCP:\n        if (hdcpLevel) {\n          hls.maxHdcpLevel = HdcpLevels[HdcpLevels.indexOf(hdcpLevel) - 1];\n          errorAction.resolved = true;\n        }\n        this.warn(`Restricting playback to HDCP-LEVEL of \"${hls.maxHdcpLevel}\" or lower`);\n        break;\n    }\n    // If not resolved by previous actions try to switch to next level\n    if (!errorAction.resolved) {\n      this.switchLevel(data, nextAutoLevel);\n    }\n  }\n  switchLevel(data, levelIndex) {\n    if (levelIndex !== undefined && data.errorAction) {\n      this.warn(`switching to level ${levelIndex} after ${data.details}`);\n      this.hls.nextAutoLevel = levelIndex;\n      data.errorAction.resolved = true;\n      // Stream controller is responsible for this but won't switch on false start\n      this.hls.nextLoadLevel = this.hls.nextAutoLevel;\n    }\n  }\n  redundantFailover(data) {\n    const {\n      hls,\n      penalizedRenditions\n    } = this;\n    const levelIndex = data.parent === PlaylistLevelType.MAIN ? data.level : hls.loadLevel;\n    const level = hls.levels[levelIndex];\n    const redundantLevels = level.url.length;\n    const errorUrlId = data.frag ? data.frag.urlId : level.urlId;\n    if (level.urlId === errorUrlId && (!data.frag || level.details)) {\n      this.penalizeRendition(level, data);\n    }\n    for (let i = 1; i < redundantLevels; i++) {\n      const newUrlId = (errorUrlId + i) % redundantLevels;\n      const penalizedRendition = penalizedRenditions[newUrlId];\n      // Check if rendition is penalized and skip if it is a bad fit for failover\n      if (!penalizedRendition || checkExpired(penalizedRendition, data, penalizedRenditions[errorUrlId])) {\n        // delete penalizedRenditions[newUrlId];\n        // Update the url id of all levels so that we stay on the same set of variants when level switching\n        this.warn(`Switching to Redundant Stream ${newUrlId + 1}/${redundantLevels}: \"${level.url[newUrlId]}\" after ${data.details}`);\n        this.playlistError = 0;\n        hls.levels.forEach(lv => {\n          lv.urlId = newUrlId;\n        });\n        hls.nextLoadLevel = levelIndex;\n        return true;\n      }\n    }\n    return false;\n  }\n  penalizeRendition(level, data) {\n    const {\n      penalizedRenditions\n    } = this;\n    const penalizedRendition = penalizedRenditions[level.urlId] || {\n      lastErrorPerfMs: 0,\n      errors: [],\n      details: undefined\n    };\n    penalizedRendition.lastErrorPerfMs = performance.now();\n    penalizedRendition.errors.push(data);\n    penalizedRendition.details = level.details;\n    penalizedRenditions[level.urlId] = penalizedRendition;\n  }\n}\nfunction checkExpired(penalizedRendition, data, currentPenaltyState) {\n  // Expire penalty for switching back to rendition after RENDITION_PENALTY_DURATION_MS\n  if (performance.now() - penalizedRendition.lastErrorPerfMs > RENDITION_PENALTY_DURATION_MS) {\n    return true;\n  }\n  // Expire penalty on GAP tag error if rendition has no GAP at position (does not cover media tracks)\n  const lastErrorDetails = penalizedRendition.details;\n  if (data.details === ErrorDetails.FRAG_GAP && lastErrorDetails && data.frag) {\n    const position = data.frag.start;\n    const candidateFrag = findFragmentByPTS(null, lastErrorDetails.fragments, position);\n    if (candidateFrag && !candidateFrag.gap) {\n      return true;\n    }\n  }\n  // Expire penalty if there are more errors in currentLevel than in penalizedRendition\n  if (currentPenaltyState && penalizedRendition.errors.length < currentPenaltyState.errors.length) {\n    const lastCandidateError = penalizedRendition.errors[penalizedRendition.errors.length - 1];\n    if (lastErrorDetails && lastCandidateError.frag && data.frag && Math.abs(lastCandidateError.frag.start - data.frag.start) > lastErrorDetails.targetduration * 3) {\n      return true;\n    }\n  }\n  return false;\n}\n\nclass BasePlaylistController {\n  constructor(hls, logPrefix) {\n    this.hls = void 0;\n    this.timer = -1;\n    this.requestScheduled = -1;\n    this.canLoad = false;\n    this.log = void 0;\n    this.warn = void 0;\n    this.log = logger.log.bind(logger, `${logPrefix}:`);\n    this.warn = logger.warn.bind(logger, `${logPrefix}:`);\n    this.hls = hls;\n  }\n  destroy() {\n    this.clearTimer();\n    // @ts-ignore\n    this.hls = this.log = this.warn = null;\n  }\n  clearTimer() {\n    clearTimeout(this.timer);\n    this.timer = -1;\n  }\n  startLoad() {\n    this.canLoad = true;\n    this.requestScheduled = -1;\n    this.loadPlaylist();\n  }\n  stopLoad() {\n    this.canLoad = false;\n    this.clearTimer();\n  }\n  switchParams(playlistUri, previous) {\n    const renditionReports = previous == null ? void 0 : previous.renditionReports;\n    if (renditionReports) {\n      let foundIndex = -1;\n      for (let i = 0; i < renditionReports.length; i++) {\n        const attr = renditionReports[i];\n        let uri;\n        try {\n          uri = new self.URL(attr.URI, previous.url).href;\n        } catch (error) {\n          logger.warn(`Could not construct new URL for Rendition Report: ${error}`);\n          uri = attr.URI || '';\n        }\n        // Use exact match. Otherwise, the last partial match, if any, will be used\n        // (Playlist URI includes a query string that the Rendition Report does not)\n        if (uri === playlistUri) {\n          foundIndex = i;\n          break;\n        } else if (uri === playlistUri.substring(0, uri.length)) {\n          foundIndex = i;\n        }\n      }\n      if (foundIndex !== -1) {\n        const attr = renditionReports[foundIndex];\n        const msn = parseInt(attr['LAST-MSN']) || (previous == null ? void 0 : previous.lastPartSn);\n        let part = parseInt(attr['LAST-PART']) || (previous == null ? void 0 : previous.lastPartIndex);\n        if (this.hls.config.lowLatencyMode) {\n          const currentGoal = Math.min(previous.age - previous.partTarget, previous.targetduration);\n          if (part >= 0 && currentGoal > previous.partTarget) {\n            part += 1;\n          }\n        }\n        return new HlsUrlParameters(msn, part >= 0 ? part : undefined, HlsSkip.No);\n      }\n    }\n  }\n  loadPlaylist(hlsUrlParameters) {\n    if (this.requestScheduled === -1) {\n      this.requestScheduled = self.performance.now();\n    }\n    // Loading is handled by the subclasses\n  }\n\n  shouldLoadPlaylist(playlist) {\n    return this.canLoad && !!playlist && !!playlist.url && (!playlist.details || playlist.details.live);\n  }\n  shouldReloadPlaylist(playlist) {\n    return this.timer === -1 && this.requestScheduled === -1 && this.shouldLoadPlaylist(playlist);\n  }\n  playlistLoaded(index, data, previousDetails) {\n    const {\n      details,\n      stats\n    } = data;\n\n    // Set last updated date-time\n    const now = self.performance.now();\n    const elapsed = stats.loading.first ? Math.max(0, now - stats.loading.first) : 0;\n    details.advancedDateTime = Date.now() - elapsed;\n\n    // if current playlist is a live playlist, arm a timer to reload it\n    if (details.live || previousDetails != null && previousDetails.live) {\n      details.reloaded(previousDetails);\n      if (previousDetails) {\n        this.log(`live playlist ${index} ${details.advanced ? 'REFRESHED ' + details.lastPartSn + '-' + details.lastPartIndex : details.updated ? 'UPDATED' : 'MISSED'}`);\n      }\n      // Merge live playlists to adjust fragment starts and fill in delta playlist skipped segments\n      if (previousDetails && details.fragments.length > 0) {\n        mergeDetails(previousDetails, details);\n      }\n      if (!this.canLoad || !details.live) {\n        return;\n      }\n      let deliveryDirectives;\n      let msn = undefined;\n      let part = undefined;\n      if (details.canBlockReload && details.endSN && details.advanced) {\n        // Load level with LL-HLS delivery directives\n        const lowLatencyMode = this.hls.config.lowLatencyMode;\n        const lastPartSn = details.lastPartSn;\n        const endSn = details.endSN;\n        const lastPartIndex = details.lastPartIndex;\n        const hasParts = lastPartIndex !== -1;\n        const lastPart = lastPartSn === endSn;\n        // When low latency mode is disabled, we'll skip part requests once the last part index is found\n        const nextSnStartIndex = lowLatencyMode ? 0 : lastPartIndex;\n        if (hasParts) {\n          msn = lastPart ? endSn + 1 : lastPartSn;\n          part = lastPart ? nextSnStartIndex : lastPartIndex + 1;\n        } else {\n          msn = endSn + 1;\n        }\n        // Low-Latency CDN Tune-in: \"age\" header and time since load indicates we're behind by more than one part\n        // Update directives to obtain the Playlist that has the estimated additional duration of media\n        const lastAdvanced = details.age;\n        const cdnAge = lastAdvanced + details.ageHeader;\n        let currentGoal = Math.min(cdnAge - details.partTarget, details.targetduration * 1.5);\n        if (currentGoal > 0) {\n          if (previousDetails && currentGoal > previousDetails.tuneInGoal) {\n            // If we attempted to get the next or latest playlist update, but currentGoal increased,\n            // then we either can't catchup, or the \"age\" header cannot be trusted.\n            this.warn(`CDN Tune-in goal increased from: ${previousDetails.tuneInGoal} to: ${currentGoal} with playlist age: ${details.age}`);\n            currentGoal = 0;\n          } else {\n            const segments = Math.floor(currentGoal / details.targetduration);\n            msn += segments;\n            if (part !== undefined) {\n              const parts = Math.round(currentGoal % details.targetduration / details.partTarget);\n              part += parts;\n            }\n            this.log(`CDN Tune-in age: ${details.ageHeader}s last advanced ${lastAdvanced.toFixed(2)}s goal: ${currentGoal} skip sn ${segments} to part ${part}`);\n          }\n          details.tuneInGoal = currentGoal;\n        }\n        deliveryDirectives = this.getDeliveryDirectives(details, data.deliveryDirectives, msn, part);\n        if (lowLatencyMode || !lastPart) {\n          this.loadPlaylist(deliveryDirectives);\n          return;\n        }\n      } else if (details.canBlockReload || details.canSkipUntil) {\n        deliveryDirectives = this.getDeliveryDirectives(details, data.deliveryDirectives, msn, part);\n      }\n      const bufferInfo = this.hls.mainForwardBufferInfo;\n      const position = bufferInfo ? bufferInfo.end - bufferInfo.len : 0;\n      const distanceToLiveEdgeMs = (details.edge - position) * 1000;\n      const reloadInterval = computeReloadInterval(details, distanceToLiveEdgeMs);\n      if (details.updated && now > this.requestScheduled + reloadInterval) {\n        this.requestScheduled = stats.loading.start;\n      }\n      if (msn !== undefined && details.canBlockReload) {\n        this.requestScheduled = stats.loading.first + reloadInterval - (details.partTarget * 1000 || 1000);\n      } else if (this.requestScheduled === -1 || this.requestScheduled + reloadInterval < now) {\n        this.requestScheduled = now;\n      } else if (this.requestScheduled - now <= 0) {\n        this.requestScheduled += reloadInterval;\n      }\n      let estimatedTimeUntilUpdate = this.requestScheduled - now;\n      estimatedTimeUntilUpdate = Math.max(0, estimatedTimeUntilUpdate);\n      this.log(`reload live playlist ${index} in ${Math.round(estimatedTimeUntilUpdate)} ms`);\n      // this.log(\n      //   `live reload ${details.updated ? 'REFRESHED' : 'MISSED'}\n      // reload in ${estimatedTimeUntilUpdate / 1000}\n      // round trip ${(stats.loading.end - stats.loading.start) / 1000}\n      // diff ${\n      //   (reloadInterval -\n      //     (estimatedTimeUntilUpdate +\n      //       stats.loading.end -\n      //       stats.loading.start)) /\n      //   1000\n      // }\n      // reload interval ${reloadInterval / 1000}\n      // target duration ${details.targetduration}\n      // distance to edge ${distanceToLiveEdgeMs / 1000}`\n      // );\n\n      this.timer = self.setTimeout(() => this.loadPlaylist(deliveryDirectives), estimatedTimeUntilUpdate);\n    } else {\n      this.clearTimer();\n    }\n  }\n  getDeliveryDirectives(details, previousDeliveryDirectives, msn, part) {\n    let skip = getSkipValue(details, msn);\n    if (previousDeliveryDirectives != null && previousDeliveryDirectives.skip && details.deltaUpdateFailed) {\n      msn = previousDeliveryDirectives.msn;\n      part = previousDeliveryDirectives.part;\n      skip = HlsSkip.No;\n    }\n    return new HlsUrlParameters(msn, part, skip);\n  }\n  checkRetry(errorEvent) {\n    const errorDetails = errorEvent.details;\n    const isTimeout = isTimeoutError(errorEvent);\n    const errorAction = errorEvent.errorAction;\n    const {\n      action,\n      retryCount = 0,\n      retryConfig\n    } = errorAction || {};\n    const retry = !!errorAction && !!retryConfig && (action === NetworkErrorAction.RetryRequest || !errorAction.resolved && action === NetworkErrorAction.SendAlternateToPenaltyBox);\n    if (retry) {\n      var _errorEvent$context;\n      this.requestScheduled = -1;\n      if (retryCount >= retryConfig.maxNumRetry) {\n        return false;\n      }\n      if (isTimeout && (_errorEvent$context = errorEvent.context) != null && _errorEvent$context.deliveryDirectives) {\n        // The LL-HLS request already timed out so retry immediately\n        this.warn(`Retrying playlist loading ${retryCount + 1}/${retryConfig.maxNumRetry} after \"${errorDetails}\" without delivery-directives`);\n        this.loadPlaylist();\n      } else {\n        const delay = getRetryDelay(retryConfig, retryCount);\n        // Schedule level/track reload\n        this.timer = self.setTimeout(() => this.loadPlaylist(), delay);\n        this.warn(`Retrying playlist loading ${retryCount + 1}/${retryConfig.maxNumRetry} after \"${errorDetails}\" in ${delay}ms`);\n      }\n      // `levelRetry = true` used to inform other controllers that a retry is happening\n      errorEvent.levelRetry = true;\n      errorAction.resolved = true;\n    }\n    return retry;\n  }\n}\n\nlet chromeOrFirefox;\nclass LevelController extends BasePlaylistController {\n  constructor(hls, contentSteeringController) {\n    super(hls, '[level-controller]');\n    this._levels = [];\n    this._firstLevel = -1;\n    this._startLevel = void 0;\n    this.currentLevel = null;\n    this.currentLevelIndex = -1;\n    this.manualLevelIndex = -1;\n    this.steering = void 0;\n    this.onParsedComplete = void 0;\n    this.steering = contentSteeringController;\n    this._registerListeners();\n  }\n  _registerListeners() {\n    const {\n      hls\n    } = this;\n    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);\n    hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);\n    hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);\n    hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);\n    hls.on(Events.FRAG_LOADED, this.onFragLoaded, this);\n    hls.on(Events.ERROR, this.onError, this);\n  }\n  _unregisterListeners() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);\n    hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);\n    hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);\n    hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);\n    hls.off(Events.FRAG_LOADED, this.onFragLoaded, this);\n    hls.off(Events.ERROR, this.onError, this);\n  }\n  destroy() {\n    this._unregisterListeners();\n    this.steering = null;\n    this.resetLevels();\n    super.destroy();\n  }\n  startLoad() {\n    const levels = this._levels;\n\n    // clean up live level details to force reload them, and reset load errors\n    levels.forEach(level => {\n      level.loadError = 0;\n      level.fragmentError = 0;\n    });\n    super.startLoad();\n  }\n  resetLevels() {\n    this._startLevel = undefined;\n    this.manualLevelIndex = -1;\n    this.currentLevelIndex = -1;\n    this.currentLevel = null;\n    this._levels = [];\n  }\n  onManifestLoading(event, data) {\n    this.resetLevels();\n  }\n  onManifestLoaded(event, data) {\n    const levels = [];\n    const levelSet = {};\n    let levelFromSet;\n\n    // regroup redundant levels together\n    data.levels.forEach(levelParsed => {\n      var _levelParsed$audioCod;\n      const attributes = levelParsed.attrs;\n\n      // erase audio codec info if browser does not support mp4a.40.34.\n      // demuxer will autodetect codec and fallback to mpeg/audio\n      if (((_levelParsed$audioCod = levelParsed.audioCodec) == null ? void 0 : _levelParsed$audioCod.indexOf('mp4a.40.34')) !== -1) {\n        chromeOrFirefox || (chromeOrFirefox = /chrome|firefox/i.test(navigator.userAgent));\n        if (chromeOrFirefox) {\n          levelParsed.audioCodec = undefined;\n        }\n      }\n      const {\n        AUDIO,\n        CODECS,\n        'FRAME-RATE': FRAMERATE,\n        'PATHWAY-ID': PATHWAY,\n        RESOLUTION,\n        SUBTITLES\n      } = attributes;\n      const contentSteeringPrefix = `${PATHWAY || '.'}-` ;\n      const levelKey = `${contentSteeringPrefix}${levelParsed.bitrate}-${RESOLUTION}-${FRAMERATE}-${CODECS}`;\n      levelFromSet = levelSet[levelKey];\n      if (!levelFromSet) {\n        levelFromSet = new Level(levelParsed);\n        levelSet[levelKey] = levelFromSet;\n        levels.push(levelFromSet);\n      } else {\n        levelFromSet.addFallback(levelParsed);\n      }\n      addGroupId(levelFromSet, 'audio', AUDIO);\n      addGroupId(levelFromSet, 'text', SUBTITLES);\n    });\n    this.filterAndSortMediaOptions(levels, data);\n  }\n  filterAndSortMediaOptions(unfilteredLevels, data) {\n    let audioTracks = [];\n    let subtitleTracks = [];\n    let resolutionFound = false;\n    let videoCodecFound = false;\n    let audioCodecFound = false;\n\n    // only keep levels with supported audio/video codecs\n    let levels = unfilteredLevels.filter(({\n      audioCodec,\n      videoCodec,\n      width,\n      height,\n      unknownCodecs\n    }) => {\n      resolutionFound || (resolutionFound = !!(width && height));\n      videoCodecFound || (videoCodecFound = !!videoCodec);\n      audioCodecFound || (audioCodecFound = !!audioCodec);\n      return !(unknownCodecs != null && unknownCodecs.length) && (!audioCodec || isCodecSupportedInMp4(audioCodec, 'audio')) && (!videoCodec || isCodecSupportedInMp4(videoCodec, 'video'));\n    });\n\n    // remove audio-only level if we also have levels with video codecs or RESOLUTION signalled\n    if ((resolutionFound || videoCodecFound) && audioCodecFound) {\n      levels = levels.filter(({\n        videoCodec,\n        width,\n        height\n      }) => !!videoCodec || !!(width && height));\n    }\n    if (levels.length === 0) {\n      // Dispatch error after MANIFEST_LOADED is done propagating\n      Promise.resolve().then(() => {\n        if (this.hls) {\n          const error = new Error('no level with compatible codecs found in manifest');\n          this.hls.trigger(Events.ERROR, {\n            type: ErrorTypes.MEDIA_ERROR,\n            details: ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR,\n            fatal: true,\n            url: data.url,\n            error,\n            reason: error.message\n          });\n        }\n      });\n      return;\n    }\n    if (data.audioTracks) {\n      audioTracks = data.audioTracks.filter(track => !track.audioCodec || isCodecSupportedInMp4(track.audioCodec, 'audio'));\n      // Assign ids after filtering as array indices by group-id\n      assignTrackIdsByGroup(audioTracks);\n    }\n    if (data.subtitles) {\n      subtitleTracks = data.subtitles;\n      assignTrackIdsByGroup(subtitleTracks);\n    }\n    // start bitrate is the first bitrate of the manifest\n    const unsortedLevels = levels.slice(0);\n    // sort levels from lowest to highest\n    levels.sort((a, b) => {\n      if (a.attrs['HDCP-LEVEL'] !== b.attrs['HDCP-LEVEL']) {\n        return (a.attrs['HDCP-LEVEL'] || '') > (b.attrs['HDCP-LEVEL'] || '') ? 1 : -1;\n      }\n      if (a.bitrate !== b.bitrate) {\n        return a.bitrate - b.bitrate;\n      }\n      if (a.attrs['FRAME-RATE'] !== b.attrs['FRAME-RATE']) {\n        return a.attrs.decimalFloatingPoint('FRAME-RATE') - b.attrs.decimalFloatingPoint('FRAME-RATE');\n      }\n      if (a.attrs.SCORE !== b.attrs.SCORE) {\n        return a.attrs.decimalFloatingPoint('SCORE') - b.attrs.decimalFloatingPoint('SCORE');\n      }\n      if (resolutionFound && a.height !== b.height) {\n        return a.height - b.height;\n      }\n      return 0;\n    });\n    let firstLevelInPlaylist = unsortedLevels[0];\n    if (this.steering) {\n      levels = this.steering.filterParsedLevels(levels);\n      if (levels.length !== unsortedLevels.length) {\n        for (let i = 0; i < unsortedLevels.length; i++) {\n          if (unsortedLevels[i].pathwayId === levels[0].pathwayId) {\n            firstLevelInPlaylist = unsortedLevels[i];\n            break;\n          }\n        }\n      }\n    }\n    this._levels = levels;\n\n    // find index of first level in sorted levels\n    for (let i = 0; i < levels.length; i++) {\n      if (levels[i] === firstLevelInPlaylist) {\n        this._firstLevel = i;\n        this.log(`manifest loaded, ${levels.length} level(s) found, first bitrate: ${firstLevelInPlaylist.bitrate}`);\n        break;\n      }\n    }\n\n    // Audio is only alternate if manifest include a URI along with the audio group tag,\n    // and this is not an audio-only stream where levels contain audio-only\n    const audioOnly = audioCodecFound && !videoCodecFound;\n    const edata = {\n      levels,\n      audioTracks,\n      subtitleTracks,\n      sessionData: data.sessionData,\n      sessionKeys: data.sessionKeys,\n      firstLevel: this._firstLevel,\n      stats: data.stats,\n      audio: audioCodecFound,\n      video: videoCodecFound,\n      altAudio: !audioOnly && audioTracks.some(t => !!t.url)\n    };\n    this.hls.trigger(Events.MANIFEST_PARSED, edata);\n\n    // Initiate loading after all controllers have received MANIFEST_PARSED\n    if (this.hls.config.autoStartLoad || this.hls.forceStartLoad) {\n      this.hls.startLoad(this.hls.config.startPosition);\n    }\n  }\n  get levels() {\n    if (this._levels.length === 0) {\n      return null;\n    }\n    return this._levels;\n  }\n  get level() {\n    return this.currentLevelIndex;\n  }\n  set level(newLevel) {\n    const levels = this._levels;\n    if (levels.length === 0) {\n      return;\n    }\n    // check if level idx is valid\n    if (newLevel < 0 || newLevel >= levels.length) {\n      // invalid level id given, trigger error\n      const error = new Error('invalid level idx');\n      const fatal = newLevel < 0;\n      this.hls.trigger(Events.ERROR, {\n        type: ErrorTypes.OTHER_ERROR,\n        details: ErrorDetails.LEVEL_SWITCH_ERROR,\n        level: newLevel,\n        fatal,\n        error,\n        reason: error.message\n      });\n      if (fatal) {\n        return;\n      }\n      newLevel = Math.min(newLevel, levels.length - 1);\n    }\n    const lastLevelIndex = this.currentLevelIndex;\n    const lastLevel = this.currentLevel;\n    const lastPathwayId = lastLevel ? lastLevel.attrs['PATHWAY-ID'] : undefined;\n    const level = levels[newLevel];\n    const pathwayId = level.attrs['PATHWAY-ID'];\n    this.currentLevelIndex = newLevel;\n    this.currentLevel = level;\n    if (lastLevelIndex === newLevel && level.details && lastLevel && lastPathwayId === pathwayId) {\n      return;\n    }\n    this.log(`Switching to level ${newLevel}${pathwayId ? ' with Pathway ' + pathwayId : ''} from level ${lastLevelIndex}${lastPathwayId ? ' with Pathway ' + lastPathwayId : ''}`);\n    const levelSwitchingData = _extends({}, level, {\n      level: newLevel,\n      maxBitrate: level.maxBitrate,\n      attrs: level.attrs,\n      uri: level.uri,\n      urlId: level.urlId\n    });\n    // @ts-ignore\n    delete levelSwitchingData._attrs;\n    // @ts-ignore\n    delete levelSwitchingData._urlId;\n    this.hls.trigger(Events.LEVEL_SWITCHING, levelSwitchingData);\n    // check if we need to load playlist for this level\n    const levelDetails = level.details;\n    if (!levelDetails || levelDetails.live) {\n      // level not retrieved yet, or live playlist we need to (re)load it\n      const hlsUrlParameters = this.switchParams(level.uri, lastLevel == null ? void 0 : lastLevel.details);\n      this.loadPlaylist(hlsUrlParameters);\n    }\n  }\n  get manualLevel() {\n    return this.manualLevelIndex;\n  }\n  set manualLevel(newLevel) {\n    this.manualLevelIndex = newLevel;\n    if (this._startLevel === undefined) {\n      this._startLevel = newLevel;\n    }\n    if (newLevel !== -1) {\n      this.level = newLevel;\n    }\n  }\n  get firstLevel() {\n    return this._firstLevel;\n  }\n  set firstLevel(newLevel) {\n    this._firstLevel = newLevel;\n  }\n  get startLevel() {\n    // hls.startLevel takes precedence over config.startLevel\n    // if none of these values are defined, fallback on this._firstLevel (first quality level appearing in variant manifest)\n    if (this._startLevel === undefined) {\n      const configStartLevel = this.hls.config.startLevel;\n      if (configStartLevel !== undefined) {\n        return configStartLevel;\n      } else {\n        return this._firstLevel;\n      }\n    } else {\n      return this._startLevel;\n    }\n  }\n  set startLevel(newLevel) {\n    this._startLevel = newLevel;\n  }\n  onError(event, data) {\n    if (data.fatal || !data.context) {\n      return;\n    }\n    if (data.context.type === PlaylistContextType.LEVEL && data.context.level === this.level) {\n      this.checkRetry(data);\n    }\n  }\n\n  // reset errors on the successful load of a fragment\n  onFragLoaded(event, {\n    frag\n  }) {\n    if (frag !== undefined && frag.type === PlaylistLevelType.MAIN) {\n      const level = this._levels[frag.level];\n      if (level !== undefined) {\n        level.loadError = 0;\n      }\n    }\n  }\n  onLevelLoaded(event, data) {\n    var _data$deliveryDirecti2;\n    const {\n      level,\n      details\n    } = data;\n    const curLevel = this._levels[level];\n    if (!curLevel) {\n      var _data$deliveryDirecti;\n      this.warn(`Invalid level index ${level}`);\n      if ((_data$deliveryDirecti = data.deliveryDirectives) != null && _data$deliveryDirecti.skip) {\n        details.deltaUpdateFailed = true;\n      }\n      return;\n    }\n\n    // only process level loaded events matching with expected level\n    if (level === this.currentLevelIndex) {\n      // reset level load error counter on successful level loaded only if there is no issues with fragments\n      if (curLevel.fragmentError === 0) {\n        curLevel.loadError = 0;\n      }\n      this.playlistLoaded(level, data, curLevel.details);\n    } else if ((_data$deliveryDirecti2 = data.deliveryDirectives) != null && _data$deliveryDirecti2.skip) {\n      // received a delta playlist update that cannot be merged\n      details.deltaUpdateFailed = true;\n    }\n  }\n  onAudioTrackSwitched(event, data) {\n    const currentLevel = this.currentLevel;\n    if (!currentLevel) {\n      return;\n    }\n    const audioGroupId = this.hls.audioTracks[data.id].groupId;\n    if (currentLevel.audioGroupIds && currentLevel.audioGroupId !== audioGroupId) {\n      let urlId = -1;\n      for (let i = 0; i < currentLevel.audioGroupIds.length; i++) {\n        if (currentLevel.audioGroupIds[i] === audioGroupId) {\n          urlId = i;\n          break;\n        }\n      }\n      if (urlId !== -1 && urlId !== currentLevel.urlId) {\n        currentLevel.urlId = urlId;\n        if (this.canLoad) {\n          this.startLoad();\n        }\n      }\n    }\n  }\n  loadPlaylist(hlsUrlParameters) {\n    super.loadPlaylist();\n    const currentLevelIndex = this.currentLevelIndex;\n    const currentLevel = this.currentLevel;\n    if (currentLevel && this.shouldLoadPlaylist(currentLevel)) {\n      const id = currentLevel.urlId;\n      let url = currentLevel.uri;\n      if (hlsUrlParameters) {\n        try {\n          url = hlsUrlParameters.addDirectives(url);\n        } catch (error) {\n          this.warn(`Could not construct new URL with HLS Delivery Directives: ${error}`);\n        }\n      }\n      const pathwayId = currentLevel.attrs['PATHWAY-ID'];\n      this.log(`Loading level index ${currentLevelIndex}${(hlsUrlParameters == null ? void 0 : hlsUrlParameters.msn) !== undefined ? ' at sn ' + hlsUrlParameters.msn + ' part ' + hlsUrlParameters.part : ''} with${pathwayId ? ' Pathway ' + pathwayId : ''} URI ${id + 1}/${currentLevel.url.length} ${url}`);\n\n      // console.log('Current audio track group ID:', this.hls.audioTracks[this.hls.audioTrack].groupId);\n      // console.log('New video quality level audio group id:', levelObject.attrs.AUDIO, level);\n      this.clearTimer();\n      this.hls.trigger(Events.LEVEL_LOADING, {\n        url,\n        level: currentLevelIndex,\n        id,\n        deliveryDirectives: hlsUrlParameters || null\n      });\n    }\n  }\n  get nextLoadLevel() {\n    if (this.manualLevelIndex !== -1) {\n      return this.manualLevelIndex;\n    } else {\n      return this.hls.nextAutoLevel;\n    }\n  }\n  set nextLoadLevel(nextLevel) {\n    this.level = nextLevel;\n    if (this.manualLevelIndex === -1) {\n      this.hls.nextAutoLevel = nextLevel;\n    }\n  }\n  removeLevel(levelIndex, urlId) {\n    const filterLevelAndGroupByIdIndex = (url, id) => id !== urlId;\n    const levels = this._levels.filter((level, index) => {\n      if (index !== levelIndex) {\n        return true;\n      }\n      if (level.url.length > 1 && urlId !== undefined) {\n        level.url = level.url.filter(filterLevelAndGroupByIdIndex);\n        if (level.audioGroupIds) {\n          level.audioGroupIds = level.audioGroupIds.filter(filterLevelAndGroupByIdIndex);\n        }\n        if (level.textGroupIds) {\n          level.textGroupIds = level.textGroupIds.filter(filterLevelAndGroupByIdIndex);\n        }\n        level.urlId = 0;\n        return true;\n      }\n      if (this.steering) {\n        this.steering.removeLevel(level);\n      }\n      return false;\n    });\n    this.hls.trigger(Events.LEVELS_UPDATED, {\n      levels\n    });\n  }\n  onLevelsUpdated(event, {\n    levels\n  }) {\n    levels.forEach((level, index) => {\n      const {\n        details\n      } = level;\n      if (details != null && details.fragments) {\n        details.fragments.forEach(fragment => {\n          fragment.level = index;\n        });\n      }\n    });\n    this._levels = levels;\n  }\n}\nfunction addGroupId(level, type, id) {\n  if (!id) {\n    return;\n  }\n  if (type === 'audio') {\n    if (!level.audioGroupIds) {\n      level.audioGroupIds = [];\n    }\n    level.audioGroupIds[level.url.length - 1] = id;\n  } else if (type === 'text') {\n    if (!level.textGroupIds) {\n      level.textGroupIds = [];\n    }\n    level.textGroupIds[level.url.length - 1] = id;\n  }\n}\nfunction assignTrackIdsByGroup(tracks) {\n  const groups = {};\n  tracks.forEach(track => {\n    const groupId = track.groupId || '';\n    track.id = groups[groupId] = groups[groupId] || 0;\n    groups[groupId]++;\n  });\n}\n\nvar FragmentState = {\n  NOT_LOADED: \"NOT_LOADED\",\n  APPENDING: \"APPENDING\",\n  PARTIAL: \"PARTIAL\",\n  OK: \"OK\"\n};\nclass FragmentTracker {\n  constructor(hls) {\n    this.activePartLists = Object.create(null);\n    this.endListFragments = Object.create(null);\n    this.fragments = Object.create(null);\n    this.timeRanges = Object.create(null);\n    this.bufferPadding = 0.2;\n    this.hls = void 0;\n    this.hasGaps = false;\n    this.hls = hls;\n    this._registerListeners();\n  }\n  _registerListeners() {\n    const {\n      hls\n    } = this;\n    hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this);\n    hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);\n    hls.on(Events.FRAG_LOADED, this.onFragLoaded, this);\n  }\n  _unregisterListeners() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this);\n    hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);\n    hls.off(Events.FRAG_LOADED, this.onFragLoaded, this);\n  }\n  destroy() {\n    this._unregisterListeners();\n    // @ts-ignore\n    this.fragments =\n    // @ts-ignore\n    this.activePartLists =\n    // @ts-ignore\n    this.endListFragments = this.timeRanges = null;\n  }\n\n  /**\n   * Return a Fragment or Part with an appended range that matches the position and levelType\n   * Otherwise, return null\n   */\n  getAppendedFrag(position, levelType) {\n    const activeParts = this.activePartLists[levelType];\n    if (activeParts) {\n      for (let i = activeParts.length; i--;) {\n        const activePart = activeParts[i];\n        if (!activePart) {\n          break;\n        }\n        const appendedPTS = activePart.end;\n        if (activePart.start <= position && appendedPTS !== null && position <= appendedPTS) {\n          return activePart;\n        }\n      }\n    }\n    return this.getBufferedFrag(position, levelType);\n  }\n\n  /**\n   * Return a buffered Fragment that matches the position and levelType.\n   * A buffered Fragment is one whose loading, parsing and appending is done (completed or \"partial\" meaning aborted).\n   * If not found any Fragment, return null\n   */\n  getBufferedFrag(position, levelType) {\n    const {\n      fragments\n    } = this;\n    const keys = Object.keys(fragments);\n    for (let i = keys.length; i--;) {\n      const fragmentEntity = fragments[keys[i]];\n      if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && fragmentEntity.buffered) {\n        const frag = fragmentEntity.body;\n        if (frag.start <= position && position <= frag.end) {\n          return frag;\n        }\n      }\n    }\n    return null;\n  }\n\n  /**\n   * Partial fragments effected by coded frame eviction will be removed\n   * The browser will unload parts of the buffer to free up memory for new buffer data\n   * Fragments will need to be reloaded when the buffer is freed up, removing partial fragments will allow them to reload(since there might be parts that are still playable)\n   */\n  detectEvictedFragments(elementaryStream, timeRange, playlistType, appendedPart) {\n    if (this.timeRanges) {\n      this.timeRanges[elementaryStream] = timeRange;\n    }\n    // Check if any flagged fragments have been unloaded\n    // excluding anything newer than appendedPartSn\n    const appendedPartSn = (appendedPart == null ? void 0 : appendedPart.fragment.sn) || -1;\n    Object.keys(this.fragments).forEach(key => {\n      const fragmentEntity = this.fragments[key];\n      if (!fragmentEntity) {\n        return;\n      }\n      if (appendedPartSn >= fragmentEntity.body.sn) {\n        return;\n      }\n      if (!fragmentEntity.buffered && !fragmentEntity.loaded) {\n        if (fragmentEntity.body.type === playlistType) {\n          this.removeFragment(fragmentEntity.body);\n        }\n        return;\n      }\n      const esData = fragmentEntity.range[elementaryStream];\n      if (!esData) {\n        return;\n      }\n      esData.time.some(time => {\n        const isNotBuffered = !this.isTimeBuffered(time.startPTS, time.endPTS, timeRange);\n        if (isNotBuffered) {\n          // Unregister partial fragment as it needs to load again to be reused\n          this.removeFragment(fragmentEntity.body);\n        }\n        return isNotBuffered;\n      });\n    });\n  }\n\n  /**\n   * Checks if the fragment passed in is loaded in the buffer properly\n   * Partially loaded fragments will be registered as a partial fragment\n   */\n  detectPartialFragments(data) {\n    const timeRanges = this.timeRanges;\n    const {\n      frag,\n      part\n    } = data;\n    if (!timeRanges || frag.sn === 'initSegment') {\n      return;\n    }\n    const fragKey = getFragmentKey(frag);\n    const fragmentEntity = this.fragments[fragKey];\n    if (!fragmentEntity || fragmentEntity.buffered && frag.gap) {\n      return;\n    }\n    const isFragHint = !frag.relurl;\n    Object.keys(timeRanges).forEach(elementaryStream => {\n      const streamInfo = frag.elementaryStreams[elementaryStream];\n      if (!streamInfo) {\n        return;\n      }\n      const timeRange = timeRanges[elementaryStream];\n      const partial = isFragHint || streamInfo.partial === true;\n      fragmentEntity.range[elementaryStream] = this.getBufferedTimes(frag, part, partial, timeRange);\n    });\n    fragmentEntity.loaded = null;\n    if (Object.keys(fragmentEntity.range).length) {\n      fragmentEntity.buffered = true;\n      const endList = fragmentEntity.body.endList = frag.endList || fragmentEntity.body.endList;\n      if (endList) {\n        this.endListFragments[fragmentEntity.body.type] = fragmentEntity;\n      }\n      if (!isPartial(fragmentEntity)) {\n        // Remove older fragment parts from lookup after frag is tracked as buffered\n        this.removeParts(frag.sn - 1, frag.type);\n      }\n    } else {\n      // remove fragment if nothing was appended\n      this.removeFragment(fragmentEntity.body);\n    }\n  }\n  removeParts(snToKeep, levelType) {\n    const activeParts = this.activePartLists[levelType];\n    if (!activeParts) {\n      return;\n    }\n    this.activePartLists[levelType] = activeParts.filter(part => part.fragment.sn >= snToKeep);\n  }\n  fragBuffered(frag, force) {\n    const fragKey = getFragmentKey(frag);\n    let fragmentEntity = this.fragments[fragKey];\n    if (!fragmentEntity && force) {\n      fragmentEntity = this.fragments[fragKey] = {\n        body: frag,\n        appendedPTS: null,\n        loaded: null,\n        buffered: false,\n        range: Object.create(null)\n      };\n      if (frag.gap) {\n        this.hasGaps = true;\n      }\n    }\n    if (fragmentEntity) {\n      fragmentEntity.loaded = null;\n      fragmentEntity.buffered = true;\n    }\n  }\n  getBufferedTimes(fragment, part, partial, timeRange) {\n    const buffered = {\n      time: [],\n      partial\n    };\n    const startPTS = fragment.start;\n    const endPTS = fragment.end;\n    const minEndPTS = fragment.minEndPTS || endPTS;\n    const maxStartPTS = fragment.maxStartPTS || startPTS;\n    for (let i = 0; i < timeRange.length; i++) {\n      const startTime = timeRange.start(i) - this.bufferPadding;\n      const endTime = timeRange.end(i) + this.bufferPadding;\n      if (maxStartPTS >= startTime && minEndPTS <= endTime) {\n        // Fragment is entirely contained in buffer\n        // No need to check the other timeRange times since it's completely playable\n        buffered.time.push({\n          startPTS: Math.max(startPTS, timeRange.start(i)),\n          endPTS: Math.min(endPTS, timeRange.end(i))\n        });\n        break;\n      } else if (startPTS < endTime && endPTS > startTime) {\n        buffered.partial = true;\n        // Check for intersection with buffer\n        // Get playable sections of the fragment\n        buffered.time.push({\n          startPTS: Math.max(startPTS, timeRange.start(i)),\n          endPTS: Math.min(endPTS, timeRange.end(i))\n        });\n      } else if (endPTS <= startTime) {\n        // No need to check the rest of the timeRange as it is in order\n        break;\n      }\n    }\n    return buffered;\n  }\n\n  /**\n   * Gets the partial fragment for a certain time\n   */\n  getPartialFragment(time) {\n    let bestFragment = null;\n    let timePadding;\n    let startTime;\n    let endTime;\n    let bestOverlap = 0;\n    const {\n      bufferPadding,\n      fragments\n    } = this;\n    Object.keys(fragments).forEach(key => {\n      const fragmentEntity = fragments[key];\n      if (!fragmentEntity) {\n        return;\n      }\n      if (isPartial(fragmentEntity)) {\n        startTime = fragmentEntity.body.start - bufferPadding;\n        endTime = fragmentEntity.body.end + bufferPadding;\n        if (time >= startTime && time <= endTime) {\n          // Use the fragment that has the most padding from start and end time\n          timePadding = Math.min(time - startTime, endTime - time);\n          if (bestOverlap <= timePadding) {\n            bestFragment = fragmentEntity.body;\n            bestOverlap = timePadding;\n          }\n        }\n      }\n    });\n    return bestFragment;\n  }\n  isEndListAppended(type) {\n    const lastFragmentEntity = this.endListFragments[type];\n    return lastFragmentEntity !== undefined && (lastFragmentEntity.buffered || isPartial(lastFragmentEntity));\n  }\n  getState(fragment) {\n    const fragKey = getFragmentKey(fragment);\n    const fragmentEntity = this.fragments[fragKey];\n    if (fragmentEntity) {\n      if (!fragmentEntity.buffered) {\n        return FragmentState.APPENDING;\n      } else if (isPartial(fragmentEntity)) {\n        return FragmentState.PARTIAL;\n      } else {\n        return FragmentState.OK;\n      }\n    }\n    return FragmentState.NOT_LOADED;\n  }\n  isTimeBuffered(startPTS, endPTS, timeRange) {\n    let startTime;\n    let endTime;\n    for (let i = 0; i < timeRange.length; i++) {\n      startTime = timeRange.start(i) - this.bufferPadding;\n      endTime = timeRange.end(i) + this.bufferPadding;\n      if (startPTS >= startTime && endPTS <= endTime) {\n        return true;\n      }\n      if (endPTS <= startTime) {\n        // No need to check the rest of the timeRange as it is in order\n        return false;\n      }\n    }\n    return false;\n  }\n  onFragLoaded(event, data) {\n    const {\n      frag,\n      part\n    } = data;\n    // don't track initsegment (for which sn is not a number)\n    // don't track frags used for bitrateTest, they're irrelevant.\n    if (frag.sn === 'initSegment' || frag.bitrateTest) {\n      return;\n    }\n\n    // Fragment entity `loaded` FragLoadedData is null when loading parts\n    const loaded = part ? null : data;\n    const fragKey = getFragmentKey(frag);\n    this.fragments[fragKey] = {\n      body: frag,\n      appendedPTS: null,\n      loaded,\n      buffered: false,\n      range: Object.create(null)\n    };\n  }\n  onBufferAppended(event, data) {\n    const {\n      frag,\n      part,\n      timeRanges\n    } = data;\n    if (frag.sn === 'initSegment') {\n      return;\n    }\n    const playlistType = frag.type;\n    if (part) {\n      let activeParts = this.activePartLists[playlistType];\n      if (!activeParts) {\n        this.activePartLists[playlistType] = activeParts = [];\n      }\n      activeParts.push(part);\n    }\n    // Store the latest timeRanges loaded in the buffer\n    this.timeRanges = timeRanges;\n    Object.keys(timeRanges).forEach(elementaryStream => {\n      const timeRange = timeRanges[elementaryStream];\n      this.detectEvictedFragments(elementaryStream, timeRange, playlistType, part);\n    });\n  }\n  onFragBuffered(event, data) {\n    this.detectPartialFragments(data);\n  }\n  hasFragment(fragment) {\n    const fragKey = getFragmentKey(fragment);\n    return !!this.fragments[fragKey];\n  }\n  hasParts(type) {\n    var _this$activePartLists;\n    return !!((_this$activePartLists = this.activePartLists[type]) != null && _this$activePartLists.length);\n  }\n  removeFragmentsInRange(start, end, playlistType, withGapOnly, unbufferedOnly) {\n    if (withGapOnly && !this.hasGaps) {\n      return;\n    }\n    Object.keys(this.fragments).forEach(key => {\n      const fragmentEntity = this.fragments[key];\n      if (!fragmentEntity) {\n        return;\n      }\n      const frag = fragmentEntity.body;\n      if (frag.type !== playlistType || withGapOnly && !frag.gap) {\n        return;\n      }\n      if (frag.start < end && frag.end > start && (fragmentEntity.buffered || unbufferedOnly)) {\n        this.removeFragment(frag);\n      }\n    });\n  }\n  removeFragment(fragment) {\n    const fragKey = getFragmentKey(fragment);\n    fragment.stats.loaded = 0;\n    fragment.clearElementaryStreamInfo();\n    const activeParts = this.activePartLists[fragment.type];\n    if (activeParts) {\n      const snToRemove = fragment.sn;\n      this.activePartLists[fragment.type] = activeParts.filter(part => part.fragment.sn !== snToRemove);\n    }\n    delete this.fragments[fragKey];\n    if (fragment.endList) {\n      delete this.endListFragments[fragment.type];\n    }\n  }\n  removeAllFragments() {\n    this.fragments = Object.create(null);\n    this.endListFragments = Object.create(null);\n    this.activePartLists = Object.create(null);\n    this.hasGaps = false;\n  }\n}\nfunction isPartial(fragmentEntity) {\n  var _fragmentEntity$range, _fragmentEntity$range2, _fragmentEntity$range3;\n  return fragmentEntity.buffered && (fragmentEntity.body.gap || ((_fragmentEntity$range = fragmentEntity.range.video) == null ? void 0 : _fragmentEntity$range.partial) || ((_fragmentEntity$range2 = fragmentEntity.range.audio) == null ? void 0 : _fragmentEntity$range2.partial) || ((_fragmentEntity$range3 = fragmentEntity.range.audiovideo) == null ? void 0 : _fragmentEntity$range3.partial));\n}\nfunction getFragmentKey(fragment) {\n  return `${fragment.type}_${fragment.level}_${fragment.urlId}_${fragment.sn}`;\n}\n\nconst MIN_CHUNK_SIZE = Math.pow(2, 17); // 128kb\n\nclass FragmentLoader {\n  constructor(config) {\n    this.config = void 0;\n    this.loader = null;\n    this.partLoadTimeout = -1;\n    this.config = config;\n  }\n  destroy() {\n    if (this.loader) {\n      this.loader.destroy();\n      this.loader = null;\n    }\n  }\n  abort() {\n    if (this.loader) {\n      // Abort the loader for current fragment. Only one may load at any given time\n      this.loader.abort();\n    }\n  }\n  load(frag, onProgress) {\n    const url = frag.url;\n    if (!url) {\n      return Promise.reject(new LoadError({\n        type: ErrorTypes.NETWORK_ERROR,\n        details: ErrorDetails.FRAG_LOAD_ERROR,\n        fatal: false,\n        frag,\n        error: new Error(`Fragment does not have a ${url ? 'part list' : 'url'}`),\n        networkDetails: null\n      }));\n    }\n    this.abort();\n    const config = this.config;\n    const FragmentILoader = config.fLoader;\n    const DefaultILoader = config.loader;\n    return new Promise((resolve, reject) => {\n      if (this.loader) {\n        this.loader.destroy();\n      }\n      if (frag.gap) {\n        if (frag.tagList.some(tags => tags[0] === 'GAP')) {\n          reject(createGapLoadError(frag));\n          return;\n        } else {\n          // Reset temporary treatment as GAP tag\n          frag.gap = false;\n        }\n      }\n      const loader = this.loader = frag.loader = FragmentILoader ? new FragmentILoader(config) : new DefaultILoader(config);\n      const loaderContext = createLoaderContext(frag);\n      const loadPolicy = getLoaderConfigWithoutReties(config.fragLoadPolicy.default);\n      const loaderConfig = {\n        loadPolicy,\n        timeout: loadPolicy.maxLoadTimeMs,\n        maxRetry: 0,\n        retryDelay: 0,\n        maxRetryDelay: 0,\n        highWaterMark: frag.sn === 'initSegment' ? Infinity : MIN_CHUNK_SIZE\n      };\n      // Assign frag stats to the loader's stats reference\n      frag.stats = loader.stats;\n      loader.load(loaderContext, loaderConfig, {\n        onSuccess: (response, stats, context, networkDetails) => {\n          this.resetLoader(frag, loader);\n          let payload = response.data;\n          if (context.resetIV && frag.decryptdata) {\n            frag.decryptdata.iv = new Uint8Array(payload.slice(0, 16));\n            payload = payload.slice(16);\n          }\n          resolve({\n            frag,\n            part: null,\n            payload,\n            networkDetails\n          });\n        },\n        onError: (response, context, networkDetails, stats) => {\n          this.resetLoader(frag, loader);\n          reject(new LoadError({\n            type: ErrorTypes.NETWORK_ERROR,\n            details: ErrorDetails.FRAG_LOAD_ERROR,\n            fatal: false,\n            frag,\n            response: _objectSpread2({\n              url,\n              data: undefined\n            }, response),\n            error: new Error(`HTTP Error ${response.code} ${response.text}`),\n            networkDetails,\n            stats\n          }));\n        },\n        onAbort: (stats, context, networkDetails) => {\n          this.resetLoader(frag, loader);\n          reject(new LoadError({\n            type: ErrorTypes.NETWORK_ERROR,\n            details: ErrorDetails.INTERNAL_ABORTED,\n            fatal: false,\n            frag,\n            error: new Error('Aborted'),\n            networkDetails,\n            stats\n          }));\n        },\n        onTimeout: (stats, context, networkDetails) => {\n          this.resetLoader(frag, loader);\n          reject(new LoadError({\n            type: ErrorTypes.NETWORK_ERROR,\n            details: ErrorDetails.FRAG_LOAD_TIMEOUT,\n            fatal: false,\n            frag,\n            error: new Error(`Timeout after ${loaderConfig.timeout}ms`),\n            networkDetails,\n            stats\n          }));\n        },\n        onProgress: (stats, context, data, networkDetails) => {\n          if (onProgress) {\n            onProgress({\n              frag,\n              part: null,\n              payload: data,\n              networkDetails\n            });\n          }\n        }\n      });\n    });\n  }\n  loadPart(frag, part, onProgress) {\n    this.abort();\n    const config = this.config;\n    const FragmentILoader = config.fLoader;\n    const DefaultILoader = config.loader;\n    return new Promise((resolve, reject) => {\n      if (this.loader) {\n        this.loader.destroy();\n      }\n      if (frag.gap || part.gap) {\n        reject(createGapLoadError(frag, part));\n        return;\n      }\n      const loader = this.loader = frag.loader = FragmentILoader ? new FragmentILoader(config) : new DefaultILoader(config);\n      const loaderContext = createLoaderContext(frag, part);\n      // Should we define another load policy for parts?\n      const loadPolicy = getLoaderConfigWithoutReties(config.fragLoadPolicy.default);\n      const loaderConfig = {\n        loadPolicy,\n        timeout: loadPolicy.maxLoadTimeMs,\n        maxRetry: 0,\n        retryDelay: 0,\n        maxRetryDelay: 0,\n        highWaterMark: MIN_CHUNK_SIZE\n      };\n      // Assign part stats to the loader's stats reference\n      part.stats = loader.stats;\n      loader.load(loaderContext, loaderConfig, {\n        onSuccess: (response, stats, context, networkDetails) => {\n          this.resetLoader(frag, loader);\n          this.updateStatsFromPart(frag, part);\n          const partLoadedData = {\n            frag,\n            part,\n            payload: response.data,\n            networkDetails\n          };\n          onProgress(partLoadedData);\n          resolve(partLoadedData);\n        },\n        onError: (response, context, networkDetails, stats) => {\n          this.resetLoader(frag, loader);\n          reject(new LoadError({\n            type: ErrorTypes.NETWORK_ERROR,\n            details: ErrorDetails.FRAG_LOAD_ERROR,\n            fatal: false,\n            frag,\n            part,\n            response: _objectSpread2({\n              url: loaderContext.url,\n              data: undefined\n            }, response),\n            error: new Error(`HTTP Error ${response.code} ${response.text}`),\n            networkDetails,\n            stats\n          }));\n        },\n        onAbort: (stats, context, networkDetails) => {\n          frag.stats.aborted = part.stats.aborted;\n          this.resetLoader(frag, loader);\n          reject(new LoadError({\n            type: ErrorTypes.NETWORK_ERROR,\n            details: ErrorDetails.INTERNAL_ABORTED,\n            fatal: false,\n            frag,\n            part,\n            error: new Error('Aborted'),\n            networkDetails,\n            stats\n          }));\n        },\n        onTimeout: (stats, context, networkDetails) => {\n          this.resetLoader(frag, loader);\n          reject(new LoadError({\n            type: ErrorTypes.NETWORK_ERROR,\n            details: ErrorDetails.FRAG_LOAD_TIMEOUT,\n            fatal: false,\n            frag,\n            part,\n            error: new Error(`Timeout after ${loaderConfig.timeout}ms`),\n            networkDetails,\n            stats\n          }));\n        }\n      });\n    });\n  }\n  updateStatsFromPart(frag, part) {\n    const fragStats = frag.stats;\n    const partStats = part.stats;\n    const partTotal = partStats.total;\n    fragStats.loaded += partStats.loaded;\n    if (partTotal) {\n      const estTotalParts = Math.round(frag.duration / part.duration);\n      const estLoadedParts = Math.min(Math.round(fragStats.loaded / partTotal), estTotalParts);\n      const estRemainingParts = estTotalParts - estLoadedParts;\n      const estRemainingBytes = estRemainingParts * Math.round(fragStats.loaded / estLoadedParts);\n      fragStats.total = fragStats.loaded + estRemainingBytes;\n    } else {\n      fragStats.total = Math.max(fragStats.loaded, fragStats.total);\n    }\n    const fragLoading = fragStats.loading;\n    const partLoading = partStats.loading;\n    if (fragLoading.start) {\n      // add to fragment loader latency\n      fragLoading.first += partLoading.first - partLoading.start;\n    } else {\n      fragLoading.start = partLoading.start;\n      fragLoading.first = partLoading.first;\n    }\n    fragLoading.end = partLoading.end;\n  }\n  resetLoader(frag, loader) {\n    frag.loader = null;\n    if (this.loader === loader) {\n      self.clearTimeout(this.partLoadTimeout);\n      this.loader = null;\n    }\n    loader.destroy();\n  }\n}\nfunction createLoaderContext(frag, part = null) {\n  const segment = part || frag;\n  const loaderContext = {\n    frag,\n    part,\n    responseType: 'arraybuffer',\n    url: segment.url,\n    headers: {},\n    rangeStart: 0,\n    rangeEnd: 0\n  };\n  const start = segment.byteRangeStartOffset;\n  const end = segment.byteRangeEndOffset;\n  if (isFiniteNumber(start) && isFiniteNumber(end)) {\n    var _frag$decryptdata;\n    let byteRangeStart = start;\n    let byteRangeEnd = end;\n    if (frag.sn === 'initSegment' && ((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method) === 'AES-128') {\n      // MAP segment encrypted with method 'AES-128', when served with HTTP Range,\n      // has the unencrypted size specified in the range.\n      // Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6\n      const fragmentLen = end - start;\n      if (fragmentLen % 16) {\n        byteRangeEnd = end + (16 - fragmentLen % 16);\n      }\n      if (start !== 0) {\n        loaderContext.resetIV = true;\n        byteRangeStart = start - 16;\n      }\n    }\n    loaderContext.rangeStart = byteRangeStart;\n    loaderContext.rangeEnd = byteRangeEnd;\n  }\n  return loaderContext;\n}\nfunction createGapLoadError(frag, part) {\n  const error = new Error(`GAP ${frag.gap ? 'tag' : 'attribute'} found`);\n  const errorData = {\n    type: ErrorTypes.MEDIA_ERROR,\n    details: ErrorDetails.FRAG_GAP,\n    fatal: false,\n    frag,\n    error,\n    networkDetails: null\n  };\n  if (part) {\n    errorData.part = part;\n  }\n  (part ? part : frag).stats.aborted = true;\n  return new LoadError(errorData);\n}\nclass LoadError extends Error {\n  constructor(data) {\n    super(data.error.message);\n    this.data = void 0;\n    this.data = data;\n  }\n}\n\nclass KeyLoader {\n  constructor(config) {\n    this.config = void 0;\n    this.keyUriToKeyInfo = {};\n    this.emeController = null;\n    this.config = config;\n  }\n  abort(type) {\n    for (const uri in this.keyUriToKeyInfo) {\n      const loader = this.keyUriToKeyInfo[uri].loader;\n      if (loader) {\n        if (type && type !== loader.context.frag.type) {\n          return;\n        }\n        loader.abort();\n      }\n    }\n  }\n  detach() {\n    for (const uri in this.keyUriToKeyInfo) {\n      const keyInfo = this.keyUriToKeyInfo[uri];\n      // Remove cached EME keys on detach\n      if (keyInfo.mediaKeySessionContext || keyInfo.decryptdata.isCommonEncryption) {\n        delete this.keyUriToKeyInfo[uri];\n      }\n    }\n  }\n  destroy() {\n    this.detach();\n    for (const uri in this.keyUriToKeyInfo) {\n      const loader = this.keyUriToKeyInfo[uri].loader;\n      if (loader) {\n        loader.destroy();\n      }\n    }\n    this.keyUriToKeyInfo = {};\n  }\n  createKeyLoadError(frag, details = ErrorDetails.KEY_LOAD_ERROR, error, networkDetails, response) {\n    return new LoadError({\n      type: ErrorTypes.NETWORK_ERROR,\n      details,\n      fatal: false,\n      frag,\n      response,\n      error,\n      networkDetails\n    });\n  }\n  loadClear(loadingFrag, encryptedFragments) {\n    if (this.emeController && this.config.emeEnabled) {\n      // access key-system with nearest key on start (loaidng frag is unencrypted)\n      const {\n        sn,\n        cc\n      } = loadingFrag;\n      for (let i = 0; i < encryptedFragments.length; i++) {\n        const frag = encryptedFragments[i];\n        if (cc <= frag.cc && (sn === 'initSegment' || frag.sn === 'initSegment' || sn < frag.sn)) {\n          this.emeController.selectKeySystemFormat(frag).then(keySystemFormat => {\n            frag.setKeyFormat(keySystemFormat);\n          });\n          break;\n        }\n      }\n    }\n  }\n  load(frag) {\n    if (!frag.decryptdata && frag.encrypted && this.emeController) {\n      // Multiple keys, but none selected, resolve in eme-controller\n      return this.emeController.selectKeySystemFormat(frag).then(keySystemFormat => {\n        return this.loadInternal(frag, keySystemFormat);\n      });\n    }\n    return this.loadInternal(frag);\n  }\n  loadInternal(frag, keySystemFormat) {\n    var _keyInfo, _keyInfo2;\n    if (keySystemFormat) {\n      frag.setKeyFormat(keySystemFormat);\n    }\n    const decryptdata = frag.decryptdata;\n    if (!decryptdata) {\n      const error = new Error(keySystemFormat ? `Expected frag.decryptdata to be defined after setting format ${keySystemFormat}` : 'Missing decryption data on fragment in onKeyLoading');\n      return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, error));\n    }\n    const uri = decryptdata.uri;\n    if (!uri) {\n      return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error(`Invalid key URI: \"${uri}\"`)));\n    }\n    let keyInfo = this.keyUriToKeyInfo[uri];\n    if ((_keyInfo = keyInfo) != null && _keyInfo.decryptdata.key) {\n      decryptdata.key = keyInfo.decryptdata.key;\n      return Promise.resolve({\n        frag,\n        keyInfo\n      });\n    }\n    // Return key load promise as long as it does not have a mediakey session with an unusable key status\n    if ((_keyInfo2 = keyInfo) != null && _keyInfo2.keyLoadPromise) {\n      var _keyInfo$mediaKeySess;\n      switch ((_keyInfo$mediaKeySess = keyInfo.mediaKeySessionContext) == null ? void 0 : _keyInfo$mediaKeySess.keyStatus) {\n        case undefined:\n        case 'status-pending':\n        case 'usable':\n        case 'usable-in-future':\n          return keyInfo.keyLoadPromise.then(keyLoadedData => {\n            // Return the correct fragment with updated decryptdata key and loaded keyInfo\n            decryptdata.key = keyLoadedData.keyInfo.decryptdata.key;\n            return {\n              frag,\n              keyInfo\n            };\n          });\n      }\n      // If we have a key session and status and it is not pending or usable, continue\n      // This will go back to the eme-controller for expired keys to get a new keyLoadPromise\n    }\n\n    // Load the key or return the loading promise\n    keyInfo = this.keyUriToKeyInfo[uri] = {\n      decryptdata,\n      keyLoadPromise: null,\n      loader: null,\n      mediaKeySessionContext: null\n    };\n    switch (decryptdata.method) {\n      case 'ISO-23001-7':\n      case 'SAMPLE-AES':\n      case 'SAMPLE-AES-CENC':\n      case 'SAMPLE-AES-CTR':\n        if (decryptdata.keyFormat === 'identity') {\n          // loadKeyHTTP handles http(s) and data URLs\n          return this.loadKeyHTTP(keyInfo, frag);\n        }\n        return this.loadKeyEME(keyInfo, frag);\n      case 'AES-128':\n        return this.loadKeyHTTP(keyInfo, frag);\n      default:\n        return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error(`Key supplied with unsupported METHOD: \"${decryptdata.method}\"`)));\n    }\n  }\n  loadKeyEME(keyInfo, frag) {\n    const keyLoadedData = {\n      frag,\n      keyInfo\n    };\n    if (this.emeController && this.config.emeEnabled) {\n      const keySessionContextPromise = this.emeController.loadKey(keyLoadedData);\n      if (keySessionContextPromise) {\n        return (keyInfo.keyLoadPromise = keySessionContextPromise.then(keySessionContext => {\n          keyInfo.mediaKeySessionContext = keySessionContext;\n          return keyLoadedData;\n        })).catch(error => {\n          // Remove promise for license renewal or retry\n          keyInfo.keyLoadPromise = null;\n          throw error;\n        });\n      }\n    }\n    return Promise.resolve(keyLoadedData);\n  }\n  loadKeyHTTP(keyInfo, frag) {\n    const config = this.config;\n    const Loader = config.loader;\n    const keyLoader = new Loader(config);\n    frag.keyLoader = keyInfo.loader = keyLoader;\n    return keyInfo.keyLoadPromise = new Promise((resolve, reject) => {\n      const loaderContext = {\n        keyInfo,\n        frag,\n        responseType: 'arraybuffer',\n        url: keyInfo.decryptdata.uri\n      };\n\n      // maxRetry is 0 so that instead of retrying the same key on the same variant multiple times,\n      // key-loader will trigger an error and rely on stream-controller to handle retry logic.\n      // this will also align retry logic with fragment-loader\n      const loadPolicy = config.keyLoadPolicy.default;\n      const loaderConfig = {\n        loadPolicy,\n        timeout: loadPolicy.maxLoadTimeMs,\n        maxRetry: 0,\n        retryDelay: 0,\n        maxRetryDelay: 0\n      };\n      const loaderCallbacks = {\n        onSuccess: (response, stats, context, networkDetails) => {\n          const {\n            frag,\n            keyInfo,\n            url: uri\n          } = context;\n          if (!frag.decryptdata || keyInfo !== this.keyUriToKeyInfo[uri]) {\n            return reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error('after key load, decryptdata unset or changed'), networkDetails));\n          }\n          keyInfo.decryptdata.key = frag.decryptdata.key = new Uint8Array(response.data);\n\n          // detach fragment key loader on load success\n          frag.keyLoader = null;\n          keyInfo.loader = null;\n          resolve({\n            frag,\n            keyInfo\n          });\n        },\n        onError: (response, context, networkDetails, stats) => {\n          this.resetLoader(context);\n          reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error(`HTTP Error ${response.code} loading key ${response.text}`), networkDetails, _objectSpread2({\n            url: loaderContext.url,\n            data: undefined\n          }, response)));\n        },\n        onTimeout: (stats, context, networkDetails) => {\n          this.resetLoader(context);\n          reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_TIMEOUT, new Error('key loading timed out'), networkDetails));\n        },\n        onAbort: (stats, context, networkDetails) => {\n          this.resetLoader(context);\n          reject(this.createKeyLoadError(frag, ErrorDetails.INTERNAL_ABORTED, new Error('key loading aborted'), networkDetails));\n        }\n      };\n      keyLoader.load(loaderContext, loaderConfig, loaderCallbacks);\n    });\n  }\n  resetLoader(context) {\n    const {\n      frag,\n      keyInfo,\n      url: uri\n    } = context;\n    const loader = keyInfo.loader;\n    if (frag.keyLoader === loader) {\n      frag.keyLoader = null;\n      keyInfo.loader = null;\n    }\n    delete this.keyUriToKeyInfo[uri];\n    if (loader) {\n      loader.destroy();\n    }\n  }\n}\n\n/**\n * @ignore\n * Sub-class specialization of EventHandler base class.\n *\n * TaskLoop allows to schedule a task function being called (optionnaly repeatedly) on the main loop,\n * scheduled asynchroneously, avoiding recursive calls in the same tick.\n *\n * The task itself is implemented in `doTick`. It can be requested and called for single execution\n * using the `tick` method.\n *\n * It will be assured that the task execution method (`tick`) only gets called once per main loop \"tick\",\n * no matter how often it gets requested for execution. Execution in further ticks will be scheduled accordingly.\n *\n * If further execution requests have already been scheduled on the next tick, it can be checked with `hasNextTick`,\n * and cancelled with `clearNextTick`.\n *\n * The task can be scheduled as an interval repeatedly with a period as parameter (see `setInterval`, `clearInterval`).\n *\n * Sub-classes need to implement the `doTick` method which will effectively have the task execution routine.\n *\n * Further explanations:\n *\n * The baseclass has a `tick` method that will schedule the doTick call. It may be called synchroneously\n * only for a stack-depth of one. On re-entrant calls, sub-sequent calls are scheduled for next main loop ticks.\n *\n * When the task execution (`tick` method) is called in re-entrant way this is detected and\n * we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further\n * task processing on the next main loop iteration (also known as \"next tick\" in the Node/JS runtime lingo).\n */\nclass TaskLoop {\n  constructor() {\n    this._boundTick = void 0;\n    this._tickTimer = null;\n    this._tickInterval = null;\n    this._tickCallCount = 0;\n    this._boundTick = this.tick.bind(this);\n  }\n  destroy() {\n    this.onHandlerDestroying();\n    this.onHandlerDestroyed();\n  }\n  onHandlerDestroying() {\n    // clear all timers before unregistering from event bus\n    this.clearNextTick();\n    this.clearInterval();\n  }\n  onHandlerDestroyed() {}\n  hasInterval() {\n    return !!this._tickInterval;\n  }\n  hasNextTick() {\n    return !!this._tickTimer;\n  }\n\n  /**\n   * @param millis - Interval time (ms)\n   * @eturns True when interval has been scheduled, false when already scheduled (no effect)\n   */\n  setInterval(millis) {\n    if (!this._tickInterval) {\n      this._tickCallCount = 0;\n      this._tickInterval = self.setInterval(this._boundTick, millis);\n      return true;\n    }\n    return false;\n  }\n\n  /**\n   * @returns True when interval was cleared, false when none was set (no effect)\n   */\n  clearInterval() {\n    if (this._tickInterval) {\n      self.clearInterval(this._tickInterval);\n      this._tickInterval = null;\n      return true;\n    }\n    return false;\n  }\n\n  /**\n   * @returns True when timeout was cleared, false when none was set (no effect)\n   */\n  clearNextTick() {\n    if (this._tickTimer) {\n      self.clearTimeout(this._tickTimer);\n      this._tickTimer = null;\n      return true;\n    }\n    return false;\n  }\n\n  /**\n   * Will call the subclass doTick implementation in this main loop tick\n   * or in the next one (via setTimeout(,0)) in case it has already been called\n   * in this tick (in case this is a re-entrant call).\n   */\n  tick() {\n    this._tickCallCount++;\n    if (this._tickCallCount === 1) {\n      this.doTick();\n      // re-entrant call to tick from previous doTick call stack\n      // -> schedule a call on the next main loop iteration to process this task processing request\n      if (this._tickCallCount > 1) {\n        // make sure only one timer exists at any time at max\n        this.tickImmediate();\n      }\n      this._tickCallCount = 0;\n    }\n  }\n  tickImmediate() {\n    this.clearNextTick();\n    this._tickTimer = self.setTimeout(this._boundTick, 0);\n  }\n\n  /**\n   * For subclass to implement task logic\n   * @abstract\n   */\n  doTick() {}\n}\n\n/**\n * Provides methods dealing with buffer length retrieval for example.\n *\n * In general, a helper around HTML5 MediaElement TimeRanges gathered from `buffered` property.\n *\n * Also @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered\n */\n\nconst noopBuffered = {\n  length: 0,\n  start: () => 0,\n  end: () => 0\n};\nclass BufferHelper {\n  /**\n   * Return true if `media`'s buffered include `position`\n   */\n  static isBuffered(media, position) {\n    try {\n      if (media) {\n        const buffered = BufferHelper.getBuffered(media);\n        for (let i = 0; i < buffered.length; i++) {\n          if (position >= buffered.start(i) && position <= buffered.end(i)) {\n            return true;\n          }\n        }\n      }\n    } catch (error) {\n      // this is to catch\n      // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer':\n      // This SourceBuffer has been removed from the parent media source\n    }\n    return false;\n  }\n  static bufferInfo(media, pos, maxHoleDuration) {\n    try {\n      if (media) {\n        const vbuffered = BufferHelper.getBuffered(media);\n        const buffered = [];\n        let i;\n        for (i = 0; i < vbuffered.length; i++) {\n          buffered.push({\n            start: vbuffered.start(i),\n            end: vbuffered.end(i)\n          });\n        }\n        return this.bufferedInfo(buffered, pos, maxHoleDuration);\n      }\n    } catch (error) {\n      // this is to catch\n      // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer':\n      // This SourceBuffer has been removed from the parent media source\n    }\n    return {\n      len: 0,\n      start: pos,\n      end: pos,\n      nextStart: undefined\n    };\n  }\n  static bufferedInfo(buffered, pos, maxHoleDuration) {\n    pos = Math.max(0, pos);\n    // sort on buffer.start/smaller end (IE does not always return sorted buffered range)\n    buffered.sort(function (a, b) {\n      const diff = a.start - b.start;\n      if (diff) {\n        return diff;\n      } else {\n        return b.end - a.end;\n      }\n    });\n    let buffered2 = [];\n    if (maxHoleDuration) {\n      // there might be some small holes between buffer time range\n      // consider that holes smaller than maxHoleDuration are irrelevant and build another\n      // buffer time range representations that discards those holes\n      for (let i = 0; i < buffered.length; i++) {\n        const buf2len = buffered2.length;\n        if (buf2len) {\n          const buf2end = buffered2[buf2len - 1].end;\n          // if small hole (value between 0 or maxHoleDuration ) or overlapping (negative)\n          if (buffered[i].start - buf2end < maxHoleDuration) {\n            // merge overlapping time ranges\n            // update lastRange.end only if smaller than item.end\n            // e.g.  [ 1, 15] with  [ 2,8] => [ 1,15] (no need to modify lastRange.end)\n            // whereas [ 1, 8] with  [ 2,15] => [ 1,15] ( lastRange should switch from [1,8] to [1,15])\n            if (buffered[i].end > buf2end) {\n              buffered2[buf2len - 1].end = buffered[i].end;\n            }\n          } else {\n            // big hole\n            buffered2.push(buffered[i]);\n          }\n        } else {\n          // first value\n          buffered2.push(buffered[i]);\n        }\n      }\n    } else {\n      buffered2 = buffered;\n    }\n    let bufferLen = 0;\n\n    // bufferStartNext can possibly be undefined based on the conditional logic below\n    let bufferStartNext;\n\n    // bufferStart and bufferEnd are buffer boundaries around current video position\n    let bufferStart = pos;\n    let bufferEnd = pos;\n    for (let i = 0; i < buffered2.length; i++) {\n      const start = buffered2[i].start;\n      const end = buffered2[i].end;\n      // logger.log('buf start/end:' + buffered.start(i) + '/' + buffered.end(i));\n      if (pos + maxHoleDuration >= start && pos < end) {\n        // play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length\n        bufferStart = start;\n        bufferEnd = end;\n        bufferLen = bufferEnd - pos;\n      } else if (pos + maxHoleDuration < start) {\n        bufferStartNext = start;\n        break;\n      }\n    }\n    return {\n      len: bufferLen,\n      start: bufferStart || 0,\n      end: bufferEnd || 0,\n      nextStart: bufferStartNext\n    };\n  }\n\n  /**\n   * Safe method to get buffered property.\n   * SourceBuffer.buffered may throw if SourceBuffer is removed from it's MediaSource\n   */\n  static getBuffered(media) {\n    try {\n      return media.buffered;\n    } catch (e) {\n      logger.log('failed to get media.buffered', e);\n      return noopBuffered;\n    }\n  }\n}\n\nclass ChunkMetadata {\n  constructor(level, sn, id, size = 0, part = -1, partial = false) {\n    this.level = void 0;\n    this.sn = void 0;\n    this.part = void 0;\n    this.id = void 0;\n    this.size = void 0;\n    this.partial = void 0;\n    this.transmuxing = getNewPerformanceTiming();\n    this.buffering = {\n      audio: getNewPerformanceTiming(),\n      video: getNewPerformanceTiming(),\n      audiovideo: getNewPerformanceTiming()\n    };\n    this.level = level;\n    this.sn = sn;\n    this.id = id;\n    this.size = size;\n    this.part = part;\n    this.partial = partial;\n  }\n}\nfunction getNewPerformanceTiming() {\n  return {\n    start: 0,\n    executeStart: 0,\n    executeEnd: 0,\n    end: 0\n  };\n}\n\nfunction findFirstFragWithCC(fragments, cc) {\n  let firstFrag = null;\n  for (let i = 0, len = fragments.length; i < len; i++) {\n    const currentFrag = fragments[i];\n    if (currentFrag && currentFrag.cc === cc) {\n      firstFrag = currentFrag;\n      break;\n    }\n  }\n  return firstFrag;\n}\nfunction shouldAlignOnDiscontinuities(lastFrag, lastLevel, details) {\n  if (lastLevel.details) {\n    if (details.endCC > details.startCC || lastFrag && lastFrag.cc < details.startCC) {\n      return true;\n    }\n  }\n  return false;\n}\n\n// Find the first frag in the previous level which matches the CC of the first frag of the new level\nfunction findDiscontinuousReferenceFrag(prevDetails, curDetails, referenceIndex = 0) {\n  const prevFrags = prevDetails.fragments;\n  const curFrags = curDetails.fragments;\n  if (!curFrags.length || !prevFrags.length) {\n    logger.log('No fragments to align');\n    return;\n  }\n  const prevStartFrag = findFirstFragWithCC(prevFrags, curFrags[0].cc);\n  if (!prevStartFrag || prevStartFrag && !prevStartFrag.startPTS) {\n    logger.log('No frag in previous level to align on');\n    return;\n  }\n  return prevStartFrag;\n}\nfunction adjustFragmentStart(frag, sliding) {\n  if (frag) {\n    const start = frag.start + sliding;\n    frag.start = frag.startPTS = start;\n    frag.endPTS = start + frag.duration;\n  }\n}\nfunction adjustSlidingStart(sliding, details) {\n  // Update segments\n  const fragments = details.fragments;\n  for (let i = 0, len = fragments.length; i < len; i++) {\n    adjustFragmentStart(fragments[i], sliding);\n  }\n  // Update LL-HLS parts at the end of the playlist\n  if (details.fragmentHint) {\n    adjustFragmentStart(details.fragmentHint, sliding);\n  }\n  details.alignedSliding = true;\n}\n\n/**\n * Using the parameters of the last level, this function computes PTS' of the new fragments so that they form a\n * contiguous stream with the last fragments.\n * The PTS of a fragment lets Hls.js know where it fits into a stream - by knowing every PTS, we know which fragment to\n * download at any given time. PTS is normally computed when the fragment is demuxed, so taking this step saves us time\n * and an extra download.\n * @param lastFrag\n * @param lastLevel\n * @param details\n */\nfunction alignStream(lastFrag, lastLevel, details) {\n  if (!lastLevel) {\n    return;\n  }\n  alignDiscontinuities(lastFrag, details, lastLevel);\n  if (!details.alignedSliding && lastLevel.details) {\n    // If the PTS wasn't figured out via discontinuity sequence that means there was no CC increase within the level.\n    // Aligning via Program Date Time should therefore be reliable, since PDT should be the same within the same\n    // discontinuity sequence.\n    alignPDT(details, lastLevel.details);\n  }\n  if (!details.alignedSliding && lastLevel.details && !details.skippedSegments) {\n    // Try to align on sn so that we pick a better start fragment.\n    // Do not perform this on playlists with delta updates as this is only to align levels on switch\n    // and adjustSliding only adjusts fragments after skippedSegments.\n    adjustSliding(lastLevel.details, details);\n  }\n}\n\n/**\n * Computes the PTS if a new level's fragments using the PTS of a fragment in the last level which shares the same\n * discontinuity sequence.\n * @param lastFrag - The last Fragment which shares the same discontinuity sequence\n * @param lastLevel - The details of the last loaded level\n * @param details - The details of the new level\n */\nfunction alignDiscontinuities(lastFrag, details, lastLevel) {\n  if (shouldAlignOnDiscontinuities(lastFrag, lastLevel, details)) {\n    const referenceFrag = findDiscontinuousReferenceFrag(lastLevel.details, details);\n    if (referenceFrag && isFiniteNumber(referenceFrag.start)) {\n      logger.log(`Adjusting PTS using last level due to CC increase within current level ${details.url}`);\n      adjustSlidingStart(referenceFrag.start, details);\n    }\n  }\n}\n\n/**\n * Computes the PTS of a new level's fragments using the difference in Program Date Time from the last level.\n * @param details - The details of the new level\n * @param lastDetails - The details of the last loaded level\n */\nfunction alignPDT(details, lastDetails) {\n  // This check protects the unsafe \"!\" usage below for null program date time access.\n  if (!lastDetails.fragments.length || !details.hasProgramDateTime || !lastDetails.hasProgramDateTime) {\n    return;\n  }\n  // if last level sliding is 1000 and its first frag PROGRAM-DATE-TIME is 2017-08-20 1:10:00 AM\n  // and if new details first frag PROGRAM DATE-TIME is 2017-08-20 1:10:08 AM\n  // then we can deduce that playlist B sliding is 1000+8 = 1008s\n  const lastPDT = lastDetails.fragments[0].programDateTime; // hasProgramDateTime check above makes this safe.\n  const newPDT = details.fragments[0].programDateTime;\n  // date diff is in ms. frag.start is in seconds\n  const sliding = (newPDT - lastPDT) / 1000 + lastDetails.fragments[0].start;\n  if (sliding && isFiniteNumber(sliding)) {\n    logger.log(`Adjusting PTS using programDateTime delta ${newPDT - lastPDT}ms, sliding:${sliding.toFixed(3)} ${details.url} `);\n    adjustSlidingStart(sliding, details);\n  }\n}\n\n/**\n * Ensures appropriate time-alignment between renditions based on PDT. Unlike `alignPDT`, which adjusts\n * the timeline based on the delta between PDTs of the 0th fragment of two playlists/`LevelDetails`,\n * this function assumes the timelines represented in `refDetails` are accurate, including the PDTs,\n * and uses the \"wallclock\"/PDT timeline as a cross-reference to `details`, adjusting the presentation\n * times/timelines of `details` accordingly.\n * Given the asynchronous nature of fetches and initial loads of live `main` and audio/subtitle tracks,\n * the primary purpose of this function is to ensure the \"local timelines\" of audio/subtitle tracks\n * are aligned to the main/video timeline, using PDT as the cross-reference/\"anchor\" that should\n * be consistent across playlists, per the HLS spec.\n * @param details - The details of the rendition you'd like to time-align (e.g. an audio rendition).\n * @param refDetails - The details of the reference rendition with start and PDT times for alignment.\n */\nfunction alignMediaPlaylistByPDT(details, refDetails) {\n  if (!details.hasProgramDateTime || !refDetails.hasProgramDateTime) {\n    return;\n  }\n  const fragments = details.fragments;\n  const refFragments = refDetails.fragments;\n  if (!fragments.length || !refFragments.length) {\n    return;\n  }\n\n  // Calculate a delta to apply to all fragments according to the delta in PDT times and start times\n  // of a fragment in the reference details, and a fragment in the target details of the same discontinuity.\n  // If a fragment of the same discontinuity was not found use the middle fragment of both.\n  const middleFrag = Math.round(refFragments.length / 2) - 1;\n  const refFrag = refFragments[middleFrag];\n  const frag = findFirstFragWithCC(fragments, refFrag.cc) || fragments[Math.round(fragments.length / 2) - 1];\n  const refPDT = refFrag.programDateTime;\n  const targetPDT = frag.programDateTime;\n  if (refPDT === null || targetPDT === null) {\n    return;\n  }\n  const delta = (targetPDT - refPDT) / 1000 - (frag.start - refFrag.start);\n  adjustSlidingStart(delta, details);\n}\n\nclass AESCrypto {\n  constructor(subtle, iv) {\n    this.subtle = void 0;\n    this.aesIV = void 0;\n    this.subtle = subtle;\n    this.aesIV = iv;\n  }\n  decrypt(data, key) {\n    return this.subtle.decrypt({\n      name: 'AES-CBC',\n      iv: this.aesIV\n    }, key, data);\n  }\n}\n\nclass FastAESKey {\n  constructor(subtle, key) {\n    this.subtle = void 0;\n    this.key = void 0;\n    this.subtle = subtle;\n    this.key = key;\n  }\n  expandKey() {\n    return this.subtle.importKey('raw', this.key, {\n      name: 'AES-CBC'\n    }, false, ['encrypt', 'decrypt']);\n  }\n}\n\n// PKCS7\nfunction removePadding(array) {\n  const outputBytes = array.byteLength;\n  const paddingBytes = outputBytes && new DataView(array.buffer).getUint8(outputBytes - 1);\n  if (paddingBytes) {\n    return sliceUint8(array, 0, outputBytes - paddingBytes);\n  }\n  return array;\n}\nclass AESDecryptor {\n  constructor() {\n    this.rcon = [0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];\n    this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)];\n    this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)];\n    this.sBox = new Uint32Array(256);\n    this.invSBox = new Uint32Array(256);\n    this.key = new Uint32Array(0);\n    this.ksRows = 0;\n    this.keySize = 0;\n    this.keySchedule = void 0;\n    this.invKeySchedule = void 0;\n    this.initTable();\n  }\n\n  // Using view.getUint32() also swaps the byte order.\n  uint8ArrayToUint32Array_(arrayBuffer) {\n    const view = new DataView(arrayBuffer);\n    const newArray = new Uint32Array(4);\n    for (let i = 0; i < 4; i++) {\n      newArray[i] = view.getUint32(i * 4);\n    }\n    return newArray;\n  }\n  initTable() {\n    const sBox = this.sBox;\n    const invSBox = this.invSBox;\n    const subMix = this.subMix;\n    const subMix0 = subMix[0];\n    const subMix1 = subMix[1];\n    const subMix2 = subMix[2];\n    const subMix3 = subMix[3];\n    const invSubMix = this.invSubMix;\n    const invSubMix0 = invSubMix[0];\n    const invSubMix1 = invSubMix[1];\n    const invSubMix2 = invSubMix[2];\n    const invSubMix3 = invSubMix[3];\n    const d = new Uint32Array(256);\n    let x = 0;\n    let xi = 0;\n    let i = 0;\n    for (i = 0; i < 256; i++) {\n      if (i < 128) {\n        d[i] = i << 1;\n      } else {\n        d[i] = i << 1 ^ 0x11b;\n      }\n    }\n    for (i = 0; i < 256; i++) {\n      let sx = xi ^ xi << 1 ^ xi << 2 ^ xi << 3 ^ xi << 4;\n      sx = sx >>> 8 ^ sx & 0xff ^ 0x63;\n      sBox[x] = sx;\n      invSBox[sx] = x;\n\n      // Compute multiplication\n      const x2 = d[x];\n      const x4 = d[x2];\n      const x8 = d[x4];\n\n      // Compute sub/invSub bytes, mix columns tables\n      let t = d[sx] * 0x101 ^ sx * 0x1010100;\n      subMix0[x] = t << 24 | t >>> 8;\n      subMix1[x] = t << 16 | t >>> 16;\n      subMix2[x] = t << 8 | t >>> 24;\n      subMix3[x] = t;\n\n      // Compute inv sub bytes, inv mix columns tables\n      t = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;\n      invSubMix0[sx] = t << 24 | t >>> 8;\n      invSubMix1[sx] = t << 16 | t >>> 16;\n      invSubMix2[sx] = t << 8 | t >>> 24;\n      invSubMix3[sx] = t;\n\n      // Compute next counter\n      if (!x) {\n        x = xi = 1;\n      } else {\n        x = x2 ^ d[d[d[x8 ^ x2]]];\n        xi ^= d[d[xi]];\n      }\n    }\n  }\n  expandKey(keyBuffer) {\n    // convert keyBuffer to Uint32Array\n    const key = this.uint8ArrayToUint32Array_(keyBuffer);\n    let sameKey = true;\n    let offset = 0;\n    while (offset < key.length && sameKey) {\n      sameKey = key[offset] === this.key[offset];\n      offset++;\n    }\n    if (sameKey) {\n      return;\n    }\n    this.key = key;\n    const keySize = this.keySize = key.length;\n    if (keySize !== 4 && keySize !== 6 && keySize !== 8) {\n      throw new Error('Invalid aes key size=' + keySize);\n    }\n    const ksRows = this.ksRows = (keySize + 6 + 1) * 4;\n    let ksRow;\n    let invKsRow;\n    const keySchedule = this.keySchedule = new Uint32Array(ksRows);\n    const invKeySchedule = this.invKeySchedule = new Uint32Array(ksRows);\n    const sbox = this.sBox;\n    const rcon = this.rcon;\n    const invSubMix = this.invSubMix;\n    const invSubMix0 = invSubMix[0];\n    const invSubMix1 = invSubMix[1];\n    const invSubMix2 = invSubMix[2];\n    const invSubMix3 = invSubMix[3];\n    let prev;\n    let t;\n    for (ksRow = 0; ksRow < ksRows; ksRow++) {\n      if (ksRow < keySize) {\n        prev = keySchedule[ksRow] = key[ksRow];\n        continue;\n      }\n      t = prev;\n      if (ksRow % keySize === 0) {\n        // Rot word\n        t = t << 8 | t >>> 24;\n\n        // Sub word\n        t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff];\n\n        // Mix Rcon\n        t ^= rcon[ksRow / keySize | 0] << 24;\n      } else if (keySize > 6 && ksRow % keySize === 4) {\n        // Sub word\n        t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff];\n      }\n      keySchedule[ksRow] = prev = (keySchedule[ksRow - keySize] ^ t) >>> 0;\n    }\n    for (invKsRow = 0; invKsRow < ksRows; invKsRow++) {\n      ksRow = ksRows - invKsRow;\n      if (invKsRow & 3) {\n        t = keySchedule[ksRow];\n      } else {\n        t = keySchedule[ksRow - 4];\n      }\n      if (invKsRow < 4 || ksRow <= 4) {\n        invKeySchedule[invKsRow] = t;\n      } else {\n        invKeySchedule[invKsRow] = invSubMix0[sbox[t >>> 24]] ^ invSubMix1[sbox[t >>> 16 & 0xff]] ^ invSubMix2[sbox[t >>> 8 & 0xff]] ^ invSubMix3[sbox[t & 0xff]];\n      }\n      invKeySchedule[invKsRow] = invKeySchedule[invKsRow] >>> 0;\n    }\n  }\n\n  // Adding this as a method greatly improves performance.\n  networkToHostOrderSwap(word) {\n    return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;\n  }\n  decrypt(inputArrayBuffer, offset, aesIV) {\n    const nRounds = this.keySize + 6;\n    const invKeySchedule = this.invKeySchedule;\n    const invSBOX = this.invSBox;\n    const invSubMix = this.invSubMix;\n    const invSubMix0 = invSubMix[0];\n    const invSubMix1 = invSubMix[1];\n    const invSubMix2 = invSubMix[2];\n    const invSubMix3 = invSubMix[3];\n    const initVector = this.uint8ArrayToUint32Array_(aesIV);\n    let initVector0 = initVector[0];\n    let initVector1 = initVector[1];\n    let initVector2 = initVector[2];\n    let initVector3 = initVector[3];\n    const inputInt32 = new Int32Array(inputArrayBuffer);\n    const outputInt32 = new Int32Array(inputInt32.length);\n    let t0, t1, t2, t3;\n    let s0, s1, s2, s3;\n    let inputWords0, inputWords1, inputWords2, inputWords3;\n    let ksRow, i;\n    const swapWord = this.networkToHostOrderSwap;\n    while (offset < inputInt32.length) {\n      inputWords0 = swapWord(inputInt32[offset]);\n      inputWords1 = swapWord(inputInt32[offset + 1]);\n      inputWords2 = swapWord(inputInt32[offset + 2]);\n      inputWords3 = swapWord(inputInt32[offset + 3]);\n      s0 = inputWords0 ^ invKeySchedule[0];\n      s1 = inputWords3 ^ invKeySchedule[1];\n      s2 = inputWords2 ^ invKeySchedule[2];\n      s3 = inputWords1 ^ invKeySchedule[3];\n      ksRow = 4;\n\n      // Iterate through the rounds of decryption\n      for (i = 1; i < nRounds; i++) {\n        t0 = invSubMix0[s0 >>> 24] ^ invSubMix1[s1 >> 16 & 0xff] ^ invSubMix2[s2 >> 8 & 0xff] ^ invSubMix3[s3 & 0xff] ^ invKeySchedule[ksRow];\n        t1 = invSubMix0[s1 >>> 24] ^ invSubMix1[s2 >> 16 & 0xff] ^ invSubMix2[s3 >> 8 & 0xff] ^ invSubMix3[s0 & 0xff] ^ invKeySchedule[ksRow + 1];\n        t2 = invSubMix0[s2 >>> 24] ^ invSubMix1[s3 >> 16 & 0xff] ^ invSubMix2[s0 >> 8 & 0xff] ^ invSubMix3[s1 & 0xff] ^ invKeySchedule[ksRow + 2];\n        t3 = invSubMix0[s3 >>> 24] ^ invSubMix1[s0 >> 16 & 0xff] ^ invSubMix2[s1 >> 8 & 0xff] ^ invSubMix3[s2 & 0xff] ^ invKeySchedule[ksRow + 3];\n        // Update state\n        s0 = t0;\n        s1 = t1;\n        s2 = t2;\n        s3 = t3;\n        ksRow = ksRow + 4;\n      }\n\n      // Shift rows, sub bytes, add round key\n      t0 = invSBOX[s0 >>> 24] << 24 ^ invSBOX[s1 >> 16 & 0xff] << 16 ^ invSBOX[s2 >> 8 & 0xff] << 8 ^ invSBOX[s3 & 0xff] ^ invKeySchedule[ksRow];\n      t1 = invSBOX[s1 >>> 24] << 24 ^ invSBOX[s2 >> 16 & 0xff] << 16 ^ invSBOX[s3 >> 8 & 0xff] << 8 ^ invSBOX[s0 & 0xff] ^ invKeySchedule[ksRow + 1];\n      t2 = invSBOX[s2 >>> 24] << 24 ^ invSBOX[s3 >> 16 & 0xff] << 16 ^ invSBOX[s0 >> 8 & 0xff] << 8 ^ invSBOX[s1 & 0xff] ^ invKeySchedule[ksRow + 2];\n      t3 = invSBOX[s3 >>> 24] << 24 ^ invSBOX[s0 >> 16 & 0xff] << 16 ^ invSBOX[s1 >> 8 & 0xff] << 8 ^ invSBOX[s2 & 0xff] ^ invKeySchedule[ksRow + 3];\n\n      // Write\n      outputInt32[offset] = swapWord(t0 ^ initVector0);\n      outputInt32[offset + 1] = swapWord(t3 ^ initVector1);\n      outputInt32[offset + 2] = swapWord(t2 ^ initVector2);\n      outputInt32[offset + 3] = swapWord(t1 ^ initVector3);\n\n      // reset initVector to last 4 unsigned int\n      initVector0 = inputWords0;\n      initVector1 = inputWords1;\n      initVector2 = inputWords2;\n      initVector3 = inputWords3;\n      offset = offset + 4;\n    }\n    return outputInt32.buffer;\n  }\n}\n\nconst CHUNK_SIZE = 16; // 16 bytes, 128 bits\n\nclass Decrypter {\n  constructor(config, {\n    removePKCS7Padding = true\n  } = {}) {\n    this.logEnabled = true;\n    this.removePKCS7Padding = void 0;\n    this.subtle = null;\n    this.softwareDecrypter = null;\n    this.key = null;\n    this.fastAesKey = null;\n    this.remainderData = null;\n    this.currentIV = null;\n    this.currentResult = null;\n    this.useSoftware = void 0;\n    this.useSoftware = config.enableSoftwareAES;\n    this.removePKCS7Padding = removePKCS7Padding;\n    // built in decryptor expects PKCS7 padding\n    if (removePKCS7Padding) {\n      try {\n        const browserCrypto = self.crypto;\n        if (browserCrypto) {\n          this.subtle = browserCrypto.subtle || browserCrypto.webkitSubtle;\n        }\n      } catch (e) {\n        /* no-op */\n      }\n    }\n    if (this.subtle === null) {\n      this.useSoftware = true;\n    }\n  }\n  destroy() {\n    this.subtle = null;\n    this.softwareDecrypter = null;\n    this.key = null;\n    this.fastAesKey = null;\n    this.remainderData = null;\n    this.currentIV = null;\n    this.currentResult = null;\n  }\n  isSync() {\n    return this.useSoftware;\n  }\n  flush() {\n    const {\n      currentResult,\n      remainderData\n    } = this;\n    if (!currentResult || remainderData) {\n      this.reset();\n      return null;\n    }\n    const data = new Uint8Array(currentResult);\n    this.reset();\n    if (this.removePKCS7Padding) {\n      return removePadding(data);\n    }\n    return data;\n  }\n  reset() {\n    this.currentResult = null;\n    this.currentIV = null;\n    this.remainderData = null;\n    if (this.softwareDecrypter) {\n      this.softwareDecrypter = null;\n    }\n  }\n  decrypt(data, key, iv) {\n    if (this.useSoftware) {\n      return new Promise((resolve, reject) => {\n        this.softwareDecrypt(new Uint8Array(data), key, iv);\n        const decryptResult = this.flush();\n        if (decryptResult) {\n          resolve(decryptResult.buffer);\n        } else {\n          reject(new Error('[softwareDecrypt] Failed to decrypt data'));\n        }\n      });\n    }\n    return this.webCryptoDecrypt(new Uint8Array(data), key, iv);\n  }\n\n  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached\n  // data is handled in the flush() call\n  softwareDecrypt(data, key, iv) {\n    const {\n      currentIV,\n      currentResult,\n      remainderData\n    } = this;\n    this.logOnce('JS AES decrypt');\n    // The output is staggered during progressive parsing - the current result is cached, and emitted on the next call\n    // This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached\n    // the end on flush(), but by that time we have already received all bytes for the segment.\n    // Progressive decryption does not work with WebCrypto\n\n    if (remainderData) {\n      data = appendUint8Array(remainderData, data);\n      this.remainderData = null;\n    }\n\n    // Byte length must be a multiple of 16 (AES-128 = 128 bit blocks = 16 bytes)\n    const currentChunk = this.getValidChunk(data);\n    if (!currentChunk.length) {\n      return null;\n    }\n    if (currentIV) {\n      iv = currentIV;\n    }\n    let softwareDecrypter = this.softwareDecrypter;\n    if (!softwareDecrypter) {\n      softwareDecrypter = this.softwareDecrypter = new AESDecryptor();\n    }\n    softwareDecrypter.expandKey(key);\n    const result = currentResult;\n    this.currentResult = softwareDecrypter.decrypt(currentChunk.buffer, 0, iv);\n    this.currentIV = sliceUint8(currentChunk, -16).buffer;\n    if (!result) {\n      return null;\n    }\n    return result;\n  }\n  webCryptoDecrypt(data, key, iv) {\n    const subtle = this.subtle;\n    if (this.key !== key || !this.fastAesKey) {\n      this.key = key;\n      this.fastAesKey = new FastAESKey(subtle, key);\n    }\n    return this.fastAesKey.expandKey().then(aesKey => {\n      // decrypt using web crypto\n      if (!subtle) {\n        return Promise.reject(new Error('web crypto not initialized'));\n      }\n      this.logOnce('WebCrypto AES decrypt');\n      const crypto = new AESCrypto(subtle, new Uint8Array(iv));\n      return crypto.decrypt(data.buffer, aesKey);\n    }).catch(err => {\n      logger.warn(`[decrypter]: WebCrypto Error, disable WebCrypto API, ${err.name}: ${err.message}`);\n      return this.onWebCryptoError(data, key, iv);\n    });\n  }\n  onWebCryptoError(data, key, iv) {\n    this.useSoftware = true;\n    this.logEnabled = true;\n    this.softwareDecrypt(data, key, iv);\n    const decryptResult = this.flush();\n    if (decryptResult) {\n      return decryptResult.buffer;\n    }\n    throw new Error('WebCrypto and softwareDecrypt: failed to decrypt data');\n  }\n  getValidChunk(data) {\n    let currentChunk = data;\n    const splitPoint = data.length - data.length % CHUNK_SIZE;\n    if (splitPoint !== data.length) {\n      currentChunk = sliceUint8(data, 0, splitPoint);\n      this.remainderData = sliceUint8(data, splitPoint);\n    }\n    return currentChunk;\n  }\n  logOnce(msg) {\n    if (!this.logEnabled) {\n      return;\n    }\n    logger.log(`[decrypter]: ${msg}`);\n    this.logEnabled = false;\n  }\n}\n\n/**\n *  TimeRanges to string helper\n */\n\nconst TimeRanges = {\n  toString: function (r) {\n    let log = '';\n    const len = r.length;\n    for (let i = 0; i < len; i++) {\n      log += `[${r.start(i).toFixed(3)}-${r.end(i).toFixed(3)}]`;\n    }\n    return log;\n  }\n};\n\nconst State = {\n  STOPPED: 'STOPPED',\n  IDLE: 'IDLE',\n  KEY_LOADING: 'KEY_LOADING',\n  FRAG_LOADING: 'FRAG_LOADING',\n  FRAG_LOADING_WAITING_RETRY: 'FRAG_LOADING_WAITING_RETRY',\n  WAITING_TRACK: 'WAITING_TRACK',\n  PARSING: 'PARSING',\n  PARSED: 'PARSED',\n  ENDED: 'ENDED',\n  ERROR: 'ERROR',\n  WAITING_INIT_PTS: 'WAITING_INIT_PTS',\n  WAITING_LEVEL: 'WAITING_LEVEL'\n};\nclass BaseStreamController extends TaskLoop {\n  constructor(hls, fragmentTracker, keyLoader, logPrefix, playlistType) {\n    super();\n    this.hls = void 0;\n    this.fragPrevious = null;\n    this.fragCurrent = null;\n    this.fragmentTracker = void 0;\n    this.transmuxer = null;\n    this._state = State.STOPPED;\n    this.playlistType = void 0;\n    this.media = null;\n    this.mediaBuffer = null;\n    this.config = void 0;\n    this.bitrateTest = false;\n    this.lastCurrentTime = 0;\n    this.nextLoadPosition = 0;\n    this.startPosition = 0;\n    this.startTimeOffset = null;\n    this.loadedmetadata = false;\n    this.retryDate = 0;\n    this.levels = null;\n    this.fragmentLoader = void 0;\n    this.keyLoader = void 0;\n    this.levelLastLoaded = null;\n    this.startFragRequested = false;\n    this.decrypter = void 0;\n    this.initPTS = [];\n    this.onvseeking = null;\n    this.onvended = null;\n    this.logPrefix = '';\n    this.log = void 0;\n    this.warn = void 0;\n    this.playlistType = playlistType;\n    this.logPrefix = logPrefix;\n    this.log = logger.log.bind(logger, `${logPrefix}:`);\n    this.warn = logger.warn.bind(logger, `${logPrefix}:`);\n    this.hls = hls;\n    this.fragmentLoader = new FragmentLoader(hls.config);\n    this.keyLoader = keyLoader;\n    this.fragmentTracker = fragmentTracker;\n    this.config = hls.config;\n    this.decrypter = new Decrypter(hls.config);\n    hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);\n  }\n  doTick() {\n    this.onTickEnd();\n  }\n  onTickEnd() {}\n\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  startLoad(startPosition) {}\n  stopLoad() {\n    this.fragmentLoader.abort();\n    this.keyLoader.abort(this.playlistType);\n    const frag = this.fragCurrent;\n    if (frag != null && frag.loader) {\n      frag.abortRequests();\n      this.fragmentTracker.removeFragment(frag);\n    }\n    this.resetTransmuxer();\n    this.fragCurrent = null;\n    this.fragPrevious = null;\n    this.clearInterval();\n    this.clearNextTick();\n    this.state = State.STOPPED;\n  }\n  _streamEnded(bufferInfo, levelDetails) {\n    // If playlist is live, there is another buffered range after the current range, nothing buffered, media is detached,\n    // of nothing loading/loaded return false\n    if (levelDetails.live || bufferInfo.nextStart || !bufferInfo.end || !this.media) {\n      return false;\n    }\n    const partList = levelDetails.partList;\n    // Since the last part isn't guaranteed to correspond to the last playlist segment for Low-Latency HLS,\n    // check instead if the last part is buffered.\n    if (partList != null && partList.length) {\n      const lastPart = partList[partList.length - 1];\n\n      // Checking the midpoint of the part for potential margin of error and related issues.\n      // NOTE: Technically I believe parts could yield content that is < the computed duration (including potential a duration of 0)\n      // and still be spec-compliant, so there may still be edge cases here. Likewise, there could be issues in end of stream\n      // part mismatches for independent audio and video playlists/segments.\n      const lastPartBuffered = BufferHelper.isBuffered(this.media, lastPart.start + lastPart.duration / 2);\n      return lastPartBuffered;\n    }\n    const playlistType = levelDetails.fragments[levelDetails.fragments.length - 1].type;\n    return this.fragmentTracker.isEndListAppended(playlistType);\n  }\n  getLevelDetails() {\n    if (this.levels && this.levelLastLoaded !== null) {\n      var _this$levels$this$lev;\n      return (_this$levels$this$lev = this.levels[this.levelLastLoaded]) == null ? void 0 : _this$levels$this$lev.details;\n    }\n  }\n  onMediaAttached(event, data) {\n    const media = this.media = this.mediaBuffer = data.media;\n    this.onvseeking = this.onMediaSeeking.bind(this);\n    this.onvended = this.onMediaEnded.bind(this);\n    media.addEventListener('seeking', this.onvseeking);\n    media.addEventListener('ended', this.onvended);\n    const config = this.config;\n    if (this.levels && config.autoStartLoad && this.state === State.STOPPED) {\n      this.startLoad(config.startPosition);\n    }\n  }\n  onMediaDetaching() {\n    const media = this.media;\n    if (media != null && media.ended) {\n      this.log('MSE detaching and video ended, reset startPosition');\n      this.startPosition = this.lastCurrentTime = 0;\n    }\n\n    // remove video listeners\n    if (media && this.onvseeking && this.onvended) {\n      media.removeEventListener('seeking', this.onvseeking);\n      media.removeEventListener('ended', this.onvended);\n      this.onvseeking = this.onvended = null;\n    }\n    if (this.keyLoader) {\n      this.keyLoader.detach();\n    }\n    this.media = this.mediaBuffer = null;\n    this.loadedmetadata = false;\n    this.fragmentTracker.removeAllFragments();\n    this.stopLoad();\n  }\n  onMediaSeeking() {\n    const {\n      config,\n      fragCurrent,\n      media,\n      mediaBuffer,\n      state\n    } = this;\n    const currentTime = media ? media.currentTime : 0;\n    const bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);\n    this.log(`media seeking to ${isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime}, state: ${state}`);\n    if (this.state === State.ENDED) {\n      this.resetLoadingState();\n    } else if (fragCurrent) {\n      // Seeking while frag load is in progress\n      const tolerance = config.maxFragLookUpTolerance;\n      const fragStartOffset = fragCurrent.start - tolerance;\n      const fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;\n      // if seeking out of buffered range or into new one\n      if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {\n        const pastFragment = currentTime > fragEndOffset;\n        // if the seek position is outside the current fragment range\n        if (currentTime < fragStartOffset || pastFragment) {\n          if (pastFragment && fragCurrent.loader) {\n            this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');\n            fragCurrent.abortRequests();\n            this.resetLoadingState();\n          }\n          this.fragPrevious = null;\n        }\n      }\n    }\n    if (media) {\n      // Remove gap fragments\n      this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, this.playlistType, true);\n      this.lastCurrentTime = currentTime;\n    }\n\n    // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target\n    if (!this.loadedmetadata && !bufferInfo.len) {\n      this.nextLoadPosition = this.startPosition = currentTime;\n    }\n\n    // Async tick to speed up processing\n    this.tickImmediate();\n  }\n  onMediaEnded() {\n    // reset startPosition and lastCurrentTime to restart playback @ stream beginning\n    this.startPosition = this.lastCurrentTime = 0;\n  }\n  onManifestLoaded(event, data) {\n    this.startTimeOffset = data.startTimeOffset;\n    this.initPTS = [];\n  }\n  onHandlerDestroying() {\n    this.stopLoad();\n    super.onHandlerDestroying();\n  }\n  onHandlerDestroyed() {\n    this.state = State.STOPPED;\n    if (this.fragmentLoader) {\n      this.fragmentLoader.destroy();\n    }\n    if (this.keyLoader) {\n      this.keyLoader.destroy();\n    }\n    if (this.decrypter) {\n      this.decrypter.destroy();\n    }\n    this.hls = this.log = this.warn = this.decrypter = this.keyLoader = this.fragmentLoader = this.fragmentTracker = null;\n    super.onHandlerDestroyed();\n  }\n  loadFragment(frag, level, targetBufferTime) {\n    this._loadFragForPlayback(frag, level, targetBufferTime);\n  }\n  _loadFragForPlayback(frag, level, targetBufferTime) {\n    const progressCallback = data => {\n      if (this.fragContextChanged(frag)) {\n        this.warn(`Fragment ${frag.sn}${data.part ? ' p: ' + data.part.index : ''} of level ${frag.level} was dropped during download.`);\n        this.fragmentTracker.removeFragment(frag);\n        return;\n      }\n      frag.stats.chunkCount++;\n      this._handleFragmentLoadProgress(data);\n    };\n    this._doFragLoad(frag, level, targetBufferTime, progressCallback).then(data => {\n      if (!data) {\n        // if we're here we probably needed to backtrack or are waiting for more parts\n        return;\n      }\n      const state = this.state;\n      if (this.fragContextChanged(frag)) {\n        if (state === State.FRAG_LOADING || !this.fragCurrent && state === State.PARSING) {\n          this.fragmentTracker.removeFragment(frag);\n          this.state = State.IDLE;\n        }\n        return;\n      }\n      if ('payload' in data) {\n        this.log(`Loaded fragment ${frag.sn} of level ${frag.level}`);\n        this.hls.trigger(Events.FRAG_LOADED, data);\n      }\n\n      // Pass through the whole payload; controllers not implementing progressive loading receive data from this callback\n      this._handleFragmentLoadComplete(data);\n    }).catch(reason => {\n      if (this.state === State.STOPPED || this.state === State.ERROR) {\n        return;\n      }\n      this.warn(reason);\n      this.resetFragmentLoading(frag);\n    });\n  }\n  clearTrackerIfNeeded(frag) {\n    var _this$mediaBuffer;\n    const {\n      fragmentTracker\n    } = this;\n    const fragState = fragmentTracker.getState(frag);\n    if (fragState === FragmentState.APPENDING) {\n      // Lower the buffer size and try again\n      const playlistType = frag.type;\n      const bufferedInfo = this.getFwdBufferInfo(this.mediaBuffer, playlistType);\n      const minForwardBufferLength = Math.max(frag.duration, bufferedInfo ? bufferedInfo.len : this.config.maxBufferLength);\n      if (this.reduceMaxBufferLength(minForwardBufferLength)) {\n        fragmentTracker.removeFragment(frag);\n      }\n    } else if (((_this$mediaBuffer = this.mediaBuffer) == null ? void 0 : _this$mediaBuffer.buffered.length) === 0) {\n      // Stop gap for bad tracker / buffer flush behavior\n      fragmentTracker.removeAllFragments();\n    } else if (fragmentTracker.hasParts(frag.type)) {\n      // In low latency mode, remove fragments for which only some parts were buffered\n      fragmentTracker.detectPartialFragments({\n        frag,\n        part: null,\n        stats: frag.stats,\n        id: frag.type\n      });\n      if (fragmentTracker.getState(frag) === FragmentState.PARTIAL) {\n        fragmentTracker.removeFragment(frag);\n      }\n    }\n  }\n  checkLiveUpdate(details) {\n    if (details.updated && !details.live) {\n      // Live stream ended, update fragment tracker\n      const lastFragment = details.fragments[details.fragments.length - 1];\n      this.fragmentTracker.detectPartialFragments({\n        frag: lastFragment,\n        part: null,\n        stats: lastFragment.stats,\n        id: lastFragment.type\n      });\n    }\n    if (!details.fragments[0]) {\n      details.deltaUpdateFailed = true;\n    }\n  }\n  flushMainBuffer(startOffset, endOffset, type = null) {\n    if (!(startOffset - endOffset)) {\n      return;\n    }\n    // When alternate audio is playing, the audio-stream-controller is responsible for the audio buffer. Otherwise,\n    // passing a null type flushes both buffers\n    const flushScope = {\n      startOffset,\n      endOffset,\n      type\n    };\n    this.hls.trigger(Events.BUFFER_FLUSHING, flushScope);\n  }\n  _loadInitSegment(frag, level) {\n    this._doFragLoad(frag, level).then(data => {\n      if (!data || this.fragContextChanged(frag) || !this.levels) {\n        throw new Error('init load aborted');\n      }\n      return data;\n    }).then(data => {\n      const {\n        hls\n      } = this;\n      const {\n        payload\n      } = data;\n      const decryptData = frag.decryptdata;\n\n      // check to see if the payload needs to be decrypted\n      if (payload && payload.byteLength > 0 && decryptData && decryptData.key && decryptData.iv && decryptData.method === 'AES-128') {\n        const startTime = self.performance.now();\n        // decrypt init segment data\n        return this.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer).catch(err => {\n          hls.trigger(Events.ERROR, {\n            type: ErrorTypes.MEDIA_ERROR,\n            details: ErrorDetails.FRAG_DECRYPT_ERROR,\n            fatal: false,\n            error: err,\n            reason: err.message,\n            frag\n          });\n          throw err;\n        }).then(decryptedData => {\n          const endTime = self.performance.now();\n          hls.trigger(Events.FRAG_DECRYPTED, {\n            frag,\n            payload: decryptedData,\n            stats: {\n              tstart: startTime,\n              tdecrypt: endTime\n            }\n          });\n          data.payload = decryptedData;\n          return data;\n        });\n      }\n      return data;\n    }).then(data => {\n      const {\n        fragCurrent,\n        hls,\n        levels\n      } = this;\n      if (!levels) {\n        throw new Error('init load aborted, missing levels');\n      }\n      const stats = frag.stats;\n      this.state = State.IDLE;\n      level.fragmentError = 0;\n      frag.data = new Uint8Array(data.payload);\n      stats.parsing.start = stats.buffering.start = self.performance.now();\n      stats.parsing.end = stats.buffering.end = self.performance.now();\n\n      // Silence FRAG_BUFFERED event if fragCurrent is null\n      if (data.frag === fragCurrent) {\n        hls.trigger(Events.FRAG_BUFFERED, {\n          stats,\n          frag: fragCurrent,\n          part: null,\n          id: frag.type\n        });\n      }\n      this.tick();\n    }).catch(reason => {\n      if (this.state === State.STOPPED || this.state === State.ERROR) {\n        return;\n      }\n      this.warn(reason);\n      this.resetFragmentLoading(frag);\n    });\n  }\n  fragContextChanged(frag) {\n    const {\n      fragCurrent\n    } = this;\n    return !frag || !fragCurrent || frag.level !== fragCurrent.level || frag.sn !== fragCurrent.sn || frag.urlId !== fragCurrent.urlId;\n  }\n  fragBufferedComplete(frag, part) {\n    var _frag$startPTS, _frag$endPTS, _this$fragCurrent, _this$fragPrevious;\n    const media = this.mediaBuffer ? this.mediaBuffer : this.media;\n    this.log(`Buffered ${frag.type} sn: ${frag.sn}${part ? ' part: ' + part.index : ''} of ${this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'} ${frag.level} (frag:[${((_frag$startPTS = frag.startPTS) != null ? _frag$startPTS : NaN).toFixed(3)}-${((_frag$endPTS = frag.endPTS) != null ? _frag$endPTS : NaN).toFixed(3)}] > buffer:${media ? TimeRanges.toString(BufferHelper.getBuffered(media)) : '(detached)'})`);\n    this.state = State.IDLE;\n    if (!media) {\n      return;\n    }\n    if (!this.loadedmetadata && frag.type == PlaylistLevelType.MAIN && media.buffered.length && ((_this$fragCurrent = this.fragCurrent) == null ? void 0 : _this$fragCurrent.sn) === ((_this$fragPrevious = this.fragPrevious) == null ? void 0 : _this$fragPrevious.sn)) {\n      this.loadedmetadata = true;\n      this.seekToStartPos();\n    }\n    this.tick();\n  }\n  seekToStartPos() {}\n  _handleFragmentLoadComplete(fragLoadedEndData) {\n    const {\n      transmuxer\n    } = this;\n    if (!transmuxer) {\n      return;\n    }\n    const {\n      frag,\n      part,\n      partsLoaded\n    } = fragLoadedEndData;\n    // If we did not load parts, or loaded all parts, we have complete (not partial) fragment data\n    const complete = !partsLoaded || partsLoaded.length === 0 || partsLoaded.some(fragLoaded => !fragLoaded);\n    const chunkMeta = new ChunkMetadata(frag.level, frag.sn, frag.stats.chunkCount + 1, 0, part ? part.index : -1, !complete);\n    transmuxer.flush(chunkMeta);\n  }\n\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  _handleFragmentLoadProgress(frag) {}\n  _doFragLoad(frag, level, targetBufferTime = null, progressCallback) {\n    var _frag$decryptdata;\n    const details = level == null ? void 0 : level.details;\n    if (!this.levels || !details) {\n      throw new Error(`frag load aborted, missing level${details ? '' : ' detail'}s`);\n    }\n    let keyLoadingPromise = null;\n    if (frag.encrypted && !((_frag$decryptdata = frag.decryptdata) != null && _frag$decryptdata.key)) {\n      this.log(`Loading key for ${frag.sn} of [${details.startSN}-${details.endSN}], ${this.logPrefix === '[stream-controller]' ? 'level' : 'track'} ${frag.level}`);\n      this.state = State.KEY_LOADING;\n      this.fragCurrent = frag;\n      keyLoadingPromise = this.keyLoader.load(frag).then(keyLoadedData => {\n        if (!this.fragContextChanged(keyLoadedData.frag)) {\n          this.hls.trigger(Events.KEY_LOADED, keyLoadedData);\n          if (this.state === State.KEY_LOADING) {\n            this.state = State.IDLE;\n          }\n          return keyLoadedData;\n        }\n      });\n      this.hls.trigger(Events.KEY_LOADING, {\n        frag\n      });\n      if (this.fragCurrent === null) {\n        keyLoadingPromise = Promise.reject(new Error(`frag load aborted, context changed in KEY_LOADING`));\n      }\n    } else if (!frag.encrypted && details.encryptedFragments.length) {\n      this.keyLoader.loadClear(frag, details.encryptedFragments);\n    }\n    targetBufferTime = Math.max(frag.start, targetBufferTime || 0);\n    if (this.config.lowLatencyMode && frag.sn !== 'initSegment') {\n      const partList = details.partList;\n      if (partList && progressCallback) {\n        if (targetBufferTime > frag.end && details.fragmentHint) {\n          frag = details.fragmentHint;\n        }\n        const partIndex = this.getNextPart(partList, frag, targetBufferTime);\n        if (partIndex > -1) {\n          const part = partList[partIndex];\n          this.log(`Loading part sn: ${frag.sn} p: ${part.index} cc: ${frag.cc} of playlist [${details.startSN}-${details.endSN}] parts [0-${partIndex}-${partList.length - 1}] ${this.logPrefix === '[stream-controller]' ? 'level' : 'track'}: ${frag.level}, target: ${parseFloat(targetBufferTime.toFixed(3))}`);\n          this.nextLoadPosition = part.start + part.duration;\n          this.state = State.FRAG_LOADING;\n          let _result;\n          if (keyLoadingPromise) {\n            _result = keyLoadingPromise.then(keyLoadedData => {\n              if (!keyLoadedData || this.fragContextChanged(keyLoadedData.frag)) {\n                return null;\n              }\n              return this.doFragPartsLoad(frag, part, level, progressCallback);\n            }).catch(error => this.handleFragLoadError(error));\n          } else {\n            _result = this.doFragPartsLoad(frag, part, level, progressCallback).catch(error => this.handleFragLoadError(error));\n          }\n          this.hls.trigger(Events.FRAG_LOADING, {\n            frag,\n            part,\n            targetBufferTime\n          });\n          if (this.fragCurrent === null) {\n            return Promise.reject(new Error(`frag load aborted, context changed in FRAG_LOADING parts`));\n          }\n          return _result;\n        } else if (!frag.url || this.loadedEndOfParts(partList, targetBufferTime)) {\n          // Fragment hint has no parts\n          return Promise.resolve(null);\n        }\n      }\n    }\n    this.log(`Loading fragment ${frag.sn} cc: ${frag.cc} ${details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : ''}${this.logPrefix === '[stream-controller]' ? 'level' : 'track'}: ${frag.level}, target: ${parseFloat(targetBufferTime.toFixed(3))}`);\n    // Don't update nextLoadPosition for fragments which are not buffered\n    if (isFiniteNumber(frag.sn) && !this.bitrateTest) {\n      this.nextLoadPosition = frag.start + frag.duration;\n    }\n    this.state = State.FRAG_LOADING;\n\n    // Load key before streaming fragment data\n    const dataOnProgress = this.config.progressive;\n    let result;\n    if (dataOnProgress && keyLoadingPromise) {\n      result = keyLoadingPromise.then(keyLoadedData => {\n        if (!keyLoadedData || this.fragContextChanged(keyLoadedData == null ? void 0 : keyLoadedData.frag)) {\n          return null;\n        }\n        return this.fragmentLoader.load(frag, progressCallback);\n      }).catch(error => this.handleFragLoadError(error));\n    } else {\n      // load unencrypted fragment data with progress event,\n      // or handle fragment result after key and fragment are finished loading\n      result = Promise.all([this.fragmentLoader.load(frag, dataOnProgress ? progressCallback : undefined), keyLoadingPromise]).then(([fragLoadedData]) => {\n        if (!dataOnProgress && fragLoadedData && progressCallback) {\n          progressCallback(fragLoadedData);\n        }\n        return fragLoadedData;\n      }).catch(error => this.handleFragLoadError(error));\n    }\n    this.hls.trigger(Events.FRAG_LOADING, {\n      frag,\n      targetBufferTime\n    });\n    if (this.fragCurrent === null) {\n      return Promise.reject(new Error(`frag load aborted, context changed in FRAG_LOADING`));\n    }\n    return result;\n  }\n  doFragPartsLoad(frag, fromPart, level, progressCallback) {\n    return new Promise((resolve, reject) => {\n      var _level$details;\n      const partsLoaded = [];\n      const initialPartList = (_level$details = level.details) == null ? void 0 : _level$details.partList;\n      const loadPart = part => {\n        this.fragmentLoader.loadPart(frag, part, progressCallback).then(partLoadedData => {\n          partsLoaded[part.index] = partLoadedData;\n          const loadedPart = partLoadedData.part;\n          this.hls.trigger(Events.FRAG_LOADED, partLoadedData);\n          const nextPart = getPartWith(level, frag.sn, part.index + 1) || findPart(initialPartList, frag.sn, part.index + 1);\n          if (nextPart) {\n            loadPart(nextPart);\n          } else {\n            return resolve({\n              frag,\n              part: loadedPart,\n              partsLoaded\n            });\n          }\n        }).catch(reject);\n      };\n      loadPart(fromPart);\n    });\n  }\n  handleFragLoadError(error) {\n    if ('data' in error) {\n      const data = error.data;\n      if (error.data && data.details === ErrorDetails.INTERNAL_ABORTED) {\n        this.handleFragLoadAborted(data.frag, data.part);\n      } else {\n        this.hls.trigger(Events.ERROR, data);\n      }\n    } else {\n      this.hls.trigger(Events.ERROR, {\n        type: ErrorTypes.OTHER_ERROR,\n        details: ErrorDetails.INTERNAL_EXCEPTION,\n        err: error,\n        error,\n        fatal: true\n      });\n    }\n    return null;\n  }\n  _handleTransmuxerFlush(chunkMeta) {\n    const context = this.getCurrentContext(chunkMeta);\n    if (!context || this.state !== State.PARSING) {\n      if (!this.fragCurrent && this.state !== State.STOPPED && this.state !== State.ERROR) {\n        this.state = State.IDLE;\n      }\n      return;\n    }\n    const {\n      frag,\n      part,\n      level\n    } = context;\n    const now = self.performance.now();\n    frag.stats.parsing.end = now;\n    if (part) {\n      part.stats.parsing.end = now;\n    }\n    this.updateLevelTiming(frag, part, level, chunkMeta.partial);\n  }\n  getCurrentContext(chunkMeta) {\n    const {\n      levels,\n      fragCurrent\n    } = this;\n    const {\n      level: levelIndex,\n      sn,\n      part: partIndex\n    } = chunkMeta;\n    if (!(levels != null && levels[levelIndex])) {\n      this.warn(`Levels object was unset while buffering fragment ${sn} of level ${levelIndex}. The current chunk will not be buffered.`);\n      return null;\n    }\n    const level = levels[levelIndex];\n    const part = partIndex > -1 ? getPartWith(level, sn, partIndex) : null;\n    const frag = part ? part.fragment : getFragmentWithSN(level, sn, fragCurrent);\n    if (!frag) {\n      return null;\n    }\n    if (fragCurrent && fragCurrent !== frag) {\n      frag.stats = fragCurrent.stats;\n    }\n    return {\n      frag,\n      part,\n      level\n    };\n  }\n  bufferFragmentData(data, frag, part, chunkMeta, noBacktracking) {\n    var _buffer;\n    if (!data || this.state !== State.PARSING) {\n      return;\n    }\n    const {\n      data1,\n      data2\n    } = data;\n    let buffer = data1;\n    if (data1 && data2) {\n      // Combine the moof + mdat so that we buffer with a single append\n      buffer = appendUint8Array(data1, data2);\n    }\n    if (!((_buffer = buffer) != null && _buffer.length)) {\n      return;\n    }\n    const segment = {\n      type: data.type,\n      frag,\n      part,\n      chunkMeta,\n      parent: frag.type,\n      data: buffer\n    };\n    this.hls.trigger(Events.BUFFER_APPENDING, segment);\n    if (data.dropped && data.independent && !part) {\n      if (noBacktracking) {\n        return;\n      }\n      // Clear buffer so that we reload previous segments sequentially if required\n      this.flushBufferGap(frag);\n    }\n  }\n  flushBufferGap(frag) {\n    const media = this.media;\n    if (!media) {\n      return;\n    }\n    // If currentTime is not buffered, clear the back buffer so that we can backtrack as much as needed\n    if (!BufferHelper.isBuffered(media, media.currentTime)) {\n      this.flushMainBuffer(0, frag.start);\n      return;\n    }\n    // Remove back-buffer without interrupting playback to allow back tracking\n    const currentTime = media.currentTime;\n    const bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);\n    const fragDuration = frag.duration;\n    const segmentFraction = Math.min(this.config.maxFragLookUpTolerance * 2, fragDuration * 0.25);\n    const start = Math.max(Math.min(frag.start - segmentFraction, bufferInfo.end - segmentFraction), currentTime + segmentFraction);\n    if (frag.start - start > segmentFraction) {\n      this.flushMainBuffer(start, frag.start);\n    }\n  }\n  getFwdBufferInfo(bufferable, type) {\n    const pos = this.getLoadPosition();\n    if (!isFiniteNumber(pos)) {\n      return null;\n    }\n    return this.getFwdBufferInfoAtPos(bufferable, pos, type);\n  }\n  getFwdBufferInfoAtPos(bufferable, pos, type) {\n    const {\n      config: {\n        maxBufferHole\n      }\n    } = this;\n    const bufferInfo = BufferHelper.bufferInfo(bufferable, pos, maxBufferHole);\n    // Workaround flaw in getting forward buffer when maxBufferHole is smaller than gap at current pos\n    if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) {\n      const bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type);\n      if (bufferedFragAtPos && bufferInfo.nextStart < bufferedFragAtPos.end) {\n        return BufferHelper.bufferInfo(bufferable, pos, Math.max(bufferInfo.nextStart, maxBufferHole));\n      }\n    }\n    return bufferInfo;\n  }\n  getMaxBufferLength(levelBitrate) {\n    const {\n      config\n    } = this;\n    let maxBufLen;\n    if (levelBitrate) {\n      maxBufLen = Math.max(8 * config.maxBufferSize / levelBitrate, config.maxBufferLength);\n    } else {\n      maxBufLen = config.maxBufferLength;\n    }\n    return Math.min(maxBufLen, config.maxMaxBufferLength);\n  }\n  reduceMaxBufferLength(threshold) {\n    const config = this.config;\n    const minLength = threshold || config.maxBufferLength;\n    if (config.maxMaxBufferLength >= minLength) {\n      // reduce max buffer length as it might be too high. we do this to avoid loop flushing ...\n      config.maxMaxBufferLength /= 2;\n      this.warn(`Reduce max buffer length to ${config.maxMaxBufferLength}s`);\n      return true;\n    }\n    return false;\n  }\n  getAppendedFrag(position, playlistType = PlaylistLevelType.MAIN) {\n    const fragOrPart = this.fragmentTracker.getAppendedFrag(position, PlaylistLevelType.MAIN);\n    if (fragOrPart && 'fragment' in fragOrPart) {\n      return fragOrPart.fragment;\n    }\n    return fragOrPart;\n  }\n  getNextFragment(pos, levelDetails) {\n    const fragments = levelDetails.fragments;\n    const fragLen = fragments.length;\n    if (!fragLen) {\n      return null;\n    }\n\n    // find fragment index, contiguous with end of buffer position\n    const {\n      config\n    } = this;\n    const start = fragments[0].start;\n    let frag;\n    if (levelDetails.live) {\n      const initialLiveManifestSize = config.initialLiveManifestSize;\n      if (fragLen < initialLiveManifestSize) {\n        this.warn(`Not enough fragments to start playback (have: ${fragLen}, need: ${initialLiveManifestSize})`);\n        return null;\n      }\n      // The real fragment start times for a live stream are only known after the PTS range for that level is known.\n      // In order to discover the range, we load the best matching fragment for that level and demux it.\n      // Do not load using live logic if the starting frag is requested - we want to use getFragmentAtPosition() so that\n      // we get the fragment matching that start time\n      if (!levelDetails.PTSKnown && !this.startFragRequested && this.startPosition === -1) {\n        frag = this.getInitialLiveFragment(levelDetails, fragments);\n        this.startPosition = frag ? this.hls.liveSyncPosition || frag.start : pos;\n      }\n    } else if (pos <= start) {\n      // VoD playlist: if loadPosition before start of playlist, load first fragment\n      frag = fragments[0];\n    }\n\n    // If we haven't run into any special cases already, just load the fragment most closely matching the requested position\n    if (!frag) {\n      const end = config.lowLatencyMode ? levelDetails.partEnd : levelDetails.fragmentEnd;\n      frag = this.getFragmentAtPosition(pos, end, levelDetails);\n    }\n    return this.mapToInitFragWhenRequired(frag);\n  }\n  isLoopLoading(frag, targetBufferTime) {\n    const trackerState = this.fragmentTracker.getState(frag);\n    return (trackerState === FragmentState.OK || trackerState === FragmentState.PARTIAL && !!frag.gap) && this.nextLoadPosition > targetBufferTime;\n  }\n  getNextFragmentLoopLoading(frag, levelDetails, bufferInfo, playlistType, maxBufLen) {\n    const gapStart = frag.gap;\n    const nextFragment = this.getNextFragment(this.nextLoadPosition, levelDetails);\n    if (nextFragment === null) {\n      return nextFragment;\n    }\n    frag = nextFragment;\n    if (gapStart && frag && !frag.gap && bufferInfo.nextStart) {\n      // Media buffered after GAP tags should not make the next buffer timerange exceed forward buffer length\n      const nextbufferInfo = this.getFwdBufferInfoAtPos(this.mediaBuffer ? this.mediaBuffer : this.media, bufferInfo.nextStart, playlistType);\n      if (nextbufferInfo !== null && bufferInfo.len + nextbufferInfo.len >= maxBufLen) {\n        // Returning here might result in not finding an audio and video candiate to skip to\n        this.log(`buffer full after gaps in \"${playlistType}\" playlist starting at sn: ${frag.sn}`);\n        return null;\n      }\n    }\n    return frag;\n  }\n  mapToInitFragWhenRequired(frag) {\n    // If an initSegment is present, it must be buffered first\n    if (frag != null && frag.initSegment && !(frag != null && frag.initSegment.data) && !this.bitrateTest) {\n      return frag.initSegment;\n    }\n    return frag;\n  }\n  getNextPart(partList, frag, targetBufferTime) {\n    let nextPart = -1;\n    let contiguous = false;\n    let independentAttrOmitted = true;\n    for (let i = 0, len = partList.length; i < len; i++) {\n      const part = partList[i];\n      independentAttrOmitted = independentAttrOmitted && !part.independent;\n      if (nextPart > -1 && targetBufferTime < part.start) {\n        break;\n      }\n      const loaded = part.loaded;\n      if (loaded) {\n        nextPart = -1;\n      } else if ((contiguous || part.independent || independentAttrOmitted) && part.fragment === frag) {\n        nextPart = i;\n      }\n      contiguous = loaded;\n    }\n    return nextPart;\n  }\n  loadedEndOfParts(partList, targetBufferTime) {\n    const lastPart = partList[partList.length - 1];\n    return lastPart && targetBufferTime > lastPart.start && lastPart.loaded;\n  }\n\n  /*\n   This method is used find the best matching first fragment for a live playlist. This fragment is used to calculate the\n   \"sliding\" of the playlist, which is its offset from the start of playback. After sliding we can compute the real\n   start and end times for each fragment in the playlist (after which this method will not need to be called).\n  */\n  getInitialLiveFragment(levelDetails, fragments) {\n    const fragPrevious = this.fragPrevious;\n    let frag = null;\n    if (fragPrevious) {\n      if (levelDetails.hasProgramDateTime) {\n        // Prefer using PDT, because it can be accurate enough to choose the correct fragment without knowing the level sliding\n        this.log(`Live playlist, switching playlist, load frag with same PDT: ${fragPrevious.programDateTime}`);\n        frag = findFragmentByPDT(fragments, fragPrevious.endProgramDateTime, this.config.maxFragLookUpTolerance);\n      }\n      if (!frag) {\n        // SN does not need to be accurate between renditions, but depending on the packaging it may be so.\n        const targetSN = fragPrevious.sn + 1;\n        if (targetSN >= levelDetails.startSN && targetSN <= levelDetails.endSN) {\n          const fragNext = fragments[targetSN - levelDetails.startSN];\n          // Ensure that we're staying within the continuity range, since PTS resets upon a new range\n          if (fragPrevious.cc === fragNext.cc) {\n            frag = fragNext;\n            this.log(`Live playlist, switching playlist, load frag with next SN: ${frag.sn}`);\n          }\n        }\n        // It's important to stay within the continuity range if available; otherwise the fragments in the playlist\n        // will have the wrong start times\n        if (!frag) {\n          frag = findFragWithCC(fragments, fragPrevious.cc);\n          if (frag) {\n            this.log(`Live playlist, switching playlist, load frag with same CC: ${frag.sn}`);\n          }\n        }\n      }\n    } else {\n      // Find a new start fragment when fragPrevious is null\n      const liveStart = this.hls.liveSyncPosition;\n      if (liveStart !== null) {\n        frag = this.getFragmentAtPosition(liveStart, this.bitrateTest ? levelDetails.fragmentEnd : levelDetails.edge, levelDetails);\n      }\n    }\n    return frag;\n  }\n\n  /*\n  This method finds the best matching fragment given the provided position.\n   */\n  getFragmentAtPosition(bufferEnd, end, levelDetails) {\n    const {\n      config\n    } = this;\n    let {\n      fragPrevious\n    } = this;\n    let {\n      fragments,\n      endSN\n    } = levelDetails;\n    const {\n      fragmentHint\n    } = levelDetails;\n    const tolerance = config.maxFragLookUpTolerance;\n    const partList = levelDetails.partList;\n    const loadingParts = !!(config.lowLatencyMode && partList != null && partList.length && fragmentHint);\n    if (loadingParts && fragmentHint && !this.bitrateTest) {\n      // Include incomplete fragment with parts at end\n      fragments = fragments.concat(fragmentHint);\n      endSN = fragmentHint.sn;\n    }\n    let frag;\n    if (bufferEnd < end) {\n      const lookupTolerance = bufferEnd > end - tolerance ? 0 : tolerance;\n      // Remove the tolerance if it would put the bufferEnd past the actual end of stream\n      // Uses buffer and sequence number to calculate switch segment (required if using EXT-X-DISCONTINUITY-SEQUENCE)\n      frag = findFragmentByPTS(fragPrevious, fragments, bufferEnd, lookupTolerance);\n    } else {\n      // reach end of playlist\n      frag = fragments[fragments.length - 1];\n    }\n    if (frag) {\n      const curSNIdx = frag.sn - levelDetails.startSN;\n      // Move fragPrevious forward to support forcing the next fragment to load\n      // when the buffer catches up to a previously buffered range.\n      const fragState = this.fragmentTracker.getState(frag);\n      if (fragState === FragmentState.OK || fragState === FragmentState.PARTIAL && frag.gap) {\n        fragPrevious = frag;\n      }\n      if (fragPrevious && frag.sn === fragPrevious.sn && (!loadingParts || partList[0].fragment.sn > frag.sn)) {\n        // Force the next fragment to load if the previous one was already selected. This can occasionally happen with\n        // non-uniform fragment durations\n        const sameLevel = fragPrevious && frag.level === fragPrevious.level;\n        if (sameLevel) {\n          const nextFrag = fragments[curSNIdx + 1];\n          if (frag.sn < endSN && this.fragmentTracker.getState(nextFrag) !== FragmentState.OK) {\n            frag = nextFrag;\n          } else {\n            frag = null;\n          }\n        }\n      }\n    }\n    return frag;\n  }\n  synchronizeToLiveEdge(levelDetails) {\n    const {\n      config,\n      media\n    } = this;\n    if (!media) {\n      return;\n    }\n    const liveSyncPosition = this.hls.liveSyncPosition;\n    const currentTime = media.currentTime;\n    const start = levelDetails.fragments[0].start;\n    const end = levelDetails.edge;\n    const withinSlidingWindow = currentTime >= start - config.maxFragLookUpTolerance && currentTime <= end;\n    // Continue if we can seek forward to sync position or if current time is outside of sliding window\n    if (liveSyncPosition !== null && media.duration > liveSyncPosition && (currentTime < liveSyncPosition || !withinSlidingWindow)) {\n      // Continue if buffer is starving or if current time is behind max latency\n      const maxLatency = config.liveMaxLatencyDuration !== undefined ? config.liveMaxLatencyDuration : config.liveMaxLatencyDurationCount * levelDetails.targetduration;\n      if (!withinSlidingWindow && media.readyState < 4 || currentTime < end - maxLatency) {\n        if (!this.loadedmetadata) {\n          this.nextLoadPosition = liveSyncPosition;\n        }\n        // Only seek if ready and there is not a significant forward buffer available for playback\n        if (media.readyState) {\n          this.warn(`Playback: ${currentTime.toFixed(3)} is located too far from the end of live sliding playlist: ${end}, reset currentTime to : ${liveSyncPosition.toFixed(3)}`);\n          media.currentTime = liveSyncPosition;\n        }\n      }\n    }\n  }\n  alignPlaylists(details, previousDetails) {\n    const {\n      levels,\n      levelLastLoaded,\n      fragPrevious\n    } = this;\n    const lastLevel = levelLastLoaded !== null ? levels[levelLastLoaded] : null;\n\n    // FIXME: If not for `shouldAlignOnDiscontinuities` requiring fragPrevious.cc,\n    //  this could all go in level-helper mergeDetails()\n    const length = details.fragments.length;\n    if (!length) {\n      this.warn(`No fragments in live playlist`);\n      return 0;\n    }\n    const slidingStart = details.fragments[0].start;\n    const firstLevelLoad = !previousDetails;\n    const aligned = details.alignedSliding && isFiniteNumber(slidingStart);\n    if (firstLevelLoad || !aligned && !slidingStart) {\n      alignStream(fragPrevious, lastLevel, details);\n      const alignedSlidingStart = details.fragments[0].start;\n      this.log(`Live playlist sliding: ${alignedSlidingStart.toFixed(2)} start-sn: ${previousDetails ? previousDetails.startSN : 'na'}->${details.startSN} prev-sn: ${fragPrevious ? fragPrevious.sn : 'na'} fragments: ${length}`);\n      return alignedSlidingStart;\n    }\n    return slidingStart;\n  }\n  waitForCdnTuneIn(details) {\n    // Wait for Low-Latency CDN Tune-in to get an updated playlist\n    const advancePartLimit = 3;\n    return details.live && details.canBlockReload && details.partTarget && details.tuneInGoal > Math.max(details.partHoldBack, details.partTarget * advancePartLimit);\n  }\n  setStartPosition(details, sliding) {\n    // compute start position if set to -1. use it straight away if value is defined\n    let startPosition = this.startPosition;\n    if (startPosition < sliding) {\n      startPosition = -1;\n    }\n    if (startPosition === -1 || this.lastCurrentTime === -1) {\n      // Use Playlist EXT-X-START:TIME-OFFSET when set\n      // Prioritize Multivariant Playlist offset so that main, audio, and subtitle stream-controller start times match\n      const offsetInMultivariantPlaylist = this.startTimeOffset !== null;\n      const startTimeOffset = offsetInMultivariantPlaylist ? this.startTimeOffset : details.startTimeOffset;\n      if (startTimeOffset !== null && isFiniteNumber(startTimeOffset)) {\n        startPosition = sliding + startTimeOffset;\n        if (startTimeOffset < 0) {\n          startPosition += details.totalduration;\n        }\n        startPosition = Math.min(Math.max(sliding, startPosition), sliding + details.totalduration);\n        this.log(`Start time offset ${startTimeOffset} found in ${offsetInMultivariantPlaylist ? 'multivariant' : 'media'} playlist, adjust startPosition to ${startPosition}`);\n        this.startPosition = startPosition;\n      } else if (details.live) {\n        // Leave this.startPosition at -1, so that we can use `getInitialLiveFragment` logic when startPosition has\n        // not been specified via the config or an as an argument to startLoad (#3736).\n        startPosition = this.hls.liveSyncPosition || sliding;\n      } else {\n        this.startPosition = startPosition = 0;\n      }\n      this.lastCurrentTime = startPosition;\n    }\n    this.nextLoadPosition = startPosition;\n  }\n  getLoadPosition() {\n    const {\n      media\n    } = this;\n    // if we have not yet loaded any fragment, start loading from start position\n    let pos = 0;\n    if (this.loadedmetadata && media) {\n      pos = media.currentTime;\n    } else if (this.nextLoadPosition) {\n      pos = this.nextLoadPosition;\n    }\n    return pos;\n  }\n  handleFragLoadAborted(frag, part) {\n    if (this.transmuxer && frag.sn !== 'initSegment' && frag.stats.aborted) {\n      this.warn(`Fragment ${frag.sn}${part ? ' part ' + part.index : ''} of level ${frag.level} was aborted`);\n      this.resetFragmentLoading(frag);\n    }\n  }\n  resetFragmentLoading(frag) {\n    if (!this.fragCurrent || !this.fragContextChanged(frag) && this.state !== State.FRAG_LOADING_WAITING_RETRY) {\n      this.state = State.IDLE;\n    }\n  }\n  onFragmentOrKeyLoadError(filterType, data) {\n    if (data.chunkMeta && !data.frag) {\n      const context = this.getCurrentContext(data.chunkMeta);\n      if (context) {\n        data.frag = context.frag;\n      }\n    }\n    const frag = data.frag;\n    // Handle frag error related to caller's filterType\n    if (!frag || frag.type !== filterType || !this.levels) {\n      return;\n    }\n    if (this.fragContextChanged(frag)) {\n      var _this$fragCurrent2;\n      this.warn(`Frag load error must match current frag to retry ${frag.url} > ${(_this$fragCurrent2 = this.fragCurrent) == null ? void 0 : _this$fragCurrent2.url}`);\n      return;\n    }\n    const gapTagEncountered = data.details === ErrorDetails.FRAG_GAP;\n    if (gapTagEncountered) {\n      this.fragmentTracker.fragBuffered(frag, true);\n    }\n    // keep retrying until the limit will be reached\n    const errorAction = data.errorAction;\n    const {\n      action,\n      retryCount = 0,\n      retryConfig\n    } = errorAction || {};\n    if (errorAction && action === NetworkErrorAction.RetryRequest && retryConfig) {\n      var _this$levelLastLoaded;\n      this.resetStartWhenNotLoaded((_this$levelLastLoaded = this.levelLastLoaded) != null ? _this$levelLastLoaded : frag.level);\n      const delay = getRetryDelay(retryConfig, retryCount);\n      this.warn(`Fragment ${frag.sn} of ${filterType} ${frag.level} errored with ${data.details}, retrying loading ${retryCount + 1}/${retryConfig.maxNumRetry} in ${delay}ms`);\n      errorAction.resolved = true;\n      this.retryDate = self.performance.now() + delay;\n      this.state = State.FRAG_LOADING_WAITING_RETRY;\n    } else if (retryConfig && errorAction) {\n      this.resetFragmentErrors(filterType);\n      if (retryCount < retryConfig.maxNumRetry) {\n        // Network retry is skipped when level switch is preferred\n        if (!gapTagEncountered) {\n          errorAction.resolved = true;\n        }\n      } else {\n        logger.warn(`${data.details} reached or exceeded max retry (${retryCount})`);\n      }\n    } else if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox) {\n      this.state = State.WAITING_LEVEL;\n    } else {\n      this.state = State.ERROR;\n    }\n    // Perform next async tick sooner to speed up error action resolution\n    this.tickImmediate();\n  }\n  reduceLengthAndFlushBuffer(data) {\n    // if in appending state\n    if (this.state === State.PARSING || this.state === State.PARSED) {\n      const playlistType = data.parent;\n      const bufferedInfo = this.getFwdBufferInfo(this.mediaBuffer, playlistType);\n      // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end\n      // reduce max buf len if current position is buffered\n      const buffered = bufferedInfo && bufferedInfo.len > 0.5;\n      if (buffered) {\n        this.reduceMaxBufferLength(bufferedInfo.len);\n      }\n      const flushBuffer = !buffered;\n      if (flushBuffer) {\n        // current position is not buffered, but browser is still complaining about buffer full error\n        // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708\n        // in that case flush the whole audio buffer to recover\n        this.warn(`Buffer full error while media.currentTime is not buffered, flush ${playlistType} buffer`);\n      }\n      if (data.frag) {\n        this.fragmentTracker.removeFragment(data.frag);\n        this.nextLoadPosition = data.frag.start;\n      }\n      this.resetLoadingState();\n      return flushBuffer;\n    }\n    return false;\n  }\n  resetFragmentErrors(filterType) {\n    if (filterType === PlaylistLevelType.AUDIO) {\n      // Reset current fragment since audio track audio is essential and may not have a fail-over track\n      this.fragCurrent = null;\n    }\n    // Fragment errors that result in a level switch or redundant fail-over\n    // should reset the stream controller state to idle\n    if (!this.loadedmetadata) {\n      this.startFragRequested = false;\n    }\n    if (this.state !== State.STOPPED) {\n      this.state = State.IDLE;\n    }\n  }\n  afterBufferFlushed(media, bufferType, playlistType) {\n    if (!media) {\n      return;\n    }\n    // After successful buffer flushing, filter flushed fragments from bufferedFrags use mediaBuffered instead of media\n    // (so that we will check against video.buffered ranges in case of alt audio track)\n    const bufferedTimeRanges = BufferHelper.getBuffered(media);\n    this.fragmentTracker.detectEvictedFragments(bufferType, bufferedTimeRanges, playlistType);\n    if (this.state === State.ENDED) {\n      this.resetLoadingState();\n    }\n  }\n  resetLoadingState() {\n    this.log('Reset loading state');\n    this.fragCurrent = null;\n    this.fragPrevious = null;\n    this.state = State.IDLE;\n  }\n  resetStartWhenNotLoaded(level) {\n    // if loadedmetadata is not set, it means that first frag request failed\n    // in that case, reset startFragRequested flag\n    if (!this.loadedmetadata) {\n      this.startFragRequested = false;\n      const details = this.levels ? this.levels[level].details : null;\n      if (details != null && details.live) {\n        // Update the start position and return to IDLE to recover live start\n        this.startPosition = -1;\n        this.setStartPosition(details, 0);\n        this.resetLoadingState();\n      } else {\n        this.nextLoadPosition = this.startPosition;\n      }\n    }\n  }\n  resetWhenMissingContext(chunkMeta) {\n    var _this$levelLastLoaded2;\n    this.warn(`The loading context changed while buffering fragment ${chunkMeta.sn} of level ${chunkMeta.level}. This chunk will not be buffered.`);\n    this.removeUnbufferedFrags();\n    this.resetStartWhenNotLoaded((_this$levelLastLoaded2 = this.levelLastLoaded) != null ? _this$levelLastLoaded2 : chunkMeta.level);\n    this.resetLoadingState();\n  }\n  removeUnbufferedFrags(start = 0) {\n    this.fragmentTracker.removeFragmentsInRange(start, Infinity, this.playlistType, false, true);\n  }\n  updateLevelTiming(frag, part, level, partial) {\n    var _this$transmuxer;\n    const details = level.details;\n    if (!details) {\n      this.warn('level.details undefined');\n      return;\n    }\n    const parsed = Object.keys(frag.elementaryStreams).reduce((result, type) => {\n      const info = frag.elementaryStreams[type];\n      if (info) {\n        const parsedDuration = info.endPTS - info.startPTS;\n        if (parsedDuration <= 0) {\n          // Destroy the transmuxer after it's next time offset failed to advance because duration was <= 0.\n          // The new transmuxer will be configured with a time offset matching the next fragment start,\n          // preventing the timeline from shifting.\n          this.warn(`Could not parse fragment ${frag.sn} ${type} duration reliably (${parsedDuration})`);\n          return result || false;\n        }\n        const drift = partial ? 0 : updateFragPTSDTS(details, frag, info.startPTS, info.endPTS, info.startDTS, info.endDTS);\n        this.hls.trigger(Events.LEVEL_PTS_UPDATED, {\n          details,\n          level,\n          drift,\n          type,\n          frag,\n          start: info.startPTS,\n          end: info.endPTS\n        });\n        return true;\n      }\n      return result;\n    }, false);\n    if (parsed) {\n      level.fragmentError = 0;\n    } else if (((_this$transmuxer = this.transmuxer) == null ? void 0 : _this$transmuxer.error) === null) {\n      const error = new Error(`Found no media in fragment ${frag.sn} of level ${frag.level} resetting transmuxer to fallback to playlist timing`);\n      if (level.fragmentError === 0) {\n        // Mark and track the odd empty segment as a gap to avoid reloading\n        level.fragmentError++;\n        frag.gap = true;\n        this.fragmentTracker.removeFragment(frag);\n        this.fragmentTracker.fragBuffered(frag, true);\n      }\n      this.warn(error.message);\n      this.hls.trigger(Events.ERROR, {\n        type: ErrorTypes.MEDIA_ERROR,\n        details: ErrorDetails.FRAG_PARSING_ERROR,\n        fatal: false,\n        error,\n        frag,\n        reason: `Found no media in msn ${frag.sn} of level \"${level.url}\"`\n      });\n      if (!this.hls) {\n        return;\n      }\n      this.resetTransmuxer();\n      // For this error fallthrough. Marking parsed will allow advancing to next fragment.\n    }\n\n    this.state = State.PARSED;\n    this.hls.trigger(Events.FRAG_PARSED, {\n      frag,\n      part\n    });\n  }\n  resetTransmuxer() {\n    if (this.transmuxer) {\n      this.transmuxer.destroy();\n      this.transmuxer = null;\n    }\n  }\n  recoverWorkerError(data) {\n    if (data.event === 'demuxerWorker') {\n      var _ref, _this$levelLastLoaded3, _this$fragCurrent3;\n      this.fragmentTracker.removeAllFragments();\n      this.resetTransmuxer();\n      this.resetStartWhenNotLoaded((_ref = (_this$levelLastLoaded3 = this.levelLastLoaded) != null ? _this$levelLastLoaded3 : (_this$fragCurrent3 = this.fragCurrent) == null ? void 0 : _this$fragCurrent3.level) != null ? _ref : 0);\n      this.resetLoadingState();\n    }\n  }\n  set state(nextState) {\n    const previousState = this._state;\n    if (previousState !== nextState) {\n      this._state = nextState;\n      this.log(`${previousState}->${nextState}`);\n    }\n  }\n  get state() {\n    return this._state;\n  }\n}\n\nfunction getSourceBuffer() {\n  return self.SourceBuffer || self.WebKitSourceBuffer;\n}\n\n/**\n * @ignore\n */\nfunction isSupported() {\n  const mediaSource = getMediaSource();\n  if (!mediaSource) {\n    return false;\n  }\n  const sourceBuffer = getSourceBuffer();\n  const isTypeSupported = mediaSource && typeof mediaSource.isTypeSupported === 'function' && mediaSource.isTypeSupported('video/mp4; codecs=\"avc1.42E01E,mp4a.40.2\"');\n\n  // if SourceBuffer is exposed ensure its API is valid\n  // Older browsers do not expose SourceBuffer globally so checking SourceBuffer.prototype is impossible\n  const sourceBufferValidAPI = !sourceBuffer || sourceBuffer.prototype && typeof sourceBuffer.prototype.appendBuffer === 'function' && typeof sourceBuffer.prototype.remove === 'function';\n  return !!isTypeSupported && !!sourceBufferValidAPI;\n}\n\n/**\n * @ignore\n */\nfunction changeTypeSupported() {\n  var _sourceBuffer$prototy;\n  const sourceBuffer = getSourceBuffer();\n  return typeof (sourceBuffer == null ? void 0 : (_sourceBuffer$prototy = sourceBuffer.prototype) == null ? void 0 : _sourceBuffer$prototy.changeType) === 'function';\n}\n\n// ensure the worker ends up in the bundle\n// If the worker should not be included this gets aliased to empty.js\nfunction hasUMDWorker() {\n  return typeof __HLS_WORKER_BUNDLE__ === 'function';\n}\nfunction injectWorker() {\n  const blob = new self.Blob([`var exports={};var module={exports:exports};function define(f){f()};define.amd=true;(${__HLS_WORKER_BUNDLE__.toString()})(true);`], {\n    type: 'text/javascript'\n  });\n  const objectURL = self.URL.createObjectURL(blob);\n  const worker = new self.Worker(objectURL);\n  return {\n    worker,\n    objectURL\n  };\n}\nfunction loadWorker(path) {\n  const scriptURL = new self.URL(path, self.location.href).href;\n  const worker = new self.Worker(scriptURL);\n  return {\n    worker,\n    scriptURL\n  };\n}\n\nfunction dummyTrack(type = '', inputTimeScale = 90000) {\n  return {\n    type,\n    id: -1,\n    pid: -1,\n    inputTimeScale,\n    sequenceNumber: -1,\n    samples: [],\n    dropped: 0\n  };\n}\n\nclass BaseAudioDemuxer {\n  constructor() {\n    this._audioTrack = void 0;\n    this._id3Track = void 0;\n    this.frameIndex = 0;\n    this.cachedData = null;\n    this.basePTS = null;\n    this.initPTS = null;\n    this.lastPTS = null;\n  }\n  resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) {\n    this._id3Track = {\n      type: 'id3',\n      id: 3,\n      pid: -1,\n      inputTimeScale: 90000,\n      sequenceNumber: 0,\n      samples: [],\n      dropped: 0\n    };\n  }\n  resetTimeStamp(deaultTimestamp) {\n    this.initPTS = deaultTimestamp;\n    this.resetContiguity();\n  }\n  resetContiguity() {\n    this.basePTS = null;\n    this.lastPTS = null;\n    this.frameIndex = 0;\n  }\n  canParse(data, offset) {\n    return false;\n  }\n  appendFrame(track, data, offset) {}\n\n  // feed incoming data to the front of the parsing pipeline\n  demux(data, timeOffset) {\n    if (this.cachedData) {\n      data = appendUint8Array(this.cachedData, data);\n      this.cachedData = null;\n    }\n    let id3Data = getID3Data(data, 0);\n    let offset = id3Data ? id3Data.length : 0;\n    let lastDataIndex;\n    const track = this._audioTrack;\n    const id3Track = this._id3Track;\n    const timestamp = id3Data ? getTimeStamp(id3Data) : undefined;\n    const length = data.length;\n    if (this.basePTS === null || this.frameIndex === 0 && isFiniteNumber(timestamp)) {\n      this.basePTS = initPTSFn(timestamp, timeOffset, this.initPTS);\n      this.lastPTS = this.basePTS;\n    }\n    if (this.lastPTS === null) {\n      this.lastPTS = this.basePTS;\n    }\n\n    // more expressive than alternative: id3Data?.length\n    if (id3Data && id3Data.length > 0) {\n      id3Track.samples.push({\n        pts: this.lastPTS,\n        dts: this.lastPTS,\n        data: id3Data,\n        type: MetadataSchema.audioId3,\n        duration: Number.POSITIVE_INFINITY\n      });\n    }\n    while (offset < length) {\n      if (this.canParse(data, offset)) {\n        const frame = this.appendFrame(track, data, offset);\n        if (frame) {\n          this.frameIndex++;\n          this.lastPTS = frame.sample.pts;\n          offset += frame.length;\n          lastDataIndex = offset;\n        } else {\n          offset = length;\n        }\n      } else if (canParse$2(data, offset)) {\n        // after a ID3.canParse, a call to ID3.getID3Data *should* always returns some data\n        id3Data = getID3Data(data, offset);\n        id3Track.samples.push({\n          pts: this.lastPTS,\n          dts: this.lastPTS,\n          data: id3Data,\n          type: MetadataSchema.audioId3,\n          duration: Number.POSITIVE_INFINITY\n        });\n        offset += id3Data.length;\n        lastDataIndex = offset;\n      } else {\n        offset++;\n      }\n      if (offset === length && lastDataIndex !== length) {\n        const partialData = sliceUint8(data, lastDataIndex);\n        if (this.cachedData) {\n          this.cachedData = appendUint8Array(this.cachedData, partialData);\n        } else {\n          this.cachedData = partialData;\n        }\n      }\n    }\n    return {\n      audioTrack: track,\n      videoTrack: dummyTrack(),\n      id3Track,\n      textTrack: dummyTrack()\n    };\n  }\n  demuxSampleAes(data, keyData, timeOffset) {\n    return Promise.reject(new Error(`[${this}] This demuxer does not support Sample-AES decryption`));\n  }\n  flush(timeOffset) {\n    // Parse cache in case of remaining frames.\n    const cachedData = this.cachedData;\n    if (cachedData) {\n      this.cachedData = null;\n      this.demux(cachedData, 0);\n    }\n    return {\n      audioTrack: this._audioTrack,\n      videoTrack: dummyTrack(),\n      id3Track: this._id3Track,\n      textTrack: dummyTrack()\n    };\n  }\n  destroy() {}\n}\n\n/**\n * Initialize PTS\n * <p>\n *    use timestamp unless it is undefined, NaN or Infinity\n * </p>\n */\nconst initPTSFn = (timestamp, timeOffset, initPTS) => {\n  if (isFiniteNumber(timestamp)) {\n    return timestamp * 90;\n  }\n  const init90kHz = initPTS ? initPTS.baseTime * 90000 / initPTS.timescale : 0;\n  return timeOffset * 90000 + init90kHz;\n};\n\n/**\n * ADTS parser helper\n * @link https://wiki.multimedia.cx/index.php?title=ADTS\n */\nfunction getAudioConfig(observer, data, offset, audioCodec) {\n  let adtsObjectType;\n  let adtsExtensionSamplingIndex;\n  let adtsChannelConfig;\n  let config;\n  const userAgent = navigator.userAgent.toLowerCase();\n  const manifestCodec = audioCodec;\n  const adtsSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];\n  // byte 2\n  adtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;\n  const adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2;\n  if (adtsSamplingIndex > adtsSamplingRates.length - 1) {\n    observer.trigger(Events.ERROR, {\n      type: ErrorTypes.MEDIA_ERROR,\n      details: ErrorDetails.FRAG_PARSING_ERROR,\n      fatal: true,\n      reason: `invalid ADTS sampling index:${adtsSamplingIndex}`\n    });\n    return;\n  }\n  adtsChannelConfig = (data[offset + 2] & 0x01) << 2;\n  // byte 3\n  adtsChannelConfig |= (data[offset + 3] & 0xc0) >>> 6;\n  logger.log(`manifest codec:${audioCodec}, ADTS type:${adtsObjectType}, samplingIndex:${adtsSamplingIndex}`);\n  // firefox: freq less than 24kHz = AAC SBR (HE-AAC)\n  if (/firefox/i.test(userAgent)) {\n    if (adtsSamplingIndex >= 6) {\n      adtsObjectType = 5;\n      config = new Array(4);\n      // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies\n      // there is a factor 2 between frame sample rate and output sample rate\n      // multiply frequency by 2 (see table below, equivalent to substract 3)\n      adtsExtensionSamplingIndex = adtsSamplingIndex - 3;\n    } else {\n      adtsObjectType = 2;\n      config = new Array(2);\n      adtsExtensionSamplingIndex = adtsSamplingIndex;\n    }\n    // Android : always use AAC\n  } else if (userAgent.indexOf('android') !== -1) {\n    adtsObjectType = 2;\n    config = new Array(2);\n    adtsExtensionSamplingIndex = adtsSamplingIndex;\n  } else {\n    /*  for other browsers (Chrome/Vivaldi/Opera ...)\n        always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...)\n    */\n    adtsObjectType = 5;\n    config = new Array(4);\n    // if (manifest codec is HE-AAC or HE-AACv2) OR (manifest codec not specified AND frequency less than 24kHz)\n    if (audioCodec && (audioCodec.indexOf('mp4a.40.29') !== -1 || audioCodec.indexOf('mp4a.40.5') !== -1) || !audioCodec && adtsSamplingIndex >= 6) {\n      // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies\n      // there is a factor 2 between frame sample rate and output sample rate\n      // multiply frequency by 2 (see table below, equivalent to substract 3)\n      adtsExtensionSamplingIndex = adtsSamplingIndex - 3;\n    } else {\n      // if (manifest codec is AAC) AND (frequency less than 24kHz AND nb channel is 1) OR (manifest codec not specified and mono audio)\n      // Chrome fails to play back with low frequency AAC LC mono when initialized with HE-AAC.  This is not a problem with stereo.\n      if (audioCodec && audioCodec.indexOf('mp4a.40.2') !== -1 && (adtsSamplingIndex >= 6 && adtsChannelConfig === 1 || /vivaldi/i.test(userAgent)) || !audioCodec && adtsChannelConfig === 1) {\n        adtsObjectType = 2;\n        config = new Array(2);\n      }\n      adtsExtensionSamplingIndex = adtsSamplingIndex;\n    }\n  }\n  /* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config\n      ISO 14496-3 (AAC).pdf - Table 1.13 — Syntax of AudioSpecificConfig()\n    Audio Profile / Audio Object Type\n    0: Null\n    1: AAC Main\n    2: AAC LC (Low Complexity)\n    3: AAC SSR (Scalable Sample Rate)\n    4: AAC LTP (Long Term Prediction)\n    5: SBR (Spectral Band Replication)\n    6: AAC Scalable\n   sampling freq\n    0: 96000 Hz\n    1: 88200 Hz\n    2: 64000 Hz\n    3: 48000 Hz\n    4: 44100 Hz\n    5: 32000 Hz\n    6: 24000 Hz\n    7: 22050 Hz\n    8: 16000 Hz\n    9: 12000 Hz\n    10: 11025 Hz\n    11: 8000 Hz\n    12: 7350 Hz\n    13: Reserved\n    14: Reserved\n    15: frequency is written explictly\n    Channel Configurations\n    These are the channel configurations:\n    0: Defined in AOT Specifc Config\n    1: 1 channel: front-center\n    2: 2 channels: front-left, front-right\n  */\n  // audioObjectType = profile => profile, the MPEG-4 Audio Object Type minus 1\n  config[0] = adtsObjectType << 3;\n  // samplingFrequencyIndex\n  config[0] |= (adtsSamplingIndex & 0x0e) >> 1;\n  config[1] |= (adtsSamplingIndex & 0x01) << 7;\n  // channelConfiguration\n  config[1] |= adtsChannelConfig << 3;\n  if (adtsObjectType === 5) {\n    // adtsExtensionSamplingIndex\n    config[1] |= (adtsExtensionSamplingIndex & 0x0e) >> 1;\n    config[2] = (adtsExtensionSamplingIndex & 0x01) << 7;\n    // adtsObjectType (force to 2, chrome is checking that object type is less than 5 ???\n    //    https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc\n    config[2] |= 2 << 2;\n    config[3] = 0;\n  }\n  return {\n    config,\n    samplerate: adtsSamplingRates[adtsSamplingIndex],\n    channelCount: adtsChannelConfig,\n    codec: 'mp4a.40.' + adtsObjectType,\n    manifestCodec\n  };\n}\nfunction isHeaderPattern$1(data, offset) {\n  return data[offset] === 0xff && (data[offset + 1] & 0xf6) === 0xf0;\n}\nfunction getHeaderLength(data, offset) {\n  return data[offset + 1] & 0x01 ? 7 : 9;\n}\nfunction getFullFrameLength(data, offset) {\n  return (data[offset + 3] & 0x03) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 0xe0) >>> 5;\n}\nfunction canGetFrameLength(data, offset) {\n  return offset + 5 < data.length;\n}\nfunction isHeader$1(data, offset) {\n  // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1\n  // Layer bits (position 14 and 15) in header should be always 0 for ADTS\n  // More info https://wiki.multimedia.cx/index.php?title=ADTS\n  return offset + 1 < data.length && isHeaderPattern$1(data, offset);\n}\nfunction canParse$1(data, offset) {\n  return canGetFrameLength(data, offset) && isHeaderPattern$1(data, offset) && getFullFrameLength(data, offset) <= data.length - offset;\n}\nfunction probe$1(data, offset) {\n  // same as isHeader but we also check that ADTS frame follows last ADTS frame\n  // or end of data is reached\n  if (isHeader$1(data, offset)) {\n    // ADTS header Length\n    const headerLength = getHeaderLength(data, offset);\n    if (offset + headerLength >= data.length) {\n      return false;\n    }\n    // ADTS frame Length\n    const frameLength = getFullFrameLength(data, offset);\n    if (frameLength <= headerLength) {\n      return false;\n    }\n    const newOffset = offset + frameLength;\n    return newOffset === data.length || isHeader$1(data, newOffset);\n  }\n  return false;\n}\nfunction initTrackConfig(track, observer, data, offset, audioCodec) {\n  if (!track.samplerate) {\n    const config = getAudioConfig(observer, data, offset, audioCodec);\n    if (!config) {\n      return;\n    }\n    track.config = config.config;\n    track.samplerate = config.samplerate;\n    track.channelCount = config.channelCount;\n    track.codec = config.codec;\n    track.manifestCodec = config.manifestCodec;\n    logger.log(`parsed codec:${track.codec}, rate:${config.samplerate}, channels:${config.channelCount}`);\n  }\n}\nfunction getFrameDuration(samplerate) {\n  return 1024 * 90000 / samplerate;\n}\nfunction parseFrameHeader(data, offset) {\n  // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header\n  const headerLength = getHeaderLength(data, offset);\n  if (offset + headerLength <= data.length) {\n    // retrieve frame size\n    const frameLength = getFullFrameLength(data, offset) - headerLength;\n    if (frameLength > 0) {\n      // logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}`);\n      return {\n        headerLength,\n        frameLength\n      };\n    }\n  }\n}\nfunction appendFrame$1(track, data, offset, pts, frameIndex) {\n  const frameDuration = getFrameDuration(track.samplerate);\n  const stamp = pts + frameIndex * frameDuration;\n  const header = parseFrameHeader(data, offset);\n  let unit;\n  if (header) {\n    const {\n      frameLength,\n      headerLength\n    } = header;\n    const _length = headerLength + frameLength;\n    const missing = Math.max(0, offset + _length - data.length);\n    // logger.log(`AAC frame ${frameIndex}, pts:${stamp} length@offset/total: ${frameLength}@${offset+headerLength}/${data.byteLength} missing: ${missing}`);\n    if (missing) {\n      unit = new Uint8Array(_length - headerLength);\n      unit.set(data.subarray(offset + headerLength, data.length), 0);\n    } else {\n      unit = data.subarray(offset + headerLength, offset + _length);\n    }\n    const _sample = {\n      unit,\n      pts: stamp\n    };\n    if (!missing) {\n      track.samples.push(_sample);\n    }\n    return {\n      sample: _sample,\n      length: _length,\n      missing\n    };\n  }\n  // overflow incomplete header\n  const length = data.length - offset;\n  unit = new Uint8Array(length);\n  unit.set(data.subarray(offset, data.length), 0);\n  const sample = {\n    unit,\n    pts: stamp\n  };\n  return {\n    sample,\n    length,\n    missing: -1\n  };\n}\n\n/**\n * AAC demuxer\n */\nclass AACDemuxer extends BaseAudioDemuxer {\n  constructor(observer, config) {\n    super();\n    this.observer = void 0;\n    this.config = void 0;\n    this.observer = observer;\n    this.config = config;\n  }\n  resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) {\n    super.resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration);\n    this._audioTrack = {\n      container: 'audio/adts',\n      type: 'audio',\n      id: 2,\n      pid: -1,\n      sequenceNumber: 0,\n      segmentCodec: 'aac',\n      samples: [],\n      manifestCodec: audioCodec,\n      duration: trackDuration,\n      inputTimeScale: 90000,\n      dropped: 0\n    };\n  }\n\n  // Source for probe info - https://wiki.multimedia.cx/index.php?title=ADTS\n  static probe(data) {\n    if (!data) {\n      return false;\n    }\n\n    // Check for the ADTS sync word\n    // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1\n    // Layer bits (position 14 and 15) in header should be always 0 for ADTS\n    // More info https://wiki.multimedia.cx/index.php?title=ADTS\n    const id3Data = getID3Data(data, 0) || [];\n    let offset = id3Data.length;\n    for (let length = data.length; offset < length; offset++) {\n      if (probe$1(data, offset)) {\n        logger.log('ADTS sync word found !');\n        return true;\n      }\n    }\n    return false;\n  }\n  canParse(data, offset) {\n    return canParse$1(data, offset);\n  }\n  appendFrame(track, data, offset) {\n    initTrackConfig(track, this.observer, data, offset, track.manifestCodec);\n    const frame = appendFrame$1(track, data, offset, this.basePTS, this.frameIndex);\n    if (frame && frame.missing === 0) {\n      return frame;\n    }\n  }\n}\n\nconst emsgSchemePattern = /\\/emsg[-/]ID3/i;\nclass MP4Demuxer {\n  constructor(observer, config) {\n    this.remainderData = null;\n    this.timeOffset = 0;\n    this.config = void 0;\n    this.videoTrack = void 0;\n    this.audioTrack = void 0;\n    this.id3Track = void 0;\n    this.txtTrack = void 0;\n    this.config = config;\n  }\n  resetTimeStamp() {}\n  resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) {\n    const videoTrack = this.videoTrack = dummyTrack('video', 1);\n    const audioTrack = this.audioTrack = dummyTrack('audio', 1);\n    const captionTrack = this.txtTrack = dummyTrack('text', 1);\n    this.id3Track = dummyTrack('id3', 1);\n    this.timeOffset = 0;\n    if (!(initSegment != null && initSegment.byteLength)) {\n      return;\n    }\n    const initData = parseInitSegment(initSegment);\n    if (initData.video) {\n      const {\n        id,\n        timescale,\n        codec\n      } = initData.video;\n      videoTrack.id = id;\n      videoTrack.timescale = captionTrack.timescale = timescale;\n      videoTrack.codec = codec;\n    }\n    if (initData.audio) {\n      const {\n        id,\n        timescale,\n        codec\n      } = initData.audio;\n      audioTrack.id = id;\n      audioTrack.timescale = timescale;\n      audioTrack.codec = codec;\n    }\n    captionTrack.id = RemuxerTrackIdConfig.text;\n    videoTrack.sampleDuration = 0;\n    videoTrack.duration = audioTrack.duration = trackDuration;\n  }\n  resetContiguity() {\n    this.remainderData = null;\n  }\n  static probe(data) {\n    // ensure we find a moof box in the first 16 kB\n    data = data.length > 16384 ? data.subarray(0, 16384) : data;\n    return findBox(data, ['moof']).length > 0;\n  }\n  demux(data, timeOffset) {\n    this.timeOffset = timeOffset;\n    // Load all data into the avc track. The CMAF remuxer will look for the data in the samples object; the rest of the fields do not matter\n    let videoSamples = data;\n    const videoTrack = this.videoTrack;\n    const textTrack = this.txtTrack;\n    if (this.config.progressive) {\n      // Split the bytestream into two ranges: one encompassing all data up until the start of the last moof, and everything else.\n      // This is done to guarantee that we're sending valid data to MSE - when demuxing progressively, we have no guarantee\n      // that the fetch loader gives us flush moof+mdat pairs. If we push jagged data to MSE, it will throw an exception.\n      if (this.remainderData) {\n        videoSamples = appendUint8Array(this.remainderData, data);\n      }\n      const segmentedData = segmentValidRange(videoSamples);\n      this.remainderData = segmentedData.remainder;\n      videoTrack.samples = segmentedData.valid || new Uint8Array();\n    } else {\n      videoTrack.samples = videoSamples;\n    }\n    const id3Track = this.extractID3Track(videoTrack, timeOffset);\n    textTrack.samples = parseSamples(timeOffset, videoTrack);\n    return {\n      videoTrack,\n      audioTrack: this.audioTrack,\n      id3Track,\n      textTrack: this.txtTrack\n    };\n  }\n  flush() {\n    const timeOffset = this.timeOffset;\n    const videoTrack = this.videoTrack;\n    const textTrack = this.txtTrack;\n    videoTrack.samples = this.remainderData || new Uint8Array();\n    this.remainderData = null;\n    const id3Track = this.extractID3Track(videoTrack, this.timeOffset);\n    textTrack.samples = parseSamples(timeOffset, videoTrack);\n    return {\n      videoTrack,\n      audioTrack: dummyTrack(),\n      id3Track,\n      textTrack: dummyTrack()\n    };\n  }\n  extractID3Track(videoTrack, timeOffset) {\n    const id3Track = this.id3Track;\n    if (videoTrack.samples.length) {\n      const emsgs = findBox(videoTrack.samples, ['emsg']);\n      if (emsgs) {\n        emsgs.forEach(data => {\n          const emsgInfo = parseEmsg(data);\n          if (emsgSchemePattern.test(emsgInfo.schemeIdUri)) {\n            const pts = isFiniteNumber(emsgInfo.presentationTime) ? emsgInfo.presentationTime / emsgInfo.timeScale : timeOffset + emsgInfo.presentationTimeDelta / emsgInfo.timeScale;\n            let duration = emsgInfo.eventDuration === 0xffffffff ? Number.POSITIVE_INFINITY : emsgInfo.eventDuration / emsgInfo.timeScale;\n            // Safari takes anything <= 0.001 seconds and maps it to Infinity\n            if (duration <= 0.001) {\n              duration = Number.POSITIVE_INFINITY;\n            }\n            const payload = emsgInfo.payload;\n            id3Track.samples.push({\n              data: payload,\n              len: payload.byteLength,\n              dts: pts,\n              pts: pts,\n              type: MetadataSchema.emsg,\n              duration: duration\n            });\n          }\n        });\n      }\n    }\n    return id3Track;\n  }\n  demuxSampleAes(data, keyData, timeOffset) {\n    return Promise.reject(new Error('The MP4 demuxer does not support SAMPLE-AES decryption'));\n  }\n  destroy() {}\n}\n\n/**\n *  MPEG parser helper\n */\n\nlet chromeVersion$1 = null;\nconst BitratesMap = [32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160];\nconst SamplingRateMap = [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000];\nconst SamplesCoefficients = [\n// MPEG 2.5\n[0,\n// Reserved\n72,\n// Layer3\n144,\n// Layer2\n12 // Layer1\n],\n// Reserved\n[0,\n// Reserved\n0,\n// Layer3\n0,\n// Layer2\n0 // Layer1\n],\n// MPEG 2\n[0,\n// Reserved\n72,\n// Layer3\n144,\n// Layer2\n12 // Layer1\n],\n// MPEG 1\n[0,\n// Reserved\n144,\n// Layer3\n144,\n// Layer2\n12 // Layer1\n]];\n\nconst BytesInSlot = [0,\n// Reserved\n1,\n// Layer3\n1,\n// Layer2\n4 // Layer1\n];\n\nfunction appendFrame(track, data, offset, pts, frameIndex) {\n  // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference\n  if (offset + 24 > data.length) {\n    return;\n  }\n  const header = parseHeader(data, offset);\n  if (header && offset + header.frameLength <= data.length) {\n    const frameDuration = header.samplesPerFrame * 90000 / header.sampleRate;\n    const stamp = pts + frameIndex * frameDuration;\n    const sample = {\n      unit: data.subarray(offset, offset + header.frameLength),\n      pts: stamp,\n      dts: stamp\n    };\n    track.config = [];\n    track.channelCount = header.channelCount;\n    track.samplerate = header.sampleRate;\n    track.samples.push(sample);\n    return {\n      sample,\n      length: header.frameLength,\n      missing: 0\n    };\n  }\n}\nfunction parseHeader(data, offset) {\n  const mpegVersion = data[offset + 1] >> 3 & 3;\n  const mpegLayer = data[offset + 1] >> 1 & 3;\n  const bitRateIndex = data[offset + 2] >> 4 & 15;\n  const sampleRateIndex = data[offset + 2] >> 2 & 3;\n  if (mpegVersion !== 1 && bitRateIndex !== 0 && bitRateIndex !== 15 && sampleRateIndex !== 3) {\n    const paddingBit = data[offset + 2] >> 1 & 1;\n    const channelMode = data[offset + 3] >> 6;\n    const columnInBitrates = mpegVersion === 3 ? 3 - mpegLayer : mpegLayer === 3 ? 3 : 4;\n    const bitRate = BitratesMap[columnInBitrates * 14 + bitRateIndex - 1] * 1000;\n    const columnInSampleRates = mpegVersion === 3 ? 0 : mpegVersion === 2 ? 1 : 2;\n    const sampleRate = SamplingRateMap[columnInSampleRates * 3 + sampleRateIndex];\n    const channelCount = channelMode === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono)\n    const sampleCoefficient = SamplesCoefficients[mpegVersion][mpegLayer];\n    const bytesInSlot = BytesInSlot[mpegLayer];\n    const samplesPerFrame = sampleCoefficient * 8 * bytesInSlot;\n    const frameLength = Math.floor(sampleCoefficient * bitRate / sampleRate + paddingBit) * bytesInSlot;\n    if (chromeVersion$1 === null) {\n      const userAgent = navigator.userAgent || '';\n      const result = userAgent.match(/Chrome\\/(\\d+)/i);\n      chromeVersion$1 = result ? parseInt(result[1]) : 0;\n    }\n    const needChromeFix = !!chromeVersion$1 && chromeVersion$1 <= 87;\n    if (needChromeFix && mpegLayer === 2 && bitRate >= 224000 && channelMode === 0) {\n      // Work around bug in Chromium by setting channelMode to dual-channel (01) instead of stereo (00)\n      data[offset + 3] = data[offset + 3] | 0x80;\n    }\n    return {\n      sampleRate,\n      channelCount,\n      frameLength,\n      samplesPerFrame\n    };\n  }\n}\nfunction isHeaderPattern(data, offset) {\n  return data[offset] === 0xff && (data[offset + 1] & 0xe0) === 0xe0 && (data[offset + 1] & 0x06) !== 0x00;\n}\nfunction isHeader(data, offset) {\n  // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1\n  // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III)\n  // More info http://www.mp3-tech.org/programmer/frame_header.html\n  return offset + 1 < data.length && isHeaderPattern(data, offset);\n}\nfunction canParse(data, offset) {\n  const headerSize = 4;\n  return isHeaderPattern(data, offset) && headerSize <= data.length - offset;\n}\nfunction probe(data, offset) {\n  // same as isHeader but we also check that MPEG frame follows last MPEG frame\n  // or end of data is reached\n  if (offset + 1 < data.length && isHeaderPattern(data, offset)) {\n    // MPEG header Length\n    const headerLength = 4;\n    // MPEG frame Length\n    const header = parseHeader(data, offset);\n    let frameLength = headerLength;\n    if (header != null && header.frameLength) {\n      frameLength = header.frameLength;\n    }\n    const newOffset = offset + frameLength;\n    return newOffset === data.length || isHeader(data, newOffset);\n  }\n  return false;\n}\n\n/**\n * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.\n */\n\nclass ExpGolomb {\n  constructor(data) {\n    this.data = void 0;\n    this.bytesAvailable = void 0;\n    this.word = void 0;\n    this.bitsAvailable = void 0;\n    this.data = data;\n    // the number of bytes left to examine in this.data\n    this.bytesAvailable = data.byteLength;\n    // the current word being examined\n    this.word = 0; // :uint\n    // the number of bits left to examine in the current word\n    this.bitsAvailable = 0; // :uint\n  }\n\n  // ():void\n  loadWord() {\n    const data = this.data;\n    const bytesAvailable = this.bytesAvailable;\n    const position = data.byteLength - bytesAvailable;\n    const workingBytes = new Uint8Array(4);\n    const availableBytes = Math.min(4, bytesAvailable);\n    if (availableBytes === 0) {\n      throw new Error('no bytes available');\n    }\n    workingBytes.set(data.subarray(position, position + availableBytes));\n    this.word = new DataView(workingBytes.buffer).getUint32(0);\n    // track the amount of this.data that has been processed\n    this.bitsAvailable = availableBytes * 8;\n    this.bytesAvailable -= availableBytes;\n  }\n\n  // (count:int):void\n  skipBits(count) {\n    let skipBytes; // :int\n    count = Math.min(count, this.bytesAvailable * 8 + this.bitsAvailable);\n    if (this.bitsAvailable > count) {\n      this.word <<= count;\n      this.bitsAvailable -= count;\n    } else {\n      count -= this.bitsAvailable;\n      skipBytes = count >> 3;\n      count -= skipBytes << 3;\n      this.bytesAvailable -= skipBytes;\n      this.loadWord();\n      this.word <<= count;\n      this.bitsAvailable -= count;\n    }\n  }\n\n  // (size:int):uint\n  readBits(size) {\n    let bits = Math.min(this.bitsAvailable, size); // :uint\n    const valu = this.word >>> 32 - bits; // :uint\n    if (size > 32) {\n      logger.error('Cannot read more than 32 bits at a time');\n    }\n    this.bitsAvailable -= bits;\n    if (this.bitsAvailable > 0) {\n      this.word <<= bits;\n    } else if (this.bytesAvailable > 0) {\n      this.loadWord();\n    } else {\n      throw new Error('no bits available');\n    }\n    bits = size - bits;\n    if (bits > 0 && this.bitsAvailable) {\n      return valu << bits | this.readBits(bits);\n    } else {\n      return valu;\n    }\n  }\n\n  // ():uint\n  skipLZ() {\n    let leadingZeroCount; // :uint\n    for (leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) {\n      if ((this.word & 0x80000000 >>> leadingZeroCount) !== 0) {\n        // the first bit of working word is 1\n        this.word <<= leadingZeroCount;\n        this.bitsAvailable -= leadingZeroCount;\n        return leadingZeroCount;\n      }\n    }\n    // we exhausted word and still have not found a 1\n    this.loadWord();\n    return leadingZeroCount + this.skipLZ();\n  }\n\n  // ():void\n  skipUEG() {\n    this.skipBits(1 + this.skipLZ());\n  }\n\n  // ():void\n  skipEG() {\n    this.skipBits(1 + this.skipLZ());\n  }\n\n  // ():uint\n  readUEG() {\n    const clz = this.skipLZ(); // :uint\n    return this.readBits(clz + 1) - 1;\n  }\n\n  // ():int\n  readEG() {\n    const valu = this.readUEG(); // :int\n    if (0x01 & valu) {\n      // the number is odd if the low order bit is set\n      return 1 + valu >>> 1; // add 1 to make it even, and divide by 2\n    } else {\n      return -1 * (valu >>> 1); // divide by two then make it negative\n    }\n  }\n\n  // Some convenience functions\n  // :Boolean\n  readBoolean() {\n    return this.readBits(1) === 1;\n  }\n\n  // ():int\n  readUByte() {\n    return this.readBits(8);\n  }\n\n  // ():int\n  readUShort() {\n    return this.readBits(16);\n  }\n\n  // ():int\n  readUInt() {\n    return this.readBits(32);\n  }\n\n  /**\n   * Advance the ExpGolomb decoder past a scaling list. The scaling\n   * list is optionally transmitted as part of a sequence parameter\n   * set and is not relevant to transmuxing.\n   * @param count the number of entries in this scaling list\n   * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1\n   */\n  skipScalingList(count) {\n    let lastScale = 8;\n    let nextScale = 8;\n    let deltaScale;\n    for (let j = 0; j < count; j++) {\n      if (nextScale !== 0) {\n        deltaScale = this.readEG();\n        nextScale = (lastScale + deltaScale + 256) % 256;\n      }\n      lastScale = nextScale === 0 ? lastScale : nextScale;\n    }\n  }\n\n  /**\n   * Read a sequence parameter set and return some interesting video\n   * properties. A sequence parameter set is the H264 metadata that\n   * describes the properties of upcoming video frames.\n   * @returns an object with configuration parsed from the\n   * sequence parameter set, including the dimensions of the\n   * associated video frames.\n   */\n  readSPS() {\n    let frameCropLeftOffset = 0;\n    let frameCropRightOffset = 0;\n    let frameCropTopOffset = 0;\n    let frameCropBottomOffset = 0;\n    let numRefFramesInPicOrderCntCycle;\n    let scalingListCount;\n    let i;\n    const readUByte = this.readUByte.bind(this);\n    const readBits = this.readBits.bind(this);\n    const readUEG = this.readUEG.bind(this);\n    const readBoolean = this.readBoolean.bind(this);\n    const skipBits = this.skipBits.bind(this);\n    const skipEG = this.skipEG.bind(this);\n    const skipUEG = this.skipUEG.bind(this);\n    const skipScalingList = this.skipScalingList.bind(this);\n    readUByte();\n    const profileIdc = readUByte(); // profile_idc\n    readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)\n    skipBits(3); // reserved_zero_3bits u(3),\n    readUByte(); // level_idc u(8)\n    skipUEG(); // seq_parameter_set_id\n    // some profiles have more optional data we don't need\n    if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {\n      const chromaFormatIdc = readUEG();\n      if (chromaFormatIdc === 3) {\n        skipBits(1);\n      } // separate_colour_plane_flag\n\n      skipUEG(); // bit_depth_luma_minus8\n      skipUEG(); // bit_depth_chroma_minus8\n      skipBits(1); // qpprime_y_zero_transform_bypass_flag\n      if (readBoolean()) {\n        // seq_scaling_matrix_present_flag\n        scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;\n        for (i = 0; i < scalingListCount; i++) {\n          if (readBoolean()) {\n            // seq_scaling_list_present_flag[ i ]\n            if (i < 6) {\n              skipScalingList(16);\n            } else {\n              skipScalingList(64);\n            }\n          }\n        }\n      }\n    }\n    skipUEG(); // log2_max_frame_num_minus4\n    const picOrderCntType = readUEG();\n    if (picOrderCntType === 0) {\n      readUEG(); // log2_max_pic_order_cnt_lsb_minus4\n    } else if (picOrderCntType === 1) {\n      skipBits(1); // delta_pic_order_always_zero_flag\n      skipEG(); // offset_for_non_ref_pic\n      skipEG(); // offset_for_top_to_bottom_field\n      numRefFramesInPicOrderCntCycle = readUEG();\n      for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {\n        skipEG();\n      } // offset_for_ref_frame[ i ]\n    }\n\n    skipUEG(); // max_num_ref_frames\n    skipBits(1); // gaps_in_frame_num_value_allowed_flag\n    const picWidthInMbsMinus1 = readUEG();\n    const picHeightInMapUnitsMinus1 = readUEG();\n    const frameMbsOnlyFlag = readBits(1);\n    if (frameMbsOnlyFlag === 0) {\n      skipBits(1);\n    } // mb_adaptive_frame_field_flag\n\n    skipBits(1); // direct_8x8_inference_flag\n    if (readBoolean()) {\n      // frame_cropping_flag\n      frameCropLeftOffset = readUEG();\n      frameCropRightOffset = readUEG();\n      frameCropTopOffset = readUEG();\n      frameCropBottomOffset = readUEG();\n    }\n    let pixelRatio = [1, 1];\n    if (readBoolean()) {\n      // vui_parameters_present_flag\n      if (readBoolean()) {\n        // aspect_ratio_info_present_flag\n        const aspectRatioIdc = readUByte();\n        switch (aspectRatioIdc) {\n          case 1:\n            pixelRatio = [1, 1];\n            break;\n          case 2:\n            pixelRatio = [12, 11];\n            break;\n          case 3:\n            pixelRatio = [10, 11];\n            break;\n          case 4:\n            pixelRatio = [16, 11];\n            break;\n          case 5:\n            pixelRatio = [40, 33];\n            break;\n          case 6:\n            pixelRatio = [24, 11];\n            break;\n          case 7:\n            pixelRatio = [20, 11];\n            break;\n          case 8:\n            pixelRatio = [32, 11];\n            break;\n          case 9:\n            pixelRatio = [80, 33];\n            break;\n          case 10:\n            pixelRatio = [18, 11];\n            break;\n          case 11:\n            pixelRatio = [15, 11];\n            break;\n          case 12:\n            pixelRatio = [64, 33];\n            break;\n          case 13:\n            pixelRatio = [160, 99];\n            break;\n          case 14:\n            pixelRatio = [4, 3];\n            break;\n          case 15:\n            pixelRatio = [3, 2];\n            break;\n          case 16:\n            pixelRatio = [2, 1];\n            break;\n          case 255:\n            {\n              pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];\n              break;\n            }\n        }\n      }\n    }\n    return {\n      width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),\n      height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),\n      pixelRatio: pixelRatio\n    };\n  }\n  readSliceType() {\n    // skip NALu type\n    this.readUByte();\n    // discard first_mb_in_slice\n    this.readUEG();\n    // return slice_type\n    return this.readUEG();\n  }\n}\n\n/**\n * SAMPLE-AES decrypter\n */\n\nclass SampleAesDecrypter {\n  constructor(observer, config, keyData) {\n    this.keyData = void 0;\n    this.decrypter = void 0;\n    this.keyData = keyData;\n    this.decrypter = new Decrypter(config, {\n      removePKCS7Padding: false\n    });\n  }\n  decryptBuffer(encryptedData) {\n    return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer);\n  }\n\n  // AAC - encrypt all full 16 bytes blocks starting from offset 16\n  decryptAacSample(samples, sampleIndex, callback) {\n    const curUnit = samples[sampleIndex].unit;\n    if (curUnit.length <= 16) {\n      // No encrypted portion in this sample (first 16 bytes is not\n      // encrypted, see https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption/Encryption/Encryption.html),\n      return;\n    }\n    const encryptedData = curUnit.subarray(16, curUnit.length - curUnit.length % 16);\n    const encryptedBuffer = encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length);\n    this.decryptBuffer(encryptedBuffer).then(decryptedBuffer => {\n      const decryptedData = new Uint8Array(decryptedBuffer);\n      curUnit.set(decryptedData, 16);\n      if (!this.decrypter.isSync()) {\n        this.decryptAacSamples(samples, sampleIndex + 1, callback);\n      }\n    });\n  }\n  decryptAacSamples(samples, sampleIndex, callback) {\n    for (;; sampleIndex++) {\n      if (sampleIndex >= samples.length) {\n        callback();\n        return;\n      }\n      if (samples[sampleIndex].unit.length < 32) {\n        continue;\n      }\n      this.decryptAacSample(samples, sampleIndex, callback);\n      if (!this.decrypter.isSync()) {\n        return;\n      }\n    }\n  }\n\n  // AVC - encrypt one 16 bytes block out of ten, starting from offset 32\n  getAvcEncryptedData(decodedData) {\n    const encryptedDataLen = Math.floor((decodedData.length - 48) / 160) * 16 + 16;\n    const encryptedData = new Int8Array(encryptedDataLen);\n    let outputPos = 0;\n    for (let inputPos = 32; inputPos < decodedData.length - 16; inputPos += 160, outputPos += 16) {\n      encryptedData.set(decodedData.subarray(inputPos, inputPos + 16), outputPos);\n    }\n    return encryptedData;\n  }\n  getAvcDecryptedUnit(decodedData, decryptedData) {\n    const uint8DecryptedData = new Uint8Array(decryptedData);\n    let inputPos = 0;\n    for (let outputPos = 32; outputPos < decodedData.length - 16; outputPos += 160, inputPos += 16) {\n      decodedData.set(uint8DecryptedData.subarray(inputPos, inputPos + 16), outputPos);\n    }\n    return decodedData;\n  }\n  decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit) {\n    const decodedData = discardEPB(curUnit.data);\n    const encryptedData = this.getAvcEncryptedData(decodedData);\n    this.decryptBuffer(encryptedData.buffer).then(decryptedBuffer => {\n      curUnit.data = this.getAvcDecryptedUnit(decodedData, decryptedBuffer);\n      if (!this.decrypter.isSync()) {\n        this.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback);\n      }\n    });\n  }\n  decryptAvcSamples(samples, sampleIndex, unitIndex, callback) {\n    if (samples instanceof Uint8Array) {\n      throw new Error('Cannot decrypt samples of type Uint8Array');\n    }\n    for (;; sampleIndex++, unitIndex = 0) {\n      if (sampleIndex >= samples.length) {\n        callback();\n        return;\n      }\n      const curUnits = samples[sampleIndex].units;\n      for (;; unitIndex++) {\n        if (unitIndex >= curUnits.length) {\n          break;\n        }\n        const curUnit = curUnits[unitIndex];\n        if (curUnit.data.length <= 48 || curUnit.type !== 1 && curUnit.type !== 5) {\n          continue;\n        }\n        this.decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit);\n        if (!this.decrypter.isSync()) {\n          return;\n        }\n      }\n    }\n  }\n}\n\nconst PACKET_LENGTH = 188;\nclass TSDemuxer {\n  constructor(observer, config, typeSupported) {\n    this.observer = void 0;\n    this.config = void 0;\n    this.typeSupported = void 0;\n    this.sampleAes = null;\n    this.pmtParsed = false;\n    this.audioCodec = void 0;\n    this.videoCodec = void 0;\n    this._duration = 0;\n    this._pmtId = -1;\n    this._avcTrack = void 0;\n    this._audioTrack = void 0;\n    this._id3Track = void 0;\n    this._txtTrack = void 0;\n    this.aacOverFlow = null;\n    this.avcSample = null;\n    this.remainderData = null;\n    this.observer = observer;\n    this.config = config;\n    this.typeSupported = typeSupported;\n  }\n  static probe(data) {\n    const syncOffset = TSDemuxer.syncOffset(data);\n    if (syncOffset > 0) {\n      logger.warn(`MPEG2-TS detected but first sync word found @ offset ${syncOffset}`);\n    }\n    return syncOffset !== -1;\n  }\n  static syncOffset(data) {\n    const length = data.length;\n    let scanwindow = Math.min(PACKET_LENGTH * 5, data.length - PACKET_LENGTH) + 1;\n    let i = 0;\n    while (i < scanwindow) {\n      // a TS init segment should contain at least 2 TS packets: PAT and PMT, each starting with 0x47\n      let foundPat = false;\n      let packetStart = -1;\n      let tsPackets = 0;\n      for (let j = i; j < length; j += PACKET_LENGTH) {\n        if (data[j] === 0x47) {\n          tsPackets++;\n          if (packetStart === -1) {\n            packetStart = j;\n            // First sync word found at offset, increase scan length (#5251)\n            if (packetStart !== 0) {\n              scanwindow = Math.min(packetStart + PACKET_LENGTH * 99, data.length - PACKET_LENGTH) + 1;\n            }\n          }\n          if (!foundPat) {\n            foundPat = parsePID(data, j) === 0;\n          }\n          // Sync word found at 0 with 3 packets, or found at offset least 2 packets up to scanwindow (#5501)\n          if (foundPat && tsPackets > 1 && (packetStart === 0 && tsPackets > 2 || j + PACKET_LENGTH > scanwindow)) {\n            return packetStart;\n          }\n        } else if (tsPackets) {\n          // Exit if sync word found, but does not contain contiguous packets (#5501)\n          return -1;\n        } else {\n          break;\n        }\n      }\n      i++;\n    }\n    return -1;\n  }\n\n  /**\n   * Creates a track model internal to demuxer used to drive remuxing input\n   */\n  static createTrack(type, duration) {\n    return {\n      container: type === 'video' || type === 'audio' ? 'video/mp2t' : undefined,\n      type,\n      id: RemuxerTrackIdConfig[type],\n      pid: -1,\n      inputTimeScale: 90000,\n      sequenceNumber: 0,\n      samples: [],\n      dropped: 0,\n      duration: type === 'audio' ? duration : undefined\n    };\n  }\n\n  /**\n   * Initializes a new init segment on the demuxer/remuxer interface. Needed for discontinuities/track-switches (or at stream start)\n   * Resets all internal track instances of the demuxer.\n   */\n  resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) {\n    this.pmtParsed = false;\n    this._pmtId = -1;\n    this._avcTrack = TSDemuxer.createTrack('video');\n    this._audioTrack = TSDemuxer.createTrack('audio', trackDuration);\n    this._id3Track = TSDemuxer.createTrack('id3');\n    this._txtTrack = TSDemuxer.createTrack('text');\n    this._audioTrack.segmentCodec = 'aac';\n\n    // flush any partial content\n    this.aacOverFlow = null;\n    this.avcSample = null;\n    this.remainderData = null;\n    this.audioCodec = audioCodec;\n    this.videoCodec = videoCodec;\n    this._duration = trackDuration;\n  }\n  resetTimeStamp() {}\n  resetContiguity() {\n    const {\n      _audioTrack,\n      _avcTrack,\n      _id3Track\n    } = this;\n    if (_audioTrack) {\n      _audioTrack.pesData = null;\n    }\n    if (_avcTrack) {\n      _avcTrack.pesData = null;\n    }\n    if (_id3Track) {\n      _id3Track.pesData = null;\n    }\n    this.aacOverFlow = null;\n    this.avcSample = null;\n    this.remainderData = null;\n  }\n  demux(data, timeOffset, isSampleAes = false, flush = false) {\n    if (!isSampleAes) {\n      this.sampleAes = null;\n    }\n    let pes;\n    const videoTrack = this._avcTrack;\n    const audioTrack = this._audioTrack;\n    const id3Track = this._id3Track;\n    const textTrack = this._txtTrack;\n    let avcId = videoTrack.pid;\n    let avcData = videoTrack.pesData;\n    let audioId = audioTrack.pid;\n    let id3Id = id3Track.pid;\n    let audioData = audioTrack.pesData;\n    let id3Data = id3Track.pesData;\n    let unknownPID = null;\n    let pmtParsed = this.pmtParsed;\n    let pmtId = this._pmtId;\n    let len = data.length;\n    if (this.remainderData) {\n      data = appendUint8Array(this.remainderData, data);\n      len = data.length;\n      this.remainderData = null;\n    }\n    if (len < PACKET_LENGTH && !flush) {\n      this.remainderData = data;\n      return {\n        audioTrack,\n        videoTrack,\n        id3Track,\n        textTrack\n      };\n    }\n    const syncOffset = Math.max(0, TSDemuxer.syncOffset(data));\n    len -= (len - syncOffset) % PACKET_LENGTH;\n    if (len < data.byteLength && !flush) {\n      this.remainderData = new Uint8Array(data.buffer, len, data.buffer.byteLength - len);\n    }\n\n    // loop through TS packets\n    let tsPacketErrors = 0;\n    for (let start = syncOffset; start < len; start += PACKET_LENGTH) {\n      if (data[start] === 0x47) {\n        const stt = !!(data[start + 1] & 0x40);\n        const pid = parsePID(data, start);\n        const atf = (data[start + 3] & 0x30) >> 4;\n\n        // if an adaption field is present, its length is specified by the fifth byte of the TS packet header.\n        let offset;\n        if (atf > 1) {\n          offset = start + 5 + data[start + 4];\n          // continue if there is only adaptation field\n          if (offset === start + PACKET_LENGTH) {\n            continue;\n          }\n        } else {\n          offset = start + 4;\n        }\n        switch (pid) {\n          case avcId:\n            if (stt) {\n              if (avcData && (pes = parsePES(avcData))) {\n                this.parseAVCPES(videoTrack, textTrack, pes, false);\n              }\n              avcData = {\n                data: [],\n                size: 0\n              };\n            }\n            if (avcData) {\n              avcData.data.push(data.subarray(offset, start + PACKET_LENGTH));\n              avcData.size += start + PACKET_LENGTH - offset;\n            }\n            break;\n          case audioId:\n            if (stt) {\n              if (audioData && (pes = parsePES(audioData))) {\n                switch (audioTrack.segmentCodec) {\n                  case 'aac':\n                    this.parseAACPES(audioTrack, pes);\n                    break;\n                  case 'mp3':\n                    this.parseMPEGPES(audioTrack, pes);\n                    break;\n                }\n              }\n              audioData = {\n                data: [],\n                size: 0\n              };\n            }\n            if (audioData) {\n              audioData.data.push(data.subarray(offset, start + PACKET_LENGTH));\n              audioData.size += start + PACKET_LENGTH - offset;\n            }\n            break;\n          case id3Id:\n            if (stt) {\n              if (id3Data && (pes = parsePES(id3Data))) {\n                this.parseID3PES(id3Track, pes);\n              }\n              id3Data = {\n                data: [],\n                size: 0\n              };\n            }\n            if (id3Data) {\n              id3Data.data.push(data.subarray(offset, start + PACKET_LENGTH));\n              id3Data.size += start + PACKET_LENGTH - offset;\n            }\n            break;\n          case 0:\n            if (stt) {\n              offset += data[offset] + 1;\n            }\n            pmtId = this._pmtId = parsePAT(data, offset);\n            // logger.log('PMT PID:'  + this._pmtId);\n            break;\n          case pmtId:\n            {\n              if (stt) {\n                offset += data[offset] + 1;\n              }\n              const parsedPIDs = parsePMT(data, offset, this.typeSupported, isSampleAes);\n\n              // only update track id if track PID found while parsing PMT\n              // this is to avoid resetting the PID to -1 in case\n              // track PID transiently disappears from the stream\n              // this could happen in case of transient missing audio samples for example\n              // NOTE this is only the PID of the track as found in TS,\n              // but we are not using this for MP4 track IDs.\n              avcId = parsedPIDs.avc;\n              if (avcId > 0) {\n                videoTrack.pid = avcId;\n              }\n              audioId = parsedPIDs.audio;\n              if (audioId > 0) {\n                audioTrack.pid = audioId;\n                audioTrack.segmentCodec = parsedPIDs.segmentCodec;\n              }\n              id3Id = parsedPIDs.id3;\n              if (id3Id > 0) {\n                id3Track.pid = id3Id;\n              }\n              if (unknownPID !== null && !pmtParsed) {\n                logger.warn(`MPEG-TS PMT found at ${start} after unknown PID '${unknownPID}'. Backtracking to sync byte @${syncOffset} to parse all TS packets.`);\n                unknownPID = null;\n                // we set it to -188, the += 188 in the for loop will reset start to 0\n                start = syncOffset - 188;\n              }\n              pmtParsed = this.pmtParsed = true;\n              break;\n            }\n          case 0x11:\n          case 0x1fff:\n            break;\n          default:\n            unknownPID = pid;\n            break;\n        }\n      } else {\n        tsPacketErrors++;\n      }\n    }\n    if (tsPacketErrors > 0) {\n      const error = new Error(`Found ${tsPacketErrors} TS packet/s that do not start with 0x47`);\n      this.observer.emit(Events.ERROR, Events.ERROR, {\n        type: ErrorTypes.MEDIA_ERROR,\n        details: ErrorDetails.FRAG_PARSING_ERROR,\n        fatal: false,\n        error,\n        reason: error.message\n      });\n    }\n    videoTrack.pesData = avcData;\n    audioTrack.pesData = audioData;\n    id3Track.pesData = id3Data;\n    const demuxResult = {\n      audioTrack,\n      videoTrack,\n      id3Track,\n      textTrack\n    };\n    if (flush) {\n      this.extractRemainingSamples(demuxResult);\n    }\n    return demuxResult;\n  }\n  flush() {\n    const {\n      remainderData\n    } = this;\n    this.remainderData = null;\n    let result;\n    if (remainderData) {\n      result = this.demux(remainderData, -1, false, true);\n    } else {\n      result = {\n        videoTrack: this._avcTrack,\n        audioTrack: this._audioTrack,\n        id3Track: this._id3Track,\n        textTrack: this._txtTrack\n      };\n    }\n    this.extractRemainingSamples(result);\n    if (this.sampleAes) {\n      return this.decrypt(result, this.sampleAes);\n    }\n    return result;\n  }\n  extractRemainingSamples(demuxResult) {\n    const {\n      audioTrack,\n      videoTrack,\n      id3Track,\n      textTrack\n    } = demuxResult;\n    const avcData = videoTrack.pesData;\n    const audioData = audioTrack.pesData;\n    const id3Data = id3Track.pesData;\n    // try to parse last PES packets\n    let pes;\n    if (avcData && (pes = parsePES(avcData))) {\n      this.parseAVCPES(videoTrack, textTrack, pes, true);\n      videoTrack.pesData = null;\n    } else {\n      // either avcData null or PES truncated, keep it for next frag parsing\n      videoTrack.pesData = avcData;\n    }\n    if (audioData && (pes = parsePES(audioData))) {\n      switch (audioTrack.segmentCodec) {\n        case 'aac':\n          this.parseAACPES(audioTrack, pes);\n          break;\n        case 'mp3':\n          this.parseMPEGPES(audioTrack, pes);\n          break;\n      }\n      audioTrack.pesData = null;\n    } else {\n      if (audioData != null && audioData.size) {\n        logger.log('last AAC PES packet truncated,might overlap between fragments');\n      }\n\n      // either audioData null or PES truncated, keep it for next frag parsing\n      audioTrack.pesData = audioData;\n    }\n    if (id3Data && (pes = parsePES(id3Data))) {\n      this.parseID3PES(id3Track, pes);\n      id3Track.pesData = null;\n    } else {\n      // either id3Data null or PES truncated, keep it for next frag parsing\n      id3Track.pesData = id3Data;\n    }\n  }\n  demuxSampleAes(data, keyData, timeOffset) {\n    const demuxResult = this.demux(data, timeOffset, true, !this.config.progressive);\n    const sampleAes = this.sampleAes = new SampleAesDecrypter(this.observer, this.config, keyData);\n    return this.decrypt(demuxResult, sampleAes);\n  }\n  decrypt(demuxResult, sampleAes) {\n    return new Promise(resolve => {\n      const {\n        audioTrack,\n        videoTrack\n      } = demuxResult;\n      if (audioTrack.samples && audioTrack.segmentCodec === 'aac') {\n        sampleAes.decryptAacSamples(audioTrack.samples, 0, () => {\n          if (videoTrack.samples) {\n            sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, () => {\n              resolve(demuxResult);\n            });\n          } else {\n            resolve(demuxResult);\n          }\n        });\n      } else if (videoTrack.samples) {\n        sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, () => {\n          resolve(demuxResult);\n        });\n      }\n    });\n  }\n  destroy() {\n    this._duration = 0;\n  }\n  parseAVCPES(track, textTrack, pes, last) {\n    const units = this.parseAVCNALu(track, pes.data);\n    let avcSample = this.avcSample;\n    let push;\n    let spsfound = false;\n    // free pes.data to save up some memory\n    pes.data = null;\n\n    // if new NAL units found and last sample still there, let's push ...\n    // this helps parsing streams with missing AUD (only do this if AUD never found)\n    if (avcSample && units.length && !track.audFound) {\n      pushAccessUnit(avcSample, track);\n      avcSample = this.avcSample = createAVCSample(false, pes.pts, pes.dts, '');\n    }\n    units.forEach(unit => {\n      var _avcSample2;\n      switch (unit.type) {\n        // NDR\n        case 1:\n          {\n            let iskey = false;\n            push = true;\n            const data = unit.data;\n            // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)\n            if (spsfound && data.length > 4) {\n              // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR\n              const sliceType = new ExpGolomb(data).readSliceType();\n              // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice\n              // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.\n              // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.\n              // I slice: A slice that is not an SI slice that is decoded using intra prediction only.\n              // if (sliceType === 2 || sliceType === 7) {\n              if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) {\n                iskey = true;\n              }\n            }\n            if (iskey) {\n              var _avcSample;\n              // if we have non-keyframe data already, that cannot belong to the same frame as a keyframe, so force a push\n              if ((_avcSample = avcSample) != null && _avcSample.frame && !avcSample.key) {\n                pushAccessUnit(avcSample, track);\n                avcSample = this.avcSample = null;\n              }\n            }\n            if (!avcSample) {\n              avcSample = this.avcSample = createAVCSample(true, pes.pts, pes.dts, '');\n            }\n            avcSample.frame = true;\n            avcSample.key = iskey;\n            break;\n            // IDR\n          }\n\n        case 5:\n          push = true;\n          // handle PES not starting with AUD\n          // if we have non-keyframe data already, that cannot belong to the same frame as a keyframe, so force a push\n          if ((_avcSample2 = avcSample) != null && _avcSample2.frame && !avcSample.key) {\n            pushAccessUnit(avcSample, track);\n            avcSample = this.avcSample = null;\n          }\n          if (!avcSample) {\n            avcSample = this.avcSample = createAVCSample(true, pes.pts, pes.dts, '');\n          }\n          avcSample.key = true;\n          avcSample.frame = true;\n          break;\n        // SEI\n        case 6:\n          {\n            push = true;\n            parseSEIMessageFromNALu(unit.data, 1, pes.pts, textTrack.samples);\n            break;\n            // SPS\n          }\n\n        case 7:\n          push = true;\n          spsfound = true;\n          if (!track.sps) {\n            const sps = unit.data;\n            const expGolombDecoder = new ExpGolomb(sps);\n            const config = expGolombDecoder.readSPS();\n            track.width = config.width;\n            track.height = config.height;\n            track.pixelRatio = config.pixelRatio;\n            track.sps = [sps];\n            track.duration = this._duration;\n            const codecarray = sps.subarray(1, 4);\n            let codecstring = 'avc1.';\n            for (let i = 0; i < 3; i++) {\n              let h = codecarray[i].toString(16);\n              if (h.length < 2) {\n                h = '0' + h;\n              }\n              codecstring += h;\n            }\n            track.codec = codecstring;\n          }\n          break;\n        // PPS\n        case 8:\n          push = true;\n          if (!track.pps) {\n            track.pps = [unit.data];\n          }\n          break;\n        // AUD\n        case 9:\n          push = false;\n          track.audFound = true;\n          if (avcSample) {\n            pushAccessUnit(avcSample, track);\n          }\n          avcSample = this.avcSample = createAVCSample(false, pes.pts, pes.dts, '');\n          break;\n        // Filler Data\n        case 12:\n          push = true;\n          break;\n        default:\n          push = false;\n          if (avcSample) {\n            avcSample.debug += 'unknown NAL ' + unit.type + ' ';\n          }\n          break;\n      }\n      if (avcSample && push) {\n        const units = avcSample.units;\n        units.push(unit);\n      }\n    });\n    // if last PES packet, push samples\n    if (last && avcSample) {\n      pushAccessUnit(avcSample, track);\n      this.avcSample = null;\n    }\n  }\n  getLastNalUnit(samples) {\n    var _avcSample3;\n    let avcSample = this.avcSample;\n    let lastUnit;\n    // try to fallback to previous sample if current one is empty\n    if (!avcSample || avcSample.units.length === 0) {\n      avcSample = samples[samples.length - 1];\n    }\n    if ((_avcSample3 = avcSample) != null && _avcSample3.units) {\n      const units = avcSample.units;\n      lastUnit = units[units.length - 1];\n    }\n    return lastUnit;\n  }\n  parseAVCNALu(track, array) {\n    const len = array.byteLength;\n    let state = track.naluState || 0;\n    const lastState = state;\n    const units = [];\n    let i = 0;\n    let value;\n    let overflow;\n    let unitType;\n    let lastUnitStart = -1;\n    let lastUnitType = 0;\n    // logger.log('PES:' + Hex.hexDump(array));\n\n    if (state === -1) {\n      // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet\n      lastUnitStart = 0;\n      // NALu type is value read from offset 0\n      lastUnitType = array[0] & 0x1f;\n      state = 0;\n      i = 1;\n    }\n    while (i < len) {\n      value = array[i++];\n      // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case\n      if (!state) {\n        state = value ? 0 : 1;\n        continue;\n      }\n      if (state === 1) {\n        state = value ? 0 : 2;\n        continue;\n      }\n      // here we have state either equal to 2 or 3\n      if (!value) {\n        state = 3;\n      } else if (value === 1) {\n        if (lastUnitStart >= 0) {\n          const unit = {\n            data: array.subarray(lastUnitStart, i - state - 1),\n            type: lastUnitType\n          };\n          // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);\n          units.push(unit);\n        } else {\n          // lastUnitStart is undefined => this is the first start code found in this PES packet\n          // first check if start code delimiter is overlapping between 2 PES packets,\n          // ie it started in last packet (lastState not zero)\n          // and ended at the beginning of this PES packet (i <= 4 - lastState)\n          const lastUnit = this.getLastNalUnit(track.samples);\n          if (lastUnit) {\n            if (lastState && i <= 4 - lastState) {\n              // start delimiter overlapping between PES packets\n              // strip start delimiter bytes from the end of last NAL unit\n              // check if lastUnit had a state different from zero\n              if (lastUnit.state) {\n                // strip last bytes\n                lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);\n              }\n            }\n            // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.\n            overflow = i - state - 1;\n            if (overflow > 0) {\n              // logger.log('first NALU found with overflow:' + overflow);\n              const tmp = new Uint8Array(lastUnit.data.byteLength + overflow);\n              tmp.set(lastUnit.data, 0);\n              tmp.set(array.subarray(0, overflow), lastUnit.data.byteLength);\n              lastUnit.data = tmp;\n              lastUnit.state = 0;\n            }\n          }\n        }\n        // check if we can read unit type\n        if (i < len) {\n          unitType = array[i] & 0x1f;\n          // logger.log('find NALU @ offset:' + i + ',type:' + unitType);\n          lastUnitStart = i;\n          lastUnitType = unitType;\n          state = 0;\n        } else {\n          // not enough byte to read unit type. let's read it on next PES parsing\n          state = -1;\n        }\n      } else {\n        state = 0;\n      }\n    }\n    if (lastUnitStart >= 0 && state >= 0) {\n      const unit = {\n        data: array.subarray(lastUnitStart, len),\n        type: lastUnitType,\n        state: state\n      };\n      units.push(unit);\n      // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);\n    }\n    // no NALu found\n    if (units.length === 0) {\n      // append pes.data to previous NAL unit\n      const lastUnit = this.getLastNalUnit(track.samples);\n      if (lastUnit) {\n        const tmp = new Uint8Array(lastUnit.data.byteLength + array.byteLength);\n        tmp.set(lastUnit.data, 0);\n        tmp.set(array, lastUnit.data.byteLength);\n        lastUnit.data = tmp;\n      }\n    }\n    track.naluState = state;\n    return units;\n  }\n  parseAACPES(track, pes) {\n    let startOffset = 0;\n    const aacOverFlow = this.aacOverFlow;\n    let data = pes.data;\n    if (aacOverFlow) {\n      this.aacOverFlow = null;\n      const frameMissingBytes = aacOverFlow.missing;\n      const sampleLength = aacOverFlow.sample.unit.byteLength;\n      // logger.log(`AAC: append overflowing ${sampleLength} bytes to beginning of new PES`);\n      if (frameMissingBytes === -1) {\n        const tmp = new Uint8Array(sampleLength + data.byteLength);\n        tmp.set(aacOverFlow.sample.unit, 0);\n        tmp.set(data, sampleLength);\n        data = tmp;\n      } else {\n        const frameOverflowBytes = sampleLength - frameMissingBytes;\n        aacOverFlow.sample.unit.set(data.subarray(0, frameMissingBytes), frameOverflowBytes);\n        track.samples.push(aacOverFlow.sample);\n        startOffset = aacOverFlow.missing;\n      }\n    }\n    // look for ADTS header (0xFFFx)\n    let offset;\n    let len;\n    for (offset = startOffset, len = data.length; offset < len - 1; offset++) {\n      if (isHeader$1(data, offset)) {\n        break;\n      }\n    }\n    // if ADTS header does not start straight from the beginning of the PES payload, raise an error\n    if (offset !== startOffset) {\n      let reason;\n      const recoverable = offset < len - 1;\n      if (recoverable) {\n        reason = `AAC PES did not start with ADTS header,offset:${offset}`;\n      } else {\n        reason = 'No ADTS header found in AAC PES';\n      }\n      const error = new Error(reason);\n      logger.warn(`parsing error: ${reason}`);\n      this.observer.emit(Events.ERROR, Events.ERROR, {\n        type: ErrorTypes.MEDIA_ERROR,\n        details: ErrorDetails.FRAG_PARSING_ERROR,\n        fatal: false,\n        levelRetry: recoverable,\n        error,\n        reason\n      });\n      if (!recoverable) {\n        return;\n      }\n    }\n    initTrackConfig(track, this.observer, data, offset, this.audioCodec);\n    let pts;\n    if (pes.pts !== undefined) {\n      pts = pes.pts;\n    } else if (aacOverFlow) {\n      // if last AAC frame is overflowing, we should ensure timestamps are contiguous:\n      // first sample PTS should be equal to last sample PTS + frameDuration\n      const frameDuration = getFrameDuration(track.samplerate);\n      pts = aacOverFlow.sample.pts + frameDuration;\n    } else {\n      logger.warn('[tsdemuxer]: AAC PES unknown PTS');\n      return;\n    }\n\n    // scan for aac samples\n    let frameIndex = 0;\n    let frame;\n    while (offset < len) {\n      frame = appendFrame$1(track, data, offset, pts, frameIndex);\n      offset += frame.length;\n      if (!frame.missing) {\n        frameIndex++;\n        for (; offset < len - 1; offset++) {\n          if (isHeader$1(data, offset)) {\n            break;\n          }\n        }\n      } else {\n        this.aacOverFlow = frame;\n        break;\n      }\n    }\n  }\n  parseMPEGPES(track, pes) {\n    const data = pes.data;\n    const length = data.length;\n    let frameIndex = 0;\n    let offset = 0;\n    const pts = pes.pts;\n    if (pts === undefined) {\n      logger.warn('[tsdemuxer]: MPEG PES unknown PTS');\n      return;\n    }\n    while (offset < length) {\n      if (isHeader(data, offset)) {\n        const frame = appendFrame(track, data, offset, pts, frameIndex);\n        if (frame) {\n          offset += frame.length;\n          frameIndex++;\n        } else {\n          // logger.log('Unable to parse Mpeg audio frame');\n          break;\n        }\n      } else {\n        // nothing found, keep looking\n        offset++;\n      }\n    }\n  }\n  parseID3PES(id3Track, pes) {\n    if (pes.pts === undefined) {\n      logger.warn('[tsdemuxer]: ID3 PES unknown PTS');\n      return;\n    }\n    const id3Sample = _extends({}, pes, {\n      type: this._avcTrack ? MetadataSchema.emsg : MetadataSchema.audioId3,\n      duration: Number.POSITIVE_INFINITY\n    });\n    id3Track.samples.push(id3Sample);\n  }\n}\nfunction createAVCSample(key, pts, dts, debug) {\n  return {\n    key,\n    frame: false,\n    pts,\n    dts,\n    units: [],\n    debug,\n    length: 0\n  };\n}\nfunction parsePID(data, offset) {\n  // pid is a 13-bit field starting at the last bit of TS[1]\n  return ((data[offset + 1] & 0x1f) << 8) + data[offset + 2];\n}\nfunction parsePAT(data, offset) {\n  // skip the PSI header and parse the first PMT entry\n  return (data[offset + 10] & 0x1f) << 8 | data[offset + 11];\n}\nfunction parsePMT(data, offset, typeSupported, isSampleAes) {\n  const result = {\n    audio: -1,\n    avc: -1,\n    id3: -1,\n    segmentCodec: 'aac'\n  };\n  const sectionLength = (data[offset + 1] & 0x0f) << 8 | data[offset + 2];\n  const tableEnd = offset + 3 + sectionLength - 4;\n  // to determine where the table is, we have to figure out how\n  // long the program info descriptors are\n  const programInfoLength = (data[offset + 10] & 0x0f) << 8 | data[offset + 11];\n  // advance the offset to the first entry in the mapping table\n  offset += 12 + programInfoLength;\n  while (offset < tableEnd) {\n    const pid = parsePID(data, offset);\n    switch (data[offset]) {\n      case 0xcf:\n        // SAMPLE-AES AAC\n        if (!isSampleAes) {\n          logger.log('ADTS AAC with AES-128-CBC frame encryption found in unencrypted stream');\n          break;\n        }\n      /* falls through */\n      case 0x0f:\n        // ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio)\n        // logger.log('AAC PID:'  + pid);\n        if (result.audio === -1) {\n          result.audio = pid;\n        }\n        break;\n\n      // Packetized metadata (ID3)\n      case 0x15:\n        // logger.log('ID3 PID:'  + pid);\n        if (result.id3 === -1) {\n          result.id3 = pid;\n        }\n        break;\n      case 0xdb:\n        // SAMPLE-AES AVC\n        if (!isSampleAes) {\n          logger.log('H.264 with AES-128-CBC slice encryption found in unencrypted stream');\n          break;\n        }\n      /* falls through */\n      case 0x1b:\n        // ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video)\n        // logger.log('AVC PID:'  + pid);\n        if (result.avc === -1) {\n          result.avc = pid;\n        }\n        break;\n\n      // ISO/IEC 11172-3 (MPEG-1 audio)\n      // or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio)\n      case 0x03:\n      case 0x04:\n        // logger.log('MPEG PID:'  + pid);\n        if (typeSupported.mpeg !== true && typeSupported.mp3 !== true) {\n          logger.log('MPEG audio found, not supported in this browser');\n        } else if (result.audio === -1) {\n          result.audio = pid;\n          result.segmentCodec = 'mp3';\n        }\n        break;\n      case 0x24:\n        logger.warn('Unsupported HEVC stream type found');\n        break;\n    }\n    // move to the next table entry\n    // skip past the elementary stream descriptors, if present\n    offset += ((data[offset + 3] & 0x0f) << 8 | data[offset + 4]) + 5;\n  }\n  return result;\n}\nfunction parsePES(stream) {\n  let i = 0;\n  let frag;\n  let pesLen;\n  let pesHdrLen;\n  let pesPts;\n  let pesDts;\n  const data = stream.data;\n  // safety check\n  if (!stream || stream.size === 0) {\n    return null;\n  }\n\n  // we might need up to 19 bytes to read PES header\n  // if first chunk of data is less than 19 bytes, let's merge it with following ones until we get 19 bytes\n  // usually only one merge is needed (and this is rare ...)\n  while (data[0].length < 19 && data.length > 1) {\n    const newData = new Uint8Array(data[0].length + data[1].length);\n    newData.set(data[0]);\n    newData.set(data[1], data[0].length);\n    data[0] = newData;\n    data.splice(1, 1);\n  }\n  // retrieve PTS/DTS from first fragment\n  frag = data[0];\n  const pesPrefix = (frag[0] << 16) + (frag[1] << 8) + frag[2];\n  if (pesPrefix === 1) {\n    pesLen = (frag[4] << 8) + frag[5];\n    // if PES parsed length is not zero and greater than total received length, stop parsing. PES might be truncated\n    // minus 6 : PES header size\n    if (pesLen && pesLen > stream.size - 6) {\n      return null;\n    }\n    const pesFlags = frag[7];\n    if (pesFlags & 0xc0) {\n      /* PES header described here : http://dvd.sourceforge.net/dvdinfo/pes-hdr.html\n          as PTS / DTS is 33 bit we cannot use bitwise operator in JS,\n          as Bitwise operators treat their operands as a sequence of 32 bits */\n      pesPts = (frag[9] & 0x0e) * 536870912 +\n      // 1 << 29\n      (frag[10] & 0xff) * 4194304 +\n      // 1 << 22\n      (frag[11] & 0xfe) * 16384 +\n      // 1 << 14\n      (frag[12] & 0xff) * 128 +\n      // 1 << 7\n      (frag[13] & 0xfe) / 2;\n      if (pesFlags & 0x40) {\n        pesDts = (frag[14] & 0x0e) * 536870912 +\n        // 1 << 29\n        (frag[15] & 0xff) * 4194304 +\n        // 1 << 22\n        (frag[16] & 0xfe) * 16384 +\n        // 1 << 14\n        (frag[17] & 0xff) * 128 +\n        // 1 << 7\n        (frag[18] & 0xfe) / 2;\n        if (pesPts - pesDts > 60 * 90000) {\n          logger.warn(`${Math.round((pesPts - pesDts) / 90000)}s delta between PTS and DTS, align them`);\n          pesPts = pesDts;\n        }\n      } else {\n        pesDts = pesPts;\n      }\n    }\n    pesHdrLen = frag[8];\n    // 9 bytes : 6 bytes for PES header + 3 bytes for PES extension\n    let payloadStartOffset = pesHdrLen + 9;\n    if (stream.size <= payloadStartOffset) {\n      return null;\n    }\n    stream.size -= payloadStartOffset;\n    // reassemble PES packet\n    const pesData = new Uint8Array(stream.size);\n    for (let j = 0, dataLen = data.length; j < dataLen; j++) {\n      frag = data[j];\n      let len = frag.byteLength;\n      if (payloadStartOffset) {\n        if (payloadStartOffset > len) {\n          // trim full frag if PES header bigger than frag\n          payloadStartOffset -= len;\n          continue;\n        } else {\n          // trim partial frag if PES header smaller than frag\n          frag = frag.subarray(payloadStartOffset);\n          len -= payloadStartOffset;\n          payloadStartOffset = 0;\n        }\n      }\n      pesData.set(frag, i);\n      i += len;\n    }\n    if (pesLen) {\n      // payload size : remove PES header + PES extension\n      pesLen -= pesHdrLen + 3;\n    }\n    return {\n      data: pesData,\n      pts: pesPts,\n      dts: pesDts,\n      len: pesLen\n    };\n  }\n  return null;\n}\nfunction pushAccessUnit(avcSample, avcTrack) {\n  if (avcSample.units.length && avcSample.frame) {\n    // if sample does not have PTS/DTS, patch with last sample PTS/DTS\n    if (avcSample.pts === undefined) {\n      const samples = avcTrack.samples;\n      const nbSamples = samples.length;\n      if (nbSamples) {\n        const lastSample = samples[nbSamples - 1];\n        avcSample.pts = lastSample.pts;\n        avcSample.dts = lastSample.dts;\n      } else {\n        // dropping samples, no timestamp found\n        avcTrack.dropped++;\n        return;\n      }\n    }\n    avcTrack.samples.push(avcSample);\n  }\n  if (avcSample.debug.length) {\n    logger.log(avcSample.pts + '/' + avcSample.dts + ':' + avcSample.debug);\n  }\n}\n\n/**\n * MP3 demuxer\n */\nclass MP3Demuxer extends BaseAudioDemuxer {\n  resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) {\n    super.resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration);\n    this._audioTrack = {\n      container: 'audio/mpeg',\n      type: 'audio',\n      id: 2,\n      pid: -1,\n      sequenceNumber: 0,\n      segmentCodec: 'mp3',\n      samples: [],\n      manifestCodec: audioCodec,\n      duration: trackDuration,\n      inputTimeScale: 90000,\n      dropped: 0\n    };\n  }\n  static probe(data) {\n    if (!data) {\n      return false;\n    }\n\n    // check if data contains ID3 timestamp and MPEG sync word\n    // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1\n    // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III)\n    // More info http://www.mp3-tech.org/programmer/frame_header.html\n    const id3Data = getID3Data(data, 0) || [];\n    let offset = id3Data.length;\n    for (let length = data.length; offset < length; offset++) {\n      if (probe(data, offset)) {\n        logger.log('MPEG Audio sync word found !');\n        return true;\n      }\n    }\n    return false;\n  }\n  canParse(data, offset) {\n    return canParse(data, offset);\n  }\n  appendFrame(track, data, offset) {\n    if (this.basePTS === null) {\n      return;\n    }\n    return appendFrame(track, data, offset, this.basePTS, this.frameIndex);\n  }\n}\n\n/**\n *  AAC helper\n */\n\nclass AAC {\n  static getSilentFrame(codec, channelCount) {\n    switch (codec) {\n      case 'mp4a.40.2':\n        if (channelCount === 1) {\n          return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]);\n        } else if (channelCount === 2) {\n          return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]);\n        } else if (channelCount === 3) {\n          return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]);\n        } else if (channelCount === 4) {\n          return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]);\n        } else if (channelCount === 5) {\n          return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]);\n        } else if (channelCount === 6) {\n          return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x00, 0xb2, 0x00, 0x20, 0x08, 0xe0]);\n        }\n        break;\n      // handle HE-AAC below (mp4a.40.5 / mp4a.40.29)\n      default:\n        if (channelCount === 1) {\n          // ffmpeg -y -f lavfi -i \"aevalsrc=0:d=0.05\" -c:a libfdk_aac -profile:a aac_he -b:a 4k output.aac && hexdump -v -e '16/1 \"0x%x,\" \"\\n\"' -v output.aac\n          return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x4e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x1c, 0x6, 0xf1, 0xc1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]);\n        } else if (channelCount === 2) {\n          // ffmpeg -y -f lavfi -i \"aevalsrc=0|0:d=0.05\" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 \"0x%x,\" \"\\n\"' -v output.aac\n          return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]);\n        } else if (channelCount === 3) {\n          // ffmpeg -y -f lavfi -i \"aevalsrc=0|0|0:d=0.05\" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 \"0x%x,\" \"\\n\"' -v output.aac\n          return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]);\n        }\n        break;\n    }\n    return undefined;\n  }\n}\n\n/**\n * Generate MP4 Box\n */\n\nconst UINT32_MAX = Math.pow(2, 32) - 1;\nclass MP4 {\n  static init() {\n    MP4.types = {\n      avc1: [],\n      // codingname\n      avcC: [],\n      btrt: [],\n      dinf: [],\n      dref: [],\n      esds: [],\n      ftyp: [],\n      hdlr: [],\n      mdat: [],\n      mdhd: [],\n      mdia: [],\n      mfhd: [],\n      minf: [],\n      moof: [],\n      moov: [],\n      mp4a: [],\n      '.mp3': [],\n      mvex: [],\n      mvhd: [],\n      pasp: [],\n      sdtp: [],\n      stbl: [],\n      stco: [],\n      stsc: [],\n      stsd: [],\n      stsz: [],\n      stts: [],\n      tfdt: [],\n      tfhd: [],\n      traf: [],\n      trak: [],\n      trun: [],\n      trex: [],\n      tkhd: [],\n      vmhd: [],\n      smhd: []\n    };\n    let i;\n    for (i in MP4.types) {\n      if (MP4.types.hasOwnProperty(i)) {\n        MP4.types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)];\n      }\n    }\n    const videoHdlr = new Uint8Array([0x00,\n    // version 0\n    0x00, 0x00, 0x00,\n    // flags\n    0x00, 0x00, 0x00, 0x00,\n    // pre_defined\n    0x76, 0x69, 0x64, 0x65,\n    // handler_type: 'vide'\n    0x00, 0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x00, 0x00, 0x00,\n    // reserved\n    0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'\n    ]);\n\n    const audioHdlr = new Uint8Array([0x00,\n    // version 0\n    0x00, 0x00, 0x00,\n    // flags\n    0x00, 0x00, 0x00, 0x00,\n    // pre_defined\n    0x73, 0x6f, 0x75, 0x6e,\n    // handler_type: 'soun'\n    0x00, 0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x00, 0x00, 0x00,\n    // reserved\n    0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'\n    ]);\n\n    MP4.HDLR_TYPES = {\n      video: videoHdlr,\n      audio: audioHdlr\n    };\n    const dref = new Uint8Array([0x00,\n    // version 0\n    0x00, 0x00, 0x00,\n    // flags\n    0x00, 0x00, 0x00, 0x01,\n    // entry_count\n    0x00, 0x00, 0x00, 0x0c,\n    // entry_size\n    0x75, 0x72, 0x6c, 0x20,\n    // 'url' type\n    0x00,\n    // version 0\n    0x00, 0x00, 0x01 // entry_flags\n    ]);\n\n    const stco = new Uint8Array([0x00,\n    // version\n    0x00, 0x00, 0x00,\n    // flags\n    0x00, 0x00, 0x00, 0x00 // entry_count\n    ]);\n\n    MP4.STTS = MP4.STSC = MP4.STCO = stco;\n    MP4.STSZ = new Uint8Array([0x00,\n    // version\n    0x00, 0x00, 0x00,\n    // flags\n    0x00, 0x00, 0x00, 0x00,\n    // sample_size\n    0x00, 0x00, 0x00, 0x00 // sample_count\n    ]);\n\n    MP4.VMHD = new Uint8Array([0x00,\n    // version\n    0x00, 0x00, 0x01,\n    // flags\n    0x00, 0x00,\n    // graphicsmode\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor\n    ]);\n\n    MP4.SMHD = new Uint8Array([0x00,\n    // version\n    0x00, 0x00, 0x00,\n    // flags\n    0x00, 0x00,\n    // balance\n    0x00, 0x00 // reserved\n    ]);\n\n    MP4.STSD = new Uint8Array([0x00,\n    // version 0\n    0x00, 0x00, 0x00,\n    // flags\n    0x00, 0x00, 0x00, 0x01]); // entry_count\n\n    const majorBrand = new Uint8Array([105, 115, 111, 109]); // isom\n    const avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1\n    const minorVersion = new Uint8Array([0, 0, 0, 1]);\n    MP4.FTYP = MP4.box(MP4.types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand);\n    MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref));\n  }\n  static box(type, ...payload) {\n    let size = 8;\n    let i = payload.length;\n    const len = i;\n    // calculate the total size we need to allocate\n    while (i--) {\n      size += payload[i].byteLength;\n    }\n    const result = new Uint8Array(size);\n    result[0] = size >> 24 & 0xff;\n    result[1] = size >> 16 & 0xff;\n    result[2] = size >> 8 & 0xff;\n    result[3] = size & 0xff;\n    result.set(type, 4);\n    // copy the payload into the result\n    for (i = 0, size = 8; i < len; i++) {\n      // copy payload[i] array @ offset size\n      result.set(payload[i], size);\n      size += payload[i].byteLength;\n    }\n    return result;\n  }\n  static hdlr(type) {\n    return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]);\n  }\n  static mdat(data) {\n    return MP4.box(MP4.types.mdat, data);\n  }\n  static mdhd(timescale, duration) {\n    duration *= timescale;\n    const upperWordDuration = Math.floor(duration / (UINT32_MAX + 1));\n    const lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1));\n    return MP4.box(MP4.types.mdhd, new Uint8Array([0x01,\n    // version 1\n    0x00, 0x00, 0x00,\n    // flags\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,\n    // creation_time\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,\n    // modification_time\n    timescale >> 24 & 0xff, timescale >> 16 & 0xff, timescale >> 8 & 0xff, timescale & 0xff,\n    // timescale\n    upperWordDuration >> 24, upperWordDuration >> 16 & 0xff, upperWordDuration >> 8 & 0xff, upperWordDuration & 0xff, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xff, lowerWordDuration >> 8 & 0xff, lowerWordDuration & 0xff, 0x55, 0xc4,\n    // 'und' language (undetermined)\n    0x00, 0x00]));\n  }\n  static mdia(track) {\n    return MP4.box(MP4.types.mdia, MP4.mdhd(track.timescale, track.duration), MP4.hdlr(track.type), MP4.minf(track));\n  }\n  static mfhd(sequenceNumber) {\n    return MP4.box(MP4.types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00,\n    // flags\n    sequenceNumber >> 24, sequenceNumber >> 16 & 0xff, sequenceNumber >> 8 & 0xff, sequenceNumber & 0xff // sequence_number\n    ]));\n  }\n\n  static minf(track) {\n    if (track.type === 'audio') {\n      return MP4.box(MP4.types.minf, MP4.box(MP4.types.smhd, MP4.SMHD), MP4.DINF, MP4.stbl(track));\n    } else {\n      return MP4.box(MP4.types.minf, MP4.box(MP4.types.vmhd, MP4.VMHD), MP4.DINF, MP4.stbl(track));\n    }\n  }\n  static moof(sn, baseMediaDecodeTime, track) {\n    return MP4.box(MP4.types.moof, MP4.mfhd(sn), MP4.traf(track, baseMediaDecodeTime));\n  }\n  static moov(tracks) {\n    let i = tracks.length;\n    const boxes = [];\n    while (i--) {\n      boxes[i] = MP4.trak(tracks[i]);\n    }\n    return MP4.box.apply(null, [MP4.types.moov, MP4.mvhd(tracks[0].timescale, tracks[0].duration)].concat(boxes).concat(MP4.mvex(tracks)));\n  }\n  static mvex(tracks) {\n    let i = tracks.length;\n    const boxes = [];\n    while (i--) {\n      boxes[i] = MP4.trex(tracks[i]);\n    }\n    return MP4.box.apply(null, [MP4.types.mvex, ...boxes]);\n  }\n  static mvhd(timescale, duration) {\n    duration *= timescale;\n    const upperWordDuration = Math.floor(duration / (UINT32_MAX + 1));\n    const lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1));\n    const bytes = new Uint8Array([0x01,\n    // version 1\n    0x00, 0x00, 0x00,\n    // flags\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,\n    // creation_time\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,\n    // modification_time\n    timescale >> 24 & 0xff, timescale >> 16 & 0xff, timescale >> 8 & 0xff, timescale & 0xff,\n    // timescale\n    upperWordDuration >> 24, upperWordDuration >> 16 & 0xff, upperWordDuration >> 8 & 0xff, upperWordDuration & 0xff, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xff, lowerWordDuration >> 8 & 0xff, lowerWordDuration & 0xff, 0x00, 0x01, 0x00, 0x00,\n    // 1.0 rate\n    0x01, 0x00,\n    // 1.0 volume\n    0x00, 0x00,\n    // reserved\n    0x00, 0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,\n    // transformation: unity matrix\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    // pre_defined\n    0xff, 0xff, 0xff, 0xff // next_track_ID\n    ]);\n\n    return MP4.box(MP4.types.mvhd, bytes);\n  }\n  static sdtp(track) {\n    const samples = track.samples || [];\n    const bytes = new Uint8Array(4 + samples.length);\n    let i;\n    let flags;\n    // leave the full box header (4 bytes) all zero\n    // write the sample table\n    for (i = 0; i < samples.length; i++) {\n      flags = samples[i].flags;\n      bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy;\n    }\n    return MP4.box(MP4.types.sdtp, bytes);\n  }\n  static stbl(track) {\n    return MP4.box(MP4.types.stbl, MP4.stsd(track), MP4.box(MP4.types.stts, MP4.STTS), MP4.box(MP4.types.stsc, MP4.STSC), MP4.box(MP4.types.stsz, MP4.STSZ), MP4.box(MP4.types.stco, MP4.STCO));\n  }\n  static avc1(track) {\n    let sps = [];\n    let pps = [];\n    let i;\n    let data;\n    let len;\n    // assemble the SPSs\n\n    for (i = 0; i < track.sps.length; i++) {\n      data = track.sps[i];\n      len = data.byteLength;\n      sps.push(len >>> 8 & 0xff);\n      sps.push(len & 0xff);\n\n      // SPS\n      sps = sps.concat(Array.prototype.slice.call(data));\n    }\n\n    // assemble the PPSs\n    for (i = 0; i < track.pps.length; i++) {\n      data = track.pps[i];\n      len = data.byteLength;\n      pps.push(len >>> 8 & 0xff);\n      pps.push(len & 0xff);\n      pps = pps.concat(Array.prototype.slice.call(data));\n    }\n    const avcc = MP4.box(MP4.types.avcC, new Uint8Array([0x01,\n    // version\n    sps[3],\n    // profile\n    sps[4],\n    // profile compat\n    sps[5],\n    // level\n    0xfc | 3,\n    // lengthSizeMinusOne, hard-coded to 4 bytes\n    0xe0 | track.sps.length // 3bit reserved (111) + numOfSequenceParameterSets\n    ].concat(sps).concat([track.pps.length // numOfPictureParameterSets\n    ]).concat(pps))); // \"PPS\"\n    const width = track.width;\n    const height = track.height;\n    const hSpacing = track.pixelRatio[0];\n    const vSpacing = track.pixelRatio[1];\n    return MP4.box(MP4.types.avc1, new Uint8Array([0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x01,\n    // data_reference_index\n    0x00, 0x00,\n    // pre_defined\n    0x00, 0x00,\n    // reserved\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    // pre_defined\n    width >> 8 & 0xff, width & 0xff,\n    // width\n    height >> 8 & 0xff, height & 0xff,\n    // height\n    0x00, 0x48, 0x00, 0x00,\n    // horizresolution\n    0x00, 0x48, 0x00, 0x00,\n    // vertresolution\n    0x00, 0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x01,\n    // frame_count\n    0x12, 0x64, 0x61, 0x69, 0x6c,\n    // dailymotion/hls.js\n    0x79, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x68, 0x6c, 0x73, 0x2e, 0x6a, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    // compressorname\n    0x00, 0x18,\n    // depth = 24\n    0x11, 0x11]),\n    // pre_defined = -1\n    avcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80,\n    // bufferSizeDB\n    0x00, 0x2d, 0xc6, 0xc0,\n    // maxBitrate\n    0x00, 0x2d, 0xc6, 0xc0])),\n    // avgBitrate\n    MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24,\n    // hSpacing\n    hSpacing >> 16 & 0xff, hSpacing >> 8 & 0xff, hSpacing & 0xff, vSpacing >> 24,\n    // vSpacing\n    vSpacing >> 16 & 0xff, vSpacing >> 8 & 0xff, vSpacing & 0xff])));\n  }\n  static esds(track) {\n    const configlen = track.config.length;\n    return new Uint8Array([0x00,\n    // version 0\n    0x00, 0x00, 0x00,\n    // flags\n\n    0x03,\n    // descriptor_type\n    0x17 + configlen,\n    // length\n    0x00, 0x01,\n    // es_id\n    0x00,\n    // stream_priority\n\n    0x04,\n    // descriptor_type\n    0x0f + configlen,\n    // length\n    0x40,\n    // codec : mpeg4_audio\n    0x15,\n    // stream_type\n    0x00, 0x00, 0x00,\n    // buffer_size\n    0x00, 0x00, 0x00, 0x00,\n    // maxBitrate\n    0x00, 0x00, 0x00, 0x00,\n    // avgBitrate\n\n    0x05 // descriptor_type\n    ].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor\n  }\n\n  static mp4a(track) {\n    const samplerate = track.samplerate;\n    return MP4.box(MP4.types.mp4a, new Uint8Array([0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x01,\n    // data_reference_index\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    // reserved\n    0x00, track.channelCount,\n    // channelcount\n    0x00, 0x10,\n    // sampleSize:16bits\n    0x00, 0x00, 0x00, 0x00,\n    // reserved2\n    samplerate >> 8 & 0xff, samplerate & 0xff,\n    //\n    0x00, 0x00]), MP4.box(MP4.types.esds, MP4.esds(track)));\n  }\n  static mp3(track) {\n    const samplerate = track.samplerate;\n    return MP4.box(MP4.types['.mp3'], new Uint8Array([0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x01,\n    // data_reference_index\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    // reserved\n    0x00, track.channelCount,\n    // channelcount\n    0x00, 0x10,\n    // sampleSize:16bits\n    0x00, 0x00, 0x00, 0x00,\n    // reserved2\n    samplerate >> 8 & 0xff, samplerate & 0xff,\n    //\n    0x00, 0x00]));\n  }\n  static stsd(track) {\n    if (track.type === 'audio') {\n      if (track.segmentCodec === 'mp3' && track.codec === 'mp3') {\n        return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp3(track));\n      }\n      return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));\n    } else {\n      return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));\n    }\n  }\n  static tkhd(track) {\n    const id = track.id;\n    const duration = track.duration * track.timescale;\n    const width = track.width;\n    const height = track.height;\n    const upperWordDuration = Math.floor(duration / (UINT32_MAX + 1));\n    const lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1));\n    return MP4.box(MP4.types.tkhd, new Uint8Array([0x01,\n    // version 1\n    0x00, 0x00, 0x07,\n    // flags\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,\n    // creation_time\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,\n    // modification_time\n    id >> 24 & 0xff, id >> 16 & 0xff, id >> 8 & 0xff, id & 0xff,\n    // track_ID\n    0x00, 0x00, 0x00, 0x00,\n    // reserved\n    upperWordDuration >> 24, upperWordDuration >> 16 & 0xff, upperWordDuration >> 8 & 0xff, upperWordDuration & 0xff, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xff, lowerWordDuration >> 8 & 0xff, lowerWordDuration & 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    // reserved\n    0x00, 0x00,\n    // layer\n    0x00, 0x00,\n    // alternate_group\n    0x00, 0x00,\n    // non-audio track volume\n    0x00, 0x00,\n    // reserved\n    0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,\n    // transformation: unity matrix\n    width >> 8 & 0xff, width & 0xff, 0x00, 0x00,\n    // width\n    height >> 8 & 0xff, height & 0xff, 0x00, 0x00 // height\n    ]));\n  }\n\n  static traf(track, baseMediaDecodeTime) {\n    const sampleDependencyTable = MP4.sdtp(track);\n    const id = track.id;\n    const upperWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1));\n    const lowerWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1));\n    return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([0x00,\n    // version 0\n    0x00, 0x00, 0x00,\n    // flags\n    id >> 24, id >> 16 & 0xff, id >> 8 & 0xff, id & 0xff // track_ID\n    ])), MP4.box(MP4.types.tfdt, new Uint8Array([0x01,\n    // version 1\n    0x00, 0x00, 0x00,\n    // flags\n    upperWordBaseMediaDecodeTime >> 24, upperWordBaseMediaDecodeTime >> 16 & 0xff, upperWordBaseMediaDecodeTime >> 8 & 0xff, upperWordBaseMediaDecodeTime & 0xff, lowerWordBaseMediaDecodeTime >> 24, lowerWordBaseMediaDecodeTime >> 16 & 0xff, lowerWordBaseMediaDecodeTime >> 8 & 0xff, lowerWordBaseMediaDecodeTime & 0xff])), MP4.trun(track, sampleDependencyTable.length + 16 +\n    // tfhd\n    20 +\n    // tfdt\n    8 +\n    // traf header\n    16 +\n    // mfhd\n    8 +\n    // moof header\n    8),\n    // mdat header\n    sampleDependencyTable);\n  }\n\n  /**\n   * Generate a track box.\n   * @param track a track definition\n   */\n  static trak(track) {\n    track.duration = track.duration || 0xffffffff;\n    return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track));\n  }\n  static trex(track) {\n    const id = track.id;\n    return MP4.box(MP4.types.trex, new Uint8Array([0x00,\n    // version 0\n    0x00, 0x00, 0x00,\n    // flags\n    id >> 24, id >> 16 & 0xff, id >> 8 & 0xff, id & 0xff,\n    // track_ID\n    0x00, 0x00, 0x00, 0x01,\n    // default_sample_description_index\n    0x00, 0x00, 0x00, 0x00,\n    // default_sample_duration\n    0x00, 0x00, 0x00, 0x00,\n    // default_sample_size\n    0x00, 0x01, 0x00, 0x01 // default_sample_flags\n    ]));\n  }\n\n  static trun(track, offset) {\n    const samples = track.samples || [];\n    const len = samples.length;\n    const arraylen = 12 + 16 * len;\n    const array = new Uint8Array(arraylen);\n    let i;\n    let sample;\n    let duration;\n    let size;\n    let flags;\n    let cts;\n    offset += 8 + arraylen;\n    array.set([track.type === 'video' ? 0x01 : 0x00,\n    // version 1 for video with signed-int sample_composition_time_offset\n    0x00, 0x0f, 0x01,\n    // flags\n    len >>> 24 & 0xff, len >>> 16 & 0xff, len >>> 8 & 0xff, len & 0xff,\n    // sample_count\n    offset >>> 24 & 0xff, offset >>> 16 & 0xff, offset >>> 8 & 0xff, offset & 0xff // data_offset\n    ], 0);\n    for (i = 0; i < len; i++) {\n      sample = samples[i];\n      duration = sample.duration;\n      size = sample.size;\n      flags = sample.flags;\n      cts = sample.cts;\n      array.set([duration >>> 24 & 0xff, duration >>> 16 & 0xff, duration >>> 8 & 0xff, duration & 0xff,\n      // sample_duration\n      size >>> 24 & 0xff, size >>> 16 & 0xff, size >>> 8 & 0xff, size & 0xff,\n      // sample_size\n      flags.isLeading << 2 | flags.dependsOn, flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.paddingValue << 1 | flags.isNonSync, flags.degradPrio & 0xf0 << 8, flags.degradPrio & 0x0f,\n      // sample_flags\n      cts >>> 24 & 0xff, cts >>> 16 & 0xff, cts >>> 8 & 0xff, cts & 0xff // sample_composition_time_offset\n      ], 12 + 16 * i);\n    }\n    return MP4.box(MP4.types.trun, array);\n  }\n  static initSegment(tracks) {\n    if (!MP4.types) {\n      MP4.init();\n    }\n    const movie = MP4.moov(tracks);\n    const result = new Uint8Array(MP4.FTYP.byteLength + movie.byteLength);\n    result.set(MP4.FTYP);\n    result.set(movie, MP4.FTYP.byteLength);\n    return result;\n  }\n}\nMP4.types = void 0;\nMP4.HDLR_TYPES = void 0;\nMP4.STTS = void 0;\nMP4.STSC = void 0;\nMP4.STCO = void 0;\nMP4.STSZ = void 0;\nMP4.VMHD = void 0;\nMP4.SMHD = void 0;\nMP4.STSD = void 0;\nMP4.FTYP = void 0;\nMP4.DINF = void 0;\n\nconst MPEG_TS_CLOCK_FREQ_HZ = 90000;\nfunction toTimescaleFromBase(baseTime, destScale, srcBase = 1, round = false) {\n  const result = baseTime * destScale * srcBase; // equivalent to `(value * scale) / (1 / base)`\n  return round ? Math.round(result) : result;\n}\nfunction toTimescaleFromScale(baseTime, destScale, srcScale = 1, round = false) {\n  return toTimescaleFromBase(baseTime, destScale, 1 / srcScale, round);\n}\nfunction toMsFromMpegTsClock(baseTime, round = false) {\n  return toTimescaleFromBase(baseTime, 1000, 1 / MPEG_TS_CLOCK_FREQ_HZ, round);\n}\nfunction toMpegTsClockFromTimescale(baseTime, srcScale = 1) {\n  return toTimescaleFromBase(baseTime, MPEG_TS_CLOCK_FREQ_HZ, 1 / srcScale);\n}\n\nconst MAX_SILENT_FRAME_DURATION = 10 * 1000; // 10 seconds\nconst AAC_SAMPLES_PER_FRAME = 1024;\nconst MPEG_AUDIO_SAMPLE_PER_FRAME = 1152;\nlet chromeVersion = null;\nlet safariWebkitVersion = null;\nclass MP4Remuxer {\n  constructor(observer, config, typeSupported, vendor = '') {\n    this.observer = void 0;\n    this.config = void 0;\n    this.typeSupported = void 0;\n    this.ISGenerated = false;\n    this._initPTS = null;\n    this._initDTS = null;\n    this.nextAvcDts = null;\n    this.nextAudioPts = null;\n    this.videoSampleDuration = null;\n    this.isAudioContiguous = false;\n    this.isVideoContiguous = false;\n    this.observer = observer;\n    this.config = config;\n    this.typeSupported = typeSupported;\n    this.ISGenerated = false;\n    if (chromeVersion === null) {\n      const userAgent = navigator.userAgent || '';\n      const result = userAgent.match(/Chrome\\/(\\d+)/i);\n      chromeVersion = result ? parseInt(result[1]) : 0;\n    }\n    if (safariWebkitVersion === null) {\n      const result = navigator.userAgent.match(/Safari\\/(\\d+)/i);\n      safariWebkitVersion = result ? parseInt(result[1]) : 0;\n    }\n  }\n  destroy() {}\n  resetTimeStamp(defaultTimeStamp) {\n    logger.log('[mp4-remuxer]: initPTS & initDTS reset');\n    this._initPTS = this._initDTS = defaultTimeStamp;\n  }\n  resetNextTimestamp() {\n    logger.log('[mp4-remuxer]: reset next timestamp');\n    this.isVideoContiguous = false;\n    this.isAudioContiguous = false;\n  }\n  resetInitSegment() {\n    logger.log('[mp4-remuxer]: ISGenerated flag reset');\n    this.ISGenerated = false;\n  }\n  getVideoStartPts(videoSamples) {\n    let rolloverDetected = false;\n    const startPTS = videoSamples.reduce((minPTS, sample) => {\n      const delta = sample.pts - minPTS;\n      if (delta < -4294967296) {\n        // 2^32, see PTSNormalize for reasoning, but we're hitting a rollover here, and we don't want that to impact the timeOffset calculation\n        rolloverDetected = true;\n        return normalizePts(minPTS, sample.pts);\n      } else if (delta > 0) {\n        return minPTS;\n      } else {\n        return sample.pts;\n      }\n    }, videoSamples[0].pts);\n    if (rolloverDetected) {\n      logger.debug('PTS rollover detected');\n    }\n    return startPTS;\n  }\n  remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset, flush, playlistType) {\n    let video;\n    let audio;\n    let initSegment;\n    let text;\n    let id3;\n    let independent;\n    let audioTimeOffset = timeOffset;\n    let videoTimeOffset = timeOffset;\n\n    // If we're remuxing audio and video progressively, wait until we've received enough samples for each track before proceeding.\n    // This is done to synchronize the audio and video streams. We know if the current segment will have samples if the \"pid\"\n    // parameter is greater than -1. The pid is set when the PMT is parsed, which contains the tracks list.\n    // However, if the initSegment has already been generated, or we've reached the end of a segment (flush),\n    // then we can remux one track without waiting for the other.\n    const hasAudio = audioTrack.pid > -1;\n    const hasVideo = videoTrack.pid > -1;\n    const length = videoTrack.samples.length;\n    const enoughAudioSamples = audioTrack.samples.length > 0;\n    const enoughVideoSamples = flush && length > 0 || length > 1;\n    const canRemuxAvc = (!hasAudio || enoughAudioSamples) && (!hasVideo || enoughVideoSamples) || this.ISGenerated || flush;\n    if (canRemuxAvc) {\n      if (!this.ISGenerated) {\n        initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset);\n      }\n      const isVideoContiguous = this.isVideoContiguous;\n      let firstKeyFrameIndex = -1;\n      let firstKeyFramePTS;\n      if (enoughVideoSamples) {\n        firstKeyFrameIndex = findKeyframeIndex(videoTrack.samples);\n        if (!isVideoContiguous && this.config.forceKeyFrameOnDiscontinuity) {\n          independent = true;\n          if (firstKeyFrameIndex > 0) {\n            logger.warn(`[mp4-remuxer]: Dropped ${firstKeyFrameIndex} out of ${length} video samples due to a missing keyframe`);\n            const startPTS = this.getVideoStartPts(videoTrack.samples);\n            videoTrack.samples = videoTrack.samples.slice(firstKeyFrameIndex);\n            videoTrack.dropped += firstKeyFrameIndex;\n            videoTimeOffset += (videoTrack.samples[0].pts - startPTS) / videoTrack.inputTimeScale;\n            firstKeyFramePTS = videoTimeOffset;\n          } else if (firstKeyFrameIndex === -1) {\n            logger.warn(`[mp4-remuxer]: No keyframe found out of ${length} video samples`);\n            independent = false;\n          }\n        }\n      }\n      if (this.ISGenerated) {\n        if (enoughAudioSamples && enoughVideoSamples) {\n          // timeOffset is expected to be the offset of the first timestamp of this fragment (first DTS)\n          // if first audio DTS is not aligned with first video DTS then we need to take that into account\n          // when providing timeOffset to remuxAudio / remuxVideo. if we don't do that, there might be a permanent / small\n          // drift between audio and video streams\n          const startPTS = this.getVideoStartPts(videoTrack.samples);\n          const tsDelta = normalizePts(audioTrack.samples[0].pts, startPTS) - startPTS;\n          const audiovideoTimestampDelta = tsDelta / videoTrack.inputTimeScale;\n          audioTimeOffset += Math.max(0, audiovideoTimestampDelta);\n          videoTimeOffset += Math.max(0, -audiovideoTimestampDelta);\n        }\n\n        // Purposefully remuxing audio before video, so that remuxVideo can use nextAudioPts, which is calculated in remuxAudio.\n        if (enoughAudioSamples) {\n          // if initSegment was generated without audio samples, regenerate it again\n          if (!audioTrack.samplerate) {\n            logger.warn('[mp4-remuxer]: regenerate InitSegment as audio detected');\n            initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset);\n          }\n          audio = this.remuxAudio(audioTrack, audioTimeOffset, this.isAudioContiguous, accurateTimeOffset, hasVideo || enoughVideoSamples || playlistType === PlaylistLevelType.AUDIO ? videoTimeOffset : undefined);\n          if (enoughVideoSamples) {\n            const audioTrackLength = audio ? audio.endPTS - audio.startPTS : 0;\n            // if initSegment was generated without video samples, regenerate it again\n            if (!videoTrack.inputTimeScale) {\n              logger.warn('[mp4-remuxer]: regenerate InitSegment as video detected');\n              initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset);\n            }\n            video = this.remuxVideo(videoTrack, videoTimeOffset, isVideoContiguous, audioTrackLength);\n          }\n        } else if (enoughVideoSamples) {\n          video = this.remuxVideo(videoTrack, videoTimeOffset, isVideoContiguous, 0);\n        }\n        if (video) {\n          video.firstKeyFrame = firstKeyFrameIndex;\n          video.independent = firstKeyFrameIndex !== -1;\n          video.firstKeyFramePTS = firstKeyFramePTS;\n        }\n      }\n    }\n\n    // Allow ID3 and text to remux, even if more audio/video samples are required\n    if (this.ISGenerated && this._initPTS && this._initDTS) {\n      if (id3Track.samples.length) {\n        id3 = flushTextTrackMetadataCueSamples(id3Track, timeOffset, this._initPTS, this._initDTS);\n      }\n      if (textTrack.samples.length) {\n        text = flushTextTrackUserdataCueSamples(textTrack, timeOffset, this._initPTS);\n      }\n    }\n    return {\n      audio,\n      video,\n      initSegment,\n      independent,\n      text,\n      id3\n    };\n  }\n  generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset) {\n    const audioSamples = audioTrack.samples;\n    const videoSamples = videoTrack.samples;\n    const typeSupported = this.typeSupported;\n    const tracks = {};\n    const _initPTS = this._initPTS;\n    let computePTSDTS = !_initPTS || accurateTimeOffset;\n    let container = 'audio/mp4';\n    let initPTS;\n    let initDTS;\n    let timescale;\n    if (computePTSDTS) {\n      initPTS = initDTS = Infinity;\n    }\n    if (audioTrack.config && audioSamples.length) {\n      // let's use audio sampling rate as MP4 time scale.\n      // rationale is that there is a integer nb of audio frames per audio sample (1024 for AAC)\n      // using audio sampling rate here helps having an integer MP4 frame duration\n      // this avoids potential rounding issue and AV sync issue\n      audioTrack.timescale = audioTrack.samplerate;\n      switch (audioTrack.segmentCodec) {\n        case 'mp3':\n          if (typeSupported.mpeg) {\n            // Chrome and Safari\n            container = 'audio/mpeg';\n            audioTrack.codec = '';\n          } else if (typeSupported.mp3) {\n            // Firefox\n            audioTrack.codec = 'mp3';\n          }\n          break;\n      }\n      tracks.audio = {\n        id: 'audio',\n        container: container,\n        codec: audioTrack.codec,\n        initSegment: audioTrack.segmentCodec === 'mp3' && typeSupported.mpeg ? new Uint8Array(0) : MP4.initSegment([audioTrack]),\n        metadata: {\n          channelCount: audioTrack.channelCount\n        }\n      };\n      if (computePTSDTS) {\n        timescale = audioTrack.inputTimeScale;\n        if (!_initPTS || timescale !== _initPTS.timescale) {\n          // remember first PTS of this demuxing context. for audio, PTS = DTS\n          initPTS = initDTS = audioSamples[0].pts - Math.round(timescale * timeOffset);\n        } else {\n          computePTSDTS = false;\n        }\n      }\n    }\n    if (videoTrack.sps && videoTrack.pps && videoSamples.length) {\n      // let's use input time scale as MP4 video timescale\n      // we use input time scale straight away to avoid rounding issues on frame duration / cts computation\n      videoTrack.timescale = videoTrack.inputTimeScale;\n      tracks.video = {\n        id: 'main',\n        container: 'video/mp4',\n        codec: videoTrack.codec,\n        initSegment: MP4.initSegment([videoTrack]),\n        metadata: {\n          width: videoTrack.width,\n          height: videoTrack.height\n        }\n      };\n      if (computePTSDTS) {\n        timescale = videoTrack.inputTimeScale;\n        if (!_initPTS || timescale !== _initPTS.timescale) {\n          const startPTS = this.getVideoStartPts(videoSamples);\n          const startOffset = Math.round(timescale * timeOffset);\n          initDTS = Math.min(initDTS, normalizePts(videoSamples[0].dts, startPTS) - startOffset);\n          initPTS = Math.min(initPTS, startPTS - startOffset);\n        } else {\n          computePTSDTS = false;\n        }\n      }\n    }\n    if (Object.keys(tracks).length) {\n      this.ISGenerated = true;\n      if (computePTSDTS) {\n        this._initPTS = {\n          baseTime: initPTS,\n          timescale: timescale\n        };\n        this._initDTS = {\n          baseTime: initDTS,\n          timescale: timescale\n        };\n      } else {\n        initPTS = timescale = undefined;\n      }\n      return {\n        tracks,\n        initPTS,\n        timescale\n      };\n    }\n  }\n  remuxVideo(track, timeOffset, contiguous, audioTrackLength) {\n    const timeScale = track.inputTimeScale;\n    const inputSamples = track.samples;\n    const outputSamples = [];\n    const nbSamples = inputSamples.length;\n    const initPTS = this._initPTS;\n    let nextAvcDts = this.nextAvcDts;\n    let offset = 8;\n    let mp4SampleDuration = this.videoSampleDuration;\n    let firstDTS;\n    let lastDTS;\n    let minPTS = Number.POSITIVE_INFINITY;\n    let maxPTS = Number.NEGATIVE_INFINITY;\n    let sortSamples = false;\n\n    // if parsed fragment is contiguous with last one, let's use last DTS value as reference\n    if (!contiguous || nextAvcDts === null) {\n      const pts = timeOffset * timeScale;\n      const cts = inputSamples[0].pts - normalizePts(inputSamples[0].dts, inputSamples[0].pts);\n      // if not contiguous, let's use target timeOffset\n      nextAvcDts = pts - cts;\n    }\n\n    // PTS is coded on 33bits, and can loop from -2^32 to 2^32\n    // PTSNormalize will make PTS/DTS value monotonic, we use last known DTS value as reference value\n    const initTime = initPTS.baseTime * timeScale / initPTS.timescale;\n    for (let i = 0; i < nbSamples; i++) {\n      const sample = inputSamples[i];\n      sample.pts = normalizePts(sample.pts - initTime, nextAvcDts);\n      sample.dts = normalizePts(sample.dts - initTime, nextAvcDts);\n      if (sample.dts < inputSamples[i > 0 ? i - 1 : i].dts) {\n        sortSamples = true;\n      }\n    }\n\n    // sort video samples by DTS then PTS then demux id order\n    if (sortSamples) {\n      inputSamples.sort(function (a, b) {\n        const deltadts = a.dts - b.dts;\n        const deltapts = a.pts - b.pts;\n        return deltadts || deltapts;\n      });\n    }\n\n    // Get first/last DTS\n    firstDTS = inputSamples[0].dts;\n    lastDTS = inputSamples[inputSamples.length - 1].dts;\n\n    // Sample duration (as expected by trun MP4 boxes), should be the delta between sample DTS\n    // set this constant duration as being the avg delta between consecutive DTS.\n    const inputDuration = lastDTS - firstDTS;\n    const averageSampleDuration = inputDuration ? Math.round(inputDuration / (nbSamples - 1)) : mp4SampleDuration || track.inputTimeScale / 30;\n\n    // if fragment are contiguous, detect hole/overlapping between fragments\n    if (contiguous) {\n      // check timestamp continuity across consecutive fragments (this is to remove inter-fragment gap/hole)\n      const delta = firstDTS - nextAvcDts;\n      const foundHole = delta > averageSampleDuration;\n      const foundOverlap = delta < -1;\n      if (foundHole || foundOverlap) {\n        if (foundHole) {\n          logger.warn(`AVC: ${toMsFromMpegTsClock(delta, true)} ms (${delta}dts) hole between fragments detected, filling it`);\n        } else {\n          logger.warn(`AVC: ${toMsFromMpegTsClock(-delta, true)} ms (${delta}dts) overlapping between fragments detected`);\n        }\n        if (!foundOverlap || nextAvcDts >= inputSamples[0].pts) {\n          firstDTS = nextAvcDts;\n          const firstPTS = inputSamples[0].pts - delta;\n          inputSamples[0].dts = firstDTS;\n          inputSamples[0].pts = firstPTS;\n          logger.log(`Video: First PTS/DTS adjusted: ${toMsFromMpegTsClock(firstPTS, true)}/${toMsFromMpegTsClock(firstDTS, true)}, delta: ${toMsFromMpegTsClock(delta, true)} ms`);\n        }\n      }\n    }\n    firstDTS = Math.max(0, firstDTS);\n    let nbNalu = 0;\n    let naluLen = 0;\n    for (let i = 0; i < nbSamples; i++) {\n      // compute total/avc sample length and nb of NAL units\n      const sample = inputSamples[i];\n      const units = sample.units;\n      const nbUnits = units.length;\n      let sampleLen = 0;\n      for (let j = 0; j < nbUnits; j++) {\n        sampleLen += units[j].data.length;\n      }\n      naluLen += sampleLen;\n      nbNalu += nbUnits;\n      sample.length = sampleLen;\n\n      // ensure sample monotonic DTS\n      sample.dts = Math.max(sample.dts, firstDTS);\n      minPTS = Math.min(sample.pts, minPTS);\n      maxPTS = Math.max(sample.pts, maxPTS);\n    }\n    lastDTS = inputSamples[nbSamples - 1].dts;\n\n    /* concatenate the video data and construct the mdat in place\n      (need 8 more bytes to fill length and mpdat type) */\n    const mdatSize = naluLen + 4 * nbNalu + 8;\n    let mdat;\n    try {\n      mdat = new Uint8Array(mdatSize);\n    } catch (err) {\n      this.observer.emit(Events.ERROR, Events.ERROR, {\n        type: ErrorTypes.MUX_ERROR,\n        details: ErrorDetails.REMUX_ALLOC_ERROR,\n        fatal: false,\n        error: err,\n        bytes: mdatSize,\n        reason: `fail allocating video mdat ${mdatSize}`\n      });\n      return;\n    }\n    const view = new DataView(mdat.buffer);\n    view.setUint32(0, mdatSize);\n    mdat.set(MP4.types.mdat, 4);\n    let stretchedLastFrame = false;\n    let minDtsDelta = Number.POSITIVE_INFINITY;\n    let minPtsDelta = Number.POSITIVE_INFINITY;\n    let maxDtsDelta = Number.NEGATIVE_INFINITY;\n    let maxPtsDelta = Number.NEGATIVE_INFINITY;\n    for (let i = 0; i < nbSamples; i++) {\n      const avcSample = inputSamples[i];\n      const avcSampleUnits = avcSample.units;\n      let mp4SampleLength = 0;\n      // convert NALU bitstream to MP4 format (prepend NALU with size field)\n      for (let j = 0, nbUnits = avcSampleUnits.length; j < nbUnits; j++) {\n        const unit = avcSampleUnits[j];\n        const unitData = unit.data;\n        const unitDataLen = unit.data.byteLength;\n        view.setUint32(offset, unitDataLen);\n        offset += 4;\n        mdat.set(unitData, offset);\n        offset += unitDataLen;\n        mp4SampleLength += 4 + unitDataLen;\n      }\n\n      // expected sample duration is the Decoding Timestamp diff of consecutive samples\n      let ptsDelta;\n      if (i < nbSamples - 1) {\n        mp4SampleDuration = inputSamples[i + 1].dts - avcSample.dts;\n        ptsDelta = inputSamples[i + 1].pts - avcSample.pts;\n      } else {\n        const config = this.config;\n        const lastFrameDuration = i > 0 ? avcSample.dts - inputSamples[i - 1].dts : averageSampleDuration;\n        ptsDelta = i > 0 ? avcSample.pts - inputSamples[i - 1].pts : averageSampleDuration;\n        if (config.stretchShortVideoTrack && this.nextAudioPts !== null) {\n          // In some cases, a segment's audio track duration may exceed the video track duration.\n          // Since we've already remuxed audio, and we know how long the audio track is, we look to\n          // see if the delta to the next segment is longer than maxBufferHole.\n          // If so, playback would potentially get stuck, so we artificially inflate\n          // the duration of the last frame to minimize any potential gap between segments.\n          const gapTolerance = Math.floor(config.maxBufferHole * timeScale);\n          const deltaToFrameEnd = (audioTrackLength ? minPTS + audioTrackLength * timeScale : this.nextAudioPts) - avcSample.pts;\n          if (deltaToFrameEnd > gapTolerance) {\n            // We subtract lastFrameDuration from deltaToFrameEnd to try to prevent any video\n            // frame overlap. maxBufferHole should be >> lastFrameDuration anyway.\n            mp4SampleDuration = deltaToFrameEnd - lastFrameDuration;\n            if (mp4SampleDuration < 0) {\n              mp4SampleDuration = lastFrameDuration;\n            } else {\n              stretchedLastFrame = true;\n            }\n            logger.log(`[mp4-remuxer]: It is approximately ${deltaToFrameEnd / 90} ms to the next segment; using duration ${mp4SampleDuration / 90} ms for the last video frame.`);\n          } else {\n            mp4SampleDuration = lastFrameDuration;\n          }\n        } else {\n          mp4SampleDuration = lastFrameDuration;\n        }\n      }\n      const compositionTimeOffset = Math.round(avcSample.pts - avcSample.dts);\n      minDtsDelta = Math.min(minDtsDelta, mp4SampleDuration);\n      maxDtsDelta = Math.max(maxDtsDelta, mp4SampleDuration);\n      minPtsDelta = Math.min(minPtsDelta, ptsDelta);\n      maxPtsDelta = Math.max(maxPtsDelta, ptsDelta);\n      outputSamples.push(new Mp4Sample(avcSample.key, mp4SampleDuration, mp4SampleLength, compositionTimeOffset));\n    }\n    if (outputSamples.length) {\n      if (chromeVersion) {\n        if (chromeVersion < 70) {\n          // Chrome workaround, mark first sample as being a Random Access Point (keyframe) to avoid sourcebuffer append issue\n          // https://code.google.com/p/chromium/issues/detail?id=229412\n          const flags = outputSamples[0].flags;\n          flags.dependsOn = 2;\n          flags.isNonSync = 0;\n        }\n      } else if (safariWebkitVersion) {\n        // Fix for \"CNN special report, with CC\" in test-streams (Safari browser only)\n        // Ignore DTS when frame durations are irregular. Safari MSE does not handle this leading to gaps.\n        if (maxPtsDelta - minPtsDelta < maxDtsDelta - minDtsDelta && averageSampleDuration / maxDtsDelta < 0.025 && outputSamples[0].cts === 0) {\n          logger.warn('Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.');\n          let dts = firstDTS;\n          for (let i = 0, len = outputSamples.length; i < len; i++) {\n            const nextDts = dts + outputSamples[i].duration;\n            const pts = dts + outputSamples[i].cts;\n            if (i < len - 1) {\n              const nextPts = nextDts + outputSamples[i + 1].cts;\n              outputSamples[i].duration = nextPts - pts;\n            } else {\n              outputSamples[i].duration = i ? outputSamples[i - 1].duration : averageSampleDuration;\n            }\n            outputSamples[i].cts = 0;\n            dts = nextDts;\n          }\n        }\n      }\n    }\n    // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)\n    mp4SampleDuration = stretchedLastFrame || !mp4SampleDuration ? averageSampleDuration : mp4SampleDuration;\n    this.nextAvcDts = nextAvcDts = lastDTS + mp4SampleDuration;\n    this.videoSampleDuration = mp4SampleDuration;\n    this.isVideoContiguous = true;\n    const moof = MP4.moof(track.sequenceNumber++, firstDTS, _extends({}, track, {\n      samples: outputSamples\n    }));\n    const type = 'video';\n    const data = {\n      data1: moof,\n      data2: mdat,\n      startPTS: minPTS / timeScale,\n      endPTS: (maxPTS + mp4SampleDuration) / timeScale,\n      startDTS: firstDTS / timeScale,\n      endDTS: nextAvcDts / timeScale,\n      type,\n      hasAudio: false,\n      hasVideo: true,\n      nb: outputSamples.length,\n      dropped: track.dropped\n    };\n    track.samples = [];\n    track.dropped = 0;\n    return data;\n  }\n  remuxAudio(track, timeOffset, contiguous, accurateTimeOffset, videoTimeOffset) {\n    const inputTimeScale = track.inputTimeScale;\n    const mp4timeScale = track.samplerate ? track.samplerate : inputTimeScale;\n    const scaleFactor = inputTimeScale / mp4timeScale;\n    const mp4SampleDuration = track.segmentCodec === 'aac' ? AAC_SAMPLES_PER_FRAME : MPEG_AUDIO_SAMPLE_PER_FRAME;\n    const inputSampleDuration = mp4SampleDuration * scaleFactor;\n    const initPTS = this._initPTS;\n    const rawMPEG = track.segmentCodec === 'mp3' && this.typeSupported.mpeg;\n    const outputSamples = [];\n    const alignedWithVideo = videoTimeOffset !== undefined;\n    let inputSamples = track.samples;\n    let offset = rawMPEG ? 0 : 8;\n    let nextAudioPts = this.nextAudioPts || -1;\n\n    // window.audioSamples ? window.audioSamples.push(inputSamples.map(s => s.pts)) : (window.audioSamples = [inputSamples.map(s => s.pts)]);\n\n    // for audio samples, also consider consecutive fragments as being contiguous (even if a level switch occurs),\n    // for sake of clarity:\n    // consecutive fragments are frags with\n    //  - less than 100ms gaps between new time offset (if accurate) and next expected PTS OR\n    //  - less than 20 audio frames distance\n    // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1)\n    // this helps ensuring audio continuity\n    // and this also avoids audio glitches/cut when switching quality, or reporting wrong duration on first audio frame\n    const timeOffsetMpegTS = timeOffset * inputTimeScale;\n    const initTime = initPTS.baseTime * inputTimeScale / initPTS.timescale;\n    this.isAudioContiguous = contiguous = contiguous || inputSamples.length && nextAudioPts > 0 && (accurateTimeOffset && Math.abs(timeOffsetMpegTS - nextAudioPts) < 9000 || Math.abs(normalizePts(inputSamples[0].pts - initTime, timeOffsetMpegTS) - nextAudioPts) < 20 * inputSampleDuration);\n\n    // compute normalized PTS\n    inputSamples.forEach(function (sample) {\n      sample.pts = normalizePts(sample.pts - initTime, timeOffsetMpegTS);\n    });\n    if (!contiguous || nextAudioPts < 0) {\n      // filter out sample with negative PTS that are not playable anyway\n      // if we don't remove these negative samples, they will shift all audio samples forward.\n      // leading to audio overlap between current / next fragment\n      inputSamples = inputSamples.filter(sample => sample.pts >= 0);\n\n      // in case all samples have negative PTS, and have been filtered out, return now\n      if (!inputSamples.length) {\n        return;\n      }\n      if (videoTimeOffset === 0) {\n        // Set the start to 0 to match video so that start gaps larger than inputSampleDuration are filled with silence\n        nextAudioPts = 0;\n      } else if (accurateTimeOffset && !alignedWithVideo) {\n        // When not seeking, not live, and LevelDetails.PTSKnown, use fragment start as predicted next audio PTS\n        nextAudioPts = Math.max(0, timeOffsetMpegTS);\n      } else {\n        // if frags are not contiguous and if we cant trust time offset, let's use first sample PTS as next audio PTS\n        nextAudioPts = inputSamples[0].pts;\n      }\n    }\n\n    // If the audio track is missing samples, the frames seem to get \"left-shifted\" within the\n    // resulting mp4 segment, causing sync issues and leaving gaps at the end of the audio segment.\n    // In an effort to prevent this from happening, we inject frames here where there are gaps.\n    // When possible, we inject a silent frame; when that's not possible, we duplicate the last\n    // frame.\n\n    if (track.segmentCodec === 'aac') {\n      const maxAudioFramesDrift = this.config.maxAudioFramesDrift;\n      for (let i = 0, nextPts = nextAudioPts; i < inputSamples.length; i++) {\n        // First, let's see how far off this frame is from where we expect it to be\n        const sample = inputSamples[i];\n        const pts = sample.pts;\n        const delta = pts - nextPts;\n        const duration = Math.abs(1000 * delta / inputTimeScale);\n\n        // When remuxing with video, if we're overlapping by more than a duration, drop this sample to stay in sync\n        if (delta <= -maxAudioFramesDrift * inputSampleDuration && alignedWithVideo) {\n          if (i === 0) {\n            logger.warn(`Audio frame @ ${(pts / inputTimeScale).toFixed(3)}s overlaps nextAudioPts by ${Math.round(1000 * delta / inputTimeScale)} ms.`);\n            this.nextAudioPts = nextAudioPts = nextPts = pts;\n          }\n        } // eslint-disable-line brace-style\n\n        // Insert missing frames if:\n        // 1: We're more than maxAudioFramesDrift frame away\n        // 2: Not more than MAX_SILENT_FRAME_DURATION away\n        // 3: currentTime (aka nextPtsNorm) is not 0\n        // 4: remuxing with video (videoTimeOffset !== undefined)\n        else if (delta >= maxAudioFramesDrift * inputSampleDuration && duration < MAX_SILENT_FRAME_DURATION && alignedWithVideo) {\n          let missing = Math.round(delta / inputSampleDuration);\n          // Adjust nextPts so that silent samples are aligned with media pts. This will prevent media samples from\n          // later being shifted if nextPts is based on timeOffset and delta is not a multiple of inputSampleDuration.\n          nextPts = pts - missing * inputSampleDuration;\n          if (nextPts < 0) {\n            missing--;\n            nextPts += inputSampleDuration;\n          }\n          if (i === 0) {\n            this.nextAudioPts = nextAudioPts = nextPts;\n          }\n          logger.warn(`[mp4-remuxer]: Injecting ${missing} audio frame @ ${(nextPts / inputTimeScale).toFixed(3)}s due to ${Math.round(1000 * delta / inputTimeScale)} ms gap.`);\n          for (let j = 0; j < missing; j++) {\n            const newStamp = Math.max(nextPts, 0);\n            let fillFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);\n            if (!fillFrame) {\n              logger.log('[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.');\n              fillFrame = sample.unit.subarray();\n            }\n            inputSamples.splice(i, 0, {\n              unit: fillFrame,\n              pts: newStamp\n            });\n            nextPts += inputSampleDuration;\n            i++;\n          }\n        }\n        sample.pts = nextPts;\n        nextPts += inputSampleDuration;\n      }\n    }\n    let firstPTS = null;\n    let lastPTS = null;\n    let mdat;\n    let mdatSize = 0;\n    let sampleLength = inputSamples.length;\n    while (sampleLength--) {\n      mdatSize += inputSamples[sampleLength].unit.byteLength;\n    }\n    for (let j = 0, _nbSamples = inputSamples.length; j < _nbSamples; j++) {\n      const audioSample = inputSamples[j];\n      const unit = audioSample.unit;\n      let pts = audioSample.pts;\n      if (lastPTS !== null) {\n        // If we have more than one sample, set the duration of the sample to the \"real\" duration; the PTS diff with\n        // the previous sample\n        const prevSample = outputSamples[j - 1];\n        prevSample.duration = Math.round((pts - lastPTS) / scaleFactor);\n      } else {\n        if (contiguous && track.segmentCodec === 'aac') {\n          // set PTS/DTS to expected PTS/DTS\n          pts = nextAudioPts;\n        }\n        // remember first PTS of our audioSamples\n        firstPTS = pts;\n        if (mdatSize > 0) {\n          /* concatenate the audio data and construct the mdat in place\n            (need 8 more bytes to fill length and mdat type) */\n          mdatSize += offset;\n          try {\n            mdat = new Uint8Array(mdatSize);\n          } catch (err) {\n            this.observer.emit(Events.ERROR, Events.ERROR, {\n              type: ErrorTypes.MUX_ERROR,\n              details: ErrorDetails.REMUX_ALLOC_ERROR,\n              fatal: false,\n              error: err,\n              bytes: mdatSize,\n              reason: `fail allocating audio mdat ${mdatSize}`\n            });\n            return;\n          }\n          if (!rawMPEG) {\n            const view = new DataView(mdat.buffer);\n            view.setUint32(0, mdatSize);\n            mdat.set(MP4.types.mdat, 4);\n          }\n        } else {\n          // no audio samples\n          return;\n        }\n      }\n      mdat.set(unit, offset);\n      const unitLen = unit.byteLength;\n      offset += unitLen;\n      // Default the sample's duration to the computed mp4SampleDuration, which will either be 1024 for AAC or 1152 for MPEG\n      // In the case that we have 1 sample, this will be the duration. If we have more than one sample, the duration\n      // becomes the PTS diff with the previous sample\n      outputSamples.push(new Mp4Sample(true, mp4SampleDuration, unitLen, 0));\n      lastPTS = pts;\n    }\n\n    // We could end up with no audio samples if all input samples were overlapping with the previously remuxed ones\n    const nbSamples = outputSamples.length;\n    if (!nbSamples) {\n      return;\n    }\n\n    // The next audio sample PTS should be equal to last sample PTS + duration\n    const lastSample = outputSamples[outputSamples.length - 1];\n    this.nextAudioPts = nextAudioPts = lastPTS + scaleFactor * lastSample.duration;\n\n    // Set the track samples from inputSamples to outputSamples before remuxing\n    const moof = rawMPEG ? new Uint8Array(0) : MP4.moof(track.sequenceNumber++, firstPTS / scaleFactor, _extends({}, track, {\n      samples: outputSamples\n    }));\n\n    // Clear the track samples. This also clears the samples array in the demuxer, since the reference is shared\n    track.samples = [];\n    const start = firstPTS / inputTimeScale;\n    const end = nextAudioPts / inputTimeScale;\n    const type = 'audio';\n    const audioData = {\n      data1: moof,\n      data2: mdat,\n      startPTS: start,\n      endPTS: end,\n      startDTS: start,\n      endDTS: end,\n      type,\n      hasAudio: true,\n      hasVideo: false,\n      nb: nbSamples\n    };\n    this.isAudioContiguous = true;\n    return audioData;\n  }\n  remuxEmptyAudio(track, timeOffset, contiguous, videoData) {\n    const inputTimeScale = track.inputTimeScale;\n    const mp4timeScale = track.samplerate ? track.samplerate : inputTimeScale;\n    const scaleFactor = inputTimeScale / mp4timeScale;\n    const nextAudioPts = this.nextAudioPts;\n    // sync with video's timestamp\n    const initDTS = this._initDTS;\n    const init90kHz = initDTS.baseTime * 90000 / initDTS.timescale;\n    const startDTS = (nextAudioPts !== null ? nextAudioPts : videoData.startDTS * inputTimeScale) + init90kHz;\n    const endDTS = videoData.endDTS * inputTimeScale + init90kHz;\n    // one sample's duration value\n    const frameDuration = scaleFactor * AAC_SAMPLES_PER_FRAME;\n    // samples count of this segment's duration\n    const nbSamples = Math.ceil((endDTS - startDTS) / frameDuration);\n    // silent frame\n    const silentFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);\n    logger.warn('[mp4-remuxer]: remux empty Audio');\n    // Can't remux if we can't generate a silent frame...\n    if (!silentFrame) {\n      logger.trace('[mp4-remuxer]: Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec');\n      return;\n    }\n    const samples = [];\n    for (let i = 0; i < nbSamples; i++) {\n      const stamp = startDTS + i * frameDuration;\n      samples.push({\n        unit: silentFrame,\n        pts: stamp,\n        dts: stamp\n      });\n    }\n    track.samples = samples;\n    return this.remuxAudio(track, timeOffset, contiguous, false);\n  }\n}\nfunction normalizePts(value, reference) {\n  let offset;\n  if (reference === null) {\n    return value;\n  }\n  if (reference < value) {\n    // - 2^33\n    offset = -8589934592;\n  } else {\n    // + 2^33\n    offset = 8589934592;\n  }\n  /* PTS is 33bit (from 0 to 2^33 -1)\n    if diff between value and reference is bigger than half of the amplitude (2^32) then it means that\n    PTS looping occured. fill the gap */\n  while (Math.abs(value - reference) > 4294967296) {\n    value += offset;\n  }\n  return value;\n}\nfunction findKeyframeIndex(samples) {\n  for (let i = 0; i < samples.length; i++) {\n    if (samples[i].key) {\n      return i;\n    }\n  }\n  return -1;\n}\nfunction flushTextTrackMetadataCueSamples(track, timeOffset, initPTS, initDTS) {\n  const length = track.samples.length;\n  if (!length) {\n    return;\n  }\n  const inputTimeScale = track.inputTimeScale;\n  for (let index = 0; index < length; index++) {\n    const sample = track.samples[index];\n    // setting id3 pts, dts to relative time\n    // using this._initPTS and this._initDTS to calculate relative time\n    sample.pts = normalizePts(sample.pts - initPTS.baseTime * inputTimeScale / initPTS.timescale, timeOffset * inputTimeScale) / inputTimeScale;\n    sample.dts = normalizePts(sample.dts - initDTS.baseTime * inputTimeScale / initDTS.timescale, timeOffset * inputTimeScale) / inputTimeScale;\n  }\n  const samples = track.samples;\n  track.samples = [];\n  return {\n    samples\n  };\n}\nfunction flushTextTrackUserdataCueSamples(track, timeOffset, initPTS) {\n  const length = track.samples.length;\n  if (!length) {\n    return;\n  }\n  const inputTimeScale = track.inputTimeScale;\n  for (let index = 0; index < length; index++) {\n    const sample = track.samples[index];\n    // setting text pts, dts to relative time\n    // using this._initPTS and this._initDTS to calculate relative time\n    sample.pts = normalizePts(sample.pts - initPTS.baseTime * inputTimeScale / initPTS.timescale, timeOffset * inputTimeScale) / inputTimeScale;\n  }\n  track.samples.sort((a, b) => a.pts - b.pts);\n  const samples = track.samples;\n  track.samples = [];\n  return {\n    samples\n  };\n}\nclass Mp4Sample {\n  constructor(isKeyframe, duration, size, cts) {\n    this.size = void 0;\n    this.duration = void 0;\n    this.cts = void 0;\n    this.flags = void 0;\n    this.duration = duration;\n    this.size = size;\n    this.cts = cts;\n    this.flags = new Mp4SampleFlags(isKeyframe);\n  }\n}\nclass Mp4SampleFlags {\n  constructor(isKeyframe) {\n    this.isLeading = 0;\n    this.isDependedOn = 0;\n    this.hasRedundancy = 0;\n    this.degradPrio = 0;\n    this.dependsOn = 1;\n    this.isNonSync = 1;\n    this.dependsOn = isKeyframe ? 2 : 1;\n    this.isNonSync = isKeyframe ? 0 : 1;\n  }\n}\n\nclass PassThroughRemuxer {\n  constructor() {\n    this.emitInitSegment = false;\n    this.audioCodec = void 0;\n    this.videoCodec = void 0;\n    this.initData = void 0;\n    this.initPTS = null;\n    this.initTracks = void 0;\n    this.lastEndTime = null;\n  }\n  destroy() {}\n  resetTimeStamp(defaultInitPTS) {\n    this.initPTS = defaultInitPTS;\n    this.lastEndTime = null;\n  }\n  resetNextTimestamp() {\n    this.lastEndTime = null;\n  }\n  resetInitSegment(initSegment, audioCodec, videoCodec, decryptdata) {\n    this.audioCodec = audioCodec;\n    this.videoCodec = videoCodec;\n    this.generateInitSegment(patchEncyptionData(initSegment, decryptdata));\n    this.emitInitSegment = true;\n  }\n  generateInitSegment(initSegment) {\n    let {\n      audioCodec,\n      videoCodec\n    } = this;\n    if (!(initSegment != null && initSegment.byteLength)) {\n      this.initTracks = undefined;\n      this.initData = undefined;\n      return;\n    }\n    const initData = this.initData = parseInitSegment(initSegment);\n\n    // Get codec from initSegment or fallback to default\n    if (!audioCodec) {\n      audioCodec = getParsedTrackCodec(initData.audio, ElementaryStreamTypes.AUDIO);\n    }\n    if (!videoCodec) {\n      videoCodec = getParsedTrackCodec(initData.video, ElementaryStreamTypes.VIDEO);\n    }\n    const tracks = {};\n    if (initData.audio && initData.video) {\n      tracks.audiovideo = {\n        container: 'video/mp4',\n        codec: audioCodec + ',' + videoCodec,\n        initSegment,\n        id: 'main'\n      };\n    } else if (initData.audio) {\n      tracks.audio = {\n        container: 'audio/mp4',\n        codec: audioCodec,\n        initSegment,\n        id: 'audio'\n      };\n    } else if (initData.video) {\n      tracks.video = {\n        container: 'video/mp4',\n        codec: videoCodec,\n        initSegment,\n        id: 'main'\n      };\n    } else {\n      logger.warn('[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes.');\n    }\n    this.initTracks = tracks;\n  }\n  remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset) {\n    var _initData, _initData2;\n    let {\n      initPTS,\n      lastEndTime\n    } = this;\n    const result = {\n      audio: undefined,\n      video: undefined,\n      text: textTrack,\n      id3: id3Track,\n      initSegment: undefined\n    };\n\n    // If we haven't yet set a lastEndDTS, or it was reset, set it to the provided timeOffset. We want to use the\n    // lastEndDTS over timeOffset whenever possible; during progressive playback, the media source will not update\n    // the media duration (which is what timeOffset is provided as) before we need to process the next chunk.\n    if (!isFiniteNumber(lastEndTime)) {\n      lastEndTime = this.lastEndTime = timeOffset || 0;\n    }\n\n    // The binary segment data is added to the videoTrack in the mp4demuxer. We don't check to see if the data is only\n    // audio or video (or both); adding it to video was an arbitrary choice.\n    const data = videoTrack.samples;\n    if (!(data != null && data.length)) {\n      return result;\n    }\n    const initSegment = {\n      initPTS: undefined,\n      timescale: 1\n    };\n    let initData = this.initData;\n    if (!((_initData = initData) != null && _initData.length)) {\n      this.generateInitSegment(data);\n      initData = this.initData;\n    }\n    if (!((_initData2 = initData) != null && _initData2.length)) {\n      // We can't remux if the initSegment could not be generated\n      logger.warn('[passthrough-remuxer.ts]: Failed to generate initSegment.');\n      return result;\n    }\n    if (this.emitInitSegment) {\n      initSegment.tracks = this.initTracks;\n      this.emitInitSegment = false;\n    }\n    const duration = getDuration(data, initData);\n    const startDTS = getStartDTS(initData, data);\n    const decodeTime = startDTS === null ? timeOffset : startDTS;\n    if (isInvalidInitPts(initPTS, decodeTime, timeOffset, duration) || initSegment.timescale !== initPTS.timescale && accurateTimeOffset) {\n      initSegment.initPTS = decodeTime - timeOffset;\n      if (initPTS && initPTS.timescale === 1) {\n        logger.warn(`Adjusting initPTS by ${initSegment.initPTS - initPTS.baseTime}`);\n      }\n      this.initPTS = initPTS = {\n        baseTime: initSegment.initPTS,\n        timescale: 1\n      };\n    }\n    const startTime = audioTrack ? decodeTime - initPTS.baseTime / initPTS.timescale : lastEndTime;\n    const endTime = startTime + duration;\n    offsetStartDTS(initData, data, initPTS.baseTime / initPTS.timescale);\n    if (duration > 0) {\n      this.lastEndTime = endTime;\n    } else {\n      logger.warn('Duration parsed from mp4 should be greater than zero');\n      this.resetNextTimestamp();\n    }\n    const hasAudio = !!initData.audio;\n    const hasVideo = !!initData.video;\n    let type = '';\n    if (hasAudio) {\n      type += 'audio';\n    }\n    if (hasVideo) {\n      type += 'video';\n    }\n    const track = {\n      data1: data,\n      startPTS: startTime,\n      startDTS: startTime,\n      endPTS: endTime,\n      endDTS: endTime,\n      type,\n      hasAudio,\n      hasVideo,\n      nb: 1,\n      dropped: 0\n    };\n    result.audio = track.type === 'audio' ? track : undefined;\n    result.video = track.type !== 'audio' ? track : undefined;\n    result.initSegment = initSegment;\n    result.id3 = flushTextTrackMetadataCueSamples(id3Track, timeOffset, initPTS, initPTS);\n    if (textTrack.samples.length) {\n      result.text = flushTextTrackUserdataCueSamples(textTrack, timeOffset, initPTS);\n    }\n    return result;\n  }\n}\nfunction isInvalidInitPts(initPTS, startDTS, timeOffset, duration) {\n  if (initPTS === null) {\n    return true;\n  }\n  // InitPTS is invalid when distance from program would be more than segment duration or a minimum of one second\n  const minDuration = Math.max(duration, 1);\n  const startTime = startDTS - initPTS.baseTime / initPTS.timescale;\n  return Math.abs(startTime - timeOffset) > minDuration;\n}\nfunction getParsedTrackCodec(track, type) {\n  const parsedCodec = track == null ? void 0 : track.codec;\n  if (parsedCodec && parsedCodec.length > 4) {\n    return parsedCodec;\n  }\n  // Since mp4-tools cannot parse full codec string (see 'TODO: Parse codec details'... in mp4-tools)\n  // Provide defaults based on codec type\n  // This allows for some playback of some fmp4 playlists without CODECS defined in manifest\n  if (parsedCodec === 'hvc1' || parsedCodec === 'hev1') {\n    return 'hvc1.1.6.L120.90';\n  }\n  if (parsedCodec === 'av01') {\n    return 'av01.0.04M.08';\n  }\n  if (parsedCodec === 'avc1' || type === ElementaryStreamTypes.VIDEO) {\n    return 'avc1.42e01e';\n  }\n  return 'mp4a.40.5';\n}\n\nlet now;\n// performance.now() not available on WebWorker, at least on Safari Desktop\ntry {\n  now = self.performance.now.bind(self.performance);\n} catch (err) {\n  logger.debug('Unable to use Performance API on this environment');\n  now = typeof self !== 'undefined' && self.Date.now;\n}\nconst muxConfig = [{\n  demux: MP4Demuxer,\n  remux: PassThroughRemuxer\n}, {\n  demux: TSDemuxer,\n  remux: MP4Remuxer\n}, {\n  demux: AACDemuxer,\n  remux: MP4Remuxer\n}, {\n  demux: MP3Demuxer,\n  remux: MP4Remuxer\n}];\nclass Transmuxer {\n  constructor(observer, typeSupported, config, vendor, id) {\n    this.async = false;\n    this.observer = void 0;\n    this.typeSupported = void 0;\n    this.config = void 0;\n    this.vendor = void 0;\n    this.id = void 0;\n    this.demuxer = void 0;\n    this.remuxer = void 0;\n    this.decrypter = void 0;\n    this.probe = void 0;\n    this.decryptionPromise = null;\n    this.transmuxConfig = void 0;\n    this.currentTransmuxState = void 0;\n    this.observer = observer;\n    this.typeSupported = typeSupported;\n    this.config = config;\n    this.vendor = vendor;\n    this.id = id;\n  }\n  configure(transmuxConfig) {\n    this.transmuxConfig = transmuxConfig;\n    if (this.decrypter) {\n      this.decrypter.reset();\n    }\n  }\n  push(data, decryptdata, chunkMeta, state) {\n    const stats = chunkMeta.transmuxing;\n    stats.executeStart = now();\n    let uintData = new Uint8Array(data);\n    const {\n      currentTransmuxState,\n      transmuxConfig\n    } = this;\n    if (state) {\n      this.currentTransmuxState = state;\n    }\n    const {\n      contiguous,\n      discontinuity,\n      trackSwitch,\n      accurateTimeOffset,\n      timeOffset,\n      initSegmentChange\n    } = state || currentTransmuxState;\n    const {\n      audioCodec,\n      videoCodec,\n      defaultInitPts,\n      duration,\n      initSegmentData\n    } = transmuxConfig;\n    const keyData = getEncryptionType(uintData, decryptdata);\n    if (keyData && keyData.method === 'AES-128') {\n      const decrypter = this.getDecrypter();\n      // Software decryption is synchronous; webCrypto is not\n      if (decrypter.isSync()) {\n        // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached\n        // data is handled in the flush() call\n        let decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer);\n        // For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress\n        const loadingParts = chunkMeta.part > -1;\n        if (loadingParts) {\n          decryptedData = decrypter.flush();\n        }\n        if (!decryptedData) {\n          stats.executeEnd = now();\n          return emptyResult(chunkMeta);\n        }\n        uintData = new Uint8Array(decryptedData);\n      } else {\n        this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer).then(decryptedData => {\n          // Calling push here is important; if flush() is called while this is still resolving, this ensures that\n          // the decrypted data has been transmuxed\n          const result = this.push(decryptedData, null, chunkMeta);\n          this.decryptionPromise = null;\n          return result;\n        });\n        return this.decryptionPromise;\n      }\n    }\n    const resetMuxers = this.needsProbing(discontinuity, trackSwitch);\n    if (resetMuxers) {\n      const error = this.configureTransmuxer(uintData);\n      if (error) {\n        logger.warn(`[transmuxer] ${error.message}`);\n        this.observer.emit(Events.ERROR, Events.ERROR, {\n          type: ErrorTypes.MEDIA_ERROR,\n          details: ErrorDetails.FRAG_PARSING_ERROR,\n          fatal: false,\n          error,\n          reason: error.message\n        });\n        stats.executeEnd = now();\n        return emptyResult(chunkMeta);\n      }\n    }\n    if (discontinuity || trackSwitch || initSegmentChange || resetMuxers) {\n      this.resetInitSegment(initSegmentData, audioCodec, videoCodec, duration, decryptdata);\n    }\n    if (discontinuity || initSegmentChange || resetMuxers) {\n      this.resetInitialTimestamp(defaultInitPts);\n    }\n    if (!contiguous) {\n      this.resetContiguity();\n    }\n    const result = this.transmux(uintData, keyData, timeOffset, accurateTimeOffset, chunkMeta);\n    const currentState = this.currentTransmuxState;\n    currentState.contiguous = true;\n    currentState.discontinuity = false;\n    currentState.trackSwitch = false;\n    stats.executeEnd = now();\n    return result;\n  }\n\n  // Due to data caching, flush calls can produce more than one TransmuxerResult (hence the Array type)\n  flush(chunkMeta) {\n    const stats = chunkMeta.transmuxing;\n    stats.executeStart = now();\n    const {\n      decrypter,\n      currentTransmuxState,\n      decryptionPromise\n    } = this;\n    if (decryptionPromise) {\n      // Upon resolution, the decryption promise calls push() and returns its TransmuxerResult up the stack. Therefore\n      // only flushing is required for async decryption\n      return decryptionPromise.then(() => {\n        return this.flush(chunkMeta);\n      });\n    }\n    const transmuxResults = [];\n    const {\n      timeOffset\n    } = currentTransmuxState;\n    if (decrypter) {\n      // The decrypter may have data cached, which needs to be demuxed. In this case we'll have two TransmuxResults\n      // This happens in the case that we receive only 1 push call for a segment (either for non-progressive downloads,\n      // or for progressive downloads with small segments)\n      const decryptedData = decrypter.flush();\n      if (decryptedData) {\n        // Push always returns a TransmuxerResult if decryptdata is null\n        transmuxResults.push(this.push(decryptedData, null, chunkMeta));\n      }\n    }\n    const {\n      demuxer,\n      remuxer\n    } = this;\n    if (!demuxer || !remuxer) {\n      // If probing failed, then Hls.js has been given content its not able to handle\n      stats.executeEnd = now();\n      return [emptyResult(chunkMeta)];\n    }\n    const demuxResultOrPromise = demuxer.flush(timeOffset);\n    if (isPromise(demuxResultOrPromise)) {\n      // Decrypt final SAMPLE-AES samples\n      return demuxResultOrPromise.then(demuxResult => {\n        this.flushRemux(transmuxResults, demuxResult, chunkMeta);\n        return transmuxResults;\n      });\n    }\n    this.flushRemux(transmuxResults, demuxResultOrPromise, chunkMeta);\n    return transmuxResults;\n  }\n  flushRemux(transmuxResults, demuxResult, chunkMeta) {\n    const {\n      audioTrack,\n      videoTrack,\n      id3Track,\n      textTrack\n    } = demuxResult;\n    const {\n      accurateTimeOffset,\n      timeOffset\n    } = this.currentTransmuxState;\n    logger.log(`[transmuxer.ts]: Flushed fragment ${chunkMeta.sn}${chunkMeta.part > -1 ? ' p: ' + chunkMeta.part : ''} of level ${chunkMeta.level}`);\n    const remuxResult = this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset, true, this.id);\n    transmuxResults.push({\n      remuxResult,\n      chunkMeta\n    });\n    chunkMeta.transmuxing.executeEnd = now();\n  }\n  resetInitialTimestamp(defaultInitPts) {\n    const {\n      demuxer,\n      remuxer\n    } = this;\n    if (!demuxer || !remuxer) {\n      return;\n    }\n    demuxer.resetTimeStamp(defaultInitPts);\n    remuxer.resetTimeStamp(defaultInitPts);\n  }\n  resetContiguity() {\n    const {\n      demuxer,\n      remuxer\n    } = this;\n    if (!demuxer || !remuxer) {\n      return;\n    }\n    demuxer.resetContiguity();\n    remuxer.resetNextTimestamp();\n  }\n  resetInitSegment(initSegmentData, audioCodec, videoCodec, trackDuration, decryptdata) {\n    const {\n      demuxer,\n      remuxer\n    } = this;\n    if (!demuxer || !remuxer) {\n      return;\n    }\n    demuxer.resetInitSegment(initSegmentData, audioCodec, videoCodec, trackDuration);\n    remuxer.resetInitSegment(initSegmentData, audioCodec, videoCodec, decryptdata);\n  }\n  destroy() {\n    if (this.demuxer) {\n      this.demuxer.destroy();\n      this.demuxer = undefined;\n    }\n    if (this.remuxer) {\n      this.remuxer.destroy();\n      this.remuxer = undefined;\n    }\n  }\n  transmux(data, keyData, timeOffset, accurateTimeOffset, chunkMeta) {\n    let result;\n    if (keyData && keyData.method === 'SAMPLE-AES') {\n      result = this.transmuxSampleAes(data, keyData, timeOffset, accurateTimeOffset, chunkMeta);\n    } else {\n      result = this.transmuxUnencrypted(data, timeOffset, accurateTimeOffset, chunkMeta);\n    }\n    return result;\n  }\n  transmuxUnencrypted(data, timeOffset, accurateTimeOffset, chunkMeta) {\n    const {\n      audioTrack,\n      videoTrack,\n      id3Track,\n      textTrack\n    } = this.demuxer.demux(data, timeOffset, false, !this.config.progressive);\n    const remuxResult = this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset, false, this.id);\n    return {\n      remuxResult,\n      chunkMeta\n    };\n  }\n  transmuxSampleAes(data, decryptData, timeOffset, accurateTimeOffset, chunkMeta) {\n    return this.demuxer.demuxSampleAes(data, decryptData, timeOffset).then(demuxResult => {\n      const remuxResult = this.remuxer.remux(demuxResult.audioTrack, demuxResult.videoTrack, demuxResult.id3Track, demuxResult.textTrack, timeOffset, accurateTimeOffset, false, this.id);\n      return {\n        remuxResult,\n        chunkMeta\n      };\n    });\n  }\n  configureTransmuxer(data) {\n    const {\n      config,\n      observer,\n      typeSupported,\n      vendor\n    } = this;\n    // probe for content type\n    let mux;\n    for (let i = 0, len = muxConfig.length; i < len; i++) {\n      if (muxConfig[i].demux.probe(data)) {\n        mux = muxConfig[i];\n        break;\n      }\n    }\n    if (!mux) {\n      return new Error('Failed to find demuxer by probing fragment data');\n    }\n    // so let's check that current remuxer and demuxer are still valid\n    const demuxer = this.demuxer;\n    const remuxer = this.remuxer;\n    const Remuxer = mux.remux;\n    const Demuxer = mux.demux;\n    if (!remuxer || !(remuxer instanceof Remuxer)) {\n      this.remuxer = new Remuxer(observer, config, typeSupported, vendor);\n    }\n    if (!demuxer || !(demuxer instanceof Demuxer)) {\n      this.demuxer = new Demuxer(observer, config, typeSupported);\n      this.probe = Demuxer.probe;\n    }\n  }\n  needsProbing(discontinuity, trackSwitch) {\n    // in case of continuity change, or track switch\n    // we might switch from content type (AAC container to TS container, or TS to fmp4 for example)\n    return !this.demuxer || !this.remuxer || discontinuity || trackSwitch;\n  }\n  getDecrypter() {\n    let decrypter = this.decrypter;\n    if (!decrypter) {\n      decrypter = this.decrypter = new Decrypter(this.config);\n    }\n    return decrypter;\n  }\n}\nfunction getEncryptionType(data, decryptData) {\n  let encryptionType = null;\n  if (data.byteLength > 0 && decryptData != null && decryptData.key != null && decryptData.iv !== null && decryptData.method != null) {\n    encryptionType = decryptData;\n  }\n  return encryptionType;\n}\nconst emptyResult = chunkMeta => ({\n  remuxResult: {},\n  chunkMeta\n});\nfunction isPromise(p) {\n  return 'then' in p && p.then instanceof Function;\n}\nclass TransmuxConfig {\n  constructor(audioCodec, videoCodec, initSegmentData, duration, defaultInitPts) {\n    this.audioCodec = void 0;\n    this.videoCodec = void 0;\n    this.initSegmentData = void 0;\n    this.duration = void 0;\n    this.defaultInitPts = void 0;\n    this.audioCodec = audioCodec;\n    this.videoCodec = videoCodec;\n    this.initSegmentData = initSegmentData;\n    this.duration = duration;\n    this.defaultInitPts = defaultInitPts || null;\n  }\n}\nclass TransmuxState {\n  constructor(discontinuity, contiguous, accurateTimeOffset, trackSwitch, timeOffset, initSegmentChange) {\n    this.discontinuity = void 0;\n    this.contiguous = void 0;\n    this.accurateTimeOffset = void 0;\n    this.trackSwitch = void 0;\n    this.timeOffset = void 0;\n    this.initSegmentChange = void 0;\n    this.discontinuity = discontinuity;\n    this.contiguous = contiguous;\n    this.accurateTimeOffset = accurateTimeOffset;\n    this.trackSwitch = trackSwitch;\n    this.timeOffset = timeOffset;\n    this.initSegmentChange = initSegmentChange;\n  }\n}\n\nvar eventemitter3 = {exports: {}};\n\n(function (module) {\n\n\tvar has = Object.prototype.hasOwnProperty\n\t  , prefix = '~';\n\n\t/**\n\t * Constructor to create a storage for our `EE` objects.\n\t * An `Events` instance is a plain object whose properties are event names.\n\t *\n\t * @constructor\n\t * @private\n\t */\n\tfunction Events() {}\n\n\t//\n\t// We try to not inherit from `Object.prototype`. In some engines creating an\n\t// instance in this way is faster than calling `Object.create(null)` directly.\n\t// If `Object.create(null)` is not supported we prefix the event names with a\n\t// character to make sure that the built-in object properties are not\n\t// overridden or used as an attack vector.\n\t//\n\tif (Object.create) {\n\t  Events.prototype = Object.create(null);\n\n\t  //\n\t  // This hack is needed because the `__proto__` property is still inherited in\n\t  // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.\n\t  //\n\t  if (!new Events().__proto__) prefix = false;\n\t}\n\n\t/**\n\t * Representation of a single event listener.\n\t *\n\t * @param {Function} fn The listener function.\n\t * @param {*} context The context to invoke the listener with.\n\t * @param {Boolean} [once=false] Specify if the listener is a one-time listener.\n\t * @constructor\n\t * @private\n\t */\n\tfunction EE(fn, context, once) {\n\t  this.fn = fn;\n\t  this.context = context;\n\t  this.once = once || false;\n\t}\n\n\t/**\n\t * Add a listener for a given event.\n\t *\n\t * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.\n\t * @param {(String|Symbol)} event The event name.\n\t * @param {Function} fn The listener function.\n\t * @param {*} context The context to invoke the listener with.\n\t * @param {Boolean} once Specify if the listener is a one-time listener.\n\t * @returns {EventEmitter}\n\t * @private\n\t */\n\tfunction addListener(emitter, event, fn, context, once) {\n\t  if (typeof fn !== 'function') {\n\t    throw new TypeError('The listener must be a function');\n\t  }\n\n\t  var listener = new EE(fn, context || emitter, once)\n\t    , evt = prefix ? prefix + event : event;\n\n\t  if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;\n\t  else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);\n\t  else emitter._events[evt] = [emitter._events[evt], listener];\n\n\t  return emitter;\n\t}\n\n\t/**\n\t * Clear event by name.\n\t *\n\t * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.\n\t * @param {(String|Symbol)} evt The Event name.\n\t * @private\n\t */\n\tfunction clearEvent(emitter, evt) {\n\t  if (--emitter._eventsCount === 0) emitter._events = new Events();\n\t  else delete emitter._events[evt];\n\t}\n\n\t/**\n\t * Minimal `EventEmitter` interface that is molded against the Node.js\n\t * `EventEmitter` interface.\n\t *\n\t * @constructor\n\t * @public\n\t */\n\tfunction EventEmitter() {\n\t  this._events = new Events();\n\t  this._eventsCount = 0;\n\t}\n\n\t/**\n\t * Return an array listing the events for which the emitter has registered\n\t * listeners.\n\t *\n\t * @returns {Array}\n\t * @public\n\t */\n\tEventEmitter.prototype.eventNames = function eventNames() {\n\t  var names = []\n\t    , events\n\t    , name;\n\n\t  if (this._eventsCount === 0) return names;\n\n\t  for (name in (events = this._events)) {\n\t    if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);\n\t  }\n\n\t  if (Object.getOwnPropertySymbols) {\n\t    return names.concat(Object.getOwnPropertySymbols(events));\n\t  }\n\n\t  return names;\n\t};\n\n\t/**\n\t * Return the listeners registered for a given event.\n\t *\n\t * @param {(String|Symbol)} event The event name.\n\t * @returns {Array} The registered listeners.\n\t * @public\n\t */\n\tEventEmitter.prototype.listeners = function listeners(event) {\n\t  var evt = prefix ? prefix + event : event\n\t    , handlers = this._events[evt];\n\n\t  if (!handlers) return [];\n\t  if (handlers.fn) return [handlers.fn];\n\n\t  for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {\n\t    ee[i] = handlers[i].fn;\n\t  }\n\n\t  return ee;\n\t};\n\n\t/**\n\t * Return the number of listeners listening to a given event.\n\t *\n\t * @param {(String|Symbol)} event The event name.\n\t * @returns {Number} The number of listeners.\n\t * @public\n\t */\n\tEventEmitter.prototype.listenerCount = function listenerCount(event) {\n\t  var evt = prefix ? prefix + event : event\n\t    , listeners = this._events[evt];\n\n\t  if (!listeners) return 0;\n\t  if (listeners.fn) return 1;\n\t  return listeners.length;\n\t};\n\n\t/**\n\t * Calls each of the listeners registered for a given event.\n\t *\n\t * @param {(String|Symbol)} event The event name.\n\t * @returns {Boolean} `true` if the event had listeners, else `false`.\n\t * @public\n\t */\n\tEventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {\n\t  var evt = prefix ? prefix + event : event;\n\n\t  if (!this._events[evt]) return false;\n\n\t  var listeners = this._events[evt]\n\t    , len = arguments.length\n\t    , args\n\t    , i;\n\n\t  if (listeners.fn) {\n\t    if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);\n\n\t    switch (len) {\n\t      case 1: return listeners.fn.call(listeners.context), true;\n\t      case 2: return listeners.fn.call(listeners.context, a1), true;\n\t      case 3: return listeners.fn.call(listeners.context, a1, a2), true;\n\t      case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;\n\t      case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;\n\t      case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;\n\t    }\n\n\t    for (i = 1, args = new Array(len -1); i < len; i++) {\n\t      args[i - 1] = arguments[i];\n\t    }\n\n\t    listeners.fn.apply(listeners.context, args);\n\t  } else {\n\t    var length = listeners.length\n\t      , j;\n\n\t    for (i = 0; i < length; i++) {\n\t      if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);\n\n\t      switch (len) {\n\t        case 1: listeners[i].fn.call(listeners[i].context); break;\n\t        case 2: listeners[i].fn.call(listeners[i].context, a1); break;\n\t        case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;\n\t        case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;\n\t        default:\n\t          if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {\n\t            args[j - 1] = arguments[j];\n\t          }\n\n\t          listeners[i].fn.apply(listeners[i].context, args);\n\t      }\n\t    }\n\t  }\n\n\t  return true;\n\t};\n\n\t/**\n\t * Add a listener for a given event.\n\t *\n\t * @param {(String|Symbol)} event The event name.\n\t * @param {Function} fn The listener function.\n\t * @param {*} [context=this] The context to invoke the listener with.\n\t * @returns {EventEmitter} `this`.\n\t * @public\n\t */\n\tEventEmitter.prototype.on = function on(event, fn, context) {\n\t  return addListener(this, event, fn, context, false);\n\t};\n\n\t/**\n\t * Add a one-time listener for a given event.\n\t *\n\t * @param {(String|Symbol)} event The event name.\n\t * @param {Function} fn The listener function.\n\t * @param {*} [context=this] The context to invoke the listener with.\n\t * @returns {EventEmitter} `this`.\n\t * @public\n\t */\n\tEventEmitter.prototype.once = function once(event, fn, context) {\n\t  return addListener(this, event, fn, context, true);\n\t};\n\n\t/**\n\t * Remove the listeners of a given event.\n\t *\n\t * @param {(String|Symbol)} event The event name.\n\t * @param {Function} fn Only remove the listeners that match this function.\n\t * @param {*} context Only remove the listeners that have this context.\n\t * @param {Boolean} once Only remove one-time listeners.\n\t * @returns {EventEmitter} `this`.\n\t * @public\n\t */\n\tEventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {\n\t  var evt = prefix ? prefix + event : event;\n\n\t  if (!this._events[evt]) return this;\n\t  if (!fn) {\n\t    clearEvent(this, evt);\n\t    return this;\n\t  }\n\n\t  var listeners = this._events[evt];\n\n\t  if (listeners.fn) {\n\t    if (\n\t      listeners.fn === fn &&\n\t      (!once || listeners.once) &&\n\t      (!context || listeners.context === context)\n\t    ) {\n\t      clearEvent(this, evt);\n\t    }\n\t  } else {\n\t    for (var i = 0, events = [], length = listeners.length; i < length; i++) {\n\t      if (\n\t        listeners[i].fn !== fn ||\n\t        (once && !listeners[i].once) ||\n\t        (context && listeners[i].context !== context)\n\t      ) {\n\t        events.push(listeners[i]);\n\t      }\n\t    }\n\n\t    //\n\t    // Reset the array, or remove it completely if we have no more listeners.\n\t    //\n\t    if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;\n\t    else clearEvent(this, evt);\n\t  }\n\n\t  return this;\n\t};\n\n\t/**\n\t * Remove all listeners, or those of the specified event.\n\t *\n\t * @param {(String|Symbol)} [event] The event name.\n\t * @returns {EventEmitter} `this`.\n\t * @public\n\t */\n\tEventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {\n\t  var evt;\n\n\t  if (event) {\n\t    evt = prefix ? prefix + event : event;\n\t    if (this._events[evt]) clearEvent(this, evt);\n\t  } else {\n\t    this._events = new Events();\n\t    this._eventsCount = 0;\n\t  }\n\n\t  return this;\n\t};\n\n\t//\n\t// Alias methods names because people roll like that.\n\t//\n\tEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\n\tEventEmitter.prototype.addListener = EventEmitter.prototype.on;\n\n\t//\n\t// Expose the prefix.\n\t//\n\tEventEmitter.prefixed = prefix;\n\n\t//\n\t// Allow `EventEmitter` to be imported as module namespace.\n\t//\n\tEventEmitter.EventEmitter = EventEmitter;\n\n\t//\n\t// Expose the module.\n\t//\n\t{\n\t  module.exports = EventEmitter;\n\t}\n} (eventemitter3));\n\nvar eventemitter3Exports = eventemitter3.exports;\nvar EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);\n\nconst MediaSource$1 = getMediaSource() || {\n  isTypeSupported: () => false\n};\nclass TransmuxerInterface {\n  constructor(hls, id, onTransmuxComplete, onFlush) {\n    this.error = null;\n    this.hls = void 0;\n    this.id = void 0;\n    this.observer = void 0;\n    this.frag = null;\n    this.part = null;\n    this.useWorker = void 0;\n    this.workerContext = null;\n    this.onwmsg = void 0;\n    this.transmuxer = null;\n    this.onTransmuxComplete = void 0;\n    this.onFlush = void 0;\n    const config = hls.config;\n    this.hls = hls;\n    this.id = id;\n    this.useWorker = !!config.enableWorker;\n    this.onTransmuxComplete = onTransmuxComplete;\n    this.onFlush = onFlush;\n    const forwardMessage = (ev, data) => {\n      data = data || {};\n      data.frag = this.frag;\n      data.id = this.id;\n      if (ev === Events.ERROR) {\n        this.error = data.error;\n      }\n      this.hls.trigger(ev, data);\n    };\n\n    // forward events to main thread\n    this.observer = new EventEmitter();\n    this.observer.on(Events.FRAG_DECRYPTED, forwardMessage);\n    this.observer.on(Events.ERROR, forwardMessage);\n    const typeSupported = {\n      mp4: MediaSource$1.isTypeSupported('video/mp4'),\n      mpeg: MediaSource$1.isTypeSupported('audio/mpeg'),\n      mp3: MediaSource$1.isTypeSupported('audio/mp4; codecs=\"mp3\"')\n    };\n    // navigator.vendor is not always available in Web Worker\n    // refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator\n    const vendor = navigator.vendor;\n    if (this.useWorker && typeof Worker !== 'undefined') {\n      const canCreateWorker = config.workerPath || hasUMDWorker();\n      if (canCreateWorker) {\n        try {\n          if (config.workerPath) {\n            logger.log(`loading Web Worker ${config.workerPath} for \"${id}\"`);\n            this.workerContext = loadWorker(config.workerPath);\n          } else {\n            logger.log(`injecting Web Worker for \"${id}\"`);\n            this.workerContext = injectWorker();\n          }\n          this.onwmsg = ev => this.onWorkerMessage(ev);\n          const {\n            worker\n          } = this.workerContext;\n          worker.addEventListener('message', this.onwmsg);\n          worker.onerror = event => {\n            const error = new Error(`${event.message}  (${event.filename}:${event.lineno})`);\n            config.enableWorker = false;\n            logger.warn(`Error in \"${id}\" Web Worker, fallback to inline`);\n            this.hls.trigger(Events.ERROR, {\n              type: ErrorTypes.OTHER_ERROR,\n              details: ErrorDetails.INTERNAL_EXCEPTION,\n              fatal: false,\n              event: 'demuxerWorker',\n              error\n            });\n          };\n          worker.postMessage({\n            cmd: 'init',\n            typeSupported: typeSupported,\n            vendor: vendor,\n            id: id,\n            config: JSON.stringify(config)\n          });\n        } catch (err) {\n          logger.warn(`Error setting up \"${id}\" Web Worker, fallback to inline`, err);\n          this.resetWorker();\n          this.error = null;\n          this.transmuxer = new Transmuxer(this.observer, typeSupported, config, vendor, id);\n        }\n        return;\n      }\n    }\n    this.transmuxer = new Transmuxer(this.observer, typeSupported, config, vendor, id);\n  }\n  resetWorker() {\n    if (this.workerContext) {\n      const {\n        worker,\n        objectURL\n      } = this.workerContext;\n      if (objectURL) {\n        // revoke the Object URL that was used to create transmuxer worker, so as not to leak it\n        self.URL.revokeObjectURL(objectURL);\n      }\n      worker.removeEventListener('message', this.onwmsg);\n      worker.onerror = null;\n      worker.terminate();\n      this.workerContext = null;\n    }\n  }\n  destroy() {\n    if (this.workerContext) {\n      this.resetWorker();\n      this.onwmsg = undefined;\n    } else {\n      const transmuxer = this.transmuxer;\n      if (transmuxer) {\n        transmuxer.destroy();\n        this.transmuxer = null;\n      }\n    }\n    const observer = this.observer;\n    if (observer) {\n      observer.removeAllListeners();\n    }\n    this.frag = null;\n    // @ts-ignore\n    this.observer = null;\n    // @ts-ignore\n    this.hls = null;\n  }\n  push(data, initSegmentData, audioCodec, videoCodec, frag, part, duration, accurateTimeOffset, chunkMeta, defaultInitPTS) {\n    var _frag$initSegment, _lastFrag$initSegment;\n    chunkMeta.transmuxing.start = self.performance.now();\n    const {\n      transmuxer\n    } = this;\n    const timeOffset = part ? part.start : frag.start;\n    // TODO: push \"clear-lead\" decrypt data for unencrypted fragments in streams with encrypted ones\n    const decryptdata = frag.decryptdata;\n    const lastFrag = this.frag;\n    const discontinuity = !(lastFrag && frag.cc === lastFrag.cc);\n    const trackSwitch = !(lastFrag && chunkMeta.level === lastFrag.level);\n    const snDiff = lastFrag ? chunkMeta.sn - lastFrag.sn : -1;\n    const partDiff = this.part ? chunkMeta.part - this.part.index : -1;\n    const progressive = snDiff === 0 && chunkMeta.id > 1 && chunkMeta.id === (lastFrag == null ? void 0 : lastFrag.stats.chunkCount);\n    const contiguous = !trackSwitch && (snDiff === 1 || snDiff === 0 && (partDiff === 1 || progressive && partDiff <= 0));\n    const now = self.performance.now();\n    if (trackSwitch || snDiff || frag.stats.parsing.start === 0) {\n      frag.stats.parsing.start = now;\n    }\n    if (part && (partDiff || !contiguous)) {\n      part.stats.parsing.start = now;\n    }\n    const initSegmentChange = !(lastFrag && ((_frag$initSegment = frag.initSegment) == null ? void 0 : _frag$initSegment.url) === ((_lastFrag$initSegment = lastFrag.initSegment) == null ? void 0 : _lastFrag$initSegment.url));\n    const state = new TransmuxState(discontinuity, contiguous, accurateTimeOffset, trackSwitch, timeOffset, initSegmentChange);\n    if (!contiguous || discontinuity || initSegmentChange) {\n      logger.log(`[transmuxer-interface, ${frag.type}]: Starting new transmux session for sn: ${chunkMeta.sn} p: ${chunkMeta.part} level: ${chunkMeta.level} id: ${chunkMeta.id}\n        discontinuity: ${discontinuity}\n        trackSwitch: ${trackSwitch}\n        contiguous: ${contiguous}\n        accurateTimeOffset: ${accurateTimeOffset}\n        timeOffset: ${timeOffset}\n        initSegmentChange: ${initSegmentChange}`);\n      const config = new TransmuxConfig(audioCodec, videoCodec, initSegmentData, duration, defaultInitPTS);\n      this.configureTransmuxer(config);\n    }\n    this.frag = frag;\n    this.part = part;\n\n    // Frags with sn of 'initSegment' are not transmuxed\n    if (this.workerContext) {\n      // post fragment payload as transferable objects for ArrayBuffer (no copy)\n      this.workerContext.worker.postMessage({\n        cmd: 'demux',\n        data,\n        decryptdata,\n        chunkMeta,\n        state\n      }, data instanceof ArrayBuffer ? [data] : []);\n    } else if (transmuxer) {\n      const transmuxResult = transmuxer.push(data, decryptdata, chunkMeta, state);\n      if (isPromise(transmuxResult)) {\n        transmuxer.async = true;\n        transmuxResult.then(data => {\n          this.handleTransmuxComplete(data);\n        }).catch(error => {\n          this.transmuxerError(error, chunkMeta, 'transmuxer-interface push error');\n        });\n      } else {\n        transmuxer.async = false;\n        this.handleTransmuxComplete(transmuxResult);\n      }\n    }\n  }\n  flush(chunkMeta) {\n    chunkMeta.transmuxing.start = self.performance.now();\n    const {\n      transmuxer\n    } = this;\n    if (this.workerContext) {\n      this.workerContext.worker.postMessage({\n        cmd: 'flush',\n        chunkMeta\n      });\n    } else if (transmuxer) {\n      let transmuxResult = transmuxer.flush(chunkMeta);\n      const asyncFlush = isPromise(transmuxResult);\n      if (asyncFlush || transmuxer.async) {\n        if (!isPromise(transmuxResult)) {\n          transmuxResult = Promise.resolve(transmuxResult);\n        }\n        transmuxResult.then(data => {\n          this.handleFlushResult(data, chunkMeta);\n        }).catch(error => {\n          this.transmuxerError(error, chunkMeta, 'transmuxer-interface flush error');\n        });\n      } else {\n        this.handleFlushResult(transmuxResult, chunkMeta);\n      }\n    }\n  }\n  transmuxerError(error, chunkMeta, reason) {\n    if (!this.hls) {\n      return;\n    }\n    this.error = error;\n    this.hls.trigger(Events.ERROR, {\n      type: ErrorTypes.MEDIA_ERROR,\n      details: ErrorDetails.FRAG_PARSING_ERROR,\n      chunkMeta,\n      fatal: false,\n      error,\n      err: error,\n      reason\n    });\n  }\n  handleFlushResult(results, chunkMeta) {\n    results.forEach(result => {\n      this.handleTransmuxComplete(result);\n    });\n    this.onFlush(chunkMeta);\n  }\n  onWorkerMessage(ev) {\n    const data = ev.data;\n    const hls = this.hls;\n    switch (data.event) {\n      case 'init':\n        {\n          var _this$workerContext;\n          const objectURL = (_this$workerContext = this.workerContext) == null ? void 0 : _this$workerContext.objectURL;\n          if (objectURL) {\n            // revoke the Object URL that was used to create transmuxer worker, so as not to leak it\n            self.URL.revokeObjectURL(objectURL);\n          }\n          break;\n        }\n      case 'transmuxComplete':\n        {\n          this.handleTransmuxComplete(data.data);\n          break;\n        }\n      case 'flush':\n        {\n          this.onFlush(data.data);\n          break;\n        }\n\n      // pass logs from the worker thread to the main logger\n      case 'workerLog':\n        if (logger[data.data.logType]) {\n          logger[data.data.logType](data.data.message);\n        }\n        break;\n      default:\n        {\n          data.data = data.data || {};\n          data.data.frag = this.frag;\n          data.data.id = this.id;\n          hls.trigger(data.event, data.data);\n          break;\n        }\n    }\n  }\n  configureTransmuxer(config) {\n    const {\n      transmuxer\n    } = this;\n    if (this.workerContext) {\n      this.workerContext.worker.postMessage({\n        cmd: 'configure',\n        config\n      });\n    } else if (transmuxer) {\n      transmuxer.configure(config);\n    }\n  }\n  handleTransmuxComplete(result) {\n    result.chunkMeta.transmuxing.end = self.performance.now();\n    this.onTransmuxComplete(result);\n  }\n}\n\nconst STALL_MINIMUM_DURATION_MS = 250;\nconst MAX_START_GAP_JUMP = 2.0;\nconst SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;\nconst SKIP_BUFFER_RANGE_START = 0.05;\nclass GapController {\n  constructor(config, media, fragmentTracker, hls) {\n    this.config = void 0;\n    this.media = null;\n    this.fragmentTracker = void 0;\n    this.hls = void 0;\n    this.nudgeRetry = 0;\n    this.stallReported = false;\n    this.stalled = null;\n    this.moved = false;\n    this.seeking = false;\n    this.config = config;\n    this.media = media;\n    this.fragmentTracker = fragmentTracker;\n    this.hls = hls;\n  }\n  destroy() {\n    this.media = null;\n    // @ts-ignore\n    this.hls = this.fragmentTracker = null;\n  }\n\n  /**\n   * Checks if the playhead is stuck within a gap, and if so, attempts to free it.\n   * A gap is an unbuffered range between two buffered ranges (or the start and the first buffered range).\n   *\n   * @param lastCurrentTime - Previously read playhead position\n   */\n  poll(lastCurrentTime, activeFrag) {\n    const {\n      config,\n      media,\n      stalled\n    } = this;\n    if (media === null) {\n      return;\n    }\n    const {\n      currentTime,\n      seeking\n    } = media;\n    const seeked = this.seeking && !seeking;\n    const beginSeek = !this.seeking && seeking;\n    this.seeking = seeking;\n\n    // The playhead is moving, no-op\n    if (currentTime !== lastCurrentTime) {\n      this.moved = true;\n      if (stalled !== null) {\n        // The playhead is now moving, but was previously stalled\n        if (this.stallReported) {\n          const _stalledDuration = self.performance.now() - stalled;\n          logger.warn(`playback not stuck anymore @${currentTime}, after ${Math.round(_stalledDuration)}ms`);\n          this.stallReported = false;\n        }\n        this.stalled = null;\n        this.nudgeRetry = 0;\n      }\n      return;\n    }\n\n    // Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek\n    if (beginSeek || seeked) {\n      this.stalled = null;\n      return;\n    }\n\n    // The playhead should not be moving\n    if (media.paused && !seeking || media.ended || media.playbackRate === 0 || !BufferHelper.getBuffered(media).length) {\n      return;\n    }\n    const bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);\n    const isBuffered = bufferInfo.len > 0;\n    const nextStart = bufferInfo.nextStart || 0;\n\n    // There is no playable buffer (seeked, waiting for buffer)\n    if (!isBuffered && !nextStart) {\n      return;\n    }\n    if (seeking) {\n      // Waiting for seeking in a buffered range to complete\n      const hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP;\n      // Next buffered range is too far ahead to jump to while still seeking\n      const noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !this.fragmentTracker.getPartialFragment(currentTime);\n      if (hasEnoughBuffer || noBufferGap) {\n        return;\n      }\n      // Reset moved state when seeking to a point in or before a gap\n      this.moved = false;\n    }\n\n    // Skip start gaps if we haven't played, but the last poll detected the start of a stall\n    // The addition poll gives the browser a chance to jump the gap for us\n    if (!this.moved && this.stalled !== null) {\n      var _level$details;\n      // Jump start gaps within jump threshold\n      const startJump = Math.max(nextStart, bufferInfo.start || 0) - currentTime;\n\n      // When joining a live stream with audio tracks, account for live playlist window sliding by allowing\n      // a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment\n      // that begins over 1 target duration after the video start position.\n      const level = this.hls.levels ? this.hls.levels[this.hls.currentLevel] : null;\n      const isLive = level == null ? void 0 : (_level$details = level.details) == null ? void 0 : _level$details.live;\n      const maxStartGapJump = isLive ? level.details.targetduration * 2 : MAX_START_GAP_JUMP;\n      const partialOrGap = this.fragmentTracker.getPartialFragment(currentTime);\n      if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {\n        this._trySkipBufferHole(partialOrGap);\n        return;\n      }\n    }\n\n    // Start tracking stall time\n    const tnow = self.performance.now();\n    if (stalled === null) {\n      this.stalled = tnow;\n      return;\n    }\n    const stalledDuration = tnow - stalled;\n    if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) {\n      // Report stalling after trying to fix\n      this._reportStall(bufferInfo);\n      if (!this.media) {\n        return;\n      }\n    }\n    const bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole);\n    this._tryFixBufferStall(bufferedWithHoles, stalledDuration);\n  }\n\n  /**\n   * Detects and attempts to fix known buffer stalling issues.\n   * @param bufferInfo - The properties of the current buffer.\n   * @param stalledDurationMs - The amount of time Hls.js has been stalling for.\n   * @private\n   */\n  _tryFixBufferStall(bufferInfo, stalledDurationMs) {\n    const {\n      config,\n      fragmentTracker,\n      media\n    } = this;\n    if (media === null) {\n      return;\n    }\n    const currentTime = media.currentTime;\n    const partial = fragmentTracker.getPartialFragment(currentTime);\n    if (partial) {\n      // Try to skip over the buffer hole caused by a partial fragment\n      // This method isn't limited by the size of the gap between buffered ranges\n      const targetTime = this._trySkipBufferHole(partial);\n      // we return here in this case, meaning\n      // the branch below only executes when we haven't seeked to a new position\n      if (targetTime || !this.media) {\n        return;\n      }\n    }\n\n    // if we haven't had to skip over a buffer hole of a partial fragment\n    // we may just have to \"nudge\" the playlist as the browser decoding/rendering engine\n    // needs to cross some sort of threshold covering all source-buffers content\n    // to start playing properly.\n    if ((bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {\n      logger.warn('Trying to nudge playhead over buffer-hole');\n      // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds\n      // We only try to jump the hole if it's under the configured size\n      // Reset stalled so to rearm watchdog timer\n      this.stalled = null;\n      this._tryNudgeBuffer();\n    }\n  }\n\n  /**\n   * Triggers a BUFFER_STALLED_ERROR event, but only once per stall period.\n   * @param bufferLen - The playhead distance from the end of the current buffer segment.\n   * @private\n   */\n  _reportStall(bufferInfo) {\n    const {\n      hls,\n      media,\n      stallReported\n    } = this;\n    if (!stallReported && media) {\n      // Report stalled error once\n      this.stallReported = true;\n      const error = new Error(`Playback stalling at @${media.currentTime} due to low buffer (${JSON.stringify(bufferInfo)})`);\n      logger.warn(error.message);\n      hls.trigger(Events.ERROR, {\n        type: ErrorTypes.MEDIA_ERROR,\n        details: ErrorDetails.BUFFER_STALLED_ERROR,\n        fatal: false,\n        error,\n        buffer: bufferInfo.len\n      });\n    }\n  }\n\n  /**\n   * Attempts to fix buffer stalls by jumping over known gaps caused by partial fragments\n   * @param partial - The partial fragment found at the current time (where playback is stalling).\n   * @private\n   */\n  _trySkipBufferHole(partial) {\n    const {\n      config,\n      hls,\n      media\n    } = this;\n    if (media === null) {\n      return 0;\n    }\n\n    // Check if currentTime is between unbuffered regions of partial fragments\n    const currentTime = media.currentTime;\n    const bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);\n    const startTime = currentTime < bufferInfo.start ? bufferInfo.start : bufferInfo.nextStart;\n    if (startTime) {\n      const bufferStarved = bufferInfo.len <= config.maxBufferHole;\n      const waiting = bufferInfo.len > 0 && bufferInfo.len < 1 && media.readyState < 3;\n      const gapLength = startTime - currentTime;\n      if (gapLength > 0 && (bufferStarved || waiting)) {\n        // Only allow large gaps to be skipped if it is a start gap, or all fragments in skip range are partial\n        if (gapLength > config.maxBufferHole) {\n          const {\n            fragmentTracker\n          } = this;\n          let startGap = false;\n          if (currentTime === 0) {\n            const startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN);\n            if (startFrag && startTime < startFrag.end) {\n              startGap = true;\n            }\n          }\n          if (!startGap) {\n            const startProvisioned = partial || fragmentTracker.getAppendedFrag(currentTime, PlaylistLevelType.MAIN);\n            if (startProvisioned) {\n              let moreToLoad = false;\n              let pos = startProvisioned.end;\n              while (pos < startTime) {\n                const provisioned = fragmentTracker.getPartialFragment(pos);\n                if (provisioned) {\n                  pos += provisioned.duration;\n                } else {\n                  moreToLoad = true;\n                  break;\n                }\n              }\n              if (moreToLoad) {\n                return 0;\n              }\n            }\n          }\n        }\n        const targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);\n        logger.warn(`skipping hole, adjusting currentTime from ${currentTime} to ${targetTime}`);\n        this.moved = true;\n        this.stalled = null;\n        media.currentTime = targetTime;\n        if (partial && !partial.gap) {\n          const error = new Error(`fragment loaded with buffer holes, seeking from ${currentTime} to ${targetTime}`);\n          hls.trigger(Events.ERROR, {\n            type: ErrorTypes.MEDIA_ERROR,\n            details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,\n            fatal: false,\n            error,\n            reason: error.message,\n            frag: partial\n          });\n        }\n        return targetTime;\n      }\n    }\n    return 0;\n  }\n\n  /**\n   * Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount.\n   * @private\n   */\n  _tryNudgeBuffer() {\n    const {\n      config,\n      hls,\n      media,\n      nudgeRetry\n    } = this;\n    if (media === null) {\n      return;\n    }\n    const currentTime = media.currentTime;\n    this.nudgeRetry++;\n    if (nudgeRetry < config.nudgeMaxRetry) {\n      const targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;\n      // playback stalled in buffered area ... let's nudge currentTime to try to overcome this\n      const error = new Error(`Nudging 'currentTime' from ${currentTime} to ${targetTime}`);\n      logger.warn(error.message);\n      media.currentTime = targetTime;\n      hls.trigger(Events.ERROR, {\n        type: ErrorTypes.MEDIA_ERROR,\n        details: ErrorDetails.BUFFER_NUDGE_ON_STALL,\n        error,\n        fatal: false\n      });\n    } else {\n      const error = new Error(`Playhead still not moving while enough data buffered @${currentTime} after ${config.nudgeMaxRetry} nudges`);\n      logger.error(error.message);\n      hls.trigger(Events.ERROR, {\n        type: ErrorTypes.MEDIA_ERROR,\n        details: ErrorDetails.BUFFER_STALLED_ERROR,\n        error,\n        fatal: true\n      });\n    }\n  }\n}\n\nconst TICK_INTERVAL$2 = 100; // how often to tick in ms\n\nclass StreamController extends BaseStreamController {\n  constructor(hls, fragmentTracker, keyLoader) {\n    super(hls, fragmentTracker, keyLoader, '[stream-controller]', PlaylistLevelType.MAIN);\n    this.audioCodecSwap = false;\n    this.gapController = null;\n    this.level = -1;\n    this._forceStartLoad = false;\n    this.altAudio = false;\n    this.audioOnly = false;\n    this.fragPlaying = null;\n    this.onvplaying = null;\n    this.onvseeked = null;\n    this.fragLastKbps = 0;\n    this.couldBacktrack = false;\n    this.backtrackFragment = null;\n    this.audioCodecSwitch = false;\n    this.videoBuffer = null;\n    this._registerListeners();\n  }\n  _registerListeners() {\n    const {\n      hls\n    } = this;\n    hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);\n    hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);\n    hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);\n    hls.on(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);\n    hls.on(Events.ERROR, this.onError, this);\n    hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);\n    hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);\n    hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);\n    hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);\n    hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);\n    hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);\n  }\n  _unregisterListeners() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);\n    hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);\n    hls.off(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);\n    hls.off(Events.ERROR, this.onError, this);\n    hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);\n    hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);\n    hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);\n    hls.off(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);\n    hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);\n    hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);\n  }\n  onHandlerDestroying() {\n    this._unregisterListeners();\n    this.onMediaDetaching();\n  }\n  startLoad(startPosition) {\n    if (this.levels) {\n      const {\n        lastCurrentTime,\n        hls\n      } = this;\n      this.stopLoad();\n      this.setInterval(TICK_INTERVAL$2);\n      this.level = -1;\n      if (!this.startFragRequested) {\n        // determine load level\n        let startLevel = hls.startLevel;\n        if (startLevel === -1) {\n          if (hls.config.testBandwidth && this.levels.length > 1) {\n            // -1 : guess start Level by doing a bitrate test by loading first fragment of lowest quality level\n            startLevel = 0;\n            this.bitrateTest = true;\n          } else {\n            startLevel = hls.nextAutoLevel;\n          }\n        }\n        // set new level to playlist loader : this will trigger start level load\n        // hls.nextLoadLevel remains until it is set to a new value or until a new frag is successfully loaded\n        this.level = hls.nextLoadLevel = startLevel;\n        this.loadedmetadata = false;\n      }\n      // if startPosition undefined but lastCurrentTime set, set startPosition to last currentTime\n      if (lastCurrentTime > 0 && startPosition === -1) {\n        this.log(`Override startPosition with lastCurrentTime @${lastCurrentTime.toFixed(3)}`);\n        startPosition = lastCurrentTime;\n      }\n      this.state = State.IDLE;\n      this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition;\n      this.tick();\n    } else {\n      this._forceStartLoad = true;\n      this.state = State.STOPPED;\n    }\n  }\n  stopLoad() {\n    this._forceStartLoad = false;\n    super.stopLoad();\n  }\n  doTick() {\n    switch (this.state) {\n      case State.WAITING_LEVEL:\n        {\n          var _levels$level;\n          const {\n            levels,\n            level\n          } = this;\n          const details = levels == null ? void 0 : (_levels$level = levels[level]) == null ? void 0 : _levels$level.details;\n          if (details && (!details.live || this.levelLastLoaded === this.level)) {\n            if (this.waitForCdnTuneIn(details)) {\n              break;\n            }\n            this.state = State.IDLE;\n            break;\n          } else if (this.hls.nextLoadLevel !== this.level) {\n            this.state = State.IDLE;\n            break;\n          }\n          break;\n        }\n      case State.FRAG_LOADING_WAITING_RETRY:\n        {\n          var _this$media;\n          const now = self.performance.now();\n          const retryDate = this.retryDate;\n          // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading\n          if (!retryDate || now >= retryDate || (_this$media = this.media) != null && _this$media.seeking) {\n            this.resetStartWhenNotLoaded(this.level);\n            this.state = State.IDLE;\n          }\n        }\n        break;\n    }\n    if (this.state === State.IDLE) {\n      this.doTickIdle();\n    }\n    this.onTickEnd();\n  }\n  onTickEnd() {\n    super.onTickEnd();\n    this.checkBuffer();\n    this.checkFragmentChanged();\n  }\n  doTickIdle() {\n    const {\n      hls,\n      levelLastLoaded,\n      levels,\n      media\n    } = this;\n    const {\n      config,\n      nextLoadLevel: level\n    } = hls;\n\n    // if start level not parsed yet OR\n    // if video not attached AND start fragment already requested OR start frag prefetch not enabled\n    // exit loop, as we either need more info (level not parsed) or we need media to be attached to load new fragment\n    if (levelLastLoaded === null || !media && (this.startFragRequested || !config.startFragPrefetch)) {\n      return;\n    }\n\n    // If the \"main\" level is audio-only but we are loading an alternate track in the same group, do not load anything\n    if (this.altAudio && this.audioOnly) {\n      return;\n    }\n    if (!(levels != null && levels[level])) {\n      return;\n    }\n    const levelInfo = levels[level];\n\n    // if buffer length is less than maxBufLen try to load a new fragment\n\n    const bufferInfo = this.getMainFwdBufferInfo();\n    if (bufferInfo === null) {\n      return;\n    }\n    const lastDetails = this.getLevelDetails();\n    if (lastDetails && this._streamEnded(bufferInfo, lastDetails)) {\n      const data = {};\n      if (this.altAudio) {\n        data.type = 'video';\n      }\n      this.hls.trigger(Events.BUFFER_EOS, data);\n      this.state = State.ENDED;\n      return;\n    }\n\n    // set next load level : this will trigger a playlist load if needed\n    if (hls.loadLevel !== level && hls.manualLevel === -1) {\n      this.log(`Adapting to level ${level} from level ${this.level}`);\n    }\n    this.level = hls.nextLoadLevel = level;\n    const levelDetails = levelInfo.details;\n    // if level info not retrieved yet, switch state and wait for level retrieval\n    // if live playlist, ensure that new playlist has been refreshed to avoid loading/try to load\n    // a useless and outdated fragment (that might even introduce load error if it is already out of the live playlist)\n    if (!levelDetails || this.state === State.WAITING_LEVEL || levelDetails.live && this.levelLastLoaded !== level) {\n      this.level = level;\n      this.state = State.WAITING_LEVEL;\n      return;\n    }\n    const bufferLen = bufferInfo.len;\n\n    // compute max Buffer Length that we could get from this load level, based on level bitrate. don't buffer more than 60 MB and more than 30s\n    const maxBufLen = this.getMaxBufferLength(levelInfo.maxBitrate);\n\n    // Stay idle if we are still with buffer margins\n    if (bufferLen >= maxBufLen) {\n      return;\n    }\n    if (this.backtrackFragment && this.backtrackFragment.start > bufferInfo.end) {\n      this.backtrackFragment = null;\n    }\n    const targetBufferTime = this.backtrackFragment ? this.backtrackFragment.start : bufferInfo.end;\n    let frag = this.getNextFragment(targetBufferTime, levelDetails);\n    // Avoid backtracking by loading an earlier segment in streams with segments that do not start with a key frame (flagged by `couldBacktrack`)\n    if (this.couldBacktrack && !this.fragPrevious && frag && frag.sn !== 'initSegment' && this.fragmentTracker.getState(frag) !== FragmentState.OK) {\n      var _this$backtrackFragme;\n      const backtrackSn = ((_this$backtrackFragme = this.backtrackFragment) != null ? _this$backtrackFragme : frag).sn;\n      const fragIdx = backtrackSn - levelDetails.startSN;\n      const backtrackFrag = levelDetails.fragments[fragIdx - 1];\n      if (backtrackFrag && frag.cc === backtrackFrag.cc) {\n        frag = backtrackFrag;\n        this.fragmentTracker.removeFragment(backtrackFrag);\n      }\n    } else if (this.backtrackFragment && bufferInfo.len) {\n      this.backtrackFragment = null;\n    }\n    // Avoid loop loading by using nextLoadPosition set for backtracking and skipping consecutive GAP tags\n    if (frag && this.isLoopLoading(frag, targetBufferTime)) {\n      const gapStart = frag.gap;\n      if (!gapStart) {\n        // Cleanup the fragment tracker before trying to find the next unbuffered fragment\n        const type = this.audioOnly && !this.altAudio ? ElementaryStreamTypes.AUDIO : ElementaryStreamTypes.VIDEO;\n        const mediaBuffer = (type === ElementaryStreamTypes.VIDEO ? this.videoBuffer : this.mediaBuffer) || this.media;\n        if (mediaBuffer) {\n          this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.MAIN);\n        }\n      }\n      frag = this.getNextFragmentLoopLoading(frag, levelDetails, bufferInfo, PlaylistLevelType.MAIN, maxBufLen);\n    }\n    if (!frag) {\n      return;\n    }\n    if (frag.initSegment && !frag.initSegment.data && !this.bitrateTest) {\n      frag = frag.initSegment;\n    }\n    this.loadFragment(frag, levelInfo, targetBufferTime);\n  }\n  loadFragment(frag, level, targetBufferTime) {\n    // Check if fragment is not loaded\n    const fragState = this.fragmentTracker.getState(frag);\n    this.fragCurrent = frag;\n    if (fragState === FragmentState.NOT_LOADED || fragState === FragmentState.PARTIAL) {\n      if (frag.sn === 'initSegment') {\n        this._loadInitSegment(frag, level);\n      } else if (this.bitrateTest) {\n        this.log(`Fragment ${frag.sn} of level ${frag.level} is being downloaded to test bitrate and will not be buffered`);\n        this._loadBitrateTestFrag(frag, level);\n      } else {\n        this.startFragRequested = true;\n        super.loadFragment(frag, level, targetBufferTime);\n      }\n    } else {\n      this.clearTrackerIfNeeded(frag);\n    }\n  }\n  getBufferedFrag(position) {\n    return this.fragmentTracker.getBufferedFrag(position, PlaylistLevelType.MAIN);\n  }\n  followingBufferedFrag(frag) {\n    if (frag) {\n      // try to get range of next fragment (500ms after this range)\n      return this.getBufferedFrag(frag.end + 0.5);\n    }\n    return null;\n  }\n\n  /*\n    on immediate level switch :\n     - pause playback if playing\n     - cancel any pending load request\n     - and trigger a buffer flush\n  */\n  immediateLevelSwitch() {\n    this.abortCurrentFrag();\n    this.flushMainBuffer(0, Number.POSITIVE_INFINITY);\n  }\n\n  /**\n   * try to switch ASAP without breaking video playback:\n   * in order to ensure smooth but quick level switching,\n   * we need to find the next flushable buffer range\n   * we should take into account new segment fetch time\n   */\n  nextLevelSwitch() {\n    const {\n      levels,\n      media\n    } = this;\n    // ensure that media is defined and that metadata are available (to retrieve currentTime)\n    if (media != null && media.readyState) {\n      let fetchdelay;\n      const fragPlayingCurrent = this.getAppendedFrag(media.currentTime);\n      if (fragPlayingCurrent && fragPlayingCurrent.start > 1) {\n        // flush buffer preceding current fragment (flush until current fragment start offset)\n        // minus 1s to avoid video freezing, that could happen if we flush keyframe of current video ...\n        this.flushMainBuffer(0, fragPlayingCurrent.start - 1);\n      }\n      const levelDetails = this.getLevelDetails();\n      if (levelDetails != null && levelDetails.live) {\n        const bufferInfo = this.getMainFwdBufferInfo();\n        // Do not flush in live stream with low buffer\n        if (!bufferInfo || bufferInfo.len < levelDetails.targetduration * 2) {\n          return;\n        }\n      }\n      if (!media.paused && levels) {\n        // add a safety delay of 1s\n        const nextLevelId = this.hls.nextLoadLevel;\n        const nextLevel = levels[nextLevelId];\n        const fragLastKbps = this.fragLastKbps;\n        if (fragLastKbps && this.fragCurrent) {\n          fetchdelay = this.fragCurrent.duration * nextLevel.maxBitrate / (1000 * fragLastKbps) + 1;\n        } else {\n          fetchdelay = 0;\n        }\n      } else {\n        fetchdelay = 0;\n      }\n      // this.log('fetchdelay:'+fetchdelay);\n      // find buffer range that will be reached once new fragment will be fetched\n      const bufferedFrag = this.getBufferedFrag(media.currentTime + fetchdelay);\n      if (bufferedFrag) {\n        // we can flush buffer range following this one without stalling playback\n        const nextBufferedFrag = this.followingBufferedFrag(bufferedFrag);\n        if (nextBufferedFrag) {\n          // if we are here, we can also cancel any loading/demuxing in progress, as they are useless\n          this.abortCurrentFrag();\n          // start flush position is in next buffered frag. Leave some padding for non-independent segments and smoother playback.\n          const maxStart = nextBufferedFrag.maxStartPTS ? nextBufferedFrag.maxStartPTS : nextBufferedFrag.start;\n          const fragDuration = nextBufferedFrag.duration;\n          const startPts = Math.max(bufferedFrag.end, maxStart + Math.min(Math.max(fragDuration - this.config.maxFragLookUpTolerance, fragDuration * 0.5), fragDuration * 0.75));\n          this.flushMainBuffer(startPts, Number.POSITIVE_INFINITY);\n        }\n      }\n    }\n  }\n  abortCurrentFrag() {\n    const fragCurrent = this.fragCurrent;\n    this.fragCurrent = null;\n    this.backtrackFragment = null;\n    if (fragCurrent) {\n      fragCurrent.abortRequests();\n      this.fragmentTracker.removeFragment(fragCurrent);\n    }\n    switch (this.state) {\n      case State.KEY_LOADING:\n      case State.FRAG_LOADING:\n      case State.FRAG_LOADING_WAITING_RETRY:\n      case State.PARSING:\n      case State.PARSED:\n        this.state = State.IDLE;\n        break;\n    }\n    this.nextLoadPosition = this.getLoadPosition();\n  }\n  flushMainBuffer(startOffset, endOffset) {\n    super.flushMainBuffer(startOffset, endOffset, this.altAudio ? 'video' : null);\n  }\n  onMediaAttached(event, data) {\n    super.onMediaAttached(event, data);\n    const media = data.media;\n    this.onvplaying = this.onMediaPlaying.bind(this);\n    this.onvseeked = this.onMediaSeeked.bind(this);\n    media.addEventListener('playing', this.onvplaying);\n    media.addEventListener('seeked', this.onvseeked);\n    this.gapController = new GapController(this.config, media, this.fragmentTracker, this.hls);\n  }\n  onMediaDetaching() {\n    const {\n      media\n    } = this;\n    if (media && this.onvplaying && this.onvseeked) {\n      media.removeEventListener('playing', this.onvplaying);\n      media.removeEventListener('seeked', this.onvseeked);\n      this.onvplaying = this.onvseeked = null;\n      this.videoBuffer = null;\n    }\n    this.fragPlaying = null;\n    if (this.gapController) {\n      this.gapController.destroy();\n      this.gapController = null;\n    }\n    super.onMediaDetaching();\n  }\n  onMediaPlaying() {\n    // tick to speed up FRAG_CHANGED triggering\n    this.tick();\n  }\n  onMediaSeeked() {\n    const media = this.media;\n    const currentTime = media ? media.currentTime : null;\n    if (isFiniteNumber(currentTime)) {\n      this.log(`Media seeked to ${currentTime.toFixed(3)}`);\n    }\n\n    // If seeked was issued before buffer was appended do not tick immediately\n    const bufferInfo = this.getMainFwdBufferInfo();\n    if (bufferInfo === null || bufferInfo.len === 0) {\n      this.warn(`Main forward buffer length on \"seeked\" event ${bufferInfo ? bufferInfo.len : 'empty'})`);\n      return;\n    }\n\n    // tick to speed up FRAG_CHANGED triggering\n    this.tick();\n  }\n  onManifestLoading() {\n    // reset buffer on manifest loading\n    this.log('Trigger BUFFER_RESET');\n    this.hls.trigger(Events.BUFFER_RESET, undefined);\n    this.fragmentTracker.removeAllFragments();\n    this.couldBacktrack = false;\n    this.startPosition = this.lastCurrentTime = 0;\n    this.levels = this.fragPlaying = this.backtrackFragment = null;\n    this.altAudio = this.audioOnly = false;\n  }\n  onManifestParsed(event, data) {\n    let aac = false;\n    let heaac = false;\n    let codec;\n    data.levels.forEach(level => {\n      // detect if we have different kind of audio codecs used amongst playlists\n      codec = level.audioCodec;\n      if (codec) {\n        if (codec.indexOf('mp4a.40.2') !== -1) {\n          aac = true;\n        }\n        if (codec.indexOf('mp4a.40.5') !== -1) {\n          heaac = true;\n        }\n      }\n    });\n    this.audioCodecSwitch = aac && heaac && !changeTypeSupported();\n    if (this.audioCodecSwitch) {\n      this.log('Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC');\n    }\n    this.levels = data.levels;\n    this.startFragRequested = false;\n  }\n  onLevelLoading(event, data) {\n    const {\n      levels\n    } = this;\n    if (!levels || this.state !== State.IDLE) {\n      return;\n    }\n    const level = levels[data.level];\n    if (!level.details || level.details.live && this.levelLastLoaded !== data.level || this.waitForCdnTuneIn(level.details)) {\n      this.state = State.WAITING_LEVEL;\n    }\n  }\n  onLevelLoaded(event, data) {\n    var _curLevel$details;\n    const {\n      levels\n    } = this;\n    const newLevelId = data.level;\n    const newDetails = data.details;\n    const duration = newDetails.totalduration;\n    if (!levels) {\n      this.warn(`Levels were reset while loading level ${newLevelId}`);\n      return;\n    }\n    this.log(`Level ${newLevelId} loaded [${newDetails.startSN},${newDetails.endSN}]${newDetails.lastPartSn ? `[part-${newDetails.lastPartSn}-${newDetails.lastPartIndex}]` : ''}, cc [${newDetails.startCC}, ${newDetails.endCC}] duration:${duration}`);\n    const curLevel = levels[newLevelId];\n    const fragCurrent = this.fragCurrent;\n    if (fragCurrent && (this.state === State.FRAG_LOADING || this.state === State.FRAG_LOADING_WAITING_RETRY)) {\n      if ((fragCurrent.level !== data.level || fragCurrent.urlId !== curLevel.urlId) && fragCurrent.loader) {\n        this.abortCurrentFrag();\n      }\n    }\n    let sliding = 0;\n    if (newDetails.live || (_curLevel$details = curLevel.details) != null && _curLevel$details.live) {\n      this.checkLiveUpdate(newDetails);\n      if (newDetails.deltaUpdateFailed) {\n        return;\n      }\n      sliding = this.alignPlaylists(newDetails, curLevel.details);\n    }\n    // override level info\n    curLevel.details = newDetails;\n    this.levelLastLoaded = newLevelId;\n    this.hls.trigger(Events.LEVEL_UPDATED, {\n      details: newDetails,\n      level: newLevelId\n    });\n\n    // only switch back to IDLE state if we were waiting for level to start downloading a new fragment\n    if (this.state === State.WAITING_LEVEL) {\n      if (this.waitForCdnTuneIn(newDetails)) {\n        // Wait for Low-Latency CDN Tune-in\n        return;\n      }\n      this.state = State.IDLE;\n    }\n    if (!this.startFragRequested) {\n      this.setStartPosition(newDetails, sliding);\n    } else if (newDetails.live) {\n      this.synchronizeToLiveEdge(newDetails);\n    }\n\n    // trigger handler right now\n    this.tick();\n  }\n  _handleFragmentLoadProgress(data) {\n    var _frag$initSegment;\n    const {\n      frag,\n      part,\n      payload\n    } = data;\n    const {\n      levels\n    } = this;\n    if (!levels) {\n      this.warn(`Levels were reset while fragment load was in progress. Fragment ${frag.sn} of level ${frag.level} will not be buffered`);\n      return;\n    }\n    const currentLevel = levels[frag.level];\n    const details = currentLevel.details;\n    if (!details) {\n      this.warn(`Dropping fragment ${frag.sn} of level ${frag.level} after level details were reset`);\n      this.fragmentTracker.removeFragment(frag);\n      return;\n    }\n    const videoCodec = currentLevel.videoCodec;\n\n    // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live)\n    const accurateTimeOffset = details.PTSKnown || !details.live;\n    const initSegmentData = (_frag$initSegment = frag.initSegment) == null ? void 0 : _frag$initSegment.data;\n    const audioCodec = this._getAudioCodec(currentLevel);\n\n    // transmux the MPEG-TS data to ISO-BMFF segments\n    // this.log(`Transmuxing ${frag.sn} of [${details.startSN} ,${details.endSN}],level ${frag.level}, cc ${frag.cc}`);\n    const transmuxer = this.transmuxer = this.transmuxer || new TransmuxerInterface(this.hls, PlaylistLevelType.MAIN, this._handleTransmuxComplete.bind(this), this._handleTransmuxerFlush.bind(this));\n    const partIndex = part ? part.index : -1;\n    const partial = partIndex !== -1;\n    const chunkMeta = new ChunkMetadata(frag.level, frag.sn, frag.stats.chunkCount, payload.byteLength, partIndex, partial);\n    const initPTS = this.initPTS[frag.cc];\n    transmuxer.push(payload, initSegmentData, audioCodec, videoCodec, frag, part, details.totalduration, accurateTimeOffset, chunkMeta, initPTS);\n  }\n  onAudioTrackSwitching(event, data) {\n    // if any URL found on new audio track, it is an alternate audio track\n    const fromAltAudio = this.altAudio;\n    const altAudio = !!data.url;\n    // if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered\n    // don't do anything if we switch to alt audio: audio stream controller is handling it.\n    // we will just have to change buffer scheduling on audioTrackSwitched\n    if (!altAudio) {\n      if (this.mediaBuffer !== this.media) {\n        this.log('Switching on main audio, use media.buffered to schedule main fragment loading');\n        this.mediaBuffer = this.media;\n        const fragCurrent = this.fragCurrent;\n        // we need to refill audio buffer from main: cancel any frag loading to speed up audio switch\n        if (fragCurrent) {\n          this.log('Switching to main audio track, cancel main fragment load');\n          fragCurrent.abortRequests();\n          this.fragmentTracker.removeFragment(fragCurrent);\n        }\n        // destroy transmuxer to force init segment generation (following audio switch)\n        this.resetTransmuxer();\n        // switch to IDLE state to load new fragment\n        this.resetLoadingState();\n      } else if (this.audioOnly) {\n        // Reset audio transmuxer so when switching back to main audio we're not still appending where we left off\n        this.resetTransmuxer();\n      }\n      const hls = this.hls;\n      // If switching from alt to main audio, flush all audio and trigger track switched\n      if (fromAltAudio) {\n        hls.trigger(Events.BUFFER_FLUSHING, {\n          startOffset: 0,\n          endOffset: Number.POSITIVE_INFINITY,\n          type: null\n        });\n        this.fragmentTracker.removeAllFragments();\n      }\n      hls.trigger(Events.AUDIO_TRACK_SWITCHED, data);\n    }\n  }\n  onAudioTrackSwitched(event, data) {\n    const trackId = data.id;\n    const altAudio = !!this.hls.audioTracks[trackId].url;\n    if (altAudio) {\n      const videoBuffer = this.videoBuffer;\n      // if we switched on alternate audio, ensure that main fragment scheduling is synced with video sourcebuffer buffered\n      if (videoBuffer && this.mediaBuffer !== videoBuffer) {\n        this.log('Switching on alternate audio, use video.buffered to schedule main fragment loading');\n        this.mediaBuffer = videoBuffer;\n      }\n    }\n    this.altAudio = altAudio;\n    this.tick();\n  }\n  onBufferCreated(event, data) {\n    const tracks = data.tracks;\n    let mediaTrack;\n    let name;\n    let alternate = false;\n    for (const type in tracks) {\n      const track = tracks[type];\n      if (track.id === 'main') {\n        name = type;\n        mediaTrack = track;\n        // keep video source buffer reference\n        if (type === 'video') {\n          const videoTrack = tracks[type];\n          if (videoTrack) {\n            this.videoBuffer = videoTrack.buffer;\n          }\n        }\n      } else {\n        alternate = true;\n      }\n    }\n    if (alternate && mediaTrack) {\n      this.log(`Alternate track found, use ${name}.buffered to schedule main fragment loading`);\n      this.mediaBuffer = mediaTrack.buffer;\n    } else {\n      this.mediaBuffer = this.media;\n    }\n  }\n  onFragBuffered(event, data) {\n    const {\n      frag,\n      part\n    } = data;\n    if (frag && frag.type !== PlaylistLevelType.MAIN) {\n      return;\n    }\n    if (this.fragContextChanged(frag)) {\n      // If a level switch was requested while a fragment was buffering, it will emit the FRAG_BUFFERED event upon completion\n      // Avoid setting state back to IDLE, since that will interfere with a level switch\n      this.warn(`Fragment ${frag.sn}${part ? ' p: ' + part.index : ''} of level ${frag.level} finished buffering, but was aborted. state: ${this.state}`);\n      if (this.state === State.PARSED) {\n        this.state = State.IDLE;\n      }\n      return;\n    }\n    const stats = part ? part.stats : frag.stats;\n    this.fragLastKbps = Math.round(8 * stats.total / (stats.buffering.end - stats.loading.first));\n    if (frag.sn !== 'initSegment') {\n      this.fragPrevious = frag;\n    }\n    this.fragBufferedComplete(frag, part);\n  }\n  onError(event, data) {\n    var _data$context;\n    if (data.fatal) {\n      this.state = State.ERROR;\n      return;\n    }\n    switch (data.details) {\n      case ErrorDetails.FRAG_GAP:\n      case ErrorDetails.FRAG_PARSING_ERROR:\n      case ErrorDetails.FRAG_DECRYPT_ERROR:\n      case ErrorDetails.FRAG_LOAD_ERROR:\n      case ErrorDetails.FRAG_LOAD_TIMEOUT:\n      case ErrorDetails.KEY_LOAD_ERROR:\n      case ErrorDetails.KEY_LOAD_TIMEOUT:\n        this.onFragmentOrKeyLoadError(PlaylistLevelType.MAIN, data);\n        break;\n      case ErrorDetails.LEVEL_LOAD_ERROR:\n      case ErrorDetails.LEVEL_LOAD_TIMEOUT:\n      case ErrorDetails.LEVEL_PARSING_ERROR:\n        // in case of non fatal error while loading level, if level controller is not retrying to load level, switch back to IDLE\n        if (!data.levelRetry && this.state === State.WAITING_LEVEL && ((_data$context = data.context) == null ? void 0 : _data$context.type) === PlaylistContextType.LEVEL) {\n          this.state = State.IDLE;\n        }\n        break;\n      case ErrorDetails.BUFFER_FULL_ERROR:\n        if (!data.parent || data.parent !== 'main') {\n          return;\n        }\n        if (this.reduceLengthAndFlushBuffer(data)) {\n          this.flushMainBuffer(0, Number.POSITIVE_INFINITY);\n        }\n        break;\n      case ErrorDetails.INTERNAL_EXCEPTION:\n        this.recoverWorkerError(data);\n        break;\n    }\n  }\n\n  // Checks the health of the buffer and attempts to resolve playback stalls.\n  checkBuffer() {\n    const {\n      media,\n      gapController\n    } = this;\n    if (!media || !gapController || !media.readyState) {\n      // Exit early if we don't have media or if the media hasn't buffered anything yet (readyState 0)\n      return;\n    }\n    if (this.loadedmetadata || !BufferHelper.getBuffered(media).length) {\n      // Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers\n      const activeFrag = this.state !== State.IDLE ? this.fragCurrent : null;\n      gapController.poll(this.lastCurrentTime, activeFrag);\n    }\n    this.lastCurrentTime = media.currentTime;\n  }\n  onFragLoadEmergencyAborted() {\n    this.state = State.IDLE;\n    // if loadedmetadata is not set, it means that we are emergency switch down on first frag\n    // in that case, reset startFragRequested flag\n    if (!this.loadedmetadata) {\n      this.startFragRequested = false;\n      this.nextLoadPosition = this.startPosition;\n    }\n    this.tickImmediate();\n  }\n  onBufferFlushed(event, {\n    type\n  }) {\n    if (type !== ElementaryStreamTypes.AUDIO || this.audioOnly && !this.altAudio) {\n      const mediaBuffer = (type === ElementaryStreamTypes.VIDEO ? this.videoBuffer : this.mediaBuffer) || this.media;\n      this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.MAIN);\n    }\n  }\n  onLevelsUpdated(event, data) {\n    this.levels = data.levels;\n  }\n  swapAudioCodec() {\n    this.audioCodecSwap = !this.audioCodecSwap;\n  }\n\n  /**\n   * Seeks to the set startPosition if not equal to the mediaElement's current time.\n   */\n  seekToStartPos() {\n    const {\n      media\n    } = this;\n    if (!media) {\n      return;\n    }\n    const currentTime = media.currentTime;\n    let startPosition = this.startPosition;\n    // only adjust currentTime if different from startPosition or if startPosition not buffered\n    // at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered\n    if (startPosition >= 0 && currentTime < startPosition) {\n      if (media.seeking) {\n        this.log(`could not seek to ${startPosition}, already seeking at ${currentTime}`);\n        return;\n      }\n      const buffered = BufferHelper.getBuffered(media);\n      const bufferStart = buffered.length ? buffered.start(0) : 0;\n      const delta = bufferStart - startPosition;\n      if (delta > 0 && (delta < this.config.maxBufferHole || delta < this.config.maxFragLookUpTolerance)) {\n        this.log(`adjusting start position by ${delta} to match buffer start`);\n        startPosition += delta;\n        this.startPosition = startPosition;\n      }\n      this.log(`seek to target start position ${startPosition} from current time ${currentTime}`);\n      media.currentTime = startPosition;\n    }\n  }\n  _getAudioCodec(currentLevel) {\n    let audioCodec = this.config.defaultAudioCodec || currentLevel.audioCodec;\n    if (this.audioCodecSwap && audioCodec) {\n      this.log('Swapping audio codec');\n      if (audioCodec.indexOf('mp4a.40.5') !== -1) {\n        audioCodec = 'mp4a.40.2';\n      } else {\n        audioCodec = 'mp4a.40.5';\n      }\n    }\n    return audioCodec;\n  }\n  _loadBitrateTestFrag(frag, level) {\n    frag.bitrateTest = true;\n    this._doFragLoad(frag, level).then(data => {\n      const {\n        hls\n      } = this;\n      if (!data || this.fragContextChanged(frag)) {\n        return;\n      }\n      level.fragmentError = 0;\n      this.state = State.IDLE;\n      this.startFragRequested = false;\n      this.bitrateTest = false;\n      const stats = frag.stats;\n      // Bitrate tests fragments are neither parsed nor buffered\n      stats.parsing.start = stats.parsing.end = stats.buffering.start = stats.buffering.end = self.performance.now();\n      hls.trigger(Events.FRAG_LOADED, data);\n      frag.bitrateTest = false;\n    });\n  }\n  _handleTransmuxComplete(transmuxResult) {\n    var _id3$samples;\n    const id = 'main';\n    const {\n      hls\n    } = this;\n    const {\n      remuxResult,\n      chunkMeta\n    } = transmuxResult;\n    const context = this.getCurrentContext(chunkMeta);\n    if (!context) {\n      this.resetWhenMissingContext(chunkMeta);\n      return;\n    }\n    const {\n      frag,\n      part,\n      level\n    } = context;\n    const {\n      video,\n      text,\n      id3,\n      initSegment\n    } = remuxResult;\n    const {\n      details\n    } = level;\n    // The audio-stream-controller handles audio buffering if Hls.js is playing an alternate audio track\n    const audio = this.altAudio ? undefined : remuxResult.audio;\n\n    // Check if the current fragment has been aborted. We check this by first seeing if we're still playing the current level.\n    // If we are, subsequently check if the currently loading fragment (fragCurrent) has changed.\n    if (this.fragContextChanged(frag)) {\n      this.fragmentTracker.removeFragment(frag);\n      return;\n    }\n    this.state = State.PARSING;\n    if (initSegment) {\n      if (initSegment != null && initSegment.tracks) {\n        const mapFragment = frag.initSegment || frag;\n        this._bufferInitSegment(level, initSegment.tracks, mapFragment, chunkMeta);\n        hls.trigger(Events.FRAG_PARSING_INIT_SEGMENT, {\n          frag: mapFragment,\n          id,\n          tracks: initSegment.tracks\n        });\n      }\n\n      // This would be nice if Number.isFinite acted as a typeguard, but it doesn't. See: https://github.com/Microsoft/TypeScript/issues/10038\n      const initPTS = initSegment.initPTS;\n      const timescale = initSegment.timescale;\n      if (isFiniteNumber(initPTS)) {\n        this.initPTS[frag.cc] = {\n          baseTime: initPTS,\n          timescale\n        };\n        hls.trigger(Events.INIT_PTS_FOUND, {\n          frag,\n          id,\n          initPTS,\n          timescale\n        });\n      }\n    }\n\n    // Avoid buffering if backtracking this fragment\n    if (video && details && frag.sn !== 'initSegment') {\n      const prevFrag = details.fragments[frag.sn - 1 - details.startSN];\n      const isFirstFragment = frag.sn === details.startSN;\n      const isFirstInDiscontinuity = !prevFrag || frag.cc > prevFrag.cc;\n      if (remuxResult.independent !== false) {\n        const {\n          startPTS,\n          endPTS,\n          startDTS,\n          endDTS\n        } = video;\n        if (part) {\n          part.elementaryStreams[video.type] = {\n            startPTS,\n            endPTS,\n            startDTS,\n            endDTS\n          };\n        } else {\n          if (video.firstKeyFrame && video.independent && chunkMeta.id === 1 && !isFirstInDiscontinuity) {\n            this.couldBacktrack = true;\n          }\n          if (video.dropped && video.independent) {\n            // Backtrack if dropped frames create a gap after currentTime\n\n            const bufferInfo = this.getMainFwdBufferInfo();\n            const targetBufferTime = (bufferInfo ? bufferInfo.end : this.getLoadPosition()) + this.config.maxBufferHole;\n            const startTime = video.firstKeyFramePTS ? video.firstKeyFramePTS : startPTS;\n            if (!isFirstFragment && targetBufferTime < startTime - this.config.maxBufferHole && !isFirstInDiscontinuity) {\n              this.backtrack(frag);\n              return;\n            } else if (isFirstInDiscontinuity) {\n              // Mark segment with a gap to avoid loop loading\n              frag.gap = true;\n            }\n            // Set video stream start to fragment start so that truncated samples do not distort the timeline, and mark it partial\n            frag.setElementaryStreamInfo(video.type, frag.start, endPTS, frag.start, endDTS, true);\n          }\n        }\n        frag.setElementaryStreamInfo(video.type, startPTS, endPTS, startDTS, endDTS);\n        if (this.backtrackFragment) {\n          this.backtrackFragment = frag;\n        }\n        this.bufferFragmentData(video, frag, part, chunkMeta, isFirstFragment || isFirstInDiscontinuity);\n      } else if (isFirstFragment || isFirstInDiscontinuity) {\n        // Mark segment with a gap to avoid loop loading\n        frag.gap = true;\n      } else {\n        this.backtrack(frag);\n        return;\n      }\n    }\n    if (audio) {\n      const {\n        startPTS,\n        endPTS,\n        startDTS,\n        endDTS\n      } = audio;\n      if (part) {\n        part.elementaryStreams[ElementaryStreamTypes.AUDIO] = {\n          startPTS,\n          endPTS,\n          startDTS,\n          endDTS\n        };\n      }\n      frag.setElementaryStreamInfo(ElementaryStreamTypes.AUDIO, startPTS, endPTS, startDTS, endDTS);\n      this.bufferFragmentData(audio, frag, part, chunkMeta);\n    }\n    if (details && id3 != null && (_id3$samples = id3.samples) != null && _id3$samples.length) {\n      const emittedID3 = {\n        id,\n        frag,\n        details,\n        samples: id3.samples\n      };\n      hls.trigger(Events.FRAG_PARSING_METADATA, emittedID3);\n    }\n    if (details && text) {\n      const emittedText = {\n        id,\n        frag,\n        details,\n        samples: text.samples\n      };\n      hls.trigger(Events.FRAG_PARSING_USERDATA, emittedText);\n    }\n  }\n  _bufferInitSegment(currentLevel, tracks, frag, chunkMeta) {\n    if (this.state !== State.PARSING) {\n      return;\n    }\n    this.audioOnly = !!tracks.audio && !tracks.video;\n\n    // if audio track is expected to come from audio stream controller, discard any coming from main\n    if (this.altAudio && !this.audioOnly) {\n      delete tracks.audio;\n    }\n    // include levelCodec in audio and video tracks\n    const {\n      audio,\n      video,\n      audiovideo\n    } = tracks;\n    if (audio) {\n      let audioCodec = currentLevel.audioCodec;\n      const ua = navigator.userAgent.toLowerCase();\n      if (this.audioCodecSwitch) {\n        if (audioCodec) {\n          if (audioCodec.indexOf('mp4a.40.5') !== -1) {\n            audioCodec = 'mp4a.40.2';\n          } else {\n            audioCodec = 'mp4a.40.5';\n          }\n        }\n        // In the case that AAC and HE-AAC audio codecs are signalled in manifest,\n        // force HE-AAC, as it seems that most browsers prefers it.\n        // don't force HE-AAC if mono stream, or in Firefox\n        if (audio.metadata.channelCount !== 1 && ua.indexOf('firefox') === -1) {\n          audioCodec = 'mp4a.40.5';\n        }\n      }\n      // HE-AAC is broken on Android, always signal audio codec as AAC even if variant manifest states otherwise\n      if (ua.indexOf('android') !== -1 && audio.container !== 'audio/mpeg') {\n        // Exclude mpeg audio\n        audioCodec = 'mp4a.40.2';\n        this.log(`Android: force audio codec to ${audioCodec}`);\n      }\n      if (currentLevel.audioCodec && currentLevel.audioCodec !== audioCodec) {\n        this.log(`Swapping manifest audio codec \"${currentLevel.audioCodec}\" for \"${audioCodec}\"`);\n      }\n      audio.levelCodec = audioCodec;\n      audio.id = 'main';\n      this.log(`Init audio buffer, container:${audio.container}, codecs[selected/level/parsed]=[${audioCodec || ''}/${currentLevel.audioCodec || ''}/${audio.codec}]`);\n    }\n    if (video) {\n      video.levelCodec = currentLevel.videoCodec;\n      video.id = 'main';\n      this.log(`Init video buffer, container:${video.container}, codecs[level/parsed]=[${currentLevel.videoCodec || ''}/${video.codec}]`);\n    }\n    if (audiovideo) {\n      this.log(`Init audiovideo buffer, container:${audiovideo.container}, codecs[level/parsed]=[${currentLevel.attrs.CODECS || ''}/${audiovideo.codec}]`);\n    }\n    this.hls.trigger(Events.BUFFER_CODECS, tracks);\n    // loop through tracks that are going to be provided to bufferController\n    Object.keys(tracks).forEach(trackName => {\n      const track = tracks[trackName];\n      const initSegment = track.initSegment;\n      if (initSegment != null && initSegment.byteLength) {\n        this.hls.trigger(Events.BUFFER_APPENDING, {\n          type: trackName,\n          data: initSegment,\n          frag,\n          part: null,\n          chunkMeta,\n          parent: frag.type\n        });\n      }\n    });\n    // trigger handler right now\n    this.tick();\n  }\n  getMainFwdBufferInfo() {\n    return this.getFwdBufferInfo(this.mediaBuffer ? this.mediaBuffer : this.media, PlaylistLevelType.MAIN);\n  }\n  backtrack(frag) {\n    this.couldBacktrack = true;\n    // Causes findFragments to backtrack through fragments to find the keyframe\n    this.backtrackFragment = frag;\n    this.resetTransmuxer();\n    this.flushBufferGap(frag);\n    this.fragmentTracker.removeFragment(frag);\n    this.fragPrevious = null;\n    this.nextLoadPosition = frag.start;\n    this.state = State.IDLE;\n  }\n  checkFragmentChanged() {\n    const video = this.media;\n    let fragPlayingCurrent = null;\n    if (video && video.readyState > 1 && video.seeking === false) {\n      const currentTime = video.currentTime;\n      /* if video element is in seeked state, currentTime can only increase.\n        (assuming that playback rate is positive ...)\n        As sometimes currentTime jumps back to zero after a\n        media decode error, check this, to avoid seeking back to\n        wrong position after a media decode error\n      */\n\n      if (BufferHelper.isBuffered(video, currentTime)) {\n        fragPlayingCurrent = this.getAppendedFrag(currentTime);\n      } else if (BufferHelper.isBuffered(video, currentTime + 0.1)) {\n        /* ensure that FRAG_CHANGED event is triggered at startup,\n          when first video frame is displayed and playback is paused.\n          add a tolerance of 100ms, in case current position is not buffered,\n          check if current pos+100ms is buffered and use that buffer range\n          for FRAG_CHANGED event reporting */\n        fragPlayingCurrent = this.getAppendedFrag(currentTime + 0.1);\n      }\n      if (fragPlayingCurrent) {\n        this.backtrackFragment = null;\n        const fragPlaying = this.fragPlaying;\n        const fragCurrentLevel = fragPlayingCurrent.level;\n        if (!fragPlaying || fragPlayingCurrent.sn !== fragPlaying.sn || fragPlaying.level !== fragCurrentLevel || fragPlayingCurrent.urlId !== fragPlaying.urlId) {\n          this.fragPlaying = fragPlayingCurrent;\n          this.hls.trigger(Events.FRAG_CHANGED, {\n            frag: fragPlayingCurrent\n          });\n          if (!fragPlaying || fragPlaying.level !== fragCurrentLevel) {\n            this.hls.trigger(Events.LEVEL_SWITCHED, {\n              level: fragCurrentLevel\n            });\n          }\n        }\n      }\n    }\n  }\n  get nextLevel() {\n    const frag = this.nextBufferedFrag;\n    if (frag) {\n      return frag.level;\n    }\n    return -1;\n  }\n  get currentFrag() {\n    const media = this.media;\n    if (media) {\n      return this.fragPlaying || this.getAppendedFrag(media.currentTime);\n    }\n    return null;\n  }\n  get currentProgramDateTime() {\n    const media = this.media;\n    if (media) {\n      const currentTime = media.currentTime;\n      const frag = this.currentFrag;\n      if (frag && isFiniteNumber(currentTime) && isFiniteNumber(frag.programDateTime)) {\n        const epocMs = frag.programDateTime + (currentTime - frag.start) * 1000;\n        return new Date(epocMs);\n      }\n    }\n    return null;\n  }\n  get currentLevel() {\n    const frag = this.currentFrag;\n    if (frag) {\n      return frag.level;\n    }\n    return -1;\n  }\n  get nextBufferedFrag() {\n    const frag = this.currentFrag;\n    if (frag) {\n      return this.followingBufferedFrag(frag);\n    }\n    return null;\n  }\n  get forceStartLoad() {\n    return this._forceStartLoad;\n  }\n}\n\n/*\n * compute an Exponential Weighted moving average\n * - https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average\n *  - heavily inspired from shaka-player\n */\n\nclass EWMA {\n  //  About half of the estimated value will be from the last |halfLife| samples by weight.\n  constructor(halfLife, estimate = 0, weight = 0) {\n    this.halfLife = void 0;\n    this.alpha_ = void 0;\n    this.estimate_ = void 0;\n    this.totalWeight_ = void 0;\n    this.halfLife = halfLife;\n    // Larger values of alpha expire historical data more slowly.\n    this.alpha_ = halfLife ? Math.exp(Math.log(0.5) / halfLife) : 0;\n    this.estimate_ = estimate;\n    this.totalWeight_ = weight;\n  }\n  sample(weight, value) {\n    const adjAlpha = Math.pow(this.alpha_, weight);\n    this.estimate_ = value * (1 - adjAlpha) + adjAlpha * this.estimate_;\n    this.totalWeight_ += weight;\n  }\n  getTotalWeight() {\n    return this.totalWeight_;\n  }\n  getEstimate() {\n    if (this.alpha_) {\n      const zeroFactor = 1 - Math.pow(this.alpha_, this.totalWeight_);\n      if (zeroFactor) {\n        return this.estimate_ / zeroFactor;\n      }\n    }\n    return this.estimate_;\n  }\n}\n\n/*\n * EWMA Bandwidth Estimator\n *  - heavily inspired from shaka-player\n * Tracks bandwidth samples and estimates available bandwidth.\n * Based on the minimum of two exponentially-weighted moving averages with\n * different half-lives.\n */\n\nclass EwmaBandWidthEstimator {\n  constructor(slow, fast, defaultEstimate, defaultTTFB = 100) {\n    this.defaultEstimate_ = void 0;\n    this.minWeight_ = void 0;\n    this.minDelayMs_ = void 0;\n    this.slow_ = void 0;\n    this.fast_ = void 0;\n    this.defaultTTFB_ = void 0;\n    this.ttfb_ = void 0;\n    this.defaultEstimate_ = defaultEstimate;\n    this.minWeight_ = 0.001;\n    this.minDelayMs_ = 50;\n    this.slow_ = new EWMA(slow);\n    this.fast_ = new EWMA(fast);\n    this.defaultTTFB_ = defaultTTFB;\n    this.ttfb_ = new EWMA(slow);\n  }\n  update(slow, fast) {\n    const {\n      slow_,\n      fast_,\n      ttfb_\n    } = this;\n    if (slow_.halfLife !== slow) {\n      this.slow_ = new EWMA(slow, slow_.getEstimate(), slow_.getTotalWeight());\n    }\n    if (fast_.halfLife !== fast) {\n      this.fast_ = new EWMA(fast, fast_.getEstimate(), fast_.getTotalWeight());\n    }\n    if (ttfb_.halfLife !== slow) {\n      this.ttfb_ = new EWMA(slow, ttfb_.getEstimate(), ttfb_.getTotalWeight());\n    }\n  }\n  sample(durationMs, numBytes) {\n    durationMs = Math.max(durationMs, this.minDelayMs_);\n    const numBits = 8 * numBytes;\n    // weight is duration in seconds\n    const durationS = durationMs / 1000;\n    // value is bandwidth in bits/s\n    const bandwidthInBps = numBits / durationS;\n    this.fast_.sample(durationS, bandwidthInBps);\n    this.slow_.sample(durationS, bandwidthInBps);\n  }\n  sampleTTFB(ttfb) {\n    // weight is frequency curve applied to TTFB in seconds\n    // (longer times have less weight with expected input under 1 second)\n    const seconds = ttfb / 1000;\n    const weight = Math.sqrt(2) * Math.exp(-Math.pow(seconds, 2) / 2);\n    this.ttfb_.sample(weight, Math.max(ttfb, 5));\n  }\n  canEstimate() {\n    return this.fast_.getTotalWeight() >= this.minWeight_;\n  }\n  getEstimate() {\n    if (this.canEstimate()) {\n      // console.log('slow estimate:'+ Math.round(this.slow_.getEstimate()));\n      // console.log('fast estimate:'+ Math.round(this.fast_.getEstimate()));\n      // Take the minimum of these two estimates.  This should have the effect of\n      // adapting down quickly, but up more slowly.\n      return Math.min(this.fast_.getEstimate(), this.slow_.getEstimate());\n    } else {\n      return this.defaultEstimate_;\n    }\n  }\n  getEstimateTTFB() {\n    if (this.ttfb_.getTotalWeight() >= this.minWeight_) {\n      return this.ttfb_.getEstimate();\n    } else {\n      return this.defaultTTFB_;\n    }\n  }\n  destroy() {}\n}\n\nclass AbrController {\n  constructor(hls) {\n    this.hls = void 0;\n    this.lastLevelLoadSec = 0;\n    this.lastLoadedFragLevel = 0;\n    this._nextAutoLevel = -1;\n    this.timer = -1;\n    this.onCheck = this._abandonRulesCheck.bind(this);\n    this.fragCurrent = null;\n    this.partCurrent = null;\n    this.bitrateTestDelay = 0;\n    this.bwEstimator = void 0;\n    this.hls = hls;\n    const config = hls.config;\n    this.bwEstimator = new EwmaBandWidthEstimator(config.abrEwmaSlowVoD, config.abrEwmaFastVoD, config.abrEwmaDefaultEstimate);\n    this.registerListeners();\n  }\n  registerListeners() {\n    const {\n      hls\n    } = this;\n    hls.on(Events.FRAG_LOADING, this.onFragLoading, this);\n    hls.on(Events.FRAG_LOADED, this.onFragLoaded, this);\n    hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);\n    hls.on(Events.LEVEL_SWITCHING, this.onLevelSwitching, this);\n    hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);\n  }\n  unregisterListeners() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.FRAG_LOADING, this.onFragLoading, this);\n    hls.off(Events.FRAG_LOADED, this.onFragLoaded, this);\n    hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);\n    hls.off(Events.LEVEL_SWITCHING, this.onLevelSwitching, this);\n    hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);\n  }\n  destroy() {\n    this.unregisterListeners();\n    this.clearTimer();\n    // @ts-ignore\n    this.hls = this.onCheck = null;\n    this.fragCurrent = this.partCurrent = null;\n  }\n  onFragLoading(event, data) {\n    var _data$part;\n    const frag = data.frag;\n    if (this.ignoreFragment(frag)) {\n      return;\n    }\n    this.fragCurrent = frag;\n    this.partCurrent = (_data$part = data.part) != null ? _data$part : null;\n    this.clearTimer();\n    this.timer = self.setInterval(this.onCheck, 100);\n  }\n  onLevelSwitching(event, data) {\n    this.clearTimer();\n  }\n  getTimeToLoadFrag(timeToFirstByteSec, bandwidth, fragSizeBits, isSwitch) {\n    const fragLoadSec = timeToFirstByteSec + fragSizeBits / bandwidth;\n    const playlistLoadSec = isSwitch ? this.lastLevelLoadSec : 0;\n    return fragLoadSec + playlistLoadSec;\n  }\n  onLevelLoaded(event, data) {\n    const config = this.hls.config;\n    const {\n      total,\n      bwEstimate\n    } = data.stats;\n    // Total is the bytelength and bwEstimate in bits/sec\n    if (isFiniteNumber(total) && isFiniteNumber(bwEstimate)) {\n      this.lastLevelLoadSec = 8 * total / bwEstimate;\n    }\n    if (data.details.live) {\n      this.bwEstimator.update(config.abrEwmaSlowLive, config.abrEwmaFastLive);\n    } else {\n      this.bwEstimator.update(config.abrEwmaSlowVoD, config.abrEwmaFastVoD);\n    }\n  }\n\n  /*\n      This method monitors the download rate of the current fragment, and will downswitch if that fragment will not load\n      quickly enough to prevent underbuffering\n    */\n  _abandonRulesCheck() {\n    const {\n      fragCurrent: frag,\n      partCurrent: part,\n      hls\n    } = this;\n    const {\n      autoLevelEnabled,\n      media\n    } = hls;\n    if (!frag || !media) {\n      return;\n    }\n    const now = performance.now();\n    const stats = part ? part.stats : frag.stats;\n    const duration = part ? part.duration : frag.duration;\n    const timeLoading = now - stats.loading.start;\n    // If frag loading is aborted, complete, or from lowest level, stop timer and return\n    if (stats.aborted || stats.loaded && stats.loaded === stats.total || frag.level === 0) {\n      this.clearTimer();\n      // reset forced auto level value so that next level will be selected\n      this._nextAutoLevel = -1;\n      return;\n    }\n\n    // This check only runs if we're in ABR mode and actually playing\n    if (!autoLevelEnabled || media.paused || !media.playbackRate || !media.readyState) {\n      return;\n    }\n    const bufferInfo = hls.mainForwardBufferInfo;\n    if (bufferInfo === null) {\n      return;\n    }\n    const ttfbEstimate = this.bwEstimator.getEstimateTTFB();\n    const playbackRate = Math.abs(media.playbackRate);\n    // To maintain stable adaptive playback, only begin monitoring frag loading after half or more of its playback duration has passed\n    if (timeLoading <= Math.max(ttfbEstimate, 1000 * (duration / (playbackRate * 2)))) {\n      return;\n    }\n\n    // bufferStarvationDelay is an estimate of the amount time (in seconds) it will take to exhaust the buffer\n    const bufferStarvationDelay = bufferInfo.len / playbackRate;\n    // Only downswitch if less than 2 fragment lengths are buffered\n    if (bufferStarvationDelay >= 2 * duration / playbackRate) {\n      return;\n    }\n    const ttfb = stats.loading.first ? stats.loading.first - stats.loading.start : -1;\n    const loadedFirstByte = stats.loaded && ttfb > -1;\n    const bwEstimate = this.bwEstimator.getEstimate();\n    const {\n      levels,\n      minAutoLevel\n    } = hls;\n    const level = levels[frag.level];\n    const expectedLen = stats.total || Math.max(stats.loaded, Math.round(duration * level.maxBitrate / 8));\n    let timeStreaming = timeLoading - ttfb;\n    if (timeStreaming < 1 && loadedFirstByte) {\n      timeStreaming = Math.min(timeLoading, stats.loaded * 8 / bwEstimate);\n    }\n    const loadRate = loadedFirstByte ? stats.loaded * 1000 / timeStreaming : 0;\n    // fragLoadDelay is an estimate of the time (in seconds) it will take to buffer the remainder of the fragment\n    const fragLoadedDelay = loadRate ? (expectedLen - stats.loaded) / loadRate : expectedLen * 8 / bwEstimate + ttfbEstimate / 1000;\n    // Only downswitch if the time to finish loading the current fragment is greater than the amount of buffer left\n    if (fragLoadedDelay <= bufferStarvationDelay) {\n      return;\n    }\n    const bwe = loadRate ? loadRate * 8 : bwEstimate;\n    let fragLevelNextLoadedDelay = Number.POSITIVE_INFINITY;\n    let nextLoadLevel;\n    // Iterate through lower level and try to find the largest one that avoids rebuffering\n    for (nextLoadLevel = frag.level - 1; nextLoadLevel > minAutoLevel; nextLoadLevel--) {\n      // compute time to load next fragment at lower level\n      // 8 = bits per byte (bps/Bps)\n      const levelNextBitrate = levels[nextLoadLevel].maxBitrate;\n      fragLevelNextLoadedDelay = this.getTimeToLoadFrag(ttfbEstimate / 1000, bwe, duration * levelNextBitrate, !levels[nextLoadLevel].details);\n      if (fragLevelNextLoadedDelay < bufferStarvationDelay) {\n        break;\n      }\n    }\n    // Only emergency switch down if it takes less time to load a new fragment at lowest level instead of continuing\n    // to load the current one\n    if (fragLevelNextLoadedDelay >= fragLoadedDelay) {\n      return;\n    }\n\n    // if estimated load time of new segment is completely unreasonable, ignore and do not emergency switch down\n    if (fragLevelNextLoadedDelay > duration * 10) {\n      return;\n    }\n    hls.nextLoadLevel = nextLoadLevel;\n    if (loadedFirstByte) {\n      // If there has been loading progress, sample bandwidth using loading time offset by minimum TTFB time\n      this.bwEstimator.sample(timeLoading - Math.min(ttfbEstimate, ttfb), stats.loaded);\n    } else {\n      // If there has been no loading progress, sample TTFB\n      this.bwEstimator.sampleTTFB(timeLoading);\n    }\n    this.clearTimer();\n    logger.warn(`[abr] Fragment ${frag.sn}${part ? ' part ' + part.index : ''} of level ${frag.level} is loading too slowly;\n      Time to underbuffer: ${bufferStarvationDelay.toFixed(3)} s\n      Estimated load time for current fragment: ${fragLoadedDelay.toFixed(3)} s\n      Estimated load time for down switch fragment: ${fragLevelNextLoadedDelay.toFixed(3)} s\n      TTFB estimate: ${ttfb}\n      Current BW estimate: ${isFiniteNumber(bwEstimate) ? (bwEstimate / 1024).toFixed(3) : 'Unknown'} Kb/s\n      New BW estimate: ${(this.bwEstimator.getEstimate() / 1024).toFixed(3)} Kb/s\n      Aborting and switching to level ${nextLoadLevel}`);\n    if (frag.loader) {\n      this.fragCurrent = this.partCurrent = null;\n      frag.abortRequests();\n    }\n    hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, {\n      frag,\n      part,\n      stats\n    });\n  }\n  onFragLoaded(event, {\n    frag,\n    part\n  }) {\n    const stats = part ? part.stats : frag.stats;\n    if (frag.type === PlaylistLevelType.MAIN) {\n      this.bwEstimator.sampleTTFB(stats.loading.first - stats.loading.start);\n    }\n    if (this.ignoreFragment(frag)) {\n      return;\n    }\n    // stop monitoring bw once frag loaded\n    this.clearTimer();\n    // store level id after successful fragment load\n    this.lastLoadedFragLevel = frag.level;\n    // reset forced auto level value so that next level will be selected\n    this._nextAutoLevel = -1;\n\n    // compute level average bitrate\n    if (this.hls.config.abrMaxWithRealBitrate) {\n      const duration = part ? part.duration : frag.duration;\n      const level = this.hls.levels[frag.level];\n      const loadedBytes = (level.loaded ? level.loaded.bytes : 0) + stats.loaded;\n      const loadedDuration = (level.loaded ? level.loaded.duration : 0) + duration;\n      level.loaded = {\n        bytes: loadedBytes,\n        duration: loadedDuration\n      };\n      level.realBitrate = Math.round(8 * loadedBytes / loadedDuration);\n    }\n    if (frag.bitrateTest) {\n      const fragBufferedData = {\n        stats,\n        frag,\n        part,\n        id: frag.type\n      };\n      this.onFragBuffered(Events.FRAG_BUFFERED, fragBufferedData);\n      frag.bitrateTest = false;\n    }\n  }\n  onFragBuffered(event, data) {\n    const {\n      frag,\n      part\n    } = data;\n    const stats = part != null && part.stats.loaded ? part.stats : frag.stats;\n    if (stats.aborted) {\n      return;\n    }\n    if (this.ignoreFragment(frag)) {\n      return;\n    }\n    // Use the difference between parsing and request instead of buffering and request to compute fragLoadingProcessing;\n    // rationale is that buffer appending only happens once media is attached. This can happen when config.startFragPrefetch\n    // is used. If we used buffering in that case, our BW estimate sample will be very large.\n    const processingMs = stats.parsing.end - stats.loading.start - Math.min(stats.loading.first - stats.loading.start, this.bwEstimator.getEstimateTTFB());\n    this.bwEstimator.sample(processingMs, stats.loaded);\n    stats.bwEstimate = this.bwEstimator.getEstimate();\n    if (frag.bitrateTest) {\n      this.bitrateTestDelay = processingMs / 1000;\n    } else {\n      this.bitrateTestDelay = 0;\n    }\n  }\n  ignoreFragment(frag) {\n    // Only count non-alt-audio frags which were actually buffered in our BW calculations\n    return frag.type !== PlaylistLevelType.MAIN || frag.sn === 'initSegment';\n  }\n  clearTimer() {\n    self.clearInterval(this.timer);\n  }\n\n  // return next auto level\n  get nextAutoLevel() {\n    const forcedAutoLevel = this._nextAutoLevel;\n    const bwEstimator = this.bwEstimator;\n    // in case next auto level has been forced, and bw not available or not reliable, return forced value\n    if (forcedAutoLevel !== -1 && !bwEstimator.canEstimate()) {\n      return forcedAutoLevel;\n    }\n\n    // compute next level using ABR logic\n    let nextABRAutoLevel = this.getNextABRAutoLevel();\n    // use forced auto level when ABR selected level has errored\n    if (forcedAutoLevel !== -1) {\n      const levels = this.hls.levels;\n      if (levels.length > Math.max(forcedAutoLevel, nextABRAutoLevel) && levels[forcedAutoLevel].loadError <= levels[nextABRAutoLevel].loadError) {\n        return forcedAutoLevel;\n      }\n    }\n    // if forced auto level has been defined, use it to cap ABR computed quality level\n    if (forcedAutoLevel !== -1) {\n      nextABRAutoLevel = Math.min(forcedAutoLevel, nextABRAutoLevel);\n    }\n    return nextABRAutoLevel;\n  }\n  getNextABRAutoLevel() {\n    const {\n      fragCurrent,\n      partCurrent,\n      hls\n    } = this;\n    const {\n      maxAutoLevel,\n      config,\n      minAutoLevel,\n      media\n    } = hls;\n    const currentFragDuration = partCurrent ? partCurrent.duration : fragCurrent ? fragCurrent.duration : 0;\n\n    // playbackRate is the absolute value of the playback rate; if media.playbackRate is 0, we use 1 to load as\n    // if we're playing back at the normal rate.\n    const playbackRate = media && media.playbackRate !== 0 ? Math.abs(media.playbackRate) : 1.0;\n    const avgbw = this.bwEstimator ? this.bwEstimator.getEstimate() : config.abrEwmaDefaultEstimate;\n    // bufferStarvationDelay is the wall-clock time left until the playback buffer is exhausted.\n    const bufferInfo = hls.mainForwardBufferInfo;\n    const bufferStarvationDelay = (bufferInfo ? bufferInfo.len : 0) / playbackRate;\n\n    // First, look to see if we can find a level matching with our avg bandwidth AND that could also guarantee no rebuffering at all\n    let bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, config.abrBandWidthFactor, config.abrBandWidthUpFactor);\n    if (bestLevel >= 0) {\n      return bestLevel;\n    }\n    logger.trace(`[abr] ${bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty'}, finding optimal quality level`);\n    // not possible to get rid of rebuffering ... let's try to find level that will guarantee less than maxStarvationDelay of rebuffering\n    // if no matching level found, logic will return 0\n    let maxStarvationDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxStarvationDelay) : config.maxStarvationDelay;\n    let bwFactor = config.abrBandWidthFactor;\n    let bwUpFactor = config.abrBandWidthUpFactor;\n    if (!bufferStarvationDelay) {\n      // in case buffer is empty, let's check if previous fragment was loaded to perform a bitrate test\n      const bitrateTestDelay = this.bitrateTestDelay;\n      if (bitrateTestDelay) {\n        // if it is the case, then we need to adjust our max starvation delay using maxLoadingDelay config value\n        // max video loading delay used in  automatic start level selection :\n        // in that mode ABR controller will ensure that video loading time (ie the time to fetch the first fragment at lowest quality level +\n        // the time to fetch the fragment at the appropriate quality level is less than ```maxLoadingDelay``` )\n        // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration\n        const maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay;\n        maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;\n        logger.trace(`[abr] bitrate test took ${Math.round(1000 * bitrateTestDelay)}ms, set first fragment max fetchDuration to ${Math.round(1000 * maxStarvationDelay)} ms`);\n        // don't use conservative factor on bitrate test\n        bwFactor = bwUpFactor = 1;\n      }\n    }\n    bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay + maxStarvationDelay, bwFactor, bwUpFactor);\n    return Math.max(bestLevel, 0);\n  }\n  findBestLevel(currentBw, minAutoLevel, maxAutoLevel, maxFetchDuration, bwFactor, bwUpFactor) {\n    var _level$details;\n    const {\n      fragCurrent,\n      partCurrent,\n      lastLoadedFragLevel: currentLevel\n    } = this;\n    const {\n      levels\n    } = this.hls;\n    const level = levels[currentLevel];\n    const live = !!(level != null && (_level$details = level.details) != null && _level$details.live);\n    const currentCodecSet = level == null ? void 0 : level.codecSet;\n    const currentFragDuration = partCurrent ? partCurrent.duration : fragCurrent ? fragCurrent.duration : 0;\n    const ttfbEstimateSec = this.bwEstimator.getEstimateTTFB() / 1000;\n    let levelSkippedMin = minAutoLevel;\n    let levelSkippedMax = -1;\n    for (let i = maxAutoLevel; i >= minAutoLevel; i--) {\n      const levelInfo = levels[i];\n      if (!levelInfo || currentCodecSet && levelInfo.codecSet !== currentCodecSet) {\n        if (levelInfo) {\n          levelSkippedMin = Math.min(i, levelSkippedMin);\n          levelSkippedMax = Math.max(i, levelSkippedMax);\n        }\n        continue;\n      }\n      if (levelSkippedMax !== -1) {\n        logger.trace(`[abr] Skipped level(s) ${levelSkippedMin}-${levelSkippedMax} with CODECS:\"${levels[levelSkippedMax].attrs.CODECS}\"; not compatible with \"${level.attrs.CODECS}\"`);\n      }\n      const levelDetails = levelInfo.details;\n      const avgDuration = (partCurrent ? levelDetails == null ? void 0 : levelDetails.partTarget : levelDetails == null ? void 0 : levelDetails.averagetargetduration) || currentFragDuration;\n      let adjustedbw;\n      // follow algorithm captured from stagefright :\n      // https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp\n      // Pick the highest bandwidth stream below or equal to estimated bandwidth.\n      // consider only 80% of the available bandwidth, but if we are switching up,\n      // be even more conservative (70%) to avoid overestimating and immediately\n      // switching back.\n      if (i <= currentLevel) {\n        adjustedbw = bwFactor * currentBw;\n      } else {\n        adjustedbw = bwUpFactor * currentBw;\n      }\n      const bitrate = levels[i].maxBitrate;\n      const fetchDuration = this.getTimeToLoadFrag(ttfbEstimateSec, adjustedbw, bitrate * avgDuration, levelDetails === undefined);\n      logger.trace(`[abr] level:${i} adjustedbw-bitrate:${Math.round(adjustedbw - bitrate)} avgDuration:${avgDuration.toFixed(1)} maxFetchDuration:${maxFetchDuration.toFixed(1)} fetchDuration:${fetchDuration.toFixed(1)}`);\n      // if adjusted bw is greater than level bitrate AND\n      if (adjustedbw > bitrate && (\n      // fragment fetchDuration unknown OR live stream OR fragment fetchDuration less than max allowed fetch duration, then this level matches\n      // we don't account for max Fetch Duration for live streams, this is to avoid switching down when near the edge of live sliding window ...\n      // special case to support startLevel = -1 (bitrateTest) on live streams : in that case we should not exit loop so that findBestLevel will return -1\n      fetchDuration === 0 || !isFiniteNumber(fetchDuration) || live && !this.bitrateTestDelay || fetchDuration < maxFetchDuration)) {\n        // as we are looping from highest to lowest, this will return the best achievable quality level\n        return i;\n      }\n    }\n    // not enough time budget even with quality level 0 ... rebuffering might happen\n    return -1;\n  }\n  set nextAutoLevel(nextLevel) {\n    this._nextAutoLevel = nextLevel;\n  }\n}\n\nclass ChunkCache {\n  constructor() {\n    this.chunks = [];\n    this.dataLength = 0;\n  }\n  push(chunk) {\n    this.chunks.push(chunk);\n    this.dataLength += chunk.length;\n  }\n  flush() {\n    const {\n      chunks,\n      dataLength\n    } = this;\n    let result;\n    if (!chunks.length) {\n      return new Uint8Array(0);\n    } else if (chunks.length === 1) {\n      result = chunks[0];\n    } else {\n      result = concatUint8Arrays(chunks, dataLength);\n    }\n    this.reset();\n    return result;\n  }\n  reset() {\n    this.chunks.length = 0;\n    this.dataLength = 0;\n  }\n}\nfunction concatUint8Arrays(chunks, dataLength) {\n  const result = new Uint8Array(dataLength);\n  let offset = 0;\n  for (let i = 0; i < chunks.length; i++) {\n    const chunk = chunks[i];\n    result.set(chunk, offset);\n    offset += chunk.length;\n  }\n  return result;\n}\n\nconst TICK_INTERVAL$1 = 100; // how often to tick in ms\n\nclass AudioStreamController extends BaseStreamController {\n  constructor(hls, fragmentTracker, keyLoader) {\n    super(hls, fragmentTracker, keyLoader, '[audio-stream-controller]', PlaylistLevelType.AUDIO);\n    this.videoBuffer = null;\n    this.videoTrackCC = -1;\n    this.waitingVideoCC = -1;\n    this.bufferedTrack = null;\n    this.switchingTrack = null;\n    this.trackId = -1;\n    this.waitingData = null;\n    this.mainDetails = null;\n    this.bufferFlushed = false;\n    this.cachedTrackLoadedData = null;\n    this._registerListeners();\n  }\n  onHandlerDestroying() {\n    this._unregisterListeners();\n    this.mainDetails = null;\n    this.bufferedTrack = null;\n    this.switchingTrack = null;\n  }\n  _registerListeners() {\n    const {\n      hls\n    } = this;\n    hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);\n    hls.on(Events.AUDIO_TRACKS_UPDATED, this.onAudioTracksUpdated, this);\n    hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);\n    hls.on(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this);\n    hls.on(Events.ERROR, this.onError, this);\n    hls.on(Events.BUFFER_RESET, this.onBufferReset, this);\n    hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);\n    hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);\n    hls.on(Events.INIT_PTS_FOUND, this.onInitPtsFound, this);\n    hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);\n  }\n  _unregisterListeners() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);\n    hls.off(Events.AUDIO_TRACKS_UPDATED, this.onAudioTracksUpdated, this);\n    hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);\n    hls.off(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this);\n    hls.off(Events.ERROR, this.onError, this);\n    hls.off(Events.BUFFER_RESET, this.onBufferReset, this);\n    hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);\n    hls.off(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);\n    hls.off(Events.INIT_PTS_FOUND, this.onInitPtsFound, this);\n    hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);\n  }\n\n  // INIT_PTS_FOUND is triggered when the video track parsed in the stream-controller has a new PTS value\n  onInitPtsFound(event, {\n    frag,\n    id,\n    initPTS,\n    timescale\n  }) {\n    // Always update the new INIT PTS\n    // Can change due level switch\n    if (id === 'main') {\n      const cc = frag.cc;\n      this.initPTS[frag.cc] = {\n        baseTime: initPTS,\n        timescale\n      };\n      this.log(`InitPTS for cc: ${cc} found from main: ${initPTS}`);\n      this.videoTrackCC = cc;\n      // If we are waiting, tick immediately to unblock audio fragment transmuxing\n      if (this.state === State.WAITING_INIT_PTS) {\n        this.tick();\n      }\n    }\n  }\n  startLoad(startPosition) {\n    if (!this.levels) {\n      this.startPosition = startPosition;\n      this.state = State.STOPPED;\n      return;\n    }\n    const lastCurrentTime = this.lastCurrentTime;\n    this.stopLoad();\n    this.setInterval(TICK_INTERVAL$1);\n    if (lastCurrentTime > 0 && startPosition === -1) {\n      this.log(`Override startPosition with lastCurrentTime @${lastCurrentTime.toFixed(3)}`);\n      startPosition = lastCurrentTime;\n      this.state = State.IDLE;\n    } else {\n      this.loadedmetadata = false;\n      this.state = State.WAITING_TRACK;\n    }\n    this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition;\n    this.tick();\n  }\n  doTick() {\n    switch (this.state) {\n      case State.IDLE:\n        this.doTickIdle();\n        break;\n      case State.WAITING_TRACK:\n        {\n          var _levels$trackId;\n          const {\n            levels,\n            trackId\n          } = this;\n          const details = levels == null ? void 0 : (_levels$trackId = levels[trackId]) == null ? void 0 : _levels$trackId.details;\n          if (details) {\n            if (this.waitForCdnTuneIn(details)) {\n              break;\n            }\n            this.state = State.WAITING_INIT_PTS;\n          }\n          break;\n        }\n      case State.FRAG_LOADING_WAITING_RETRY:\n        {\n          var _this$media;\n          const now = performance.now();\n          const retryDate = this.retryDate;\n          // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading\n          if (!retryDate || now >= retryDate || (_this$media = this.media) != null && _this$media.seeking) {\n            this.log('RetryDate reached, switch back to IDLE state');\n            this.resetStartWhenNotLoaded(this.trackId);\n            this.state = State.IDLE;\n          }\n          break;\n        }\n      case State.WAITING_INIT_PTS:\n        {\n          // Ensure we don't get stuck in the WAITING_INIT_PTS state if the waiting frag CC doesn't match any initPTS\n          const waitingData = this.waitingData;\n          if (waitingData) {\n            const {\n              frag,\n              part,\n              cache,\n              complete\n            } = waitingData;\n            if (this.initPTS[frag.cc] !== undefined) {\n              this.waitingData = null;\n              this.waitingVideoCC = -1;\n              this.state = State.FRAG_LOADING;\n              const payload = cache.flush();\n              const data = {\n                frag,\n                part,\n                payload,\n                networkDetails: null\n              };\n              this._handleFragmentLoadProgress(data);\n              if (complete) {\n                super._handleFragmentLoadComplete(data);\n              }\n            } else if (this.videoTrackCC !== this.waitingVideoCC) {\n              // Drop waiting fragment if videoTrackCC has changed since waitingFragment was set and initPTS was not found\n              this.log(`Waiting fragment cc (${frag.cc}) cancelled because video is at cc ${this.videoTrackCC}`);\n              this.clearWaitingFragment();\n            } else {\n              // Drop waiting fragment if an earlier fragment is needed\n              const pos = this.getLoadPosition();\n              const bufferInfo = BufferHelper.bufferInfo(this.mediaBuffer, pos, this.config.maxBufferHole);\n              const waitingFragmentAtPosition = fragmentWithinToleranceTest(bufferInfo.end, this.config.maxFragLookUpTolerance, frag);\n              if (waitingFragmentAtPosition < 0) {\n                this.log(`Waiting fragment cc (${frag.cc}) @ ${frag.start} cancelled because another fragment at ${bufferInfo.end} is needed`);\n                this.clearWaitingFragment();\n              }\n            }\n          } else {\n            this.state = State.IDLE;\n          }\n        }\n    }\n    this.onTickEnd();\n  }\n  clearWaitingFragment() {\n    const waitingData = this.waitingData;\n    if (waitingData) {\n      this.fragmentTracker.removeFragment(waitingData.frag);\n      this.waitingData = null;\n      this.waitingVideoCC = -1;\n      this.state = State.IDLE;\n    }\n  }\n  resetLoadingState() {\n    this.clearWaitingFragment();\n    super.resetLoadingState();\n  }\n  onTickEnd() {\n    const {\n      media\n    } = this;\n    if (!(media != null && media.readyState)) {\n      // Exit early if we don't have media or if the media hasn't buffered anything yet (readyState 0)\n      return;\n    }\n    this.lastCurrentTime = media.currentTime;\n  }\n  doTickIdle() {\n    const {\n      hls,\n      levels,\n      media,\n      trackId\n    } = this;\n    const config = hls.config;\n    if (!(levels != null && levels[trackId])) {\n      return;\n    }\n\n    // if video not attached AND\n    // start fragment already requested OR start frag prefetch not enabled\n    // exit loop\n    // => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop\n    if (!media && (this.startFragRequested || !config.startFragPrefetch)) {\n      return;\n    }\n    const levelInfo = levels[trackId];\n    const trackDetails = levelInfo.details;\n    if (!trackDetails || trackDetails.live && this.levelLastLoaded !== trackId || this.waitForCdnTuneIn(trackDetails)) {\n      this.state = State.WAITING_TRACK;\n      return;\n    }\n    const bufferable = this.mediaBuffer ? this.mediaBuffer : this.media;\n    if (this.bufferFlushed && bufferable) {\n      this.bufferFlushed = false;\n      this.afterBufferFlushed(bufferable, ElementaryStreamTypes.AUDIO, PlaylistLevelType.AUDIO);\n    }\n    const bufferInfo = this.getFwdBufferInfo(bufferable, PlaylistLevelType.AUDIO);\n    if (bufferInfo === null) {\n      return;\n    }\n    const {\n      bufferedTrack,\n      switchingTrack\n    } = this;\n    if (!switchingTrack && this._streamEnded(bufferInfo, trackDetails)) {\n      hls.trigger(Events.BUFFER_EOS, {\n        type: 'audio'\n      });\n      this.state = State.ENDED;\n      return;\n    }\n    const mainBufferInfo = this.getFwdBufferInfo(this.videoBuffer ? this.videoBuffer : this.media, PlaylistLevelType.MAIN);\n    const bufferLen = bufferInfo.len;\n    const maxBufLen = this.getMaxBufferLength(mainBufferInfo == null ? void 0 : mainBufferInfo.len);\n\n    // if buffer length is less than maxBufLen try to load a new fragment\n    if (bufferLen >= maxBufLen && !switchingTrack) {\n      return;\n    }\n    const fragments = trackDetails.fragments;\n    const start = fragments[0].start;\n    let targetBufferTime = bufferInfo.end;\n    if (switchingTrack && media) {\n      const pos = this.getLoadPosition();\n      if (bufferedTrack && switchingTrack.attrs !== bufferedTrack.attrs) {\n        targetBufferTime = pos;\n      }\n      // if currentTime (pos) is less than alt audio playlist start time, it means that alt audio is ahead of currentTime\n      if (trackDetails.PTSKnown && pos < start) {\n        // if everything is buffered from pos to start or if audio buffer upfront, let's seek to start\n        if (bufferInfo.end > start || bufferInfo.nextStart) {\n          this.log('Alt audio track ahead of main track, seek to start of alt audio track');\n          media.currentTime = start + 0.05;\n        }\n      }\n    }\n    let frag = this.getNextFragment(targetBufferTime, trackDetails);\n    let atGap = false;\n    // Avoid loop loading by using nextLoadPosition set for backtracking and skipping consecutive GAP tags\n    if (frag && this.isLoopLoading(frag, targetBufferTime)) {\n      atGap = !!frag.gap;\n      frag = this.getNextFragmentLoopLoading(frag, trackDetails, bufferInfo, PlaylistLevelType.MAIN, maxBufLen);\n    }\n    if (!frag) {\n      this.bufferFlushed = true;\n      return;\n    }\n\n    // Buffer audio up to one target duration ahead of main buffer\n    const atBufferSyncLimit = mainBufferInfo && frag.start > mainBufferInfo.end + trackDetails.targetduration;\n    if (atBufferSyncLimit ||\n    // Or wait for main buffer after buffing some audio\n    !(mainBufferInfo != null && mainBufferInfo.len) && bufferInfo.len) {\n      // Check fragment-tracker for main fragments since GAP segments do not show up in bufferInfo\n      const mainFrag = this.getAppendedFrag(frag.start, PlaylistLevelType.MAIN);\n      if (mainFrag === null) {\n        return;\n      }\n      // Bridge gaps in main buffer\n      atGap || (atGap = !!mainFrag.gap || !!atBufferSyncLimit && mainBufferInfo.len === 0);\n      if (atBufferSyncLimit && !atGap || atGap && bufferInfo.nextStart && bufferInfo.nextStart < mainFrag.end) {\n        return;\n      }\n    }\n    this.loadFragment(frag, levelInfo, targetBufferTime);\n  }\n  getMaxBufferLength(mainBufferLength) {\n    const maxConfigBuffer = super.getMaxBufferLength();\n    if (!mainBufferLength) {\n      return maxConfigBuffer;\n    }\n    return Math.min(Math.max(maxConfigBuffer, mainBufferLength), this.config.maxMaxBufferLength);\n  }\n  onMediaDetaching() {\n    this.videoBuffer = null;\n    super.onMediaDetaching();\n  }\n  onAudioTracksUpdated(event, {\n    audioTracks\n  }) {\n    this.resetTransmuxer();\n    this.levels = audioTracks.map(mediaPlaylist => new Level(mediaPlaylist));\n  }\n  onAudioTrackSwitching(event, data) {\n    // if any URL found on new audio track, it is an alternate audio track\n    const altAudio = !!data.url;\n    this.trackId = data.id;\n    const {\n      fragCurrent\n    } = this;\n    if (fragCurrent) {\n      fragCurrent.abortRequests();\n      this.removeUnbufferedFrags(fragCurrent.start);\n    }\n    this.resetLoadingState();\n    // destroy useless transmuxer when switching audio to main\n    if (!altAudio) {\n      this.resetTransmuxer();\n    } else {\n      // switching to audio track, start timer if not already started\n      this.setInterval(TICK_INTERVAL$1);\n    }\n\n    // should we switch tracks ?\n    if (altAudio) {\n      this.switchingTrack = data;\n      // main audio track are handled by stream-controller, just do something if switching to alt audio track\n      this.state = State.IDLE;\n    } else {\n      this.switchingTrack = null;\n      this.bufferedTrack = data;\n      this.state = State.STOPPED;\n    }\n    this.tick();\n  }\n  onManifestLoading() {\n    this.fragmentTracker.removeAllFragments();\n    this.startPosition = this.lastCurrentTime = 0;\n    this.bufferFlushed = false;\n    this.levels = this.mainDetails = this.waitingData = this.bufferedTrack = this.cachedTrackLoadedData = this.switchingTrack = null;\n    this.startFragRequested = false;\n    this.trackId = this.videoTrackCC = this.waitingVideoCC = -1;\n  }\n  onLevelLoaded(event, data) {\n    this.mainDetails = data.details;\n    if (this.cachedTrackLoadedData !== null) {\n      this.hls.trigger(Events.AUDIO_TRACK_LOADED, this.cachedTrackLoadedData);\n      this.cachedTrackLoadedData = null;\n    }\n  }\n  onAudioTrackLoaded(event, data) {\n    var _track$details;\n    if (this.mainDetails == null) {\n      this.cachedTrackLoadedData = data;\n      return;\n    }\n    const {\n      levels\n    } = this;\n    const {\n      details: newDetails,\n      id: trackId\n    } = data;\n    if (!levels) {\n      this.warn(`Audio tracks were reset while loading level ${trackId}`);\n      return;\n    }\n    this.log(`Track ${trackId} loaded [${newDetails.startSN},${newDetails.endSN}]${newDetails.lastPartSn ? `[part-${newDetails.lastPartSn}-${newDetails.lastPartIndex}]` : ''},duration:${newDetails.totalduration}`);\n    const track = levels[trackId];\n    let sliding = 0;\n    if (newDetails.live || (_track$details = track.details) != null && _track$details.live) {\n      this.checkLiveUpdate(newDetails);\n      const mainDetails = this.mainDetails;\n      if (newDetails.deltaUpdateFailed || !mainDetails) {\n        return;\n      }\n      if (!track.details && newDetails.hasProgramDateTime && mainDetails.hasProgramDateTime) {\n        // Make sure our audio rendition is aligned with the \"main\" rendition, using\n        // pdt as our reference times.\n        alignMediaPlaylistByPDT(newDetails, mainDetails);\n        sliding = newDetails.fragments[0].start;\n      } else {\n        sliding = this.alignPlaylists(newDetails, track.details);\n      }\n    }\n    track.details = newDetails;\n    this.levelLastLoaded = trackId;\n\n    // compute start position if we are aligned with the main playlist\n    if (!this.startFragRequested && (this.mainDetails || !newDetails.live)) {\n      this.setStartPosition(track.details, sliding);\n    }\n    // only switch back to IDLE state if we were waiting for track to start downloading a new fragment\n    if (this.state === State.WAITING_TRACK && !this.waitForCdnTuneIn(newDetails)) {\n      this.state = State.IDLE;\n    }\n\n    // trigger handler right now\n    this.tick();\n  }\n  _handleFragmentLoadProgress(data) {\n    var _frag$initSegment;\n    const {\n      frag,\n      part,\n      payload\n    } = data;\n    const {\n      config,\n      trackId,\n      levels\n    } = this;\n    if (!levels) {\n      this.warn(`Audio tracks were reset while fragment load was in progress. Fragment ${frag.sn} of level ${frag.level} will not be buffered`);\n      return;\n    }\n    const track = levels[trackId];\n    if (!track) {\n      this.warn('Audio track is undefined on fragment load progress');\n      return;\n    }\n    const details = track.details;\n    if (!details) {\n      this.warn('Audio track details undefined on fragment load progress');\n      this.removeUnbufferedFrags(frag.start);\n      return;\n    }\n    const audioCodec = config.defaultAudioCodec || track.audioCodec || 'mp4a.40.2';\n    let transmuxer = this.transmuxer;\n    if (!transmuxer) {\n      transmuxer = this.transmuxer = new TransmuxerInterface(this.hls, PlaylistLevelType.AUDIO, this._handleTransmuxComplete.bind(this), this._handleTransmuxerFlush.bind(this));\n    }\n\n    // Check if we have video initPTS\n    // If not we need to wait for it\n    const initPTS = this.initPTS[frag.cc];\n    const initSegmentData = (_frag$initSegment = frag.initSegment) == null ? void 0 : _frag$initSegment.data;\n    if (initPTS !== undefined) {\n      // this.log(`Transmuxing ${sn} of [${details.startSN} ,${details.endSN}],track ${trackId}`);\n      // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live)\n      const accurateTimeOffset = false; // details.PTSKnown || !details.live;\n      const partIndex = part ? part.index : -1;\n      const partial = partIndex !== -1;\n      const chunkMeta = new ChunkMetadata(frag.level, frag.sn, frag.stats.chunkCount, payload.byteLength, partIndex, partial);\n      transmuxer.push(payload, initSegmentData, audioCodec, '', frag, part, details.totalduration, accurateTimeOffset, chunkMeta, initPTS);\n    } else {\n      this.log(`Unknown video PTS for cc ${frag.cc}, waiting for video PTS before demuxing audio frag ${frag.sn} of [${details.startSN} ,${details.endSN}],track ${trackId}`);\n      const {\n        cache\n      } = this.waitingData = this.waitingData || {\n        frag,\n        part,\n        cache: new ChunkCache(),\n        complete: false\n      };\n      cache.push(new Uint8Array(payload));\n      this.waitingVideoCC = this.videoTrackCC;\n      this.state = State.WAITING_INIT_PTS;\n    }\n  }\n  _handleFragmentLoadComplete(fragLoadedData) {\n    if (this.waitingData) {\n      this.waitingData.complete = true;\n      return;\n    }\n    super._handleFragmentLoadComplete(fragLoadedData);\n  }\n  onBufferReset( /* event: Events.BUFFER_RESET */\n  ) {\n    // reset reference to sourcebuffers\n    this.mediaBuffer = this.videoBuffer = null;\n    this.loadedmetadata = false;\n  }\n  onBufferCreated(event, data) {\n    const audioTrack = data.tracks.audio;\n    if (audioTrack) {\n      this.mediaBuffer = audioTrack.buffer || null;\n    }\n    if (data.tracks.video) {\n      this.videoBuffer = data.tracks.video.buffer || null;\n    }\n  }\n  onFragBuffered(event, data) {\n    const {\n      frag,\n      part\n    } = data;\n    if (frag.type !== PlaylistLevelType.AUDIO) {\n      if (!this.loadedmetadata && frag.type === PlaylistLevelType.MAIN) {\n        const bufferable = this.videoBuffer || this.media;\n        if (bufferable) {\n          const bufferedTimeRanges = BufferHelper.getBuffered(bufferable);\n          if (bufferedTimeRanges.length) {\n            this.loadedmetadata = true;\n          }\n        }\n      }\n      return;\n    }\n    if (this.fragContextChanged(frag)) {\n      // If a level switch was requested while a fragment was buffering, it will emit the FRAG_BUFFERED event upon completion\n      // Avoid setting state back to IDLE or concluding the audio switch; otherwise, the switched-to track will not buffer\n      this.warn(`Fragment ${frag.sn}${part ? ' p: ' + part.index : ''} of level ${frag.level} finished buffering, but was aborted. state: ${this.state}, audioSwitch: ${this.switchingTrack ? this.switchingTrack.name : 'false'}`);\n      return;\n    }\n    if (frag.sn !== 'initSegment') {\n      this.fragPrevious = frag;\n      const track = this.switchingTrack;\n      if (track) {\n        this.bufferedTrack = track;\n        this.switchingTrack = null;\n        this.hls.trigger(Events.AUDIO_TRACK_SWITCHED, _objectSpread2({}, track));\n      }\n    }\n    this.fragBufferedComplete(frag, part);\n  }\n  onError(event, data) {\n    var _data$context;\n    if (data.fatal) {\n      this.state = State.ERROR;\n      return;\n    }\n    switch (data.details) {\n      case ErrorDetails.FRAG_GAP:\n      case ErrorDetails.FRAG_PARSING_ERROR:\n      case ErrorDetails.FRAG_DECRYPT_ERROR:\n      case ErrorDetails.FRAG_LOAD_ERROR:\n      case ErrorDetails.FRAG_LOAD_TIMEOUT:\n      case ErrorDetails.KEY_LOAD_ERROR:\n      case ErrorDetails.KEY_LOAD_TIMEOUT:\n        this.onFragmentOrKeyLoadError(PlaylistLevelType.AUDIO, data);\n        break;\n      case ErrorDetails.AUDIO_TRACK_LOAD_ERROR:\n      case ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT:\n      case ErrorDetails.LEVEL_PARSING_ERROR:\n        // in case of non fatal error while loading track, if not retrying to load track, switch back to IDLE\n        if (!data.levelRetry && this.state === State.WAITING_TRACK && ((_data$context = data.context) == null ? void 0 : _data$context.type) === PlaylistContextType.AUDIO_TRACK) {\n          this.state = State.IDLE;\n        }\n        break;\n      case ErrorDetails.BUFFER_FULL_ERROR:\n        if (!data.parent || data.parent !== 'audio') {\n          return;\n        }\n        if (this.reduceLengthAndFlushBuffer(data)) {\n          this.bufferedTrack = null;\n          super.flushMainBuffer(0, Number.POSITIVE_INFINITY, 'audio');\n        }\n        break;\n      case ErrorDetails.INTERNAL_EXCEPTION:\n        this.recoverWorkerError(data);\n        break;\n    }\n  }\n  onBufferFlushed(event, {\n    type\n  }) {\n    if (type === ElementaryStreamTypes.AUDIO) {\n      this.bufferFlushed = true;\n      if (this.state === State.ENDED) {\n        this.state = State.IDLE;\n      }\n    }\n  }\n  _handleTransmuxComplete(transmuxResult) {\n    var _id3$samples;\n    const id = 'audio';\n    const {\n      hls\n    } = this;\n    const {\n      remuxResult,\n      chunkMeta\n    } = transmuxResult;\n    const context = this.getCurrentContext(chunkMeta);\n    if (!context) {\n      this.resetWhenMissingContext(chunkMeta);\n      return;\n    }\n    const {\n      frag,\n      part,\n      level\n    } = context;\n    const {\n      details\n    } = level;\n    const {\n      audio,\n      text,\n      id3,\n      initSegment\n    } = remuxResult;\n\n    // Check if the current fragment has been aborted. We check this by first seeing if we're still playing the current level.\n    // If we are, subsequently check if the currently loading fragment (fragCurrent) has changed.\n    if (this.fragContextChanged(frag) || !details) {\n      this.fragmentTracker.removeFragment(frag);\n      return;\n    }\n    this.state = State.PARSING;\n    if (this.switchingTrack && audio) {\n      this.completeAudioSwitch(this.switchingTrack);\n    }\n    if (initSegment != null && initSegment.tracks) {\n      const mapFragment = frag.initSegment || frag;\n      this._bufferInitSegment(initSegment.tracks, mapFragment, chunkMeta);\n      hls.trigger(Events.FRAG_PARSING_INIT_SEGMENT, {\n        frag: mapFragment,\n        id,\n        tracks: initSegment.tracks\n      });\n      // Only flush audio from old audio tracks when PTS is known on new audio track\n    }\n\n    if (audio) {\n      const {\n        startPTS,\n        endPTS,\n        startDTS,\n        endDTS\n      } = audio;\n      if (part) {\n        part.elementaryStreams[ElementaryStreamTypes.AUDIO] = {\n          startPTS,\n          endPTS,\n          startDTS,\n          endDTS\n        };\n      }\n      frag.setElementaryStreamInfo(ElementaryStreamTypes.AUDIO, startPTS, endPTS, startDTS, endDTS);\n      this.bufferFragmentData(audio, frag, part, chunkMeta);\n    }\n    if (id3 != null && (_id3$samples = id3.samples) != null && _id3$samples.length) {\n      const emittedID3 = _extends({\n        id,\n        frag,\n        details\n      }, id3);\n      hls.trigger(Events.FRAG_PARSING_METADATA, emittedID3);\n    }\n    if (text) {\n      const emittedText = _extends({\n        id,\n        frag,\n        details\n      }, text);\n      hls.trigger(Events.FRAG_PARSING_USERDATA, emittedText);\n    }\n  }\n  _bufferInitSegment(tracks, frag, chunkMeta) {\n    if (this.state !== State.PARSING) {\n      return;\n    }\n    // delete any video track found on audio transmuxer\n    if (tracks.video) {\n      delete tracks.video;\n    }\n\n    // include levelCodec in audio and video tracks\n    const track = tracks.audio;\n    if (!track) {\n      return;\n    }\n    track.levelCodec = track.codec;\n    track.id = 'audio';\n    this.log(`Init audio buffer, container:${track.container}, codecs[parsed]=[${track.codec}]`);\n    this.hls.trigger(Events.BUFFER_CODECS, tracks);\n    const initSegment = track.initSegment;\n    if (initSegment != null && initSegment.byteLength) {\n      const segment = {\n        type: 'audio',\n        frag,\n        part: null,\n        chunkMeta,\n        parent: frag.type,\n        data: initSegment\n      };\n      this.hls.trigger(Events.BUFFER_APPENDING, segment);\n    }\n    // trigger handler right now\n    this.tick();\n  }\n  loadFragment(frag, track, targetBufferTime) {\n    // only load if fragment is not loaded or if in audio switch\n    const fragState = this.fragmentTracker.getState(frag);\n    this.fragCurrent = frag;\n\n    // we force a frag loading in audio switch as fragment tracker might not have evicted previous frags in case of quick audio switch\n    if (this.switchingTrack || fragState === FragmentState.NOT_LOADED || fragState === FragmentState.PARTIAL) {\n      var _track$details2;\n      if (frag.sn === 'initSegment') {\n        this._loadInitSegment(frag, track);\n      } else if ((_track$details2 = track.details) != null && _track$details2.live && !this.initPTS[frag.cc]) {\n        this.log(`Waiting for video PTS in continuity counter ${frag.cc} of live stream before loading audio fragment ${frag.sn} of level ${this.trackId}`);\n        this.state = State.WAITING_INIT_PTS;\n      } else {\n        this.startFragRequested = true;\n        super.loadFragment(frag, track, targetBufferTime);\n      }\n    } else {\n      this.clearTrackerIfNeeded(frag);\n    }\n  }\n  completeAudioSwitch(switchingTrack) {\n    const {\n      hls,\n      media,\n      bufferedTrack\n    } = this;\n    const bufferedAttributes = bufferedTrack == null ? void 0 : bufferedTrack.attrs;\n    const switchAttributes = switchingTrack.attrs;\n    if (media && bufferedAttributes && (bufferedAttributes.CHANNELS !== switchAttributes.CHANNELS || bufferedAttributes.NAME !== switchAttributes.NAME || bufferedAttributes.LANGUAGE !== switchAttributes.LANGUAGE)) {\n      this.log('Switching audio track : flushing all audio');\n      super.flushMainBuffer(0, Number.POSITIVE_INFINITY, 'audio');\n    }\n    this.bufferedTrack = switchingTrack;\n    this.switchingTrack = null;\n    hls.trigger(Events.AUDIO_TRACK_SWITCHED, _objectSpread2({}, switchingTrack));\n  }\n}\n\nclass AudioTrackController extends BasePlaylistController {\n  constructor(hls) {\n    super(hls, '[audio-track-controller]');\n    this.tracks = [];\n    this.groupId = null;\n    this.tracksInGroup = [];\n    this.trackId = -1;\n    this.currentTrack = null;\n    this.selectDefaultTrack = true;\n    this.registerListeners();\n  }\n  registerListeners() {\n    const {\n      hls\n    } = this;\n    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);\n    hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);\n    hls.on(Events.LEVEL_SWITCHING, this.onLevelSwitching, this);\n    hls.on(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this);\n    hls.on(Events.ERROR, this.onError, this);\n  }\n  unregisterListeners() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);\n    hls.off(Events.LEVEL_LOADING, this.onLevelLoading, this);\n    hls.off(Events.LEVEL_SWITCHING, this.onLevelSwitching, this);\n    hls.off(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this);\n    hls.off(Events.ERROR, this.onError, this);\n  }\n  destroy() {\n    this.unregisterListeners();\n    this.tracks.length = 0;\n    this.tracksInGroup.length = 0;\n    this.currentTrack = null;\n    super.destroy();\n  }\n  onManifestLoading() {\n    this.tracks = [];\n    this.groupId = null;\n    this.tracksInGroup = [];\n    this.trackId = -1;\n    this.currentTrack = null;\n    this.selectDefaultTrack = true;\n  }\n  onManifestParsed(event, data) {\n    this.tracks = data.audioTracks || [];\n  }\n  onAudioTrackLoaded(event, data) {\n    const {\n      id,\n      groupId,\n      details\n    } = data;\n    const trackInActiveGroup = this.tracksInGroup[id];\n    if (!trackInActiveGroup || trackInActiveGroup.groupId !== groupId) {\n      this.warn(`Track with id:${id} and group:${groupId} not found in active group ${trackInActiveGroup.groupId}`);\n      return;\n    }\n    const curDetails = trackInActiveGroup.details;\n    trackInActiveGroup.details = data.details;\n    this.log(`audio-track ${id} \"${trackInActiveGroup.name}\" lang:${trackInActiveGroup.lang} group:${groupId} loaded [${details.startSN}-${details.endSN}]`);\n    if (id === this.trackId) {\n      this.playlistLoaded(id, data, curDetails);\n    }\n  }\n  onLevelLoading(event, data) {\n    this.switchLevel(data.level);\n  }\n  onLevelSwitching(event, data) {\n    this.switchLevel(data.level);\n  }\n  switchLevel(levelIndex) {\n    const levelInfo = this.hls.levels[levelIndex];\n    if (!(levelInfo != null && levelInfo.audioGroupIds)) {\n      return;\n    }\n    const audioGroupId = levelInfo.audioGroupIds[levelInfo.urlId];\n    if (this.groupId !== audioGroupId) {\n      this.groupId = audioGroupId || null;\n      const audioTracks = this.tracks.filter(track => !audioGroupId || track.groupId === audioGroupId);\n\n      // Disable selectDefaultTrack if there are no default tracks\n      if (this.selectDefaultTrack && !audioTracks.some(track => track.default)) {\n        this.selectDefaultTrack = false;\n      }\n      this.tracksInGroup = audioTracks;\n      const audioTracksUpdated = {\n        audioTracks\n      };\n      this.log(`Updating audio tracks, ${audioTracks.length} track(s) found in group:${audioGroupId}`);\n      this.hls.trigger(Events.AUDIO_TRACKS_UPDATED, audioTracksUpdated);\n      this.selectInitialTrack();\n    } else if (this.shouldReloadPlaylist(this.currentTrack)) {\n      // Retry playlist loading if no playlist is or has been loaded yet\n      this.setAudioTrack(this.trackId);\n    }\n  }\n  onError(event, data) {\n    if (data.fatal || !data.context) {\n      return;\n    }\n    if (data.context.type === PlaylistContextType.AUDIO_TRACK && data.context.id === this.trackId && data.context.groupId === this.groupId) {\n      this.requestScheduled = -1;\n      this.checkRetry(data);\n    }\n  }\n  get audioTracks() {\n    return this.tracksInGroup;\n  }\n  get audioTrack() {\n    return this.trackId;\n  }\n  set audioTrack(newId) {\n    // If audio track is selected from API then don't choose from the manifest default track\n    this.selectDefaultTrack = false;\n    this.setAudioTrack(newId);\n  }\n  setAudioTrack(newId) {\n    const tracks = this.tracksInGroup;\n\n    // check if level idx is valid\n    if (newId < 0 || newId >= tracks.length) {\n      this.warn('Invalid id passed to audio-track controller');\n      return;\n    }\n\n    // stopping live reloading timer if any\n    this.clearTimer();\n    const lastTrack = this.currentTrack;\n    tracks[this.trackId];\n    const track = tracks[newId];\n    const {\n      groupId,\n      name\n    } = track;\n    this.log(`Switching to audio-track ${newId} \"${name}\" lang:${track.lang} group:${groupId}`);\n    this.trackId = newId;\n    this.currentTrack = track;\n    this.selectDefaultTrack = false;\n    this.hls.trigger(Events.AUDIO_TRACK_SWITCHING, _objectSpread2({}, track));\n    // Do not reload track unless live\n    if (track.details && !track.details.live) {\n      return;\n    }\n    const hlsUrlParameters = this.switchParams(track.url, lastTrack == null ? void 0 : lastTrack.details);\n    this.loadPlaylist(hlsUrlParameters);\n  }\n  selectInitialTrack() {\n    const audioTracks = this.tracksInGroup;\n    const trackId = this.findTrackId(this.currentTrack) | this.findTrackId(null);\n    if (trackId !== -1) {\n      this.setAudioTrack(trackId);\n    } else {\n      const error = new Error(`No track found for running audio group-ID: ${this.groupId} track count: ${audioTracks.length}`);\n      this.warn(error.message);\n      this.hls.trigger(Events.ERROR, {\n        type: ErrorTypes.MEDIA_ERROR,\n        details: ErrorDetails.AUDIO_TRACK_LOAD_ERROR,\n        fatal: true,\n        error\n      });\n    }\n  }\n  findTrackId(currentTrack) {\n    const audioTracks = this.tracksInGroup;\n    for (let i = 0; i < audioTracks.length; i++) {\n      const track = audioTracks[i];\n      if (!this.selectDefaultTrack || track.default) {\n        if (!currentTrack || currentTrack.attrs['STABLE-RENDITION-ID'] !== undefined && currentTrack.attrs['STABLE-RENDITION-ID'] === track.attrs['STABLE-RENDITION-ID']) {\n          return track.id;\n        }\n        if (currentTrack.name === track.name && currentTrack.lang === track.lang) {\n          return track.id;\n        }\n      }\n    }\n    return -1;\n  }\n  loadPlaylist(hlsUrlParameters) {\n    super.loadPlaylist();\n    const audioTrack = this.tracksInGroup[this.trackId];\n    if (this.shouldLoadPlaylist(audioTrack)) {\n      const id = audioTrack.id;\n      const groupId = audioTrack.groupId;\n      let url = audioTrack.url;\n      if (hlsUrlParameters) {\n        try {\n          url = hlsUrlParameters.addDirectives(url);\n        } catch (error) {\n          this.warn(`Could not construct new URL with HLS Delivery Directives: ${error}`);\n        }\n      }\n      // track not retrieved yet, or live playlist we need to (re)load it\n      this.log(`loading audio-track playlist ${id} \"${audioTrack.name}\" lang:${audioTrack.lang} group:${groupId}`);\n      this.clearTimer();\n      this.hls.trigger(Events.AUDIO_TRACK_LOADING, {\n        url,\n        id,\n        groupId,\n        deliveryDirectives: hlsUrlParameters || null\n      });\n    }\n  }\n}\n\nfunction subtitleOptionsIdentical(trackList1, trackList2) {\n  if (trackList1.length !== trackList2.length) {\n    return false;\n  }\n  for (let i = 0; i < trackList1.length; i++) {\n    if (!subtitleAttributesIdentical(trackList1[i].attrs, trackList2[i].attrs)) {\n      return false;\n    }\n  }\n  return true;\n}\nfunction subtitleAttributesIdentical(attrs1, attrs2) {\n  // Media options with the same rendition ID must be bit identical\n  const stableRenditionId = attrs1['STABLE-RENDITION-ID'];\n  if (stableRenditionId) {\n    return stableRenditionId === attrs2['STABLE-RENDITION-ID'];\n  }\n  // When rendition ID is not present, compare attributes\n  return !['LANGUAGE', 'NAME', 'CHARACTERISTICS', 'AUTOSELECT', 'DEFAULT', 'FORCED'].some(subtitleAttribute => attrs1[subtitleAttribute] !== attrs2[subtitleAttribute]);\n}\n\nconst TICK_INTERVAL = 500; // how often to tick in ms\n\nclass SubtitleStreamController extends BaseStreamController {\n  constructor(hls, fragmentTracker, keyLoader) {\n    super(hls, fragmentTracker, keyLoader, '[subtitle-stream-controller]', PlaylistLevelType.SUBTITLE);\n    this.levels = [];\n    this.currentTrackId = -1;\n    this.tracksBuffered = [];\n    this.mainDetails = null;\n    this._registerListeners();\n  }\n  onHandlerDestroying() {\n    this._unregisterListeners();\n    this.mainDetails = null;\n  }\n  _registerListeners() {\n    const {\n      hls\n    } = this;\n    hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);\n    hls.on(Events.ERROR, this.onError, this);\n    hls.on(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);\n    hls.on(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);\n    hls.on(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this);\n    hls.on(Events.SUBTITLE_FRAG_PROCESSED, this.onSubtitleFragProcessed, this);\n    hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);\n    hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);\n  }\n  _unregisterListeners() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);\n    hls.off(Events.ERROR, this.onError, this);\n    hls.off(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);\n    hls.off(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);\n    hls.off(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this);\n    hls.off(Events.SUBTITLE_FRAG_PROCESSED, this.onSubtitleFragProcessed, this);\n    hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);\n    hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);\n  }\n  startLoad(startPosition) {\n    this.stopLoad();\n    this.state = State.IDLE;\n    this.setInterval(TICK_INTERVAL);\n    this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition;\n    this.tick();\n  }\n  onManifestLoading() {\n    this.mainDetails = null;\n    this.fragmentTracker.removeAllFragments();\n  }\n  onMediaDetaching() {\n    this.tracksBuffered = [];\n    super.onMediaDetaching();\n  }\n  onLevelLoaded(event, data) {\n    this.mainDetails = data.details;\n  }\n  onSubtitleFragProcessed(event, data) {\n    const {\n      frag,\n      success\n    } = data;\n    this.fragPrevious = frag;\n    this.state = State.IDLE;\n    if (!success) {\n      return;\n    }\n    const buffered = this.tracksBuffered[this.currentTrackId];\n    if (!buffered) {\n      return;\n    }\n\n    // Create/update a buffered array matching the interface used by BufferHelper.bufferedInfo\n    // so we can re-use the logic used to detect how much has been buffered\n    let timeRange;\n    const fragStart = frag.start;\n    for (let i = 0; i < buffered.length; i++) {\n      if (fragStart >= buffered[i].start && fragStart <= buffered[i].end) {\n        timeRange = buffered[i];\n        break;\n      }\n    }\n    const fragEnd = frag.start + frag.duration;\n    if (timeRange) {\n      timeRange.end = fragEnd;\n    } else {\n      timeRange = {\n        start: fragStart,\n        end: fragEnd\n      };\n      buffered.push(timeRange);\n    }\n    this.fragmentTracker.fragBuffered(frag);\n  }\n  onBufferFlushing(event, data) {\n    const {\n      startOffset,\n      endOffset\n    } = data;\n    if (startOffset === 0 && endOffset !== Number.POSITIVE_INFINITY) {\n      const endOffsetSubtitles = endOffset - 1;\n      if (endOffsetSubtitles <= 0) {\n        return;\n      }\n      data.endOffsetSubtitles = Math.max(0, endOffsetSubtitles);\n      this.tracksBuffered.forEach(buffered => {\n        for (let i = 0; i < buffered.length;) {\n          if (buffered[i].end <= endOffsetSubtitles) {\n            buffered.shift();\n            continue;\n          } else if (buffered[i].start < endOffsetSubtitles) {\n            buffered[i].start = endOffsetSubtitles;\n          } else {\n            break;\n          }\n          i++;\n        }\n      });\n      this.fragmentTracker.removeFragmentsInRange(startOffset, endOffsetSubtitles, PlaylistLevelType.SUBTITLE);\n    }\n  }\n  onFragBuffered(event, data) {\n    if (!this.loadedmetadata && data.frag.type === PlaylistLevelType.MAIN) {\n      var _this$media;\n      if ((_this$media = this.media) != null && _this$media.buffered.length) {\n        this.loadedmetadata = true;\n      }\n    }\n  }\n\n  // If something goes wrong, proceed to next frag, if we were processing one.\n  onError(event, data) {\n    const frag = data.frag;\n    if ((frag == null ? void 0 : frag.type) === PlaylistLevelType.SUBTITLE) {\n      if (this.fragCurrent) {\n        this.fragCurrent.abortRequests();\n      }\n      if (this.state !== State.STOPPED) {\n        this.state = State.IDLE;\n      }\n    }\n  }\n\n  // Got all new subtitle levels.\n  onSubtitleTracksUpdated(event, {\n    subtitleTracks\n  }) {\n    if (subtitleOptionsIdentical(this.levels, subtitleTracks)) {\n      this.levels = subtitleTracks.map(mediaPlaylist => new Level(mediaPlaylist));\n      return;\n    }\n    this.tracksBuffered = [];\n    this.levels = subtitleTracks.map(mediaPlaylist => {\n      const level = new Level(mediaPlaylist);\n      this.tracksBuffered[level.id] = [];\n      return level;\n    });\n    this.fragmentTracker.removeFragmentsInRange(0, Number.POSITIVE_INFINITY, PlaylistLevelType.SUBTITLE);\n    this.fragPrevious = null;\n    this.mediaBuffer = null;\n  }\n  onSubtitleTrackSwitch(event, data) {\n    this.currentTrackId = data.id;\n    if (!this.levels.length || this.currentTrackId === -1) {\n      this.clearInterval();\n      return;\n    }\n\n    // Check if track has the necessary details to load fragments\n    const currentTrack = this.levels[this.currentTrackId];\n    if (currentTrack != null && currentTrack.details) {\n      this.mediaBuffer = this.mediaBufferTimeRanges;\n    } else {\n      this.mediaBuffer = null;\n    }\n    if (currentTrack) {\n      this.setInterval(TICK_INTERVAL);\n    }\n  }\n\n  // Got a new set of subtitle fragments.\n  onSubtitleTrackLoaded(event, data) {\n    var _track$details;\n    const {\n      details: newDetails,\n      id: trackId\n    } = data;\n    const {\n      currentTrackId,\n      levels\n    } = this;\n    if (!levels.length) {\n      return;\n    }\n    const track = levels[currentTrackId];\n    if (trackId >= levels.length || trackId !== currentTrackId || !track) {\n      return;\n    }\n    this.mediaBuffer = this.mediaBufferTimeRanges;\n    let sliding = 0;\n    if (newDetails.live || (_track$details = track.details) != null && _track$details.live) {\n      const mainDetails = this.mainDetails;\n      if (newDetails.deltaUpdateFailed || !mainDetails) {\n        return;\n      }\n      const mainSlidingStartFragment = mainDetails.fragments[0];\n      if (!track.details) {\n        if (newDetails.hasProgramDateTime && mainDetails.hasProgramDateTime) {\n          alignMediaPlaylistByPDT(newDetails, mainDetails);\n          sliding = newDetails.fragments[0].start;\n        } else if (mainSlidingStartFragment) {\n          // line up live playlist with main so that fragments in range are loaded\n          sliding = mainSlidingStartFragment.start;\n          addSliding(newDetails, sliding);\n        }\n      } else {\n        sliding = this.alignPlaylists(newDetails, track.details);\n        if (sliding === 0 && mainSlidingStartFragment) {\n          // realign with main when there is no overlap with last refresh\n          sliding = mainSlidingStartFragment.start;\n          addSliding(newDetails, sliding);\n        }\n      }\n    }\n    track.details = newDetails;\n    this.levelLastLoaded = trackId;\n    if (!this.startFragRequested && (this.mainDetails || !newDetails.live)) {\n      this.setStartPosition(track.details, sliding);\n    }\n\n    // trigger handler right now\n    this.tick();\n\n    // If playlist is misaligned because of bad PDT or drift, delete details to resync with main on reload\n    if (newDetails.live && !this.fragCurrent && this.media && this.state === State.IDLE) {\n      const foundFrag = findFragmentByPTS(null, newDetails.fragments, this.media.currentTime, 0);\n      if (!foundFrag) {\n        this.warn('Subtitle playlist not aligned with playback');\n        track.details = undefined;\n      }\n    }\n  }\n  _handleFragmentLoadComplete(fragLoadedData) {\n    const {\n      frag,\n      payload\n    } = fragLoadedData;\n    const decryptData = frag.decryptdata;\n    const hls = this.hls;\n    if (this.fragContextChanged(frag)) {\n      return;\n    }\n    // check to see if the payload needs to be decrypted\n    if (payload && payload.byteLength > 0 && decryptData && decryptData.key && decryptData.iv && decryptData.method === 'AES-128') {\n      const startTime = performance.now();\n      // decrypt the subtitles\n      this.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer).catch(err => {\n        hls.trigger(Events.ERROR, {\n          type: ErrorTypes.MEDIA_ERROR,\n          details: ErrorDetails.FRAG_DECRYPT_ERROR,\n          fatal: false,\n          error: err,\n          reason: err.message,\n          frag\n        });\n        throw err;\n      }).then(decryptedData => {\n        const endTime = performance.now();\n        hls.trigger(Events.FRAG_DECRYPTED, {\n          frag,\n          payload: decryptedData,\n          stats: {\n            tstart: startTime,\n            tdecrypt: endTime\n          }\n        });\n      }).catch(err => {\n        this.warn(`${err.name}: ${err.message}`);\n        this.state = State.IDLE;\n      });\n    }\n  }\n  doTick() {\n    if (!this.media) {\n      this.state = State.IDLE;\n      return;\n    }\n    if (this.state === State.IDLE) {\n      const {\n        currentTrackId,\n        levels\n      } = this;\n      const track = levels[currentTrackId];\n      if (!levels.length || !track || !track.details) {\n        return;\n      }\n      const {\n        config\n      } = this;\n      const currentTime = this.getLoadPosition();\n      const bufferedInfo = BufferHelper.bufferedInfo(this.tracksBuffered[this.currentTrackId] || [], currentTime, config.maxBufferHole);\n      const {\n        end: targetBufferTime,\n        len: bufferLen\n      } = bufferedInfo;\n      const mainBufferInfo = this.getFwdBufferInfo(this.media, PlaylistLevelType.MAIN);\n      const trackDetails = track.details;\n      const maxBufLen = this.getMaxBufferLength(mainBufferInfo == null ? void 0 : mainBufferInfo.len) + trackDetails.levelTargetDuration;\n      if (bufferLen > maxBufLen) {\n        return;\n      }\n      const fragments = trackDetails.fragments;\n      const fragLen = fragments.length;\n      const end = trackDetails.edge;\n      let foundFrag = null;\n      const fragPrevious = this.fragPrevious;\n      if (targetBufferTime < end) {\n        const tolerance = config.maxFragLookUpTolerance;\n        const lookupTolerance = targetBufferTime > end - tolerance ? 0 : tolerance;\n        foundFrag = findFragmentByPTS(fragPrevious, fragments, Math.max(fragments[0].start, targetBufferTime), lookupTolerance);\n        if (!foundFrag && fragPrevious && fragPrevious.start < fragments[0].start) {\n          foundFrag = fragments[0];\n        }\n      } else {\n        foundFrag = fragments[fragLen - 1];\n      }\n      if (!foundFrag) {\n        return;\n      }\n      foundFrag = this.mapToInitFragWhenRequired(foundFrag);\n      if (foundFrag.sn !== 'initSegment') {\n        // Load earlier fragment in same discontinuity to make up for misaligned playlists and cues that extend beyond end of segment\n        const curSNIdx = foundFrag.sn - trackDetails.startSN;\n        const prevFrag = fragments[curSNIdx - 1];\n        if (prevFrag && prevFrag.cc === foundFrag.cc && this.fragmentTracker.getState(prevFrag) === FragmentState.NOT_LOADED) {\n          foundFrag = prevFrag;\n        }\n      }\n      if (this.fragmentTracker.getState(foundFrag) === FragmentState.NOT_LOADED) {\n        // only load if fragment is not loaded\n        this.loadFragment(foundFrag, track, targetBufferTime);\n      }\n    }\n  }\n  getMaxBufferLength(mainBufferLength) {\n    const maxConfigBuffer = super.getMaxBufferLength();\n    if (!mainBufferLength) {\n      return maxConfigBuffer;\n    }\n    return Math.max(maxConfigBuffer, mainBufferLength);\n  }\n  loadFragment(frag, level, targetBufferTime) {\n    this.fragCurrent = frag;\n    if (frag.sn === 'initSegment') {\n      this._loadInitSegment(frag, level);\n    } else {\n      this.startFragRequested = true;\n      super.loadFragment(frag, level, targetBufferTime);\n    }\n  }\n  get mediaBufferTimeRanges() {\n    return new BufferableInstance(this.tracksBuffered[this.currentTrackId] || []);\n  }\n}\nclass BufferableInstance {\n  constructor(timeranges) {\n    this.buffered = void 0;\n    const getRange = (name, index, length) => {\n      index = index >>> 0;\n      if (index > length - 1) {\n        throw new DOMException(`Failed to execute '${name}' on 'TimeRanges': The index provided (${index}) is greater than the maximum bound (${length})`);\n      }\n      return timeranges[index][name];\n    };\n    this.buffered = {\n      get length() {\n        return timeranges.length;\n      },\n      end(index) {\n        return getRange('end', index, timeranges.length);\n      },\n      start(index) {\n        return getRange('start', index, timeranges.length);\n      }\n    };\n  }\n}\n\nclass SubtitleTrackController extends BasePlaylistController {\n  constructor(hls) {\n    super(hls, '[subtitle-track-controller]');\n    this.media = null;\n    this.tracks = [];\n    this.groupId = null;\n    this.tracksInGroup = [];\n    this.trackId = -1;\n    this.selectDefaultTrack = true;\n    this.queuedDefaultTrack = -1;\n    this.trackChangeListener = () => this.onTextTracksChanged();\n    this.asyncPollTrackChange = () => this.pollTrackChange(0);\n    this.useTextTrackPolling = false;\n    this.subtitlePollingInterval = -1;\n    this._subtitleDisplay = true;\n    this.registerListeners();\n  }\n  destroy() {\n    this.unregisterListeners();\n    this.tracks.length = 0;\n    this.tracksInGroup.length = 0;\n    this.trackChangeListener = this.asyncPollTrackChange = null;\n    super.destroy();\n  }\n  get subtitleDisplay() {\n    return this._subtitleDisplay;\n  }\n  set subtitleDisplay(value) {\n    this._subtitleDisplay = value;\n    if (this.trackId > -1) {\n      this.toggleTrackModes(this.trackId);\n    }\n  }\n  registerListeners() {\n    const {\n      hls\n    } = this;\n    hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);\n    hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);\n    hls.on(Events.LEVEL_SWITCHING, this.onLevelSwitching, this);\n    hls.on(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this);\n    hls.on(Events.ERROR, this.onError, this);\n  }\n  unregisterListeners() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);\n    hls.off(Events.LEVEL_LOADING, this.onLevelLoading, this);\n    hls.off(Events.LEVEL_SWITCHING, this.onLevelSwitching, this);\n    hls.off(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this);\n    hls.off(Events.ERROR, this.onError, this);\n  }\n\n  // Listen for subtitle track change, then extract the current track ID.\n  onMediaAttached(event, data) {\n    this.media = data.media;\n    if (!this.media) {\n      return;\n    }\n    if (this.queuedDefaultTrack > -1) {\n      this.subtitleTrack = this.queuedDefaultTrack;\n      this.queuedDefaultTrack = -1;\n    }\n    this.useTextTrackPolling = !(this.media.textTracks && 'onchange' in this.media.textTracks);\n    if (this.useTextTrackPolling) {\n      this.pollTrackChange(500);\n    } else {\n      this.media.textTracks.addEventListener('change', this.asyncPollTrackChange);\n    }\n  }\n  pollTrackChange(timeout) {\n    self.clearInterval(this.subtitlePollingInterval);\n    this.subtitlePollingInterval = self.setInterval(this.trackChangeListener, timeout);\n  }\n  onMediaDetaching() {\n    if (!this.media) {\n      return;\n    }\n    self.clearInterval(this.subtitlePollingInterval);\n    if (!this.useTextTrackPolling) {\n      this.media.textTracks.removeEventListener('change', this.asyncPollTrackChange);\n    }\n    if (this.trackId > -1) {\n      this.queuedDefaultTrack = this.trackId;\n    }\n    const textTracks = filterSubtitleTracks(this.media.textTracks);\n    // Clear loaded cues on media detachment from tracks\n    textTracks.forEach(track => {\n      clearCurrentCues(track);\n    });\n    // Disable all subtitle tracks before detachment so when reattached only tracks in that content are enabled.\n    this.subtitleTrack = -1;\n    this.media = null;\n  }\n  onManifestLoading() {\n    this.tracks = [];\n    this.groupId = null;\n    this.tracksInGroup = [];\n    this.trackId = -1;\n    this.selectDefaultTrack = true;\n  }\n\n  // Fired whenever a new manifest is loaded.\n  onManifestParsed(event, data) {\n    this.tracks = data.subtitleTracks;\n  }\n  onSubtitleTrackLoaded(event, data) {\n    const {\n      id,\n      details\n    } = data;\n    const {\n      trackId\n    } = this;\n    const currentTrack = this.tracksInGroup[trackId];\n    if (!currentTrack) {\n      this.warn(`Invalid subtitle track id ${id}`);\n      return;\n    }\n    const curDetails = currentTrack.details;\n    currentTrack.details = data.details;\n    this.log(`subtitle track ${id} loaded [${details.startSN}-${details.endSN}]`);\n    if (id === this.trackId) {\n      this.playlistLoaded(id, data, curDetails);\n    }\n  }\n  onLevelLoading(event, data) {\n    this.switchLevel(data.level);\n  }\n  onLevelSwitching(event, data) {\n    this.switchLevel(data.level);\n  }\n  switchLevel(levelIndex) {\n    const levelInfo = this.hls.levels[levelIndex];\n    if (!(levelInfo != null && levelInfo.textGroupIds)) {\n      return;\n    }\n    const textGroupId = levelInfo.textGroupIds[levelInfo.urlId];\n    const lastTrack = this.tracksInGroup ? this.tracksInGroup[this.trackId] : undefined;\n    if (this.groupId !== textGroupId) {\n      const subtitleTracks = this.tracks.filter(track => !textGroupId || track.groupId === textGroupId);\n      this.tracksInGroup = subtitleTracks;\n      const initialTrackId = this.findTrackId(lastTrack == null ? void 0 : lastTrack.name) || this.findTrackId();\n      this.groupId = textGroupId || null;\n      const subtitleTracksUpdated = {\n        subtitleTracks\n      };\n      this.log(`Updating subtitle tracks, ${subtitleTracks.length} track(s) found in \"${textGroupId}\" group-id`);\n      this.hls.trigger(Events.SUBTITLE_TRACKS_UPDATED, subtitleTracksUpdated);\n      if (initialTrackId !== -1) {\n        this.setSubtitleTrack(initialTrackId, lastTrack);\n      }\n    } else if (this.shouldReloadPlaylist(lastTrack)) {\n      // Retry playlist loading if no playlist is or has been loaded yet\n      this.setSubtitleTrack(this.trackId, lastTrack);\n    }\n  }\n  findTrackId(name) {\n    const textTracks = this.tracksInGroup;\n    for (let i = 0; i < textTracks.length; i++) {\n      const track = textTracks[i];\n      if (!this.selectDefaultTrack || track.default) {\n        if (!name || name === track.name) {\n          return track.id;\n        }\n      }\n    }\n    return -1;\n  }\n  onError(event, data) {\n    if (data.fatal || !data.context) {\n      return;\n    }\n    if (data.context.type === PlaylistContextType.SUBTITLE_TRACK && data.context.id === this.trackId && data.context.groupId === this.groupId) {\n      this.checkRetry(data);\n    }\n  }\n\n  /** get alternate subtitle tracks list from playlist **/\n  get subtitleTracks() {\n    return this.tracksInGroup;\n  }\n\n  /** get/set index of the selected subtitle track (based on index in subtitle track lists) **/\n  get subtitleTrack() {\n    return this.trackId;\n  }\n  set subtitleTrack(newId) {\n    this.selectDefaultTrack = false;\n    const lastTrack = this.tracksInGroup ? this.tracksInGroup[this.trackId] : undefined;\n    this.setSubtitleTrack(newId, lastTrack);\n  }\n  loadPlaylist(hlsUrlParameters) {\n    super.loadPlaylist();\n    const currentTrack = this.tracksInGroup[this.trackId];\n    if (this.shouldLoadPlaylist(currentTrack)) {\n      const id = currentTrack.id;\n      const groupId = currentTrack.groupId;\n      let url = currentTrack.url;\n      if (hlsUrlParameters) {\n        try {\n          url = hlsUrlParameters.addDirectives(url);\n        } catch (error) {\n          this.warn(`Could not construct new URL with HLS Delivery Directives: ${error}`);\n        }\n      }\n      this.log(`Loading subtitle playlist for id ${id}`);\n      this.hls.trigger(Events.SUBTITLE_TRACK_LOADING, {\n        url,\n        id,\n        groupId,\n        deliveryDirectives: hlsUrlParameters || null\n      });\n    }\n  }\n\n  /**\n   * Disables the old subtitleTrack and sets current mode on the next subtitleTrack.\n   * This operates on the DOM textTracks.\n   * A value of -1 will disable all subtitle tracks.\n   */\n  toggleTrackModes(newId) {\n    const {\n      media,\n      trackId\n    } = this;\n    if (!media) {\n      return;\n    }\n    const textTracks = filterSubtitleTracks(media.textTracks);\n    const groupTracks = textTracks.filter(track => track.groupId === this.groupId);\n    if (newId === -1) {\n      [].slice.call(textTracks).forEach(track => {\n        track.mode = 'disabled';\n      });\n    } else {\n      const oldTrack = groupTracks[trackId];\n      if (oldTrack) {\n        oldTrack.mode = 'disabled';\n      }\n    }\n    const nextTrack = groupTracks[newId];\n    if (nextTrack) {\n      nextTrack.mode = this.subtitleDisplay ? 'showing' : 'hidden';\n    }\n  }\n\n  /**\n   * This method is responsible for validating the subtitle index and periodically reloading if live.\n   * Dispatches the SUBTITLE_TRACK_SWITCH event, which instructs the subtitle-stream-controller to load the selected track.\n   */\n  setSubtitleTrack(newId, lastTrack) {\n    var _tracks$newId;\n    const tracks = this.tracksInGroup;\n\n    // setting this.subtitleTrack will trigger internal logic\n    // if media has not been attached yet, it will fail\n    // we keep a reference to the default track id\n    // and we'll set subtitleTrack when onMediaAttached is triggered\n    if (!this.media) {\n      this.queuedDefaultTrack = newId;\n      return;\n    }\n    if (this.trackId !== newId) {\n      this.toggleTrackModes(newId);\n    }\n\n    // exit if track id as already set or invalid\n    if (this.trackId === newId && (newId === -1 || (_tracks$newId = tracks[newId]) != null && _tracks$newId.details) || newId < -1 || newId >= tracks.length) {\n      return;\n    }\n\n    // stopping live reloading timer if any\n    this.clearTimer();\n    const track = tracks[newId];\n    this.log(`Switching to subtitle-track ${newId}` + (track ? ` \"${track.name}\" lang:${track.lang} group:${track.groupId}` : ''));\n    this.trackId = newId;\n    if (track) {\n      const {\n        id,\n        groupId = '',\n        name,\n        type,\n        url\n      } = track;\n      this.hls.trigger(Events.SUBTITLE_TRACK_SWITCH, {\n        id,\n        groupId,\n        name,\n        type,\n        url\n      });\n      const hlsUrlParameters = this.switchParams(track.url, lastTrack == null ? void 0 : lastTrack.details);\n      this.loadPlaylist(hlsUrlParameters);\n    } else {\n      // switch to -1\n      this.hls.trigger(Events.SUBTITLE_TRACK_SWITCH, {\n        id: newId\n      });\n    }\n  }\n  onTextTracksChanged() {\n    if (!this.useTextTrackPolling) {\n      self.clearInterval(this.subtitlePollingInterval);\n    }\n    // Media is undefined when switching streams via loadSource()\n    if (!this.media || !this.hls.config.renderTextTracksNatively) {\n      return;\n    }\n    let trackId = -1;\n    const tracks = filterSubtitleTracks(this.media.textTracks);\n    for (let id = 0; id < tracks.length; id++) {\n      if (tracks[id].mode === 'hidden') {\n        // Do not break in case there is a following track with showing.\n        trackId = id;\n      } else if (tracks[id].mode === 'showing') {\n        trackId = id;\n        break;\n      }\n    }\n\n    // Setting current subtitleTrack will invoke code.\n    if (this.subtitleTrack !== trackId) {\n      this.subtitleTrack = trackId;\n    }\n  }\n}\nfunction filterSubtitleTracks(textTrackList) {\n  const tracks = [];\n  for (let i = 0; i < textTrackList.length; i++) {\n    const track = textTrackList[i];\n    // Edge adds a track without a label; we don't want to use it\n    if ((track.kind === 'subtitles' || track.kind === 'captions') && track.label) {\n      tracks.push(textTrackList[i]);\n    }\n  }\n  return tracks;\n}\n\nclass BufferOperationQueue {\n  constructor(sourceBufferReference) {\n    this.buffers = void 0;\n    this.queues = {\n      video: [],\n      audio: [],\n      audiovideo: []\n    };\n    this.buffers = sourceBufferReference;\n  }\n  append(operation, type) {\n    const queue = this.queues[type];\n    queue.push(operation);\n    if (queue.length === 1 && this.buffers[type]) {\n      this.executeNext(type);\n    }\n  }\n  insertAbort(operation, type) {\n    const queue = this.queues[type];\n    queue.unshift(operation);\n    this.executeNext(type);\n  }\n  appendBlocker(type) {\n    let execute;\n    const promise = new Promise(resolve => {\n      execute = resolve;\n    });\n    const operation = {\n      execute,\n      onStart: () => {},\n      onComplete: () => {},\n      onError: () => {}\n    };\n    this.append(operation, type);\n    return promise;\n  }\n  executeNext(type) {\n    const {\n      buffers,\n      queues\n    } = this;\n    const sb = buffers[type];\n    const queue = queues[type];\n    if (queue.length) {\n      const operation = queue[0];\n      try {\n        // Operations are expected to result in an 'updateend' event being fired. If not, the queue will lock. Operations\n        // which do not end with this event must call _onSBUpdateEnd manually\n        operation.execute();\n      } catch (e) {\n        logger.warn('[buffer-operation-queue]: Unhandled exception executing the current operation');\n        operation.onError(e);\n\n        // Only shift the current operation off, otherwise the updateend handler will do this for us\n        if (!(sb != null && sb.updating)) {\n          queue.shift();\n          this.executeNext(type);\n        }\n      }\n    }\n  }\n  shiftAndExecuteNext(type) {\n    this.queues[type].shift();\n    this.executeNext(type);\n  }\n  current(type) {\n    return this.queues[type][0];\n  }\n}\n\nconst MediaSource = getMediaSource();\nconst VIDEO_CODEC_PROFILE_REPACE = /([ha]vc.)(?:\\.[^.,]+)+/;\nclass BufferController {\n  // The level details used to determine duration, target-duration and live\n\n  // cache the self generated object url to detect hijack of video tag\n\n  // A queue of buffer operations which require the SourceBuffer to not be updating upon execution\n\n  // References to event listeners for each SourceBuffer, so that they can be referenced for event removal\n\n  // The number of BUFFER_CODEC events received before any sourceBuffers are created\n\n  // The total number of BUFFER_CODEC events received\n\n  // A reference to the attached media element\n\n  // A reference to the active media source\n\n  // Last MP3 audio chunk appended\n\n  // counters\n\n  constructor(hls) {\n    this.details = null;\n    this._objectUrl = null;\n    this.operationQueue = void 0;\n    this.listeners = void 0;\n    this.hls = void 0;\n    this.bufferCodecEventsExpected = 0;\n    this._bufferCodecEventsTotal = 0;\n    this.media = null;\n    this.mediaSource = null;\n    this.lastMpegAudioChunk = null;\n    this.appendError = 0;\n    this.tracks = {};\n    this.pendingTracks = {};\n    this.sourceBuffer = void 0;\n    // Keep as arrow functions so that we can directly reference these functions directly as event listeners\n    this._onMediaSourceOpen = () => {\n      const {\n        media,\n        mediaSource\n      } = this;\n      logger.log('[buffer-controller]: Media source opened');\n      if (media) {\n        media.removeEventListener('emptied', this._onMediaEmptied);\n        this.updateMediaElementDuration();\n        this.hls.trigger(Events.MEDIA_ATTACHED, {\n          media\n        });\n      }\n      if (mediaSource) {\n        // once received, don't listen anymore to sourceopen event\n        mediaSource.removeEventListener('sourceopen', this._onMediaSourceOpen);\n      }\n      this.checkPendingTracks();\n    };\n    this._onMediaSourceClose = () => {\n      logger.log('[buffer-controller]: Media source closed');\n    };\n    this._onMediaSourceEnded = () => {\n      logger.log('[buffer-controller]: Media source ended');\n    };\n    this._onMediaEmptied = () => {\n      const {\n        media,\n        _objectUrl\n      } = this;\n      if (media && media.src !== _objectUrl) {\n        logger.error(`Media element src was set while attaching MediaSource (${_objectUrl} > ${media.src})`);\n      }\n    };\n    this.hls = hls;\n    this._initSourceBuffer();\n    this.registerListeners();\n  }\n  hasSourceTypes() {\n    return this.getSourceBufferTypes().length > 0 || Object.keys(this.pendingTracks).length > 0;\n  }\n  destroy() {\n    this.unregisterListeners();\n    this.details = null;\n    this.lastMpegAudioChunk = null;\n  }\n  registerListeners() {\n    const {\n      hls\n    } = this;\n    hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);\n    hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);\n    hls.on(Events.BUFFER_RESET, this.onBufferReset, this);\n    hls.on(Events.BUFFER_APPENDING, this.onBufferAppending, this);\n    hls.on(Events.BUFFER_CODECS, this.onBufferCodecs, this);\n    hls.on(Events.BUFFER_EOS, this.onBufferEos, this);\n    hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);\n    hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this);\n    hls.on(Events.FRAG_PARSED, this.onFragParsed, this);\n    hls.on(Events.FRAG_CHANGED, this.onFragChanged, this);\n  }\n  unregisterListeners() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);\n    hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);\n    hls.off(Events.BUFFER_RESET, this.onBufferReset, this);\n    hls.off(Events.BUFFER_APPENDING, this.onBufferAppending, this);\n    hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this);\n    hls.off(Events.BUFFER_EOS, this.onBufferEos, this);\n    hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);\n    hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this);\n    hls.off(Events.FRAG_PARSED, this.onFragParsed, this);\n    hls.off(Events.FRAG_CHANGED, this.onFragChanged, this);\n  }\n  _initSourceBuffer() {\n    this.sourceBuffer = {};\n    this.operationQueue = new BufferOperationQueue(this.sourceBuffer);\n    this.listeners = {\n      audio: [],\n      video: [],\n      audiovideo: []\n    };\n    this.lastMpegAudioChunk = null;\n  }\n  onManifestLoading() {\n    this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = 0;\n    this.details = null;\n  }\n  onManifestParsed(event, data) {\n    // in case of alt audio 2 BUFFER_CODECS events will be triggered, one per stream controller\n    // sourcebuffers will be created all at once when the expected nb of tracks will be reached\n    // in case alt audio is not used, only one BUFFER_CODEC event will be fired from main stream controller\n    // it will contain the expected nb of source buffers, no need to compute it\n    let codecEvents = 2;\n    if (data.audio && !data.video || !data.altAudio || !true) {\n      codecEvents = 1;\n    }\n    this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = codecEvents;\n    logger.log(`${this.bufferCodecEventsExpected} bufferCodec event(s) expected`);\n  }\n  onMediaAttaching(event, data) {\n    const media = this.media = data.media;\n    if (media && MediaSource) {\n      const ms = this.mediaSource = new MediaSource();\n      // MediaSource listeners are arrow functions with a lexical scope, and do not need to be bound\n      ms.addEventListener('sourceopen', this._onMediaSourceOpen);\n      ms.addEventListener('sourceended', this._onMediaSourceEnded);\n      ms.addEventListener('sourceclose', this._onMediaSourceClose);\n      // link video and media Source\n      media.src = self.URL.createObjectURL(ms);\n      // cache the locally generated object url\n      this._objectUrl = media.src;\n      media.addEventListener('emptied', this._onMediaEmptied);\n    }\n  }\n  onMediaDetaching() {\n    const {\n      media,\n      mediaSource,\n      _objectUrl\n    } = this;\n    if (mediaSource) {\n      logger.log('[buffer-controller]: media source detaching');\n      if (mediaSource.readyState === 'open') {\n        try {\n          // endOfStream could trigger exception if any sourcebuffer is in updating state\n          // we don't really care about checking sourcebuffer state here,\n          // as we are anyway detaching the MediaSource\n          // let's just avoid this exception to propagate\n          mediaSource.endOfStream();\n        } catch (err) {\n          logger.warn(`[buffer-controller]: onMediaDetaching: ${err.message} while calling endOfStream`);\n        }\n      }\n      // Clean up the SourceBuffers by invoking onBufferReset\n      this.onBufferReset();\n      mediaSource.removeEventListener('sourceopen', this._onMediaSourceOpen);\n      mediaSource.removeEventListener('sourceended', this._onMediaSourceEnded);\n      mediaSource.removeEventListener('sourceclose', this._onMediaSourceClose);\n\n      // Detach properly the MediaSource from the HTMLMediaElement as\n      // suggested in https://github.com/w3c/media-source/issues/53.\n      if (media) {\n        media.removeEventListener('emptied', this._onMediaEmptied);\n        if (_objectUrl) {\n          self.URL.revokeObjectURL(_objectUrl);\n        }\n\n        // clean up video tag src only if it's our own url. some external libraries might\n        // hijack the video tag and change its 'src' without destroying the Hls instance first\n        if (media.src === _objectUrl) {\n          media.removeAttribute('src');\n          media.load();\n        } else {\n          logger.warn('[buffer-controller]: media.src was changed by a third party - skip cleanup');\n        }\n      }\n      this.mediaSource = null;\n      this.media = null;\n      this._objectUrl = null;\n      this.bufferCodecEventsExpected = this._bufferCodecEventsTotal;\n      this.pendingTracks = {};\n      this.tracks = {};\n    }\n    this.hls.trigger(Events.MEDIA_DETACHED, undefined);\n  }\n  onBufferReset() {\n    this.getSourceBufferTypes().forEach(type => {\n      const sb = this.sourceBuffer[type];\n      try {\n        if (sb) {\n          this.removeBufferListeners(type);\n          if (this.mediaSource) {\n            this.mediaSource.removeSourceBuffer(sb);\n          }\n          // Synchronously remove the SB from the map before the next call in order to prevent an async function from\n          // accessing it\n          this.sourceBuffer[type] = undefined;\n        }\n      } catch (err) {\n        logger.warn(`[buffer-controller]: Failed to reset the ${type} buffer`, err);\n      }\n    });\n    this._initSourceBuffer();\n  }\n  onBufferCodecs(event, data) {\n    const sourceBufferCount = this.getSourceBufferTypes().length;\n    Object.keys(data).forEach(trackName => {\n      if (sourceBufferCount) {\n        // check if SourceBuffer codec needs to change\n        const track = this.tracks[trackName];\n        if (track && typeof track.buffer.changeType === 'function') {\n          const {\n            id,\n            codec,\n            levelCodec,\n            container,\n            metadata\n          } = data[trackName];\n          const currentCodec = (track.levelCodec || track.codec).replace(VIDEO_CODEC_PROFILE_REPACE, '$1');\n          const nextCodec = (levelCodec || codec).replace(VIDEO_CODEC_PROFILE_REPACE, '$1');\n          if (currentCodec !== nextCodec) {\n            const mimeType = `${container};codecs=${levelCodec || codec}`;\n            this.appendChangeType(trackName, mimeType);\n            logger.log(`[buffer-controller]: switching codec ${currentCodec} to ${nextCodec}`);\n            this.tracks[trackName] = {\n              buffer: track.buffer,\n              codec,\n              container,\n              levelCodec,\n              metadata,\n              id\n            };\n          }\n        }\n      } else {\n        // if source buffer(s) not created yet, appended buffer tracks in this.pendingTracks\n        this.pendingTracks[trackName] = data[trackName];\n      }\n    });\n\n    // if sourcebuffers already created, do nothing ...\n    if (sourceBufferCount) {\n      return;\n    }\n    this.bufferCodecEventsExpected = Math.max(this.bufferCodecEventsExpected - 1, 0);\n    if (this.mediaSource && this.mediaSource.readyState === 'open') {\n      this.checkPendingTracks();\n    }\n  }\n  appendChangeType(type, mimeType) {\n    const {\n      operationQueue\n    } = this;\n    const operation = {\n      execute: () => {\n        const sb = this.sourceBuffer[type];\n        if (sb) {\n          logger.log(`[buffer-controller]: changing ${type} sourceBuffer type to ${mimeType}`);\n          sb.changeType(mimeType);\n        }\n        operationQueue.shiftAndExecuteNext(type);\n      },\n      onStart: () => {},\n      onComplete: () => {},\n      onError: e => {\n        logger.warn(`[buffer-controller]: Failed to change ${type} SourceBuffer type`, e);\n      }\n    };\n    operationQueue.append(operation, type);\n  }\n  onBufferAppending(event, eventData) {\n    const {\n      hls,\n      operationQueue,\n      tracks\n    } = this;\n    const {\n      data,\n      type,\n      frag,\n      part,\n      chunkMeta\n    } = eventData;\n    const chunkStats = chunkMeta.buffering[type];\n    const bufferAppendingStart = self.performance.now();\n    chunkStats.start = bufferAppendingStart;\n    const fragBuffering = frag.stats.buffering;\n    const partBuffering = part ? part.stats.buffering : null;\n    if (fragBuffering.start === 0) {\n      fragBuffering.start = bufferAppendingStart;\n    }\n    if (partBuffering && partBuffering.start === 0) {\n      partBuffering.start = bufferAppendingStart;\n    }\n\n    // TODO: Only update timestampOffset when audio/mpeg fragment or part is not contiguous with previously appended\n    // Adjusting `SourceBuffer.timestampOffset` (desired point in the timeline where the next frames should be appended)\n    // in Chrome browser when we detect MPEG audio container and time delta between level PTS and `SourceBuffer.timestampOffset`\n    // is greater than 100ms (this is enough to handle seek for VOD or level change for LIVE videos).\n    // More info here: https://github.com/video-dev/hls.js/issues/332#issuecomment-257986486\n    const audioTrack = tracks.audio;\n    let checkTimestampOffset = false;\n    if (type === 'audio' && (audioTrack == null ? void 0 : audioTrack.container) === 'audio/mpeg') {\n      checkTimestampOffset = !this.lastMpegAudioChunk || chunkMeta.id === 1 || this.lastMpegAudioChunk.sn !== chunkMeta.sn;\n      this.lastMpegAudioChunk = chunkMeta;\n    }\n    const fragStart = frag.start;\n    const operation = {\n      execute: () => {\n        chunkStats.executeStart = self.performance.now();\n        if (checkTimestampOffset) {\n          const sb = this.sourceBuffer[type];\n          if (sb) {\n            const delta = fragStart - sb.timestampOffset;\n            if (Math.abs(delta) >= 0.1) {\n              logger.log(`[buffer-controller]: Updating audio SourceBuffer timestampOffset to ${fragStart} (delta: ${delta}) sn: ${frag.sn})`);\n              sb.timestampOffset = fragStart;\n            }\n          }\n        }\n        this.appendExecutor(data, type);\n      },\n      onStart: () => {\n        // logger.debug(`[buffer-controller]: ${type} SourceBuffer updatestart`);\n      },\n      onComplete: () => {\n        // logger.debug(`[buffer-controller]: ${type} SourceBuffer updateend`);\n        const end = self.performance.now();\n        chunkStats.executeEnd = chunkStats.end = end;\n        if (fragBuffering.first === 0) {\n          fragBuffering.first = end;\n        }\n        if (partBuffering && partBuffering.first === 0) {\n          partBuffering.first = end;\n        }\n        const {\n          sourceBuffer\n        } = this;\n        const timeRanges = {};\n        for (const type in sourceBuffer) {\n          timeRanges[type] = BufferHelper.getBuffered(sourceBuffer[type]);\n        }\n        this.appendError = 0;\n        this.hls.trigger(Events.BUFFER_APPENDED, {\n          type,\n          frag,\n          part,\n          chunkMeta,\n          parent: frag.type,\n          timeRanges\n        });\n      },\n      onError: err => {\n        // in case any error occured while appending, put back segment in segments table\n        logger.error(`[buffer-controller]: Error encountered while trying to append to the ${type} SourceBuffer`, err);\n        const event = {\n          type: ErrorTypes.MEDIA_ERROR,\n          parent: frag.type,\n          details: ErrorDetails.BUFFER_APPEND_ERROR,\n          frag,\n          part,\n          chunkMeta,\n          error: err,\n          err,\n          fatal: false\n        };\n        if (err.code === DOMException.QUOTA_EXCEEDED_ERR) {\n          // QuotaExceededError: http://www.w3.org/TR/html5/infrastructure.html#quotaexceedederror\n          // let's stop appending any segments, and report BUFFER_FULL_ERROR error\n          event.details = ErrorDetails.BUFFER_FULL_ERROR;\n        } else {\n          this.appendError++;\n          event.details = ErrorDetails.BUFFER_APPEND_ERROR;\n          /* with UHD content, we could get loop of quota exceeded error until\n            browser is able to evict some data from sourcebuffer. Retrying can help recover.\n          */\n          if (this.appendError > hls.config.appendErrorMaxRetry) {\n            logger.error(`[buffer-controller]: Failed ${hls.config.appendErrorMaxRetry} times to append segment in sourceBuffer`);\n            event.fatal = true;\n          }\n        }\n        hls.trigger(Events.ERROR, event);\n      }\n    };\n    operationQueue.append(operation, type);\n  }\n  onBufferFlushing(event, data) {\n    const {\n      operationQueue\n    } = this;\n    const flushOperation = type => ({\n      execute: this.removeExecutor.bind(this, type, data.startOffset, data.endOffset),\n      onStart: () => {\n        // logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);\n      },\n      onComplete: () => {\n        // logger.debug(`[buffer-controller]: Finished flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);\n        this.hls.trigger(Events.BUFFER_FLUSHED, {\n          type\n        });\n      },\n      onError: e => {\n        logger.warn(`[buffer-controller]: Failed to remove from ${type} SourceBuffer`, e);\n      }\n    });\n    if (data.type) {\n      operationQueue.append(flushOperation(data.type), data.type);\n    } else {\n      this.getSourceBufferTypes().forEach(type => {\n        operationQueue.append(flushOperation(type), type);\n      });\n    }\n  }\n  onFragParsed(event, data) {\n    const {\n      frag,\n      part\n    } = data;\n    const buffersAppendedTo = [];\n    const elementaryStreams = part ? part.elementaryStreams : frag.elementaryStreams;\n    if (elementaryStreams[ElementaryStreamTypes.AUDIOVIDEO]) {\n      buffersAppendedTo.push('audiovideo');\n    } else {\n      if (elementaryStreams[ElementaryStreamTypes.AUDIO]) {\n        buffersAppendedTo.push('audio');\n      }\n      if (elementaryStreams[ElementaryStreamTypes.VIDEO]) {\n        buffersAppendedTo.push('video');\n      }\n    }\n    const onUnblocked = () => {\n      const now = self.performance.now();\n      frag.stats.buffering.end = now;\n      if (part) {\n        part.stats.buffering.end = now;\n      }\n      const stats = part ? part.stats : frag.stats;\n      this.hls.trigger(Events.FRAG_BUFFERED, {\n        frag,\n        part,\n        stats,\n        id: frag.type\n      });\n    };\n    if (buffersAppendedTo.length === 0) {\n      logger.warn(`Fragments must have at least one ElementaryStreamType set. type: ${frag.type} level: ${frag.level} sn: ${frag.sn}`);\n    }\n    this.blockBuffers(onUnblocked, buffersAppendedTo);\n  }\n  onFragChanged(event, data) {\n    this.flushBackBuffer();\n  }\n\n  // on BUFFER_EOS mark matching sourcebuffer(s) as ended and trigger checkEos()\n  // an undefined data.type will mark all buffers as EOS.\n  onBufferEos(event, data) {\n    const ended = this.getSourceBufferTypes().reduce((acc, type) => {\n      const sb = this.sourceBuffer[type];\n      if (sb && (!data.type || data.type === type)) {\n        sb.ending = true;\n        if (!sb.ended) {\n          sb.ended = true;\n          logger.log(`[buffer-controller]: ${type} sourceBuffer now EOS`);\n        }\n      }\n      return acc && !!(!sb || sb.ended);\n    }, true);\n    if (ended) {\n      logger.log(`[buffer-controller]: Queueing mediaSource.endOfStream()`);\n      this.blockBuffers(() => {\n        this.getSourceBufferTypes().forEach(type => {\n          const sb = this.sourceBuffer[type];\n          if (sb) {\n            sb.ending = false;\n          }\n        });\n        const {\n          mediaSource\n        } = this;\n        if (!mediaSource || mediaSource.readyState !== 'open') {\n          if (mediaSource) {\n            logger.info(`[buffer-controller]: Could not call mediaSource.endOfStream(). mediaSource.readyState: ${mediaSource.readyState}`);\n          }\n          return;\n        }\n        logger.log(`[buffer-controller]: Calling mediaSource.endOfStream()`);\n        // Allow this to throw and be caught by the enqueueing function\n        mediaSource.endOfStream();\n      });\n    }\n  }\n  onLevelUpdated(event, {\n    details\n  }) {\n    if (!details.fragments.length) {\n      return;\n    }\n    this.details = details;\n    if (this.getSourceBufferTypes().length) {\n      this.blockBuffers(this.updateMediaElementDuration.bind(this));\n    } else {\n      this.updateMediaElementDuration();\n    }\n  }\n  flushBackBuffer() {\n    const {\n      hls,\n      details,\n      media,\n      sourceBuffer\n    } = this;\n    if (!media || details === null) {\n      return;\n    }\n    const sourceBufferTypes = this.getSourceBufferTypes();\n    if (!sourceBufferTypes.length) {\n      return;\n    }\n\n    // Support for deprecated liveBackBufferLength\n    const backBufferLength = details.live && hls.config.liveBackBufferLength !== null ? hls.config.liveBackBufferLength : hls.config.backBufferLength;\n    if (!isFiniteNumber(backBufferLength) || backBufferLength < 0) {\n      return;\n    }\n    const currentTime = media.currentTime;\n    const targetDuration = details.levelTargetDuration;\n    const maxBackBufferLength = Math.max(backBufferLength, targetDuration);\n    const targetBackBufferPosition = Math.floor(currentTime / targetDuration) * targetDuration - maxBackBufferLength;\n    sourceBufferTypes.forEach(type => {\n      const sb = sourceBuffer[type];\n      if (sb) {\n        const buffered = BufferHelper.getBuffered(sb);\n        // when target buffer start exceeds actual buffer start\n        if (buffered.length > 0 && targetBackBufferPosition > buffered.start(0)) {\n          hls.trigger(Events.BACK_BUFFER_REACHED, {\n            bufferEnd: targetBackBufferPosition\n          });\n\n          // Support for deprecated event:\n          if (details.live) {\n            hls.trigger(Events.LIVE_BACK_BUFFER_REACHED, {\n              bufferEnd: targetBackBufferPosition\n            });\n          } else if (sb.ended && buffered.end(buffered.length - 1) - currentTime < targetDuration * 2) {\n            logger.info(`[buffer-controller]: Cannot flush ${type} back buffer while SourceBuffer is in ended state`);\n            return;\n          }\n          hls.trigger(Events.BUFFER_FLUSHING, {\n            startOffset: 0,\n            endOffset: targetBackBufferPosition,\n            type\n          });\n        }\n      }\n    });\n  }\n\n  /**\n   * Update Media Source duration to current level duration or override to Infinity if configuration parameter\n   * 'liveDurationInfinity` is set to `true`\n   * More details: https://github.com/video-dev/hls.js/issues/355\n   */\n  updateMediaElementDuration() {\n    if (!this.details || !this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') {\n      return;\n    }\n    const {\n      details,\n      hls,\n      media,\n      mediaSource\n    } = this;\n    const levelDuration = details.fragments[0].start + details.totalduration;\n    const mediaDuration = media.duration;\n    const msDuration = isFiniteNumber(mediaSource.duration) ? mediaSource.duration : 0;\n    if (details.live && hls.config.liveDurationInfinity) {\n      // Override duration to Infinity\n      logger.log('[buffer-controller]: Media Source duration is set to Infinity');\n      mediaSource.duration = Infinity;\n      this.updateSeekableRange(details);\n    } else if (levelDuration > msDuration && levelDuration > mediaDuration || !isFiniteNumber(mediaDuration)) {\n      // levelDuration was the last value we set.\n      // not using mediaSource.duration as the browser may tweak this value\n      // only update Media Source duration if its value increase, this is to avoid\n      // flushing already buffered portion when switching between quality level\n      logger.log(`[buffer-controller]: Updating Media Source duration to ${levelDuration.toFixed(3)}`);\n      mediaSource.duration = levelDuration;\n    }\n  }\n  updateSeekableRange(levelDetails) {\n    const mediaSource = this.mediaSource;\n    const fragments = levelDetails.fragments;\n    const len = fragments.length;\n    if (len && levelDetails.live && mediaSource != null && mediaSource.setLiveSeekableRange) {\n      const start = Math.max(0, fragments[0].start);\n      const end = Math.max(start, start + levelDetails.totalduration);\n      mediaSource.setLiveSeekableRange(start, end);\n    }\n  }\n  checkPendingTracks() {\n    const {\n      bufferCodecEventsExpected,\n      operationQueue,\n      pendingTracks\n    } = this;\n\n    // Check if we've received all of the expected bufferCodec events. When none remain, create all the sourceBuffers at once.\n    // This is important because the MSE spec allows implementations to throw QuotaExceededErrors if creating new sourceBuffers after\n    // data has been appended to existing ones.\n    // 2 tracks is the max (one for audio, one for video). If we've reach this max go ahead and create the buffers.\n    const pendingTracksCount = Object.keys(pendingTracks).length;\n    if (pendingTracksCount && !bufferCodecEventsExpected || pendingTracksCount === 2) {\n      // ok, let's create them now !\n      this.createSourceBuffers(pendingTracks);\n      this.pendingTracks = {};\n      // append any pending segments now !\n      const buffers = this.getSourceBufferTypes();\n      if (buffers.length) {\n        this.hls.trigger(Events.BUFFER_CREATED, {\n          tracks: this.tracks\n        });\n        buffers.forEach(type => {\n          operationQueue.executeNext(type);\n        });\n      } else {\n        const error = new Error('could not create source buffer for media codec(s)');\n        this.hls.trigger(Events.ERROR, {\n          type: ErrorTypes.MEDIA_ERROR,\n          details: ErrorDetails.BUFFER_INCOMPATIBLE_CODECS_ERROR,\n          fatal: true,\n          error,\n          reason: error.message\n        });\n      }\n    }\n  }\n  createSourceBuffers(tracks) {\n    const {\n      sourceBuffer,\n      mediaSource\n    } = this;\n    if (!mediaSource) {\n      throw Error('createSourceBuffers called when mediaSource was null');\n    }\n    for (const trackName in tracks) {\n      if (!sourceBuffer[trackName]) {\n        const track = tracks[trackName];\n        if (!track) {\n          throw Error(`source buffer exists for track ${trackName}, however track does not`);\n        }\n        // use levelCodec as first priority\n        const codec = track.levelCodec || track.codec;\n        const mimeType = `${track.container};codecs=${codec}`;\n        logger.log(`[buffer-controller]: creating sourceBuffer(${mimeType})`);\n        try {\n          const sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType);\n          const sbName = trackName;\n          this.addBufferListener(sbName, 'updatestart', this._onSBUpdateStart);\n          this.addBufferListener(sbName, 'updateend', this._onSBUpdateEnd);\n          this.addBufferListener(sbName, 'error', this._onSBUpdateError);\n          this.tracks[trackName] = {\n            buffer: sb,\n            codec: codec,\n            container: track.container,\n            levelCodec: track.levelCodec,\n            metadata: track.metadata,\n            id: track.id\n          };\n        } catch (err) {\n          logger.error(`[buffer-controller]: error while trying to add sourceBuffer: ${err.message}`);\n          this.hls.trigger(Events.ERROR, {\n            type: ErrorTypes.MEDIA_ERROR,\n            details: ErrorDetails.BUFFER_ADD_CODEC_ERROR,\n            fatal: false,\n            error: err,\n            mimeType: mimeType\n          });\n        }\n      }\n    }\n  }\n  _onSBUpdateStart(type) {\n    const {\n      operationQueue\n    } = this;\n    const operation = operationQueue.current(type);\n    operation.onStart();\n  }\n  _onSBUpdateEnd(type) {\n    const {\n      operationQueue\n    } = this;\n    const operation = operationQueue.current(type);\n    operation.onComplete();\n    operationQueue.shiftAndExecuteNext(type);\n  }\n  _onSBUpdateError(type, event) {\n    const error = new Error(`${type} SourceBuffer error`);\n    logger.error(`[buffer-controller]: ${error}`, event);\n    // according to http://www.w3.org/TR/media-source/#sourcebuffer-append-error\n    // SourceBuffer errors are not necessarily fatal; if so, the HTMLMediaElement will fire an error event\n    this.hls.trigger(Events.ERROR, {\n      type: ErrorTypes.MEDIA_ERROR,\n      details: ErrorDetails.BUFFER_APPENDING_ERROR,\n      error,\n      fatal: false\n    });\n    // updateend is always fired after error, so we'll allow that to shift the current operation off of the queue\n    const operation = this.operationQueue.current(type);\n    if (operation) {\n      operation.onError(event);\n    }\n  }\n\n  // This method must result in an updateend event; if remove is not called, _onSBUpdateEnd must be called manually\n  removeExecutor(type, startOffset, endOffset) {\n    const {\n      media,\n      mediaSource,\n      operationQueue,\n      sourceBuffer\n    } = this;\n    const sb = sourceBuffer[type];\n    if (!media || !mediaSource || !sb) {\n      logger.warn(`[buffer-controller]: Attempting to remove from the ${type} SourceBuffer, but it does not exist`);\n      operationQueue.shiftAndExecuteNext(type);\n      return;\n    }\n    const mediaDuration = isFiniteNumber(media.duration) ? media.duration : Infinity;\n    const msDuration = isFiniteNumber(mediaSource.duration) ? mediaSource.duration : Infinity;\n    const removeStart = Math.max(0, startOffset);\n    const removeEnd = Math.min(endOffset, mediaDuration, msDuration);\n    if (removeEnd > removeStart && !sb.ending) {\n      sb.ended = false;\n      logger.log(`[buffer-controller]: Removing [${removeStart},${removeEnd}] from the ${type} SourceBuffer`);\n      sb.remove(removeStart, removeEnd);\n    } else {\n      // Cycle the queue\n      operationQueue.shiftAndExecuteNext(type);\n    }\n  }\n\n  // This method must result in an updateend event; if append is not called, _onSBUpdateEnd must be called manually\n  appendExecutor(data, type) {\n    const {\n      operationQueue,\n      sourceBuffer\n    } = this;\n    const sb = sourceBuffer[type];\n    if (!sb) {\n      logger.warn(`[buffer-controller]: Attempting to append to the ${type} SourceBuffer, but it does not exist`);\n      operationQueue.shiftAndExecuteNext(type);\n      return;\n    }\n    sb.ended = false;\n    sb.appendBuffer(data);\n  }\n\n  // Enqueues an operation to each SourceBuffer queue which, upon execution, resolves a promise. When all promises\n  // resolve, the onUnblocked function is executed. Functions calling this method do not need to unblock the queue\n  // upon completion, since we already do it here\n  blockBuffers(onUnblocked, buffers = this.getSourceBufferTypes()) {\n    if (!buffers.length) {\n      logger.log('[buffer-controller]: Blocking operation requested, but no SourceBuffers exist');\n      Promise.resolve().then(onUnblocked);\n      return;\n    }\n    const {\n      operationQueue\n    } = this;\n\n    // logger.debug(`[buffer-controller]: Blocking ${buffers} SourceBuffer`);\n    const blockingOperations = buffers.map(type => operationQueue.appendBlocker(type));\n    Promise.all(blockingOperations).then(() => {\n      // logger.debug(`[buffer-controller]: Blocking operation resolved; unblocking ${buffers} SourceBuffer`);\n      onUnblocked();\n      buffers.forEach(type => {\n        const sb = this.sourceBuffer[type];\n        // Only cycle the queue if the SB is not updating. There's a bug in Chrome which sets the SB updating flag to\n        // true when changing the MediaSource duration (https://bugs.chromium.org/p/chromium/issues/detail?id=959359&can=2&q=mediasource%20duration)\n        // While this is a workaround, it's probably useful to have around\n        if (!(sb != null && sb.updating)) {\n          operationQueue.shiftAndExecuteNext(type);\n        }\n      });\n    });\n  }\n  getSourceBufferTypes() {\n    return Object.keys(this.sourceBuffer);\n  }\n  addBufferListener(type, event, fn) {\n    const buffer = this.sourceBuffer[type];\n    if (!buffer) {\n      return;\n    }\n    const listener = fn.bind(this, type);\n    this.listeners[type].push({\n      event,\n      listener\n    });\n    buffer.addEventListener(event, listener);\n  }\n  removeBufferListeners(type) {\n    const buffer = this.sourceBuffer[type];\n    if (!buffer) {\n      return;\n    }\n    this.listeners[type].forEach(l => {\n      buffer.removeEventListener(l.event, l.listener);\n    });\n  }\n}\n\n/**\n *\n * This code was ported from the dash.js project at:\n *   https://github.com/Dash-Industry-Forum/dash.js/blob/development/externals/cea608-parser.js\n *   https://github.com/Dash-Industry-Forum/dash.js/commit/8269b26a761e0853bb21d78780ed945144ecdd4d#diff-71bc295a2d6b6b7093a1d3290d53a4b2\n *\n * The original copyright appears below:\n *\n * The copyright in this software is being made available under the BSD License,\n * included below. This software may be subject to other third party and contributor\n * rights, including patent rights, and no such rights are granted under this license.\n *\n * Copyright (c) 2015-2016, DASH Industry Forum.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *  1. Redistributions of source code must retain the above copyright notice, this\n *  list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright notice,\n *  this list of conditions and the following disclaimer in the documentation and/or\n *  other materials provided with the distribution.\n *  2. Neither the name of Dash Industry Forum nor the names of its\n *  contributors may be used to endorse or promote products derived from this software\n *  without specific prior written permission.\n *\n *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY\n *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n *  POSSIBILITY OF SUCH DAMAGE.\n */\n/**\n *  Exceptions from regular ASCII. CodePoints are mapped to UTF-16 codes\n */\n\nconst specialCea608CharsCodes = {\n  0x2a: 0xe1,\n  // lowercase a, acute accent\n  0x5c: 0xe9,\n  // lowercase e, acute accent\n  0x5e: 0xed,\n  // lowercase i, acute accent\n  0x5f: 0xf3,\n  // lowercase o, acute accent\n  0x60: 0xfa,\n  // lowercase u, acute accent\n  0x7b: 0xe7,\n  // lowercase c with cedilla\n  0x7c: 0xf7,\n  // division symbol\n  0x7d: 0xd1,\n  // uppercase N tilde\n  0x7e: 0xf1,\n  // lowercase n tilde\n  0x7f: 0x2588,\n  // Full block\n  // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS\n  // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F\n  // THIS MEANS THAT \\x50 MUST BE ADDED TO THE VALUES\n  0x80: 0xae,\n  // Registered symbol (R)\n  0x81: 0xb0,\n  // degree sign\n  0x82: 0xbd,\n  // 1/2 symbol\n  0x83: 0xbf,\n  // Inverted (open) question mark\n  0x84: 0x2122,\n  // Trademark symbol (TM)\n  0x85: 0xa2,\n  // Cents symbol\n  0x86: 0xa3,\n  // Pounds sterling\n  0x87: 0x266a,\n  // Music 8'th note\n  0x88: 0xe0,\n  // lowercase a, grave accent\n  0x89: 0x20,\n  // transparent space (regular)\n  0x8a: 0xe8,\n  // lowercase e, grave accent\n  0x8b: 0xe2,\n  // lowercase a, circumflex accent\n  0x8c: 0xea,\n  // lowercase e, circumflex accent\n  0x8d: 0xee,\n  // lowercase i, circumflex accent\n  0x8e: 0xf4,\n  // lowercase o, circumflex accent\n  0x8f: 0xfb,\n  // lowercase u, circumflex accent\n  // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS\n  // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F\n  0x90: 0xc1,\n  // capital letter A with acute\n  0x91: 0xc9,\n  // capital letter E with acute\n  0x92: 0xd3,\n  // capital letter O with acute\n  0x93: 0xda,\n  // capital letter U with acute\n  0x94: 0xdc,\n  // capital letter U with diaresis\n  0x95: 0xfc,\n  // lowercase letter U with diaeresis\n  0x96: 0x2018,\n  // opening single quote\n  0x97: 0xa1,\n  // inverted exclamation mark\n  0x98: 0x2a,\n  // asterisk\n  0x99: 0x2019,\n  // closing single quote\n  0x9a: 0x2501,\n  // box drawings heavy horizontal\n  0x9b: 0xa9,\n  // copyright sign\n  0x9c: 0x2120,\n  // Service mark\n  0x9d: 0x2022,\n  // (round) bullet\n  0x9e: 0x201c,\n  // Left double quotation mark\n  0x9f: 0x201d,\n  // Right double quotation mark\n  0xa0: 0xc0,\n  // uppercase A, grave accent\n  0xa1: 0xc2,\n  // uppercase A, circumflex\n  0xa2: 0xc7,\n  // uppercase C with cedilla\n  0xa3: 0xc8,\n  // uppercase E, grave accent\n  0xa4: 0xca,\n  // uppercase E, circumflex\n  0xa5: 0xcb,\n  // capital letter E with diaresis\n  0xa6: 0xeb,\n  // lowercase letter e with diaresis\n  0xa7: 0xce,\n  // uppercase I, circumflex\n  0xa8: 0xcf,\n  // uppercase I, with diaresis\n  0xa9: 0xef,\n  // lowercase i, with diaresis\n  0xaa: 0xd4,\n  // uppercase O, circumflex\n  0xab: 0xd9,\n  // uppercase U, grave accent\n  0xac: 0xf9,\n  // lowercase u, grave accent\n  0xad: 0xdb,\n  // uppercase U, circumflex\n  0xae: 0xab,\n  // left-pointing double angle quotation mark\n  0xaf: 0xbb,\n  // right-pointing double angle quotation mark\n  // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS\n  // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F\n  0xb0: 0xc3,\n  // Uppercase A, tilde\n  0xb1: 0xe3,\n  // Lowercase a, tilde\n  0xb2: 0xcd,\n  // Uppercase I, acute accent\n  0xb3: 0xcc,\n  // Uppercase I, grave accent\n  0xb4: 0xec,\n  // Lowercase i, grave accent\n  0xb5: 0xd2,\n  // Uppercase O, grave accent\n  0xb6: 0xf2,\n  // Lowercase o, grave accent\n  0xb7: 0xd5,\n  // Uppercase O, tilde\n  0xb8: 0xf5,\n  // Lowercase o, tilde\n  0xb9: 0x7b,\n  // Open curly brace\n  0xba: 0x7d,\n  // Closing curly brace\n  0xbb: 0x5c,\n  // Backslash\n  0xbc: 0x5e,\n  // Caret\n  0xbd: 0x5f,\n  // Underscore\n  0xbe: 0x7c,\n  // Pipe (vertical line)\n  0xbf: 0x223c,\n  // Tilde operator\n  0xc0: 0xc4,\n  // Uppercase A, umlaut\n  0xc1: 0xe4,\n  // Lowercase A, umlaut\n  0xc2: 0xd6,\n  // Uppercase O, umlaut\n  0xc3: 0xf6,\n  // Lowercase o, umlaut\n  0xc4: 0xdf,\n  // Esszett (sharp S)\n  0xc5: 0xa5,\n  // Yen symbol\n  0xc6: 0xa4,\n  // Generic currency sign\n  0xc7: 0x2503,\n  // Box drawings heavy vertical\n  0xc8: 0xc5,\n  // Uppercase A, ring\n  0xc9: 0xe5,\n  // Lowercase A, ring\n  0xca: 0xd8,\n  // Uppercase O, stroke\n  0xcb: 0xf8,\n  // Lowercase o, strok\n  0xcc: 0x250f,\n  // Box drawings heavy down and right\n  0xcd: 0x2513,\n  // Box drawings heavy down and left\n  0xce: 0x2517,\n  // Box drawings heavy up and right\n  0xcf: 0x251b // Box drawings heavy up and left\n};\n\n/**\n * Utils\n */\nconst getCharForByte = function getCharForByte(byte) {\n  let charCode = byte;\n  if (specialCea608CharsCodes.hasOwnProperty(byte)) {\n    charCode = specialCea608CharsCodes[byte];\n  }\n  return String.fromCharCode(charCode);\n};\nconst NR_ROWS = 15;\nconst NR_COLS = 100;\n// Tables to look up row from PAC data\nconst rowsLowCh1 = {\n  0x11: 1,\n  0x12: 3,\n  0x15: 5,\n  0x16: 7,\n  0x17: 9,\n  0x10: 11,\n  0x13: 12,\n  0x14: 14\n};\nconst rowsHighCh1 = {\n  0x11: 2,\n  0x12: 4,\n  0x15: 6,\n  0x16: 8,\n  0x17: 10,\n  0x13: 13,\n  0x14: 15\n};\nconst rowsLowCh2 = {\n  0x19: 1,\n  0x1a: 3,\n  0x1d: 5,\n  0x1e: 7,\n  0x1f: 9,\n  0x18: 11,\n  0x1b: 12,\n  0x1c: 14\n};\nconst rowsHighCh2 = {\n  0x19: 2,\n  0x1a: 4,\n  0x1d: 6,\n  0x1e: 8,\n  0x1f: 10,\n  0x1b: 13,\n  0x1c: 15\n};\nconst backgroundColors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'black', 'transparent'];\nclass CaptionsLogger {\n  constructor() {\n    this.time = null;\n    this.verboseLevel = 0;\n  }\n  log(severity, msg) {\n    if (this.verboseLevel >= severity) {\n      const m = typeof msg === 'function' ? msg() : msg;\n      logger.log(`${this.time} [${severity}] ${m}`);\n    }\n  }\n}\nconst numArrayToHexArray = function numArrayToHexArray(numArray) {\n  const hexArray = [];\n  for (let j = 0; j < numArray.length; j++) {\n    hexArray.push(numArray[j].toString(16));\n  }\n  return hexArray;\n};\nclass PenState {\n  constructor(foreground, underline, italics, background, flash) {\n    this.foreground = void 0;\n    this.underline = void 0;\n    this.italics = void 0;\n    this.background = void 0;\n    this.flash = void 0;\n    this.foreground = foreground || 'white';\n    this.underline = underline || false;\n    this.italics = italics || false;\n    this.background = background || 'black';\n    this.flash = flash || false;\n  }\n  reset() {\n    this.foreground = 'white';\n    this.underline = false;\n    this.italics = false;\n    this.background = 'black';\n    this.flash = false;\n  }\n  setStyles(styles) {\n    const attribs = ['foreground', 'underline', 'italics', 'background', 'flash'];\n    for (let i = 0; i < attribs.length; i++) {\n      const style = attribs[i];\n      if (styles.hasOwnProperty(style)) {\n        this[style] = styles[style];\n      }\n    }\n  }\n  isDefault() {\n    return this.foreground === 'white' && !this.underline && !this.italics && this.background === 'black' && !this.flash;\n  }\n  equals(other) {\n    return this.foreground === other.foreground && this.underline === other.underline && this.italics === other.italics && this.background === other.background && this.flash === other.flash;\n  }\n  copy(newPenState) {\n    this.foreground = newPenState.foreground;\n    this.underline = newPenState.underline;\n    this.italics = newPenState.italics;\n    this.background = newPenState.background;\n    this.flash = newPenState.flash;\n  }\n  toString() {\n    return 'color=' + this.foreground + ', underline=' + this.underline + ', italics=' + this.italics + ', background=' + this.background + ', flash=' + this.flash;\n  }\n}\n\n/**\n * Unicode character with styling and background.\n * @constructor\n */\nclass StyledUnicodeChar {\n  constructor(uchar, foreground, underline, italics, background, flash) {\n    this.uchar = void 0;\n    this.penState = void 0;\n    this.uchar = uchar || ' '; // unicode character\n    this.penState = new PenState(foreground, underline, italics, background, flash);\n  }\n  reset() {\n    this.uchar = ' ';\n    this.penState.reset();\n  }\n  setChar(uchar, newPenState) {\n    this.uchar = uchar;\n    this.penState.copy(newPenState);\n  }\n  setPenState(newPenState) {\n    this.penState.copy(newPenState);\n  }\n  equals(other) {\n    return this.uchar === other.uchar && this.penState.equals(other.penState);\n  }\n  copy(newChar) {\n    this.uchar = newChar.uchar;\n    this.penState.copy(newChar.penState);\n  }\n  isEmpty() {\n    return this.uchar === ' ' && this.penState.isDefault();\n  }\n}\n\n/**\n * CEA-608 row consisting of NR_COLS instances of StyledUnicodeChar.\n * @constructor\n */\nclass Row {\n  constructor(logger) {\n    this.chars = void 0;\n    this.pos = void 0;\n    this.currPenState = void 0;\n    this.cueStartTime = void 0;\n    this.logger = void 0;\n    this.chars = [];\n    for (let i = 0; i < NR_COLS; i++) {\n      this.chars.push(new StyledUnicodeChar());\n    }\n    this.logger = logger;\n    this.pos = 0;\n    this.currPenState = new PenState();\n  }\n  equals(other) {\n    let equal = true;\n    for (let i = 0; i < NR_COLS; i++) {\n      if (!this.chars[i].equals(other.chars[i])) {\n        equal = false;\n        break;\n      }\n    }\n    return equal;\n  }\n  copy(other) {\n    for (let i = 0; i < NR_COLS; i++) {\n      this.chars[i].copy(other.chars[i]);\n    }\n  }\n  isEmpty() {\n    let empty = true;\n    for (let i = 0; i < NR_COLS; i++) {\n      if (!this.chars[i].isEmpty()) {\n        empty = false;\n        break;\n      }\n    }\n    return empty;\n  }\n\n  /**\n   *  Set the cursor to a valid column.\n   */\n  setCursor(absPos) {\n    if (this.pos !== absPos) {\n      this.pos = absPos;\n    }\n    if (this.pos < 0) {\n      this.logger.log(3, 'Negative cursor position ' + this.pos);\n      this.pos = 0;\n    } else if (this.pos > NR_COLS) {\n      this.logger.log(3, 'Too large cursor position ' + this.pos);\n      this.pos = NR_COLS;\n    }\n  }\n\n  /**\n   * Move the cursor relative to current position.\n   */\n  moveCursor(relPos) {\n    const newPos = this.pos + relPos;\n    if (relPos > 1) {\n      for (let i = this.pos + 1; i < newPos + 1; i++) {\n        this.chars[i].setPenState(this.currPenState);\n      }\n    }\n    this.setCursor(newPos);\n  }\n\n  /**\n   * Backspace, move one step back and clear character.\n   */\n  backSpace() {\n    this.moveCursor(-1);\n    this.chars[this.pos].setChar(' ', this.currPenState);\n  }\n  insertChar(byte) {\n    if (byte >= 0x90) {\n      // Extended char\n      this.backSpace();\n    }\n    const char = getCharForByte(byte);\n    if (this.pos >= NR_COLS) {\n      this.logger.log(0, () => 'Cannot insert ' + byte.toString(16) + ' (' + char + ') at position ' + this.pos + '. Skipping it!');\n      return;\n    }\n    this.chars[this.pos].setChar(char, this.currPenState);\n    this.moveCursor(1);\n  }\n  clearFromPos(startPos) {\n    let i;\n    for (i = startPos; i < NR_COLS; i++) {\n      this.chars[i].reset();\n    }\n  }\n  clear() {\n    this.clearFromPos(0);\n    this.pos = 0;\n    this.currPenState.reset();\n  }\n  clearToEndOfRow() {\n    this.clearFromPos(this.pos);\n  }\n  getTextString() {\n    const chars = [];\n    let empty = true;\n    for (let i = 0; i < NR_COLS; i++) {\n      const char = this.chars[i].uchar;\n      if (char !== ' ') {\n        empty = false;\n      }\n      chars.push(char);\n    }\n    if (empty) {\n      return '';\n    } else {\n      return chars.join('');\n    }\n  }\n  setPenStyles(styles) {\n    this.currPenState.setStyles(styles);\n    const currChar = this.chars[this.pos];\n    currChar.setPenState(this.currPenState);\n  }\n}\n\n/**\n * Keep a CEA-608 screen of 32x15 styled characters\n * @constructor\n */\nclass CaptionScreen {\n  constructor(logger) {\n    this.rows = void 0;\n    this.currRow = void 0;\n    this.nrRollUpRows = void 0;\n    this.lastOutputScreen = void 0;\n    this.logger = void 0;\n    this.rows = [];\n    for (let i = 0; i < NR_ROWS; i++) {\n      this.rows.push(new Row(logger));\n    } // Note that we use zero-based numbering (0-14)\n\n    this.logger = logger;\n    this.currRow = NR_ROWS - 1;\n    this.nrRollUpRows = null;\n    this.lastOutputScreen = null;\n    this.reset();\n  }\n  reset() {\n    for (let i = 0; i < NR_ROWS; i++) {\n      this.rows[i].clear();\n    }\n    this.currRow = NR_ROWS - 1;\n  }\n  equals(other) {\n    let equal = true;\n    for (let i = 0; i < NR_ROWS; i++) {\n      if (!this.rows[i].equals(other.rows[i])) {\n        equal = false;\n        break;\n      }\n    }\n    return equal;\n  }\n  copy(other) {\n    for (let i = 0; i < NR_ROWS; i++) {\n      this.rows[i].copy(other.rows[i]);\n    }\n  }\n  isEmpty() {\n    let empty = true;\n    for (let i = 0; i < NR_ROWS; i++) {\n      if (!this.rows[i].isEmpty()) {\n        empty = false;\n        break;\n      }\n    }\n    return empty;\n  }\n  backSpace() {\n    const row = this.rows[this.currRow];\n    row.backSpace();\n  }\n  clearToEndOfRow() {\n    const row = this.rows[this.currRow];\n    row.clearToEndOfRow();\n  }\n\n  /**\n   * Insert a character (without styling) in the current row.\n   */\n  insertChar(char) {\n    const row = this.rows[this.currRow];\n    row.insertChar(char);\n  }\n  setPen(styles) {\n    const row = this.rows[this.currRow];\n    row.setPenStyles(styles);\n  }\n  moveCursor(relPos) {\n    const row = this.rows[this.currRow];\n    row.moveCursor(relPos);\n  }\n  setCursor(absPos) {\n    this.logger.log(2, 'setCursor: ' + absPos);\n    const row = this.rows[this.currRow];\n    row.setCursor(absPos);\n  }\n  setPAC(pacData) {\n    this.logger.log(2, () => 'pacData = ' + JSON.stringify(pacData));\n    let newRow = pacData.row - 1;\n    if (this.nrRollUpRows && newRow < this.nrRollUpRows - 1) {\n      newRow = this.nrRollUpRows - 1;\n    }\n\n    // Make sure this only affects Roll-up Captions by checking this.nrRollUpRows\n    if (this.nrRollUpRows && this.currRow !== newRow) {\n      // clear all rows first\n      for (let i = 0; i < NR_ROWS; i++) {\n        this.rows[i].clear();\n      }\n\n      // Copy this.nrRollUpRows rows from lastOutputScreen and place it in the newRow location\n      // topRowIndex - the start of rows to copy (inclusive index)\n      const topRowIndex = this.currRow + 1 - this.nrRollUpRows;\n      // We only copy if the last position was already shown.\n      // We use the cueStartTime value to check this.\n      const lastOutputScreen = this.lastOutputScreen;\n      if (lastOutputScreen) {\n        const prevLineTime = lastOutputScreen.rows[topRowIndex].cueStartTime;\n        const time = this.logger.time;\n        if (prevLineTime && time !== null && prevLineTime < time) {\n          for (let i = 0; i < this.nrRollUpRows; i++) {\n            this.rows[newRow - this.nrRollUpRows + i + 1].copy(lastOutputScreen.rows[topRowIndex + i]);\n          }\n        }\n      }\n    }\n    this.currRow = newRow;\n    const row = this.rows[this.currRow];\n    if (pacData.indent !== null) {\n      const indent = pacData.indent;\n      const prevPos = Math.max(indent - 1, 0);\n      row.setCursor(pacData.indent);\n      pacData.color = row.chars[prevPos].penState.foreground;\n    }\n    const styles = {\n      foreground: pacData.color,\n      underline: pacData.underline,\n      italics: pacData.italics,\n      background: 'black',\n      flash: false\n    };\n    this.setPen(styles);\n  }\n\n  /**\n   * Set background/extra foreground, but first do back_space, and then insert space (backwards compatibility).\n   */\n  setBkgData(bkgData) {\n    this.logger.log(2, () => 'bkgData = ' + JSON.stringify(bkgData));\n    this.backSpace();\n    this.setPen(bkgData);\n    this.insertChar(0x20); // Space\n  }\n\n  setRollUpRows(nrRows) {\n    this.nrRollUpRows = nrRows;\n  }\n  rollUp() {\n    if (this.nrRollUpRows === null) {\n      this.logger.log(3, 'roll_up but nrRollUpRows not set yet');\n      return; // Not properly setup\n    }\n\n    this.logger.log(1, () => this.getDisplayText());\n    const topRowIndex = this.currRow + 1 - this.nrRollUpRows;\n    const topRow = this.rows.splice(topRowIndex, 1)[0];\n    topRow.clear();\n    this.rows.splice(this.currRow, 0, topRow);\n    this.logger.log(2, 'Rolling up');\n    // this.logger.log(VerboseLevel.TEXT, this.get_display_text())\n  }\n\n  /**\n   * Get all non-empty rows with as unicode text.\n   */\n  getDisplayText(asOneRow) {\n    asOneRow = asOneRow || false;\n    const displayText = [];\n    let text = '';\n    let rowNr = -1;\n    for (let i = 0; i < NR_ROWS; i++) {\n      const rowText = this.rows[i].getTextString();\n      if (rowText) {\n        rowNr = i + 1;\n        if (asOneRow) {\n          displayText.push('Row ' + rowNr + \": '\" + rowText + \"'\");\n        } else {\n          displayText.push(rowText.trim());\n        }\n      }\n    }\n    if (displayText.length > 0) {\n      if (asOneRow) {\n        text = '[' + displayText.join(' | ') + ']';\n      } else {\n        text = displayText.join('\\n');\n      }\n    }\n    return text;\n  }\n  getTextAndFormat() {\n    return this.rows;\n  }\n}\n\n// var modes = ['MODE_ROLL-UP', 'MODE_POP-ON', 'MODE_PAINT-ON', 'MODE_TEXT'];\n\nclass Cea608Channel {\n  constructor(channelNumber, outputFilter, logger) {\n    this.chNr = void 0;\n    this.outputFilter = void 0;\n    this.mode = void 0;\n    this.verbose = void 0;\n    this.displayedMemory = void 0;\n    this.nonDisplayedMemory = void 0;\n    this.lastOutputScreen = void 0;\n    this.currRollUpRow = void 0;\n    this.writeScreen = void 0;\n    this.cueStartTime = void 0;\n    this.logger = void 0;\n    this.chNr = channelNumber;\n    this.outputFilter = outputFilter;\n    this.mode = null;\n    this.verbose = 0;\n    this.displayedMemory = new CaptionScreen(logger);\n    this.nonDisplayedMemory = new CaptionScreen(logger);\n    this.lastOutputScreen = new CaptionScreen(logger);\n    this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1];\n    this.writeScreen = this.displayedMemory;\n    this.mode = null;\n    this.cueStartTime = null; // Keeps track of where a cue started.\n    this.logger = logger;\n  }\n  reset() {\n    this.mode = null;\n    this.displayedMemory.reset();\n    this.nonDisplayedMemory.reset();\n    this.lastOutputScreen.reset();\n    this.outputFilter.reset();\n    this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1];\n    this.writeScreen = this.displayedMemory;\n    this.mode = null;\n    this.cueStartTime = null;\n  }\n  getHandler() {\n    return this.outputFilter;\n  }\n  setHandler(newHandler) {\n    this.outputFilter = newHandler;\n  }\n  setPAC(pacData) {\n    this.writeScreen.setPAC(pacData);\n  }\n  setBkgData(bkgData) {\n    this.writeScreen.setBkgData(bkgData);\n  }\n  setMode(newMode) {\n    if (newMode === this.mode) {\n      return;\n    }\n    this.mode = newMode;\n    this.logger.log(2, () => 'MODE=' + newMode);\n    if (this.mode === 'MODE_POP-ON') {\n      this.writeScreen = this.nonDisplayedMemory;\n    } else {\n      this.writeScreen = this.displayedMemory;\n      this.writeScreen.reset();\n    }\n    if (this.mode !== 'MODE_ROLL-UP') {\n      this.displayedMemory.nrRollUpRows = null;\n      this.nonDisplayedMemory.nrRollUpRows = null;\n    }\n    this.mode = newMode;\n  }\n  insertChars(chars) {\n    for (let i = 0; i < chars.length; i++) {\n      this.writeScreen.insertChar(chars[i]);\n    }\n    const screen = this.writeScreen === this.displayedMemory ? 'DISP' : 'NON_DISP';\n    this.logger.log(2, () => screen + ': ' + this.writeScreen.getDisplayText(true));\n    if (this.mode === 'MODE_PAINT-ON' || this.mode === 'MODE_ROLL-UP') {\n      this.logger.log(1, () => 'DISPLAYED: ' + this.displayedMemory.getDisplayText(true));\n      this.outputDataUpdate();\n    }\n  }\n  ccRCL() {\n    // Resume Caption Loading (switch mode to Pop On)\n    this.logger.log(2, 'RCL - Resume Caption Loading');\n    this.setMode('MODE_POP-ON');\n  }\n  ccBS() {\n    // BackSpace\n    this.logger.log(2, 'BS - BackSpace');\n    if (this.mode === 'MODE_TEXT') {\n      return;\n    }\n    this.writeScreen.backSpace();\n    if (this.writeScreen === this.displayedMemory) {\n      this.outputDataUpdate();\n    }\n  }\n  ccAOF() {\n    // Reserved (formerly Alarm Off)\n  }\n  ccAON() {\n    // Reserved (formerly Alarm On)\n  }\n  ccDER() {\n    // Delete to End of Row\n    this.logger.log(2, 'DER- Delete to End of Row');\n    this.writeScreen.clearToEndOfRow();\n    this.outputDataUpdate();\n  }\n  ccRU(nrRows) {\n    // Roll-Up Captions-2,3,or 4 Rows\n    this.logger.log(2, 'RU(' + nrRows + ') - Roll Up');\n    this.writeScreen = this.displayedMemory;\n    this.setMode('MODE_ROLL-UP');\n    this.writeScreen.setRollUpRows(nrRows);\n  }\n  ccFON() {\n    // Flash On\n    this.logger.log(2, 'FON - Flash On');\n    this.writeScreen.setPen({\n      flash: true\n    });\n  }\n  ccRDC() {\n    // Resume Direct Captioning (switch mode to PaintOn)\n    this.logger.log(2, 'RDC - Resume Direct Captioning');\n    this.setMode('MODE_PAINT-ON');\n  }\n  ccTR() {\n    // Text Restart in text mode (not supported, however)\n    this.logger.log(2, 'TR');\n    this.setMode('MODE_TEXT');\n  }\n  ccRTD() {\n    // Resume Text Display in Text mode (not supported, however)\n    this.logger.log(2, 'RTD');\n    this.setMode('MODE_TEXT');\n  }\n  ccEDM() {\n    // Erase Displayed Memory\n    this.logger.log(2, 'EDM - Erase Displayed Memory');\n    this.displayedMemory.reset();\n    this.outputDataUpdate(true);\n  }\n  ccCR() {\n    // Carriage Return\n    this.logger.log(2, 'CR - Carriage Return');\n    this.writeScreen.rollUp();\n    this.outputDataUpdate(true);\n  }\n  ccENM() {\n    // Erase Non-Displayed Memory\n    this.logger.log(2, 'ENM - Erase Non-displayed Memory');\n    this.nonDisplayedMemory.reset();\n  }\n  ccEOC() {\n    // End of Caption (Flip Memories)\n    this.logger.log(2, 'EOC - End Of Caption');\n    if (this.mode === 'MODE_POP-ON') {\n      const tmp = this.displayedMemory;\n      this.displayedMemory = this.nonDisplayedMemory;\n      this.nonDisplayedMemory = tmp;\n      this.writeScreen = this.nonDisplayedMemory;\n      this.logger.log(1, () => 'DISP: ' + this.displayedMemory.getDisplayText());\n    }\n    this.outputDataUpdate(true);\n  }\n  ccTO(nrCols) {\n    // Tab Offset 1,2, or 3 columns\n    this.logger.log(2, 'TO(' + nrCols + ') - Tab Offset');\n    this.writeScreen.moveCursor(nrCols);\n  }\n  ccMIDROW(secondByte) {\n    // Parse MIDROW command\n    const styles = {\n      flash: false\n    };\n    styles.underline = secondByte % 2 === 1;\n    styles.italics = secondByte >= 0x2e;\n    if (!styles.italics) {\n      const colorIndex = Math.floor(secondByte / 2) - 0x10;\n      const colors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta'];\n      styles.foreground = colors[colorIndex];\n    } else {\n      styles.foreground = 'white';\n    }\n    this.logger.log(2, 'MIDROW: ' + JSON.stringify(styles));\n    this.writeScreen.setPen(styles);\n  }\n  outputDataUpdate(dispatch = false) {\n    const time = this.logger.time;\n    if (time === null) {\n      return;\n    }\n    if (this.outputFilter) {\n      if (this.cueStartTime === null && !this.displayedMemory.isEmpty()) {\n        // Start of a new cue\n        this.cueStartTime = time;\n      } else {\n        if (!this.displayedMemory.equals(this.lastOutputScreen)) {\n          this.outputFilter.newCue(this.cueStartTime, time, this.lastOutputScreen);\n          if (dispatch && this.outputFilter.dispatchCue) {\n            this.outputFilter.dispatchCue();\n          }\n          this.cueStartTime = this.displayedMemory.isEmpty() ? null : time;\n        }\n      }\n      this.lastOutputScreen.copy(this.displayedMemory);\n    }\n  }\n  cueSplitAtTime(t) {\n    if (this.outputFilter) {\n      if (!this.displayedMemory.isEmpty()) {\n        if (this.outputFilter.newCue) {\n          this.outputFilter.newCue(this.cueStartTime, t, this.displayedMemory);\n        }\n        this.cueStartTime = t;\n      }\n    }\n  }\n}\n\n// Will be 1 or 2 when parsing captions\n\nclass Cea608Parser {\n  constructor(field, out1, out2) {\n    this.channels = void 0;\n    this.currentChannel = 0;\n    this.cmdHistory = void 0;\n    this.logger = void 0;\n    const logger = new CaptionsLogger();\n    this.channels = [null, new Cea608Channel(field, out1, logger), new Cea608Channel(field + 1, out2, logger)];\n    this.cmdHistory = createCmdHistory();\n    this.logger = logger;\n  }\n  getHandler(channel) {\n    return this.channels[channel].getHandler();\n  }\n  setHandler(channel, newHandler) {\n    this.channels[channel].setHandler(newHandler);\n  }\n\n  /**\n   * Add data for time t in forms of list of bytes (unsigned ints). The bytes are treated as pairs.\n   */\n  addData(time, byteList) {\n    let cmdFound;\n    let a;\n    let b;\n    let charsFound = false;\n    this.logger.time = time;\n    for (let i = 0; i < byteList.length; i += 2) {\n      a = byteList[i] & 0x7f;\n      b = byteList[i + 1] & 0x7f;\n      if (a === 0 && b === 0) {\n        continue;\n      } else {\n        this.logger.log(3, '[' + numArrayToHexArray([byteList[i], byteList[i + 1]]) + '] -> (' + numArrayToHexArray([a, b]) + ')');\n      }\n      cmdFound = this.parseCmd(a, b);\n      if (!cmdFound) {\n        cmdFound = this.parseMidrow(a, b);\n      }\n      if (!cmdFound) {\n        cmdFound = this.parsePAC(a, b);\n      }\n      if (!cmdFound) {\n        cmdFound = this.parseBackgroundAttributes(a, b);\n      }\n      if (!cmdFound) {\n        charsFound = this.parseChars(a, b);\n        if (charsFound) {\n          const currChNr = this.currentChannel;\n          if (currChNr && currChNr > 0) {\n            const channel = this.channels[currChNr];\n            channel.insertChars(charsFound);\n          } else {\n            this.logger.log(2, 'No channel found yet. TEXT-MODE?');\n          }\n        }\n      }\n      if (!cmdFound && !charsFound) {\n        this.logger.log(2, \"Couldn't parse cleaned data \" + numArrayToHexArray([a, b]) + ' orig: ' + numArrayToHexArray([byteList[i], byteList[i + 1]]));\n      }\n    }\n  }\n\n  /**\n   * Parse Command.\n   * @returns True if a command was found\n   */\n  parseCmd(a, b) {\n    const {\n      cmdHistory\n    } = this;\n    const cond1 = (a === 0x14 || a === 0x1c || a === 0x15 || a === 0x1d) && b >= 0x20 && b <= 0x2f;\n    const cond2 = (a === 0x17 || a === 0x1f) && b >= 0x21 && b <= 0x23;\n    if (!(cond1 || cond2)) {\n      return false;\n    }\n    if (hasCmdRepeated(a, b, cmdHistory)) {\n      setLastCmd(null, null, cmdHistory);\n      this.logger.log(3, 'Repeated command (' + numArrayToHexArray([a, b]) + ') is dropped');\n      return true;\n    }\n    const chNr = a === 0x14 || a === 0x15 || a === 0x17 ? 1 : 2;\n    const channel = this.channels[chNr];\n    if (a === 0x14 || a === 0x15 || a === 0x1c || a === 0x1d) {\n      if (b === 0x20) {\n        channel.ccRCL();\n      } else if (b === 0x21) {\n        channel.ccBS();\n      } else if (b === 0x22) {\n        channel.ccAOF();\n      } else if (b === 0x23) {\n        channel.ccAON();\n      } else if (b === 0x24) {\n        channel.ccDER();\n      } else if (b === 0x25) {\n        channel.ccRU(2);\n      } else if (b === 0x26) {\n        channel.ccRU(3);\n      } else if (b === 0x27) {\n        channel.ccRU(4);\n      } else if (b === 0x28) {\n        channel.ccFON();\n      } else if (b === 0x29) {\n        channel.ccRDC();\n      } else if (b === 0x2a) {\n        channel.ccTR();\n      } else if (b === 0x2b) {\n        channel.ccRTD();\n      } else if (b === 0x2c) {\n        channel.ccEDM();\n      } else if (b === 0x2d) {\n        channel.ccCR();\n      } else if (b === 0x2e) {\n        channel.ccENM();\n      } else if (b === 0x2f) {\n        channel.ccEOC();\n      }\n    } else {\n      // a == 0x17 || a == 0x1F\n      channel.ccTO(b - 0x20);\n    }\n    setLastCmd(a, b, cmdHistory);\n    this.currentChannel = chNr;\n    return true;\n  }\n\n  /**\n   * Parse midrow styling command\n   */\n  parseMidrow(a, b) {\n    let chNr = 0;\n    if ((a === 0x11 || a === 0x19) && b >= 0x20 && b <= 0x2f) {\n      if (a === 0x11) {\n        chNr = 1;\n      } else {\n        chNr = 2;\n      }\n      if (chNr !== this.currentChannel) {\n        this.logger.log(0, 'Mismatch channel in midrow parsing');\n        return false;\n      }\n      const channel = this.channels[chNr];\n      if (!channel) {\n        return false;\n      }\n      channel.ccMIDROW(b);\n      this.logger.log(3, 'MIDROW (' + numArrayToHexArray([a, b]) + ')');\n      return true;\n    }\n    return false;\n  }\n\n  /**\n   * Parse Preable Access Codes (Table 53).\n   * @returns {Boolean} Tells if PAC found\n   */\n  parsePAC(a, b) {\n    let row;\n    const cmdHistory = this.cmdHistory;\n    const case1 = (a >= 0x11 && a <= 0x17 || a >= 0x19 && a <= 0x1f) && b >= 0x40 && b <= 0x7f;\n    const case2 = (a === 0x10 || a === 0x18) && b >= 0x40 && b <= 0x5f;\n    if (!(case1 || case2)) {\n      return false;\n    }\n    if (hasCmdRepeated(a, b, cmdHistory)) {\n      setLastCmd(null, null, cmdHistory);\n      return true; // Repeated commands are dropped (once)\n    }\n\n    const chNr = a <= 0x17 ? 1 : 2;\n    if (b >= 0x40 && b <= 0x5f) {\n      row = chNr === 1 ? rowsLowCh1[a] : rowsLowCh2[a];\n    } else {\n      // 0x60 <= b <= 0x7F\n      row = chNr === 1 ? rowsHighCh1[a] : rowsHighCh2[a];\n    }\n    const channel = this.channels[chNr];\n    if (!channel) {\n      return false;\n    }\n    channel.setPAC(this.interpretPAC(row, b));\n    setLastCmd(a, b, cmdHistory);\n    this.currentChannel = chNr;\n    return true;\n  }\n\n  /**\n   * Interpret the second byte of the pac, and return the information.\n   * @returns pacData with style parameters\n   */\n  interpretPAC(row, byte) {\n    let pacIndex;\n    const pacData = {\n      color: null,\n      italics: false,\n      indent: null,\n      underline: false,\n      row: row\n    };\n    if (byte > 0x5f) {\n      pacIndex = byte - 0x60;\n    } else {\n      pacIndex = byte - 0x40;\n    }\n    pacData.underline = (pacIndex & 1) === 1;\n    if (pacIndex <= 0xd) {\n      pacData.color = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'white'][Math.floor(pacIndex / 2)];\n    } else if (pacIndex <= 0xf) {\n      pacData.italics = true;\n      pacData.color = 'white';\n    } else {\n      pacData.indent = Math.floor((pacIndex - 0x10) / 2) * 4;\n    }\n    return pacData; // Note that row has zero offset. The spec uses 1.\n  }\n\n  /**\n   * Parse characters.\n   * @returns An array with 1 to 2 codes corresponding to chars, if found. null otherwise.\n   */\n  parseChars(a, b) {\n    let channelNr;\n    let charCodes = null;\n    let charCode1 = null;\n    if (a >= 0x19) {\n      channelNr = 2;\n      charCode1 = a - 8;\n    } else {\n      channelNr = 1;\n      charCode1 = a;\n    }\n    if (charCode1 >= 0x11 && charCode1 <= 0x13) {\n      // Special character\n      let oneCode;\n      if (charCode1 === 0x11) {\n        oneCode = b + 0x50;\n      } else if (charCode1 === 0x12) {\n        oneCode = b + 0x70;\n      } else {\n        oneCode = b + 0x90;\n      }\n      this.logger.log(2, \"Special char '\" + getCharForByte(oneCode) + \"' in channel \" + channelNr);\n      charCodes = [oneCode];\n    } else if (a >= 0x20 && a <= 0x7f) {\n      charCodes = b === 0 ? [a] : [a, b];\n    }\n    if (charCodes) {\n      const hexCodes = numArrayToHexArray(charCodes);\n      this.logger.log(3, 'Char codes =  ' + hexCodes.join(','));\n      setLastCmd(a, b, this.cmdHistory);\n    }\n    return charCodes;\n  }\n\n  /**\n   * Parse extended background attributes as well as new foreground color black.\n   * @returns True if background attributes are found\n   */\n  parseBackgroundAttributes(a, b) {\n    const case1 = (a === 0x10 || a === 0x18) && b >= 0x20 && b <= 0x2f;\n    const case2 = (a === 0x17 || a === 0x1f) && b >= 0x2d && b <= 0x2f;\n    if (!(case1 || case2)) {\n      return false;\n    }\n    let index;\n    const bkgData = {};\n    if (a === 0x10 || a === 0x18) {\n      index = Math.floor((b - 0x20) / 2);\n      bkgData.background = backgroundColors[index];\n      if (b % 2 === 1) {\n        bkgData.background = bkgData.background + '_semi';\n      }\n    } else if (b === 0x2d) {\n      bkgData.background = 'transparent';\n    } else {\n      bkgData.foreground = 'black';\n      if (b === 0x2f) {\n        bkgData.underline = true;\n      }\n    }\n    const chNr = a <= 0x17 ? 1 : 2;\n    const channel = this.channels[chNr];\n    channel.setBkgData(bkgData);\n    setLastCmd(a, b, this.cmdHistory);\n    return true;\n  }\n\n  /**\n   * Reset state of parser and its channels.\n   */\n  reset() {\n    for (let i = 0; i < Object.keys(this.channels).length; i++) {\n      const channel = this.channels[i];\n      if (channel) {\n        channel.reset();\n      }\n    }\n    this.cmdHistory = createCmdHistory();\n  }\n\n  /**\n   * Trigger the generation of a cue, and the start of a new one if displayScreens are not empty.\n   */\n  cueSplitAtTime(t) {\n    for (let i = 0; i < this.channels.length; i++) {\n      const channel = this.channels[i];\n      if (channel) {\n        channel.cueSplitAtTime(t);\n      }\n    }\n  }\n}\nfunction setLastCmd(a, b, cmdHistory) {\n  cmdHistory.a = a;\n  cmdHistory.b = b;\n}\nfunction hasCmdRepeated(a, b, cmdHistory) {\n  return cmdHistory.a === a && cmdHistory.b === b;\n}\nfunction createCmdHistory() {\n  return {\n    a: null,\n    b: null\n  };\n}\n\nclass OutputFilter {\n  constructor(timelineController, trackName) {\n    this.timelineController = void 0;\n    this.cueRanges = [];\n    this.trackName = void 0;\n    this.startTime = null;\n    this.endTime = null;\n    this.screen = null;\n    this.timelineController = timelineController;\n    this.trackName = trackName;\n  }\n  dispatchCue() {\n    if (this.startTime === null) {\n      return;\n    }\n    this.timelineController.addCues(this.trackName, this.startTime, this.endTime, this.screen, this.cueRanges);\n    this.startTime = null;\n  }\n  newCue(startTime, endTime, screen) {\n    if (this.startTime === null || this.startTime > startTime) {\n      this.startTime = startTime;\n    }\n    this.endTime = endTime;\n    this.screen = screen;\n    this.timelineController.createCaptionsTrack(this.trackName);\n  }\n  reset() {\n    this.cueRanges = [];\n    this.startTime = null;\n  }\n}\n\n/**\n * Copyright 2013 vtt.js Contributors\n *\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nvar VTTCue = (function () {\n  if (typeof self !== 'undefined' && self.VTTCue) {\n    return self.VTTCue;\n  }\n  const AllowedDirections = ['', 'lr', 'rl'];\n  const AllowedAlignments = ['start', 'middle', 'end', 'left', 'right'];\n  function isAllowedValue(allowed, value) {\n    if (typeof value !== 'string') {\n      return false;\n    }\n    // necessary for assuring the generic conforms to the Array interface\n    if (!Array.isArray(allowed)) {\n      return false;\n    }\n    // reset the type so that the next narrowing works well\n    const lcValue = value.toLowerCase();\n    // use the allow list to narrow the type to a specific subset of strings\n    if (~allowed.indexOf(lcValue)) {\n      return lcValue;\n    }\n    return false;\n  }\n  function findDirectionSetting(value) {\n    return isAllowedValue(AllowedDirections, value);\n  }\n  function findAlignSetting(value) {\n    return isAllowedValue(AllowedAlignments, value);\n  }\n  function extend(obj, ...rest) {\n    let i = 1;\n    for (; i < arguments.length; i++) {\n      const cobj = arguments[i];\n      for (const p in cobj) {\n        obj[p] = cobj[p];\n      }\n    }\n    return obj;\n  }\n  function VTTCue(startTime, endTime, text) {\n    const cue = this;\n    const baseObj = {\n      enumerable: true\n    };\n    /**\n     * Shim implementation specific properties. These properties are not in\n     * the spec.\n     */\n\n    // Lets us know when the VTTCue's data has changed in such a way that we need\n    // to recompute its display state. This lets us compute its display state\n    // lazily.\n    cue.hasBeenReset = false;\n\n    /**\n     * VTTCue and TextTrackCue properties\n     * http://dev.w3.org/html5/webvtt/#vttcue-interface\n     */\n\n    let _id = '';\n    let _pauseOnExit = false;\n    let _startTime = startTime;\n    let _endTime = endTime;\n    let _text = text;\n    let _region = null;\n    let _vertical = '';\n    let _snapToLines = true;\n    let _line = 'auto';\n    let _lineAlign = 'start';\n    let _position = 50;\n    let _positionAlign = 'middle';\n    let _size = 50;\n    let _align = 'middle';\n    Object.defineProperty(cue, 'id', extend({}, baseObj, {\n      get: function () {\n        return _id;\n      },\n      set: function (value) {\n        _id = '' + value;\n      }\n    }));\n    Object.defineProperty(cue, 'pauseOnExit', extend({}, baseObj, {\n      get: function () {\n        return _pauseOnExit;\n      },\n      set: function (value) {\n        _pauseOnExit = !!value;\n      }\n    }));\n    Object.defineProperty(cue, 'startTime', extend({}, baseObj, {\n      get: function () {\n        return _startTime;\n      },\n      set: function (value) {\n        if (typeof value !== 'number') {\n          throw new TypeError('Start time must be set to a number.');\n        }\n        _startTime = value;\n        this.hasBeenReset = true;\n      }\n    }));\n    Object.defineProperty(cue, 'endTime', extend({}, baseObj, {\n      get: function () {\n        return _endTime;\n      },\n      set: function (value) {\n        if (typeof value !== 'number') {\n          throw new TypeError('End time must be set to a number.');\n        }\n        _endTime = value;\n        this.hasBeenReset = true;\n      }\n    }));\n    Object.defineProperty(cue, 'text', extend({}, baseObj, {\n      get: function () {\n        return _text;\n      },\n      set: function (value) {\n        _text = '' + value;\n        this.hasBeenReset = true;\n      }\n    }));\n\n    // todo: implement VTTRegion polyfill?\n    Object.defineProperty(cue, 'region', extend({}, baseObj, {\n      get: function () {\n        return _region;\n      },\n      set: function (value) {\n        _region = value;\n        this.hasBeenReset = true;\n      }\n    }));\n    Object.defineProperty(cue, 'vertical', extend({}, baseObj, {\n      get: function () {\n        return _vertical;\n      },\n      set: function (value) {\n        const setting = findDirectionSetting(value);\n        // Have to check for false because the setting an be an empty string.\n        if (setting === false) {\n          throw new SyntaxError('An invalid or illegal string was specified.');\n        }\n        _vertical = setting;\n        this.hasBeenReset = true;\n      }\n    }));\n    Object.defineProperty(cue, 'snapToLines', extend({}, baseObj, {\n      get: function () {\n        return _snapToLines;\n      },\n      set: function (value) {\n        _snapToLines = !!value;\n        this.hasBeenReset = true;\n      }\n    }));\n    Object.defineProperty(cue, 'line', extend({}, baseObj, {\n      get: function () {\n        return _line;\n      },\n      set: function (value) {\n        if (typeof value !== 'number' && value !== 'auto') {\n          throw new SyntaxError('An invalid number or illegal string was specified.');\n        }\n        _line = value;\n        this.hasBeenReset = true;\n      }\n    }));\n    Object.defineProperty(cue, 'lineAlign', extend({}, baseObj, {\n      get: function () {\n        return _lineAlign;\n      },\n      set: function (value) {\n        const setting = findAlignSetting(value);\n        if (!setting) {\n          throw new SyntaxError('An invalid or illegal string was specified.');\n        }\n        _lineAlign = setting;\n        this.hasBeenReset = true;\n      }\n    }));\n    Object.defineProperty(cue, 'position', extend({}, baseObj, {\n      get: function () {\n        return _position;\n      },\n      set: function (value) {\n        if (value < 0 || value > 100) {\n          throw new Error('Position must be between 0 and 100.');\n        }\n        _position = value;\n        this.hasBeenReset = true;\n      }\n    }));\n    Object.defineProperty(cue, 'positionAlign', extend({}, baseObj, {\n      get: function () {\n        return _positionAlign;\n      },\n      set: function (value) {\n        const setting = findAlignSetting(value);\n        if (!setting) {\n          throw new SyntaxError('An invalid or illegal string was specified.');\n        }\n        _positionAlign = setting;\n        this.hasBeenReset = true;\n      }\n    }));\n    Object.defineProperty(cue, 'size', extend({}, baseObj, {\n      get: function () {\n        return _size;\n      },\n      set: function (value) {\n        if (value < 0 || value > 100) {\n          throw new Error('Size must be between 0 and 100.');\n        }\n        _size = value;\n        this.hasBeenReset = true;\n      }\n    }));\n    Object.defineProperty(cue, 'align', extend({}, baseObj, {\n      get: function () {\n        return _align;\n      },\n      set: function (value) {\n        const setting = findAlignSetting(value);\n        if (!setting) {\n          throw new SyntaxError('An invalid or illegal string was specified.');\n        }\n        _align = setting;\n        this.hasBeenReset = true;\n      }\n    }));\n\n    /**\n     * Other <track> spec defined properties\n     */\n\n    // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state\n    cue.displayState = undefined;\n  }\n\n  /**\n   * VTTCue methods\n   */\n\n  VTTCue.prototype.getCueAsHTML = function () {\n    // Assume WebVTT.convertCueToDOMTree is on the global.\n    const WebVTT = self.WebVTT;\n    return WebVTT.convertCueToDOMTree(self, this.text);\n  };\n  // this is a polyfill hack\n  return VTTCue;\n})();\n\n/*\n * Source: https://github.com/mozilla/vtt.js/blob/master/dist/vtt.js\n */\n\nclass StringDecoder {\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  decode(data, options) {\n    if (!data) {\n      return '';\n    }\n    if (typeof data !== 'string') {\n      throw new Error('Error - expected string data.');\n    }\n    return decodeURIComponent(encodeURIComponent(data));\n  }\n}\n\n// Try to parse input as a time stamp.\nfunction parseTimeStamp(input) {\n  function computeSeconds(h, m, s, f) {\n    return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + parseFloat(f || 0);\n  }\n  const m = input.match(/^(?:(\\d+):)?(\\d{2}):(\\d{2})(\\.\\d+)?/);\n  if (!m) {\n    return null;\n  }\n  if (parseFloat(m[2]) > 59) {\n    // Timestamp takes the form of [hours]:[minutes].[milliseconds]\n    // First position is hours as it's over 59.\n    return computeSeconds(m[2], m[3], 0, m[4]);\n  }\n  // Timestamp takes the form of [hours (optional)]:[minutes]:[seconds].[milliseconds]\n  return computeSeconds(m[1], m[2], m[3], m[4]);\n}\n\n// A settings object holds key/value pairs and will ignore anything but the first\n// assignment to a specific key.\nclass Settings {\n  constructor() {\n    this.values = Object.create(null);\n  }\n  // Only accept the first assignment to any key.\n  set(k, v) {\n    if (!this.get(k) && v !== '') {\n      this.values[k] = v;\n    }\n  }\n  // Return the value for a key, or a default value.\n  // If 'defaultKey' is passed then 'dflt' is assumed to be an object with\n  // a number of possible default values as properties where 'defaultKey' is\n  // the key of the property that will be chosen; otherwise it's assumed to be\n  // a single value.\n  get(k, dflt, defaultKey) {\n    if (defaultKey) {\n      return this.has(k) ? this.values[k] : dflt[defaultKey];\n    }\n    return this.has(k) ? this.values[k] : dflt;\n  }\n  // Check whether we have a value for a key.\n  has(k) {\n    return k in this.values;\n  }\n  // Accept a setting if its one of the given alternatives.\n  alt(k, v, a) {\n    for (let n = 0; n < a.length; ++n) {\n      if (v === a[n]) {\n        this.set(k, v);\n        break;\n      }\n    }\n  }\n  // Accept a setting if its a valid (signed) integer.\n  integer(k, v) {\n    if (/^-?\\d+$/.test(v)) {\n      // integer\n      this.set(k, parseInt(v, 10));\n    }\n  }\n  // Accept a setting if its a valid percentage.\n  percent(k, v) {\n    if (/^([\\d]{1,3})(\\.[\\d]*)?%$/.test(v)) {\n      const percent = parseFloat(v);\n      if (percent >= 0 && percent <= 100) {\n        this.set(k, percent);\n        return true;\n      }\n    }\n    return false;\n  }\n}\n\n// Helper function to parse input into groups separated by 'groupDelim', and\n// interpret each group as a key/value pair separated by 'keyValueDelim'.\nfunction parseOptions(input, callback, keyValueDelim, groupDelim) {\n  const groups = groupDelim ? input.split(groupDelim) : [input];\n  for (const i in groups) {\n    if (typeof groups[i] !== 'string') {\n      continue;\n    }\n    const kv = groups[i].split(keyValueDelim);\n    if (kv.length !== 2) {\n      continue;\n    }\n    const k = kv[0];\n    const v = kv[1];\n    callback(k, v);\n  }\n}\nconst defaults = new VTTCue(0, 0, '');\n// 'middle' was changed to 'center' in the spec: https://github.com/w3c/webvtt/pull/244\n//  Safari doesn't yet support this change, but FF and Chrome do.\nconst center = defaults.align === 'middle' ? 'middle' : 'center';\nfunction parseCue(input, cue, regionList) {\n  // Remember the original input if we need to throw an error.\n  const oInput = input;\n  // 4.1 WebVTT timestamp\n  function consumeTimeStamp() {\n    const ts = parseTimeStamp(input);\n    if (ts === null) {\n      throw new Error('Malformed timestamp: ' + oInput);\n    }\n\n    // Remove time stamp from input.\n    input = input.replace(/^[^\\sa-zA-Z-]+/, '');\n    return ts;\n  }\n\n  // 4.4.2 WebVTT cue settings\n  function consumeCueSettings(input, cue) {\n    const settings = new Settings();\n    parseOptions(input, function (k, v) {\n      let vals;\n      switch (k) {\n        case 'region':\n          // Find the last region we parsed with the same region id.\n          for (let i = regionList.length - 1; i >= 0; i--) {\n            if (regionList[i].id === v) {\n              settings.set(k, regionList[i].region);\n              break;\n            }\n          }\n          break;\n        case 'vertical':\n          settings.alt(k, v, ['rl', 'lr']);\n          break;\n        case 'line':\n          vals = v.split(',');\n          settings.integer(k, vals[0]);\n          if (settings.percent(k, vals[0])) {\n            settings.set('snapToLines', false);\n          }\n          settings.alt(k, vals[0], ['auto']);\n          if (vals.length === 2) {\n            settings.alt('lineAlign', vals[1], ['start', center, 'end']);\n          }\n          break;\n        case 'position':\n          vals = v.split(',');\n          settings.percent(k, vals[0]);\n          if (vals.length === 2) {\n            settings.alt('positionAlign', vals[1], ['start', center, 'end', 'line-left', 'line-right', 'auto']);\n          }\n          break;\n        case 'size':\n          settings.percent(k, v);\n          break;\n        case 'align':\n          settings.alt(k, v, ['start', center, 'end', 'left', 'right']);\n          break;\n      }\n    }, /:/, /\\s/);\n\n    // Apply default values for any missing fields.\n    cue.region = settings.get('region', null);\n    cue.vertical = settings.get('vertical', '');\n    let line = settings.get('line', 'auto');\n    if (line === 'auto' && defaults.line === -1) {\n      // set numeric line number for Safari\n      line = -1;\n    }\n    cue.line = line;\n    cue.lineAlign = settings.get('lineAlign', 'start');\n    cue.snapToLines = settings.get('snapToLines', true);\n    cue.size = settings.get('size', 100);\n    cue.align = settings.get('align', center);\n    let position = settings.get('position', 'auto');\n    if (position === 'auto' && defaults.position === 50) {\n      // set numeric position for Safari\n      position = cue.align === 'start' || cue.align === 'left' ? 0 : cue.align === 'end' || cue.align === 'right' ? 100 : 50;\n    }\n    cue.position = position;\n  }\n  function skipWhitespace() {\n    input = input.replace(/^\\s+/, '');\n  }\n\n  // 4.1 WebVTT cue timings.\n  skipWhitespace();\n  cue.startTime = consumeTimeStamp(); // (1) collect cue start time\n  skipWhitespace();\n  if (input.slice(0, 3) !== '-->') {\n    // (3) next characters must match '-->'\n    throw new Error(\"Malformed time stamp (time stamps must be separated by '-->'): \" + oInput);\n  }\n  input = input.slice(3);\n  skipWhitespace();\n  cue.endTime = consumeTimeStamp(); // (5) collect cue end time\n\n  // 4.1 WebVTT cue settings list.\n  skipWhitespace();\n  consumeCueSettings(input, cue);\n}\nfunction fixLineBreaks(input) {\n  return input.replace(/<br(?: \\/)?>/gi, '\\n');\n}\nclass VTTParser {\n  constructor() {\n    this.state = 'INITIAL';\n    this.buffer = '';\n    this.decoder = new StringDecoder();\n    this.regionList = [];\n    this.cue = null;\n    this.oncue = void 0;\n    this.onparsingerror = void 0;\n    this.onflush = void 0;\n  }\n  parse(data) {\n    const _this = this;\n\n    // If there is no data then we won't decode it, but will just try to parse\n    // whatever is in buffer already. This may occur in circumstances, for\n    // example when flush() is called.\n    if (data) {\n      // Try to decode the data that we received.\n      _this.buffer += _this.decoder.decode(data, {\n        stream: true\n      });\n    }\n    function collectNextLine() {\n      let buffer = _this.buffer;\n      let pos = 0;\n      buffer = fixLineBreaks(buffer);\n      while (pos < buffer.length && buffer[pos] !== '\\r' && buffer[pos] !== '\\n') {\n        ++pos;\n      }\n      const line = buffer.slice(0, pos);\n      // Advance the buffer early in case we fail below.\n      if (buffer[pos] === '\\r') {\n        ++pos;\n      }\n      if (buffer[pos] === '\\n') {\n        ++pos;\n      }\n      _this.buffer = buffer.slice(pos);\n      return line;\n    }\n\n    // 3.2 WebVTT metadata header syntax\n    function parseHeader(input) {\n      parseOptions(input, function (k, v) {\n        // switch (k) {\n        // case 'region':\n        // 3.3 WebVTT region metadata header syntax\n        // console.log('parse region', v);\n        // parseRegion(v);\n        // break;\n        // }\n      }, /:/);\n    }\n\n    // 5.1 WebVTT file parsing.\n    try {\n      let line = '';\n      if (_this.state === 'INITIAL') {\n        // We can't start parsing until we have the first line.\n        if (!/\\r\\n|\\n/.test(_this.buffer)) {\n          return this;\n        }\n        line = collectNextLine();\n        // strip of UTF-8 BOM if any\n        // https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8\n        const m = line.match(/^(ï»¿)?WEBVTT([ \\t].*)?$/);\n        if (!(m != null && m[0])) {\n          throw new Error('Malformed WebVTT signature.');\n        }\n        _this.state = 'HEADER';\n      }\n      let alreadyCollectedLine = false;\n      while (_this.buffer) {\n        // We can't parse a line until we have the full line.\n        if (!/\\r\\n|\\n/.test(_this.buffer)) {\n          return this;\n        }\n        if (!alreadyCollectedLine) {\n          line = collectNextLine();\n        } else {\n          alreadyCollectedLine = false;\n        }\n        switch (_this.state) {\n          case 'HEADER':\n            // 13-18 - Allow a header (metadata) under the WEBVTT line.\n            if (/:/.test(line)) {\n              parseHeader(line);\n            } else if (!line) {\n              // An empty line terminates the header and starts the body (cues).\n              _this.state = 'ID';\n            }\n            continue;\n          case 'NOTE':\n            // Ignore NOTE blocks.\n            if (!line) {\n              _this.state = 'ID';\n            }\n            continue;\n          case 'ID':\n            // Check for the start of NOTE blocks.\n            if (/^NOTE($|[ \\t])/.test(line)) {\n              _this.state = 'NOTE';\n              break;\n            }\n            // 19-29 - Allow any number of line terminators, then initialize new cue values.\n            if (!line) {\n              continue;\n            }\n            _this.cue = new VTTCue(0, 0, '');\n            _this.state = 'CUE';\n            // 30-39 - Check if self line contains an optional identifier or timing data.\n            if (line.indexOf('-->') === -1) {\n              _this.cue.id = line;\n              continue;\n            }\n          // Process line as start of a cue.\n          /* falls through */\n          case 'CUE':\n            // 40 - Collect cue timings and settings.\n            if (!_this.cue) {\n              _this.state = 'BADCUE';\n              continue;\n            }\n            try {\n              parseCue(line, _this.cue, _this.regionList);\n            } catch (e) {\n              // In case of an error ignore rest of the cue.\n              _this.cue = null;\n              _this.state = 'BADCUE';\n              continue;\n            }\n            _this.state = 'CUETEXT';\n            continue;\n          case 'CUETEXT':\n            {\n              const hasSubstring = line.indexOf('-->') !== -1;\n              // 34 - If we have an empty line then report the cue.\n              // 35 - If we have the special substring '-->' then report the cue,\n              // but do not collect the line as we need to process the current\n              // one as a new cue.\n              if (!line || hasSubstring && (alreadyCollectedLine = true)) {\n                // We are done parsing self cue.\n                if (_this.oncue && _this.cue) {\n                  _this.oncue(_this.cue);\n                }\n                _this.cue = null;\n                _this.state = 'ID';\n                continue;\n              }\n              if (_this.cue === null) {\n                continue;\n              }\n              if (_this.cue.text) {\n                _this.cue.text += '\\n';\n              }\n              _this.cue.text += line;\n            }\n            continue;\n          case 'BADCUE':\n            // 54-62 - Collect and discard the remaining cue.\n            if (!line) {\n              _this.state = 'ID';\n            }\n        }\n      }\n    } catch (e) {\n      // If we are currently parsing a cue, report what we have.\n      if (_this.state === 'CUETEXT' && _this.cue && _this.oncue) {\n        _this.oncue(_this.cue);\n      }\n      _this.cue = null;\n      // Enter BADWEBVTT state if header was not parsed correctly otherwise\n      // another exception occurred so enter BADCUE state.\n      _this.state = _this.state === 'INITIAL' ? 'BADWEBVTT' : 'BADCUE';\n    }\n    return this;\n  }\n  flush() {\n    const _this = this;\n    try {\n      // Finish decoding the stream.\n      // _this.buffer += _this.decoder.decode();\n      // Synthesize the end of the current cue or region.\n      if (_this.cue || _this.state === 'HEADER') {\n        _this.buffer += '\\n\\n';\n        _this.parse();\n      }\n      // If we've flushed, parsed, and we're still on the INITIAL state then\n      // that means we don't have enough of the stream to parse the first\n      // line.\n      if (_this.state === 'INITIAL' || _this.state === 'BADWEBVTT') {\n        throw new Error('Malformed WebVTT signature.');\n      }\n    } catch (e) {\n      if (_this.onparsingerror) {\n        _this.onparsingerror(e);\n      }\n    }\n    if (_this.onflush) {\n      _this.onflush();\n    }\n    return this;\n  }\n}\n\nconst LINEBREAKS = /\\r\\n|\\n\\r|\\n|\\r/g;\n\n// String.prototype.startsWith is not supported in IE11\nconst startsWith = function startsWith(inputString, searchString, position = 0) {\n  return inputString.slice(position, position + searchString.length) === searchString;\n};\nconst cueString2millis = function cueString2millis(timeString) {\n  let ts = parseInt(timeString.slice(-3));\n  const secs = parseInt(timeString.slice(-6, -4));\n  const mins = parseInt(timeString.slice(-9, -7));\n  const hours = timeString.length > 9 ? parseInt(timeString.substring(0, timeString.indexOf(':'))) : 0;\n  if (!isFiniteNumber(ts) || !isFiniteNumber(secs) || !isFiniteNumber(mins) || !isFiniteNumber(hours)) {\n    throw Error(`Malformed X-TIMESTAMP-MAP: Local:${timeString}`);\n  }\n  ts += 1000 * secs;\n  ts += 60 * 1000 * mins;\n  ts += 60 * 60 * 1000 * hours;\n  return ts;\n};\n\n// From https://github.com/darkskyapp/string-hash\nconst hash = function hash(text) {\n  let _hash = 5381;\n  let i = text.length;\n  while (i) {\n    _hash = _hash * 33 ^ text.charCodeAt(--i);\n  }\n  return (_hash >>> 0).toString();\n};\n\n// Create a unique hash id for a cue based on start/end times and text.\n// This helps timeline-controller to avoid showing repeated captions.\nfunction generateCueId(startTime, endTime, text) {\n  return hash(startTime.toString()) + hash(endTime.toString()) + hash(text);\n}\nconst calculateOffset = function calculateOffset(vttCCs, cc, presentationTime) {\n  let currCC = vttCCs[cc];\n  let prevCC = vttCCs[currCC.prevCC];\n\n  // This is the first discontinuity or cues have been processed since the last discontinuity\n  // Offset = current discontinuity time\n  if (!prevCC || !prevCC.new && currCC.new) {\n    vttCCs.ccOffset = vttCCs.presentationOffset = currCC.start;\n    currCC.new = false;\n    return;\n  }\n\n  // There have been discontinuities since cues were last parsed.\n  // Offset = time elapsed\n  while ((_prevCC = prevCC) != null && _prevCC.new) {\n    var _prevCC;\n    vttCCs.ccOffset += currCC.start - prevCC.start;\n    currCC.new = false;\n    currCC = prevCC;\n    prevCC = vttCCs[currCC.prevCC];\n  }\n  vttCCs.presentationOffset = presentationTime;\n};\nfunction parseWebVTT(vttByteArray, initPTS, vttCCs, cc, timeOffset, callBack, errorCallBack) {\n  const parser = new VTTParser();\n  // Convert byteArray into string, replacing any somewhat exotic linefeeds with \"\\n\", then split on that character.\n  // Uint8Array.prototype.reduce is not implemented in IE11\n  const vttLines = utf8ArrayToStr(new Uint8Array(vttByteArray)).trim().replace(LINEBREAKS, '\\n').split('\\n');\n  const cues = [];\n  const init90kHz = initPTS ? toMpegTsClockFromTimescale(initPTS.baseTime, initPTS.timescale) : 0;\n  let cueTime = '00:00.000';\n  let timestampMapMPEGTS = 0;\n  let timestampMapLOCAL = 0;\n  let parsingError;\n  let inHeader = true;\n  parser.oncue = function (cue) {\n    // Adjust cue timing; clamp cues to start no earlier than - and drop cues that don't end after - 0 on timeline.\n    const currCC = vttCCs[cc];\n    let cueOffset = vttCCs.ccOffset;\n\n    // Calculate subtitle PTS offset\n    const webVttMpegTsMapOffset = (timestampMapMPEGTS - init90kHz) / 90000;\n\n    // Update offsets for new discontinuities\n    if (currCC != null && currCC.new) {\n      if (timestampMapLOCAL !== undefined) {\n        // When local time is provided, offset = discontinuity start time - local time\n        cueOffset = vttCCs.ccOffset = currCC.start;\n      } else {\n        calculateOffset(vttCCs, cc, webVttMpegTsMapOffset);\n      }\n    }\n    if (webVttMpegTsMapOffset) {\n      if (!initPTS) {\n        parsingError = new Error('Missing initPTS for VTT MPEGTS');\n        return;\n      }\n      // If we have MPEGTS, offset = presentation time + discontinuity offset\n      cueOffset = webVttMpegTsMapOffset - vttCCs.presentationOffset;\n    }\n    const duration = cue.endTime - cue.startTime;\n    const startTime = normalizePts((cue.startTime + cueOffset - timestampMapLOCAL) * 90000, timeOffset * 90000) / 90000;\n    cue.startTime = Math.max(startTime, 0);\n    cue.endTime = Math.max(startTime + duration, 0);\n\n    //trim trailing webvtt block whitespaces\n    const text = cue.text.trim();\n\n    // Fix encoding of special characters\n    cue.text = decodeURIComponent(encodeURIComponent(text));\n\n    // If the cue was not assigned an id from the VTT file (line above the content), create one.\n    if (!cue.id) {\n      cue.id = generateCueId(cue.startTime, cue.endTime, text);\n    }\n    if (cue.endTime > 0) {\n      cues.push(cue);\n    }\n  };\n  parser.onparsingerror = function (error) {\n    parsingError = error;\n  };\n  parser.onflush = function () {\n    if (parsingError) {\n      errorCallBack(parsingError);\n      return;\n    }\n    callBack(cues);\n  };\n\n  // Go through contents line by line.\n  vttLines.forEach(line => {\n    if (inHeader) {\n      // Look for X-TIMESTAMP-MAP in header.\n      if (startsWith(line, 'X-TIMESTAMP-MAP=')) {\n        // Once found, no more are allowed anyway, so stop searching.\n        inHeader = false;\n        // Extract LOCAL and MPEGTS.\n        line.slice(16).split(',').forEach(timestamp => {\n          if (startsWith(timestamp, 'LOCAL:')) {\n            cueTime = timestamp.slice(6);\n          } else if (startsWith(timestamp, 'MPEGTS:')) {\n            timestampMapMPEGTS = parseInt(timestamp.slice(7));\n          }\n        });\n        try {\n          // Convert cue time to seconds\n          timestampMapLOCAL = cueString2millis(cueTime) / 1000;\n        } catch (error) {\n          parsingError = error;\n        }\n        // Return without parsing X-TIMESTAMP-MAP line.\n        return;\n      } else if (line === '') {\n        inHeader = false;\n      }\n    }\n    // Parse line by default.\n    parser.parse(line + '\\n');\n  });\n  parser.flush();\n}\n\nconst IMSC1_CODEC = 'stpp.ttml.im1t';\n\n// Time format: h:m:s:frames(.subframes)\nconst HMSF_REGEX = /^(\\d{2,}):(\\d{2}):(\\d{2}):(\\d{2})\\.?(\\d+)?$/;\n\n// Time format: hours, minutes, seconds, milliseconds, frames, ticks\nconst TIME_UNIT_REGEX = /^(\\d*(?:\\.\\d*)?)(h|m|s|ms|f|t)$/;\nconst textAlignToLineAlign = {\n  left: 'start',\n  center: 'center',\n  right: 'end',\n  start: 'start',\n  end: 'end'\n};\nfunction parseIMSC1(payload, initPTS, callBack, errorCallBack) {\n  const results = findBox(new Uint8Array(payload), ['mdat']);\n  if (results.length === 0) {\n    errorCallBack(new Error('Could not parse IMSC1 mdat'));\n    return;\n  }\n  const ttmlList = results.map(mdat => utf8ArrayToStr(mdat));\n  const syncTime = toTimescaleFromScale(initPTS.baseTime, 1, initPTS.timescale);\n  try {\n    ttmlList.forEach(ttml => callBack(parseTTML(ttml, syncTime)));\n  } catch (error) {\n    errorCallBack(error);\n  }\n}\nfunction parseTTML(ttml, syncTime) {\n  const parser = new DOMParser();\n  const xmlDoc = parser.parseFromString(ttml, 'text/xml');\n  const tt = xmlDoc.getElementsByTagName('tt')[0];\n  if (!tt) {\n    throw new Error('Invalid ttml');\n  }\n  const defaultRateInfo = {\n    frameRate: 30,\n    subFrameRate: 1,\n    frameRateMultiplier: 0,\n    tickRate: 0\n  };\n  const rateInfo = Object.keys(defaultRateInfo).reduce((result, key) => {\n    result[key] = tt.getAttribute(`ttp:${key}`) || defaultRateInfo[key];\n    return result;\n  }, {});\n  const trim = tt.getAttribute('xml:space') !== 'preserve';\n  const styleElements = collectionToDictionary(getElementCollection(tt, 'styling', 'style'));\n  const regionElements = collectionToDictionary(getElementCollection(tt, 'layout', 'region'));\n  const cueElements = getElementCollection(tt, 'body', '[begin]');\n  return [].map.call(cueElements, cueElement => {\n    const cueText = getTextContent(cueElement, trim);\n    if (!cueText || !cueElement.hasAttribute('begin')) {\n      return null;\n    }\n    const startTime = parseTtmlTime(cueElement.getAttribute('begin'), rateInfo);\n    const duration = parseTtmlTime(cueElement.getAttribute('dur'), rateInfo);\n    let endTime = parseTtmlTime(cueElement.getAttribute('end'), rateInfo);\n    if (startTime === null) {\n      throw timestampParsingError(cueElement);\n    }\n    if (endTime === null) {\n      if (duration === null) {\n        throw timestampParsingError(cueElement);\n      }\n      endTime = startTime + duration;\n    }\n    const cue = new VTTCue(startTime - syncTime, endTime - syncTime, cueText);\n    cue.id = generateCueId(cue.startTime, cue.endTime, cue.text);\n    const region = regionElements[cueElement.getAttribute('region')];\n    const style = styleElements[cueElement.getAttribute('style')];\n\n    // Apply styles to cue\n    const styles = getTtmlStyles(region, style, styleElements);\n    const {\n      textAlign\n    } = styles;\n    if (textAlign) {\n      // cue.positionAlign not settable in FF~2016\n      const lineAlign = textAlignToLineAlign[textAlign];\n      if (lineAlign) {\n        cue.lineAlign = lineAlign;\n      }\n      cue.align = textAlign;\n    }\n    _extends(cue, styles);\n    return cue;\n  }).filter(cue => cue !== null);\n}\nfunction getElementCollection(fromElement, parentName, childName) {\n  const parent = fromElement.getElementsByTagName(parentName)[0];\n  if (parent) {\n    return [].slice.call(parent.querySelectorAll(childName));\n  }\n  return [];\n}\nfunction collectionToDictionary(elementsWithId) {\n  return elementsWithId.reduce((dict, element) => {\n    const id = element.getAttribute('xml:id');\n    if (id) {\n      dict[id] = element;\n    }\n    return dict;\n  }, {});\n}\nfunction getTextContent(element, trim) {\n  return [].slice.call(element.childNodes).reduce((str, node, i) => {\n    var _node$childNodes;\n    if (node.nodeName === 'br' && i) {\n      return str + '\\n';\n    }\n    if ((_node$childNodes = node.childNodes) != null && _node$childNodes.length) {\n      return getTextContent(node, trim);\n    } else if (trim) {\n      return str + node.textContent.trim().replace(/\\s+/g, ' ');\n    }\n    return str + node.textContent;\n  }, '');\n}\nfunction getTtmlStyles(region, style, styleElements) {\n  const ttsNs = 'http://www.w3.org/ns/ttml#styling';\n  let regionStyle = null;\n  const styleAttributes = ['displayAlign', 'textAlign', 'color', 'backgroundColor', 'fontSize', 'fontFamily'\n  // 'fontWeight',\n  // 'lineHeight',\n  // 'wrapOption',\n  // 'fontStyle',\n  // 'direction',\n  // 'writingMode'\n  ];\n\n  const regionStyleName = region != null && region.hasAttribute('style') ? region.getAttribute('style') : null;\n  if (regionStyleName && styleElements.hasOwnProperty(regionStyleName)) {\n    regionStyle = styleElements[regionStyleName];\n  }\n  return styleAttributes.reduce((styles, name) => {\n    const value = getAttributeNS(style, ttsNs, name) || getAttributeNS(region, ttsNs, name) || getAttributeNS(regionStyle, ttsNs, name);\n    if (value) {\n      styles[name] = value;\n    }\n    return styles;\n  }, {});\n}\nfunction getAttributeNS(element, ns, name) {\n  if (!element) {\n    return null;\n  }\n  return element.hasAttributeNS(ns, name) ? element.getAttributeNS(ns, name) : null;\n}\nfunction timestampParsingError(node) {\n  return new Error(`Could not parse ttml timestamp ${node}`);\n}\nfunction parseTtmlTime(timeAttributeValue, rateInfo) {\n  if (!timeAttributeValue) {\n    return null;\n  }\n  let seconds = parseTimeStamp(timeAttributeValue);\n  if (seconds === null) {\n    if (HMSF_REGEX.test(timeAttributeValue)) {\n      seconds = parseHoursMinutesSecondsFrames(timeAttributeValue, rateInfo);\n    } else if (TIME_UNIT_REGEX.test(timeAttributeValue)) {\n      seconds = parseTimeUnits(timeAttributeValue, rateInfo);\n    }\n  }\n  return seconds;\n}\nfunction parseHoursMinutesSecondsFrames(timeAttributeValue, rateInfo) {\n  const m = HMSF_REGEX.exec(timeAttributeValue);\n  const frames = (m[4] | 0) + (m[5] | 0) / rateInfo.subFrameRate;\n  return (m[1] | 0) * 3600 + (m[2] | 0) * 60 + (m[3] | 0) + frames / rateInfo.frameRate;\n}\nfunction parseTimeUnits(timeAttributeValue, rateInfo) {\n  const m = TIME_UNIT_REGEX.exec(timeAttributeValue);\n  const value = Number(m[1]);\n  const unit = m[2];\n  switch (unit) {\n    case 'h':\n      return value * 3600;\n    case 'm':\n      return value * 60;\n    case 'ms':\n      return value * 1000;\n    case 'f':\n      return value / rateInfo.frameRate;\n    case 't':\n      return value / rateInfo.tickRate;\n  }\n  return value;\n}\n\nclass TimelineController {\n  constructor(hls) {\n    this.hls = void 0;\n    this.media = null;\n    this.config = void 0;\n    this.enabled = true;\n    this.Cues = void 0;\n    this.textTracks = [];\n    this.tracks = [];\n    this.initPTS = [];\n    this.unparsedVttFrags = [];\n    this.captionsTracks = {};\n    this.nonNativeCaptionsTracks = {};\n    this.cea608Parser1 = void 0;\n    this.cea608Parser2 = void 0;\n    this.lastSn = -1;\n    this.lastPartIndex = -1;\n    this.prevCC = -1;\n    this.vttCCs = newVTTCCs();\n    this.captionsProperties = void 0;\n    this.hls = hls;\n    this.config = hls.config;\n    this.Cues = hls.config.cueHandler;\n    this.captionsProperties = {\n      textTrack1: {\n        label: this.config.captionsTextTrack1Label,\n        languageCode: this.config.captionsTextTrack1LanguageCode\n      },\n      textTrack2: {\n        label: this.config.captionsTextTrack2Label,\n        languageCode: this.config.captionsTextTrack2LanguageCode\n      },\n      textTrack3: {\n        label: this.config.captionsTextTrack3Label,\n        languageCode: this.config.captionsTextTrack3LanguageCode\n      },\n      textTrack4: {\n        label: this.config.captionsTextTrack4Label,\n        languageCode: this.config.captionsTextTrack4LanguageCode\n      }\n    };\n    if (this.config.enableCEA708Captions) {\n      const channel1 = new OutputFilter(this, 'textTrack1');\n      const channel2 = new OutputFilter(this, 'textTrack2');\n      const channel3 = new OutputFilter(this, 'textTrack3');\n      const channel4 = new OutputFilter(this, 'textTrack4');\n      this.cea608Parser1 = new Cea608Parser(1, channel1, channel2);\n      this.cea608Parser2 = new Cea608Parser(3, channel3, channel4);\n    }\n    hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);\n    hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);\n    hls.on(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);\n    hls.on(Events.FRAG_LOADING, this.onFragLoading, this);\n    hls.on(Events.FRAG_LOADED, this.onFragLoaded, this);\n    hls.on(Events.FRAG_PARSING_USERDATA, this.onFragParsingUserdata, this);\n    hls.on(Events.FRAG_DECRYPTED, this.onFragDecrypted, this);\n    hls.on(Events.INIT_PTS_FOUND, this.onInitPtsFound, this);\n    hls.on(Events.SUBTITLE_TRACKS_CLEARED, this.onSubtitleTracksCleared, this);\n    hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);\n  }\n  destroy() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);\n    hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);\n    hls.off(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);\n    hls.off(Events.FRAG_LOADING, this.onFragLoading, this);\n    hls.off(Events.FRAG_LOADED, this.onFragLoaded, this);\n    hls.off(Events.FRAG_PARSING_USERDATA, this.onFragParsingUserdata, this);\n    hls.off(Events.FRAG_DECRYPTED, this.onFragDecrypted, this);\n    hls.off(Events.INIT_PTS_FOUND, this.onInitPtsFound, this);\n    hls.off(Events.SUBTITLE_TRACKS_CLEARED, this.onSubtitleTracksCleared, this);\n    hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);\n    // @ts-ignore\n    this.hls = this.config = this.cea608Parser1 = this.cea608Parser2 = null;\n  }\n  addCues(trackName, startTime, endTime, screen, cueRanges) {\n    // skip cues which overlap more than 50% with previously parsed time ranges\n    let merged = false;\n    for (let i = cueRanges.length; i--;) {\n      const cueRange = cueRanges[i];\n      const overlap = intersection(cueRange[0], cueRange[1], startTime, endTime);\n      if (overlap >= 0) {\n        cueRange[0] = Math.min(cueRange[0], startTime);\n        cueRange[1] = Math.max(cueRange[1], endTime);\n        merged = true;\n        if (overlap / (endTime - startTime) > 0.5) {\n          return;\n        }\n      }\n    }\n    if (!merged) {\n      cueRanges.push([startTime, endTime]);\n    }\n    if (this.config.renderTextTracksNatively) {\n      const track = this.captionsTracks[trackName];\n      this.Cues.newCue(track, startTime, endTime, screen);\n    } else {\n      const cues = this.Cues.newCue(null, startTime, endTime, screen);\n      this.hls.trigger(Events.CUES_PARSED, {\n        type: 'captions',\n        cues,\n        track: trackName\n      });\n    }\n  }\n\n  // Triggered when an initial PTS is found; used for synchronisation of WebVTT.\n  onInitPtsFound(event, {\n    frag,\n    id,\n    initPTS,\n    timescale\n  }) {\n    const {\n      unparsedVttFrags\n    } = this;\n    if (id === 'main') {\n      this.initPTS[frag.cc] = {\n        baseTime: initPTS,\n        timescale\n      };\n    }\n\n    // Due to asynchronous processing, initial PTS may arrive later than the first VTT fragments are loaded.\n    // Parse any unparsed fragments upon receiving the initial PTS.\n    if (unparsedVttFrags.length) {\n      this.unparsedVttFrags = [];\n      unparsedVttFrags.forEach(frag => {\n        this.onFragLoaded(Events.FRAG_LOADED, frag);\n      });\n    }\n  }\n  getExistingTrack(trackName) {\n    const {\n      media\n    } = this;\n    if (media) {\n      for (let i = 0; i < media.textTracks.length; i++) {\n        const textTrack = media.textTracks[i];\n        if (textTrack[trackName]) {\n          return textTrack;\n        }\n      }\n    }\n    return null;\n  }\n  createCaptionsTrack(trackName) {\n    if (this.config.renderTextTracksNatively) {\n      this.createNativeTrack(trackName);\n    } else {\n      this.createNonNativeTrack(trackName);\n    }\n  }\n  createNativeTrack(trackName) {\n    if (this.captionsTracks[trackName]) {\n      return;\n    }\n    const {\n      captionsProperties,\n      captionsTracks,\n      media\n    } = this;\n    const {\n      label,\n      languageCode\n    } = captionsProperties[trackName];\n    // Enable reuse of existing text track.\n    const existingTrack = this.getExistingTrack(trackName);\n    if (!existingTrack) {\n      const textTrack = this.createTextTrack('captions', label, languageCode);\n      if (textTrack) {\n        // Set a special property on the track so we know it's managed by Hls.js\n        textTrack[trackName] = true;\n        captionsTracks[trackName] = textTrack;\n      }\n    } else {\n      captionsTracks[trackName] = existingTrack;\n      clearCurrentCues(captionsTracks[trackName]);\n      sendAddTrackEvent(captionsTracks[trackName], media);\n    }\n  }\n  createNonNativeTrack(trackName) {\n    if (this.nonNativeCaptionsTracks[trackName]) {\n      return;\n    }\n    // Create a list of a single track for the provider to consume\n    const trackProperties = this.captionsProperties[trackName];\n    if (!trackProperties) {\n      return;\n    }\n    const label = trackProperties.label;\n    const track = {\n      _id: trackName,\n      label,\n      kind: 'captions',\n      default: trackProperties.media ? !!trackProperties.media.default : false,\n      closedCaptions: trackProperties.media\n    };\n    this.nonNativeCaptionsTracks[trackName] = track;\n    this.hls.trigger(Events.NON_NATIVE_TEXT_TRACKS_FOUND, {\n      tracks: [track]\n    });\n  }\n  createTextTrack(kind, label, lang) {\n    const media = this.media;\n    if (!media) {\n      return;\n    }\n    return media.addTextTrack(kind, label, lang);\n  }\n  onMediaAttaching(event, data) {\n    this.media = data.media;\n    this._cleanTracks();\n  }\n  onMediaDetaching() {\n    const {\n      captionsTracks\n    } = this;\n    Object.keys(captionsTracks).forEach(trackName => {\n      clearCurrentCues(captionsTracks[trackName]);\n      delete captionsTracks[trackName];\n    });\n    this.nonNativeCaptionsTracks = {};\n  }\n  onManifestLoading() {\n    this.lastSn = -1; // Detect discontinuity in fragment parsing\n    this.lastPartIndex = -1;\n    this.prevCC = -1;\n    this.vttCCs = newVTTCCs(); // Detect discontinuity in subtitle manifests\n    this._cleanTracks();\n    this.tracks = [];\n    this.captionsTracks = {};\n    this.nonNativeCaptionsTracks = {};\n    this.textTracks = [];\n    this.unparsedVttFrags = [];\n    this.initPTS = [];\n    if (this.cea608Parser1 && this.cea608Parser2) {\n      this.cea608Parser1.reset();\n      this.cea608Parser2.reset();\n    }\n  }\n  _cleanTracks() {\n    // clear outdated subtitles\n    const {\n      media\n    } = this;\n    if (!media) {\n      return;\n    }\n    const textTracks = media.textTracks;\n    if (textTracks) {\n      for (let i = 0; i < textTracks.length; i++) {\n        clearCurrentCues(textTracks[i]);\n      }\n    }\n  }\n  onSubtitleTracksUpdated(event, data) {\n    const tracks = data.subtitleTracks || [];\n    const hasIMSC1 = tracks.some(track => track.textCodec === IMSC1_CODEC);\n    if (this.config.enableWebVTT || hasIMSC1 && this.config.enableIMSC1) {\n      const listIsIdentical = subtitleOptionsIdentical(this.tracks, tracks);\n      if (listIsIdentical) {\n        this.tracks = tracks;\n        return;\n      }\n      this.textTracks = [];\n      this.tracks = tracks;\n      if (this.config.renderTextTracksNatively) {\n        const inUseTracks = this.media ? this.media.textTracks : null;\n        this.tracks.forEach((track, index) => {\n          let textTrack;\n          if (inUseTracks && index < inUseTracks.length) {\n            let inUseTrack = null;\n            for (let i = 0; i < inUseTracks.length; i++) {\n              if (canReuseVttTextTrack(inUseTracks[i], track)) {\n                inUseTrack = inUseTracks[i];\n                break;\n              }\n            }\n\n            // Reuse tracks with the same label, but do not reuse 608/708 tracks\n            if (inUseTrack) {\n              textTrack = inUseTrack;\n            }\n          }\n          if (textTrack) {\n            clearCurrentCues(textTrack);\n          } else {\n            const textTrackKind = this._captionsOrSubtitlesFromCharacteristics(track);\n            textTrack = this.createTextTrack(textTrackKind, track.name, track.lang);\n            if (textTrack) {\n              textTrack.mode = 'disabled';\n            }\n          }\n          if (textTrack) {\n            textTrack.groupId = track.groupId;\n            this.textTracks.push(textTrack);\n          }\n        });\n      } else if (this.tracks.length) {\n        // Create a list of tracks for the provider to consume\n        const tracksList = this.tracks.map(track => {\n          return {\n            label: track.name,\n            kind: track.type.toLowerCase(),\n            default: track.default,\n            subtitleTrack: track\n          };\n        });\n        this.hls.trigger(Events.NON_NATIVE_TEXT_TRACKS_FOUND, {\n          tracks: tracksList\n        });\n      }\n    }\n  }\n  _captionsOrSubtitlesFromCharacteristics(track) {\n    if (track.attrs.CHARACTERISTICS) {\n      const transcribesSpokenDialog = /transcribes-spoken-dialog/gi.test(track.attrs.CHARACTERISTICS);\n      const describesMusicAndSound = /describes-music-and-sound/gi.test(track.attrs.CHARACTERISTICS);\n      if (transcribesSpokenDialog && describesMusicAndSound) {\n        return 'captions';\n      }\n    }\n    return 'subtitles';\n  }\n  onManifestLoaded(event, data) {\n    if (this.config.enableCEA708Captions && data.captions) {\n      data.captions.forEach(captionsTrack => {\n        const instreamIdMatch = /(?:CC|SERVICE)([1-4])/.exec(captionsTrack.instreamId);\n        if (!instreamIdMatch) {\n          return;\n        }\n        const trackName = `textTrack${instreamIdMatch[1]}`;\n        const trackProperties = this.captionsProperties[trackName];\n        if (!trackProperties) {\n          return;\n        }\n        trackProperties.label = captionsTrack.name;\n        if (captionsTrack.lang) {\n          // optional attribute\n          trackProperties.languageCode = captionsTrack.lang;\n        }\n        trackProperties.media = captionsTrack;\n      });\n    }\n  }\n  closedCaptionsForLevel(frag) {\n    const level = this.hls.levels[frag.level];\n    return level == null ? void 0 : level.attrs['CLOSED-CAPTIONS'];\n  }\n  onFragLoading(event, data) {\n    const {\n      cea608Parser1,\n      cea608Parser2,\n      lastSn,\n      lastPartIndex\n    } = this;\n    if (!this.enabled || !(cea608Parser1 && cea608Parser2)) {\n      return;\n    }\n    // if this frag isn't contiguous, clear the parser so cues with bad start/end times aren't added to the textTrack\n    if (data.frag.type === PlaylistLevelType.MAIN) {\n      var _data$part$index, _data$part;\n      const sn = data.frag.sn;\n      const partIndex = (_data$part$index = data == null ? void 0 : (_data$part = data.part) == null ? void 0 : _data$part.index) != null ? _data$part$index : -1;\n      if (!(sn === lastSn + 1 || sn === lastSn && partIndex === lastPartIndex + 1)) {\n        cea608Parser1.reset();\n        cea608Parser2.reset();\n      }\n      this.lastSn = sn;\n      this.lastPartIndex = partIndex;\n    }\n  }\n  onFragLoaded(event, data) {\n    const {\n      frag,\n      payload\n    } = data;\n    if (frag.type === PlaylistLevelType.SUBTITLE) {\n      // If fragment is subtitle type, parse as WebVTT.\n      if (payload.byteLength) {\n        const decryptData = frag.decryptdata;\n        // fragment after decryption has a stats object\n        const decrypted = ('stats' in data);\n        // If the subtitles are not encrypted, parse VTTs now. Otherwise, we need to wait.\n        if (decryptData == null || !decryptData.encrypted || decrypted) {\n          const trackPlaylistMedia = this.tracks[frag.level];\n          const vttCCs = this.vttCCs;\n          if (!vttCCs[frag.cc]) {\n            vttCCs[frag.cc] = {\n              start: frag.start,\n              prevCC: this.prevCC,\n              new: true\n            };\n            this.prevCC = frag.cc;\n          }\n          if (trackPlaylistMedia && trackPlaylistMedia.textCodec === IMSC1_CODEC) {\n            this._parseIMSC1(frag, payload);\n          } else {\n            this._parseVTTs(data);\n          }\n        }\n      } else {\n        // In case there is no payload, finish unsuccessfully.\n        this.hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {\n          success: false,\n          frag,\n          error: new Error('Empty subtitle payload')\n        });\n      }\n    }\n  }\n  _parseIMSC1(frag, payload) {\n    const hls = this.hls;\n    parseIMSC1(payload, this.initPTS[frag.cc], cues => {\n      this._appendCues(cues, frag.level);\n      hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {\n        success: true,\n        frag: frag\n      });\n    }, error => {\n      logger.log(`Failed to parse IMSC1: ${error}`);\n      hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {\n        success: false,\n        frag: frag,\n        error\n      });\n    });\n  }\n  _parseVTTs(data) {\n    var _frag$initSegment;\n    const {\n      frag,\n      payload\n    } = data;\n    // We need an initial synchronisation PTS. Store fragments as long as none has arrived\n    const {\n      initPTS,\n      unparsedVttFrags\n    } = this;\n    const maxAvCC = initPTS.length - 1;\n    if (!initPTS[frag.cc] && maxAvCC === -1) {\n      unparsedVttFrags.push(data);\n      return;\n    }\n    const hls = this.hls;\n    // Parse the WebVTT file contents.\n    const payloadWebVTT = (_frag$initSegment = frag.initSegment) != null && _frag$initSegment.data ? appendUint8Array(frag.initSegment.data, new Uint8Array(payload)) : payload;\n    parseWebVTT(payloadWebVTT, this.initPTS[frag.cc], this.vttCCs, frag.cc, frag.start, cues => {\n      this._appendCues(cues, frag.level);\n      hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {\n        success: true,\n        frag: frag\n      });\n    }, error => {\n      const missingInitPTS = error.message === 'Missing initPTS for VTT MPEGTS';\n      if (missingInitPTS) {\n        unparsedVttFrags.push(data);\n      } else {\n        this._fallbackToIMSC1(frag, payload);\n      }\n      // Something went wrong while parsing. Trigger event with success false.\n      logger.log(`Failed to parse VTT cue: ${error}`);\n      if (missingInitPTS && maxAvCC > frag.cc) {\n        return;\n      }\n      hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {\n        success: false,\n        frag: frag,\n        error\n      });\n    });\n  }\n  _fallbackToIMSC1(frag, payload) {\n    // If textCodec is unknown, try parsing as IMSC1. Set textCodec based on the result\n    const trackPlaylistMedia = this.tracks[frag.level];\n    if (!trackPlaylistMedia.textCodec) {\n      parseIMSC1(payload, this.initPTS[frag.cc], () => {\n        trackPlaylistMedia.textCodec = IMSC1_CODEC;\n        this._parseIMSC1(frag, payload);\n      }, () => {\n        trackPlaylistMedia.textCodec = 'wvtt';\n      });\n    }\n  }\n  _appendCues(cues, fragLevel) {\n    const hls = this.hls;\n    if (this.config.renderTextTracksNatively) {\n      const textTrack = this.textTracks[fragLevel];\n      // WebVTTParser.parse is an async method and if the currently selected text track mode is set to \"disabled\"\n      // before parsing is done then don't try to access currentTrack.cues.getCueById as cues will be null\n      // and trying to access getCueById method of cues will throw an exception\n      // Because we check if the mode is disabled, we can force check `cues` below. They can't be null.\n      if (!textTrack || textTrack.mode === 'disabled') {\n        return;\n      }\n      cues.forEach(cue => addCueToTrack(textTrack, cue));\n    } else {\n      const currentTrack = this.tracks[fragLevel];\n      if (!currentTrack) {\n        return;\n      }\n      const track = currentTrack.default ? 'default' : 'subtitles' + fragLevel;\n      hls.trigger(Events.CUES_PARSED, {\n        type: 'subtitles',\n        cues,\n        track\n      });\n    }\n  }\n  onFragDecrypted(event, data) {\n    const {\n      frag\n    } = data;\n    if (frag.type === PlaylistLevelType.SUBTITLE) {\n      this.onFragLoaded(Events.FRAG_LOADED, data);\n    }\n  }\n  onSubtitleTracksCleared() {\n    this.tracks = [];\n    this.captionsTracks = {};\n  }\n  onFragParsingUserdata(event, data) {\n    const {\n      cea608Parser1,\n      cea608Parser2\n    } = this;\n    if (!this.enabled || !(cea608Parser1 && cea608Parser2)) {\n      return;\n    }\n    const {\n      frag,\n      samples\n    } = data;\n    if (frag.type === PlaylistLevelType.MAIN && this.closedCaptionsForLevel(frag) === 'NONE') {\n      return;\n    }\n    // If the event contains captions (found in the bytes property), push all bytes into the parser immediately\n    // It will create the proper timestamps based on the PTS value\n    for (let i = 0; i < samples.length; i++) {\n      const ccBytes = samples[i].bytes;\n      if (ccBytes) {\n        const ccdatas = this.extractCea608Data(ccBytes);\n        cea608Parser1.addData(samples[i].pts, ccdatas[0]);\n        cea608Parser2.addData(samples[i].pts, ccdatas[1]);\n      }\n    }\n  }\n  onBufferFlushing(event, {\n    startOffset,\n    endOffset,\n    endOffsetSubtitles,\n    type\n  }) {\n    const {\n      media\n    } = this;\n    if (!media || media.currentTime < endOffset) {\n      return;\n    }\n    // Clear 608 caption cues from the captions TextTracks when the video back buffer is flushed\n    // Forward cues are never removed because we can loose streamed 608 content from recent fragments\n    if (!type || type === 'video') {\n      const {\n        captionsTracks\n      } = this;\n      Object.keys(captionsTracks).forEach(trackName => removeCuesInRange(captionsTracks[trackName], startOffset, endOffset));\n    }\n    if (this.config.renderTextTracksNatively) {\n      // Clear VTT/IMSC1 subtitle cues from the subtitle TextTracks when the back buffer is flushed\n      if (startOffset === 0 && endOffsetSubtitles !== undefined) {\n        const {\n          textTracks\n        } = this;\n        Object.keys(textTracks).forEach(trackName => removeCuesInRange(textTracks[trackName], startOffset, endOffsetSubtitles));\n      }\n    }\n  }\n  extractCea608Data(byteArray) {\n    const actualCCBytes = [[], []];\n    const count = byteArray[0] & 0x1f;\n    let position = 2;\n    for (let j = 0; j < count; j++) {\n      const tmpByte = byteArray[position++];\n      const ccbyte1 = 0x7f & byteArray[position++];\n      const ccbyte2 = 0x7f & byteArray[position++];\n      if (ccbyte1 === 0 && ccbyte2 === 0) {\n        continue;\n      }\n      const ccValid = (0x04 & tmpByte) !== 0; // Support all four channels\n      if (ccValid) {\n        const ccType = 0x03 & tmpByte;\n        if (0x00 /* CEA608 field1*/ === ccType || 0x01 /* CEA608 field2*/ === ccType) {\n          // Exclude CEA708 CC data.\n          actualCCBytes[ccType].push(ccbyte1);\n          actualCCBytes[ccType].push(ccbyte2);\n        }\n      }\n    }\n    return actualCCBytes;\n  }\n}\nfunction canReuseVttTextTrack(inUseTrack, manifestTrack) {\n  return !!inUseTrack && inUseTrack.label === manifestTrack.name && !(inUseTrack.textTrack1 || inUseTrack.textTrack2);\n}\nfunction intersection(x1, x2, y1, y2) {\n  return Math.min(x2, y2) - Math.max(x1, y1);\n}\nfunction newVTTCCs() {\n  return {\n    ccOffset: 0,\n    presentationOffset: 0,\n    0: {\n      start: 0,\n      prevCC: -1,\n      new: true\n    }\n  };\n}\n\n/*\n * cap stream level to media size dimension controller\n */\n\nclass CapLevelController {\n  constructor(hls) {\n    this.hls = void 0;\n    this.autoLevelCapping = void 0;\n    this.firstLevel = void 0;\n    this.media = void 0;\n    this.restrictedLevels = void 0;\n    this.timer = void 0;\n    this.clientRect = void 0;\n    this.streamController = void 0;\n    this.hls = hls;\n    this.autoLevelCapping = Number.POSITIVE_INFINITY;\n    this.firstLevel = -1;\n    this.media = null;\n    this.restrictedLevels = [];\n    this.timer = undefined;\n    this.clientRect = null;\n    this.registerListeners();\n  }\n  setStreamController(streamController) {\n    this.streamController = streamController;\n  }\n  destroy() {\n    this.unregisterListener();\n    if (this.hls.config.capLevelToPlayerSize) {\n      this.stopCapping();\n    }\n    this.media = null;\n    this.clientRect = null;\n    // @ts-ignore\n    this.hls = this.streamController = null;\n  }\n  registerListeners() {\n    const {\n      hls\n    } = this;\n    hls.on(Events.FPS_DROP_LEVEL_CAPPING, this.onFpsDropLevelCapping, this);\n    hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);\n    hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);\n    hls.on(Events.BUFFER_CODECS, this.onBufferCodecs, this);\n    hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n  }\n  unregisterListener() {\n    const {\n      hls\n    } = this;\n    hls.off(Events.FPS_DROP_LEVEL_CAPPING, this.onFpsDropLevelCapping, this);\n    hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);\n    hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);\n    hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this);\n    hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\n  }\n  onFpsDropLevelCapping(event, data) {\n    // Don't add a restricted level more than once\n    const level = this.hls.levels[data.droppedLevel];\n    if (this.isLevelAllowed(level)) {\n      this.restrictedLevels.push({\n        bitrate: level.bitrate,\n        height: level.height,\n        width: level.width\n      });\n    }\n  }\n  onMediaAttaching(event, data) {\n    this.media = data.media instanceof HTMLVideoElement ? data.media : null;\n    this.clientRect = null;\n  }\n  onManifestParsed(event, data) {\n    const hls = this.hls;\n    this.restrictedLevels = [];\n    this.firstLevel = data.firstLevel;\n    if (hls.config.capLevelToPlayerSize && data.video) {\n      // Start capping immediately if the manifest has signaled video codecs\n      this.startCapping();\n    }\n  }\n\n  // Only activate capping when playing a video stream; otherwise, multi-bitrate audio-only streams will be restricted\n  // to the first level\n  onBufferCodecs(event, data) {\n    const hls = this.hls;\n    if (hls.config.capLevelToPlayerSize && data.video) {\n      // If the manifest did not signal a video codec capping has been deferred until we're certain video is present\n      this.startCapping();\n    }\n  }\n  onMediaDetaching() {\n    this.stopCapping();\n  }\n  detectPlayerSize() {\n    if (this.media && this.mediaHeight > 0 && this.mediaWidth > 0) {\n      const levels = this.hls.levels;\n      if (levels.length) {\n        const hls = this.hls;\n        hls.autoLevelCapping = this.getMaxLevel(levels.length - 1);\n        if (hls.autoLevelCapping > this.autoLevelCapping && this.streamController) {\n          // if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch\n          // usually happen when the user go to the fullscreen mode.\n          this.streamController.nextLevelSwitch();\n        }\n        this.autoLevelCapping = hls.autoLevelCapping;\n      }\n    }\n  }\n\n  /*\n   * returns level should be the one with the dimensions equal or greater than the media (player) dimensions (so the video will be downscaled)\n   */\n  getMaxLevel(capLevelIndex) {\n    const levels = this.hls.levels;\n    if (!levels.length) {\n      return -1;\n    }\n    const validLevels = levels.filter((level, index) => this.isLevelAllowed(level) && index <= capLevelIndex);\n    this.clientRect = null;\n    return CapLevelController.getMaxLevelByMediaSize(validLevels, this.mediaWidth, this.mediaHeight);\n  }\n  startCapping() {\n    if (this.timer) {\n      // Don't reset capping if started twice; this can happen if the manifest signals a video codec\n      return;\n    }\n    this.autoLevelCapping = Number.POSITIVE_INFINITY;\n    this.hls.firstLevel = this.getMaxLevel(this.firstLevel);\n    self.clearInterval(this.timer);\n    this.timer = self.setInterval(this.detectPlayerSize.bind(this), 1000);\n    this.detectPlayerSize();\n  }\n  stopCapping() {\n    this.restrictedLevels = [];\n    this.firstLevel = -1;\n    this.autoLevelCapping = Number.POSITIVE_INFINITY;\n    if (this.timer) {\n      self.clearInterval(this.timer);\n      this.timer = undefined;\n    }\n  }\n  getDimensions() {\n    if (this.clientRect) {\n      return this.clientRect;\n    }\n    const media = this.media;\n    const boundsRect = {\n      width: 0,\n      height: 0\n    };\n    if (media) {\n      const clientRect = media.getBoundingClientRect();\n      boundsRect.width = clientRect.width;\n      boundsRect.height = clientRect.height;\n      if (!boundsRect.width && !boundsRect.height) {\n        // When the media element has no width or height (equivalent to not being in the DOM),\n        // then use its width and height attributes (media.width, media.height)\n        boundsRect.width = clientRect.right - clientRect.left || media.width || 0;\n        boundsRect.height = clientRect.bottom - clientRect.top || media.height || 0;\n      }\n    }\n    this.clientRect = boundsRect;\n    return boundsRect;\n  }\n  get mediaWidth() {\n    return this.getDimensions().width * this.contentScaleFactor;\n  }\n  get mediaHeight() {\n    return this.getDimensions().height * this.contentScaleFactor;\n  }\n  get contentScaleFactor() {\n    let pixelRatio = 1;\n    if (!this.hls.config.ignoreDevicePixelRatio) {\n      try {\n        pixelRatio = self.devicePixelRatio;\n      } catch (e) {\n        /* no-op */\n      }\n    }\n    return pixelRatio;\n  }\n  isLevelAllowed(level) {\n    const restrictedLevels = this.restrictedLevels;\n    return !restrictedLevels.some(restrictedLevel => {\n      return level.bitrate === restrictedLevel.bitrate && level.width === restrictedLevel.width && level.height === restrictedLevel.height;\n    });\n  }\n  static getMaxLevelByMediaSize(levels, width, height) {\n    if (!(levels != null && levels.length)) {\n      return -1;\n    }\n\n    // Levels can have the same dimensions but differing bandwidths - since levels are ordered, we can look to the next\n    // to determine whether we've chosen the greatest bandwidth for the media's dimensions\n    const atGreatestBandwidth = (curLevel, nextLevel) => {\n      if (!nextLevel) {\n        return true;\n      }\n      return curLevel.width !== nextLevel.width || curLevel.height !== nextLevel.height;\n    };\n\n    // If we run through the loop without breaking, the media's dimensions are greater than every level, so default to\n    // the max level\n    let maxLevelIndex = levels.length - 1;\n    for (let i = 0; i < levels.length; i += 1) {\n      const level = levels[i];\n      if ((level.width >= width || level.height >= height) && atGreatestBandwidth(level, levels[i + 1])) {\n        maxLevelIndex = i;\n        break;\n      }\n    }\n    return maxLevelIndex;\n  }\n}\n\nclass FPSController {\n  // stream controller must be provided as a dependency!\n\n  constructor(hls) {\n    this.hls = void 0;\n    this.isVideoPlaybackQualityAvailable = false;\n    this.timer = void 0;\n    this.media = null;\n    this.lastTime = void 0;\n    this.lastDroppedFrames = 0;\n    this.lastDecodedFrames = 0;\n    this.streamController = void 0;\n    this.hls = hls;\n    this.registerListeners();\n  }\n  setStreamController(streamController) {\n    this.streamController = streamController;\n  }\n  registerListeners() {\n    this.hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);\n  }\n  unregisterListeners() {\n    this.hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);\n  }\n  destroy() {\n    if (this.timer) {\n      clearInterval(this.timer);\n    }\n    this.unregisterListeners();\n    this.isVideoPlaybackQualityAvailable = false;\n    this.media = null;\n  }\n  onMediaAttaching(event, data) {\n    const config = this.hls.config;\n    if (config.capLevelOnFPSDrop) {\n      const media = data.media instanceof self.HTMLVideoElement ? data.media : null;\n      this.media = media;\n      if (media && typeof media.getVideoPlaybackQuality === 'function') {\n        this.isVideoPlaybackQualityAvailable = true;\n      }\n      self.clearInterval(this.timer);\n      this.timer = self.setInterval(this.checkFPSInterval.bind(this), config.fpsDroppedMonitoringPeriod);\n    }\n  }\n  checkFPS(video, decodedFrames, droppedFrames) {\n    const currentTime = performance.now();\n    if (decodedFrames) {\n      if (this.lastTime) {\n        const currentPeriod = currentTime - this.lastTime;\n        const currentDropped = droppedFrames - this.lastDroppedFrames;\n        const currentDecoded = decodedFrames - this.lastDecodedFrames;\n        const droppedFPS = 1000 * currentDropped / currentPeriod;\n        const hls = this.hls;\n        hls.trigger(Events.FPS_DROP, {\n          currentDropped: currentDropped,\n          currentDecoded: currentDecoded,\n          totalDroppedFrames: droppedFrames\n        });\n        if (droppedFPS > 0) {\n          // logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));\n          if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) {\n            let currentLevel = hls.currentLevel;\n            logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);\n            if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) {\n              currentLevel = currentLevel - 1;\n              hls.trigger(Events.FPS_DROP_LEVEL_CAPPING, {\n                level: currentLevel,\n                droppedLevel: hls.currentLevel\n              });\n              hls.autoLevelCapping = currentLevel;\n              this.streamController.nextLevelSwitch();\n            }\n          }\n        }\n      }\n      this.lastTime = currentTime;\n      this.lastDroppedFrames = droppedFrames;\n      this.lastDecodedFrames = decodedFrames;\n    }\n  }\n  checkFPSInterval() {\n    const video = this.media;\n    if (video) {\n      if (this.isVideoPlaybackQualityAvailable) {\n        const videoPlaybackQuality = video.getVideoPlaybackQuality();\n        this.checkFPS(video, videoPlaybackQuality.totalVideoFrames, videoPlaybackQuality.droppedVideoFrames);\n      } else {\n        // HTMLVideoElement doesn't include the webkit types\n        this.checkFPS(video, video.webkitDecodedFrameCount, video.webkitDroppedFrameCount);\n      }\n    }\n  }\n}\n\nconst LOGGER_PREFIX = '[eme]';\n/**\n * Controller to deal with encrypted media extensions (EME)\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Encrypted_Media_Extensions_API\n *\n * @class\n * @constructor\n */\nclass EMEController {\n  constructor(hls) {\n    this.hls = void 0;\n    this.config = void 0;\n    this.media = null;\n    this.keyFormatPromise = null;\n    this.keySystemAccessPromises = {};\n    this._requestLicenseFailureCount = 0;\n    this.mediaKeySessions = [];\n    this.keyIdToKeySessionPromise = {};\n    this.setMediaKeysQueue = EMEController.CDMCleanupPromise ? [EMEController.CDMCleanupPromise] : [];\n    this.onMediaEncrypted = this._onMediaEncrypted.bind(this);\n    this.onWaitingForKey = this._onWaitingForKey.bind(this);\n    this.debug = logger.debug.bind(logger, LOGGER_PREFIX);\n    this.log = logger.log.bind(logger, LOGGER_PREFIX);\n    this.warn = logger.warn.bind(logger, LOGGER_PREFIX);\n    this.error = logger.error.bind(logger, LOGGER_PREFIX);\n    this.hls = hls;\n    this.config = hls.config;\n    this.registerListeners();\n  }\n  destroy() {\n    this.unregisterListeners();\n    this.onMediaDetached();\n    // Remove any references that could be held in config options or callbacks\n    const config = this.config;\n    config.requestMediaKeySystemAccessFunc = null;\n    config.licenseXhrSetup = config.licenseResponseCallback = undefined;\n    config.drmSystems = config.drmSystemOptions = {};\n    // @ts-ignore\n    this.hls = this.onMediaEncrypted = this.onWaitingForKey = this.keyIdToKeySessionPromise = null;\n    // @ts-ignore\n    this.config = null;\n  }\n  registerListeners() {\n    this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    this.hls.on(Events.MEDIA_DETACHED, this.onMediaDetached, this);\n    this.hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    this.hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);\n  }\n  unregisterListeners() {\n    this.hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    this.hls.off(Events.MEDIA_DETACHED, this.onMediaDetached, this);\n    this.hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    this.hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);\n  }\n  getLicenseServerUrl(keySystem) {\n    const {\n      drmSystems,\n      widevineLicenseUrl\n    } = this.config;\n    const keySystemConfiguration = drmSystems[keySystem];\n    if (keySystemConfiguration) {\n      return keySystemConfiguration.licenseUrl;\n    }\n\n    // For backward compatibility\n    if (keySystem === KeySystems.WIDEVINE && widevineLicenseUrl) {\n      return widevineLicenseUrl;\n    }\n    throw new Error(`no license server URL configured for key-system \"${keySystem}\"`);\n  }\n  getServerCertificateUrl(keySystem) {\n    const {\n      drmSystems\n    } = this.config;\n    const keySystemConfiguration = drmSystems[keySystem];\n    if (keySystemConfiguration) {\n      return keySystemConfiguration.serverCertificateUrl;\n    } else {\n      this.log(`No Server Certificate in config.drmSystems[\"${keySystem}\"]`);\n    }\n  }\n  attemptKeySystemAccess(keySystemsToAttempt) {\n    const levels = this.hls.levels;\n    const uniqueCodec = (value, i, a) => !!value && a.indexOf(value) === i;\n    const audioCodecs = levels.map(level => level.audioCodec).filter(uniqueCodec);\n    const videoCodecs = levels.map(level => level.videoCodec).filter(uniqueCodec);\n    if (audioCodecs.length + videoCodecs.length === 0) {\n      videoCodecs.push('avc1.42e01e');\n    }\n    return new Promise((resolve, reject) => {\n      const attempt = keySystems => {\n        const keySystem = keySystems.shift();\n        this.getMediaKeysPromise(keySystem, audioCodecs, videoCodecs).then(mediaKeys => resolve({\n          keySystem,\n          mediaKeys\n        })).catch(error => {\n          if (keySystems.length) {\n            attempt(keySystems);\n          } else if (error instanceof EMEKeyError) {\n            reject(error);\n          } else {\n            reject(new EMEKeyError({\n              type: ErrorTypes.KEY_SYSTEM_ERROR,\n              details: ErrorDetails.KEY_SYSTEM_NO_ACCESS,\n              error,\n              fatal: true\n            }, error.message));\n          }\n        });\n      };\n      attempt(keySystemsToAttempt);\n    });\n  }\n  requestMediaKeySystemAccess(keySystem, supportedConfigurations) {\n    const {\n      requestMediaKeySystemAccessFunc\n    } = this.config;\n    if (!(typeof requestMediaKeySystemAccessFunc === 'function')) {\n      let errMessage = `Configured requestMediaKeySystemAccess is not a function ${requestMediaKeySystemAccessFunc}`;\n      if (requestMediaKeySystemAccess === null && self.location.protocol === 'http:') {\n        errMessage = `navigator.requestMediaKeySystemAccess is not available over insecure protocol ${location.protocol}`;\n      }\n      return Promise.reject(new Error(errMessage));\n    }\n    return requestMediaKeySystemAccessFunc(keySystem, supportedConfigurations);\n  }\n  getMediaKeysPromise(keySystem, audioCodecs, videoCodecs) {\n    // This can throw, but is caught in event handler callpath\n    const mediaKeySystemConfigs = getSupportedMediaKeySystemConfigurations(keySystem, audioCodecs, videoCodecs, this.config.drmSystemOptions);\n    const keySystemAccessPromises = this.keySystemAccessPromises[keySystem];\n    let keySystemAccess = keySystemAccessPromises == null ? void 0 : keySystemAccessPromises.keySystemAccess;\n    if (!keySystemAccess) {\n      this.log(`Requesting encrypted media \"${keySystem}\" key-system access with config: ${JSON.stringify(mediaKeySystemConfigs)}`);\n      keySystemAccess = this.requestMediaKeySystemAccess(keySystem, mediaKeySystemConfigs);\n      const _keySystemAccessPromises = this.keySystemAccessPromises[keySystem] = {\n        keySystemAccess\n      };\n      keySystemAccess.catch(error => {\n        this.log(`Failed to obtain access to key-system \"${keySystem}\": ${error}`);\n      });\n      return keySystemAccess.then(mediaKeySystemAccess => {\n        this.log(`Access for key-system \"${mediaKeySystemAccess.keySystem}\" obtained`);\n        const certificateRequest = this.fetchServerCertificate(keySystem);\n        this.log(`Create media-keys for \"${keySystem}\"`);\n        _keySystemAccessPromises.mediaKeys = mediaKeySystemAccess.createMediaKeys().then(mediaKeys => {\n          this.log(`Media-keys created for \"${keySystem}\"`);\n          return certificateRequest.then(certificate => {\n            if (certificate) {\n              return this.setMediaKeysServerCertificate(mediaKeys, keySystem, certificate);\n            }\n            return mediaKeys;\n          });\n        });\n        _keySystemAccessPromises.mediaKeys.catch(error => {\n          this.error(`Failed to create media-keys for \"${keySystem}\"}: ${error}`);\n        });\n        return _keySystemAccessPromises.mediaKeys;\n      });\n    }\n    return keySystemAccess.then(() => keySystemAccessPromises.mediaKeys);\n  }\n  createMediaKeySessionContext({\n    decryptdata,\n    keySystem,\n    mediaKeys\n  }) {\n    this.log(`Creating key-system session \"${keySystem}\" keyId: ${Hex.hexDump(decryptdata.keyId || [])}`);\n    const mediaKeysSession = mediaKeys.createSession();\n    const mediaKeySessionContext = {\n      decryptdata,\n      keySystem,\n      mediaKeys,\n      mediaKeysSession,\n      keyStatus: 'status-pending'\n    };\n    this.mediaKeySessions.push(mediaKeySessionContext);\n    return mediaKeySessionContext;\n  }\n  renewKeySession(mediaKeySessionContext) {\n    const decryptdata = mediaKeySessionContext.decryptdata;\n    if (decryptdata.pssh) {\n      const keySessionContext = this.createMediaKeySessionContext(mediaKeySessionContext);\n      const keyId = this.getKeyIdString(decryptdata);\n      const scheme = 'cenc';\n      this.keyIdToKeySessionPromise[keyId] = this.generateRequestWithPreferredKeySession(keySessionContext, scheme, decryptdata.pssh, 'expired');\n    } else {\n      this.warn(`Could not renew expired session. Missing pssh initData.`);\n    }\n    this.removeSession(mediaKeySessionContext);\n  }\n  getKeyIdString(decryptdata) {\n    if (!decryptdata) {\n      throw new Error('Could not read keyId of undefined decryptdata');\n    }\n    if (decryptdata.keyId === null) {\n      throw new Error('keyId is null');\n    }\n    return Hex.hexDump(decryptdata.keyId);\n  }\n  updateKeySession(mediaKeySessionContext, data) {\n    var _mediaKeySessionConte;\n    const keySession = mediaKeySessionContext.mediaKeysSession;\n    this.log(`Updating key-session \"${keySession.sessionId}\" for keyID ${Hex.hexDump(((_mediaKeySessionConte = mediaKeySessionContext.decryptdata) == null ? void 0 : _mediaKeySessionConte.keyId) || [])}\n      } (data length: ${data ? data.byteLength : data})`);\n    return keySession.update(data);\n  }\n  selectKeySystemFormat(frag) {\n    const keyFormats = Object.keys(frag.levelkeys || {});\n    if (!this.keyFormatPromise) {\n      this.log(`Selecting key-system from fragment (sn: ${frag.sn} ${frag.type}: ${frag.level}) key formats ${keyFormats.join(', ')}`);\n      this.keyFormatPromise = this.getKeyFormatPromise(keyFormats);\n    }\n    return this.keyFormatPromise;\n  }\n  getKeyFormatPromise(keyFormats) {\n    return new Promise((resolve, reject) => {\n      const keySystemsInConfig = getKeySystemsForConfig(this.config);\n      const keySystemsToAttempt = keyFormats.map(keySystemFormatToKeySystemDomain).filter(value => !!value && keySystemsInConfig.indexOf(value) !== -1);\n      return this.getKeySystemSelectionPromise(keySystemsToAttempt).then(({\n        keySystem\n      }) => {\n        const keySystemFormat = keySystemDomainToKeySystemFormat(keySystem);\n        if (keySystemFormat) {\n          resolve(keySystemFormat);\n        } else {\n          reject(new Error(`Unable to find format for key-system \"${keySystem}\"`));\n        }\n      }).catch(reject);\n    });\n  }\n  loadKey(data) {\n    const decryptdata = data.keyInfo.decryptdata;\n    const keyId = this.getKeyIdString(decryptdata);\n    const keyDetails = `(keyId: ${keyId} format: \"${decryptdata.keyFormat}\" method: ${decryptdata.method} uri: ${decryptdata.uri})`;\n    this.log(`Starting session for key ${keyDetails}`);\n    let keySessionContextPromise = this.keyIdToKeySessionPromise[keyId];\n    if (!keySessionContextPromise) {\n      keySessionContextPromise = this.keyIdToKeySessionPromise[keyId] = this.getKeySystemForKeyPromise(decryptdata).then(({\n        keySystem,\n        mediaKeys\n      }) => {\n        this.throwIfDestroyed();\n        this.log(`Handle encrypted media sn: ${data.frag.sn} ${data.frag.type}: ${data.frag.level} using key ${keyDetails}`);\n        return this.attemptSetMediaKeys(keySystem, mediaKeys).then(() => {\n          this.throwIfDestroyed();\n          const keySessionContext = this.createMediaKeySessionContext({\n            keySystem,\n            mediaKeys,\n            decryptdata\n          });\n          const scheme = 'cenc';\n          return this.generateRequestWithPreferredKeySession(keySessionContext, scheme, decryptdata.pssh, 'playlist-key');\n        });\n      });\n      keySessionContextPromise.catch(error => this.handleError(error));\n    }\n    return keySessionContextPromise;\n  }\n  throwIfDestroyed(message = 'Invalid state') {\n    if (!this.hls) {\n      throw new Error('invalid state');\n    }\n  }\n  handleError(error) {\n    if (!this.hls) {\n      return;\n    }\n    this.error(error.message);\n    if (error instanceof EMEKeyError) {\n      this.hls.trigger(Events.ERROR, error.data);\n    } else {\n      this.hls.trigger(Events.ERROR, {\n        type: ErrorTypes.KEY_SYSTEM_ERROR,\n        details: ErrorDetails.KEY_SYSTEM_NO_KEYS,\n        error,\n        fatal: true\n      });\n    }\n  }\n  getKeySystemForKeyPromise(decryptdata) {\n    const keyId = this.getKeyIdString(decryptdata);\n    const mediaKeySessionContext = this.keyIdToKeySessionPromise[keyId];\n    if (!mediaKeySessionContext) {\n      const keySystem = keySystemFormatToKeySystemDomain(decryptdata.keyFormat);\n      const keySystemsToAttempt = keySystem ? [keySystem] : getKeySystemsForConfig(this.config);\n      return this.attemptKeySystemAccess(keySystemsToAttempt);\n    }\n    return mediaKeySessionContext;\n  }\n  getKeySystemSelectionPromise(keySystemsToAttempt) {\n    if (!keySystemsToAttempt.length) {\n      keySystemsToAttempt = getKeySystemsForConfig(this.config);\n    }\n    if (keySystemsToAttempt.length === 0) {\n      throw new EMEKeyError({\n        type: ErrorTypes.KEY_SYSTEM_ERROR,\n        details: ErrorDetails.KEY_SYSTEM_NO_CONFIGURED_LICENSE,\n        fatal: true\n      }, `Missing key-system license configuration options ${JSON.stringify({\n        drmSystems: this.config.drmSystems\n      })}`);\n    }\n    return this.attemptKeySystemAccess(keySystemsToAttempt);\n  }\n  _onMediaEncrypted(event) {\n    const {\n      initDataType,\n      initData\n    } = event;\n    this.debug(`\"${event.type}\" event: init data type: \"${initDataType}\"`);\n\n    // Ignore event when initData is null\n    if (initData === null) {\n      return;\n    }\n    let keyId;\n    let keySystemDomain;\n    if (initDataType === 'sinf' && this.config.drmSystems[KeySystems.FAIRPLAY]) {\n      // Match sinf keyId to playlist skd://keyId=\n      const json = bin2str(new Uint8Array(initData));\n      try {\n        const sinf = base64Decode(JSON.parse(json).sinf);\n        const tenc = parseSinf(new Uint8Array(sinf));\n        if (!tenc) {\n          return;\n        }\n        keyId = tenc.subarray(8, 24);\n        keySystemDomain = KeySystems.FAIRPLAY;\n      } catch (error) {\n        this.warn('Failed to parse sinf \"encrypted\" event message initData');\n        return;\n      }\n    } else {\n      // Support clear-lead key-session creation (otherwise depend on playlist keys)\n      const psshInfo = parsePssh(initData);\n      if (psshInfo === null) {\n        return;\n      }\n      if (psshInfo.version === 0 && psshInfo.systemId === KeySystemIds.WIDEVINE && psshInfo.data) {\n        keyId = psshInfo.data.subarray(8, 24);\n      }\n      keySystemDomain = keySystemIdToKeySystemDomain(psshInfo.systemId);\n    }\n    if (!keySystemDomain || !keyId) {\n      return;\n    }\n    const keyIdHex = Hex.hexDump(keyId);\n    const {\n      keyIdToKeySessionPromise,\n      mediaKeySessions\n    } = this;\n    let keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex];\n    for (let i = 0; i < mediaKeySessions.length; i++) {\n      // Match playlist key\n      const keyContext = mediaKeySessions[i];\n      const decryptdata = keyContext.decryptdata;\n      if (decryptdata.pssh || !decryptdata.keyId) {\n        continue;\n      }\n      const oldKeyIdHex = Hex.hexDump(decryptdata.keyId);\n      if (keyIdHex === oldKeyIdHex || decryptdata.uri.replace(/-/g, '').indexOf(keyIdHex) !== -1) {\n        keySessionContextPromise = keyIdToKeySessionPromise[oldKeyIdHex];\n        delete keyIdToKeySessionPromise[oldKeyIdHex];\n        decryptdata.pssh = new Uint8Array(initData);\n        decryptdata.keyId = keyId;\n        keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] = keySessionContextPromise.then(() => {\n          return this.generateRequestWithPreferredKeySession(keyContext, initDataType, initData, 'encrypted-event-key-match');\n        });\n        break;\n      }\n    }\n    if (!keySessionContextPromise) {\n      // Clear-lead key (not encountered in playlist)\n      keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] = this.getKeySystemSelectionPromise([keySystemDomain]).then(({\n        keySystem,\n        mediaKeys\n      }) => {\n        var _keySystemToKeySystem;\n        this.throwIfDestroyed();\n        const decryptdata = new LevelKey('ISO-23001-7', keyIdHex, (_keySystemToKeySystem = keySystemDomainToKeySystemFormat(keySystem)) != null ? _keySystemToKeySystem : '');\n        decryptdata.pssh = new Uint8Array(initData);\n        decryptdata.keyId = keyId;\n        return this.attemptSetMediaKeys(keySystem, mediaKeys).then(() => {\n          this.throwIfDestroyed();\n          const keySessionContext = this.createMediaKeySessionContext({\n            decryptdata,\n            keySystem,\n            mediaKeys\n          });\n          return this.generateRequestWithPreferredKeySession(keySessionContext, initDataType, initData, 'encrypted-event-no-match');\n        });\n      });\n    }\n    keySessionContextPromise.catch(error => this.handleError(error));\n  }\n  _onWaitingForKey(event) {\n    this.log(`\"${event.type}\" event`);\n  }\n  attemptSetMediaKeys(keySystem, mediaKeys) {\n    const queue = this.setMediaKeysQueue.slice();\n    this.log(`Setting media-keys for \"${keySystem}\"`);\n    // Only one setMediaKeys() can run at one time, and multiple setMediaKeys() operations\n    // can be queued for execution for multiple key sessions.\n    const setMediaKeysPromise = Promise.all(queue).then(() => {\n      if (!this.media) {\n        throw new Error('Attempted to set mediaKeys without media element attached');\n      }\n      return this.media.setMediaKeys(mediaKeys);\n    });\n    this.setMediaKeysQueue.push(setMediaKeysPromise);\n    return setMediaKeysPromise.then(() => {\n      this.log(`Media-keys set for \"${keySystem}\"`);\n      queue.push(setMediaKeysPromise);\n      this.setMediaKeysQueue = this.setMediaKeysQueue.filter(p => queue.indexOf(p) === -1);\n    });\n  }\n  generateRequestWithPreferredKeySession(context, initDataType, initData, reason) {\n    var _this$config$drmSyste, _this$config$drmSyste2;\n    const generateRequestFilter = (_this$config$drmSyste = this.config.drmSystems) == null ? void 0 : (_this$config$drmSyste2 = _this$config$drmSyste[context.keySystem]) == null ? void 0 : _this$config$drmSyste2.generateRequest;\n    if (generateRequestFilter) {\n      try {\n        const mappedInitData = generateRequestFilter.call(this.hls, initDataType, initData, context);\n        if (!mappedInitData) {\n          throw new Error('Invalid response from configured generateRequest filter');\n        }\n        initDataType = mappedInitData.initDataType;\n        initData = context.decryptdata.pssh = mappedInitData.initData ? new Uint8Array(mappedInitData.initData) : null;\n      } catch (error) {\n        var _this$hls;\n        this.warn(error.message);\n        if ((_this$hls = this.hls) != null && _this$hls.config.debug) {\n          throw error;\n        }\n      }\n    }\n    if (initData === null) {\n      this.log(`Skipping key-session request for \"${reason}\" (no initData)`);\n      return Promise.resolve(context);\n    }\n    const keyId = this.getKeyIdString(context.decryptdata);\n    this.log(`Generating key-session request for \"${reason}\": ${keyId} (init data type: ${initDataType} length: ${initData ? initData.byteLength : null})`);\n    const licenseStatus = new EventEmitter();\n    context.mediaKeysSession.onmessage = event => {\n      const keySession = context.mediaKeysSession;\n      if (!keySession) {\n        licenseStatus.emit('error', new Error('invalid state'));\n        return;\n      }\n      const {\n        messageType,\n        message\n      } = event;\n      this.log(`\"${messageType}\" message event for session \"${keySession.sessionId}\" message size: ${message.byteLength}`);\n      if (messageType === 'license-request' || messageType === 'license-renewal') {\n        this.renewLicense(context, message).catch(error => {\n          this.handleError(error);\n          licenseStatus.emit('error', error);\n        });\n      } else if (messageType === 'license-release') {\n        if (context.keySystem === KeySystems.FAIRPLAY) {\n          this.updateKeySession(context, strToUtf8array('acknowledged'));\n          this.removeSession(context);\n        }\n      } else {\n        this.warn(`unhandled media key message type \"${messageType}\"`);\n      }\n    };\n    context.mediaKeysSession.onkeystatuseschange = event => {\n      const keySession = context.mediaKeysSession;\n      if (!keySession) {\n        licenseStatus.emit('error', new Error('invalid state'));\n        return;\n      }\n      this.onKeyStatusChange(context);\n      const keyStatus = context.keyStatus;\n      licenseStatus.emit('keyStatus', keyStatus);\n      if (keyStatus === 'expired') {\n        this.warn(`${context.keySystem} expired for key ${keyId}`);\n        this.renewKeySession(context);\n      }\n    };\n    const keyUsablePromise = new Promise((resolve, reject) => {\n      licenseStatus.on('error', reject);\n      licenseStatus.on('keyStatus', keyStatus => {\n        if (keyStatus.startsWith('usable')) {\n          resolve();\n        } else if (keyStatus === 'output-restricted') {\n          reject(new EMEKeyError({\n            type: ErrorTypes.KEY_SYSTEM_ERROR,\n            details: ErrorDetails.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED,\n            fatal: false\n          }, 'HDCP level output restricted'));\n        } else if (keyStatus === 'internal-error') {\n          reject(new EMEKeyError({\n            type: ErrorTypes.KEY_SYSTEM_ERROR,\n            details: ErrorDetails.KEY_SYSTEM_STATUS_INTERNAL_ERROR,\n            fatal: true\n          }, `key status changed to \"${keyStatus}\"`));\n        } else if (keyStatus === 'expired') {\n          reject(new Error('key expired while generating request'));\n        } else {\n          this.warn(`unhandled key status change \"${keyStatus}\"`);\n        }\n      });\n    });\n    return context.mediaKeysSession.generateRequest(initDataType, initData).then(() => {\n      var _context$mediaKeysSes;\n      this.log(`Request generated for key-session \"${(_context$mediaKeysSes = context.mediaKeysSession) == null ? void 0 : _context$mediaKeysSes.sessionId}\" keyId: ${keyId}`);\n    }).catch(error => {\n      throw new EMEKeyError({\n        type: ErrorTypes.KEY_SYSTEM_ERROR,\n        details: ErrorDetails.KEY_SYSTEM_NO_SESSION,\n        error,\n        fatal: false\n      }, `Error generating key-session request: ${error}`);\n    }).then(() => keyUsablePromise).catch(error => {\n      licenseStatus.removeAllListeners();\n      this.removeSession(context);\n      throw error;\n    }).then(() => {\n      licenseStatus.removeAllListeners();\n      return context;\n    });\n  }\n  onKeyStatusChange(mediaKeySessionContext) {\n    mediaKeySessionContext.mediaKeysSession.keyStatuses.forEach((status, keyId) => {\n      this.log(`key status change \"${status}\" for keyStatuses keyId: ${Hex.hexDump('buffer' in keyId ? new Uint8Array(keyId.buffer, keyId.byteOffset, keyId.byteLength) : new Uint8Array(keyId))} session keyId: ${Hex.hexDump(new Uint8Array(mediaKeySessionContext.decryptdata.keyId || []))} uri: ${mediaKeySessionContext.decryptdata.uri}`);\n      mediaKeySessionContext.keyStatus = status;\n    });\n  }\n  fetchServerCertificate(keySystem) {\n    const config = this.config;\n    const Loader = config.loader;\n    const certLoader = new Loader(config);\n    const url = this.getServerCertificateUrl(keySystem);\n    if (!url) {\n      return Promise.resolve();\n    }\n    this.log(`Fetching serverCertificate for \"${keySystem}\"`);\n    return new Promise((resolve, reject) => {\n      const loaderContext = {\n        responseType: 'arraybuffer',\n        url\n      };\n      const loadPolicy = config.certLoadPolicy.default;\n      const loaderConfig = {\n        loadPolicy,\n        timeout: loadPolicy.maxLoadTimeMs,\n        maxRetry: 0,\n        retryDelay: 0,\n        maxRetryDelay: 0\n      };\n      const loaderCallbacks = {\n        onSuccess: (response, stats, context, networkDetails) => {\n          resolve(response.data);\n        },\n        onError: (response, contex, networkDetails, stats) => {\n          reject(new EMEKeyError({\n            type: ErrorTypes.KEY_SYSTEM_ERROR,\n            details: ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED,\n            fatal: true,\n            networkDetails,\n            response: _objectSpread2({\n              url: loaderContext.url,\n              data: undefined\n            }, response)\n          }, `\"${keySystem}\" certificate request failed (${url}). Status: ${response.code} (${response.text})`));\n        },\n        onTimeout: (stats, context, networkDetails) => {\n          reject(new EMEKeyError({\n            type: ErrorTypes.KEY_SYSTEM_ERROR,\n            details: ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED,\n            fatal: true,\n            networkDetails,\n            response: {\n              url: loaderContext.url,\n              data: undefined\n            }\n          }, `\"${keySystem}\" certificate request timed out (${url})`));\n        },\n        onAbort: (stats, context, networkDetails) => {\n          reject(new Error('aborted'));\n        }\n      };\n      certLoader.load(loaderContext, loaderConfig, loaderCallbacks);\n    });\n  }\n  setMediaKeysServerCertificate(mediaKeys, keySystem, cert) {\n    return new Promise((resolve, reject) => {\n      mediaKeys.setServerCertificate(cert).then(success => {\n        this.log(`setServerCertificate ${success ? 'success' : 'not supported by CDM'} (${cert == null ? void 0 : cert.byteLength}) on \"${keySystem}\"`);\n        resolve(mediaKeys);\n      }).catch(error => {\n        reject(new EMEKeyError({\n          type: ErrorTypes.KEY_SYSTEM_ERROR,\n          details: ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED,\n          error,\n          fatal: true\n        }, error.message));\n      });\n    });\n  }\n  renewLicense(context, keyMessage) {\n    return this.requestLicense(context, new Uint8Array(keyMessage)).then(data => {\n      return this.updateKeySession(context, new Uint8Array(data)).catch(error => {\n        throw new EMEKeyError({\n          type: ErrorTypes.KEY_SYSTEM_ERROR,\n          details: ErrorDetails.KEY_SYSTEM_SESSION_UPDATE_FAILED,\n          error,\n          fatal: true\n        }, error.message);\n      });\n    });\n  }\n  setupLicenseXHR(xhr, url, keysListItem, licenseChallenge) {\n    const licenseXhrSetup = this.config.licenseXhrSetup;\n    if (!licenseXhrSetup) {\n      xhr.open('POST', url, true);\n      return Promise.resolve({\n        xhr,\n        licenseChallenge\n      });\n    }\n    return Promise.resolve().then(() => {\n      if (!keysListItem.decryptdata) {\n        throw new Error('Key removed');\n      }\n      return licenseXhrSetup.call(this.hls, xhr, url, keysListItem, licenseChallenge);\n    }).catch(error => {\n      if (!keysListItem.decryptdata) {\n        // Key session removed. Cancel license request.\n        throw error;\n      }\n      // let's try to open before running setup\n      xhr.open('POST', url, true);\n      return licenseXhrSetup.call(this.hls, xhr, url, keysListItem, licenseChallenge);\n    }).then(licenseXhrSetupResult => {\n      // if licenseXhrSetup did not yet call open, let's do it now\n      if (!xhr.readyState) {\n        xhr.open('POST', url, true);\n      }\n      const finalLicenseChallenge = licenseXhrSetupResult ? licenseXhrSetupResult : licenseChallenge;\n      return {\n        xhr,\n        licenseChallenge: finalLicenseChallenge\n      };\n    });\n  }\n  requestLicense(keySessionContext, licenseChallenge) {\n    const keyLoadPolicy = this.config.keyLoadPolicy.default;\n    return new Promise((resolve, reject) => {\n      const url = this.getLicenseServerUrl(keySessionContext.keySystem);\n      this.log(`Sending license request to URL: ${url}`);\n      const xhr = new XMLHttpRequest();\n      xhr.responseType = 'arraybuffer';\n      xhr.onreadystatechange = () => {\n        if (!this.hls || !keySessionContext.mediaKeysSession) {\n          return reject(new Error('invalid state'));\n        }\n        if (xhr.readyState === 4) {\n          if (xhr.status === 200) {\n            this._requestLicenseFailureCount = 0;\n            let data = xhr.response;\n            this.log(`License received ${data instanceof ArrayBuffer ? data.byteLength : data}`);\n            const licenseResponseCallback = this.config.licenseResponseCallback;\n            if (licenseResponseCallback) {\n              try {\n                data = licenseResponseCallback.call(this.hls, xhr, url, keySessionContext);\n              } catch (error) {\n                this.error(error);\n              }\n            }\n            resolve(data);\n          } else {\n            const retryConfig = keyLoadPolicy.errorRetry;\n            const maxNumRetry = retryConfig ? retryConfig.maxNumRetry : 0;\n            this._requestLicenseFailureCount++;\n            if (this._requestLicenseFailureCount > maxNumRetry || xhr.status >= 400 && xhr.status < 500) {\n              reject(new EMEKeyError({\n                type: ErrorTypes.KEY_SYSTEM_ERROR,\n                details: ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED,\n                fatal: true,\n                networkDetails: xhr,\n                response: {\n                  url,\n                  data: undefined,\n                  code: xhr.status,\n                  text: xhr.statusText\n                }\n              }, `License Request XHR failed (${url}). Status: ${xhr.status} (${xhr.statusText})`));\n            } else {\n              const attemptsLeft = maxNumRetry - this._requestLicenseFailureCount + 1;\n              this.warn(`Retrying license request, ${attemptsLeft} attempts left`);\n              this.requestLicense(keySessionContext, licenseChallenge).then(resolve, reject);\n            }\n          }\n        }\n      };\n      if (keySessionContext.licenseXhr && keySessionContext.licenseXhr.readyState !== XMLHttpRequest.DONE) {\n        keySessionContext.licenseXhr.abort();\n      }\n      keySessionContext.licenseXhr = xhr;\n      this.setupLicenseXHR(xhr, url, keySessionContext, licenseChallenge).then(({\n        xhr,\n        licenseChallenge\n      }) => {\n        xhr.send(licenseChallenge);\n      });\n    });\n  }\n  onMediaAttached(event, data) {\n    if (!this.config.emeEnabled) {\n      return;\n    }\n    const media = data.media;\n\n    // keep reference of media\n    this.media = media;\n    media.addEventListener('encrypted', this.onMediaEncrypted);\n    media.addEventListener('waitingforkey', this.onWaitingForKey);\n  }\n  onMediaDetached() {\n    const media = this.media;\n    const mediaKeysList = this.mediaKeySessions;\n    if (media) {\n      media.removeEventListener('encrypted', this.onMediaEncrypted);\n      media.removeEventListener('waitingforkey', this.onWaitingForKey);\n      this.media = null;\n    }\n    this._requestLicenseFailureCount = 0;\n    this.setMediaKeysQueue = [];\n    this.mediaKeySessions = [];\n    this.keyIdToKeySessionPromise = {};\n    LevelKey.clearKeyUriToKeyIdMap();\n\n    // Close all sessions and remove media keys from the video element.\n    const keySessionCount = mediaKeysList.length;\n    EMEController.CDMCleanupPromise = Promise.all(mediaKeysList.map(mediaKeySessionContext => this.removeSession(mediaKeySessionContext)).concat(media == null ? void 0 : media.setMediaKeys(null).catch(error => {\n      this.log(`Could not clear media keys: ${error}. media.src: ${media == null ? void 0 : media.src}`);\n    }))).then(() => {\n      if (keySessionCount) {\n        this.log('finished closing key sessions and clearing media keys');\n        mediaKeysList.length = 0;\n      }\n    }).catch(error => {\n      this.log(`Could not close sessions and clear media keys: ${error}. media.src: ${media == null ? void 0 : media.src}`);\n    });\n  }\n  onManifestLoading() {\n    this.keyFormatPromise = null;\n  }\n  onManifestLoaded(event, {\n    sessionKeys\n  }) {\n    if (!sessionKeys || !this.config.emeEnabled) {\n      return;\n    }\n    if (!this.keyFormatPromise) {\n      const keyFormats = sessionKeys.reduce((formats, sessionKey) => {\n        if (formats.indexOf(sessionKey.keyFormat) === -1) {\n          formats.push(sessionKey.keyFormat);\n        }\n        return formats;\n      }, []);\n      this.log(`Selecting key-system from session-keys ${keyFormats.join(', ')}`);\n      this.keyFormatPromise = this.getKeyFormatPromise(keyFormats);\n    }\n  }\n  removeSession(mediaKeySessionContext) {\n    const {\n      mediaKeysSession,\n      licenseXhr\n    } = mediaKeySessionContext;\n    if (mediaKeysSession) {\n      this.log(`Remove licenses and keys and close session ${mediaKeysSession.sessionId}`);\n      mediaKeysSession.onmessage = null;\n      mediaKeysSession.onkeystatuseschange = null;\n      if (licenseXhr && licenseXhr.readyState !== XMLHttpRequest.DONE) {\n        licenseXhr.abort();\n      }\n      mediaKeySessionContext.mediaKeysSession = mediaKeySessionContext.decryptdata = mediaKeySessionContext.licenseXhr = undefined;\n      const index = this.mediaKeySessions.indexOf(mediaKeySessionContext);\n      if (index > -1) {\n        this.mediaKeySessions.splice(index, 1);\n      }\n      return mediaKeysSession.remove().catch(error => {\n        this.log(`Could not remove session: ${error}`);\n      }).then(() => {\n        return mediaKeysSession.close();\n      }).catch(error => {\n        this.log(`Could not close session: ${error}`);\n      });\n    }\n  }\n}\nEMEController.CDMCleanupPromise = void 0;\nclass EMEKeyError extends Error {\n  constructor(data, message) {\n    super(message);\n    this.data = void 0;\n    data.error || (data.error = new Error(message));\n    this.data = data;\n    data.err = data.error;\n  }\n}\n\n/**\n * CMCD spec version\n */\nconst CMCDVersion = 1;\n\n/**\n * CMCD Object Type\n */\nvar CMCDObjectType = {\n  MANIFEST: \"m\",\n  AUDIO: \"a\",\n  VIDEO: \"v\",\n  MUXED: \"av\",\n  INIT: \"i\",\n  CAPTION: \"c\",\n  TIMED_TEXT: \"tt\",\n  KEY: \"k\",\n  OTHER: \"o\"\n};\n\n/**\n * CMCD Streaming Format\n */\nconst CMCDStreamingFormatHLS = 'h';\n\n/**\n * CMCD Streaming Type\n */\n\n/**\n * CMCD Headers\n */\n\n/**\n * CMCD\n */\n\n/**\n * Controller to deal with Common Media Client Data (CMCD)\n * @see https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf\n */\nclass CMCDController {\n  // eslint-disable-line no-restricted-globals\n  // eslint-disable-line no-restricted-globals\n\n  constructor(hls) {\n    this.hls = void 0;\n    this.config = void 0;\n    this.media = void 0;\n    this.sid = void 0;\n    this.cid = void 0;\n    this.useHeaders = false;\n    this.initialized = false;\n    this.starved = false;\n    this.buffering = true;\n    this.audioBuffer = void 0;\n    this.videoBuffer = void 0;\n    this.onWaiting = () => {\n      if (this.initialized) {\n        this.starved = true;\n      }\n      this.buffering = true;\n    };\n    this.onPlaying = () => {\n      if (!this.initialized) {\n        this.initialized = true;\n      }\n      this.buffering = false;\n    };\n    /**\n     * Apply CMCD data to a manifest request.\n     */\n    this.applyPlaylistData = context => {\n      try {\n        this.apply(context, {\n          ot: CMCDObjectType.MANIFEST,\n          su: !this.initialized\n        });\n      } catch (error) {\n        logger.warn('Could not generate manifest CMCD data.', error);\n      }\n    };\n    /**\n     * Apply CMCD data to a segment request\n     */\n    this.applyFragmentData = context => {\n      try {\n        const fragment = context.frag;\n        const level = this.hls.levels[fragment.level];\n        const ot = this.getObjectType(fragment);\n        const data = {\n          d: fragment.duration * 1000,\n          ot\n        };\n        if (ot === CMCDObjectType.VIDEO || ot === CMCDObjectType.AUDIO || ot == CMCDObjectType.MUXED) {\n          data.br = level.bitrate / 1000;\n          data.tb = this.getTopBandwidth(ot) / 1000;\n          data.bl = this.getBufferLength(ot);\n        }\n        this.apply(context, data);\n      } catch (error) {\n        logger.warn('Could not generate segment CMCD data.', error);\n      }\n    };\n    this.hls = hls;\n    const config = this.config = hls.config;\n    const {\n      cmcd\n    } = config;\n    if (cmcd != null) {\n      config.pLoader = this.createPlaylistLoader();\n      config.fLoader = this.createFragmentLoader();\n      this.sid = cmcd.sessionId || CMCDController.uuid();\n      this.cid = cmcd.contentId;\n      this.useHeaders = cmcd.useHeaders === true;\n      this.registerListeners();\n    }\n  }\n  registerListeners() {\n    const hls = this.hls;\n    hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    hls.on(Events.MEDIA_DETACHED, this.onMediaDetached, this);\n    hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);\n  }\n  unregisterListeners() {\n    const hls = this.hls;\n    hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);\n    hls.off(Events.MEDIA_DETACHED, this.onMediaDetached, this);\n    hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);\n  }\n  destroy() {\n    this.unregisterListeners();\n    this.onMediaDetached();\n\n    // @ts-ignore\n    this.hls = this.config = this.audioBuffer = this.videoBuffer = null;\n  }\n  onMediaAttached(event, data) {\n    this.media = data.media;\n    this.media.addEventListener('waiting', this.onWaiting);\n    this.media.addEventListener('playing', this.onPlaying);\n  }\n  onMediaDetached() {\n    if (!this.media) {\n      return;\n    }\n    this.media.removeEventListener('waiting', this.onWaiting);\n    this.media.removeEventListener('playing', this.onPlaying);\n\n    // @ts-ignore\n    this.media = null;\n  }\n  onBufferCreated(event, data) {\n    var _data$tracks$audio, _data$tracks$video;\n    this.audioBuffer = (_data$tracks$audio = data.tracks.audio) == null ? void 0 : _data$tracks$audio.buffer;\n    this.videoBuffer = (_data$tracks$video = data.tracks.video) == null ? void 0 : _data$tracks$video.buffer;\n  }\n  /**\n   * Create baseline CMCD data\n   */\n  createData() {\n    var _this$media;\n    return {\n      v: CMCDVersion,\n      sf: CMCDStreamingFormatHLS,\n      sid: this.sid,\n      cid: this.cid,\n      pr: (_this$media = this.media) == null ? void 0 : _this$media.playbackRate,\n      mtp: this.hls.bandwidthEstimate / 1000\n    };\n  }\n\n  /**\n   * Apply CMCD data to a request.\n   */\n  apply(context, data = {}) {\n    // apply baseline data\n    _extends(data, this.createData());\n    const isVideo = data.ot === CMCDObjectType.INIT || data.ot === CMCDObjectType.VIDEO || data.ot === CMCDObjectType.MUXED;\n    if (this.starved && isVideo) {\n      data.bs = true;\n      data.su = true;\n      this.starved = false;\n    }\n    if (data.su == null) {\n      data.su = this.buffering;\n    }\n\n    // TODO: Implement rtp, nrr, nor, dl\n\n    if (this.useHeaders) {\n      const headers = CMCDController.toHeaders(data);\n      if (!Object.keys(headers).length) {\n        return;\n      }\n      if (!context.headers) {\n        context.headers = {};\n      }\n      _extends(context.headers, headers);\n    } else {\n      const query = CMCDController.toQuery(data);\n      if (!query) {\n        return;\n      }\n      context.url = CMCDController.appendQueryToUri(context.url, query);\n    }\n  }\n  /**\n   * The CMCD object type.\n   */\n  getObjectType(fragment) {\n    const {\n      type\n    } = fragment;\n    if (type === 'subtitle') {\n      return CMCDObjectType.TIMED_TEXT;\n    }\n    if (fragment.sn === 'initSegment') {\n      return CMCDObjectType.INIT;\n    }\n    if (type === 'audio') {\n      return CMCDObjectType.AUDIO;\n    }\n    if (type === 'main') {\n      if (!this.hls.audioTracks.length) {\n        return CMCDObjectType.MUXED;\n      }\n      return CMCDObjectType.VIDEO;\n    }\n    return undefined;\n  }\n\n  /**\n   * Get the highest bitrate.\n   */\n  getTopBandwidth(type) {\n    let bitrate = 0;\n    let levels;\n    const hls = this.hls;\n    if (type === CMCDObjectType.AUDIO) {\n      levels = hls.audioTracks;\n    } else {\n      const max = hls.maxAutoLevel;\n      const len = max > -1 ? max + 1 : hls.levels.length;\n      levels = hls.levels.slice(0, len);\n    }\n    for (const level of levels) {\n      if (level.bitrate > bitrate) {\n        bitrate = level.bitrate;\n      }\n    }\n    return bitrate > 0 ? bitrate : NaN;\n  }\n\n  /**\n   * Get the buffer length for a media type in milliseconds\n   */\n  getBufferLength(type) {\n    const media = this.hls.media;\n    const buffer = type === CMCDObjectType.AUDIO ? this.audioBuffer : this.videoBuffer;\n    if (!buffer || !media) {\n      return NaN;\n    }\n    const info = BufferHelper.bufferInfo(buffer, media.currentTime, this.config.maxBufferHole);\n    return info.len * 1000;\n  }\n\n  /**\n   * Create a playlist loader\n   */\n  createPlaylistLoader() {\n    const {\n      pLoader\n    } = this.config;\n    const apply = this.applyPlaylistData;\n    const Ctor = pLoader || this.config.loader;\n    return class CmcdPlaylistLoader {\n      constructor(config) {\n        this.loader = void 0;\n        this.loader = new Ctor(config);\n      }\n      get stats() {\n        return this.loader.stats;\n      }\n      get context() {\n        return this.loader.context;\n      }\n      destroy() {\n        this.loader.destroy();\n      }\n      abort() {\n        this.loader.abort();\n      }\n      load(context, config, callbacks) {\n        apply(context);\n        this.loader.load(context, config, callbacks);\n      }\n    };\n  }\n\n  /**\n   * Create a playlist loader\n   */\n  createFragmentLoader() {\n    const {\n      fLoader\n    } = this.config;\n    const apply = this.applyFragmentData;\n    const Ctor = fLoader || this.config.loader;\n    return class CmcdFragmentLoader {\n      constructor(config) {\n        this.loader = void 0;\n        this.loader = new Ctor(config);\n      }\n      get stats() {\n        return this.loader.stats;\n      }\n      get context() {\n        return this.loader.context;\n      }\n      destroy() {\n        this.loader.destroy();\n      }\n      abort() {\n        this.loader.abort();\n      }\n      load(context, config, callbacks) {\n        apply(context);\n        this.loader.load(context, config, callbacks);\n      }\n    };\n  }\n\n  /**\n   * Generate a random v4 UUI\n   *\n   * @returns {string}\n   */\n  static uuid() {\n    const url = URL.createObjectURL(new Blob());\n    const uuid = url.toString();\n    URL.revokeObjectURL(url);\n    return uuid.slice(uuid.lastIndexOf('/') + 1);\n  }\n\n  /**\n   * Serialize a CMCD data object according to the rules defined in the\n   * section 3.2 of\n   * [CTA-5004](https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf).\n   */\n  static serialize(data) {\n    const results = [];\n    const isValid = value => !Number.isNaN(value) && value != null && value !== '' && value !== false;\n    const toRounded = value => Math.round(value);\n    const toHundred = value => toRounded(value / 100) * 100;\n    const toUrlSafe = value => encodeURIComponent(value);\n    const formatters = {\n      br: toRounded,\n      d: toRounded,\n      bl: toHundred,\n      dl: toHundred,\n      mtp: toHundred,\n      nor: toUrlSafe,\n      rtp: toHundred,\n      tb: toRounded\n    };\n    const keys = Object.keys(data || {}).sort();\n    for (const key of keys) {\n      let value = data[key];\n\n      // ignore invalid values\n      if (!isValid(value)) {\n        continue;\n      }\n\n      // Version should only be reported if not equal to 1.\n      if (key === 'v' && value === 1) {\n        continue;\n      }\n\n      // Playback rate should only be sent if not equal to 1.\n      if (key == 'pr' && value === 1) {\n        continue;\n      }\n\n      // Certain values require special formatting\n      const formatter = formatters[key];\n      if (formatter) {\n        value = formatter(value);\n      }\n\n      // Serialize the key/value pair\n      const type = typeof value;\n      let result;\n      if (key === 'ot' || key === 'sf' || key === 'st') {\n        result = `${key}=${value}`;\n      } else if (type === 'boolean') {\n        result = key;\n      } else if (type === 'number') {\n        result = `${key}=${value}`;\n      } else {\n        result = `${key}=${JSON.stringify(value)}`;\n      }\n      results.push(result);\n    }\n    return results.join(',');\n  }\n\n  /**\n   * Convert a CMCD data object to request headers according to the rules\n   * defined in the section 2.1 and 3.2 of\n   * [CTA-5004](https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf).\n   */\n  static toHeaders(data) {\n    const keys = Object.keys(data);\n    const headers = {};\n    const headerNames = ['Object', 'Request', 'Session', 'Status'];\n    const headerGroups = [{}, {}, {}, {}];\n    const headerMap = {\n      br: 0,\n      d: 0,\n      ot: 0,\n      tb: 0,\n      bl: 1,\n      dl: 1,\n      mtp: 1,\n      nor: 1,\n      nrr: 1,\n      su: 1,\n      cid: 2,\n      pr: 2,\n      sf: 2,\n      sid: 2,\n      st: 2,\n      v: 2,\n      bs: 3,\n      rtp: 3\n    };\n    for (const key of keys) {\n      // Unmapped fields are mapped to the Request header\n      const index = headerMap[key] != null ? headerMap[key] : 1;\n      headerGroups[index][key] = data[key];\n    }\n    for (let i = 0; i < headerGroups.length; i++) {\n      const value = CMCDController.serialize(headerGroups[i]);\n      if (value) {\n        headers[`CMCD-${headerNames[i]}`] = value;\n      }\n    }\n    return headers;\n  }\n\n  /**\n   * Convert a CMCD data object to query args according to the rules\n   * defined in the section 2.2 and 3.2 of\n   * [CTA-5004](https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf).\n   */\n  static toQuery(data) {\n    return `CMCD=${encodeURIComponent(CMCDController.serialize(data))}`;\n  }\n\n  /**\n   * Append query args to a uri.\n   */\n  static appendQueryToUri(uri, query) {\n    if (!query) {\n      return uri;\n    }\n    const separator = uri.includes('?') ? '&' : '?';\n    return `${uri}${separator}${query}`;\n  }\n}\n\nconst PATHWAY_PENALTY_DURATION_MS = 300000;\nclass ContentSteeringController {\n  constructor(hls) {\n    this.hls = void 0;\n    this.log = void 0;\n    this.loader = null;\n    this.uri = null;\n    this.pathwayId = '.';\n    this.pathwayPriority = null;\n    this.timeToLoad = 300;\n    this.reloadTimer = -1;\n    this.updated = 0;\n    this.started = false;\n    this.enabled = true;\n    this.levels = null;\n    this.audioTracks = null;\n    this.subtitleTracks = null;\n    this.penalizedPathways = {};\n    this.hls = hls;\n    this.log = logger.log.bind(logger, `[content-steering]:`);\n    this.registerListeners();\n  }\n  registerListeners() {\n    const hls = this.hls;\n    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);\n    hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);\n    hls.on(Events.ERROR, this.onError, this);\n  }\n  unregisterListeners() {\n    const hls = this.hls;\n    if (!hls) {\n      return;\n    }\n    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);\n    hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);\n    hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);\n    hls.off(Events.ERROR, this.onError, this);\n  }\n  startLoad() {\n    this.started = true;\n    self.clearTimeout(this.reloadTimer);\n    if (this.enabled && this.uri) {\n      if (this.updated) {\n        const ttl = Math.max(this.timeToLoad * 1000 - (performance.now() - this.updated), 0);\n        this.scheduleRefresh(this.uri, ttl);\n      } else {\n        this.loadSteeringManifest(this.uri);\n      }\n    }\n  }\n  stopLoad() {\n    this.started = false;\n    if (this.loader) {\n      this.loader.destroy();\n      this.loader = null;\n    }\n    self.clearTimeout(this.reloadTimer);\n  }\n  destroy() {\n    this.unregisterListeners();\n    this.stopLoad();\n    // @ts-ignore\n    this.hls = null;\n    this.levels = this.audioTracks = this.subtitleTracks = null;\n  }\n  removeLevel(levelToRemove) {\n    const levels = this.levels;\n    if (levels) {\n      this.levels = levels.filter(level => level !== levelToRemove);\n    }\n  }\n  onManifestLoading() {\n    this.stopLoad();\n    this.enabled = true;\n    this.timeToLoad = 300;\n    this.updated = 0;\n    this.uri = null;\n    this.pathwayId = '.';\n    this.levels = this.audioTracks = this.subtitleTracks = null;\n  }\n  onManifestLoaded(event, data) {\n    const {\n      contentSteering\n    } = data;\n    if (contentSteering === null) {\n      return;\n    }\n    this.pathwayId = contentSteering.pathwayId;\n    this.uri = contentSteering.uri;\n    if (this.started) {\n      this.startLoad();\n    }\n  }\n  onManifestParsed(event, data) {\n    this.audioTracks = data.audioTracks;\n    this.subtitleTracks = data.subtitleTracks;\n  }\n  onError(event, data) {\n    const {\n      errorAction\n    } = data;\n    if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox && errorAction.flags === ErrorActionFlags.MoveAllAlternatesMatchingHost) {\n      let pathwayPriority = this.pathwayPriority;\n      const pathwayId = this.pathwayId;\n      if (!this.penalizedPathways[pathwayId]) {\n        this.penalizedPathways[pathwayId] = performance.now();\n      }\n      if (!pathwayPriority && this.levels) {\n        // If PATHWAY-PRIORITY was not provided, list pathways for error handling\n        pathwayPriority = this.levels.reduce((pathways, level) => {\n          if (pathways.indexOf(level.pathwayId) === -1) {\n            pathways.push(level.pathwayId);\n          }\n          return pathways;\n        }, []);\n      }\n      if (pathwayPriority && pathwayPriority.length > 1) {\n        this.updatePathwayPriority(pathwayPriority);\n        errorAction.resolved = this.pathwayId !== pathwayId;\n      }\n    }\n  }\n  filterParsedLevels(levels) {\n    // Filter levels to only include those that are in the initial pathway\n    this.levels = levels;\n    let pathwayLevels = this.getLevelsForPathway(this.pathwayId);\n    if (pathwayLevels.length === 0) {\n      const pathwayId = levels[0].pathwayId;\n      this.log(`No levels found in Pathway ${this.pathwayId}. Setting initial Pathway to \"${pathwayId}\"`);\n      pathwayLevels = this.getLevelsForPathway(pathwayId);\n      this.pathwayId = pathwayId;\n    }\n    if (pathwayLevels.length !== levels.length) {\n      this.log(`Found ${pathwayLevels.length}/${levels.length} levels in Pathway \"${this.pathwayId}\"`);\n      return pathwayLevels;\n    }\n    return levels;\n  }\n  getLevelsForPathway(pathwayId) {\n    if (this.levels === null) {\n      return [];\n    }\n    return this.levels.filter(level => pathwayId === level.pathwayId);\n  }\n  updatePathwayPriority(pathwayPriority) {\n    this.pathwayPriority = pathwayPriority;\n    let levels;\n\n    // Evaluate if we should remove the pathway from the penalized list\n    const penalizedPathways = this.penalizedPathways;\n    const now = performance.now();\n    Object.keys(penalizedPathways).forEach(pathwayId => {\n      if (now - penalizedPathways[pathwayId] > PATHWAY_PENALTY_DURATION_MS) {\n        delete penalizedPathways[pathwayId];\n      }\n    });\n    for (let i = 0; i < pathwayPriority.length; i++) {\n      const pathwayId = pathwayPriority[i];\n      if (penalizedPathways[pathwayId]) {\n        continue;\n      }\n      if (pathwayId === this.pathwayId) {\n        return;\n      }\n      const selectedIndex = this.hls.nextLoadLevel;\n      const selectedLevel = this.hls.levels[selectedIndex];\n      levels = this.getLevelsForPathway(pathwayId);\n      if (levels.length > 0) {\n        this.log(`Setting Pathway to \"${pathwayId}\"`);\n        this.pathwayId = pathwayId;\n        this.hls.trigger(Events.LEVELS_UPDATED, {\n          levels\n        });\n        // Set LevelController's level to trigger LEVEL_SWITCHING which loads playlist if needed\n        const levelAfterChange = this.hls.levels[selectedIndex];\n        if (selectedLevel && levelAfterChange && this.levels) {\n          if (levelAfterChange.attrs['STABLE-VARIANT-ID'] !== selectedLevel.attrs['STABLE-VARIANT-ID'] && levelAfterChange.bitrate !== selectedLevel.bitrate) {\n            this.log(`Unstable Pathways change from bitrate ${selectedLevel.bitrate} to ${levelAfterChange.bitrate}`);\n          }\n          this.hls.nextLoadLevel = selectedIndex;\n        }\n        break;\n      }\n    }\n  }\n  clonePathways(pathwayClones) {\n    const levels = this.levels;\n    if (!levels) {\n      return;\n    }\n    const audioGroupCloneMap = {};\n    const subtitleGroupCloneMap = {};\n    pathwayClones.forEach(pathwayClone => {\n      const {\n        ID: cloneId,\n        'BASE-ID': baseId,\n        'URI-REPLACEMENT': uriReplacement\n      } = pathwayClone;\n      if (levels.some(level => level.pathwayId === cloneId)) {\n        return;\n      }\n      const clonedVariants = this.getLevelsForPathway(baseId).map(baseLevel => {\n        const levelParsed = _extends({}, baseLevel);\n        levelParsed.details = undefined;\n        levelParsed.url = performUriReplacement(baseLevel.uri, baseLevel.attrs['STABLE-VARIANT-ID'], 'PER-VARIANT-URIS', uriReplacement);\n        const attributes = new AttrList(baseLevel.attrs);\n        attributes['PATHWAY-ID'] = cloneId;\n        const clonedAudioGroupId = attributes.AUDIO && `${attributes.AUDIO}_clone_${cloneId}`;\n        const clonedSubtitleGroupId = attributes.SUBTITLES && `${attributes.SUBTITLES}_clone_${cloneId}`;\n        if (clonedAudioGroupId) {\n          audioGroupCloneMap[attributes.AUDIO] = clonedAudioGroupId;\n          attributes.AUDIO = clonedAudioGroupId;\n        }\n        if (clonedSubtitleGroupId) {\n          subtitleGroupCloneMap[attributes.SUBTITLES] = clonedSubtitleGroupId;\n          attributes.SUBTITLES = clonedSubtitleGroupId;\n        }\n        levelParsed.attrs = attributes;\n        const clonedLevel = new Level(levelParsed);\n        addGroupId(clonedLevel, 'audio', clonedAudioGroupId);\n        addGroupId(clonedLevel, 'text', clonedSubtitleGroupId);\n        return clonedLevel;\n      });\n      levels.push(...clonedVariants);\n      cloneRenditionGroups(this.audioTracks, audioGroupCloneMap, uriReplacement, cloneId);\n      cloneRenditionGroups(this.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId);\n    });\n  }\n  loadSteeringManifest(uri) {\n    const config = this.hls.config;\n    const Loader = config.loader;\n    if (this.loader) {\n      this.loader.destroy();\n    }\n    this.loader = new Loader(config);\n    let url;\n    try {\n      url = new self.URL(uri);\n    } catch (error) {\n      this.enabled = false;\n      this.log(`Failed to parse Steering Manifest URI: ${uri}`);\n      return;\n    }\n    if (url.protocol !== 'data:') {\n      const throughput = (this.hls.bandwidthEstimate || config.abrEwmaDefaultEstimate) | 0;\n      url.searchParams.set('_HLS_pathway', this.pathwayId);\n      url.searchParams.set('_HLS_throughput', '' + throughput);\n    }\n    const context = {\n      responseType: 'json',\n      url: url.href\n    };\n    const loadPolicy = config.steeringManifestLoadPolicy.default;\n    const legacyRetryCompatibility = loadPolicy.errorRetry || loadPolicy.timeoutRetry || {};\n    const loaderConfig = {\n      loadPolicy,\n      timeout: loadPolicy.maxLoadTimeMs,\n      maxRetry: legacyRetryCompatibility.maxNumRetry || 0,\n      retryDelay: legacyRetryCompatibility.retryDelayMs || 0,\n      maxRetryDelay: legacyRetryCompatibility.maxRetryDelayMs || 0\n    };\n    const callbacks = {\n      onSuccess: (response, stats, context, networkDetails) => {\n        this.log(`Loaded steering manifest: \"${url}\"`);\n        const steeringData = response.data;\n        if (steeringData.VERSION !== 1) {\n          this.log(`Steering VERSION ${steeringData.VERSION} not supported!`);\n          return;\n        }\n        this.updated = performance.now();\n        this.timeToLoad = steeringData.TTL;\n        const {\n          'RELOAD-URI': reloadUri,\n          'PATHWAY-CLONES': pathwayClones,\n          'PATHWAY-PRIORITY': pathwayPriority\n        } = steeringData;\n        if (reloadUri) {\n          try {\n            this.uri = new self.URL(reloadUri, url).href;\n          } catch (error) {\n            this.enabled = false;\n            this.log(`Failed to parse Steering Manifest RELOAD-URI: ${reloadUri}`);\n            return;\n          }\n        }\n        this.scheduleRefresh(this.uri || context.url);\n        if (pathwayClones) {\n          this.clonePathways(pathwayClones);\n        }\n        if (pathwayPriority) {\n          this.updatePathwayPriority(pathwayPriority);\n        }\n      },\n      onError: (error, context, networkDetails, stats) => {\n        this.log(`Error loading steering manifest: ${error.code} ${error.text} (${context.url})`);\n        this.stopLoad();\n        if (error.code === 410) {\n          this.enabled = false;\n          this.log(`Steering manifest ${context.url} no longer available`);\n          return;\n        }\n        let ttl = this.timeToLoad * 1000;\n        if (error.code === 429) {\n          const loader = this.loader;\n          if (typeof (loader == null ? void 0 : loader.getResponseHeader) === 'function') {\n            const retryAfter = loader.getResponseHeader('Retry-After');\n            if (retryAfter) {\n              ttl = parseFloat(retryAfter) * 1000;\n            }\n          }\n          this.log(`Steering manifest ${context.url} rate limited`);\n          return;\n        }\n        this.scheduleRefresh(this.uri || context.url, ttl);\n      },\n      onTimeout: (stats, context, networkDetails) => {\n        this.log(`Timeout loading steering manifest (${context.url})`);\n        this.scheduleRefresh(this.uri || context.url);\n      }\n    };\n    this.log(`Requesting steering manifest: ${url}`);\n    this.loader.load(context, loaderConfig, callbacks);\n  }\n  scheduleRefresh(uri, ttlMs = this.timeToLoad * 1000) {\n    self.clearTimeout(this.reloadTimer);\n    this.reloadTimer = self.setTimeout(() => {\n      this.loadSteeringManifest(uri);\n    }, ttlMs);\n  }\n}\nfunction cloneRenditionGroups(tracks, groupCloneMap, uriReplacement, cloneId) {\n  if (!tracks) {\n    return;\n  }\n  Object.keys(groupCloneMap).forEach(audioGroupId => {\n    const clonedTracks = tracks.filter(track => track.groupId === audioGroupId).map(track => {\n      const clonedTrack = _extends({}, track);\n      clonedTrack.details = undefined;\n      clonedTrack.attrs = new AttrList(clonedTrack.attrs);\n      clonedTrack.url = clonedTrack.attrs.URI = performUriReplacement(track.url, track.attrs['STABLE-RENDITION-ID'], 'PER-RENDITION-URIS', uriReplacement);\n      clonedTrack.groupId = clonedTrack.attrs['GROUP-ID'] = groupCloneMap[audioGroupId];\n      clonedTrack.attrs['PATHWAY-ID'] = cloneId;\n      return clonedTrack;\n    });\n    tracks.push(...clonedTracks);\n  });\n}\nfunction performUriReplacement(uri, stableId, perOptionKey, uriReplacement) {\n  const {\n    HOST: host,\n    PARAMS: params,\n    [perOptionKey]: perOptionUris\n  } = uriReplacement;\n  let perVariantUri;\n  if (stableId) {\n    perVariantUri = perOptionUris == null ? void 0 : perOptionUris[stableId];\n    if (perVariantUri) {\n      uri = perVariantUri;\n    }\n  }\n  const url = new self.URL(uri);\n  if (host && !perVariantUri) {\n    url.host = host;\n  }\n  if (params) {\n    Object.keys(params).sort().forEach(key => {\n      if (key) {\n        url.searchParams.set(key, params[key]);\n      }\n    });\n  }\n  return url.href;\n}\n\nconst AGE_HEADER_LINE_REGEX = /^age:\\s*[\\d.]+\\s*$/im;\nclass XhrLoader {\n  constructor(config) {\n    this.xhrSetup = void 0;\n    this.requestTimeout = void 0;\n    this.retryTimeout = void 0;\n    this.retryDelay = void 0;\n    this.config = null;\n    this.callbacks = null;\n    this.context = void 0;\n    this.loader = null;\n    this.stats = void 0;\n    this.xhrSetup = config ? config.xhrSetup || null : null;\n    this.stats = new LoadStats();\n    this.retryDelay = 0;\n  }\n  destroy() {\n    this.callbacks = null;\n    this.abortInternal();\n    this.loader = null;\n    this.config = null;\n  }\n  abortInternal() {\n    const loader = this.loader;\n    self.clearTimeout(this.requestTimeout);\n    self.clearTimeout(this.retryTimeout);\n    if (loader) {\n      loader.onreadystatechange = null;\n      loader.onprogress = null;\n      if (loader.readyState !== 4) {\n        this.stats.aborted = true;\n        loader.abort();\n      }\n    }\n  }\n  abort() {\n    var _this$callbacks;\n    this.abortInternal();\n    if ((_this$callbacks = this.callbacks) != null && _this$callbacks.onAbort) {\n      this.callbacks.onAbort(this.stats, this.context, this.loader);\n    }\n  }\n  load(context, config, callbacks) {\n    if (this.stats.loading.start) {\n      throw new Error('Loader can only be used once.');\n    }\n    this.stats.loading.start = self.performance.now();\n    this.context = context;\n    this.config = config;\n    this.callbacks = callbacks;\n    this.loadInternal();\n  }\n  loadInternal() {\n    const {\n      config,\n      context\n    } = this;\n    if (!config) {\n      return;\n    }\n    const xhr = this.loader = new self.XMLHttpRequest();\n    const stats = this.stats;\n    stats.loading.first = 0;\n    stats.loaded = 0;\n    stats.aborted = false;\n    const xhrSetup = this.xhrSetup;\n    if (xhrSetup) {\n      Promise.resolve().then(() => {\n        if (this.stats.aborted) return;\n        return xhrSetup(xhr, context.url);\n      }).catch(error => {\n        xhr.open('GET', context.url, true);\n        return xhrSetup(xhr, context.url);\n      }).then(() => {\n        if (this.stats.aborted) return;\n        this.openAndSendXhr(xhr, context, config);\n      }).catch(error => {\n        // IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS\n        this.callbacks.onError({\n          code: xhr.status,\n          text: error.message\n        }, context, xhr, stats);\n        return;\n      });\n    } else {\n      this.openAndSendXhr(xhr, context, config);\n    }\n  }\n  openAndSendXhr(xhr, context, config) {\n    if (!xhr.readyState) {\n      xhr.open('GET', context.url, true);\n    }\n    const headers = this.context.headers;\n    const {\n      maxTimeToFirstByteMs,\n      maxLoadTimeMs\n    } = config.loadPolicy;\n    if (headers) {\n      for (const header in headers) {\n        xhr.setRequestHeader(header, headers[header]);\n      }\n    }\n    if (context.rangeEnd) {\n      xhr.setRequestHeader('Range', 'bytes=' + context.rangeStart + '-' + (context.rangeEnd - 1));\n    }\n    xhr.onreadystatechange = this.readystatechange.bind(this);\n    xhr.onprogress = this.loadprogress.bind(this);\n    xhr.responseType = context.responseType;\n    // setup timeout before we perform request\n    self.clearTimeout(this.requestTimeout);\n    config.timeout = maxTimeToFirstByteMs && isFiniteNumber(maxTimeToFirstByteMs) ? maxTimeToFirstByteMs : maxLoadTimeMs;\n    this.requestTimeout = self.setTimeout(this.loadtimeout.bind(this), config.timeout);\n    xhr.send();\n  }\n  readystatechange() {\n    const {\n      context,\n      loader: xhr,\n      stats\n    } = this;\n    if (!context || !xhr) {\n      return;\n    }\n    const readyState = xhr.readyState;\n    const config = this.config;\n\n    // don't proceed if xhr has been aborted\n    if (stats.aborted) {\n      return;\n    }\n\n    // >= HEADERS_RECEIVED\n    if (readyState >= 2) {\n      if (stats.loading.first === 0) {\n        stats.loading.first = Math.max(self.performance.now(), stats.loading.start);\n        // readyState >= 2 AND readyState !==4 (readyState = HEADERS_RECEIVED || LOADING) rearm timeout as xhr not finished yet\n        if (config.timeout !== config.loadPolicy.maxLoadTimeMs) {\n          self.clearTimeout(this.requestTimeout);\n          config.timeout = config.loadPolicy.maxLoadTimeMs;\n          this.requestTimeout = self.setTimeout(this.loadtimeout.bind(this), config.loadPolicy.maxLoadTimeMs - (stats.loading.first - stats.loading.start));\n        }\n      }\n      if (readyState === 4) {\n        self.clearTimeout(this.requestTimeout);\n        xhr.onreadystatechange = null;\n        xhr.onprogress = null;\n        const status = xhr.status;\n        // http status between 200 to 299 are all successful\n        const useResponse = xhr.responseType !== 'text';\n        if (status >= 200 && status < 300 && (useResponse && xhr.response || xhr.responseText !== null)) {\n          stats.loading.end = Math.max(self.performance.now(), stats.loading.first);\n          const data = useResponse ? xhr.response : xhr.responseText;\n          const len = xhr.responseType === 'arraybuffer' ? data.byteLength : data.length;\n          stats.loaded = stats.total = len;\n          stats.bwEstimate = stats.total * 8000 / (stats.loading.end - stats.loading.first);\n          if (!this.callbacks) {\n            return;\n          }\n          const onProgress = this.callbacks.onProgress;\n          if (onProgress) {\n            onProgress(stats, context, data, xhr);\n          }\n          if (!this.callbacks) {\n            return;\n          }\n          const response = {\n            url: xhr.responseURL,\n            data: data,\n            code: status\n          };\n          this.callbacks.onSuccess(response, stats, context, xhr);\n        } else {\n          const retryConfig = config.loadPolicy.errorRetry;\n          const retryCount = stats.retry;\n          // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error\n          if (shouldRetry(retryConfig, retryCount, false, status)) {\n            this.retry(retryConfig);\n          } else {\n            logger.error(`${status} while loading ${context.url}`);\n            this.callbacks.onError({\n              code: status,\n              text: xhr.statusText\n            }, context, xhr, stats);\n          }\n        }\n      }\n    }\n  }\n  loadtimeout() {\n    var _this$config;\n    const retryConfig = (_this$config = this.config) == null ? void 0 : _this$config.loadPolicy.timeoutRetry;\n    const retryCount = this.stats.retry;\n    if (shouldRetry(retryConfig, retryCount, true)) {\n      this.retry(retryConfig);\n    } else {\n      logger.warn(`timeout while loading ${this.context.url}`);\n      const callbacks = this.callbacks;\n      if (callbacks) {\n        this.abortInternal();\n        callbacks.onTimeout(this.stats, this.context, this.loader);\n      }\n    }\n  }\n  retry(retryConfig) {\n    const {\n      context,\n      stats\n    } = this;\n    this.retryDelay = getRetryDelay(retryConfig, stats.retry);\n    stats.retry++;\n    logger.warn(`${status ? 'HTTP Status ' + status : 'Timeout'} while loading ${context.url}, retrying ${stats.retry}/${retryConfig.maxNumRetry} in ${this.retryDelay}ms`);\n    // abort and reset internal state\n    this.abortInternal();\n    this.loader = null;\n    // schedule retry\n    self.clearTimeout(this.retryTimeout);\n    this.retryTimeout = self.setTimeout(this.loadInternal.bind(this), this.retryDelay);\n  }\n  loadprogress(event) {\n    const stats = this.stats;\n    stats.loaded = event.loaded;\n    if (event.lengthComputable) {\n      stats.total = event.total;\n    }\n  }\n  getCacheAge() {\n    let result = null;\n    if (this.loader && AGE_HEADER_LINE_REGEX.test(this.loader.getAllResponseHeaders())) {\n      const ageHeader = this.loader.getResponseHeader('age');\n      result = ageHeader ? parseFloat(ageHeader) : null;\n    }\n    return result;\n  }\n  getResponseHeader(name) {\n    if (this.loader && new RegExp(`^${name}:\\\\s*[\\\\d.]+\\\\s*$`, 'im').test(this.loader.getAllResponseHeaders())) {\n      return this.loader.getResponseHeader(name);\n    }\n    return null;\n  }\n}\n\nfunction fetchSupported() {\n  if (\n  // @ts-ignore\n  self.fetch && self.AbortController && self.ReadableStream && self.Request) {\n    try {\n      new self.ReadableStream({}); // eslint-disable-line no-new\n      return true;\n    } catch (e) {\n      /* noop */\n    }\n  }\n  return false;\n}\nconst BYTERANGE = /(\\d+)-(\\d+)\\/(\\d+)/;\nclass FetchLoader {\n  constructor(config /* HlsConfig */) {\n    this.fetchSetup = void 0;\n    this.requestTimeout = void 0;\n    this.request = void 0;\n    this.response = void 0;\n    this.controller = void 0;\n    this.context = void 0;\n    this.config = null;\n    this.callbacks = null;\n    this.stats = void 0;\n    this.loader = null;\n    this.fetchSetup = config.fetchSetup || getRequest;\n    this.controller = new self.AbortController();\n    this.stats = new LoadStats();\n  }\n  destroy() {\n    this.loader = this.callbacks = null;\n    this.abortInternal();\n  }\n  abortInternal() {\n    const response = this.response;\n    if (!(response != null && response.ok)) {\n      this.stats.aborted = true;\n      this.controller.abort();\n    }\n  }\n  abort() {\n    var _this$callbacks;\n    this.abortInternal();\n    if ((_this$callbacks = this.callbacks) != null && _this$callbacks.onAbort) {\n      this.callbacks.onAbort(this.stats, this.context, this.response);\n    }\n  }\n  load(context, config, callbacks) {\n    const stats = this.stats;\n    if (stats.loading.start) {\n      throw new Error('Loader can only be used once.');\n    }\n    stats.loading.start = self.performance.now();\n    const initParams = getRequestParameters(context, this.controller.signal);\n    const onProgress = callbacks.onProgress;\n    const isArrayBuffer = context.responseType === 'arraybuffer';\n    const LENGTH = isArrayBuffer ? 'byteLength' : 'length';\n    const {\n      maxTimeToFirstByteMs,\n      maxLoadTimeMs\n    } = config.loadPolicy;\n    this.context = context;\n    this.config = config;\n    this.callbacks = callbacks;\n    this.request = this.fetchSetup(context, initParams);\n    self.clearTimeout(this.requestTimeout);\n    config.timeout = maxTimeToFirstByteMs && isFiniteNumber(maxTimeToFirstByteMs) ? maxTimeToFirstByteMs : maxLoadTimeMs;\n    this.requestTimeout = self.setTimeout(() => {\n      this.abortInternal();\n      callbacks.onTimeout(stats, context, this.response);\n    }, config.timeout);\n    self.fetch(this.request).then(response => {\n      this.response = this.loader = response;\n      const first = Math.max(self.performance.now(), stats.loading.start);\n      self.clearTimeout(this.requestTimeout);\n      config.timeout = maxLoadTimeMs;\n      this.requestTimeout = self.setTimeout(() => {\n        this.abortInternal();\n        callbacks.onTimeout(stats, context, this.response);\n      }, maxLoadTimeMs - (first - stats.loading.start));\n      if (!response.ok) {\n        const {\n          status,\n          statusText\n        } = response;\n        throw new FetchError(statusText || 'fetch, bad network response', status, response);\n      }\n      stats.loading.first = first;\n      stats.total = getContentLength(response.headers) || stats.total;\n      if (onProgress && isFiniteNumber(config.highWaterMark)) {\n        return this.loadProgressively(response, stats, context, config.highWaterMark, onProgress);\n      }\n      if (isArrayBuffer) {\n        return response.arrayBuffer();\n      }\n      if (context.responseType === 'json') {\n        return response.json();\n      }\n      return response.text();\n    }).then(responseData => {\n      const {\n        response\n      } = this;\n      self.clearTimeout(this.requestTimeout);\n      stats.loading.end = Math.max(self.performance.now(), stats.loading.first);\n      const total = responseData[LENGTH];\n      if (total) {\n        stats.loaded = stats.total = total;\n      }\n      const loaderResponse = {\n        url: response.url,\n        data: responseData,\n        code: response.status\n      };\n      if (onProgress && !isFiniteNumber(config.highWaterMark)) {\n        onProgress(stats, context, responseData, response);\n      }\n      callbacks.onSuccess(loaderResponse, stats, context, response);\n    }).catch(error => {\n      self.clearTimeout(this.requestTimeout);\n      if (stats.aborted) {\n        return;\n      }\n      // CORS errors result in an undefined code. Set it to 0 here to align with XHR's behavior\n      // when destroying, 'error' itself can be undefined\n      const code = !error ? 0 : error.code || 0;\n      const text = !error ? null : error.message;\n      callbacks.onError({\n        code,\n        text\n      }, context, error ? error.details : null, stats);\n    });\n  }\n  getCacheAge() {\n    let result = null;\n    if (this.response) {\n      const ageHeader = this.response.headers.get('age');\n      result = ageHeader ? parseFloat(ageHeader) : null;\n    }\n    return result;\n  }\n  getResponseHeader(name) {\n    return this.response ? this.response.headers.get(name) : null;\n  }\n  loadProgressively(response, stats, context, highWaterMark = 0, onProgress) {\n    const chunkCache = new ChunkCache();\n    const reader = response.body.getReader();\n    const pump = () => {\n      return reader.read().then(data => {\n        if (data.done) {\n          if (chunkCache.dataLength) {\n            onProgress(stats, context, chunkCache.flush(), response);\n          }\n          return Promise.resolve(new ArrayBuffer(0));\n        }\n        const chunk = data.value;\n        const len = chunk.length;\n        stats.loaded += len;\n        if (len < highWaterMark || chunkCache.dataLength) {\n          // The current chunk is too small to to be emitted or the cache already has data\n          // Push it to the cache\n          chunkCache.push(chunk);\n          if (chunkCache.dataLength >= highWaterMark) {\n            // flush in order to join the typed arrays\n            onProgress(stats, context, chunkCache.flush(), response);\n          }\n        } else {\n          // If there's nothing cached already, and the chache is large enough\n          // just emit the progress event\n          onProgress(stats, context, chunk, response);\n        }\n        return pump();\n      }).catch(() => {\n        /* aborted */\n        return Promise.reject();\n      });\n    };\n    return pump();\n  }\n}\nfunction getRequestParameters(context, signal) {\n  const initParams = {\n    method: 'GET',\n    mode: 'cors',\n    credentials: 'same-origin',\n    signal,\n    headers: new self.Headers(_extends({}, context.headers))\n  };\n  if (context.rangeEnd) {\n    initParams.headers.set('Range', 'bytes=' + context.rangeStart + '-' + String(context.rangeEnd - 1));\n  }\n  return initParams;\n}\nfunction getByteRangeLength(byteRangeHeader) {\n  const result = BYTERANGE.exec(byteRangeHeader);\n  if (result) {\n    return parseInt(result[2]) - parseInt(result[1]) + 1;\n  }\n}\nfunction getContentLength(headers) {\n  const contentRange = headers.get('Content-Range');\n  if (contentRange) {\n    const byteRangeLength = getByteRangeLength(contentRange);\n    if (isFiniteNumber(byteRangeLength)) {\n      return byteRangeLength;\n    }\n  }\n  const contentLength = headers.get('Content-Length');\n  if (contentLength) {\n    return parseInt(contentLength);\n  }\n}\nfunction getRequest(context, initParams) {\n  return new self.Request(context.url, initParams);\n}\nclass FetchError extends Error {\n  constructor(message, code, details) {\n    super(message);\n    this.code = void 0;\n    this.details = void 0;\n    this.code = code;\n    this.details = details;\n  }\n}\n\nconst WHITESPACE_CHAR = /\\s/;\nconst Cues = {\n  newCue(track, startTime, endTime, captionScreen) {\n    const result = [];\n    let row;\n    // the type data states this is VTTCue, but it can potentially be a TextTrackCue on old browsers\n    let cue;\n    let indenting;\n    let indent;\n    let text;\n    const Cue = self.VTTCue || self.TextTrackCue;\n    for (let r = 0; r < captionScreen.rows.length; r++) {\n      row = captionScreen.rows[r];\n      indenting = true;\n      indent = 0;\n      text = '';\n      if (!row.isEmpty()) {\n        var _track$cues;\n        for (let c = 0; c < row.chars.length; c++) {\n          if (WHITESPACE_CHAR.test(row.chars[c].uchar) && indenting) {\n            indent++;\n          } else {\n            text += row.chars[c].uchar;\n            indenting = false;\n          }\n        }\n        // To be used for cleaning-up orphaned roll-up captions\n        row.cueStartTime = startTime;\n\n        // Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE\n        if (startTime === endTime) {\n          endTime += 0.0001;\n        }\n        if (indent >= 16) {\n          indent--;\n        } else {\n          indent++;\n        }\n        const cueText = fixLineBreaks(text.trim());\n        const id = generateCueId(startTime, endTime, cueText);\n\n        // If this cue already exists in the track do not push it\n        if (!(track != null && (_track$cues = track.cues) != null && _track$cues.getCueById(id))) {\n          cue = new Cue(startTime, endTime, cueText);\n          cue.id = id;\n          cue.line = r + 1;\n          cue.align = 'left';\n          // Clamp the position between 10 and 80 percent (CEA-608 PAC indent code)\n          // https://dvcs.w3.org/hg/text-tracks/raw-file/default/608toVTT/608toVTT.html#positioning-in-cea-608\n          // Firefox throws an exception and captions break with out of bounds 0-100 values\n          cue.position = 10 + Math.min(80, Math.floor(indent * 8 / 32) * 10);\n          result.push(cue);\n        }\n      }\n    }\n    if (track && result.length) {\n      // Sort bottom cues in reverse order so that they render in line order when overlapping in Chrome\n      result.sort((cueA, cueB) => {\n        if (cueA.line === 'auto' || cueB.line === 'auto') {\n          return 0;\n        }\n        if (cueA.line > 8 && cueB.line > 8) {\n          return cueB.line - cueA.line;\n        }\n        return cueA.line - cueB.line;\n      });\n      result.forEach(cue => addCueToTrack(track, cue));\n    }\n    return result;\n  }\n};\n\n/**\n * @deprecated use fragLoadPolicy.default\n */\n\n/**\n * @deprecated use manifestLoadPolicy.default and playlistLoadPolicy.default\n */\n\nconst defaultLoadPolicy = {\n  maxTimeToFirstByteMs: 8000,\n  maxLoadTimeMs: 20000,\n  timeoutRetry: null,\n  errorRetry: null\n};\n\n/**\n * @ignore\n * If possible, keep hlsDefaultConfig shallow\n * It is cloned whenever a new Hls instance is created, by keeping the config\n * shallow the properties are cloned, and we don't end up manipulating the default\n */\nconst hlsDefaultConfig = _objectSpread2(_objectSpread2({\n  autoStartLoad: true,\n  // used by stream-controller\n  startPosition: -1,\n  // used by stream-controller\n  defaultAudioCodec: undefined,\n  // used by stream-controller\n  debug: false,\n  // used by logger\n  capLevelOnFPSDrop: false,\n  // used by fps-controller\n  capLevelToPlayerSize: false,\n  // used by cap-level-controller\n  ignoreDevicePixelRatio: false,\n  // used by cap-level-controller\n  initialLiveManifestSize: 1,\n  // used by stream-controller\n  maxBufferLength: 30,\n  // used by stream-controller\n  backBufferLength: Infinity,\n  // used by buffer-controller\n  maxBufferSize: 60 * 1000 * 1000,\n  // used by stream-controller\n  maxBufferHole: 0.1,\n  // used by stream-controller\n  highBufferWatchdogPeriod: 2,\n  // used by stream-controller\n  nudgeOffset: 0.1,\n  // used by stream-controller\n  nudgeMaxRetry: 3,\n  // used by stream-controller\n  maxFragLookUpTolerance: 0.25,\n  // used by stream-controller\n  liveSyncDurationCount: 3,\n  // used by latency-controller\n  liveMaxLatencyDurationCount: Infinity,\n  // used by latency-controller\n  liveSyncDuration: undefined,\n  // used by latency-controller\n  liveMaxLatencyDuration: undefined,\n  // used by latency-controller\n  maxLiveSyncPlaybackRate: 1,\n  // used by latency-controller\n  liveDurationInfinity: false,\n  // used by buffer-controller\n  /**\n   * @deprecated use backBufferLength\n   */\n  liveBackBufferLength: null,\n  // used by buffer-controller\n  maxMaxBufferLength: 600,\n  // used by stream-controller\n  enableWorker: true,\n  // used by transmuxer\n  workerPath: null,\n  // used by transmuxer\n  enableSoftwareAES: true,\n  // used by decrypter\n  startLevel: undefined,\n  // used by level-controller\n  startFragPrefetch: false,\n  // used by stream-controller\n  fpsDroppedMonitoringPeriod: 5000,\n  // used by fps-controller\n  fpsDroppedMonitoringThreshold: 0.2,\n  // used by fps-controller\n  appendErrorMaxRetry: 3,\n  // used by buffer-controller\n  loader: XhrLoader,\n  // loader: FetchLoader,\n  fLoader: undefined,\n  // used by fragment-loader\n  pLoader: undefined,\n  // used by playlist-loader\n  xhrSetup: undefined,\n  // used by xhr-loader\n  licenseXhrSetup: undefined,\n  // used by eme-controller\n  licenseResponseCallback: undefined,\n  // used by eme-controller\n  abrController: AbrController,\n  bufferController: BufferController,\n  capLevelController: CapLevelController,\n  errorController: ErrorController,\n  fpsController: FPSController,\n  stretchShortVideoTrack: false,\n  // used by mp4-remuxer\n  maxAudioFramesDrift: 1,\n  // used by mp4-remuxer\n  forceKeyFrameOnDiscontinuity: true,\n  // used by ts-demuxer\n  abrEwmaFastLive: 3,\n  // used by abr-controller\n  abrEwmaSlowLive: 9,\n  // used by abr-controller\n  abrEwmaFastVoD: 3,\n  // used by abr-controller\n  abrEwmaSlowVoD: 9,\n  // used by abr-controller\n  abrEwmaDefaultEstimate: 5e5,\n  // 500 kbps  // used by abr-controller\n  abrBandWidthFactor: 0.95,\n  // used by abr-controller\n  abrBandWidthUpFactor: 0.7,\n  // used by abr-controller\n  abrMaxWithRealBitrate: false,\n  // used by abr-controller\n  maxStarvationDelay: 4,\n  // used by abr-controller\n  maxLoadingDelay: 4,\n  // used by abr-controller\n  minAutoBitrate: 0,\n  // used by hls\n  emeEnabled: false,\n  // used by eme-controller\n  widevineLicenseUrl: undefined,\n  // used by eme-controller\n  drmSystems: {},\n  // used by eme-controller\n  drmSystemOptions: {},\n  // used by eme-controller\n  requestMediaKeySystemAccessFunc: requestMediaKeySystemAccess ,\n  // used by eme-controller\n  testBandwidth: true,\n  progressive: false,\n  lowLatencyMode: true,\n  cmcd: undefined,\n  enableDateRangeMetadataCues: true,\n  enableEmsgMetadataCues: true,\n  enableID3MetadataCues: true,\n  certLoadPolicy: {\n    default: defaultLoadPolicy\n  },\n  keyLoadPolicy: {\n    default: {\n      maxTimeToFirstByteMs: 8000,\n      maxLoadTimeMs: 20000,\n      timeoutRetry: {\n        maxNumRetry: 1,\n        retryDelayMs: 1000,\n        maxRetryDelayMs: 20000,\n        backoff: 'linear'\n      },\n      errorRetry: {\n        maxNumRetry: 8,\n        retryDelayMs: 1000,\n        maxRetryDelayMs: 20000,\n        backoff: 'linear'\n      }\n    }\n  },\n  manifestLoadPolicy: {\n    default: {\n      maxTimeToFirstByteMs: Infinity,\n      maxLoadTimeMs: 20000,\n      timeoutRetry: {\n        maxNumRetry: 2,\n        retryDelayMs: 0,\n        maxRetryDelayMs: 0\n      },\n      errorRetry: {\n        maxNumRetry: 1,\n        retryDelayMs: 1000,\n        maxRetryDelayMs: 8000\n      }\n    }\n  },\n  playlistLoadPolicy: {\n    default: {\n      maxTimeToFirstByteMs: 10000,\n      maxLoadTimeMs: 20000,\n      timeoutRetry: {\n        maxNumRetry: 2,\n        retryDelayMs: 0,\n        maxRetryDelayMs: 0\n      },\n      errorRetry: {\n        maxNumRetry: 2,\n        retryDelayMs: 1000,\n        maxRetryDelayMs: 8000\n      }\n    }\n  },\n  fragLoadPolicy: {\n    default: {\n      maxTimeToFirstByteMs: 10000,\n      maxLoadTimeMs: 120000,\n      timeoutRetry: {\n        maxNumRetry: 4,\n        retryDelayMs: 0,\n        maxRetryDelayMs: 0\n      },\n      errorRetry: {\n        maxNumRetry: 6,\n        retryDelayMs: 1000,\n        maxRetryDelayMs: 8000\n      }\n    }\n  },\n  steeringManifestLoadPolicy: {\n    default: {\n      maxTimeToFirstByteMs: 10000,\n      maxLoadTimeMs: 20000,\n      timeoutRetry: {\n        maxNumRetry: 2,\n        retryDelayMs: 0,\n        maxRetryDelayMs: 0\n      },\n      errorRetry: {\n        maxNumRetry: 1,\n        retryDelayMs: 1000,\n        maxRetryDelayMs: 8000\n      }\n    }\n  },\n  // These default settings are deprecated in favor of the above policies\n  // and are maintained for backwards compatibility\n  manifestLoadingTimeOut: 10000,\n  manifestLoadingMaxRetry: 1,\n  manifestLoadingRetryDelay: 1000,\n  manifestLoadingMaxRetryTimeout: 64000,\n  levelLoadingTimeOut: 10000,\n  levelLoadingMaxRetry: 4,\n  levelLoadingRetryDelay: 1000,\n  levelLoadingMaxRetryTimeout: 64000,\n  fragLoadingTimeOut: 20000,\n  fragLoadingMaxRetry: 6,\n  fragLoadingRetryDelay: 1000,\n  fragLoadingMaxRetryTimeout: 64000\n}, timelineConfig()), {}, {\n  subtitleStreamController: SubtitleStreamController ,\n  subtitleTrackController: SubtitleTrackController ,\n  timelineController: TimelineController ,\n  audioStreamController: AudioStreamController ,\n  audioTrackController: AudioTrackController ,\n  emeController: EMEController ,\n  cmcdController: CMCDController ,\n  contentSteeringController: ContentSteeringController\n});\nfunction timelineConfig() {\n  return {\n    cueHandler: Cues,\n    // used by timeline-controller\n    enableWebVTT: true,\n    // used by timeline-controller\n    enableIMSC1: true,\n    // used by timeline-controller\n    enableCEA708Captions: true,\n    // used by timeline-controller\n    captionsTextTrack1Label: 'English',\n    // used by timeline-controller\n    captionsTextTrack1LanguageCode: 'en',\n    // used by timeline-controller\n    captionsTextTrack2Label: 'Spanish',\n    // used by timeline-controller\n    captionsTextTrack2LanguageCode: 'es',\n    // used by timeline-controller\n    captionsTextTrack3Label: 'Unknown CC',\n    // used by timeline-controller\n    captionsTextTrack3LanguageCode: '',\n    // used by timeline-controller\n    captionsTextTrack4Label: 'Unknown CC',\n    // used by timeline-controller\n    captionsTextTrack4LanguageCode: '',\n    // used by timeline-controller\n    renderTextTracksNatively: true\n  };\n}\n\n/**\n * @ignore\n */\nfunction mergeConfig(defaultConfig, userConfig) {\n  if ((userConfig.liveSyncDurationCount || userConfig.liveMaxLatencyDurationCount) && (userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)) {\n    throw new Error(\"Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration\");\n  }\n  if (userConfig.liveMaxLatencyDurationCount !== undefined && (userConfig.liveSyncDurationCount === undefined || userConfig.liveMaxLatencyDurationCount <= userConfig.liveSyncDurationCount)) {\n    throw new Error('Illegal hls.js config: \"liveMaxLatencyDurationCount\" must be greater than \"liveSyncDurationCount\"');\n  }\n  if (userConfig.liveMaxLatencyDuration !== undefined && (userConfig.liveSyncDuration === undefined || userConfig.liveMaxLatencyDuration <= userConfig.liveSyncDuration)) {\n    throw new Error('Illegal hls.js config: \"liveMaxLatencyDuration\" must be greater than \"liveSyncDuration\"');\n  }\n  const defaultsCopy = deepCpy(defaultConfig);\n\n  // Backwards compatibility with deprecated config values\n  const deprecatedSettingTypes = ['manifest', 'level', 'frag'];\n  const deprecatedSettings = ['TimeOut', 'MaxRetry', 'RetryDelay', 'MaxRetryTimeout'];\n  deprecatedSettingTypes.forEach(type => {\n    const policyName = `${type === 'level' ? 'playlist' : type}LoadPolicy`;\n    const policyNotSet = userConfig[policyName] === undefined;\n    const report = [];\n    deprecatedSettings.forEach(setting => {\n      const deprecatedSetting = `${type}Loading${setting}`;\n      const value = userConfig[deprecatedSetting];\n      if (value !== undefined && policyNotSet) {\n        report.push(deprecatedSetting);\n        const settings = defaultsCopy[policyName].default;\n        userConfig[policyName] = {\n          default: settings\n        };\n        switch (setting) {\n          case 'TimeOut':\n            settings.maxLoadTimeMs = value;\n            settings.maxTimeToFirstByteMs = value;\n            break;\n          case 'MaxRetry':\n            settings.errorRetry.maxNumRetry = value;\n            settings.timeoutRetry.maxNumRetry = value;\n            break;\n          case 'RetryDelay':\n            settings.errorRetry.retryDelayMs = value;\n            settings.timeoutRetry.retryDelayMs = value;\n            break;\n          case 'MaxRetryTimeout':\n            settings.errorRetry.maxRetryDelayMs = value;\n            settings.timeoutRetry.maxRetryDelayMs = value;\n            break;\n        }\n      }\n    });\n    if (report.length) {\n      logger.warn(`hls.js config: \"${report.join('\", \"')}\" setting(s) are deprecated, use \"${policyName}\": ${JSON.stringify(userConfig[policyName])}`);\n    }\n  });\n  return _objectSpread2(_objectSpread2({}, defaultsCopy), userConfig);\n}\nfunction deepCpy(obj) {\n  if (obj && typeof obj === 'object') {\n    if (Array.isArray(obj)) {\n      return obj.map(deepCpy);\n    }\n    return Object.keys(obj).reduce((result, key) => {\n      result[key] = deepCpy(obj[key]);\n      return result;\n    }, {});\n  }\n  return obj;\n}\n\n/**\n * @ignore\n */\nfunction enableStreamingMode(config) {\n  const currentLoader = config.loader;\n  if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) {\n    // If a developer has configured their own loader, respect that choice\n    logger.log('[config]: Custom loader detected, cannot enable progressive streaming');\n    config.progressive = false;\n  } else {\n    const canStreamProgressively = fetchSupported();\n    if (canStreamProgressively) {\n      config.loader = FetchLoader;\n      config.progressive = true;\n      config.enableSoftwareAES = true;\n      logger.log('[config]: Progressive streaming enabled, using FetchLoader');\n    }\n  }\n}\n\n/**\n * The `Hls` class is the core of the HLS.js library used to instantiate player instances.\n * @public\n */\nclass Hls {\n  /**\n   * The runtime configuration used by the player. At instantiation this is combination of `hls.userConfig` merged over `Hls.DefaultConfig`.\n   */\n\n  /**\n   * The configuration object provided on player instantiation.\n   */\n\n  /**\n   * Get the video-dev/hls.js package version.\n   */\n  static get version() {\n    return \"1.4.13\";\n  }\n\n  /**\n   * Check if the required MediaSource Extensions are available.\n   */\n  static isSupported() {\n    return isSupported();\n  }\n  static get Events() {\n    return Events;\n  }\n  static get ErrorTypes() {\n    return ErrorTypes;\n  }\n  static get ErrorDetails() {\n    return ErrorDetails;\n  }\n\n  /**\n   * Get the default configuration applied to new instances.\n   */\n  static get DefaultConfig() {\n    if (!Hls.defaultConfig) {\n      return hlsDefaultConfig;\n    }\n    return Hls.defaultConfig;\n  }\n\n  /**\n   * Replace the default configuration applied to new instances.\n   */\n  static set DefaultConfig(defaultConfig) {\n    Hls.defaultConfig = defaultConfig;\n  }\n\n  /**\n   * Creates an instance of an HLS client that can attach to exactly one `HTMLMediaElement`.\n   * @param userConfig - Configuration options applied over `Hls.DefaultConfig`\n   */\n  constructor(userConfig = {}) {\n    this.config = void 0;\n    this.userConfig = void 0;\n    this.coreComponents = void 0;\n    this.networkControllers = void 0;\n    this._emitter = new EventEmitter();\n    this._autoLevelCapping = void 0;\n    this._maxHdcpLevel = null;\n    this.abrController = void 0;\n    this.bufferController = void 0;\n    this.capLevelController = void 0;\n    this.latencyController = void 0;\n    this.levelController = void 0;\n    this.streamController = void 0;\n    this.audioTrackController = void 0;\n    this.subtitleTrackController = void 0;\n    this.emeController = void 0;\n    this.cmcdController = void 0;\n    this._media = null;\n    this.url = null;\n    enableLogs(userConfig.debug || false, 'Hls instance');\n    const config = this.config = mergeConfig(Hls.DefaultConfig, userConfig);\n    this.userConfig = userConfig;\n    this._autoLevelCapping = -1;\n    if (config.progressive) {\n      enableStreamingMode(config);\n    }\n\n    // core controllers and network loaders\n    const {\n      abrController: ConfigAbrController,\n      bufferController: ConfigBufferController,\n      capLevelController: ConfigCapLevelController,\n      errorController: ConfigErrorController,\n      fpsController: ConfigFpsController\n    } = config;\n    const errorController = new ConfigErrorController(this);\n    const abrController = this.abrController = new ConfigAbrController(this);\n    const bufferController = this.bufferController = new ConfigBufferController(this);\n    const capLevelController = this.capLevelController = new ConfigCapLevelController(this);\n    const fpsController = new ConfigFpsController(this);\n    const playListLoader = new PlaylistLoader(this);\n    const id3TrackController = new ID3TrackController(this);\n    const ConfigContentSteeringController = config.contentSteeringController;\n    // ConentSteeringController is defined before LevelController to receive Multivariant Playlist events first\n    const contentSteering = ConfigContentSteeringController ? new ConfigContentSteeringController(this) : null;\n    const levelController = this.levelController = new LevelController(this, contentSteering);\n    // FragmentTracker must be defined before StreamController because the order of event handling is important\n    const fragmentTracker = new FragmentTracker(this);\n    const keyLoader = new KeyLoader(this.config);\n    const streamController = this.streamController = new StreamController(this, fragmentTracker, keyLoader);\n\n    // Cap level controller uses streamController to flush the buffer\n    capLevelController.setStreamController(streamController);\n    // fpsController uses streamController to switch when frames are being dropped\n    fpsController.setStreamController(streamController);\n    const networkControllers = [playListLoader, levelController, streamController];\n    if (contentSteering) {\n      networkControllers.splice(1, 0, contentSteering);\n    }\n    this.networkControllers = networkControllers;\n    const coreComponents = [abrController, bufferController, capLevelController, fpsController, id3TrackController, fragmentTracker];\n    this.audioTrackController = this.createController(config.audioTrackController, networkControllers);\n    const AudioStreamControllerClass = config.audioStreamController;\n    if (AudioStreamControllerClass) {\n      networkControllers.push(new AudioStreamControllerClass(this, fragmentTracker, keyLoader));\n    }\n    // subtitleTrackController must be defined before subtitleStreamController because the order of event handling is important\n    this.subtitleTrackController = this.createController(config.subtitleTrackController, networkControllers);\n    const SubtitleStreamControllerClass = config.subtitleStreamController;\n    if (SubtitleStreamControllerClass) {\n      networkControllers.push(new SubtitleStreamControllerClass(this, fragmentTracker, keyLoader));\n    }\n    this.createController(config.timelineController, coreComponents);\n    keyLoader.emeController = this.emeController = this.createController(config.emeController, coreComponents);\n    this.cmcdController = this.createController(config.cmcdController, coreComponents);\n    this.latencyController = this.createController(LatencyController, coreComponents);\n    this.coreComponents = coreComponents;\n\n    // Error controller handles errors before and after all other controllers\n    // This listener will be invoked after all other controllers error listeners\n    networkControllers.push(errorController);\n    const onErrorOut = errorController.onErrorOut;\n    if (typeof onErrorOut === 'function') {\n      this.on(Events.ERROR, onErrorOut, errorController);\n    }\n  }\n  createController(ControllerClass, components) {\n    if (ControllerClass) {\n      const controllerInstance = new ControllerClass(this);\n      if (components) {\n        components.push(controllerInstance);\n      }\n      return controllerInstance;\n    }\n    return null;\n  }\n\n  // Delegate the EventEmitter through the public API of Hls.js\n  on(event, listener, context = this) {\n    this._emitter.on(event, listener, context);\n  }\n  once(event, listener, context = this) {\n    this._emitter.once(event, listener, context);\n  }\n  removeAllListeners(event) {\n    this._emitter.removeAllListeners(event);\n  }\n  off(event, listener, context = this, once) {\n    this._emitter.off(event, listener, context, once);\n  }\n  listeners(event) {\n    return this._emitter.listeners(event);\n  }\n  emit(event, name, eventObject) {\n    return this._emitter.emit(event, name, eventObject);\n  }\n  trigger(event, eventObject) {\n    if (this.config.debug) {\n      return this.emit(event, event, eventObject);\n    } else {\n      try {\n        return this.emit(event, event, eventObject);\n      } catch (e) {\n        logger.error('An internal error happened while handling event ' + event + '. Error message: \"' + e.message + '\". Here is a stacktrace:', e);\n        this.trigger(Events.ERROR, {\n          type: ErrorTypes.OTHER_ERROR,\n          details: ErrorDetails.INTERNAL_EXCEPTION,\n          fatal: false,\n          event: event,\n          error: e\n        });\n      }\n    }\n    return false;\n  }\n  listenerCount(event) {\n    return this._emitter.listenerCount(event);\n  }\n\n  /**\n   * Dispose of the instance\n   */\n  destroy() {\n    logger.log('destroy');\n    this.trigger(Events.DESTROYING, undefined);\n    this.detachMedia();\n    this.removeAllListeners();\n    this._autoLevelCapping = -1;\n    this.url = null;\n    this.networkControllers.forEach(component => component.destroy());\n    this.networkControllers.length = 0;\n    this.coreComponents.forEach(component => component.destroy());\n    this.coreComponents.length = 0;\n    // Remove any references that could be held in config options or callbacks\n    const config = this.config;\n    config.xhrSetup = config.fetchSetup = undefined;\n    // @ts-ignore\n    this.userConfig = null;\n  }\n\n  /**\n   * Attaches Hls.js to a media element\n   */\n  attachMedia(media) {\n    logger.log('attachMedia');\n    this._media = media;\n    this.trigger(Events.MEDIA_ATTACHING, {\n      media: media\n    });\n  }\n\n  /**\n   * Detach Hls.js from the media\n   */\n  detachMedia() {\n    logger.log('detachMedia');\n    this.trigger(Events.MEDIA_DETACHING, undefined);\n    this._media = null;\n  }\n\n  /**\n   * Set the source URL. Can be relative or absolute.\n   */\n  loadSource(url) {\n    this.stopLoad();\n    const media = this.media;\n    const loadedSource = this.url;\n    const loadingSource = this.url = urlToolkitExports.buildAbsoluteURL(self.location.href, url, {\n      alwaysNormalize: true\n    });\n    logger.log(`loadSource:${loadingSource}`);\n    if (media && loadedSource && (loadedSource !== loadingSource || this.bufferController.hasSourceTypes())) {\n      this.detachMedia();\n      this.attachMedia(media);\n    }\n    // when attaching to a source URL, trigger a playlist load\n    this.trigger(Events.MANIFEST_LOADING, {\n      url: url\n    });\n  }\n\n  /**\n   * Start loading data from the stream source.\n   * Depending on default config, client starts loading automatically when a source is set.\n   *\n   * @param startPosition - Set the start position to stream from.\n   * Defaults to -1 (None: starts from earliest point)\n   */\n  startLoad(startPosition = -1) {\n    logger.log(`startLoad(${startPosition})`);\n    this.networkControllers.forEach(controller => {\n      controller.startLoad(startPosition);\n    });\n  }\n\n  /**\n   * Stop loading of any stream data.\n   */\n  stopLoad() {\n    logger.log('stopLoad');\n    this.networkControllers.forEach(controller => {\n      controller.stopLoad();\n    });\n  }\n\n  /**\n   * Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)\n   */\n  swapAudioCodec() {\n    logger.log('swapAudioCodec');\n    this.streamController.swapAudioCodec();\n  }\n\n  /**\n   * When the media-element fails, this allows to detach and then re-attach it\n   * as one call (convenience method).\n   *\n   * Automatic recovery of media-errors by this process is configurable.\n   */\n  recoverMediaError() {\n    logger.log('recoverMediaError');\n    const media = this._media;\n    this.detachMedia();\n    if (media) {\n      this.attachMedia(media);\n    }\n  }\n  removeLevel(levelIndex, urlId = 0) {\n    this.levelController.removeLevel(levelIndex, urlId);\n  }\n\n  /**\n   * @returns an array of levels (variants) sorted by HDCP-LEVEL, BANDWIDTH, SCORE, and RESOLUTION (height)\n   */\n  get levels() {\n    const levels = this.levelController.levels;\n    return levels ? levels : [];\n  }\n\n  /**\n   * Index of quality level (variant) currently played\n   */\n  get currentLevel() {\n    return this.streamController.currentLevel;\n  }\n\n  /**\n   * Set quality level index immediately. This will flush the current buffer to replace the quality asap. That means playback will interrupt at least shortly to re-buffer and re-sync eventually. Set to -1 for automatic level selection.\n   */\n  set currentLevel(newLevel) {\n    logger.log(`set currentLevel:${newLevel}`);\n    this.loadLevel = newLevel;\n    this.abrController.clearTimer();\n    this.streamController.immediateLevelSwitch();\n  }\n\n  /**\n   * Index of next quality level loaded as scheduled by stream controller.\n   */\n  get nextLevel() {\n    return this.streamController.nextLevel;\n  }\n\n  /**\n   * Set quality level index for next loaded data.\n   * This will switch the video quality asap, without interrupting playback.\n   * May abort current loading of data, and flush parts of buffer (outside currently played fragment region).\n   * @param newLevel - Pass -1 for automatic level selection\n   */\n  set nextLevel(newLevel) {\n    logger.log(`set nextLevel:${newLevel}`);\n    this.levelController.manualLevel = newLevel;\n    this.streamController.nextLevelSwitch();\n  }\n\n  /**\n   * Return the quality level of the currently or last (of none is loaded currently) segment\n   */\n  get loadLevel() {\n    return this.levelController.level;\n  }\n\n  /**\n   * Set quality level index for next loaded data in a conservative way.\n   * This will switch the quality without flushing, but interrupt current loading.\n   * Thus the moment when the quality switch will appear in effect will only be after the already existing buffer.\n   * @param newLevel - Pass -1 for automatic level selection\n   */\n  set loadLevel(newLevel) {\n    logger.log(`set loadLevel:${newLevel}`);\n    this.levelController.manualLevel = newLevel;\n  }\n\n  /**\n   * get next quality level loaded\n   */\n  get nextLoadLevel() {\n    return this.levelController.nextLoadLevel;\n  }\n\n  /**\n   * Set quality level of next loaded segment in a fully \"non-destructive\" way.\n   * Same as `loadLevel` but will wait for next switch (until current loading is done).\n   */\n  set nextLoadLevel(level) {\n    this.levelController.nextLoadLevel = level;\n  }\n\n  /**\n   * Return \"first level\": like a default level, if not set,\n   * falls back to index of first level referenced in manifest\n   */\n  get firstLevel() {\n    return Math.max(this.levelController.firstLevel, this.minAutoLevel);\n  }\n\n  /**\n   * Sets \"first-level\", see getter.\n   */\n  set firstLevel(newLevel) {\n    logger.log(`set firstLevel:${newLevel}`);\n    this.levelController.firstLevel = newLevel;\n  }\n\n  /**\n   * Return start level (level of first fragment that will be played back)\n   * if not overrided by user, first level appearing in manifest will be used as start level\n   * if -1 : automatic start level selection, playback will start from level matching download bandwidth\n   * (determined from download of first segment)\n   */\n  get startLevel() {\n    return this.levelController.startLevel;\n  }\n\n  /**\n   * set  start level (level of first fragment that will be played back)\n   * if not overrided by user, first level appearing in manifest will be used as start level\n   * if -1 : automatic start level selection, playback will start from level matching download bandwidth\n   * (determined from download of first segment)\n   */\n  set startLevel(newLevel) {\n    logger.log(`set startLevel:${newLevel}`);\n    // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel\n    if (newLevel !== -1) {\n      newLevel = Math.max(newLevel, this.minAutoLevel);\n    }\n    this.levelController.startLevel = newLevel;\n  }\n\n  /**\n   * Whether level capping is enabled.\n   * Default value is set via `config.capLevelToPlayerSize`.\n   */\n  get capLevelToPlayerSize() {\n    return this.config.capLevelToPlayerSize;\n  }\n\n  /**\n   * Enables or disables level capping. If disabled after previously enabled, `nextLevelSwitch` will be immediately called.\n   */\n  set capLevelToPlayerSize(shouldStartCapping) {\n    const newCapLevelToPlayerSize = !!shouldStartCapping;\n    if (newCapLevelToPlayerSize !== this.config.capLevelToPlayerSize) {\n      if (newCapLevelToPlayerSize) {\n        this.capLevelController.startCapping(); // If capping occurs, nextLevelSwitch will happen based on size.\n      } else {\n        this.capLevelController.stopCapping();\n        this.autoLevelCapping = -1;\n        this.streamController.nextLevelSwitch(); // Now we're uncapped, get the next level asap.\n      }\n\n      this.config.capLevelToPlayerSize = newCapLevelToPlayerSize;\n    }\n  }\n\n  /**\n   * Capping/max level value that should be used by automatic level selection algorithm (`ABRController`)\n   */\n  get autoLevelCapping() {\n    return this._autoLevelCapping;\n  }\n\n  /**\n   * Returns the current bandwidth estimate in bits per second, when available. Otherwise, `NaN` is returned.\n   */\n  get bandwidthEstimate() {\n    const {\n      bwEstimator\n    } = this.abrController;\n    if (!bwEstimator) {\n      return NaN;\n    }\n    return bwEstimator.getEstimate();\n  }\n\n  /**\n   * get time to first byte estimate\n   * @type {number}\n   */\n  get ttfbEstimate() {\n    const {\n      bwEstimator\n    } = this.abrController;\n    if (!bwEstimator) {\n      return NaN;\n    }\n    return bwEstimator.getEstimateTTFB();\n  }\n\n  /**\n   * Capping/max level value that should be used by automatic level selection algorithm (`ABRController`)\n   */\n  set autoLevelCapping(newLevel) {\n    if (this._autoLevelCapping !== newLevel) {\n      logger.log(`set autoLevelCapping:${newLevel}`);\n      this._autoLevelCapping = newLevel;\n    }\n  }\n  get maxHdcpLevel() {\n    return this._maxHdcpLevel;\n  }\n  set maxHdcpLevel(value) {\n    if (HdcpLevels.indexOf(value) > -1) {\n      this._maxHdcpLevel = value;\n    }\n  }\n\n  /**\n   * True when automatic level selection enabled\n   */\n  get autoLevelEnabled() {\n    return this.levelController.manualLevel === -1;\n  }\n\n  /**\n   * Level set manually (if any)\n   */\n  get manualLevel() {\n    return this.levelController.manualLevel;\n  }\n\n  /**\n   * min level selectable in auto mode according to config.minAutoBitrate\n   */\n  get minAutoLevel() {\n    const {\n      levels,\n      config: {\n        minAutoBitrate\n      }\n    } = this;\n    if (!levels) return 0;\n    const len = levels.length;\n    for (let i = 0; i < len; i++) {\n      if (levels[i].maxBitrate >= minAutoBitrate) {\n        return i;\n      }\n    }\n    return 0;\n  }\n\n  /**\n   * max level selectable in auto mode according to autoLevelCapping\n   */\n  get maxAutoLevel() {\n    const {\n      levels,\n      autoLevelCapping,\n      maxHdcpLevel\n    } = this;\n    let maxAutoLevel;\n    if (autoLevelCapping === -1 && levels && levels.length) {\n      maxAutoLevel = levels.length - 1;\n    } else {\n      maxAutoLevel = autoLevelCapping;\n    }\n    if (maxHdcpLevel) {\n      for (let i = maxAutoLevel; i--;) {\n        const hdcpLevel = levels[i].attrs['HDCP-LEVEL'];\n        if (hdcpLevel && hdcpLevel <= maxHdcpLevel) {\n          return i;\n        }\n      }\n    }\n    return maxAutoLevel;\n  }\n\n  /**\n   * next automatically selected quality level\n   */\n  get nextAutoLevel() {\n    // ensure next auto level is between  min and max auto level\n    return Math.min(Math.max(this.abrController.nextAutoLevel, this.minAutoLevel), this.maxAutoLevel);\n  }\n\n  /**\n   * this setter is used to force next auto level.\n   * this is useful to force a switch down in auto mode:\n   * in case of load error on level N, hls.js can set nextAutoLevel to N-1 for example)\n   * forced value is valid for one fragment. upon successful frag loading at forced level,\n   * this value will be resetted to -1 by ABR controller.\n   */\n  set nextAutoLevel(nextLevel) {\n    this.abrController.nextAutoLevel = Math.max(this.minAutoLevel, nextLevel);\n  }\n\n  /**\n   * get the datetime value relative to media.currentTime for the active level Program Date Time if present\n   */\n  get playingDate() {\n    return this.streamController.currentProgramDateTime;\n  }\n  get mainForwardBufferInfo() {\n    return this.streamController.getMainFwdBufferInfo();\n  }\n\n  /**\n   * Get the list of selectable audio tracks\n   */\n  get audioTracks() {\n    const audioTrackController = this.audioTrackController;\n    return audioTrackController ? audioTrackController.audioTracks : [];\n  }\n\n  /**\n   * index of the selected audio track (index in audio track lists)\n   */\n  get audioTrack() {\n    const audioTrackController = this.audioTrackController;\n    return audioTrackController ? audioTrackController.audioTrack : -1;\n  }\n\n  /**\n   * selects an audio track, based on its index in audio track lists\n   */\n  set audioTrack(audioTrackId) {\n    const audioTrackController = this.audioTrackController;\n    if (audioTrackController) {\n      audioTrackController.audioTrack = audioTrackId;\n    }\n  }\n\n  /**\n   * get alternate subtitle tracks list from playlist\n   */\n  get subtitleTracks() {\n    const subtitleTrackController = this.subtitleTrackController;\n    return subtitleTrackController ? subtitleTrackController.subtitleTracks : [];\n  }\n\n  /**\n   * index of the selected subtitle track (index in subtitle track lists)\n   */\n  get subtitleTrack() {\n    const subtitleTrackController = this.subtitleTrackController;\n    return subtitleTrackController ? subtitleTrackController.subtitleTrack : -1;\n  }\n  get media() {\n    return this._media;\n  }\n\n  /**\n   * select an subtitle track, based on its index in subtitle track lists\n   */\n  set subtitleTrack(subtitleTrackId) {\n    const subtitleTrackController = this.subtitleTrackController;\n    if (subtitleTrackController) {\n      subtitleTrackController.subtitleTrack = subtitleTrackId;\n    }\n  }\n\n  /**\n   * Whether subtitle display is enabled or not\n   */\n  get subtitleDisplay() {\n    const subtitleTrackController = this.subtitleTrackController;\n    return subtitleTrackController ? subtitleTrackController.subtitleDisplay : false;\n  }\n\n  /**\n   * Enable/disable subtitle display rendering\n   */\n  set subtitleDisplay(value) {\n    const subtitleTrackController = this.subtitleTrackController;\n    if (subtitleTrackController) {\n      subtitleTrackController.subtitleDisplay = value;\n    }\n  }\n\n  /**\n   * get mode for Low-Latency HLS loading\n   */\n  get lowLatencyMode() {\n    return this.config.lowLatencyMode;\n  }\n\n  /**\n   * Enable/disable Low-Latency HLS part playlist and segment loading, and start live streams at playlist PART-HOLD-BACK rather than HOLD-BACK.\n   */\n  set lowLatencyMode(mode) {\n    this.config.lowLatencyMode = mode;\n  }\n\n  /**\n   * Position (in seconds) of live sync point (ie edge of live position minus safety delay defined by ```hls.config.liveSyncDuration```)\n   * @returns null prior to loading live Playlist\n   */\n  get liveSyncPosition() {\n    return this.latencyController.liveSyncPosition;\n  }\n\n  /**\n   * Estimated position (in seconds) of live edge (ie edge of live playlist plus time sync playlist advanced)\n   * @returns 0 before first playlist is loaded\n   */\n  get latency() {\n    return this.latencyController.latency;\n  }\n\n  /**\n   * maximum distance from the edge before the player seeks forward to ```hls.liveSyncPosition```\n   * configured using ```liveMaxLatencyDurationCount``` (multiple of target duration) or ```liveMaxLatencyDuration```\n   * @returns 0 before first playlist is loaded\n   */\n  get maxLatency() {\n    return this.latencyController.maxLatency;\n  }\n\n  /**\n   * target distance from the edge as calculated by the latency controller\n   */\n  get targetLatency() {\n    return this.latencyController.targetLatency;\n  }\n\n  /**\n   * the rate at which the edge of the current live playlist is advancing or 1 if there is none\n   */\n  get drift() {\n    return this.latencyController.drift;\n  }\n\n  /**\n   * set to true when startLoad is called before MANIFEST_PARSED event\n   */\n  get forceStartLoad() {\n    return this.streamController.forceStartLoad;\n  }\n}\nHls.defaultConfig = void 0;\n\nexport { Hls as default };\n//# sourceMappingURL=hls.js.map\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/Control.Draw.js",
    "content": "/**\n * @class L.Control.Draw\n * @aka L.Draw\n */\nL.Control.Draw = L.Control.extend({\n\n\t// Options\n\toptions: {\n\t\tposition: 'topleft',\n\t\tdraw: {},\n\t\tedit: false\n\t},\n\n\t// @method initialize(): void\n\t// Initializes draw control, toolbars from the options\n\tinitialize: function (options) {\n\t\tif (L.version < '0.7') {\n\t\t\tthrow new Error('Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/');\n\t\t}\n\n\t\tL.Control.prototype.initialize.call(this, options);\n\n\t\tvar toolbar;\n\n\t\tthis._toolbars = {};\n\n\t\t// Initialize toolbars\n\t\tif (L.DrawToolbar && this.options.draw) {\n\t\t\ttoolbar = new L.DrawToolbar(this.options.draw);\n\n\t\t\tthis._toolbars[L.DrawToolbar.TYPE] = toolbar;\n\n\t\t\t// Listen for when toolbar is enabled\n\t\t\tthis._toolbars[L.DrawToolbar.TYPE].on('enable', this._toolbarEnabled, this);\n\t\t}\n\n\t\tif (L.EditToolbar && this.options.edit) {\n\t\t\ttoolbar = new L.EditToolbar(this.options.edit);\n\n\t\t\tthis._toolbars[L.EditToolbar.TYPE] = toolbar;\n\n\t\t\t// Listen for when toolbar is enabled\n\t\t\tthis._toolbars[L.EditToolbar.TYPE].on('enable', this._toolbarEnabled, this);\n\t\t}\n\t\tL.toolbar = this; //set global var for editing the toolbar\n\t},\n\n\t// @method onAdd(): container\n\t// Adds the toolbar container to the map\n\tonAdd: function (map) {\n\t\tvar container = L.DomUtil.create('div', 'leaflet-draw'),\n\t\t\taddedTopClass = false,\n\t\t\ttopClassName = 'leaflet-draw-toolbar-top',\n\t\t\ttoolbarContainer;\n\n\t\tfor (var toolbarId in this._toolbars) {\n\t\t\tif (this._toolbars.hasOwnProperty(toolbarId)) {\n\t\t\t\ttoolbarContainer = this._toolbars[toolbarId].addToolbar(map);\n\n\t\t\t\tif (toolbarContainer) {\n\t\t\t\t\t// Add class to the first toolbar to remove the margin\n\t\t\t\t\tif (!addedTopClass) {\n\t\t\t\t\t\tif (!L.DomUtil.hasClass(toolbarContainer, topClassName)) {\n\t\t\t\t\t\t\tL.DomUtil.addClass(toolbarContainer.childNodes[0], topClassName);\n\t\t\t\t\t\t}\n\t\t\t\t\t\taddedTopClass = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tcontainer.appendChild(toolbarContainer);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn container;\n\t},\n\n\t// @method onRemove(): void\n\t// Removes the toolbars from the map toolbar container\n\tonRemove: function () {\n\t\tfor (var toolbarId in this._toolbars) {\n\t\t\tif (this._toolbars.hasOwnProperty(toolbarId)) {\n\t\t\t\tthis._toolbars[toolbarId].removeToolbar();\n\t\t\t}\n\t\t}\n\t},\n\n\t// @method setDrawingOptions(options): void\n\t// Sets options to all toolbar instances\n\tsetDrawingOptions: function (options) {\n\t\tfor (var toolbarId in this._toolbars) {\n\t\t\tif (this._toolbars[toolbarId] instanceof L.DrawToolbar) {\n\t\t\t\tthis._toolbars[toolbarId].setOptions(options);\n\t\t\t}\n\t\t}\n\t},\n\n\t_toolbarEnabled: function (e) {\n\t\tvar enabledToolbar = e.target;\n\n\t\tfor (var toolbarId in this._toolbars) {\n\t\t\tif (this._toolbars[toolbarId] !== enabledToolbar) {\n\t\t\t\tthis._toolbars[toolbarId].disable();\n\t\t\t}\n\t\t}\n\t}\n});\n\nL.Map.mergeOptions({\n\tdrawControlTooltips: true,\n\tdrawControl: false\n});\n\nL.Map.addInitHook(function () {\n\tif (this.options.drawControl) {\n\t\tthis.drawControl = new L.Control.Draw();\n\t\tthis.addControl(this.drawControl);\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/Leaflet.Draw.Event.js",
    "content": "/**\n * ### Events\n * Once you have successfully added the Leaflet.draw plugin to your map you will want to respond to the different\n * actions users can initiate. The following events will be triggered on the map:\n *\n * @class L.Draw.Event\n * @aka Draw.Event\n *\n * Use `L.Draw.Event.EVENTNAME` constants to ensure events are correct.\n *\n * @example\n * ```js\n * map.on(L.Draw.Event.CREATED; function (e) {\n *    var type = e.layerType,\n *        layer = e.layer;\n *\n *    if (type === 'marker') {\n *        // Do marker specific actions\n *    }\n *\n *    // Do whatever else you need to. (save to db; add to map etc)\n *    map.addLayer(layer);\n *});\n * ```\n */\nL.Draw.Event = {};\n/**\n * @event draw:created: PolyLine; Polygon; Rectangle; Circle; Marker | String\n *\n * Layer that was just created.\n * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker`\n * Triggered when a new vector or marker has been created.\n *\n */\nL.Draw.Event.CREATED = 'draw:created';\n\n/**\n * @event draw:edited: LayerGroup\n *\n * List of all layers just edited on the map.\n *\n *\n * Triggered when layers in the FeatureGroup; initialised with the plugin; have been edited and saved.\n *\n * @example\n * ```js\n *      map.on('draw:edited', function (e) {\n     *          var layers = e.layers;\n     *          layers.eachLayer(function (layer) {\n     *              //do whatever you want; most likely save back to db\n     *          });\n     *      });\n * ```\n */\nL.Draw.Event.EDITED = 'draw:edited';\n\n/**\n * @event draw:deleted: LayerGroup\n *\n * List of all layers just removed from the map.\n *\n * Triggered when layers have been removed (and saved) from the FeatureGroup.\n */\nL.Draw.Event.DELETED = 'draw:deleted';\n\n/**\n * @event draw:drawstart: String\n *\n * The type of layer this is. One of:`polyline`; `polygon`; `rectangle`; `circle`; `marker`\n *\n * Triggered when the user has chosen to draw a particular vector or marker.\n */\nL.Draw.Event.DRAWSTART = 'draw:drawstart';\n\n/**\n * @event draw:drawstop: String\n *\n * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker`\n *\n * Triggered when the user has finished a particular vector or marker.\n */\n\nL.Draw.Event.DRAWSTOP = 'draw:drawstop';\n\n/**\n * @event draw:drawvertex: LayerGroup\n *\n * List of all layers just being added from the map.\n *\n * Triggered when a vertex is created on a polyline or polygon.\n */\nL.Draw.Event.DRAWVERTEX = 'draw:drawvertex';\n\n/**\n * @event draw:editstart: String\n *\n * The type of edit this is. One of: `edit`\n *\n * Triggered when the user starts edit mode by clicking the edit tool button.\n */\n\nL.Draw.Event.EDITSTART = 'draw:editstart';\n\n/**\n * @event draw:editmove: ILayer\n *\n *  Layer that was just moved.\n *\n * Triggered as the user moves a rectangle; circle or marker.\n */\nL.Draw.Event.EDITMOVE = 'draw:editmove';\n\n/**\n * @event draw:editresize: ILayer\n *\n * Layer that was just moved.\n *\n * Triggered as the user resizes a rectangle or circle.\n */\nL.Draw.Event.EDITRESIZE = 'draw:editresize';\n\n/**\n * @event draw:editvertex: LayerGroup\n *\n * List of all layers just being edited from the map.\n *\n * Triggered when a vertex is edited on a polyline or polygon.\n */\nL.Draw.Event.EDITVERTEX = 'draw:editvertex';\n\n/**\n * @event draw:editstop: String\n *\n * The type of edit this is. One of: `edit`\n *\n * Triggered when the user has finshed editing (edit mode) and saves edits.\n */\nL.Draw.Event.EDITSTOP = 'draw:editstop';\n\n/**\n * @event draw:deletestart: String\n *\n * The type of edit this is. One of: `remove`\n *\n * Triggered when the user starts remove mode by clicking the remove tool button.\n */\nL.Draw.Event.DELETESTART = 'draw:deletestart';\n\n/**\n * @event draw:deletestop: String\n *\n * The type of edit this is. One of: `remove`\n *\n * Triggered when the user has finished removing shapes (remove mode) and saves.\n */\nL.Draw.Event.DELETESTOP = 'draw:deletestop';\n\n/**\n * @event draw:toolbaropened: String\n *\n * Triggered when a toolbar is opened.\n */\nL.Draw.Event.TOOLBAROPENED = 'draw:toolbaropened';\n\n/**\n * @event draw:toolbarclosed: String\n *\n * Triggered when a toolbar is closed.\n */\nL.Draw.Event.TOOLBARCLOSED = 'draw:toolbarclosed';\n\n/**\n * @event draw:markercontext: String\n *\n * Triggered when a marker is right clicked.\n */\nL.Draw.Event.MARKERCONTEXT = 'draw:markercontext';"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/Leaflet.draw.js",
    "content": "/**\n * Leaflet.draw assumes that you have already included the Leaflet library.\n */\nL.drawVersion = '0.4.2';\n/**\n * @class L.Draw\n * @aka Draw\n *\n *\n * To add the draw toolbar set the option drawControl: true in the map options.\n *\n * @example\n * ```js\n *      var map = L.map('map', {drawControl: true}).setView([51.505, -0.09], 13);\n *\n *      L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {\n *          attribution: '&copy; <a href=\"http://osm.org/copyright\">OpenStreetMap</a> contributors'\n *      }).addTo(map);\n * ```\n *\n * ### Adding the edit toolbar\n * To use the edit toolbar you must initialise the Leaflet.draw control and manually add it to the map.\n *\n * ```js\n *      var map = L.map('map').setView([51.505, -0.09], 13);\n *\n *      L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {\n *          attribution: '&copy; <a href=\"http://osm.org/copyright\">OpenStreetMap</a> contributors'\n *      }).addTo(map);\n *\n *      // FeatureGroup is to store editable layers\n *      var drawnItems = new L.FeatureGroup();\n *      map.addLayer(drawnItems);\n *\n *      var drawControl = new L.Control.Draw({\n *          edit: {\n *              featureGroup: drawnItems\n *          }\n *      });\n *      map.addControl(drawControl);\n * ```\n *\n * The key here is the featureGroup option. This tells the plugin which FeatureGroup contains the layers that\n * should be editable. The featureGroup can contain 0 or more features with geometry types Point, LineString, and Polygon.\n * Leaflet.draw does not work with multigeometry features such as MultiPoint, MultiLineString, MultiPolygon,\n * or GeometryCollection. If you need to add multigeometry features to the draw plugin, convert them to a\n * FeatureCollection of non-multigeometries (Points, LineStrings, or Polygons).\n */\nL.Draw = {};\n\n/**\n * @class L.drawLocal\n * @aka L.drawLocal\n *\n * The core toolbar class of the API — it is used to create the toolbar ui\n *\n * @example\n * ```js\n *      var modifiedDraw = L.drawLocal.extend({\n *          draw: {\n *              toolbar: {\n *                  buttons: {\n *                      polygon: 'Draw an awesome polygon'\n *                  }\n *              }\n *          }\n *      });\n * ```\n *\n * The default state for the control is the draw toolbar just below the zoom control.\n *  This will allow map users to draw vectors and markers.\n *  **Please note the edit toolbar is not enabled by default.**\n */\nL.drawLocal = {\n\t// format: {\n\t// \tnumeric: {\n\t// \t\tdelimiters: {\n\t// \t\t\tthousands: ',',\n\t// \t\t\tdecimal: '.'\n\t// \t\t}\n\t// \t}\n\t// },\n\tdraw: {\n\t\ttoolbar: {\n\t\t\t// #TODO: this should be reorganized where actions are nested in actions\n\t\t\t// ex: actions.undo  or actions.cancel\n\t\t\tactions: {\n\t\t\t\ttitle: 'Cancel drawing',\n\t\t\t\ttext: 'Cancel'\n\t\t\t},\n\t\t\tfinish: {\n\t\t\t\ttitle: 'Finish drawing',\n\t\t\t\ttext: 'Finish'\n\t\t\t},\n\t\t\tundo: {\n\t\t\t\ttitle: 'Delete last point drawn',\n\t\t\t\ttext: 'Delete last point'\n\t\t\t},\n\t\t\tbuttons: {\n\t\t\t\tpolyline: 'Draw a polyline',\n\t\t\t\tpolygon: 'Draw a polygon',\n\t\t\t\trectangle: 'Draw a rectangle',\n\t\t\t\tcircle: 'Draw a circle',\n\t\t\t\tmarker: 'Draw a marker',\n\t\t\t\tcirclemarker: 'Draw a circlemarker'\n\t\t\t}\n\t\t},\n\t\thandlers: {\n\t\t\tcircle: {\n\t\t\t\ttooltip: {\n\t\t\t\t\tstart: 'Click and drag to draw circle.'\n\t\t\t\t},\n\t\t\t\tradius: 'Radius'\n\t\t\t},\n\t\t\tcirclemarker: {\n\t\t\t\ttooltip: {\n\t\t\t\t\tstart: 'Click map to place circle marker.'\n\t\t\t\t}\n\t\t\t},\n\t\t\tmarker: {\n\t\t\t\ttooltip: {\n\t\t\t\t\tstart: 'Click map to place marker.'\n\t\t\t\t}\n\t\t\t},\n\t\t\tpolygon: {\n\t\t\t\ttooltip: {\n\t\t\t\t\tstart: 'Click to start drawing shape.',\n\t\t\t\t\tcont: 'Click to continue drawing shape.',\n\t\t\t\t\tend: 'Click first point to close this shape.'\n\t\t\t\t}\n\t\t\t},\n\t\t\tpolyline: {\n\t\t\t\terror: '<strong>Error:</strong> shape edges cannot cross!',\n\t\t\t\ttooltip: {\n\t\t\t\t\tstart: 'Click to start drawing line.',\n\t\t\t\t\tcont: 'Click to continue drawing line.',\n\t\t\t\t\tend: 'Click last point to finish line.'\n\t\t\t\t}\n\t\t\t},\n\t\t\trectangle: {\n\t\t\t\ttooltip: {\n\t\t\t\t\tstart: 'Click and drag to draw rectangle.'\n\t\t\t\t}\n\t\t\t},\n\t\t\tsimpleshape: {\n\t\t\t\ttooltip: {\n\t\t\t\t\tend: 'Release mouse to finish drawing.'\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\tedit: {\n\t\ttoolbar: {\n\t\t\tactions: {\n\t\t\t\tsave: {\n\t\t\t\t\ttitle: 'Save changes',\n\t\t\t\t\ttext: 'Save'\n\t\t\t\t},\n\t\t\t\tcancel: {\n\t\t\t\t\ttitle: 'Cancel editing, discards all changes',\n\t\t\t\t\ttext: 'Cancel'\n\t\t\t\t},\n\t\t\t\tclearAll: {\n\t\t\t\t\ttitle: 'Clear all layers',\n\t\t\t\t\ttext: 'Clear All'\n\t\t\t\t}\n\t\t\t},\n\t\t\tbuttons: {\n\t\t\t\tedit: 'Edit layers',\n\t\t\t\teditDisabled: 'No layers to edit',\n\t\t\t\tremove: 'Delete layers',\n\t\t\t\tremoveDisabled: 'No layers to delete'\n\t\t\t}\n\t\t},\n\t\thandlers: {\n\t\t\tedit: {\n\t\t\t\ttooltip: {\n\t\t\t\t\ttext: 'Drag handles or markers to edit features.',\n\t\t\t\t\tsubtext: 'Click cancel to undo changes.'\n\t\t\t\t}\n\t\t\t},\n\t\t\tremove: {\n\t\t\t\ttooltip: {\n\t\t\t\t\ttext: 'Click on a feature to remove.'\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/Toolbar.js",
    "content": "/**\n * @class L.Draw.Toolbar\n * @aka Toolbar\n *\n * The toolbar class of the API — it is used to create the ui\n * This will be depreciated\n *\n * @example\n *\n * ```js\n *    var toolbar = L.Toolbar();\n *    toolbar.addToolbar(map);\n * ```\n *\n * ### Disabling a toolbar\n *\n * If you do not want a particular toolbar in your app you can turn it off by setting the toolbar to false.\n *\n * ```js\n *      var drawControl = new L.Control.Draw({\n *          draw: false,\n *          edit: {\n *              featureGroup: editableLayers\n *          }\n *      });\n * ```\n *\n * ### Disabling a toolbar item\n *\n * If you want to turn off a particular toolbar item, set it to false. The following disables drawing polygons and\n * markers. It also turns off the ability to edit layers.\n *\n * ```js\n *      var drawControl = new L.Control.Draw({\n *          draw: {\n *              polygon: false,\n *              marker: false\n *          },\n *          edit: {\n *              featureGroup: editableLayers,\n *              edit: false\n *          }\n *      });\n * ```\n */\nL.Toolbar = L.Class.extend({\n\t// @section Methods for modifying the toolbar\n\n\t// @method initialize(options): void\n\t// Toolbar constructor\n\tinitialize: function (options) {\n\t\tL.setOptions(this, options);\n\n\t\tthis._modes = {};\n\t\tthis._actionButtons = [];\n\t\tthis._activeMode = null;\n\n\t\tvar version = L.version.split('.');\n\t\t//If Version is >= 1.2.0\n\t\tif (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {\n\t\t\tL.Toolbar.include(L.Evented.prototype);\n\t\t} else {\n\t\t\tL.Toolbar.include(L.Mixin.Events);\n\t\t}\n\t},\n\n\t// @method enabled(): boolean\n\t// Gets a true/false of whether the toolbar is enabled\n\tenabled: function () {\n\t\treturn this._activeMode !== null;\n\t},\n\n\t// @method disable(): void\n\t// Disables the toolbar\n\tdisable: function () {\n\t\tif (!this.enabled()) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._activeMode.handler.disable();\n\t},\n\n\t// @method addToolbar(map): L.DomUtil\n\t// Adds the toolbar to the map and returns the toolbar dom element\n\taddToolbar: function (map) {\n\t\tvar container = L.DomUtil.create('div', 'leaflet-draw-section'),\n\t\t\tbuttonIndex = 0,\n\t\t\tbuttonClassPrefix = this._toolbarClass || '',\n\t\t\tmodeHandlers = this.getModeHandlers(map),\n\t\t\ti;\n\n\t\tthis._toolbarContainer = L.DomUtil.create('div', 'leaflet-draw-toolbar leaflet-bar');\n\t\tthis._map = map;\n\n\t\tfor (i = 0; i < modeHandlers.length; i++) {\n\t\t\tif (modeHandlers[i].enabled) {\n\t\t\t\tthis._initModeHandler(\n\t\t\t\t\tmodeHandlers[i].handler,\n\t\t\t\t\tthis._toolbarContainer,\n\t\t\t\t\tbuttonIndex++,\n\t\t\t\t\tbuttonClassPrefix,\n\t\t\t\t\tmodeHandlers[i].title\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// if no buttons were added, do not add the toolbar\n\t\tif (!buttonIndex) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Save button index of the last button, -1 as we would have ++ after the last button\n\t\tthis._lastButtonIndex = --buttonIndex;\n\n\t\t// Create empty actions part of the toolbar\n\t\tthis._actionsContainer = L.DomUtil.create('ul', 'leaflet-draw-actions');\n\n\t\t// Add draw and cancel containers to the control container\n\t\tcontainer.appendChild(this._toolbarContainer);\n\t\tcontainer.appendChild(this._actionsContainer);\n\n\t\treturn container;\n\t},\n\n\t// @method removeToolbar(): void\n\t// Removes the toolbar and drops the handler event listeners\n\tremoveToolbar: function () {\n\t\t// Dispose each handler\n\t\tfor (var handlerId in this._modes) {\n\t\t\tif (this._modes.hasOwnProperty(handlerId)) {\n\t\t\t\t// Unbind handler button\n\t\t\t\tthis._disposeButton(\n\t\t\t\t\tthis._modes[handlerId].button,\n\t\t\t\t\tthis._modes[handlerId].handler.enable,\n\t\t\t\t\tthis._modes[handlerId].handler\n\t\t\t\t);\n\n\t\t\t\t// Make sure is disabled\n\t\t\t\tthis._modes[handlerId].handler.disable();\n\n\t\t\t\t// Unbind handler\n\t\t\t\tthis._modes[handlerId].handler\n\t\t\t\t\t.off('enabled', this._handlerActivated, this)\n\t\t\t\t\t.off('disabled', this._handlerDeactivated, this);\n\t\t\t}\n\t\t}\n\t\tthis._modes = {};\n\n\t\t// Dispose the actions toolbar\n\t\tfor (var i = 0, l = this._actionButtons.length; i < l; i++) {\n\t\t\tthis._disposeButton(\n\t\t\t\tthis._actionButtons[i].button,\n\t\t\t\tthis._actionButtons[i].callback,\n\t\t\t\tthis\n\t\t\t);\n\t\t}\n\t\tthis._actionButtons = [];\n\t\tthis._actionsContainer = null;\n\t},\n\n\t_initModeHandler: function (handler, container, buttonIndex, classNamePredix, buttonTitle) {\n\t\tvar type = handler.type;\n\n\t\tthis._modes[type] = {};\n\n\t\tthis._modes[type].handler = handler;\n\n\t\tthis._modes[type].button = this._createButton({\n\t\t\ttype: type,\n\t\t\ttitle: buttonTitle,\n\t\t\tclassName: classNamePredix + '-' + type,\n\t\t\tcontainer: container,\n\t\t\tcallback: this._modes[type].handler.enable,\n\t\t\tcontext: this._modes[type].handler\n\t\t});\n\n\t\tthis._modes[type].buttonIndex = buttonIndex;\n\n\t\tthis._modes[type].handler\n\t\t\t.on('enabled', this._handlerActivated, this)\n\t\t\t.on('disabled', this._handlerDeactivated, this);\n\t},\n\n\t/* Detect iOS based on browser User Agent, based on:\n\t * http://stackoverflow.com/a/9039885 */\n\t_detectIOS: function () {\n\t\tvar iOS = (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream);\n\t\treturn iOS;\n\t},\n\n\t_createButton: function (options) {\n\n\t\tvar link = L.DomUtil.create('a', options.className || '', options.container);\n\t\t// Screen reader tag\n\t\tvar sr = L.DomUtil.create('span', 'sr-only', options.container);\n\n\t\tlink.href = '#';\n\t\tlink.appendChild(sr);\n\n\t\tif (options.title) {\n\t\t\tlink.title = options.title;\n\t\t\tsr.innerHTML = options.title;\n\t\t}\n\n\t\tif (options.text) {\n\t\t\tlink.innerHTML = options.text;\n\t\t\tsr.innerHTML = options.text;\n\t\t}\n\n\t\t/* iOS does not use click events */\n\t\tvar buttonEvent = this._detectIOS() ? 'touchstart' : 'click';\n\n\t\tL.DomEvent\n\t\t\t.on(link, 'click', L.DomEvent.stopPropagation)\n\t\t\t.on(link, 'mousedown', L.DomEvent.stopPropagation)\n\t\t\t.on(link, 'dblclick', L.DomEvent.stopPropagation)\n\t\t\t.on(link, 'touchstart', L.DomEvent.stopPropagation)\n\t\t\t.on(link, 'click', L.DomEvent.preventDefault)\n\t\t\t.on(link, buttonEvent, options.callback, options.context);\n\n\t\treturn link;\n\t},\n\n\t_disposeButton: function (button, callback) {\n\t\t/* iOS does not use click events */\n\t\tvar buttonEvent = this._detectIOS() ? 'touchstart' : 'click';\n\n\t\tL.DomEvent\n\t\t\t.off(button, 'click', L.DomEvent.stopPropagation)\n\t\t\t.off(button, 'mousedown', L.DomEvent.stopPropagation)\n\t\t\t.off(button, 'dblclick', L.DomEvent.stopPropagation)\n\t\t\t.off(button, 'touchstart', L.DomEvent.stopPropagation)\n\t\t\t.off(button, 'click', L.DomEvent.preventDefault)\n\t\t\t.off(button, buttonEvent, callback);\n\t},\n\n\t_handlerActivated: function (e) {\n\t\t// Disable active mode (if present)\n\t\tthis.disable();\n\n\t\t// Cache new active feature\n\t\tthis._activeMode = this._modes[e.handler];\n\n\t\tL.DomUtil.addClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled');\n\n\t\tthis._showActionsToolbar();\n\n\t\tthis.fire('enable');\n\t},\n\n\t_handlerDeactivated: function () {\n\t\tthis._hideActionsToolbar();\n\n\t\tL.DomUtil.removeClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled');\n\n\t\tthis._activeMode = null;\n\n\t\tthis.fire('disable');\n\t},\n\n\t_createActions: function (handler) {\n\t\tvar container = this._actionsContainer,\n\t\t\tbuttons = this.getActions(handler),\n\t\t\tl = buttons.length,\n\t\t\tli, di, dl, button;\n\n\t\t// Dispose the actions toolbar (todo: dispose only not used buttons)\n\t\tfor (di = 0, dl = this._actionButtons.length; di < dl; di++) {\n\t\t\tthis._disposeButton(this._actionButtons[di].button, this._actionButtons[di].callback);\n\t\t}\n\t\tthis._actionButtons = [];\n\n\t\t// Remove all old buttons\n\t\twhile (container.firstChild) {\n\t\t\tcontainer.removeChild(container.firstChild);\n\t\t}\n\n\t\tfor (var i = 0; i < l; i++) {\n\t\t\tif ('enabled' in buttons[i] && !buttons[i].enabled) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tli = L.DomUtil.create('li', '', container);\n\n\t\t\tbutton = this._createButton({\n\t\t\t\ttitle: buttons[i].title,\n\t\t\t\ttext: buttons[i].text,\n\t\t\t\tcontainer: li,\n\t\t\t\tcallback: buttons[i].callback,\n\t\t\t\tcontext: buttons[i].context\n\t\t\t});\n\n\t\t\tthis._actionButtons.push({\n\t\t\t\tbutton: button,\n\t\t\t\tcallback: buttons[i].callback\n\t\t\t});\n\t\t}\n\t},\n\n\t_showActionsToolbar: function () {\n\t\tvar buttonIndex = this._activeMode.buttonIndex,\n\t\t\tlastButtonIndex = this._lastButtonIndex,\n\t\t\ttoolbarPosition = this._activeMode.button.offsetTop - 1;\n\n\t\t// Recreate action buttons on every click\n\t\tthis._createActions(this._activeMode.handler);\n\n\t\t// Correctly position the cancel button\n\t\tthis._actionsContainer.style.top = toolbarPosition + 'px';\n\n\t\tif (buttonIndex === 0) {\n\t\t\tL.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop');\n\t\t\tL.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-top');\n\t\t}\n\n\t\tif (buttonIndex === lastButtonIndex) {\n\t\t\tL.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom');\n\t\t\tL.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-bottom');\n\t\t}\n\n\t\tthis._actionsContainer.style.display = 'block';\n\t\tthis._map.fire(L.Draw.Event.TOOLBAROPENED);\n\t},\n\n\t_hideActionsToolbar: function () {\n\t\tthis._actionsContainer.style.display = 'none';\n\n\t\tL.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop');\n\t\tL.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom');\n\t\tL.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-top');\n\t\tL.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-bottom');\n\t\tthis._map.fire(L.Draw.Event.TOOLBARCLOSED);\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/Tooltip.js",
    "content": "L.Draw = L.Draw || {};\n/**\n * @class L.Draw.Tooltip\n * @aka Tooltip\n *\n * The tooltip class — it is used to display the tooltip while drawing\n * This will be depreciated\n *\n * @example\n *\n * ```js\n *    var tooltip = L.Draw.Tooltip();\n * ```\n *\n */\nL.Draw.Tooltip = L.Class.extend({\n\n\t// @section Methods for modifying draw state\n\n\t// @method initialize(map): void\n\t// Tooltip constructor\n\tinitialize: function (map) {\n\t\tthis._map = map;\n\t\tthis._popupPane = map._panes.popupPane;\n\t\tthis._visible = false;\n\n\t\tthis._container = map.options.drawControlTooltips ?\n\t\t\tL.DomUtil.create('div', 'leaflet-draw-tooltip', this._popupPane) : null;\n\t\tthis._singleLineLabel = false;\n\n\t\tthis._map.on('mouseout', this._onMouseOut, this);\n\t},\n\n\t// @method dispose(): void\n\t// Remove Tooltip DOM and unbind events\n\tdispose: function () {\n\t\tthis._map.off('mouseout', this._onMouseOut, this);\n\n\t\tif (this._container) {\n\t\t\tthis._popupPane.removeChild(this._container);\n\t\t\tthis._container = null;\n\t\t}\n\t},\n\n\t// @method updateContent(labelText): this\n\t// Changes the tooltip text to string in function call\n\tupdateContent: function (labelText) {\n\t\tif (!this._container) {\n\t\t\treturn this;\n\t\t}\n\t\tlabelText.subtext = labelText.subtext || '';\n\n\t\t// update the vertical position (only if changed)\n\t\tif (labelText.subtext.length === 0 && !this._singleLineLabel) {\n\t\t\tL.DomUtil.addClass(this._container, 'leaflet-draw-tooltip-single');\n\t\t\tthis._singleLineLabel = true;\n\t\t}\n\t\telse if (labelText.subtext.length > 0 && this._singleLineLabel) {\n\t\t\tL.DomUtil.removeClass(this._container, 'leaflet-draw-tooltip-single');\n\t\t\tthis._singleLineLabel = false;\n\t\t}\n\n\t\tthis._container.innerHTML =\n\t\t\t(labelText.subtext.length > 0 ?\n\t\t\t\t'<span class=\"leaflet-draw-tooltip-subtext\">' + labelText.subtext + '</span>' + '<br />' : '') +\n\t\t\t'<span>' + labelText.text + '</span>';\n\n\t\tif (!labelText.text && !labelText.subtext) {\n\t\t\tthis._visible = false;\n\t\t\tthis._container.style.visibility = 'hidden';\n\t\t} else {\n\t\t\tthis._visible = true;\n\t\t\tthis._container.style.visibility = 'inherit';\n\t\t}\n\n\t\treturn this;\n\t},\n\n\t// @method updatePosition(latlng): this\n\t// Changes the location of the tooltip\n\tupdatePosition: function (latlng) {\n\t\tvar pos = this._map.latLngToLayerPoint(latlng),\n\t\t\ttooltipContainer = this._container;\n\n\t\tif (this._container) {\n\t\t\tif (this._visible) {\n\t\t\t\ttooltipContainer.style.visibility = 'inherit';\n\t\t\t}\n\t\t\tL.DomUtil.setPosition(tooltipContainer, pos);\n\t\t}\n\n\t\treturn this;\n\t},\n\n\t// @method showAsError(): this\n\t// Applies error class to tooltip\n\tshowAsError: function () {\n\t\tif (this._container) {\n\t\t\tL.DomUtil.addClass(this._container, 'leaflet-error-draw-tooltip');\n\t\t}\n\t\treturn this;\n\t},\n\n\t// @method removeError(): this\n\t// Removes the error class from the tooltip\n\tremoveError: function () {\n\t\tif (this._container) {\n\t\t\tL.DomUtil.removeClass(this._container, 'leaflet-error-draw-tooltip');\n\t\t}\n\t\treturn this;\n\t},\n\n\t_onMouseOut: function () {\n\t\tif (this._container) {\n\t\t\tthis._container.style.visibility = 'hidden';\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/draw/DrawToolbar.js",
    "content": "/**\n * @class L.DrawToolbar\n * @aka Toolbar\n */\nL.DrawToolbar = L.Toolbar.extend({\n\n\tstatics: {\n\t\tTYPE: 'draw'\n\t},\n\n\toptions: {\n\t\tpolyline: {},\n\t\tpolygon: {},\n\t\trectangle: {},\n\t\tcircle: {},\n\t\tmarker: {},\n\t\tcirclemarker: {}\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (options) {\n\t\t// Ensure that the options are merged correctly since L.extend is only shallow\n\t\tfor (var type in this.options) {\n\t\t\tif (this.options.hasOwnProperty(type)) {\n\t\t\t\tif (options[type]) {\n\t\t\t\t\toptions[type] = L.extend({}, this.options[type], options[type]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis._toolbarClass = 'leaflet-draw-draw';\n\t\tL.Toolbar.prototype.initialize.call(this, options);\n\t},\n\n\t// @method getModeHandlers(): object\n\t// Get mode handlers information\n\tgetModeHandlers: function (map) {\n\t\treturn [\n\t\t\t{\n\t\t\t\tenabled: this.options.polyline,\n\t\t\t\thandler: new L.Draw.Polyline(map, this.options.polyline),\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.buttons.polyline\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: this.options.polygon,\n\t\t\t\thandler: new L.Draw.Polygon(map, this.options.polygon),\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.buttons.polygon\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: this.options.rectangle,\n\t\t\t\thandler: new L.Draw.Rectangle(map, this.options.rectangle),\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.buttons.rectangle\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: this.options.circle,\n\t\t\t\thandler: new L.Draw.Circle(map, this.options.circle),\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.buttons.circle\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: this.options.marker,\n\t\t\t\thandler: new L.Draw.Marker(map, this.options.marker),\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.buttons.marker\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: this.options.circlemarker,\n\t\t\t\thandler: new L.Draw.CircleMarker(map, this.options.circlemarker),\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.buttons.circlemarker\n\t\t\t}\n\t\t];\n\t},\n\n\t// @method getActions(): object\n\t// Get action information\n\tgetActions: function (handler) {\n\t\treturn [\n\t\t\t{\n\t\t\t\tenabled: handler.completeShape,\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.finish.title,\n\t\t\t\ttext: L.drawLocal.draw.toolbar.finish.text,\n\t\t\t\tcallback: handler.completeShape,\n\t\t\t\tcontext: handler\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: handler.deleteLastVertex,\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.undo.title,\n\t\t\t\ttext: L.drawLocal.draw.toolbar.undo.text,\n\t\t\t\tcallback: handler.deleteLastVertex,\n\t\t\t\tcontext: handler\n\t\t\t},\n\t\t\t{\n\t\t\t\ttitle: L.drawLocal.draw.toolbar.actions.title,\n\t\t\t\ttext: L.drawLocal.draw.toolbar.actions.text,\n\t\t\t\tcallback: this.disable,\n\t\t\t\tcontext: this\n\t\t\t}\n\t\t];\n\t},\n\n\t// @method setOptions(): void\n\t// Sets the options to the toolbar\n\tsetOptions: function (options) {\n\t\tL.setOptions(this, options);\n\n\t\tfor (var type in this._modes) {\n\t\t\tif (this._modes.hasOwnProperty(type) && options.hasOwnProperty(type)) {\n\t\t\t\tthis._modes[type].handler.setOptions(options[type]);\n\t\t\t}\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/draw/handler/Draw.Circle.js",
    "content": "/**\n * @class L.Draw.Circle\n * @aka Draw.Circle\n * @inherits L.Draw.SimpleShape\n */\nL.Draw.Circle = L.Draw.SimpleShape.extend({\n\tstatics: {\n\t\tTYPE: 'circle'\n\t},\n\n\toptions: {\n\t\tshapeOptions: {\n\t\t\tstroke: true,\n\t\t\tcolor: '#3388ff',\n\t\t\tweight: 4,\n\t\t\topacity: 0.5,\n\t\t\tfill: true,\n\t\t\tfillColor: null, //same as color by default\n\t\t\tfillOpacity: 0.2,\n\t\t\tclickable: true\n\t\t},\n\t\tshowRadius: true,\n\t\tmetric: true, // Whether to use the metric measurement system or imperial\n\t\tfeet: true, // When not metric, use feet instead of yards for display\n\t\tnautic: false // When not metric, not feet use nautic mile for display\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.Draw.Circle.TYPE;\n\n\t\tthis._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start;\n\n\t\tL.Draw.SimpleShape.prototype.initialize.call(this, map, options);\n\t},\n\n\t_drawShape: function (latlng) {\n\t\t// Calculate the distance based on the version\n\t\tif (L.GeometryUtil.isVersion07x()) {\n\t\t\tvar distance = this._startLatLng.distanceTo(latlng);\n\t\t} else {\n\t\t\tvar distance = this._map.distance(this._startLatLng, latlng);\n\t\t}\n\n\t\tif (!this._shape) {\n\t\t\tthis._shape = new L.Circle(this._startLatLng, distance, this.options.shapeOptions);\n\t\t\tthis._map.addLayer(this._shape);\n\t\t} else {\n\t\t\tthis._shape.setRadius(distance);\n\t\t}\n\t},\n\n\t_fireCreatedEvent: function () {\n\t\tvar circle = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions);\n\t\tL.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, circle);\n\t},\n\n\t_onMouseMove: function (e) {\n\t\tvar latlng = e.latlng,\n\t\t\tshowRadius = this.options.showRadius,\n\t\t\tuseMetric = this.options.metric,\n\t\t\tradius;\n\n\t\tthis._tooltip.updatePosition(latlng);\n\t\tif (this._isDrawing) {\n\t\t\tthis._drawShape(latlng);\n\n\t\t\t// Get the new radius (rounded to 1 dp)\n\t\t\tradius = this._shape.getRadius().toFixed(1);\n\n\t\t\tvar subtext = '';\n\t\t\tif (showRadius) {\n\t\t\t\tsubtext = L.drawLocal.draw.handlers.circle.radius + ': ' +\n\t\t\t\t\tL.GeometryUtil.readableDistance(radius, useMetric, this.options.feet, this.options.nautic);\n\t\t\t}\n\t\t\tthis._tooltip.updateContent({\n\t\t\t\ttext: this._endLabelText,\n\t\t\t\tsubtext: subtext\n\t\t\t});\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/draw/handler/Draw.CircleMarker.js",
    "content": "/**\n * @class L.Draw.CircleMarker\n * @aka Draw.CircleMarker\n * @inherits L.Draw.Marker\n */\nL.Draw.CircleMarker = L.Draw.Marker.extend({\n\tstatics: {\n\t\tTYPE: 'circlemarker'\n\t},\n\n\toptions: {\n\t\tstroke: true,\n\t\tcolor: '#3388ff',\n\t\tweight: 4,\n\t\topacity: 0.5,\n\t\tfill: true,\n\t\tfillColor: null, //same as color by default\n\t\tfillOpacity: 0.2,\n\t\tclickable: true,\n\t\tzIndexOffset: 2000 // This should be > than the highest z-index any markers\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.Draw.CircleMarker.TYPE;\n\n\t\tthis._initialLabelText = L.drawLocal.draw.handlers.circlemarker.tooltip.start;\n\n\t\tL.Draw.Feature.prototype.initialize.call(this, map, options);\n\t},\n\n\n\t_fireCreatedEvent: function () {\n\t\tvar circleMarker = new L.CircleMarker(this._marker.getLatLng(), this.options);\n\t\tL.Draw.Feature.prototype._fireCreatedEvent.call(this, circleMarker);\n\t},\n\n\t_createMarker: function (latlng) {\n\t\treturn new L.CircleMarker(latlng, this.options);\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/draw/handler/Draw.Feature.js",
    "content": "L.Draw = L.Draw || {};\n\n/**\n * @class L.Draw.Feature\n * @aka Draw.Feature\n */\nL.Draw.Feature = L.Handler.extend({\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\tthis._map = map;\n\t\tthis._container = map._container;\n\t\tthis._overlayPane = map._panes.overlayPane;\n\t\tthis._popupPane = map._panes.popupPane;\n\n\t\t// Merge default shapeOptions options with custom shapeOptions\n\t\tif (options && options.shapeOptions) {\n\t\t\toptions.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions);\n\t\t}\n\t\tL.setOptions(this, options);\n\n\t\tvar version = L.version.split('.');\n\t\t//If Version is >= 1.2.0\n\t\tif (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {\n\t\t\tL.Draw.Feature.include(L.Evented.prototype);\n\t\t} else {\n\t\t\tL.Draw.Feature.include(L.Mixin.Events);\n\t\t}\n\t},\n\n\t// @method enable(): void\n\t// Enables this handler\n\tenable: function () {\n\t\tif (this._enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tL.Handler.prototype.enable.call(this);\n\n\t\tthis.fire('enabled', {handler: this.type});\n\n\t\tthis._map.fire(L.Draw.Event.DRAWSTART, {layerType: this.type});\n\t},\n\n\t// @method disable(): void\n\tdisable: function () {\n\t\tif (!this._enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tL.Handler.prototype.disable.call(this);\n\n\t\tthis._map.fire(L.Draw.Event.DRAWSTOP, {layerType: this.type});\n\n\t\tthis.fire('disabled', {handler: this.type});\n\t},\n\n\t// @method addHooks(): void\n\t// Add's event listeners to this handler\n\taddHooks: function () {\n\t\tvar map = this._map;\n\n\t\tif (map) {\n\t\t\tL.DomUtil.disableTextSelection();\n\n\t\t\tmap.getContainer().focus();\n\n\t\t\tthis._tooltip = new L.Draw.Tooltip(this._map);\n\n\t\t\tL.DomEvent.on(this._container, 'keyup', this._cancelDrawing, this);\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Removes event listeners from this handler\n\tremoveHooks: function () {\n\t\tif (this._map) {\n\t\t\tL.DomUtil.enableTextSelection();\n\n\t\t\tthis._tooltip.dispose();\n\t\t\tthis._tooltip = null;\n\n\t\t\tL.DomEvent.off(this._container, 'keyup', this._cancelDrawing, this);\n\t\t}\n\t},\n\n\t// @method setOptions(object): void\n\t// Sets new options to this handler\n\tsetOptions: function (options) {\n\t\tL.setOptions(this, options);\n\t},\n\n\t_fireCreatedEvent: function (layer) {\n\t\tthis._map.fire(L.Draw.Event.CREATED, {layer: layer, layerType: this.type});\n\t},\n\n\t// Cancel drawing when the escape key is pressed\n\t_cancelDrawing: function (e) {\n\t\tif (e.keyCode === 27) {\n\t\t\tthis._map.fire('draw:canceled', {layerType: this.type});\n\t\t\tthis.disable();\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/draw/handler/Draw.Marker.js",
    "content": "/**\n * @class L.Draw.Marker\n * @aka Draw.Marker\n * @inherits L.Draw.Feature\n */\nL.Draw.Marker = L.Draw.Feature.extend({\n\tstatics: {\n\t\tTYPE: 'marker'\n\t},\n\n\toptions: {\n\t\ticon: new L.Icon.Default(),\n\t\trepeatMode: false,\n\t\tzIndexOffset: 2000 // This should be > than the highest z-index any markers\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.Draw.Marker.TYPE;\n\n\t\tthis._initialLabelText = L.drawLocal.draw.handlers.marker.tooltip.start;\n\n\t\tL.Draw.Feature.prototype.initialize.call(this, map, options);\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler.\n\taddHooks: function () {\n\t\tL.Draw.Feature.prototype.addHooks.call(this);\n\n\t\tif (this._map) {\n\t\t\tthis._tooltip.updateContent({text: this._initialLabelText});\n\n\t\t\t// Same mouseMarker as in Draw.Polyline\n\t\t\tif (!this._mouseMarker) {\n\t\t\t\tthis._mouseMarker = L.marker(this._map.getCenter(), {\n\t\t\t\t\ticon: L.divIcon({\n\t\t\t\t\t\tclassName: 'leaflet-mouse-marker',\n\t\t\t\t\t\ticonAnchor: [20, 20],\n\t\t\t\t\t\ticonSize: [40, 40]\n\t\t\t\t\t}),\n\t\t\t\t\topacity: 0,\n\t\t\t\t\tzIndexOffset: this.options.zIndexOffset\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthis._mouseMarker\n\t\t\t\t.on('click', this._onClick, this)\n\t\t\t\t.addTo(this._map);\n\n\t\t\tthis._map.on('mousemove', this._onMouseMove, this);\n\t\t\tthis._map.on('click', this._onTouch, this);\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler.\n\tremoveHooks: function () {\n\t\tL.Draw.Feature.prototype.removeHooks.call(this);\n\n\t\tif (this._map) {\n\t\t\tthis._map\n\t\t\t\t.off('click', this._onClick, this)\n\t\t\t\t.off('click', this._onTouch, this);\n\t\t\tif (this._marker) {\n\t\t\t\tthis._marker.off('click', this._onClick, this);\n\t\t\t\tthis._map\n\t\t\t\t\t.removeLayer(this._marker);\n\t\t\t\tdelete this._marker;\n\t\t\t}\n\n\t\t\tthis._mouseMarker.off('click', this._onClick, this);\n\t\t\tthis._map.removeLayer(this._mouseMarker);\n\t\t\tdelete this._mouseMarker;\n\n\t\t\tthis._map.off('mousemove', this._onMouseMove, this);\n\t\t}\n\t},\n\n\t_onMouseMove: function (e) {\n\t\tvar latlng = e.latlng;\n\n\t\tthis._tooltip.updatePosition(latlng);\n\t\tthis._mouseMarker.setLatLng(latlng);\n\n\t\tif (!this._marker) {\n\t\t\tthis._marker = this._createMarker(latlng);\n\t\t\t// Bind to both marker and map to make sure we get the click event.\n\t\t\tthis._marker.on('click', this._onClick, this);\n\t\t\tthis._map\n\t\t\t\t.on('click', this._onClick, this)\n\t\t\t\t.addLayer(this._marker);\n\t\t}\n\t\telse {\n\t\t\tlatlng = this._mouseMarker.getLatLng();\n\t\t\tthis._marker.setLatLng(latlng);\n\t\t}\n\t},\n\n\t_createMarker: function (latlng) {\n\t\treturn new L.Marker(latlng, {\n\t\t\ticon: this.options.icon,\n\t\t\tzIndexOffset: this.options.zIndexOffset\n\t\t});\n\t},\n\n\t_onClick: function () {\n\t\tthis._fireCreatedEvent();\n\n\t\tthis.disable();\n\t\tif (this.options.repeatMode) {\n\t\t\tthis.enable();\n\t\t}\n\t},\n\n\t_onTouch: function (e) {\n\t\t// called on click & tap, only really does any thing on tap\n\t\tthis._onMouseMove(e); // creates & places marker\n\t\tthis._onClick(); // permanently places marker & ends interaction\n\t},\n\n\t_fireCreatedEvent: function () {\n\t\tvar marker = new L.Marker.Touch(this._marker.getLatLng(), {icon: this.options.icon});\n\t\tL.Draw.Feature.prototype._fireCreatedEvent.call(this, marker);\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/draw/handler/Draw.Polygon.js",
    "content": "/**\n * @class L.Draw.Polygon\n * @aka Draw.Polygon\n * @inherits L.Draw.Polyline\n */\nL.Draw.Polygon = L.Draw.Polyline.extend({\n\tstatics: {\n\t\tTYPE: 'polygon'\n\t},\n\n\tPoly: L.Polygon,\n\n\toptions: {\n\t\tshowArea: false,\n\t\tshowLength: false,\n\t\tshapeOptions: {\n\t\t\tstroke: true,\n\t\t\tcolor: '#3388ff',\n\t\t\tweight: 4,\n\t\t\topacity: 0.5,\n\t\t\tfill: true,\n\t\t\tfillColor: null, //same as color by default\n\t\t\tfillOpacity: 0.2,\n\t\t\tclickable: true\n\t\t},\n\t\t// Whether to use the metric measurement system (truthy) or not (falsy).\n\t\t// Also defines the units to use for the metric system as an array of\n\t\t// strings (e.g. `['ha', 'm']`).\n\t\tmetric: true,\n\t\tfeet: true, // When not metric, to use feet instead of yards for display.\n\t\tnautic: false, // When not metric, not feet use nautic mile for display\n\t\t// Defines the precision for each type of unit (e.g. {km: 2, ft: 0}\n\t\tprecision: {}\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\tL.Draw.Polyline.prototype.initialize.call(this, map, options);\n\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.Draw.Polygon.TYPE;\n\t},\n\n\t_updateFinishHandler: function () {\n\t\tvar markerCount = this._markers.length;\n\n\t\t// The first marker should have a click handler to close the polygon\n\t\tif (markerCount === 1) {\n\t\t\tthis._markers[0].on('click', this._finishShape, this);\n\t\t}\n\n\t\t// Add and update the double click handler\n\t\tif (markerCount > 2) {\n\t\t\tthis._markers[markerCount - 1].on('dblclick', this._finishShape, this);\n\t\t\t// Only need to remove handler if has been added before\n\t\t\tif (markerCount > 3) {\n\t\t\t\tthis._markers[markerCount - 2].off('dblclick', this._finishShape, this);\n\t\t\t}\n\t\t}\n\t},\n\n\t_getTooltipText: function () {\n\t\tvar text, subtext;\n\n\t\tif (this._markers.length === 0) {\n\t\t\ttext = L.drawLocal.draw.handlers.polygon.tooltip.start;\n\t\t} else if (this._markers.length < 3) {\n\t\t\ttext = L.drawLocal.draw.handlers.polygon.tooltip.cont;\n\t\t\tsubtext = this._getMeasurementString();\n\t\t} else {\n\t\t\ttext = L.drawLocal.draw.handlers.polygon.tooltip.end;\n\t\t\tsubtext = this._getMeasurementString();\n\t\t}\n\n\t\treturn {\n\t\t\ttext: text,\n\t\t\tsubtext: subtext\n\t\t};\n\t},\n\n\t_getMeasurementString: function () {\n\t\tvar area = this._area,\n\t\t\tmeasurementString = '';\n\n\n\t\tif (!area && !this.options.showLength) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (this.options.showLength) {\n\t\t\tmeasurementString = L.Draw.Polyline.prototype._getMeasurementString.call(this);\n\t\t}\n\n\t\tif (area) {\n\t\t\tmeasurementString += '<br>' + L.GeometryUtil.readableArea(area, this.options.metric, this.options.precision);\n\t\t}\n\n\t\treturn measurementString;\n\t},\n\n\t_shapeIsValid: function () {\n\t\treturn this._markers.length >= 3;\n\t},\n\n\t_vertexChanged: function (latlng, added) {\n\t\tvar latLngs;\n\n\t\t// Check to see if we should show the area\n\t\tif (!this.options.allowIntersection && this.options.showArea) {\n\t\t\tlatLngs = this._poly.getLatLngs();\n\n\t\t\tthis._area = L.GeometryUtil.geodesicArea(latLngs);\n\t\t}\n\n\t\tL.Draw.Polyline.prototype._vertexChanged.call(this, latlng, added);\n\t},\n\n\t_cleanUpShape: function () {\n\t\tvar markerCount = this._markers.length;\n\n\t\tif (markerCount > 0) {\n\t\t\tthis._markers[0].off('click', this._finishShape, this);\n\n\t\t\tif (markerCount > 2) {\n\t\t\t\tthis._markers[markerCount - 1].off('dblclick', this._finishShape, this);\n\t\t\t}\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/draw/handler/Draw.Polyline.js",
    "content": "/**\n * @class L.Draw.Polyline\n * @aka Draw.Polyline\n * @inherits L.Draw.Feature\n */\nL.Draw.Polyline = L.Draw.Feature.extend({\n\tstatics: {\n\t\tTYPE: 'polyline'\n\t},\n\n\tPoly: L.Polyline,\n\n\toptions: {\n\t\tallowIntersection: true,\n\t\trepeatMode: false,\n\t\tdrawError: {\n\t\t\tcolor: '#b00b00',\n\t\t\ttimeout: 2500\n\t\t},\n\t\ticon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(8, 8),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon'\n\t\t}),\n\t\ttouchIcon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(20, 20),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'\n\t\t}),\n\t\tguidelineDistance: 20,\n\t\tmaxGuideLineLength: 4000,\n\t\tshapeOptions: {\n\t\t\tstroke: true,\n\t\t\tcolor: '#3388ff',\n\t\t\tweight: 4,\n\t\t\topacity: 0.5,\n\t\t\tfill: false,\n\t\t\tclickable: true\n\t\t},\n\t\tmetric: true, // Whether to use the metric measurement system or imperial\n\t\tfeet: true, // When not metric, to use feet instead of yards for display.\n\t\tnautic: false, // When not metric, not feet use nautic mile for display\n\t\tshowLength: true, // Whether to display distance in the tooltip\n\t\tzIndexOffset: 2000, // This should be > than the highest z-index any map layers\n\t\tfactor: 1, // To change distance calculation\n\t\tmaxPoints: 0 // Once this number of points are placed, finish shape\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\t// if touch, switch to touch icon\n\t\tif (L.Browser.touch) {\n\t\t\tthis.options.icon = this.options.touchIcon;\n\t\t}\n\n\t\t// Need to set this here to ensure the correct message is used.\n\t\tthis.options.drawError.message = L.drawLocal.draw.handlers.polyline.error;\n\n\t\t// Merge default drawError options with custom options\n\t\tif (options && options.drawError) {\n\t\t\toptions.drawError = L.Util.extend({}, this.options.drawError, options.drawError);\n\t\t}\n\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.Draw.Polyline.TYPE;\n\n\t\tL.Draw.Feature.prototype.initialize.call(this, map, options);\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler\n\taddHooks: function () {\n\t\tL.Draw.Feature.prototype.addHooks.call(this);\n\t\tif (this._map) {\n\t\t\tthis._markers = [];\n\n\t\t\tthis._markerGroup = new L.LayerGroup();\n\t\t\tthis._map.addLayer(this._markerGroup);\n\n\t\t\tthis._poly = new L.Polyline([], this.options.shapeOptions);\n\n\t\t\tthis._tooltip.updateContent(this._getTooltipText());\n\n\t\t\t// Make a transparent marker that will used to catch click events. These click\n\t\t\t// events will create the vertices. We need to do this so we can ensure that\n\t\t\t// we can create vertices over other map layers (markers, vector layers). We\n\t\t\t// also do not want to trigger any click handlers of objects we are clicking on\n\t\t\t// while drawing.\n\t\t\tif (!this._mouseMarker) {\n\t\t\t\tthis._mouseMarker = L.marker(this._map.getCenter(), {\n\t\t\t\t\ticon: L.divIcon({\n\t\t\t\t\t\tclassName: 'leaflet-mouse-marker',\n\t\t\t\t\t\ticonAnchor: [20, 20],\n\t\t\t\t\t\ticonSize: [40, 40]\n\t\t\t\t\t}),\n\t\t\t\t\topacity: 0,\n\t\t\t\t\tzIndexOffset: this.options.zIndexOffset\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthis._mouseMarker\n\t\t\t\t.on('mouseout', this._onMouseOut, this)\n\t\t\t\t.on('mousemove', this._onMouseMove, this) // Necessary to prevent 0.8 stutter\n\t\t\t\t.on('mousedown', this._onMouseDown, this)\n\t\t\t\t.on('mouseup', this._onMouseUp, this) // Necessary for 0.8 compatibility\n\t\t\t\t.addTo(this._map);\n\n\t\t\tthis._map\n\t\t\t\t.on('mouseup', this._onMouseUp, this) // Necessary for 0.7 compatibility\n\t\t\t\t.on('mousemove', this._onMouseMove, this)\n\t\t\t\t.on('zoomlevelschange', this._onZoomEnd, this)\n\t\t\t\t.on('touchstart', this._onTouch, this)\n\t\t\t\t.on('zoomend', this._onZoomEnd, this);\n\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler.\n\tremoveHooks: function () {\n\t\tL.Draw.Feature.prototype.removeHooks.call(this);\n\n\t\tthis._clearHideErrorTimeout();\n\n\t\tthis._cleanUpShape();\n\n\t\t// remove markers from map\n\t\tthis._map.removeLayer(this._markerGroup);\n\t\tdelete this._markerGroup;\n\t\tdelete this._markers;\n\n\t\tthis._map.removeLayer(this._poly);\n\t\tdelete this._poly;\n\n\t\tthis._mouseMarker\n\t\t\t.off('mousedown', this._onMouseDown, this)\n\t\t\t.off('mouseout', this._onMouseOut, this)\n\t\t\t.off('mouseup', this._onMouseUp, this)\n\t\t\t.off('mousemove', this._onMouseMove, this);\n\t\tthis._map.removeLayer(this._mouseMarker);\n\t\tdelete this._mouseMarker;\n\n\t\t// clean up DOM\n\t\tthis._clearGuides();\n\n\t\tthis._map\n\t\t\t.off('mouseup', this._onMouseUp, this)\n\t\t\t.off('mousemove', this._onMouseMove, this)\n\t\t\t.off('zoomlevelschange', this._onZoomEnd, this)\n\t\t\t.off('zoomend', this._onZoomEnd, this)\n\t\t\t.off('touchstart', this._onTouch, this)\n\t\t\t.off('click', this._onTouch, this);\n\t},\n\n\t// @method deleteLastVertex(): void\n\t// Remove the last vertex from the polyline, removes polyline from map if only one point exists.\n\tdeleteLastVertex: function () {\n\t\tif (this._markers.length <= 1) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar lastMarker = this._markers.pop(),\n\t\t\tpoly = this._poly,\n\t\t\t// Replaces .spliceLatLngs()\n\t\t\tlatlngs = poly.getLatLngs(),\n\t\t\tlatlng = latlngs.splice(-1, 1)[0];\n\t\tthis._poly.setLatLngs(latlngs);\n\n\t\tthis._markerGroup.removeLayer(lastMarker);\n\n\t\tif (poly.getLatLngs().length < 2) {\n\t\t\tthis._map.removeLayer(poly);\n\t\t}\n\n\t\tthis._vertexChanged(latlng, false);\n\t},\n\n\t// @method addVertex(): void\n\t// Add a vertex to the end of the polyline\n\taddVertex: function (latlng) {\n\t\tvar markersLength = this._markers.length;\n\t\t// markersLength must be greater than or equal to 2 before intersections can occur\n\t\tif (markersLength >= 2 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) {\n\t\t\tthis._showErrorTooltip();\n\t\t\treturn;\n\t\t}\n\t\telse if (this._errorShown) {\n\t\t\tthis._hideErrorTooltip();\n\t\t}\n\n\t\tthis._markers.push(this._createMarker(latlng));\n\n\t\tthis._poly.addLatLng(latlng);\n\n\t\tif (this._poly.getLatLngs().length === 2) {\n\t\t\tthis._map.addLayer(this._poly);\n\t\t}\n\n\t\tthis._vertexChanged(latlng, true);\n\t},\n\n\t// @method completeShape(): void\n\t// Closes the polyline between the first and last points\n\tcompleteShape: function () {\n\t\tif (this._markers.length <= 1 || !this._shapeIsValid()) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._fireCreatedEvent();\n\t\tthis.disable();\n\n\t\tif (this.options.repeatMode) {\n\t\t\tthis.enable();\n\t\t}\n\t},\n\n\t_finishShape: function () {\n\t\tvar latlngs = this._poly._defaultShape ? this._poly._defaultShape() : this._poly.getLatLngs();\n\t\tvar intersects = this._poly.newLatLngIntersects(latlngs[latlngs.length - 1]);\n\n\t\tif ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) {\n\t\t\tthis._showErrorTooltip();\n\t\t\treturn;\n\t\t}\n\n\t\tthis._fireCreatedEvent();\n\t\tthis.disable();\n\t\tif (this.options.repeatMode) {\n\t\t\tthis.enable();\n\t\t}\n\t},\n\n\t// Called to verify the shape is valid when the user tries to finish it\n\t// Return false if the shape is not valid\n\t_shapeIsValid: function () {\n\t\treturn true;\n\t},\n\n\t_onZoomEnd: function () {\n\t\tif (this._markers !== null) {\n\t\t\tthis._updateGuide();\n\t\t}\n\t},\n\n\t_onMouseMove: function (e) {\n\t\tvar newPos = this._map.mouseEventToLayerPoint(e.originalEvent);\n\t\tvar latlng = this._map.layerPointToLatLng(newPos);\n\n\t\t// Save latlng\n\t\t// should this be moved to _updateGuide() ?\n\t\tthis._currentLatLng = latlng;\n\n\t\tthis._updateTooltip(latlng);\n\n\t\t// Update the guide line\n\t\tthis._updateGuide(newPos);\n\n\t\t// Update the mouse marker position\n\t\tthis._mouseMarker.setLatLng(latlng);\n\n\t\tL.DomEvent.preventDefault(e.originalEvent);\n\t},\n\n\t_vertexChanged: function (latlng, added) {\n\t\tthis._map.fire(L.Draw.Event.DRAWVERTEX, {layers: this._markerGroup});\n\t\tthis._updateFinishHandler();\n\n\t\tthis._updateRunningMeasure(latlng, added);\n\n\t\tthis._clearGuides();\n\n\t\tthis._updateTooltip();\n\t},\n\n\t_onMouseDown: function (e) {\n\t\tif (!this._clickHandled && !this._touchHandled && !this._disableMarkers) {\n\t\t\tthis._onMouseMove(e);\n\t\t\tthis._clickHandled = true;\n\t\t\tthis._disableNewMarkers();\n\t\t\tvar originalEvent = e.originalEvent;\n\t\t\tvar clientX = originalEvent.clientX;\n\t\t\tvar clientY = originalEvent.clientY;\n\t\t\tthis._startPoint.call(this, clientX, clientY);\n\t\t}\n\t},\n\n\t_startPoint: function (clientX, clientY) {\n\t\tthis._mouseDownOrigin = L.point(clientX, clientY);\n\t},\n\n\t_onMouseUp: function (e) {\n\t\tvar originalEvent = e.originalEvent;\n\t\tvar clientX = originalEvent.clientX;\n\t\tvar clientY = originalEvent.clientY;\n\t\tthis._endPoint.call(this, clientX, clientY, e);\n\t\tthis._clickHandled = null;\n\t},\n\n\t_endPoint: function (clientX, clientY, e) {\n\t\tif (this._mouseDownOrigin) {\n\t\t\tvar dragCheckDistance = L.point(clientX, clientY)\n\t\t\t\t.distanceTo(this._mouseDownOrigin);\n\t\t\tvar lastPtDistance = this._calculateFinishDistance(e.latlng);\n\t\t\tif (this.options.maxPoints > 1 && this.options.maxPoints == this._markers.length + 1) {\n\t\t\t\tthis.addVertex(e.latlng);\n\t\t\t\tthis._finishShape();\n\t\t\t} else if (lastPtDistance < 10 && L.Browser.touch) {\n\t\t\t\tthis._finishShape();\n\t\t\t} else if (Math.abs(dragCheckDistance) < 9 * (window.devicePixelRatio || 1)) {\n\t\t\t\tthis.addVertex(e.latlng);\n\t\t\t}\n\t\t\tthis._enableNewMarkers(); // after a short pause, enable new markers\n\t\t}\n\t\tthis._mouseDownOrigin = null;\n\t},\n\n\t// ontouch prevented by clickHandled flag because some browsers fire both click/touch events,\n\t// causing unwanted behavior\n\t_onTouch: function (e) {\n\t\tvar originalEvent = e.originalEvent;\n\t\tvar clientX;\n\t\tvar clientY;\n\t\tif (originalEvent.touches && originalEvent.touches[0] && !this._clickHandled && !this._touchHandled && !this._disableMarkers) {\n\t\t\tclientX = originalEvent.touches[0].clientX;\n\t\t\tclientY = originalEvent.touches[0].clientY;\n\t\t\tthis._disableNewMarkers();\n\t\t\tthis._touchHandled = true;\n\t\t\tthis._startPoint.call(this, clientX, clientY);\n\t\t\tthis._endPoint.call(this, clientX, clientY, e);\n\t\t\tthis._touchHandled = null;\n\t\t}\n\t\tthis._clickHandled = null;\n\t},\n\n\t_onMouseOut: function () {\n\t\tif (this._tooltip) {\n\t\t\tthis._tooltip._onMouseOut.call(this._tooltip);\n\t\t}\n\t},\n\n\t// calculate if we are currently within close enough distance\n\t// of the closing point (first point for shapes, last point for lines)\n\t// this is semi-ugly code but the only reliable way i found to get the job done\n\t// note: calculating point.distanceTo between mouseDownOrigin and last marker did NOT work\n\t_calculateFinishDistance: function (potentialLatLng) {\n\t\tvar lastPtDistance;\n\t\tif (this._markers.length > 0) {\n\t\t\tvar finishMarker;\n\t\t\tif (this.type === L.Draw.Polyline.TYPE) {\n\t\t\t\tfinishMarker = this._markers[this._markers.length - 1];\n\t\t\t} else if (this.type === L.Draw.Polygon.TYPE) {\n\t\t\t\tfinishMarker = this._markers[0];\n\t\t\t} else {\n\t\t\t\treturn Infinity;\n\t\t\t}\n\t\t\tvar lastMarkerPoint = this._map.latLngToContainerPoint(finishMarker.getLatLng()),\n\t\t\t\tpotentialMarker = new L.Marker(potentialLatLng, {\n\t\t\t\t\ticon: this.options.icon,\n\t\t\t\t\tzIndexOffset: this.options.zIndexOffset * 2\n\t\t\t\t});\n\t\t\tvar potentialMarkerPint = this._map.latLngToContainerPoint(potentialMarker.getLatLng());\n\t\t\tlastPtDistance = lastMarkerPoint.distanceTo(potentialMarkerPint);\n\t\t} else {\n\t\t\tlastPtDistance = Infinity;\n\t\t}\n\t\treturn lastPtDistance;\n\t},\n\n\t_updateFinishHandler: function () {\n\t\tvar markerCount = this._markers.length;\n\t\t// The last marker should have a click handler to close the polyline\n\t\tif (markerCount > 1) {\n\t\t\tthis._markers[markerCount - 1].on('click', this._finishShape, this);\n\t\t}\n\n\t\t// Remove the old marker click handler (as only the last point should close the polyline)\n\t\tif (markerCount > 2) {\n\t\t\tthis._markers[markerCount - 2].off('click', this._finishShape, this);\n\t\t}\n\t},\n\n\t_createMarker: function (latlng) {\n\t\tvar marker = new L.Marker(latlng, {\n\t\t\ticon: this.options.icon,\n\t\t\tzIndexOffset: this.options.zIndexOffset * 2\n\t\t});\n\n\t\tthis._markerGroup.addLayer(marker);\n\n\t\treturn marker;\n\t},\n\n\t_updateGuide: function (newPos) {\n\t\tvar markerCount = this._markers ? this._markers.length : 0;\n\n\t\tif (markerCount > 0) {\n\t\t\tnewPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng);\n\n\t\t\t// draw the guide line\n\t\t\tthis._clearGuides();\n\t\t\tthis._drawGuide(\n\t\t\t\tthis._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()),\n\t\t\t\tnewPos\n\t\t\t);\n\t\t}\n\t},\n\n\t_updateTooltip: function (latLng) {\n\t\tvar text = this._getTooltipText();\n\n\t\tif (latLng) {\n\t\t\tthis._tooltip.updatePosition(latLng);\n\t\t}\n\n\t\tif (!this._errorShown) {\n\t\t\tthis._tooltip.updateContent(text);\n\t\t}\n\t},\n\n\t_drawGuide: function (pointA, pointB) {\n\t\tvar length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))),\n\t\t\tguidelineDistance = this.options.guidelineDistance,\n\t\t\tmaxGuideLineLength = this.options.maxGuideLineLength,\n\t\t\t// Only draw a guideline with a max length\n\t\t\ti = length > maxGuideLineLength ? length - maxGuideLineLength : guidelineDistance,\n\t\t\tfraction,\n\t\t\tdashPoint,\n\t\t\tdash;\n\n\t\t//create the guides container if we haven't yet\n\t\tif (!this._guidesContainer) {\n\t\t\tthis._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane);\n\t\t}\n\n\t\t//draw a dash every GuildeLineDistance\n\t\tfor (; i < length; i += this.options.guidelineDistance) {\n\t\t\t//work out fraction along line we are\n\t\t\tfraction = i / length;\n\n\t\t\t//calculate new x,y point\n\t\t\tdashPoint = {\n\t\t\t\tx: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)),\n\t\t\t\ty: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y))\n\t\t\t};\n\n\t\t\t//add guide dash to guide container\n\t\t\tdash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer);\n\t\t\tdash.style.backgroundColor =\n\t\t\t\t!this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color;\n\n\t\t\tL.DomUtil.setPosition(dash, dashPoint);\n\t\t}\n\t},\n\n\t_updateGuideColor: function (color) {\n\t\tif (this._guidesContainer) {\n\t\t\tfor (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) {\n\t\t\t\tthis._guidesContainer.childNodes[i].style.backgroundColor = color;\n\t\t\t}\n\t\t}\n\t},\n\n\t// removes all child elements (guide dashes) from the guides container\n\t_clearGuides: function () {\n\t\tif (this._guidesContainer) {\n\t\t\twhile (this._guidesContainer.firstChild) {\n\t\t\t\tthis._guidesContainer.removeChild(this._guidesContainer.firstChild);\n\t\t\t}\n\t\t}\n\t},\n\n\t_getTooltipText: function () {\n\t\tvar showLength = this.options.showLength,\n\t\t\tlabelText, distanceStr;\n\t\tif (this._markers.length === 0) {\n\t\t\tlabelText = {\n\t\t\t\ttext: L.drawLocal.draw.handlers.polyline.tooltip.start\n\t\t\t};\n\t\t} else {\n\t\t\tdistanceStr = showLength ? this._getMeasurementString() : '';\n\n\t\t\tif (this._markers.length === 1) {\n\t\t\t\tlabelText = {\n\t\t\t\t\ttext: L.drawLocal.draw.handlers.polyline.tooltip.cont,\n\t\t\t\t\tsubtext: distanceStr\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tlabelText = {\n\t\t\t\t\ttext: L.drawLocal.draw.handlers.polyline.tooltip.end,\n\t\t\t\t\tsubtext: distanceStr\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn labelText;\n\t},\n\n\t_updateRunningMeasure: function (latlng, added) {\n\t\tvar markersLength = this._markers.length,\n\t\t\tpreviousMarkerIndex, distance;\n\n\t\tif (this._markers.length === 1) {\n\t\t\tthis._measurementRunningTotal = 0;\n\t\t} else {\n\t\t\tpreviousMarkerIndex = markersLength - (added ? 2 : 1);\n\n\t\t\t// Calculate the distance based on the version\n\t\t\tif (L.GeometryUtil.isVersion07x()) {\n\t\t\t\tdistance = latlng.distanceTo(this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1);\n\t\t\t} else {\n\t\t\t\tdistance = this._map.distance(latlng, this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1);\n\t\t\t}\n\n\t\t\tthis._measurementRunningTotal += distance * (added ? 1 : -1);\n\t\t}\n\t},\n\n\t_getMeasurementString: function () {\n\t\tvar currentLatLng = this._currentLatLng,\n\t\t\tpreviousLatLng = this._markers[this._markers.length - 1].getLatLng(),\n\t\t\tdistance;\n\n\t\t// Calculate the distance from the last fixed point to the mouse position based on the version\n\t\tif (L.GeometryUtil.isVersion07x()) {\n\t\t\tdistance = previousLatLng && currentLatLng && currentLatLng.distanceTo ? this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0;\n\t\t} else {\n\t\t\tdistance = previousLatLng && currentLatLng ? this._measurementRunningTotal + this._map.distance(currentLatLng, previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0;\n\t\t}\n\n\t\treturn L.GeometryUtil.readableDistance(distance, this.options.metric, this.options.feet, this.options.nautic, this.options.precision);\n\t},\n\n\t_showErrorTooltip: function () {\n\t\tthis._errorShown = true;\n\n\t\t// Update tooltip\n\t\tthis._tooltip\n\t\t\t.showAsError()\n\t\t\t.updateContent({text: this.options.drawError.message});\n\n\t\t// Update shape\n\t\tthis._updateGuideColor(this.options.drawError.color);\n\t\tthis._poly.setStyle({color: this.options.drawError.color});\n\n\t\t// Hide the error after 2 seconds\n\t\tthis._clearHideErrorTimeout();\n\t\tthis._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout);\n\t},\n\n\t_hideErrorTooltip: function () {\n\t\tthis._errorShown = false;\n\n\t\tthis._clearHideErrorTimeout();\n\n\t\t// Revert tooltip\n\t\tthis._tooltip\n\t\t\t.removeError()\n\t\t\t.updateContent(this._getTooltipText());\n\n\t\t// Revert shape\n\t\tthis._updateGuideColor(this.options.shapeOptions.color);\n\t\tthis._poly.setStyle({color: this.options.shapeOptions.color});\n\t},\n\n\t_clearHideErrorTimeout: function () {\n\t\tif (this._hideErrorTimeout) {\n\t\t\tclearTimeout(this._hideErrorTimeout);\n\t\t\tthis._hideErrorTimeout = null;\n\t\t}\n\t},\n\n\t// disable new markers temporarily;\n\t// this is to prevent duplicated touch/click events in some browsers\n\t_disableNewMarkers: function () {\n\t\tthis._disableMarkers = true;\n\t},\n\n\t// see _disableNewMarkers\n\t_enableNewMarkers: function () {\n\t\tsetTimeout(function () {\n\t\t\tthis._disableMarkers = false;\n\t\t}.bind(this), 50);\n\t},\n\n\t_cleanUpShape: function () {\n\t\tif (this._markers.length > 1) {\n\t\t\tthis._markers[this._markers.length - 1].off('click', this._finishShape, this);\n\t\t}\n\t},\n\n\t_fireCreatedEvent: function () {\n\t\tvar poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions);\n\t\tL.Draw.Feature.prototype._fireCreatedEvent.call(this, poly);\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/draw/handler/Draw.Rectangle.js",
    "content": "/**\n * @class L.Draw.Rectangle\n * @aka Draw.Rectangle\n * @inherits L.Draw.SimpleShape\n */\nL.Draw.Rectangle = L.Draw.SimpleShape.extend({\n\tstatics: {\n\t\tTYPE: 'rectangle'\n\t},\n\n\toptions: {\n\t\tshapeOptions: {\n\t\t\tstroke: true,\n\t\t\tcolor: '#3388ff',\n\t\t\tweight: 4,\n\t\t\topacity: 0.5,\n\t\t\tfill: true,\n\t\t\tfillColor: null, //same as color by default\n\t\t\tfillOpacity: 0.2,\n\t\t\tshowArea: true,\n\t\t\tclickable: true\n\t\t},\n\t\tmetric: true // Whether to use the metric measurement system or imperial\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.Draw.Rectangle.TYPE;\n\n\t\tthis._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start;\n\n\t\tL.Draw.SimpleShape.prototype.initialize.call(this, map, options);\n\t},\n\n\t// @method disable(): void\n\tdisable: function () {\n\t\tif (!this._enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._isCurrentlyTwoClickDrawing = false;\n\t\tL.Draw.SimpleShape.prototype.disable.call(this);\n\t},\n\n\t_onMouseUp: function (e) {\n\t\tif (!this._shape && !this._isCurrentlyTwoClickDrawing) {\n\t\t\tthis._isCurrentlyTwoClickDrawing = true;\n\t\t\treturn;\n\t\t}\n\n\t\t// Make sure closing click is on map\n\t\tif (this._isCurrentlyTwoClickDrawing && !_hasAncestor(e.target, 'leaflet-pane')) {\n\t\t\treturn;\n\t\t}\n\n\t\tL.Draw.SimpleShape.prototype._onMouseUp.call(this);\n\t},\n\n\t_drawShape: function (latlng) {\n\t\tif (!this._shape) {\n\t\t\tthis._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions);\n\t\t\tthis._map.addLayer(this._shape);\n\t\t} else {\n\t\t\tthis._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng));\n\t\t}\n\t},\n\n\t_fireCreatedEvent: function () {\n\t\tvar rectangle = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions);\n\t\tL.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle);\n\t},\n\n\t_getTooltipText: function () {\n\t\tvar tooltipText = L.Draw.SimpleShape.prototype._getTooltipText.call(this),\n\t\t\tshape = this._shape,\n\t\t\tshowArea = this.options.showArea,\n\t\t\tlatLngs, area, subtext;\n\n\t\tif (shape) {\n\t\t\tlatLngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs();\n\t\t\tarea = L.GeometryUtil.geodesicArea(latLngs);\n\t\t\tsubtext = showArea ? L.GeometryUtil.readableArea(area, this.options.metric) : '';\n\t\t}\n\n\t\treturn {\n\t\t\ttext: tooltipText.text,\n\t\t\tsubtext: subtext\n\t\t};\n\t}\n});\n\nfunction _hasAncestor(el, cls) {\n\twhile ((el = el.parentElement) && !el.classList.contains(cls)) {\n\t\t;\n\t}\n\treturn el;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/draw/handler/Draw.SimpleShape.js",
    "content": "L.SimpleShape = {};\n/**\n * @class L.Draw.SimpleShape\n * @aka Draw.SimpleShape\n * @inherits L.Draw.Feature\n */\nL.Draw.SimpleShape = L.Draw.Feature.extend({\n\toptions: {\n\t\trepeatMode: false\n\t},\n\n\t// @method initialize(): void\n\tinitialize: function (map, options) {\n\t\tthis._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end;\n\n\t\tL.Draw.Feature.prototype.initialize.call(this, map, options);\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler.\n\taddHooks: function () {\n\t\tL.Draw.Feature.prototype.addHooks.call(this);\n\t\tif (this._map) {\n\t\t\tthis._mapDraggable = this._map.dragging.enabled();\n\n\t\t\tif (this._mapDraggable) {\n\t\t\t\tthis._map.dragging.disable();\n\t\t\t}\n\n\t\t\t//TODO refactor: move cursor to styles\n\t\t\tthis._container.style.cursor = 'crosshair';\n\n\t\t\tthis._tooltip.updateContent({text: this._initialLabelText});\n\n\t\t\tthis._map\n\t\t\t\t.on('mousedown', this._onMouseDown, this)\n\t\t\t\t.on('mousemove', this._onMouseMove, this)\n\t\t\t\t.on('touchstart', this._onMouseDown, this)\n\t\t\t\t.on('touchmove', this._onMouseMove, this);\n\n\t\t\t// we should prevent default, otherwise default behavior (scrolling) will fire,\n\t\t\t// and that will cause document.touchend to fire and will stop the drawing\n\t\t\t// (circle, rectangle) in touch mode.\n\t\t\t// (update): we have to send passive now to prevent scroll, because by default it is {passive: true} now, which means,\n\t\t\t// handler can't event.preventDefault\n\t\t\t// check the news https://developers.google.com/web/updates/2016/06/passive-event-listeners\n\t\t\tdocument.addEventListener('touchstart', L.DomEvent.preventDefault, {passive: false});\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler.\n\tremoveHooks: function () {\n\t\tL.Draw.Feature.prototype.removeHooks.call(this);\n\t\tif (this._map) {\n\t\t\tif (this._mapDraggable) {\n\t\t\t\tthis._map.dragging.enable();\n\t\t\t}\n\n\t\t\t//TODO refactor: move cursor to styles\n\t\t\tthis._container.style.cursor = '';\n\n\t\t\tthis._map\n\t\t\t\t.off('mousedown', this._onMouseDown, this)\n\t\t\t\t.off('mousemove', this._onMouseMove, this)\n\t\t\t\t.off('touchstart', this._onMouseDown, this)\n\t\t\t\t.off('touchmove', this._onMouseMove, this);\n\n\t\t\tL.DomEvent.off(document, 'mouseup', this._onMouseUp, this);\n\t\t\tL.DomEvent.off(document, 'touchend', this._onMouseUp, this);\n\n\t\t\tdocument.removeEventListener('touchstart', L.DomEvent.preventDefault);\n\n\t\t\t// If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return\n\t\t\tif (this._shape) {\n\t\t\t\tthis._map.removeLayer(this._shape);\n\t\t\t\tdelete this._shape;\n\t\t\t}\n\t\t}\n\t\tthis._isDrawing = false;\n\t},\n\n\t_getTooltipText: function () {\n\t\treturn {\n\t\t\ttext: this._endLabelText\n\t\t};\n\t},\n\n\t_onMouseDown: function (e) {\n\t\tthis._isDrawing = true;\n\t\tthis._startLatLng = e.latlng;\n\n\t\tL.DomEvent\n\t\t\t.on(document, 'mouseup', this._onMouseUp, this)\n\t\t\t.on(document, 'touchend', this._onMouseUp, this)\n\t\t\t.preventDefault(e.originalEvent);\n\t},\n\n\t_onMouseMove: function (e) {\n\t\tvar latlng = e.latlng;\n\n\t\tthis._tooltip.updatePosition(latlng);\n\t\tif (this._isDrawing) {\n\t\t\tthis._tooltip.updateContent(this._getTooltipText());\n\t\t\tthis._drawShape(latlng);\n\t\t}\n\t},\n\n\t_onMouseUp: function () {\n\t\tif (this._shape) {\n\t\t\tthis._fireCreatedEvent();\n\t\t}\n\n\t\tthis.disable();\n\t\tif (this.options.repeatMode) {\n\t\t\tthis.enable();\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/edit/EditToolbar.js",
    "content": "/*L.Map.mergeOptions({\n editControl: true\n });*/\n/**\n * @class L.EditToolbar\n * @aka EditToolbar\n */\nL.EditToolbar = L.Toolbar.extend({\n\tstatics: {\n\t\tTYPE: 'edit'\n\t},\n\n\toptions: {\n\t\tedit: {\n\t\t\tselectedPathOptions: {\n\t\t\t\tdashArray: '10, 10',\n\n\t\t\t\tfill: true,\n\t\t\t\tfillColor: '#fe57a1',\n\t\t\t\tfillOpacity: 0.1,\n\n\t\t\t\t// Whether to user the existing layers color\n\t\t\t\tmaintainColor: false\n\t\t\t}\n\t\t},\n\t\tremove: {},\n\t\tpoly: null,\n\t\tfeatureGroup: null /* REQUIRED! TODO: perhaps if not set then all layers on the map are selectable? */\n\t},\n\n\t// @method intialize(): void\n\tinitialize: function (options) {\n\t\t// Need to set this manually since null is an acceptable value here\n\t\tif (options.edit) {\n\t\t\tif (typeof options.edit.selectedPathOptions === 'undefined') {\n\t\t\t\toptions.edit.selectedPathOptions = this.options.edit.selectedPathOptions;\n\t\t\t}\n\t\t\toptions.edit.selectedPathOptions = L.extend({}, this.options.edit.selectedPathOptions, options.edit.selectedPathOptions);\n\t\t}\n\n\t\tif (options.remove) {\n\t\t\toptions.remove = L.extend({}, this.options.remove, options.remove);\n\t\t}\n\n\t\tif (options.poly) {\n\t\t\toptions.poly = L.extend({}, this.options.poly, options.poly);\n\t\t}\n\n\t\tthis._toolbarClass = 'leaflet-draw-edit';\n\t\tL.Toolbar.prototype.initialize.call(this, options);\n\n\t\tthis._selectedFeatureCount = 0;\n\t},\n\n\t// @method getModeHandlers(): object\n\t// Get mode handlers information\n\tgetModeHandlers: function (map) {\n\t\tvar featureGroup = this.options.featureGroup;\n\t\treturn [\n\t\t\t{\n\t\t\t\tenabled: this.options.edit,\n\t\t\t\thandler: new L.EditToolbar.Edit(map, {\n\t\t\t\t\tfeatureGroup: featureGroup,\n\t\t\t\t\tselectedPathOptions: this.options.edit.selectedPathOptions,\n\t\t\t\t\tpoly: this.options.poly\n\t\t\t\t}),\n\t\t\t\ttitle: L.drawLocal.edit.toolbar.buttons.edit\n\t\t\t},\n\t\t\t{\n\t\t\t\tenabled: this.options.remove,\n\t\t\t\thandler: new L.EditToolbar.Delete(map, {\n\t\t\t\t\tfeatureGroup: featureGroup\n\t\t\t\t}),\n\t\t\t\ttitle: L.drawLocal.edit.toolbar.buttons.remove\n\t\t\t}\n\t\t];\n\t},\n\n\t// @method getActions(): object\n\t// Get actions information\n\tgetActions: function (handler) {\n\t\tvar actions = [\n\t\t\t{\n\t\t\t\ttitle: L.drawLocal.edit.toolbar.actions.save.title,\n\t\t\t\ttext: L.drawLocal.edit.toolbar.actions.save.text,\n\t\t\t\tcallback: this._save,\n\t\t\t\tcontext: this\n\t\t\t},\n\t\t\t{\n\t\t\t\ttitle: L.drawLocal.edit.toolbar.actions.cancel.title,\n\t\t\t\ttext: L.drawLocal.edit.toolbar.actions.cancel.text,\n\t\t\t\tcallback: this.disable,\n\t\t\t\tcontext: this\n\t\t\t}\n\t\t];\n\n\t\tif (handler.removeAllLayers) {\n\t\t\tactions.push({\n\t\t\t\ttitle: L.drawLocal.edit.toolbar.actions.clearAll.title,\n\t\t\t\ttext: L.drawLocal.edit.toolbar.actions.clearAll.text,\n\t\t\t\tcallback: this._clearAllLayers,\n\t\t\t\tcontext: this\n\t\t\t});\n\t\t}\n\n\t\treturn actions;\n\t},\n\n\t// @method addToolbar(map): L.DomUtil\n\t// Adds the toolbar to the map\n\taddToolbar: function (map) {\n\t\tvar container = L.Toolbar.prototype.addToolbar.call(this, map);\n\n\t\tthis._checkDisabled();\n\n\t\tthis.options.featureGroup.on('layeradd layerremove', this._checkDisabled, this);\n\n\t\treturn container;\n\t},\n\n\t// @method removeToolbar(): void\n\t// Removes the toolbar from the map\n\tremoveToolbar: function () {\n\t\tthis.options.featureGroup.off('layeradd layerremove', this._checkDisabled, this);\n\n\t\tL.Toolbar.prototype.removeToolbar.call(this);\n\t},\n\n\t// @method disable(): void\n\t// Disables the toolbar\n\tdisable: function () {\n\t\tif (!this.enabled()) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._activeMode.handler.revertLayers();\n\n\t\tL.Toolbar.prototype.disable.call(this);\n\t},\n\n\t_save: function () {\n\t\tthis._activeMode.handler.save();\n\t\tif (this._activeMode) {\n\t\t\tthis._activeMode.handler.disable();\n\t\t}\n\t},\n\n\t_clearAllLayers: function () {\n\t\tthis._activeMode.handler.removeAllLayers();\n\t\tif (this._activeMode) {\n\t\t\tthis._activeMode.handler.disable();\n\t\t}\n\t},\n\n\t_checkDisabled: function () {\n\t\tvar featureGroup = this.options.featureGroup,\n\t\t\thasLayers = featureGroup.getLayers().length !== 0,\n\t\t\tbutton;\n\n\t\tif (this.options.edit) {\n\t\t\tbutton = this._modes[L.EditToolbar.Edit.TYPE].button;\n\n\t\t\tif (hasLayers) {\n\t\t\t\tL.DomUtil.removeClass(button, 'leaflet-disabled');\n\t\t\t} else {\n\t\t\t\tL.DomUtil.addClass(button, 'leaflet-disabled');\n\t\t\t}\n\n\t\t\tbutton.setAttribute(\n\t\t\t\t'title',\n\t\t\t\thasLayers ?\n\t\t\t\t\tL.drawLocal.edit.toolbar.buttons.edit\n\t\t\t\t\t: L.drawLocal.edit.toolbar.buttons.editDisabled\n\t\t\t);\n\t\t}\n\n\t\tif (this.options.remove) {\n\t\t\tbutton = this._modes[L.EditToolbar.Delete.TYPE].button;\n\n\t\t\tif (hasLayers) {\n\t\t\t\tL.DomUtil.removeClass(button, 'leaflet-disabled');\n\t\t\t} else {\n\t\t\t\tL.DomUtil.addClass(button, 'leaflet-disabled');\n\t\t\t}\n\n\t\t\tbutton.setAttribute(\n\t\t\t\t'title',\n\t\t\t\thasLayers ?\n\t\t\t\t\tL.drawLocal.edit.toolbar.buttons.remove\n\t\t\t\t\t: L.drawLocal.edit.toolbar.buttons.removeDisabled\n\t\t\t);\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/edit/handler/Edit.Circle.js",
    "content": "L.Edit = L.Edit || {};\n/**\n * @class L.Edit.Circle\n * @aka Edit.Circle\n * @inherits L.Edit.CircleMarker\n */\nL.Edit.Circle = L.Edit.CircleMarker.extend({\n\n\t_createResizeMarker: function () {\n\t\tvar center = this._shape.getLatLng(),\n\t\t\tresizemarkerPoint = this._getResizeMarkerPoint(center);\n\n\t\tthis._resizeMarkers = [];\n\t\tthis._resizeMarkers.push(this._createMarker(resizemarkerPoint, this.options.resizeIcon));\n\t},\n\n\t_getResizeMarkerPoint: function (latlng) {\n\t\t// From L.shape.getBounds()\n\t\tvar delta = this._shape._radius * Math.cos(Math.PI / 4),\n\t\t\tpoint = this._map.project(latlng);\n\t\treturn this._map.unproject([point.x + delta, point.y - delta]);\n\t},\n\n\t_resize: function (latlng) {\n\t\tvar moveLatLng = this._moveMarker.getLatLng();\n\n\t\t// Calculate the radius based on the version\n\t\tif (L.GeometryUtil.isVersion07x()) {\n\t\t\tradius = moveLatLng.distanceTo(latlng);\n\t\t} else {\n\t\t\tradius = this._map.distance(moveLatLng, latlng);\n\t\t}\n\t\tthis._shape.setRadius(radius);\n\n\t\tif (this._map.editTooltip) {\n\t\t\tthis._map._editTooltip.updateContent({\n\t\t\t\ttext: L.drawLocal.edit.handlers.edit.tooltip.subtext + '<br />' + L.drawLocal.edit.handlers.edit.tooltip.text,\n\t\t\t\tsubtext: L.drawLocal.draw.handlers.circle.radius + ': ' +\n\t\t\t\tL.GeometryUtil.readableDistance(radius, true, this.options.feet, this.options.nautic)\n\t\t\t});\n\t\t}\n\n\t\tthis._shape.setRadius(radius);\n\n\t\tthis._map.fire(L.Draw.Event.EDITRESIZE, {layer: this._shape});\n\t}\n});\n\nL.Circle.addInitHook(function () {\n\tif (L.Edit.Circle) {\n\t\tthis.editing = new L.Edit.Circle(this);\n\n\t\tif (this.options.editable) {\n\t\t\tthis.editing.enable();\n\t\t}\n\t}\n\n\tthis.on('add', function () {\n\t\tif (this.editing && this.editing.enabled()) {\n\t\t\tthis.editing.addHooks();\n\t\t}\n\t});\n\n\tthis.on('remove', function () {\n\t\tif (this.editing && this.editing.enabled()) {\n\t\t\tthis.editing.removeHooks();\n\t\t}\n\t});\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/edit/handler/Edit.CircleMarker.js",
    "content": "L.Edit = L.Edit || {};\n/**\n * @class L.Edit.CircleMarker\n * @aka Edit.Circle\n * @inherits L.Edit.SimpleShape\n */\nL.Edit.CircleMarker = L.Edit.SimpleShape.extend({\n\t_createMoveMarker: function () {\n\t\tvar center = this._shape.getLatLng();\n\n\t\tthis._moveMarker = this._createMarker(center, this.options.moveIcon);\n\t},\n\n\t_createResizeMarker: function () {\n\t\t// To avoid an undefined check in L.Edit.SimpleShape.removeHooks\n\t\tthis._resizeMarkers = [];\n\t},\n\n\t_move: function (latlng) {\n\t\tif (this._resizeMarkers.length) {\n\t\t\tvar resizemarkerPoint = this._getResizeMarkerPoint(latlng);\n\t\t\t// Move the resize marker\n\t\t\tthis._resizeMarkers[0].setLatLng(resizemarkerPoint);\n\t\t}\n\n\t\t// Move the circle\n\t\tthis._shape.setLatLng(latlng);\n\n\t\tthis._map.fire(L.Draw.Event.EDITMOVE, {layer: this._shape});\n\t},\n});\n\nL.CircleMarker.addInitHook(function () {\n\tif (L.Edit.CircleMarker) {\n\t\tthis.editing = new L.Edit.CircleMarker(this);\n\n\t\tif (this.options.editable) {\n\t\t\tthis.editing.enable();\n\t\t}\n\t}\n\n\tthis.on('add', function () {\n\t\tif (this.editing && this.editing.enabled()) {\n\t\t\tthis.editing.addHooks();\n\t\t}\n\t});\n\n\tthis.on('remove', function () {\n\t\tif (this.editing && this.editing.enabled()) {\n\t\t\tthis.editing.removeHooks();\n\t\t}\n\t});\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/edit/handler/Edit.Marker.js",
    "content": "L.Edit = L.Edit || {};\n\n/**\n * @class L.Edit.Marker\n * @aka Edit.Marker\n */\nL.Edit.Marker = L.Handler.extend({\n\t// @method initialize(): void\n\tinitialize: function (marker, options) {\n\t\tthis._marker = marker;\n\t\tL.setOptions(this, options);\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler\n\taddHooks: function () {\n\t\tvar marker = this._marker;\n\n\t\tmarker.dragging.enable();\n\t\tmarker.on('dragend', this._onDragEnd, marker);\n\t\tthis._toggleMarkerHighlight();\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler\n\tremoveHooks: function () {\n\t\tvar marker = this._marker;\n\n\t\tmarker.dragging.disable();\n\t\tmarker.off('dragend', this._onDragEnd, marker);\n\t\tthis._toggleMarkerHighlight();\n\t},\n\n\t_onDragEnd: function (e) {\n\t\tvar layer = e.target;\n\t\tlayer.edited = true;\n\t\tthis._map.fire(L.Draw.Event.EDITMOVE, {layer: layer});\n\t},\n\n\t_toggleMarkerHighlight: function () {\n\t\tvar icon = this._marker._icon;\n\n\t\t// Don't do anything if this layer is a marker but doesn't have an icon. Markers\n\t\t// should usually have icons. If using Leaflet.draw with Leaflet.markercluster there\n\t\t// is a chance that a marker doesn't.\n\t\tif (!icon) {\n\t\t\treturn;\n\t\t}\n\n\t\t// This is quite naughty, but I don't see another way of doing it. (short of setting a new icon)\n\t\ticon.style.display = 'none';\n\n\t\tif (L.DomUtil.hasClass(icon, 'leaflet-edit-marker-selected')) {\n\t\t\tL.DomUtil.removeClass(icon, 'leaflet-edit-marker-selected');\n\t\t\t// Offset as the border will make the icon move.\n\t\t\tthis._offsetMarker(icon, -4);\n\n\t\t} else {\n\t\t\tL.DomUtil.addClass(icon, 'leaflet-edit-marker-selected');\n\t\t\t// Offset as the border will make the icon move.\n\t\t\tthis._offsetMarker(icon, 4);\n\t\t}\n\n\t\ticon.style.display = '';\n\t},\n\n\t_offsetMarker: function (icon, offset) {\n\t\tvar iconMarginTop = parseInt(icon.style.marginTop, 10) - offset,\n\t\t\ticonMarginLeft = parseInt(icon.style.marginLeft, 10) - offset;\n\n\t\ticon.style.marginTop = iconMarginTop + 'px';\n\t\ticon.style.marginLeft = iconMarginLeft + 'px';\n\t}\n});\n\nL.Marker.addInitHook(function () {\n\tif (L.Edit.Marker) {\n\t\tthis.editing = new L.Edit.Marker(this);\n\n\t\tif (this.options.editable) {\n\t\t\tthis.editing.enable();\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/edit/handler/Edit.Poly.js",
    "content": "L.Edit = L.Edit || {};\n\n/**\n * @class L.Edit.Polyline\n * @aka L.Edit.Poly\n * @aka Edit.Poly\n */\nL.Edit.Poly = L.Handler.extend({\n\t// @method initialize(): void\n\tinitialize: function (poly) {\n\n\t\tthis.latlngs = [poly._latlngs];\n\t\tif (poly._holes) {\n\t\t\tthis.latlngs = this.latlngs.concat(poly._holes);\n\t\t}\n\n\t\tthis._poly = poly;\n\n\t\tthis._poly.on('revert-edited', this._updateLatLngs, this);\n\t},\n\n\t// Compatibility method to normalize Poly* objects\n\t// between 0.7.x and 1.0+\n\t_defaultShape: function () {\n\t\tif (!L.Polyline._flat) {\n\t\t\treturn this._poly._latlngs;\n\t\t}\n\t\treturn L.Polyline._flat(this._poly._latlngs) ? this._poly._latlngs : this._poly._latlngs[0];\n\t},\n\n\t_eachVertexHandler: function (callback) {\n\t\tfor (var i = 0; i < this._verticesHandlers.length; i++) {\n\t\t\tcallback(this._verticesHandlers[i]);\n\t\t}\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler\n\taddHooks: function () {\n\t\tthis._initHandlers();\n\t\tthis._eachVertexHandler(function (handler) {\n\t\t\thandler.addHooks();\n\t\t});\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler\n\tremoveHooks: function () {\n\t\tthis._eachVertexHandler(function (handler) {\n\t\t\thandler.removeHooks();\n\t\t});\n\t},\n\n\t// @method updateMarkers(): void\n\t// Fire an update for each vertex handler\n\tupdateMarkers: function () {\n\t\tthis._eachVertexHandler(function (handler) {\n\t\t\thandler.updateMarkers();\n\t\t});\n\t},\n\n\t_initHandlers: function () {\n\t\tthis._verticesHandlers = [];\n\t\tfor (var i = 0; i < this.latlngs.length; i++) {\n\t\t\tthis._verticesHandlers.push(new L.Edit.PolyVerticesEdit(this._poly, this.latlngs[i], this._poly.options.poly));\n\t\t}\n\t},\n\n\t_updateLatLngs: function (e) {\n\t\tthis.latlngs = [e.layer._latlngs];\n\t\tif (e.layer._holes) {\n\t\t\tthis.latlngs = this.latlngs.concat(e.layer._holes);\n\t\t}\n\t}\n\n});\n\n/**\n * @class L.Edit.PolyVerticesEdit\n * @aka Edit.PolyVerticesEdit\n */\nL.Edit.PolyVerticesEdit = L.Handler.extend({\n\toptions: {\n\t\ticon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(8, 8),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon'\n\t\t}),\n\t\ttouchIcon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(20, 20),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'\n\t\t}),\n\t\tdrawError: {\n\t\t\tcolor: '#b00b00',\n\t\t\ttimeout: 1000\n\t\t}\n\n\n\t},\n\n\t// @method intialize(): void\n\tinitialize: function (poly, latlngs, options) {\n\t\t// if touch, switch to touch icon\n\t\tif (L.Browser.touch) {\n\t\t\tthis.options.icon = this.options.touchIcon;\n\t\t}\n\t\tthis._poly = poly;\n\n\t\tif (options && options.drawError) {\n\t\t\toptions.drawError = L.Util.extend({}, this.options.drawError, options.drawError);\n\t\t}\n\n\t\tthis._latlngs = latlngs;\n\n\t\tL.setOptions(this, options);\n\t},\n\n\t// Compatibility method to normalize Poly* objects\n\t// between 0.7.x and 1.0+\n\t_defaultShape: function () {\n\t\tif (!L.Polyline._flat) {\n\t\t\treturn this._latlngs;\n\t\t}\n\t\treturn L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0];\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler.\n\taddHooks: function () {\n\t\tvar poly = this._poly;\n\t\tvar path = poly._path;\n\n\t\tif (!(poly instanceof L.Polygon)) {\n\t\t\tpoly.options.fill = false;\n\t\t\tif (poly.options.editing) {\n\t\t\t\tpoly.options.editing.fill = false;\n\t\t\t}\n\t\t}\n\n\t\tif (path) {\n\t\t\tif (poly.options.editing && poly.options.editing.className) {\n\t\t\t\tif (poly.options.original.className) {\n\t\t\t\t\tpoly.options.original.className.split(' ').forEach(function (className) {\n\t\t\t\t\t\tL.DomUtil.removeClass(path, className);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tpoly.options.editing.className.split(' ').forEach(function (className) {\n\t\t\t\t\tL.DomUtil.addClass(path, className);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tpoly.setStyle(poly.options.editing);\n\n\t\tif (this._poly._map) {\n\n\t\t\tthis._map = this._poly._map; // Set map\n\n\t\t\tif (!this._markerGroup) {\n\t\t\t\tthis._initMarkers();\n\t\t\t}\n\t\t\tthis._poly._map.addLayer(this._markerGroup);\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler.\n\tremoveHooks: function () {\n\t\tvar poly = this._poly;\n\t\tvar path = poly._path;\n\n\t\tif (path) {\n\t\t\tif (poly.options.editing && poly.options.editing.className) {\n\t\t\t\tpoly.options.editing.className.split(' ').forEach(function (className) {\n\t\t\t\t\tL.DomUtil.removeClass(path, className);\n\t\t\t\t});\n\t\t\t\tif (poly.options.original.className) {\n\t\t\t\t\tpoly.options.original.className.split(' ').forEach(function (className) {\n\t\t\t\t\t\tL.DomUtil.addClass(path, className);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpoly.setStyle(poly.options.original);\n\n\t\tif (poly._map) {\n\t\t\tpoly._map.removeLayer(this._markerGroup);\n\t\t\tdelete this._markerGroup;\n\t\t\tdelete this._markers;\n\t\t}\n\t},\n\n\t// @method updateMarkers(): void\n\t// Clear markers and update their location\n\tupdateMarkers: function () {\n\t\tthis._markerGroup.clearLayers();\n\t\tthis._initMarkers();\n\t},\n\n\t_initMarkers: function () {\n\t\tif (!this._markerGroup) {\n\t\t\tthis._markerGroup = new L.LayerGroup();\n\t\t}\n\t\tthis._markers = [];\n\n\t\tvar latlngs = this._defaultShape(),\n\t\t\ti, j, len, marker;\n\n\t\tfor (i = 0, len = latlngs.length; i < len; i++) {\n\n\t\t\tmarker = this._createMarker(latlngs[i], i);\n\t\t\tmarker.on('click', this._onMarkerClick, this);\n\t\t\tmarker.on('contextmenu', this._onContextMenu, this);\n\t\t\tthis._markers.push(marker);\n\t\t}\n\n\t\tvar markerLeft, markerRight;\n\n\t\tfor (i = 0, j = len - 1; i < len; j = i++) {\n\t\t\tif (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tmarkerLeft = this._markers[j];\n\t\t\tmarkerRight = this._markers[i];\n\n\t\t\tthis._createMiddleMarker(markerLeft, markerRight);\n\t\t\tthis._updatePrevNext(markerLeft, markerRight);\n\t\t}\n\t},\n\n\t_createMarker: function (latlng, index) {\n\t\t// Extending L.Marker in TouchEvents.js to include touch.\n\t\tvar marker = new L.Marker.Touch(latlng, {\n\t\t\tdraggable: true,\n\t\t\ticon: this.options.icon,\n\t\t});\n\n\t\tmarker._origLatLng = latlng;\n\t\tmarker._index = index;\n\n\t\tmarker\n\t\t\t.on('dragstart', this._onMarkerDragStart, this)\n\t\t\t.on('drag', this._onMarkerDrag, this)\n\t\t\t.on('dragend', this._fireEdit, this)\n\t\t\t.on('touchmove', this._onTouchMove, this)\n\t\t\t.on('touchend', this._fireEdit, this)\n\t\t\t.on('MSPointerMove', this._onTouchMove, this)\n\t\t\t.on('MSPointerUp', this._fireEdit, this);\n\n\t\tthis._markerGroup.addLayer(marker);\n\n\t\treturn marker;\n\t},\n\n\t_onMarkerDragStart: function () {\n\t\tthis._poly.fire('editstart');\n\t},\n\n\t_spliceLatLngs: function () {\n\t\tvar latlngs = this._defaultShape();\n\t\tvar removed = [].splice.apply(latlngs, arguments);\n\t\tthis._poly._convertLatLngs(latlngs, true);\n\t\tthis._poly.redraw();\n\t\treturn removed;\n\t},\n\n\t_removeMarker: function (marker) {\n\t\tvar i = marker._index;\n\n\t\tthis._markerGroup.removeLayer(marker);\n\t\tthis._markers.splice(i, 1);\n\t\tthis._spliceLatLngs(i, 1);\n\t\tthis._updateIndexes(i, -1);\n\n\t\tmarker\n\t\t\t.off('dragstart', this._onMarkerDragStart, this)\n\t\t\t.off('drag', this._onMarkerDrag, this)\n\t\t\t.off('dragend', this._fireEdit, this)\n\t\t\t.off('touchmove', this._onMarkerDrag, this)\n\t\t\t.off('touchend', this._fireEdit, this)\n\t\t\t.off('click', this._onMarkerClick, this)\n\t\t\t.off('MSPointerMove', this._onTouchMove, this)\n\t\t\t.off('MSPointerUp', this._fireEdit, this);\n\t},\n\n\t_fireEdit: function () {\n\t\tthis._poly.edited = true;\n\t\tthis._poly.fire('edit');\n\t\tthis._poly._map.fire(L.Draw.Event.EDITVERTEX, {layers: this._markerGroup, poly: this._poly});\n\t},\n\n\t_onMarkerDrag: function (e) {\n\t\tvar marker = e.target;\n\t\tvar poly = this._poly;\n\n\t\tL.extend(marker._origLatLng, marker._latlng);\n\n\t\tif (marker._middleLeft) {\n\t\t\tmarker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));\n\t\t}\n\t\tif (marker._middleRight) {\n\t\t\tmarker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));\n\t\t}\n\n\t\tif (poly.options.poly) {\n\t\t\tvar tooltip = poly._map._editTooltip; // Access the tooltip\n\n\t\t\t// If we don't allow intersections and the polygon intersects\n\t\t\tif (!poly.options.poly.allowIntersection && poly.intersects()) {\n\n\t\t\t\tvar originalColor = poly.options.color;\n\t\t\t\tpoly.setStyle({color: this.options.drawError.color});\n\n\t\t\t\t// Manually trigger 'dragend' behavior on marker we are about to remove\n\t\t\t\t// WORKAROUND: introduced in 1.0.0-rc2, may be related to #4484\n\t\t\t\tif (L.version.indexOf('0.7') !== 0) {\n\t\t\t\t\tmarker.dragging._draggable._onUp(e);\n\t\t\t\t}\n\t\t\t\tthis._onMarkerClick(e); // Remove violating marker\n\t\t\t\t// FIXME: Reset the marker to it's original position (instead of remove)\n\n\t\t\t\tif (tooltip) {\n\t\t\t\t\ttooltip.updateContent({\n\t\t\t\t\t\ttext: L.drawLocal.draw.handlers.polyline.error\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Reset everything back to normal after a second\n\t\t\t\tsetTimeout(function () {\n\t\t\t\t\tpoly.setStyle({color: originalColor});\n\t\t\t\t\tif (tooltip) {\n\t\t\t\t\t\ttooltip.updateContent({\n\t\t\t\t\t\t\ttext: L.drawLocal.edit.handlers.edit.tooltip.text,\n\t\t\t\t\t\t\tsubtext: L.drawLocal.edit.handlers.edit.tooltip.subtext\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}, 1000);\n\t\t\t}\n\t\t}\n\t\t//refresh the bounds when draging\n\t\tthis._poly._bounds._southWest = L.latLng(Infinity, Infinity);\n\t\tthis._poly._bounds._northEast = L.latLng(-Infinity, -Infinity);\n\t\tvar latlngs = this._poly.getLatLngs();\n\t\tthis._poly._convertLatLngs(latlngs, true);\n\t\tthis._poly.redraw();\n\t\tthis._poly.fire('editdrag');\n\t},\n\n\t_onMarkerClick: function (e) {\n\n\t\tvar minPoints = L.Polygon && (this._poly instanceof L.Polygon) ? 4 : 3,\n\t\t\tmarker = e.target;\n\n\t\t// If removing this point would create an invalid polyline/polygon don't remove\n\t\tif (this._defaultShape().length < minPoints) {\n\t\t\treturn;\n\t\t}\n\n\t\t// remove the marker\n\t\tthis._removeMarker(marker);\n\n\t\t// update prev/next links of adjacent markers\n\t\tthis._updatePrevNext(marker._prev, marker._next);\n\n\t\t// remove ghost markers near the removed marker\n\t\tif (marker._middleLeft) {\n\t\t\tthis._markerGroup.removeLayer(marker._middleLeft);\n\t\t}\n\t\tif (marker._middleRight) {\n\t\t\tthis._markerGroup.removeLayer(marker._middleRight);\n\t\t}\n\n\t\t// create a ghost marker in place of the removed one\n\t\tif (marker._prev && marker._next) {\n\t\t\tthis._createMiddleMarker(marker._prev, marker._next);\n\n\t\t} else if (!marker._prev) {\n\t\t\tmarker._next._middleLeft = null;\n\n\t\t} else if (!marker._next) {\n\t\t\tmarker._prev._middleRight = null;\n\t\t}\n\n\t\tthis._fireEdit();\n\t},\n\n\t_onContextMenu: function (e) {\n\t\tvar marker = e.target;\n\t\tvar poly = this._poly;\n\t\tthis._poly._map.fire(L.Draw.Event.MARKERCONTEXT, {marker: marker, layers: this._markerGroup, poly: this._poly});\n\t\tL.DomEvent.stopPropagation;\n\t},\n\n\t_onTouchMove: function (e) {\n\n\t\tvar layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]),\n\t\t\tlatlng = this._map.layerPointToLatLng(layerPoint),\n\t\t\tmarker = e.target;\n\n\t\tL.extend(marker._origLatLng, latlng);\n\n\t\tif (marker._middleLeft) {\n\t\t\tmarker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));\n\t\t}\n\t\tif (marker._middleRight) {\n\t\t\tmarker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));\n\t\t}\n\n\t\tthis._poly.redraw();\n\t\tthis.updateMarkers();\n\t},\n\n\t_updateIndexes: function (index, delta) {\n\t\tthis._markerGroup.eachLayer(function (marker) {\n\t\t\tif (marker._index > index) {\n\t\t\t\tmarker._index += delta;\n\t\t\t}\n\t\t});\n\t},\n\n\t_createMiddleMarker: function (marker1, marker2) {\n\t\tvar latlng = this._getMiddleLatLng(marker1, marker2),\n\t\t\tmarker = this._createMarker(latlng),\n\t\t\tonClick,\n\t\t\tonDragStart,\n\t\t\tonDragEnd;\n\n\t\tmarker.setOpacity(0.6);\n\n\t\tmarker1._middleRight = marker2._middleLeft = marker;\n\n\t\tonDragStart = function () {\n\t\t\tmarker.off('touchmove', onDragStart, this);\n\t\t\tvar i = marker2._index;\n\n\t\t\tmarker._index = i;\n\n\t\t\tmarker\n\t\t\t\t.off('click', onClick, this)\n\t\t\t\t.on('click', this._onMarkerClick, this);\n\n\t\t\tlatlng.lat = marker.getLatLng().lat;\n\t\t\tlatlng.lng = marker.getLatLng().lng;\n\t\t\tthis._spliceLatLngs(i, 0, latlng);\n\t\t\tthis._markers.splice(i, 0, marker);\n\n\t\t\tmarker.setOpacity(1);\n\n\t\t\tthis._updateIndexes(i, 1);\n\t\t\tmarker2._index++;\n\t\t\tthis._updatePrevNext(marker1, marker);\n\t\t\tthis._updatePrevNext(marker, marker2);\n\n\t\t\tthis._poly.fire('editstart');\n\t\t};\n\n\t\tonDragEnd = function () {\n\t\t\tmarker.off('dragstart', onDragStart, this);\n\t\t\tmarker.off('dragend', onDragEnd, this);\n\t\t\tmarker.off('touchmove', onDragStart, this);\n\n\t\t\tthis._createMiddleMarker(marker1, marker);\n\t\t\tthis._createMiddleMarker(marker, marker2);\n\t\t};\n\n\t\tonClick = function () {\n\t\t\tonDragStart.call(this);\n\t\t\tonDragEnd.call(this);\n\t\t\tthis._fireEdit();\n\t\t};\n\n\t\tmarker\n\t\t\t.on('click', onClick, this)\n\t\t\t.on('dragstart', onDragStart, this)\n\t\t\t.on('dragend', onDragEnd, this)\n\t\t\t.on('touchmove', onDragStart, this);\n\n\t\tthis._markerGroup.addLayer(marker);\n\t},\n\n\t_updatePrevNext: function (marker1, marker2) {\n\t\tif (marker1) {\n\t\t\tmarker1._next = marker2;\n\t\t}\n\t\tif (marker2) {\n\t\t\tmarker2._prev = marker1;\n\t\t}\n\t},\n\n\t_getMiddleLatLng: function (marker1, marker2) {\n\t\tvar map = this._poly._map,\n\t\t\tp1 = map.project(marker1.getLatLng()),\n\t\t\tp2 = map.project(marker2.getLatLng());\n\n\t\treturn map.unproject(p1._add(p2)._divideBy(2));\n\t}\n});\n\nL.Polyline.addInitHook(function () {\n\n\t// Check to see if handler has already been initialized. This is to support versions of Leaflet that still have L.Handler.PolyEdit\n\tif (this.editing) {\n\t\treturn;\n\t}\n\n\tif (L.Edit.Poly) {\n\n\t\tthis.editing = new L.Edit.Poly(this);\n\n\t\tif (this.options.editable) {\n\t\t\tthis.editing.enable();\n\t\t}\n\t}\n\n\tthis.on('add', function () {\n\t\tif (this.editing && this.editing.enabled()) {\n\t\t\tthis.editing.addHooks();\n\t\t}\n\t});\n\n\tthis.on('remove', function () {\n\t\tif (this.editing && this.editing.enabled()) {\n\t\t\tthis.editing.removeHooks();\n\t\t}\n\t});\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/edit/handler/Edit.Rectangle.js",
    "content": "L.Edit = L.Edit || {};\n/**\n * @class L.Edit.Rectangle\n * @aka Edit.Rectangle\n * @inherits L.Edit.SimpleShape\n */\nL.Edit.Rectangle = L.Edit.SimpleShape.extend({\n\t_createMoveMarker: function () {\n\t\tvar bounds = this._shape.getBounds(),\n\t\t\tcenter = bounds.getCenter();\n\n\t\tthis._moveMarker = this._createMarker(center, this.options.moveIcon);\n\t},\n\n\t_createResizeMarker: function () {\n\t\tvar corners = this._getCorners();\n\n\t\tthis._resizeMarkers = [];\n\n\t\tfor (var i = 0, l = corners.length; i < l; i++) {\n\t\t\tthis._resizeMarkers.push(this._createMarker(corners[i], this.options.resizeIcon));\n\t\t\t// Monkey in the corner index as we will need to know this for dragging\n\t\t\tthis._resizeMarkers[i]._cornerIndex = i;\n\t\t}\n\t},\n\n\t_onMarkerDragStart: function (e) {\n\t\tL.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);\n\n\t\t// Save a reference to the opposite point\n\t\tvar corners = this._getCorners(),\n\t\t\tmarker = e.target,\n\t\t\tcurrentCornerIndex = marker._cornerIndex;\n\n\t\tthis._oppositeCorner = corners[(currentCornerIndex + 2) % 4];\n\n\t\tthis._toggleCornerMarkers(0, currentCornerIndex);\n\t},\n\n\t_onMarkerDragEnd: function (e) {\n\t\tvar marker = e.target,\n\t\t\tbounds, center;\n\n\t\t// Reset move marker position to the center\n\t\tif (marker === this._moveMarker) {\n\t\t\tbounds = this._shape.getBounds();\n\t\t\tcenter = bounds.getCenter();\n\n\t\t\tmarker.setLatLng(center);\n\t\t}\n\n\t\tthis._toggleCornerMarkers(1);\n\n\t\tthis._repositionCornerMarkers();\n\n\t\tL.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, e);\n\t},\n\n\t_move: function (newCenter) {\n\t\tvar latlngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs(),\n\t\t\tbounds = this._shape.getBounds(),\n\t\t\tcenter = bounds.getCenter(),\n\t\t\toffset, newLatLngs = [];\n\n\t\t// Offset the latlngs to the new center\n\t\tfor (var i = 0, l = latlngs.length; i < l; i++) {\n\t\t\toffset = [latlngs[i].lat - center.lat, latlngs[i].lng - center.lng];\n\t\t\tnewLatLngs.push([newCenter.lat + offset[0], newCenter.lng + offset[1]]);\n\t\t}\n\n\t\tthis._shape.setLatLngs(newLatLngs);\n\n\t\t// Reposition the resize markers\n\t\tthis._repositionCornerMarkers();\n\n\t\tthis._map.fire(L.Draw.Event.EDITMOVE, {layer: this._shape});\n\t},\n\n\t_resize: function (latlng) {\n\t\tvar bounds;\n\n\t\t// Update the shape based on the current position of this corner and the opposite point\n\t\tthis._shape.setBounds(L.latLngBounds(latlng, this._oppositeCorner));\n\n\t\t// Reposition the move marker\n\t\tbounds = this._shape.getBounds();\n\t\tthis._moveMarker.setLatLng(bounds.getCenter());\n\n\t\tthis._map.fire(L.Draw.Event.EDITRESIZE, {layer: this._shape});\n\t},\n\n\t_getCorners: function () {\n\t\tvar bounds = this._shape.getBounds(),\n\t\t\tnw = bounds.getNorthWest(),\n\t\t\tne = bounds.getNorthEast(),\n\t\t\tse = bounds.getSouthEast(),\n\t\t\tsw = bounds.getSouthWest();\n\n\t\treturn [nw, ne, se, sw];\n\t},\n\n\t_toggleCornerMarkers: function (opacity) {\n\t\tfor (var i = 0, l = this._resizeMarkers.length; i < l; i++) {\n\t\t\tthis._resizeMarkers[i].setOpacity(opacity);\n\t\t}\n\t},\n\n\t_repositionCornerMarkers: function () {\n\t\tvar corners = this._getCorners();\n\n\t\tfor (var i = 0, l = this._resizeMarkers.length; i < l; i++) {\n\t\t\tthis._resizeMarkers[i].setLatLng(corners[i]);\n\t\t}\n\t}\n});\n\nL.Rectangle.addInitHook(function () {\n\tif (L.Edit.Rectangle) {\n\t\tthis.editing = new L.Edit.Rectangle(this);\n\n\t\tif (this.options.editable) {\n\t\t\tthis.editing.enable();\n\t\t}\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/edit/handler/Edit.SimpleShape.js",
    "content": "L.Edit = L.Edit || {};\n/**\n * @class L.Edit.SimpleShape\n * @aka Edit.SimpleShape\n */\nL.Edit.SimpleShape = L.Handler.extend({\n\toptions: {\n\t\tmoveIcon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(8, 8),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move'\n\t\t}),\n\t\tresizeIcon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(8, 8),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize'\n\t\t}),\n\t\ttouchMoveIcon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(20, 20),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon'\n\t\t}),\n\t\ttouchResizeIcon: new L.DivIcon({\n\t\t\ticonSize: new L.Point(20, 20),\n\t\t\tclassName: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon'\n\t\t}),\n\t},\n\n\t// @method intialize(): void\n\tinitialize: function (shape, options) {\n\t\t// if touch, switch to touch icon\n\t\tif (L.Browser.touch) {\n\t\t\tthis.options.moveIcon = this.options.touchMoveIcon;\n\t\t\tthis.options.resizeIcon = this.options.touchResizeIcon;\n\t\t}\n\n\t\tthis._shape = shape;\n\t\tL.Util.setOptions(this, options);\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler\n\taddHooks: function () {\n\t\tvar shape = this._shape;\n\t\tif (this._shape._map) {\n\t\t\tthis._map = this._shape._map;\n\t\t\tshape.setStyle(shape.options.editing);\n\n\t\t\tif (shape._map) {\n\t\t\t\tthis._map = shape._map;\n\t\t\t\tif (!this._markerGroup) {\n\t\t\t\t\tthis._initMarkers();\n\t\t\t\t}\n\t\t\t\tthis._map.addLayer(this._markerGroup);\n\t\t\t}\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler\n\tremoveHooks: function () {\n\t\tvar shape = this._shape;\n\n\t\tshape.setStyle(shape.options.original);\n\n\t\tif (shape._map) {\n\t\t\tthis._unbindMarker(this._moveMarker);\n\n\t\t\tfor (var i = 0, l = this._resizeMarkers.length; i < l; i++) {\n\t\t\t\tthis._unbindMarker(this._resizeMarkers[i]);\n\t\t\t}\n\t\t\tthis._resizeMarkers = null;\n\n\t\t\tthis._map.removeLayer(this._markerGroup);\n\t\t\tdelete this._markerGroup;\n\t\t}\n\n\t\tthis._map = null;\n\t},\n\n\t// @method updateMarkers(): void\n\t// Remove the edit markers from this layer\n\tupdateMarkers: function () {\n\t\tthis._markerGroup.clearLayers();\n\t\tthis._initMarkers();\n\t},\n\n\t_initMarkers: function () {\n\t\tif (!this._markerGroup) {\n\t\t\tthis._markerGroup = new L.LayerGroup();\n\t\t}\n\n\t\t// Create center marker\n\t\tthis._createMoveMarker();\n\n\t\t// Create edge marker\n\t\tthis._createResizeMarker();\n\t},\n\n\t_createMoveMarker: function () {\n\t\t// Children override\n\t},\n\n\t_createResizeMarker: function () {\n\t\t// Children override\n\t},\n\n\t_createMarker: function (latlng, icon) {\n\t\t// Extending L.Marker in TouchEvents.js to include touch.\n\t\tvar marker = new L.Marker.Touch(latlng, {\n\t\t\tdraggable: true,\n\t\t\ticon: icon,\n\t\t\tzIndexOffset: 10\n\t\t});\n\n\t\tthis._bindMarker(marker);\n\n\t\tthis._markerGroup.addLayer(marker);\n\n\t\treturn marker;\n\t},\n\n\t_bindMarker: function (marker) {\n\t\tmarker\n\t\t\t.on('dragstart', this._onMarkerDragStart, this)\n\t\t\t.on('drag', this._onMarkerDrag, this)\n\t\t\t.on('dragend', this._onMarkerDragEnd, this)\n\t\t\t.on('touchstart', this._onTouchStart, this)\n\t\t\t.on('touchmove', this._onTouchMove, this)\n\t\t\t.on('MSPointerMove', this._onTouchMove, this)\n\t\t\t.on('touchend', this._onTouchEnd, this)\n\t\t\t.on('MSPointerUp', this._onTouchEnd, this);\n\t},\n\n\t_unbindMarker: function (marker) {\n\t\tmarker\n\t\t\t.off('dragstart', this._onMarkerDragStart, this)\n\t\t\t.off('drag', this._onMarkerDrag, this)\n\t\t\t.off('dragend', this._onMarkerDragEnd, this)\n\t\t\t.off('touchstart', this._onTouchStart, this)\n\t\t\t.off('touchmove', this._onTouchMove, this)\n\t\t\t.off('MSPointerMove', this._onTouchMove, this)\n\t\t\t.off('touchend', this._onTouchEnd, this)\n\t\t\t.off('MSPointerUp', this._onTouchEnd, this);\n\t},\n\n\t_onMarkerDragStart: function (e) {\n\t\tvar marker = e.target;\n\t\tmarker.setOpacity(0);\n\n\t\tthis._shape.fire('editstart');\n\t},\n\n\t_fireEdit: function () {\n\t\tthis._shape.edited = true;\n\t\tthis._shape.fire('edit');\n\t},\n\n\t_onMarkerDrag: function (e) {\n\t\tvar marker = e.target,\n\t\t\tlatlng = marker.getLatLng();\n\n\t\tif (marker === this._moveMarker) {\n\t\t\tthis._move(latlng);\n\t\t} else {\n\t\t\tthis._resize(latlng);\n\t\t}\n\n\t\tthis._shape.redraw();\n\t\tthis._shape.fire('editdrag');\n\t},\n\n\t_onMarkerDragEnd: function (e) {\n\t\tvar marker = e.target;\n\t\tmarker.setOpacity(1);\n\n\t\tthis._fireEdit();\n\t},\n\n\t_onTouchStart: function (e) {\n\t\tL.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);\n\n\t\tif (typeof(this._getCorners) === 'function') {\n\t\t\t// Save a reference to the opposite point\n\t\t\tvar corners = this._getCorners(),\n\t\t\t\tmarker = e.target,\n\t\t\t\tcurrentCornerIndex = marker._cornerIndex;\n\n\t\t\tmarker.setOpacity(0);\n\n\t\t\t// Copyed from Edit.Rectangle.js line 23 _onMarkerDragStart()\n\t\t\t// Latlng is null otherwise.\n\t\t\tthis._oppositeCorner = corners[(currentCornerIndex + 2) % 4];\n\t\t\tthis._toggleCornerMarkers(0, currentCornerIndex);\n\t\t}\n\n\t\tthis._shape.fire('editstart');\n\t},\n\n\t_onTouchMove: function (e) {\n\t\tvar layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]),\n\t\t\tlatlng = this._map.layerPointToLatLng(layerPoint),\n\t\t\tmarker = e.target;\n\n\t\tif (marker === this._moveMarker) {\n\t\t\tthis._move(latlng);\n\t\t} else {\n\t\t\tthis._resize(latlng);\n\t\t}\n\n\t\tthis._shape.redraw();\n\n\t\t// prevent touchcancel in IOS\n\t\t// e.preventDefault();\n\t\treturn false;\n\t},\n\n\t_onTouchEnd: function (e) {\n\t\tvar marker = e.target;\n\t\tmarker.setOpacity(1);\n\t\tthis.updateMarkers();\n\t\tthis._fireEdit();\n\t},\n\n\t_move: function () {\n\t\t// Children override\n\t},\n\n\t_resize: function () {\n\t\t// Children override\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/edit/handler/EditToolbar.Delete.js",
    "content": "/**\n * @class L.EditToolbar.Delete\n * @aka EditToolbar.Delete\n */\nL.EditToolbar.Delete = L.Handler.extend({\n\tstatics: {\n\t\tTYPE: 'remove' // not delete as delete is reserved in js\n\t},\n\n\t// @method intialize(): void\n\tinitialize: function (map, options) {\n\t\tL.Handler.prototype.initialize.call(this, map);\n\n\t\tL.Util.setOptions(this, options);\n\n\t\t// Store the selectable layer group for ease of access\n\t\tthis._deletableLayers = this.options.featureGroup;\n\n\t\tif (!(this._deletableLayers instanceof L.FeatureGroup)) {\n\t\t\tthrow new Error('options.featureGroup must be a L.FeatureGroup');\n\t\t}\n\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.EditToolbar.Delete.TYPE;\n\n\t\tvar version = L.version.split('.');\n\t\t//If Version is >= 1.2.0\n\t\tif (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {\n\t\t\tL.EditToolbar.Delete.include(L.Evented.prototype);\n\t\t} else {\n\t\t\tL.EditToolbar.Delete.include(L.Mixin.Events);\n\t\t}\n\n\t},\n\n\t// @method enable(): void\n\t// Enable the delete toolbar\n\tenable: function () {\n\t\tif (this._enabled || !this._hasAvailableLayers()) {\n\t\t\treturn;\n\t\t}\n\t\tthis.fire('enabled', {handler: this.type});\n\n\t\tthis._map.fire(L.Draw.Event.DELETESTART, {handler: this.type});\n\n\t\tL.Handler.prototype.enable.call(this);\n\n\t\tthis._deletableLayers\n\t\t\t.on('layeradd', this._enableLayerDelete, this)\n\t\t\t.on('layerremove', this._disableLayerDelete, this);\n\t},\n\n\t// @method disable(): void\n\t// Disable the delete toolbar\n\tdisable: function () {\n\t\tif (!this._enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._deletableLayers\n\t\t\t.off('layeradd', this._enableLayerDelete, this)\n\t\t\t.off('layerremove', this._disableLayerDelete, this);\n\n\t\tL.Handler.prototype.disable.call(this);\n\n\t\tthis._map.fire(L.Draw.Event.DELETESTOP, {handler: this.type});\n\n\t\tthis.fire('disabled', {handler: this.type});\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks to this handler\n\taddHooks: function () {\n\t\tvar map = this._map;\n\n\t\tif (map) {\n\t\t\tmap.getContainer().focus();\n\n\t\t\tthis._deletableLayers.eachLayer(this._enableLayerDelete, this);\n\t\t\tthis._deletedLayers = new L.LayerGroup();\n\n\t\t\tthis._tooltip = new L.Draw.Tooltip(this._map);\n\t\t\tthis._tooltip.updateContent({text: L.drawLocal.edit.handlers.remove.tooltip.text});\n\n\t\t\tthis._map.on('mousemove', this._onMouseMove, this);\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks from this handler\n\tremoveHooks: function () {\n\t\tif (this._map) {\n\t\t\tthis._deletableLayers.eachLayer(this._disableLayerDelete, this);\n\t\t\tthis._deletedLayers = null;\n\n\t\t\tthis._tooltip.dispose();\n\t\t\tthis._tooltip = null;\n\n\t\t\tthis._map.off('mousemove', this._onMouseMove, this);\n\t\t}\n\t},\n\n\t// @method revertLayers(): void\n\t// Revert the deleted layers back to their prior state.\n\trevertLayers: function () {\n\t\t// Iterate of the deleted layers and add them back into the featureGroup\n\t\tthis._deletedLayers.eachLayer(function (layer) {\n\t\t\tthis._deletableLayers.addLayer(layer);\n\t\t\tlayer.fire('revert-deleted', {layer: layer});\n\t\t}, this);\n\t},\n\n\t// @method save(): void\n\t// Save deleted layers\n\tsave: function () {\n\t\tthis._map.fire(L.Draw.Event.DELETED, {layers: this._deletedLayers});\n\t},\n\n\t// @method removeAllLayers(): void\n\t// Remove all delateable layers\n\tremoveAllLayers: function () {\n\t\t// Iterate of the delateable layers and add remove them\n\t\tthis._deletableLayers.eachLayer(function (layer) {\n\t\t\tthis._removeLayer({layer: layer});\n\t\t}, this);\n\t\tthis.save();\n\t},\n\n\t_enableLayerDelete: function (e) {\n\t\tvar layer = e.layer || e.target || e;\n\n\t\tlayer.on('click', this._removeLayer, this);\n\t},\n\n\t_disableLayerDelete: function (e) {\n\t\tvar layer = e.layer || e.target || e;\n\n\t\tlayer.off('click', this._removeLayer, this);\n\n\t\t// Remove from the deleted layers so we can't accidentally revert if the user presses cancel\n\t\tthis._deletedLayers.removeLayer(layer);\n\t},\n\n\t_removeLayer: function (e) {\n\t\tvar layer = e.layer || e.target || e;\n\n\t\tthis._deletableLayers.removeLayer(layer);\n\n\t\tthis._deletedLayers.addLayer(layer);\n\n\t\tlayer.fire('deleted');\n\t},\n\n\t_onMouseMove: function (e) {\n\t\tthis._tooltip.updatePosition(e.latlng);\n\t},\n\n\t_hasAvailableLayers: function () {\n\t\treturn this._deletableLayers.getLayers().length !== 0;\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/edit/handler/EditToolbar.Edit.js",
    "content": "/**\n * @class L.EditToolbar.Edit\n * @aka EditToolbar.Edit\n */\nL.EditToolbar.Edit = L.Handler.extend({\n\tstatics: {\n\t\tTYPE: 'edit'\n\t},\n\n\t// @method intialize(): void\n\tinitialize: function (map, options) {\n\t\tL.Handler.prototype.initialize.call(this, map);\n\n\t\tL.setOptions(this, options);\n\n\t\t// Store the selectable layer group for ease of access\n\t\tthis._featureGroup = options.featureGroup;\n\n\t\tif (!(this._featureGroup instanceof L.FeatureGroup)) {\n\t\t\tthrow new Error('options.featureGroup must be a L.FeatureGroup');\n\t\t}\n\n\t\tthis._uneditedLayerProps = {};\n\n\t\t// Save the type so super can fire, need to do this as cannot do this.TYPE :(\n\t\tthis.type = L.EditToolbar.Edit.TYPE;\n\n\t\tvar version = L.version.split('.');\n\t\t//If Version is >= 1.2.0\n\t\tif (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {\n\t\t\tL.EditToolbar.Edit.include(L.Evented.prototype);\n\t\t} else {\n\t\t\tL.EditToolbar.Edit.include(L.Mixin.Events);\n\t\t}\n\t},\n\n\t// @method enable(): void\n\t// Enable the edit toolbar\n\tenable: function () {\n\t\tif (this._enabled || !this._hasAvailableLayers()) {\n\t\t\treturn;\n\t\t}\n\t\tthis.fire('enabled', {handler: this.type});\n\t\t//this disable other handlers\n\n\t\tthis._map.fire(L.Draw.Event.EDITSTART, {handler: this.type});\n\t\t//allow drawLayer to be updated before beginning edition.\n\n\t\tL.Handler.prototype.enable.call(this);\n\t\tthis._featureGroup\n\t\t\t.on('layeradd', this._enableLayerEdit, this)\n\t\t\t.on('layerremove', this._disableLayerEdit, this);\n\t},\n\n\t// @method disable(): void\n\t// Disable the edit toolbar\n\tdisable: function () {\n\t\tif (!this._enabled) {\n\t\t\treturn;\n\t\t}\n\t\tthis._featureGroup\n\t\t\t.off('layeradd', this._enableLayerEdit, this)\n\t\t\t.off('layerremove', this._disableLayerEdit, this);\n\t\tL.Handler.prototype.disable.call(this);\n\t\tthis._map.fire(L.Draw.Event.EDITSTOP, {handler: this.type});\n\t\tthis.fire('disabled', {handler: this.type});\n\t},\n\n\t// @method addHooks(): void\n\t// Add listener hooks for this handler\n\taddHooks: function () {\n\t\tvar map = this._map;\n\n\t\tif (map) {\n\t\t\tmap.getContainer().focus();\n\n\t\t\tthis._featureGroup.eachLayer(this._enableLayerEdit, this);\n\n\t\t\tthis._tooltip = new L.Draw.Tooltip(this._map);\n\t\t\tthis._tooltip.updateContent({\n\t\t\t\ttext: L.drawLocal.edit.handlers.edit.tooltip.text,\n\t\t\t\tsubtext: L.drawLocal.edit.handlers.edit.tooltip.subtext\n\t\t\t});\n\n\t\t\t// Quickly access the tooltip to update for intersection checking\n\t\t\tmap._editTooltip = this._tooltip;\n\n\t\t\tthis._updateTooltip();\n\n\t\t\tthis._map\n\t\t\t\t.on('mousemove', this._onMouseMove, this)\n\t\t\t\t.on('touchmove', this._onMouseMove, this)\n\t\t\t\t.on('MSPointerMove', this._onMouseMove, this)\n\t\t\t\t.on(L.Draw.Event.EDITVERTEX, this._updateTooltip, this);\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Remove listener hooks for this handler\n\tremoveHooks: function () {\n\t\tif (this._map) {\n\t\t\t// Clean up selected layers.\n\t\t\tthis._featureGroup.eachLayer(this._disableLayerEdit, this);\n\n\t\t\t// Clear the backups of the original layers\n\t\t\tthis._uneditedLayerProps = {};\n\n\t\t\tthis._tooltip.dispose();\n\t\t\tthis._tooltip = null;\n\n\t\t\tthis._map\n\t\t\t\t.off('mousemove', this._onMouseMove, this)\n\t\t\t\t.off('touchmove', this._onMouseMove, this)\n\t\t\t\t.off('MSPointerMove', this._onMouseMove, this)\n\t\t\t\t.off(L.Draw.Event.EDITVERTEX, this._updateTooltip, this);\n\t\t}\n\t},\n\n\t// @method revertLayers(): void\n\t// Revert each layer's geometry changes\n\trevertLayers: function () {\n\t\tthis._featureGroup.eachLayer(function (layer) {\n\t\t\tthis._revertLayer(layer);\n\t\t}, this);\n\t},\n\n\t// @method save(): void\n\t// Save the layer geometries\n\tsave: function () {\n\t\tvar editedLayers = new L.LayerGroup();\n\t\tthis._featureGroup.eachLayer(function (layer) {\n\t\t\tif (layer.edited) {\n\t\t\t\teditedLayers.addLayer(layer);\n\t\t\t\tlayer.edited = false;\n\t\t\t}\n\t\t});\n\t\tthis._map.fire(L.Draw.Event.EDITED, {layers: editedLayers});\n\t},\n\n\t_backupLayer: function (layer) {\n\t\tvar id = L.Util.stamp(layer);\n\n\t\tif (!this._uneditedLayerProps[id]) {\n\t\t\t// Polyline, Polygon or Rectangle\n\t\t\tif (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {\n\t\t\t\tthis._uneditedLayerProps[id] = {\n\t\t\t\t\tlatlngs: L.LatLngUtil.cloneLatLngs(layer.getLatLngs())\n\t\t\t\t};\n\t\t\t} else if (layer instanceof L.Circle) {\n\t\t\t\tthis._uneditedLayerProps[id] = {\n\t\t\t\t\tlatlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()),\n\t\t\t\t\tradius: layer.getRadius()\n\t\t\t\t};\n\t\t\t} else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker\n\t\t\t\tthis._uneditedLayerProps[id] = {\n\t\t\t\t\tlatlng: L.LatLngUtil.cloneLatLng(layer.getLatLng())\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t},\n\n\t_getTooltipText: function () {\n\t\treturn ({\n\t\t\ttext: L.drawLocal.edit.handlers.edit.tooltip.text,\n\t\t\tsubtext: L.drawLocal.edit.handlers.edit.tooltip.subtext\n\t\t});\n\t},\n\n\t_updateTooltip: function () {\n\t\tthis._tooltip.updateContent(this._getTooltipText());\n\t},\n\n\t_revertLayer: function (layer) {\n\t\tvar id = L.Util.stamp(layer);\n\t\tlayer.edited = false;\n\t\tif (this._uneditedLayerProps.hasOwnProperty(id)) {\n\t\t\t// Polyline, Polygon or Rectangle\n\t\t\tif (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {\n\t\t\t\tlayer.setLatLngs(this._uneditedLayerProps[id].latlngs);\n\t\t\t} else if (layer instanceof L.Circle) {\n\t\t\t\tlayer.setLatLng(this._uneditedLayerProps[id].latlng);\n\t\t\t\tlayer.setRadius(this._uneditedLayerProps[id].radius);\n\t\t\t} else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker or CircleMarker\n\t\t\t\tlayer.setLatLng(this._uneditedLayerProps[id].latlng);\n\t\t\t}\n\n\t\t\tlayer.fire('revert-edited', {layer: layer});\n\t\t}\n\t},\n\n\t_enableLayerEdit: function (e) {\n\t\tvar layer = e.layer || e.target || e,\n\t\t\tpathOptions, poly;\n\n\t\t// Back up this layer (if haven't before)\n\t\tthis._backupLayer(layer);\n\n\t\tif (this.options.poly) {\n\t\t\tpoly = L.Util.extend({}, this.options.poly);\n\t\t\tlayer.options.poly = poly;\n\t\t}\n\n\t\t// Set different style for editing mode\n\t\tif (this.options.selectedPathOptions) {\n\t\t\tpathOptions = L.Util.extend({}, this.options.selectedPathOptions);\n\n\t\t\t// Use the existing color of the layer\n\t\t\tif (pathOptions.maintainColor) {\n\t\t\t\tpathOptions.color = layer.options.color;\n\t\t\t\tpathOptions.fillColor = layer.options.fillColor;\n\t\t\t}\n\n\t\t\tlayer.options.original = L.extend({}, layer.options);\n\t\t\tlayer.options.editing = pathOptions;\n\n\t\t}\n\n\t\tif (layer instanceof L.Marker) {\n\t\t\tif (layer.editing) {\n\t\t\t\tlayer.editing.enable();\n\t\t\t}\n\t\t\tlayer.dragging.enable();\n\t\t\tlayer\n\t\t\t\t.on('dragend', this._onMarkerDragEnd)\n\t\t\t\t// #TODO: remove when leaflet finally fixes their draggable so it's touch friendly again.\n\t\t\t\t.on('touchmove', this._onTouchMove, this)\n\t\t\t\t.on('MSPointerMove', this._onTouchMove, this)\n\t\t\t\t.on('touchend', this._onMarkerDragEnd, this)\n\t\t\t\t.on('MSPointerUp', this._onMarkerDragEnd, this);\n\t\t} else {\n\t\t\tlayer.editing.enable();\n\t\t}\n\t},\n\n\t_disableLayerEdit: function (e) {\n\t\tvar layer = e.layer || e.target || e;\n\n\t\tlayer.edited = false;\n\t\tif (layer.editing) {\n\t\t\tlayer.editing.disable();\n\t\t}\n\n\t\tdelete layer.options.editing;\n\t\tdelete layer.options.original;\n\t\t// Reset layer styles to that of before select\n\t\tif (this._selectedPathOptions) {\n\t\t\tif (layer instanceof L.Marker) {\n\t\t\t\tthis._toggleMarkerHighlight(layer);\n\t\t\t} else {\n\t\t\t\t// reset the layer style to what is was before being selected\n\t\t\t\tlayer.setStyle(layer.options.previousOptions);\n\t\t\t\t// remove the cached options for the layer object\n\t\t\t\tdelete layer.options.previousOptions;\n\t\t\t}\n\t\t}\n\n\t\tif (layer instanceof L.Marker) {\n\t\t\tlayer.dragging.disable();\n\t\t\tlayer\n\t\t\t\t.off('dragend', this._onMarkerDragEnd, this)\n\t\t\t\t.off('touchmove', this._onTouchMove, this)\n\t\t\t\t.off('MSPointerMove', this._onTouchMove, this)\n\t\t\t\t.off('touchend', this._onMarkerDragEnd, this)\n\t\t\t\t.off('MSPointerUp', this._onMarkerDragEnd, this);\n\t\t} else {\n\t\t\tlayer.editing.disable();\n\t\t}\n\t},\n\n\t_onMouseMove: function (e) {\n\t\tthis._tooltip.updatePosition(e.latlng);\n\t},\n\n\t_onMarkerDragEnd: function (e) {\n\t\tvar layer = e.target;\n\t\tlayer.edited = true;\n\t\tthis._map.fire(L.Draw.Event.EDITMOVE, {layer: layer});\n\t},\n\n\t_onTouchMove: function (e) {\n\t\tvar touchEvent = e.originalEvent.changedTouches[0],\n\t\t\tlayerPoint = this._map.mouseEventToLayerPoint(touchEvent),\n\t\t\tlatlng = this._map.layerPointToLatLng(layerPoint);\n\t\te.target.setLatLng(latlng);\n\t},\n\n\t_hasAvailableLayers: function () {\n\t\treturn this._featureGroup.getLayers().length !== 0;\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/ext/GeometryUtil.js",
    "content": "(function () {\n\n\tvar defaultPrecision = {\n\t\tkm: 2,\n\t\tha: 2,\n\t\tm: 0,\n\t\tmi: 2,\n\t\tac: 2,\n\t\tyd: 0,\n\t\tft: 0,\n\t\tnm: 2\n\t};\n\n\n\t/**\n\t * @class L.GeometryUtil\n\t * @aka GeometryUtil\n\t */\n\tL.GeometryUtil = L.extend(L.GeometryUtil || {}, {\n\t\t// Ported from the OpenLayers implementation. See https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270\n\n\t\t// @method geodesicArea(): number\n\t\tgeodesicArea: function (latLngs) {\n\t\t\tvar pointsCount = latLngs.length,\n\t\t\t\tarea = 0.0,\n\t\t\t\td2r = Math.PI / 180,\n\t\t\t\tp1, p2;\n\n\t\t\tif (pointsCount > 2) {\n\t\t\t\tfor (var i = 0; i < pointsCount; i++) {\n\t\t\t\t\tp1 = latLngs[i];\n\t\t\t\t\tp2 = latLngs[(i + 1) % pointsCount];\n\t\t\t\t\tarea += ((p2.lng - p1.lng) * d2r) *\n\t\t\t\t\t\t(2 + Math.sin(p1.lat * d2r) + Math.sin(p2.lat * d2r));\n\t\t\t\t}\n\t\t\t\tarea = area * 6378137.0 * 6378137.0 / 2.0;\n\t\t\t}\n\n\t\t\treturn Math.abs(area);\n\t\t},\n\n\t\t// @method formattedNumber(n, precision): string\n\t\t// Returns n in specified number format (if defined) and precision\n\t\tformattedNumber: function (n, precision) {\n\t\t\tvar formatted = parseFloat(n).toFixed(precision),\n\t\t\t\tformat = L.drawLocal.format && L.drawLocal.format.numeric,\n\t\t\t\tdelimiters = format && format.delimiters,\n\t\t\t\tthousands = delimiters && delimiters.thousands,\n\t\t\t\tdecimal = delimiters && delimiters.decimal;\n\n\t\t\tif (thousands || decimal) {\n\t\t\t\tvar splitValue = formatted.split('.');\n\t\t\t\tformatted = thousands ? splitValue[0].replace(/(\\d)(?=(\\d{3})+(?!\\d))/g, '$1' + thousands) : splitValue[0];\n\t\t\t\tdecimal = decimal || '.';\n\t\t\t\tif (splitValue.length > 1) {\n\t\t\t\t\tformatted = formatted + decimal + splitValue[1];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn formatted;\n\t\t},\n\n\t\t// @method readableArea(area, isMetric, precision): string\n\t\t// Returns a readable area string in yards or metric.\n\t\t// The value will be rounded as defined by the precision option object.\n\t\treadableArea: function (area, isMetric, precision) {\n\t\t\tvar areaStr,\n\t\t\t\tunits,\n\t\t\t\tprecision = L.Util.extend({}, defaultPrecision, precision);\n\n\t\t\tif (isMetric) {\n\t\t\t\tunits = ['ha', 'm'];\n\t\t\t\ttype = typeof isMetric;\n\t\t\t\tif (type === 'string') {\n\t\t\t\t\tunits = [isMetric];\n\t\t\t\t} else if (type !== 'boolean') {\n\t\t\t\t\tunits = isMetric;\n\t\t\t\t}\n\n\t\t\t\tif (area >= 1000000 && units.indexOf('km') !== -1) {\n\t\t\t\t\tareaStr = L.GeometryUtil.formattedNumber(area * 0.000001, precision['km']) + ' km²';\n\t\t\t\t} else if (area >= 10000 && units.indexOf('ha') !== -1) {\n\t\t\t\t\tareaStr = L.GeometryUtil.formattedNumber(area * 0.0001, precision['ha']) + ' ha';\n\t\t\t\t} else {\n\t\t\t\t\tareaStr = L.GeometryUtil.formattedNumber(area, precision['m']) + ' m²';\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tarea /= 0.836127; // Square yards in 1 meter\n\n\t\t\t\tif (area >= 3097600) { //3097600 square yards in 1 square mile\n\t\t\t\t\tareaStr = L.GeometryUtil.formattedNumber(area / 3097600, precision['mi']) + ' mi²';\n\t\t\t\t} else if (area >= 4840) { //4840 square yards in 1 acre\n\t\t\t\t\tareaStr = L.GeometryUtil.formattedNumber(area / 4840, precision['ac']) + ' acres';\n\t\t\t\t} else {\n\t\t\t\t\tareaStr = L.GeometryUtil.formattedNumber(area, precision['yd']) + ' yd²';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn areaStr;\n\t\t},\n\n\t\t// @method readableDistance(distance, units): string\n\t\t// Converts a metric distance to one of [ feet, nauticalMile, metric or yards ] string\n\t\t//\n\t\t// @alternative\n\t\t// @method readableDistance(distance, isMetric, useFeet, isNauticalMile, precision): string\n\t\t// Converts metric distance to distance string.\n\t\t// The value will be rounded as defined by the precision option object.\n\t\treadableDistance: function (distance, isMetric, isFeet, isNauticalMile, precision) {\n\t\t\tvar distanceStr,\n\t\t\t\tunits,\n\t\t\t\tprecision = L.Util.extend({}, defaultPrecision, precision);\n\n\t\t\tif (isMetric) {\n\t\t\t\tunits = typeof isMetric == 'string' ? isMetric : 'metric';\n\t\t\t} else if (isFeet) {\n\t\t\t\tunits = 'feet';\n\t\t\t} else if (isNauticalMile) {\n\t\t\t\tunits = 'nauticalMile';\n\t\t\t} else {\n\t\t\t\tunits = 'yards';\n\t\t\t}\n\n\t\t\tswitch (units) {\n\t\t\t\tcase 'metric':\n\t\t\t\t\t// show metres when distance is < 1km, then show km\n\t\t\t\t\tif (distance > 1000) {\n\t\t\t\t\t\tdistanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['km']) + ' km';\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdistanceStr = L.GeometryUtil.formattedNumber(distance, precision['m']) + ' m';\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'feet':\n\t\t\t\t\tdistance *= 1.09361 * 3;\n\t\t\t\t\tdistanceStr = L.GeometryUtil.formattedNumber(distance, precision['ft']) + ' ft';\n\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'nauticalMile':\n\t\t\t\t\tdistance *= 0.53996;\n\t\t\t\t\tdistanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['nm']) + ' nm';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'yards':\n\t\t\t\tdefault:\n\t\t\t\t\tdistance *= 1.09361;\n\n\t\t\t\t\tif (distance > 1760) {\n\t\t\t\t\t\tdistanceStr = L.GeometryUtil.formattedNumber(distance / 1760, precision['mi']) + ' miles';\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdistanceStr = L.GeometryUtil.formattedNumber(distance, precision['yd']) + ' yd';\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn distanceStr;\n\t\t},\n\n\t\t// @method isVersion07x(): boolean\n\t\t// Returns true if the Leaflet version is 0.7.x, false otherwise.\n\t\tisVersion07x: function () {\n\t\t\tvar version = L.version.split('.');\n\t\t\t//If Version is == 0.7.*\n\t\t\treturn parseInt(version[0], 10) === 0 && parseInt(version[1], 10) === 7;\n\t\t},\n\t});\n\n})();\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/ext/LatLngUtil.js",
    "content": "/**\n * @class L.LatLngUtil\n * @aka LatLngUtil\n */\nL.LatLngUtil = {\n\t// Clones a LatLngs[], returns [][]\n\n\t// @method cloneLatLngs(LatLngs[]): L.LatLngs[]\n\t// Clone the latLng point or points or nested points and return an array with those points\n\tcloneLatLngs: function (latlngs) {\n\t\tvar clone = [];\n\t\tfor (var i = 0, l = latlngs.length; i < l; i++) {\n\t\t\t// Check for nested array (Polyline/Polygon)\n\t\t\tif (Array.isArray(latlngs[i])) {\n\t\t\t\tclone.push(L.LatLngUtil.cloneLatLngs(latlngs[i]));\n\t\t\t} else {\n\t\t\t\tclone.push(this.cloneLatLng(latlngs[i]));\n\t\t\t}\n\t\t}\n\t\treturn clone;\n\t},\n\n\t// @method cloneLatLng(LatLng): L.LatLng\n\t// Clone the latLng and return a new LatLng object.\n\tcloneLatLng: function (latlng) {\n\t\treturn L.latLng(latlng.lat, latlng.lng);\n\t}\n};\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/ext/LineUtil.Intersect.js",
    "content": "/**\n * @class L.LineUtil\n * @aka Util\n * @aka L.Utils\n */\nL.Util.extend(L.LineUtil, {\n\n\t// @method segmentsIntersect(): boolean\n\t// Checks to see if two line segments intersect. Does not handle degenerate cases.\n\t// http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf\n\tsegmentsIntersect: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2, /*Point*/ p3) {\n\t\treturn this._checkCounterclockwise(p, p2, p3) !==\n\t\t\tthis._checkCounterclockwise(p1, p2, p3) &&\n\t\t\tthis._checkCounterclockwise(p, p1, p2) !==\n\t\t\tthis._checkCounterclockwise(p, p1, p3);\n\t},\n\n\t// check to see if points are in counterclockwise order\n\t_checkCounterclockwise: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {\n\t\treturn (p2.y - p.y) * (p1.x - p.x) > (p1.y - p.y) * (p2.x - p.x);\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/ext/Polygon.Intersect.js",
    "content": "/**\n * @class L.Polygon\n * @aka Polygon\n */\nL.Polygon.include({\n\n\t// @method intersects(): boolean\n\t// Checks a polygon for any intersecting line segments. Ignores holes.\n\tintersects: function () {\n\t\tvar polylineIntersects,\n\t\t\tpoints = this._getProjectedPoints(),\n\t\t\tlen, firstPoint, lastPoint, maxIndex;\n\n\t\tif (this._tooFewPointsForIntersection()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tpolylineIntersects = L.Polyline.prototype.intersects.call(this);\n\n\t\t// If already found an intersection don't need to check for any more.\n\t\tif (polylineIntersects) {\n\t\t\treturn true;\n\t\t}\n\n\t\tlen = points.length;\n\t\tfirstPoint = points[0];\n\t\tlastPoint = points[len - 1];\n\t\tmaxIndex = len - 2;\n\n\t\t// Check the line segment between last and first point. Don't need to check the first line segment (minIndex = 1)\n\t\treturn this._lineSegmentsIntersectsRange(lastPoint, firstPoint, maxIndex, 1);\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/ext/Polyline.Intersect.js",
    "content": "/**\n * @class L.Polyline\n * @aka Polyline\n */\nL.Polyline.include({\n\n\t// @method intersects(): boolean\n\t// Check to see if this polyline has any linesegments that intersect.\n\t// NOTE: does not support detecting intersection for degenerate cases.\n\tintersects: function () {\n\t\tvar points = this._getProjectedPoints(),\n\t\t\tlen = points ? points.length : 0,\n\t\t\ti, p, p1;\n\n\t\tif (this._tooFewPointsForIntersection()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (i = len - 1; i >= 3; i--) {\n\t\t\tp = points[i - 1];\n\t\t\tp1 = points[i];\n\n\n\t\t\tif (this._lineSegmentsIntersectsRange(p, p1, i - 2)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t},\n\n\t// @method newLatLngIntersects(): boolean\n\t// Check for intersection if new latlng was added to this polyline.\n\t// NOTE: does not support detecting intersection for degenerate cases.\n\tnewLatLngIntersects: function (latlng, skipFirst) {\n\t\t// Cannot check a polyline for intersecting lats/lngs when not added to the map\n\t\tif (!this._map) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn this.newPointIntersects(this._map.latLngToLayerPoint(latlng), skipFirst);\n\t},\n\n\t// @method newPointIntersects(): boolean\n\t// Check for intersection if new point was added to this polyline.\n\t// newPoint must be a layer point.\n\t// NOTE: does not support detecting intersection for degenerate cases.\n\tnewPointIntersects: function (newPoint, skipFirst) {\n\t\tvar points = this._getProjectedPoints(),\n\t\t\tlen = points ? points.length : 0,\n\t\t\tlastPoint = points ? points[len - 1] : null,\n\t\t\t// The previous previous line segment. Previous line segment doesn't need testing.\n\t\t\tmaxIndex = len - 2;\n\n\t\tif (this._tooFewPointsForIntersection(1)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn this._lineSegmentsIntersectsRange(lastPoint, newPoint, maxIndex, skipFirst ? 1 : 0);\n\t},\n\n\t// Polylines with 2 sides can only intersect in cases where points are collinear (we don't support detecting these).\n\t// Cannot have intersection when < 3 line segments (< 4 points)\n\t_tooFewPointsForIntersection: function (extraPoints) {\n\t\tvar points = this._getProjectedPoints(),\n\t\t\tlen = points ? points.length : 0;\n\t\t// Increment length by extraPoints if present\n\t\tlen += extraPoints || 0;\n\n\t\treturn !points || len <= 3;\n\t},\n\n\t// Checks a line segment intersections with any line segments before its predecessor.\n\t// Don't need to check the predecessor as will never intersect.\n\t_lineSegmentsIntersectsRange: function (p, p1, maxIndex, minIndex) {\n\t\tvar points = this._getProjectedPoints(),\n\t\t\tp2, p3;\n\n\t\tminIndex = minIndex || 0;\n\n\t\t// Check all previous line segments (beside the immediately previous) for intersections\n\t\tfor (var j = maxIndex; j > minIndex; j--) {\n\t\t\tp2 = points[j - 1];\n\t\t\tp3 = points[j];\n\n\t\t\tif (L.LineUtil.segmentsIntersect(p, p1, p2, p3)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t},\n\n\t_getProjectedPoints: function () {\n\t\tif (!this._defaultShape) {\n\t\t\treturn this._originalPoints;\n\t\t}\n\t\tvar points = [],\n\t\t\t_shape = this._defaultShape();\n\n\t\tfor (var i = 0; i < _shape.length; i++) {\n\t\t\tpoints.push(this._map.latLngToLayerPoint(_shape[i]));\n\t\t}\n\t\treturn points;\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/ext/TouchEvents.js",
    "content": "L.Map.mergeOptions({\n\ttouchExtend: true\n});\n\n/**\n * @class L.Map.TouchExtend\n * @aka TouchExtend\n */\nL.Map.TouchExtend = L.Handler.extend({\n\n\t// @method initialize(): void\n\t// Sets TouchExtend private accessor variables\n\tinitialize: function (map) {\n\t\tthis._map = map;\n\t\tthis._container = map._container;\n\t\tthis._pane = map._panes.overlayPane;\n\t},\n\n\t// @method addHooks(): void\n\t// Adds dom listener events to the map container\n\taddHooks: function () {\n\t\tL.DomEvent.on(this._container, 'touchstart', this._onTouchStart, this);\n\t\tL.DomEvent.on(this._container, 'touchend', this._onTouchEnd, this);\n\t\tL.DomEvent.on(this._container, 'touchmove', this._onTouchMove, this);\n\t\tif (this._detectIE()) {\n\t\t\tL.DomEvent.on(this._container, 'MSPointerDown', this._onTouchStart, this);\n\t\t\tL.DomEvent.on(this._container, 'MSPointerUp', this._onTouchEnd, this);\n\t\t\tL.DomEvent.on(this._container, 'MSPointerMove', this._onTouchMove, this);\n\t\t\tL.DomEvent.on(this._container, 'MSPointerCancel', this._onTouchCancel, this);\n\n\t\t} else {\n\t\t\tL.DomEvent.on(this._container, 'touchcancel', this._onTouchCancel, this);\n\t\t\tL.DomEvent.on(this._container, 'touchleave', this._onTouchLeave, this);\n\t\t}\n\t},\n\n\t// @method removeHooks(): void\n\t// Removes dom listener events from the map container\n\tremoveHooks: function () {\n\t\tL.DomEvent.off(this._container, 'touchstart', this._onTouchStart);\n\t\tL.DomEvent.off(this._container, 'touchend', this._onTouchEnd);\n\t\tL.DomEvent.off(this._container, 'touchmove', this._onTouchMove);\n\t\tif (this._detectIE()) {\n\t\t\tL.DomEvent.off(this._container, 'MSPointerDowm', this._onTouchStart);\n\t\t\tL.DomEvent.off(this._container, 'MSPointerUp', this._onTouchEnd);\n\t\t\tL.DomEvent.off(this._container, 'MSPointerMove', this._onTouchMove);\n\t\t\tL.DomEvent.off(this._container, 'MSPointerCancel', this._onTouchCancel);\n\t\t} else {\n\t\t\tL.DomEvent.off(this._container, 'touchcancel', this._onTouchCancel);\n\t\t\tL.DomEvent.off(this._container, 'touchleave', this._onTouchLeave);\n\t\t}\n\t},\n\n\t_touchEvent: function (e, type) {\n\t\t// #TODO: fix the pageX error that is do a bug in Android where a single touch triggers two click events\n\t\t// _filterClick is what leaflet uses as a workaround.\n\t\t// This is a problem with more things than just android. Another problem is touchEnd has no touches in\n\t\t// its touch list.\n\t\tvar touchEvent = {};\n\t\tif (typeof e.touches !== 'undefined') {\n\t\t\tif (!e.touches.length) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttouchEvent = e.touches[0];\n\t\t} else if (e.pointerType === 'touch') {\n\t\t\ttouchEvent = e;\n\t\t\tif (!this._filterClick(e)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\n\t\tvar containerPoint = this._map.mouseEventToContainerPoint(touchEvent),\n\t\t\tlayerPoint = this._map.mouseEventToLayerPoint(touchEvent),\n\t\t\tlatlng = this._map.layerPointToLatLng(layerPoint);\n\n\t\tthis._map.fire(type, {\n\t\t\tlatlng: latlng,\n\t\t\tlayerPoint: layerPoint,\n\t\t\tcontainerPoint: containerPoint,\n\t\t\tpageX: touchEvent.pageX,\n\t\t\tpageY: touchEvent.pageY,\n\t\t\toriginalEvent: e\n\t\t});\n\t},\n\n\t/** Borrowed from Leaflet and modified for bool ops **/\n\t_filterClick: function (e) {\n\t\tvar timeStamp = (e.timeStamp || e.originalEvent.timeStamp),\n\t\t\telapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);\n\n\t\t// are they closer together than 500ms yet more than 100ms?\n\t\t// Android typically triggers them ~300ms apart while multiple listeners\n\t\t// on the same event should be triggered far faster;\n\t\t// or check if click is simulated on the element, and if it is, reject any non-simulated events\n\t\tif ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {\n\t\t\tL.DomEvent.stop(e);\n\t\t\treturn false;\n\t\t}\n\t\tL.DomEvent._lastClick = timeStamp;\n\t\treturn true;\n\t},\n\n\t_onTouchStart: function (e) {\n\t\tif (!this._map._loaded) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar type = 'touchstart';\n\t\tthis._touchEvent(e, type);\n\n\t},\n\n\t_onTouchEnd: function (e) {\n\t\tif (!this._map._loaded) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar type = 'touchend';\n\t\tthis._touchEvent(e, type);\n\t},\n\n\t_onTouchCancel: function (e) {\n\t\tif (!this._map._loaded) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar type = 'touchcancel';\n\t\tif (this._detectIE()) {\n\t\t\ttype = 'pointercancel';\n\t\t}\n\t\tthis._touchEvent(e, type);\n\t},\n\n\t_onTouchLeave: function (e) {\n\t\tif (!this._map._loaded) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar type = 'touchleave';\n\t\tthis._touchEvent(e, type);\n\t},\n\n\t_onTouchMove: function (e) {\n\t\tif (!this._map._loaded) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar type = 'touchmove';\n\t\tthis._touchEvent(e, type);\n\t},\n\n\t_detectIE: function () {\n\t\tvar ua = window.navigator.userAgent;\n\n\t\tvar msie = ua.indexOf('MSIE ');\n\t\tif (msie > 0) {\n\t\t\t// IE 10 or older => return version number\n\t\t\treturn parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);\n\t\t}\n\n\t\tvar trident = ua.indexOf('Trident/');\n\t\tif (trident > 0) {\n\t\t\t// IE 11 => return version number\n\t\t\tvar rv = ua.indexOf('rv:');\n\t\t\treturn parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);\n\t\t}\n\n\t\tvar edge = ua.indexOf('Edge/');\n\t\tif (edge > 0) {\n\t\t\t// IE 12 => return version number\n\t\t\treturn parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);\n\t\t}\n\n\t\t// other browser\n\t\treturn false;\n\t}\n});\n\nL.Map.addInitHook('addHandler', 'touchExtend', L.Map.TouchExtend);\n\n\n/**\n * @class L.Marker.Touch\n * @aka Marker.Touch\n *\n * This isn't full Touch support. This is just to get markers to also support dom touch events after creation\n * #TODO: find a better way of getting markers to support touch.\n */\nL.Marker.Touch = L.Marker.extend({\n\n\t_initInteraction: function () {\n\t\tif (!this.addInteractiveTarget) {\n\t\t\t// 0.7.x support\n\t\t\treturn this._initInteractionLegacy();\n\t\t}\n\t\t// TODO this may need be updated to re-add touch events for 1.0+\n\t\treturn L.Marker.prototype._initInteraction.apply(this);\n\t},\n\n\t// This is an exact copy of https://github.com/Leaflet/Leaflet/blob/v0.7/src/layer/marker/Marker.js\n\t// with the addition of the touch events\n\t_initInteractionLegacy: function () {\n\n\t\tif (!this.options.clickable) {\n\t\t\treturn;\n\t\t}\n\n\t\t// TODO refactor into something shared with Map/Path/etc. to DRY it up\n\n\t\tvar icon = this._icon,\n\t\t\tevents = ['dblclick',\n\t\t\t\t'mousedown',\n\t\t\t\t'mouseover',\n\t\t\t\t'mouseout',\n\t\t\t\t'contextmenu',\n\t\t\t\t'touchstart',\n\t\t\t\t'touchend',\n\t\t\t\t'touchmove'];\n\t\tif (this._detectIE) {\n\t\t\tevents.concat(['MSPointerDown',\n\t\t\t\t'MSPointerUp',\n\t\t\t\t'MSPointerMove',\n\t\t\t\t'MSPointerCancel']);\n\t\t} else {\n\t\t\tevents.concat(['touchcancel']);\n\t\t}\n\n\t\tL.DomUtil.addClass(icon, 'leaflet-clickable');\n\t\tL.DomEvent.on(icon, 'click', this._onMouseClick, this);\n\t\tL.DomEvent.on(icon, 'keypress', this._onKeyPress, this);\n\n\t\tfor (var i = 0; i < events.length; i++) {\n\t\t\tL.DomEvent.on(icon, events[i], this._fireMouseEvent, this);\n\t\t}\n\n\t\tif (L.Handler.MarkerDrag) {\n\t\t\tthis.dragging = new L.Handler.MarkerDrag(this);\n\n\t\t\tif (this.options.draggable) {\n\t\t\t\tthis.dragging.enable();\n\t\t\t}\n\t\t}\n\t},\n\n\t_detectIE: function () {\n\t\tvar ua = window.navigator.userAgent;\n\n\t\tvar msie = ua.indexOf('MSIE ');\n\t\tif (msie > 0) {\n\t\t\t// IE 10 or older => return version number\n\t\t\treturn parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);\n\t\t}\n\n\t\tvar trident = ua.indexOf('Trident/');\n\t\tif (trident > 0) {\n\t\t\t// IE 11 => return version number\n\t\t\tvar rv = ua.indexOf('rv:');\n\t\t\treturn parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);\n\t\t}\n\n\t\tvar edge = ua.indexOf('Edge/');\n\t\tif (edge > 0) {\n\t\t\t// IE 12 => return version number\n\t\t\treturn parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);\n\t\t}\n\n\t\t// other browser\n\t\treturn false;\n\t}\n});\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/leaflet-measure.css",
    "content": ".leaflet-control-measure h3,.leaflet-measure-resultpopup h3{margin:0 0 12px;padding-bottom:10px;border-bottom:1px solid #ddd}.leaflet-control-measure p,.leaflet-measure-resultpopup p{margin:10px 0 0;line-height:1.5em}.leaflet-control-measure p:first-child,.leaflet-measure-resultpopup p:first-child{margin-top:0}.leaflet-control-measure .tasks,.leaflet-measure-resultpopup .tasks{margin:12px 0 0;padding:10px 0 0;border-top:1px solid #ddd;text-align:right;list-style:none;list-style-image:none}.leaflet-control-measure .tasks li,.leaflet-measure-resultpopup .tasks li{display:inline;margin:0 10px 0 0}.leaflet-control-measure .tasks li:last-child,.leaflet-measure-resultpopup .tasks li:last-child{margin-right:0}.leaflet-control-measure .coorddivider,.leaflet-measure-resultpopup .coorddivider{color:#999}.leaflet-control-measure{max-width:280px;background:#fff}.leaflet-control-measure .leaflet-control-measure-toggle,.leaflet-control-measure .leaflet-control-measure-toggle:hover{background-size:14px 14px;background-image:url(assets/rulers.png);border:0;border-radius:4px;text-indent:100%;white-space:nowrap;overflow:hidden}.leaflet-touch .leaflet-control-measure .leaflet-control-measure-toggle,.leaflet-touch .leaflet-control-measure .leaflet-control-measure-toggle:hover{border-radius:2px}.leaflet-retina .leaflet-control-measure .leaflet-control-measure-toggle,.leaflet-retina .leaflet-control-measure .leaflet-control-measure-toggle:hover{background-image:url(assets/rulers_@2X.png)}.leaflet-touch .leaflet-control-measure .leaflet-control-measure-toggle,.leaflet-touch .leaflet-control-measure .leaflet-control-measure-toggle:hover{background-size:16px 16px}.leaflet-control-measure .startprompt h3{margin-bottom:10px}.leaflet-control-measure .startprompt .tasks{margin-top:0;padding-top:0;border-top:0;text-align:left}.leaflet-control-measure .leaflet-control-measure-interaction{padding:10px 12px}.leaflet-control-measure .results .group{margin-top:10px;padding-top:10px;border-top:1px dotted #eaeaea}.leaflet-control-measure .results .group:first-child{margin-top:0;padding-top:0;border-top:0}.leaflet-control-measure .results .heading{margin-right:5px;color:#999}.leaflet-control-measure a.start{display:inline;width:auto;height:auto;padding-left:20px;margin-right:4px;line-height:1em;border:0;text-align:left;background-image:url(\"assets/start.png\");background-repeat:no-repeat;background-position:0 50%;background-size:12px 12px;color:#5e66cc;text-decoration:none}.leaflet-control-measure a.start,.leaflet-control-measure a.start:hover{background-color:transparent}.leaflet-retina .leaflet-control-measure a.start{background-image:url(\"assets/start_@2X.png\")}.leaflet-control-measure a.start:hover{opacity:.5;text-decoration:none}.leaflet-control-measure a.cancel{display:inline;width:auto;height:auto;padding-left:20px;margin-right:4px;line-height:1em;border:0;text-align:left;background-image:url(\"assets/cancel.png\");background-repeat:no-repeat;background-position:0 50%;background-size:12px 12px;color:#5e66cc;text-decoration:none}.leaflet-control-measure a.cancel,.leaflet-control-measure a.cancel:hover{background-color:transparent}.leaflet-retina .leaflet-control-measure a.cancel{background-image:url(\"assets/cancel_@2X.png\")}.leaflet-control-measure a.cancel:hover{opacity:.5;text-decoration:none}.leaflet-control-measure a.finish{display:inline;width:auto;height:auto;padding-left:20px;margin-right:4px;line-height:1em;border:0;text-align:left;background-image:url(\"assets/check.png\");background-repeat:no-repeat;background-position:0 50%;background-size:12px 12px;color:#5e66cc;text-decoration:none}.leaflet-control-measure a.finish,.leaflet-control-measure a.finish:hover{background-color:transparent}.leaflet-retina .leaflet-control-measure a.finish{background-image:url(\"assets/check_@2X.png\")}.leaflet-control-measure a.finish:hover{opacity:.5;text-decoration:none}.leaflet-measure-resultpopup a.zoomto{display:inline;width:auto;height:auto;padding-left:20px;margin-right:4px;line-height:1em;border:0;text-align:left;background-image:url(\"assets/focus.png\");background-repeat:no-repeat;background-position:0 50%;background-size:12px 12px;color:#5e66cc;text-decoration:none}.leaflet-measure-resultpopup a.zoomto,.leaflet-measure-resultpopup a.zoomto:hover{background-color:transparent}.leaflet-retina .leaflet-measure-resultpopup a.zoomto{background-image:url(\"assets/focus_@2X.png\")}.leaflet-measure-resultpopup a.zoomto:hover{opacity:.5;text-decoration:none}.leaflet-measure-resultpopup a.deletemarkup{display:inline;width:auto;height:auto;padding-left:20px;margin-right:4px;line-height:1em;border:0;text-align:left;background-image:url(\"assets/trash.png\");background-repeat:no-repeat;background-position:0 50%;background-size:12px 12px;color:#5e66cc;text-decoration:none}.leaflet-measure-resultpopup a.deletemarkup,.leaflet-measure-resultpopup a.deletemarkup:hover{background-color:transparent}.leaflet-retina .leaflet-measure-resultpopup a.deletemarkup{background-image:url(\"assets/trash_@2X.png\")}.leaflet-measure-resultpopup a.deletemarkup:hover{opacity:.5;text-decoration:none}"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/leaflet-measure.js",
    "content": "!function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,\"a\",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p=\"/dist/\",t(t.s=28)}([function(e,t,r){function n(e){return null==e?void 0===e?u:a:l&&l in Object(e)?i(e):s(e)}var o=r(4),i=r(38),s=r(39),a=\"[object Null]\",u=\"[object Undefined]\",l=o?o.toStringTag:void 0;e.exports=n},function(e,t){function r(e){return null!=e&&\"object\"==typeof e}e.exports=r},function(e,t){function r(e){var t=typeof e;return null!=e&&(\"object\"==t||\"function\"==t)}e.exports=r},function(e,t,r){\"use strict\";function n(e,t,r){if(r=r||{},!h(r))throw new Error(\"options is invalid\");var n=r.bbox,o=r.id;if(void 0===e)throw new Error(\"geometry is required\");if(t&&t.constructor!==Object)throw new Error(\"properties must be an Object\");n&&d(n),o&&m(o);var i={type:\"Feature\"};return o&&(i.id=o),n&&(i.bbox=n),i.properties=t||{},i.geometry=e,i}function o(e,t,r){if(!e)throw new Error(\"coordinates is required\");if(!Array.isArray(e))throw new Error(\"coordinates must be an Array\");if(e.length<2)throw new Error(\"coordinates must be at least 2 numbers long\");if(!p(e[0])||!p(e[1]))throw new Error(\"coordinates must contain numbers\");return n({type:\"Point\",coordinates:e},t,r)}function i(e,t,r){if(!e)throw new Error(\"coordinates is required\");for(var o=0;o<e.length;o++){var i=e[o];if(i.length<4)throw new Error(\"Each LinearRing of a Polygon must have 4 or more Positions.\");for(var s=0;s<i[i.length-1].length;s++){if(0===o&&0===s&&!p(i[0][0])||!p(i[0][1]))throw new Error(\"coordinates must contain numbers\");if(i[i.length-1][s]!==i[0][s])throw new Error(\"First and last Position are not equivalent.\")}}return n({type:\"Polygon\",coordinates:e},t,r)}function s(e,t,r){if(!e)throw new Error(\"coordinates is required\");if(e.length<2)throw new Error(\"coordinates must be an array of two or more positions\");if(!p(e[0][1])||!p(e[0][1]))throw new Error(\"coordinates must contain numbers\");return n({type:\"LineString\",coordinates:e},t,r)}function a(e,t,r){if(!e)throw new Error(\"coordinates is required\");return n({type:\"MultiLineString\",coordinates:e},t,r)}function u(e,t,r){if(!e)throw new Error(\"coordinates is required\");return n({type:\"MultiPoint\",coordinates:e},t,r)}function l(e,t,r){if(!e)throw new Error(\"coordinates is required\");return n({type:\"MultiPolygon\",coordinates:e},t,r)}function c(e,t){if(void 0===e||null===e)throw new Error(\"radians is required\");if(t&&\"string\"!=typeof t)throw new Error(\"units must be a string\");var r=y[t||\"kilometers\"];if(!r)throw new Error(t+\" units is invalid\");return e*r}function f(e){if(null===e||void 0===e)throw new Error(\"degrees is required\");return e%360*Math.PI/180}function p(e){return!isNaN(e)&&null!==e&&!Array.isArray(e)}function h(e){return!!e&&e.constructor===Object}function d(e){if(!e)throw new Error(\"bbox is required\");if(!Array.isArray(e))throw new Error(\"bbox must be an Array\");if(4!==e.length&&6!==e.length)throw new Error(\"bbox must be an Array of 4 or 6 numbers\");e.forEach(function(e){if(!p(e))throw new Error(\"bbox must only contain numbers\")})}function m(e){if(!e)throw new Error(\"id is required\");if(-1===[\"string\",\"number\"].indexOf(typeof e))throw new Error(\"id must be a number or a string\")}r.d(t,\"b\",function(){return n}),r.d(t,\"f\",function(){return o}),r.d(t,\"e\",function(){return s}),r.d(t,\"g\",function(){return c}),r.d(t,\"a\",function(){return f}),r.d(t,\"c\",function(){return p}),r.d(t,\"d\",function(){return h});var y={meters:6371008.8,metres:6371008.8,millimeters:6371008800,millimetres:6371008800,centimeters:637100880,centimetres:637100880,kilometers:6371.0088,kilometres:6371.0088,miles:3958.761333810546,nauticalmiles:6371008.8/1852,inches:6371008.8*39.37,yards:6371008.8/1.0936,feet:20902260.511392,radians:1,degrees:6371008.8/111325}},function(e,t,r){var n=r(5),o=n.Symbol;e.exports=o},function(e,t,r){var n=r(11),o=\"object\"==typeof self&&self&&self.Object===Object&&self,i=n||o||Function(\"return this\")();e.exports=i},function(e,t){function r(e,t){return e===t||e!==e&&t!==t}e.exports=r},function(e,t,r){function n(e){return null!=e&&i(e.length)&&!o(e)}var o=r(10),i=r(16);e.exports=n},function(e,t,r){function n(e,t,r){\"__proto__\"==t&&o?o(e,t,{configurable:!0,enumerable:!0,value:r,writable:!0}):e[t]=r}var o=r(9);e.exports=n},function(e,t,r){var n=r(35),o=function(){try{var e=n(Object,\"defineProperty\");return e({},\"\",{}),e}catch(e){}}();e.exports=o},function(e,t,r){function n(e){if(!i(e))return!1;var t=o(e);return t==a||t==u||t==s||t==l}var o=r(0),i=r(2),s=\"[object AsyncFunction]\",a=\"[object Function]\",u=\"[object GeneratorFunction]\",l=\"[object Proxy]\";e.exports=n},function(e,t,r){(function(t){var r=\"object\"==typeof t&&t&&t.Object===Object&&t;e.exports=r}).call(t,r(37))},function(e,t,r){function n(e,t){return s(i(e,t,o),e+\"\")}var o=r(13),i=r(45),s=r(46);e.exports=n},function(e,t){function r(e){return e}e.exports=r},function(e,t){function r(e,t,r){switch(r.length){case 0:return e.call(t);case 1:return e.call(t,r[0]);case 2:return e.call(t,r[0],r[1]);case 3:return e.call(t,r[0],r[1],r[2])}return e.apply(t,r)}e.exports=r},function(e,t,r){function n(e,t,r){if(!a(r))return!1;var n=typeof t;return!!(\"number\"==n?i(r)&&s(t,r.length):\"string\"==n&&t in r)&&o(r[t],e)}var o=r(6),i=r(7),s=r(17),a=r(2);e.exports=n},function(e,t){function r(e){return\"number\"==typeof e&&e>-1&&e%1==0&&e<=n}var n=9007199254740991;e.exports=r},function(e,t){function r(e,t){var r=typeof e;return!!(t=null==t?n:t)&&(\"number\"==r||\"symbol\"!=r&&o.test(e))&&e>-1&&e%1==0&&e<t}var n=9007199254740991,o=/^(?:0|[1-9]\\d*)$/;e.exports=r},function(e,t,r){function n(e,t){var r=s(e),n=!r&&i(e),c=!r&&!n&&a(e),p=!r&&!n&&!c&&l(e),h=r||n||c||p,d=h?o(e.length,String):[],m=d.length;for(var y in e)!t&&!f.call(e,y)||h&&(\"length\"==y||c&&(\"offset\"==y||\"parent\"==y)||p&&(\"buffer\"==y||\"byteLength\"==y||\"byteOffset\"==y)||u(y,m))||d.push(y);return d}var o=r(51),i=r(52),s=r(19),a=r(54),u=r(17),l=r(56),c=Object.prototype,f=c.hasOwnProperty;e.exports=n},function(e,t){var r=Array.isArray;e.exports=r},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,\"loaded\",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,\"id\",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,t){function r(e){var t=e&&e.constructor;return e===(\"function\"==typeof t&&t.prototype||n)}var n=Object.prototype;e.exports=r},function(e,t,r){function n(e){if(!i(e))return!1;var t=o(e);return t==u||t==a||\"string\"==typeof e.message&&\"string\"==typeof e.name&&!s(e)}var o=r(0),i=r(1),s=r(63),a=\"[object DOMException]\",u=\"[object Error]\";e.exports=n},function(e,t){function r(e,t){return function(r){return e(t(r))}}e.exports=r},function(e,t){function r(e,t){for(var r=-1,n=null==e?0:e.length,o=Array(n);++r<n;)o[r]=t(e[r],r,e);return o}e.exports=r},function(e,t){var r=/<%=([\\s\\S]+?)%>/g;e.exports=r},function(e,t,r){function n(e){return null==e?\"\":o(e)}var o=r(75);e.exports=n},function(e,t,r){\"use strict\";function n(e,t,r){if(null!==e)for(var o,i,s,a,u,l,c,f,p=0,h=0,d=e.type,m=\"FeatureCollection\"===d,y=\"Feature\"===d,v=m?e.features.length:1,g=0;g<v;g++){c=m?e.features[g].geometry:y?e.geometry:e,f=!!c&&\"GeometryCollection\"===c.type,u=f?c.geometries.length:1;for(var b=0;b<u;b++){var _=0,j=0;if(null!==(a=f?c.geometries[b]:c)){l=a.coordinates;var x=a.type;switch(p=!r||\"Polygon\"!==x&&\"MultiPolygon\"!==x?0:1,x){case null:break;case\"Point\":if(!1===t(l,h,g,_,j))return!1;h++,_++;break;case\"LineString\":case\"MultiPoint\":for(o=0;o<l.length;o++){if(!1===t(l[o],h,g,_,j))return!1;h++,\"MultiPoint\"===x&&_++}\"LineString\"===x&&_++;break;case\"Polygon\":case\"MultiLineString\":for(o=0;o<l.length;o++){for(i=0;i<l[o].length-p;i++){if(!1===t(l[o][i],h,g,_,j))return!1;h++}\"MultiLineString\"===x&&_++,\"Polygon\"===x&&j++}\"Polygon\"===x&&_++;break;case\"MultiPolygon\":for(o=0;o<l.length;o++){for(\"MultiPolygon\"===x&&(j=0),i=0;i<l[o].length;i++){for(s=0;s<l[o][i].length-p;s++){if(!1===t(l[o][i][s],h,g,_,j))return!1;h++}j++}_++}break;case\"GeometryCollection\":for(o=0;o<a.geometries.length;o++)if(!1===n(a.geometries[o],t,r))return!1;break;default:throw new Error(\"Unknown Geometry Type\")}}}}}function o(e,t){var r,n,o,i,s,a,u,l,c,f,p=0,h=\"FeatureCollection\"===e.type,d=\"Feature\"===e.type,m=h?e.features.length:1;for(r=0;r<m;r++){for(a=h?e.features[r].geometry:d?e.geometry:e,l=h?e.features[r].properties:d?e.properties:{},c=h?e.features[r].bbox:d?e.bbox:void 0,f=h?e.features[r].id:d?e.id:void 0,u=!!a&&\"GeometryCollection\"===a.type,s=u?a.geometries.length:1,o=0;o<s;o++)if(null!==(i=u?a.geometries[o]:a))switch(i.type){case\"Point\":case\"LineString\":case\"MultiPoint\":case\"Polygon\":case\"MultiLineString\":case\"MultiPolygon\":if(!1===t(i,p,l,c,f))return!1;break;case\"GeometryCollection\":for(n=0;n<i.geometries.length;n++)if(!1===t(i.geometries[n],p,l,c,f))return!1;break;default:throw new Error(\"Unknown Geometry Type\")}else if(!1===t(null,p,l,c,f))return!1;p++}}function i(e,t,r){var n=r;return o(e,function(e,o,i,s,a){n=0===o&&void 0===r?e:t(n,e,o,i,s,a)}),n}function s(e,t){o(e,function(e,r,n,o,i){var s=null===e?null:e.type;switch(s){case null:case\"Point\":case\"LineString\":case\"Polygon\":if(!1===t(Object(l.b)(e,n,{bbox:o,id:i}),r,0))return!1;return}var a;switch(s){case\"MultiPoint\":a=\"Point\";break;case\"MultiLineString\":a=\"LineString\";break;case\"MultiPolygon\":a=\"Polygon\"}for(var u=0;u<e.coordinates.length;u++){var c=e.coordinates[u],f={type:a,coordinates:c};if(!1===t(Object(l.b)(f,n),r,u))return!1}})}function a(e,t){s(e,function(e,r,o){var i=0;if(e.geometry){var s=e.geometry.type;if(\"Point\"!==s&&\"MultiPoint\"!==s){var a;return!1!==n(e,function(n,s,u,c,f){if(void 0===a)return void(a=n);var p=Object(l.e)([a,n],e.properties);if(!1===t(p,r,o,f,i))return!1;i++,a=n})&&void 0}}})}function u(e,t,r){var n=r,o=!1;return a(e,function(e,i,s,a,u){n=!1===o&&void 0===r?e:t(n,e,i,s,a,u),o=!0}),n}r.d(t,\"a\",function(){return i}),r.d(t,\"b\",function(){return u});var l=r(3)},function(e,t,r){e.exports=r(29)},function(e,t,r){\"use strict\";function n(e){return e&&e.__esModule?e:{default:e}}r(30);var o=r(31),i=n(o),s=r(79),a=n(s),u=r(80),l=n(u),c=r(85),f=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t.default=e,t}(c),p=r(86),h=n(p),d=r(87),m=r(88),y={imports:{numberFormat:d.numberFormat},interpolate:/{{([\\s\\S]+?)}}/g},v=(0,i.default)(m.controlTemplate,y),g=(0,i.default)(m.resultsTemplate,y),b=(0,i.default)(m.pointPopupTemplate,y),_=(0,i.default)(m.linePopupTemplate,y),j=(0,i.default)(m.areaPopupTemplate,y);L.Control.Measure=L.Control.extend({_className:\"leaflet-control-measure\",options:{units:{},position:\"topright\",primaryLengthUnit:\"feet\",secondaryLengthUnit:\"miles\",primaryAreaUnit:\"acres\",activeColor:\"#ABE67E\",completedColor:\"#C8F2BE\",captureZIndex:1e4,popupOptions:{className:\"leaflet-measure-resultpopup\",autoPanPadding:[10,10]}},initialize:function(e){L.setOptions(this,e);var t=this.options,r=t.activeColor,n=t.completedColor;this._symbols=new h.default({activeColor:r,completedColor:n}),this.options.units=L.extend({},a.default,this.options.units)},onAdd:function(e){return this._map=e,this._latlngs=[],this._initLayout(),e.on(\"click\",this._collapse,this),this._layer=L.layerGroup().addTo(e),this._container},onRemove:function(e){e.off(\"click\",this._collapse,this),e.removeLayer(this._layer)},_initLayout:function(){var e=this._className,t=this._container=L.DomUtil.create(\"div\",e+\" leaflet-bar\");t.innerHTML=v({model:{className:e}}),t.setAttribute(\"aria-haspopup\",!0),L.DomEvent.disableClickPropagation(t),L.DomEvent.disableScrollPropagation(t);var r=this.$toggle=(0,c.selectOne)(\".js-toggle\",t);this.$interaction=(0,c.selectOne)(\".js-interaction\",t);var n=(0,c.selectOne)(\".js-start\",t),o=(0,c.selectOne)(\".js-cancel\",t),i=(0,c.selectOne)(\".js-finish\",t);this.$startPrompt=(0,c.selectOne)(\".js-startprompt\",t),this.$measuringPrompt=(0,c.selectOne)(\".js-measuringprompt\",t),this.$startHelp=(0,c.selectOne)(\".js-starthelp\",t),this.$results=(0,c.selectOne)(\".js-results\",t),this.$measureTasks=(0,c.selectOne)(\".js-measuretasks\",t),this._collapse(),this._updateMeasureNotStarted(),L.Browser.android||(L.DomEvent.on(t,\"mouseenter\",this._expand,this),L.DomEvent.on(t,\"mouseleave\",this._collapse,this)),L.DomEvent.on(r,\"click\",L.DomEvent.stop),L.Browser.touch?L.DomEvent.on(r,\"click\",this._expand,this):L.DomEvent.on(r,\"focus\",this._expand,this),L.DomEvent.on(n,\"click\",L.DomEvent.stop),L.DomEvent.on(n,\"click\",this._startMeasure,this),L.DomEvent.on(o,\"click\",L.DomEvent.stop),L.DomEvent.on(o,\"click\",this._finishMeasure,this),L.DomEvent.on(i,\"click\",L.DomEvent.stop),L.DomEvent.on(i,\"click\",this._handleMeasureDoubleClick,this)},_expand:function(){f.hide(this.$toggle),f.show(this.$interaction)},_collapse:function(){this._locked||(f.hide(this.$interaction),f.show(this.$toggle))},_updateMeasureNotStarted:function(){f.hide(this.$startHelp),f.hide(this.$results),f.hide(this.$measureTasks),f.hide(this.$measuringPrompt),f.show(this.$startPrompt)},_updateMeasureStartedNoPoints:function(){f.hide(this.$results),f.show(this.$startHelp),f.show(this.$measureTasks),f.hide(this.$startPrompt),f.show(this.$measuringPrompt)},_updateMeasureStartedWithPoints:function(){f.hide(this.$startHelp),f.show(this.$results),f.show(this.$measureTasks),f.hide(this.$startPrompt),f.show(this.$measuringPrompt)},_startMeasure:function(){this._locked=!0,this._measureVertexes=L.featureGroup().addTo(this._layer),this._captureMarker=L.marker(this._map.getCenter(),{clickable:!0,zIndexOffset:this.options.captureZIndex,opacity:0}).addTo(this._layer),this._setCaptureMarkerIcon(),this._captureMarker.on(\"mouseout\",this._handleMapMouseOut,this).on(\"dblclick\",this._handleMeasureDoubleClick,this).on(\"click\",this._handleMeasureClick,this),this._map.on(\"mousemove\",this._handleMeasureMove,this).on(\"mouseout\",this._handleMapMouseOut,this).on(\"move\",this._centerCaptureMarker,this).on(\"resize\",this._setCaptureMarkerIcon,this),L.DomEvent.on(this._container,\"mouseenter\",this._handleMapMouseOut,this),this._updateMeasureStartedNoPoints(),this._map.fire(\"measurestart\",null,!1)},_finishMeasure:function(){var e=L.extend({},this._resultsModel,{points:this._latlngs});this._locked=!1,L.DomEvent.off(this._container,\"mouseover\",this._handleMapMouseOut,this),this._clearMeasure(),this._captureMarker.off(\"mouseout\",this._handleMapMouseOut,this).off(\"dblclick\",this._handleMeasureDoubleClick,this).off(\"click\",this._handleMeasureClick,this),this._map.off(\"mousemove\",this._handleMeasureMove,this).off(\"mouseout\",this._handleMapMouseOut,this).off(\"move\",this._centerCaptureMarker,this).off(\"resize\",this._setCaptureMarkerIcon,this),this._layer.removeLayer(this._measureVertexes).removeLayer(this._captureMarker),this._measureVertexes=null,this._updateMeasureNotStarted(),this._collapse(),this._map.fire(\"measurefinish\",e,!1)},_clearMeasure:function(){this._latlngs=[],this._resultsModel=null,this._measureVertexes.clearLayers(),this._measureDrag&&this._layer.removeLayer(this._measureDrag),this._measureArea&&this._layer.removeLayer(this._measureArea),this._measureBoundary&&this._layer.removeLayer(this._measureBoundary),this._measureDrag=null,this._measureArea=null,this._measureBoundary=null},_centerCaptureMarker:function(){this._captureMarker.setLatLng(this._map.getCenter())},_setCaptureMarkerIcon:function(){this._captureMarker.setIcon(L.divIcon({iconSize:this._map.getSize().multiplyBy(2)}))},_getMeasurementDisplayStrings:function(e){function t(e,t,o,i,s){if(t&&n[t]){var a=r(e,n[t],i,s);if(o&&n[o]){a=a+\" (\"+r(e,n[o],i,s)+\")\"}return a}return r(e,null,i,s)}function r(e,t,r,n){var o={acres:\"Acres\",feet:\"Feet\",kilometers:\"Kilometers\",hectares:\"Hectares\",meters:\"Meters\",miles:\"Miles\",sqfeet:\"Sq Feet\",sqmeters:\"Sq Meters\",sqmiles:\"Sq Miles\"},i=L.extend({factor:1,decimals:0},t);return[(0,d.numberFormat)(e*i.factor,i.decimals,r||\".\",n||\",\"),o[i.display]||i.display].join(\" \")}var n=this.options.units;return{lengthDisplay:t(e.length,this.options.primaryLengthUnit,this.options.secondaryLengthUnit,this.options.decPoint,this.options.thousandsSep),areaDisplay:t(e.area,this.options.primaryAreaUnit,this.options.secondaryAreaUnit,this.options.decPoint,this.options.thousandsSep)}},_updateResults:function(){var e=(0,l.default)(this._latlngs),t=this._resultsModel=L.extend({},e,this._getMeasurementDisplayStrings(e),{pointCount:this._latlngs.length});this.$results.innerHTML=g({model:t})},_handleMeasureMove:function(e){this._measureDrag?this._measureDrag.setLatLng(e.latlng):this._measureDrag=L.circleMarker(e.latlng,this._symbols.getSymbol(\"measureDrag\")).addTo(this._layer),this._measureDrag.bringToFront()},_handleMeasureDoubleClick:function(){var e=this._latlngs,t=void 0,r=void 0;if(this._finishMeasure(),e.length){e.length>2&&e.push(e[0]);var n=(0,l.default)(e);1===e.length?(t=L.circleMarker(e[0],this._symbols.getSymbol(\"resultPoint\")),r=b({model:n})):2===e.length?(t=L.polyline(e,this._symbols.getSymbol(\"resultLine\")),r=_({model:L.extend({},n,this._getMeasurementDisplayStrings(n))})):(t=L.polygon(e,this._symbols.getSymbol(\"resultArea\")),r=j({model:L.extend({},n,this._getMeasurementDisplayStrings(n))}));var o=L.DomUtil.create(\"div\",\"\");o.innerHTML=r;var i=(0,c.selectOne)(\".js-zoomto\",o);i&&(L.DomEvent.on(i,\"click\",L.DomEvent.stop),L.DomEvent.on(i,\"click\",function(){t.getBounds?this._map.fitBounds(t.getBounds(),{padding:[20,20],maxZoom:17}):t.getLatLng&&this._map.panTo(t.getLatLng())},this));var s=(0,c.selectOne)(\".js-deletemarkup\",o);s&&(L.DomEvent.on(s,\"click\",L.DomEvent.stop),L.DomEvent.on(s,\"click\",function(){this._layer.removeLayer(t)},this)),t.addTo(this._layer),t.bindPopup(o,this.options.popupOptions),t.getBounds?t.openPopup(t.getBounds().getCenter()):t.getLatLng&&t.openPopup(t.getLatLng())}},_handleMeasureClick:function(e){var t=this._map.mouseEventToLatLng(e.originalEvent),r=this._latlngs[this._latlngs.length-1],n=this._symbols.getSymbol(\"measureVertex\");r&&t.equals(r)||(this._latlngs.push(t),this._addMeasureArea(this._latlngs),this._addMeasureBoundary(this._latlngs),this._measureVertexes.eachLayer(function(e){e.setStyle(n),e._path.setAttribute(\"class\",n.className)}),this._addNewVertex(t),this._measureBoundary&&this._measureBoundary.bringToFront(),this._measureVertexes.bringToFront()),this._updateResults(),this._updateMeasureStartedWithPoints()},_handleMapMouseOut:function(){this._measureDrag&&(this._layer.removeLayer(this._measureDrag),this._measureDrag=null)},_addNewVertex:function(e){L.circleMarker(e,this._symbols.getSymbol(\"measureVertexActive\")).addTo(this._measureVertexes)},_addMeasureArea:function(e){if(e.length<3)return void(this._measureArea&&(this._layer.removeLayer(this._measureArea),this._measureArea=null));this._measureArea?this._measureArea.setLatLngs(e):this._measureArea=L.polygon(e,this._symbols.getSymbol(\"measureArea\")).addTo(this._layer)},_addMeasureBoundary:function(e){if(e.length<2)return void(this._measureBoundary&&(this._layer.removeLayer(this._measureBoundary),this._measureBoundary=null));this._measureBoundary?this._measureBoundary.setLatLngs(e):this._measureBoundary=L.polyline(e,this._symbols.getSymbol(\"measureBoundary\")).addTo(this._layer)}}),L.Map.mergeOptions({measureControl:!1}),L.Map.addInitHook(function(){this.options.measureControl&&(this.measureControl=(new L.Control.Measure).addTo(this))}),L.control.measure=function(e){return new L.Control.Measure(e)}},function(e,t){},function(e,t,r){function n(e,t,r){var n=h.imports._.templateSettings||h;r&&c(e,t,r)&&(t=void 0),e=d(e),t=o({},t,n,a);var j,x,M=o({},t.imports,n.imports,a),w=f(M),L=s(M,w),O=0,P=t.interpolate||b,k=\"__p += '\",C=RegExp((t.escape||b).source+\"|\"+P.source+\"|\"+(P===p?g:b).source+\"|\"+(t.evaluate||b).source+\"|$\",\"g\"),E=\"sourceURL\"in t?\"//# sourceURL=\"+t.sourceURL+\"\\n\":\"\";e.replace(C,function(t,r,n,o,i,s){return n||(n=o),k+=e.slice(O,s).replace(_,u),r&&(j=!0,k+=\"' +\\n__e(\"+r+\") +\\n'\"),i&&(x=!0,k+=\"';\\n\"+i+\";\\n__p += '\"),n&&(k+=\"' +\\n((__t = (\"+n+\")) == null ? '' : __t) +\\n'\"),O=s+t.length,t}),k+=\"';\\n\";var S=t.variable;S||(k=\"with (obj) {\\n\"+k+\"\\n}\\n\"),k=(x?k.replace(m,\"\"):k).replace(y,\"$1\").replace(v,\"$1;\"),k=\"function(\"+(S||\"obj\")+\") {\\n\"+(S?\"\":\"obj || (obj = {});\\n\")+\"var __t, __p = ''\"+(j?\", __e = _.escape\":\"\")+(x?\", __j = Array.prototype.join;\\nfunction print() { __p += __j.call(arguments, '') }\\n\":\";\\n\")+k+\"return __p\\n}\";var A=i(function(){return Function(w,E+\"return \"+k).apply(void 0,L)});if(A.source=k,l(A))throw A;return A}var o=r(32),i=r(62),s=r(65),a=r(66),u=r(67),l=r(22),c=r(15),f=r(68),p=r(25),h=r(71),d=r(26),m=/\\b__p \\+= '';/g,y=/\\b(__p \\+=) '' \\+/g,v=/(__e\\(.*?\\)|\\b__t\\)) \\+\\n'';/g,g=/\\$\\{([^\\\\}]*(?:\\\\.[^\\\\}]*)*)\\}/g,b=/($^)/,_=/['\\n\\r\\u2028\\u2029\\\\]/g;e.exports=n},function(e,t,r){var n=r(33),o=r(44),i=r(50),s=o(function(e,t,r,o){n(t,i(t),e,o)});e.exports=s},function(e,t,r){function n(e,t,r,n){var s=!r;r||(r={});for(var a=-1,u=t.length;++a<u;){var l=t[a],c=n?n(r[l],e[l],l,r,e):void 0;void 0===c&&(c=e[l]),s?i(r,l,c):o(r,l,c)}return r}var o=r(34),i=r(8);e.exports=n},function(e,t,r){function n(e,t,r){var n=e[t];a.call(e,t)&&i(n,r)&&(void 0!==r||t in e)||o(e,t,r)}var o=r(8),i=r(6),s=Object.prototype,a=s.hasOwnProperty;e.exports=n},function(e,t,r){function n(e,t){var r=i(e,t);return o(r)?r:void 0}var o=r(36),i=r(43);e.exports=n},function(e,t,r){function n(e){return!(!s(e)||i(e))&&(o(e)?d:l).test(a(e))}var o=r(10),i=r(40),s=r(2),a=r(42),u=/[\\\\^$.*+?()[\\]{}|]/g,l=/^\\[object .+?Constructor\\]$/,c=Function.prototype,f=Object.prototype,p=c.toString,h=f.hasOwnProperty,d=RegExp(\"^\"+p.call(h).replace(u,\"\\\\$&\").replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g,\"$1.*?\")+\"$\");e.exports=n},function(e,t){var r;r=function(){return this}();try{r=r||Function(\"return this\")()||(0,eval)(\"this\")}catch(e){\"object\"==typeof window&&(r=window)}e.exports=r},function(e,t,r){function n(e){var t=s.call(e,u),r=e[u];try{e[u]=void 0;var n=!0}catch(e){}var o=a.call(e);return n&&(t?e[u]=r:delete e[u]),o}var o=r(4),i=Object.prototype,s=i.hasOwnProperty,a=i.toString,u=o?o.toStringTag:void 0;e.exports=n},function(e,t){function r(e){return o.call(e)}var n=Object.prototype,o=n.toString;e.exports=r},function(e,t,r){function n(e){return!!i&&i in e}var o=r(41),i=function(){var e=/[^.]+$/.exec(o&&o.keys&&o.keys.IE_PROTO||\"\");return e?\"Symbol(src)_1.\"+e:\"\"}();e.exports=n},function(e,t,r){var n=r(5),o=n[\"__core-js_shared__\"];e.exports=o},function(e,t){function r(e){if(null!=e){try{return o.call(e)}catch(e){}try{return e+\"\"}catch(e){}}return\"\"}var n=Function.prototype,o=n.toString;e.exports=r},function(e,t){function r(e,t){return null==e?void 0:e[t]}e.exports=r},function(e,t,r){function n(e){return o(function(t,r){var n=-1,o=r.length,s=o>1?r[o-1]:void 0,a=o>2?r[2]:void 0;for(s=e.length>3&&\"function\"==typeof s?(o--,s):void 0,a&&i(r[0],r[1],a)&&(s=o<3?void 0:s,o=1),t=Object(t);++n<o;){var u=r[n];u&&e(t,u,n,s)}return t})}var o=r(12),i=r(15);e.exports=n},function(e,t,r){function n(e,t,r){return t=i(void 0===t?e.length-1:t,0),function(){for(var n=arguments,s=-1,a=i(n.length-t,0),u=Array(a);++s<a;)u[s]=n[t+s];s=-1;for(var l=Array(t+1);++s<t;)l[s]=n[s];return l[t]=r(u),o(e,this,l)}}var o=r(14),i=Math.max;e.exports=n},function(e,t,r){var n=r(47),o=r(49),i=o(n);e.exports=i},function(e,t,r){var n=r(48),o=r(9),i=r(13),s=o?function(e,t){return o(e,\"toString\",{configurable:!0,enumerable:!1,value:n(t),writable:!0})}:i;e.exports=s},function(e,t){function r(e){return function(){return e}}e.exports=r},function(e,t){function r(e){var t=0,r=0;return function(){var s=i(),a=o-(s-r);if(r=s,a>0){if(++t>=n)return arguments[0]}else t=0;return e.apply(void 0,arguments)}}var n=800,o=16,i=Date.now;e.exports=r},function(e,t,r){function n(e){return s(e)?o(e,!0):i(e)}var o=r(18),i=r(60),s=r(7);e.exports=n},function(e,t){function r(e,t){for(var r=-1,n=Array(e);++r<e;)n[r]=t(r);return n}e.exports=r},function(e,t,r){var n=r(53),o=r(1),i=Object.prototype,s=i.hasOwnProperty,a=i.propertyIsEnumerable,u=n(function(){return arguments}())?n:function(e){return o(e)&&s.call(e,\"callee\")&&!a.call(e,\"callee\")};e.exports=u},function(e,t,r){function n(e){return i(e)&&o(e)==s}var o=r(0),i=r(1),s=\"[object Arguments]\";e.exports=n},function(e,t,r){(function(e){var n=r(5),o=r(55),i=\"object\"==typeof t&&t&&!t.nodeType&&t,s=i&&\"object\"==typeof e&&e&&!e.nodeType&&e,a=s&&s.exports===i,u=a?n.Buffer:void 0,l=u?u.isBuffer:void 0,c=l||o;e.exports=c}).call(t,r(20)(e))},function(e,t){function r(){return!1}e.exports=r},function(e,t,r){var n=r(57),o=r(58),i=r(59),s=i&&i.isTypedArray,a=s?o(s):n;e.exports=a},function(e,t,r){function n(e){return s(e)&&i(e.length)&&!!a[o(e)]}var o=r(0),i=r(16),s=r(1),a={};a[\"[object Float32Array]\"]=a[\"[object Float64Array]\"]=a[\"[object Int8Array]\"]=a[\"[object Int16Array]\"]=a[\"[object Int32Array]\"]=a[\"[object Uint8Array]\"]=a[\"[object Uint8ClampedArray]\"]=a[\"[object Uint16Array]\"]=a[\"[object Uint32Array]\"]=!0,a[\"[object Arguments]\"]=a[\"[object Array]\"]=a[\"[object ArrayBuffer]\"]=a[\"[object Boolean]\"]=a[\"[object DataView]\"]=a[\"[object Date]\"]=a[\"[object Error]\"]=a[\"[object Function]\"]=a[\"[object Map]\"]=a[\"[object Number]\"]=a[\"[object Object]\"]=a[\"[object RegExp]\"]=a[\"[object Set]\"]=a[\"[object String]\"]=a[\"[object WeakMap]\"]=!1,e.exports=n},function(e,t){function r(e){return function(t){return e(t)}}e.exports=r},function(e,t,r){(function(e){var n=r(11),o=\"object\"==typeof t&&t&&!t.nodeType&&t,i=o&&\"object\"==typeof e&&e&&!e.nodeType&&e,s=i&&i.exports===o,a=s&&n.process,u=function(){try{return a&&a.binding&&a.binding(\"util\")}catch(e){}}();e.exports=u}).call(t,r(20)(e))},function(e,t,r){function n(e){if(!o(e))return s(e);var t=i(e),r=[];for(var n in e)(\"constructor\"!=n||!t&&u.call(e,n))&&r.push(n);return r}var o=r(2),i=r(21),s=r(61),a=Object.prototype,u=a.hasOwnProperty;e.exports=n},function(e,t){function r(e){var t=[];if(null!=e)for(var r in Object(e))t.push(r);return t}e.exports=r},function(e,t,r){var n=r(14),o=r(12),i=r(22),s=o(function(e,t){try{return n(e,void 0,t)}catch(e){return i(e)?e:new Error(e)}});e.exports=s},function(e,t,r){function n(e){if(!s(e)||o(e)!=a)return!1;var t=i(e);if(null===t)return!0;var r=f.call(t,\"constructor\")&&t.constructor;return\"function\"==typeof r&&r instanceof r&&c.call(r)==p}var o=r(0),i=r(64),s=r(1),a=\"[object Object]\",u=Function.prototype,l=Object.prototype,c=u.toString,f=l.hasOwnProperty,p=c.call(Object);e.exports=n},function(e,t,r){var n=r(23),o=n(Object.getPrototypeOf,Object);e.exports=o},function(e,t,r){function n(e,t){return o(t,function(t){return e[t]})}var o=r(24);e.exports=n},function(e,t,r){function n(e,t,r,n){return void 0===e||o(e,i[r])&&!s.call(n,r)?t:e}var o=r(6),i=Object.prototype,s=i.hasOwnProperty;e.exports=n},function(e,t){function r(e){return\"\\\\\"+n[e]}var n={\"\\\\\":\"\\\\\",\"'\":\"'\",\"\\n\":\"n\",\"\\r\":\"r\",\"\\u2028\":\"u2028\",\"\\u2029\":\"u2029\"};e.exports=r},function(e,t,r){function n(e){return s(e)?o(e):i(e)}var o=r(18),i=r(69),s=r(7);e.exports=n},function(e,t,r){function n(e){if(!o(e))return i(e);var t=[];for(var r in Object(e))a.call(e,r)&&\"constructor\"!=r&&t.push(r);return t}var o=r(21),i=r(70),s=Object.prototype,a=s.hasOwnProperty;e.exports=n},function(e,t,r){var n=r(23),o=n(Object.keys,Object);e.exports=o},function(e,t,r){var n=r(72),o=r(77),i=r(78),s=r(25),a={escape:o,evaluate:i,interpolate:s,variable:\"\",imports:{_:{escape:n}}};e.exports=a},function(e,t,r){function n(e){return e=i(e),e&&a.test(e)?e.replace(s,o):e}var o=r(73),i=r(26),s=/[&<>\"']/g,a=RegExp(s.source);e.exports=n},function(e,t,r){var n=r(74),o={\"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",'\"':\"&quot;\",\"'\":\"&#39;\"},i=n(o);e.exports=i},function(e,t){function r(e){return function(t){return null==e?void 0:e[t]}}e.exports=r},function(e,t,r){function n(e){if(\"string\"==typeof e)return e;if(s(e))return i(e,n)+\"\";if(a(e))return c?c.call(e):\"\";var t=e+\"\";return\"0\"==t&&1/e==-u?\"-0\":t}var o=r(4),i=r(24),s=r(19),a=r(76),u=1/0,l=o?o.prototype:void 0,c=l?l.toString:void 0;e.exports=n},function(e,t,r){function n(e){return\"symbol\"==typeof e||i(e)&&o(e)==s}var o=r(0),i=r(1),s=\"[object Symbol]\";e.exports=n},function(e,t){var r=/<%-([\\s\\S]+?)%>/g;e.exports=r},function(e,t){var r=/<%([\\s\\S]+?)%>/g;e.exports=r},function(e,t,r){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default={acres:{factor:24711e-8,display:\"acres\",decimals:2},feet:{factor:3.2808,display:\"feet\",decimals:0},kilometers:{factor:.001,display:\"kilometers\",decimals:2},hectares:{factor:1e-4,display:\"hectares\",decimals:2},meters:{factor:1,display:\"meters\",decimals:0},miles:{factor:3.2808/5280,display:\"miles\",decimals:2},sqfeet:{factor:10.7639,display:\"sqfeet\",decimals:0},sqmeters:{factor:1,display:\"sqmeters\",decimals:0},sqmiles:{factor:3.86102e-7,display:\"sqmiles\",decimals:2}}},function(e,t,r){\"use strict\";function n(e){return e&&e.__esModule?e:{default:e}}function o(e){return e<10?\"0\"+e.toString():e.toString()}function i(e,t,r){var n=Math.abs(e),i=Math.floor(n),s=Math.floor(60*(n-i)),a=Math.round(3600*(n-i-s/60)*100)/100,u=n===e?t:r;return o(i)+\"&deg; \"+o(s)+\"' \"+o(a)+'\" '+u}function s(e){var t=e[e.length-1],r=e.map(function(e){return[e.lat,e.lng]}),n=L.polyline(r),o=L.polygon(r),s=1e3*(0,u.default)(n.toGeoJSON(),{units:\"kilometers\"}),a=(0,c.default)(o.toGeoJSON());return{lastCoord:{dd:{x:t.lng,y:t.lat},dms:{x:i(t.lng,\"E\",\"W\"),y:i(t.lat,\"N\",\"S\")}},length:s,area:a}}Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=s;var a=r(81),u=n(a),l=r(84),c=n(l)},function(e,t,r){\"use strict\";function n(e,t){if(t=t||{},!Object(s.d)(t))throw new Error(\"options is invalid\");if(!e)throw new Error(\"geojson is required\");return Object(i.b)(e,function(e,r){var n=r.geometry.coordinates;return e+Object(o.a)(n[0],n[1],t)},0)}Object.defineProperty(t,\"__esModule\",{value:!0});var o=r(82),i=r(27),s=r(3);t.default=n},function(e,t,r){\"use strict\";function n(e,t,r){if(r=r||{},!Object(i.d)(r))throw new Error(\"options is invalid\");var n=r.units,s=Object(o.a)(e),a=Object(o.a)(t),u=Object(i.a)(a[1]-s[1]),l=Object(i.a)(a[0]-s[0]),c=Object(i.a)(s[1]),f=Object(i.a)(a[1]),p=Math.pow(Math.sin(u/2),2)+Math.pow(Math.sin(l/2),2)*Math.cos(c)*Math.cos(f);return Object(i.g)(2*Math.atan2(Math.sqrt(p),Math.sqrt(1-p)),n)}var o=r(83),i=r(3);t.a=n},function(e,t,r){\"use strict\";function n(e){if(!e)throw new Error(\"coord is required\");if(\"Feature\"===e.type&&null!==e.geometry&&\"Point\"===e.geometry.type)return e.geometry.coordinates;if(\"Point\"===e.type)return e.coordinates;if(Array.isArray(e)&&e.length>=2&&void 0===e[0].length&&void 0===e[1].length)return e;throw new Error(\"coord must be GeoJSON Point or an Array of numbers\")}r.d(t,\"a\",function(){return n});r(3)},function(e,t,r){\"use strict\";function n(e){return Object(u.a)(e,function(e,t){return e+o(t)},0)}function o(e){var t,r=0;switch(e.type){case\"Polygon\":return i(e.coordinates);case\"MultiPolygon\":for(t=0;t<e.coordinates.length;t++)r+=i(e.coordinates[t]);return r;case\"Point\":case\"MultiPoint\":case\"LineString\":case\"MultiLineString\":return 0;case\"GeometryCollection\":for(t=0;t<e.geometries.length;t++)r+=o(e.geometries[t]);return r}}function i(e){var t=0;if(e&&e.length>0){t+=Math.abs(s(e[0]));for(var r=1;r<e.length;r++)t-=Math.abs(s(e[r]))}return t}function s(e){var t,r,n,o,i,s,u,c=0,f=e.length;if(f>2){for(u=0;u<f;u++)u===f-2?(o=f-2,i=f-1,s=0):u===f-1?(o=f-1,i=0,s=1):(o=u,i=u+1,s=u+2),t=e[o],r=e[i],n=e[s],c+=(a(n[0])-a(t[0]))*Math.sin(a(r[1]));c=c*l*l/2}return c}function a(e){return e*Math.PI/180}Object.defineProperty(t,\"__esModule\",{value:!0});var u=r(27),l=6378137;t.default=n},function(e,t,r){\"use strict\";function n(e,t){return t||(t=document),t.querySelector(e)}function o(e,t){return t||(t=document),Array.prototype.slice.call(t.querySelectorAll(e))}function i(e){if(e)return e.setAttribute(\"style\",\"display:none;\"),e}function s(e){if(e)return e.removeAttribute(\"style\"),e}Object.defineProperty(t,\"__esModule\",{value:!0}),t.selectOne=n,t.selectAll=o,t.hide=i,t.show=s},function(e,t,r){\"use strict\";function n(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}Object.defineProperty(t,\"__esModule\",{value:!0});var o=function(){function e(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,\"value\"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,r,n){return r&&e(t.prototype,r),n&&e(t,n),t}}(),i={activeColor:\"#ABE67E\",completedColor:\"#C8F2BE\"},s=function(){function e(t){n(this,e),this._options=L.extend({},i,this._options,t)}return o(e,[{key:\"getSymbol\",value:function(e){return{measureDrag:{clickable:!1,radius:4,color:this._options.activeColor,weight:2,opacity:.7,fillColor:this._options.activeColor,fillOpacity:.5,className:\"layer-measuredrag\"},measureArea:{clickable:!1,stroke:!1,fillColor:this._options.activeColor,fillOpacity:.2,className:\"layer-measurearea\"},measureBoundary:{clickable:!1,color:this._options.activeColor,weight:2,opacity:.9,fill:!1,className:\"layer-measureboundary\"},measureVertex:{clickable:!1,radius:4,color:this._options.activeColor,weight:2,opacity:1,fillColor:this._options.activeColor,fillOpacity:.7,className:\"layer-measurevertex\"},measureVertexActive:{clickable:!1,radius:4,color:this._options.activeColor,weight:2,opacity:1,fillColor:this._options.activeColor,fillOpacity:1,className:\"layer-measurevertex active\"},resultArea:{clickable:!0,color:this._options.completedColor,weight:2,opacity:.9,fillColor:this._options.completedColor,fillOpacity:.2,className:\"layer-measure-resultarea\"},resultLine:{clickable:!0,color:this._options.completedColor,weight:3,opacity:.9,fill:!1,className:\"layer-measure-resultline\"},resultPoint:{clickable:!0,radius:4,color:this._options.completedColor,weight:2,opacity:1,fillColor:this._options.completedColor,fillOpacity:.7,className:\"layer-measure-resultpoint\"}}[e]}}]),e}();t.default=s},function(e,t,r){\"use strict\";function n(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:2,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:\".\",n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:\",\",o=e<0?\"-\":\"\",i=Math.abs(+e||0),s=parseInt(i.toFixed(t),10)+\"\",a=s.length>3?s.length%3:0;return[o,a?s.substr(0,a)+n:\"\",s.substr(a).replace(/(\\d{3})(?=\\d)/g,\"$1\"+n),t?\"\"+r+Math.abs(i-s).toFixed(t).slice(2):\"\"].join(\"\")}Object.defineProperty(t,\"__esModule\",{value:!0}),t.numberFormat=n},function(e,t,r){\"use strict\";function n(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,\"__esModule\",{value:!0});var o=r(89);Object.defineProperty(t,\"controlTemplate\",{enumerable:!0,get:function(){return n(o).default}});var i=r(90);Object.defineProperty(t,\"resultsTemplate\",{enumerable:!0,get:function(){return n(i).default}});var s=r(91);Object.defineProperty(t,\"pointPopupTemplate\",{enumerable:!0,get:function(){return n(s).default}});var a=r(92);Object.defineProperty(t,\"linePopupTemplate\",{enumerable:!0,get:function(){return n(a).default}});var u=r(93);Object.defineProperty(t,\"areaPopupTemplate\",{enumerable:!0,get:function(){return n(u).default}})},function(e,t,r){e.exports='<a class=\"{{ model.className }}-toggle js-toggle\" href=# title=\"Measure distances and areas\">Measure</a> <div class=\"{{ model.className }}-interaction js-interaction\"> <div class=\"js-startprompt startprompt\"> <h3>Measure distances and areas</h3> <ul class=tasks> <a href=# class=\"js-start start\">Create a new measurement</a> </ul> </div> <div class=js-measuringprompt> <h3>Measure distances and areas</h3> <p class=js-starthelp>Start creating a measurement by adding points to the map</p> <div class=\"js-results results\"></div> <ul class=\"js-measuretasks tasks\"> <li><a href=# class=\"js-cancel cancel\">Cancel</a></li> <li><a href=# class=\"js-finish finish\">Finish measurement</a></li> </ul> </div> </div> '},function(e,t,r){e.exports='<div class=group> <p class=\"lastpoint heading\">Last point</p> <p>{{ model.lastCoord.dms.y }} <span class=coorddivider>/</span> {{ model.lastCoord.dms.x }}</p> <p>{{ numberFormat(model.lastCoord.dd.y, 6) }} <span class=coorddivider>/</span> {{ numberFormat(model.lastCoord.dd.x, 6) }}</p> </div> <% if (model.pointCount > 1) { %> <div class=group> <p><span class=heading>Path distance</span> {{ model.lengthDisplay }}</p> </div> <% } %> <% if (model.pointCount > 2) { %> <div class=group> <p><span class=heading>Area</span> {{ model.areaDisplay }}</p> </div> <% } %> '},function(e,t,r){e.exports='<h3>Point location</h3> <p>{{ model.lastCoord.dms.y }} <span class=coorddivider>/</span> {{ model.lastCoord.dms.x }}</p> <p>{{ numberFormat(model.lastCoord.dd.y, 6) }} <span class=coorddivider>/</span> {{ numberFormat(model.lastCoord.dd.x, 6) }}</p> <ul class=tasks> <li><a href=# class=\"js-zoomto zoomto\">Center on this location</a></li> <li><a href=# class=\"js-deletemarkup deletemarkup\">Delete</a></li> </ul> '},function(e,t,r){e.exports='<h3>Linear measurement</h3> <p>{{ model.lengthDisplay }}</p> <ul class=tasks> <li><a href=# class=\"js-zoomto zoomto\">Center on this line</a></li> <li><a href=# class=\"js-deletemarkup deletemarkup\">Delete</a></li> </ul> '},function(e,t,r){e.exports='<h3>Area measurement</h3> <p>{{ model.areaDisplay }}</p> <p>{{ model.lengthDisplay }} Perimeter</p> <ul class=tasks> <li><a href=# class=\"js-zoomto zoomto\">Center on this area</a></li> <li><a href=# class=\"js-deletemarkup deletemarkup\">Delete</a></li> </ul> '}]);"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/leaflet.css",
    "content": "/* required styles */\n\n.leaflet-pane,\n.leaflet-tile,\n.leaflet-marker-icon,\n.leaflet-marker-shadow,\n.leaflet-tile-container,\n.leaflet-pane > svg,\n.leaflet-pane > canvas,\n.leaflet-zoom-box,\n.leaflet-image-layer,\n.leaflet-layer {\n\tposition: absolute;\n\tleft: 0;\n\ttop: 0;\n\t}\n.leaflet-container {\n\toverflow: hidden;\n\t}\n.leaflet-tile,\n.leaflet-marker-icon,\n.leaflet-marker-shadow {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t        user-select: none;\n\t  -webkit-user-drag: none;\n\t}\n/* Prevents IE11 from highlighting tiles in blue */\n.leaflet-tile::selection {\n\tbackground: transparent;\n}\n/* Safari renders non-retina tile on retina better with this, but Chrome is worse */\n.leaflet-safari .leaflet-tile {\n\timage-rendering: -webkit-optimize-contrast;\n\t}\n/* hack that prevents hw layers \"stretching\" when loading new tiles */\n.leaflet-safari .leaflet-tile-container {\n\twidth: 1600px;\n\theight: 1600px;\n\t-webkit-transform-origin: 0 0;\n\t}\n.leaflet-marker-icon,\n.leaflet-marker-shadow {\n\tdisplay: block;\n\t}\n/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */\n/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */\n.leaflet-container .leaflet-overlay-pane svg {\n\tmax-width: none !important;\n\tmax-height: none !important;\n\t}\n.leaflet-container .leaflet-marker-pane img,\n.leaflet-container .leaflet-shadow-pane img,\n.leaflet-container .leaflet-tile-pane img,\n.leaflet-container img.leaflet-image-layer,\n.leaflet-container .leaflet-tile {\n\tmax-width: none !important;\n\tmax-height: none !important;\n\twidth: auto;\n\tpadding: 0;\n\t}\n\n.leaflet-container img.leaflet-tile {\n\t/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */\n\tmix-blend-mode: plus-lighter;\n}\n\n.leaflet-container.leaflet-touch-zoom {\n\t-ms-touch-action: pan-x pan-y;\n\ttouch-action: pan-x pan-y;\n\t}\n.leaflet-container.leaflet-touch-drag {\n\t-ms-touch-action: pinch-zoom;\n\t/* Fallback for FF which doesn't support pinch-zoom */\n\ttouch-action: none;\n\ttouch-action: pinch-zoom;\n}\n.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {\n\t-ms-touch-action: none;\n\ttouch-action: none;\n}\n.leaflet-container {\n\t-webkit-tap-highlight-color: transparent;\n}\n.leaflet-container a {\n\t-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);\n}\n.leaflet-tile {\n\tfilter: inherit;\n\tvisibility: hidden;\n\t}\n.leaflet-tile-loaded {\n\tvisibility: inherit;\n\t}\n.leaflet-zoom-box {\n\twidth: 0;\n\theight: 0;\n\t-moz-box-sizing: border-box;\n\t     box-sizing: border-box;\n\tz-index: 800;\n\t}\n/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */\n.leaflet-overlay-pane svg {\n\t-moz-user-select: none;\n\t}\n\n.leaflet-pane         { z-index: 400; }\n\n.leaflet-tile-pane    { z-index: 200; }\n.leaflet-overlay-pane { z-index: 400; }\n.leaflet-shadow-pane  { z-index: 500; }\n.leaflet-marker-pane  { z-index: 600; }\n.leaflet-tooltip-pane   { z-index: 650; }\n.leaflet-popup-pane   { z-index: 700; }\n\n.leaflet-map-pane canvas { z-index: 100; }\n.leaflet-map-pane svg    { z-index: 200; }\n\n.leaflet-vml-shape {\n\twidth: 1px;\n\theight: 1px;\n\t}\n.lvml {\n\tbehavior: url(#default#VML);\n\tdisplay: inline-block;\n\tposition: absolute;\n\t}\n\n\n/* control positioning */\n\n.leaflet-control {\n\tposition: relative;\n\tz-index: 800;\n\tpointer-events: visiblePainted; /* IE 9-10 doesn't have auto */\n\tpointer-events: auto;\n\t}\n.leaflet-top,\n.leaflet-bottom {\n\tposition: absolute;\n\tz-index: 1000;\n\tpointer-events: none;\n\t}\n.leaflet-top {\n\ttop: 0;\n\t}\n.leaflet-right {\n\tright: 0;\n\t}\n.leaflet-bottom {\n\tbottom: 0;\n\t}\n.leaflet-left {\n\tleft: 0;\n\t}\n.leaflet-control {\n\tfloat: left;\n\tclear: both;\n\t}\n.leaflet-right .leaflet-control {\n\tfloat: right;\n\t}\n.leaflet-top .leaflet-control {\n\tmargin-top: 10px;\n\t}\n.leaflet-bottom .leaflet-control {\n\tmargin-bottom: 10px;\n\t}\n.leaflet-left .leaflet-control {\n\tmargin-left: 10px;\n\t}\n.leaflet-right .leaflet-control {\n\tmargin-right: 10px;\n\t}\n\n\n/* zoom and fade animations */\n\n.leaflet-fade-anim .leaflet-popup {\n\topacity: 0;\n\t-webkit-transition: opacity 0.2s linear;\n\t   -moz-transition: opacity 0.2s linear;\n\t        transition: opacity 0.2s linear;\n\t}\n.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {\n\topacity: 1;\n\t}\n.leaflet-zoom-animated {\n\t-webkit-transform-origin: 0 0;\n\t    -ms-transform-origin: 0 0;\n\t        transform-origin: 0 0;\n\t}\nsvg.leaflet-zoom-animated {\n\twill-change: transform;\n}\n\n.leaflet-zoom-anim .leaflet-zoom-animated {\n\t-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);\n\t   -moz-transition:    -moz-transform 0.25s cubic-bezier(0,0,0.25,1);\n\t        transition:         transform 0.25s cubic-bezier(0,0,0.25,1);\n\t}\n.leaflet-zoom-anim .leaflet-tile,\n.leaflet-pan-anim .leaflet-tile {\n\t-webkit-transition: none;\n\t   -moz-transition: none;\n\t        transition: none;\n\t}\n\n.leaflet-zoom-anim .leaflet-zoom-hide {\n\tvisibility: hidden;\n\t}\n\n\n/* cursors */\n\n.leaflet-interactive {\n\tcursor: pointer;\n\t}\n.leaflet-grab {\n\tcursor: -webkit-grab;\n\tcursor:    -moz-grab;\n\tcursor:         grab;\n\t}\n.leaflet-crosshair,\n.leaflet-crosshair .leaflet-interactive {\n\tcursor: crosshair;\n\t}\n.leaflet-popup-pane,\n.leaflet-control {\n\tcursor: auto;\n\t}\n.leaflet-dragging .leaflet-grab,\n.leaflet-dragging .leaflet-grab .leaflet-interactive,\n.leaflet-dragging .leaflet-marker-draggable {\n\tcursor: move;\n\tcursor: -webkit-grabbing;\n\tcursor:    -moz-grabbing;\n\tcursor:         grabbing;\n\t}\n\n/* marker & overlays interactivity */\n.leaflet-marker-icon,\n.leaflet-marker-shadow,\n.leaflet-image-layer,\n.leaflet-pane > svg path,\n.leaflet-tile-container {\n\tpointer-events: none;\n\t}\n\n.leaflet-marker-icon.leaflet-interactive,\n.leaflet-image-layer.leaflet-interactive,\n.leaflet-pane > svg path.leaflet-interactive,\nsvg.leaflet-image-layer.leaflet-interactive path {\n\tpointer-events: visiblePainted; /* IE 9-10 doesn't have auto */\n\tpointer-events: auto;\n\t}\n\n/* visual tweaks */\n\n.leaflet-container {\n\tbackground: #ddd;\n\toutline-offset: 1px;\n\t}\n.leaflet-container a {\n\tcolor: #0078A8;\n\t}\n.leaflet-zoom-box {\n\tborder: 2px dotted #38f;\n\tbackground: rgba(255,255,255,0.5);\n\t}\n\n\n/* general typography */\n.leaflet-container {\n\tfont-family: \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n\tfont-size: 12px;\n\tfont-size: 0.75rem;\n\tline-height: 1.5;\n\t}\n\n\n/* general toolbar styles */\n\n.leaflet-bar {\n\tbox-shadow: 0 1px 5px rgba(0,0,0,0.65);\n\tborder-radius: 4px;\n\t}\n.leaflet-bar a {\n\tbackground-color: #fff;\n\tborder-bottom: 1px solid #ccc;\n\twidth: 26px;\n\theight: 26px;\n\tline-height: 26px;\n\tdisplay: block;\n\ttext-align: center;\n\ttext-decoration: none;\n\tcolor: black;\n\t}\n.leaflet-bar a,\n.leaflet-control-layers-toggle {\n\tbackground-position: 50% 50%;\n\tbackground-repeat: no-repeat;\n\tdisplay: block;\n\t}\n.leaflet-bar a:hover,\n.leaflet-bar a:focus {\n\tbackground-color: #f4f4f4;\n\t}\n.leaflet-bar a:first-child {\n\tborder-top-left-radius: 4px;\n\tborder-top-right-radius: 4px;\n\t}\n.leaflet-bar a:last-child {\n\tborder-bottom-left-radius: 4px;\n\tborder-bottom-right-radius: 4px;\n\tborder-bottom: none;\n\t}\n.leaflet-bar a.leaflet-disabled {\n\tcursor: default;\n\tbackground-color: #f4f4f4;\n\tcolor: #bbb;\n\t}\n\n.leaflet-touch .leaflet-bar a {\n\twidth: 30px;\n\theight: 30px;\n\tline-height: 30px;\n\t}\n.leaflet-touch .leaflet-bar a:first-child {\n\tborder-top-left-radius: 2px;\n\tborder-top-right-radius: 2px;\n\t}\n.leaflet-touch .leaflet-bar a:last-child {\n\tborder-bottom-left-radius: 2px;\n\tborder-bottom-right-radius: 2px;\n\t}\n\n/* zoom control */\n\n.leaflet-control-zoom-in,\n.leaflet-control-zoom-out {\n\tfont: bold 18px 'Lucida Console', Monaco, monospace;\n\ttext-indent: 1px;\n\t}\n\n.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out  {\n\tfont-size: 22px;\n\t}\n\n\n/* layers control */\n\n.leaflet-control-layers {\n\tbox-shadow: 0 1px 5px rgba(0,0,0,0.4);\n\tbackground: #fff;\n\tborder-radius: 5px;\n\t}\n.leaflet-control-layers-toggle {\n\tbackground-image: url(images/layers.png);\n\twidth: 36px;\n\theight: 36px;\n\t}\n.leaflet-retina .leaflet-control-layers-toggle {\n\tbackground-image: url(images/layers-2x.png);\n\tbackground-size: 26px 26px;\n\t}\n.leaflet-touch .leaflet-control-layers-toggle {\n\twidth: 44px;\n\theight: 44px;\n\t}\n.leaflet-control-layers .leaflet-control-layers-list,\n.leaflet-control-layers-expanded .leaflet-control-layers-toggle {\n\tdisplay: none;\n\t}\n.leaflet-control-layers-expanded .leaflet-control-layers-list {\n\tdisplay: block;\n\tposition: relative;\n\t}\n.leaflet-control-layers-expanded {\n\tpadding: 6px 10px 6px 6px;\n\tcolor: #333;\n\tbackground: #fff;\n\t}\n.leaflet-control-layers-scrollbar {\n\toverflow-y: scroll;\n\toverflow-x: hidden;\n\tpadding-right: 5px;\n\t}\n.leaflet-control-layers-selector {\n\tmargin-top: 2px;\n\tposition: relative;\n\ttop: 1px;\n\t}\n.leaflet-control-layers label {\n\tdisplay: block;\n\tfont-size: 13px;\n\tfont-size: 1.08333em;\n\t}\n.leaflet-control-layers-separator {\n\theight: 0;\n\tborder-top: 1px solid #ddd;\n\tmargin: 5px -10px 5px -6px;\n\t}\n\n/* Default icon URLs */\n.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */\n\tbackground-image: url(images/marker-icon.png);\n\t}\n\n\n/* attribution and scale controls */\n\n.leaflet-container .leaflet-control-attribution {\n\tbackground: #fff;\n\tbackground: rgba(255, 255, 255, 0.8);\n\tmargin: 0;\n\t}\n.leaflet-control-attribution,\n.leaflet-control-scale-line {\n\tpadding: 0 5px;\n\tcolor: #333;\n\tline-height: 1.4;\n\t}\n.leaflet-control-attribution a {\n\ttext-decoration: none;\n\t}\n.leaflet-control-attribution a:hover,\n.leaflet-control-attribution a:focus {\n\ttext-decoration: underline;\n\t}\n.leaflet-attribution-flag {\n\tdisplay: inline !important;\n\tvertical-align: baseline !important;\n\twidth: 1em;\n\theight: 0.6669em;\n\t}\n.leaflet-left .leaflet-control-scale {\n\tmargin-left: 5px;\n\t}\n.leaflet-bottom .leaflet-control-scale {\n\tmargin-bottom: 5px;\n\t}\n.leaflet-control-scale-line {\n\tborder: 2px solid #777;\n\tborder-top: none;\n\tline-height: 1.1;\n\tpadding: 2px 5px 1px;\n\twhite-space: nowrap;\n\t-moz-box-sizing: border-box;\n\t     box-sizing: border-box;\n\tbackground: rgba(255, 255, 255, 0.8);\n\ttext-shadow: 1px 1px #fff;\n\t}\n.leaflet-control-scale-line:not(:first-child) {\n\tborder-top: 2px solid #777;\n\tborder-bottom: none;\n\tmargin-top: -2px;\n\t}\n.leaflet-control-scale-line:not(:first-child):not(:last-child) {\n\tborder-bottom: 2px solid #777;\n\t}\n\n.leaflet-touch .leaflet-control-attribution,\n.leaflet-touch .leaflet-control-layers,\n.leaflet-touch .leaflet-bar {\n\tbox-shadow: none;\n\t}\n.leaflet-touch .leaflet-control-layers,\n.leaflet-touch .leaflet-bar {\n\tborder: 2px solid rgba(0,0,0,0.2);\n\tbackground-clip: padding-box;\n\t}\n\n\n/* popup */\n\n.leaflet-popup {\n\tposition: absolute;\n\ttext-align: center;\n\tmargin-bottom: 20px;\n\t}\n.leaflet-popup-content-wrapper {\n\tpadding: 1px;\n\ttext-align: left;\n\tborder-radius: 12px;\n\t}\n.leaflet-popup-content {\n\tmargin: 13px 24px 13px 20px;\n\tline-height: 1.3;\n\tfont-size: 13px;\n\tfont-size: 1.08333em;\n\tmin-height: 1px;\n\t}\n.leaflet-popup-content p {\n\tmargin: 17px 0;\n\tmargin: 1.3em 0;\n\t}\n.leaflet-popup-tip-container {\n\twidth: 40px;\n\theight: 20px;\n\tposition: absolute;\n\tleft: 50%;\n\tmargin-top: -1px;\n\tmargin-left: -20px;\n\toverflow: hidden;\n\tpointer-events: none;\n\t}\n.leaflet-popup-tip {\n\twidth: 17px;\n\theight: 17px;\n\tpadding: 1px;\n\n\tmargin: -10px auto 0;\n\tpointer-events: auto;\n\n\t-webkit-transform: rotate(45deg);\n\t   -moz-transform: rotate(45deg);\n\t    -ms-transform: rotate(45deg);\n\t        transform: rotate(45deg);\n\t}\n.leaflet-popup-content-wrapper,\n.leaflet-popup-tip {\n\tbackground: white;\n\tcolor: #333;\n\tbox-shadow: 0 3px 14px rgba(0,0,0,0.4);\n\t}\n.leaflet-container a.leaflet-popup-close-button {\n\tposition: absolute;\n\ttop: 0;\n\tright: 0;\n\tborder: none;\n\ttext-align: center;\n\twidth: 24px;\n\theight: 24px;\n\tfont: 16px/24px Tahoma, Verdana, sans-serif;\n\tcolor: #757575;\n\ttext-decoration: none;\n\tbackground: transparent;\n\t}\n.leaflet-container a.leaflet-popup-close-button:hover,\n.leaflet-container a.leaflet-popup-close-button:focus {\n\tcolor: #585858;\n\t}\n.leaflet-popup-scrolled {\n\toverflow: auto;\n\t}\n\n.leaflet-oldie .leaflet-popup-content-wrapper {\n\t-ms-zoom: 1;\n\t}\n.leaflet-oldie .leaflet-popup-tip {\n\twidth: 24px;\n\tmargin: 0 auto;\n\n\t-ms-filter: \"progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)\";\n\tfilter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);\n\t}\n\n.leaflet-oldie .leaflet-control-zoom,\n.leaflet-oldie .leaflet-control-layers,\n.leaflet-oldie .leaflet-popup-content-wrapper,\n.leaflet-oldie .leaflet-popup-tip {\n\tborder: 1px solid #999;\n\t}\n\n\n/* div icon */\n\n.leaflet-div-icon {\n\tbackground: #fff;\n\tborder: 1px solid #666;\n\t}\n\n\n/* Tooltip */\n/* Base styles for the element that has a tooltip */\n.leaflet-tooltip {\n\tposition: absolute;\n\tpadding: 6px;\n\tbackground-color: #fff;\n\tborder: 1px solid #fff;\n\tborder-radius: 3px;\n\tcolor: #222;\n\twhite-space: nowrap;\n\t-webkit-user-select: none;\n\t-moz-user-select: none;\n\t-ms-user-select: none;\n\tuser-select: none;\n\tpointer-events: none;\n\tbox-shadow: 0 1px 3px rgba(0,0,0,0.4);\n\t}\n.leaflet-tooltip.leaflet-interactive {\n\tcursor: pointer;\n\tpointer-events: auto;\n\t}\n.leaflet-tooltip-top:before,\n.leaflet-tooltip-bottom:before,\n.leaflet-tooltip-left:before,\n.leaflet-tooltip-right:before {\n\tposition: absolute;\n\tpointer-events: none;\n\tborder: 6px solid transparent;\n\tbackground: transparent;\n\tcontent: \"\";\n\t}\n\n/* Directions */\n\n.leaflet-tooltip-bottom {\n\tmargin-top: 6px;\n}\n.leaflet-tooltip-top {\n\tmargin-top: -6px;\n}\n.leaflet-tooltip-bottom:before,\n.leaflet-tooltip-top:before {\n\tleft: 50%;\n\tmargin-left: -6px;\n\t}\n.leaflet-tooltip-top:before {\n\tbottom: 0;\n\tmargin-bottom: -12px;\n\tborder-top-color: #fff;\n\t}\n.leaflet-tooltip-bottom:before {\n\ttop: 0;\n\tmargin-top: -12px;\n\tmargin-left: -6px;\n\tborder-bottom-color: #fff;\n\t}\n.leaflet-tooltip-left {\n\tmargin-left: -6px;\n}\n.leaflet-tooltip-right {\n\tmargin-left: 6px;\n}\n.leaflet-tooltip-left:before,\n.leaflet-tooltip-right:before {\n\ttop: 50%;\n\tmargin-top: -6px;\n\t}\n.leaflet-tooltip-left:before {\n\tright: 0;\n\tmargin-right: -12px;\n\tborder-left-color: #fff;\n\t}\n.leaflet-tooltip-right:before {\n\tleft: 0;\n\tmargin-left: -12px;\n\tborder-right-color: #fff;\n\t}\n\n/* Printing */\n\n@media print {\n\t/* Prevent printers from removing background-images of controls. */\n\t.leaflet-control {\n\t\t-webkit-print-color-adjust: exact;\n\t\tprint-color-adjust: exact;\n\t\t}\n\t}\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/leaflet.draw.css",
    "content": "/* ================================================================== */\n/* Toolbars\n/* ================================================================== */\n\n.leaflet-draw-section {\n\tposition: relative;\n}\n\n.leaflet-draw-toolbar {\n\tmargin-top: 12px;\n}\n\n.leaflet-draw-toolbar-top {\n\tmargin-top: 0;\n}\n\n.leaflet-draw-toolbar-notop a:first-child {\n\tborder-top-right-radius: 0;\n}\n\n.leaflet-draw-toolbar-nobottom a:last-child {\n\tborder-bottom-right-radius: 0;\n}\n\n.leaflet-draw-toolbar a {\n\tbackground-image: url('images/spritesheet.png');\n\tbackground-image: linear-gradient(transparent, transparent), url('images/spritesheet.svg');\n\tbackground-repeat: no-repeat;\n\tbackground-size: 300px 30px;\n\tbackground-clip: padding-box;\n}\n\n.leaflet-retina .leaflet-draw-toolbar a {\n\tbackground-image: url('images/spritesheet-2x.png');\n\tbackground-image: linear-gradient(transparent, transparent), url('images/spritesheet.svg');\n}\n\n.leaflet-draw a {\n\tdisplay: block;\n\ttext-align: center;\n\ttext-decoration: none;\n}\n\n.leaflet-draw a .sr-only {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\tpadding: 0;\n\tmargin: -1px;\n\toverflow: hidden;\n\tclip: rect(0, 0, 0, 0);\n\tborder: 0;\n}\n\n/* ================================================================== */\n/* Toolbar actions menu\n/* ================================================================== */\n\n.leaflet-draw-actions {\n\tdisplay: none;\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tposition: absolute;\n\tleft: 26px; /* leaflet-draw-toolbar.left + leaflet-draw-toolbar.width */\n\ttop: 0;\n\twhite-space: nowrap;\n}\n\n.leaflet-touch .leaflet-draw-actions {\n\tleft: 32px;\n}\n\n.leaflet-right .leaflet-draw-actions {\n\tright: 26px;\n\tleft: auto;\n}\n\n.leaflet-touch .leaflet-right .leaflet-draw-actions {\n\tright: 32px;\n\tleft: auto;\n}\n\n.leaflet-draw-actions li {\n\tdisplay: inline-block;\n}\n\n.leaflet-draw-actions li:first-child a {\n\tborder-left: none;\n}\n\n.leaflet-draw-actions li:last-child a {\n\t-webkit-border-radius: 0 4px 4px 0;\n\tborder-radius: 0 4px 4px 0;\n}\n\n.leaflet-right .leaflet-draw-actions li:last-child a {\n\t-webkit-border-radius: 0;\n\tborder-radius: 0;\n}\n\n.leaflet-right .leaflet-draw-actions li:first-child a {\n\t-webkit-border-radius: 4px 0 0 4px;\n\tborder-radius: 4px 0 0 4px;\n}\n\n.leaflet-draw-actions a {\n\tbackground-color: #919187;\n\tborder-left: 1px solid #AAA;\n\tcolor: #FFF;\n\tfont: 11px/19px \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n\tline-height: 28px;\n\ttext-decoration: none;\n\tpadding-left: 10px;\n\tpadding-right: 10px;\n\theight: 28px;\n}\n\n.leaflet-touch .leaflet-draw-actions a {\n\tfont-size: 12px;\n\tline-height: 30px;\n\theight: 30px;\n}\n\n.leaflet-draw-actions-bottom {\n\tmargin-top: 0;\n}\n\n.leaflet-draw-actions-top {\n\tmargin-top: 1px;\n}\n\n.leaflet-draw-actions-top a,\n.leaflet-draw-actions-bottom a {\n\theight: 27px;\n\tline-height: 27px;\n}\n\n.leaflet-draw-actions a:hover {\n\tbackground-color: #A0A098;\n}\n\n.leaflet-draw-actions-top.leaflet-draw-actions-bottom a {\n\theight: 26px;\n\tline-height: 26px;\n}\n\n/* ================================================================== */\n/* Draw toolbar\n/* ================================================================== */\n\n.leaflet-draw-toolbar .leaflet-draw-draw-polyline {\n\tbackground-position: -2px -2px;\n}\n\n.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polyline {\n\tbackground-position: 0 -1px;\n}\n\n.leaflet-draw-toolbar .leaflet-draw-draw-polygon {\n\tbackground-position: -31px -2px;\n}\n\n.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polygon {\n\tbackground-position: -29px -1px;\n}\n\n.leaflet-draw-toolbar .leaflet-draw-draw-rectangle {\n\tbackground-position: -62px -2px;\n}\n\n.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-rectangle {\n\tbackground-position: -60px -1px;\n}\n\n.leaflet-draw-toolbar .leaflet-draw-draw-circle {\n\tbackground-position: -92px -2px;\n}\n\n.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circle {\n\tbackground-position: -90px -1px;\n}\n\n.leaflet-draw-toolbar .leaflet-draw-draw-marker {\n\tbackground-position: -122px -2px;\n}\n\n.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-marker {\n\tbackground-position: -120px -1px;\n}\n\n.leaflet-draw-toolbar .leaflet-draw-draw-circlemarker {\n\tbackground-position: -273px -2px;\n}\n\n.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circlemarker {\n\tbackground-position: -271px -1px;\n}\n\n/* ================================================================== */\n/* Edit toolbar\n/* ================================================================== */\n\n.leaflet-draw-toolbar .leaflet-draw-edit-edit {\n\tbackground-position: -152px -2px;\n}\n\n.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit {\n\tbackground-position: -150px -1px;\n}\n\n.leaflet-draw-toolbar .leaflet-draw-edit-remove {\n\tbackground-position: -182px -2px;\n}\n\n.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove {\n\tbackground-position: -180px -1px;\n}\n\n.leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled {\n\tbackground-position: -212px -2px;\n}\n\n.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled {\n\tbackground-position: -210px -1px;\n}\n\n.leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled {\n\tbackground-position: -242px -2px;\n}\n\n.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled {\n\tbackground-position: -240px -2px;\n}\n\n/* ================================================================== */\n/* Drawing styles\n/* ================================================================== */\n\n.leaflet-mouse-marker {\n\tbackground-color: #fff;\n\tcursor: crosshair;\n}\n\n.leaflet-draw-tooltip {\n\tbackground: rgb(54, 54, 54);\n\tbackground: rgba(0, 0, 0, 0.5);\n\tborder: 1px solid transparent;\n\t-webkit-border-radius: 4px;\n\tborder-radius: 4px;\n\tcolor: #fff;\n\tfont: 12px/18px \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n\tmargin-left: 20px;\n\tmargin-top: -21px;\n\tpadding: 4px 8px;\n\tposition: absolute;\n\tvisibility: hidden;\n\twhite-space: nowrap;\n\tz-index: 6;\n}\n\n.leaflet-draw-tooltip:before {\n\tborder-right: 6px solid black;\n\tborder-right-color: rgba(0, 0, 0, 0.5);\n\tborder-top: 6px solid transparent;\n\tborder-bottom: 6px solid transparent;\n\tcontent: \"\";\n\tposition: absolute;\n\ttop: 7px;\n\tleft: -7px;\n}\n\n.leaflet-error-draw-tooltip {\n\tbackground-color: #F2DEDE;\n\tborder: 1px solid #E6B6BD;\n\tcolor: #B94A48;\n}\n\n.leaflet-error-draw-tooltip:before {\n\tborder-right-color: #E6B6BD;\n}\n\n.leaflet-draw-tooltip-single {\n\tmargin-top: -12px\n}\n\n.leaflet-draw-tooltip-subtext {\n\tcolor: #f8d5e4;\n}\n\n.leaflet-draw-guide-dash {\n\tfont-size: 1%;\n\topacity: 0.6;\n\tposition: absolute;\n\twidth: 5px;\n\theight: 5px;\n}\n\n/* ================================================================== */\n/* Edit styles\n/* ================================================================== */\n\n.leaflet-edit-marker-selected {\n\tbackground-color: rgba(254, 87, 161, 0.1);\n\tborder: 4px dashed rgba(254, 87, 161, 0.6);\n\t-webkit-border-radius: 4px;\n\tborder-radius: 4px;\n\tbox-sizing: content-box;\n}\n\n.leaflet-edit-move {\n\tcursor: move;\n}\n\n.leaflet-edit-resize {\n\tcursor: pointer;\n}\n\n/* ================================================================== */\n/* Old IE styles\n/* ================================================================== */\n\n.leaflet-oldie .leaflet-draw-toolbar {\n\tborder: 1px solid #999;\n}\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/leaflet.js",
    "content": "// @ts-nocheck\n/* @preserve\n * Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com\n * (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade\n */\n!function(t,e){\"object\"==typeof exports&&\"undefined\"!=typeof module?e(exports):\"function\"==typeof define&&define.amd?define([\"exports\"],e):e((t=\"undefined\"!=typeof globalThis?globalThis:t||self).leaflet={})}(this,function(t){\"use strict\";function l(t){for(var e,i,n=1,o=arguments.length;n<o;n++)for(e in i=arguments[n])t[e]=i[e];return t}var R=Object.create||function(t){return N.prototype=t,new N};function N(){}function a(t,e){var i,n=Array.prototype.slice;return t.bind?t.bind.apply(t,n.call(arguments,1)):(i=n.call(arguments,2),function(){return t.apply(e,i.length?i.concat(n.call(arguments)):arguments)})}var D=0;function h(t){return\"_leaflet_id\"in t||(t._leaflet_id=++D),t._leaflet_id}function j(t,e,i){var n,o,s=function(){n=!1,o&&(r.apply(i,o),o=!1)},r=function(){n?o=arguments:(t.apply(i,arguments),setTimeout(s,e),n=!0)};return r}function H(t,e,i){var n=e[1],e=e[0],o=n-e;return t===n&&i?t:((t-e)%o+o)%o+e}function u(){return!1}function i(t,e){return!1===e?t:(e=Math.pow(10,void 0===e?6:e),Math.round(t*e)/e)}function W(t){return t.trim?t.trim():t.replace(/^\\s+|\\s+$/g,\"\")}function F(t){return W(t).split(/\\s+/)}function c(t,e){for(var i in Object.prototype.hasOwnProperty.call(t,\"options\")||(t.options=t.options?R(t.options):{}),e)t.options[i]=e[i];return t.options}function U(t,e,i){var n,o=[];for(n in t)o.push(encodeURIComponent(i?n.toUpperCase():n)+\"=\"+encodeURIComponent(t[n]));return(e&&-1!==e.indexOf(\"?\")?\"&\":\"?\")+o.join(\"&\")}var V=/\\{ *([\\w_ -]+) *\\}/g;function q(t,i){return t.replace(V,function(t,e){e=i[e];if(void 0===e)throw new Error(\"No value provided for variable \"+t);return e=\"function\"==typeof e?e(i):e})}var d=Array.isArray||function(t){return\"[object Array]\"===Object.prototype.toString.call(t)};function G(t,e){for(var i=0;i<t.length;i++)if(t[i]===e)return i;return-1}var K=\"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=\";function Y(t){return window[\"webkit\"+t]||window[\"moz\"+t]||window[\"ms\"+t]}var X=0;function J(t){var e=+new Date,i=Math.max(0,16-(e-X));return X=e+i,window.setTimeout(t,i)}var $=window.requestAnimationFrame||Y(\"RequestAnimationFrame\")||J,Q=window.cancelAnimationFrame||Y(\"CancelAnimationFrame\")||Y(\"CancelRequestAnimationFrame\")||function(t){window.clearTimeout(t)};function x(t,e,i){if(!i||$!==J)return $.call(window,a(t,e));t.call(e)}function r(t){t&&Q.call(window,t)}var tt={__proto__:null,extend:l,create:R,bind:a,get lastId(){return D},stamp:h,throttle:j,wrapNum:H,falseFn:u,formatNum:i,trim:W,splitWords:F,setOptions:c,getParamString:U,template:q,isArray:d,indexOf:G,emptyImageUrl:K,requestFn:$,cancelFn:Q,requestAnimFrame:x,cancelAnimFrame:r};function et(){}et.extend=function(t){function e(){c(this),this.initialize&&this.initialize.apply(this,arguments),this.callInitHooks()}var i,n=e.__super__=this.prototype,o=R(n);for(i in(o.constructor=e).prototype=o,this)Object.prototype.hasOwnProperty.call(this,i)&&\"prototype\"!==i&&\"__super__\"!==i&&(e[i]=this[i]);if(t.statics&&l(e,t.statics),t.includes){var s=t.includes;if(\"undefined\"!=typeof L&&L&&L.Mixin){s=d(s)?s:[s];for(var r=0;r<s.length;r++)s[r]===L.Mixin.Events&&console.warn(\"Deprecated include of L.Mixin.Events: this property will be removed in future releases, please inherit from L.Evented instead.\",(new Error).stack)}l.apply(null,[o].concat(t.includes))}return l(o,t),delete o.statics,delete o.includes,o.options&&(o.options=n.options?R(n.options):{},l(o.options,t.options)),o._initHooks=[],o.callInitHooks=function(){if(!this._initHooksCalled){n.callInitHooks&&n.callInitHooks.call(this),this._initHooksCalled=!0;for(var t=0,e=o._initHooks.length;t<e;t++)o._initHooks[t].call(this)}},e},et.include=function(t){var e=this.prototype.options;return l(this.prototype,t),t.options&&(this.prototype.options=e,this.mergeOptions(t.options)),this},et.mergeOptions=function(t){return l(this.prototype.options,t),this},et.addInitHook=function(t){var e=Array.prototype.slice.call(arguments,1),i=\"function\"==typeof t?t:function(){this[t].apply(this,e)};return this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(i),this};var e={on:function(t,e,i){if(\"object\"==typeof t)for(var n in t)this._on(n,t[n],e);else for(var o=0,s=(t=F(t)).length;o<s;o++)this._on(t[o],e,i);return this},off:function(t,e,i){if(arguments.length)if(\"object\"==typeof t)for(var n in t)this._off(n,t[n],e);else{t=F(t);for(var o=1===arguments.length,s=0,r=t.length;s<r;s++)o?this._off(t[s]):this._off(t[s],e,i)}else delete this._events;return this},_on:function(t,e,i,n){\"function\"!=typeof e?console.warn(\"wrong listener type: \"+typeof e):!1===this._listens(t,e,i)&&(e={fn:e,ctx:i=i===this?void 0:i},n&&(e.once=!0),this._events=this._events||{},this._events[t]=this._events[t]||[],this._events[t].push(e))},_off:function(t,e,i){var n,o,s;if(this._events&&(n=this._events[t]))if(1===arguments.length){if(this._firingCount)for(o=0,s=n.length;o<s;o++)n[o].fn=u;delete this._events[t]}else\"function\"!=typeof e?console.warn(\"wrong listener type: \"+typeof e):!1!==(e=this._listens(t,e,i))&&(i=n[e],this._firingCount&&(i.fn=u,this._events[t]=n=n.slice()),n.splice(e,1))},fire:function(t,e,i){if(this.listens(t,i)){var n=l({},e,{type:t,target:this,sourceTarget:e&&e.sourceTarget||this});if(this._events){var o=this._events[t];if(o){this._firingCount=this._firingCount+1||1;for(var s=0,r=o.length;s<r;s++){var a=o[s],h=a.fn;a.once&&this.off(t,h,a.ctx),h.call(a.ctx||this,n)}this._firingCount--}}i&&this._propagateEvent(n)}return this},listens:function(t,e,i,n){\"string\"!=typeof t&&console.warn('\"string\" type argument expected');var o=e,s=(\"function\"!=typeof e&&(n=!!e,i=o=void 0),this._events&&this._events[t]);if(s&&s.length&&!1!==this._listens(t,o,i))return!0;if(n)for(var r in this._eventParents)if(this._eventParents[r].listens(t,e,i,n))return!0;return!1},_listens:function(t,e,i){if(this._events){var n=this._events[t]||[];if(!e)return!!n.length;i===this&&(i=void 0);for(var o=0,s=n.length;o<s;o++)if(n[o].fn===e&&n[o].ctx===i)return o}return!1},once:function(t,e,i){if(\"object\"==typeof t)for(var n in t)this._on(n,t[n],e,!0);else for(var o=0,s=(t=F(t)).length;o<s;o++)this._on(t[o],e,i,!0);return this},addEventParent:function(t){return this._eventParents=this._eventParents||{},this._eventParents[h(t)]=t,this},removeEventParent:function(t){return this._eventParents&&delete this._eventParents[h(t)],this},_propagateEvent:function(t){for(var e in this._eventParents)this._eventParents[e].fire(t.type,l({layer:t.target,propagatedFrom:t.target},t),!0)}},it=(e.addEventListener=e.on,e.removeEventListener=e.clearAllEventListeners=e.off,e.addOneTimeEventListener=e.once,e.fireEvent=e.fire,e.hasEventListeners=e.listens,et.extend(e));function p(t,e,i){this.x=i?Math.round(t):t,this.y=i?Math.round(e):e}var nt=Math.trunc||function(t){return 0<t?Math.floor(t):Math.ceil(t)};function m(t,e,i){return t instanceof p?t:d(t)?new p(t[0],t[1]):null==t?t:\"object\"==typeof t&&\"x\"in t&&\"y\"in t?new p(t.x,t.y):new p(t,e,i)}function f(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;n<o;n++)this.extend(i[n])}function _(t,e){return!t||t instanceof f?t:new f(t,e)}function s(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;n<o;n++)this.extend(i[n])}function g(t,e){return t instanceof s?t:new s(t,e)}function v(t,e,i){if(isNaN(t)||isNaN(e))throw new Error(\"Invalid LatLng object: (\"+t+\", \"+e+\")\");this.lat=+t,this.lng=+e,void 0!==i&&(this.alt=+i)}function w(t,e,i){return t instanceof v?t:d(t)&&\"object\"!=typeof t[0]?3===t.length?new v(t[0],t[1],t[2]):2===t.length?new v(t[0],t[1]):null:null==t?t:\"object\"==typeof t&&\"lat\"in t?new v(t.lat,\"lng\"in t?t.lng:t.lon,t.alt):void 0===e?null:new v(t,e,i)}p.prototype={clone:function(){return new p(this.x,this.y)},add:function(t){return this.clone()._add(m(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(m(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},scaleBy:function(t){return new p(this.x*t.x,this.y*t.y)},unscaleBy:function(t){return new p(this.x/t.x,this.y/t.y)},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.clone()._ceil()},_ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},trunc:function(){return this.clone()._trunc()},_trunc:function(){return this.x=nt(this.x),this.y=nt(this.y),this},distanceTo:function(t){var e=(t=m(t)).x-this.x,t=t.y-this.y;return Math.sqrt(e*e+t*t)},equals:function(t){return(t=m(t)).x===this.x&&t.y===this.y},contains:function(t){return t=m(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return\"Point(\"+i(this.x)+\", \"+i(this.y)+\")\"}},f.prototype={extend:function(t){var e,i;if(t){if(t instanceof p||\"number\"==typeof t[0]||\"x\"in t)e=i=m(t);else if(e=(t=_(t)).min,i=t.max,!e||!i)return this;this.min||this.max?(this.min.x=Math.min(e.x,this.min.x),this.max.x=Math.max(i.x,this.max.x),this.min.y=Math.min(e.y,this.min.y),this.max.y=Math.max(i.y,this.max.y)):(this.min=e.clone(),this.max=i.clone())}return this},getCenter:function(t){return m((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return m(this.min.x,this.max.y)},getTopRight:function(){return m(this.max.x,this.min.y)},getTopLeft:function(){return this.min},getBottomRight:function(){return this.max},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var e,i;return(t=(\"number\"==typeof t[0]||t instanceof p?m:_)(t))instanceof f?(e=t.min,i=t.max):e=i=t,e.x>=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=_(t);var e=this.min,i=this.max,n=t.min,t=t.max,o=t.x>=e.x&&n.x<=i.x,t=t.y>=e.y&&n.y<=i.y;return o&&t},overlaps:function(t){t=_(t);var e=this.min,i=this.max,n=t.min,t=t.max,o=t.x>e.x&&n.x<i.x,t=t.y>e.y&&n.y<i.y;return o&&t},isValid:function(){return!(!this.min||!this.max)},pad:function(t){var e=this.min,i=this.max,n=Math.abs(e.x-i.x)*t,t=Math.abs(e.y-i.y)*t;return _(m(e.x-n,e.y-t),m(i.x+n,i.y+t))},equals:function(t){return!!t&&(t=_(t),this.min.equals(t.getTopLeft())&&this.max.equals(t.getBottomRight()))}},s.prototype={extend:function(t){var e,i,n=this._southWest,o=this._northEast;if(t instanceof v)i=e=t;else{if(!(t instanceof s))return t?this.extend(w(t)||g(t)):this;if(e=t._southWest,i=t._northEast,!e||!i)return this}return n||o?(n.lat=Math.min(e.lat,n.lat),n.lng=Math.min(e.lng,n.lng),o.lat=Math.max(i.lat,o.lat),o.lng=Math.max(i.lng,o.lng)):(this._southWest=new v(e.lat,e.lng),this._northEast=new v(i.lat,i.lng)),this},pad:function(t){var e=this._southWest,i=this._northEast,n=Math.abs(e.lat-i.lat)*t,t=Math.abs(e.lng-i.lng)*t;return new s(new v(e.lat-n,e.lng-t),new v(i.lat+n,i.lng+t))},getCenter:function(){return new v((this._southWest.lat+this._northEast.lat)/2,(this._southWest.lng+this._northEast.lng)/2)},getSouthWest:function(){return this._southWest},getNorthEast:function(){return this._northEast},getNorthWest:function(){return new v(this.getNorth(),this.getWest())},getSouthEast:function(){return new v(this.getSouth(),this.getEast())},getWest:function(){return this._southWest.lng},getSouth:function(){return this._southWest.lat},getEast:function(){return this._northEast.lng},getNorth:function(){return this._northEast.lat},contains:function(t){t=(\"number\"==typeof t[0]||t instanceof v||\"lat\"in t?w:g)(t);var e,i,n=this._southWest,o=this._northEast;return t instanceof s?(e=t.getSouthWest(),i=t.getNorthEast()):e=i=t,e.lat>=n.lat&&i.lat<=o.lat&&e.lng>=n.lng&&i.lng<=o.lng},intersects:function(t){t=g(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),t=t.getNorthEast(),o=t.lat>=e.lat&&n.lat<=i.lat,t=t.lng>=e.lng&&n.lng<=i.lng;return o&&t},overlaps:function(t){t=g(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),t=t.getNorthEast(),o=t.lat>e.lat&&n.lat<i.lat,t=t.lng>e.lng&&n.lng<i.lng;return o&&t},toBBoxString:function(){return[this.getWest(),this.getSouth(),this.getEast(),this.getNorth()].join(\",\")},equals:function(t,e){return!!t&&(t=g(t),this._southWest.equals(t.getSouthWest(),e)&&this._northEast.equals(t.getNorthEast(),e))},isValid:function(){return!(!this._southWest||!this._northEast)}};var ot={latLngToPoint:function(t,e){t=this.projection.project(t),e=this.scale(e);return this.transformation._transform(t,e)},pointToLatLng:function(t,e){e=this.scale(e),t=this.transformation.untransform(t,e);return this.projection.unproject(t)},project:function(t){return this.projection.project(t)},unproject:function(t){return this.projection.unproject(t)},scale:function(t){return 256*Math.pow(2,t)},zoom:function(t){return Math.log(t/256)/Math.LN2},getProjectedBounds:function(t){var e;return this.infinite?null:(e=this.projection.bounds,t=this.scale(t),new f(this.transformation.transform(e.min,t),this.transformation.transform(e.max,t)))},infinite:!(v.prototype={equals:function(t,e){return!!t&&(t=w(t),Math.max(Math.abs(this.lat-t.lat),Math.abs(this.lng-t.lng))<=(void 0===e?1e-9:e))},toString:function(t){return\"LatLng(\"+i(this.lat,t)+\", \"+i(this.lng,t)+\")\"},distanceTo:function(t){return st.distance(this,w(t))},wrap:function(){return st.wrapLatLng(this)},toBounds:function(t){var t=180*t/40075017,e=t/Math.cos(Math.PI/180*this.lat);return g([this.lat-t,this.lng-e],[this.lat+t,this.lng+e])},clone:function(){return new v(this.lat,this.lng,this.alt)}}),wrapLatLng:function(t){var e=this.wrapLng?H(t.lng,this.wrapLng,!0):t.lng;return new v(this.wrapLat?H(t.lat,this.wrapLat,!0):t.lat,e,t.alt)},wrapLatLngBounds:function(t){var e=t.getCenter(),i=this.wrapLatLng(e),n=e.lat-i.lat,e=e.lng-i.lng;return 0==n&&0==e?t:(i=t.getSouthWest(),t=t.getNorthEast(),new s(new v(i.lat-n,i.lng-e),new v(t.lat-n,t.lng-e)))}},st=l({},ot,{wrapLng:[-180,180],R:6371e3,distance:function(t,e){var i=Math.PI/180,n=t.lat*i,o=e.lat*i,s=Math.sin((e.lat-t.lat)*i/2),e=Math.sin((e.lng-t.lng)*i/2),t=s*s+Math.cos(n)*Math.cos(o)*e*e,i=2*Math.atan2(Math.sqrt(t),Math.sqrt(1-t));return this.R*i}}),rt=6378137,rt={R:rt,MAX_LATITUDE:85.0511287798,project:function(t){var e=Math.PI/180,i=this.MAX_LATITUDE,i=Math.max(Math.min(i,t.lat),-i),i=Math.sin(i*e);return new p(this.R*t.lng*e,this.R*Math.log((1+i)/(1-i))/2)},unproject:function(t){var e=180/Math.PI;return new v((2*Math.atan(Math.exp(t.y/this.R))-Math.PI/2)*e,t.x*e/this.R)},bounds:new f([-(rt=rt*Math.PI),-rt],[rt,rt])};function at(t,e,i,n){d(t)?(this._a=t[0],this._b=t[1],this._c=t[2],this._d=t[3]):(this._a=t,this._b=e,this._c=i,this._d=n)}function ht(t,e,i,n){return new at(t,e,i,n)}at.prototype={transform:function(t,e){return this._transform(t.clone(),e)},_transform:function(t,e){return t.x=(e=e||1)*(this._a*t.x+this._b),t.y=e*(this._c*t.y+this._d),t},untransform:function(t,e){return new p((t.x/(e=e||1)-this._b)/this._a,(t.y/e-this._d)/this._c)}};var lt=l({},st,{code:\"EPSG:3857\",projection:rt,transformation:ht(lt=.5/(Math.PI*rt.R),.5,-lt,.5)}),ut=l({},lt,{code:\"EPSG:900913\"});function ct(t){return document.createElementNS(\"http://www.w3.org/2000/svg\",t)}function dt(t,e){for(var i,n,o,s,r=\"\",a=0,h=t.length;a<h;a++){for(i=0,n=(o=t[a]).length;i<n;i++)r+=(i?\"L\":\"M\")+(s=o[i]).x+\" \"+s.y;r+=e?b.svg?\"z\":\"x\":\"\"}return r||\"M0 0\"}var _t=document.documentElement.style,pt=\"ActiveXObject\"in window,mt=pt&&!document.addEventListener,n=\"msLaunchUri\"in navigator&&!(\"documentMode\"in document),ft=y(\"webkit\"),gt=y(\"android\"),vt=y(\"android 2\")||y(\"android 3\"),yt=parseInt(/WebKit\\/([0-9]+)|$/.exec(navigator.userAgent)[1],10),yt=gt&&y(\"Google\")&&yt<537&&!(\"AudioNode\"in window),xt=!!window.opera,wt=!n&&y(\"chrome\"),bt=y(\"gecko\")&&!ft&&!xt&&!pt,Pt=!wt&&y(\"safari\"),Lt=y(\"phantom\"),o=\"OTransition\"in _t,Tt=0===navigator.platform.indexOf(\"Win\"),Mt=pt&&\"transition\"in _t,zt=\"WebKitCSSMatrix\"in window&&\"m11\"in new window.WebKitCSSMatrix&&!vt,_t=\"MozPerspective\"in _t,Ct=!window.L_DISABLE_3D&&(Mt||zt||_t)&&!o&&!Lt,Zt=\"undefined\"!=typeof orientation||y(\"mobile\"),St=Zt&&ft,Et=Zt&&zt,kt=!window.PointerEvent&&window.MSPointerEvent,Ot=!(!window.PointerEvent&&!kt),At=\"ontouchstart\"in window||!!window.TouchEvent,Bt=!window.L_NO_TOUCH&&(At||Ot),It=Zt&&xt,Rt=Zt&&bt,Nt=1<(window.devicePixelRatio||window.screen.deviceXDPI/window.screen.logicalXDPI),Dt=function(){var t=!1;try{var e=Object.defineProperty({},\"passive\",{get:function(){t=!0}});window.addEventListener(\"testPassiveEventSupport\",u,e),window.removeEventListener(\"testPassiveEventSupport\",u,e)}catch(t){}return t}(),jt=!!document.createElement(\"canvas\").getContext,Ht=!(!document.createElementNS||!ct(\"svg\").createSVGRect),Wt=!!Ht&&((Wt=document.createElement(\"div\")).innerHTML=\"<svg/>\",\"http://www.w3.org/2000/svg\"===(Wt.firstChild&&Wt.firstChild.namespaceURI));function y(t){return 0<=navigator.userAgent.toLowerCase().indexOf(t)}var b={ie:pt,ielt9:mt,edge:n,webkit:ft,android:gt,android23:vt,androidStock:yt,opera:xt,chrome:wt,gecko:bt,safari:Pt,phantom:Lt,opera12:o,win:Tt,ie3d:Mt,webkit3d:zt,gecko3d:_t,any3d:Ct,mobile:Zt,mobileWebkit:St,mobileWebkit3d:Et,msPointer:kt,pointer:Ot,touch:Bt,touchNative:At,mobileOpera:It,mobileGecko:Rt,retina:Nt,passiveEvents:Dt,canvas:jt,svg:Ht,vml:!Ht&&function(){try{var t=document.createElement(\"div\"),e=(t.innerHTML='<v:shape adj=\"1\"/>',t.firstChild);return e.style.behavior=\"url(#default#VML)\",e&&\"object\"==typeof e.adj}catch(t){return!1}}(),inlineSvg:Wt,mac:0===navigator.platform.indexOf(\"Mac\"),linux:0===navigator.platform.indexOf(\"Linux\")},Ft=b.msPointer?\"MSPointerDown\":\"pointerdown\",Ut=b.msPointer?\"MSPointerMove\":\"pointermove\",Vt=b.msPointer?\"MSPointerUp\":\"pointerup\",qt=b.msPointer?\"MSPointerCancel\":\"pointercancel\",Gt={touchstart:Ft,touchmove:Ut,touchend:Vt,touchcancel:qt},Kt={touchstart:function(t,e){e.MSPOINTER_TYPE_TOUCH&&e.pointerType===e.MSPOINTER_TYPE_TOUCH&&O(e);ee(t,e)},touchmove:ee,touchend:ee,touchcancel:ee},Yt={},Xt=!1;function Jt(t,e,i){return\"touchstart\"!==e||Xt||(document.addEventListener(Ft,$t,!0),document.addEventListener(Ut,Qt,!0),document.addEventListener(Vt,te,!0),document.addEventListener(qt,te,!0),Xt=!0),Kt[e]?(i=Kt[e].bind(this,i),t.addEventListener(Gt[e],i,!1),i):(console.warn(\"wrong event specified:\",e),u)}function $t(t){Yt[t.pointerId]=t}function Qt(t){Yt[t.pointerId]&&(Yt[t.pointerId]=t)}function te(t){delete Yt[t.pointerId]}function ee(t,e){if(e.pointerType!==(e.MSPOINTER_TYPE_MOUSE||\"mouse\")){for(var i in e.touches=[],Yt)e.touches.push(Yt[i]);e.changedTouches=[e],t(e)}}var ie=200;function ne(t,i){t.addEventListener(\"dblclick\",i);var n,o=0;function e(t){var e;1!==t.detail?n=t.detail:\"mouse\"===t.pointerType||t.sourceCapabilities&&!t.sourceCapabilities.firesTouchEvents||((e=Ne(t)).some(function(t){return t instanceof HTMLLabelElement&&t.attributes.for})&&!e.some(function(t){return t instanceof HTMLInputElement||t instanceof HTMLSelectElement})||((e=Date.now())-o<=ie?2===++n&&i(function(t){var e,i,n={};for(i in t)e=t[i],n[i]=e&&e.bind?e.bind(t):e;return(t=n).type=\"dblclick\",n.detail=2,n.isTrusted=!1,n._simulated=!0,n}(t)):n=1,o=e))}return t.addEventListener(\"click\",e),{dblclick:i,simDblclick:e}}var oe,se,re,ae,he,le,ue=we([\"transform\",\"webkitTransform\",\"OTransform\",\"MozTransform\",\"msTransform\"]),ce=we([\"webkitTransition\",\"transition\",\"OTransition\",\"MozTransition\",\"msTransition\"]),de=\"webkitTransition\"===ce||\"OTransition\"===ce?ce+\"End\":\"transitionend\";function _e(t){return\"string\"==typeof t?document.getElementById(t):t}function pe(t,e){var i=t.style[e]||t.currentStyle&&t.currentStyle[e];return\"auto\"===(i=i&&\"auto\"!==i||!document.defaultView?i:(t=document.defaultView.getComputedStyle(t,null))?t[e]:null)?null:i}function P(t,e,i){t=document.createElement(t);return t.className=e||\"\",i&&i.appendChild(t),t}function T(t){var e=t.parentNode;e&&e.removeChild(t)}function me(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function fe(t){var e=t.parentNode;e&&e.lastChild!==t&&e.appendChild(t)}function ge(t){var e=t.parentNode;e&&e.firstChild!==t&&e.insertBefore(t,e.firstChild)}function ve(t,e){return void 0!==t.classList?t.classList.contains(e):0<(t=xe(t)).length&&new RegExp(\"(^|\\\\s)\"+e+\"(\\\\s|$)\").test(t)}function M(t,e){var i;if(void 0!==t.classList)for(var n=F(e),o=0,s=n.length;o<s;o++)t.classList.add(n[o]);else ve(t,e)||ye(t,((i=xe(t))?i+\" \":\"\")+e)}function z(t,e){void 0!==t.classList?t.classList.remove(e):ye(t,W((\" \"+xe(t)+\" \").replace(\" \"+e+\" \",\" \")))}function ye(t,e){void 0===t.className.baseVal?t.className=e:t.className.baseVal=e}function xe(t){return void 0===(t=t.correspondingElement?t.correspondingElement:t).className.baseVal?t.className:t.className.baseVal}function C(t,e){if(\"opacity\"in t.style)t.style.opacity=e;else if(\"filter\"in t.style){var i=!1,n=\"DXImageTransform.Microsoft.Alpha\";try{i=t.filters.item(n)}catch(t){if(1===e)return}e=Math.round(100*e),i?(i.Enabled=100!==e,i.Opacity=e):t.style.filter+=\" progid:\"+n+\"(opacity=\"+e+\")\"}}function we(t){for(var e=document.documentElement.style,i=0;i<t.length;i++)if(t[i]in e)return t[i];return!1}function be(t,e,i){e=e||new p(0,0);t.style[ue]=(b.ie3d?\"translate(\"+e.x+\"px,\"+e.y+\"px)\":\"translate3d(\"+e.x+\"px,\"+e.y+\"px,0)\")+(i?\" scale(\"+i+\")\":\"\")}function Z(t,e){t._leaflet_pos=e,b.any3d?be(t,e):(t.style.left=e.x+\"px\",t.style.top=e.y+\"px\")}function Pe(t){return t._leaflet_pos||new p(0,0)}function Le(){S(window,\"dragstart\",O)}function Te(){k(window,\"dragstart\",O)}function Me(t){for(;-1===t.tabIndex;)t=t.parentNode;t.style&&(ze(),le=(he=t).style.outlineStyle,t.style.outlineStyle=\"none\",S(window,\"keydown\",ze))}function ze(){he&&(he.style.outlineStyle=le,le=he=void 0,k(window,\"keydown\",ze))}function Ce(t){for(;!((t=t.parentNode).offsetWidth&&t.offsetHeight||t===document.body););return t}function Ze(t){var e=t.getBoundingClientRect();return{x:e.width/t.offsetWidth||1,y:e.height/t.offsetHeight||1,boundingClientRect:e}}ae=\"onselectstart\"in document?(re=function(){S(window,\"selectstart\",O)},function(){k(window,\"selectstart\",O)}):(se=we([\"userSelect\",\"WebkitUserSelect\",\"OUserSelect\",\"MozUserSelect\",\"msUserSelect\"]),re=function(){var t;se&&(t=document.documentElement.style,oe=t[se],t[se]=\"none\")},function(){se&&(document.documentElement.style[se]=oe,oe=void 0)});pt={__proto__:null,TRANSFORM:ue,TRANSITION:ce,TRANSITION_END:de,get:_e,getStyle:pe,create:P,remove:T,empty:me,toFront:fe,toBack:ge,hasClass:ve,addClass:M,removeClass:z,setClass:ye,getClass:xe,setOpacity:C,testProp:we,setTransform:be,setPosition:Z,getPosition:Pe,get disableTextSelection(){return re},get enableTextSelection(){return ae},disableImageDrag:Le,enableImageDrag:Te,preventOutline:Me,restoreOutline:ze,getSizedParentNode:Ce,getScale:Ze};function S(t,e,i,n){if(e&&\"object\"==typeof e)for(var o in e)ke(t,o,e[o],i);else for(var s=0,r=(e=F(e)).length;s<r;s++)ke(t,e[s],i,n);return this}var E=\"_leaflet_events\";function k(t,e,i,n){if(1===arguments.length)Se(t),delete t[E];else if(e&&\"object\"==typeof e)for(var o in e)Oe(t,o,e[o],i);else if(e=F(e),2===arguments.length)Se(t,function(t){return-1!==G(e,t)});else for(var s=0,r=e.length;s<r;s++)Oe(t,e[s],i,n);return this}function Se(t,e){for(var i in t[E]){var n=i.split(/\\d/)[0];e&&!e(n)||Oe(t,n,null,null,i)}}var Ee={mouseenter:\"mouseover\",mouseleave:\"mouseout\",wheel:!(\"onwheel\"in window)&&\"mousewheel\"};function ke(e,t,i,n){var o,s,r=t+h(i)+(n?\"_\"+h(n):\"\");e[E]&&e[E][r]||(s=o=function(t){return i.call(n||e,t||window.event)},!b.touchNative&&b.pointer&&0===t.indexOf(\"touch\")?o=Jt(e,t,o):b.touch&&\"dblclick\"===t?o=ne(e,o):\"addEventListener\"in e?\"touchstart\"===t||\"touchmove\"===t||\"wheel\"===t||\"mousewheel\"===t?e.addEventListener(Ee[t]||t,o,!!b.passiveEvents&&{passive:!1}):\"mouseenter\"===t||\"mouseleave\"===t?e.addEventListener(Ee[t],o=function(t){t=t||window.event,We(e,t)&&s(t)},!1):e.addEventListener(t,s,!1):e.attachEvent(\"on\"+t,o),e[E]=e[E]||{},e[E][r]=o)}function Oe(t,e,i,n,o){o=o||e+h(i)+(n?\"_\"+h(n):\"\");var s,r,i=t[E]&&t[E][o];i&&(!b.touchNative&&b.pointer&&0===e.indexOf(\"touch\")?(n=t,r=i,Gt[s=e]?n.removeEventListener(Gt[s],r,!1):console.warn(\"wrong event specified:\",s)):b.touch&&\"dblclick\"===e?(n=i,(r=t).removeEventListener(\"dblclick\",n.dblclick),r.removeEventListener(\"click\",n.simDblclick)):\"removeEventListener\"in t?t.removeEventListener(Ee[e]||e,i,!1):t.detachEvent(\"on\"+e,i),t[E][o]=null)}function Ae(t){return t.stopPropagation?t.stopPropagation():t.originalEvent?t.originalEvent._stopped=!0:t.cancelBubble=!0,this}function Be(t){return ke(t,\"wheel\",Ae),this}function Ie(t){return S(t,\"mousedown touchstart dblclick contextmenu\",Ae),t._leaflet_disable_click=!0,this}function O(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this}function Re(t){return O(t),Ae(t),this}function Ne(t){if(t.composedPath)return t.composedPath();for(var e=[],i=t.target;i;)e.push(i),i=i.parentNode;return e}function De(t,e){var i,n;return e?(n=(i=Ze(e)).boundingClientRect,new p((t.clientX-n.left)/i.x-e.clientLeft,(t.clientY-n.top)/i.y-e.clientTop)):new p(t.clientX,t.clientY)}var je=b.linux&&b.chrome?window.devicePixelRatio:b.mac?3*window.devicePixelRatio:0<window.devicePixelRatio?2*window.devicePixelRatio:1;function He(t){return b.edge?t.wheelDeltaY/2:t.deltaY&&0===t.deltaMode?-t.deltaY/je:t.deltaY&&1===t.deltaMode?20*-t.deltaY:t.deltaY&&2===t.deltaMode?60*-t.deltaY:t.deltaX||t.deltaZ?0:t.wheelDelta?(t.wheelDeltaY||t.wheelDelta)/2:t.detail&&Math.abs(t.detail)<32765?20*-t.detail:t.detail?t.detail/-32765*60:0}function We(t,e){var i=e.relatedTarget;if(!i)return!0;try{for(;i&&i!==t;)i=i.parentNode}catch(t){return!1}return i!==t}var mt={__proto__:null,on:S,off:k,stopPropagation:Ae,disableScrollPropagation:Be,disableClickPropagation:Ie,preventDefault:O,stop:Re,getPropagationPath:Ne,getMousePosition:De,getWheelDelta:He,isExternalTarget:We,addListener:S,removeListener:k},Fe=it.extend({run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=i||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=Pe(t),this._offset=e.subtract(this._startPos),this._startTime=+new Date,this.fire(\"start\"),this._animate()},stop:function(){this._inProgress&&(this._step(!0),this._complete())},_animate:function(){this._animId=x(this._animate,this),this._step()},_step:function(t){var e=+new Date-this._startTime,i=1e3*this._duration;e<i?this._runFrame(this._easeOut(e/i),t):(this._runFrame(1),this._complete())},_runFrame:function(t,e){t=this._startPos.add(this._offset.multiplyBy(t));e&&t._round(),Z(this._el,t),this.fire(\"step\")},_complete:function(){r(this._animId),this._inProgress=!1,this.fire(\"end\")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),A=it.extend({options:{crs:lt,center:void 0,zoom:void 0,minZoom:void 0,maxZoom:void 0,layers:[],maxBounds:void 0,renderer:void 0,zoomAnimation:!0,zoomAnimationThreshold:4,fadeAnimation:!0,markerZoomAnimation:!0,transform3DLimit:8388608,zoomSnap:1,zoomDelta:1,trackResize:!0},initialize:function(t,e){e=c(this,e),this._handlers=[],this._layers={},this._zoomBoundLayers={},this._sizeChanged=!0,this._initContainer(t),this._initLayout(),this._onResize=a(this._onResize,this),this._initEvents(),e.maxBounds&&this.setMaxBounds(e.maxBounds),void 0!==e.zoom&&(this._zoom=this._limitZoom(e.zoom)),e.center&&void 0!==e.zoom&&this.setView(w(e.center),e.zoom,{reset:!0}),this.callInitHooks(),this._zoomAnimated=ce&&b.any3d&&!b.mobileOpera&&this.options.zoomAnimation,this._zoomAnimated&&(this._createAnimProxy(),S(this._proxy,de,this._catchTransitionEnd,this)),this._addLayers(this.options.layers)},setView:function(t,e,i){if((e=void 0===e?this._zoom:this._limitZoom(e),t=this._limitCenter(w(t),e,this.options.maxBounds),i=i||{},this._stop(),this._loaded&&!i.reset&&!0!==i)&&(void 0!==i.animate&&(i.zoom=l({animate:i.animate},i.zoom),i.pan=l({animate:i.animate,duration:i.duration},i.pan)),this._zoom!==e?this._tryAnimatedZoom&&this._tryAnimatedZoom(t,e,i.zoom):this._tryAnimatedPan(t,i.pan)))return clearTimeout(this._sizeTimer),this;return this._resetView(t,e,i.pan&&i.pan.noMoveStart),this},setZoom:function(t,e){return this._loaded?this.setView(this.getCenter(),t,{zoom:e}):(this._zoom=t,this)},zoomIn:function(t,e){return t=t||(b.any3d?this.options.zoomDelta:1),this.setZoom(this._zoom+t,e)},zoomOut:function(t,e){return t=t||(b.any3d?this.options.zoomDelta:1),this.setZoom(this._zoom-t,e)},setZoomAround:function(t,e,i){var n=this.getZoomScale(e),o=this.getSize().divideBy(2),t=(t instanceof p?t:this.latLngToContainerPoint(t)).subtract(o).multiplyBy(1-1/n),n=this.containerPointToLatLng(o.add(t));return this.setView(n,e,{zoom:i})},_getBoundsCenterZoom:function(t,e){e=e||{},t=t.getBounds?t.getBounds():g(t);var i=m(e.paddingTopLeft||e.padding||[0,0]),n=m(e.paddingBottomRight||e.padding||[0,0]),o=this.getBoundsZoom(t,!1,i.add(n));return(o=\"number\"==typeof e.maxZoom?Math.min(e.maxZoom,o):o)===1/0?{center:t.getCenter(),zoom:o}:(e=n.subtract(i).divideBy(2),n=this.project(t.getSouthWest(),o),i=this.project(t.getNorthEast(),o),{center:this.unproject(n.add(i).divideBy(2).add(e),o),zoom:o})},fitBounds:function(t,e){if((t=g(t)).isValid())return t=this._getBoundsCenterZoom(t,e),this.setView(t.center,t.zoom,e);throw new Error(\"Bounds are not valid.\")},fitWorld:function(t){return this.fitBounds([[-90,-180],[90,180]],t)},panTo:function(t,e){return this.setView(t,this._zoom,{pan:e})},panBy:function(t,e){var i;return e=e||{},(t=m(t).round()).x||t.y?(!0===e.animate||this.getSize().contains(t)?(this._panAnim||(this._panAnim=new Fe,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),e.noMoveStart||this.fire(\"movestart\"),!1!==e.animate?(M(this._mapPane,\"leaflet-pan-anim\"),i=this._getMapPanePos().subtract(t).round(),this._panAnim.run(this._mapPane,i,e.duration||.25,e.easeLinearity)):(this._rawPanBy(t),this.fire(\"move\").fire(\"moveend\"))):this._resetView(this.unproject(this.project(this.getCenter()).add(t)),this.getZoom()),this):this.fire(\"moveend\")},flyTo:function(n,o,t){if(!1===(t=t||{}).animate||!b.any3d)return this.setView(n,o,t);this._stop();var s=this.project(this.getCenter()),r=this.project(n),e=this.getSize(),a=this._zoom,h=(n=w(n),o=void 0===o?a:o,Math.max(e.x,e.y)),i=h*this.getZoomScale(a,o),l=r.distanceTo(s)||1,u=1.42,c=u*u;function d(t){t=(i*i-h*h+(t?-1:1)*c*c*l*l)/(2*(t?i:h)*c*l),t=Math.sqrt(t*t+1)-t;return t<1e-9?-18:Math.log(t)}function _(t){return(Math.exp(t)-Math.exp(-t))/2}function p(t){return(Math.exp(t)+Math.exp(-t))/2}var m=d(0);function f(t){return h*(p(m)*(_(t=m+u*t)/p(t))-_(m))/c}var g=Date.now(),v=(d(1)-m)/u,y=t.duration?1e3*t.duration:1e3*v*.8;return this._moveStart(!0,t.noMoveStart),function t(){var e=(Date.now()-g)/y,i=(1-Math.pow(1-e,1.5))*v;e<=1?(this._flyToFrame=x(t,this),this._move(this.unproject(s.add(r.subtract(s).multiplyBy(f(i)/l)),a),this.getScaleZoom(h/(e=i,h*(p(m)/p(m+u*e))),a),{flyTo:!0})):this._move(n,o)._moveEnd(!0)}.call(this),this},flyToBounds:function(t,e){t=this._getBoundsCenterZoom(t,e);return this.flyTo(t.center,t.zoom,e)},setMaxBounds:function(t){return t=g(t),this.listens(\"moveend\",this._panInsideMaxBounds)&&this.off(\"moveend\",this._panInsideMaxBounds),t.isValid()?(this.options.maxBounds=t,this._loaded&&this._panInsideMaxBounds(),this.on(\"moveend\",this._panInsideMaxBounds)):(this.options.maxBounds=null,this)},setMinZoom:function(t){var e=this.options.minZoom;return this.options.minZoom=t,this._loaded&&e!==t&&(this.fire(\"zoomlevelschange\"),this.getZoom()<this.options.minZoom)?this.setZoom(t):this},setMaxZoom:function(t){var e=this.options.maxZoom;return this.options.maxZoom=t,this._loaded&&e!==t&&(this.fire(\"zoomlevelschange\"),this.getZoom()>this.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,e){this._enforcingBounds=!0;var i=this.getCenter(),t=this._limitCenter(i,this._zoom,g(t));return i.equals(t)||this.panTo(t,e),this._enforcingBounds=!1,this},panInside:function(t,e){var i=m((e=e||{}).paddingTopLeft||e.padding||[0,0]),n=m(e.paddingBottomRight||e.padding||[0,0]),o=this.project(this.getCenter()),t=this.project(t),s=this.getPixelBounds(),i=_([s.min.add(i),s.max.subtract(n)]),s=i.getSize();return i.contains(t)||(this._enforcingBounds=!0,n=t.subtract(i.getCenter()),i=i.extend(t).getSize().subtract(s),o.x+=n.x<0?-i.x:i.x,o.y+=n.y<0?-i.y:i.y,this.panTo(this.unproject(o),e),this._enforcingBounds=!1),this},invalidateSize:function(t){if(!this._loaded)return this;t=l({animate:!1,pan:!0},!0===t?{animate:!0}:t);var e=this.getSize(),i=(this._sizeChanged=!0,this._lastCenter=null,this.getSize()),n=e.divideBy(2).round(),o=i.divideBy(2).round(),n=n.subtract(o);return n.x||n.y?(t.animate&&t.pan?this.panBy(n):(t.pan&&this._rawPanBy(n),this.fire(\"move\"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(a(this.fire,this,\"moveend\"),200)):this.fire(\"moveend\")),this.fire(\"resize\",{oldSize:e,newSize:i})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire(\"viewreset\"),this._stop()},locate:function(t){var e,i;return t=this._locateOptions=l({timeout:1e4,watch:!1},t),\"geolocation\"in navigator?(e=a(this._handleGeolocationResponse,this),i=a(this._handleGeolocationError,this),t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t)):this._handleGeolocationError({code:0,message:\"Geolocation not supported.\"}),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var e;this._container._leaflet_id&&(e=t.code,t=t.message||(1===e?\"permission denied\":2===e?\"position unavailable\":\"timeout\"),this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire(\"locationerror\",{code:e,message:\"Geolocation error: \"+t+\".\"}))},_handleGeolocationResponse:function(t){if(this._container._leaflet_id){var e,i,n=new v(t.coords.latitude,t.coords.longitude),o=n.toBounds(2*t.coords.accuracy),s=this._locateOptions,r=(s.setView&&(e=this.getBoundsZoom(o),this.setView(n,s.maxZoom?Math.min(e,s.maxZoom):e)),{latlng:n,bounds:o,timestamp:t.timestamp});for(i in t.coords)\"number\"==typeof t.coords[i]&&(r[i]=t.coords[i]);this.fire(\"locationfound\",r)}},addHandler:function(t,e){return e&&(e=this[t]=new e(this),this._handlers.push(e),this.options[t]&&e.enable()),this},remove:function(){if(this._initEvents(!0),this.options.maxBounds&&this.off(\"moveend\",this._panInsideMaxBounds),this._containerId!==this._container._leaflet_id)throw new Error(\"Map container is being reused by another instance\");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}for(var t in void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),T(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._resizeRequest&&(r(this._resizeRequest),this._resizeRequest=null),this._clearHandlers(),this._loaded&&this.fire(\"unload\"),this._layers)this._layers[t].remove();for(t in this._panes)T(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,e){e=P(\"div\",\"leaflet-pane\"+(t?\" leaflet-\"+t.replace(\"Pane\",\"\")+\"-pane\":\"\"),e||this._mapPane);return t&&(this._panes[t]=e),e},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter.clone():this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new s(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,e,i){t=g(t),i=m(i||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),t=t.getSouthEast(),i=this.getSize().subtract(i),t=_(this.project(t,n),this.project(r,n)).getSize(),r=b.any3d?this.options.zoomSnap:1,a=i.x/t.x,i=i.y/t.y,t=e?Math.max(a,i):Math.min(a,i),n=this.getScaleZoom(t,n);return r&&(n=Math.round(n/(r/100))*(r/100),n=e?Math.ceil(n/r)*r:Math.floor(n/r)*r),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new p(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,e){t=this._getTopLeftPoint(t,e);return new f(t,t.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return\"string\"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,e){var i=this.options.crs;return e=void 0===e?this._zoom:e,i.scale(t)/i.scale(e)},getScaleZoom:function(t,e){var i=this.options.crs,t=(e=void 0===e?this._zoom:e,i.zoom(t*i.scale(e)));return isNaN(t)?1/0:t},project:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.latLngToPoint(w(t),e)},unproject:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.pointToLatLng(m(t),e)},layerPointToLatLng:function(t){t=m(t).add(this.getPixelOrigin());return this.unproject(t)},latLngToLayerPoint:function(t){return this.project(w(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(w(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(g(t))},distance:function(t,e){return this.options.crs.distance(w(t),w(e))},containerPointToLayerPoint:function(t){return m(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return m(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){t=this.containerPointToLayerPoint(m(t));return this.layerPointToLatLng(t)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(w(t)))},mouseEventToContainerPoint:function(t){return De(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){t=this._container=_e(t);if(!t)throw new Error(\"Map container not found.\");if(t._leaflet_id)throw new Error(\"Map container is already initialized.\");S(t,\"scroll\",this._onScroll,this),this._containerId=h(t)},_initLayout:function(){var t=this._container,e=(this._fadeAnimated=this.options.fadeAnimation&&b.any3d,M(t,\"leaflet-container\"+(b.touch?\" leaflet-touch\":\"\")+(b.retina?\" leaflet-retina\":\"\")+(b.ielt9?\" leaflet-oldie\":\"\")+(b.safari?\" leaflet-safari\":\"\")+(this._fadeAnimated?\" leaflet-fade-anim\":\"\")),pe(t,\"position\"));\"absolute\"!==e&&\"relative\"!==e&&\"fixed\"!==e&&\"sticky\"!==e&&(t.style.position=\"relative\"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane(\"mapPane\",this._container),Z(this._mapPane,new p(0,0)),this.createPane(\"tilePane\"),this.createPane(\"overlayPane\"),this.createPane(\"shadowPane\"),this.createPane(\"markerPane\"),this.createPane(\"tooltipPane\"),this.createPane(\"popupPane\"),this.options.markerZoomAnimation||(M(t.markerPane,\"leaflet-zoom-hide\"),M(t.shadowPane,\"leaflet-zoom-hide\"))},_resetView:function(t,e,i){Z(this._mapPane,new p(0,0));var n=!this._loaded,o=(this._loaded=!0,e=this._limitZoom(e),this.fire(\"viewprereset\"),this._zoom!==e);this._moveStart(o,i)._move(t,e)._moveEnd(o),this.fire(\"viewreset\"),n&&this.fire(\"load\")},_moveStart:function(t,e){return t&&this.fire(\"zoomstart\"),e||this.fire(\"movestart\"),this},_move:function(t,e,i,n){void 0===e&&(e=this._zoom);var o=this._zoom!==e;return this._zoom=e,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),n?i&&i.pinch&&this.fire(\"zoom\",i):((o||i&&i.pinch)&&this.fire(\"zoom\",i),this.fire(\"move\",i)),this},_moveEnd:function(t){return t&&this.fire(\"zoomend\"),this.fire(\"moveend\")},_stop:function(){return r(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){Z(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error(\"Set map center and zoom first.\")},_initEvents:function(t){this._targets={};var e=t?k:S;e((this._targets[h(this._container)]=this)._container,\"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress keydown keyup\",this._handleDOMEvent,this),this.options.trackResize&&e(window,\"resize\",this._onResize,this),b.any3d&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,\"moveend\",this._onMoveEnd)},_onResize:function(){r(this._resizeRequest),this._resizeRequest=x(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,e){for(var i,n=[],o=\"mouseout\"===e||\"mouseover\"===e,s=t.target||t.srcElement,r=!1;s;){if((i=this._targets[h(s)])&&(\"click\"===e||\"preclick\"===e)&&this._draggableMoved(i)){r=!0;break}if(i&&i.listens(e,!0)){if(o&&!We(s,t))break;if(n.push(i),o)break}if(s===this._container)break;s=s.parentNode}return n=n.length||r||o||!this.listens(e,!0)?n:[this]},_isClickDisabled:function(t){for(;t&&t!==this._container;){if(t._leaflet_disable_click)return!0;t=t.parentNode}},_handleDOMEvent:function(t){var e,i=t.target||t.srcElement;!this._loaded||i._leaflet_disable_events||\"click\"===t.type&&this._isClickDisabled(i)||(\"mousedown\"===(e=t.type)&&Me(i),this._fireDOMEvent(t,e))},_mouseEvents:[\"click\",\"dblclick\",\"mouseover\",\"mouseout\",\"contextmenu\"],_fireDOMEvent:function(t,e,i){\"click\"===t.type&&((a=l({},t)).type=\"preclick\",this._fireDOMEvent(a,a.type,i));var n=this._findEventTargets(t,e);if(i){for(var o=[],s=0;s<i.length;s++)i[s].listens(e,!0)&&o.push(i[s]);n=o.concat(n)}if(n.length){\"contextmenu\"===e&&O(t);var r,a=n[0],h={originalEvent:t};for(\"keypress\"!==t.type&&\"keydown\"!==t.type&&\"keyup\"!==t.type&&(r=a.getLatLng&&(!a._radius||a._radius<=10),h.containerPoint=r?this.latLngToContainerPoint(a.getLatLng()):this.mouseEventToContainerPoint(t),h.layerPoint=this.containerPointToLayerPoint(h.containerPoint),h.latlng=r?a.getLatLng():this.layerPointToLatLng(h.layerPoint)),s=0;s<n.length;s++)if(n[s].fire(e,h,!0),h.originalEvent._stopped||!1===n[s].options.bubblingMouseEvents&&-1!==G(this._mouseEvents,e))return}},_draggableMoved:function(t){return(t=t.dragging&&t.dragging.enabled()?t:this).dragging&&t.dragging.moved()||this.boxZoom&&this.boxZoom.moved()},_clearHandlers:function(){for(var t=0,e=this._handlers.length;t<e;t++)this._handlers[t].disable()},whenReady:function(t,e){return this._loaded?t.call(e||this,{target:this}):this.on(\"load\",t,e),this},_getMapPanePos:function(){return Pe(this._mapPane)||new p(0,0)},_moved:function(){var t=this._getMapPanePos();return t&&!t.equals([0,0])},_getTopLeftPoint:function(t,e){return(t&&void 0!==e?this._getNewPixelOrigin(t,e):this.getPixelOrigin()).subtract(this._getMapPanePos())},_getNewPixelOrigin:function(t,e){var i=this.getSize()._divideBy(2);return this.project(t,e)._subtract(i)._add(this._getMapPanePos())._round()},_latLngToNewLayerPoint:function(t,e,i){i=this._getNewPixelOrigin(i,e);return this.project(t,e)._subtract(i)},_latLngBoundsToNewLayerBounds:function(t,e,i){i=this._getNewPixelOrigin(i,e);return _([this.project(t.getSouthWest(),e)._subtract(i),this.project(t.getNorthWest(),e)._subtract(i),this.project(t.getSouthEast(),e)._subtract(i),this.project(t.getNorthEast(),e)._subtract(i)])},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitCenter:function(t,e,i){var n,o;return!i||(n=this.project(t,e),o=this.getSize().divideBy(2),o=new f(n.subtract(o),n.add(o)),o=this._getBoundsOffset(o,i,e),Math.abs(o.x)<=1&&Math.abs(o.y)<=1)?t:this.unproject(n.add(o),e)},_limitOffset:function(t,e){var i;return e?(i=new f((i=this.getPixelBounds()).min.add(t),i.max.add(t)),t.add(this._getBoundsOffset(i,e))):t},_getBoundsOffset:function(t,e,i){e=_(this.project(e.getNorthEast(),i),this.project(e.getSouthWest(),i)),i=e.min.subtract(t.min),e=e.max.subtract(t.max);return new p(this._rebound(i.x,-e.x),this._rebound(i.y,-e.y))},_rebound:function(t,e){return 0<t+e?Math.round(t-e)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(e))},_limitZoom:function(t){var e=this.getMinZoom(),i=this.getMaxZoom(),n=b.any3d?this.options.zoomSnap:1;return n&&(t=Math.round(t/n)*n),Math.max(e,Math.min(i,t))},_onPanTransitionStep:function(){this.fire(\"move\")},_onPanTransitionEnd:function(){z(this._mapPane,\"leaflet-pan-anim\"),this.fire(\"moveend\")},_tryAnimatedPan:function(t,e){t=this._getCenterOffset(t)._trunc();return!(!0!==(e&&e.animate)&&!this.getSize().contains(t))&&(this.panBy(t,e),!0)},_createAnimProxy:function(){var t=this._proxy=P(\"div\",\"leaflet-proxy leaflet-zoom-animated\");this._panes.mapPane.appendChild(t),this.on(\"zoomanim\",function(t){var e=ue,i=this._proxy.style[e];be(this._proxy,this.project(t.center,t.zoom),this.getZoomScale(t.zoom,1)),i===this._proxy.style[e]&&this._animatingZoom&&this._onZoomTransitionEnd()},this),this.on(\"load moveend\",this._animMoveEnd,this),this._on(\"unload\",this._destroyAnimProxy,this)},_destroyAnimProxy:function(){T(this._proxy),this.off(\"load moveend\",this._animMoveEnd,this),delete this._proxy},_animMoveEnd:function(){var t=this.getCenter(),e=this.getZoom();be(this._proxy,this.project(t,e),this.getZoomScale(e,1))},_catchTransitionEnd:function(t){this._animatingZoom&&0<=t.propertyName.indexOf(\"transform\")&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName(\"leaflet-zoom-animated\").length},_tryAnimatedZoom:function(t,e,i){if(!this._animatingZoom){if(i=i||{},!this._zoomAnimated||!1===i.animate||this._nothingToAnimate()||Math.abs(e-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),n=this._getCenterOffset(t)._divideBy(1-1/n);if(!0!==i.animate&&!this.getSize().contains(n))return!1;x(function(){this._moveStart(!0,i.noMoveStart||!1)._animateZoom(t,e,!0)},this)}return!0},_animateZoom:function(t,e,i,n){this._mapPane&&(i&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=e,M(this._mapPane,\"leaflet-zoom-anim\")),this.fire(\"zoomanim\",{center:t,zoom:e,noUpdate:n}),this._tempFireZoomEvent||(this._tempFireZoomEvent=this._zoom!==this._animateToZoom),this._move(this._animateToCenter,this._animateToZoom,void 0,!0),setTimeout(a(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&z(this._mapPane,\"leaflet-zoom-anim\"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom,void 0,!0),this._tempFireZoomEvent&&this.fire(\"zoom\"),delete this._tempFireZoomEvent,this.fire(\"move\"),this._moveEnd(!0))}});function Ue(t){return new B(t)}var B=et.extend({options:{position:\"topright\"},initialize:function(t){c(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),t=t._controlCorners[i];return M(e,\"leaflet-control\"),-1!==i.indexOf(\"bottom\")?t.insertBefore(e,t.firstChild):t.appendChild(e),this._map.on(\"unload\",this.remove,this),this},remove:function(){return this._map&&(T(this._container),this.onRemove&&this.onRemove(this._map),this._map.off(\"unload\",this.remove,this),this._map=null),this},_refocusOnMap:function(t){this._map&&t&&0<t.screenX&&0<t.screenY&&this._map.getContainer().focus()}}),Ve=(A.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.remove(),this},_initControlPos:function(){var i=this._controlCorners={},n=\"leaflet-\",o=this._controlContainer=P(\"div\",n+\"control-container\",this._container);function t(t,e){i[t+e]=P(\"div\",n+t+\" \"+n+e,o)}t(\"top\",\"left\"),t(\"top\",\"right\"),t(\"bottom\",\"left\"),t(\"bottom\",\"right\")},_clearControlPos:function(){for(var t in this._controlCorners)T(this._controlCorners[t]);T(this._controlContainer),delete this._controlCorners,delete this._controlContainer}}),B.extend({options:{collapsed:!0,position:\"topright\",autoZIndex:!0,hideSingleBase:!1,sortLayers:!1,sortFunction:function(t,e,i,n){return i<n?-1:n<i?1:0}},initialize:function(t,e,i){for(var n in c(this,i),this._layerControlInputs=[],this._layers=[],this._lastZIndex=0,this._handlingClick=!1,this._preventClick=!1,t)this._addLayer(t[n],n);for(n in e)this._addLayer(e[n],n,!0)},onAdd:function(t){this._initLayout(),this._update(),(this._map=t).on(\"zoomend\",this._checkDisabledLayers,this);for(var e=0;e<this._layers.length;e++)this._layers[e].layer.on(\"add remove\",this._onLayerChange,this);return this._container},addTo:function(t){return B.prototype.addTo.call(this,t),this._expandIfNotCollapsed()},onRemove:function(){this._map.off(\"zoomend\",this._checkDisabledLayers,this);for(var t=0;t<this._layers.length;t++)this._layers[t].layer.off(\"add remove\",this._onLayerChange,this)},addBaseLayer:function(t,e){return this._addLayer(t,e),this._map?this._update():this},addOverlay:function(t,e){return this._addLayer(t,e,!0),this._map?this._update():this},removeLayer:function(t){t.off(\"add remove\",this._onLayerChange,this);t=this._getLayer(h(t));return t&&this._layers.splice(this._layers.indexOf(t),1),this._map?this._update():this},expand:function(){M(this._container,\"leaflet-control-layers-expanded\"),this._section.style.height=null;var t=this._map.getSize().y-(this._container.offsetTop+50);return t<this._section.clientHeight?(M(this._section,\"leaflet-control-layers-scrollbar\"),this._section.style.height=t+\"px\"):z(this._section,\"leaflet-control-layers-scrollbar\"),this._checkDisabledLayers(),this},collapse:function(){return z(this._container,\"leaflet-control-layers-expanded\"),this},_initLayout:function(){var t=\"leaflet-control-layers\",e=this._container=P(\"div\",t),i=this.options.collapsed,n=(e.setAttribute(\"aria-haspopup\",!0),Ie(e),Be(e),this._section=P(\"section\",t+\"-list\")),o=(i&&(this._map.on(\"click\",this.collapse,this),S(e,{mouseenter:this._expandSafely,mouseleave:this.collapse},this)),this._layersLink=P(\"a\",t+\"-toggle\",e));o.href=\"#\",o.title=\"Layers\",o.setAttribute(\"role\",\"button\"),S(o,{keydown:function(t){13===t.keyCode&&this._expandSafely()},click:function(t){O(t),this._expandSafely()}},this),i||this.expand(),this._baseLayersList=P(\"div\",t+\"-base\",n),this._separator=P(\"div\",t+\"-separator\",n),this._overlaysList=P(\"div\",t+\"-overlays\",n),e.appendChild(n)},_getLayer:function(t){for(var e=0;e<this._layers.length;e++)if(this._layers[e]&&h(this._layers[e].layer)===t)return this._layers[e]},_addLayer:function(t,e,i){this._map&&t.on(\"add remove\",this._onLayerChange,this),this._layers.push({layer:t,name:e,overlay:i}),this.options.sortLayers&&this._layers.sort(a(function(t,e){return this.options.sortFunction(t.layer,e.layer,t.name,e.name)},this)),this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex)),this._expandIfNotCollapsed()},_update:function(){if(this._container){me(this._baseLayersList),me(this._overlaysList),this._layerControlInputs=[];for(var t,e,i,n=0,o=0;o<this._layers.length;o++)i=this._layers[o],this._addItem(i),e=e||i.overlay,t=t||!i.overlay,n+=i.overlay?0:1;this.options.hideSingleBase&&(this._baseLayersList.style.display=(t=t&&1<n)?\"\":\"none\"),this._separator.style.display=e&&t?\"\":\"none\"}return this},_onLayerChange:function(t){this._handlingClick||this._update();var e=this._getLayer(h(t.target)),t=e.overlay?\"add\"===t.type?\"overlayadd\":\"overlayremove\":\"add\"===t.type?\"baselayerchange\":null;t&&this._map.fire(t,e)},_createRadioElement:function(t,e){t='<input type=\"radio\" class=\"leaflet-control-layers-selector\" name=\"'+t+'\"'+(e?' checked=\"checked\"':\"\")+\"/>\",e=document.createElement(\"div\");return e.innerHTML=t,e.firstChild},_addItem:function(t){var e,i=document.createElement(\"label\"),n=this._map.hasLayer(t.layer),n=(t.overlay?((e=document.createElement(\"input\")).type=\"checkbox\",e.className=\"leaflet-control-layers-selector\",e.defaultChecked=n):e=this._createRadioElement(\"leaflet-base-layers_\"+h(this),n),this._layerControlInputs.push(e),e.layerId=h(t.layer),S(e,\"click\",this._onInputClick,this),document.createElement(\"span\")),o=(n.innerHTML=\" \"+t.name,document.createElement(\"span\"));return i.appendChild(o),o.appendChild(e),o.appendChild(n),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(i),this._checkDisabledLayers(),i},_onInputClick:function(){if(!this._preventClick){var t,e,i=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=i.length-1;0<=s;s--)t=i[s],e=this._getLayer(t.layerId).layer,t.checked?n.push(e):t.checked||o.push(e);for(s=0;s<o.length;s++)this._map.hasLayer(o[s])&&this._map.removeLayer(o[s]);for(s=0;s<n.length;s++)this._map.hasLayer(n[s])||this._map.addLayer(n[s]);this._handlingClick=!1,this._refocusOnMap()}},_checkDisabledLayers:function(){for(var t,e,i=this._layerControlInputs,n=this._map.getZoom(),o=i.length-1;0<=o;o--)t=i[o],e=this._getLayer(t.layerId).layer,t.disabled=void 0!==e.options.minZoom&&n<e.options.minZoom||void 0!==e.options.maxZoom&&n>e.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expandSafely:function(){var t=this._section,e=(this._preventClick=!0,S(t,\"click\",O),this.expand(),this);setTimeout(function(){k(t,\"click\",O),e._preventClick=!1})}})),qe=B.extend({options:{position:\"topleft\",zoomInText:'<span aria-hidden=\"true\">+</span>',zoomInTitle:\"Zoom in\",zoomOutText:'<span aria-hidden=\"true\">&#x2212;</span>',zoomOutTitle:\"Zoom out\"},onAdd:function(t){var e=\"leaflet-control-zoom\",i=P(\"div\",e+\" leaflet-bar\"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,e+\"-in\",i,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,e+\"-out\",i,this._zoomOut),this._updateDisabled(),t.on(\"zoomend zoomlevelschange\",this._updateDisabled,this),i},onRemove:function(t){t.off(\"zoomend zoomlevelschange\",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoom<this._map.getMaxZoom()&&this._map.zoomIn(this._map.options.zoomDelta*(t.shiftKey?3:1))},_zoomOut:function(t){!this._disabled&&this._map._zoom>this._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,e,i,n,o){i=P(\"a\",i,n);return i.innerHTML=t,i.href=\"#\",i.title=e,i.setAttribute(\"role\",\"button\"),i.setAttribute(\"aria-label\",e),Ie(i),S(i,\"click\",Re),S(i,\"click\",o,this),S(i,\"click\",this._refocusOnMap,this),i},_updateDisabled:function(){var t=this._map,e=\"leaflet-disabled\";z(this._zoomInButton,e),z(this._zoomOutButton,e),this._zoomInButton.setAttribute(\"aria-disabled\",\"false\"),this._zoomOutButton.setAttribute(\"aria-disabled\",\"false\"),!this._disabled&&t._zoom!==t.getMinZoom()||(M(this._zoomOutButton,e),this._zoomOutButton.setAttribute(\"aria-disabled\",\"true\")),!this._disabled&&t._zoom!==t.getMaxZoom()||(M(this._zoomInButton,e),this._zoomInButton.setAttribute(\"aria-disabled\",\"true\"))}}),Ge=(A.mergeOptions({zoomControl:!0}),A.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new qe,this.addControl(this.zoomControl))}),B.extend({options:{position:\"bottomleft\",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var e=\"leaflet-control-scale\",i=P(\"div\",e),n=this.options;return this._addScales(n,e+\"-line\",i),t.on(n.updateWhenIdle?\"moveend\":\"move\",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?\"moveend\":\"move\",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=P(\"div\",e,i)),t.imperial&&(this._iScale=P(\"div\",e,i))},_update:function(){var t=this._map,e=t.getSize().y/2,t=t.distance(t.containerPointToLatLng([0,e]),t.containerPointToLatLng([this.options.maxWidth,e]));this._updateScales(t)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var e=this._getRoundNum(t);this._updateScale(this._mScale,e<1e3?e+\" m\":e/1e3+\" km\",e/t)},_updateImperial:function(t){var e,i,t=3.2808399*t;5280<t?(i=this._getRoundNum(e=t/5280),this._updateScale(this._iScale,i+\" mi\",i/e)):(i=this._getRoundNum(t),this._updateScale(this._iScale,i+\" ft\",i/t))},_updateScale:function(t,e,i){t.style.width=Math.round(this.options.maxWidth*i)+\"px\",t.innerHTML=e},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+\"\").length-1),t=t/e;return e*(t=10<=t?10:5<=t?5:3<=t?3:2<=t?2:1)}})),Ke=B.extend({options:{position:\"bottomright\",prefix:'<a href=\"https://leafletjs.com\" title=\"A JavaScript library for interactive maps\">'+(b.inlineSvg?'<svg aria-hidden=\"true\" xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" class=\"leaflet-attribution-flag\"><path fill=\"#4C7BE1\" d=\"M0 0h12v4H0z\"/><path fill=\"#FFD500\" d=\"M0 4h12v3H0z\"/><path fill=\"#E0BC00\" d=\"M0 7h12v1H0z\"/></svg> ':\"\")+\"Leaflet</a>\"},initialize:function(t){c(this,t),this._attributions={}},onAdd:function(t){for(var e in(t.attributionControl=this)._container=P(\"div\",\"leaflet-control-attribution\"),Ie(this._container),t._layers)t._layers[e].getAttribution&&this.addAttribution(t._layers[e].getAttribution());return this._update(),t.on(\"layeradd\",this._addAttribution,this),this._container},onRemove:function(t){t.off(\"layeradd\",this._addAttribution,this)},_addAttribution:function(t){t.layer.getAttribution&&(this.addAttribution(t.layer.getAttribution()),t.layer.once(\"remove\",function(){this.removeAttribution(t.layer.getAttribution())},this))},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t&&(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update()),this},removeAttribution:function(t){return t&&this._attributions[t]&&(this._attributions[t]--,this._update()),this},_update:function(){if(this._map){var t,e=[];for(t in this._attributions)this._attributions[t]&&e.push(t);var i=[];this.options.prefix&&i.push(this.options.prefix),e.length&&i.push(e.join(\", \")),this._container.innerHTML=i.join(' <span aria-hidden=\"true\">|</span> ')}}}),n=(A.mergeOptions({attributionControl:!0}),A.addInitHook(function(){this.options.attributionControl&&(new Ke).addTo(this)}),B.Layers=Ve,B.Zoom=qe,B.Scale=Ge,B.Attribution=Ke,Ue.layers=function(t,e,i){return new Ve(t,e,i)},Ue.zoom=function(t){return new qe(t)},Ue.scale=function(t){return new Ge(t)},Ue.attribution=function(t){return new Ke(t)},et.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled||(this._enabled=!0,this.addHooks()),this},disable:function(){return this._enabled&&(this._enabled=!1,this.removeHooks()),this},enabled:function(){return!!this._enabled}})),ft=(n.addTo=function(t,e){return t.addHandler(e,this),this},{Events:e}),Ye=b.touch?\"touchstart mousedown\":\"mousedown\",Xe=it.extend({options:{clickTolerance:3},initialize:function(t,e,i,n){c(this,n),this._element=t,this._dragStartTarget=e||t,this._preventOutline=i},enable:function(){this._enabled||(S(this._dragStartTarget,Ye,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Xe._dragging===this&&this.finishDrag(!0),k(this._dragStartTarget,Ye,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){var e,i;this._enabled&&(this._moved=!1,ve(this._element,\"leaflet-zoom-anim\")||(t.touches&&1!==t.touches.length?Xe._dragging===this&&this.finishDrag():Xe._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||((Xe._dragging=this)._preventOutline&&Me(this._element),Le(),re(),this._moving||(this.fire(\"down\"),i=t.touches?t.touches[0]:t,e=Ce(this._element),this._startPoint=new p(i.clientX,i.clientY),this._startPos=Pe(this._element),this._parentScale=Ze(e),i=\"mousedown\"===t.type,S(document,i?\"mousemove\":\"touchmove\",this._onMove,this),S(document,i?\"mouseup\":\"touchend touchcancel\",this._onUp,this)))))},_onMove:function(t){var e;this._enabled&&(t.touches&&1<t.touches.length?this._moved=!0:!(e=new p((e=t.touches&&1===t.touches.length?t.touches[0]:t).clientX,e.clientY)._subtract(this._startPoint)).x&&!e.y||Math.abs(e.x)+Math.abs(e.y)<this.options.clickTolerance||(e.x/=this._parentScale.x,e.y/=this._parentScale.y,O(t),this._moved||(this.fire(\"dragstart\"),this._moved=!0,M(document.body,\"leaflet-dragging\"),this._lastTarget=t.target||t.srcElement,window.SVGElementInstance&&this._lastTarget instanceof window.SVGElementInstance&&(this._lastTarget=this._lastTarget.correspondingUseElement),M(this._lastTarget,\"leaflet-drag-target\")),this._newPos=this._startPos.add(e),this._moving=!0,this._lastEvent=t,this._updatePosition()))},_updatePosition:function(){var t={originalEvent:this._lastEvent};this.fire(\"predrag\",t),Z(this._element,this._newPos),this.fire(\"drag\",t)},_onUp:function(){this._enabled&&this.finishDrag()},finishDrag:function(t){z(document.body,\"leaflet-dragging\"),this._lastTarget&&(z(this._lastTarget,\"leaflet-drag-target\"),this._lastTarget=null),k(document,\"mousemove touchmove\",this._onMove,this),k(document,\"mouseup touchend touchcancel\",this._onUp,this),Te(),ae();var e=this._moved&&this._moving;this._moving=!1,Xe._dragging=!1,e&&this.fire(\"dragend\",{noInertia:t,distance:this._newPos.distanceTo(this._startPos)})}});function Je(t,e,i){for(var n,o,s,r,a,h,l,u=[1,4,2,8],c=0,d=t.length;c<d;c++)t[c]._code=si(t[c],e);for(s=0;s<4;s++){for(h=u[s],n=[],c=0,o=(d=t.length)-1;c<d;o=c++)r=t[c],a=t[o],r._code&h?a._code&h||((l=oi(a,r,h,e,i))._code=si(l,e),n.push(l)):(a._code&h&&((l=oi(a,r,h,e,i))._code=si(l,e),n.push(l)),n.push(r));t=n}return t}function $e(t,e){var i,n,o,s,r,a,h;if(!t||0===t.length)throw new Error(\"latlngs not passed\");I(t)||(console.warn(\"latlngs are not flat! Only the first ring will be used\"),t=t[0]);for(var l=w([0,0]),u=g(t),c=(u.getNorthWest().distanceTo(u.getSouthWest())*u.getNorthEast().distanceTo(u.getNorthWest())<1700&&(l=Qe(t)),t.length),d=[],_=0;_<c;_++){var p=w(t[_]);d.push(e.project(w([p.lat-l.lat,p.lng-l.lng])))}for(_=r=a=h=0,i=c-1;_<c;i=_++)n=d[_],o=d[i],s=n.y*o.x-o.y*n.x,a+=(n.x+o.x)*s,h+=(n.y+o.y)*s,r+=3*s;u=0===r?d[0]:[a/r,h/r],u=e.unproject(m(u));return w([u.lat+l.lat,u.lng+l.lng])}function Qe(t){for(var e=0,i=0,n=0,o=0;o<t.length;o++){var s=w(t[o]);e+=s.lat,i+=s.lng,n++}return w([e/n,i/n])}var ti,gt={__proto__:null,clipPolygon:Je,polygonCenter:$e,centroid:Qe};function ei(t,e){if(e&&t.length){var i=t=function(t,e){for(var i=[t[0]],n=1,o=0,s=t.length;n<s;n++)(function(t,e){var i=e.x-t.x,e=e.y-t.y;return i*i+e*e})(t[n],t[o])>e&&(i.push(t[n]),o=n);o<s-1&&i.push(t[s-1]);return i}(t,e=e*e),n=i.length,o=new(typeof Uint8Array!=void 0+\"\"?Uint8Array:Array)(n);o[0]=o[n-1]=1,function t(e,i,n,o,s){var r,a,h,l=0;for(a=o+1;a<=s-1;a++)h=ri(e[a],e[o],e[s],!0),l<h&&(r=a,l=h);n<l&&(i[r]=1,t(e,i,n,o,r),t(e,i,n,r,s))}(i,o,e,0,n-1);var s,r=[];for(s=0;s<n;s++)o[s]&&r.push(i[s]);return r}return t.slice()}function ii(t,e,i){return Math.sqrt(ri(t,e,i,!0))}function ni(t,e,i,n,o){var s,r,a,h=n?ti:si(t,i),l=si(e,i);for(ti=l;;){if(!(h|l))return[t,e];if(h&l)return!1;a=si(r=oi(t,e,s=h||l,i,o),i),s===h?(t=r,h=a):(e=r,l=a)}}function oi(t,e,i,n,o){var s,r,a=e.x-t.x,e=e.y-t.y,h=n.min,n=n.max;return 8&i?(s=t.x+a*(n.y-t.y)/e,r=n.y):4&i?(s=t.x+a*(h.y-t.y)/e,r=h.y):2&i?(s=n.x,r=t.y+e*(n.x-t.x)/a):1&i&&(s=h.x,r=t.y+e*(h.x-t.x)/a),new p(s,r,o)}function si(t,e){var i=0;return t.x<e.min.x?i|=1:t.x>e.max.x&&(i|=2),t.y<e.min.y?i|=4:t.y>e.max.y&&(i|=8),i}function ri(t,e,i,n){var o=e.x,e=e.y,s=i.x-o,r=i.y-e,a=s*s+r*r;return 0<a&&(1<(a=((t.x-o)*s+(t.y-e)*r)/a)?(o=i.x,e=i.y):0<a&&(o+=s*a,e+=r*a)),s=t.x-o,r=t.y-e,n?s*s+r*r:new p(o,e)}function I(t){return!d(t[0])||\"object\"!=typeof t[0][0]&&void 0!==t[0][0]}function ai(t){return console.warn(\"Deprecated use of _flat, please use L.LineUtil.isFlat instead.\"),I(t)}function hi(t,e){var i,n,o,s,r,a;if(!t||0===t.length)throw new Error(\"latlngs not passed\");I(t)||(console.warn(\"latlngs are not flat! Only the first ring will be used\"),t=t[0]);for(var h=w([0,0]),l=g(t),u=(l.getNorthWest().distanceTo(l.getSouthWest())*l.getNorthEast().distanceTo(l.getNorthWest())<1700&&(h=Qe(t)),t.length),c=[],d=0;d<u;d++){var _=w(t[d]);c.push(e.project(w([_.lat-h.lat,_.lng-h.lng])))}for(i=d=0;d<u-1;d++)i+=c[d].distanceTo(c[d+1])/2;if(0===i)a=c[0];else for(n=d=0;d<u-1;d++)if(o=c[d],s=c[d+1],i<(n+=r=o.distanceTo(s))){a=[s.x-(r=(n-i)/r)*(s.x-o.x),s.y-r*(s.y-o.y)];break}l=e.unproject(m(a));return w([l.lat+h.lat,l.lng+h.lng])}var vt={__proto__:null,simplify:ei,pointToSegmentDistance:ii,closestPointOnSegment:function(t,e,i){return ri(t,e,i)},clipSegment:ni,_getEdgeIntersection:oi,_getBitCode:si,_sqClosestPointOnSegment:ri,isFlat:I,_flat:ai,polylineCenter:hi},yt={project:function(t){return new p(t.lng,t.lat)},unproject:function(t){return new v(t.y,t.x)},bounds:new f([-180,-90],[180,90])},xt={R:6378137,R_MINOR:6356752.314245179,bounds:new f([-20037508.34279,-15496570.73972],[20037508.34279,18764656.23138]),project:function(t){var e=Math.PI/180,i=this.R,n=t.lat*e,o=this.R_MINOR/i,o=Math.sqrt(1-o*o),s=o*Math.sin(n),s=Math.tan(Math.PI/4-n/2)/Math.pow((1-s)/(1+s),o/2),n=-i*Math.log(Math.max(s,1e-10));return new p(t.lng*e*i,n)},unproject:function(t){for(var e,i=180/Math.PI,n=this.R,o=this.R_MINOR/n,s=Math.sqrt(1-o*o),r=Math.exp(-t.y/n),a=Math.PI/2-2*Math.atan(r),h=0,l=.1;h<15&&1e-7<Math.abs(l);h++)e=s*Math.sin(a),e=Math.pow((1-e)/(1+e),s/2),a+=l=Math.PI/2-2*Math.atan(r*e)-a;return new v(a*i,t.x*i/n)}},wt={__proto__:null,LonLat:yt,Mercator:xt,SphericalMercator:rt},Pt=l({},st,{code:\"EPSG:3395\",projection:xt,transformation:ht(bt=.5/(Math.PI*xt.R),.5,-bt,.5)}),li=l({},st,{code:\"EPSG:4326\",projection:yt,transformation:ht(1/180,1,-1/180,.5)}),Lt=l({},ot,{projection:yt,transformation:ht(1,0,-1,0),scale:function(t){return Math.pow(2,t)},zoom:function(t){return Math.log(t)/Math.LN2},distance:function(t,e){var i=e.lng-t.lng,e=e.lat-t.lat;return Math.sqrt(i*i+e*e)},infinite:!0}),o=(ot.Earth=st,ot.EPSG3395=Pt,ot.EPSG3857=lt,ot.EPSG900913=ut,ot.EPSG4326=li,ot.Simple=Lt,it.extend({options:{pane:\"overlayPane\",attribution:null,bubblingMouseEvents:!0},addTo:function(t){return t.addLayer(this),this},remove:function(){return this.removeFrom(this._map||this._mapToAdd)},removeFrom:function(t){return t&&t.removeLayer(this),this},getPane:function(t){return this._map.getPane(t?this.options[t]||t:this.options.pane)},addInteractiveTarget:function(t){return this._map._targets[h(t)]=this},removeInteractiveTarget:function(t){return delete this._map._targets[h(t)],this},getAttribution:function(){return this.options.attribution},_layerAdd:function(t){var e,i=t.target;i.hasLayer(this)&&(this._map=i,this._zoomAnimated=i._zoomAnimated,this.getEvents&&(e=this.getEvents(),i.on(e,this),this.once(\"remove\",function(){i.off(e,this)},this)),this.onAdd(i),this.fire(\"add\"),i.fire(\"layeradd\",{layer:this}))}})),ui=(A.include({addLayer:function(t){var e;if(t._layerAdd)return e=h(t),this._layers[e]||((this._layers[e]=t)._mapToAdd=this,t.beforeAdd&&t.beforeAdd(this),this.whenReady(t._layerAdd,t)),this;throw new Error(\"The provided object is not a Layer.\")},removeLayer:function(t){var e=h(t);return this._layers[e]&&(this._loaded&&t.onRemove(this),delete this._layers[e],this._loaded&&(this.fire(\"layerremove\",{layer:t}),t.fire(\"remove\")),t._map=t._mapToAdd=null),this},hasLayer:function(t){return h(t)in this._layers},eachLayer:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},_addLayers:function(t){for(var e=0,i=(t=t?d(t)?t:[t]:[]).length;e<i;e++)this.addLayer(t[e])},_addZoomLimit:function(t){isNaN(t.options.maxZoom)&&isNaN(t.options.minZoom)||(this._zoomBoundLayers[h(t)]=t,this._updateZoomLevels())},_removeZoomLimit:function(t){t=h(t);this._zoomBoundLayers[t]&&(delete this._zoomBoundLayers[t],this._updateZoomLevels())},_updateZoomLevels:function(){var t,e=1/0,i=-1/0,n=this._getZoomSpan();for(t in this._zoomBoundLayers)var o=this._zoomBoundLayers[t].options,e=void 0===o.minZoom?e:Math.min(e,o.minZoom),i=void 0===o.maxZoom?i:Math.max(i,o.maxZoom);this._layersMaxZoom=i===-1/0?void 0:i,this._layersMinZoom=e===1/0?void 0:e,n!==this._getZoomSpan()&&this.fire(\"zoomlevelschange\"),void 0===this.options.maxZoom&&this._layersMaxZoom&&this.getZoom()>this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()<this._layersMinZoom&&this.setZoom(this._layersMinZoom)}}),o.extend({initialize:function(t,e){var i,n;if(c(this,e),this._layers={},t)for(i=0,n=t.length;i<n;i++)this.addLayer(t[i])},addLayer:function(t){var e=this.getLayerId(t);return this._layers[e]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){t=t in this._layers?t:this.getLayerId(t);return this._map&&this._layers[t]&&this._map.removeLayer(this._layers[t]),delete this._layers[t],this},hasLayer:function(t){return(\"number\"==typeof t?t:this.getLayerId(t))in this._layers},clearLayers:function(){return this.eachLayer(this.removeLayer,this)},invoke:function(t){var e,i,n=Array.prototype.slice.call(arguments,1);for(e in this._layers)(i=this._layers[e])[t]&&i[t].apply(i,n);return this},onAdd:function(t){this.eachLayer(t.addLayer,t)},onRemove:function(t){this.eachLayer(t.removeLayer,t)},eachLayer:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},getLayer:function(t){return this._layers[t]},getLayers:function(){var t=[];return this.eachLayer(t.push,t),t},setZIndex:function(t){return this.invoke(\"setZIndex\",t)},getLayerId:h})),ci=ui.extend({addLayer:function(t){return this.hasLayer(t)?this:(t.addEventParent(this),ui.prototype.addLayer.call(this,t),this.fire(\"layeradd\",{layer:t}))},removeLayer:function(t){return this.hasLayer(t)?((t=t in this._layers?this._layers[t]:t).removeEventParent(this),ui.prototype.removeLayer.call(this,t),this.fire(\"layerremove\",{layer:t})):this},setStyle:function(t){return this.invoke(\"setStyle\",t)},bringToFront:function(){return this.invoke(\"bringToFront\")},bringToBack:function(){return this.invoke(\"bringToBack\")},getBounds:function(){var t,e=new s;for(t in this._layers){var i=this._layers[t];e.extend(i.getBounds?i.getBounds():i.getLatLng())}return e}}),di=et.extend({options:{popupAnchor:[0,0],tooltipAnchor:[0,0],crossOrigin:!1},initialize:function(t){c(this,t)},createIcon:function(t){return this._createIcon(\"icon\",t)},createShadow:function(t){return this._createIcon(\"shadow\",t)},_createIcon:function(t,e){var i=this._getIconUrl(t);if(i)return i=this._createImg(i,e&&\"IMG\"===e.tagName?e:null),this._setIconStyles(i,t),!this.options.crossOrigin&&\"\"!==this.options.crossOrigin||(i.crossOrigin=!0===this.options.crossOrigin?\"\":this.options.crossOrigin),i;if(\"icon\"===t)throw new Error(\"iconUrl not set in Icon options (see the docs).\");return null},_setIconStyles:function(t,e){var i=this.options,n=i[e+\"Size\"],n=m(n=\"number\"==typeof n?[n,n]:n),o=m(\"shadow\"===e&&i.shadowAnchor||i.iconAnchor||n&&n.divideBy(2,!0));t.className=\"leaflet-marker-\"+e+\" \"+(i.className||\"\"),o&&(t.style.marginLeft=-o.x+\"px\",t.style.marginTop=-o.y+\"px\"),n&&(t.style.width=n.x+\"px\",t.style.height=n.y+\"px\")},_createImg:function(t,e){return(e=e||document.createElement(\"img\")).src=t,e},_getIconUrl:function(t){return b.retina&&this.options[t+\"RetinaUrl\"]||this.options[t+\"Url\"]}});var _i=di.extend({options:{iconUrl:\"marker-icon.png\",iconRetinaUrl:\"marker-icon-2x.png\",shadowUrl:\"marker-shadow.png\",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],tooltipAnchor:[16,-28],shadowSize:[41,41]},_getIconUrl:function(t){return\"string\"!=typeof _i.imagePath&&(_i.imagePath=this._detectIconPath()),(this.options.imagePath||_i.imagePath)+di.prototype._getIconUrl.call(this,t)},_stripUrl:function(t){function e(t,e,i){return(e=e.exec(t))&&e[i]}return(t=e(t,/^url\\((['\"])?(.+)\\1\\)$/,2))&&e(t,/^(.*)marker-icon\\.png$/,1)},_detectIconPath:function(){var t=P(\"div\",\"leaflet-default-icon-path\",document.body),e=pe(t,\"background-image\")||pe(t,\"backgroundImage\");return document.body.removeChild(t),(e=this._stripUrl(e))?e:(t=document.querySelector('link[href$=\"leaflet.css\"]'))?t.href.substring(0,t.href.length-\"leaflet.css\".length-1):\"\"}}),pi=n.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new Xe(t,t,!0)),this._draggable.on({dragstart:this._onDragStart,predrag:this._onPreDrag,drag:this._onDrag,dragend:this._onDragEnd},this).enable(),M(t,\"leaflet-marker-draggable\")},removeHooks:function(){this._draggable.off({dragstart:this._onDragStart,predrag:this._onPreDrag,drag:this._onDrag,dragend:this._onDragEnd},this).disable(),this._marker._icon&&z(this._marker._icon,\"leaflet-marker-draggable\")},moved:function(){return this._draggable&&this._draggable._moved},_adjustPan:function(t){var e=this._marker,i=e._map,n=this._marker.options.autoPanSpeed,o=this._marker.options.autoPanPadding,s=Pe(e._icon),r=i.getPixelBounds(),a=i.getPixelOrigin(),a=_(r.min._subtract(a).add(o),r.max._subtract(a).subtract(o));a.contains(s)||(o=m((Math.max(a.max.x,s.x)-a.max.x)/(r.max.x-a.max.x)-(Math.min(a.min.x,s.x)-a.min.x)/(r.min.x-a.min.x),(Math.max(a.max.y,s.y)-a.max.y)/(r.max.y-a.max.y)-(Math.min(a.min.y,s.y)-a.min.y)/(r.min.y-a.min.y)).multiplyBy(n),i.panBy(o,{animate:!1}),this._draggable._newPos._add(o),this._draggable._startPos._add(o),Z(e._icon,this._draggable._newPos),this._onDrag(t),this._panRequest=x(this._adjustPan.bind(this,t)))},_onDragStart:function(){this._oldLatLng=this._marker.getLatLng(),this._marker.closePopup&&this._marker.closePopup(),this._marker.fire(\"movestart\").fire(\"dragstart\")},_onPreDrag:function(t){this._marker.options.autoPan&&(r(this._panRequest),this._panRequest=x(this._adjustPan.bind(this,t)))},_onDrag:function(t){var e=this._marker,i=e._shadow,n=Pe(e._icon),o=e._map.layerPointToLatLng(n);i&&Z(i,n),e._latlng=o,t.latlng=o,t.oldLatLng=this._oldLatLng,e.fire(\"move\",t).fire(\"drag\",t)},_onDragEnd:function(t){r(this._panRequest),delete this._oldLatLng,this._marker.fire(\"moveend\").fire(\"dragend\",t)}}),mi=o.extend({options:{icon:new _i,interactive:!0,keyboard:!0,title:\"\",alt:\"Marker\",zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250,pane:\"markerPane\",shadowPane:\"shadowPane\",bubblingMouseEvents:!1,autoPanOnFocus:!0,draggable:!1,autoPan:!1,autoPanPadding:[50,50],autoPanSpeed:10},initialize:function(t,e){c(this,e),this._latlng=w(t)},onAdd:function(t){this._zoomAnimated=this._zoomAnimated&&t.options.markerZoomAnimation,this._zoomAnimated&&t.on(\"zoomanim\",this._animateZoom,this),this._initIcon(),this.update()},onRemove:function(t){this.dragging&&this.dragging.enabled()&&(this.options.draggable=!0,this.dragging.removeHooks()),delete this.dragging,this._zoomAnimated&&t.off(\"zoomanim\",this._animateZoom,this),this._removeIcon(),this._removeShadow()},getEvents:function(){return{zoom:this.update,viewreset:this.update}},getLatLng:function(){return this._latlng},setLatLng:function(t){var e=this._latlng;return this._latlng=w(t),this.update(),this.fire(\"move\",{oldLatLng:e,latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update()},getIcon:function(){return this.options.icon},setIcon:function(t){return this.options.icon=t,this._map&&(this._initIcon(),this.update()),this._popup&&this.bindPopup(this._popup,this._popup.options),this},getElement:function(){return this._icon},update:function(){var t;return this._icon&&this._map&&(t=this._map.latLngToLayerPoint(this._latlng).round(),this._setPos(t)),this},_initIcon:function(){var t=this.options,e=\"leaflet-zoom-\"+(this._zoomAnimated?\"animated\":\"hide\"),i=t.icon.createIcon(this._icon),n=!1,i=(i!==this._icon&&(this._icon&&this._removeIcon(),n=!0,t.title&&(i.title=t.title),\"IMG\"===i.tagName&&(i.alt=t.alt||\"\")),M(i,e),t.keyboard&&(i.tabIndex=\"0\",i.setAttribute(\"role\",\"button\")),this._icon=i,t.riseOnHover&&this.on({mouseover:this._bringToFront,mouseout:this._resetZIndex}),this.options.autoPanOnFocus&&S(i,\"focus\",this._panOnFocus,this),t.icon.createShadow(this._shadow)),o=!1;i!==this._shadow&&(this._removeShadow(),o=!0),i&&(M(i,e),i.alt=\"\"),this._shadow=i,t.opacity<1&&this._updateOpacity(),n&&this.getPane().appendChild(this._icon),this._initInteraction(),i&&o&&this.getPane(t.shadowPane).appendChild(this._shadow)},_removeIcon:function(){this.options.riseOnHover&&this.off({mouseover:this._bringToFront,mouseout:this._resetZIndex}),this.options.autoPanOnFocus&&k(this._icon,\"focus\",this._panOnFocus,this),T(this._icon),this.removeInteractiveTarget(this._icon),this._icon=null},_removeShadow:function(){this._shadow&&T(this._shadow),this._shadow=null},_setPos:function(t){this._icon&&Z(this._icon,t),this._shadow&&Z(this._shadow,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){this._icon&&(this._icon.style.zIndex=this._zIndex+t)},_animateZoom:function(t){t=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center).round();this._setPos(t)},_initInteraction:function(){var t;this.options.interactive&&(M(this._icon,\"leaflet-interactive\"),this.addInteractiveTarget(this._icon),pi&&(t=this.options.draggable,this.dragging&&(t=this.dragging.enabled(),this.dragging.disable()),this.dragging=new pi(this),t&&this.dragging.enable()))},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},_updateOpacity:function(){var t=this.options.opacity;this._icon&&C(this._icon,t),this._shadow&&C(this._shadow,t)},_bringToFront:function(){this._updateZIndex(this.options.riseOffset)},_resetZIndex:function(){this._updateZIndex(0)},_panOnFocus:function(){var t,e,i=this._map;i&&(t=(e=this.options.icon.options).iconSize?m(e.iconSize):m(0,0),e=e.iconAnchor?m(e.iconAnchor):m(0,0),i.panInside(this._latlng,{paddingTopLeft:e,paddingBottomRight:t.subtract(e)}))},_getPopupAnchor:function(){return this.options.icon.options.popupAnchor},_getTooltipAnchor:function(){return this.options.icon.options.tooltipAnchor}});var fi=o.extend({options:{stroke:!0,color:\"#3388ff\",weight:3,opacity:1,lineCap:\"round\",lineJoin:\"round\",dashArray:null,dashOffset:null,fill:!1,fillColor:null,fillOpacity:.2,fillRule:\"evenodd\",interactive:!0,bubblingMouseEvents:!0},beforeAdd:function(t){this._renderer=t.getRenderer(this)},onAdd:function(){this._renderer._initPath(this),this._reset(),this._renderer._addPath(this)},onRemove:function(){this._renderer._removePath(this)},redraw:function(){return this._map&&this._renderer._updatePath(this),this},setStyle:function(t){return c(this,t),this._renderer&&(this._renderer._updateStyle(this),this.options.stroke&&t&&Object.prototype.hasOwnProperty.call(t,\"weight\")&&this._updateBounds()),this},bringToFront:function(){return this._renderer&&this._renderer._bringToFront(this),this},bringToBack:function(){return this._renderer&&this._renderer._bringToBack(this),this},getElement:function(){return this._path},_reset:function(){this._project(),this._update()},_clickTolerance:function(){return(this.options.stroke?this.options.weight/2:0)+(this._renderer.options.tolerance||0)}}),gi=fi.extend({options:{fill:!0,radius:10},initialize:function(t,e){c(this,e),this._latlng=w(t),this._radius=this.options.radius},setLatLng:function(t){var e=this._latlng;return this._latlng=w(t),this.redraw(),this.fire(\"move\",{oldLatLng:e,latlng:this._latlng})},getLatLng:function(){return this._latlng},setRadius:function(t){return this.options.radius=this._radius=t,this.redraw()},getRadius:function(){return this._radius},setStyle:function(t){var e=t&&t.radius||this._radius;return fi.prototype.setStyle.call(this,t),this.setRadius(e),this},_project:function(){this._point=this._map.latLngToLayerPoint(this._latlng),this._updateBounds()},_updateBounds:function(){var t=this._radius,e=this._radiusY||t,i=this._clickTolerance(),t=[t+i,e+i];this._pxBounds=new f(this._point.subtract(t),this._point.add(t))},_update:function(){this._map&&this._updatePath()},_updatePath:function(){this._renderer._updateCircle(this)},_empty:function(){return this._radius&&!this._renderer._bounds.intersects(this._pxBounds)},_containsPoint:function(t){return t.distanceTo(this._point)<=this._radius+this._clickTolerance()}});var vi=gi.extend({initialize:function(t,e,i){if(c(this,e=\"number\"==typeof e?l({},i,{radius:e}):e),this._latlng=w(t),isNaN(this.options.radius))throw new Error(\"Circle radius cannot be NaN\");this._mRadius=this.options.radius},setRadius:function(t){return this._mRadius=t,this.redraw()},getRadius:function(){return this._mRadius},getBounds:function(){var t=[this._radius,this._radiusY||this._radius];return new s(this._map.layerPointToLatLng(this._point.subtract(t)),this._map.layerPointToLatLng(this._point.add(t)))},setStyle:fi.prototype.setStyle,_project:function(){var t,e,i,n,o,s=this._latlng.lng,r=this._latlng.lat,a=this._map,h=a.options.crs;h.distance===st.distance?(n=Math.PI/180,o=this._mRadius/st.R/n,t=a.project([r+o,s]),e=a.project([r-o,s]),e=t.add(e).divideBy(2),i=a.unproject(e).lat,n=Math.acos((Math.cos(o*n)-Math.sin(r*n)*Math.sin(i*n))/(Math.cos(r*n)*Math.cos(i*n)))/n,!isNaN(n)&&0!==n||(n=o/Math.cos(Math.PI/180*r)),this._point=e.subtract(a.getPixelOrigin()),this._radius=isNaN(n)?0:e.x-a.project([i,s-n]).x,this._radiusY=e.y-t.y):(o=h.unproject(h.project(this._latlng).subtract([this._mRadius,0])),this._point=a.latLngToLayerPoint(this._latlng),this._radius=this._point.x-a.latLngToLayerPoint(o).x),this._updateBounds()}});var yi=fi.extend({options:{smoothFactor:1,noClip:!1},initialize:function(t,e){c(this,e),this._setLatLngs(t)},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._setLatLngs(t),this.redraw()},isEmpty:function(){return!this._latlngs.length},closestLayerPoint:function(t){for(var e=1/0,i=null,n=ri,o=0,s=this._parts.length;o<s;o++)for(var r=this._parts[o],a=1,h=r.length;a<h;a++){var l,u,c=n(t,l=r[a-1],u=r[a],!0);c<e&&(e=c,i=n(t,l,u))}return i&&(i.distance=Math.sqrt(e)),i},getCenter:function(){if(this._map)return hi(this._defaultShape(),this._map.options.crs);throw new Error(\"Must add layer to map before using getCenter()\")},getBounds:function(){return this._bounds},addLatLng:function(t,e){return e=e||this._defaultShape(),t=w(t),e.push(t),this._bounds.extend(t),this.redraw()},_setLatLngs:function(t){this._bounds=new s,this._latlngs=this._convertLatLngs(t)},_defaultShape:function(){return I(this._latlngs)?this._latlngs:this._latlngs[0]},_convertLatLngs:function(t){for(var e=[],i=I(t),n=0,o=t.length;n<o;n++)i?(e[n]=w(t[n]),this._bounds.extend(e[n])):e[n]=this._convertLatLngs(t[n]);return e},_project:function(){var t=new f;this._rings=[],this._projectLatlngs(this._latlngs,this._rings,t),this._bounds.isValid()&&t.isValid()&&(this._rawPxBounds=t,this._updateBounds())},_updateBounds:function(){var t=this._clickTolerance(),t=new p(t,t);this._rawPxBounds&&(this._pxBounds=new f([this._rawPxBounds.min.subtract(t),this._rawPxBounds.max.add(t)]))},_projectLatlngs:function(t,e,i){var n,o,s=t[0]instanceof v,r=t.length;if(s){for(o=[],n=0;n<r;n++)o[n]=this._map.latLngToLayerPoint(t[n]),i.extend(o[n]);e.push(o)}else for(n=0;n<r;n++)this._projectLatlngs(t[n],e,i)},_clipPoints:function(){var t=this._renderer._bounds;if(this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else for(var e,i,n,o,s=this._parts,r=0,a=0,h=this._rings.length;r<h;r++)for(e=0,i=(o=this._rings[r]).length;e<i-1;e++)(n=ni(o[e],o[e+1],t,e,!0))&&(s[a]=s[a]||[],s[a].push(n[0]),n[1]===o[e+1]&&e!==i-2||(s[a].push(n[1]),a++))},_simplifyPoints:function(){for(var t=this._parts,e=this.options.smoothFactor,i=0,n=t.length;i<n;i++)t[i]=ei(t[i],e)},_update:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),this._updatePath())},_updatePath:function(){this._renderer._updatePoly(this)},_containsPoint:function(t,e){var i,n,o,s,r,a,h=this._clickTolerance();if(this._pxBounds&&this._pxBounds.contains(t))for(i=0,s=this._parts.length;i<s;i++)for(n=0,o=(r=(a=this._parts[i]).length)-1;n<r;o=n++)if((e||0!==n)&&ii(t,a[o],a[n])<=h)return!0;return!1}});yi._flat=ai;var xi=yi.extend({options:{fill:!0},isEmpty:function(){return!this._latlngs.length||!this._latlngs[0].length},getCenter:function(){if(this._map)return $e(this._defaultShape(),this._map.options.crs);throw new Error(\"Must add layer to map before using getCenter()\")},_convertLatLngs:function(t){var t=yi.prototype._convertLatLngs.call(this,t),e=t.length;return 2<=e&&t[0]instanceof v&&t[0].equals(t[e-1])&&t.pop(),t},_setLatLngs:function(t){yi.prototype._setLatLngs.call(this,t),I(this._latlngs)&&(this._latlngs=[this._latlngs])},_defaultShape:function(){return(I(this._latlngs[0])?this._latlngs:this._latlngs[0])[0]},_clipPoints:function(){var t=this._renderer._bounds,e=this.options.weight,e=new p(e,e),t=new f(t.min.subtract(e),t.max.add(e));if(this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else for(var i,n=0,o=this._rings.length;n<o;n++)(i=Je(this._rings[n],t,!0)).length&&this._parts.push(i)},_updatePath:function(){this._renderer._updatePoly(this,!0)},_containsPoint:function(t){var e,i,n,o,s,r,a,h,l=!1;if(!this._pxBounds||!this._pxBounds.contains(t))return!1;for(o=0,a=this._parts.length;o<a;o++)for(s=0,r=(h=(e=this._parts[o]).length)-1;s<h;r=s++)i=e[s],n=e[r],i.y>t.y!=n.y>t.y&&t.x<(n.x-i.x)*(t.y-i.y)/(n.y-i.y)+i.x&&(l=!l);return l||yi.prototype._containsPoint.call(this,t,!0)}});var wi=ci.extend({initialize:function(t,e){c(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,n,o=d(t)?t:t.features;if(o){for(e=0,i=o.length;e<i;e++)((n=o[e]).geometries||n.geometry||n.features||n.coordinates)&&this.addData(n);return this}var s,r=this.options;return(!r.filter||r.filter(t))&&(s=bi(t,r))?(s.feature=Zi(t),s.defaultOptions=s.options,this.resetStyle(s),r.onEachFeature&&r.onEachFeature(t,s),this.addLayer(s)):this},resetStyle:function(t){return void 0===t?this.eachLayer(this.resetStyle,this):(t.options=l({},t.defaultOptions),this._setLayerStyle(t,this.options.style),this)},setStyle:function(e){return this.eachLayer(function(t){this._setLayerStyle(t,e)},this)},_setLayerStyle:function(t,e){t.setStyle&&(\"function\"==typeof e&&(e=e(t.feature)),t.setStyle(e))}});function bi(t,e){var i,n,o,s,r=\"Feature\"===t.type?t.geometry:t,a=r?r.coordinates:null,h=[],l=e&&e.pointToLayer,u=e&&e.coordsToLatLng||Li;if(!a&&!r)return null;switch(r.type){case\"Point\":return Pi(l,t,i=u(a),e);case\"MultiPoint\":for(o=0,s=a.length;o<s;o++)i=u(a[o]),h.push(Pi(l,t,i,e));return new ci(h);case\"LineString\":case\"MultiLineString\":return n=Ti(a,\"LineString\"===r.type?0:1,u),new yi(n,e);case\"Polygon\":case\"MultiPolygon\":return n=Ti(a,\"Polygon\"===r.type?1:2,u),new xi(n,e);case\"GeometryCollection\":for(o=0,s=r.geometries.length;o<s;o++){var c=bi({geometry:r.geometries[o],type:\"Feature\",properties:t.properties},e);c&&h.push(c)}return new ci(h);case\"FeatureCollection\":for(o=0,s=r.features.length;o<s;o++){var d=bi(r.features[o],e);d&&h.push(d)}return new ci(h);default:throw new Error(\"Invalid GeoJSON object.\")}}function Pi(t,e,i,n){return t?t(e,i):new mi(i,n&&n.markersInheritOptions&&n)}function Li(t){return new v(t[1],t[0],t[2])}function Ti(t,e,i){for(var n,o=[],s=0,r=t.length;s<r;s++)n=e?Ti(t[s],e-1,i):(i||Li)(t[s]),o.push(n);return o}function Mi(t,e){return void 0!==(t=w(t)).alt?[i(t.lng,e),i(t.lat,e),i(t.alt,e)]:[i(t.lng,e),i(t.lat,e)]}function zi(t,e,i,n){for(var o=[],s=0,r=t.length;s<r;s++)o.push(e?zi(t[s],I(t[s])?0:e-1,i,n):Mi(t[s],n));return!e&&i&&0<o.length&&o.push(o[0].slice()),o}function Ci(t,e){return t.feature?l({},t.feature,{geometry:e}):Zi(e)}function Zi(t){return\"Feature\"===t.type||\"FeatureCollection\"===t.type?t:{type:\"Feature\",properties:{},geometry:t}}Tt={toGeoJSON:function(t){return Ci(this,{type:\"Point\",coordinates:Mi(this.getLatLng(),t)})}};function Si(t,e){return new wi(t,e)}mi.include(Tt),vi.include(Tt),gi.include(Tt),yi.include({toGeoJSON:function(t){var e=!I(this._latlngs);return Ci(this,{type:(e?\"Multi\":\"\")+\"LineString\",coordinates:zi(this._latlngs,e?1:0,!1,t)})}}),xi.include({toGeoJSON:function(t){var e=!I(this._latlngs),i=e&&!I(this._latlngs[0]),t=zi(this._latlngs,i?2:e?1:0,!0,t);return Ci(this,{type:(i?\"Multi\":\"\")+\"Polygon\",coordinates:t=e?t:[t]})}}),ui.include({toMultiPoint:function(e){var i=[];return this.eachLayer(function(t){i.push(t.toGeoJSON(e).geometry.coordinates)}),Ci(this,{type:\"MultiPoint\",coordinates:i})},toGeoJSON:function(e){var i,n,t=this.feature&&this.feature.geometry&&this.feature.geometry.type;return\"MultiPoint\"===t?this.toMultiPoint(e):(i=\"GeometryCollection\"===t,n=[],this.eachLayer(function(t){t.toGeoJSON&&(t=t.toGeoJSON(e),i?n.push(t.geometry):\"FeatureCollection\"===(t=Zi(t)).type?n.push.apply(n,t.features):n.push(t))}),i?Ci(this,{geometries:n,type:\"GeometryCollection\"}):{type:\"FeatureCollection\",features:n})}});var Mt=Si,Ei=o.extend({options:{opacity:1,alt:\"\",interactive:!1,crossOrigin:!1,errorOverlayUrl:\"\",zIndex:1,className:\"\"},initialize:function(t,e,i){this._url=t,this._bounds=g(e),c(this,i)},onAdd:function(){this._image||(this._initImage(),this.options.opacity<1&&this._updateOpacity()),this.options.interactive&&(M(this._image,\"leaflet-interactive\"),this.addInteractiveTarget(this._image)),this.getPane().appendChild(this._image),this._reset()},onRemove:function(){T(this._image),this.options.interactive&&this.removeInteractiveTarget(this._image)},setOpacity:function(t){return this.options.opacity=t,this._image&&this._updateOpacity(),this},setStyle:function(t){return t.opacity&&this.setOpacity(t.opacity),this},bringToFront:function(){return this._map&&fe(this._image),this},bringToBack:function(){return this._map&&ge(this._image),this},setUrl:function(t){return this._url=t,this._image&&(this._image.src=t),this},setBounds:function(t){return this._bounds=g(t),this._map&&this._reset(),this},getEvents:function(){var t={zoom:this._reset,viewreset:this._reset};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},getBounds:function(){return this._bounds},getElement:function(){return this._image},_initImage:function(){var t=\"IMG\"===this._url.tagName,e=this._image=t?this._url:P(\"img\");M(e,\"leaflet-image-layer\"),this._zoomAnimated&&M(e,\"leaflet-zoom-animated\"),this.options.className&&M(e,this.options.className),e.onselectstart=u,e.onmousemove=u,e.onload=a(this.fire,this,\"load\"),e.onerror=a(this._overlayOnError,this,\"error\"),!this.options.crossOrigin&&\"\"!==this.options.crossOrigin||(e.crossOrigin=!0===this.options.crossOrigin?\"\":this.options.crossOrigin),this.options.zIndex&&this._updateZIndex(),t?this._url=e.src:(e.src=this._url,e.alt=this.options.alt)},_animateZoom:function(t){var e=this._map.getZoomScale(t.zoom),t=this._map._latLngBoundsToNewLayerBounds(this._bounds,t.zoom,t.center).min;be(this._image,t,e)},_reset:function(){var t=this._image,e=new f(this._map.latLngToLayerPoint(this._bounds.getNorthWest()),this._map.latLngToLayerPoint(this._bounds.getSouthEast())),i=e.getSize();Z(t,e.min),t.style.width=i.x+\"px\",t.style.height=i.y+\"px\"},_updateOpacity:function(){C(this._image,this.options.opacity)},_updateZIndex:function(){this._image&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._image.style.zIndex=this.options.zIndex)},_overlayOnError:function(){this.fire(\"error\");var t=this.options.errorOverlayUrl;t&&this._url!==t&&(this._url=t,this._image.src=t)},getCenter:function(){return this._bounds.getCenter()}}),ki=Ei.extend({options:{autoplay:!0,loop:!0,keepAspectRatio:!0,muted:!1,playsInline:!0},_initImage:function(){var t=\"VIDEO\"===this._url.tagName,e=this._image=t?this._url:P(\"video\");if(M(e,\"leaflet-image-layer\"),this._zoomAnimated&&M(e,\"leaflet-zoom-animated\"),this.options.className&&M(e,this.options.className),e.onselectstart=u,e.onmousemove=u,e.onloadeddata=a(this.fire,this,\"load\"),t){for(var i=e.getElementsByTagName(\"source\"),n=[],o=0;o<i.length;o++)n.push(i[o].src);this._url=0<i.length?n:[e.src]}else{d(this._url)||(this._url=[this._url]),!this.options.keepAspectRatio&&Object.prototype.hasOwnProperty.call(e.style,\"objectFit\")&&(e.style.objectFit=\"fill\"),e.autoplay=!!this.options.autoplay,e.loop=!!this.options.loop,e.muted=!!this.options.muted,e.playsInline=!!this.options.playsInline;for(var s=0;s<this._url.length;s++){var r=P(\"source\");r.src=this._url[s],e.appendChild(r)}}}});var Oi=Ei.extend({_initImage:function(){var t=this._image=this._url;M(t,\"leaflet-image-layer\"),this._zoomAnimated&&M(t,\"leaflet-zoom-animated\"),this.options.className&&M(t,this.options.className),t.onselectstart=u,t.onmousemove=u}});var Ai=o.extend({options:{interactive:!1,offset:[0,0],className:\"\",pane:void 0,content:\"\"},initialize:function(t,e){t&&(t instanceof v||d(t))?(this._latlng=w(t),c(this,e)):(c(this,t),this._source=e),this.options.content&&(this._content=this.options.content)},openOn:function(t){return(t=arguments.length?t:this._source._map).hasLayer(this)||t.addLayer(this),this},close:function(){return this._map&&this._map.removeLayer(this),this},toggle:function(t){return this._map?this.close():(arguments.length?this._source=t:t=this._source,this._prepareOpen(),this.openOn(t._map)),this},onAdd:function(t){this._zoomAnimated=t._zoomAnimated,this._container||this._initLayout(),t._fadeAnimated&&C(this._container,0),clearTimeout(this._removeTimeout),this.getPane().appendChild(this._container),this.update(),t._fadeAnimated&&C(this._container,1),this.bringToFront(),this.options.interactive&&(M(this._container,\"leaflet-interactive\"),this.addInteractiveTarget(this._container))},onRemove:function(t){t._fadeAnimated?(C(this._container,0),this._removeTimeout=setTimeout(a(T,void 0,this._container),200)):T(this._container),this.options.interactive&&(z(this._container,\"leaflet-interactive\"),this.removeInteractiveTarget(this._container))},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=w(t),this._map&&(this._updatePosition(),this._adjustPan()),this},getContent:function(){return this._content},setContent:function(t){return this._content=t,this.update(),this},getElement:function(){return this._container},update:function(){this._map&&(this._container.style.visibility=\"hidden\",this._updateContent(),this._updateLayout(),this._updatePosition(),this._container.style.visibility=\"\",this._adjustPan())},getEvents:function(){var t={zoom:this._updatePosition,viewreset:this._updatePosition};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},isOpen:function(){return!!this._map&&this._map.hasLayer(this)},bringToFront:function(){return this._map&&fe(this._container),this},bringToBack:function(){return this._map&&ge(this._container),this},_prepareOpen:function(t){if(!(i=this._source)._map)return!1;if(i instanceof ci){var e,i=null,n=this._source._layers;for(e in n)if(n[e]._map){i=n[e];break}if(!i)return!1;this._source=i}if(!t)if(i.getCenter)t=i.getCenter();else if(i.getLatLng)t=i.getLatLng();else{if(!i.getBounds)throw new Error(\"Unable to get source layer LatLng.\");t=i.getBounds().getCenter()}return this.setLatLng(t),this._map&&this.update(),!0},_updateContent:function(){if(this._content){var t=this._contentNode,e=\"function\"==typeof this._content?this._content(this._source||this):this._content;if(\"string\"==typeof e)t.innerHTML=e;else{for(;t.hasChildNodes();)t.removeChild(t.firstChild);t.appendChild(e)}this.fire(\"contentupdate\")}},_updatePosition:function(){var t,e,i;this._map&&(e=this._map.latLngToLayerPoint(this._latlng),t=m(this.options.offset),i=this._getAnchor(),this._zoomAnimated?Z(this._container,e.add(i)):t=t.add(e).add(i),e=this._containerBottom=-t.y,i=this._containerLeft=-Math.round(this._containerWidth/2)+t.x,this._container.style.bottom=e+\"px\",this._container.style.left=i+\"px\")},_getAnchor:function(){return[0,0]}}),Bi=(A.include({_initOverlay:function(t,e,i,n){var o=e;return o instanceof t||(o=new t(n).setContent(e)),i&&o.setLatLng(i),o}}),o.include({_initOverlay:function(t,e,i,n){var o=i;return o instanceof t?(c(o,n),o._source=this):(o=e&&!n?e:new t(n,this)).setContent(i),o}}),Ai.extend({options:{pane:\"popupPane\",offset:[0,7],maxWidth:300,minWidth:50,maxHeight:null,autoPan:!0,autoPanPaddingTopLeft:null,autoPanPaddingBottomRight:null,autoPanPadding:[5,5],keepInView:!1,closeButton:!0,autoClose:!0,closeOnEscapeKey:!0,className:\"\"},openOn:function(t){return!(t=arguments.length?t:this._source._map).hasLayer(this)&&t._popup&&t._popup.options.autoClose&&t.removeLayer(t._popup),t._popup=this,Ai.prototype.openOn.call(this,t)},onAdd:function(t){Ai.prototype.onAdd.call(this,t),t.fire(\"popupopen\",{popup:this}),this._source&&(this._source.fire(\"popupopen\",{popup:this},!0),this._source instanceof fi||this._source.on(\"preclick\",Ae))},onRemove:function(t){Ai.prototype.onRemove.call(this,t),t.fire(\"popupclose\",{popup:this}),this._source&&(this._source.fire(\"popupclose\",{popup:this},!0),this._source instanceof fi||this._source.off(\"preclick\",Ae))},getEvents:function(){var t=Ai.prototype.getEvents.call(this);return(void 0!==this.options.closeOnClick?this.options.closeOnClick:this._map.options.closePopupOnClick)&&(t.preclick=this.close),this.options.keepInView&&(t.moveend=this._adjustPan),t},_initLayout:function(){var t=\"leaflet-popup\",e=this._container=P(\"div\",t+\" \"+(this.options.className||\"\")+\" leaflet-zoom-animated\"),i=this._wrapper=P(\"div\",t+\"-content-wrapper\",e);this._contentNode=P(\"div\",t+\"-content\",i),Ie(e),Be(this._contentNode),S(e,\"contextmenu\",Ae),this._tipContainer=P(\"div\",t+\"-tip-container\",e),this._tip=P(\"div\",t+\"-tip\",this._tipContainer),this.options.closeButton&&((i=this._closeButton=P(\"a\",t+\"-close-button\",e)).setAttribute(\"role\",\"button\"),i.setAttribute(\"aria-label\",\"Close popup\"),i.href=\"#close\",i.innerHTML='<span aria-hidden=\"true\">&#215;</span>',S(i,\"click\",function(t){O(t),this.close()},this))},_updateLayout:function(){var t=this._contentNode,e=t.style,i=(e.width=\"\",e.whiteSpace=\"nowrap\",t.offsetWidth),i=Math.min(i,this.options.maxWidth),i=(i=Math.max(i,this.options.minWidth),e.width=i+1+\"px\",e.whiteSpace=\"\",e.height=\"\",t.offsetHeight),n=this.options.maxHeight,o=\"leaflet-popup-scrolled\";(n&&n<i?(e.height=n+\"px\",M):z)(t,o),this._containerWidth=this._container.offsetWidth},_animateZoom:function(t){var t=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center),e=this._getAnchor();Z(this._container,t.add(e))},_adjustPan:function(){var t,e,i,n,o,s,r,a;this.options.autoPan&&(this._map._panAnim&&this._map._panAnim.stop(),this._autopanning?this._autopanning=!1:(t=this._map,e=parseInt(pe(this._container,\"marginBottom\"),10)||0,e=this._container.offsetHeight+e,a=this._containerWidth,(i=new p(this._containerLeft,-e-this._containerBottom))._add(Pe(this._container)),i=t.layerPointToContainerPoint(i),o=m(this.options.autoPanPadding),n=m(this.options.autoPanPaddingTopLeft||o),o=m(this.options.autoPanPaddingBottomRight||o),s=t.getSize(),r=0,i.x+a+o.x>s.x&&(r=i.x+a-s.x+o.x),i.x-r-n.x<(a=0)&&(r=i.x-n.x),i.y+e+o.y>s.y&&(a=i.y+e-s.y+o.y),i.y-a-n.y<0&&(a=i.y-n.y),(r||a)&&(this.options.keepInView&&(this._autopanning=!0),t.fire(\"autopanstart\").panBy([r,a]))))},_getAnchor:function(){return m(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}})),Ii=(A.mergeOptions({closePopupOnClick:!0}),A.include({openPopup:function(t,e,i){return this._initOverlay(Bi,t,e,i).openOn(this),this},closePopup:function(t){return(t=arguments.length?t:this._popup)&&t.close(),this}}),o.include({bindPopup:function(t,e){return this._popup=this._initOverlay(Bi,this._popup,t,e),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t){return this._popup&&(this instanceof ci||(this._popup._source=this),this._popup._prepareOpen(t||this._latlng)&&this._popup.openOn(this._map)),this},closePopup:function(){return this._popup&&this._popup.close(),this},togglePopup:function(){return this._popup&&this._popup.toggle(this),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var e;this._popup&&this._map&&(Re(t),e=t.layer||t.target,this._popup._source!==e||e instanceof fi?(this._popup._source=e,this.openPopup(t.latlng)):this._map.hasLayer(this._popup)?this.closePopup():this.openPopup(t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}}),Ai.extend({options:{pane:\"tooltipPane\",offset:[0,0],direction:\"auto\",permanent:!1,sticky:!1,opacity:.9},onAdd:function(t){Ai.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire(\"tooltipopen\",{tooltip:this}),this._source&&(this.addEventParent(this._source),this._source.fire(\"tooltipopen\",{tooltip:this},!0))},onRemove:function(t){Ai.prototype.onRemove.call(this,t),t.fire(\"tooltipclose\",{tooltip:this}),this._source&&(this.removeEventParent(this._source),this._source.fire(\"tooltipclose\",{tooltip:this},!0))},getEvents:function(){var t=Ai.prototype.getEvents.call(this);return this.options.permanent||(t.preclick=this.close),t},_initLayout:function(){var t=\"leaflet-tooltip \"+(this.options.className||\"\")+\" leaflet-zoom-\"+(this._zoomAnimated?\"animated\":\"hide\");this._contentNode=this._container=P(\"div\",t),this._container.setAttribute(\"role\",\"tooltip\"),this._container.setAttribute(\"id\",\"leaflet-tooltip-\"+h(this))},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var e,i=this._map,n=this._container,o=i.latLngToContainerPoint(i.getCenter()),i=i.layerPointToContainerPoint(t),s=this.options.direction,r=n.offsetWidth,a=n.offsetHeight,h=m(this.options.offset),l=this._getAnchor(),i=\"top\"===s?(e=r/2,a):\"bottom\"===s?(e=r/2,0):(e=\"center\"===s?r/2:\"right\"===s?0:\"left\"===s?r:i.x<o.x?(s=\"right\",0):(s=\"left\",r+2*(h.x+l.x)),a/2);t=t.subtract(m(e,i,!0)).add(h).add(l),z(n,\"leaflet-tooltip-right\"),z(n,\"leaflet-tooltip-left\"),z(n,\"leaflet-tooltip-top\"),z(n,\"leaflet-tooltip-bottom\"),M(n,\"leaflet-tooltip-\"+s),Z(n,t)},_updatePosition:function(){var t=this._map.latLngToLayerPoint(this._latlng);this._setPosition(t)},setOpacity:function(t){this.options.opacity=t,this._container&&C(this._container,t)},_animateZoom:function(t){t=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);this._setPosition(t)},_getAnchor:function(){return m(this._source&&this._source._getTooltipAnchor&&!this.options.sticky?this._source._getTooltipAnchor():[0,0])}})),Ri=(A.include({openTooltip:function(t,e,i){return this._initOverlay(Ii,t,e,i).openOn(this),this},closeTooltip:function(t){return t.close(),this}}),o.include({bindTooltip:function(t,e){return this._tooltip&&this.isTooltipOpen()&&this.unbindTooltip(),this._tooltip=this._initOverlay(Ii,this._tooltip,t,e),this._initTooltipInteractions(),this._tooltip.options.permanent&&this._map&&this._map.hasLayer(this)&&this.openTooltip(),this},unbindTooltip:function(){return this._tooltip&&(this._initTooltipInteractions(!0),this.closeTooltip(),this._tooltip=null),this},_initTooltipInteractions:function(t){var e,i;!t&&this._tooltipHandlersAdded||(e=t?\"off\":\"on\",i={remove:this.closeTooltip,move:this._moveTooltip},this._tooltip.options.permanent?i.add=this._openTooltip:(i.mouseover=this._openTooltip,i.mouseout=this.closeTooltip,i.click=this._openTooltip,this._map?this._addFocusListeners():i.add=this._addFocusListeners),this._tooltip.options.sticky&&(i.mousemove=this._moveTooltip),this[e](i),this._tooltipHandlersAdded=!t)},openTooltip:function(t){return this._tooltip&&(this instanceof ci||(this._tooltip._source=this),this._tooltip._prepareOpen(t)&&(this._tooltip.openOn(this._map),this.getElement?this._setAriaDescribedByOnLayer(this):this.eachLayer&&this.eachLayer(this._setAriaDescribedByOnLayer,this))),this},closeTooltip:function(){if(this._tooltip)return this._tooltip.close()},toggleTooltip:function(){return this._tooltip&&this._tooltip.toggle(this),this},isTooltipOpen:function(){return this._tooltip.isOpen()},setTooltipContent:function(t){return this._tooltip&&this._tooltip.setContent(t),this},getTooltip:function(){return this._tooltip},_addFocusListeners:function(){this.getElement?this._addFocusListenersOnLayer(this):this.eachLayer&&this.eachLayer(this._addFocusListenersOnLayer,this)},_addFocusListenersOnLayer:function(t){var e=\"function\"==typeof t.getElement&&t.getElement();e&&(S(e,\"focus\",function(){this._tooltip._source=t,this.openTooltip()},this),S(e,\"blur\",this.closeTooltip,this))},_setAriaDescribedByOnLayer:function(t){t=\"function\"==typeof t.getElement&&t.getElement();t&&t.setAttribute(\"aria-describedby\",this._tooltip._container.id)},_openTooltip:function(t){var e;this._tooltip&&this._map&&(this._map.dragging&&this._map.dragging.moving()&&!this._openOnceFlag?(this._openOnceFlag=!0,(e=this)._map.once(\"moveend\",function(){e._openOnceFlag=!1,e._openTooltip(t)})):(this._tooltip._source=t.layer||t.target,this.openTooltip(this._tooltip.options.sticky?t.latlng:void 0)))},_moveTooltip:function(t){var e=t.latlng;this._tooltip.options.sticky&&t.originalEvent&&(t=this._map.mouseEventToContainerPoint(t.originalEvent),t=this._map.containerPointToLayerPoint(t),e=this._map.layerPointToLatLng(t)),this._tooltip.setLatLng(e)}}),di.extend({options:{iconSize:[12,12],html:!1,bgPos:null,className:\"leaflet-div-icon\"},createIcon:function(t){var t=t&&\"DIV\"===t.tagName?t:document.createElement(\"div\"),e=this.options;return e.html instanceof Element?(me(t),t.appendChild(e.html)):t.innerHTML=!1!==e.html?e.html:\"\",e.bgPos&&(e=m(e.bgPos),t.style.backgroundPosition=-e.x+\"px \"+-e.y+\"px\"),this._setIconStyles(t,\"icon\"),t},createShadow:function(){return null}}));di.Default=_i;var Ni=o.extend({options:{tileSize:256,opacity:1,updateWhenIdle:b.mobile,updateWhenZooming:!0,updateInterval:200,zIndex:1,bounds:null,minZoom:0,maxZoom:void 0,maxNativeZoom:void 0,minNativeZoom:void 0,noWrap:!1,pane:\"tilePane\",className:\"\",keepBuffer:2},initialize:function(t){c(this,t)},onAdd:function(){this._initContainer(),this._levels={},this._tiles={},this._resetView()},beforeAdd:function(t){t._addZoomLimit(this)},onRemove:function(t){this._removeAllTiles(),T(this._container),t._removeZoomLimit(this),this._container=null,this._tileZoom=void 0},bringToFront:function(){return this._map&&(fe(this._container),this._setAutoZIndex(Math.max)),this},bringToBack:function(){return this._map&&(ge(this._container),this._setAutoZIndex(Math.min)),this},getContainer:function(){return this._container},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},isLoading:function(){return this._loading},redraw:function(){var t;return this._map&&(this._removeAllTiles(),(t=this._clampZoom(this._map.getZoom()))!==this._tileZoom&&(this._tileZoom=t,this._updateLevels()),this._update()),this},getEvents:function(){var t={viewprereset:this._invalidateAll,viewreset:this._resetView,zoom:this._resetView,moveend:this._onMoveEnd};return this.options.updateWhenIdle||(this._onMove||(this._onMove=j(this._onMoveEnd,this.options.updateInterval,this)),t.move=this._onMove),this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},createTile:function(){return document.createElement(\"div\")},getTileSize:function(){var t=this.options.tileSize;return t instanceof p?t:new p(t,t)},_updateZIndex:function(){this._container&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t){for(var e,i=this.getPane().children,n=-t(-1/0,1/0),o=0,s=i.length;o<s;o++)e=i[o].style.zIndex,i[o]!==this._container&&e&&(n=t(n,+e));isFinite(n)&&(this.options.zIndex=n+t(-1,1),this._updateZIndex())},_updateOpacity:function(){if(this._map&&!b.ielt9){C(this._container,this.options.opacity);var t,e=+new Date,i=!1,n=!1;for(t in this._tiles){var o,s=this._tiles[t];s.current&&s.loaded&&(o=Math.min(1,(e-s.loaded)/200),C(s.el,o),o<1?i=!0:(s.active?n=!0:this._onOpaqueTile(s),s.active=!0))}n&&!this._noPrune&&this._pruneTiles(),i&&(r(this._fadeFrame),this._fadeFrame=x(this._updateOpacity,this))}},_onOpaqueTile:u,_initContainer:function(){this._container||(this._container=P(\"div\",\"leaflet-layer \"+(this.options.className||\"\")),this._updateZIndex(),this.options.opacity<1&&this._updateOpacity(),this.getPane().appendChild(this._container))},_updateLevels:function(){var t=this._tileZoom,e=this.options.maxZoom;if(void 0!==t){for(var i in this._levels)i=Number(i),this._levels[i].el.children.length||i===t?(this._levels[i].el.style.zIndex=e-Math.abs(t-i),this._onUpdateLevel(i)):(T(this._levels[i].el),this._removeTilesAtZoom(i),this._onRemoveLevel(i),delete this._levels[i]);var n=this._levels[t],o=this._map;return n||((n=this._levels[t]={}).el=P(\"div\",\"leaflet-tile-container leaflet-zoom-animated\",this._container),n.el.style.zIndex=e,n.origin=o.project(o.unproject(o.getPixelOrigin()),t).round(),n.zoom=t,this._setZoomTransform(n,o.getCenter(),o.getZoom()),u(n.el.offsetWidth),this._onCreateLevel(n)),this._level=n}},_onUpdateLevel:u,_onRemoveLevel:u,_onCreateLevel:u,_pruneTiles:function(){if(this._map){var t,e,i,n=this._map.getZoom();if(n>this.options.maxZoom||n<this.options.minZoom)this._removeAllTiles();else{for(t in this._tiles)(i=this._tiles[t]).retain=i.current;for(t in this._tiles)(i=this._tiles[t]).current&&!i.active&&(e=i.coords,this._retainParent(e.x,e.y,e.z,e.z-5)||this._retainChildren(e.x,e.y,e.z,e.z+2));for(t in this._tiles)this._tiles[t].retain||this._removeTile(t)}}},_removeTilesAtZoom:function(t){for(var e in this._tiles)this._tiles[e].coords.z===t&&this._removeTile(e)},_removeAllTiles:function(){for(var t in this._tiles)this._removeTile(t)},_invalidateAll:function(){for(var t in this._levels)T(this._levels[t].el),this._onRemoveLevel(Number(t)),delete this._levels[t];this._removeAllTiles(),this._tileZoom=void 0},_retainParent:function(t,e,i,n){var t=Math.floor(t/2),e=Math.floor(e/2),i=i-1,o=new p(+t,+e),o=(o.z=i,this._tileCoordsToKey(o)),o=this._tiles[o];return o&&o.active?o.retain=!0:(o&&o.loaded&&(o.retain=!0),n<i&&this._retainParent(t,e,i,n))},_retainChildren:function(t,e,i,n){for(var o=2*t;o<2*t+2;o++)for(var s=2*e;s<2*e+2;s++){var r=new p(o,s),r=(r.z=i+1,this._tileCoordsToKey(r)),r=this._tiles[r];r&&r.active?r.retain=!0:(r&&r.loaded&&(r.retain=!0),i+1<n&&this._retainChildren(o,s,i+1,n))}},_resetView:function(t){t=t&&(t.pinch||t.flyTo);this._setView(this._map.getCenter(),this._map.getZoom(),t,t)},_animateZoom:function(t){this._setView(t.center,t.zoom,!0,t.noUpdate)},_clampZoom:function(t){var e=this.options;return void 0!==e.minNativeZoom&&t<e.minNativeZoom?e.minNativeZoom:void 0!==e.maxNativeZoom&&e.maxNativeZoom<t?e.maxNativeZoom:t},_setView:function(t,e,i,n){var o=Math.round(e),o=void 0!==this.options.maxZoom&&o>this.options.maxZoom||void 0!==this.options.minZoom&&o<this.options.minZoom?void 0:this._clampZoom(o),s=this.options.updateWhenZooming&&o!==this._tileZoom;n&&!s||(this._tileZoom=o,this._abortLoading&&this._abortLoading(),this._updateLevels(),this._resetGrid(),void 0!==o&&this._update(t),i||this._pruneTiles(),this._noPrune=!!i),this._setZoomTransforms(t,e)},_setZoomTransforms:function(t,e){for(var i in this._levels)this._setZoomTransform(this._levels[i],t,e)},_setZoomTransform:function(t,e,i){var n=this._map.getZoomScale(i,t.zoom),e=t.origin.multiplyBy(n).subtract(this._map._getNewPixelOrigin(e,i)).round();b.any3d?be(t.el,e,n):Z(t.el,e)},_resetGrid:function(){var t=this._map,e=t.options.crs,i=this._tileSize=this.getTileSize(),n=this._tileZoom,o=this._map.getPixelWorldBounds(this._tileZoom);o&&(this._globalTileRange=this._pxBoundsToTileRange(o)),this._wrapX=e.wrapLng&&!this.options.noWrap&&[Math.floor(t.project([0,e.wrapLng[0]],n).x/i.x),Math.ceil(t.project([0,e.wrapLng[1]],n).x/i.y)],this._wrapY=e.wrapLat&&!this.options.noWrap&&[Math.floor(t.project([e.wrapLat[0],0],n).y/i.x),Math.ceil(t.project([e.wrapLat[1],0],n).y/i.y)]},_onMoveEnd:function(){this._map&&!this._map._animatingZoom&&this._update()},_getTiledPixelBounds:function(t){var e=this._map,i=e._animatingZoom?Math.max(e._animateToZoom,e.getZoom()):e.getZoom(),i=e.getZoomScale(i,this._tileZoom),t=e.project(t,this._tileZoom).floor(),e=e.getSize().divideBy(2*i);return new f(t.subtract(e),t.add(e))},_update:function(t){var e=this._map;if(e){var i=this._clampZoom(e.getZoom());if(void 0===t&&(t=e.getCenter()),void 0!==this._tileZoom){var n,e=this._getTiledPixelBounds(t),o=this._pxBoundsToTileRange(e),s=o.getCenter(),r=[],e=this.options.keepBuffer,a=new f(o.getBottomLeft().subtract([e,-e]),o.getTopRight().add([e,-e]));if(!(isFinite(o.min.x)&&isFinite(o.min.y)&&isFinite(o.max.x)&&isFinite(o.max.y)))throw new Error(\"Attempted to load an infinite number of tiles\");for(n in this._tiles){var h=this._tiles[n].coords;h.z===this._tileZoom&&a.contains(new p(h.x,h.y))||(this._tiles[n].current=!1)}if(1<Math.abs(i-this._tileZoom))this._setView(t,i);else{for(var l=o.min.y;l<=o.max.y;l++)for(var u=o.min.x;u<=o.max.x;u++){var c,d=new p(u,l);d.z=this._tileZoom,this._isValidTile(d)&&((c=this._tiles[this._tileCoordsToKey(d)])?c.current=!0:r.push(d))}if(r.sort(function(t,e){return t.distanceTo(s)-e.distanceTo(s)}),0!==r.length){this._loading||(this._loading=!0,this.fire(\"loading\"));for(var _=document.createDocumentFragment(),u=0;u<r.length;u++)this._addTile(r[u],_);this._level.el.appendChild(_)}}}}},_isValidTile:function(t){var e=this._map.options.crs;if(!e.infinite){var i=this._globalTileRange;if(!e.wrapLng&&(t.x<i.min.x||t.x>i.max.x)||!e.wrapLat&&(t.y<i.min.y||t.y>i.max.y))return!1}return!this.options.bounds||(e=this._tileCoordsToBounds(t),g(this.options.bounds).overlaps(e))},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var e=this._map,i=this.getTileSize(),n=t.scaleBy(i),i=n.add(i);return[e.unproject(n,t.z),e.unproject(i,t.z)]},_tileCoordsToBounds:function(t){t=this._tileCoordsToNwSe(t),t=new s(t[0],t[1]);return t=this.options.noWrap?t:this._map.wrapLatLngBounds(t)},_tileCoordsToKey:function(t){return t.x+\":\"+t.y+\":\"+t.z},_keyToTileCoords:function(t){var t=t.split(\":\"),e=new p(+t[0],+t[1]);return e.z=+t[2],e},_removeTile:function(t){var e=this._tiles[t];e&&(T(e.el),delete this._tiles[t],this.fire(\"tileunload\",{tile:e.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){M(t,\"leaflet-tile\");var e=this.getTileSize();t.style.width=e.x+\"px\",t.style.height=e.y+\"px\",t.onselectstart=u,t.onmousemove=u,b.ielt9&&this.options.opacity<1&&C(t,this.options.opacity)},_addTile:function(t,e){var i=this._getTilePos(t),n=this._tileCoordsToKey(t),o=this.createTile(this._wrapCoords(t),a(this._tileReady,this,t));this._initTile(o),this.createTile.length<2&&x(a(this._tileReady,this,t,null,o)),Z(o,i),this._tiles[n]={el:o,coords:t,current:!0},e.appendChild(o),this.fire(\"tileloadstart\",{tile:o,coords:t})},_tileReady:function(t,e,i){e&&this.fire(\"tileerror\",{error:e,tile:i,coords:t});var n=this._tileCoordsToKey(t);(i=this._tiles[n])&&(i.loaded=+new Date,this._map._fadeAnimated?(C(i.el,0),r(this._fadeFrame),this._fadeFrame=x(this._updateOpacity,this)):(i.active=!0,this._pruneTiles()),e||(M(i.el,\"leaflet-tile-loaded\"),this.fire(\"tileload\",{tile:i.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire(\"load\"),b.ielt9||!this._map._fadeAnimated?x(this._pruneTiles,this):setTimeout(a(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var e=new p(this._wrapX?H(t.x,this._wrapX):t.x,this._wrapY?H(t.y,this._wrapY):t.y);return e.z=t.z,e},_pxBoundsToTileRange:function(t){var e=this.getTileSize();return new f(t.min.unscaleBy(e).floor(),t.max.unscaleBy(e).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}});var Di=Ni.extend({options:{minZoom:0,maxZoom:18,subdomains:\"abc\",errorTileUrl:\"\",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1,referrerPolicy:!1},initialize:function(t,e){this._url=t,(e=c(this,e)).detectRetina&&b.retina&&0<e.maxZoom?(e.tileSize=Math.floor(e.tileSize/2),e.zoomReverse?(e.zoomOffset--,e.minZoom=Math.min(e.maxZoom,e.minZoom+1)):(e.zoomOffset++,e.maxZoom=Math.max(e.minZoom,e.maxZoom-1)),e.minZoom=Math.max(0,e.minZoom)):e.zoomReverse?e.minZoom=Math.min(e.maxZoom,e.minZoom):e.maxZoom=Math.max(e.minZoom,e.maxZoom),\"string\"==typeof e.subdomains&&(e.subdomains=e.subdomains.split(\"\")),this.on(\"tileunload\",this._onTileRemove)},setUrl:function(t,e){return this._url===t&&void 0===e&&(e=!0),this._url=t,e||this.redraw(),this},createTile:function(t,e){var i=document.createElement(\"img\");return S(i,\"load\",a(this._tileOnLoad,this,e,i)),S(i,\"error\",a(this._tileOnError,this,e,i)),!this.options.crossOrigin&&\"\"!==this.options.crossOrigin||(i.crossOrigin=!0===this.options.crossOrigin?\"\":this.options.crossOrigin),\"string\"==typeof this.options.referrerPolicy&&(i.referrerPolicy=this.options.referrerPolicy),i.alt=\"\",i.src=this.getTileUrl(t),i},getTileUrl:function(t){var e={r:b.retina?\"@2x\":\"\",s:this._getSubdomain(t),x:t.x,y:t.y,z:this._getZoomForUrl()};return this._map&&!this._map.options.crs.infinite&&(t=this._globalTileRange.max.y-t.y,this.options.tms&&(e.y=t),e[\"-y\"]=t),q(this._url,l(e,this.options))},_tileOnLoad:function(t,e){b.ielt9?setTimeout(a(t,this,null,e),0):t(null,e)},_tileOnError:function(t,e,i){var n=this.options.errorTileUrl;n&&e.getAttribute(\"src\")!==n&&(e.src=n),t(i,e)},_onTileRemove:function(t){t.tile.onload=null},_getZoomForUrl:function(){var t=this._tileZoom,e=this.options.maxZoom;return(t=this.options.zoomReverse?e-t:t)+this.options.zoomOffset},_getSubdomain:function(t){t=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[t]},_abortLoading:function(){var t,e,i;for(t in this._tiles)this._tiles[t].coords.z!==this._tileZoom&&((i=this._tiles[t].el).onload=u,i.onerror=u,i.complete||(i.src=K,e=this._tiles[t].coords,T(i),delete this._tiles[t],this.fire(\"tileabort\",{tile:i,coords:e})))},_removeTile:function(t){var e=this._tiles[t];if(e)return e.el.setAttribute(\"src\",K),Ni.prototype._removeTile.call(this,t)},_tileReady:function(t,e,i){if(this._map&&(!i||i.getAttribute(\"src\")!==K))return Ni.prototype._tileReady.call(this,t,e,i)}});function ji(t,e){return new Di(t,e)}var Hi=Di.extend({defaultWmsParams:{service:\"WMS\",request:\"GetMap\",layers:\"\",styles:\"\",format:\"image/jpeg\",transparent:!1,version:\"1.1.1\"},options:{crs:null,uppercase:!1},initialize:function(t,e){this._url=t;var i,n=l({},this.defaultWmsParams);for(i in e)i in this.options||(n[i]=e[i]);var t=(e=c(this,e)).detectRetina&&b.retina?2:1,o=this.getTileSize();n.width=o.x*t,n.height=o.y*t,this.wmsParams=n},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var e=1.3<=this._wmsVersion?\"crs\":\"srs\";this.wmsParams[e]=this._crs.code,Di.prototype.onAdd.call(this,t)},getTileUrl:function(t){var e=this._tileCoordsToNwSe(t),i=this._crs,i=_(i.project(e[0]),i.project(e[1])),e=i.min,i=i.max,e=(1.3<=this._wmsVersion&&this._crs===li?[e.y,e.x,i.y,i.x]:[e.x,e.y,i.x,i.y]).join(\",\"),i=Di.prototype.getTileUrl.call(this,t);return i+U(this.wmsParams,i,this.options.uppercase)+(this.options.uppercase?\"&BBOX=\":\"&bbox=\")+e},setParams:function(t,e){return l(this.wmsParams,t),e||this.redraw(),this}});Di.WMS=Hi,ji.wms=function(t,e){return new Hi(t,e)};var Wi=o.extend({options:{padding:.1},initialize:function(t){c(this,t),h(this),this._layers=this._layers||{}},onAdd:function(){this._container||(this._initContainer(),M(this._container,\"leaflet-zoom-animated\")),this.getPane().appendChild(this._container),this._update(),this.on(\"update\",this._updatePaths,this)},onRemove:function(){this.off(\"update\",this._updatePaths,this),this._destroyContainer()},getEvents:function(){var t={viewreset:this._reset,zoom:this._onZoom,moveend:this._update,zoomend:this._onZoomEnd};return this._zoomAnimated&&(t.zoomanim=this._onAnimZoom),t},_onAnimZoom:function(t){this._updateTransform(t.center,t.zoom)},_onZoom:function(){this._updateTransform(this._map.getCenter(),this._map.getZoom())},_updateTransform:function(t,e){var i=this._map.getZoomScale(e,this._zoom),n=this._map.getSize().multiplyBy(.5+this.options.padding),o=this._map.project(this._center,e),n=n.multiplyBy(-i).add(o).subtract(this._map._getNewPixelOrigin(t,e));b.any3d?be(this._container,n,i):Z(this._container,n)},_reset:function(){for(var t in this._update(),this._updateTransform(this._center,this._zoom),this._layers)this._layers[t]._reset()},_onZoomEnd:function(){for(var t in this._layers)this._layers[t]._project()},_updatePaths:function(){for(var t in this._layers)this._layers[t]._update()},_update:function(){var t=this.options.padding,e=this._map.getSize(),i=this._map.containerPointToLayerPoint(e.multiplyBy(-t)).round();this._bounds=new f(i,i.add(e.multiplyBy(1+2*t)).round()),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}}),Fi=Wi.extend({options:{tolerance:0},getEvents:function(){var t=Wi.prototype.getEvents.call(this);return t.viewprereset=this._onViewPreReset,t},_onViewPreReset:function(){this._postponeUpdatePaths=!0},onAdd:function(){Wi.prototype.onAdd.call(this),this._draw()},_initContainer:function(){var t=this._container=document.createElement(\"canvas\");S(t,\"mousemove\",this._onMouseMove,this),S(t,\"click dblclick mousedown mouseup contextmenu\",this._onClick,this),S(t,\"mouseout\",this._handleMouseOut,this),t._leaflet_disable_events=!0,this._ctx=t.getContext(\"2d\")},_destroyContainer:function(){r(this._redrawRequest),delete this._ctx,T(this._container),k(this._container),delete this._container},_updatePaths:function(){if(!this._postponeUpdatePaths){for(var t in this._redrawBounds=null,this._layers)this._layers[t]._update();this._redraw()}},_update:function(){var t,e,i,n;this._map._animatingZoom&&this._bounds||(Wi.prototype._update.call(this),t=this._bounds,e=this._container,i=t.getSize(),n=b.retina?2:1,Z(e,t.min),e.width=n*i.x,e.height=n*i.y,e.style.width=i.x+\"px\",e.style.height=i.y+\"px\",b.retina&&this._ctx.scale(2,2),this._ctx.translate(-t.min.x,-t.min.y),this.fire(\"update\"))},_reset:function(){Wi.prototype._reset.call(this),this._postponeUpdatePaths&&(this._postponeUpdatePaths=!1,this._updatePaths())},_initPath:function(t){this._updateDashArray(t);t=(this._layers[h(t)]=t)._order={layer:t,prev:this._drawLast,next:null};this._drawLast&&(this._drawLast.next=t),this._drawLast=t,this._drawFirst=this._drawFirst||this._drawLast},_addPath:function(t){this._requestRedraw(t)},_removePath:function(t){var e=t._order,i=e.next,e=e.prev;i?i.prev=e:this._drawLast=e,e?e.next=i:this._drawFirst=i,delete t._order,delete this._layers[h(t)],this._requestRedraw(t)},_updatePath:function(t){this._extendRedrawBounds(t),t._project(),t._update(),this._requestRedraw(t)},_updateStyle:function(t){this._updateDashArray(t),this._requestRedraw(t)},_updateDashArray:function(t){if(\"string\"==typeof t.options.dashArray){for(var e,i=t.options.dashArray.split(/[, ]+/),n=[],o=0;o<i.length;o++){if(e=Number(i[o]),isNaN(e))return;n.push(e)}t.options._dashArray=n}else t.options._dashArray=t.options.dashArray},_requestRedraw:function(t){this._map&&(this._extendRedrawBounds(t),this._redrawRequest=this._redrawRequest||x(this._redraw,this))},_extendRedrawBounds:function(t){var e;t._pxBounds&&(e=(t.options.weight||0)+1,this._redrawBounds=this._redrawBounds||new f,this._redrawBounds.extend(t._pxBounds.min.subtract([e,e])),this._redrawBounds.extend(t._pxBounds.max.add([e,e])))},_redraw:function(){this._redrawRequest=null,this._redrawBounds&&(this._redrawBounds.min._floor(),this._redrawBounds.max._ceil()),this._clear(),this._draw(),this._redrawBounds=null},_clear:function(){var t,e=this._redrawBounds;e?(t=e.getSize(),this._ctx.clearRect(e.min.x,e.min.y,t.x,t.y)):(this._ctx.save(),this._ctx.setTransform(1,0,0,1,0,0),this._ctx.clearRect(0,0,this._container.width,this._container.height),this._ctx.restore())},_draw:function(){var t,e,i=this._redrawBounds;this._ctx.save(),i&&(e=i.getSize(),this._ctx.beginPath(),this._ctx.rect(i.min.x,i.min.y,e.x,e.y),this._ctx.clip()),this._drawing=!0;for(var n=this._drawFirst;n;n=n.next)t=n.layer,(!i||t._pxBounds&&t._pxBounds.intersects(i))&&t._updatePath();this._drawing=!1,this._ctx.restore()},_updatePoly:function(t,e){if(this._drawing){var i,n,o,s,r=t._parts,a=r.length,h=this._ctx;if(a){for(h.beginPath(),i=0;i<a;i++){for(n=0,o=r[i].length;n<o;n++)s=r[i][n],h[n?\"lineTo\":\"moveTo\"](s.x,s.y);e&&h.closePath()}this._fillStroke(h,t)}}},_updateCircle:function(t){var e,i,n,o;this._drawing&&!t._empty()&&(e=t._point,i=this._ctx,n=Math.max(Math.round(t._radius),1),1!=(o=(Math.max(Math.round(t._radiusY),1)||n)/n)&&(i.save(),i.scale(1,o)),i.beginPath(),i.arc(e.x,e.y/o,n,0,2*Math.PI,!1),1!=o&&i.restore(),this._fillStroke(i,t))},_fillStroke:function(t,e){var i=e.options;i.fill&&(t.globalAlpha=i.fillOpacity,t.fillStyle=i.fillColor||i.color,t.fill(i.fillRule||\"evenodd\")),i.stroke&&0!==i.weight&&(t.setLineDash&&t.setLineDash(e.options&&e.options._dashArray||[]),t.globalAlpha=i.opacity,t.lineWidth=i.weight,t.strokeStyle=i.color,t.lineCap=i.lineCap,t.lineJoin=i.lineJoin,t.stroke())},_onClick:function(t){for(var e,i,n=this._map.mouseEventToLayerPoint(t),o=this._drawFirst;o;o=o.next)(e=o.layer).options.interactive&&e._containsPoint(n)&&((\"click\"===t.type||\"preclick\"===t.type)&&this._map._draggableMoved(e)||(i=e));this._fireEvent(!!i&&[i],t)},_onMouseMove:function(t){var e;!this._map||this._map.dragging.moving()||this._map._animatingZoom||(e=this._map.mouseEventToLayerPoint(t),this._handleMouseHover(t,e))},_handleMouseOut:function(t){var e=this._hoveredLayer;e&&(z(this._container,\"leaflet-interactive\"),this._fireEvent([e],t,\"mouseout\"),this._hoveredLayer=null,this._mouseHoverThrottled=!1)},_handleMouseHover:function(t,e){if(!this._mouseHoverThrottled){for(var i,n,o=this._drawFirst;o;o=o.next)(i=o.layer).options.interactive&&i._containsPoint(e)&&(n=i);n!==this._hoveredLayer&&(this._handleMouseOut(t),n&&(M(this._container,\"leaflet-interactive\"),this._fireEvent([n],t,\"mouseover\"),this._hoveredLayer=n)),this._fireEvent(!!this._hoveredLayer&&[this._hoveredLayer],t),this._mouseHoverThrottled=!0,setTimeout(a(function(){this._mouseHoverThrottled=!1},this),32)}},_fireEvent:function(t,e,i){this._map._fireDOMEvent(e,i||e.type,t)},_bringToFront:function(t){var e,i,n=t._order;n&&(e=n.next,i=n.prev,e&&((e.prev=i)?i.next=e:e&&(this._drawFirst=e),n.prev=this._drawLast,(this._drawLast.next=n).next=null,this._drawLast=n,this._requestRedraw(t)))},_bringToBack:function(t){var e,i,n=t._order;n&&(e=n.next,(i=n.prev)&&((i.next=e)?e.prev=i:i&&(this._drawLast=i),n.prev=null,n.next=this._drawFirst,this._drawFirst.prev=n,this._drawFirst=n,this._requestRedraw(t)))}});function Ui(t){return b.canvas?new Fi(t):null}var Vi=function(){try{return document.namespaces.add(\"lvml\",\"urn:schemas-microsoft-com:vml\"),function(t){return document.createElement(\"<lvml:\"+t+' class=\"lvml\">')}}catch(t){}return function(t){return document.createElement(\"<\"+t+' xmlns=\"urn:schemas-microsoft.com:vml\" class=\"lvml\">')}}(),zt={_initContainer:function(){this._container=P(\"div\",\"leaflet-vml-container\")},_update:function(){this._map._animatingZoom||(Wi.prototype._update.call(this),this.fire(\"update\"))},_initPath:function(t){var e=t._container=Vi(\"shape\");M(e,\"leaflet-vml-shape \"+(this.options.className||\"\")),e.coordsize=\"1 1\",t._path=Vi(\"path\"),e.appendChild(t._path),this._updateStyle(t),this._layers[h(t)]=t},_addPath:function(t){var e=t._container;this._container.appendChild(e),t.options.interactive&&t.addInteractiveTarget(e)},_removePath:function(t){var e=t._container;T(e),t.removeInteractiveTarget(e),delete this._layers[h(t)]},_updateStyle:function(t){var e=t._stroke,i=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(e=e||(t._stroke=Vi(\"stroke\")),o.appendChild(e),e.weight=n.weight+\"px\",e.color=n.color,e.opacity=n.opacity,n.dashArray?e.dashStyle=d(n.dashArray)?n.dashArray.join(\" \"):n.dashArray.replace(/( *, *)/g,\" \"):e.dashStyle=\"\",e.endcap=n.lineCap.replace(\"butt\",\"flat\"),e.joinstyle=n.lineJoin):e&&(o.removeChild(e),t._stroke=null),n.fill?(i=i||(t._fill=Vi(\"fill\")),o.appendChild(i),i.color=n.fillColor||n.color,i.opacity=n.fillOpacity):i&&(o.removeChild(i),t._fill=null)},_updateCircle:function(t){var e=t._point.round(),i=Math.round(t._radius),n=Math.round(t._radiusY||i);this._setPath(t,t._empty()?\"M0 0\":\"AL \"+e.x+\",\"+e.y+\" \"+i+\",\"+n+\" 0,23592600\")},_setPath:function(t,e){t._path.v=e},_bringToFront:function(t){fe(t._container)},_bringToBack:function(t){ge(t._container)}},qi=b.vml?Vi:ct,Gi=Wi.extend({_initContainer:function(){this._container=qi(\"svg\"),this._container.setAttribute(\"pointer-events\",\"none\"),this._rootGroup=qi(\"g\"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){T(this._container),k(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_update:function(){var t,e,i;this._map._animatingZoom&&this._bounds||(Wi.prototype._update.call(this),e=(t=this._bounds).getSize(),i=this._container,this._svgSize&&this._svgSize.equals(e)||(this._svgSize=e,i.setAttribute(\"width\",e.x),i.setAttribute(\"height\",e.y)),Z(i,t.min),i.setAttribute(\"viewBox\",[t.min.x,t.min.y,e.x,e.y].join(\" \")),this.fire(\"update\"))},_initPath:function(t){var e=t._path=qi(\"path\");t.options.className&&M(e,t.options.className),t.options.interactive&&M(e,\"leaflet-interactive\"),this._updateStyle(t),this._layers[h(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){T(t._path),t.removeInteractiveTarget(t._path),delete this._layers[h(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var e=t._path,t=t.options;e&&(t.stroke?(e.setAttribute(\"stroke\",t.color),e.setAttribute(\"stroke-opacity\",t.opacity),e.setAttribute(\"stroke-width\",t.weight),e.setAttribute(\"stroke-linecap\",t.lineCap),e.setAttribute(\"stroke-linejoin\",t.lineJoin),t.dashArray?e.setAttribute(\"stroke-dasharray\",t.dashArray):e.removeAttribute(\"stroke-dasharray\"),t.dashOffset?e.setAttribute(\"stroke-dashoffset\",t.dashOffset):e.removeAttribute(\"stroke-dashoffset\")):e.setAttribute(\"stroke\",\"none\"),t.fill?(e.setAttribute(\"fill\",t.fillColor||t.color),e.setAttribute(\"fill-opacity\",t.fillOpacity),e.setAttribute(\"fill-rule\",t.fillRule||\"evenodd\")):e.setAttribute(\"fill\",\"none\"))},_updatePoly:function(t,e){this._setPath(t,dt(t._parts,e))},_updateCircle:function(t){var e=t._point,i=Math.max(Math.round(t._radius),1),n=\"a\"+i+\",\"+(Math.max(Math.round(t._radiusY),1)||i)+\" 0 1,0 \",e=t._empty()?\"M0 0\":\"M\"+(e.x-i)+\",\"+e.y+n+2*i+\",0 \"+n+2*-i+\",0 \";this._setPath(t,e)},_setPath:function(t,e){t._path.setAttribute(\"d\",e)},_bringToFront:function(t){fe(t._path)},_bringToBack:function(t){ge(t._path)}});function Ki(t){return b.svg||b.vml?new Gi(t):null}b.vml&&Gi.include(zt),A.include({getRenderer:function(t){t=(t=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer)||(this._renderer=this._createRenderer());return this.hasLayer(t)||this.addLayer(t),t},_getPaneRenderer:function(t){var e;return\"overlayPane\"!==t&&void 0!==t&&(void 0===(e=this._paneRenderers[t])&&(e=this._createRenderer({pane:t}),this._paneRenderers[t]=e),e)},_createRenderer:function(t){return this.options.preferCanvas&&Ui(t)||Ki(t)}});var Yi=xi.extend({initialize:function(t,e){xi.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return[(t=g(t)).getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});Gi.create=qi,Gi.pointsToPath=dt,wi.geometryToLayer=bi,wi.coordsToLatLng=Li,wi.coordsToLatLngs=Ti,wi.latLngToCoords=Mi,wi.latLngsToCoords=zi,wi.getFeature=Ci,wi.asFeature=Zi,A.mergeOptions({boxZoom:!0});var _t=n.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on(\"unload\",this._destroy,this)},addHooks:function(){S(this._container,\"mousedown\",this._onMouseDown,this)},removeHooks:function(){k(this._container,\"mousedown\",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){T(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),re(),Le(),this._startPoint=this._map.mouseEventToContainerPoint(t),S(document,{contextmenu:Re,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=P(\"div\",\"leaflet-zoom-box\",this._container),M(this._container,\"leaflet-crosshair\"),this._map.fire(\"boxzoomstart\")),this._point=this._map.mouseEventToContainerPoint(t);var t=new f(this._point,this._startPoint),e=t.getSize();Z(this._box,t.min),this._box.style.width=e.x+\"px\",this._box.style.height=e.y+\"px\"},_finish:function(){this._moved&&(T(this._box),z(this._container,\"leaflet-crosshair\")),ae(),Te(),k(document,{contextmenu:Re,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){1!==t.which&&1!==t.button||(this._finish(),this._moved&&(this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(a(this._resetState,this),0),t=new s(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point)),this._map.fitBounds(t).fire(\"boxzoomend\",{boxZoomBounds:t})))},_onKeyDown:function(t){27===t.keyCode&&(this._finish(),this._clearDeferredResetState(),this._resetState())}}),Ct=(A.addInitHook(\"addHandler\",\"boxZoom\",_t),A.mergeOptions({doubleClickZoom:!0}),n.extend({addHooks:function(){this._map.on(\"dblclick\",this._onDoubleClick,this)},removeHooks:function(){this._map.off(\"dblclick\",this._onDoubleClick,this)},_onDoubleClick:function(t){var e=this._map,i=e.getZoom(),n=e.options.zoomDelta,i=t.originalEvent.shiftKey?i-n:i+n;\"center\"===e.options.doubleClickZoom?e.setZoom(i):e.setZoomAround(t.containerPoint,i)}})),Zt=(A.addInitHook(\"addHandler\",\"doubleClickZoom\",Ct),A.mergeOptions({dragging:!0,inertia:!0,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0}),n.extend({addHooks:function(){var t;this._draggable||(t=this._map,this._draggable=new Xe(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on(\"predrag\",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on(\"predrag\",this._onPreDragWrap,this),t.on(\"zoomend\",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))),M(this._map._container,\"leaflet-grab leaflet-touch-drag\"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){z(this._map._container,\"leaflet-grab\"),z(this._map._container,\"leaflet-touch-drag\"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t,e=this._map;e._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity?(t=g(this._map.options.maxBounds),this._offsetLimit=_(this._map.latLngToContainerPoint(t.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(t.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))):this._offsetLimit=null,e.fire(\"movestart\").fire(\"dragstart\"),e.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){var e,i;this._map.options.inertia&&(e=this._lastTime=+new Date,i=this._lastPos=this._draggable._absPos||this._draggable._newPos,this._positions.push(i),this._times.push(e),this._prunePositions(e)),this._map.fire(\"move\",t).fire(\"drag\",t)},_prunePositions:function(t){for(;1<this._positions.length&&50<t-this._times[0];)this._positions.shift(),this._times.shift()},_onZoomEnd:function(){var t=this._map.getSize().divideBy(2),e=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=e.subtract(t).x,this._worldWidth=this._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(t,e){return t-(t-e)*this._viscosity},_onPreDragLimit:function(){var t,e;this._viscosity&&this._offsetLimit&&(t=this._draggable._newPos.subtract(this._draggable._startPos),e=this._offsetLimit,t.x<e.min.x&&(t.x=this._viscousLimit(t.x,e.min.x)),t.y<e.min.y&&(t.y=this._viscousLimit(t.y,e.min.y)),t.x>e.max.x&&(t.x=this._viscousLimit(t.x,e.max.x)),t.y>e.max.y&&(t.y=this._viscousLimit(t.y,e.max.y)),this._draggable._newPos=this._draggable._startPos.add(t))},_onPreDragWrap:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,n=(n+e+i)%t-e-i,t=Math.abs(o+i)<Math.abs(n+i)?o:n;this._draggable._absPos=this._draggable._newPos.clone(),this._draggable._newPos.x=t},_onDragEnd:function(t){var e,i,n,o,s=this._map,r=s.options,a=!r.inertia||t.noInertia||this._times.length<2;s.fire(\"dragend\",t),!a&&(this._prunePositions(+new Date),t=this._lastPos.subtract(this._positions[0]),a=(this._lastTime-this._times[0])/1e3,e=r.easeLinearity,a=(t=t.multiplyBy(e/a)).distanceTo([0,0]),i=Math.min(r.inertiaMaxSpeed,a),t=t.multiplyBy(i/a),n=i/(r.inertiaDeceleration*e),(o=t.multiplyBy(-n/2).round()).x||o.y)?(o=s._limitOffset(o,s.options.maxBounds),x(function(){s.panBy(o,{duration:n,easeLinearity:e,noMoveStart:!0,animate:!0})})):s.fire(\"moveend\")}})),St=(A.addInitHook(\"addHandler\",\"dragging\",Zt),A.mergeOptions({keyboard:!0,keyboardPanDelta:80}),n.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61,171],zoomOut:[189,109,54,173]},initialize:function(t){this._map=t,this._setPanDelta(t.options.keyboardPanDelta),this._setZoomDelta(t.options.zoomDelta)},addHooks:function(){var t=this._map._container;t.tabIndex<=0&&(t.tabIndex=\"0\"),S(t,{focus:this._onFocus,blur:this._onBlur,mousedown:this._onMouseDown},this),this._map.on({focus:this._addHooks,blur:this._removeHooks},this)},removeHooks:function(){this._removeHooks(),k(this._map._container,{focus:this._onFocus,blur:this._onBlur,mousedown:this._onMouseDown},this),this._map.off({focus:this._addHooks,blur:this._removeHooks},this)},_onMouseDown:function(){var t,e,i;this._focused||(i=document.body,t=document.documentElement,e=i.scrollTop||t.scrollTop,i=i.scrollLeft||t.scrollLeft,this._map._container.focus(),window.scrollTo(i,e))},_onFocus:function(){this._focused=!0,this._map.fire(\"focus\")},_onBlur:function(){this._focused=!1,this._map.fire(\"blur\")},_setPanDelta:function(t){for(var e=this._panKeys={},i=this.keyCodes,n=0,o=i.left.length;n<o;n++)e[i.left[n]]=[-1*t,0];for(n=0,o=i.right.length;n<o;n++)e[i.right[n]]=[t,0];for(n=0,o=i.down.length;n<o;n++)e[i.down[n]]=[0,t];for(n=0,o=i.up.length;n<o;n++)e[i.up[n]]=[0,-1*t]},_setZoomDelta:function(t){for(var e=this._zoomKeys={},i=this.keyCodes,n=0,o=i.zoomIn.length;n<o;n++)e[i.zoomIn[n]]=t;for(n=0,o=i.zoomOut.length;n<o;n++)e[i.zoomOut[n]]=-t},_addHooks:function(){S(document,\"keydown\",this._onKeyDown,this)},_removeHooks:function(){k(document,\"keydown\",this._onKeyDown,this)},_onKeyDown:function(t){if(!(t.altKey||t.ctrlKey||t.metaKey)){var e,i,n=t.keyCode,o=this._map;if(n in this._panKeys)o._panAnim&&o._panAnim._inProgress||(i=this._panKeys[n],t.shiftKey&&(i=m(i).multiplyBy(3)),o.options.maxBounds&&(i=o._limitOffset(m(i),o.options.maxBounds)),o.options.worldCopyJump?(e=o.wrapLatLng(o.unproject(o.project(o.getCenter()).add(i))),o.panTo(e)):o.panBy(i));else if(n in this._zoomKeys)o.setZoom(o.getZoom()+(t.shiftKey?3:1)*this._zoomKeys[n]);else{if(27!==n||!o._popup||!o._popup.options.closeOnEscapeKey)return;o.closePopup()}Re(t)}}})),Et=(A.addInitHook(\"addHandler\",\"keyboard\",St),A.mergeOptions({scrollWheelZoom:!0,wheelDebounceTime:40,wheelPxPerZoomLevel:60}),n.extend({addHooks:function(){S(this._map._container,\"wheel\",this._onWheelScroll,this),this._delta=0},removeHooks:function(){k(this._map._container,\"wheel\",this._onWheelScroll,this)},_onWheelScroll:function(t){var e=He(t),i=this._map.options.wheelDebounceTime,e=(this._delta+=e,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date),Math.max(i-(+new Date-this._startTime),0));clearTimeout(this._timer),this._timer=setTimeout(a(this._performZoom,this),e),Re(t)},_performZoom:function(){var t=this._map,e=t.getZoom(),i=this._map.options.zoomSnap||0,n=(t._stop(),this._delta/(4*this._map.options.wheelPxPerZoomLevel)),n=4*Math.log(2/(1+Math.exp(-Math.abs(n))))/Math.LN2,i=i?Math.ceil(n/i)*i:n,n=t._limitZoom(e+(0<this._delta?i:-i))-e;this._delta=0,this._startTime=null,n&&(\"center\"===t.options.scrollWheelZoom?t.setZoom(e+n):t.setZoomAround(this._lastMousePos,e+n))}})),kt=(A.addInitHook(\"addHandler\",\"scrollWheelZoom\",Et),A.mergeOptions({tapHold:b.touchNative&&b.safari&&b.mobile,tapTolerance:15}),n.extend({addHooks:function(){S(this._map._container,\"touchstart\",this._onDown,this)},removeHooks:function(){k(this._map._container,\"touchstart\",this._onDown,this)},_onDown:function(t){var e;clearTimeout(this._holdTimeout),1===t.touches.length&&(e=t.touches[0],this._startPos=this._newPos=new p(e.clientX,e.clientY),this._holdTimeout=setTimeout(a(function(){this._cancel(),this._isTapValid()&&(S(document,\"touchend\",O),S(document,\"touchend touchcancel\",this._cancelClickPrevent),this._simulateEvent(\"contextmenu\",e))},this),600),S(document,\"touchend touchcancel contextmenu\",this._cancel,this),S(document,\"touchmove\",this._onMove,this))},_cancelClickPrevent:function t(){k(document,\"touchend\",O),k(document,\"touchend touchcancel\",t)},_cancel:function(){clearTimeout(this._holdTimeout),k(document,\"touchend touchcancel contextmenu\",this._cancel,this),k(document,\"touchmove\",this._onMove,this)},_onMove:function(t){t=t.touches[0];this._newPos=new p(t.clientX,t.clientY)},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_simulateEvent:function(t,e){t=new MouseEvent(t,{bubbles:!0,cancelable:!0,view:window,screenX:e.screenX,screenY:e.screenY,clientX:e.clientX,clientY:e.clientY});t._simulated=!0,e.target.dispatchEvent(t)}})),Ot=(A.addInitHook(\"addHandler\",\"tapHold\",kt),A.mergeOptions({touchZoom:b.touch,bounceAtZoomLimits:!0}),n.extend({addHooks:function(){M(this._map._container,\"leaflet-touch-zoom\"),S(this._map._container,\"touchstart\",this._onTouchStart,this)},removeHooks:function(){z(this._map._container,\"leaflet-touch-zoom\"),k(this._map._container,\"touchstart\",this._onTouchStart,this)},_onTouchStart:function(t){var e,i,n=this._map;!t.touches||2!==t.touches.length||n._animatingZoom||this._zooming||(e=n.mouseEventToContainerPoint(t.touches[0]),i=n.mouseEventToContainerPoint(t.touches[1]),this._centerPoint=n.getSize()._divideBy(2),this._startLatLng=n.containerPointToLatLng(this._centerPoint),\"center\"!==n.options.touchZoom&&(this._pinchStartLatLng=n.containerPointToLatLng(e.add(i)._divideBy(2))),this._startDist=e.distanceTo(i),this._startZoom=n.getZoom(),this._moved=!1,this._zooming=!0,n._stop(),S(document,\"touchmove\",this._onTouchMove,this),S(document,\"touchend touchcancel\",this._onTouchEnd,this),O(t))},_onTouchMove:function(t){if(t.touches&&2===t.touches.length&&this._zooming){var e=this._map,i=e.mouseEventToContainerPoint(t.touches[0]),n=e.mouseEventToContainerPoint(t.touches[1]),o=i.distanceTo(n)/this._startDist;if(this._zoom=e.getScaleZoom(o,this._startZoom),!e.options.bounceAtZoomLimits&&(this._zoom<e.getMinZoom()&&o<1||this._zoom>e.getMaxZoom()&&1<o)&&(this._zoom=e._limitZoom(this._zoom)),\"center\"===e.options.touchZoom){if(this._center=this._startLatLng,1==o)return}else{i=i._add(n)._divideBy(2)._subtract(this._centerPoint);if(1==o&&0===i.x&&0===i.y)return;this._center=e.unproject(e.project(this._pinchStartLatLng,this._zoom).subtract(i),this._zoom)}this._moved||(e._moveStart(!0,!1),this._moved=!0),r(this._animRequest);n=a(e._move,e,this._center,this._zoom,{pinch:!0,round:!1},void 0);this._animRequest=x(n,this,!0),O(t)}},_onTouchEnd:function(){this._moved&&this._zooming?(this._zooming=!1,r(this._animRequest),k(document,\"touchmove\",this._onTouchMove,this),k(document,\"touchend touchcancel\",this._onTouchEnd,this),this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.zoomSnap):this._map._resetView(this._center,this._map._limitZoom(this._zoom))):this._zooming=!1}})),Xi=(A.addInitHook(\"addHandler\",\"touchZoom\",Ot),A.BoxZoom=_t,A.DoubleClickZoom=Ct,A.Drag=Zt,A.Keyboard=St,A.ScrollWheelZoom=Et,A.TapHold=kt,A.TouchZoom=Ot,t.Bounds=f,t.Browser=b,t.CRS=ot,t.Canvas=Fi,t.Circle=vi,t.CircleMarker=gi,t.Class=et,t.Control=B,t.DivIcon=Ri,t.DivOverlay=Ai,t.DomEvent=mt,t.DomUtil=pt,t.Draggable=Xe,t.Evented=it,t.FeatureGroup=ci,t.GeoJSON=wi,t.GridLayer=Ni,t.Handler=n,t.Icon=di,t.ImageOverlay=Ei,t.LatLng=v,t.LatLngBounds=s,t.Layer=o,t.LayerGroup=ui,t.LineUtil=vt,t.Map=A,t.Marker=mi,t.Mixin=ft,t.Path=fi,t.Point=p,t.PolyUtil=gt,t.Polygon=xi,t.Polyline=yi,t.Popup=Bi,t.PosAnimation=Fe,t.Projection=wt,t.Rectangle=Yi,t.Renderer=Wi,t.SVG=Gi,t.SVGOverlay=Oi,t.TileLayer=Di,t.Tooltip=Ii,t.Transformation=at,t.Util=tt,t.VideoOverlay=ki,t.bind=a,t.bounds=_,t.canvas=Ui,t.circle=function(t,e,i){return new vi(t,e,i)},t.circleMarker=function(t,e){return new gi(t,e)},t.control=Ue,t.divIcon=function(t){return new Ri(t)},t.extend=l,t.featureGroup=function(t,e){return new ci(t,e)},t.geoJSON=Si,t.geoJson=Mt,t.gridLayer=function(t){return new Ni(t)},t.icon=function(t){return new di(t)},t.imageOverlay=function(t,e,i){return new Ei(t,e,i)},t.latLng=w,t.latLngBounds=g,t.layerGroup=function(t,e){return new ui(t,e)},t.map=function(t,e){return new A(t,e)},t.marker=function(t,e){return new mi(t,e)},t.point=m,t.polygon=function(t,e){return new xi(t,e)},t.polyline=function(t,e){return new yi(t,e)},t.popup=function(t,e){return new Bi(t,e)},t.rectangle=function(t,e){return new Yi(t,e)},t.setOptions=c,t.stamp=h,t.svg=Ki,t.svgOverlay=function(t,e,i){return new Oi(t,e,i)},t.tileLayer=ji,t.tooltip=function(t,e){return new Ii(t,e)},t.transformation=ht,t.version=\"1.9.4\",t.videoOverlay=function(t,e,i){return new ki(t,e,i)},window.L);t.noConflict=function(){return window.L=Xi,this},window.L=t});\n//# sourceMappingURL=leaflet.js.map\n"
  },
  {
    "path": "public/assets/lib/vendor/leaflet/shp.esm.js",
    "content": "function globals(defs) {\n  defs('EPSG:4326', \"+title=WGS 84 (long/lat) +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees\");\n  defs('EPSG:4269', \"+title=NAD83 (long/lat) +proj=longlat +a=6378137.0 +b=6356752.31414036 +ellps=GRS80 +datum=NAD83 +units=degrees\");\n  defs('EPSG:3857', \"+title=WGS 84 / Pseudo-Mercator +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs\");\n\n  defs.WGS84 = defs['EPSG:4326'];\n  defs['EPSG:3785'] = defs['EPSG:3857']; // maintain backward compat, official code is 3857\n  defs.GOOGLE = defs['EPSG:3857'];\n  defs['EPSG:900913'] = defs['EPSG:3857'];\n  defs['EPSG:102113'] = defs['EPSG:3857'];\n}\n\nvar PJD_3PARAM = 1;\nvar PJD_7PARAM = 2;\nvar PJD_GRIDSHIFT = 3;\nvar PJD_WGS84 = 4; // WGS84 or equivalent\nvar PJD_NODATUM = 5; // WGS84 or equivalent\nvar SRS_WGS84_SEMIMAJOR = 6378137.0;  // only used in grid shift transforms\nvar SRS_WGS84_SEMIMINOR = 6356752.314;  // only used in grid shift transforms\nvar SRS_WGS84_ESQUARED = 0.0066943799901413165; // only used in grid shift transforms\nvar SEC_TO_RAD = 4.84813681109535993589914102357e-6;\nvar HALF_PI = Math.PI/2;\n// ellipoid pj_set_ell.c\nvar SIXTH = 0.1666666666666666667;\n/* 1/6 */\nvar RA4 = 0.04722222222222222222;\n/* 17/360 */\nvar RA6 = 0.02215608465608465608;\nvar EPSLN = 1.0e-10;\n// you'd think you could use Number.EPSILON above but that makes\n// Mollweide get into an infinate loop.\n\nvar D2R$1 = 0.01745329251994329577;\nvar R2D = 57.29577951308232088;\nvar FORTPI = Math.PI/4;\nvar TWO_PI = Math.PI * 2;\n// SPI is slightly greater than Math.PI, so values that exceed the -180..180\n// degree range by a tiny amount don't get wrapped. This prevents points that\n// have drifted from their original location along the 180th meridian (due to\n// floating point error) from changing their sign.\nvar SPI = 3.14159265359;\n\nvar exports$2 = {};\n\nexports$2.greenwich = 0.0; //\"0dE\",\nexports$2.lisbon = -9.131906111111; //\"9d07'54.862\\\"W\",\nexports$2.paris = 2.337229166667; //\"2d20'14.025\\\"E\",\nexports$2.bogota = -74.080916666667; //\"74d04'51.3\\\"W\",\nexports$2.madrid = -3.687938888889; //\"3d41'16.58\\\"W\",\nexports$2.rome = 12.452333333333; //\"12d27'8.4\\\"E\",\nexports$2.bern = 7.439583333333; //\"7d26'22.5\\\"E\",\nexports$2.jakarta = 106.807719444444; //\"106d48'27.79\\\"E\",\nexports$2.ferro = -17.666666666667; //\"17d40'W\",\nexports$2.brussels = 4.367975; //\"4d22'4.71\\\"E\",\nexports$2.stockholm = 18.058277777778; //\"18d3'29.8\\\"E\",\nexports$2.athens = 23.7163375; //\"23d42'58.815\\\"E\",\nexports$2.oslo = 10.722916666667; //\"10d43'22.5\\\"E\"\n\nvar units = {\n  ft: {to_meter: 0.3048},\n  'us-ft': {to_meter: 1200 / 3937}\n};\n\nvar ignoredChar = /[\\s_\\-\\/\\(\\)]/g;\nfunction match(obj, key) {\n  if (obj[key]) {\n    return obj[key];\n  }\n  var keys = Object.keys(obj);\n  var lkey = key.toLowerCase().replace(ignoredChar, '');\n  var i = -1;\n  var testkey, processedKey;\n  while (++i < keys.length) {\n    testkey = keys[i];\n    processedKey = testkey.toLowerCase().replace(ignoredChar, '');\n    if (processedKey === lkey) {\n      return obj[testkey];\n    }\n  }\n}\n\nfunction projStr(defData) {\n  var self = {};\n  var paramObj = defData.split('+').map(function(v) {\n    return v.trim();\n  }).filter(function(a) {\n    return a;\n  }).reduce(function(p, a) {\n    var split = a.split('=');\n    split.push(true);\n    p[split[0].toLowerCase()] = split[1];\n    return p;\n  }, {});\n  var paramName, paramVal, paramOutname;\n  var params = {\n    proj: 'projName',\n    datum: 'datumCode',\n    rf: function(v) {\n      self.rf = parseFloat(v);\n    },\n    lat_0: function(v) {\n      self.lat0 = v * D2R$1;\n    },\n    lat_1: function(v) {\n      self.lat1 = v * D2R$1;\n    },\n    lat_2: function(v) {\n      self.lat2 = v * D2R$1;\n    },\n    lat_ts: function(v) {\n      self.lat_ts = v * D2R$1;\n    },\n    lon_0: function(v) {\n      self.long0 = v * D2R$1;\n    },\n    lon_1: function(v) {\n      self.long1 = v * D2R$1;\n    },\n    lon_2: function(v) {\n      self.long2 = v * D2R$1;\n    },\n    alpha: function(v) {\n      self.alpha = parseFloat(v) * D2R$1;\n    },\n    gamma: function(v) {\n      self.rectified_grid_angle = parseFloat(v);\n    },\n    lonc: function(v) {\n      self.longc = v * D2R$1;\n    },\n    x_0: function(v) {\n      self.x0 = parseFloat(v);\n    },\n    y_0: function(v) {\n      self.y0 = parseFloat(v);\n    },\n    k_0: function(v) {\n      self.k0 = parseFloat(v);\n    },\n    k: function(v) {\n      self.k0 = parseFloat(v);\n    },\n    a: function(v) {\n      self.a = parseFloat(v);\n    },\n    b: function(v) {\n      self.b = parseFloat(v);\n    },\n    r_a: function() {\n      self.R_A = true;\n    },\n    zone: function(v) {\n      self.zone = parseInt(v, 10);\n    },\n    south: function() {\n      self.utmSouth = true;\n    },\n    towgs84: function(v) {\n      self.datum_params = v.split(\",\").map(function(a) {\n        return parseFloat(a);\n      });\n    },\n    to_meter: function(v) {\n      self.to_meter = parseFloat(v);\n    },\n    units: function(v) {\n      self.units = v;\n      var unit = match(units, v);\n      if (unit) {\n        self.to_meter = unit.to_meter;\n      }\n    },\n    from_greenwich: function(v) {\n      self.from_greenwich = v * D2R$1;\n    },\n    pm: function(v) {\n      var pm = match(exports$2, v);\n      self.from_greenwich = (pm ? pm : parseFloat(v)) * D2R$1;\n    },\n    nadgrids: function(v) {\n      if (v === '@null') {\n        self.datumCode = 'none';\n      }\n      else {\n        self.nadgrids = v;\n      }\n    },\n    axis: function(v) {\n      var legalAxis = \"ewnsud\";\n      if (v.length === 3 && legalAxis.indexOf(v.substr(0, 1)) !== -1 && legalAxis.indexOf(v.substr(1, 1)) !== -1 && legalAxis.indexOf(v.substr(2, 1)) !== -1) {\n        self.axis = v;\n      }\n    },\n    approx: function() {\n      self.approx = true;\n    }\n  };\n  for (paramName in paramObj) {\n    paramVal = paramObj[paramName];\n    if (paramName in params) {\n      paramOutname = params[paramName];\n      if (typeof paramOutname === 'function') {\n        paramOutname(paramVal);\n      }\n      else {\n        self[paramOutname] = paramVal;\n      }\n    }\n    else {\n      self[paramName] = paramVal;\n    }\n  }\n  if(typeof self.datumCode === 'string' && self.datumCode !== \"WGS84\"){\n    self.datumCode = self.datumCode.toLowerCase();\n  }\n  return self;\n}\n\nvar NEUTRAL = 1;\nvar KEYWORD = 2;\nvar NUMBER = 3;\nvar QUOTED = 4;\nvar AFTERQUOTE = 5;\nvar ENDED = -1;\nvar whitespace = /\\s/;\nvar latin = /[A-Za-z]/;\nvar keyword = /[A-Za-z84_]/;\nvar endThings = /[,\\]]/;\nvar digets = /[\\d\\.E\\-\\+]/;\n// const ignoredChar = /[\\s_\\-\\/\\(\\)]/g;\nfunction Parser(text) {\n  if (typeof text !== 'string') {\n    throw new Error('not a string');\n  }\n  this.text = text.trim();\n  this.level = 0;\n  this.place = 0;\n  this.root = null;\n  this.stack = [];\n  this.currentObject = null;\n  this.state = NEUTRAL;\n}\nParser.prototype.readCharicter = function() {\n  var char = this.text[this.place++];\n  if (this.state !== QUOTED) {\n    while (whitespace.test(char)) {\n      if (this.place >= this.text.length) {\n        return;\n      }\n      char = this.text[this.place++];\n    }\n  }\n  switch (this.state) {\n    case NEUTRAL:\n      return this.neutral(char);\n    case KEYWORD:\n      return this.keyword(char)\n    case QUOTED:\n      return this.quoted(char);\n    case AFTERQUOTE:\n      return this.afterquote(char);\n    case NUMBER:\n      return this.number(char);\n    case ENDED:\n      return;\n  }\n};\nParser.prototype.afterquote = function(char) {\n  if (char === '\"') {\n    this.word += '\"';\n    this.state = QUOTED;\n    return;\n  }\n  if (endThings.test(char)) {\n    this.word = this.word.trim();\n    this.afterItem(char);\n    return;\n  }\n  throw new Error('havn\\'t handled \"' +char + '\" in afterquote yet, index ' + this.place);\n};\nParser.prototype.afterItem = function(char) {\n  if (char === ',') {\n    if (this.word !== null) {\n      this.currentObject.push(this.word);\n    }\n    this.word = null;\n    this.state = NEUTRAL;\n    return;\n  }\n  if (char === ']') {\n    this.level--;\n    if (this.word !== null) {\n      this.currentObject.push(this.word);\n      this.word = null;\n    }\n    this.state = NEUTRAL;\n    this.currentObject = this.stack.pop();\n    if (!this.currentObject) {\n      this.state = ENDED;\n    }\n\n    return;\n  }\n};\nParser.prototype.number = function(char) {\n  if (digets.test(char)) {\n    this.word += char;\n    return;\n  }\n  if (endThings.test(char)) {\n    this.word = parseFloat(this.word);\n    this.afterItem(char);\n    return;\n  }\n  throw new Error('havn\\'t handled \"' +char + '\" in number yet, index ' + this.place);\n};\nParser.prototype.quoted = function(char) {\n  if (char === '\"') {\n    this.state = AFTERQUOTE;\n    return;\n  }\n  this.word += char;\n  return;\n};\nParser.prototype.keyword = function(char) {\n  if (keyword.test(char)) {\n    this.word += char;\n    return;\n  }\n  if (char === '[') {\n    var newObjects = [];\n    newObjects.push(this.word);\n    this.level++;\n    if (this.root === null) {\n      this.root = newObjects;\n    } else {\n      this.currentObject.push(newObjects);\n    }\n    this.stack.push(this.currentObject);\n    this.currentObject = newObjects;\n    this.state = NEUTRAL;\n    return;\n  }\n  if (endThings.test(char)) {\n    this.afterItem(char);\n    return;\n  }\n  throw new Error('havn\\'t handled \"' +char + '\" in keyword yet, index ' + this.place);\n};\nParser.prototype.neutral = function(char) {\n  if (latin.test(char)) {\n    this.word = char;\n    this.state = KEYWORD;\n    return;\n  }\n  if (char === '\"') {\n    this.word = '';\n    this.state = QUOTED;\n    return;\n  }\n  if (digets.test(char)) {\n    this.word = char;\n    this.state = NUMBER;\n    return;\n  }\n  if (endThings.test(char)) {\n    this.afterItem(char);\n    return;\n  }\n  throw new Error('havn\\'t handled \"' +char + '\" in neutral yet, index ' + this.place);\n};\nParser.prototype.output = function() {\n  while (this.place < this.text.length) {\n    this.readCharicter();\n  }\n  if (this.state === ENDED) {\n    return this.root;\n  }\n  throw new Error('unable to parse string \"' +this.text + '\". State is ' + this.state);\n};\n\nfunction parseString(txt) {\n  var parser = new Parser(txt);\n  return parser.output();\n}\n\nfunction mapit(obj, key, value) {\n  if (Array.isArray(key)) {\n    value.unshift(key);\n    key = null;\n  }\n  var thing = key ? {} : obj;\n\n  var out = value.reduce(function(newObj, item) {\n    sExpr(item, newObj);\n    return newObj\n  }, thing);\n  if (key) {\n    obj[key] = out;\n  }\n}\n\nfunction sExpr(v, obj) {\n  if (!Array.isArray(v)) {\n    obj[v] = true;\n    return;\n  }\n  var key = v.shift();\n  if (key === 'PARAMETER') {\n    key = v.shift();\n  }\n  if (v.length === 1) {\n    if (Array.isArray(v[0])) {\n      obj[key] = {};\n      sExpr(v[0], obj[key]);\n      return;\n    }\n    obj[key] = v[0];\n    return;\n  }\n  if (!v.length) {\n    obj[key] = true;\n    return;\n  }\n  if (key === 'TOWGS84') {\n    obj[key] = v;\n    return;\n  }\n  if (key === 'AXIS') {\n    if (!(key in obj)) {\n      obj[key] = [];\n    }\n    obj[key].push(v);\n    return;\n  }\n  if (!Array.isArray(key)) {\n    obj[key] = {};\n  }\n\n  var i;\n  switch (key) {\n    case 'UNIT':\n    case 'PRIMEM':\n    case 'VERT_DATUM':\n      obj[key] = {\n        name: v[0].toLowerCase(),\n        convert: v[1]\n      };\n      if (v.length === 3) {\n        sExpr(v[2], obj[key]);\n      }\n      return;\n    case 'SPHEROID':\n    case 'ELLIPSOID':\n      obj[key] = {\n        name: v[0],\n        a: v[1],\n        rf: v[2]\n      };\n      if (v.length === 4) {\n        sExpr(v[3], obj[key]);\n      }\n      return;\n    case 'PROJECTEDCRS':\n    case 'PROJCRS':\n    case 'GEOGCS':\n    case 'GEOCCS':\n    case 'PROJCS':\n    case 'LOCAL_CS':\n    case 'GEODCRS':\n    case 'GEODETICCRS':\n    case 'GEODETICDATUM':\n    case 'EDATUM':\n    case 'ENGINEERINGDATUM':\n    case 'VERT_CS':\n    case 'VERTCRS':\n    case 'VERTICALCRS':\n    case 'COMPD_CS':\n    case 'COMPOUNDCRS':\n    case 'ENGINEERINGCRS':\n    case 'ENGCRS':\n    case 'FITTED_CS':\n    case 'LOCAL_DATUM':\n    case 'DATUM':\n      v[0] = ['name', v[0]];\n      mapit(obj, key, v);\n      return;\n    default:\n      i = -1;\n      while (++i < v.length) {\n        if (!Array.isArray(v[i])) {\n          return sExpr(v, obj[key]);\n        }\n      }\n      return mapit(obj, key, v);\n  }\n}\n\nvar D2R = 0.01745329251994329577;\n\n\n\nfunction rename(obj, params) {\n  var outName = params[0];\n  var inName = params[1];\n  if (!(outName in obj) && (inName in obj)) {\n    obj[outName] = obj[inName];\n    if (params.length === 3) {\n      obj[outName] = params[2](obj[outName]);\n    }\n  }\n}\n\nfunction d2r(input) {\n  return input * D2R;\n}\n\nfunction cleanWKT(wkt) {\n  if (wkt.type === 'GEOGCS') {\n    wkt.projName = 'longlat';\n  } else if (wkt.type === 'LOCAL_CS') {\n    wkt.projName = 'identity';\n    wkt.local = true;\n  } else {\n    if (typeof wkt.PROJECTION === 'object') {\n      wkt.projName = Object.keys(wkt.PROJECTION)[0];\n    } else {\n      wkt.projName = wkt.PROJECTION;\n    }\n  }\n  if (wkt.AXIS) {\n    var axisOrder = '';\n    for (var i = 0, ii = wkt.AXIS.length; i < ii; ++i) {\n      var axis = [wkt.AXIS[i][0].toLowerCase(), wkt.AXIS[i][1].toLowerCase()];\n      if (axis[0].indexOf('north') !== -1 || ((axis[0] === 'y' || axis[0] === 'lat') && axis[1] === 'north')) {\n        axisOrder += 'n';\n      } else if (axis[0].indexOf('south') !== -1 || ((axis[0] === 'y' || axis[0] === 'lat') && axis[1] === 'south')) {\n        axisOrder += 's';\n      } else if (axis[0].indexOf('east') !== -1 || ((axis[0] === 'x' || axis[0] === 'lon') && axis[1] === 'east')) {\n        axisOrder += 'e';\n      } else if (axis[0].indexOf('west') !== -1 || ((axis[0] === 'x' || axis[0] === 'lon') && axis[1] === 'west')) {\n        axisOrder += 'w';\n      }\n    }\n    if (axisOrder.length === 2) {\n      axisOrder += 'u';\n    }\n    if (axisOrder.length === 3) {\n      wkt.axis = axisOrder;\n    }\n  }\n  if (wkt.UNIT) {\n    wkt.units = wkt.UNIT.name.toLowerCase();\n    if (wkt.units === 'metre') {\n      wkt.units = 'meter';\n    }\n    if (wkt.UNIT.convert) {\n      if (wkt.type === 'GEOGCS') {\n        if (wkt.DATUM && wkt.DATUM.SPHEROID) {\n          wkt.to_meter = wkt.UNIT.convert*wkt.DATUM.SPHEROID.a;\n        }\n      } else {\n        wkt.to_meter = wkt.UNIT.convert;\n      }\n    }\n  }\n  var geogcs = wkt.GEOGCS;\n  if (wkt.type === 'GEOGCS') {\n    geogcs = wkt;\n  }\n  if (geogcs) {\n    //if(wkt.GEOGCS.PRIMEM&&wkt.GEOGCS.PRIMEM.convert){\n    //  wkt.from_greenwich=wkt.GEOGCS.PRIMEM.convert*D2R;\n    //}\n    if (geogcs.DATUM) {\n      wkt.datumCode = geogcs.DATUM.name.toLowerCase();\n    } else {\n      wkt.datumCode = geogcs.name.toLowerCase();\n    }\n    if (wkt.datumCode.slice(0, 2) === 'd_') {\n      wkt.datumCode = wkt.datumCode.slice(2);\n    }\n    if (wkt.datumCode === 'new_zealand_geodetic_datum_1949' || wkt.datumCode === 'new_zealand_1949') {\n      wkt.datumCode = 'nzgd49';\n    }\n    if (wkt.datumCode === 'wgs_1984' || wkt.datumCode === 'world_geodetic_system_1984') {\n      if (wkt.PROJECTION === 'Mercator_Auxiliary_Sphere') {\n        wkt.sphere = true;\n      }\n      wkt.datumCode = 'wgs84';\n    }\n    if (wkt.datumCode.slice(-6) === '_ferro') {\n      wkt.datumCode = wkt.datumCode.slice(0, - 6);\n    }\n    if (wkt.datumCode.slice(-8) === '_jakarta') {\n      wkt.datumCode = wkt.datumCode.slice(0, - 8);\n    }\n    if (~wkt.datumCode.indexOf('belge')) {\n      wkt.datumCode = 'rnb72';\n    }\n    if (geogcs.DATUM && geogcs.DATUM.SPHEROID) {\n      wkt.ellps = geogcs.DATUM.SPHEROID.name.replace('_19', '').replace(/[Cc]larke\\_18/, 'clrk');\n      if (wkt.ellps.toLowerCase().slice(0, 13) === 'international') {\n        wkt.ellps = 'intl';\n      }\n\n      wkt.a = geogcs.DATUM.SPHEROID.a;\n      wkt.rf = parseFloat(geogcs.DATUM.SPHEROID.rf, 10);\n    }\n\n    if (geogcs.DATUM && geogcs.DATUM.TOWGS84) {\n      wkt.datum_params = geogcs.DATUM.TOWGS84;\n    }\n    if (~wkt.datumCode.indexOf('osgb_1936')) {\n      wkt.datumCode = 'osgb36';\n    }\n    if (~wkt.datumCode.indexOf('osni_1952')) {\n      wkt.datumCode = 'osni52';\n    }\n    if (~wkt.datumCode.indexOf('tm65')\n      || ~wkt.datumCode.indexOf('geodetic_datum_of_1965')) {\n      wkt.datumCode = 'ire65';\n    }\n    if (wkt.datumCode === 'ch1903+') {\n      wkt.datumCode = 'ch1903';\n    }\n    if (~wkt.datumCode.indexOf('israel')) {\n      wkt.datumCode = 'isr93';\n    }\n  }\n  if (wkt.b && !isFinite(wkt.b)) {\n    wkt.b = wkt.a;\n  }\n\n  function toMeter(input) {\n    var ratio = wkt.to_meter || 1;\n    return input * ratio;\n  }\n  var renamer = function(a) {\n    return rename(wkt, a);\n  };\n  var list = [\n    ['standard_parallel_1', 'Standard_Parallel_1'],\n    ['standard_parallel_1', 'Latitude of 1st standard parallel'],\n    ['standard_parallel_2', 'Standard_Parallel_2'],\n    ['standard_parallel_2', 'Latitude of 2nd standard parallel'],\n    ['false_easting', 'False_Easting'],\n    ['false_easting', 'False easting'],\n    ['false-easting', 'Easting at false origin'],\n    ['false_northing', 'False_Northing'],\n    ['false_northing', 'False northing'],\n    ['false_northing', 'Northing at false origin'],\n    ['central_meridian', 'Central_Meridian'],\n    ['central_meridian', 'Longitude of natural origin'],\n    ['central_meridian', 'Longitude of false origin'],\n    ['latitude_of_origin', 'Latitude_Of_Origin'],\n    ['latitude_of_origin', 'Central_Parallel'],\n    ['latitude_of_origin', 'Latitude of natural origin'],\n    ['latitude_of_origin', 'Latitude of false origin'],\n    ['scale_factor', 'Scale_Factor'],\n    ['k0', 'scale_factor'],\n    ['latitude_of_center', 'Latitude_Of_Center'],\n    ['latitude_of_center', 'Latitude_of_center'],\n    ['lat0', 'latitude_of_center', d2r],\n    ['longitude_of_center', 'Longitude_Of_Center'],\n    ['longitude_of_center', 'Longitude_of_center'],\n    ['longc', 'longitude_of_center', d2r],\n    ['x0', 'false_easting', toMeter],\n    ['y0', 'false_northing', toMeter],\n    ['long0', 'central_meridian', d2r],\n    ['lat0', 'latitude_of_origin', d2r],\n    ['lat0', 'standard_parallel_1', d2r],\n    ['lat1', 'standard_parallel_1', d2r],\n    ['lat2', 'standard_parallel_2', d2r],\n    ['azimuth', 'Azimuth'],\n    ['alpha', 'azimuth', d2r],\n    ['srsCode', 'name']\n  ];\n  list.forEach(renamer);\n  if (!wkt.long0 && wkt.longc && (wkt.projName === 'Albers_Conic_Equal_Area' || wkt.projName === 'Lambert_Azimuthal_Equal_Area')) {\n    wkt.long0 = wkt.longc;\n  }\n  if (!wkt.lat_ts && wkt.lat1 && (wkt.projName === 'Stereographic_South_Pole' || wkt.projName === 'Polar Stereographic (variant B)')) {\n    wkt.lat0 = d2r(wkt.lat1 > 0 ? 90 : -90);\n    wkt.lat_ts = wkt.lat1;\n  } else if (!wkt.lat_ts && wkt.lat0 && wkt.projName === 'Polar_Stereographic') {\n    wkt.lat_ts = wkt.lat0;\n    wkt.lat0 = d2r(wkt.lat0 > 0 ? 90 : -90);\n  }\n}\nfunction wkt(wkt) {\n  var lisp = parseString(wkt);\n  var type = lisp.shift();\n  var name = lisp.shift();\n  lisp.unshift(['name', name]);\n  lisp.unshift(['type', type]);\n  var obj = {};\n  sExpr(lisp, obj);\n  cleanWKT(obj);\n  return obj;\n}\n\nfunction defs(name) {\n  /*global console*/\n  var that = this;\n  if (arguments.length === 2) {\n    var def = arguments[1];\n    if (typeof def === 'string') {\n      if (def.charAt(0) === '+') {\n        defs[name] = projStr(arguments[1]);\n      }\n      else {\n        defs[name] = wkt(arguments[1]);\n      }\n    } else {\n      defs[name] = def;\n    }\n  }\n  else if (arguments.length === 1) {\n    if (Array.isArray(name)) {\n      return name.map(function(v) {\n        if (Array.isArray(v)) {\n          defs.apply(that, v);\n        }\n        else {\n          defs(v);\n        }\n      });\n    }\n    else if (typeof name === 'string') {\n      if (name in defs) {\n        return defs[name];\n      }\n    }\n    else if ('EPSG' in name) {\n      defs['EPSG:' + name.EPSG] = name;\n    }\n    else if ('ESRI' in name) {\n      defs['ESRI:' + name.ESRI] = name;\n    }\n    else if ('IAU2000' in name) {\n      defs['IAU2000:' + name.IAU2000] = name;\n    }\n    else {\n      console.log(name);\n    }\n    return;\n  }\n\n\n}\nglobals(defs);\n\nfunction testObj(code){\n  return typeof code === 'string';\n}\nfunction testDef(code){\n  return code in defs;\n}\nvar codeWords = ['PROJECTEDCRS', 'PROJCRS', 'GEOGCS','GEOCCS','PROJCS','LOCAL_CS', 'GEODCRS', 'GEODETICCRS', 'GEODETICDATUM', 'ENGCRS', 'ENGINEERINGCRS'];\nfunction testWKT(code){\n  return codeWords.some(function (word) {\n    return code.indexOf(word) > -1;\n  });\n}\nvar codes = ['3857', '900913', '3785', '102113'];\nfunction checkMercator(item) {\n  var auth = match(item, 'authority');\n  if (!auth) {\n    return;\n  }\n  var code = match(auth, 'epsg');\n  return code && codes.indexOf(code) > -1;\n}\nfunction checkProjStr(item) {\n  var ext = match(item, 'extension');\n  if (!ext) {\n    return;\n  }\n  return match(ext, 'proj4');\n}\nfunction testProj(code){\n  return code[0] === '+';\n}\nfunction parse(code){\n  if (testObj(code)) {\n    //check to see if this is a WKT string\n    if (testDef(code)) {\n      return defs[code];\n    }\n    if (testWKT(code)) {\n      var out = wkt(code);\n      // test of spetial case, due to this being a very common and often malformed\n      if (checkMercator(out)) {\n        return defs['EPSG:3857'];\n      }\n      var maybeProjStr = checkProjStr(out);\n      if (maybeProjStr) {\n        return projStr(maybeProjStr);\n      }\n      return out;\n    }\n    if (testProj(code)) {\n      return projStr(code);\n    }\n  }else {\n    return code;\n  }\n}\n\nfunction extend(destination, source) {\n  destination = destination || {};\n  var value, property;\n  if (!source) {\n    return destination;\n  }\n  for (property in source) {\n    value = source[property];\n    if (value !== undefined) {\n      destination[property] = value;\n    }\n  }\n  return destination;\n}\n\nfunction msfnz(eccent, sinphi, cosphi) {\n  var con = eccent * sinphi;\n  return cosphi / (Math.sqrt(1 - con * con));\n}\n\nfunction sign(x) {\n  return x<0 ? -1 : 1;\n}\n\nfunction adjust_lon(x) {\n  return (Math.abs(x) <= SPI) ? x : (x - (sign(x) * TWO_PI));\n}\n\nfunction tsfnz(eccent, phi, sinphi) {\n  var con = eccent * sinphi;\n  var com = 0.5 * eccent;\n  con = Math.pow(((1 - con) / (1 + con)), com);\n  return (Math.tan(0.5 * (HALF_PI - phi)) / con);\n}\n\nfunction phi2z(eccent, ts) {\n  var eccnth = 0.5 * eccent;\n  var con, dphi;\n  var phi = HALF_PI - 2 * Math.atan(ts);\n  for (var i = 0; i <= 15; i++) {\n    con = eccent * Math.sin(phi);\n    dphi = HALF_PI - 2 * Math.atan(ts * (Math.pow(((1 - con) / (1 + con)), eccnth))) - phi;\n    phi += dphi;\n    if (Math.abs(dphi) <= 0.0000000001) {\n      return phi;\n    }\n  }\n  //console.log(\"phi2z has NoConvergence\");\n  return -9999;\n}\n\nfunction init$v() {\n  var con = this.b / this.a;\n  this.es = 1 - con * con;\n  if(!('x0' in this)){\n    this.x0 = 0;\n  }\n  if(!('y0' in this)){\n    this.y0 = 0;\n  }\n  this.e = Math.sqrt(this.es);\n  if (this.lat_ts) {\n    if (this.sphere) {\n      this.k0 = Math.cos(this.lat_ts);\n    }\n    else {\n      this.k0 = msfnz(this.e, Math.sin(this.lat_ts), Math.cos(this.lat_ts));\n    }\n  }\n  else {\n    if (!this.k0) {\n      if (this.k) {\n        this.k0 = this.k;\n      }\n      else {\n        this.k0 = 1;\n      }\n    }\n  }\n}\n\n/* Mercator forward equations--mapping lat,long to x,y\n  --------------------------------------------------*/\n\nfunction forward$u(p) {\n  var lon = p.x;\n  var lat = p.y;\n  // convert to radians\n  if (lat * R2D > 90 && lat * R2D < -90 && lon * R2D > 180 && lon * R2D < -180) {\n    return null;\n  }\n\n  var x, y;\n  if (Math.abs(Math.abs(lat) - HALF_PI) <= EPSLN) {\n    return null;\n  }\n  else {\n    if (this.sphere) {\n      x = this.x0 + this.a * this.k0 * adjust_lon(lon - this.long0);\n      y = this.y0 + this.a * this.k0 * Math.log(Math.tan(FORTPI + 0.5 * lat));\n    }\n    else {\n      var sinphi = Math.sin(lat);\n      var ts = tsfnz(this.e, lat, sinphi);\n      x = this.x0 + this.a * this.k0 * adjust_lon(lon - this.long0);\n      y = this.y0 - this.a * this.k0 * Math.log(ts);\n    }\n    p.x = x;\n    p.y = y;\n    return p;\n  }\n}\n\n/* Mercator inverse equations--mapping x,y to lat/long\n  --------------------------------------------------*/\nfunction inverse$u(p) {\n\n  var x = p.x - this.x0;\n  var y = p.y - this.y0;\n  var lon, lat;\n\n  if (this.sphere) {\n    lat = HALF_PI - 2 * Math.atan(Math.exp(-y / (this.a * this.k0)));\n  }\n  else {\n    var ts = Math.exp(-y / (this.a * this.k0));\n    lat = phi2z(this.e, ts);\n    if (lat === -9999) {\n      return null;\n    }\n  }\n  lon = adjust_lon(this.long0 + x / (this.a * this.k0));\n\n  p.x = lon;\n  p.y = lat;\n  return p;\n}\n\nvar names$w = [\"Mercator\", \"Popular Visualisation Pseudo Mercator\", \"Mercator_1SP\", \"Mercator_Auxiliary_Sphere\", \"merc\"];\nvar merc = {\n  init: init$v,\n  forward: forward$u,\n  inverse: inverse$u,\n  names: names$w\n};\n\nfunction init$u() {\n  //no-op for longlat\n}\n\nfunction identity(pt) {\n  return pt;\n}\nvar names$v = [\"longlat\", \"identity\"];\nvar longlat = {\n  init: init$u,\n  forward: identity,\n  inverse: identity,\n  names: names$v\n};\n\nvar projs = [merc, longlat];\nvar names$u = {};\nvar projStore = [];\n\nfunction add(proj, i) {\n  var len = projStore.length;\n  if (!proj.names) {\n    console.log(i);\n    return true;\n  }\n  projStore[len] = proj;\n  proj.names.forEach(function(n) {\n    names$u[n.toLowerCase()] = len;\n  });\n  return this;\n}\n\nfunction get(name) {\n  if (!name) {\n    return false;\n  }\n  var n = name.toLowerCase();\n  if (typeof names$u[n] !== 'undefined' && projStore[names$u[n]]) {\n    return projStore[names$u[n]];\n  }\n}\n\nfunction start() {\n  projs.forEach(add);\n}\nvar projections = {\n  start: start,\n  add: add,\n  get: get\n};\n\nvar exports$1 = {};\nexports$1.MERIT = {\n  a: 6378137.0,\n  rf: 298.257,\n  ellipseName: \"MERIT 1983\"\n};\n\nexports$1.SGS85 = {\n  a: 6378136.0,\n  rf: 298.257,\n  ellipseName: \"Soviet Geodetic System 85\"\n};\n\nexports$1.GRS80 = {\n  a: 6378137.0,\n  rf: 298.257222101,\n  ellipseName: \"GRS 1980(IUGG, 1980)\"\n};\n\nexports$1.IAU76 = {\n  a: 6378140.0,\n  rf: 298.257,\n  ellipseName: \"IAU 1976\"\n};\n\nexports$1.airy = {\n  a: 6377563.396,\n  b: 6356256.910,\n  ellipseName: \"Airy 1830\"\n};\n\nexports$1.APL4 = {\n  a: 6378137,\n  rf: 298.25,\n  ellipseName: \"Appl. Physics. 1965\"\n};\n\nexports$1.NWL9D = {\n  a: 6378145.0,\n  rf: 298.25,\n  ellipseName: \"Naval Weapons Lab., 1965\"\n};\n\nexports$1.mod_airy = {\n  a: 6377340.189,\n  b: 6356034.446,\n  ellipseName: \"Modified Airy\"\n};\n\nexports$1.andrae = {\n  a: 6377104.43,\n  rf: 300.0,\n  ellipseName: \"Andrae 1876 (Den., Iclnd.)\"\n};\n\nexports$1.aust_SA = {\n  a: 6378160.0,\n  rf: 298.25,\n  ellipseName: \"Australian Natl & S. Amer. 1969\"\n};\n\nexports$1.GRS67 = {\n  a: 6378160.0,\n  rf: 298.2471674270,\n  ellipseName: \"GRS 67(IUGG 1967)\"\n};\n\nexports$1.bessel = {\n  a: 6377397.155,\n  rf: 299.1528128,\n  ellipseName: \"Bessel 1841\"\n};\n\nexports$1.bess_nam = {\n  a: 6377483.865,\n  rf: 299.1528128,\n  ellipseName: \"Bessel 1841 (Namibia)\"\n};\n\nexports$1.clrk66 = {\n  a: 6378206.4,\n  b: 6356583.8,\n  ellipseName: \"Clarke 1866\"\n};\n\nexports$1.clrk80 = {\n  a: 6378249.145,\n  rf: 293.4663,\n  ellipseName: \"Clarke 1880 mod.\"\n};\n\nexports$1.clrk80ign = {\n  a: 6378249.2,\n  b: 6356515,\n  rf: 293.4660213,\n  ellipseName: \"Clarke 1880 (IGN)\"\n};\n\nexports$1.clrk58 = {\n  a: 6378293.645208759,\n  rf: 294.2606763692654,\n  ellipseName: \"Clarke 1858\"\n};\n\nexports$1.CPM = {\n  a: 6375738.7,\n  rf: 334.29,\n  ellipseName: \"Comm. des Poids et Mesures 1799\"\n};\n\nexports$1.delmbr = {\n  a: 6376428.0,\n  rf: 311.5,\n  ellipseName: \"Delambre 1810 (Belgium)\"\n};\n\nexports$1.engelis = {\n  a: 6378136.05,\n  rf: 298.2566,\n  ellipseName: \"Engelis 1985\"\n};\n\nexports$1.evrst30 = {\n  a: 6377276.345,\n  rf: 300.8017,\n  ellipseName: \"Everest 1830\"\n};\n\nexports$1.evrst48 = {\n  a: 6377304.063,\n  rf: 300.8017,\n  ellipseName: \"Everest 1948\"\n};\n\nexports$1.evrst56 = {\n  a: 6377301.243,\n  rf: 300.8017,\n  ellipseName: \"Everest 1956\"\n};\n\nexports$1.evrst69 = {\n  a: 6377295.664,\n  rf: 300.8017,\n  ellipseName: \"Everest 1969\"\n};\n\nexports$1.evrstSS = {\n  a: 6377298.556,\n  rf: 300.8017,\n  ellipseName: \"Everest (Sabah & Sarawak)\"\n};\n\nexports$1.fschr60 = {\n  a: 6378166.0,\n  rf: 298.3,\n  ellipseName: \"Fischer (Mercury Datum) 1960\"\n};\n\nexports$1.fschr60m = {\n  a: 6378155.0,\n  rf: 298.3,\n  ellipseName: \"Fischer 1960\"\n};\n\nexports$1.fschr68 = {\n  a: 6378150.0,\n  rf: 298.3,\n  ellipseName: \"Fischer 1968\"\n};\n\nexports$1.helmert = {\n  a: 6378200.0,\n  rf: 298.3,\n  ellipseName: \"Helmert 1906\"\n};\n\nexports$1.hough = {\n  a: 6378270.0,\n  rf: 297.0,\n  ellipseName: \"Hough\"\n};\n\nexports$1.intl = {\n  a: 6378388.0,\n  rf: 297.0,\n  ellipseName: \"International 1909 (Hayford)\"\n};\n\nexports$1.kaula = {\n  a: 6378163.0,\n  rf: 298.24,\n  ellipseName: \"Kaula 1961\"\n};\n\nexports$1.lerch = {\n  a: 6378139.0,\n  rf: 298.257,\n  ellipseName: \"Lerch 1979\"\n};\n\nexports$1.mprts = {\n  a: 6397300.0,\n  rf: 191.0,\n  ellipseName: \"Maupertius 1738\"\n};\n\nexports$1.new_intl = {\n  a: 6378157.5,\n  b: 6356772.2,\n  ellipseName: \"New International 1967\"\n};\n\nexports$1.plessis = {\n  a: 6376523.0,\n  rf: 6355863.0,\n  ellipseName: \"Plessis 1817 (France)\"\n};\n\nexports$1.krass = {\n  a: 6378245.0,\n  rf: 298.3,\n  ellipseName: \"Krassovsky, 1942\"\n};\n\nexports$1.SEasia = {\n  a: 6378155.0,\n  b: 6356773.3205,\n  ellipseName: \"Southeast Asia\"\n};\n\nexports$1.walbeck = {\n  a: 6376896.0,\n  b: 6355834.8467,\n  ellipseName: \"Walbeck\"\n};\n\nexports$1.WGS60 = {\n  a: 6378165.0,\n  rf: 298.3,\n  ellipseName: \"WGS 60\"\n};\n\nexports$1.WGS66 = {\n  a: 6378145.0,\n  rf: 298.25,\n  ellipseName: \"WGS 66\"\n};\n\nexports$1.WGS7 = {\n  a: 6378135.0,\n  rf: 298.26,\n  ellipseName: \"WGS 72\"\n};\n\nvar WGS84 = exports$1.WGS84 = {\n  a: 6378137.0,\n  rf: 298.257223563,\n  ellipseName: \"WGS 84\"\n};\n\nexports$1.sphere = {\n  a: 6370997.0,\n  b: 6370997.0,\n  ellipseName: \"Normal Sphere (r=6370997)\"\n};\n\nfunction eccentricity(a, b, rf, R_A) {\n  var a2 = a * a; // used in geocentric\n  var b2 = b * b; // used in geocentric\n  var es = (a2 - b2) / a2; // e ^ 2\n  var e = 0;\n  if (R_A) {\n    a *= 1 - es * (SIXTH + es * (RA4 + es * RA6));\n    a2 = a * a;\n    es = 0;\n  } else {\n    e = Math.sqrt(es); // eccentricity\n  }\n  var ep2 = (a2 - b2) / b2; // used in geocentric\n  return {\n    es: es,\n    e: e,\n    ep2: ep2\n  };\n}\nfunction sphere(a, b, rf, ellps, sphere) {\n  if (!a) { // do we have an ellipsoid?\n    var ellipse = match(exports$1, ellps);\n    if (!ellipse) {\n      ellipse = WGS84;\n    }\n    a = ellipse.a;\n    b = ellipse.b;\n    rf = ellipse.rf;\n  }\n\n  if (rf && !b) {\n    b = (1.0 - 1.0 / rf) * a;\n  }\n  if (rf === 0 || Math.abs(a - b) < EPSLN) {\n    sphere = true;\n    b = a;\n  }\n  return {\n    a: a,\n    b: b,\n    rf: rf,\n    sphere: sphere\n  };\n}\n\nvar exports = {};\nexports.wgs84 = {\n  towgs84: \"0,0,0\",\n  ellipse: \"WGS84\",\n  datumName: \"WGS84\"\n};\n\nexports.ch1903 = {\n  towgs84: \"674.374,15.056,405.346\",\n  ellipse: \"bessel\",\n  datumName: \"swiss\"\n};\n\nexports.ggrs87 = {\n  towgs84: \"-199.87,74.79,246.62\",\n  ellipse: \"GRS80\",\n  datumName: \"Greek_Geodetic_Reference_System_1987\"\n};\n\nexports.nad83 = {\n  towgs84: \"0,0,0\",\n  ellipse: \"GRS80\",\n  datumName: \"North_American_Datum_1983\"\n};\n\nexports.nad27 = {\n  nadgrids: \"@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat\",\n  ellipse: \"clrk66\",\n  datumName: \"North_American_Datum_1927\"\n};\n\nexports.potsdam = {\n  towgs84: \"598.1,73.7,418.2,0.202,0.045,-2.455,6.7\",\n  ellipse: \"bessel\",\n  datumName: \"Potsdam Rauenberg 1950 DHDN\"\n};\n\nexports.carthage = {\n  towgs84: \"-263.0,6.0,431.0\",\n  ellipse: \"clark80\",\n  datumName: \"Carthage 1934 Tunisia\"\n};\n\nexports.hermannskogel = {\n  towgs84: \"577.326,90.129,463.919,5.137,1.474,5.297,2.4232\",\n  ellipse: \"bessel\",\n  datumName: \"Hermannskogel\"\n};\n\nexports.militargeographische_institut = {\n  towgs84: \"577.326,90.129,463.919,5.137,1.474,5.297,2.4232\",\n  ellipse: \"bessel\",\n  datumName: \"Militar-Geographische Institut\"\n};\n\nexports.osni52 = {\n  towgs84: \"482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15\",\n  ellipse: \"airy\",\n  datumName: \"Irish National\"\n};\n\nexports.ire65 = {\n  towgs84: \"482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15\",\n  ellipse: \"mod_airy\",\n  datumName: \"Ireland 1965\"\n};\n\nexports.rassadiran = {\n  towgs84: \"-133.63,-157.5,-158.62\",\n  ellipse: \"intl\",\n  datumName: \"Rassadiran\"\n};\n\nexports.nzgd49 = {\n  towgs84: \"59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993\",\n  ellipse: \"intl\",\n  datumName: \"New Zealand Geodetic Datum 1949\"\n};\n\nexports.osgb36 = {\n  towgs84: \"446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894\",\n  ellipse: \"airy\",\n  datumName: \"Airy 1830\"\n};\n\nexports.s_jtsk = {\n  towgs84: \"589,76,480\",\n  ellipse: 'bessel',\n  datumName: 'S-JTSK (Ferro)'\n};\n\nexports.beduaram = {\n  towgs84: '-106,-87,188',\n  ellipse: 'clrk80',\n  datumName: 'Beduaram'\n};\n\nexports.gunung_segara = {\n  towgs84: '-403,684,41',\n  ellipse: 'bessel',\n  datumName: 'Gunung Segara Jakarta'\n};\n\nexports.rnb72 = {\n  towgs84: \"106.869,-52.2978,103.724,-0.33657,0.456955,-1.84218,1\",\n  ellipse: \"intl\",\n  datumName: \"Reseau National Belge 1972\"\n};\n\nfunction datum(datumCode, datum_params, a, b, es, ep2, nadgrids) {\n  var out = {};\n\n  if (datumCode === undefined || datumCode === 'none') {\n    out.datum_type = PJD_NODATUM;\n  } else {\n    out.datum_type = PJD_WGS84;\n  }\n\n  if (datum_params) {\n    out.datum_params = datum_params.map(parseFloat);\n    if (out.datum_params[0] !== 0 || out.datum_params[1] !== 0 || out.datum_params[2] !== 0) {\n      out.datum_type = PJD_3PARAM;\n    }\n    if (out.datum_params.length > 3) {\n      if (out.datum_params[3] !== 0 || out.datum_params[4] !== 0 || out.datum_params[5] !== 0 || out.datum_params[6] !== 0) {\n        out.datum_type = PJD_7PARAM;\n        out.datum_params[3] *= SEC_TO_RAD;\n        out.datum_params[4] *= SEC_TO_RAD;\n        out.datum_params[5] *= SEC_TO_RAD;\n        out.datum_params[6] = (out.datum_params[6] / 1000000.0) + 1.0;\n      }\n    }\n  }\n\n  if (nadgrids) {\n    out.datum_type = PJD_GRIDSHIFT;\n    out.grids = nadgrids;\n  }\n  out.a = a; //datum object also uses these values\n  out.b = b;\n  out.es = es;\n  out.ep2 = ep2;\n  return out;\n}\n\n/**\n * Resources for details of NTv2 file formats:\n * - https://web.archive.org/web/20140127204822if_/http://www.mgs.gov.on.ca:80/stdprodconsume/groups/content/@mgs/@iandit/documents/resourcelist/stel02_047447.pdf\n * - http://mimaka.com/help/gs/html/004_NTV2%20Data%20Format.htm\n */\n\nvar loadedNadgrids = {};\n\n/**\n * Load a binary NTv2 file (.gsb) to a key that can be used in a proj string like +nadgrids=<key>. Pass the NTv2 file\n * as an ArrayBuffer.\n */\nfunction nadgrid(key, data) {\n  var view = new DataView(data);\n  var isLittleEndian = detectLittleEndian(view);\n  var header = readHeader(view, isLittleEndian);\n  var subgrids = readSubgrids(view, header, isLittleEndian);\n  var nadgrid = {header: header, subgrids: subgrids};\n  loadedNadgrids[key] = nadgrid;\n  return nadgrid;\n}\n\n/**\n * Given a proj4 value for nadgrids, return an array of loaded grids\n */\nfunction getNadgrids(nadgrids) {\n  // Format details: http://proj.maptools.org/gen_parms.html\n  if (nadgrids === undefined) { return null; }\n  var grids = nadgrids.split(',');\n  return grids.map(parseNadgridString);\n}\n\nfunction parseNadgridString(value) {\n  if (value.length === 0) {\n    return null;\n  }\n  var optional = value[0] === '@';\n  if (optional) {\n    value = value.slice(1);\n  }\n  if (value === 'null') {\n    return {name: 'null', mandatory: !optional, grid: null, isNull: true};\n  }\n  return {\n    name: value,\n    mandatory: !optional,\n    grid: loadedNadgrids[value] || null,\n    isNull: false\n  };\n}\n\nfunction secondsToRadians(seconds) {\n  return (seconds / 3600) * Math.PI / 180;\n}\n\nfunction detectLittleEndian(view) {\n  var nFields = view.getInt32(8, false);\n  if (nFields === 11) {\n    return false;\n  }\n  nFields = view.getInt32(8, true);\n  if (nFields !== 11) {\n    console.warn('Failed to detect nadgrid endian-ness, defaulting to little-endian');\n  }\n  return true;\n}\n\nfunction readHeader(view, isLittleEndian) {\n  return {\n    nFields: view.getInt32(8, isLittleEndian),\n    nSubgridFields: view.getInt32(24, isLittleEndian),\n    nSubgrids: view.getInt32(40, isLittleEndian),\n    shiftType: decodeString(view, 56, 56 + 8).trim(),\n    fromSemiMajorAxis: view.getFloat64(120, isLittleEndian),\n    fromSemiMinorAxis: view.getFloat64(136, isLittleEndian),\n    toSemiMajorAxis: view.getFloat64(152, isLittleEndian),\n    toSemiMinorAxis: view.getFloat64(168, isLittleEndian),\n  };\n}\n\nfunction decodeString(view, start, end) {\n  return String.fromCharCode.apply(null, new Uint8Array(view.buffer.slice(start, end)));\n}\n\nfunction readSubgrids(view, header, isLittleEndian) {\n  var gridOffset = 176;\n  var grids = [];\n  for (var i = 0; i < header.nSubgrids; i++) {\n    var subHeader = readGridHeader(view, gridOffset, isLittleEndian);\n    var nodes = readGridNodes(view, gridOffset, subHeader, isLittleEndian);\n    var lngColumnCount = Math.round(\n      1 + (subHeader.upperLongitude - subHeader.lowerLongitude) / subHeader.longitudeInterval);\n    var latColumnCount = Math.round(\n      1 + (subHeader.upperLatitude - subHeader.lowerLatitude) / subHeader.latitudeInterval);\n    // Proj4 operates on radians whereas the coordinates are in seconds in the grid\n    grids.push({\n      ll: [secondsToRadians(subHeader.lowerLongitude), secondsToRadians(subHeader.lowerLatitude)],\n      del: [secondsToRadians(subHeader.longitudeInterval), secondsToRadians(subHeader.latitudeInterval)],\n      lim: [lngColumnCount, latColumnCount],\n      count: subHeader.gridNodeCount,\n      cvs: mapNodes(nodes)\n    });\n    gridOffset += 176 + subHeader.gridNodeCount * 16;\n  }\n  return grids;\n}\n\nfunction mapNodes(nodes) {\n  return nodes.map(function (r) {return [secondsToRadians(r.longitudeShift), secondsToRadians(r.latitudeShift)];});\n}\n\nfunction readGridHeader(view, offset, isLittleEndian) {\n  return {\n    name: decodeString(view, offset + 8, offset + 16).trim(),\n    parent: decodeString(view, offset + 24, offset + 24 + 8).trim(),\n    lowerLatitude: view.getFloat64(offset + 72, isLittleEndian),\n    upperLatitude: view.getFloat64(offset + 88, isLittleEndian),\n    lowerLongitude: view.getFloat64(offset + 104, isLittleEndian),\n    upperLongitude: view.getFloat64(offset + 120, isLittleEndian),\n    latitudeInterval: view.getFloat64(offset + 136, isLittleEndian),\n    longitudeInterval: view.getFloat64(offset + 152, isLittleEndian),\n    gridNodeCount: view.getInt32(offset + 168, isLittleEndian)\n  };\n}\n\nfunction readGridNodes(view, offset, gridHeader, isLittleEndian) {\n  var nodesOffset = offset + 176;\n  var gridRecordLength = 16;\n  var gridShiftRecords = [];\n  for (var i = 0; i < gridHeader.gridNodeCount; i++) {\n    var record = {\n      latitudeShift: view.getFloat32(nodesOffset + i * gridRecordLength, isLittleEndian),\n      longitudeShift: view.getFloat32(nodesOffset + i * gridRecordLength + 4, isLittleEndian),\n      latitudeAccuracy: view.getFloat32(nodesOffset + i * gridRecordLength + 8, isLittleEndian),\n      longitudeAccuracy: view.getFloat32(nodesOffset + i * gridRecordLength + 12, isLittleEndian),\n    };\n    gridShiftRecords.push(record);\n  }\n  return gridShiftRecords;\n}\n\nfunction Projection(srsCode,callback) {\n  if (!(this instanceof Projection)) {\n    return new Projection(srsCode);\n  }\n  callback = callback || function(error){\n    if(error){\n      throw error;\n    }\n  };\n  var json = parse(srsCode);\n  if(typeof json !== 'object'){\n    callback(srsCode);\n    return;\n  }\n  var ourProj = Projection.projections.get(json.projName);\n  if(!ourProj){\n    callback(srsCode);\n    return;\n  }\n  if (json.datumCode && json.datumCode !== 'none') {\n    var datumDef = match(exports, json.datumCode);\n    if (datumDef) {\n      json.datum_params = json.datum_params || (datumDef.towgs84 ? datumDef.towgs84.split(',') : null);\n      json.ellps = datumDef.ellipse;\n      json.datumName = datumDef.datumName ? datumDef.datumName : json.datumCode;\n    }\n  }\n  json.k0 = json.k0 || 1.0;\n  json.axis = json.axis || 'enu';\n  json.ellps = json.ellps || 'wgs84';\n  json.lat1 = json.lat1 || json.lat0; // Lambert_Conformal_Conic_1SP, for example, needs this\n\n  var sphere_ = sphere(json.a, json.b, json.rf, json.ellps, json.sphere);\n  var ecc = eccentricity(sphere_.a, sphere_.b, sphere_.rf, json.R_A);\n  var nadgrids = getNadgrids(json.nadgrids);\n  var datumObj = json.datum || datum(json.datumCode, json.datum_params, sphere_.a, sphere_.b, ecc.es, ecc.ep2,\n    nadgrids);\n\n  extend(this, json); // transfer everything over from the projection because we don't know what we'll need\n  extend(this, ourProj); // transfer all the methods from the projection\n\n  // copy the 4 things over we calculated in deriveConstants.sphere\n  this.a = sphere_.a;\n  this.b = sphere_.b;\n  this.rf = sphere_.rf;\n  this.sphere = sphere_.sphere;\n\n  // copy the 3 things we calculated in deriveConstants.eccentricity\n  this.es = ecc.es;\n  this.e = ecc.e;\n  this.ep2 = ecc.ep2;\n\n  // add in the datum object\n  this.datum = datumObj;\n\n  // init the projection\n  this.init();\n\n  // legecy callback from back in the day when it went to spatialreference.org\n  callback(null, this);\n\n}\nProjection.projections = projections;\nProjection.projections.start();\n\nfunction compareDatums(source, dest) {\n  if (source.datum_type !== dest.datum_type) {\n    return false; // false, datums are not equal\n  } else if (source.a !== dest.a || Math.abs(source.es - dest.es) > 0.000000000050) {\n    // the tolerance for es is to ensure that GRS80 and WGS84\n    // are considered identical\n    return false;\n  } else if (source.datum_type === PJD_3PARAM) {\n    return (source.datum_params[0] === dest.datum_params[0] && source.datum_params[1] === dest.datum_params[1] && source.datum_params[2] === dest.datum_params[2]);\n  } else if (source.datum_type === PJD_7PARAM) {\n    return (source.datum_params[0] === dest.datum_params[0] && source.datum_params[1] === dest.datum_params[1] && source.datum_params[2] === dest.datum_params[2] && source.datum_params[3] === dest.datum_params[3] && source.datum_params[4] === dest.datum_params[4] && source.datum_params[5] === dest.datum_params[5] && source.datum_params[6] === dest.datum_params[6]);\n  } else {\n    return true; // datums are equal\n  }\n} // cs_compare_datums()\n\n/*\n * The function Convert_Geodetic_To_Geocentric converts geodetic coordinates\n * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z),\n * according to the current ellipsoid parameters.\n *\n *    Latitude  : Geodetic latitude in radians                     (input)\n *    Longitude : Geodetic longitude in radians                    (input)\n *    Height    : Geodetic height, in meters                       (input)\n *    X         : Calculated Geocentric X coordinate, in meters    (output)\n *    Y         : Calculated Geocentric Y coordinate, in meters    (output)\n *    Z         : Calculated Geocentric Z coordinate, in meters    (output)\n *\n */\nfunction geodeticToGeocentric(p, es, a) {\n  var Longitude = p.x;\n  var Latitude = p.y;\n  var Height = p.z ? p.z : 0; //Z value not always supplied\n\n  var Rn; /*  Earth radius at location  */\n  var Sin_Lat; /*  Math.sin(Latitude)  */\n  var Sin2_Lat; /*  Square of Math.sin(Latitude)  */\n  var Cos_Lat; /*  Math.cos(Latitude)  */\n\n  /*\n   ** Don't blow up if Latitude is just a little out of the value\n   ** range as it may just be a rounding issue.  Also removed longitude\n   ** test, it should be wrapped by Math.cos() and Math.sin().  NFW for PROJ.4, Sep/2001.\n   */\n  if (Latitude < -HALF_PI && Latitude > -1.001 * HALF_PI) {\n    Latitude = -HALF_PI;\n  } else if (Latitude > HALF_PI && Latitude < 1.001 * HALF_PI) {\n    Latitude = HALF_PI;\n  } else if (Latitude < -HALF_PI) {\n    /* Latitude out of range */\n    //..reportError('geocent:lat out of range:' + Latitude);\n    return { x: -Infinity, y: -Infinity, z: p.z };\n  } else if (Latitude > HALF_PI) {\n    /* Latitude out of range */\n    return { x: Infinity, y: Infinity, z: p.z };\n  }\n\n  if (Longitude > Math.PI) {\n    Longitude -= (2 * Math.PI);\n  }\n  Sin_Lat = Math.sin(Latitude);\n  Cos_Lat = Math.cos(Latitude);\n  Sin2_Lat = Sin_Lat * Sin_Lat;\n  Rn = a / (Math.sqrt(1.0e0 - es * Sin2_Lat));\n  return {\n    x: (Rn + Height) * Cos_Lat * Math.cos(Longitude),\n    y: (Rn + Height) * Cos_Lat * Math.sin(Longitude),\n    z: ((Rn * (1 - es)) + Height) * Sin_Lat\n  };\n} // cs_geodetic_to_geocentric()\n\nfunction geocentricToGeodetic(p, es, a, b) {\n  /* local defintions and variables */\n  /* end-criterium of loop, accuracy of sin(Latitude) */\n  var genau = 1e-12;\n  var genau2 = (genau * genau);\n  var maxiter = 30;\n\n  var P; /* distance between semi-minor axis and location */\n  var RR; /* distance between center and location */\n  var CT; /* sin of geocentric latitude */\n  var ST; /* cos of geocentric latitude */\n  var RX;\n  var RK;\n  var RN; /* Earth radius at location */\n  var CPHI0; /* cos of start or old geodetic latitude in iterations */\n  var SPHI0; /* sin of start or old geodetic latitude in iterations */\n  var CPHI; /* cos of searched geodetic latitude */\n  var SPHI; /* sin of searched geodetic latitude */\n  var SDPHI; /* end-criterium: addition-theorem of sin(Latitude(iter)-Latitude(iter-1)) */\n  var iter; /* # of continous iteration, max. 30 is always enough (s.a.) */\n\n  var X = p.x;\n  var Y = p.y;\n  var Z = p.z ? p.z : 0.0; //Z value not always supplied\n  var Longitude;\n  var Latitude;\n  var Height;\n\n  P = Math.sqrt(X * X + Y * Y);\n  RR = Math.sqrt(X * X + Y * Y + Z * Z);\n\n  /*      special cases for latitude and longitude */\n  if (P / a < genau) {\n\n    /*  special case, if P=0. (X=0., Y=0.) */\n    Longitude = 0.0;\n\n    /*  if (X,Y,Z)=(0.,0.,0.) then Height becomes semi-minor axis\n     *  of ellipsoid (=center of mass), Latitude becomes PI/2 */\n    if (RR / a < genau) {\n      Latitude = HALF_PI;\n      Height = -b;\n      return {\n        x: p.x,\n        y: p.y,\n        z: p.z\n      };\n    }\n  } else {\n    /*  ellipsoidal (geodetic) longitude\n     *  interval: -PI < Longitude <= +PI */\n    Longitude = Math.atan2(Y, X);\n  }\n\n  /* --------------------------------------------------------------\n   * Following iterative algorithm was developped by\n   * \"Institut for Erdmessung\", University of Hannover, July 1988.\n   * Internet: www.ife.uni-hannover.de\n   * Iterative computation of CPHI,SPHI and Height.\n   * Iteration of CPHI and SPHI to 10**-12 radian resp.\n   * 2*10**-7 arcsec.\n   * --------------------------------------------------------------\n   */\n  CT = Z / RR;\n  ST = P / RR;\n  RX = 1.0 / Math.sqrt(1.0 - es * (2.0 - es) * ST * ST);\n  CPHI0 = ST * (1.0 - es) * RX;\n  SPHI0 = CT * RX;\n  iter = 0;\n\n  /* loop to find sin(Latitude) resp. Latitude\n   * until |sin(Latitude(iter)-Latitude(iter-1))| < genau */\n  do {\n    iter++;\n    RN = a / Math.sqrt(1.0 - es * SPHI0 * SPHI0);\n\n    /*  ellipsoidal (geodetic) height */\n    Height = P * CPHI0 + Z * SPHI0 - RN * (1.0 - es * SPHI0 * SPHI0);\n\n    RK = es * RN / (RN + Height);\n    RX = 1.0 / Math.sqrt(1.0 - RK * (2.0 - RK) * ST * ST);\n    CPHI = ST * (1.0 - RK) * RX;\n    SPHI = CT * RX;\n    SDPHI = SPHI * CPHI0 - CPHI * SPHI0;\n    CPHI0 = CPHI;\n    SPHI0 = SPHI;\n  }\n  while (SDPHI * SDPHI > genau2 && iter < maxiter);\n\n  /*      ellipsoidal (geodetic) latitude */\n  Latitude = Math.atan(SPHI / Math.abs(CPHI));\n  return {\n    x: Longitude,\n    y: Latitude,\n    z: Height\n  };\n} // cs_geocentric_to_geodetic()\n\n/****************************************************************/\n// pj_geocentic_to_wgs84( p )\n//  p = point to transform in geocentric coordinates (x,y,z)\n\n\n/** point object, nothing fancy, just allows values to be\n    passed back and forth by reference rather than by value.\n    Other point classes may be used as long as they have\n    x and y properties, which will get modified in the transform method.\n*/\nfunction geocentricToWgs84(p, datum_type, datum_params) {\n\n  if (datum_type === PJD_3PARAM) {\n    // if( x[io] === HUGE_VAL )\n    //    continue;\n    return {\n      x: p.x + datum_params[0],\n      y: p.y + datum_params[1],\n      z: p.z + datum_params[2],\n    };\n  } else if (datum_type === PJD_7PARAM) {\n    var Dx_BF = datum_params[0];\n    var Dy_BF = datum_params[1];\n    var Dz_BF = datum_params[2];\n    var Rx_BF = datum_params[3];\n    var Ry_BF = datum_params[4];\n    var Rz_BF = datum_params[5];\n    var M_BF = datum_params[6];\n    // if( x[io] === HUGE_VAL )\n    //    continue;\n    return {\n      x: M_BF * (p.x - Rz_BF * p.y + Ry_BF * p.z) + Dx_BF,\n      y: M_BF * (Rz_BF * p.x + p.y - Rx_BF * p.z) + Dy_BF,\n      z: M_BF * (-Ry_BF * p.x + Rx_BF * p.y + p.z) + Dz_BF\n    };\n  }\n} // cs_geocentric_to_wgs84\n\n/****************************************************************/\n// pj_geocentic_from_wgs84()\n//  coordinate system definition,\n//  point to transform in geocentric coordinates (x,y,z)\nfunction geocentricFromWgs84(p, datum_type, datum_params) {\n\n  if (datum_type === PJD_3PARAM) {\n    //if( x[io] === HUGE_VAL )\n    //    continue;\n    return {\n      x: p.x - datum_params[0],\n      y: p.y - datum_params[1],\n      z: p.z - datum_params[2],\n    };\n\n  } else if (datum_type === PJD_7PARAM) {\n    var Dx_BF = datum_params[0];\n    var Dy_BF = datum_params[1];\n    var Dz_BF = datum_params[2];\n    var Rx_BF = datum_params[3];\n    var Ry_BF = datum_params[4];\n    var Rz_BF = datum_params[5];\n    var M_BF = datum_params[6];\n    var x_tmp = (p.x - Dx_BF) / M_BF;\n    var y_tmp = (p.y - Dy_BF) / M_BF;\n    var z_tmp = (p.z - Dz_BF) / M_BF;\n    //if( x[io] === HUGE_VAL )\n    //    continue;\n\n    return {\n      x: x_tmp + Rz_BF * y_tmp - Ry_BF * z_tmp,\n      y: -Rz_BF * x_tmp + y_tmp + Rx_BF * z_tmp,\n      z: Ry_BF * x_tmp - Rx_BF * y_tmp + z_tmp\n    };\n  } //cs_geocentric_from_wgs84()\n}\n\nfunction checkParams(type) {\n  return (type === PJD_3PARAM || type === PJD_7PARAM);\n}\n\nfunction datum_transform(source, dest, point) {\n  // Short cut if the datums are identical.\n  if (compareDatums(source, dest)) {\n    return point; // in this case, zero is sucess,\n    // whereas cs_compare_datums returns 1 to indicate TRUE\n    // confusing, should fix this\n  }\n\n  // Explicitly skip datum transform by setting 'datum=none' as parameter for either source or dest\n  if (source.datum_type === PJD_NODATUM || dest.datum_type === PJD_NODATUM) {\n    return point;\n  }\n\n  // If this datum requires grid shifts, then apply it to geodetic coordinates.\n  var source_a = source.a;\n  var source_es = source.es;\n  if (source.datum_type === PJD_GRIDSHIFT) {\n    var gridShiftCode = applyGridShift(source, false, point);\n    if (gridShiftCode !== 0) {\n      return undefined;\n    }\n    source_a = SRS_WGS84_SEMIMAJOR;\n    source_es = SRS_WGS84_ESQUARED;\n  }\n\n  var dest_a = dest.a;\n  var dest_b = dest.b;\n  var dest_es = dest.es;\n  if (dest.datum_type === PJD_GRIDSHIFT) {\n    dest_a = SRS_WGS84_SEMIMAJOR;\n    dest_b = SRS_WGS84_SEMIMINOR;\n    dest_es = SRS_WGS84_ESQUARED;\n  }\n\n  // Do we need to go through geocentric coordinates?\n  if (source_es === dest_es && source_a === dest_a && !checkParams(source.datum_type) &&  !checkParams(dest.datum_type)) {\n    return point;\n  }\n\n  // Convert to geocentric coordinates.\n  point = geodeticToGeocentric(point, source_es, source_a);\n  // Convert between datums\n  if (checkParams(source.datum_type)) {\n    point = geocentricToWgs84(point, source.datum_type, source.datum_params);\n  }\n  if (checkParams(dest.datum_type)) {\n    point = geocentricFromWgs84(point, dest.datum_type, dest.datum_params);\n  }\n  point = geocentricToGeodetic(point, dest_es, dest_a, dest_b);\n\n  if (dest.datum_type === PJD_GRIDSHIFT) {\n    var destGridShiftResult = applyGridShift(dest, true, point);\n    if (destGridShiftResult !== 0) {\n      return undefined;\n    }\n  }\n\n  return point;\n}\n\nfunction applyGridShift(source, inverse, point) {\n  if (source.grids === null || source.grids.length === 0) {\n    console.log('Grid shift grids not found');\n    return -1;\n  }\n  var input = {x: -point.x, y: point.y};\n  var output = {x: Number.NaN, y: Number.NaN};\n  var attemptedGrids = [];\n  outer:\n  for (var i = 0; i < source.grids.length; i++) {\n    var grid = source.grids[i];\n    attemptedGrids.push(grid.name);\n    if (grid.isNull) {\n      output = input;\n      break;\n    }\n    grid.mandatory;\n    if (grid.grid === null) {\n      if (grid.mandatory) {\n        console.log(\"Unable to find mandatory grid '\" + grid.name + \"'\");\n        return -1;\n      }\n      continue;\n    }\n    var subgrids = grid.grid.subgrids;\n    for (var j = 0, jj = subgrids.length; j < jj; j++) {\n      var subgrid = subgrids[j];\n      // skip tables that don't match our point at all\n      var epsilon = (Math.abs(subgrid.del[1]) + Math.abs(subgrid.del[0])) / 10000.0;\n      var minX = subgrid.ll[0] - epsilon;\n      var minY = subgrid.ll[1] - epsilon;\n      var maxX = subgrid.ll[0] + (subgrid.lim[0] - 1) * subgrid.del[0] + epsilon;\n      var maxY = subgrid.ll[1] + (subgrid.lim[1] - 1) * subgrid.del[1] + epsilon;\n      if (minY > input.y || minX > input.x || maxY < input.y || maxX < input.x ) {\n        continue;\n      }\n      output = applySubgridShift(input, inverse, subgrid);\n      if (!isNaN(output.x)) {\n        break outer;\n      }\n    }\n  }\n  if (isNaN(output.x)) {\n    console.log(\"Failed to find a grid shift table for location '\"+\n      -input.x * R2D + \" \" + input.y * R2D + \" tried: '\" + attemptedGrids + \"'\");\n    return -1;\n  }\n  point.x = -output.x;\n  point.y = output.y;\n  return 0;\n}\n\nfunction applySubgridShift(pin, inverse, ct) {\n  var val = {x: Number.NaN, y: Number.NaN};\n  if (isNaN(pin.x)) { return val; }\n  var tb = {x: pin.x, y: pin.y};\n  tb.x -= ct.ll[0];\n  tb.y -= ct.ll[1];\n  tb.x = adjust_lon(tb.x - Math.PI) + Math.PI;\n  var t = nadInterpolate(tb, ct);\n  if (inverse) {\n    if (isNaN(t.x)) {\n      return val;\n    }\n    t.x = tb.x - t.x;\n    t.y = tb.y - t.y;\n    var i = 9, tol = 1e-12;\n    var dif, del;\n    do {\n      del = nadInterpolate(t, ct);\n      if (isNaN(del.x)) {\n        console.log(\"Inverse grid shift iteration failed, presumably at grid edge.  Using first approximation.\");\n        break;\n      }\n      dif = {x: tb.x - (del.x + t.x), y: tb.y - (del.y + t.y)};\n      t.x += dif.x;\n      t.y += dif.y;\n    } while (i-- && Math.abs(dif.x) > tol && Math.abs(dif.y) > tol);\n    if (i < 0) {\n      console.log(\"Inverse grid shift iterator failed to converge.\");\n      return val;\n    }\n    val.x = adjust_lon(t.x + ct.ll[0]);\n    val.y = t.y + ct.ll[1];\n  } else {\n    if (!isNaN(t.x)) {\n      val.x = pin.x + t.x;\n      val.y = pin.y + t.y;\n    }\n  }\n  return val;\n}\n\nfunction nadInterpolate(pin, ct) {\n  var t = {x: pin.x / ct.del[0], y: pin.y / ct.del[1]};\n  var indx = {x: Math.floor(t.x), y: Math.floor(t.y)};\n  var frct = {x: t.x - 1.0 * indx.x, y: t.y - 1.0 * indx.y};\n  var val= {x: Number.NaN, y: Number.NaN};\n  var inx;\n  if (indx.x < 0 || indx.x >= ct.lim[0]) {\n    return val;\n  }\n  if (indx.y < 0 || indx.y >= ct.lim[1]) {\n    return val;\n  }\n  inx = (indx.y * ct.lim[0]) + indx.x;\n  var f00 = {x: ct.cvs[inx][0], y: ct.cvs[inx][1]};\n  inx++;\n  var f10= {x: ct.cvs[inx][0], y: ct.cvs[inx][1]};\n  inx += ct.lim[0];\n  var f11 = {x: ct.cvs[inx][0], y: ct.cvs[inx][1]};\n  inx--;\n  var f01 = {x: ct.cvs[inx][0], y: ct.cvs[inx][1]};\n  var m11 = frct.x * frct.y, m10 = frct.x * (1.0 - frct.y),\n    m00 = (1.0 - frct.x) * (1.0 - frct.y), m01 = (1.0 - frct.x) * frct.y;\n  val.x = (m00 * f00.x + m10 * f10.x + m01 * f01.x + m11 * f11.x);\n  val.y = (m00 * f00.y + m10 * f10.y + m01 * f01.y + m11 * f11.y);\n  return val;\n}\n\nfunction adjust_axis(crs, denorm, point) {\n  var xin = point.x,\n    yin = point.y,\n    zin = point.z || 0.0;\n  var v, t, i;\n  var out = {};\n  for (i = 0; i < 3; i++) {\n    if (denorm && i === 2 && point.z === undefined) {\n      continue;\n    }\n    if (i === 0) {\n      v = xin;\n      if (\"ew\".indexOf(crs.axis[i]) !== -1) {\n        t = 'x';\n      } else {\n        t = 'y';\n      }\n\n    }\n    else if (i === 1) {\n      v = yin;\n      if (\"ns\".indexOf(crs.axis[i]) !== -1) {\n        t = 'y';\n      } else {\n        t = 'x';\n      }\n    }\n    else {\n      v = zin;\n      t = 'z';\n    }\n    switch (crs.axis[i]) {\n    case 'e':\n      out[t] = v;\n      break;\n    case 'w':\n      out[t] = -v;\n      break;\n    case 'n':\n      out[t] = v;\n      break;\n    case 's':\n      out[t] = -v;\n      break;\n    case 'u':\n      if (point[t] !== undefined) {\n        out.z = v;\n      }\n      break;\n    case 'd':\n      if (point[t] !== undefined) {\n        out.z = -v;\n      }\n      break;\n    default:\n      //console.log(\"ERROR: unknow axis (\"+crs.axis[i]+\") - check definition of \"+crs.projName);\n      return null;\n    }\n  }\n  return out;\n}\n\nfunction common (array){\n  var out = {\n    x: array[0],\n    y: array[1]\n  };\n  if (array.length>2) {\n    out.z = array[2];\n  }\n  if (array.length>3) {\n    out.m = array[3];\n  }\n  return out;\n}\n\nfunction checkSanity (point) {\n  checkCoord(point.x);\n  checkCoord(point.y);\n}\nfunction checkCoord(num) {\n  if (typeof Number.isFinite === 'function') {\n    if (Number.isFinite(num)) {\n      return;\n    }\n    throw new TypeError('coordinates must be finite numbers');\n  }\n  if (typeof num !== 'number' || num !== num || !isFinite(num)) {\n    throw new TypeError('coordinates must be finite numbers');\n  }\n}\n\nfunction checkNotWGS(source, dest) {\n  return (\n    (source.datum.datum_type === PJD_3PARAM || source.datum.datum_type === PJD_7PARAM || source.datum.datum_type === PJD_GRIDSHIFT) && dest.datumCode !== 'WGS84') ||\n    ((dest.datum.datum_type === PJD_3PARAM || dest.datum.datum_type === PJD_7PARAM || dest.datum.datum_type === PJD_GRIDSHIFT) && source.datumCode !== 'WGS84');\n}\n\nfunction transform(source, dest, point, enforceAxis) {\n  var wgs84;\n  if (Array.isArray(point)) {\n    point = common(point);\n  } else {\n    // Clone the point object so inputs don't get modified\n    point = {\n      x: point.x,\n      y: point.y,\n      z: point.z,\n      m: point.m\n    };\n  }\n  var hasZ = point.z !== undefined;\n  checkSanity(point);\n  // Workaround for datum shifts towgs84, if either source or destination projection is not wgs84\n  if (source.datum && dest.datum && checkNotWGS(source, dest)) {\n    wgs84 = new Projection('WGS84');\n    point = transform(source, wgs84, point, enforceAxis);\n    source = wgs84;\n  }\n  // DGR, 2010/11/12\n  if (enforceAxis && source.axis !== 'enu') {\n    point = adjust_axis(source, false, point);\n  }\n  // Transform source points to long/lat, if they aren't already.\n  if (source.projName === 'longlat') {\n    point = {\n      x: point.x * D2R$1,\n      y: point.y * D2R$1,\n      z: point.z || 0\n    };\n  } else {\n    if (source.to_meter) {\n      point = {\n        x: point.x * source.to_meter,\n        y: point.y * source.to_meter,\n        z: point.z || 0\n      };\n    }\n    point = source.inverse(point); // Convert Cartesian to longlat\n    if (!point) {\n      return;\n    }\n  }\n  // Adjust for the prime meridian if necessary\n  if (source.from_greenwich) {\n    point.x += source.from_greenwich;\n  }\n\n  // Convert datums if needed, and if possible.\n  point = datum_transform(source.datum, dest.datum, point);\n  if (!point) {\n    return;\n  }\n\n  // Adjust for the prime meridian if necessary\n  if (dest.from_greenwich) {\n    point = {\n      x: point.x - dest.from_greenwich,\n      y: point.y,\n      z: point.z || 0\n    };\n  }\n\n  if (dest.projName === 'longlat') {\n    // convert radians to decimal degrees\n    point = {\n      x: point.x * R2D,\n      y: point.y * R2D,\n      z: point.z || 0\n    };\n  } else { // else project\n    point = dest.forward(point);\n    if (dest.to_meter) {\n      point = {\n        x: point.x / dest.to_meter,\n        y: point.y / dest.to_meter,\n        z: point.z || 0\n      };\n    }\n  }\n\n  // DGR, 2010/11/12\n  if (enforceAxis && dest.axis !== 'enu') {\n    return adjust_axis(dest, true, point);\n  }\n\n  if (point && !hasZ) {\n    delete point.z;\n  }\n  return point;\n}\n\nvar wgs84 = Projection('WGS84');\n\nfunction transformer(from, to, coords, enforceAxis) {\n  var transformedArray, out, keys;\n  if (Array.isArray(coords)) {\n    transformedArray = transform(from, to, coords, enforceAxis) || {x: NaN, y: NaN};\n    if (coords.length > 2) {\n      if ((typeof from.name !== 'undefined' && from.name === 'geocent') || (typeof to.name !== 'undefined' && to.name === 'geocent')) {\n        if (typeof transformedArray.z === 'number') {\n          return [transformedArray.x, transformedArray.y, transformedArray.z].concat(coords.splice(3));\n        } else {\n          return [transformedArray.x, transformedArray.y, coords[2]].concat(coords.splice(3));\n        }\n      } else {\n        return [transformedArray.x, transformedArray.y].concat(coords.splice(2));\n      }\n    } else {\n      return [transformedArray.x, transformedArray.y];\n    }\n  } else {\n    out = transform(from, to, coords, enforceAxis);\n    keys = Object.keys(coords);\n    if (keys.length === 2) {\n      return out;\n    }\n    keys.forEach(function (key) {\n      if ((typeof from.name !== 'undefined' && from.name === 'geocent') || (typeof to.name !== 'undefined' && to.name === 'geocent')) {\n        if (key === 'x' || key === 'y' || key === 'z') {\n          return;\n        }\n      } else {\n        if (key === 'x' || key === 'y') {\n          return;\n        }\n      }\n      out[key] = coords[key];\n    });\n    return out;\n  }\n}\n\nfunction checkProj(item) {\n  if (item instanceof Projection) {\n    return item;\n  }\n  if (item.oProj) {\n    return item.oProj;\n  }\n  return Projection(item);\n}\n\nfunction proj4(fromProj, toProj, coord) {\n  fromProj = checkProj(fromProj);\n  var single = false;\n  var obj;\n  if (typeof toProj === 'undefined') {\n    toProj = fromProj;\n    fromProj = wgs84;\n    single = true;\n  } else if (typeof toProj.x !== 'undefined' || Array.isArray(toProj)) {\n    coord = toProj;\n    toProj = fromProj;\n    fromProj = wgs84;\n    single = true;\n  }\n  toProj = checkProj(toProj);\n  if (coord) {\n    return transformer(fromProj, toProj, coord);\n  } else {\n    obj = {\n      forward: function (coords, enforceAxis) {\n        return transformer(fromProj, toProj, coords, enforceAxis);\n      },\n      inverse: function (coords, enforceAxis) {\n        return transformer(toProj, fromProj, coords, enforceAxis);\n      }\n    };\n    if (single) {\n      obj.oProj = toProj;\n    }\n    return obj;\n  }\n}\n\n/**\n * UTM zones are grouped, and assigned to one of a group of 6\n * sets.\n *\n * {int} @private\n */\nvar NUM_100K_SETS = 6;\n\n/**\n * The column letters (for easting) of the lower left value, per\n * set.\n *\n * {string} @private\n */\nvar SET_ORIGIN_COLUMN_LETTERS = 'AJSAJS';\n\n/**\n * The row letters (for northing) of the lower left value, per\n * set.\n *\n * {string} @private\n */\nvar SET_ORIGIN_ROW_LETTERS = 'AFAFAF';\n\nvar A$1 = 65; // A\nvar I = 73; // I\nvar O = 79; // O\nvar V = 86; // V\nvar Z = 90; // Z\nvar mgrs = {\n  forward: forward$t,\n  inverse: inverse$t,\n  toPoint: toPoint\n};\n/**\n * Conversion of lat/lon to MGRS.\n *\n * @param {object} ll Object literal with lat and lon properties on a\n *     WGS84 ellipsoid.\n * @param {int} accuracy Accuracy in digits (5 for 1 m, 4 for 10 m, 3 for\n *      100 m, 2 for 1000 m or 1 for 10000 m). Optional, default is 5.\n * @return {string} the MGRS string for the given location and accuracy.\n */\nfunction forward$t(ll, accuracy) {\n  accuracy = accuracy || 5; // default accuracy 1m\n  return encode(LLtoUTM({\n    lat: ll[1],\n    lon: ll[0]\n  }), accuracy);\n}\n/**\n * Conversion of MGRS to lat/lon.\n *\n * @param {string} mgrs MGRS string.\n * @return {array} An array with left (longitude), bottom (latitude), right\n *     (longitude) and top (latitude) values in WGS84, representing the\n *     bounding box for the provided MGRS reference.\n */\nfunction inverse$t(mgrs) {\n  var bbox = UTMtoLL(decode(mgrs.toUpperCase()));\n  if (bbox.lat && bbox.lon) {\n    return [bbox.lon, bbox.lat, bbox.lon, bbox.lat];\n  }\n  return [bbox.left, bbox.bottom, bbox.right, bbox.top];\n}\nfunction toPoint(mgrs) {\n  var bbox = UTMtoLL(decode(mgrs.toUpperCase()));\n  if (bbox.lat && bbox.lon) {\n    return [bbox.lon, bbox.lat];\n  }\n  return [(bbox.left + bbox.right) / 2, (bbox.top + bbox.bottom) / 2];\n}/**\n * Conversion from degrees to radians.\n *\n * @private\n * @param {number} deg the angle in degrees.\n * @return {number} the angle in radians.\n */\nfunction degToRad(deg) {\n  return (deg * (Math.PI / 180.0));\n}\n\n/**\n * Conversion from radians to degrees.\n *\n * @private\n * @param {number} rad the angle in radians.\n * @return {number} the angle in degrees.\n */\nfunction radToDeg(rad) {\n  return (180.0 * (rad / Math.PI));\n}\n\n/**\n * Converts a set of Longitude and Latitude co-ordinates to UTM\n * using the WGS84 ellipsoid.\n *\n * @private\n * @param {object} ll Object literal with lat and lon properties\n *     representing the WGS84 coordinate to be converted.\n * @return {object} Object literal containing the UTM value with easting,\n *     northing, zoneNumber and zoneLetter properties, and an optional\n *     accuracy property in digits. Returns null if the conversion failed.\n */\nfunction LLtoUTM(ll) {\n  var Lat = ll.lat;\n  var Long = ll.lon;\n  var a = 6378137.0; //ellip.radius;\n  var eccSquared = 0.00669438; //ellip.eccsq;\n  var k0 = 0.9996;\n  var LongOrigin;\n  var eccPrimeSquared;\n  var N, T, C, A, M;\n  var LatRad = degToRad(Lat);\n  var LongRad = degToRad(Long);\n  var LongOriginRad;\n  var ZoneNumber;\n  // (int)\n  ZoneNumber = Math.floor((Long + 180) / 6) + 1;\n\n  //Make sure the longitude 180.00 is in Zone 60\n  if (Long === 180) {\n    ZoneNumber = 60;\n  }\n\n  // Special zone for Norway\n  if (Lat >= 56.0 && Lat < 64.0 && Long >= 3.0 && Long < 12.0) {\n    ZoneNumber = 32;\n  }\n\n  // Special zones for Svalbard\n  if (Lat >= 72.0 && Lat < 84.0) {\n    if (Long >= 0.0 && Long < 9.0) {\n      ZoneNumber = 31;\n    }\n    else if (Long >= 9.0 && Long < 21.0) {\n      ZoneNumber = 33;\n    }\n    else if (Long >= 21.0 && Long < 33.0) {\n      ZoneNumber = 35;\n    }\n    else if (Long >= 33.0 && Long < 42.0) {\n      ZoneNumber = 37;\n    }\n  }\n\n  LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3; //+3 puts origin\n  // in middle of\n  // zone\n  LongOriginRad = degToRad(LongOrigin);\n\n  eccPrimeSquared = (eccSquared) / (1 - eccSquared);\n\n  N = a / Math.sqrt(1 - eccSquared * Math.sin(LatRad) * Math.sin(LatRad));\n  T = Math.tan(LatRad) * Math.tan(LatRad);\n  C = eccPrimeSquared * Math.cos(LatRad) * Math.cos(LatRad);\n  A = Math.cos(LatRad) * (LongRad - LongOriginRad);\n\n  M = a * ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256) * LatRad - (3 * eccSquared / 8 + 3 * eccSquared * eccSquared / 32 + 45 * eccSquared * eccSquared * eccSquared / 1024) * Math.sin(2 * LatRad) + (15 * eccSquared * eccSquared / 256 + 45 * eccSquared * eccSquared * eccSquared / 1024) * Math.sin(4 * LatRad) - (35 * eccSquared * eccSquared * eccSquared / 3072) * Math.sin(6 * LatRad));\n\n  var UTMEasting = (k0 * N * (A + (1 - T + C) * A * A * A / 6.0 + (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120.0) + 500000.0);\n\n  var UTMNorthing = (k0 * (M + N * Math.tan(LatRad) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24.0 + (61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720.0)));\n  if (Lat < 0.0) {\n    UTMNorthing += 10000000.0; //10000000 meter offset for\n    // southern hemisphere\n  }\n\n  return {\n    northing: Math.round(UTMNorthing),\n    easting: Math.round(UTMEasting),\n    zoneNumber: ZoneNumber,\n    zoneLetter: getLetterDesignator(Lat)\n  };\n}\n\n/**\n * Converts UTM coords to lat/long, using the WGS84 ellipsoid. This is a convenience\n * class where the Zone can be specified as a single string eg.\"60N\" which\n * is then broken down into the ZoneNumber and ZoneLetter.\n *\n * @private\n * @param {object} utm An object literal with northing, easting, zoneNumber\n *     and zoneLetter properties. If an optional accuracy property is\n *     provided (in meters), a bounding box will be returned instead of\n *     latitude and longitude.\n * @return {object} An object literal containing either lat and lon values\n *     (if no accuracy was provided), or top, right, bottom and left values\n *     for the bounding box calculated according to the provided accuracy.\n *     Returns null if the conversion failed.\n */\nfunction UTMtoLL(utm) {\n\n  var UTMNorthing = utm.northing;\n  var UTMEasting = utm.easting;\n  var zoneLetter = utm.zoneLetter;\n  var zoneNumber = utm.zoneNumber;\n  // check the ZoneNummber is valid\n  if (zoneNumber < 0 || zoneNumber > 60) {\n    return null;\n  }\n\n  var k0 = 0.9996;\n  var a = 6378137.0; //ellip.radius;\n  var eccSquared = 0.00669438; //ellip.eccsq;\n  var eccPrimeSquared;\n  var e1 = (1 - Math.sqrt(1 - eccSquared)) / (1 + Math.sqrt(1 - eccSquared));\n  var N1, T1, C1, R1, D, M;\n  var LongOrigin;\n  var mu, phi1Rad;\n\n  // remove 500,000 meter offset for longitude\n  var x = UTMEasting - 500000.0;\n  var y = UTMNorthing;\n\n  // We must know somehow if we are in the Northern or Southern\n  // hemisphere, this is the only time we use the letter So even\n  // if the Zone letter isn't exactly correct it should indicate\n  // the hemisphere correctly\n  if (zoneLetter < 'N') {\n    y -= 10000000.0; // remove 10,000,000 meter offset used\n    // for southern hemisphere\n  }\n\n  // There are 60 zones with zone 1 being at West -180 to -174\n  LongOrigin = (zoneNumber - 1) * 6 - 180 + 3; // +3 puts origin\n  // in middle of\n  // zone\n\n  eccPrimeSquared = (eccSquared) / (1 - eccSquared);\n\n  M = y / k0;\n  mu = M / (a * (1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256));\n\n  phi1Rad = mu + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * Math.sin(2 * mu) + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * Math.sin(4 * mu) + (151 * e1 * e1 * e1 / 96) * Math.sin(6 * mu);\n  // double phi1 = ProjMath.radToDeg(phi1Rad);\n\n  N1 = a / Math.sqrt(1 - eccSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad));\n  T1 = Math.tan(phi1Rad) * Math.tan(phi1Rad);\n  C1 = eccPrimeSquared * Math.cos(phi1Rad) * Math.cos(phi1Rad);\n  R1 = a * (1 - eccSquared) / Math.pow(1 - eccSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad), 1.5);\n  D = x / (N1 * k0);\n\n  var lat = phi1Rad - (N1 * Math.tan(phi1Rad) / R1) * (D * D / 2 - (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * eccPrimeSquared) * D * D * D * D / 24 + (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * eccPrimeSquared - 3 * C1 * C1) * D * D * D * D * D * D / 720);\n  lat = radToDeg(lat);\n\n  var lon = (D - (1 + 2 * T1 + C1) * D * D * D / 6 + (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * eccPrimeSquared + 24 * T1 * T1) * D * D * D * D * D / 120) / Math.cos(phi1Rad);\n  lon = LongOrigin + radToDeg(lon);\n\n  var result;\n  if (utm.accuracy) {\n    var topRight = UTMtoLL({\n      northing: utm.northing + utm.accuracy,\n      easting: utm.easting + utm.accuracy,\n      zoneLetter: utm.zoneLetter,\n      zoneNumber: utm.zoneNumber\n    });\n    result = {\n      top: topRight.lat,\n      right: topRight.lon,\n      bottom: lat,\n      left: lon\n    };\n  }\n  else {\n    result = {\n      lat: lat,\n      lon: lon\n    };\n  }\n  return result;\n}\n\n/**\n * Calculates the MGRS letter designator for the given latitude.\n *\n * @private\n * @param {number} lat The latitude in WGS84 to get the letter designator\n *     for.\n * @return {char} The letter designator.\n */\nfunction getLetterDesignator(lat) {\n  //This is here as an error flag to show that the Latitude is\n  //outside MGRS limits\n  var LetterDesignator = 'Z';\n\n  if ((84 >= lat) && (lat >= 72)) {\n    LetterDesignator = 'X';\n  }\n  else if ((72 > lat) && (lat >= 64)) {\n    LetterDesignator = 'W';\n  }\n  else if ((64 > lat) && (lat >= 56)) {\n    LetterDesignator = 'V';\n  }\n  else if ((56 > lat) && (lat >= 48)) {\n    LetterDesignator = 'U';\n  }\n  else if ((48 > lat) && (lat >= 40)) {\n    LetterDesignator = 'T';\n  }\n  else if ((40 > lat) && (lat >= 32)) {\n    LetterDesignator = 'S';\n  }\n  else if ((32 > lat) && (lat >= 24)) {\n    LetterDesignator = 'R';\n  }\n  else if ((24 > lat) && (lat >= 16)) {\n    LetterDesignator = 'Q';\n  }\n  else if ((16 > lat) && (lat >= 8)) {\n    LetterDesignator = 'P';\n  }\n  else if ((8 > lat) && (lat >= 0)) {\n    LetterDesignator = 'N';\n  }\n  else if ((0 > lat) && (lat >= -8)) {\n    LetterDesignator = 'M';\n  }\n  else if ((-8 > lat) && (lat >= -16)) {\n    LetterDesignator = 'L';\n  }\n  else if ((-16 > lat) && (lat >= -24)) {\n    LetterDesignator = 'K';\n  }\n  else if ((-24 > lat) && (lat >= -32)) {\n    LetterDesignator = 'J';\n  }\n  else if ((-32 > lat) && (lat >= -40)) {\n    LetterDesignator = 'H';\n  }\n  else if ((-40 > lat) && (lat >= -48)) {\n    LetterDesignator = 'G';\n  }\n  else if ((-48 > lat) && (lat >= -56)) {\n    LetterDesignator = 'F';\n  }\n  else if ((-56 > lat) && (lat >= -64)) {\n    LetterDesignator = 'E';\n  }\n  else if ((-64 > lat) && (lat >= -72)) {\n    LetterDesignator = 'D';\n  }\n  else if ((-72 > lat) && (lat >= -80)) {\n    LetterDesignator = 'C';\n  }\n  return LetterDesignator;\n}\n\n/**\n * Encodes a UTM location as MGRS string.\n *\n * @private\n * @param {object} utm An object literal with easting, northing,\n *     zoneLetter, zoneNumber\n * @param {number} accuracy Accuracy in digits (1-5).\n * @return {string} MGRS string for the given UTM location.\n */\nfunction encode(utm, accuracy) {\n  // prepend with leading zeroes\n  var seasting = \"00000\" + utm.easting,\n    snorthing = \"00000\" + utm.northing;\n\n  return utm.zoneNumber + utm.zoneLetter + get100kID(utm.easting, utm.northing, utm.zoneNumber) + seasting.substr(seasting.length - 5, accuracy) + snorthing.substr(snorthing.length - 5, accuracy);\n}\n\n/**\n * Get the two letter 100k designator for a given UTM easting,\n * northing and zone number value.\n *\n * @private\n * @param {number} easting\n * @param {number} northing\n * @param {number} zoneNumber\n * @return the two letter 100k designator for the given UTM location.\n */\nfunction get100kID(easting, northing, zoneNumber) {\n  var setParm = get100kSetForZone(zoneNumber);\n  var setColumn = Math.floor(easting / 100000);\n  var setRow = Math.floor(northing / 100000) % 20;\n  return getLetter100kID(setColumn, setRow, setParm);\n}\n\n/**\n * Given a UTM zone number, figure out the MGRS 100K set it is in.\n *\n * @private\n * @param {number} i An UTM zone number.\n * @return {number} the 100k set the UTM zone is in.\n */\nfunction get100kSetForZone(i) {\n  var setParm = i % NUM_100K_SETS;\n  if (setParm === 0) {\n    setParm = NUM_100K_SETS;\n  }\n\n  return setParm;\n}\n\n/**\n * Get the two-letter MGRS 100k designator given information\n * translated from the UTM northing, easting and zone number.\n *\n * @private\n * @param {number} column the column index as it relates to the MGRS\n *        100k set spreadsheet, created from the UTM easting.\n *        Values are 1-8.\n * @param {number} row the row index as it relates to the MGRS 100k set\n *        spreadsheet, created from the UTM northing value. Values\n *        are from 0-19.\n * @param {number} parm the set block, as it relates to the MGRS 100k set\n *        spreadsheet, created from the UTM zone. Values are from\n *        1-60.\n * @return two letter MGRS 100k code.\n */\nfunction getLetter100kID(column, row, parm) {\n  // colOrigin and rowOrigin are the letters at the origin of the set\n  var index = parm - 1;\n  var colOrigin = SET_ORIGIN_COLUMN_LETTERS.charCodeAt(index);\n  var rowOrigin = SET_ORIGIN_ROW_LETTERS.charCodeAt(index);\n\n  // colInt and rowInt are the letters to build to return\n  var colInt = colOrigin + column - 1;\n  var rowInt = rowOrigin + row;\n  var rollover = false;\n\n  if (colInt > Z) {\n    colInt = colInt - Z + A$1 - 1;\n    rollover = true;\n  }\n\n  if (colInt === I || (colOrigin < I && colInt > I) || ((colInt > I || colOrigin < I) && rollover)) {\n    colInt++;\n  }\n\n  if (colInt === O || (colOrigin < O && colInt > O) || ((colInt > O || colOrigin < O) && rollover)) {\n    colInt++;\n\n    if (colInt === I) {\n      colInt++;\n    }\n  }\n\n  if (colInt > Z) {\n    colInt = colInt - Z + A$1 - 1;\n  }\n\n  if (rowInt > V) {\n    rowInt = rowInt - V + A$1 - 1;\n    rollover = true;\n  }\n  else {\n    rollover = false;\n  }\n\n  if (((rowInt === I) || ((rowOrigin < I) && (rowInt > I))) || (((rowInt > I) || (rowOrigin < I)) && rollover)) {\n    rowInt++;\n  }\n\n  if (((rowInt === O) || ((rowOrigin < O) && (rowInt > O))) || (((rowInt > O) || (rowOrigin < O)) && rollover)) {\n    rowInt++;\n\n    if (rowInt === I) {\n      rowInt++;\n    }\n  }\n\n  if (rowInt > V) {\n    rowInt = rowInt - V + A$1 - 1;\n  }\n\n  var twoLetter = String.fromCharCode(colInt) + String.fromCharCode(rowInt);\n  return twoLetter;\n}\n\n/**\n * Decode the UTM parameters from a MGRS string.\n *\n * @private\n * @param {string} mgrsString an UPPERCASE coordinate string is expected.\n * @return {object} An object literal with easting, northing, zoneLetter,\n *     zoneNumber and accuracy (in meters) properties.\n */\nfunction decode(mgrsString) {\n\n  if (mgrsString && mgrsString.length === 0) {\n    throw (\"MGRSPoint coverting from nothing\");\n  }\n\n  var length = mgrsString.length;\n\n  var hunK = null;\n  var sb = \"\";\n  var testChar;\n  var i = 0;\n\n  // get Zone number\n  while (!(/[A-Z]/).test(testChar = mgrsString.charAt(i))) {\n    if (i >= 2) {\n      throw (\"MGRSPoint bad conversion from: \" + mgrsString);\n    }\n    sb += testChar;\n    i++;\n  }\n\n  var zoneNumber = parseInt(sb, 10);\n\n  if (i === 0 || i + 3 > length) {\n    // A good MGRS string has to be 4-5 digits long,\n    // ##AAA/#AAA at least.\n    throw (\"MGRSPoint bad conversion from: \" + mgrsString);\n  }\n\n  var zoneLetter = mgrsString.charAt(i++);\n\n  // Should we check the zone letter here? Why not.\n  if (zoneLetter <= 'A' || zoneLetter === 'B' || zoneLetter === 'Y' || zoneLetter >= 'Z' || zoneLetter === 'I' || zoneLetter === 'O') {\n    throw (\"MGRSPoint zone letter \" + zoneLetter + \" not handled: \" + mgrsString);\n  }\n\n  hunK = mgrsString.substring(i, i += 2);\n\n  var set = get100kSetForZone(zoneNumber);\n\n  var east100k = getEastingFromChar(hunK.charAt(0), set);\n  var north100k = getNorthingFromChar(hunK.charAt(1), set);\n\n  // We have a bug where the northing may be 2000000 too low.\n  // How\n  // do we know when to roll over?\n\n  while (north100k < getMinNorthing(zoneLetter)) {\n    north100k += 2000000;\n  }\n\n  // calculate the char index for easting/northing separator\n  var remainder = length - i;\n\n  if (remainder % 2 !== 0) {\n    throw (\"MGRSPoint has to have an even number \\nof digits after the zone letter and two 100km letters - front \\nhalf for easting meters, second half for \\nnorthing meters\" + mgrsString);\n  }\n\n  var sep = remainder / 2;\n\n  var sepEasting = 0.0;\n  var sepNorthing = 0.0;\n  var accuracyBonus, sepEastingString, sepNorthingString, easting, northing;\n  if (sep > 0) {\n    accuracyBonus = 100000.0 / Math.pow(10, sep);\n    sepEastingString = mgrsString.substring(i, i + sep);\n    sepEasting = parseFloat(sepEastingString) * accuracyBonus;\n    sepNorthingString = mgrsString.substring(i + sep);\n    sepNorthing = parseFloat(sepNorthingString) * accuracyBonus;\n  }\n\n  easting = sepEasting + east100k;\n  northing = sepNorthing + north100k;\n\n  return {\n    easting: easting,\n    northing: northing,\n    zoneLetter: zoneLetter,\n    zoneNumber: zoneNumber,\n    accuracy: accuracyBonus\n  };\n}\n\n/**\n * Given the first letter from a two-letter MGRS 100k zone, and given the\n * MGRS table set for the zone number, figure out the easting value that\n * should be added to the other, secondary easting value.\n *\n * @private\n * @param {char} e The first letter from a two-letter MGRS 100´k zone.\n * @param {number} set The MGRS table set for the zone number.\n * @return {number} The easting value for the given letter and set.\n */\nfunction getEastingFromChar(e, set) {\n  // colOrigin is the letter at the origin of the set for the\n  // column\n  var curCol = SET_ORIGIN_COLUMN_LETTERS.charCodeAt(set - 1);\n  var eastingValue = 100000.0;\n  var rewindMarker = false;\n\n  while (curCol !== e.charCodeAt(0)) {\n    curCol++;\n    if (curCol === I) {\n      curCol++;\n    }\n    if (curCol === O) {\n      curCol++;\n    }\n    if (curCol > Z) {\n      if (rewindMarker) {\n        throw (\"Bad character: \" + e);\n      }\n      curCol = A$1;\n      rewindMarker = true;\n    }\n    eastingValue += 100000.0;\n  }\n\n  return eastingValue;\n}\n\n/**\n * Given the second letter from a two-letter MGRS 100k zone, and given the\n * MGRS table set for the zone number, figure out the northing value that\n * should be added to the other, secondary northing value. You have to\n * remember that Northings are determined from the equator, and the vertical\n * cycle of letters mean a 2000000 additional northing meters. This happens\n * approx. every 18 degrees of latitude. This method does *NOT* count any\n * additional northings. You have to figure out how many 2000000 meters need\n * to be added for the zone letter of the MGRS coordinate.\n *\n * @private\n * @param {char} n Second letter of the MGRS 100k zone\n * @param {number} set The MGRS table set number, which is dependent on the\n *     UTM zone number.\n * @return {number} The northing value for the given letter and set.\n */\nfunction getNorthingFromChar(n, set) {\n\n  if (n > 'V') {\n    throw (\"MGRSPoint given invalid Northing \" + n);\n  }\n\n  // rowOrigin is the letter at the origin of the set for the\n  // column\n  var curRow = SET_ORIGIN_ROW_LETTERS.charCodeAt(set - 1);\n  var northingValue = 0.0;\n  var rewindMarker = false;\n\n  while (curRow !== n.charCodeAt(0)) {\n    curRow++;\n    if (curRow === I) {\n      curRow++;\n    }\n    if (curRow === O) {\n      curRow++;\n    }\n    // fixing a bug making whole application hang in this loop\n    // when 'n' is a wrong character\n    if (curRow > V) {\n      if (rewindMarker) { // making sure that this loop ends\n        throw (\"Bad character: \" + n);\n      }\n      curRow = A$1;\n      rewindMarker = true;\n    }\n    northingValue += 100000.0;\n  }\n\n  return northingValue;\n}\n\n/**\n * The function getMinNorthing returns the minimum northing value of a MGRS\n * zone.\n *\n * Ported from Geotrans' c Lattitude_Band_Value structure table.\n *\n * @private\n * @param {char} zoneLetter The MGRS zone to get the min northing for.\n * @return {number}\n */\nfunction getMinNorthing(zoneLetter) {\n  var northing;\n  switch (zoneLetter) {\n  case 'C':\n    northing = 1100000.0;\n    break;\n  case 'D':\n    northing = 2000000.0;\n    break;\n  case 'E':\n    northing = 2800000.0;\n    break;\n  case 'F':\n    northing = 3700000.0;\n    break;\n  case 'G':\n    northing = 4600000.0;\n    break;\n  case 'H':\n    northing = 5500000.0;\n    break;\n  case 'J':\n    northing = 6400000.0;\n    break;\n  case 'K':\n    northing = 7300000.0;\n    break;\n  case 'L':\n    northing = 8200000.0;\n    break;\n  case 'M':\n    northing = 9100000.0;\n    break;\n  case 'N':\n    northing = 0.0;\n    break;\n  case 'P':\n    northing = 800000.0;\n    break;\n  case 'Q':\n    northing = 1700000.0;\n    break;\n  case 'R':\n    northing = 2600000.0;\n    break;\n  case 'S':\n    northing = 3500000.0;\n    break;\n  case 'T':\n    northing = 4400000.0;\n    break;\n  case 'U':\n    northing = 5300000.0;\n    break;\n  case 'V':\n    northing = 6200000.0;\n    break;\n  case 'W':\n    northing = 7000000.0;\n    break;\n  case 'X':\n    northing = 7900000.0;\n    break;\n  default:\n    northing = -1.0;\n  }\n  if (northing >= 0.0) {\n    return northing;\n  }\n  else {\n    throw (\"Invalid zone letter: \" + zoneLetter);\n  }\n\n}\n\nfunction Point(x, y, z) {\n  if (!(this instanceof Point)) {\n    return new Point(x, y, z);\n  }\n  if (Array.isArray(x)) {\n    this.x = x[0];\n    this.y = x[1];\n    this.z = x[2] || 0.0;\n  } else if(typeof x === 'object') {\n    this.x = x.x;\n    this.y = x.y;\n    this.z = x.z || 0.0;\n  } else if (typeof x === 'string' && typeof y === 'undefined') {\n    var coords = x.split(',');\n    this.x = parseFloat(coords[0], 10);\n    this.y = parseFloat(coords[1], 10);\n    this.z = parseFloat(coords[2], 10) || 0.0;\n  } else {\n    this.x = x;\n    this.y = y;\n    this.z = z || 0.0;\n  }\n  console.warn('proj4.Point will be removed in version 3, use proj4.toPoint');\n}\n\nPoint.fromMGRS = function(mgrsStr) {\n  return new Point(toPoint(mgrsStr));\n};\nPoint.prototype.toMGRS = function(accuracy) {\n  return forward$t([this.x, this.y], accuracy);\n};\n\nvar C00 = 1;\nvar C02 = 0.25;\nvar C04 = 0.046875;\nvar C06 = 0.01953125;\nvar C08 = 0.01068115234375;\nvar C22 = 0.75;\nvar C44 = 0.46875;\nvar C46 = 0.01302083333333333333;\nvar C48 = 0.00712076822916666666;\nvar C66 = 0.36458333333333333333;\nvar C68 = 0.00569661458333333333;\nvar C88 = 0.3076171875;\n\nfunction pj_enfn(es) {\n  var en = [];\n  en[0] = C00 - es * (C02 + es * (C04 + es * (C06 + es * C08)));\n  en[1] = es * (C22 - es * (C04 + es * (C06 + es * C08)));\n  var t = es * es;\n  en[2] = t * (C44 - es * (C46 + es * C48));\n  t *= es;\n  en[3] = t * (C66 - es * C68);\n  en[4] = t * es * C88;\n  return en;\n}\n\nfunction pj_mlfn(phi, sphi, cphi, en) {\n  cphi *= sphi;\n  sphi *= sphi;\n  return (en[0] * phi - cphi * (en[1] + sphi * (en[2] + sphi * (en[3] + sphi * en[4]))));\n}\n\nvar MAX_ITER$3 = 20;\n\nfunction pj_inv_mlfn(arg, es, en) {\n  var k = 1 / (1 - es);\n  var phi = arg;\n  for (var i = MAX_ITER$3; i; --i) { /* rarely goes over 2 iterations */\n    var s = Math.sin(phi);\n    var t = 1 - es * s * s;\n    //t = this.pj_mlfn(phi, s, Math.cos(phi), en) - arg;\n    //phi -= t * (t * Math.sqrt(t)) * k;\n    t = (pj_mlfn(phi, s, Math.cos(phi), en) - arg) * (t * Math.sqrt(t)) * k;\n    phi -= t;\n    if (Math.abs(t) < EPSLN) {\n      return phi;\n    }\n  }\n  //..reportError(\"cass:pj_inv_mlfn: Convergence error\");\n  return phi;\n}\n\n// Heavily based on this tmerc projection implementation\n// https://github.com/mbloch/mapshaper-proj/blob/master/src/projections/tmerc.js\n\n\nfunction init$t() {\n  this.x0 = this.x0 !== undefined ? this.x0 : 0;\n  this.y0 = this.y0 !== undefined ? this.y0 : 0;\n  this.long0 = this.long0 !== undefined ? this.long0 : 0;\n  this.lat0 = this.lat0 !== undefined ? this.lat0 : 0;\n\n  if (this.es) {\n    this.en = pj_enfn(this.es);\n    this.ml0 = pj_mlfn(this.lat0, Math.sin(this.lat0), Math.cos(this.lat0), this.en);\n  }\n}\n\n/**\n    Transverse Mercator Forward  - long/lat to x/y\n    long/lat in radians\n  */\nfunction forward$s(p) {\n  var lon = p.x;\n  var lat = p.y;\n\n  var delta_lon = adjust_lon(lon - this.long0);\n  var con;\n  var x, y;\n  var sin_phi = Math.sin(lat);\n  var cos_phi = Math.cos(lat);\n\n  if (!this.es) {\n    var b = cos_phi * Math.sin(delta_lon);\n\n    if ((Math.abs(Math.abs(b) - 1)) < EPSLN) {\n      return (93);\n    }\n    else {\n      x = 0.5 * this.a * this.k0 * Math.log((1 + b) / (1 - b)) + this.x0;\n      y = cos_phi * Math.cos(delta_lon) / Math.sqrt(1 - Math.pow(b, 2));\n      b = Math.abs(y);\n\n      if (b >= 1) {\n        if ((b - 1) > EPSLN) {\n          return (93);\n        }\n        else {\n          y = 0;\n        }\n      }\n      else {\n        y = Math.acos(y);\n      }\n\n      if (lat < 0) {\n        y = -y;\n      }\n\n      y = this.a * this.k0 * (y - this.lat0) + this.y0;\n    }\n  }\n  else {\n    var al = cos_phi * delta_lon;\n    var als = Math.pow(al, 2);\n    var c = this.ep2 * Math.pow(cos_phi, 2);\n    var cs = Math.pow(c, 2);\n    var tq = Math.abs(cos_phi) > EPSLN ? Math.tan(lat) : 0;\n    var t = Math.pow(tq, 2);\n    var ts = Math.pow(t, 2);\n    con = 1 - this.es * Math.pow(sin_phi, 2);\n    al = al / Math.sqrt(con);\n    var ml = pj_mlfn(lat, sin_phi, cos_phi, this.en);\n\n    x = this.a * (this.k0 * al * (1 +\n      als / 6 * (1 - t + c +\n      als / 20 * (5 - 18 * t + ts + 14 * c - 58 * t * c +\n      als / 42 * (61 + 179 * ts - ts * t - 479 * t))))) +\n      this.x0;\n\n    y = this.a * (this.k0 * (ml - this.ml0 +\n      sin_phi * delta_lon * al / 2 * (1 +\n      als / 12 * (5 - t + 9 * c + 4 * cs +\n      als / 30 * (61 + ts - 58 * t + 270 * c - 330 * t * c +\n      als / 56 * (1385 + 543 * ts - ts * t - 3111 * t)))))) +\n      this.y0;\n  }\n\n  p.x = x;\n  p.y = y;\n\n  return p;\n}\n\n/**\n    Transverse Mercator Inverse  -  x/y to long/lat\n  */\nfunction inverse$s(p) {\n  var con, phi;\n  var lat, lon;\n  var x = (p.x - this.x0) * (1 / this.a);\n  var y = (p.y - this.y0) * (1 / this.a);\n\n  if (!this.es) {\n    var f = Math.exp(x / this.k0);\n    var g = 0.5 * (f - 1 / f);\n    var temp = this.lat0 + y / this.k0;\n    var h = Math.cos(temp);\n    con = Math.sqrt((1 - Math.pow(h, 2)) / (1 + Math.pow(g, 2)));\n    lat = Math.asin(con);\n\n    if (y < 0) {\n      lat = -lat;\n    }\n\n    if ((g === 0) && (h === 0)) {\n      lon = 0;\n    }\n    else {\n      lon = adjust_lon(Math.atan2(g, h) + this.long0);\n    }\n  }\n  else { // ellipsoidal form\n    con = this.ml0 + y / this.k0;\n    phi = pj_inv_mlfn(con, this.es, this.en);\n\n    if (Math.abs(phi) < HALF_PI) {\n      var sin_phi = Math.sin(phi);\n      var cos_phi = Math.cos(phi);\n      var tan_phi = Math.abs(cos_phi) > EPSLN ? Math.tan(phi) : 0;\n      var c = this.ep2 * Math.pow(cos_phi, 2);\n      var cs = Math.pow(c, 2);\n      var t = Math.pow(tan_phi, 2);\n      var ts = Math.pow(t, 2);\n      con = 1 - this.es * Math.pow(sin_phi, 2);\n      var d = x * Math.sqrt(con) / this.k0;\n      var ds = Math.pow(d, 2);\n      con = con * tan_phi;\n\n      lat = phi - (con * ds / (1 - this.es)) * 0.5 * (1 -\n        ds / 12 * (5 + 3 * t - 9 * c * t + c - 4 * cs -\n        ds / 30 * (61 + 90 * t - 252 * c * t + 45 * ts + 46 * c -\n        ds / 56 * (1385 + 3633 * t + 4095 * ts + 1574 * ts * t))));\n\n      lon = adjust_lon(this.long0 + (d * (1 -\n        ds / 6 * (1 + 2 * t + c -\n        ds / 20 * (5 + 28 * t + 24 * ts + 8 * c * t + 6 * c -\n        ds / 42 * (61 + 662 * t + 1320 * ts + 720 * ts * t)))) / cos_phi));\n    }\n    else {\n      lat = HALF_PI * sign(y);\n      lon = 0;\n    }\n  }\n\n  p.x = lon;\n  p.y = lat;\n\n  return p;\n}\n\nvar names$t = [\"Fast_Transverse_Mercator\", \"Fast Transverse Mercator\"];\nvar tmerc = {\n  init: init$t,\n  forward: forward$s,\n  inverse: inverse$s,\n  names: names$t\n};\n\nfunction sinh(x) {\n  var r = Math.exp(x);\n  r = (r - 1 / r) / 2;\n  return r;\n}\n\nfunction hypot(x, y) {\n  x = Math.abs(x);\n  y = Math.abs(y);\n  var a = Math.max(x, y);\n  var b = Math.min(x, y) / (a ? a : 1);\n\n  return a * Math.sqrt(1 + Math.pow(b, 2));\n}\n\nfunction log1py(x) {\n  var y = 1 + x;\n  var z = y - 1;\n\n  return z === 0 ? x : x * Math.log(y) / z;\n}\n\nfunction asinhy(x) {\n  var y = Math.abs(x);\n  y = log1py(y * (1 + y / (hypot(1, y) + 1)));\n\n  return x < 0 ? -y : y;\n}\n\nfunction gatg(pp, B) {\n  var cos_2B = 2 * Math.cos(2 * B);\n  var i = pp.length - 1;\n  var h1 = pp[i];\n  var h2 = 0;\n  var h;\n\n  while (--i >= 0) {\n    h = -h2 + cos_2B * h1 + pp[i];\n    h2 = h1;\n    h1 = h;\n  }\n\n  return (B + h * Math.sin(2 * B));\n}\n\nfunction clens(pp, arg_r) {\n  var r = 2 * Math.cos(arg_r);\n  var i = pp.length - 1;\n  var hr1 = pp[i];\n  var hr2 = 0;\n  var hr;\n\n  while (--i >= 0) {\n    hr = -hr2 + r * hr1 + pp[i];\n    hr2 = hr1;\n    hr1 = hr;\n  }\n\n  return Math.sin(arg_r) * hr;\n}\n\nfunction cosh(x) {\n  var r = Math.exp(x);\n  r = (r + 1 / r) / 2;\n  return r;\n}\n\nfunction clens_cmplx(pp, arg_r, arg_i) {\n  var sin_arg_r = Math.sin(arg_r);\n  var cos_arg_r = Math.cos(arg_r);\n  var sinh_arg_i = sinh(arg_i);\n  var cosh_arg_i = cosh(arg_i);\n  var r = 2 * cos_arg_r * cosh_arg_i;\n  var i = -2 * sin_arg_r * sinh_arg_i;\n  var j = pp.length - 1;\n  var hr = pp[j];\n  var hi1 = 0;\n  var hr1 = 0;\n  var hi = 0;\n  var hr2;\n  var hi2;\n\n  while (--j >= 0) {\n    hr2 = hr1;\n    hi2 = hi1;\n    hr1 = hr;\n    hi1 = hi;\n    hr = -hr2 + r * hr1 - i * hi1 + pp[j];\n    hi = -hi2 + i * hr1 + r * hi1;\n  }\n\n  r = sin_arg_r * cosh_arg_i;\n  i = cos_arg_r * sinh_arg_i;\n\n  return [r * hr - i * hi, r * hi + i * hr];\n}\n\n// Heavily based on this etmerc projection implementation\n// https://github.com/mbloch/mapshaper-proj/blob/master/src/projections/etmerc.js\n\n\nfunction init$s() {\n  if (!this.approx && (isNaN(this.es) || this.es <= 0)) {\n    throw new Error('Incorrect elliptical usage. Try using the +approx option in the proj string, or PROJECTION[\"Fast_Transverse_Mercator\"] in the WKT.');\n  }\n  if (this.approx) {\n    // When '+approx' is set, use tmerc instead\n    tmerc.init.apply(this);\n    this.forward = tmerc.forward;\n    this.inverse = tmerc.inverse;\n  }\n\n  this.x0 = this.x0 !== undefined ? this.x0 : 0;\n  this.y0 = this.y0 !== undefined ? this.y0 : 0;\n  this.long0 = this.long0 !== undefined ? this.long0 : 0;\n  this.lat0 = this.lat0 !== undefined ? this.lat0 : 0;\n\n  this.cgb = [];\n  this.cbg = [];\n  this.utg = [];\n  this.gtu = [];\n\n  var f = this.es / (1 + Math.sqrt(1 - this.es));\n  var n = f / (2 - f);\n  var np = n;\n\n  this.cgb[0] = n * (2 + n * (-2 / 3 + n * (-2 + n * (116 / 45 + n * (26 / 45 + n * (-2854 / 675 ))))));\n  this.cbg[0] = n * (-2 + n * ( 2 / 3 + n * ( 4 / 3 + n * (-82 / 45 + n * (32 / 45 + n * (4642 / 4725))))));\n\n  np = np * n;\n  this.cgb[1] = np * (7 / 3 + n * (-8 / 5 + n * (-227 / 45 + n * (2704 / 315 + n * (2323 / 945)))));\n  this.cbg[1] = np * (5 / 3 + n * (-16 / 15 + n * ( -13 / 9 + n * (904 / 315 + n * (-1522 / 945)))));\n\n  np = np * n;\n  this.cgb[2] = np * (56 / 15 + n * (-136 / 35 + n * (-1262 / 105 + n * (73814 / 2835))));\n  this.cbg[2] = np * (-26 / 15 + n * (34 / 21 + n * (8 / 5 + n * (-12686 / 2835))));\n\n  np = np * n;\n  this.cgb[3] = np * (4279 / 630 + n * (-332 / 35 + n * (-399572 / 14175)));\n  this.cbg[3] = np * (1237 / 630 + n * (-12 / 5 + n * ( -24832 / 14175)));\n\n  np = np * n;\n  this.cgb[4] = np * (4174 / 315 + n * (-144838 / 6237));\n  this.cbg[4] = np * (-734 / 315 + n * (109598 / 31185));\n\n  np = np * n;\n  this.cgb[5] = np * (601676 / 22275);\n  this.cbg[5] = np * (444337 / 155925);\n\n  np = Math.pow(n, 2);\n  this.Qn = this.k0 / (1 + n) * (1 + np * (1 / 4 + np * (1 / 64 + np / 256)));\n\n  this.utg[0] = n * (-0.5 + n * ( 2 / 3 + n * (-37 / 96 + n * ( 1 / 360 + n * (81 / 512 + n * (-96199 / 604800))))));\n  this.gtu[0] = n * (0.5 + n * (-2 / 3 + n * (5 / 16 + n * (41 / 180 + n * (-127 / 288 + n * (7891 / 37800))))));\n\n  this.utg[1] = np * (-1 / 48 + n * (-1 / 15 + n * (437 / 1440 + n * (-46 / 105 + n * (1118711 / 3870720)))));\n  this.gtu[1] = np * (13 / 48 + n * (-3 / 5 + n * (557 / 1440 + n * (281 / 630 + n * (-1983433 / 1935360)))));\n\n  np = np * n;\n  this.utg[2] = np * (-17 / 480 + n * (37 / 840 + n * (209 / 4480 + n * (-5569 / 90720 ))));\n  this.gtu[2] = np * (61 / 240 + n * (-103 / 140 + n * (15061 / 26880 + n * (167603 / 181440))));\n\n  np = np * n;\n  this.utg[3] = np * (-4397 / 161280 + n * (11 / 504 + n * (830251 / 7257600)));\n  this.gtu[3] = np * (49561 / 161280 + n * (-179 / 168 + n * (6601661 / 7257600)));\n\n  np = np * n;\n  this.utg[4] = np * (-4583 / 161280 + n * (108847 / 3991680));\n  this.gtu[4] = np * (34729 / 80640 + n * (-3418889 / 1995840));\n\n  np = np * n;\n  this.utg[5] = np * (-20648693 / 638668800);\n  this.gtu[5] = np * (212378941 / 319334400);\n\n  var Z = gatg(this.cbg, this.lat0);\n  this.Zb = -this.Qn * (Z + clens(this.gtu, 2 * Z));\n}\n\nfunction forward$r(p) {\n  var Ce = adjust_lon(p.x - this.long0);\n  var Cn = p.y;\n\n  Cn = gatg(this.cbg, Cn);\n  var sin_Cn = Math.sin(Cn);\n  var cos_Cn = Math.cos(Cn);\n  var sin_Ce = Math.sin(Ce);\n  var cos_Ce = Math.cos(Ce);\n\n  Cn = Math.atan2(sin_Cn, cos_Ce * cos_Cn);\n  Ce = Math.atan2(sin_Ce * cos_Cn, hypot(sin_Cn, cos_Cn * cos_Ce));\n  Ce = asinhy(Math.tan(Ce));\n\n  var tmp = clens_cmplx(this.gtu, 2 * Cn, 2 * Ce);\n\n  Cn = Cn + tmp[0];\n  Ce = Ce + tmp[1];\n\n  var x;\n  var y;\n\n  if (Math.abs(Ce) <= 2.623395162778) {\n    x = this.a * (this.Qn * Ce) + this.x0;\n    y = this.a * (this.Qn * Cn + this.Zb) + this.y0;\n  }\n  else {\n    x = Infinity;\n    y = Infinity;\n  }\n\n  p.x = x;\n  p.y = y;\n\n  return p;\n}\n\nfunction inverse$r(p) {\n  var Ce = (p.x - this.x0) * (1 / this.a);\n  var Cn = (p.y - this.y0) * (1 / this.a);\n\n  Cn = (Cn - this.Zb) / this.Qn;\n  Ce = Ce / this.Qn;\n\n  var lon;\n  var lat;\n\n  if (Math.abs(Ce) <= 2.623395162778) {\n    var tmp = clens_cmplx(this.utg, 2 * Cn, 2 * Ce);\n\n    Cn = Cn + tmp[0];\n    Ce = Ce + tmp[1];\n    Ce = Math.atan(sinh(Ce));\n\n    var sin_Cn = Math.sin(Cn);\n    var cos_Cn = Math.cos(Cn);\n    var sin_Ce = Math.sin(Ce);\n    var cos_Ce = Math.cos(Ce);\n\n    Cn = Math.atan2(sin_Cn * cos_Ce, hypot(sin_Ce, cos_Ce * cos_Cn));\n    Ce = Math.atan2(sin_Ce, cos_Ce * cos_Cn);\n\n    lon = adjust_lon(Ce + this.long0);\n    lat = gatg(this.cgb, Cn);\n  }\n  else {\n    lon = Infinity;\n    lat = Infinity;\n  }\n\n  p.x = lon;\n  p.y = lat;\n\n  return p;\n}\n\nvar names$s = [\"Extended_Transverse_Mercator\", \"Extended Transverse Mercator\", \"etmerc\", \"Transverse_Mercator\", \"Transverse Mercator\", \"Gauss Kruger\", \"Gauss_Kruger\", \"tmerc\"];\nvar etmerc = {\n  init: init$s,\n  forward: forward$r,\n  inverse: inverse$r,\n  names: names$s\n};\n\nfunction adjust_zone(zone, lon) {\n  if (zone === undefined) {\n    zone = Math.floor((adjust_lon(lon) + Math.PI) * 30 / Math.PI) + 1;\n\n    if (zone < 0) {\n      return 0;\n    } else if (zone > 60) {\n      return 60;\n    }\n  }\n  return zone;\n}\n\nvar dependsOn = 'etmerc';\n\n\nfunction init$r() {\n  var zone = adjust_zone(this.zone, this.long0);\n  if (zone === undefined) {\n    throw new Error('unknown utm zone');\n  }\n  this.lat0 = 0;\n  this.long0 =  ((6 * Math.abs(zone)) - 183) * D2R$1;\n  this.x0 = 500000;\n  this.y0 = this.utmSouth ? 10000000 : 0;\n  this.k0 = 0.9996;\n\n  etmerc.init.apply(this);\n  this.forward = etmerc.forward;\n  this.inverse = etmerc.inverse;\n}\n\nvar names$r = [\"Universal Transverse Mercator System\", \"utm\"];\nvar utm = {\n  init: init$r,\n  names: names$r,\n  dependsOn: dependsOn\n};\n\nfunction srat(esinp, exp) {\n  return (Math.pow((1 - esinp) / (1 + esinp), exp));\n}\n\nvar MAX_ITER$2 = 20;\n\nfunction init$q() {\n  var sphi = Math.sin(this.lat0);\n  var cphi = Math.cos(this.lat0);\n  cphi *= cphi;\n  this.rc = Math.sqrt(1 - this.es) / (1 - this.es * sphi * sphi);\n  this.C = Math.sqrt(1 + this.es * cphi * cphi / (1 - this.es));\n  this.phic0 = Math.asin(sphi / this.C);\n  this.ratexp = 0.5 * this.C * this.e;\n  this.K = Math.tan(0.5 * this.phic0 + FORTPI) / (Math.pow(Math.tan(0.5 * this.lat0 + FORTPI), this.C) * srat(this.e * sphi, this.ratexp));\n}\n\nfunction forward$q(p) {\n  var lon = p.x;\n  var lat = p.y;\n\n  p.y = 2 * Math.atan(this.K * Math.pow(Math.tan(0.5 * lat + FORTPI), this.C) * srat(this.e * Math.sin(lat), this.ratexp)) - HALF_PI;\n  p.x = this.C * lon;\n  return p;\n}\n\nfunction inverse$q(p) {\n  var DEL_TOL = 1e-14;\n  var lon = p.x / this.C;\n  var lat = p.y;\n  var num = Math.pow(Math.tan(0.5 * lat + FORTPI) / this.K, 1 / this.C);\n  for (var i = MAX_ITER$2; i > 0; --i) {\n    lat = 2 * Math.atan(num * srat(this.e * Math.sin(p.y), - 0.5 * this.e)) - HALF_PI;\n    if (Math.abs(lat - p.y) < DEL_TOL) {\n      break;\n    }\n    p.y = lat;\n  }\n  /* convergence failed */\n  if (!i) {\n    return null;\n  }\n  p.x = lon;\n  p.y = lat;\n  return p;\n}\n\nvar names$q = [\"gauss\"];\nvar gauss = {\n  init: init$q,\n  forward: forward$q,\n  inverse: inverse$q,\n  names: names$q\n};\n\nfunction init$p() {\n  gauss.init.apply(this);\n  if (!this.rc) {\n    return;\n  }\n  this.sinc0 = Math.sin(this.phic0);\n  this.cosc0 = Math.cos(this.phic0);\n  this.R2 = 2 * this.rc;\n  if (!this.title) {\n    this.title = \"Oblique Stereographic Alternative\";\n  }\n}\n\nfunction forward$p(p) {\n  var sinc, cosc, cosl, k;\n  p.x = adjust_lon(p.x - this.long0);\n  gauss.forward.apply(this, [p]);\n  sinc = Math.sin(p.y);\n  cosc = Math.cos(p.y);\n  cosl = Math.cos(p.x);\n  k = this.k0 * this.R2 / (1 + this.sinc0 * sinc + this.cosc0 * cosc * cosl);\n  p.x = k * cosc * Math.sin(p.x);\n  p.y = k * (this.cosc0 * sinc - this.sinc0 * cosc * cosl);\n  p.x = this.a * p.x + this.x0;\n  p.y = this.a * p.y + this.y0;\n  return p;\n}\n\nfunction inverse$p(p) {\n  var sinc, cosc, lon, lat, rho;\n  p.x = (p.x - this.x0) / this.a;\n  p.y = (p.y - this.y0) / this.a;\n\n  p.x /= this.k0;\n  p.y /= this.k0;\n  if ((rho = hypot(p.x, p.y))) {\n    var c = 2 * Math.atan2(rho, this.R2);\n    sinc = Math.sin(c);\n    cosc = Math.cos(c);\n    lat = Math.asin(cosc * this.sinc0 + p.y * sinc * this.cosc0 / rho);\n    lon = Math.atan2(p.x * sinc, rho * this.cosc0 * cosc - p.y * this.sinc0 * sinc);\n  }\n  else {\n    lat = this.phic0;\n    lon = 0;\n  }\n\n  p.x = lon;\n  p.y = lat;\n  gauss.inverse.apply(this, [p]);\n  p.x = adjust_lon(p.x + this.long0);\n  return p;\n}\n\nvar names$p = [\"Stereographic_North_Pole\", \"Oblique_Stereographic\", \"sterea\",\"Oblique Stereographic Alternative\",\"Double_Stereographic\"];\nvar sterea = {\n  init: init$p,\n  forward: forward$p,\n  inverse: inverse$p,\n  names: names$p\n};\n\nfunction ssfn_(phit, sinphi, eccen) {\n  sinphi *= eccen;\n  return (Math.tan(0.5 * (HALF_PI + phit)) * Math.pow((1 - sinphi) / (1 + sinphi), 0.5 * eccen));\n}\n\nfunction init$o() {\n\n  // setting default parameters\n  this.x0 = this.x0 || 0;\n  this.y0 = this.y0 || 0;\n  this.lat0 = this.lat0 || 0;\n  this.long0 = this.long0 || 0;\n\n  this.coslat0 = Math.cos(this.lat0);\n  this.sinlat0 = Math.sin(this.lat0);\n  if (this.sphere) {\n    if (this.k0 === 1 && !isNaN(this.lat_ts) && Math.abs(this.coslat0) <= EPSLN) {\n      this.k0 = 0.5 * (1 + sign(this.lat0) * Math.sin(this.lat_ts));\n    }\n  }\n  else {\n    if (Math.abs(this.coslat0) <= EPSLN) {\n      if (this.lat0 > 0) {\n        //North pole\n        //trace('stere:north pole');\n        this.con = 1;\n      }\n      else {\n        //South pole\n        //trace('stere:south pole');\n        this.con = -1;\n      }\n    }\n    this.cons = Math.sqrt(Math.pow(1 + this.e, 1 + this.e) * Math.pow(1 - this.e, 1 - this.e));\n    if (this.k0 === 1 && !isNaN(this.lat_ts) && Math.abs(this.coslat0) <= EPSLN && Math.abs(Math.cos(this.lat_ts)) > EPSLN) {\n      // When k0 is 1 (default value) and lat_ts is a vaild number and lat0 is at a pole and lat_ts is not at a pole\n      // Recalculate k0 using formula 21-35 from p161 of Snyder, 1987\n      this.k0 = 0.5 * this.cons * msfnz(this.e, Math.sin(this.lat_ts), Math.cos(this.lat_ts)) / tsfnz(this.e, this.con * this.lat_ts, this.con * Math.sin(this.lat_ts));\n    }\n    this.ms1 = msfnz(this.e, this.sinlat0, this.coslat0);\n    this.X0 = 2 * Math.atan(this.ssfn_(this.lat0, this.sinlat0, this.e)) - HALF_PI;\n    this.cosX0 = Math.cos(this.X0);\n    this.sinX0 = Math.sin(this.X0);\n  }\n}\n\n// Stereographic forward equations--mapping lat,long to x,y\nfunction forward$o(p) {\n  var lon = p.x;\n  var lat = p.y;\n  var sinlat = Math.sin(lat);\n  var coslat = Math.cos(lat);\n  var A, X, sinX, cosX, ts, rh;\n  var dlon = adjust_lon(lon - this.long0);\n\n  if (Math.abs(Math.abs(lon - this.long0) - Math.PI) <= EPSLN && Math.abs(lat + this.lat0) <= EPSLN) {\n    //case of the origine point\n    //trace('stere:this is the origin point');\n    p.x = NaN;\n    p.y = NaN;\n    return p;\n  }\n  if (this.sphere) {\n    //trace('stere:sphere case');\n    A = 2 * this.k0 / (1 + this.sinlat0 * sinlat + this.coslat0 * coslat * Math.cos(dlon));\n    p.x = this.a * A * coslat * Math.sin(dlon) + this.x0;\n    p.y = this.a * A * (this.coslat0 * sinlat - this.sinlat0 * coslat * Math.cos(dlon)) + this.y0;\n    return p;\n  }\n  else {\n    X = 2 * Math.atan(this.ssfn_(lat, sinlat, this.e)) - HALF_PI;\n    cosX = Math.cos(X);\n    sinX = Math.sin(X);\n    if (Math.abs(this.coslat0) <= EPSLN) {\n      ts = tsfnz(this.e, lat * this.con, this.con * sinlat);\n      rh = 2 * this.a * this.k0 * ts / this.cons;\n      p.x = this.x0 + rh * Math.sin(lon - this.long0);\n      p.y = this.y0 - this.con * rh * Math.cos(lon - this.long0);\n      //trace(p.toString());\n      return p;\n    }\n    else if (Math.abs(this.sinlat0) < EPSLN) {\n      //Eq\n      //trace('stere:equateur');\n      A = 2 * this.a * this.k0 / (1 + cosX * Math.cos(dlon));\n      p.y = A * sinX;\n    }\n    else {\n      //other case\n      //trace('stere:normal case');\n      A = 2 * this.a * this.k0 * this.ms1 / (this.cosX0 * (1 + this.sinX0 * sinX + this.cosX0 * cosX * Math.cos(dlon)));\n      p.y = A * (this.cosX0 * sinX - this.sinX0 * cosX * Math.cos(dlon)) + this.y0;\n    }\n    p.x = A * cosX * Math.sin(dlon) + this.x0;\n  }\n  //trace(p.toString());\n  return p;\n}\n\n//* Stereographic inverse equations--mapping x,y to lat/long\nfunction inverse$o(p) {\n  p.x -= this.x0;\n  p.y -= this.y0;\n  var lon, lat, ts, ce, Chi;\n  var rh = Math.sqrt(p.x * p.x + p.y * p.y);\n  if (this.sphere) {\n    var c = 2 * Math.atan(rh / (2 * this.a * this.k0));\n    lon = this.long0;\n    lat = this.lat0;\n    if (rh <= EPSLN) {\n      p.x = lon;\n      p.y = lat;\n      return p;\n    }\n    lat = Math.asin(Math.cos(c) * this.sinlat0 + p.y * Math.sin(c) * this.coslat0 / rh);\n    if (Math.abs(this.coslat0) < EPSLN) {\n      if (this.lat0 > 0) {\n        lon = adjust_lon(this.long0 + Math.atan2(p.x, - 1 * p.y));\n      }\n      else {\n        lon = adjust_lon(this.long0 + Math.atan2(p.x, p.y));\n      }\n    }\n    else {\n      lon = adjust_lon(this.long0 + Math.atan2(p.x * Math.sin(c), rh * this.coslat0 * Math.cos(c) - p.y * this.sinlat0 * Math.sin(c)));\n    }\n    p.x = lon;\n    p.y = lat;\n    return p;\n  }\n  else {\n    if (Math.abs(this.coslat0) <= EPSLN) {\n      if (rh <= EPSLN) {\n        lat = this.lat0;\n        lon = this.long0;\n        p.x = lon;\n        p.y = lat;\n        //trace(p.toString());\n        return p;\n      }\n      p.x *= this.con;\n      p.y *= this.con;\n      ts = rh * this.cons / (2 * this.a * this.k0);\n      lat = this.con * phi2z(this.e, ts);\n      lon = this.con * adjust_lon(this.con * this.long0 + Math.atan2(p.x, - 1 * p.y));\n    }\n    else {\n      ce = 2 * Math.atan(rh * this.cosX0 / (2 * this.a * this.k0 * this.ms1));\n      lon = this.long0;\n      if (rh <= EPSLN) {\n        Chi = this.X0;\n      }\n      else {\n        Chi = Math.asin(Math.cos(ce) * this.sinX0 + p.y * Math.sin(ce) * this.cosX0 / rh);\n        lon = adjust_lon(this.long0 + Math.atan2(p.x * Math.sin(ce), rh * this.cosX0 * Math.cos(ce) - p.y * this.sinX0 * Math.sin(ce)));\n      }\n      lat = -1 * phi2z(this.e, Math.tan(0.5 * (HALF_PI + Chi)));\n    }\n  }\n  p.x = lon;\n  p.y = lat;\n\n  //trace(p.toString());\n  return p;\n\n}\n\nvar names$o = [\"stere\", \"Stereographic_South_Pole\", \"Polar Stereographic (variant B)\", \"Polar_Stereographic\"];\nvar stere = {\n  init: init$o,\n  forward: forward$o,\n  inverse: inverse$o,\n  names: names$o,\n  ssfn_: ssfn_\n};\n\n/*\n  references:\n    Formules et constantes pour le Calcul pour la\n    projection cylindrique conforme à axe oblique et pour la transformation entre\n    des systèmes de référence.\n    http://www.swisstopo.admin.ch/internet/swisstopo/fr/home/topics/survey/sys/refsys/switzerland.parsysrelated1.31216.downloadList.77004.DownloadFile.tmp/swissprojectionfr.pdf\n  */\n\nfunction init$n() {\n  var phy0 = this.lat0;\n  this.lambda0 = this.long0;\n  var sinPhy0 = Math.sin(phy0);\n  var semiMajorAxis = this.a;\n  var invF = this.rf;\n  var flattening = 1 / invF;\n  var e2 = 2 * flattening - Math.pow(flattening, 2);\n  var e = this.e = Math.sqrt(e2);\n  this.R = this.k0 * semiMajorAxis * Math.sqrt(1 - e2) / (1 - e2 * Math.pow(sinPhy0, 2));\n  this.alpha = Math.sqrt(1 + e2 / (1 - e2) * Math.pow(Math.cos(phy0), 4));\n  this.b0 = Math.asin(sinPhy0 / this.alpha);\n  var k1 = Math.log(Math.tan(Math.PI / 4 + this.b0 / 2));\n  var k2 = Math.log(Math.tan(Math.PI / 4 + phy0 / 2));\n  var k3 = Math.log((1 + e * sinPhy0) / (1 - e * sinPhy0));\n  this.K = k1 - this.alpha * k2 + this.alpha * e / 2 * k3;\n}\n\nfunction forward$n(p) {\n  var Sa1 = Math.log(Math.tan(Math.PI / 4 - p.y / 2));\n  var Sa2 = this.e / 2 * Math.log((1 + this.e * Math.sin(p.y)) / (1 - this.e * Math.sin(p.y)));\n  var S = -this.alpha * (Sa1 + Sa2) + this.K;\n\n  // spheric latitude\n  var b = 2 * (Math.atan(Math.exp(S)) - Math.PI / 4);\n\n  // spheric longitude\n  var I = this.alpha * (p.x - this.lambda0);\n\n  // psoeudo equatorial rotation\n  var rotI = Math.atan(Math.sin(I) / (Math.sin(this.b0) * Math.tan(b) + Math.cos(this.b0) * Math.cos(I)));\n\n  var rotB = Math.asin(Math.cos(this.b0) * Math.sin(b) - Math.sin(this.b0) * Math.cos(b) * Math.cos(I));\n\n  p.y = this.R / 2 * Math.log((1 + Math.sin(rotB)) / (1 - Math.sin(rotB))) + this.y0;\n  p.x = this.R * rotI + this.x0;\n  return p;\n}\n\nfunction inverse$n(p) {\n  var Y = p.x - this.x0;\n  var X = p.y - this.y0;\n\n  var rotI = Y / this.R;\n  var rotB = 2 * (Math.atan(Math.exp(X / this.R)) - Math.PI / 4);\n\n  var b = Math.asin(Math.cos(this.b0) * Math.sin(rotB) + Math.sin(this.b0) * Math.cos(rotB) * Math.cos(rotI));\n  var I = Math.atan(Math.sin(rotI) / (Math.cos(this.b0) * Math.cos(rotI) - Math.sin(this.b0) * Math.tan(rotB)));\n\n  var lambda = this.lambda0 + I / this.alpha;\n\n  var S = 0;\n  var phy = b;\n  var prevPhy = -1000;\n  var iteration = 0;\n  while (Math.abs(phy - prevPhy) > 0.0000001) {\n    if (++iteration > 20) {\n      //...reportError(\"omercFwdInfinity\");\n      return;\n    }\n    //S = Math.log(Math.tan(Math.PI / 4 + phy / 2));\n    S = 1 / this.alpha * (Math.log(Math.tan(Math.PI / 4 + b / 2)) - this.K) + this.e * Math.log(Math.tan(Math.PI / 4 + Math.asin(this.e * Math.sin(phy)) / 2));\n    prevPhy = phy;\n    phy = 2 * Math.atan(Math.exp(S)) - Math.PI / 2;\n  }\n\n  p.x = lambda;\n  p.y = phy;\n  return p;\n}\n\nvar names$n = [\"somerc\"];\nvar somerc = {\n  init: init$n,\n  forward: forward$n,\n  inverse: inverse$n,\n  names: names$n\n};\n\nvar TOL = 1e-7;\n\nfunction isTypeA(P) {\n  var typeAProjections = ['Hotine_Oblique_Mercator','Hotine_Oblique_Mercator_Azimuth_Natural_Origin'];\n  var projectionName = typeof P.PROJECTION === \"object\" ? Object.keys(P.PROJECTION)[0] : P.PROJECTION;\n  \n  return 'no_uoff' in P || 'no_off' in P || typeAProjections.indexOf(projectionName) !== -1;\n}\n\n\n/* Initialize the Oblique Mercator  projection\n    ------------------------------------------*/\nfunction init$m() {  \n  var con, com, cosph0, D, F, H, L, sinph0, p, J, gamma = 0,\n    gamma0, lamc = 0, lam1 = 0, lam2 = 0, phi1 = 0, phi2 = 0, alpha_c = 0;\n  \n  // only Type A uses the no_off or no_uoff property\n  // https://github.com/OSGeo/proj.4/issues/104\n  this.no_off = isTypeA(this);\n  this.no_rot = 'no_rot' in this;\n  \n  var alp = false;\n  if (\"alpha\" in this) {\n    alp = true;\n  }\n\n  var gam = false;\n  if (\"rectified_grid_angle\" in this) {\n    gam = true;\n  }\n\n  if (alp) {\n    alpha_c = this.alpha;\n  }\n  \n  if (gam) {\n    gamma = (this.rectified_grid_angle * D2R$1);\n  }\n  \n  if (alp || gam) {\n    lamc = this.longc;\n  } else {\n    lam1 = this.long1;\n    phi1 = this.lat1;\n    lam2 = this.long2;\n    phi2 = this.lat2;\n    \n    if (Math.abs(phi1 - phi2) <= TOL || (con = Math.abs(phi1)) <= TOL ||\n        Math.abs(con - HALF_PI) <= TOL || Math.abs(Math.abs(this.lat0) - HALF_PI) <= TOL ||\n        Math.abs(Math.abs(phi2) - HALF_PI) <= TOL) {\n      throw new Error();\n    }\n  }\n  \n  var one_es = 1.0 - this.es;\n  com = Math.sqrt(one_es);\n  \n  if (Math.abs(this.lat0) > EPSLN) {\n    sinph0 = Math.sin(this.lat0);\n    cosph0 = Math.cos(this.lat0);\n    con = 1 - this.es * sinph0 * sinph0;\n    this.B = cosph0 * cosph0;\n    this.B = Math.sqrt(1 + this.es * this.B * this.B / one_es);\n    this.A = this.B * this.k0 * com / con;\n    D = this.B * com / (cosph0 * Math.sqrt(con));\n    F = D * D -1;\n    \n    if (F <= 0) {\n      F = 0;\n    } else {\n      F = Math.sqrt(F);\n      if (this.lat0 < 0) {\n        F = -F;\n      }\n    }\n    \n    this.E = F += D;\n    this.E *= Math.pow(tsfnz(this.e, this.lat0, sinph0), this.B);\n  } else {\n    this.B = 1 / com;\n    this.A = this.k0;\n    this.E = D = F = 1;\n  }\n  \n  if (alp || gam) {\n    if (alp) {\n      gamma0 = Math.asin(Math.sin(alpha_c) / D);\n      if (!gam) {\n        gamma = alpha_c;\n      }\n    } else {\n      gamma0 = gamma;\n      alpha_c = Math.asin(D * Math.sin(gamma0));\n    }\n    this.lam0 = lamc - Math.asin(0.5 * (F - 1 / F) * Math.tan(gamma0)) / this.B;\n  } else {\n    H = Math.pow(tsfnz(this.e, phi1, Math.sin(phi1)), this.B);\n    L = Math.pow(tsfnz(this.e, phi2, Math.sin(phi2)), this.B);\n    F = this.E / H;\n    p = (L - H) / (L + H);\n    J = this.E * this.E;\n    J = (J - L * H) / (J + L * H);\n    con = lam1 - lam2;\n    \n    if (con < -Math.pi) {\n      lam2 -=TWO_PI;\n    } else if (con > Math.pi) {\n      lam2 += TWO_PI;\n    }\n    \n    this.lam0 = adjust_lon(0.5 * (lam1 + lam2) - Math.atan(J * Math.tan(0.5 * this.B * (lam1 - lam2)) / p) / this.B);\n    gamma0 = Math.atan(2 * Math.sin(this.B * adjust_lon(lam1 - this.lam0)) / (F - 1 / F));\n    gamma = alpha_c = Math.asin(D * Math.sin(gamma0));\n  }\n  \n  this.singam = Math.sin(gamma0);\n  this.cosgam = Math.cos(gamma0);\n  this.sinrot = Math.sin(gamma);\n  this.cosrot = Math.cos(gamma);\n  \n  this.rB = 1 / this.B;\n  this.ArB = this.A * this.rB;\n  this.BrA = 1 / this.ArB;\n  this.A * this.B;\n  \n  if (this.no_off) {\n    this.u_0 = 0;\n  } else {\n    this.u_0 = Math.abs(this.ArB * Math.atan(Math.sqrt(D * D - 1) / Math.cos(alpha_c)));\n    \n    if (this.lat0 < 0) {\n      this.u_0 = - this.u_0;\n    }  \n  }\n    \n  F = 0.5 * gamma0;\n  this.v_pole_n = this.ArB * Math.log(Math.tan(FORTPI - F));\n  this.v_pole_s = this.ArB * Math.log(Math.tan(FORTPI + F));\n}\n\n\n/* Oblique Mercator forward equations--mapping lat,long to x,y\n    ----------------------------------------------------------*/\nfunction forward$m(p) {\n  var coords = {};\n  var S, T, U, V, W, temp, u, v;\n  p.x = p.x - this.lam0;\n  \n  if (Math.abs(Math.abs(p.y) - HALF_PI) > EPSLN) {\n    W = this.E / Math.pow(tsfnz(this.e, p.y, Math.sin(p.y)), this.B);\n    \n    temp = 1 / W;\n    S = 0.5 * (W - temp);\n    T = 0.5 * (W + temp);\n    V = Math.sin(this.B * p.x);\n    U = (S * this.singam - V * this.cosgam) / T;\n        \n    if (Math.abs(Math.abs(U) - 1.0) < EPSLN) {\n      throw new Error();\n    }\n    \n    v = 0.5 * this.ArB * Math.log((1 - U)/(1 + U));\n    temp = Math.cos(this.B * p.x);\n    \n    if (Math.abs(temp) < TOL) {\n      u = this.A * p.x;\n    } else {\n      u = this.ArB * Math.atan2((S * this.cosgam + V * this.singam), temp);\n    }    \n  } else {\n    v = p.y > 0 ? this.v_pole_n : this.v_pole_s;\n    u = this.ArB * p.y;\n  }\n     \n  if (this.no_rot) {\n    coords.x = u;\n    coords.y = v;\n  } else {\n    u -= this.u_0;\n    coords.x = v * this.cosrot + u * this.sinrot;\n    coords.y = u * this.cosrot - v * this.sinrot;\n  }\n  \n  coords.x = (this.a * coords.x + this.x0);\n  coords.y = (this.a * coords.y + this.y0);\n  \n  return coords;\n}\n\nfunction inverse$m(p) {\n  var u, v, Qp, Sp, Tp, Vp, Up;\n  var coords = {};\n  \n  p.x = (p.x - this.x0) * (1.0 / this.a);\n  p.y = (p.y - this.y0) * (1.0 / this.a);\n\n  if (this.no_rot) {\n    v = p.y;\n    u = p.x;\n  } else {\n    v = p.x * this.cosrot - p.y * this.sinrot;\n    u = p.y * this.cosrot + p.x * this.sinrot + this.u_0;\n  }\n  \n  Qp = Math.exp(-this.BrA * v);\n  Sp = 0.5 * (Qp - 1 / Qp);\n  Tp = 0.5 * (Qp + 1 / Qp);\n  Vp = Math.sin(this.BrA * u);\n  Up = (Vp * this.cosgam + Sp * this.singam) / Tp;\n  \n  if (Math.abs(Math.abs(Up) - 1) < EPSLN) {\n    coords.x = 0;\n    coords.y = Up < 0 ? -HALF_PI : HALF_PI;\n  } else {\n    coords.y = this.E / Math.sqrt((1 + Up) / (1 - Up));\n    coords.y = phi2z(this.e, Math.pow(coords.y, 1 / this.B));\n    \n    if (coords.y === Infinity) {\n      throw new Error();\n    }\n        \n    coords.x = -this.rB * Math.atan2((Sp * this.cosgam - Vp * this.singam), Math.cos(this.BrA * u));\n  }\n  \n  coords.x += this.lam0;\n  \n  return coords;\n}\n\nvar names$m = [\"Hotine_Oblique_Mercator\", \"Hotine Oblique Mercator\", \"Hotine_Oblique_Mercator_Azimuth_Natural_Origin\", \"Hotine_Oblique_Mercator_Two_Point_Natural_Origin\", \"Hotine_Oblique_Mercator_Azimuth_Center\", \"Oblique_Mercator\", \"omerc\"];\nvar omerc = {\n  init: init$m,\n  forward: forward$m,\n  inverse: inverse$m,\n  names: names$m\n};\n\nfunction init$l() {\n  \n  //double lat0;                    /* the reference latitude               */\n  //double long0;                   /* the reference longitude              */\n  //double lat1;                    /* first standard parallel              */\n  //double lat2;                    /* second standard parallel             */\n  //double r_maj;                   /* major axis                           */\n  //double r_min;                   /* minor axis                           */\n  //double false_east;              /* x offset in meters                   */\n  //double false_north;             /* y offset in meters                   */\n  \n  //the above value can be set with proj4.defs\n  //example: proj4.defs(\"EPSG:2154\",\"+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs\");\n\n  if (!this.lat2) {\n    this.lat2 = this.lat1;\n  } //if lat2 is not defined\n  if (!this.k0) {\n    this.k0 = 1;\n  }\n  this.x0 = this.x0 || 0;\n  this.y0 = this.y0 || 0;\n  // Standard Parallels cannot be equal and on opposite sides of the equator\n  if (Math.abs(this.lat1 + this.lat2) < EPSLN) {\n    return;\n  }\n\n  var temp = this.b / this.a;\n  this.e = Math.sqrt(1 - temp * temp);\n\n  var sin1 = Math.sin(this.lat1);\n  var cos1 = Math.cos(this.lat1);\n  var ms1 = msfnz(this.e, sin1, cos1);\n  var ts1 = tsfnz(this.e, this.lat1, sin1);\n\n  var sin2 = Math.sin(this.lat2);\n  var cos2 = Math.cos(this.lat2);\n  var ms2 = msfnz(this.e, sin2, cos2);\n  var ts2 = tsfnz(this.e, this.lat2, sin2);\n\n  var ts0 = tsfnz(this.e, this.lat0, Math.sin(this.lat0));\n\n  if (Math.abs(this.lat1 - this.lat2) > EPSLN) {\n    this.ns = Math.log(ms1 / ms2) / Math.log(ts1 / ts2);\n  }\n  else {\n    this.ns = sin1;\n  }\n  if (isNaN(this.ns)) {\n    this.ns = sin1;\n  }\n  this.f0 = ms1 / (this.ns * Math.pow(ts1, this.ns));\n  this.rh = this.a * this.f0 * Math.pow(ts0, this.ns);\n  if (!this.title) {\n    this.title = \"Lambert Conformal Conic\";\n  }\n}\n\n// Lambert Conformal conic forward equations--mapping lat,long to x,y\n// -----------------------------------------------------------------\nfunction forward$l(p) {\n\n  var lon = p.x;\n  var lat = p.y;\n\n  // singular cases :\n  if (Math.abs(2 * Math.abs(lat) - Math.PI) <= EPSLN) {\n    lat = sign(lat) * (HALF_PI - 2 * EPSLN);\n  }\n\n  var con = Math.abs(Math.abs(lat) - HALF_PI);\n  var ts, rh1;\n  if (con > EPSLN) {\n    ts = tsfnz(this.e, lat, Math.sin(lat));\n    rh1 = this.a * this.f0 * Math.pow(ts, this.ns);\n  }\n  else {\n    con = lat * this.ns;\n    if (con <= 0) {\n      return null;\n    }\n    rh1 = 0;\n  }\n  var theta = this.ns * adjust_lon(lon - this.long0);\n  p.x = this.k0 * (rh1 * Math.sin(theta)) + this.x0;\n  p.y = this.k0 * (this.rh - rh1 * Math.cos(theta)) + this.y0;\n\n  return p;\n}\n\n// Lambert Conformal Conic inverse equations--mapping x,y to lat/long\n// -----------------------------------------------------------------\nfunction inverse$l(p) {\n\n  var rh1, con, ts;\n  var lat, lon;\n  var x = (p.x - this.x0) / this.k0;\n  var y = (this.rh - (p.y - this.y0) / this.k0);\n  if (this.ns > 0) {\n    rh1 = Math.sqrt(x * x + y * y);\n    con = 1;\n  }\n  else {\n    rh1 = -Math.sqrt(x * x + y * y);\n    con = -1;\n  }\n  var theta = 0;\n  if (rh1 !== 0) {\n    theta = Math.atan2((con * x), (con * y));\n  }\n  if ((rh1 !== 0) || (this.ns > 0)) {\n    con = 1 / this.ns;\n    ts = Math.pow((rh1 / (this.a * this.f0)), con);\n    lat = phi2z(this.e, ts);\n    if (lat === -9999) {\n      return null;\n    }\n  }\n  else {\n    lat = -HALF_PI;\n  }\n  lon = adjust_lon(theta / this.ns + this.long0);\n\n  p.x = lon;\n  p.y = lat;\n  return p;\n}\n\nvar names$l = [\n  \"Lambert Tangential Conformal Conic Projection\",\n  \"Lambert_Conformal_Conic\",\n  \"Lambert_Conformal_Conic_1SP\",\n  \"Lambert_Conformal_Conic_2SP\",\n  \"lcc\",\n  \"Lambert Conic Conformal (1SP)\",\n  \"Lambert Conic Conformal (2SP)\"\n];\n\nvar lcc = {\n  init: init$l,\n  forward: forward$l,\n  inverse: inverse$l,\n  names: names$l\n};\n\nfunction init$k() {\n  this.a = 6377397.155;\n  this.es = 0.006674372230614;\n  this.e = Math.sqrt(this.es);\n  if (!this.lat0) {\n    this.lat0 = 0.863937979737193;\n  }\n  if (!this.long0) {\n    this.long0 = 0.7417649320975901 - 0.308341501185665;\n  }\n  /* if scale not set default to 0.9999 */\n  if (!this.k0) {\n    this.k0 = 0.9999;\n  }\n  this.s45 = 0.785398163397448; /* 45 */\n  this.s90 = 2 * this.s45;\n  this.fi0 = this.lat0;\n  this.e2 = this.es;\n  this.e = Math.sqrt(this.e2);\n  this.alfa = Math.sqrt(1 + (this.e2 * Math.pow(Math.cos(this.fi0), 4)) / (1 - this.e2));\n  this.uq = 1.04216856380474;\n  this.u0 = Math.asin(Math.sin(this.fi0) / this.alfa);\n  this.g = Math.pow((1 + this.e * Math.sin(this.fi0)) / (1 - this.e * Math.sin(this.fi0)), this.alfa * this.e / 2);\n  this.k = Math.tan(this.u0 / 2 + this.s45) / Math.pow(Math.tan(this.fi0 / 2 + this.s45), this.alfa) * this.g;\n  this.k1 = this.k0;\n  this.n0 = this.a * Math.sqrt(1 - this.e2) / (1 - this.e2 * Math.pow(Math.sin(this.fi0), 2));\n  this.s0 = 1.37008346281555;\n  this.n = Math.sin(this.s0);\n  this.ro0 = this.k1 * this.n0 / Math.tan(this.s0);\n  this.ad = this.s90 - this.uq;\n}\n\n/* ellipsoid */\n/* calculate xy from lat/lon */\n/* Constants, identical to inverse transform function */\nfunction forward$k(p) {\n  var gfi, u, deltav, s, d, eps, ro;\n  var lon = p.x;\n  var lat = p.y;\n  var delta_lon = adjust_lon(lon - this.long0);\n  /* Transformation */\n  gfi = Math.pow(((1 + this.e * Math.sin(lat)) / (1 - this.e * Math.sin(lat))), (this.alfa * this.e / 2));\n  u = 2 * (Math.atan(this.k * Math.pow(Math.tan(lat / 2 + this.s45), this.alfa) / gfi) - this.s45);\n  deltav = -delta_lon * this.alfa;\n  s = Math.asin(Math.cos(this.ad) * Math.sin(u) + Math.sin(this.ad) * Math.cos(u) * Math.cos(deltav));\n  d = Math.asin(Math.cos(u) * Math.sin(deltav) / Math.cos(s));\n  eps = this.n * d;\n  ro = this.ro0 * Math.pow(Math.tan(this.s0 / 2 + this.s45), this.n) / Math.pow(Math.tan(s / 2 + this.s45), this.n);\n  p.y = ro * Math.cos(eps) / 1;\n  p.x = ro * Math.sin(eps) / 1;\n\n  if (!this.czech) {\n    p.y *= -1;\n    p.x *= -1;\n  }\n  return (p);\n}\n\n/* calculate lat/lon from xy */\nfunction inverse$k(p) {\n  var u, deltav, s, d, eps, ro, fi1;\n  var ok;\n\n  /* Transformation */\n  /* revert y, x*/\n  var tmp = p.x;\n  p.x = p.y;\n  p.y = tmp;\n  if (!this.czech) {\n    p.y *= -1;\n    p.x *= -1;\n  }\n  ro = Math.sqrt(p.x * p.x + p.y * p.y);\n  eps = Math.atan2(p.y, p.x);\n  d = eps / Math.sin(this.s0);\n  s = 2 * (Math.atan(Math.pow(this.ro0 / ro, 1 / this.n) * Math.tan(this.s0 / 2 + this.s45)) - this.s45);\n  u = Math.asin(Math.cos(this.ad) * Math.sin(s) - Math.sin(this.ad) * Math.cos(s) * Math.cos(d));\n  deltav = Math.asin(Math.cos(s) * Math.sin(d) / Math.cos(u));\n  p.x = this.long0 - deltav / this.alfa;\n  fi1 = u;\n  ok = 0;\n  var iter = 0;\n  do {\n    p.y = 2 * (Math.atan(Math.pow(this.k, - 1 / this.alfa) * Math.pow(Math.tan(u / 2 + this.s45), 1 / this.alfa) * Math.pow((1 + this.e * Math.sin(fi1)) / (1 - this.e * Math.sin(fi1)), this.e / 2)) - this.s45);\n    if (Math.abs(fi1 - p.y) < 0.0000000001) {\n      ok = 1;\n    }\n    fi1 = p.y;\n    iter += 1;\n  } while (ok === 0 && iter < 15);\n  if (iter >= 15) {\n    return null;\n  }\n\n  return (p);\n}\n\nvar names$k = [\"Krovak\", \"krovak\"];\nvar krovak = {\n  init: init$k,\n  forward: forward$k,\n  inverse: inverse$k,\n  names: names$k\n};\n\nfunction mlfn(e0, e1, e2, e3, phi) {\n  return (e0 * phi - e1 * Math.sin(2 * phi) + e2 * Math.sin(4 * phi) - e3 * Math.sin(6 * phi));\n}\n\nfunction e0fn(x) {\n  return (1 - 0.25 * x * (1 + x / 16 * (3 + 1.25 * x)));\n}\n\nfunction e1fn(x) {\n  return (0.375 * x * (1 + 0.25 * x * (1 + 0.46875 * x)));\n}\n\nfunction e2fn(x) {\n  return (0.05859375 * x * x * (1 + 0.75 * x));\n}\n\nfunction e3fn(x) {\n  return (x * x * x * (35 / 3072));\n}\n\nfunction gN(a, e, sinphi) {\n  var temp = e * sinphi;\n  return a / Math.sqrt(1 - temp * temp);\n}\n\nfunction adjust_lat(x) {\n  return (Math.abs(x) < HALF_PI) ? x : (x - (sign(x) * Math.PI));\n}\n\nfunction imlfn(ml, e0, e1, e2, e3) {\n  var phi;\n  var dphi;\n\n  phi = ml / e0;\n  for (var i = 0; i < 15; i++) {\n    dphi = (ml - (e0 * phi - e1 * Math.sin(2 * phi) + e2 * Math.sin(4 * phi) - e3 * Math.sin(6 * phi))) / (e0 - 2 * e1 * Math.cos(2 * phi) + 4 * e2 * Math.cos(4 * phi) - 6 * e3 * Math.cos(6 * phi));\n    phi += dphi;\n    if (Math.abs(dphi) <= 0.0000000001) {\n      return phi;\n    }\n  }\n\n  //..reportError(\"IMLFN-CONV:Latitude failed to converge after 15 iterations\");\n  return NaN;\n}\n\nfunction init$j() {\n  if (!this.sphere) {\n    this.e0 = e0fn(this.es);\n    this.e1 = e1fn(this.es);\n    this.e2 = e2fn(this.es);\n    this.e3 = e3fn(this.es);\n    this.ml0 = this.a * mlfn(this.e0, this.e1, this.e2, this.e3, this.lat0);\n  }\n}\n\n/* Cassini forward equations--mapping lat,long to x,y\n  -----------------------------------------------------------------------*/\nfunction forward$j(p) {\n\n  /* Forward equations\n      -----------------*/\n  var x, y;\n  var lam = p.x;\n  var phi = p.y;\n  lam = adjust_lon(lam - this.long0);\n\n  if (this.sphere) {\n    x = this.a * Math.asin(Math.cos(phi) * Math.sin(lam));\n    y = this.a * (Math.atan2(Math.tan(phi), Math.cos(lam)) - this.lat0);\n  }\n  else {\n    //ellipsoid\n    var sinphi = Math.sin(phi);\n    var cosphi = Math.cos(phi);\n    var nl = gN(this.a, this.e, sinphi);\n    var tl = Math.tan(phi) * Math.tan(phi);\n    var al = lam * Math.cos(phi);\n    var asq = al * al;\n    var cl = this.es * cosphi * cosphi / (1 - this.es);\n    var ml = this.a * mlfn(this.e0, this.e1, this.e2, this.e3, phi);\n\n    x = nl * al * (1 - asq * tl * (1 / 6 - (8 - tl + 8 * cl) * asq / 120));\n    y = ml - this.ml0 + nl * sinphi / cosphi * asq * (0.5 + (5 - tl + 6 * cl) * asq / 24);\n\n\n  }\n\n  p.x = x + this.x0;\n  p.y = y + this.y0;\n  return p;\n}\n\n/* Inverse equations\n  -----------------*/\nfunction inverse$j(p) {\n  p.x -= this.x0;\n  p.y -= this.y0;\n  var x = p.x / this.a;\n  var y = p.y / this.a;\n  var phi, lam;\n\n  if (this.sphere) {\n    var dd = y + this.lat0;\n    phi = Math.asin(Math.sin(dd) * Math.cos(x));\n    lam = Math.atan2(Math.tan(x), Math.cos(dd));\n  }\n  else {\n    /* ellipsoid */\n    var ml1 = this.ml0 / this.a + y;\n    var phi1 = imlfn(ml1, this.e0, this.e1, this.e2, this.e3);\n    if (Math.abs(Math.abs(phi1) - HALF_PI) <= EPSLN) {\n      p.x = this.long0;\n      p.y = HALF_PI;\n      if (y < 0) {\n        p.y *= -1;\n      }\n      return p;\n    }\n    var nl1 = gN(this.a, this.e, Math.sin(phi1));\n\n    var rl1 = nl1 * nl1 * nl1 / this.a / this.a * (1 - this.es);\n    var tl1 = Math.pow(Math.tan(phi1), 2);\n    var dl = x * this.a / nl1;\n    var dsq = dl * dl;\n    phi = phi1 - nl1 * Math.tan(phi1) / rl1 * dl * dl * (0.5 - (1 + 3 * tl1) * dl * dl / 24);\n    lam = dl * (1 - dsq * (tl1 / 3 + (1 + 3 * tl1) * tl1 * dsq / 15)) / Math.cos(phi1);\n\n  }\n\n  p.x = adjust_lon(lam + this.long0);\n  p.y = adjust_lat(phi);\n  return p;\n\n}\n\nvar names$j = [\"Cassini\", \"Cassini_Soldner\", \"cass\"];\nvar cass = {\n  init: init$j,\n  forward: forward$j,\n  inverse: inverse$j,\n  names: names$j\n};\n\nfunction qsfnz(eccent, sinphi) {\n  var con;\n  if (eccent > 1.0e-7) {\n    con = eccent * sinphi;\n    return ((1 - eccent * eccent) * (sinphi / (1 - con * con) - (0.5 / eccent) * Math.log((1 - con) / (1 + con))));\n  }\n  else {\n    return (2 * sinphi);\n  }\n}\n\n/*\n  reference\n    \"New Equal-Area Map Projections for Noncircular Regions\", John P. Snyder,\n    The American Cartographer, Vol 15, No. 4, October 1988, pp. 341-355.\n  */\n\nvar S_POLE = 1;\n\nvar N_POLE = 2;\nvar EQUIT = 3;\nvar OBLIQ = 4;\n\n/* Initialize the Lambert Azimuthal Equal Area projection\n  ------------------------------------------------------*/\nfunction init$i() {\n  var t = Math.abs(this.lat0);\n  if (Math.abs(t - HALF_PI) < EPSLN) {\n    this.mode = this.lat0 < 0 ? this.S_POLE : this.N_POLE;\n  }\n  else if (Math.abs(t) < EPSLN) {\n    this.mode = this.EQUIT;\n  }\n  else {\n    this.mode = this.OBLIQ;\n  }\n  if (this.es > 0) {\n    var sinphi;\n\n    this.qp = qsfnz(this.e, 1);\n    this.mmf = 0.5 / (1 - this.es);\n    this.apa = authset(this.es);\n    switch (this.mode) {\n    case this.N_POLE:\n      this.dd = 1;\n      break;\n    case this.S_POLE:\n      this.dd = 1;\n      break;\n    case this.EQUIT:\n      this.rq = Math.sqrt(0.5 * this.qp);\n      this.dd = 1 / this.rq;\n      this.xmf = 1;\n      this.ymf = 0.5 * this.qp;\n      break;\n    case this.OBLIQ:\n      this.rq = Math.sqrt(0.5 * this.qp);\n      sinphi = Math.sin(this.lat0);\n      this.sinb1 = qsfnz(this.e, sinphi) / this.qp;\n      this.cosb1 = Math.sqrt(1 - this.sinb1 * this.sinb1);\n      this.dd = Math.cos(this.lat0) / (Math.sqrt(1 - this.es * sinphi * sinphi) * this.rq * this.cosb1);\n      this.ymf = (this.xmf = this.rq) / this.dd;\n      this.xmf *= this.dd;\n      break;\n    }\n  }\n  else {\n    if (this.mode === this.OBLIQ) {\n      this.sinph0 = Math.sin(this.lat0);\n      this.cosph0 = Math.cos(this.lat0);\n    }\n  }\n}\n\n/* Lambert Azimuthal Equal Area forward equations--mapping lat,long to x,y\n  -----------------------------------------------------------------------*/\nfunction forward$i(p) {\n\n  /* Forward equations\n      -----------------*/\n  var x, y, coslam, sinlam, sinphi, q, sinb, cosb, b, cosphi;\n  var lam = p.x;\n  var phi = p.y;\n\n  lam = adjust_lon(lam - this.long0);\n  if (this.sphere) {\n    sinphi = Math.sin(phi);\n    cosphi = Math.cos(phi);\n    coslam = Math.cos(lam);\n    if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {\n      y = (this.mode === this.EQUIT) ? 1 + cosphi * coslam : 1 + this.sinph0 * sinphi + this.cosph0 * cosphi * coslam;\n      if (y <= EPSLN) {\n        return null;\n      }\n      y = Math.sqrt(2 / y);\n      x = y * cosphi * Math.sin(lam);\n      y *= (this.mode === this.EQUIT) ? sinphi : this.cosph0 * sinphi - this.sinph0 * cosphi * coslam;\n    }\n    else if (this.mode === this.N_POLE || this.mode === this.S_POLE) {\n      if (this.mode === this.N_POLE) {\n        coslam = -coslam;\n      }\n      if (Math.abs(phi + this.lat0) < EPSLN) {\n        return null;\n      }\n      y = FORTPI - phi * 0.5;\n      y = 2 * ((this.mode === this.S_POLE) ? Math.cos(y) : Math.sin(y));\n      x = y * Math.sin(lam);\n      y *= coslam;\n    }\n  }\n  else {\n    sinb = 0;\n    cosb = 0;\n    b = 0;\n    coslam = Math.cos(lam);\n    sinlam = Math.sin(lam);\n    sinphi = Math.sin(phi);\n    q = qsfnz(this.e, sinphi);\n    if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {\n      sinb = q / this.qp;\n      cosb = Math.sqrt(1 - sinb * sinb);\n    }\n    switch (this.mode) {\n    case this.OBLIQ:\n      b = 1 + this.sinb1 * sinb + this.cosb1 * cosb * coslam;\n      break;\n    case this.EQUIT:\n      b = 1 + cosb * coslam;\n      break;\n    case this.N_POLE:\n      b = HALF_PI + phi;\n      q = this.qp - q;\n      break;\n    case this.S_POLE:\n      b = phi - HALF_PI;\n      q = this.qp + q;\n      break;\n    }\n    if (Math.abs(b) < EPSLN) {\n      return null;\n    }\n    switch (this.mode) {\n    case this.OBLIQ:\n    case this.EQUIT:\n      b = Math.sqrt(2 / b);\n      if (this.mode === this.OBLIQ) {\n        y = this.ymf * b * (this.cosb1 * sinb - this.sinb1 * cosb * coslam);\n      }\n      else {\n        y = (b = Math.sqrt(2 / (1 + cosb * coslam))) * sinb * this.ymf;\n      }\n      x = this.xmf * b * cosb * sinlam;\n      break;\n    case this.N_POLE:\n    case this.S_POLE:\n      if (q >= 0) {\n        x = (b = Math.sqrt(q)) * sinlam;\n        y = coslam * ((this.mode === this.S_POLE) ? b : -b);\n      }\n      else {\n        x = y = 0;\n      }\n      break;\n    }\n  }\n\n  p.x = this.a * x + this.x0;\n  p.y = this.a * y + this.y0;\n  return p;\n}\n\n/* Inverse equations\n  -----------------*/\nfunction inverse$i(p) {\n  p.x -= this.x0;\n  p.y -= this.y0;\n  var x = p.x / this.a;\n  var y = p.y / this.a;\n  var lam, phi, cCe, sCe, q, rho, ab;\n  if (this.sphere) {\n    var cosz = 0,\n      rh, sinz = 0;\n\n    rh = Math.sqrt(x * x + y * y);\n    phi = rh * 0.5;\n    if (phi > 1) {\n      return null;\n    }\n    phi = 2 * Math.asin(phi);\n    if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {\n      sinz = Math.sin(phi);\n      cosz = Math.cos(phi);\n    }\n    switch (this.mode) {\n    case this.EQUIT:\n      phi = (Math.abs(rh) <= EPSLN) ? 0 : Math.asin(y * sinz / rh);\n      x *= sinz;\n      y = cosz * rh;\n      break;\n    case this.OBLIQ:\n      phi = (Math.abs(rh) <= EPSLN) ? this.lat0 : Math.asin(cosz * this.sinph0 + y * sinz * this.cosph0 / rh);\n      x *= sinz * this.cosph0;\n      y = (cosz - Math.sin(phi) * this.sinph0) * rh;\n      break;\n    case this.N_POLE:\n      y = -y;\n      phi = HALF_PI - phi;\n      break;\n    case this.S_POLE:\n      phi -= HALF_PI;\n      break;\n    }\n    lam = (y === 0 && (this.mode === this.EQUIT || this.mode === this.OBLIQ)) ? 0 : Math.atan2(x, y);\n  }\n  else {\n    ab = 0;\n    if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {\n      x /= this.dd;\n      y *= this.dd;\n      rho = Math.sqrt(x * x + y * y);\n      if (rho < EPSLN) {\n        p.x = this.long0;\n        p.y = this.lat0;\n        return p;\n      }\n      sCe = 2 * Math.asin(0.5 * rho / this.rq);\n      cCe = Math.cos(sCe);\n      x *= (sCe = Math.sin(sCe));\n      if (this.mode === this.OBLIQ) {\n        ab = cCe * this.sinb1 + y * sCe * this.cosb1 / rho;\n        q = this.qp * ab;\n        y = rho * this.cosb1 * cCe - y * this.sinb1 * sCe;\n      }\n      else {\n        ab = y * sCe / rho;\n        q = this.qp * ab;\n        y = rho * cCe;\n      }\n    }\n    else if (this.mode === this.N_POLE || this.mode === this.S_POLE) {\n      if (this.mode === this.N_POLE) {\n        y = -y;\n      }\n      q = (x * x + y * y);\n      if (!q) {\n        p.x = this.long0;\n        p.y = this.lat0;\n        return p;\n      }\n      ab = 1 - q / this.qp;\n      if (this.mode === this.S_POLE) {\n        ab = -ab;\n      }\n    }\n    lam = Math.atan2(x, y);\n    phi = authlat(Math.asin(ab), this.apa);\n  }\n\n  p.x = adjust_lon(this.long0 + lam);\n  p.y = phi;\n  return p;\n}\n\n/* determine latitude from authalic latitude */\nvar P00 = 0.33333333333333333333;\n\nvar P01 = 0.17222222222222222222;\nvar P02 = 0.10257936507936507936;\nvar P10 = 0.06388888888888888888;\nvar P11 = 0.06640211640211640211;\nvar P20 = 0.01641501294219154443;\n\nfunction authset(es) {\n  var t;\n  var APA = [];\n  APA[0] = es * P00;\n  t = es * es;\n  APA[0] += t * P01;\n  APA[1] = t * P10;\n  t *= es;\n  APA[0] += t * P02;\n  APA[1] += t * P11;\n  APA[2] = t * P20;\n  return APA;\n}\n\nfunction authlat(beta, APA) {\n  var t = beta + beta;\n  return (beta + APA[0] * Math.sin(t) + APA[1] * Math.sin(t + t) + APA[2] * Math.sin(t + t + t));\n}\n\nvar names$i = [\"Lambert Azimuthal Equal Area\", \"Lambert_Azimuthal_Equal_Area\", \"laea\"];\nvar laea = {\n  init: init$i,\n  forward: forward$i,\n  inverse: inverse$i,\n  names: names$i,\n  S_POLE: S_POLE,\n  N_POLE: N_POLE,\n  EQUIT: EQUIT,\n  OBLIQ: OBLIQ\n};\n\nfunction asinz(x) {\n  if (Math.abs(x) > 1) {\n    x = (x > 1) ? 1 : -1;\n  }\n  return Math.asin(x);\n}\n\nfunction init$h() {\n\n  if (Math.abs(this.lat1 + this.lat2) < EPSLN) {\n    return;\n  }\n  this.temp = this.b / this.a;\n  this.es = 1 - Math.pow(this.temp, 2);\n  this.e3 = Math.sqrt(this.es);\n\n  this.sin_po = Math.sin(this.lat1);\n  this.cos_po = Math.cos(this.lat1);\n  this.t1 = this.sin_po;\n  this.con = this.sin_po;\n  this.ms1 = msfnz(this.e3, this.sin_po, this.cos_po);\n  this.qs1 = qsfnz(this.e3, this.sin_po);\n\n  this.sin_po = Math.sin(this.lat2);\n  this.cos_po = Math.cos(this.lat2);\n  this.t2 = this.sin_po;\n  this.ms2 = msfnz(this.e3, this.sin_po, this.cos_po);\n  this.qs2 = qsfnz(this.e3, this.sin_po);\n\n  this.sin_po = Math.sin(this.lat0);\n  this.cos_po = Math.cos(this.lat0);\n  this.t3 = this.sin_po;\n  this.qs0 = qsfnz(this.e3, this.sin_po);\n\n  if (Math.abs(this.lat1 - this.lat2) > EPSLN) {\n    this.ns0 = (this.ms1 * this.ms1 - this.ms2 * this.ms2) / (this.qs2 - this.qs1);\n  }\n  else {\n    this.ns0 = this.con;\n  }\n  this.c = this.ms1 * this.ms1 + this.ns0 * this.qs1;\n  this.rh = this.a * Math.sqrt(this.c - this.ns0 * this.qs0) / this.ns0;\n}\n\n/* Albers Conical Equal Area forward equations--mapping lat,long to x,y\n  -------------------------------------------------------------------*/\nfunction forward$h(p) {\n\n  var lon = p.x;\n  var lat = p.y;\n\n  this.sin_phi = Math.sin(lat);\n  this.cos_phi = Math.cos(lat);\n\n  var qs = qsfnz(this.e3, this.sin_phi);\n  var rh1 = this.a * Math.sqrt(this.c - this.ns0 * qs) / this.ns0;\n  var theta = this.ns0 * adjust_lon(lon - this.long0);\n  var x = rh1 * Math.sin(theta) + this.x0;\n  var y = this.rh - rh1 * Math.cos(theta) + this.y0;\n\n  p.x = x;\n  p.y = y;\n  return p;\n}\n\nfunction inverse$h(p) {\n  var rh1, qs, con, theta, lon, lat;\n\n  p.x -= this.x0;\n  p.y = this.rh - p.y + this.y0;\n  if (this.ns0 >= 0) {\n    rh1 = Math.sqrt(p.x * p.x + p.y * p.y);\n    con = 1;\n  }\n  else {\n    rh1 = -Math.sqrt(p.x * p.x + p.y * p.y);\n    con = -1;\n  }\n  theta = 0;\n  if (rh1 !== 0) {\n    theta = Math.atan2(con * p.x, con * p.y);\n  }\n  con = rh1 * this.ns0 / this.a;\n  if (this.sphere) {\n    lat = Math.asin((this.c - con * con) / (2 * this.ns0));\n  }\n  else {\n    qs = (this.c - con * con) / this.ns0;\n    lat = this.phi1z(this.e3, qs);\n  }\n\n  lon = adjust_lon(theta / this.ns0 + this.long0);\n  p.x = lon;\n  p.y = lat;\n  return p;\n}\n\n/* Function to compute phi1, the latitude for the inverse of the\n   Albers Conical Equal-Area projection.\n-------------------------------------------*/\nfunction phi1z(eccent, qs) {\n  var sinphi, cosphi, con, com, dphi;\n  var phi = asinz(0.5 * qs);\n  if (eccent < EPSLN) {\n    return phi;\n  }\n\n  var eccnts = eccent * eccent;\n  for (var i = 1; i <= 25; i++) {\n    sinphi = Math.sin(phi);\n    cosphi = Math.cos(phi);\n    con = eccent * sinphi;\n    com = 1 - con * con;\n    dphi = 0.5 * com * com / cosphi * (qs / (1 - eccnts) - sinphi / com + 0.5 / eccent * Math.log((1 - con) / (1 + con)));\n    phi = phi + dphi;\n    if (Math.abs(dphi) <= 1e-7) {\n      return phi;\n    }\n  }\n  return null;\n}\n\nvar names$h = [\"Albers_Conic_Equal_Area\", \"Albers\", \"aea\"];\nvar aea = {\n  init: init$h,\n  forward: forward$h,\n  inverse: inverse$h,\n  names: names$h,\n  phi1z: phi1z\n};\n\n/*\n  reference:\n    Wolfram Mathworld \"Gnomonic Projection\"\n    http://mathworld.wolfram.com/GnomonicProjection.html\n    Accessed: 12th November 2009\n  */\nfunction init$g() {\n\n  /* Place parameters in static storage for common use\n      -------------------------------------------------*/\n  this.sin_p14 = Math.sin(this.lat0);\n  this.cos_p14 = Math.cos(this.lat0);\n  // Approximation for projecting points to the horizon (infinity)\n  this.infinity_dist = 1000 * this.a;\n  this.rc = 1;\n}\n\n/* Gnomonic forward equations--mapping lat,long to x,y\n    ---------------------------------------------------*/\nfunction forward$g(p) {\n  var sinphi, cosphi; /* sin and cos value        */\n  var dlon; /* delta longitude value      */\n  var coslon; /* cos of longitude        */\n  var ksp; /* scale factor          */\n  var g;\n  var x, y;\n  var lon = p.x;\n  var lat = p.y;\n  /* Forward equations\n      -----------------*/\n  dlon = adjust_lon(lon - this.long0);\n\n  sinphi = Math.sin(lat);\n  cosphi = Math.cos(lat);\n\n  coslon = Math.cos(dlon);\n  g = this.sin_p14 * sinphi + this.cos_p14 * cosphi * coslon;\n  ksp = 1;\n  if ((g > 0) || (Math.abs(g) <= EPSLN)) {\n    x = this.x0 + this.a * ksp * cosphi * Math.sin(dlon) / g;\n    y = this.y0 + this.a * ksp * (this.cos_p14 * sinphi - this.sin_p14 * cosphi * coslon) / g;\n  }\n  else {\n\n    // Point is in the opposing hemisphere and is unprojectable\n    // We still need to return a reasonable point, so we project\n    // to infinity, on a bearing\n    // equivalent to the northern hemisphere equivalent\n    // This is a reasonable approximation for short shapes and lines that\n    // straddle the horizon.\n\n    x = this.x0 + this.infinity_dist * cosphi * Math.sin(dlon);\n    y = this.y0 + this.infinity_dist * (this.cos_p14 * sinphi - this.sin_p14 * cosphi * coslon);\n\n  }\n  p.x = x;\n  p.y = y;\n  return p;\n}\n\nfunction inverse$g(p) {\n  var rh; /* Rho */\n  var sinc, cosc;\n  var c;\n  var lon, lat;\n\n  /* Inverse equations\n      -----------------*/\n  p.x = (p.x - this.x0) / this.a;\n  p.y = (p.y - this.y0) / this.a;\n\n  p.x /= this.k0;\n  p.y /= this.k0;\n\n  if ((rh = Math.sqrt(p.x * p.x + p.y * p.y))) {\n    c = Math.atan2(rh, this.rc);\n    sinc = Math.sin(c);\n    cosc = Math.cos(c);\n\n    lat = asinz(cosc * this.sin_p14 + (p.y * sinc * this.cos_p14) / rh);\n    lon = Math.atan2(p.x * sinc, rh * this.cos_p14 * cosc - p.y * this.sin_p14 * sinc);\n    lon = adjust_lon(this.long0 + lon);\n  }\n  else {\n    lat = this.phic0;\n    lon = 0;\n  }\n\n  p.x = lon;\n  p.y = lat;\n  return p;\n}\n\nvar names$g = [\"gnom\"];\nvar gnom = {\n  init: init$g,\n  forward: forward$g,\n  inverse: inverse$g,\n  names: names$g\n};\n\nfunction iqsfnz(eccent, q) {\n  var temp = 1 - (1 - eccent * eccent) / (2 * eccent) * Math.log((1 - eccent) / (1 + eccent));\n  if (Math.abs(Math.abs(q) - temp) < 1.0E-6) {\n    if (q < 0) {\n      return (-1 * HALF_PI);\n    }\n    else {\n      return HALF_PI;\n    }\n  }\n  //var phi = 0.5* q/(1-eccent*eccent);\n  var phi = Math.asin(0.5 * q);\n  var dphi;\n  var sin_phi;\n  var cos_phi;\n  var con;\n  for (var i = 0; i < 30; i++) {\n    sin_phi = Math.sin(phi);\n    cos_phi = Math.cos(phi);\n    con = eccent * sin_phi;\n    dphi = Math.pow(1 - con * con, 2) / (2 * cos_phi) * (q / (1 - eccent * eccent) - sin_phi / (1 - con * con) + 0.5 / eccent * Math.log((1 - con) / (1 + con)));\n    phi += dphi;\n    if (Math.abs(dphi) <= 0.0000000001) {\n      return phi;\n    }\n  }\n\n  //console.log(\"IQSFN-CONV:Latitude failed to converge after 30 iterations\");\n  return NaN;\n}\n\n/*\n  reference:\n    \"Cartographic Projection Procedures for the UNIX Environment-\n    A User's Manual\" by Gerald I. Evenden,\n    USGS Open File Report 90-284and Release 4 Interim Reports (2003)\n*/\nfunction init$f() {\n  //no-op\n  if (!this.sphere) {\n    this.k0 = msfnz(this.e, Math.sin(this.lat_ts), Math.cos(this.lat_ts));\n  }\n}\n\n/* Cylindrical Equal Area forward equations--mapping lat,long to x,y\n    ------------------------------------------------------------*/\nfunction forward$f(p) {\n  var lon = p.x;\n  var lat = p.y;\n  var x, y;\n  /* Forward equations\n      -----------------*/\n  var dlon = adjust_lon(lon - this.long0);\n  if (this.sphere) {\n    x = this.x0 + this.a * dlon * Math.cos(this.lat_ts);\n    y = this.y0 + this.a * Math.sin(lat) / Math.cos(this.lat_ts);\n  }\n  else {\n    var qs = qsfnz(this.e, Math.sin(lat));\n    x = this.x0 + this.a * this.k0 * dlon;\n    y = this.y0 + this.a * qs * 0.5 / this.k0;\n  }\n\n  p.x = x;\n  p.y = y;\n  return p;\n}\n\n/* Cylindrical Equal Area inverse equations--mapping x,y to lat/long\n    ------------------------------------------------------------*/\nfunction inverse$f(p) {\n  p.x -= this.x0;\n  p.y -= this.y0;\n  var lon, lat;\n\n  if (this.sphere) {\n    lon = adjust_lon(this.long0 + (p.x / this.a) / Math.cos(this.lat_ts));\n    lat = Math.asin((p.y / this.a) * Math.cos(this.lat_ts));\n  }\n  else {\n    lat = iqsfnz(this.e, 2 * p.y * this.k0 / this.a);\n    lon = adjust_lon(this.long0 + p.x / (this.a * this.k0));\n  }\n\n  p.x = lon;\n  p.y = lat;\n  return p;\n}\n\nvar names$f = [\"cea\"];\nvar cea = {\n  init: init$f,\n  forward: forward$f,\n  inverse: inverse$f,\n  names: names$f\n};\n\nfunction init$e() {\n\n  this.x0 = this.x0 || 0;\n  this.y0 = this.y0 || 0;\n  this.lat0 = this.lat0 || 0;\n  this.long0 = this.long0 || 0;\n  this.lat_ts = this.lat_ts || 0;\n  this.title = this.title || \"Equidistant Cylindrical (Plate Carre)\";\n\n  this.rc = Math.cos(this.lat_ts);\n}\n\n// forward equations--mapping lat,long to x,y\n// -----------------------------------------------------------------\nfunction forward$e(p) {\n\n  var lon = p.x;\n  var lat = p.y;\n\n  var dlon = adjust_lon(lon - this.long0);\n  var dlat = adjust_lat(lat - this.lat0);\n  p.x = this.x0 + (this.a * dlon * this.rc);\n  p.y = this.y0 + (this.a * dlat);\n  return p;\n}\n\n// inverse equations--mapping x,y to lat/long\n// -----------------------------------------------------------------\nfunction inverse$e(p) {\n\n  var x = p.x;\n  var y = p.y;\n\n  p.x = adjust_lon(this.long0 + ((x - this.x0) / (this.a * this.rc)));\n  p.y = adjust_lat(this.lat0 + ((y - this.y0) / (this.a)));\n  return p;\n}\n\nvar names$e = [\"Equirectangular\", \"Equidistant_Cylindrical\", \"eqc\"];\nvar eqc = {\n  init: init$e,\n  forward: forward$e,\n  inverse: inverse$e,\n  names: names$e\n};\n\nvar MAX_ITER$1 = 20;\n\nfunction init$d() {\n  /* Place parameters in static storage for common use\n      -------------------------------------------------*/\n  this.temp = this.b / this.a;\n  this.es = 1 - Math.pow(this.temp, 2); // devait etre dans tmerc.js mais n y est pas donc je commente sinon retour de valeurs nulles\n  this.e = Math.sqrt(this.es);\n  this.e0 = e0fn(this.es);\n  this.e1 = e1fn(this.es);\n  this.e2 = e2fn(this.es);\n  this.e3 = e3fn(this.es);\n  this.ml0 = this.a * mlfn(this.e0, this.e1, this.e2, this.e3, this.lat0); //si que des zeros le calcul ne se fait pas\n}\n\n/* Polyconic forward equations--mapping lat,long to x,y\n    ---------------------------------------------------*/\nfunction forward$d(p) {\n  var lon = p.x;\n  var lat = p.y;\n  var x, y, el;\n  var dlon = adjust_lon(lon - this.long0);\n  el = dlon * Math.sin(lat);\n  if (this.sphere) {\n    if (Math.abs(lat) <= EPSLN) {\n      x = this.a * dlon;\n      y = -1 * this.a * this.lat0;\n    }\n    else {\n      x = this.a * Math.sin(el) / Math.tan(lat);\n      y = this.a * (adjust_lat(lat - this.lat0) + (1 - Math.cos(el)) / Math.tan(lat));\n    }\n  }\n  else {\n    if (Math.abs(lat) <= EPSLN) {\n      x = this.a * dlon;\n      y = -1 * this.ml0;\n    }\n    else {\n      var nl = gN(this.a, this.e, Math.sin(lat)) / Math.tan(lat);\n      x = nl * Math.sin(el);\n      y = this.a * mlfn(this.e0, this.e1, this.e2, this.e3, lat) - this.ml0 + nl * (1 - Math.cos(el));\n    }\n\n  }\n  p.x = x + this.x0;\n  p.y = y + this.y0;\n  return p;\n}\n\n/* Inverse equations\n  -----------------*/\nfunction inverse$d(p) {\n  var lon, lat, x, y, i;\n  var al, bl;\n  var phi, dphi;\n  x = p.x - this.x0;\n  y = p.y - this.y0;\n\n  if (this.sphere) {\n    if (Math.abs(y + this.a * this.lat0) <= EPSLN) {\n      lon = adjust_lon(x / this.a + this.long0);\n      lat = 0;\n    }\n    else {\n      al = this.lat0 + y / this.a;\n      bl = x * x / this.a / this.a + al * al;\n      phi = al;\n      var tanphi;\n      for (i = MAX_ITER$1; i; --i) {\n        tanphi = Math.tan(phi);\n        dphi = -1 * (al * (phi * tanphi + 1) - phi - 0.5 * (phi * phi + bl) * tanphi) / ((phi - al) / tanphi - 1);\n        phi += dphi;\n        if (Math.abs(dphi) <= EPSLN) {\n          lat = phi;\n          break;\n        }\n      }\n      lon = adjust_lon(this.long0 + (Math.asin(x * Math.tan(phi) / this.a)) / Math.sin(lat));\n    }\n  }\n  else {\n    if (Math.abs(y + this.ml0) <= EPSLN) {\n      lat = 0;\n      lon = adjust_lon(this.long0 + x / this.a);\n    }\n    else {\n\n      al = (this.ml0 + y) / this.a;\n      bl = x * x / this.a / this.a + al * al;\n      phi = al;\n      var cl, mln, mlnp, ma;\n      var con;\n      for (i = MAX_ITER$1; i; --i) {\n        con = this.e * Math.sin(phi);\n        cl = Math.sqrt(1 - con * con) * Math.tan(phi);\n        mln = this.a * mlfn(this.e0, this.e1, this.e2, this.e3, phi);\n        mlnp = this.e0 - 2 * this.e1 * Math.cos(2 * phi) + 4 * this.e2 * Math.cos(4 * phi) - 6 * this.e3 * Math.cos(6 * phi);\n        ma = mln / this.a;\n        dphi = (al * (cl * ma + 1) - ma - 0.5 * cl * (ma * ma + bl)) / (this.es * Math.sin(2 * phi) * (ma * ma + bl - 2 * al * ma) / (4 * cl) + (al - ma) * (cl * mlnp - 2 / Math.sin(2 * phi)) - mlnp);\n        phi -= dphi;\n        if (Math.abs(dphi) <= EPSLN) {\n          lat = phi;\n          break;\n        }\n      }\n\n      //lat=phi4z(this.e,this.e0,this.e1,this.e2,this.e3,al,bl,0,0);\n      cl = Math.sqrt(1 - this.es * Math.pow(Math.sin(lat), 2)) * Math.tan(lat);\n      lon = adjust_lon(this.long0 + Math.asin(x * cl / this.a) / Math.sin(lat));\n    }\n  }\n\n  p.x = lon;\n  p.y = lat;\n  return p;\n}\n\nvar names$d = [\"Polyconic\", \"poly\"];\nvar poly = {\n  init: init$d,\n  forward: forward$d,\n  inverse: inverse$d,\n  names: names$d\n};\n\nfunction init$c() {\n  this.A = [];\n  this.A[1] = 0.6399175073;\n  this.A[2] = -0.1358797613;\n  this.A[3] = 0.063294409;\n  this.A[4] = -0.02526853;\n  this.A[5] = 0.0117879;\n  this.A[6] = -0.0055161;\n  this.A[7] = 0.0026906;\n  this.A[8] = -0.001333;\n  this.A[9] = 0.00067;\n  this.A[10] = -0.00034;\n\n  this.B_re = [];\n  this.B_im = [];\n  this.B_re[1] = 0.7557853228;\n  this.B_im[1] = 0;\n  this.B_re[2] = 0.249204646;\n  this.B_im[2] = 0.003371507;\n  this.B_re[3] = -0.001541739;\n  this.B_im[3] = 0.041058560;\n  this.B_re[4] = -0.10162907;\n  this.B_im[4] = 0.01727609;\n  this.B_re[5] = -0.26623489;\n  this.B_im[5] = -0.36249218;\n  this.B_re[6] = -0.6870983;\n  this.B_im[6] = -1.1651967;\n\n  this.C_re = [];\n  this.C_im = [];\n  this.C_re[1] = 1.3231270439;\n  this.C_im[1] = 0;\n  this.C_re[2] = -0.577245789;\n  this.C_im[2] = -0.007809598;\n  this.C_re[3] = 0.508307513;\n  this.C_im[3] = -0.112208952;\n  this.C_re[4] = -0.15094762;\n  this.C_im[4] = 0.18200602;\n  this.C_re[5] = 1.01418179;\n  this.C_im[5] = 1.64497696;\n  this.C_re[6] = 1.9660549;\n  this.C_im[6] = 2.5127645;\n\n  this.D = [];\n  this.D[1] = 1.5627014243;\n  this.D[2] = 0.5185406398;\n  this.D[3] = -0.03333098;\n  this.D[4] = -0.1052906;\n  this.D[5] = -0.0368594;\n  this.D[6] = 0.007317;\n  this.D[7] = 0.01220;\n  this.D[8] = 0.00394;\n  this.D[9] = -0.0013;\n}\n\n/**\n    New Zealand Map Grid Forward  - long/lat to x/y\n    long/lat in radians\n  */\nfunction forward$c(p) {\n  var n;\n  var lon = p.x;\n  var lat = p.y;\n\n  var delta_lat = lat - this.lat0;\n  var delta_lon = lon - this.long0;\n\n  // 1. Calculate d_phi and d_psi    ...                          // and d_lambda\n  // For this algorithm, delta_latitude is in seconds of arc x 10-5, so we need to scale to those units. Longitude is radians.\n  var d_phi = delta_lat / SEC_TO_RAD * 1E-5;\n  var d_lambda = delta_lon;\n  var d_phi_n = 1; // d_phi^0\n\n  var d_psi = 0;\n  for (n = 1; n <= 10; n++) {\n    d_phi_n = d_phi_n * d_phi;\n    d_psi = d_psi + this.A[n] * d_phi_n;\n  }\n\n  // 2. Calculate theta\n  var th_re = d_psi;\n  var th_im = d_lambda;\n\n  // 3. Calculate z\n  var th_n_re = 1;\n  var th_n_im = 0; // theta^0\n  var th_n_re1;\n  var th_n_im1;\n\n  var z_re = 0;\n  var z_im = 0;\n  for (n = 1; n <= 6; n++) {\n    th_n_re1 = th_n_re * th_re - th_n_im * th_im;\n    th_n_im1 = th_n_im * th_re + th_n_re * th_im;\n    th_n_re = th_n_re1;\n    th_n_im = th_n_im1;\n    z_re = z_re + this.B_re[n] * th_n_re - this.B_im[n] * th_n_im;\n    z_im = z_im + this.B_im[n] * th_n_re + this.B_re[n] * th_n_im;\n  }\n\n  // 4. Calculate easting and northing\n  p.x = (z_im * this.a) + this.x0;\n  p.y = (z_re * this.a) + this.y0;\n\n  return p;\n}\n\n/**\n    New Zealand Map Grid Inverse  -  x/y to long/lat\n  */\nfunction inverse$c(p) {\n  var n;\n  var x = p.x;\n  var y = p.y;\n\n  var delta_x = x - this.x0;\n  var delta_y = y - this.y0;\n\n  // 1. Calculate z\n  var z_re = delta_y / this.a;\n  var z_im = delta_x / this.a;\n\n  // 2a. Calculate theta - first approximation gives km accuracy\n  var z_n_re = 1;\n  var z_n_im = 0; // z^0\n  var z_n_re1;\n  var z_n_im1;\n\n  var th_re = 0;\n  var th_im = 0;\n  for (n = 1; n <= 6; n++) {\n    z_n_re1 = z_n_re * z_re - z_n_im * z_im;\n    z_n_im1 = z_n_im * z_re + z_n_re * z_im;\n    z_n_re = z_n_re1;\n    z_n_im = z_n_im1;\n    th_re = th_re + this.C_re[n] * z_n_re - this.C_im[n] * z_n_im;\n    th_im = th_im + this.C_im[n] * z_n_re + this.C_re[n] * z_n_im;\n  }\n\n  // 2b. Iterate to refine the accuracy of the calculation\n  //        0 iterations gives km accuracy\n  //        1 iteration gives m accuracy -- good enough for most mapping applications\n  //        2 iterations bives mm accuracy\n  for (var i = 0; i < this.iterations; i++) {\n    var th_n_re = th_re;\n    var th_n_im = th_im;\n    var th_n_re1;\n    var th_n_im1;\n\n    var num_re = z_re;\n    var num_im = z_im;\n    for (n = 2; n <= 6; n++) {\n      th_n_re1 = th_n_re * th_re - th_n_im * th_im;\n      th_n_im1 = th_n_im * th_re + th_n_re * th_im;\n      th_n_re = th_n_re1;\n      th_n_im = th_n_im1;\n      num_re = num_re + (n - 1) * (this.B_re[n] * th_n_re - this.B_im[n] * th_n_im);\n      num_im = num_im + (n - 1) * (this.B_im[n] * th_n_re + this.B_re[n] * th_n_im);\n    }\n\n    th_n_re = 1;\n    th_n_im = 0;\n    var den_re = this.B_re[1];\n    var den_im = this.B_im[1];\n    for (n = 2; n <= 6; n++) {\n      th_n_re1 = th_n_re * th_re - th_n_im * th_im;\n      th_n_im1 = th_n_im * th_re + th_n_re * th_im;\n      th_n_re = th_n_re1;\n      th_n_im = th_n_im1;\n      den_re = den_re + n * (this.B_re[n] * th_n_re - this.B_im[n] * th_n_im);\n      den_im = den_im + n * (this.B_im[n] * th_n_re + this.B_re[n] * th_n_im);\n    }\n\n    // Complex division\n    var den2 = den_re * den_re + den_im * den_im;\n    th_re = (num_re * den_re + num_im * den_im) / den2;\n    th_im = (num_im * den_re - num_re * den_im) / den2;\n  }\n\n  // 3. Calculate d_phi              ...                                    // and d_lambda\n  var d_psi = th_re;\n  var d_lambda = th_im;\n  var d_psi_n = 1; // d_psi^0\n\n  var d_phi = 0;\n  for (n = 1; n <= 9; n++) {\n    d_psi_n = d_psi_n * d_psi;\n    d_phi = d_phi + this.D[n] * d_psi_n;\n  }\n\n  // 4. Calculate latitude and longitude\n  // d_phi is calcuated in second of arc * 10^-5, so we need to scale back to radians. d_lambda is in radians.\n  var lat = this.lat0 + (d_phi * SEC_TO_RAD * 1E5);\n  var lon = this.long0 + d_lambda;\n\n  p.x = lon;\n  p.y = lat;\n\n  return p;\n}\n\nvar names$c = [\"New_Zealand_Map_Grid\", \"nzmg\"];\nvar nzmg = {\n  init: init$c,\n  forward: forward$c,\n  inverse: inverse$c,\n  names: names$c\n};\n\n/*\n  reference\n    \"New Equal-Area Map Projections for Noncircular Regions\", John P. Snyder,\n    The American Cartographer, Vol 15, No. 4, October 1988, pp. 341-355.\n  */\n\n\n/* Initialize the Miller Cylindrical projection\n  -------------------------------------------*/\nfunction init$b() {\n  //no-op\n}\n\n/* Miller Cylindrical forward equations--mapping lat,long to x,y\n    ------------------------------------------------------------*/\nfunction forward$b(p) {\n  var lon = p.x;\n  var lat = p.y;\n  /* Forward equations\n      -----------------*/\n  var dlon = adjust_lon(lon - this.long0);\n  var x = this.x0 + this.a * dlon;\n  var y = this.y0 + this.a * Math.log(Math.tan((Math.PI / 4) + (lat / 2.5))) * 1.25;\n\n  p.x = x;\n  p.y = y;\n  return p;\n}\n\n/* Miller Cylindrical inverse equations--mapping x,y to lat/long\n    ------------------------------------------------------------*/\nfunction inverse$b(p) {\n  p.x -= this.x0;\n  p.y -= this.y0;\n\n  var lon = adjust_lon(this.long0 + p.x / this.a);\n  var lat = 2.5 * (Math.atan(Math.exp(0.8 * p.y / this.a)) - Math.PI / 4);\n\n  p.x = lon;\n  p.y = lat;\n  return p;\n}\n\nvar names$b = [\"Miller_Cylindrical\", \"mill\"];\nvar mill = {\n  init: init$b,\n  forward: forward$b,\n  inverse: inverse$b,\n  names: names$b\n};\n\nvar MAX_ITER = 20;\n\n\nfunction init$a() {\n  /* Place parameters in static storage for common use\n    -------------------------------------------------*/\n\n\n  if (!this.sphere) {\n    this.en = pj_enfn(this.es);\n  }\n  else {\n    this.n = 1;\n    this.m = 0;\n    this.es = 0;\n    this.C_y = Math.sqrt((this.m + 1) / this.n);\n    this.C_x = this.C_y / (this.m + 1);\n  }\n\n}\n\n/* Sinusoidal forward equations--mapping lat,long to x,y\n  -----------------------------------------------------*/\nfunction forward$a(p) {\n  var x, y;\n  var lon = p.x;\n  var lat = p.y;\n  /* Forward equations\n    -----------------*/\n  lon = adjust_lon(lon - this.long0);\n\n  if (this.sphere) {\n    if (!this.m) {\n      lat = this.n !== 1 ? Math.asin(this.n * Math.sin(lat)) : lat;\n    }\n    else {\n      var k = this.n * Math.sin(lat);\n      for (var i = MAX_ITER; i; --i) {\n        var V = (this.m * lat + Math.sin(lat) - k) / (this.m + Math.cos(lat));\n        lat -= V;\n        if (Math.abs(V) < EPSLN) {\n          break;\n        }\n      }\n    }\n    x = this.a * this.C_x * lon * (this.m + Math.cos(lat));\n    y = this.a * this.C_y * lat;\n\n  }\n  else {\n\n    var s = Math.sin(lat);\n    var c = Math.cos(lat);\n    y = this.a * pj_mlfn(lat, s, c, this.en);\n    x = this.a * lon * c / Math.sqrt(1 - this.es * s * s);\n  }\n\n  p.x = x;\n  p.y = y;\n  return p;\n}\n\nfunction inverse$a(p) {\n  var lat, temp, lon, s;\n\n  p.x -= this.x0;\n  lon = p.x / this.a;\n  p.y -= this.y0;\n  lat = p.y / this.a;\n\n  if (this.sphere) {\n    lat /= this.C_y;\n    lon = lon / (this.C_x * (this.m + Math.cos(lat)));\n    if (this.m) {\n      lat = asinz((this.m * lat + Math.sin(lat)) / this.n);\n    }\n    else if (this.n !== 1) {\n      lat = asinz(Math.sin(lat) / this.n);\n    }\n    lon = adjust_lon(lon + this.long0);\n    lat = adjust_lat(lat);\n  }\n  else {\n    lat = pj_inv_mlfn(p.y / this.a, this.es, this.en);\n    s = Math.abs(lat);\n    if (s < HALF_PI) {\n      s = Math.sin(lat);\n      temp = this.long0 + p.x * Math.sqrt(1 - this.es * s * s) / (this.a * Math.cos(lat));\n      //temp = this.long0 + p.x / (this.a * Math.cos(lat));\n      lon = adjust_lon(temp);\n    }\n    else if ((s - EPSLN) < HALF_PI) {\n      lon = this.long0;\n    }\n  }\n  p.x = lon;\n  p.y = lat;\n  return p;\n}\n\nvar names$a = [\"Sinusoidal\", \"sinu\"];\nvar sinu = {\n  init: init$a,\n  forward: forward$a,\n  inverse: inverse$a,\n  names: names$a\n};\n\nfunction init$9() {}\n/* Mollweide forward equations--mapping lat,long to x,y\n    ----------------------------------------------------*/\nfunction forward$9(p) {\n\n  /* Forward equations\n      -----------------*/\n  var lon = p.x;\n  var lat = p.y;\n\n  var delta_lon = adjust_lon(lon - this.long0);\n  var theta = lat;\n  var con = Math.PI * Math.sin(lat);\n\n  /* Iterate using the Newton-Raphson method to find theta\n      -----------------------------------------------------*/\n  while (true) {\n    var delta_theta = -(theta + Math.sin(theta) - con) / (1 + Math.cos(theta));\n    theta += delta_theta;\n    if (Math.abs(delta_theta) < EPSLN) {\n      break;\n    }\n  }\n  theta /= 2;\n\n  /* If the latitude is 90 deg, force the x coordinate to be \"0 + false easting\"\n       this is done here because of precision problems with \"cos(theta)\"\n       --------------------------------------------------------------------------*/\n  if (Math.PI / 2 - Math.abs(lat) < EPSLN) {\n    delta_lon = 0;\n  }\n  var x = 0.900316316158 * this.a * delta_lon * Math.cos(theta) + this.x0;\n  var y = 1.4142135623731 * this.a * Math.sin(theta) + this.y0;\n\n  p.x = x;\n  p.y = y;\n  return p;\n}\n\nfunction inverse$9(p) {\n  var theta;\n  var arg;\n\n  /* Inverse equations\n      -----------------*/\n  p.x -= this.x0;\n  p.y -= this.y0;\n  arg = p.y / (1.4142135623731 * this.a);\n\n  /* Because of division by zero problems, 'arg' can not be 1.  Therefore\n       a number very close to one is used instead.\n       -------------------------------------------------------------------*/\n  if (Math.abs(arg) > 0.999999999999) {\n    arg = 0.999999999999;\n  }\n  theta = Math.asin(arg);\n  var lon = adjust_lon(this.long0 + (p.x / (0.900316316158 * this.a * Math.cos(theta))));\n  if (lon < (-Math.PI)) {\n    lon = -Math.PI;\n  }\n  if (lon > Math.PI) {\n    lon = Math.PI;\n  }\n  arg = (2 * theta + Math.sin(2 * theta)) / Math.PI;\n  if (Math.abs(arg) > 1) {\n    arg = 1;\n  }\n  var lat = Math.asin(arg);\n\n  p.x = lon;\n  p.y = lat;\n  return p;\n}\n\nvar names$9 = [\"Mollweide\", \"moll\"];\nvar moll = {\n  init: init$9,\n  forward: forward$9,\n  inverse: inverse$9,\n  names: names$9\n};\n\nfunction init$8() {\n\n  /* Place parameters in static storage for common use\n      -------------------------------------------------*/\n  // Standard Parallels cannot be equal and on opposite sides of the equator\n  if (Math.abs(this.lat1 + this.lat2) < EPSLN) {\n    return;\n  }\n  this.lat2 = this.lat2 || this.lat1;\n  this.temp = this.b / this.a;\n  this.es = 1 - Math.pow(this.temp, 2);\n  this.e = Math.sqrt(this.es);\n  this.e0 = e0fn(this.es);\n  this.e1 = e1fn(this.es);\n  this.e2 = e2fn(this.es);\n  this.e3 = e3fn(this.es);\n\n  this.sinphi = Math.sin(this.lat1);\n  this.cosphi = Math.cos(this.lat1);\n\n  this.ms1 = msfnz(this.e, this.sinphi, this.cosphi);\n  this.ml1 = mlfn(this.e0, this.e1, this.e2, this.e3, this.lat1);\n\n  if (Math.abs(this.lat1 - this.lat2) < EPSLN) {\n    this.ns = this.sinphi;\n  }\n  else {\n    this.sinphi = Math.sin(this.lat2);\n    this.cosphi = Math.cos(this.lat2);\n    this.ms2 = msfnz(this.e, this.sinphi, this.cosphi);\n    this.ml2 = mlfn(this.e0, this.e1, this.e2, this.e3, this.lat2);\n    this.ns = (this.ms1 - this.ms2) / (this.ml2 - this.ml1);\n  }\n  this.g = this.ml1 + this.ms1 / this.ns;\n  this.ml0 = mlfn(this.e0, this.e1, this.e2, this.e3, this.lat0);\n  this.rh = this.a * (this.g - this.ml0);\n}\n\n/* Equidistant Conic forward equations--mapping lat,long to x,y\n  -----------------------------------------------------------*/\nfunction forward$8(p) {\n  var lon = p.x;\n  var lat = p.y;\n  var rh1;\n\n  /* Forward equations\n      -----------------*/\n  if (this.sphere) {\n    rh1 = this.a * (this.g - lat);\n  }\n  else {\n    var ml = mlfn(this.e0, this.e1, this.e2, this.e3, lat);\n    rh1 = this.a * (this.g - ml);\n  }\n  var theta = this.ns * adjust_lon(lon - this.long0);\n  var x = this.x0 + rh1 * Math.sin(theta);\n  var y = this.y0 + this.rh - rh1 * Math.cos(theta);\n  p.x = x;\n  p.y = y;\n  return p;\n}\n\n/* Inverse equations\n  -----------------*/\nfunction inverse$8(p) {\n  p.x -= this.x0;\n  p.y = this.rh - p.y + this.y0;\n  var con, rh1, lat, lon;\n  if (this.ns >= 0) {\n    rh1 = Math.sqrt(p.x * p.x + p.y * p.y);\n    con = 1;\n  }\n  else {\n    rh1 = -Math.sqrt(p.x * p.x + p.y * p.y);\n    con = -1;\n  }\n  var theta = 0;\n  if (rh1 !== 0) {\n    theta = Math.atan2(con * p.x, con * p.y);\n  }\n\n  if (this.sphere) {\n    lon = adjust_lon(this.long0 + theta / this.ns);\n    lat = adjust_lat(this.g - rh1 / this.a);\n    p.x = lon;\n    p.y = lat;\n    return p;\n  }\n  else {\n    var ml = this.g - rh1 / this.a;\n    lat = imlfn(ml, this.e0, this.e1, this.e2, this.e3);\n    lon = adjust_lon(this.long0 + theta / this.ns);\n    p.x = lon;\n    p.y = lat;\n    return p;\n  }\n\n}\n\nvar names$8 = [\"Equidistant_Conic\", \"eqdc\"];\nvar eqdc = {\n  init: init$8,\n  forward: forward$8,\n  inverse: inverse$8,\n  names: names$8\n};\n\n/* Initialize the Van Der Grinten projection\n  ----------------------------------------*/\nfunction init$7() {\n  //this.R = 6370997; //Radius of earth\n  this.R = this.a;\n}\n\nfunction forward$7(p) {\n\n  var lon = p.x;\n  var lat = p.y;\n\n  /* Forward equations\n    -----------------*/\n  var dlon = adjust_lon(lon - this.long0);\n  var x, y;\n\n  if (Math.abs(lat) <= EPSLN) {\n    x = this.x0 + this.R * dlon;\n    y = this.y0;\n  }\n  var theta = asinz(2 * Math.abs(lat / Math.PI));\n  if ((Math.abs(dlon) <= EPSLN) || (Math.abs(Math.abs(lat) - HALF_PI) <= EPSLN)) {\n    x = this.x0;\n    if (lat >= 0) {\n      y = this.y0 + Math.PI * this.R * Math.tan(0.5 * theta);\n    }\n    else {\n      y = this.y0 + Math.PI * this.R * -Math.tan(0.5 * theta);\n    }\n    //  return(OK);\n  }\n  var al = 0.5 * Math.abs((Math.PI / dlon) - (dlon / Math.PI));\n  var asq = al * al;\n  var sinth = Math.sin(theta);\n  var costh = Math.cos(theta);\n\n  var g = costh / (sinth + costh - 1);\n  var gsq = g * g;\n  var m = g * (2 / sinth - 1);\n  var msq = m * m;\n  var con = Math.PI * this.R * (al * (g - msq) + Math.sqrt(asq * (g - msq) * (g - msq) - (msq + asq) * (gsq - msq))) / (msq + asq);\n  if (dlon < 0) {\n    con = -con;\n  }\n  x = this.x0 + con;\n  //con = Math.abs(con / (Math.PI * this.R));\n  var q = asq + g;\n  con = Math.PI * this.R * (m * q - al * Math.sqrt((msq + asq) * (asq + 1) - q * q)) / (msq + asq);\n  if (lat >= 0) {\n    //y = this.y0 + Math.PI * this.R * Math.sqrt(1 - con * con - 2 * al * con);\n    y = this.y0 + con;\n  }\n  else {\n    //y = this.y0 - Math.PI * this.R * Math.sqrt(1 - con * con - 2 * al * con);\n    y = this.y0 - con;\n  }\n  p.x = x;\n  p.y = y;\n  return p;\n}\n\n/* Van Der Grinten inverse equations--mapping x,y to lat/long\n  ---------------------------------------------------------*/\nfunction inverse$7(p) {\n  var lon, lat;\n  var xx, yy, xys, c1, c2, c3;\n  var a1;\n  var m1;\n  var con;\n  var th1;\n  var d;\n\n  /* inverse equations\n    -----------------*/\n  p.x -= this.x0;\n  p.y -= this.y0;\n  con = Math.PI * this.R;\n  xx = p.x / con;\n  yy = p.y / con;\n  xys = xx * xx + yy * yy;\n  c1 = -Math.abs(yy) * (1 + xys);\n  c2 = c1 - 2 * yy * yy + xx * xx;\n  c3 = -2 * c1 + 1 + 2 * yy * yy + xys * xys;\n  d = yy * yy / c3 + (2 * c2 * c2 * c2 / c3 / c3 / c3 - 9 * c1 * c2 / c3 / c3) / 27;\n  a1 = (c1 - c2 * c2 / 3 / c3) / c3;\n  m1 = 2 * Math.sqrt(-a1 / 3);\n  con = ((3 * d) / a1) / m1;\n  if (Math.abs(con) > 1) {\n    if (con >= 0) {\n      con = 1;\n    }\n    else {\n      con = -1;\n    }\n  }\n  th1 = Math.acos(con) / 3;\n  if (p.y >= 0) {\n    lat = (-m1 * Math.cos(th1 + Math.PI / 3) - c2 / 3 / c3) * Math.PI;\n  }\n  else {\n    lat = -(-m1 * Math.cos(th1 + Math.PI / 3) - c2 / 3 / c3) * Math.PI;\n  }\n\n  if (Math.abs(xx) < EPSLN) {\n    lon = this.long0;\n  }\n  else {\n    lon = adjust_lon(this.long0 + Math.PI * (xys - 1 + Math.sqrt(1 + 2 * (xx * xx - yy * yy) + xys * xys)) / 2 / xx);\n  }\n\n  p.x = lon;\n  p.y = lat;\n  return p;\n}\n\nvar names$7 = [\"Van_der_Grinten_I\", \"VanDerGrinten\", \"vandg\"];\nvar vandg = {\n  init: init$7,\n  forward: forward$7,\n  inverse: inverse$7,\n  names: names$7\n};\n\nfunction init$6() {\n  this.sin_p12 = Math.sin(this.lat0);\n  this.cos_p12 = Math.cos(this.lat0);\n}\n\nfunction forward$6(p) {\n  var lon = p.x;\n  var lat = p.y;\n  var sinphi = Math.sin(p.y);\n  var cosphi = Math.cos(p.y);\n  var dlon = adjust_lon(lon - this.long0);\n  var e0, e1, e2, e3, Mlp, Ml, tanphi, Nl1, Nl, psi, Az, G, H, GH, Hs, c, kp, cos_c, s, s2, s3, s4, s5;\n  if (this.sphere) {\n    if (Math.abs(this.sin_p12 - 1) <= EPSLN) {\n      //North Pole case\n      p.x = this.x0 + this.a * (HALF_PI - lat) * Math.sin(dlon);\n      p.y = this.y0 - this.a * (HALF_PI - lat) * Math.cos(dlon);\n      return p;\n    }\n    else if (Math.abs(this.sin_p12 + 1) <= EPSLN) {\n      //South Pole case\n      p.x = this.x0 + this.a * (HALF_PI + lat) * Math.sin(dlon);\n      p.y = this.y0 + this.a * (HALF_PI + lat) * Math.cos(dlon);\n      return p;\n    }\n    else {\n      //default case\n      cos_c = this.sin_p12 * sinphi + this.cos_p12 * cosphi * Math.cos(dlon);\n      c = Math.acos(cos_c);\n      kp = c ? c / Math.sin(c) : 1;\n      p.x = this.x0 + this.a * kp * cosphi * Math.sin(dlon);\n      p.y = this.y0 + this.a * kp * (this.cos_p12 * sinphi - this.sin_p12 * cosphi * Math.cos(dlon));\n      return p;\n    }\n  }\n  else {\n    e0 = e0fn(this.es);\n    e1 = e1fn(this.es);\n    e2 = e2fn(this.es);\n    e3 = e3fn(this.es);\n    if (Math.abs(this.sin_p12 - 1) <= EPSLN) {\n      //North Pole case\n      Mlp = this.a * mlfn(e0, e1, e2, e3, HALF_PI);\n      Ml = this.a * mlfn(e0, e1, e2, e3, lat);\n      p.x = this.x0 + (Mlp - Ml) * Math.sin(dlon);\n      p.y = this.y0 - (Mlp - Ml) * Math.cos(dlon);\n      return p;\n    }\n    else if (Math.abs(this.sin_p12 + 1) <= EPSLN) {\n      //South Pole case\n      Mlp = this.a * mlfn(e0, e1, e2, e3, HALF_PI);\n      Ml = this.a * mlfn(e0, e1, e2, e3, lat);\n      p.x = this.x0 + (Mlp + Ml) * Math.sin(dlon);\n      p.y = this.y0 + (Mlp + Ml) * Math.cos(dlon);\n      return p;\n    }\n    else {\n      //Default case\n      tanphi = sinphi / cosphi;\n      Nl1 = gN(this.a, this.e, this.sin_p12);\n      Nl = gN(this.a, this.e, sinphi);\n      psi = Math.atan((1 - this.es) * tanphi + this.es * Nl1 * this.sin_p12 / (Nl * cosphi));\n      Az = Math.atan2(Math.sin(dlon), this.cos_p12 * Math.tan(psi) - this.sin_p12 * Math.cos(dlon));\n      if (Az === 0) {\n        s = Math.asin(this.cos_p12 * Math.sin(psi) - this.sin_p12 * Math.cos(psi));\n      }\n      else if (Math.abs(Math.abs(Az) - Math.PI) <= EPSLN) {\n        s = -Math.asin(this.cos_p12 * Math.sin(psi) - this.sin_p12 * Math.cos(psi));\n      }\n      else {\n        s = Math.asin(Math.sin(dlon) * Math.cos(psi) / Math.sin(Az));\n      }\n      G = this.e * this.sin_p12 / Math.sqrt(1 - this.es);\n      H = this.e * this.cos_p12 * Math.cos(Az) / Math.sqrt(1 - this.es);\n      GH = G * H;\n      Hs = H * H;\n      s2 = s * s;\n      s3 = s2 * s;\n      s4 = s3 * s;\n      s5 = s4 * s;\n      c = Nl1 * s * (1 - s2 * Hs * (1 - Hs) / 6 + s3 / 8 * GH * (1 - 2 * Hs) + s4 / 120 * (Hs * (4 - 7 * Hs) - 3 * G * G * (1 - 7 * Hs)) - s5 / 48 * GH);\n      p.x = this.x0 + c * Math.sin(Az);\n      p.y = this.y0 + c * Math.cos(Az);\n      return p;\n    }\n  }\n\n\n}\n\nfunction inverse$6(p) {\n  p.x -= this.x0;\n  p.y -= this.y0;\n  var rh, z, sinz, cosz, lon, lat, con, e0, e1, e2, e3, Mlp, M, N1, psi, Az, cosAz, tmp, A, B, D, Ee, F, sinpsi;\n  if (this.sphere) {\n    rh = Math.sqrt(p.x * p.x + p.y * p.y);\n    if (rh > (2 * HALF_PI * this.a)) {\n      return;\n    }\n    z = rh / this.a;\n\n    sinz = Math.sin(z);\n    cosz = Math.cos(z);\n\n    lon = this.long0;\n    if (Math.abs(rh) <= EPSLN) {\n      lat = this.lat0;\n    }\n    else {\n      lat = asinz(cosz * this.sin_p12 + (p.y * sinz * this.cos_p12) / rh);\n      con = Math.abs(this.lat0) - HALF_PI;\n      if (Math.abs(con) <= EPSLN) {\n        if (this.lat0 >= 0) {\n          lon = adjust_lon(this.long0 + Math.atan2(p.x, - p.y));\n        }\n        else {\n          lon = adjust_lon(this.long0 - Math.atan2(-p.x, p.y));\n        }\n      }\n      else {\n        /*con = cosz - this.sin_p12 * Math.sin(lat);\n        if ((Math.abs(con) < EPSLN) && (Math.abs(p.x) < EPSLN)) {\n          //no-op, just keep the lon value as is\n        } else {\n          var temp = Math.atan2((p.x * sinz * this.cos_p12), (con * rh));\n          lon = adjust_lon(this.long0 + Math.atan2((p.x * sinz * this.cos_p12), (con * rh)));\n        }*/\n        lon = adjust_lon(this.long0 + Math.atan2(p.x * sinz, rh * this.cos_p12 * cosz - p.y * this.sin_p12 * sinz));\n      }\n    }\n\n    p.x = lon;\n    p.y = lat;\n    return p;\n  }\n  else {\n    e0 = e0fn(this.es);\n    e1 = e1fn(this.es);\n    e2 = e2fn(this.es);\n    e3 = e3fn(this.es);\n    if (Math.abs(this.sin_p12 - 1) <= EPSLN) {\n      //North pole case\n      Mlp = this.a * mlfn(e0, e1, e2, e3, HALF_PI);\n      rh = Math.sqrt(p.x * p.x + p.y * p.y);\n      M = Mlp - rh;\n      lat = imlfn(M / this.a, e0, e1, e2, e3);\n      lon = adjust_lon(this.long0 + Math.atan2(p.x, - 1 * p.y));\n      p.x = lon;\n      p.y = lat;\n      return p;\n    }\n    else if (Math.abs(this.sin_p12 + 1) <= EPSLN) {\n      //South pole case\n      Mlp = this.a * mlfn(e0, e1, e2, e3, HALF_PI);\n      rh = Math.sqrt(p.x * p.x + p.y * p.y);\n      M = rh - Mlp;\n\n      lat = imlfn(M / this.a, e0, e1, e2, e3);\n      lon = adjust_lon(this.long0 + Math.atan2(p.x, p.y));\n      p.x = lon;\n      p.y = lat;\n      return p;\n    }\n    else {\n      //default case\n      rh = Math.sqrt(p.x * p.x + p.y * p.y);\n      Az = Math.atan2(p.x, p.y);\n      N1 = gN(this.a, this.e, this.sin_p12);\n      cosAz = Math.cos(Az);\n      tmp = this.e * this.cos_p12 * cosAz;\n      A = -tmp * tmp / (1 - this.es);\n      B = 3 * this.es * (1 - A) * this.sin_p12 * this.cos_p12 * cosAz / (1 - this.es);\n      D = rh / N1;\n      Ee = D - A * (1 + A) * Math.pow(D, 3) / 6 - B * (1 + 3 * A) * Math.pow(D, 4) / 24;\n      F = 1 - A * Ee * Ee / 2 - D * Ee * Ee * Ee / 6;\n      psi = Math.asin(this.sin_p12 * Math.cos(Ee) + this.cos_p12 * Math.sin(Ee) * cosAz);\n      lon = adjust_lon(this.long0 + Math.asin(Math.sin(Az) * Math.sin(Ee) / Math.cos(psi)));\n      sinpsi = Math.sin(psi);\n      lat = Math.atan2((sinpsi - this.es * F * this.sin_p12) * Math.tan(psi), sinpsi * (1 - this.es));\n      p.x = lon;\n      p.y = lat;\n      return p;\n    }\n  }\n\n}\n\nvar names$6 = [\"Azimuthal_Equidistant\", \"aeqd\"];\nvar aeqd = {\n  init: init$6,\n  forward: forward$6,\n  inverse: inverse$6,\n  names: names$6\n};\n\nfunction init$5() {\n  //double temp;      /* temporary variable    */\n\n  /* Place parameters in static storage for common use\n      -------------------------------------------------*/\n  this.sin_p14 = Math.sin(this.lat0);\n  this.cos_p14 = Math.cos(this.lat0);\n}\n\n/* Orthographic forward equations--mapping lat,long to x,y\n    ---------------------------------------------------*/\nfunction forward$5(p) {\n  var sinphi, cosphi; /* sin and cos value        */\n  var dlon; /* delta longitude value      */\n  var coslon; /* cos of longitude        */\n  var ksp; /* scale factor          */\n  var g, x, y;\n  var lon = p.x;\n  var lat = p.y;\n  /* Forward equations\n      -----------------*/\n  dlon = adjust_lon(lon - this.long0);\n\n  sinphi = Math.sin(lat);\n  cosphi = Math.cos(lat);\n\n  coslon = Math.cos(dlon);\n  g = this.sin_p14 * sinphi + this.cos_p14 * cosphi * coslon;\n  ksp = 1;\n  if ((g > 0) || (Math.abs(g) <= EPSLN)) {\n    x = this.a * ksp * cosphi * Math.sin(dlon);\n    y = this.y0 + this.a * ksp * (this.cos_p14 * sinphi - this.sin_p14 * cosphi * coslon);\n  }\n  p.x = x;\n  p.y = y;\n  return p;\n}\n\nfunction inverse$5(p) {\n  var rh; /* height above ellipsoid      */\n  var z; /* angle          */\n  var sinz, cosz; /* sin of z and cos of z      */\n  var con;\n  var lon, lat;\n  /* Inverse equations\n      -----------------*/\n  p.x -= this.x0;\n  p.y -= this.y0;\n  rh = Math.sqrt(p.x * p.x + p.y * p.y);\n  z = asinz(rh / this.a);\n\n  sinz = Math.sin(z);\n  cosz = Math.cos(z);\n\n  lon = this.long0;\n  if (Math.abs(rh) <= EPSLN) {\n    lat = this.lat0;\n    p.x = lon;\n    p.y = lat;\n    return p;\n  }\n  lat = asinz(cosz * this.sin_p14 + (p.y * sinz * this.cos_p14) / rh);\n  con = Math.abs(this.lat0) - HALF_PI;\n  if (Math.abs(con) <= EPSLN) {\n    if (this.lat0 >= 0) {\n      lon = adjust_lon(this.long0 + Math.atan2(p.x, - p.y));\n    }\n    else {\n      lon = adjust_lon(this.long0 - Math.atan2(-p.x, p.y));\n    }\n    p.x = lon;\n    p.y = lat;\n    return p;\n  }\n  lon = adjust_lon(this.long0 + Math.atan2((p.x * sinz), rh * this.cos_p14 * cosz - p.y * this.sin_p14 * sinz));\n  p.x = lon;\n  p.y = lat;\n  return p;\n}\n\nvar names$5 = [\"ortho\"];\nvar ortho = {\n  init: init$5,\n  forward: forward$5,\n  inverse: inverse$5,\n  names: names$5\n};\n\n// QSC projection rewritten from the original PROJ4\n// https://github.com/OSGeo/proj.4/blob/master/src/PJ_qsc.c\n\n\n/* constants */\nvar FACE_ENUM = {\n    FRONT: 1,\n    RIGHT: 2,\n    BACK: 3,\n    LEFT: 4,\n    TOP: 5,\n    BOTTOM: 6\n};\n\nvar AREA_ENUM = {\n    AREA_0: 1,\n    AREA_1: 2,\n    AREA_2: 3,\n    AREA_3: 4\n};\n\nfunction init$4() {\n\n  this.x0 = this.x0 || 0;\n  this.y0 = this.y0 || 0;\n  this.lat0 = this.lat0 || 0;\n  this.long0 = this.long0 || 0;\n  this.lat_ts = this.lat_ts || 0;\n  this.title = this.title || \"Quadrilateralized Spherical Cube\";\n\n  /* Determine the cube face from the center of projection. */\n  if (this.lat0 >= HALF_PI - FORTPI / 2.0) {\n    this.face = FACE_ENUM.TOP;\n  } else if (this.lat0 <= -(HALF_PI - FORTPI / 2.0)) {\n    this.face = FACE_ENUM.BOTTOM;\n  } else if (Math.abs(this.long0) <= FORTPI) {\n    this.face = FACE_ENUM.FRONT;\n  } else if (Math.abs(this.long0) <= HALF_PI + FORTPI) {\n    this.face = this.long0 > 0.0 ? FACE_ENUM.RIGHT : FACE_ENUM.LEFT;\n  } else {\n    this.face = FACE_ENUM.BACK;\n  }\n\n  /* Fill in useful values for the ellipsoid <-> sphere shift\n   * described in [LK12]. */\n  if (this.es !== 0) {\n    this.one_minus_f = 1 - (this.a - this.b) / this.a;\n    this.one_minus_f_squared = this.one_minus_f * this.one_minus_f;\n  }\n}\n\n// QSC forward equations--mapping lat,long to x,y\n// -----------------------------------------------------------------\nfunction forward$4(p) {\n  var xy = {x: 0, y: 0};\n  var lat, lon;\n  var theta, phi;\n  var t, mu;\n  /* nu; */\n  var area = {value: 0};\n\n  // move lon according to projection's lon\n  p.x -= this.long0;\n\n  /* Convert the geodetic latitude to a geocentric latitude.\n   * This corresponds to the shift from the ellipsoid to the sphere\n   * described in [LK12]. */\n  if (this.es !== 0) {//if (P->es != 0) {\n    lat = Math.atan(this.one_minus_f_squared * Math.tan(p.y));\n  } else {\n    lat = p.y;\n  }\n\n  /* Convert the input lat, lon into theta, phi as used by QSC.\n   * This depends on the cube face and the area on it.\n   * For the top and bottom face, we can compute theta and phi\n   * directly from phi, lam. For the other faces, we must use\n   * unit sphere cartesian coordinates as an intermediate step. */\n  lon = p.x; //lon = lp.lam;\n  if (this.face === FACE_ENUM.TOP) {\n    phi = HALF_PI - lat;\n    if (lon >= FORTPI && lon <= HALF_PI + FORTPI) {\n      area.value = AREA_ENUM.AREA_0;\n      theta = lon - HALF_PI;\n    } else if (lon > HALF_PI + FORTPI || lon <= -(HALF_PI + FORTPI)) {\n      area.value = AREA_ENUM.AREA_1;\n      theta = (lon > 0.0 ? lon - SPI : lon + SPI);\n    } else if (lon > -(HALF_PI + FORTPI) && lon <= -FORTPI) {\n      area.value = AREA_ENUM.AREA_2;\n      theta = lon + HALF_PI;\n    } else {\n      area.value = AREA_ENUM.AREA_3;\n      theta = lon;\n    }\n  } else if (this.face === FACE_ENUM.BOTTOM) {\n    phi = HALF_PI + lat;\n    if (lon >= FORTPI && lon <= HALF_PI + FORTPI) {\n      area.value = AREA_ENUM.AREA_0;\n      theta = -lon + HALF_PI;\n    } else if (lon < FORTPI && lon >= -FORTPI) {\n      area.value = AREA_ENUM.AREA_1;\n      theta = -lon;\n    } else if (lon < -FORTPI && lon >= -(HALF_PI + FORTPI)) {\n      area.value = AREA_ENUM.AREA_2;\n      theta = -lon - HALF_PI;\n    } else {\n      area.value = AREA_ENUM.AREA_3;\n      theta = (lon > 0.0 ? -lon + SPI : -lon - SPI);\n    }\n  } else {\n    var q, r, s;\n    var sinlat, coslat;\n    var sinlon, coslon;\n\n    if (this.face === FACE_ENUM.RIGHT) {\n      lon = qsc_shift_lon_origin(lon, +HALF_PI);\n    } else if (this.face === FACE_ENUM.BACK) {\n      lon = qsc_shift_lon_origin(lon, +SPI);\n    } else if (this.face === FACE_ENUM.LEFT) {\n      lon = qsc_shift_lon_origin(lon, -HALF_PI);\n    }\n    sinlat = Math.sin(lat);\n    coslat = Math.cos(lat);\n    sinlon = Math.sin(lon);\n    coslon = Math.cos(lon);\n    q = coslat * coslon;\n    r = coslat * sinlon;\n    s = sinlat;\n\n    if (this.face === FACE_ENUM.FRONT) {\n      phi = Math.acos(q);\n      theta = qsc_fwd_equat_face_theta(phi, s, r, area);\n    } else if (this.face === FACE_ENUM.RIGHT) {\n      phi = Math.acos(r);\n      theta = qsc_fwd_equat_face_theta(phi, s, -q, area);\n    } else if (this.face === FACE_ENUM.BACK) {\n      phi = Math.acos(-q);\n      theta = qsc_fwd_equat_face_theta(phi, s, -r, area);\n    } else if (this.face === FACE_ENUM.LEFT) {\n      phi = Math.acos(-r);\n      theta = qsc_fwd_equat_face_theta(phi, s, q, area);\n    } else {\n      /* Impossible */\n      phi = theta = 0;\n      area.value = AREA_ENUM.AREA_0;\n    }\n  }\n\n  /* Compute mu and nu for the area of definition.\n   * For mu, see Eq. (3-21) in [OL76], but note the typos:\n   * compare with Eq. (3-14). For nu, see Eq. (3-38). */\n  mu = Math.atan((12 / SPI) * (theta + Math.acos(Math.sin(theta) * Math.cos(FORTPI)) - HALF_PI));\n  t = Math.sqrt((1 - Math.cos(phi)) / (Math.cos(mu) * Math.cos(mu)) / (1 - Math.cos(Math.atan(1 / Math.cos(theta)))));\n\n  /* Apply the result to the real area. */\n  if (area.value === AREA_ENUM.AREA_1) {\n    mu += HALF_PI;\n  } else if (area.value === AREA_ENUM.AREA_2) {\n    mu += SPI;\n  } else if (area.value === AREA_ENUM.AREA_3) {\n    mu += 1.5 * SPI;\n  }\n\n  /* Now compute x, y from mu and nu */\n  xy.x = t * Math.cos(mu);\n  xy.y = t * Math.sin(mu);\n  xy.x = xy.x * this.a + this.x0;\n  xy.y = xy.y * this.a + this.y0;\n\n  p.x = xy.x;\n  p.y = xy.y;\n  return p;\n}\n\n// QSC inverse equations--mapping x,y to lat/long\n// -----------------------------------------------------------------\nfunction inverse$4(p) {\n  var lp = {lam: 0, phi: 0};\n  var mu, nu, cosmu, tannu;\n  var tantheta, theta, cosphi, phi;\n  var t;\n  var area = {value: 0};\n\n  /* de-offset */\n  p.x = (p.x - this.x0) / this.a;\n  p.y = (p.y - this.y0) / this.a;\n\n  /* Convert the input x, y to the mu and nu angles as used by QSC.\n   * This depends on the area of the cube face. */\n  nu = Math.atan(Math.sqrt(p.x * p.x + p.y * p.y));\n  mu = Math.atan2(p.y, p.x);\n  if (p.x >= 0.0 && p.x >= Math.abs(p.y)) {\n    area.value = AREA_ENUM.AREA_0;\n  } else if (p.y >= 0.0 && p.y >= Math.abs(p.x)) {\n    area.value = AREA_ENUM.AREA_1;\n    mu -= HALF_PI;\n  } else if (p.x < 0.0 && -p.x >= Math.abs(p.y)) {\n    area.value = AREA_ENUM.AREA_2;\n    mu = (mu < 0.0 ? mu + SPI : mu - SPI);\n  } else {\n    area.value = AREA_ENUM.AREA_3;\n    mu += HALF_PI;\n  }\n\n  /* Compute phi and theta for the area of definition.\n   * The inverse projection is not described in the original paper, but some\n   * good hints can be found here (as of 2011-12-14):\n   * http://fits.gsfc.nasa.gov/fitsbits/saf.93/saf.9302\n   * (search for \"Message-Id: <9302181759.AA25477 at fits.cv.nrao.edu>\") */\n  t = (SPI / 12) * Math.tan(mu);\n  tantheta = Math.sin(t) / (Math.cos(t) - (1 / Math.sqrt(2)));\n  theta = Math.atan(tantheta);\n  cosmu = Math.cos(mu);\n  tannu = Math.tan(nu);\n  cosphi = 1 - cosmu * cosmu * tannu * tannu * (1 - Math.cos(Math.atan(1 / Math.cos(theta))));\n  if (cosphi < -1) {\n    cosphi = -1;\n  } else if (cosphi > +1) {\n    cosphi = +1;\n  }\n\n  /* Apply the result to the real area on the cube face.\n   * For the top and bottom face, we can compute phi and lam directly.\n   * For the other faces, we must use unit sphere cartesian coordinates\n   * as an intermediate step. */\n  if (this.face === FACE_ENUM.TOP) {\n    phi = Math.acos(cosphi);\n    lp.phi = HALF_PI - phi;\n    if (area.value === AREA_ENUM.AREA_0) {\n      lp.lam = theta + HALF_PI;\n    } else if (area.value === AREA_ENUM.AREA_1) {\n      lp.lam = (theta < 0.0 ? theta + SPI : theta - SPI);\n    } else if (area.value === AREA_ENUM.AREA_2) {\n      lp.lam = theta - HALF_PI;\n    } else /* area.value == AREA_ENUM.AREA_3 */ {\n      lp.lam = theta;\n    }\n  } else if (this.face === FACE_ENUM.BOTTOM) {\n    phi = Math.acos(cosphi);\n    lp.phi = phi - HALF_PI;\n    if (area.value === AREA_ENUM.AREA_0) {\n      lp.lam = -theta + HALF_PI;\n    } else if (area.value === AREA_ENUM.AREA_1) {\n      lp.lam = -theta;\n    } else if (area.value === AREA_ENUM.AREA_2) {\n      lp.lam = -theta - HALF_PI;\n    } else /* area.value == AREA_ENUM.AREA_3 */ {\n      lp.lam = (theta < 0.0 ? -theta - SPI : -theta + SPI);\n    }\n  } else {\n    /* Compute phi and lam via cartesian unit sphere coordinates. */\n    var q, r, s;\n    q = cosphi;\n    t = q * q;\n    if (t >= 1) {\n      s = 0;\n    } else {\n      s = Math.sqrt(1 - t) * Math.sin(theta);\n    }\n    t += s * s;\n    if (t >= 1) {\n      r = 0;\n    } else {\n      r = Math.sqrt(1 - t);\n    }\n    /* Rotate q,r,s into the correct area. */\n    if (area.value === AREA_ENUM.AREA_1) {\n      t = r;\n      r = -s;\n      s = t;\n    } else if (area.value === AREA_ENUM.AREA_2) {\n      r = -r;\n      s = -s;\n    } else if (area.value === AREA_ENUM.AREA_3) {\n      t = r;\n      r = s;\n      s = -t;\n    }\n    /* Rotate q,r,s into the correct cube face. */\n    if (this.face === FACE_ENUM.RIGHT) {\n      t = q;\n      q = -r;\n      r = t;\n    } else if (this.face === FACE_ENUM.BACK) {\n      q = -q;\n      r = -r;\n    } else if (this.face === FACE_ENUM.LEFT) {\n      t = q;\n      q = r;\n      r = -t;\n    }\n    /* Now compute phi and lam from the unit sphere coordinates. */\n    lp.phi = Math.acos(-s) - HALF_PI;\n    lp.lam = Math.atan2(r, q);\n    if (this.face === FACE_ENUM.RIGHT) {\n      lp.lam = qsc_shift_lon_origin(lp.lam, -HALF_PI);\n    } else if (this.face === FACE_ENUM.BACK) {\n      lp.lam = qsc_shift_lon_origin(lp.lam, -SPI);\n    } else if (this.face === FACE_ENUM.LEFT) {\n      lp.lam = qsc_shift_lon_origin(lp.lam, +HALF_PI);\n    }\n  }\n\n  /* Apply the shift from the sphere to the ellipsoid as described\n   * in [LK12]. */\n  if (this.es !== 0) {\n    var invert_sign;\n    var tanphi, xa;\n    invert_sign = (lp.phi < 0 ? 1 : 0);\n    tanphi = Math.tan(lp.phi);\n    xa = this.b / Math.sqrt(tanphi * tanphi + this.one_minus_f_squared);\n    lp.phi = Math.atan(Math.sqrt(this.a * this.a - xa * xa) / (this.one_minus_f * xa));\n    if (invert_sign) {\n      lp.phi = -lp.phi;\n    }\n  }\n\n  lp.lam += this.long0;\n  p.x = lp.lam;\n  p.y = lp.phi;\n  return p;\n}\n\n/* Helper function for forward projection: compute the theta angle\n * and determine the area number. */\nfunction qsc_fwd_equat_face_theta(phi, y, x, area) {\n  var theta;\n  if (phi < EPSLN) {\n    area.value = AREA_ENUM.AREA_0;\n    theta = 0.0;\n  } else {\n    theta = Math.atan2(y, x);\n    if (Math.abs(theta) <= FORTPI) {\n      area.value = AREA_ENUM.AREA_0;\n    } else if (theta > FORTPI && theta <= HALF_PI + FORTPI) {\n      area.value = AREA_ENUM.AREA_1;\n      theta -= HALF_PI;\n    } else if (theta > HALF_PI + FORTPI || theta <= -(HALF_PI + FORTPI)) {\n      area.value = AREA_ENUM.AREA_2;\n      theta = (theta >= 0.0 ? theta - SPI : theta + SPI);\n    } else {\n      area.value = AREA_ENUM.AREA_3;\n      theta += HALF_PI;\n    }\n  }\n  return theta;\n}\n\n/* Helper function: shift the longitude. */\nfunction qsc_shift_lon_origin(lon, offset) {\n  var slon = lon + offset;\n  if (slon < -SPI) {\n    slon += TWO_PI;\n  } else if (slon > +SPI) {\n    slon -= TWO_PI;\n  }\n  return slon;\n}\n\nvar names$4 = [\"Quadrilateralized Spherical Cube\", \"Quadrilateralized_Spherical_Cube\", \"qsc\"];\nvar qsc = {\n  init: init$4,\n  forward: forward$4,\n  inverse: inverse$4,\n  names: names$4\n};\n\n// Robinson projection\n// Based on https://github.com/OSGeo/proj.4/blob/master/src/PJ_robin.c\n// Polynomial coeficients from http://article.gmane.org/gmane.comp.gis.proj-4.devel/6039\n\n\nvar COEFS_X = [\n    [1.0000, 2.2199e-17, -7.15515e-05, 3.1103e-06],\n    [0.9986, -0.000482243, -2.4897e-05, -1.3309e-06],\n    [0.9954, -0.00083103, -4.48605e-05, -9.86701e-07],\n    [0.9900, -0.00135364, -5.9661e-05, 3.6777e-06],\n    [0.9822, -0.00167442, -4.49547e-06, -5.72411e-06],\n    [0.9730, -0.00214868, -9.03571e-05, 1.8736e-08],\n    [0.9600, -0.00305085, -9.00761e-05, 1.64917e-06],\n    [0.9427, -0.00382792, -6.53386e-05, -2.6154e-06],\n    [0.9216, -0.00467746, -0.00010457, 4.81243e-06],\n    [0.8962, -0.00536223, -3.23831e-05, -5.43432e-06],\n    [0.8679, -0.00609363, -0.000113898, 3.32484e-06],\n    [0.8350, -0.00698325, -6.40253e-05, 9.34959e-07],\n    [0.7986, -0.00755338, -5.00009e-05, 9.35324e-07],\n    [0.7597, -0.00798324, -3.5971e-05, -2.27626e-06],\n    [0.7186, -0.00851367, -7.01149e-05, -8.6303e-06],\n    [0.6732, -0.00986209, -0.000199569, 1.91974e-05],\n    [0.6213, -0.010418, 8.83923e-05, 6.24051e-06],\n    [0.5722, -0.00906601, 0.000182, 6.24051e-06],\n    [0.5322, -0.00677797, 0.000275608, 6.24051e-06]\n];\n\nvar COEFS_Y = [\n    [-5.20417e-18, 0.0124, 1.21431e-18, -8.45284e-11],\n    [0.0620, 0.0124, -1.26793e-09, 4.22642e-10],\n    [0.1240, 0.0124, 5.07171e-09, -1.60604e-09],\n    [0.1860, 0.0123999, -1.90189e-08, 6.00152e-09],\n    [0.2480, 0.0124002, 7.10039e-08, -2.24e-08],\n    [0.3100, 0.0123992, -2.64997e-07, 8.35986e-08],\n    [0.3720, 0.0124029, 9.88983e-07, -3.11994e-07],\n    [0.4340, 0.0123893, -3.69093e-06, -4.35621e-07],\n    [0.4958, 0.0123198, -1.02252e-05, -3.45523e-07],\n    [0.5571, 0.0121916, -1.54081e-05, -5.82288e-07],\n    [0.6176, 0.0119938, -2.41424e-05, -5.25327e-07],\n    [0.6769, 0.011713, -3.20223e-05, -5.16405e-07],\n    [0.7346, 0.0113541, -3.97684e-05, -6.09052e-07],\n    [0.7903, 0.0109107, -4.89042e-05, -1.04739e-06],\n    [0.8435, 0.0103431, -6.4615e-05, -1.40374e-09],\n    [0.8936, 0.00969686, -6.4636e-05, -8.547e-06],\n    [0.9394, 0.00840947, -0.000192841, -4.2106e-06],\n    [0.9761, 0.00616527, -0.000256, -4.2106e-06],\n    [1.0000, 0.00328947, -0.000319159, -4.2106e-06]\n];\n\nvar FXC = 0.8487;\nvar FYC = 1.3523;\nvar C1 = R2D/5; // rad to 5-degree interval\nvar RC1 = 1/C1;\nvar NODES = 18;\n\nvar poly3_val = function(coefs, x) {\n    return coefs[0] + x * (coefs[1] + x * (coefs[2] + x * coefs[3]));\n};\n\nvar poly3_der = function(coefs, x) {\n    return coefs[1] + x * (2 * coefs[2] + x * 3 * coefs[3]);\n};\n\nfunction newton_rapshon(f_df, start, max_err, iters) {\n    var x = start;\n    for (; iters; --iters) {\n        var upd = f_df(x);\n        x -= upd;\n        if (Math.abs(upd) < max_err) {\n            break;\n        }\n    }\n    return x;\n}\n\nfunction init$3() {\n    this.x0 = this.x0 || 0;\n    this.y0 = this.y0 || 0;\n    this.long0 = this.long0 || 0;\n    this.es = 0;\n    this.title = this.title || \"Robinson\";\n}\n\nfunction forward$3(ll) {\n    var lon = adjust_lon(ll.x - this.long0);\n\n    var dphi = Math.abs(ll.y);\n    var i = Math.floor(dphi * C1);\n    if (i < 0) {\n        i = 0;\n    } else if (i >= NODES) {\n        i = NODES - 1;\n    }\n    dphi = R2D * (dphi - RC1 * i);\n    var xy = {\n        x: poly3_val(COEFS_X[i], dphi) * lon,\n        y: poly3_val(COEFS_Y[i], dphi)\n    };\n    if (ll.y < 0) {\n        xy.y = -xy.y;\n    }\n\n    xy.x = xy.x * this.a * FXC + this.x0;\n    xy.y = xy.y * this.a * FYC + this.y0;\n    return xy;\n}\n\nfunction inverse$3(xy) {\n    var ll = {\n        x: (xy.x - this.x0) / (this.a * FXC),\n        y: Math.abs(xy.y - this.y0) / (this.a * FYC)\n    };\n\n    if (ll.y >= 1) { // pathologic case\n        ll.x /= COEFS_X[NODES][0];\n        ll.y = xy.y < 0 ? -HALF_PI : HALF_PI;\n    } else {\n        // find table interval\n        var i = Math.floor(ll.y * NODES);\n        if (i < 0) {\n            i = 0;\n        } else if (i >= NODES) {\n            i = NODES - 1;\n        }\n        for (;;) {\n            if (COEFS_Y[i][0] > ll.y) {\n                --i;\n            } else if (COEFS_Y[i+1][0] <= ll.y) {\n                ++i;\n            } else {\n                break;\n            }\n        }\n        // linear interpolation in 5 degree interval\n        var coefs = COEFS_Y[i];\n        var t = 5 * (ll.y - coefs[0]) / (COEFS_Y[i+1][0] - coefs[0]);\n        // find t so that poly3_val(coefs, t) = ll.y\n        t = newton_rapshon(function(x) {\n            return (poly3_val(coefs, x) - ll.y) / poly3_der(coefs, x);\n        }, t, EPSLN, 100);\n\n        ll.x /= poly3_val(COEFS_X[i], t);\n        ll.y = (5 * i + t) * D2R$1;\n        if (xy.y < 0) {\n            ll.y = -ll.y;\n        }\n    }\n\n    ll.x = adjust_lon(ll.x + this.long0);\n    return ll;\n}\n\nvar names$3 = [\"Robinson\", \"robin\"];\nvar robin = {\n  init: init$3,\n  forward: forward$3,\n  inverse: inverse$3,\n  names: names$3\n};\n\nfunction init$2() {\n    this.name = 'geocent';\n\n}\n\nfunction forward$2(p) {\n    var point = geodeticToGeocentric(p, this.es, this.a);\n    return point;\n}\n\nfunction inverse$2(p) {\n    var point = geocentricToGeodetic(p, this.es, this.a, this.b);\n    return point;\n}\n\nvar names$2 = [\"Geocentric\", 'geocentric', \"geocent\", \"Geocent\"];\nvar geocent = {\n    init: init$2,\n    forward: forward$2,\n    inverse: inverse$2,\n    names: names$2\n};\n\nvar mode = {\n  N_POLE: 0,\n  S_POLE: 1,\n  EQUIT: 2,\n  OBLIQ: 3\n};\n\nvar params = {\n  h:     { def: 100000, num: true },           // default is Karman line, no default in PROJ.7\n  azi:   { def: 0, num: true, degrees: true }, // default is North\n  tilt:  { def: 0, num: true, degrees: true }, // default is Nadir\n  long0: { def: 0, num: true },                // default is Greenwich, conversion to rad is automatic\n  lat0:  { def: 0, num: true }                 // default is Equator, conversion to rad is automatic\n};\n\nfunction init$1() {\n  Object.keys(params).forEach(function (p) {\n    if (typeof this[p] === \"undefined\") {\n      this[p] = params[p].def;\n    } else if (params[p].num && isNaN(this[p])) {\n      throw new Error(\"Invalid parameter value, must be numeric \" + p + \" = \" + this[p]);\n    } else if (params[p].num) {\n      this[p] = parseFloat(this[p]);\n    }\n    if (params[p].degrees) {\n      this[p] = this[p] * D2R$1;\n    }\n  }.bind(this));\n\n  if (Math.abs((Math.abs(this.lat0) - HALF_PI)) < EPSLN) {\n    this.mode = this.lat0 < 0 ? mode.S_POLE : mode.N_POLE;\n  } else if (Math.abs(this.lat0) < EPSLN) {\n    this.mode = mode.EQUIT;\n  } else {\n    this.mode = mode.OBLIQ;\n    this.sinph0 = Math.sin(this.lat0);\n    this.cosph0 = Math.cos(this.lat0);\n  }\n\n  this.pn1 = this.h / this.a;  // Normalize relative to the Earth's radius\n\n  if (this.pn1 <= 0 || this.pn1 > 1e10) {\n    throw new Error(\"Invalid height\");\n  }\n  \n  this.p = 1 + this.pn1;\n  this.rp = 1 / this.p;\n  this.h1 = 1 / this.pn1;\n  this.pfact = (this.p + 1) * this.h1;\n  this.es = 0;\n\n  var omega = this.tilt;\n  var gamma = this.azi;\n  this.cg = Math.cos(gamma);\n  this.sg = Math.sin(gamma);\n  this.cw = Math.cos(omega);\n  this.sw = Math.sin(omega);\n}\n\nfunction forward$1(p) {\n  p.x -= this.long0;\n  var sinphi = Math.sin(p.y);\n  var cosphi = Math.cos(p.y);\n  var coslam = Math.cos(p.x);\n  var x, y;\n  switch (this.mode) {\n    case mode.OBLIQ:\n      y = this.sinph0 * sinphi + this.cosph0 * cosphi * coslam;\n      break;\n    case mode.EQUIT:\n      y = cosphi * coslam;\n      break;\n    case mode.S_POLE:\n      y = -sinphi;\n      break;\n    case mode.N_POLE:\n      y = sinphi;\n      break;\n  }\n  y = this.pn1 / (this.p - y);\n  x = y * cosphi * Math.sin(p.x);\n\n  switch (this.mode) {\n    case mode.OBLIQ:\n      y *= this.cosph0 * sinphi - this.sinph0 * cosphi * coslam;\n      break;\n    case mode.EQUIT:\n      y *= sinphi;\n      break;\n    case mode.N_POLE:\n      y *= -(cosphi * coslam);\n      break;\n    case mode.S_POLE:\n      y *= cosphi * coslam;\n      break;\n  }\n\n  // Tilt \n  var yt, ba;\n  yt = y * this.cg + x * this.sg;\n  ba = 1 / (yt * this.sw * this.h1 + this.cw);\n  x = (x * this.cg - y * this.sg) * this.cw * ba;\n  y = yt * ba;\n\n  p.x = x * this.a;\n  p.y = y * this.a;\n  return p;\n}\n\nfunction inverse$1(p) {\n  p.x /= this.a;\n  p.y /= this.a;\n  var r = { x: p.x, y: p.y };\n\n  // Un-Tilt\n  var bm, bq, yt;\n  yt = 1 / (this.pn1 - p.y * this.sw);\n  bm = this.pn1 * p.x * yt;\n  bq = this.pn1 * p.y * this.cw * yt;\n  p.x = bm * this.cg + bq * this.sg;\n  p.y = bq * this.cg - bm * this.sg;\n\n  var rh = hypot(p.x, p.y);\n  if (Math.abs(rh) < EPSLN) {\n    r.x = 0;\n    r.y = p.y;\n  } else {\n    var cosz, sinz;\n    sinz = 1 - rh * rh * this.pfact;\n    sinz = (this.p - Math.sqrt(sinz)) / (this.pn1 / rh + rh / this.pn1);\n    cosz = Math.sqrt(1 - sinz * sinz);\n    switch (this.mode) {\n      case mode.OBLIQ:\n        r.y = Math.asin(cosz * this.sinph0 + p.y * sinz * this.cosph0 / rh);\n        p.y = (cosz - this.sinph0 * Math.sin(r.y)) * rh;\n        p.x *= sinz * this.cosph0;\n        break;\n      case mode.EQUIT:\n        r.y = Math.asin(p.y * sinz / rh);\n        p.y = cosz * rh;\n        p.x *= sinz;\n        break;\n      case mode.N_POLE:\n        r.y = Math.asin(cosz);\n        p.y = -p.y;\n        break;\n      case mode.S_POLE:\n        r.y = -Math.asin(cosz);\n        break;\n    }\n    r.x = Math.atan2(p.x, p.y);\n  }\n\n  p.x = r.x + this.long0;\n  p.y = r.y;\n  return p;\n}\n\nvar names$1 = [\"Tilted_Perspective\", \"tpers\"];\nvar tpers = {\n  init: init$1,\n  forward: forward$1,\n  inverse: inverse$1,\n  names: names$1\n};\n\nfunction init() {\n    this.flip_axis = (this.sweep === 'x' ? 1 : 0);\n    this.h = Number(this.h);\n    this.radius_g_1 = this.h / this.a;\n\n    if (this.radius_g_1 <= 0 || this.radius_g_1 > 1e10) {\n        throw new Error();\n    }\n\n    this.radius_g = 1.0 + this.radius_g_1;\n    this.C = this.radius_g * this.radius_g - 1.0;\n\n    if (this.es !== 0.0) {\n        var one_es = 1.0 - this.es;\n        var rone_es = 1 / one_es;\n\n        this.radius_p = Math.sqrt(one_es);\n        this.radius_p2 = one_es;\n        this.radius_p_inv2 = rone_es;\n\n        this.shape = 'ellipse'; // Use as a condition in the forward and inverse functions.\n    } else {\n        this.radius_p = 1.0;\n        this.radius_p2 = 1.0;\n        this.radius_p_inv2 = 1.0;\n\n        this.shape = 'sphere';  // Use as a condition in the forward and inverse functions.\n    }\n\n    if (!this.title) {\n        this.title = \"Geostationary Satellite View\";\n    }\n}\n\nfunction forward(p) {\n    var lon = p.x;\n    var lat = p.y;\n    var tmp, v_x, v_y, v_z;\n    lon = lon - this.long0;\n\n    if (this.shape === 'ellipse') {\n        lat = Math.atan(this.radius_p2 * Math.tan(lat));\n        var r = this.radius_p / hypot(this.radius_p * Math.cos(lat), Math.sin(lat));\n\n        v_x = r * Math.cos(lon) * Math.cos(lat);\n        v_y = r * Math.sin(lon) * Math.cos(lat);\n        v_z = r * Math.sin(lat);\n\n        if (((this.radius_g - v_x) * v_x - v_y * v_y - v_z * v_z * this.radius_p_inv2) < 0.0) {\n            p.x = Number.NaN;\n            p.y = Number.NaN;\n            return p;\n        }\n\n        tmp = this.radius_g - v_x;\n        if (this.flip_axis) {\n            p.x = this.radius_g_1 * Math.atan(v_y / hypot(v_z, tmp));\n            p.y = this.radius_g_1 * Math.atan(v_z / tmp);\n        } else {\n            p.x = this.radius_g_1 * Math.atan(v_y / tmp);\n            p.y = this.radius_g_1 * Math.atan(v_z / hypot(v_y, tmp));\n        }\n    } else if (this.shape === 'sphere') {\n        tmp = Math.cos(lat);\n        v_x = Math.cos(lon) * tmp;\n        v_y = Math.sin(lon) * tmp;\n        v_z = Math.sin(lat);\n        tmp = this.radius_g - v_x;\n\n        if (this.flip_axis) {\n            p.x = this.radius_g_1 * Math.atan(v_y / hypot(v_z, tmp));\n            p.y = this.radius_g_1 * Math.atan(v_z / tmp);\n        } else {\n            p.x = this.radius_g_1 * Math.atan(v_y / tmp);\n            p.y = this.radius_g_1 * Math.atan(v_z / hypot(v_y, tmp));\n        }\n    }\n    p.x = p.x * this.a;\n    p.y = p.y * this.a;\n    return p;\n}\n\nfunction inverse(p) {\n    var v_x = -1.0;\n    var v_y = 0.0;\n    var v_z = 0.0;\n    var a, b, det, k;\n\n    p.x = p.x / this.a;\n    p.y = p.y / this.a;\n\n    if (this.shape === 'ellipse') {\n        if (this.flip_axis) {\n            v_z = Math.tan(p.y / this.radius_g_1);\n            v_y = Math.tan(p.x / this.radius_g_1) * hypot(1.0, v_z);\n        } else {\n            v_y = Math.tan(p.x / this.radius_g_1);\n            v_z = Math.tan(p.y / this.radius_g_1) * hypot(1.0, v_y);\n        }\n\n        var v_zp = v_z / this.radius_p;\n        a = v_y * v_y + v_zp * v_zp + v_x * v_x;\n        b = 2 * this.radius_g * v_x;\n        det = (b * b) - 4 * a * this.C;\n\n        if (det < 0.0) {\n            p.x = Number.NaN;\n            p.y = Number.NaN;\n            return p;\n        }\n\n        k = (-b - Math.sqrt(det)) / (2.0 * a);\n        v_x = this.radius_g + k * v_x;\n        v_y *= k;\n        v_z *= k;\n\n        p.x = Math.atan2(v_y, v_x);\n        p.y = Math.atan(v_z * Math.cos(p.x) / v_x);\n        p.y = Math.atan(this.radius_p_inv2 * Math.tan(p.y));\n    } else if (this.shape === 'sphere') {\n        if (this.flip_axis) {\n            v_z = Math.tan(p.y / this.radius_g_1);\n            v_y = Math.tan(p.x / this.radius_g_1) * Math.sqrt(1.0 + v_z * v_z);\n        } else {\n            v_y = Math.tan(p.x / this.radius_g_1);\n            v_z = Math.tan(p.y / this.radius_g_1) * Math.sqrt(1.0 + v_y * v_y);\n        }\n\n        a = v_y * v_y + v_z * v_z + v_x * v_x;\n        b = 2 * this.radius_g * v_x;\n        det = (b * b) - 4 * a * this.C;\n        if (det < 0.0) {\n            p.x = Number.NaN;\n            p.y = Number.NaN;\n            return p;\n        }\n\n        k = (-b - Math.sqrt(det)) / (2.0 * a);\n        v_x = this.radius_g + k * v_x;\n        v_y *= k;\n        v_z *= k;\n\n        p.x = Math.atan2(v_y, v_x);\n        p.y = Math.atan(v_z * Math.cos(p.x) / v_x);\n    }\n    p.x = p.x + this.long0;\n    return p;\n}\n\nvar names = [\"Geostationary Satellite View\", \"Geostationary_Satellite\", \"geos\"];\nvar geos = {\n    init: init,\n    forward: forward,\n    inverse: inverse,\n    names: names,\n};\n\nfunction includedProjections(proj4){\n  proj4.Proj.projections.add(tmerc);\n  proj4.Proj.projections.add(etmerc);\n  proj4.Proj.projections.add(utm);\n  proj4.Proj.projections.add(sterea);\n  proj4.Proj.projections.add(stere);\n  proj4.Proj.projections.add(somerc);\n  proj4.Proj.projections.add(omerc);\n  proj4.Proj.projections.add(lcc);\n  proj4.Proj.projections.add(krovak);\n  proj4.Proj.projections.add(cass);\n  proj4.Proj.projections.add(laea);\n  proj4.Proj.projections.add(aea);\n  proj4.Proj.projections.add(gnom);\n  proj4.Proj.projections.add(cea);\n  proj4.Proj.projections.add(eqc);\n  proj4.Proj.projections.add(poly);\n  proj4.Proj.projections.add(nzmg);\n  proj4.Proj.projections.add(mill);\n  proj4.Proj.projections.add(sinu);\n  proj4.Proj.projections.add(moll);\n  proj4.Proj.projections.add(eqdc);\n  proj4.Proj.projections.add(vandg);\n  proj4.Proj.projections.add(aeqd);\n  proj4.Proj.projections.add(ortho);\n  proj4.Proj.projections.add(qsc);\n  proj4.Proj.projections.add(robin);\n  proj4.Proj.projections.add(geocent);\n  proj4.Proj.projections.add(tpers);\n  proj4.Proj.projections.add(geos);\n}\n\nproj4.defaultDatum = 'WGS84'; //default datum\nproj4.Proj = Projection;\nproj4.WGS84 = new proj4.Proj('WGS84');\nproj4.Point = Point;\nproj4.toPoint = common;\nproj4.defs = defs;\nproj4.nadgrid = nadgrid;\nproj4.transform = transform;\nproj4.mgrs = mgrs;\nproj4.version = '__VERSION__';\nincludedProjections(proj4);\n\nvar f,m=\"deflate-raw\",x=self.DecompressionStream;try{new x(m),f=async t=>{let n=new x(m),e=n.writable.getWriter(),i=n.readable.getReader();e.write(t),e.close();let c,o=[],s=0,a=0,l;for(;!(l=await i.read()).done;)c=l.value,o.push(c),s+=c.length;return o.length-1?(c=new Uint8Array(s),o.map(r=>{c.set(r,a),a+=r.length;}),c):o[0]};}catch{}var _=new TextDecoder,h=t=>{throw new Error(\"but-unzip~\"+t)},E=t=>_.decode(t),A=t=>{let n=t.length-20,e=Math.max(n-65516,2);for(;(n=t.lastIndexOf(80,n-1))!==-1&&!(t[n+1]===75&&t[n+2]===5&&t[n+3]===6)&&n>e;);return n};function*C(t,n=f){let e=A(t);e===-1&&h(2);let i=(r,d)=>t.subarray(e+=r,e+=d),c=new DataView(t.buffer,t.byteOffset),o=r=>c.getUint16(r+e,!0),s=r=>c.getUint32(r+e,!0),a=o(10);for(a!==o(8)&&h(3),e=s(16);a--;){let r=o(10),d=o(28),g=o(30),y=o(32),b=s(20),w=s(42),p=E(i(46,d)),D=E(i(g,y)),L=e,u;e=w,u=i(30+o(26)+o(28),b),yield {filename:p,comment:D,read:()=>r&8?n(u):r?h(1):u},e=L;}}\n\nconst regex$1 = /.+\\.(shp|dbf|json|prj|cpg)$/i;\nvar unzip = async (buffer) => {\n  const files = {};\n  const proms = [];\n  for (const entry of C(buffer)) {\n    if (!regex$1.test(entry.filename)) {\n      continue;\n    }\n    proms.push(Promise.resolve(entry.read()).then(bytes => files[entry.filename] = bytes));\n  }\n  await Promise.all(proms);\n  const out = {};\n  const decoder = new TextDecoder();\n  for (const [key, value] of Object.entries(files)) {\n    if (key.slice(-3).toLowerCase() === 'shp' || key.slice(-3).toLowerCase() === 'dbf') {\n      out[key] = new DataView(value.buffer, value.byteOffset, value.byteLength);\n    } else {\n      out[key] = decoder.decode(value);\n    }\n  }\n  return out;\n};\n\nconst URL$1 = globalThis.URL;\n\nvar combine$1 = (base, type) => {\n  if (!type) {\n    return base;\n  }\n  const url = new URL$1(base);\n  url.pathname = `${url.pathname}.${type}`;\n  return url.href;\n};\n\nasync function binaryAjax(_url, type) {\n\n  const url = combine$1(_url, type);\n  const isOptionalTxt = type === 'prj' || type === 'cpg';\n  try {\n    const resp = await fetch(url);\n    if (resp.status > 399) {\n      throw new Error(resp.statusText);\n    }\n    if (isOptionalTxt) {\n      return resp.text();\n    }\n    const parsed = await resp.arrayBuffer();\n    return new DataView(parsed)\n  } catch (e) {\n    if (isOptionalTxt || type === 'dbf') {\n      return false;\n    }\n    throw e;\n  }\n}\n\nfunction isClockWise(array) {\n  let sum = 0;\n  let i = 1;\n  const len = array.length;\n  let prev, cur;\n  const bbox = [array[0][0], array[0][1], array[0][0], array[0][1]];\n  while (i < len) {\n    prev = cur || array[0];\n    cur = array[i];\n    sum += ((cur[0] - prev[0]) * (cur[1] + prev[1]));\n    i++;\n    if (cur[0] < bbox[0]) {\n      bbox[0] = cur[0];\n    }\n    if (cur[1] < bbox[1]) {\n      bbox[1] = cur[1];\n    }\n    if (cur[0] > bbox[2]) {\n      bbox[2] = cur[0];\n    }\n    if (cur[1] > bbox[3]) {\n      bbox[3] = cur[1];\n    }\n  }\n  return {\n    ring: array,\n    clockWise: sum > 0,\n    bbox,\n    children: []\n  }\n\n}\n\nfunction contains(outer, inner) {\n  if (outer.bbox[0] > inner.bbox[0]) {\n    return false;\n  }\n  if (outer.bbox[1] > inner.bbox[1]) {\n    return false;\n  }\n  if (outer.bbox[2] < inner.bbox[2]) {\n    return false;\n  }\n  if (outer.bbox[3] < inner.bbox[3]) {\n    return false;\n  }\n  return true;\n}\n\nfunction handleRings(rings) {\n  const outers = [];\n  const inners = [];\n  for (const ring of rings) {\n    const proccessed = isClockWise(ring);\n    if (proccessed.clockWise) {\n      outers.push(proccessed);\n    } else {\n      inners.push(proccessed);\n    }\n  }\n  // this is an optimization, \n  // but it would also put in weird bad rings that would otherwise get left out\n  // if (outers.length === 1) {\n  //   const out = [outers[0].ring]\n  //   for (const inner of inners) {\n  //     out.push(inner.ring);\n\n  //   }\n  //   return [out];\n  // }\n  for (const inner of inners) {\n    for (const outer of outers) {\n      if (contains(outer, inner)) {\n        outer.children.push(inner.ring);\n        break;\n      }\n    }\n  }\n  const out = [];\n  for (const outer of outers) {\n    out.push([outer.ring].concat(outer.children));\n  }\n  return out;\n}\nParseShp.prototype.parsePoint = function (data) {\n  return {\n    type: 'Point',\n    coordinates: this.parseCoord(data, 0)\n  };\n};\nParseShp.prototype.parseZPoint = function (data) {\n  const pointXY = this.parsePoint(data);\n  pointXY.coordinates.push(data.getFloat64(16, true));\n  return pointXY;\n};\nParseShp.prototype.parsePointArray = function (data, offset, num) {\n  const out = [];\n  let done = 0;\n  while (done < num) {\n    out.push(this.parseCoord(data, offset));\n    offset += 16;\n    done++;\n  }\n  return out;\n};\nParseShp.prototype.parseZPointArray = function (data, zOffset, num, coordinates) {\n  let i = 0;\n  while (i < num) {\n    coordinates[i].push(data.getFloat64(zOffset, true));\n    i++;\n    zOffset += 8;\n  }\n  return coordinates;\n};\nParseShp.prototype.parseArrayGroup = function (data, offset, partOffset, num, tot) {\n  const out = [];\n  let done = 0;\n  let curNum; let nextNum = 0;\n  let pointNumber;\n  while (done < num) {\n    done++;\n    partOffset += 4;\n    curNum = nextNum;\n    if (done === num) {\n      nextNum = tot;\n    } else {\n      nextNum = data.getInt32(partOffset, true);\n    }\n    pointNumber = nextNum - curNum;\n    if (!pointNumber) {\n      continue;\n    }\n    out.push(this.parsePointArray(data, offset, pointNumber));\n    offset += (pointNumber << 4);\n  }\n  return out;\n};\nParseShp.prototype.parseZArrayGroup = function (data, zOffset, num, coordinates) {\n  let i = 0;\n  while (i < num) {\n    coordinates[i] = this.parseZPointArray(data, zOffset, coordinates[i].length, coordinates[i]);\n    zOffset += (coordinates[i].length << 3);\n    i++;\n  }\n  return coordinates;\n};\nParseShp.prototype.parseMultiPoint = function (data) {\n  const out = {};\n  const num = data.getInt32(32, true);\n  if (!num) {\n    return null;\n  }\n  const mins = this.parseCoord(data, 0);\n  const maxs = this.parseCoord(data, 16);\n  out.bbox = [\n    mins[0],\n    mins[1],\n    maxs[0],\n    maxs[1]\n  ];\n  const offset = 36;\n  if (num === 1) {\n    out.type = 'Point';\n    out.coordinates = this.parseCoord(data, offset);\n  } else {\n    out.type = 'MultiPoint';\n    out.coordinates = this.parsePointArray(data, offset, num);\n  }\n  return out;\n};\nParseShp.prototype.parseZMultiPoint = function (data) {\n  const geoJson = this.parseMultiPoint(data);\n  if (!geoJson) {\n    return null;\n  }\n  let num;\n  if (geoJson.type === 'Point') {\n    geoJson.coordinates.push(data.getFloat64(72, true));\n    return geoJson;\n  } else {\n    num = geoJson.coordinates.length;\n  }\n  const zOffset = 52 + (num << 4);\n  geoJson.coordinates = this.parseZPointArray(data, zOffset, num, geoJson.coordinates);\n  return geoJson;\n};\nParseShp.prototype.parsePolyline = function (data) {\n  const out = {};\n  const numParts = data.getInt32(32, true);\n  if (!numParts) {\n    return null;\n  }\n  const mins = this.parseCoord(data, 0);\n  const maxs = this.parseCoord(data, 16);\n  out.bbox = [\n    mins[0],\n    mins[1],\n    maxs[0],\n    maxs[1]\n  ];\n  const num = data.getInt32(36, true);\n  let offset, partOffset;\n  if (numParts === 1) {\n    out.type = 'LineString';\n    offset = 44;\n    out.coordinates = this.parsePointArray(data, offset, num);\n  } else {\n    out.type = 'MultiLineString';\n    offset = 40 + (numParts << 2);\n    partOffset = 40;\n    out.coordinates = this.parseArrayGroup(data, offset, partOffset, numParts, num);\n  }\n  return out;\n};\nParseShp.prototype.parseZPolyline = function (data) {\n  const geoJson = this.parsePolyline(data);\n  if (!geoJson) {\n    return null;\n  }\n  const num = geoJson.coordinates.length;\n  let zOffset;\n  if (geoJson.type === 'LineString') {\n    zOffset = 60 + (num << 4);\n    geoJson.coordinates = this.parseZPointArray(data, zOffset, num, geoJson.coordinates);\n    return geoJson;\n  } else {\n    const totalPoints = geoJson.coordinates.reduce(function (a, v) {\n      return a + v.length;\n    }, 0);\n    zOffset = 56 + (totalPoints << 4) + (num << 2);\n    geoJson.coordinates = this.parseZArrayGroup(data, zOffset, num, geoJson.coordinates);\n    return geoJson;\n  }\n};\nParseShp.prototype.polyFuncs = function (out) {\n  if (!out) {\n    return out;\n  }\n  if (out.type === 'LineString') {\n    out.type = 'Polygon';\n    out.coordinates = [out.coordinates];\n    return out;\n  } else {\n    out.coordinates = handleRings(out.coordinates);\n    if (out.coordinates.length === 1) {\n      out.type = 'Polygon';\n      out.coordinates = out.coordinates[0];\n      return out;\n    } else {\n      out.type = 'MultiPolygon';\n      return out;\n    }\n  }\n};\nParseShp.prototype.parsePolygon = function (data) {\n  return this.polyFuncs(this.parsePolyline(data));\n};\nParseShp.prototype.parseZPolygon = function (data) {\n  return this.polyFuncs(this.parseZPolyline(data));\n};\nconst shpFuncObj = {\n  1: 'parsePoint',\n  3: 'parsePolyline',\n  5: 'parsePolygon',\n  8: 'parseMultiPoint',\n  11: 'parseZPoint',\n  13: 'parseZPolyline',\n  15: 'parseZPolygon',\n  18: 'parseZMultiPoint'\n};\n\nfunction makeParseCoord(trans) {\n  if (trans) {\n    return function (data, offset) {\n      const args = [data.getFloat64(offset, true), data.getFloat64(offset + 8, true)];\n      return trans.inverse(args);\n    };\n  } else {\n    return function (data, offset) {\n      return [data.getFloat64(offset, true), data.getFloat64(offset + 8, true)];\n    };\n  }\n}\n\nfunction ParseShp(buffer, trans) {\n  if (!(this instanceof ParseShp)) {\n    return new ParseShp(buffer, trans);\n  }\n  this.buffer = buffer;\n  this.headers = this.parseHeader();\n  this.shpFuncs(trans);\n  this.rows = this.getRows();\n}\nParseShp.prototype.shpFuncs = function (tran) {\n  let num = this.headers.shpCode;\n  if (num > 20) {\n    num -= 20;\n  }\n  if (!(num in shpFuncObj)) {\n    throw new Error(`I don't know shp type \"${num}\"`);\n  }\n  this.parseFunc = this[shpFuncObj[num]];\n  this.parseCoord = makeParseCoord(tran);\n};\nParseShp.prototype.getShpCode = function () {\n  return this.parseHeader().shpCode;\n};\nParseShp.prototype.parseHeader = function () {\n  const view = this.buffer;\n  return {\n    length: view.getInt32(6 << 2) << 1,\n    version: view.getInt32(7 << 2, true),\n    shpCode: view.getInt32(8 << 2, true),\n    bbox: [\n      view.getFloat64(9 << 2, true),\n      view.getFloat64(11 << 2, true),\n      view.getFloat64(13 << 2, true),\n      view.getFloat64(15 << 2, true)\n    ]\n  };\n};\nParseShp.prototype.getRows = function () {\n  let offset = 100;\n  const len = this.buffer.byteLength - 8;\n  const out = [];\n  let current;\n  while (offset <= len) {\n    current = this.getRow(offset);\n    if (!current) {\n      break;\n    }\n    offset += 8;\n    offset += current.len;\n    if (current.type) {\n      out.push(this.parseFunc(current.data));\n    } else {\n      out.push(null);\n    }\n  }\n  return out;\n};\nParseShp.prototype.getRow = function (offset) {\n  const id = this.buffer.getInt32(offset);\n  const len = this.buffer.getInt32(offset + 4) << 1;\n  if (len === 0) {\n    return {\n      id: id,\n      len: len,\n      type: 0\n    };\n  }\n\n  if (offset + len + 8 > this.buffer.byteLength) {\n    return;\n  }\n  return {\n    id: id,\n    len: len,\n    data: new DataView(this.buffer.buffer, this.buffer.byteOffset + offset + 12, len - 4),\n    type: this.buffer.getInt32(offset + 8, true)\n  };\n};\nfunction parseShp (buffer, trans) {\n  return new ParseShp(buffer, trans).rows;\n}\n\nvar regex = /^(?:ANSI\\s)?(\\d+)$/m;\nfunction createDecoder(encoding, second) {\n  if (!encoding) {\n    return browserDecoder;\n  }\n  try {\n    new TextDecoder(encoding.trim());\n  } catch (e) {\n    var match = regex.exec(encoding);\n    if (match && !second) {\n      return createDecoder('windows-' + match[1], true);\n    } else {\n      encoding = undefined;\n      return browserDecoder;\n    }\n  }\n  return browserDecoder;\n  function browserDecoder(buffer) {\n    var decoder = new TextDecoder(encoding ? encoding : undefined);\n    var out = decoder.decode(buffer, {\n      stream: true\n    }) + decoder.decode();\n    return out.replace(/\\0/g, '').trim();\n  }\n}\n\nfunction dbfHeader(data) {\n  var out = {};\n  out.lastUpdated = new Date(data.getUint8(1) + 1900, data.getUint8(2), data.getUint8(3));\n  out.records = data.getUint32(4, true);\n  out.headerLen = data.getUint16(8, true);\n  out.recLen = data.getUint16(10, true);\n  return out;\n}\n\nfunction dbfRowHeader(data, headerLen, decoder) {\n  var out = [];\n  var offset = 32;\n  while (offset < headerLen) {\n    out.push({\n      name: decoder(new Uint8Array(data.buffer.slice(data.byteOffset + offset, data.byteOffset + offset + 11))),\n      dataType: String.fromCharCode(data.getUint8(offset + 11)),\n      len: data.getUint8(offset + 16),\n      decimal: data.getUint8(offset + 17)\n    });\n    if (data.getUint8(offset + 32) === 13) {\n      break;\n    } else {\n      offset += 32;\n    }\n  }\n  return out;\n}\n\nfunction rowFuncs(buffer, offset, len, type, decoder) {\n  const data = new Uint8Array(buffer.buffer.slice(buffer.byteOffset + offset, buffer.byteOffset + offset + len));\n\n  var textData = decoder(data);\n  switch (type) {\n    case 'N':\n    case 'F':\n    case 'O':\n      return parseFloat(textData, 10);\n    case 'D':\n      return new Date(textData.slice(0, 4), parseInt(textData.slice(4, 6), 10) - 1, textData.slice(6, 8));\n    case 'L':\n      return textData.toLowerCase() === 'y' || textData.toLowerCase() === 't';\n    default:\n      return textData;\n  }\n}\n\nfunction parseRow(buffer, offset, rowHeaders, decoder) {\n  var out = {};\n  var i = 0;\n  var len = rowHeaders.length;\n  var field;\n  var header;\n  while (i < len) {\n    header = rowHeaders[i];\n    field = rowFuncs(buffer, offset, header.len, header.dataType, decoder);\n    offset += header.len;\n    if (typeof field !== 'undefined') {\n      out[header.name] = field;\n    }\n    i++;\n  }\n  return out;\n}\n\nfunction parseDbf (buffer, encoding) {\n  var decoder = createDecoder(encoding);\n  var header = dbfHeader(buffer);\n  var rowHeaders = dbfRowHeader(buffer, header.headerLen - 1, decoder);\n\n  var offset = ((rowHeaders.length + 1) << 5) + 2;\n  var recLen = header.recLen;\n  var records = header.records;\n  var out = [];\n  while (records) {\n    out.push(parseRow(buffer, offset, rowHeaders, decoder));\n    offset += recLen;\n    records--;\n  }\n  return out;\n}\n\nconst URL = globalThis.URL;\nconst toUitn8Arr = b => {\n  if (!b) {\n    throw new Error('forgot to pass buffer');\n  }\n  if (isArrayBuffer(b)) {\n    return new Uint8Array(b);\n  }\n  if (isArrayBuffer(b.buffer)) {\n    if (b.BYTES_PER_ELEMENT === 1) {\n      return b;\n    }\n    return new Uint8Array(b.buffer, b.byteOffset, b.byteLength);\n  }\n  throw new Error('invalid buffer like object')\n};\nconst txtDecoder = new TextDecoder();\nconst toString = (possibleString) => {\n  if (!possibleString) {\n    return;\n  }\n  if (typeof possibleString === 'string') {\n    return possibleString;\n  }\n  if (isArrayBuffer(possibleString) || ArrayBuffer.isView(possibleString) || isDataView(possibleString)) {\n    return txtDecoder.decode(possibleString);\n  }\n};\nconst toDataView = b => {\n  if (!b) {\n    throw new Error('forgot to pass buffer');\n  }\n  if (isDataView(b)) {\n    return b;\n  }\n  if (isArrayBuffer(b)) {\n    return new DataView(b);\n  }\n  if (isArrayBuffer(b.buffer)) {\n    return new DataView(b.buffer, b.byteOffset, b.byteLength);\n  }\n  throw new Error('invalid buffer like object')\n};\n\nfunction isArrayBuffer(subject) {\n  return subject instanceof globalThis.ArrayBuffer || Object.prototype.toString.call(subject) === '[object ArrayBuffer]';\n}\nfunction isDataView(subject) {\n  return subject instanceof globalThis.DataView || Object.prototype.toString.call(subject) === '[object DataView]'\n}\n\nconst combine = function ([shp, dbf]) {\n  const out = {};\n  out.type = 'FeatureCollection';\n  out.features = [];\n  let i = 0;\n  const len = shp.length;\n  if (!dbf) {\n    dbf = [];\n  }\n  while (i < len) {\n    out.features.push({\n      type: 'Feature',\n      geometry: shp[i],\n      properties: dbf[i] || {}\n    });\n    i++;\n  }\n  return out;\n};\nconst parseZip = async function (buffer, whiteList) {\n  let key;\n  buffer = toUitn8Arr(buffer);\n  const zip = await unzip(buffer);\n  const names = [];\n  whiteList = whiteList || [];\n  for (key in zip) {\n    if (key.indexOf('__MACOSX') !== -1) {\n      continue;\n    }\n    if (key.slice(-4).toLowerCase() === '.shp') {\n      names.push(key.slice(0, -4));\n      zip[key.slice(0, -3) + key.slice(-3).toLowerCase()] = zip[key];\n    } else if (key.slice(-4).toLowerCase() === '.prj') {\n      zip[key.slice(0, -3) + key.slice(-3).toLowerCase()] = proj4(zip[key]);\n    } else if (key.slice(-5).toLowerCase() === '.json' || whiteList.indexOf(key.split('.').pop()) > -1) {\n      names.push(key.slice(0, -3) + key.slice(-3).toLowerCase());\n    } else if (key.slice(-4).toLowerCase() === '.dbf' || key.slice(-4).toLowerCase() === '.cpg') {\n      zip[key.slice(0, -3) + key.slice(-3).toLowerCase()] = zip[key];\n    }\n  }\n  if (!names.length) {\n    throw new Error('no layers founds');\n  }\n  const geojson = names.map(function (name) {\n    let parsed, dbf;\n    const lastDotIdx = name.lastIndexOf('.');\n    if (lastDotIdx > -1 && name.slice(lastDotIdx).indexOf('json') > -1) {\n      parsed = JSON.parse(zip[name]);\n      parsed.fileName = name.slice(0, lastDotIdx);\n    } else if (whiteList.indexOf(name.slice(lastDotIdx + 1)) > -1) {\n      parsed = zip[name];\n      parsed.fileName = name;\n    } else {\n      if (zip[name + '.dbf']) {\n        dbf = parseDbf(zip[name + '.dbf'], zip[name + '.cpg']);\n      }\n      parsed = combine([parseShp(zip[name + '.shp'], zip[name + '.prj']), dbf]);\n      parsed.fileName = name;\n    }\n    return parsed;\n  });\n  if (geojson.length === 1) {\n    return geojson[0];\n  } else {\n    return geojson;\n  }\n};\nasync function getZip(base, whiteList) {\n  const a = await binaryAjax(base);\n  return parseZip(a, whiteList);\n}\nconst handleShp = async (base) => {\n  const args = await Promise.all([\n    binaryAjax(base, 'shp'),\n    binaryAjax(base, 'prj')\n  ]);\n  let prj = false;\n  try {\n    if (args[1]) {\n      prj = proj4(args[1]);\n    }\n  } catch (e) {\n    prj = false;\n  }\n  return parseShp(args[0], prj);\n};\nconst handleDbf = async (base) => {\n  const [dbf, cpg] = await Promise.all([\n    binaryAjax(base, 'dbf'),\n    binaryAjax(base, 'cpg')\n  ]);\n  if (!dbf) {\n    return;\n  }\n  return parseDbf(dbf, cpg);\n};\nconst checkSuffix = (base, suffix) => {\n  const url = new URL(base, globalThis?.document?.location);\n  return url.pathname.slice(-4).toLowerCase() === suffix;\n};\nconst fromObject = ({ shp, dbf, cpg, prj }) => {\n  const things = [\n    _parseShp(shp, prj)\n  ];\n  if (dbf) {\n    things.push(_parseDbf(dbf, cpg));\n  }\n  return combine(things);\n};\nconst getShapefile = async function (base, whiteList) {\n  if (typeof base !== 'string') {\n    if (isArrayBuffer(base) || ArrayBuffer.isView(base) || isDataView(base)) {\n      return parseZip(base);\n    }\n    if (base.shp) {\n      return fromObject(base);\n    }\n    throw new TypeError('must be a string, some sort of Buffer, or an object with at least a .shp property')\n  }\n  if (checkSuffix(base, '.zip')) {\n    return getZip(base, whiteList);\n  }\n  if (checkSuffix(base, '.shp')) {\n    base = base.slice(0, -4);\n  }\n  const results = await Promise.all([\n    handleShp(base),\n    handleDbf(base)\n  ]);\n  return combine(results);\n};\nconst _parseShp = function (shp, prj) {\n  shp = toDataView(shp);\n  prj = toString(prj);\n  if (typeof prj === 'string') {\n    try {\n      prj = proj4(prj);\n    } catch (e) {\n      prj = false;\n    }\n  }\n  return parseShp(shp, prj);\n};\nconst _parseDbf = function (dbf, cpg) {\n  dbf = toDataView(dbf);\n  cpg = toString(cpg);\n  return parseDbf(dbf, cpg);\n};\n\nexport { combine, getShapefile as default, getShapefile, _parseDbf as parseDbf, _parseShp as parseShp, parseZip };\n"
  },
  {
    "path": "public/assets/lib/vendor/pdfjs/pdf.js",
    "content": "/**\n * @licstart The following is the entire license notice for the\n * JavaScript code in this page\n *\n * Copyright 2023 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * @licend The above is the entire license notice for the\n * JavaScript code in this page\n */\n\n/******/ var __webpack_modules__ = ({\n\n/***/ 640:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n  AnnotationLayer: () => (/* binding */ AnnotationLayer),\n  FreeTextAnnotationElement: () => (/* binding */ FreeTextAnnotationElement),\n  InkAnnotationElement: () => (/* binding */ InkAnnotationElement),\n  StampAnnotationElement: () => (/* binding */ StampAnnotationElement)\n});\n\n// EXTERNAL MODULE: ./src/shared/util.js\nvar util = __webpack_require__(266);\n// EXTERNAL MODULE: ./src/display/display_utils.js\nvar display_utils = __webpack_require__(473);\n// EXTERNAL MODULE: ./src/display/annotation_storage.js\nvar annotation_storage = __webpack_require__(780);\n;// CONCATENATED MODULE: ./src/shared/scripting_utils.js\nfunction makeColorComp(n) {\n  return Math.floor(Math.max(0, Math.min(1, n)) * 255).toString(16).padStart(2, \"0\");\n}\nfunction scaleAndClamp(x) {\n  return Math.max(0, Math.min(255, 255 * x));\n}\nclass ColorConverters {\n  static CMYK_G([c, y, m, k]) {\n    return [\"G\", 1 - Math.min(1, 0.3 * c + 0.59 * m + 0.11 * y + k)];\n  }\n  static G_CMYK([g]) {\n    return [\"CMYK\", 0, 0, 0, 1 - g];\n  }\n  static G_RGB([g]) {\n    return [\"RGB\", g, g, g];\n  }\n  static G_rgb([g]) {\n    g = scaleAndClamp(g);\n    return [g, g, g];\n  }\n  static G_HTML([g]) {\n    const G = makeColorComp(g);\n    return `#${G}${G}${G}`;\n  }\n  static RGB_G([r, g, b]) {\n    return [\"G\", 0.3 * r + 0.59 * g + 0.11 * b];\n  }\n  static RGB_rgb(color) {\n    return color.map(scaleAndClamp);\n  }\n  static RGB_HTML(color) {\n    return `#${color.map(makeColorComp).join(\"\")}`;\n  }\n  static T_HTML() {\n    return \"#00000000\";\n  }\n  static T_rgb() {\n    return [null];\n  }\n  static CMYK_RGB([c, y, m, k]) {\n    return [\"RGB\", 1 - Math.min(1, c + k), 1 - Math.min(1, m + k), 1 - Math.min(1, y + k)];\n  }\n  static CMYK_rgb([c, y, m, k]) {\n    return [scaleAndClamp(1 - Math.min(1, c + k)), scaleAndClamp(1 - Math.min(1, m + k)), scaleAndClamp(1 - Math.min(1, y + k))];\n  }\n  static CMYK_HTML(components) {\n    const rgb = this.CMYK_RGB(components).slice(1);\n    return this.RGB_HTML(rgb);\n  }\n  static RGB_CMYK([r, g, b]) {\n    const c = 1 - r;\n    const m = 1 - g;\n    const y = 1 - b;\n    const k = Math.min(c, m, y);\n    return [\"CMYK\", c, m, y, k];\n  }\n}\n\n// EXTERNAL MODULE: ./src/display/xfa_layer.js\nvar xfa_layer = __webpack_require__(160);\n;// CONCATENATED MODULE: ./src/display/annotation_layer.js\n\n\n\n\n\nconst DEFAULT_TAB_INDEX = 1000;\nconst DEFAULT_FONT_SIZE = 9;\nconst GetElementsByNameSet = new WeakSet();\nfunction getRectDims(rect) {\n  return {\n    width: rect[2] - rect[0],\n    height: rect[3] - rect[1]\n  };\n}\nclass AnnotationElementFactory {\n  static create(parameters) {\n    const subtype = parameters.data.annotationType;\n    switch (subtype) {\n      case util.AnnotationType.LINK:\n        return new LinkAnnotationElement(parameters);\n      case util.AnnotationType.TEXT:\n        return new TextAnnotationElement(parameters);\n      case util.AnnotationType.WIDGET:\n        const fieldType = parameters.data.fieldType;\n        switch (fieldType) {\n          case \"Tx\":\n            return new TextWidgetAnnotationElement(parameters);\n          case \"Btn\":\n            if (parameters.data.radioButton) {\n              return new RadioButtonWidgetAnnotationElement(parameters);\n            } else if (parameters.data.checkBox) {\n              return new CheckboxWidgetAnnotationElement(parameters);\n            }\n            return new PushButtonWidgetAnnotationElement(parameters);\n          case \"Ch\":\n            return new ChoiceWidgetAnnotationElement(parameters);\n          case \"Sig\":\n            return new SignatureWidgetAnnotationElement(parameters);\n        }\n        return new WidgetAnnotationElement(parameters);\n      case util.AnnotationType.POPUP:\n        return new PopupAnnotationElement(parameters);\n      case util.AnnotationType.FREETEXT:\n        return new FreeTextAnnotationElement(parameters);\n      case util.AnnotationType.LINE:\n        return new LineAnnotationElement(parameters);\n      case util.AnnotationType.SQUARE:\n        return new SquareAnnotationElement(parameters);\n      case util.AnnotationType.CIRCLE:\n        return new CircleAnnotationElement(parameters);\n      case util.AnnotationType.POLYLINE:\n        return new PolylineAnnotationElement(parameters);\n      case util.AnnotationType.CARET:\n        return new CaretAnnotationElement(parameters);\n      case util.AnnotationType.INK:\n        return new InkAnnotationElement(parameters);\n      case util.AnnotationType.POLYGON:\n        return new PolygonAnnotationElement(parameters);\n      case util.AnnotationType.HIGHLIGHT:\n        return new HighlightAnnotationElement(parameters);\n      case util.AnnotationType.UNDERLINE:\n        return new UnderlineAnnotationElement(parameters);\n      case util.AnnotationType.SQUIGGLY:\n        return new SquigglyAnnotationElement(parameters);\n      case util.AnnotationType.STRIKEOUT:\n        return new StrikeOutAnnotationElement(parameters);\n      case util.AnnotationType.STAMP:\n        return new StampAnnotationElement(parameters);\n      case util.AnnotationType.FILEATTACHMENT:\n        return new FileAttachmentAnnotationElement(parameters);\n      default:\n        return new AnnotationElement(parameters);\n    }\n  }\n}\nclass AnnotationElement {\n  #hasBorder = false;\n  constructor(parameters, {\n    isRenderable = false,\n    ignoreBorder = false,\n    createQuadrilaterals = false\n  } = {}) {\n    this.isRenderable = isRenderable;\n    this.data = parameters.data;\n    this.layer = parameters.layer;\n    this.linkService = parameters.linkService;\n    this.downloadManager = parameters.downloadManager;\n    this.imageResourcesPath = parameters.imageResourcesPath;\n    this.renderForms = parameters.renderForms;\n    this.svgFactory = parameters.svgFactory;\n    this.annotationStorage = parameters.annotationStorage;\n    this.enableScripting = parameters.enableScripting;\n    this.hasJSActions = parameters.hasJSActions;\n    this._fieldObjects = parameters.fieldObjects;\n    this.parent = parameters.parent;\n    if (isRenderable) {\n      this.container = this._createContainer(ignoreBorder);\n    }\n    if (createQuadrilaterals) {\n      this._createQuadrilaterals();\n    }\n  }\n  static _hasPopupData({\n    titleObj,\n    contentsObj,\n    richText\n  }) {\n    return !!(titleObj?.str || contentsObj?.str || richText?.str);\n  }\n  get hasPopupData() {\n    return AnnotationElement._hasPopupData(this.data);\n  }\n  _createContainer(ignoreBorder) {\n    const {\n      data,\n      parent: {\n        page,\n        viewport\n      }\n    } = this;\n    const container = document.createElement(\"section\");\n    container.setAttribute(\"data-annotation-id\", data.id);\n    if (!(this instanceof WidgetAnnotationElement)) {\n      container.tabIndex = DEFAULT_TAB_INDEX;\n    }\n    container.style.zIndex = this.parent.zIndex++;\n    if (this.data.popupRef) {\n      container.setAttribute(\"aria-haspopup\", \"dialog\");\n    }\n    if (data.noRotate) {\n      container.classList.add(\"norotate\");\n    }\n    const {\n      pageWidth,\n      pageHeight,\n      pageX,\n      pageY\n    } = viewport.rawDims;\n    if (!data.rect || this instanceof PopupAnnotationElement) {\n      const {\n        rotation\n      } = data;\n      if (!data.hasOwnCanvas && rotation !== 0) {\n        this.setRotation(rotation, container);\n      }\n      return container;\n    }\n    const {\n      width,\n      height\n    } = getRectDims(data.rect);\n    const rect = util.Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]);\n    if (!ignoreBorder && data.borderStyle.width > 0) {\n      container.style.borderWidth = `${data.borderStyle.width}px`;\n      const horizontalRadius = data.borderStyle.horizontalCornerRadius;\n      const verticalRadius = data.borderStyle.verticalCornerRadius;\n      if (horizontalRadius > 0 || verticalRadius > 0) {\n        const radius = `calc(${horizontalRadius}px * var(--scale-factor)) / calc(${verticalRadius}px * var(--scale-factor))`;\n        container.style.borderRadius = radius;\n      } else if (this instanceof RadioButtonWidgetAnnotationElement) {\n        const radius = `calc(${width}px * var(--scale-factor)) / calc(${height}px * var(--scale-factor))`;\n        container.style.borderRadius = radius;\n      }\n      switch (data.borderStyle.style) {\n        case util.AnnotationBorderStyleType.SOLID:\n          container.style.borderStyle = \"solid\";\n          break;\n        case util.AnnotationBorderStyleType.DASHED:\n          container.style.borderStyle = \"dashed\";\n          break;\n        case util.AnnotationBorderStyleType.BEVELED:\n          (0,util.warn)(\"Unimplemented border style: beveled\");\n          break;\n        case util.AnnotationBorderStyleType.INSET:\n          (0,util.warn)(\"Unimplemented border style: inset\");\n          break;\n        case util.AnnotationBorderStyleType.UNDERLINE:\n          container.style.borderBottomStyle = \"solid\";\n          break;\n        default:\n          break;\n      }\n      const borderColor = data.borderColor || null;\n      if (borderColor) {\n        this.#hasBorder = true;\n        container.style.borderColor = util.Util.makeHexColor(borderColor[0] | 0, borderColor[1] | 0, borderColor[2] | 0);\n      } else {\n        container.style.borderWidth = 0;\n      }\n    }\n    container.style.left = `${100 * (rect[0] - pageX) / pageWidth}%`;\n    container.style.top = `${100 * (rect[1] - pageY) / pageHeight}%`;\n    const {\n      rotation\n    } = data;\n    if (data.hasOwnCanvas || rotation === 0) {\n      container.style.width = `${100 * width / pageWidth}%`;\n      container.style.height = `${100 * height / pageHeight}%`;\n    } else {\n      this.setRotation(rotation, container);\n    }\n    return container;\n  }\n  setRotation(angle, container = this.container) {\n    if (!this.data.rect) {\n      return;\n    }\n    const {\n      pageWidth,\n      pageHeight\n    } = this.parent.viewport.rawDims;\n    const {\n      width,\n      height\n    } = getRectDims(this.data.rect);\n    let elementWidth, elementHeight;\n    if (angle % 180 === 0) {\n      elementWidth = 100 * width / pageWidth;\n      elementHeight = 100 * height / pageHeight;\n    } else {\n      elementWidth = 100 * height / pageWidth;\n      elementHeight = 100 * width / pageHeight;\n    }\n    container.style.width = `${elementWidth}%`;\n    container.style.height = `${elementHeight}%`;\n    container.setAttribute(\"data-main-rotation\", (360 - angle) % 360);\n  }\n  get _commonActions() {\n    const setColor = (jsName, styleName, event) => {\n      const color = event.detail[jsName];\n      const colorType = color[0];\n      const colorArray = color.slice(1);\n      event.target.style[styleName] = ColorConverters[`${colorType}_HTML`](colorArray);\n      this.annotationStorage.setValue(this.data.id, {\n        [styleName]: ColorConverters[`${colorType}_rgb`](colorArray)\n      });\n    };\n    return (0,util.shadow)(this, \"_commonActions\", {\n      display: event => {\n        const {\n          display\n        } = event.detail;\n        const hidden = display % 2 === 1;\n        this.container.style.visibility = hidden ? \"hidden\" : \"visible\";\n        this.annotationStorage.setValue(this.data.id, {\n          noView: hidden,\n          noPrint: display === 1 || display === 2\n        });\n      },\n      print: event => {\n        this.annotationStorage.setValue(this.data.id, {\n          noPrint: !event.detail.print\n        });\n      },\n      hidden: event => {\n        const {\n          hidden\n        } = event.detail;\n        this.container.style.visibility = hidden ? \"hidden\" : \"visible\";\n        this.annotationStorage.setValue(this.data.id, {\n          noPrint: hidden,\n          noView: hidden\n        });\n      },\n      focus: event => {\n        setTimeout(() => event.target.focus({\n          preventScroll: false\n        }), 0);\n      },\n      userName: event => {\n        event.target.title = event.detail.userName;\n      },\n      readonly: event => {\n        event.target.disabled = event.detail.readonly;\n      },\n      required: event => {\n        this._setRequired(event.target, event.detail.required);\n      },\n      bgColor: event => {\n        setColor(\"bgColor\", \"backgroundColor\", event);\n      },\n      fillColor: event => {\n        setColor(\"fillColor\", \"backgroundColor\", event);\n      },\n      fgColor: event => {\n        setColor(\"fgColor\", \"color\", event);\n      },\n      textColor: event => {\n        setColor(\"textColor\", \"color\", event);\n      },\n      borderColor: event => {\n        setColor(\"borderColor\", \"borderColor\", event);\n      },\n      strokeColor: event => {\n        setColor(\"strokeColor\", \"borderColor\", event);\n      },\n      rotation: event => {\n        const angle = event.detail.rotation;\n        this.setRotation(angle);\n        this.annotationStorage.setValue(this.data.id, {\n          rotation: angle\n        });\n      }\n    });\n  }\n  _dispatchEventFromSandbox(actions, jsEvent) {\n    const commonActions = this._commonActions;\n    for (const name of Object.keys(jsEvent.detail)) {\n      const action = actions[name] || commonActions[name];\n      action?.(jsEvent);\n    }\n  }\n  _setDefaultPropertiesFromJS(element) {\n    if (!this.enableScripting) {\n      return;\n    }\n    const storedData = this.annotationStorage.getRawValue(this.data.id);\n    if (!storedData) {\n      return;\n    }\n    const commonActions = this._commonActions;\n    for (const [actionName, detail] of Object.entries(storedData)) {\n      const action = commonActions[actionName];\n      if (action) {\n        const eventProxy = {\n          detail: {\n            [actionName]: detail\n          },\n          target: element\n        };\n        action(eventProxy);\n        delete storedData[actionName];\n      }\n    }\n  }\n  _createQuadrilaterals() {\n    if (!this.container) {\n      return;\n    }\n    const {\n      quadPoints\n    } = this.data;\n    if (!quadPoints) {\n      return;\n    }\n    const [rectBlX, rectBlY, rectTrX, rectTrY] = this.data.rect;\n    if (quadPoints.length === 1) {\n      const [, {\n        x: trX,\n        y: trY\n      }, {\n        x: blX,\n        y: blY\n      }] = quadPoints[0];\n      if (rectTrX === trX && rectTrY === trY && rectBlX === blX && rectBlY === blY) {\n        return;\n      }\n    }\n    const {\n      style\n    } = this.container;\n    let svgBuffer;\n    if (this.#hasBorder) {\n      const {\n        borderColor,\n        borderWidth\n      } = style;\n      style.borderWidth = 0;\n      svgBuffer = [\"url('data:image/svg+xml;utf8,\", `<svg xmlns=\"http://www.w3.org/2000/svg\"`, ` preserveAspectRatio=\"none\" viewBox=\"0 0 1 1\">`, `<g fill=\"transparent\" stroke=\"${borderColor}\" stroke-width=\"${borderWidth}\">`];\n      this.container.classList.add(\"hasBorder\");\n    }\n    const width = rectTrX - rectBlX;\n    const height = rectTrY - rectBlY;\n    const {\n      svgFactory\n    } = this;\n    const svg = svgFactory.createElement(\"svg\");\n    svg.classList.add(\"quadrilateralsContainer\");\n    svg.setAttribute(\"width\", 0);\n    svg.setAttribute(\"height\", 0);\n    const defs = svgFactory.createElement(\"defs\");\n    svg.append(defs);\n    const clipPath = svgFactory.createElement(\"clipPath\");\n    const id = `clippath_${this.data.id}`;\n    clipPath.setAttribute(\"id\", id);\n    clipPath.setAttribute(\"clipPathUnits\", \"objectBoundingBox\");\n    defs.append(clipPath);\n    for (const [, {\n      x: trX,\n      y: trY\n    }, {\n      x: blX,\n      y: blY\n    }] of quadPoints) {\n      const rect = svgFactory.createElement(\"rect\");\n      const x = (blX - rectBlX) / width;\n      const y = (rectTrY - trY) / height;\n      const rectWidth = (trX - blX) / width;\n      const rectHeight = (trY - blY) / height;\n      rect.setAttribute(\"x\", x);\n      rect.setAttribute(\"y\", y);\n      rect.setAttribute(\"width\", rectWidth);\n      rect.setAttribute(\"height\", rectHeight);\n      clipPath.append(rect);\n      svgBuffer?.push(`<rect vector-effect=\"non-scaling-stroke\" x=\"${x}\" y=\"${y}\" width=\"${rectWidth}\" height=\"${rectHeight}\"/>`);\n    }\n    if (this.#hasBorder) {\n      svgBuffer.push(`</g></svg>')`);\n      style.backgroundImage = svgBuffer.join(\"\");\n    }\n    this.container.append(svg);\n    this.container.style.clipPath = `url(#${id})`;\n  }\n  _createPopup() {\n    const {\n      container,\n      data\n    } = this;\n    container.setAttribute(\"aria-haspopup\", \"dialog\");\n    const popup = new PopupAnnotationElement({\n      data: {\n        color: data.color,\n        titleObj: data.titleObj,\n        modificationDate: data.modificationDate,\n        contentsObj: data.contentsObj,\n        richText: data.richText,\n        parentRect: data.rect,\n        borderStyle: 0,\n        id: `popup_${data.id}`,\n        rotation: data.rotation\n      },\n      parent: this.parent,\n      elements: [this]\n    });\n    this.parent.div.append(popup.render());\n  }\n  render() {\n    (0,util.unreachable)(\"Abstract method `AnnotationElement.render` called\");\n  }\n  _getElementsByName(name, skipId = null) {\n    const fields = [];\n    if (this._fieldObjects) {\n      const fieldObj = this._fieldObjects[name];\n      if (fieldObj) {\n        for (const {\n          page,\n          id,\n          exportValues\n        } of fieldObj) {\n          if (page === -1) {\n            continue;\n          }\n          if (id === skipId) {\n            continue;\n          }\n          const exportValue = typeof exportValues === \"string\" ? exportValues : null;\n          const domElement = document.querySelector(`[data-element-id=\"${id}\"]`);\n          if (domElement && !GetElementsByNameSet.has(domElement)) {\n            (0,util.warn)(`_getElementsByName - element not allowed: ${id}`);\n            continue;\n          }\n          fields.push({\n            id,\n            exportValue,\n            domElement\n          });\n        }\n      }\n      return fields;\n    }\n    for (const domElement of document.getElementsByName(name)) {\n      const {\n        exportValue\n      } = domElement;\n      const id = domElement.getAttribute(\"data-element-id\");\n      if (id === skipId) {\n        continue;\n      }\n      if (!GetElementsByNameSet.has(domElement)) {\n        continue;\n      }\n      fields.push({\n        id,\n        exportValue,\n        domElement\n      });\n    }\n    return fields;\n  }\n  show() {\n    if (this.container) {\n      this.container.hidden = false;\n    }\n    this.popup?.maybeShow();\n  }\n  hide() {\n    if (this.container) {\n      this.container.hidden = true;\n    }\n    this.popup?.forceHide();\n  }\n  getElementsToTriggerPopup() {\n    return this.container;\n  }\n  addHighlightArea() {\n    const triggers = this.getElementsToTriggerPopup();\n    if (Array.isArray(triggers)) {\n      for (const element of triggers) {\n        element.classList.add(\"highlightArea\");\n      }\n    } else {\n      triggers.classList.add(\"highlightArea\");\n    }\n  }\n  get _isEditable() {\n    return false;\n  }\n  _editOnDoubleClick() {\n    if (!this._isEditable) {\n      return;\n    }\n    const {\n      annotationEditorType: mode,\n      data: {\n        id: editId\n      }\n    } = this;\n    this.container.addEventListener(\"dblclick\", () => {\n      this.linkService.eventBus?.dispatch(\"switchannotationeditormode\", {\n        source: this,\n        mode,\n        editId\n      });\n    });\n  }\n}\nclass LinkAnnotationElement extends AnnotationElement {\n  constructor(parameters, options = null) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: !!options?.ignoreBorder,\n      createQuadrilaterals: true\n    });\n    this.isTooltipOnly = parameters.data.isTooltipOnly;\n  }\n  render() {\n    const {\n      data,\n      linkService\n    } = this;\n    const link = document.createElement(\"a\");\n    link.setAttribute(\"data-element-id\", data.id);\n    let isBound = false;\n    if (data.url) {\n      linkService.addLinkAttributes(link, data.url, data.newWindow);\n      isBound = true;\n    } else if (data.action) {\n      this._bindNamedAction(link, data.action);\n      isBound = true;\n    } else if (data.attachment) {\n      this.#bindAttachment(link, data.attachment, data.attachmentDest);\n      isBound = true;\n    } else if (data.setOCGState) {\n      this.#bindSetOCGState(link, data.setOCGState);\n      isBound = true;\n    } else if (data.dest) {\n      this._bindLink(link, data.dest);\n      isBound = true;\n    } else {\n      if (data.actions && (data.actions.Action || data.actions[\"Mouse Up\"] || data.actions[\"Mouse Down\"]) && this.enableScripting && this.hasJSActions) {\n        this._bindJSAction(link, data);\n        isBound = true;\n      }\n      if (data.resetForm) {\n        this._bindResetFormAction(link, data.resetForm);\n        isBound = true;\n      } else if (this.isTooltipOnly && !isBound) {\n        this._bindLink(link, \"\");\n        isBound = true;\n      }\n    }\n    this.container.classList.add(\"linkAnnotation\");\n    if (isBound) {\n      this.container.append(link);\n    }\n    return this.container;\n  }\n  #setInternalLink() {\n    this.container.setAttribute(\"data-internal-link\", \"\");\n  }\n  _bindLink(link, destination) {\n    link.href = this.linkService.getDestinationHash(destination);\n    link.onclick = () => {\n      if (destination) {\n        this.linkService.goToDestination(destination);\n      }\n      return false;\n    };\n    if (destination || destination === \"\") {\n      this.#setInternalLink();\n    }\n  }\n  _bindNamedAction(link, action) {\n    link.href = this.linkService.getAnchorUrl(\"\");\n    link.onclick = () => {\n      this.linkService.executeNamedAction(action);\n      return false;\n    };\n    this.#setInternalLink();\n  }\n  #bindAttachment(link, attachment, dest = null) {\n    link.href = this.linkService.getAnchorUrl(\"\");\n    link.onclick = () => {\n      this.downloadManager?.openOrDownloadData(attachment.content, attachment.filename, dest);\n      return false;\n    };\n    this.#setInternalLink();\n  }\n  #bindSetOCGState(link, action) {\n    link.href = this.linkService.getAnchorUrl(\"\");\n    link.onclick = () => {\n      this.linkService.executeSetOCGState(action);\n      return false;\n    };\n    this.#setInternalLink();\n  }\n  _bindJSAction(link, data) {\n    link.href = this.linkService.getAnchorUrl(\"\");\n    const map = new Map([[\"Action\", \"onclick\"], [\"Mouse Up\", \"onmouseup\"], [\"Mouse Down\", \"onmousedown\"]]);\n    for (const name of Object.keys(data.actions)) {\n      const jsName = map.get(name);\n      if (!jsName) {\n        continue;\n      }\n      link[jsName] = () => {\n        this.linkService.eventBus?.dispatch(\"dispatcheventinsandbox\", {\n          source: this,\n          detail: {\n            id: data.id,\n            name\n          }\n        });\n        return false;\n      };\n    }\n    if (!link.onclick) {\n      link.onclick = () => false;\n    }\n    this.#setInternalLink();\n  }\n  _bindResetFormAction(link, resetForm) {\n    const otherClickAction = link.onclick;\n    if (!otherClickAction) {\n      link.href = this.linkService.getAnchorUrl(\"\");\n    }\n    this.#setInternalLink();\n    if (!this._fieldObjects) {\n      (0,util.warn)(`_bindResetFormAction - \"resetForm\" action not supported, ` + \"ensure that the `fieldObjects` parameter is provided.\");\n      if (!otherClickAction) {\n        link.onclick = () => false;\n      }\n      return;\n    }\n    link.onclick = () => {\n      otherClickAction?.();\n      const {\n        fields: resetFormFields,\n        refs: resetFormRefs,\n        include\n      } = resetForm;\n      const allFields = [];\n      if (resetFormFields.length !== 0 || resetFormRefs.length !== 0) {\n        const fieldIds = new Set(resetFormRefs);\n        for (const fieldName of resetFormFields) {\n          const fields = this._fieldObjects[fieldName] || [];\n          for (const {\n            id\n          } of fields) {\n            fieldIds.add(id);\n          }\n        }\n        for (const fields of Object.values(this._fieldObjects)) {\n          for (const field of fields) {\n            if (fieldIds.has(field.id) === include) {\n              allFields.push(field);\n            }\n          }\n        }\n      } else {\n        for (const fields of Object.values(this._fieldObjects)) {\n          allFields.push(...fields);\n        }\n      }\n      const storage = this.annotationStorage;\n      const allIds = [];\n      for (const field of allFields) {\n        const {\n          id\n        } = field;\n        allIds.push(id);\n        switch (field.type) {\n          case \"text\":\n            {\n              const value = field.defaultValue || \"\";\n              storage.setValue(id, {\n                value\n              });\n              break;\n            }\n          case \"checkbox\":\n          case \"radiobutton\":\n            {\n              const value = field.defaultValue === field.exportValues;\n              storage.setValue(id, {\n                value\n              });\n              break;\n            }\n          case \"combobox\":\n          case \"listbox\":\n            {\n              const value = field.defaultValue || \"\";\n              storage.setValue(id, {\n                value\n              });\n              break;\n            }\n          default:\n            continue;\n        }\n        const domElement = document.querySelector(`[data-element-id=\"${id}\"]`);\n        if (!domElement) {\n          continue;\n        } else if (!GetElementsByNameSet.has(domElement)) {\n          (0,util.warn)(`_bindResetFormAction - element not allowed: ${id}`);\n          continue;\n        }\n        domElement.dispatchEvent(new Event(\"resetform\"));\n      }\n      if (this.enableScripting) {\n        this.linkService.eventBus?.dispatch(\"dispatcheventinsandbox\", {\n          source: this,\n          detail: {\n            id: \"app\",\n            ids: allIds,\n            name: \"ResetForm\"\n          }\n        });\n      }\n      return false;\n    };\n  }\n}\nclass TextAnnotationElement extends AnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true\n    });\n  }\n  render() {\n    this.container.classList.add(\"textAnnotation\");\n    const image = document.createElement(\"img\");\n    image.src = this.imageResourcesPath + \"annotation-\" + this.data.name.toLowerCase() + \".svg\";\n    image.setAttribute(\"data-l10n-id\", \"pdfjs-text-annotation-type\");\n    image.setAttribute(\"data-l10n-args\", JSON.stringify({\n      type: this.data.name\n    }));\n    if (!this.data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    }\n    this.container.append(image);\n    return this.container;\n  }\n}\nclass WidgetAnnotationElement extends AnnotationElement {\n  render() {\n    if (this.data.alternativeText) {\n      this.container.title = this.data.alternativeText;\n    }\n    return this.container;\n  }\n  showElementAndHideCanvas(element) {\n    if (this.data.hasOwnCanvas) {\n      if (element.previousSibling?.nodeName === \"CANVAS\") {\n        element.previousSibling.hidden = true;\n      }\n      element.hidden = false;\n    }\n  }\n  _getKeyModifier(event) {\n    return util.FeatureTest.platform.isMac ? event.metaKey : event.ctrlKey;\n  }\n  _setEventListener(element, elementData, baseName, eventName, valueGetter) {\n    if (baseName.includes(\"mouse\")) {\n      element.addEventListener(baseName, event => {\n        this.linkService.eventBus?.dispatch(\"dispatcheventinsandbox\", {\n          source: this,\n          detail: {\n            id: this.data.id,\n            name: eventName,\n            value: valueGetter(event),\n            shift: event.shiftKey,\n            modifier: this._getKeyModifier(event)\n          }\n        });\n      });\n    } else {\n      element.addEventListener(baseName, event => {\n        if (baseName === \"blur\") {\n          if (!elementData.focused || !event.relatedTarget) {\n            return;\n          }\n          elementData.focused = false;\n        } else if (baseName === \"focus\") {\n          if (elementData.focused) {\n            return;\n          }\n          elementData.focused = true;\n        }\n        if (!valueGetter) {\n          return;\n        }\n        this.linkService.eventBus?.dispatch(\"dispatcheventinsandbox\", {\n          source: this,\n          detail: {\n            id: this.data.id,\n            name: eventName,\n            value: valueGetter(event)\n          }\n        });\n      });\n    }\n  }\n  _setEventListeners(element, elementData, names, getter) {\n    for (const [baseName, eventName] of names) {\n      if (eventName === \"Action\" || this.data.actions?.[eventName]) {\n        if (eventName === \"Focus\" || eventName === \"Blur\") {\n          elementData ||= {\n            focused: false\n          };\n        }\n        this._setEventListener(element, elementData, baseName, eventName, getter);\n        if (eventName === \"Focus\" && !this.data.actions?.Blur) {\n          this._setEventListener(element, elementData, \"blur\", \"Blur\", null);\n        } else if (eventName === \"Blur\" && !this.data.actions?.Focus) {\n          this._setEventListener(element, elementData, \"focus\", \"Focus\", null);\n        }\n      }\n    }\n  }\n  _setBackgroundColor(element) {\n    const color = this.data.backgroundColor || null;\n    element.style.backgroundColor = color === null ? \"transparent\" : util.Util.makeHexColor(color[0], color[1], color[2]);\n  }\n  _setTextStyle(element) {\n    const TEXT_ALIGNMENT = [\"left\", \"center\", \"right\"];\n    const {\n      fontColor\n    } = this.data.defaultAppearanceData;\n    const fontSize = this.data.defaultAppearanceData.fontSize || DEFAULT_FONT_SIZE;\n    const style = element.style;\n    let computedFontSize;\n    const BORDER_SIZE = 2;\n    const roundToOneDecimal = x => Math.round(10 * x) / 10;\n    if (this.data.multiLine) {\n      const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE);\n      const numberOfLines = Math.round(height / (util.LINE_FACTOR * fontSize)) || 1;\n      const lineHeight = height / numberOfLines;\n      computedFontSize = Math.min(fontSize, roundToOneDecimal(lineHeight / util.LINE_FACTOR));\n    } else {\n      const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE);\n      computedFontSize = Math.min(fontSize, roundToOneDecimal(height / util.LINE_FACTOR));\n    }\n    style.fontSize = `calc(${computedFontSize}px * var(--scale-factor))`;\n    style.color = util.Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]);\n    if (this.data.textAlignment !== null) {\n      style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment];\n    }\n  }\n  _setRequired(element, isRequired) {\n    if (isRequired) {\n      element.setAttribute(\"required\", true);\n    } else {\n      element.removeAttribute(\"required\");\n    }\n    element.setAttribute(\"aria-required\", isRequired);\n  }\n}\nclass TextWidgetAnnotationElement extends WidgetAnnotationElement {\n  constructor(parameters) {\n    const isRenderable = parameters.renderForms || parameters.data.hasOwnCanvas || !parameters.data.hasAppearance && !!parameters.data.fieldValue;\n    super(parameters, {\n      isRenderable\n    });\n  }\n  setPropertyOnSiblings(base, key, value, keyInStorage) {\n    const storage = this.annotationStorage;\n    for (const element of this._getElementsByName(base.name, base.id)) {\n      if (element.domElement) {\n        element.domElement[key] = value;\n      }\n      storage.setValue(element.id, {\n        [keyInStorage]: value\n      });\n    }\n  }\n  render() {\n    const storage = this.annotationStorage;\n    const id = this.data.id;\n    this.container.classList.add(\"textWidgetAnnotation\");\n    let element = null;\n    if (this.renderForms) {\n      const storedData = storage.getValue(id, {\n        value: this.data.fieldValue\n      });\n      let textContent = storedData.value || \"\";\n      const maxLen = storage.getValue(id, {\n        charLimit: this.data.maxLen\n      }).charLimit;\n      if (maxLen && textContent.length > maxLen) {\n        textContent = textContent.slice(0, maxLen);\n      }\n      let fieldFormattedValues = storedData.formattedValue || this.data.textContent?.join(\"\\n\") || null;\n      if (fieldFormattedValues && this.data.comb) {\n        fieldFormattedValues = fieldFormattedValues.replaceAll(/\\s+/g, \"\");\n      }\n      const elementData = {\n        userValue: textContent,\n        formattedValue: fieldFormattedValues,\n        lastCommittedValue: null,\n        commitKey: 1,\n        focused: false\n      };\n      if (this.data.multiLine) {\n        element = document.createElement(\"textarea\");\n        element.textContent = fieldFormattedValues ?? textContent;\n        if (this.data.doNotScroll) {\n          element.style.overflowY = \"hidden\";\n        }\n      } else {\n        element = document.createElement(\"input\");\n        element.type = \"text\";\n        element.setAttribute(\"value\", fieldFormattedValues ?? textContent);\n        if (this.data.doNotScroll) {\n          element.style.overflowX = \"hidden\";\n        }\n      }\n      if (this.data.hasOwnCanvas) {\n        element.hidden = true;\n      }\n      GetElementsByNameSet.add(element);\n      element.setAttribute(\"data-element-id\", id);\n      element.disabled = this.data.readOnly;\n      element.name = this.data.fieldName;\n      element.tabIndex = DEFAULT_TAB_INDEX;\n      this._setRequired(element, this.data.required);\n      if (maxLen) {\n        element.maxLength = maxLen;\n      }\n      element.addEventListener(\"input\", event => {\n        storage.setValue(id, {\n          value: event.target.value\n        });\n        this.setPropertyOnSiblings(element, \"value\", event.target.value, \"value\");\n        elementData.formattedValue = null;\n      });\n      element.addEventListener(\"resetform\", event => {\n        const defaultValue = this.data.defaultFieldValue ?? \"\";\n        element.value = elementData.userValue = defaultValue;\n        elementData.formattedValue = null;\n      });\n      let blurListener = event => {\n        const {\n          formattedValue\n        } = elementData;\n        if (formattedValue !== null && formattedValue !== undefined) {\n          event.target.value = formattedValue;\n        }\n        event.target.scrollLeft = 0;\n      };\n      if (this.enableScripting && this.hasJSActions) {\n        element.addEventListener(\"focus\", event => {\n          if (elementData.focused) {\n            return;\n          }\n          const {\n            target\n          } = event;\n          if (elementData.userValue) {\n            target.value = elementData.userValue;\n          }\n          elementData.lastCommittedValue = target.value;\n          elementData.commitKey = 1;\n          if (!this.data.actions?.Focus) {\n            elementData.focused = true;\n          }\n        });\n        element.addEventListener(\"updatefromsandbox\", jsEvent => {\n          this.showElementAndHideCanvas(jsEvent.target);\n          const actions = {\n            value(event) {\n              elementData.userValue = event.detail.value ?? \"\";\n              storage.setValue(id, {\n                value: elementData.userValue.toString()\n              });\n              event.target.value = elementData.userValue;\n            },\n            formattedValue(event) {\n              const {\n                formattedValue\n              } = event.detail;\n              elementData.formattedValue = formattedValue;\n              if (formattedValue !== null && formattedValue !== undefined && event.target !== document.activeElement) {\n                event.target.value = formattedValue;\n              }\n              storage.setValue(id, {\n                formattedValue\n              });\n            },\n            selRange(event) {\n              event.target.setSelectionRange(...event.detail.selRange);\n            },\n            charLimit: event => {\n              const {\n                charLimit\n              } = event.detail;\n              const {\n                target\n              } = event;\n              if (charLimit === 0) {\n                target.removeAttribute(\"maxLength\");\n                return;\n              }\n              target.setAttribute(\"maxLength\", charLimit);\n              let value = elementData.userValue;\n              if (!value || value.length <= charLimit) {\n                return;\n              }\n              value = value.slice(0, charLimit);\n              target.value = elementData.userValue = value;\n              storage.setValue(id, {\n                value\n              });\n              this.linkService.eventBus?.dispatch(\"dispatcheventinsandbox\", {\n                source: this,\n                detail: {\n                  id,\n                  name: \"Keystroke\",\n                  value,\n                  willCommit: true,\n                  commitKey: 1,\n                  selStart: target.selectionStart,\n                  selEnd: target.selectionEnd\n                }\n              });\n            }\n          };\n          this._dispatchEventFromSandbox(actions, jsEvent);\n        });\n        element.addEventListener(\"keydown\", event => {\n          elementData.commitKey = 1;\n          let commitKey = -1;\n          if (event.key === \"Escape\") {\n            commitKey = 0;\n          } else if (event.key === \"Enter\" && !this.data.multiLine) {\n            commitKey = 2;\n          } else if (event.key === \"Tab\") {\n            elementData.commitKey = 3;\n          }\n          if (commitKey === -1) {\n            return;\n          }\n          const {\n            value\n          } = event.target;\n          if (elementData.lastCommittedValue === value) {\n            return;\n          }\n          elementData.lastCommittedValue = value;\n          elementData.userValue = value;\n          this.linkService.eventBus?.dispatch(\"dispatcheventinsandbox\", {\n            source: this,\n            detail: {\n              id,\n              name: \"Keystroke\",\n              value,\n              willCommit: true,\n              commitKey,\n              selStart: event.target.selectionStart,\n              selEnd: event.target.selectionEnd\n            }\n          });\n        });\n        const _blurListener = blurListener;\n        blurListener = null;\n        element.addEventListener(\"blur\", event => {\n          if (!elementData.focused || !event.relatedTarget) {\n            return;\n          }\n          if (!this.data.actions?.Blur) {\n            elementData.focused = false;\n          }\n          const {\n            value\n          } = event.target;\n          elementData.userValue = value;\n          if (elementData.lastCommittedValue !== value) {\n            this.linkService.eventBus?.dispatch(\"dispatcheventinsandbox\", {\n              source: this,\n              detail: {\n                id,\n                name: \"Keystroke\",\n                value,\n                willCommit: true,\n                commitKey: elementData.commitKey,\n                selStart: event.target.selectionStart,\n                selEnd: event.target.selectionEnd\n              }\n            });\n          }\n          _blurListener(event);\n        });\n        if (this.data.actions?.Keystroke) {\n          element.addEventListener(\"beforeinput\", event => {\n            elementData.lastCommittedValue = null;\n            const {\n              data,\n              target\n            } = event;\n            const {\n              value,\n              selectionStart,\n              selectionEnd\n            } = target;\n            let selStart = selectionStart,\n              selEnd = selectionEnd;\n            switch (event.inputType) {\n              case \"deleteWordBackward\":\n                {\n                  const match = value.substring(0, selectionStart).match(/\\w*[^\\w]*$/);\n                  if (match) {\n                    selStart -= match[0].length;\n                  }\n                  break;\n                }\n              case \"deleteWordForward\":\n                {\n                  const match = value.substring(selectionStart).match(/^[^\\w]*\\w*/);\n                  if (match) {\n                    selEnd += match[0].length;\n                  }\n                  break;\n                }\n              case \"deleteContentBackward\":\n                if (selectionStart === selectionEnd) {\n                  selStart -= 1;\n                }\n                break;\n              case \"deleteContentForward\":\n                if (selectionStart === selectionEnd) {\n                  selEnd += 1;\n                }\n                break;\n            }\n            event.preventDefault();\n            this.linkService.eventBus?.dispatch(\"dispatcheventinsandbox\", {\n              source: this,\n              detail: {\n                id,\n                name: \"Keystroke\",\n                value,\n                change: data || \"\",\n                willCommit: false,\n                selStart,\n                selEnd\n              }\n            });\n          });\n        }\n        this._setEventListeners(element, elementData, [[\"focus\", \"Focus\"], [\"blur\", \"Blur\"], [\"mousedown\", \"Mouse Down\"], [\"mouseenter\", \"Mouse Enter\"], [\"mouseleave\", \"Mouse Exit\"], [\"mouseup\", \"Mouse Up\"]], event => event.target.value);\n      }\n      if (blurListener) {\n        element.addEventListener(\"blur\", blurListener);\n      }\n      if (this.data.comb) {\n        const fieldWidth = this.data.rect[2] - this.data.rect[0];\n        const combWidth = fieldWidth / maxLen;\n        element.classList.add(\"comb\");\n        element.style.letterSpacing = `calc(${combWidth}px * var(--scale-factor) - 1ch)`;\n      }\n    } else {\n      element = document.createElement(\"div\");\n      element.textContent = this.data.fieldValue;\n      element.style.verticalAlign = \"middle\";\n      element.style.display = \"table-cell\";\n      if (this.data.hasOwnCanvas) {\n        element.hidden = true;\n      }\n    }\n    this._setTextStyle(element);\n    this._setBackgroundColor(element);\n    this._setDefaultPropertiesFromJS(element);\n    this.container.append(element);\n    return this.container;\n  }\n}\nclass SignatureWidgetAnnotationElement extends WidgetAnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: !!parameters.data.hasOwnCanvas\n    });\n  }\n}\nclass CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: parameters.renderForms\n    });\n  }\n  render() {\n    const storage = this.annotationStorage;\n    const data = this.data;\n    const id = data.id;\n    let value = storage.getValue(id, {\n      value: data.exportValue === data.fieldValue\n    }).value;\n    if (typeof value === \"string\") {\n      value = value !== \"Off\";\n      storage.setValue(id, {\n        value\n      });\n    }\n    this.container.classList.add(\"buttonWidgetAnnotation\", \"checkBox\");\n    const element = document.createElement(\"input\");\n    GetElementsByNameSet.add(element);\n    element.setAttribute(\"data-element-id\", id);\n    element.disabled = data.readOnly;\n    this._setRequired(element, this.data.required);\n    element.type = \"checkbox\";\n    element.name = data.fieldName;\n    if (value) {\n      element.setAttribute(\"checked\", true);\n    }\n    element.setAttribute(\"exportValue\", data.exportValue);\n    element.tabIndex = DEFAULT_TAB_INDEX;\n    element.addEventListener(\"change\", event => {\n      const {\n        name,\n        checked\n      } = event.target;\n      for (const checkbox of this._getElementsByName(name, id)) {\n        const curChecked = checked && checkbox.exportValue === data.exportValue;\n        if (checkbox.domElement) {\n          checkbox.domElement.checked = curChecked;\n        }\n        storage.setValue(checkbox.id, {\n          value: curChecked\n        });\n      }\n      storage.setValue(id, {\n        value: checked\n      });\n    });\n    element.addEventListener(\"resetform\", event => {\n      const defaultValue = data.defaultFieldValue || \"Off\";\n      event.target.checked = defaultValue === data.exportValue;\n    });\n    if (this.enableScripting && this.hasJSActions) {\n      element.addEventListener(\"updatefromsandbox\", jsEvent => {\n        const actions = {\n          value(event) {\n            event.target.checked = event.detail.value !== \"Off\";\n            storage.setValue(id, {\n              value: event.target.checked\n            });\n          }\n        };\n        this._dispatchEventFromSandbox(actions, jsEvent);\n      });\n      this._setEventListeners(element, null, [[\"change\", \"Validate\"], [\"change\", \"Action\"], [\"focus\", \"Focus\"], [\"blur\", \"Blur\"], [\"mousedown\", \"Mouse Down\"], [\"mouseenter\", \"Mouse Enter\"], [\"mouseleave\", \"Mouse Exit\"], [\"mouseup\", \"Mouse Up\"]], event => event.target.checked);\n    }\n    this._setBackgroundColor(element);\n    this._setDefaultPropertiesFromJS(element);\n    this.container.append(element);\n    return this.container;\n  }\n}\nclass RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: parameters.renderForms\n    });\n  }\n  render() {\n    this.container.classList.add(\"buttonWidgetAnnotation\", \"radioButton\");\n    const storage = this.annotationStorage;\n    const data = this.data;\n    const id = data.id;\n    let value = storage.getValue(id, {\n      value: data.fieldValue === data.buttonValue\n    }).value;\n    if (typeof value === \"string\") {\n      value = value !== data.buttonValue;\n      storage.setValue(id, {\n        value\n      });\n    }\n    if (value) {\n      for (const radio of this._getElementsByName(data.fieldName, id)) {\n        storage.setValue(radio.id, {\n          value: false\n        });\n      }\n    }\n    const element = document.createElement(\"input\");\n    GetElementsByNameSet.add(element);\n    element.setAttribute(\"data-element-id\", id);\n    element.disabled = data.readOnly;\n    this._setRequired(element, this.data.required);\n    element.type = \"radio\";\n    element.name = data.fieldName;\n    if (value) {\n      element.setAttribute(\"checked\", true);\n    }\n    element.tabIndex = DEFAULT_TAB_INDEX;\n    element.addEventListener(\"change\", event => {\n      const {\n        name,\n        checked\n      } = event.target;\n      for (const radio of this._getElementsByName(name, id)) {\n        storage.setValue(radio.id, {\n          value: false\n        });\n      }\n      storage.setValue(id, {\n        value: checked\n      });\n    });\n    element.addEventListener(\"resetform\", event => {\n      const defaultValue = data.defaultFieldValue;\n      event.target.checked = defaultValue !== null && defaultValue !== undefined && defaultValue === data.buttonValue;\n    });\n    if (this.enableScripting && this.hasJSActions) {\n      const pdfButtonValue = data.buttonValue;\n      element.addEventListener(\"updatefromsandbox\", jsEvent => {\n        const actions = {\n          value: event => {\n            const checked = pdfButtonValue === event.detail.value;\n            for (const radio of this._getElementsByName(event.target.name)) {\n              const curChecked = checked && radio.id === id;\n              if (radio.domElement) {\n                radio.domElement.checked = curChecked;\n              }\n              storage.setValue(radio.id, {\n                value: curChecked\n              });\n            }\n          }\n        };\n        this._dispatchEventFromSandbox(actions, jsEvent);\n      });\n      this._setEventListeners(element, null, [[\"change\", \"Validate\"], [\"change\", \"Action\"], [\"focus\", \"Focus\"], [\"blur\", \"Blur\"], [\"mousedown\", \"Mouse Down\"], [\"mouseenter\", \"Mouse Enter\"], [\"mouseleave\", \"Mouse Exit\"], [\"mouseup\", \"Mouse Up\"]], event => event.target.checked);\n    }\n    this._setBackgroundColor(element);\n    this._setDefaultPropertiesFromJS(element);\n    this.container.append(element);\n    return this.container;\n  }\n}\nclass PushButtonWidgetAnnotationElement extends LinkAnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      ignoreBorder: parameters.data.hasAppearance\n    });\n  }\n  render() {\n    const container = super.render();\n    container.classList.add(\"buttonWidgetAnnotation\", \"pushButton\");\n    if (this.data.alternativeText) {\n      container.title = this.data.alternativeText;\n    }\n    const linkElement = container.lastChild;\n    if (this.enableScripting && this.hasJSActions && linkElement) {\n      this._setDefaultPropertiesFromJS(linkElement);\n      linkElement.addEventListener(\"updatefromsandbox\", jsEvent => {\n        this._dispatchEventFromSandbox({}, jsEvent);\n      });\n    }\n    return container;\n  }\n}\nclass ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: parameters.renderForms\n    });\n  }\n  render() {\n    this.container.classList.add(\"choiceWidgetAnnotation\");\n    const storage = this.annotationStorage;\n    const id = this.data.id;\n    const storedData = storage.getValue(id, {\n      value: this.data.fieldValue\n    });\n    const selectElement = document.createElement(\"select\");\n    GetElementsByNameSet.add(selectElement);\n    selectElement.setAttribute(\"data-element-id\", id);\n    selectElement.disabled = this.data.readOnly;\n    this._setRequired(selectElement, this.data.required);\n    selectElement.name = this.data.fieldName;\n    selectElement.tabIndex = DEFAULT_TAB_INDEX;\n    let addAnEmptyEntry = this.data.combo && this.data.options.length > 0;\n    if (!this.data.combo) {\n      selectElement.size = this.data.options.length;\n      if (this.data.multiSelect) {\n        selectElement.multiple = true;\n      }\n    }\n    selectElement.addEventListener(\"resetform\", event => {\n      const defaultValue = this.data.defaultFieldValue;\n      for (const option of selectElement.options) {\n        option.selected = option.value === defaultValue;\n      }\n    });\n    for (const option of this.data.options) {\n      const optionElement = document.createElement(\"option\");\n      optionElement.textContent = option.displayValue;\n      optionElement.value = option.exportValue;\n      if (storedData.value.includes(option.exportValue)) {\n        optionElement.setAttribute(\"selected\", true);\n        addAnEmptyEntry = false;\n      }\n      selectElement.append(optionElement);\n    }\n    let removeEmptyEntry = null;\n    if (addAnEmptyEntry) {\n      const noneOptionElement = document.createElement(\"option\");\n      noneOptionElement.value = \" \";\n      noneOptionElement.setAttribute(\"hidden\", true);\n      noneOptionElement.setAttribute(\"selected\", true);\n      selectElement.prepend(noneOptionElement);\n      removeEmptyEntry = () => {\n        noneOptionElement.remove();\n        selectElement.removeEventListener(\"input\", removeEmptyEntry);\n        removeEmptyEntry = null;\n      };\n      selectElement.addEventListener(\"input\", removeEmptyEntry);\n    }\n    const getValue = isExport => {\n      const name = isExport ? \"value\" : \"textContent\";\n      const {\n        options,\n        multiple\n      } = selectElement;\n      if (!multiple) {\n        return options.selectedIndex === -1 ? null : options[options.selectedIndex][name];\n      }\n      return Array.prototype.filter.call(options, option => option.selected).map(option => option[name]);\n    };\n    let selectedValues = getValue(false);\n    const getItems = event => {\n      const options = event.target.options;\n      return Array.prototype.map.call(options, option => {\n        return {\n          displayValue: option.textContent,\n          exportValue: option.value\n        };\n      });\n    };\n    if (this.enableScripting && this.hasJSActions) {\n      selectElement.addEventListener(\"updatefromsandbox\", jsEvent => {\n        const actions = {\n          value(event) {\n            removeEmptyEntry?.();\n            const value = event.detail.value;\n            const values = new Set(Array.isArray(value) ? value : [value]);\n            for (const option of selectElement.options) {\n              option.selected = values.has(option.value);\n            }\n            storage.setValue(id, {\n              value: getValue(true)\n            });\n            selectedValues = getValue(false);\n          },\n          multipleSelection(event) {\n            selectElement.multiple = true;\n          },\n          remove(event) {\n            const options = selectElement.options;\n            const index = event.detail.remove;\n            options[index].selected = false;\n            selectElement.remove(index);\n            if (options.length > 0) {\n              const i = Array.prototype.findIndex.call(options, option => option.selected);\n              if (i === -1) {\n                options[0].selected = true;\n              }\n            }\n            storage.setValue(id, {\n              value: getValue(true),\n              items: getItems(event)\n            });\n            selectedValues = getValue(false);\n          },\n          clear(event) {\n            while (selectElement.length !== 0) {\n              selectElement.remove(0);\n            }\n            storage.setValue(id, {\n              value: null,\n              items: []\n            });\n            selectedValues = getValue(false);\n          },\n          insert(event) {\n            const {\n              index,\n              displayValue,\n              exportValue\n            } = event.detail.insert;\n            const selectChild = selectElement.children[index];\n            const optionElement = document.createElement(\"option\");\n            optionElement.textContent = displayValue;\n            optionElement.value = exportValue;\n            if (selectChild) {\n              selectChild.before(optionElement);\n            } else {\n              selectElement.append(optionElement);\n            }\n            storage.setValue(id, {\n              value: getValue(true),\n              items: getItems(event)\n            });\n            selectedValues = getValue(false);\n          },\n          items(event) {\n            const {\n              items\n            } = event.detail;\n            while (selectElement.length !== 0) {\n              selectElement.remove(0);\n            }\n            for (const item of items) {\n              const {\n                displayValue,\n                exportValue\n              } = item;\n              const optionElement = document.createElement(\"option\");\n              optionElement.textContent = displayValue;\n              optionElement.value = exportValue;\n              selectElement.append(optionElement);\n            }\n            if (selectElement.options.length > 0) {\n              selectElement.options[0].selected = true;\n            }\n            storage.setValue(id, {\n              value: getValue(true),\n              items: getItems(event)\n            });\n            selectedValues = getValue(false);\n          },\n          indices(event) {\n            const indices = new Set(event.detail.indices);\n            for (const option of event.target.options) {\n              option.selected = indices.has(option.index);\n            }\n            storage.setValue(id, {\n              value: getValue(true)\n            });\n            selectedValues = getValue(false);\n          },\n          editable(event) {\n            event.target.disabled = !event.detail.editable;\n          }\n        };\n        this._dispatchEventFromSandbox(actions, jsEvent);\n      });\n      selectElement.addEventListener(\"input\", event => {\n        const exportValue = getValue(true);\n        storage.setValue(id, {\n          value: exportValue\n        });\n        event.preventDefault();\n        this.linkService.eventBus?.dispatch(\"dispatcheventinsandbox\", {\n          source: this,\n          detail: {\n            id,\n            name: \"Keystroke\",\n            value: selectedValues,\n            changeEx: exportValue,\n            willCommit: false,\n            commitKey: 1,\n            keyDown: false\n          }\n        });\n      });\n      this._setEventListeners(selectElement, null, [[\"focus\", \"Focus\"], [\"blur\", \"Blur\"], [\"mousedown\", \"Mouse Down\"], [\"mouseenter\", \"Mouse Enter\"], [\"mouseleave\", \"Mouse Exit\"], [\"mouseup\", \"Mouse Up\"], [\"input\", \"Action\"], [\"input\", \"Validate\"]], event => event.target.value);\n    } else {\n      selectElement.addEventListener(\"input\", function (event) {\n        storage.setValue(id, {\n          value: getValue(true)\n        });\n      });\n    }\n    if (this.data.combo) {\n      this._setTextStyle(selectElement);\n    } else {}\n    this._setBackgroundColor(selectElement);\n    this._setDefaultPropertiesFromJS(selectElement);\n    this.container.append(selectElement);\n    return this.container;\n  }\n}\nclass PopupAnnotationElement extends AnnotationElement {\n  constructor(parameters) {\n    const {\n      data,\n      elements\n    } = parameters;\n    super(parameters, {\n      isRenderable: AnnotationElement._hasPopupData(data)\n    });\n    this.elements = elements;\n  }\n  render() {\n    this.container.classList.add(\"popupAnnotation\");\n    const popup = new PopupElement({\n      container: this.container,\n      color: this.data.color,\n      titleObj: this.data.titleObj,\n      modificationDate: this.data.modificationDate,\n      contentsObj: this.data.contentsObj,\n      richText: this.data.richText,\n      rect: this.data.rect,\n      parentRect: this.data.parentRect || null,\n      parent: this.parent,\n      elements: this.elements,\n      open: this.data.open\n    });\n    const elementIds = [];\n    for (const element of this.elements) {\n      element.popup = popup;\n      elementIds.push(element.data.id);\n      element.addHighlightArea();\n    }\n    this.container.setAttribute(\"aria-controls\", elementIds.map(id => `${util.AnnotationPrefix}${id}`).join(\",\"));\n    return this.container;\n  }\n}\nclass PopupElement {\n  #boundKeyDown = this.#keyDown.bind(this);\n  #boundHide = this.#hide.bind(this);\n  #boundShow = this.#show.bind(this);\n  #boundToggle = this.#toggle.bind(this);\n  #color = null;\n  #container = null;\n  #contentsObj = null;\n  #dateObj = null;\n  #elements = null;\n  #parent = null;\n  #parentRect = null;\n  #pinned = false;\n  #popup = null;\n  #rect = null;\n  #richText = null;\n  #titleObj = null;\n  #wasVisible = false;\n  constructor({\n    container,\n    color,\n    elements,\n    titleObj,\n    modificationDate,\n    contentsObj,\n    richText,\n    parent,\n    rect,\n    parentRect,\n    open\n  }) {\n    this.#container = container;\n    this.#titleObj = titleObj;\n    this.#contentsObj = contentsObj;\n    this.#richText = richText;\n    this.#parent = parent;\n    this.#color = color;\n    this.#rect = rect;\n    this.#parentRect = parentRect;\n    this.#elements = elements;\n    this.#dateObj = display_utils.PDFDateString.toDateObject(modificationDate);\n    this.trigger = elements.flatMap(e => e.getElementsToTriggerPopup());\n    for (const element of this.trigger) {\n      element.addEventListener(\"click\", this.#boundToggle);\n      element.addEventListener(\"mouseenter\", this.#boundShow);\n      element.addEventListener(\"mouseleave\", this.#boundHide);\n      element.classList.add(\"popupTriggerArea\");\n    }\n    for (const element of elements) {\n      element.container?.addEventListener(\"keydown\", this.#boundKeyDown);\n    }\n    this.#container.hidden = true;\n    if (open) {\n      this.#toggle();\n    }\n  }\n  render() {\n    if (this.#popup) {\n      return;\n    }\n    const {\n      page: {\n        view\n      },\n      viewport: {\n        rawDims: {\n          pageWidth,\n          pageHeight,\n          pageX,\n          pageY\n        }\n      }\n    } = this.#parent;\n    const popup = this.#popup = document.createElement(\"div\");\n    popup.className = \"popup\";\n    if (this.#color) {\n      const baseColor = popup.style.outlineColor = util.Util.makeHexColor(...this.#color);\n      if (CSS.supports(\"background-color\", \"color-mix(in srgb, red 30%, white)\")) {\n        popup.style.backgroundColor = `color-mix(in srgb, ${baseColor} 30%, white)`;\n      } else {\n        const BACKGROUND_ENLIGHT = 0.7;\n        popup.style.backgroundColor = util.Util.makeHexColor(...this.#color.map(c => Math.floor(BACKGROUND_ENLIGHT * (255 - c) + c)));\n      }\n    }\n    const header = document.createElement(\"span\");\n    header.className = \"header\";\n    const title = document.createElement(\"h1\");\n    header.append(title);\n    ({\n      dir: title.dir,\n      str: title.textContent\n    } = this.#titleObj);\n    popup.append(header);\n    if (this.#dateObj) {\n      const modificationDate = document.createElement(\"span\");\n      modificationDate.classList.add(\"popupDate\");\n      modificationDate.setAttribute(\"data-l10n-id\", \"pdfjs-annotation-date-string\");\n      modificationDate.setAttribute(\"data-l10n-args\", JSON.stringify({\n        date: this.#dateObj.toLocaleDateString(),\n        time: this.#dateObj.toLocaleTimeString()\n      }));\n      header.append(modificationDate);\n    }\n    const contentsObj = this.#contentsObj;\n    const richText = this.#richText;\n    if (richText?.str && (!contentsObj?.str || contentsObj.str === richText.str)) {\n      xfa_layer.XfaLayer.render({\n        xfaHtml: richText.html,\n        intent: \"richText\",\n        div: popup\n      });\n      popup.lastChild.classList.add(\"richText\", \"popupContent\");\n    } else {\n      const contents = this._formatContents(contentsObj);\n      popup.append(contents);\n    }\n    let useParentRect = !!this.#parentRect;\n    let rect = useParentRect ? this.#parentRect : this.#rect;\n    for (const element of this.#elements) {\n      if (!rect || util.Util.intersect(element.data.rect, rect) !== null) {\n        rect = element.data.rect;\n        useParentRect = true;\n        break;\n      }\n    }\n    const normalizedRect = util.Util.normalizeRect([rect[0], view[3] - rect[1] + view[1], rect[2], view[3] - rect[3] + view[1]]);\n    const HORIZONTAL_SPACE_AFTER_ANNOTATION = 5;\n    const parentWidth = useParentRect ? rect[2] - rect[0] + HORIZONTAL_SPACE_AFTER_ANNOTATION : 0;\n    const popupLeft = normalizedRect[0] + parentWidth;\n    const popupTop = normalizedRect[1];\n    const {\n      style\n    } = this.#container;\n    style.left = `${100 * (popupLeft - pageX) / pageWidth}%`;\n    style.top = `${100 * (popupTop - pageY) / pageHeight}%`;\n    this.#container.append(popup);\n  }\n  _formatContents({\n    str,\n    dir\n  }) {\n    const p = document.createElement(\"p\");\n    p.classList.add(\"popupContent\");\n    p.dir = dir;\n    const lines = str.split(/(?:\\r\\n?|\\n)/);\n    for (let i = 0, ii = lines.length; i < ii; ++i) {\n      const line = lines[i];\n      p.append(document.createTextNode(line));\n      if (i < ii - 1) {\n        p.append(document.createElement(\"br\"));\n      }\n    }\n    return p;\n  }\n  #keyDown(event) {\n    if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) {\n      return;\n    }\n    if (event.key === \"Enter\" || event.key === \"Escape\" && this.#pinned) {\n      this.#toggle();\n    }\n  }\n  #toggle() {\n    this.#pinned = !this.#pinned;\n    if (this.#pinned) {\n      this.#show();\n      this.#container.addEventListener(\"click\", this.#boundToggle);\n      this.#container.addEventListener(\"keydown\", this.#boundKeyDown);\n    } else {\n      this.#hide();\n      this.#container.removeEventListener(\"click\", this.#boundToggle);\n      this.#container.removeEventListener(\"keydown\", this.#boundKeyDown);\n    }\n  }\n  #show() {\n    if (!this.#popup) {\n      this.render();\n    }\n    if (!this.isVisible) {\n      this.#container.hidden = false;\n      this.#container.style.zIndex = parseInt(this.#container.style.zIndex) + 1000;\n    } else if (this.#pinned) {\n      this.#container.classList.add(\"focused\");\n    }\n  }\n  #hide() {\n    this.#container.classList.remove(\"focused\");\n    if (this.#pinned || !this.isVisible) {\n      return;\n    }\n    this.#container.hidden = true;\n    this.#container.style.zIndex = parseInt(this.#container.style.zIndex) - 1000;\n  }\n  forceHide() {\n    this.#wasVisible = this.isVisible;\n    if (!this.#wasVisible) {\n      return;\n    }\n    this.#container.hidden = true;\n  }\n  maybeShow() {\n    if (!this.#wasVisible) {\n      return;\n    }\n    this.#wasVisible = false;\n    this.#container.hidden = false;\n  }\n  get isVisible() {\n    return this.#container.hidden === false;\n  }\n}\nclass FreeTextAnnotationElement extends AnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: true\n    });\n    this.textContent = parameters.data.textContent;\n    this.textPosition = parameters.data.textPosition;\n    this.annotationEditorType = util.AnnotationEditorType.FREETEXT;\n  }\n  render() {\n    this.container.classList.add(\"freeTextAnnotation\");\n    if (this.textContent) {\n      const content = document.createElement(\"div\");\n      content.classList.add(\"annotationTextContent\");\n      content.setAttribute(\"role\", \"comment\");\n      for (const line of this.textContent) {\n        const lineSpan = document.createElement(\"span\");\n        lineSpan.textContent = line;\n        content.append(lineSpan);\n      }\n      this.container.append(content);\n    }\n    if (!this.data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    }\n    this._editOnDoubleClick();\n    return this.container;\n  }\n  get _isEditable() {\n    return this.data.hasOwnCanvas;\n  }\n}\nclass LineAnnotationElement extends AnnotationElement {\n  #line = null;\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: true\n    });\n  }\n  render() {\n    this.container.classList.add(\"lineAnnotation\");\n    const data = this.data;\n    const {\n      width,\n      height\n    } = getRectDims(data.rect);\n    const svg = this.svgFactory.create(width, height, true);\n    const line = this.#line = this.svgFactory.createElement(\"svg:line\");\n    line.setAttribute(\"x1\", data.rect[2] - data.lineCoordinates[0]);\n    line.setAttribute(\"y1\", data.rect[3] - data.lineCoordinates[1]);\n    line.setAttribute(\"x2\", data.rect[2] - data.lineCoordinates[2]);\n    line.setAttribute(\"y2\", data.rect[3] - data.lineCoordinates[3]);\n    line.setAttribute(\"stroke-width\", data.borderStyle.width || 1);\n    line.setAttribute(\"stroke\", \"transparent\");\n    line.setAttribute(\"fill\", \"transparent\");\n    svg.append(line);\n    this.container.append(svg);\n    if (!data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    }\n    return this.container;\n  }\n  getElementsToTriggerPopup() {\n    return this.#line;\n  }\n  addHighlightArea() {\n    this.container.classList.add(\"highlightArea\");\n  }\n}\nclass SquareAnnotationElement extends AnnotationElement {\n  #square = null;\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: true\n    });\n  }\n  render() {\n    this.container.classList.add(\"squareAnnotation\");\n    const data = this.data;\n    const {\n      width,\n      height\n    } = getRectDims(data.rect);\n    const svg = this.svgFactory.create(width, height, true);\n    const borderWidth = data.borderStyle.width;\n    const square = this.#square = this.svgFactory.createElement(\"svg:rect\");\n    square.setAttribute(\"x\", borderWidth / 2);\n    square.setAttribute(\"y\", borderWidth / 2);\n    square.setAttribute(\"width\", width - borderWidth);\n    square.setAttribute(\"height\", height - borderWidth);\n    square.setAttribute(\"stroke-width\", borderWidth || 1);\n    square.setAttribute(\"stroke\", \"transparent\");\n    square.setAttribute(\"fill\", \"transparent\");\n    svg.append(square);\n    this.container.append(svg);\n    if (!data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    }\n    return this.container;\n  }\n  getElementsToTriggerPopup() {\n    return this.#square;\n  }\n  addHighlightArea() {\n    this.container.classList.add(\"highlightArea\");\n  }\n}\nclass CircleAnnotationElement extends AnnotationElement {\n  #circle = null;\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: true\n    });\n  }\n  render() {\n    this.container.classList.add(\"circleAnnotation\");\n    const data = this.data;\n    const {\n      width,\n      height\n    } = getRectDims(data.rect);\n    const svg = this.svgFactory.create(width, height, true);\n    const borderWidth = data.borderStyle.width;\n    const circle = this.#circle = this.svgFactory.createElement(\"svg:ellipse\");\n    circle.setAttribute(\"cx\", width / 2);\n    circle.setAttribute(\"cy\", height / 2);\n    circle.setAttribute(\"rx\", width / 2 - borderWidth / 2);\n    circle.setAttribute(\"ry\", height / 2 - borderWidth / 2);\n    circle.setAttribute(\"stroke-width\", borderWidth || 1);\n    circle.setAttribute(\"stroke\", \"transparent\");\n    circle.setAttribute(\"fill\", \"transparent\");\n    svg.append(circle);\n    this.container.append(svg);\n    if (!data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    }\n    return this.container;\n  }\n  getElementsToTriggerPopup() {\n    return this.#circle;\n  }\n  addHighlightArea() {\n    this.container.classList.add(\"highlightArea\");\n  }\n}\nclass PolylineAnnotationElement extends AnnotationElement {\n  #polyline = null;\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: true\n    });\n    this.containerClassName = \"polylineAnnotation\";\n    this.svgElementName = \"svg:polyline\";\n  }\n  render() {\n    this.container.classList.add(this.containerClassName);\n    const data = this.data;\n    const {\n      width,\n      height\n    } = getRectDims(data.rect);\n    const svg = this.svgFactory.create(width, height, true);\n    let points = [];\n    for (const coordinate of data.vertices) {\n      const x = coordinate.x - data.rect[0];\n      const y = data.rect[3] - coordinate.y;\n      points.push(x + \",\" + y);\n    }\n    points = points.join(\" \");\n    const polyline = this.#polyline = this.svgFactory.createElement(this.svgElementName);\n    polyline.setAttribute(\"points\", points);\n    polyline.setAttribute(\"stroke-width\", data.borderStyle.width || 1);\n    polyline.setAttribute(\"stroke\", \"transparent\");\n    polyline.setAttribute(\"fill\", \"transparent\");\n    svg.append(polyline);\n    this.container.append(svg);\n    if (!data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    }\n    return this.container;\n  }\n  getElementsToTriggerPopup() {\n    return this.#polyline;\n  }\n  addHighlightArea() {\n    this.container.classList.add(\"highlightArea\");\n  }\n}\nclass PolygonAnnotationElement extends PolylineAnnotationElement {\n  constructor(parameters) {\n    super(parameters);\n    this.containerClassName = \"polygonAnnotation\";\n    this.svgElementName = \"svg:polygon\";\n  }\n}\nclass CaretAnnotationElement extends AnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: true\n    });\n  }\n  render() {\n    this.container.classList.add(\"caretAnnotation\");\n    if (!this.data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    }\n    return this.container;\n  }\n}\nclass InkAnnotationElement extends AnnotationElement {\n  #polylines = [];\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: true\n    });\n    this.containerClassName = \"inkAnnotation\";\n    this.svgElementName = \"svg:polyline\";\n    this.annotationEditorType = util.AnnotationEditorType.INK;\n  }\n  render() {\n    this.container.classList.add(this.containerClassName);\n    const data = this.data;\n    const {\n      width,\n      height\n    } = getRectDims(data.rect);\n    const svg = this.svgFactory.create(width, height, true);\n    for (const inkList of data.inkLists) {\n      let points = [];\n      for (const coordinate of inkList) {\n        const x = coordinate.x - data.rect[0];\n        const y = data.rect[3] - coordinate.y;\n        points.push(`${x},${y}`);\n      }\n      points = points.join(\" \");\n      const polyline = this.svgFactory.createElement(this.svgElementName);\n      this.#polylines.push(polyline);\n      polyline.setAttribute(\"points\", points);\n      polyline.setAttribute(\"stroke-width\", data.borderStyle.width || 1);\n      polyline.setAttribute(\"stroke\", \"transparent\");\n      polyline.setAttribute(\"fill\", \"transparent\");\n      if (!data.popupRef && this.hasPopupData) {\n        this._createPopup();\n      }\n      svg.append(polyline);\n    }\n    this.container.append(svg);\n    return this.container;\n  }\n  getElementsToTriggerPopup() {\n    return this.#polylines;\n  }\n  addHighlightArea() {\n    this.container.classList.add(\"highlightArea\");\n  }\n}\nclass HighlightAnnotationElement extends AnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: true,\n      createQuadrilaterals: true\n    });\n  }\n  render() {\n    if (!this.data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    }\n    this.container.classList.add(\"highlightAnnotation\");\n    return this.container;\n  }\n}\nclass UnderlineAnnotationElement extends AnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: true,\n      createQuadrilaterals: true\n    });\n  }\n  render() {\n    if (!this.data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    }\n    this.container.classList.add(\"underlineAnnotation\");\n    return this.container;\n  }\n}\nclass SquigglyAnnotationElement extends AnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: true,\n      createQuadrilaterals: true\n    });\n  }\n  render() {\n    if (!this.data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    }\n    this.container.classList.add(\"squigglyAnnotation\");\n    return this.container;\n  }\n}\nclass StrikeOutAnnotationElement extends AnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: true,\n      createQuadrilaterals: true\n    });\n  }\n  render() {\n    if (!this.data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    }\n    this.container.classList.add(\"strikeoutAnnotation\");\n    return this.container;\n  }\n}\nclass StampAnnotationElement extends AnnotationElement {\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true,\n      ignoreBorder: true\n    });\n  }\n  render() {\n    this.container.classList.add(\"stampAnnotation\");\n    if (!this.data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    }\n    return this.container;\n  }\n}\nclass FileAttachmentAnnotationElement extends AnnotationElement {\n  #trigger = null;\n  constructor(parameters) {\n    super(parameters, {\n      isRenderable: true\n    });\n    const {\n      filename,\n      content\n    } = this.data.file;\n    this.filename = (0,display_utils.getFilenameFromUrl)(filename, true);\n    this.content = content;\n    this.linkService.eventBus?.dispatch(\"fileattachmentannotation\", {\n      source: this,\n      filename,\n      content\n    });\n  }\n  render() {\n    this.container.classList.add(\"fileAttachmentAnnotation\");\n    const {\n      container,\n      data\n    } = this;\n    let trigger;\n    if (data.hasAppearance || data.fillAlpha === 0) {\n      trigger = document.createElement(\"div\");\n    } else {\n      trigger = document.createElement(\"img\");\n      trigger.src = `${this.imageResourcesPath}annotation-${/paperclip/i.test(data.name) ? \"paperclip\" : \"pushpin\"}.svg`;\n      if (data.fillAlpha && data.fillAlpha < 1) {\n        trigger.style = `filter: opacity(${Math.round(data.fillAlpha * 100)}%);`;\n      }\n    }\n    trigger.addEventListener(\"dblclick\", this.#download.bind(this));\n    this.#trigger = trigger;\n    const {\n      isMac\n    } = util.FeatureTest.platform;\n    container.addEventListener(\"keydown\", evt => {\n      if (evt.key === \"Enter\" && (isMac ? evt.metaKey : evt.ctrlKey)) {\n        this.#download();\n      }\n    });\n    if (!data.popupRef && this.hasPopupData) {\n      this._createPopup();\n    } else {\n      trigger.classList.add(\"popupTriggerArea\");\n    }\n    container.append(trigger);\n    return container;\n  }\n  getElementsToTriggerPopup() {\n    return this.#trigger;\n  }\n  addHighlightArea() {\n    this.container.classList.add(\"highlightArea\");\n  }\n  #download() {\n    this.downloadManager?.openOrDownloadData(this.content, this.filename);\n  }\n}\nclass AnnotationLayer {\n  #accessibilityManager = null;\n  #annotationCanvasMap = null;\n  #editableAnnotations = new Map();\n  constructor({\n    div,\n    accessibilityManager,\n    annotationCanvasMap,\n    page,\n    viewport\n  }) {\n    this.div = div;\n    this.#accessibilityManager = accessibilityManager;\n    this.#annotationCanvasMap = annotationCanvasMap;\n    this.page = page;\n    this.viewport = viewport;\n    this.zIndex = 0;\n  }\n  #appendElement(element, id) {\n    const contentElement = element.firstChild || element;\n    contentElement.id = `${util.AnnotationPrefix}${id}`;\n    this.div.append(element);\n    this.#accessibilityManager?.moveElementInDOM(this.div, element, contentElement, false);\n  }\n  async render(params) {\n    const {\n      annotations\n    } = params;\n    const layer = this.div;\n    (0,display_utils.setLayerDimensions)(layer, this.viewport);\n    const popupToElements = new Map();\n    const elementParams = {\n      data: null,\n      layer,\n      linkService: params.linkService,\n      downloadManager: params.downloadManager,\n      imageResourcesPath: params.imageResourcesPath || \"\",\n      renderForms: params.renderForms !== false,\n      svgFactory: new display_utils.DOMSVGFactory(),\n      annotationStorage: params.annotationStorage || new annotation_storage.AnnotationStorage(),\n      enableScripting: params.enableScripting === true,\n      hasJSActions: params.hasJSActions,\n      fieldObjects: params.fieldObjects,\n      parent: this,\n      elements: null\n    };\n    for (const data of annotations) {\n      if (data.noHTML) {\n        continue;\n      }\n      const isPopupAnnotation = data.annotationType === util.AnnotationType.POPUP;\n      if (!isPopupAnnotation) {\n        const {\n          width,\n          height\n        } = getRectDims(data.rect);\n        if (width <= 0 || height <= 0) {\n          continue;\n        }\n      } else {\n        const elements = popupToElements.get(data.id);\n        if (!elements) {\n          continue;\n        }\n        elementParams.elements = elements;\n      }\n      elementParams.data = data;\n      const element = AnnotationElementFactory.create(elementParams);\n      if (!element.isRenderable) {\n        continue;\n      }\n      if (!isPopupAnnotation && data.popupRef) {\n        const elements = popupToElements.get(data.popupRef);\n        if (!elements) {\n          popupToElements.set(data.popupRef, [element]);\n        } else {\n          elements.push(element);\n        }\n      }\n      if (element.annotationEditorType > 0) {\n        this.#editableAnnotations.set(element.data.id, element);\n      }\n      const rendered = element.render();\n      if (data.hidden) {\n        rendered.style.visibility = \"hidden\";\n      }\n      this.#appendElement(rendered, data.id);\n    }\n    this.#setAnnotationCanvasMap();\n  }\n  update({\n    viewport\n  }) {\n    const layer = this.div;\n    this.viewport = viewport;\n    (0,display_utils.setLayerDimensions)(layer, {\n      rotation: viewport.rotation\n    });\n    this.#setAnnotationCanvasMap();\n    layer.hidden = false;\n  }\n  #setAnnotationCanvasMap() {\n    if (!this.#annotationCanvasMap) {\n      return;\n    }\n    const layer = this.div;\n    for (const [id, canvas] of this.#annotationCanvasMap) {\n      const element = layer.querySelector(`[data-annotation-id=\"${id}\"]`);\n      if (!element) {\n        continue;\n      }\n      const {\n        firstChild\n      } = element;\n      if (!firstChild) {\n        element.append(canvas);\n      } else if (firstChild.nodeName === \"CANVAS\") {\n        firstChild.replaceWith(canvas);\n      } else {\n        firstChild.before(canvas);\n      }\n    }\n    this.#annotationCanvasMap.clear();\n  }\n  getEditableAnnotations() {\n    return Array.from(this.#editableAnnotations.values());\n  }\n  getEditableAnnotation(id) {\n    return this.#editableAnnotations.get(id);\n  }\n}\n\n\n/***/ }),\n\n/***/ 780:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   AnnotationStorage: () => (/* binding */ AnnotationStorage),\n/* harmony export */   PrintAnnotationStorage: () => (/* binding */ PrintAnnotationStorage),\n/* harmony export */   SerializableEmpty: () => (/* binding */ SerializableEmpty)\n/* harmony export */ });\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n/* harmony import */ var _editor_editor_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(796);\n/* harmony import */ var _shared_murmurhash3_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(825);\n\n\n\nconst SerializableEmpty = Object.freeze({\n  map: null,\n  hash: \"\",\n  transfer: undefined\n});\nclass AnnotationStorage {\n  #modified = false;\n  #storage = new Map();\n  constructor() {\n    this.onSetModified = null;\n    this.onResetModified = null;\n    this.onAnnotationEditor = null;\n  }\n  getValue(key, defaultValue) {\n    const value = this.#storage.get(key);\n    if (value === undefined) {\n      return defaultValue;\n    }\n    return Object.assign(defaultValue, value);\n  }\n  getRawValue(key) {\n    return this.#storage.get(key);\n  }\n  remove(key) {\n    this.#storage.delete(key);\n    if (this.#storage.size === 0) {\n      this.resetModified();\n    }\n    if (typeof this.onAnnotationEditor === \"function\") {\n      for (const value of this.#storage.values()) {\n        if (value instanceof _editor_editor_js__WEBPACK_IMPORTED_MODULE_1__.AnnotationEditor) {\n          return;\n        }\n      }\n      this.onAnnotationEditor(null);\n    }\n  }\n  setValue(key, value) {\n    const obj = this.#storage.get(key);\n    let modified = false;\n    if (obj !== undefined) {\n      for (const [entry, val] of Object.entries(value)) {\n        if (obj[entry] !== val) {\n          modified = true;\n          obj[entry] = val;\n        }\n      }\n    } else {\n      modified = true;\n      this.#storage.set(key, value);\n    }\n    if (modified) {\n      this.#setModified();\n    }\n    if (value instanceof _editor_editor_js__WEBPACK_IMPORTED_MODULE_1__.AnnotationEditor && typeof this.onAnnotationEditor === \"function\") {\n      this.onAnnotationEditor(value.constructor._type);\n    }\n  }\n  has(key) {\n    return this.#storage.has(key);\n  }\n  getAll() {\n    return this.#storage.size > 0 ? (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.objectFromMap)(this.#storage) : null;\n  }\n  setAll(obj) {\n    for (const [key, val] of Object.entries(obj)) {\n      this.setValue(key, val);\n    }\n  }\n  get size() {\n    return this.#storage.size;\n  }\n  #setModified() {\n    if (!this.#modified) {\n      this.#modified = true;\n      if (typeof this.onSetModified === \"function\") {\n        this.onSetModified();\n      }\n    }\n  }\n  resetModified() {\n    if (this.#modified) {\n      this.#modified = false;\n      if (typeof this.onResetModified === \"function\") {\n        this.onResetModified();\n      }\n    }\n  }\n  get print() {\n    return new PrintAnnotationStorage(this);\n  }\n  get serializable() {\n    if (this.#storage.size === 0) {\n      return SerializableEmpty;\n    }\n    const map = new Map(),\n      hash = new _shared_murmurhash3_js__WEBPACK_IMPORTED_MODULE_2__.MurmurHash3_64(),\n      transfer = [];\n    const context = Object.create(null);\n    let hasBitmap = false;\n    for (const [key, val] of this.#storage) {\n      const serialized = val instanceof _editor_editor_js__WEBPACK_IMPORTED_MODULE_1__.AnnotationEditor ? val.serialize(false, context) : val;\n      if (serialized) {\n        map.set(key, serialized);\n        hash.update(`${key}:${JSON.stringify(serialized)}`);\n        hasBitmap ||= !!serialized.bitmap;\n      }\n    }\n    if (hasBitmap) {\n      for (const value of map.values()) {\n        if (value.bitmap) {\n          transfer.push(value.bitmap);\n        }\n      }\n    }\n    return map.size > 0 ? {\n      map,\n      hash: hash.hexdigest(),\n      transfer\n    } : SerializableEmpty;\n  }\n}\nclass PrintAnnotationStorage extends AnnotationStorage {\n  #serializable;\n  constructor(parent) {\n    super();\n    const {\n      map,\n      hash,\n      transfer\n    } = parent.serializable;\n    const clone = structuredClone(map, transfer ? {\n      transfer\n    } : null);\n    this.#serializable = {\n      map: clone,\n      hash,\n      transfer\n    };\n  }\n  get print() {\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Should not call PrintAnnotationStorage.print\");\n  }\n  get serializable() {\n    return this.#serializable;\n  }\n}\n\n\n/***/ }),\n\n/***/ 406:\n/***/ ((__webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n__webpack_require__.a(__webpack_module__, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try {\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   PDFDataRangeTransport: () => (/* binding */ PDFDataRangeTransport),\n/* harmony export */   PDFWorker: () => (/* binding */ PDFWorker),\n/* harmony export */   build: () => (/* binding */ build),\n/* harmony export */   getDocument: () => (/* binding */ getDocument),\n/* harmony export */   version: () => (/* binding */ version)\n/* harmony export */ });\n/* unused harmony exports DefaultCanvasFactory, DefaultCMapReaderFactory, DefaultFilterFactory, DefaultStandardFontDataFactory, LoopbackPort, PDFDocumentLoadingTask, PDFDocumentProxy, PDFPageProxy, PDFWorkerUtil, RenderTask */\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n/* harmony import */ var _annotation_storage_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(780);\n/* harmony import */ var _display_utils_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(473);\n/* harmony import */ var _font_loader_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(742);\n/* harmony import */ var display_node_utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(738);\n/* harmony import */ var _canvas_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(250);\n/* harmony import */ var _worker_options_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(368);\n/* harmony import */ var _shared_message_handler_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(694);\n/* harmony import */ var _metadata_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(472);\n/* harmony import */ var _optional_content_config_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(890);\n/* harmony import */ var _transport_stream_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(92);\n/* harmony import */ var display_fetch_stream__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(171);\n/* harmony import */ var display_network__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(474);\n/* harmony import */ var display_node_stream__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(498);\n/* harmony import */ var _xfa_text_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(521);\nvar __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([display_node_utils__WEBPACK_IMPORTED_MODULE_4__, display_node_stream__WEBPACK_IMPORTED_MODULE_13__]);\n([display_node_utils__WEBPACK_IMPORTED_MODULE_4__, display_node_stream__WEBPACK_IMPORTED_MODULE_13__] = __webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__);\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nconst DEFAULT_RANGE_CHUNK_SIZE = 65536;\nconst RENDERING_CANCELLED_TIMEOUT = 100;\nconst DELAYED_CLEANUP_TIMEOUT = 5000;\nconst DefaultCanvasFactory = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS ? display_node_utils__WEBPACK_IMPORTED_MODULE_4__.NodeCanvasFactory : _display_utils_js__WEBPACK_IMPORTED_MODULE_2__.DOMCanvasFactory;\nconst DefaultCMapReaderFactory = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS ? display_node_utils__WEBPACK_IMPORTED_MODULE_4__.NodeCMapReaderFactory : _display_utils_js__WEBPACK_IMPORTED_MODULE_2__.DOMCMapReaderFactory;\nconst DefaultFilterFactory = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS ? display_node_utils__WEBPACK_IMPORTED_MODULE_4__.NodeFilterFactory : _display_utils_js__WEBPACK_IMPORTED_MODULE_2__.DOMFilterFactory;\nconst DefaultStandardFontDataFactory = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS ? display_node_utils__WEBPACK_IMPORTED_MODULE_4__.NodeStandardFontDataFactory : _display_utils_js__WEBPACK_IMPORTED_MODULE_2__.DOMStandardFontDataFactory;\nfunction getDocument(src) {\n  if (typeof src === \"string\" || src instanceof URL) {\n    src = {\n      url: src\n    };\n  } else if ((0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isArrayBuffer)(src)) {\n    src = {\n      data: src\n    };\n  }\n  if (typeof src !== \"object\") {\n    throw new Error(\"Invalid parameter in getDocument, need parameter object.\");\n  }\n  if (!src.url && !src.data && !src.range) {\n    throw new Error(\"Invalid parameter object: need either .data, .range or .url\");\n  }\n  const task = new PDFDocumentLoadingTask();\n  const {\n    docId\n  } = task;\n  const url = src.url ? getUrlProp(src.url) : null;\n  const data = src.data ? getDataProp(src.data) : null;\n  const httpHeaders = src.httpHeaders || null;\n  const withCredentials = src.withCredentials === true;\n  const password = src.password ?? null;\n  const rangeTransport = src.range instanceof PDFDataRangeTransport ? src.range : null;\n  const rangeChunkSize = Number.isInteger(src.rangeChunkSize) && src.rangeChunkSize > 0 ? src.rangeChunkSize : DEFAULT_RANGE_CHUNK_SIZE;\n  let worker = src.worker instanceof PDFWorker ? src.worker : null;\n  const verbosity = src.verbosity;\n  const docBaseUrl = typeof src.docBaseUrl === \"string\" && !(0,_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.isDataScheme)(src.docBaseUrl) ? src.docBaseUrl : null;\n  const cMapUrl = typeof src.cMapUrl === \"string\" ? src.cMapUrl : null;\n  const cMapPacked = src.cMapPacked !== false;\n  const CMapReaderFactory = src.CMapReaderFactory || DefaultCMapReaderFactory;\n  const standardFontDataUrl = typeof src.standardFontDataUrl === \"string\" ? src.standardFontDataUrl : null;\n  const StandardFontDataFactory = src.StandardFontDataFactory || DefaultStandardFontDataFactory;\n  const ignoreErrors = src.stopAtErrors !== true;\n  const maxImageSize = Number.isInteger(src.maxImageSize) && src.maxImageSize > -1 ? src.maxImageSize : -1;\n  const isEvalSupported = src.isEvalSupported !== false;\n  const isOffscreenCanvasSupported = typeof src.isOffscreenCanvasSupported === \"boolean\" ? src.isOffscreenCanvasSupported : !_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS;\n  const canvasMaxAreaInBytes = Number.isInteger(src.canvasMaxAreaInBytes) ? src.canvasMaxAreaInBytes : -1;\n  const disableFontFace = typeof src.disableFontFace === \"boolean\" ? src.disableFontFace : _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS;\n  const fontExtraProperties = src.fontExtraProperties === true;\n  const enableXfa = src.enableXfa === true;\n  const ownerDocument = src.ownerDocument || globalThis.document;\n  const disableRange = src.disableRange === true;\n  const disableStream = src.disableStream === true;\n  const disableAutoFetch = src.disableAutoFetch === true;\n  const pdfBug = src.pdfBug === true;\n  const length = rangeTransport ? rangeTransport.length : src.length ?? NaN;\n  const useSystemFonts = typeof src.useSystemFonts === \"boolean\" ? src.useSystemFonts : !_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS && !disableFontFace;\n  const useWorkerFetch = typeof src.useWorkerFetch === \"boolean\" ? src.useWorkerFetch : CMapReaderFactory === _display_utils_js__WEBPACK_IMPORTED_MODULE_2__.DOMCMapReaderFactory && StandardFontDataFactory === _display_utils_js__WEBPACK_IMPORTED_MODULE_2__.DOMStandardFontDataFactory && cMapUrl && standardFontDataUrl && (0,_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.isValidFetchUrl)(cMapUrl, document.baseURI) && (0,_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.isValidFetchUrl)(standardFontDataUrl, document.baseURI);\n  const canvasFactory = src.canvasFactory || new DefaultCanvasFactory({\n    ownerDocument\n  });\n  const filterFactory = src.filterFactory || new DefaultFilterFactory({\n    docId,\n    ownerDocument\n  });\n  const styleElement = null;\n  (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.setVerbosityLevel)(verbosity);\n  const transportFactory = {\n    canvasFactory,\n    filterFactory\n  };\n  if (!useWorkerFetch) {\n    transportFactory.cMapReaderFactory = new CMapReaderFactory({\n      baseUrl: cMapUrl,\n      isCompressed: cMapPacked\n    });\n    transportFactory.standardFontDataFactory = new StandardFontDataFactory({\n      baseUrl: standardFontDataUrl\n    });\n  }\n  if (!worker) {\n    const workerParams = {\n      verbosity,\n      port: _worker_options_js__WEBPACK_IMPORTED_MODULE_6__.GlobalWorkerOptions.workerPort\n    };\n    worker = workerParams.port ? PDFWorker.fromPort(workerParams) : new PDFWorker(workerParams);\n    task._worker = worker;\n  }\n  const fetchDocParams = {\n    docId,\n    apiVersion: '4.0.269',\n    data,\n    password,\n    disableAutoFetch,\n    rangeChunkSize,\n    length,\n    docBaseUrl,\n    enableXfa,\n    evaluatorOptions: {\n      maxImageSize,\n      disableFontFace,\n      ignoreErrors,\n      isEvalSupported,\n      isOffscreenCanvasSupported,\n      canvasMaxAreaInBytes,\n      fontExtraProperties,\n      useSystemFonts,\n      cMapUrl: useWorkerFetch ? cMapUrl : null,\n      standardFontDataUrl: useWorkerFetch ? standardFontDataUrl : null\n    }\n  };\n  const transportParams = {\n    ignoreErrors,\n    isEvalSupported,\n    disableFontFace,\n    fontExtraProperties,\n    enableXfa,\n    ownerDocument,\n    disableAutoFetch,\n    pdfBug,\n    styleElement\n  };\n  worker.promise.then(function () {\n    if (task.destroyed) {\n      throw new Error(\"Loading aborted\");\n    }\n    const workerIdPromise = _fetchDocument(worker, fetchDocParams);\n    const networkStreamPromise = new Promise(function (resolve) {\n      let networkStream;\n      if (rangeTransport) {\n        networkStream = new _transport_stream_js__WEBPACK_IMPORTED_MODULE_10__.PDFDataTransportStream({\n          length,\n          initialData: rangeTransport.initialData,\n          progressiveDone: rangeTransport.progressiveDone,\n          contentDispositionFilename: rangeTransport.contentDispositionFilename,\n          disableRange,\n          disableStream\n        }, rangeTransport);\n      } else if (!data) {\n        const createPDFNetworkStream = params => {\n          if (_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS) {\n            return new display_node_stream__WEBPACK_IMPORTED_MODULE_13__.PDFNodeStream(params);\n          }\n          return (0,_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.isValidFetchUrl)(params.url) ? new display_fetch_stream__WEBPACK_IMPORTED_MODULE_11__.PDFFetchStream(params) : new display_network__WEBPACK_IMPORTED_MODULE_12__.PDFNetworkStream(params);\n        };\n        networkStream = createPDFNetworkStream({\n          url,\n          length,\n          httpHeaders,\n          withCredentials,\n          rangeChunkSize,\n          disableRange,\n          disableStream\n        });\n      }\n      resolve(networkStream);\n    });\n    return Promise.all([workerIdPromise, networkStreamPromise]).then(function ([workerId, networkStream]) {\n      if (task.destroyed) {\n        throw new Error(\"Loading aborted\");\n      }\n      const messageHandler = new _shared_message_handler_js__WEBPACK_IMPORTED_MODULE_7__.MessageHandler(docId, workerId, worker.port);\n      const transport = new WorkerTransport(messageHandler, task, networkStream, transportParams, transportFactory);\n      task._transport = transport;\n      messageHandler.send(\"Ready\", null);\n    });\n  }).catch(task._capability.reject);\n  return task;\n}\nasync function _fetchDocument(worker, source) {\n  if (worker.destroyed) {\n    throw new Error(\"Worker was destroyed\");\n  }\n  const workerId = await worker.messageHandler.sendWithPromise(\"GetDocRequest\", source, source.data ? [source.data.buffer] : null);\n  if (worker.destroyed) {\n    throw new Error(\"Worker was destroyed\");\n  }\n  return workerId;\n}\nfunction getUrlProp(val) {\n  if (val instanceof URL) {\n    return val.href;\n  }\n  try {\n    return new URL(val, window.location).href;\n  } catch {\n    if (_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS && typeof val === \"string\") {\n      return val;\n    }\n  }\n  throw new Error(\"Invalid PDF url data: \" + \"either string or URL-object is expected in the url property.\");\n}\nfunction getDataProp(val) {\n  if (_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS && typeof Buffer !== \"undefined\" && val instanceof Buffer) {\n    throw new Error(\"Please provide binary data as `Uint8Array`, rather than `Buffer`.\");\n  }\n  if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) {\n    return val;\n  }\n  if (typeof val === \"string\") {\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.stringToBytes)(val);\n  }\n  if (typeof val === \"object\" && !isNaN(val?.length) || (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isArrayBuffer)(val)) {\n    return new Uint8Array(val);\n  }\n  throw new Error(\"Invalid PDF binary data: either TypedArray, \" + \"string, or array-like object is expected in the data property.\");\n}\nclass PDFDocumentLoadingTask {\n  static #docId = 0;\n  constructor() {\n    this._capability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this._transport = null;\n    this._worker = null;\n    this.docId = `d${PDFDocumentLoadingTask.#docId++}`;\n    this.destroyed = false;\n    this.onPassword = null;\n    this.onProgress = null;\n  }\n  get promise() {\n    return this._capability.promise;\n  }\n  async destroy() {\n    this.destroyed = true;\n    try {\n      if (this._worker?.port) {\n        this._worker._pendingDestroy = true;\n      }\n      await this._transport?.destroy();\n    } catch (ex) {\n      if (this._worker?.port) {\n        delete this._worker._pendingDestroy;\n      }\n      throw ex;\n    }\n    this._transport = null;\n    if (this._worker) {\n      this._worker.destroy();\n      this._worker = null;\n    }\n  }\n}\nclass PDFDataRangeTransport {\n  constructor(length, initialData, progressiveDone = false, contentDispositionFilename = null) {\n    this.length = length;\n    this.initialData = initialData;\n    this.progressiveDone = progressiveDone;\n    this.contentDispositionFilename = contentDispositionFilename;\n    this._rangeListeners = [];\n    this._progressListeners = [];\n    this._progressiveReadListeners = [];\n    this._progressiveDoneListeners = [];\n    this._readyCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n  }\n  addRangeListener(listener) {\n    this._rangeListeners.push(listener);\n  }\n  addProgressListener(listener) {\n    this._progressListeners.push(listener);\n  }\n  addProgressiveReadListener(listener) {\n    this._progressiveReadListeners.push(listener);\n  }\n  addProgressiveDoneListener(listener) {\n    this._progressiveDoneListeners.push(listener);\n  }\n  onDataRange(begin, chunk) {\n    for (const listener of this._rangeListeners) {\n      listener(begin, chunk);\n    }\n  }\n  onDataProgress(loaded, total) {\n    this._readyCapability.promise.then(() => {\n      for (const listener of this._progressListeners) {\n        listener(loaded, total);\n      }\n    });\n  }\n  onDataProgressiveRead(chunk) {\n    this._readyCapability.promise.then(() => {\n      for (const listener of this._progressiveReadListeners) {\n        listener(chunk);\n      }\n    });\n  }\n  onDataProgressiveDone() {\n    this._readyCapability.promise.then(() => {\n      for (const listener of this._progressiveDoneListeners) {\n        listener();\n      }\n    });\n  }\n  transportReady() {\n    this._readyCapability.resolve();\n  }\n  requestDataRange(begin, end) {\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Abstract method PDFDataRangeTransport.requestDataRange\");\n  }\n  abort() {}\n}\nclass PDFDocumentProxy {\n  constructor(pdfInfo, transport) {\n    this._pdfInfo = pdfInfo;\n    this._transport = transport;\n  }\n  get annotationStorage() {\n    return this._transport.annotationStorage;\n  }\n  get filterFactory() {\n    return this._transport.filterFactory;\n  }\n  get numPages() {\n    return this._pdfInfo.numPages;\n  }\n  get fingerprints() {\n    return this._pdfInfo.fingerprints;\n  }\n  get isPureXfa() {\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"isPureXfa\", !!this._transport._htmlForXfa);\n  }\n  get allXfaHtml() {\n    return this._transport._htmlForXfa;\n  }\n  getPage(pageNumber) {\n    return this._transport.getPage(pageNumber);\n  }\n  getPageIndex(ref) {\n    return this._transport.getPageIndex(ref);\n  }\n  getDestinations() {\n    return this._transport.getDestinations();\n  }\n  getDestination(id) {\n    return this._transport.getDestination(id);\n  }\n  getPageLabels() {\n    return this._transport.getPageLabels();\n  }\n  getPageLayout() {\n    return this._transport.getPageLayout();\n  }\n  getPageMode() {\n    return this._transport.getPageMode();\n  }\n  getViewerPreferences() {\n    return this._transport.getViewerPreferences();\n  }\n  getOpenAction() {\n    return this._transport.getOpenAction();\n  }\n  getAttachments() {\n    return this._transport.getAttachments();\n  }\n  getJSActions() {\n    return this._transport.getDocJSActions();\n  }\n  getOutline() {\n    return this._transport.getOutline();\n  }\n  getOptionalContentConfig() {\n    return this._transport.getOptionalContentConfig();\n  }\n  getPermissions() {\n    return this._transport.getPermissions();\n  }\n  getMetadata() {\n    return this._transport.getMetadata();\n  }\n  getMarkInfo() {\n    return this._transport.getMarkInfo();\n  }\n  getData() {\n    return this._transport.getData();\n  }\n  saveDocument() {\n    return this._transport.saveDocument();\n  }\n  getDownloadInfo() {\n    return this._transport.downloadInfoCapability.promise;\n  }\n  cleanup(keepLoadedFonts = false) {\n    return this._transport.startCleanup(keepLoadedFonts || this.isPureXfa);\n  }\n  destroy() {\n    return this.loadingTask.destroy();\n  }\n  get loadingParams() {\n    return this._transport.loadingParams;\n  }\n  get loadingTask() {\n    return this._transport.loadingTask;\n  }\n  getFieldObjects() {\n    return this._transport.getFieldObjects();\n  }\n  hasJSActions() {\n    return this._transport.hasJSActions();\n  }\n  getCalculationOrderIds() {\n    return this._transport.getCalculationOrderIds();\n  }\n}\nclass PDFPageProxy {\n  #delayedCleanupTimeout = null;\n  #pendingCleanup = false;\n  constructor(pageIndex, pageInfo, transport, pdfBug = false) {\n    this._pageIndex = pageIndex;\n    this._pageInfo = pageInfo;\n    this._transport = transport;\n    this._stats = pdfBug ? new _display_utils_js__WEBPACK_IMPORTED_MODULE_2__.StatTimer() : null;\n    this._pdfBug = pdfBug;\n    this.commonObjs = transport.commonObjs;\n    this.objs = new PDFObjects();\n    this._maybeCleanupAfterRender = false;\n    this._intentStates = new Map();\n    this.destroyed = false;\n  }\n  get pageNumber() {\n    return this._pageIndex + 1;\n  }\n  get rotate() {\n    return this._pageInfo.rotate;\n  }\n  get ref() {\n    return this._pageInfo.ref;\n  }\n  get userUnit() {\n    return this._pageInfo.userUnit;\n  }\n  get view() {\n    return this._pageInfo.view;\n  }\n  getViewport({\n    scale,\n    rotation = this.rotate,\n    offsetX = 0,\n    offsetY = 0,\n    dontFlip = false\n  } = {}) {\n    return new _display_utils_js__WEBPACK_IMPORTED_MODULE_2__.PageViewport({\n      viewBox: this.view,\n      scale,\n      rotation,\n      offsetX,\n      offsetY,\n      dontFlip\n    });\n  }\n  getAnnotations({\n    intent = \"display\"\n  } = {}) {\n    const intentArgs = this._transport.getRenderingIntent(intent);\n    return this._transport.getAnnotations(this._pageIndex, intentArgs.renderingIntent);\n  }\n  getJSActions() {\n    return this._transport.getPageJSActions(this._pageIndex);\n  }\n  get filterFactory() {\n    return this._transport.filterFactory;\n  }\n  get isPureXfa() {\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"isPureXfa\", !!this._transport._htmlForXfa);\n  }\n  async getXfa() {\n    return this._transport._htmlForXfa?.children[this._pageIndex] || null;\n  }\n  render({\n    canvasContext,\n    viewport,\n    intent = \"display\",\n    annotationMode = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationMode.ENABLE,\n    transform = null,\n    background = null,\n    optionalContentConfigPromise = null,\n    annotationCanvasMap = null,\n    pageColors = null,\n    printAnnotationStorage = null\n  }) {\n    this._stats?.time(\"Overall\");\n    const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage);\n    this.#pendingCleanup = false;\n    this.#abortDelayedCleanup();\n    if (!optionalContentConfigPromise) {\n      optionalContentConfigPromise = this._transport.getOptionalContentConfig();\n    }\n    let intentState = this._intentStates.get(intentArgs.cacheKey);\n    if (!intentState) {\n      intentState = Object.create(null);\n      this._intentStates.set(intentArgs.cacheKey, intentState);\n    }\n    if (intentState.streamReaderCancelTimeout) {\n      clearTimeout(intentState.streamReaderCancelTimeout);\n      intentState.streamReaderCancelTimeout = null;\n    }\n    const intentPrint = !!(intentArgs.renderingIntent & _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.RenderingIntentFlag.PRINT);\n    if (!intentState.displayReadyCapability) {\n      intentState.displayReadyCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n      intentState.operatorList = {\n        fnArray: [],\n        argsArray: [],\n        lastChunk: false,\n        separateAnnots: null\n      };\n      this._stats?.time(\"Page Request\");\n      this._pumpOperatorList(intentArgs);\n    }\n    const complete = error => {\n      intentState.renderTasks.delete(internalRenderTask);\n      if (this._maybeCleanupAfterRender || intentPrint) {\n        this.#pendingCleanup = true;\n      }\n      this.#tryCleanup(!intentPrint);\n      if (error) {\n        internalRenderTask.capability.reject(error);\n        this._abortOperatorList({\n          intentState,\n          reason: error instanceof Error ? error : new Error(error)\n        });\n      } else {\n        internalRenderTask.capability.resolve();\n      }\n      this._stats?.timeEnd(\"Rendering\");\n      this._stats?.timeEnd(\"Overall\");\n    };\n    const internalRenderTask = new InternalRenderTask({\n      callback: complete,\n      params: {\n        canvasContext,\n        viewport,\n        transform,\n        background\n      },\n      objs: this.objs,\n      commonObjs: this.commonObjs,\n      annotationCanvasMap,\n      operatorList: intentState.operatorList,\n      pageIndex: this._pageIndex,\n      canvasFactory: this._transport.canvasFactory,\n      filterFactory: this._transport.filterFactory,\n      useRequestAnimationFrame: !intentPrint,\n      pdfBug: this._pdfBug,\n      pageColors\n    });\n    (intentState.renderTasks ||= new Set()).add(internalRenderTask);\n    const renderTask = internalRenderTask.task;\n    Promise.all([intentState.displayReadyCapability.promise, optionalContentConfigPromise]).then(([transparency, optionalContentConfig]) => {\n      if (this.destroyed) {\n        complete();\n        return;\n      }\n      this._stats?.time(\"Rendering\");\n      internalRenderTask.initializeGraphics({\n        transparency,\n        optionalContentConfig\n      });\n      internalRenderTask.operatorListChanged();\n    }).catch(complete);\n    return renderTask;\n  }\n  getOperatorList({\n    intent = \"display\",\n    annotationMode = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationMode.ENABLE,\n    printAnnotationStorage = null\n  } = {}) {\n    function operatorListChanged() {\n      if (intentState.operatorList.lastChunk) {\n        intentState.opListReadCapability.resolve(intentState.operatorList);\n        intentState.renderTasks.delete(opListTask);\n      }\n    }\n    const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, true);\n    let intentState = this._intentStates.get(intentArgs.cacheKey);\n    if (!intentState) {\n      intentState = Object.create(null);\n      this._intentStates.set(intentArgs.cacheKey, intentState);\n    }\n    let opListTask;\n    if (!intentState.opListReadCapability) {\n      opListTask = Object.create(null);\n      opListTask.operatorListChanged = operatorListChanged;\n      intentState.opListReadCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n      (intentState.renderTasks ||= new Set()).add(opListTask);\n      intentState.operatorList = {\n        fnArray: [],\n        argsArray: [],\n        lastChunk: false,\n        separateAnnots: null\n      };\n      this._stats?.time(\"Page Request\");\n      this._pumpOperatorList(intentArgs);\n    }\n    return intentState.opListReadCapability.promise;\n  }\n  streamTextContent({\n    includeMarkedContent = false,\n    disableNormalization = false\n  } = {}) {\n    const TEXT_CONTENT_CHUNK_SIZE = 100;\n    return this._transport.messageHandler.sendWithStream(\"GetTextContent\", {\n      pageIndex: this._pageIndex,\n      includeMarkedContent: includeMarkedContent === true,\n      disableNormalization: disableNormalization === true\n    }, {\n      highWaterMark: TEXT_CONTENT_CHUNK_SIZE,\n      size(textContent) {\n        return textContent.items.length;\n      }\n    });\n  }\n  getTextContent(params = {}) {\n    if (this._transport._htmlForXfa) {\n      return this.getXfa().then(xfa => {\n        return _xfa_text_js__WEBPACK_IMPORTED_MODULE_14__.XfaText.textContent(xfa);\n      });\n    }\n    const readableStream = this.streamTextContent(params);\n    return new Promise(function (resolve, reject) {\n      function pump() {\n        reader.read().then(function ({\n          value,\n          done\n        }) {\n          if (done) {\n            resolve(textContent);\n            return;\n          }\n          Object.assign(textContent.styles, value.styles);\n          textContent.items.push(...value.items);\n          pump();\n        }, reject);\n      }\n      const reader = readableStream.getReader();\n      const textContent = {\n        items: [],\n        styles: Object.create(null)\n      };\n      pump();\n    });\n  }\n  getStructTree() {\n    return this._transport.getStructTree(this._pageIndex);\n  }\n  _destroy() {\n    this.destroyed = true;\n    const waitOn = [];\n    for (const intentState of this._intentStates.values()) {\n      this._abortOperatorList({\n        intentState,\n        reason: new Error(\"Page was destroyed.\"),\n        force: true\n      });\n      if (intentState.opListReadCapability) {\n        continue;\n      }\n      for (const internalRenderTask of intentState.renderTasks) {\n        waitOn.push(internalRenderTask.completed);\n        internalRenderTask.cancel();\n      }\n    }\n    this.objs.clear();\n    this.#pendingCleanup = false;\n    this.#abortDelayedCleanup();\n    return Promise.all(waitOn);\n  }\n  cleanup(resetStats = false) {\n    this.#pendingCleanup = true;\n    const success = this.#tryCleanup(false);\n    if (resetStats && success) {\n      this._stats &&= new _display_utils_js__WEBPACK_IMPORTED_MODULE_2__.StatTimer();\n    }\n    return success;\n  }\n  #tryCleanup(delayed = false) {\n    this.#abortDelayedCleanup();\n    if (!this.#pendingCleanup || this.destroyed) {\n      return false;\n    }\n    if (delayed) {\n      this.#delayedCleanupTimeout = setTimeout(() => {\n        this.#delayedCleanupTimeout = null;\n        this.#tryCleanup(false);\n      }, DELAYED_CLEANUP_TIMEOUT);\n      return false;\n    }\n    for (const {\n      renderTasks,\n      operatorList\n    } of this._intentStates.values()) {\n      if (renderTasks.size > 0 || !operatorList.lastChunk) {\n        return false;\n      }\n    }\n    this._intentStates.clear();\n    this.objs.clear();\n    this.#pendingCleanup = false;\n    return true;\n  }\n  #abortDelayedCleanup() {\n    if (this.#delayedCleanupTimeout) {\n      clearTimeout(this.#delayedCleanupTimeout);\n      this.#delayedCleanupTimeout = null;\n    }\n  }\n  _startRenderPage(transparency, cacheKey) {\n    const intentState = this._intentStates.get(cacheKey);\n    if (!intentState) {\n      return;\n    }\n    this._stats?.timeEnd(\"Page Request\");\n    intentState.displayReadyCapability?.resolve(transparency);\n  }\n  _renderPageChunk(operatorListChunk, intentState) {\n    for (let i = 0, ii = operatorListChunk.length; i < ii; i++) {\n      intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]);\n      intentState.operatorList.argsArray.push(operatorListChunk.argsArray[i]);\n    }\n    intentState.operatorList.lastChunk = operatorListChunk.lastChunk;\n    intentState.operatorList.separateAnnots = operatorListChunk.separateAnnots;\n    for (const internalRenderTask of intentState.renderTasks) {\n      internalRenderTask.operatorListChanged();\n    }\n    if (operatorListChunk.lastChunk) {\n      this.#tryCleanup(true);\n    }\n  }\n  _pumpOperatorList({\n    renderingIntent,\n    cacheKey,\n    annotationStorageSerializable\n  }) {\n    const {\n      map,\n      transfer\n    } = annotationStorageSerializable;\n    const readableStream = this._transport.messageHandler.sendWithStream(\"GetOperatorList\", {\n      pageIndex: this._pageIndex,\n      intent: renderingIntent,\n      cacheKey,\n      annotationStorage: map\n    }, transfer);\n    const reader = readableStream.getReader();\n    const intentState = this._intentStates.get(cacheKey);\n    intentState.streamReader = reader;\n    const pump = () => {\n      reader.read().then(({\n        value,\n        done\n      }) => {\n        if (done) {\n          intentState.streamReader = null;\n          return;\n        }\n        if (this._transport.destroyed) {\n          return;\n        }\n        this._renderPageChunk(value, intentState);\n        pump();\n      }, reason => {\n        intentState.streamReader = null;\n        if (this._transport.destroyed) {\n          return;\n        }\n        if (intentState.operatorList) {\n          intentState.operatorList.lastChunk = true;\n          for (const internalRenderTask of intentState.renderTasks) {\n            internalRenderTask.operatorListChanged();\n          }\n          this.#tryCleanup(true);\n        }\n        if (intentState.displayReadyCapability) {\n          intentState.displayReadyCapability.reject(reason);\n        } else if (intentState.opListReadCapability) {\n          intentState.opListReadCapability.reject(reason);\n        } else {\n          throw reason;\n        }\n      });\n    };\n    pump();\n  }\n  _abortOperatorList({\n    intentState,\n    reason,\n    force = false\n  }) {\n    if (!intentState.streamReader) {\n      return;\n    }\n    if (intentState.streamReaderCancelTimeout) {\n      clearTimeout(intentState.streamReaderCancelTimeout);\n      intentState.streamReaderCancelTimeout = null;\n    }\n    if (!force) {\n      if (intentState.renderTasks.size > 0) {\n        return;\n      }\n      if (reason instanceof _display_utils_js__WEBPACK_IMPORTED_MODULE_2__.RenderingCancelledException) {\n        let delay = RENDERING_CANCELLED_TIMEOUT;\n        if (reason.extraDelay > 0 && reason.extraDelay < 1000) {\n          delay += reason.extraDelay;\n        }\n        intentState.streamReaderCancelTimeout = setTimeout(() => {\n          intentState.streamReaderCancelTimeout = null;\n          this._abortOperatorList({\n            intentState,\n            reason,\n            force: true\n          });\n        }, delay);\n        return;\n      }\n    }\n    intentState.streamReader.cancel(new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AbortException(reason.message)).catch(() => {});\n    intentState.streamReader = null;\n    if (this._transport.destroyed) {\n      return;\n    }\n    for (const [curCacheKey, curIntentState] of this._intentStates) {\n      if (curIntentState === intentState) {\n        this._intentStates.delete(curCacheKey);\n        break;\n      }\n    }\n    this.cleanup();\n  }\n  get stats() {\n    return this._stats;\n  }\n}\nclass LoopbackPort {\n  #listeners = new Set();\n  #deferred = Promise.resolve();\n  postMessage(obj, transfer) {\n    const event = {\n      data: structuredClone(obj, transfer ? {\n        transfer\n      } : null)\n    };\n    this.#deferred.then(() => {\n      for (const listener of this.#listeners) {\n        listener.call(this, event);\n      }\n    });\n  }\n  addEventListener(name, listener) {\n    this.#listeners.add(listener);\n  }\n  removeEventListener(name, listener) {\n    this.#listeners.delete(listener);\n  }\n  terminate() {\n    this.#listeners.clear();\n  }\n}\nconst PDFWorkerUtil = {\n  isWorkerDisabled: false,\n  fakeWorkerId: 0\n};\n{\n  if (_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS) {\n    PDFWorkerUtil.isWorkerDisabled = true;\n    _worker_options_js__WEBPACK_IMPORTED_MODULE_6__.GlobalWorkerOptions.workerSrc ||= \"./pdf.worker.mjs\";\n  }\n  PDFWorkerUtil.isSameOrigin = function (baseUrl, otherUrl) {\n    let base;\n    try {\n      base = new URL(baseUrl);\n      if (!base.origin || base.origin === \"null\") {\n        return false;\n      }\n    } catch {\n      return false;\n    }\n    const other = new URL(otherUrl, base);\n    return base.origin === other.origin;\n  };\n  PDFWorkerUtil.createCDNWrapper = function (url) {\n    const wrapper = `await import(\"${url}\");`;\n    return URL.createObjectURL(new Blob([wrapper], {\n      type: \"text/javascript\"\n    }));\n  };\n}\nclass PDFWorker {\n  static #workerPorts;\n  constructor({\n    name = null,\n    port = null,\n    verbosity = (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.getVerbosityLevel)()\n  } = {}) {\n    this.name = name;\n    this.destroyed = false;\n    this.verbosity = verbosity;\n    this._readyCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this._port = null;\n    this._webWorker = null;\n    this._messageHandler = null;\n    if (port) {\n      if (PDFWorker.#workerPorts?.has(port)) {\n        throw new Error(\"Cannot use more than one PDFWorker per port.\");\n      }\n      (PDFWorker.#workerPorts ||= new WeakMap()).set(port, this);\n      this._initializeFromPort(port);\n      return;\n    }\n    this._initialize();\n  }\n  get promise() {\n    return this._readyCapability.promise;\n  }\n  get port() {\n    return this._port;\n  }\n  get messageHandler() {\n    return this._messageHandler;\n  }\n  _initializeFromPort(port) {\n    this._port = port;\n    this._messageHandler = new _shared_message_handler_js__WEBPACK_IMPORTED_MODULE_7__.MessageHandler(\"main\", \"worker\", port);\n    this._messageHandler.on(\"ready\", function () {});\n    this._readyCapability.resolve();\n    this._messageHandler.send(\"configure\", {\n      verbosity: this.verbosity\n    });\n  }\n  _initialize() {\n    if (!PDFWorkerUtil.isWorkerDisabled && !PDFWorker.#mainThreadWorkerMessageHandler) {\n      let {\n        workerSrc\n      } = PDFWorker;\n      try {\n        if (!PDFWorkerUtil.isSameOrigin(window.location.href, workerSrc)) {\n          workerSrc = PDFWorkerUtil.createCDNWrapper(new URL(workerSrc, window.location).href);\n        }\n        const worker = new Worker(workerSrc, {\n          type: \"module\"\n        });\n        const messageHandler = new _shared_message_handler_js__WEBPACK_IMPORTED_MODULE_7__.MessageHandler(\"main\", \"worker\", worker);\n        const terminateEarly = () => {\n          worker.removeEventListener(\"error\", onWorkerError);\n          messageHandler.destroy();\n          worker.terminate();\n          if (this.destroyed) {\n            this._readyCapability.reject(new Error(\"Worker was destroyed\"));\n          } else {\n            this._setupFakeWorker();\n          }\n        };\n        const onWorkerError = () => {\n          if (!this._webWorker) {\n            terminateEarly();\n          }\n        };\n        worker.addEventListener(\"error\", onWorkerError);\n        messageHandler.on(\"test\", data => {\n          worker.removeEventListener(\"error\", onWorkerError);\n          if (this.destroyed) {\n            terminateEarly();\n            return;\n          }\n          if (data) {\n            this._messageHandler = messageHandler;\n            this._port = worker;\n            this._webWorker = worker;\n            this._readyCapability.resolve();\n            messageHandler.send(\"configure\", {\n              verbosity: this.verbosity\n            });\n          } else {\n            this._setupFakeWorker();\n            messageHandler.destroy();\n            worker.terminate();\n          }\n        });\n        messageHandler.on(\"ready\", data => {\n          worker.removeEventListener(\"error\", onWorkerError);\n          if (this.destroyed) {\n            terminateEarly();\n            return;\n          }\n          try {\n            sendTest();\n          } catch {\n            this._setupFakeWorker();\n          }\n        });\n        const sendTest = () => {\n          const testObj = new Uint8Array();\n          messageHandler.send(\"test\", testObj, [testObj.buffer]);\n        };\n        sendTest();\n        return;\n      } catch {\n        (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.info)(\"The worker has been disabled.\");\n      }\n    }\n    this._setupFakeWorker();\n  }\n  _setupFakeWorker() {\n    if (!PDFWorkerUtil.isWorkerDisabled) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(\"Setting up fake worker.\");\n      PDFWorkerUtil.isWorkerDisabled = true;\n    }\n    PDFWorker._setupFakeWorkerGlobal.then(WorkerMessageHandler => {\n      if (this.destroyed) {\n        this._readyCapability.reject(new Error(\"Worker was destroyed\"));\n        return;\n      }\n      const port = new LoopbackPort();\n      this._port = port;\n      const id = `fake${PDFWorkerUtil.fakeWorkerId++}`;\n      const workerHandler = new _shared_message_handler_js__WEBPACK_IMPORTED_MODULE_7__.MessageHandler(id + \"_worker\", id, port);\n      WorkerMessageHandler.setup(workerHandler, port);\n      const messageHandler = new _shared_message_handler_js__WEBPACK_IMPORTED_MODULE_7__.MessageHandler(id, id + \"_worker\", port);\n      this._messageHandler = messageHandler;\n      this._readyCapability.resolve();\n      messageHandler.send(\"configure\", {\n        verbosity: this.verbosity\n      });\n    }).catch(reason => {\n      this._readyCapability.reject(new Error(`Setting up fake worker failed: \"${reason.message}\".`));\n    });\n  }\n  destroy() {\n    this.destroyed = true;\n    if (this._webWorker) {\n      this._webWorker.terminate();\n      this._webWorker = null;\n    }\n    PDFWorker.#workerPorts?.delete(this._port);\n    this._port = null;\n    if (this._messageHandler) {\n      this._messageHandler.destroy();\n      this._messageHandler = null;\n    }\n  }\n  static fromPort(params) {\n    if (!params?.port) {\n      throw new Error(\"PDFWorker.fromPort - invalid method signature.\");\n    }\n    const cachedPort = this.#workerPorts?.get(params.port);\n    if (cachedPort) {\n      if (cachedPort._pendingDestroy) {\n        throw new Error(\"PDFWorker.fromPort - the worker is being destroyed.\\n\" + \"Please remember to await `PDFDocumentLoadingTask.destroy()`-calls.\");\n      }\n      return cachedPort;\n    }\n    return new PDFWorker(params);\n  }\n  static get workerSrc() {\n    if (_worker_options_js__WEBPACK_IMPORTED_MODULE_6__.GlobalWorkerOptions.workerSrc) {\n      return _worker_options_js__WEBPACK_IMPORTED_MODULE_6__.GlobalWorkerOptions.workerSrc;\n    }\n    throw new Error('No \"GlobalWorkerOptions.workerSrc\" specified.');\n  }\n  static get #mainThreadWorkerMessageHandler() {\n    try {\n      return globalThis.pdfjsWorker?.WorkerMessageHandler || null;\n    } catch {\n      return null;\n    }\n  }\n  static get _setupFakeWorkerGlobal() {\n    const loader = async () => {\n      if (this.#mainThreadWorkerMessageHandler) {\n        return this.#mainThreadWorkerMessageHandler;\n      }\n      const worker = await import(/* webpackIgnore: true */ this.workerSrc);\n      return worker.WorkerMessageHandler;\n    };\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"_setupFakeWorkerGlobal\", loader());\n  }\n}\nclass WorkerTransport {\n  #methodPromises = new Map();\n  #pageCache = new Map();\n  #pagePromises = new Map();\n  #passwordCapability = null;\n  constructor(messageHandler, loadingTask, networkStream, params, factory) {\n    this.messageHandler = messageHandler;\n    this.loadingTask = loadingTask;\n    this.commonObjs = new PDFObjects();\n    this.fontLoader = new _font_loader_js__WEBPACK_IMPORTED_MODULE_3__.FontLoader({\n      ownerDocument: params.ownerDocument,\n      styleElement: params.styleElement\n    });\n    this._params = params;\n    this.canvasFactory = factory.canvasFactory;\n    this.filterFactory = factory.filterFactory;\n    this.cMapReaderFactory = factory.cMapReaderFactory;\n    this.standardFontDataFactory = factory.standardFontDataFactory;\n    this.destroyed = false;\n    this.destroyCapability = null;\n    this._networkStream = networkStream;\n    this._fullReader = null;\n    this._lastProgress = null;\n    this.downloadInfoCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this.setupMessageHandler();\n  }\n  #cacheSimpleMethod(name, data = null) {\n    const cachedPromise = this.#methodPromises.get(name);\n    if (cachedPromise) {\n      return cachedPromise;\n    }\n    const promise = this.messageHandler.sendWithPromise(name, data);\n    this.#methodPromises.set(name, promise);\n    return promise;\n  }\n  get annotationStorage() {\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"annotationStorage\", new _annotation_storage_js__WEBPACK_IMPORTED_MODULE_1__.AnnotationStorage());\n  }\n  getRenderingIntent(intent, annotationMode = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationMode.ENABLE, printAnnotationStorage = null, isOpList = false) {\n    let renderingIntent = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.RenderingIntentFlag.DISPLAY;\n    let annotationStorageSerializable = _annotation_storage_js__WEBPACK_IMPORTED_MODULE_1__.SerializableEmpty;\n    switch (intent) {\n      case \"any\":\n        renderingIntent = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.RenderingIntentFlag.ANY;\n        break;\n      case \"display\":\n        break;\n      case \"print\":\n        renderingIntent = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.RenderingIntentFlag.PRINT;\n        break;\n      default:\n        (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`getRenderingIntent - invalid intent: ${intent}`);\n    }\n    switch (annotationMode) {\n      case _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationMode.DISABLE:\n        renderingIntent += _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.RenderingIntentFlag.ANNOTATIONS_DISABLE;\n        break;\n      case _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationMode.ENABLE:\n        break;\n      case _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationMode.ENABLE_FORMS:\n        renderingIntent += _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.RenderingIntentFlag.ANNOTATIONS_FORMS;\n        break;\n      case _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationMode.ENABLE_STORAGE:\n        renderingIntent += _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.RenderingIntentFlag.ANNOTATIONS_STORAGE;\n        const annotationStorage = renderingIntent & _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.RenderingIntentFlag.PRINT && printAnnotationStorage instanceof _annotation_storage_js__WEBPACK_IMPORTED_MODULE_1__.PrintAnnotationStorage ? printAnnotationStorage : this.annotationStorage;\n        annotationStorageSerializable = annotationStorage.serializable;\n        break;\n      default:\n        (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`getRenderingIntent - invalid annotationMode: ${annotationMode}`);\n    }\n    if (isOpList) {\n      renderingIntent += _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.RenderingIntentFlag.OPLIST;\n    }\n    return {\n      renderingIntent,\n      cacheKey: `${renderingIntent}_${annotationStorageSerializable.hash}`,\n      annotationStorageSerializable\n    };\n  }\n  destroy() {\n    if (this.destroyCapability) {\n      return this.destroyCapability.promise;\n    }\n    this.destroyed = true;\n    this.destroyCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this.#passwordCapability?.reject(new Error(\"Worker was destroyed during onPassword callback\"));\n    const waitOn = [];\n    for (const page of this.#pageCache.values()) {\n      waitOn.push(page._destroy());\n    }\n    this.#pageCache.clear();\n    this.#pagePromises.clear();\n    if (this.hasOwnProperty(\"annotationStorage\")) {\n      this.annotationStorage.resetModified();\n    }\n    const terminated = this.messageHandler.sendWithPromise(\"Terminate\", null);\n    waitOn.push(terminated);\n    Promise.all(waitOn).then(() => {\n      this.commonObjs.clear();\n      this.fontLoader.clear();\n      this.#methodPromises.clear();\n      this.filterFactory.destroy();\n      this._networkStream?.cancelAllRequests(new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AbortException(\"Worker was terminated.\"));\n      if (this.messageHandler) {\n        this.messageHandler.destroy();\n        this.messageHandler = null;\n      }\n      this.destroyCapability.resolve();\n    }, this.destroyCapability.reject);\n    return this.destroyCapability.promise;\n  }\n  setupMessageHandler() {\n    const {\n      messageHandler,\n      loadingTask\n    } = this;\n    messageHandler.on(\"GetReader\", (data, sink) => {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(this._networkStream, \"GetReader - no `IPDFStream` instance available.\");\n      this._fullReader = this._networkStream.getFullReader();\n      this._fullReader.onProgress = evt => {\n        this._lastProgress = {\n          loaded: evt.loaded,\n          total: evt.total\n        };\n      };\n      sink.onPull = () => {\n        this._fullReader.read().then(function ({\n          value,\n          done\n        }) {\n          if (done) {\n            sink.close();\n            return;\n          }\n          (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(value instanceof ArrayBuffer, \"GetReader - expected an ArrayBuffer.\");\n          sink.enqueue(new Uint8Array(value), 1, [value]);\n        }).catch(reason => {\n          sink.error(reason);\n        });\n      };\n      sink.onCancel = reason => {\n        this._fullReader.cancel(reason);\n        sink.ready.catch(readyReason => {\n          if (this.destroyed) {\n            return;\n          }\n          throw readyReason;\n        });\n      };\n    });\n    messageHandler.on(\"ReaderHeadersReady\", data => {\n      const headersCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n      const fullReader = this._fullReader;\n      fullReader.headersReady.then(() => {\n        if (!fullReader.isStreamingSupported || !fullReader.isRangeSupported) {\n          if (this._lastProgress) {\n            loadingTask.onProgress?.(this._lastProgress);\n          }\n          fullReader.onProgress = evt => {\n            loadingTask.onProgress?.({\n              loaded: evt.loaded,\n              total: evt.total\n            });\n          };\n        }\n        headersCapability.resolve({\n          isStreamingSupported: fullReader.isStreamingSupported,\n          isRangeSupported: fullReader.isRangeSupported,\n          contentLength: fullReader.contentLength\n        });\n      }, headersCapability.reject);\n      return headersCapability.promise;\n    });\n    messageHandler.on(\"GetRangeReader\", (data, sink) => {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(this._networkStream, \"GetRangeReader - no `IPDFStream` instance available.\");\n      const rangeReader = this._networkStream.getRangeReader(data.begin, data.end);\n      if (!rangeReader) {\n        sink.close();\n        return;\n      }\n      sink.onPull = () => {\n        rangeReader.read().then(function ({\n          value,\n          done\n        }) {\n          if (done) {\n            sink.close();\n            return;\n          }\n          (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(value instanceof ArrayBuffer, \"GetRangeReader - expected an ArrayBuffer.\");\n          sink.enqueue(new Uint8Array(value), 1, [value]);\n        }).catch(reason => {\n          sink.error(reason);\n        });\n      };\n      sink.onCancel = reason => {\n        rangeReader.cancel(reason);\n        sink.ready.catch(readyReason => {\n          if (this.destroyed) {\n            return;\n          }\n          throw readyReason;\n        });\n      };\n    });\n    messageHandler.on(\"GetDoc\", ({\n      pdfInfo\n    }) => {\n      this._numPages = pdfInfo.numPages;\n      this._htmlForXfa = pdfInfo.htmlForXfa;\n      delete pdfInfo.htmlForXfa;\n      loadingTask._capability.resolve(new PDFDocumentProxy(pdfInfo, this));\n    });\n    messageHandler.on(\"DocException\", function (ex) {\n      let reason;\n      switch (ex.name) {\n        case \"PasswordException\":\n          reason = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PasswordException(ex.message, ex.code);\n          break;\n        case \"InvalidPDFException\":\n          reason = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.InvalidPDFException(ex.message);\n          break;\n        case \"MissingPDFException\":\n          reason = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.MissingPDFException(ex.message);\n          break;\n        case \"UnexpectedResponseException\":\n          reason = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.UnexpectedResponseException(ex.message, ex.status);\n          break;\n        case \"UnknownErrorException\":\n          reason = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.UnknownErrorException(ex.message, ex.details);\n          break;\n        default:\n          (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"DocException - expected a valid Error.\");\n      }\n      loadingTask._capability.reject(reason);\n    });\n    messageHandler.on(\"PasswordRequest\", exception => {\n      this.#passwordCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n      if (loadingTask.onPassword) {\n        const updatePassword = password => {\n          if (password instanceof Error) {\n            this.#passwordCapability.reject(password);\n          } else {\n            this.#passwordCapability.resolve({\n              password\n            });\n          }\n        };\n        try {\n          loadingTask.onPassword(updatePassword, exception.code);\n        } catch (ex) {\n          this.#passwordCapability.reject(ex);\n        }\n      } else {\n        this.#passwordCapability.reject(new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PasswordException(exception.message, exception.code));\n      }\n      return this.#passwordCapability.promise;\n    });\n    messageHandler.on(\"DataLoaded\", data => {\n      loadingTask.onProgress?.({\n        loaded: data.length,\n        total: data.length\n      });\n      this.downloadInfoCapability.resolve(data);\n    });\n    messageHandler.on(\"StartRenderPage\", data => {\n      if (this.destroyed) {\n        return;\n      }\n      const page = this.#pageCache.get(data.pageIndex);\n      page._startRenderPage(data.transparency, data.cacheKey);\n    });\n    messageHandler.on(\"commonobj\", ([id, type, exportedData]) => {\n      if (this.destroyed) {\n        return;\n      }\n      if (this.commonObjs.has(id)) {\n        return;\n      }\n      switch (type) {\n        case \"Font\":\n          const params = this._params;\n          if (\"error\" in exportedData) {\n            const exportedError = exportedData.error;\n            (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Error during font loading: ${exportedError}`);\n            this.commonObjs.resolve(id, exportedError);\n            break;\n          }\n          const inspectFont = params.pdfBug && globalThis.FontInspector?.enabled ? (font, url) => globalThis.FontInspector.fontAdded(font, url) : null;\n          const font = new _font_loader_js__WEBPACK_IMPORTED_MODULE_3__.FontFaceObject(exportedData, {\n            isEvalSupported: params.isEvalSupported,\n            disableFontFace: params.disableFontFace,\n            ignoreErrors: params.ignoreErrors,\n            inspectFont\n          });\n          this.fontLoader.bind(font).catch(reason => {\n            return messageHandler.sendWithPromise(\"FontFallback\", {\n              id\n            });\n          }).finally(() => {\n            if (!params.fontExtraProperties && font.data) {\n              font.data = null;\n            }\n            this.commonObjs.resolve(id, font);\n          });\n          break;\n        case \"FontPath\":\n        case \"Image\":\n        case \"Pattern\":\n          this.commonObjs.resolve(id, exportedData);\n          break;\n        default:\n          throw new Error(`Got unknown common object type ${type}`);\n      }\n    });\n    messageHandler.on(\"obj\", ([id, pageIndex, type, imageData]) => {\n      if (this.destroyed) {\n        return;\n      }\n      const pageProxy = this.#pageCache.get(pageIndex);\n      if (pageProxy.objs.has(id)) {\n        return;\n      }\n      if (pageProxy._intentStates.size === 0) {\n        imageData?.bitmap?.close();\n        return;\n      }\n      switch (type) {\n        case \"Image\":\n          pageProxy.objs.resolve(id, imageData);\n          if (imageData) {\n            let length;\n            if (imageData.bitmap) {\n              const {\n                width,\n                height\n              } = imageData;\n              length = width * height * 4;\n            } else {\n              length = imageData.data?.length || 0;\n            }\n            if (length > _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.MAX_IMAGE_SIZE_TO_CACHE) {\n              pageProxy._maybeCleanupAfterRender = true;\n            }\n          }\n          break;\n        case \"Pattern\":\n          pageProxy.objs.resolve(id, imageData);\n          break;\n        default:\n          throw new Error(`Got unknown object type ${type}`);\n      }\n    });\n    messageHandler.on(\"DocProgress\", data => {\n      if (this.destroyed) {\n        return;\n      }\n      loadingTask.onProgress?.({\n        loaded: data.loaded,\n        total: data.total\n      });\n    });\n    messageHandler.on(\"FetchBuiltInCMap\", data => {\n      if (this.destroyed) {\n        return Promise.reject(new Error(\"Worker was destroyed.\"));\n      }\n      if (!this.cMapReaderFactory) {\n        return Promise.reject(new Error(\"CMapReaderFactory not initialized, see the `useWorkerFetch` parameter.\"));\n      }\n      return this.cMapReaderFactory.fetch(data);\n    });\n    messageHandler.on(\"FetchStandardFontData\", data => {\n      if (this.destroyed) {\n        return Promise.reject(new Error(\"Worker was destroyed.\"));\n      }\n      if (!this.standardFontDataFactory) {\n        return Promise.reject(new Error(\"StandardFontDataFactory not initialized, see the `useWorkerFetch` parameter.\"));\n      }\n      return this.standardFontDataFactory.fetch(data);\n    });\n  }\n  getData() {\n    return this.messageHandler.sendWithPromise(\"GetData\", null);\n  }\n  saveDocument() {\n    if (this.annotationStorage.size <= 0) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(\"saveDocument called while `annotationStorage` is empty, \" + \"please use the getData-method instead.\");\n    }\n    const {\n      map,\n      transfer\n    } = this.annotationStorage.serializable;\n    return this.messageHandler.sendWithPromise(\"SaveDocument\", {\n      isPureXfa: !!this._htmlForXfa,\n      numPages: this._numPages,\n      annotationStorage: map,\n      filename: this._fullReader?.filename ?? null\n    }, transfer).finally(() => {\n      this.annotationStorage.resetModified();\n    });\n  }\n  getPage(pageNumber) {\n    if (!Number.isInteger(pageNumber) || pageNumber <= 0 || pageNumber > this._numPages) {\n      return Promise.reject(new Error(\"Invalid page request.\"));\n    }\n    const pageIndex = pageNumber - 1,\n      cachedPromise = this.#pagePromises.get(pageIndex);\n    if (cachedPromise) {\n      return cachedPromise;\n    }\n    const promise = this.messageHandler.sendWithPromise(\"GetPage\", {\n      pageIndex\n    }).then(pageInfo => {\n      if (this.destroyed) {\n        throw new Error(\"Transport destroyed\");\n      }\n      const page = new PDFPageProxy(pageIndex, pageInfo, this, this._params.pdfBug);\n      this.#pageCache.set(pageIndex, page);\n      return page;\n    });\n    this.#pagePromises.set(pageIndex, promise);\n    return promise;\n  }\n  getPageIndex(ref) {\n    if (typeof ref !== \"object\" || ref === null || !Number.isInteger(ref.num) || ref.num < 0 || !Number.isInteger(ref.gen) || ref.gen < 0) {\n      return Promise.reject(new Error(\"Invalid pageIndex request.\"));\n    }\n    return this.messageHandler.sendWithPromise(\"GetPageIndex\", {\n      num: ref.num,\n      gen: ref.gen\n    });\n  }\n  getAnnotations(pageIndex, intent) {\n    return this.messageHandler.sendWithPromise(\"GetAnnotations\", {\n      pageIndex,\n      intent\n    });\n  }\n  getFieldObjects() {\n    return this.#cacheSimpleMethod(\"GetFieldObjects\");\n  }\n  hasJSActions() {\n    return this.#cacheSimpleMethod(\"HasJSActions\");\n  }\n  getCalculationOrderIds() {\n    return this.messageHandler.sendWithPromise(\"GetCalculationOrderIds\", null);\n  }\n  getDestinations() {\n    return this.messageHandler.sendWithPromise(\"GetDestinations\", null);\n  }\n  getDestination(id) {\n    if (typeof id !== \"string\") {\n      return Promise.reject(new Error(\"Invalid destination request.\"));\n    }\n    return this.messageHandler.sendWithPromise(\"GetDestination\", {\n      id\n    });\n  }\n  getPageLabels() {\n    return this.messageHandler.sendWithPromise(\"GetPageLabels\", null);\n  }\n  getPageLayout() {\n    return this.messageHandler.sendWithPromise(\"GetPageLayout\", null);\n  }\n  getPageMode() {\n    return this.messageHandler.sendWithPromise(\"GetPageMode\", null);\n  }\n  getViewerPreferences() {\n    return this.messageHandler.sendWithPromise(\"GetViewerPreferences\", null);\n  }\n  getOpenAction() {\n    return this.messageHandler.sendWithPromise(\"GetOpenAction\", null);\n  }\n  getAttachments() {\n    return this.messageHandler.sendWithPromise(\"GetAttachments\", null);\n  }\n  getDocJSActions() {\n    return this.#cacheSimpleMethod(\"GetDocJSActions\");\n  }\n  getPageJSActions(pageIndex) {\n    return this.messageHandler.sendWithPromise(\"GetPageJSActions\", {\n      pageIndex\n    });\n  }\n  getStructTree(pageIndex) {\n    return this.messageHandler.sendWithPromise(\"GetStructTree\", {\n      pageIndex\n    });\n  }\n  getOutline() {\n    return this.messageHandler.sendWithPromise(\"GetOutline\", null);\n  }\n  getOptionalContentConfig() {\n    return this.messageHandler.sendWithPromise(\"GetOptionalContentConfig\", null).then(results => {\n      return new _optional_content_config_js__WEBPACK_IMPORTED_MODULE_9__.OptionalContentConfig(results);\n    });\n  }\n  getPermissions() {\n    return this.messageHandler.sendWithPromise(\"GetPermissions\", null);\n  }\n  getMetadata() {\n    const name = \"GetMetadata\",\n      cachedPromise = this.#methodPromises.get(name);\n    if (cachedPromise) {\n      return cachedPromise;\n    }\n    const promise = this.messageHandler.sendWithPromise(name, null).then(results => {\n      return {\n        info: results[0],\n        metadata: results[1] ? new _metadata_js__WEBPACK_IMPORTED_MODULE_8__.Metadata(results[1]) : null,\n        contentDispositionFilename: this._fullReader?.filename ?? null,\n        contentLength: this._fullReader?.contentLength ?? null\n      };\n    });\n    this.#methodPromises.set(name, promise);\n    return promise;\n  }\n  getMarkInfo() {\n    return this.messageHandler.sendWithPromise(\"GetMarkInfo\", null);\n  }\n  async startCleanup(keepLoadedFonts = false) {\n    if (this.destroyed) {\n      return;\n    }\n    await this.messageHandler.sendWithPromise(\"Cleanup\", null);\n    for (const page of this.#pageCache.values()) {\n      const cleanupSuccessful = page.cleanup();\n      if (!cleanupSuccessful) {\n        throw new Error(`startCleanup: Page ${page.pageNumber} is currently rendering.`);\n      }\n    }\n    this.commonObjs.clear();\n    if (!keepLoadedFonts) {\n      this.fontLoader.clear();\n    }\n    this.#methodPromises.clear();\n    this.filterFactory.destroy(true);\n  }\n  get loadingParams() {\n    const {\n      disableAutoFetch,\n      enableXfa\n    } = this._params;\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"loadingParams\", {\n      disableAutoFetch,\n      enableXfa\n    });\n  }\n}\nclass PDFObjects {\n  #objs = Object.create(null);\n  #ensureObj(objId) {\n    return this.#objs[objId] ||= {\n      capability: new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability(),\n      data: null\n    };\n  }\n  get(objId, callback = null) {\n    if (callback) {\n      const obj = this.#ensureObj(objId);\n      obj.capability.promise.then(() => callback(obj.data));\n      return null;\n    }\n    const obj = this.#objs[objId];\n    if (!obj?.capability.settled) {\n      throw new Error(`Requesting object that isn't resolved yet ${objId}.`);\n    }\n    return obj.data;\n  }\n  has(objId) {\n    const obj = this.#objs[objId];\n    return obj?.capability.settled || false;\n  }\n  resolve(objId, data = null) {\n    const obj = this.#ensureObj(objId);\n    obj.data = data;\n    obj.capability.resolve();\n  }\n  clear() {\n    for (const objId in this.#objs) {\n      const {\n        data\n      } = this.#objs[objId];\n      data?.bitmap?.close();\n    }\n    this.#objs = Object.create(null);\n  }\n}\nclass RenderTask {\n  #internalRenderTask = null;\n  constructor(internalRenderTask) {\n    this.#internalRenderTask = internalRenderTask;\n    this.onContinue = null;\n  }\n  get promise() {\n    return this.#internalRenderTask.capability.promise;\n  }\n  cancel(extraDelay = 0) {\n    this.#internalRenderTask.cancel(null, extraDelay);\n  }\n  get separateAnnots() {\n    const {\n      separateAnnots\n    } = this.#internalRenderTask.operatorList;\n    if (!separateAnnots) {\n      return false;\n    }\n    const {\n      annotationCanvasMap\n    } = this.#internalRenderTask;\n    return separateAnnots.form || separateAnnots.canvas && annotationCanvasMap?.size > 0;\n  }\n}\nclass InternalRenderTask {\n  static #canvasInUse = new WeakSet();\n  constructor({\n    callback,\n    params,\n    objs,\n    commonObjs,\n    annotationCanvasMap,\n    operatorList,\n    pageIndex,\n    canvasFactory,\n    filterFactory,\n    useRequestAnimationFrame = false,\n    pdfBug = false,\n    pageColors = null\n  }) {\n    this.callback = callback;\n    this.params = params;\n    this.objs = objs;\n    this.commonObjs = commonObjs;\n    this.annotationCanvasMap = annotationCanvasMap;\n    this.operatorListIdx = null;\n    this.operatorList = operatorList;\n    this._pageIndex = pageIndex;\n    this.canvasFactory = canvasFactory;\n    this.filterFactory = filterFactory;\n    this._pdfBug = pdfBug;\n    this.pageColors = pageColors;\n    this.running = false;\n    this.graphicsReadyCallback = null;\n    this.graphicsReady = false;\n    this._useRequestAnimationFrame = useRequestAnimationFrame === true && typeof window !== \"undefined\";\n    this.cancelled = false;\n    this.capability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this.task = new RenderTask(this);\n    this._cancelBound = this.cancel.bind(this);\n    this._continueBound = this._continue.bind(this);\n    this._scheduleNextBound = this._scheduleNext.bind(this);\n    this._nextBound = this._next.bind(this);\n    this._canvas = params.canvasContext.canvas;\n  }\n  get completed() {\n    return this.capability.promise.catch(function () {});\n  }\n  initializeGraphics({\n    transparency = false,\n    optionalContentConfig\n  }) {\n    if (this.cancelled) {\n      return;\n    }\n    if (this._canvas) {\n      if (InternalRenderTask.#canvasInUse.has(this._canvas)) {\n        throw new Error(\"Cannot use the same canvas during multiple render() operations. \" + \"Use different canvas or ensure previous operations were \" + \"cancelled or completed.\");\n      }\n      InternalRenderTask.#canvasInUse.add(this._canvas);\n    }\n    if (this._pdfBug && globalThis.StepperManager?.enabled) {\n      this.stepper = globalThis.StepperManager.create(this._pageIndex);\n      this.stepper.init(this.operatorList);\n      this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint();\n    }\n    const {\n      canvasContext,\n      viewport,\n      transform,\n      background\n    } = this.params;\n    this.gfx = new _canvas_js__WEBPACK_IMPORTED_MODULE_5__.CanvasGraphics(canvasContext, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, {\n      optionalContentConfig\n    }, this.annotationCanvasMap, this.pageColors);\n    this.gfx.beginDrawing({\n      transform,\n      viewport,\n      transparency,\n      background\n    });\n    this.operatorListIdx = 0;\n    this.graphicsReady = true;\n    this.graphicsReadyCallback?.();\n  }\n  cancel(error = null, extraDelay = 0) {\n    this.running = false;\n    this.cancelled = true;\n    this.gfx?.endDrawing();\n    InternalRenderTask.#canvasInUse.delete(this._canvas);\n    this.callback(error || new _display_utils_js__WEBPACK_IMPORTED_MODULE_2__.RenderingCancelledException(`Rendering cancelled, page ${this._pageIndex + 1}`, extraDelay));\n  }\n  operatorListChanged() {\n    if (!this.graphicsReady) {\n      this.graphicsReadyCallback ||= this._continueBound;\n      return;\n    }\n    this.stepper?.updateOperatorList(this.operatorList);\n    if (this.running) {\n      return;\n    }\n    this._continue();\n  }\n  _continue() {\n    this.running = true;\n    if (this.cancelled) {\n      return;\n    }\n    if (this.task.onContinue) {\n      this.task.onContinue(this._scheduleNextBound);\n    } else {\n      this._scheduleNext();\n    }\n  }\n  _scheduleNext() {\n    if (this._useRequestAnimationFrame) {\n      window.requestAnimationFrame(() => {\n        this._nextBound().catch(this._cancelBound);\n      });\n    } else {\n      Promise.resolve().then(this._nextBound).catch(this._cancelBound);\n    }\n  }\n  async _next() {\n    if (this.cancelled) {\n      return;\n    }\n    this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, this.operatorListIdx, this._continueBound, this.stepper);\n    if (this.operatorListIdx === this.operatorList.argsArray.length) {\n      this.running = false;\n      if (this.operatorList.lastChunk) {\n        this.gfx.endDrawing();\n        InternalRenderTask.#canvasInUse.delete(this._canvas);\n        this.callback();\n      }\n    }\n  }\n}\nconst version = '4.0.269';\nconst build = 'f4b396f6c';\n\n__webpack_async_result__();\n} catch(e) { __webpack_async_result__(e); } });\n\n/***/ }),\n\n/***/ 822:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   BaseCMapReaderFactory: () => (/* binding */ BaseCMapReaderFactory),\n/* harmony export */   BaseCanvasFactory: () => (/* binding */ BaseCanvasFactory),\n/* harmony export */   BaseFilterFactory: () => (/* binding */ BaseFilterFactory),\n/* harmony export */   BaseSVGFactory: () => (/* binding */ BaseSVGFactory),\n/* harmony export */   BaseStandardFontDataFactory: () => (/* binding */ BaseStandardFontDataFactory)\n/* harmony export */ });\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n\nclass BaseFilterFactory {\n  constructor() {\n    if (this.constructor === BaseFilterFactory) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Cannot initialize BaseFilterFactory.\");\n    }\n  }\n  addFilter(maps) {\n    return \"none\";\n  }\n  addHCMFilter(fgColor, bgColor) {\n    return \"none\";\n  }\n  addHighlightHCMFilter(fgColor, bgColor, newFgColor, newBgColor) {\n    return \"none\";\n  }\n  destroy(keepHCM = false) {}\n}\nclass BaseCanvasFactory {\n  constructor() {\n    if (this.constructor === BaseCanvasFactory) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Cannot initialize BaseCanvasFactory.\");\n    }\n  }\n  create(width, height) {\n    if (width <= 0 || height <= 0) {\n      throw new Error(\"Invalid canvas size\");\n    }\n    const canvas = this._createCanvas(width, height);\n    return {\n      canvas,\n      context: canvas.getContext(\"2d\")\n    };\n  }\n  reset(canvasAndContext, width, height) {\n    if (!canvasAndContext.canvas) {\n      throw new Error(\"Canvas is not specified\");\n    }\n    if (width <= 0 || height <= 0) {\n      throw new Error(\"Invalid canvas size\");\n    }\n    canvasAndContext.canvas.width = width;\n    canvasAndContext.canvas.height = height;\n  }\n  destroy(canvasAndContext) {\n    if (!canvasAndContext.canvas) {\n      throw new Error(\"Canvas is not specified\");\n    }\n    canvasAndContext.canvas.width = 0;\n    canvasAndContext.canvas.height = 0;\n    canvasAndContext.canvas = null;\n    canvasAndContext.context = null;\n  }\n  _createCanvas(width, height) {\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Abstract method `_createCanvas` called.\");\n  }\n}\nclass BaseCMapReaderFactory {\n  constructor({\n    baseUrl = null,\n    isCompressed = true\n  }) {\n    if (this.constructor === BaseCMapReaderFactory) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Cannot initialize BaseCMapReaderFactory.\");\n    }\n    this.baseUrl = baseUrl;\n    this.isCompressed = isCompressed;\n  }\n  async fetch({\n    name\n  }) {\n    if (!this.baseUrl) {\n      throw new Error('The CMap \"baseUrl\" parameter must be specified, ensure that ' + 'the \"cMapUrl\" and \"cMapPacked\" API parameters are provided.');\n    }\n    if (!name) {\n      throw new Error(\"CMap name must be specified.\");\n    }\n    const url = this.baseUrl + name + (this.isCompressed ? \".bcmap\" : \"\");\n    const compressionType = this.isCompressed ? _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.CMapCompressionType.BINARY : _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.CMapCompressionType.NONE;\n    return this._fetchData(url, compressionType).catch(reason => {\n      throw new Error(`Unable to load ${this.isCompressed ? \"binary \" : \"\"}CMap at: ${url}`);\n    });\n  }\n  _fetchData(url, compressionType) {\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Abstract method `_fetchData` called.\");\n  }\n}\nclass BaseStandardFontDataFactory {\n  constructor({\n    baseUrl = null\n  }) {\n    if (this.constructor === BaseStandardFontDataFactory) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Cannot initialize BaseStandardFontDataFactory.\");\n    }\n    this.baseUrl = baseUrl;\n  }\n  async fetch({\n    filename\n  }) {\n    if (!this.baseUrl) {\n      throw new Error('The standard font \"baseUrl\" parameter must be specified, ensure that ' + 'the \"standardFontDataUrl\" API parameter is provided.');\n    }\n    if (!filename) {\n      throw new Error(\"Font filename must be specified.\");\n    }\n    const url = `${this.baseUrl}${filename}`;\n    return this._fetchData(url).catch(reason => {\n      throw new Error(`Unable to load font data at: ${url}`);\n    });\n  }\n  _fetchData(url) {\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Abstract method `_fetchData` called.\");\n  }\n}\nclass BaseSVGFactory {\n  constructor() {\n    if (this.constructor === BaseSVGFactory) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Cannot initialize BaseSVGFactory.\");\n    }\n  }\n  create(width, height, skipDimensions = false) {\n    if (width <= 0 || height <= 0) {\n      throw new Error(\"Invalid SVG dimensions\");\n    }\n    const svg = this._createSVG(\"svg:svg\");\n    svg.setAttribute(\"version\", \"1.1\");\n    if (!skipDimensions) {\n      svg.setAttribute(\"width\", `${width}px`);\n      svg.setAttribute(\"height\", `${height}px`);\n    }\n    svg.setAttribute(\"preserveAspectRatio\", \"none\");\n    svg.setAttribute(\"viewBox\", `0 0 ${width} ${height}`);\n    return svg;\n  }\n  createElement(type) {\n    if (typeof type !== \"string\") {\n      throw new Error(\"Invalid SVG element type\");\n    }\n    return this._createSVG(type);\n  }\n  _createSVG(type) {\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Abstract method `_createSVG` called.\");\n  }\n}\n\n\n/***/ }),\n\n/***/ 250:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n  CanvasGraphics: () => (/* binding */ CanvasGraphics)\n});\n\n// EXTERNAL MODULE: ./src/shared/util.js\nvar util = __webpack_require__(266);\n// EXTERNAL MODULE: ./src/display/display_utils.js\nvar display_utils = __webpack_require__(473);\n;// CONCATENATED MODULE: ./src/display/pattern_helper.js\n\n\nconst PathType = {\n  FILL: \"Fill\",\n  STROKE: \"Stroke\",\n  SHADING: \"Shading\"\n};\nfunction applyBoundingBox(ctx, bbox) {\n  if (!bbox) {\n    return;\n  }\n  const width = bbox[2] - bbox[0];\n  const height = bbox[3] - bbox[1];\n  const region = new Path2D();\n  region.rect(bbox[0], bbox[1], width, height);\n  ctx.clip(region);\n}\nclass BaseShadingPattern {\n  constructor() {\n    if (this.constructor === BaseShadingPattern) {\n      (0,util.unreachable)(\"Cannot initialize BaseShadingPattern.\");\n    }\n  }\n  getPattern() {\n    (0,util.unreachable)(\"Abstract method `getPattern` called.\");\n  }\n}\nclass RadialAxialShadingPattern extends BaseShadingPattern {\n  constructor(IR) {\n    super();\n    this._type = IR[1];\n    this._bbox = IR[2];\n    this._colorStops = IR[3];\n    this._p0 = IR[4];\n    this._p1 = IR[5];\n    this._r0 = IR[6];\n    this._r1 = IR[7];\n    this.matrix = null;\n  }\n  _createGradient(ctx) {\n    let grad;\n    if (this._type === \"axial\") {\n      grad = ctx.createLinearGradient(this._p0[0], this._p0[1], this._p1[0], this._p1[1]);\n    } else if (this._type === \"radial\") {\n      grad = ctx.createRadialGradient(this._p0[0], this._p0[1], this._r0, this._p1[0], this._p1[1], this._r1);\n    }\n    for (const colorStop of this._colorStops) {\n      grad.addColorStop(colorStop[0], colorStop[1]);\n    }\n    return grad;\n  }\n  getPattern(ctx, owner, inverse, pathType) {\n    let pattern;\n    if (pathType === PathType.STROKE || pathType === PathType.FILL) {\n      const ownerBBox = owner.current.getClippedPathBoundingBox(pathType, (0,display_utils.getCurrentTransform)(ctx)) || [0, 0, 0, 0];\n      const width = Math.ceil(ownerBBox[2] - ownerBBox[0]) || 1;\n      const height = Math.ceil(ownerBBox[3] - ownerBBox[1]) || 1;\n      const tmpCanvas = owner.cachedCanvases.getCanvas(\"pattern\", width, height, true);\n      const tmpCtx = tmpCanvas.context;\n      tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);\n      tmpCtx.beginPath();\n      tmpCtx.rect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);\n      tmpCtx.translate(-ownerBBox[0], -ownerBBox[1]);\n      inverse = util.Util.transform(inverse, [1, 0, 0, 1, ownerBBox[0], ownerBBox[1]]);\n      tmpCtx.transform(...owner.baseTransform);\n      if (this.matrix) {\n        tmpCtx.transform(...this.matrix);\n      }\n      applyBoundingBox(tmpCtx, this._bbox);\n      tmpCtx.fillStyle = this._createGradient(tmpCtx);\n      tmpCtx.fill();\n      pattern = ctx.createPattern(tmpCanvas.canvas, \"no-repeat\");\n      const domMatrix = new DOMMatrix(inverse);\n      pattern.setTransform(domMatrix);\n    } else {\n      applyBoundingBox(ctx, this._bbox);\n      pattern = this._createGradient(ctx);\n    }\n    return pattern;\n  }\n}\nfunction drawTriangle(data, context, p1, p2, p3, c1, c2, c3) {\n  const coords = context.coords,\n    colors = context.colors;\n  const bytes = data.data,\n    rowSize = data.width * 4;\n  let tmp;\n  if (coords[p1 + 1] > coords[p2 + 1]) {\n    tmp = p1;\n    p1 = p2;\n    p2 = tmp;\n    tmp = c1;\n    c1 = c2;\n    c2 = tmp;\n  }\n  if (coords[p2 + 1] > coords[p3 + 1]) {\n    tmp = p2;\n    p2 = p3;\n    p3 = tmp;\n    tmp = c2;\n    c2 = c3;\n    c3 = tmp;\n  }\n  if (coords[p1 + 1] > coords[p2 + 1]) {\n    tmp = p1;\n    p1 = p2;\n    p2 = tmp;\n    tmp = c1;\n    c1 = c2;\n    c2 = tmp;\n  }\n  const x1 = (coords[p1] + context.offsetX) * context.scaleX;\n  const y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY;\n  const x2 = (coords[p2] + context.offsetX) * context.scaleX;\n  const y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY;\n  const x3 = (coords[p3] + context.offsetX) * context.scaleX;\n  const y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY;\n  if (y1 >= y3) {\n    return;\n  }\n  const c1r = colors[c1],\n    c1g = colors[c1 + 1],\n    c1b = colors[c1 + 2];\n  const c2r = colors[c2],\n    c2g = colors[c2 + 1],\n    c2b = colors[c2 + 2];\n  const c3r = colors[c3],\n    c3g = colors[c3 + 1],\n    c3b = colors[c3 + 2];\n  const minY = Math.round(y1),\n    maxY = Math.round(y3);\n  let xa, car, cag, cab;\n  let xb, cbr, cbg, cbb;\n  for (let y = minY; y <= maxY; y++) {\n    if (y < y2) {\n      const k = y < y1 ? 0 : (y1 - y) / (y1 - y2);\n      xa = x1 - (x1 - x2) * k;\n      car = c1r - (c1r - c2r) * k;\n      cag = c1g - (c1g - c2g) * k;\n      cab = c1b - (c1b - c2b) * k;\n    } else {\n      let k;\n      if (y > y3) {\n        k = 1;\n      } else if (y2 === y3) {\n        k = 0;\n      } else {\n        k = (y2 - y) / (y2 - y3);\n      }\n      xa = x2 - (x2 - x3) * k;\n      car = c2r - (c2r - c3r) * k;\n      cag = c2g - (c2g - c3g) * k;\n      cab = c2b - (c2b - c3b) * k;\n    }\n    let k;\n    if (y < y1) {\n      k = 0;\n    } else if (y > y3) {\n      k = 1;\n    } else {\n      k = (y1 - y) / (y1 - y3);\n    }\n    xb = x1 - (x1 - x3) * k;\n    cbr = c1r - (c1r - c3r) * k;\n    cbg = c1g - (c1g - c3g) * k;\n    cbb = c1b - (c1b - c3b) * k;\n    const x1_ = Math.round(Math.min(xa, xb));\n    const x2_ = Math.round(Math.max(xa, xb));\n    let j = rowSize * y + x1_ * 4;\n    for (let x = x1_; x <= x2_; x++) {\n      k = (xa - x) / (xa - xb);\n      if (k < 0) {\n        k = 0;\n      } else if (k > 1) {\n        k = 1;\n      }\n      bytes[j++] = car - (car - cbr) * k | 0;\n      bytes[j++] = cag - (cag - cbg) * k | 0;\n      bytes[j++] = cab - (cab - cbb) * k | 0;\n      bytes[j++] = 255;\n    }\n  }\n}\nfunction drawFigure(data, figure, context) {\n  const ps = figure.coords;\n  const cs = figure.colors;\n  let i, ii;\n  switch (figure.type) {\n    case \"lattice\":\n      const verticesPerRow = figure.verticesPerRow;\n      const rows = Math.floor(ps.length / verticesPerRow) - 1;\n      const cols = verticesPerRow - 1;\n      for (i = 0; i < rows; i++) {\n        let q = i * verticesPerRow;\n        for (let j = 0; j < cols; j++, q++) {\n          drawTriangle(data, context, ps[q], ps[q + 1], ps[q + verticesPerRow], cs[q], cs[q + 1], cs[q + verticesPerRow]);\n          drawTriangle(data, context, ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow], cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]);\n        }\n      }\n      break;\n    case \"triangles\":\n      for (i = 0, ii = ps.length; i < ii; i += 3) {\n        drawTriangle(data, context, ps[i], ps[i + 1], ps[i + 2], cs[i], cs[i + 1], cs[i + 2]);\n      }\n      break;\n    default:\n      throw new Error(\"illegal figure\");\n  }\n}\nclass MeshShadingPattern extends BaseShadingPattern {\n  constructor(IR) {\n    super();\n    this._coords = IR[2];\n    this._colors = IR[3];\n    this._figures = IR[4];\n    this._bounds = IR[5];\n    this._bbox = IR[7];\n    this._background = IR[8];\n    this.matrix = null;\n  }\n  _createMeshCanvas(combinedScale, backgroundColor, cachedCanvases) {\n    const EXPECTED_SCALE = 1.1;\n    const MAX_PATTERN_SIZE = 3000;\n    const BORDER_SIZE = 2;\n    const offsetX = Math.floor(this._bounds[0]);\n    const offsetY = Math.floor(this._bounds[1]);\n    const boundsWidth = Math.ceil(this._bounds[2]) - offsetX;\n    const boundsHeight = Math.ceil(this._bounds[3]) - offsetY;\n    const width = Math.min(Math.ceil(Math.abs(boundsWidth * combinedScale[0] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);\n    const height = Math.min(Math.ceil(Math.abs(boundsHeight * combinedScale[1] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);\n    const scaleX = boundsWidth / width;\n    const scaleY = boundsHeight / height;\n    const context = {\n      coords: this._coords,\n      colors: this._colors,\n      offsetX: -offsetX,\n      offsetY: -offsetY,\n      scaleX: 1 / scaleX,\n      scaleY: 1 / scaleY\n    };\n    const paddedWidth = width + BORDER_SIZE * 2;\n    const paddedHeight = height + BORDER_SIZE * 2;\n    const tmpCanvas = cachedCanvases.getCanvas(\"mesh\", paddedWidth, paddedHeight, false);\n    const tmpCtx = tmpCanvas.context;\n    const data = tmpCtx.createImageData(width, height);\n    if (backgroundColor) {\n      const bytes = data.data;\n      for (let i = 0, ii = bytes.length; i < ii; i += 4) {\n        bytes[i] = backgroundColor[0];\n        bytes[i + 1] = backgroundColor[1];\n        bytes[i + 2] = backgroundColor[2];\n        bytes[i + 3] = 255;\n      }\n    }\n    for (const figure of this._figures) {\n      drawFigure(data, figure, context);\n    }\n    tmpCtx.putImageData(data, BORDER_SIZE, BORDER_SIZE);\n    const canvas = tmpCanvas.canvas;\n    return {\n      canvas,\n      offsetX: offsetX - BORDER_SIZE * scaleX,\n      offsetY: offsetY - BORDER_SIZE * scaleY,\n      scaleX,\n      scaleY\n    };\n  }\n  getPattern(ctx, owner, inverse, pathType) {\n    applyBoundingBox(ctx, this._bbox);\n    let scale;\n    if (pathType === PathType.SHADING) {\n      scale = util.Util.singularValueDecompose2dScale((0,display_utils.getCurrentTransform)(ctx));\n    } else {\n      scale = util.Util.singularValueDecompose2dScale(owner.baseTransform);\n      if (this.matrix) {\n        const matrixScale = util.Util.singularValueDecompose2dScale(this.matrix);\n        scale = [scale[0] * matrixScale[0], scale[1] * matrixScale[1]];\n      }\n    }\n    const temporaryPatternCanvas = this._createMeshCanvas(scale, pathType === PathType.SHADING ? null : this._background, owner.cachedCanvases);\n    if (pathType !== PathType.SHADING) {\n      ctx.setTransform(...owner.baseTransform);\n      if (this.matrix) {\n        ctx.transform(...this.matrix);\n      }\n    }\n    ctx.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY);\n    ctx.scale(temporaryPatternCanvas.scaleX, temporaryPatternCanvas.scaleY);\n    return ctx.createPattern(temporaryPatternCanvas.canvas, \"no-repeat\");\n  }\n}\nclass DummyShadingPattern extends BaseShadingPattern {\n  getPattern() {\n    return \"hotpink\";\n  }\n}\nfunction getShadingPattern(IR) {\n  switch (IR[0]) {\n    case \"RadialAxial\":\n      return new RadialAxialShadingPattern(IR);\n    case \"Mesh\":\n      return new MeshShadingPattern(IR);\n    case \"Dummy\":\n      return new DummyShadingPattern();\n  }\n  throw new Error(`Unknown IR type: ${IR[0]}`);\n}\nconst PaintType = {\n  COLORED: 1,\n  UNCOLORED: 2\n};\nclass TilingPattern {\n  static MAX_PATTERN_SIZE = 3000;\n  constructor(IR, color, ctx, canvasGraphicsFactory, baseTransform) {\n    this.operatorList = IR[2];\n    this.matrix = IR[3] || [1, 0, 0, 1, 0, 0];\n    this.bbox = IR[4];\n    this.xstep = IR[5];\n    this.ystep = IR[6];\n    this.paintType = IR[7];\n    this.tilingType = IR[8];\n    this.color = color;\n    this.ctx = ctx;\n    this.canvasGraphicsFactory = canvasGraphicsFactory;\n    this.baseTransform = baseTransform;\n  }\n  createPatternCanvas(owner) {\n    const operatorList = this.operatorList;\n    const bbox = this.bbox;\n    const xstep = this.xstep;\n    const ystep = this.ystep;\n    const paintType = this.paintType;\n    const tilingType = this.tilingType;\n    const color = this.color;\n    const canvasGraphicsFactory = this.canvasGraphicsFactory;\n    (0,util.info)(\"TilingType: \" + tilingType);\n    const x0 = bbox[0],\n      y0 = bbox[1],\n      x1 = bbox[2],\n      y1 = bbox[3];\n    const matrixScale = util.Util.singularValueDecompose2dScale(this.matrix);\n    const curMatrixScale = util.Util.singularValueDecompose2dScale(this.baseTransform);\n    const combinedScale = [matrixScale[0] * curMatrixScale[0], matrixScale[1] * curMatrixScale[1]];\n    const dimx = this.getSizeAndScale(xstep, this.ctx.canvas.width, combinedScale[0]);\n    const dimy = this.getSizeAndScale(ystep, this.ctx.canvas.height, combinedScale[1]);\n    const tmpCanvas = owner.cachedCanvases.getCanvas(\"pattern\", dimx.size, dimy.size, true);\n    const tmpCtx = tmpCanvas.context;\n    const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx);\n    graphics.groupLevel = owner.groupLevel;\n    this.setFillAndStrokeStyleToContext(graphics, paintType, color);\n    let adjustedX0 = x0;\n    let adjustedY0 = y0;\n    let adjustedX1 = x1;\n    let adjustedY1 = y1;\n    if (x0 < 0) {\n      adjustedX0 = 0;\n      adjustedX1 += Math.abs(x0);\n    }\n    if (y0 < 0) {\n      adjustedY0 = 0;\n      adjustedY1 += Math.abs(y0);\n    }\n    tmpCtx.translate(-(dimx.scale * adjustedX0), -(dimy.scale * adjustedY0));\n    graphics.transform(dimx.scale, 0, 0, dimy.scale, 0, 0);\n    tmpCtx.save();\n    this.clipBbox(graphics, adjustedX0, adjustedY0, adjustedX1, adjustedY1);\n    graphics.baseTransform = (0,display_utils.getCurrentTransform)(graphics.ctx);\n    graphics.executeOperatorList(operatorList);\n    graphics.endDrawing();\n    return {\n      canvas: tmpCanvas.canvas,\n      scaleX: dimx.scale,\n      scaleY: dimy.scale,\n      offsetX: adjustedX0,\n      offsetY: adjustedY0\n    };\n  }\n  getSizeAndScale(step, realOutputSize, scale) {\n    step = Math.abs(step);\n    const maxSize = Math.max(TilingPattern.MAX_PATTERN_SIZE, realOutputSize);\n    let size = Math.ceil(step * scale);\n    if (size >= maxSize) {\n      size = maxSize;\n    } else {\n      scale = size / step;\n    }\n    return {\n      scale,\n      size\n    };\n  }\n  clipBbox(graphics, x0, y0, x1, y1) {\n    const bboxWidth = x1 - x0;\n    const bboxHeight = y1 - y0;\n    graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);\n    graphics.current.updateRectMinMax((0,display_utils.getCurrentTransform)(graphics.ctx), [x0, y0, x1, y1]);\n    graphics.clip();\n    graphics.endPath();\n  }\n  setFillAndStrokeStyleToContext(graphics, paintType, color) {\n    const context = graphics.ctx,\n      current = graphics.current;\n    switch (paintType) {\n      case PaintType.COLORED:\n        const ctx = this.ctx;\n        context.fillStyle = ctx.fillStyle;\n        context.strokeStyle = ctx.strokeStyle;\n        current.fillColor = ctx.fillStyle;\n        current.strokeColor = ctx.strokeStyle;\n        break;\n      case PaintType.UNCOLORED:\n        const cssColor = util.Util.makeHexColor(color[0], color[1], color[2]);\n        context.fillStyle = cssColor;\n        context.strokeStyle = cssColor;\n        current.fillColor = cssColor;\n        current.strokeColor = cssColor;\n        break;\n      default:\n        throw new util.FormatError(`Unsupported paint type: ${paintType}`);\n    }\n  }\n  getPattern(ctx, owner, inverse, pathType) {\n    let matrix = inverse;\n    if (pathType !== PathType.SHADING) {\n      matrix = util.Util.transform(matrix, owner.baseTransform);\n      if (this.matrix) {\n        matrix = util.Util.transform(matrix, this.matrix);\n      }\n    }\n    const temporaryPatternCanvas = this.createPatternCanvas(owner);\n    let domMatrix = new DOMMatrix(matrix);\n    domMatrix = domMatrix.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY);\n    domMatrix = domMatrix.scale(1 / temporaryPatternCanvas.scaleX, 1 / temporaryPatternCanvas.scaleY);\n    const pattern = ctx.createPattern(temporaryPatternCanvas.canvas, \"repeat\");\n    pattern.setTransform(domMatrix);\n    return pattern;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/shared/image_utils.js\n\nfunction convertToRGBA(params) {\n  switch (params.kind) {\n    case ImageKind.GRAYSCALE_1BPP:\n      return convertBlackAndWhiteToRGBA(params);\n    case ImageKind.RGB_24BPP:\n      return convertRGBToRGBA(params);\n  }\n  return null;\n}\nfunction convertBlackAndWhiteToRGBA({\n  src,\n  srcPos = 0,\n  dest,\n  width,\n  height,\n  nonBlackColor = 0xffffffff,\n  inverseDecode = false\n}) {\n  const black = util.FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff;\n  const [zeroMapping, oneMapping] = inverseDecode ? [nonBlackColor, black] : [black, nonBlackColor];\n  const widthInSource = width >> 3;\n  const widthRemainder = width & 7;\n  const srcLength = src.length;\n  dest = new Uint32Array(dest.buffer);\n  let destPos = 0;\n  for (let i = 0; i < height; i++) {\n    for (const max = srcPos + widthInSource; srcPos < max; srcPos++) {\n      const elem = srcPos < srcLength ? src[srcPos] : 255;\n      dest[destPos++] = elem & 0b10000000 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b1000000 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b100000 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b10000 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b1000 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b100 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b10 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b1 ? oneMapping : zeroMapping;\n    }\n    if (widthRemainder === 0) {\n      continue;\n    }\n    const elem = srcPos < srcLength ? src[srcPos++] : 255;\n    for (let j = 0; j < widthRemainder; j++) {\n      dest[destPos++] = elem & 1 << 7 - j ? oneMapping : zeroMapping;\n    }\n  }\n  return {\n    srcPos,\n    destPos\n  };\n}\nfunction convertRGBToRGBA({\n  src,\n  srcPos = 0,\n  dest,\n  destPos = 0,\n  width,\n  height\n}) {\n  let i = 0;\n  const len32 = src.length >> 2;\n  const src32 = new Uint32Array(src.buffer, srcPos, len32);\n  if (FeatureTest.isLittleEndian) {\n    for (; i < len32 - 2; i += 3, destPos += 4) {\n      const s1 = src32[i];\n      const s2 = src32[i + 1];\n      const s3 = src32[i + 2];\n      dest[destPos] = s1 | 0xff000000;\n      dest[destPos + 1] = s1 >>> 24 | s2 << 8 | 0xff000000;\n      dest[destPos + 2] = s2 >>> 16 | s3 << 16 | 0xff000000;\n      dest[destPos + 3] = s3 >>> 8 | 0xff000000;\n    }\n    for (let j = i * 4, jj = src.length; j < jj; j += 3) {\n      dest[destPos++] = src[j] | src[j + 1] << 8 | src[j + 2] << 16 | 0xff000000;\n    }\n  } else {\n    for (; i < len32 - 2; i += 3, destPos += 4) {\n      const s1 = src32[i];\n      const s2 = src32[i + 1];\n      const s3 = src32[i + 2];\n      dest[destPos] = s1 | 0xff;\n      dest[destPos + 1] = s1 << 24 | s2 >>> 8 | 0xff;\n      dest[destPos + 2] = s2 << 16 | s3 >>> 16 | 0xff;\n      dest[destPos + 3] = s3 << 8 | 0xff;\n    }\n    for (let j = i * 4, jj = src.length; j < jj; j += 3) {\n      dest[destPos++] = src[j] << 24 | src[j + 1] << 16 | src[j + 2] << 8 | 0xff;\n    }\n  }\n  return {\n    srcPos,\n    destPos\n  };\n}\nfunction grayToRGBA(src, dest) {\n  if (FeatureTest.isLittleEndian) {\n    for (let i = 0, ii = src.length; i < ii; i++) {\n      dest[i] = src[i] * 0x10101 | 0xff000000;\n    }\n  } else {\n    for (let i = 0, ii = src.length; i < ii; i++) {\n      dest[i] = src[i] * 0x1010100 | 0x000000ff;\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/display/canvas.js\n\n\n\n\nconst MIN_FONT_SIZE = 16;\nconst MAX_FONT_SIZE = 100;\nconst MAX_GROUP_SIZE = 4096;\nconst EXECUTION_TIME = 15;\nconst EXECUTION_STEPS = 10;\nconst MAX_SIZE_TO_COMPILE = 1000;\nconst FULL_CHUNK_HEIGHT = 16;\nfunction mirrorContextOperations(ctx, destCtx) {\n  if (ctx._removeMirroring) {\n    throw new Error(\"Context is already forwarding operations.\");\n  }\n  ctx.__originalSave = ctx.save;\n  ctx.__originalRestore = ctx.restore;\n  ctx.__originalRotate = ctx.rotate;\n  ctx.__originalScale = ctx.scale;\n  ctx.__originalTranslate = ctx.translate;\n  ctx.__originalTransform = ctx.transform;\n  ctx.__originalSetTransform = ctx.setTransform;\n  ctx.__originalResetTransform = ctx.resetTransform;\n  ctx.__originalClip = ctx.clip;\n  ctx.__originalMoveTo = ctx.moveTo;\n  ctx.__originalLineTo = ctx.lineTo;\n  ctx.__originalBezierCurveTo = ctx.bezierCurveTo;\n  ctx.__originalRect = ctx.rect;\n  ctx.__originalClosePath = ctx.closePath;\n  ctx.__originalBeginPath = ctx.beginPath;\n  ctx._removeMirroring = () => {\n    ctx.save = ctx.__originalSave;\n    ctx.restore = ctx.__originalRestore;\n    ctx.rotate = ctx.__originalRotate;\n    ctx.scale = ctx.__originalScale;\n    ctx.translate = ctx.__originalTranslate;\n    ctx.transform = ctx.__originalTransform;\n    ctx.setTransform = ctx.__originalSetTransform;\n    ctx.resetTransform = ctx.__originalResetTransform;\n    ctx.clip = ctx.__originalClip;\n    ctx.moveTo = ctx.__originalMoveTo;\n    ctx.lineTo = ctx.__originalLineTo;\n    ctx.bezierCurveTo = ctx.__originalBezierCurveTo;\n    ctx.rect = ctx.__originalRect;\n    ctx.closePath = ctx.__originalClosePath;\n    ctx.beginPath = ctx.__originalBeginPath;\n    delete ctx._removeMirroring;\n  };\n  ctx.save = function ctxSave() {\n    destCtx.save();\n    this.__originalSave();\n  };\n  ctx.restore = function ctxRestore() {\n    destCtx.restore();\n    this.__originalRestore();\n  };\n  ctx.translate = function ctxTranslate(x, y) {\n    destCtx.translate(x, y);\n    this.__originalTranslate(x, y);\n  };\n  ctx.scale = function ctxScale(x, y) {\n    destCtx.scale(x, y);\n    this.__originalScale(x, y);\n  };\n  ctx.transform = function ctxTransform(a, b, c, d, e, f) {\n    destCtx.transform(a, b, c, d, e, f);\n    this.__originalTransform(a, b, c, d, e, f);\n  };\n  ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) {\n    destCtx.setTransform(a, b, c, d, e, f);\n    this.__originalSetTransform(a, b, c, d, e, f);\n  };\n  ctx.resetTransform = function ctxResetTransform() {\n    destCtx.resetTransform();\n    this.__originalResetTransform();\n  };\n  ctx.rotate = function ctxRotate(angle) {\n    destCtx.rotate(angle);\n    this.__originalRotate(angle);\n  };\n  ctx.clip = function ctxRotate(rule) {\n    destCtx.clip(rule);\n    this.__originalClip(rule);\n  };\n  ctx.moveTo = function (x, y) {\n    destCtx.moveTo(x, y);\n    this.__originalMoveTo(x, y);\n  };\n  ctx.lineTo = function (x, y) {\n    destCtx.lineTo(x, y);\n    this.__originalLineTo(x, y);\n  };\n  ctx.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) {\n    destCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);\n    this.__originalBezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);\n  };\n  ctx.rect = function (x, y, width, height) {\n    destCtx.rect(x, y, width, height);\n    this.__originalRect(x, y, width, height);\n  };\n  ctx.closePath = function () {\n    destCtx.closePath();\n    this.__originalClosePath();\n  };\n  ctx.beginPath = function () {\n    destCtx.beginPath();\n    this.__originalBeginPath();\n  };\n}\nclass CachedCanvases {\n  constructor(canvasFactory) {\n    this.canvasFactory = canvasFactory;\n    this.cache = Object.create(null);\n  }\n  getCanvas(id, width, height) {\n    let canvasEntry;\n    if (this.cache[id] !== undefined) {\n      canvasEntry = this.cache[id];\n      this.canvasFactory.reset(canvasEntry, width, height);\n    } else {\n      canvasEntry = this.canvasFactory.create(width, height);\n      this.cache[id] = canvasEntry;\n    }\n    return canvasEntry;\n  }\n  delete(id) {\n    delete this.cache[id];\n  }\n  clear() {\n    for (const id in this.cache) {\n      const canvasEntry = this.cache[id];\n      this.canvasFactory.destroy(canvasEntry);\n      delete this.cache[id];\n    }\n  }\n}\nfunction drawImageAtIntegerCoords(ctx, srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH) {\n  const [a, b, c, d, tx, ty] = (0,display_utils.getCurrentTransform)(ctx);\n  if (b === 0 && c === 0) {\n    const tlX = destX * a + tx;\n    const rTlX = Math.round(tlX);\n    const tlY = destY * d + ty;\n    const rTlY = Math.round(tlY);\n    const brX = (destX + destW) * a + tx;\n    const rWidth = Math.abs(Math.round(brX) - rTlX) || 1;\n    const brY = (destY + destH) * d + ty;\n    const rHeight = Math.abs(Math.round(brY) - rTlY) || 1;\n    ctx.setTransform(Math.sign(a), 0, 0, Math.sign(d), rTlX, rTlY);\n    ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rWidth, rHeight);\n    ctx.setTransform(a, b, c, d, tx, ty);\n    return [rWidth, rHeight];\n  }\n  if (a === 0 && d === 0) {\n    const tlX = destY * c + tx;\n    const rTlX = Math.round(tlX);\n    const tlY = destX * b + ty;\n    const rTlY = Math.round(tlY);\n    const brX = (destY + destH) * c + tx;\n    const rWidth = Math.abs(Math.round(brX) - rTlX) || 1;\n    const brY = (destX + destW) * b + ty;\n    const rHeight = Math.abs(Math.round(brY) - rTlY) || 1;\n    ctx.setTransform(0, Math.sign(b), Math.sign(c), 0, rTlX, rTlY);\n    ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rHeight, rWidth);\n    ctx.setTransform(a, b, c, d, tx, ty);\n    return [rHeight, rWidth];\n  }\n  ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH);\n  const scaleX = Math.hypot(a, b);\n  const scaleY = Math.hypot(c, d);\n  return [scaleX * destW, scaleY * destH];\n}\nfunction compileType3Glyph(imgData) {\n  const {\n    width,\n    height\n  } = imgData;\n  if (width > MAX_SIZE_TO_COMPILE || height > MAX_SIZE_TO_COMPILE) {\n    return null;\n  }\n  const POINT_TO_PROCESS_LIMIT = 1000;\n  const POINT_TYPES = new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]);\n  const width1 = width + 1;\n  let points = new Uint8Array(width1 * (height + 1));\n  let i, j, j0;\n  const lineSize = width + 7 & ~7;\n  let data = new Uint8Array(lineSize * height),\n    pos = 0;\n  for (const elem of imgData.data) {\n    let mask = 128;\n    while (mask > 0) {\n      data[pos++] = elem & mask ? 0 : 255;\n      mask >>= 1;\n    }\n  }\n  let count = 0;\n  pos = 0;\n  if (data[pos] !== 0) {\n    points[0] = 1;\n    ++count;\n  }\n  for (j = 1; j < width; j++) {\n    if (data[pos] !== data[pos + 1]) {\n      points[j] = data[pos] ? 2 : 1;\n      ++count;\n    }\n    pos++;\n  }\n  if (data[pos] !== 0) {\n    points[j] = 2;\n    ++count;\n  }\n  for (i = 1; i < height; i++) {\n    pos = i * lineSize;\n    j0 = i * width1;\n    if (data[pos - lineSize] !== data[pos]) {\n      points[j0] = data[pos] ? 1 : 8;\n      ++count;\n    }\n    let sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0);\n    for (j = 1; j < width; j++) {\n      sum = (sum >> 2) + (data[pos + 1] ? 4 : 0) + (data[pos - lineSize + 1] ? 8 : 0);\n      if (POINT_TYPES[sum]) {\n        points[j0 + j] = POINT_TYPES[sum];\n        ++count;\n      }\n      pos++;\n    }\n    if (data[pos - lineSize] !== data[pos]) {\n      points[j0 + j] = data[pos] ? 2 : 4;\n      ++count;\n    }\n    if (count > POINT_TO_PROCESS_LIMIT) {\n      return null;\n    }\n  }\n  pos = lineSize * (height - 1);\n  j0 = i * width1;\n  if (data[pos] !== 0) {\n    points[j0] = 8;\n    ++count;\n  }\n  for (j = 1; j < width; j++) {\n    if (data[pos] !== data[pos + 1]) {\n      points[j0 + j] = data[pos] ? 4 : 8;\n      ++count;\n    }\n    pos++;\n  }\n  if (data[pos] !== 0) {\n    points[j0 + j] = 4;\n    ++count;\n  }\n  if (count > POINT_TO_PROCESS_LIMIT) {\n    return null;\n  }\n  const steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]);\n  const path = new Path2D();\n  for (i = 0; count && i <= height; i++) {\n    let p = i * width1;\n    const end = p + width;\n    while (p < end && !points[p]) {\n      p++;\n    }\n    if (p === end) {\n      continue;\n    }\n    path.moveTo(p % width1, i);\n    const p0 = p;\n    let type = points[p];\n    do {\n      const step = steps[type];\n      do {\n        p += step;\n      } while (!points[p]);\n      const pp = points[p];\n      if (pp !== 5 && pp !== 10) {\n        type = pp;\n        points[p] = 0;\n      } else {\n        type = pp & 0x33 * type >> 4;\n        points[p] &= type >> 2 | type << 2;\n      }\n      path.lineTo(p % width1, p / width1 | 0);\n      if (!points[p]) {\n        --count;\n      }\n    } while (p0 !== p);\n    --i;\n  }\n  data = null;\n  points = null;\n  const drawOutline = function (c) {\n    c.save();\n    c.scale(1 / width, -1 / height);\n    c.translate(0, -height);\n    c.fill(path);\n    c.beginPath();\n    c.restore();\n  };\n  return drawOutline;\n}\nclass CanvasExtraState {\n  constructor(width, height) {\n    this.alphaIsShape = false;\n    this.fontSize = 0;\n    this.fontSizeScale = 1;\n    this.textMatrix = util.IDENTITY_MATRIX;\n    this.textMatrixScale = 1;\n    this.fontMatrix = util.FONT_IDENTITY_MATRIX;\n    this.leading = 0;\n    this.x = 0;\n    this.y = 0;\n    this.lineX = 0;\n    this.lineY = 0;\n    this.charSpacing = 0;\n    this.wordSpacing = 0;\n    this.textHScale = 1;\n    this.textRenderingMode = util.TextRenderingMode.FILL;\n    this.textRise = 0;\n    this.fillColor = \"#000000\";\n    this.strokeColor = \"#000000\";\n    this.patternFill = false;\n    this.fillAlpha = 1;\n    this.strokeAlpha = 1;\n    this.lineWidth = 1;\n    this.activeSMask = null;\n    this.transferMaps = \"none\";\n    this.startNewPathAndClipBox([0, 0, width, height]);\n  }\n  clone() {\n    const clone = Object.create(this);\n    clone.clipBox = this.clipBox.slice();\n    return clone;\n  }\n  setCurrentPoint(x, y) {\n    this.x = x;\n    this.y = y;\n  }\n  updatePathMinMax(transform, x, y) {\n    [x, y] = util.Util.applyTransform([x, y], transform);\n    this.minX = Math.min(this.minX, x);\n    this.minY = Math.min(this.minY, y);\n    this.maxX = Math.max(this.maxX, x);\n    this.maxY = Math.max(this.maxY, y);\n  }\n  updateRectMinMax(transform, rect) {\n    const p1 = util.Util.applyTransform(rect, transform);\n    const p2 = util.Util.applyTransform(rect.slice(2), transform);\n    const p3 = util.Util.applyTransform([rect[0], rect[3]], transform);\n    const p4 = util.Util.applyTransform([rect[2], rect[1]], transform);\n    this.minX = Math.min(this.minX, p1[0], p2[0], p3[0], p4[0]);\n    this.minY = Math.min(this.minY, p1[1], p2[1], p3[1], p4[1]);\n    this.maxX = Math.max(this.maxX, p1[0], p2[0], p3[0], p4[0]);\n    this.maxY = Math.max(this.maxY, p1[1], p2[1], p3[1], p4[1]);\n  }\n  updateScalingPathMinMax(transform, minMax) {\n    util.Util.scaleMinMax(transform, minMax);\n    this.minX = Math.min(this.minX, minMax[0]);\n    this.maxX = Math.max(this.maxX, minMax[1]);\n    this.minY = Math.min(this.minY, minMax[2]);\n    this.maxY = Math.max(this.maxY, minMax[3]);\n  }\n  updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3, minMax) {\n    const box = util.Util.bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3);\n    if (minMax) {\n      minMax[0] = Math.min(minMax[0], box[0], box[2]);\n      minMax[1] = Math.max(minMax[1], box[0], box[2]);\n      minMax[2] = Math.min(minMax[2], box[1], box[3]);\n      minMax[3] = Math.max(minMax[3], box[1], box[3]);\n      return;\n    }\n    this.updateRectMinMax(transform, box);\n  }\n  getPathBoundingBox(pathType = PathType.FILL, transform = null) {\n    const box = [this.minX, this.minY, this.maxX, this.maxY];\n    if (pathType === PathType.STROKE) {\n      if (!transform) {\n        (0,util.unreachable)(\"Stroke bounding box must include transform.\");\n      }\n      const scale = util.Util.singularValueDecompose2dScale(transform);\n      const xStrokePad = scale[0] * this.lineWidth / 2;\n      const yStrokePad = scale[1] * this.lineWidth / 2;\n      box[0] -= xStrokePad;\n      box[1] -= yStrokePad;\n      box[2] += xStrokePad;\n      box[3] += yStrokePad;\n    }\n    return box;\n  }\n  updateClipFromPath() {\n    const intersect = util.Util.intersect(this.clipBox, this.getPathBoundingBox());\n    this.startNewPathAndClipBox(intersect || [0, 0, 0, 0]);\n  }\n  isEmptyClip() {\n    return this.minX === Infinity;\n  }\n  startNewPathAndClipBox(box) {\n    this.clipBox = box;\n    this.minX = Infinity;\n    this.minY = Infinity;\n    this.maxX = 0;\n    this.maxY = 0;\n  }\n  getClippedPathBoundingBox(pathType = PathType.FILL, transform = null) {\n    return util.Util.intersect(this.clipBox, this.getPathBoundingBox(pathType, transform));\n  }\n}\nfunction putBinaryImageData(ctx, imgData) {\n  if (typeof ImageData !== \"undefined\" && imgData instanceof ImageData) {\n    ctx.putImageData(imgData, 0, 0);\n    return;\n  }\n  const height = imgData.height,\n    width = imgData.width;\n  const partialChunkHeight = height % FULL_CHUNK_HEIGHT;\n  const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;\n  const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;\n  const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);\n  let srcPos = 0,\n    destPos;\n  const src = imgData.data;\n  const dest = chunkImgData.data;\n  let i, j, thisChunkHeight, elemsInThisChunk;\n  if (imgData.kind === util.ImageKind.GRAYSCALE_1BPP) {\n    const srcLength = src.byteLength;\n    const dest32 = new Uint32Array(dest.buffer, 0, dest.byteLength >> 2);\n    const dest32DataLength = dest32.length;\n    const fullSrcDiff = width + 7 >> 3;\n    const white = 0xffffffff;\n    const black = util.FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff;\n    for (i = 0; i < totalChunks; i++) {\n      thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;\n      destPos = 0;\n      for (j = 0; j < thisChunkHeight; j++) {\n        const srcDiff = srcLength - srcPos;\n        let k = 0;\n        const kEnd = srcDiff > fullSrcDiff ? width : srcDiff * 8 - 7;\n        const kEndUnrolled = kEnd & ~7;\n        let mask = 0;\n        let srcByte = 0;\n        for (; k < kEndUnrolled; k += 8) {\n          srcByte = src[srcPos++];\n          dest32[destPos++] = srcByte & 128 ? white : black;\n          dest32[destPos++] = srcByte & 64 ? white : black;\n          dest32[destPos++] = srcByte & 32 ? white : black;\n          dest32[destPos++] = srcByte & 16 ? white : black;\n          dest32[destPos++] = srcByte & 8 ? white : black;\n          dest32[destPos++] = srcByte & 4 ? white : black;\n          dest32[destPos++] = srcByte & 2 ? white : black;\n          dest32[destPos++] = srcByte & 1 ? white : black;\n        }\n        for (; k < kEnd; k++) {\n          if (mask === 0) {\n            srcByte = src[srcPos++];\n            mask = 128;\n          }\n          dest32[destPos++] = srcByte & mask ? white : black;\n          mask >>= 1;\n        }\n      }\n      while (destPos < dest32DataLength) {\n        dest32[destPos++] = 0;\n      }\n      ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);\n    }\n  } else if (imgData.kind === util.ImageKind.RGBA_32BPP) {\n    j = 0;\n    elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4;\n    for (i = 0; i < fullChunks; i++) {\n      dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));\n      srcPos += elemsInThisChunk;\n      ctx.putImageData(chunkImgData, 0, j);\n      j += FULL_CHUNK_HEIGHT;\n    }\n    if (i < totalChunks) {\n      elemsInThisChunk = width * partialChunkHeight * 4;\n      dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));\n      ctx.putImageData(chunkImgData, 0, j);\n    }\n  } else if (imgData.kind === util.ImageKind.RGB_24BPP) {\n    thisChunkHeight = FULL_CHUNK_HEIGHT;\n    elemsInThisChunk = width * thisChunkHeight;\n    for (i = 0; i < totalChunks; i++) {\n      if (i >= fullChunks) {\n        thisChunkHeight = partialChunkHeight;\n        elemsInThisChunk = width * thisChunkHeight;\n      }\n      destPos = 0;\n      for (j = elemsInThisChunk; j--;) {\n        dest[destPos++] = src[srcPos++];\n        dest[destPos++] = src[srcPos++];\n        dest[destPos++] = src[srcPos++];\n        dest[destPos++] = 255;\n      }\n      ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);\n    }\n  } else {\n    throw new Error(`bad image kind: ${imgData.kind}`);\n  }\n}\nfunction putBinaryImageMask(ctx, imgData) {\n  if (imgData.bitmap) {\n    ctx.drawImage(imgData.bitmap, 0, 0);\n    return;\n  }\n  const height = imgData.height,\n    width = imgData.width;\n  const partialChunkHeight = height % FULL_CHUNK_HEIGHT;\n  const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;\n  const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;\n  const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);\n  let srcPos = 0;\n  const src = imgData.data;\n  const dest = chunkImgData.data;\n  for (let i = 0; i < totalChunks; i++) {\n    const thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;\n    ({\n      srcPos\n    } = convertBlackAndWhiteToRGBA({\n      src,\n      srcPos,\n      dest,\n      width,\n      height: thisChunkHeight,\n      nonBlackColor: 0\n    }));\n    ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);\n  }\n}\nfunction copyCtxState(sourceCtx, destCtx) {\n  const properties = [\"strokeStyle\", \"fillStyle\", \"fillRule\", \"globalAlpha\", \"lineWidth\", \"lineCap\", \"lineJoin\", \"miterLimit\", \"globalCompositeOperation\", \"font\", \"filter\"];\n  for (const property of properties) {\n    if (sourceCtx[property] !== undefined) {\n      destCtx[property] = sourceCtx[property];\n    }\n  }\n  if (sourceCtx.setLineDash !== undefined) {\n    destCtx.setLineDash(sourceCtx.getLineDash());\n    destCtx.lineDashOffset = sourceCtx.lineDashOffset;\n  }\n}\nfunction resetCtxToDefault(ctx) {\n  ctx.strokeStyle = ctx.fillStyle = \"#000000\";\n  ctx.fillRule = \"nonzero\";\n  ctx.globalAlpha = 1;\n  ctx.lineWidth = 1;\n  ctx.lineCap = \"butt\";\n  ctx.lineJoin = \"miter\";\n  ctx.miterLimit = 10;\n  ctx.globalCompositeOperation = \"source-over\";\n  ctx.font = \"10px sans-serif\";\n  if (ctx.setLineDash !== undefined) {\n    ctx.setLineDash([]);\n    ctx.lineDashOffset = 0;\n  }\n  if (!util.isNodeJS) {\n    const {\n      filter\n    } = ctx;\n    if (filter !== \"none\" && filter !== \"\") {\n      ctx.filter = \"none\";\n    }\n  }\n}\nfunction composeSMaskBackdrop(bytes, r0, g0, b0) {\n  const length = bytes.length;\n  for (let i = 3; i < length; i += 4) {\n    const alpha = bytes[i];\n    if (alpha === 0) {\n      bytes[i - 3] = r0;\n      bytes[i - 2] = g0;\n      bytes[i - 1] = b0;\n    } else if (alpha < 255) {\n      const alpha_ = 255 - alpha;\n      bytes[i - 3] = bytes[i - 3] * alpha + r0 * alpha_ >> 8;\n      bytes[i - 2] = bytes[i - 2] * alpha + g0 * alpha_ >> 8;\n      bytes[i - 1] = bytes[i - 1] * alpha + b0 * alpha_ >> 8;\n    }\n  }\n}\nfunction composeSMaskAlpha(maskData, layerData, transferMap) {\n  const length = maskData.length;\n  const scale = 1 / 255;\n  for (let i = 3; i < length; i += 4) {\n    const alpha = transferMap ? transferMap[maskData[i]] : maskData[i];\n    layerData[i] = layerData[i] * alpha * scale | 0;\n  }\n}\nfunction composeSMaskLuminosity(maskData, layerData, transferMap) {\n  const length = maskData.length;\n  for (let i = 3; i < length; i += 4) {\n    const y = maskData[i - 3] * 77 + maskData[i - 2] * 152 + maskData[i - 1] * 28;\n    layerData[i] = transferMap ? layerData[i] * transferMap[y >> 8] >> 8 : layerData[i] * y >> 16;\n  }\n}\nfunction genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop, transferMap, layerOffsetX, layerOffsetY, maskOffsetX, maskOffsetY) {\n  const hasBackdrop = !!backdrop;\n  const r0 = hasBackdrop ? backdrop[0] : 0;\n  const g0 = hasBackdrop ? backdrop[1] : 0;\n  const b0 = hasBackdrop ? backdrop[2] : 0;\n  const composeFn = subtype === \"Luminosity\" ? composeSMaskLuminosity : composeSMaskAlpha;\n  const PIXELS_TO_PROCESS = 1048576;\n  const chunkSize = Math.min(height, Math.ceil(PIXELS_TO_PROCESS / width));\n  for (let row = 0; row < height; row += chunkSize) {\n    const chunkHeight = Math.min(chunkSize, height - row);\n    const maskData = maskCtx.getImageData(layerOffsetX - maskOffsetX, row + (layerOffsetY - maskOffsetY), width, chunkHeight);\n    const layerData = layerCtx.getImageData(layerOffsetX, row + layerOffsetY, width, chunkHeight);\n    if (hasBackdrop) {\n      composeSMaskBackdrop(maskData.data, r0, g0, b0);\n    }\n    composeFn(maskData.data, layerData.data, transferMap);\n    layerCtx.putImageData(layerData, layerOffsetX, row + layerOffsetY);\n  }\n}\nfunction composeSMask(ctx, smask, layerCtx, layerBox) {\n  const layerOffsetX = layerBox[0];\n  const layerOffsetY = layerBox[1];\n  const layerWidth = layerBox[2] - layerOffsetX;\n  const layerHeight = layerBox[3] - layerOffsetY;\n  if (layerWidth === 0 || layerHeight === 0) {\n    return;\n  }\n  genericComposeSMask(smask.context, layerCtx, layerWidth, layerHeight, smask.subtype, smask.backdrop, smask.transferMap, layerOffsetX, layerOffsetY, smask.offsetX, smask.offsetY);\n  ctx.save();\n  ctx.globalAlpha = 1;\n  ctx.globalCompositeOperation = \"source-over\";\n  ctx.setTransform(1, 0, 0, 1, 0, 0);\n  ctx.drawImage(layerCtx.canvas, 0, 0);\n  ctx.restore();\n}\nfunction getImageSmoothingEnabled(transform, interpolate) {\n  const scale = util.Util.singularValueDecompose2dScale(transform);\n  scale[0] = Math.fround(scale[0]);\n  scale[1] = Math.fround(scale[1]);\n  const actualScale = Math.fround((globalThis.devicePixelRatio || 1) * display_utils.PixelsPerInch.PDF_TO_CSS_UNITS);\n  if (interpolate !== undefined) {\n    return interpolate;\n  } else if (scale[0] <= actualScale || scale[1] <= actualScale) {\n    return true;\n  }\n  return false;\n}\nconst LINE_CAP_STYLES = [\"butt\", \"round\", \"square\"];\nconst LINE_JOIN_STYLES = [\"miter\", \"round\", \"bevel\"];\nconst NORMAL_CLIP = {};\nconst EO_CLIP = {};\nclass CanvasGraphics {\n  constructor(canvasCtx, commonObjs, objs, canvasFactory, filterFactory, {\n    optionalContentConfig,\n    markedContentStack = null\n  }, annotationCanvasMap, pageColors) {\n    this.ctx = canvasCtx;\n    this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height);\n    this.stateStack = [];\n    this.pendingClip = null;\n    this.pendingEOFill = false;\n    this.res = null;\n    this.xobjs = null;\n    this.commonObjs = commonObjs;\n    this.objs = objs;\n    this.canvasFactory = canvasFactory;\n    this.filterFactory = filterFactory;\n    this.groupStack = [];\n    this.processingType3 = null;\n    this.baseTransform = null;\n    this.baseTransformStack = [];\n    this.groupLevel = 0;\n    this.smaskStack = [];\n    this.smaskCounter = 0;\n    this.tempSMask = null;\n    this.suspendedCtx = null;\n    this.contentVisible = true;\n    this.markedContentStack = markedContentStack || [];\n    this.optionalContentConfig = optionalContentConfig;\n    this.cachedCanvases = new CachedCanvases(this.canvasFactory);\n    this.cachedPatterns = new Map();\n    this.annotationCanvasMap = annotationCanvasMap;\n    this.viewportScale = 1;\n    this.outputScaleX = 1;\n    this.outputScaleY = 1;\n    this.pageColors = pageColors;\n    this._cachedScaleForStroking = [-1, 0];\n    this._cachedGetSinglePixelWidth = null;\n    this._cachedBitmapsMap = new Map();\n  }\n  getObject(data, fallback = null) {\n    if (typeof data === \"string\") {\n      return data.startsWith(\"g_\") ? this.commonObjs.get(data) : this.objs.get(data);\n    }\n    return fallback;\n  }\n  beginDrawing({\n    transform,\n    viewport,\n    transparency = false,\n    background = null\n  }) {\n    const width = this.ctx.canvas.width;\n    const height = this.ctx.canvas.height;\n    const savedFillStyle = this.ctx.fillStyle;\n    this.ctx.fillStyle = background || \"#ffffff\";\n    this.ctx.fillRect(0, 0, width, height);\n    this.ctx.fillStyle = savedFillStyle;\n    if (transparency) {\n      const transparentCanvas = this.cachedCanvases.getCanvas(\"transparent\", width, height);\n      this.compositeCtx = this.ctx;\n      this.transparentCanvas = transparentCanvas.canvas;\n      this.ctx = transparentCanvas.context;\n      this.ctx.save();\n      this.ctx.transform(...(0,display_utils.getCurrentTransform)(this.compositeCtx));\n    }\n    this.ctx.save();\n    resetCtxToDefault(this.ctx);\n    if (transform) {\n      this.ctx.transform(...transform);\n      this.outputScaleX = transform[0];\n      this.outputScaleY = transform[0];\n    }\n    this.ctx.transform(...viewport.transform);\n    this.viewportScale = viewport.scale;\n    this.baseTransform = (0,display_utils.getCurrentTransform)(this.ctx);\n  }\n  executeOperatorList(operatorList, executionStartIdx, continueCallback, stepper) {\n    const argsArray = operatorList.argsArray;\n    const fnArray = operatorList.fnArray;\n    let i = executionStartIdx || 0;\n    const argsArrayLen = argsArray.length;\n    if (argsArrayLen === i) {\n      return i;\n    }\n    const chunkOperations = argsArrayLen - i > EXECUTION_STEPS && typeof continueCallback === \"function\";\n    const endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0;\n    let steps = 0;\n    const commonObjs = this.commonObjs;\n    const objs = this.objs;\n    let fnId;\n    while (true) {\n      if (stepper !== undefined && i === stepper.nextBreakPoint) {\n        stepper.breakIt(i, continueCallback);\n        return i;\n      }\n      fnId = fnArray[i];\n      if (fnId !== util.OPS.dependency) {\n        this[fnId].apply(this, argsArray[i]);\n      } else {\n        for (const depObjId of argsArray[i]) {\n          const objsPool = depObjId.startsWith(\"g_\") ? commonObjs : objs;\n          if (!objsPool.has(depObjId)) {\n            objsPool.get(depObjId, continueCallback);\n            return i;\n          }\n        }\n      }\n      i++;\n      if (i === argsArrayLen) {\n        return i;\n      }\n      if (chunkOperations && ++steps > EXECUTION_STEPS) {\n        if (Date.now() > endTime) {\n          continueCallback();\n          return i;\n        }\n        steps = 0;\n      }\n    }\n  }\n  #restoreInitialState() {\n    while (this.stateStack.length || this.inSMaskMode) {\n      this.restore();\n    }\n    this.ctx.restore();\n    if (this.transparentCanvas) {\n      this.ctx = this.compositeCtx;\n      this.ctx.save();\n      this.ctx.setTransform(1, 0, 0, 1, 0, 0);\n      this.ctx.drawImage(this.transparentCanvas, 0, 0);\n      this.ctx.restore();\n      this.transparentCanvas = null;\n    }\n  }\n  endDrawing() {\n    this.#restoreInitialState();\n    this.cachedCanvases.clear();\n    this.cachedPatterns.clear();\n    for (const cache of this._cachedBitmapsMap.values()) {\n      for (const canvas of cache.values()) {\n        if (typeof HTMLCanvasElement !== \"undefined\" && canvas instanceof HTMLCanvasElement) {\n          canvas.width = canvas.height = 0;\n        }\n      }\n      cache.clear();\n    }\n    this._cachedBitmapsMap.clear();\n    this.#drawFilter();\n  }\n  #drawFilter() {\n    if (this.pageColors) {\n      const hcmFilterId = this.filterFactory.addHCMFilter(this.pageColors.foreground, this.pageColors.background);\n      if (hcmFilterId !== \"none\") {\n        const savedFilter = this.ctx.filter;\n        this.ctx.filter = hcmFilterId;\n        this.ctx.drawImage(this.ctx.canvas, 0, 0);\n        this.ctx.filter = savedFilter;\n      }\n    }\n  }\n  _scaleImage(img, inverseTransform) {\n    const width = img.width;\n    const height = img.height;\n    let widthScale = Math.max(Math.hypot(inverseTransform[0], inverseTransform[1]), 1);\n    let heightScale = Math.max(Math.hypot(inverseTransform[2], inverseTransform[3]), 1);\n    let paintWidth = width,\n      paintHeight = height;\n    let tmpCanvasId = \"prescale1\";\n    let tmpCanvas, tmpCtx;\n    while (widthScale > 2 && paintWidth > 1 || heightScale > 2 && paintHeight > 1) {\n      let newWidth = paintWidth,\n        newHeight = paintHeight;\n      if (widthScale > 2 && paintWidth > 1) {\n        newWidth = paintWidth >= 16384 ? Math.floor(paintWidth / 2) - 1 || 1 : Math.ceil(paintWidth / 2);\n        widthScale /= paintWidth / newWidth;\n      }\n      if (heightScale > 2 && paintHeight > 1) {\n        newHeight = paintHeight >= 16384 ? Math.floor(paintHeight / 2) - 1 || 1 : Math.ceil(paintHeight) / 2;\n        heightScale /= paintHeight / newHeight;\n      }\n      tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight);\n      tmpCtx = tmpCanvas.context;\n      tmpCtx.clearRect(0, 0, newWidth, newHeight);\n      tmpCtx.drawImage(img, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight);\n      img = tmpCanvas.canvas;\n      paintWidth = newWidth;\n      paintHeight = newHeight;\n      tmpCanvasId = tmpCanvasId === \"prescale1\" ? \"prescale2\" : \"prescale1\";\n    }\n    return {\n      img,\n      paintWidth,\n      paintHeight\n    };\n  }\n  _createMaskCanvas(img) {\n    const ctx = this.ctx;\n    const {\n      width,\n      height\n    } = img;\n    const fillColor = this.current.fillColor;\n    const isPatternFill = this.current.patternFill;\n    const currentTransform = (0,display_utils.getCurrentTransform)(ctx);\n    let cache, cacheKey, scaled, maskCanvas;\n    if ((img.bitmap || img.data) && img.count > 1) {\n      const mainKey = img.bitmap || img.data.buffer;\n      cacheKey = JSON.stringify(isPatternFill ? currentTransform : [currentTransform.slice(0, 4), fillColor]);\n      cache = this._cachedBitmapsMap.get(mainKey);\n      if (!cache) {\n        cache = new Map();\n        this._cachedBitmapsMap.set(mainKey, cache);\n      }\n      const cachedImage = cache.get(cacheKey);\n      if (cachedImage && !isPatternFill) {\n        const offsetX = Math.round(Math.min(currentTransform[0], currentTransform[2]) + currentTransform[4]);\n        const offsetY = Math.round(Math.min(currentTransform[1], currentTransform[3]) + currentTransform[5]);\n        return {\n          canvas: cachedImage,\n          offsetX,\n          offsetY\n        };\n      }\n      scaled = cachedImage;\n    }\n    if (!scaled) {\n      maskCanvas = this.cachedCanvases.getCanvas(\"maskCanvas\", width, height);\n      putBinaryImageMask(maskCanvas.context, img);\n    }\n    let maskToCanvas = util.Util.transform(currentTransform, [1 / width, 0, 0, -1 / height, 0, 0]);\n    maskToCanvas = util.Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]);\n    const [minX, minY, maxX, maxY] = util.Util.getAxialAlignedBoundingBox([0, 0, width, height], maskToCanvas);\n    const drawnWidth = Math.round(maxX - minX) || 1;\n    const drawnHeight = Math.round(maxY - minY) || 1;\n    const fillCanvas = this.cachedCanvases.getCanvas(\"fillCanvas\", drawnWidth, drawnHeight);\n    const fillCtx = fillCanvas.context;\n    const offsetX = minX;\n    const offsetY = minY;\n    fillCtx.translate(-offsetX, -offsetY);\n    fillCtx.transform(...maskToCanvas);\n    if (!scaled) {\n      scaled = this._scaleImage(maskCanvas.canvas, (0,display_utils.getCurrentTransformInverse)(fillCtx));\n      scaled = scaled.img;\n      if (cache && isPatternFill) {\n        cache.set(cacheKey, scaled);\n      }\n    }\n    fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled((0,display_utils.getCurrentTransform)(fillCtx), img.interpolate);\n    drawImageAtIntegerCoords(fillCtx, scaled, 0, 0, scaled.width, scaled.height, 0, 0, width, height);\n    fillCtx.globalCompositeOperation = \"source-in\";\n    const inverse = util.Util.transform((0,display_utils.getCurrentTransformInverse)(fillCtx), [1, 0, 0, 1, -offsetX, -offsetY]);\n    fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, PathType.FILL) : fillColor;\n    fillCtx.fillRect(0, 0, width, height);\n    if (cache && !isPatternFill) {\n      this.cachedCanvases.delete(\"fillCanvas\");\n      cache.set(cacheKey, fillCanvas.canvas);\n    }\n    return {\n      canvas: fillCanvas.canvas,\n      offsetX: Math.round(offsetX),\n      offsetY: Math.round(offsetY)\n    };\n  }\n  setLineWidth(width) {\n    if (width !== this.current.lineWidth) {\n      this._cachedScaleForStroking[0] = -1;\n    }\n    this.current.lineWidth = width;\n    this.ctx.lineWidth = width;\n  }\n  setLineCap(style) {\n    this.ctx.lineCap = LINE_CAP_STYLES[style];\n  }\n  setLineJoin(style) {\n    this.ctx.lineJoin = LINE_JOIN_STYLES[style];\n  }\n  setMiterLimit(limit) {\n    this.ctx.miterLimit = limit;\n  }\n  setDash(dashArray, dashPhase) {\n    const ctx = this.ctx;\n    if (ctx.setLineDash !== undefined) {\n      ctx.setLineDash(dashArray);\n      ctx.lineDashOffset = dashPhase;\n    }\n  }\n  setRenderingIntent(intent) {}\n  setFlatness(flatness) {}\n  setGState(states) {\n    for (const [key, value] of states) {\n      switch (key) {\n        case \"LW\":\n          this.setLineWidth(value);\n          break;\n        case \"LC\":\n          this.setLineCap(value);\n          break;\n        case \"LJ\":\n          this.setLineJoin(value);\n          break;\n        case \"ML\":\n          this.setMiterLimit(value);\n          break;\n        case \"D\":\n          this.setDash(value[0], value[1]);\n          break;\n        case \"RI\":\n          this.setRenderingIntent(value);\n          break;\n        case \"FL\":\n          this.setFlatness(value);\n          break;\n        case \"Font\":\n          this.setFont(value[0], value[1]);\n          break;\n        case \"CA\":\n          this.current.strokeAlpha = value;\n          break;\n        case \"ca\":\n          this.current.fillAlpha = value;\n          this.ctx.globalAlpha = value;\n          break;\n        case \"BM\":\n          this.ctx.globalCompositeOperation = value;\n          break;\n        case \"SMask\":\n          this.current.activeSMask = value ? this.tempSMask : null;\n          this.tempSMask = null;\n          this.checkSMaskState();\n          break;\n        case \"TR\":\n          this.ctx.filter = this.current.transferMaps = this.filterFactory.addFilter(value);\n          break;\n      }\n    }\n  }\n  get inSMaskMode() {\n    return !!this.suspendedCtx;\n  }\n  checkSMaskState() {\n    const inSMaskMode = this.inSMaskMode;\n    if (this.current.activeSMask && !inSMaskMode) {\n      this.beginSMaskMode();\n    } else if (!this.current.activeSMask && inSMaskMode) {\n      this.endSMaskMode();\n    }\n  }\n  beginSMaskMode() {\n    if (this.inSMaskMode) {\n      throw new Error(\"beginSMaskMode called while already in smask mode\");\n    }\n    const drawnWidth = this.ctx.canvas.width;\n    const drawnHeight = this.ctx.canvas.height;\n    const cacheId = \"smaskGroupAt\" + this.groupLevel;\n    const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight);\n    this.suspendedCtx = this.ctx;\n    this.ctx = scratchCanvas.context;\n    const ctx = this.ctx;\n    ctx.setTransform(...(0,display_utils.getCurrentTransform)(this.suspendedCtx));\n    copyCtxState(this.suspendedCtx, ctx);\n    mirrorContextOperations(ctx, this.suspendedCtx);\n    this.setGState([[\"BM\", \"source-over\"], [\"ca\", 1], [\"CA\", 1]]);\n  }\n  endSMaskMode() {\n    if (!this.inSMaskMode) {\n      throw new Error(\"endSMaskMode called while not in smask mode\");\n    }\n    this.ctx._removeMirroring();\n    copyCtxState(this.ctx, this.suspendedCtx);\n    this.ctx = this.suspendedCtx;\n    this.suspendedCtx = null;\n  }\n  compose(dirtyBox) {\n    if (!this.current.activeSMask) {\n      return;\n    }\n    if (!dirtyBox) {\n      dirtyBox = [0, 0, this.ctx.canvas.width, this.ctx.canvas.height];\n    } else {\n      dirtyBox[0] = Math.floor(dirtyBox[0]);\n      dirtyBox[1] = Math.floor(dirtyBox[1]);\n      dirtyBox[2] = Math.ceil(dirtyBox[2]);\n      dirtyBox[3] = Math.ceil(dirtyBox[3]);\n    }\n    const smask = this.current.activeSMask;\n    const suspendedCtx = this.suspendedCtx;\n    composeSMask(suspendedCtx, smask, this.ctx, dirtyBox);\n    this.ctx.save();\n    this.ctx.setTransform(1, 0, 0, 1, 0, 0);\n    this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);\n    this.ctx.restore();\n  }\n  save() {\n    if (this.inSMaskMode) {\n      copyCtxState(this.ctx, this.suspendedCtx);\n      this.suspendedCtx.save();\n    } else {\n      this.ctx.save();\n    }\n    const old = this.current;\n    this.stateStack.push(old);\n    this.current = old.clone();\n  }\n  restore() {\n    if (this.stateStack.length === 0 && this.inSMaskMode) {\n      this.endSMaskMode();\n    }\n    if (this.stateStack.length !== 0) {\n      this.current = this.stateStack.pop();\n      if (this.inSMaskMode) {\n        this.suspendedCtx.restore();\n        copyCtxState(this.suspendedCtx, this.ctx);\n      } else {\n        this.ctx.restore();\n      }\n      this.checkSMaskState();\n      this.pendingClip = null;\n      this._cachedScaleForStroking[0] = -1;\n      this._cachedGetSinglePixelWidth = null;\n    }\n  }\n  transform(a, b, c, d, e, f) {\n    this.ctx.transform(a, b, c, d, e, f);\n    this._cachedScaleForStroking[0] = -1;\n    this._cachedGetSinglePixelWidth = null;\n  }\n  constructPath(ops, args, minMax) {\n    const ctx = this.ctx;\n    const current = this.current;\n    let x = current.x,\n      y = current.y;\n    let startX, startY;\n    const currentTransform = (0,display_utils.getCurrentTransform)(ctx);\n    const isScalingMatrix = currentTransform[0] === 0 && currentTransform[3] === 0 || currentTransform[1] === 0 && currentTransform[2] === 0;\n    const minMaxForBezier = isScalingMatrix ? minMax.slice(0) : null;\n    for (let i = 0, j = 0, ii = ops.length; i < ii; i++) {\n      switch (ops[i] | 0) {\n        case util.OPS.rectangle:\n          x = args[j++];\n          y = args[j++];\n          const width = args[j++];\n          const height = args[j++];\n          const xw = x + width;\n          const yh = y + height;\n          ctx.moveTo(x, y);\n          if (width === 0 || height === 0) {\n            ctx.lineTo(xw, yh);\n          } else {\n            ctx.lineTo(xw, y);\n            ctx.lineTo(xw, yh);\n            ctx.lineTo(x, yh);\n          }\n          if (!isScalingMatrix) {\n            current.updateRectMinMax(currentTransform, [x, y, xw, yh]);\n          }\n          ctx.closePath();\n          break;\n        case util.OPS.moveTo:\n          x = args[j++];\n          y = args[j++];\n          ctx.moveTo(x, y);\n          if (!isScalingMatrix) {\n            current.updatePathMinMax(currentTransform, x, y);\n          }\n          break;\n        case util.OPS.lineTo:\n          x = args[j++];\n          y = args[j++];\n          ctx.lineTo(x, y);\n          if (!isScalingMatrix) {\n            current.updatePathMinMax(currentTransform, x, y);\n          }\n          break;\n        case util.OPS.curveTo:\n          startX = x;\n          startY = y;\n          x = args[j + 4];\n          y = args[j + 5];\n          ctx.bezierCurveTo(args[j], args[j + 1], args[j + 2], args[j + 3], x, y);\n          current.updateCurvePathMinMax(currentTransform, startX, startY, args[j], args[j + 1], args[j + 2], args[j + 3], x, y, minMaxForBezier);\n          j += 6;\n          break;\n        case util.OPS.curveTo2:\n          startX = x;\n          startY = y;\n          ctx.bezierCurveTo(x, y, args[j], args[j + 1], args[j + 2], args[j + 3]);\n          current.updateCurvePathMinMax(currentTransform, startX, startY, x, y, args[j], args[j + 1], args[j + 2], args[j + 3], minMaxForBezier);\n          x = args[j + 2];\n          y = args[j + 3];\n          j += 4;\n          break;\n        case util.OPS.curveTo3:\n          startX = x;\n          startY = y;\n          x = args[j + 2];\n          y = args[j + 3];\n          ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);\n          current.updateCurvePathMinMax(currentTransform, startX, startY, args[j], args[j + 1], x, y, x, y, minMaxForBezier);\n          j += 4;\n          break;\n        case util.OPS.closePath:\n          ctx.closePath();\n          break;\n      }\n    }\n    if (isScalingMatrix) {\n      current.updateScalingPathMinMax(currentTransform, minMaxForBezier);\n    }\n    current.setCurrentPoint(x, y);\n  }\n  closePath() {\n    this.ctx.closePath();\n  }\n  stroke(consumePath = true) {\n    const ctx = this.ctx;\n    const strokeColor = this.current.strokeColor;\n    ctx.globalAlpha = this.current.strokeAlpha;\n    if (this.contentVisible) {\n      if (typeof strokeColor === \"object\" && strokeColor?.getPattern) {\n        ctx.save();\n        ctx.strokeStyle = strokeColor.getPattern(ctx, this, (0,display_utils.getCurrentTransformInverse)(ctx), PathType.STROKE);\n        this.rescaleAndStroke(false);\n        ctx.restore();\n      } else {\n        this.rescaleAndStroke(true);\n      }\n    }\n    if (consumePath) {\n      this.consumePath(this.current.getClippedPathBoundingBox());\n    }\n    ctx.globalAlpha = this.current.fillAlpha;\n  }\n  closeStroke() {\n    this.closePath();\n    this.stroke();\n  }\n  fill(consumePath = true) {\n    const ctx = this.ctx;\n    const fillColor = this.current.fillColor;\n    const isPatternFill = this.current.patternFill;\n    let needRestore = false;\n    if (isPatternFill) {\n      ctx.save();\n      ctx.fillStyle = fillColor.getPattern(ctx, this, (0,display_utils.getCurrentTransformInverse)(ctx), PathType.FILL);\n      needRestore = true;\n    }\n    const intersect = this.current.getClippedPathBoundingBox();\n    if (this.contentVisible && intersect !== null) {\n      if (this.pendingEOFill) {\n        ctx.fill(\"evenodd\");\n        this.pendingEOFill = false;\n      } else {\n        ctx.fill();\n      }\n    }\n    if (needRestore) {\n      ctx.restore();\n    }\n    if (consumePath) {\n      this.consumePath(intersect);\n    }\n  }\n  eoFill() {\n    this.pendingEOFill = true;\n    this.fill();\n  }\n  fillStroke() {\n    this.fill(false);\n    this.stroke(false);\n    this.consumePath();\n  }\n  eoFillStroke() {\n    this.pendingEOFill = true;\n    this.fillStroke();\n  }\n  closeFillStroke() {\n    this.closePath();\n    this.fillStroke();\n  }\n  closeEOFillStroke() {\n    this.pendingEOFill = true;\n    this.closePath();\n    this.fillStroke();\n  }\n  endPath() {\n    this.consumePath();\n  }\n  clip() {\n    this.pendingClip = NORMAL_CLIP;\n  }\n  eoClip() {\n    this.pendingClip = EO_CLIP;\n  }\n  beginText() {\n    this.current.textMatrix = util.IDENTITY_MATRIX;\n    this.current.textMatrixScale = 1;\n    this.current.x = this.current.lineX = 0;\n    this.current.y = this.current.lineY = 0;\n  }\n  endText() {\n    const paths = this.pendingTextPaths;\n    const ctx = this.ctx;\n    if (paths === undefined) {\n      ctx.beginPath();\n      return;\n    }\n    ctx.save();\n    ctx.beginPath();\n    for (const path of paths) {\n      ctx.setTransform(...path.transform);\n      ctx.translate(path.x, path.y);\n      path.addToPath(ctx, path.fontSize);\n    }\n    ctx.restore();\n    ctx.clip();\n    ctx.beginPath();\n    delete this.pendingTextPaths;\n  }\n  setCharSpacing(spacing) {\n    this.current.charSpacing = spacing;\n  }\n  setWordSpacing(spacing) {\n    this.current.wordSpacing = spacing;\n  }\n  setHScale(scale) {\n    this.current.textHScale = scale / 100;\n  }\n  setLeading(leading) {\n    this.current.leading = -leading;\n  }\n  setFont(fontRefName, size) {\n    const fontObj = this.commonObjs.get(fontRefName);\n    const current = this.current;\n    if (!fontObj) {\n      throw new Error(`Can't find font for ${fontRefName}`);\n    }\n    current.fontMatrix = fontObj.fontMatrix || util.FONT_IDENTITY_MATRIX;\n    if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) {\n      (0,util.warn)(\"Invalid font matrix for font \" + fontRefName);\n    }\n    if (size < 0) {\n      size = -size;\n      current.fontDirection = -1;\n    } else {\n      current.fontDirection = 1;\n    }\n    this.current.font = fontObj;\n    this.current.fontSize = size;\n    if (fontObj.isType3Font) {\n      return;\n    }\n    const name = fontObj.loadedName || \"sans-serif\";\n    const typeface = fontObj.systemFontInfo?.css || `\"${name}\", ${fontObj.fallbackName}`;\n    let bold = \"normal\";\n    if (fontObj.black) {\n      bold = \"900\";\n    } else if (fontObj.bold) {\n      bold = \"bold\";\n    }\n    const italic = fontObj.italic ? \"italic\" : \"normal\";\n    let browserFontSize = size;\n    if (size < MIN_FONT_SIZE) {\n      browserFontSize = MIN_FONT_SIZE;\n    } else if (size > MAX_FONT_SIZE) {\n      browserFontSize = MAX_FONT_SIZE;\n    }\n    this.current.fontSizeScale = size / browserFontSize;\n    this.ctx.font = `${italic} ${bold} ${browserFontSize}px ${typeface}`;\n  }\n  setTextRenderingMode(mode) {\n    this.current.textRenderingMode = mode;\n  }\n  setTextRise(rise) {\n    this.current.textRise = rise;\n  }\n  moveText(x, y) {\n    this.current.x = this.current.lineX += x;\n    this.current.y = this.current.lineY += y;\n  }\n  setLeadingMoveText(x, y) {\n    this.setLeading(-y);\n    this.moveText(x, y);\n  }\n  setTextMatrix(a, b, c, d, e, f) {\n    this.current.textMatrix = [a, b, c, d, e, f];\n    this.current.textMatrixScale = Math.hypot(a, b);\n    this.current.x = this.current.lineX = 0;\n    this.current.y = this.current.lineY = 0;\n  }\n  nextLine() {\n    this.moveText(0, this.current.leading);\n  }\n  paintChar(character, x, y, patternTransform) {\n    const ctx = this.ctx;\n    const current = this.current;\n    const font = current.font;\n    const textRenderingMode = current.textRenderingMode;\n    const fontSize = current.fontSize / current.fontSizeScale;\n    const fillStrokeMode = textRenderingMode & util.TextRenderingMode.FILL_STROKE_MASK;\n    const isAddToPathSet = !!(textRenderingMode & util.TextRenderingMode.ADD_TO_PATH_FLAG);\n    const patternFill = current.patternFill && !font.missingFile;\n    let addToPath;\n    if (font.disableFontFace || isAddToPathSet || patternFill) {\n      addToPath = font.getPathGenerator(this.commonObjs, character);\n    }\n    if (font.disableFontFace || patternFill) {\n      ctx.save();\n      ctx.translate(x, y);\n      ctx.beginPath();\n      addToPath(ctx, fontSize);\n      if (patternTransform) {\n        ctx.setTransform(...patternTransform);\n      }\n      if (fillStrokeMode === util.TextRenderingMode.FILL || fillStrokeMode === util.TextRenderingMode.FILL_STROKE) {\n        ctx.fill();\n      }\n      if (fillStrokeMode === util.TextRenderingMode.STROKE || fillStrokeMode === util.TextRenderingMode.FILL_STROKE) {\n        ctx.stroke();\n      }\n      ctx.restore();\n    } else {\n      if (fillStrokeMode === util.TextRenderingMode.FILL || fillStrokeMode === util.TextRenderingMode.FILL_STROKE) {\n        ctx.fillText(character, x, y);\n      }\n      if (fillStrokeMode === util.TextRenderingMode.STROKE || fillStrokeMode === util.TextRenderingMode.FILL_STROKE) {\n        ctx.strokeText(character, x, y);\n      }\n    }\n    if (isAddToPathSet) {\n      const paths = this.pendingTextPaths ||= [];\n      paths.push({\n        transform: (0,display_utils.getCurrentTransform)(ctx),\n        x,\n        y,\n        fontSize,\n        addToPath\n      });\n    }\n  }\n  get isFontSubpixelAAEnabled() {\n    const {\n      context: ctx\n    } = this.cachedCanvases.getCanvas(\"isFontSubpixelAAEnabled\", 10, 10);\n    ctx.scale(1.5, 1);\n    ctx.fillText(\"I\", 0, 10);\n    const data = ctx.getImageData(0, 0, 10, 10).data;\n    let enabled = false;\n    for (let i = 3; i < data.length; i += 4) {\n      if (data[i] > 0 && data[i] < 255) {\n        enabled = true;\n        break;\n      }\n    }\n    return (0,util.shadow)(this, \"isFontSubpixelAAEnabled\", enabled);\n  }\n  showText(glyphs) {\n    const current = this.current;\n    const font = current.font;\n    if (font.isType3Font) {\n      return this.showType3Text(glyphs);\n    }\n    const fontSize = current.fontSize;\n    if (fontSize === 0) {\n      return undefined;\n    }\n    const ctx = this.ctx;\n    const fontSizeScale = current.fontSizeScale;\n    const charSpacing = current.charSpacing;\n    const wordSpacing = current.wordSpacing;\n    const fontDirection = current.fontDirection;\n    const textHScale = current.textHScale * fontDirection;\n    const glyphsLength = glyphs.length;\n    const vertical = font.vertical;\n    const spacingDir = vertical ? 1 : -1;\n    const defaultVMetrics = font.defaultVMetrics;\n    const widthAdvanceScale = fontSize * current.fontMatrix[0];\n    const simpleFillText = current.textRenderingMode === util.TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill;\n    ctx.save();\n    ctx.transform(...current.textMatrix);\n    ctx.translate(current.x, current.y + current.textRise);\n    if (fontDirection > 0) {\n      ctx.scale(textHScale, -1);\n    } else {\n      ctx.scale(textHScale, 1);\n    }\n    let patternTransform;\n    if (current.patternFill) {\n      ctx.save();\n      const pattern = current.fillColor.getPattern(ctx, this, (0,display_utils.getCurrentTransformInverse)(ctx), PathType.FILL);\n      patternTransform = (0,display_utils.getCurrentTransform)(ctx);\n      ctx.restore();\n      ctx.fillStyle = pattern;\n    }\n    let lineWidth = current.lineWidth;\n    const scale = current.textMatrixScale;\n    if (scale === 0 || lineWidth === 0) {\n      const fillStrokeMode = current.textRenderingMode & util.TextRenderingMode.FILL_STROKE_MASK;\n      if (fillStrokeMode === util.TextRenderingMode.STROKE || fillStrokeMode === util.TextRenderingMode.FILL_STROKE) {\n        lineWidth = this.getSinglePixelWidth();\n      }\n    } else {\n      lineWidth /= scale;\n    }\n    if (fontSizeScale !== 1.0) {\n      ctx.scale(fontSizeScale, fontSizeScale);\n      lineWidth /= fontSizeScale;\n    }\n    ctx.lineWidth = lineWidth;\n    if (font.isInvalidPDFjsFont) {\n      const chars = [];\n      let width = 0;\n      for (const glyph of glyphs) {\n        chars.push(glyph.unicode);\n        width += glyph.width;\n      }\n      ctx.fillText(chars.join(\"\"), 0, 0);\n      current.x += width * widthAdvanceScale * textHScale;\n      ctx.restore();\n      this.compose();\n      return undefined;\n    }\n    let x = 0,\n      i;\n    for (i = 0; i < glyphsLength; ++i) {\n      const glyph = glyphs[i];\n      if (typeof glyph === \"number\") {\n        x += spacingDir * glyph * fontSize / 1000;\n        continue;\n      }\n      let restoreNeeded = false;\n      const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;\n      const character = glyph.fontChar;\n      const accent = glyph.accent;\n      let scaledX, scaledY;\n      let width = glyph.width;\n      if (vertical) {\n        const vmetric = glyph.vmetric || defaultVMetrics;\n        const vx = -(glyph.vmetric ? vmetric[1] : width * 0.5) * widthAdvanceScale;\n        const vy = vmetric[2] * widthAdvanceScale;\n        width = vmetric ? -vmetric[0] : width;\n        scaledX = vx / fontSizeScale;\n        scaledY = (x + vy) / fontSizeScale;\n      } else {\n        scaledX = x / fontSizeScale;\n        scaledY = 0;\n      }\n      if (font.remeasure && width > 0) {\n        const measuredWidth = ctx.measureText(character).width * 1000 / fontSize * fontSizeScale;\n        if (width < measuredWidth && this.isFontSubpixelAAEnabled) {\n          const characterScaleX = width / measuredWidth;\n          restoreNeeded = true;\n          ctx.save();\n          ctx.scale(characterScaleX, 1);\n          scaledX /= characterScaleX;\n        } else if (width !== measuredWidth) {\n          scaledX += (width - measuredWidth) / 2000 * fontSize / fontSizeScale;\n        }\n      }\n      if (this.contentVisible && (glyph.isInFont || font.missingFile)) {\n        if (simpleFillText && !accent) {\n          ctx.fillText(character, scaledX, scaledY);\n        } else {\n          this.paintChar(character, scaledX, scaledY, patternTransform);\n          if (accent) {\n            const scaledAccentX = scaledX + fontSize * accent.offset.x / fontSizeScale;\n            const scaledAccentY = scaledY - fontSize * accent.offset.y / fontSizeScale;\n            this.paintChar(accent.fontChar, scaledAccentX, scaledAccentY, patternTransform);\n          }\n        }\n      }\n      const charWidth = vertical ? width * widthAdvanceScale - spacing * fontDirection : width * widthAdvanceScale + spacing * fontDirection;\n      x += charWidth;\n      if (restoreNeeded) {\n        ctx.restore();\n      }\n    }\n    if (vertical) {\n      current.y -= x;\n    } else {\n      current.x += x * textHScale;\n    }\n    ctx.restore();\n    this.compose();\n    return undefined;\n  }\n  showType3Text(glyphs) {\n    const ctx = this.ctx;\n    const current = this.current;\n    const font = current.font;\n    const fontSize = current.fontSize;\n    const fontDirection = current.fontDirection;\n    const spacingDir = font.vertical ? 1 : -1;\n    const charSpacing = current.charSpacing;\n    const wordSpacing = current.wordSpacing;\n    const textHScale = current.textHScale * fontDirection;\n    const fontMatrix = current.fontMatrix || util.FONT_IDENTITY_MATRIX;\n    const glyphsLength = glyphs.length;\n    const isTextInvisible = current.textRenderingMode === util.TextRenderingMode.INVISIBLE;\n    let i, glyph, width, spacingLength;\n    if (isTextInvisible || fontSize === 0) {\n      return;\n    }\n    this._cachedScaleForStroking[0] = -1;\n    this._cachedGetSinglePixelWidth = null;\n    ctx.save();\n    ctx.transform(...current.textMatrix);\n    ctx.translate(current.x, current.y);\n    ctx.scale(textHScale, fontDirection);\n    for (i = 0; i < glyphsLength; ++i) {\n      glyph = glyphs[i];\n      if (typeof glyph === \"number\") {\n        spacingLength = spacingDir * glyph * fontSize / 1000;\n        this.ctx.translate(spacingLength, 0);\n        current.x += spacingLength * textHScale;\n        continue;\n      }\n      const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;\n      const operatorList = font.charProcOperatorList[glyph.operatorListId];\n      if (!operatorList) {\n        (0,util.warn)(`Type3 character \"${glyph.operatorListId}\" is not available.`);\n        continue;\n      }\n      if (this.contentVisible) {\n        this.processingType3 = glyph;\n        this.save();\n        ctx.scale(fontSize, fontSize);\n        ctx.transform(...fontMatrix);\n        this.executeOperatorList(operatorList);\n        this.restore();\n      }\n      const transformed = util.Util.applyTransform([glyph.width, 0], fontMatrix);\n      width = transformed[0] * fontSize + spacing;\n      ctx.translate(width, 0);\n      current.x += width * textHScale;\n    }\n    ctx.restore();\n    this.processingType3 = null;\n  }\n  setCharWidth(xWidth, yWidth) {}\n  setCharWidthAndBounds(xWidth, yWidth, llx, lly, urx, ury) {\n    this.ctx.rect(llx, lly, urx - llx, ury - lly);\n    this.ctx.clip();\n    this.endPath();\n  }\n  getColorN_Pattern(IR) {\n    let pattern;\n    if (IR[0] === \"TilingPattern\") {\n      const color = IR[1];\n      const baseTransform = this.baseTransform || (0,display_utils.getCurrentTransform)(this.ctx);\n      const canvasGraphicsFactory = {\n        createCanvasGraphics: ctx => {\n          return new CanvasGraphics(ctx, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, {\n            optionalContentConfig: this.optionalContentConfig,\n            markedContentStack: this.markedContentStack\n          });\n        }\n      };\n      pattern = new TilingPattern(IR, color, this.ctx, canvasGraphicsFactory, baseTransform);\n    } else {\n      pattern = this._getPattern(IR[1], IR[2]);\n    }\n    return pattern;\n  }\n  setStrokeColorN() {\n    this.current.strokeColor = this.getColorN_Pattern(arguments);\n  }\n  setFillColorN() {\n    this.current.fillColor = this.getColorN_Pattern(arguments);\n    this.current.patternFill = true;\n  }\n  setStrokeRGBColor(r, g, b) {\n    const color = util.Util.makeHexColor(r, g, b);\n    this.ctx.strokeStyle = color;\n    this.current.strokeColor = color;\n  }\n  setFillRGBColor(r, g, b) {\n    const color = util.Util.makeHexColor(r, g, b);\n    this.ctx.fillStyle = color;\n    this.current.fillColor = color;\n    this.current.patternFill = false;\n  }\n  _getPattern(objId, matrix = null) {\n    let pattern;\n    if (this.cachedPatterns.has(objId)) {\n      pattern = this.cachedPatterns.get(objId);\n    } else {\n      pattern = getShadingPattern(this.getObject(objId));\n      this.cachedPatterns.set(objId, pattern);\n    }\n    if (matrix) {\n      pattern.matrix = matrix;\n    }\n    return pattern;\n  }\n  shadingFill(objId) {\n    if (!this.contentVisible) {\n      return;\n    }\n    const ctx = this.ctx;\n    this.save();\n    const pattern = this._getPattern(objId);\n    ctx.fillStyle = pattern.getPattern(ctx, this, (0,display_utils.getCurrentTransformInverse)(ctx), PathType.SHADING);\n    const inv = (0,display_utils.getCurrentTransformInverse)(ctx);\n    if (inv) {\n      const {\n        width,\n        height\n      } = ctx.canvas;\n      const [x0, y0, x1, y1] = util.Util.getAxialAlignedBoundingBox([0, 0, width, height], inv);\n      this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);\n    } else {\n      this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);\n    }\n    this.compose(this.current.getClippedPathBoundingBox());\n    this.restore();\n  }\n  beginInlineImage() {\n    (0,util.unreachable)(\"Should not call beginInlineImage\");\n  }\n  beginImageData() {\n    (0,util.unreachable)(\"Should not call beginImageData\");\n  }\n  paintFormXObjectBegin(matrix, bbox) {\n    if (!this.contentVisible) {\n      return;\n    }\n    this.save();\n    this.baseTransformStack.push(this.baseTransform);\n    if (Array.isArray(matrix) && matrix.length === 6) {\n      this.transform(...matrix);\n    }\n    this.baseTransform = (0,display_utils.getCurrentTransform)(this.ctx);\n    if (bbox) {\n      const width = bbox[2] - bbox[0];\n      const height = bbox[3] - bbox[1];\n      this.ctx.rect(bbox[0], bbox[1], width, height);\n      this.current.updateRectMinMax((0,display_utils.getCurrentTransform)(this.ctx), bbox);\n      this.clip();\n      this.endPath();\n    }\n  }\n  paintFormXObjectEnd() {\n    if (!this.contentVisible) {\n      return;\n    }\n    this.restore();\n    this.baseTransform = this.baseTransformStack.pop();\n  }\n  beginGroup(group) {\n    if (!this.contentVisible) {\n      return;\n    }\n    this.save();\n    if (this.inSMaskMode) {\n      this.endSMaskMode();\n      this.current.activeSMask = null;\n    }\n    const currentCtx = this.ctx;\n    if (!group.isolated) {\n      (0,util.info)(\"TODO: Support non-isolated groups.\");\n    }\n    if (group.knockout) {\n      (0,util.warn)(\"Knockout groups not supported.\");\n    }\n    const currentTransform = (0,display_utils.getCurrentTransform)(currentCtx);\n    if (group.matrix) {\n      currentCtx.transform(...group.matrix);\n    }\n    if (!group.bbox) {\n      throw new Error(\"Bounding box is required.\");\n    }\n    let bounds = util.Util.getAxialAlignedBoundingBox(group.bbox, (0,display_utils.getCurrentTransform)(currentCtx));\n    const canvasBounds = [0, 0, currentCtx.canvas.width, currentCtx.canvas.height];\n    bounds = util.Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0];\n    const offsetX = Math.floor(bounds[0]);\n    const offsetY = Math.floor(bounds[1]);\n    let drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1);\n    let drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1);\n    let scaleX = 1,\n      scaleY = 1;\n    if (drawnWidth > MAX_GROUP_SIZE) {\n      scaleX = drawnWidth / MAX_GROUP_SIZE;\n      drawnWidth = MAX_GROUP_SIZE;\n    }\n    if (drawnHeight > MAX_GROUP_SIZE) {\n      scaleY = drawnHeight / MAX_GROUP_SIZE;\n      drawnHeight = MAX_GROUP_SIZE;\n    }\n    this.current.startNewPathAndClipBox([0, 0, drawnWidth, drawnHeight]);\n    let cacheId = \"groupAt\" + this.groupLevel;\n    if (group.smask) {\n      cacheId += \"_smask_\" + this.smaskCounter++ % 2;\n    }\n    const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight);\n    const groupCtx = scratchCanvas.context;\n    groupCtx.scale(1 / scaleX, 1 / scaleY);\n    groupCtx.translate(-offsetX, -offsetY);\n    groupCtx.transform(...currentTransform);\n    if (group.smask) {\n      this.smaskStack.push({\n        canvas: scratchCanvas.canvas,\n        context: groupCtx,\n        offsetX,\n        offsetY,\n        scaleX,\n        scaleY,\n        subtype: group.smask.subtype,\n        backdrop: group.smask.backdrop,\n        transferMap: group.smask.transferMap || null,\n        startTransformInverse: null\n      });\n    } else {\n      currentCtx.setTransform(1, 0, 0, 1, 0, 0);\n      currentCtx.translate(offsetX, offsetY);\n      currentCtx.scale(scaleX, scaleY);\n      currentCtx.save();\n    }\n    copyCtxState(currentCtx, groupCtx);\n    this.ctx = groupCtx;\n    this.setGState([[\"BM\", \"source-over\"], [\"ca\", 1], [\"CA\", 1]]);\n    this.groupStack.push(currentCtx);\n    this.groupLevel++;\n  }\n  endGroup(group) {\n    if (!this.contentVisible) {\n      return;\n    }\n    this.groupLevel--;\n    const groupCtx = this.ctx;\n    const ctx = this.groupStack.pop();\n    this.ctx = ctx;\n    this.ctx.imageSmoothingEnabled = false;\n    if (group.smask) {\n      this.tempSMask = this.smaskStack.pop();\n      this.restore();\n    } else {\n      this.ctx.restore();\n      const currentMtx = (0,display_utils.getCurrentTransform)(this.ctx);\n      this.restore();\n      this.ctx.save();\n      this.ctx.setTransform(...currentMtx);\n      const dirtyBox = util.Util.getAxialAlignedBoundingBox([0, 0, groupCtx.canvas.width, groupCtx.canvas.height], currentMtx);\n      this.ctx.drawImage(groupCtx.canvas, 0, 0);\n      this.ctx.restore();\n      this.compose(dirtyBox);\n    }\n  }\n  beginAnnotation(id, rect, transform, matrix, hasOwnCanvas) {\n    this.#restoreInitialState();\n    resetCtxToDefault(this.ctx);\n    this.ctx.save();\n    this.save();\n    if (this.baseTransform) {\n      this.ctx.setTransform(...this.baseTransform);\n    }\n    if (Array.isArray(rect) && rect.length === 4) {\n      const width = rect[2] - rect[0];\n      const height = rect[3] - rect[1];\n      if (hasOwnCanvas && this.annotationCanvasMap) {\n        transform = transform.slice();\n        transform[4] -= rect[0];\n        transform[5] -= rect[1];\n        rect = rect.slice();\n        rect[0] = rect[1] = 0;\n        rect[2] = width;\n        rect[3] = height;\n        const [scaleX, scaleY] = util.Util.singularValueDecompose2dScale((0,display_utils.getCurrentTransform)(this.ctx));\n        const {\n          viewportScale\n        } = this;\n        const canvasWidth = Math.ceil(width * this.outputScaleX * viewportScale);\n        const canvasHeight = Math.ceil(height * this.outputScaleY * viewportScale);\n        this.annotationCanvas = this.canvasFactory.create(canvasWidth, canvasHeight);\n        const {\n          canvas,\n          context\n        } = this.annotationCanvas;\n        this.annotationCanvasMap.set(id, canvas);\n        this.annotationCanvas.savedCtx = this.ctx;\n        this.ctx = context;\n        this.ctx.save();\n        this.ctx.setTransform(scaleX, 0, 0, -scaleY, 0, height * scaleY);\n        resetCtxToDefault(this.ctx);\n      } else {\n        resetCtxToDefault(this.ctx);\n        this.ctx.rect(rect[0], rect[1], width, height);\n        this.ctx.clip();\n        this.endPath();\n      }\n    }\n    this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height);\n    this.transform(...transform);\n    this.transform(...matrix);\n  }\n  endAnnotation() {\n    if (this.annotationCanvas) {\n      this.ctx.restore();\n      this.#drawFilter();\n      this.ctx = this.annotationCanvas.savedCtx;\n      delete this.annotationCanvas.savedCtx;\n      delete this.annotationCanvas;\n    }\n  }\n  paintImageMaskXObject(img) {\n    if (!this.contentVisible) {\n      return;\n    }\n    const count = img.count;\n    img = this.getObject(img.data, img);\n    img.count = count;\n    const ctx = this.ctx;\n    const glyph = this.processingType3;\n    if (glyph) {\n      if (glyph.compiled === undefined) {\n        glyph.compiled = compileType3Glyph(img);\n      }\n      if (glyph.compiled) {\n        glyph.compiled(ctx);\n        return;\n      }\n    }\n    const mask = this._createMaskCanvas(img);\n    const maskCanvas = mask.canvas;\n    ctx.save();\n    ctx.setTransform(1, 0, 0, 1, 0, 0);\n    ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY);\n    ctx.restore();\n    this.compose();\n  }\n  paintImageMaskXObjectRepeat(img, scaleX, skewX = 0, skewY = 0, scaleY, positions) {\n    if (!this.contentVisible) {\n      return;\n    }\n    img = this.getObject(img.data, img);\n    const ctx = this.ctx;\n    ctx.save();\n    const currentTransform = (0,display_utils.getCurrentTransform)(ctx);\n    ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0);\n    const mask = this._createMaskCanvas(img);\n    ctx.setTransform(1, 0, 0, 1, mask.offsetX - currentTransform[4], mask.offsetY - currentTransform[5]);\n    for (let i = 0, ii = positions.length; i < ii; i += 2) {\n      const trans = util.Util.transform(currentTransform, [scaleX, skewX, skewY, scaleY, positions[i], positions[i + 1]]);\n      const [x, y] = util.Util.applyTransform([0, 0], trans);\n      ctx.drawImage(mask.canvas, x, y);\n    }\n    ctx.restore();\n    this.compose();\n  }\n  paintImageMaskXObjectGroup(images) {\n    if (!this.contentVisible) {\n      return;\n    }\n    const ctx = this.ctx;\n    const fillColor = this.current.fillColor;\n    const isPatternFill = this.current.patternFill;\n    for (const image of images) {\n      const {\n        data,\n        width,\n        height,\n        transform\n      } = image;\n      const maskCanvas = this.cachedCanvases.getCanvas(\"maskCanvas\", width, height);\n      const maskCtx = maskCanvas.context;\n      maskCtx.save();\n      const img = this.getObject(data, image);\n      putBinaryImageMask(maskCtx, img);\n      maskCtx.globalCompositeOperation = \"source-in\";\n      maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this, (0,display_utils.getCurrentTransformInverse)(ctx), PathType.FILL) : fillColor;\n      maskCtx.fillRect(0, 0, width, height);\n      maskCtx.restore();\n      ctx.save();\n      ctx.transform(...transform);\n      ctx.scale(1, -1);\n      drawImageAtIntegerCoords(ctx, maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1);\n      ctx.restore();\n    }\n    this.compose();\n  }\n  paintImageXObject(objId) {\n    if (!this.contentVisible) {\n      return;\n    }\n    const imgData = this.getObject(objId);\n    if (!imgData) {\n      (0,util.warn)(\"Dependent image isn't ready yet\");\n      return;\n    }\n    this.paintInlineImageXObject(imgData);\n  }\n  paintImageXObjectRepeat(objId, scaleX, scaleY, positions) {\n    if (!this.contentVisible) {\n      return;\n    }\n    const imgData = this.getObject(objId);\n    if (!imgData) {\n      (0,util.warn)(\"Dependent image isn't ready yet\");\n      return;\n    }\n    const width = imgData.width;\n    const height = imgData.height;\n    const map = [];\n    for (let i = 0, ii = positions.length; i < ii; i += 2) {\n      map.push({\n        transform: [scaleX, 0, 0, scaleY, positions[i], positions[i + 1]],\n        x: 0,\n        y: 0,\n        w: width,\n        h: height\n      });\n    }\n    this.paintInlineImageXObjectGroup(imgData, map);\n  }\n  applyTransferMapsToCanvas(ctx) {\n    if (this.current.transferMaps !== \"none\") {\n      ctx.filter = this.current.transferMaps;\n      ctx.drawImage(ctx.canvas, 0, 0);\n      ctx.filter = \"none\";\n    }\n    return ctx.canvas;\n  }\n  applyTransferMapsToBitmap(imgData) {\n    if (this.current.transferMaps === \"none\") {\n      return imgData.bitmap;\n    }\n    const {\n      bitmap,\n      width,\n      height\n    } = imgData;\n    const tmpCanvas = this.cachedCanvases.getCanvas(\"inlineImage\", width, height);\n    const tmpCtx = tmpCanvas.context;\n    tmpCtx.filter = this.current.transferMaps;\n    tmpCtx.drawImage(bitmap, 0, 0);\n    tmpCtx.filter = \"none\";\n    return tmpCanvas.canvas;\n  }\n  paintInlineImageXObject(imgData) {\n    if (!this.contentVisible) {\n      return;\n    }\n    const width = imgData.width;\n    const height = imgData.height;\n    const ctx = this.ctx;\n    this.save();\n    if (!util.isNodeJS) {\n      const {\n        filter\n      } = ctx;\n      if (filter !== \"none\" && filter !== \"\") {\n        ctx.filter = \"none\";\n      }\n    }\n    ctx.scale(1 / width, -1 / height);\n    let imgToPaint;\n    if (imgData.bitmap) {\n      imgToPaint = this.applyTransferMapsToBitmap(imgData);\n    } else if (typeof HTMLElement === \"function\" && imgData instanceof HTMLElement || !imgData.data) {\n      imgToPaint = imgData;\n    } else {\n      const tmpCanvas = this.cachedCanvases.getCanvas(\"inlineImage\", width, height);\n      const tmpCtx = tmpCanvas.context;\n      putBinaryImageData(tmpCtx, imgData);\n      imgToPaint = this.applyTransferMapsToCanvas(tmpCtx);\n    }\n    const scaled = this._scaleImage(imgToPaint, (0,display_utils.getCurrentTransformInverse)(ctx));\n    ctx.imageSmoothingEnabled = getImageSmoothingEnabled((0,display_utils.getCurrentTransform)(ctx), imgData.interpolate);\n    drawImageAtIntegerCoords(ctx, scaled.img, 0, 0, scaled.paintWidth, scaled.paintHeight, 0, -height, width, height);\n    this.compose();\n    this.restore();\n  }\n  paintInlineImageXObjectGroup(imgData, map) {\n    if (!this.contentVisible) {\n      return;\n    }\n    const ctx = this.ctx;\n    let imgToPaint;\n    if (imgData.bitmap) {\n      imgToPaint = imgData.bitmap;\n    } else {\n      const w = imgData.width;\n      const h = imgData.height;\n      const tmpCanvas = this.cachedCanvases.getCanvas(\"inlineImage\", w, h);\n      const tmpCtx = tmpCanvas.context;\n      putBinaryImageData(tmpCtx, imgData);\n      imgToPaint = this.applyTransferMapsToCanvas(tmpCtx);\n    }\n    for (const entry of map) {\n      ctx.save();\n      ctx.transform(...entry.transform);\n      ctx.scale(1, -1);\n      drawImageAtIntegerCoords(ctx, imgToPaint, entry.x, entry.y, entry.w, entry.h, 0, -1, 1, 1);\n      ctx.restore();\n    }\n    this.compose();\n  }\n  paintSolidColorImageMask() {\n    if (!this.contentVisible) {\n      return;\n    }\n    this.ctx.fillRect(0, 0, 1, 1);\n    this.compose();\n  }\n  markPoint(tag) {}\n  markPointProps(tag, properties) {}\n  beginMarkedContent(tag) {\n    this.markedContentStack.push({\n      visible: true\n    });\n  }\n  beginMarkedContentProps(tag, properties) {\n    if (tag === \"OC\") {\n      this.markedContentStack.push({\n        visible: this.optionalContentConfig.isVisible(properties)\n      });\n    } else {\n      this.markedContentStack.push({\n        visible: true\n      });\n    }\n    this.contentVisible = this.isContentVisible();\n  }\n  endMarkedContent() {\n    this.markedContentStack.pop();\n    this.contentVisible = this.isContentVisible();\n  }\n  beginCompat() {}\n  endCompat() {}\n  consumePath(clipBox) {\n    const isEmpty = this.current.isEmptyClip();\n    if (this.pendingClip) {\n      this.current.updateClipFromPath();\n    }\n    if (!this.pendingClip) {\n      this.compose(clipBox);\n    }\n    const ctx = this.ctx;\n    if (this.pendingClip) {\n      if (!isEmpty) {\n        if (this.pendingClip === EO_CLIP) {\n          ctx.clip(\"evenodd\");\n        } else {\n          ctx.clip();\n        }\n      }\n      this.pendingClip = null;\n    }\n    this.current.startNewPathAndClipBox(this.current.clipBox);\n    ctx.beginPath();\n  }\n  getSinglePixelWidth() {\n    if (!this._cachedGetSinglePixelWidth) {\n      const m = (0,display_utils.getCurrentTransform)(this.ctx);\n      if (m[1] === 0 && m[2] === 0) {\n        this._cachedGetSinglePixelWidth = 1 / Math.min(Math.abs(m[0]), Math.abs(m[3]));\n      } else {\n        const absDet = Math.abs(m[0] * m[3] - m[2] * m[1]);\n        const normX = Math.hypot(m[0], m[2]);\n        const normY = Math.hypot(m[1], m[3]);\n        this._cachedGetSinglePixelWidth = Math.max(normX, normY) / absDet;\n      }\n    }\n    return this._cachedGetSinglePixelWidth;\n  }\n  getScaleForStroking() {\n    if (this._cachedScaleForStroking[0] === -1) {\n      const {\n        lineWidth\n      } = this.current;\n      const {\n        a,\n        b,\n        c,\n        d\n      } = this.ctx.getTransform();\n      let scaleX, scaleY;\n      if (b === 0 && c === 0) {\n        const normX = Math.abs(a);\n        const normY = Math.abs(d);\n        if (normX === normY) {\n          if (lineWidth === 0) {\n            scaleX = scaleY = 1 / normX;\n          } else {\n            const scaledLineWidth = normX * lineWidth;\n            scaleX = scaleY = scaledLineWidth < 1 ? 1 / scaledLineWidth : 1;\n          }\n        } else if (lineWidth === 0) {\n          scaleX = 1 / normX;\n          scaleY = 1 / normY;\n        } else {\n          const scaledXLineWidth = normX * lineWidth;\n          const scaledYLineWidth = normY * lineWidth;\n          scaleX = scaledXLineWidth < 1 ? 1 / scaledXLineWidth : 1;\n          scaleY = scaledYLineWidth < 1 ? 1 / scaledYLineWidth : 1;\n        }\n      } else {\n        const absDet = Math.abs(a * d - b * c);\n        const normX = Math.hypot(a, b);\n        const normY = Math.hypot(c, d);\n        if (lineWidth === 0) {\n          scaleX = normY / absDet;\n          scaleY = normX / absDet;\n        } else {\n          const baseArea = lineWidth * absDet;\n          scaleX = normY > baseArea ? normY / baseArea : 1;\n          scaleY = normX > baseArea ? normX / baseArea : 1;\n        }\n      }\n      this._cachedScaleForStroking[0] = scaleX;\n      this._cachedScaleForStroking[1] = scaleY;\n    }\n    return this._cachedScaleForStroking;\n  }\n  rescaleAndStroke(saveRestore) {\n    const {\n      ctx\n    } = this;\n    const {\n      lineWidth\n    } = this.current;\n    const [scaleX, scaleY] = this.getScaleForStroking();\n    ctx.lineWidth = lineWidth || 1;\n    if (scaleX === 1 && scaleY === 1) {\n      ctx.stroke();\n      return;\n    }\n    const dashes = ctx.getLineDash();\n    if (saveRestore) {\n      ctx.save();\n    }\n    ctx.scale(scaleX, scaleY);\n    if (dashes.length > 0) {\n      const scale = Math.max(scaleX, scaleY);\n      ctx.setLineDash(dashes.map(x => x / scale));\n      ctx.lineDashOffset /= scale;\n    }\n    ctx.stroke();\n    if (saveRestore) {\n      ctx.restore();\n    }\n  }\n  isContentVisible() {\n    for (let i = this.markedContentStack.length - 1; i >= 0; i--) {\n      if (!this.markedContentStack[i].visible) {\n        return false;\n      }\n    }\n    return true;\n  }\n}\nfor (const op in util.OPS) {\n  if (CanvasGraphics.prototype[op] !== undefined) {\n    CanvasGraphics.prototype[util.OPS[op]] = CanvasGraphics.prototype[op];\n  }\n}\n\n\n/***/ }),\n\n/***/ 473:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   DOMCMapReaderFactory: () => (/* binding */ DOMCMapReaderFactory),\n/* harmony export */   DOMCanvasFactory: () => (/* binding */ DOMCanvasFactory),\n/* harmony export */   DOMFilterFactory: () => (/* binding */ DOMFilterFactory),\n/* harmony export */   DOMSVGFactory: () => (/* binding */ DOMSVGFactory),\n/* harmony export */   DOMStandardFontDataFactory: () => (/* binding */ DOMStandardFontDataFactory),\n/* harmony export */   PDFDateString: () => (/* binding */ PDFDateString),\n/* harmony export */   PageViewport: () => (/* binding */ PageViewport),\n/* harmony export */   PixelsPerInch: () => (/* binding */ PixelsPerInch),\n/* harmony export */   RenderingCancelledException: () => (/* binding */ RenderingCancelledException),\n/* harmony export */   StatTimer: () => (/* binding */ StatTimer),\n/* harmony export */   fetchData: () => (/* binding */ fetchData),\n/* harmony export */   getColorValues: () => (/* binding */ getColorValues),\n/* harmony export */   getCurrentTransform: () => (/* binding */ getCurrentTransform),\n/* harmony export */   getCurrentTransformInverse: () => (/* binding */ getCurrentTransformInverse),\n/* harmony export */   getFilenameFromUrl: () => (/* binding */ getFilenameFromUrl),\n/* harmony export */   getPdfFilenameFromUrl: () => (/* binding */ getPdfFilenameFromUrl),\n/* harmony export */   getRGB: () => (/* binding */ getRGB),\n/* harmony export */   getXfaPageViewport: () => (/* binding */ getXfaPageViewport),\n/* harmony export */   isDataScheme: () => (/* binding */ isDataScheme),\n/* harmony export */   isPdfFile: () => (/* binding */ isPdfFile),\n/* harmony export */   isValidFetchUrl: () => (/* binding */ isValidFetchUrl),\n/* harmony export */   noContextMenu: () => (/* binding */ noContextMenu),\n/* harmony export */   setLayerDimensions: () => (/* binding */ setLayerDimensions)\n/* harmony export */ });\n/* unused harmony export deprecated */\n/* harmony import */ var _base_factory_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(822);\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(266);\n\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\nclass PixelsPerInch {\n  static CSS = 96.0;\n  static PDF = 72.0;\n  static PDF_TO_CSS_UNITS = this.CSS / this.PDF;\n}\nclass DOMFilterFactory extends _base_factory_js__WEBPACK_IMPORTED_MODULE_0__.BaseFilterFactory {\n  #_cache;\n  #_defs;\n  #docId;\n  #document;\n  #hcmFilter;\n  #hcmKey;\n  #hcmUrl;\n  #hcmHighlightFilter;\n  #hcmHighlightKey;\n  #hcmHighlightUrl;\n  #id = 0;\n  constructor({\n    docId,\n    ownerDocument = globalThis.document\n  } = {}) {\n    super();\n    this.#docId = docId;\n    this.#document = ownerDocument;\n  }\n  get #cache() {\n    return this.#_cache ||= new Map();\n  }\n  get #defs() {\n    if (!this.#_defs) {\n      const div = this.#document.createElement(\"div\");\n      const {\n        style\n      } = div;\n      style.visibility = \"hidden\";\n      style.contain = \"strict\";\n      style.width = style.height = 0;\n      style.position = \"absolute\";\n      style.top = style.left = 0;\n      style.zIndex = -1;\n      const svg = this.#document.createElementNS(SVG_NS, \"svg\");\n      svg.setAttribute(\"width\", 0);\n      svg.setAttribute(\"height\", 0);\n      this.#_defs = this.#document.createElementNS(SVG_NS, \"defs\");\n      div.append(svg);\n      svg.append(this.#_defs);\n      this.#document.body.append(div);\n    }\n    return this.#_defs;\n  }\n  addFilter(maps) {\n    if (!maps) {\n      return \"none\";\n    }\n    let value = this.#cache.get(maps);\n    if (value) {\n      return value;\n    }\n    let tableR, tableG, tableB, key;\n    if (maps.length === 1) {\n      const mapR = maps[0];\n      const buffer = new Array(256);\n      for (let i = 0; i < 256; i++) {\n        buffer[i] = mapR[i] / 255;\n      }\n      key = tableR = tableG = tableB = buffer.join(\",\");\n    } else {\n      const [mapR, mapG, mapB] = maps;\n      const bufferR = new Array(256);\n      const bufferG = new Array(256);\n      const bufferB = new Array(256);\n      for (let i = 0; i < 256; i++) {\n        bufferR[i] = mapR[i] / 255;\n        bufferG[i] = mapG[i] / 255;\n        bufferB[i] = mapB[i] / 255;\n      }\n      tableR = bufferR.join(\",\");\n      tableG = bufferG.join(\",\");\n      tableB = bufferB.join(\",\");\n      key = `${tableR}${tableG}${tableB}`;\n    }\n    value = this.#cache.get(key);\n    if (value) {\n      this.#cache.set(maps, value);\n      return value;\n    }\n    const id = `g_${this.#docId}_transfer_map_${this.#id++}`;\n    const url = `url(#${id})`;\n    this.#cache.set(maps, url);\n    this.#cache.set(key, url);\n    const filter = this.#createFilter(id);\n    this.#addTransferMapConversion(tableR, tableG, tableB, filter);\n    return url;\n  }\n  addHCMFilter(fgColor, bgColor) {\n    const key = `${fgColor}-${bgColor}`;\n    if (this.#hcmKey === key) {\n      return this.#hcmUrl;\n    }\n    this.#hcmKey = key;\n    this.#hcmUrl = \"none\";\n    this.#hcmFilter?.remove();\n    if (!fgColor || !bgColor) {\n      return this.#hcmUrl;\n    }\n    const fgRGB = this.#getRGB(fgColor);\n    fgColor = _shared_util_js__WEBPACK_IMPORTED_MODULE_1__.Util.makeHexColor(...fgRGB);\n    const bgRGB = this.#getRGB(bgColor);\n    bgColor = _shared_util_js__WEBPACK_IMPORTED_MODULE_1__.Util.makeHexColor(...bgRGB);\n    this.#defs.style.color = \"\";\n    if (fgColor === \"#000000\" && bgColor === \"#ffffff\" || fgColor === bgColor) {\n      return this.#hcmUrl;\n    }\n    const map = new Array(256);\n    for (let i = 0; i <= 255; i++) {\n      const x = i / 255;\n      map[i] = x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;\n    }\n    const table = map.join(\",\");\n    const id = `g_${this.#docId}_hcm_filter`;\n    const filter = this.#hcmHighlightFilter = this.#createFilter(id);\n    this.#addTransferMapConversion(table, table, table, filter);\n    this.#addGrayConversion(filter);\n    const getSteps = (c, n) => {\n      const start = fgRGB[c] / 255;\n      const end = bgRGB[c] / 255;\n      const arr = new Array(n + 1);\n      for (let i = 0; i <= n; i++) {\n        arr[i] = start + i / n * (end - start);\n      }\n      return arr.join(\",\");\n    };\n    this.#addTransferMapConversion(getSteps(0, 5), getSteps(1, 5), getSteps(2, 5), filter);\n    this.#hcmUrl = `url(#${id})`;\n    return this.#hcmUrl;\n  }\n  addHighlightHCMFilter(fgColor, bgColor, newFgColor, newBgColor) {\n    const key = `${fgColor}-${bgColor}-${newFgColor}-${newBgColor}`;\n    if (this.#hcmHighlightKey === key) {\n      return this.#hcmHighlightUrl;\n    }\n    this.#hcmHighlightKey = key;\n    this.#hcmHighlightUrl = \"none\";\n    this.#hcmHighlightFilter?.remove();\n    if (!fgColor || !bgColor) {\n      return this.#hcmHighlightUrl;\n    }\n    const [fgRGB, bgRGB] = [fgColor, bgColor].map(this.#getRGB.bind(this));\n    let fgGray = Math.round(0.2126 * fgRGB[0] + 0.7152 * fgRGB[1] + 0.0722 * fgRGB[2]);\n    let bgGray = Math.round(0.2126 * bgRGB[0] + 0.7152 * bgRGB[1] + 0.0722 * bgRGB[2]);\n    let [newFgRGB, newBgRGB] = [newFgColor, newBgColor].map(this.#getRGB.bind(this));\n    if (bgGray < fgGray) {\n      [fgGray, bgGray, newFgRGB, newBgRGB] = [bgGray, fgGray, newBgRGB, newFgRGB];\n    }\n    this.#defs.style.color = \"\";\n    const getSteps = (fg, bg, n) => {\n      const arr = new Array(256);\n      const step = (bgGray - fgGray) / n;\n      const newStart = fg / 255;\n      const newStep = (bg - fg) / (255 * n);\n      let prev = 0;\n      for (let i = 0; i <= n; i++) {\n        const k = Math.round(fgGray + i * step);\n        const value = newStart + i * newStep;\n        for (let j = prev; j <= k; j++) {\n          arr[j] = value;\n        }\n        prev = k + 1;\n      }\n      for (let i = prev; i < 256; i++) {\n        arr[i] = arr[prev - 1];\n      }\n      return arr.join(\",\");\n    };\n    const id = `g_${this.#docId}_hcm_highlight_filter`;\n    const filter = this.#hcmHighlightFilter = this.#createFilter(id);\n    this.#addGrayConversion(filter);\n    this.#addTransferMapConversion(getSteps(newFgRGB[0], newBgRGB[0], 5), getSteps(newFgRGB[1], newBgRGB[1], 5), getSteps(newFgRGB[2], newBgRGB[2], 5), filter);\n    this.#hcmHighlightUrl = `url(#${id})`;\n    return this.#hcmHighlightUrl;\n  }\n  destroy(keepHCM = false) {\n    if (keepHCM && (this.#hcmUrl || this.#hcmHighlightUrl)) {\n      return;\n    }\n    if (this.#_defs) {\n      this.#_defs.parentNode.parentNode.remove();\n      this.#_defs = null;\n    }\n    if (this.#_cache) {\n      this.#_cache.clear();\n      this.#_cache = null;\n    }\n    this.#id = 0;\n  }\n  #addGrayConversion(filter) {\n    const feColorMatrix = this.#document.createElementNS(SVG_NS, \"feColorMatrix\");\n    feColorMatrix.setAttribute(\"type\", \"matrix\");\n    feColorMatrix.setAttribute(\"values\", \"0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0\");\n    filter.append(feColorMatrix);\n  }\n  #createFilter(id) {\n    const filter = this.#document.createElementNS(SVG_NS, \"filter\");\n    filter.setAttribute(\"color-interpolation-filters\", \"sRGB\");\n    filter.setAttribute(\"id\", id);\n    this.#defs.append(filter);\n    return filter;\n  }\n  #appendFeFunc(feComponentTransfer, func, table) {\n    const feFunc = this.#document.createElementNS(SVG_NS, func);\n    feFunc.setAttribute(\"type\", \"discrete\");\n    feFunc.setAttribute(\"tableValues\", table);\n    feComponentTransfer.append(feFunc);\n  }\n  #addTransferMapConversion(rTable, gTable, bTable, filter) {\n    const feComponentTransfer = this.#document.createElementNS(SVG_NS, \"feComponentTransfer\");\n    filter.append(feComponentTransfer);\n    this.#appendFeFunc(feComponentTransfer, \"feFuncR\", rTable);\n    this.#appendFeFunc(feComponentTransfer, \"feFuncG\", gTable);\n    this.#appendFeFunc(feComponentTransfer, \"feFuncB\", bTable);\n  }\n  #getRGB(color) {\n    this.#defs.style.color = color;\n    return getRGB(getComputedStyle(this.#defs).getPropertyValue(\"color\"));\n  }\n}\nclass DOMCanvasFactory extends _base_factory_js__WEBPACK_IMPORTED_MODULE_0__.BaseCanvasFactory {\n  constructor({\n    ownerDocument = globalThis.document\n  } = {}) {\n    super();\n    this._document = ownerDocument;\n  }\n  _createCanvas(width, height) {\n    const canvas = this._document.createElement(\"canvas\");\n    canvas.width = width;\n    canvas.height = height;\n    return canvas;\n  }\n}\nasync function fetchData(url, type = \"text\") {\n  if (isValidFetchUrl(url, document.baseURI)) {\n    const response = await fetch(url);\n    if (!response.ok) {\n      throw new Error(response.statusText);\n    }\n    switch (type) {\n      case \"arraybuffer\":\n        return response.arrayBuffer();\n      case \"blob\":\n        return response.blob();\n      case \"json\":\n        return response.json();\n    }\n    return response.text();\n  }\n  return new Promise((resolve, reject) => {\n    const request = new XMLHttpRequest();\n    request.open(\"GET\", url, true);\n    request.responseType = type;\n    request.onreadystatechange = () => {\n      if (request.readyState !== XMLHttpRequest.DONE) {\n        return;\n      }\n      if (request.status === 200 || request.status === 0) {\n        let data;\n        switch (type) {\n          case \"arraybuffer\":\n          case \"blob\":\n          case \"json\":\n            data = request.response;\n            break;\n          default:\n            data = request.responseText;\n            break;\n        }\n        if (data) {\n          resolve(data);\n          return;\n        }\n      }\n      reject(new Error(request.statusText));\n    };\n    request.send(null);\n  });\n}\nclass DOMCMapReaderFactory extends _base_factory_js__WEBPACK_IMPORTED_MODULE_0__.BaseCMapReaderFactory {\n  _fetchData(url, compressionType) {\n    return fetchData(url, this.isCompressed ? \"arraybuffer\" : \"text\").then(data => {\n      return {\n        cMapData: data instanceof ArrayBuffer ? new Uint8Array(data) : (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_1__.stringToBytes)(data),\n        compressionType\n      };\n    });\n  }\n}\nclass DOMStandardFontDataFactory extends _base_factory_js__WEBPACK_IMPORTED_MODULE_0__.BaseStandardFontDataFactory {\n  _fetchData(url) {\n    return fetchData(url, \"arraybuffer\").then(data => {\n      return new Uint8Array(data);\n    });\n  }\n}\nclass DOMSVGFactory extends _base_factory_js__WEBPACK_IMPORTED_MODULE_0__.BaseSVGFactory {\n  _createSVG(type) {\n    return document.createElementNS(SVG_NS, type);\n  }\n}\nclass PageViewport {\n  constructor({\n    viewBox,\n    scale,\n    rotation,\n    offsetX = 0,\n    offsetY = 0,\n    dontFlip = false\n  }) {\n    this.viewBox = viewBox;\n    this.scale = scale;\n    this.rotation = rotation;\n    this.offsetX = offsetX;\n    this.offsetY = offsetY;\n    const centerX = (viewBox[2] + viewBox[0]) / 2;\n    const centerY = (viewBox[3] + viewBox[1]) / 2;\n    let rotateA, rotateB, rotateC, rotateD;\n    rotation %= 360;\n    if (rotation < 0) {\n      rotation += 360;\n    }\n    switch (rotation) {\n      case 180:\n        rotateA = -1;\n        rotateB = 0;\n        rotateC = 0;\n        rotateD = 1;\n        break;\n      case 90:\n        rotateA = 0;\n        rotateB = 1;\n        rotateC = 1;\n        rotateD = 0;\n        break;\n      case 270:\n        rotateA = 0;\n        rotateB = -1;\n        rotateC = -1;\n        rotateD = 0;\n        break;\n      case 0:\n        rotateA = 1;\n        rotateB = 0;\n        rotateC = 0;\n        rotateD = -1;\n        break;\n      default:\n        throw new Error(\"PageViewport: Invalid rotation, must be a multiple of 90 degrees.\");\n    }\n    if (dontFlip) {\n      rotateC = -rotateC;\n      rotateD = -rotateD;\n    }\n    let offsetCanvasX, offsetCanvasY;\n    let width, height;\n    if (rotateA === 0) {\n      offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;\n      offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;\n      width = (viewBox[3] - viewBox[1]) * scale;\n      height = (viewBox[2] - viewBox[0]) * scale;\n    } else {\n      offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;\n      offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;\n      width = (viewBox[2] - viewBox[0]) * scale;\n      height = (viewBox[3] - viewBox[1]) * scale;\n    }\n    this.transform = [rotateA * scale, rotateB * scale, rotateC * scale, rotateD * scale, offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY];\n    this.width = width;\n    this.height = height;\n  }\n  get rawDims() {\n    const {\n      viewBox\n    } = this;\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_1__.shadow)(this, \"rawDims\", {\n      pageWidth: viewBox[2] - viewBox[0],\n      pageHeight: viewBox[3] - viewBox[1],\n      pageX: viewBox[0],\n      pageY: viewBox[1]\n    });\n  }\n  clone({\n    scale = this.scale,\n    rotation = this.rotation,\n    offsetX = this.offsetX,\n    offsetY = this.offsetY,\n    dontFlip = false\n  } = {}) {\n    return new PageViewport({\n      viewBox: this.viewBox.slice(),\n      scale,\n      rotation,\n      offsetX,\n      offsetY,\n      dontFlip\n    });\n  }\n  convertToViewportPoint(x, y) {\n    return _shared_util_js__WEBPACK_IMPORTED_MODULE_1__.Util.applyTransform([x, y], this.transform);\n  }\n  convertToViewportRectangle(rect) {\n    const topLeft = _shared_util_js__WEBPACK_IMPORTED_MODULE_1__.Util.applyTransform([rect[0], rect[1]], this.transform);\n    const bottomRight = _shared_util_js__WEBPACK_IMPORTED_MODULE_1__.Util.applyTransform([rect[2], rect[3]], this.transform);\n    return [topLeft[0], topLeft[1], bottomRight[0], bottomRight[1]];\n  }\n  convertToPdfPoint(x, y) {\n    return _shared_util_js__WEBPACK_IMPORTED_MODULE_1__.Util.applyInverseTransform([x, y], this.transform);\n  }\n}\nclass RenderingCancelledException extends _shared_util_js__WEBPACK_IMPORTED_MODULE_1__.BaseException {\n  constructor(msg, extraDelay = 0) {\n    super(msg, \"RenderingCancelledException\");\n    this.extraDelay = extraDelay;\n  }\n}\nfunction isDataScheme(url) {\n  const ii = url.length;\n  let i = 0;\n  while (i < ii && url[i].trim() === \"\") {\n    i++;\n  }\n  return url.substring(i, i + 5).toLowerCase() === \"data:\";\n}\nfunction isPdfFile(filename) {\n  return typeof filename === \"string\" && /\\.pdf$/i.test(filename);\n}\nfunction getFilenameFromUrl(url, onlyStripPath = false) {\n  if (!onlyStripPath) {\n    [url] = url.split(/[#?]/, 1);\n  }\n  return url.substring(url.lastIndexOf(\"/\") + 1);\n}\nfunction getPdfFilenameFromUrl(url, defaultFilename = \"document.pdf\") {\n  if (typeof url !== \"string\") {\n    return defaultFilename;\n  }\n  if (isDataScheme(url)) {\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_1__.warn)('getPdfFilenameFromUrl: ignore \"data:\"-URL for performance reasons.');\n    return defaultFilename;\n  }\n  const reURI = /^(?:(?:[^:]+:)?\\/\\/[^/]+)?([^?#]*)(\\?[^#]*)?(#.*)?$/;\n  const reFilename = /[^/?#=]+\\.pdf\\b(?!.*\\.pdf\\b)/i;\n  const splitURI = reURI.exec(url);\n  let suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]);\n  if (suggestedFilename) {\n    suggestedFilename = suggestedFilename[0];\n    if (suggestedFilename.includes(\"%\")) {\n      try {\n        suggestedFilename = reFilename.exec(decodeURIComponent(suggestedFilename))[0];\n      } catch {}\n    }\n  }\n  return suggestedFilename || defaultFilename;\n}\nclass StatTimer {\n  started = Object.create(null);\n  times = [];\n  time(name) {\n    if (name in this.started) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_1__.warn)(`Timer is already running for ${name}`);\n    }\n    this.started[name] = Date.now();\n  }\n  timeEnd(name) {\n    if (!(name in this.started)) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_1__.warn)(`Timer has not been started for ${name}`);\n    }\n    this.times.push({\n      name,\n      start: this.started[name],\n      end: Date.now()\n    });\n    delete this.started[name];\n  }\n  toString() {\n    const outBuf = [];\n    let longest = 0;\n    for (const {\n      name\n    } of this.times) {\n      longest = Math.max(name.length, longest);\n    }\n    for (const {\n      name,\n      start,\n      end\n    } of this.times) {\n      outBuf.push(`${name.padEnd(longest)} ${end - start}ms\\n`);\n    }\n    return outBuf.join(\"\");\n  }\n}\nfunction isValidFetchUrl(url, baseUrl) {\n  try {\n    const {\n      protocol\n    } = baseUrl ? new URL(url, baseUrl) : new URL(url);\n    return protocol === \"http:\" || protocol === \"https:\";\n  } catch {\n    return false;\n  }\n}\nfunction noContextMenu(e) {\n  e.preventDefault();\n}\nfunction deprecated(details) {\n  console.log(\"Deprecated API usage: \" + details);\n}\nlet pdfDateStringRegex;\nclass PDFDateString {\n  static toDateObject(input) {\n    if (!input || typeof input !== \"string\") {\n      return null;\n    }\n    pdfDateStringRegex ||= new RegExp(\"^D:\" + \"(\\\\d{4})\" + \"(\\\\d{2})?\" + \"(\\\\d{2})?\" + \"(\\\\d{2})?\" + \"(\\\\d{2})?\" + \"(\\\\d{2})?\" + \"([Z|+|-])?\" + \"(\\\\d{2})?\" + \"'?\" + \"(\\\\d{2})?\" + \"'?\");\n    const matches = pdfDateStringRegex.exec(input);\n    if (!matches) {\n      return null;\n    }\n    const year = parseInt(matches[1], 10);\n    let month = parseInt(matches[2], 10);\n    month = month >= 1 && month <= 12 ? month - 1 : 0;\n    let day = parseInt(matches[3], 10);\n    day = day >= 1 && day <= 31 ? day : 1;\n    let hour = parseInt(matches[4], 10);\n    hour = hour >= 0 && hour <= 23 ? hour : 0;\n    let minute = parseInt(matches[5], 10);\n    minute = minute >= 0 && minute <= 59 ? minute : 0;\n    let second = parseInt(matches[6], 10);\n    second = second >= 0 && second <= 59 ? second : 0;\n    const universalTimeRelation = matches[7] || \"Z\";\n    let offsetHour = parseInt(matches[8], 10);\n    offsetHour = offsetHour >= 0 && offsetHour <= 23 ? offsetHour : 0;\n    let offsetMinute = parseInt(matches[9], 10) || 0;\n    offsetMinute = offsetMinute >= 0 && offsetMinute <= 59 ? offsetMinute : 0;\n    if (universalTimeRelation === \"-\") {\n      hour += offsetHour;\n      minute += offsetMinute;\n    } else if (universalTimeRelation === \"+\") {\n      hour -= offsetHour;\n      minute -= offsetMinute;\n    }\n    return new Date(Date.UTC(year, month, day, hour, minute, second));\n  }\n}\nfunction getXfaPageViewport(xfaPage, {\n  scale = 1,\n  rotation = 0\n}) {\n  const {\n    width,\n    height\n  } = xfaPage.attributes.style;\n  const viewBox = [0, 0, parseInt(width), parseInt(height)];\n  return new PageViewport({\n    viewBox,\n    scale,\n    rotation\n  });\n}\nfunction getRGB(color) {\n  if (color.startsWith(\"#\")) {\n    const colorRGB = parseInt(color.slice(1), 16);\n    return [(colorRGB & 0xff0000) >> 16, (colorRGB & 0x00ff00) >> 8, colorRGB & 0x0000ff];\n  }\n  if (color.startsWith(\"rgb(\")) {\n    return color.slice(4, -1).split(\",\").map(x => parseInt(x));\n  }\n  if (color.startsWith(\"rgba(\")) {\n    return color.slice(5, -1).split(\",\").map(x => parseInt(x)).slice(0, 3);\n  }\n  (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_1__.warn)(`Not a valid color format: \"${color}\"`);\n  return [0, 0, 0];\n}\nfunction getColorValues(colors) {\n  const span = document.createElement(\"span\");\n  span.style.visibility = \"hidden\";\n  document.body.append(span);\n  for (const name of colors.keys()) {\n    span.style.color = name;\n    const computedColor = window.getComputedStyle(span).color;\n    colors.set(name, getRGB(computedColor));\n  }\n  span.remove();\n}\nfunction getCurrentTransform(ctx) {\n  const {\n    a,\n    b,\n    c,\n    d,\n    e,\n    f\n  } = ctx.getTransform();\n  return [a, b, c, d, e, f];\n}\nfunction getCurrentTransformInverse(ctx) {\n  const {\n    a,\n    b,\n    c,\n    d,\n    e,\n    f\n  } = ctx.getTransform().invertSelf();\n  return [a, b, c, d, e, f];\n}\nfunction setLayerDimensions(div, viewport, mustFlip = false, mustRotate = true) {\n  if (viewport instanceof PageViewport) {\n    const {\n      pageWidth,\n      pageHeight\n    } = viewport.rawDims;\n    const {\n      style\n    } = div;\n    const useRound = _shared_util_js__WEBPACK_IMPORTED_MODULE_1__.FeatureTest.isCSSRoundSupported;\n    const w = `var(--scale-factor) * ${pageWidth}px`,\n      h = `var(--scale-factor) * ${pageHeight}px`;\n    const widthStr = useRound ? `round(${w}, 1px)` : `calc(${w})`,\n      heightStr = useRound ? `round(${h}, 1px)` : `calc(${h})`;\n    if (!mustFlip || viewport.rotation % 180 === 0) {\n      style.width = widthStr;\n      style.height = heightStr;\n    } else {\n      style.width = heightStr;\n      style.height = widthStr;\n    }\n  }\n  if (mustRotate) {\n    div.setAttribute(\"data-main-rotation\", viewport.rotation);\n  }\n}\n\n\n/***/ }),\n\n/***/ 423:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   DrawLayer: () => (/* binding */ DrawLayer)\n/* harmony export */ });\n/* harmony import */ var _display_utils_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(473);\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(266);\n\n\nclass DrawLayer {\n  #parent = null;\n  #id = 0;\n  #mapping = new Map();\n  constructor({\n    pageIndex\n  }) {\n    this.pageIndex = pageIndex;\n  }\n  setParent(parent) {\n    if (!this.#parent) {\n      this.#parent = parent;\n      return;\n    }\n    if (this.#parent !== parent) {\n      if (this.#mapping.size > 0) {\n        for (const root of this.#mapping.values()) {\n          root.remove();\n          parent.append(root);\n        }\n      }\n      this.#parent = parent;\n    }\n  }\n  static get _svgFactory() {\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_1__.shadow)(this, \"_svgFactory\", new _display_utils_js__WEBPACK_IMPORTED_MODULE_0__.DOMSVGFactory());\n  }\n  static #setBox(element, {\n    x,\n    y,\n    width,\n    height\n  }) {\n    const {\n      style\n    } = element;\n    style.top = `${100 * y}%`;\n    style.left = `${100 * x}%`;\n    style.width = `${100 * width}%`;\n    style.height = `${100 * height}%`;\n  }\n  #createSVG(box) {\n    const svg = DrawLayer._svgFactory.create(1, 1, true);\n    this.#parent.append(svg);\n    DrawLayer.#setBox(svg, box);\n    return svg;\n  }\n  highlight({\n    outlines,\n    box\n  }, color, opacity) {\n    const id = this.#id++;\n    const root = this.#createSVG(box);\n    root.classList.add(\"highlight\");\n    const defs = DrawLayer._svgFactory.createElement(\"defs\");\n    root.append(defs);\n    const path = DrawLayer._svgFactory.createElement(\"path\");\n    defs.append(path);\n    const pathId = `path_p${this.pageIndex}_${id}`;\n    path.setAttribute(\"id\", pathId);\n    path.setAttribute(\"d\", DrawLayer.#extractPathFromHighlightOutlines(outlines));\n    const clipPath = DrawLayer._svgFactory.createElement(\"clipPath\");\n    defs.append(clipPath);\n    const clipPathId = `clip_${pathId}`;\n    clipPath.setAttribute(\"id\", clipPathId);\n    clipPath.setAttribute(\"clipPathUnits\", \"objectBoundingBox\");\n    const clipPathUse = DrawLayer._svgFactory.createElement(\"use\");\n    clipPath.append(clipPathUse);\n    clipPathUse.setAttribute(\"href\", `#${pathId}`);\n    clipPathUse.classList.add(\"clip\");\n    const use = DrawLayer._svgFactory.createElement(\"use\");\n    root.append(use);\n    root.setAttribute(\"fill\", color);\n    root.setAttribute(\"fill-opacity\", opacity);\n    use.setAttribute(\"href\", `#${pathId}`);\n    this.#mapping.set(id, root);\n    return {\n      id,\n      clipPathId: `url(#${clipPathId})`\n    };\n  }\n  highlightOutline({\n    outlines,\n    box\n  }) {\n    const id = this.#id++;\n    const root = this.#createSVG(box);\n    root.classList.add(\"highlightOutline\");\n    const defs = DrawLayer._svgFactory.createElement(\"defs\");\n    root.append(defs);\n    const path = DrawLayer._svgFactory.createElement(\"path\");\n    defs.append(path);\n    const pathId = `path_p${this.pageIndex}_${id}`;\n    path.setAttribute(\"id\", pathId);\n    path.setAttribute(\"d\", DrawLayer.#extractPathFromHighlightOutlines(outlines));\n    path.setAttribute(\"vector-effect\", \"non-scaling-stroke\");\n    const use1 = DrawLayer._svgFactory.createElement(\"use\");\n    root.append(use1);\n    use1.setAttribute(\"href\", `#${pathId}`);\n    const use2 = use1.cloneNode();\n    root.append(use2);\n    use1.classList.add(\"mainOutline\");\n    use2.classList.add(\"secondaryOutline\");\n    this.#mapping.set(id, root);\n    return id;\n  }\n  static #extractPathFromHighlightOutlines(polygons) {\n    const buffer = [];\n    for (const polygon of polygons) {\n      let [prevX, prevY] = polygon;\n      buffer.push(`M${prevX} ${prevY}`);\n      for (let i = 2; i < polygon.length; i += 2) {\n        const x = polygon[i];\n        const y = polygon[i + 1];\n        if (x === prevX) {\n          buffer.push(`V${y}`);\n          prevY = y;\n        } else if (y === prevY) {\n          buffer.push(`H${x}`);\n          prevX = x;\n        }\n      }\n      buffer.push(\"Z\");\n    }\n    return buffer.join(\" \");\n  }\n  updateBox(id, box) {\n    DrawLayer.#setBox(this.#mapping.get(id), box);\n  }\n  rotate(id, angle) {\n    this.#mapping.get(id).setAttribute(\"data-main-rotation\", angle);\n  }\n  changeColor(id, color) {\n    this.#mapping.get(id).setAttribute(\"fill\", color);\n  }\n  changeOpacity(id, opacity) {\n    this.#mapping.get(id).setAttribute(\"fill-opacity\", opacity);\n  }\n  addClass(id, className) {\n    this.#mapping.get(id).classList.add(className);\n  }\n  removeClass(id, className) {\n    this.#mapping.get(id).classList.remove(className);\n  }\n  remove(id) {\n    this.#mapping.get(id).remove();\n    this.#mapping.delete(id);\n  }\n  destroy() {\n    this.#parent = null;\n    for (const root of this.#mapping.values()) {\n      root.remove();\n    }\n    this.#mapping.clear();\n  }\n}\n\n\n/***/ }),\n\n/***/ 331:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n  AnnotationEditorLayer: () => (/* binding */ AnnotationEditorLayer)\n});\n\n// EXTERNAL MODULE: ./src/shared/util.js\nvar util = __webpack_require__(266);\n// EXTERNAL MODULE: ./src/display/editor/editor.js + 1 modules\nvar editor_editor = __webpack_require__(796);\n// EXTERNAL MODULE: ./src/display/editor/tools.js\nvar tools = __webpack_require__(812);\n// EXTERNAL MODULE: ./src/display/annotation_layer.js + 1 modules\nvar annotation_layer = __webpack_require__(640);\n;// CONCATENATED MODULE: ./src/display/editor/freetext.js\n\n\n\n\nclass FreeTextEditor extends editor_editor.AnnotationEditor {\n  #boundEditorDivBlur = this.editorDivBlur.bind(this);\n  #boundEditorDivFocus = this.editorDivFocus.bind(this);\n  #boundEditorDivInput = this.editorDivInput.bind(this);\n  #boundEditorDivKeydown = this.editorDivKeydown.bind(this);\n  #color;\n  #content = \"\";\n  #editorDivId = `${this.id}-editor`;\n  #fontSize;\n  #initialData = null;\n  static _freeTextDefaultContent = \"\";\n  static _internalPadding = 0;\n  static _defaultColor = null;\n  static _defaultFontSize = 10;\n  static get _keyboardManager() {\n    const proto = FreeTextEditor.prototype;\n    const arrowChecker = self => self.isEmpty();\n    const small = tools.AnnotationEditorUIManager.TRANSLATE_SMALL;\n    const big = tools.AnnotationEditorUIManager.TRANSLATE_BIG;\n    return (0,util.shadow)(this, \"_keyboardManager\", new tools.KeyboardManager([[[\"ctrl+s\", \"mac+meta+s\", \"ctrl+p\", \"mac+meta+p\"], proto.commitOrRemove, {\n      bubbles: true\n    }], [[\"ctrl+Enter\", \"mac+meta+Enter\", \"Escape\", \"mac+Escape\"], proto.commitOrRemove], [[\"ArrowLeft\", \"mac+ArrowLeft\"], proto._translateEmpty, {\n      args: [-small, 0],\n      checker: arrowChecker\n    }], [[\"ctrl+ArrowLeft\", \"mac+shift+ArrowLeft\"], proto._translateEmpty, {\n      args: [-big, 0],\n      checker: arrowChecker\n    }], [[\"ArrowRight\", \"mac+ArrowRight\"], proto._translateEmpty, {\n      args: [small, 0],\n      checker: arrowChecker\n    }], [[\"ctrl+ArrowRight\", \"mac+shift+ArrowRight\"], proto._translateEmpty, {\n      args: [big, 0],\n      checker: arrowChecker\n    }], [[\"ArrowUp\", \"mac+ArrowUp\"], proto._translateEmpty, {\n      args: [0, -small],\n      checker: arrowChecker\n    }], [[\"ctrl+ArrowUp\", \"mac+shift+ArrowUp\"], proto._translateEmpty, {\n      args: [0, -big],\n      checker: arrowChecker\n    }], [[\"ArrowDown\", \"mac+ArrowDown\"], proto._translateEmpty, {\n      args: [0, small],\n      checker: arrowChecker\n    }], [[\"ctrl+ArrowDown\", \"mac+shift+ArrowDown\"], proto._translateEmpty, {\n      args: [0, big],\n      checker: arrowChecker\n    }]]));\n  }\n  static _type = \"freetext\";\n  static _editorType = util.AnnotationEditorType.FREETEXT;\n  constructor(params) {\n    super({\n      ...params,\n      name: \"freeTextEditor\"\n    });\n    this.#color = params.color || FreeTextEditor._defaultColor || editor_editor.AnnotationEditor._defaultLineColor;\n    this.#fontSize = params.fontSize || FreeTextEditor._defaultFontSize;\n  }\n  static initialize(l10n) {\n    editor_editor.AnnotationEditor.initialize(l10n, {\n      strings: [\"pdfjs-free-text-default-content\"]\n    });\n    const style = getComputedStyle(document.documentElement);\n    this._internalPadding = parseFloat(style.getPropertyValue(\"--freetext-padding\"));\n  }\n  static updateDefaultParams(type, value) {\n    switch (type) {\n      case util.AnnotationEditorParamsType.FREETEXT_SIZE:\n        FreeTextEditor._defaultFontSize = value;\n        break;\n      case util.AnnotationEditorParamsType.FREETEXT_COLOR:\n        FreeTextEditor._defaultColor = value;\n        break;\n    }\n  }\n  updateParams(type, value) {\n    switch (type) {\n      case util.AnnotationEditorParamsType.FREETEXT_SIZE:\n        this.#updateFontSize(value);\n        break;\n      case util.AnnotationEditorParamsType.FREETEXT_COLOR:\n        this.#updateColor(value);\n        break;\n    }\n  }\n  static get defaultPropertiesToUpdate() {\n    return [[util.AnnotationEditorParamsType.FREETEXT_SIZE, FreeTextEditor._defaultFontSize], [util.AnnotationEditorParamsType.FREETEXT_COLOR, FreeTextEditor._defaultColor || editor_editor.AnnotationEditor._defaultLineColor]];\n  }\n  get propertiesToUpdate() {\n    return [[util.AnnotationEditorParamsType.FREETEXT_SIZE, this.#fontSize], [util.AnnotationEditorParamsType.FREETEXT_COLOR, this.#color]];\n  }\n  #updateFontSize(fontSize) {\n    const setFontsize = size => {\n      this.editorDiv.style.fontSize = `calc(${size}px * var(--scale-factor))`;\n      this.translate(0, -(size - this.#fontSize) * this.parentScale);\n      this.#fontSize = size;\n      this.#setEditorDimensions();\n    };\n    const savedFontsize = this.#fontSize;\n    this.addCommands({\n      cmd: () => {\n        setFontsize(fontSize);\n      },\n      undo: () => {\n        setFontsize(savedFontsize);\n      },\n      mustExec: true,\n      type: util.AnnotationEditorParamsType.FREETEXT_SIZE,\n      overwriteIfSameType: true,\n      keepUndo: true\n    });\n  }\n  #updateColor(color) {\n    const savedColor = this.#color;\n    this.addCommands({\n      cmd: () => {\n        this.#color = this.editorDiv.style.color = color;\n      },\n      undo: () => {\n        this.#color = this.editorDiv.style.color = savedColor;\n      },\n      mustExec: true,\n      type: util.AnnotationEditorParamsType.FREETEXT_COLOR,\n      overwriteIfSameType: true,\n      keepUndo: true\n    });\n  }\n  _translateEmpty(x, y) {\n    this._uiManager.translateSelectedEditors(x, y, true);\n  }\n  getInitialTranslation() {\n    const scale = this.parentScale;\n    return [-FreeTextEditor._internalPadding * scale, -(FreeTextEditor._internalPadding + this.#fontSize) * scale];\n  }\n  rebuild() {\n    if (!this.parent) {\n      return;\n    }\n    super.rebuild();\n    if (this.div === null) {\n      return;\n    }\n    if (!this.isAttachedToDOM) {\n      this.parent.add(this);\n    }\n  }\n  enableEditMode() {\n    if (this.isInEditMode()) {\n      return;\n    }\n    this.parent.setEditingState(false);\n    this.parent.updateToolbar(util.AnnotationEditorType.FREETEXT);\n    super.enableEditMode();\n    this.overlayDiv.classList.remove(\"enabled\");\n    this.editorDiv.contentEditable = true;\n    this._isDraggable = false;\n    this.div.removeAttribute(\"aria-activedescendant\");\n    this.editorDiv.addEventListener(\"keydown\", this.#boundEditorDivKeydown);\n    this.editorDiv.addEventListener(\"focus\", this.#boundEditorDivFocus);\n    this.editorDiv.addEventListener(\"blur\", this.#boundEditorDivBlur);\n    this.editorDiv.addEventListener(\"input\", this.#boundEditorDivInput);\n  }\n  disableEditMode() {\n    if (!this.isInEditMode()) {\n      return;\n    }\n    this.parent.setEditingState(true);\n    super.disableEditMode();\n    this.overlayDiv.classList.add(\"enabled\");\n    this.editorDiv.contentEditable = false;\n    this.div.setAttribute(\"aria-activedescendant\", this.#editorDivId);\n    this._isDraggable = true;\n    this.editorDiv.removeEventListener(\"keydown\", this.#boundEditorDivKeydown);\n    this.editorDiv.removeEventListener(\"focus\", this.#boundEditorDivFocus);\n    this.editorDiv.removeEventListener(\"blur\", this.#boundEditorDivBlur);\n    this.editorDiv.removeEventListener(\"input\", this.#boundEditorDivInput);\n    this.div.focus({\n      preventScroll: true\n    });\n    this.isEditing = false;\n    this.parent.div.classList.add(\"freetextEditing\");\n  }\n  focusin(event) {\n    if (!this._focusEventsAllowed) {\n      return;\n    }\n    super.focusin(event);\n    if (event.target !== this.editorDiv) {\n      this.editorDiv.focus();\n    }\n  }\n  onceAdded() {\n    if (this.width) {\n      this.#cheatInitialRect();\n      return;\n    }\n    this.enableEditMode();\n    this.editorDiv.focus();\n    if (this._initialOptions?.isCentered) {\n      this.center();\n    }\n    this._initialOptions = null;\n  }\n  isEmpty() {\n    return !this.editorDiv || this.editorDiv.innerText.trim() === \"\";\n  }\n  remove() {\n    this.isEditing = false;\n    if (this.parent) {\n      this.parent.setEditingState(true);\n      this.parent.div.classList.add(\"freetextEditing\");\n    }\n    super.remove();\n  }\n  #extractText() {\n    const divs = this.editorDiv.getElementsByTagName(\"div\");\n    if (divs.length === 0) {\n      return this.editorDiv.innerText;\n    }\n    const buffer = [];\n    for (const div of divs) {\n      buffer.push(div.innerText.replace(/\\r\\n?|\\n/, \"\"));\n    }\n    return buffer.join(\"\\n\");\n  }\n  #setEditorDimensions() {\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    let rect;\n    if (this.isAttachedToDOM) {\n      rect = this.div.getBoundingClientRect();\n    } else {\n      const {\n        currentLayer,\n        div\n      } = this;\n      const savedDisplay = div.style.display;\n      div.style.display = \"hidden\";\n      currentLayer.div.append(this.div);\n      rect = div.getBoundingClientRect();\n      div.remove();\n      div.style.display = savedDisplay;\n    }\n    if (this.rotation % 180 === this.parentRotation % 180) {\n      this.width = rect.width / parentWidth;\n      this.height = rect.height / parentHeight;\n    } else {\n      this.width = rect.height / parentWidth;\n      this.height = rect.width / parentHeight;\n    }\n    this.fixAndSetPosition();\n  }\n  commit() {\n    if (!this.isInEditMode()) {\n      return;\n    }\n    super.commit();\n    this.disableEditMode();\n    const savedText = this.#content;\n    const newText = this.#content = this.#extractText().trimEnd();\n    if (savedText === newText) {\n      return;\n    }\n    const setText = text => {\n      this.#content = text;\n      if (!text) {\n        this.remove();\n        return;\n      }\n      this.#setContent();\n      this._uiManager.rebuild(this);\n      this.#setEditorDimensions();\n    };\n    this.addCommands({\n      cmd: () => {\n        setText(newText);\n      },\n      undo: () => {\n        setText(savedText);\n      },\n      mustExec: false\n    });\n    this.#setEditorDimensions();\n  }\n  shouldGetKeyboardEvents() {\n    return this.isInEditMode();\n  }\n  enterInEditMode() {\n    this.enableEditMode();\n    this.editorDiv.focus();\n  }\n  dblclick(event) {\n    this.enterInEditMode();\n  }\n  keydown(event) {\n    if (event.target === this.div && event.key === \"Enter\") {\n      this.enterInEditMode();\n      event.preventDefault();\n    }\n  }\n  editorDivKeydown(event) {\n    FreeTextEditor._keyboardManager.exec(this, event);\n  }\n  editorDivFocus(event) {\n    this.isEditing = true;\n  }\n  editorDivBlur(event) {\n    this.isEditing = false;\n  }\n  editorDivInput(event) {\n    this.parent.div.classList.toggle(\"freetextEditing\", this.isEmpty());\n  }\n  disableEditing() {\n    this.editorDiv.setAttribute(\"role\", \"comment\");\n    this.editorDiv.removeAttribute(\"aria-multiline\");\n  }\n  enableEditing() {\n    this.editorDiv.setAttribute(\"role\", \"textbox\");\n    this.editorDiv.setAttribute(\"aria-multiline\", true);\n  }\n  render() {\n    if (this.div) {\n      return this.div;\n    }\n    let baseX, baseY;\n    if (this.width) {\n      baseX = this.x;\n      baseY = this.y;\n    }\n    super.render();\n    this.editorDiv = document.createElement(\"div\");\n    this.editorDiv.className = \"internal\";\n    this.editorDiv.setAttribute(\"id\", this.#editorDivId);\n    this.editorDiv.setAttribute(\"data-l10n-id\", \"pdfjs-free-text\");\n    this.enableEditing();\n    editor_editor.AnnotationEditor._l10nPromise.get(\"pdfjs-free-text-default-content\").then(msg => this.editorDiv?.setAttribute(\"default-content\", msg));\n    this.editorDiv.contentEditable = true;\n    const {\n      style\n    } = this.editorDiv;\n    style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`;\n    style.color = this.#color;\n    this.div.append(this.editorDiv);\n    this.overlayDiv = document.createElement(\"div\");\n    this.overlayDiv.classList.add(\"overlay\", \"enabled\");\n    this.div.append(this.overlayDiv);\n    (0,tools.bindEvents)(this, this.div, [\"dblclick\", \"keydown\"]);\n    if (this.width) {\n      const [parentWidth, parentHeight] = this.parentDimensions;\n      if (this.annotationElementId) {\n        const {\n          position\n        } = this.#initialData;\n        let [tx, ty] = this.getInitialTranslation();\n        [tx, ty] = this.pageTranslationToScreen(tx, ty);\n        const [pageWidth, pageHeight] = this.pageDimensions;\n        const [pageX, pageY] = this.pageTranslation;\n        let posX, posY;\n        switch (this.rotation) {\n          case 0:\n            posX = baseX + (position[0] - pageX) / pageWidth;\n            posY = baseY + this.height - (position[1] - pageY) / pageHeight;\n            break;\n          case 90:\n            posX = baseX + (position[0] - pageX) / pageWidth;\n            posY = baseY - (position[1] - pageY) / pageHeight;\n            [tx, ty] = [ty, -tx];\n            break;\n          case 180:\n            posX = baseX - this.width + (position[0] - pageX) / pageWidth;\n            posY = baseY - (position[1] - pageY) / pageHeight;\n            [tx, ty] = [-tx, -ty];\n            break;\n          case 270:\n            posX = baseX + (position[0] - pageX - this.height * pageHeight) / pageWidth;\n            posY = baseY + (position[1] - pageY - this.width * pageWidth) / pageHeight;\n            [tx, ty] = [-ty, tx];\n            break;\n        }\n        this.setAt(posX * parentWidth, posY * parentHeight, tx, ty);\n      } else {\n        this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight);\n      }\n      this.#setContent();\n      this._isDraggable = true;\n      this.editorDiv.contentEditable = false;\n    } else {\n      this._isDraggable = false;\n      this.editorDiv.contentEditable = true;\n    }\n    return this.div;\n  }\n  #setContent() {\n    this.editorDiv.replaceChildren();\n    if (!this.#content) {\n      return;\n    }\n    for (const line of this.#content.split(\"\\n\")) {\n      const div = document.createElement(\"div\");\n      div.append(line ? document.createTextNode(line) : document.createElement(\"br\"));\n      this.editorDiv.append(div);\n    }\n  }\n  get contentDiv() {\n    return this.editorDiv;\n  }\n  static deserialize(data, parent, uiManager) {\n    let initialData = null;\n    if (data instanceof annotation_layer.FreeTextAnnotationElement) {\n      const {\n        data: {\n          defaultAppearanceData: {\n            fontSize,\n            fontColor\n          },\n          rect,\n          rotation,\n          id\n        },\n        textContent,\n        textPosition,\n        parent: {\n          page: {\n            pageNumber\n          }\n        }\n      } = data;\n      if (!textContent || textContent.length === 0) {\n        return null;\n      }\n      initialData = data = {\n        annotationType: util.AnnotationEditorType.FREETEXT,\n        color: Array.from(fontColor),\n        fontSize,\n        value: textContent.join(\"\\n\"),\n        position: textPosition,\n        pageIndex: pageNumber - 1,\n        rect,\n        rotation,\n        id,\n        deleted: false\n      };\n    }\n    const editor = super.deserialize(data, parent, uiManager);\n    editor.#fontSize = data.fontSize;\n    editor.#color = util.Util.makeHexColor(...data.color);\n    editor.#content = data.value;\n    editor.annotationElementId = data.id || null;\n    editor.#initialData = initialData;\n    return editor;\n  }\n  serialize(isForCopying = false) {\n    if (this.isEmpty()) {\n      return null;\n    }\n    if (this.deleted) {\n      return {\n        pageIndex: this.pageIndex,\n        id: this.annotationElementId,\n        deleted: true\n      };\n    }\n    const padding = FreeTextEditor._internalPadding * this.parentScale;\n    const rect = this.getRect(padding, padding);\n    const color = editor_editor.AnnotationEditor._colorManager.convert(this.isAttachedToDOM ? getComputedStyle(this.editorDiv).color : this.#color);\n    const serialized = {\n      annotationType: util.AnnotationEditorType.FREETEXT,\n      color,\n      fontSize: this.#fontSize,\n      value: this.#content,\n      pageIndex: this.pageIndex,\n      rect,\n      rotation: this.rotation,\n      structTreeParentId: this._structTreeParentId\n    };\n    if (isForCopying) {\n      return serialized;\n    }\n    if (this.annotationElementId && !this.#hasElementChanged(serialized)) {\n      return null;\n    }\n    serialized.id = this.annotationElementId;\n    return serialized;\n  }\n  #hasElementChanged(serialized) {\n    const {\n      value,\n      fontSize,\n      color,\n      rect,\n      pageIndex\n    } = this.#initialData;\n    return serialized.value !== value || serialized.fontSize !== fontSize || serialized.rect.some((x, i) => Math.abs(x - rect[i]) >= 1) || serialized.color.some((c, i) => c !== color[i]) || serialized.pageIndex !== pageIndex;\n  }\n  #cheatInitialRect(delayed = false) {\n    if (!this.annotationElementId) {\n      return;\n    }\n    this.#setEditorDimensions();\n    if (!delayed && (this.width === 0 || this.height === 0)) {\n      setTimeout(() => this.#cheatInitialRect(true), 0);\n      return;\n    }\n    const padding = FreeTextEditor._internalPadding * this.parentScale;\n    this.#initialData.rect = this.getRect(padding, padding);\n  }\n}\n\n// EXTERNAL MODULE: ./src/display/display_utils.js\nvar display_utils = __webpack_require__(473);\n;// CONCATENATED MODULE: ./src/display/editor/ink.js\n\n\n\n\n\nclass InkEditor extends editor_editor.AnnotationEditor {\n  #baseHeight = 0;\n  #baseWidth = 0;\n  #boundCanvasPointermove = this.canvasPointermove.bind(this);\n  #boundCanvasPointerleave = this.canvasPointerleave.bind(this);\n  #boundCanvasPointerup = this.canvasPointerup.bind(this);\n  #boundCanvasPointerdown = this.canvasPointerdown.bind(this);\n  #canvasContextMenuTimeoutId = null;\n  #currentPath2D = new Path2D();\n  #disableEditing = false;\n  #hasSomethingToDraw = false;\n  #isCanvasInitialized = false;\n  #observer = null;\n  #realWidth = 0;\n  #realHeight = 0;\n  #requestFrameCallback = null;\n  static _defaultColor = null;\n  static _defaultOpacity = 1;\n  static _defaultThickness = 1;\n  static _type = \"ink\";\n  static _editorType = util.AnnotationEditorType.INK;\n  constructor(params) {\n    super({\n      ...params,\n      name: \"inkEditor\"\n    });\n    this.color = params.color || null;\n    this.thickness = params.thickness || null;\n    this.opacity = params.opacity || null;\n    this.paths = [];\n    this.bezierPath2D = [];\n    this.allRawPaths = [];\n    this.currentPath = [];\n    this.scaleFactor = 1;\n    this.translationX = this.translationY = 0;\n    this.x = 0;\n    this.y = 0;\n    this._willKeepAspectRatio = true;\n  }\n  static initialize(l10n) {\n    editor_editor.AnnotationEditor.initialize(l10n);\n  }\n  static updateDefaultParams(type, value) {\n    switch (type) {\n      case util.AnnotationEditorParamsType.INK_THICKNESS:\n        InkEditor._defaultThickness = value;\n        break;\n      case util.AnnotationEditorParamsType.INK_COLOR:\n        InkEditor._defaultColor = value;\n        break;\n      case util.AnnotationEditorParamsType.INK_OPACITY:\n        InkEditor._defaultOpacity = value / 100;\n        break;\n    }\n  }\n  updateParams(type, value) {\n    switch (type) {\n      case util.AnnotationEditorParamsType.INK_THICKNESS:\n        this.#updateThickness(value);\n        break;\n      case util.AnnotationEditorParamsType.INK_COLOR:\n        this.#updateColor(value);\n        break;\n      case util.AnnotationEditorParamsType.INK_OPACITY:\n        this.#updateOpacity(value);\n        break;\n    }\n  }\n  static get defaultPropertiesToUpdate() {\n    return [[util.AnnotationEditorParamsType.INK_THICKNESS, InkEditor._defaultThickness], [util.AnnotationEditorParamsType.INK_COLOR, InkEditor._defaultColor || editor_editor.AnnotationEditor._defaultLineColor], [util.AnnotationEditorParamsType.INK_OPACITY, Math.round(InkEditor._defaultOpacity * 100)]];\n  }\n  get propertiesToUpdate() {\n    return [[util.AnnotationEditorParamsType.INK_THICKNESS, this.thickness || InkEditor._defaultThickness], [util.AnnotationEditorParamsType.INK_COLOR, this.color || InkEditor._defaultColor || editor_editor.AnnotationEditor._defaultLineColor], [util.AnnotationEditorParamsType.INK_OPACITY, Math.round(100 * (this.opacity ?? InkEditor._defaultOpacity))]];\n  }\n  #updateThickness(thickness) {\n    const savedThickness = this.thickness;\n    this.addCommands({\n      cmd: () => {\n        this.thickness = thickness;\n        this.#fitToContent();\n      },\n      undo: () => {\n        this.thickness = savedThickness;\n        this.#fitToContent();\n      },\n      mustExec: true,\n      type: util.AnnotationEditorParamsType.INK_THICKNESS,\n      overwriteIfSameType: true,\n      keepUndo: true\n    });\n  }\n  #updateColor(color) {\n    const savedColor = this.color;\n    this.addCommands({\n      cmd: () => {\n        this.color = color;\n        this.#redraw();\n      },\n      undo: () => {\n        this.color = savedColor;\n        this.#redraw();\n      },\n      mustExec: true,\n      type: util.AnnotationEditorParamsType.INK_COLOR,\n      overwriteIfSameType: true,\n      keepUndo: true\n    });\n  }\n  #updateOpacity(opacity) {\n    opacity /= 100;\n    const savedOpacity = this.opacity;\n    this.addCommands({\n      cmd: () => {\n        this.opacity = opacity;\n        this.#redraw();\n      },\n      undo: () => {\n        this.opacity = savedOpacity;\n        this.#redraw();\n      },\n      mustExec: true,\n      type: util.AnnotationEditorParamsType.INK_OPACITY,\n      overwriteIfSameType: true,\n      keepUndo: true\n    });\n  }\n  rebuild() {\n    if (!this.parent) {\n      return;\n    }\n    super.rebuild();\n    if (this.div === null) {\n      return;\n    }\n    if (!this.canvas) {\n      this.#createCanvas();\n      this.#createObserver();\n    }\n    if (!this.isAttachedToDOM) {\n      this.parent.add(this);\n      this.#setCanvasDims();\n    }\n    this.#fitToContent();\n  }\n  remove() {\n    if (this.canvas === null) {\n      return;\n    }\n    if (!this.isEmpty()) {\n      this.commit();\n    }\n    this.canvas.width = this.canvas.height = 0;\n    this.canvas.remove();\n    this.canvas = null;\n    if (this.#canvasContextMenuTimeoutId) {\n      clearTimeout(this.#canvasContextMenuTimeoutId);\n      this.#canvasContextMenuTimeoutId = null;\n    }\n    this.#observer.disconnect();\n    this.#observer = null;\n    super.remove();\n  }\n  setParent(parent) {\n    if (!this.parent && parent) {\n      this._uiManager.removeShouldRescale(this);\n    } else if (this.parent && parent === null) {\n      this._uiManager.addShouldRescale(this);\n    }\n    super.setParent(parent);\n  }\n  onScaleChanging() {\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    const width = this.width * parentWidth;\n    const height = this.height * parentHeight;\n    this.setDimensions(width, height);\n  }\n  enableEditMode() {\n    if (this.#disableEditing || this.canvas === null) {\n      return;\n    }\n    super.enableEditMode();\n    this._isDraggable = false;\n    this.canvas.addEventListener(\"pointerdown\", this.#boundCanvasPointerdown);\n  }\n  disableEditMode() {\n    if (!this.isInEditMode() || this.canvas === null) {\n      return;\n    }\n    super.disableEditMode();\n    this._isDraggable = !this.isEmpty();\n    this.div.classList.remove(\"editing\");\n    this.canvas.removeEventListener(\"pointerdown\", this.#boundCanvasPointerdown);\n  }\n  onceAdded() {\n    this._isDraggable = !this.isEmpty();\n  }\n  isEmpty() {\n    return this.paths.length === 0 || this.paths.length === 1 && this.paths[0].length === 0;\n  }\n  #getInitialBBox() {\n    const {\n      parentRotation,\n      parentDimensions: [width, height]\n    } = this;\n    switch (parentRotation) {\n      case 90:\n        return [0, height, height, width];\n      case 180:\n        return [width, height, width, height];\n      case 270:\n        return [width, 0, height, width];\n      default:\n        return [0, 0, width, height];\n    }\n  }\n  #setStroke() {\n    const {\n      ctx,\n      color,\n      opacity,\n      thickness,\n      parentScale,\n      scaleFactor\n    } = this;\n    ctx.lineWidth = thickness * parentScale / scaleFactor;\n    ctx.lineCap = \"round\";\n    ctx.lineJoin = \"round\";\n    ctx.miterLimit = 10;\n    ctx.strokeStyle = `${color}${(0,tools.opacityToHex)(opacity)}`;\n  }\n  #startDrawing(x, y) {\n    this.canvas.addEventListener(\"contextmenu\", display_utils.noContextMenu);\n    this.canvas.addEventListener(\"pointerleave\", this.#boundCanvasPointerleave);\n    this.canvas.addEventListener(\"pointermove\", this.#boundCanvasPointermove);\n    this.canvas.addEventListener(\"pointerup\", this.#boundCanvasPointerup);\n    this.canvas.removeEventListener(\"pointerdown\", this.#boundCanvasPointerdown);\n    this.isEditing = true;\n    if (!this.#isCanvasInitialized) {\n      this.#isCanvasInitialized = true;\n      this.#setCanvasDims();\n      this.thickness ||= InkEditor._defaultThickness;\n      this.color ||= InkEditor._defaultColor || editor_editor.AnnotationEditor._defaultLineColor;\n      this.opacity ??= InkEditor._defaultOpacity;\n    }\n    this.currentPath.push([x, y]);\n    this.#hasSomethingToDraw = false;\n    this.#setStroke();\n    this.#requestFrameCallback = () => {\n      this.#drawPoints();\n      if (this.#requestFrameCallback) {\n        window.requestAnimationFrame(this.#requestFrameCallback);\n      }\n    };\n    window.requestAnimationFrame(this.#requestFrameCallback);\n  }\n  #draw(x, y) {\n    const [lastX, lastY] = this.currentPath.at(-1);\n    if (this.currentPath.length > 1 && x === lastX && y === lastY) {\n      return;\n    }\n    const currentPath = this.currentPath;\n    let path2D = this.#currentPath2D;\n    currentPath.push([x, y]);\n    this.#hasSomethingToDraw = true;\n    if (currentPath.length <= 2) {\n      path2D.moveTo(...currentPath[0]);\n      path2D.lineTo(x, y);\n      return;\n    }\n    if (currentPath.length === 3) {\n      this.#currentPath2D = path2D = new Path2D();\n      path2D.moveTo(...currentPath[0]);\n    }\n    this.#makeBezierCurve(path2D, ...currentPath.at(-3), ...currentPath.at(-2), x, y);\n  }\n  #endPath() {\n    if (this.currentPath.length === 0) {\n      return;\n    }\n    const lastPoint = this.currentPath.at(-1);\n    this.#currentPath2D.lineTo(...lastPoint);\n  }\n  #stopDrawing(x, y) {\n    this.#requestFrameCallback = null;\n    x = Math.min(Math.max(x, 0), this.canvas.width);\n    y = Math.min(Math.max(y, 0), this.canvas.height);\n    this.#draw(x, y);\n    this.#endPath();\n    let bezier;\n    if (this.currentPath.length !== 1) {\n      bezier = this.#generateBezierPoints();\n    } else {\n      const xy = [x, y];\n      bezier = [[xy, xy.slice(), xy.slice(), xy]];\n    }\n    const path2D = this.#currentPath2D;\n    const currentPath = this.currentPath;\n    this.currentPath = [];\n    this.#currentPath2D = new Path2D();\n    const cmd = () => {\n      this.allRawPaths.push(currentPath);\n      this.paths.push(bezier);\n      this.bezierPath2D.push(path2D);\n      this.rebuild();\n    };\n    const undo = () => {\n      this.allRawPaths.pop();\n      this.paths.pop();\n      this.bezierPath2D.pop();\n      if (this.paths.length === 0) {\n        this.remove();\n      } else {\n        if (!this.canvas) {\n          this.#createCanvas();\n          this.#createObserver();\n        }\n        this.#fitToContent();\n      }\n    };\n    this.addCommands({\n      cmd,\n      undo,\n      mustExec: true\n    });\n  }\n  #drawPoints() {\n    if (!this.#hasSomethingToDraw) {\n      return;\n    }\n    this.#hasSomethingToDraw = false;\n    const thickness = Math.ceil(this.thickness * this.parentScale);\n    const lastPoints = this.currentPath.slice(-3);\n    const x = lastPoints.map(xy => xy[0]);\n    const y = lastPoints.map(xy => xy[1]);\n    const xMin = Math.min(...x) - thickness;\n    const xMax = Math.max(...x) + thickness;\n    const yMin = Math.min(...y) - thickness;\n    const yMax = Math.max(...y) + thickness;\n    const {\n      ctx\n    } = this;\n    ctx.save();\n    ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n    for (const path of this.bezierPath2D) {\n      ctx.stroke(path);\n    }\n    ctx.stroke(this.#currentPath2D);\n    ctx.restore();\n  }\n  #makeBezierCurve(path2D, x0, y0, x1, y1, x2, y2) {\n    const prevX = (x0 + x1) / 2;\n    const prevY = (y0 + y1) / 2;\n    const x3 = (x1 + x2) / 2;\n    const y3 = (y1 + y2) / 2;\n    path2D.bezierCurveTo(prevX + 2 * (x1 - prevX) / 3, prevY + 2 * (y1 - prevY) / 3, x3 + 2 * (x1 - x3) / 3, y3 + 2 * (y1 - y3) / 3, x3, y3);\n  }\n  #generateBezierPoints() {\n    const path = this.currentPath;\n    if (path.length <= 2) {\n      return [[path[0], path[0], path.at(-1), path.at(-1)]];\n    }\n    const bezierPoints = [];\n    let i;\n    let [x0, y0] = path[0];\n    for (i = 1; i < path.length - 2; i++) {\n      const [x1, y1] = path[i];\n      const [x2, y2] = path[i + 1];\n      const x3 = (x1 + x2) / 2;\n      const y3 = (y1 + y2) / 2;\n      const control1 = [x0 + 2 * (x1 - x0) / 3, y0 + 2 * (y1 - y0) / 3];\n      const control2 = [x3 + 2 * (x1 - x3) / 3, y3 + 2 * (y1 - y3) / 3];\n      bezierPoints.push([[x0, y0], control1, control2, [x3, y3]]);\n      [x0, y0] = [x3, y3];\n    }\n    const [x1, y1] = path[i];\n    const [x2, y2] = path[i + 1];\n    const control1 = [x0 + 2 * (x1 - x0) / 3, y0 + 2 * (y1 - y0) / 3];\n    const control2 = [x2 + 2 * (x1 - x2) / 3, y2 + 2 * (y1 - y2) / 3];\n    bezierPoints.push([[x0, y0], control1, control2, [x2, y2]]);\n    return bezierPoints;\n  }\n  #redraw() {\n    if (this.isEmpty()) {\n      this.#updateTransform();\n      return;\n    }\n    this.#setStroke();\n    const {\n      canvas,\n      ctx\n    } = this;\n    ctx.setTransform(1, 0, 0, 1, 0, 0);\n    ctx.clearRect(0, 0, canvas.width, canvas.height);\n    this.#updateTransform();\n    for (const path of this.bezierPath2D) {\n      ctx.stroke(path);\n    }\n  }\n  commit() {\n    if (this.#disableEditing) {\n      return;\n    }\n    super.commit();\n    this.isEditing = false;\n    this.disableEditMode();\n    this.setInForeground();\n    this.#disableEditing = true;\n    this.div.classList.add(\"disabled\");\n    this.#fitToContent(true);\n    this.select();\n    this.parent.addInkEditorIfNeeded(true);\n    this.moveInDOM();\n    this.div.focus({\n      preventScroll: true\n    });\n  }\n  focusin(event) {\n    if (!this._focusEventsAllowed) {\n      return;\n    }\n    super.focusin(event);\n    this.enableEditMode();\n  }\n  canvasPointerdown(event) {\n    if (event.button !== 0 || !this.isInEditMode() || this.#disableEditing) {\n      return;\n    }\n    this.setInForeground();\n    event.preventDefault();\n    if (event.pointerType !== \"mouse\" && !this.div.contains(document.activeElement)) {\n      this.div.focus({\n        preventScroll: true\n      });\n    }\n    this.#startDrawing(event.offsetX, event.offsetY);\n  }\n  canvasPointermove(event) {\n    event.preventDefault();\n    this.#draw(event.offsetX, event.offsetY);\n  }\n  canvasPointerup(event) {\n    event.preventDefault();\n    this.#endDrawing(event);\n  }\n  canvasPointerleave(event) {\n    this.#endDrawing(event);\n  }\n  #endDrawing(event) {\n    this.canvas.removeEventListener(\"pointerleave\", this.#boundCanvasPointerleave);\n    this.canvas.removeEventListener(\"pointermove\", this.#boundCanvasPointermove);\n    this.canvas.removeEventListener(\"pointerup\", this.#boundCanvasPointerup);\n    this.canvas.addEventListener(\"pointerdown\", this.#boundCanvasPointerdown);\n    if (this.#canvasContextMenuTimeoutId) {\n      clearTimeout(this.#canvasContextMenuTimeoutId);\n    }\n    this.#canvasContextMenuTimeoutId = setTimeout(() => {\n      this.#canvasContextMenuTimeoutId = null;\n      this.canvas.removeEventListener(\"contextmenu\", display_utils.noContextMenu);\n    }, 10);\n    this.#stopDrawing(event.offsetX, event.offsetY);\n    this.addToAnnotationStorage();\n    this.setInBackground();\n  }\n  #createCanvas() {\n    this.canvas = document.createElement(\"canvas\");\n    this.canvas.width = this.canvas.height = 0;\n    this.canvas.className = \"inkEditorCanvas\";\n    this.canvas.setAttribute(\"data-l10n-id\", \"pdfjs-ink-canvas\");\n    this.div.append(this.canvas);\n    this.ctx = this.canvas.getContext(\"2d\");\n  }\n  #createObserver() {\n    this.#observer = new ResizeObserver(entries => {\n      const rect = entries[0].contentRect;\n      if (rect.width && rect.height) {\n        this.setDimensions(rect.width, rect.height);\n      }\n    });\n    this.#observer.observe(this.div);\n  }\n  get isResizable() {\n    return !this.isEmpty() && this.#disableEditing;\n  }\n  render() {\n    if (this.div) {\n      return this.div;\n    }\n    let baseX, baseY;\n    if (this.width) {\n      baseX = this.x;\n      baseY = this.y;\n    }\n    super.render();\n    this.div.setAttribute(\"data-l10n-id\", \"pdfjs-ink\");\n    const [x, y, w, h] = this.#getInitialBBox();\n    this.setAt(x, y, 0, 0);\n    this.setDims(w, h);\n    this.#createCanvas();\n    if (this.width) {\n      const [parentWidth, parentHeight] = this.parentDimensions;\n      this.setAspectRatio(this.width * parentWidth, this.height * parentHeight);\n      this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight);\n      this.#isCanvasInitialized = true;\n      this.#setCanvasDims();\n      this.setDims(this.width * parentWidth, this.height * parentHeight);\n      this.#redraw();\n      this.div.classList.add(\"disabled\");\n    } else {\n      this.div.classList.add(\"editing\");\n      this.enableEditMode();\n    }\n    this.#createObserver();\n    return this.div;\n  }\n  #setCanvasDims() {\n    if (!this.#isCanvasInitialized) {\n      return;\n    }\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    this.canvas.width = Math.ceil(this.width * parentWidth);\n    this.canvas.height = Math.ceil(this.height * parentHeight);\n    this.#updateTransform();\n  }\n  setDimensions(width, height) {\n    const roundedWidth = Math.round(width);\n    const roundedHeight = Math.round(height);\n    if (this.#realWidth === roundedWidth && this.#realHeight === roundedHeight) {\n      return;\n    }\n    this.#realWidth = roundedWidth;\n    this.#realHeight = roundedHeight;\n    this.canvas.style.visibility = \"hidden\";\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    this.width = width / parentWidth;\n    this.height = height / parentHeight;\n    this.fixAndSetPosition();\n    if (this.#disableEditing) {\n      this.#setScaleFactor(width, height);\n    }\n    this.#setCanvasDims();\n    this.#redraw();\n    this.canvas.style.visibility = \"visible\";\n    this.fixDims();\n  }\n  #setScaleFactor(width, height) {\n    const padding = this.#getPadding();\n    const scaleFactorW = (width - padding) / this.#baseWidth;\n    const scaleFactorH = (height - padding) / this.#baseHeight;\n    this.scaleFactor = Math.min(scaleFactorW, scaleFactorH);\n  }\n  #updateTransform() {\n    const padding = this.#getPadding() / 2;\n    this.ctx.setTransform(this.scaleFactor, 0, 0, this.scaleFactor, this.translationX * this.scaleFactor + padding, this.translationY * this.scaleFactor + padding);\n  }\n  static #buildPath2D(bezier) {\n    const path2D = new Path2D();\n    for (let i = 0, ii = bezier.length; i < ii; i++) {\n      const [first, control1, control2, second] = bezier[i];\n      if (i === 0) {\n        path2D.moveTo(...first);\n      }\n      path2D.bezierCurveTo(control1[0], control1[1], control2[0], control2[1], second[0], second[1]);\n    }\n    return path2D;\n  }\n  static #toPDFCoordinates(points, rect, rotation) {\n    const [blX, blY, trX, trY] = rect;\n    switch (rotation) {\n      case 0:\n        for (let i = 0, ii = points.length; i < ii; i += 2) {\n          points[i] += blX;\n          points[i + 1] = trY - points[i + 1];\n        }\n        break;\n      case 90:\n        for (let i = 0, ii = points.length; i < ii; i += 2) {\n          const x = points[i];\n          points[i] = points[i + 1] + blX;\n          points[i + 1] = x + blY;\n        }\n        break;\n      case 180:\n        for (let i = 0, ii = points.length; i < ii; i += 2) {\n          points[i] = trX - points[i];\n          points[i + 1] += blY;\n        }\n        break;\n      case 270:\n        for (let i = 0, ii = points.length; i < ii; i += 2) {\n          const x = points[i];\n          points[i] = trX - points[i + 1];\n          points[i + 1] = trY - x;\n        }\n        break;\n      default:\n        throw new Error(\"Invalid rotation\");\n    }\n    return points;\n  }\n  static #fromPDFCoordinates(points, rect, rotation) {\n    const [blX, blY, trX, trY] = rect;\n    switch (rotation) {\n      case 0:\n        for (let i = 0, ii = points.length; i < ii; i += 2) {\n          points[i] -= blX;\n          points[i + 1] = trY - points[i + 1];\n        }\n        break;\n      case 90:\n        for (let i = 0, ii = points.length; i < ii; i += 2) {\n          const x = points[i];\n          points[i] = points[i + 1] - blY;\n          points[i + 1] = x - blX;\n        }\n        break;\n      case 180:\n        for (let i = 0, ii = points.length; i < ii; i += 2) {\n          points[i] = trX - points[i];\n          points[i + 1] -= blY;\n        }\n        break;\n      case 270:\n        for (let i = 0, ii = points.length; i < ii; i += 2) {\n          const x = points[i];\n          points[i] = trY - points[i + 1];\n          points[i + 1] = trX - x;\n        }\n        break;\n      default:\n        throw new Error(\"Invalid rotation\");\n    }\n    return points;\n  }\n  #serializePaths(s, tx, ty, rect) {\n    const paths = [];\n    const padding = this.thickness / 2;\n    const shiftX = s * tx + padding;\n    const shiftY = s * ty + padding;\n    for (const bezier of this.paths) {\n      const buffer = [];\n      const points = [];\n      for (let j = 0, jj = bezier.length; j < jj; j++) {\n        const [first, control1, control2, second] = bezier[j];\n        const p10 = s * first[0] + shiftX;\n        const p11 = s * first[1] + shiftY;\n        const p20 = s * control1[0] + shiftX;\n        const p21 = s * control1[1] + shiftY;\n        const p30 = s * control2[0] + shiftX;\n        const p31 = s * control2[1] + shiftY;\n        const p40 = s * second[0] + shiftX;\n        const p41 = s * second[1] + shiftY;\n        if (j === 0) {\n          buffer.push(p10, p11);\n          points.push(p10, p11);\n        }\n        buffer.push(p20, p21, p30, p31, p40, p41);\n        points.push(p20, p21);\n        if (j === jj - 1) {\n          points.push(p40, p41);\n        }\n      }\n      paths.push({\n        bezier: InkEditor.#toPDFCoordinates(buffer, rect, this.rotation),\n        points: InkEditor.#toPDFCoordinates(points, rect, this.rotation)\n      });\n    }\n    return paths;\n  }\n  #getBbox() {\n    let xMin = Infinity;\n    let xMax = -Infinity;\n    let yMin = Infinity;\n    let yMax = -Infinity;\n    for (const path of this.paths) {\n      for (const [first, control1, control2, second] of path) {\n        const bbox = util.Util.bezierBoundingBox(...first, ...control1, ...control2, ...second);\n        xMin = Math.min(xMin, bbox[0]);\n        yMin = Math.min(yMin, bbox[1]);\n        xMax = Math.max(xMax, bbox[2]);\n        yMax = Math.max(yMax, bbox[3]);\n      }\n    }\n    return [xMin, yMin, xMax, yMax];\n  }\n  #getPadding() {\n    return this.#disableEditing ? Math.ceil(this.thickness * this.parentScale) : 0;\n  }\n  #fitToContent(firstTime = false) {\n    if (this.isEmpty()) {\n      return;\n    }\n    if (!this.#disableEditing) {\n      this.#redraw();\n      return;\n    }\n    const bbox = this.#getBbox();\n    const padding = this.#getPadding();\n    this.#baseWidth = Math.max(editor_editor.AnnotationEditor.MIN_SIZE, bbox[2] - bbox[0]);\n    this.#baseHeight = Math.max(editor_editor.AnnotationEditor.MIN_SIZE, bbox[3] - bbox[1]);\n    const width = Math.ceil(padding + this.#baseWidth * this.scaleFactor);\n    const height = Math.ceil(padding + this.#baseHeight * this.scaleFactor);\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    this.width = width / parentWidth;\n    this.height = height / parentHeight;\n    this.setAspectRatio(width, height);\n    const prevTranslationX = this.translationX;\n    const prevTranslationY = this.translationY;\n    this.translationX = -bbox[0];\n    this.translationY = -bbox[1];\n    this.#setCanvasDims();\n    this.#redraw();\n    this.#realWidth = width;\n    this.#realHeight = height;\n    this.setDims(width, height);\n    const unscaledPadding = firstTime ? padding / this.scaleFactor / 2 : 0;\n    this.translate(prevTranslationX - this.translationX - unscaledPadding, prevTranslationY - this.translationY - unscaledPadding);\n  }\n  static deserialize(data, parent, uiManager) {\n    if (data instanceof annotation_layer.InkAnnotationElement) {\n      return null;\n    }\n    const editor = super.deserialize(data, parent, uiManager);\n    editor.thickness = data.thickness;\n    editor.color = util.Util.makeHexColor(...data.color);\n    editor.opacity = data.opacity;\n    const [pageWidth, pageHeight] = editor.pageDimensions;\n    const width = editor.width * pageWidth;\n    const height = editor.height * pageHeight;\n    const scaleFactor = editor.parentScale;\n    const padding = data.thickness / 2;\n    editor.#disableEditing = true;\n    editor.#realWidth = Math.round(width);\n    editor.#realHeight = Math.round(height);\n    const {\n      paths,\n      rect,\n      rotation\n    } = data;\n    for (let {\n      bezier\n    } of paths) {\n      bezier = InkEditor.#fromPDFCoordinates(bezier, rect, rotation);\n      const path = [];\n      editor.paths.push(path);\n      let p0 = scaleFactor * (bezier[0] - padding);\n      let p1 = scaleFactor * (bezier[1] - padding);\n      for (let i = 2, ii = bezier.length; i < ii; i += 6) {\n        const p10 = scaleFactor * (bezier[i] - padding);\n        const p11 = scaleFactor * (bezier[i + 1] - padding);\n        const p20 = scaleFactor * (bezier[i + 2] - padding);\n        const p21 = scaleFactor * (bezier[i + 3] - padding);\n        const p30 = scaleFactor * (bezier[i + 4] - padding);\n        const p31 = scaleFactor * (bezier[i + 5] - padding);\n        path.push([[p0, p1], [p10, p11], [p20, p21], [p30, p31]]);\n        p0 = p30;\n        p1 = p31;\n      }\n      const path2D = this.#buildPath2D(path);\n      editor.bezierPath2D.push(path2D);\n    }\n    const bbox = editor.#getBbox();\n    editor.#baseWidth = Math.max(editor_editor.AnnotationEditor.MIN_SIZE, bbox[2] - bbox[0]);\n    editor.#baseHeight = Math.max(editor_editor.AnnotationEditor.MIN_SIZE, bbox[3] - bbox[1]);\n    editor.#setScaleFactor(width, height);\n    return editor;\n  }\n  serialize() {\n    if (this.isEmpty()) {\n      return null;\n    }\n    const rect = this.getRect(0, 0);\n    const color = editor_editor.AnnotationEditor._colorManager.convert(this.ctx.strokeStyle);\n    return {\n      annotationType: util.AnnotationEditorType.INK,\n      color,\n      thickness: this.thickness,\n      opacity: this.opacity,\n      paths: this.#serializePaths(this.scaleFactor / this.parentScale, this.translationX, this.translationY, rect),\n      pageIndex: this.pageIndex,\n      rect,\n      rotation: this.rotation,\n      structTreeParentId: this._structTreeParentId\n    };\n  }\n}\n\n;// CONCATENATED MODULE: ./src/display/editor/stamp.js\n\n\n\n\nclass StampEditor extends editor_editor.AnnotationEditor {\n  #bitmap = null;\n  #bitmapId = null;\n  #bitmapPromise = null;\n  #bitmapUrl = null;\n  #bitmapFile = null;\n  #bitmapFileName = \"\";\n  #canvas = null;\n  #observer = null;\n  #resizeTimeoutId = null;\n  #isSvg = false;\n  #hasBeenAddedInUndoStack = false;\n  static _type = \"stamp\";\n  static _editorType = util.AnnotationEditorType.STAMP;\n  constructor(params) {\n    super({\n      ...params,\n      name: \"stampEditor\"\n    });\n    this.#bitmapUrl = params.bitmapUrl;\n    this.#bitmapFile = params.bitmapFile;\n  }\n  static initialize(l10n) {\n    editor_editor.AnnotationEditor.initialize(l10n);\n  }\n  static get supportedTypes() {\n    const types = [\"apng\", \"avif\", \"bmp\", \"gif\", \"jpeg\", \"png\", \"svg+xml\", \"webp\", \"x-icon\"];\n    return (0,util.shadow)(this, \"supportedTypes\", types.map(type => `image/${type}`));\n  }\n  static get supportedTypesStr() {\n    return (0,util.shadow)(this, \"supportedTypesStr\", this.supportedTypes.join(\",\"));\n  }\n  static isHandlingMimeForPasting(mime) {\n    return this.supportedTypes.includes(mime);\n  }\n  static paste(item, parent) {\n    parent.pasteEditor(util.AnnotationEditorType.STAMP, {\n      bitmapFile: item.getAsFile()\n    });\n  }\n  #getBitmapFetched(data, fromId = false) {\n    if (!data) {\n      this.remove();\n      return;\n    }\n    this.#bitmap = data.bitmap;\n    if (!fromId) {\n      this.#bitmapId = data.id;\n      this.#isSvg = data.isSvg;\n    }\n    if (data.file) {\n      this.#bitmapFileName = data.file.name;\n    }\n    this.#createCanvas();\n  }\n  #getBitmapDone() {\n    this.#bitmapPromise = null;\n    this._uiManager.enableWaiting(false);\n    if (this.#canvas) {\n      this.div.focus();\n    }\n  }\n  #getBitmap() {\n    if (this.#bitmapId) {\n      this._uiManager.enableWaiting(true);\n      this._uiManager.imageManager.getFromId(this.#bitmapId).then(data => this.#getBitmapFetched(data, true)).finally(() => this.#getBitmapDone());\n      return;\n    }\n    if (this.#bitmapUrl) {\n      const url = this.#bitmapUrl;\n      this.#bitmapUrl = null;\n      this._uiManager.enableWaiting(true);\n      this.#bitmapPromise = this._uiManager.imageManager.getFromUrl(url).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone());\n      return;\n    }\n    if (this.#bitmapFile) {\n      const file = this.#bitmapFile;\n      this.#bitmapFile = null;\n      this._uiManager.enableWaiting(true);\n      this.#bitmapPromise = this._uiManager.imageManager.getFromFile(file).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone());\n      return;\n    }\n    const input = document.createElement(\"input\");\n    input.type = \"file\";\n    input.accept = StampEditor.supportedTypesStr;\n    this.#bitmapPromise = new Promise(resolve => {\n      input.addEventListener(\"change\", async () => {\n        if (!input.files || input.files.length === 0) {\n          this.remove();\n        } else {\n          this._uiManager.enableWaiting(true);\n          const data = await this._uiManager.imageManager.getFromFile(input.files[0]);\n          this.#getBitmapFetched(data);\n        }\n        resolve();\n      });\n      input.addEventListener(\"cancel\", () => {\n        this.remove();\n        resolve();\n      });\n    }).finally(() => this.#getBitmapDone());\n    input.click();\n  }\n  remove() {\n    if (this.#bitmapId) {\n      this.#bitmap = null;\n      this._uiManager.imageManager.deleteId(this.#bitmapId);\n      this.#canvas?.remove();\n      this.#canvas = null;\n      this.#observer?.disconnect();\n      this.#observer = null;\n      if (this.#resizeTimeoutId) {\n        clearTimeout(this.#resizeTimeoutId);\n        this.#resizeTimeoutId = null;\n      }\n    }\n    super.remove();\n  }\n  rebuild() {\n    if (!this.parent) {\n      if (this.#bitmapId) {\n        this.#getBitmap();\n      }\n      return;\n    }\n    super.rebuild();\n    if (this.div === null) {\n      return;\n    }\n    if (this.#bitmapId) {\n      this.#getBitmap();\n    }\n    if (!this.isAttachedToDOM) {\n      this.parent.add(this);\n    }\n  }\n  onceAdded() {\n    this._isDraggable = true;\n    this.div.focus();\n  }\n  isEmpty() {\n    return !(this.#bitmapPromise || this.#bitmap || this.#bitmapUrl || this.#bitmapFile);\n  }\n  get isResizable() {\n    return true;\n  }\n  render() {\n    if (this.div) {\n      return this.div;\n    }\n    let baseX, baseY;\n    if (this.width) {\n      baseX = this.x;\n      baseY = this.y;\n    }\n    super.render();\n    this.div.hidden = true;\n    if (this.#bitmap) {\n      this.#createCanvas();\n    } else {\n      this.#getBitmap();\n    }\n    if (this.width) {\n      const [parentWidth, parentHeight] = this.parentDimensions;\n      this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight);\n    }\n    return this.div;\n  }\n  #createCanvas() {\n    const {\n      div\n    } = this;\n    let {\n      width,\n      height\n    } = this.#bitmap;\n    const [pageWidth, pageHeight] = this.pageDimensions;\n    const MAX_RATIO = 0.75;\n    if (this.width) {\n      width = this.width * pageWidth;\n      height = this.height * pageHeight;\n    } else if (width > MAX_RATIO * pageWidth || height > MAX_RATIO * pageHeight) {\n      const factor = Math.min(MAX_RATIO * pageWidth / width, MAX_RATIO * pageHeight / height);\n      width *= factor;\n      height *= factor;\n    }\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    this.setDims(width * parentWidth / pageWidth, height * parentHeight / pageHeight);\n    this._uiManager.enableWaiting(false);\n    const canvas = this.#canvas = document.createElement(\"canvas\");\n    div.append(canvas);\n    div.hidden = false;\n    this.#drawBitmap(width, height);\n    this.#createObserver();\n    if (!this.#hasBeenAddedInUndoStack) {\n      this.parent.addUndoableEditor(this);\n      this.#hasBeenAddedInUndoStack = true;\n    }\n    this._uiManager._eventBus.dispatch(\"reporttelemetry\", {\n      source: this,\n      details: {\n        type: \"editing\",\n        subtype: this.editorType,\n        data: {\n          action: \"inserted_image\"\n        }\n      }\n    });\n    this.addAltTextButton();\n    if (this.#bitmapFileName) {\n      canvas.setAttribute(\"aria-label\", this.#bitmapFileName);\n    }\n  }\n  #setDimensions(width, height) {\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    this.width = width / parentWidth;\n    this.height = height / parentHeight;\n    this.setDims(width, height);\n    if (this._initialOptions?.isCentered) {\n      this.center();\n    } else {\n      this.fixAndSetPosition();\n    }\n    this._initialOptions = null;\n    if (this.#resizeTimeoutId !== null) {\n      clearTimeout(this.#resizeTimeoutId);\n    }\n    const TIME_TO_WAIT = 200;\n    this.#resizeTimeoutId = setTimeout(() => {\n      this.#resizeTimeoutId = null;\n      this.#drawBitmap(width, height);\n    }, TIME_TO_WAIT);\n  }\n  #scaleBitmap(width, height) {\n    const {\n      width: bitmapWidth,\n      height: bitmapHeight\n    } = this.#bitmap;\n    let newWidth = bitmapWidth;\n    let newHeight = bitmapHeight;\n    let bitmap = this.#bitmap;\n    while (newWidth > 2 * width || newHeight > 2 * height) {\n      const prevWidth = newWidth;\n      const prevHeight = newHeight;\n      if (newWidth > 2 * width) {\n        newWidth = newWidth >= 16384 ? Math.floor(newWidth / 2) - 1 : Math.ceil(newWidth / 2);\n      }\n      if (newHeight > 2 * height) {\n        newHeight = newHeight >= 16384 ? Math.floor(newHeight / 2) - 1 : Math.ceil(newHeight / 2);\n      }\n      const offscreen = new OffscreenCanvas(newWidth, newHeight);\n      const ctx = offscreen.getContext(\"2d\");\n      ctx.drawImage(bitmap, 0, 0, prevWidth, prevHeight, 0, 0, newWidth, newHeight);\n      bitmap = offscreen.transferToImageBitmap();\n    }\n    return bitmap;\n  }\n  #drawBitmap(width, height) {\n    width = Math.ceil(width);\n    height = Math.ceil(height);\n    const canvas = this.#canvas;\n    if (!canvas || canvas.width === width && canvas.height === height) {\n      return;\n    }\n    canvas.width = width;\n    canvas.height = height;\n    const bitmap = this.#isSvg ? this.#bitmap : this.#scaleBitmap(width, height);\n    const ctx = canvas.getContext(\"2d\");\n    ctx.filter = this._uiManager.hcmFilter;\n    ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, width, height);\n  }\n  getImageForAltText() {\n    return this.#canvas;\n  }\n  #serializeBitmap(toUrl) {\n    if (toUrl) {\n      if (this.#isSvg) {\n        const url = this._uiManager.imageManager.getSvgUrl(this.#bitmapId);\n        if (url) {\n          return url;\n        }\n      }\n      const canvas = document.createElement(\"canvas\");\n      ({\n        width: canvas.width,\n        height: canvas.height\n      } = this.#bitmap);\n      const ctx = canvas.getContext(\"2d\");\n      ctx.drawImage(this.#bitmap, 0, 0);\n      return canvas.toDataURL();\n    }\n    if (this.#isSvg) {\n      const [pageWidth, pageHeight] = this.pageDimensions;\n      const width = Math.round(this.width * pageWidth * display_utils.PixelsPerInch.PDF_TO_CSS_UNITS);\n      const height = Math.round(this.height * pageHeight * display_utils.PixelsPerInch.PDF_TO_CSS_UNITS);\n      const offscreen = new OffscreenCanvas(width, height);\n      const ctx = offscreen.getContext(\"2d\");\n      ctx.drawImage(this.#bitmap, 0, 0, this.#bitmap.width, this.#bitmap.height, 0, 0, width, height);\n      return offscreen.transferToImageBitmap();\n    }\n    return structuredClone(this.#bitmap);\n  }\n  #createObserver() {\n    this.#observer = new ResizeObserver(entries => {\n      const rect = entries[0].contentRect;\n      if (rect.width && rect.height) {\n        this.#setDimensions(rect.width, rect.height);\n      }\n    });\n    this.#observer.observe(this.div);\n  }\n  static deserialize(data, parent, uiManager) {\n    if (data instanceof annotation_layer.StampAnnotationElement) {\n      return null;\n    }\n    const editor = super.deserialize(data, parent, uiManager);\n    const {\n      rect,\n      bitmapUrl,\n      bitmapId,\n      isSvg,\n      accessibilityData\n    } = data;\n    if (bitmapId && uiManager.imageManager.isValidId(bitmapId)) {\n      editor.#bitmapId = bitmapId;\n    } else {\n      editor.#bitmapUrl = bitmapUrl;\n    }\n    editor.#isSvg = isSvg;\n    const [parentWidth, parentHeight] = editor.pageDimensions;\n    editor.width = (rect[2] - rect[0]) / parentWidth;\n    editor.height = (rect[3] - rect[1]) / parentHeight;\n    if (accessibilityData) {\n      editor.altTextData = accessibilityData;\n    }\n    return editor;\n  }\n  serialize(isForCopying = false, context = null) {\n    if (this.isEmpty()) {\n      return null;\n    }\n    const serialized = {\n      annotationType: util.AnnotationEditorType.STAMP,\n      bitmapId: this.#bitmapId,\n      pageIndex: this.pageIndex,\n      rect: this.getRect(0, 0),\n      rotation: this.rotation,\n      isSvg: this.#isSvg,\n      structTreeParentId: this._structTreeParentId\n    };\n    if (isForCopying) {\n      serialized.bitmapUrl = this.#serializeBitmap(true);\n      serialized.accessibilityData = this.altTextData;\n      return serialized;\n    }\n    const {\n      decorative,\n      altText\n    } = this.altTextData;\n    if (!decorative && altText) {\n      serialized.accessibilityData = {\n        type: \"Figure\",\n        alt: altText\n      };\n    }\n    if (context === null) {\n      return serialized;\n    }\n    context.stamps ||= new Map();\n    const area = this.#isSvg ? (serialized.rect[2] - serialized.rect[0]) * (serialized.rect[3] - serialized.rect[1]) : null;\n    if (!context.stamps.has(this.#bitmapId)) {\n      context.stamps.set(this.#bitmapId, {\n        area,\n        serialized\n      });\n      serialized.bitmap = this.#serializeBitmap(false);\n    } else if (this.#isSvg) {\n      const prevData = context.stamps.get(this.#bitmapId);\n      if (area > prevData.area) {\n        prevData.area = area;\n        prevData.serialized.bitmap.close();\n        prevData.serialized.bitmap = this.#serializeBitmap(false);\n      }\n    }\n    return serialized;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/display/editor/annotation_editor_layer.js\n\n\n\n\n\n\nclass AnnotationEditorLayer {\n  #accessibilityManager;\n  #allowClick = false;\n  #annotationLayer = null;\n  #boundPointerup = this.pointerup.bind(this);\n  #boundPointerdown = this.pointerdown.bind(this);\n  #editorFocusTimeoutId = null;\n  #editors = new Map();\n  #hadPointerDown = false;\n  #isCleaningUp = false;\n  #isDisabling = false;\n  #uiManager;\n  static _initialized = false;\n  static #editorTypes = new Map([FreeTextEditor, InkEditor, StampEditor].map(type => [type._editorType, type]));\n  constructor({\n    uiManager,\n    pageIndex,\n    div,\n    accessibilityManager,\n    annotationLayer,\n    viewport,\n    l10n\n  }) {\n    const editorTypes = [...AnnotationEditorLayer.#editorTypes.values()];\n    if (!AnnotationEditorLayer._initialized) {\n      AnnotationEditorLayer._initialized = true;\n      for (const editorType of editorTypes) {\n        editorType.initialize(l10n);\n      }\n    }\n    uiManager.registerEditorTypes(editorTypes);\n    this.#uiManager = uiManager;\n    this.pageIndex = pageIndex;\n    this.div = div;\n    this.#accessibilityManager = accessibilityManager;\n    this.#annotationLayer = annotationLayer;\n    this.viewport = viewport;\n    this.#uiManager.addLayer(this);\n  }\n  get isEmpty() {\n    return this.#editors.size === 0;\n  }\n  updateToolbar(mode) {\n    this.#uiManager.updateToolbar(mode);\n  }\n  updateMode(mode = this.#uiManager.getMode()) {\n    this.#cleanup();\n    if (mode === util.AnnotationEditorType.INK) {\n      this.addInkEditorIfNeeded(false);\n      this.disableClick();\n    } else {\n      this.enableClick();\n    }\n    if (mode !== util.AnnotationEditorType.NONE) {\n      const {\n        classList\n      } = this.div;\n      for (const editorType of AnnotationEditorLayer.#editorTypes.values()) {\n        classList.toggle(`${editorType._type}Editing`, mode === editorType._editorType);\n      }\n      this.div.hidden = false;\n    }\n  }\n  addInkEditorIfNeeded(isCommitting) {\n    if (!isCommitting && this.#uiManager.getMode() !== util.AnnotationEditorType.INK) {\n      return;\n    }\n    if (!isCommitting) {\n      for (const editor of this.#editors.values()) {\n        if (editor.isEmpty()) {\n          editor.setInBackground();\n          return;\n        }\n      }\n    }\n    const editor = this.#createAndAddNewEditor({\n      offsetX: 0,\n      offsetY: 0\n    }, false);\n    editor.setInBackground();\n  }\n  setEditingState(isEditing) {\n    this.#uiManager.setEditingState(isEditing);\n  }\n  addCommands(params) {\n    this.#uiManager.addCommands(params);\n  }\n  togglePointerEvents(enabled = false) {\n    this.div.classList.toggle(\"disabled\", !enabled);\n  }\n  enable() {\n    this.togglePointerEvents(true);\n    const annotationElementIds = new Set();\n    for (const editor of this.#editors.values()) {\n      editor.enableEditing();\n      if (editor.annotationElementId) {\n        annotationElementIds.add(editor.annotationElementId);\n      }\n    }\n    if (!this.#annotationLayer) {\n      return;\n    }\n    const editables = this.#annotationLayer.getEditableAnnotations();\n    for (const editable of editables) {\n      editable.hide();\n      if (this.#uiManager.isDeletedAnnotationElement(editable.data.id)) {\n        continue;\n      }\n      if (annotationElementIds.has(editable.data.id)) {\n        continue;\n      }\n      const editor = this.deserialize(editable);\n      if (!editor) {\n        continue;\n      }\n      this.addOrRebuild(editor);\n      editor.enableEditing();\n    }\n  }\n  disable() {\n    this.#isDisabling = true;\n    this.togglePointerEvents(false);\n    const hiddenAnnotationIds = new Set();\n    for (const editor of this.#editors.values()) {\n      editor.disableEditing();\n      if (!editor.annotationElementId || editor.serialize() !== null) {\n        hiddenAnnotationIds.add(editor.annotationElementId);\n        continue;\n      }\n      this.getEditableAnnotation(editor.annotationElementId)?.show();\n      editor.remove();\n    }\n    if (this.#annotationLayer) {\n      const editables = this.#annotationLayer.getEditableAnnotations();\n      for (const editable of editables) {\n        const {\n          id\n        } = editable.data;\n        if (hiddenAnnotationIds.has(id) || this.#uiManager.isDeletedAnnotationElement(id)) {\n          continue;\n        }\n        editable.show();\n      }\n    }\n    this.#cleanup();\n    if (this.isEmpty) {\n      this.div.hidden = true;\n    }\n    const {\n      classList\n    } = this.div;\n    for (const editorType of AnnotationEditorLayer.#editorTypes.values()) {\n      classList.remove(`${editorType._type}Editing`);\n    }\n    this.#isDisabling = false;\n  }\n  getEditableAnnotation(id) {\n    return this.#annotationLayer?.getEditableAnnotation(id) || null;\n  }\n  setActiveEditor(editor) {\n    const currentActive = this.#uiManager.getActive();\n    if (currentActive === editor) {\n      return;\n    }\n    this.#uiManager.setActiveEditor(editor);\n  }\n  enableClick() {\n    this.div.addEventListener(\"pointerdown\", this.#boundPointerdown);\n    this.div.addEventListener(\"pointerup\", this.#boundPointerup);\n  }\n  disableClick() {\n    this.div.removeEventListener(\"pointerdown\", this.#boundPointerdown);\n    this.div.removeEventListener(\"pointerup\", this.#boundPointerup);\n  }\n  attach(editor) {\n    this.#editors.set(editor.id, editor);\n    const {\n      annotationElementId\n    } = editor;\n    if (annotationElementId && this.#uiManager.isDeletedAnnotationElement(annotationElementId)) {\n      this.#uiManager.removeDeletedAnnotationElement(editor);\n    }\n  }\n  detach(editor) {\n    this.#editors.delete(editor.id);\n    this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv);\n    if (!this.#isDisabling && editor.annotationElementId) {\n      this.#uiManager.addDeletedAnnotationElement(editor);\n    }\n  }\n  remove(editor) {\n    this.detach(editor);\n    this.#uiManager.removeEditor(editor);\n    editor.div.remove();\n    editor.isAttachedToDOM = false;\n    if (!this.#isCleaningUp) {\n      this.addInkEditorIfNeeded(false);\n    }\n  }\n  changeParent(editor) {\n    if (editor.parent === this) {\n      return;\n    }\n    if (editor.annotationElementId) {\n      this.#uiManager.addDeletedAnnotationElement(editor.annotationElementId);\n      editor_editor.AnnotationEditor.deleteAnnotationElement(editor);\n      editor.annotationElementId = null;\n    }\n    this.attach(editor);\n    editor.parent?.detach(editor);\n    editor.setParent(this);\n    if (editor.div && editor.isAttachedToDOM) {\n      editor.div.remove();\n      this.div.append(editor.div);\n    }\n  }\n  add(editor) {\n    this.changeParent(editor);\n    this.#uiManager.addEditor(editor);\n    this.attach(editor);\n    if (!editor.isAttachedToDOM) {\n      const div = editor.render();\n      this.div.append(div);\n      editor.isAttachedToDOM = true;\n    }\n    editor.fixAndSetPosition();\n    editor.onceAdded();\n    this.#uiManager.addToAnnotationStorage(editor);\n  }\n  moveEditorInDOM(editor) {\n    if (!editor.isAttachedToDOM) {\n      return;\n    }\n    const {\n      activeElement\n    } = document;\n    if (editor.div.contains(activeElement) && !this.#editorFocusTimeoutId) {\n      editor._focusEventsAllowed = false;\n      this.#editorFocusTimeoutId = setTimeout(() => {\n        this.#editorFocusTimeoutId = null;\n        if (!editor.div.contains(document.activeElement)) {\n          editor.div.addEventListener(\"focusin\", () => {\n            editor._focusEventsAllowed = true;\n          }, {\n            once: true\n          });\n          activeElement.focus();\n        } else {\n          editor._focusEventsAllowed = true;\n        }\n      }, 0);\n    }\n    editor._structTreeParentId = this.#accessibilityManager?.moveElementInDOM(this.div, editor.div, editor.contentDiv, true);\n  }\n  addOrRebuild(editor) {\n    if (editor.needsToBeRebuilt()) {\n      editor.parent ||= this;\n      editor.rebuild();\n    } else {\n      this.add(editor);\n    }\n  }\n  addUndoableEditor(editor) {\n    const cmd = () => editor._uiManager.rebuild(editor);\n    const undo = () => {\n      editor.remove();\n    };\n    this.addCommands({\n      cmd,\n      undo,\n      mustExec: false\n    });\n  }\n  getNextId() {\n    return this.#uiManager.getId();\n  }\n  #createNewEditor(params) {\n    const editorType = AnnotationEditorLayer.#editorTypes.get(this.#uiManager.getMode());\n    return editorType ? new editorType.prototype.constructor(params) : null;\n  }\n  pasteEditor(mode, params) {\n    this.#uiManager.updateToolbar(mode);\n    this.#uiManager.updateMode(mode);\n    const {\n      offsetX,\n      offsetY\n    } = this.#getCenterPoint();\n    const id = this.getNextId();\n    const editor = this.#createNewEditor({\n      parent: this,\n      id,\n      x: offsetX,\n      y: offsetY,\n      uiManager: this.#uiManager,\n      isCentered: true,\n      ...params\n    });\n    if (editor) {\n      this.add(editor);\n    }\n  }\n  deserialize(data) {\n    return AnnotationEditorLayer.#editorTypes.get(data.annotationType ?? data.annotationEditorType)?.deserialize(data, this, this.#uiManager) || null;\n  }\n  #createAndAddNewEditor(event, isCentered) {\n    const id = this.getNextId();\n    const editor = this.#createNewEditor({\n      parent: this,\n      id,\n      x: event.offsetX,\n      y: event.offsetY,\n      uiManager: this.#uiManager,\n      isCentered\n    });\n    if (editor) {\n      this.add(editor);\n    }\n    return editor;\n  }\n  #getCenterPoint() {\n    const {\n      x,\n      y,\n      width,\n      height\n    } = this.div.getBoundingClientRect();\n    const tlX = Math.max(0, x);\n    const tlY = Math.max(0, y);\n    const brX = Math.min(window.innerWidth, x + width);\n    const brY = Math.min(window.innerHeight, y + height);\n    const centerX = (tlX + brX) / 2 - x;\n    const centerY = (tlY + brY) / 2 - y;\n    const [offsetX, offsetY] = this.viewport.rotation % 180 === 0 ? [centerX, centerY] : [centerY, centerX];\n    return {\n      offsetX,\n      offsetY\n    };\n  }\n  addNewEditor() {\n    this.#createAndAddNewEditor(this.#getCenterPoint(), true);\n  }\n  setSelected(editor) {\n    this.#uiManager.setSelected(editor);\n  }\n  toggleSelected(editor) {\n    this.#uiManager.toggleSelected(editor);\n  }\n  isSelected(editor) {\n    return this.#uiManager.isSelected(editor);\n  }\n  unselect(editor) {\n    this.#uiManager.unselect(editor);\n  }\n  pointerup(event) {\n    const {\n      isMac\n    } = util.FeatureTest.platform;\n    if (event.button !== 0 || event.ctrlKey && isMac) {\n      return;\n    }\n    if (event.target !== this.div) {\n      return;\n    }\n    if (!this.#hadPointerDown) {\n      return;\n    }\n    this.#hadPointerDown = false;\n    if (!this.#allowClick) {\n      this.#allowClick = true;\n      return;\n    }\n    if (this.#uiManager.getMode() === util.AnnotationEditorType.STAMP) {\n      this.#uiManager.unselectAll();\n      return;\n    }\n    this.#createAndAddNewEditor(event, false);\n  }\n  pointerdown(event) {\n    if (this.#hadPointerDown) {\n      this.#hadPointerDown = false;\n      return;\n    }\n    const {\n      isMac\n    } = util.FeatureTest.platform;\n    if (event.button !== 0 || event.ctrlKey && isMac) {\n      return;\n    }\n    if (event.target !== this.div) {\n      return;\n    }\n    this.#hadPointerDown = true;\n    const editor = this.#uiManager.getActive();\n    this.#allowClick = !editor || editor.isEmpty();\n  }\n  findNewParent(editor, x, y) {\n    const layer = this.#uiManager.findParent(x, y);\n    if (layer === null || layer === this) {\n      return false;\n    }\n    layer.changeParent(editor);\n    return true;\n  }\n  destroy() {\n    if (this.#uiManager.getActive()?.parent === this) {\n      this.#uiManager.commitOrRemove();\n      this.#uiManager.setActiveEditor(null);\n    }\n    if (this.#editorFocusTimeoutId) {\n      clearTimeout(this.#editorFocusTimeoutId);\n      this.#editorFocusTimeoutId = null;\n    }\n    for (const editor of this.#editors.values()) {\n      this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv);\n      editor.setParent(null);\n      editor.isAttachedToDOM = false;\n      editor.div.remove();\n    }\n    this.div = null;\n    this.#editors.clear();\n    this.#uiManager.removeLayer(this);\n  }\n  #cleanup() {\n    this.#isCleaningUp = true;\n    for (const editor of this.#editors.values()) {\n      if (editor.isEmpty()) {\n        editor.remove();\n      }\n    }\n    this.#isCleaningUp = false;\n  }\n  render({\n    viewport\n  }) {\n    this.viewport = viewport;\n    (0,display_utils.setLayerDimensions)(this.div, viewport);\n    for (const editor of this.#uiManager.getEditors(this.pageIndex)) {\n      this.add(editor);\n    }\n    this.updateMode();\n  }\n  update({\n    viewport\n  }) {\n    this.#uiManager.commitOrRemove();\n    this.viewport = viewport;\n    (0,display_utils.setLayerDimensions)(this.div, {\n      rotation: viewport.rotation\n    });\n    this.updateMode();\n  }\n  get pageDimensions() {\n    const {\n      pageWidth,\n      pageHeight\n    } = this.viewport.rawDims;\n    return [pageWidth, pageHeight];\n  }\n}\n\n\n/***/ }),\n\n/***/ 796:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n  AnnotationEditor: () => (/* binding */ AnnotationEditor)\n});\n\n// EXTERNAL MODULE: ./src/display/editor/tools.js\nvar tools = __webpack_require__(812);\n// EXTERNAL MODULE: ./src/shared/util.js\nvar util = __webpack_require__(266);\n// EXTERNAL MODULE: ./src/display/display_utils.js\nvar display_utils = __webpack_require__(473);\n;// CONCATENATED MODULE: ./src/display/editor/toolbar.js\n\nclass EditorToolbar {\n  #toolbar = null;\n  #editor;\n  #buttons = null;\n  constructor(editor) {\n    this.#editor = editor;\n  }\n  render() {\n    const editToolbar = this.#toolbar = document.createElement(\"div\");\n    editToolbar.className = \"editToolbar\";\n    editToolbar.addEventListener(\"contextmenu\", display_utils.noContextMenu);\n    editToolbar.addEventListener(\"pointerdown\", EditorToolbar.#pointerDown);\n    const buttons = this.#buttons = document.createElement(\"div\");\n    buttons.className = \"buttons\";\n    editToolbar.append(buttons);\n    this.#addDeleteButton();\n    return editToolbar;\n  }\n  static #pointerDown(e) {\n    e.stopPropagation();\n  }\n  #focusIn(e) {\n    this.#editor._focusEventsAllowed = false;\n    e.preventDefault();\n    e.stopPropagation();\n  }\n  #focusOut(e) {\n    this.#editor._focusEventsAllowed = true;\n    e.preventDefault();\n    e.stopPropagation();\n  }\n  #addListenersToElement(element) {\n    element.addEventListener(\"focusin\", this.#focusIn.bind(this), {\n      capture: true\n    });\n    element.addEventListener(\"focusout\", this.#focusOut.bind(this), {\n      capture: true\n    });\n    element.addEventListener(\"contextmenu\", display_utils.noContextMenu);\n  }\n  hide() {\n    this.#toolbar.classList.add(\"hidden\");\n  }\n  show() {\n    this.#toolbar.classList.remove(\"hidden\");\n  }\n  #addDeleteButton() {\n    const button = document.createElement(\"button\");\n    button.className = \"delete\";\n    button.tabIndex = 0;\n    button.setAttribute(\"data-l10n-id\", \"pdfjs-editor-remove-button\");\n    this.#addListenersToElement(button);\n    button.addEventListener(\"click\", e => {\n      this.#editor._uiManager.delete();\n    });\n    this.#buttons.append(button);\n  }\n  remove() {\n    this.#toolbar.remove();\n  }\n}\n\n;// CONCATENATED MODULE: ./src/display/editor/editor.js\n\n\n\n\nclass AnnotationEditor {\n  #allResizerDivs = null;\n  #altText = \"\";\n  #altTextDecorative = false;\n  #altTextButton = null;\n  #altTextTooltip = null;\n  #altTextTooltipTimeout = null;\n  #altTextWasFromKeyBoard = false;\n  #keepAspectRatio = false;\n  #resizersDiv = null;\n  #savedDimensions = null;\n  #boundFocusin = this.focusin.bind(this);\n  #boundFocusout = this.focusout.bind(this);\n  #editToolbar = null;\n  #focusedResizerName = \"\";\n  #hasBeenClicked = false;\n  #isEditing = false;\n  #isInEditMode = false;\n  #isResizerEnabledForKeyboard = false;\n  #moveInDOMTimeout = null;\n  _initialOptions = Object.create(null);\n  _uiManager = null;\n  _focusEventsAllowed = true;\n  _l10nPromise = null;\n  #isDraggable = false;\n  #zIndex = AnnotationEditor._zIndex++;\n  static _borderLineWidth = -1;\n  static _colorManager = new tools.ColorManager();\n  static _zIndex = 1;\n  static SMALL_EDITOR_SIZE = 0;\n  static get _resizerKeyboardManager() {\n    const resize = AnnotationEditor.prototype._resizeWithKeyboard;\n    const small = tools.AnnotationEditorUIManager.TRANSLATE_SMALL;\n    const big = tools.AnnotationEditorUIManager.TRANSLATE_BIG;\n    return (0,util.shadow)(this, \"_resizerKeyboardManager\", new tools.KeyboardManager([[[\"ArrowLeft\", \"mac+ArrowLeft\"], resize, {\n      args: [-small, 0]\n    }], [[\"ctrl+ArrowLeft\", \"mac+shift+ArrowLeft\"], resize, {\n      args: [-big, 0]\n    }], [[\"ArrowRight\", \"mac+ArrowRight\"], resize, {\n      args: [small, 0]\n    }], [[\"ctrl+ArrowRight\", \"mac+shift+ArrowRight\"], resize, {\n      args: [big, 0]\n    }], [[\"ArrowUp\", \"mac+ArrowUp\"], resize, {\n      args: [0, -small]\n    }], [[\"ctrl+ArrowUp\", \"mac+shift+ArrowUp\"], resize, {\n      args: [0, -big]\n    }], [[\"ArrowDown\", \"mac+ArrowDown\"], resize, {\n      args: [0, small]\n    }], [[\"ctrl+ArrowDown\", \"mac+shift+ArrowDown\"], resize, {\n      args: [0, big]\n    }], [[\"Escape\", \"mac+Escape\"], AnnotationEditor.prototype._stopResizingWithKeyboard]]));\n  }\n  constructor(parameters) {\n    if (this.constructor === AnnotationEditor) {\n      (0,util.unreachable)(\"Cannot initialize AnnotationEditor.\");\n    }\n    this.parent = parameters.parent;\n    this.id = parameters.id;\n    this.width = this.height = null;\n    this.pageIndex = parameters.parent.pageIndex;\n    this.name = parameters.name;\n    this.div = null;\n    this._uiManager = parameters.uiManager;\n    this.annotationElementId = null;\n    this._willKeepAspectRatio = false;\n    this._initialOptions.isCentered = parameters.isCentered;\n    this._structTreeParentId = null;\n    const {\n      rotation,\n      rawDims: {\n        pageWidth,\n        pageHeight,\n        pageX,\n        pageY\n      }\n    } = this.parent.viewport;\n    this.rotation = rotation;\n    this.pageRotation = (360 + rotation - this._uiManager.viewParameters.rotation) % 360;\n    this.pageDimensions = [pageWidth, pageHeight];\n    this.pageTranslation = [pageX, pageY];\n    const [width, height] = this.parentDimensions;\n    this.x = parameters.x / width;\n    this.y = parameters.y / height;\n    this.isAttachedToDOM = false;\n    this.deleted = false;\n  }\n  get editorType() {\n    return Object.getPrototypeOf(this).constructor._type;\n  }\n  static get _defaultLineColor() {\n    return (0,util.shadow)(this, \"_defaultLineColor\", this._colorManager.getHexCode(\"CanvasText\"));\n  }\n  static deleteAnnotationElement(editor) {\n    const fakeEditor = new FakeEditor({\n      id: editor.parent.getNextId(),\n      parent: editor.parent,\n      uiManager: editor._uiManager\n    });\n    fakeEditor.annotationElementId = editor.annotationElementId;\n    fakeEditor.deleted = true;\n    fakeEditor._uiManager.addToAnnotationStorage(fakeEditor);\n  }\n  static initialize(l10n, options = null) {\n    AnnotationEditor._l10nPromise ||= new Map([\"pdfjs-editor-alt-text-button-label\", \"pdfjs-editor-alt-text-edit-button-label\", \"pdfjs-editor-alt-text-decorative-tooltip\", \"pdfjs-editor-resizer-label-topLeft\", \"pdfjs-editor-resizer-label-topMiddle\", \"pdfjs-editor-resizer-label-topRight\", \"pdfjs-editor-resizer-label-middleRight\", \"pdfjs-editor-resizer-label-bottomRight\", \"pdfjs-editor-resizer-label-bottomMiddle\", \"pdfjs-editor-resizer-label-bottomLeft\", \"pdfjs-editor-resizer-label-middleLeft\"].map(str => [str, l10n.get(str.replaceAll(/([A-Z])/g, c => `-${c.toLowerCase()}`))]));\n    if (options?.strings) {\n      for (const str of options.strings) {\n        AnnotationEditor._l10nPromise.set(str, l10n.get(str));\n      }\n    }\n    if (AnnotationEditor._borderLineWidth !== -1) {\n      return;\n    }\n    const style = getComputedStyle(document.documentElement);\n    AnnotationEditor._borderLineWidth = parseFloat(style.getPropertyValue(\"--outline-width\")) || 0;\n  }\n  static updateDefaultParams(_type, _value) {}\n  static get defaultPropertiesToUpdate() {\n    return [];\n  }\n  static isHandlingMimeForPasting(mime) {\n    return false;\n  }\n  static paste(item, parent) {\n    (0,util.unreachable)(\"Not implemented\");\n  }\n  get propertiesToUpdate() {\n    return [];\n  }\n  get _isDraggable() {\n    return this.#isDraggable;\n  }\n  set _isDraggable(value) {\n    this.#isDraggable = value;\n    this.div?.classList.toggle(\"draggable\", value);\n  }\n  get isEnterHandled() {\n    return true;\n  }\n  center() {\n    const [pageWidth, pageHeight] = this.pageDimensions;\n    switch (this.parentRotation) {\n      case 90:\n        this.x -= this.height * pageHeight / (pageWidth * 2);\n        this.y += this.width * pageWidth / (pageHeight * 2);\n        break;\n      case 180:\n        this.x += this.width / 2;\n        this.y += this.height / 2;\n        break;\n      case 270:\n        this.x += this.height * pageHeight / (pageWidth * 2);\n        this.y -= this.width * pageWidth / (pageHeight * 2);\n        break;\n      default:\n        this.x -= this.width / 2;\n        this.y -= this.height / 2;\n        break;\n    }\n    this.fixAndSetPosition();\n  }\n  addCommands(params) {\n    this._uiManager.addCommands(params);\n  }\n  get currentLayer() {\n    return this._uiManager.currentLayer;\n  }\n  setInBackground() {\n    this.div.style.zIndex = 0;\n  }\n  setInForeground() {\n    this.div.style.zIndex = this.#zIndex;\n  }\n  setParent(parent) {\n    if (parent !== null) {\n      this.pageIndex = parent.pageIndex;\n      this.pageDimensions = parent.pageDimensions;\n    } else {\n      this.#stopResizing();\n    }\n    this.parent = parent;\n  }\n  focusin(event) {\n    if (!this._focusEventsAllowed) {\n      return;\n    }\n    if (!this.#hasBeenClicked) {\n      this.parent.setSelected(this);\n    } else {\n      this.#hasBeenClicked = false;\n    }\n  }\n  focusout(event) {\n    if (!this._focusEventsAllowed) {\n      return;\n    }\n    if (!this.isAttachedToDOM) {\n      return;\n    }\n    const target = event.relatedTarget;\n    if (target?.closest(`#${this.id}`)) {\n      return;\n    }\n    event.preventDefault();\n    if (!this.parent?.isMultipleSelection) {\n      this.commitOrRemove();\n    }\n  }\n  commitOrRemove() {\n    if (this.isEmpty()) {\n      this.remove();\n    } else {\n      this.commit();\n    }\n  }\n  commit() {\n    this.addToAnnotationStorage();\n  }\n  addToAnnotationStorage() {\n    this._uiManager.addToAnnotationStorage(this);\n  }\n  setAt(x, y, tx, ty) {\n    const [width, height] = this.parentDimensions;\n    [tx, ty] = this.screenToPageTranslation(tx, ty);\n    this.x = (x + tx) / width;\n    this.y = (y + ty) / height;\n    this.fixAndSetPosition();\n  }\n  #translate([width, height], x, y) {\n    [x, y] = this.screenToPageTranslation(x, y);\n    this.x += x / width;\n    this.y += y / height;\n    this.fixAndSetPosition();\n  }\n  translate(x, y) {\n    this.#translate(this.parentDimensions, x, y);\n  }\n  translateInPage(x, y) {\n    this.#translate(this.pageDimensions, x, y);\n    this.div.scrollIntoView({\n      block: \"nearest\"\n    });\n  }\n  drag(tx, ty) {\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    this.x += tx / parentWidth;\n    this.y += ty / parentHeight;\n    if (this.parent && (this.x < 0 || this.x > 1 || this.y < 0 || this.y > 1)) {\n      const {\n        x,\n        y\n      } = this.div.getBoundingClientRect();\n      if (this.parent.findNewParent(this, x, y)) {\n        this.x -= Math.floor(this.x);\n        this.y -= Math.floor(this.y);\n      }\n    }\n    let {\n      x,\n      y\n    } = this;\n    const [bx, by] = this.#getBaseTranslation();\n    x += bx;\n    y += by;\n    this.div.style.left = `${(100 * x).toFixed(2)}%`;\n    this.div.style.top = `${(100 * y).toFixed(2)}%`;\n    this.div.scrollIntoView({\n      block: \"nearest\"\n    });\n  }\n  #getBaseTranslation() {\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    const {\n      _borderLineWidth\n    } = AnnotationEditor;\n    const x = _borderLineWidth / parentWidth;\n    const y = _borderLineWidth / parentHeight;\n    switch (this.rotation) {\n      case 90:\n        return [-x, y];\n      case 180:\n        return [x, y];\n      case 270:\n        return [x, -y];\n      default:\n        return [-x, -y];\n    }\n  }\n  fixAndSetPosition() {\n    const [pageWidth, pageHeight] = this.pageDimensions;\n    let {\n      x,\n      y,\n      width,\n      height\n    } = this;\n    width *= pageWidth;\n    height *= pageHeight;\n    x *= pageWidth;\n    y *= pageHeight;\n    switch (this.rotation) {\n      case 0:\n        x = Math.max(0, Math.min(pageWidth - width, x));\n        y = Math.max(0, Math.min(pageHeight - height, y));\n        break;\n      case 90:\n        x = Math.max(0, Math.min(pageWidth - height, x));\n        y = Math.min(pageHeight, Math.max(width, y));\n        break;\n      case 180:\n        x = Math.min(pageWidth, Math.max(width, x));\n        y = Math.min(pageHeight, Math.max(height, y));\n        break;\n      case 270:\n        x = Math.min(pageWidth, Math.max(height, x));\n        y = Math.max(0, Math.min(pageHeight - width, y));\n        break;\n    }\n    this.x = x /= pageWidth;\n    this.y = y /= pageHeight;\n    const [bx, by] = this.#getBaseTranslation();\n    x += bx;\n    y += by;\n    const {\n      style\n    } = this.div;\n    style.left = `${(100 * x).toFixed(2)}%`;\n    style.top = `${(100 * y).toFixed(2)}%`;\n    this.moveInDOM();\n  }\n  static #rotatePoint(x, y, angle) {\n    switch (angle) {\n      case 90:\n        return [y, -x];\n      case 180:\n        return [-x, -y];\n      case 270:\n        return [-y, x];\n      default:\n        return [x, y];\n    }\n  }\n  screenToPageTranslation(x, y) {\n    return AnnotationEditor.#rotatePoint(x, y, this.parentRotation);\n  }\n  pageTranslationToScreen(x, y) {\n    return AnnotationEditor.#rotatePoint(x, y, 360 - this.parentRotation);\n  }\n  #getRotationMatrix(rotation) {\n    switch (rotation) {\n      case 90:\n        {\n          const [pageWidth, pageHeight] = this.pageDimensions;\n          return [0, -pageWidth / pageHeight, pageHeight / pageWidth, 0];\n        }\n      case 180:\n        return [-1, 0, 0, -1];\n      case 270:\n        {\n          const [pageWidth, pageHeight] = this.pageDimensions;\n          return [0, pageWidth / pageHeight, -pageHeight / pageWidth, 0];\n        }\n      default:\n        return [1, 0, 0, 1];\n    }\n  }\n  get parentScale() {\n    return this._uiManager.viewParameters.realScale;\n  }\n  get parentRotation() {\n    return (this._uiManager.viewParameters.rotation + this.pageRotation) % 360;\n  }\n  get parentDimensions() {\n    const {\n      parentScale,\n      pageDimensions: [pageWidth, pageHeight]\n    } = this;\n    const scaledWidth = pageWidth * parentScale;\n    const scaledHeight = pageHeight * parentScale;\n    return util.FeatureTest.isCSSRoundSupported ? [Math.round(scaledWidth), Math.round(scaledHeight)] : [scaledWidth, scaledHeight];\n  }\n  setDims(width, height) {\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    this.div.style.width = `${(100 * width / parentWidth).toFixed(2)}%`;\n    if (!this.#keepAspectRatio) {\n      this.div.style.height = `${(100 * height / parentHeight).toFixed(2)}%`;\n    }\n    this.#altTextButton?.classList.toggle(\"small\", width < AnnotationEditor.SMALL_EDITOR_SIZE || height < AnnotationEditor.SMALL_EDITOR_SIZE);\n  }\n  fixDims() {\n    const {\n      style\n    } = this.div;\n    const {\n      height,\n      width\n    } = style;\n    const widthPercent = width.endsWith(\"%\");\n    const heightPercent = !this.#keepAspectRatio && height.endsWith(\"%\");\n    if (widthPercent && heightPercent) {\n      return;\n    }\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    if (!widthPercent) {\n      style.width = `${(100 * parseFloat(width) / parentWidth).toFixed(2)}%`;\n    }\n    if (!this.#keepAspectRatio && !heightPercent) {\n      style.height = `${(100 * parseFloat(height) / parentHeight).toFixed(2)}%`;\n    }\n  }\n  getInitialTranslation() {\n    return [0, 0];\n  }\n  #createResizers() {\n    if (this.#resizersDiv) {\n      return;\n    }\n    this.#resizersDiv = document.createElement(\"div\");\n    this.#resizersDiv.classList.add(\"resizers\");\n    const classes = this._willKeepAspectRatio ? [\"topLeft\", \"topRight\", \"bottomRight\", \"bottomLeft\"] : [\"topLeft\", \"topMiddle\", \"topRight\", \"middleRight\", \"bottomRight\", \"bottomMiddle\", \"bottomLeft\", \"middleLeft\"];\n    for (const name of classes) {\n      const div = document.createElement(\"div\");\n      this.#resizersDiv.append(div);\n      div.classList.add(\"resizer\", name);\n      div.setAttribute(\"data-resizer-name\", name);\n      div.addEventListener(\"pointerdown\", this.#resizerPointerdown.bind(this, name));\n      div.addEventListener(\"contextmenu\", display_utils.noContextMenu);\n      div.tabIndex = -1;\n    }\n    this.div.prepend(this.#resizersDiv);\n  }\n  #resizerPointerdown(name, event) {\n    event.preventDefault();\n    const {\n      isMac\n    } = util.FeatureTest.platform;\n    if (event.button !== 0 || event.ctrlKey && isMac) {\n      return;\n    }\n    this.#toggleAltTextButton(false);\n    const boundResizerPointermove = this.#resizerPointermove.bind(this, name);\n    const savedDraggable = this._isDraggable;\n    this._isDraggable = false;\n    const pointerMoveOptions = {\n      passive: true,\n      capture: true\n    };\n    this.parent.togglePointerEvents(false);\n    window.addEventListener(\"pointermove\", boundResizerPointermove, pointerMoveOptions);\n    const savedX = this.x;\n    const savedY = this.y;\n    const savedWidth = this.width;\n    const savedHeight = this.height;\n    const savedParentCursor = this.parent.div.style.cursor;\n    const savedCursor = this.div.style.cursor;\n    this.div.style.cursor = this.parent.div.style.cursor = window.getComputedStyle(event.target).cursor;\n    const pointerUpCallback = () => {\n      this.parent.togglePointerEvents(true);\n      this.#toggleAltTextButton(true);\n      this._isDraggable = savedDraggable;\n      window.removeEventListener(\"pointerup\", pointerUpCallback);\n      window.removeEventListener(\"blur\", pointerUpCallback);\n      window.removeEventListener(\"pointermove\", boundResizerPointermove, pointerMoveOptions);\n      this.parent.div.style.cursor = savedParentCursor;\n      this.div.style.cursor = savedCursor;\n      this.#addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight);\n    };\n    window.addEventListener(\"pointerup\", pointerUpCallback);\n    window.addEventListener(\"blur\", pointerUpCallback);\n  }\n  #addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight) {\n    const newX = this.x;\n    const newY = this.y;\n    const newWidth = this.width;\n    const newHeight = this.height;\n    if (newX === savedX && newY === savedY && newWidth === savedWidth && newHeight === savedHeight) {\n      return;\n    }\n    this.addCommands({\n      cmd: () => {\n        this.width = newWidth;\n        this.height = newHeight;\n        this.x = newX;\n        this.y = newY;\n        const [parentWidth, parentHeight] = this.parentDimensions;\n        this.setDims(parentWidth * newWidth, parentHeight * newHeight);\n        this.fixAndSetPosition();\n      },\n      undo: () => {\n        this.width = savedWidth;\n        this.height = savedHeight;\n        this.x = savedX;\n        this.y = savedY;\n        const [parentWidth, parentHeight] = this.parentDimensions;\n        this.setDims(parentWidth * savedWidth, parentHeight * savedHeight);\n        this.fixAndSetPosition();\n      },\n      mustExec: true\n    });\n  }\n  #resizerPointermove(name, event) {\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    const savedX = this.x;\n    const savedY = this.y;\n    const savedWidth = this.width;\n    const savedHeight = this.height;\n    const minWidth = AnnotationEditor.MIN_SIZE / parentWidth;\n    const minHeight = AnnotationEditor.MIN_SIZE / parentHeight;\n    const round = x => Math.round(x * 10000) / 10000;\n    const rotationMatrix = this.#getRotationMatrix(this.rotation);\n    const transf = (x, y) => [rotationMatrix[0] * x + rotationMatrix[2] * y, rotationMatrix[1] * x + rotationMatrix[3] * y];\n    const invRotationMatrix = this.#getRotationMatrix(360 - this.rotation);\n    const invTransf = (x, y) => [invRotationMatrix[0] * x + invRotationMatrix[2] * y, invRotationMatrix[1] * x + invRotationMatrix[3] * y];\n    let getPoint;\n    let getOpposite;\n    let isDiagonal = false;\n    let isHorizontal = false;\n    switch (name) {\n      case \"topLeft\":\n        isDiagonal = true;\n        getPoint = (w, h) => [0, 0];\n        getOpposite = (w, h) => [w, h];\n        break;\n      case \"topMiddle\":\n        getPoint = (w, h) => [w / 2, 0];\n        getOpposite = (w, h) => [w / 2, h];\n        break;\n      case \"topRight\":\n        isDiagonal = true;\n        getPoint = (w, h) => [w, 0];\n        getOpposite = (w, h) => [0, h];\n        break;\n      case \"middleRight\":\n        isHorizontal = true;\n        getPoint = (w, h) => [w, h / 2];\n        getOpposite = (w, h) => [0, h / 2];\n        break;\n      case \"bottomRight\":\n        isDiagonal = true;\n        getPoint = (w, h) => [w, h];\n        getOpposite = (w, h) => [0, 0];\n        break;\n      case \"bottomMiddle\":\n        getPoint = (w, h) => [w / 2, h];\n        getOpposite = (w, h) => [w / 2, 0];\n        break;\n      case \"bottomLeft\":\n        isDiagonal = true;\n        getPoint = (w, h) => [0, h];\n        getOpposite = (w, h) => [w, 0];\n        break;\n      case \"middleLeft\":\n        isHorizontal = true;\n        getPoint = (w, h) => [0, h / 2];\n        getOpposite = (w, h) => [w, h / 2];\n        break;\n    }\n    const point = getPoint(savedWidth, savedHeight);\n    const oppositePoint = getOpposite(savedWidth, savedHeight);\n    let transfOppositePoint = transf(...oppositePoint);\n    const oppositeX = round(savedX + transfOppositePoint[0]);\n    const oppositeY = round(savedY + transfOppositePoint[1]);\n    let ratioX = 1;\n    let ratioY = 1;\n    let [deltaX, deltaY] = this.screenToPageTranslation(event.movementX, event.movementY);\n    [deltaX, deltaY] = invTransf(deltaX / parentWidth, deltaY / parentHeight);\n    if (isDiagonal) {\n      const oldDiag = Math.hypot(savedWidth, savedHeight);\n      ratioX = ratioY = Math.max(Math.min(Math.hypot(oppositePoint[0] - point[0] - deltaX, oppositePoint[1] - point[1] - deltaY) / oldDiag, 1 / savedWidth, 1 / savedHeight), minWidth / savedWidth, minHeight / savedHeight);\n    } else if (isHorizontal) {\n      ratioX = Math.max(minWidth, Math.min(1, Math.abs(oppositePoint[0] - point[0] - deltaX))) / savedWidth;\n    } else {\n      ratioY = Math.max(minHeight, Math.min(1, Math.abs(oppositePoint[1] - point[1] - deltaY))) / savedHeight;\n    }\n    const newWidth = round(savedWidth * ratioX);\n    const newHeight = round(savedHeight * ratioY);\n    transfOppositePoint = transf(...getOpposite(newWidth, newHeight));\n    const newX = oppositeX - transfOppositePoint[0];\n    const newY = oppositeY - transfOppositePoint[1];\n    this.width = newWidth;\n    this.height = newHeight;\n    this.x = newX;\n    this.y = newY;\n    this.setDims(parentWidth * newWidth, parentHeight * newHeight);\n    this.fixAndSetPosition();\n  }\n  async addAltTextButton() {\n    if (this.#altTextButton) {\n      return;\n    }\n    const altText = this.#altTextButton = document.createElement(\"button\");\n    altText.className = \"altText\";\n    const msg = await AnnotationEditor._l10nPromise.get(\"pdfjs-editor-alt-text-button-label\");\n    altText.textContent = msg;\n    altText.setAttribute(\"aria-label\", msg);\n    altText.tabIndex = \"0\";\n    altText.addEventListener(\"contextmenu\", display_utils.noContextMenu);\n    altText.addEventListener(\"pointerdown\", event => event.stopPropagation());\n    const onClick = event => {\n      this.#altTextButton.hidden = true;\n      event.preventDefault();\n      this._uiManager.editAltText(this);\n    };\n    altText.addEventListener(\"click\", onClick, {\n      capture: true\n    });\n    altText.addEventListener(\"keydown\", event => {\n      if (event.target === altText && event.key === \"Enter\") {\n        this.#altTextWasFromKeyBoard = true;\n        onClick(event);\n      }\n    });\n    this.#setAltTextButtonState();\n    this.div.append(altText);\n    if (!AnnotationEditor.SMALL_EDITOR_SIZE) {\n      const PERCENT = 40;\n      AnnotationEditor.SMALL_EDITOR_SIZE = Math.min(128, Math.round(altText.getBoundingClientRect().width * (1 + PERCENT / 100)));\n    }\n  }\n  async #setAltTextButtonState() {\n    const button = this.#altTextButton;\n    if (!button) {\n      return;\n    }\n    if (!this.#altText && !this.#altTextDecorative) {\n      button.classList.remove(\"done\");\n      this.#altTextTooltip?.remove();\n      return;\n    }\n    button.classList.add(\"done\");\n    AnnotationEditor._l10nPromise.get(\"pdfjs-editor-alt-text-edit-button-label\").then(msg => {\n      button.setAttribute(\"aria-label\", msg);\n    });\n    let tooltip = this.#altTextTooltip;\n    if (!tooltip) {\n      this.#altTextTooltip = tooltip = document.createElement(\"span\");\n      tooltip.className = \"tooltip\";\n      tooltip.setAttribute(\"role\", \"tooltip\");\n      const id = tooltip.id = `alt-text-tooltip-${this.id}`;\n      button.setAttribute(\"aria-describedby\", id);\n      const DELAY_TO_SHOW_TOOLTIP = 100;\n      button.addEventListener(\"mouseenter\", () => {\n        this.#altTextTooltipTimeout = setTimeout(() => {\n          this.#altTextTooltipTimeout = null;\n          this.#altTextTooltip.classList.add(\"show\");\n          this._uiManager._eventBus.dispatch(\"reporttelemetry\", {\n            source: this,\n            details: {\n              type: \"editing\",\n              subtype: this.editorType,\n              data: {\n                action: \"alt_text_tooltip\"\n              }\n            }\n          });\n        }, DELAY_TO_SHOW_TOOLTIP);\n      });\n      button.addEventListener(\"mouseleave\", () => {\n        if (this.#altTextTooltipTimeout) {\n          clearTimeout(this.#altTextTooltipTimeout);\n          this.#altTextTooltipTimeout = null;\n        }\n        this.#altTextTooltip?.classList.remove(\"show\");\n      });\n    }\n    tooltip.innerText = this.#altTextDecorative ? await AnnotationEditor._l10nPromise.get(\"pdfjs-editor-alt-text-decorative-tooltip\") : this.#altText;\n    if (!tooltip.parentNode) {\n      button.append(tooltip);\n    }\n    const element = this.getImageForAltText();\n    element?.setAttribute(\"aria-describedby\", tooltip.id);\n  }\n  #toggleAltTextButton(enabled = false) {\n    if (!this.#altTextButton) {\n      return;\n    }\n    if (!enabled && this.#altTextTooltipTimeout) {\n      clearTimeout(this.#altTextTooltipTimeout);\n      this.#altTextTooltipTimeout = null;\n    }\n    this.#altTextButton.disabled = !enabled;\n  }\n  altTextFinish() {\n    if (!this.#altTextButton) {\n      return;\n    }\n    this.#altTextButton.hidden = false;\n    this.#altTextButton.focus({\n      focusVisible: this.#altTextWasFromKeyBoard\n    });\n    this.#altTextWasFromKeyBoard = false;\n  }\n  addEditToolbar() {\n    if (this.#editToolbar || this.#isInEditMode) {\n      return;\n    }\n    this.#editToolbar = new EditorToolbar(this);\n    this.div.append(this.#editToolbar.render());\n  }\n  removeEditToolbar() {\n    if (!this.#editToolbar) {\n      return;\n    }\n    this.#editToolbar.remove();\n    this.#editToolbar = null;\n  }\n  getClientDimensions() {\n    return this.div.getBoundingClientRect();\n  }\n  get altTextData() {\n    return {\n      altText: this.#altText,\n      decorative: this.#altTextDecorative\n    };\n  }\n  set altTextData({\n    altText,\n    decorative\n  }) {\n    if (this.#altText === altText && this.#altTextDecorative === decorative) {\n      return;\n    }\n    this.#altText = altText;\n    this.#altTextDecorative = decorative;\n    this.#setAltTextButtonState();\n  }\n  render() {\n    this.div = document.createElement(\"div\");\n    this.div.setAttribute(\"data-editor-rotation\", (360 - this.rotation) % 360);\n    this.div.className = this.name;\n    this.div.setAttribute(\"id\", this.id);\n    this.div.setAttribute(\"tabIndex\", 0);\n    this.setInForeground();\n    this.div.addEventListener(\"focusin\", this.#boundFocusin);\n    this.div.addEventListener(\"focusout\", this.#boundFocusout);\n    const [parentWidth, parentHeight] = this.parentDimensions;\n    if (this.parentRotation % 180 !== 0) {\n      this.div.style.maxWidth = `${(100 * parentHeight / parentWidth).toFixed(2)}%`;\n      this.div.style.maxHeight = `${(100 * parentWidth / parentHeight).toFixed(2)}%`;\n    }\n    const [tx, ty] = this.getInitialTranslation();\n    this.translate(tx, ty);\n    (0,tools.bindEvents)(this, this.div, [\"pointerdown\"]);\n    return this.div;\n  }\n  pointerdown(event) {\n    const {\n      isMac\n    } = util.FeatureTest.platform;\n    if (event.button !== 0 || event.ctrlKey && isMac) {\n      event.preventDefault();\n      return;\n    }\n    this.#hasBeenClicked = true;\n    this.#setUpDragSession(event);\n  }\n  #setUpDragSession(event) {\n    if (!this._isDraggable) {\n      return;\n    }\n    const isSelected = this._uiManager.isSelected(this);\n    this._uiManager.setUpDragSession();\n    let pointerMoveOptions, pointerMoveCallback;\n    if (isSelected) {\n      pointerMoveOptions = {\n        passive: true,\n        capture: true\n      };\n      pointerMoveCallback = e => {\n        const [tx, ty] = this.screenToPageTranslation(e.movementX, e.movementY);\n        this._uiManager.dragSelectedEditors(tx, ty);\n      };\n      window.addEventListener(\"pointermove\", pointerMoveCallback, pointerMoveOptions);\n    }\n    const pointerUpCallback = () => {\n      window.removeEventListener(\"pointerup\", pointerUpCallback);\n      window.removeEventListener(\"blur\", pointerUpCallback);\n      if (isSelected) {\n        window.removeEventListener(\"pointermove\", pointerMoveCallback, pointerMoveOptions);\n      }\n      this.#hasBeenClicked = false;\n      if (!this._uiManager.endDragSession()) {\n        const {\n          isMac\n        } = util.FeatureTest.platform;\n        if (event.ctrlKey && !isMac || event.shiftKey || event.metaKey && isMac) {\n          this.parent.toggleSelected(this);\n        } else {\n          this.parent.setSelected(this);\n        }\n      }\n    };\n    window.addEventListener(\"pointerup\", pointerUpCallback);\n    window.addEventListener(\"blur\", pointerUpCallback);\n  }\n  moveInDOM() {\n    if (this.#moveInDOMTimeout) {\n      clearTimeout(this.#moveInDOMTimeout);\n    }\n    this.#moveInDOMTimeout = setTimeout(() => {\n      this.#moveInDOMTimeout = null;\n      this.parent?.moveEditorInDOM(this);\n    }, 0);\n  }\n  _setParentAndPosition(parent, x, y) {\n    parent.changeParent(this);\n    this.x = x;\n    this.y = y;\n    this.fixAndSetPosition();\n  }\n  getRect(tx, ty) {\n    const scale = this.parentScale;\n    const [pageWidth, pageHeight] = this.pageDimensions;\n    const [pageX, pageY] = this.pageTranslation;\n    const shiftX = tx / scale;\n    const shiftY = ty / scale;\n    const x = this.x * pageWidth;\n    const y = this.y * pageHeight;\n    const width = this.width * pageWidth;\n    const height = this.height * pageHeight;\n    switch (this.rotation) {\n      case 0:\n        return [x + shiftX + pageX, pageHeight - y - shiftY - height + pageY, x + shiftX + width + pageX, pageHeight - y - shiftY + pageY];\n      case 90:\n        return [x + shiftY + pageX, pageHeight - y + shiftX + pageY, x + shiftY + height + pageX, pageHeight - y + shiftX + width + pageY];\n      case 180:\n        return [x - shiftX - width + pageX, pageHeight - y + shiftY + pageY, x - shiftX + pageX, pageHeight - y + shiftY + height + pageY];\n      case 270:\n        return [x - shiftY - height + pageX, pageHeight - y - shiftX - width + pageY, x - shiftY + pageX, pageHeight - y - shiftX + pageY];\n      default:\n        throw new Error(\"Invalid rotation\");\n    }\n  }\n  getRectInCurrentCoords(rect, pageHeight) {\n    const [x1, y1, x2, y2] = rect;\n    const width = x2 - x1;\n    const height = y2 - y1;\n    switch (this.rotation) {\n      case 0:\n        return [x1, pageHeight - y2, width, height];\n      case 90:\n        return [x1, pageHeight - y1, height, width];\n      case 180:\n        return [x2, pageHeight - y1, width, height];\n      case 270:\n        return [x2, pageHeight - y2, height, width];\n      default:\n        throw new Error(\"Invalid rotation\");\n    }\n  }\n  onceAdded() {}\n  isEmpty() {\n    return false;\n  }\n  enableEditMode() {\n    this.#isInEditMode = true;\n  }\n  disableEditMode() {\n    this.#isInEditMode = false;\n  }\n  isInEditMode() {\n    return this.#isInEditMode;\n  }\n  shouldGetKeyboardEvents() {\n    return this.#isResizerEnabledForKeyboard;\n  }\n  needsToBeRebuilt() {\n    return this.div && !this.isAttachedToDOM;\n  }\n  rebuild() {\n    this.div?.addEventListener(\"focusin\", this.#boundFocusin);\n    this.div?.addEventListener(\"focusout\", this.#boundFocusout);\n  }\n  serialize(isForCopying = false, context = null) {\n    (0,util.unreachable)(\"An editor must be serializable\");\n  }\n  static deserialize(data, parent, uiManager) {\n    const editor = new this.prototype.constructor({\n      parent,\n      id: parent.getNextId(),\n      uiManager\n    });\n    editor.rotation = data.rotation;\n    const [pageWidth, pageHeight] = editor.pageDimensions;\n    const [x, y, width, height] = editor.getRectInCurrentCoords(data.rect, pageHeight);\n    editor.x = x / pageWidth;\n    editor.y = y / pageHeight;\n    editor.width = width / pageWidth;\n    editor.height = height / pageHeight;\n    return editor;\n  }\n  remove() {\n    this.div.removeEventListener(\"focusin\", this.#boundFocusin);\n    this.div.removeEventListener(\"focusout\", this.#boundFocusout);\n    if (!this.isEmpty()) {\n      this.commit();\n    }\n    if (this.parent) {\n      this.parent.remove(this);\n    } else {\n      this._uiManager.removeEditor(this);\n    }\n    this.#altTextButton?.remove();\n    this.#altTextButton = null;\n    this.#altTextTooltip = null;\n    if (this.#moveInDOMTimeout) {\n      clearTimeout(this.#moveInDOMTimeout);\n      this.#moveInDOMTimeout = null;\n    }\n    this.#stopResizing();\n    this.removeEditToolbar();\n  }\n  get isResizable() {\n    return false;\n  }\n  makeResizable() {\n    if (this.isResizable) {\n      this.#createResizers();\n      this.#resizersDiv.classList.remove(\"hidden\");\n      (0,tools.bindEvents)(this, this.div, [\"keydown\"]);\n    }\n  }\n  keydown(event) {\n    if (!this.isResizable || event.target !== this.div || event.key !== \"Enter\") {\n      return;\n    }\n    this._uiManager.setSelected(this);\n    this.#savedDimensions = {\n      savedX: this.x,\n      savedY: this.y,\n      savedWidth: this.width,\n      savedHeight: this.height\n    };\n    const children = this.#resizersDiv.children;\n    if (!this.#allResizerDivs) {\n      this.#allResizerDivs = Array.from(children);\n      const boundResizerKeydown = this.#resizerKeydown.bind(this);\n      const boundResizerBlur = this.#resizerBlur.bind(this);\n      for (const div of this.#allResizerDivs) {\n        const name = div.getAttribute(\"data-resizer-name\");\n        div.setAttribute(\"role\", \"spinbutton\");\n        div.addEventListener(\"keydown\", boundResizerKeydown);\n        div.addEventListener(\"blur\", boundResizerBlur);\n        div.addEventListener(\"focus\", this.#resizerFocus.bind(this, name));\n        AnnotationEditor._l10nPromise.get(`pdfjs-editor-resizer-label-${name}`).then(msg => div.setAttribute(\"aria-label\", msg));\n      }\n    }\n    const first = this.#allResizerDivs[0];\n    let firstPosition = 0;\n    for (const div of children) {\n      if (div === first) {\n        break;\n      }\n      firstPosition++;\n    }\n    const nextFirstPosition = (360 - this.rotation + this.parentRotation) % 360 / 90 * (this.#allResizerDivs.length / 4);\n    if (nextFirstPosition !== firstPosition) {\n      if (nextFirstPosition < firstPosition) {\n        for (let i = 0; i < firstPosition - nextFirstPosition; i++) {\n          this.#resizersDiv.append(this.#resizersDiv.firstChild);\n        }\n      } else if (nextFirstPosition > firstPosition) {\n        for (let i = 0; i < nextFirstPosition - firstPosition; i++) {\n          this.#resizersDiv.firstChild.before(this.#resizersDiv.lastChild);\n        }\n      }\n      let i = 0;\n      for (const child of children) {\n        const div = this.#allResizerDivs[i++];\n        const name = div.getAttribute(\"data-resizer-name\");\n        AnnotationEditor._l10nPromise.get(`pdfjs-editor-resizer-label-${name}`).then(msg => child.setAttribute(\"aria-label\", msg));\n      }\n    }\n    this.#setResizerTabIndex(0);\n    this.#isResizerEnabledForKeyboard = true;\n    this.#resizersDiv.firstChild.focus({\n      focusVisible: true\n    });\n    event.preventDefault();\n    event.stopImmediatePropagation();\n  }\n  #resizerKeydown(event) {\n    AnnotationEditor._resizerKeyboardManager.exec(this, event);\n  }\n  #resizerBlur(event) {\n    if (this.#isResizerEnabledForKeyboard && event.relatedTarget?.parentNode !== this.#resizersDiv) {\n      this.#stopResizing();\n    }\n  }\n  #resizerFocus(name) {\n    this.#focusedResizerName = this.#isResizerEnabledForKeyboard ? name : \"\";\n  }\n  #setResizerTabIndex(value) {\n    if (!this.#allResizerDivs) {\n      return;\n    }\n    for (const div of this.#allResizerDivs) {\n      div.tabIndex = value;\n    }\n  }\n  _resizeWithKeyboard(x, y) {\n    if (!this.#isResizerEnabledForKeyboard) {\n      return;\n    }\n    this.#resizerPointermove(this.#focusedResizerName, {\n      movementX: x,\n      movementY: y\n    });\n  }\n  #stopResizing() {\n    this.#isResizerEnabledForKeyboard = false;\n    this.#setResizerTabIndex(-1);\n    if (this.#savedDimensions) {\n      const {\n        savedX,\n        savedY,\n        savedWidth,\n        savedHeight\n      } = this.#savedDimensions;\n      this.#addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight);\n      this.#savedDimensions = null;\n    }\n  }\n  _stopResizingWithKeyboard() {\n    this.#stopResizing();\n    this.div.focus();\n  }\n  select() {\n    this.makeResizable();\n    this.div?.classList.add(\"selectedEditor\");\n    this.addEditToolbar();\n    this.#editToolbar?.show();\n  }\n  unselect() {\n    this.#resizersDiv?.classList.add(\"hidden\");\n    this.div?.classList.remove(\"selectedEditor\");\n    if (this.div?.contains(document.activeElement)) {\n      this._uiManager.currentLayer.div.focus();\n    }\n    this.#editToolbar?.hide();\n  }\n  updateParams(type, value) {}\n  disableEditing() {\n    if (this.#altTextButton) {\n      this.#altTextButton.hidden = true;\n    }\n  }\n  enableEditing() {\n    if (this.#altTextButton) {\n      this.#altTextButton.hidden = false;\n    }\n  }\n  enterInEditMode() {}\n  getImageForAltText() {\n    return null;\n  }\n  get contentDiv() {\n    return this.div;\n  }\n  get isEditing() {\n    return this.#isEditing;\n  }\n  set isEditing(value) {\n    this.#isEditing = value;\n    if (!this.parent) {\n      return;\n    }\n    if (value) {\n      this.parent.setSelected(this);\n      this.parent.setActiveEditor(this);\n    } else {\n      this.parent.setActiveEditor(null);\n    }\n  }\n  setAspectRatio(width, height) {\n    this.#keepAspectRatio = true;\n    const aspectRatio = width / height;\n    const {\n      style\n    } = this.div;\n    style.aspectRatio = aspectRatio;\n    style.height = \"auto\";\n  }\n  static get MIN_SIZE() {\n    return 16;\n  }\n}\nclass FakeEditor extends AnnotationEditor {\n  constructor(params) {\n    super(params);\n    this.annotationElementId = params.annotationElementId;\n    this.deleted = true;\n  }\n  serialize() {\n    return {\n      id: this.annotationElementId,\n      deleted: true,\n      pageIndex: this.pageIndex\n    };\n  }\n}\n\n\n/***/ }),\n\n/***/ 405:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   Outliner: () => (/* binding */ Outliner)\n/* harmony export */ });\nclass Outliner {\n  #box;\n  #verticalEdges = [];\n  #intervals = [];\n  constructor(boxes, borderWidth = 0, innerMargin = 0, isLTR = true) {\n    let minX = Infinity;\n    let maxX = -Infinity;\n    let minY = Infinity;\n    let maxY = -Infinity;\n    const NUMBER_OF_DIGITS = 4;\n    const EPSILON = 10 ** -NUMBER_OF_DIGITS;\n    for (const {\n      x,\n      y,\n      width,\n      height\n    } of boxes) {\n      const x1 = Math.floor((x - borderWidth) / EPSILON) * EPSILON;\n      const x2 = Math.ceil((x + width + borderWidth) / EPSILON) * EPSILON;\n      const y1 = Math.floor((y - borderWidth) / EPSILON) * EPSILON;\n      const y2 = Math.ceil((y + height + borderWidth) / EPSILON) * EPSILON;\n      const left = [x1, y1, y2, true];\n      const right = [x2, y1, y2, false];\n      this.#verticalEdges.push(left, right);\n      minX = Math.min(minX, x1);\n      maxX = Math.max(maxX, x2);\n      minY = Math.min(minY, y1);\n      maxY = Math.max(maxY, y2);\n    }\n    const bboxWidth = maxX - minX + 2 * innerMargin;\n    const bboxHeight = maxY - minY + 2 * innerMargin;\n    const shiftedMinX = minX - innerMargin;\n    const shiftedMinY = minY - innerMargin;\n    const lastEdge = this.#verticalEdges.at(isLTR ? -1 : -2);\n    const lastPoint = [lastEdge[0], lastEdge[2]];\n    for (const edge of this.#verticalEdges) {\n      const [x, y1, y2] = edge;\n      edge[0] = (x - shiftedMinX) / bboxWidth;\n      edge[1] = (y1 - shiftedMinY) / bboxHeight;\n      edge[2] = (y2 - shiftedMinY) / bboxHeight;\n    }\n    this.#box = {\n      x: shiftedMinX,\n      y: shiftedMinY,\n      width: bboxWidth,\n      height: bboxHeight,\n      lastPoint\n    };\n  }\n  getOutlines() {\n    this.#verticalEdges.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]);\n    const outlineVerticalEdges = [];\n    for (const edge of this.#verticalEdges) {\n      if (edge[3]) {\n        outlineVerticalEdges.push(...this.#breakEdge(edge));\n        this.#insert(edge);\n      } else {\n        this.#remove(edge);\n        outlineVerticalEdges.push(...this.#breakEdge(edge));\n      }\n    }\n    return this.#getOutlines(outlineVerticalEdges);\n  }\n  #getOutlines(outlineVerticalEdges) {\n    const edges = [];\n    const allEdges = new Set();\n    for (const edge of outlineVerticalEdges) {\n      const [x, y1, y2] = edge;\n      edges.push([x, y1, edge], [x, y2, edge]);\n    }\n    edges.sort((a, b) => a[1] - b[1] || a[0] - b[0]);\n    for (let i = 0, ii = edges.length; i < ii; i += 2) {\n      const edge1 = edges[i][2];\n      const edge2 = edges[i + 1][2];\n      edge1.push(edge2);\n      edge2.push(edge1);\n      allEdges.add(edge1);\n      allEdges.add(edge2);\n    }\n    const outlines = [];\n    let outline;\n    while (allEdges.size > 0) {\n      const edge = allEdges.values().next().value;\n      let [x, y1, y2, edge1, edge2] = edge;\n      allEdges.delete(edge);\n      let lastPointX = x;\n      let lastPointY = y1;\n      outline = [x, y2];\n      outlines.push(outline);\n      while (true) {\n        let e;\n        if (allEdges.has(edge1)) {\n          e = edge1;\n        } else if (allEdges.has(edge2)) {\n          e = edge2;\n        } else {\n          break;\n        }\n        allEdges.delete(e);\n        [x, y1, y2, edge1, edge2] = e;\n        if (lastPointX !== x) {\n          outline.push(lastPointX, lastPointY, x, lastPointY === y1 ? y1 : y2);\n          lastPointX = x;\n        }\n        lastPointY = lastPointY === y1 ? y2 : y1;\n      }\n      outline.push(lastPointX, lastPointY);\n    }\n    return {\n      outlines,\n      box: this.#box\n    };\n  }\n  #binarySearch(y) {\n    const array = this.#intervals;\n    let start = 0;\n    let end = array.length - 1;\n    while (start <= end) {\n      const middle = start + end >> 1;\n      const y1 = array[middle][0];\n      if (y1 === y) {\n        return middle;\n      }\n      if (y1 < y) {\n        start = middle + 1;\n      } else {\n        end = middle - 1;\n      }\n    }\n    return end + 1;\n  }\n  #insert([, y1, y2]) {\n    const index = this.#binarySearch(y1);\n    this.#intervals.splice(index, 0, [y1, y2]);\n  }\n  #remove([, y1, y2]) {\n    const index = this.#binarySearch(y1);\n    for (let i = index; i < this.#intervals.length; i++) {\n      const [start, end] = this.#intervals[i];\n      if (start !== y1) {\n        break;\n      }\n      if (start === y1 && end === y2) {\n        this.#intervals.splice(i, 1);\n        return;\n      }\n    }\n    for (let i = index - 1; i >= 0; i--) {\n      const [start, end] = this.#intervals[i];\n      if (start !== y1) {\n        break;\n      }\n      if (start === y1 && end === y2) {\n        this.#intervals.splice(i, 1);\n        return;\n      }\n    }\n  }\n  #breakEdge(edge) {\n    const [x, y1, y2] = edge;\n    const results = [[x, y1, y2]];\n    const index = this.#binarySearch(y2);\n    for (let i = 0; i < index; i++) {\n      const [start, end] = this.#intervals[i];\n      for (let j = 0, jj = results.length; j < jj; j++) {\n        const [, y3, y4] = results[j];\n        if (end <= y3 || y4 <= start) {\n          continue;\n        }\n        if (y3 >= start) {\n          if (y4 > end) {\n            results[j][1] = end;\n          } else {\n            if (jj === 1) {\n              return [];\n            }\n            results.splice(j, 1);\n            j--;\n            jj--;\n          }\n          continue;\n        }\n        results[j][2] = start;\n        if (y4 > end) {\n          results.push([x, end, y4]);\n        }\n      }\n    }\n    return results;\n  }\n}\n\n\n/***/ }),\n\n/***/ 812:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   AnnotationEditorUIManager: () => (/* binding */ AnnotationEditorUIManager),\n/* harmony export */   ColorManager: () => (/* binding */ ColorManager),\n/* harmony export */   KeyboardManager: () => (/* binding */ KeyboardManager),\n/* harmony export */   bindEvents: () => (/* binding */ bindEvents),\n/* harmony export */   opacityToHex: () => (/* binding */ opacityToHex)\n/* harmony export */ });\n/* unused harmony export CommandManager */\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n/* harmony import */ var _display_utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(473);\n\n\nfunction bindEvents(obj, element, names) {\n  for (const name of names) {\n    element.addEventListener(name, obj[name].bind(obj));\n  }\n}\nfunction opacityToHex(opacity) {\n  return Math.round(Math.min(255, Math.max(1, 255 * opacity))).toString(16).padStart(2, \"0\");\n}\nclass IdManager {\n  #id = 0;\n  getId() {\n    return `${_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorPrefix}${this.#id++}`;\n  }\n}\nclass ImageManager {\n  #baseId = (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.getUuid)();\n  #id = 0;\n  #cache = null;\n  static get _isSVGFittingCanvas() {\n    const svg = `data:image/svg+xml;charset=UTF-8,<svg viewBox=\"0 0 1 1\" width=\"1\" height=\"1\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"1\" height=\"1\" style=\"fill:red;\"/></svg>`;\n    const canvas = new OffscreenCanvas(1, 3);\n    const ctx = canvas.getContext(\"2d\");\n    const image = new Image();\n    image.src = svg;\n    const promise = image.decode().then(() => {\n      ctx.drawImage(image, 0, 0, 1, 1, 0, 0, 1, 3);\n      return new Uint32Array(ctx.getImageData(0, 0, 1, 1).data.buffer)[0] === 0;\n    });\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"_isSVGFittingCanvas\", promise);\n  }\n  async #get(key, rawData) {\n    this.#cache ||= new Map();\n    let data = this.#cache.get(key);\n    if (data === null) {\n      return null;\n    }\n    if (data?.bitmap) {\n      data.refCounter += 1;\n      return data;\n    }\n    try {\n      data ||= {\n        bitmap: null,\n        id: `image_${this.#baseId}_${this.#id++}`,\n        refCounter: 0,\n        isSvg: false\n      };\n      let image;\n      if (typeof rawData === \"string\") {\n        data.url = rawData;\n        image = await (0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.fetchData)(rawData, \"blob\");\n      } else {\n        image = data.file = rawData;\n      }\n      if (image.type === \"image/svg+xml\") {\n        const mustRemoveAspectRatioPromise = ImageManager._isSVGFittingCanvas;\n        const fileReader = new FileReader();\n        const imageElement = new Image();\n        const imagePromise = new Promise((resolve, reject) => {\n          imageElement.onload = () => {\n            data.bitmap = imageElement;\n            data.isSvg = true;\n            resolve();\n          };\n          fileReader.onload = async () => {\n            const url = data.svgUrl = fileReader.result;\n            imageElement.src = (await mustRemoveAspectRatioPromise) ? `${url}#svgView(preserveAspectRatio(none))` : url;\n          };\n          imageElement.onerror = fileReader.onerror = reject;\n        });\n        fileReader.readAsDataURL(image);\n        await imagePromise;\n      } else {\n        data.bitmap = await createImageBitmap(image);\n      }\n      data.refCounter = 1;\n    } catch (e) {\n      console.error(e);\n      data = null;\n    }\n    this.#cache.set(key, data);\n    if (data) {\n      this.#cache.set(data.id, data);\n    }\n    return data;\n  }\n  async getFromFile(file) {\n    const {\n      lastModified,\n      name,\n      size,\n      type\n    } = file;\n    return this.#get(`${lastModified}_${name}_${size}_${type}`, file);\n  }\n  async getFromUrl(url) {\n    return this.#get(url, url);\n  }\n  async getFromId(id) {\n    this.#cache ||= new Map();\n    const data = this.#cache.get(id);\n    if (!data) {\n      return null;\n    }\n    if (data.bitmap) {\n      data.refCounter += 1;\n      return data;\n    }\n    if (data.file) {\n      return this.getFromFile(data.file);\n    }\n    return this.getFromUrl(data.url);\n  }\n  getSvgUrl(id) {\n    const data = this.#cache.get(id);\n    if (!data?.isSvg) {\n      return null;\n    }\n    return data.svgUrl;\n  }\n  deleteId(id) {\n    this.#cache ||= new Map();\n    const data = this.#cache.get(id);\n    if (!data) {\n      return;\n    }\n    data.refCounter -= 1;\n    if (data.refCounter !== 0) {\n      return;\n    }\n    data.bitmap = null;\n  }\n  isValidId(id) {\n    return id.startsWith(`image_${this.#baseId}_`);\n  }\n}\nclass CommandManager {\n  #commands = [];\n  #locked = false;\n  #maxSize;\n  #position = -1;\n  constructor(maxSize = 128) {\n    this.#maxSize = maxSize;\n  }\n  add({\n    cmd,\n    undo,\n    mustExec,\n    type = NaN,\n    overwriteIfSameType = false,\n    keepUndo = false\n  }) {\n    if (mustExec) {\n      cmd();\n    }\n    if (this.#locked) {\n      return;\n    }\n    const save = {\n      cmd,\n      undo,\n      type\n    };\n    if (this.#position === -1) {\n      if (this.#commands.length > 0) {\n        this.#commands.length = 0;\n      }\n      this.#position = 0;\n      this.#commands.push(save);\n      return;\n    }\n    if (overwriteIfSameType && this.#commands[this.#position].type === type) {\n      if (keepUndo) {\n        save.undo = this.#commands[this.#position].undo;\n      }\n      this.#commands[this.#position] = save;\n      return;\n    }\n    const next = this.#position + 1;\n    if (next === this.#maxSize) {\n      this.#commands.splice(0, 1);\n    } else {\n      this.#position = next;\n      if (next < this.#commands.length) {\n        this.#commands.splice(next);\n      }\n    }\n    this.#commands.push(save);\n  }\n  undo() {\n    if (this.#position === -1) {\n      return;\n    }\n    this.#locked = true;\n    this.#commands[this.#position].undo();\n    this.#locked = false;\n    this.#position -= 1;\n  }\n  redo() {\n    if (this.#position < this.#commands.length - 1) {\n      this.#position += 1;\n      this.#locked = true;\n      this.#commands[this.#position].cmd();\n      this.#locked = false;\n    }\n  }\n  hasSomethingToUndo() {\n    return this.#position !== -1;\n  }\n  hasSomethingToRedo() {\n    return this.#position < this.#commands.length - 1;\n  }\n  destroy() {\n    this.#commands = null;\n  }\n}\nclass KeyboardManager {\n  constructor(callbacks) {\n    this.buffer = [];\n    this.callbacks = new Map();\n    this.allKeys = new Set();\n    const {\n      isMac\n    } = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.FeatureTest.platform;\n    for (const [keys, callback, options = {}] of callbacks) {\n      for (const key of keys) {\n        const isMacKey = key.startsWith(\"mac+\");\n        if (isMac && isMacKey) {\n          this.callbacks.set(key.slice(4), {\n            callback,\n            options\n          });\n          this.allKeys.add(key.split(\"+\").at(-1));\n        } else if (!isMac && !isMacKey) {\n          this.callbacks.set(key, {\n            callback,\n            options\n          });\n          this.allKeys.add(key.split(\"+\").at(-1));\n        }\n      }\n    }\n  }\n  #serialize(event) {\n    if (event.altKey) {\n      this.buffer.push(\"alt\");\n    }\n    if (event.ctrlKey) {\n      this.buffer.push(\"ctrl\");\n    }\n    if (event.metaKey) {\n      this.buffer.push(\"meta\");\n    }\n    if (event.shiftKey) {\n      this.buffer.push(\"shift\");\n    }\n    this.buffer.push(event.key);\n    const str = this.buffer.join(\"+\");\n    this.buffer.length = 0;\n    return str;\n  }\n  exec(self, event) {\n    if (!this.allKeys.has(event.key)) {\n      return;\n    }\n    const info = this.callbacks.get(this.#serialize(event));\n    if (!info) {\n      return;\n    }\n    const {\n      callback,\n      options: {\n        bubbles = false,\n        args = [],\n        checker = null\n      }\n    } = info;\n    if (checker && !checker(self, event)) {\n      return;\n    }\n    callback.bind(self, ...args)();\n    if (!bubbles) {\n      event.stopPropagation();\n      event.preventDefault();\n    }\n  }\n}\nclass ColorManager {\n  static _colorsMapping = new Map([[\"CanvasText\", [0, 0, 0]], [\"Canvas\", [255, 255, 255]]]);\n  get _colors() {\n    const colors = new Map([[\"CanvasText\", null], [\"Canvas\", null]]);\n    (0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.getColorValues)(colors);\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"_colors\", colors);\n  }\n  convert(color) {\n    const rgb = (0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.getRGB)(color);\n    if (!window.matchMedia(\"(forced-colors: active)\").matches) {\n      return rgb;\n    }\n    for (const [name, RGB] of this._colors) {\n      if (RGB.every((x, i) => x === rgb[i])) {\n        return ColorManager._colorsMapping.get(name);\n      }\n    }\n    return rgb;\n  }\n  getHexCode(name) {\n    const rgb = this._colors.get(name);\n    if (!rgb) {\n      return name;\n    }\n    return _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.Util.makeHexColor(...rgb);\n  }\n}\nclass AnnotationEditorUIManager {\n  #activeEditor = null;\n  #allEditors = new Map();\n  #allLayers = new Map();\n  #altTextManager = null;\n  #annotationStorage = null;\n  #commandManager = new CommandManager();\n  #currentPageIndex = 0;\n  #deletedAnnotationsElementIds = new Set();\n  #draggingEditors = null;\n  #editorTypes = null;\n  #editorsToRescale = new Set();\n  #filterFactory = null;\n  #focusMainContainerTimeoutId = null;\n  #idManager = new IdManager();\n  #isEnabled = false;\n  #isWaiting = false;\n  #lastActiveElement = null;\n  #mode = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.NONE;\n  #selectedEditors = new Set();\n  #pageColors = null;\n  #boundBlur = this.blur.bind(this);\n  #boundFocus = this.focus.bind(this);\n  #boundCopy = this.copy.bind(this);\n  #boundCut = this.cut.bind(this);\n  #boundPaste = this.paste.bind(this);\n  #boundKeydown = this.keydown.bind(this);\n  #boundOnEditingAction = this.onEditingAction.bind(this);\n  #boundOnPageChanging = this.onPageChanging.bind(this);\n  #boundOnScaleChanging = this.onScaleChanging.bind(this);\n  #boundOnRotationChanging = this.onRotationChanging.bind(this);\n  #previousStates = {\n    isEditing: false,\n    isEmpty: true,\n    hasSomethingToUndo: false,\n    hasSomethingToRedo: false,\n    hasSelectedEditor: false\n  };\n  #translation = [0, 0];\n  #translationTimeoutId = null;\n  #container = null;\n  #viewer = null;\n  static TRANSLATE_SMALL = 1;\n  static TRANSLATE_BIG = 10;\n  static get _keyboardManager() {\n    const proto = AnnotationEditorUIManager.prototype;\n    const arrowChecker = self => {\n      return self.#container.contains(document.activeElement) && self.hasSomethingToControl();\n    };\n    const textInputChecker = (_self, {\n      target: el\n    }) => {\n      if (el instanceof HTMLInputElement) {\n        const {\n          type\n        } = el;\n        return type !== \"text\" && type !== \"number\";\n      }\n      return true;\n    };\n    const small = this.TRANSLATE_SMALL;\n    const big = this.TRANSLATE_BIG;\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"_keyboardManager\", new KeyboardManager([[[\"ctrl+a\", \"mac+meta+a\"], proto.selectAll, {\n      checker: textInputChecker\n    }], [[\"ctrl+z\", \"mac+meta+z\"], proto.undo, {\n      checker: textInputChecker\n    }], [[\"ctrl+y\", \"ctrl+shift+z\", \"mac+meta+shift+z\", \"ctrl+shift+Z\", \"mac+meta+shift+Z\"], proto.redo, {\n      checker: textInputChecker\n    }], [[\"Backspace\", \"alt+Backspace\", \"ctrl+Backspace\", \"shift+Backspace\", \"mac+Backspace\", \"mac+alt+Backspace\", \"mac+ctrl+Backspace\", \"Delete\", \"ctrl+Delete\", \"shift+Delete\", \"mac+Delete\"], proto.delete, {\n      checker: textInputChecker\n    }], [[\"Enter\", \"mac+Enter\"], proto.addNewEditorFromKeyboard, {\n      checker: (self, {\n        target: el\n      }) => !(el instanceof HTMLButtonElement) && self.#container.contains(el) && !self.isEnterHandled\n    }], [[\" \", \"mac+ \"], proto.addNewEditorFromKeyboard, {\n      checker: self => self.#container.contains(document.activeElement)\n    }], [[\"Escape\", \"mac+Escape\"], proto.unselectAll], [[\"ArrowLeft\", \"mac+ArrowLeft\"], proto.translateSelectedEditors, {\n      args: [-small, 0],\n      checker: arrowChecker\n    }], [[\"ctrl+ArrowLeft\", \"mac+shift+ArrowLeft\"], proto.translateSelectedEditors, {\n      args: [-big, 0],\n      checker: arrowChecker\n    }], [[\"ArrowRight\", \"mac+ArrowRight\"], proto.translateSelectedEditors, {\n      args: [small, 0],\n      checker: arrowChecker\n    }], [[\"ctrl+ArrowRight\", \"mac+shift+ArrowRight\"], proto.translateSelectedEditors, {\n      args: [big, 0],\n      checker: arrowChecker\n    }], [[\"ArrowUp\", \"mac+ArrowUp\"], proto.translateSelectedEditors, {\n      args: [0, -small],\n      checker: arrowChecker\n    }], [[\"ctrl+ArrowUp\", \"mac+shift+ArrowUp\"], proto.translateSelectedEditors, {\n      args: [0, -big],\n      checker: arrowChecker\n    }], [[\"ArrowDown\", \"mac+ArrowDown\"], proto.translateSelectedEditors, {\n      args: [0, small],\n      checker: arrowChecker\n    }], [[\"ctrl+ArrowDown\", \"mac+shift+ArrowDown\"], proto.translateSelectedEditors, {\n      args: [0, big],\n      checker: arrowChecker\n    }]]));\n  }\n  constructor(container, viewer, altTextManager, eventBus, pdfDocument, pageColors) {\n    this.#container = container;\n    this.#viewer = viewer;\n    this.#altTextManager = altTextManager;\n    this._eventBus = eventBus;\n    this._eventBus._on(\"editingaction\", this.#boundOnEditingAction);\n    this._eventBus._on(\"pagechanging\", this.#boundOnPageChanging);\n    this._eventBus._on(\"scalechanging\", this.#boundOnScaleChanging);\n    this._eventBus._on(\"rotationchanging\", this.#boundOnRotationChanging);\n    this.#annotationStorage = pdfDocument.annotationStorage;\n    this.#filterFactory = pdfDocument.filterFactory;\n    this.#pageColors = pageColors;\n    this.viewParameters = {\n      realScale: _display_utils_js__WEBPACK_IMPORTED_MODULE_1__.PixelsPerInch.PDF_TO_CSS_UNITS,\n      rotation: 0\n    };\n  }\n  destroy() {\n    this.#removeKeyboardManager();\n    this.#removeFocusManager();\n    this._eventBus._off(\"editingaction\", this.#boundOnEditingAction);\n    this._eventBus._off(\"pagechanging\", this.#boundOnPageChanging);\n    this._eventBus._off(\"scalechanging\", this.#boundOnScaleChanging);\n    this._eventBus._off(\"rotationchanging\", this.#boundOnRotationChanging);\n    for (const layer of this.#allLayers.values()) {\n      layer.destroy();\n    }\n    this.#allLayers.clear();\n    this.#allEditors.clear();\n    this.#editorsToRescale.clear();\n    this.#activeEditor = null;\n    this.#selectedEditors.clear();\n    this.#commandManager.destroy();\n    this.#altTextManager.destroy();\n    if (this.#focusMainContainerTimeoutId) {\n      clearTimeout(this.#focusMainContainerTimeoutId);\n      this.#focusMainContainerTimeoutId = null;\n    }\n    if (this.#translationTimeoutId) {\n      clearTimeout(this.#translationTimeoutId);\n      this.#translationTimeoutId = null;\n    }\n  }\n  get hcmFilter() {\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"hcmFilter\", this.#pageColors ? this.#filterFactory.addHCMFilter(this.#pageColors.foreground, this.#pageColors.background) : \"none\");\n  }\n  get direction() {\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"direction\", getComputedStyle(this.#container).direction);\n  }\n  editAltText(editor) {\n    this.#altTextManager?.editAltText(this, editor);\n  }\n  onPageChanging({\n    pageNumber\n  }) {\n    this.#currentPageIndex = pageNumber - 1;\n  }\n  focusMainContainer() {\n    this.#container.focus();\n  }\n  findParent(x, y) {\n    for (const layer of this.#allLayers.values()) {\n      const {\n        x: layerX,\n        y: layerY,\n        width,\n        height\n      } = layer.div.getBoundingClientRect();\n      if (x >= layerX && x <= layerX + width && y >= layerY && y <= layerY + height) {\n        return layer;\n      }\n    }\n    return null;\n  }\n  disableUserSelect(value = false) {\n    this.#viewer.classList.toggle(\"noUserSelect\", value);\n  }\n  addShouldRescale(editor) {\n    this.#editorsToRescale.add(editor);\n  }\n  removeShouldRescale(editor) {\n    this.#editorsToRescale.delete(editor);\n  }\n  onScaleChanging({\n    scale\n  }) {\n    this.commitOrRemove();\n    this.viewParameters.realScale = scale * _display_utils_js__WEBPACK_IMPORTED_MODULE_1__.PixelsPerInch.PDF_TO_CSS_UNITS;\n    for (const editor of this.#editorsToRescale) {\n      editor.onScaleChanging();\n    }\n  }\n  onRotationChanging({\n    pagesRotation\n  }) {\n    this.commitOrRemove();\n    this.viewParameters.rotation = pagesRotation;\n  }\n  addToAnnotationStorage(editor) {\n    if (!editor.isEmpty() && this.#annotationStorage && !this.#annotationStorage.has(editor.id)) {\n      this.#annotationStorage.setValue(editor.id, editor);\n    }\n  }\n  #addFocusManager() {\n    window.addEventListener(\"focus\", this.#boundFocus);\n    window.addEventListener(\"blur\", this.#boundBlur);\n  }\n  #removeFocusManager() {\n    window.removeEventListener(\"focus\", this.#boundFocus);\n    window.removeEventListener(\"blur\", this.#boundBlur);\n  }\n  blur() {\n    if (!this.hasSelection) {\n      return;\n    }\n    const {\n      activeElement\n    } = document;\n    for (const editor of this.#selectedEditors) {\n      if (editor.div.contains(activeElement)) {\n        this.#lastActiveElement = [editor, activeElement];\n        editor._focusEventsAllowed = false;\n        break;\n      }\n    }\n  }\n  focus() {\n    if (!this.#lastActiveElement) {\n      return;\n    }\n    const [lastEditor, lastActiveElement] = this.#lastActiveElement;\n    this.#lastActiveElement = null;\n    lastActiveElement.addEventListener(\"focusin\", () => {\n      lastEditor._focusEventsAllowed = true;\n    }, {\n      once: true\n    });\n    lastActiveElement.focus();\n  }\n  #addKeyboardManager() {\n    window.addEventListener(\"keydown\", this.#boundKeydown);\n  }\n  #removeKeyboardManager() {\n    window.removeEventListener(\"keydown\", this.#boundKeydown);\n  }\n  #addCopyPasteListeners() {\n    document.addEventListener(\"copy\", this.#boundCopy);\n    document.addEventListener(\"cut\", this.#boundCut);\n    document.addEventListener(\"paste\", this.#boundPaste);\n  }\n  #removeCopyPasteListeners() {\n    document.removeEventListener(\"copy\", this.#boundCopy);\n    document.removeEventListener(\"cut\", this.#boundCut);\n    document.removeEventListener(\"paste\", this.#boundPaste);\n  }\n  addEditListeners() {\n    this.#addKeyboardManager();\n    this.#addCopyPasteListeners();\n  }\n  removeEditListeners() {\n    this.#removeKeyboardManager();\n    this.#removeCopyPasteListeners();\n  }\n  copy(event) {\n    event.preventDefault();\n    this.#activeEditor?.commitOrRemove();\n    if (!this.hasSelection) {\n      return;\n    }\n    const editors = [];\n    for (const editor of this.#selectedEditors) {\n      const serialized = editor.serialize(true);\n      if (serialized) {\n        editors.push(serialized);\n      }\n    }\n    if (editors.length === 0) {\n      return;\n    }\n    event.clipboardData.setData(\"application/pdfjs\", JSON.stringify(editors));\n  }\n  cut(event) {\n    this.copy(event);\n    this.delete();\n  }\n  paste(event) {\n    event.preventDefault();\n    const {\n      clipboardData\n    } = event;\n    for (const item of clipboardData.items) {\n      for (const editorType of this.#editorTypes) {\n        if (editorType.isHandlingMimeForPasting(item.type)) {\n          editorType.paste(item, this.currentLayer);\n          return;\n        }\n      }\n    }\n    let data = clipboardData.getData(\"application/pdfjs\");\n    if (!data) {\n      return;\n    }\n    try {\n      data = JSON.parse(data);\n    } catch (ex) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`paste: \"${ex.message}\".`);\n      return;\n    }\n    if (!Array.isArray(data)) {\n      return;\n    }\n    this.unselectAll();\n    const layer = this.currentLayer;\n    try {\n      const newEditors = [];\n      for (const editor of data) {\n        const deserializedEditor = layer.deserialize(editor);\n        if (!deserializedEditor) {\n          return;\n        }\n        newEditors.push(deserializedEditor);\n      }\n      const cmd = () => {\n        for (const editor of newEditors) {\n          this.#addEditorToLayer(editor);\n        }\n        this.#selectEditors(newEditors);\n      };\n      const undo = () => {\n        for (const editor of newEditors) {\n          editor.remove();\n        }\n      };\n      this.addCommands({\n        cmd,\n        undo,\n        mustExec: true\n      });\n    } catch (ex) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`paste: \"${ex.message}\".`);\n    }\n  }\n  keydown(event) {\n    if (!this.isEditorHandlingKeyboard) {\n      AnnotationEditorUIManager._keyboardManager.exec(this, event);\n    }\n  }\n  onEditingAction(details) {\n    if ([\"undo\", \"redo\", \"delete\", \"selectAll\"].includes(details.name)) {\n      this[details.name]();\n    }\n  }\n  #dispatchUpdateStates(details) {\n    const hasChanged = Object.entries(details).some(([key, value]) => this.#previousStates[key] !== value);\n    if (hasChanged) {\n      this._eventBus.dispatch(\"annotationeditorstateschanged\", {\n        source: this,\n        details: Object.assign(this.#previousStates, details)\n      });\n    }\n  }\n  #dispatchUpdateUI(details) {\n    this._eventBus.dispatch(\"annotationeditorparamschanged\", {\n      source: this,\n      details\n    });\n  }\n  setEditingState(isEditing) {\n    if (isEditing) {\n      this.#addFocusManager();\n      this.#addKeyboardManager();\n      this.#addCopyPasteListeners();\n      this.#dispatchUpdateStates({\n        isEditing: this.#mode !== _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.NONE,\n        isEmpty: this.#isEmpty(),\n        hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(),\n        hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(),\n        hasSelectedEditor: false\n      });\n    } else {\n      this.#removeFocusManager();\n      this.#removeKeyboardManager();\n      this.#removeCopyPasteListeners();\n      this.#dispatchUpdateStates({\n        isEditing: false\n      });\n      this.disableUserSelect(false);\n    }\n  }\n  registerEditorTypes(types) {\n    if (this.#editorTypes) {\n      return;\n    }\n    this.#editorTypes = types;\n    for (const editorType of this.#editorTypes) {\n      this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate);\n    }\n  }\n  getId() {\n    return this.#idManager.getId();\n  }\n  get currentLayer() {\n    return this.#allLayers.get(this.#currentPageIndex);\n  }\n  getLayer(pageIndex) {\n    return this.#allLayers.get(pageIndex);\n  }\n  get currentPageIndex() {\n    return this.#currentPageIndex;\n  }\n  addLayer(layer) {\n    this.#allLayers.set(layer.pageIndex, layer);\n    if (this.#isEnabled) {\n      layer.enable();\n    } else {\n      layer.disable();\n    }\n  }\n  removeLayer(layer) {\n    this.#allLayers.delete(layer.pageIndex);\n  }\n  updateMode(mode, editId = null, isFromKeyboard = false) {\n    if (this.#mode === mode) {\n      return;\n    }\n    this.#mode = mode;\n    if (mode === _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.NONE) {\n      this.setEditingState(false);\n      this.#disableAll();\n      return;\n    }\n    this.setEditingState(true);\n    this.#enableAll();\n    this.unselectAll();\n    for (const layer of this.#allLayers.values()) {\n      layer.updateMode(mode);\n    }\n    if (!editId && isFromKeyboard) {\n      this.addNewEditorFromKeyboard();\n      return;\n    }\n    if (!editId) {\n      return;\n    }\n    for (const editor of this.#allEditors.values()) {\n      if (editor.annotationElementId === editId) {\n        this.setSelected(editor);\n        editor.enterInEditMode();\n        break;\n      }\n    }\n  }\n  addNewEditorFromKeyboard() {\n    this.currentLayer.addNewEditor();\n  }\n  updateToolbar(mode) {\n    if (mode === this.#mode) {\n      return;\n    }\n    this._eventBus.dispatch(\"switchannotationeditormode\", {\n      source: this,\n      mode\n    });\n  }\n  updateParams(type, value) {\n    if (!this.#editorTypes) {\n      return;\n    }\n    if (type === _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorParamsType.CREATE) {\n      this.currentLayer.addNewEditor();\n      return;\n    }\n    for (const editor of this.#selectedEditors) {\n      editor.updateParams(type, value);\n    }\n    for (const editorType of this.#editorTypes) {\n      editorType.updateDefaultParams(type, value);\n    }\n  }\n  enableWaiting(mustWait = false) {\n    if (this.#isWaiting === mustWait) {\n      return;\n    }\n    this.#isWaiting = mustWait;\n    for (const layer of this.#allLayers.values()) {\n      if (mustWait) {\n        layer.disableClick();\n      } else {\n        layer.enableClick();\n      }\n      layer.div.classList.toggle(\"waiting\", mustWait);\n    }\n  }\n  #enableAll() {\n    if (!this.#isEnabled) {\n      this.#isEnabled = true;\n      for (const layer of this.#allLayers.values()) {\n        layer.enable();\n      }\n    }\n  }\n  #disableAll() {\n    this.unselectAll();\n    if (this.#isEnabled) {\n      this.#isEnabled = false;\n      for (const layer of this.#allLayers.values()) {\n        layer.disable();\n      }\n    }\n  }\n  getEditors(pageIndex) {\n    const editors = [];\n    for (const editor of this.#allEditors.values()) {\n      if (editor.pageIndex === pageIndex) {\n        editors.push(editor);\n      }\n    }\n    return editors;\n  }\n  getEditor(id) {\n    return this.#allEditors.get(id);\n  }\n  addEditor(editor) {\n    this.#allEditors.set(editor.id, editor);\n  }\n  removeEditor(editor) {\n    if (editor.div.contains(document.activeElement)) {\n      if (this.#focusMainContainerTimeoutId) {\n        clearTimeout(this.#focusMainContainerTimeoutId);\n      }\n      this.#focusMainContainerTimeoutId = setTimeout(() => {\n        this.focusMainContainer();\n        this.#focusMainContainerTimeoutId = null;\n      }, 0);\n    }\n    this.#allEditors.delete(editor.id);\n    this.unselect(editor);\n    if (!editor.annotationElementId || !this.#deletedAnnotationsElementIds.has(editor.annotationElementId)) {\n      this.#annotationStorage?.remove(editor.id);\n    }\n  }\n  addDeletedAnnotationElement(editor) {\n    this.#deletedAnnotationsElementIds.add(editor.annotationElementId);\n    editor.deleted = true;\n  }\n  isDeletedAnnotationElement(annotationElementId) {\n    return this.#deletedAnnotationsElementIds.has(annotationElementId);\n  }\n  removeDeletedAnnotationElement(editor) {\n    this.#deletedAnnotationsElementIds.delete(editor.annotationElementId);\n    editor.deleted = false;\n  }\n  #addEditorToLayer(editor) {\n    const layer = this.#allLayers.get(editor.pageIndex);\n    if (layer) {\n      layer.addOrRebuild(editor);\n    } else {\n      this.addEditor(editor);\n    }\n  }\n  setActiveEditor(editor) {\n    if (this.#activeEditor === editor) {\n      return;\n    }\n    this.#activeEditor = editor;\n    if (editor) {\n      this.#dispatchUpdateUI(editor.propertiesToUpdate);\n    }\n  }\n  toggleSelected(editor) {\n    if (this.#selectedEditors.has(editor)) {\n      this.#selectedEditors.delete(editor);\n      editor.unselect();\n      this.#dispatchUpdateStates({\n        hasSelectedEditor: this.hasSelection\n      });\n      return;\n    }\n    this.#selectedEditors.add(editor);\n    editor.select();\n    this.#dispatchUpdateUI(editor.propertiesToUpdate);\n    this.#dispatchUpdateStates({\n      hasSelectedEditor: true\n    });\n  }\n  setSelected(editor) {\n    for (const ed of this.#selectedEditors) {\n      if (ed !== editor) {\n        ed.unselect();\n      }\n    }\n    this.#selectedEditors.clear();\n    this.#selectedEditors.add(editor);\n    editor.select();\n    this.#dispatchUpdateUI(editor.propertiesToUpdate);\n    this.#dispatchUpdateStates({\n      hasSelectedEditor: true\n    });\n  }\n  isSelected(editor) {\n    return this.#selectedEditors.has(editor);\n  }\n  get firstSelectedEditor() {\n    return this.#selectedEditors.values().next().value;\n  }\n  unselect(editor) {\n    editor.unselect();\n    this.#selectedEditors.delete(editor);\n    this.#dispatchUpdateStates({\n      hasSelectedEditor: this.hasSelection\n    });\n  }\n  get hasSelection() {\n    return this.#selectedEditors.size !== 0;\n  }\n  get isEnterHandled() {\n    return this.#selectedEditors.size === 1 && this.firstSelectedEditor.isEnterHandled;\n  }\n  undo() {\n    this.#commandManager.undo();\n    this.#dispatchUpdateStates({\n      hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(),\n      hasSomethingToRedo: true,\n      isEmpty: this.#isEmpty()\n    });\n  }\n  redo() {\n    this.#commandManager.redo();\n    this.#dispatchUpdateStates({\n      hasSomethingToUndo: true,\n      hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(),\n      isEmpty: this.#isEmpty()\n    });\n  }\n  addCommands(params) {\n    this.#commandManager.add(params);\n    this.#dispatchUpdateStates({\n      hasSomethingToUndo: true,\n      hasSomethingToRedo: false,\n      isEmpty: this.#isEmpty()\n    });\n  }\n  #isEmpty() {\n    if (this.#allEditors.size === 0) {\n      return true;\n    }\n    if (this.#allEditors.size === 1) {\n      for (const editor of this.#allEditors.values()) {\n        return editor.isEmpty();\n      }\n    }\n    return false;\n  }\n  delete() {\n    this.commitOrRemove();\n    if (!this.hasSelection) {\n      return;\n    }\n    const editors = [...this.#selectedEditors];\n    const cmd = () => {\n      for (const editor of editors) {\n        editor.remove();\n      }\n    };\n    const undo = () => {\n      for (const editor of editors) {\n        this.#addEditorToLayer(editor);\n      }\n    };\n    this.addCommands({\n      cmd,\n      undo,\n      mustExec: true\n    });\n  }\n  commitOrRemove() {\n    this.#activeEditor?.commitOrRemove();\n  }\n  hasSomethingToControl() {\n    return this.#activeEditor || this.hasSelection;\n  }\n  #selectEditors(editors) {\n    this.#selectedEditors.clear();\n    for (const editor of editors) {\n      if (editor.isEmpty()) {\n        continue;\n      }\n      this.#selectedEditors.add(editor);\n      editor.select();\n    }\n    this.#dispatchUpdateStates({\n      hasSelectedEditor: true\n    });\n  }\n  selectAll() {\n    for (const editor of this.#selectedEditors) {\n      editor.commit();\n    }\n    this.#selectEditors(this.#allEditors.values());\n  }\n  unselectAll() {\n    if (this.#activeEditor) {\n      this.#activeEditor.commitOrRemove();\n      return;\n    }\n    if (!this.hasSelection) {\n      return;\n    }\n    for (const editor of this.#selectedEditors) {\n      editor.unselect();\n    }\n    this.#selectedEditors.clear();\n    this.#dispatchUpdateStates({\n      hasSelectedEditor: false\n    });\n  }\n  translateSelectedEditors(x, y, noCommit = false) {\n    if (!noCommit) {\n      this.commitOrRemove();\n    }\n    if (!this.hasSelection) {\n      return;\n    }\n    this.#translation[0] += x;\n    this.#translation[1] += y;\n    const [totalX, totalY] = this.#translation;\n    const editors = [...this.#selectedEditors];\n    const TIME_TO_WAIT = 1000;\n    if (this.#translationTimeoutId) {\n      clearTimeout(this.#translationTimeoutId);\n    }\n    this.#translationTimeoutId = setTimeout(() => {\n      this.#translationTimeoutId = null;\n      this.#translation[0] = this.#translation[1] = 0;\n      this.addCommands({\n        cmd: () => {\n          for (const editor of editors) {\n            if (this.#allEditors.has(editor.id)) {\n              editor.translateInPage(totalX, totalY);\n            }\n          }\n        },\n        undo: () => {\n          for (const editor of editors) {\n            if (this.#allEditors.has(editor.id)) {\n              editor.translateInPage(-totalX, -totalY);\n            }\n          }\n        },\n        mustExec: false\n      });\n    }, TIME_TO_WAIT);\n    for (const editor of editors) {\n      editor.translateInPage(x, y);\n    }\n  }\n  setUpDragSession() {\n    if (!this.hasSelection) {\n      return;\n    }\n    this.disableUserSelect(true);\n    this.#draggingEditors = new Map();\n    for (const editor of this.#selectedEditors) {\n      this.#draggingEditors.set(editor, {\n        savedX: editor.x,\n        savedY: editor.y,\n        savedPageIndex: editor.pageIndex,\n        newX: 0,\n        newY: 0,\n        newPageIndex: -1\n      });\n    }\n  }\n  endDragSession() {\n    if (!this.#draggingEditors) {\n      return false;\n    }\n    this.disableUserSelect(false);\n    const map = this.#draggingEditors;\n    this.#draggingEditors = null;\n    let mustBeAddedInUndoStack = false;\n    for (const [{\n      x,\n      y,\n      pageIndex\n    }, value] of map) {\n      value.newX = x;\n      value.newY = y;\n      value.newPageIndex = pageIndex;\n      mustBeAddedInUndoStack ||= x !== value.savedX || y !== value.savedY || pageIndex !== value.savedPageIndex;\n    }\n    if (!mustBeAddedInUndoStack) {\n      return false;\n    }\n    const move = (editor, x, y, pageIndex) => {\n      if (this.#allEditors.has(editor.id)) {\n        const parent = this.#allLayers.get(pageIndex);\n        if (parent) {\n          editor._setParentAndPosition(parent, x, y);\n        } else {\n          editor.pageIndex = pageIndex;\n          editor.x = x;\n          editor.y = y;\n        }\n      }\n    };\n    this.addCommands({\n      cmd: () => {\n        for (const [editor, {\n          newX,\n          newY,\n          newPageIndex\n        }] of map) {\n          move(editor, newX, newY, newPageIndex);\n        }\n      },\n      undo: () => {\n        for (const [editor, {\n          savedX,\n          savedY,\n          savedPageIndex\n        }] of map) {\n          move(editor, savedX, savedY, savedPageIndex);\n        }\n      },\n      mustExec: true\n    });\n    return true;\n  }\n  dragSelectedEditors(tx, ty) {\n    if (!this.#draggingEditors) {\n      return;\n    }\n    for (const editor of this.#draggingEditors.keys()) {\n      editor.drag(tx, ty);\n    }\n  }\n  rebuild(editor) {\n    if (editor.parent === null) {\n      const parent = this.getLayer(editor.pageIndex);\n      if (parent) {\n        parent.changeParent(editor);\n        parent.addOrRebuild(editor);\n      } else {\n        this.addEditor(editor);\n        this.addToAnnotationStorage(editor);\n        editor.rebuild();\n      }\n    } else {\n      editor.parent.addOrRebuild(editor);\n    }\n  }\n  get isEditorHandlingKeyboard() {\n    return this.getActive()?.shouldGetKeyboardEvents() || this.#selectedEditors.size === 1 && this.firstSelectedEditor.shouldGetKeyboardEvents();\n  }\n  isActive(editor) {\n    return this.#activeEditor === editor;\n  }\n  getActive() {\n    return this.#activeEditor;\n  }\n  getMode() {\n    return this.#mode;\n  }\n  get imageManager() {\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"imageManager\", new ImageManager());\n  }\n}\n\n\n/***/ }),\n\n/***/ 171:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   PDFFetchStream: () => (/* binding */ PDFFetchStream)\n/* harmony export */ });\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n/* harmony import */ var _network_utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(253);\n\n\n;\nfunction createFetchOptions(headers, withCredentials, abortController) {\n  return {\n    method: \"GET\",\n    headers,\n    signal: abortController.signal,\n    mode: \"cors\",\n    credentials: withCredentials ? \"include\" : \"same-origin\",\n    redirect: \"follow\"\n  };\n}\nfunction createHeaders(httpHeaders) {\n  const headers = new Headers();\n  for (const property in httpHeaders) {\n    const value = httpHeaders[property];\n    if (value === undefined) {\n      continue;\n    }\n    headers.append(property, value);\n  }\n  return headers;\n}\nfunction getArrayBuffer(val) {\n  if (val instanceof Uint8Array) {\n    return val.buffer;\n  }\n  if (val instanceof ArrayBuffer) {\n    return val;\n  }\n  (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`getArrayBuffer - unexpected data format: ${val}`);\n  return new Uint8Array(val).buffer;\n}\nclass PDFFetchStream {\n  constructor(source) {\n    this.source = source;\n    this.isHttp = /^https?:/i.test(source.url);\n    this.httpHeaders = this.isHttp && source.httpHeaders || {};\n    this._fullRequestReader = null;\n    this._rangeRequestReaders = [];\n  }\n  get _progressiveDataLength() {\n    return this._fullRequestReader?._loaded ?? 0;\n  }\n  getFullReader() {\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(!this._fullRequestReader, \"PDFFetchStream.getFullReader can only be called once.\");\n    this._fullRequestReader = new PDFFetchStreamReader(this);\n    return this._fullRequestReader;\n  }\n  getRangeReader(begin, end) {\n    if (end <= this._progressiveDataLength) {\n      return null;\n    }\n    const reader = new PDFFetchStreamRangeReader(this, begin, end);\n    this._rangeRequestReaders.push(reader);\n    return reader;\n  }\n  cancelAllRequests(reason) {\n    this._fullRequestReader?.cancel(reason);\n    for (const reader of this._rangeRequestReaders.slice(0)) {\n      reader.cancel(reason);\n    }\n  }\n}\nclass PDFFetchStreamReader {\n  constructor(stream) {\n    this._stream = stream;\n    this._reader = null;\n    this._loaded = 0;\n    this._filename = null;\n    const source = stream.source;\n    this._withCredentials = source.withCredentials || false;\n    this._contentLength = source.length;\n    this._headersCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this._disableRange = source.disableRange || false;\n    this._rangeChunkSize = source.rangeChunkSize;\n    if (!this._rangeChunkSize && !this._disableRange) {\n      this._disableRange = true;\n    }\n    this._abortController = new AbortController();\n    this._isStreamingSupported = !source.disableStream;\n    this._isRangeSupported = !source.disableRange;\n    this._headers = createHeaders(this._stream.httpHeaders);\n    const url = source.url;\n    fetch(url, createFetchOptions(this._headers, this._withCredentials, this._abortController)).then(response => {\n      if (!(0,_network_utils_js__WEBPACK_IMPORTED_MODULE_1__.validateResponseStatus)(response.status)) {\n        throw (0,_network_utils_js__WEBPACK_IMPORTED_MODULE_1__.createResponseStatusError)(response.status, url);\n      }\n      this._reader = response.body.getReader();\n      this._headersCapability.resolve();\n      const getResponseHeader = name => {\n        return response.headers.get(name);\n      };\n      const {\n        allowRangeRequests,\n        suggestedLength\n      } = (0,_network_utils_js__WEBPACK_IMPORTED_MODULE_1__.validateRangeRequestCapabilities)({\n        getResponseHeader,\n        isHttp: this._stream.isHttp,\n        rangeChunkSize: this._rangeChunkSize,\n        disableRange: this._disableRange\n      });\n      this._isRangeSupported = allowRangeRequests;\n      this._contentLength = suggestedLength || this._contentLength;\n      this._filename = (0,_network_utils_js__WEBPACK_IMPORTED_MODULE_1__.extractFilenameFromHeader)(getResponseHeader);\n      if (!this._isStreamingSupported && this._isRangeSupported) {\n        this.cancel(new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AbortException(\"Streaming is disabled.\"));\n      }\n    }).catch(this._headersCapability.reject);\n    this.onProgress = null;\n  }\n  get headersReady() {\n    return this._headersCapability.promise;\n  }\n  get filename() {\n    return this._filename;\n  }\n  get contentLength() {\n    return this._contentLength;\n  }\n  get isRangeSupported() {\n    return this._isRangeSupported;\n  }\n  get isStreamingSupported() {\n    return this._isStreamingSupported;\n  }\n  async read() {\n    await this._headersCapability.promise;\n    const {\n      value,\n      done\n    } = await this._reader.read();\n    if (done) {\n      return {\n        value,\n        done\n      };\n    }\n    this._loaded += value.byteLength;\n    this.onProgress?.({\n      loaded: this._loaded,\n      total: this._contentLength\n    });\n    return {\n      value: getArrayBuffer(value),\n      done: false\n    };\n  }\n  cancel(reason) {\n    this._reader?.cancel(reason);\n    this._abortController.abort();\n  }\n}\nclass PDFFetchStreamRangeReader {\n  constructor(stream, begin, end) {\n    this._stream = stream;\n    this._reader = null;\n    this._loaded = 0;\n    const source = stream.source;\n    this._withCredentials = source.withCredentials || false;\n    this._readCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this._isStreamingSupported = !source.disableStream;\n    this._abortController = new AbortController();\n    this._headers = createHeaders(this._stream.httpHeaders);\n    this._headers.append(\"Range\", `bytes=${begin}-${end - 1}`);\n    const url = source.url;\n    fetch(url, createFetchOptions(this._headers, this._withCredentials, this._abortController)).then(response => {\n      if (!(0,_network_utils_js__WEBPACK_IMPORTED_MODULE_1__.validateResponseStatus)(response.status)) {\n        throw (0,_network_utils_js__WEBPACK_IMPORTED_MODULE_1__.createResponseStatusError)(response.status, url);\n      }\n      this._readCapability.resolve();\n      this._reader = response.body.getReader();\n    }).catch(this._readCapability.reject);\n    this.onProgress = null;\n  }\n  get isStreamingSupported() {\n    return this._isStreamingSupported;\n  }\n  async read() {\n    await this._readCapability.promise;\n    const {\n      value,\n      done\n    } = await this._reader.read();\n    if (done) {\n      return {\n        value,\n        done\n      };\n    }\n    this._loaded += value.byteLength;\n    this.onProgress?.({\n      loaded: this._loaded\n    });\n    return {\n      value: getArrayBuffer(value),\n      done: false\n    };\n  }\n  cancel(reason) {\n    this._reader?.cancel(reason);\n    this._abortController.abort();\n  }\n}\n\n\n/***/ }),\n\n/***/ 742:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   FontFaceObject: () => (/* binding */ FontFaceObject),\n/* harmony export */   FontLoader: () => (/* binding */ FontLoader)\n/* harmony export */ });\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n\nclass FontLoader {\n  #systemFonts = new Set();\n  constructor({\n    ownerDocument = globalThis.document,\n    styleElement = null\n  }) {\n    this._document = ownerDocument;\n    this.nativeFontFaces = new Set();\n    this.styleElement = null;\n    this.loadingRequests = [];\n    this.loadTestFontId = 0;\n  }\n  addNativeFontFace(nativeFontFace) {\n    this.nativeFontFaces.add(nativeFontFace);\n    this._document.fonts.add(nativeFontFace);\n  }\n  removeNativeFontFace(nativeFontFace) {\n    this.nativeFontFaces.delete(nativeFontFace);\n    this._document.fonts.delete(nativeFontFace);\n  }\n  insertRule(rule) {\n    if (!this.styleElement) {\n      this.styleElement = this._document.createElement(\"style\");\n      this._document.documentElement.getElementsByTagName(\"head\")[0].append(this.styleElement);\n    }\n    const styleSheet = this.styleElement.sheet;\n    styleSheet.insertRule(rule, styleSheet.cssRules.length);\n  }\n  clear() {\n    for (const nativeFontFace of this.nativeFontFaces) {\n      this._document.fonts.delete(nativeFontFace);\n    }\n    this.nativeFontFaces.clear();\n    this.#systemFonts.clear();\n    if (this.styleElement) {\n      this.styleElement.remove();\n      this.styleElement = null;\n    }\n  }\n  async loadSystemFont({\n    systemFontInfo: info,\n    _inspectFont\n  }) {\n    if (!info || this.#systemFonts.has(info.loadedName)) {\n      return;\n    }\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(!this.disableFontFace, \"loadSystemFont shouldn't be called when `disableFontFace` is set.\");\n    if (this.isFontLoadingAPISupported) {\n      const {\n        loadedName,\n        src,\n        style\n      } = info;\n      const fontFace = new FontFace(loadedName, src, style);\n      this.addNativeFontFace(fontFace);\n      try {\n        await fontFace.load();\n        this.#systemFonts.add(loadedName);\n        _inspectFont?.(info);\n      } catch {\n        (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Cannot load system font: ${info.baseFontName}, installing it could help to improve PDF rendering.`);\n        this.removeNativeFontFace(fontFace);\n      }\n      return;\n    }\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Not implemented: loadSystemFont without the Font Loading API.\");\n  }\n  async bind(font) {\n    if (font.attached || font.missingFile && !font.systemFontInfo) {\n      return;\n    }\n    font.attached = true;\n    if (font.systemFontInfo) {\n      await this.loadSystemFont(font);\n      return;\n    }\n    if (this.isFontLoadingAPISupported) {\n      const nativeFontFace = font.createNativeFontFace();\n      if (nativeFontFace) {\n        this.addNativeFontFace(nativeFontFace);\n        try {\n          await nativeFontFace.loaded;\n        } catch (ex) {\n          (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Failed to load font '${nativeFontFace.family}': '${ex}'.`);\n          font.disableFontFace = true;\n          throw ex;\n        }\n      }\n      return;\n    }\n    const rule = font.createFontFaceRule();\n    if (rule) {\n      this.insertRule(rule);\n      if (this.isSyncFontLoadingSupported) {\n        return;\n      }\n      await new Promise(resolve => {\n        const request = this._queueLoadingCallback(resolve);\n        this._prepareFontLoadEvent(font, request);\n      });\n    }\n  }\n  get isFontLoadingAPISupported() {\n    const hasFonts = !!this._document?.fonts;\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"isFontLoadingAPISupported\", hasFonts);\n  }\n  get isSyncFontLoadingSupported() {\n    let supported = false;\n    if (_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS) {\n      supported = true;\n    } else if (typeof navigator !== \"undefined\" && typeof navigator?.userAgent === \"string\" && /Mozilla\\/5.0.*?rv:\\d+.*? Gecko/.test(navigator.userAgent)) {\n      supported = true;\n    }\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"isSyncFontLoadingSupported\", supported);\n  }\n  _queueLoadingCallback(callback) {\n    function completeRequest() {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(!request.done, \"completeRequest() cannot be called twice.\");\n      request.done = true;\n      while (loadingRequests.length > 0 && loadingRequests[0].done) {\n        const otherRequest = loadingRequests.shift();\n        setTimeout(otherRequest.callback, 0);\n      }\n    }\n    const {\n      loadingRequests\n    } = this;\n    const request = {\n      done: false,\n      complete: completeRequest,\n      callback\n    };\n    loadingRequests.push(request);\n    return request;\n  }\n  get _loadTestFont() {\n    const testFont = atob(\"T1RUTwALAIAAAwAwQ0ZGIDHtZg4AAAOYAAAAgUZGVE1lkzZwAAAEHAAAABxHREVGABQA\" + \"FQAABDgAAAAeT1MvMlYNYwkAAAEgAAAAYGNtYXABDQLUAAACNAAAAUJoZWFk/xVFDQAA\" + \"ALwAAAA2aGhlYQdkA+oAAAD0AAAAJGhtdHgD6AAAAAAEWAAAAAZtYXhwAAJQAAAAARgA\" + \"AAAGbmFtZVjmdH4AAAGAAAAAsXBvc3T/hgAzAAADeAAAACAAAQAAAAEAALZRFsRfDzz1\" + \"AAsD6AAAAADOBOTLAAAAAM4KHDwAAAAAA+gDIQAAAAgAAgAAAAAAAAABAAADIQAAAFoD\" + \"6AAAAAAD6AABAAAAAAAAAAAAAAAAAAAAAQAAUAAAAgAAAAQD6AH0AAUAAAKKArwAAACM\" + \"AooCvAAAAeAAMQECAAACAAYJAAAAAAAAAAAAAQAAAAAAAAAAAAAAAFBmRWQAwAAuAC4D\" + \"IP84AFoDIQAAAAAAAQAAAAAAAAAAACAAIAABAAAADgCuAAEAAAAAAAAAAQAAAAEAAAAA\" + \"AAEAAQAAAAEAAAAAAAIAAQAAAAEAAAAAAAMAAQAAAAEAAAAAAAQAAQAAAAEAAAAAAAUA\" + \"AQAAAAEAAAAAAAYAAQAAAAMAAQQJAAAAAgABAAMAAQQJAAEAAgABAAMAAQQJAAIAAgAB\" + \"AAMAAQQJAAMAAgABAAMAAQQJAAQAAgABAAMAAQQJAAUAAgABAAMAAQQJAAYAAgABWABY\" + \"AAAAAAAAAwAAAAMAAAAcAAEAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAAAC7//wAA\" + \"AC7////TAAEAAAAAAAABBgAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\" + \"AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\" + \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\" + \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\" + \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\" + \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAD/gwAyAAAAAQAAAAAAAAAAAAAAAAAA\" + \"AAABAAQEAAEBAQJYAAEBASH4DwD4GwHEAvgcA/gXBIwMAYuL+nz5tQXkD5j3CBLnEQAC\" + \"AQEBIVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYAAABAQAADwACAQEEE/t3\" + \"Dov6fAH6fAT+fPp8+nwHDosMCvm1Cvm1DAz6fBQAAAAAAAABAAAAAMmJbzEAAAAAzgTj\" + \"FQAAAADOBOQpAAEAAAAAAAAADAAUAAQAAAABAAAAAgABAAAAAAAAAAAD6AAAAAAAAA==\");\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, \"_loadTestFont\", testFont);\n  }\n  _prepareFontLoadEvent(font, request) {\n    function int32(data, offset) {\n      return data.charCodeAt(offset) << 24 | data.charCodeAt(offset + 1) << 16 | data.charCodeAt(offset + 2) << 8 | data.charCodeAt(offset + 3) & 0xff;\n    }\n    function spliceString(s, offset, remove, insert) {\n      const chunk1 = s.substring(0, offset);\n      const chunk2 = s.substring(offset + remove);\n      return chunk1 + insert + chunk2;\n    }\n    let i, ii;\n    const canvas = this._document.createElement(\"canvas\");\n    canvas.width = 1;\n    canvas.height = 1;\n    const ctx = canvas.getContext(\"2d\");\n    let called = 0;\n    function isFontReady(name, callback) {\n      if (++called > 30) {\n        (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(\"Load test font never loaded.\");\n        callback();\n        return;\n      }\n      ctx.font = \"30px \" + name;\n      ctx.fillText(\".\", 0, 20);\n      const imageData = ctx.getImageData(0, 0, 1, 1);\n      if (imageData.data[3] > 0) {\n        callback();\n        return;\n      }\n      setTimeout(isFontReady.bind(null, name, callback));\n    }\n    const loadTestFontId = `lt${Date.now()}${this.loadTestFontId++}`;\n    let data = this._loadTestFont;\n    const COMMENT_OFFSET = 976;\n    data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length, loadTestFontId);\n    const CFF_CHECKSUM_OFFSET = 16;\n    const XXXX_VALUE = 0x58585858;\n    let checksum = int32(data, CFF_CHECKSUM_OFFSET);\n    for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) {\n      checksum = checksum - XXXX_VALUE + int32(loadTestFontId, i) | 0;\n    }\n    if (i < loadTestFontId.length) {\n      checksum = checksum - XXXX_VALUE + int32(loadTestFontId + \"XXX\", i) | 0;\n    }\n    data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.string32)(checksum));\n    const url = `url(data:font/opentype;base64,${btoa(data)});`;\n    const rule = `@font-face {font-family:\"${loadTestFontId}\";src:${url}}`;\n    this.insertRule(rule);\n    const div = this._document.createElement(\"div\");\n    div.style.visibility = \"hidden\";\n    div.style.width = div.style.height = \"10px\";\n    div.style.position = \"absolute\";\n    div.style.top = div.style.left = \"0px\";\n    for (const name of [font.loadedName, loadTestFontId]) {\n      const span = this._document.createElement(\"span\");\n      span.textContent = \"Hi\";\n      span.style.fontFamily = name;\n      div.append(span);\n    }\n    this._document.body.append(div);\n    isFontReady(loadTestFontId, () => {\n      div.remove();\n      request.complete();\n    });\n  }\n}\nclass FontFaceObject {\n  constructor(translatedData, {\n    isEvalSupported = true,\n    disableFontFace = false,\n    ignoreErrors = false,\n    inspectFont = null\n  }) {\n    this.compiledGlyphs = Object.create(null);\n    for (const i in translatedData) {\n      this[i] = translatedData[i];\n    }\n    this.isEvalSupported = isEvalSupported !== false;\n    this.disableFontFace = disableFontFace === true;\n    this.ignoreErrors = ignoreErrors === true;\n    this._inspectFont = inspectFont;\n  }\n  createNativeFontFace() {\n    if (!this.data || this.disableFontFace) {\n      return null;\n    }\n    let nativeFontFace;\n    if (!this.cssFontInfo) {\n      nativeFontFace = new FontFace(this.loadedName, this.data, {});\n    } else {\n      const css = {\n        weight: this.cssFontInfo.fontWeight\n      };\n      if (this.cssFontInfo.italicAngle) {\n        css.style = `oblique ${this.cssFontInfo.italicAngle}deg`;\n      }\n      nativeFontFace = new FontFace(this.cssFontInfo.fontFamily, this.data, css);\n    }\n    this._inspectFont?.(this);\n    return nativeFontFace;\n  }\n  createFontFaceRule() {\n    if (!this.data || this.disableFontFace) {\n      return null;\n    }\n    const data = (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.bytesToString)(this.data);\n    const url = `url(data:${this.mimetype};base64,${btoa(data)});`;\n    let rule;\n    if (!this.cssFontInfo) {\n      rule = `@font-face {font-family:\"${this.loadedName}\";src:${url}}`;\n    } else {\n      let css = `font-weight: ${this.cssFontInfo.fontWeight};`;\n      if (this.cssFontInfo.italicAngle) {\n        css += `font-style: oblique ${this.cssFontInfo.italicAngle}deg;`;\n      }\n      rule = `@font-face {font-family:\"${this.cssFontInfo.fontFamily}\";${css}src:${url}}`;\n    }\n    this._inspectFont?.(this, url);\n    return rule;\n  }\n  getPathGenerator(objs, character) {\n    if (this.compiledGlyphs[character] !== undefined) {\n      return this.compiledGlyphs[character];\n    }\n    let cmds;\n    try {\n      cmds = objs.get(this.loadedName + \"_path_\" + character);\n    } catch (ex) {\n      if (!this.ignoreErrors) {\n        throw ex;\n      }\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`getPathGenerator - ignoring character: \"${ex}\".`);\n      return this.compiledGlyphs[character] = function (c, size) {};\n    }\n    if (this.isEvalSupported && _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.FeatureTest.isEvalSupported) {\n      const jsBuf = [];\n      for (const current of cmds) {\n        const args = current.args !== undefined ? current.args.join(\",\") : \"\";\n        jsBuf.push(\"c.\", current.cmd, \"(\", args, \");\\n\");\n      }\n      return this.compiledGlyphs[character] = new Function(\"c\", \"size\", jsBuf.join(\"\"));\n    }\n    return this.compiledGlyphs[character] = function (c, size) {\n      for (const current of cmds) {\n        if (current.cmd === \"scale\") {\n          current.args = [size, -size];\n        }\n        c[current.cmd].apply(c, current.args);\n      }\n    };\n  }\n}\n\n\n/***/ }),\n\n/***/ 472:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   Metadata: () => (/* binding */ Metadata)\n/* harmony export */ });\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n\nclass Metadata {\n  #metadataMap;\n  #data;\n  constructor({\n    parsedData,\n    rawData\n  }) {\n    this.#metadataMap = parsedData;\n    this.#data = rawData;\n  }\n  getRaw() {\n    return this.#data;\n  }\n  get(name) {\n    return this.#metadataMap.get(name) ?? null;\n  }\n  getAll() {\n    return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.objectFromMap)(this.#metadataMap);\n  }\n  has(name) {\n    return this.#metadataMap.has(name);\n  }\n}\n\n\n/***/ }),\n\n/***/ 474:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   PDFNetworkStream: () => (/* binding */ PDFNetworkStream)\n/* harmony export */ });\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n/* harmony import */ var _network_utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(253);\n\n\n;\nconst OK_RESPONSE = 200;\nconst PARTIAL_CONTENT_RESPONSE = 206;\nfunction getArrayBuffer(xhr) {\n  const data = xhr.response;\n  if (typeof data !== \"string\") {\n    return data;\n  }\n  return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.stringToBytes)(data).buffer;\n}\nclass NetworkManager {\n  constructor(url, args = {}) {\n    this.url = url;\n    this.isHttp = /^https?:/i.test(url);\n    this.httpHeaders = this.isHttp && args.httpHeaders || Object.create(null);\n    this.withCredentials = args.withCredentials || false;\n    this.currXhrId = 0;\n    this.pendingRequests = Object.create(null);\n  }\n  requestRange(begin, end, listeners) {\n    const args = {\n      begin,\n      end\n    };\n    for (const prop in listeners) {\n      args[prop] = listeners[prop];\n    }\n    return this.request(args);\n  }\n  requestFull(listeners) {\n    return this.request(listeners);\n  }\n  request(args) {\n    const xhr = new XMLHttpRequest();\n    const xhrId = this.currXhrId++;\n    const pendingRequest = this.pendingRequests[xhrId] = {\n      xhr\n    };\n    xhr.open(\"GET\", this.url);\n    xhr.withCredentials = this.withCredentials;\n    for (const property in this.httpHeaders) {\n      const value = this.httpHeaders[property];\n      if (value === undefined) {\n        continue;\n      }\n      xhr.setRequestHeader(property, value);\n    }\n    if (this.isHttp && \"begin\" in args && \"end\" in args) {\n      xhr.setRequestHeader(\"Range\", `bytes=${args.begin}-${args.end - 1}`);\n      pendingRequest.expectedStatus = PARTIAL_CONTENT_RESPONSE;\n    } else {\n      pendingRequest.expectedStatus = OK_RESPONSE;\n    }\n    xhr.responseType = \"arraybuffer\";\n    if (args.onError) {\n      xhr.onerror = function (evt) {\n        args.onError(xhr.status);\n      };\n    }\n    xhr.onreadystatechange = this.onStateChange.bind(this, xhrId);\n    xhr.onprogress = this.onProgress.bind(this, xhrId);\n    pendingRequest.onHeadersReceived = args.onHeadersReceived;\n    pendingRequest.onDone = args.onDone;\n    pendingRequest.onError = args.onError;\n    pendingRequest.onProgress = args.onProgress;\n    xhr.send(null);\n    return xhrId;\n  }\n  onProgress(xhrId, evt) {\n    const pendingRequest = this.pendingRequests[xhrId];\n    if (!pendingRequest) {\n      return;\n    }\n    pendingRequest.onProgress?.(evt);\n  }\n  onStateChange(xhrId, evt) {\n    const pendingRequest = this.pendingRequests[xhrId];\n    if (!pendingRequest) {\n      return;\n    }\n    const xhr = pendingRequest.xhr;\n    if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) {\n      pendingRequest.onHeadersReceived();\n      delete pendingRequest.onHeadersReceived;\n    }\n    if (xhr.readyState !== 4) {\n      return;\n    }\n    if (!(xhrId in this.pendingRequests)) {\n      return;\n    }\n    delete this.pendingRequests[xhrId];\n    if (xhr.status === 0 && this.isHttp) {\n      pendingRequest.onError?.(xhr.status);\n      return;\n    }\n    const xhrStatus = xhr.status || OK_RESPONSE;\n    const ok_response_on_range_request = xhrStatus === OK_RESPONSE && pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE;\n    if (!ok_response_on_range_request && xhrStatus !== pendingRequest.expectedStatus) {\n      pendingRequest.onError?.(xhr.status);\n      return;\n    }\n    const chunk = getArrayBuffer(xhr);\n    if (xhrStatus === PARTIAL_CONTENT_RESPONSE) {\n      const rangeHeader = xhr.getResponseHeader(\"Content-Range\");\n      const matches = /bytes (\\d+)-(\\d+)\\/(\\d+)/.exec(rangeHeader);\n      pendingRequest.onDone({\n        begin: parseInt(matches[1], 10),\n        chunk\n      });\n    } else if (chunk) {\n      pendingRequest.onDone({\n        begin: 0,\n        chunk\n      });\n    } else {\n      pendingRequest.onError?.(xhr.status);\n    }\n  }\n  getRequestXhr(xhrId) {\n    return this.pendingRequests[xhrId].xhr;\n  }\n  isPendingRequest(xhrId) {\n    return xhrId in this.pendingRequests;\n  }\n  abortRequest(xhrId) {\n    const xhr = this.pendingRequests[xhrId].xhr;\n    delete this.pendingRequests[xhrId];\n    xhr.abort();\n  }\n}\nclass PDFNetworkStream {\n  constructor(source) {\n    this._source = source;\n    this._manager = new NetworkManager(source.url, {\n      httpHeaders: source.httpHeaders,\n      withCredentials: source.withCredentials\n    });\n    this._rangeChunkSize = source.rangeChunkSize;\n    this._fullRequestReader = null;\n    this._rangeRequestReaders = [];\n  }\n  _onRangeRequestReaderClosed(reader) {\n    const i = this._rangeRequestReaders.indexOf(reader);\n    if (i >= 0) {\n      this._rangeRequestReaders.splice(i, 1);\n    }\n  }\n  getFullReader() {\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(!this._fullRequestReader, \"PDFNetworkStream.getFullReader can only be called once.\");\n    this._fullRequestReader = new PDFNetworkStreamFullRequestReader(this._manager, this._source);\n    return this._fullRequestReader;\n  }\n  getRangeReader(begin, end) {\n    const reader = new PDFNetworkStreamRangeRequestReader(this._manager, begin, end);\n    reader.onClosed = this._onRangeRequestReaderClosed.bind(this);\n    this._rangeRequestReaders.push(reader);\n    return reader;\n  }\n  cancelAllRequests(reason) {\n    this._fullRequestReader?.cancel(reason);\n    for (const reader of this._rangeRequestReaders.slice(0)) {\n      reader.cancel(reason);\n    }\n  }\n}\nclass PDFNetworkStreamFullRequestReader {\n  constructor(manager, source) {\n    this._manager = manager;\n    const args = {\n      onHeadersReceived: this._onHeadersReceived.bind(this),\n      onDone: this._onDone.bind(this),\n      onError: this._onError.bind(this),\n      onProgress: this._onProgress.bind(this)\n    };\n    this._url = source.url;\n    this._fullRequestId = manager.requestFull(args);\n    this._headersReceivedCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this._disableRange = source.disableRange || false;\n    this._contentLength = source.length;\n    this._rangeChunkSize = source.rangeChunkSize;\n    if (!this._rangeChunkSize && !this._disableRange) {\n      this._disableRange = true;\n    }\n    this._isStreamingSupported = false;\n    this._isRangeSupported = false;\n    this._cachedChunks = [];\n    this._requests = [];\n    this._done = false;\n    this._storedError = undefined;\n    this._filename = null;\n    this.onProgress = null;\n  }\n  _onHeadersReceived() {\n    const fullRequestXhrId = this._fullRequestId;\n    const fullRequestXhr = this._manager.getRequestXhr(fullRequestXhrId);\n    const getResponseHeader = name => {\n      return fullRequestXhr.getResponseHeader(name);\n    };\n    const {\n      allowRangeRequests,\n      suggestedLength\n    } = (0,_network_utils_js__WEBPACK_IMPORTED_MODULE_1__.validateRangeRequestCapabilities)({\n      getResponseHeader,\n      isHttp: this._manager.isHttp,\n      rangeChunkSize: this._rangeChunkSize,\n      disableRange: this._disableRange\n    });\n    if (allowRangeRequests) {\n      this._isRangeSupported = true;\n    }\n    this._contentLength = suggestedLength || this._contentLength;\n    this._filename = (0,_network_utils_js__WEBPACK_IMPORTED_MODULE_1__.extractFilenameFromHeader)(getResponseHeader);\n    if (this._isRangeSupported) {\n      this._manager.abortRequest(fullRequestXhrId);\n    }\n    this._headersReceivedCapability.resolve();\n  }\n  _onDone(data) {\n    if (data) {\n      if (this._requests.length > 0) {\n        const requestCapability = this._requests.shift();\n        requestCapability.resolve({\n          value: data.chunk,\n          done: false\n        });\n      } else {\n        this._cachedChunks.push(data.chunk);\n      }\n    }\n    this._done = true;\n    if (this._cachedChunks.length > 0) {\n      return;\n    }\n    for (const requestCapability of this._requests) {\n      requestCapability.resolve({\n        value: undefined,\n        done: true\n      });\n    }\n    this._requests.length = 0;\n  }\n  _onError(status) {\n    this._storedError = (0,_network_utils_js__WEBPACK_IMPORTED_MODULE_1__.createResponseStatusError)(status, this._url);\n    this._headersReceivedCapability.reject(this._storedError);\n    for (const requestCapability of this._requests) {\n      requestCapability.reject(this._storedError);\n    }\n    this._requests.length = 0;\n    this._cachedChunks.length = 0;\n  }\n  _onProgress(evt) {\n    this.onProgress?.({\n      loaded: evt.loaded,\n      total: evt.lengthComputable ? evt.total : this._contentLength\n    });\n  }\n  get filename() {\n    return this._filename;\n  }\n  get isRangeSupported() {\n    return this._isRangeSupported;\n  }\n  get isStreamingSupported() {\n    return this._isStreamingSupported;\n  }\n  get contentLength() {\n    return this._contentLength;\n  }\n  get headersReady() {\n    return this._headersReceivedCapability.promise;\n  }\n  async read() {\n    if (this._storedError) {\n      throw this._storedError;\n    }\n    if (this._cachedChunks.length > 0) {\n      const chunk = this._cachedChunks.shift();\n      return {\n        value: chunk,\n        done: false\n      };\n    }\n    if (this._done) {\n      return {\n        value: undefined,\n        done: true\n      };\n    }\n    const requestCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this._requests.push(requestCapability);\n    return requestCapability.promise;\n  }\n  cancel(reason) {\n    this._done = true;\n    this._headersReceivedCapability.reject(reason);\n    for (const requestCapability of this._requests) {\n      requestCapability.resolve({\n        value: undefined,\n        done: true\n      });\n    }\n    this._requests.length = 0;\n    if (this._manager.isPendingRequest(this._fullRequestId)) {\n      this._manager.abortRequest(this._fullRequestId);\n    }\n    this._fullRequestReader = null;\n  }\n}\nclass PDFNetworkStreamRangeRequestReader {\n  constructor(manager, begin, end) {\n    this._manager = manager;\n    const args = {\n      onDone: this._onDone.bind(this),\n      onError: this._onError.bind(this),\n      onProgress: this._onProgress.bind(this)\n    };\n    this._url = manager.url;\n    this._requestId = manager.requestRange(begin, end, args);\n    this._requests = [];\n    this._queuedChunk = null;\n    this._done = false;\n    this._storedError = undefined;\n    this.onProgress = null;\n    this.onClosed = null;\n  }\n  _close() {\n    this.onClosed?.(this);\n  }\n  _onDone(data) {\n    const chunk = data.chunk;\n    if (this._requests.length > 0) {\n      const requestCapability = this._requests.shift();\n      requestCapability.resolve({\n        value: chunk,\n        done: false\n      });\n    } else {\n      this._queuedChunk = chunk;\n    }\n    this._done = true;\n    for (const requestCapability of this._requests) {\n      requestCapability.resolve({\n        value: undefined,\n        done: true\n      });\n    }\n    this._requests.length = 0;\n    this._close();\n  }\n  _onError(status) {\n    this._storedError = (0,_network_utils_js__WEBPACK_IMPORTED_MODULE_1__.createResponseStatusError)(status, this._url);\n    for (const requestCapability of this._requests) {\n      requestCapability.reject(this._storedError);\n    }\n    this._requests.length = 0;\n    this._queuedChunk = null;\n  }\n  _onProgress(evt) {\n    if (!this.isStreamingSupported) {\n      this.onProgress?.({\n        loaded: evt.loaded\n      });\n    }\n  }\n  get isStreamingSupported() {\n    return false;\n  }\n  async read() {\n    if (this._storedError) {\n      throw this._storedError;\n    }\n    if (this._queuedChunk !== null) {\n      const chunk = this._queuedChunk;\n      this._queuedChunk = null;\n      return {\n        value: chunk,\n        done: false\n      };\n    }\n    if (this._done) {\n      return {\n        value: undefined,\n        done: true\n      };\n    }\n    const requestCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this._requests.push(requestCapability);\n    return requestCapability.promise;\n  }\n  cancel(reason) {\n    this._done = true;\n    for (const requestCapability of this._requests) {\n      requestCapability.resolve({\n        value: undefined,\n        done: true\n      });\n    }\n    this._requests.length = 0;\n    if (this._manager.isPendingRequest(this._requestId)) {\n      this._manager.abortRequest(this._requestId);\n    }\n    this._close();\n  }\n}\n\n\n/***/ }),\n\n/***/ 253:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n  createResponseStatusError: () => (/* binding */ createResponseStatusError),\n  extractFilenameFromHeader: () => (/* binding */ extractFilenameFromHeader),\n  validateRangeRequestCapabilities: () => (/* binding */ validateRangeRequestCapabilities),\n  validateResponseStatus: () => (/* binding */ validateResponseStatus)\n});\n\n// EXTERNAL MODULE: ./src/shared/util.js\nvar util = __webpack_require__(266);\n;// CONCATENATED MODULE: ./src/display/content_disposition.js\n\nfunction getFilenameFromContentDispositionHeader(contentDisposition) {\n  let needsEncodingFixup = true;\n  let tmp = toParamRegExp(\"filename\\\\*\", \"i\").exec(contentDisposition);\n  if (tmp) {\n    tmp = tmp[1];\n    let filename = rfc2616unquote(tmp);\n    filename = unescape(filename);\n    filename = rfc5987decode(filename);\n    filename = rfc2047decode(filename);\n    return fixupEncoding(filename);\n  }\n  tmp = rfc2231getparam(contentDisposition);\n  if (tmp) {\n    const filename = rfc2047decode(tmp);\n    return fixupEncoding(filename);\n  }\n  tmp = toParamRegExp(\"filename\", \"i\").exec(contentDisposition);\n  if (tmp) {\n    tmp = tmp[1];\n    let filename = rfc2616unquote(tmp);\n    filename = rfc2047decode(filename);\n    return fixupEncoding(filename);\n  }\n  function toParamRegExp(attributePattern, flags) {\n    return new RegExp(\"(?:^|;)\\\\s*\" + attributePattern + \"\\\\s*=\\\\s*\" + \"(\" + '[^\";\\\\s][^;\\\\s]*' + \"|\" + '\"(?:[^\"\\\\\\\\]|\\\\\\\\\"?)+\"?' + \")\", flags);\n  }\n  function textdecode(encoding, value) {\n    if (encoding) {\n      if (!/^[\\x00-\\xFF]+$/.test(value)) {\n        return value;\n      }\n      try {\n        const decoder = new TextDecoder(encoding, {\n          fatal: true\n        });\n        const buffer = (0,util.stringToBytes)(value);\n        value = decoder.decode(buffer);\n        needsEncodingFixup = false;\n      } catch {}\n    }\n    return value;\n  }\n  function fixupEncoding(value) {\n    if (needsEncodingFixup && /[\\x80-\\xff]/.test(value)) {\n      value = textdecode(\"utf-8\", value);\n      if (needsEncodingFixup) {\n        value = textdecode(\"iso-8859-1\", value);\n      }\n    }\n    return value;\n  }\n  function rfc2231getparam(contentDispositionStr) {\n    const matches = [];\n    let match;\n    const iter = toParamRegExp(\"filename\\\\*((?!0\\\\d)\\\\d+)(\\\\*?)\", \"ig\");\n    while ((match = iter.exec(contentDispositionStr)) !== null) {\n      let [, n, quot, part] = match;\n      n = parseInt(n, 10);\n      if (n in matches) {\n        if (n === 0) {\n          break;\n        }\n        continue;\n      }\n      matches[n] = [quot, part];\n    }\n    const parts = [];\n    for (let n = 0; n < matches.length; ++n) {\n      if (!(n in matches)) {\n        break;\n      }\n      let [quot, part] = matches[n];\n      part = rfc2616unquote(part);\n      if (quot) {\n        part = unescape(part);\n        if (n === 0) {\n          part = rfc5987decode(part);\n        }\n      }\n      parts.push(part);\n    }\n    return parts.join(\"\");\n  }\n  function rfc2616unquote(value) {\n    if (value.startsWith('\"')) {\n      const parts = value.slice(1).split('\\\\\"');\n      for (let i = 0; i < parts.length; ++i) {\n        const quotindex = parts[i].indexOf('\"');\n        if (quotindex !== -1) {\n          parts[i] = parts[i].slice(0, quotindex);\n          parts.length = i + 1;\n        }\n        parts[i] = parts[i].replaceAll(/\\\\(.)/g, \"$1\");\n      }\n      value = parts.join('\"');\n    }\n    return value;\n  }\n  function rfc5987decode(extvalue) {\n    const encodingend = extvalue.indexOf(\"'\");\n    if (encodingend === -1) {\n      return extvalue;\n    }\n    const encoding = extvalue.slice(0, encodingend);\n    const langvalue = extvalue.slice(encodingend + 1);\n    const value = langvalue.replace(/^[^']*'/, \"\");\n    return textdecode(encoding, value);\n  }\n  function rfc2047decode(value) {\n    if (!value.startsWith(\"=?\") || /[\\x00-\\x19\\x80-\\xff]/.test(value)) {\n      return value;\n    }\n    return value.replaceAll(/=\\?([\\w-]*)\\?([QqBb])\\?((?:[^?]|\\?(?!=))*)\\?=/g, function (matches, charset, encoding, text) {\n      if (encoding === \"q\" || encoding === \"Q\") {\n        text = text.replaceAll(\"_\", \" \");\n        text = text.replaceAll(/=([0-9a-fA-F]{2})/g, function (match, hex) {\n          return String.fromCharCode(parseInt(hex, 16));\n        });\n        return textdecode(charset, text);\n      }\n      try {\n        text = atob(text);\n      } catch {}\n      return textdecode(charset, text);\n    });\n  }\n  return \"\";\n}\n\n// EXTERNAL MODULE: ./src/display/display_utils.js\nvar display_utils = __webpack_require__(473);\n;// CONCATENATED MODULE: ./src/display/network_utils.js\n\n\n\nfunction validateRangeRequestCapabilities({\n  getResponseHeader,\n  isHttp,\n  rangeChunkSize,\n  disableRange\n}) {\n  const returnValues = {\n    allowRangeRequests: false,\n    suggestedLength: undefined\n  };\n  const length = parseInt(getResponseHeader(\"Content-Length\"), 10);\n  if (!Number.isInteger(length)) {\n    return returnValues;\n  }\n  returnValues.suggestedLength = length;\n  if (length <= 2 * rangeChunkSize) {\n    return returnValues;\n  }\n  if (disableRange || !isHttp) {\n    return returnValues;\n  }\n  if (getResponseHeader(\"Accept-Ranges\") !== \"bytes\") {\n    return returnValues;\n  }\n  const contentEncoding = getResponseHeader(\"Content-Encoding\") || \"identity\";\n  if (contentEncoding !== \"identity\") {\n    return returnValues;\n  }\n  returnValues.allowRangeRequests = true;\n  return returnValues;\n}\nfunction extractFilenameFromHeader(getResponseHeader) {\n  const contentDisposition = getResponseHeader(\"Content-Disposition\");\n  if (contentDisposition) {\n    let filename = getFilenameFromContentDispositionHeader(contentDisposition);\n    if (filename.includes(\"%\")) {\n      try {\n        filename = decodeURIComponent(filename);\n      } catch {}\n    }\n    if ((0,display_utils.isPdfFile)(filename)) {\n      return filename;\n    }\n  }\n  return null;\n}\nfunction createResponseStatusError(status, url) {\n  if (status === 404 || status === 0 && url.startsWith(\"file:\")) {\n    return new util.MissingPDFException('Missing PDF \"' + url + '\".');\n  }\n  return new util.UnexpectedResponseException(`Unexpected server response (${status}) while retrieving PDF \"${url}\".`, status);\n}\nfunction validateResponseStatus(status) {\n  return status === 200 || status === 206;\n}\n\n\n/***/ }),\n\n/***/ 498:\n/***/ ((__webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n__webpack_require__.a(__webpack_module__, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try {\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   PDFNodeStream: () => (/* binding */ PDFNodeStream)\n/* harmony export */ });\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n/* harmony import */ var _network_utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(253);\n\n\n;\nlet fs, http, https, url;\nif (_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.isNodeJS) {\n  fs = await import(/* webpackIgnore: true */ \"fs\");\n  http = await import(/* webpackIgnore: true */ \"http\");\n  https = await import(/* webpackIgnore: true */ \"https\");\n  url = await import(/* webpackIgnore: true */ \"url\");\n}\nconst fileUriRegex = /^file:\\/\\/\\/[a-zA-Z]:\\//;\nfunction parseUrl(sourceUrl) {\n  const parsedUrl = url.parse(sourceUrl);\n  if (parsedUrl.protocol === \"file:\" || parsedUrl.host) {\n    return parsedUrl;\n  }\n  if (/^[a-z]:[/\\\\]/i.test(sourceUrl)) {\n    return url.parse(`file:///${sourceUrl}`);\n  }\n  if (!parsedUrl.host) {\n    parsedUrl.protocol = \"file:\";\n  }\n  return parsedUrl;\n}\nclass PDFNodeStream {\n  constructor(source) {\n    this.source = source;\n    this.url = parseUrl(source.url);\n    this.isHttp = this.url.protocol === \"http:\" || this.url.protocol === \"https:\";\n    this.isFsUrl = this.url.protocol === \"file:\";\n    this.httpHeaders = this.isHttp && source.httpHeaders || {};\n    this._fullRequestReader = null;\n    this._rangeRequestReaders = [];\n  }\n  get _progressiveDataLength() {\n    return this._fullRequestReader?._loaded ?? 0;\n  }\n  getFullReader() {\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(!this._fullRequestReader, \"PDFNodeStream.getFullReader can only be called once.\");\n    this._fullRequestReader = this.isFsUrl ? new PDFNodeStreamFsFullReader(this) : new PDFNodeStreamFullReader(this);\n    return this._fullRequestReader;\n  }\n  getRangeReader(start, end) {\n    if (end <= this._progressiveDataLength) {\n      return null;\n    }\n    const rangeReader = this.isFsUrl ? new PDFNodeStreamFsRangeReader(this, start, end) : new PDFNodeStreamRangeReader(this, start, end);\n    this._rangeRequestReaders.push(rangeReader);\n    return rangeReader;\n  }\n  cancelAllRequests(reason) {\n    this._fullRequestReader?.cancel(reason);\n    for (const reader of this._rangeRequestReaders.slice(0)) {\n      reader.cancel(reason);\n    }\n  }\n}\nclass BaseFullReader {\n  constructor(stream) {\n    this._url = stream.url;\n    this._done = false;\n    this._storedError = null;\n    this.onProgress = null;\n    const source = stream.source;\n    this._contentLength = source.length;\n    this._loaded = 0;\n    this._filename = null;\n    this._disableRange = source.disableRange || false;\n    this._rangeChunkSize = source.rangeChunkSize;\n    if (!this._rangeChunkSize && !this._disableRange) {\n      this._disableRange = true;\n    }\n    this._isStreamingSupported = !source.disableStream;\n    this._isRangeSupported = !source.disableRange;\n    this._readableStream = null;\n    this._readCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this._headersCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n  }\n  get headersReady() {\n    return this._headersCapability.promise;\n  }\n  get filename() {\n    return this._filename;\n  }\n  get contentLength() {\n    return this._contentLength;\n  }\n  get isRangeSupported() {\n    return this._isRangeSupported;\n  }\n  get isStreamingSupported() {\n    return this._isStreamingSupported;\n  }\n  async read() {\n    await this._readCapability.promise;\n    if (this._done) {\n      return {\n        value: undefined,\n        done: true\n      };\n    }\n    if (this._storedError) {\n      throw this._storedError;\n    }\n    const chunk = this._readableStream.read();\n    if (chunk === null) {\n      this._readCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n      return this.read();\n    }\n    this._loaded += chunk.length;\n    this.onProgress?.({\n      loaded: this._loaded,\n      total: this._contentLength\n    });\n    const buffer = new Uint8Array(chunk).buffer;\n    return {\n      value: buffer,\n      done: false\n    };\n  }\n  cancel(reason) {\n    if (!this._readableStream) {\n      this._error(reason);\n      return;\n    }\n    this._readableStream.destroy(reason);\n  }\n  _error(reason) {\n    this._storedError = reason;\n    this._readCapability.resolve();\n  }\n  _setReadableStream(readableStream) {\n    this._readableStream = readableStream;\n    readableStream.on(\"readable\", () => {\n      this._readCapability.resolve();\n    });\n    readableStream.on(\"end\", () => {\n      readableStream.destroy();\n      this._done = true;\n      this._readCapability.resolve();\n    });\n    readableStream.on(\"error\", reason => {\n      this._error(reason);\n    });\n    if (!this._isStreamingSupported && this._isRangeSupported) {\n      this._error(new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AbortException(\"streaming is disabled\"));\n    }\n    if (this._storedError) {\n      this._readableStream.destroy(this._storedError);\n    }\n  }\n}\nclass BaseRangeReader {\n  constructor(stream) {\n    this._url = stream.url;\n    this._done = false;\n    this._storedError = null;\n    this.onProgress = null;\n    this._loaded = 0;\n    this._readableStream = null;\n    this._readCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    const source = stream.source;\n    this._isStreamingSupported = !source.disableStream;\n  }\n  get isStreamingSupported() {\n    return this._isStreamingSupported;\n  }\n  async read() {\n    await this._readCapability.promise;\n    if (this._done) {\n      return {\n        value: undefined,\n        done: true\n      };\n    }\n    if (this._storedError) {\n      throw this._storedError;\n    }\n    const chunk = this._readableStream.read();\n    if (chunk === null) {\n      this._readCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n      return this.read();\n    }\n    this._loaded += chunk.length;\n    this.onProgress?.({\n      loaded: this._loaded\n    });\n    const buffer = new Uint8Array(chunk).buffer;\n    return {\n      value: buffer,\n      done: false\n    };\n  }\n  cancel(reason) {\n    if (!this._readableStream) {\n      this._error(reason);\n      return;\n    }\n    this._readableStream.destroy(reason);\n  }\n  _error(reason) {\n    this._storedError = reason;\n    this._readCapability.resolve();\n  }\n  _setReadableStream(readableStream) {\n    this._readableStream = readableStream;\n    readableStream.on(\"readable\", () => {\n      this._readCapability.resolve();\n    });\n    readableStream.on(\"end\", () => {\n      readableStream.destroy();\n      this._done = true;\n      this._readCapability.resolve();\n    });\n    readableStream.on(\"error\", reason => {\n      this._error(reason);\n    });\n    if (this._storedError) {\n      this._readableStream.destroy(this._storedError);\n    }\n  }\n}\nfunction createRequestOptions(parsedUrl, headers) {\n  return {\n    protocol: parsedUrl.protocol,\n    auth: parsedUrl.auth,\n    host: parsedUrl.hostname,\n    port: parsedUrl.port,\n    path: parsedUrl.path,\n    method: \"GET\",\n    headers\n  };\n}\nclass PDFNodeStreamFullReader extends BaseFullReader {\n  constructor(stream) {\n    super(stream);\n    const handleResponse = response => {\n      if (response.statusCode === 404) {\n        const error = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.MissingPDFException(`Missing PDF \"${this._url}\".`);\n        this._storedError = error;\n        this._headersCapability.reject(error);\n        return;\n      }\n      this._headersCapability.resolve();\n      this._setReadableStream(response);\n      const getResponseHeader = name => {\n        return this._readableStream.headers[name.toLowerCase()];\n      };\n      const {\n        allowRangeRequests,\n        suggestedLength\n      } = (0,_network_utils_js__WEBPACK_IMPORTED_MODULE_1__.validateRangeRequestCapabilities)({\n        getResponseHeader,\n        isHttp: stream.isHttp,\n        rangeChunkSize: this._rangeChunkSize,\n        disableRange: this._disableRange\n      });\n      this._isRangeSupported = allowRangeRequests;\n      this._contentLength = suggestedLength || this._contentLength;\n      this._filename = (0,_network_utils_js__WEBPACK_IMPORTED_MODULE_1__.extractFilenameFromHeader)(getResponseHeader);\n    };\n    this._request = null;\n    if (this._url.protocol === \"http:\") {\n      this._request = http.request(createRequestOptions(this._url, stream.httpHeaders), handleResponse);\n    } else {\n      this._request = https.request(createRequestOptions(this._url, stream.httpHeaders), handleResponse);\n    }\n    this._request.on(\"error\", reason => {\n      this._storedError = reason;\n      this._headersCapability.reject(reason);\n    });\n    this._request.end();\n  }\n}\nclass PDFNodeStreamRangeReader extends BaseRangeReader {\n  constructor(stream, start, end) {\n    super(stream);\n    this._httpHeaders = {};\n    for (const property in stream.httpHeaders) {\n      const value = stream.httpHeaders[property];\n      if (value === undefined) {\n        continue;\n      }\n      this._httpHeaders[property] = value;\n    }\n    this._httpHeaders.Range = `bytes=${start}-${end - 1}`;\n    const handleResponse = response => {\n      if (response.statusCode === 404) {\n        const error = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.MissingPDFException(`Missing PDF \"${this._url}\".`);\n        this._storedError = error;\n        return;\n      }\n      this._setReadableStream(response);\n    };\n    this._request = null;\n    if (this._url.protocol === \"http:\") {\n      this._request = http.request(createRequestOptions(this._url, this._httpHeaders), handleResponse);\n    } else {\n      this._request = https.request(createRequestOptions(this._url, this._httpHeaders), handleResponse);\n    }\n    this._request.on(\"error\", reason => {\n      this._storedError = reason;\n    });\n    this._request.end();\n  }\n}\nclass PDFNodeStreamFsFullReader extends BaseFullReader {\n  constructor(stream) {\n    super(stream);\n    let path = decodeURIComponent(this._url.path);\n    if (fileUriRegex.test(this._url.href)) {\n      path = path.replace(/^\\//, \"\");\n    }\n    fs.lstat(path, (error, stat) => {\n      if (error) {\n        if (error.code === \"ENOENT\") {\n          error = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.MissingPDFException(`Missing PDF \"${path}\".`);\n        }\n        this._storedError = error;\n        this._headersCapability.reject(error);\n        return;\n      }\n      this._contentLength = stat.size;\n      this._setReadableStream(fs.createReadStream(path));\n      this._headersCapability.resolve();\n    });\n  }\n}\nclass PDFNodeStreamFsRangeReader extends BaseRangeReader {\n  constructor(stream, start, end) {\n    super(stream);\n    let path = decodeURIComponent(this._url.path);\n    if (fileUriRegex.test(this._url.href)) {\n      path = path.replace(/^\\//, \"\");\n    }\n    this._setReadableStream(fs.createReadStream(path, {\n      start,\n      end: end - 1\n    }));\n  }\n}\n\n__webpack_async_result__();\n} catch(e) { __webpack_async_result__(e); } }, 1);\n\n/***/ }),\n\n/***/ 738:\n/***/ ((__webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n__webpack_require__.a(__webpack_module__, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try {\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   NodeCMapReaderFactory: () => (/* binding */ NodeCMapReaderFactory),\n/* harmony export */   NodeCanvasFactory: () => (/* binding */ NodeCanvasFactory),\n/* harmony export */   NodeFilterFactory: () => (/* binding */ NodeFilterFactory),\n/* harmony export */   NodeStandardFontDataFactory: () => (/* binding */ NodeStandardFontDataFactory)\n/* harmony export */ });\n/* harmony import */ var _base_factory_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(822);\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(266);\n\n\n;\nlet fs, canvas, path2d_polyfill;\nif (_shared_util_js__WEBPACK_IMPORTED_MODULE_1__.isNodeJS) {\n  fs = await import(/* webpackIgnore: true */ \"fs\");\n  try {\n    canvas = await import(/* webpackIgnore: true */ \"canvas\");\n  } catch {}\n  try {\n    path2d_polyfill = await import(/* webpackIgnore: true */ \"path2d-polyfill\");\n  } catch {}\n}\n;\nconst fetchData = function (url) {\n  return new Promise((resolve, reject) => {\n    fs.readFile(url, (error, data) => {\n      if (error || !data) {\n        reject(new Error(error));\n        return;\n      }\n      resolve(new Uint8Array(data));\n    });\n  });\n};\nclass NodeFilterFactory extends _base_factory_js__WEBPACK_IMPORTED_MODULE_0__.BaseFilterFactory {}\nclass NodeCanvasFactory extends _base_factory_js__WEBPACK_IMPORTED_MODULE_0__.BaseCanvasFactory {\n  _createCanvas(width, height) {\n    return canvas.createCanvas(width, height);\n  }\n}\nclass NodeCMapReaderFactory extends _base_factory_js__WEBPACK_IMPORTED_MODULE_0__.BaseCMapReaderFactory {\n  _fetchData(url, compressionType) {\n    return fetchData(url).then(data => {\n      return {\n        cMapData: data,\n        compressionType\n      };\n    });\n  }\n}\nclass NodeStandardFontDataFactory extends _base_factory_js__WEBPACK_IMPORTED_MODULE_0__.BaseStandardFontDataFactory {\n  _fetchData(url) {\n    return fetchData(url);\n  }\n}\n\n__webpack_async_result__();\n} catch(e) { __webpack_async_result__(e); } }, 1);\n\n/***/ }),\n\n/***/ 890:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   OptionalContentConfig: () => (/* binding */ OptionalContentConfig)\n/* harmony export */ });\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n/* harmony import */ var _shared_murmurhash3_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(825);\n\n\nconst INTERNAL = Symbol(\"INTERNAL\");\nclass OptionalContentGroup {\n  #visible = true;\n  constructor(name, intent) {\n    this.name = name;\n    this.intent = intent;\n  }\n  get visible() {\n    return this.#visible;\n  }\n  _setVisible(internal, visible) {\n    if (internal !== INTERNAL) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)(\"Internal method `_setVisible` called.\");\n    }\n    this.#visible = visible;\n  }\n}\nclass OptionalContentConfig {\n  #cachedGetHash = null;\n  #groups = new Map();\n  #initialHash = null;\n  #order = null;\n  constructor(data) {\n    this.name = null;\n    this.creator = null;\n    if (data === null) {\n      return;\n    }\n    this.name = data.name;\n    this.creator = data.creator;\n    this.#order = data.order;\n    for (const group of data.groups) {\n      this.#groups.set(group.id, new OptionalContentGroup(group.name, group.intent));\n    }\n    if (data.baseState === \"OFF\") {\n      for (const group of this.#groups.values()) {\n        group._setVisible(INTERNAL, false);\n      }\n    }\n    for (const on of data.on) {\n      this.#groups.get(on)._setVisible(INTERNAL, true);\n    }\n    for (const off of data.off) {\n      this.#groups.get(off)._setVisible(INTERNAL, false);\n    }\n    this.#initialHash = this.getHash();\n  }\n  #evaluateVisibilityExpression(array) {\n    const length = array.length;\n    if (length < 2) {\n      return true;\n    }\n    const operator = array[0];\n    for (let i = 1; i < length; i++) {\n      const element = array[i];\n      let state;\n      if (Array.isArray(element)) {\n        state = this.#evaluateVisibilityExpression(element);\n      } else if (this.#groups.has(element)) {\n        state = this.#groups.get(element).visible;\n      } else {\n        (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Optional content group not found: ${element}`);\n        return true;\n      }\n      switch (operator) {\n        case \"And\":\n          if (!state) {\n            return false;\n          }\n          break;\n        case \"Or\":\n          if (state) {\n            return true;\n          }\n          break;\n        case \"Not\":\n          return !state;\n        default:\n          return true;\n      }\n    }\n    return operator === \"And\";\n  }\n  isVisible(group) {\n    if (this.#groups.size === 0) {\n      return true;\n    }\n    if (!group) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(\"Optional content group not defined.\");\n      return true;\n    }\n    if (group.type === \"OCG\") {\n      if (!this.#groups.has(group.id)) {\n        (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Optional content group not found: ${group.id}`);\n        return true;\n      }\n      return this.#groups.get(group.id).visible;\n    } else if (group.type === \"OCMD\") {\n      if (group.expression) {\n        return this.#evaluateVisibilityExpression(group.expression);\n      }\n      if (!group.policy || group.policy === \"AnyOn\") {\n        for (const id of group.ids) {\n          if (!this.#groups.has(id)) {\n            (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Optional content group not found: ${id}`);\n            return true;\n          }\n          if (this.#groups.get(id).visible) {\n            return true;\n          }\n        }\n        return false;\n      } else if (group.policy === \"AllOn\") {\n        for (const id of group.ids) {\n          if (!this.#groups.has(id)) {\n            (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Optional content group not found: ${id}`);\n            return true;\n          }\n          if (!this.#groups.get(id).visible) {\n            return false;\n          }\n        }\n        return true;\n      } else if (group.policy === \"AnyOff\") {\n        for (const id of group.ids) {\n          if (!this.#groups.has(id)) {\n            (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Optional content group not found: ${id}`);\n            return true;\n          }\n          if (!this.#groups.get(id).visible) {\n            return true;\n          }\n        }\n        return false;\n      } else if (group.policy === \"AllOff\") {\n        for (const id of group.ids) {\n          if (!this.#groups.has(id)) {\n            (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Optional content group not found: ${id}`);\n            return true;\n          }\n          if (this.#groups.get(id).visible) {\n            return false;\n          }\n        }\n        return true;\n      }\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Unknown optional content policy ${group.policy}.`);\n      return true;\n    }\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Unknown group type ${group.type}.`);\n    return true;\n  }\n  setVisibility(id, visible = true) {\n    if (!this.#groups.has(id)) {\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Optional content group not found: ${id}`);\n      return;\n    }\n    this.#groups.get(id)._setVisible(INTERNAL, !!visible);\n    this.#cachedGetHash = null;\n  }\n  get hasInitialVisibility() {\n    return this.#initialHash === null || this.getHash() === this.#initialHash;\n  }\n  getOrder() {\n    if (!this.#groups.size) {\n      return null;\n    }\n    if (this.#order) {\n      return this.#order.slice();\n    }\n    return [...this.#groups.keys()];\n  }\n  getGroups() {\n    return this.#groups.size > 0 ? (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.objectFromMap)(this.#groups) : null;\n  }\n  getGroup(id) {\n    return this.#groups.get(id) || null;\n  }\n  getHash() {\n    if (this.#cachedGetHash !== null) {\n      return this.#cachedGetHash;\n    }\n    const hash = new _shared_murmurhash3_js__WEBPACK_IMPORTED_MODULE_1__.MurmurHash3_64();\n    for (const [id, group] of this.#groups) {\n      hash.update(`${id}:${group.visible}`);\n    }\n    return this.#cachedGetHash = hash.hexdigest();\n  }\n}\n\n\n/***/ }),\n\n/***/ 739:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   renderTextLayer: () => (/* binding */ renderTextLayer),\n/* harmony export */   updateTextLayer: () => (/* binding */ updateTextLayer)\n/* harmony export */ });\n/* unused harmony export TextLayerRenderTask */\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n/* harmony import */ var _display_utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(473);\n\n\nconst MAX_TEXT_DIVS_TO_RENDER = 100000;\nconst DEFAULT_FONT_SIZE = 30;\nconst DEFAULT_FONT_ASCENT = 0.8;\nconst ascentCache = new Map();\nfunction getCtx(size, isOffscreenCanvasSupported) {\n  let ctx;\n  if (isOffscreenCanvasSupported && _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.FeatureTest.isOffscreenCanvasSupported) {\n    ctx = new OffscreenCanvas(size, size).getContext(\"2d\", {\n      alpha: false\n    });\n  } else {\n    const canvas = document.createElement(\"canvas\");\n    canvas.width = canvas.height = size;\n    ctx = canvas.getContext(\"2d\", {\n      alpha: false\n    });\n  }\n  return ctx;\n}\nfunction getAscent(fontFamily, isOffscreenCanvasSupported) {\n  const cachedAscent = ascentCache.get(fontFamily);\n  if (cachedAscent) {\n    return cachedAscent;\n  }\n  const ctx = getCtx(DEFAULT_FONT_SIZE, isOffscreenCanvasSupported);\n  ctx.font = `${DEFAULT_FONT_SIZE}px ${fontFamily}`;\n  const metrics = ctx.measureText(\"\");\n  let ascent = metrics.fontBoundingBoxAscent;\n  let descent = Math.abs(metrics.fontBoundingBoxDescent);\n  if (ascent) {\n    const ratio = ascent / (ascent + descent);\n    ascentCache.set(fontFamily, ratio);\n    ctx.canvas.width = ctx.canvas.height = 0;\n    return ratio;\n  }\n  ctx.strokeStyle = \"red\";\n  ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE);\n  ctx.strokeText(\"g\", 0, 0);\n  let pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE).data;\n  descent = 0;\n  for (let i = pixels.length - 1 - 3; i >= 0; i -= 4) {\n    if (pixels[i] > 0) {\n      descent = Math.ceil(i / 4 / DEFAULT_FONT_SIZE);\n      break;\n    }\n  }\n  ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE);\n  ctx.strokeText(\"A\", 0, DEFAULT_FONT_SIZE);\n  pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE).data;\n  ascent = 0;\n  for (let i = 0, ii = pixels.length; i < ii; i += 4) {\n    if (pixels[i] > 0) {\n      ascent = DEFAULT_FONT_SIZE - Math.floor(i / 4 / DEFAULT_FONT_SIZE);\n      break;\n    }\n  }\n  ctx.canvas.width = ctx.canvas.height = 0;\n  if (ascent) {\n    const ratio = ascent / (ascent + descent);\n    ascentCache.set(fontFamily, ratio);\n    return ratio;\n  }\n  ascentCache.set(fontFamily, DEFAULT_FONT_ASCENT);\n  return DEFAULT_FONT_ASCENT;\n}\nfunction appendText(task, geom, styles) {\n  const textDiv = document.createElement(\"span\");\n  const textDivProperties = {\n    angle: 0,\n    canvasWidth: 0,\n    hasText: geom.str !== \"\",\n    hasEOL: geom.hasEOL,\n    fontSize: 0\n  };\n  task._textDivs.push(textDiv);\n  const tx = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.Util.transform(task._transform, geom.transform);\n  let angle = Math.atan2(tx[1], tx[0]);\n  const style = styles[geom.fontName];\n  if (style.vertical) {\n    angle += Math.PI / 2;\n  }\n  const fontFamily = task._fontInspectorEnabled && style.fontSubstitution || style.fontFamily;\n  const fontHeight = Math.hypot(tx[2], tx[3]);\n  const fontAscent = fontHeight * getAscent(fontFamily, task._isOffscreenCanvasSupported);\n  let left, top;\n  if (angle === 0) {\n    left = tx[4];\n    top = tx[5] - fontAscent;\n  } else {\n    left = tx[4] + fontAscent * Math.sin(angle);\n    top = tx[5] - fontAscent * Math.cos(angle);\n  }\n  const scaleFactorStr = \"calc(var(--scale-factor)*\";\n  const divStyle = textDiv.style;\n  if (task._container === task._rootContainer) {\n    divStyle.left = `${(100 * left / task._pageWidth).toFixed(2)}%`;\n    divStyle.top = `${(100 * top / task._pageHeight).toFixed(2)}%`;\n  } else {\n    divStyle.left = `${scaleFactorStr}${left.toFixed(2)}px)`;\n    divStyle.top = `${scaleFactorStr}${top.toFixed(2)}px)`;\n  }\n  divStyle.fontSize = `${scaleFactorStr}${fontHeight.toFixed(2)}px)`;\n  divStyle.fontFamily = fontFamily;\n  textDivProperties.fontSize = fontHeight;\n  textDiv.setAttribute(\"role\", \"presentation\");\n  textDiv.textContent = geom.str;\n  textDiv.dir = geom.dir;\n  if (task._fontInspectorEnabled) {\n    textDiv.dataset.fontName = style.fontSubstitutionLoadedName || geom.fontName;\n  }\n  if (angle !== 0) {\n    textDivProperties.angle = angle * (180 / Math.PI);\n  }\n  let shouldScaleText = false;\n  if (geom.str.length > 1) {\n    shouldScaleText = true;\n  } else if (geom.str !== \" \" && geom.transform[0] !== geom.transform[3]) {\n    const absScaleX = Math.abs(geom.transform[0]),\n      absScaleY = Math.abs(geom.transform[3]);\n    if (absScaleX !== absScaleY && Math.max(absScaleX, absScaleY) / Math.min(absScaleX, absScaleY) > 1.5) {\n      shouldScaleText = true;\n    }\n  }\n  if (shouldScaleText) {\n    textDivProperties.canvasWidth = style.vertical ? geom.height : geom.width;\n  }\n  task._textDivProperties.set(textDiv, textDivProperties);\n  if (task._isReadableStream) {\n    task._layoutText(textDiv);\n  }\n}\nfunction layout(params) {\n  const {\n    div,\n    scale,\n    properties,\n    ctx,\n    prevFontSize,\n    prevFontFamily\n  } = params;\n  const {\n    style\n  } = div;\n  let transform = \"\";\n  if (properties.canvasWidth !== 0 && properties.hasText) {\n    const {\n      fontFamily\n    } = style;\n    const {\n      canvasWidth,\n      fontSize\n    } = properties;\n    if (prevFontSize !== fontSize || prevFontFamily !== fontFamily) {\n      ctx.font = `${fontSize * scale}px ${fontFamily}`;\n      params.prevFontSize = fontSize;\n      params.prevFontFamily = fontFamily;\n    }\n    const {\n      width\n    } = ctx.measureText(div.textContent);\n    if (width > 0) {\n      transform = `scaleX(${canvasWidth * scale / width})`;\n    }\n  }\n  if (properties.angle !== 0) {\n    transform = `rotate(${properties.angle}deg) ${transform}`;\n  }\n  if (transform.length > 0) {\n    style.transform = transform;\n  }\n}\nfunction render(task) {\n  if (task._canceled) {\n    return;\n  }\n  const textDivs = task._textDivs;\n  const capability = task._capability;\n  const textDivsLength = textDivs.length;\n  if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) {\n    capability.resolve();\n    return;\n  }\n  if (!task._isReadableStream) {\n    for (const textDiv of textDivs) {\n      task._layoutText(textDiv);\n    }\n  }\n  capability.resolve();\n}\nclass TextLayerRenderTask {\n  constructor({\n    textContentSource,\n    container,\n    viewport,\n    textDivs,\n    textDivProperties,\n    textContentItemsStr,\n    isOffscreenCanvasSupported\n  }) {\n    this._textContentSource = textContentSource;\n    this._isReadableStream = textContentSource instanceof ReadableStream;\n    this._container = this._rootContainer = container;\n    this._textDivs = textDivs || [];\n    this._textContentItemsStr = textContentItemsStr || [];\n    this._isOffscreenCanvasSupported = isOffscreenCanvasSupported;\n    this._fontInspectorEnabled = !!globalThis.FontInspector?.enabled;\n    this._reader = null;\n    this._textDivProperties = textDivProperties || new WeakMap();\n    this._canceled = false;\n    this._capability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this._layoutTextParams = {\n      prevFontSize: null,\n      prevFontFamily: null,\n      div: null,\n      scale: viewport.scale * (globalThis.devicePixelRatio || 1),\n      properties: null,\n      ctx: getCtx(0, isOffscreenCanvasSupported)\n    };\n    const {\n      pageWidth,\n      pageHeight,\n      pageX,\n      pageY\n    } = viewport.rawDims;\n    this._transform = [1, 0, 0, -1, -pageX, pageY + pageHeight];\n    this._pageWidth = pageWidth;\n    this._pageHeight = pageHeight;\n    (0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.setLayerDimensions)(container, viewport);\n    this._capability.promise.finally(() => {\n      this._layoutTextParams = null;\n    }).catch(() => {});\n  }\n  get promise() {\n    return this._capability.promise;\n  }\n  cancel() {\n    this._canceled = true;\n    if (this._reader) {\n      this._reader.cancel(new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AbortException(\"TextLayer task cancelled.\")).catch(() => {});\n      this._reader = null;\n    }\n    this._capability.reject(new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AbortException(\"TextLayer task cancelled.\"));\n  }\n  _processItems(items, styleCache) {\n    for (const item of items) {\n      if (item.str === undefined) {\n        if (item.type === \"beginMarkedContentProps\" || item.type === \"beginMarkedContent\") {\n          const parent = this._container;\n          this._container = document.createElement(\"span\");\n          this._container.classList.add(\"markedContent\");\n          if (item.id !== null) {\n            this._container.setAttribute(\"id\", `${item.id}`);\n          }\n          parent.append(this._container);\n        } else if (item.type === \"endMarkedContent\") {\n          this._container = this._container.parentNode;\n        }\n        continue;\n      }\n      this._textContentItemsStr.push(item.str);\n      appendText(this, item, styleCache);\n    }\n  }\n  _layoutText(textDiv) {\n    const textDivProperties = this._layoutTextParams.properties = this._textDivProperties.get(textDiv);\n    this._layoutTextParams.div = textDiv;\n    layout(this._layoutTextParams);\n    if (textDivProperties.hasText) {\n      this._container.append(textDiv);\n    }\n    if (textDivProperties.hasEOL) {\n      const br = document.createElement(\"br\");\n      br.setAttribute(\"role\", \"presentation\");\n      this._container.append(br);\n    }\n  }\n  _render() {\n    const capability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    let styleCache = Object.create(null);\n    if (this._isReadableStream) {\n      const pump = () => {\n        this._reader.read().then(({\n          value,\n          done\n        }) => {\n          if (done) {\n            capability.resolve();\n            return;\n          }\n          Object.assign(styleCache, value.styles);\n          this._processItems(value.items, styleCache);\n          pump();\n        }, capability.reject);\n      };\n      this._reader = this._textContentSource.getReader();\n      pump();\n    } else if (this._textContentSource) {\n      const {\n        items,\n        styles\n      } = this._textContentSource;\n      this._processItems(items, styles);\n      capability.resolve();\n    } else {\n      throw new Error('No \"textContentSource\" parameter specified.');\n    }\n    capability.promise.then(() => {\n      styleCache = null;\n      render(this);\n    }, this._capability.reject);\n  }\n}\nfunction renderTextLayer(params) {\n  const task = new TextLayerRenderTask(params);\n  task._render();\n  return task;\n}\nfunction updateTextLayer({\n  container,\n  viewport,\n  textDivs,\n  textDivProperties,\n  isOffscreenCanvasSupported,\n  mustRotate = true,\n  mustRescale = true\n}) {\n  if (mustRotate) {\n    (0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.setLayerDimensions)(container, {\n      rotation: viewport.rotation\n    });\n  }\n  if (mustRescale) {\n    const ctx = getCtx(0, isOffscreenCanvasSupported);\n    const scale = viewport.scale * (globalThis.devicePixelRatio || 1);\n    const params = {\n      prevFontSize: null,\n      prevFontFamily: null,\n      div: null,\n      scale,\n      properties: null,\n      ctx\n    };\n    for (const div of textDivs) {\n      params.properties = textDivProperties.get(div);\n      params.div = div;\n      layout(params);\n    }\n  }\n}\n\n\n/***/ }),\n\n/***/ 92:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   PDFDataTransportStream: () => (/* binding */ PDFDataTransportStream)\n/* harmony export */ });\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n/* harmony import */ var _display_utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(473);\n\n\nclass PDFDataTransportStream {\n  constructor({\n    length,\n    initialData,\n    progressiveDone = false,\n    contentDispositionFilename = null,\n    disableRange = false,\n    disableStream = false\n  }, pdfDataRangeTransport) {\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(pdfDataRangeTransport, 'PDFDataTransportStream - missing required \"pdfDataRangeTransport\" argument.');\n    this._queuedChunks = [];\n    this._progressiveDone = progressiveDone;\n    this._contentDispositionFilename = contentDispositionFilename;\n    if (initialData?.length > 0) {\n      const buffer = initialData instanceof Uint8Array && initialData.byteLength === initialData.buffer.byteLength ? initialData.buffer : new Uint8Array(initialData).buffer;\n      this._queuedChunks.push(buffer);\n    }\n    this._pdfDataRangeTransport = pdfDataRangeTransport;\n    this._isStreamingSupported = !disableStream;\n    this._isRangeSupported = !disableRange;\n    this._contentLength = length;\n    this._fullRequestReader = null;\n    this._rangeReaders = [];\n    this._pdfDataRangeTransport.addRangeListener((begin, chunk) => {\n      this._onReceiveData({\n        begin,\n        chunk\n      });\n    });\n    this._pdfDataRangeTransport.addProgressListener((loaded, total) => {\n      this._onProgress({\n        loaded,\n        total\n      });\n    });\n    this._pdfDataRangeTransport.addProgressiveReadListener(chunk => {\n      this._onReceiveData({\n        chunk\n      });\n    });\n    this._pdfDataRangeTransport.addProgressiveDoneListener(() => {\n      this._onProgressiveDone();\n    });\n    this._pdfDataRangeTransport.transportReady();\n  }\n  _onReceiveData({\n    begin,\n    chunk\n  }) {\n    const buffer = chunk instanceof Uint8Array && chunk.byteLength === chunk.buffer.byteLength ? chunk.buffer : new Uint8Array(chunk).buffer;\n    if (begin === undefined) {\n      if (this._fullRequestReader) {\n        this._fullRequestReader._enqueue(buffer);\n      } else {\n        this._queuedChunks.push(buffer);\n      }\n    } else {\n      const found = this._rangeReaders.some(function (rangeReader) {\n        if (rangeReader._begin !== begin) {\n          return false;\n        }\n        rangeReader._enqueue(buffer);\n        return true;\n      });\n      (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(found, \"_onReceiveData - no `PDFDataTransportStreamRangeReader` instance found.\");\n    }\n  }\n  get _progressiveDataLength() {\n    return this._fullRequestReader?._loaded ?? 0;\n  }\n  _onProgress(evt) {\n    if (evt.total === undefined) {\n      this._rangeReaders[0]?.onProgress?.({\n        loaded: evt.loaded\n      });\n    } else {\n      this._fullRequestReader?.onProgress?.({\n        loaded: evt.loaded,\n        total: evt.total\n      });\n    }\n  }\n  _onProgressiveDone() {\n    this._fullRequestReader?.progressiveDone();\n    this._progressiveDone = true;\n  }\n  _removeRangeReader(reader) {\n    const i = this._rangeReaders.indexOf(reader);\n    if (i >= 0) {\n      this._rangeReaders.splice(i, 1);\n    }\n  }\n  getFullReader() {\n    (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(!this._fullRequestReader, \"PDFDataTransportStream.getFullReader can only be called once.\");\n    const queuedChunks = this._queuedChunks;\n    this._queuedChunks = null;\n    return new PDFDataTransportStreamReader(this, queuedChunks, this._progressiveDone, this._contentDispositionFilename);\n  }\n  getRangeReader(begin, end) {\n    if (end <= this._progressiveDataLength) {\n      return null;\n    }\n    const reader = new PDFDataTransportStreamRangeReader(this, begin, end);\n    this._pdfDataRangeTransport.requestDataRange(begin, end);\n    this._rangeReaders.push(reader);\n    return reader;\n  }\n  cancelAllRequests(reason) {\n    this._fullRequestReader?.cancel(reason);\n    for (const reader of this._rangeReaders.slice(0)) {\n      reader.cancel(reason);\n    }\n    this._pdfDataRangeTransport.abort();\n  }\n}\nclass PDFDataTransportStreamReader {\n  constructor(stream, queuedChunks, progressiveDone = false, contentDispositionFilename = null) {\n    this._stream = stream;\n    this._done = progressiveDone || false;\n    this._filename = (0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.isPdfFile)(contentDispositionFilename) ? contentDispositionFilename : null;\n    this._queuedChunks = queuedChunks || [];\n    this._loaded = 0;\n    for (const chunk of this._queuedChunks) {\n      this._loaded += chunk.byteLength;\n    }\n    this._requests = [];\n    this._headersReady = Promise.resolve();\n    stream._fullRequestReader = this;\n    this.onProgress = null;\n  }\n  _enqueue(chunk) {\n    if (this._done) {\n      return;\n    }\n    if (this._requests.length > 0) {\n      const requestCapability = this._requests.shift();\n      requestCapability.resolve({\n        value: chunk,\n        done: false\n      });\n    } else {\n      this._queuedChunks.push(chunk);\n    }\n    this._loaded += chunk.byteLength;\n  }\n  get headersReady() {\n    return this._headersReady;\n  }\n  get filename() {\n    return this._filename;\n  }\n  get isRangeSupported() {\n    return this._stream._isRangeSupported;\n  }\n  get isStreamingSupported() {\n    return this._stream._isStreamingSupported;\n  }\n  get contentLength() {\n    return this._stream._contentLength;\n  }\n  async read() {\n    if (this._queuedChunks.length > 0) {\n      const chunk = this._queuedChunks.shift();\n      return {\n        value: chunk,\n        done: false\n      };\n    }\n    if (this._done) {\n      return {\n        value: undefined,\n        done: true\n      };\n    }\n    const requestCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this._requests.push(requestCapability);\n    return requestCapability.promise;\n  }\n  cancel(reason) {\n    this._done = true;\n    for (const requestCapability of this._requests) {\n      requestCapability.resolve({\n        value: undefined,\n        done: true\n      });\n    }\n    this._requests.length = 0;\n  }\n  progressiveDone() {\n    if (this._done) {\n      return;\n    }\n    this._done = true;\n  }\n}\nclass PDFDataTransportStreamRangeReader {\n  constructor(stream, begin, end) {\n    this._stream = stream;\n    this._begin = begin;\n    this._end = end;\n    this._queuedChunk = null;\n    this._requests = [];\n    this._done = false;\n    this.onProgress = null;\n  }\n  _enqueue(chunk) {\n    if (this._done) {\n      return;\n    }\n    if (this._requests.length === 0) {\n      this._queuedChunk = chunk;\n    } else {\n      const requestsCapability = this._requests.shift();\n      requestsCapability.resolve({\n        value: chunk,\n        done: false\n      });\n      for (const requestCapability of this._requests) {\n        requestCapability.resolve({\n          value: undefined,\n          done: true\n        });\n      }\n      this._requests.length = 0;\n    }\n    this._done = true;\n    this._stream._removeRangeReader(this);\n  }\n  get isStreamingSupported() {\n    return false;\n  }\n  async read() {\n    if (this._queuedChunk) {\n      const chunk = this._queuedChunk;\n      this._queuedChunk = null;\n      return {\n        value: chunk,\n        done: false\n      };\n    }\n    if (this._done) {\n      return {\n        value: undefined,\n        done: true\n      };\n    }\n    const requestCapability = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this._requests.push(requestCapability);\n    return requestCapability.promise;\n  }\n  cancel(reason) {\n    this._done = true;\n    for (const requestCapability of this._requests) {\n      requestCapability.resolve({\n        value: undefined,\n        done: true\n      });\n    }\n    this._requests.length = 0;\n    this._stream._removeRangeReader(this);\n  }\n}\n\n\n/***/ }),\n\n/***/ 368:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   GlobalWorkerOptions: () => (/* binding */ GlobalWorkerOptions)\n/* harmony export */ });\nconst GlobalWorkerOptions = Object.create(null);\nGlobalWorkerOptions.workerPort = null;\nGlobalWorkerOptions.workerSrc = \"\";\n\n\n/***/ }),\n\n/***/ 160:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   XfaLayer: () => (/* binding */ XfaLayer)\n/* harmony export */ });\n/* harmony import */ var _xfa_text_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(521);\n\nclass XfaLayer {\n  static setupStorage(html, id, element, storage, intent) {\n    const storedData = storage.getValue(id, {\n      value: null\n    });\n    switch (element.name) {\n      case \"textarea\":\n        if (storedData.value !== null) {\n          html.textContent = storedData.value;\n        }\n        if (intent === \"print\") {\n          break;\n        }\n        html.addEventListener(\"input\", event => {\n          storage.setValue(id, {\n            value: event.target.value\n          });\n        });\n        break;\n      case \"input\":\n        if (element.attributes.type === \"radio\" || element.attributes.type === \"checkbox\") {\n          if (storedData.value === element.attributes.xfaOn) {\n            html.setAttribute(\"checked\", true);\n          } else if (storedData.value === element.attributes.xfaOff) {\n            html.removeAttribute(\"checked\");\n          }\n          if (intent === \"print\") {\n            break;\n          }\n          html.addEventListener(\"change\", event => {\n            storage.setValue(id, {\n              value: event.target.checked ? event.target.getAttribute(\"xfaOn\") : event.target.getAttribute(\"xfaOff\")\n            });\n          });\n        } else {\n          if (storedData.value !== null) {\n            html.setAttribute(\"value\", storedData.value);\n          }\n          if (intent === \"print\") {\n            break;\n          }\n          html.addEventListener(\"input\", event => {\n            storage.setValue(id, {\n              value: event.target.value\n            });\n          });\n        }\n        break;\n      case \"select\":\n        if (storedData.value !== null) {\n          html.setAttribute(\"value\", storedData.value);\n          for (const option of element.children) {\n            if (option.attributes.value === storedData.value) {\n              option.attributes.selected = true;\n            } else if (option.attributes.hasOwnProperty(\"selected\")) {\n              delete option.attributes.selected;\n            }\n          }\n        }\n        html.addEventListener(\"input\", event => {\n          const options = event.target.options;\n          const value = options.selectedIndex === -1 ? \"\" : options[options.selectedIndex].value;\n          storage.setValue(id, {\n            value\n          });\n        });\n        break;\n    }\n  }\n  static setAttributes({\n    html,\n    element,\n    storage = null,\n    intent,\n    linkService\n  }) {\n    const {\n      attributes\n    } = element;\n    const isHTMLAnchorElement = html instanceof HTMLAnchorElement;\n    if (attributes.type === \"radio\") {\n      attributes.name = `${attributes.name}-${intent}`;\n    }\n    for (const [key, value] of Object.entries(attributes)) {\n      if (value === null || value === undefined) {\n        continue;\n      }\n      switch (key) {\n        case \"class\":\n          if (value.length) {\n            html.setAttribute(key, value.join(\" \"));\n          }\n          break;\n        case \"dataId\":\n          break;\n        case \"id\":\n          html.setAttribute(\"data-element-id\", value);\n          break;\n        case \"style\":\n          Object.assign(html.style, value);\n          break;\n        case \"textContent\":\n          html.textContent = value;\n          break;\n        default:\n          if (!isHTMLAnchorElement || key !== \"href\" && key !== \"newWindow\") {\n            html.setAttribute(key, value);\n          }\n      }\n    }\n    if (isHTMLAnchorElement) {\n      linkService.addLinkAttributes(html, attributes.href, attributes.newWindow);\n    }\n    if (storage && attributes.dataId) {\n      this.setupStorage(html, attributes.dataId, element, storage);\n    }\n  }\n  static render(parameters) {\n    const storage = parameters.annotationStorage;\n    const linkService = parameters.linkService;\n    const root = parameters.xfaHtml;\n    const intent = parameters.intent || \"display\";\n    const rootHtml = document.createElement(root.name);\n    if (root.attributes) {\n      this.setAttributes({\n        html: rootHtml,\n        element: root,\n        intent,\n        linkService\n      });\n    }\n    const isNotForRichText = intent !== \"richText\";\n    const rootDiv = parameters.div;\n    rootDiv.append(rootHtml);\n    if (parameters.viewport) {\n      const transform = `matrix(${parameters.viewport.transform.join(\",\")})`;\n      rootDiv.style.transform = transform;\n    }\n    if (isNotForRichText) {\n      rootDiv.setAttribute(\"class\", \"xfaLayer xfaFont\");\n    }\n    const textDivs = [];\n    if (root.children.length === 0) {\n      if (root.value) {\n        const node = document.createTextNode(root.value);\n        rootHtml.append(node);\n        if (isNotForRichText && _xfa_text_js__WEBPACK_IMPORTED_MODULE_0__.XfaText.shouldBuildText(root.name)) {\n          textDivs.push(node);\n        }\n      }\n      return {\n        textDivs\n      };\n    }\n    const stack = [[root, -1, rootHtml]];\n    while (stack.length > 0) {\n      const [parent, i, html] = stack.at(-1);\n      if (i + 1 === parent.children.length) {\n        stack.pop();\n        continue;\n      }\n      const child = parent.children[++stack.at(-1)[1]];\n      if (child === null) {\n        continue;\n      }\n      const {\n        name\n      } = child;\n      if (name === \"#text\") {\n        const node = document.createTextNode(child.value);\n        textDivs.push(node);\n        html.append(node);\n        continue;\n      }\n      const childHtml = child?.attributes?.xmlns ? document.createElementNS(child.attributes.xmlns, name) : document.createElement(name);\n      html.append(childHtml);\n      if (child.attributes) {\n        this.setAttributes({\n          html: childHtml,\n          element: child,\n          storage,\n          intent,\n          linkService\n        });\n      }\n      if (child.children?.length > 0) {\n        stack.push([child, -1, childHtml]);\n      } else if (child.value) {\n        const node = document.createTextNode(child.value);\n        if (isNotForRichText && _xfa_text_js__WEBPACK_IMPORTED_MODULE_0__.XfaText.shouldBuildText(name)) {\n          textDivs.push(node);\n        }\n        childHtml.append(node);\n      }\n    }\n    for (const el of rootDiv.querySelectorAll(\".xfaNonInteractive input, .xfaNonInteractive textarea\")) {\n      el.setAttribute(\"readOnly\", true);\n    }\n    return {\n      textDivs\n    };\n  }\n  static update(parameters) {\n    const transform = `matrix(${parameters.viewport.transform.join(\",\")})`;\n    parameters.div.style.transform = transform;\n    parameters.div.hidden = false;\n  }\n}\n\n\n/***/ }),\n\n/***/ 521:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   XfaText: () => (/* binding */ XfaText)\n/* harmony export */ });\nclass XfaText {\n  static textContent(xfa) {\n    const items = [];\n    const output = {\n      items,\n      styles: Object.create(null)\n    };\n    function walk(node) {\n      if (!node) {\n        return;\n      }\n      let str = null;\n      const name = node.name;\n      if (name === \"#text\") {\n        str = node.value;\n      } else if (!XfaText.shouldBuildText(name)) {\n        return;\n      } else if (node?.attributes?.textContent) {\n        str = node.attributes.textContent;\n      } else if (node.value) {\n        str = node.value;\n      }\n      if (str !== null) {\n        items.push({\n          str\n        });\n      }\n      if (!node.children) {\n        return;\n      }\n      for (const child of node.children) {\n        walk(child);\n      }\n    }\n    walk(xfa);\n    return output;\n  }\n  static shouldBuildText(name) {\n    return !(name === \"textarea\" || name === \"input\" || name === \"option\" || name === \"select\");\n  }\n}\n\n\n/***/ }),\n\n/***/ 907:\n/***/ ((__webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n__webpack_require__.a(__webpack_module__, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try {\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   AbortException: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AbortException),\n/* harmony export */   AnnotationEditorLayer: () => (/* reexport safe */ _display_editor_annotation_editor_layer_js__WEBPACK_IMPORTED_MODULE_4__.AnnotationEditorLayer),\n/* harmony export */   AnnotationEditorParamsType: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorParamsType),\n/* harmony export */   AnnotationEditorType: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType),\n/* harmony export */   AnnotationEditorUIManager: () => (/* reexport safe */ _display_editor_tools_js__WEBPACK_IMPORTED_MODULE_5__.AnnotationEditorUIManager),\n/* harmony export */   AnnotationLayer: () => (/* reexport safe */ _display_annotation_layer_js__WEBPACK_IMPORTED_MODULE_6__.AnnotationLayer),\n/* harmony export */   AnnotationMode: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationMode),\n/* harmony export */   CMapCompressionType: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.CMapCompressionType),\n/* harmony export */   DOMSVGFactory: () => (/* reexport safe */ _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.DOMSVGFactory),\n/* harmony export */   DrawLayer: () => (/* reexport safe */ _display_draw_layer_js__WEBPACK_IMPORTED_MODULE_7__.DrawLayer),\n/* harmony export */   FeatureTest: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.FeatureTest),\n/* harmony export */   GlobalWorkerOptions: () => (/* reexport safe */ _display_worker_options_js__WEBPACK_IMPORTED_MODULE_8__.GlobalWorkerOptions),\n/* harmony export */   ImageKind: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.ImageKind),\n/* harmony export */   InvalidPDFException: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.InvalidPDFException),\n/* harmony export */   MissingPDFException: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.MissingPDFException),\n/* harmony export */   OPS: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.OPS),\n/* harmony export */   Outliner: () => (/* reexport safe */ _display_editor_outliner_js__WEBPACK_IMPORTED_MODULE_9__.Outliner),\n/* harmony export */   PDFDataRangeTransport: () => (/* reexport safe */ _display_api_js__WEBPACK_IMPORTED_MODULE_1__.PDFDataRangeTransport),\n/* harmony export */   PDFDateString: () => (/* reexport safe */ _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.PDFDateString),\n/* harmony export */   PDFWorker: () => (/* reexport safe */ _display_api_js__WEBPACK_IMPORTED_MODULE_1__.PDFWorker),\n/* harmony export */   PasswordResponses: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PasswordResponses),\n/* harmony export */   PermissionFlag: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PermissionFlag),\n/* harmony export */   PixelsPerInch: () => (/* reexport safe */ _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.PixelsPerInch),\n/* harmony export */   PromiseCapability: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability),\n/* harmony export */   RenderingCancelledException: () => (/* reexport safe */ _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.RenderingCancelledException),\n/* harmony export */   UnexpectedResponseException: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.UnexpectedResponseException),\n/* harmony export */   Util: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.Util),\n/* harmony export */   VerbosityLevel: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.VerbosityLevel),\n/* harmony export */   XfaLayer: () => (/* reexport safe */ _display_xfa_layer_js__WEBPACK_IMPORTED_MODULE_10__.XfaLayer),\n/* harmony export */   build: () => (/* reexport safe */ _display_api_js__WEBPACK_IMPORTED_MODULE_1__.build),\n/* harmony export */   createValidAbsoluteUrl: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.createValidAbsoluteUrl),\n/* harmony export */   fetchData: () => (/* reexport safe */ _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.fetchData),\n/* harmony export */   getDocument: () => (/* reexport safe */ _display_api_js__WEBPACK_IMPORTED_MODULE_1__.getDocument),\n/* harmony export */   getFilenameFromUrl: () => (/* reexport safe */ _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.getFilenameFromUrl),\n/* harmony export */   getPdfFilenameFromUrl: () => (/* reexport safe */ _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.getPdfFilenameFromUrl),\n/* harmony export */   getXfaPageViewport: () => (/* reexport safe */ _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.getXfaPageViewport),\n/* harmony export */   isDataScheme: () => (/* reexport safe */ _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.isDataScheme),\n/* harmony export */   isPdfFile: () => (/* reexport safe */ _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.isPdfFile),\n/* harmony export */   noContextMenu: () => (/* reexport safe */ _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.noContextMenu),\n/* harmony export */   normalizeUnicode: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.normalizeUnicode),\n/* harmony export */   renderTextLayer: () => (/* reexport safe */ _display_text_layer_js__WEBPACK_IMPORTED_MODULE_3__.renderTextLayer),\n/* harmony export */   setLayerDimensions: () => (/* reexport safe */ _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__.setLayerDimensions),\n/* harmony export */   shadow: () => (/* reexport safe */ _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow),\n/* harmony export */   updateTextLayer: () => (/* reexport safe */ _display_text_layer_js__WEBPACK_IMPORTED_MODULE_3__.updateTextLayer),\n/* harmony export */   version: () => (/* reexport safe */ _display_api_js__WEBPACK_IMPORTED_MODULE_1__.version)\n/* harmony export */ });\n/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n/* harmony import */ var _display_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(406);\n/* harmony import */ var _display_display_utils_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(473);\n/* harmony import */ var _display_text_layer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(739);\n/* harmony import */ var _display_editor_annotation_editor_layer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(331);\n/* harmony import */ var _display_editor_tools_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(812);\n/* harmony import */ var _display_annotation_layer_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(640);\n/* harmony import */ var _display_draw_layer_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(423);\n/* harmony import */ var _display_worker_options_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(368);\n/* harmony import */ var _display_editor_outliner_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(405);\n/* harmony import */ var _display_xfa_layer_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(160);\nvar __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([_display_api_js__WEBPACK_IMPORTED_MODULE_1__]);\n_display_api_js__WEBPACK_IMPORTED_MODULE_1__ = (__webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__)[0];\n\n\n\n\n\n\n\n\n\n\n\nconst pdfjsVersion = '4.0.269';\nconst pdfjsBuild = 'f4b396f6c';\n\n__webpack_async_result__();\n} catch(e) { __webpack_async_result__(e); } });\n\n/***/ }),\n\n/***/ 694:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   MessageHandler: () => (/* binding */ MessageHandler)\n/* harmony export */ });\n/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n\nconst CallbackKind = {\n  UNKNOWN: 0,\n  DATA: 1,\n  ERROR: 2\n};\nconst StreamKind = {\n  UNKNOWN: 0,\n  CANCEL: 1,\n  CANCEL_COMPLETE: 2,\n  CLOSE: 3,\n  ENQUEUE: 4,\n  ERROR: 5,\n  PULL: 6,\n  PULL_COMPLETE: 7,\n  START_COMPLETE: 8\n};\nfunction wrapReason(reason) {\n  if (!(reason instanceof Error || typeof reason === \"object\" && reason !== null)) {\n    (0,_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)('wrapReason: Expected \"reason\" to be a (possibly cloned) Error.');\n  }\n  switch (reason.name) {\n    case \"AbortException\":\n      return new _util_js__WEBPACK_IMPORTED_MODULE_0__.AbortException(reason.message);\n    case \"MissingPDFException\":\n      return new _util_js__WEBPACK_IMPORTED_MODULE_0__.MissingPDFException(reason.message);\n    case \"PasswordException\":\n      return new _util_js__WEBPACK_IMPORTED_MODULE_0__.PasswordException(reason.message, reason.code);\n    case \"UnexpectedResponseException\":\n      return new _util_js__WEBPACK_IMPORTED_MODULE_0__.UnexpectedResponseException(reason.message, reason.status);\n    case \"UnknownErrorException\":\n      return new _util_js__WEBPACK_IMPORTED_MODULE_0__.UnknownErrorException(reason.message, reason.details);\n    default:\n      return new _util_js__WEBPACK_IMPORTED_MODULE_0__.UnknownErrorException(reason.message, reason.toString());\n  }\n}\nclass MessageHandler {\n  constructor(sourceName, targetName, comObj) {\n    this.sourceName = sourceName;\n    this.targetName = targetName;\n    this.comObj = comObj;\n    this.callbackId = 1;\n    this.streamId = 1;\n    this.streamSinks = Object.create(null);\n    this.streamControllers = Object.create(null);\n    this.callbackCapabilities = Object.create(null);\n    this.actionHandler = Object.create(null);\n    this._onComObjOnMessage = event => {\n      const data = event.data;\n      if (data.targetName !== this.sourceName) {\n        return;\n      }\n      if (data.stream) {\n        this.#processStreamMessage(data);\n        return;\n      }\n      if (data.callback) {\n        const callbackId = data.callbackId;\n        const capability = this.callbackCapabilities[callbackId];\n        if (!capability) {\n          throw new Error(`Cannot resolve callback ${callbackId}`);\n        }\n        delete this.callbackCapabilities[callbackId];\n        if (data.callback === CallbackKind.DATA) {\n          capability.resolve(data.data);\n        } else if (data.callback === CallbackKind.ERROR) {\n          capability.reject(wrapReason(data.reason));\n        } else {\n          throw new Error(\"Unexpected callback case\");\n        }\n        return;\n      }\n      const action = this.actionHandler[data.action];\n      if (!action) {\n        throw new Error(`Unknown action from worker: ${data.action}`);\n      }\n      if (data.callbackId) {\n        const cbSourceName = this.sourceName;\n        const cbTargetName = data.sourceName;\n        new Promise(function (resolve) {\n          resolve(action(data.data));\n        }).then(function (result) {\n          comObj.postMessage({\n            sourceName: cbSourceName,\n            targetName: cbTargetName,\n            callback: CallbackKind.DATA,\n            callbackId: data.callbackId,\n            data: result\n          });\n        }, function (reason) {\n          comObj.postMessage({\n            sourceName: cbSourceName,\n            targetName: cbTargetName,\n            callback: CallbackKind.ERROR,\n            callbackId: data.callbackId,\n            reason: wrapReason(reason)\n          });\n        });\n        return;\n      }\n      if (data.streamId) {\n        this.#createStreamSink(data);\n        return;\n      }\n      action(data.data);\n    };\n    comObj.addEventListener(\"message\", this._onComObjOnMessage);\n  }\n  on(actionName, handler) {\n    const ah = this.actionHandler;\n    if (ah[actionName]) {\n      throw new Error(`There is already an actionName called \"${actionName}\"`);\n    }\n    ah[actionName] = handler;\n  }\n  send(actionName, data, transfers) {\n    this.comObj.postMessage({\n      sourceName: this.sourceName,\n      targetName: this.targetName,\n      action: actionName,\n      data\n    }, transfers);\n  }\n  sendWithPromise(actionName, data, transfers) {\n    const callbackId = this.callbackId++;\n    const capability = new _util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n    this.callbackCapabilities[callbackId] = capability;\n    try {\n      this.comObj.postMessage({\n        sourceName: this.sourceName,\n        targetName: this.targetName,\n        action: actionName,\n        callbackId,\n        data\n      }, transfers);\n    } catch (ex) {\n      capability.reject(ex);\n    }\n    return capability.promise;\n  }\n  sendWithStream(actionName, data, queueingStrategy, transfers) {\n    const streamId = this.streamId++,\n      sourceName = this.sourceName,\n      targetName = this.targetName,\n      comObj = this.comObj;\n    return new ReadableStream({\n      start: controller => {\n        const startCapability = new _util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n        this.streamControllers[streamId] = {\n          controller,\n          startCall: startCapability,\n          pullCall: null,\n          cancelCall: null,\n          isClosed: false\n        };\n        comObj.postMessage({\n          sourceName,\n          targetName,\n          action: actionName,\n          streamId,\n          data,\n          desiredSize: controller.desiredSize\n        }, transfers);\n        return startCapability.promise;\n      },\n      pull: controller => {\n        const pullCapability = new _util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n        this.streamControllers[streamId].pullCall = pullCapability;\n        comObj.postMessage({\n          sourceName,\n          targetName,\n          stream: StreamKind.PULL,\n          streamId,\n          desiredSize: controller.desiredSize\n        });\n        return pullCapability.promise;\n      },\n      cancel: reason => {\n        (0,_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(reason instanceof Error, \"cancel must have a valid reason\");\n        const cancelCapability = new _util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n        this.streamControllers[streamId].cancelCall = cancelCapability;\n        this.streamControllers[streamId].isClosed = true;\n        comObj.postMessage({\n          sourceName,\n          targetName,\n          stream: StreamKind.CANCEL,\n          streamId,\n          reason: wrapReason(reason)\n        });\n        return cancelCapability.promise;\n      }\n    }, queueingStrategy);\n  }\n  #createStreamSink(data) {\n    const streamId = data.streamId,\n      sourceName = this.sourceName,\n      targetName = data.sourceName,\n      comObj = this.comObj;\n    const self = this,\n      action = this.actionHandler[data.action];\n    const streamSink = {\n      enqueue(chunk, size = 1, transfers) {\n        if (this.isCancelled) {\n          return;\n        }\n        const lastDesiredSize = this.desiredSize;\n        this.desiredSize -= size;\n        if (lastDesiredSize > 0 && this.desiredSize <= 0) {\n          this.sinkCapability = new _util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability();\n          this.ready = this.sinkCapability.promise;\n        }\n        comObj.postMessage({\n          sourceName,\n          targetName,\n          stream: StreamKind.ENQUEUE,\n          streamId,\n          chunk\n        }, transfers);\n      },\n      close() {\n        if (this.isCancelled) {\n          return;\n        }\n        this.isCancelled = true;\n        comObj.postMessage({\n          sourceName,\n          targetName,\n          stream: StreamKind.CLOSE,\n          streamId\n        });\n        delete self.streamSinks[streamId];\n      },\n      error(reason) {\n        (0,_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(reason instanceof Error, \"error must have a valid reason\");\n        if (this.isCancelled) {\n          return;\n        }\n        this.isCancelled = true;\n        comObj.postMessage({\n          sourceName,\n          targetName,\n          stream: StreamKind.ERROR,\n          streamId,\n          reason: wrapReason(reason)\n        });\n      },\n      sinkCapability: new _util_js__WEBPACK_IMPORTED_MODULE_0__.PromiseCapability(),\n      onPull: null,\n      onCancel: null,\n      isCancelled: false,\n      desiredSize: data.desiredSize,\n      ready: null\n    };\n    streamSink.sinkCapability.resolve();\n    streamSink.ready = streamSink.sinkCapability.promise;\n    this.streamSinks[streamId] = streamSink;\n    new Promise(function (resolve) {\n      resolve(action(data.data, streamSink));\n    }).then(function () {\n      comObj.postMessage({\n        sourceName,\n        targetName,\n        stream: StreamKind.START_COMPLETE,\n        streamId,\n        success: true\n      });\n    }, function (reason) {\n      comObj.postMessage({\n        sourceName,\n        targetName,\n        stream: StreamKind.START_COMPLETE,\n        streamId,\n        reason: wrapReason(reason)\n      });\n    });\n  }\n  #processStreamMessage(data) {\n    const streamId = data.streamId,\n      sourceName = this.sourceName,\n      targetName = data.sourceName,\n      comObj = this.comObj;\n    const streamController = this.streamControllers[streamId],\n      streamSink = this.streamSinks[streamId];\n    switch (data.stream) {\n      case StreamKind.START_COMPLETE:\n        if (data.success) {\n          streamController.startCall.resolve();\n        } else {\n          streamController.startCall.reject(wrapReason(data.reason));\n        }\n        break;\n      case StreamKind.PULL_COMPLETE:\n        if (data.success) {\n          streamController.pullCall.resolve();\n        } else {\n          streamController.pullCall.reject(wrapReason(data.reason));\n        }\n        break;\n      case StreamKind.PULL:\n        if (!streamSink) {\n          comObj.postMessage({\n            sourceName,\n            targetName,\n            stream: StreamKind.PULL_COMPLETE,\n            streamId,\n            success: true\n          });\n          break;\n        }\n        if (streamSink.desiredSize <= 0 && data.desiredSize > 0) {\n          streamSink.sinkCapability.resolve();\n        }\n        streamSink.desiredSize = data.desiredSize;\n        new Promise(function (resolve) {\n          resolve(streamSink.onPull?.());\n        }).then(function () {\n          comObj.postMessage({\n            sourceName,\n            targetName,\n            stream: StreamKind.PULL_COMPLETE,\n            streamId,\n            success: true\n          });\n        }, function (reason) {\n          comObj.postMessage({\n            sourceName,\n            targetName,\n            stream: StreamKind.PULL_COMPLETE,\n            streamId,\n            reason: wrapReason(reason)\n          });\n        });\n        break;\n      case StreamKind.ENQUEUE:\n        (0,_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(streamController, \"enqueue should have stream controller\");\n        if (streamController.isClosed) {\n          break;\n        }\n        streamController.controller.enqueue(data.chunk);\n        break;\n      case StreamKind.CLOSE:\n        (0,_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(streamController, \"close should have stream controller\");\n        if (streamController.isClosed) {\n          break;\n        }\n        streamController.isClosed = true;\n        streamController.controller.close();\n        this.#deleteStreamController(streamController, streamId);\n        break;\n      case StreamKind.ERROR:\n        (0,_util_js__WEBPACK_IMPORTED_MODULE_0__.assert)(streamController, \"error should have stream controller\");\n        streamController.controller.error(wrapReason(data.reason));\n        this.#deleteStreamController(streamController, streamId);\n        break;\n      case StreamKind.CANCEL_COMPLETE:\n        if (data.success) {\n          streamController.cancelCall.resolve();\n        } else {\n          streamController.cancelCall.reject(wrapReason(data.reason));\n        }\n        this.#deleteStreamController(streamController, streamId);\n        break;\n      case StreamKind.CANCEL:\n        if (!streamSink) {\n          break;\n        }\n        new Promise(function (resolve) {\n          resolve(streamSink.onCancel?.(wrapReason(data.reason)));\n        }).then(function () {\n          comObj.postMessage({\n            sourceName,\n            targetName,\n            stream: StreamKind.CANCEL_COMPLETE,\n            streamId,\n            success: true\n          });\n        }, function (reason) {\n          comObj.postMessage({\n            sourceName,\n            targetName,\n            stream: StreamKind.CANCEL_COMPLETE,\n            streamId,\n            reason: wrapReason(reason)\n          });\n        });\n        streamSink.sinkCapability.reject(wrapReason(data.reason));\n        streamSink.isCancelled = true;\n        delete this.streamSinks[streamId];\n        break;\n      default:\n        throw new Error(\"Unexpected stream case\");\n    }\n  }\n  async #deleteStreamController(streamController, streamId) {\n    await Promise.allSettled([streamController.startCall?.promise, streamController.pullCall?.promise, streamController.cancelCall?.promise]);\n    delete this.streamControllers[streamId];\n  }\n  destroy() {\n    this.comObj.removeEventListener(\"message\", this._onComObjOnMessage);\n  }\n}\n\n\n/***/ }),\n\n/***/ 825:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   MurmurHash3_64: () => (/* binding */ MurmurHash3_64)\n/* harmony export */ });\n/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(266);\n\nconst SEED = 0xc3d2e1f0;\nconst MASK_HIGH = 0xffff0000;\nconst MASK_LOW = 0xffff;\nclass MurmurHash3_64 {\n  constructor(seed) {\n    this.h1 = seed ? seed & 0xffffffff : SEED;\n    this.h2 = seed ? seed & 0xffffffff : SEED;\n  }\n  update(input) {\n    let data, length;\n    if (typeof input === \"string\") {\n      data = new Uint8Array(input.length * 2);\n      length = 0;\n      for (let i = 0, ii = input.length; i < ii; i++) {\n        const code = input.charCodeAt(i);\n        if (code <= 0xff) {\n          data[length++] = code;\n        } else {\n          data[length++] = code >>> 8;\n          data[length++] = code & 0xff;\n        }\n      }\n    } else if ((0,_util_js__WEBPACK_IMPORTED_MODULE_0__.isArrayBuffer)(input)) {\n      data = input.slice();\n      length = data.byteLength;\n    } else {\n      throw new Error(\"Wrong data format in MurmurHash3_64_update. \" + \"Input must be a string or array.\");\n    }\n    const blockCounts = length >> 2;\n    const tailLength = length - blockCounts * 4;\n    const dataUint32 = new Uint32Array(data.buffer, 0, blockCounts);\n    let k1 = 0,\n      k2 = 0;\n    let h1 = this.h1,\n      h2 = this.h2;\n    const C1 = 0xcc9e2d51,\n      C2 = 0x1b873593;\n    const C1_LOW = C1 & MASK_LOW,\n      C2_LOW = C2 & MASK_LOW;\n    for (let i = 0; i < blockCounts; i++) {\n      if (i & 1) {\n        k1 = dataUint32[i];\n        k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW;\n        k1 = k1 << 15 | k1 >>> 17;\n        k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW;\n        h1 ^= k1;\n        h1 = h1 << 13 | h1 >>> 19;\n        h1 = h1 * 5 + 0xe6546b64;\n      } else {\n        k2 = dataUint32[i];\n        k2 = k2 * C1 & MASK_HIGH | k2 * C1_LOW & MASK_LOW;\n        k2 = k2 << 15 | k2 >>> 17;\n        k2 = k2 * C2 & MASK_HIGH | k2 * C2_LOW & MASK_LOW;\n        h2 ^= k2;\n        h2 = h2 << 13 | h2 >>> 19;\n        h2 = h2 * 5 + 0xe6546b64;\n      }\n    }\n    k1 = 0;\n    switch (tailLength) {\n      case 3:\n        k1 ^= data[blockCounts * 4 + 2] << 16;\n      case 2:\n        k1 ^= data[blockCounts * 4 + 1] << 8;\n      case 1:\n        k1 ^= data[blockCounts * 4];\n        k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW;\n        k1 = k1 << 15 | k1 >>> 17;\n        k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW;\n        if (blockCounts & 1) {\n          h1 ^= k1;\n        } else {\n          h2 ^= k1;\n        }\n    }\n    this.h1 = h1;\n    this.h2 = h2;\n  }\n  hexdigest() {\n    let h1 = this.h1,\n      h2 = this.h2;\n    h1 ^= h2 >>> 1;\n    h1 = h1 * 0xed558ccd & MASK_HIGH | h1 * 0x8ccd & MASK_LOW;\n    h2 = h2 * 0xff51afd7 & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16;\n    h1 ^= h2 >>> 1;\n    h1 = h1 * 0x1a85ec53 & MASK_HIGH | h1 * 0xec53 & MASK_LOW;\n    h2 = h2 * 0xc4ceb9fe & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16;\n    h1 ^= h2 >>> 1;\n    return (h1 >>> 0).toString(16).padStart(8, \"0\") + (h2 >>> 0).toString(16).padStart(8, \"0\");\n  }\n}\n\n\n/***/ }),\n\n/***/ 266:\n/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {\n\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   AbortException: () => (/* binding */ AbortException),\n/* harmony export */   AnnotationBorderStyleType: () => (/* binding */ AnnotationBorderStyleType),\n/* harmony export */   AnnotationEditorParamsType: () => (/* binding */ AnnotationEditorParamsType),\n/* harmony export */   AnnotationEditorPrefix: () => (/* binding */ AnnotationEditorPrefix),\n/* harmony export */   AnnotationEditorType: () => (/* binding */ AnnotationEditorType),\n/* harmony export */   AnnotationMode: () => (/* binding */ AnnotationMode),\n/* harmony export */   AnnotationPrefix: () => (/* binding */ AnnotationPrefix),\n/* harmony export */   AnnotationType: () => (/* binding */ AnnotationType),\n/* harmony export */   BaseException: () => (/* binding */ BaseException),\n/* harmony export */   CMapCompressionType: () => (/* binding */ CMapCompressionType),\n/* harmony export */   FONT_IDENTITY_MATRIX: () => (/* binding */ FONT_IDENTITY_MATRIX),\n/* harmony export */   FeatureTest: () => (/* binding */ FeatureTest),\n/* harmony export */   FormatError: () => (/* binding */ FormatError),\n/* harmony export */   IDENTITY_MATRIX: () => (/* binding */ IDENTITY_MATRIX),\n/* harmony export */   ImageKind: () => (/* binding */ ImageKind),\n/* harmony export */   InvalidPDFException: () => (/* binding */ InvalidPDFException),\n/* harmony export */   LINE_FACTOR: () => (/* binding */ LINE_FACTOR),\n/* harmony export */   MAX_IMAGE_SIZE_TO_CACHE: () => (/* binding */ MAX_IMAGE_SIZE_TO_CACHE),\n/* harmony export */   MissingPDFException: () => (/* binding */ MissingPDFException),\n/* harmony export */   OPS: () => (/* binding */ OPS),\n/* harmony export */   PasswordException: () => (/* binding */ PasswordException),\n/* harmony export */   PasswordResponses: () => (/* binding */ PasswordResponses),\n/* harmony export */   PermissionFlag: () => (/* binding */ PermissionFlag),\n/* harmony export */   PromiseCapability: () => (/* binding */ PromiseCapability),\n/* harmony export */   RenderingIntentFlag: () => (/* binding */ RenderingIntentFlag),\n/* harmony export */   TextRenderingMode: () => (/* binding */ TextRenderingMode),\n/* harmony export */   UnexpectedResponseException: () => (/* binding */ UnexpectedResponseException),\n/* harmony export */   UnknownErrorException: () => (/* binding */ UnknownErrorException),\n/* harmony export */   Util: () => (/* binding */ Util),\n/* harmony export */   VerbosityLevel: () => (/* binding */ VerbosityLevel),\n/* harmony export */   assert: () => (/* binding */ assert),\n/* harmony export */   bytesToString: () => (/* binding */ bytesToString),\n/* harmony export */   createValidAbsoluteUrl: () => (/* binding */ createValidAbsoluteUrl),\n/* harmony export */   getUuid: () => (/* binding */ getUuid),\n/* harmony export */   getVerbosityLevel: () => (/* binding */ getVerbosityLevel),\n/* harmony export */   info: () => (/* binding */ info),\n/* harmony export */   isArrayBuffer: () => (/* binding */ isArrayBuffer),\n/* harmony export */   isNodeJS: () => (/* binding */ isNodeJS),\n/* harmony export */   normalizeUnicode: () => (/* binding */ normalizeUnicode),\n/* harmony export */   objectFromMap: () => (/* binding */ objectFromMap),\n/* harmony export */   setVerbosityLevel: () => (/* binding */ setVerbosityLevel),\n/* harmony export */   shadow: () => (/* binding */ shadow),\n/* harmony export */   string32: () => (/* binding */ string32),\n/* harmony export */   stringToBytes: () => (/* binding */ stringToBytes),\n/* harmony export */   unreachable: () => (/* binding */ unreachable),\n/* harmony export */   warn: () => (/* binding */ warn)\n/* harmony export */ });\n/* unused harmony exports AnnotationActionEventType, AnnotationFieldFlag, AnnotationFlag, AnnotationReplyType, BASELINE_FACTOR, DocumentActionEventType, getModificationDate, isArrayEqual, LINE_DESCENT_FACTOR, objectSize, PageActionEventType, stringToPDFString, stringToUTF8String, utf8StringToString */\nconst isNodeJS = typeof process === \"object\" && process + \"\" === \"[object process]\" && !process.versions.nw && !(process.versions.electron && process.type && process.type !== \"browser\");\nconst IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];\nconst FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];\nconst MAX_IMAGE_SIZE_TO_CACHE = 10e6;\nconst LINE_FACTOR = 1.35;\nconst LINE_DESCENT_FACTOR = 0.35;\nconst BASELINE_FACTOR = LINE_DESCENT_FACTOR / LINE_FACTOR;\nconst RenderingIntentFlag = {\n  ANY: 0x01,\n  DISPLAY: 0x02,\n  PRINT: 0x04,\n  SAVE: 0x08,\n  ANNOTATIONS_FORMS: 0x10,\n  ANNOTATIONS_STORAGE: 0x20,\n  ANNOTATIONS_DISABLE: 0x40,\n  OPLIST: 0x100\n};\nconst AnnotationMode = {\n  DISABLE: 0,\n  ENABLE: 1,\n  ENABLE_FORMS: 2,\n  ENABLE_STORAGE: 3\n};\nconst AnnotationEditorPrefix = \"pdfjs_internal_editor_\";\nconst AnnotationEditorType = {\n  DISABLE: -1,\n  NONE: 0,\n  FREETEXT: 3,\n  HIGHLIGHT: 9,\n  STAMP: 13,\n  INK: 15\n};\nconst AnnotationEditorParamsType = {\n  RESIZE: 1,\n  CREATE: 2,\n  FREETEXT_SIZE: 11,\n  FREETEXT_COLOR: 12,\n  FREETEXT_OPACITY: 13,\n  INK_COLOR: 21,\n  INK_THICKNESS: 22,\n  INK_OPACITY: 23\n};\nconst PermissionFlag = {\n  PRINT: 0x04,\n  MODIFY_CONTENTS: 0x08,\n  COPY: 0x10,\n  MODIFY_ANNOTATIONS: 0x20,\n  FILL_INTERACTIVE_FORMS: 0x100,\n  COPY_FOR_ACCESSIBILITY: 0x200,\n  ASSEMBLE: 0x400,\n  PRINT_HIGH_QUALITY: 0x800\n};\nconst TextRenderingMode = {\n  FILL: 0,\n  STROKE: 1,\n  FILL_STROKE: 2,\n  INVISIBLE: 3,\n  FILL_ADD_TO_PATH: 4,\n  STROKE_ADD_TO_PATH: 5,\n  FILL_STROKE_ADD_TO_PATH: 6,\n  ADD_TO_PATH: 7,\n  FILL_STROKE_MASK: 3,\n  ADD_TO_PATH_FLAG: 4\n};\nconst ImageKind = {\n  GRAYSCALE_1BPP: 1,\n  RGB_24BPP: 2,\n  RGBA_32BPP: 3\n};\nconst AnnotationType = {\n  TEXT: 1,\n  LINK: 2,\n  FREETEXT: 3,\n  LINE: 4,\n  SQUARE: 5,\n  CIRCLE: 6,\n  POLYGON: 7,\n  POLYLINE: 8,\n  HIGHLIGHT: 9,\n  UNDERLINE: 10,\n  SQUIGGLY: 11,\n  STRIKEOUT: 12,\n  STAMP: 13,\n  CARET: 14,\n  INK: 15,\n  POPUP: 16,\n  FILEATTACHMENT: 17,\n  SOUND: 18,\n  MOVIE: 19,\n  WIDGET: 20,\n  SCREEN: 21,\n  PRINTERMARK: 22,\n  TRAPNET: 23,\n  WATERMARK: 24,\n  THREED: 25,\n  REDACT: 26\n};\nconst AnnotationReplyType = {\n  GROUP: \"Group\",\n  REPLY: \"R\"\n};\nconst AnnotationFlag = {\n  INVISIBLE: 0x01,\n  HIDDEN: 0x02,\n  PRINT: 0x04,\n  NOZOOM: 0x08,\n  NOROTATE: 0x10,\n  NOVIEW: 0x20,\n  READONLY: 0x40,\n  LOCKED: 0x80,\n  TOGGLENOVIEW: 0x100,\n  LOCKEDCONTENTS: 0x200\n};\nconst AnnotationFieldFlag = {\n  READONLY: 0x0000001,\n  REQUIRED: 0x0000002,\n  NOEXPORT: 0x0000004,\n  MULTILINE: 0x0001000,\n  PASSWORD: 0x0002000,\n  NOTOGGLETOOFF: 0x0004000,\n  RADIO: 0x0008000,\n  PUSHBUTTON: 0x0010000,\n  COMBO: 0x0020000,\n  EDIT: 0x0040000,\n  SORT: 0x0080000,\n  FILESELECT: 0x0100000,\n  MULTISELECT: 0x0200000,\n  DONOTSPELLCHECK: 0x0400000,\n  DONOTSCROLL: 0x0800000,\n  COMB: 0x1000000,\n  RICHTEXT: 0x2000000,\n  RADIOSINUNISON: 0x2000000,\n  COMMITONSELCHANGE: 0x4000000\n};\nconst AnnotationBorderStyleType = {\n  SOLID: 1,\n  DASHED: 2,\n  BEVELED: 3,\n  INSET: 4,\n  UNDERLINE: 5\n};\nconst AnnotationActionEventType = {\n  E: \"Mouse Enter\",\n  X: \"Mouse Exit\",\n  D: \"Mouse Down\",\n  U: \"Mouse Up\",\n  Fo: \"Focus\",\n  Bl: \"Blur\",\n  PO: \"PageOpen\",\n  PC: \"PageClose\",\n  PV: \"PageVisible\",\n  PI: \"PageInvisible\",\n  K: \"Keystroke\",\n  F: \"Format\",\n  V: \"Validate\",\n  C: \"Calculate\"\n};\nconst DocumentActionEventType = {\n  WC: \"WillClose\",\n  WS: \"WillSave\",\n  DS: \"DidSave\",\n  WP: \"WillPrint\",\n  DP: \"DidPrint\"\n};\nconst PageActionEventType = {\n  O: \"PageOpen\",\n  C: \"PageClose\"\n};\nconst VerbosityLevel = {\n  ERRORS: 0,\n  WARNINGS: 1,\n  INFOS: 5\n};\nconst CMapCompressionType = {\n  NONE: 0,\n  BINARY: 1\n};\nconst OPS = {\n  dependency: 1,\n  setLineWidth: 2,\n  setLineCap: 3,\n  setLineJoin: 4,\n  setMiterLimit: 5,\n  setDash: 6,\n  setRenderingIntent: 7,\n  setFlatness: 8,\n  setGState: 9,\n  save: 10,\n  restore: 11,\n  transform: 12,\n  moveTo: 13,\n  lineTo: 14,\n  curveTo: 15,\n  curveTo2: 16,\n  curveTo3: 17,\n  closePath: 18,\n  rectangle: 19,\n  stroke: 20,\n  closeStroke: 21,\n  fill: 22,\n  eoFill: 23,\n  fillStroke: 24,\n  eoFillStroke: 25,\n  closeFillStroke: 26,\n  closeEOFillStroke: 27,\n  endPath: 28,\n  clip: 29,\n  eoClip: 30,\n  beginText: 31,\n  endText: 32,\n  setCharSpacing: 33,\n  setWordSpacing: 34,\n  setHScale: 35,\n  setLeading: 36,\n  setFont: 37,\n  setTextRenderingMode: 38,\n  setTextRise: 39,\n  moveText: 40,\n  setLeadingMoveText: 41,\n  setTextMatrix: 42,\n  nextLine: 43,\n  showText: 44,\n  showSpacedText: 45,\n  nextLineShowText: 46,\n  nextLineSetSpacingShowText: 47,\n  setCharWidth: 48,\n  setCharWidthAndBounds: 49,\n  setStrokeColorSpace: 50,\n  setFillColorSpace: 51,\n  setStrokeColor: 52,\n  setStrokeColorN: 53,\n  setFillColor: 54,\n  setFillColorN: 55,\n  setStrokeGray: 56,\n  setFillGray: 57,\n  setStrokeRGBColor: 58,\n  setFillRGBColor: 59,\n  setStrokeCMYKColor: 60,\n  setFillCMYKColor: 61,\n  shadingFill: 62,\n  beginInlineImage: 63,\n  beginImageData: 64,\n  endInlineImage: 65,\n  paintXObject: 66,\n  markPoint: 67,\n  markPointProps: 68,\n  beginMarkedContent: 69,\n  beginMarkedContentProps: 70,\n  endMarkedContent: 71,\n  beginCompat: 72,\n  endCompat: 73,\n  paintFormXObjectBegin: 74,\n  paintFormXObjectEnd: 75,\n  beginGroup: 76,\n  endGroup: 77,\n  beginAnnotation: 80,\n  endAnnotation: 81,\n  paintImageMaskXObject: 83,\n  paintImageMaskXObjectGroup: 84,\n  paintImageXObject: 85,\n  paintInlineImageXObject: 86,\n  paintInlineImageXObjectGroup: 87,\n  paintImageXObjectRepeat: 88,\n  paintImageMaskXObjectRepeat: 89,\n  paintSolidColorImageMask: 90,\n  constructPath: 91\n};\nconst PasswordResponses = {\n  NEED_PASSWORD: 1,\n  INCORRECT_PASSWORD: 2\n};\nlet verbosity = VerbosityLevel.WARNINGS;\nfunction setVerbosityLevel(level) {\n  if (Number.isInteger(level)) {\n    verbosity = level;\n  }\n}\nfunction getVerbosityLevel() {\n  return verbosity;\n}\nfunction info(msg) {\n  if (verbosity >= VerbosityLevel.INFOS) {\n    console.log(`Info: ${msg}`);\n  }\n}\nfunction warn(msg) {\n  if (verbosity >= VerbosityLevel.WARNINGS) {\n    console.log(`Warning: ${msg}`);\n  }\n}\nfunction unreachable(msg) {\n  throw new Error(msg);\n}\nfunction assert(cond, msg) {\n  if (!cond) {\n    unreachable(msg);\n  }\n}\nfunction _isValidProtocol(url) {\n  switch (url?.protocol) {\n    case \"http:\":\n    case \"https:\":\n    case \"ftp:\":\n    case \"mailto:\":\n    case \"tel:\":\n      return true;\n    default:\n      return false;\n  }\n}\nfunction createValidAbsoluteUrl(url, baseUrl = null, options = null) {\n  if (!url) {\n    return null;\n  }\n  try {\n    if (options && typeof url === \"string\") {\n      if (options.addDefaultProtocol && url.startsWith(\"www.\")) {\n        const dots = url.match(/\\./g);\n        if (dots?.length >= 2) {\n          url = `http://${url}`;\n        }\n      }\n      if (options.tryConvertEncoding) {\n        try {\n          url = stringToUTF8String(url);\n        } catch {}\n      }\n    }\n    const absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url);\n    if (_isValidProtocol(absoluteUrl)) {\n      return absoluteUrl;\n    }\n  } catch {}\n  return null;\n}\nfunction shadow(obj, prop, value, nonSerializable = false) {\n  Object.defineProperty(obj, prop, {\n    value,\n    enumerable: !nonSerializable,\n    configurable: true,\n    writable: false\n  });\n  return value;\n}\nconst BaseException = function BaseExceptionClosure() {\n  function BaseException(message, name) {\n    if (this.constructor === BaseException) {\n      unreachable(\"Cannot initialize BaseException.\");\n    }\n    this.message = message;\n    this.name = name;\n  }\n  BaseException.prototype = new Error();\n  BaseException.constructor = BaseException;\n  return BaseException;\n}();\nclass PasswordException extends BaseException {\n  constructor(msg, code) {\n    super(msg, \"PasswordException\");\n    this.code = code;\n  }\n}\nclass UnknownErrorException extends BaseException {\n  constructor(msg, details) {\n    super(msg, \"UnknownErrorException\");\n    this.details = details;\n  }\n}\nclass InvalidPDFException extends BaseException {\n  constructor(msg) {\n    super(msg, \"InvalidPDFException\");\n  }\n}\nclass MissingPDFException extends BaseException {\n  constructor(msg) {\n    super(msg, \"MissingPDFException\");\n  }\n}\nclass UnexpectedResponseException extends BaseException {\n  constructor(msg, status) {\n    super(msg, \"UnexpectedResponseException\");\n    this.status = status;\n  }\n}\nclass FormatError extends BaseException {\n  constructor(msg) {\n    super(msg, \"FormatError\");\n  }\n}\nclass AbortException extends BaseException {\n  constructor(msg) {\n    super(msg, \"AbortException\");\n  }\n}\nfunction bytesToString(bytes) {\n  if (typeof bytes !== \"object\" || bytes?.length === undefined) {\n    unreachable(\"Invalid argument for bytesToString\");\n  }\n  const length = bytes.length;\n  const MAX_ARGUMENT_COUNT = 8192;\n  if (length < MAX_ARGUMENT_COUNT) {\n    return String.fromCharCode.apply(null, bytes);\n  }\n  const strBuf = [];\n  for (let i = 0; i < length; i += MAX_ARGUMENT_COUNT) {\n    const chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);\n    const chunk = bytes.subarray(i, chunkEnd);\n    strBuf.push(String.fromCharCode.apply(null, chunk));\n  }\n  return strBuf.join(\"\");\n}\nfunction stringToBytes(str) {\n  if (typeof str !== \"string\") {\n    unreachable(\"Invalid argument for stringToBytes\");\n  }\n  const length = str.length;\n  const bytes = new Uint8Array(length);\n  for (let i = 0; i < length; ++i) {\n    bytes[i] = str.charCodeAt(i) & 0xff;\n  }\n  return bytes;\n}\nfunction string32(value) {\n  return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff);\n}\nfunction objectSize(obj) {\n  return Object.keys(obj).length;\n}\nfunction objectFromMap(map) {\n  const obj = Object.create(null);\n  for (const [key, value] of map) {\n    obj[key] = value;\n  }\n  return obj;\n}\nfunction isLittleEndian() {\n  const buffer8 = new Uint8Array(4);\n  buffer8[0] = 1;\n  const view32 = new Uint32Array(buffer8.buffer, 0, 1);\n  return view32[0] === 1;\n}\nfunction isEvalSupported() {\n  try {\n    new Function(\"\");\n    return true;\n  } catch {\n    return false;\n  }\n}\nclass FeatureTest {\n  static get isLittleEndian() {\n    return shadow(this, \"isLittleEndian\", isLittleEndian());\n  }\n  static get isEvalSupported() {\n    return shadow(this, \"isEvalSupported\", isEvalSupported());\n  }\n  static get isOffscreenCanvasSupported() {\n    return shadow(this, \"isOffscreenCanvasSupported\", typeof OffscreenCanvas !== \"undefined\");\n  }\n  static get platform() {\n    if (typeof navigator !== \"undefined\" && typeof navigator?.platform === \"string\") {\n      return shadow(this, \"platform\", {\n        isMac: navigator.platform.includes(\"Mac\")\n      });\n    }\n    return shadow(this, \"platform\", {\n      isMac: false\n    });\n  }\n  static get isCSSRoundSupported() {\n    return shadow(this, \"isCSSRoundSupported\", globalThis.CSS?.supports?.(\"width: round(1.5px, 1px)\"));\n  }\n}\nconst hexNumbers = [...Array(256).keys()].map(n => n.toString(16).padStart(2, \"0\"));\nclass Util {\n  static makeHexColor(r, g, b) {\n    return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`;\n  }\n  static scaleMinMax(transform, minMax) {\n    let temp;\n    if (transform[0]) {\n      if (transform[0] < 0) {\n        temp = minMax[0];\n        minMax[0] = minMax[1];\n        minMax[1] = temp;\n      }\n      minMax[0] *= transform[0];\n      minMax[1] *= transform[0];\n      if (transform[3] < 0) {\n        temp = minMax[2];\n        minMax[2] = minMax[3];\n        minMax[3] = temp;\n      }\n      minMax[2] *= transform[3];\n      minMax[3] *= transform[3];\n    } else {\n      temp = minMax[0];\n      minMax[0] = minMax[2];\n      minMax[2] = temp;\n      temp = minMax[1];\n      minMax[1] = minMax[3];\n      minMax[3] = temp;\n      if (transform[1] < 0) {\n        temp = minMax[2];\n        minMax[2] = minMax[3];\n        minMax[3] = temp;\n      }\n      minMax[2] *= transform[1];\n      minMax[3] *= transform[1];\n      if (transform[2] < 0) {\n        temp = minMax[0];\n        minMax[0] = minMax[1];\n        minMax[1] = temp;\n      }\n      minMax[0] *= transform[2];\n      minMax[1] *= transform[2];\n    }\n    minMax[0] += transform[4];\n    minMax[1] += transform[4];\n    minMax[2] += transform[5];\n    minMax[3] += transform[5];\n  }\n  static transform(m1, m2) {\n    return [m1[0] * m2[0] + m1[2] * m2[1], m1[1] * m2[0] + m1[3] * m2[1], m1[0] * m2[2] + m1[2] * m2[3], m1[1] * m2[2] + m1[3] * m2[3], m1[0] * m2[4] + m1[2] * m2[5] + m1[4], m1[1] * m2[4] + m1[3] * m2[5] + m1[5]];\n  }\n  static applyTransform(p, m) {\n    const xt = p[0] * m[0] + p[1] * m[2] + m[4];\n    const yt = p[0] * m[1] + p[1] * m[3] + m[5];\n    return [xt, yt];\n  }\n  static applyInverseTransform(p, m) {\n    const d = m[0] * m[3] - m[1] * m[2];\n    const xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;\n    const yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;\n    return [xt, yt];\n  }\n  static getAxialAlignedBoundingBox(r, m) {\n    const p1 = this.applyTransform(r, m);\n    const p2 = this.applyTransform(r.slice(2, 4), m);\n    const p3 = this.applyTransform([r[0], r[3]], m);\n    const p4 = this.applyTransform([r[2], r[1]], m);\n    return [Math.min(p1[0], p2[0], p3[0], p4[0]), Math.min(p1[1], p2[1], p3[1], p4[1]), Math.max(p1[0], p2[0], p3[0], p4[0]), Math.max(p1[1], p2[1], p3[1], p4[1])];\n  }\n  static inverseTransform(m) {\n    const d = m[0] * m[3] - m[1] * m[2];\n    return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d];\n  }\n  static singularValueDecompose2dScale(m) {\n    const transpose = [m[0], m[2], m[1], m[3]];\n    const a = m[0] * transpose[0] + m[1] * transpose[2];\n    const b = m[0] * transpose[1] + m[1] * transpose[3];\n    const c = m[2] * transpose[0] + m[3] * transpose[2];\n    const d = m[2] * transpose[1] + m[3] * transpose[3];\n    const first = (a + d) / 2;\n    const second = Math.sqrt((a + d) ** 2 - 4 * (a * d - c * b)) / 2;\n    const sx = first + second || 1;\n    const sy = first - second || 1;\n    return [Math.sqrt(sx), Math.sqrt(sy)];\n  }\n  static normalizeRect(rect) {\n    const r = rect.slice(0);\n    if (rect[0] > rect[2]) {\n      r[0] = rect[2];\n      r[2] = rect[0];\n    }\n    if (rect[1] > rect[3]) {\n      r[1] = rect[3];\n      r[3] = rect[1];\n    }\n    return r;\n  }\n  static intersect(rect1, rect2) {\n    const xLow = Math.max(Math.min(rect1[0], rect1[2]), Math.min(rect2[0], rect2[2]));\n    const xHigh = Math.min(Math.max(rect1[0], rect1[2]), Math.max(rect2[0], rect2[2]));\n    if (xLow > xHigh) {\n      return null;\n    }\n    const yLow = Math.max(Math.min(rect1[1], rect1[3]), Math.min(rect2[1], rect2[3]));\n    const yHigh = Math.min(Math.max(rect1[1], rect1[3]), Math.max(rect2[1], rect2[3]));\n    if (yLow > yHigh) {\n      return null;\n    }\n    return [xLow, yLow, xHigh, yHigh];\n  }\n  static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3) {\n    const tvalues = [],\n      bounds = [[], []];\n    let a, b, c, t, t1, t2, b2ac, sqrtb2ac;\n    for (let i = 0; i < 2; ++i) {\n      if (i === 0) {\n        b = 6 * x0 - 12 * x1 + 6 * x2;\n        a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;\n        c = 3 * x1 - 3 * x0;\n      } else {\n        b = 6 * y0 - 12 * y1 + 6 * y2;\n        a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;\n        c = 3 * y1 - 3 * y0;\n      }\n      if (Math.abs(a) < 1e-12) {\n        if (Math.abs(b) < 1e-12) {\n          continue;\n        }\n        t = -c / b;\n        if (0 < t && t < 1) {\n          tvalues.push(t);\n        }\n        continue;\n      }\n      b2ac = b * b - 4 * c * a;\n      sqrtb2ac = Math.sqrt(b2ac);\n      if (b2ac < 0) {\n        continue;\n      }\n      t1 = (-b + sqrtb2ac) / (2 * a);\n      if (0 < t1 && t1 < 1) {\n        tvalues.push(t1);\n      }\n      t2 = (-b - sqrtb2ac) / (2 * a);\n      if (0 < t2 && t2 < 1) {\n        tvalues.push(t2);\n      }\n    }\n    let j = tvalues.length,\n      mt;\n    const jlen = j;\n    while (j--) {\n      t = tvalues[j];\n      mt = 1 - t;\n      bounds[0][j] = mt * mt * mt * x0 + 3 * mt * mt * t * x1 + 3 * mt * t * t * x2 + t * t * t * x3;\n      bounds[1][j] = mt * mt * mt * y0 + 3 * mt * mt * t * y1 + 3 * mt * t * t * y2 + t * t * t * y3;\n    }\n    bounds[0][jlen] = x0;\n    bounds[1][jlen] = y0;\n    bounds[0][jlen + 1] = x3;\n    bounds[1][jlen + 1] = y3;\n    bounds[0].length = bounds[1].length = jlen + 2;\n    return [Math.min(...bounds[0]), Math.min(...bounds[1]), Math.max(...bounds[0]), Math.max(...bounds[1])];\n  }\n}\nconst PDFStringTranslateTable = (/* unused pure expression or super */ null && ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2d8, 0x2c7, 0x2c6, 0x2d9, 0x2dd, 0x2db, 0x2da, 0x2dc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x192, 0x2044, 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018, 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x141, 0x152, 0x160, 0x178, 0x17d, 0x131, 0x142, 0x153, 0x161, 0x17e, 0, 0x20ac]));\nfunction stringToPDFString(str) {\n  if (str[0] >= \"\\xEF\") {\n    let encoding;\n    if (str[0] === \"\\xFE\" && str[1] === \"\\xFF\") {\n      encoding = \"utf-16be\";\n      if (str.length % 2 === 1) {\n        str = str.slice(0, -1);\n      }\n    } else if (str[0] === \"\\xFF\" && str[1] === \"\\xFE\") {\n      encoding = \"utf-16le\";\n      if (str.length % 2 === 1) {\n        str = str.slice(0, -1);\n      }\n    } else if (str[0] === \"\\xEF\" && str[1] === \"\\xBB\" && str[2] === \"\\xBF\") {\n      encoding = \"utf-8\";\n    }\n    if (encoding) {\n      try {\n        const decoder = new TextDecoder(encoding, {\n          fatal: true\n        });\n        const buffer = stringToBytes(str);\n        const decoded = decoder.decode(buffer);\n        if (!decoded.includes(\"\\x1b\")) {\n          return decoded;\n        }\n        return decoded.replaceAll(/\\x1b[^\\x1b]*(?:\\x1b|$)/g, \"\");\n      } catch (ex) {\n        warn(`stringToPDFString: \"${ex}\".`);\n      }\n    }\n  }\n  const strBuf = [];\n  for (let i = 0, ii = str.length; i < ii; i++) {\n    const charCode = str.charCodeAt(i);\n    if (charCode === 0x1b) {\n      while (++i < ii && str.charCodeAt(i) !== 0x1b) {}\n      continue;\n    }\n    const code = PDFStringTranslateTable[charCode];\n    strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));\n  }\n  return strBuf.join(\"\");\n}\nfunction stringToUTF8String(str) {\n  return decodeURIComponent(escape(str));\n}\nfunction utf8StringToString(str) {\n  return unescape(encodeURIComponent(str));\n}\nfunction isArrayBuffer(v) {\n  return typeof v === \"object\" && v?.byteLength !== undefined;\n}\nfunction isArrayEqual(arr1, arr2) {\n  if (arr1.length !== arr2.length) {\n    return false;\n  }\n  for (let i = 0, ii = arr1.length; i < ii; i++) {\n    if (arr1[i] !== arr2[i]) {\n      return false;\n    }\n  }\n  return true;\n}\nfunction getModificationDate(date = new Date()) {\n  const buffer = [date.getUTCFullYear().toString(), (date.getUTCMonth() + 1).toString().padStart(2, \"0\"), date.getUTCDate().toString().padStart(2, \"0\"), date.getUTCHours().toString().padStart(2, \"0\"), date.getUTCMinutes().toString().padStart(2, \"0\"), date.getUTCSeconds().toString().padStart(2, \"0\")];\n  return buffer.join(\"\");\n}\nclass PromiseCapability {\n  #settled = false;\n  constructor() {\n    this.promise = new Promise((resolve, reject) => {\n      this.resolve = data => {\n        this.#settled = true;\n        resolve(data);\n      };\n      this.reject = reason => {\n        this.#settled = true;\n        reject(reason);\n      };\n    });\n  }\n  get settled() {\n    return this.#settled;\n  }\n}\nlet NormalizeRegex = null;\nlet NormalizationMap = null;\nfunction normalizeUnicode(str) {\n  if (!NormalizeRegex) {\n    NormalizeRegex = /([\\u00a0\\u00b5\\u037e\\u0eb3\\u2000-\\u200a\\u202f\\u2126\\ufb00-\\ufb04\\ufb06\\ufb20-\\ufb36\\ufb38-\\ufb3c\\ufb3e\\ufb40-\\ufb41\\ufb43-\\ufb44\\ufb46-\\ufba1\\ufba4-\\ufba9\\ufbae-\\ufbb1\\ufbd3-\\ufbdc\\ufbde-\\ufbe7\\ufbea-\\ufbf8\\ufbfc-\\ufbfd\\ufc00-\\ufc5d\\ufc64-\\ufcf1\\ufcf5-\\ufd3d\\ufd88\\ufdf4\\ufdfa-\\ufdfb\\ufe71\\ufe77\\ufe79\\ufe7b\\ufe7d]+)|(\\ufb05+)/gu;\n    NormalizationMap = new Map([[\"ﬅ\", \"ſt\"]]);\n  }\n  return str.replaceAll(NormalizeRegex, (_, p1, p2) => {\n    return p1 ? p1.normalize(\"NFKC\") : NormalizationMap.get(p2);\n  });\n}\nfunction getUuid() {\n  if (typeof crypto !== \"undefined\" && typeof crypto?.randomUUID === \"function\") {\n    return crypto.randomUUID();\n  }\n  const buf = new Uint8Array(32);\n  if (typeof crypto !== \"undefined\" && typeof crypto?.getRandomValues === \"function\") {\n    crypto.getRandomValues(buf);\n  } else {\n    for (let i = 0; i < 32; i++) {\n      buf[i] = Math.floor(Math.random() * 255);\n    }\n  }\n  return bytesToString(buf);\n}\nconst AnnotationPrefix = \"pdfjs_internal_id_\";\n\n\n/***/ })\n\n/******/ });\n/************************************************************************/\n/******/ // The module cache\n/******/ var __webpack_module_cache__ = {};\n/******/ \n/******/ // The require function\n/******/ function __webpack_require__(moduleId) {\n/******/ \t// Check if module is in cache\n/******/ \tvar cachedModule = __webpack_module_cache__[moduleId];\n/******/ \tif (cachedModule !== undefined) {\n/******/ \t\treturn cachedModule.exports;\n/******/ \t}\n/******/ \t// Create a new module (and put it into the cache)\n/******/ \tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t// no module.id needed\n/******/ \t\t// no module.loaded needed\n/******/ \t\texports: {}\n/******/ \t};\n/******/ \n/******/ \t// Execute the module function\n/******/ \t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \n/******/ \t// Return the exports of the module\n/******/ \treturn module.exports;\n/******/ }\n/******/ \n/************************************************************************/\n/******/ /* webpack/runtime/async module */\n/******/ (() => {\n/******/ \tvar webpackQueues = typeof Symbol === \"function\" ? Symbol(\"webpack queues\") : \"__webpack_queues__\";\n/******/ \tvar webpackExports = typeof Symbol === \"function\" ? Symbol(\"webpack exports\") : \"__webpack_exports__\";\n/******/ \tvar webpackError = typeof Symbol === \"function\" ? Symbol(\"webpack error\") : \"__webpack_error__\";\n/******/ \tvar resolveQueue = (queue) => {\n/******/ \t\tif(queue && queue.d < 1) {\n/******/ \t\t\tqueue.d = 1;\n/******/ \t\t\tqueue.forEach((fn) => (fn.r--));\n/******/ \t\t\tqueue.forEach((fn) => (fn.r-- ? fn.r++ : fn()));\n/******/ \t\t}\n/******/ \t}\n/******/ \tvar wrapDeps = (deps) => (deps.map((dep) => {\n/******/ \t\tif(dep !== null && typeof dep === \"object\") {\n/******/ \t\t\tif(dep[webpackQueues]) return dep;\n/******/ \t\t\tif(dep.then) {\n/******/ \t\t\t\tvar queue = [];\n/******/ \t\t\t\tqueue.d = 0;\n/******/ \t\t\t\tdep.then((r) => {\n/******/ \t\t\t\t\tobj[webpackExports] = r;\n/******/ \t\t\t\t\tresolveQueue(queue);\n/******/ \t\t\t\t}, (e) => {\n/******/ \t\t\t\t\tobj[webpackError] = e;\n/******/ \t\t\t\t\tresolveQueue(queue);\n/******/ \t\t\t\t});\n/******/ \t\t\t\tvar obj = {};\n/******/ \t\t\t\tobj[webpackQueues] = (fn) => (fn(queue));\n/******/ \t\t\t\treturn obj;\n/******/ \t\t\t}\n/******/ \t\t}\n/******/ \t\tvar ret = {};\n/******/ \t\tret[webpackQueues] = x => {};\n/******/ \t\tret[webpackExports] = dep;\n/******/ \t\treturn ret;\n/******/ \t}));\n/******/ \t__webpack_require__.a = (module, body, hasAwait) => {\n/******/ \t\tvar queue;\n/******/ \t\thasAwait && ((queue = []).d = -1);\n/******/ \t\tvar depQueues = new Set();\n/******/ \t\tvar exports = module.exports;\n/******/ \t\tvar currentDeps;\n/******/ \t\tvar outerResolve;\n/******/ \t\tvar reject;\n/******/ \t\tvar promise = new Promise((resolve, rej) => {\n/******/ \t\t\treject = rej;\n/******/ \t\t\touterResolve = resolve;\n/******/ \t\t});\n/******/ \t\tpromise[webpackExports] = exports;\n/******/ \t\tpromise[webpackQueues] = (fn) => (queue && fn(queue), depQueues.forEach(fn), promise[\"catch\"](x => {}));\n/******/ \t\tmodule.exports = promise;\n/******/ \t\tbody((deps) => {\n/******/ \t\t\tcurrentDeps = wrapDeps(deps);\n/******/ \t\t\tvar fn;\n/******/ \t\t\tvar getResult = () => (currentDeps.map((d) => {\n/******/ \t\t\t\tif(d[webpackError]) throw d[webpackError];\n/******/ \t\t\t\treturn d[webpackExports];\n/******/ \t\t\t}))\n/******/ \t\t\tvar promise = new Promise((resolve) => {\n/******/ \t\t\t\tfn = () => (resolve(getResult));\n/******/ \t\t\t\tfn.r = 0;\n/******/ \t\t\t\tvar fnQueue = (q) => (q !== queue && !depQueues.has(q) && (depQueues.add(q), q && !q.d && (fn.r++, q.push(fn))));\n/******/ \t\t\t\tcurrentDeps.map((dep) => (dep[webpackQueues](fnQueue)));\n/******/ \t\t\t});\n/******/ \t\t\treturn fn.r ? promise : getResult();\n/******/ \t\t}, (err) => ((err ? reject(promise[webpackError] = err) : outerResolve(exports)), resolveQueue(queue)));\n/******/ \t\tqueue && queue.d < 0 && (queue.d = 0);\n/******/ \t};\n/******/ })();\n/******/ \n/******/ /* webpack/runtime/define property getters */\n/******/ (() => {\n/******/ \t// define getter functions for harmony exports\n/******/ \t__webpack_require__.d = (exports, definition) => {\n/******/ \t\tfor(var key in definition) {\n/******/ \t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t}\n/******/ \t\t}\n/******/ \t};\n/******/ })();\n/******/ \n/******/ /* webpack/runtime/hasOwnProperty shorthand */\n/******/ (() => {\n/******/ \t__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))\n/******/ })();\n/******/ \n/************************************************************************/\n/******/ \n/******/ // startup\n/******/ // Load entry module and return exports\n/******/ // This entry module used 'module' so it can't be inlined\n/******/ var __webpack_exports__ = __webpack_require__(907);\n/******/ __webpack_exports__ = globalThis.pdfjsLib = await (globalThis.pdfjsLibPromise = __webpack_exports__);\n/******/ var __webpack_exports__AbortException = __webpack_exports__.AbortException;\n/******/ var __webpack_exports__AnnotationEditorLayer = __webpack_exports__.AnnotationEditorLayer;\n/******/ var __webpack_exports__AnnotationEditorParamsType = __webpack_exports__.AnnotationEditorParamsType;\n/******/ var __webpack_exports__AnnotationEditorType = __webpack_exports__.AnnotationEditorType;\n/******/ var __webpack_exports__AnnotationEditorUIManager = __webpack_exports__.AnnotationEditorUIManager;\n/******/ var __webpack_exports__AnnotationLayer = __webpack_exports__.AnnotationLayer;\n/******/ var __webpack_exports__AnnotationMode = __webpack_exports__.AnnotationMode;\n/******/ var __webpack_exports__CMapCompressionType = __webpack_exports__.CMapCompressionType;\n/******/ var __webpack_exports__DOMSVGFactory = __webpack_exports__.DOMSVGFactory;\n/******/ var __webpack_exports__DrawLayer = __webpack_exports__.DrawLayer;\n/******/ var __webpack_exports__FeatureTest = __webpack_exports__.FeatureTest;\n/******/ var __webpack_exports__GlobalWorkerOptions = __webpack_exports__.GlobalWorkerOptions;\n/******/ var __webpack_exports__ImageKind = __webpack_exports__.ImageKind;\n/******/ var __webpack_exports__InvalidPDFException = __webpack_exports__.InvalidPDFException;\n/******/ var __webpack_exports__MissingPDFException = __webpack_exports__.MissingPDFException;\n/******/ var __webpack_exports__OPS = __webpack_exports__.OPS;\n/******/ var __webpack_exports__Outliner = __webpack_exports__.Outliner;\n/******/ var __webpack_exports__PDFDataRangeTransport = __webpack_exports__.PDFDataRangeTransport;\n/******/ var __webpack_exports__PDFDateString = __webpack_exports__.PDFDateString;\n/******/ var __webpack_exports__PDFWorker = __webpack_exports__.PDFWorker;\n/******/ var __webpack_exports__PasswordResponses = __webpack_exports__.PasswordResponses;\n/******/ var __webpack_exports__PermissionFlag = __webpack_exports__.PermissionFlag;\n/******/ var __webpack_exports__PixelsPerInch = __webpack_exports__.PixelsPerInch;\n/******/ var __webpack_exports__PromiseCapability = __webpack_exports__.PromiseCapability;\n/******/ var __webpack_exports__RenderingCancelledException = __webpack_exports__.RenderingCancelledException;\n/******/ var __webpack_exports__UnexpectedResponseException = __webpack_exports__.UnexpectedResponseException;\n/******/ var __webpack_exports__Util = __webpack_exports__.Util;\n/******/ var __webpack_exports__VerbosityLevel = __webpack_exports__.VerbosityLevel;\n/******/ var __webpack_exports__XfaLayer = __webpack_exports__.XfaLayer;\n/******/ var __webpack_exports__build = __webpack_exports__.build;\n/******/ var __webpack_exports__createValidAbsoluteUrl = __webpack_exports__.createValidAbsoluteUrl;\n/******/ var __webpack_exports__fetchData = __webpack_exports__.fetchData;\n/******/ var __webpack_exports__getDocument = __webpack_exports__.getDocument;\n/******/ var __webpack_exports__getFilenameFromUrl = __webpack_exports__.getFilenameFromUrl;\n/******/ var __webpack_exports__getPdfFilenameFromUrl = __webpack_exports__.getPdfFilenameFromUrl;\n/******/ var __webpack_exports__getXfaPageViewport = __webpack_exports__.getXfaPageViewport;\n/******/ var __webpack_exports__isDataScheme = __webpack_exports__.isDataScheme;\n/******/ var __webpack_exports__isPdfFile = __webpack_exports__.isPdfFile;\n/******/ var __webpack_exports__noContextMenu = __webpack_exports__.noContextMenu;\n/******/ var __webpack_exports__normalizeUnicode = __webpack_exports__.normalizeUnicode;\n/******/ var __webpack_exports__renderTextLayer = __webpack_exports__.renderTextLayer;\n/******/ var __webpack_exports__setLayerDimensions = __webpack_exports__.setLayerDimensions;\n/******/ var __webpack_exports__shadow = __webpack_exports__.shadow;\n/******/ var __webpack_exports__updateTextLayer = __webpack_exports__.updateTextLayer;\n/******/ var __webpack_exports__version = __webpack_exports__.version;\n/******/ export { __webpack_exports__AbortException as AbortException, __webpack_exports__AnnotationEditorLayer as AnnotationEditorLayer, __webpack_exports__AnnotationEditorParamsType as AnnotationEditorParamsType, __webpack_exports__AnnotationEditorType as AnnotationEditorType, __webpack_exports__AnnotationEditorUIManager as AnnotationEditorUIManager, __webpack_exports__AnnotationLayer as AnnotationLayer, __webpack_exports__AnnotationMode as AnnotationMode, __webpack_exports__CMapCompressionType as CMapCompressionType, __webpack_exports__DOMSVGFactory as DOMSVGFactory, __webpack_exports__DrawLayer as DrawLayer, __webpack_exports__FeatureTest as FeatureTest, __webpack_exports__GlobalWorkerOptions as GlobalWorkerOptions, __webpack_exports__ImageKind as ImageKind, __webpack_exports__InvalidPDFException as InvalidPDFException, __webpack_exports__MissingPDFException as MissingPDFException, __webpack_exports__OPS as OPS, __webpack_exports__Outliner as Outliner, __webpack_exports__PDFDataRangeTransport as PDFDataRangeTransport, __webpack_exports__PDFDateString as PDFDateString, __webpack_exports__PDFWorker as PDFWorker, __webpack_exports__PasswordResponses as PasswordResponses, __webpack_exports__PermissionFlag as PermissionFlag, __webpack_exports__PixelsPerInch as PixelsPerInch, __webpack_exports__PromiseCapability as PromiseCapability, __webpack_exports__RenderingCancelledException as RenderingCancelledException, __webpack_exports__UnexpectedResponseException as UnexpectedResponseException, __webpack_exports__Util as Util, __webpack_exports__VerbosityLevel as VerbosityLevel, __webpack_exports__XfaLayer as XfaLayer, __webpack_exports__build as build, __webpack_exports__createValidAbsoluteUrl as createValidAbsoluteUrl, __webpack_exports__fetchData as fetchData, __webpack_exports__getDocument as getDocument, __webpack_exports__getFilenameFromUrl as getFilenameFromUrl, __webpack_exports__getPdfFilenameFromUrl as getPdfFilenameFromUrl, __webpack_exports__getXfaPageViewport as getXfaPageViewport, __webpack_exports__isDataScheme as isDataScheme, __webpack_exports__isPdfFile as isPdfFile, __webpack_exports__noContextMenu as noContextMenu, __webpack_exports__normalizeUnicode as normalizeUnicode, __webpack_exports__renderTextLayer as renderTextLayer, __webpack_exports__setLayerDimensions as setLayerDimensions, __webpack_exports__shadow as shadow, __webpack_exports__updateTextLayer as updateTextLayer, __webpack_exports__version as version };\n/******/ \n\n//# sourceMappingURL=pdf.mjs.map"
  },
  {
    "path": "public/assets/lib/vendor/pdfjs/pdf.sandbox.js",
    "content": "/**\n * @licstart The following is the entire license notice for the\n * JavaScript code in this page\n *\n * Copyright 2023 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * @licend The above is the entire license notice for the\n * JavaScript code in this page\n */\n\n/******/ // The require scope\n/******/ var __webpack_require__ = {};\n/******/ \n/************************************************************************/\n/******/ /* webpack/runtime/define property getters */\n/******/ (() => {\n/******/ \t// define getter functions for harmony exports\n/******/ \t__webpack_require__.d = (exports, definition) => {\n/******/ \t\tfor(var key in definition) {\n/******/ \t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t}\n/******/ \t\t}\n/******/ \t};\n/******/ })();\n/******/ \n/******/ /* webpack/runtime/hasOwnProperty shorthand */\n/******/ (() => {\n/******/ \t__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))\n/******/ })();\n/******/ \n/************************************************************************/\nvar __webpack_exports__ = globalThis.pdfjsSandbox = {};\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n  QuickJSSandbox: () => (/* binding */ QuickJSSandbox)\n});\n\n;// CONCATENATED MODULE: ./external/quickjs/quickjs-eval.js\nvar Module=(()=>{var _scriptDir=typeof document!=='undefined'&&document.currentScript?document.currentScript.src:undefined;return function(Module){Module=Module||{};var c;c||(c=typeof Module!=='undefined'?Module:{});var h,n;c.ready=new Promise(function(a,b){h=a;n=b;});var r=Object.assign({},c),t=\"\";\"undefined\"!=typeof document&&document.currentScript&&(t=document.currentScript.src);_scriptDir&&(t=_scriptDir);0!==t.indexOf(\"blob:\")?t=t.substr(0,t.replace(/[?#].*/,\"\").lastIndexOf(\"/\")+1):t=\"\";var aa=c.print||console.log.bind(console),u=c.printErr||console.warn.bind(console);Object.assign(c,r);r=null;var v;c.wasmBinary&&(v=c.wasmBinary);var noExitRuntime=c.noExitRuntime||!0;\"object\"!=typeof WebAssembly&&w(\"no native wasm support detected\");var x,y=!1;function z(a,b,d,e){var f={string:function(l){var p=0;if(null!==l&&void 0!==l&&0!==l){var S=(l.length<<2)+1;p=A(S);B(l,C,p,S);}return p;},array:function(l){var p=A(l.length);D.set(l,p);return p;}};a=c[\"_\"+a];var g=[],k=0;if(e)for(var m=0;m<e.length;m++){var q=f[d[m]];q?(0===k&&(k=E()),g[m]=q(e[m])):g[m]=e[m];}d=a.apply(null,g);return d=function(l){0!==k&&F(k);return\"string\"===b?G(l):\"boolean\"===b?!!l:l;}(d);}var H=\"undefined\"!=typeof TextDecoder?new TextDecoder(\"utf8\"):void 0;function I(a,b){for(var d=b+NaN,e=b;a[e]&&!(e>=d);)++e;if(16<e-b&&a.buffer&&H)return H.decode(a.subarray(b,e));for(d=\"\";b<e;){var f=a[b++];if(f&128){var g=a[b++]&63;if(192==(f&224))d+=String.fromCharCode((f&31)<<6|g);else{var k=a[b++]&63;f=224==(f&240)?(f&15)<<12|g<<6|k:(f&7)<<18|g<<12|k<<6|a[b++]&63;65536>f?d+=String.fromCharCode(f):(f-=65536,d+=String.fromCharCode(55296|f>>10,56320|f&1023));}}else d+=String.fromCharCode(f);}return d;}function G(a){return a?I(C,a):\"\";}function B(a,b,d,e){if(0<e){e=d+e-1;for(var f=0;f<a.length;++f){var g=a.charCodeAt(f);if(55296<=g&&57343>=g){var k=a.charCodeAt(++f);g=65536+((g&1023)<<10)|k&1023;}if(127>=g){if(d>=e)break;b[d++]=g;}else{if(2047>=g){if(d+1>=e)break;b[d++]=192|g>>6;}else{if(65535>=g){if(d+2>=e)break;b[d++]=224|g>>12;}else{if(d+3>=e)break;b[d++]=240|g>>18;b[d++]=128|g>>12&63;}b[d++]=128|g>>6&63;}b[d++]=128|g&63;}}b[d]=0;}}function J(a){for(var b=0,d=0;d<a.length;++d){var e=a.charCodeAt(d);55296<=e&&57343>=e&&(e=65536+((e&1023)<<10)|a.charCodeAt(++d)&1023);127>=e?++b:b=2047>=e?b+2:65535>=e?b+3:b+4;}return b;}function K(a){var b=J(a)+1,d=L(b);d&&B(a,D,d,b);return d;}var M,D,C,N;function ba(){var a=x.buffer;M=a;c.HEAP8=D=new Int8Array(a);c.HEAP16=new Int16Array(a);c.HEAP32=N=new Int32Array(a);c.HEAPU8=C=new Uint8Array(a);c.HEAPU16=new Uint16Array(a);c.HEAPU32=new Uint32Array(a);c.HEAPF32=new Float32Array(a);c.HEAPF64=new Float64Array(a);}var O,ca=[],da=[],ea=[];function fa(){var a=c.preRun.shift();ca.unshift(a);}var P=0,Q=null,R=null;function w(a){if(c.onAbort)c.onAbort(a);a=\"Aborted(\"+a+\")\";u(a);y=!0;a=new WebAssembly.RuntimeError(a+\". Build with -sASSERTIONS for more info.\");n(a);throw a;}function T(a){return a.startsWith(\"data:application/octet-stream;base64,\");}var U;U=\"data:application/octet-stream;base64,AGFzbQEAAAAByQZtYAR/fn9/AX5gA39/fwF/YAJ/fwF/YAJ/fwBgAX8Bf2AFf35/f38BfmADf39/AGABfwBgAXwBfGACf34BfmAEf39/fwF/YAJ/fgBgAn9/AX5gAn9+AX9gA39/fgF/YAN/fn8BfmABfgF/YAN/fn8AYAZ/fn9/f38BfmADf35/AX9gBX9/f39/AX9gBn9+fn9/fwF+YAN/fn4BfmAEf39/fwBgBH9/fn8Bf2ADf39/AX5gBH9/f38BfmAGf39/f39/AX9gBX9+fn5+AGABfwF+YAN/fn4Bf2ACfHwBfGABfgF+YAV/fn9+fwF/YAV/fn5/fwF+YAd/fn9+fn5/AX9gAABgBX9/f39/AGAEf35+fwBgBX9+fn5/AX9gB39/f39/f38Bf2AGf35/fn5/AX9gBH9+f34BfmACfn8Bf2AEf35+fwF/YAJ+fwBgCX9/f39/f39/fwF/YAR/fn5/AX5gBn9/f39/fwF+YAN/fn4AYAR/fn9/AX9gBX9+fn9/AGACfn4BfmAHf35/f39/fwF+YAF/AXxgA39/fgBgBH9+f38AYAR/fn9+AX9gBH9+fn4Bf2AEf39/fgF/YAh/f39/f39/fwF/YAd/f39/f39/AGACfH8BfGADfn9/AX9gA3x8fwF8YAR/f35/AGAEf35+fgF+YAABf2AGf3x/f39/AX9gAAF8YAV/fn9+fwF+YAF8AX5gAX4BfGAFf39+f38Bf2AGf39+fn5+AX9gBn9/f39/fwBgAn98AGAEf39+fwF+YAR/fn9+AGAFf39/f34BfmAHf35+fn9/fwF+YAR+fn5+AX9gCn9/f39/f39/f38Bf2AHf39/f39/fgF+YAd8f39/f39/AGADf39+AX5gBX9/f39/AX5gBX9+f39/AGAFf3x/f38BfmAGf35+fn5/AX9gBH98f38Bf2AGf35/f39/AX9gBX9/fn5/AX9gBX9+f39/AX9gBn9/fn5/fwF/YAV/fn5+fgF/YAJ/fwF8YAR/f35+AX9gBX9/fn5+AX5gB39/f35+fn8Bf2AEf39/fgF+YAJ8fwF/YAJ8fAF/YAh/fn5+fn9+fgF+YAN/fnwBfmAAAX5gAn5+AXxgA35+fgF/YAN/f3wAAkkMAWEBYQAXAWEBYgAkAWEBYwAKAWEBZABFAWEBZQADAWEBZgAGAWEBZwADAWEBaAABAWEBaQA2AWEBagAEAWEBawAHAWEBbAAXA9QI0ggLEAMgAwQQA0YGBkcDAgMhAwEDNwMDEBEiATgLEAcECQENCQICAwwcBgQiAw0dAw0dCQIGKw4BBAcEBw45SAIBAwIDCgYdBw8CCRAKAQoeDgQDBAMMAQQJFkkGBgYNEwMCJQMPOgccJgEHDAEjARMPBBwCARRKBAoDBBAYBgEBAiwtAg0QOhQdCwQCBw0EBBMNGhAhCRYNLQwGDS4EAQdLCgMnLw4EABMCEAEKTAYBAjwEBk0CBA0PDg4ODgYHAjAGAgIxTk8UEz0bBwQUARYCDhMyLAEnA1ABAjABAgc+ASE9AgcHAgQWAwQPEAQNAwQJARlRBAYzAgYDUgIEFChTBQ0/Aw4DAQ4eAjkhDQkBLAIBAwcEJgMEKwEICAQEGwIHBiUJFgYUAQQCBgEEDgUyAzRUAgIEDFVWBAVXARYXB1gnGA8DFAYGAgECARkKCEAfBAQCAgoBBAIEAgYNADAEGRoKAQIKBgoBBAMEAQQBAgM0QQ4gAFkXDwQDWgQMBwMWAyINDBkbD1sGAQEGIgUPAw0DCgICXAECAgwrEDgKFwMBBxcCCC0IBAIBAQYKBAEEPAIGAwkUAQMAAgMBCgIuAQcBAgICFA0BCgIKCgIXIBBdNwMTAxAEEwQCBBYOBxcUAwIGEQNeXy8ZEBsIYAlhYgBCGgIdHQ0WAQINKTEKDhUADj8KAwQCAQRjGAkNEAQZCQMGDxgCAgMCAxwGFGQHAgEECAdlCCQbAgICFwQHBAoEAgECBAECKCgCAWYADw8BAQ0JBAEAAGcgCQUABSEAHhsbAQQDAy4UAQEDAgICCxABAwIEAQIBBwIMFAQEBCA0BWgyQSQDCQMDCwYGAQ4qCQoHDAADIAEGFQkQHh8FDAcQAw8FGwoXAQIHEQwFAGkOAwMDJgUFBSUCGjUMAgIiAgEEAgIDBgEHAiceEwwYQgMODgYJCgINDhhDDAceHiUBEAMEGQEZBAECAgIBAwAKBWoxHGsDAgIEFwQoPkA2HRwmHAQCAx8EbAYHHwEACB8CCAA1AAAGBgYGBgYGBgYGBQUAAAABDAEMAQwBDAEMAQwBDAEMBQAkBQEAAAAABQAACQAFAA8JAAUPEgAACQAAAAAAAAAAAAAAAAgAAAgIBRIFBQAAAAUFBQAAAAAABQUFBQAAAAAAAAAAAAAAAAAABQAAAAAAAAUAAAUFAwAAAAAABQAABQEAAAAFAAAFAAUFAAkJAAAAAAUFFgkAAAAAAAAAAAAFAAAABQUAAAAFAAUAAAIAAAAAAAAFAAAAEgUSBQAAEgUSEhIAAAAZEQsRCwsLEQsSEgUFDwUFBQUFEgApKhMjEzsYEQsAABIJAAAAAAAAAAAPCQkAIxMYExIZIwEaGhoBAxELEQsLCxELEQsLCxELEQsRCxELCxELEQsGGRUVFRUBAwMDFRUVFQAEB0MAAQADRAgICAAPAQUICAgICAEPCAgICBUICAgfCAgIAwQHAXAB+AL4AgUHAQGAAoCAAgYJAX8BQZDBxAILB0ANAW0CAAFuALMEAW8A3QgBcACBBQFxAL8HAXIAiAcBcwCzBgF0AKMCAXUA6QEBdgEAAXcAvQgBeAC8CAF5ALsICfUFAQBBAQv3ApUEsQiwCK8Irgi1CLQIswjBB9sEqweQB4MH6gbpAr4GsgbJBJ4GkQaQBo8GjgbVCIkGyQjGCMAIvgjsBboIuQi4CLcItgjqBYQErQiyCIsImgWKCOcB4QfYB6wIjQiQBesH1AfTB9IH0AfMB8oHkge1BqsIqgipCKgIpwinBaYIpQikCKMIogihCKAInwieCJ0InAibCJoImQiYCPADlwjwA5YIlQiUCJMIjAiICIcIhgiJCKUFkgiRCPUH9AfzB/IH8QfwB+8H7gftB+AH3wfeB/AD3QenBdwH2wfaB9kHkAiPCI4IhQiECIMIggiBCIAI/wf+B/0H/Af7B/oH+Qf4B/cH9gfsB+oH6QfoB+cH5gflB+QH4wfiB9cH1gfVB4wC0QfPB84HzQfLB8kHqQXIB8cHxgfFB/0ExAfDB8IHqgXAB74HvQe8B7sHuge5B7gHtweyBbYHtQfZBLQHsweyB9cEsQewB68HrgfYBK0HrAeqB6kHqAenB6YHpQekB6MHmgOiB6EHoAefB54HnQecB5sHmgeZB5gHlwf9A5YHlQexBbMFlAeTB5EHjweOB40HjAeLB4oHiQfTBNIEhweGB4UHhAeCB4EHgAf/Bv4G/Qb8BvsG+gb5BvgG9wb2BvUG9AbzBvIG8QbwBu8G7gbtBuwG6wbpBugG5wbmBuUG5AbjBuIG4QbgBt8G3gbdBtwG2wbSCNEI1gjaBsoIjQbbCLIE2QjUCK8E2gKZBcwIxQjDCNkG0wjLCMQI3AjaCNgIpgKzA80IzgjXCNgG1wbWBtUG1AbTBtIG0QbQBs8GzgbNBswGywbKBskGyAbHBsYGxQbEBsMGwgbLBMEGygTABr8GvQa8BrsGuga5BrgGtwa2BrQGsQagBp8GnQacBq4GsAasBqoGqAamBqQGogatBq8GqwapBqcGpQajBqEGxwSbBpoGmQaYBpcGlgaVBpQGkwaSBoUExwTQCIgGzwiVBJUEyAjHCMIIwQi/CArDuBLSCDUBAX8CQCABQiCIp0F1SQ0AIAGnIgIgAigCACICQQFrNgIAIAJBAUoNACAAKAIQIAEQhgULCxMAIABCgICAgHCDQoCAgIDgAFELTQECfyAAKAJAIgJBgAJqIQMgAigCnAIgACgCBEcEQCADQcABEBAgAyAAKAIEEB4gAiAAKAIENgKcAgsgAiACKAKEAjYCmAIgAyABEBALIgEBfyAAQiCIp0F1TwRAIACnIgEgASgCAEEBajYCAAsgAAsoAQF/IwBBEGsiAiQAIAIgAToADyAAIAJBD2pBARCKARogAkEQaiQAC5sWAgZ/AX4jAEEQayICJAAgACAAQRBqIgQQjwIgACAAKAI4IgE2AjQgAiABNgIMIABBADYCMCAAIAAoAhQ2AgQDQCAAIAE2AhggACAAKAIIIgM2AhQCQAJAAn8CQAJAAkACQAJAAkACfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgASwAACIFQf8BcSIGDn0AFxcXFxcXFxcEAwQEAhcXFxcXFxcXFxcXFxcXFxcXFwQSGAgHDBMYFxcLDRcOCQUKHBwcHBwcHBwcFxcPERAWFwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHFwYXFAcBBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcXFRcLIAEgACgCPEkNGiAEQap/NgIADB8LIAAgAUEBahDZAw0cIAIgACgCODYCDAweCyABQQFqIAEgAS0AAUEKRhshAQsgAiABQQFqNgIMDB4LIAIgAUEBajYCDAweCwJAAkAgAS0AASIDQSpHBEAgA0EvRg0BIANBPUcNAiACIAFBAmo2AgwgBEGGfzYCAAwdCyACIAFBAmoiATYCDANAAkACQAJAAkACQAJAIAEtAAAiA0EKaw4EAQMDAgALIANBKkcEQCADDQMgASAAKAI8SQ0EIABB3RhBABAVDCELIAEtAAFBL0cNAyACIAFBAmo2AgwMJQsgAEEBNgIwIAAgACgCCEEBajYCCCACIAFBAWo2AgwMAwsgAEEBNgIwIAIgAUEBajYCDAwCCyADQRh0QRh1QQBODQAgAUEGIAJBDGoQYSIBQX5xQajAAEYEQCAAQQE2AjAMAgsgAUF/Rw0BIAIgAigCDEEBajYCDAwBCyACIAFBAWo2AgwLIAIoAgwhAQwACwALIAFBAmohAUEADBULIAIgAUEBajYCDCAEQS82AgAMGgsgAS0AAUH1AEcNFCACIAFBAWo2AgQCQCACQQRqQQEQgwIiAUEATgRAIAEQxQINAQsgAigCDCEBDBULIAIgAigCBDYCDCACQQE2AggMFgsgAkEANgIIIAIgAUEBajYCDCAGIQEMFQsgAiABQQFqIgU2AgwgAiABQQJqNgIEQdwAIQMCQCABLQABIgZB3ABGBEAgAS0AAkH1AEcNASACQQRqQQEQgwIhAwwBCyAGIgNBGHRBGHVBAE4NACAFQQYgAkEEahBhIQMLIAMQxQJFBEAgAEGpzwBBABAVDBYLIAIgAigCBDYCDCACQQA2AgggACACQQxqIAJBCGogA0EBEPcEIgFFDRUgAEGpfzYCECAAIAE2AiAMFwsgAS0AASIDQS5GBEAgAS0AAkEuRw0SIAIgAUEDajYCDCAEQaV/NgIADBcLIANBMGtB/wFxQQpPDREMEgsgAS0AARBFRQ0RIAAoAkAtAG5BAXFFDREgAEHP1ABBABAVDBMLIAEtAAEiA0EqRwRAIANBPUcNECACIAFBAmo2AgwgBEGFfzYCAAwVCyABLQACQT1GBEAgAiABQQNqNgIMIARBkH82AgAMFQsgAiABQQJqNgIMIARBo382AgAMFAsgAS0AAUE9Rw0OIAIgAUECajYCDCAEQYd/NgIADBMLIAEtAAEiA0ErRwRAIANBPUcNDiACIAFBAmo2AgwgBEGIfzYCAAwTCyACIAFBAmo2AgwgBEGVfzYCAAwSCyABLQABIgVBLUcEQCAFQT1HDQ0gAiABQQJqNgIMIARBiX82AgAMEgsCQCAAKAJIRQ0AIAEtAAJBPkcNACAAKAIEIANHDQsLIAIgAUECajYCDCAEQZR/NgIADBELAkACQAJAIAEtAAEiA0E8aw4CAQACCyACIAFBAmo2AgwgBEGafzYCAAwSCyABLQACQT1GBEAgAiABQQNqNgIMIARBin82AgAMEgsgAiABQQJqNgIMIARBln82AgAMEQsgACgCSEUgA0EhR3INCyABLQACQS1HDQsgAS0AA0EtRg0JDAsLAkACQCABLQABQT1rDgIAAQwLIAIgAUECajYCDCAEQZx/NgIADBALAkACQAJAIAEtAAJBPWsOAgEAAgsgAS0AA0E9RgRAIAIgAUEEajYCDCAEQYx/NgIADBILIAIgAUEDajYCDCAEQZh/NgIADBELIAIgAUEDajYCDCAEQYt/NgIADBALIAIgAUECajYCDCAEQZd/NgIADA8LAkACQCABLQABQT1rDgIAAQsLIAEtAAJBPUYEQCACIAFBA2o2AgwgBEGefzYCAAwQCyACIAFBAmo2AgwgBEGdfzYCAAwPCyACIAFBAmo2AgwgBEGkfzYCAAwOCyABLQABQT1HDQggAS0AAkE9RgRAIAIgAUEDajYCDCAEQaB/NgIADA4LIAIgAUECajYCDCAEQZ9/NgIADA0LIAEtAAEiA0EmRwRAIANBPUcNCCACIAFBAmo2AgwgBEGNfzYCAAwNCyABLQACQT1GBEAgAiABQQNqNgIMIARBkX82AgAMDQsgAiABQQJqNgIMIARBoX82AgAMDAsgAS0AAUE9Rw0GIAIgAUECajYCDCAEQY5/NgIADAsLIAEtAAEiA0H8AEcEQCADQT1HDQYgAiABQQJqNgIMIARBj382AgAMCwsgAS0AAkE9RgRAIAIgAUEDajYCDCAEQZJ/NgIADAsLIAIgAUECajYCDCAEQaJ/NgIADAoLIAEtAAEiA0EuRwRAIANBP0cNBSABLQACQT1GBEAgAiABQQNqNgIMIARBk382AgAMCwsgAiABQQJqNgIMIARBpn82AgAMCgsgAS0AAkEwa0H/AXFBCkkNBCACIAFBAmo2AgwgBEGnfzYCAAwJCyAFQQBODQMgAUEGIAJBDGoQYSIBQX5xQajAAEYEQCAAKAIIIQMMCwsgARDlAg0LIAEQxQIEQCACQQA2AggMBgsgAEGOL0EAEBUMBgsgACAGQQEgAUEBaiAEIAJBDGoQkgNFDQcMBQtBAQshAwNAAn8CQAJAAkACQCADRQRAIAIgATYCDAwBCyABLQAAIgNFDQICQCADQQprDgQPAAAPAAsgA0EYdEEYdUEATg0DIAFBBiACQQxqEGEiA0F+cUGowABGDQ4gAigCDCEBIANBf0YNAQtBASEDDAQLIAFBAWoMAgsgASAAKAI8Tw0LCyABQQFqCyEBQQAhAwwACwALIAQgBjYCACACIAFBAWo2AgwMBAsgACgCACABIAJBDGpBAEE0EMQCIgcQDQ0BAkAgB0KAgICAcINCgICAgMB+UgRAIAIoAgxBBiACQQhqEGEQwQFFDQELIAAoAgAgBxAMIABB/j5BABAVDAILIABBgH82AhAgACAHNwMgDAMLIAAgAkEMaiACQQhqIAFBABD3BCIBRQ0AIAAgATYCICACKAIIIQYgAEEANgIoIAAgBjYCJAJAIAFBJUkNACABQS1NBEAgACgCQCIDLQBuQQFxDQEgAUEtRw0DIAMvAWwiBUEBcQ0BIAVBgP4DcUGABkcNAyADKAJkDQMgAygCBCIDRQ0DIAMtAGxBAXENAQwDCyABQS5HDQIgACgCRA0AIAAoAkAiAy8BbCIFQQJxDQAgBUGA/gNxQYAGRw0CIAMoAmQNAiADKAIEIgNFDQIgAy0AbEECcUUNAgsgBgRAIABBg382AhAgAEEBNgIoDAMLIAQgAUHWAGs2AgAMAgsgBEGofzYCAEF/DAILIARBg382AgALIAAgAigCDDYCOEEACyEAIAJBEGokACAADwsgAEEBNgIwIAAgA0EBajYCCAsgAigCDCEBDAALAAsSACAAQoCAgIBwg0KAgICAMFELFQAgARDyAUUEQCAAKAIQIAEQhAULC9AGAgV/AX4jAEEgayIHJABCgICAgOAAIQoCQAJAAkACQAJAAkACQAJAAkACQCABQiCIpyIGQQFqDggDBQUAAQUFCQILIAAgAkHHPRDIAQwGCyAAIAJBwOAAEMgBDAULIAZBeUYNAQwCCyABpyEGDAILIAGnIQYgAhBeBEAgAhB8IgUgBikCBCIKp0H/////B3FPDQEgBkEQaiECIAACfyAKQoCAgIAIg1BFBEAgAiAFQQF0ai8BAAwBCyACIAVqLQAAC0H//wNxEKYDIQoMBQsgAkEwRw0AIAYpAgRC/////weDIQoMBAsgACABEJ0EpyIGRQ0CCwNAIAYoAhAiCCAIKAIYIAJxQX9zQQJ0aigCACEFIAgQKiEJAkADQCAFRQ0BIAIgCSAFQQFrQQN0IgVqIggoAgRHBEAgCCgCAEH///8fcSEFDAELCyAGKAIUIAVqIQUCQAJAAkACQCAIKAIAQR52QQFrDgMAAQIDCyAFKAIAIgJFDQYgACACrUKAgICAcIQQDyADQQBBABA2IQoMBwsgBSgCACgCECkDACIBEIYBBEAgACACEOIBDAULIAEQDyEKDAYLIAAgBiACIAUgCBDRAkUNAgwDCyAFKQMAEA8hCgwECwJAIAYtAAUiBUEEcUUNACAFQQhxBEAgAhBeBEAgAhB8IgUgBigCKEkEQCAAIAatQoCAgIBwhCAFEHshCgwHCyAGLwEGQRVrQf//A3FBCUkNBQwCCyAGLwEGQRVrQf//A3FBCEsNASAAIAIQpQMiBUUNAUKAgICA4ABCgICAgDAgBUEASBshCgwFCyAAKAIQKAJEIAYvAQZBGGxqKAIUIgVFDQAgBSgCFARAIAAgBq1CgICAgHCEEA8iASACIAMgBSgCFBEqACEKIAAgARAMDAULIAUoAgBFDQAgACAHIAatQoCAgIBwhBAPIgEgAiAFKAIAERgAIQUgACABEAwgBUEASA0CIAVFDQAgBy0AAEEQcQRAIAAgBykDGBAMIAAgBykDECADQQBBABA2IQoMBQsgBykDCCEKDAQLIAYoAhAoAiwiBg0AC0KAgICAMCEKIARFDQIgACACENACC0KAgICA4AAhCgwBC0KAgICAMCEKCyAHQSBqJAAgCgtfAQJ/IwBBEGsiBCQAIAAoAgAhAyAEIAI2AgwgA0EDIAEgAkEAENsFIAMgAygCECkDgAEgACgCDCAAKAIIIAAoAkAiAAR/IAAoAmhBAEdBAXQFQQALEMcCIARBEGokAAsNACAAIAEgAkEEEK8DCzcBAX5CgICAgMB+IAC9IgFCgICAgMCBgPz/AH0gAUL///////////8Ag0KAgICAgICA+P8AVhsLDwAgACgCQEGAAmogARAxCysAIAEQ8gFFBEAgACgCECgCOCABQQJ0aigCACIAIAAoAgBBAWo2AgALIAELCwAgACgCECABECELKQAgACABIAIgA0KAgICAMEKAgICAMCAEQYDOAHIQeCECIAAgAxAMIAILDwAgACAAKAIAIAEQGRA6C0oAIAAQ9QJFBEBBfw8LIAJBAEgEQCAAEDUhAgsgACABQf8BcRAOIAAgAhA6IAAoAkAoAqQCIAJBFGxqIgAgACgCAEEBajYCACACCygBAX8jAEEQayICJAAgAiABNgIMIAAgAkEMakEEEIoBGiACQRBqJAALGAEBfiABKQMAIQMgASACNwMAIAAgAxAMCzEAIAFBAE4EQCAAQbQBEA4gACABEDogACgCQCIAKAKkAiABQRRsaiAAKAKEAjYCBAsLEQAgAEEQaiABIAAoAgQRAwALCwAgAEL/////b1YLGAAgAUKAgICAYFoEQCAAIAGnIAIRAwALCxcAIAAgASACQoCAgIAwIAMgBEECEOMBCzMBAX8gAgRAIAAhAwNAIAMgAS0AADoAACADQQFqIQMgAUEBaiEBIAJBAWsiAg0ACwsgAAvkBAICfgZ/IANBACADQQBKGyELA0AgCiALRwRAIAAgAiAKQQR0aiIDKAIAELUFIQYjAEHgAGsiCSQAIAMtAAQhB0KAgICAMCEEAkACQAJAAkACQAJAAkACQAJAAkAgAy0ABQ4KAQICBQcDBAgFAAYLIAAgAygCCBC1BSEIAn4CQAJAAkAgAygCDEEBag4DAgABCQsgACAAKQPAASIEIAggBEEAEBQMAgsgACAAKAIoKQMQIgQgCCAEQQAQFAwBCyAAIAEgCCABQQAQFAshBCAAIAgQEyAGQcIBRgRAQQEhBwwICyAGQcsBRw0HQQAhBwwHCwJAIAZBwgFGBEBBASEHDAELIAZBywFHDQBBACEHCyAAIAEgBkECIAMgBxCUAxoMBwsgACABIAZCgICAgDAgAygCCAR+IAkgAygCADYCECAJQSBqIghBwABBoyggCUEQahBXGiAAIAMoAgggCEEAQQpBCCADLQAFQQJGGyADLgEGEMsBBUKAgICAMAsiBCADKAIMBH4gCSADKAIANgIAIAlBIGoiCEHAAEGcKCAJEFcaIAAgAygCDCAIQQFBC0EJIAMtAAVBAkYbIAMuAQYQywEFQoCAgIAwCyIFIAdBgDpyEHgaIAAgBBAMIAAgBRAMDAYLIAMpAwgiBEKAgICACHxC/////w9YBEAgBEL/////D4MhBAwFCyAEuRAXIQQMBAsgAysDCBAXIQQMAwsgACABIAZBAiADIAcQlAMaDAMLEAEACyADNQIIIQQLIAAgASAGIAQgBxAbGgsgCUHgAGokACAAIAYQEyAKQQFqIQoMAQsLCzIBAX8CQCABQiCIp0F1SQ0AIAGnIgIgAigCACICQQFrNgIAIAJBAUoNACAAIAEQhgULCxIAIABCgICAgHCDQoCAgIAgUQsLACAAQfQcQQAQFgsHACAAQTBqC54BAQF+AkACQAJAAkACQAJAAkAgARBWQQhqDhAFAwAAAAAAAQIEAAAAAAECAAsgAEGJHEEAEBZCgICAgOAADwsgARAPDwsgAEEEEKQBIQIMAwsgACAAQQUQpAEiAkEwIAGnKQIEQv////8Hg0EAEBsaDAILIABBBhCkASECDAELIABBBxCkASECCyACEA1FBEAgACACIAEQDxDPAQsgAguzBAELfyMAQRBrIggkACAAKAIAIQUgCCACNgIMQX8hCQJAA0ACQCAIIAIiA0EEaiICNgIMIAMoAgAiB0F/Rg0AIAAoAgQhCgNAIAEiBCAKTg0DIAQgBCAFaiILLQAAIgZBAnQiDEGwmgFqLQAAaiIBIApKDQMgBkHAAUYEQCALKAABIQkMAQsLIAYgB0cEQCAHQf8BcSAGRiAHQQh2Qf8BcSAGRnIgB0EQdkH/AXEgBkZyRSAHQRh2IAZHcSAGRSAHQYACSXJyDQMgACAGNgIQCyAEQQFqIQQCQAJAAkACQAJAAkACQAJAIAxBs5oBai0AAEEFaw4YAAkACQkBCQkBCQkBAQECAgICBAUGBwkDCQsgBCAFai0AACEEIAggA0EIaiICNgIMIAMoAgQiA0F/RgRAIAAgBDYCFAwJCyADIARGDQgMCQsgBCAFai8AACEEIAggA0EIaiICNgIMIAMoAgQiA0F/RgRAIAAgBDYCFAwICyADIARGDQcMCAsgACAEIAVqKAAANgIYDAYLIAAgBCAFaiIDKAAANgIYIAAgAy8ABDYCHAwFCyAAIAQgBWooAAA2AiAMBAsgACAEIAVqIgMoAAA2AiAgACADLQAENgIcDAMLIAAgBCAFaiIDKAAANgIgIAAgAy8ABDYCHAwCCyAAIAQgBWoiAygAADYCICAAIAMoAAQ2AhggACADLQAINgIcDAELCyAAIAk2AgwgACABNgIIQQEhDQsgCEEQaiQAIA0LvwEDAn8BfgF8QX8hAgJAAkACQAJAAkACQCABQiCIpyIDQQdqDg4CBAQEBAQDAAEBAQQEBQQLIAGnQQBHDwsgAacPCyABpykCBCEEIAAgARAMIARC/////weDQgBSDwsgAactAAUhAiAAIAEQDCACQX9zQYABcUEHdg8LIANBB2tBbU0EQCABEEkiBUQAAAAAAAAAAGIgBb1C////////////AINCgYCAgICAgPj/AFRxDwsgACABEAxBASECCyACCwsAIAAgAUEAEKAECxkAIAAoAhAgARDoASIBRQRAIAAQyQELIAELPwEBfyMAQRBrIgIkAAJ/IAEgACgCEEcEQCACIAE2AgAgAEG8/QAgAhAVQX8MAQsgABARCyEAIAJBEGokACAACygBAX8jAEEQayICJAAgAiABOwEOIAAgAkEOakECEIoBGiACQRBqJAALCwAgACABQQEQ4gULxQoCBX8PfiMAQeAAayIFJAAgBEL///////8/gyEMIAIgBIVCgICAgICAgICAf4MhCiACQv///////z+DIg1CIIghDiAEQjCIp0H//wFxIQcCQAJAIAJCMIinQf//AXEiCUH//wFrQYKAfk8EQCAHQf//AWtBgYB+Sw0BCyABUCACQv///////////wCDIgtCgICAgICAwP//AFQgC0KAgICAgIDA//8AURtFBEAgAkKAgICAgIAghCEKDAILIANQIARC////////////AIMiAkKAgICAgIDA//8AVCACQoCAgICAgMD//wBRG0UEQCAEQoCAgICAgCCEIQogAyEBDAILIAEgC0KAgICAgIDA//8AhYRQBEAgAiADhFAEQEKAgICAgIDg//8AIQpCACEBDAMLIApCgICAgICAwP//AIQhCkIAIQEMAgsgAyACQoCAgICAgMD//wCFhFAEQCABIAuEIQJCACEBIAJQBEBCgICAgICA4P//ACEKDAMLIApCgICAgICAwP//AIQhCgwCCyABIAuEUARAQgAhAQwCCyACIAOEUARAQgAhAQwCCyALQv///////z9YBEAgBUHQAGogASANIAEgDSANUCIGG3kgBkEGdK18pyIGQQ9rEHNBECAGayEGIAUpA1giDUIgiCEOIAUpA1AhAQsgAkL///////8/Vg0AIAVBQGsgAyAMIAMgDCAMUCIIG3kgCEEGdK18pyIIQQ9rEHMgBiAIa0EQaiEGIAUpA0ghDCAFKQNAIQMLIANCD4YiC0KAgP7/D4MiAiABQiCIIgR+IhAgC0IgiCITIAFC/////w+DIgF+fCIPQiCGIhEgASACfnwiCyARVK0gAiANQv////8PgyINfiIVIAQgE358IhEgDEIPhiADQjGIhCISQv////8PgyIDIAF+fCIUIA8gEFStQiCGIA9CIIiEfCIPIAIgDkKAgASEIgx+IhYgDSATfnwiDiASQiCIQoCAgIAIhCICIAF+fCIQIAMgBH58IhJCIIZ8Ihd8IQEgByAJaiAGakH//wBrIQYCQCACIAR+IhggDCATfnwiBCAYVK0gBCAEIAMgDX58IgRWrXwgAiAMfnwgBCAEIBEgFVStIBEgFFatfHwiBFatfCADIAx+IgMgAiANfnwiAiADVK1CIIYgAkIgiIR8IAQgAkIghnwiAiAEVK18IAIgAiAQIBJWrSAOIBZUrSAOIBBWrXx8QiCGIBJCIIiEfCICVq18IAIgAiAPIBRUrSAPIBdWrXx8IgJWrXwiBEKAgICAgIDAAINQRQRAIAZBAWohBgwBCyALQj+IIQMgBEIBhiACQj+IhCEEIAJCAYYgAUI/iIQhAiALQgGGIQsgAyABQgGGhCEBCyAGQf//AU4EQCAKQoCAgICAgMD//wCEIQpCACEBDAELAn4gBkEATARAQQEgBmsiB0GAAU8EQEIAIQEMAwsgBUEwaiALIAEgBkH/AGoiBhBzIAVBIGogAiAEIAYQcyAFQRBqIAsgASAHEKECIAUgAiAEIAcQoQIgBSkDMCAFKQM4hEIAUq0gBSkDICAFKQMQhIQhCyAFKQMoIAUpAxiEIQEgBSkDACECIAUpAwgMAQsgBEL///////8/gyAGrUIwhoQLIAqEIQogC1AgAUIAWSABQoCAgICAgICAgH9RG0UEQCAKIAJCAXwiASACVK18IQoMAQsgCyABQoCAgICAgICAgH+FhFBFBEAgAiEBDAELIAogAiACQgGDfCIBIAJUrXwhCgsgACABNwMAIAAgCjcDCCAFQeAAaiQAC2oBAn8CQCAAKALYAiIDRQ0AIAAoAuACIgQgACgC3AJODQAgACgC6AIgAUsNACAAKALkAiACRg0AIAMgBEEDdGoiAyACNgIEIAMgATYCACAAIAE2AugCIAAgBEEBajYC4AIgACACNgLkAgsLDAAgACgCQEF/ENADCyEAIAAgASACQoCAgIAwIAMgBEECEOMBIQIgACABEAwgAgsZACABBEAgACABQRBrrUKAgICAkH+EEAwLC28BAn8gAUIgiKciAyABpyICQQBIckUEQCACEJUBDwsgA0F4RgRAIAAgACgCECACENYCEBkPC0EAIQIgACABEJgEIgEQDQR/QQAFIAFCgICAgHCDQoCAgICAf1EEQCAAIAEQmAIPCyAAIAGnEKUECwvrAQICfwF+QoCAgIDgACEDIAAoAhQEfkKAgICA4AAFIAAoAgQhASAAKAIIIgJFBEAgACgCACABEBogAEEANgIEIAAoAgBBLxAyDwsgACgCDCACSgRAIAAoAgAoAhAgASACIAAoAhAiAXQgAWtBEWoQ5wEiAUUEQCAAKAIEIQELIAAgATYCBAsgASAAKAIQIgIEfyACBSABIAAoAghqQQA6ABAgACgCEAtBH3StIAEpAgRC/////3eDhCIDNwIEIAEgA0KAgICAeIMgADUCCEL/////B4OENwIEIABBADYCBCABrUKAgICAkH+ECwsPACAAKAJAQYACaiABEB4LSwECfyABQoCAgIBwWgR/IAGnIgMvAQYiAkENRgRAQQEPCyACQSlGBEAgAygCIC0AEA8LIAAoAhAoAkQgAkEYbGooAhBBAEcFQQALCxAAIAAgACgCKCkDCEEBEFMLFAEBfiAAIAEQLiECIAAgARAMIAILcgEBfwJ/IAAoAggiAiAAKAIMTgRAQX8gACACQQFqIAEQ1QINARoLAkAgACgCEARAIAAgACgCCCICQQFqNgIIIAAoAgQgAkEBdGogATsBEAwBCyAAIAAoAggiAkEBajYCCCACIAAoAgRqIAE6ABALQQALCywBAX8jAEEQayIDJAAgAyACNgIMIABB3ABqQYABIAEgAhDZAhogA0EQaiQACygBAX8CQCAAQoCAgIBwVA0AIAEgAKciAS8BBkcNACABKAIgIQILIAILKAAgACACQTAgAkEAEBQiAhANBEAgAUIANwMAQX8PCyAAIAEgAhCwAQsNACAAIAEgAkEAEKoDC38BA38gACEBAkAgAEEDcQRAA0AgAS0AAEUNAiABQQFqIgFBA3ENAAsLA0AgASICQQRqIQEgAigCACIDQX9zIANBgYKECGtxQYCBgoR4cUUNAAsgA0H/AXFFBEAgAiAAaw8LA0AgAi0AASEDIAJBAWoiASECIAMNAAsLIAEgAGsLFQAgACgCACAAKAIEEBogAEEANgIECwoAIABBMGtBCkkLIwECfyAAKAIAIgEgACgCBCICNgIEIAIgATYCACAAQgA3AgALDAAgACABIAIQDxBbCxEAIAAgASACIANBgIABEJcCCxEAIABCgICAgMCBgPz/AHy/CwwAIAAgASAAIAFKGwspAQF/IAIEQCAAIQMDQCADIAE6AAAgA0EBaiEDIAJBAWsiAg0ACwsgAAsOACAAIAEoAgAgARCIBQsrAQF/IABBEGohAiAALQAHQYABcQRAIAIgAUEBdGovAQAPCyABIAJqLQAACx0AIAAgASkDEBAMIAAgASkDGBAMIAAgASkDCBAMC7AEAgN/AX4CQAJAAkACQAJAA0AgAigCECIFIAUoAhggA3FBf3NBAnRqKAIAIQQgBRAqIQYDQCAERQ0EIAMgBiAEQQFrQQN0IgRqIgUoAgRHBEAgBSgCAEH///8fcSEEDAELCyACKAIUIARqIQQgBSgCACEGIAFFDQEgAUKAgICAMDcDGCABQoCAgIAwNwMQIAFCgICAgDA3AwggASAGQRp2QQdxIgY2AgACQAJAAkACQCAFKAIAQR52QQFrDgMAAQIDCyABIAZBEHI2AgAgBCgCACIABEAgASAArUKAgICAcIQQDzcDEAtBASEFIAQoAgQiAEUNByABIACtQoCAgIBwhBAPNwMYQQEPCyAEKAIAKAIQKQMAIgcQhgENBCABIAcQDzcDCEEBDwsgACACIAMgBCAFENECRQ0BDAYLCyABIAQpAwAQDzcDCEEBDwtBASEFIAZBgICAgHxxQYCAgIB4Rw0CIAQoAgAoAhApAwAQhgFFDQILIAAgAxDiAQwCC0EAIQUgAi0ABSIEQQRxRQ0AIARBCHEEQCADEF5FDQEgAxB8IgMgAigCKCIESSEFIAFFIAMgBE9yDQEgAUKAgICAMDcDGCABQoCAgIAwNwMQIAFBBzYCACABIAAgAq1CgICAgHCEIAMQezcDCEEBDwsgACgCECgCRCACLwEGQRhsaigCFCIERQ0AIAQoAgAiBEUNACAAIAEgAq1CgICAgHCEIAMgBBEYACEFCyAFDwtBfwsNACAAIAEgAkEGEK8DCxEAIAAgACgCJBCgAkECEOAFCxcAIAAoAgwgACgCCEEAIAAoAhARAQAaC5UBAQN/IAAoAhAhAyABEOwEIQQgAygC1AEgBBDfBSIFIAMoAsgBENQCQQJ0aiEDA0ACQCADKAIAIgNFDQACQCADKAIUIAVHDQAgAygCLCAERw0AIAMoAiBFDQELIANBKGohAwwBCwsCQCADBEAgAxCgAiEDDAELIAAgBEECEOUEIgMNAEKAgICA4AAPCyAAIAMgAhDgBQsmAQF/AkAgACgCEEGDf0cNACAAKAIgIAFHDQAgACgCJEUhAgsgAgsKACAAIAFBARBTCxcBAX9BByAAQiCIpyIBIAFBB2tBbkkbCyoBAX8jAEEQayIEJAAgBCADNgIMIAAgASACIAMQ2QIhACAEQRBqJAAgAAuNAQECfyABKAJ8IgRBgIAETgRAIABB5CVBABBQQX8PC0F/IQMgACABQfQAakEQIAFB+ABqIARBAWoQgAEEf0F/BSABIAEoAnwiA0EBajYCfCABKAJ0IANBBHRqIgNCADcCACADQgA3AgggAyAAIAIQGTYCACADIAMoAgxBgP///wdyNgIMIAEoAnxBAWsLC68CAQR/IAIgA0kEfyABQRBqIQQgAS0AB0GAAXEEQCAEIAJBAXRqIQVBACEBQQAhBCADIAJrIgJBACACQQBKGyEDA0AgASADRwRAIAQgBSABQQF0ai8BAHIhBCABQQFqIQEMAQsLAkACQCAAKAIIIAJqIgYgACgCDCIHSgRAQX8hASAAIAYgBBDVAkUNAQwCCyAAKAIQIARBgAJIcg0AQX8hASAAIAcQ7gMNAQsCQCAAKAIQRQRAQQAhAQNAIAEgA0YNAiAAKAIEIAAoAgggAWpqIAUgAUEBdGotAAA6ABAgAUEBaiEBDAALAAsgACgCBCAAKAIIQQF0akEQaiAFIAJBAXQQJRoLIAAgACgCCCACajYCCEEAIQELIAEPCyAAIAIgBGogAyACaxCdAgVBAAsLEQAgACABEA8gAhAPQQEQ3wELiQECAXwBfyACQiCIpyIEQQJNBEAgASACp7c5AwBBAA8LIARBB2tBbU0EQCABIAIQSTkDAEEADwsCfyAAIAIQoAEiAhANBEBEAAAAAAAA+H8hA0F/DAELIAIQViIAQQdHBEAgAEUEQCACp7chA0EADAILEAEACyACEEkhA0EACyEAIAEgAzkDACAAC4IDAgR/An4CQCAAKQNwIgVQRSAFIAApA3ggACgCBCIBIAAoAiwiAmusfCIGV3FFBEAjAEEQayICJABBfyEBAkACfyAAIAAoAkgiA0EBayADcjYCSCAAKAIUIAAoAhxHBEAgAEEAQQAgACgCJBEBABoLIABBADYCHCAAQgA3AxAgACgCACIDQQRxBEAgACADQSByNgIAQX8MAQsgACAAKAIsIAAoAjBqIgQ2AgggACAENgIEIANBG3RBH3ULDQAgACACQQ9qQQEgACgCIBEBAEEBRw0AIAItAA8hAQsgAkEQaiQAIAEiA0EATg0BIAAoAgQhASAAKAIsIQILIABCfzcDcCAAIAE2AmggACAGIAIgAWusfDcDeEF/DwsgBkIBfCEGIAAoAgQhASAAKAIIIQICQCAAKQNwIgVQDQAgBSAGfSIFIAIgAWusWQ0AIAEgBadqIQILIAAgAjYCaCAAIAYgACgCLCIAIAFrrHw3A3ggACABTwRAIAFBAWsgAzoAAAsgAwsJACAAIAE2AAALBwAgAEEfdgsMACAAIAFB/wFxEBALCwAgACABQQAQ4gUL3AEBBn8gAEEBaiEFAkACQCAALQAAIgNBGHRBGHUiB0EATgRAIAUhAQwBC0F/IQQgB0FAayIDQf8BcUE9Sw0BIANBGHRBGHVBAnRBlN4BaigCACIGIAFODQEgBkEBayEIIAAgBmpBAWohASAHIAZB890Bai0AAHEhA0EAIQADQCAAIAZHBEAgBSwAACIEQb9/SgRAQX8PBSAEQT9xIANBBnRyIQMgAEEBaiEAIAVBAWohBQwCCwALC0F/IQQgAyAIQQJ0QYDeAWooAgBJDQELIAIgATYCACADIQQLIAQLCQAgAEEBELsBCywAIAFCgICAgGCDQoCAgIAgUQRAIABBrTtBABAWQoCAgIDgAA8LIAAgARAuC0UBAX8gAkL/////B1gEQCAAIAEgAhChAQ8LIAAgAhCdAyIDRQRAQoCAgIDgAA8LIAAgASADIAFBABAUIQEgACADEBMgAQtJAQF/AkAgACABIAIQDxDOBSIFDQACQCABKAIAIgBBAEgEQCAAIARqIgBBACAAQQBKGyEDDAELIAAgA0wNAQsgASADNgIACyAFCzMBAX8gAQRAA0AgAiADRkUEQCAAIAEgA0EDdGooAgQQEyADQQFqIQMMAQsLIAAgARAaCwsYACAALQAAQSBxRQRAIAEgAiAAEK0EGgsLrgIAAkACQAJAAkAgAkEDTARAAkACQAJAAkACQAJAAkACQAJAIAFB2ABrDgkAAQIDBAUGBwgKCyAAIAJBPWtB/wFxEBAPCyAAIAJBOWtB/wFxEBAPCyAAIAJBNWtB/wFxEBAPCyAAIAJBMWtB/wFxEBAPCyAAIAJBLWtB/wFxEBAPCyAAIAJBKWtB/wFxEBAPCyAAIAJBJWtB/wFxEBAPCyAAIAJBIWtB/wFxEBAPCyAAIAJBHWtB/wFxEBAPCyACQf8BSw0BAkACQAJAIAFB2ABrDgMAAQIECyAAQcABEBAMBQsgAEHBARAQDAQLIABBwgEQEAwDCyABQSJGDQELIAAgAUH/AXEQECAAIAJB//8DcRAxDwsgACACQRRrQf8BcRAQDwsgACACQf8BcRAQCxsBAX8gACABEDsEf0EABSAAQak2QQAQFkF/CwsZAQF/IAEgAhBAIgNFBEAgACACEJwDCyADCyYBAX8jAEEQayICJAAgAkEANgIMIABBASABQQAQqwMgAkEQaiQACxkAIAAoAhAgARCcAiIBRQRAIAAQyQELIAELbQEBfyMAQYACayIFJAAgBEGAwARxIAIgA0xyRQRAIAUgAUH/AXEgAiADayICQYACIAJBgAJJIgEbEEsaIAFFBEADQCAAIAVBgAIQZyACQYACayICQf8BSw0ACwsgACAFIAIQZwsgBUGAAmokAAsPACAAKAJAQYACaiABEBALbwIBfgF/IAAhBAJAAkAgARASDQAgACABQTsgAUEAEBQiAxANBEAgAw8LIAMQIg0BIAAgAxAMIAAgARCPAyIEDQBCgICAgOAADwsgBCgCKCACQQN0aikDABAPIQMLIAAgAyACEFMhASAAIAMQDCABCzEAIAAgASACQoCAgIAIfEL/////D1gEfiACQv////8PgwUgArkQFwsgA0GHgAEQzQILEAAgACAANgIEIAAgADYCAAt1AQF+IAAgASAEfiACIAN+fCADQiCIIgIgAUIgiCIEfnwgA0L/////D4MiAyABQv////8PgyIBfiIFQiCIIAMgBH58IgNCIIh8IAEgAn4gA0L/////D4N8IgFCIIh8NwMIIAAgBUL/////D4MgAUIghoQ3AwALUAEBfgJAIANBwABxBEAgASADQUBqrYYhAkIAIQEMAQsgA0UNACACIAOtIgSGIAFBwAAgA2utiIQhAiABIASGIQELIAAgATcDACAAIAI3AwgLYgACQAJAIAFBAEgNACAAKAKsAiABTA0AIAAoAqQCIAFBFGxqIgAgACgCACACaiIANgIAIABBAEgNASAADwtB3xZBvuMAQcioAUHUPhAAAAtB+PMAQb7jAEHLqAFB1D4QAAALDAAgAEGu4gBBABAWCw0AIAAgASABEEMQ/gELQwEDfwJAIAJFDQADQCAALQAAIgQgAS0AACIFRgRAIAFBAWohASAAQQFqIQAgAkEBayICDQEMAgsLIAQgBWshAwsgAwubDQEIfyMAQRBrIgokAAJAAkAgAUL/////b1gEQCAAECkMAQsgBkGAwABxIQwgBkGAMHEhDiABpyEJAkACQAJAAkACQANAIAkoAhAiByAHKAIYIAJxQX9zQQJ0aigCACELIAcQKiEIAkADQCALRQ0BIAIgCCALQQFrQQN0IgtqIgcoAgRHBEAgBygCAEH///8fcSELDAELCyAJKAIUIAtqIQggCiAHNgIMIAxFIAcoAgAiC0GAgICAAnFFckUEQCAAIApBCGogAxAPQQAQzgINCAJ+IAooAggiB0EATgRAIAetDAELIAe4EBcLIQMgCSgCECIIIAgoAhggAnFBf3NBAnRqKAIAIQcgCBAqIQgCQANAIAcEQCAIIAdBAWtBA3QiC2oiBygCBCACRg0CIAcoAgBB////H3EhBwwBCwtBz+oAQb7jAEHYxgBBqwsQAAALIAkoAhQgC2ohCCAKIAc2AgwgBygCACELCyALQRp2Ig0gBhChA0UNBiANQTBxIg1BMEYEQCAAIAkgAiAIIAcQ0QJFDQIMCAsgBkGA9ABxRQ0FIA4EQCAEp0EAIAAgBBA7GyECIAWnQQAgACAFEDsbIQwCQCALQYCAgIB8cUGAgICABEcEQEF/IQcgACAJIApBDGoQ5AENCwJAIAooAgwoAgBBgICAgHxxQYCAgIB4RgRAIAAoAhAgCCgCABD6AQwBCyAAIAgpAwAQDAsgCigCDCIHIAcoAgBB////vwFxQYCAgIAEcjYCACAIQgA3AwAMAQsgC0GAgIAgcQ0AIAZBgBBxBEAgAiAIKAIARw0JCyAGQYAgcUUNACAMIAgoAgRHDQgLIAZBgBBxBEAgCCgCACIHBEAgACAHrUKAgICAcIQQDAsgAgRAIAQQDxoLIAggAjYCAAsgBkGAIHFFDQYgCCgCBCICBEAgACACrUKAgICAcIQQDAsgDARAIAUQDxoLIAggDDYCBAwGCyANQSBGDQQgDUEQRgRAQX8hByAAIAkgCkEMahDkAQ0JIAgoAgAiAgRAIAAgAq1CgICAgHCEEAwLIAgoAgQiAgRAIAAgAq1CgICAgHCEEAwLIAooAgwiAiACKAIAQf///78DcTYCACAIQoCAgIAwNwMAIAooAgwoAgAhCwwFCyAMRSALQYCAgOAAcXINBEEBIQcgACADIAgpAwAQWkUNBgwICyAKQQA2AgwgCS0ABUEIcUUNAiAJLwEGIgdBAkcNASACEF5FDQIgAhB8IgggCSgCKE8NAiAORSAGQQcQkwRBB0ZxRQRAIAAgCRCgA0UNAQwHCwtBASEHIAxFDQYgACAJKAIkIAhBA3RqIAMQDxAfDAYLIAdBFWtB//8DcUEISw0AAkACQCACEF5FBEAgACACENcFIgEQEg0DQX8hByABEA0NCCAAIAEQ0wUiAkEASARAIAAgARAMDAkLIAJFBEAgACABEAwgACAGQf0MEHkhBwwJCwJ/IAEQViICQQdHBEBBACACDQEaIAGnQR92DAELIAEQSb1CP4inCyECIAAgARAMIAJFDQEgACAGQZ4NEHkhBwwICyACEHwiAiAJEJIESQ0BCyAAIAZBvA0QeSEHDAYLIA5FIAZBBxCTBEEHRnFFBEAgACAGQY4kEHkhBwwGC0EBIQcgDEUNBSAAIAEgAq0gAxAPIAYQ4QEhBwwFCyAAIAkgAiADIAQgBSAGEJYEIQcMBAsgC0GAgICAfHFBgICAgHhGBEAgDARAIAkvAQZBC0YEQCAAIAMgCCgCACgCECkDABBaRQ0ECyAAIAgoAgAoAhAgAxAPEB8LIAZBggRxQYAERw0BQX8hByAAIAkgCkEMahDkAQ0EIAgoAgAoAhApAwAQDyEBIAAoAhAgCCgCABD6ASAIIAE3AwAgCigCDCICIAIoAgBB////vwNxNgIADAELIAtBgICAgAJxBEBBASECIAwEQCAAIAkgAxAPIAYQ1QUhAgsgBkGCBHFBgARGBEAgCiAJKAIQECoiBjYCDEF/IQcgACAJIApBDGogBigCAEEadkE9cRCfAw0FCyACIQcMBAsgDARAIAAgCCkDABAMIAggAxAPNwMACyAGQYAEcUUNAEF/IQcgACAJIApBDGogCigCDCgCAEEadkE9cSAGQQJxchCfAw0DC0F/QQEgACAJIApBDGogBkEIdkEFcSIAQX9zIAooAgwoAgBBGnZxIAAgBnFyEJ8DGyEHDAILIAAgBkHG0QAQeSEHDAELQX8hBwsgCkEQaiQAIAcLTAECfyMAQRBrIgMkAAJAIAFBgIABcUUEQCABQYCAAnFFDQEgABD7AUUNAQsgA0EANgIMIABBBCACQQAQqwNBfyEECyADQRBqJAAgBAvMAQECfwJAIAFCgICAgHBaBEAgAachAwNAAkAgAy0ABUEEcUUNACAAKAIQKAJEIAMvAQZBGGxqKAIUIgRFDQAgBCgCEEUNACAAIAOtQoCAgIBwhBAPIgEgAiAEKAIQERMAIQIgACABEAwgAg8LIAOtQoCAgIBwhBAPIQEgAEEAIAMgAhBPIQQgACABEAwgBA0CAkAgAy8BBkEVa0H//wNxQQhLDQAgACACEKUDIgRFDQAgBEEfdQ8LIAMoAhAoAiwiAw0ACwtBACEECyAECxoAIAAgASACQQBOBH4gAq0FIAK4EBcLEKEBCwsAIABB/////wdxC8cJAgR+BH8jAEHwAGsiCiQAIARC////////////AIMhBQJAAkAgAVAiCSACQv///////////wCDIgZCgICAgICAwP//AH1CgICAgICAwICAf1QgBlAbRQRAIANCAFIgBUKAgICAgIDA//8AfSIIQoCAgICAgMCAgH9WIAhCgICAgICAwICAf1EbDQELIAkgBkKAgICAgIDA//8AVCAGQoCAgICAgMD//wBRG0UEQCACQoCAgICAgCCEIQQgASEDDAILIANQIAVCgICAgICAwP//AFQgBUKAgICAgIDA//8AURtFBEAgBEKAgICAgIAghCEEDAILIAEgBkKAgICAgIDA//8AhYRQBEBCgICAgICA4P//ACACIAEgA4UgAiAEhUKAgICAgICAgIB/hYRQIgkbIQRCACABIAkbIQMMAgsgAyAFQoCAgICAgMD//wCFhFANASABIAaEUARAIAMgBYRCAFINAiABIAODIQMgAiAEgyEEDAILIAMgBYRQRQ0AIAEhAyACIQQMAQsgAyABIAEgA1QgBSAGViAFIAZRGyIMGyEFIAQgAiAMGyIIQv///////z+DIQYgAiAEIAwbIgdCMIinQf//AXEhCyAIQjCIp0H//wFxIglFBEAgCkHgAGogBSAGIAUgBiAGUCIJG3kgCUEGdK18pyIJQQ9rEHMgCikDaCEGIAopA2AhBUEQIAlrIQkLIAEgAyAMGyEDIAdC////////P4MhBCALRQRAIApB0ABqIAMgBCADIAQgBFAiCxt5IAtBBnStfKciC0EPaxBzQRAgC2shCyAKKQNYIQQgCikDUCEDCyAEQgOGIANCPYiEQoCAgICAgIAEhCECIAZCA4YgBUI9iIQhBCADQgOGIQEgByAIhSEDAkAgCSALRg0AIAkgC2siC0H/AEsEQEIAIQJCASEBDAELIApBQGsgASACQYABIAtrEHMgCkEwaiABIAIgCxChAiAKKQMwIAopA0AgCikDSIRCAFKthCEBIAopAzghAgsgBEKAgICAgICABIQhByAFQgOGIQYCQCADQgBTBEBCACEDQgAhBCABIAaFIAIgB4WEUA0CIAYgAX0hBSAHIAJ9IAEgBlatfSIEQv////////8DVg0BIApBIGogBSAEIAUgBCAEUCILG3kgC0EGdK18p0EMayILEHMgCSALayEJIAopAyghBCAKKQMgIQUMAQsgASAGfCIFIAFUrSACIAd8fCIEQoCAgICAgIAIg1ANACAFQgGDIARCP4YgBUIBiISEIQUgCUEBaiEJIARCAYghBAsgCEKAgICAgICAgIB/gyEBIAlB//8BTgRAIAFCgICAgICAwP//AIQhBEIAIQMMAQtBACELAkAgCUEASgRAIAkhCwwBCyAKQRBqIAUgBCAJQf8AahBzIAogBSAEQQEgCWsQoQIgCikDACAKKQMQIAopAxiEQgBSrYQhBSAKKQMIIQQLIARCPYYgBUIDiIQiAiAFp0EHcSIJQQRLrXwiAyACVK0gBEIDiEL///////8/gyALrUIwhoQgAYR8IQQCQCAJQQRGBEAgBCADQgGDIgEgA3wiAyABVK18IQQMAQsgCUUNAQsLIAAgAzcDACAAIAQ3AwggCkHwAGokAAvJBQEFfyMAQeAAayIDJAAgAyABNgJcAkACQAJAAkACQAJAAkACQAJAAkACQANAIAJBFGwiBCADakEUayEFA0ACQCADIAMoAlwiAUEEajYCXAJAAkACQAJAAkAgASgCACIGDggAAQIDAwMECAULIAJBBE4NECADIAFBCGo2AlwgASgCBCEBIAMgBGoiBCAAKAIMIAAoAhAQiAEgAkEBaiECIAQgARC2BEUNBgwJCyACQQRODQ4gAyABQQhqNgJcIAEoAgQhASADIARqIgQgACgCDCAAKAIQEIgBIAJBAWohAiAEIAEQtQRFDQUMCAsgAkEETg0MIAMgAUEIajYCXCABKAIEIQEgAyAEaiIEIAAoAgwgACgCEBCIASACQQFqIQIgBCABEN8CRQ0EDAcLIAJBAUwNCiACQQRPDQkgAyAEaiIBIAAoAgwgACgCEBCIASABIAFBKGsiBCgCCCAEKAIAIAFBFGsiBSgCCCAFKAIAIAZBA2sQqgINBSACQQFrIQIgBBBSIAUQUiAEIAEoAhA2AhAgBCABKQIINwIIIAQgASkCADcCAAwDCyACQQBMDQcgBRCpAkUNAQwFCwsLEAEACyACQQFHDQIgACADKAIAEOACBH9BfwUgACgCCCADKAIIIAMoAgBBAnQQJRogACADKAIANgIAQQALIQEgAxBSDAkLIAJBAWohAgtBACEBIAJBACACQQBKGyEAA0AgACABRgRAQX8hAQwJBSADIAFBFGxqEFIgAUEBaiEBDAELAAsAC0Hu8gBB7uMAQaYKQdohEAAAC0G/8gBB7uMAQZsKQdohEAAAC0Hd5wBB7uMAQYwKQdohEAAAC0H78QBB7uMAQYsKQdohEAAAC0Hd5wBB7uMAQYAKQdohEAAAC0Hd5wBB7uMAQfkJQdohEAAAC0Hd5wBB7uMAQfIJQdohEAAACyADQeAAaiQAIAELaQECfwJ/IAAoAgAiA0ECaiIEIAAoAgRKBEBBfyAAIAQQ4AINARogACgCACEDCyAAIANBAWo2AgAgACgCCCIEIANBAnRqIAE2AgAgACAAKAIAIgBBAWo2AgAgBCAAQQJ0aiACNgIAQQALC2oBAX8gBCADKAIASgR/IwBBEGsiBSQAIAAgASgCACAEIAMoAgBBA2xBAm0QSiIAIAJsIAVBDGoQtwEiBAR/IAMgBSgCDCACbiAAajYCACABIAQ2AgBBAAVBfwshACAFQRBqJAAgAAVBAAsLRwACQCAAIAEgAhAPEM0FIgANACABKQMAIgJCAFMEQCABIAIgBXwiAjcDAAsgAiADWQRAIAQiAyACWQ0BCyABIAM3AwALIAALmAECA38BfiAAIAAoAtgBIgFBAWs2AtgBIAFBAUwEf0EAIQEgAEGQzgA2AtgBAkAgACgCECICKAKQASIDRQ0AIAIgAigClAEgAxECAEUNACAAQYjeAEEAEFACQCAAKAIQKQOAASIEQoCAgIBwVA0AIASnIgAvAQZBA0cNACAAIAAtAAVB3wFxQSByOgAFC0F/IQELIAEFQQALC8oDAQh/IAFBEGohCAJAAkACfwJAAkAgASgCECIELQAQBEAgACgCECIFKALUASAEKAIUIAIQwAIgAxDAAiIKIAUoAsgBENQCQQJ0aiEGA0ACQCAGKAIAIgdFDQACQCAHKAIUIApHDQAgBygCLCAEKAIsRw0AQQAhBiAHKAIgIAQoAiAiCUEBakcNAANAIAYgCUcEQCAHIAZBA3QiBWoiCygCNCAEIAVqIgUoAjRHDQIgBkEBaiEGIAUoAjAgCygCMHNBgICAIEkNAQwCCwsgByAJQQN0aiIFKAI0IAJHDQAgBSgCMEEadiADRg0BCyAHQShqIQYMAQsLIAciBQRAIAUoAhwiAiAEKAIcRwRAIAAgASgCFCACQQN0EJoCIgJFDQcgASACNgIUCyAIIAUQoAIiAjYCACAAKAIQIAQQngIMAwsgBCgCAEEBRg0BIAAgBBDSBSIERQ0FIARBAToAECAAKAIQIAQQngMgACgCECAIKAIAEJ4CIAggBDYCAAsgBCgCAEEBRw0DC0EAIAAgCCABIAIgAxDkBA0BGiAIKAIAIQILIAEoAhQgAigCIEEDdGpBCGsLDwtBzvIAQb7jAEHMPkGzCRAAAAtBAAt+AgJ/AX4jAEEQayIDJAAgAAJ+IAFFBEBCAAwBCyADIAEgAUEfdSICcyACayICrUIAIAJnIgJB0QBqEHMgAykDCEKAgICAgIDAAIVBnoABIAJrrUIwhnwgAUGAgICAeHGtQiCGhCEEIAMpAwALNwMAIAAgBDcDCCADQRBqJAALpAIBB38jAEEQayIFJAACQCAAKAJAIgFFBEAMAQsCQCABAn8gASgCyAEiAiABKALEASIDSARAIAEoAswBIQQgAgwBCyACQQFqIANBA2xBAm0QSiIGQQN0IQMgACgCACEEAkAgASgCzAEiByABQdABakYEQCAEQQAgAyAFQQxqELcBIgRFDQMgBCABKALMASABKALIAUEDdBAlGgwBCyAEIAcgAyAFQQxqELcBIgRFDQILIAUoAgwhAyABIAQ2AswBIAEgA0EDdiAGajYCxAEgASgCyAELQQFqNgLIASAEIAJBA3RqIgMgASgCvAE2AgAgAyABKALAATYCBCAAQbIBEA4gACACQf//A3EQGCABIAI2ArwBDAELQX8hAgsgBUEQaiQAIAILEwAgAEKAgICAcINCgICAgMAAUQtJAQJ/IAJBKRBAIgQtABEEQCAAEMsCQQAPCyAAIAQpAwgiAiADIAJBABAUIgIQDQR/QQAFIAFCgICAgDAgAiACECgbNwMAIAQLCyQAIAAgATYCDCAAQQA2AgggAEIANwIAIAAgAkHtAiACGzYCEAsOACAAKAIQIAEgAhDcBQtMAQJ/An8gACgCBCIDIAJqIgQgACgCCEsEf0F/IAAgBBDOAQ0BGiAAKAIEBSADCyAAKAIAaiABIAIQJRogACAAKAIEIAJqNgIEQQALC6UFAQR/IwBBEGsiBCQAIAQgACgCODYCDAJ/IAEhAyAEKAIMIQACQAJAAn8DQCAAIgJBAWohAAJAIAItAAAiAUEJayIFQRdLDQBBASAFdCIFQY2AgARxDQEgBUEScUUNACADRQ0BDAMLAkAgAUEvRwRAQT0hAyABQT1HDQFBpH8gAC0AAEE+Rg0DGgwFCyAALQAAIgFBKkcEQCABQS9HBEBBLyEDDAYLQS8hASADDQQDQAJAAkAgAUEKaw4EBQEBBQALIAFFDQQLIAAtAAEhASAAQQFqIQAMAAsACwNAIAAiAUEBaiEAIAEtAAEiAkENRgRAIAMNBQwBCyACRQ0CIANBACACQQpGGw0EIAJBKkcNACABLQACQS9HDQALIAFBA2ohAAwBCwsgASIDEMUCRQ0CAkACQAJAAkACQCADQeUAaw4FAQIEBAADCyAALQAAIgFB7gBGBH9Bt38gAi0AAhDBAUUNCBogAC0AAAUgAQtB/wFxQe0ARw0DIAItAAJB8ABHDQMgAi0AA0HvAEcNAyACLQAEQfIARw0DIAItAAVB9ABHDQMgAi0ABhDBAQ0DIAQgAkEGajYCDEFNDAcLIAAtAABB+ABHDQIgAi0AAkHwAEcNAiACLQADQe8ARw0CIAItAARB8gBHDQIgAi0ABUH0AEcNAiACLQAGEMEBDQIgBCACQQZqNgIMQUsMBgsgAC0AAEH1AEcNASACLQACQe4ARw0BIAItAANB4wBHDQEgAi0ABEH0AEcNASACLQAFQekARw0BIAItAAZB7wBHDQEgAi0AB0HuAEcNASACLQAIEMEBDQFBRQwFCyADQe8ARw0AIAAtAABB5gBHDQAgAi0AAhDBAQ0AQVkMBAtBg38LDAILQQoMAQsgAwshACAEQRBqJAAgAAufAQECfwJAAkAgAkL/////B1gEQCAAIAEgAqcQlQEQeiIEQQBMDQEgACABIAIQoQEiAhANRQ0CQX8hBAwCCyAAIAIQnQMiBUUEQEF/IQQMAQsCQCAAIAEgBRB6IgRBAEwEQEKAgICAMCECDAELIAAgASAFIAFBABAUIgIQDUUNAEF/IQQLIAAgBRATDAELQoCAgIAwIQILIAMgAjcDACAECxYAIABCgICAgHBaBEAgAKcgATYCIAsLDQAgACABIAEQQxCdAgtqAQF/IAAoAhQEQCAAKAIAIAEQDEF/DwsCQCABQoCAgIBwg0KAgICAkH9RDQAgACgCACABED0iARANRQ0AIAAQigNBfw8LIAAgAaciAkEAIAIoAgRB/////wdxEFkhAiAAKAIAIAEQDCACCxYBAX8gAEIgiKciAUUgAUEHa0FuSXILSgECfyACQv////8HWARAIAAgASACIANBgIABEOEBDwsgACACEJ0DIgRFBEAgACADEAxBfw8LIAAgASAEIAMQSCEFIAAgBBATIAUL+gkBEn8jAEEwayIHJAAgAUEANgIAIAJBADYCACAHQQA2AiwgB0EANgIoIARBMHEhDiAEQRBxIREgAygCECIJECohBQJAAkACQAJ/A0AgCSgCICAISgRAAkAgBSgCBCIMRQ0AQQAgESAFKAIAQYCAgIABcRsgBCAAIAwQpAMiDXZBAXFFcg0AAkAgDkUNACAFKAIAQYCAgIB8cUGAgICAeEcNACADKAIUIAhBA3RqKAIAKAIQKQMAEIYBRQ0AIAAgBSgCBBDiAUF/DAQLIAAgB0EkaiAMELYBBEAgC0EBaiELDAELIA1FBEAgD0EBaiEPDAELIApBAWohCgsgBUEIaiEFIAhBAWohCAwBCwtBACEFAkAgAy0ABSIGQQRxRQ0AIAZBCHEEQCAEQQFxRQ0BIAMoAiggC2ohCwwBCyADLwEGIgZBBUYEQCAEQQFxRQ0BIAOtQoCAgIBwhBCaBCALaiELDAELIAAoAhAoAkQgBkEYbGooAhQiBkUNACAGKAIEIgZFDQBBfyAAIAdBLGogB0EoaiADrUKAgICAcIQgBhE7AA0BGkEAIQgDQCAIIAcoAihPDQEgBCAAIAhBA3QiCSAHKAIsaigCBCIGEKQDdkEBcQRAAkAgDkUEQEEAIQYMAQsgACAHIAMgBhBPIgZBAEgEQCAAIAcoAiwgBygCKBBmQX8MBQsgBgR/IAcoAgAhBiAAIAcQTiAGQQJ2QQFxBUEACyEGIAcoAiwgCWogBjYCAAsgBSARRSAGcmohBQsgCEEBaiEIDAALAAsgACALIA9qIg8gCmogBWoiE0EBEEpBA3QQLyIQRQRAIAAgBygCLCAHKAIoEGZBfwwBC0EAIQkgAygCECIVECohBSALIQYgDyEKQQEhFEEAIQgDQCAIIBUoAiBORQRAAkAgBSgCBCISRQ0AQQAgESAFKAIAQYCAgIABcSIMGyAEIAAgEhCkAyINdkEBcUVyDQAgDEEcdiEWAn8gACAHQSRqIBIQtgEEQCAJQQFqIQ5BACEUIAYhDCAKDAELIA1FBEAgBkEBaiEMIAkhDiAGIQkgCgwBCyAJIQ4gBiEMIAohCSAKQQFqCyENIAAgEhAZIQogECAJQQN0aiIGIBY2AgAgBiAKNgIEIA4hCSAMIQYgDSEKCyAFQQhqIQUgCEEBaiEIDAELCwJAIAMtAAUiDUEEcUUNAAJ/IA1BCHEEQCAEQQFxRQ0CIAMoAigMAQsgAy8BBkEFRwRAQQAhBQNAIAcoAiwhAyAFIAcoAihPRQRAAkBBACARIAMgBUEDdGoiAygCACIMGyAEIAAgAygCBCINEKQDdkEBcUVyRQRAIBAgCkEDdGoiAyAMNgIAIAMgDTYCBCAKQQFqIQoMAQsgACANEBMLIAVBAWohBQwBCwsgACADEBoMAgsgBEEBcUUNASADrUKAgICAcIQQmgQLIQhBACEFIAhBACAIQQBKGyEEA0AgBCAFRg0BIBAgCUEDdGoiA0EBNgIAIAMgBRCVATYCBCAFQQFqIQUgCUEBaiEJDAALAAsgCSALRw0BIAYgD0cNAiAKIBNHDQMgC0UgFHJFBEAgECALQQhBJyAAEK4CCyABIBA2AgAgAiATNgIAQQALIQUgB0EwaiQAIAUPC0GrFkG+4wBByjtB2D8QAAALQf4VQb7jAEHLO0HYPxAAAAtBxxZBvuMAQcw7Qdg/EAAACx8BAX4gACgCECIAKQOAASEBIABCgICAgCA3A4ABIAELGQAgACAAKAIQIgApA4ABEAwgACABNwOAAQsLACAAQYCAgIB4cguEAgEBfwJAIAAoAggiAiAAKAIMTg0AIAAoAhAEQCAAIAJBAWo2AgggACgCBCACQQF0aiABOwEQQQAPCyABQf8BSw0AIAAgAkEBajYCCCAAKAIEIAJqIAE6ABBBAA8LAn8gACgCCCICIAAoAgxOBEBBfyAAIAJBAWogARDVAg0BGgsCQCAAKAIQBEAgACAAKAIIIgJBAWo2AgggACgCBCACQQF0aiABOwEQDAELIAFB/wFNBEAgACAAKAIIIgJBAWo2AgggAiAAKAIEaiABOgAQDAELQX8gACAAKAIMEO4DDQEaIAAgACgCCCICQQFqNgIIIAAoAgQgAkEBdGogATsBEAtBAAsLNQEBfyAAKAIAIgEEQCAAKAIUIAFBACAAKAIQEQEAGgsgAEIANwIAIABCADcCECAAQgA3AggLLQECf0F/IQMgACABQQAQmwEiAgR/IAIQmgEEQCAAEHVBfw8LIAIoAigFQX8LCwkAIABBARD1BAsQACAAKAIgKAIMKAIgLQAEC2kBA38jAEEQayIDJAACQAJAIAFCgICAgHBUDQAgAaciBC8BBiEFIAIEQCAFQR5HDQEMAgsgBUEVa0H//wNxQQlJDQELIANB5hBBkQ4gAhs2AgAgAEG0KCADEBZBACEECyADQRBqJAAgBAt7AQF/QX8hAiAAKAIUBH9BfwUgAUKAgICAcINCgICAgJB/UgRAIAAoAgAgARAuIgEQDQRAIAAQigNBfw8LIAAgAaciAkEAIAIoAgRB/////wdxEFkhAiAAKAIAIAEQDCACDwsgACABpyIAQQAgACgCBEH/////B3EQWQsLjgICA38BfiACIAEpAgQiB6dB/////wdxIANHckUEQCABrUKAgICAkH+EEA8PCyABQRBqIQUgB0KAgICACINQIAMgAmsiBEEATHJFBEAgAyACIAIgA0gbIQZBACEDIAIhAQNAIAEgBkZFBEAgAyAFIAFBAXRqLwEAciEDIAFBAWohAQwBCwsgA0GAAk4EQCAAIAUgAkEBdGogBBCcBA8LQQAhASAAIARBABD9ASIARQRAQoCAgIDgAA8LIABBEGohAwNAIAEgBEZFBEAgASADaiAFIAEgAmpBAXRqLQAAOgAAIAFBAWohAQwBCwsgAyAEakEAOgAAIACtQoCAgICQf4QPCyAAIAIgBWogBBDYAgsTACAAQoCAgIBwg0KAgICAkH9RCx4AIAAgASACQQBOBH4gAq0FIAK4EBcLIAMgBBDNAgufAgEEfyMAQRBrIgIkAAJAAkACQAJAAkADQAJAAkACQCABEFZBCGoOEAQCBQUFBQUBCAAABgUFCAgFCyABQv////8PgyEBDAcLIAAgAUEBEMMBIgEQDUUNAQwFCwsgACACQQhqIAEQkAIhAyAAIAEQDCADRQ0DIAIgAyADEIgDIgRqIgU2AgxCACEBAkAgBCACKAIIRg0AIAAgBSACQQxqQQBBBBDEAiIBEA0NACACIAIoAgwQiAMgAigCDGoiBDYCDCACKAIIIAQgA2tGDQAgACABEAxCgICAgMB+IQELIAAgAxA3DAQLIAAgARAMIABBhDJBABAWDAILIAAgARAMC0KAgICAwH4hAQwBC0KAgICA4AAhAQsgAkEQaiQAIAELzQIBA38CQCABQoCAgIBwVCACQv////8PVnINACACpyIEIAGnIgMoAihPDQACQAJAAkACQAJAAkACQAJAAkACQCADLwEGIgVBCGsOFgEKCgoKCgoKCgoKCgoDAgMEBQYHCAkACyAFQQJHDQkLIAMoAiQgBEEDdGopAwAQDw8LIAMoAiQgBGowAABC/////w+DDwsgAygCJCAEajEAAA8LIAMoAiQgBEEBdGoyAQBC/////w+DDwsgAygCJCAEQQF0ajMBAA8LIAMoAiQgBEECdGo1AgAPCyADKAIkIARBAnRqKAIAIgBBAE4EQCAArQ8LIAC4EBcPCyADKAIkIARBAnRqKgIAuxAXDwsgAygCJCAEQQN0aisDABAXDwsgACACEDghAyAAIAIQDCADRQRAQoCAgIDgAA8LIAAgASADIAFBABAUIQEgACADEBMgAQuzAQEDfyABQoCAgIBwVARAQQAPCyABpyICLwEGQSlGBEAjAEEQayIEJAACQAJAIAAgBEEIaiABQeEAEIcBIgJFDQAgBCkDCCIBEBIEQCAAIAIpAwAQogEhAwwCCyAAIAEgAikDCEEBIAIQNiIBEA0NACAAIAEQLSEDIAAgAikDABCiASICQQBIDQAgAiADRg0BIABB9dAAQQAQFgtBfyEDCyAEQRBqJAAgAw8LIAItAAVBAXELHgAgAEKAgICAcINCgICAgJB/UQRAIACnIAEQngQLCxYAIAAgACgCKCABQQN0aikDACABEFMLJAEBfyMAQRBrIgMkACADIAI2AgwgACABIAIQqAQgA0EQaiQACw0AIABBACABQQAQoQQLGQAgACABIAJBASADIAQgBSAGIAcgCBCGAgshAQJ/IAAoApgCIgJBAE4EfyAAKAKAAiACai0AAAVBAAsLrQUBB38jAEGQAmsiBiQAIAZBADoAECAAIAYQ/AIgAEEQaiEJQQEhBAJAAkADQEF+IQgCQAJAAkACQAJAAkACQAJAAkACQAJAIAkoAgAiA0H+AGoOBQEJCQkHAAsCQAJAAkACQAJAIANBKGsOAgECAAsCQCADQTtrDgMHDQkACwJAIANB2wBrDgMBDQMACwJAIANB+wBrDgMBDQQACyADQaV/Rg0HIANBL0YNCSADQap/Rw0MDBALIARB/wFNDQQMDgsgBEEBayIEIAZBEGpqLQAAQShHDQ0MCQsgBEEBayIEIAZBEGpqLQAAQdsARw0MDAgLQf0AIQUgBEEBayIEIAZBEGpqLQAAIghB+wBGDQlBqn8hAyAIQeAARw0MIAAgCRCPAiAAQQA2AjAgACAAKAIUNgIEIAAgACgCOBDZAw0MCyAAKAIoQeAARg0GQeAAIQMgBEH/AUsNCgsgBkEQaiAEaiADOgAAIARBAWohBAwFCyAHIARBAkZyIQdBOyEFDAYLIAdBAnIgByAEQQJGGyEHQaV/IQUMBQsgB0EEciEHQT0hBQwEC0F/IQgLAn8CQCAFQYABaiIDQRVNQQBBASADdEGbgMABcRsNACAFQSlGIAVB3QBGciAFQdUAaiIDQQdNQQBBASADdEGHAXEbciAFQf0ARnINAEEBDAELQQALRQ0AIAAgACgCOCAIajYCOCAAEPAEDQQLIAkoAgAhAwsgA0GDf0cEQCADIQUMAQtBWSEFIABBwwAQVA0AIABBLRBUDQBBg38hBQsgABARDQEgBEEBSw0AC0FZIAAoAhAgAEHDABBUGyEDIAJFDQEgA0EKIAAoAgQgACgCFEYbIQMMAQtBqn8hAwsgAQRAIAEgBzYCAAsgACAGEPsCIQAgBkGQAmokAEF/IAMgABsLEQAgACAAKAKwAigCADYCsAILTgAgASAAKAKwAjYCACAAIAE2ArACIAFBfzYCFCABIAU2AhAgASAENgIMIAEgAzYCCCABIAI2AgQgACgCvAEhACABQQA2AhwgASAANgIYC50GAQZ/IAAoAgAhBQJAAkACQAJAAkACQAJAAkACQAJAAkACQCADDgcEAAAAAAECAwsgASACIAEoAsABQQEQ1QMiBEEASA0FAkAgBEH/////A00EQCABKAJ0IgYgBEEEdGoiCCgCBCIHIAEoArwBIglGBEAgA0EDRw0CIAEtAG5BAXENAiAGIARBBHRqKAIMQfgAcUEIRw0CDAkLIAgoAgxB+ABxQRhHDQcgB0ECaiAJRg0BDAcLIAEoArwBIAEoAvABRw0GCyAAQc0vQQAQFQwHCyAFIAEgAkEDEPMCDwsgASACIAEoAsABQQAQ1QNBAE4NAiABKAIoBEACQCABIAIQtQIiA0UNACADLQAEQQJxRQ0AIAMoAgggASgCvAFHDQAgASgCJEEBRg0EC0GAgICABEF/IAUgASACEPQCGw8LIAEgAhCHAiIAQQBODQggBSABIAIQWCIAQQBIDQgCQCACQc0ARw0AIAEoAkhFDQAgASAANgKYAQsgASgCdCAAQQR0aiABKAK8ATYCCCAADwsQAQALIAUgASACQQAQ8wIhAAwGCyAAQc0vQQAQFQwCCyABKAK8ASEHIANBAksNACAHIAEoAvABRw0AIAEgAhDyBEEASA0AIABBsM4AQQAQFQwBC0EAIQQgASgCfCIGQQAgBkEAShshCANAAkAgBCAIRgRAQX8hBAwBCwJAIAEoAnQgBEEEdGoiBigCACACRw0AIAYoAgQNACABIAYoAgggBxDxBA0BCyAEQQFqIQQMAQsLIARBAE4EQCAAQcbSAEEAEBUMAQsCQCABKAIoRQ0AIAEgAhC1AiIERQ0AIAEgBCgCCCAHEPEERQ0AIABBoDBBABAVDAELIAEoAiBFDQIgASgCJEEBSw0CIAcgASgC8AFHDQIgBSABIAIQ9AIiAA0BC0F/DwsgACAALQAEQfkBcUEGQQIgA0ECRhtyOgAEQYCAgIAEDwsgBSABIAJBASADQQRGQQF0IANBA0YbEPMCIgBBAEgNACABKAJ0IABBBHRqIgEgASgCDEF8cSADQQJGckECcjYCDCAADwsgAAuzAQEDfwJAAkAgACgCQCICEKgBIgNBvwFHBEAgA0HNAEcNASACKAKYAiEDIAJBfzYCmAIgAiADNgKEAiAAQc0AEA4gACABEBwPCyACKAKYAiIDIAMgAigCgAIiBGooAAFrQQFqIgMgBGoiBC0AAEHWAEcNASAAKAIAIAQoAAEQEyACKAKAAiADakEBaiAAKAIAIAEQGRBdIAJBfzYCmAILDwtBtCBBvuMAQdOwAUGyzQAQAAALMgAgACABIAJCgICAgAh8Qv////8PWAR+IAJC/////w+DBSACuRAXCyADIARBB3IQzQILqQEBAn8jAEEQayIEJAACQAJAIAAgASACQQBBACAEQQxqEJUFIgEQDQ0AIAQoAgwiBUECRwRAIAMgBTYCACABIQIMAgsgACABQekAIAFBABAUIgIQDQ0AIAMgACACEC0iAzYCAEKAgICAMCECIANFBEAgACABQcAAIAFBABAUIQILIAAgARAMDAELIAAgARAMIANBADYCAEKAgICA4AAhAgsgBEEQaiQAIAILIgAgACABIAJCAEL/////////D0IAEIEBIQEgACACEAwgAQuQCQIIfwF+IwBBEGsiAyQAIAAgAEEQaiIHEI8CIAAgACgCOCIBNgI0IAMgATYCDCAAIAAoAhQ2AgQCfwJAA0ACQCAAIAE2AhggACAAKAIIIgU2AhRBIiEEAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCABLAAAIgZB/wFxIgIOewAJCQkJCQkJCQYEBQUDCQkJCQkJCQkJCQkJCQkJCQkJBgkCCQ4JCQEJCQkLCQoJBwgMDAwMDAwMDAwJCQkJCQkJDg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JCQkJDgkODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODgkLIAEgACgCPEkNDCAHQap/NgIADA4LQSchBCAAKAJMRQ0LCyAAIARBASABQQFqIAcgA0EMahCSA0UNDAwQCyABQQFqIAEgAS0AAUEKRhshAQsgAyABQQFqIgE2AgwgACAFQQFqNgIIDA0LIAAoAkxFDQcLIAMgAUEBaiIBNgIMDAsLIAAoAkxFDQUgAS0AASIEQS9GDQggBEEqRw0FIAFBAmohAQNAIAMgATYCDANAAkACQAJAAkAgAS0AACICQQprDgQBAgIDAAsgAkEqRwRAIAINAiABIAAoAjxJDQNB3RghAQwPCyABLQABQS9HDQIgAyABQQJqIgE2AgwMDwsgACAAKAIIQQFqNgIIDAELIAJBGHRBGHVBAE4NACABQQYgA0EMahBhIQIgAygCDCEBIAJBf0cNAQsLIAFBAWohAQwACwALIAEtAAEQRUUNAwwECyAGQQBODQNBji8hAQwHCyABLQABEEVFDQIMAQsgACgCTEUNASABLQABEEVFDQELIAAoAgAgASADQQxqQQBBCiAAKAJMIgIbIAJBAEdBAnQQxAIiCRANDQYgAEGAfzYCECAAIAk3AyAMAgsgByACNgIAIAMgAUEBajYCDAwBCyADIAFBAWo2AgxBACEEIwBBkAFrIgEkACADKAIMIQUgAUGAATYCCCABIAFBEGoiBjYCDAJ/A0AgASgCCEEGayEIAkADQCAEIAZqIAI6AAAgBEEBaiEEIAUsAAAiAkEASA0BIAJB/wFxIgJBA3ZBHHFB0OABaigCACACdkEBcUUNASAFQQFqIQUgBCAISQ0AC0EAIAAoAgAgAUEMaiABQQhqIAFBEGoQjQUNAhogASgCDCEGDAELCyAAKAIAIAYgBBCtAwshAiABKAIMIgQgAUEQakcEQCAAKAIAIAQQGgsgAyAFNgIMIAFBkAFqJAAgAkUNBCAAQYN/NgIQIABCADcCJCAAIAI2AiALIAAgAygCDDYCOEEADAQLIAFBAmohAQNAIAMgATYCDANAAkACQCABLQAAIgIEQCACQQprDgQGAQEGAQsgASAAKAI8Tw0FDAELIAJBGHRBGHVBAE4NACABQQYgA0EMahBhIgJBfnFBqMAARgRAIAMoAgwhAQwFCyADKAIMIQEgAkF/Rw0BCwsgAUEBaiEBDAALAAsLIAAgAUEAEBULIAdBqH82AgBBfwshACADQRBqJAAgAAsRACAAIAEgASACIANBAhCMBAusAQICfwJ+An8gAkUEQEKAgICAMCEGQQAMAQsgACgCECIDKQOAASEGIANCgICAgCA3A4ABQX8LIQNBfyEEAkAgACABQQYgAUEAEBQiBRANDQACQCAFEBINACAFECgNACAAIAUgAUEAQQAQNiEBAn8gAyACDQAaQX8gARANDQAaIAMgARAiDQAaIAAQKUF/CyEEIAAgARAMDAELIAMhBAsgAgRAIAAgBhCUAQsgBAsMACAAIAEgACABSBsLHQAgAEKAgICAcFoEfyAApy0ABUEEdkEBcQVBAAsLsAEBAX8jAEEQayIDJAACQAJAIAIQXgRAIAEgAhB8NgIAQQEhAgwBCyAAKAIQIgAoAiwgAk0NAQJ/AkAgACgCOCACQQJ0aigCACIAKQIEQoCAgICAgICAQINCgICAgICAgIDAAFINACADQQxqIAAQ5wVFDQBBASADKAIMIgBBf0cNARoLQQAhAEEACyECIAEgADYCAAsgA0EQaiQAIAIPC0GtyABBvuMAQb8YQe4OEAAAC0UAIAAoAhAgASACEOcBIgEgAkVyRQRAIAAQyQFBAA8LIAMEQCADQQAgACgCECABEKMEIgAgAmsiAiAAIAJJGzYCAAsgAQv5AQIDfgJ/IwBBEGsiBSQAAn4gAb0iA0L///////////8AgyICQoCAgICAgIAIfUL/////////7/8AWARAIAJCPIYhBCACQgSIQoCAgICAgICAPHwMAQsgAkKAgICAgICA+P8AWgRAIANCPIYhBCADQgSIQoCAgICAgMD//wCEDAELIAJQBEBCAAwBCyAFIAJCACADp2dBIGogAkIgiKdnIAJCgICAgBBUGyIGQTFqEHMgBSkDACEEIAUpAwhCgICAgICAwACFQYz4ACAGa61CMIaECyECIAAgBDcDACAAIAIgA0KAgICAgICAgIB/g4Q3AwggBUEQaiQACyoBAX8jAEEQayIDJAAgAyACNgIMIAAgASACQfUCQQAQqQQaIANBEGokAAsbACAAIAFB/wFxEBAgACgCBCEBIAAgAhAeIAELiwwBB38jAEEgayICJAACQAJAAkACQAJAAkACQAJ/IAAoAhAiA0GDf0cEQEEAIANBV0cNARogACgCQCIDLQBsQQFxRQRAIABB7dgAQQAQFQwDCyADKAJkRQRAIABBpzdBABAVDAMLQX8hAyAAEBENCAJAAkACQAJAIAAoAhAiBEEpaw4EAgEBAgALIARB3QBGIARBOmtBAklyIARB/QBGcg0BCyAAKAIwDQAgBEEqRgRAIAAQEQ0LQQEhBgsgACABELsBRQ0BDAoLIABBBhAOCyAAKAJALQBsIQEgBgRAIAAQNSEEIAAQNSEDIABB/gBB/QAgAUEDRhsQDiAAQQ4QDiAAQQYQDiAAQQYQDiAAIAQQICAAQYUBEA4gAUEDRyIFRQRAIABBiwEQDgsgAEGBARAOIABBwgAQDiAAQekAEBwgAEHqAEF/EB0hBiAAIAMQICAAIAUEf0GJAQUgAEHBABAOIABBwAAQHCAAQYsBEA5BigELEA4gAEEREA4gAEHqAEF/EB0hBSAAQQ4QDiAAQesAIAQQHRogACAFECAgAEEBEA4gAEECEDogAEGrARAOIABB6gBBfxAdIQQgAUEDRyIFRQRAIABBiwEQDgsgAEGGARAOIABBABBuIABB6gBBfxAdIQcgBUUEQCAAQYsBEA4LIABBgQEQDiAAQcIAEA4gAEHpABAcIABB6QAgAxAdGiAAQcEAEA4gAEHAABAcIAAgBxAgIABBDxAOIABBDxAOIABBDxAOIABBARD2AiAAIAQQICAAQYYBEA4gAEEBEG4gAEHqAEF/EB0hBCABQQNHIgFFBEAgAEGLARAOCyAAQYEBEA4gAEHCABAOIABB6QAQHCAAQekAIAMQHRogAEHrACAGEB0aIAAgBBAgIABBhgEQDiAAQQIQbiAAQeoAQX8QHSEDIAFFBEAgAEGLARAOCyAAIAMQICAAQTAQDkEAIQMgAEEAEBwgAEEEEG4gACAGECAgAEHBABAOIABBwAAQHCAAQQ8QDiAAQQ8QDiAAQQ8QDgwJCyABQQNGBEAgAEGLARAOCyAAQYgBEA4gAEHpAEF/EB0hASAAQQEQ9gIMBAsgACgCIAshBkF/IQNBfyEEAkACfwJAIABBon8gAUEEciIHIgUQzAMNACAAKAIQQaZ/RgRAIAVBe3EhCCAAEDUhBQNAIAAQEQ0CIABBERAOIABBsAEQDiAAQekAIAUQHRogAEEOEA4gAEEIIAgQswINAiAAKAIQQaZ/Rg0ACyAAIAUQIAtBAAwBC0F/Cw0AIAAoAhBBP0YEQCAAEBENASAAQekAQX8QHSEFIAAQYg0BIABBOhAwDQEgAEHrAEF/EB0hCCAAIAUQICAAIAdBAXEQuwENASAAIAgQIAtBACEECyAEDQYgACgCECIEQfsAaiEDIARBPUcgA0ELS3FFBEAgABARDQEgACACQRxqIAJBGGogAkEUaiACQRBqQQAgBEE9RyAEELwBQQBIDQEgACABELsBBEAgACgCACACKAIUEBMMAgsgBEE9RgRAIAIoAhwiAUE8Rw0HIAIoAhQgBkcNBiAAIAYQrQEMBgsgACADQcC0AWotAAAQDiACKAIcIQEMBgtBACEDIARB7wBqQQJLDQYgABARDQAgACACQRxqIAJBGGogAkEUaiACQRBqIAJBDGpBASAEELwBQQBIDQAgAEEREA4gBEGTf0YEQCAAQbABEA4LIABB6gBB6QAgBEGSf0YbQX8QHSEDIABBDhAOIAAgARC7AUUNASAAKAIAIAIoAhQQEwtBfyEDDAULAkAgAigCHCIBQTxHDQAgAigCFCAGRw0AIAAgBhCtAQsgAigCDEEBayIEQQNPDQEgACAEQRVqQf8BcRAOIAAgASACKAIYIAIoAhQgAigCEEEBQQAQ1AEgAEHrAEF/EB0hASAAIAMQICACKAIMIQMDQCADBEAgAEEPEA4gAiACKAIMQQFrIgM2AgwMAQsLCyAAIAEQIEEAIQMMAwsQAQALQTwhAQtBACEDIAAgASACKAIYIAIoAhQgAigCEEECQQAQ1AELIAJBIGokACADC6sFAQZ/QQIhDAJAAkACQAJAAkAgACgCQCIJEKgBIghBxwBrDgQEAgIBAAsgCEHBAEYNAiAIQbwBRwRAIAhBtgFHDQIgCSgCgAIgCSgCmAJqIgsoAAEhCiALLwAFIQsgCkEIRg0CIApBOkcEQCAKQfEARg0DIApBzQBHDQULIAktAG5BAXFFDQQgAEGm0wBBABAVQX8PC0EBIQwgCSgCgAIgCSgCmAJqIgcoAAEhCiAHLwAFIQsMAwtBAyEMDAILIAdBu39GBEAgAEHn1gBBABAVQX8PCyAHQX5xQZR/RgRAIABBo9sAQQAQFUF/DwsgB0FfcUHbAEYEQCAAQfkaQQAQFUF/DwsgAEGI1wBBABAVQX8PC0EBIQwgCSgCgAIgCSgCmAJqKAABIQoLIAkoApgCIQ1BfyEHIAlBfzYCmAIgCSANNgKEAgJAAkAgBgRAAkACQAJAAkAgCEHHAGsOBAEDAwIACwJAIAhBwQBHBEAgCEG8AUYNASAIQbYBRw0EIAAQNSEHIABBuQEQDiAAIAoQHCAAIAcQOiAAIAsQGCAJIAdBARB0GkE8IQggAEE8EA4MBwsgAEHCABAOIAAgChAcQcEAIQgMBgsgAEG9ARAOIAAgChAcIAAgCxAYQbwBIQgMBQsgAEHxABAOIABBExAOQccAIQgMAwsgAEHwABAOIABBFBAOQcoAIQgMAgsQAQALAkACQAJAIAhBxwBrDgQBBAQCAAsgCEG2AUcNAyAAEDUhByAAQbkBEA4gACAKEBwgACAHEDogACALEBggCSAHQQEQdBpBPCEIDAMLIABB8QAQDkHHACEIDAILIABB8AAQDkHKACEIDAELIAAgCBAOCyABIAg2AgAgAiALNgIAIAMgCjYCACAEIAc2AgAgBQRAIAUgDDYCAAtBAAtaAQN/IwBBEGsiASQAAkAgACgCECIDQap/Rg0AIANBO0cEQCADQf0ARg0BIAAoAjANASABQTs2AgAgAEG8/QAgARAVQX8hAgwBCyAAEBEhAgsgAUEQaiQAIAILGQAgASACQQ9xOgAEIAFBCGogAEHQAGoQTAu1AQEFfyMAQSBrIgUkAAJ+AkAgAkKAgICAcINCgICAgJB/UgRAIAAgAhA9IgIQDQ0BCyAAIAVBCGogARBDIgcgAxBDIghqIAKnIgYoAgQiBEH/////B3FqIARBH3YQqgMNACAFQQhqIgQgASAHEJ0CGiAEIAZBACAGKAIEQf////8HcRBZGiAEIAMgCBCdAhogACACEAwgBBA5DAELIAAgAhAMQoCAgIDgAAshAiAFQSBqJAAgAgs7AAJ/IAAgAUGAgARPBH9BfyAAIAFBgIAEa0EKdkGAsANqEJYBDQEaIAFB/wdxQYC4A3IFIAELEJYBCwtRACAAQf8ATQRAIABBA3ZB/P///wFxQdDgAWooAgAgAHZBAXEPCyAAQX5xQYzAAEYgABC5BAR/QQEFIABBwIICQcCHAkEUEOECQQBHC0EAR3ILUwEBfyABQoCAgIBwWgR/IAGnLwEGIgJBKUYEQAJ/QQAgAUEpEEAiAkUNABogAi0AEQRAIAAQywJBfwwBCyAAIAIpAwAQwgELDwsgAkECRgVBAAsLyQICAX4CfyMAQRBrIgUkAAJAIAFCgICAgHBUBEAgASEDDAELIAJBb3EhBAJAAkACQCACQRBxDQAgACABQcIBIAFBABAUIgMQDQ0BIAMQEg0AIAMQKA0AIAUgAEHGAEEWIARBAUYbQcgAIAQbEDI3AwggACADIAFBASAFQQhqEDYhAyAAIAUpAwgQDCADEA0NASAAIAEQDCADQoCAgIBwVA0DIAAgAxAMIABB+8gAQQAQFgwCCyAEQQBHIQRBACECA0AgAkECRwRAIAAgAUE3QTkgAiAERhsgAUEAEBQiAxANDQICQCAAIAMQO0UNACAAIAMgAUEAQQAQNiIDEA0NAyADQv////9vVg0AIAAgARAMDAULIAAgAxAMIAJBAWohAgwBCwsgAEH7yABBABAWCyAAIAEQDAtCgICAgOAAIQMLIAVBEGokACADC1cBAn8jAEEQayIDJABBfyEEIAAgA0EIaiACEI4ERQRAQQAhBCABIAMpAwgiAkKAgICAgICAEFoEfiAAQb8OEGtBfyEEQgAFIAILNwMACyADQRBqJAAgBAsNACAAIAEgAhAPEM4FC8wBAgF/AXwCfwNAAkACQAJ/AkACQCACEFYOCAAAAAAEBAQBBAsgAqcMAQsgAhBJIgS9IgJCNIinQf8PcSIDQZ0ISw0BIASZRAAAAAAAAOBBYwRAIASqDAELQYCAgIB4CyEAQQAMAwtBACEAQQAgA0HSCEsNAhpBACACQv////////8Hg0KAgICAgICACIQgA0GTCGuthkIgiKciAGsgACACQgBTGyEAQQAMAgsgACACEKABIgIQDUUNAAtBACEAQX8LIQMgASAANgIAIAMLCwAgACABIAIQkwILLwEBfyMAQdAAayIDJAAgAyAAIANBEGogARCJATYCACAAIAIgAxAWIANB0ABqJAALLAEBfyAAKAIQIgEtAIgBRQRAIAFBAToAiAEgAEHaC0EAEFAgAUEAOgCIAQsLDQAgACABIAEQQxCtAwsWACAAIAEgAiADIAQgBSAAKQMwEIsCCxsAIAAgAUH/AXEQECAAIAIgACgCBGtBBGsQHguOAQECfyMAQRBrIgIkAAJ/IAEEQCAAQSBqIAAgAEHBAGtBGkkbIABB/wBNDQEaIAJBBGogAEECELcDGiACKAIEDAELIABBIGsgACAAQeEAa0EaSRsgAEH/AE0NABogAkEEaiAAQQAQtwMhASACKAIEIgMgACADQf8ASxsgACABQQFGGwshACACQRBqJAAgAAtmAQF/An9BACAAKAIIIgIgAU8NABpBfyAAKAIMDQAaIAAoAhQgACgCACACQQNsQQF2IgIgASABIAJJGyIBIAAoAhARAQAiAkUEQCAAQQE2AgxBfw8LIAAgATYCCCAAIAI2AgBBAAsLVQECfwJAIAFCgICAgHBUDQAgAaciAy8BBiIEQQpLQQEgBHRB8AlxRXINACAAIAMpAyAQDCADIAI3AyAPCyAAIAIQDCABEA1FBEAgAEGszABBABAWCwsnACAAIAApA8ABIAIgARAPIgFBAxDsARogACABIAMQ7gUgACABEAwLIAEBfiAAIAAgAiABIANBBEEAEMsBIgUgASAEENABIAULjgIBAn8jAEEwayIFJAACfyACIAEoAgBPBEAgBSACNgIkIAUgAzYCICAAQZf4ACAFQSBqEFBBfwwBCwJAIAEoAgQgBE4NACABIAQ2AgQgBEH//wNIDQAgBSACNgIEIAUgAzYCACAAQb/4ACAFEFBBfwwBCyABKAIIIAJBAXRqIgMvAQAiBkH//wNHBEBBACAEIAZGDQEaIAUgAjYCGCAFIAQ2AhQgBSAGNgIQIABB8PcAIAVBEGoQUEF/DAELIAMgBDsBAEF/IAAgAUEMakEEIAFBFGogASgCEEEBahCAAQ0AGiABIAEoAhAiAEEBajYCECABKAIMIABBAnRqIAI2AgBBAAshAyAFQTBqJAAgAwtrAQF+AkAgAkUgAUKAgICAcINCgICAgJB/UnINACABEA8hAyAAKAIAIAOnEKUEIgJFDQAgAhBeDQAgAEEEEA4gACACEDpBAA8LIAAgARAPENMDIgJBAEgEQEF/DwsgAEECEA4gACACEDpBAAv4AgACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAFBxwBrDgQBDQ0CAAsgAUE8RwRAIAFBvAFHBEAgAUG2AUYNByABQcEARw0OC0EVIQQCQCAFDgUGBgUEAA4LQRshBAwECyAAKAIAIAMQEyAAIAQQIAtBsQEhBAJAAkACQCAFDgUFBgABAg4LQRYhBAwEC0EZIQQMAwtBHSEEDAILQRchAQJAIAUOBQoKCQgACwtBHyEBDAgLQRghBAsgACAEEA4LAkAgAUHHAGsOBAMICAcACyABQTxGDQMgAUHBAEYNCCABQbwBRg0BIAFBtgFHDQcLIAVBAk8NCCAAQbsBQbcBIAYbEA4MCQsgAEG+ARAODAgLIABByQAQDg8LIABBPRAODwtBGiEBCyAAIAEQDgsgAEHLABAODwsQAQALIABBwwAQDiAAIAMQOg8LQdXrAEG+4wBBt7kBQYfJABAAAAsgACADEDogACACQf//A3EQGAvNEgEKfyMAQUBqIgYkACAEQQBIBEAgACAGQShqQQAQqQEaIAYoAihBAnEhBAsgABA1IQogABA1IQsgACgCQCgChAIhDQJAIAMEQCAAQREQDiAAQQYQDiAAQasBEA4gAEHqACAKEB0aIAAgCxAgDAELIABB6wAgChAdGiAAIAsQICAAQREQDgsgACgCQCgChAIhDgJAAkACQAJAAkAgACgCECIHQdsARwRAIAdB+wBGBEBBfyEHIAAQEQ0GIABB7wAQDiAEBEAgAEELEA4gAEEbEA4LIAFBSUYgAUFRRnIhDCABQbF/RyEPA0ACQAJAAkACQAJAAkACQAJAAkACQAJAIAAoAhAiB0Glf0cEQCAHQf0ARg0LIAAgBkE4akEAQQFBABDSAyIHQQBIDRIgBkG2ATYCMCAGQQA2AjQgACgCQCIJKAK8ASEIIAZBfzYCPCAGIAg2AiwgBkEANgIIIAcNAiAAEBFFDQEgBigCOCEHDAYLIARFBEAgACgCAEH6OkEAEFAMEgtBfyEHIAAQEQ0SAkAgAQRAIAYgACACENEDIgg2AjQgCEUNFCAGQbYBNgIwIAAoAkAoArwBIQcgBkF/NgI8IAYgBzYCLCAGQQA2AggMAQsgABC0Ag0TIAAgBkEwaiAGQSxqIAZBNGogBkE8aiAGQQhqQQBB+wAQvAENEwsgACgCEEH9AEYNAiAAQf8UQQAQFQwQCwJAIAAoAhBBIHJB+wBHDQAgACAGQShqQQAQqQEiB0EsRiAHQf0ARnJFIAdBPUdxDQACQCAGKAI4IgdFBEAgBARAIABB8AAQDiAAQRgQDiAAQQcQDiAAQdEAEA4gAEEYEA4LIABByAAQDgwBCyAEBEAgAEEbEA4gAEEHEA4gAEHMABAOIAAgBxAcIABBGxAOCyAAQcIAEA4gACAHEDoLQX8hByAAIAEgAkEBQX9BARDVAUEASA0SIAAoAhBB/QBGDQogAEEsEDBFDQsMEgsCQAJ/IAYoAjgiB0UEQCAAQfEAEA4gBEUEQEESIQgMAwtBGCEJIABBGBAOIABBBxAOIABB0QAQDkESDAELIARFBEBBESEIDAILQRshCSAAQRsQDiAAQQcQDiAAQcwAEA4gACAHEBxBEQshCCAAIAkQDgsgACAIEA4gAQRAIAYgACACENEDIgg2AjQgCEUNBSAHRQ0EDAYLIAAQtAINBAwCCwJAIAIEfyAAIAYoAjgiBxDvBA0FIAAoAkAFIAkLLQBuQQFxRQ0AIAYoAjgiB0HNAEcgB0E6R3ENACAAQfkaQQAQFQwECyAEBEAgAEEbEA4gAEEHEA4gAEHMABAOIAAgBigCOBAcIABBGxAOCyABQQAgDxtFBEAgAEEREA4gAEG2ARAOIAAgBigCOCIHEBwgACAAKAJALwG8ARAYDAILIAYgACgCACAGKAI4EBkiBzYCNCAAQcIAEA4gACAHEDoMBgsgAEELEA4gAEHTABAOIAAgBigCCCIHQQJ0QQRqIAdBBXRBQGtyQfwBcRBuDAQLIAAgBkEwaiAGQSxqIAZBNGogBkE8aiAGQQhqQQBB+wAQvAENASAGKAIIIQgCQAJAIAdFBEBBHiEHAkAgCEEBaw4DAwIABAtBICEHIABBIBAODAILIAhBAWsiCEEDTw0EIAAgCEEBdEEbakH/AXEQDgwEC0EcIQcLIAAgBxAOCyAAQccAEA4MAgsgACgCACAHEBMMCgsgAEHBABAOIAAgBxA6CyABRQ0BIAYoAjQhBwsgACAHIAEQtwINByAGIAAoAkAoArwBNgIsCwJAIAAoAhBBPUcEQCAGKAIwIQcMAQsgAEEREA4gAEEGEA4gAEGrARAOIABB6QBBfxAdIQggABARDQcgAEEOEA4gABBiDQcgBigCMCIHQbYBRyAHQTxHcUUEQCAAIAYoAjQQrQELIAAgCBAgCyAAIAcgBigCLCAGKAI0IAYoAjxBASAMENQBIAAoAhBB/QBGDQBBfyEHIABBLBAwRQ0BDAgLCyAAQQ4QDiAEBEAgAEEOEA4LQX8hByAAEBFFDQIMBgsgAEGiD0EAEBUMBAsgABARDQMgACgCQCAGQQhqQQBBf0F/QQIQqwEgBkEBNgIkIABB/QAQDiABQUlGIAFBUUZyIQwDQAJAIAAoAhAiB0HdAEYNACAHIgRBpX9HIglFBEAgABARDQZB+fUAIQggACgCECIEQSxGIARB3QBGcg0ECwJAAkAgBEH7AEYgBEHbAEZyRQRAIARBLEcNASAAQYABEA4gAEEAEG4gAEEOEA4gAEEOEA4MAgsgACAGQShqQQAQqQEiBEEsRiAEQd0ARnJFIARBPUdxDQACQCAJRQRAIARBPUYEQEGxyQAhCAwICyAAQQAQ7gQMAQsgAEGAARAOIABBABBuIABBDhAOCyAAIAEgAkEBIAYoAihBAnFBARDVAUEASA0HDAELIAZBADYCOCAGQQA2AjQCQCABBEAgBiAAIAIQ0QMiBDYCNCAERQ0HIAAgBCABELcCDQcgBkG2ATYCMCAGIAAoAkAoArwBNgIsDAELIAAQtAINByAAIAZBMGogBkEsaiAGQTRqIAZBPGogBkE4akEAQdsAELwBDQcLAkAgCUUEQCAAIAYoAjgQ7gQMAQsgAEGAARAOIAAgBi0AOBBuIABBDhAOIAAoAhBBPUcNACAAQREQDiAAQQYQDiAAQasBEA4gAEHpAEF/EB0hBCAAEBENBiAAQQ4QDiAAEGINBiAGKAIwIghBtgFHIAhBPEdxRQRAIAAgBigCNBCtAQsgACAEECALIAAgBigCMCAGKAIsIAYoAjQgBigCPEEBIAwQ1AELIAAoAhBB3QBGDQAgB0Glf0YEQEHOzAAhCAwECyAAQSwQMEUNAQwFCwsgAEGDARAOIAAoAkAQqgEgABARDQMLAkAgBUUNACAAKAIQQT1HDQBBfyEHIABB6wBBfxAdIQEgABARDQQgACAKECAgAwRAIABBDhAOCyAAEGINBCAAQesAIAsQHRogACABECBBASEHDAQLIANFBEAgAEGAOUEAEBUMAwsgACgCQCgCgAIgDWpBsQEgDiANaxBLGiAAKAJAKAKkAiAKQRRsaiIAIAAoAgBBAWs2AgBBACEHDAMLIAAgCEEAEBUMAQsgACgCACAGKAI0EBMLQX8hBwsgBkFAayQAIAcLKwAgACgCQCgCpAFBAE4EQCAAQQYQDiAAQdkAEA4gACAAKAJALwGkARAYCwsSACAAQYN/RiAAQdUAakEuSXILEwAgACABIAIgAyAEQQBBABCKAgusAQIBfwF+IAApAgQiBKdB/////wdxIQMCQAJAIARCgICAgAiDUEUEQCACIAMgAiADShshAyAAQRBqIQADQCACIANGDQIgACACQQF0ai8BACABRg0DIAJBAWohAgwACwALIAFB/wFLDQAgAiADIAIgA0obIQMgAEEQaiEAIAFB/wFxIQEDQCACIANGDQEgACACai0AACABRg0CIAJBAWohAgwACwALQX8hAgsgAguNAQEBfyMAQRBrIgMkACADIAI3AwgCQCAAIAFBhgEgAUEAEBQiAhANDQAgACACEDsEQCAAIAIgAUEBIANBCGoQNiICEA0NASACECINASACECgNASAAIAIQDCAAQco8QQAQFkKAgICA4AAhAgwBCyAAIAIQDCAAIAFBASADQQhqEJAFIQILIANBEGokACACC6MBAgN/AX4gAEEQaiECIAEoAgAiBEEBaiEDAkAgACkCBCIFQoCAgIAIg1BFBEAgAiAEQQF0ai8BACIAQYD4A3FBgLADRyADIAWnQf////8HcU5yDQEgAiADQQF0ai8BACICQYD4A3FBgLgDRw0BIABBCnRBgPg/cSACQf8HcXJBgIAEaiEAIARBAmohAwwBCyACIARqLQAAIQALIAEgAzYCACAACygAIAAgAkEwIAJBABAUIgIQDQRAIAFBADYCAEF/DwsgACABIAIQ6QMLMwEBfwJAIAFCgICAgHBUDQAgAaciAy8BBkESRw0AIANBIGoPCyACBEAgAEESEJwDC0EAC10BAX9BfyEEAkAgACABECsiARANDQAgACABpyACEJQEIQQgACABEAwgBA0AIANBgIABcUUEQEEAIQQgA0GAgAJxRQ0BIAAQ+wFFDQELIABBiApBABAWQX8hBAsgBAvWAgIDfwJ8IAEQViEGIAIQViEEAkACQAJ8AkACQAJAAkACQAJAAkACQCAGQQhqDhACAQoKCgoKAwQACQkKCgoFCgsgBEEBRw0JIAGnIAKnRg8LIARBeUcNCCABpyACpxCVAkUhBQwICyABpyACp0YgBEF4RnEhBQwHCyAEQX9HDQYgAacgAqdGIQUMBgsgAae3IQcgBEEHRg0BIAQNBSACp7cMAwsgARBJIQcgBEUNASAEQQdHDQQLIAIQSQwBCyACp7cLIQgCQCADBEAgCL1C////////////AIMiAUKBgICAgICA+P8AVCAHvUL///////////8AgyICQoCAgICAgID4/wBYcUUEQCACQoGAgICAgID4/wBUIAFCgICAgICAgPj/AFZzDwsgA0ECRw0BCyAHIAhhDwsgB70gCL1RDwsgBCAGRiEFCyAAIAEQDCAAIAIQDCAFCzQBAX8CQCABQYCAAXFFBEAgAUGAgAJxRQ0BIAAQ+wFFDQELIAAgAkGqDBDIAUF/IQMLIAMLkAUBBH8jAEEQayIIJAACQAJAAkACQCABQoCAgIBwVCACQv////8PVnINACACpyEGAkACQAJAAkACQAJAAkACQAJAIAGnIgUvAQYiB0EIaw4WCAkJCQkJCQkJCQkJCQYFBQQEAwMCAQALIAdBAkcNCCAFKAIoIgcgBksNCSAGIAdHDQggBS0ABUEJcUEJRw0IIAUoAhAhBgNAAkAgBigCLCIHBEAgBygCECEGAkAgBy8BBkEBaw4CAAIMCyAGLQARRQ0CDAsLIAAgBSADIAQQlwQhBwwNCyAHLQAFQQhxDQALDAgLQX8hByAAIAhBCGogAxBbDQogBSgCKCAGTQ0FIAUoAiQgBkEDdGogCCsDCDkDAAwJC0F/IQcgACAIQQhqIAMQWw0JIAUoAiggBk0NBCAFKAIkIAZBAnRqIAgrAwi2OAIADAgLQX8hByAAIAhBBGogAxDGAQ0IIAUoAiggBk0NAyAFKAIkIAZBAnRqIAgoAgQ2AgAMBwtBfyEHIAAgCEEEaiADEMYBDQcgBSgCKCAGTQ0CQQEhByAFKAIkIAZBAXRqIAgoAgQ7AQAMBwtBfyEHIAAgCEEEaiADEMYBDQYgBSgCKCAGTQ0BIAUoAiQgBmogCCgCBDoAAAwFC0F/IQcgACAIQQRqIAMQ1AUNBSAFKAIoIAZNDQAgBSgCJCAGaiAIKAIEOgAADAQLIAAgBEHTDhB5IQcMBAsgBSgCKCAGTQ0AIAAgBSgCJCAGQQN0aiADEB8MAgsgACACEDghBSAAIAIQDCAFRQRAIAAgAxAMQX8hBwwDCyAAIAEgBSADIAQQlwIhByAAIAUQEwwCCyAAIAUoAiQgBkEDdGogAxAfC0EBIQcLIAhBEGokACAHCzwBAX8jAEHQAGsiAiQAIAIgAQR/IAAgAkEQaiABEIkBBUG10gALNgIAIABBiN0AIAIQ0gIgAkHQAGokAAugogEDIH8FfgJ8IwBB4ABrIgghESAIJAAgACgCECEWQoCAgIDgACEoAkAgABCCAQ0AAn8CQAJAAkACQAJAAkAgAUL/////b1gEQCAGQQRxRQ0BIAGnIggiBigCPCEHIAgoAhgiGSgCJCETIBkoAiAiECgCMCEJIBAvASohCyAGQQA2AjwgCCAWKAKMATYCECAIKAIgIRUgCCgCMCEGIAgoAiQhEiAWIAhBEGoiFDYCjAEgEiALQQN0aiEaIBUhGCAGIQsgCCgCDEUNBgwECyABpyIZLwEGIgdBDUYNAiAWKAJEIAdBGGxqKAIQIgcNAQsgAEGpNkEAEBYMBgsgACABIAIgBCAFIAYgBxEVACEoDAULIBkoAiAiEC8BLiEVIBAvASohCSAQLwEoIQcgESAQLQAQNgJYIBEgATcDOCARIAQ2AlQgEUHIAGoQcSAZKAIkIRMgCCAHIAdBACAEIAdIGyAGQQJxQQF2GyIGIAkgFWpqQQN0QQ9qQfD//wFxayIYJAAgBSEVIAZFDQEgBCAQLwEoELQBIgdBACAHQQBKGyEHA0AgByASRgRAIAcgEC8BKCIIIAcgCEsbIRUDQCAHIBVHBEAgGCAHQQN0akKAgICAMDcDACAHQQFqIQcMAQsLIBEgCDYCVCAYIRUMAwUgGCASQQN0IghqIAUgCGopAwAQDzcDACASQQFqIRIMAQsACwALQQEMAgsgESAVNgJAIBEgGCAGQQN0aiISNgJEIBAvASohCEEAIQcDQCAHIAhHBEAgEiAHQQN0akKAgICAMDcDACAHQQFqIQcMAQsLIBAoAhQhBiARIBYoAowBNgIwIBYgEUEwaiIUNgKMASAQKAIwIQkgEiAIQQN0aiIHIRoLQQALIQgDQAJAAkACQAJAIAhFBEAgEkEIaiEbIBJBEGohHCASQRhqIR0gFUEIaiEeIBVBEGohHyAVQRhqISAgGkEYaiEiIAJCIIinIiNBfnEhJCARQTBqISUgEUEgaiEhIAchCAJAA0ACQCAGQQFqIQtCACEoQoCAgIAwIQECQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgBi0AACIOQQFrDvMB1AEAJAiRAQkKCwwNDg8QERITFBcVFhgZGhsgISIjHB8dHigmJikpKivYAeMBLC0uL9cBMDEyMzQ1Njc4ODk5Op4BoQE8Oz2OAY8BkAGSAZMBlAGcAZ0BoAGfAaIBlQGWAZcBmAGZAaMBpAGlAZoBmgGbAZsBPj9AQUJDa2xtcXJzdG5vcHV8e3h/gAGBAcgByQHKAcsBywHLAcsBywHLAXZ2dneCAYQBhgGDAYUBiAGHAYkBigGLAYwB1wHVAdYB1gHiAa4BrQGwAa8BsQGxAbMBsgGnAbQBjQHFAcYBxwGpAaoBqwGmAagBrAG1AbcBtgG7AbwBvQG+AcQBwwG/AcABwQHCAbgBugG5AdEB3AEBAQEBAQEBAQECAwQFBkRFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpagd+fXp5JSUlJcwBzQHOAc8B0wELIAcgECgCNCALKAAAQQN0aikDABAPNwMAIAZBBWohCyAHQQhqIQgM2wELIAcgDkGzAWutNwMAIAdBCGohCAzaAQsgByALLAAArTcDACAGQQJqIQsgB0EIaiEIDNkBCyAHIAsuAACtNwMAIAZBA2ohCyAHQQhqIQgM2AELIAcgECgCNCAGLQABQQN0aikDABAPNwMAIAdBCGohCCAGQQJqIQsM1wELIAcgCSAQKAI0IAYtAAFBA3RqKQMAEA8gEyAUEI0EIgE3AwAgB0EIaiEIIAZBAmohCyABEA1FDdYBDNgBCyAHIAlBLxAyNwMAIAdBCGohCAzVAQsgCSAHQQhrIggpAwAiAUEwIAFBABAUIgEQDQ3YASAJIAgpAwAQDCAIIAE3AwAM0wELIAcgCSALKAAAEGA3AwAgBkEFaiELIAdBCGohCAzTAQsgB0KAgICAMDcDACAHQQhqIQgM0gELIAdCgICAgCA3AwAgB0EIaiEIDNEBCyAQLQAQQQFxIQgCQAJAAn4gAiAjQX9GDQAaIAIgCA0AGiAkQQJHDQEgCSkDwAELEA8hKAwBCyAJIAIQKyIoEA0N1QELIAcgKDcDACAHQQhqIQgM0AELIAdCgICAgBA3AwAgB0EIaiEIDM8BCyAHQoGAgIAQNwMAIAdBCGohCAzOAQsgByAJEDwiATcDACAHQQhqIQggARANRQ3NAQzPAQsgBkECaiELAkACQAJAAkACQAJAAkACQCAGLQABDgcAAQIDBAUGBwsgBwJ+QQAhCEEAIQogCSAJKAIoKQMIQQgQUyIBEA1FBEAgCSABpyIMQTBBAxCDASAErTcDAAJAIARBAEwNACAJIARBA3QQLyIKBEADQCAEIAhGDQIgCiAIQQN0Ig1qIAUgDWopAwAQDzcDACAIQQFqIQgMAAsACyAJIAEQDEKAgICA4AAMAgsgDCAENgIoIAwgCjYCJCAJIAFBwwEgCSkDqAEQD0EDEBsaIAkgAUHOAEKAgICAMCAJKQOwASInICdBgDAQeBoLIAELIgE3AwAgB0EIaiEIIAEQDUUN0wEM1QELIAcCfiAEIBAvASgQtAEhCEEAIQoCQCAJIAkoAigpAwhBCRBTIgEQDQ0AIAkgAaciDUEwQQMQgwEgBK03AwAgCEEAIAhBAEobIQ4DQAJAAkACQCAKIA5GBEAgCCAEIAQgCEgbIQoDQCAIIApGDQQgCSABIAggBSAIQQN0aikDABAPQQcQnwEhDCAIQQFqIQggDEEATg0ACwwBCyAJIBQgCkEBEIoEIgxFDQAgCSANIAoQlQFBJxCDASIPDQEgCSgCECAMEPoBCyAJIAEQDEKAgICA4AAhAQwDCyAPIAw2AgAgCkEBaiEKDAELCyAJIAFBwwEgCSkDqAEQD0EDEBsaIAkgAUHOACAJKAIQKAKMASkDCBAPQQMQGxogAQwBCyABCyIBNwMAIAdBCGohCCABEA1FDdIBDNQBCyAHIBQpAwgQDzcDACAHQQhqIQgM0QELIAcgAxAPNwMAIAdBCGohCAzQAQsgByAZKAIoIgYEfiAGrUKAgICAcIQQDwVCgICAgDALNwMAIAdBCGohCAzPAQsgByAJQoCAgIAgEFUiATcDACAHQQhqIQggARANRQ3OAQzQAQsgBwJ+AkAgCRC+BSIKBEAgCSAKELwFIQggCSAKEBMgCA0BCyAJQZoTQQAQFkKAgICA4AAMAQsCfiAIKQNoIgEQEgRAQoCAgIDgACAJQoCAgIAgEFUiARANDQEaIAggATcDaAsgARAPCwsiATcDACAHQQhqIQggARANRQ3NAQzPAQsQAQALIAsvAAAhCwJAIAkQUSIBEA0NACAEIAsgBCALShshCiALIQgDQCAIIApGDQEgCCALayEMIAhBA3QhDSAIQQFqIQggCSABIAwgBSANaikDABAPQQcQnwFBAE4NAAsgCSABEAxCgICAgOAAIQELIAcgATcDACAHQQhqIQggBkEDaiELIAEQDUUNywEMzQELIAkgB0EIayIIKQMAEAwMygELIAkgB0EQayIGKQMAEAwgBiAHQQhrIggpAwA3AwAMyQELIAkgB0EYayIGKQMAEAwgBiAHQRBrIgYpAwA3AwAgBiAHQQhrIggpAwA3AwAMyAELIAcgB0EIaykDABAPNwMAIAdBCGohCAzHAQsgByAHQRBrKQMAEA83AwAgByAHQQhrKQMAEA83AwggB0EQaiEIDMYBCyAHIAdBGGspAwAQDzcDACAHIAdBEGspAwAQDzcDCCAHIAdBCGspAwAQDzcDECAHQRhqIQgMxQELIAcgB0EIayIGKQMANwMAIAYgB0EQaykDABAPNwMAIAdBCGohCAzEAQsgByAHQQhrIgYpAwAiATcDACAGIAdBEGsiBikDADcDACAGIAEQDzcDACAHQQhqIQgMwwELIAcgB0EIayIGKQMAIgE3AwAgB0EQayIIKQMAIScgCCAHQRhrIggpAwA3AwAgBiAnNwMAIAggARAPNwMAIAdBCGohCAzCAQsgByAHQQhrIgYpAwAiATcDACAHQRBrIggpAwAhJyAIIAdBGGsiCCkDADcDACAGICc3AwAgCCAHQSBrIgYpAwA3AwAgBiABEA83AwAgB0EIaiEIDMEBCyAHQRBrIgYpAwAhASAGIAdBGGsiBikDADcDACAGIAE3AwAMvwELIAdBGGsiBikDACEBIAYgB0EQayIGKQMANwMAIAdBCGsiCCkDACEnIAggATcDACAGICc3AwAMvgELIAdBIGsiBikDACEBIAYgB0EYayIGKQMANwMAIAdBEGsiCCkDACEnIAggB0EIayIIKQMANwMAIAYgJzcDACAIIAE3AwAMvQELIAdBKGsiBikDACEBIAYgB0EgayIGKQMANwMAIAdBGGsiCCkDACEnIAggB0EQayIIKQMANwMAIAYgJzcDACAIIAdBCGsiBikDADcDACAGIAE3AwAMvAELIAdBCGsiBikDACEBIAYgB0EQayIGKQMANwMAIAdBGGsiCCkDACEnIAggATcDACAGICc3AwAMuwELIAdBEGsiBikDACEBIAYgB0EYayIGKQMANwMAIAdBIGsiCCkDACEnIAggATcDACAGICc3AwAMugELIAdBEGsiBikDACEBIAYgB0EYayIGKQMANwMAIAdBIGsiCCkDACEnIAggB0EoayIIKQMANwMAIAYgJzcDACAIIAE3AwAMuQELIAdBCGsiBikDACEBIAYgB0EQayIGKQMANwMAIAYgATcDAAy4AQsgB0EgayIGKQMAIQEgBiAHQRBrIgYpAwA3AwAgB0EIayIIKQMAIScgCCAHQRhrIggpAwA3AwAgBiABNwMAIAggJzcDAAy3AQsgByAJIBAoAjQgCygAAEEDdGopAwAQDyATIBQQjQQiATcDACAHQQhqIQggBkEFaiELIAEQDUUNtwEMuQELIA5B7AFrIQ0MAQsgCy8AACENIAZBA2ohCwsgFCALNgIgIAkgByANQQN0ayIIQQhrKQMAQoCAgIAwQoCAgIAwIA0gCEEAEOMBIgEQDQ24ASAOQSNGDbsBQX8hBiANQX8gDUEAThshCgNAIAYgCkcEQCAJIAggBkEDdGopAwAQDCAGQQFqIQYMAQsLIAcgDUF/c0EDdGoiBiABNwMAIAZBCGohCAy0AQsgCy8AACEIIBQgBkEDaiIKNgIgQX4hCyAJIAcgCEEDdGsiDEEQaykDACAMQQhrKQMAIAggDEEAEIwEIgEQDQRAIAohCwy4AQsDQCAIIAtHBEAgCSAMIAtBA3RqKQMAEAwgC0EBaiELDAELCyAHQX4gCGtBA3RqIgYgATcDACAGQQhqIQggCiELDLMBCyALLwAAIQggFCAGQQNqIgs2AiAgCSAHIAhBA3RrIgpBCGspAwAgCkEQaykDAEKAgICAMCAIIApBABDjASIBEA0NtgFBfiEGIA5BJUYNuQEDQCAGIAhHBEAgCSAKIAZBA3RqKQMAEAwgBkEBaiEGDAELCyAHQX4gCGtBA3RqIgYgATcDACAGQQhqIQgMsgELIAZBA2ohCiALLwAAIQgCQCAJEFEiARANRQRAQQAhCyAHIAhBA3RrIQwDQCAIIAtGDQIgCSABIAsQlQEgDCALQQN0aiINKQMAQYeAARAbIQ4gDUKAgICAMDcDACALQQFqIQsgDkEATg0ACyAJIAEQDAsgCiELDLYBCyAMIAE3AwAgDEEIaiEIIAohCwyxAQsgBkEDaiEKIAkgB0EYayIMKQMAQQIgB0EQayIIIAsvAAAQmgMiARANBEAgCiELDLUBCyAJIAwpAwAQDCAJIAgpAwAQDCAJIAdBCGspAwAQDCAMIAE3AwAgCiELDLABC0KAgICAECEoAkAgB0EIaykDACIBECINAEKBgICAECEoIAEQEg0AIABB3d8AQQAQFgy0AQsgByAoNwMAIAdBCGohCAyvAQsgAxASRQ2tASAJQe35AEEAEBYMsgELIAchCCAHQRBrKQMAIQECfwJAAkAgB0EIaykDACInQv////9vWA0AICenIgovAQYQ+AFFDQAgCigCKCIMRQ0AIAwoAhAiDSANKAIYQX9zQQJ0Qfh5cmooAgAhCiANECohDQJAA0AgCgRAIA0gCkEBayIKQQN0aiIOKAIEQcEBRg0CIA4oAgBB////H3EhCgwBCwsgCUHo3ABBABAWDAILIAFCgICAgHBUDQAgDCgCFCAKQQN0aikDACInQoCAgIBwg0KAgICAgH9SDQAgCSAnEJgCIQwgAacoAhAiDSAMIA0oAhhxQX9zQQJ0aigCACEKIA0QKiENA0AgCgRAQQAgDSAKQQFrQQN0aiIKKAIEIAxGDQQaIAooAgBB////H3EhCgwBCwsgCUGiHEEAEBYMAQsgCRApC0F/C0EATg2tAQyxAQsCfyAHQRBrIggpAwAhAQJ/AkACQCAHQQhrIg4pAwAiJ0L/////b1gEQCAJECkMAQsgJ6ciDCgCECINIA0oAhhBf3NBAnRB+HlyaigCACEKIA0QKiENAkACQANAIAoEQCANIApBAWsiCkEDdGoiDygCBEHBAUYNAiAPKAIAQf///x9xIQoMAQsLQX8gCUH3ABDJBSInEA0NBBogCSAMQcEBQQcQgwEiCkUEQCAJICcQDEF/DAYLIAogJxAPIic3AwAMAQsgDCgCFCAKQQN0aikDABAPIScLIAkgJxCYAiEKIAFC/////29YBEAgCRApIAkgChATDAELIAkgAacgCkEHEIMBIQwgCSAKEBMgDA0BC0F/DAILIAxCgICAgDA3AwBBAAsLQQBIDbABIAkgCCkDABAMIAkgDikDABAMDKwBCyAJIAdBCGsiBykDABCUAQyvAQsgCygAACEIIAZBBmohCwJAAkACQAJAAkACQCAGLQAFIgoOBQABAgMEBQsgCUGAgAEgCBDgARoMswELIAkgCBDMBQyyAQsgCSAIEOIBDLEBCyAJQdr8AEEAENICDLABCyAJQZrZAEEAEBYMrwELIBEgCjYCECAJQYXjACARQRBqEFAMrgELIAsvAAAhCCAGLwADIQogFCAGQQVqIgs2AiAgCkEBayEMAn4gCSAHIAhBA3RrIgpBCGsiDSkDACAJKQO4ARBaBEAgCUKAgICAMCAIBH4gCikDAAVCgICAgDALQQIgDBCZAwwBCyAJIA0pAwBCgICAgDBCgICAgDAgCCAKQQAQ4wELIgEQDQ2tAUF/IQYDQCAGIAhHBEAgCSAKIAZBA3RqKQMAEAwgBkEBaiEGDAELCyAHIAhBf3NBA3RqIgYgATcDACAGQQhqIQgMqQELIAZBA2ohCiALLwAAQQFrIQ4CQCAJIBFBGGogB0EIayIIKQMAEIsEIgsEQAJ+IAkgB0EQayIMKQMAIAkpA7gBEFoEQCAJQoCAgIAwIBEoAhgiDQR+IAspAwAFQoCAgIAwC0ECIA4QmQMMAQsgCSAMKQMAQoCAgIAwIBEoAhgiDSALECQLIQEgCSALIA0QmAMgARANRQ0BCyAKIQsMrQELIAkgDCkDABAMIAkgCCkDABAMIAwgATcDACAKIQsMqAELIAdBEGsiBiAJQoCAgIAwIAYpAwAgB0EIayIIKQMAEMsFNwMADKcBCyAJIAdBCGsiCCkDABD8ASIBEA0NqgEgCSAIKQMAEAwgCCABNwMADKUBCyAHQQhrIgopAwAhKCMAQTBrIggkACAJEL4FIgwEfiAJIAwQYAVCgICAgCALIQEgCSAMEBMCQCABEA0EQCABIScMAQsCQCAJIAhBIGoQkAMiJxANBEAgASEoDAELIAggCCkDICIpNwMAIAggKDcDGCAIIAE3AxAgCCAIKQMoIig3AwggCUEoQQQgCBCDAyAJIAEQDCAJICkQDAsgCSAoEAwLIAhBMGokACAnEA0NqQEgCSAKKQMAEAwgCiAnNwMADKQBCyAGQQVqIQogCSgCyAEoAhAiDCALKAAAIg0gDCgCGHFBf3NBAnRqKAIAIQggDBAqIQwCQANAIAgEQEEBIQsgDCAIQQFrQQN0aiIIKAIEIA1GDQIgCCgCAEH///8fcSEIDAELCyAJIAkpA8ABIA0QeiILQQBODQBBfyELCyALQQBIBEAgCiELDKkBCyAHIAtBAEetQoCAgIAQhDcDACAHQQhqIQggCiELDKQBCyAGQQVqIQoCfiAOQTdrIQ0gCSgCyAEiDigCECIMIAsoAAAiCCAMKAIYcUF/c0ECdGooAgAhCyAMECohDAJAA0AgC0UNASAIIAwgC0EBayILQQN0aiIPKAIERwRAIA8oAgBB////H3EhCwwBCwsgDigCFCALQQN0aikDACIBEIYBBEAgCSAIEOIBQoCAgIDgAAwCCyABEA8MAQsgCSAJKQPAASIBIAggASANEBQLIgEQDQRAIAohCwyoAQsgByABNwMAIAdBCGohCCAKIQsMowELIAsoAAAhCCAGQQVqIQsgCSAIIAdBCGsiCCkDACAOQTlrEMoFQQBODaIBDKQBCyAGQQVqIQogCygAACELIAdBEGsiCCgCAEUEQCAJIAsQ0AIgCiELDKYBCyAJIAsgB0EIaykDAEECEMoFIgZBHnZBAnEhDCAKIQsgBkEATg2hAQyiAQsgCygAACEKIAchCCAGQQZqIQsCfyAGLQAFIQ0gCSgCwAEiDygCECIOIA4oAhggCnFBf3NBAnRqKAIAIQwgDhAqIQ4CQAJAAkACQAJAA0AgDEUNASAMQQN0IA5qIhdBCGshDCAKIBdBBGsoAgBHBEAgDCgCAEH///8fcSEMDAELCyANQYABcQRAIAwtAANBBHENAwwECyANQcAAcUUNAiAMKAIAIgxBgICAIHENAiAMQYCAgIB8cUGAgICABEYNASAMQYCAgMABcUGAgIDAAUYNAgwBCyANQYABcQ0BIA8tAAVBAXENAQsgCSAKQaH8ABDIAQwCCyAJKALIASgCECINIA0oAhggCnFBf3NBAnRqKAIAIQwgDRAqIQ0DQEEAIAxFDQMaIA0gDEEBa0EDdGoiDCgCBCAKRg0BIAwoAgBB////H3EhDAwACwALIAkgChDMBQtBfwtFDaABDKQBCyALKAAAIQogByEIIAZBBmohCwJ/IAYtAAUiDEECcUEFciAMQQFxQQZyIAxBgAFxIg0bIRcgCUHIAUHAASANG2ooAgAiDigCECIPIA8oAhggCnFBf3NBAnRqKAIAIQxCgICAgMAAQoCAgIAwIA0bIQEgDxAqIQ0CQANAIAwEQCANIAxBAWtBA3RqIgwoAgQgCkYNAiAMKAIAQf///x9xIQwMAQsLIA4tAAVBAXFFDQBBfyAJIA4gCiAXEIMBIgpFDQEaIAogATcDAAtBAAtFDZ8BDKMBCyAGQQZqIQogB0EIayIIKQMAIQEgBi0ABSEOIAkpA8ABIienKAIQIgwgCygAACINIAwoAhhxQX9zQQJ0aigCACELIAwQKiEMIAkgJyANIAFCgICAgDBCgICAgDACfwJAA0AgC0UNASALQQN0IAxqQQhrIg8oAgAhCyANIA8oAgRHBEAgC0H///8fcSELDAELC0GAwAEgC0GAgIAgcUUNARoLIA5Bhs4BcgsQeEEfdQRAIAohCwyjAQsgCSAIKQMAEAwgCiELDJ4BCyAHIBIgCy8AAEEDdGopAwAQDzcDACAGQQNqIQsgB0EIaiEIDJ0BCyAJIBIgCy8AAEEDdGogB0EIayIIKQMAEB8gBkEDaiELDJwBCyAJIBIgCy8AAEEDdGogB0EIaykDABAPEB8gBkEDaiELDJoBCyAHIBUgCy8AAEEDdGopAwAQDzcDACAGQQNqIQsgB0EIaiEIDJoBCyAJIBUgCy8AAEEDdGogB0EIayIIKQMAEB8gBkEDaiELDJkBCyAJIBUgCy8AAEEDdGogB0EIaykDABAPEB8gBkEDaiELDJcBCyAHIBIgBi0AAUEDdGopAwAQDzcDACAGQQJqIQsgB0EIaiEIDJcBCyAJIBIgBi0AAUEDdGogB0EIayIIKQMAEB8gBkECaiELDJYBCyAJIBIgBi0AAUEDdGogB0EIaykDABAPEB8gBkECaiELDJQBCyAHIBIpAwAQDzcDACAHQQhqIQgMlAELIAcgGykDABAPNwMAIAdBCGohCAyTAQsgByAcKQMAEA83AwAgB0EIaiEIDJIBCyAHIB0pAwAQDzcDACAHQQhqIQgMkQELIAkgEiAHQQhrIggpAwAQHwyQAQsgCSAbIAdBCGsiCCkDABAfDI8BCyAJIBwgB0EIayIIKQMAEB8MjgELIAkgHSAHQQhrIggpAwAQHwyNAQsgCSASIAdBCGspAwAQDxAfIAchCAyMAQsgCSAbIAdBCGspAwAQDxAfIAchCAyLAQsgCSAcIAdBCGspAwAQDxAfIAchCAyKAQsgCSAdIAdBCGspAwAQDxAfIAchCAyJAQsgByAVKQMAEA83AwAgB0EIaiEIDIgBCyAHIB4pAwAQDzcDACAHQQhqIQgMhwELIAcgHykDABAPNwMAIAdBCGohCAyGAQsgByAgKQMAEA83AwAgB0EIaiEIDIUBCyAJIBUgB0EIayIIKQMAEB8MhAELIAkgHiAHQQhrIggpAwAQHwyDAQsgCSAfIAdBCGsiCCkDABAfDIIBCyAJICAgB0EIayIIKQMAEB8MgQELIAkgFSAHQQhrKQMAEA8QHyAHIQgMgAELIAkgHiAHQQhrKQMAEA8QHyAHIQgMfwsgCSAfIAdBCGspAwAQDxAfIAchCAx+CyAJICAgB0EIaykDABAPEB8gByEIDH0LIAcgEygCACgCECkDABAPNwMAIAdBCGohCAx8CyAHIBMoAgQoAhApAwAQDzcDACAHQQhqIQgMewsgByATKAIIKAIQKQMAEA83AwAgB0EIaiEIDHoLIAcgEygCDCgCECkDABAPNwMAIAdBCGohCAx5CyAJIBMoAgAoAhAgB0EIayIIKQMAEB8MeAsgCSATKAIEKAIQIAdBCGsiCCkDABAfDHcLIAkgEygCCCgCECAHQQhrIggpAwAQHwx2CyAJIBMoAgwoAhAgB0EIayIIKQMAEB8MdQsgCSATKAIAKAIQIAdBCGspAwAQDxAfIAchCAx0CyAJIBMoAgQoAhAgB0EIaykDABAPEB8gByEIDHMLIAkgEygCCCgCECAHQQhrKQMAEA8QHyAHIQgMcgsgCSATKAIMKAIQIAdBCGspAwAQDxAfIAchCAxxCyAHIBMgCy8AAEECdGooAgAoAhApAwAQDzcDACAGQQNqIQsgB0EIaiEIDHALIAkgEyALLwAAQQJ0aigCACgCECAHQQhrIggpAwAQHyAGQQNqIQsMbwsgCSATIAsvAABBAnRqKAIAKAIQIAdBCGspAwAQDxAfIAZBA2ohCyAHIQgMbgsgBkEDaiEKIBMgCy8AACIIQQJ0aigCACgCECkDACIBEIYBRQRAIAcgARAPNwMAIAdBCGohCCAKIQsMbgsgCSAQIAhBARDKAiAKIQsMcQsgBkEDaiEKIBMgCy8AACIIQQJ0aigCACgCECILKQMAEIYBRQRAIAkgCyAHQQhrIggpAwAQHyAKIQsMbQsgCSAQIAhBARDKAiAKIQsMcAsgBkEDaiEKIBMgCy8AACIIQQJ0aigCACgCECILKQMAEIYBRQRAIAkgECAIQQEQygIgCiELDHALIAkgCyAHQQhrIggpAwAQHyAKIQsMawsgCSASIAsvAABBA3RqQoCAgIDAABAfIAZBA2ohCyAHIQgMagsgBkEDaiEKIBIgCy8AACIIQQN0aikDACIBEIYBRQRAIAcgARAPNwMAIAdBCGohCCAKIQsMagsgCSAQIAhBABDKAiAKIQsMbQsgBkEDaiEKIBIgCy8AACIIQQN0aiILKQMAEIYBRQRAIAkgCyAHQQhrIggpAwAQHyAKIQsMaQsgCSAQIAhBABDKAiAKIQsMbAsgBkEDaiEKIBIgCy8AAEEDdGoiCCkDABCGAUUEQCAJQbjXAEEAENICIAohCwxsCyAJIAggB0EIayIIKQMAEB8gCiELDGcLIAsvAAAhCiAUQRhqIQwgFCgCHCELA0AgDCALIghHBEAgCCgCBCELIAhBAmsvAQAgCkcNASAIQQhrIggtAAVBAnENASAUKAIUIApBA3RqKQMAEA8hASAIIAhBGGo2AhAgCCABNwMYIAhBCGoQRiAIIAgtAAVBAXI6AAUgCSgCECAIQQMQvgEMAQsLIAZBA2ohCyAHIQgMZgsgCygAACEKIAYvAAUhDCAHIAlCgICAgCAQVSIBNwMAIAdBCGohCCAGQQdqIQsCQAJAIAEQDQ0AAkAgDkH6AEYEQCATIAxBAnRqKAIAIgwgDCgCAEEBajYCAAwBCyAJIBQgDCAOQfkARhCKBCIMRQ0BCyAJIAcoAgAgCkEiEIMBIg0NASAWIAwQ+gELIAghBwxqCyANIAw2AgAgByAJIAoQYDcDCCAHQRBqIQgMZQsgB0EQaiEIIAsoAAAhCiAGQQVqIQsCfyAJKQPIASIBpyIOKAIQIg0gDSgCGCAKcUF/c0ECdGooAgAhDCANECohDSAHAn4CQAJAAkACQANAIAxFDQEgCiANIAxBAWsiD0EDdGoiDCgCBEcEQCAMKAIAQf///x9xIQwMAQsLIA4oAhQgD0EDdGopAwAQhgEEQCAJIAoQ4gEMAgsgDC0AA0EIcQ0DIAlBgIABIAoQ4AEMBQsgCSAJKQPAASAKEHoiDEEATg0BC0F/DAMLQoCAgIAwIAxFDQEaIAkpA8ABIQELIAEQDws3AwAgByAJIAoQYDcDCEEAC0UNZAxoCyALIAsoAABqIQsgByEIIAkQggFFDWMMZwsgCyALLgAAaiELIAchCCAJEIIBRQ1iDGYLIAsgCywAAGohCyAHIQggCRCCAUUNYQxlCyAGQQVqIQoCfyAHQQhrIggpAwAiAUL/////P1gEQCABpwwBCyAJIAEQLQsEfyAKIAsoAABqQQRrBSAKCyELIAkQggFFDWAMYgsgBkEFaiEKAn8gB0EIayIIKQMAIgFC/////z9YBEAgAacMAQsgCSABEC0LBH8gCgUgCiALKAAAakEEawshCyAJEIIBRQ1fDGELIAZBAmohCgJ/IAdBCGsiCCkDACIBQv////8/WARAIAGnDAELIAkgARAtCwR/IAssAAAgCmpBAWsFIAoLIQsgCRCCAUUNXgxgCyAGQQJqIQoCfyAHQQhrIggpAwAiAUL/////P1gEQCABpwwBCyAJIAEQLQsEfyAKBSALLAAAIApqQQFrCyELIAkQggFFDV0MXwsgByALIAsoAABqIBAoAhRrrUKAgICA0ACENwMAIAZBBWohCyAHQQhqIQgMXAsgCygAACEIIAcgBiAQKAIUa0EFaq03AwAgCCALaiELIAdBCGohCAxbCwJAIAdBCGsiCCkDACIBQv////8PVg0AIAGnIgogECgCGE8NACAQKAIUIApqIQsMWwsgCUHayQBBABBQDF4LIAchCCAHQQhrIgoCfiAKKQMAIQFBACENIwBBEGsiCiQAIAFCIIinIg5BAWoiDEEETUEAQQEgDHRBGXEbRQRAIAkgARCWBSEBCwJAAkACQCAJQRgQLyIMRQ0AIAlCgICAgCBBERBTIicQDQRAIAkgDBAaDAELIAxBADYCECAMIAE3AwAgDEEANgIIICenIAw2AiAgDkF+cUECRg0CIAEQDyIoIQECQANAAkACQCAJIAEQmQIiARAoRQRAIAEQDQ0EIAkgCkEMaiAKQQhqIAGnQREQkgENAiAJIAooAgwgCigCCCIOEGYgDkUNASAJIAEQDCAoEA8hAQNAIAkgCkEMaiAKQQhqIAGnQSEQkgENA0EAIQwgCigCDCENIAooAgghDgNAIAwgDkcEQCAJICcgDSAMQQN0aiIPKAIEQoCAgIAgIA8oAgBBAEdBAnQQGxogDEEBaiEMDAELCyAJIA0gDhBmIAkgARCZAiIBECgNCCABEA0NBSAJEIIBRQ0ACwwCCwJAICinIg4tAAVBCHFFDQAgDigCECIXECohDyAXKAIgIhdBACAXQQBKGyEXA0AgDSAXRwRAIA8tAANBEHENAiAPQQhqIQ8gDUEBaiENDAELCyAMQQE2AgggDCAOKAIoNgIMDAcLIAkgCkEMaiAKQQhqIA5BERCSAQ0DIAooAgwhDSAKKAIIIQ5BACEMA0AgDCAORwRAIAkgJyANIAxBA3RqKAIEQoCAgIAgQQAQlwIaIAxBAWohDAwBCwsgCSANIA4QZgwGCyAJEIIBRQ0BCwsgCSABEAwLIAkgJxAMDAELIAkgARAMC0KAgICA4AAhJwsgCkEQaiQAICciAQs3AwBBf0EAIAEQDRtFDVkMXQtCgYCAgBAhAUKAgICAMCEnAkACQCAHQQhrKQMAIihCgICAgHBUDQAgKKciDS8BBkERRw0AIA0oAiAhCANAAkAgCCgCCARAIAgoAhAiDCAIKAIMTw0DIAwQlQEhCiAIIAxBAWo2AhAMAQsgCCgCECIMIA0oAhAiCigCIE8NAiAKECogDEEDdGoiDigCBCEKIAggDEEBajYCECAKRQ0BIA4tAANBEHFFDQELIAkgCCkDACAKEHoiDEEASA0CIAxFDQALQoCAgIAQIQEgCSAKEGAhJwsgByABNwMIIAcgJzcDAEEAIQwLIAwNXCAHQRBqIQgMWAsgCSAHQQAQlwMNWyAHQoCAgIDQADcDCCAHQRBqIQgMVwsgB0EQaiEIIAZBAmohC0F9IAYtAAFrIQ0jAEEQayIMJABBASEKIAxBATYCDAJAAkAgByANQQN0aiINKQMAIgEQEkUEQEF/IQ5BfyEKAkAgCSABIA0pAwggDEEMahCvASIBEA0NACAMKAIMIgoNAEEAIQoMAgsgCSANKQMAEAwgDUKAgICAMDcDACAKQQBIDQIgCSABEAwLQoCAgIAwIQELIAcgATcDAEEAIQ4gByAKQQBHrUKAgICAEIQ3AwgLIAxBEGokACAORQ1WDFoLIAkgB0EBEJcDDVkgB0KAgICA0AA3AwggB0EQaiEIDFULIwBBEGsiCCQAAn8gB0EIayIKKQMAIgEQIkUEQCAJQYIdQQAQFkF/DAELQX8gCSABIAhBDGoQnwUiJxANDQAaIAkgARAMIAogJzcDACAHIAgoAgxBAEetQoCAgIAQhDcDAEEACyEKIAhBEGokACAKDVggB0EIaiEIDFQLIAdBCGspAwAQIg1SIAlBgh1BABAWDFcLIAkgB0EQayIKKQMAEAwgB0EYayIIKQMAIgEQEg1SIAkgAUEAELMBBEAgCiEHDFcLIAkgCCkDABAMDFILIAdBCGsiBykDACEBA0ACQCAHIBpNDQAgB0EIayIIKQMAIidCgICAgHCDQoCAgIDQAFENACAJICcQDCAIIQcMAQsLIAcgIkkEQCAJQes0QQAQUCAJIAEQDAxWCyAHIAdBCGsiBikDADcDACAHQRBrIggpAwAhJyAIIAdBGGsiCCkDADcDACAGICc3AwAgCCABNwMAIAdBCGohCAxRCyAJIAdBGGspAwAgB0EgaykDAEEBIAdBCGsiCBAkIgEQDQ1UIAkgCCkDABAMIAggATcDACAHIQgMUAsgBkECaiELIAkgB0EgayIIKQMAIgFBF0EGIAYtAAEiCkEBcRsgAUEAEBQiJxANDVNCgYCAgBAhAQJAICcQEg0AICcQKA0AIAgpAwAhAQJ+IApBAnEEQCAJICcgAUEAQQAQNgwBCyAJICcgAUEBIAdBCGsQNgsiARANDVQgCSAHQQhrIgYpAwAQDCAGIAE3AwBCgICAgBAhAQsgByABNwMAIAdBCGohCAxPCwJ/IAdBCGsiBikDACIBQv////8/WARAIAGnQQBHDAELIAkgARAtCyEIIAYgCEWtQoCAgIAQhDcDACAHIQgMTgsgBkEFaiEKIAkgB0EIayIIKQMAIgEgCygAACABQQAQFCIBEA0EQCAKIQsMUgsgCSAIKQMAEAwgCCABNwMAIAchCCAKIQsMTQsgBkEFaiEKIAkgB0EIaykDACIBIAsoAAAgAUEAEBQiARANBEAgCiELDFELIAcgATcDACAHQQhqIQggCiELDEwLIAkgB0EQayIIKQMAIAsoAAAgB0EIaykDAEGAgAIQlwIhByAJIAgpAwAQDCAGQQVqIQsgB0EATg1LDE0LIAZBBWohCiAJIAsoAAAQyQUiARANBEAgCiELDE8LIAcgATcDACAHQQhqIQggCiELDEoLAn4gB0EIayIIKQMAIQEgB0EQayIMKQMAIidC/////29YBEAgCRApQoCAgIDgAAwBCyABQoCAgIBwg0KAgICAgH9SBEAgCRDqA0KAgICA4AAMAQsgCSABEJgCIQcgJ6ciDigCECINIAcgDSgCGHFBf3NBAnRqKAIAIQogDRAqIQ0CQANAIAoEQCANIApBAWsiCkEDdGoiDygCBCAHRg0CIA8oAgBB////H3EhCgwBCwsgCSAHEJ4FQoCAgIDgAAwBCyAOKAIUIApBA3RqKQMAEA8LIQEgCSAIKQMAEAwgCSAMKQMAEAwgDCABNwMAIAEQDUUNSQxLCwJ/IAdBCGsiDSkDACEBIAdBEGspAwAhJwJAAkAgB0EYayIIKQMAIihC/////29YBEAgCRApDAELIAFCgICAgHCDQoCAgICAf1IEQCAJEOoDDAELIAkgARCYAiEHICinIg4oAhAiDCAHIAwoAhhxQX9zQQJ0aigCACEKIAwQKiEMA0AgCgRAIAwgCkEBayIKQQN0aiIPKAIEIAdGDQMgDygCAEH///8fcSEKDAELCyAJIAcQngULIAkgJxAMQX8MAQsgCSAOKAIUIApBA3RqICcQH0EACyEHIAkgCCkDABAMIAkgDSkDABAMIAdBAE4NSAxKCwJ/IAdBEGsiCCkDACEBIAdBCGspAwAhJwJAAkAgB0EYaykDACIoQv////9vWARAIAkQKQwBCyABQoCAgIBwg0KAgICAgH9SBEAgCRDqAwwBCyAJIAEQmAIhByAopyINKAIQIgwgByAMKAIYcUF/c0ECdGooAgAhCiAMECohDAJAA0AgCkUNASAHIAwgCkEBa0EDdGoiCigCBEcEQCAKKAIAQf///x9xIQoMAQsLIAkgB0GDHxDIAQwBCyAJIA0gB0EHEIMBIgcNAQsgCSAnEAxBfwwBCyAHICc3AwBBAAshByAJIAgpAwAQDCAHQQBODUcMSQsgCygAACEIIAZBBWohCyAJIAdBEGspAwAgCCAHQQhrIggpAwBBh4ABEBtBAE4NRgxICyALKAAAIQogByEIIAZBBWohCyAJIAdBCGspAwAgChDIBUEATg1FDEkLIAchCCAJIAdBCGspAwAgB0EQaykDABDHBUEATg1EDEgLAkAgB0EIayIIKQMAIgEQIkUEQCABEChFDQELIAkgB0EQaykDACABQQEQmwJBAEgNSAsgCSABEAwMQwsgCSAHQQhrKQMAIAdBEGspAwAQiQQgByEIDEILAn8gDkHVAEYEQEF9IAkgB0EQaykDABA4IggNARoMRwsgCygAACEIIAZBBWohC0F+CyEKIAstAAAhBiALQQFqIQsgBkEEcSENIAcgCkEDdGopAwAhJwJ+An8CQAJAAkAgBkEDcQ4CAAECC0KAgICAMCEoIAdBCGspAwAiASEqQYPOAQwCC0KAgICAMCEqQYGaASEGQoCAgIAwISggB0EIaykDACIBDAILQoCAgIAwISogB0EIaykDACIBIShBgaoBCyEGQoCAgIAwCyErQdL+ACEMIAkgCBCbBSEpAkAgBiANciIKIgZBgBBxRQRAQc3+ACEMIAZBgCBxRQ0BCyAJIAwgKUHcgwEQvwEhKQtBfyEGAkAgKRANDQAgCSABQTYgKUEBEBtBAEgNACAJIAEgJxCJBEEAIQYLIAZBAE4EQCAJICcgCCAqICsgKCAKEHghBgsgCSAHQQhrKQMAEAwgBkEedkECcSEMIAcgDkHVAEYEfyAJIAgQEyAJIAdBEGspAwAQDEF+BUF/C0EDdGohCCAGQQBIDUIMQQsgCygAACENIAZBBmohCyAOQdcARiEOIAciCEEIayIPKQMAISogB0EQayEMAn4CQAJAAkACfiAGLQAFQQFxBEBCgICAgCAgDCkDACInECgNARpCgICAgDAhKCAnELUBRQRAQb4pIQpCgICAgDAhKQwECyAJICdBOyAnQQAQFCIpEA0NBCApECgNAiApECINAkH7PCEKDAMLIAkoAigpAwgQDwshKSAJKQMwEA8hJwsgCSApEFUiKBANDQEgKqciCi0AEUEwcUUEQCAJICdBDRBTIgEQDQ0CQoCAgIAwISogCSABIAogEyAUEKAFIgEQDQ0CIAkgASAoEIkEIAFBARCyAyAJIAFBMCAKMwEsQQEQGxoCQCAOBEAgCSABIAdBGGspAwAQxwVBAE4NAQwECyAJIAEgDRDIBUEASA0DC0EAIQogCSAoQTwgARAPIgFBg4ABEBtBAEgNAiABIAkgAUE7ICgQDyIoQYCAARAbQQBODQMaDAILQZ/rAEG+4wBBqPwAQaEgEAAACyAJIApBABAWCyAJICcQDCAJICkQDCAJICoQDEF/IQogKCEpIAEhJ0KAgICAMCEoQoCAgIAwCyEBIAkgKRAMIAkgJxAMIAwgATcDACAPICg3AwAgCkEATg1ADEQLIAkgB0EQayIKKQMAIAdBCGsiCCkDABChASEBIAkgCikDABAMIAogATcDACABEA1FDT8MQQsgB0EIayIIIAkgB0EQaykDACAIKQMAEKEBIgE3AwAgByEIIAEQDUUNPgxCCyAHQQhrKQMAIQEgB0EQaykDACInEBIEQCAJIAEQOCIIRQ1CIAkgCBDQAiAJIAgQEwxCCyAJICcgARAPEKEBIgEQDQ1BIAcgATcDACAHQQhqIQgMPQsgCSAHQQhrIg0pAwAQOCIKRQ1AIAkgB0EQayIIKQMAIAogB0EYayIMKQMAQQAQFCEBIAkgChATIAEQDQ1AIAkgDSkDABAMIAkgCCkDABAMIAkgDCkDABAMIAwgATcDAAw8CyAJIAdBGGsiCCkDACAHQRBrKQMAIAdBCGspAwBBgIACEOEBIQcgCSAIKQMAEAwgB0EATg07DD0LIAdBGGsiCCkDACIoEBIhDCAJEPsBIQoCfyAMBEAgCgRAIAkgB0EQaykDABA4IghFDUEgCSAIENACIAkgCBATDEELIAggCSkDwAEQDyIoNwMAQYCAAgwBC0GAgAZBgIACIAobCyEGIAkgKCAHQRBrKQMAIAdBCGspAwAgBhDhASEGIAkgCCkDABAMIAZBHnZBAnEhDCAGQQBIDTsMOgsgB0EYayIKKQMAQv////9vWARAIAkQKQw+CyAJIAdBEGsiDSkDABA4IgxFDT0gCSAKKQMAIAwgB0EIaykDACAHQSBrIggpAwBBgIACEIgEIQYgCSAMEBMgCSAIKQMAEAwgCSAKKQMAEAwgCSANKQMAEAwgBkEedkECcSEMIAZBAEgNOgw5CyAJIAdBGGspAwAgB0EQaykDABAPIAdBCGsiCCkDAEGHgAEQzQJBAE4NOAw6CyMAQRBrIggkAAJAIAdBEGsiDikDACIoQoCAgIAQWgRAIAlBv9oAQQAQUEF/IQ0MAQtBfyENIAkgB0EIayIMKQMAIgFBwwEgAUEAEBQiARANDQAgAUEpQQEQjwQhDyAJIAEQDCAJIAwpAwBBABD2ASIBEA0NACAJIAFB6gAgAUEAEBQiJxANBEAgCSABEAwMAQsgKKchCgJAAkACQCAPRQ0AICdBKkEAEI8ERQ0AIAwpAwAgCEEMaiAIQQhqEI4CRQ0AIAkgCEEEaiAMKQMAENwBDQIgCCgCBCIPIAgoAghHDQAgB0EYayEXIAgoAgwhJkEAIQwDQCAMIA9GDQIgCSAXKQMAIAogJiAMQQN0aikDABAPQQcQnwFBAEgNAyAMQQFqIQwgCkEBaiEKDAALAAsgB0EYayEMA0AgCSABICcgCEEEahCvASIoEA0NAiAIKAIEDQEgCSAMKQMAIAogKEEHEJ8BQQBIDQIgCkEBaiEKDAALAAsgDiAKrTcDACAJIAEQDCAJICcQDEEAIQ0MAQsgCSABQQEQswEaIAkgARAMIAkgJxAMCyAIQRBqJAAgDQ07IAkgB0EIayIIKQMAEAwMNwsgBkECaiELIAchCCAJIAcgBi0AASIKQX9zQQN0QWByaikDACAHIApBAnZBf3NBA3RBQHJqKQMAIAcgCkEFdkF/c0EDdGopAwBBABDGBUUNNgw6CwJAIAdBCGsiCCkDACIBQiCIIiggB0EQayIKKQMAIidCIIgiKYRQBEAgAUIghkIghyAnQiCGQiCHfCIBQoCAgIAIfEL/////D1YNASAKIAFC/////w+DNwMADDcLICmnQQdrQW1LICinQQdrQW1Lcg0AIAogJxBJIAEQSaAQFzcDAAw2CyAJIAcQxQVFDTUMOQsgBkECaiELAkAgEiAGLQABQQN0aiIIKQMAIgFCIIgiKCAHQQhrIgcpAwAiJ0IgiIRQBEAgJ0IghkIghyABQiCGQiCHfCInQoCAgIAIfEL/////D1YNASAIICdC/////w+DNwMAIAchCAw2CyAoQvn///8PUg0AIAkgJ0ECEMMBIgEQDQ05IAkgCCkDABAPIAEQyQIiARANDTkgCSAIIAEQHyAHIQgMNQsgESABEA83AyAgESAHKQMANwMoIAkgJRDFBQ04IAkgCCARKQMgEB8gByEIDDQLIAdBCGsiCCkDACIBQiCIIiggB0EQayIKKQMAIidCIIgiKYRQBEAgJ0IghkIghyABQiCGQiCHfSIBQoCAgIAIfEL/////D1YNBCAKIAFC/////w+DNwMADDQLICmnQQdrQW1LICinQQdrQW1Lcg0DIAogJxBJIAEQSaEQFzcDAAwzCwJ8IAdBCGsiCCkDACIBQiCIIiggB0EQayIKKQMAIidCIIgiKYRQBEAgAUIghkIghyAnQiCGQiCHfiIoQoCAgIAIfEKAgICAEFoEQCAouQwCC0QAAAAAAAAAgCAoUCABICeEQoCAgIAIg0IAUnENARogCiAoQv////8PgzcDAAw0CyApp0EHa0FtSyAop0EHa0FtS3INAyAnEEkgARBJogshLCAKICwQFzcDAAwyCyAHQQhrIggpAwAiASAHQRBrIgopAwAiJ4RC/////w9WDQEgFC0AKEEEcQ0BIAoCfiAnp7cgAae3oyIsvQJ/ICyZRAAAAAAAAOBBYwRAICyqDAELQYCAgIB4CyIGt71RBEAgBq0MAQsgLBAXCzcDAAwxCyAHQQhrIggpAwAiASAHQRBrIgopAwAiJ4RC/////w9WDQAgJ6ciDEEASCABpyINQQBMcg0AIAogDCANcK03AwAMMAsjAEEQayIIJAAgB0EIayIMKQMAIQECfwJAIAkgCEEIaiAHQRBrIgopAwAQWwRAIAkgARAMDAELIAkgCCABEFsNACAKAn4CfAJAAkACQAJAAkACQCAOQZoBaw4GAAECBAUDBAsgCCsDCCAIKwMAogwFCyAIKwMIIAgrAwCjDAQLIAgrAwggCCsDABCHBgwDCyAIKwMIIAgrAwAQmQUMAgsQAQALIAgrAwggCCsDAKELIiy9An8gLJlEAAAAAAAA4EFjBEAgLKoMAQtBgICAgHgLIgq3vVEEQCAKrQwBCyAsEBcLNwMAQQAMAQsgCkKAgICAMDcDACAMQoCAgIAwNwMAQX8LIQogCEEQaiQAIAoNMyAHQQhrIQgMLwsgB0EEaygCACIIQQdrIQogCEUgCkFuSXINLSAHIQggCSAHQY0BEJICRQ0uDDILAkACfCAHQQhrIggpAwAiAUIgiKciCkUEQEQAAAAAAAAAgCABpyIGRQ0BGkQAAAAAAADgQSAGQYCAgIB4Rg0BGiAIQgAgAX1C/////w+DNwMAIAchCAwwCyAKQQdrQW1LDQEgARBJmgshLCAIICwQFzcDACAHIQgMLgsgByEIIAkgB0GMARCSAkUNLQwxCyAHQQhrIggpAwAiAadB/////wdGIAFC/////w9WckUEQCAIIAFCAXxC/////w+DNwMAIAchCAwtCyAHIQggCSAHQY8BEJICRQ0sDDALIAdBCGsiCCkDACIBp0GAgICAeEYgAUL/////D1ZyRQRAIAggAUIBfUL/////D4M3AwAgByEIDCwLIAchCCAJIAdBjgEQkgJFDSsMLwsjAEEQayIIJAACf0F/IAkgCEEIaiAHQQhrIgopAwAQWw0AGiAHAn4gCCsDCCIsIA5BAXRBoAJruKBEAAAAAAAA8L+gIi29An8gLZlEAAAAAAAA4EFjBEAgLaoMAQtBgICAgHgLIgy3vVEEQCAMrQwBCyAtEBcLNwMAICy9An8gLJlEAAAAAAAA4EFjBEAgLKoMAQtBgICAgHgLIgy3vVEEQCAMrSEBQQAMAQsgLBAXIQFBAAshDCAKIAE3AwAgCEEQaiQAIAwNLiAHQQhqIQgMKgsgBkECaiELIBIgBi0AAUEDdGoiCCkDACIBp0H/////B0YgAUL/////D1ZyRQRAIAggAUIBfEL/////D4M3AwAMKQsgESABEA83AxggCSAhQY8BEJICDS0gCSAIIBEpAxgQHwwoCyAGQQJqIQsgEiAGLQABQQN0aiIIKQMAIgGnQYCAgIB4RiABQv////8PVnJFBEAgCCABQgF9Qv////8PgzcDAAwoCyARIAEQDzcDGCAJICFBjgEQkgINLCAJIAggESkDGBAfDCcLIAdBCGsiCCkDACIBQv////8PWARAIAggAUL/////D4U3AwAgByEIDCgLIAchCCMAQRBrIgokACAJIApBDGogB0EIayINKQMAEMYBIQwgDUKAgICAMCAKNQIMQv////8PhSAMGzcDACAKQRBqJABBf0EAIAwbRQ0nDCsLIAdBCGsiCCkDACIBIAdBEGsiCikDACInhEL/////D1gEQCAKICenIAGndK03AwAMJwsgCSAHQaABEMgCRQ0mDCoLIAdBCGsiCCkDACIBIAdBEGsiCikDACInhEL/////D1gEQCAKAn4gJ6cgAad2IgZBAE4EQCAGrQwBCyAGuBAXCzcDAAwmCyMAQRBrIgokACAHQQhrIg0pAwAhAQJ/AkAgCSAKQQxqIAdBEGsiDCkDABDpAwRAIAkgARAMDAELIAkgCkEIaiABEOkDDQAgDAJ+IAooAgwgCigCCHYiDEEATgRAIAytDAELIAy4EBcLNwMAQQAMAQsgDEKAgICAMDcDACANQoCAgIAwNwMAQX8LIQwgCkEQaiQAIAxFDSUMKQsgB0EIayIIKQMAIgEgB0EQayIKKQMAIieEQv////8PWARAIAogJ6cgAad1rTcDAAwlCyAJIAdBoQEQyAJFDSQMKAsgB0EIayIIKQMAIgEgB0EQayIKKQMAIieEQv////8PWARAIAogASAngzcDAAwkCyAJIAdBrQEQyAJFDSMMJwsgB0EIayIIKQMAIAdBEGsiCikDAIQiAUL/////D1gEQCAKIAE3AwAMIwsgCSAHQa8BEMgCRQ0iDCYLIAdBCGsiCCkDACIBIAdBEGsiCikDACInhEL/////D1gEQCAKIAEgJ4VC/////w+DNwMADCILIAkgB0GuARDIAkUNIQwlCyAHQQhrIggpAwAiASAHQRBrIgopAwAiJ4RC/////w9YBEAgCiAnpyABp0itQoCAgIAQhDcDAAwhCyAJIAdBowEQlgNFDSAMJAsgB0EIayIIKQMAIgEgB0EQayIKKQMAIieEQv////8PWARAIAogJ6cgAadMrUKAgICAEIQ3AwAMIAsgCSAHQaQBEJYDRQ0fDCMLIAdBCGsiCCkDACIBIAdBEGsiCikDACInhEL/////D1gEQCAKICenIAGnSq1CgICAgBCENwMADB8LIAkgB0GlARCWA0UNHgwiCyAHQQhrIggpAwAiASAHQRBrIgopAwAiJ4RC/////w9YBEAgCiAnpyABp06tQoCAgIAQhDcDAAweCyAJIAdBpgEQlgNFDR0MIQsgB0EIayIIKQMAIgEgB0EQayIKKQMAIieEQv////8PWARAIAogJ6cgAadGrUKAgICAEIQ3AwAMHQsgCSAHQQAQwwVFDRwMIAsgB0EIayIIKQMAIgEgB0EQayIKKQMAIieEQv////8PWARAIAogJ6cgAadHrUKAgICAEIQ3AwAMHAsgCSAHQQEQwwVFDRsMHwsgB0EIayIIKQMAIgEgB0EQayIGKQMAIieEQv////8PWARAIAYgJ6cgAadGrUKAgICAEIQ3AwAMGwsgCSAHQQAQwgUMGgsgB0EIayIIKQMAIgEgB0EQayIGKQMAIieEQv////8PWARAIAYgJ6cgAadHrUKAgICAEIQ3AwAMGgsgCSAHQQEQwgUMGQsCfyAHQQhrKQMAIgFC/////29YBEAgCUHq2wBBABAWQX8MAQtBfyEIAkAgCSAHQRBrIg0pAwAiJxA4IgpFDQAgCSABIAoQeiEMIAkgChATIAxBAEgNACAJICcQDCAJIAEQDCANIAxBAEetQoCAgIAQhDcDAEEAIQgLIAgLDRwgB0EIayEIDBgLAn8gCSAHQRBrIgopAwAiASAHQQhrKQMAIicQ2gUiCEEASARAIAgMAQsgCSABEAwgCSAnEAwgCiAIQQBHrUKAgICAEIQ3AwBBAAsNGyAHQQhrIQgMFwsgCSAHQQhrIgYpAwAiARCHBCEIIAkgARAMIAYgCSAIEDI3AwAgByEIDBYLIAdBEGsiDSkDACEBQX8hCAJAIAkgB0EIaykDACInEDgiCkUNACAJIAEgCkGAgAIQ3gEhDCAJIAoQEyAMQQBIDQAgCSABEAwgCSAnEAwgDSAMQQBHrUKAgICAEIQ3AwBBACEICyAIDRkgB0EIayEIDBULIAsoAAAhCCAGQQVqIQsgCSAJKQPAASAIQQAQ3gEiCEEASA0YIAcgCEEAR61CgICAgBCENwMAIAdBCGohCAwUCyAHQQhrIggpAwAiAUL/////b1YNEiAJIAEQKyIBEA0NFyAJIAgpAwAQDCAIIAE3AwAgByEIDBMLIAdBCGsiCCkDACIBQiCIp0EIaiIKQQhNQQBBASAKdEGDAnEbDREgCSABEJgEIgEQDQ0WIAkgCCkDABAMIAggATcDACAHIQgMEgsCQCAHQRBrKQMAIgEQEkUEQCABEChFDQELIAlB8glBABAWDBYLIAdBCGsiCCkDACIBQiCIp0EIaiIKQQhNQQBBASAKdEGDAnEbDRAgCSABEJgEIgEQDQ0VIAkgCCkDABAMIAggATcDACAHIQgMEQsgBkEKaiEKIAYoAAUhDCAGLQAJIQ0gCSAHQQhrIggpAwAiASALKAAAIgsQeiIPQQBIDQ4CQCAPRQ0AIA0EQEEAIQ0gCSABQc0BIAFBABAUIicQDQR/QX8FICcQIgRAIAkgCSAnIAsgJ0EAEBQQLSENCyAJICcQDCANCyINQQBIDRAgDQ0BCwJAAkACQAJAAkACQAJAIA5B8gBrDgYAAQIDBAUGCyAJIAEgCyABQQAQFCIBEA0NFSAJIAggARAfDAULIAkgASALIAdBEGsiBykDAEGAgAIQlwIhCyAJIAgpAwAQDCALQQBIDRQMBAsgCSABIAtBABDeASILQQBIDRMgCSAIKQMAEAwgCCALQQBHrUKAgICAEIQ3AwAMAwsgByAJIAsQYDcDACAHQQhqIQcMAgsgCSABIAsgAUEAEBQiARANDREgByABNwMAIAdBCGohBwwBCyAJIAEgCyABQQAQFCIBEA0NECAJIAgpAwAQDCAIQoCAgIAwNwMAIAcgATcDACAHQQhqIQcLIAogDGpBBWshCyAHIQgMEQsgCSAIKQMAEAwgCiELDBALIAdBCGspAwAiKEKAgICAcINCgICAgDBRDQwMBQsgB0EIaykDACIoQoCAgIBwg0KAgICAIFENCwwECyAJIAdBCGspAwAiKBCHBEHFAEYNAQwDCyAJIAdBCGspAwAiKBCHBEEbRw0CCyAJICgQDAwICyAHQQhrKQMAIihCgICAgGCDQoCAgIAgUQ0HCyAJICgQDCAHQQhrQoCAgIAQNwMAIAchCAwJCyAQKAIUIQggESAONgIEIBEgCEF/cyALajYCACAJQccPIBEQUAwMCyAHIAs1AAA3AwAgBkEFaiELIAdBCGohCAwHC0IBISgMDAtCAiEoDAsLQoCAgIAwISgMCgsgB0EIayIHKQMAIQEMCgsgB0EIa0KBgICAEDcDACAHIQgMAgsgCiELDAULIAchCAtBACEMCyAIIQcgCyEGIAxFDQELCyAIIQcLQQEhCAwFC0EAIQhBACEGAkAgFikDgAEiAUKAgICAcFQNACABpyIKLwEGQQNHDQAgCigCECIKIAooAhhBf3NBAnRBqH5yaigCACEGIAoQKiEKA0ACQCAGRQRAQQAhBgwBCyAGQQN0IApqIgxBCGshBiAMQQRrKAIAQTVGDQAgBigCAEH///8fcSEGDAELCyAGRSEGCyAGBEAgFCALNgIgIAkgAUEAQQBBABDHAiAWKQOAASEBCwJAIAFCgICAgHBUDQAgAaciBi8BBkEDRw0AIAYtAAVBBXZBAXEhCAsCQCAIDQAgByEGA0AgBiIHIBpNDQEgCSAHQQhrIgYpAwAiARAMIAFCgICAgHCDQoCAgIDQAFINACABpyIIDQUgCSAHQRBrIgYpAwAQDCAJIAdBGGspAwBBARCzARoMAAsAC0KAgICA4AAhKEKAgICA4AAhASAQLQARQTBxRQ0BCyAUIAc2AiwgFCALNgIgDAELIBRBGGoQ5wNFBEAgFiAUEMEFCwN+IAcgGE0EfiABBSAJIBgpAwAQDCAYQQhqIRgMAQsLISgLIBYgFCgCADYCjAEMAgsgBiAWKQOAATcDACAWQoCAgIAgNwOAASAQKAIUIAhqIQZBACEIDAALAAsgEUHgAGokACAoC4gBAQJ/IAEoAhAiAy0AEEUEQEEADwsCQCADKAIAQQFHBEAgAgR/IAIoAgAgAxAqa0EDdQVBAAshBCAAIAMQ0gUiA0UEQEF/DwsgACgCECABKAIQEJ4CIAEgAzYCECACRQ0BIAIgAxAqIARBA3RqNgIAQQAPCyAAKAIQIAMQkQQgA0EAOgAQC0EACxAAIABBAnQgAUEDdGpBMGoLpQECAX8BfiAAIAApAzBBDxBTIgcQDUUEQCAAIARBA3RBCGoQLyIGRQRAIAAgBxAMQoCAgIDgAA8LIAYgAzsBBiAGIAQ6AAUgBiACOgAEIAYgATYCAEEAIQEgBEEAIARBAEobIQMDQCABIANGRQRAIAYgAUEDdCIEaiAEIAVqKQMAEA83AwggAUEBaiEBDAELCyAHIAYQjQEgACAHQS8gAhCpAwsgBwsTACAAQRBqIAEgAiAAKAIIEQEACxEAIABBEGogASAAKAIAEQIAC8wMAQd/AkAgAEUNACAAQQhrIgMgAEEEaygCACIBQXhxIgBqIQUCQCABQQFxDQAgAUEDcUUNASADIAMoAgAiAWsiA0GovQQoAgBJDQEgACABaiEAQay9BCgCACADRwRAIAFB/wFNBEAgAygCCCICIAFBA3YiBEEDdEHAvQRqRhogAiADKAIMIgFGBEBBmL0EQZi9BCgCAEF+IAR3cTYCAAwDCyACIAE2AgwgASACNgIIDAILIAMoAhghBgJAIAMgAygCDCIBRwRAIAMoAggiAiABNgIMIAEgAjYCCAwBCwJAIANBFGoiAigCACIEDQAgA0EQaiICKAIAIgQNAEEAIQEMAQsDQCACIQcgBCIBQRRqIgIoAgAiBA0AIAFBEGohAiABKAIQIgQNAAsgB0EANgIACyAGRQ0BAkAgAygCHCICQQJ0Qci/BGoiBCgCACADRgRAIAQgATYCACABDQFBnL0EQZy9BCgCAEF+IAJ3cTYCAAwDCyAGQRBBFCAGKAIQIANGG2ogATYCACABRQ0CCyABIAY2AhggAygCECICBEAgASACNgIQIAIgATYCGAsgAygCFCICRQ0BIAEgAjYCFCACIAE2AhgMAQsgBSgCBCIBQQNxQQNHDQBBoL0EIAA2AgAgBSABQX5xNgIEIAMgAEEBcjYCBCAAIANqIAA2AgAPCyADIAVPDQAgBSgCBCIBQQFxRQ0AAkAgAUECcUUEQEGwvQQoAgAgBUYEQEGwvQQgAzYCAEGkvQRBpL0EKAIAIABqIgA2AgAgAyAAQQFyNgIEIANBrL0EKAIARw0DQaC9BEEANgIAQay9BEEANgIADwtBrL0EKAIAIAVGBEBBrL0EIAM2AgBBoL0EQaC9BCgCACAAaiIANgIAIAMgAEEBcjYCBCAAIANqIAA2AgAPCyABQXhxIABqIQACQCABQf8BTQRAIAUoAggiAiABQQN2IgRBA3RBwL0EakYaIAIgBSgCDCIBRgRAQZi9BEGYvQQoAgBBfiAEd3E2AgAMAgsgAiABNgIMIAEgAjYCCAwBCyAFKAIYIQYCQCAFIAUoAgwiAUcEQCAFKAIIIgJBqL0EKAIASRogAiABNgIMIAEgAjYCCAwBCwJAIAVBFGoiAigCACIEDQAgBUEQaiICKAIAIgQNAEEAIQEMAQsDQCACIQcgBCIBQRRqIgIoAgAiBA0AIAFBEGohAiABKAIQIgQNAAsgB0EANgIACyAGRQ0AAkAgBSgCHCICQQJ0Qci/BGoiBCgCACAFRgRAIAQgATYCACABDQFBnL0EQZy9BCgCAEF+IAJ3cTYCAAwCCyAGQRBBFCAGKAIQIAVGG2ogATYCACABRQ0BCyABIAY2AhggBSgCECICBEAgASACNgIQIAIgATYCGAsgBSgCFCICRQ0AIAEgAjYCFCACIAE2AhgLIAMgAEEBcjYCBCAAIANqIAA2AgAgA0GsvQQoAgBHDQFBoL0EIAA2AgAPCyAFIAFBfnE2AgQgAyAAQQFyNgIEIAAgA2ogADYCAAsgAEH/AU0EQCAAQQN2IgFBA3RBwL0EaiEAAn9BmL0EKAIAIgJBASABdCIBcUUEQEGYvQQgASACcjYCACAADAELIAAoAggLIQIgACADNgIIIAIgAzYCDCADIAA2AgwgAyACNgIIDwtBHyECIABB////B00EQCAAQQh2IgEgAUGA/j9qQRB2QQhxIgF0IgIgAkGA4B9qQRB2QQRxIgJ0IgQgBEGAgA9qQRB2QQJxIgR0QQ92IAEgAnIgBHJrIgFBAXQgACABQRVqdkEBcXJBHGohAgsgAyACNgIcIANCADcCECACQQJ0Qci/BGohAQJAAkACQEGcvQQoAgAiBEEBIAJ0IgdxRQRAQZy9BCAEIAdyNgIAIAEgAzYCACADIAE2AhgMAQsgAEEAQRkgAkEBdmsgAkEfRht0IQIgASgCACEBA0AgASIEKAIEQXhxIABGDQIgAkEddiEBIAJBAXQhAiAEIAFBBHFqIgdBEGooAgAiAQ0ACyAHIAM2AhAgAyAENgIYCyADIAM2AgwgAyADNgIIDAELIAQoAggiACADNgIMIAQgAzYCCCADQQA2AhggAyAENgIMIAMgADYCCAtBuL0EQbi9BCgCAEEBayIAQX8gABs2AgALC6gBAAJAIAFBgAhOBEAgAEQAAAAAAADgf6IhACABQf8PSQRAIAFB/wdrIQEMAgsgAEQAAAAAAADgf6IhACABQf0XIAFB/RdIG0H+D2shAQwBCyABQYF4Sg0AIABEAAAAAAAAYAOiIQAgAUG4cEsEQCABQckHaiEBDAELIABEAAAAAAAAYAOiIQAgAUHwaCABQfBoShtBkg9qIQELIAAgAUH/B2qtQjSGv6ILRAEBf0F/IQMgACAAKAIEIAJqEM4BBH9BfwUgACgCACABaiIDIAJqIAMgACgCBCABaxCBAiAAIAAoAgQgAmo2AgRBAAsLHwAgACABIAAgAhDKASICIAMgBBAbIQQgACACEBMgBAtgACAEQfIAIANBxABrIANBtQFGG0H/AXEQECAEIAAgAhAZEB4gBSABIAUoAgAQ0AMiADYCACAEIAAQHiAEIAZB/wFxEBAgASAFKAIAQQEQdBogASABKALQAkEBajYC0AIL8wcCBH8BfiMAQRBrIgMkAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAAKAIQIgJBzQBqDgMEAQMACyACQewAakECSQ0BAkAgAkEraw4DAQYBAAsgAkFYRg0EIAJB/gBGDQAgAkEhRw0FC0F/IQQgABARDQwgAEEQEO4BDQxBjAEhBAJAAkAgAkEraw4DBwEJAAsgAkG0f0cEQCACQSFGDQggAkH+AEcNAUGVASEEDAkLIABBDhAOQQYhBAwICxABAAsgABARDQggAEEAEO4BDQggACADQQxqIANBCGogAyADQQRqQQBBASACELwBDQggACACQQZrQf8BcRAOIAAgAygCDCADKAIIIAMoAgAgAygCBEECQQAQ1AEMBwtBfyEEIAAQEQ0KIABBEBDuAQ0KQZcBIQQgACgCQCIBEKgBQbYBRw0FIAEoAoACIAEoApgCakG1AToAAAwFC0F/IQQCfyAAKAJAIQFBfyECAkAgABARDQAgAEEQEO4BDQACQAJAAkACQAJAAkACQAJAIAEQqAEiAkHHAGsOBAEGBgUACyACQbwBRg0DIAJBtgFGDQIgAkHBAEcNBSABKAKYAiICIAEoAoACaigAASEFIAFBfzYCmAIgASACNgKEAiAAIAAoAgAgBRBgIgZBARDTASECIAAoAgAgBhAMIAAoAgAgBRATIAJFDQEMBwsgASgCmAIhAiABQX82ApgCIAEgAjYChAILIABBmAEQDgwECyABKAKAAiABKAKYAmoiAigAASIFQQhGIAVB8QBGcg0CIAEtAG5BAXEEQCAAQenTAEEAEBVBfwwGCyACQbgBOgAADAMLIABBxNwAQQAQFUF/DAQLIABBMBAOIABBABAcIABBAxBuQQAMAwsgAEEOEA4gAEEKEA4LQQAhAgsgAgtFDQgMCQsgACgCQCIBLQBsQQJxRQRAIABB0tgAQQAQFQwGCyABKAJkRQRAIABBizdBABAVDAYLQX8hBCAAEBENCCAAQRAQ7gENCEGLASEEDAMLQX8hBCAAIAFBBHFBAnIQzwMNByAAKAIwDQMgACgCECICQX5xQZR/Rw0DIAAgA0EMaiADQQhqIAMgA0EEakEAQQEgAhC8AQ0HIAAgAkEEa0H/AXEQDiAAIAMoAgwgAygCCCADKAIAIAMoAgRBA0EAENQBIAAQEUUNAwwHC0GNASEEDAELQZYBIQQLIAAgBBAODAMLQQAhBCABQRhxRQ0DIAAoAhBBo39HDQMgAUEQcUUNASAAKAIAQcv9AEEAENMCC0F/IQQMAgtBfyEEIAAQEQ0BIABBCBDuAQ0BIABBnwEQDgtBACEECyADQRBqJAAgBAt8AQJ/IAAoAkAiAQRAIAEoArwBIQIgAEGzARAOIAAgAkH//wNxEBggASABKALMASACQQN0aigCACIANgK8AQNAAkAgAEEASARAQX8hAAwBCyABKALMASAAQQN0aiICKAIEIgBBAE4NACACKAIAIQAMAQsLIAEgADYCwAELCzYBAX8jAEHQAGsiASQAIAEgACgCACABQRBqIAAoAiAQiQE2AgAgAEHpMCABEBUgAUHQAGokAAuQJgETfyMAQTBrIgckACAAKAIAIQ8CQCAAIgIoAhBBg39HDQAgAigCKA0AIAJBABCLAUE6RiEDCwJAAkACQAJAAkAgA0UEQCACKAIQIQMMAQsgDyACKAIgEBkhCyACKAJAQbACaiEAAkADQCAAKAIAIgBFDQEgACgCBCALRw0ACyACQZTPAEEAEBUMAgsgAhARDQEgAkE6EDANASACKAIQIgNBxwBqQQNJDQAgAhA1IQNBACEAIAIoAkAgB0EQaiALIANBf0EAEKsBIAIgAUEedEEfdUEAQQMgAigCQC0AbkEBcRtxEPEBDQEgAiADECAgAigCQBCqAQwDCwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCADQdIAag4kAxEBHREREREREREFBAYHBwgREQIJEREMEAsPHBISEhEREREcAAsgA0GDf0YNDCADQTtGDQkgA0H7AEcNECACEPcCDR0MHgsgAigCQCgCIARAIAJBuDZBABAVDB0LIAIQEQ0cQQAhACACAn9BACACKAIQIgFBO0YNABpBACABQf0ARg0AGkEAIAIoAjANABogAhCZAQ0dQQELEPYCIAIQvQENHAweCyACEBENGyACKAIwBEAgAkGIEEEAEBUMHAsgAhCZAQ0bIAJBLxAOIAIQvQFFDRwMGwsgAhARDRogAhCFARogAhDWASACEIgCDRogAkHpAEF/EB0hACACIAIoAkAtAG5Bf3NBAXEiARDxAQ0aAkAgAigCEEGvf0cEQCAAIQMMAQsgAkHrAEF/EB0hAyACEBENGyACIAAQICACIAEQ8QENGwsgAiADECAMFwsgAhA1IQAgAhA1IQEgAigCQCAHQRBqIAsgASAAQQAQqwEgAhARDRkgAhDWASACIAAQICACEIgCDRkgAkHpACABEB0aIAIQuAINGSACQesAIAAQHRogAiABECAgAigCQBCqAQwaCyACEDUhACACEDUhASACEDUhAyACKAJAIAdBEGogCyABIABBABCrASACEBENGCACIAMQICACENYBIAIQuAINGCACIAAQICACQbp/EDANGCACEIgCDRggAigCEEE7RgRAIAIQEQ0ZCyACQeoAIAMQHRogAiABECAgAigCQBCqAQwZCyACEBENFyACENYBQQAhASAHQQA2AgwCQCACKAIQIgBBWEcEQCAAQShHDQEgAiAHQQxqQQAQqQEaDAELIAIoAkAtAGxBAnFFBEAgAkHwIUEAEBUMGQsgAhARDRhBASEBCyACQSgQMA0XIActAAxBAXFFBEAgASEEIwBBQGoiBSQAIAIoAgAhDSACKAJAIg4oArwBIRAgAhA1IQMgAhA1IREgAhA1IRIgAhA1IRMgAhCFARpBASEIIAIoAkAgBUEQaiALIBIgA0EBEKsBIAUgEDYCKCACQesAQX8QHSEUIAIoAkAoAoQCIQkgAiATECAgAigCECEAQVEhAUF/IQoCQAJAAkACQAJAAkACQAJAAkAgAkEEENYDDgIAAQgLIABBSUYhDCAAQVFGIgEhCCABIABBsX9GckUgAEFJR3ENASAAIQELIAIQEQ0EAkAgAigCECIAQfsARiAAQdsARnJFBEAgAEGDf0YEQCACKAIoRQ0CCyACQZLfAEEAEBUMBgtBASEIIAIgAUEAQQFBf0EAENUBQQBIDQcgBUEANgI8DAMLIAUgDSACKAIgEBkiBjYCPCACEBEEQCACKAIAIAYQEwwFCyACIAYgARC3AkUNASACKAIAIAYQEwwECwJAAkAgAigCEEEgckH7AEcNACACIAVBDGpBABCpASIBQVlHIAFBt39HcQ0AIAJBAEEAQQEgBSgCDEECcUEBENUBQQBODQEMBQsgAhC0Ag0EIAIgBUE4aiAFQTRqIAVBPGogBUEIakEAQQBBu38QvAENBCACIAUoAjggBSgCNCAFKAI8IAUoAghBBEEAENQBCyAFQQA2AjxBACEIDAILIAJBuwFBuwFBtwEgCBsgDBsQDiACIAYQHCACIA4vAbwBEBhBACEICyABIQALIAJB6wAgERAdGiACKAJAKAKEAiEKIAIgFBAgAkAgAigCECIMQT1HDQACQCACEBFFBEAgAkEAELsBRQ0BCyANIAYQEwwCCyAGRQ0AIAJBtwEQDiACIAYQHCACIA4vAbwBEBgLIA0gBhATAkACQAJAIAJBwwAQVCIBBEAgBUEBNgIsIAUgBSgCIEECajYCIEGDxgAhBiAMQT1GDQEMAwsgAigCEEG3f0cNASAEBEAgAkGS/QBBABAVDAQLIAxBPUcNAkG/OiEGIABBsX9HDQAgDi0AbkEBcUUgCEF/c3ENAgsgBSAGNgIAIAJBuiwgBRAVDAILIAJB0DhBABAVDAELIAIQEQ0AAkAgAQRAIAIQYkUNAQwCCyACEJkBDQELIAIgAigCQCgCvAEgEBC2AiACQf4AQf0AIAQbQfwAIAEbEA4gAkHrACADEB0aIAJBKRAwRQ0BC0F/IQoMAQsgAigCQCIAQYACaiIIIAAoAoQCIg0gCiAJayIGahDOARogCCAAKAKAAiAJaiAGEIoBGiAAKAKAAiAJakGxASAGEEsaIAIoAkAiBiAAKAKEAkEFazYCmAIgAyAGKAKsAiIAIAAgA0gbIQ4gDSAJayENIAMhAANAIAAgDkcEQCAGKAKkAiAAQRRsaiIMKAIEIgggCUggCCAKTnJFBEAgDCAIIA1qNgIECyAAQQFqIQAMAQsLIAIgERAgQX8hCiACELgCDQAgAiACKAJAKAK8ASAQELYCIAIgAxAgAn8gAQRAIAQEQCACQRQQDiACQQ4QDiACQSQQDiACQQAQGCACQYsBEA4gAkGCARAOQYMBDAILIAJBgAEQDiACQQAQbkGDAQwBCyACQf8AEA5BDgshACACQekAIBMQHRogAkEOEA4gAiASECAgAiAAEA4gAigCQBCqASACEO8BQQAhCgsgBUFAayQAIApFDRkMGAsgAigCQCgCvAEhBiACEIUBGiACKAIQIgBBO0YNE0FRIQMCQCACQQQQ1gMOAgASGAsgAEGxf0YgAEFRRnINECAAIgNBSUYNESACQQAQ9QQNFyACQQ4QDgwSCyACEBENFiADQcQAaiEBQQAhAAJAIAIoAjANACACKAIQQYN/Rw0AIAIoAigNACACKAIgIQALAn8gAigCQCIEQbACaiEDIAQoArwBIQUCQANAIAMoAgAiAwRAIAIgBSADKAIYELYCIAMoAhghBQJAIAEEQCADKAIMIgRBf0YNASAABEAgAygCBCAARw0CCwwECyADKAIIIgRBf0YNACAABEAgAygCBCAARw0BCwwDCyADKAIcBH8gAkGDARAOQQMFQQALIQQDQCADKAIQIARKBEAgAkEOEA4gBEEBaiEEDAELCyADKAIUQX9GDQEgAkEGEA4gAkHtACADKAIUEB0aIAJBDhAODAELCwJAIABFBEAgAQRAIAJB+jNBABAVDAILIAJByMIAQQAQFQwBCyACQYnaAEEAEBULQX8MAQsgAkHrACAEEB0aQQALDRYgAARAIAIQEQ0XCyACEL0BRQ0XDBYLIAIQEQ0VIAIQ1gEgAhCIAg0VIAIQhQEaIAIQNSEEQX8hASACKAJAIAdBEGogCyAEQX9BARCrASACQfsAEDANFUF/IQMCQANAAkACQAJAIAIoAhAiAEHBAGoOAgABAgsgAUEASAR/QX8FIAJB6wBBfxAdCyEAIAIgARAgA0AgAhARDRogAkEREA4gAhCZAQ0aIAJBOhAwDRogAkGrARAOIAIoAhBBv39GBEAgAkHqACAAEB0hAAwBCwsgAkHpAEF/EB0hASACIAAQIAwCCyACEBENGCACQToQMA0YIANBAE4EQCACQZkZQQAQFQwZCyABQQBIBEAgAkHrAEF/EB0hAQsgAkG0ARAOIAJBABA6IAIoAkAoAoQCQQRrIQMMAQsCQAJAIABB/QBHBEAgAUEATg0BIAJB9xhBABAVDBoLIAJB/QAQMA0ZIANBAEgNASACKAJAKAKAAiADaiABEF0gAigCQCgCpAIgAUEUbGogA0EEajYCBAwDCyACQQcQ8QFFDQEMGAsLIAIgARAgCyACIAQQICACQQ4QDiACKAJAEKoBDBILIAIQ1gEgAhARDRQgAhA1IQEgAhA1IQAgAhA1IQMgAhA1IQQgAkHsACABEB0aIAIoAkAgB0EQakEAQX9Bf0EBEKsBIAcgAzYCJCACEPcCDRQgAigCQBCqASACEPUCBEAgAkEOEA4gAkEGEA4gAkHtACADEB0aIAJBDhAOIAJB6wAgBBAdGgsCQAJAAkAgAigCEEE9ag4CAA8BCyACEBENFiACEIUBGiACIAEQICACKAIQQfsARgRAIAJBDhAODA4LIAJBKBAwDRYgAigCECIBQfsARiABQdsARnINAQJAIAFBg39GBEAgAigCKEUNAQsgAkGn3gBBABAVDBcLIA8gAigCIBAZIQECQCACEBFFBEAgAiABQUMQtwJBAE4NAQsgDyABEBMMFwsgAkG3ARAOIAIgARA6IAIgAigCQC8BvAEQGAwMCyACQbwMQQAQFQwVCyACQVFBAEEBQX9BARDVAUEATg0KDBQLIAIQEUUNFAwTCyACKAJALQBuQQFxBEAgAkGGwQBBABAVDBMLIAIQEQ0SIAIQiAINEiACEIUBGiACIAIoAkBB1ABBABCsASIAQQBIDRIgAkHvABAOIAJB2QAQDiACIABB//8DcRAYIAIQ1gEgAhC4Ag0SDA8LIAFBAXFFDQEgAUEEcQ0GIAJBABCLAUEqRg0BDAYLIAIoAigEQCACEPABDBELQVEhAwJAIAIgARDWAw4CAA8RCyACQYUBEFRFDQMgAkEBEIsBQUVHDQMgAUEEcQ0FCyACQbIRQQAQFQwPCyABQQRxRQRAIAJB9hBBABAVDA8LQX8hAUEAIQAgAkEAQQAQ+gJFDRAMEQsgAhARDQ0gAhC9AUUNDgwNCyACEJkBDQwCQCACKAJAKAKkAUEATgRAIAJB2QAQDiACIAIoAkAvAaQBEBgMAQsgAkEOEA4LIAIQvQFFDQ0MDAsgAigCICEBIwBB0ABrIgAkACAAIAIoAgAgAEEQaiABEIkBNgIAIAJBvSggABAVIABB0ABqJAAMCwtBACEAIAJBAUEAIAIoAhggAigCFBDYAQ0KDAwLIAJBKRAwDQkLIAJB7AAgABAdGiACEIUBGiACKAJAIAdBEGpBAEF/QX9BARCrASAHIAM2AiQgAhD3Ag0IIAIoAkAQqgEgAhDvASACEO8BIAIQ9QIEQCACQQ4QDiACQQYQDiACQe0AIAMQHRogAkEOEA4gAkHrACAEEB0aCyAAIQELIAIgARAgIAJB7QAgAxAdGiACQS8QDiACIAMQICACKAIQQURGBEAgAhARDQhBACEAIAIoAkAgB0EQakEAQX9Bf0ECEKsBIAIoAkAiASgCpAFBAE4EQCACKAIAIAFB0QAQWCIAQQBIDQkgAkHYABAOIAIgAigCQC8BpAEQGCACQdkAEA4gAiAAQf//A3EQGCACENYBCyACEPcCDQggAigCQCIBKAKkAUEATgR/IAJB2AAQDiACIABB//8DcRAYIAJB2QAQDiACIAIoAkAvAaQBEBggAigCQAUgAQsQqgELIAJB7gAQDiACIAQQIAwICyAAIQMLIAIQEQ0FIAJBACADQQAQ2AMNBQsgAiACKAJAKAK8ASAGELYCCyACQTsQMA0DIAIQNSEEIAIQNSEAIAIQNSEDIAIQNSEFIAIoAkAgB0EQaiALIAUgAEEAEKsBIAMhASACKAIQQTtHBEAgAiAEECAgAhCZAQ0EIAJB6QAgBRAdGiAEIQELIAJBOxAwDQMCQCACKAIQQSlGBEAgByABNgIcQQAhBCABIQAMAQsgAkHrACADEB0aIAIoAkAoAoQCIQQgAiAAECAgAhCZAQ0EIAJBDhAOIAEgA0YNACACQesAIAEQHRoLIAJBKRAwDQMgAigCQCgChAIhCCACIAMQICACELgCDQMgAiACKAJAKAK8ASAGELYCAkAgASADRiAAIAFGckUEQCACKAJAIgFBgAJqIgYgASgChAIiCSAIIARrIgNqEM4BGiAGIAEoAoACIARqIAMQigEaIAEoAoACIARqQbEBIAMQSxogAigCQCIDIAEoAoQCQQVrNgKYAiAAIAMoAqwCIgEgACABShshBiAJIARrIQkDQCAAIAZGDQIgAygCpAIgAEEUbGoiCigCBCIBIARIIAEgCE5yRQRAIAogASAJajYCBAsgAEEBaiEADAALAAsgAkHrACAAEB0aCyACIAUQICACKAJAEKoBCyACEO8BDAMLIAFBBHENACACQfERQQAQFQwBCyACEBENAEEAIQAgAkEBIANBABDYAw0AIAIQvQFFDQILQX8hAAwBC0EAIQALIA8gCxATIAAhAQsgB0EwaiQAIAELCAAgAEHPAUgLmAEBAX4CQAJAAkAgARAiRQ0AIAAgAUE8IAFBABAUIgEQDQ0CAkAgARASDQAgARAiRQRAIAAgARAMDAILIAAgAUHMASABQQAQFCEDIAAgARAMAkAgAxANDQAgAxASDQEgAxAoDQEgAxC1AQ0AIAAgAxAMIABB3ylBABAWDAMLIAMPCyACEA8PCyAAECkLQoCAgIDgACEBCyABCxIAIAEQ8gFFBEAgACABEIQFCwsNACAAQRpBJEEZEOsFC60CAQN+AkACQCACBEAgACABQc4BIAFBABAUIgMQDQ0CIAMQEkUEQCADEChFDQILIAAgAUHDASABQQAQFCIDEA0NAiAAIAEgAxDoAyEBIAAgAxAMIAEQDQRAIAEPCwJ+QoCAgIDgACEDIAAgAUHqACABQQAQFCIEEA1FBEAgAEEwEKQBIgMQDQRAIAAgBBAMIAMMAgsgAEEQEGwiAkUEQCAAIAMQDCAAIAQQDEKAgICA4AAMAgsgARAPIQUgAiAENwMIIAIgBTcDACADIAIQjQELIAMLIQMgACABEAwgAw8LIAAgAUHDASABQQAQFCIDEA0NAQsgACADEDtFBEAgACADEAwgAEHj0QBBABAWQoCAgIDgAA8LIAAgASADEOgDIQEgACADEAwgASEDCyADCykBAX8gAEKAgICAcINCgICAgJB/UQR/IACnKAIEQf////8HcQVBAQtFCy0BAX9BASEBAkACQAJAIABBDWsOBAIBAQIACyAAQS1GDQELIABBMUYhAQsgAQsKACAAIAEQDxAtC2kBAX8CQAJAIAFFDQAgASgCACICQQBMDQEgASACQQFrIgI2AgAgAg0AAkAgAS0ABUEBcQRAIAAgASkDGBAnIAEQnwIMAQsgAUEIahBGCyAAIAEQIQsPC0Go8wBBvuMAQfQoQcTGABAAAAscACAAKAIQKAKMASIARQRAQQAPCyAAKAIoQQFxC5sCAgN/An4gAUKAgICAcFoEQCABpyICLwEGQSlGBEAjAEEQayIDJABCgICAgOAAIQUCQCAAIANBCGogAUHfABCHASICRQ0AIAMpAwgiARASBEAgACACKQMAEPwBIQUMAQsCQCAAIAEgAikDCEEBIAIQNiIBEA0NAAJAAkACQCABQiCIp0EBag4EAAEBAAELIAAgAikDABCiASIEQQBIDQEgBA0CIAAgAikDABD8ASIGEA0NASAAIAYQDCAGpyABp0YNAgsgACABEAwgAEHpywBBABAWDAILIAAgARAMDAELIAEhBQsgA0EQaiQAIAUPCyACKAIQKAIsIgBFBEBCgICAgCAPCyAArUKAgICAcIQQDw8LIAAgARCdBBAPCxsAIAAoAhAgASACEOEFIgFFBEAgABDJAQsgAQvyAgIEfwF+IwBBIGsiBCQAIAEgAmohBSABIQMDQAJAIAMgBU8NACADLAAAQQBIDQAgA0EBaiEDDAELCwJ+AkAgAyABayIGQYCAgIAETwRAIABBmsMAQQAQUAwBCyADIAVGBEAgACABIAIQ2AIMAgsgACAEIAIQQkUEQCAEIAEgBhCdAhoDQCADIAVJBEAgAywAACIAQQBOBEAgBCAAQf8BcRA+GiADQQFqIQMMAgUCQCADIAUgA2sgBEEcahBhIgFB//8DTQRAIAQoAhwhAwwBCyABQf//wwBNBEAgBCgCHCEDIAQgAUGAgARrQQp2QYCwA2oQlgEaIAFB/wdxQYC4A3IhAQwBCwNAQf3/AyEBIAMgBU8NASADLAAAQb9/TARAIANBAWohAwwBCwsDQCADQQFqIgMgBU8NASADLAAAQUBIDQALCyAEIAEQlgEaDAILAAsLIAQQOQwCCyAEEEQLQoCAgIDgAAshByAEQSBqJAAgBwvbAQIBfwJ+QQEhBAJAIABCAFIgAUL///////////8AgyIFQoCAgICAgMD//wBWIAVCgICAgICAwP//AFEbDQAgAkIAUiADQv///////////wCDIgZCgICAgICAwP//AFYgBkKAgICAgIDA//8AURsNACAAIAKEIAUgBoSEUARAQQAPCyABIAODQgBZBEBBfyEEIAAgAlQgASADUyABIANRGw0BIAAgAoUgASADhYRCAFIPC0F/IQQgACACViABIANVIAEgA1EbDQAgACAChSABIAOFhEIAUiEECyAEC1IBAn9BpLMEKAIAIgEgAEEDakF8cSICaiEAAkAgAkEAIAAgAU0bDQAgAD8AQRB0SwRAIAAQCUUNAQtBpLMEIAA2AgAgAQ8LQcSzBEEwNgIAQX8LRwAgACABSQRAIAAgASACECUaDwsgAgRAIAAgAmohACABIAJqIQEDQCAAQQFrIgAgAUEBayIBLQAAOgAAIAJBAWsiAg0ACwsLIgAgACABQTsgAhAPIgIgAxAbGiAAIAJBPCABEA8gBBAbGgvhBAEGfyAAKAIAIgRBAWohAkEIIQMCQAJAAkAgBC0AACIGQTBrIgdBCE8EQEF+IQUCQAJAAkACQAJAAkAgBkHuAGsOCwEJCQkCCQMFBAkFAAsCQCAGQeIAaw4FCAkJCQAJC0EMIQMMBwtBCiEDDAYLQQ0hAwwFC0EJIQMMBAtBCyEDDAMLAkAgAUUNACACLQAAQfsARw0AIARBAmohAiAELQACIQRBACEDA0AgAiEBQX8hBSAEEOsCIgJBAEgNBSACIANBBHRyIgNB///DAEsNBSABQQFqIgItAAAiBEH9AEcNAAsgAUECaiECDAMLIARBAkEEIAZB+ABGGyIHakEBaiEEQQAhA0EAIQUDQCAFIAdHBEAgAi0AABDrAiIGQQBIBEBBfw8FIAVBAWohBSACQQFqIQIgBiADQQR0ciEDDAILAAsLIAFBAkcgA0GAeHFBgLADR3INASAELQAAQdwARw0BIAQtAAFB9QBHDQFBACECQQAhBQNAAkAgAkEERg0AIAIgBGotAAIQ6wIiAUEASA0AIAJBAWohAiABIAVBBHRyIQUMAQsLIAJBBEcgBUGAuANJciAFQf+/A0tyDQEgA0EKdEGA+D9xIAVB/wdxckGAgARqIQMgBEEGaiECDAILIAFBAkYEQEF/IQUgBw0DIAItAAAQRQ0DQQAhAwwCCyACLQAAQTBrIgFBB0sEQCAHIQMMAgsgBEECaiECIAEgB0EDdHIiA0EfSw0BIAQtAAJBMGsiAUEHSw0BIARBA2ohAiABIANBA3RyIQMMAQsgBCECCyAAIAI2AgAgAyEFCyAFC4sBAQN/IwBBkAFrIgMkACADIAI2AowBAn8gA0GAASABIAIQ2QIiBEH/AE0EQCAAIAMgBBCKAQwBC0F/IAAgBCAAKAIEakEBahDOAQ0AGiADIAI2AowBIAAoAgQiBSAAKAIAaiAAKAIIIAVrIAEgAhDZAhogACAAKAIEIARqNgIEQQALGiADQZABaiQAC5wBAQR/IwBBEGsiAiQAIAJBJToACkEBIQMgAUGAAk4EQCACQfUAOgALIAIgAUEIdkEPcUHL7ABqLQAAOgANIAIgAUEMdkEPcUHL7ABqLQAAOgAMQQQhAwsgAkEKaiIEIANqIgUgAUEPcUHL7ABqLQAAOgABIAUgAUEEdkEPcUHL7ABqLQAAOgAAIAAgBCADQQJyEJ0CGiACQRBqJAALtgEBAn8CQCACIAEoAgQiCkYEQCADIQsMAQsgACAKIAIgAyAEIAUgBiAHIAggCRCGAiIFQQBODQBBfw8LQQAhAiABKALAAiIDQQAgA0EAShshAwJAA0AgAiADRwRAAkAgBSABKALIAiACQQN0aiIKLwECRw0AIAotAAAiCkEBdkEBcSAERw0AIAsgCkEBcUYNAwsgAkEBaiECDAELCyAAIAEgCyAEIAUgBiAHIAggCRDLAyECCyACC0cBAn8gACgCfCECAkADQCACQQBKBEAgACgCdCACQQFrIgJBBHRqIgMoAgAgAUcNASADKAIEDQEMAgsLIAAgARDyBCECCyACCykBAX9BfyEBAkAgAEEoEDANACAAEJkBDQBBf0EAIABBKRAwGyEBCyABC9EBAQJ/IAAoAgAhBSMAQdAAayIGJAACQCABIAMQvwUEQAJAIAAEQCAGIAUgBkEQaiADEIkBNgIAIABBgPsAIAYQFQwBCyAFIANBgPsAEJUDC0EAIQAMAQtBACEAIAUgAUEcakEUIAFBJGogASgCIEEBahCAAQ0AIAEgASgCICIAQQFqNgIgIAEoAhwgAEEUbGoiAEIANwIAIABBADYCECAAQgA3AgggACAFIAIQGTYCDCAFIAMQGSEBIAAgBDYCCCAAIAE2AhALIAZB0ABqJAAgAAvcFQEKfyMAQRBrIg4kACAAKAJAIQcgACgCACELAkACQAJAAkAgAUECTQRAAkAgAg0AQQAhAiAAQYUBEFRFDQAgAEEBEIsBQQpGDQBBfyEIIAAQEQ0FQQIhAgtBfyEIIAAQEQ0EIAAoAhAiCkEqRgRAIAAQEQ0FIAAoAhAhCiACQQFyIQILAkACQAJAAkAgCkEpag4CAQIACyAKQYN/Rw0EAkAgACgCKA0AIAJBAXFFIAFBAkdyRSAAKAIgIgpBLUZxDQAgAkECcUUgAUECR3IgCkEuR3INAwsgABDwAQwHCyABQQJHDQMgBy0AbkEBcUUNAQwDCyABQQJHDQIgACgCRA0CCyALIAAoAiAQGSEKIAAQEUUNAgwDCyABQQNGDQEgC0EAEBkaDAELQQAhCiABQQJGIAVBAkZyDQAgAEH73gBBABAVDAILAkACQAJAIAcoAiAiCEUgAUEBS3INACAHKAIkQQFHDQAgByAKELUCIglFDQAgCSgCCCAHKAK8AUcNACAAQfvVAEEAEBUMAQtBfyEPAkAgAUEBRwRADAELAkAgAg0AIActAG5BAXENACAHIAogBygCwAFBABDVA0EATg0AIAcgChCHAkGAgICAenFBgICAgAJGDQAgCkHNAEYEQCAHKAJIDQELQQEhDQsCQCAIRQ0AIAcoAiRBAUsNACAHKAK8ASIIIAcoAvABRw0AIAcgChC1AiIJRQ0BIAkoAgggCEcNASAAQaAwQQAQFQwCC0F/IQggACAHIApBBEEDIAIbEKwBIg9BAEgNAwsgCyAHQQAgAUEBSyAAKAIMIAQQ9wMiBw0BCyALIAoQE0F/IQgMAgsgBgRAIAYgBzYCAAsgACAHNgJAIAcgCjYCcCAHIAFBCEYiBDYCYCAHIAFBA0ciCDYCTCAHIAg2AkggByACRSABQQNJcTYCNCAHIAFBBGtBBUkiCTYCMEEBIQxBASEQIAhFBEAgBygCBCIIKAJcIRAgCCgCWCEJIAgoAlAhDCAIKAJUIQQLIAcgEDYCXCAHIAk2AlggByAENgJUIAcgDDYCUCAHIAJB/wFxIAFBCHRyOwFsIAFBB2tBAU0EQCAAQSsQDgsgAUEHRgRAIAAQ9AQLIAdCATcCOAJAAkACQAJAIAFBA0cgACgCECIEQYN/R3JFBEAgACgCKA0DIAsgByAAKAIgENQDQQBIDQQgB0EBNgKMAQwBCwJAIARBKEYEQCAAIA5BDGpBABCpARogDi0ADEEEcQRAIAdBATYCPAsgABARRQ0BDAULIABBKBAwDQQLIAcoAjwEQEF/IQggB0F/NgK8ASAAEIUBQQBIDQYLQQAhCQJAA0AgACgCECIIQSlGDQEgCEGlf0ciDEUEQCAHQQA2AjggABARDQYgACgCECEICwJAAkACQAJAIAhBg39HBEAgCEH7AEcgCEHbAEdxDQQgB0EANgI4AkAgDEUEQCAAQQ0QDiAHKAKIASEIDAELIAsgB0EAENQDIQggAEHbABAOCyAAIAhB//8DcRAYIABBUUGxfyAHKAI8G0EBQQFBf0EBENUBIgRBAEgNCiAEIAlyIQRBASEJIARFBEAgByAHKAKMAUEBajYCjAFBACEJCyAMRQ0BDAMLIAAoAigNCCAAKAIgIgRBLUYEQCAHLQBsQQFGDQkLIAcoAjwEQCAAIAcgBEEBEKwBQQBIDQoLIAsgByAEENQDIghBAEgNCSAAEBENCSAMDQEgAEENEA4gACAIQf//A3EiCBAYIAcoAjwEQCAAQREQDiAAQbsBEA4gACAEEBwgACAHLwG8ARAYCyAAQdwAEA4gACAIEBggB0EANgI4CyAAKAIQQSlGDQQgAEEpEDAaDAgLAkAgACgCEEE9RgRAIAdBADYCOCAAEBENCSAAEDUhCSAAQdsAEA4gACAIQf//A3EiCBAYIABBERAOIABBBhAOIABBqwEQDiAAQekAIAkQHRogAEEOEA4gABBiDQkgACAEEK0BIABBERAOIABB3AAQDiAAIAgQGCAAIAkQIEEBIQkMAQsgCUUEQCAHIAcoAowBQQFqNgKMAQsgBygCPEUNASAAQdsAEA4gACAIQf//A3EQGAsgAEG7ARAOIAAgBBAcIAAgBy8BvAEQGAsgACgCEEEpRg0CIABBLBAwRQ0BDAYLCyAAQZYuQQAQFQwECwJAAkAgAUEEaw4CAQACCyAHKAKIAUEBRg0BDAILIAcoAogBDQELIAcoAjwEQCAHKALMASAHKAK8AUEDdGpBBGohCANAAkAgCCgCACIEQQBIDQAgBygCdCIIIARBBHQiBGoiCSgCBCAHKAK8AUcNACAHIAkoAgAiCRCHAkEASARAIAsgByAJEFhBAEgNBiAHKAJ0IQggAEG2ARAOIAAgBCAIaiIJKAIAEBwgACAHLwG8ARAYIABBtwEQDiAAIAkoAgAQHCAAQQAQGAsgBCAIakEIaiEIDAELCyAAQbMBEA4gACAHLwG8ARAYIAdBADYCvAEgByAHKALMASgCBDYCwAELIAAQEQ0CIAJBfXFBAUYEQCAAQYcBEA4LIAdBATYCZCAAEIUBGiAHIAcoArwBNgLwAQJAAkAgACgCEEGkf0cNACAAEBENBCAAKAIQQfsARg0AIAAgByAKEPMEDQQgABBiDQQgAEEuQSggAhsQDiAHLQBuQQJxDQEgByAAKAI0IANrIgI2ApADIAcgCyADIAIQowMiAjYCjAMgAg0BDAQLIABB+wAQMA0DIAAQ+QQNAyAAIAcgChDzBA0DA0AgACgCEEH9AEcEQCAAEPgERQ0BDAULCyAHLQBuQQJxRQRAIAcgACgCOCADayICNgKQAyAHIAsgAyACEKMDIgI2AowDIAJFDQQLIAAQEQ0DIAAQ9QJFDQAgAEEAEPYCCyAAIAcoAgQ2AkAgBygCcCECIAcgAEKAgICAIBDTAyIDNgIIIAFBAk8EQEEAIQggAUEJa0F9Sw0FIABBAxAOIAAgAxA6IAINBSAAQc0AEA4gAEEAEDoMBQsgAUEBRgRAIABBAxAOIAAgAxA6IA0EQAJAIAAoAkAiASgCKARAIAsgASACEPQCIgFFDQYgAUEANgIIIAEgAS0ABEH+AXEgACgCQC0AbkEBcXI6AAQMAQsgASACEIcCQQBODQAgCyABIAIQWEEASA0FCyAAQREQDiAAQbcBEA4gACACEBwgAEEAEBgLQQAhCCAPQQBOBEAgACgCQCgCdCAPQQR0aiIBIAEoAgxB/4CAgHhxIANBB3RBgP///wdxcjYCDCAAQQ4QDgwGCyAAQbsBEA4gACACEBwgACAAKAJALwG8ARAYDAULAkACQCAAKAJAIgEoAihFBEAgACABIAJBBhCsASIBQQBIDQUgACgCQCEAIAFBgICAgAJxBEAgACgCgAEgAUEEdGoiACAAKAIMQf+AgIB4cSADQQd0QYD///8HcXI2AgwMAgsgACgCdCABQQR0aiIAIAAoAgxB/4CAgHhxIANBB3RBgP///wdxcjYCDAwBCyALIAEgAkH8ACACGyIBEPQCIgJFDQQgAiADNgIAIAUNAQtBACEIDAULQQAhCCAAIAAoAkAoApQDIAEgAUEWIAVBAUYbQQAQiQINBAwCCyAAQcAtQQAQFQwBCyAAEPABCyAAIAcoAgQ2AkAgCyAHEI0DQX8hCCAGRQ0BIAZBADYCAAwBCyALIAoQEwsgDkEQaiQAIAgLegEBfyAAIAZBDBBTIgYQDUUEQCAGpyIHIAAQoAIiADYCICAHIAU7ASogByAEOgApIAcgAzoAKCAHIAE2AiQgByAHLQAFQe8BcSAEQQJrQQRJQQR0cjoABSAAIAYgACACQdyDASACGxDKASIBIAMQqQMgACABEBMLIAYL0AECAX4BfyMAQRBrIgIkAAJAIAEQIkUEQCAAEClCgICAgOAAIQUMAQsCQCAEDQAgAykDACIFQSoQQEUNACAAIAVBPCAFQQAQFCIFEA0NASAAIAUgARBaIQYgACAFEAwgBkUNACADKQMAEA8hBQwBCyAAIAIgARDDAiIBEA1FBEAgACACIARBA3RqKQMAQoCAgIAwQQEgAxAkIQUgACACKQMAEAwgACACKQMIEAwgBRANBEAgACABEAwMAgsgACAFEAwLIAEhBQsgAkEQaiQAIAULDAAgACABEAwgARANC0QBAn8CQCAAQoCAgIBwVA0AIACnIgMvAQZBAkcNACADLQAFQQhxRQ0AIAIgAygCKDYCACABIAMoAiQ2AgBBASEECyAEC3gBAX8CQAJAAkACQAJAIAEoAgAiAkH/AGoOBAAAAwECCyAAKAIAIAEpAxAQDA8LIAAoAgAgASkDEBAMIAAoAgAgASkDGBAMDwsgAkGpf0cNAQsgACgCACABKAIQEBMPCyACQdUAakEtTQRAIAAoAgAgASgCEBATCwsNACAAIAEgAkEAEKEECw4AIAEgACgCEEErEOcCC9MBAwF/AX4BfCMAQRBrIgMkAAJ/IAAgA0EIaiABQQhrIgEpAwAQWwRAQoCAgIAwIQRBfwwBCwJ8AkACQAJAAkACQCACQYwBaw4EAgQBAAMLIAMrAwhEAAAAAAAA8D+gDAQLIAMrAwhEAAAAAAAA8L+gDAMLIAMrAwiaDAILEAEACyADKwMICyIFvQJ/IAWZRAAAAAAAAOBBYwRAIAWqDAELQYCAgIB4CyIAt71RBEAgAK0hBEEADAELIAUQFyEEQQALIQAgASAENwMAIANBEGokACAACw0AIAAgASACEA8QxgELSQECfyACQv////8HWARAIAAgASACpxCVAUGAgAEQ3gEPCyAAIAIQnQMiA0UEQEF/DwsgACABIANBgIABEN4BIQQgACADEBMgBAtKAQF/AkAgACABIAAoAgRB/////wdxIgIgASgCBEH/////B3EiARC0ARDkBSIADQBBACEAIAEgAkYNAEF/QQEgASACSxshAAsgAAsgACAAIAEgAkEATgR+IAKtBSACuBAXCyADQYCAARDhAQvNCgIHfwF+IwBBIGsiCSQAAkACQAJAAkACQAJAAn8CQAJAAkACQAJAIAFCIIinQQFqDgUDAgIAAQILIAAgAxAMIAAgAkGmPRDIAUF/IQUMCgsgACADEAwgACACQZrgABDIAUF/IQUMCQsgACABEJ0EpyEGDAELIAGnIQYCQAJAA0AgBigCECIHIAcoAhggAnFBf3NBAnRqKAIAIQUgBxAqIQgDQCAGIQcgBUUNAyACIAggBUEBa0EDdCIHaiIFKAIERwRAIAUoAgBB////H3EhBQwBCwsgBSgCACIIQRp2IQogBigCFCAHaiEHIAhBgICAwH5xQYCAgMAARgRAIAAgByADEB8MBgsCQCAIQYCAgIACcQRAIAYvAQZBAkcNASACQTBHDQMgACAGIAMgBBDVBSEFDAwLIApBMHEiCEEwRwRAIAhBIEcEQCAIQRBHDQkgACAHKAIEIAEgAyAEEKIDIQUMDQsgBi8BBkELRg0IIAAgBygCACgCECADEB8MBwsgACAGIAIgByAFENECRQ0BDAoLC0G/5wBBvuMAQY/CAEHBPxAAAAtBsMEAQb7jAEGQwgBBwT8QAAALQQAMAQtBAQshBQNAAkACQCAFRQRAAkAgBi0ABSIFQQRxRQ0AAkAgBUEIcQRAIAIQXgRAIAIQfCIFIAYoAihPDQIgBiAHRw0FIAAgASAFrSADIAQQ4QEhBQwNCyAGLwEGQRVrQf//A3FBCEsNAiAAIAIQpQMiCEUNAkF/IQUgCEEATg0JDAoLIAAoAhAoAkQgBi8BBkEYbGooAhQiBUUNASAFKAIYBEAgACAGrUKAgICAcIQQDyIMIAIgAyABIAQgBSgCGBEpACEFIAAgDBAMDAoLIAUoAgBFDQEgACAJIAatQoCAgIBwhBAPIgwgAiAFKAIAERgAIQUgACAMEAwgBUEASA0JIAVFDQEgCS0AAEEQcQRAIABBACAJKQMYIgynIAwQEhsgASADIAQQogMhBSAAIAkpAxAQDCAAIAkpAxgQDAwMCyAAIAkpAwgQDCAJLQAAQQJxRQ0HIAYgB0cNAyAAIAEgAiADQoCAgIAwQoCAgIAwQYDAABB4IQUMCQsgBi8BBkEVa0H//wNxQQlJDQcLIAYoAhAoAiwhBkEBIQUMAwsgBkUNAANAIAYoAhAiCCAIKAIYIAJxQX9zQQJ0aigCACEFIAgQKiEKA0AgBUUNAyACIAogBUEBa0EDdCIFaiIIKAIERwRAIAgoAgBB////H3EhBQwBCwsgBigCFCAFaiEKAkAgCCgCACIFQRp2QTBxIgtBMEcEQCALQRBHDQEgACAKKAIEIAEgAyAEEKIDIQUMCwtBfyEFIAAgBiACIAogCBDRAkUNAQwKCwsgBUGAgIDAAHENAQwECyAEQYCABHEEQCAAIAMQDCAAIAIQ0AJBfyEFDAgLIAdFBEAgACADEAwgACAEQfQcEHkhBQwICyAHLQAFIgZBAXFFBEAgACADEAwgACAEQdzQABB5IQUMCAsgBkEEcQRAAkAgBkEIcUUgBy8BBkECR3INACACEF5FDQAgAhB8IAcoAihHDQAgACAHIAMgBBCXBCEFDAkLIAAgByACIANCgICAgDBCgICAgDAgBEGHzgByEJYEIQUMBgsgACAHIAJBBxCDASICRQ0GIAIgAzcDAAwCC0EAIQUMAAsAC0EBIQUMBAsgACADEAwgACAEIAIQ4AEhBQwDCyAAIAAgAxCgASIBEAxBfyEFIAEQDQ0CIAAgBEHTDhB5IQUMAgsgACADEAwMAQsgACADEAxBfyEFCyAJQSBqJAAgBQsNACAAKAIQIAGnENYCCxUBAX4gACABEPwBIQIgACABEAwgAgshACAAKAIQIAEgAhDnASIBIAJFcgR/IAEFIAAQyQFBAAsL8QMCA38BfgJAAkAgAwRAIAFCgICAgGCDQoCAgIAgUg0BDAILIAFCgICAgHBUDQELQQEhBAJAAkAgAkIgiKdBAWoOBAACAgECCyACpyEFCwJAAkAgAUL/////b1hBACADGw0AIAGnIgYvAQZBKUYEQCMAQSBrIgQkAAJAAkAgACAEQRhqIAFB4AAQhwEiBUUNACAFKQMAIQEgBCkDGCIHEBIEQCAAIAEgAiADEJsCIQMMAgsgBCACNwMIIAQgATcDACAAIAcgBSkDCEECIAQQNiIBEA0NACAAIAEQLUUEQCADRQRAQQAhAwwDCyAAQYfMAEEAEBYMAQsgACAFKQMAEKIBIgZBAEgNAEEBIQMgBg0BIAAgBSkDABD8ASIBEA0NACAAIAEQDCACpyABp0YNASAAQenLAEEAEBYLQX8hAwsgBEEgaiQAIAMPCyAGKAIQKAIsIAVGDQAgBi0ABUEBcUUEQCADRQ0CIABB3NAAQQAQFkF/DwsgBQRAIAUhBANAIAQgBkYEQCADRQ0EIABBqTpBABAWQX8PCyAEKAIQKAIsIgQNAAsgAhAPGgtBfyEEIAAgBkEAEOQBDQAgBigCECIDKAIsIgQEQCAAIAStQoCAgIBwhBAMCyADIAU2AixBASEECyAEDwtBAA8LIAAQKUF/CxkAIAAgARDoASIABEAgAEEAIAEQSxoLIAALkwEBAn8CfyAAKAIIIAJqIgQgACgCDEoEQEF/IAAgBEEAENUCDQEaCwJAIAAoAhAEQCACQQAgAkEAShshBANAIAMgBEYNAiAAKAIEIAAoAgggA2pBAXRqIAEgA2otAAA7ARAgA0EBaiEDDAALAAsgACgCBCAAKAIIakEQaiABIAIQJRoLIAAgACgCCCACajYCCEEACwuiAQECfyABIAEoAgAiAkEBazYCACACQQFMBEACQCABKAIARQRAIAEtABAEQCAAIAEQkQQLIAEoAiwiAgRAIAAgAq1CgICAgHCEECcLQQAhAiABECohAwNAIAEoAiAgAksEQCAAIAMoAgQQ9AEgAkEBaiECIANBCGohAwwBCwsgARCfAiAAIAEQwQIQIQwBC0Hg9ABBvuMAQcMiQf3yABAAAAsLCwkAIABBCGoQRgsRACAAIAAoAgBBAWo2AgAgAAtQAQF+AkAgA0HAAHEEQCACIANBQGqtiCEBQgAhAgwBCyADRQ0AIAJBwAAgA2uthiABIAOtIgSIhCEBIAIgBIghAgsgACABNwMAIAAgAjcDCAtjAgF/AX4jAEEQayICJAAgAAJ+IAFFBEBCAAwBCyACIAGtQgAgAWciAUHRAGoQcyACKQMIQoCAgICAgMAAhUGegAEgAWutQjCGfCEDIAIpAwALNwMAIAAgAzcDCCACQRBqJAALiS4BC38jAEEQayILJAACQAJAAkACQAJAAkACQAJAAkACQAJAIABB9AFNBEBBmL0EKAIAIgZBECAAQQtqQXhxIABBC0kbIgdBA3YiAnYiAUEDcQRAAkAgAUF/c0EBcSACaiICQQN0IgBBwL0EaiIBIABByL0EaigCACIDKAIIIgBGBEBBmL0EIAZBfiACd3E2AgAMAQsgACABNgIMIAEgADYCCAsgAyACQQN0IgBBA3I2AgQgACADaiIAIAAoAgRBAXI2AgQgA0EIaiEADAwLIAdBoL0EKAIAIgpNDQEgAQRAAkBBAiACdCIAQQAgAGtyIAEgAnRxIgBBACAAa3FBAWsiACAAQQx2QRBxIgJ2IgFBBXZBCHEiACACciABIAB2IgFBAnZBBHEiAHIgASAAdiIBQQF2QQJxIgByIAEgAHYiAUEBdkEBcSIAciABIAB2aiICQQN0IgBBwL0EaiIBIABByL0EaigCACIEKAIIIgBGBEBBmL0EIAZBfiACd3EiBjYCAAwBCyAAIAE2AgwgASAANgIICyAEIAdBA3I2AgQgBCAHaiIBIAJBA3QiACAHayICQQFyNgIEIAAgBGogAjYCACAKBEAgCkEDdiIAQQN0QcC9BGohBUGsvQQoAgAhAwJ/IAZBASAAdCIAcUUEQEGYvQQgACAGcjYCACAFDAELIAUoAggLIQAgBSADNgIIIAAgAzYCDCADIAU2AgwgAyAANgIIC0GsvQQgATYCAEGgvQQgAjYCACAEQQhqIQAMDAtBnL0EKAIAIglFDQEgCUEAIAlrcUEBayIAIABBDHZBEHEiAnYiAUEFdkEIcSIAIAJyIAEgAHYiAUECdkEEcSIAciABIAB2IgFBAXZBAnEiAHIgASAAdiIBQQF2QQFxIgByIAEgAHZqQQJ0Qci/BGooAgAiASgCBEF4cSAHayEDIAEhAgNAAkAgAigCECIARQRAIAIoAhQiAEUNAQsgACgCBEF4cSAHayICIAMgAiADSSICGyEDIAAgASACGyEBIAAhAgwBCwsgASgCGCEIIAEgASgCDCIFRwRAIAEoAggiAEGovQQoAgBJGiAAIAU2AgwgBSAANgIIDAsLIAFBFGoiAigCACIARQRAIAEoAhAiAEUNAyABQRBqIQILA0AgAiEEIAAiBUEUaiICKAIAIgANACAFQRBqIQIgBSgCECIADQALIARBADYCAAwKC0F/IQcgAEG/f0sNACAAQQtqIgBBeHEhB0GcvQQoAgAiCUUNAEEAIAdrIQMCQAJAAkACf0EAIAdBgAJJDQAaQR8gB0H///8HSw0AGiAAQQh2IgAgAEGA/j9qQRB2QQhxIgJ0IgAgAEGA4B9qQRB2QQRxIgF0IgAgAEGAgA9qQRB2QQJxIgB0QQ92IAEgAnIgAHJrIgBBAXQgByAAQRVqdkEBcXJBHGoLIgZBAnRByL8EaigCACICRQRAQQAhAAwBC0EAIQAgB0EAQRkgBkEBdmsgBkEfRht0IQEDQAJAIAIoAgRBeHEgB2siBCADTw0AIAIhBSAEIgMNAEEAIQMgAiEADAMLIAAgAigCFCIEIAQgAiABQR12QQRxaigCECICRhsgACAEGyEAIAFBAXQhASACDQALCyAAIAVyRQRAQQAhBUECIAZ0IgBBACAAa3IgCXEiAEUNAyAAQQAgAGtxQQFrIgAgAEEMdkEQcSICdiIBQQV2QQhxIgAgAnIgASAAdiIBQQJ2QQRxIgByIAEgAHYiAUEBdkECcSIAciABIAB2IgFBAXZBAXEiAHIgASAAdmpBAnRByL8EaigCACEACyAARQ0BCwNAIAAoAgRBeHEgB2siASADSSECIAEgAyACGyEDIAAgBSACGyEFIAAoAhAiAQR/IAEFIAAoAhQLIgANAAsLIAVFDQAgA0GgvQQoAgAgB2tPDQAgBSgCGCEGIAUgBSgCDCIBRwRAIAUoAggiAEGovQQoAgBJGiAAIAE2AgwgASAANgIIDAkLIAVBFGoiAigCACIARQRAIAUoAhAiAEUNAyAFQRBqIQILA0AgAiEEIAAiAUEUaiICKAIAIgANACABQRBqIQIgASgCECIADQALIARBADYCAAwICyAHQaC9BCgCACICTQRAQay9BCgCACEDAkAgAiAHayIBQRBPBEBBoL0EIAE2AgBBrL0EIAMgB2oiADYCACAAIAFBAXI2AgQgAiADaiABNgIAIAMgB0EDcjYCBAwBC0GsvQRBADYCAEGgvQRBADYCACADIAJBA3I2AgQgAiADaiIAIAAoAgRBAXI2AgQLIANBCGohAAwKCyAHQaS9BCgCACIISQRAQaS9BCAIIAdrIgE2AgBBsL0EQbC9BCgCACICIAdqIgA2AgAgACABQQFyNgIEIAIgB0EDcjYCBCACQQhqIQAMCgtBACEAIAdBL2oiCQJ/QfDABCgCAARAQfjABCgCAAwBC0H8wARCfzcCAEH0wARCgKCAgICABDcCAEHwwAQgC0EMakFwcUHYqtWqBXM2AgBBhMEEQQA2AgBB1MAEQQA2AgBBgCALIgFqIgZBACABayIEcSICIAdNDQlB0MAEKAIAIgUEQEHIwAQoAgAiAyACaiIBIANNIAEgBUtyDQoLQdTABC0AAEEEcQ0EAkACQEGwvQQoAgAiAwRAQdjABCEAA0AgAyAAKAIAIgFPBEAgASAAKAIEaiADSw0DCyAAKAIIIgANAAsLQQAQgAIiAUF/Rg0FIAIhBkH0wAQoAgAiA0EBayIAIAFxBEAgAiABayAAIAFqQQAgA2txaiEGCyAGIAdNIAZB/v///wdLcg0FQdDABCgCACIFBEBByMAEKAIAIgMgBmoiACADTSAAIAVLcg0GCyAGEIACIgAgAUcNAQwHCyAGIAhrIARxIgZB/v///wdLDQQgBhCAAiIBIAAoAgAgACgCBGpGDQMgASEACyAAQX9GIAdBMGogBk1yRQRAQfjABCgCACIBIAkgBmtqQQAgAWtxIgFB/v///wdLBEAgACEBDAcLIAEQgAJBf0cEQCABIAZqIQYgACEBDAcLQQAgBmsQgAIaDAQLIAAiAUF/Rw0FDAMLQQAhBQwHC0EAIQEMBQsgAUF/Rw0CC0HUwARB1MAEKAIAQQRyNgIACyACQf7///8HSw0BIAIQgAIiAUF/RkEAEIACIgBBf0ZyIAAgAU1yDQEgACABayIGIAdBKGpNDQELQcjABEHIwAQoAgAgBmoiADYCAEHMwAQoAgAgAEkEQEHMwAQgADYCAAsCQAJAAkBBsL0EKAIAIgQEQEHYwAQhAANAIAEgACgCACIDIAAoAgQiAmpGDQIgACgCCCIADQALDAILQai9BCgCACIAQQAgACABTRtFBEBBqL0EIAE2AgALQQAhAEHcwAQgBjYCAEHYwAQgATYCAEG4vQRBfzYCAEG8vQRB8MAEKAIANgIAQeTABEEANgIAA0AgAEEDdCIDQci9BGogA0HAvQRqIgI2AgAgA0HMvQRqIAI2AgAgAEEBaiIAQSBHDQALQaS9BCAGQShrIgNBeCABa0EHcUEAIAFBCGpBB3EbIgBrIgI2AgBBsL0EIAAgAWoiADYCACAAIAJBAXI2AgQgASADakEoNgIEQbS9BEGAwQQoAgA2AgAMAgsgAC0ADEEIcSADIARLciABIARNcg0AIAAgAiAGajYCBEGwvQQgBEF4IARrQQdxQQAgBEEIakEHcRsiAGoiAjYCAEGkvQRBpL0EKAIAIAZqIgEgAGsiADYCACACIABBAXI2AgQgASAEakEoNgIEQbS9BEGAwQQoAgA2AgAMAQtBqL0EKAIAIAFLBEBBqL0EIAE2AgALIAEgBmohAkHYwAQhAAJAAkACQAJAAkACQANAIAIgACgCAEcEQCAAKAIIIgANAQwCCwsgAC0ADEEIcUUNAQtB2MAEIQADQCAEIAAoAgAiAk8EQCACIAAoAgRqIgUgBEsNAwsgACgCCCEADAALAAsgACABNgIAIAAgACgCBCAGajYCBCABQXggAWtBB3FBACABQQhqQQdxG2oiCSAHQQNyNgIEIAJBeCACa0EHcUEAIAJBCGpBB3EbaiIGIAcgCWoiCGshAiAEIAZGBEBBsL0EIAg2AgBBpL0EQaS9BCgCACACaiIANgIAIAggAEEBcjYCBAwDC0GsvQQoAgAgBkYEQEGsvQQgCDYCAEGgvQRBoL0EKAIAIAJqIgA2AgAgCCAAQQFyNgIEIAAgCGogADYCAAwDCyAGKAIEIgBBA3FBAUYEQCAAQXhxIQQCQCAAQf8BTQRAIAYoAggiAyAAQQN2IgBBA3RBwL0EakYaIAMgBigCDCIBRgRAQZi9BEGYvQQoAgBBfiAAd3E2AgAMAgsgAyABNgIMIAEgAzYCCAwBCyAGKAIYIQcCQCAGIAYoAgwiAUcEQCAGKAIIIgAgATYCDCABIAA2AggMAQsCQCAGQRRqIgAoAgAiAw0AIAZBEGoiACgCACIDDQBBACEBDAELA0AgACEFIAMiAUEUaiIAKAIAIgMNACABQRBqIQAgASgCECIDDQALIAVBADYCAAsgB0UNAAJAIAYoAhwiA0ECdEHIvwRqIgAoAgAgBkYEQCAAIAE2AgAgAQ0BQZy9BEGcvQQoAgBBfiADd3E2AgAMAgsgB0EQQRQgBygCECAGRhtqIAE2AgAgAUUNAQsgASAHNgIYIAYoAhAiAARAIAEgADYCECAAIAE2AhgLIAYoAhQiAEUNACABIAA2AhQgACABNgIYCyACIARqIQIgBCAGaiIGKAIEIQALIAYgAEF+cTYCBCAIIAJBAXI2AgQgAiAIaiACNgIAIAJB/wFNBEAgAkEDdiIAQQN0QcC9BGohAgJ/QZi9BCgCACIBQQEgAHQiAHFFBEBBmL0EIAAgAXI2AgAgAgwBCyACKAIICyEAIAIgCDYCCCAAIAg2AgwgCCACNgIMIAggADYCCAwDC0EfIQAgAkH///8HTQRAIAJBCHYiACAAQYD+P2pBEHZBCHEiA3QiACAAQYDgH2pBEHZBBHEiAXQiACAAQYCAD2pBEHZBAnEiAHRBD3YgASADciAAcmsiAEEBdCACIABBFWp2QQFxckEcaiEACyAIIAA2AhwgCEIANwIQIABBAnRByL8EaiEFAkBBnL0EKAIAIgNBASAAdCIBcUUEQEGcvQQgASADcjYCACAFIAg2AgAgCCAFNgIYDAELIAJBAEEZIABBAXZrIABBH0YbdCEAIAUoAgAhAQNAIAEiAygCBEF4cSACRg0DIABBHXYhASAAQQF0IQAgAyABQQRxaiIFKAIQIgENAAsgBSAINgIQIAggAzYCGAsgCCAINgIMIAggCDYCCAwCC0GkvQQgBkEoayIDQXggAWtBB3FBACABQQhqQQdxGyIAayICNgIAQbC9BCAAIAFqIgA2AgAgACACQQFyNgIEIAEgA2pBKDYCBEG0vQRBgMEEKAIANgIAIAQgBUEnIAVrQQdxQQAgBUEna0EHcRtqQS9rIgAgACAEQRBqSRsiAkEbNgIEIAJB4MAEKQIANwIQIAJB2MAEKQIANwIIQeDABCACQQhqNgIAQdzABCAGNgIAQdjABCABNgIAQeTABEEANgIAIAJBGGohAANAIABBBzYCBCAAQQhqIQEgAEEEaiEAIAEgBUkNAAsgAiAERg0DIAIgAigCBEF+cTYCBCAEIAIgBGsiBUEBcjYCBCACIAU2AgAgBUH/AU0EQCAFQQN2IgBBA3RBwL0EaiECAn9BmL0EKAIAIgFBASAAdCIAcUUEQEGYvQQgACABcjYCACACDAELIAIoAggLIQAgAiAENgIIIAAgBDYCDCAEIAI2AgwgBCAANgIIDAQLQR8hACAFQf///wdNBEAgBUEIdiIAIABBgP4/akEQdkEIcSICdCIAIABBgOAfakEQdkEEcSIBdCIAIABBgIAPakEQdkECcSIAdEEPdiABIAJyIAByayIAQQF0IAUgAEEVanZBAXFyQRxqIQALIAQgADYCHCAEQgA3AhAgAEECdEHIvwRqIQMCQEGcvQQoAgAiAkEBIAB0IgFxRQRAQZy9BCABIAJyNgIAIAMgBDYCACAEIAM2AhgMAQsgBUEAQRkgAEEBdmsgAEEfRht0IQAgAygCACEBA0AgASICKAIEQXhxIAVGDQQgAEEddiEBIABBAXQhACACIAFBBHFqIgMoAhAiAQ0ACyADIAQ2AhAgBCACNgIYCyAEIAQ2AgwgBCAENgIIDAMLIAMoAggiACAINgIMIAMgCDYCCCAIQQA2AhggCCADNgIMIAggADYCCAsgCUEIaiEADAULIAIoAggiACAENgIMIAIgBDYCCCAEQQA2AhggBCACNgIMIAQgADYCCAtBpL0EKAIAIgAgB00NAEGkvQQgACAHayIBNgIAQbC9BEGwvQQoAgAiAiAHaiIANgIAIAAgAUEBcjYCBCACIAdBA3I2AgQgAkEIaiEADAMLQcSzBEEwNgIAQQAhAAwCCwJAIAZFDQACQCAFKAIcIgJBAnRByL8EaiIAKAIAIAVGBEAgACABNgIAIAENAUGcvQQgCUF+IAJ3cSIJNgIADAILIAZBEEEUIAYoAhAgBUYbaiABNgIAIAFFDQELIAEgBjYCGCAFKAIQIgAEQCABIAA2AhAgACABNgIYCyAFKAIUIgBFDQAgASAANgIUIAAgATYCGAsCQCADQQ9NBEAgBSADIAdqIgBBA3I2AgQgACAFaiIAIAAoAgRBAXI2AgQMAQsgBSAHQQNyNgIEIAUgB2oiBCADQQFyNgIEIAMgBGogAzYCACADQf8BTQRAIANBA3YiAEEDdEHAvQRqIQICf0GYvQQoAgAiAUEBIAB0IgBxRQRAQZi9BCAAIAFyNgIAIAIMAQsgAigCCAshACACIAQ2AgggACAENgIMIAQgAjYCDCAEIAA2AggMAQtBHyEAIANB////B00EQCADQQh2IgAgAEGA/j9qQRB2QQhxIgJ0IgAgAEGA4B9qQRB2QQRxIgF0IgAgAEGAgA9qQRB2QQJxIgB0QQ92IAEgAnIgAHJrIgBBAXQgAyAAQRVqdkEBcXJBHGohAAsgBCAANgIcIARCADcCECAAQQJ0Qci/BGohAQJAAkAgCUEBIAB0IgJxRQRAQZy9BCACIAlyNgIAIAEgBDYCAAwBCyADQQBBGSAAQQF2ayAAQR9GG3QhACABKAIAIQcDQCAHIgEoAgRBeHEgA0YNAiAAQR12IQIgAEEBdCEAIAEgAkEEcWoiAigCECIHDQALIAIgBDYCEAsgBCABNgIYIAQgBDYCDCAEIAQ2AggMAQsgASgCCCIAIAQ2AgwgASAENgIIIARBADYCGCAEIAE2AgwgBCAANgIICyAFQQhqIQAMAQsCQCAIRQ0AAkAgASgCHCICQQJ0Qci/BGoiACgCACABRgRAIAAgBTYCACAFDQFBnL0EIAlBfiACd3E2AgAMAgsgCEEQQRQgCCgCECABRhtqIAU2AgAgBUUNAQsgBSAINgIYIAEoAhAiAARAIAUgADYCECAAIAU2AhgLIAEoAhQiAEUNACAFIAA2AhQgACAFNgIYCwJAIANBD00EQCABIAMgB2oiAEEDcjYCBCAAIAFqIgAgACgCBEEBcjYCBAwBCyABIAdBA3I2AgQgASAHaiICIANBAXI2AgQgAiADaiADNgIAIAoEQCAKQQN2IgBBA3RBwL0EaiEEQay9BCgCACEFAn9BASAAdCIAIAZxRQRAQZi9BCAAIAZyNgIAIAQMAQsgBCgCCAshACAEIAU2AgggACAFNgIMIAUgBDYCDCAFIAA2AggLQay9BCACNgIAQaC9BCADNgIACyABQQhqIQALIAtBEGokACAAC4MBAgN/AX4CQCAAQoCAgIAQVARAIAAhBQwBCwNAIAFBAWsiASAAIABCCoAiBUIKfn2nQTByOgAAIABC/////58BViECIAUhACACDQALCyAFpyICBEADQCABQQFrIgEgAiACQQpuIgNBCmxrQTByOgAAIAJBCUshBCADIQIgBA0ACwsgAQvjAQECfyACQQBHIQMCQAJAAkAgAEEDcUUgAkVyDQAgAUH/AXEhBANAIAAtAAAgBEYNAiACQQFrIgJBAEchAyAAQQFqIgBBA3FFDQEgAg0ACwsgA0UNAQsCQCAALQAAIAFB/wFxRiACQQRJckUEQCABQf8BcUGBgoQIbCEDA0AgACgCACADcyIEQX9zIARBgYKECGtxQYCBgoR4cQ0CIABBBGohACACQQRrIgJBA0sNAAsLIAJFDQELIAFB/wFxIQEDQCABIAAtAABGBEAgAA8LIABBAWohACACQQFrIgINAAsLQQAL5QUDBHwBfwF+AkACQAJAAnwCQCAAvSIGQiCIp0H/////B3EiBUH60I2CBE8EQCAAvUL///////////8Ag0KAgICAgICA+P8AVg0FIAZCAFMEQEQAAAAAAADwvw8LIABE7zn6/kIuhkBkRQ0BIABEAAAAAAAA4H+iDwsgBUHD3Nj+A0kNAiAFQbHFwv8DSw0AIAZCAFkEQEEBIQVEdjx5Ne856j0hASAARAAA4P5CLua/oAwCC0F/IQVEdjx5Ne856r0hASAARAAA4P5CLuY/oAwBCwJ/IABE/oIrZUcV9z+iRAAAAAAAAOA/IACmoCIBmUQAAAAAAADgQWMEQCABqgwBC0GAgICAeAsiBbciAkR2PHk17znqPaIhASAAIAJEAADg/kIu5r+ioAsiACAAIAGhIgChIAGhIQEMAQsgBUGAgMDkA0kNAUEAIQULIAAgAEQAAAAAAADgP6IiA6IiAiACIAIgAiACIAJELcMJbrf9ir6iRDlS5obKz9A+oKJEt9uqnhnOFL+gokSFVf4ZoAFaP6CiRPQQEREREaG/oKJEAAAAAAAA8D+gIgREAAAAAAAACEAgBCADoqEiA6FEAAAAAAAAGEAgACADoqGjoiEDIAVFBEAgACAAIAOiIAKhoQ8LIAAgAyABoaIgAaEgAqEhAQJAAkACQCAFQQFqDgMAAgECCyAAIAGhRAAAAAAAAOA/okQAAAAAAADgv6APCyAARAAAAAAAANC/YwRAIAEgAEQAAAAAAADgP6ChRAAAAAAAAADAog8LIAAgAaEiACAAoEQAAAAAAADwP6APCyAFQf8Haq1CNIa/IQIgBUE5TwRAIAAgAaFEAAAAAAAA8D+gIgAgAKBEAAAAAAAA4H+iIAAgAqIgBUGACEYbRAAAAAAAAPC/oA8LRAAAAAAAAPA/Qf8HIAVrrUI0hr8iA6EgACABoaAgACABIAOgoUQAAAAAAADwP6AgBUETTRsgAqIhAAsgAAuNAQAgACAAIAAgACAARAn3/Q3hPQI/okSIsgF14O9JP6CiRDuPaLUogqS/oKJEVUSIDlXByT+gokR9b+sDEtbUv6CiRFVVVVVVVcU/oCAAoiAAIAAgACAARIKSLrHFuLM/okRZAY0bbAbmv6CiRMiKWZzlKgBAoKJESy2KHCc6A8CgokQAAAAAAADwP6CjC4QCAQZ/IwBBEGsiBCQAAkAgBEEMaiAAQYCtA0EcELwEIgFBAEgNACABQeCtA2ohAQNAAn8gAUEBaiABLQAAIgZBP3EiAkEwSQ0AGiACQQh0IQMgAkE3TQRAIAMgAS0AAWpB0N8AayECIAFBAmoMAQsgAS0AAiADQYDwAGsgAS0AAUEIdHJqQbAQaiECIAFBA2oLIQMgAyAGQX9zQYABcUEHdmohAQJAIAAgAiAEKAIMIgNqQQFqIgJJBEACQAJAIAZBBnYOAwMABQELIAFBAWstAAAgACADa2ohBQwEC0HmASEFDAMLIAQgAjYCDAwBCwsgAUEBay0AACEFCyAEQRBqJAAgBQtZAQN/QX8hASAAIAAoAgAiAkECaiIDEOACBH9BfwUgACgCCCIBQQRqIAEgAkECdCICEIECIAAoAggiAUEANgIAIAEgAmpBfzYCBCAAIAM2AgAgABC6BEEACwvyAQEEfwJAA0ACQAJAAkACfyACIAdMIgggBCAGTHJFBEAgASAHQQJ0aigCACIJIAMgBkECdGooAgAiCEkEQCAJDAILIAggCUcNAyAGQQFqIQYgB0EBaiEHIAkhCAwECyAIDQEgASAHQQJ0aigCAAshCCAHQQFqIQcMAgsgBCAGTA0DIAMgBkECdGooAgAhCAsgBkEBaiEGCwJ/AkACQAJAAkAgBQ4DAwABAgsgBiAHcUEBcQwDCyAGIAdzQQFxDAILEAEACyAGIAdyQQFxCyEJIAkgACgCAEEBcUYNACAAIAgQvgRFDQALQX8PCyAAELoEQQALawIBfgJ/IAAoAgAhAwNAIAMtAAAiBEE6a0H/AXFB9gFPBEAgAkIKfiAErUL/AYN8QjB9IgJC/////wdUIgQgAXIEQCACQv////8HIAQbIQIgA0EBaiEDDAIFQX8PCwALCyAAIAM2AgAgAqcLCwAgAEHaC0EAED8LFgAgACABQf8BcRAQIAAgAkH/AXEQEAuKCAEPfyMAQeAEayIMJAAgACACEL4DIQ0gACACQYABchC+AyESAkAgAkUgAUECSXINACAMIAE2AgQgDCAANgIAIAxBADYCCEEAIAJrIRAgDEEMciELA0AgCyAMTQ0BIAtBDGsiCygCCCIOQTIgDkEyShshEyALKAIEIQkgCygCACEFA0ACQCAFIAlBB08EfyAOIBNHDQEgAiAJbCIGIAJrIQggCUEBdiACbCEKIAUgAhC+AyEJA0ACQCAKRQRAA0AgBiACayIGRQ0CIAUgBSAGaiACIAkRBgAgBiACayEIQQAhAANAIABBAXQgAmoiASAGTw0BIAEgCEkEQCABQQAgAiABIAVqIgcgAiAHaiAEIAMRAQBBAEobaiEBCyAAIAVqIgcgASAFaiIAIAQgAxEBAEEASg0BIAcgACACIAkRBgAgASEADAALAAsACyAKIAJrIgohAANAIABBAXQgAmoiASAGTw0CIAEgCEkEQCABQQAgAiABIAVqIgcgAiAHaiAEIAMRAQBBAEobaiEBCyAAIAVqIgcgASAFaiIAIAQgAxEBAEEASg0CIAcgACACIAkRBgAgASEADAALAAsLQQAFIAkLIAJsaiEIIAUhBwNAIAIgB2oiByEAIAcgCE8NAwNAIAAgBU0NASAAIBBqIgEgACAEIAMRAQBBAEwNASAAIAEgAiANEQYAIAEhAAwACwALAAsgDkEBaiEOQQEhByAFAn8gBSAJQQJ2IAJsIgFqIgYgBSABQQF0aiIIIAQgAxEBACEAIAggBSABQQNsaiIKIAQgAxEBACEBAkAgAEEASARAIAFBAEgNASAKIAYgBiAKIAQgAxEBAEEASBsMAgsgAUEASg0AIAYgCiAGIAogBCADEQEAQQBIGyEICyAICyACIA0RBgAgBSACIAlsaiIKIQEgCiEIIAIgBWoiDyEAQQEhEQNAAkACQCAAIAFPDQAgBSAAIAQgAxEBACIGQQBIDQAgBg0BIA8gACACIA0RBgAgAiAPaiEPIBFBAWohEQwBCwJAA0AgACABIBBqIgFPDQEgBSABIAQgAxEBACIGQQBMBEAgBg0BIAggEGoiCCABIAIgDREGACAJQQFrIQkMAQsLIAAgASACIA0RBgAMAQsgBSAAIA8gBWsiBiAAIA9rIgEgASAGSxsiAWsgASASEQYAIAAgCiAKIAhrIgEgCCAAayIGIAEgBkkbIgBrIAAgEhEGACAJIAdrIQggCiAGayEBAkAgCCAHIBFrIglJBEAgBSEHIAkhACABIQUgCCEJDAELIAEhByAIIQALIAsgDjYCCCALIAA2AgQgCyAHNgIAIAtBDGohCwwCCyAAIAJqIQAgB0EBaiEHDAALAAsACwALIAxB4ARqJAALTgEBfyABEJABBEAgARAPDwsCQCABQoCAgIBwVA0AIAGnIgIvAQZBBEcNACACKQMgIgEQkAFFDQAgARAPDwsgAEGkMkEAEBZCgICAgOAAC40CAQJ/IwBBEGsiAyQAIAMgAjcDCEKAgICA4AAhAgJAIAAgARDCASIEQQBIDQAgBEUEQCAAQoCAgIAwQQEgA0EIahDpAiECDAELIAAgAUE8IAFBABAUIgEQDQRAIAEhAgwBCwJAAkAgARC1AUUNACAAIAEQjwMiBEUNASAAIARGDQAgACABIAQpA0AQWkUNACAAIAEQDEKAgICAMCEBCyABECIEQCAAIAFBzAEgAUEAEBQhAiAAIAEQDCACEA0NAkKAgICAMCACIAIQKBshAQsgARASBEAgAEKAgICAMEEBIANBCGoQ6QIhAgwCCyAAIAFBASADQQhqELIBIQILIAAgARAMCyADQRBqJAAgAgsaACAAQd4AQdgAIAEbEBAgACACQf//A3EQMQvwAQEDfwNAAkAgAiADTA0AIAEgA2oiBS0AACIGQQJ0IQcCQAJAIAZBtAFHBEAgBkHAAUcNASAEIAUoAAE2AgAMAgsgACAFKAABIgVBABB0QQBKDQIgACgCpAIgBUEUbGooAhBFDQFBguoAQb7jAEGI8AFBotUAEAAACyAHQbOaAWotAAAiBkEcSw0AQQEgBnQiBkGAgIAccUUEQCAGQYCAgOAAcUUEQCAGQYCAgIIBcUUNAiAAIAUoAAFBfxB0GgwCCyAAIAUoAAVBfxB0GgsgACgCACAFKAABEBMLIAMgB0GwmgFqLQAAaiEDDAELCyADC7kDAQV/IAFFBEAgACACQQRxQQhyEO4BDwtBfyEDAkACQAJAIAAgAUEBayIEIAIQswINACAEQQdLDQEgAkF7cSEFIAJBAXEhBiABQQFrIQcDQCAAKAIQIQECQAJAAkACQAJAAkACQAJAAkACQCAHDgcAAQIDBAUGBwsgAUElRwRAQZoBIQIgAUEqRg0JIAFBL0cNDUGbASECDAkLQZwBIQIMCAtBnQEhAkEAIQMCQCABQStrDgMICgAKC0GeASECDAcLIAFB6gBqIgFBA08NCiABQeAAayECDAYLQQAhAwJAAkACQAJAIAFB5gBqDgMBCwIACwJAIAFByQBqDgIIAwALQaMBIQICQCABQTxrDgMJCwALC0GlASECDAgLQaQBIQIMBwtBpgEhAgwGC0GnASECDAULIAFB4wBqIgFBBE8NCEGp16rleiABQQN0diECDAQLQa0BIQIgAUEmRw0HDAMLQa4BIQIgAUHeAEcNBgwCC0GvASECIAFB/ABHDQUMAQtBqAEhAiAGRQ0CC0F/IQMgABARDQEgACAEIAUQswINASAAIAJB/wFxEA4MAAsACyADDwsQAQALQQALCQAgAEECEM8DC1MBBH8gACgC9AEiAkEAIAJBAEobIQRBACECAkADQCACIARGDQEgASAAKAL8ASIFIAJBBHRqKAIMRwRAIAJBAWohAgwBCwsgBSACQQR0aiEDCyADCzYAA0AgASACTEUEQCAAQbMBEA4gACABQf//A3EQGCAAKAJAKALMASABQQN0aigCACEBDAELCwvZAQEBfyAAIAAoAkAiAyABAn8CQAJAAkACQAJAIAFBJ0YNACABQc0ARiABQTpGckUEQCABQcUARg0BIAFBLUcNAiADLQBsQQFHDQIgAEHKMEEAEBVBfw8LIAMtAG5BAXEEQCAAQcTTAEEAEBVBfw8LIAFBxQBHDQELIAJBsX9GDQMgAkFDRg0BIAJBUUcgAkFJR3ENAiAAQdHPAEEAEBVBfw8LIAJBsX9GDQIgAkFDRg0AQQEgAkFRRg0DGiACQUlHDQFBAgwDC0EFDAILEAEAC0EGCxCsAUEfdQsJACAAQQAQ8QELQAEBfwJAIAJCgICAgHBUDQAgAqciAy8BBkEKRw0AIAMpAyAiAhCQAUUNACAAIAEgAhBHDwsgAEGhHUEAEBZBfwsbAQF+IAAgASACIAMgBBDGAiEFIAAgARAMIAUL2gMCBn8BfiMAQTBrIgUkACABQSoQQCEGIAVCADcCKAJAA0AgB0ECRwRAQQAhBCAAQSAQbCIIBEADQCAEQQJGRQRAIAggBEEDdCIJaiADIAlqKQMAEA83AwggBEEBaiEEDAELCyAIIAIgB0EDdGopAwAiCkKAgICAMCAAIAoQOxsQDzcDGCAFQShqIAdBAnRqIAg2AgAgB0EBaiEHDAIFQX8hBCAHQQFHDQMgACgCECAFKAIoELwCDAMLAAsLAkAgBigCACIERQRAQQAhBANAIARBAkYNAiAFQShqIARBAnRqKAIAIAYgBEEDdGpBBGoQTCAEQQFqIQQMAAsACwJAIARBAkcNAEECIQQgBigCFA0AIAAoAhAiAigCmAEiA0UNACAAIAEgBikDGEEBIAIoApwBIAMRMwAgBigCACEECyAFIAVBKGogBEEBayIDQQJ0aigCACICKQMINwMAIAUgAikDEDcDCCAFIAIpAxg3AxBBACEEIAUgA0EAR61CgICAgBCENwMYIAUgBikDGDcDICAAQS1BBSAFEIMDA0AgBEECRg0BIAAoAhAgBUEoaiAEQQJ0aigCABC8AiAEQQFqIQQMAAsACyAGQQE2AhRBACEECyAFQTBqJAAgBAsjACAAIAEpAwgQJyAAIAEpAxAQJyAAIAEpAxgQJyAAIAEQIQsMACAAIAEgACABUxsLhgIBAX8jAEEQayIHJAAgByAAOQMIIAcgAUEBayIFNgIAIAZBgAFBzNgAIAcQVxogAyAGLQAAQS1GNgIAIAQgBi0AAToAACABQQJOBEAgBEEBaiAGQQNqIAUQJRoLIAEgBGpBADoAACACAn8gASAGaiABQQFKakECaiECQQAhA0EAIQQDQCACIgFBAWohAiABLAAAEIMGDQALAkACQAJAIAEsAAAiBUEraw4DAQIAAgtBASEECyACLAAAIQUgAiEBCyAFEEUEQANAIANBCmwgASwAAGtBMGohAyABLAABIQIgAUEBaiEBIAIQRQ0ACwsgA0EAIANrIAQbQQFqCzYCACAHQRBqJAALCgAgACABQQJ0agsOACAAIAFqQYGA3PF5bAsQACAAIAAoAhhBf3NBAnRqCyEAIAAgAa0gASkDAEKAgICAMCABKAIIIAEoAiBBBBDjAQuWAgIFfwF+IwBBEGsiBCQAIwBBEGsiAyQAIANCgICAgDA3AwggA0KAgICAMDcDACAAQSxBAkEAQQIgAxDmASEIIANBEGokACAEIAg3AwggCBANRQRAAn4CfiACEBIEQCAAIAJBASAEQQhqEOoFDAELIAAgAkEBIARBCGoQsgELIggQDUUEQCAEKQMIQQ8QQCEHA0AgBUECRgRAA0AgBkECRwRAIAEgBkEDdCIDaiADIAdqKQMIEA83AwAgBkEBaiEGDAELCyAEKQMIIQIgCAwDCyAFQQN0IQMgBUEBaiEFIAAgAyAHaikDCBBpRQ0ACwsgACAEKQMIEAwgCCECQoCAgIDgAAshCCAAIAIQDAsgBEEQaiQAIAgLkwwDCX8DfgF8IwBB0ABrIggkACAIIAE2AkxB3wBBgAIgBEEgcRshCQJAAkACQAJAAkACQAJAAkAgAS0AACIHQStrDgMBAgACC0EBIQwLIAggAUEBaiIBNgJMIARBgAhxRQ0BIAEtAAAhBwsgB0EwRw0AAn8CQAJAAkACQAJAAkAgAS0AASIHQfgARwRAIAdB7wBGDQIgB0HYAEcNAQsgA0FvcQ0KIAggAUECaiIGNgJMQRAMBgsgA0UgB0HPAEZxDQEgB0HiAEYNAiADRSAHQcIARnENAyADIAdBMGtB/wFxQQlLcg0HIARBEHFFDQggAUEBaiEGQQEhBQNAIAdB+AFxQTBHDQUgASAFQQFqIgVqLQAAIQcMAAsACyADDQgLIARBBHFFDQYgCCABQQJqIgY2AkxBCAwDCyADDQYLIARBBHFFDQQgCCABQQJqIgY2AkxBAgwBC0GAAiEJIAdB/gFxQThGDQMgCCAGNgJMQQgLIQNCgICAgMB+IQ4gBi0AABD1ASADSA0DDAQLIARBgQFxDQACfyAIQcwAaiEHQdELIQUDQCAFLQAAIgYEQCAGIAEtAABHBEBBAAwDBSAFQQFqIQUgAUEBaiEBDAILAAsLIAcEQCAHIAE2AgALQQELRQ0ARAAAAAAAAPD/RAAAAAAAAPB/IAwbIhG9An8gEZlEAAAAAAAA4EFjBEAgEaoMAQtBgICAgHgLIgC3vVEEQCAArSEODAQLIBEQFyEODAMLIAMNAQtBCiEDCyAIKAJMIgpBAWohB0EAIQEgA0EKRyELAkADQAJAIAEgCmoiBS0AACIGQRh0QRh1IQ0gBhD1ASADTgRAIAkgDUcNASALIAFBAUdyRQRAIAotAABBMEYNBAsgBS0AARD1ASADTg0BCyAIIAogAUEBaiIBajYCTAwBCwsgBSEHC0EAIQsCQCAEQQFxDQACQCAGQS5HDQAgByAKTQRAIActAAEQ9QEgA04NAgsgCCAHQQFqIgU2AkxCgICAgMB+IQ4gCSAHLAABIgZGDQIDQCAGQf8BcRD1ASADTgRAQQEhCyAJIAZBGHRBGHVHDQIgBS0AARD1ASADTg0CCyAIIAVBAWoiATYCTCAFLQABIQYgASEFDAALAAsgBSAKTQ0AAkAgBkH/AXFB5QBHBEAgA0EKRiAGQf8BcUHFAEZxDQEgBkEgckH/AXFB8ABHIANBEEtyDQJBASADdEGEggRxDQEMAgsgA0EKRw0BC0EBIQsgBUEBaiEBAkACQAJAIAUtAAFBK2sOAwACAQILIAVBAmohAQwBCyAFQQJqIQELIAEtAAAQRUUNACABIQUDQCAIIAUiAUEBaiIFNgJMIAEtAAEiBEEYdEEYdSEHIAQQRQ0AIAcgCUcNASABLQACEEUNAAsLIAUgCkYEQEKAgICAwH4hDgwBCyAIIQkCQCAFIAprIgRBAmoiB0HBAE8EQCAAKAIQIAcQ6AEiCUUNAQtBACEBQQAhBiAMBEAgCUEtOgAAQQEhBgsgBEEAIARBAEobIQQDQCABIARHBEAgASAKai0AACIFQd8ARwRAIAYgCWogBToAACAGQQFqIQYLIAFBAWohAQwBCwsgBiAJakEAOgAAAn4gA0EKRwRAQoCAgIDAfiALDQEaCwJ8QgAhDiALIANBCkZxRQRAIAkgCS0AACIGQS1GaiEBA0AgASIEQQFqIQEgBC0AACIFQTBGDQALAn4gA0EKRgRAQgohD0KYs+bMmbPmzBkMAQtBACADa6wgA6wiD4ALIRBBACEBA0ACQCAFRQ0AIAUQ9QEiBSADTg0AIA4gBawgDiAPfnwgDiAQViIFGyEOIAEgBWohASAELQABIQUgBEEBaiEEDAELCyAOuiERIAEEQCADtyABtxCCBiARoiERCyARmiARIAZBLUYbDAELIAkQ+gULIhG9An8gEZlEAAAAAAAA4EFjBEAgEaoMAQtBgICAgHgLIgG3vVEEQCABrQwBCyAREBcLIQ4gB0HBAEkNASAAKAIQIAkQIQwBCyAAEMkBQoCAgIDgACEOCyACBEAgAiAIKAJMNgIACyAIQdAAaiQAIA4LKwAgAEH/AE0EQCAAQQN2Qfz///8BcUHA4AFqKAIAIAB2QQFxDwsgABC5BAsmAQF+IAAgASACIAFBABAUIgUQDQR+IAUFIAAgBSABIAMgBBA2CwuzBwIMfwF+IwBB4ABrIgUkACAAIAVByABqEJECAkAgAgRAIAUgAjYCQCAFQcgAakGqKCAFQUBrEIQCIANBf0cEQCAFIAM2AjAgBUHIAGpBgeMAIAVBMGoQhAILIAVByABqQQoQECAAIAFBMSAAIAIQdkEDEBsaIAAgAUEyIAOtQQMQGxogBEECcQ0BCyAEQQFxIQ0gACgCEEGMAWohAgNAIAIoAgAiAkUNASANBEBBACENDAELQQAhBAJAIAIpAwgiEUKAgICAcFQNACARpyIIKAIQIgYgBigCGEF/c0ECdEGkfnJqKAIAIQMgBhAqIQYDQCADRQ0BIAYgA0EBayIJQQN0aiIHKAIAIQMgBygCBEE2RwRAIANB////H3EhAwwBCwsgA0H/////A0sNACAIKAIUIAlBA3RqKQMAIhFCgICAgHCDQoCAgICQf1INACAAIBEQpgEhBAsgBSAEBH8gBEHA7wAgBC0AABsFQcDvAAs2AiAgBUHIAGpBqiggBUEgahCEAiAAIAQQNwJAIAIoAggiAy8BBhD4AQRAIAMoAiAiCC8AESIEQQt2QQFxIQMgBEGACHFFDQEgAigCICAIKAIUQX9zaiEQQQAhDiMAQRBrIgkkAEF/IQQCQCAILQASQQRxRQ0AIAgoAlAiB0UNACAHIAgoAkxqIQsgCCgCRCEGA0AgBiEEIAcgC08NASAHQQFqIQoCfyAHLQAAIgZFBEACQCAJQQhqIAogCxCTBSIMQQBIDQAgCSgCCCEPQQAhByMAQRBrIgYkAAJAIAZBDGogCiAMaiIMIAsQkwUiCkEASARAQX8hCgwBCyAGKAIMIgdBAXZBACAHQQFxa3MhBwsgCSAHNgIMIAZBEGokACAKIgdBAEgNACAJKAIMIARqIQYgByAMagwCCyAIKAJEIQQMAwsgBCAGQQFrIgYgBkH/AXFBBW4iD0EFbGtB/wFxakEBayEGIAoLIQcgDiAPaiIOIBBNDQALCyAJQRBqJAAgBSAAIAgoAkAQogQiBkHt7wAgBhs2AhAgBUHIAGpBlyggBUEQahCEAiAAIAYQNyAEQX9HBEAgBSAENgIAIAVByABqQYHjACAFEIQCCyAFQcgAakEpEBAMAQtBACEDIAVByABqQcP3AEEAEIQCCyAFQcgAakEKEBAgA0UNAAsLIAVByABqQQAQEEKAgICAICERIAUoAlRFBEAgACAFKAJIEHYhEQsgBUHIAGoQlwEgACABQTUgEUEDEBsaIAVB4ABqJAAL7AECAn8BfiMAQRBrIgMkACABQQhrIgQpAwAhBQJ/AkAgACADQQxqIAFBEGsiASkDABDGAQRAIAAgBRAMDAELIAAgA0EIaiAFEMYBDQAgAQJ/AkACQAJAAkACQAJAIAJBrQFrDgMBAwIACwJAIAJBoAFrDgIFAAQLIAMoAgwgAygCCHUMBQsgAygCCCADKAIMcQwECyADKAIIIAMoAgxyDAMLIAMoAgggAygCDHMMAgsQAQALIAMoAgwgAygCCHQLrTcDAEEADAELIAFCgICAgDA3AwAgBEKAgICAMDcDAEF/CyEAIANBEGokACAAC+oEAgd/An4CQCABQoCAgIBwg0KAgICAkH9SBEBCgICAgOAAIQogACABED0iARANDQELAkAgAkKAgICAcINCgICAgJB/UQ0AQoCAgIDgACEKIAAgAhA9IgIQDUUNACABIQIMAQsCQCACpyIFKQIEIgpC/////weDUA0AAkAgAaciAygCAEEBRw0AIAMpAgQgCoVCgICAgAiDQgBSDQAgACgCECADEKMEIAUoAgQiBkH/////B3EiCCADKAIEIgRB/////wdxIgdqIAZBH3Z0IARBH3YiCWtBEWpJDQAgA0EQaiEEIAkEQCAEIAdBAXRqIAVBEGogBkEBdBAlGiADIAMpAgQiCiAFKQIEfEL/////B4MgCkKAgICAeIOENwIEDAILIAQgB2ogBUEQaiAIECUaIAMgAykCBCIKIAUpAgR8Qv////8HgyILIApCgICAgHiDhDcCBCAEIAunakEAOgAADAELAn4CQAJAIAUpAgQiCqdB/////wdxIAMpAgQiC6dB/////wdxaiIGQYCAgIAETwRAIABBmsMAQQAQUAwBCyAAIAYgCiALhKciCEEfdhD9ASIHDQELQoCAgIDgAAwBCyAHQRBqIQQCQCAIQQBOBEAgBCADQRBqIAMoAgRB/////wdxECUiBCADKAIEQf////8HcWogBUEQaiAFKAIEQf////8HcRAlGiAEIAZqQQA6AAAMAQsgBCADIAMoAgRB/////wdxEJQFIAQgAygCBEEBdGogBSAFKAIEQf////8HcRCUBQsgB61CgICAgJB/hAshCiAAIAEQDAwBCyABIQoLIAAgAhAMIAoLQAAgAAJ/An8gAwRAIAEoAiQgAkEDdGpBBGoMAQtBACABKAIgIgNFDQEaIAMgAS8BKCACakEEdGoLKAIACxDiAQsLACAAQZ8JQQAQFguBDAINfwR+IwBBgAFrIgskACALIQUjAEHgAWsiCCQAAkAgAb0iEkKAgICAgICA+P8Ag0KAgICAgICA+P8AUQRAIBJC////////////AINCgYCAgICAgPj/AFoEQCAFQc7CuQI2AAAMAgsgAUQAAAAAAAAAAGMEQCAFQS06AAAgBUEBaiEFCyAFQdkLLQAAOgAIIAVB0QspAAA3AAAMAQsCQCAERQRAAn4gAZlEAAAAAAAA4ENjBEAgAbAMAQtCgICAgICAgICAfwsiE0KAgICAgICAEH1CgYCAgICAgGBUIBO5IAFicg0BIAhB1QFqIgNBADoAACATIBNCP4ciEoUgEn0hEiACrSEUA0AgAyICQQFrIgNBMEHXACASIBIgFIAiFSAUfn2nIgRBCkgbIARqOgAAIBIgFFohBCAVIRIgBA0ACyATQgBTBEAgAkECayIDQS06AAALIAUgAxCBBgwCC0QAAAAAAAAAACABIAFEAAAAAAAAAABhGyEBIARBAkcNACMAQYACayICJAACQCACQYABaiABIANBAWoiBEEAEIcDIAJqLQB/QTVHDQAgAkGAAWogASAEQYAIEIcDIgYgAiABIARBgBAQhwNHDQAgAkGAAWogAiAGEHcNAEGACEGAECACLQCAAUEtRhshCQsgBSABIAMgCRCHAxogAkGAAmokAAwBCyADIQIgCEEIaiENIAhBDGohDiAIQRBqIQwjAEGQA2siByQAAkAgBEEDcUEBRiIPRQRAQREhAkEBIQoDQCACIApNBEBBACEJDAMLIAEgAiAKakEBdiIJIA0gDiAMQQAgB0GQAmoiBhC+AiAGEPoFIAFhBEAgCUEBIAlBAEwbIQYDQCAJQQJIBEAgBiECDAMLIAkiAkEBayIJIAxqLQAAQTBGDQALBSAJQQFqIQoLDAALAAsgASACQQFqIgYgB0EMaiAHQQhqIAdBkAFqIgpBACAHQZACahC+AiACIApqLQAAQTVHDQAgASAGIAdBDGogB0EIaiAHQZABaiIKQYAIIAdBkAJqIhAQvgIgASAGIAdBBGogByAHQRBqIhFBgBAgEBC+AiAKIBEgBhB3DQAgBygCDCAHKAIERw0AQYAIQYAQIAcoAggbIQkLIAEgAiANIA4gDCAJIAdBkAJqEL4CIAdBkANqJAAgCCgCDARAIAVBLToAACAFQQFqIQULIAgoAgghBgJAIARBBHENACAGQQBMIAYgA0EVIA8bSnJFBEAgAiAGTARAQQAhBCAGIAJrIgNBACADQQBKGyEDIAUgCEEQaiACECUgAmohBQNAIAMgBEcEQCAFQTA6AAAgBEEBaiEEIAVBAWohBQwBCwsgBUEAOgAADAMLIAUgCEEQaiAGECUgBmoiBEEuOgAAQQAhBSACIAZrIgJBACACQQBKGyECA0AgBEEBaiEEIAIgBUcEQCAEIAhBEGogBSAGamotAAA6AAAgBUEBaiEFDAELCyAEQQA6AAAMAgsgBkEFakEFSw0AIAVBsNwAOwAAQQAhBEEAIAZrIQMgBUECaiEFA0AgAyAERwRAIAVBMDoAACAEQQFqIQQgBUEBaiEFDAELCyAFIAhBEGogAhAlIAJqQQA6AAAMAQsgBSAILQAQOgAAAkAgAkECSARAIAVBAWohBAwBCyAFQS46AAEgBUECaiEEQQEhBQNAIAIgBUYNASAEIAhBEGogBWotAAA6AAAgBUEBaiEFIARBAWohBAwACwALIARB5QA6AAAgBkEBayEDIAZBAEwEfyAEQQFqBSAEQSs6AAEgBEECagshAiAIIAM2AgAjAEEQayIEJAAgBCAINgIMIwBBoAFrIgMkACADQQhqIgVBgLEEQZABECUaIAMgAjYCNCADIAI2AhwgA0H/////B0F+IAJrIgYgBkH/////B0sbIgY2AjggAyACIAZqIgI2AiQgAyACNgIYIAVBnOMAIAgQqAQgBgRAIAMoAhwiAiACIAMoAhhGa0EAOgAACyADQaABaiQAIARBEGokAAsgCEHgAWokACAAIAsQdiESIAtBgAFqJAAgEgs3AQF/IAAgAhA4IQUgACACEAwgBUUEQCAAIAMQDEF/DwsgACABIAUgAyAEEBshBCAAIAUQEyAEC5MCAgJ/AXwjAEEQayIEJAACQAJAAkACQCACQiCIpyIFQQJNBEAgAqciA0EATg0DDAELIAVBB2tBbU0EQCAEAn8gAhBJIgZEAAAAAAAA8EFjIAZEAAAAAAAAAABmcQRAIAarDAELQQALIgM2AgwgBiADuGENAwwBCyADBEBBfyEDIAAgAhCgASICEA0NBCAAIARBDGogAkEBEM4CDQQgBCgCDCEDDAMLIAAgBEEMaiACEMcBBEAgACACEAwMAgtBfyEDIAAgAhCgASICEA0NAyAAIARBCGogAkEAEM4CDQMgBCgCCCIDIAQoAgxGDQILIABBx8EAEGsLQX8hAwwBCyABIAM2AgBBACEDCyAEQRBqJAAgAwsfACAAIAEgACACEMoBIgIgAUEAEBQhASAAIAIQEyABCzIBAX8jAEHQAGsiAiQAIAIgACACQRBqIAEQiQE2AgAgAEGw4QAgAhDSAiACQdAAaiQAC5EBAgF/AX4jAEEQayIFJAAgBSAENgIMQX8hBCAAIAEgBUEMahDkAUUEQCADEJsEIAEgAiADKAIEIAMoAgBBA3FBAnRBvKIBaigCABEaACEGIAMQ2AUgBSgCDCIAIAAoAgBB/////wNxNgIAIANCgICAgDAgBiAGEA0iABs3AwBBf0EAIAAbIQQLIAVBEGokACAECw0AIAAgASACQQIQrwMLDQAgACABIAJBAxCvAwsKACAAQSAgAWt2C9MBAQN/IwBBEGsiBSQAQX8hAwJAIAAoAhQNAAJAAkAgAUGAgICABE4EQCAAKAIAQZrDAEEAEFAMAQsgASAAKAIMQQNsQQJtEEpB/////wMQtAEhASAAKAIQIgQgAkGAAkhyRQRAIAAgARDuAyEDDAMLIAAoAgAgACgCBCABIAR0IARrQRFqIAVBDGoQtwEiAg0BCyAAEIoDDAELIAAoAhAhAyAFKAIMIQQgACACNgIEIAAgBCADdiABakH/////AxC0ATYCDEEAIQMLIAVBEGokACADC4EBAgJ/AX4CQCABKQIEIgRC//////////+/f1YEQCABKAIMIQAMAQsgACgCNCAEQiCIpyAAKAIkQQFrcUECdGohAiAAKAI4IQMDQCADIAIoAgAiAEECdGooAgAiAiABRg0BIAJBDGohAiAADQALQdX1AEG+4wBB+BRBrQ4QAAALIAAL8wYCBn8BfgJAAkACQAJ/IAJBAkwEQCACIAEpAgQiCUI+iKdGBEAgACABENYCIgMQ8gFFDQUgASABKAIAQQFrNgIAIAMPCyAAKAI0IAAoAiRBAWsgASACEOUFQf////8DcSIHcSIIQQJ0aiEDIAmnQf////8HcSEFA0AgAiADKAIAIgNFDQIaAkAgACgCOCADQQJ0aigCACIEKQIEIglCIIinQf////8DcSAHRyAJQj6IpyACR3IgCadB/////wdxIAVHcg0AIAQgASAFEOQFDQAgAxDyAQ0EIAQgBCgCAEEBajYCAAwECyAEQQxqIQMMAAsACyACQQNHIQdBAwshBQJAIAAoAjwNAEEAIQNB0wEgACgCLEEDbEECbRBKIgRB/////wNLDQEgACAAKAI4IARBAnQQ5wEiBkUNASAAKAIsIgJFBEAgAEEQEJwCIgJFBEAgACAGECEMAwsgAkEBNgIAIAIgAikCBEKAgICAgICAgECENwIEIAYgAjYCACAAIAAoAihBAWo2AihBASECCyAAIAI2AjwgACAGNgI4IAAgBDYCLCAEQQFrIQYDQCACIARPDQEgACgCOCACQQJ0akEAIAJBAWoiAyACIAZGGxDjBTYCACADIQIMAAsACwJAIAEEQCABKQIEIglC//////////8/WARAIAEgCSAFrUI+hoQ3AgQMAgsgACAJpyICQR91IAJB/////wdxIAJBH3Z0akERahDoASICRQRAQQAhAwwECyACQQE2AgAgAiACKQIEQv////93gyABKQIEQoCAgIAIg4QiCTcCBCACIAlCgICAgHiDIAEpAgRC/////weDhDcCBCACQRBqIAFBEGogASgCBCIDQR91IANB/////wdxIANBH3Z0akEBahAlGiAAIAEQpAQgAiEBDAELIABBEBDoASIBRQRAQQAPCyABQoGAgICAgICAgH83AgALIAAgACgCOCAAKAI8IgNBAnRqIgIoAgBBAXY2AjwgAiABNgIAIAEgAzYCDCABIAE1AgQgB61CIIaEIAWtQj6GhDcCBCAAIAAoAihBAWo2AiggBUEDRg0CIAEgACgCNCAIQQJ0aiIBKAIANgIMIAEgAzYCACAAKAIoIAAoAjBIDQIgACAAKAIkQQF0EMAFGgwCCyABRQ0BCyAAIAEQpAQgAw8LIAMLRgAgAkEATARAIABBLxAyDwsgACACQQAQ/QEiAEUEQEKAgICA4AAPCyAAQRBqIAEgAhAlIAJqQQA6AAAgAK1CgICAgJB/hAuiAQECfyMAQaABayIEJABBfyEFIAQgAUEBa0EAIAEbNgKUASAEIAAgBEGeAWogARsiADYCkAEgBEEAQZABEEsiBEF/NgJMIARB9wI2AiQgBEF/NgJQIAQgBEGfAWo2AiwgBCAEQZABajYCVAJAIAFBAEgEQEHEswRBPTYCAAwBCyAAQQA6AAAgBCACIANB9QJB9gIQqQQhBQsgBEGgAWokACAFC50DAwF+A38DfAJAAkACQAJAIAC9IgFCAFkEQCABQiCIpyICQf//P0sNAQsgAUL///////////8Ag1AEQEQAAAAAAADwvyAAIACiow8LIAFCAFkNASAAIAChRAAAAAAAAAAAow8LIAJB//+//wdLDQJBgIDA/wMhA0GBeCEEIAJBgIDA/wNHBEAgAiEDDAILIAGnDQFEAAAAAAAAAAAPCyAARAAAAAAAAFBDor0iAUIgiKchA0HLdyEECyAEIANB4r4laiICQRR2arciBkQAAOD+Qi7mP6IgAUL/////D4MgAkH//z9xQZ7Bmv8Daq1CIIaEv0QAAAAAAADwv6AiACAAIABEAAAAAAAAAECgoyIFIAAgAEQAAAAAAADgP6KiIgcgBSAFoiIFIAWiIgAgACAARJ/GeNAJmsM/okSveI4dxXHMP6CiRAT6l5mZmdk/oKIgBSAAIAAgAEREUj7fEvHCP6JE3gPLlmRGxz+gokRZkyKUJEnSP6CiRJNVVVVVVeU/oKKgoKIgBkR2PHk17znqPaKgIAehoKAhAAsgAAuZAQEDfCAAIACiIgMgAyADoqIgA0R81c9aOtnlPaJE65wriublWr6goiADIANEff6xV+Mdxz6iRNVhwRmgASq/oKJEpvgQERERgT+goCEFIAMgAKIhBCACRQRAIAQgAyAFokRJVVVVVVXFv6CiIACgDwsgACADIAFEAAAAAAAA4D+iIAUgBKKhoiABoSAERElVVVVVVcU/oqChC5IBAQN8RAAAAAAAAPA/IAAgAKIiAkQAAAAAAADgP6IiA6EiBEQAAAAAAADwPyAEoSADoSACIAIgAiACRJAVyxmgAfo+okR3UcEWbMFWv6CiRExVVVVVVaU/oKIgAiACoiIDIAOiIAIgAkTUOIi+6fqovaJExLG0vZ7uIT6gokStUpyAT36SvqCioKIgACABoqGgoAsrACAAQYABTwR/IABBzwFNBEAgAEGABWoPCyAAQQF0QdypA2ovAQAFIAALCxAAIAAvAAAgAC0AAkEQdHILvQIBB38CQCABRQ0AA0AgAkEDRgRAIAFBAXEiBUUgAUEGcUVyIQcDQCAEQfICRg0DAkACQCADIARBAnRBwOEBaigCACICQQR2QQ9xIgZ2QQFxRQ0AIAJBD3YhASACQQh2Qf8AcSEIAkACQAJAIAZBBGsOAgABAgsgB0UNASABIAVqIQZBACECA0AgAiAITw0DIAIgBmohASACQQJqIQIgACABIAFBAWoQf0UNAAsMAwsgB0UNACABQQFqIQIgBUUEQCAAIAEgAhB/DQMLIAAgAiABQQJqIgIQf0UEQCAFRQ0CIAAgAiABQQNqEH9FDQILQX8PCyAAIAEgASAIahB/DQELIARBAWohBAwBCwtBfw8FIAEgAnZBAXEEQCACQQJ0QYziA2ooAgAgA3IhAwsgAkEBaiECDAELAAsAC0EAC00BAX8gASAAKAIEIgJKBEAgACgCDCAAKAIIIAEgAkEDbEECbRBKIgFBAnQgACgCEBEBACICRQRAQX8PCyAAIAE2AgQgACACNgIIC0EAC5QCAQJ/IwBBEGsiBCQAAkAgBEEMaiAAIAIgAxC8BCICQQBIDQAgASACaiECA0AgAkEBaiEBAkAgAi0AACIDQT9NBEAgBCgCDCADQQN2akEBaiICIABLDQMgBCADQQdxIAJqQQFqIgM2AgwgBUEBcyEFDAELIANBGHRBGHVBAEgEQCAEIAMgBCgCDGpB/wBrIgM2AgwMAQsgA0HfAE0EQCAEIAQoAgwgAi0AASADQQh0cmpB//8AayIDNgIMIAJBAmohAQwBCyAEIAQoAgwgAi0AAiADQRB0IAItAAFBCHRycmpB////AmsiAzYCDCACQQNqIQELIAAgA0kNASAFQQFzIQUgASECDAALAAsgBEEQaiQAIAULTAECfyMAQRBrIgMkAAJ/IAIgASgCACIELQAARwRAIAMgAjYCACAAQbz9ACADED9BfwwBCyABIARBAWo2AgBBAAshAiADQRBqJAAgAgseACAAQTBrQQpJIABBX3FBwQBrQRpJciAAQd8ARnILrQEBA38gACgCQBoCQCAAKAIEIQMgACABEMYEDQBBBSADayEEA0AgACgCGCICLQAAQfwARwRAQQAPCyAAIAJBAWo2AhggACgCBCECIAAgA0EFEOsBBEAgABCsAkF/DwsgACgCACADakEJOgAAIAAoAgAgA2pBAWogAiAEahBdIABBB0EAELoBIQIgACABEMYEDQEgACgCACACaiAAKAIEIAJrQQRrEF0MAAsAC0F/C0gBAn8CQANAIAFBCkYNASABQQJ0QZLgAWovAQAgAEoNASABQQF0IQIgAUEBaiEBIAJBAXRBlOABai8BACAATA0AC0EBDwtBAAukAgEBfwJ/An8gAUH/AE0EQCAAIAE6AAAgAEEBagwBCwJAIAFB/w9NBEAgACABQQZ2QcABcjoAACAAIQIMAQsCfyABQf//A00EQCAAIAFBDHZB4AFyOgAAIABBAWoMAQsCQCABQf///wBNBEAgACABQRJ2QfABcjoAACAAIQIMAQsCfyABQf///x9NBEAgACABQRh2QfgBcjoAACAAQQFqDAELQQAgAUEASA0FGiAAIAFBHnZB/AFyOgAAIAAgAUEYdkE/cUGAAXI6AAEgAEECagsiAiABQRJ2QT9xQYABcjoAAAsgAiABQQx2QT9xQYABcjoAASACQQJqCyICIAFBBnZBP3FBgAFyOgAACyACIAFBP3FBgAFyOgABIAJBAmoLIABrCwskACAAQgA3AgAgACABNgIUIABCADcCCCAAIAJB4QIgAhs2AhALJwECfwJAIAAgAUEAEJsBIgMEQCADEJoBRQ0BIAAQdQtBfyECCyACC8kBAgN/AX4jAEEQayIFJAACQCAAIAFBAhBvIgEQDQ0AAkACQCACQQFHDQAgAykDACIHEJABRQ0AIAAgBUEMaiAHEA9BARDOAg0BIAAgAUEwAn4gBSgCDCICQQBOBEAgAq0MAQsgArgQFwsQSEEASA0BDAILIAJBACACQQBKGyECA0AgAiAERg0CIAAgASAEIAMgBEEDdGopAwAQDxCWAiEGIARBAWohBCAGQQBODQALCyAAIAEQDEKAgICA4AAhAQsgBUEQaiQAIAELEQAgACABIAIgAyAEIAUQywELDQAgAEEGQX9BBRDrBQt8AgJ+AX8gACACKQMAIgNBABCbASIFRQRAQoCAgIDgAA8LIAAgA0KAgICAMBDzASIDEA0EQCADDwsgAkEIaiECIAFBAWtBABBKIQEgAxASBEAgAEKAgICAMCABIAIgBS8BBhDsBQ8LIAAgAyABIAIQxQMhBCAAIAMQDCAECxEAIAAgASACIANBAEEAEMsBCy4AIABBDBAvIgAEQCAAIAM2AgggACACNgIEIAAgASgCEDYCACABIAA2AhALIAALawEBfwJAIAEoAqABIgNBAE4NACAAIAEgAhBYIgNBAEgNACABIAM2AqABIANBBHQiACABKAJ0aiICIAIoAgxBh39xQSByNgIMIAEtAG5BAXFFDQAgASgCdCAAaiIAIAAoAgxBAXI2AgwLIAMLLgEBfwJAIAEoApgBIgJBAE4NACAAIAFBzQAQWCICQQBIDQAgASACNgKYAQsgAgsyACAAKAIAIAEgAiADEPMCIgBBAE4EQCABKAJ0IABBBHRqIgEgASgCDEEDcjYCDAsgAAtwAQJ/IAEoAgBBAEgEQCABIAAQNTYCAAsgAEEREA4gAEGwARAOIAJBACACQQBKGyECIABB6QBBfxAdIQQDQCACIANGRQRAIABBDhAOIANBAWohAwwBCwsgAEEGEA4gAEHrACABKAIAEB0aIAAgBBAgC2gAIAAgASACEFgiAEEATgRAIAEoAnQgAEEEdGoiAiACKAIMQYd/cSADQQN0QfgAcXI2AgwgAiABKAK8ASIDNgIEIAIgASgCwAE2AgggASgCzAEgA0EDdGogADYCBCABIAA2AsABCyAAC24BAX8gACABQfwBakEQIAFB+AFqIAEoAvQBQQFqEIABRQRAIAEgASgC9AEiA0EBajYC9AEgASgC/AEgA0EEdGoiA0F/NgIAIAMgAy0ABEH4AXE6AAQgAyABKAK8ATYCCCADIAAgAhAZNgIMCyADC0wBAn8CQCAAKAJAEKgBIgBBI2siAkENTUEAQQEgAnRB5fAAcRsNAAJAAkAgAEHrAGsOBAIBAQIACyAAQeoBa0ECSQ0BC0EBIQELIAELsQMBA38gACgCQEGwAmohAwNAQQAhAgJAA0AgAygCACIDRQ0BIAMoAhwEQCABRQRAIABBBhAOCyAAQYQBEA5BgwEhAiAAIAAoAkAtAGxBA0YEfyAAQQ4QDiAAQQ4QDiAAQcIAEA4gAEEGEBwgAEEREA4gAEGwARAOIABB6gBBfxAdIQEgAEEkEA4gAEEAEBggAEGBARAOIABBiwEQDiAAQesAQX8QHSEEIAAgARAgIABBDhAOIAAgBBAgQQ4FQYMBCxAOQX0hAkEBIQELIAMoAhAgAmohAiADKAIUQX9GDQALQQ9BDiABGyEEA0AgAgRAIAAgBBAOIAJBAWshAgwBCwsgAUUEQCAAQQYQDgsgAEHtACADKAIUEB0aQQEhAQwBCwsgAAJ/IAAoAkAiAigCYARAAkAgAUUEQEF/IQIMAQsgAEEqEA4gAEHpAEF/EB0hAiAAQQ4QDgsgAEG2ARAOIABBCBAcIABBABAYIAAgAhAgQSgMAQsgAi0AbCIEBEACQCABRQRAQQYhAwwBC0GLASEDQS4gBEEDRw0CGgsgACADEA5BLgwBC0EoQSkgARsLEA4LTwEBf0F/IQECQCAAQfsAEDANACAAKAIQQf0ARwRAIAAQhQEaA0AgAEEHEPEBDQIgACgCEEH9AEcNAAsgABDvAQtBf0EAIAAQERshAQsgAQuZAQEEfyABKAIUIgVBACAFQQBKGyEGIAFBEGohBAJAA0AgAyAGRwRAIAQoAgAgA0EDdGooAgAgAkYNAiADQQFqIQMMAQsLQX8hAyAAIARBCCABQRhqIAVBAWoQgAENACABIAEoAhQiBEEBajYCFCABKAIQIQMgACACEBkhASADIARBA3RqIgBBADYCBCAAIAE2AgAgBiEDCyADC2UBAX8gAEH6ABBURQRAIABB5t4AQQAQFUEADwsCQCAAEBENACAAKAIQQYF/RwRAIABB1t4AQQAQFUEADwsgACgCACAAKQMgEDgiAUUNACAAEBFFBEAgAQ8LIAAoAgAgARATC0EAC4UTARd/IwBBQGoiAyQAIAAoAgAhCCAAKAJAIQQgA0EANgI8IAAoAhghFSAEIAQtAG4iFkEBcjoAbgJ/AkAgABARDQACQAJAIAAoAhBBg39GBEAgACgCKEUNASAAEPABDAMLIAEgAkECRnINASAAQavQAEEAEBUMAgsgCCAAKAIgEBkhCSAAEBENAQsgAUUEQCAIIAlB/AAgCRsQGSEKCyAAEIUBGgJ/IAAoAhAiBUFMRgRAIAAQEQ0CIAAQtAINAkEBDAELIABBBhAOQQALIQ0gCQRAIAAgBCAJQQIQrAFBAEgNAQsgAEH7ABAwDQAgBUFMRiERIAAQhQEaIABBAhAOIAQoAoQCIRcgAEEAEDogAEHWABAOIAAgCUEWQS8gChsgCRsQHCAAIA0QbiAEKAKYAiEYQQAhAQNAIAFBAkcEQCADQRBqIAFBBHRqIgZBADYCCCAGQgA3AwAgAUEBaiEBDAELCyADQQA2AjRBCEEHIAVBTEYbIRIgBUFMRyETA0ACQAJ/An8CQAJAIAAoAhAiAUE7RwRAIAFB/QBGDQVBACABQVZHDQMaIAAQEQ0HIAAoAhBBO2sOAwECAQILIAAQEUUNBQwGCyAIQSwQGRogA0EsNgI8IAAoAhghFEEAIQ9BACEQQQAhB0EsDAILIABBGxAOQQELIRAgACgCGCEUIAAgA0E8akEBQQBBARDSAyIHQQBIDQMgAUFWRiEPIAMoAjwLIQsgC0E7RiAPcSALQTxHIA9yIhlBASAHQW9xIg4bRSALQfgARnJyBEAgAEHvzwBBABAVDAMLIAdBEHEhDAJAAkACQAJAIAdBbnFBAkYEQCAMBEACQCAEIAsgBCgCvAEQzgMiAUEATgRAIAQoAnQgAUEEdGoiBSgCDCIGQQN2QQ9xIgFBCU1BAEEBIAF0QeAEcRsgASAOQQVqRnINBCAFIAZBh39xQcgAcjYCDAwBCyAAIAQgCyAOQQVqEPECQQBIDQkLIAAgA0EQaiAQQQR0ahDqBEEASA0ICyAAIA5BAmpBACAUIAAoAhRBACADQQxqEIoCDQcgDARAIAMoAgxBATYCuAEgAEHQABAOIABBuwEQDiADKAI8IQECQCAOQQJHBEAgCCABEOkEIgFFDQogACABEBwgACAEIAFBCBDxAiEFIAggARATIAVBAE4NAQwKCyAAIAEQHAsgACAAKAJALwG8ARAYDAULAkAgAygCPEUEQCAAQdUAEA4MAQsgAEHUABAOIAAgAygCPBAcCyAAIA5BAWtB/wFxEG4MBAtBBiEBQQEhB0EAIQVBACEGAkACQAJAAkACQCAODgcAAgICBAMBAgsgACgCEEEoRg0BIAtBO2tBAU0EQCAAQZjQAEEAEBUMCwsgDARAIAQgCyAEKAK8ARDOA0EATg0FIAAgBCALQQUQ8QJBAEgNCyAAQQUQDiAAIAMoAjwQHCAAQbsBEA4gACADKAI8EBwgACAAKAJALwG8ARAYCyADQRBqIBBBBHRqIgEoAgBFBEAgACABEOgEDQsLQQAhByADKAI8RQRAIAEoAgQhBiMAQSBrIgUkACAFIAY2AgAgBUEQaiIGQRBB8xAgBRBXGiAIQfUAQfQAIA8bIAYQ5gQhBiAFQSBqJAAgBiIHRQ0LIAAgBCAHQQIQrAFBAEgEQCAIIAcQEwwMCyAAQfAAEA4gAEG7ARAOIAAgBxAcIAAgACgCQC8BvAEQGAsgACABKAIANgJAIABBtgEQDiAAQQgQHCAAQQAQGAJAIAMoAjxFBEAgAEG2ARAOIAAgBxAcIAAgACgCQC8BvAEQGCABIAEoAgRBAWo2AgQgCCAHEBMMAQsgDEUNACAAQbYBEA4gACADKAI8EBwgACAAKAJALwG8ARAYCwJAIAAoAhBBPUYEQCAAEBENDCAAEGJFDQEMDAsgAEEGEA4LAkAgDARAIAAQzQMgAEHGABAODAELIAMoAjwiAUUEQCAAEM0DIABB0QAQDiAAQQ4QDgwBCyAAIAEQrQEgAEHMABAOIAAgAygCPBAcCyAAIAAoAkAoAgQ2AkAgABC9AUUNBwwKC0EDIQcMAgtBACEHIBkNASARIQUgEyEGIBIhASADKAI0RQ0BIABB3NcAQQAQFQwIC0ECIQcLIAwEQCAAIANBEGogEEEEdGoQ6gRBAEgNBwsgACABIAcgFCAAKAIUQQAgA0E4ahCKAg0GIAUgBnJBAUYEQCADIAMoAjg2AjQMBAsgDEUNAiADKAI4QQE2ArgBIAQgAygCPCIBIAQoArwBEM4DQQBIDQELIABBieEAQQAQFQwFCyAAIAQgAUEGEPECQQBIDQQgAEHQABAOIABBzQAQDiAAIAMoAjwQHCAAQbsBEA4gACADKAI8EBwgACAAKAJALwG8ARAYDAELAkAgAygCPEUEQCAAQdUAEA4MAQsgAEHUABAOIAAgAygCPBAcCyAAQQAQbgsgDwRAIABBGxAOCyAIIAMoAjwQEyADQQA2AjwMAQsLIAMoAjQiAUUEQCADQTRqIREjAEEQayIBJAAgACABEPwCIABBhQhBgAggDRsiBTYCOCAAKAI8IRIgACAFQRhBBCANG2o2AjwgACgCFCETQX8hBiAAEBFFBEAgAEEIQQcgDRtBACAFIBNBACAREIoCIQYLIAAgEjYCPCAAIAEQ+wIhDSABQRBqJAAgBiANcg0BIAMoAjQhAQsgBCgCgAIgF2ogASgCCBBdIAQtAG5BAnFFBEAgCCADKAI0KAKMAxAaIAMoAjQgACgCOCAVayIBNgKQAyAIIBUgARCjAyEBIAMoAjQgATYCjAMgAUUNAQsgABARDQAgACAEQfYAQQIQrAFBAEgNAAJAIAMoAhAEQCAAIANBEGoQ5wQMAQsgAEEGEA4LIABBuwEQDiAAQfYAEBwgACAAKAJALwG8ARAYIABBDhAOIAMoAiAEQCAAQREQDiAAIANBIGoQ5wQgAEEkEA4gAEEAEBggAEEOEA4LIAkEQCAAQREQDiAAQbsBEA4gACAJEBwgACAELwG8ARAYCyAAEO8BIAAQ7wECQCAKBEAgACAEIApBARCsAUEASA0CIABBuwEQDiAAIAoQHCAAIAQvAbwBEBgMAQsgCQ0AIABBvwEQDiAAIAQoApgCIBhrQQFqEDoLQQAgAkUNARpBACAAIAQoApQDIAogCkEWIAJBAUYbQQAQiQINARoLIAggAygCPBATQX8LIQAgCCAJEBMgCCAKEBMgBCAWOgBuIANBQGskACAACy4AIAAgASgCADYCFCAAIAEoAgQ2AgggACABKAIMNgI4IAAgASgCCDYCMCAAEBELKgAgASAAKAIENgIAIAEgACgCFDYCBCABIAAoAhg2AgwgASAAKAIwNgIICxgAIAAgACABgSIAIABCP4cgAYN8fSABfwseACAAIAEgACACEA8gAxCTAyICQQAQgAUgACACEAwLZQEDfyABKAIQIgQgASgCFEEBayACEOIDcUEDdCIFakEEaiEDA38gAygCACIDIAQgBWpGBEBBAA8LIAAgAykDCBAPIAIQD0ECEN8BBH8gA0EYawUgA0EEaiEDIAEoAhAhBAwBCwsLKQACQCAAQiCIp0EHa0FtSw0AIAAQSUQAAAAAAAAAAGINAEIAIQALIAAL0wMCCH8DfiMAQTBrIgQkAEKAgICA4AAhDAJAIAAgARArIgEQDQ0AQoCAgIAwIQwCQAJAIAAgBEEsaiAEQShqIAGnIgkgAkFvcRCSAQ0AIAAQUSIMEA0NACACQRBxIQogBCgCLCEGIAQoAighByADQQFrIQtBACECA0AgAiAHRg0CIAYgAkEDdGooAgQhAwJAAkAgCgRAIAAgBEEIaiAJIAMQTyIFQQBIBEBBAiEFDAILIAVFBEBBBSEFDAILIAAgBEEIahBOQQUhBSAEKAIIQQRxRQ0BCwJAAkACQAJAAkAgCw4CAQIACyAAIAMQYCINEA1FDQIMBwsgACABIAMgAUEAEBQiDRANRQ0BDAYLIAAQUSINEA0NBSAAIAMQYCIOEA0NASAAIA1CACAOQYCAARCuAUEASA0BIAAgASADIAFBABAUIg4QDQ0BIAAgDUIBIA5BgIABEK4BQQBIDQELIAAgDCAIrSANQQAQrgFBAEgNBCAIQQFqIQgMAgsgACANEAwMAwsgBUECaw4EAgQEAAQLIAJBAWohAgwACwALIAAgDBAMQoCAgIDgACEMIAQoAighByAEKAIsIQYLIAAgBiAHEGYgACABEAwLIARBMGokACAMC8QDAgZ+BX8jAEEQayINJAACQCABQoCAgIBwVA0AIAGnIgwvAQZBAkYEQCAMLQAFQQhxDQELQQAhDAsgBUEASCEOIAVBAE4hDwNAAkAgBCAKVwRAQQAhBQwBCyAKQn+FIAR8IAogDhsiBiADfCEIIAIgBnwhCQJAAkAgDEUNACAMLQAFQQhxRSAIQgBTcg0AIAkgDDUCKCIHWiAHIAhYcg0AIAQgCn0hCyAPRQRAQgAhBiALIAhCAXwQvQIgCUIBfBC9AiIHQgAgB0IAVRshCwNAIAYgC1ENAyAAIAwoAiQiBSAJIAZ9p0EDdGogBSAIIAZ9p0EDdGopAwAQDxAfIAZCAXwhBgwACwALQgAhBiALIAcgCH0QvQIgByAJfRC9AiIHQgAgB0IAVRshCwNAIAYgC1ENAiAAIAwoAiQiBSAGIAl8p0EDdGogBSAGIAh8p0EDdGopAwAQDxAfIAZCAXwhBgwACwALQX8hBSAAIAEgCCANQQhqEIwBIhBBAEgNASAQBEBCASEHIAAgASAJIA0pAwgQkQFBAE4NAQwCC0IBIQcgACABIAkQlAJBAEgNAQsgByAKfCEKDAELCyANQRBqJAAgBQt4AQJ/IAAoAhAhBSAAIAJBA3RBGGoQLyIERQRADwsgBCACNgIQIAQgATYCDCAEIAA2AghBACEAIAJBACACQQBKGyEBA0AgACABRwRAIAQgAEEDdCICaiACIANqKQMAEA83AxggAEEBaiEADAELCyAEIAVBoAFqEEwLZwIBfwF+IwBBEGsiAyQAAn4CQAJAIAJFDQAgACkCBCIEQv////8HgyABVw0AIARCgICAgAiDQgBSDQELIAFCAXwMAQsgAyABPgIMIAAgA0EMahDbARogAzQCDAshASADQRBqJAAgAQskACAAQQh0QYCA/AdxIABBGHRyIABBCHZBgP4DcSAAQRh2cnILCQAgACABOwAAC0sAIwBBEGsiAyQAIAMgATkDCCADIAI2AgAgAEGAAUHvxwAgAxBXIgBBgAFOBEBB1cgAQb7jAEGD2QBBofIAEAAACyADQRBqJAAgAAtwAQN/IwBBEGsiAiQAIAAhAQNAAkAgASwAACIDQQBOBEAgA0H/AXFBCWsiA0EXS0EBIAN0QZ+AgARxRXINASABQQFqIQEMAgsgAUEGIAJBDGoQYRDlAkUNACACKAIMIQEMAQsLIAJBEGokACABIABrC9UEAgl/AX4CfiABKQNAIgsQEgRAIwBBIGsiAiQAAkAgAEELEKQBIgsQDQ0AIAJCADcDGCACQgA3AxAgAkIANwMIIAAgAkEIaiABQQAQogUhBCAAIAIoAggQGgJAIAQEQCACKAIUIQYMAQsgC6chByACKAIcIghBACAIQQBKGyEJIAIoAhQhBkEAIQQCQANAIAQgCUcEQAJAAkACQCAGIARBDGxqIgMoAggiBQRAIAIgATYCAAwBCwJAIAAgAiACQQRqIAEgAygCABDsAyIFDgQABgYCBgsgAigCBCEFCyAFKAIMQf0ARgRAIANBAjYCBCADIAIoAgAoAhAgBSgCAEEDdGooAgQ2AggMAgsgA0EBNgIEIAUoAgQiCgRAIAMgCjYCCAwCCyADIAIoAgAoAkgoAiQgBSgCAEECdGooAgA2AggMAQsgA0EANgIECyAEQQFqIQQMAQsLIAYgCEEMQS8gABCuAkEAIQQDQCAEIAlHBEACQAJAAkAgBiAEQQxsaiIDKAIEQQFrDgIAAQILIAMoAgghBSAAIAcgAygCAEEmEIMBIgNFDQUgBSAFKAIAQQFqNgIAIAMgBTYCAAwBCyAAIAsgAygCAEEBIAMoAghBBhCUA0EASA0ECyAEQQFqIQQMAQsLIAAgBhAaIAAgC0HJASAAQf4AEDJBABAbGiAHIActAAVB/gFxOgAFDAILIAAgBSABIAMoAgAQ6wMLIAAgBhAaIAAgCxAMQoCAgIDgACELCyACQSBqJABCgICAgOAAIAsQDQ0BGiABIAs3A0ALIAsQDwsLIwAgACgCACAAKAIEEBogAEEANgIMIABCADcCBCAAQX82AhQLeQECfyAAIAFBEGoQwQUCQCABKAIgIgIEQCABKAI8IgNFDQEDQCACIANPRQRAIAAgAikDABAnIAJBCGohAiABKAI8IQMMAQsLIAAgASgCIBAhCyAAIAEpAxgQJyAAIAEpAwAQJw8LQdvqAEG+4wBBiZQBQZbTABAAAAsNACAAIAEgAkETEPQDC+kDAQN/IAFBEGohAyABKAIUIQIDQCACIANGRQRAIAJBGGshBCACKAIEIQIgACAEEI0DDAELCyAAKAIQIAEoAoACIAEoAoQCIAEoAqACEKMFIAFBgAJqEJcBIAAgASgCzAIQGiAAIAEoAqQCEBogACABKALYAhAaQQAhAgNAIAEoArQCIQMgAiABKAK4Ak5FBEAgACADIAJBA3RqKQMAEAwgAkEBaiECDAELCyAAIAMQGiAAIAEoAnAQE0EAIQIDQCABKAJ0IQMgAiABKAJ8TkUEQCAAIAMgAkEEdGooAgAQEyACQQFqIQIMAQsLIAAgAxAaQQAhAgNAIAEoAoABIQMgAiABKAKIAU5FBEAgACADIAJBBHRqKAIAEBMgAkEBaiECDAELCyAAIAMQGkEAIQIDQCABKAL8ASEDIAIgASgC9AFORQRAIAAgAyACQQR0aigCDBATIAJBAWohAgwBCwsgACADEBpBACECA0AgASgCyAIhAyACIAEoAsACTkUEQCAAIAMgAkEDdGooAgQQEyACQQFqIQIMAQsLIAAgAxAaIAEoAswBIgIgAUHQAWpHBEAgACACEBoLIAAgASgC7AIQEyABQfQCahCXASAAIAEoAowDEBogASgCBARAIAFBGGoQRgsgACABEBoLEQAgACABIAIgAyAEQQIQjAQLjAEBAn8CQANAIAFCgICAgHBUDQECQAJAAkACQAJAAkAgAaciAi8BBiIDQQxrDgUFAQMHAQALIANBKUYNASADQS1rDgUABgYGAAYLIAIoAiAoAjAPCyACKAIgIgJFDQQgAi0AEUUNASAAEMsCQQAPCyACKAIgIQILIAIpAwAhAQwBCwsgAigCICEACyAACw8AIAAgAUKAgICAMBDDAgthAQN+IAAQUSIEEA1FBEAgAUEAIAFBAEobrSEFA0AgAyAFUQRAIAQPCyAAIAQgAyACIAOnQQN0aikDABAPQQAQrgEhASADQgF8IQMgAUEATg0ACyAAIAQQDAtCgICAgOAAC40GAQZ/IwBBMGsiByQAIAcgAzYCLAJ/AkAgACgCACAHQRBqQSAQQg0AIAFB4ABHIQoCQAJAA0AgAyAAKAI8IgtPDQECQCADLQAAIgZBH0sNACAAKAJARQRAQYnEACEGIAINBAwFCyAKRQRAIAZBDUcNAUEKIQYgA0EBaiADIAMtAAFBCkYbIQMMAQsgBkEKaw4EAgAAAgALIAcgA0EBaiIJNgIsAkACQAJAAkACQAJAIAEgBkcEQCAGQdwARg0BIAZBJEcNAkEkIQYgCg0FIAktAABB+wBHDQUgByADQQJqNgIsQSQhAQsgBEGBfzYCACAEIAE2AhggBCAHQRBqEDk3AxAgBSAHKAIsNgIAQQAMCgtBASEGAkACQAJAAkAgCS0AACIIQQprDgQCAwMBAAsgCEHcAEYgCEEiRnIgCEEnRnINBCAIDQIgCSALTw0JIAcgA0ECajYCLEEAIQYMBgtBAkEBIAMtAAJBCkYbIQYLIAcgAyAGakEBaiIDNgIsIAFB4ABGDQYgACAAKAIIQQFqNgIIDAYLAkACQAJAIAhBMGtB/wFxQQlNBEAgACgCQCIGRQ0CIAFB4ABHBEAgBi0AbkEBcUUNAgsCQCAIQTBHDQAgAy0AAkEwa0H/AXFBCkkNACAHIANBAmo2AixBACEGDAgLIAFB4ABGIAhBN0tyDQJBmdQAIQYgAg0LDAwLIAhBGHRBGHVBAE4NACAJQQYgB0EMahBhIgZBgIDEAE8NByAHIAcoAgwiAzYCLCAGQX5xQajAAEYNCAwGCyAHQSxqQQEQgwIiBkF/Rw0BC0GVPyEGIAINCAwJCyAGQQBODQMgByAHKAIsQQFqNgIsDAILIAZBGHRBGHVBAE4NAiADQQYgB0EMahBhIgZB///DAEsNAyAHIAcoAgw2AiwMAgsgByADQQJqNgIsCyAIIQYLIAdBEGogBhDAAQ0EIAcoAiwhAwwBCwtBiNgAIQYgAg0BDAILQePDACEGIAJFDQELIAAgBkEAEBULIAdBEGoQREF/CyEGIAdBMGokACAGC2oBAn4CQAJAIAAQPCIDEA0EQCADIQQMAQtCgICAgOAAIQQgACADQcAAIAFBBxAbQQBIBEAgAyEBDAELIAMhASAAIANB6QAgAkEAR61CgICAgBCEQQcQG0EATg0BCyAAIAEQDCAEIQMLIAMLwAEBA38CQCABQoCAgIBwWgR/IAGnIggoAhAiByAHKAIYIAJxQX9zQQJ0aigCACEGIAcQKiEHAkADQCAGRQ0BIAIgByAGQQFrQQN0aiIGKAIERwRAIAYoAgBB////H3EhBgwBCwsQAQALIAAgCCACIAVBB3FBMHIQgwEiAkUEQEF/DwsgAiAAEKACIgA2AgAgAEEDcQ0BIAIgBDYCBCACIAAgA3I2AgBBAQVBAAsPC0GH9QBBvuMAQd7IAEG8ChAAAAswAQF/IwBB0ABrIgMkACADIAAgA0EQaiABEIkBNgIAIAAgAiADENMCIANB0ABqJAAL7AICAn8CfiMAQRBrIgMkACABQQhrIgQpAwAhBQJ/AkAgACABQRBrIgEpAwBBARDDASIGEA0EQCAAIAUQDAwBCyAAIAVBARDDASIFEA0EQCAAIAYQDAwBCyABAn8gBkKAgICAcINCgICAgJB/UiAFQoCAgIBwg0KAgICAkH9SckUEQCAGpyAFpxCVAiEEIAAgBhAMIAAgBRAMAkACQAJAAkAgAkGjAWsOAwABAgMLIARBH3YMBAsgBEEATAwDCyAEQQBKDAILIARBAE4MAQsgACADQQhqIAYQWwRAIAAgBRAMDAILIAAgAyAFEFsNAQJAAkACQAJAIAJBowFrDgMDAAECCyADKwMIIAMrAwBlDAMLIAMrAwggAysDAGQMAgsgAysDCCADKwMAZgwBCyADKwMIIAMrAwBjC61CgICAgBCENwMAQQAMAQsgAUKAgICAMDcDACAEQoCAgIAwNwMAQX8LIQAgA0EQaiQAIAALUwICfgJ/QX8hBQJAIAAgAUEIayIGKQMAIgQgAhD2ASIDEA0NACAAIAQQDCAGIAM3AwAgACADQeoAIANBABAUIgMQDQ0AIAEgAzcDAEEAIQULIAULLgEBfwNAIAIgA0ZFBEAgACABIANBA3RqKQMAEAwgA0EBaiEDDAELCyAAIAEQGgtlAQJ/IwBBEGsiBSQAAkAgAhCeAUUEQCACEA8hAgwBCyAAIAVBDGogAhCQAiIGRQRAQoCAgIDgACECDAELIAAgASAGIAUoAgxBmO8AIAMgBBC3BSECIAAgBhA3CyAFQRBqJAAgAgu8AQIDfgF/IwBBEGsiAiQAQoCAgIDgACEFAkAgACABEGkNACADKQMAIQYCQAJAIAMpAwgiB0IgiKciA0EDRwRAIARBAkYNAiADQQJGDQEMAgsgBEECRg0BCyAAIAEgBkEAQQAQJCEFDAELIAAgAkEMaiAHEIsEIgNFDQAgAigCDCEIAn4gBEEBcQRAIAAgASAGIAggAxCOAwwBCyAAIAEgBiAIIAMQJAshBSAAIAMgCBCYAwsgAkEQaiQAIAULDQAgACABEA8gAhDDAQscACAAIAAoAhAoAkQgAUEYbGooAgRBlN4AEMgBC2QBAn8jAEEwayICJAACfyABQv////8HWARAIAGnEJUBDAELIAIgATcDACACQRBqIgNBGEGT3AAgAhBXGkEAIAAgAxB2IgEQDQ0AGiAAKAIQIAGnQQEQ1wILIQAgAkEwaiQAIAALPAEBfyABIAAoAtQBIAEoAhQgACgCyAEQ1AJBAnRqIgIoAgA2AiggAiABNgIAIAAgACgC0AFBAWo2AtABC0MAAn9BACACKAIAKAIAQRp2IANGDQAaQX8gACABIAIQ5AENABogAigCACIAIAAoAgBB////H3EgA0EadHI2AgBBAAsLqwEBBH9BfyECAkAgACABQQAQ5AENACABKAIoIgQgASgCECIDKAIgaiIFIAMoAhxLBEAgACABQRBqIAEgBRDRBQ0BCyABKAIkIQNBACECA0AgAiAERkUEQCAAIAEgAhCVAUEHEIMBIAMpAwA3AwAgAkEBaiECIANBCGohAwwBCwsgACABKAIkEBpBACECIAFBADYCKCABQgA3AyAgASABLQAFQfcBcToABQsgAgt5AQN/AkACQCAAQQFxIgINACABQYECcUGBAkYgAUGACHFBACAAIAFzQQRxG3INASACIAFBgPQAcUVyDQAgAEEwcSICQRBGIAFBgDBxIgRBAEdzDQEgAEECcSABQYIEcUGCBEdyIAJBEEZyDQAgBEUNAQtBASEDCyADC5MBAQF/IwBBEGsiBSQAIAUgAzcDCAJAIAEEQCAAIAGtQoCAgIBwhBAPIAJBASAFQQhqEDYhAiAAIAUpAwgQDEF/IQEgAhANDQEgACACEAxBASEBDAELIAAgAxAMIARBgIABcUUEQEEAIQEgBEGAgAJxRQ0BIAAQ+wFFDQELIABB2wlBABAWQX8hAQsgBUEQaiQAIAELIgAgACACQQFqEC8iAARAIAAgASACECUgAmpBADoAAAsgAAtgAgF/AX4CQCABEF4NAAJAAkACQCAAKAIQKAI4IAFBAnRqKAIAKQIEIgNCPoinQQFrDgMDAgABC0EBIQICQCADQiCIp0H/////A3EOAgMAAQtBAg8LEAEAC0EBIQILIAILKAEBfgJ/QQAgACABENcFIgIQEg0AGkF/IAIQDQ0AGiAAIAIQDEEBCwtOAgF/AX4jAEEQayICJAACfiABQf8BTQRAIAIgAToADyAAIAJBD2pBARDYAgwBCyACIAE7AQwgACACQQxqQQEQnAQLIQMgAkEQaiQAIAML4gEBBH8gABANBH9BtLMEKAIAEJMBIQBBtLMEKAIAIABBxtAAEOQDIQJBtLMEKAIAIQMCQCACRQRAIAMgABAMDAELIAMgAEG2wAAQ5AMhA0G0swQoAgAhBCADRQRAIAQgAhA3QbSzBCgCACAAEAwMAQsgBCAAQY7TABDkAyEEQbSzBCgCACEFIARFBEAgBSACEDdBtLMEKAIAIAMQN0G0swQoAgAgABAMDAELIAUgABAMIAIgBCADIAEQC0G0swQoAgAgAhA3QbSzBCgCACADEDdBtLMEKAIAIAQQNwtBAQVBAAsLKQECfwJAIABCgICAgHBUDQAgAKciAi8BBhD4AUUNACACKAIgIQELIAELIQAgACABQTAgA61BARAbGiAAIAFBNiAAIAIQMkEBEBsaC08BAX8gASACNgIMIAEgADYCACABQQA2AhQgASADNgIQIAFBADYCCCABIAAgAiADEP0BIgA2AgQgAAR/QQAFIAFBfzYCFCABQQA2AgxBfwsLNwAgACABIAIgAwJ/QQAgACgCECIALQCIAQ0AGkEBIAAoAowBIgBFDQAaIAApAwgQqANFCxDbBQv8AQIFfwF+IAEoAgwhAgJAAkACQCABKQIEIgdCgICAgICAgIBAWgRAIAAoAjghBAwBCwJAIAEgACgCOCIEIAAoAjQgB0IgiKcgACgCJEEBa3FBAnRqIgMoAgAiBUECdGooAgAiBkYEQCADIAI2AgAMAQsDQCAGIQMgBUUNAyAEIAMoAgwiBUECdGooAgAiBiABRw0ACyADIAI2AgwLIAUhAgsgBCACQQJ0aiAAKAI8EOMFNgIAIAAgAjYCPCAAIAEQISAAIAAoAigiAEEBazYCKCAAQQBMDQEPC0HV9QBBvuMAQdgWQcAbEAAAC0Hk8wBBvuMAQewWQcAbEAAAC40CAgR/AX4CQAJAIAIEQCABLAAAEEUNAQsCfyAAKAIQIQQgASACQQEQ6AUiA0H/////A3EhBiAEKAI0IAQoAiRBAWsgA3FBAnRqIQMDQAJAAkAgAygCACIFRQ0AIAQoAjggBUECdGooAgAiAykCBCIHQiCIp0H/////A3EgBkcgB0KAgICAgICAgECDQoCAgICAgICAwABSciAHp0H/////B3EgAkcgB0KAgICACINCAFJycg0BIANBEGogASACEHcNASAFEPIBDQAgAyADKAIAQQFqNgIACyAFDAILIANBDGohAwwACwALIgMNAQtBACEDIAAgASACEP4BIgcQDQ0AIAAgB6cQpQQhAwsgAwvHAgEDfyAAIAAoAgAiAUEBayICNgIAAkAgAUEBSg0AIAJFBEAgACgCECECQQAhASAAQQAQpgQgACAAKQPAARAMIAAgACkDyAEQDCAAIAApA7ABEAwgACAAKQO4ARAMIAAgACkDqAEQDANAIAFBCEYEQEEAIQEDQCAAKAIoIQMgAigCQCABSgRAIAAgAyABQQN0aikDABAMIAFBAWohAQwBCwsgAiADECEgACAAKQOYARAMIAAgACkDoAEQDCAAIAApA1AQDCAAIAApA0AQDCAAIAApA0gQDCAAIAApAzgQDCAAIAApAzAQDCAAKAIQIQEgACgCJCICBEAgASACEJ4CCyAAQRRqEEYgABCfAiAAKAIQIAAQIQwDBSAAIAAgAUEDdGopA1gQDCABQQFqIQEMAQsACwALQcX0AEG+4wBB6BFBxBMQAAALCyYBAX8jAEEQayIEJAAgBCACNgIMIAAgAyABIAIQqwMgBEEQaiQAC6MCAQN/An8CQCABQf8BcSIDBEAgAEEDcQRAA0AgAC0AACICRSACIAFB/wFxRnINAyAAQQFqIgBBA3ENAAsLAkAgACgCACICQX9zIAJBgYKECGtxQYCBgoR4cQ0AIAIgA0GBgoQIbCIDcyIEQX9zIARBgYKECGtxQYCBgoR4cQ0AA0AgACgCBCECIABBBGohACACQYGChAhrIAJBf3NxQYCBgoR4cQ0BIAIgA3MiBEF/cyAEQYGChAhrcUGAgYKEeHFFDQALCyACQf8BcSICRSACIAFB/wFxRnINAQNAAkAgAEEBaiECIAAtAAEiA0UNACACIQAgAyABQf8BcUcNAQsLIAIMAgsgABBDIABqDAELIAALIgBBACAALQAAIAFB/wFxRhsLrAEDAXwBfgF/IAC9IgJCNIinQf8PcSIDQbIITQR8IANB/QdNBEAgAEQAAAAAAAAAAKIPCwJ8IAAgAJogAkIAWRsiAEQAAAAAAAAwQ6BEAAAAAAAAMMOgIAChIgFEAAAAAAAA4D9kBEAgACABoEQAAAAAAADwv6AMAQsgACABoCIAIAFEAAAAAAAA4L9lRQ0AGiAARAAAAAAAAPA/oAsiACAAmiACQgBZGwUgAAsLKgEBfyAAQoCAgIBwWgRAIACnIgIgAi0ABUHvAXEgAUEEdEEQcXI6AAULC9QDAwJ/BHwBfiAAvSIHQiCIpyEBAkACfAJ8AkAgAUH5hOr+A0sgB0IAWXFFBEAgAUGAgMD/e08EQEQAAAAAAADw/yAARAAAAAAAAPC/YQ0EGiAAIAChRAAAAAAAAAAAow8LIAFBAXRBgICAygdJDQQgAUHF/cr+e08NAUQAAAAAAAAAAAwCCyABQf//v/8HSw0DCyAARAAAAAAAAPA/oCIDvSIHQiCIp0HiviVqIgFBFHZB/wdrIQIgACADoUQAAAAAAADwP6AgACADRAAAAAAAAPC/oKEgAUH//7+ABEsbIAOjRAAAAAAAAAAAIAFB//+/mgRNGyEFIAdC/////w+DIAFB//8/cUGewZr/A2qtQiCGhL9EAAAAAAAA8L+gIQAgArcLIgNEAADg/kIu5j+iIAAgACAARAAAAAAAAABAoKMiBCAAIABEAAAAAAAA4D+ioiIGIAQgBKIiBCAEoiIAIAAgAESfxnjQCZrDP6JEr3iOHcVxzD+gokQE+peZmZnZP6CiIAQgACAAIABERFI+3xLxwj+iRN4Dy5ZkRsc/oKJEWZMilCRJ0j+gokSTVVVVVVXlP6CioKCiIANEdjx5Ne856j2iIAWgoCAGoaCgCw8LIAAL8AEBA38gAEUEQEGgswQoAgAEQEGgswQoAgAQtAMhAQtB2LMEKAIABEBB2LMEKAIAELQDIAFyIQELQZi0BCgCACIABEADQCAAKAJMGiAAKAIUIAAoAhxHBEAgABC0AyABciEBCyAAKAI4IgANAAsLIAEPCyAAKAJMQQBOIQICQAJAIAAoAhQgACgCHEYNACAAQQBBACAAKAIkEQEAGiAAKAIUDQBBfyEBDAELIAAoAgQiASAAKAIIIgNHBEAgACABIANrrEEBIAAoAigRDwAaC0EAIQEgAEEANgIcIABCADcDECAAQgA3AgQgAkUNAAsgAQtpAQR/IAEQQyEDA0ACQCAALQAARQRAQX8hAgwBCwNAAn8gAEEsELADIgRFBEAgABBDDAELIAQgAGsLIgUgA0YEQCAAIAEgAxB3RQ0CCyAAIAVqQQFqIQAgBA0ACyACQQFqIQIMAQsLIAILYAEBfyMAQSBrIgMkACADIAAoAhA2AhggAyAAKQIINwMQIAMgACkCADcDCCAAQQA2AgggAEIANwIAIAAgAygCECADKAIIIAEgAkEAEKoCIQAgA0EIahBSIANBIGokACAAC5AFAQd/AkACQCABQf8ATQRAIAJFDQEgAUEgaiABIAFBwQBrQRpJGyEBDAILIAJBAEchCEHxAiEFA0AgAyAFSg0CIAEgAyAFakEBdiIGQQJ0QcDhAWooAgAiB0EPdiIESQRAIAZBAWshBQwBCyABIAdBCHZB/wBxIARqTwRAIAZBAWohAwwBCwsgB0EIdEGAHnEiCSAGQZDtAWotAAAiBXIhAwJAAkACQAJAAkACQAJAAkACQCAHQQR2IgdBD3EiBg4NAAAAAAECAwQFBgYHBwgLIAJBAkcgBkECSXIgAiAHQQFxR3ENCSABIARrIANBAnRBwOEBaigCAEEPdmohAQwJCyABIARrIgNBAXEgAkEAR0YNCCADQQFzIARqIQEMCAsgASAEayIEQQFGBEBBAUF/IAIbIAFqIQEMCAsgBCACRUEBdEcNB0ECQX4gAhsgAWohAQwHCyABIARrIQEgAkUEQCAAQZkHNgIEIAAgASADQQV2Qf4AcUGQ8AFqLwEAajYCAEECDwsgASAFQT9xQQF0QZDwAWovAQBqIQEMBgsgAkEBRg0FIAMgAkECRkEFdGohAQwFCyACQQFGDQQgA0EBdEGQ8AFqLwEAIAJBAkZqIQEMBAsgBkEJayAIRw0DIANBAXRBkPABai8BACEBDAMLIAZBC2sgAkcNAiAAIAVBP3FBAXRBkPABai8BADYCBCAAIANBBXZB/gBxQZDwAWovAQAgASAEa2o2AgBBAg8LIAINASAAIAlBB3ZBkPABai8BADYCACAAIAVBD3FBAXRBkPABai8BADYCCCAAIAVBA3ZBHnFBkPABai8BADYCBEEDDwsgAUEgayABIAFB4QBrQRpJGyEBCyAAIAE2AgBBAQsXACAAIAFB/wFxEBAgACACQf//A3EQMQunGAESfyMAQRBrIggkACAIIAIoAgAiBDYCDAJAAkACQAJAAkACQAJAAkAgBC0AACIHBEAgB0HcAEcNBSAEQQFqIgUgACgCHE8NASAIIARBAmo2AgwCQAJAAkACQAJAAkACQAJAAkACQCAELQABIgdB0wBrDgUEAQEBBgALAkAgB0HjAGsOAggHAAsCQCAHQfMAaw4FAwEBAQUACyAHQcQARg0BIAdB0ABGIAdB8ABGcg0ICyAAKAIoQQF0IQQMCwtBASEGDAQLQQIhBgwDC0EDIQYMAgtBBCEGDAELQQUhBgtBfyEHIAZBAXRBfHFB4OABaigCACIDLwEAIQQgASAAKAJAQewCEIgBIAZBAXEhACADQQJqIQMgBEEBdCEGQQAhBAJAAkADQCAEIAZHBEAgBEEBdCEFIARBAWohBCABIAMgBWovAQAQvgRFDQEMAgsLQQAhBCAARQ0BIAEQqQJFDQELIAEQUkF/IQQLIAQNCgwECwJAIAQtAAIiAUHfAXFBwQBrQf8BcUEaTwRAIAAoAighByADRSABQd8ARiABQTBrQf8BcUEKSXJFcg0BIAcNBwsgCCAEQQNqNgIMIAFBH3EhBwwJCyAHDQUgCCAFNgIMQdwAIQcMCAsgACgCKEUEQEEAIQQMBAsgB0HQAEYhEkF/IQcgACEKIAEhAyMAQYABayIGJAACfwJAAkAgCCgCDCIALQAAQfsARgRAIAZBQGshBAJAAkADQAJAIABBAWohASAALQABIgUQ4wJFDQAgBCAGQUBra0E+Sw0CIAQgBToAACAEQQFqIQQgASEADAELCyAEQQA6AAAgBiEEAkAgAS0AACIFQT1HDQAgAEECaiEBA0AgAS0AACIFEOMCRQ0BIAQgBmtBP08EQCAKQZLJAEEAED8MBwUgBCAFOgAAIARBAWohBCABQQFqIQEMAQsACwALIARBADoAACAFQf0ARwRAIApB3/kAQQAQPwwFC0EAIQQCQAJAIAZBQGtB3hVBBxB3RQ0AIAZBQGtBn+MAQQMQd0UNAEEBIQQgBkFAa0GQI0ESEHdFDQAgBigCQEHzxuEDRw0BCyADIAooAkBB7AIQiAECfyAEIQ9BACEFIwBBMGsiCSQAAkACQEGAiAIgBhC1AyINQQBIBEBBfiEQDAELIAMhDCAPBEAgCUEYaiIMIAMoAgwgAygCEBCIASAJIAMoAgwgAygCEBCIAQsgDUEBaiERQbCaAiEAA0AgAEGyrwJJBEAgBSELIAAtAAAiBEEYdEEYdSEOAn8gAEEBaiAEQf8AcSIFQeAASQ0AGiAFQe8ATQRAIAAtAAEgBUEIdHJBoL8BayEFIABBAmoMAQsgAC0AAiAFQRB0ciAALQABQQh0ckGg378DayEFIABBA2oLIQQgDkEATgRAIAUgC2pBAWohBSAEIQAMAgsgBEEBaiEAIAUgC2pBAWohBSARIAQtAABHDQEgDCALIAUQf0UNAQwDCwsgD0UNAEHArwIhACANQTdGIRMgDUEYRyEUQQAhBANAIABB/LUCSQRAIAQhBSAALAAAIgtB/wFxIQQCfyAAQQFqIAtBAE4NABogC0G/f00EQCAALQABIARBCHRyQYD/AWshBCAAQQJqDAELIAAtAAIgBEEQdHIgAC0AAUEIdHJBgP/+BWshBCAAQQNqCyIAQQFqIQ4gBCAFakEBaiEEIAAtAAAhCwJAAkAgE0UEQEEAIQAgFA0BCyALRQ0BIAkgBSAEEH9FDQEMBQsDQCAAIAtGDQEgACAOaiEVIABBAWohACARIBUtAABHDQALIAkgBSAEEH8NBAsgCyAOaiEADAELCwJAIA1BN0cgDUEYR3FFBEAgCRCpAg0DIAMgDCgCCCAMKAIAIAkoAgggCSgCAEEBEKoCRQ0BDAMLIAMgDCgCCCAMKAIAIAkoAgggCSgCAEEAEKoCDQILIAwQUiAJEFILIAlBMGokACAQDAELA0AgD0UNACAMEFIgCRBSDAALAAsiAEUNAiADEFIgAEF+Rw0EIApBxxVBABA/DAULAkAgBkFAa0GJDEEREHcEQCAGQUBrQbbjAEEDEHcNAQsgAyAKKAJAQewCEIgBIAMgBhC3BCIARQ0CIAMQUiAAQX5HDQQgCkHoC0EAED8MBQsgBi0AAA0AIAMgCigCQEHsAhCIASADIAZBQGsQtwQiAEF/RgRAIAMQUgwECyAAQQBODQEjAEGgBGsiACQAQX4hBAJAQcC7AiAGQUBrELUDIgVBAEgNAAJ/AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAFQSJrDhMABwECBhAODREPDAgJEgQDBQsKEwtBfyEEQQAgA0EAQYABEH9FDRMaDBQLQX8hBEEAIANBAEGAgMQAEH9FDRIaDBMLIABChoCAgPAANwMIIABCgICAgBA3AwAgAyAAEH4MEQsgAEKDgICA8AA3AyAgAEKBgICAEDcDGCAAQoCAgICAgAQ3AxAgAyAAQRBqEH4MEAsgAEFAa0KDgICA8AA3AwAgAEKBgICAMDcDOCAAQoCAgIDAADcDMCADIABBMGoQfgwPCyAAQoOAgIDwADcDYCAAQoGAgIDAADcDWCAAQoCAgIAgNwNQIAMgAEHQAGoQfgwOCyAAQQc2ApABIABCg4CAgDA3A4gBIABCg4CAgBA3A4ABIABCgYCAgMAANwN4IABCgICAgOABNwNwIAMgAEHwAGoQfgwNCyAAQoOAgIDwADcDyAEgAEKBgICAIDcDwAEgAEKDgICAMDcDuAEgAEKDgICAEDcDsAEgAEKBgICAwAA3A6gBIABCgICAgOCHATcDoAEgAyAAQaABahB+DAwLIABBBzYC6AEgAEKDgICA4AA3A+ABIABCgYCAgNAANwPYASAAQoCAgICQqICAPzcD0AEgAyAAQdABahB+DAsLIABCg4CAgPAANwOAAiAAQoGAgIDQADcD+AEgAEKAgICAgCg3A/ABIAMgAEHwAWoQfgwKCyAAQoSAgIDwADcDyAIgAEKDgICA4AA3A8ACIABCgYCAgLABNwO4AiAAQp6AgIAwNwOwAiAAQp2AgIAQNwOoAiAAQoOAgIAQNwOgAiAAQoGAgIDwADcDmAIgAEKAgICA4IcBNwOQAiADIABBkAJqEH4MCQsgAEEHNgKYAyAAQoaAgIDAADcDkAMgAEKMgICAMDcDiAMgAEKDgICAEDcDgAMgAEKBgICA4AM3A/gCIABCgYCAgNADNwPwAiAAQoiAgIAwNwPoAiAAQoOAgIAQNwPgAiAAQoGAgIDwADcD2AIgAEKAgICA4N/BADcD0AIgAyAAQdACahB+DAgLIANBARDfAgwHCyADQQIQ3wIMBgsgA0EHEN8CDAULIABChYCAgPAANwOwAyAAQoGAgIDQATcDqAMgAEKCgICAEDcDoAMgAyAAQaADahB+DAQLIABChYCAgPAANwPQAyAAQoGAgIDgATcDyAMgAEKCgICAwAA3A8ADIAMgAEHAA2oQfgwDCyAAQoWAgIDwADcD8AMgAEKBgICA8AE3A+gDIABCgoCAgMAANwPgAyADIABB4ANqEH4MAgsgAEKFgICA8AA3A5AEIABCgYCAgKABNwOIBCAAQoGAgICABjcDgAQgAyAAQYAEahB+DAELIAVBIUsNASADIAVBEGoQtQQLIQQLIABBoARqJAAgBCIARQ0BIAMQUiAAQX5HDQMLIApB2s0AQQAQPwwDCwJAIBJFDQAgAxCpAkUNACADEFIMAwsgCCABQQFqNgIMQQAMAwsgCkHHNEEAED8MAQsgChCsAgtBfwshACAGQYABaiQAIABFDQIMCAtBACEHIAQgACgCHEkNBQsgAEGU2wBBABA/QX8hBwwGC0GAgICABCEHDAQLIAggBTYCDCAIQQxqIAQQgwIiAUEATgRAIAEhBwwECwJAIAFBfkcNACAIKAIMLQAAIgFFDQBB5vUAIAFBEBClAg0CCyAAKAIoRQ0BCyAAQaI4QQAQP0F/IQcMAwsgCCgCDCEEIAdBGHRBGHVBAE4NACAEQQYgCEEMahBhIgdBgIAESQ0BIAAoAigNASAAQbwyQQAQP0F/IQcMAgsgCCAEQQFqNgIMCyACIAgoAgw2AgALIAhBEGokACAHCx8BAX8gACgCPCIBQQBIBH8gABDBBBogACgCPAUgAQsLpQIBBH8jAEEQayIEJAAgBCABKAIAIgU2AgwgAkEBdCEGIAAhAwJ/A0ACQAJAAkACfwJAAkAgBS0AACICQdwARwRAIAJBPkcNASAAIANGDQYgA0EAOgAAIAEgBCgCDEEBajYCAEEADAgLIAQgBUEBajYCDCAFLQABQfUARg0BDAULIAJBGHRBGHVBAE4NAiAFQQYgBEEMahBhDAELIARBDGogBhCDAgsiAkH//8MASw0CDAELIAQgBUEBajYCDAsCQCAAIANGBEAgAhDFAkUNAgwBCyACEMEBRQ0BCyADIABrQfkASg0AAn8gAkH/AE0EQCADIAI6AAAgA0EBagwBCyADIAIQ5gIgA2oLIQMgBCgCDCEFDAELC0F/CyECIARBEGokACACCzEBAX9BASEBAkACQAJAIABBCmsOBAIBAQIACyAAQajAAEYNAQsgAEGpwABGIQELIAELqAIBA38CQAJAIAAoAjAiCUEBaiIKIAAoAiwiCE0EQCAAKAIoIQgMAQsgACgCICAAKAIoIAhBA2xBAXYiCEEIIAhBCEsbIgkgACgCJGwQhQQiCEUEQEF/IQgMAgsgACAINgIoIAAgCTYCLCAAKAIwIglBAWohCgsgACAKNgIwIAggACgCJCAJbGoiCCAHNgIEIAggBjoAACAIIAQ2AgwgCCAFNgIIIAggAzoAASAIQRBqIQQgACgCDEEBdCEFQQAhAANAIAAgBUZFBEAgBCAAQQJ0IgZqIAEgBmooAgA2AgAgAEEBaiEADAELCyAEIAVBAnRqIQFBACEIQQAhAANAIAAgA0YNASABIABBAnQiBGogAiAEaigCADYCACAAQQFqIQAMAAsACyAIC2sAAkACQAJAAkACQCAAIAFyQQ9xDg8ABAMEAgQDBAEEAwQCBAMEC0HiAkHjAiABQRBGGw8LQeQCQeUCIAFBCEYbDwtB5gJB5wIgAUEERhsPC0HoAkHpAiABQQJGGw8LQeoCQesCIAFBAUYbC1IBAn8CfyAAKAIEIgMgAmoiBCAAKAIISwR/QX8gACAEEM4BDQEaIAAoAgQFIAMLIAAoAgAiA2ogASADaiACECUaIAAgACgCBCACajYCBEEACxoLDAAgACgCECABEO0DC1sBAX8CQCABQiCIpyICQX9HBEAgAkF4Rw0BIAEQDw8LIAGnIgIvAQZBB0cNACACKQMgIgFCgICAgHCDQoCAgICAf1INACABEA8PCyAAQbY8QQAQFkKAgICA4AALUgEEfyAEQQAgBEEAShshCEEAIQQCQANAIAQgCEYNASADIARqIQUgAiAEaiEGIARBAWohBCAAIAYQTSIGIAEgBRBNIgVGDQALIAYgBWshBwsgBwtDAQJ/A0ACQCACQQBKBH8gACABEE0Q6wIiBEEATg0BQX8FIAMLDwsgAkEBayECIAFBAWohASAEIANBBHRyIQMMAAsACyYBAX8jAEEQayICJAAgAkEANgIMIABBBSABQQAQqwMgAkEQaiQAC3gBAn8jAEEQayIEJAACQCAAIAEgAiADELIBIgEQDQ0AAkAgACABEJgBIgVBAEgNACACQQFHDQEgACAEQQhqIAMpAwAQDxCwAQ0AIAQpAwggBa1XDQEgAEGQPkEAEBYLIAAgARAMQoCAgIDgACEBCyAEQRBqJAAgAQtCAQF/AkAgACABaiIALQABQT1HDQBBASECAkACQCAALQAAIgBBFmsOBAIBAQIACyAAQbEBRg0BCyAAQR1GIQILIAILaQAgAUEBakEITQRAIAAgAUHNAGtB/wFxEBAPCyABQYABakH/AU0EQCAAQbsBEBAgACABQf8BcRAQDwsgAUGAgAJqQf//A00EQCAAQbwBEBAgACABQf//A3EQMQ8LIABBARAQIAAgARAeC2kBBH8gACgCBCEFAkADQCABIAVODQECQAJAIAAoAgAgAWoiAy0AACIEQbQBRwRAIARBwAFGDQEgBEHrAEcNBCACIAMoAAFHDQQMAgsgAiADKAABRg0BCyABQQVqIQEMAQsLQQEhBgsgBguBAgEFfyAAIAFBfxB0GgJAA0AgBkEKRgRAQesAIQQMAgsCQCABQQBIDQAgASAAKAKsAk4NACAAKAKkAiABQRRsaigCCCEFIAAoAoACIQcDQAJAAkAgBSAHaiIILQAAIgRBtAFGDQAgBEHAAUcEQCAEQQ5HDQJBKSEEA0AgByAFQQFqIgVqLQAAIgNBDkYNAAsgA0EpRg0GQQ4hBAwGCyADRQ0AIAMgCCgAATYCAAsgBSAEQQJ0QbCaAWotAABqIQUMAQsLIARB6wBHDQIgBkEBaiEGIAgoAAEhAQwBCwtB3xZBvuMAQf/zAUHXGhAAAAsgAiAENgIAIAAgAUEBEHQaIAELNgACQCAAIAFBCBBYIgBBAEgNACABKAJgRQ0AIAEoAnQgAEEEdGoiASABKAIMQQJyNgIMCyAAC6UBAQJ/IAEoAsACIgpBgIAETgRAIABB/SVBABBQQX8PC0F/IQkgACABQcgCakEIIAFBxAJqIApBAWoQgAEEf0F/BSABIAEoAsACIglBAWo2AsACIAEoAsgCIAlBA3RqIgkgBDsBAiAJIAdBA3RBCHEgBkECdEEEcSADQQF0QQJxIAJBAXFycnIgCEEEdHI6AAAgCSAAIAUQGTYCBCABKALAAkEBawsL1AEBA38CQAJAIAFBoX9GBEBBfyEDIABBCCACELMCRQ0BDAILQX8hAyAAQaF/IAIQzAMNAQtBACEDIAAoAhAgAUcNAEHpAEHqACABQaF/RhshBSACQXtxIQIgABA1IQQDQEF/IQMgABARDQEgAEEREA4gACAFIAQQHRogAEEOEA4CQCABQaF/RgRAIABBCCACELMCRQ0BDAMLIABBoX8gAhDMAw0CCyAAKAIQIgMgAUYNAAsgA0Gmf0YEQCAAQbcIQQAQFUF/DwsgACAEECBBACEDCyADC40BAQJ/AkACQCAAKAJAIgEQqAEiAkG/AUcEQCACQc0ARw0BIAEoApgCIQIgAUF/NgKYAiABIAI2AoQCIABBzgAQDg8LIAEoApgCIgAgACABKAKAAiICaigAAWsgAmoiAC0AAUHWAEcNASAAQdcAOgABIAFBfzYCmAILDwtBtCBBvuMAQe2wAUGs3QAQAAALWQEDfyAAKALMASACQQN0akEEaiEDA0ACQEF/IQQgAygCACIDQX9GDQAgACgCdCADQQR0aiIFKAIEIAJHDQAgAyEEIAUoAgAgAUYNACAFQQhqIQMMAQsLIAQLxSECCX8BfiMAQRBrIgckACABQQJxIgRBAXYhCUF+IQICQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgACgCECIDQYABag4HAgMSDQEBBQALAkAgA0HVAGoODAkLDAEBAQEKAQEBDwALAkAgA0E7ag4KBwEBCAEBAQEREAALIANBKEYNBSADQS9GDQMgA0HbAEYgA0H7AEZyDQ0LIAAoAjghAiAHIAAoAhgiATYCBCAHIAIgAWs2AgAgAEGq+gAgBxAVDBQLAkAgACkDICILQv////8PWARAIABBARAOIAAgC6cQOgwBCyAAIAtBABDTAUEASA0UC0F/IQEgABARDRQMEQtBfyEBIAAgACkDIEEBENMBDRMgABARRQ0QDBMLQX8hAgsgACAAKAI4IAJqNgI4IAAoAgAoAugBRQRAIABB790AQQAQFQwRC0F/IQEgABDwBA0RQQAhAiAAIAApAyBBABDTARogACgCACIEIAApAyAgACkDKCAEKALoAREWACILEA0EQCAAKAJAIgQEQCAEKAJoQQBHQQF0IQILIAAoAgAiBCAEKAIQKQOAASAAKAIMIAAoAhQgAhDHAgwSCyAAIAtBABDTASEEIAAoAgAgCxAMIAQNESAAQTMQDiAAEBFFDQ8MEQsCQCABQQRxRQ0AQQAhAiAAQQBBARCpAUGkf0cNAEF/IQEgAEEDQQAgACgCGCAAKAIUENgBRQ0PDBELQX8hASAAEIgCRQ0NDBALQX8hAUEAIQIgAEECQQAgACgCGCAAKAIUENgBRQ0NDA8LQX8hAUEAIQIgAEEBQQAQ+gJFDQwMDgtBfyEBIAAQEQ0NIABBBxAODAoLQX8hASAAEBENDCAAQbYBEA4gAEEIEBwMCAtBfyEBIAAQEQ0LIABBCRAODAgLQX8hASAAEBENCiAAQQoQDgwHCyAAKAIoBEAgABDwAQwJCwJAIAFBBHEiAkUNACAAQQEQiwFBpH9HDQBBfyEBQQAhAiAAQQNBACAAKAIYIAAoAhQQ2AFFDQgMCgsCQAJAIABBhQEQVEUNACAAQQEQiwFBCkYNACAAKAIUIQYgACgCGCEDQX8hASAAEBENCyAAKAIQIgRBRUYEQCAAQQJBAiADIAYQ2AFFDQkMDAsCQCACRQ0AAkAgBEEoRgR/IABBAEEBEKkBQaR/Rg0BIAAoAhAFIAQLQYN/Rw0BIAAoAigNASAAQQEQiwFBpH9HDQELIABBA0ECIAMgBhDYAUUNCQwMC0GFASECIAAoAgBBhQEQGRoMAQsCQCAAKAIgIgJBzQBHDQAgACgCQCgCXA0AIABB/yxBABAVDAoLQX8hASAAKAIAIAIQGSECIAAQEQ0KCyAAQbYBEA4gACACEDogACAAKAJALwG8ARAYDAYLIAAgB0EMakEAEKkBQT1GBEAgAEEAQQBBACAHKAIMQQJxQQEQ1QFBAE4NBgwICyAAKAIQQfsARgRAQQAhAyMAQRBrIgUkACAFQQA2AgwCQAJAIAAQEQ0AIABBCxAOAkADQCAAKAIQIgFB/QBGDQECQAJAIAFBpX9GBEBBfyEIIAAQEQ0GIAAQYg0GIABBBxAOIABB0wAQDiAAQQYQbiAAQQ4QDiAAQQ4QDgwBCyAAKAIUIQQgACgCGCECIAAgBUEMakEBQQFBABDSAyIGQQBIDQECQAJAIAZBAUYEQCAAQbYBEA4gACAFKAIMIggQHCAAIAAoAkAvAbwBEBgMAQsgACgCEEEoRgRAIAACfyAGQX5xIgFBAkYEQEEAIQggBkECagwBCyAGQQNrQQAgBkEEa0EDSRshCEEGCyAIIAIgBBDYAQ0EAkAgBSgCDCIIRQRAIABB1QAQDgwBCyAAQdQAEA4gACAIEBwLIAAgBkEBa0EEckEEIAFBAkYbQf8BcRBuDAILIABBOhAwDQMgABBiDQMCQCAFKAIMIghBxABHBEAgCA0BIAAQzQMgAEHRABAOIABBDhAOQQAhCAwDCyADBEAgAEGOzgBBABAVQcQAIQgMCAsgAEHPABAOQQEhA0HEACEIDAILIAAgCBCtAQsgAEHMABAOIAAgCBAcCyAAKAIAIAgQEwsgBUEANgIMIAAoAhBBLEcNAiAAEBFFDQELCyAFKAIMIQgMAQtBACEIIABB/QAQMEUNAQsgACgCACAIEBNBfyEICyAFQRBqJAAgCEUNBgwIC0EAIQJBfyEEAkAgABARDQACQANAIAAoAhAiAUHdAEYgAkEfS3IgAUGlf0ZyIAFBLEZyRQRAIAAQYg0DIAJBAWohAiAAKAIQIgFB3QBGDQEgAUEsRw0CIAAQEUUNAQwDCwsgAEEmEA4gACACQf//A3EQGEEAIQMDQCAAKAIQIQECQAJAAkACQCACQf7///8HTQRAIAFBLEYNAyABQaV/Rg0CIAFB3QBGDQEgABBiDQcgAEHMABAOIAAgAhCVARA6IAJBAWohAkEAIQMgACgCEEEsRw0FDAQLIAFB3QBHDQELIANFDQQgAEEREA4gAEEBEA4gACACEDogAEHDABAOIABBMBAcDAQLIABBARAOIAAgAhA6A0ACQAJAAkAgACgCECICQaV/RwRAQY8BIQEgAkEsRw0BQQEhAwwCCyAAEBENCEHSACEBIAAQYkUNAQwICyACQd0ARg0BIAAQYg0HIABB0QAQDkEAIQMLIAAgARAOIAAoAhBBLEcNACAAEBFFDQEMBgsLIAMEQCAAQRIQDiAAQcMAEA4gAEEwEBwMBAsgAEEOEA4MAwtBASEDIAJBAWohAgsgABARRQ0ACwwBCyAAQd0AEDAhBAsgBEUNBQwHC0F/IQEgABARDQcgACgCEEEuRgRAIAAQEQ0IIABB1gAQVEUEQCAAQegaQQAQFQwJCyAAKAJAKAJQRQRAIABBoiJBABAVDAkLIAAQEQ0IIABBtgEQDiAAQfEAEBwMBAsgAEEAEM8DDQdBASEJIAAoAhBBKEYEQEEBIQIMBgsgAEEREA4gAEEhEA4MAwtBfyEBIAAQEQ0GAkAgACgCECICQdsARiACQS5GckUEQCACQShHDQFBAiECIAAoAkAoAlQNBiAAQYkpQQAQFQwICyAAKAJAKAJYRQRAIABB4NkAQQAQFQwICyAAQbYBEA4gAEEIEBxBACECIABBABAYIABBtgEQDiAAQfMAEBwgAEEAEBggAEE0EA4MBQsgAEH7/ABBABAVDAYLQX8hASAAEBENBSAAKAIQQS5GBEAgABARDQYgAEH7ABBURQRAIABBqd8AQQAQFQwHCyAAKAJERQRAIABBtNYAQQAQFQwHCyAAEBENBiAAQQwQDiAAQQYQbgwDCyAAQSgQMA0FIARFBEAgAEGX/gBBABAVDAYLIAAQYg0FIABBKRAwDQUgAEE1EA5BACECQQEhCQwDC0F/IQFBACECIABBAEEAEO0EDQQMAgtBACECIABBABAYDAELQQAhAgsgB0F/NgIMA0AgACgCQCEDAkACQAJAAkACQAJAAkACfwJAIAAoAhAiAUGnf0ciBkUEQCAAEBENCyAAKAIQIgFBKEYEQEEBIQogCQ0CCyABQdsARw0FDAkLIAFBgn9HIAJyRQRAQQAhCiAHKAIMQQBIBEBBAyEEQQAMAwsgAEH1OUEAEBUMCwsgAUEoRw0DQQAhCiAJRQ0DCyAAEBENCUEAIQQgAgRAQQAhBSACIQQMAgtBAQshBkEBIQFBACEFAkACQAJAAkACQCADEKgBIgJBxwBrDgQBBAQDAAsgAkG8AUcEQCACQbYBRg0CIAJBwQBHDQQgAygCgAIgAygCmAJqQcIAOgAAQQIhAUHBACEFDAQLIAMoAoACIAMoApgCakG9AToAAEECIQFBvAEhBQwDCyADKAKAAiADKAKYAmpByAA6AABBAiEBQccAIQUMAgsgAygCgAIgAygCmAJqIggoAAEhAiAKRQRAQTEhBSAGIAJBOkZxDQMLIAMhAiAILwAFIQZBACEFA0ACQCACRQ0AIAIoAswBIAZBA3RqQQRqIQYDQCAGKAIAIgZBAE4EQCACKAJ0IAZBBHRqIgYoAgBB1ABGBEBBASEFDAMFIAZBCGohBgwCCwALCyACKAIMIQYgAigCBCECDAELCyAFRQRAQbYBIQUMAgtBugEhBSAIQboBOgAADAELQccAIQUgAygCgAIgAygCmAJqQccAOgAAQQIhAQsgCkUNACAAIAdBDGogARDyAgsCQCAEQQNGBEAgAEEBIAdBCGoQ7QQNCQwBCwJAIARBAkciBkUEQCAAQbYBEA4gAEHyABAcIABBABAYIABBNBAOIABBtgEQDiAAQfEAEBwgAEEAEBgMAQsgBEEBRw0AIABBERAOC0EAIQECQAJAA0AgACgCECICQSlGDQIgAUH//wNGBEAgB0H//wM2AgggAEG+H0EAEBUMDAsgAkGlf0YNASAAEGJFBEAgAUEBaiEBIAAoAhBBKUYNAyAAQSwQMEUNAQsLIAcgATYCCAwKCyAHIAE2AgggAEEmEA4gACABQf//A3EQGCAAQQEQDiAAIAEQOgNAAkACQCAAKAIQIgFBpX9HBEAgAUEpRg0CIAAQYg0NIABB0QAQDkGPASECDAELQX8hASAAEBENDUHSACECIAAQYg0NCyAAIAIQDiAAKAIQQSlGDQBBfyEBIABBLBAwRQ0BDAwLCyAAEBENCSAAQQ4QDgJAAkACQAJAIAVBugFrDgMBAwEACyAFQTFGDQEgBUHHAEYNACAFQcEARw0CCyAAQRgQDiAAQScQDiAAIARBAUYQGEEAIQIMCgsgAEEyEA4MBwsgBkUEQCAAQScQDiAAQQEQGAwGCyAEQQFGBEAgAEEYEA4gAEEnEA4gAEEBEBhBACECDAkLIABBBhAOIABBGxAOIABBJxAOQQAhAiAAQQAQGAwICyAHIAE2AgggABARDQgLAkACQAJAAkAgBUG6AWsOAwEDAQALIAVBMUYNASAFQccARg0AIAVBwQBHDQILIABBJBAOIAAgBy8BCBAYQQAhAgwICyAAQTEQDiAAIAcvAQgQGAwFCwJAAkACQCAEQQFrDgIBAAILIABBIRAOIAAgBy8BCBAYDAULIABBIRAOIAAgBy8BCBAYQQAhAgwHCyAAQSIQDiAAIAcvAQgQGEEAIQIMBgsgAUHbAEYNBCABQS5HDQEgABARDQYgACgCECEBCwJAIAFBqX9GBEAgAxCoAUE0RgRAIABBoy9BABAVDAgLIAZFBEAgACAHQQxqQQEQ8gILIABBvAEQDiAAIAAoAiAQHCAAIAAoAkAvAbwBEBgMAQsgARDXAUUEQCAAQYPQAEEAEBUMBwsgAxCoAUE0RgRAIAAgACgCACAAKAIgEGAiC0EBENMBIQEgACgCACALEAwgAQ0HIABBygAQDgwBCyAGRQRAIAAgB0EMakEBEPICCyAAQcEAEA4gACAAKAIgEBwLQX8hASAAEBFFDQQMBgtBACEBIAcoAgwiAkEASA0FIAAgAhAgDAULIABBERAOIABBuwEQDiAAQQgQHEEAIQIgAEEAEBggABD0BAwCCyAAIAMvAbwBEBggA0EBNgJEQQAhAgwBCyADEKgBIQQgBkUEQCAAIAdBDGpBARDyAgtBfyEBIAAQEQ0CIAAQmQENAiAAQd0AEDANAiAEQTRGBEAgAEHKABAOBSAAQccAEA4LDAALAAtBfyEBCyAHQRBqJAAgAQtpAAJAIAFBAE4NAEF/IQEgACgCACAAQaQCakEUIABBqAJqIAAoAqwCQQFqEIABDQAgACAAKAKsAiIBQQFqNgKsAiAAKAKkAiABQRRsaiIAQQA2AhAgAEJ/NwIIIABCgICAgHA3AgALIAELgQEBAX8CQAJAIAAoAhBBg39HDQAgACgCKA0AIAAoAiAhAiAAKAJALQBuQQFxRQ0BIAJBzQBGDQAgAkE6Rw0BCyAAQfkaQQAQFUEADwsgACgCACACEBkhAgJAAkAgAQRAIAAgAhDvBA0BCyAAEBFFDQELIAAoAgAgAhATQQAhAgsgAgvaBAEEfwJAAkACQAJ/AkACQAJAAkACQCACRQ0AAkAgAEHBABBURQRAIABBwgAQVEUNAQsgACgCACAAKAIgEBkhBSAAEBENBEEBIQcCQAJAIAAoAhAiCEEoaw4FBAEBAQQACyAIQTpGIAhB/QBGcg0DCyAAKAIAIAUQE0EDQQIgBUHCAEYbIQYMAQsgACgCEEEqRgRAIAAQEQ0IQQQhBgwBCyAAQYUBEFRFDQAgAEEBEIsBQQpGDQAgACgCACAAKAIgEBkhBSAAEBENA0EBIQcCQAJAIAAoAhAiCEEoaw4FAwEBAQMACyAIQTpGIAhB/QBGcg0CCyAAKAIAIAUQE0EFIQYgACgCEEEqRw0AIAAQEQ0HQQYhBgsgACgCECIFENcBRQ0BQQAhByAFQYN/RgRAIAAoAihFIQcLIAAoAgAgACgCIBAZIQUgABARDQILQQAgBiADRSAHRXJyDQMaIAAoAhAiAEE6RyACRSAAQShHcnEhBkEAIQQMBgsCQAJAAkAgBUGAAWoOAgEAAgsgACgCACAAKQMgEDgiBUUNBiAAEBENAgwDCyAAKAIAIAApAyAQOCIFRQ0FIAAQEUUNAgwBCyAFQdsARwRAIARFIAVBqX9Hcg0EIAAoAgAgACgCIBAZIQUgABARDQFBEAwDCyAAEBENBCAAEJkBDQQgAEHdABAwDQRBACEFQQAMAgsgACgCACAFEBMMAwtBAAshBCAGQQJJDQIgACgCEEEoRg0CIAAoAgAgBRATCyAAQfjNAEEAEBULIAFBADYCAEF/DwsgASAFNgIAIAQgBnILVAEBf0F/IQIgACgCACAAKAJAIgBBtAJqQQggAEG8AmogACgCuAJBAWoQgAFFBEAgACAAKAK4AiICQQFqNgK4AiAAKAK0AiACQQN0aiABNwMACyACC5IBAQJ/IAEoAogBIgRBgIAETgRAIABBqx9BABBQQX8PC0F/IQMgACABQYABakEQIAFBhAFqIARBAWoQgAEEf0F/BSABIAEoAogBIgNBAWo2AogBIAEoAoABIANBBHRqIgNCADcCACADQgA3AgggAyAAIAIQGTYCACADIAMoAgxBgP///wdyNgIMIAEoAogBQQFrCwuQAQECfwJAA0AgAkEATgRAAkAgACgCdCACQQR0aiIEKAIAIAFHDQAgBCgCDCIFQQJxDQMgA0UNACAFQfgAcUEYRg0DCyAEKAIIIQIMAQsLAkAgACgCIEUNACAAKAIkDQBBgICAgAQhAgJAIAAgARC1AiIABEAgAC0ABEECcQ0BC0EAIQALIAANAQtBfyECCyACC58BAQN/IwBBEGsiAiQAIABBJxBUBH8gACACEPwCQX8Cf0F/IAAQEQ0AGgJAIAAoAhAiA0EvaiIEQQdNQQBBASAEdEHBAXEbIANB+wBGckUEQEEBIANB2wBGDQIaIANBg39HDQFBACAAKAIoDQIaCyABQQRxQQJ2IAAoAgQgACgCFEZyDAELQQALIAAgAhD7AhsFQQALIQAgAkEQaiQAIAALgwIBBX8CQAJAAkAgAkHNAEYgAkE6RnJFBEAgACgCACEFIAJBFkcNASAAKAJAIQYMAgsgAEHKxQBBABAVDAILIAAoAkAiBigCwAIiB0EAIAdBAEobIQcDQCAEIAdGDQEgBEEDdCEIIARBAWohBCAIIAYoAsgCaigCBCACRw0ACyAAQbHFAEEAEBUMAQsgBSAGIANB/QBGQQAgASgCOCACQQFBAUEAEMsDIgBBAEgNACAFIAFBNGpBDCABQTxqIAEoAjhBAWoQgAENACABIAEoAjgiAkEBajYCOCABKAI0IQEgBSADEBkhAyABIAJBDGxqIgEgADYCACABIAM2AgRBAA8LQX8LqgQBB38jAEEQayIFJAAgACgCQCEHIAAoAgAhBiACQbF/RyEJQbt/Qbt/Qbd/IAJBUUYiCBsgAkFJRhtB/wFxIQoCfwJAAkADQAJAAkAgACgCECIEQYN/RgRAIAAoAigEQCAAEPABDAYLIAhFIAJBSUdxIAYgACgCIBAZIgRBJ0dyRQRAIABB+C9BABAVDAULIAAQEQ0EIAAgBCACELcCDQQgAwRAIAAgACgCQCgClAMgBCAEQQAQiQJFDQULAkAgACgCEEE9RgRAIAAQEQ0GIAlFBEAgAEG2ARAOIAAgBBAcIAAgBy8BvAEQGCAAIAVBDGogBUEIaiAFIAVBBGpBAEEAQT0QvAFBAEgNByAAIAEQuwEEQCAGIAUoAgAQEwwICyAAIAQQrQEgACAFKAIMIAUoAgggBSgCACAFKAIEQQBBABDUAQwCCyAAIAEQuwENBiAAIAQQrQEgACAKEA4gACAEEBwgACAHLwG8ARAYDAELIAhFBEAgAkFJRw0BIABBjtIAQQAQFQwGCyAAQQYQDiAAQbsBEA4gACAEEBwgACAHLwG8ARAYCyAGIAQQEwwBCyAEQSByQfsARw0BIAAgBUEMakEAEKkBQT1HDQEgAEEGEA5BfyAAIAJBAEEBIAUoAgxBAnFBARDVAUEASA0FGgtBACAAKAIQQSxHDQQaIAAQEUUNAQwDCwsgAEGS3wBBABAVDAELIAYgBBATC0F/CyEEIAVBEGokACAEC+oCAgR/AX4jAEEgayICJAACfwJAIAAoAgAgAkEIakEgEEINAAJAA0ACQCABIgMgACgCPE8NACADQQFqIQECQAJAAkACQAJAIAMtAAAiBUHcAGsOBQIDAwMBAAsgBUEkRw0CQSQhBCABLQAAQfsARw0DIANBAmohAQsgAEGCfzYCECAAIAU2AiggAkEIahA5IQYgACABNgI4IAAgBjcDIEEADAcLIAJBCGpB3AAQPg0FIAEgACgCPE8NAiADQQJqIQEgAy0AASEFCwJAAkACQCAFIgRBCmsOBAECAgACCyABIAEtAABBCkZqIQELIAAgACgCCEEBajYCCEEKIQQMAQsgBEEYdEEYdUEATg0AIAFBAWtBBiACQQRqEGEiBEH//8MASw0DIAIoAgQhAQsgAkEIaiAEEMABRQ0BDAMLCyAAQePDAEEAEBUMAQsgAEGI2ABBABAVCyACQQhqEERBfwshASACQSBqJAAgAQt2AQJ/IAEgAS0AAEF8cUEBciIEOgAAIAEgAi0ADEECdEEEcSAEQXlxciIEOgAAIAEgBEF1cSACLQAMQQJ0QQhxciIEOgAAIAItAAwhBSABIAM7AQIgASAEQQ1xIAVBAXRB8AFxcjoAACABIAAgAigCABAZNgIECyEAIABCkAOBUK1C7gJC7QIgAEIDg1AbIABC5ACBUK19fAt/AQJ/IwBBMGsiASQAIAEgAEKZ+P//v0FZBH8gAELoB38iAEL/////ByAAQv////8HUxunBUGAgICAeAs2AixByLMEQcyzBEHQswQQBSABQSxqIAEQBCABQdSzBEHQswQgASgCIBsoAgA2AiggASgCJCECIAFBMGokACACQURtC4gEAwl+AX8BfCMAQRBrIg4kAAJ/QX8gACAOQQhqIAEQuQINABoCfCAOKwMIIg+9Qv///////////wCDQoGAgICAgID4/wBaBEBEAAAAAAAAAAAgBA0BGkEADAILAn4gD5lEAAAAAAAA4ENjBEAgD7AMAQtCgICAgICAgICAfwshBUQAAAAAAAAAACADRQ0AGkEAIAUQ3ANrIgCsQuDUA34gBXwhBSAAtwshDyAFQoC4mSkQ/AQiASABQugHfyIGQugHfn0hCCABQoDd2wF/IQkgAULg1AN/QjyBIQogBkI8gSELIA4gBSABfUKAuJkpfyIFNwMAQgAhASAFQgR8QgcQ/AQhDCAOKQMAIg1CkM4AfkLJ9t4BEP0CQrIPfCEFA0ACQAJAIA0gBRD7BH0iBkIAUwRAQn8hBwwBC0IBIQcgBRDbAyAGVQ0BCyAFIAd8IQUMAQsLIA4gBjcDACAFIQcgDikDACEGA0ACQCABQgtRDQAgAadBAnRB4LMBajQCACEFIAFCAVEEQCAHENsDIAV8Qu0CfSEFCyAFIAZVDQAgAUIBfCEBIAYgBX0hBgwBCwsgAiAPOQNAIAIgDLk5AzggAiAIuTkDMCACIAu5OQMoIAIgCrk5AyAgAiAJuTkDGCACIAG5OQMIIAIgB7k5AwAgAiAGQgF8uTkDEEEBCyEAIA5BEGokACAACw0AIAAgASACQQEQgAULIQAgASgCBEEFRwRAIAFBBTYCBCAAKAIQIAFBCGoQiwMLC1kCAn8BfiMAQRBrIgMkAEF/IQQCQCAAIAFBABB7IgUQDQ0AIAAgA0EMaiAFEMYBDQAgACABQQAgAygCDCACaiIArRCWAkEASA0AIABFIQQLIANBEGokACAECxsAIAEoAiAEQCAAIAFBKGoQiwMgAUEANgIgCwugAQICfwF8AkACfAJAAkACQAJAAkAgABBWIgJBCGoOCgIBBgYGBgYCAwAECyAApyEBDAULIACnQQAQ5QUhAQwECyAAp0HbGGwhAQwDCyAAp0HbGGy3DAELIAJBB0cNAUQAAAAAAAD4fyAAEEkiAyADvUL///////////8Ag0KAgICAgICA+P8AVhsLvSIAQiCIIACFp0HbGGwhAQsgASACcwsHACAAQQFxCy4BAX8gACAAIAEgACACEMoBIgIgAUEAEBQiARCmASEDIAAgARAMIAAgAhATIAMLEgAgAEEIdCAAQQh2ckH//wNxC1ABAX8gAEEgEC8iAgRAIAJBATYCACACQoCAgIDAAEKAgICAMCABGzcDGCACIAJBGGo2AhAgAiACLQAFQQFyOgAFIAAoAhAgAkEDEL4BCyACCwoAIAAoAgQgAEYLMgACQCAAIAIgAUEAQQAQJCICEA0NACACECINACAAIAIQDCAAEClCgICAgOAAIQILIAILCwAgACABIAIQxgELCwAgAEG2PEEAEBYLdQECfyMAQZABayIEJABB3PsAIQUCQAJAAkACQCABQQFqDgUDAgIAAQILQZ37ACEFDAELQdseIQULIAAgBEHQAGogAxCJASEBIAQgACAEQRBqIAIoAgQQiQE2AgQgBCABNgIAIAAgBSAEENMCCyAEQZABaiQAC2kBAn8jAEEQayIFJAAgBUEANgIIIAVCADcDACAAIAEgAiADIAQgBRChBSECA0AgBSgCACEBIAYgBSgCCE5FBEAgACABIAZBA3RqKAIEEBMgBkEBaiEGDAELCyAAIAEQGiAFQRBqJAAgAgseACABKAIAQQRHBEAgACABQQhqEIsDIAFBBDYCAAsLpQEBBX8jAEEQayIDJABBfyECAkAgACgCFA0AIAAoAgAgACgCBCABQQF0QRBqIANBDGoQtwEiBEUEQCAAEIoDDAELIARBEGohBSAAKAIIIQIgAygCDCEGA0AgAkEATEUEQCAFIAJBAWsiAkEBdGogAiAFai0AADsBAAwBCwsgAEEBNgIQIAAgBDYCBCAAIAZBAXYgAWo2AgxBACECCyADQRBqJAAgAgtUAQJ/IAAgASkDGCACECMgACABKQMAIAIQIwJAIAEoAjwiBEUNACABKAIgIQMDQCADIARPDQEgACADKQMAIAIQIyADQQhqIQMgASgCPCEEDAALAAsLGgEBfyABpygCICIDBEAgACADKQMAIAIQIwsLRAEBfyABIAEoAgBBAWsiAjYCAAJAIAJFBEAgASgCBEUNASABQRBqEEYgACABECELDwtBvgtBvuMAQd/lAkGI2QAQAAALoAIBBH8gAUEoahBxIAEgAqcoAiAiBi0AEDYCOCABIAYoAhQ2AjAgASAAIAYvASggBBBKIgggBi8BKmogBi8BLmpBARBKQQN0EC8iADYCICAARQRAQX8PCyABIAIQDzcDGCADEA8hAiABIAg2AjQgASAENgIIIAEgAjcDACABIAEoAiAiByAIQQN0aiIANgIkIAEgACAGLwEqQQN0ajYCPEEAIQAgBEEAIARBAEobIQkDQCAAIAlGRQRAIAUgAEEDdCIHaikDABAPIQIgByABKAIgIgdqIAI3AwAgAEEBaiEADAELCyAEIAggBi8BKmoiACAAIARIGyEAA38gACAERgR/QQAFIAcgBEEDdGpCgICAgDA3AwAgBEEBaiEEDAELCwt9AQR/IAGnIgYvAQYhByAAQRgQLyIFRQRAIAAgAhAMQX8PCyACpyIIKAIgIQAgBSAEIAdB5YoBajEAAIY+AhQgBSADpyIHNgIQIAUgCDYCDCAFIAY2AgggBSAAQQxqEEwgBiAEPgIoIAYgBTYCICAGIAAoAgggB2o2AiRBAAvtAQEEfwJ+IAAoAhAhBQJAIAAgASADEG8iARANRQRAIAJCgICAgAhaBEAgAEHcwQAQawwCCyAAQRwQLyIERQRAQQAhBAwCCyAEIAKnIgY2AgACQAJAIANBFEcNACAFKAK4ASIHRQ0AIAQgBSgCxAEgBkEBEEogBxECACIFNgIIIAVFDQMgBUEAIAYQSxoMAQsgBCAAIAZBARBKEGwiBjYCCCAGRQ0CCyAEQQxqEHEgBEEuNgIYIARBADYCFCAEIANBFEY6AAUgBEEAOgAEIAEgBBCNAQsgAQwBCyAAIAEQDCAAIAQQGkKAgICA4AALCzsBAX8gACgCECIDIAEgAhDXAiIBRQRAIAAQyQFCgICAgOAADwsgAygCOCABQQJ0ajUCAEKAgICAgH+ECxMAIABCgICAgHCDQoCAgICAf1EL6gEBAX8gAEGYAxBsIgYEQCAGIAA2AgAgBkEQahBxIAZBfzYCCCAGIAE2AgQgAQRAIAZBGGogAUEQahBMIAYgAS0AbjoAbiAGIAEoArwBNgIMCyAGIAM2AiwgBiACNgIgIAAgBkGAAmoQkQIgBkEANgJwIAZBfzYCmAIgBkGQAWpB/wFBKBBLGiAGQoSAgIAQNwLEASAGIAZB0AFqNgLMASAGQn83AtABIAZBfzYC8AEgBkKAgICAcDcCvAEgACAEEMoBIQEgBiAFNgLwAiAGIAE2AuwCIAAgBkH0AmoQkQIgBiAFNgKcAgsgBgs7ACAAnUQAAAAAAAAAAKBEAAAAAAAA+H8gAEQAANzCCLI+Q2UbRAAAAAAAAPh/IABEAADcwgiyPsNmGwvlAgMCfAN/AX4CfyAAKwMIIgJEAAAAAAAAKEAQhwYiA5lEAAAAAAAA4EFjBEAgA6oMAQtBgICAgHgLIgRBDGogBCAEQQBIGyIEQQBKIQYgBEEAIAYbIQYCfiAAKwMAIAJEAAAAAAAAKECjnKAiAplEAAAAAAAA4ENjBEAgArAMAQtCgICAgICAgICAfwsiBxD7BLkhAgNAIAUgBkZFBEAgBUECdEHgswFqKAIAIQQgBUEBRgRAIAQgBxDbA6dqQe0CayEECyAFQQFqIQUgAiAEt6AhAgwBCwsgAiAAKwMQRAAAAAAAAPC/oKBEAAAAAHCZlEGiIAArAzAgACsDKEQAAAAAAECPQKIgACsDGEQAAAAAQHdLQaIgACsDIEQAAAAAAEztQKKgoKCgIQIgAQR8IAICfiACmUQAAAAAAADgQ2MEQCACsAwBC0KAgICAgICAgIB/CxDcA0Hg1ANst6AFIAILEPgDCxUBAX4gACABEIwFIQIgACABEAwgAguiCwIJfgN/IwBBEGsiDiQAIA4gAjcDCAJAAkACQAJAAkACQAJAAkACQCACEFZBB2oODwMCAgICAgAEBAQCAgICAQILAkACQAJAAkACQAJAIAKnIg0vAQZBBGsOAwEABAULQoCAgIAwIQMgACACED0iAhANDQEgDiAAIAIQ+gMiAjcDCCACEA0NASABKAIoIAIQjwEhDQwMCyAOIAAgAhCgASICNwMIQoCAgIAwIQMgAhANRQ0BC0KAgICAMCEIQoCAgIAwIQRCgICAgDAhBkKAgICAMCEJDAkLIAEoAiggAhCPASENDAkLIAEoAiggDSkDIBCcASENIAAgAhAMDAgLQoCAgIAwIQYgACABKQMIQQEgDkEIahD9AyIFEA0NBSAAIAUQLQRAIABBn9gAQQAQFgwGCyAAIAMQDyILIAEpAxgQDxDJAiIJEA0EQEKAgICAMCEDQoCAgIAwIQgMBQsCQCABKQMYEPcBRQRAAkAgAEHbgwEgCRAPIgVB3IMBEL8BIgMQDQRAQoCAgIAwIQgMAQsgAEGg/wAQdiIIEA1FDQILQoCAgIAwIQQgBSEJDAgLIAEpAyAQDyEDIAEpAyAQDyEICyAAIAAgASkDCEEBIA5BCGpBABCzBRCNAg0EIAAgAhDCASINQQBIDQQCQAJAAkAgDQRAIAAgDiACEEENCCABKAIoQdsAED4aIA4pAwAiCkIAIApCAFUbIQwgAUEoaiENA0AgBCAMUgRAIARQRQRAIAEoAihBLBA+GgsgASgCKCADEJwBGiAAIAIgBBBkIgcQDQ0KIAAgBEKAgICACFoEfiAEuRAXBSAECxA9IgUQDQRAQoCAgIAwIQQgBSEGDA0LIAAgASACIAcgBRD8AyEHIAAgBRAMIAcQDQ0KIARCAXwhBCAAIAFCgICAgCAgByAHEBIbIAkQ+wNFDQEMCgsLIApCAFcEQEKAgICAMCEEQd0AIQ9CgICAgDAhBQwEC0KAgICAMCEEQd0AIQ9CgICAgDAhBSABKQMYEPcBRQ0BDAMLAn4gASkDECIFEBJFBEAgBRAPDAELIABCgICAgDBBASAOQQhqQQAQsgULIgQQDQ0JIAAgDiAEEEENCSABKAIoQfsAED4aQgAhBSAOKQMAIgZCACAGQgBVGyEKIAFBKGohDUKAgICAMCEGA0AgBSAKUgRAIAAgBhAMIAAgBCAFEGQiBhANDQsgACACIAYQDyIGEKEBIgcQDQ0LIAAgASACIAcgBhD8AyIHEA0NCyAHEBJFBEAgDwRAIAEoAihBLBA+GgsgACAGEPoDIgYQDQRAIAAgBxAMDA0LIAEoAiggAxCcARogASgCKCAGEJwBGiABKAIoQToQPhogASgCKCAIEJwBGkEBIQ8gACABIAcgCRD7Aw0MCyAFQgF8IQUMAQsLIA9FBEBB/QAhDwwCC0H9ACEPIAYhBSABKAIYKAIEQf////8HcUUNAgsgDSgCAEEKED4aIA0oAgAgCxCcARoLIAYhBQsgASgCKCAPED4aQQAhDSAAIAAgASkDCEEAQQBBABCxBRCNAgRAIAUhBgwHCyAAIAIQDCAAIAQQDCAAIAMQDCAAIAgQDCAAIAkQDCAAIAUQDAwHCyACEEm9QoCAgICAgID4/wCDQoCAgICAgID4/wBSDQJCgICAgCAhAiAOQoCAgIAgNwMIDAILIAAgAhAMDAULIA4gACACEPoDIgI3AwhCgICAgDAhA0KAgICAMCEIQoCAgIAwIQRCgICAgDAhBkKAgICAMCEJIAIQDQ0DCyABKAIoIAIQjwEhDQwDC0KAgICAMCEEDAELQoCAgIAwIQNCgICAgDAhCEKAgICAMCEEQoCAgIAwIQkLIAAgAhAMIAAgBBAMIAAgAxAMIAAgCBAMIAAgCRAMIAAgBhAMQX8hDQsgDkEQaiQAIA0LmwICAX8BfiMAQSBrIgUkACAFIAQ3AxgCQAJAAkAgAxAiBEBCgICAgOAAIQYgACADQYsBIANBABAUIgQQDQRAIAMhBAwDCyAAIAQQOwRAIAAgBCADQQEgBUEYahA2IQQgACADEAwgBBANRQ0CDAMLIAAgBBAMCyADIQQLAkAgASkDACIDEBIEQCAEIQMMAQsgBSAENwMIIAUgBSkDGDcDACAAIAMgAkECIAUQJCEDIAAgBBAMQoCAgIDgACEGIAMhBCADEA0NAQtCgICAgDAhBgJAIAMQVkEHaiIBQQ5LDQBBASABdEGBxwFxDQIgAUEGRw0AIAMhBCAAIAMQO0UNAgwBCyADIQQLIAAgBBAMIAYhAwsgBUEgaiQAIAMLvwICAn8EfiMAQSBrIgQkAEKAgICA4AAhCAJAIAAgBEEYaiAAIAEQKyIJEEENAAJAIAQpAxgiB0IAVw0AIARCADcDECACQQJOBEAgACAEQRBqIAMpAwhCACAHIAcQgQENAgsCQAJAIAkgBEEMaiAEQQhqEI4CRQRAIAQpAxAhAQwBCyAEKQMQIgYgBDUCCCIBIAEgBlMbIQEgBCgCDCECA0AgASAGUgRAIAanIQUgBkIBfCEGIAAgAykDABAPIAIgBUEDdGopAwAQD0ECEN8BRQ0BDAMLCyAEIAE3AxALIAEgByABIAdVGyEGA0AgASAGUQ0CIAAgCSABEGQiBxANDQMgAUIBfCEBIAAgAykDABAPIAdBAhDfAUUNAAsLQoGAgIAQIQgMAQtCgICAgBAhCAsgACAJEAwgBEEgaiQAIAgL7AUCCX4DfyMAQeAAayINJABCgICAgDAhBSANQoCAgIAwNwMwIA1CgICAgDA3AyggDUKAgICAMDcDGCANIA1ByABqIg82AkAgDSAAQS8QMiIHNwM4IAAgD0EAEEIaIA0gABBRIgQ3AyBCgICAgOAAIQgCQAJAIAQQDQ0AAkAgACACEDsEQCANIAI3AxgMAQsgACACEMIBIg5BAEgNASAORQ0AIA0gABBRIgk3AyggCRANDQEgACANQQhqIAIQQQ0BIA0pAwgiBEIAIARCAFUbIQsDQCAGIAtRDQEgDSAAIAIgBhBkIgQ3AxAgBBANDQICQAJAAkAgBBAiBEAgBKcvAQZB/v8DcUEERw0CIA0gACAEED0iBDcDECAEEA1FDQEMBgsgBBCQAQRAIA0gACAEED0iBDcDECAEEA1FDQEMBgsgBBCeAUUNAQsgACAJQQEgDUEQahD9AyIMEA0EQCAAIAQQDAwFCyAAIAwQLQ0AIAAgCSAKIAQQkQEaIApCAXwhCgwBCyAAIAQQDAsgBkIBfCEGDAALAAsCQCADEA8iBBAiRQ0AAkACQAJAIASnLwEGQQRrDgIAAQILIAAgBBCgASEEDAELIAAgBBA9IQQLIAQQDUUNACAAIAQQDAwBCyANAn4gBBCQAQRAIAAgDUEEaiAEQQpBABBlDQIgAEGX/wAgDSgCBBD+AQwBCyAEEJ4BBEAgACAEpyIOQQAgDigCBEH/////B3FBChC0ARCdAQwBCyAHEA8LIgI3AzAgACAEEAwgAhANDQAgABA8IgUQDQ0AIAAgBUEvIAEQDyIBQQcQG0EASA0AIAAgDUEYaiAFIAEQDyAHEPwDIgEQDQ0AIAEQEgRAQoCAgIAwIQgMAQsgACANQRhqIAEgBxD7AyEOIA0oAkAhDyAODQAgDxA5IQgMAQsgDxBECyAAIAUQDCAAIA0pAzgQDCAAIA0pAzAQDCAAIA0pAygQDCAAIA0pAyAQDCANQeAAaiQAIAgLfAIBfwF+IwBB0ABrIgQkACAAIAQgASACIAMQtAUgBEEANgJMQoCAgIAwIQUCQAJAIAQQsQENACAEEIEEIgUQDQ0AIAQoAhBBqn9GDQEgBEH52gBBABAVCyAAIAUQDCAEIARBEGoQjwJCgICAgOAAIQULIARB0ABqJAAgBQtAAQF/IwBBEGsiAiQAAn8gASAAKAIQRwRAIAIgATYCACAAQbz9ACACEBVBfwwBCyAAELEBCyEAIAJBEGokACAAC98EAgR/An4jAEEQayIDJAAgACgCACECAkACQAJAAkACQAJAAkACQAJAAkAgACgCECIBQYABag4EAgEFAwALIAFBqn9GDQMgAUHbAEcEQCABQfsARw0FQoCAgIAgIQUgABCxAQ0IIAIQPCIFEA0NCAJAIAAoAhAiAUH9AEYNAANAAkAgAUGBf0YEQCACIAApAyAQOCIBDQEMDAsgACgCTEUgAUGDf0dyDQogAiAAKAIgEBkhAQsCQAJAIAAQsQENACAAQToQgAQNACAAEIEEIgYQDUUNAQsgAiABEBMMCwsgAiAFIAEgBkEHEBshBCACIAEQEyAEQQBIDQogACgCEEEsRw0BIAAQsQENCiAAKAJMRSAAKAIQIgFB/QBHcg0ACwsgAEH9ABCABA0IDAkLQoCAgIAgIQUgABCxAQ0HIAIQUSIFEA0NBwJAIAAoAhBB3QBGDQBBACEBA0AgABCBBCIGEA0NCSACIAUgASAGQQcQnwFBAEgNCSAAKAIQQSxHDQEgABCxAQ0JIAFBAWohASAAKAJMRQ0AIAAoAhBB3QBHDQALCyAAQd0AEIAEDQcMCAsgACkDIBAPIQUgABCxAQ0GDAcLIAApAyAhBSAAELEBDQUMBgsgACgCIEEBayIBQQJLDQEgAUEDdEGI3QFqKQMAIQUgABCxAQ0EDAULIABBkRRBABAVDAELIAAoAjghASADIAAoAhgiBDYCBCADIAEgBGs2AgAgAEHR+gAgAxAVC0KAgICAICEFDAELIABBws0AQQAQFQsgAiAFEAxCgICAgOAAIQULIANBEGokACAFCw4AIAAoAhAoAowBKQMIC0cCAX4BfyABECJFBEBBAA8LQX8hAyAAIAFBxAEgAUEAEBQiAhANBH9BfwUgAhASRQRAIAAgAhAtDwsgACABQQAQ3QFBAEcLC7IIAg1/AX4jAEHgAGsiBiQAAkAgAhASRQRAQoCAgIDgACEQIAAgBkHcAGogAhCQAiIHRQ0BIAYoAlwhBQNAIAUgCEcEQAJAIAcgCGosAABB5wBrQR93IgRBCUtBywUgBHZBAXFFckUEQCAEQQJ0QeDcAWooAgAiBCAJcUUNAQsgACAHEDcgAEGnJEEAENMCDAQLIAhBAWohCCAEIAlyIQkMAQsLIAAgBxA3C0KAgICA4AAhECAAIAZB3ABqIAEgCUEEdkF/c0EBcRChBCIMRQ0AIAYoAlwhBSMAQeABayIEJAAgBEEAQdwBEEsiA0F/NgI8IANCgYCAgHA3AjQgAyAMNgIgIAMgBSAMajYCHCADIAw2AhggAyAANgJAIAMgCTYCJCADIAlBA3ZBAXE2AjAgAyAJQQF2QQFxNgIsIAMgCUEEdkEBcTYCKCADIABB7AIQ5wIgA0HEAGoiDiAAQewCEOcCIAMgCUH/AXEQECADQQAQECADQQAQECADQQAQHiAJQSBxRQRAIANBCEEGELoBGiADQQQQXyADQQdBdRC6ARoLIAZBEGohCCADQQtBABCtAgJ/AkAgA0EAEOQCDQAgA0EMQQAQrQIgA0EKEF8gAygCGC0AAARAIANB2NoAQQAQPwwBCyADKAIMBEAgAxCsAgwBCwJ/IAMoAgRBB2shDyADKAIAQQdqIQlBACEFAkACQANAAkACQAJAAkACQCAKIA9IBH8gCSAKaiIHLQAAIgRBHU8NBSAKIARBoOEBai0AACILaiAPSg0HAkAgBEEPaw4MAAIFBQUFAwQFBQACBQsgBUEBaiEEIAUgDUgEQCAEIQUMBQsgBUH+AUohByAEIgUhDSAHRQ0EQX8FIA0LDAgLIAVBAEwNBiAFQQFrIQUMAgsgBy8AAUECdCALaiELDAELIAcvAAFBA3QgC2ohCwsgCiALaiEKDAELC0GX6ABB1eMAQfoNQcLIABAAAAtB4DpB1eMAQfsNQcLIABAAAAtBxvMAQdXjAEGIDkHCyAAQAAALIgRBAEgEQCADQbohQQAQPwwBCyADKAIAIAMoAjQ6AAEgAygCACAEOgACIAMoAgBBA2ogAygCBEEHaxBdIAMoAkgiBCADKAI0QQFrSwRAIAMgAygCRCAEEIoBGiADKAIAIgQgBC0AAEGAAXI6AAALIA4QlwEgCEEAOgAAIAYgAygCBDYCWCADKAIADAELIAMQlwEgDhCXASADQdwAaiEHIAhBP2ohBQNAIActAAAiBEUgBSAITXJFBEAgCCAEOgAAIAhBAWohCCAHQQFqIQcMAQsLIAhBADoAACAGQQA2AlhBAAshBCADQeABaiQAIAAgDBA3IARFBEAgBiAGQRBqNgIAIABB0iggBhDTAgwBCyAAIAQgBigCWBDYAiEQIAAgBBAaCyAGQeAAaiQAIBALDgAgACgCECABIAIQ5wELswECBX8BfiABKQJUIgdCOIZCOIenRQRAIAEgB0KAfoNCAYQ3AlQDQCABKAIUIARMBEBBAA8LAn8gASgCECAEQQN0aiIGKAIAIQJBACEFQQAgACABKAIEEKIEIgNFDQAaIAAgACACEKIEIgIEfyAAIAMgAhC9BSEFIAAgAxA3IAIFIAMLEDcgBQsiA0UEQEF/DwsgBiADNgIEIARBAWohBEF/IQIgACADEIYEQQBODQALCyACC3ABAX9BxgAhAgJAAkACQAJAAkACQAJAAkACQCABEFZBCGoOEAYBBwcHBwcCCAAFAwcHBwgHC0HHAA8LQcgADwsgAacsAAVBAE4NAQtBxQAPC0EbIQIgACABEDsNAwtByQAPC0HKAA8LQcwAIQILIAIL6QMCA38BfiMAQSBrIgYkACABEA8hAQJAAkACQAJAAkADQAJAAkACQCABpyIHLQAFQQRxRQ0AIAAoAhAoAkQgBy8BBkEYbGooAhQiCEUNACAIKAIYIghFDQAgACABIAIgAyAEIAUgCBEpACEHDAELIAAgBiAHIAIQTyIHQQBODQELIAAgARAMDAULAkAgBwRAIAYtAABBEHEEQCAAQQAgBikDGCIJpyAJEBIbIAQgAyAFEKIDIQcgACAGKQMQEAwgACAGKQMYEAwgACABEAwMCAsgACAGKQMIEAwgBi0AAEECcQ0BIAAgARAMDAMLIAAgARCZAiIBEChFDQELCyAAIAEQDCAEECJFBEAgACADEAwgACAFQegcEHkhBwwFCyAAIAYgBKciCCACEE8iB0EASA0DIAdFDQIgBi0AAEEQcQRAIAAgBikDEBAMIAAgBikDGBAMIAAgAxAMIAAgBUGZOxB5IQcMBQsgACAGKQMIEAwgBi0AAEECcUUNACAILwEGQQtHDQELIAAgAxAMIAAgBSACEOABIQcMAwsgACAEIAIgA0KAgICAMEKAgICAMEGAwAAQeCEHDAELIAAgCCACIANCgICAgDBCgICAgDAgBUGHzgByEJYEIQcLIAAgAxAMCyAGQSBqJAAgBwtjAQJ/AkAgAUKAgICAcFQNACABpyIDLwEGEPgBRQ0AIAMoAiAtABFBCHFFDQAgAygCKCIEBEAgACAErUKAgICAcIQQDAtBACEAIAMgAkKAgICAcFoEfyACEA+nBUEACzYCKAsLxgEBA38gAUEcaiEEIAFBGGohBgNAIAYgBCgCACIERwRAAkAgBEECay8BACACRw0AIARBCGsiBS0ABUEBdkEBcSADRw0AIAUgBSgCAEEBajYCACAFDwsgBEEEaiEEDAELCyAAQSAQLyIARQRAQQAPCyAAQQE2AgAgACACOwEGIAAgAC0ABUH8AXEgA0EBdEECcXI6AAUgAEEIaiAGEEwgAUEQQRQgAxtqKAIAIQEgAEKAgICAMDcDGCAAIAEgAkEDdGo2AhAgAAufAgIFfwF+IwBBEGsiBiQAAkAgAkL/////b1gEQCAAQbMdQQAQFgwBCyAAIAZBDGogAhDcAQ0AIAYoAgwiBEGBgARPBEAgAEGrH0EAEFAMAQsgACAEQQEgBBtBA3QQbCIFRQ0AAkACQCACpyIHLwEGIgNBCEcgA0ECR3ENACAHLQAFQQhxRQ0AIAQgBygCKEcNAEEAIQMDQCADIARGDQIgBSADQQN0IgBqIAcoAiQgAGopAwAQDzcDACADQQFqIQMMAAsAC0EAIQMDQCADIARGDQEgACACIAMQeyIIEA0EQCAAIAUgAxCYA0EAIQMMAwUgBSADQQN0aiAINwMAIANBAWohAwwBCwALAAsgASAENgIAIAUhAwsgBkEQaiQAIAMLhAICAn8CfkKAgICA4AAhCQJAIAAQggENAAJAAkAgAUKAgICAcFoEQCABpyIGLQAFQRBxRQRAIABB3ylBABAWQoCAgIDgAA8LIAVBAXIhBSAGLwEGIgdBDUYNAiAAKAIQKAJEIAdBGGxqKAIQIgYNAQsgAEGpNkEAEBZCgICAgOAADwsgACABIAIgAyAEIAUgBhEVAA8LIAYoAiAtABFBBHEEQCAAIAFCgICAgDAgAiADIAQgBRDjAQ8LIAAgAkEBEG8iCBANDQACQCAAIAEgCCACIAMgBCAFEOMBIgFC/////29YBEAgARANRQ0BCyAAIAgQDCABDwsgACABEAwgCCEJCyAJC9ABAgF/AX4CQAJAIAAgAaciBC8AEUEDdkEGcUHWogFqLwEAEKQBIgUQDQRADAELAkAgACAFIAQgAiADEKAFIgEQDQ0AIAAgASAEKAIcIgJBLyACGyAELwEsEKkDIAQvABEiAkEQcQRAIAAgACgCKEGQA0HAAiACQTBxQTBGG2opAwAQVSIFEA0NASAAIAFBOyAFQQIQGxogAQ8LIAJBAXFFDQIgAUEBELIDIAAgAUE7QQBBAEECEJQDGiABDwsLIAAgARAMQoCAgIDgACEBCyABCw0AIAAgASACEA8QzQULNQECfwJAIABCgICAgHBUDQAgAKciBC8BBkEMRw0AIAQoAiQgAUcNACAELgEqIAJGIQMLIAML8wMBDX8jAEEgayIFJAAgA0EAIANBAEobIQ1BACEDA0ACQCADIA1GBEBBACEKDAELIAVBADYCGCAFQgA3AxAgBUIANwMIIAUgASADQQxsaiIHKAIENgIMIAUgBygCCDYCECACIANqIQZBfyEKIANBAWohAyAHKAIAIQdBfyELAkAgBkH//wNLDQACQCAGIAAoAkAiBEkEQCAAKAJEIgQgBkEYbGooAgBFDQEMAgtBMyAGQQFqIARBA2xBAm0QShBKIghBA3QhDiAAQcwAaiEEIABByABqIQ8DQCAPIAQoAgAiCUcEQCAAIAkoAhQgDhDnASIMRQ0DIAggACgCQCIEIAQgCEgbIRADQCAEIBBHBEAgDCAEQQN0akKAgICAIDcDACAEQQFqIQQMAQsLIAkgDDYCFCAJQQRqIQQMAQsLIAAgACgCRCAIQRhsEOcBIgRFDQEgBCAAKAJAIglBGGxqQQAgCCAJa0EYbBBLGiAAIAg2AkAgACAENgJECyAEIAZBGGxqIgQgBjYCACAHEPIBRQRAIAAoAjggB0ECdGooAgAiBiAGKAIAQQFqNgIACyAEIAc2AgQgBCAFKAIMNgIIIAQgBSgCEDYCDCAEIAUoAhQ2AhAgBCAFKAIYNgIUQQAhCwsgC0EATg0BCwsgBUEgaiQAIAoLTwEDfyAAKALUASABKAIUIAAoAsgBENQCQQJ0aiECA0AgAiIDKAIAIgRBKGohAiABIARHDQALIAMgASgCKDYCACAAIAAoAtABQQFrNgLQAQsYACAAKAIgKAIUIAAvAQZB5YoBai0AAHYLGAAgACAAQQh2QQdxIgBxIABBf3MgAXFyC7YIAQx/IwBBEGsiBiQAAkACQANAIAEoAhAiBCAEKAIYIAJxQX9zIghBAnRqKAIAIQVBACEDIAQQKiEHA0AgBQRAIAYgByAFQQFrIgpBA3RqIgQ2AgwgBCgCACEFIAIgBCgCBEYEQEEAIQkgBUGAgIAgcUUNBUF/IQkgACABIAZBDGoQ5AENBSABKAIQIQICQCADBEAgAhAqIAMgB2tBA3VBACADG0EDdGoiAyADKAIAQYCAgGBxIAYoAgwoAgBB////H3FyNgIAIAYoAgwhAwwBCyACIAhBAnRqIAYoAgwiAygCAEH///8fcTYCAAtBASEJIAIgAigCJEEBajYCJCAAKAIQIAEoAhQgCkEDdGoiBCADKAIAQRp2EM8FIAAgBigCDCgCBBATIAYoAgwiAyADKAIAQf///x9xNgIAIAYoAgxBADYCBCAEQoCAgIAwNwMAIAIoAiQiA0EISA0FIAMgAigCIEEBdkkNBSAAIQNBACECQQAhCgJAAkACQCABKAIQIgctABBFBEBBAiAHKAIgIAcoAiRrEEoiCyAHKAIcSw0BIAcoAhhBAWohAANAIAAiBEEBdiIAIAtPDQALAkAgAyAEIAsQ5QEQLyIARQ0AIARBAWshDCAAIAQQvwIhACAHQQhqEEYgACAHQTAQJSIFQQhqIAMoAhBB0ABqEEwgBSAEQQJ0IgBrQQAgABBLGiAHQTBqIQAgBUEwaiEIIAEoAhQhDQNAIAUoAiAiBCAKSwRAIAAoAgQiBARAIAggBDYCBCAIIAAoAgBBgICAYHEiBCAIKAIAQf///x9xcjYCACAIIAQgBSAAKAIEIAxxQX9zQQJ0aiIOKAIAQf///x9xcjYCACAOIAJBAWoiBDYCACANIAJBA3RqIA0gCkEDdGopAwA3AwAgCEEIaiEIIAQhAgsgCkEBaiEKIABBCGohAAwBCwsgAiAEIAUoAiRrRw0DIAVBADYCJCAFIAs2AhwgBSAMNgIYIAUgAjYCICABIAU2AhAgAyAHEMECEBogAyABKAIUIAtBA3QQmgIiAEUNACABIAA2AhQLDAMLQYriAEG+4wBBrSNBmCYQAAALQf3HAEG+4wBBsSNBmCYQAAALQcb2AEG+4wBB1iNBmCYQAAALDAUFIAVB////H3EhBSAEIQMMAgsACwtBASEJIAEtAAUiA0EEcUUNAiADQQhxRQ0BIAAgBkEIaiACELYBRQ0CIAYoAggiAyABKAIoIgRPDQIgAS8BBiIFQQhGIAVBAkZyRQRAQQAhCQwDCyAEQQFrIANGBEAgACABKAIkIANBA3RqKQMAEAwgASADNgIoDAMLIAAgARCgA0UNAAtBfyEJDAELIAAoAhAoAkQgAS8BBkEYbGooAhQiA0UNACADKAIIIgNFDQAgACABrUKAgICAcIQgAiADERMAIQkLIAZBEGokACAJCwQAQQAL7gQCA38BfiMAQRBrIggkAAJAAkACQAJAAkAgAS0ABSIHQQRxRQ0AIAEvAQYiCUECRgRAAkAgB0EIcQRAAkAgAhBeBEAgCCACEHwiCTYCDCAJIAEoAihHDQEgB0EBcUUNBiAGQYAwcQ0BIAZBABCTBEEHRw0BIAAgASADEA8gBhCXBCEHDAkLIAAgCEEMaiACELYBRQ0EC0F/IQcgACABEKADRQ0BDAcLIAAgCEEMaiACELYBRQ0CCyAAIAhBCGogASgCFCIJKQMAEMcBGiAIKAIMQQFqIgcgCCgCCE0NASABKAIQECotAANBCHFFBEAgACAGQTAQ4AEhBwwGCyAAIAkgB0EATgR+IAetBSAHuBAXCxAfDAELIAlBFWtB//8DcUEITQRAIAAgAhClAyIHRQ0BIAdBAEgNBCAAIAZB3g0QeSEHDAULIAZBgIAIcQ0AIAAoAhAoAkQgCUEYbGooAhQiB0UNACABrUKAgICAcIQhCiAHKAIMIgcEQCAAIAogAiADIAQgBSAGIAcRIwAhBwwFCyAAIAoQogEiB0EASA0DIAdFDQELIAEtAAVBAXENAQsgACAGQdzQABB5IQcMAgsgACABIAIgBkEFcUEQciAGQQdxIAZBgDBxIgIbEIMBIgFFDQAgAgRAIAFBADYCAAJAIAZBgBBxRQ0AIAAgBBA7RQ0AIAEgBBAPPgIACyABQQA2AgRBASEHIAZBgCBxRQ0CIAAgBRA7RQ0CIAEgBRAPPgIEDAILAkAgBkGAwABxBEAgASADEA83AwAMAQsgAUKAgICAMDcDAAtBASEHDAELQX8hBwsgCEEQaiQAIAcL5QECBX8BfiABKAIUIgQpAwAiCUL/////D1YgASgCKCIGQQFqIgUgCadNckUEQCABKAIQECotAANBCHFFBEAgACACEAwgACADQTAQ4AEPCyAEIAWtNwMACwJAIAUgASgCIE0NACMAQRBrIgMkACAAIAEoAiQgBSABKAIgQQNsQQF2EEoiBEEDdCADQQxqELcBIgcEfyADKAIMIQggASAHNgIkIAEgCEEDdiAEajYCIEEABUF/CyEEIANBEGokACAERQ0AIAAgAhAMQX8PCyABKAIkIAZBA3RqIAI3AwAgASAFNgIoQQELCwAgACABQQEQoAQLwgEBA38gAUKAgICAcFQEQEEADwsgAaciAi8BBkEpRgRAIwBBEGsiBCQAAkACQCAAIARBCGogAUHiABCHASICRQ0AIAQpAwgiARASBEAgACACKQMAEJkEIQMMAgsgACABIAIpAwhBASACEDYiARANDQAgACABEC0iA0UEQEEAIQMMAgsgACACKQMAEKIBIgJBAEgNACACRQ0BIABB6iJBABAWC0F/IQMLIARBEGokACADDwsgAiACLQAFQf4BcToABUEBCy4BAX8gAKcpAyAiAEKAgICAcINCgICAgJB/UQR/IACnKAIEQf////8HcQVBAAsLCgAgACgCAEF8cQszACAAIAJBARD9ASIARQRAQoCAgIDgAA8LIABBEGogASACQQF0ECUaIACtQoCAgICQf4QLZQICfwF+QQQhAkKAgICAICEEAkACQAJAAkACQAJAIAEQViIDQQhqDgoDAgUFBQUFBQQBAAsgA0EHRg0DDAQLQQYhAgwCC0EFIQIMAQtBByECCyAAKAIoIAJBA3RqKQMAIQQLIAQLYAEBfCAAKQIEQv//////////P1gEQCABIAErAwhEAAAAAAAA8D8gACgCALciAqOgOQMIIAEgASsDECAAKAIEIgBBH3UgAEH/////B3EgAEEfdnRqQRFquCACo6A5AxALC+kGAQV/AkACQAJAAkACQAJAAkACQAJAIAEtAARBD3EOBgABBAIDBgULIAAgASgCECIHIAIRAwAgBxAqIQUDQCAHKAIgIANKBEACQCAFKAIERQ0AIAEoAhQgA0EDdGohBAJAAkACQAJAIAUoAgBBHnZBAWsOAwABAgMLIAQoAgAiBgRAIAAgBiACEQMACyAEKAIEIgRFDQMgACAEIAIRAwAMAwsgBCgCACIELQAFQQFxRQ0CIAAgBCACEQMADAILIAAgBBCbBCACEQMADAELIAAgBCkDACACECMLIANBAWohAyAFQQhqIQUMAQsLIAEvAQYiA0EBRg0GIAAoAkQgA0EYbGooAgwiA0UNBiAAIAGtQoCAgIBwhCACIAMREQAPCwNAIAEoAjggA0oEQCAAIAEoAjQgA0EDdGopAwAgAhAjIANBAWohAwwBCwsgASgCMCIBRQ0FIAAgASACEQMADwsgAS0ABUEBcUUNBSAAIAEoAhApAwAgAhAjDwsgASgCIARAIAAgAUEoaiACEO8DCyAAIAEpAxAgAhAjIAAgASkDGCACECMPCyABKAIsIgFFDQIgACABIAIRAwAPCxABAAsgAUHkAWohAyABQeABaiEHA0AgByADKAIAIgVHBEAgBUEIayEDQQAhBANAIAMoAiAgBEoEQAJAIAMoAhwgBEEUbGoiBigCCA0AIAYoAgQiBkUNACAAIAYgAhEDAAsgBEEBaiEEDAELCyAAIAMpA0AgAhAjIAAgAykDSCACECMgACADKQNgIAIQIyAAIAMpA2ggAhAjIAVBBGohAwwBCwsgACABKQPAASACECMgACABKQPIASACECMgACABKQOwASACECMgACABKQO4ASACECMgACABKQOoASACECNBACEDA0AgA0EIRgRAQQAhAwNAIAAoAkAgA0oEQCAAIAEoAiggA0EDdGopAwAgAhAjIANBAWohAwwBCwsgACABKQOYASACECMgACABKQOgASACECMgACABKQNQIAIQIyAAIAEpA0AgAhAjIAAgASkDSCACECMgACABKQM4IAIQIyAAIAEpAzAgAhAjIAEoAiQiAQRAIAAgASACEQMACwUgACABIANBA3RqKQNYIAIQIyADQQFqIQMMAQsLCw8LQZniAEG+4wBBjixB0joQAAALiAICAX4CfyMAQTBrIgQkAEHK5gAhBUKAgICA4AAhAwJAAkACQAJAAkACQAJAAkACQAJAAkACQCABEFZBCGoOEAUGCQkJCQoEAAECAwkJCwgJCyAEIAE+AgAgBEEQaiIFQSBBnOMAIAQQVxoMCQsgAEEDQQIgAacbEDIhAwwJCyAAQQEQMiEDDAgLIABBxQAQMiEDDAcLIAAgAUEAEJsDIgEQDQRAIAEhAwwHCyAAIAEgAhCgBCEDIAAgARAMDAYLIAJFDQELIAEQDyEDDAQLIABBw8MAQQAQFgwDCyAAIAEQSUEKQQBBABDMAiEDDAILQbfmACEFCyAAIAUQdiEDCyAEQTBqJAAgAwutBAIIfwF+AkACQAJAAkACQCACQoCAgIBwg0KAgICAkH9SBEAgACACEC4iAhANRQ0BDAILIAIQDyECCyACpyIEQRBqIQcgBCkCBCIMp0H/////B3EhBgJAIAxCgICAgAiDUARAQQAhBANAIAQgBkZFBEAgBSAEIAdqLQAAQQd2aiEFIARBAWohBAwBCwsgBUUEQCAHIQQgAQ0EDAYLIAAgBSAGakEAEP0BIghFDQIgCEEQaiEEQQAhBQNAIAUgBkYNAgJ/IAUgB2osAAAiA0EATgRAIAQgAzoAACAEQQFqDAELIAQgA0E/cUGAAXI6AAEgBCADQcABcUEGdkHAAXI6AAAgBEECagshBCAFQQFqIQUMAAsACyAAIAZBA2xBABD9ASIIRQ0BIAhBEGohBANAIAkiBSAGTg0BIAVBAWohCSAHIAVBAXRqLwEAIgpB/wBNBEAgBCAKOgAAIARBAWohBAUCQCAKQYD4A3FBgLADRyADciAGIAlMcg0AIAcgCUEBdGovAQAiC0GA+ANxQYC4A0cNACAKQQp0QYD4P3EgC0H/B3FyQYCABGohCiAFQQJqIQkLIAQgChDmAiAEaiEECwwACwALIARBADoAACAIIAQgCEEQaiIHa0H/////B3GtIAgpAgRCgICAgHiDhDcCBCAAIAIQDCABRQ0CIAgoAgRB/////wdxIQYMAQtBACEGQQAhB0EAIQQgAUUNAgsgASAGNgIACyAHIQQLIAQLJQIBfwF+IAAgARAyIgMQDUUEQCAAIAMQpgEhAiAAIAMQDAsgAgsMACABIAAoAgwRBAALPQEBfyABIAEoAgAiAkEBazYCACACQQFMBEAgASkCBEKAgICAgICAgMAAWgRAIAAgARCsAw8LIAAgARAhCwtVAQJ/IwBBEGsiAiQAIAAoAhAhAAJ/AkAgAkEMaiABEOcFRQ0AIAIoAgwiA0EASA0AIAAgARCkBCADEJUBDAELIAAgAUEBENcCCyEBIAJBEGokACABC14BA38gAEHgAWohBCAAKALkASECA0AgAiAERwRAIAJBCGshAyACKAIEIQICQAJAAkAgAQ4DAgABBAsgAywAVA0DDAELIAMpAlRCIIZCOIenDQILIAAgAxDpBQwBCwsLRAEBfyMAQRBrIgUkACAFIAEgAiADIARCgICAgICAgICAf4UQfSAFKQMAIQEgACAFKQMINwMIIAAgATcDACAFQRBqJAALEAAgACABIAJBAEEAEKkEGgvUAgEEfyMAQdABayIFJAAgBSACNgLMASAFQaABaiICQQBBKBBLGiAFIAUoAswBNgLIAQJAQQAgASAFQcgBaiAFQdAAaiACIAMgBBD3BUEASARAQX8hAQwBCyAAKAJMQQBOIQYgACgCACEHIAAoAkhBAEwEQCAAIAdBX3E2AgALAn8CQAJAIAAoAjBFBEAgAEHQADYCMCAAQQA2AhwgAEIANwMQIAAoAiwhCCAAIAU2AiwMAQsgACgCEA0BC0F/IAAQrgQNARoLIAAgASAFQcgBaiAFQdAAaiAFQaABaiADIAQQ9wULIQIgCARAIABBAEEAIAAoAiQRAQAaIABBADYCMCAAIAg2AiwgAEEANgIcIAAoAhQhASAAQgA3AxAgAkF/IAEbIQILIAAgACgCACIAIAdBIHFyNgIAQX8gAiAAQSBxGyEBIAZFDQALIAVB0AFqJAAgAQsgAQF+IAAgACACIAFBAUECQQAQywEiBCABIAMQ0AEgBAs8AQF/IABCADcDcCAAIAAoAiwgACgCBCIBa6w3A3ggACAAKAIIIgAgAWusQgBXQQFyBH8gAAUgAQs2AmgLSgECfwJAIAAtAAAiAkUgAiABLQAAIgNHcg0AA0AgAS0AASEDIAAtAAEiAkUNASABQQFqIQEgAEEBaiEAIAIgA0YNAAsLIAIgA2sLwQEBA38CQCABIAIoAhAiAwR/IAMFIAIQrgQNASACKAIQCyACKAIUIgVrSwRAIAIgACABIAIoAiQRAQAPCwJAIAIoAlBBAEgEQEEAIQMMAQsgASEEA0AgBCIDRQRAQQAhAwwCCyAAIANBAWsiBGotAABBCkcNAAsgAiAAIAMgAigCJBEBACIEIANJDQEgACADaiEAIAEgA2shASACKAIUIQULIAUgACABECUaIAIgAigCFCABajYCFCABIANqIQQLIAQLWQEBfyAAIAAoAkgiAUEBayABcjYCSCAAKAIAIgFBCHEEQCAAIAFBIHI2AgBBfw8LIABCADcCBCAAIAAoAiwiATYCHCAAIAE2AhQgACABIAAoAjBqNgIQQQALmAQDA3wCfgJ/AnwCQCAAvSIEQjSIp0H/D3EiBkHJB2tBP0kEQCAGIQcMAQsgBkHIB00EQCAARAAAAAAAAPA/oA8LIAZBiQhJDQBEAAAAAAAAAAAgBEKAgICAgICAeFENARogBkH/D0YEQCAARAAAAAAAAPA/oA8LIARCAFMEQEQAAAAAAAAAEBCKBg8LRAAAAAAAAABwEIoGDwtBsJsEKwMAIACiQbibBCsDACIBoCICIAGhIgFByJsEKwMAoiABQcCbBCsDAKIgAKCgIgEgAaIiACAAoiABQeibBCsDAKJB4JsEKwMAoKIgACABQdibBCsDAKJB0JsEKwMAoKIgAr0iBadBBHRB8A9xIgZBoJwEaisDACABoKCgIQAgBkGonARqKQMAIAVCLYZ8IQQgB0UEQAJ8IAVCgICAgAiDUARAIARCgICAgICAgIg/fb8iASAAoiABoEQAAAAAAAAAf6IMAQsjAEEQayEHIARCgICAgICAgPA/fL8iAiAAoiIBIAKgIgNEAAAAAAAA8D9jBHwgB0KAgICAgICACDcDCCAHIAcrAwhEAAAAAAAAEACiOQMIRAAAAAAAAAAAIANEAAAAAAAA8D+gIgAgASACIAOhoCADRAAAAAAAAPA/IAChoKCgRAAAAAAAAPC/oCIAIABEAAAAAAAAAABhGwUgAwtEAAAAAAAAEACiCw8LIAS/IgEgAKIgAaALC3UCAnwBfiAAAn4QAyIBRAAAAAAAQI9AoyICmUQAAAAAAADgQ2MEQCACsAwBC0KAgICAgICAgIB/CyIDPgIAIAACfyABIANC6Ad+uaFEAAAAAABAj0CiIgGZRAAAAAAAAOBBYwRAIAGqDAELQYCAgIB4CzYCBAvAGAMUfwR8AX4jAEEwayIJJAACQAJAAkAgAL0iGkIgiKciA0H/////B3EiBEH61L2ABE0EQCADQf//P3FB+8MkRg0BIARB/LKLgARNBEAgGkIAWQRAIAEgAEQAAEBU+yH5v6AiAEQxY2IaYbTQvaAiFjkDACABIAAgFqFEMWNiGmG00L2gOQMIQQEhAgwFCyABIABEAABAVPsh+T+gIgBEMWNiGmG00D2gIhY5AwAgASAAIBahRDFjYhphtNA9oDkDCEF/IQIMBAsgGkIAWQRAIAEgAEQAAEBU+yEJwKAiAEQxY2IaYbTgvaAiFjkDACABIAAgFqFEMWNiGmG04L2gOQMIQQIhAgwECyABIABEAABAVPshCUCgIgBEMWNiGmG04D2gIhY5AwAgASAAIBahRDFjYhphtOA9oDkDCEF+IQIMAwsgBEG7jPGABE0EQCAEQbz714AETQRAIARB/LLLgARGDQIgGkIAWQRAIAEgAEQAADB/fNkSwKAiAETKlJOnkQ7pvaAiFjkDACABIAAgFqFEypSTp5EO6b2gOQMIQQMhAgwFCyABIABEAAAwf3zZEkCgIgBEypSTp5EO6T2gIhY5AwAgASAAIBahRMqUk6eRDuk9oDkDCEF9IQIMBAsgBEH7w+SABEYNASAaQgBZBEAgASAARAAAQFT7IRnAoCIARDFjYhphtPC9oCIWOQMAIAEgACAWoUQxY2IaYbTwvaA5AwhBBCECDAQLIAEgAEQAAEBU+yEZQKAiAEQxY2IaYbTwPaAiFjkDACABIAAgFqFEMWNiGmG08D2gOQMIQXwhAgwDCyAEQfrD5IkESw0BCyAAIABEg8jJbTBf5D+iRAAAAAAAADhDoEQAAAAAAAA4w6AiF0QAAEBU+yH5v6KgIhYgF0QxY2IaYbTQPaIiGKEiGUQYLURU+yHpv2MhAwJ/IBeZRAAAAAAAAOBBYwRAIBeqDAELQYCAgIB4CyECAkAgAwRAIAJBAWshAiAXRAAAAAAAAPC/oCIXRDFjYhphtNA9oiEYIAAgF0QAAEBU+yH5v6KgIRYMAQsgGUQYLURU+yHpP2RFDQAgAkEBaiECIBdEAAAAAAAA8D+gIhdEMWNiGmG00D2iIRggACAXRAAAQFT7Ifm/oqAhFgsgASAWIBihIgA5AwACQCAEQRR2IgMgAL1CNIinQf8PcWtBEUgNACABIBYgF0QAAGAaYbTQPaIiAKEiGSAXRHNwAy6KGaM7oiAWIBmhIAChoSIYoSIAOQMAIAMgAL1CNIinQf8PcWtBMkgEQCAZIRYMAQsgASAZIBdEAAAALooZozuiIgChIhYgF0TBSSAlmoN7OaIgGSAWoSAAoaEiGKEiADkDAAsgASAWIAChIBihOQMIDAELIARBgIDA/wdPBEAgASAAIAChIgA5AwAgASAAOQMIDAELIBpC/////////weDQoCAgICAgICwwQCEvyEAQQEhAwNAIAlBEGogAkEDdGoCfyAAmUQAAAAAAADgQWMEQCAAqgwBC0GAgICAeAu3IhY5AwAgACAWoUQAAAAAAABwQaIhAEEBIQIgA0EBcSEHQQAhAyAHDQALIAkgADkDIAJAIABEAAAAAAAAAABiBEBBAyEDDAELQQIhAgNAIAlBEGogAiIDQQFrIgJBA3RqKwMARAAAAAAAAAAAYQ0ACwsgCUEQaiEPIwBBsARrIgYkACAEQRR2QZYIayICQQNrQRhtIgRBACAEQQBKGyIQQWhsIAJqIQRBlIUEKAIAIgogAyIMQQFrIghqQQBOBEAgCiAMaiEDIBAgCGshAgNAIAZBwAJqIAVBA3RqIAJBAEgEfEQAAAAAAAAAAAUgAkECdEGghQRqKAIAtws5AwAgAkEBaiECIAVBAWoiBSADRw0ACwsgBEEYayEHQQAhAyAKQQAgCkEAShshBSAMQQBMIQsDQAJAIAsEQEQAAAAAAAAAACEADAELIAMgCGohDkEAIQJEAAAAAAAAAAAhAANAIA8gAkEDdGorAwAgBkHAAmogDiACa0EDdGorAwCiIACgIQAgAkEBaiICIAxHDQALCyAGIANBA3RqIAA5AwAgAyAFRiECIANBAWohAyACRQ0AC0EvIARrIRJBMCAEayEOIARBGWshEyAKIQMCQANAIAYgA0EDdGorAwAhAEEAIQIgAyEFIANBAEwiDUUEQANAIAZB4ANqIAJBAnRqAn8CfyAARAAAAAAAAHA+oiIWmUQAAAAAAADgQWMEQCAWqgwBC0GAgICAeAu3IhZEAAAAAAAAcMGiIACgIgCZRAAAAAAAAOBBYwRAIACqDAELQYCAgIB4CzYCACAGIAVBAWsiBUEDdGorAwAgFqAhACACQQFqIgIgA0cNAAsLAn8gACAHEOoBIgAgAEQAAAAAAADAP6KcRAAAAAAAACDAoqAiAJlEAAAAAAAA4EFjBEAgAKoMAQtBgICAgHgLIQggACAIt6EhAAJAAkACQAJ/IAdBAEwiFEUEQCADQQJ0IAZqIgIgAigC3AMiAiACIA51IgIgDnRrIgU2AtwDIAIgCGohCCAFIBJ1DAELIAcNASADQQJ0IAZqKALcA0EXdQsiC0EATA0CDAELQQIhCyAARAAAAAAAAOA/Zg0AQQAhCwwBC0EAIQJBACEFIA1FBEADQCAGQeADaiACQQJ0aiIVKAIAIQ1B////ByERAn8CQCAFDQBBgICACCERIA0NAEEADAELIBUgESANazYCAEEBCyEFIAJBAWoiAiADRw0ACwsCQCAUDQBB////AyECAkACQCATDgIBAAILQf///wEhAgsgA0ECdCAGaiINIA0oAtwDIAJxNgLcAwsgCEEBaiEIIAtBAkcNAEQAAAAAAADwPyAAoSEAQQIhCyAFRQ0AIABEAAAAAAAA8D8gBxDqAaEhAAsgAEQAAAAAAAAAAGEEQEEAIQUCQCAKIAMiAk4NAANAIAZB4ANqIAJBAWsiAkECdGooAgAgBXIhBSACIApKDQALIAVFDQAgByEEA0AgBEEYayEEIAZB4ANqIANBAWsiA0ECdGooAgBFDQALDAMLQQEhAgNAIAIiBUEBaiECIAZB4ANqIAogBWtBAnRqKAIARQ0ACyADIAVqIQUDQCAGQcACaiADIAxqIghBA3RqIANBAWoiAyAQakECdEGghQRqKAIAtzkDAEEAIQJEAAAAAAAAAAAhACAMQQBKBEADQCAPIAJBA3RqKwMAIAZBwAJqIAggAmtBA3RqKwMAoiAAoCEAIAJBAWoiAiAMRw0ACwsgBiADQQN0aiAAOQMAIAMgBUgNAAsgBSEDDAELCwJAIABBGCAEaxDqASIARAAAAAAAAHBBZgRAIAZB4ANqIANBAnRqAn8CfyAARAAAAAAAAHA+oiIWmUQAAAAAAADgQWMEQCAWqgwBC0GAgICAeAsiArdEAAAAAAAAcMGiIACgIgCZRAAAAAAAAOBBYwRAIACqDAELQYCAgIB4CzYCACADQQFqIQMMAQsCfyAAmUQAAAAAAADgQWMEQCAAqgwBC0GAgICAeAshAiAHIQQLIAZB4ANqIANBAnRqIAI2AgALRAAAAAAAAPA/IAQQ6gEhAAJAIANBAEgNACADIQIDQCAGIAIiBEEDdGogACAGQeADaiACQQJ0aigCALeiOQMAIAJBAWshAiAARAAAAAAAAHA+oiEAIAQNAAsgA0EASA0AIAMhAgNAIAMgAiIEayEHRAAAAAAAAAAAIQBBACECA0ACQCACQQN0QfCaBGorAwAgBiACIARqQQN0aisDAKIgAKAhACACIApODQAgAiAHSSEFIAJBAWohAiAFDQELCyAGQaABaiAHQQN0aiAAOQMAIARBAWshAiAEQQBKDQALC0QAAAAAAAAAACEAIANBAE4EQCADIQIDQCACIgRBAWshAiAAIAZBoAFqIARBA3RqKwMAoCEAIAQNAAsLIAkgAJogACALGzkDACAGKwOgASAAoSEAQQEhAiADQQBKBEADQCAAIAZBoAFqIAJBA3RqKwMAoCEAIAIgA0chBCACQQFqIQIgBA0ACwsgCSAAmiAAIAsbOQMIIAZBsARqJAAgCEEHcSECIAkrAwAhACAaQgBTBEAgASAAmjkDACABIAkrAwiaOQMIQQAgAmshAgwBCyABIAA5AwAgASAJKwMIOQMICyAJQTBqJAAgAgv+AwMDfAJ/AX4gAL0iBkIgiKdB/////wdxIgRBgIDAoARPBEAgAEQYLURU+yH5PyAApiAAvUL///////////8Ag0KAgICAgICA+P8AVhsPCwJAAn8gBEH//+/+A00EQEF/IARBgICA8gNPDQEaDAILIACZIQAgBEH//8v/A00EQCAEQf//l/8DTQRAIAAgAKBEAAAAAAAA8L+gIABEAAAAAAAAAECgoyEAQQAMAgsgAEQAAAAAAADwv6AgAEQAAAAAAADwP6CjIQBBAQwBCyAEQf//jYAETQRAIABEAAAAAAAA+L+gIABEAAAAAAAA+D+iRAAAAAAAAPA/oKMhAEECDAELRAAAAAAAAPC/IACjIQBBAwshBSAAIACiIgIgAqIiASABIAEgASABRC9saixEtKK/okSa/d5SLd6tv6CiRG2adK/ysLO/oKJEcRYj/sZxvL+gokTE65iZmZnJv6CiIQMgAiABIAEgASABIAFEEdoi4zqtkD+iROsNdiRLe6k/oKJEUT3QoGYNsT+gokRuIEzFzUW3P6CiRP+DAJIkScI/oKJEDVVVVVVV1T+goiEBIARB///v/gNNBEAgACAAIAMgAaCioQ8LIAVBA3QiBEGQhARqKwMAIAAgAyABoKIgBEGwhARqKwMAoSAAoaEiAJogACAGQgBTGyEACyAACxYAQfS0BEH8swQ2AgBBrLQEQSo2AgALmAYBBH9BASEJIAJBAXRBwNkCai8BACECIAVFBEAgACACNgIAQQEPCyACQbDkAmohBkESIQcCQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAFQQFrDiIAAAAAAAAAAQECAgICAgQDAwMDAwMFBQUFBQUFBQYHCAkJCwsgBiABIANrIAVsQQF0aiEBQQAhAgNAIAIgBUYEQCAFDwsgACACQQJ0aiABIAJBAXRqLwAAIgM2AgAgAkEBaiECIAMNAAsMCwsgBUEHayIIIAEgA2tsIQIgBCAIbEEBdCEBQQAhBwNAIAcgCEYNCiAGIAJBAnYgAWpqLQAAIAJBAXQiA0EGcXZBEHRBgIAMcSADIAZqLwAAciIDRQ0LIAAgB0ECdGogAzYCACAHQQFqIQcgAkEBaiECDAALAAsgBiAFQQlrIgggASADa2xqIQFBACECA0AgAiAIRg0JIAAgAkECdGogASACai0AABDdAiIDNgIAIAJBAWohAiADDQALDAkLIAVBAXEgBUEQayICQQFLaiEIIAJBAXZBAmohCQsgASADayEBQQAhAgNAIAIgCUYEQCAJDwUgACACQQJ0aiAGIAJBAXRqLwAAIAFBACACIAhGG2o2AgAgAkEBaiECDAELAAsACyAFQRVrIQcLIAcgASADa2wgBmpBAmohAUEAIQIgBi8AACEDA0AgAiAHRgRAIAcPBSAAIAJBAnRqQSAgASACai0AACIEIANqIARB/wFGGzYCACACQQFqIQIMAQsACwALIAAgBiABIANrQQNsaiIBLwAAIgI2AgAgAkUNAyAAIAEtAAIQ3QI2AgQMAgsgACAGLwAANgIAIAAgBi8AAjYCCCAAIAEgA2tBAXQgBmovAAQ2AgRBAw8LIAEgA2shAiAAAn8gBUEhRgRAIAYgAkF+cWoiAUEBaiEHIAEtAAAQ3QIMAQsgBiACQQF2QQNsaiIBQQJqIQcgAS8AAAsiAUEgQSBBASABQZAIa0EgSRsgAUGAAkkbaiABIAJBAXEbNgIAIAAgBy0AABDdAjYCBAtBAiEICyAIDwtBAAuPAgEGfyABQQJ0QaDiA2ooAgAiAiABQQF0QfDjA2ovAQBqIQdBACEBAkADQCACIAdPDQEgAkEBaiEFAkACQCACLQAAIgNBP00EQCAEIANBA3ZqQQFqIQIgAQRAIAAgBCACEH8NAwsgAUEBcyEBIANBB3EgAmpBAWohAwwBCwJ/IAMgBGpB/wBrIANBGHRBGHVBAEgNABogA0HfAE0EQCACQQJqIQUgAi0AASAEIANBCHRqakH//wBrDAELIAJBA2ohBSACLQACIAQgA0EQdGogAi0AAUEIdGpqQf///wJrCyEDIAQhAgsgAQRAIAAgAiADEH8NAQsgAUEBcyEBIAUhAiADIQQMAQsLQX8hBgsgBgujAgEIfyABQQZxIQYgAUECdkEBcSEJQdDDAyEDAkADQCADQYniA08NASACIQQgAy0AACICQR9xIQUCfyADQQFqIAJBBXYiAkEHRw0AGiADLAABIghB/wFxIQIgCEEATgRAIAJBB2ohAiADQQJqDAELIAhBv39NBEAgAy0AAiACQQh0ckH5/gFrIQIgA0EDagwBCyADLQADIAJBEHRyIAMtAAJBCHRyQfn+/gVrIQIgA0EEagshAyACIARqQQFqIQICQAJAIAVBH0YEQCAGRQ0DIAZBBkYNASAEIAlqIQQDQCACIARNDQQgACAEIARBAWoQfyEFIARBAmohBCAFRQ0ACwwCCyABIAV2QQFxRQ0CCyAAIAQgAhB/RQ0BCwtBfyEHCyAHCzgAQYC2AiABELUDIgFBAEgEQEF+DwsgACABQR1NBH9CASABrYanBSABQQJ0Qai6AmooAgALELYEC7gCAQl/IwBB0ABrIgYkACACQQAgAkEAShshDANAIAcgDEcEQAJAIAEgB0ECdGooAgAiAkGA2AJrIgRBo9cATQRAIAAgBEH//wNxIgJBzARuIgVBgCJyEB4gACAEIAVBzARsa0H//wNxQRxuQeEiahAeIAJBHHAiAkUNASAAIAJBpyNqEB4MAQtBACEEQQAhCkG0BSEFAkADQCAEIAVKDQEgAiAEIAVqQQJtIghBAnRB4MMCaigCACIJQQ52IgtJBEAgCEEBayEFDAELIAIgCUEHdkH/AHEiBCALak8EQCAIQQFqIQQMAQsLIAlBAXEgA0sNACAGIAIgCCALIAQgCUEBdkE/cRC0BCEKCyAKIgQEQCAAIAYgBCADELgEDAELIAAgAhAeCyAHQQFqIQcMAQsLIAZB0ABqJAALEQAgAEGQ+QFB0IECQSIQ4QILtQEBB38gACgCACEFIAAoAgghAgNAIAFBAWoiAyAFTkUEQAJAIAIgAUECdGooAgAiByACIANBAnRqKAIARgRAIAEhAwwBCwNAAkAgASIDQQFqIQYgAUEDaiAFTg0AIAIgBkECdGooAgAgAiADQQJqIgFBAnRqKAIARg0BCwsgAiAEQQJ0aiIBIAc2AgAgASACIAZBAnRqKAIANgIEIARBAmohBAsgA0ECaiEBDAELCyAAIAQ2AgALEQAgAEHw8gFBwPgBQRcQ4QILpQEBA38gASACEN4CQf///wBxSQRAIABBADYCAEEADwtBfyEEIAIgA0EBayIFQQNsahDeAiABSwR/QQAhAwNAIAUgA2tBAkhFBEAgAyAFakECbSIEIAUgAiAEQQNsahDeAkH///8AcSABSyIGGyEFIAMgBCAGGyEDDAELCyAAIAIgA0EDbGoQ3gIiAEH///8AcTYCACADQQV0IABBFXZqQSBqBUF/CwtuAQV/QfECIQEDQCABIAJOBEAgACABIAJqQQF2IgNBAnRBwOEBaigCACIEQQ92IgVJBEAgA0EBayEBDAILIAAgBEEIdkH/AHEgBWpJBEBBAQ8FIANBAWohAgwCCwALCyAAQZDxAUHQ8gFBBhDhAgtJAQF/An8gACgCACICIAAoAgROBEBBfyAAIAJBAWoQ4AINARogACgCACECCyAAIAJBAWo2AgAgACgCCCACQQJ0aiABNgIAQQALCzUBAX8jAEEQayIDJAAgAyABNgIIIAMgAkEBajYCDCAAIANBCGpBAhC2AyEAIANBEGokACAAC5cCAQN/IAEoAgAiAkH+/wdPBEAgAEHdJkEAED9Bfw8LAkAgAkEBTQRAIABBAkF/ELoBGgwBCyABKAIIIAJBAnRqIgRBBGsoAgAiA0F/RgRAIARBCGsoAgAhAwsgAkEBdiECIANB//8DTQRAIABBFSACELgDQQAhAgNAIAIgASgCAE4NAiAAIAJBAnQiAyABKAIIai8BABAxIABBfyABKAIIIANBBHJqKAIAQQFrIgMgA0F+RhtB//8DcRAxIAJBAmohAgwACwALIABBFiACELgDQQAhAgNAIAIgASgCAE4NASAAIAJBAnQiAyABKAIIaigCABAeIAAgASgCCCADQQRyaigCAEEBaxAeIAJBAmohAgwACwALQQALJgEBfyAAKAI4IgFBAEgEQCAAIAAgAEE8akEAEMIEIgE2AjgLIAEL4AIBBX8jAEGQAWsiBCQAIAFBADYCACAAKAIgIQNBASEGA0AgBCADNgKMAQJAAkACQCAAKAIcIgcgA00EQCAGIQUMAQsCQAJAAkACQCADLQAAIgVB2wBrDgIBAgALIAVBKEcNBSADLQABQT9HDQIgAy0AAkE8Rw0FIAMtAAMiBUEhRiAFQT1Gcg0FIAFBATYCAAJAIAJFDQAgBCADQQNqNgKMASAEIARBjAFqIAAoAigQuwMNACAEIAIQrARFDQULIAZBAWohBSAGQf0BSg0DIAQoAowBIQMgBSEGDAULA0AgBCADIgVBAWoiAzYCjAEgAyAHTw0FAkAgAy0AAEHcAGsOAgAGAQsgBCAFQQJqIgM2AowBDAALAAsgBCADQQFqIgM2AowBDAMLIAZB/QFKIQcgBkEBaiIFIQYgB0UNAgtBfyAFIAIbIQYLIARBkAFqJAAgBg8LIANBAWohAwwACwALXQEEfyABEEMhAyAAKAJEIgIgACgCSGohBEEBIQADQAJAIAIgBE8EQEF/IQAMAQsgAyACEEMiBUYEQCABIAIgAxB3RQ0BCyAAQQFqIQAgAiAFakEBaiECDAELCyAAC+EaAQh/IAAoAgQhDiAAKAIIIQwDQAJAIAUhByAEQQFqIQgCQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAn8CQAJAAkAgBC0AACINQQFrDhwCAQkKBwgGBAQACwsMDw0OEhITExoZBQUQERgXFgtBASEEIAZFDR8gByEEDCALQQUhCSAIKAAADAELQQMhCSAILwAACyEIIAcgDk8NGwJAIAxFBEAgB0EBaiEFIActAAAhCgwBCyAHLwEAIgpBgPgDcUGAsANHIAxBAkdyIA4gB0ECaiIFTXINACAFLwEAIgtBgPgDcUGAuANHDQAgCkEKdEGA+D9xIAtB/wdxckGAgARqIQogB0EEaiEFCyAEIAlqIQQgACgCGAR/IAogACgCHBDNAQUgCgsgCEYNHgwbCyAEQQVqIgkgCSAIKAAAaiIIIA1BCUYiChshBCAAIAEgAiADIAggCSAKGyAHQQBBABC9A0EATg0dDBkLIAAgASACIAMgBEEFaiIEIAgoAABqIAcgDUEWa0EAEL0DQQBODRwMGAsgCCAIKAAAakEEaiEEDBYLIAghBCAFIAAoAgAiCEYNGiAAKAIURQ0XAkAgDEUEQCAFQQFrLQAAIQsMAQsgBUECay8BACILQYD4A3FBgLgDRyAMQQJHcg0AIAggBUEEayIHSw0AIAcvAQAiCEGA+ANxQYCwA0cNACALQf8HcSAIQf8HcUEKdHJBgIAEaiELCyALELwDDRoMFwsgCCEEIAcgDiIFRg0ZIAAoAhRFDRYCQCAMRQRAIActAAAhCQwBCyAHLwEAIglBgPgDcUGAsANHIAxBAkdyIAdBAmogDk9yDQAgBy8BAiIFQYD4A3FBgLgDRw0AIAlBCnRBgPg/cSAFQf8HcXJBgIAEaiEJCyAHIQUgCRC8Aw0ZDBYLIAcgDkYNFQJAIAxFBEAgB0EBaiEFIActAAAhCQwBCyAHLwEAIglBgPgDcUGAsANHIAxBAkdyIA4gB0ECaiIFTXINACAFLwEAIgRBgPgDcUGAuANHDQAgCUEKdEGA+D9xIARB/wdxckGAgARqIQkgB0EEaiEFCyAIIQQgCRC8A0UNGAwVCyAHIA5GDRQgDEUEQCAHQQFqIQUgCCEEDBgLIAghBCAHLwEAQYD4A3FBgLADRyAMQQJHciAOIAdBAmoiBU1yDRcgB0EEaiAFIAcvAQJBgPgDcUGAuANGGyEFDBcLIAgtAAAiBSAAKAIMTw0JIA0gBUEBdGpBAnQgAWpBLGsgBzYCACAEQQJqIQQMEQsgBC0AAiIJIAAoAgxPDQcgBEEDaiEEIAgtAAAhBQNAIAUgCUsNESABIAVBA3RqQgA3AgAgBUEBaiEFDAALAAsgAiADQQJ0aiAIKAAANgIAIANBAWohAyAEQQVqIQQMDwsgA0EBayEDDA0LIAgoAAAhBSADQQJ0IAJqQQRrIgggCCgCAEEBayIINgIAIAQgBUEAIAgbakEFaiEEDA0LIAIgA0ECdGogBzYCACADQQFqIQMMCwsgBEEAIAgoAAAgAiADQQFrIgNBAnRqKAIAIAdGG2pBBWohBAwLC0EAIQlBACELIAAoAgAiBCAHRwRAAkAgDEUEQCAHQQFrLQAAIQUMAQsgB0ECay8BACIFQYD4A3FBgLgDRyAMQQJHcg0AIAQgB0EEayIKSw0AIAovAQAiBEGA+ANxQYCwA0cNACAFQf8HcSAEQf8HcUEKdHJBgIAEaiEFCyAFEOMCIQsLIAcgDkkEQAJAIAxFBEAgBy0AACEFDAELIAcvAQAiBUGA+ANxQYCwA0cgDEECR3IgB0ECaiAOT3INACAHLwECIgRBgPgDcUGAuANHDQAgBUEKdEGA+D9xIARB/wdxckGAgARqIQULIAUQ4wIhCQsgByEFIAghBEESIA1rIAkgC3NGDQ8MDAsgBC0AASIIIAAoAgxPDQsgBEECaiEEIAEgCEEDdGoiCCgCACIKRQ0OIAgoAgQiC0UNDiANQRNGDQcDQCAKIAtPDQ8gBSAAKAIAIg1GDQwCQAJ/AkAgDARAIAtBAmsiBy8BACIIQYD4A3FBgLgDRyAMQQJHciAHIApNcg0BIAdBAmsvAQAiCUGA+ANxQYCwA0cNASAIQf8HcSAJQf8HcUEKdHJBgIAEaiEIIAtBBGsMAgsgBUEBayIFLQAAIQkgC0EBayILLQAAIQgMAgsgBwshCwJAIAVBAmsiBy8BACIJQYD4A3FBgLgDRyAMQQJHciAHIA1Ncg0AIAdBAmsvAQAiDUGA+ANxQYCwA0cNACAJQf8HcSANQf8HcUEKdHJBgIAEaiEJIAVBBGshBQwBCyAHIQULIAAoAhgEQCAIIAAoAhwQzQEhCCAJIAAoAhwQzQEhCQsgCCAJRg0ACwwLC0G+F0HV4wBB3RFBvMAAEAAAC0GnF0HV4wBB1BFBvMAAEAAACxABAAsgBEERaiINIAgoAABqIQdBACEJIAQoAAUhCiAEKAAJIQsDQAJAAkAgACABIAIgAyANIAVBARDEBCIEQQFqDgIMAQALIAQhBSALQf////8HRiALIAlBAWoiCUtyDQELCyAJIApJDQcgByEEIAkgCk0NCiAAIAEgAiADIAggBUEDIAkgCmsQvQNBAE4NCgwGCyAHIAAoAgAiCUYNBiAMRQRAIAdBAWshBSAIIQQMCgsgB0ECayEFIAghBCAMQQJHDQkgBS8BAEGA+ANxQYC4A0cgBSAJTXINCSAHQQRrIAUgBUECay8BAEGA+ANxQYCwA0YbIQUMCQsgCC8AACELIAcgDk8NBQJAIAxFBEAgB0EBaiEFIActAAAhCQwBCyAHLwEAIglBgPgDcUGAsANHIAxBAkdyIA4gB0ECaiIFTXINACAFLwEAIghBgPgDcUGAuANHDQAgCUEKdEGA+D9xIAhB/wdxckGAgARqIQkgB0EEaiEFCyAAKAIYBEAgCSAAKAIcEM0BIQkLIAkgBEEDaiIHKAAASQ0FQQAhCiAJIAQgC0EBayIIQQN0aigAB0sNBQNAIAggCkkNBiAJIAcgCCAKakEBdiIEQQN0aiINKAAASQRAIARBAWshCAwBCyAJIA0oAARLBEAgBEEBaiEKDAELCyAHIAtBA3RqIQQMCAsgCC8AACEKIAcgDk8NBAJAIAxFBEAgB0EBaiEFIActAAAhCQwBCyAHLwEAIglBgPgDcUGAsANHIAxBAkdyIA4gB0ECaiIFTXINACAFLwEAIghBgPgDcUGAuANHDQAgCUEKdEGA+D9xIAhB/wdxckGAgARqIQkgB0EEaiEFCyAAKAIYBEAgCSAAKAIcEM0BIQkLIAkgBEEDaiIHLwAASQ0EAkAgBCAKQQFrIghBAnRqLwAFIgtB//8DRiAJQf//A09xDQBBACEEIAkgC0sNBQNAIAQgCEsNBiAJIAcgBCAIakEBdiILQQJ0aiINLwAASQRAIAtBAWshCAwBCyAJIA0vAAJNDQEgC0EBaiEEDAALAAsgByAKQQJ0aiEEDAcLA0AgCiALTw0HIAUgDk8NBAJ/An8CQCAMBEAgCi8BACIIQYD4A3FBgLADRyAMQQJHciAKQQJqIgcgC09yDQEgBy8BACIJQYD4A3FBgLgDRw0BIAhBCnRBgPg/cSAJQf8HcXJBgIAEaiEIIApBBGoMAgsgBS0AACEJIAotAAAhCCAKQQFqIQogBUEBagwCCyAHCyEKAkAgBS8BACIJQYD4A3FBgLADRyAMQQJHciAFQQJqIgcgDk9yDQAgBy8BACINQYD4A3FBgLgDRw0AIAlBCnRBgPg/cSANQf8HcXJBgIAEaiEJIAVBBGoMAQsgBwshBSAAKAIYBEAgCCAAKAIcEM0BIQggCSAAKAIcEM0BIQkLIAggCUYNAAsMAwsgCCEEDAULIAchBQwEC0F/DwtBACEEIAYNAQsgACgCMCEFAkADQCAFRQ0CAkACQAJAAkACQCAAKAIoIAVBAWsiBSAAKAIkbGoiBy0AACIDDgQAAgIBAgtBASEIIAQNAgwDC0EBIQggBA0BIAEgB0EQaiIDIAAoAgxBA3QQJRogAiADIAAoAgxBA3RqIActAAEiA0ECdBAlGiAHKAIIIQVBACEEIAcoAgwiCSgADCEKA0ACfwJAIAQgCkcEQCAFQQFrIAxFDQIaIAVBAmshCCAMQQJHDQEgCC8BAEGA+ANxQYC4A0cNASAIIAAoAgBNDQEgBUEEayAIIAhBAmsvAQBBgPgDcUGAsANGGwwCCyAJKAAAIQQgByAFNgIIIAcgBygCBEEBayIINgIEIAQgCWpBEGohBCAIDQkgACAAKAIwQQFrNgIwDAkLIAgLIQUgBEEBaiEEDAALAAtBACEIIARBAEciBCADQQFGIglxQQEgBCADQQJHchtFDQAgCUUNAQwDCyAAIAU2AjAgCCEEDAELCyABIAdBEGogACgCDEEDdBAlGgsgBygCCCEFIAcoAgwhBCACIAcgACgCDEEDdGpBEGogBy0AASIDQQJ0ECUaIAAgACgCMEEBazYCMAwBCwsgBAucAgEEfyMAQUBqIgckACAHIAEtAAAiCEEBdkEBcTYCICAHIAhBAnZBAXE2AhwgByAIQQR2QQFxIgg2AiQgByABLQABIgk2AhQgAS0AAiEKIAdBADYCOCAHIAY2AiggByAFQQIgBSAIGyAFQQFHGzYCECAHIAIgBCAFdGo2AgwgByACNgIIIAcgCjYCGCAHQgA3AzAgByAKQQJ0IgYgCUEDdGpBEGo2AiwgCUEBdCEEQQAhCANAIAQgCEZFBEAgACAIQQJ0akEANgIAIAhBAWohCAwBCwsgByAGQQ9qQfAPcWsiBCQAIAdBCGogACAEQQAgAUEHaiACIAMgBXRqQQAQxAQhACAHKAIoIAcoAjBBABCFBBogB0FAayQAIAALiiEBEn8gACgCBCEQA0BBACEDAkACQCAAKAIYIgIgACgCHE8NACACLQAAIgJBKUYgAkH8AEZyDQAgACgCBCESQQAhBEEAIQlBACEGIwBBIGsiBSQAIAUgACgCGCICNgIcAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAItAAAiA0Ekaw4LAQkJCQQJEhIJCQIACwJAAkAgA0HbAGsOBAcGCAEACyADQfsAaw4DAwkHCAsgBSACQQFqNgIcIABBBRBfDA4LIAUgAkEBajYCHCAAQQYQXwwNCyAFIAJBAWo2AhwgACgCNCEJIAAoAgQhAyABRQ0JIABBGxBfIABBBEEDIAAoAjAbEF8gAEEbEF8MCwsgACgCKARAIABBkitBABA/DBALIAItAAEQRUUNBSAFIAJBAWo2AgggBUEIakEBEKsCGgJAIAUoAggiAy0AACICQSxHDQAgBSADQQFqNgIIIAMtAAEiAhBFRQ0AIAVBCGpBARCrAhogBSgCCC0AACECCyACQf8BcUH9AEcNBQwOCwJAIAItAAFBP0YEQEEDIQdBACEDAkACQAJAAkAgAi0AAiIEQTprDgQAAwEOAgsgACACQQNqNgIYIAAoAjQhCSAAKAIEIQNBfyECIAAgARDkAg0UIAUgACgCGDYCHCAAIAVBHGpBKRDiAkUNDgwUC0EBIQNBBCEHIAItAAMiBEE9RgRAQQEhBgwNC0EBIQYgBEEhRg0MIAUgAkEDajYCHCAAQdwAaiIDIAVBHGogACgCKBC7AwRAIABBgc8AQQAQPwwTCyAAIAMQwwRBAEoEQCAAQezOAEEAED8MEwsgAEHEAGogAyADEENBAWoQigEaIABBATYCPAwDCyAEQSFGDQsLIABB6DNBABA/DBALIAUgAkEBajYCHCAAQcQAakEAEBALIAAoAjQiCUH/AU4EQCAAQYElQQAQPwwPCyAAIAlBAWo2AjQgACgCBCEDIAAgAUELaiAJEK0CIAAgBSgCHDYCGEF/IQIgACABEOQCDQ8gBSAAKAIYNgIcIABBDCABayAJEK0CIAAgBUEcakEpEOICRQ0JDA8LAkACQAJAAkACQAJAAkAgAi0AASIDQTBrDhMDBAQEBAQEBAQECgoKCgoKCgoBAAsgA0HrAEYNASADQeIARw0JCyAAQRFBEiADQeIARhsQXyAFIAJBAmo2AhwMDgsCQCACLQACQTxHBEBB184AIQIgACgCKA0BIAAQugMNAQwJCyAFIAJBA2o2AgggAEHcAGoiAyAFQQhqIAAoAigQuwMEQEGBzwAhAiAAKAIoDQEgABC6Aw0BDAkLIAAgAxDDBCIEQQBODQMgACAFQQRqIAMQwgQiBEEATg0DQcThACECIAAoAigNACAAELoDRQ0ICyAAIAJBABA/DBELIAUgAkECajYCHCACLQACIQMgACgCKARAIAMQRUUNCSAAQcM3QQAQPwwRCyADQfgBcUEwRw0IIAUgAkEDajYCHCACLQACQTBrIQQgAi0AA0H4AXFBMEcNCCAFIAJBBGo2AhwgAi0AAyAEQQN0akEwayEEDAgLIAUgAkEBaiIDNgIcIAVBHGpBABCrAiIEQQBOBEAgBCAAKAI0SA0CIAAQwQQgBEoNAgsgACgCKEUEQCAFIAM2AhwgAy0AACIJQTdLDQdBACEEIAlBM00EQCAFIAJBAmoiAzYCHCACLQACIQkgAi0AAUEwayEECyAJQfgBcUEwRw0IIAUgA0EBajYCHCADLQAAIARBA3RqQTBrIQQgAy0AAUH4AXFBMEcNCCAFIANBAmo2AhwgAy0AASAEQQN0akEwayEEDAgLIABB8DdBABA/DA8LIAUgBSgCCDYCHAsgACgCNCEJIAAoAgQhAyAAIAFBE2ogBBCtAgwICyAAKAI0IQkgACgCBCEDIAEEQCAAQRsQXwtBfyECIwBBQGoiBiQAIAZBKGogACgCQEHsAhCIASAGIAUoAhwiCEEBaiIENgI8IAgtAAEiC0HeAEYEQCAGIAhBAmoiBDYCPAsCfwJAA0ACQAJAIAQtAABB3QBHBEAgACAGQRBqIAZBPGpBARC5AyIIQQBIDQQCQAJAAkACQCAGKAI8IgQtAABBLUcNACAELQABQd0ARg0AIAYgBEEBajYCDCAIQYCAgIAETwRAIAAoAihFDQEgBkEQahBSDAMLIAAgBkEQaiAGQQxqQQEQuQMiB0EASA0IIAdBgICAgARJDQEgBkEQahBSIAAoAigNAgsgCEGAgICABEkNAiAGQShqIAYoAhggBigCEBC2AyEIIAZBEGoQUiAIRQ0GDAULIAYgBigCDCIENgI8IAcgCE8NAwsgAEH60gBBABA/DAULIAZBKGogCCAIEL8ERQ0DDAILIAAoAiwEQCMAQSBrIggkACAIQQhqIgcgBkEoaiIKKAIMQewCEIgBIAhC4YCAgLAPNwIAIAcgCigCCCAKKAIAIAhBAkEBEKoCIgdFBEBBACEHIAgoAhAhDANAIAgoAggiDSAHSgRAIAwgB0ECdGoiDSANKAIAQSBrNgIAIAdBAWohBwwBCwsgCiAMIA0QtgMhBwsgCEEIahBSIAhBIGokACAHDQILIAtB3gBGBEAgBkEoahCpAg0CCyAAIAZBKGoQwAQNAyAGQShqEFIgBSAEQQFqNgIcQQAMBAsgBkEoaiAIIAcQvwRFDQELCyAAEKwCCyAGQShqEFJBfwshBCAGQUBrJAAgBA0NIAFFDQcgAEEbEF8MBwsgACgCKEUNASAAQZIrQQAQPwwLCyADQT9GDQkLIAAgBUEIaiAFQRxqQQAQuQMiBEEATg0BDAkLIAUgAkECajYCHCACLQABIQQLIAAoAjQhCSAAKAIEIQMgAQRAIABBGxBfCwJAIARBgICAgAROBEAgACAFQQhqIgIQwAQhBiACEFJBfyECIAZFDQEMCgsgACgCLARAIAQgACgCKBDNASEECyAEQf//A0wEQCAAQQEgBBC4AwwBCyAAQQIgBBC6ARoLIAFFDQIgAEEbEF8MAgsgAEEEQQMgACgCMBsQXwwBCyACIAdqIQhBfyECAn9BfyADDQAaQX8gACgCKA0AGiAAKAI0IQkgACgCBAshAyAAQRhBFyAEQSFGG0EAELoBIQQgACAINgIYIAAgBhDkAg0GIAUgACgCGDYCHCAAIAVBHGpBKRDiAg0GIABBChBfIAAoAgwNBiAAKAIAIARqIAAoAgQgBGtBBGsQXQsgA0EASA0AAkACQAJAAkACQAJAIAUoAhwiAi0AACIGQSprDgIBAgALIAZBP0YNAiAGQfsARw0FIAItAAEQRQ0DIAAoAihFDQUMBwsgBSACQQFqIgI2AhxBACEEQf////8HIQgMAwtBASEEIAUgAkEBaiICNgIcQf////8HIQgMAgtBASEIIAUgAkEBaiICNgIcQQAhBAwBCyAFIAJBAWo2AhwgBUEcakEBEKsCIgQhCAJAIAUoAhwiBy0AACIGQSxHDQAgBSAHQQFqNgIcIActAAEiBhBFRQRAQf////8HIQgMAQsgBUEcakEBEKsCIgggBEgNBCAFKAIcLQAAIQYLAkAgBkH9AEYNACAAKAIoDQAgBSACNgIcDAILQX8hAiAAIAVBHGpB/QAQ4gINBiAFKAIcIQILAn8gAi0AAEE/RgRAIAUgAkEBajYCHCAAKAIEIANrIQdBACEGQQAMAQsCQCAIQQBMDQAgACgCDA0DIAAoAgAgA2ohCyAAKAIEIANrIQ1BACEHQQAhAgNAAkAgByANTgRAIAIhBgwBC0F/IQYgByALaiIOLQAAIg9BoOEBai0AACEKQQIhDAJAAkACQAJAIA9BAWsOFgICAgIDAwQEBAQEBAQEBAQDAwQEAQAEC0EDIQwLIA4vAAEgDHQgCmohCgsgAkEBaiECCyAHIApqIQcMAQsLIAYiAkEATA0AIABBChBfIAAgA0EREOsBDQMgACgCACADakEcOgAAIAMgACgCAGpBAWogACgCBCADa0ERaxBdIAMgACgCAGpBBWogBBBdIAMgACgCAGpBCWogCBBdIAMgACgCAGpBDWogAhBdDAILIAAoAgwNAkEBIQYgACgCACADaiETIAAoAgQgA2shB0EAIQ1BACEPIwBBgAJrIgIkACACQQBB/wEQSyEMQX4hCgNAIAcgDUoEQCANIBNqIgstAAAiEUGg4QFqLQAAIQ5BAiECAkACQAJAAkACQAJAAkACQCARQQFrDhsCAgICBwcGBgYGAwMEBgcHBwcFBQEABgYHBgcGC0EDIQILIAsvAAEgAnQgDmohDgtBASAKIApBfkYbIQoMBAsgDCALLQABaiICIAItAABBAXI6AAAMAwsgCy0AASICIAstAAIiCyACIAtLGyELA0AgAiALRg0DIAIgDGoiESARLQAAQQFyOgAAIAJBAWohAgwACwALQQEhDyAMIAstAAFqIgIgAi0AAEECcjoAAAwBC0EAIAogCkF+RhshCgsgDSAOaiENDAELCwJ/AkAgD0UNAEEAIQIDQCACQf8BRg0BIAIgDGohCyACQQFqIQIgCy0AAEEDRw0AC0F/DAELQQAgCiAKQX5GGwshAiAMQYACaiQAIAJFCyECAkAgBEUEQCAAKAI0IAlHBEAgACADQQMQ6wENBCAAKAIAIANqQQ06AAAgAyAAKAIAaiAJOgABIAMgACgCAGogAC0ANEEBazoAAiADQQNqIQMLAkACQAJAIAgOAgABAgsgACADNgIEDAQLIAAgA0EFEOsBDQQgACgCACADaiAGQQhyOgAAIAAoAgAgA2pBAWogBxBdDAMLIAhB/////wdGDQEgACADQQoQ6wENAyAAKAIAIANqQQ86AAAgAyAAKAIAakEBaiAIEF0gA0EFaiICIAAoAgBqIAZBCHI6AAAgAyAAKAIAakEGaiAHQQVqEF0gAEEOIAIQzAEgAEEQEF8MAgsgAiAEQQFHIAhB/////wdHcnJFBEAgAEEJIAZrIAMQzAEMAgsgBEEBRwRAIAAgA0EFEOsBDQMgACgCACADakEPOgAAIAAoAgAgA2pBAWogBBBdIABBDiADQQVqIgMQzAEgAEEQEF8LIAhB/////wdGBEAgACgCBCEEIAAgBkEIciACIAdqQQVqELoBGiACBEAgAEEZEF8gACADIAcQvwMgAEEaIAQQzAEMAwsgACADIAcQvwMgAEEHIAQQzAEMAgsgBCAITg0BIABBDyAIIARrELoBGiAAKAIEIQIgACAGQQhyIAdBBWoQugEaIAAgAyAHEL8DIABBDiACEMwBIABBEBBfDAELIAAgAyACQQVqEOsBDQEgACgCACADaiAGQQhyOgAAIAAoAgAgA2pBAWogAiAHakEFahBdIAIEQCADIAAoAgBqQRk6AAUgAEEaIAMQzAEMAQsgAEEHIAMQzAELIAAgBSgCHDYCGEEAIQIMBAsgABCsAgwCCyAAQesXQQAQPwwBCyAAQdkdQQAQPwtBfyECCyAFQSBqJAAgAiIDDQAgAUUNAiAAIAAoAgQiAyASayICIANqEM4BRQ0BQX8hAwsgAw8LIAAoAgAgEGoiBiACaiAGIAMgEGsQgQIgACgCACIGIBBqIAMgBmogAhAlGgwACwALCQAgASACEPIFC5UBAQN+IAG9IgJC////////////AIMhAyAAvSIEQv///////////wCDQoGAgICAgID4/wBaBEAgA0KBgICAgICA+P8AVA8LAn9BfyADQoCAgICAgID4/wBWIAAgAWNyDQAaQQEgACABZA0AGkEAIABEAAAAAAAAAABiDQAaIARCAFMEQCACQj+Hp0F/cw8LIAJCP4inCwujAQEBfgJAAkAgAkUEQCAAQS8QMiEEIAEQEiECDAELIAMpAwAhBAJ+AkAgARASIgJFDQAgBBD2A0UNACAAQdf5ACAAIAAoAhAgBKcQ1gIQMkHJ+QAQvwEMAQsgACAEEC4LIgQQDQ0BCyACDQAgACABQQUQbyIBEA1FBEAgACABIAQQzwEgACABQTAgBKcpAgRC/////weDQQAQGxoLIAEhBAsgBAtKAgF/AX5CgICAgOAAIQQgACABIAIQmwEiAwR+IAMQmgEEQCACRQRAQgAPCyAAEHVCgICAgOAADwsgAygCIDUCEAVCgICAgOAACwsqACAAIAEgAhCbASIARQRAQoCAgIDgAA8LIAAoAiA1AgxCgICAgHCEEA8LRgEBfwJAIAAoAgggAmoiAyAAKAIMSgRAIAAgAyABENUCDQELA0AgAkEATARAQQAPCyACQQFrIQIgACABEJYBRQ0ACwtBfwt4AQV/IAEoAgRB/////wdxIgNFBEAgAg8LIAAoAgRB/////wdxIQUgA0EBayEGIAFBABBNIQcCQANAIAIgA2ogBUoNASAAIAcgAhDZASIEQQBIIAMgBGogBUpyDQEgACABIARBAWoiAkEBIAYQwgMNAAsgBA8LQX8LggEBAn8CQAJAIAAgARCDBCIDQQBIDQAgA0UNASAAIAFB7QAgAUEAEBQiARANDQBBiRwhAgJAIAEQEg0AIAEQKA0AIAAgARA9IgEQDQ0BQQAhAiABp0HnAEEAENkBIQMgACABEAwgA0EATg0CQeXFACECCyAAIAJBABAWC0F/IQILIAILXAEBfwJAAkACQAJAIAFCIIinQQFqDgMBAgACCyABEA8PCyABpyICLwEGQQZHDQAgAikDICIBQoCAgIBwg0KAgICAEFENAQsgAEHWO0EAEBZCgICAgOAAIQELIAELEABBvv4AIABBCxClAkEARwtdAQJ/QbH+ACEDAkACQCABKAIEQf////8HcSIEIAJMDQAgASACEE1BJUcNAEGxGSEDIAJBAmogBE4NACABIAJBAWpBAhDDAyICQQBODQELIAAgAxDEA0F/IQILIAILVAAjAEEQayICJAAgACACQQhqIAMpAwAQRwR+QoCAgIDgAAUgAikDCEKAgICAgICA+P8Ag0KAgICAgICA+P8AUq1CgICAgBCECyEBIAJBEGokACABC1QAIwBBEGsiAiQAIAAgAkEIaiADKQMAEEcEfkKAgICA4AAFIAIpAwhC////////////AINCgICAgICAgPj/AFatQoCAgIAQhAshASACQRBqJAAgAQv4AgIDfwN+IwBBMGsiCCQAIANCACADQgBVGyENIAVBAWshCiAFQQBMIQVCACEDA0ACQCADIA1RBEAgBCEMDAELQn8hDCAAIAIgAyAIQShqEIwBIglBAEgNAAJAIAlFDQAgBhASRQRAIAggCCkDKDcDACADIQsgA0KAgICACFoEQCADuRAXIQsLIAggAjcDECAIIAs3AwggCCAAIAYgB0EDIAgQJCILNwMoIAAgCCkDABAMIAAgCCkDCBAMIAsQDQ0CCwJAAkACQCAFDQAgACAIKQMoIgsQwgEiCUEASA0BIAlFDQAgACAIQSBqIAsQQUEASA0BIAAgASALIAgpAyAgBCAKQoCAgIAwQoCAgIAwENQEIgRCAFMNASAAIAsQDAwDCyAEQv////////8PUw0BIABBi8MAQQAQFiAIKQMoIQsLIAAgCxAMDAILIAAgASAEIAgpAygQcEEASA0BIARCAXwhBAsgA0IBfCEDDAELCyAIQTBqJAAgDAsMACAAQgAgAEIAVRsLKAACQCABEBJFBEAgARAoRQ0BCyAAIAEQPQ8LIAAgAUE4QQBBABC6AgujAgIGfwF+IwBBMGsiAiQAAkACQCADKQMAIgEQIkUNAEKAgICA4AAhCyAAIAEQmQQiA0EASA0BIANFBEAgAEHdygBBABAWDAILIAAgAkEsaiACQShqIAGnIglBAxCSAQ0BIAIoAiwhBiACKAIoIQdBACEDAkADQCADIAdHBEAgBiADQQN0aigCBCEIQYCCASEFAkAgBEUNACAAIAJBCGogCSAIEE8iCkEASA0DIApFDQAgAigCCCEFIAAgAkEIahBOQYCGAUGAggEgBUECcRshBQsgACABIAhCgICAgDBCgICAgDBCgICAgDAgBRB4QQBIDQIgA0EBaiEDDAELCyAAIAYgBxBmDAELIAAgBiAHEGYMAQsgARAPIQsLIAJBMGokACALC+sBAQF+AkACQCABECgEQCAAQek9EHYhBAwBCyABEBIEQCAAQf/gABB2IQQMAQsgACABECsiARANDQEgACABEMIBIgNBAEgEQCAAIAEQDEKAgICA4AAPCwJ/QY0BIAMNABpBlwEgACABEDsNABpBjAEgAacvAQYiA0ESS0EBIAN0QfiOEHFFcg0AGiAAKAIQKAJEIANBGGxqKAIECyECIAAgAUHJASABQQAQFCEEIAAgARAMQoCAgIDgACEBIAQQDQ0BIAQQngENACAAIAQQDCAAIAIQMiEECyAAQdf+ACAEQYLnABC/ASEBCyABC5YDAQF+IwBBIGsiAiQAIAMpAwAhAQJAAkACQCAEBEAgAUL/////b1gEQCAAECkMAwsgARAPIQUMAQsgACABECsiBSEBIAUQDQ0CCwJAIAAgAykDCBA4IgNFDQBCgICAgDAhAQJAAkAgBUKAgICAcFQNACAAIAIgBacgAxBPIgRBAEgNAiAERQ0AIAAQPCIBEA0NAQJAIAItAABBEHEEQCAAIAFBwQAgAikDEBAPQYeAARAbQQBIDQMgACABQcIAIAIpAxgQD0GHgAEQG0EATg0BDAMLIAAgAUHAACACKQMIEA9Bh4ABEBtBAEgNAiAAIAFBPiACNQIAQgGIQgGDQoCAgIAQhEGHgAEQG0EASA0CCyAAIAFBPyACNQIAQgKIQgGDQoCAgIAQhEGHgAEQG0EASA0BIAAgAUE9IAI1AgBCAYNCgICAgBCEQYeAARAbQQBIDQEgACACEE4LIAAgAxATIAAgBRAMDAMLIAAgAhBOIAAgARAMCyAAIAMQEyAAIAUQDAtCgICAgOAAIQELIAJBIGokACABC1UBAX8jAEEgayIFJAACQCAAIAUgAxCKBUEASARAQX8hAgwBCyAAIAEgAiAFKQMIIAUpAxAgBSkDGCAFKAIAIARyEHghAiAAIAUQTgsgBUEgaiQAIAILEgAgAEGJIEEAEBZCgICAgOAAC/EBAgZ/AX4jAEEQayIDJAACQCABECJFBEAgABApQX8hBAwBC0F/IQQgACACECsiCRANDQACQCAAIANBDGogA0EIaiAJp0ETEJIBQQBIBEBCgICAgDAhAiADKAIIIQYgAygCDCEHDAELQQAhBEKAgICAMCECIAMoAgwhByADKAIIIQYDQCAFIAZGDQEgACACEAwgACAJIAcgBUEDdGoiCCgCBCAJQQAQFCICEA1FBEAgBUEBaiEFIAAgASAIKAIEIAJBgIABENoEQQBODQELC0F/IQQLIAAgByAGEGYgACAJEAwgACACEAwLIANBEGokACAEC4cDAQR/QQEhCCADIQYCQANAAkAgBiIHKALMASAFQQN0akEEaiEFA0ACQCAFKAIAIgVBAEgEQEF/IQUMAQsgBygCdCAFQQR0aiIGKAIAIARGDQAgBkEIaiEFDAELCyAFQQBOBEAgBygCdCAFQQR0aigCDEEDdkEPcSEJQQEhBiAIBEBBACEGDAILIAAgAyAHQQAgBSAEQQFBAUEAEKcBIgVBAE4NAQwDCyAHKAIEIgYEQCAHKAIMIQVBACEIDAIFAkAgBygCIEUNAEEAIQUgBygCwAIiBkEAIAZBAEobIQYDQCAFIAZGDQEgBCAHKALIAiIJIAVBA3RqKAIERgRAIAkgBUEDdGotAAAiCEEEdiEJIAMgB0YEQEEBIQYMBQtBASEGIAAgAyAHQQAgCEEBdkEBcSAFIAQgCEECdkEBcSAIQQN2QQFxIAkQhgIiBUEASA0GDAQFIAVBAWohBQwBCwALAAsgACAEQb38ABCVAwwDCwALCyABIAY2AgAgAiAJNgIAIAUPC0F/C8YBAQF/IAEgA2otAABBPEYEQCAAIARB/wFxEBAgACAFQf//A3EQMSADQQFqIQMLIAEgAigCBCIAQQVrIgJqIgYtAABBtAFGBEAgACABai0AAEEWRgRAIAZBEToAACAAQQRrIQILIABBAmohBiABIAJqIgAgBEEBajoAACAAQQFqIAVB//8DcRCGAyACQQNqIQADQCAAIAZORQRAIAAgAWpBsQE6AAAgAEEBaiEADAELCyADDwtB4T5BvuMAQezlAUGPxwAQAAALswEBAX9BfyEDAkAgASgCTEUNAAJAAkACQAJAIAJB8QBrDgMCAQADCyABKAK0ASIDQQBODQMgASAAIAFB8wAQWCIANgK0ASAADwsgASgCsAEiA0EATg0CIAEgACABQfIAEFgiADYCsAEgAA8LIAEoAqwBIgNBAE4NASABIAAgAUHxABBYIgA2AqwBIAAPCyACQQhHDQAgASgCqAEiA0EATg0AIAEgACABEMoDIgM2AqgBCyADC88ZAgR+BH8gAEH4ARCcAiIGBH8CfyAGQQE2AgAgACAGQQUQvgEgBiAAIAAoAkBBA3QQ6AEiBzYCKCAHRQRAIAAgBhAhQQAMAQsgBiAANgIQIAZBFGogAEHIAGoQTEEAIQcgACgCQCIAQQAgAEEAShshAANAIAAgB0cEQCAGKAIoIAdBA3RqQoCAgIAgNwMAIAdBAWohBwwBCwsgBkKAgICAIDcDUCAGQoCAgIAgNwNIIAZCgICAgCA3A0AgBkHgAWoQcUEAIQcgBkKAgICAIBBVIQEgBigCKCABNwMIIAYgBkEJQdyDAUEAQQBBACABEIsCIgE3AzAgARAPIQEgBigCKCABNwNoIAYQPCEBIAYoAiggATcDGCAGIAFB0LQBQQMQJgNAIAYoAighACAHQQhHBEAgBiAGIAApAxgQVSIBQTYgBiAHQQJ0QZCKAWooAgAQ9gRBAxAbGiAGIAFBMyAGQS8QMkEDEBsaIAYgB0EDdGogATcDWCAHQQFqIQcMAQsLIAYgACkDCEECEFMhASAGKAIoIAE3AxAgBiAGIAEQ7ARBARDlBDYCJCAGIAZBJGpBAEEwQQoQ5AQaIAYLBUEACyIABEAjAEHQAGsiBiQAIAAgAEEKQQBBABDtAjcDsAEgAEELQQBBABDtAiEBIAAgACkDMEHPAEKAgICAMCABIAApA7ABQYEyEHgaIAAgACkDMEHNAEKAgICAMCABIAApA7ABQYEyEHgaIAAgARAMIAAgAEKAgICAMEEBIABBsAFqQQEQ1wQQDCAAIAAQPDcDwAEgACAAQoCAgIAgEFU3A8gBIAAgAEHCHUEMQQEgACgCKCkDCBDRAUGAtQFBFxAmIAAgACgCKCkDCEHwtwFBCxAmIAAgACkDMEGguQFBBxAmIAAgAEENQYI3QQFBBUEAEOoCIgE3AzggACABEA9BgjcgACkDMBDQASAAIABBDkH5K0EBQQVBfxDqAiIBQfkrIAAoAigpAxgQ0AEDQCAFQQhHBEAgACAAQQ4gBUECdEGQigFqKAIAIgdBAkEBIAVBB0YbQQUgBSABEIsCIAcgACAFQQN0aikDWBDQASAFQQFqIQUMAQsLIAAgABA8IgE3A5gBIAAgAUGQugFBARAmIAAgACgCKCkDEEGgugFBIBAmIAAgAEGWDkEPQQEgACgCKCkDEBDRARAPIgE3A0AgACABQaC+AUEEECYgBkGwigFBygAQJSIGIQVB4wAhByAAQoCAgIAgEFUhAQNAIAcEQCAAIAEgBUKBgICAEEEHEOwBGiAFEEMgBWpBAWoiBS0AACEHDAELCyAAIAAoAigpAxBBzQEgAUEBEBsaIAAgACAAKAIoKQMQIgFB6wAgAUEAEBQ3A6gBIAAgACkDmAEQVSEBIAAoAiggATcDqAIgACABQeC+AUECECYgACAAKQPAAUGAvwFBEBAmIAAgACgCKCkDCEEEEFMhASAAKAIoIAE3AyAgACABQgAQzwEgACAAKAIoKQMgQdDBAUEGECYgACAAQbUyQRBBASAAKAIoKQMgENEBQbDCAUEOECYgACAAKAIoKQMIQQYQUyEBIAAoAiggATcDMCAAIAFCgICAgBAQzwEgACAAKAIoKQMwQZDEAUECECYgAEHkO0ERQQEgACgCKCkDMBDRARogACAAKAIoKQMIQQUQUyEBIAAoAiggATcDKCAAIAEgAEEvEDIQzwEgACAAQarFAEESQQEgACgCKCkDKBDRAUGwxAFBAxAmIAAgACgCKCkDKEHgxAFBMRAmIAAgACkDmAEQVSEBIAAoAiggATcDsAIgACABQeDLAUECECYjAEEQayIFJAAgBUEIahCwBCAAQgEgBTQCDCAFNAIIQsCEPX58IgEgAVAbNwPQASAFQRBqJAAgACAAKQPAAUGAzAFBARAmIAAgACkDwAFB0NEBQQEQJiAAEDwhASAAKAIoIAE3AzggACABQcDTAUEFECYgACAAQcM8QRNBACAAKAIoKQM4ENEBIgFBkNQBQQIQJkHCASEFA0AgBUHPAUcEQCAAIAEgACAGIAUQiQEiB0EuELADIghBAWogByAIGyAAIAUQYEEAEOwBGiAFQQFqIQUMAQsLIAAgACkDmAEQVSEBIAAoAiggATcDwAIgACABQbDUAUEEECYgACAAKQMwEFUhASAAKAIoIAE3A4ABIABBDUHWNkEBQQVBARDqAiEBIAAgACgCKCkDgAFB8NQBQQEQJiAAIAAoAigiBSkDgAEgBSkDwAJBAUEBEIICIAAgASAAKAIoKQOAAUEAQQEQggIgACABEAwgACAAQRRB+T5BARDtAiIBNwO4ASAAIAApA8ABQTogARAPQQMQGxogACAAKQPAARAPIgFBigEgAUEDEBsaIAZB0ABqJAAgABA8IQEgACgCKCABNwNQIAAgAUGgrQFBLxAmIAAgAEHKygBBFUEHIAAoAigpA1AQ0QFBkLQBQQMQJiAAQRY2AuwBIAAgACgCKCkDKEHgogFBARAmIABBJTYC6AEgABA8IQEgACgCKCABNwOQASAAIAFB8KIBQREQJiAAIABB3TNBF0ECIAAoAigpA5ABENEBEA8iATcDSCAAIAFBgKUBQQEQJiAAIAApA5gBEFUhASAAKAIoIAE3A7gCIAAgAUGQpQFBAhAmIAAgACkDwAFBsKUBQQEQJiAAKAIQIgVBKRCMBkUEQCAFQdiIAUEpQQEQkAQaIAUoAkQiBUEYNgLoByAFQeSIATYC7AcLIABBGUGtCUECQQJBABDLASIBQQEQsgMgACABQfClAUEBECYgACAAKQPAAUGtCSABQQMQ7AEaQQAhBSMAQUBqIgYkAANAAkAgBUEERgRAQQAhBQNAIAVBAkYNAiAAIAApA5gBEFUhASAAKAIoIAVBA3RqIAE3A5gCIAAgASAFQQJ0QZCJAWooAgAgBUGciQFqLQAAECYgBUEBaiEFDAALAAsgACAGIAVBpwFqEIkBIQcgABA8IQEgBUEfakEDdCIIIAAoAihqIAE3AwAgACABIAVBAnRBgIkBaigCACAFQZiJAWotAAAQJiAAQRogB0EAQQMgBRDqAiEBIAVBAU0EQCAAIAFBwKoBQQEQJgsgACABIAcgACgCKCAIaikDABDQASAFQQFqIQUMAQsLIAZBQGskACMAQUBqIgYkACAAEDwhASAAKAIoIAE3A5gBIAAgAUGA1QFBAxAmIAAgAEH4MUEbIAAoAigpA5gBEKoEQbDVAUECECYgABA8IQEgACgCKCABNwOgASAAIAFB0NUBQQMQJiAAIABB0TFBHCAAKAIoKQOgARCqBEGA1gFBARAmIAAgABA8IgFBkNYBQR4QJiAAIAFBNyAAIAAoAigpAxAiAkE3IAJBABAUQQMQGxogACAAQR1BkQ5BABDtAiICQfDZAUEDECYgACACIAEQ7gVBFSEFA0AgBUEeRwRAIAAgARBVIQMgBUEDdCIHIAAoAihqIAM3AwAgACADQavoAEEBIAVB5YoBai0AAHStIgNBABDsARogACAAQR4gACAGIAVBiAFqEIkBIghBA0EDIAUgAhCLAiIEIAggACgCKCAHaikDABDQASAAIARBq+gAIANBABDsARogBUEBaiEFDAELCyAAIAEQDCAAIAIQDCAAEDwhASAAKAIoIAE3A/ABIAAgAUGg2gFBFBAmIABB5hBBHyAAKAIoKQPwARCqBBogBkFAayQAIAAoAhAiBUEqEIwGRQRAIAVBoIkBQSpBCRCQBBogBSgCRCIFQagJakEgNgIAIAVB+AhqQSE2AgAgBUHgCGpBITYCACAFQcgIakEiNgIAIAVBsAhqQSM2AgAgBUGYCGpBIzYCAAsgABA8IQEgACgCKCABNwPQAiAAIAFB0KoBQQQQJiAAIABBJEGMywBBAUECQQAQywEQDyIBNwNQIAAgAUGQqwFBBxAmIAAgAUGMywAgACgCKCkD0AIQ0AEgACAAKQMwEFUhASAAKAIoIAE3A+gCIABBDUH9NkEBQQVBAiAAKQM4EIsCIQEgACAAKAIoKQPoAkGArAFBARAmIAAgASAAKAIoKQPoAkEAQQEQggIgACABEAwgACAAEDwiATcDoAEgACABQZCsAUEBECYgACAAKQOgARBVIQEgACgCKCABNwOAAyAAIAFBoKwBQQMQJiAAIAApA6ABEFUhASAAKAIoIAE3A5ADIAAgAUHQrAFBBBAmIAAgACkDMBBVIQEgACgCKCABNwOIAyAAQQ1B0TZBAUEFQQMgACkDOBCLAiEBIAAgACgCKCkDiANBkK0BQQEQJiAAIAAoAigiBSkDiAMgBSkDkANBAUEBEIICIAAgASAAKAIoKQOIA0EAQQEQggIgACABEAwLIAALCQAgACABOgAAC0UAIAAoAswBIAFBA3RqQQRqIQEDQCABKAIAIgFBAEhFBEAgACgCdCABQQR0aiIBIAEoAgxBBHI2AgwgAUEIaiEBDAELCwuqFwEIfyMAQRBrIgskACALQX82AgwCf0EBIAJB8QBrQQNJDQAaQQEgAkEIRg0AGkEACyENIAEoAswBIANBA3RqQQRqIQMCQAJAAkACQAJAA0AgAygCACIDQQBOBEAgAiABKAJ0IgogA0EEdGoiCSgCACIMRgRAIAMhCQJAIARBtwFrDgMABAAECyAKIAlBBHRqLQAMQQFxRQ0DIAVBMBAQIAUgACACEBkQHiAFQQAQEAwHCyANIAxB1ABHckUEQCAFQdgAEBAgBSADQf//A3EQMSAAIAEgAiAEIAUgC0EMakEBEO0BCyAJQQhqIQMMAQsLQX8hCSADQX5HBEAgASACEIcCIQkLIA1FIAlBAE5yRQRAIAAgASACEN8EIQkLAkAgAkHNAEcgCUEATnJFBEAgASgCSEUNASAAIAEQ8AIhCQsgCUEATg0BCwJAIAEoAiwEQCABKAJwIAJGDQELIANBfkcNAwwECyAAIAEgAhDvAiIJQQBIDQELAkACQAJAAkAgBEG1AWsOBwICAAMAAQIHCwJAIAlBgICAgAJxIgMNACABKAJ0IAlBBHRqLQAMQQFxRQ0AIAVBMBAQIAUgACACEBkQHiAFQQAQEAwHCwJAIARBtwFrDgMCAwAHCwJAIAMNACABKAJ0IAlBBHRqKAIMQfgAcUEgRw0AIAVBCxAQIAVB2AAQECAFIAlB//8DcRAxIAVBzAAQECAFIAAgAhAZIgIQHiAFQQQQECAFIAAgAhAZEB4MBwsCQCALKAIMQX9HDQAgBiAHKAIEEMYDRQ0AIAUgBiAHIAgCfyADBEAgCUGAgICAAmshCUHbAAwBC0HiAEHYACABKAJ0IAlBBHRqLQAMQQJxGwsgCRDeBCEIDAcLIAMEQCAFQfkAEBAgBSAAIAIQGRAeIAUgCUH//wNxEDEMBwsgBUH4ABAQIAUgACACEBkQHiAFIAlB//8DcRAxDAYLIAVBBhAQCyAJQYCAgIACcQRAIAVB3ABB3ABB2wAgBEG7AUYbIARBtwFGGxAQIAUgCUH//wNxEDEMBQsCQAJAAkAgBEG3AWsOBQABAQEAAQtB4wBB2QAgASgCdCAJQQR0ai0ADEECcSIAGyEDIABFIARBuwFHcg0BQeQAQdkAIAJBCEYbIQMMAQtB4gBB2AAgASgCdCAJQQR0ai0ADEECcRshAwsgBSADEBAgBSAJQf//A3EQMQwECyAFQQkQEAwDCyADQX5GDQELIA0gASgCkAFBAEhyDQAgBUHYABAQIAUgAS8BkAEQMSAAIAEgAiAEIAUgC0EMakEAEO0BCyANIAEoApQBQQBIckUEQCAFQdgAEBAgBSABLwGUARAxIAAgASACIAQgBSALQQxqQQAQ7QELIAJBzQBHIQ4gASEDAkACQAJAAkADQCADKAIEIgpFBEAgAyEKDAILIAooAswBIAMoAgxBA3RqQQRqIQMDQCADKAIAIgNBAE4EQCACIAooAnQiDyADQQR0aiIMKAIAIhBGBEAgAyEJAkAgBEG3AWsOAwAGAAYLIA8gCUEEdGotAAxBAXFFDQUgBUEwEBAgBSAAIAIQGRAeIAVBABAQDAgFAkAgDSAQQdQAR3INACAMIAwoAgxBBHI2AgwgACABIApBACADQdQAQQBBAEEAEKcBIgNBAEgNACAFQd4AEBAgBSADQf//A3EQMSAAIAEgAiAEIAUgC0EMakEBEO0BCyAMQQhqIQMMAgsACwsgCUEATg0CIANBfkYiA0UEQCAKIAIQhwIiCUEATg0DCyANBEAgACAKIAIQ3wQiCUEATg0DCwJAAkAgDg0AIAooAkhFDQAgACAKEPACIQkMAQsCQCAKKAIsRQ0AIAooAnAgAkcNACAAIAogAhDvAiEJDAELAkAgAw0AIA0gCigCkAEiA0EASHINACAKKAJ0IANBBHRqIgMgAygCDEEEcjYCDCAAIAEgCkEAIAooApABIAMoAgBBAEEAQQAQpwEhAyAFQd4AEBAgBSADQf//A3EQMSAAIAEgAiAEIAUgC0EMakEAEO0BCyANIAooApQBIgNBAEhyRQRAIAooAnQgA0EEdGoiAyADKAIMQQRyNgIMIAAgASAKQQAgCigClAEgAygCAEEAQQBBABCnASEDIAVB3gAQECAFIANB//8DcRAxIAAgASACIAQgBSALQQxqQQAQ7QELIAoiAygCIEUNAQwCCwsgCUEATg0BCyAKKAIgRQ0CQQAhAwNAIAooAsACIANKBEAgAiAKKALIAiADQQN0aiIPKAIEIg5GBEAgASAKRg0EIAAgASAKQQAgDy0AACIJQQF2QQFxIAMgAiAJQQJ2QQFxIAlBA3ZBAXEgCUEEdhCGAiEDDAQFAkACQCAOQX5xQdIARwRAIA0gDkHUAEdyRQ0BDAILIA0NAQsgAyEMIAEgCkcEQCAAIAEgCkEAIA8tAABBAXZBAXEgAyAOQQBBAEEAEIYCIQwLIAVB3gAQECAFIAxB//8DcRAxIAAgASACIAQgBSALQQxqIA5B1ABGEO0BCyADQQFqIQMMAgsACwsgCUEASA0CCwJ/IAlBgICAgAJxBEAgCigCgAEgCUGAgICAAmsiA0EEdGoiCSAJKAIMQQRyNgIMIAAgASAKQQEgAyACQQBBAEEAEKcBDAELIAlBBHQiAyAKKAJ0aiIMIAwoAgxBBHI2AgwgACABIApBACAJIAIgCigCdCADaigCDCIDQQFxIANBAXZBAXEgA0EDdkEPcRCnAQsiA0EASA0BCwJAAkACQAJAAkACQAJAIARBtQFrDgcBAQAGAAMBCAsgASgCyAIgA0EDdGotAAAiCUEEcQRAIAVBMBAQIAUgACACEBkQHiAFQQAQEAwIC0EAIQoCQCAEQbcBaw4DAgYACAsgCUHwAXFBwABGBEAgBUELEBAgBUHeABAQIAUgA0H//wNxEDEgBUHMABAQIAUgACACEBkiAhAeIAVBBBAQIAUgACACEBkQHgwICwJAIAsoAgxBf0cNACAGIAcoAgQQxgNFDQAgBSAGIAcgCEHlAEHeACAJQQhxGyADEN4EIQgMCAsgBUH6ABAQIAUgACACEBkQHiAFIANB//8DcRAxDAcLIARBuwFGIQogBEG3AWsOBQACAgIAAgtB5gBB3wAgASgCyAIgA0EDdGotAABBCHEiBBshACAERSAKRXINAkHnAEHfACACQQhGGyEADAILIAVBBhAQC0HlAEHeACABKALIAiADQQN0ai0AAEEIcRshAAsgBSAAEBAgBSADQf//A3EQMQwCCyAFQQkQEAwBCwJAAkACQAJAAkAgBEG1AWsOBwICAgQAAQMFCwJAIAsoAgxBf0cNACAGIAcoAgQQxgNFDQACfyABLQBuQQFxIgQEQCAFQTYQECAFIAAgAhAZEB4LIAYgCGotAABBPEYEQCAFQTgQECAFIAAgAhAZEB4gCEEBaiEICyAGIAcoAgQiB0EFayIJaiIMLQAAQbQBRgRAIAYgB2otAAAhAwJAAkAgBARAQTshCgJAAkACQAJAIANBGWsOBQIBAQEDAAtBFSEEIANBFkYNBCADQbEBRg0FCxABAAtBGCEEDAILQRshBAwBC0E5IQpBESEEIANBFkcNAQsgDCAEOgAAIAdBBGshCQsgB0ECaiEDIAYgCWoiBCAKOgAAIARBAWogACACEBkQXSAJQQVqIQADQCAAIANIBEAgACAGakGxAToAACAAQQFqIQAMAQsLIAgMAQtB4T5BvuMAQZ3mAUHRxgAQAAALIQgMBQsgBUH7ABAQIAUgACACEBkQHgwECyAFQQYQECAFQTgQECAFIAAgAhAZEB4MAwsgBSAEQf4Aa0H/AXEQECAFIAAgAhAZEB4MAgsgBUE6EBAgBSAAIAIQGRAeDAELIAVBmQEQECAFIAAgAhAZEB4LIAsoAgwiAEEATgRAIAVBtAEQECAFIAAQHiABKAKkAiAAQRRsaiAFKAIENgIICyALQRBqJAAgCAuNAgEEfyAAKAIQIQYgASgCACIFLQAQBH8gBiAFEJEEIAUoAhQgAxDAAiAEEMACBUEACyEHAn8gBSgCICIIIAUoAhxOBEAgACABIAIgCEEBahDRBQRAQX8gBS0AEEUNAhogBiAFEJ4DQX8PCyABKAIAIQULIAUtABAEQCAFIAc2AhQgBiAFEJ4DCyAFIAUoAiAiAUEBajYCICAFECogAUEDdGoiASAAIAMQGSIANgIEIAEgASgCAEH///8fcSAEQRp0cjYCACAFIAUtABEgABBecjoAESABIAEoAgBBgICAYHEgBSAAIAUoAhhxQX9zQQJ0aiIAKAIAQf///x9xcjYCACAAIAUoAiA2AgBBAAsL1AIBCX8gACgCECIEKALQAUEBdEECaiAEKALMAUoEQCAEQQEgBCgCyAFBAWoiB3QiCUECdBCcAiIIBEAgBCgCzAEiA0EAIANBAEobIQoDQCAEKALUASEDIAYgCkcEQCADIAZBAnRqKAIAIQUDQCAFBEAgBSgCKCEDIAUgCCAFKAIUIAcQ1AJBAnRqIgsoAgA2AiggCyAFNgIAIAMhBQwBCwsgBkEBaiEGDAELCyAEIAMQISAEIAg2AtQBIAQgCTYCzAEgBCAHNgLIAQsLIABBBCACEOUBEC8iA0UEQEEADwsgA0EEEL8CIgNBATYCACAEIANBAhC+ASABBEAgAa1CgICAgHCEEA8aCyADIAE2AiwgA0IANwIgIAMgAjYCHCADQQM2AhggA0EQayICQgA3AgAgAkIANwIIIANBATsBECADIAEQ3wU2AhQgACgCECADEJ4DIAMLrgECA38BfiMAQRBrIgMkACAAIAEQMiIGEA1FBEACQAJAIAAgA0EMaiAGEJACIgFFDQAgACACEEMiBCADKAIMakEBahAvIgVFDQAgBSABIAMoAgwQJSIFIAMoAgxqIAIgBBAlGiAFIAMoAgwgBGpqQQA6AAAgACAFIAMoAgwgBGoQrQMhBCAAIAUQGiAAIAEQNwwBCyAAIAEQN0EAIQQLIAAgBhAMCyADQRBqJAAgBAtLAQF/IAAgASgCADYCQCAAQSkQDiAAIAAoAkAoAgQ2AkAgAEKAgICAIBDTAyECIAEoAgAgAjYCCCAAQQMQDiAAIAIQOiAAQdAAEA4LzwEBAX8gACgCACAAKAJAQQBBACAAKAIMQQAQ9wMiAgRAIAJBADYCcCACQQA2AmAgAkKAgICAEDcCSCACQgE3AjAgAkGADDsBbCACQgE3AlggAkIBNwJQCyABIAI2AgAgAkUEQEF/DwsgACACNgJAIABBCRAOIAEgASgCACgCmAI2AgwgAEHpAEF/EB0hASAAQbYBEA4gAEEIEBwgAEEAEBggAEG2ARAOIABB8wAQHCAAQQAQGCAAQS0QDiAAIAEQICAAIAAoAkAoAgQ2AkBBAAsNACAAIAFBuu8AEOYEC0cBAX8Cf0EAIAEoAggNABogASgCACICBH8gAgVBfyAAIAEQ6AQNARogASgCAAsoAoACIAEoAgxqQQo6AAAgAUEBNgIIQQALC6EBAQV/IwBBEGsiBCQAIAGnIgUoAhAiAyADKAIYQX9zQQJ0Qbx+cmooAgAhAiADECohAwJAAkADQCACRQ0BIAJBA3QgA2oiBkEIayECIAZBBGsoAgBBMEcEQCACKAIAQf///x9xIQIMAQsLIAQgAjYCDCAAIAUgBEEMaiACKAIAQRp2QTxxEJ8DDQELIAUgBS0ABUH+AXE6AAULIARBEGokAAsRACAAp0EAIABC/////29WGwv8BAIFfwN+IwBBMGsiBCQAIAAoAgAhBUKAgICAMCEKQoCAgIAwIQgCQCABBEBBfyEDIAUQUSIIEA0NASAAIAhBABDTASEGIAUgCBAMIAYNASAFEFEiChANDQEgBSAIQfAAIApBgIABEBtBAEgNAQsgAEEQaiEGQQAhAwJAAkADQCAGKAIAQYJ/RgRAIAAoAhghByAEIAYpAxg3AyggBCAGKQMQNwMgIAQgBikDCDcDGCAEIAYpAwA3AxAgB0EBaiEHIAApAyAhCQJAAkACQCABBEAgBSAKIAMgCRAPQYSAARCfAUEASA0CIAUgCCADAn4gAEHgAEEAIAcgBEEQaiAEQQxqEJIDRQRAIAQpAyAMAQsgBEKAgICAMDcDIEKAgICAMAtBhIABEJ8BQQBIDQIgACgCKEHgAEcNASAFIAoQ6wQgBSAIEOsEIAIgA0EBajYCAAwHCyAFIAkQDCAAQoCAgIAwNwMgIABB4ABBASAHIARBEGogBEEMahCSAw0BAkAgBCkDICIJpygCBEH/////B3FBASADGwRAIAAgCUEBENMBIQcgACgCACAJEAwgBw0DIANFBEAgACgCKEHgAEYNCSAAQcIAEA4gAEHcABAcCyADQQFqIQMMAQsgACgCACAJEAwLIAAoAihB4ABGDQULIAAQEQ0AIAAQmQENACAGKAIAQf0ARwRAIABBqTlBABAVDAELIAAgBhCPAiAAQQA2AjAgACAAKAIUNgIEIAAgACgCOBDZA0UNAQtBfyEDDAULIANBAWohAwwBCwsgAEGCfxAwIQMMAgsgAEEkEA4gACADQQFrQf//A3EQGAsgABARIQMLIARBMGokACADC28BAX8gAEEmEA4gAEEAEBggAEEBEA4gAEEAEDogACAAEDUiAhAgIABBgAEQDiAAIAFBAmpB/wFxEG4gAEHqAEF/EB0hASAAQdEAEA4gAEGPARAOIABB6wAgAhAdGiAAIAEQICAAQQ4QDiAAQQ4QDgudAQEFfyAAKAJAIgQoAogBIgNBACADQQBKGyEDAkADQAJAIAIgA0YEQEEAIQMgBCgCfCICQQAgAkEAShshBUEAIQIDQCACIAVGDQQgAkEEdCEGIAJBAWohAiAGIAQoAnRqKAIAIAFHDQALDAELIAJBBHQhBSACQQFqIQIgBSAEKAKAAWooAgAgAUcNAQsLIABB5BJBABAVQX8hAwsgAwv3BAIIfwF+IwBBQGoiAiQAIAAoAjghAUF/IQgCQCAAKAIAIAJBKGpBIBBCDQACQCAAKAIAIAJBEGpBARBCDQAgAUEBaiEDQQAhAQJAA0AgAyIHIAAoAjxPDQEgASEGQQEhASAHQQFqIQNB2wAhBAJAAkACQAJAAkACQAJAIActAAAiBUHbAGsOAwUDAQALIAVBL0cEQCAFQQprDgQGAgIGAgtBLyEEIAYNBANAIAIgA0EBajYCDAJAIAMsAAAiAUEATgRAIAFB/wFxIQEMAQsgA0EGIAJBDGoQYSIBQYCAxABPDQULIAEQwQEEQCACQRBqIAEQwAENCiACKAIMIQMMAQsLIABBhH82AhAgACACQShqEDk3AyAgAkEQahA5IQkgACADNgI4IAAgCTcDKEEAIQgMCQtB3QAhBEEAIQEMAwsgBUEYdEEYdUEATgRAIAYhASAFIQQMAwsgB0EGIAJBCGoQYSIEQYCAxABPDQEgBEF+cUGowABGDQMgAigCCCEDIAYhAQwCCyACQShqQdwAED4NBSAHQQJqIQUCQCAHLQABIgQEQCAEQQprDgQEAQEEAQtBACEEIAYhASAFIgMgACgCPE8NBQwCCyAEQRh0QRh1QQBOBEAgBiEBIAUhAwwCC0EHQQZBACADQQYgAkEMahBhIgRBfnFBqMAARhsgBEH//8MASyIBGyIDRQRAIAUgAigCDCABGyEDIAYhAQwCCyADQQZrDgICAAYLIABBiNgAQQAQFQwECyACQShqIAQQwAFFDQEMAwsLIABBnzNBABAVDAELIABBxDNBABAVCyACQShqEEQgAkEQahBECyACQUBrJAAgCAszAQF/A0ACQCABQQBOBH8gASACRw0BQQEFQQALDwsgACgCzAEgAUEDdGooAgAhAQwACwALQwECfyAAKAKIASECQX8hAwJAA0AgAkEATA0BIAAoAoABIAJBAWsiAkEEdGooAgAgAUcNAAsgAkGAgICAAnIhAwsgAwuDAwEGfyABKAI4IQMCQAJAAkAgAS0AbkEBcQRAIANFBEBBry4hAyABKAJADQMLQdbVACEDIAJBOkYgAkHNAEZyDQJBACECIAEoAogBIgNBACADQQBKGyEEA0AgAiAERg0CQbHVACEDIAEoAoABIAJBBHRqKAIAIgZBOkYgBkHNAEZyDQMgAkEBaiECDAALAAsgA0UNACABLwFsIgJBggxGDQAgAkEIdkEDaw4EAAICAAILQQAhBCABKAKIASICQQAgAkEAShshCEEAIQMDQCADIAhGDQJBACECAkAgASgCgAEiBSADQQR0aigCACIGRQ0AA0ACQCACIANGBEBBACECIAEoAnwiBUEAIAVBAEobIQUDQCACIAVGDQQgBiABKAJ0IAJBBHRqIgcoAgBGBEAgBygCBEUNAwsgAkEBaiECDAALAAsgAkEEdCEHIAJBAWohAiAFIAdqKAIAIAZHDQELC0GvEiEDDAILIANBAWohAwwACwALIAAgA0EAEBVBfyEECyAEC2EBAX8gAEG2ARAOIABB9gAQHCAAIAAoAkAvAbwBEBggAEEREA4gAEHpAEF/EB0hASAAQbYBEA4gAEEIEBwgAEEAEBggAEEbEA4gAEEkEA4gAEEAEBggACABECAgAEEOEA4LUQECf0F/IQJBASEDA0ACQCAAIAEQuwENACADRQRAIAAoAkBBfzYCmAILIAAoAhBBLEcEQEEAIQIMAQsgABARDQAgAEEOEA5BACEDDAELCyACCykBAX4gACABEMoBIgFFBEBCgICAgOAADwsgACABEDIhAiAAIAEQEyACC9sCAQR/IwBBoAFrIgUkACABKAIAIQcgBUGAATYCCCAFIAVBEGo2AgwgBAR/IAVBIzoAEEEBBUEACyEEAn8CQANAIAUgBzYCnAECfyADQf8ATARAIAUoAgwiBiAEaiADOgAAIARBAWoMAQsgBSgCDCIGIARqIAMQ5gIgBGoLIQQgBSAFKAKcASIDIghBAWo2ApwBAkAgAy0AACIDQdwARgRAQdwAIQMgCC0AAUH1AEcNASAFQZwBakEBEIMCIQMgAkEBNgIADAELIANBGHRBGHVBAE4NACAHQQYgBUGcAWoQYSEDCyADEMEBRQ0BIAUoApwBIQcgBCAFKAIIQQZrSQ0AIAAoAgAgBUEMaiAFQQhqIAVBEGoQjQVFDQALIAUoAgwhBkEADAELIAAoAgAgBiAEEK0DCyEDIAVBEGogBkcEQCAAKAIAIAYQGgsgASAHNgIAIAVBoAFqJAAgAwucDQEHfwJAAkACQAJAIAAoAhAiBkFFRwRAIAAoAkAhASAAQYUBEFRFDQIgAEEBEIsBQUVHDQELQX8hBiAAQQBBACAAKAIYIAAoAhQQ2AFFDQIMAwsgACgCECEGCwJAAkACQCAGQTVqDgMAAgECCyABKAKUA0UNAUF/IQYCfyAAKAIAIQMgACgCQCgClAMhAQJAAkACQCAAEBENAAJAAkACQAJAIAAoAhAiAkE7ag4EAgEBAAELIABBAEEBEPoCDAYLIABBhQEQVEUNASAAQQEQiwFBRUcNAQsgAEEAQQAgACgCGCAAKAIUQQFBABCKAgwECyAAEBENAAJAAkAgAkGxf0YNAAJAIAJBQEcEQCACQUlGIAJBUUZyDQIgAkEqRwRAIAJB+wBHDQQgASgCICEEA0ACQCAAKAIQIgJB/QBGDQAgAhDXAUUNCUEAIQIgAyAAKAIgEBkhBQJAAkACQCAAEBENACAAQfkAEFRFDQEgABARDQAgACgCEBDXAUUEQCAAQafeAEEAEBUMAQsgAyAAKAIgEBkhAiAAEBFFDQILIAMgBRATDAoLIAMgBRAZIQILIAAgASAFIAJBABCJAiEHIAMgBRATIAMgAhATIAdFDQcgACgCEEEsRw0AIAAQEUUNAQwHCwsgAEH9ABAwDQUgAEH6ABBURQ0CIAAQ+QIiAkUNBSADIAEgAhD4AiEFIAMgAhATIAVBAEgNBQNAIAQgASgCIE4NAyABKAIcIARBFGxqIgIgBTYCACACQQE2AgggBEEBaiEEDAALAAsgAEH5ABBUBEAgABARDQUgACgCEBDXAUUNByADIAAoAiAQGSECIAAQEQ0GIAAQ+QIiBEUNBiADIAEgBBD4AiEFIAMgBBATIAVBAEgNBiAAIAFB/QAgAkEBEIkCIQEgAyACEBMgAUUNBSABIAU2AgAMAgsgABD5AiIERQ0EIAMgASAEEPgCIQIgAyAEEBMgAkEASA0EIAMgAUEoakEEIAFBMGogASgCLEEBahCAAQR/QX8FIAEgASgCLCIDQQFqNgIsIAEoAiggA0ECdGogAjYCAEEAC0EATg0BDAQLAkACQAJAAkAgACgCEEE7ag4EAgEBAAELIABBAEECEPoCDAkLIABBhQEQVEUNASAAQQEQiwFBRUcNAQsgAEEAQQAgACgCGCAAKAIUQQJBABCKAgwHCyAAEGINAyAAQRYQrQEgACAAKAJAQfwAQQEQrAFBAEgNAyAAQbsBEA4gAEH8ABAcIABBABAYIAAgAUH8AEEWQQAQiQJFDQMLIAAQvQEMBQsgAEEBIAJBARDYAwwECyAAQYwPQQAQFQtBfwwCCyADIAIQE0F/DAELIABBp94AQQAQFUF/C0UNAgwDCyABKAKUA0UNACAAQQAQiwEiAUEoRiABQS5Gcg0AQX8hBgJ/IAAoAgAhASAAKAJAKAKUAyEEQX8hBwJAAkACQCAAEBENACAEKAI4IQUCQAJAAkACQAJAIAAoAhAiA0H/AGoOAwACAQILIAEgACkDIBA4IgJFDQQgABARRQ0DIAEgAhATQX8MBwsgACgCKARAIAAQ8AFBfwwHC0EWIQIgASAAKAIgEBkhAyAAEBENBCAAIAQgA0EWENcDDQQgASADEBMgACgCEEEsRw0BIAAQEQ0DIAAoAhAhAwsgA0H7AEcEQCADQSpHDQEgABARDQMgAEH5ABBURQRAIABBm/oAQQAQFUF/DAcLIAAQEQ0DIAAoAhAQ1wFFDQVB/QAhAiABIAAoAiAQGSEDIAAQEQ0EIAAgBCADQf0AENcDDQQgASADEBMMAQsgABARDQIDQAJAIAAoAhAiAkH9AEYNACACENcBRQ0GQQAhAyABIAAoAiAQGSECIAAQEQ0FAkAgAEH5ABBUBEAgABARDQcgACgCEBDXAUUEQCAAQafeAEEAEBUMCAsgASAAKAIgEBkhAyAAEBFFDQEMBwsgASACEBkhAwsgACAEIAMgAhDXAw0FIAEgAxATIAEgAhATIAAoAhBBLEcNACAAEBFFDQEMBAsLIABB/QAQMA0CCyAAEPkCIgJFDQELIAEgBCACEPgCIQMgASACEBMgA0EASA0AIAUgBCgCOCIBIAEgBUgbIQEDQCABIAVHBEAgBCgCNCAFQQxsaiADNgIIIAVBAWohBQwBCwsgABC9ASEHCyAHDAILIAEgAxATIAEgAhATQX8MAQsgAEGn3gBBABAVQX8LRQ0BDAILQX8hBiAAQQcQ8QENAQtBACEGCyAGC+wCAQN/IwBBQGoiAiQAAkAgACgCEEGBf0cNACAAIAJBEGoQ/AJBgX8hAQNAAkAgAUGBf0cNACAAKAI4IQEgAiAAKAIYIgNBAWo2AgQgAiABIANrQQJrNgIAIAJBIGpBFEGSKCACEFcaQX8hASAAEBENAgJAAkACQCAAKAIQIgNBgAFqDlcBAQEBAQMDAwMDAwMDAwMDAwMDAwEBAwMDAwMDAwMDAwMDAwMDAwMDAwMCAQEBAQMBAQEBAwEBAwMBAQEDAwEDAwEBAwMBAQEBAQEBAwEBAwEBAQEBAQEACyADQf0ARg0BIANBO0cNAiAAEBFFDQEMBAsgACgCMEUNAQsCQAJ/IAJBIGpB0htBCxB3RQRAIAAoAkAiAUEBNgJAQQEMAQsgAkEgakGpNEEKEHcNASAAKAJAIQFBAgshAyABIAEtAG4gA3I6AG4LIAAoAhAhAQwBCwsgACACQRBqEPsCIQELIAJBQGskACABCzUBAn9BASECIAAoAgAiAUHxAGtBA0kgAUEIRnIgAUHTAEZyBH9BAQUgACgCDEH4AHFBIEYLCzMAIABCsQ99QgQQ/QIgAELtAn58IABC7Q59QuQAEP0CfSAAQsEMfUKQAxD9AnxCyvErfQsSACAAIAGBIgBCP4cgAYMgAHwLggIDBH8BfgJ8IwBB4ABrIgYkAEKAgICA4AAhCQJAIAAgASAGQRBqIARBD3EiCCAEQQh2QQ9xIgdFEN0DIgVBAEgNAEQAAAAAAAD4fyEKAkAgBUUgAkEATHINAEEAIQUgBEEEdkEPcSAHayIEIAIgAiAEShsiAkEAIAJBAEobIQIDQCACIAVHBEAgACAGQQhqIAMgBUEDdGopAwAQRw0DIAYrAwgiC71CgICAgICAgPj/AINCgICAgICAgPj/AFENAiAGQRBqIAUgB2pBA3RqIAudOQMAIAVBAWohBQwBCwsgBkEQaiAIEPkDIQoLIAAgASAKEP4EIQkLIAZB4ABqJAAgCQt5AQF/AkAgAUKAgICAcFQNACABpyIDLwEGQQpHDQAgACADKQMgEAwgAwJ+IAK9An8gAplEAAAAAAAA4EFjBEAgAqoMAQtBgICAgHgLIgC3vVEEQCAArQwBCyACEBcLIgE3AyAgARAPDwsgAEGhHUEAEBZCgICAgOAAC4ABAQN/IwBBEGsiBCQAIAQgATcDCCADQQF0IQZBACEDA0ACQAJAIANBAkYNACAAQTdBASADIAZqQQEgBEEIahDmASIBEA1FDQFBfyEFIANBAUcNACAAIAIpAwAQDAsgBEEQaiQAIAUPCyACIANBA3RqIAE3AwAgA0EBaiEDDAALAAtxAQF/IwBBEGsiBCQAIAQgAjcDCCABKAJMIgEQRiAAIAAgAUEgaiADQQN0aikDAEKAgICAMEEBIARBCGoQJBAMIAAgASkDEBAMIAAgASkDGBAMIAAgASkDIBAMIAAgASkDKBAMIAAgARAaIARBEGokAAtNAQF+QbCzBCgCAARAQbizBCkDACIAUEUEQEG0swQoAgAgABAMC0G0swQoAgAQrgNBtLMEQQA2AgBBsLMEKAIAEMQFQbCzBEEANgIACwuHBgIEfwJ+IAFBCGohAyABQcgAaiEEAkACQAJAA0AgBBDnAw0CIAEoAkwhAgJAAkACfwJAAkACQAJAIAEoAgQOBgACAgUJAQYLIAIoAghFDQIgACABEN8DDAYLAkACQCACKAIIDgIIAAELIAFBBDYCBCACKQMQIQYjAEEwayICJAAgAiAGNwMoIAAgACkDUEEBIAJBKGpBABCMAiIGEA1FBEAgACABNQIAQoCAgIBwhCACQQEQ/wRFBEAgAkKAgICAMDcDGCACQoCAgIAwNwMQIAAgBiACIAJBEGoQuwIaIAAgAikDABAMIAAgAikDCBAMCyAAIAYQDAsgAkEwaiQADwsgACABIAIpAxAQ3gMPCyACKQMQEA8hBgJAIAIoAggiBUECRw0AIAEoAgRBAUcNACAAIAYQlAFBAQwCCyABKAJEIgIgBa03AwAgAkEIayAGNwMAIAEgAkEIajYCRAtBAAshAiABQQM2AgQgASACNgIUCyAAIAMQwgIiBxANBEAgABCTASEGIAAgARDfAyAAIAEgBhDeAyAAIAYQDAwCCyAHQv////8PWARAIAEoAkRBCGsiAikDACEGIAJCgICAgDA3AwACQAJAIAenIgIOAwEAAAMLIAEgAjYCBCAAIAEgBkEAEP4CIAAgBhAMDAMLIwBBMGsiAiQAIAIgBjcDKAJAIAAgACkDUEEBIAJBKGpBABCMAiIHEA0NACAAIAE1AgBCgICAgHCEIAJBEGpBABD/BARAIAAgBxAMDAELIAJCgICAgDA3AwggAkKAgICAMDcDACAAIAcgAkEQaiACELsCGiAAIAcQDEEAIQEDQCABQQJGDQEgACACQRBqIAFBA3RqKQMAEAwgAUEBaiEBDAALAAsgAkEwaiQAIAAgBhAMDwsgBxASRQ0EIAEoAkRBCGsiAikDACEGIAJCgICAgDA3AwAgACABEN8DIAAgASAGQQEQ/gIgACAGEAwMAQsLEAEACyAAIAFCgICAgDBBARD+AgsPC0H19gBBvuMAQbWZAUHyExAAAAspAQF+IAAgACkDkAFBAxBTIgIQDUUEQCAAIAJBNCABEA9BAxAbGgsgAgswAQF/IAAoAjggAUECdGooAgAiASABKAIAIgJBAWs2AgAgAkEBTARAIAAgARCsAwsLHwEBfyABIAEoAgBBAWsiAjYCACACRQRAIAAgARAhCwufAgEDfyMAQRBrIgMkAAJAAkACQAJAAkACQAJAIAFCIIinIgJBCGoOCAIAAwMDBAEBAwsgAaciAikCBEKAgICAgICAgMAAVA0EIAAgAhCsAwwFCyAALQBoQQJGDQQgAadBCGoiAhBGIAIgAEHYAGoiAiACKAIEEIgFIAAtAGgNBCAAQQE6AGggAEHYAGohAgJAAkADQCACIAAoAlwiBEcEQCAEQQhrIgQoAgANAiAAIAQQ3gUMAQsLIABBADoAaAwBC0Gz9ABBvuMAQdsqQesVEAAACwwECyAAIAGnEKwDDAMLIAMgAjYCACMAQRBrIgAkACAAIAM2AgxBkLIEQa+AASADEKgEIABBEGokAAsQAQALIAAgAhAhCyADQRBqJAALiQEBAX8gAigCBEUEQCACQRhqEEYCQCABKAIABEAgAhCmBQwBCyAAIAIpAyAQJwsgACACKQMoECcgAiACKAIAQQFrIgM2AgACQCADRQRAIAJBEGoQRiAAIAIQIQwBCyACQoCAgIAwNwMoIAJCgICAgDA3AyAgAkEBNgIECyABIAEoAgxBAWs2AgwLCx4AIAEgADYCBCAAIAI2AgQgACABNgIAIAIgADYCAAs/AQF/IAFBACABQQBKGyEBA0ACQCABIANGBEBBfyEDDAELIAAgA0EDdGooAgQgAkYNACADQQFqIQMMAQsLIAMLnQQCAn8EfgJAIAIQIkUEQCAAECkMAQsCQCAAIAJBPRB6BH9CgICAgDAhBUKAgICAMCEGQoCAgIAwIQcgACACQT0gAkEAEBQiCBANDQFBgQJBgAIgACAIEC0bBUEACyEDIAAgAkE+EHoEQEKAgICAMCEFQoCAgIAwIQZCgICAgDAhByAAIAJBPiACQQAQFCIIEA0NAUGCBEGABCAAIAgQLRsgA3IhAwsgACACQT8QegRAQoCAgIAwIQVCgICAgDAhBkKAgICAMCEHIAAgAkE/IAJBABAUIggQDQ0BQYQIQYAIIAAgCBAtGyADciEDC0KAgICAMCEGAkAgACACQcAAEHpFBEBCgICAgDAhBwwBC0KAgICAMCEFIAAgAkHAACACQQAQFCIHEA0EQAwCCyADQYDAAHIhAwsCQAJAIAAgAkHBABB6RQ0AQoCAgIAwIQVBgC4hBCAAIAJBwQAgAkEAEBQiBhANDQEgA0GAEHIhAyAGEBINACAAIAYQO0UNAQsCQCAAIAJBwgAQekUEQEKAgICAMCEFDAELQfEtIQQgACACQcIAIAJBABAUIgUQDQ0BIANBgCByIQMgBRASDQAgACAFEDtFDQELIANBgDBxBEBBltEAIQQgA0GAxABxDQELIAEgBTcDGCABIAY3AxAgASAHNwMIIAEgAzYCAEEADwsgACAEQQAQFgsgACAHEAwgACAGEAwgACAFEAwLQX8LiAMCB38CfiMAQSBrIgQkACAEQQA2AgwgBEEANgIIAkAgACABIAIgAUEAEBQiCxANBEAgCyEBDAELAkACQCALECJFBEAMAQsgACALEMIBIglBAEgNAQJAIAkEQCAAIARBDGogCxDcAUUNAQwDCyAAIARBCGogBEEMaiALp0EREJIBIQUgBCgCCCEGIAVBAEgNAgsgBCgCDCEIA0AgByAIRg0BAkAgCQRAIAAgBxDmBSIFDQEMBAsgACAGIAdBA3RqKAIEEBkhBQsgACALIAUgAxCLBSIMEA0EQCAAIAUQEwwDCwJ/IAwQEgRAIAAgCyAFQQAQ3gEMAQsgACALIAUgDEEHEBsLIQogACAFEBMgB0EBaiEHIApBAE4NAAsMAQsgACAGIAgQZkEAIQYgACACEGAiDBANDQAgBCALNwMYIAQgDDcDECAAIAMgAUECIARBEGoQJCEBIAAgDBAMIAAgCxAMDAELIAAgBiAEKAIMEGYgACALEAxCgICAgOAAIQELIARBIGokACABC+sCAQN/IwBBQGoiAyQAAkAgACABEGMiARANDQACQCAAIANBIGogAaciBCgCBEH/////B3FBAmoQQg0AIANBIGpBIhA+DQAgA0EANgI8A0AgBCgCBEH/////B3EgAkoEQAJAAkACQAJAAkACQAJAAkACQAJAIAQgA0E8ahDbASICQQhrDgYFAgQBBgMACyACQSJGIAJB3ABGcg0GCyACQYBwcUGAsANHIAJBIE9xDQYgAyACNgIAIANBEGoiAkEQQcAPIAMQVxogA0EgaiACEI4BDQoMBwtB9AAhAgwEC0HyACECDAMLQe4AIQIMAgtB4gAhAgwBC0HmACECCyADQSBqQdwAED4NBCADQSBqIAIQPkUNAQwECyADQSBqIAIQwAENAwsgAygCPCECDAELCyADQSBqQSIQPg0AIAAgARAMIANBIGoQOSEBDAELIAAgARAMIANBIGoQREKAgICA4AAhAQsgA0FAayQAIAELbgEEf0F/IQZBfyACKAIAIgRBAXYgBGogBEGp1arVeksbIQUCQAJAIAMgASgCACIHRgRAIAAgBRAvIgBFDQIgACADIAQQJRoMAQsgACAHIAUQmgIiAEUNAQsgASAANgIAIAIgBTYCAEEAIQYLIAYLYQECfwNAIAAoAigiAUEATEUEQCAAIAFBAWsiATYCKCAAKAIAIAAoAgQgAUEDdGopAwAQDAwBCwsgACgCBCIBIABBCGoiAkcEQCAAKAIAIAEQGgsgAEEENgIsIAAgAjYCBAukBQILfwV+IwBBMGsiAiQAIAEpAyAhDyABKQMYIQ4gASkDCCENIAEpAwAhEAJ+AkACQCABKQMoIhEQngEEQCANEJ4BDQELIABB/MMAQQAQFgwBCyAAIAJBCGpBABBCGiACQQA2AiQCQCAOEBJFBEAgACACQSRqIA4Q3AENAQsgACACQShqIBAQ3AENACAAIAJBLGogASkDEBDHAUEASA0AIA2nIQcgAigCLCIKIAIoAihqIQsgEaciBCgCBEH/////B3EhCCACKAIkIQlBACEBA0ACQAJAAkAgBEEkIAEQ2QEiBkEASA0AIAZBAWoiAyAITw0AIAJBCGogBCABIAYQWRogBkECaiEBAkACQAJAAkAgBCADEE0iBUEkaw4EAAMFAQILIAJBCGpBJBA+GgwGCyACQQhqIAcgCyAHKAIEQf////8HcRBZGgwFCyAFQeAARg0DCwJAIAVBMGsiA0EJTQRAAkAgASAITw0AIAQgARBNIgVBMGtBCUsNACAGQQNqIAEgBSADQQpsaiIBQTBLIAFBMGsiBSAJSXEiDBshASAFIAMgDBshAwsgA0UgAyAJT3INASAAIA4gA60QZCINEA0NBiANEBINBSACQQhqIA0QjwFFDQUMBgsgBUE8Rw0AIA8QEg0AIARBPiABENkBIgNBAEgNACAAIAQgASADEJ0BIg0QDQ0FIAAgDyANEKEBIg0QDQ0FIA0QEkUEQCACQQhqIA0QjwENBgsgA0EBaiEBDAQLIAJBCGogBCAGIAEQWRoMAwsgAkEIaiIAIAQgASAEKAIEQf////8HcRBZGiAAEDkMBQsgAkEIaiAQEJwBRQ0BDAILIAJBCGogB0EAIAoQWRoMAAsACyACQQhqEEQLQoCAgIDgAAshDiACQTBqJAAgDgvqBQIIfwV+IwBBEGsiByQAQoCAgIDgACEPAkAgACABQQEQ3QEiAkUNACAAIAMpAwAQLiINEA0EQCANIQ8MAQsCQCAAIAFB1QAgAUEAEBQiDBANDQAgACAHQQhqIAwQsAENACACKAIEQRBqIgItAABBIXEiBEUEQCAHQgA3AwgLAkAgAi0AASIJRQRAQQAhAwwBCyAAIAlBA3QQLyIDRQ0BCwJAAkACQAJAAkACQAJAAkAgBykDCCIMIA2nIgopAgQiDkL/////B4NVDQAgAyACIApBEGoiCCAMpyAOpyIFQf////8HcSAFQR92IgUgABDFBCIGQQFGDQMgBkEASA0BIAZBAkYNACAERQ0CCyAAIAFB1QBCABBIQQBODQEMBAsgAEGZNUEAEFAMAwsgACANEAxCgICAgCAhAQwBCyAEBEAgACABQdUAIAMoAgQgCGsgBXWtEEhBAEgNAgtCgICAgDAhDCAAEFEiARANDQIgAi0AAEGAAXEEfyACIAIoAANqQQdqBUEACyIEBEAgAEKAgICAIBBVIgwQDQ0DC0EAIQIDQCACIAlHBEBCgICAgDAhDgJAIAMgAkEDdGooAgAiBkUNACADIAJBA3RBBHJqKAIAIgtFDQAgACAKIAYgCGsgBXUgCyAIayAFdRCdASIOEA0NBQsgBEUgAkVyRQRAAkAgBC0AAEUNACAAIAwgBCAOEA8iEEGHgAEQ7AFBAE4NACAAIBAQDAwGCyAEEEMgBGpBAWohBAsgACABIAIgDkGHgAEQnwEhBiACQQFqIQIgBkEATg0BDAQLCyAAIAFBhwEgDEGHgAEQG0EASA0CIAAgAUHXACADKAIAIAhrIAV1rUGHgAEQG0EASA0CIAAgAUHYACANQYeAARAbQQBIDQMLIAAgAxAaIAEhDwwEC0KAgICAICEBQoCAgIAwIQwLIAAgDBAMIAAgDRAMCyAAIAEQDCAAIAMQGgwBCyAAIA0QDAsgB0EQaiQAIA8LMAADQCABQYABSUUEQCAAIAFBgAFyQf8BcRAQIAFBB3YhAQwBCwsgACABQf8BcRAQC18AIABCKIZCgICAgICAwP8AgyAAQjiGhCAAQhiGQoCAgICA4D+DIABCCIZCgICAgPAfg4SEIABCCIhCgICA+A+DIABCGIhCgID8B4OEIABCKIhCgP4DgyAAQjiIhISEC10BBH8gASEDAkADQCACIANNIARBBEtyDQEgAy0AACIGQf8AcSAEQQdsdCAFciEFIARBAWohBCADQQFqIQMgBkGAAXENAAsgACAFNgIAIAMgAWsPCyAAQQA2AgBBfwtfAQF/IAFBEGohAwJAIAEtAAdBgAFxBEAgACADIAJBAXQQJRoMAQtBACEBIAJBACACQQBKGyECA0AgASACRg0BIAAgAUEBdGogASADai0AADsBACABQQFqIQEMAAsACwuwAQECfyMAQRBrIgYkAAJAAkAgAhAiRQ0AIAKnIgcvAQZBDEcNACAHLQApQQxHDQAgACABIAMgAwR/IAQFIAZCgICAgDA3AwggBkEIagsgBSAHLgEqIAcoAiQREgAhAgwBCwJAIAAgAiABIAMgBBAkIgIQDUUEQCACECINASAAIAIQDCAAQYIdQQAQFgsgBUEANgIAQoCAgIDgACECDAELIAVBAjYCAAsgBkEQaiQAIAILFAEBfiAAIAEQKyECIAAgARAMIAILHAEBfyAAQoCAgIBwWgR/IACnLQAFQQd2BUEACwsNACAAIAEgAkEAEN8BC0MAAnwgAb1CgICAgICAgPj/AINCgICAgICAgPj/AFEEQEQAAAAAAAD4fyAAmUQAAAAAAADwP2ENARoLIAAgARCCBgsLewEBfgJAAn4gBEEEcQRAQSYhAiAAIAEQYwwBC0ElIQIgACABECsLIgEQDQ0AIAAgAhCkASIFEA0NACAAQRAQLyICBEAgAkEANgIMIAIgBEEDcTYCCCACIAE3AwAgBSACEI0BIAUPCyAAIAUQDAsgACABEAxCgICAgOAAC5UBAgJ+AX8gACABEDIhAwJAIAEQXg0AQQAgACgCECgCOCABQQJ0aigCACkCBCICQoCAgICAgICAQINCgICAgICAgICAf1IgAkKAgICA8P///z+DUCACQv//////////v39WcRsNACACp0F/c0EfdkEBIAJC/////weDUBshBAsgBAR+IABBhOcAIANBgucAEL8BBSADCwvcAwEFfyAAQeAAaiIEEHEgAEHQAGohBSAAKAJUIQECQAJAA0AgASAFRwRAIAFBBGstAABBEE8NAiABKAIEIQIgACABQQhrIgNBBhCfBCADIAMtAARBD3FBEHI6AAQgAiEBIAMoAgANASADQQhqIgIQRiACIAQQTAwBCwsMAQtB+vQAQb7jAEHELEG6xgAQAAALIABB1ABqIQEgAEHQAGohAwJAAkADQCADIAEoAgAiAkcEQCACQQhrIgEoAgBBAEwNAiABIAEtAARBD3E6AAQgACABQQcQnwQgAkEEaiEBDAELCyAAQeQAaiEBIABB4ABqIQIDQCACIAEoAgAiAUcEQCAAIAFBCGtBCBCfBCABQQRqIQEMAQsLDAELQZfzAEG+4wBB5yxB7DsQAAALIAAiAkECOgBoIABB2ABqIQMgAEHgAGohBANAIAQgAigCZCIARwRAIABBCGshASAAQQRrLQAAQQ5xBEAgAUEIaiIAEEYgACADEEwFIAIgARDeBQsMAQsLIAJBADoAaCACKAJcIQACQAJAA0AgACADRwRAIABBBGstAABBDnENAiAAKAIEIQEgAiAAQQhrECEgASEADAELCyADEHEMAQtBv+0AQb7jAEGdLUGwJRAAAAsLpwEBBX8gAKciAygCECIBIAEoAhhBf3NBAnRBpH5yaigCACECIAEQKiEBA0AgAkUEQEEADwsgASACQQFrIgRBA3RqIgUoAgAhAiAFKAIEQTZHBEAgAkH///8fcSECDAELC0EBIQECQCACQf////8DSw0AIAMoAhQgBEEDdGopAwAiAEKAgICAcINCgICAgJB/Ug0AIACnKAIEQf////8HcUEARyEBCyABCwwAIAAgAUHSFBDIAQtQAgF/AX4CQCAAIAFB6QAgAUEAEBQiBBANRQRAIAAgBBAtIQMgACABQcAAIAFBABAUIgEQDUUNAQtCgICAgOAAIQFBACEDCyACIAM2AgAgAQvEAQEEfyABpyIFIAI2AiAgBUIANwIkAkAgAigCPCIGRQ0AAkAgACAGQQJ0EGwiCEUNACAFIAg2AiRBACEFA0AgBSACKAI8Tg0CIAIoAiQgBUEDdGoiBy8BAiEGAkAgBy0AACIHQQFxBEAgACAEIAYgB0EBdkEBcRCKBCIGDQEMAwsgAyAGQQJ0aigCACIGIAYoAgBBAWo2AgALIAggBUECdGogBjYCACAFQQFqIQUMAAsACyAAIAEQDEKAgICA4AAhAQsgAQvrAwEFfyMAQRBrIgckAAJAAkADQCABQQA2AgAgAkEANgIAQQAhBiAFKAIIIghBACAIQQBKGyEIA0ACQCAGIAhGBEBBfyEGDAELIAMgBSgCACAGQQN0aiIKKAIARgRAIAooAgQgBEYNAQsgBkEBaiEGDAELCyAGQQBOBEBBAiEGDAMLIAAgBUEIIAVBBGogBSgCCEEBahCAAQR/QX8FIAUgBSgCCCIGQQFqNgIIIAUoAgAgBkEDdGoiBiADNgIAIAYgACAEEBk2AgRBAAtBAEgEQEF/IQYMAwsgAyAEEL8FIgYEQCAGKAIIRQ0CIAYoAgwiBEH9AEYNAiADKAIQIAYoAgBBA3RqKAIEIQMMAQsLIARBFkcEQANAIAMoAiwgCUoEQAJAAkAgACAHQQxqIAdBCGogAygCECADKAIoIAlBAnRqKAIAQQN0aigCBCAEIAUQoQUiBkEBag4FBgABAQYBCyACKAIAIgYEQCABKAIAIAcoAgxGBEAgBygCCCgCDCAGKAIMRg0CCyABQQA2AgAgAkEANgIAQQMhBgwGCyABIAcoAgw2AgAgAiAHKAIINgIACyAJQQFqIQkMAQsLQQAhBiACKAIADQILQQEhBgwBCyABIAM2AgAgAiAGNgIAQQAhBgsgB0EQaiQAIAYL2QMBCH8gASgCCCIGQQAgBkEAShshBAJAAkADQCAEIAVGDQEgBUECdCEHIAVBAWohBSAHIAEoAgBqKAIAIAJHDQALQQAhBAwBC0F/IQQgACABQQQgAUEEaiAGQQFqEIABDQAgASABKAIIIgRBAWo2AgggASgCACAEQQJ0aiACNgIAIANBAEchCiABQRBqIQsgAUEMaiEJQQAhBQNAAkAgAigCICAFTARAQQAhBEEAIQUDQCAFIAIoAixODQQgBUECdCEDIAVBAWohBSAAIAEgAigCECADIAIoAihqKAIAQQN0aigCBEEBEKIFRQ0ACwwBCwJAIAogAigCHCAFQRRsaiIHKAIQIgRBFkZxDQBBACEGIAEoAhQiCEEAIAhBAEobIQgDQAJAIAYgCEYEQEF/IQYMAQsgASgCDCAGQQxsaigCACAERg0AIAZBAWohBgwBCwsgBiIEQQBIBEAgACAJQQwgCyABKAIUQQFqEIABDQIgASABKAIUIgRBAWo2AhQgASgCDCAEQQxsaiIEIAcoAhA2AgACQCADRQRAIAcoAghFDQELIARBADYCCAwCCyAEIAc2AggMAQsgCSgCACAEQQxsakEANgIICyAFQQFqIQUMAQsLQX8PCyAEC2UBBH8DQCACIAVKBEAgASAFaiIGLQAAIgRBD2ogBCAEQbEBSxsgBCADG0ECdCIEQbCaAWotAAAhByAEQbOaAWotAABBF2tB/wFxQQRNBEAgACAGKAABEPQBCyAFIAdqIQUMAQsLC0gBA38gAkEAIAJBAEobIQIDQCACIANGBEBBAA8LIAEgA2ohBCADQQF0IQUgA0EBaiEDIAAgBWovAQAgBC0AAGsiBEUNAAsgBAtYAQJ/IAEEQAJAIAAoAgggACgCBCIDIAFqSQ0AIAEQowIiAUUNACAAIANBCGo2AgQgACAAKAIAQQFqNgIAIAEhAgsgAg8LQdz1AEG+4wBBog1BouMAEAAAC0wBA38gACgCIEEYaiEBAkADQCABIgMoAgAiAkUNASACQQxqIQEgACACRw0ACyADIAAoAgw2AgAPC0H56gBBvuMAQbzlAkH/xgAQAAALGAEBfyABpygCICIDBEAgACADIAIRAwALC+NyAhN/AX4jAEEQayIUJAAgASgCyAEiB0EAIAdBAEobIQQDQCACIARHBEAgASgCzAEgAkEDdGpBfzYCBCACQQFqIQIMAQsLIAEoAjwEQCABKALMAUF+NgIMC0EAIQIgASgCfCIEQQAgBEEAShshCAJ+AkACQAJAA0AgAiAIRgRAAkBBAiECIAdBAiAHQQJKGyEHA0ACQCACIAdGBEBBACECA0AgAiAIRg0CAkAgASgCdCACQQR0aiIEKAIIQQBODQAgBCgCBCIHQQJIDQAgBCABKALMASIEIAQgB0EDdGooAgBBA3RqKAIENgIICyACQQFqIQIMAAsACyABKALMASIEIAJBA3RqIgYoAgRBAEgEQCAGIAQgBigCAEEDdGooAgQ2AgQLIAJBAWohAgwBCwsgASgCRARAAkACQCABKAIgDQAgAS0AbkEBcQ0AIAEgACABQdIAEFg2ApABIAEoAjxFDQAgASAAIAFB0wAQWDYClAELAkAgASgCTCIHRQ0AIAEoAqgBQQBIBEAgASAAIAEQygM2AqgBCyABKAKsAUEASARAIAEgACABQfEAEFg2AqwBCwJAIAEoAmBFDQAgASgCsAFBAE4NACABIAAgAUHyABBYNgKwAQsgASgCMEUNACABKAK0AUEATg0AIAEgACABQfMAEFg2ArQBCwJAIAEoAkgiBkUNACAAIAEQ8AIaIAEoAjxFDQAgAS0AbkEBcQ0AAkAgASgCnAFBAE4NACABKALMAUEMaiECA0ACQEF/IQQgAigCACICQQBIDQAgASgCdCACQQR0aiIIKAIEQQFHDQAgAiEEIAgoAgBBzQBGDQAgCEEIaiECDAELCyAEQQBODQAgACABQc0AEFgiCEEASA0AIAEoAnQgCEEEdGoiBCABKALMASICKAIMNgIIIAIgCDYCDCAEQQE2AgQgBCAEKAIMQQJyNgIMIAEgCDYCnAELCwJAIAEoAixFDQAgASgCcCICRQ0AIAAgASACEO8CGgsCQAJAIAEoAiAEQCABIQIMAQsgASECIAEoAsACDQELA0AgAigCBCIEBEAgAigCDCEIAkAgBw0AIAQoAkxFBEBBACEHDAELIAQoAqgBQQBIBEAgBCAAIAQQygM2AqgBCyAEKAKsAUEASARAIAQgACAEQfEAEFg2AqwBCwJAIAQoAmBFDQAgBCgCsAFBAE4NACAEIAAgBEHyABBYNgKwAQtBASEHIAQoAjBFDQAgBCgCtAFBAE4NACAEIAAgBEHzABBYNgK0AQsCQCAGDQAgBCgCSEUEQEEAIQYMAQsgACAEEPACGkEBIQYLAkAgBCgCLEUNACAEKAJwIgJFDQAgACAEIAIQ7wIaCyAEKALMASAIQQN0akEEaiECA0AgAigCACIFQQBOBEAgBCgCdCAFQQR0aiIIIAgoAgwiAkEEcjYCDCAAIAEgBEEAIAUgCCgCACACQQFxIAJBAXZBAXEgAkEDdkEPcRCnARogCEEIaiECDAELCwJAIAVBfkcEQEEAIQIDQCAEKAKIASACTARAQQAhAgNAIAIgBCgCfE4NBAJAIAQoAnQgAkEEdGoiCCgCBA0AIAgoAgAiCEUgCEHRAEZyDQAgACABIARBACACIAhBAEEAQQAQpwEaCyACQQFqIQIMAAsACyAEKAKAASACQQR0aigCACIIBEAgACABIARBASACIAhBAEEAQQAQpwEaCyACQQFqIQIMAAsAC0EAIQIDQCACIAQoAnxODQECQCAEKAJ0IAJBBHRqIggoAgQNACAIEPoERQ0AIAAgASAEQQAgAiAIKAIAQQBBAEEAEKcBGgsgAkEBaiECDAALAAsgBCICKAIgRQ0BQQAhAgNAIAQoAsACIAJMBEAgBCECDAMFIAAgASAEQQAgBCgCyAIgAkEDdGoiCC0AACIFQQF2QQFxIAIgCCgCBCAFQQJ2QQFxIAVBA3ZBAXEgBUEEdhCGAhogAkEBaiECDAELAAsACwsMAQtBi/QAQb7jAEG17AFBvyUQAAALCyABKAKUAwRAQQAhAiABKAKUAyEFAkADQAJAIAEoAvQBIAJMBEBBACEHQQAhAgNAIAIgBSgCIE4NBCAFKAIcIAJBFGxqIggoAghFBEAgCCgCDCEGQQAhCiABKALAAiIEQQAgBEEAShshBANAAkAgBCAKRgRAQX8hCgwBCyABKALIAiAKQQN0aigCBCAGRg0AIApBAWohCgwBCwsgCiIEQQBIBEAgACAGQawUEJUDDAQLIAggBDYCAAsgAkEBaiECDAALAAsgACABQQFBACACIAEoAvwBIAJBBHRqIgQoAgwgBC0ABCIEQQJ2QQFxIARBAXZBAXFBABDLAyEEIAJBAWohAiAEQQBODQELC0F/IQcLIAcNAQsgAUEQaiEHIAEoAhQhAgJAA0AgAiAHRwRAIAIoAgQhBCACQRBrKAIAIQYgACACQRhrEKgFIhUQDQ0DIAZBAEgNAiABKAK0AiAGQQN0aiAVNwMAIAQhAgwBCwsCf0EAIQIjAEGQAWsiDCQAIAwgASgCgAIiEzYCUCAMIAEoAoQCIgM2AlQgACAMQfgAahCRAiABQYACaiESA38gASgC9AEgAkwEf0EAIQdBAAVBACEEIAEoAsACIgdBACAHQQBKGyEIIAEoAvwBIAJBBHRqIQUCQCAMQfgAagJ/A0AgBCAIRwRAIAEoAsgCIARBA3RqIgYoAgQiByAFKAIMRgRAIAEoAiRBAkcNBCAGLQAAQQhxRQ0EIAxB+ABqIgRBMBAQIAQgACAFKAIMEBkQHkEBDAMLIAdBfnFB0gBGDQMgBEEBaiEEDAELCyAMQfgAaiIEQT8QECAEIAAgBSgCDBAZEB4gBS0ABEEGdCIEQYB/cSAEQcAAciAFKAIAQQBIGwtB/wFxEBALIAJBAWohAgwBCwshAgNAAkACQAJAAkACQAJAAkACfwJAAkAgAyAHIgRKBEAgBCAEIBNqIhAtAAAiBkECdEGwmgFqLQAAIhFqIQcCQAJAAkACQAJAAkACQAJAAkAgBkGxAWsOEBQFBgQBAQEBAgEBAwMDFAgACyAGQRFrIgRBH0sNDkEBIAR0QYCA0Ix8cQ0PIARFDQYgBEEFRw0OIAxBfzYCGCAMQsn6gIDgATcDECAMQdAAaiAHIAxBEGoQLEUNESAMQfgAaiAMLQBgEBAgDCgCWCEHIAwoAlwiBEF/RiACIARGcg0TIAEgASgC3AJBAWo2AtwCIAxB+ABqIgJBwAEQECACIAQQHiAEIQIMEwsgACABIBAoAAEiBCAQLwAFIAYgDEH4AGpBAEEAIAcQ4wQhByAAIAQQEwwSCyAQKAABIQggEC8ACSEEIAEoAqQCIBAoAAVBFGxqIgYgBigCAEEBazYCACAAIAEgCCAEQbkBIAxB+ABqIBMgBiAHEOMEIQcgACAIEBMMEQsCfyAQKAABIQggEC8ABSEKIAxB+ABqIQsjAEEQayINJABBfyEOAkACQAJAIAAgDUEIaiANQQxqIAEgCCAKEN0EIg9BAEgNACANKAIMIgVFDQECQAJAAkACQCAGQbwBaw4DAAABAgsCQAJAAkAgBUEFaw4FAAECBQIECyAGQb0BRgRAIAtBERAQCyALIA0oAgggDxCxAiALQcQAEBBBACEODAULIAsgDSgCCCAPELECIAtBLBAQQQAhDiAGQb0BRg0EIAtBDxAQDAQLIAZBvQFGBEAgC0EREBALIAsgDSgCCCAPELECIAtBLBAQIAtBJBAQQQAhDiALQQAQMQwDCwJAAkACQCAFQQVrDgUAAQECAgMLIAsgDSgCCCAPELECIAtBxQAQEEEAIQ4MBAsgC0EwEBAgCyAAIAgQGRAeQQAhDiALQQAQEAwDCyAAIAgQ6QQiBUUNAiAAIA1BCGogDUEMaiABIAUgChDdBCEGIAAgBRATIAZBAEgNAiANKAIMQQhHDQQgCyANKAIIIAYQsQIgC0EbEBAgC0EeEBAgC0EsEBAgC0EdEBAgC0EkEBAgC0EBEDFBACEODAILEAEACyALQTAQECALIAAgCBAZEB5BACEOIAtBABAQCyANQRBqJAAgDgwCC0GF6wBBvuMAQZvrAUGo3AAQAAALQYDpAEG+4wBB2OsBQajcABAAAAtBAEgEQANAIAMgBEwNCCAMQfgAaiAEIBNqIgIgAi0AAEECdEGwmgFqLQAAIgIQigEaIAIgBGohBAwACwALIAAgCBATDBALIBAoAAEiBEEASA0IIAQgASgCrAJODQggASgCpAIgBEEUbGogDCgCfCARajYCCAwNCyAQLwABIgogASgC8AFGBEACQCAMQfgAaiEJQQAhBkEAIQ4DQAJAIAEoAogBIAZMBEBBACEGA0AgBiABKAJ8Tg0CAkAgASgCdCAGQQR0aiIEKAIEDQAgBC0AD0HAAHENACAJQQMQECAJIAQoAgxBAXRBCHUQHiAJQdkAEBAgCSAGQf//A3EQMQsgBkEBaiEGDAALAAsgASgCgAEgBkEEdGoiBC0AD0HAAHFFBEAgCUEDEBAgCSAEKAIMQQF0QQh1EB4gCUHcABAQIAkgBkH//wNxEDELIAZBAWohBgwBCwtBfyENIAEoApQDBEAgAUF/ENADIQ0gCUEIEBAgCUHpABAQIAkgDRAeIAEgDUEBEHQaIAEgASgC0AJBAWo2AtACCwNAAkACQCABKAL0ASAOSgRAQQAhBiABKALAAiIEQQAgBEEAShshBCABKAL8ASAOQQR0aiILLQAEIgVBAXEhDwJ/A0AgBCAGRwRAIAEoAsgCIAZBA3RqKAIEIgggCygCDEYEQEEAIQ8gBiEEQQIMAwsgCEF+cUHSAEYEQCAJQd4AEBAgCSAGQf//A3EQMUEBIQ8gBiEEQQEMAwUgBkEBaiEGDAILAAsLIAEoAiRBAEchCEEAIAsoAgBBAE4gBUECcSIGGw0CIAlBPhAQIAkgACALKAIMEBkQHiAJQYB/QYJ/IAVBBHEbQQAgBhsgCHJBgwFxEBBBAAshCEEAIAsoAgAiBkEASCAPGw0CAkAgBkEATgRAIAlBAxAQIAkgCygCABAeIAsoAgxB/ABHDQEgCUHNABAQIABBFhAZGiAJQRYQHgwBCyAJQQYQEAsCQAJAAkAgCEEBaw4CAQACCyAJQd8AEBAgCSAEQf//A3EQMQwECyAJQcwAEBAgCSAAIAsoAgwQGRAeIAlBDhAQDAMLIAlBORAQIAkgACALKAIMEBkQHgwCCyABKAKUAwRAIAlBKRAQIAlBtAEQECAJIA0QHiABKAKkAiANQRRsaiAJKAIENgIICyAAIAEoAvwBEBogAUIANwL0ASABQQA2AvwBDAMLIAlBAxAQIAkgCygCABAeIAlBwAAQECAJIAAgCygCDBAZEB4gCSAIEBALIAAgCygCDBATIA5BAWohDgwACwALCyABKALMASAKQQN0akEEaiEEA0AgBCgCACIFQQBIDQ8gASgCdCAFQQR0aiIIKAIEIApHDQ8gASgCnAEgBUcEQCAMQfgAaiIGIAgoAgxBA3ZBD3FBAWtBAU0EfyAMQfgAaiIEQQMQECAEIAgoAgxBAXRBCHUQHkHZAAVB4QALEBAgBiAFQf//A3EQMQsgCEEIaiEEDAALAAsgASgCzAEgEC8AASIGQQN0akEEaiEEA0AgBCgCACIFQQBIDQ4gASgCdCAFQQR0aiIIKAIEIAZHDQ4gCC0ADEEEcQRAIAxB+ABqIgRB6AAQECAEIAVB//8DcRAxCyAIQQhqIQQMAAsACyAMQX82AkggDELp1IGA4AE3A0AgDEHQAGogByAMQUBrECxFDQogDCgCaCIFQQBIDQYgBSABKAKsAk4NBiAMKAJcIQYgDCgCWCEIIAwoAmAhCSAFIQQDQEEAIQsgASIKKAKAAiEOIAEoAqQCIQ8DQAJAIAtBFEYNACAPIARBFGxqKAIEIQoDQCAKIA5qIgQtAAAiDUG0AUYgDUHAAUZyBEAgCkEFaiEKDAEFIA1B6wBHDQIgC0EBaiELIAQoAAEhBAwDCwALAAsLIAohBCAMQo6AgIBwNwM4IAwgCTYCNCAMQRE2AjAgDEHQAGogBCAMQTBqECwEQCAMKAJoIQQMAQsLIAxBfzYCJCAMIAk2AiAgDEHQAGogBCAMQSBqECxFDQogASABKALQAkEBajYC0AIgASAFQX8QdBogASAMKAJoIgdBARB0GiAMQfgAaiIEIAlB/wFxEBAgBCAHEB4gCCEHIAZBf0YgAiAGRnINDCABIAEoAtwCQQFqNgLcAiAMQfgAaiICQcABEBAgAiAGEB4gBiECDAwLIBAoAAEhAiABIAEoAtwCQQFqNgLcAgwJCyASEJcBIBIgDCkDiAE3AhAgEiAMKQOAATcCCCASIAwpA3g3AgBBACASKAIMRQ0CGiAAEMkBDAELIBIQlwEgEiAMKQOIATcCECASIAwpA4ABNwIIIBIgDCkDeDcCAAtBfwshAiAMQZABaiQAIAIMCAtB3xZBvuMAQYzyAUHSJRAAAAtBhBdBvuMAQd3yAUHSJRAAAAsCQAJAAkAgBkHpAGsOBgQEAgQBAwALIAZBMUYEQCAQLwABIQYgASAQLwADIgQQ4gQgDEH4AGoiCEExEBAgCCAGEDEgCCABKALMASAEQQN0ai8BBEEBakH//wNxEDEMBwsgBkEyRwRAIAZBzQBHDQUgECgAAUUNBwwFCyABIBAvAAEiBhDiBCAMQfgAaiIEQTIQECAEIAEoAswBIAZBA3RqLwEEQQFqQf//A3EQMQwGCyABIAEoAtACQQFqNgLQAiAQKAABIgRBAEgNBCAEIAEoAqwCTg0EIAEoAqQCIARBFGxqIgYoAgQhBCAMQu6AgIBwNwMAIAxB0ABqIAQgDBAsRQ0DIAYgBigCAEEBazYCAAwFCyABIAEoAtACQQFqNgLQAgsgDEF/NgJMIAxB+ABqIBAgERCKARogASATIAMgByAMQcwAahCyAiIHIANODQMgDCgCTCIEQQBIIAIgBEZyDQMgASABKALcAkEBajYC3AIgDEH4AGoiAkHAARAQIAIgBBAeIAQhAgwDCyABIAEoAtACQQFqNgLQAgsgDEH4AGogECAREIoBGgwBCwtB3xZBvuMAQbzxAUHSJRAAAAsNAQJ/IwBB0AVrIgMkACABKAKkAiEPIAMgASgC8AI2AsgFIAMgASgCgAIiCzYCiAUgAyABKAKEAiIONgKMBSAAIANBsAVqEJECAkACfwJAIAEoAtACIgIEQCABIAEoAgAgAkEEdBBsIgI2AswCIAJFDQELAkAgASgC3AIiAkUNACABLQBuQQJxDQAgASABKAIAIAJBA3QQbCICNgLYAiACRQ0BIAFBADYC6AIgASABKALwAjYC5AILIAEoArQBQQBOBEAgA0GwBWoiAkEMEBAgAkEEEBAgAkHZACABKAK0ARBoCyABKAKwAUEATgRAIANBsAVqIgJBDBAQIAJBAhAQIAJB2QAgASgCsAEQaAsgASgCrAFBAE4EQCADQbAFaiICQQwQECACQQMQECACQdkAIAEoAqwBEGgLAkAgASgCqAFBAEgNACABKAJgBEAgA0GwBWoiAkHhABAQIAIgAS8BqAEQMQwBCyADQbAFaiICQQgQECACQdkAIAEoAqgBEGgLIAEoApgBQQBOBEBBACECIAEtAG5BAXFFBEAgASgCOEEARyECCyADQbAFaiIEQQwQECAEIAIQECABKAKcASICQQBOBEAgA0GwBWpB2gAgAhBoCyADQbAFakHZACABKAKYARBoCyABKAKgAUEATgRAIANBsAVqIgJBDBAQIAJBAhAQIAJB2QAgASgCoAEQaAsgASgCkAFBAE4EQCADQbAFaiICQQwQECACQQUQECACQdkAIAEoApABEGgLIAEoApQBQQBOBEAgA0GwBWoiAkEMEBAgAkEFEBAgAkHZACABKAKUARBoCyABQYACaiENQQAhAgNAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAIgDk4EQEEAIQIgASgCrAIiBEEAIARBAEobIQcDQCACIAdGDQIgAkEUbCEEIAJBAWohAiAEIA9qKAIQRQ0AC0Gs6gBBvuMAQf36AUHrIxAAAAsgAiACIAtqIgktAAAiBUECdEGwmgFqLQAAIgpqIQQCQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAVB2ABrDiAQEhoREhoREhoaGhoaGhoaGgQEAQMCGhoMDAUFBQUFBQALAkAgBUEBaw4VCQoKCxoNBxoICBoaGgYaGg8aGhoOAAsgBUEiayIGQR9LDRhBASAGdCIHQcDhAXENEiAHQQVxRQRAIAZBH0cNGSAJKAABQTBHDRogAEEwEBMgASADKAK0BSADKALIBRA0IANBsAVqQecBEBAgBCECDCMLIAkvAAEhAiADQqiAgIBwNwMAIANBiAVqIAQgAxAsBEACQCADKAKUBSIEQQBIBEAgAygCyAUhBAwBCyADIAQ2AsgFCyABIAMoArQFIAQQNCADQbAFaiAFQQFqIAIQaCABIAsgDiADKAKQBSADQcgFahCyAiECDCMLIAEgAygCtAUgAygCyAUQNCADQbAFaiAFIAIQaCAEIQIMIgsgCSgAASEFIAQhBwwWC0HtACEFIAkoAAEhBgwUC0HsACEFIAkoAAEhBgwTCyADQYgFaiAEIAEgCSgAASADQcwFakEAEMkDIgYQyAMEQCABIAZBfxB0GiADQbAFakEOEBAgBCECDB8LIANC64CAgHA3AxAgA0GIBWogBCADQRBqECxFDRIgAygClAUhCCADQYgFaiADKAKQBSIHIAYQyANFDRIgCEEATgRAIAMgCDYCyAULIAEgBkF/EHQaIAVBA3MhBSADKAKgBSEGDBwLIAkoAAEhBiAJLQAJIQcgASAJKAAFIANBzAVqQQAQyQMiCkEASA0PIAogASgCrAJODQ8gASADKAK0BSADKALIBRA0IAEgASgC1AIiAkEBajYC1AIgASgCzAIgAkEEdGoiCEEENgIEIAggBTYCACADKAK0BSECIAggCjYCDCAIIAJBBWo2AgggA0GwBWoiAiAFEBAgAiAGEB4gAiAPIApBFGxqIgIoAgwgAygCtAVrEB4gAigCDEF/RgRAIAAgAiADKAK0BUEEa0EEEO4CRQ0dCyADQbAFaiAHEBAgBCECDB0LIANCqYCAgHA3AyAgA0GIBWogBCADQSBqECxFDRMgBCECIAMoApQFIgRBAEgNHCADIAQ2AsgFDBwLIANCq4GAgHA3A1AgA0GIBWogBCADQdAAahAsBEACQCADKAKUBSICQQBIBEAgAygCyAUhAgwBCyADIAI2AsgFCyABIAMoArQFIAIQNCADQbAFakHxARAQDBgLIANBfzYCSCADQqyBgICQzRo3A0AgA0GIBWogBCADQUBrECxFDQACQCADKAKUBSIFQQBIBEAgAygCyAUhBQwBCyADIAU2AsgFCyABIAMoArQFIAUQNCADQbAFakHxARAQIAMoApgFQQNzIQUMGAsgA0Lp1IGAcDcDMCADQYgFaiAEIANBMGoQLEUNESAFQQpGIQgMDQsCQCAJKAABIgdB/////wdxRQ0AIANCjIGAgHA3A5ABIANBiAVqIAQgA0GQAWoQLEUNACADKAKUBSICQQBOBEAgAyACNgLIBQsgA0KOgICAcDcDgAEgA0GIBWogAygCkAUgA0GAAWoQLARAIAMoApQFIgJBAEgNFyADIAI2AsgFDBcLIAEgAygCtAUgAygCyAUQNCADQbAFakEAIAdrEMcDDBYLIANCjoCAgHA3A3AgA0GIBWogBCADQfAAahAsBEAgAygClAUiAkEASA0WIAMgAjYCyAUMFgsgA0Lp1IGAcDcDYCADQYgFaiAEIANB4ABqECwEQCAHQQBHIQgMDQsgASADKAK0BSADKALIBRA0IANBsAVqIAcQxwMgBCECDBkLIAkoAAEiB0H/AUoNDyABIAMoArQFIAMoAsgFEDQgA0GwBWoiAiAFQcUAa0H/AXEQECACIAdB/wFxEBAgBCECDBgLIAkoAAEhAiADQo6AgIBwNwOgASADQYgFaiAEIANBoAFqECwEQCAAIAIQEyADKAKUBSICQQBIDRQgAyACNgLIBQwUCyACQS9HDQ4gAEEvEBMgASADKAK0BSADKALIBRA0IANBsAVqQb8BEBAgBCECDBcLIANCyYCAgHA3A9gBIANC2Lb5gnA3A9ABIANBiAVqIAQiAiADQdABahAsDRYgA0F/NgLIASADQoGEkICQCTcDwAEgA0GIBWogAiADQcABahAsDRYgA0F/NgK4ASADQoaOqMiQCTcDsAEgA0GIBWogBCADQbABahAsDRYMDQsgA0KOgICAcDcDoAIgA0GIBWogBCADQaACahAsBEAgAygClAUiAkEASA0SIAMgAjYCyAUMEgsgA0KogICAcDcDkAIgA0GIBWogBCADQZACahAsBEACQCADKAKUBSICQQBIBEAgAygCyAUhAgwBCyADIAI2AsgFCyABIAMoArQFIAIQNCADQbAFakEpEBAMEgsgA0Lp1IGAcDcDgAJBACEIIANBiAVqIAQgA0GAAmoQLA0IIANCq4GAgHA3A/ABIANBiAVqIAQgA0HwAWoQLARAAkAgAygClAUiAkEASARAIAMoAsgFIQIMAQsgAyACNgLIBQsgASADKAK0BSACEDQgA0GwBWpB8AEQEAwSCyADQX82AugBIANCrIGAgJDNGjcD4AEgA0GIBWogBCADQeABahAsRQ0MAkAgAygClAUiBUEASARAIAMoAsgFIQUMAQsgAyAFNgLIBQsgASADKAK0BSAFEDQgA0GwBWpB8AEQECADKAKYBUEDcyEFDBILIANBfzYCuAIgA0LD9oCA4AE3A7ACIANBiAVqIAQgA0GwAmoQLEUNCwJAIAMoApQFIgJBAEgEQCADKALIBSECDAELIAMgAjYCyAULIAEgAygCtAUgAhA0IANBsAVqIgIgAy0AmAUQECACIAMoAqgFEB4MEAsgA0F/NgLoAiADQtm4/YJwNwPgAiADQYgFaiAEIANB4AJqECxFDQogAygClAUiAkEATgRAIAMgAjYCyAULIANCjoCAgHA3A9ACIAMoApgFIgVBAWohBwJAIANBiAVqIAMoApAFIgIgA0HQAmoQLAR/IAMoApQFIgJBAE4EQCADIAI2AsgFCyADIAMoApwFNgLEAkF/IQQgA0F/NgLIAiADIAVBAWs2AsACIANBiAVqIAMoApAFIgIgA0HAAmoQLEUNASADKAKQBSECIAMoApQFBUF/CyEEIAchBQsgASADKAK0BSADKALIBRA0IANBsAVqIAUgAygCnAUQaCAEQQBIDRMgAyAENgLIBQwTCyAJLwABIgdB/wFLDQkgA0KOgICAcDcC/AMgAyAHNgL4AyADQpCjgoCQCzcD8AMCQCADQYgFaiAEIANB8ANqECxFBEAgA0KOgICAcDcD4AMgAyAHNgLcAyADQdkANgLYAyADQo6fgoCQAjcD0AMgA0GIBWogBCADQdADahAsRQ0BCwJAIAMoApQFIgVBAEgEQCADKALIBSEFDAELIAMgBTYCyAULIAEgAygCtAUgBRA0IANBsAVqIgRBkwFBkwFBkgEgAygCmAUiAkGRAUYbIAJBjwFGGxAQIAQgB0H/AXEQEAwPCyADQo6AgIBwNwLEAyADIAc2AsADIANCkYCAgJALNwO4AyADQoSAgIDQEzcDsAMgA0GIBWogBCADQbADahAsBEACQCADKAKUBSIFQQBIBEAgAygCyAUhBQwBCyADIAU2AsgFCyABIAMoArQFIAUQNAJAIAMoAqgFQS9GBEAgAEEvEBMgA0GwBWpBvwEQEAwBCyADQbAFaiICQQQQECACIAMoAqgFEB4LIANBsAVqIgJBlAEQECACIAdB/wFxEBAMDwsgA0KOgICAcDcCpAMgAyAHNgKgAyADQpGAgICQCzcDmAMgA0KBgICA0BM3A5ADIANBiAVqIAQgA0GQA2oQLARAAkAgAygClAUiBUEASARAIAMoAsgFIQUMAQsgAyAFNgLIBQsgASADKAK0BSAFEDQgA0GwBWoiAiADKAKgBRDHAyACQZQBEBAgAiAHQf8BcRAQDA8LIANCjoCAgHA3A4gDIAMgBzYChAMgA0HZADYCgAMgA0KdgYCAkAI3A/gCIANC2Lb5gnA3A/ACIANBiAVqIAQgA0HwAmoQLARAAkAgAygClAUiBUEASARAIAMoAsgFIQUMAQsgAyAFNgLIBQsgASADKAK0BSAFEDQgA0GwBWoiAiADKAKYBSADKAKcBRBoIAJBlAEQECACIAdB/wFxEBAMDwsgASADKAK0BSADKALIBRA0IANBsAVqQdgAIAcQaCAEIQIMEgsgCS8AASECIAEgAygCtAUgAygCyAUQNCADQbAFaiAFIAIQaCAEIQIMEQsgAyAJLwABIgI2ApQEIANBfzYCmAQgAyAFQQFrNgKQBCADQYgFaiAEIANBkARqECwEQAJAIAMoApQFIgRBAEgEQCADKALIBSEEDAELIAMgBDYCyAULIAEgAygCtAUgBBA0IANBsAVqIAVBAWogAhBoDA0LIAEgAygCtAUgAygCyAUQNCADQbAFaiAFIAIQaCAEIQIMEAsgASALIA4gBCADQcgFahCyAiEEDAYLIAEoAtQCIQ4gASgCzAIhB0EAIQhBACEPA0ACQCAIIA5IBEBBAyEFIAcoAgAiBEHpAGtBA08EQCAEQesBRw0CQQEhBQsCQCABKAKkAiAHKAIMQRRsaigCDCAHKAIIIgtrIgJBgH9IIAIgBUH/AGpKckUEQCAHQQE2AgQgBEHrAUYEQEHqASECIAdB6gE2AgAMAgsgByAEQf8AaiICNgIADAELIARB6wBHIAJBgIACakH//wNLcg0CIAdC64GAgCA3AgBBAiEFQesBIQILIAsgAygCsAVqQQFrIAI6AAAgBygCBCIEIAMoArAFIAtqaiICIAIgBWogAygCtAUgBSALaiAEamsQgQIgAyADKAK0BSAFazYCtAVBACEEIAEoAqwCIgJBACACQQBKGyEKIAEoAqQCIQIDQCAEIApGBEAgASgC1AIhDiAHIQYgCCEEA0ACQCAOIARBAWoiBEwEQEEAIQIgASgC4AIiBEEAIARBAEobIQoDQCACIApGDQIgCyABKALYAiACQQN0aiIGKAIAIgRJBEAgBiAEIAVrNgIACyACQQFqIQIMAAsACyAGIgJBEGohBiACKAIYIgogC0wNASACIAogBWs2AhgMAQsLIA9BAWohDwwDCyALIAIoAgwiBkgEQCACIAYgBWs2AgwLIAJBFGohAiAEQQFqIQQMAAsACwJAIA9FDQAgASgCzAIhAkEAIQUDQCAFIA5ODQEgASgCpAIgAigCDEEUbGooAgwgAigCCCIHayEEAkACQAJAAkAgAigCBEEBaw4EAAEDAgMLIAMoArAFIAdqIARB/wFxEOEEDAILIAMoArAFIAdqIARB//8DcRCGAwwBCyADKAKwBSAHaiAEEF0LIAJBEGohAiAFQQFqIQUgASgC1AIhDgwACwALIAAgASgCzAIQGiABQQA2AswCIAAgASgCpAIQGiABQQA2AqQCQQAhCkEAIQQCQCABLQBuQQJxDQAgASgC2AJFDQAgASgC8AIhBiABKAIAIAFB9AJqIgUQkQIDQCAKIAEoAuACTg0BAkAgASgC2AIgCkEDdGoiAigCBCIHQQBIIAYgB0ZyDQAgAigCACICIARrIghBAEgNAAJAIAcgBmsiBkEBaiIEQQRLIAhBMktyRQRAIAUgBCAIQQVsakEBakH/AXEQEAwBCyAFQQAQECAFIAgQkQUgBSAGQQF0IAZBH3VzEJEFCyACIQQgByEGCyAKQQFqIQoMAAsACyAAIAEoAtgCEBogAUEANgLYAiANEJcBIA0gAykDwAU3AhAgDSADKQO4BTcCCCANIAMpA7AFNwIAIAFBATYCoAJBACANKAIMRQ0SGiAAEMkBDBELIAdBEGohByAIQQFqIQgMAAsAC0HfFkG+4wBBrPcBQesjEAAACyADKAKUBSIEQQBOBEAgAyAENgLIBQsgAygCoAUhBSADKAKQBSEHIAMoApgFQekAayAIRg0BIAEgBUF/EHQaIAchAgwMCyAEIQcMCQsgA0F/NgKEBSADQYgFaiAHIAEgBSADQcwFaiADQYQFahDJAyIGEMgDBEAgASAGQX8QdBogByECDAsLIAMoAswFIghBKGsiBEEHS0EBIAR0QYMBcUVyRQRAIAEgBkF/EHQaIAEgAygCtAUgAygCyAUQNCADQbAFaiAIQf8BcRAQIAEgCyAOIAcgA0HIBWoQsgIhAgwLC0HrACEFDAgLAkAgBUGQAWtBAk8EQCAFQZcBRg0BIAVBtAFHBEAgBUHAAUcNAyADIAkoAAE2AsgFIAQhAgwMCyAJKAABIgJBAEgNAyACIAEoAqwCTg0DIA8gAkEUbGoiCCgCDEF/Rw0EIAggAygCtAU2AgwgCCgCECEGA0AgBiICBEAgCCgCDCACKAIEIgdrIQUgAigCACEGAkACQAJAAkAgAigCCEEBaw4EAgEDAAMLIAMoArAFIAdqIAUQXQwCCyAFQYCAAmpBgIAETw0JIAMoArAFIAdqIAVB//8DcRCGAwwBCyAFQYABakGAAk8NCSADKAKwBSAHaiAFQf8BcRDhBAsgACACEBoMAQsLIAhBADYCECAEIQIMCwsgA0KOgICAcDcD2AQgA0LZuP2CcDcD0AQgA0GIBWogBCADQdAEahAsBEAgAygClAUiAkEATgRAIAMgAjYCyAULIAMgAygCnAUiBjYCxAQgA0F/NgLIBCADIAMoApgFIgRBAWs2AsAEIANBiAVqIAMoApAFIgIgA0HABGoQLARAIAMoApQFIgJBAE4EQCADIAI2AsgFCyAEQQFqIQQgAygCkAUhAgsgASADKAK0BSADKALIBRA0IANBsAVqIgcgBUECa0H/AXEQECAHIAQgBhBoDAsLIANCjoCAgHA3A7gEIANCmICAgLDoDjcDsAQgA0GIBWogBCADQbAEahAsBEACQCADKAKUBSICQQBIBEAgAygCyAUhAgwBCyADIAI2AsgFCyABIAMoArQFIAIQNCADQbAFaiICIAVBAmtB/wFxEBAgAiADLQCYBRAQIAIgAygCqAUQHgwHCyADQo6AgIBwNwOoBCADQpmAgICQCTcDoAQgA0GIBWogBCADQaAEahAsRQ0BAkAgAygClAUiAkEASARAIAMoAsgFIQIMAQsgAyACNgLIBQsgASADKAK0BSACEDQgA0GwBWoiAiAFQQJrQf8BcRAQIAJByQAQEAwGCyADQX82AvgEIANChICAgLCV69SqfzcD8AQgA0GIBWogBCADQfAEahAsRQ0AIAMoApQFIgdBAE4EQCADIAc2AsgFCyADKAKYBSEGIAMoAqgFIgdBxQBGBH9B8gEFIAdBG0cNAUHzAQshByAGQX1xQakBRgRAIAEgAygCtAUgAygCyAUQNCADQbAFaiAHEBAgACADKAKoBRATDAYLIANC6YCAgHA3A+AEIANBiAVqIAMoApAFIANB4ARqECxFDQACQCADKAKUBSIFQQBIBEAgAygCyAUhBQwBCyADIAU2AsgFCyABIAMoArQFIAUQNCADQbAFaiAHEBAgACADKAKoBRATQeoAIQUMBgsgASADKAK0BSADKALIBRA0IANBsAVqIAkgChCKARogBCECDAgLQd8WQb7jAEHj9QFB6yMQAAALQbDyAEG+4wBB5fUBQesjEAAAC0GfxgBBvuMAQfD1AUHrIxAAAAtBisYAQb7jAEH09QFB6yMQAAALIAMoApAFIQIMAwsgAygCoAUhBiADKAKQBSEHCyABIAMoArQFIAMoAsgFEDQgBUHrAEciCkUEQCABIAsgDiAHIANByAVqELICIQcLIAZBAEgNBCAGIAEoAqwCTg0EIAEgASgC1AIiBEEBajYC1AIgASgCzAIgBEEEdGoiEUEENgIEIBEgBTYCACADKAK0BSEIIBEgBjYCDCARIAhBAWo2AggCQCAPIAZBFGxqIgkoAgwiBEF/RgRAIAkoAgggAkF/c2oiAkH/AEogBUHpAGtBAktyRQRAIBFBATYCBCARIAVB/wBqIgQ2AgAgA0GwBWoiAiAEQf8BcRAQIAJBABAQIAchAiAAIAkgAygCtAVBAWtBARDuAg0EDAMLIAJB//8BSiAKcg0BIBFBAjYCBCARQesBNgIAIANBsAVqIgJB6wEQECACQQAQMSAHIQIgACAJIAMoArQFQQJrQQIQ7gINAwwCCyAEIAhBf3NqIgZBgAFqQf8BSyAFQekAa0ECS3JFBEAgEUEBNgIEIBEgBUH/AGoiBDYCACADQbAFaiICIARB/wFxEBAgAiAGQf8BcRAQIAchAgwDCyAGQYCAAmpB//8DSyAKcg0AIBFBAjYCBCARQesBNgIAIANBsAVqIgJB6wEQECACIAZB//8DcRAxIAchAgwCCyADQbAFaiICIAVB/wFxEBAgAiAJKAIMIAMoArQFaxAeIAchAiAJKAIMQX9HDQEgACAJIAMoArQFQQRrQQQQ7gINAQsLIANBsAVqEJcBC0F/CyECIANB0AVqJAAgAgwBC0HfFkG+4wBB5fYBQesjEAAACw0BQQAhCiMAQSBrIgkkACABKAKAAiEPIAkgASgChAIiAjYCCCAJIAAgAkEBdBAvIgc2AhACQCAHRQRAQX8hBAwBC0EAIQQgAkEAIAJBAEobIQIDQCACIARHBEAgByAEQQF0akH//wM7AQAgBEEBaiEEDAELCyAJQQA2AhwgCUIANwIUIAlBADYCDAJ/AkAgACAJQQhqQQBBAEEAENIBDQADQAJAAkACQCAJKAIYIgJBAEoEQCAJIAJBAWsiAjYCGCAPIAkoAhQgAkECdGooAgAiDWoiDi0AACILQQxqQf8BcUENSQRAQfz4ACEGDAQLIA0gC0EPaiALIAtBsQFLGyIFQQJ0IgdBsJoBai0AAGoiCCAJKAIISgRAQZf4ACEGDAQLIAkoAhAgDUEBdGovAQAhBCAHQbGaAWotAAAhBgJAIAVBIWsiAkEQS0EBIAJ0Qb+ABHFFckUEQCAGIA4vAAFqIQYMAQsgBUH7AWtBA0sNACAGIAtqQewBayEGCyAEIAZIBEBB3fgAIQYMBAsCQCAHQbKaAWotAAAgBmsgBGoiBCAJKAIMTA0AIAkgBDYCDCAEQf7/A0wNAEG/+AAhBgwECwJAAkACQAJAAkACQAJAIAtB6QBrDg8CAgECAwsJCQkEBgQFBQUACyALQSNrIgJBDUsNB0EBIAJ0QeXwAHENCgwHCyANIA4oAAFqQQFqIQgMBwsgACAJQQhqIA0gDigAAWpBAWogCyAEENIBDQkMBgsgACAJQQhqIA0gDigAAWpBAWogCyAEQQFqENIBDQgMBQsgACAJQQhqIA0gDigABWpBBWogCyAEQQFqENIBDQcMBAsgACAJQQhqIA0gDigABWpBBWogCyAEQQJqENIBRQ0DDAYLIAAgCUEIaiANIA4oAAVqQQVqIAsgBEEBaxDSAQ0FDAILIAAgCSgCEBAaIAAgCSgCFBAaIAkoAgwhCkEADAULAkACQAJAIAtB6AFrDgQCAgEAAwsgDSAOLgABakEBaiEIDAILIA1BAWoiAiACIA9qLAAAaiEIDAELIAAgCUEIaiANQQFqIgIgAiAPaiwAAGogCyAEENIBDQMLIAAgCUEIaiAIIAsgBBDSAUUNAQwCCwsgCSANNgIEIAkgCzYCACAAIAYgCRBQCyAAIAkoAhAQGiAAIAkoAhQQGkF/CyEEIBQgCjYCDAsgCUEgaiQAIARBAEgNAUHAAEHYACABLQBuQQJxIgIbIgggASgCuAJBA3RqIQUgAAJ/IAIEQCAFIAEoAkRFDQEaCyABKAJ8IAEoAogBakEEdCAFagsiBiABKALAAkEDdGoiAiABKAKEAmoQbCIKRQ0BIApBATYCACAKIAIgCmoiBDYCFCAKIAEoAoQCIgI2AhggBCABKAKAAiACECUaIAAgASgCgAIQGiABQQA2AoACIAogASgCcDYCHCABKAJ8IgcgASgCiAEiBGpBAEwNBiABLQBuQQJxRQ0EIAEoAkQNBEEAIQIDQCACIAdOBEBBACECA0AgASgCiAEgAkwEQEEAIQIDQCACIAEoAsACTg0KIAAgAkEDdCIEIAEoAsgCaigCBBATIAEoAsgCIARqQQA2AgQgAkEBaiECDAALAAUgACABKAKAASACQQR0aigCABATIAJBAWohAgwBCwALAAUgACABKAJ0IAJBBHRqKAIAEBMgAkEBaiECIAEoAnwhBwwBCwALAAtB1fMAQb7jAEGD/gFBizYQAAALBSABKAJ0IAJBBHRqIgQgASgCzAEgBCgCBEEDdGoiBCgCBDYCCCAEIAI2AgQgAkEBaiECDAELCyAAIAEQjQNCgICAgOAADAMLIAogBSAKaiICNgIgIAIgASgCgAEgBEEEdBAlGiAKKAIgIAEoAogBQQR0aiABKAJ0IAEoAnxBBHQQJRoLIAogASgCfDsBKiAKIAEoAogBOwEoIAogASgCjAE7ASwgACABKAKAARAaIAAgASgCdBAaCyAKIAEoArgCIgQ2AjggBARAIAogCCAKaiICNgI0IAIgASgCtAIgBEEDdBAlGgsgACABKAK0AhAaIAFBADYCtAIgCiAUKAIMOwEuAkAgAS0AbkECcQRAIAAgASgC7AIQEyABQfQCahCXAQwBCyAKIAovABFBgAhyOwARIAogASgC7AI2AkAgCiABKALwAjYCRCAKIAAgASgC9AIgASgC+AIQmgIiAjYCUCACRQRAIAogASgC9AI2AlALIAogASgC+AI2AkwgCiABKAKMAzYCVCAKIAEoApADNgJICyABKALMASICIAFB0AFqRwRAIAAgAhAaCyAKIAEoAsACIgQ2AjwgBARAIAogBiAKaiICNgIkIAIgASgCyAIgBEEDdBAlGgsgACABKALIAhAaIAFBADYCyAIgCiAKLwARQX5xIAEvATRBAXFyIgI7ABEgCiABLwE4QQF0QQJxIAJBfXFyIgI7ABEgCiABLQBuOgAQIAogAS8BYEECdEEEcSACQXtxciICOwARIAogAkFPcSABLwFsQQR0QTBxciICOwARIAogASgCtAFBAEgEfyABKAK4AUEAR0EDdAVBCAsgAkF3cXIiAjsAESAKIAEvAVBBBnRBwABxIAJBv39xciICOwARIAogAkH/fnEgAS8BVEEHdEGAAXFyIgI7ABEgCiACQf99cSABLwFYQQh0QYACcXIiAjsAESAKIAJB/3txIAEvAVxBCXRBgARxciICOwARIAogAkH/7wNxIAEvAWhBC3RBgBBxcjsAESAKIAAQoAIiADYCMCAAKAIQIApBARC+ASABKAIEBEAgAUEYahBGCyAAIAEQGiAKrUKAgICAYIQLIRUgFEEQaiQAIBUL7wkDAXwLfwF+IwBB0AJrIgIkAEKAgICA4AAhEQJAIAAgASACQcABaiAEQQR2IgNBAXFBABDdAyIGQQBIDQAgA0EPcSENIAZFBEAgDUECRgRAIABByukAEGsMAgsgAEHCygAQdiERDAELAn8gAisDgAIiBZlEAAAAAAAA4EFjBEAgBaoMAQtBgICAgHgLIQ4CfyACKwP4ASIFmUQAAAAAAADgQWMEQCAFqgwBC0GAgICAeAshDwJ/IAIrA/ABIgWZRAAAAAAAAOBBYwRAIAWqDAELQYCAgIB4CyEQAn8gAisD6AEiBZlEAAAAAAAA4EFjBEAgBaoMAQtBgICAgHgLIQkCfyACKwPgASIFmUQAAAAAAADgQWMEQCAFqgwBC0GAgICAeAshCgJ/IAIrA9gBIgWZRAAAAAAAAOBBYwRAIAWqDAELQYCAgIB4CyEHAn8gAisD0AEiBZlEAAAAAAAA4EFjBEAgBaoMAQtBgICAgHgLIQsCfyACKwPIASIFmUQAAAAAAADgQWMEQCAFqgwBC0GAgICAeAshDCAEQQFxIQgCfyACKwPAASIFmUQAAAAAAADgQWMEQCAFqgwBC0GAgICAeAshBkEAIQMCQCAIRQ0AIARBD3EhCAJAAkACQAJAIA0OBAABAgMECyACIAY2AmAgAiALNgJUIAIgBkEfdkEEcjYCXCACIAxBA2xBsLMBajYCWCACIA9BA2xBkLMBajYCUCACQZACakHAAEH3/gAgAkHQAGoQVyEDDAMLIAIgBjYCgAEgAiALNgJ4IAIgBkEfdkEEcjYCfCACIAxBA2xBsLMBajYCdCACIA9BA2xBkLMBajYCcCACQZACakHAAEHt4gAgAkHwAGoQVyEDIAhBA0cNAiACQZACaiADakEgOgAAIANBAWohAwwCCyACIAY2AqABIAJBkAJqIghBwABB0OIAQcriACAGQZDOAEkbIAJBoAFqEFchAyACIAs2ApQBIAIgDEEBajYCkAEgAyAIakHAACADa0H75wAgAkGQAWoQVyADaiEDDAELIAIgCzYCtAEgAiAMQQFqNgKwASACIAY2ArwBIAIgBkEfdkEEcjYCuAEgAkGQAmpBwABB3uIAIAJBsAFqEFchAyAIQQNHDQAgAkGQAmogA2pBrMAAOwAAIANBAmohAwsCQCAEQQJxRQ0AAkACQAJAAkAgDQ4EAAECAwQLIAIgCTYCCCACIAo2AgQgAiAHNgIAIAJBkAJqIANqQcAAIANrQb3oACACEFcgA2ohAwwDCyACIAk2AiggAiAKNgIkIAIgBzYCICACQZACaiIHIANqQcAAIANrQb3oACACQSBqEFcgA2oiAyAHakEtQSsgDkEASBs6AAAgAiAOIA5BH3UiBHMgBGsiBEE8biIGNgIQIAIgBCAGQTxsazYCFCAHIANBAWoiBGpBPyADa0HV4gAgAkEQahBXIARqIQMMAgsgAiAQNgI8IAIgCTYCOCACIAo2AjQgAiAHNgIwIAJBkAJqIANqQcAAIANrQYbnACACQTBqEFcgA2ohAwwBCyACIAk2AkggAiAKNgJEIAJBwQBB0AAgB0EMSBs2AkwgAiAHQQFqQQxvQQFrNgJAIAJBkAJqIANqQcAAIANrQe/pACACQUBrEFcgA2ohAwsgACACQZACaiADEP4BIRELIAJB0AJqJAAgEQtZAQF8IAAgAykDABCmASICRQRAQoCAgIDgAA8LIAIQCCEEIAAgAhA3IAS9An8gBJlEAAAAAAAA4EFjBEAgBKoMAQtBgICAgHgLIgC3vVEEQCAArQ8LIAQQFws6AgJ/AX4jAEEQayIAJAAgAEEIahCwBCAANAIIIQIgACgCDCEBIABBEGokACABQegHbawgAkLoB358C7cBAgR/AX4gAEEIEC8iBEUEQEF/DwsgBEIBNwIAA0ACQAJAIANBAkYNACAAIAApAzAgA0ErahBTIgcQDUUEQCAAQRAQLyIFDQIgACAHEAwLQX8hBiADRQ0AIAAgASkDABAMCyAAKAIQIAQQhQUgBg8LIAQgBCgCAEEBajYCACAFIAQ2AgggBSACEA83AwAgByAFEI0BIAAgB0EvQQEQqQMgASADQQN0aiAHNwMAIANBAWohAwwACwALdAEDfyABQcgAaiEDIAEoAkwhAgNAIAIgA0ZFBEAgAigCBCEEIAAgAikDEBAnIAAgAikDGBAnIAAgAikDIBAnIAAgAikDKBAnIAAgAhAhIAQhAgwBCwsgASgCBEF+cUEERwRAIAAgAUEIahCLAwsgACABECELPQEBfyABIAEoAgBBAWsiAjYCACACRQRAIAAgARDhAyAAIAEpAxAQJyAAIAEpAxgQJyABEJ8CIAAgARAhCwvBAwIEfwJ+IwBBMGsiAiQAAkACQCAAIAFBKGoQwgIiBhANDQAgAiABKAJkQQhrIgMpAwA3AyAgA0KAgICAMDcDACAGEBIEQCAAIAAgASkDEEKAgICAMEEBIAJBIGoQJBAMIAAgAikDIBAMIAAoAhAgARDhAwwCCyAAIAYQDCAAIAApA1BBASACQSBqQQAQjAIhBiAAIAIpAyAQDCAGEA0NAAJ/IAJBEGohBEEAIQMDQAJAAkAgA0ECRg0AIAQgA0EDdGogACAAKQMwIANBLmoQUyIHNwMAIAcQDUUNAUF/IQUgA0EBRw0AIAAgBCkDABAMCyAFDAILIAEgASgCAEEBajYCACAHpyABNgIgIANBAWohAwwACwALBEAgACAGEAwMAQsgAkKAgICAMDcDCCACQoCAgIAwNwMAIAAgBiACQRBqIAIQuwIhBCAAIAYQDEEAIQMDQCADQQJHBEAgACACQRBqIANBA3RqKQMAEAwgA0EBaiEDDAELCyAERQ0BCyACIAAQkwE3AyggACABKQMYQoCAgIAwQQEgAkEoahAkIQYgACACKQMoEAwgACgCECABEOEDIAAgBhAMCyACQTBqJAALwAICBX8BfiMAQTBrIgUkAAJAIAFBKhBAIgZFDQAgBigCAA0AIAAgBkEYaiACEA8iAhAfIAYgA0EBaiIENgIAAkAgBEECRw0AIAYoAhQNACAAKAIQIgQoApgBIgdFDQAgACABIAJBACAEKAKcASAHETMACyADQQBHrUKAgICAEIQhASAGIANBA3RqIgRBBGohCCAEKAIIIQQDQCAEIAhGRQRAIAQoAgQhByAFIAQpAwg3AwAgBSAEKQMQNwMIIAQpAxghCSAFIAI3AyAgBSABNwMYIAUgCTcDECAAQS1BBSAFEIMDIAQQRiAAKAIQIAQQvAIgByEEDAELCyAGQQEgA2tBA3RqIgNBBGohByADKAIIIQQDQCAEIAdGDQEgBCgCBCEDIAQQRiAAKAIQIAQQvAIgAyEEDAALAAsgBUEwaiQAC8ECAgN+An8jAEEQayICJABCgICAgDAhBQJAAkAgACACQQhqIAAgARArIgEQQQ0AAkAgAikDCCIHQgBXBEAMAQsgB0IBfSEGAkACQAJAAkAgASACQQRqIAIQjgJFDQAgByACKAIAIgitUg0AIAGnIQkgAigCBCEDIARFDQEgAykDACEFIAMgA0EIaiAIQQN0QQhrEIECDAILAkAgBARAIAAgAUIAEGQiBRANDQYgACABQgBCASAGQQEQggNFDQEMBgsgACABIAYQZCIFEA0NBQsgACABIAYQlAJBAE4NAgwECyAIQQN0IANqQQhrKQMAIQULIAkgCSgCKEEBazYCKAsgB0KBgICACFQNACAGuRAXIQYLIAAgAUEwIAYQSEEATg0BCyAAIAUQDEKAgICA4AAhBQsgACABEAwgAkEQaiQAIAULEAAgACADKQMAQREgBBCBAwuuAgIFfgF/IwBBEGsiCiQAAn4CQCAAIApBCGogACABECsiBRBBDQAgCikDCCIBIAKsIgh8IgZCgICAgICAgBBZBEAgAEGqwwBBABAWDAELAkAgBEUgAkEATHJFBEAgACAFIAhCACABQX8QggMNAgwBCyABIQcLIAJBACACQQBKG60hCEIAIQEDQCABIAhSBEAgASAHfCEJIAGnIQIgAUIBfCEBIAAgBSAJIAMgAkEDdGopAwAQDxCRAUEATg0BDAILCyAAIAVBMCAGQoCAgIAIfCIHQv////8PWAR+IAZC/////w+DBSAGuRAXCxBIQQBIDQAgACAFEAwgBkL/////D4MgB0L/////D1gNARogBrkQFwwBCyAAIAUQDEKAgICA4AALIQEgCkEQaiQAIAELPAAgAUEAQdAAEEsiASAENgIMIAEgADYCACABIAIgA2o2AjwgASACNgI4IAFBATYCCCABQqCAgIAQNwMQC38BBH8gAS0AAEHbAEYEQCABQQFqIgMQQ0EBayECIAAoAhAoAjghBEHCASEBA0AgAUHPAUcEQAJAIAQgAUECdGooAgAiBSgCBEH/////B3EgAkcNACAFQRBqIAMgAhB3DQAgACABEBkPCyABQQFqIQEMAQsLEAEACyAAIAEQygELFwAgACAAKQPAASABIAIgA0EAQX8QtwULNQEBfyAAKALsASIHRQRAIABB2d0AQQAQFkKAgICA4AAPCyAAIAEgAiADIAQgBSAGIAcRNQALxgICAn4Cf0KAgICAMCECAkACQCABKQJUIgNCGIZCOIenDQAgA0IghkI4h6cEQCADQhCGQjiHp0UNASAAIAEpA2AQDxCUAUKAgICA4AAPCyABIANC/////49gg0KAgICAEIQ3AlQDQCABKAIUIARKBEAgASgCECAEQQN0aigCBCIFKQJUQhiGQjiHp0UEQCAAIAUQuAUiAhANDQQgACACEAwLIARBAWohBAwBCwsCQCABKAJQIgQEQEKAgICA4ABCgICAgDAgACABIAQRAgBBAEgbIQIMAQsgACABKQNIQoCAgIAwQQBBABA2IQIgAUKAgICAMDcDSAsgAhANBEAgAUEBOgBZIAEgACgCECkDgAEQDzcDYAsgASABKQJUQv///4eAYINCgICACIQ3AlQLIAIPCyABIAEpAlRC/////49ggzcCVCACC8AFAgd/AX4jAEEQayIFJAACQCABKQJUIglCKIZCOIenDQAgASAJQv//g3iDQoCABIQ3AlQDQAJAIAEoAhQgA0wEQEEAIQMDQCABKAIgIANKBEACQCABKAIcIgQgA0EUbGoiAigCCEEBRw0AIAIoAgwiB0H9AEYNACAAIAVBCGogBUEMaiABKAIQIAIoAgBBA3RqKAIEIAcQ7AMiAkUNACAAIAIgASAEIANBFGxqKAIQEOsDDAQLIANBAWohAwwBCwtBACECIAEoAlANAyABKAJIKAIkIQhBACEDQQAhBANAAkAgASgCOCAETARAA0AgAyABKAIgTg0CIAEoAhwgA0EUbGoiAigCCEUEQCAIIAIoAgBBAnRqKAIAIgQgBCgCAEEBajYCACACIAQ2AgQLIANBAWohAwwACwALIAEoAhAgASgCNCAEQQxsaiIHKAIIQQN0aigCBCECAkACQCAHKAIEIgZB/QBGBEAgACACEIkDIgkQDUUNAQwGCyAAIAVBCGogBUEMaiACIAYQ7AMiBgRAIAAgBiACIAcoAgQQ6wMMBgsCQCAFKAIMIgYoAgxB/QBGBEAgACAFKAIIKAIQIAYoAgBBA3RqKAIEEIkDIgkQDQ0HIABBARDmAyICRQRAIAAgCRAMDAgLIAAgAkEYaiAJEB8MAQsgBigCBCICRQRAIAUoAggoAkgoAiQgBigCAEECdGooAgAhAgsgAiACKAIAQQFqNgIACyAIIAcoAgBBAnRqIAI2AgAMAQsgACAIIAcoAgBBAnRqKAIAQRhqIAkQHwsgBEEBaiEEDAELC0F/IQIgACABKQNIQoGAgIAQQQBBABAkIgkQDQ0DIAAgCRAMQQAhAgwDCyADQQN0IQRBfyECIANBAWohAyAAIAQgASgCEGooAgQQuQVBAE4NAQwCCwtBfyECCyAFQRBqJAAgAgv/AgIGfwJ+AkAgASkCVEIwhkI4h6cNAAJAIAEoAlAEQANAIAIgASgCIE4NAiABKAIcIAJBFGxqIgMoAghFBEAgAEEAEOYDIgRFBEBBfw8LIAMgBDYCBAsgAkEBaiECDAALAAtBfyEEIAEpA0ghCEF/IQcgACAAKQMwQQ0QUyIJEA1FBEAgCaciAyAIpyICNgIgIAIgAigCAEEBajYCACADQgA3AiQCQAJAAkAgAigCPCIFRQ0AIAAgBUECdBBsIgVFDQEgAyAFNgIkQQAhAwNAIAMgAigCPE4NASACKAIkIANBA3RqLQAAIgZBAXEEQCAAIAZBA3ZBAXEQ5gMiBkUNAyAFIANBAnRqIAY2AgALIANBAWohAwwACwALIAEgCTcDSEEAIQcMAQsgCSEICyAAIAgQDAsgBw0BCyABQQE6AFVBACECA0AgASgCFCACTARAQQAPCyACQQN0IQNBfyEEIAJBAWohAiAAIAMgASgCEGooAgQQugVBAE4NAAsLIAQLiwEAAkACQAJAAkACQCABQiCIp0EDag4CAQACCyAAIAAgASADIAQQjQQgAkEAQQAQNg8LIAAgARAMAkAgACABpyIDELoFQQBIDQAgACADELkFQQBIDQAgACADELgFIgEQDUUNAwsgAEECEKYEDAELIAAgARAMIABBu94AQQAQFgtCgICAgOAAIQELIAELQAECfyAAQeQBaiECIABB4AFqIQMDQCADIAIoAgAiAEYEQEEADwsgAEEEaiECIABBBGsoAgAgAUcNAAsgAEEIawuoAwEEfyMAQRBrIgUkAAJ/IAAoAhAiBigCqAEiA0UEQAJ/IAItAABBLkcEQCAAIAIgAhBDEKMDDAELIAEQ/wUhAyAAIAIQQyADIAFrQQAgAxsiA2pBAmoQLyIEBH8gAyAEIAEgAxAlIgFqQQA6AAACQANAAkAgAi0AAEEuRw0AQQIhAwJAAkAgAi0AAUEuaw4CAAECCyACLQACQS9HDQEgAS0AAEUNAyABEP8FIgNBAWogASADGyIDQZL2ABCsBEUNASADQZH2ABCsBEUNASADIAEgA0lrQQA6AABBAyEDCyACIANqIQIMAQsLIAEtAABFDQAgARBDIAFqQS87AAALIAEQQyABaiACEIEGIAEFQQALCwwBCyAAIAEgAiAGKAKwASADEQoACyEDQQAhAgJAIANFDQACQCAAIAMQygEiBEUNACAAIAQQvAUiAQRAIAAgAxAaIAAgBBATIAEhAgwCCyAAIAQQEyAGKAKsASIBRQRAIAUgAzYCACAAQYb8ACAFENICDAELIAAgAyAGKAKwASABEQEAIQILIAAgAxAaCyAFQRBqJAAgAgtvAgN/AX4CQCAAKAIQKAKMASICRQ0AA0AgAUEASgRAIAFBAWshASACKAIAIgINAQwCCwsgAikDCCIEQoCAgIBwVA0AIASnIgEvAQYQ+AFFDQAgASgCICIBLQASQQRxRQ0AIAAgASgCQBAZIQMLIAMLUgEEfyAAKAIgIgJBACACQQBKGyEEQQAhAgNAAkAgAiAERwR/IAAoAhwiBSACQRRsaigCECABRw0BIAUgAkEUbGoFQQALDwsgAkEBaiECDAALAAvZAQEHf0F/IQIgASABQQFrcUUEQCAAIAFBAnQQnAIiBQR/IAFB/////wNqQf////8DcSEHIAAoAjQhBgNAIAMgACgCJE9FBEAgBiADQQJ0aigCACECA0AgAgRAIAAoAjggAkECdGooAgAiBCgCDCEIIAQgBSAHIAQoAghxQQJ0aiIEKAIANgIMIAQgAjYCACAIIQIMAQsLIANBAWohAwwBCwsgACAGECEgACABQQF0NgIwIAAgATYCJCAAIAU2AjRBAAVBfwsPC0Gq9QBBvuMAQYAUQarCABAAAAuCAQIEfwF+IAFBGGohBCABKAIcIQIDQCACIARGRQRAIAIoAgQhBSABQRBBFCACQQNrIgMtAABBAnEbaigCACACQQJrLwEAQQN0aikDABAPIQYgAiACQRBqNgIIIAIgBjcDECADIAMtAABBAXI6AAAgACACQQhrQQMQvgEgBSECDAELCwsrAQF/IAFBEGsiAyAAIAMpAwAgAUEIaykDABCYBSACR61CgICAgBCENwMAC5kEAgV/An4jAEEQayIFJAAgAUEIayIHKQMAIQggAUEQayIGKQMAIQkCfwJAAkACQAJAAkADQCAIEFYhAQJAA0BBASABRSAJEFYiBEEHRnEgASAERnIgBEUgAUEHRnEbBEAgACAJIAgQmAUhAwwGC0EBIQMgBEECRiABQQNGcSABQQJGIARBA0Zxcg0FAkACQAJAAkACQAJAAkACQCAEQXlGBEAgASIDQQFqDgkKAQUNDQ0NDQENCyABQXlHDQFBeSEDIARBAWoOCQYAAgwMDAwMAAwLIAAgBUEIaiAJEFsNDSAAIAUgCBBbDQ4gBSsDCCAFKwMAYSEDDAwLIARBAUcNAQsgCUL/////D4MhCQwFCyABQQFHDQELIAhC/////w+DIQgMBQsgBEF/Rw0BIAFBCGoiA0EPS0EBIAN0QYGCAnFFcg0FCyAAIAlBAhDDASIJEA1FDQEMBwsLIAEiA0F/Rw0DQX8hAyAEQQhqIgFBD0tBASABdEGDggJxRXINAwsgACAIQQIQwwEiCBANRQ0ACyAAIAkQDAwECyABIQMLAn8gCRCXBQRAQQEgA0F+cUECRg0BGgsgBEF+cUECRiAIEJcFQQBHcQshAyAAIAkQDCAAIAgQDAsgBiACIANHrUKAgICAEIQ3AwBBAAwCCyAAIAgQDAsgBkKAgICAMDcDACAHQoCAgIAwNwMAQX8LIQEgBUEQaiQAIAEL2wIBBX8jAEEQayIDJAAgACAAKQOAARAnIABBoAFqIQQgACgCpAEhAQNAIAEgBEZFBEAgASgCBCEFQQAhAgNAIAIgASgCEE5FBEAgACABIAJBA3RqKQMYECcgAkEBaiECDAELCyAAIAEQISAFIQEMAQsLIAQQcSAAEJwFIABB0ABqEOcDBEBBACECA0ACQCAAKAJEIQEgAiAAKAJATg0AIAEgAkEYbGoiASgCAARAIAAgASgCBBD0AQsgAkEBaiECDAELCyAAIAEQIUEAIQIDQAJAIAAoAjghASACIAAoAixODQAgASACQQJ0aigCACIBEOMDRQRAIAAgARAhCyACQQFqIQIMAQsLIAAgARAhIAAgACgCNBAhIAAgACgC1AEQISADIAApAhg3AwggAyAAKQIQNwMAIAMgACAAKAIEEQMAIANBEGokAA8LQan2AEG+4wBBvw9Bic0AEAAAC8wCAwJ+A38BfCMAQRBrIgQkACABQQhrIgYpAwAhAgJ/AkACQAJAAkAgAUEQayIFKQMAIgNCIIinIgFBACABQQdrQW1LG0UEQCACQiCIpyIBRSABQQdrQW5Jcg0BCyAAIANBAhDDASIDEA0NAiAAIAJBAhDDASICEA0EQCADIQIMAwsgA0KAgICAcINCgICAgJB/UiACQoCAgIBwg0KAgICAkH9ScQ0AIAUgACADIAIQyQIiAzcDACADEA0NAwwBCyAAIARBCGogAxBbDQEgACAEIAIQWw0CIAUCfiAEKwMIIAQrAwCgIge9An8gB5lEAAAAAAAA4EFjBEAgB6oMAQtBgICAgHgLIgC3vVEEQCAArQwBCyAHEBcLNwMAC0EADAILIAAgAhAMCyAFQoCAgIAwNwMAIAZCgICAgDA3AwBBfwshACAEQRBqJAAgAAuDAwEJfyMAQTBrIgckAAJAIAJCgICAgHBUDQBBEyEFAkAgAqciCi0ABUEEcUUNACAAKAIQKAJEIAovAQZBGGxqKAIUIghFDQBBA0ETIAgoAgQbIQULQX8hCSAAIAdBLGogB0EoaiAKIAUQkgENACADp0EAIANC/////29WGyEMIAVBEHEhDSAHKAIsIQggBygCKCELQQAhBQJAA0AgBSALRwRAAkACQCAMRQ0AIABBACAMIAggBUEDdGooAgQQTyIGRQ0AIAZBAE4NAQwECyANRQRAIAAgB0EIaiAKIAggBUEDdGooAgQQTyIGQQBIDQQgBkUNASAHKAIIIQYgACAHQQhqEE4gBkEEcUUNAQsgACACIAggBUEDdGoiBigCBCACQQAQFCIDEA0NAyAGKAIEIQYCfyAEBEAgACABIAYgAxBIDAELIAAgASAGIANBBxAbC0EASA0DCyAFQQFqIQUMAQsLIAAgCCALEGZBACEJDAELIAAgCCALEGYLIAdBMGokACAJC1MBAn8CQAJAIAEQIkUNACABEJ0FDQBBfyEDIAAgAhA4IgRFDQEgACAEEJsFIQIgACAEEBMgAhANDQEgACABQTYgAkEBEBtBAEgNAQtBACEDCyADCzIAAkAgAkUNACABECJFDQAgARCdBQ0AIAAgAUE2IAAgAhAyQQEQG0EATg0AQX8PC0EAC2gBAX8gACgCECECAkAgARBeRQRAIAIoAiwgAU0NASACKAI4IAFBAnRqKAIAIgGtQoCAgICQf4QQDxogACABQQQQ9QMPC0GO9wBBvuMAQc4XQYs8EAAAC0GXyABBvuMAQc8XQYs8EAAAC9YBAQR/IAAoAsgBIgYoAhAiBCAEKAIYIAFxQX9zQQJ0aigCACEFIAQQKiEEAkADQCAFRQ0BIAEgBCAFQQFrIgdBA3RqIgUoAgRHBEAgBSgCAEH///8fcSEFDAELCyAGKAIUIAdBA3RqIQQCQCADQQFGDQAgBCkDABCGAQRAIAAgAhAMIAAgBSgCBBDiAUF/DwsgBS0AA0EIcQ0AIAAgAhAMIABBgIABIAEQ4AEPCyAAIAQgAhAfQQAPCyAAIAApA8ABIAEgAkGAgAZBgIACIAAQ+wEbEJcCC30BAX8CQCACQoCAgIBwg0KAgICAkH9RIANCgICAgHCDQoCAgICQf1FxRQRAIABB1t4AQQAQFgwBCyAAIAFBEhBvIgEQDQ0AIAGnIgQgAj4CICAEIAM+AiQgACABQdUAQgBBAhAbGiABDwsgACADEAwgACACEAxCgICAgOAACw0AIAAgAUHq+gAQlQML0gEDAX4BfAF/A0ACQEF/IQUCQAJAAkAgAhBWDggAAAAAAgIDAQILIAJCIIZCIIchA0EAIQUMAgtBACEFIAIQSSIEvUL///////////8Ag0KAgICAgICA+P8AVg0BQoCAgICAgICAgH8hAyAERAAAAAAAAODDYw0BQv///////////wAhAyAERAAAAAAAAOBDZA0BIASZRAAAAAAAAOBDYwRAIASwIQMMAgtCgICAgICAgICAfyEDDAELIAAgAhCgASICEA1FDQELCyABIAM3AwAgBQu8AQICfwF8A0ACQEF/IQQCQAJAAkAgAhBWDggAAAAAAgIDAQILIAKnIQNBACEEDAILQQAhBCACEEkiBb1C////////////AINCgICAgICAgPj/AFYNAUGAgICAeCEDIAVEAAAAAAAA4MFjDQFB/////wchAyAFRAAAwP///99BZA0BIAWZRAAAAAAAAOBBYwRAIAWqIQMMAgtBgICAgHghAwwBCyAAIAIQoAEiAhANRQ0BCwsgASADNgIAIAQLbQACQAJAAkACQAJAIAJBBHZBA3FBAWsOAwABAgMLIAEoAgAiAgRAIAAgAq1CgICAgHCEECcLIAEoAgQiAUUNAyAAIAGtQoCAgIBwhBAnDwsgACABKAIAEPoBDwsgARDYBQ8LIAAgASkDABAnCwsLACAAIAEQDxCgAQuZAwEGfyADIAEoAgAiBSgCHEEDbEECbRBKIQYCQCACBEAgACACKAIUIAZBA3QQmgIiA0UNASACIAM2AhQLIAUoAhhBAWoiBCEDA0AgAyICQQF0IQMgAiAGSQ0ACwJAIAIgBEcEQCAAIAIgBhDlARAvIgNFDQIgAyACEL8CIQcgBUEIahBGIAcgBSAFKAIgQQN0QTBqECUiBEEIaiAAKAIQQdAAahBMIAQgAkEBayIJNgIYQQAhAyAEIAJBAnQiAmtBACACEEsaIARBMGohAgNAIAMgBCgCIE9FBEACQCACKAIEIghFBEAgA0EBaiEDDAELIAIgAigCAEGAgIBgcSAEIAggCXFBf3NBAnRqIggoAgBB////H3FyNgIAIAggA0EBaiIDNgIACyACQQhqIQIMAQsLIAAgBRDBAhAaDAELIAVBCGoiAhBGIAAgBRDBAiAEIAYQ5QEQmgIiA0UEQCACIAAoAhBB0ABqEEwMAgsgAyAEEL8CIgdBCGogACgCEEHQAGoQTAsgASAHNgIAIAcgBjYCHEEADwtBfwugAQEDfwJAIAAgASgCGEEBaiICIAEoAhwQ5QEiAxAvIgRFBEBBACECDAELIAQgARDBAiADECUgAhC/AiICQQE2AgAgACgCECACQQIQvgFBACEBIAJBADoAECACKAIsIgMEQCADrUKAgICAcIQQDxoLIAIQKiEDA0AgASACKAIgTw0BIAAgAygCBBAZGiADQQhqIQMgAUEBaiEBDAALAAsgAgtfAgF/AXwjAEEQayICJAACf0EAIAEQkAFFDQAaQX8gACACQQhqIAEQRw0AGiACKwMIIgO9QoCAgICAgID4/wCDQoCAgICAgID4/wBSIAOcIANhcQshACACQRBqJAAgAAu7AQEBfCABAn8CfwNAAkACQAJAIAIQVg4IAAAAAAICAgECC0EAIQBBAEH/ASACpxC0ARBKDAQLQQAiACACEEkiA71C////////////AINCgICAgICAgPj/AFYgA0QAAAAAAAAAAGNyDQIaQf8BIANEAAAAAADgb0BkDQMaAn8gA54iA5lEAAAAAAAA4EFjBEAgA6oMAQtBgICAgHgLDAMLIAAgAhCgASICEA1FDQALQX8LIQBBAAs2AgAgAAvBBAEIfyMAQRBrIgYkAAJ/QX8gACAGQQxqIAJBABDOAg0AGiABKAIQLQAzQQhxRQRAIAAgA0EwEOABDAELIAEtAAVBCHEEQCAGKAIMIgMgASgCKCIFSQRAIAMhBANAIAQgBUZFBEAgACABKAIkIARBA3RqKQMAEAwgBEEBaiEEDAELCyABIAM2AigLIANBAE4EfiADrQUgA7gQFwshAiABKAIUIAI3AwBBAQwBCyAAIAZBBGogASgCFCkDABDHARoCQCAGKAIEIgcgBigCDCIJSwRAIAEoAhAiCygCICIEIAcgCWtPBEADQCAJIAciBUkEQCAAIAEgACAFQQFrIgcQ5gUiChCUBCEEIAAgChATIAQNAQsLIAYgBTYCBAwCCyAJIQUgCxAqIgchCANAIAQgCkwEQCAGIAU2AgRBACEIA0AgBCAITA0EAkAgBygCBCIERQ0AIAAgBkEIaiAEELYBRQ0AIAYoAgggBUkNACAAIAEgBygCBBCUBBogASgCECILECogCEEDdGohBwsgB0EIaiEHIAhBAWohCCALKAIgIQQMAAsABQJAIAgoAgQiBEUNACAAIAZBCGogBBC2AUUNACAGKAIIIgQgBUkNACAFIARBAWogCC0AA0EEcRshBQsgCEEIaiEIIApBAWohCiALKAIgIQQMAQsACwALIAYgCTYCBCAJIQULIAAgASgCFCAFQQBOBH4gBa0FIAW4EBcLEB9BASAFIAlNDQAaIAAgA0HS0QAQeQshBCAGQRBqJAAgBAupBAEJfyMAQRBrIgIkACACQQA2AgwgAkIANwMAIAJBfzYCCAJAIAJB4AFBlIgBKAIAEQIAIgQEQCAEQQBB4AEQSyIAQZyIASkCADcCCCAAQZSIASkCADcCACAAKAIMRQRAIABBATYCDAsgACACKQMANwMQIAAgAikDCDcDGCAAQYCAEDYCbCAAQcgAahBxIABB0ABqEHEgAEHYAGoQcSAAQQA6AGggAEGgAWoQcSAAQQA2AjQgAEIANwIkIABBADYCPCAAQQA2AixBfyEGAkAgAEGAAhDABQ0AQZCLASEBQQEhAwNAIANBzwFGBEBBACEGDAILQQRBA0EBIANBwQFLGyADQcEBRhshCCAAIAEQQyIFQQAQ4QUiBwR/IAdBEGogASAFECUgBWpBADoAACAAIAcgCBDXAgVBAAtFDQEgA0EBaiEDIAEgBWpBAWohAQwACwALAkAgBg0AIABB4IMBQQFBKBCQBEEASA0AIAAoAkQiAUECNgL4AiABQQM2ArACIAFB+IcBNgKcAiABQdyHATYCjAEgAUHAhwE2AtQBIAFBBDYCkAMgAUEFNgLgAiAAQQA2AtABIABChICAgIACNwPIASAAIABBwAAQnAIiATYC1AFBAEF/IAEbDQAgAEGAgBA2AnAgAEEANgJ0IAAgACgCcCIBBH8gACgCdCABawVBAAs2AnggAEKAgICAIDcDgAEMAgsgABDEBQtBACEECyACQRBqJAAgBAuoAwIEfwJ+IAAoAhAhAiABEF4EQCABEHytDwsCQCABIAIoAixJBEACQCACKAI4IAFBAnRqKAIAIgUpAgQiBkKAgICAgICAgECDQoCAgICAgICAwABSDQAgBUEQaiEBIAanQf////8HcSEEAkACQAJAIAZCgICAgAiDUEUEQCAERQ0EIAEhAgJAIAEvAQAiA0EtRw0AIAFBAmohAiABLwECIgNBMEcNACAEQQJGDQILIAMQRQ0DIANByQBHIAEgBEEBdGogAmtBEEdyDQQgAkECakHIogFBDhB3RQ0DDAQLIARFDQMgASECIAEtAAAiA0EtRw0BIAFBAWohAiABLQABIgNBMEcgBEECR3INAQtEAAAAAAAAAIAQFw8LIAMQRQ0AIANByQBHIAEgBGogAmtBCEdyDQEgAkEBakHSC0EHEHcNAQsgACAFrUKAgICAkH+EENAFIgYQDQ0CIAAgBhAuIgcQDQRAIAAgBhAMIAcPCyAFIAenEJUCIQEgACAHEAwgAUUNAiAAIAYQDAtCgICAgDAPC0GtyABBvuMAQdkYQYryABAAAAsgBgsKACAAEJsEEK4DC/gBAQN/AkAgACACEDtFDQAgAqciBC8BBkEORgRAIAAgASAEKAIgKQMAENoFDwsgAUKAgICAcFQNAAJAIAAgAkE7IAJBABAUIgJC/////29YBEBBfyEDIAIQDQ0BIABBuhxBABAWDAELIAGnIQMgAqchBQJAA0ACQCADKAIQKAIsIgRFBEAgAy8BBkEpRw0DIAOtQoCAgIBwhBAPIQEDQEF/IQMgACABEJkCIgEQDQ0FIAEQKA0EIAGnIAVGBEAgACABEAwMAwsgABCCAUUNAAsgACABEAwMBAsgBCIDIAVHDQELC0EBIQMMAQtBACEDCyAAIAIQDAsgAwuHAQIBfwF+IwBBEGsiAyQAIAMgATcDCAJ/AkAgAhAiBEBBfyAAIAJBywEgAkEAEBQiBBANDQIaAkAgBBAoDQAgBBASDQAgACAAIAQgAkEBIANBCGoQNhAtDAMLIAAgAhA7DQELIABBx9sAQQAQFkF/DAELIAAgASACENkFCyEAIANBEGokACAAC3QCAX4BfyMAQYACayIGJAAgBkGAAiACIAMQ2QIaAkAgACAAIAFBA3RqKQNYQQMQUyIFEA0EQEKAgICAICEFDAELIAAgBUEzIAAgBhB2QQMQGxoLIAQEQCAAIAVBAEEAQQAQxwILIAAgBRCUASAGQYACaiQAC58DAgR/AX4jAEEQayIGJAACQAJAAkACQCACEF4EQCAGIAIQfDYCACABQcAAQfMQIAYQVxoMAQsgACgCLCACTQ0CIAJFBEAgAUHw7wAoAAA2AAMgAUHt7wAoAAA2AAAMAQsgACgCOCACQQJ0aigCACIEEOMDDQMgASECAkAgBEUNACAEKQIEIgdCgICAgAiDUARAIARBEGohAyAHp0H/////B3EhBUEAIQJBACEAA0AgAiAFRkUEQCAAIAIgA2otAAByIQAgAkEBaiECDAELCyAAQYABSA0DCyAEQRBqIQVBACEAIAEhAgNAIAAgB6dB/////wdxTw0BAn8gB0KAgICACINQRQRAIAUgAEEBdGovAQAMAQsgACAFai0AAAshAyACIAFrQTlKDQECfyADQf8ATQRAIAIgAzoAACACQQFqDAELIAIgAxDmAiACagshAiAAQQFqIQAgBCkCBCEHDAALAAsgAkEAOgAACyABIQMLIAZBEGokACADDwtBrcgAQb7jAEHfF0GH6AAQAAALQav3AEG+4wBB6RdBh+gAEAAACxwAIAAQIkUEQEEADwsgAKctAAVBAXZBf3NBAXELswUBBH8CQAJAAkAgAS0ABEEPcQ4CAgABCyAAIAEoAhQgASgCGEEBEKMFAkAgASgCIEUNAANAIAIgAS8BKiABLwEoak8NASAAIAEoAiAgAkEEdGooAgAQ9AEgAkEBaiECDAALAAtBACECA0AgASgCOCACTARAAkBBACECA0AgASgCPCACSgRAIAAgASgCJCACQQN0aigCBBD0ASACQQFqIQIMAQsLIAEoAjAiAgRAIAIQrgMLIAAgASgCHBD0ASABLQASQQRxBEAgACABKAJAEPQBIAAgASgCUBAhIAAgASgCVBAhCyABEJ8CAkAgAC0AaEECRw0AIAEoAgBFDQAgAUEIaiAAQdgAahBMDAELIAAgARAhCwUgACABKAI0IAJBA3RqKQMAECcgAkEBaiECDAELCw8LEAEACyABIAEtAAVBAnI6AAUgASgCECIEECohAwNAIAEoAhQhBSAEKAIgIAJKBEAgACAFIAJBA3RqIAMoAgBBGnYQzwUgAkEBaiECIANBCGohAwwBCwsgACAFECEgACAEEJ4CIAFCADcDECABKAIYBEACQCABQRhqIQICQAJAA0AgAigCACICBEAgAigCCCgCAEUNAiACKAIEDQMgAkEYahBGIAJBEGoQRiACQQxqIQIMAQsLIAEoAhghAgNAIAIEQCACKAIMIQMgACACKQMoECcgACACECEgAyECDAELCyABQQA2AhgMAgtBz8AAQb7jAEHu5QJB8MYAEAAAC0G9C0G+4wBB7+UCQfDGABAAAAsLIAAoAkQgAS8BBkEYbGooAggiAgRAIAAgAa1CgICAgHCEIAIRCwALIAFCADcDICABQQA7AQYgAUEANgIoIAEQnwICQAJAIAAtAGhBAkcNACABKAIARQ0AIAFBCGogAEHYAGoQTAwBCyAAIAEQIQsLCQBBASAAEMACC4gDAQJ/IAAoAhAiAygCbCADKAIUQTBqSQRAIAMQnAUgAyADKAIUIgNBAXYgA2o2AmwLAkAgAEEwEC8iAwRAIANBADYCICADQQA2AhggA0EBOgAFIAMgAjsBBiADIAE2AhAgAyAAIAEoAhxBA3QQLyIENgIUIAQNASAAIAMQGgsgACgCECABEJ4CQoCAgIDgAA8LAkACQAJAAkACQAJAAkACQCACQQFrDh4HAAYEBAQEAgYEBgEGBgYGBgUGBgICAgICAgICAgMGCyADQQA2AiggA0IANwMgIAMgAy0ABUEMcjoABSABIAAoAiRHBH8gACADQTBBChCDAQUgBAtCADcDAAwGCyAEQoCAgIAwNwMADAULIANCADcCJCADIAMtAAVBDHI6AAUMBAsgA0IANwIkDAMLIANCgICAgDA3AyAMAQsgA0IANwMgCyAAKAIQKAJEIAJBGGxqKAIURQ0AIAMgAy0ABUEEcjoABQsgA0EBNgIAIAAoAhAgA0EAEL4BIAOtQoCAgIBwhAs8ACAAIAEgAnQgAmtBEWoQ6AEiAARAIABBADYCDCAAQQE2AgAgACABQf////8HcSACQR90cq03AgQLIAAL2QECAX8BfiMAQdAAayIDJAACQAJ+IAEQXgRAIAMgARB8NgIAIANBEGoiAUHAAEHzECADEFcaIAAgARB2DAELIAAoAhAiACgCLCABTQ0BAkACQCAAKAI4IgAgAUECdGooAgAiASkCBCIEQoCAgICAgICAQINCgICAgICAgIDAAFENACACRQ0BIASnQYCAgIB4Rw0AIAAoArwBIQELIAGtQoCAgICQf4QQDwwBCyABrUKAgICAgH+EEA8LIQQgA0HQAGokACAEDwtBrcgAQb7jAEGYGEHsyQAQAAALCgAgAEEBdEEBcgupAQICfwF+IAEpAgRCgICAgAiDIQUgAC0AB0GAAXFFBEAgBVAEQCAAQRBqIAFBEGogAhB3DwtBACABQRBqIABBEGogAhCkBWsPCyABQRBqIQEgAEEQaiEAIAVQBEAgACABIAIQpAUPCwJ/IAJBACACQQBKGyEEA0BBACADIARGDQEaIANBAXQhAiADQQFqIQMgACACai8BACABIAJqLwEAayICRQ0ACyACCwtgAgJ/AX4gAEEQaiEDIAApAgQiBKdB/////wdxIQAgBEKAgICACINQRQRAA0AgACACRwRAIAMgAkEBdGovAQAgAUGHAmxqIQEgAkEBaiECDAELCyABDwsgAyAAIAEQ6AULXwICfwF+IwBBEGsiAiQAAkAgAUEATgRAIAEQlQEhAwwBCyACIAE2AgAgAkEFaiIBQQtB8xAgAhBXGiAAIAEQdiIEEA0NACAAKAIQIASnQQEQ1wIhAwsgAkEQaiQAIAML1QECBX8BfgJAIAEpAgQiB6dB/////wdxIgRBC2tBdkkNAAJ/IAdCgICAgAiDUCIGRQRAIAEvARAMAQsgAS0AEAsiAhBFRQ0AAn8CQCACQTBGBEBBACAEQQFHDQIaDAELIAFBEGohBSACQTBrIQNBASEBA0AgASAERg0BAn8gBkUEQCAFIAFBAXRqLwEADAELIAEgBWotAAALIgIQRUUNAyACQTBrrCADrUIKfnwiB6chAyABQQFqIQEgB0KAgICAEFQNAAsMAgsgACADNgIAQQELDwtBAAssAQF/A0AgASADRkUEQCAAIANqLQAAIAJBhwJsaiECIANBAWohAwwBCwsgAguNAgECfyAAIAEoAgQQEwNAIAEoAhAhAyACIAEoAhRORQRAIAAgAyACQQN0aigCABATIAJBAWohAgwBCwsgACADEBpBACECA0ACQCABKAIcIQMgAiABKAIgTg0AIAMgAkEUbGoiAygCCEUEQCAAKAIQIAMoAgQQ+gELIAAgAygCEBATIAAgAygCDBATIAJBAWohAgwBCwsgACADEBogACABKAIoEBpBACECA0AgASgCNCEDIAIgASgCOE5FBEAgACADIAJBDGxqKAIEEBMgAkEBaiECDAELCyAAIAMQGiAAIAEpA0AQDCAAIAEpA0gQDCAAIAEpA2AQDCAAIAEpA2gQDCABQQhqEEYgACABEBoLqgICAX8DfiMAQSBrIgIkAEKAgICA4AAhBgJAIAAgAykDACIFEGkNACAAIAFBKhBvIgEQDQ0AIAACfgJAIABBIBBsIgRFDQBBACEDIARBADYCFCAEQQA2AgADQCADQQJGRQRAIAQgA0EDdGpBBGoQcSADQQFqIQMMAQsLIARCgICAgDA3AxggASAEEI0BIAAgAkEQaiABEKwFDQACQCAAIAVCgICAgDBBAiACQRBqECQiBxANBEAgAiAAEJMBNwMIIAAgAikDGEKAgICAMEEBIAJBCGoQJCEFIAAgAikDCBAMIAUQDQ0BIAAgBRAMCyAAIAcQDCAAIAIpAxAQDCABIQYgAikDGAwCCyAAIAIpAxAQDCAAIAIpAxgQDAsgAQsQDAsgAkEgaiQAIAYLOAEBfyAAQTBrIgRBCk8EfyAAQcEAayADTQRAIABBN2sPCyAAQdcAayACIABB4QBrIAFJGwUgBAsLuAkCBX4EfyMAQRBrIgIkACAEQeWKAWotAAAiC60hBQJAAkAgAykDACIGQv////9vWARAQoCAgIDgACEHIAAgAkEIaiAGEMQBDQIgAEKAgICAMCACKQMIIgggBYYQjAMiBRANDQJCACEGIAJCADcDAAwBCwJAAkAgBqciCi8BBiIMQRNrQf//A3FBAU0EQCAKKAIgIQpCgICAgOAAIQcgACACIAMpAwgQxAENBCAKLQAEDQICQCACKQMAIgZBfyALdEF/cyILrINQBEAgBiAKKAIAIgysIghYDQELIABB7BkQawwFCwJAIAMpAxAiCRASBEAgCyAMcQ0BIAIgCCAGfSAFiCIINwMIDAMLIAAgAkEIaiAJEMQBDQUgCi0ABA0DIAo0AgAgAikDCCIIIAWGIAZ8Wg0CCyAAQfjBABBrDAQLIAxBFWtB//8DcUEITQRAAn4CQAJAIAAgASAEEG8iARANDQACQAJAIAanIgMQmgFFBEAgAygCKCEKQoCAgIAwIQUgAygCICIMKAIMIgsoAiAiDS0ABUUEQCAAIAutQoCAgIBwhEKAgICAMBDzASIFEA0NAwsgACAFIAqtIgggBEHligFqMQAAhhCMAyEHIAAgBRAMIAcQDQ0CIAMQmgFFDQEgACAHEAwLIAAQdQwBCyAHQRMQQCELIAAgASAHQgAgCBDzAw0AIAMvAQYgBEYNAkEAIQMDQCADIApGDQIgACAGIAMQeyIFEA0NASAAIAEgAyAFEJYCIQQgA0EBaiEDIARBAE4NAAsLIAAgARAMQoCAgIDgACEBCyABDAELIAsoAgggDSgCCCAMKAIQaiALKAIAECUaIAELIQcMBAsjAEEQayIDJABCgICAgOAAIQUgACABIAQQbyIHEA1FBEBCgICAgDAhAQJ+AkAgACAGQcMBIAZBABAUIgUQDQ0AAkACQCAFEBINACAFECgNAEEAIQojAEEQayILJAAgA0EANgIEAkAgABBRIggQDQ0AQoCAgIAwIQkCQCAAIAYgBRDoAyIBEA0NACAAIAFB6gAgAUEAEBQiCRANDQADQCAAIAEgCSALQQxqEK8BIgYQDQ0BIAsoAgwEQCAAIAYQDCAAIAkQDCAAIAEQDCADIAo2AgQMAwsgACAIIAqtIAZBgIABEK4BQQBIDQEgCkEBaiEKDAALAAsgACAJEAwgACABEAwgACAIEAxCgICAgOAAIQgLIAtBEGokACAIIQEgACAFEAwgARANDQIgAyADNQIEIgU3AwgMAQsgACADQQhqIAYQQQ0BIAYQDyEBIAMpAwghBQsgAEKAgICAMCAFIARB5YoBajEAAIYQjAMiBhANDQAgACAHIAZCACAFEPMDDQBBACEEA0AgByAErSAFWQ0CGiAAIAEgBBB7IgYQDQ0BIAAgByAEIAYQlgIhCiAEQQFqIQQgCkEATg0ACwsgACABEAwgByEBQoCAgIDgAAshBSAAIAEQDAsgA0EQaiQAIAUhBwwDCyADKQMAEA8hBQwBCyAAEHUMAQsCQCAAIAEgBBBvIgcQDQRAIAAgBRAMDAELIAAgByAFIAYgCBDzA0UNASAAIAcQDAtCgICAgOAAIQcLIAJBEGokACAHC9IDAgJ+An8jAEEgayIEJAACQCABQv///////////wCDIgNCgICAgICAwIA8fSADQoCAgICAgMD/wwB9VARAIAFCBIYgAEI8iIQhAyAAQv//////////D4MiAEKBgICAgICAgAhaBEAgA0KBgICAgICAgMAAfCECDAILIANCgICAgICAgIBAfSECIABCgICAgICAgIAIUg0BIAIgA0IBg3whAgwBCyAAUCADQoCAgICAgMD//wBUIANCgICAgICAwP//AFEbRQRAIAFCBIYgAEI8iIRC/////////wODQoCAgICAgID8/wCEIQIMAQtCgICAgICAgPj/ACECIANC////////v//DAFYNAEIAIQIgA0IwiKciBUGR9wBJDQAgBEEQaiAAIAFC////////P4NCgICAgICAwACEIgIgBUGB9wBrEHMgBCAAIAJBgfgAIAVrEKECIAQpAwhCBIYgBCkDACIAQjyIhCECIAQpAxAgBCkDGIRCAFKtIABC//////////8Pg4QiAEKBgICAgICAgAhaBEAgAkIBfCECDAELIABCgICAgICAgIAIUg0AIAJCAYMgAnwhAgsgBEEgaiQAIAIgAUKAgICAgICAgIB/g4S/Cw8AIAAgASACQQBBAxCCAguiDwIFfw5+IwBB0AJrIgUkACAEQv///////z+DIQogAkL///////8/gyEMIAIgBIVCgICAgICAgICAf4MhDSAEQjCIp0H//wFxIQgCQAJAIAJCMIinQf//AXEiCUH//wFrQYKAfk8EQCAIQf//AWtBgYB+Sw0BCyABUCACQv///////////wCDIg9CgICAgICAwP//AFQgD0KAgICAgIDA//8AURtFBEAgAkKAgICAgIAghCENDAILIANQIARC////////////AIMiAkKAgICAgIDA//8AVCACQoCAgICAgMD//wBRG0UEQCAEQoCAgICAgCCEIQ0gAyEBDAILIAEgD0KAgICAgIDA//8AhYRQBEAgAyACQoCAgICAgMD//wCFhFAEQEIAIQFCgICAgICA4P//ACENDAMLIA1CgICAgICAwP//AIQhDUIAIQEMAgsgAyACQoCAgICAgMD//wCFhFAEQEIAIQEMAgsgASAPhFAEQEKAgICAgIDg//8AIA0gAiADhFAbIQ1CACEBDAILIAIgA4RQBEAgDUKAgICAgIDA//8AhCENQgAhAQwCCyAPQv///////z9YBEAgBUHAAmogASAMIAEgDCAMUCIGG3kgBkEGdK18pyIGQQ9rEHNBECAGayEGIAUpA8gCIQwgBSkDwAIhAQsgAkL///////8/Vg0AIAVBsAJqIAMgCiADIAogClAiBxt5IAdBBnStfKciB0EPaxBzIAYgB2pBEGshBiAFKQO4AiEKIAUpA7ACIQMLIAVBoAJqIApCgICAgICAwACEIhJCD4YgA0IxiIQiAkIAQoCAgICw5ryC9QAgAn0iBEIAEHIgBUGQAmpCACAFKQOoAn1CACAEQgAQciAFQYACaiAFKQOYAkIBhiAFKQOQAkI/iIQiBEIAIAJCABByIAVB8AFqIARCAEIAIAUpA4gCfUIAEHIgBUHgAWogBSkD+AFCAYYgBSkD8AFCP4iEIgRCACACQgAQciAFQdABaiAEQgBCACAFKQPoAX1CABByIAVBwAFqIAUpA9gBQgGGIAUpA9ABQj+IhCIEQgAgAkIAEHIgBUGwAWogBEIAQgAgBSkDyAF9QgAQciAFQaABaiACQgAgBSkDuAFCAYYgBSkDsAFCP4iEQgF9IgJCABByIAVBkAFqIANCD4ZCACACQgAQciAFQfAAaiACQgBCACAFKQOoASAFKQOgASIPIAUpA5gBfCIEIA9UrXwgBEIBVq18fUIAEHIgBUGAAWpCASAEfUIAIAJCABByIAYgCSAIa2ohBgJ/IAUpA3AiEEIBhiIUIAUpA4gBIg5CAYYgBSkDgAFCP4iEfCILQufsAH0iFUIgiCICIAxCgICAgICAwACEIhZCAYYgAUI/iIQiDEIgiCIEfiIRIAFCAYYiD0IgiCIKIAsgFVatIAsgFFStIAUpA3hCAYYgEEI/iIQgDkI/iHx8fEIBfSIQQiCIIgt+fCIOIBFUrSAOIA4gEEL/////D4MiECAMQv////8PgyIUfnwiDlatfCAEIAt+fCAEIBB+IhMgCyAUfnwiESATVK1CIIYgEUIgiIR8IA4gDiARQiCGfCIOVq18IA4gDiAVQv////8PgyIVIBR+IhMgAiAKfnwiESATVK0gESARIBAgD0L+////D4MiE358IhFWrXx8Ig5WrXwgDiAEIBV+IhcgCyATfnwiBCACIBR+fCILIAogEH58IhBCIIggCyAQVq0gBCAXVK0gBCALVq18fEIghoR8IgQgDlStfCAEIBEgAiATfiICIAogFX58IgpCIIggAiAKVq1CIIaEfCICIBFUrSACIBBCIIZ8IAJUrXx8IgIgBFStfCIEQv////////8AWARAIAVB0ABqIAIgBCADIBIQciABQjGGIAUpA1h9IAUpA1AiAUIAUq19IQtCACABfSEKIAZB/v8AagwBCyAFQeAAaiAEQj+GIAJCAYiEIgIgBEIBiCIEIAMgEhByIAFCMIYgBSkDaH0gBSkDYCIMQgBSrX0hC0IAIAx9IQogASEPIBYhDCAGQf//AGoLIgZB//8BTgRAIA1CgICAgICAwP//AIQhDUIAIQEMAQsCfiAGQQBKBEAgC0IBhiAKQj+IhCELIARC////////P4MgBq1CMIaEIQwgCkIBhgwBCyAGQY9/TARAQgAhAQwCCyAFQUBrIAIgBEEBIAZrEKECIAVBMGogDyAMIAZB8ABqEHMgBUEgaiADIBIgBSkDQCICIAUpA0giDBByIAUpAzggBSkDKEIBhiAFKQMgIgFCP4iEfSAFKQMwIgQgAUIBhiIBVK19IQsgBCABfQshBCAFQRBqIAMgEkIDQgAQciAFIAMgEkIFQgAQciAMIAIgAiADIAJCAYMiASAEfCIDVCALIAEgA1atfCIBIBJWIAEgElEbrXwiAlatfCIEIAIgAiAEQoCAgICAgMD//wBUIAMgBSkDEFYgASAFKQMYIgRWIAEgBFEbca18IgJWrXwiBCACIARCgICAgICAwP//AFQgAyAFKQMAViABIAUpAwgiA1YgASADURtxrXwiASACVK18IA2EIQ0LIAAgATcDACAAIA03AwggBUHQAmokAAvEAQIBfwJ+QX8hAwJAIABCAFIgAUL///////////8AgyIEQoCAgICAgMD//wBWIARCgICAgICAwP//AFEbDQBBACACQv///////////wCDIgVCgICAgICAwP//AFYgBUKAgICAgIDA//8AURsNACAAIAQgBYSEUARAQQAPCyABIAKDQgBZBEBBACABIAJTIAEgAlEbDQEgACABIAKFhEIAUg8LIABCAFIgASACVSABIAJRGw0AIAAgASAChYRCAFIhAwsgAwuLDAEGfyAAIAFqIQUCQAJAIAAoAgQiAkEBcQ0AIAJBA3FFDQEgACgCACICIAFqIQECQCAAIAJrIgBBrL0EKAIARwRAIAJB/wFNBEAgACgCCCIEIAJBA3YiAkEDdEHAvQRqRhogACgCDCIDIARHDQJBmL0EQZi9BCgCAEF+IAJ3cTYCAAwDCyAAKAIYIQYCQCAAIAAoAgwiA0cEQCAAKAIIIgJBqL0EKAIASRogAiADNgIMIAMgAjYCCAwBCwJAIABBFGoiAigCACIEDQAgAEEQaiICKAIAIgQNAEEAIQMMAQsDQCACIQcgBCIDQRRqIgIoAgAiBA0AIANBEGohAiADKAIQIgQNAAsgB0EANgIACyAGRQ0CAkAgACgCHCIEQQJ0Qci/BGoiAigCACAARgRAIAIgAzYCACADDQFBnL0EQZy9BCgCAEF+IAR3cTYCAAwECyAGQRBBFCAGKAIQIABGG2ogAzYCACADRQ0DCyADIAY2AhggACgCECICBEAgAyACNgIQIAIgAzYCGAsgACgCFCICRQ0CIAMgAjYCFCACIAM2AhgMAgsgBSgCBCICQQNxQQNHDQFBoL0EIAE2AgAgBSACQX5xNgIEIAAgAUEBcjYCBCAFIAE2AgAPCyAEIAM2AgwgAyAENgIICwJAIAUoAgQiAkECcUUEQEGwvQQoAgAgBUYEQEGwvQQgADYCAEGkvQRBpL0EKAIAIAFqIgE2AgAgACABQQFyNgIEIABBrL0EKAIARw0DQaC9BEEANgIAQay9BEEANgIADwtBrL0EKAIAIAVGBEBBrL0EIAA2AgBBoL0EQaC9BCgCACABaiIBNgIAIAAgAUEBcjYCBCAAIAFqIAE2AgAPCyACQXhxIAFqIQECQCACQf8BTQRAIAUoAggiBCACQQN2IgJBA3RBwL0EakYaIAQgBSgCDCIDRgRAQZi9BEGYvQQoAgBBfiACd3E2AgAMAgsgBCADNgIMIAMgBDYCCAwBCyAFKAIYIQYCQCAFIAUoAgwiA0cEQCAFKAIIIgJBqL0EKAIASRogAiADNgIMIAMgAjYCCAwBCwJAIAVBFGoiBCgCACICDQAgBUEQaiIEKAIAIgINAEEAIQMMAQsDQCAEIQcgAiIDQRRqIgQoAgAiAg0AIANBEGohBCADKAIQIgINAAsgB0EANgIACyAGRQ0AAkAgBSgCHCIEQQJ0Qci/BGoiAigCACAFRgRAIAIgAzYCACADDQFBnL0EQZy9BCgCAEF+IAR3cTYCAAwCCyAGQRBBFCAGKAIQIAVGG2ogAzYCACADRQ0BCyADIAY2AhggBSgCECICBEAgAyACNgIQIAIgAzYCGAsgBSgCFCICRQ0AIAMgAjYCFCACIAM2AhgLIAAgAUEBcjYCBCAAIAFqIAE2AgAgAEGsvQQoAgBHDQFBoL0EIAE2AgAPCyAFIAJBfnE2AgQgACABQQFyNgIEIAAgAWogATYCAAsgAUH/AU0EQCABQQN2IgJBA3RBwL0EaiEBAn9BmL0EKAIAIgNBASACdCICcUUEQEGYvQQgAiADcjYCACABDAELIAEoAggLIQIgASAANgIIIAIgADYCDCAAIAE2AgwgACACNgIIDwtBHyECIAFB////B00EQCABQQh2IgIgAkGA/j9qQRB2QQhxIgR0IgIgAkGA4B9qQRB2QQRxIgN0IgIgAkGAgA9qQRB2QQJxIgJ0QQ92IAMgBHIgAnJrIgJBAXQgASACQRVqdkEBcXJBHGohAgsgACACNgIcIABCADcCECACQQJ0Qci/BGohBwJAAkBBnL0EKAIAIgRBASACdCIDcUUEQEGcvQQgAyAEcjYCACAHIAA2AgAgACAHNgIYDAELIAFBAEEZIAJBAXZrIAJBH0YbdCECIAcoAgAhAwNAIAMiBCgCBEF4cSABRg0CIAJBHXYhAyACQQF0IQIgBCADQQRxaiIHQRBqKAIAIgMNAAsgByAANgIQIAAgBDYCGAsgACAANgIMIAAgADYCCA8LIAQoAggiASAANgIMIAQgADYCCCAAQQA2AhggACAENgIMIAAgATYCCAsLnAgBC38gAEUEQCABEKMCDwsgAUFATwRAQcSzBEEwNgIAQQAPCwJ/QRAgAUELakF4cSABQQtJGyEFIABBCGsiBigCBCIJQXhxIQQCQCAJQQNxRQRAQQAgBUGAAkkNAhogBUEEaiAETQRAIAYhAiAEIAVrQfjABCgCAEEBdE0NAgtBAAwCCyAEIAZqIQcCQCAEIAVPBEAgBCAFayIDQRBJDQEgBiAJQQFxIAVyQQJyNgIEIAUgBmoiAiADQQNyNgIEIAcgBygCBEEBcjYCBCACIAMQ8QUMAQtBsL0EKAIAIAdGBEBBpL0EKAIAIARqIgQgBU0NAiAGIAlBAXEgBXJBAnI2AgQgBSAGaiIDIAQgBWsiAkEBcjYCBEGkvQQgAjYCAEGwvQQgAzYCAAwBC0GsvQQoAgAgB0YEQEGgvQQoAgAgBGoiAyAFSQ0CAkAgAyAFayICQRBPBEAgBiAJQQFxIAVyQQJyNgIEIAUgBmoiBCACQQFyNgIEIAMgBmoiAyACNgIAIAMgAygCBEF+cTYCBAwBCyAGIAlBAXEgA3JBAnI2AgQgAyAGaiICIAIoAgRBAXI2AgRBACECQQAhBAtBrL0EIAQ2AgBBoL0EIAI2AgAMAQsgBygCBCIDQQJxDQEgA0F4cSAEaiIKIAVJDQEgCiAFayEMAkAgA0H/AU0EQCAHKAIIIgQgA0EDdiICQQN0QcC9BGpGGiAEIAcoAgwiA0YEQEGYvQRBmL0EKAIAQX4gAndxNgIADAILIAQgAzYCDCADIAQ2AggMAQsgBygCGCELAkAgByAHKAIMIghHBEAgBygCCCICQai9BCgCAEkaIAIgCDYCDCAIIAI2AggMAQsCQCAHQRRqIgQoAgAiAg0AIAdBEGoiBCgCACICDQBBACEIDAELA0AgBCEDIAIiCEEUaiIEKAIAIgINACAIQRBqIQQgCCgCECICDQALIANBADYCAAsgC0UNAAJAIAcoAhwiA0ECdEHIvwRqIgIoAgAgB0YEQCACIAg2AgAgCA0BQZy9BEGcvQQoAgBBfiADd3E2AgAMAgsgC0EQQRQgCygCECAHRhtqIAg2AgAgCEUNAQsgCCALNgIYIAcoAhAiAgRAIAggAjYCECACIAg2AhgLIAcoAhQiAkUNACAIIAI2AhQgAiAINgIYCyAMQQ9NBEAgBiAJQQFxIApyQQJyNgIEIAYgCmoiAiACKAIEQQFyNgIEDAELIAYgCUEBcSAFckECcjYCBCAFIAZqIgMgDEEDcjYCBCAGIApqIgIgAigCBEEBcjYCBCADIAwQ8QULIAYhAgsgAgsiAgRAIAJBCGoPCyABEKMCIgNFBEBBAA8LIAMgAEF8QXggBigCBCICQQNxGyACQXhxaiICIAEgASACSxsQJRogABDpASADC5kCACAARQRAQQAPCwJ/AkAgAAR/IAFB/wBNDQECQEH0tAQoAgAoAgBFBEAgAUGAf3FBgL8DRg0DDAELIAFB/w9NBEAgACABQT9xQYABcjoAASAAIAFBBnZBwAFyOgAAQQIMBAsgAUGAQHFBgMADRyABQYCwA09xRQRAIAAgAUE/cUGAAXI6AAIgACABQQx2QeABcjoAACAAIAFBBnZBP3FBgAFyOgABQQMMBAsgAUGAgARrQf//P00EQCAAIAFBP3FBgAFyOgADIAAgAUESdkHwAXI6AAAgACABQQZ2QT9xQYABcjoAAiAAIAFBDHZBP3FBgAFyOgABQQQMBAsLQcSzBEEZNgIAQX8FQQELDAELIAAgAToAAEEBCwsWACAARQRAQQAPC0HEswQgADYCAEF/C8QCAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAFBCWsOEgAKCwwKCwIDBAUMCwwMCgsHCAkLIAIgAigCACIBQQRqNgIAIAAgASgCADYCAA8LAAsgAiACKAIAIgFBBGo2AgAgACABMgEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMwEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMAAANwMADwsgAiACKAIAIgFBBGo2AgAgACABMQAANwMADwsACyACIAIoAgBBB2pBeHEiAUEIajYCACAAIAErAwA5AwAPCyAAIAIgAxEDAAsPCyACIAIoAgAiAUEEajYCACAAIAE0AgA3AwAPCyACIAIoAgAiAUEEajYCACAAIAE1AgA3AwAPCyACIAIoAgBBB2pBeHEiAUEIajYCACAAIAEpAwA3AwALawEEfyAAKAIALAAAEEVFBEBBAA8LA0AgACgCACEDQX8hASACQcyZs+YATQRAQX8gAywAAEEwayIEIAJBCmwiAWogBEH/////ByABa0obIQELIAAgA0EBajYCACABIQIgAywAARBFDQALIAIL8RICEX8BfiMAQdAAayIHJAAgByABNgJMIAdBN2ohFiAHQThqIRJBACEBAkACQAJAAkADQCABQf////8HIA1rSg0BIAEgDWohDSAHKAJMIgwhAQJAAkACQCAMLQAAIgsEQANAAkACQCALQf8BcSIIRQRAIAEhCwwBCyAIQSVHDQEgASELA0AgAS0AAUElRw0BIAcgAUECaiIINgJMIAtBAWohCyABLQACIQogCCEBIApBJUYNAAsLIAsgDGsiAUH/////ByANayIXSg0HIAAEQCAAIAwgARBnCyABDQZBfyEQQQEhCCAHKAJMLAABEEUhASAHKAJMIQoCQCABRQ0AIAotAAJBJEcNACAKLAABQTBrIRBBASEUQQMhCAsgByAIIApqIgE2AkxBACEOAkAgASwAACITQSBrIgpBH0sEQCABIQgMAQsgASEIQQEgCnQiCUGJ0QRxRQ0AA0AgByABQQFqIgg2AkwgCSAOciEOIAEsAAEiE0EgayIKQSBPDQEgCCEBQQEgCnQiCUGJ0QRxDQALCwJAIBNBKkYEQCAHAn8CQCAILAABEEVFDQAgBygCTCIBLQACQSRHDQAgASwAAUECdCAEakHAAWtBCjYCACABLAABQQN0IANqQYADaygCACEPQQEhFCABQQNqDAELIBQNBkEAIRRBACEPIAAEQCACIAIoAgAiAUEEajYCACABKAIAIQ8LIAcoAkxBAWoLIgE2AkwgD0EATg0BQQAgD2shDyAOQYDAAHIhDgwBCyAHQcwAahD2BSIPQQBIDQggBygCTCEBC0EAIQhBfyEJAn9BACABLQAAQS5HDQAaIAEtAAFBKkYEQCAHAn8CQCABLAACEEVFDQAgBygCTCIBLQADQSRHDQAgASwAAkECdCAEakHAAWtBCjYCACABLAACQQN0IANqQYADaygCACEJIAFBBGoMAQsgFA0GIAAEfyACIAIoAgAiAUEEajYCACABKAIABUEACyEJIAcoAkxBAmoLIgE2AkwgCUF/c0EfdgwBCyAHIAFBAWo2AkwgB0HMAGoQ9gUhCSAHKAJMIQFBAQshFQNAIAghEUEcIQsgASwAAEH7AGtBRkkNCSAHIAFBAWoiEzYCTCABLAAAIQggEyEBIAggEUE6bGpB36wEai0AACIIQQFrQQhJDQALAkACQCAIQRtHBEAgCEUNCyAQQQBOBEAgBCAQQQJ0aiAINgIAIAcgAyAQQQN0aikDADcDQAwCCyAARQ0IIAdBQGsgCCACIAYQ9QUgBygCTCETDAILIBBBAE4NCgtBACEBIABFDQcLIA5B//97cSIKIA4gDkGAwABxGyEIQQAhDkHrDyEQIBIhCwJAAkACQAJ/AkACQAJAAkACfwJAAkACQAJAAkACQAJAIBNBAWssAAAiAUFfcSABIAFBD3FBA0YbIAEgERsiAUHYAGsOIQQUFBQUFBQUFA4UDwYODg4UBhQUFBQCBQMUFAkUARQUBAALAkAgAUHBAGsOBw4UCxQODg4ACyABQdMARg0JDBMLIAcpA0AhGEHrDwwFC0EAIQECQAJAAkACQAJAAkACQCARQf8BcQ4IAAECAwQaBQYaCyAHKAJAIA02AgAMGQsgBygCQCANNgIADBgLIAcoAkAgDaw3AwAMFwsgBygCQCANOwEADBYLIAcoAkAgDToAAAwVCyAHKAJAIA02AgAMFAsgBygCQCANrDcDAAwTCyAJQQggCUEISxshCSAIQQhyIQhB+AAhAQsgEiEKIAFBIHEhESAHKQNAIhhQRQRAA0AgCkEBayIKIBinQQ9xQfCwBGotAAAgEXI6AAAgGEIPViEMIBhCBIghGCAMDQALCyAKIQwgCEEIcUUgBykDQFByDQMgAUEEdkHrD2ohEEECIQ4MAwsgEiEBIAcpA0AiGFBFBEADQCABQQFrIgEgGKdBB3FBMHI6AAAgGEIHViEKIBhCA4ghGCAKDQALCyABIQwgCEEIcUUNAiAJIBIgDGsiAUEBaiABIAlIGyEJDAILIAcpA0AiGEIAUwRAIAdCACAYfSIYNwNAQQEhDkHrDwwBCyAIQYAQcQRAQQEhDkHsDwwBC0HtD0HrDyAIQQFxIg4bCyEQIBggEhCkAiEMCyAVQQAgCUEASBsNDiAIQf//e3EgCCAVGyEIIAcpA0AiGEIAUiAJckUEQCASIgwhC0EAIQkMDAsgCSAYUCASIAxraiIBIAEgCUgbIQkMCwsgBygCQCIBQbz3ACABGyIMIAlB/////wcgCUH/////B0kbEIAGIgEgDGohCyAJQQBOBEAgCiEIIAEhCQwLCyAKIQggASEJIAstAAANDQwKCyAJBEAgBygCQAwCC0EAIQEgAEEgIA9BACAIEG0MAgsgB0EANgIMIAcgBykDQD4CCCAHIAdBCGoiATYCQEF/IQkgAQshC0EAIQECQANAIAsoAgAiCkUNASAHQQRqIAoQ8wUiDEEASCIKIAwgCSABa0tyRQRAIAtBBGohCyAJIAEgDGoiAUsNAQwCCwsgCg0NC0E9IQsgAUEASA0LIABBICAPIAEgCBBtIAFFBEBBACEBDAELQQAhCSAHKAJAIQsDQCALKAIAIgpFDQEgB0EEaiAKEPMFIgogCWoiCSABSw0BIAAgB0EEaiAKEGcgC0EEaiELIAEgCUsNAAsLIABBICAPIAEgCEGAwABzEG0gDyABIAEgD0gbIQEMCAsgFUEAIAlBAEgbDQhBPSELIAAgBysDQCAPIAkgCCABIAURRAAiAUEATg0HDAkLIAcgBykDQDwAN0EBIQkgFiEMIAohCAwECyAHIAFBAWoiCDYCTCABLQABIQsgCCEBDAALAAsgAA0HIBRFDQJBASEBA0AgBCABQQJ0aigCACIABEAgAyABQQN0aiAAIAIgBhD1BUEBIQ0gAUEBaiIBQQpHDQEMCQsLQQEhDSABQQpPDQcDQCAEIAFBAnRqKAIADQEgAUEBaiIBQQpHDQALDAcLQRwhCwwECyAJIAsgDGsiESAJIBFKGyIKQf////8HIA5rSg0CQT0hCyAPIAogDmoiCSAJIA9IGyIBIBdKDQMgAEEgIAEgCSAIEG0gACAQIA4QZyAAQTAgASAJIAhBgIAEcxBtIABBMCAKIBFBABBtIAAgDCAREGcgAEEgIAEgCSAIQYDAAHMQbQwBCwtBACENDAMLQT0hCwtBxLMEIAs2AgALQX8hDQsgB0HQAGokACANC38CAX8BfiAAvSIDQjSIp0H/D3EiAkH/D0cEfCACRQRAIAEgAEQAAAAAAAAAAGEEf0EABSAARAAAAAAAAPBDoiABEPgFIQAgASgCAEFAags2AgAgAA8LIAEgAkH+B2s2AgAgA0L/////////h4B/g0KAgICAgICA8D+EvwUgAAsLqAMDAnwDfwF+IAC9IghCIIinIgVB+P///wdxQaiolv8DSSIGRQRARBgtRFT7Iek/IAAgAJogCEIAWSIHG6FEB1wUMyamgTwgASABmiAHG6GgIQAgBUEfdiEFRAAAAAAAAAAAIQELIAAgACAAIACiIgSiIgNEY1VVVVVV1T+iIAQgAyAEIASiIgMgAyADIAMgA0RzU2Dby3XzvqJEppI3oIh+FD+gokQBZfLy2ERDP6CiRCgDVskibW0/oKJEN9YGhPRklj+gokR6/hARERHBP6AgBCADIAMgAyADIANE1Hq/dHAq+z6iROmn8DIPuBI/oKJEaBCNGvcmMD+gokQVg+D+yNtXP6CiRJOEbunjJoI/oKJE/kGzG7qhqz+goqCiIAGgoiABoKAiA6AhASAGRQRAQQEgAkEBdGu3IgQgACADIAEgAaIgASAEoKOhoCIAIACgoSIAmiAAIAUbDwsgAgR8RAAAAAAAAPC/IAGjIgQgBL1CgICAgHCDvyIEIAMgAb1CgICAgHCDvyIBIAChoaIgBCABokQAAAAAAADwP6CgoiAEoAUgAQsL0DIDFH8HfgF8IwBBEGsiDyQAIwBBoAFrIgMkACADIAA2AjwgAyAANgIUIANBfzYCGCADQRBqIgAQqwQgAyEQIwBBMGsiDCQAQZCtBCgCACEOQYStBCgCACENA0ACfyAAKAIEIgMgACgCaEcEQCAAIANBAWo2AgQgAy0AAAwBCyAAEFwLIgIQgwYNAAtBASEDAkACQCACQStrDgMAAQABC0F/QQEgAkEtRhshAyAAKAIEIgIgACgCaEcEQCAAIAJBAWo2AgQgAi0AACECDAELIAAQXCECCwJAAkACQANAIARByAtqLAAAIAJBIHJGBEACQCAEQQZLDQAgACgCBCICIAAoAmhHBEAgACACQQFqNgIEIAItAAAhAgwBCyAAEFwhAgsgBEEBaiIEQQhHDQEMAgsLIARBA0cEQCAEQQhGDQEgBEEESQ0CIARBCEYNAQsgACkDcCIVQgBZBEAgACAAKAIEQQFrNgIECyAEQQRJDQAgFUIAUyECA0AgAkUEQCAAIAAoAgRBAWs2AgQLIARBAWsiBEEDSw0ACwtCACEVIwBBEGsiAiQAAn4gA7JDAACAf5S8IgNB/////wdxIgBBgICABGtB////9wdNBEAgAK1CGYZCgICAgICAgMA/fAwBCyADrUIZhkKAgICAgIDA//8AhCAAQYCAgPwHTw0AGkIAIABFDQAaIAIgAK1CACAAZyIAQdEAahBzIAIpAwAhFSACKQMIQoCAgICAgMAAhUGJ/wAgAGutQjCGhAshFiAMIBU3AwAgDCAWIANBgICAgHhxrUIghoQ3AwggAkEQaiQAIAwpAwghFSAMKQMAIRYMAQsCQAJAAkAgBA0AQQAhBANAIARB0jtqLAAAIAJBIHJHDQECQCAEQQFLDQAgACgCBCICIAAoAmhHBEAgACACQQFqNgIEIAItAAAhAgwBCyAAEFwhAgsgBEEBaiIEQQNHDQALDAELAkACQCAEDgQAAQECAQsCQCACQTBHDQACfyAAKAIEIgEgACgCaEcEQCAAIAFBAWo2AgQgAS0AAAwBCyAAEFwLQV9xQdgARgRAIwBBsANrIgIkAAJ/IAAoAgQiASAAKAJoRwRAIAAgAUEBajYCBCABLQAADAELIAAQXAshBAJAAn8DQCAEQTBHBEACQCAEQS5HDQQgACgCBCIBIAAoAmhGDQAgACABQQFqNgIEIAEtAAAMAwsFIAAoAgQiASAAKAJoRwR/QQEhBiAAIAFBAWo2AgQgAS0AAAVBASEGIAAQXAshBAwBCwsgABBcCyEEQQEhCSAEQTBHDQADQCAYQgF9IRgCfyAAKAIEIgYgACgCaEcEQCAAIAZBAWo2AgQgBi0AAAwBCyAAEFwLIgRBMEYNAAtBASEGC0KAgICAgIDA/z8hFgJAA0ACQCAEQSByIQECQAJAIARBMGsiB0EKSQ0AIARBLkcgAUHhAGtBBk9xDQQgBEEuRw0AIAkNAkEBIQkgFSEYDAELIAFB1wBrIAcgBEE5ShshBgJAIBVCB1cEQCAGIAVBBHRqIQUMAQsgFUIcWARAIAJBMGogBhCEASACQSBqIBogFkIAQoCAgICAgMD9PxAzIAJBEGogAikDMCACKQM4IAIpAyAiGiACKQMoIhYQMyACIAIpAxAgAikDGCAXIBkQfSACKQMIIRkgAikDACEXDAELIAZFIAtyDQAgAkHQAGogGiAWQgBCgICAgICAgP8/EDMgAkFAayACKQNQIAIpA1ggFyAZEH0gAikDSCEZQQEhCyACKQNAIRcLIBVCAXwhFUEBIQYLIAAoAgQiASAAKAJoRwR/IAAgAUEBajYCBCABLQAABSAAEFwLIQQMAQsLQS4hBAsCfiAGRQRAIAApA3BCAFkEQAJAIAAgACgCBCIFQQFrNgIEIAAgBUECazYCBCAJRQ0AIAAgBUEDazYCBAsLIAJB4ABqIAO3RAAAAAAAAAAAohC4ASACKQNgIRcgAikDaAwBCyAVQgdXBEAgFSEWA0AgBUEEdCEFIBZCAXwiFkIIUg0ACwsCQAJAAkAgBEFfcUHQAEYEQCAAEPsFIhZCgICAgICAgICAf1INAyAAKQNwQgBZDQEMAgtCACEWIAApA3BCAFMNAgsgACAAKAIEQQFrNgIEC0IAIRYLIAVFBEAgAkHwAGogA7dEAAAAAAAAAACiELgBIAIpA3AhFyACKQN4DAELIBggFSAJG0IChiAWfEIgfSIVQQAgDmutVQRAQcSzBEHEADYCACACQaABaiADEIQBIAJBkAFqIAIpA6ABIAIpA6gBQn9C////////v///ABAzIAJBgAFqIAIpA5ABIAIpA5gBQn9C////////v///ABAzIAIpA4ABIRcgAikDiAEMAQsgDkHiAWusIBVXBEAgBUEATgRAA0AgAkGgA2ogFyAZQgBCgICAgICAwP+/fxB9IBcgGUKAgICAgICA/z8Q8AUhACACQZADaiAXIBkgFyACKQOgAyAAQQBIIgYbIBkgAikDqAMgBhsQfSAVQgF9IRUgAikDmAMhGSACKQOQAyEXIAVBAXQgAEEATnIiBUEATg0ACwsCfiAVIA6sfUIgfCIWpyIAQQAgAEEAShsgDSAWIA2tUxsiAEHxAE4EQCACQYADaiADEIQBIAIpA4gDIRggAikDgAMhGkIADAELIAJB4AJqRAAAAAAAAPA/QZABIABrEOoBELgBIAJB0AJqIAMQhAEgAkHwAmogAikD4AIgAikD6AIgAikD0AIiGiACKQPYAiIYEP4FIAIpA/gCIRsgAikD8AILIRYgAkHAAmogBSAFQQFxRSAXIBlCAEIAEP8BQQBHIABBIEhxcSIAahCiAiACQbACaiAaIBggAikDwAIgAikDyAIQMyACQZACaiACKQOwAiACKQO4AiAWIBsQfSACQaACaiAaIBhCACAXIAAbQgAgGSAAGxAzIAJBgAJqIAIpA6ACIAIpA6gCIAIpA5ACIAIpA5gCEH0gAkHwAWogAikDgAIgAikDiAIgFiAbEKcEIAIpA/ABIhYgAikD+AEiGEIAQgAQ/wFFBEBBxLMEQcQANgIACyACQeABaiAWIBggFacQ/QUgAikD4AEhFyACKQPoAQwBC0HEswRBxAA2AgAgAkHQAWogAxCEASACQcABaiACKQPQASACKQPYAUIAQoCAgICAgMAAEDMgAkGwAWogAikDwAEgAikDyAFCAEKAgICAgIDAABAzIAIpA7ABIRcgAikDuAELIRUgDCAXNwMQIAwgFTcDGCACQbADaiQAIAwpAxghFSAMKQMQIRYMBQsgACkDcEIAUw0AIAAgACgCBEEBazYCBAsgACEFIAIhACADIQtBACECIwBBkMYAayIBJABBACANIA5qIhNrIRQCQAJ/A0AgAEEwRwRAAkAgAEEuRw0EIAUoAgQiACAFKAJoRg0AIAUgAEEBajYCBCAALQAADAMLBSAFKAIEIgAgBSgCaEcEf0EBIQIgBSAAQQFqNgIEIAAtAAAFQQEhAiAFEFwLIQAMAQsLIAUQXAshAEEBIQcgAEEwRw0AA0AgFUIBfSEVAn8gBSgCBCIAIAUoAmhHBEAgBSAAQQFqNgIEIAAtAAAMAQsgBRBcCyIAQTBGDQALQQEhAgsgAUEANgKQBiAMAn4CQAJAAkACQCAAQS5GIgMgAEEwayIIQQlNcgRAA0ACQCADQQFxBEAgB0UEQCAWIRVBASEHDAILIAJFIQMMBAsgFkIBfCEWIAZB/A9MBEAgCSAWpyAAQTBGGyEJIAFBkAZqIAZBAnRqIgMgCgR/IAAgAygCAEEKbGpBMGsFIAgLNgIAQQEhAkEAIApBAWoiACAAQQlGIgAbIQogACAGaiEGDAELIABBMEYNACABIAEoAoBGQQFyNgKARkHcjwEhCQsCfyAFKAIEIgAgBSgCaEcEQCAFIABBAWo2AgQgAC0AAAwBCyAFEFwLIgBBLkYiAyAAQTBrIghBCklyDQALCyAVIBYgBxshFSACRSAAQV9xQcUAR3JFBEACQCAFEPsFIhdCgICAgICAgICAf1INAEIAIRcgBSkDcEIAUw0AIAUgBSgCBEEBazYCBAsgAkUNAyAVIBd8IRUMBAsgAkUhAyAAQQBIDQELIAUpA3BCAFMNACAFIAUoAgRBAWs2AgQLIANFDQELQcSzBEEcNgIAQgAhFiAFEKsEQgAMAQsgASgCkAYiAEUEQCABIAu3RAAAAAAAAAAAohC4ASABKQMAIRYgASkDCAwBCyAVIBZSIBZCCVVyIA1BHkxBACAAIA12G3JFBEAgAUEwaiALEIQBIAFBIGogABCiAiABQRBqIAEpAzAgASkDOCABKQMgIAEpAygQMyABKQMQIRYgASkDGAwBCyAOQX5trSAVUwRAQcSzBEHEADYCACABQeAAaiALEIQBIAFB0ABqIAEpA2AgASkDaEJ/Qv///////7///wAQMyABQUBrIAEpA1AgASkDWEJ/Qv///////7///wAQMyABKQNAIRYgASkDSAwBCyAOQeIBa6wgFVUEQEHEswRBxAA2AgAgAUGQAWogCxCEASABQYABaiABKQOQASABKQOYAUIAQoCAgICAgMAAEDMgAUHwAGogASkDgAEgASkDiAFCAEKAgICAgIDAABAzIAEpA3AhFiABKQN4DAELIAoEQCAKQQhMBEAgAUGQBmogBkECdGoiACgCACEEA0AgBEEKbCEEIApBAWoiCkEJRw0ACyAAIAQ2AgALIAZBAWohBgsCQCAJIBWnIgdKIAlBCU5yIAdBEUpyDQAgB0EJRgRAIAFBwAFqIAsQhAEgAUGwAWogASgCkAYQogIgAUGgAWogASkDwAEgASkDyAEgASkDsAEgASkDuAEQMyABKQOgASEWIAEpA6gBDAILIAdBCEwEQCABQZACaiALEIQBIAFBgAJqIAEoApAGEKICIAFB8AFqIAEpA5ACIAEpA5gCIAEpA4ACIAEpA4gCEDMgAUHgAWpBACAHa0ECdEGArQRqKAIAEIQBIAFB0AFqIAEpA/ABIAEpA/gBIAEpA+ABIAEpA+gBEO8FIAEpA9ABIRYgASkD2AEMAgsgDSAHQX1sakEbaiIAQR5MQQAgASgCkAYiAyAAdhsNACABQeACaiALEIQBIAFB0AJqIAMQogIgAUHAAmogASkD4AIgASkD6AIgASkD0AIgASkD2AIQMyABQbACaiAHQQJ0QbisBGooAgAQhAEgAUGgAmogASkDwAIgASkDyAIgASkDsAIgASkDuAIQMyABKQOgAiEWIAEpA6gCDAELA0AgAUGQBmogBiIAQQFrIgZBAnRqKAIARQ0AC0EAIQoCQCAHQQlvIgJFBEBBACEDDAELQQAhAyACQQlqIAIgB0EASBshAgJAIABFBEBBACEADAELQYCU69wDQQAgAmtBAnRBgK0EaigCACIFbSEGQQAhCEEAIQQDQCABQZAGaiAEQQJ0aiIJIAggCSgCACIJIAVuIhFqIgg2AgAgA0EBakH/D3EgAyAIRSADIARGcSIIGyEDIAdBCWsgByAIGyEHIAYgCSAFIBFsa2whCCAEQQFqIgQgAEcNAAsgCEUNACABQZAGaiAAQQJ0aiAINgIAIABBAWohAAsgByACa0EJaiEHCwNAIAFBkAZqIANBAnRqIQYCQANAIAdBJE4EQCAHQSRHDQIgBigCAEHR6fkETw0CCyAAQf8PaiECQQAhCANAIAitIAFBkAZqIAJB/w9xIgVBAnRqIgI1AgBCHYZ8IhVCgZTr3ANUBH9BAAUgFSAVQoCU69wDgCIWQoCU69wDfn0hFSAWpwshCCACIBWnIgI2AgAgACAAIAAgBSACGyADIAVGGyAFIABBAWtB/w9xRxshACAFQQFrIQIgAyAFRw0ACyAKQR1rIQogCEUNAAsgACADQQFrQf8PcSIDRgRAIAFBkAZqIgIgAEH+D2pB/w9xQQJ0aiIFIAUoAgAgAEEBa0H/D3EiAEECdCACaigCAHI2AgALIAdBCWohByABQZAGaiADQQJ0aiAINgIADAELCwJAA0AgAEEBakH/D3EhBSABQZAGaiAAQQFrQf8PcUECdGohCANAQQlBASAHQS1KGyEGAkADQCADIQJBACEEAkADQAJAIAIgBGpB/w9xIgMgAEYNACABQZAGaiADQQJ0aigCACIDIARBAnRB0KwEaigCACIJSQ0AIAMgCUsNAiAEQQFqIgRBBEcNAQsLIAdBJEcNAEIAIRVBACEEQgAhFgNAIAAgAiAEakH/D3EiA0YEQCAAQQFqQf8PcSIAQQJ0IAFqQQA2AowGCyABQYAGaiABQZAGaiADQQJ0aigCABCiAiABQfAFaiAVIBZCAEKAgICA5Zq3jsAAEDMgAUHgBWogASkD8AUgASkD+AUgASkDgAYgASkDiAYQfSABKQPoBSEWIAEpA+AFIRUgBEEBaiIEQQRHDQALIAFB0AVqIAsQhAEgAUHABWogFSAWIAEpA9AFIAEpA9gFEDMgASkDyAUhFkIAIRUgASkDwAUhFyAKQfEAaiIHIA5rIgVBACAFQQBKGyANIAUgDUgiBhsiA0HwAEwNAgwFCyAGIApqIQogACEDIAAgAkYNAAtBgJTr3AMgBnYhCUF/IAZ0QX9zIRFBACEEIAIhAwNAIAFBkAZqIAJBAnRqIhIgBCASKAIAIhIgBnZqIgQ2AgAgA0EBakH/D3EgAyAERSACIANGcSIEGyEDIAdBCWsgByAEGyEHIBEgEnEgCWwhBCACQQFqQf8PcSICIABHDQALIARFDQEgAyAFRwRAIAFBkAZqIABBAnRqIAQ2AgAgBSEADAMLIAggCCgCAEEBcjYCAAwBCwsLIAFBkAVqRAAAAAAAAPA/QeEBIANrEOoBELgBIAFBsAVqIAEpA5AFIAEpA5gFIBcgFhD+BSABKQO4BSEZIAEpA7AFIRogAUGABWpEAAAAAAAA8D9B8QAgA2sQ6gEQuAEgAUGgBWogFyAWIAEpA4AFIAEpA4gFEPwFIAFB8ARqIBcgFiABKQOgBSIVIAEpA6gFIhgQpwQgAUHgBGogGiAZIAEpA/AEIAEpA/gEEH0gASkD6AQhFiABKQPgBCEXCwJAIAJBBGpB/w9xIgQgAEYNAAJAIAFBkAZqIARBAnRqKAIAIgRB/8m17gFNBEAgBEUgAkEFakH/D3EgAEZxDQEgAUHwA2ogC7dEAAAAAAAA0D+iELgBIAFB4ANqIBUgGCABKQPwAyABKQP4AxB9IAEpA+gDIRggASkD4AMhFQwBCyAEQYDKte4BRwRAIAFB0ARqIAu3RAAAAAAAAOg/ohC4ASABQcAEaiAVIBggASkD0AQgASkD2AQQfSABKQPIBCEYIAEpA8AEIRUMAQsgC7chHCAAIAJBBWpB/w9xRgRAIAFBkARqIBxEAAAAAAAA4D+iELgBIAFBgARqIBUgGCABKQOQBCABKQOYBBB9IAEpA4gEIRggASkDgAQhFQwBCyABQbAEaiAcRAAAAAAAAOg/ohC4ASABQaAEaiAVIBggASkDsAQgASkDuAQQfSABKQOoBCEYIAEpA6AEIRULIANB7wBKDQAgAUHQA2ogFSAYQgBCgICAgICAwP8/EPwFIAEpA9ADIAEpA9gDQgBCABD/AQ0AIAFBwANqIBUgGEIAQoCAgICAgMD/PxB9IAEpA8gDIRggASkDwAMhFQsgAUGwA2ogFyAWIBUgGBB9IAFBoANqIAEpA7ADIAEpA7gDIBogGRCnBCABKQOoAyEWIAEpA6ADIRcCQEF+IBNrIAdB/////wdxTg0AIAEgFkL///////////8AgzcDmAMgASAXNwOQAyABQYADaiAXIBZCAEKAgICAgICA/z8QMyABKQOQAyABKQOYA0KAgICAgICAuMAAEPAFIQAgFiABKQOIAyAAQQBIIgIbIRYgFyABKQOAAyACGyEXQQAgFCAKIABBAE5qIgpB7gBqTiAVIBhCAEIAEP8BQQBHIAYgBiADIAVHcSACG3EbDQBBxLMEQcQANgIACyABQfACaiAXIBYgChD9BSABKQPwAiEWIAEpA/gCCzcDKCAMIBY3AyAgAUGQxgBqJAAgDCkDKCEVIAwpAyAhFgwDCyAAKQNwQgBZBEAgACAAKAIEQQFrNgIEC0HEswRBHDYCAAwBCwJAAn8gACgCBCIDIAAoAmhHBEAgACADQQFqNgIEIAMtAAAMAQsgABBcC0EoRgRAQQEhBAwBC0KAgICAgIDg//8AIRUgACkDcEIAUw0CIAAgACgCBEEBazYCBAwCCwNAAn8gACgCBCIDIAAoAmhHBEAgACADQQFqNgIEIAMtAAAMAQsgABBcCyIDQTBrQQpJIANBwQBrQRpJciADQd8ARnJFIANB4QBrQRpPcUUEQCAEQQFqIQQMAQsLQoCAgICAgOD//wAhFSADQSlGDQEgACkDcCIYQgBZBEAgACAAKAIEQQFrNgIECyAERQ0BA0AgBEEBayEEIBhCAFkEQCAAIAAoAgRBAWs2AgQLIAQNAAsMAQsgABCrBAsgECAWNwMAIBAgFTcDCCAMQTBqJAAgECkDACEVIA8gECkDCDcDCCAPIBU3AwAgEEGgAWokACAPKQMAIA8pAwgQ7QUhHCAPQRBqJAAgHAv8AwIEfwF+AkACQAJ/AkACQAJ/IAAoAgQiASAAKAJoRwRAIAAgAUEBajYCBCABLQAADAELIAAQXAsiAUEraw4DAAEAAQsgAUEtRgJ/IAAoAgQiASAAKAJoRwRAIAAgAUEBajYCBCABLQAADAELIAAQXAsiAUE6ayICQXVLDQEaIAApA3BCAFMNAiAAIAAoAgRBAWs2AgQMAgsgAUE6ayECQQALIQQgAkF2SQ0AIAFBMGsiAkEKSQRAA0AgASADQQpsakEwayIDQcyZs+YASAJ/IAAoAgQiASAAKAJoRwRAIAAgAUEBajYCBCABLQAADAELIAAQXAsiAUEwayICQQlNcQ0ACyADrCEFCwJAIAJBCk8NAANAIAGtIAVCCn58QjB9IQUCfyAAKAIEIgEgACgCaEcEQCAAIAFBAWo2AgQgAS0AAAwBCyAAEFwLIgFBMGsiAkEJSw0BIAVCro+F18fC66MBUw0ACwsgAkEKSQRAA0ACfyAAKAIEIgEgACgCaEcEQCAAIAFBAWo2AgQgAS0AAAwBCyAAEFwLQTBrQQpJDQALCyAAKQNwQgBZBEAgACAAKAIEQQFrNgIEC0IAIAV9IAUgBBshBQwBC0KAgICAgICAgIB/IQUgACkDcEIAUw0AIAAgACgCBEEBazYCBEKAgICAgICAgIB/DwsgBQvQBgIEfwN+IwBBgAFrIgUkAAJAAkACQCADIARCAEIAEP8BRQ0AAn8gBEL///////8/gyEJAn8gBEIwiKdB//8BcSIGQf//AUcEQEEEIAYNARpBAkEDIAMgCYRQGwwCCyADIAmEUAsLIQcgAkIwiKciCEH//wFxIgZB//8BRg0AIAcNAQsgBUEQaiABIAIgAyAEEDMgBSAFKQMQIgEgBSkDGCICIAEgAhDvBSAFKQMIIQIgBSkDACEEDAELIAEgAkL///////8/gyAGrUIwhoQiCiADIARC////////P4MgBEIwiKdB//8BcSIHrUIwhoQiCRD/AUEATARAIAEgCiADIAkQ/wEEQCABIQQMAgsgBUHwAGogASACQgBCABAzIAUpA3ghAiAFKQNwIQQMAQsgBgR+IAEFIAVB4ABqIAEgCkIAQoCAgICAgMC7wAAQMyAFKQNoIgpCMIinQfgAayEGIAUpA2ALIQQgB0UEQCAFQdAAaiADIAlCAEKAgICAgIDAu8AAEDMgBSkDWCIJQjCIp0H4AGshByAFKQNQIQMLIAlC////////P4NCgICAgICAwACEIQkgCkL///////8/g0KAgICAgIDAAIQhCiAGIAdKBEADQAJ+IAogCX0gAyAEVq19IgtCAFkEQCALIAQgA30iBIRQBEAgBUEgaiABIAJCAEIAEDMgBSkDKCECIAUpAyAhBAwFCyALQgGGIARCP4iEDAELIApCAYYgBEI/iIQLIQogBEIBhiEEIAZBAWsiBiAHSg0ACyAHIQYLAkAgCiAJfSADIARWrX0iCUIAUwRAIAohCQwBCyAJIAQgA30iBIRCAFINACAFQTBqIAEgAkIAQgAQMyAFKQM4IQIgBSkDMCEEDAELIAlC////////P1gEQANAIARCP4ghASAGQQFrIQYgBEIBhiEEIAEgCUIBhoQiCUKAgICAgIDAAFQNAAsLIAhBgIACcSEHIAZBAEwEQCAFQUBrIAQgCUL///////8/gyAGQfgAaiAHcq1CMIaEQgBCgICAgICAwMM/EDMgBSkDSCECIAUpA0AhBAwBCyAJQv///////z+DIAYgB3KtQjCGhCECCyAAIAQ3AwAgACACNwMIIAVBgAFqJAALvwIBAX8jAEHQAGsiBCQAAkAgA0GAgAFOBEAgBEEgaiABIAJCAEKAgICAgICA//8AEDMgBCkDKCECIAQpAyAhASADQf//AUkEQCADQf//AGshAwwCCyAEQRBqIAEgAkIAQoCAgICAgID//wAQMyADQf3/AiADQf3/AkgbQf7/AWshAyAEKQMYIQIgBCkDECEBDAELIANBgYB/Sg0AIARBQGsgASACQgBCgICAgICAgDkQMyAEKQNIIQIgBCkDQCEBIANB9IB+SwRAIANBjf8AaiEDDAELIARBMGogASACQgBCgICAgICAgDkQMyADQeiBfSADQeiBfUobQZr+AWohAyAEKQM4IQIgBCkDMCEBCyAEIAEgAkIAIANB//8Aaq1CMIYQMyAAIAQpAwg3AwggACAEKQMANwMAIARB0ABqJAALNQAgACABNwMAIAAgAkL///////8/gyAEQjCIp0GAgAJxIAJCMIinQf//AXFyrUIwhoQ3AwgLMQECfwJ/IAAQQ0EBaiEBA0BBACABRQ0BGiAAIAFBAWsiAWoiAi0AAEEvRw0ACyACCwsXAQF/IABBACABEKUCIgIgAGsgASACGwvRAQEBfwJAAkAgACABc0EDcQRAIAEtAAAhAgwBCyABQQNxBEADQCAAIAEtAAAiAjoAACACRQ0DIABBAWohACABQQFqIgFBA3ENAAsLIAEoAgAiAkF/cyACQYGChAhrcUGAgYKEeHENAANAIAAgAjYCACABKAIEIQIgAEEEaiEAIAFBBGohASACQYGChAhrIAJBf3NxQYCBgoR4cUUNAAsLIAAgAjoAACACQf8BcUUNAANAIAAgAS0AASICOgABIABBAWohACABQQFqIQEgAg0ACwsLwg8DB3wIfwJ+RAAAAAAAAPA/IQMCQAJAAkAgAb0iEUIgiKciDUH/////B3EiCSARpyIMckUNACAAvSISQiCIpyEPIBKnIhBFIA9BgIDA/wNGcQ0AIA9B/////wdxIgpBgIDA/wdLIApBgIDA/wdGIBBBAEdxciAJQYCAwP8HS3JFIAxFIAlBgIDA/wdHcnFFBEAgACABoA8LAkACfwJAAn9BACASQgBZDQAaQQIgCUH///+ZBEsNABpBACAJQYCAwP8DSQ0AGiAJQRR2IQ4gCUGAgICKBEkNAUEAIAxBswggDmsiC3YiDiALdCAMRw0AGkECIA5BAXFrCyILIAxFDQEaDAILIAwNAUEAIAlBkwggDmsiC3YiDCALdCAJRw0AGkECIAxBAXFrCyELIAlBgIDA/wdGBEAgCkGAgMD/A2sgEHJFDQIgCkGAgMD/A08EQCABRAAAAAAAAAAAIBFCAFkbDwtEAAAAAAAAAAAgAZogEUIAWRsPCyAJQYCAwP8DRgRAIBFCAFkEQCAADwtEAAAAAAAA8D8gAKMPCyANQYCAgIAERgRAIAAgAKIPCyANQYCAgP8DRyASQgBTcg0AIACfDwsgAJkhAiAPQf////8DcUGAgMD/A0dBACAKGyAQckUEQEQAAAAAAADwPyACoyACIBFCAFMbIQMgEkIAWQ0BIAsgCkGAgMD/A2tyRQRAIAMgA6EiACAAow8LIAOaIAMgC0EBRhsPCwJAIBJCAFkNAAJAAkAgCw4CAAECCyAAIAChIgAgAKMPC0QAAAAAAADwvyEDCwJ8IAlBgYCAjwRPBEAgCUGBgMCfBE8EQCAKQf//v/8DTQRARAAAAAAAAPB/RAAAAAAAAAAAIBFCAFMbDwtEAAAAAAAA8H9EAAAAAAAAAAAgDUEAShsPCyAKQf7/v/8DTQRAIANEnHUAiDzkN36iRJx1AIg85Dd+oiADRFnz+MIfbqUBokRZ8/jCH26lAaIgEUIAUxsPCyAKQYGAwP8DTwRAIANEnHUAiDzkN36iRJx1AIg85Dd+oiADRFnz+MIfbqUBokRZ8/jCH26lAaIgDUEAShsPCyACRAAAAAAAAPC/oCIARETfXfgLrlQ+oiAAIACiRAAAAAAAAOA/IAAgAEQAAAAAAADQv6JEVVVVVVVV1T+goqGiRP6CK2VHFfe/oqAiAiACIABEAAAAYEcV9z+iIgKgvUKAgICAcIO/IgAgAqGhDAELIAJEAAAAAAAAQEOiIgAgAiAKQYCAwABJIgkbIQIgAL1CIIinIAogCRsiDEH//z9xIgpBgIDA/wNyIQsgDEEUdUHMd0GBeCAJG2ohDEEAIQkCQCAKQY+xDkkNACAKQfrsLkkEQEEBIQkMAQsgCkGAgID/A3IhCyAMQQFqIQwLIAlBA3QiCkGwrARqKwMAIAK9Qv////8PgyALrUIghoS/IgQgCkGgrARqKwMAIgWhIgZEAAAAAAAA8D8gBSAEoKMiB6IiAr1CgICAgHCDvyIAIAAgAKIiCEQAAAAAAAAIQKAgByAGIAAgCUESdCALQQF2akGAgKCAAmqtQiCGvyIGoqEgACAEIAYgBaGhoqGiIgQgAiAAoKIgAiACoiIAIACiIAAgACAAIAAgAETvTkVKKH7KP6JEZdvJk0qGzT+gokQBQR2pYHTRP6CiRE0mj1FVVdU/oKJE/6tv27Zt2z+gokQDMzMzMzPjP6CioCIFoL1CgICAgHCDvyIAoiIGIAQgAKIgAiAFIABEAAAAAAAACMCgIAihoaKgIgKgvUKAgICAcIO/IgBE9QFbFOAvPr6iIAIgACAGoaFE/QM63AnH7j+ioKAiAiAKQcCsBGorAwAiBCACIABEAAAA4AnH7j+iIgKgoCAMtyIFoL1CgICAgHCDvyIAIAWhIAShIAKhoQshAiABIBFCgICAgHCDvyIEoSAAoiACIAGioCICIAAgBKIiAaAiAL0iEachCQJAIBFCIIinIgpBgIDAhAROBEAgCkGAgMCEBGsgCXINAyACRP6CK2VHFZc8oCAAIAGhZEUNAQwDCyAKQYD4//8HcUGAmMOEBEkNACAKQYDovPsDaiAJcg0DIAIgACABoWVFDQAMAwtBACEJIAMCfCAKQf////8HcSILQYGAgP8DTwR+QQBBgIDAACALQRR2Qf4Ha3YgCmoiCkH//z9xQYCAwAByQZMIIApBFHZB/w9xIgtrdiIJayAJIBFCAFMbIQkgAiABQYCAQCALQf8Ha3UgCnGtQiCGv6EiAaC9BSARC0KAgICAcIO/IgBEAAAAAEMu5j+iIgMgAiAAIAGhoUTvOfr+Qi7mP6IgAEQ5bKgMYVwgvqKgIgKgIgAgACAAIAAgAKIiASABIAEgASABRNCkvnJpN2Y+okTxa9LFQb27vqCiRCzeJa9qVhE/oKJEk72+FmzBZr+gokQ+VVVVVVXFP6CioSIBoiABRAAAAAAAAADAoKMgACACIAAgA6GhIgCiIACgoaFEAAAAAAAA8D+gIgC9IhFCIIinIAlBFHRqIgpB//8/TARAIAAgCRDqAQwBCyARQv////8PgyAKrUIghoS/C6IhAwsgAw8LIANEnHUAiDzkN36iRJx1AIg85Dd+og8LIANEWfP4wh9upQGiRFnz+MIfbqUBogsQACAAQSBGIABBCWtBBUlyC0UBAnwgACACIAKiIgQ5AwAgASACIAJEAAAAAgAAoEGiIgMgAiADoaAiAqEiAyADoiACIAKgIAOiIAIgAqIgBKGgoDkDAAszACABAn8gAigCTEEASARAIAAgASACEK0EDAELIAAgASACEK0ECyIARgRADwsgACABbhoLfQECfyMAQRBrIgEkACABQQo6AA8CQAJAIAAoAhAiAgR/IAIFIAAQrgQNAiAAKAIQCyAAKAIUIgJGDQAgACgCUEEKRg0AIAAgAkEBajYCFCACQQo6AAAMAQsgACABQQ9qQQEgACgCJBEBAEEBRw0AIAEtAA8aCyABQRBqJAALiQQCBH4CfwJAIAG9IgRCAYYiA1AgBEL///////////8Ag0KAgICAgICA+P8AVnJFBEAgAL0iBUI0iKdB/w9xIgZB/w9HDQELIAAgAaIiACAAow8LIAMgBUIBhiICWgRAIABEAAAAAAAAAACiIAAgAiADURsPCyAEQjSIp0H/D3EhBwJ+IAZFBEBBACEGIAVCDIYiAkIAWQRAA0AgBkEBayEGIAJCAYYiAkIAWQ0ACwsgBUEBIAZrrYYMAQsgBUL/////////B4NCgICAgICAgAiECyECAn4gB0UEQEEAIQcgBEIMhiIDQgBZBEADQCAHQQFrIQcgA0IBhiIDQgBZDQALCyAEQQEgB2uthgwBCyAEQv////////8Hg0KAgICAgICACIQLIQQgBiAHSgRAA0ACQCACIAR9IgNCAFMNACADIgJCAFINACAARAAAAAAAAAAAog8LIAJCAYYhAiAGQQFrIgYgB0oNAAsgByEGCwJAIAIgBH0iA0IAUw0AIAMiAkIAUg0AIABEAAAAAAAAAACiDwsCQCACQv////////8HVgRAIAIhAwwBCwNAIAZBAWshBiACQoCAgICAgIAEVCEHIAJCAYYiAyECIAcNAAsLIAVCgICAgICAgICAf4MhAiAGQQBKBH4gA0KAgICAgICACH0gBq1CNIaEBSADQQEgBmutiAsgAoS/C9oBAQR/IAAoAlQhAwJAIAAoAhQiBiAAKAIcIgVHBEAgACAFNgIUIAAgBSAGIAVrIgUQiAYgBUkNAQsCQCADKAIQQeEARwRAIAMoAgAhBAwBCyADIAMoAgQiBDYCAAsgAygCDCAEaiABIAMoAgggBGsiASACIAEgAkkbIgQQJRogAyADKAIAIARqIgE2AgAgASADKAIETQ0AIAMgATYCBAJ/IAMoAggiAiABSwRAIAMoAgwgAWoMAQsgAC0AAEEEcUUgAkVyDQEgAiADKAIMakEBawtBADoAAAsgBAufAQECfgJAIAMpAwAiBEKAgICAcFoEQCADKQMIIgVC/////29WDQELIAAQKUKAgICA4AAPCyAAQoCAgIAgQSkQUyIBEA1FBEAgAEEYEC8iAkUEQCAAIAEQDEKAgICA4AAPCyACIAQQDyIENwMAIAIgBRAPNwMIIAAgBBA7IQAgAkEAOgARIAIgADoAECABIAIQjQEgASAEELUBELIDCyABCxgBAX8jAEEQayIBIAA5AwggASsDCCAAogsoACABRAAAAAAAAMB/oiAARIvdGhVmIJbAoBCvBKJEAAAAAAAAwH+iCyMBAX8gASAAKAJASQR/IAAoAkQgAUEYbGooAgBBAEcFQQALC64CAwF8AX4BfyAAvSICQiCIp0H/////B3EiA0GAgMD/A08EQCACpyADQYCAwP8Da3JFBEBEAAAAAAAAAABEGC1EVPshCUAgAkIAWRsPC0QAAAAAAAAAACAAIAChow8LAnwgA0H////+A00EQEQYLURU+yH5PyADQYGAgOMDSQ0BGkQHXBQzJqaRPCAAIAAgAKIQpwKioSAAoUQYLURU+yH5P6APCyACQgBTBEBEGC1EVPsh+T8gAEQAAAAAAADwP6BEAAAAAAAA4D+iIgCfIgEgASAAEKcCokQHXBQzJqaRvKCgoSIAIACgDwtEAAAAAAAA8D8gAKFEAAAAAAAA4D+iIgCfIgEgABCnAqIgACABvUKAgICAcIO/IgAgAKKhIAEgAKCjoCAAoCIAIACgCwvpAgEFfiADKQMIIQggACADKQMAIgUQgwQiA0EATgRAAkAgARASRQ0AIAAQggQhASADRQ0AIAgQEkUNACAAIAVBPCAFQQAQFCIGEA0EQCAGDwsgACAGIAEQWiECIAAgBhAMIAJFDQAgBRAPDwsCQAJAAkACQCAAIAVBABDdASICBEAgAjUCAEKAgICAkH+EEA8hBCAIEBJFDQEgAjUCBEKAgICAkH+EEA8hBgwDCwJAAkAgAwRAQoCAgIAwIQcgACAFQewAIAVBABAUIgQQDQ0GIAgQEkUNASAAIAVB7QAgBUEAEBQiBxANRQ0CDAYLIAUQDyEECyAIEA8hBwsgBBASBEAgAEEvEDIhBAwCCyAAIAQQLiEGIAAgBBAMIAYiBBANDQMMAQsgACAIEC4iBxANDQILIAAgBCAHEIQEIgYQDQ0BIAAgBxAMCyAAIAEgBCAGEMsFDwsgACAEEAwgACAHEAwLQoCAgIDgAAvSDQIIfwF+IwBB0ABrIgkkACAAIAkgAiADIAQQtAUjAEEQayIDJAACQCAJKAI4IgItAABBI0cNACACLQABQSFHDQAgAyACQQJqIgI2AgwDQAJAAkACQCACIAkoAjxPDQACQCACLQAAIgdBCmsOBAEAAAEACyAHQRh0QRh1QQBODQIgAkEGIANBDGoQYSIHQX5xQajAAEcNASADKAIMIQILIAkgAjYCOAwDCyADKAIMIQIgB0F/Rw0BCyADIAJBAWoiAjYCDAwACwALIANBEGokAAJAAkACQAJAAkACQAJAAkAgBUEDcSIHQQJGBEAgACgCECgCjAEiDEUNAiAMKQMIIg9C/////29YDQMgD6ciAi8BBhD4AUUNBCACKAIkIQ0gAigCICIDLQAQIQhBACECDAELIAVBA3YhCCAHQQFHBEAgCEEDcSEIQQAhA0EAIQIMAQtCgICAgOAAIQ8gACAEEMoBIgJFDQcCfyAAQfAAEGwiA0UEQCAAIAIQEyADDAELIANCgICAgDA3A2ggA0KAgICAMDcDYCADQoCAgIAwNwNIIANCgICAgDA3A0AgAyACNgIEIANBATYCACADQQhqIABB4AFqEEwgAwsiAkUNByAIQQJxQQFyIQhBACEDCyAAQQBBAUEAIARBARD3AyIERQ0DIAkgBDYCQCAEIAdBAkciCzYCTCAEIAc2AiQgBCAFQQZ2QQFxNgJoAn8gC0UEQCAEIAMvABFBBnZBAXE2AlAgBCADLwARQQd2QQFxNgJUIAQgAy0AEkEBcTYCWCADLwARQQl2QQFxDAELIARBADYCWCAEQgA3AlBBAQshByAEIAg6AG4gBCAHNgJcIABB0AAQGRogBEHQADYCcAJAAkAgAwRAQQAhCyADKAI8IQcgAy8BKiEIIAMvASghCiAEQQA2AsACIARBADYCyAIgBCAHIAggCmpqIgc2AsQCAkAgB0UNACAEIAAgB0EDdBAvIgc2AsgCIAdFBEBBfyELDAELA0AgBkEATgRAIAMoAiAgBiADLwEoakEEdGoiBygCBEEASgRAIAQgBCgCwAIiCEEBajYCwAIgACAEKALIAiAIQQN0aiAHIAYQ2gMLIAcoAgghBgwBCwtBACEHAkAgBkF+RgRAA0AgByADLwEqTw0CAkAgAygCICAHIAMvAShqQQR0aiIGKAIEDQAgBhD6BEUNACAEIAQoAsACIghBAWo2AsACIAAgBCgCyAIgCEEDdGogBiAHENoDCyAHQQFqIQcMAAsACwNAIAMvASggB00EQEEAIQcDQCAHIAMvASpPDQMCQCADKAIgIAcgAy8BKGpBBHRqIgYoAgQNACAGKAIAQdEARg0AIAQgBCgCwAIiCEEBajYCwAIgACAEKALIAiAIQQN0aiAGIAcQ2gMLIAdBAWohBwwACwAFIAQgBCgCwAIiBkEBajYCwAIgAygCICEIIAQoAsgCIAZBA3RqIgYgBzsBAiAGQQM6AAAgBiAAIAggB0EEdGooAgAQGTYCBCAHQQFqIQcMAQsACwALQQAhBgNAIAYgAygCPE4NASADKAIkIQggBCAEKALAAiIHQQFqNgLAAiAEKALIAiAHQQN0aiIHIActAAAiCkH+AXE6AAAgByAIIAZBA3RqIggtAABBAnEgCkH8AXFyIgo6AAAgByAKQfoBcSAILQAAQQRxciIKOgAAIAcgCkH2AXEgCC0AAEEIcXIiCjoAACAILQAAIQ4gByAGOwECIAcgCkEOcSAOQfABcXI6AAAgByAAIAgoAgQQGTYCBCAGQQFqIQYMAAsACyALDQELIAQgAjYClAMgCSACRTYCSCAJIAJBAEc2AkQgCRCFARogBCAEKAK8ATYC8AEgCSgCQCEDQX8hBgJAIAkQEQ0AIAkQ+QQNACADIAMoAiRBAk8EfyADLQBuQX9zQQFxBUEBCzYCKCAJKAJERQRAIAMgCSgCACADQdEAEFgiBzYCpAEgB0EASA0BCwNAIAkoAhBBqn9HBEAgCRD4BEUNAQwCCwsgCSAJKAJEBH9BKQUgCUHYABAOIAkgAy8BpAEQGEEoCxAOQQAhBgsgBkUNAQsgCSAJQRBqEI8CIAAgBBCNAwwECyAAIAQQqAUiDxANDQMgAgRAIAIgDzcDSCAAIAIQhgRBAEgNBSACrUKAgICAUIQQDyEPCyAFQSBxDQYgACAPIAEgDSAMELsFIQ8MBgtB7uoAQb7jAEG9hgJB9z8QAAALQdDoAEG+4wBBvoYCQfc/EAAAC0HN9wBBvuMAQcCGAkH3PxAAAAsgAkUNAQsgACACEOkFC0KAgICA4AAhDwsgCUHQAGokACAPC8QEAwJ+Bn8BfCMAQdAAayIGJAACQCAGAnwCQAJAAkACQAJAQQAgAiABEBIiChsiAg4CAAECCxCrBbkMBAsCQCADKQMAIgRCgICAgHBUDQAgBKciAi8BBkEKRw0AIAIpAyAiBRCQAUUNACAAIAZBQGsgBRBHDQIMAwsgBiAAIARBAhCbAyIENwMAIAQQngEEQCAAQoCAgIAwQQEgBhCqBSEFIAAgBBAMIAUQDQ0CIAAgBkFAayAFEFtFDQMMAgsgACAGQUBrIAQQW0UNAgwBCyAGQQBBOBBLIgdCgICAgICAgPg/NwMQIAJBByACQQdIGyIJQQAgCUEAShshAgNAAkBEAAAAAAAA+H8gAiAIRwR/IAAgB0HIAGogAyAIQQN0IgtqKQMAEEcNAyAHKwNIIgy9QoCAgICAgID4/wCDQoCAgICAgID4/wBSDQEgCAUgAgsgCUcNBBogB0EBEPkDDAQLIAcgC2ogDJ05AwACQCAIDQAgBysDACIMRAAAAAAAAAAAZkUgDEQAAAAAAABZQGNFcg0AIAcgDEQAAAAAALCdQKA5AwALIAhBAWohCAwACwALQoCAgIDgACEBDAILIAYrA0AQ+AMLIgw5A0ACQCAAIAFBChBvIgQQDUUEQCAAIAQCfiAMvQJ/IAyZRAAAAAAAAOBBYwRAIAyqDAELQYCAgIB4CyICt71RBEAgAq0MAQsgDBAXCxDPASAKDQELIAQhAQwBCyAAIARBAEEAQRMQqQUhASAAIAQQDAsgBkHQAGokACABCxYAIAAgACkDwAEgAykDAEEDQX8QmQMLOwEBfwNAIAIEQCAALQAAIQMgACABLQAAOgAAIAEgAzoAACABQQFqIQEgAEEBaiEAIAJBAWshAgwBCwsLGgAgAC0AACECIAAgAS0AADoAACABIAI6AAALQgEBfyACQQF2IQIDQCACBEAgAC8BACEDIAAgAS8BADsBACABIAM7AQAgAUECaiEBIABBAmohACACQQFrIQIMAQsLCxoAIAAvAQAhAiAAIAEvAQA7AQAgASACOwEAC0IBAX8gAkECdiECA0AgAgRAIAAoAgAhAyAAIAEoAgA2AgAgASADNgIAIAFBBGohASAAQQRqIQAgAkEBayECDAELCwsaACAAKAIAIQIgACABKAIANgIAIAEgAjYCAAtCAQF+IAJBA3YhAgNAIAIEQCAAKQMAIQMgACABKQMANwMAIAEgAzcDACABQQhqIQEgAEEIaiEAIAJBAWshAgwBCwsLHAEBfiAAKQMAIQMgACABKQMANwMAIAEgAzcDAAtaAQJ+IAJBBHYhAgNAIAIEQCAAKQMAIQMgACABKQMANwMAIAApAwghBCAAIAEpAwg3AwggASAENwMIIAEgAzcDACABQRBqIQEgAEEQaiEAIAJBAWshAgwBCwsLNAECfiAAKQMAIQMgACABKQMANwMAIAApAwghBCAAIAEpAwg3AwggASAENwMIIAEgAzcDAAucAwIDfwJ+IwBBIGsiBSQAQoCAgIDgACEIAkAgACABQR4QaiIHRQ0AIAAgBUEQaiADKQMAEMQBDQAgAykDCCEBIAVBADYCHAJ+AkAgBEEbTARAIAAgBUEcaiABEMcBDQMMAQsgACAFQQhqIAEQRw0CIARBHEYEQCAFIAUrAwi2OAIcDAELIAUpAwgMAQtCAAshAUEBIQYgAkEDTgRAIAAgAykDEBD5AUEBcyEGCyAHKAIMKAIgIgItAAQEQCAAEHUMAQsgBzUCFCAFKQMQIglBASAEQeWKAWotAAB0rHxUBEAgAEGo2gAQawwBCyAJpyACKAIIIAcoAhBqaiEAAkACQAJAAkACQAJAIARBFmsOCAQEAAABAQECAwsgBSgCHCEDIAYEQCAFIANB//8DcRDlAyIDNgIcCyAAIANB//8DcRCGAwwECyAFKAIcIQMgBgRAIAUgAxCFAyIDNgIcCyAAIAMQXQwDCyAAIAYEfiABEJIFBSABCzcAAAwCCxABAAsgACAFKAIcOgAAC0KAgICAMCEICyAFQSBqJAAgCAulAwIBfgN/IwBBEGsiByQAQoCAgIDgACEFAkAgACABQR4QaiIIRQ0AIAAgB0EIaiADKQMAEMQBDQBBASEGIAJBAk4EQCAAIAMpAwgQ+QFBAXMhBgsgCCgCDCgCICICLQAEBEAgABB1DAELIAg1AhQgBykDCCIBQQEgBEHligFqLQAAdKx8VARAIABBqNoAEGsMAQsgAacgAigCCCAIKAIQamohAAJAAkACQAJAAkACQAJAAkACQCAEQRZrDggIAAECAwQFBgcLIAAxAAAhBQwICyAALwAAIQAgBgR/IAAQ5QMFIAALQRB0QRB1rSEFDAcLIAAvAAAhACAGBH8gABDlAwUgAAutIQUMBgsgACgAACEAIAYEfyAAEIUDBSAAC60hBQwFCyAAKAAAIQAgBgRAIAAQhQMhAAsgAEEATgRAIACtIQUMBQsgALgQFyEFDAQLIAAoAAAhACAGBH8gABCFAwUgAAu+uxAXIQUMAwsgACkAACEBIAYEfiABEJIFBSABC78QFyEFDAILEAEACyAAMAAAQv////8PgyEFCyAHQRBqJAAgBQtVAQF/IAEQEkUEQCAAQd8pQQAQFkKAgICA4AAPCwJ+AkAgAkUNACADKQMAIgEQEg0AQoCAgIDgACAAIAEQLiIBEA0NARogAachBAsgACAEQQMQ9QMLC4ABAQN/IwBBEGsiBSQAIAUgAq03AwgCQCAAIAFBASAFQQhqEMUDIgEQDQ0AIAJBACACQQBKGyECA0AgAiAERg0BIAAgASAEIAMgBEEDdGopAwAQDxCWAiEGIARBAWohBCAGQQBODQALIAAgARAMQoCAgIDgACEBCyAFQRBqJAAgAQuBBQICfwl+IwBBMGsiBCQAIAMpAwAhBkKAgICAMCEJIARCgICAgDA3AxhBASEFAkACQAJAAkACfiACQQJIBEBCgICAgDAhDEKAgICAMAwBCwJAIAMpAwgiDBASDQAgACAMEGkNAkEAIQUgAkEDSQ0AIAMpAxAMAQtCgICAgDALIQ0gACAGQcMBIAZBABAUIggQDQ0AAkAgCBASRQRAIAAgCBAMIAAQUSIKEA0EQEKAgICAMCELQoCAgIAwIQgMBAsgBCAGEA83AxAgACAEQRBqQQhyQQAQlwMhAiAEKQMYIQsgBCkDECEIIAINAwNAIAAgCCALIARBBGoQrwEiBhANRQRAIAQoAgQNAyAAIAogByAGEHAhAiAHQgF8IQcgAkEATg0BCwsgCBASDQQgACAIQQEQswEaDAMLQoCAgIAwIQtCgICAgDAhCCAAIAYQKyIKEA0NAwsgACAEQQhqIAoQQUEASA0BIAQCfiAEKQMIIgZCgICAgAh8Qv////8PWARAIAZC/////w+DDAELIAa5EBcLIgc3AyAgACABQQEgBEEgahDFAyEJIAAgBxAMAkAgCRANDQBCACEHIAZCACAGQgBVGyEOA0AgByAOUQ0FIAAgCiAHEGQiBhANDQECQCAFBEAgBiEBDAELIAQgBjcDICAEIAdC/////w+DNwMoIAAgDCANQQIgBEEgahAkIQEgACAGEAwgARANDQILIAAgCSAHIAEQkQEhAiAHQgF8IQcgAkEATg0ACwsMAgtCgICAgDAhC0KAgICAMCEIQoCAgIAwIQoLCyAAIAkQDEKAgICA4AAhCQsgACAKEAwgACAIEAwgACALEAwgBEEwaiQAIAkLDwAgACsDACABKwMAEMgECwkAIAErAwAQFwsRACAAKgIAuyABKgIAuxDIBAsKACABKgIAuxAXCxcAIAEoAgAiASAAKAIAIgBJIAAgAUlrCxgAIAEoAgAiAEEATgRAIACtDwsgALgQFwsXACABKAIAIgEgACgCACIASCAAIAFIawsHACABNQIACw0AIAAvAQAgAS8BAGsLBwAgATMBAAsNACAALgEAIAEuAQBrCw4AIAEyAQBC/////w+DCw0AIAAtAAAgAS0AAGsLBwAgATEAAAsNACAALAAAIAEsAABrCw4AIAEwAABC/////w+DC9UJBAR/AXwBfgF9IwBBEGsiBiQAQoCAgIDgACEKAkAgACABEJgBIghBAEgNAEF/IQUCQAJAAkAgCEUNAEEBIQcCQAJAIARBAUYEQEF/IQcgBiAIQQFrNgIMIAJBAkgNASAAIAYgAykDCBBHDQYgBisDACIJvUL///////////8Ag0KBgICAgICA+P8AWgRAIAZBADYCDAwCCyAJRAAAAAAAAAAAZgRAIAkgBigCDLdjRQ0CIAYCfyAJmUQAAAAAAADgQWMEQCAJqgwBC0GAgICAeAs2AgwMAgsgCSAIt6AiCUQAAAAAAAAAAGMNBCAGAn8gCZlEAAAAAAAA4EFjBEAgCaoMAQtBgICAgHgLNgIMDAELIAZBADYCDCACQQJIBEAgCCECDAILIAAgBkEMaiADKQMIIAgiAiACEGUNBQwBC0F/IQILIAGnIgAQmgEEQCAEQX9HDQJBAEF/IAMpAwAQEhshBQwDCwJ/IAMpAwAiARBWIgNBB0cEQCADDQIgBiABQiCGQiCHIgq5Igk5AwBBAQwBCyAGIAEQSSIJOQMAIAkCfiAJmUQAAAAAAADgQ2MEQCAJsAwBC0KAgICAgICAgIB/CyIKuWELIQMCQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAC8BBkEVaw4JAQABAwQGBwkKDAsgA0UNCyAKQoABfEKAAlQNAQwLCyADRSAKQv8BVnINCgsgACgCJCEAIAqnIQMgBEEBRgRAIANB//8DcSEDIAYoAgwhBQNAIAIgBUYNCiADIAAgBWotAABGDQsgBiAFIAdqIgU2AgwMAAsACyAAIAYoAgwiAmogA0H//wNxIAggAmsQpQIiAkUNCSACIABrIQUMCQsgA0UNCCAKQoCAAnxCgIAEVA0BDAgLIANFIApC//8DVnINBwsgACgCJCEAIAYoAgwhBSAKp0H//wNxIQMDQCACIAVGDQYgACAFQQF0ai8BACADRg0HIAYgBSAHaiIFNgIMDAALAAsgA0UNBSAKQoCAgIAIfEKAgICAEFQNAQwFCyADRSAKQv////8PVnINBAsgACgCJCEAIAqnIQMgBigCDCEFA0AgAiAFRg0DIAAgBUECdGooAgAgA0YNBCAGIAUgB2oiBTYCDAwACwALIAm9Qv///////////wCDQoGAgICAgID4/wBaBEAgBEF/Rw0EIAAoAiQhACAGKAIMIQUDQCACIAVGDQMgACAFQQJ0aigCAEH/////B3FBgICA/AdLDQQgBiAFIAdqIgU2AgwMAAsACyAJIAm2Igu7Yg0CIAAoAiQhACAGKAIMIQUDQCACIAVGDQIgACAFQQJ0aioCACALWw0DIAYgBSAHaiIFNgIMDAALAAsgACgCJCEAIAm9Qv///////////wCDQoGAgICAgID4/wBaBEAgBEF/Rw0DIAYoAgwhBQNAIAIgBUYNAiAAIAVBA3RqKQMAQv///////////wCDQoCAgICAgID4/wBWDQMgBiAFIAdqIgU2AgwMAAsACyAGKAIMIQUDQCACIAVGDQEgACAFQQN0aisDACAJYQ0CIAYgBSAHaiIFNgIMDAALAAtBfyEFCyAEQX9GDQELIAWtIQoMAQsgBUF/c0Efdq1CgICAgBCEIQoLIAZBEGokACAKC0ABAX4gACADKQMAEPkBQQBHrUKAgICAEIQhBCABEBIEQCAEDwsgACABQQYQbyIBEA1FBEAgACABIAQQzwELIAEL5CYDDn8MfgJ8IwBB0AFrIgckAEGwswQoAgAEQAJ/QYAIEKMCIgwhAEHxEEErELADIQECQAJAQcHkAEHxECwAABCwA0UEQEHEswRBHDYCAAwBCyAAQQFyRQRAQcSzBEEwNgIADAELQbAJQbARIAAbEKMCIgINAQtBAAwBCyACQQBBpAEQSxogAkF/NgJQIAJBfzYCPCACIAJBkAFqNgJUIAJBgAg2AjAgAiACQawBajYCLCAARQRAIAJBrAlqIgBBAEGACBBLGgsgAkGACDYCmAEgAiAANgKcASACQfEQLAAANgKgASABRQRAIAJBCEEEQfEQLQAAQfIARhs2AgALAkACQEHxEC0AACIEQeEARwRAIARB8gBHDQEgAkGACDYClAEMAgsgAiAAQYAIEIAGIgA2ApQBIAIgADYCkAEMAQsgAUUNACAAQQA6AAALIAJB7gI2AiggAkHvAjYCJCACQfACNgIgIAJB8QI2AgxB3bMELQAARQRAIAJBfzYCTAsgAkGYtAQoAgA2AjhBmLQEKAIAIgAEQCAAIAI2AjQLQZi0BCACNgIAIAILIQJBsLMEKAIAIQgjAEFAaiIAJAAgAEEAQcAAEEshBSAHQQBB0AEQSyIAIAg1AhA3AxggACAINQIUNwMAIAg1AhghDiAAQgI3AyAgACAONwMIIAAgCCgCQEEDdEHgAWqtNwMQIAhBzABqIQEgCEHIAGohCgNAIAogASgCACIDRwRAIAMoAhAhASAAIAApAyBCAnw3AyAgACAAKQMQIAgoAkBBA3RB+AFqrXw3AxAgACAAKQPAASADMwEIfDcDwAEgACAAKQPIASADNAIMfDcDyAEgA0EUayEEAkAgAUUNACABLQAQDQAgASgCGCEGIAAgACkDaEIBfDcDaCAAIAApA3AgBkEBaiABKAIcEOUBrXw3A3ALIARB4AFqIgshBgNAIAsgBigCBCIGRwRAIAAgACkDICIQQgF8Ig83AyAgACAAKQMQQvAAfCIONwMQIAYoAggEQCAAIBBCAnwiDzcDICAAIA4gBigCDEEDdK18Ig43AxALAkAgBigCFEUNACAAIA9CAXw3AyAgACAOIAYoAhgiBEEUbK18NwMQQQAhAQNAIAEgBE4NAQJAIAYoAhQgAUEUbGoiCSgCCA0AIAkoAgRFDQAgACAAKQMgQgF8NwMgIAkoAgQpAxggBRCjASAGKAIYIQQLIAFBAWohAQwACwALIAYoAiAEQCAAIAApAyBCAXw3AyAgACAAKQMQIAYoAiRBAnStfDcDEAsgBigCLARAIAAgACkDIEIBfDcDICAAIAApAxAgBigCMEEMbK18NwMQCyAGKQM4IAUQowEgBikDQCAFEKMBDAELCyADQQRqIQEMAQsLIAhB1ABqIQEgCEHQAGohCwNAIAsgASgCACIKRwRAIApBCGshAwJAAkACQCAKQQRrLQAAQQ9xDgIBAAILQQAhASADKAIgBH8gAy8BKiADLwEoakEEdEFAawVBwAALIQQgAygCNARAIAMoAjgiBkEDdCEJA0AgASAGSARAIAMoAjQgAUEDdGopAwAgBRCjASABQQFqIQEgAygCOCEGDAELCyAEIAlqIQQLIAMoAiQEQCADKAI8QQN0IARqIQQLAkAgAy8AESIGQYAgcQ0AIAMoAhRFDQAgBSAFKQMoIAM0Ahh8NwMoIAMvABEhBgtBACEBAkAgBkGACHFFDQAgAygCVAR/QQEhASAEIAMoAkhqQRlqBSAEQRhqCyEEIAMoAkwiA0UNACAFIAUpAzBCAXw3AzAgBSAFKQM4IAOsfDcDOCABQQFqIQELIAUgBSsDICAEt6A5AyAgBSAFKQMYQgF8NwMYIAUgBSsDACABt6A5AwAMAQsgAygCECEJIAAgACkDSEIBfDcDSAJAIAMoAhRFDQAgACAAKQMgQgF8NwMgIAAgACkDYCAJKAIcQQN0rXw3A2AgACAAKQNYIAkoAiAiBKx8NwNYQQAhBiAJECohAQNAIAQgBkwNAQJAIAEoAgRFDQAgASgCAEH/////A0sNACADKAIUIAZBA3RqKQMAIAUQowEgCSgCICEECyAGQQFqIQYgAUEIaiEBDAALAAsgCS0AEEUEQCAJKAIYIQEgACAAKQNoQgF8NwNoIAAgACkDcCABQQFqIAkoAhwQ5QGtfDcDcAsCQAJAAkACQAJAAkACQAJAAkACQCADLwEGQQJrDhMACQEBAQEACQEJAgMEBQkHBggICQsgACAAKQOoAUIBfDcDqAEgAy0ABUEIcUUNCSAAIAApA7ABQgF8NwOwASADKAIkRQ0JIAAgACkDIEIBfDcDICAAIAApAxAgAygCKEEDdK18NwMQIAAgACkDuAEgAzUCKHw3A7gBQQAhAQNAIAEgAygCKE8NCiADKAIkIAFBA3RqKQMAIAUQowEgAUEBaiEBDAALAAsgAykDICAFEKMBDAgLIAAgACkDoAFCAXw3A6ABDAcLIAMoAiQiCUUNBiADKAIgIQYgACAAKQMgQgF8NwMgIAAgACkDgAEgBigCPCIEQQJ0rXw3A4ABQQAhAQNAIAEgBE4NBwJAIAkgAUECdGooAgAiA0UNACAAAn5EAAAAAAAA8D8gAygCALciGqMgACkDILmgIhuZRAAAAAAAAOBDYwRAIBuwDAELQoCAgICAgICAgH8LNwMgIAACfkQAAAAAAABAQCAaoyAAKQOAAbmgIhqZRAAAAAAAAOBDYwRAIBqwDAELQoCAgICAgICAgH8LNwOAASADKAIQIg0gA0EYakcNACANKQMAIAUQowEgBigCPCEECyABQQFqIQEMAAsACyADKAIgIQRBACEBA0AgBCgCECIDIAFKBEAgBCABQQN0aikDGCAFEKMBIAFBAWohAQwBCwsgACAAKQMgQgF8NwMgIAAgACkDECADQQN0QRhqrXw3AxAMBQsgAygCICIERQ0EQQAhAQNAIAQtAAUiAyABSwRAIAQgAUEDdGopAwggBRCjASABQQFqIQEMAQsLIAAgACkDIEIBfDcDICAAIAApAxAgA61CA4Z8Qgh8NwMQDAQLIAMoAiAgBRCeBCADKAIkIAUQngQMAwsgAygCICIBRQ0CIAEpAwAgBRCjASAAIAApAyBCAXw3AyAgACAAKQMQQhh8NwMQDAILIAMoAiAiAUUNASAAIAApAyAiDkIBfDcDICAAIAApAxBCHHwiDzcDECABKAIIRQ0BIAAgDkICfDcDICAAIA8gATQCAHw3AxAMAQsgAygCIEUNACAAIAApAyBCAXw3AyALIApBBGohAQwBCwsgACAAKQNQIAApA0giD0IwfnwiEDcDUCAAIAApAxAgCCgCzAEiAUECdK18IhE3AxBBACEEIAFBACABQQBKGyEDIAApAyAhDgNAIAMgBEcEQCAIKALUASAEQQJ0aiEBA0AgASgCACIBBEAgASgCGCEGIAAgACkDaEIBfDcDaCAAIAApA3AgBkEBaiABKAIcEOUBrXw3A3AgAUEoaiEBDAELCyAEQQFqIQQMAQsLIAAgDkIDfCISNwMgIAAgCCgCKCIDrDcDKCAAIAgoAiwiBCAIKAIkakECdK0iDjcDMEEAIQEgBEEAIARBAEobIQYDQCABIAZHBEAgCCgCOCABQQJ0aigCACIEEOMDRQRAIAAgDiAEKAIEIgRBH3UgBEH/////B3EgBEEfdnRqQRFqrXwiDjcDMAsgAUEBaiEBDAELCyAAAn4gBSsDCBCxAyIamUQAAAAAAADgQ2MEQCAasAwBC0KAgICAgICAgIB/CyITNwM4IAACfiAFKwMQELEDIhqZRAAAAAAAAOBDYwRAIBqwDAELQoCAgICAgICAgH8LIhQ3A0AgACAFKQMYIhU3A3ggAAJ+IAUrAyAQsQMiGplEAAAAAAAA4ENjBEAgGrAMAQtCgICAgICAgICAfwsiFjcDgAEgACAFKQMoIhc3A4gBIAAgBSkDMCIYNwOQASAAIAUpAzgiGTcDmAEgBSsDACEaIAAgACkDcCAAKQNgIBkgFyAQIBF8IBR8IBZ8fHwgDnx8fDcDECAAAn4gGhCxAyADt6AgE7mgIA+5oCAAKQNouaAgFbmgIBi5oCASuaAiGplEAAAAAAAA4ENjBEAgGrAMAQtCgICAgICAgICAfws3AyAgBUFAayQAQbCzBCgCACEBQQAhBEEAIQYjAEGwBmsiACQAIAAgBzQCCDcDmAQgAEEgNgKQBCACQZmDASAAQZAEahClASABBEADQCAEQQVHBEAgASAEQQN0IghBtIgBaigCACIDEOgBIgUEQCADIAEgBRCjBCIJTQRAIAAgCEGwiAFqKAIANgKIBCAAIAM2AoAEIAAgCSADazYChAQgAkHb/wAgAEGABGoQpQFBASEGCyABIAUQIQsgBEEBaiEEDAELCyAGRQRAQe3/AEEhIAIQhQYLIABB4ARqQQBB0AEQSxogAUHUAGohBCABQdAAaiEDA0AgAyAEKAIAIgRHBEAgBEEEay0AAEEPcUUEQCAAQeAEaiAEQQhrLwEGIgVBMyAFQTNJG0ECdGoiBSAFKAIAQQFqNgIACyAEQQRqIQQMAQsLQaj/AEESIAIQhQYgACgC4AQiBARAIABBycwANgL4AyAAQQA2AvQDIAAgBDYC8AMgAkHK/wAgAEHwA2oQpQELQQEhBANAIARBM0cEQCAAQeAEaiAEQQJ0aigCACIDBEAgACABIABBoARqIARBDGxB1IMBaigCABDcBTYC6AMgACAENgLkAyAAIAM2AuADIAJByv8AIABB4ANqEKUBCyAEQQFqIQQMAQsLIAAoAqwGIgEEQCAAQYcxNgLYAyAAQQA2AtQDIAAgATYC0AMgAkHK/wAgAEHQA2oQpQELAkACQCACKAJMIgFBAE4EQCABRQ0BQay0BCgCACABQf////97cUcNAQsCQCACKAJQQQpGDQAgAigCFCIBIAIoAhBGDQAgAiABQQFqNgIUIAFBCjoAAAwCCyACEIYGDAELIAIgAigCTCIBQf////8DIAEbNgJMAkACQCACKAJQQQpGDQAgAigCFCIBIAIoAhBGDQAgAiABQQFqNgIUIAFBCjoAAAwBCyACEIYGCyACKAJMGiACQQA2AkwLCyAAQdzsADYCyAMgAEGl6AA2AsQDIABB9ewANgLAAyACQbv/ACAAQcADahClASAHKQMYIg5QRQRAIAAgBykDACIPNwOwAyAAIA43A6gDIAAgD7kgDrmjOQO4AyAAQcDfADYCoAMgAkHvgQEgAEGgA2oQuQEgBykDICEOIAcpAwAhECAHKQMQIQ8gAEEINgKIAyAAIA83A4ADIAAgECAPfbkgDrmjOQOQAyAAIA43A/gCIABB0d8ANgLwAiACQZWCASAAQfACahC5AQsgBykDKCIOUEUEQCAAIAcpAzAiDzcD4AIgACAONwPYAiAAIA+5IA65ozkD6AIgAEGiIzYC0AIgAkHKgQEgAEHQAmoQuQELIAcpAzgiDlBFBEAgACAHKQNAIg83A8ACIAAgDjcDuAIgACAPuSAOuaM5A8gCIABBhiQ2ArACIAJBzIIBIABBsAJqELkBCyAHKQNIIg5QRQRAIAAgBykDUCIPNwOgAiAAIA43A5gCIAAgD7kgDrmjOQOoAiAAQYEgNgKQAiACQfqAASAAQZACahC5ASAHKQNYIQ4gBykDSCEPIAAgBykDYDcDgAIgACAOuSAPuaM5A4gCIAAgDjcD+AEgAEGrJjYC8AEgAkH6gAEgAEHwAWoQuQEgBykDaCEOIAAgBykDcCIPNwPgASAAIA+5IA65ozkD6AEgACAONwPYASAAQZMlNgLQASACQfOCASAAQdABahC5AQsCQCAHKQN4Ig5QDQAgACAHKQOAATcDwAEgACAONwO4ASAAQcsiNgKwASACQZyAASAAQbABahClASAHKQN4IQ4gACAHKQOIASIPNwOgASAAIA+5IA65ozkDqAEgACAONwOYASAAQYTVADYCkAEgAkGhgQEgAEGQAWoQuQEgBykDkAEiDlANACAAIAcpA5gBIg83A4ABIAAgDjcDeCAAIA+5IA65ozkDiAEgAEH6zAA2AnAgAkGhgQEgAEHwAGoQuQELIAcpA6ABIg5QRQRAIAAgDjcDaCAAQd4iNgJgIAJBj4ABIABB4ABqEKUBCwJAIAcpA6gBIg5QDQAgACAONwNYIABB1B42AlAgAkGPgAEgAEHQAGoQpQEgBykDsAEiDlANACAAIA43A0ggAEHNHjYCQCACQY+AASAAQUBrEKUBIAcpA7ABIQ8gACAHKQO4ASIOQgOGNwMwIAAgDrkgD7mjOQM4IAAgDjcDKCAAQdYfNgIgIAJBz4ABIABBIGoQuQELIAcpA8ABIg5QRQRAIAAgBykDyAE3AxAgACAONwMIIABB+h82AgAgAkGcgAEgABClAQsgAEGwBmokACACKAJMGiACELQDGiACIAIoAgwRBAAaIAItAABBAXFFBEAgAigCNCIABEAgACACKAI4NgI4CyACKAI4IgEEQCABIAA2AjQLIAJBmLQEKAIARgRAQZi0BCABNgIACyACKAJgEOkBIAIQ6QELIAwQCiAMEOkBCyAHQdABaiQAC6wCAgR/A34jAEEgayIFJABCgICAgOAAIQsCQCAAIAEQmAEiCEEASA0AQSwhB0KAgICAMCEKAkAgAkEATCAEcg0AIAMpAwAiCRASDQAgACAJEC4iChANDQFBfyEHIAqnIgYoAgRBAUcNACAGLQAQIQcLIAAgBUEIakEAEEIaQQAhAgJAA0AgAiAIRwRAAkAgAkUNACAHQQBOBEAgBUEIaiAHED5FDQEMBAsgBUEIaiAGQQAgBigCBEH/////B3EQWQ0DCwJAIAAgASACEHsiCRAoDQAgCRASDQAgCRANDQMgBUEIaiAEBH4gACAJENYEBSAJCxCPAQ0DCyACQQFqIQIMAQsLIAAgChAMIAVBCGoQOSELDAELIAVBCGoQRCAAIAoQDAsgBUEgaiQAIAsLqwIDA38BfgF8IwBBIGsiAyQAIAIoAgRFBEAgASgCACEFIAMgAigCACIBIAIoAhwgACgCACIAIAIoAiBsaiACKAIYEQwANwMQIAMgASACKAIcIAUgAigCIGxqIAIoAhgRDAA3AxgCQCABIAIpAxBCgICAgDBBAiADQRBqECQiBhANBEAgAkEBNgIEDAELAkACfyAGQv////8PWARAIAanIgRBH3UgBEEASmoMAQsgASADQQhqIAYQW0EASA0BIAMrAwgiB0QAAAAAAAAAAGQgB0QAAAAAAAAAAGNrCyIERQRAIAAgBUsgACAFSWshBAsgASACKQMIEOgCQQBODQEgAkEBNgIEDAELIAJBATYCBAsgASADKQMQEAwgASADKQMYEAwLIANBIGokACAEC+IEAgZ/An4jAEEwayICJAAgAiABNwMQIAIgADYCCCACQQA2AgwgAiADKQMAIgo3AxhCgICAgOAAIQsCQAJAIAAgARCYASIEQQBIDQAgChASIgVFBEAgACAKEGkNAQsCQCAEQQJJDQAgAaciAy8BBkEVayIGQf//A3FBCU8NAiACIAZBEHRBEHVBAnQiB0Gs3QFqKAIANgIgQQEgAy8BBkHligFqLQAAIgl0IQggAygCJCEGIAVFBEAgACAEQQJ0EC8iBUUNAkEAIQMDQCADIARGRQRAIAUgA0ECdGogAzYCACADQQFqIQMMAQsLIAIgCDYCKCACIAY2AiQgBSAEQQRBOSACQQhqEK4CAkAgAigCDEUEQCAAIAQgCXQiAxAvIgcNAQsgACAFEBoMAwsgByAGIAMQJSEHQQAhAwJAAkACQAJAAkAgCEEBaw4IAAEIAggICAMICwNAIAMgBEYNBCADIAZqIAcgBSADQQJ0aigCAGotAAA6AAAgA0EBaiEDDAALAAsDQCADIARGDQMgBiADQQF0aiAHIAUgA0ECdGooAgBBAXRqLwEAOwEAIANBAWohAwwACwALA0AgAyAERg0CIAYgA0ECdCIIaiAHIAUgCGooAgBBAnRqKAIANgIAIANBAWohAwwACwALA0AgAyAERg0BIAYgA0EDdGogByAFIANBAnRqKAIAQQN0aikDADcDACADQQFqIQMMAAsACyAAIAcQGiAAIAUQGgwBCyAGIAQgCCAHQdDdAWooAgAgAkEIahCuAiACKAIMDQELIAEQDyELCyACQTBqJAAgCw8LEAEAC/EBAgJ/A34jAEEwayICJABCgICAgOAAIQcCQCAAIAFBABCbASIFRQ0AIAAgAkEMaiADKQMAIAUoAigiBCAEEGUNACACIAQ2AgggAykDCCIGEBIEfyAEBSAAIAJBCGogBiAEIAQQZQ0BIAIoAggLIAIoAgwiA2tBABBKIQQgACABQQAQygQiBhANDQAgBS8BBiEFIAAgBhAMIAAgAUEAEMsEIggQDQ0AIAIgCDcDGCACIAE3AxAgAiAErTcDKCACIAanIAMgBUHligFqLQAAdGqtNwMgIABBBCACQRBqEOwCIQcgACAIEAwLIAJBMGokACAHC/wCAgR/BH4jAEEgayICJABCgICAgDAhCAJAAkAgACABEJgBIgRBAEgNACAAIAJBDGogAykDACAEIAQQZQ0AIAIgBDYCCCADKQMIIgkQEgR/IAQFIAAgAkEIaiAJIAQgBBBlDQEgAigCCAsgAigCDCIFa0EAEEohAyAAIAFBABCbASIERQ0AIAQvAQYhByACIAOtIgo3AxggAiABNwMQIABBAiACQRBqEOwCIggQDQ0AIANBAEwNASAAIAEQ6AINACAAIAgQ6AINAAJAIAAgCEEAEJsBIgZFDQAgBC8BBiAGLwEGRw0AIAYQkgQgA0kNACAEEJIEIAMgBWpJDQAgBigCJCAEKAIkIAUgB0HligFqLQAAIgB0aiADIAB0ECUaDAILQgAhCQNAIAkgClENAiAAIAEgBSAJp2qtEKEBIgsQDQ0BIAAgCCAJIAtBgIABEOEBIQMgCUIBfCEJIANBAE4NAAsLIAAgCBAMQoCAgIDgACEICyACQSBqJAAgCAvNAgEBfiAAIAEQmAEiAkEASARAQoCAgIDgAA8LAkAgAkUNAAJAAkACQAJAAkAgAaciAC8BBkHligFqLQAADgQAAQIDBAsgACgCJCIAIAJqIQIDQCAAIAJBAWsiAk8NBSAALQAAIQMgACACLQAAOgAAIAIgAzoAACAAQQFqIQAMAAsACyAAKAIkIgAgAkEBdGohAgNAIAAgAkECayICTw0EIAAvAQAhAyAAIAIvAQA7AQAgAiADOwEAIABBAmohAAwACwALIAAoAiQiACACQQJ0aiECA0AgACACQQRrIgJPDQMgACgCACEDIAAgAigCADYCACACIAM2AgAgAEEEaiEADAALAAsgACgCJCIAIAJBA3RqIQIDQCAAIAJBCGsiAk8NAiAAKQMAIQQgACACKQMANwMAIAIgBDcDACAAQQhqIQAMAAsACxABAAsgARAPC+4BAgZ+An8jAEEgayILJABCgICAgDAhBgJAAkAgACABEJgBIgxBAEgNACAAIAMpAwAiCBBpDQBCgICAgDAhByACQQJOBEAgAykDCCEHCyAMrSEJA0AgBSAJUgRAIAAgASAFEKEBIgYQDQ0CIAsgATcDECALIAU3AwggCyAGNwMAIAAgCCAHQQMgCxAkIgoQDQ0CIAAgChAtBEAgBEUEQCAGIQUMBQsgACAGEAwMBAUgACAGEAwgBUIBfCEFDAILAAsLQv////8PQoCAgIAwIAQbIQUMAQsgACAGEAxCgICAgOAAIQULIAtBIGokACAFC7UEAgR/A34jAEEQayIEJABCgICAgOAAIQkCQCAAIAEQmAEiBkEASA0AAn4gAaciBS8BBiIHQRVGBEAgACAEIAMpAwAQDxDUBQ0CIAQ0AgAMAQsgB0EbTQRAIAAgBCADKQMAEMcBDQIgBDUCAAwBCyAAIAQgAykDABBHDQEgBS8BBkEcRgRAIAQrAwC2vK0MAQsgBCkDAAshCCAEQQA2AgACQCACQQFMBEAgBCAGNgIMDAELIAAgBCADKQMIIAYgBhBlDQEgBCAGNgIMIAJBA0kNACADKQMQIgoQEg0AIAAgBEEMaiAKIAYgBhBlDQELIAUQmgEEQCAAEHUMAQsCQAJAAkACQAJAAkACQAJAAkAgBS8BBkHligFqLQAADgQAAQIDBAsgBCgCDCICIAQoAgAiAEwNByAFKAIkIABqIAinIAIgAGsQSxoMBwsgBCgCACIAIAQoAgwiAiAAIAJKGyECIAinIQMDQCAAIAJGDQQgBSgCJCAAQQF0aiADOwEAIABBAWohAAwACwALIAQoAgAiACAEKAIMIgIgACACShshAiAIpyEDA0AgACACRg0EIAUoAiQgAEECdGogAzYCACAAQQFqIQAMAAsACyAEKAIAIgAgBCgCDCICIAAgAkobIQIDQCAAIAJGDQQgBSgCJCAAQQN0aiAINwMAIABBAWohAAwACwALEAEACyAEIAI2AgAMAgsgBCACNgIADAELIAQgAjYCAAsgARAPIQkLIARBEGokACAJC/ABAgN/An4jAEEQayIFJABCgICAgOAAIQcCQCAAIAEQmAEiBEEASA0AIAAgBUEMaiADKQMAIAQgBBBlDQAgACAFQQhqIAMpAwggBCAEEGUNACAFIAQ2AgQCfyAEIAJBA0gNABogBCADKQMQIggQEg0AGiAAIAVBBGogCCAEIAQQZQ0BIAUoAgQLIAUoAggiBmsgBCAFKAIMIgNrELQBIgJBAEoEQCABpyIEEJoBBEAgABB1DAILIAQoAiQiACADIAQvAQZB5YoBai0AACIDdGogACAGIAN0aiACIAN0EIECCyABEA8hBwsgBUEQaiQAIAcLSgIBfgF/QoCAgIAwIQICQCABQoCAgIBwVA0AIAGnLwEGIgNBFWtB//8DcUEISw0AIAAgACgCECgCRCADQRhsaigCBBAyIQILIAILRwEBfgJAAkAgAkUEQAwBCyAAIAMpAwAQ0AUiBBANDQELIAEQEg0AIAAgAUEEEG8iARANRQRAIAAgASAEEM8BCyABIQQLIAQLLAEBfkKAgICA4AAhBSAAIAEQ6AIEfkKAgICA4AAFIAAgASACIAMgBBCaBQsLoAMCBH4HfyADKQMAIQUgAkECTgR+IAMpAwgFQoCAgIAwCyEEIwBBEGsiAiQAQoCAgIDgACEHQoCAgIAwIQYCQCAAIAFBABCbASIDRQ0AIAAgAiAEEI4EDQACQAJAAkACQCACKQMAIgRCAFMNACADEJoBDQMgACAFECsiBhANDQQgBqciCC8BBiIKQRVrQf//A3FBCE0EQCAIKAIgIgsoAgwoAiAiDC0ABA0EIAMvAQYhCSADKAIgIg0oAgwoAiAhDiACIAg1AigiBTcDCCAEIAM1AiggBX1VDQEgCSAKRw0CIAQgCUHligFqMQAAIgGGpyAOKAIIIA0oAhBqaiAMKAIIIAsoAhBqIAUgAYanEIECDAMLIAAgAkEIaiAGEEENBCAEIAM1AiggAikDCCIFfVcNAQsgAEHHwQAQawwDCyAEpyEIQQAhAwNAIAUgA61XDQEgACAGIAMQeyIEEA0NAyADIAhqIQkgA0EBaiEDIAAgASAJIAQQlgJBAE4NAAsMAgtCgICAgDAhBwwBCyAAEHULIAAgBhAMIAJBEGokACAHC0oCAX8BfkKAgICA4AAhBCAAIAEgAhCbASIDBH4gAxCaAQRAIAJFBEBCAA8LIAAQdUKAgICA4AAPCyADKAIgNQIUBUKAgICA4AALCx4AIAAgAUEAEJsBIgBFBEBCgICAgOAADwsgADUCKAs9AQF+QoCAgIAQIQEgAykDACIEQoCAgIBwWgR+IASnLwEGQRVrQf//A3FBCkmtQoCAgIAQhAVCgICAgBALC5ADAgV+AX8jAEEgayICJABCgICAgOAAIQgCQCAAIAEgBBBqIgpFDQAgCi0ABARAIAAQdQwBCyAAIAJBGGogAykDAEIAIAo0AgAiBSAFEIEBDQAgAiAFNwMQIAMpAwgiBhASBH4gBQUgACACQRBqIAZCACAFIAUQgQENASACKQMQCyACKQMYIgl9ENUEIQcgACABQoCAgIAwEPMBIgYQDQRAIAYhCAwBCwJAIAYQEgRAIABCgICAgDAgByAEEPQDIQUMAQsgAiAHQoCAgIAIfEL/////D1gEfiAHQv////8PgwUgB7kQFws3AwggACAGQQEgAkEIahCyASEFIAAgBhAMIAAgAikDCBAMCwJAIAUQDQ0AAkAgACAFIAQQaiIDRQ0AIAAgBSABEFoEQCAAQeMxQQAQFgwBCwJAIAMtAAQNACADNAIAIAdTBEAgAEHzPUEAEBYMAgsgCi0ABA0AIAMoAgggCigCCCAJp2ogB6cQJRoMAgsgABB1CyAAIAUQDAwBCyAFIQgLIAJBIGokACAICy4AIAAgASACEGoiAEUEQEKAgICA4AAPCyAAKAIAIgBBAE4EQCAArQ8LIAC4EBcL9AIBAX4gAUEoEEAhAiAEQQE2AgACQAJAIAJFBEAgAEHzKkEAEBYMAQsCQAJAAkACQAJAAkACQAJAIAIoAgBBAWsOBAICBwEACyAFRQ0CIAAgAhDAAwtCgICAgDAhASAFQQFrDgIDBAcLIAMpAwAQDyEBAkAgBUECRw0AQQEhAyACKAIAQQFHDQAgACABEJQBDAILIAIoAkQiAyAFrTcDACADQQhrIAE3AwAgAiADQQhqNgJEC0EAIQMLIAJBAzYCACACIAM2AhQgACACQQhqEMICIQEgAkEBNgIAIAEQDQRAIAAgAhDAAyABDwsgAigCREEIayIDKQMAIQYgA0KAgICAMDcDACABQv////8PWARAIAGnQQJGBEAgAkECNgIAIARBAjYCACAGDwsgBEEANgIAIAYPCyAAIAEQDCAAIAIQwAMgBg8LIAMpAwAQDw8LIAAgAykDABAPEJQBDAELIABB0SpBABAWC0KAgICA4AAhAQsgAQtlAQF+IAMpAwAiARD2A0UEQCAAQbY8QQAQFkKAgICA4AAPC0KAgICAMCEEIAGnKQIEQoCAgICAgICAQINCgICAgICAgICAf1EEfiABQv////8Pg0KAgICAkH+EEA8FQoCAgIAwCwsvAQF+QoCAgIDgACEBIAAgAykDABAuIgQQDQR+QoCAgIDgAAUgACAEp0ECEPUDCwtJAgF+AX8gACABEMEDIgEQDQRAIAEPC0KAgICAMCECIAGnIgMoAgRBgICAgHhHBEAgACAAKAIQIAMQ1gIQMiECCyAAIAEQDCACCwkAIAAgARDBAwtOAQF+IwBBEGsiAiQAIAIgACABEMEDIgE3AwgCQCABEA0EQCABIQQMAQsgAEKAgICAMEEBIAJBCGoQyQQhBCAAIAEQDAsgAkEQaiQAIAQLLQBCgICAgOAAIAAgAykDACADKQMIQQAQmwIiAEEAR61CgICAgBCEIABBAEgbC4YBAQN+IAMpAwAiASEEIAJBBE4EQCADKQMYIQQLIAFC/////29YBEAgABApQoCAgIDgAA8LIAMpAxAhBkKAgICA4AAhBQJAIAAgAykDCBA4IgJFDQAgACABIAIgBhAPIARBABCIBCEDIAAgAhATIANBAEgNACADQQBHrUKAgICAEIQhBQsgBQsqACADKQMAIgFC/////29YBEAgABApQoCAgIDgAA8LIAAgAUEDQQAQgQMLYwEBfiADKQMAIgRC/////29YBEAgABApQoCAgIDgAA8LQoCAgIDgACEBAkAgACADKQMIEDgiAkUNACAAIAQgAhB6IQMgACACEBMgA0EASA0AIANBAEetQoCAgIAQhCEBCyABC2MBAn4CQAJAIAMpAwAiAUL/////b1gEQCAAECkMAQsgAykDCCEFIAEhBCACQQNOBEAgAykDECEECyAAIAUQOCICDQELQoCAgIDgAA8LIAAgASACIARBABAUIQEgACACEBMgAQtmAQF+IAMpAwAiBEL/////b1gEQCAAEClCgICAgOAADwtCgICAgOAAIQECQCAAIAMpAwgQOCICRQ0AIAAgBCACQQAQ3gEhAyAAIAIQEyADQQBIDQAgA0EAR61CgICAgBCEIQELIAELigECAX8CfiMAQRBrIgQkACADKQMIIQUgAykDACIGIQECQAJAAkACQCACQQNIDQAgAykDECIBELUBDQAgAEHfKUEAEBYMAQsgACAEQQxqIAUQiwQiAg0BC0KAgICA4AAhAQwBCyAAIAYgASAEKAIMIgMgAhCOAyEBIAAgAiADEJgDCyAEQRBqJAAgAQscACAAIAMpAwBBACACQQFrEEogA0EIakECEJoDC0MAIwBBEGsiAiQAAn5CgICAgOAAIAAgAkEMaiADKQMAEMcBDQAaQiAgAigCDCIARQ0AGiAAZ60LIQEgAkEQaiQAIAELUAAjAEEQayICJABCgICAgOAAIQECQCAAIAJBDGogAykDABCTAg0AIAAgAkEIaiADKQMIEJMCDQAgAigCCCACKAIMbK0hAQsgAkEQaiQAIAELBgAgALa7C1AAIAAgACkD0AEiAUIMiCABhSIBQhmGIAGFIgFCG4ggAYUiATcD0AEgAUKdurP7lJL9oiV+QgyIQoCAgICAgID4P4S/RAAAAAAAAPC/oBAXC/UDAwN8BX8DfiMAQRBrIggkACAIQgA3AwgCQAJAIAJBAEwNAEKAgICA4AAhASAAIAhBCGogAykDABBHDQFBASEJIAgrAwghBCACQQFHBEADQCACIAlGDQIgACAIIAMgCUEDdGopAwAQRw0DIAlBAWohCSAIKwMAIQUjAEEgayIHJAAgBL1C////////////AIMiDSAFvUL///////////8AgyIMIAwgDVYbIg6/IQQCQCAOQjSIpyIKQf8PRg0AIA0gDCAMIA1UGyIMvyEFAkAgDlANACAMQjSIpyILQf8PRg0AIAsgCmtBwQBOBEAgBSAEoCEEDAILAnwgC0H+C08EQCAERAAAAAAAADAUoiEEIAVEAAAAAAAAMBSiIQVEAAAAAAAAsGsMAQtEAAAAAAAA8D8gCkG8BEsNABogBEQAAAAAAACwa6IhBCAFRAAAAAAAALBroiEFRAAAAAAAADAUCyEGIAdBGGogB0EQaiAFEIQGIAdBCGogByAEEIQGIAYgBysDACAHKwMQoCAHKwMIoCAHKwMYoJ+iIQQMAQsgBSEECyAHQSBqJAAMAAsACyAEmSEECyAEvQJ/IASZRAAAAAAAAOBBYwRAIASqDAELQYCAgIB4CyIAt71RBEAgAK0hAQwBCyAEEBchAQsgCEEQaiQAIAELTgAgACAARAAAAAAAAPC/RAAAAAAAAPA/IABEAAAAAAAAAABjGyAAvUL///////////8Ag0KAgICAgICA+P8AVhsgAEQAAAAAAAAAAGEbC4MBAgJ+AX8gAL0iAUI0iKdB/w9xIgNB/gdNBEAgAUKAgICAgICAgIB/gyECIANB/gdHIAFCgICAgICAgPC/f1FyRQRAIAJCgICAgICAgPg/hL8PCyACvw8LIANBsghNBHwgAUI/hyABfEIBQbMIIANrrYYiAUIBiHxCACABfYO/BSAACwvdBAICfAV/IwBBEGsiCCQAAn4gAkUEQEQAAAAAAADw/0QAAAAAAADwfyAEGxAXDAELAnwgAykDACIBQv////8PWARAIAJBASACQQFKGyELIAGnIQlBASEHA0AgByALRwRAIAm3IAMgB0EDdGopAwAiAUKAgICAEFoNAxogAachCgJ/IAQEQCAJIAoQSgwBCyAJIAoQtAELIQkgB0EBaiEHDAELCyAJrQwCC0KAgICA4AAgACAIQQhqIAEQRw0BGkEBIQcgCCsDCAshBSAHIAIgAiAHSBshAgNAIAIgB0cEQEKAgICA4AAgACAIIAMgB0EDdGopAwAQRw0CGgJAIAW9Qv///////////wCDQoCAgICAgID4/wBWDQAgCCsDACIGvUL///////////8Ag0KAgICAgICA+P8AVgRAIAYhBQwBCyAEBEAgBSAFIAalIAa9Qv///////////wCDQoCAgICAgID4/wBWGyAGIAW9Qv///////////wCDQoCAgICAgID4/wBYGyAGvSAFvYO/IAVEAAAAAAAAAABiIAZEAAAAAAAAAABichshBQwBCyAFIAUgBqQgBr1C////////////AINCgICAgICAgPj/AFYbIAYgBb1C////////////AINCgICAgICAgPj/AFgbIAa9IAW9hL8gBUQAAAAAAAAAAGIgBkQAAAAAAAAAAGJyGyEFCyAHQQFqIQcMAQsLIAW9An8gBZlEAAAAAAAA4EFjBEAgBaoMAQtBgICAgHgLIgC3vVEEQCAArQwBCyAFEBcLIQEgCEEQaiQAIAEL0AEBAn8jAEEQayICJAACfiAAIAFBJhBqIgNFBEAgBEEANgIAQoCAgIDgAAwBCwJAIAMpAwAiARASRQRAIAIgAygCDCIFNgIMIAUgAaciBigCBEH/////B3FJDQEgACABEAwgA0KAgICAMDcDAAsgBEEBNgIAQoCAgIAwDAELIAYgAkEMahDbASEHIAMgAigCDDYCDCAEQQA2AgAgB0H//wNNBEAgACAHQf//A3EQpgMMAQsgACAGIAVBAXRqQRBqQQIQnAQLIQEgAkEQaiQAIAELxwICAn8CfiMAQSBrIgIkAEKAgICA4AAhBwJAIAAgARBjIgEQDQ0AIAAgAkEIaiIFQQcQQhogBUE8ED4aIAUgBEEDdCIFQfDKAWooAgAiBhCOARpBnj0gBHZBAXFFBEAgAkEIaiIEQSAQPhogBCAFQfTKAWooAgAQjgEaIARByv4AEI4BGiAAIAMpAwAQYyIIEA0EQCAAIAEQDCACQQhqEEQMAgsgCKchA0EAIQQDQCAEIAMoAgRB/////wdxT0UEQAJAIAMgBBBNIgVBIkYEQCACQQhqQfTvABCOARoMAQsgAkEIaiAFEJYBGgsgBEEBaiEEDAELCyAAIAgQDCACQQhqQSIQPhoLIAJBCGoiAEE+ED4aIAAgARCPARogAEH29QAQjgEaIAAgBhCOARogAkEIakE+ED4aIAAQOSEHCyACQSBqJAAgBwu7BAEIfyMAQTBrIgIkAAJAIAAgARBjIgEQDQ0AIAGnIggoAgRB/////wdxIgNFDQACQCAAIAJBGGogAxBCDQBBACEDIAJBADYCFANAAkAgCCgCBEH/////B3EgA0oEQEEAIQMCfwJAIARFIAggAkEUahDbASIMQaMHR3INACACKAIUQQFrIQsjAEEQayIHJAAgByALNgIMA0AgBygCDCIFQQBMBH9BAAUgCEEQaiEJIAVBAWshBgJAAkAgCC0AB0GAAXEEQCAJIAZBAXRqLwEAIgpBgPgDcUGAuANHIAVBAklyDQEgCSAFQQJrIgVBAXRqLwEAIglBgNAAakH//wNxQYAISw0BIApB/wdxIAlB/wdxQQp0ckGAgARqIQoMAgsgBiAJai0AACEKCyAGIQULIAcgBTYCDCAKCyIGELsEDQALAkAgBhC9BEUEQEEAIQYMAQtBASEGIAcgC0EBaiIFNgIMA0AgBSAIKAIEQf////8HcU4NASAIIAdBDGoQ2wEiBRC7BARAIAcoAgwhBQwBCwsgBRC9BEUhBgsgB0EQaiQAIAZFDQAgAkHCBzYCCEEBDAELIAJBCGogDCAEELcDCyIGQQAgBkEAShshBgNAIAMgBkYNAiADQQJ0IQUgA0EBaiEDIAJBGGogBSACQQhqaigCABDAAUUNAAsMAwsgACABEAwgAkEYahA5IQEMAwsgAigCFCEDDAALAAsgACABEAwgAkEYahBEQoCAgIDgACEBCyACQTBqJAAgAQtaAQF+QoCAgIDgACEEIAAgARBjIgEQDQR+QoCAgIDgAAUgACADKQMAEC4iBBANBEAgACABEAxCgICAgOAADwsgAacgBKcQlQIhAiAAIAEQDCAAIAQQDCACrQsLCQAgACABEIwFC18AAn4CQCABQiCIpyICQX9HBEAgAkF5Rw0BIAEQDwwCCyABpyICLwEGQQVHDQAgAikDICIBQoCAgIBwg0KAgICAkH9SDQAgARAPDAELIABB/MMAQQAQFkKAgICA4AALC6ABAgF/AX4gACABEGMiARANBEAgAQ8LIAGnIgUoAgRB/////wdxIQJBACEDAkAgBEEBcUUNAANAIAIgA0YEQCACIQMMAgsgBSADEE0Q5QJFDQEgA0EBaiEDDAALAAsCQCAEQQJxRQRAIAIhBAwBCwNAIAIiBCADTA0BIAUgBEEBayICEE0Q5QINAAsLIAAgBSADIAQQnQEhBiAAIAEQDCAGC6YDAgZ/A34jAEEgayIFJABCgICAgOAAIQwCQCAAIAEQYyIBEA0NAAJAAkAgACAFQQRqIAMpAwAQxQENACAFKAIEIgcgAaciCSgCBEH/////B3EiCEwNAUEgIQpCgICAgDAhCwJAIAJBAkgNACADKQMIIg0QEg0AIAAgDRAuIgsQDQ0BAkACQCALpyIGKAIEQf////8HcQ4CAAECCyAAIAsQDAwDCyAGQQAQTSEKQQAhBgsgB0GAgICABE4EQCAAQZrDAEEAEFAMAQsgACAFQQhqIAcQQkUEQAJAIAQEQCAFQQhqIAlBACAIEFkNAQsgByAIayECAkACQCAGBEADQCACIgNBAEwNAiADIAMgBigCBEH/////B3EQtAEiB2shAiAFQQhqIAZBACAHEFlFDQALIAUgAzYCBAwDCyAFQQhqIAogAhDMBA0CDAELIAUgAzYCBAsgBEUEQCAFQQhqIAlBACAIEFkNAQsgACALEAwgACABEAwgBUEIahA5IQwMBAsgBUEIahBECyAAIAsQDAsgACABEAwMAQsgASEMCyAFQSBqJAAgDAv0BAIEfgV/IwBB0ABrIgIkACADKQMIIQggAykDACEFAkACQAJAIAEQEkUEQCABEChFDQELIABBiRxBABAWDAELAkAgBRASDQAgBRAoDQAgBARAIAAgBRDOBEEASA0CC0KAgICA4AAhBiAAIAVBxgEgBUEAEBQiBxANDQIgBxASDQAgBxAoDQAgAiAINwMoIAIgATcDICAAIAcgBUECIAJBIGoQNiEGDAILIAAgAkEIakEAEEIaQoCAgIAwIQcCQCAAIAEQLiIGEA0EQEKAgICAMCEFDAELIAAgBRAuIgUQDQ0AIAAgCBA7Ig1FBEAgACAIEC4iBxANDQELIAanIQogBaciDCkCBCEBA0ACQAJAIAFC/////weDUARAQQAhAyALRQ0BIAkgCigCBEH/////B3FPDQIgCUEBaiEDDAELIAogDCAJEM0EIgNBAE4NACALDQEgAkEIahBEIAAgBRAMIAAgBxAMDAULIAIgBTcDIAJ+IA0EQCACIAY3AzAgAiADrTcDKCAAIAAgCEKAgICAMEEDIAJBIGoQJBA9DAELIAIgBzcDSCACQoCAgIAwNwNAIAJCgICAgDA3AzggAiAGNwMoIAIgA603AzAgACACQSBqEI8FCyIBEA0NAiACQQhqIgsgCiAJIAMQWRogCyABEI8BGiAMKQIEIgGnQf////8HcSADaiEJQQEhCyAEDQELCyACQQhqIgMgCiAJIAooAgRB/////wdxEFkaIAAgBRAMIAAgBxAMIAAgBhAMIAMQOSEGDAILIAJBCGoQRCAAIAUQDCAAIAcQDCAAIAYQDAtCgICAgOAAIQYLIAJB0ABqJAAgBguCAgIDfwF+IwBBIGsiAiQAAkACQCAAIAEQYyIBEA0NACAAIAIgAykDABCOBA0AIAIpAwAiB0KAgICACFoEQCAAQdYXEGsMAQsgAaciBSgCBCIGQf////8HcSIERQ0BIAenIgNBAUYNASAHIAStfkKAgICABFoEQCAAQZrDAEEAEFAMAQsgACACQQhqIAMgBGwgBkEfdhCqAw0AAkAgBEEBRwRAA0AgA0EATA0CIAJBCGogBUEAIAQQWRogA0EBayEDDAALAAsgAkEIaiAFQQAQTSADEMwEGgsgACABEAwgAkEIahA5IQEMAQsgACABEAxCgICAgOAAIQELIAJBIGokACABC6UBAgJ/An4jAEEQayICJAACQCAAIAEQYyIBEA0EQCABIQYMAQtCgICAgOAAIQYCQCAAIAJBDGogAykDACABpyIFKAIEQf////8HcSIEIAQQZQ0AIAIgBDYCCCADKQMIIgcQEkUEQCAAIAJBCGogByAEIAQQZQ0BIAIoAgghBAsgACAFIAIoAgwiAyAEIAMQShCdASEGCyAAIAEQDAsgAkEQaiQAIAYLpwECA38CfiMAQRBrIgIkAAJAIAAgARBjIgEQDQRAIAEhBwwBC0KAgICA4AAhBwJAIAAgAkEMaiADKQMAIAGnIgYoAgRB/////wdxIgQgBBBlDQAgAiAEIAIoAgwiBWsiBDYCCCAAIAYgBSADKQMIIggQEgR/IAQFIAAgAkEIaiAIIARBABBlDQEgAigCCAsgBWoQnQEhBwsgACABEAwLIAJBEGokACAHC7sBAgJ/An4jAEEQayICJAACQCAAIAEQYyIBEA0EQCABIQYMAQtCgICAgOAAIQYCQCAAIAJBDGogAykDACABpyIFKAIEQf////8HcUEAEGUNACACIAUoAgRB/////wdxIgQ2AgggAykDCCIHEBJFBEAgACACQQhqIAcgBEEAEGUNASACKAIIIQQLIAAgBSACKAIMIgMgBCADIARIGyADIAQgAyAEShsQnQEhBgsgACABEAwLIAJBEGokACAGC5IEAgl+A38jAEEQayINJAAgAykDCCEHIAMpAwAhBAJAAkACQCABEBJFBEAgARAoRQ0BCyAAQYkcQQAQFgwBCwJAIAQQEiICDQAgBBAoDQBCgICAgOAAIQUgACAEQcgBIARBABAUIggQDQ0CIAgQEg0AIAgQKA0AIA0gBzcDCCANIAE3AwAgACAIIARBAiANEDYhBQwCC0KAgICAMCEKAkAgACABEC4iDBANBEBCgICAgDAhBQwBCyAAEFEiBRANDQACQCAHEBIEQCANQX82AgAMAQsgACANIAcQxwFBAEgNAQsgDKciDikCBCEBIAAgBBAuIgoQDQ0AAkAgDSgCACIDRQ0AIAGnQf////8HcSEPAkAgAgRADAELIAqnIgIpAgRC/////weDIQsgDwRAIAFC/////weDIAt9IAtQrSIEfSEHIAOtIQgDQAJAIAQgCXwiASAHVQ0AIA4gAiABpxDNBCIDQQBIDQAgACAOIAmnIAMQnQEiARANDQUgACAFIAYgAUEAEK4BQQBIDQUgCyADrHwhCSAGQgF8IgYgCFINAQwECwsgBkL/////D4MhBgwBCyALUA0BCyAAIA4gCacgDxCdASIBEA0NASAAIAUgBiABQQAQrgFBAEgNAQsgACAMEAwgACAKEAwMAgsgACAFEAwgACAMEAwgACAKEAwLQoCAgIDgACEFCyANQRBqJAAgBQuvAwEFfiABEBIEQCAAEIIEIQELIAAgAUE7IAFBABAUIgUQDQRAIAUPCwJAAkAgBRAiRQRAIAAgBRAMIAAgARCPAyICRQ0BAn8gBEEASARAIAIoAihBGGoMAQsgAiAEQQN0akHYAGoLKQMAEA8hBQsgACAFQQMQUyEBIAAgBRAMIAEQDQ0BAkAgAyAEQQdGQQN0aikDACIFEBJFBEAgACAFEC4iBRANDQEgACABQTMgBUEDEBsaCyAEQQdGBEAgAykDACEGIwBBEGsiAiQAQoCAgIAwIQUCQAJAIAAgBkEAEPYBIgYQDQRAQoCAgIAwIQgMAQsgACAGQeoAIAZBABAUIggQDQ0AIAAQUSIFEA0NAANAIAAgBiAIIAJBDGoQrwEiCRANRQRAIAIoAgwNAyAAIAUgByAJEHAhAyAHQgF8IQcgA0EATg0BCwsgACAGQQEQswEaCyAAIAUQDEKAgICA4AAhBQsgACAIEAwgACAGEAwgAkEQaiQAIAUQDQ0BIAAgAUE0IAVBAxAbGgsgACABQQBBAEEBEMcCIAEPCyAAIAEQDAtCgICAgOAAIQELIAEL0gIBA34jAEEwayICJAAgAiABNwMoIAMpAwAhBQJAAkAgARASRQRAIAEQKEUNAQsgAEGJHEEAEBZCgICAgOAAIQcMAQsCQCAFEBINACAFECgNAEKAgICA4AAhByAAIAUgBCAFQQAQFCIGEA0NAQJAIARBxQFHDQAgACAFEM4EQQBODQAgACAGEAwMAgsgBhASDQAgBhAoDQAgACAGIAVBASACQShqEDYhBwwBCyACIAAgARAuIgY3AwhCgICAgOAAIQcgBhANDQAgAiAFNwMQAkACQAJ/IARBxQFHBEBCgICAgDAhAUEBDAELIABBgcYAEHYiARANDQEgAiABNwMYQQILIQMgACAAKQNIIAMgAkEQahCyASEFIAAgARAMIAUQDUUNAQsgACAGEAwMAQsgACAFIARBASACQQhqELoCIQcgACACKQMIEAwLIAJBMGokACAHC/kCAgV/A34jAEEQayIFJAACQCAAIAEQYyIKEA0EQCAKIQEMAQsCQCAAIAMpAwAQgwQiBgRAQoCAgIDgACEBQoCAgIAwIQsgBkEATA0BIABBxd0AQQAQFgwBC0KAgICA4AAhASAAIAMpAwAQLiILEA0NACALpyIHKAIEIQggBSAKpyIJKAIEQf////8HcSIGQQAgBEECRhs2AgwCQCACQQJIDQAgAykDCCIMEBINACAAIAVBDGogDCAGQQAQZQ0BCyAGIAhB/////wdxIgZrIQICQAJAAkACQCAEDgIAAQILIAUoAgwhAwwCCyAFKAIMIgMgAkohBEKAgICAECEBIAMhAiAERQ0BDAILIAUgBSgCDCAGayIDNgIMIAMhAgtCgICAgBAhASADQQBIIAIgA0hyDQADQCAJIAcgA0EAIAYQwgNFBEBCgYCAgBAhAQwCCyACIANHIQQgA0EBaiEDIAQNAAsLIAAgChAMIAAgCxAMCyAFQRBqJAAgAQuWAwMHfwF8AX4jAEEQayIFJAACQCAAIAEQYyIBEA0NAAJAAkAgACADKQMAEC4iDRANDQAgDaciCSgCBEH/////B3EhBiABpyIKKAIEQf////8HcSEHAkAgBARAIAUgByAGayILNgIMQX8hCEEAIQQgAkECSA0BIAAgBSADKQMIEEcNAiAFKwMAIgy9Qv///////////wCDQoCAgICAgID4/wBWDQEgDEQAAAAAAAAAAGUEQCAFQQA2AgwMAgsgDCALt2NFDQEgBQJ/IAyZRAAAAAAAAOBBYwRAIAyqDAELQYCAgIB4CzYCDAwBCyAFQQA2AgwgAkECTgRAIAAgBUEMaiADKQMIIAdBABBlDQILIAcgBmshBEEBIQgLQX8hAiAGIAdLDQEgBCAFKAIMIgNrIAhsQQBIDQEDQCAKIAkgA0EAIAYQwgNFBEAgAyECDAMLIAMgBEYNAiADIAhqIQMMAAsACyAAIAEQDCAAIA0QDEKAgICA4AAhAQwBCyAAIAEQDCAAIA0QDCACrSEBCyAFQRBqJAAgAQuGAQIBfgF/IwBBEGsiAiQAAkAgACABEGMiBBANBEAgBCEBDAELQoCAgIDgACEBAkAgACACQQxqIAMpAwAQxQENAEKAgICAMCEBIAIoAgwiA0EASA0AIAMgBKciBSgCBEH/////B3FPDQAgBSACQQxqENsBrSEBCyAAIAQQDAsgAkEQaiQAIAELTAEBfyACQQAgAkEAShshAiAAIAEQYyEBA0ACQCACIARGDQAgARANDQAgACABIAMgBEEDdGopAwAQDxDJAiEBIARBAWohBAwBCwsgAQu7AQIBfwF+IwBBEGsiAiQAAkAgACABEGMiBRANBEAgBSEBDAELAn5CgICAgOAAIAAgAkEMaiADKQMAEMUBDQAaAkAgAigCDCIDQQBOBEAgAyAFpyIEKQIEIgGnQf////8HcUkNAQsgAEEAQQAQ2AIMAQsgBEEQaiEEIAACfyABQoCAgIAIg1BFBEAgBCADQQF0ai8BAAwBCyADIARqLQAAC0H//wNxEKYDCyEBIAAgBRAMCyACQRBqJAAgAQurAQIBfwJ+IwBBEGsiAiQAAkAgACABEGMiBRANBEAgBSEBDAELQoCAgIDgACEBAkAgACACQQxqIAMpAwAQxQENAEKAgICAwH4hASACKAIMIgNBAEgNACADIAWnIgQpAgQiBqdB/////wdxTw0AIARBEGohBCAGQoCAgIAIg1BFBEAgBCADQQF0ajMBACEBDAELIAMgBGoxAAAhAQsgACAFEAwLIAJBEGokACABC5ECAgF/Bn4jAEEgayIEJAAgACAEQQhqQQAQQhpCgICAgDAhBQJ+AkACQCAAIAMpAwAQKyIGEA0NACAAIAAgBkHwACAGQQAQFBCWBSIFEA0NACAAIAQgBRBBQQBIDQBCACEBIAQpAwAiB0IAIAdCAFUbIQggB0IBfSEHIAKsIQkDQCABIAhRDQIgACAAIAUgARBkED0iChANDQEgBEEIaiAKEI8BGiABIAdZIQIgAUIBfCEBIAEgCVkgAnINACAEQQhqIAMgAadBA3RqKQMAEJwBRQ0ACwsgACAGEAwgACAFEAwgBEEIahBEQoCAgIDgAAwBCyAAIAYQDCAAIAUQDCAEQQhqEDkLIQEgBEEgaiQAIAEL6wECA38BfCMAQSBrIgQkAAJ+AkAgACAEIAIQQg0AIAJBACACQQBKGyEGAkADQCAFIAZHBEACQCADIAVBA3RqKQMAIgFC/////w9YBEAgAaciAkH//8MATQ0BDAQLIAAgBEEYaiABEEcNBCAEKwMYIgdEAAAAAAAAAABjIAdEAAAAAP//MEFkcg0DIAcCfyAHmUQAAAAAAADgQWMEQCAHqgwBC0GAgICAeAsiArdiDQMLIAVBAWohBSAEIAIQwAFFDQEMAwsLIAQQOQwCCyAAQYkYEGsLIAQQREKAgICA4AALIQEgBEEgaiQAIAELigEBAn8jAEEgayIEJAAgACAEQQhqIAIQQhogAkEAIAJBAEobIQICfgNAIAIgBUcEQAJAIAAgBEEEaiADIAVBA3RqKQMAEJMCRQRAIARBCGogBC8BBBCWAUUNAQsgBEEIahBEQoCAgIDgAAwDCyAFQQFqIQUMAQsLIARBCGoQOQshASAEQSBqJAAgAQsJACAAIAEQzwQLHwAgACABEM8EIgEQDQR+IAEFIABBA0ECIAGnGxAyCwuBAQEBfCMAQRBrIgIkAAJ+QoCAgIAQIAMpAwAiARCQAUUNABpCgICAgOAAIAAgAkEIaiABEEcNABogAisDCCIEvUKAgICAgICA+P8Ag0KAgICAgICA+P8AUiAEnCAEYXEgBJlE////////P0Nlca1CgICAgBCECyEBIAJBEGokACABCyYAQoCAgIDgACAAIAMpAwAQ0wUiAEEAR61CgICAgBCEIABBAEgbCyAAIAMpAwAQkAFFBEBCgICAgBAPCyAAIAEgAiADENIECyAAIAMpAwAQkAFFBEBCgICAgBAPCyAAIAEgAiADENMECwkAIAAgARCvAgvFAQIBfwF+IwBBEGsiAiQAAn4gACABEK8CIgEQDQRAIAEMAQtBCiEFAkACQCAEDQAgAykDACIGEBINACMAQRBrIgMkAEF/IQQCQCAAIANBDGogBhDFAQ0AIAMoAgwiBEEla0FcSw0AIABBrfAAEGtBfyEECyADQRBqJAAgBCIFQQBIDQELQoCAgIDgACAAIAJBCGogARBbDQEaIAAgAisDCCAFQQBBABDMAgwBCyAAIAEQDEKAgICA4AALIQEgAkEQaiQAIAELwwECAX4BfCMAQRBrIgIkAAJAIAAgARCvAiIEEA0EQCAEIQEMAQtCgICAgOAAIQEgACACIAQQWw0AAkACQCADKQMAIgQQEgRAIAIrAwAhBQwBCyAAIAJBDGogBBDFAQ0CIAIrAwAiBb1CgICAgICAgPj/AINCgICAgICAgPj/AFINAQsgACAFEBcQPSEBDAELIAIoAgwiA0HlAGtBm39NBEAgAEHhHxBrDAELIAAgBUEKIANBARDMAiEBCyACQRBqJAAgAQuaAQIBfgF8IwBBEGsiAiQAAkAgACABEK8CIgQQDQRAIAQhAQwBC0KAgICA4AAhASAAIAIgBBBbDQAgACACQQxqIAMpAwAQxQENACACKAIMIgNB5QBPBEAgAEHhHxBrDAELIAIrAwAiBZlEUO/i1uQaS0RmBEAgACAFEBcQPSEBDAELIAAgBUEKIANBAhDMAiEBCyACQRBqJAAgAQvPAQMBfwF+AXwjAEEQayICJAACQCAAIAEQrwIiBRANBEAgBSEBDAELQoCAgIDgACEBIAAgAiAFEFsNACAAIAJBDGogAykDABDFAQ0AIAIrAwAiBr1CgICAgICAgPj/AINCgICAgICAgPj/AFEEQCAAIAYQFxA9IQEMAQsgAgJ/IAMpAwAQEgRAQQQhA0EADAELIAIoAgwiBEHlAE8EQCAAQeEfEGsMAgtBBSEDIARBAWoLIgQ2AgwgACAGQQogBCADEMwCIQELIAJBEGokACABC3sBAn9CgICAgDAhAQJAIAJBA2tBfkkNACAAIAMpAwBCgICAgDBCgICAgDAQ/gMiARANDQAgACABEKYBIQQgACABEAwgBEUEQEKAgICA4AAPCyAEIAJBAkYEfyAAIAMpAwgQ+QEFQQALEAYgACAEEDdCgICAgDAhAQsgAQubAgIDfwF+IwBBEGsiBCQAIARBADoAD0KAgICAMCEBAkAgAkEDa0F+SQ0AAkAgACADKQMAEKYBIgVFDQACQCACQQJHDQAgACADKQMIQoCAgIAwQoCAgIAwEP4DIgcQDQRAIAAgBRA3IAchAQwDCyAAIAcQpgEhBiAAIAcQDCAGDQAgACAFEDcMAQsgBSAGIARBD2oQByECIAAgBRA3IAAgBhA3IAJFDQECfiAELQAPRQRAIAAgAiACEENB1u8AEP8DDAELAkAgAEEDEKQBIgEQDQRAQoCAgIAgIQEMAQsgACABQTMgACACEHZBAxAbGgsgACABEJQBQoCAgIDgAAshASACEOkBDAELQoCAgIDgACEBCyAEQRBqJAAgAQtUACMAQRBrIgAkACAAQQhqELAEAn4gADQCDCAANAIIQsCEPX58IgFCgICAgAh8Qv////8PWARAIAFC/////w+DDAELIAG5EBcLIQEgAEEQaiQAIAELwQMCBX8CfiMAQSBrIgUkACAAIAVBCGoiBkEAEEIaIAZBKBA+GiAEQX5xQQJGBEAgBUEIakGN/wAQjgEaCyAFQQhqQcg2EI4BGiAEQX1xQQFGBEAgBUEIakEqED4aCyAFQQhqQcv5ABCOARpBACEGIAJBAWsiB0EAIAdBAEobIQgCQAJAAkADQCAGIAhHBEAgBgRAIAVBCGpBLBA+GgsgBkEDdCEJIAZBAWohBiAFQQhqIAMgCWopAwAQnAFFDQEMAgsLIAVBCGpBov8AEI4BGiACQQBKBEAgBUEIaiADIAdBA3RqKQMAEJwBDQELIAVBCGoiAkGl9gAQjgEaQoCAgIAwIQsgAhA5IgoQDQ0BIAAgACkDwAEgCkEDQX8QmQMhCyAAIAoQDCALEA0NASABEBINAiAAIAFBOyABQQAQFCIKEA0NASAKECJFBEAgACAKEAwgACABEI8DIgJFDQIgAigCKCAEQQF0QdaiAWovAQBBA3RqKQMAEA8hCgsgACALIApBARCbAiECIAAgChAMIAJBAE4NAgwBCyAFQQhqEERCgICAgDAhCwsgACALEAxCgICAgOAAIQsLIAVBIGokACALC/MBAQR/IwBBIGsiAiQAIAAgAykDABAuIgEQDUUEQCAAIAJBCGpBABBCGiABpyIFKAIEQf////8HcSEGQQAhAwNAIAMgBk5FBEACQCAFIAMQTSIEQSVHDQACQCADQQZqIAZKDQAgBSADQQFqEE1B9QBHDQAgBSADQQJqQQQQwwMiBEEASA0AIANBBWohAwwBC0ElIQQgA0EDaiAGSg0AIAUgA0EBakECEMMDIgRBJSAEQQBOIgcbIQQgA0ECaiADIAcbIQMLIAJBCGogBBCWARogA0EBaiEDDAELCyAAIAEQDCACQQhqEDkhAQsgAkEgaiQAIAELsAEBA38jAEEgayICJAAgACADKQMAEC4iARANRQRAIAAgAkEIaiABpyIFKAIEQf////8HcRBCGiAFKAIEQf////8HcSEGQQAhAwNAIAMgBkcEQAJAIAUgAxBNIgRB/wFMBH9BgMEBIARBxQAQpQIFQQALBEAgAkEIaiAEEJYBGgwBCyACQQhqIAQQhQILIANBAWohAwwBCwsgACABEAwgAkEIahA5IQELIAJBIGokACABC+UDAQV/IwBBIGsiBiQAAkAgACADKQMAEC4iARANDQAgACAGQQhqIAGnIggoAgRB/////wdxEEIaQQAhAwJAA0AgCCgCBEH/////B3EiCSADSgRAIANBAWohAkEAIQcCQCAIIAMQTSIFQf8BSg0AQQEhByAFQTBrQQpJIAVBX3FBwQBrQRpJcg0AQcH5ACAFQQkQpQINAEEAIQcgBA0AIAUQ0ARBAEchBwsgBwRAIAZBCGogBRCWARogAiEDDAILAkAgBUGA+ANxIgdBgLADRwRAIAdBgLgDRw0BQfwuIQcMBAtBoSwhByACIAlODQMgCCACEE0iAkGAwANrQYB4SQ0DIAVBCnRBgPg/cSACQf8HcXJBgIAEaiEFIANBAmohAgsgBUH/AEwEQCAGQQhqIAUQhQIFIAZBCGoiAyAFQf8PTQR/IAVBBnZBwAFyBSAGQQhqIAVB//8DTQR/IAVBDHZB4AFyBSAGQQhqIAVBEnZB8AFyEIUCIAVBDHZBP3FBgAFyCxCFAiAFQQZ2QT9xQYABcgsQhQIgAyAFQT9xQYABchCFAgsgAiEDDAELCyAAIAEQDCAGQQhqEDkhAQwBCyAAIAcQxAMgACABEAwgBkEIahBEQoCAgIDgACEBCyAGQSBqJAAgAQvLAwEFfyMAQSBrIgckAAJAIAAgAykDABAuIgEQDQ0AIAAgB0EIakEAEEIaIAGnIQhBACECA0ACQAJAAkAgCCgCBEH/////B3EgAkoEQCAIIAIQTSIDQSVGBEAgACAIIAIQ0QQiA0EASA0DIAJBA2ohBSADQf8ATQRAIAQEQCAFIQIMBgtBJSADIAMQ0AQiBhshAyACQQFqIAUgBhshAgwFCwJ/IANBYHFBwAFGBEAgA0EfcSEDQYABIQZBAQwBCyADQXBxQeABRgRAIANBD3EhA0GAECEGQQIMAQsgA0F4cUHwAUcEQEEBIQZBACEDQQAMAQsgA0EHcSEDQYCABCEGQQMLIQIDQCACQQBMDQMgACAIIAUQ0QQiCUEASA0EIAVBA2ohBSAJQcABcUGAAUcEQEEAIQMMBAUgAkEBayECIAlBP3EgA0EGdHIhAwwBCwALAAsgAkEBaiECDAMLIAAgARAMIAdBCGoQOSEBDAQLIAUhAiADIAZIIANB///DAEpyRSADQYBwcUGAsANHcQ0BIABBnfAAEMQDCyAAIAEQDCAHQQhqEERCgICAgOAAIQEMAgsgB0EIaiADEMABGgwACwALIAdBIGokACABC84BAgF/An4jAEEQayICJAACQEG4swQpAwBQDQBBtLMEKAIAIAAgABBDEP4BIQNBtLMEKAIAIAEgARBDQczvABD/AyIEQcCzBCgCABCnAwRAQbSzBCgCACAEEAxBtLMEKAIAIAMQDAwBCyACIAQ3AwggAiADNwMAQbSzBCgCAEG4swQpAwBCgICAgDBBAiACECQhA0G0swQoAgAgAikDABAMQbSzBCgCACACKQMIEAwgA0HAswQoAgAQpwMaQbSzBCgCACADEAwLIAJBEGokAAs3ACAAIAMpAwAQpgEiAkUEQEKAgICA4AAPCyAAIAIQiAMgAmpBAEEKQQAQxAIhASAAIAIQNyABC4gBAQF/IwBBEGsiAiQAAkAgACADKQMAEKYBIgRFBEBCgICAgOAAIQEMAQsCfkKAgICA4AAgACACQQxqIAMpAwgQkwINABogAigCDCIDBEBCgICAgMB+IANBJWtBXUkNARoLIAAgBBCIAyAEakEAIANBgQgQxAILIQEgACAEEDcLIAJBEGokACABC8cBAgN+An8jAEEQayIHJABCgICAgOAAIQUCQAJ+IAEQtQEEQCAHIAKtNwMIIAAgAUEBIAdBCGoQsgEMAQsgABBRCyIEEA0NACACQQAgAkEAShutIQZCACEBAkADQCABIAZSBEAgACAEIAEgAyABp0EDdGopAwAQD0GAgAEQrgEhCCABQgF8IQEgCEEATg0BDAILCyAAIARBMCACQQBOBH4gAq0FIAK4EBcLEEhBAEgNACAEIQUMAQsgACAEEAwLIAdBEGokACAFC70GAgJ/CH4jAEEwayIEJAAgAykDACEGQoCAgIAwIQogBEKAgICAMDcDGEEBIQUCQAJAAkACQAJ+IAJBAkgEQEKAgICAMCEMQoCAgIAwDAELAkAgAykDCCIMEBINACAAIAwQaQ0CQQAhBSACQQNJDQAgAykDEAwBC0KAgICAMAshDSAAIAZBwwEgBkEAEBQiBxANDQACQAJAAkACQCAHEBJFBEAgACAHEAwCfiABELUBBEAgACABQQBBABCyAQwBCyAAEFELIggQDQRAQoCAgIAwIQEMBwsgBCAGEA83AxAgACAEQRBqQQhyQQAQlwMhAiAEKQMYIQogBCkDECEBIAINBgNAIAAgASAKIARBCGoQrwEiBhANDQIgBCgCCARAQoCAgIAwIQsMBgsCQCAFBEAgBiEHDAELIAQgBjcDICAEIAlC/////w+DNwMoIAAgDCANQQIgBEEgahAkIQcgACAGEAwgBxANDQMLIAAgCCAJIAcQcEEASA0CIAlCAXwhCQwACwALIAAgBhArIgsQDQ0CIAAgBEEIaiALEEFBAEgNAiAEAn4gBCkDCCIGQoCAgIAIfEL/////D1gEQCAGQv////8PgwwBCyAGuRAXCyIJNwMgAn4gARC1AQRAIAAgAUEBIARBIGoQsgEMAQsgAEKAgICAMEEBIARBIGoQ6QILIQggACAJEAwgCBANDQFCACEBIAZCACAGQgBVGyEJA0AgASAJUQRAQoCAgIAwIQEMBQsgACALIAEQZCIGEA0NAgJAIAUEQCAGIQcMAQsgBCAGNwMgIAQgAUL/////D4M3AyggACAMIA1BAiAEQSBqECQhByAAIAYQDCAHEA0NAwsgACAIIAEgBxBwIQIgAUIBfCEBIAJBAE4NAAsMAQsgARASDQQgACABQQEQswEaDAQLQoCAgIAwIQEMBAtCgICAgDAhAUKAgICAMCEIDAMLIAAgCEEwIAmnIgJBAE4EfiAJQv////8PgwUgArgQFwsQSEEASA0CDAMLQoCAgIAwIQFCgICAgDAhCAtCgICAgDAhCwsgACAIEAxCgICAgOAAIQgLIAAgCxAMIAAgARAMIAAgChAMIARBMGokACAICyYAQoCAgIDgACAAIAMpAwAQwgEiAEEAR61CgICAgBCEIABBAEgbC4ICAgF/BH4jAEEQayIFJABCgICAgDAhBgJAAkAgACAFQQhqIAAgARArIggQQQ0AIAVBATYCBAJAIAQEQCADKQMAIQlCgICAgDAhByACQQJOBEAgAykDCCEHCyAAIAkQaUUNAQwCC0KAgICAMCEJIAJBAEwEQEKAgICAMCEHDAELQoCAgIAwIQcgAykDACIBEBINACAAIAVBBGogARDFAUEASA0BCyAAIAhCABCwAiIBEA0EQCABIQYMAQsgASEGIAAgASAIIAUpAwhCACAFKAIEIAkgBxDUBEIAUw0AIAghBgwBCyAAIAgQDEKAgICA4AAhAQsgACAGEAwgBUEQaiQAIAEL6QECBH4BfyMAQSBrIggkAAJAAkAgACAIQRhqIAAgARArIgEQQQ0AIAAgCEEIaiADKQMAQgAgCCkDGCIEIAQQgQENACAAIAhBEGogAykDCEIAIAQgBBCBAQ0AIAggBDcDAAJ+IAQgAkEDSA0AGiAEIAMpAxAiBRASDQAaIAAgCCAFQgAgBCAEEIEBDQEgCCkDAAshByAAIAEgCCkDCCIFIAgpAxAiBiAHIAZ9IAQgBX0QvQIiBEF/QQEgBSAEIAZ8UxtBASAFIAZVGxCCA0UNAQsgACABEAxCgICAgOAAIQELIAhBIGokACABCz0AAkAgARASDQAgAacgABCCBKdGDQAgACABQQEQbw8LIAMpAwAiARBWQX5xQQJGBEAgABA8DwsgACABECsL7QYCCH4CfyMAQTBrIg0kAEKAgICAMCEFAkACQCAAIA1BIGogACABECsiChBBDQAgACANQRhqIAMpAwBCACANKQMgIgYgBhCBAQ0AAkAgBgJ/AkAgBARAAkACQCACDgIDAAELIAYgDSkDGH0hCAwCCyAAIA1BCGogAykDCEIAIAYgDSkDGH1CABCBAQ0EIA0pAwghCCACQQJrDAILIA0gBjcDECAGIQEgAykDCCILEBJFBEAgACANQRBqIAtCACAGIAYQgQENBCANKQMQIQELQQAhAiABIA0pAxh9ENUEIQgMAgsgDSAINwMIQQALIgKtfCAIfUKAgICAgICAEFMNACAAQarDAEEAEBYMAQsgACAKIAhCgICAgAh8Qv////8PWAR+IAhC/////w+DBSAIuRAXCyIFELACIQEgACAFEAwCQCABEA0NACANIA0pAxgiCyAIfCIJNwMQAkAgCiANQQRqIA0QjgJFBEAgCyEFDAELIAshBQJ/QQAgAUKAgICAcFQNABogAaciDi8BBkECRgRAQQEgDi0ABUEIcQ0BGgtBAAtFDQAgDSgCBCEOIA01AgAhDANAIAUgCVkgBSAMWXINASAAIAEgByAOIAWnQQN0aikDABAPQYCAARCuAUEASA0CIAdCAXwhByAFQgF8IQUMAAsACyAFIAkgBSAJVRshCQNAIAUgCVIEQCAAIAogBSANQShqEIwBIg5BAEgNAiAOBEAgACABIAcgDSkDKEGAgAEQrgFBAEgNAwsgB0IBfCEHIAVCAXwhBQwBCwsgACABQTAgB0KAgICACFoEfiAHuRAXBSAHCxBIQQBIDQAgBARAIAYgAq0iB3wgCH0hCQJAIAcgCFENACAAIAogByALfCAIIAt8IgUgBiAFfUF/QQEgByAIVRsQggNBAEgNAgNAIAYgCVcNASAAIAogBkIBfSIGEJQCQQBODQALDAILQgAhBQNAIAUgB1IEQCAFIAt8IQYgBachAiAFQgF8IQUgACAKIAYgAkEDdCADaikDEBAPEJEBQQBODQEMAwsLIAEhBSAAIApBMCAJQoCAgIAIfEL/////D1gEfiAJQv////8PgwUgCbkQFwsQSEEASA0CCyAKIQUMAgsgASEFCyAAIAoQDEKAgICA4AAhAQsgACAFEAwgDUEwaiQAIAELvQIDAn4FfwF8IwBBIGsiBSQAAkAgAigCBA0AIAIoAgAhBgJAAkACfyACKAIIBEAgACkAACABKQAAUQ0CIAUgACkDADcDECAFIAEpAwA3AxggBiACKQMQQoCAgIAwQQIgBUEQahAkIgMQDQ0DIANC/////w9YBEAgA6ciAkEfdSACQQBKagwCCyAGIAVBCGogAxBbQQBIDQMgBSsDCCIKRAAAAAAAAAAAZCAKRAAAAAAAAAAAY2sMAQsgACgCCCIIRQRAIAYgACkDABAuIgMQDQ0DIAAgA6ciCDYCCAsgASgCCCIJBH8gCAUgBiABKQMAEC4iAxANDQMgASADpyIJNgIIIAAoAggLIAkQlQILIgcNAgsgACkDECIDIAEpAxAiBFUgAyAEU2shBwwBCyACQQE2AgQLIAVBIGokACAHC40FAgV+BH8jAEEwayIKJAAgCkIANwIcIAogADYCGCAKIAMpAwAiBTcDKAJAAkACfwJAAkACQCAFEBJFBEAgACAFEGkEQEKAgICAMCEBQQAhAgwCCyAKQQE2AiALQQAhAiAAIApBEGogACABECsiARBBRQ0BCwwBCwNAIAopAxAiByAEVQRAIAkgC00EQCAAIAIgCSAJQQF2akEfakFwcSIJQRhsIApBDGoQtwEiA0UNAyAKKAIMQRhuIAlqIQkgAyECC0EAIAAgASAEIAIgC0EYbGoiDBCMASIDQQBIDQMaAkAgA0UNACAMKQMAEBIEQCAGQgF8IQYMAQsgDCAENwMQIAxBADYCCCALQQFqIQsLIARCAXwhBAwBCwsgAiALQRhBOCAKQRhqEK4CQQAgCigCHA0BGiALrSEFQgAhBANAAkAgBCAFUgRAIAIgBKciCUEYbGoiAygCCCIMBEAgACAMrUKAgICAkH+EEAwLIAMpAwAhCCAEIAMpAxBRBEAgACAIEAwMAgsgACABIAQgCBCRAUEATg0BIAlBAWoMBAsgACACEBogBSAGfCAGQj+HIAaDfSEEA0AgBCAFUQRAIAQgByAEIAdVGyEFA0AgBCAFUQ0IIAAgASAEEJQCIQIgBEIBfCEEIAJBAE4NAAsMBgsgACABIAVCgICAgDAQkQEhAiAFQgF8IQUgAkEATg0ACwwECyAEQgF8IQQMAAsAC0EACyEDIAsgAyADIAtJGyELA0AgAyALRwRAIAAgAiADQRhsaiIJKQMAEAwgCSgCCCIJBEAgACAJrUKAgICAkH+EEAwLIANBAWohAwwBCwsgACACEBoLIAAgARAMQoCAgIDgACEBCyAKQTBqJAAgAQuzAwICfgJ/IwBBMGsiAiQAIAJCgICAgDA3AygCQAJ+QoCAgIAwIAAgAkEQaiAAIAEQKyIBEEENABoCQAJAAkAgASACQRxqIAJBDGoQjgJFBEAgAikDECEFDAELIAIpAxAiBSACKAIMIgOtUQ0BCwNAIAQgBUIBfSIFWQ0EAkACQCAAIAEgBCACQShqEIwBIgNBAEgNACAAIAEgBSACQSBqEIwBIgZBAEgNAAJAAkAgBgRAIAAgASAEIAIpAyAQkQFBAEgNAyADRQ0CIAAgASAFIAIpAygQkQFBAE4NAQwHCyADRQ0DIAAgASAEEJQCQQBIDQIgACABIAUgAikDKBCRAUEASA0GCyACQoCAgIAwNwMoDAILIAAgASAFEJQCQQBODQELIAIpAygMBAsgBEIBfCEEDAALAAsgA0ECSQ0CQQAhACACKAIcIQYDQCAAIANBAWsiA08NAyAGIABBA3RqIgcpAwAhBCAHIAYgA0EDdGoiBykDADcDACAHIAQ3AwAgAEEBaiEADAALAAtCgICAgDALIQQgACAEEAwgACABEAxCgICAgOAAIQELIAJBMGokACABC2wBAX5CgICAgOAAIQQgACABECsiARANRQRAAn5CgICAgOAAIAAgAUHbACABQQAQFCIEEA0NABogACAEEDtFBEAgACAEEAwgACABQQBBABDYBAwBCyAAIAQgAUEAQQAQNgshBCAAIAEQDAsgBAvWAgICfwR+IwBBIGsiBSQAAn4CQCAAIAUgACABECsiCRBBDQBBLCEGQoCAgIAwIQgCQCACQQBMIARyRQRAQQAhAiADKQMAIgEQEg0BIAAgARAuIggQDQ0CQX8hBiAIpyICKAIEQQFHDQEgAi0AECEGDAELQQAhAgsgACAFQQhqQQAQQhpCACEBIAUpAwAiB0IAIAdCAFUbIQoCQANAIAEgClIEQAJAIAFQDQAgBkEATgRAIAVBCGogBhA+GgwBCyAFQQhqIAJBACACKAIEQf////8HcRBZGgsgACAJIAGnEHsiBxANDQICQCAHECgNACAHEBINACAFQQhqIAQEfiAAIAcQ1gQFIAcLEI8BDQMLIAFCAXwhAQwBCwsgACAIEAwgACAJEAwgBUEIahA5DAILIAVBCGoQRCAAIAgQDAsgACAJEAxCgICAgOAACyEBIAVBIGokACABC/QBAgF/An4jAEEgayIEJAACfgJAAkACQCAAIARBEGogACABECsiBRBBDQAgBCkDECIGQgBXDQEgBCAGQgF9IgE3AwggAkECTgRAIAAgBEEIaiADKQMIQn8gASAGEIEBDQEgBCkDCCEBCwNAIAFCAFMNAiAAIAUgASAEQRhqEIwBIgJBAEgNASACBEAgACADKQMAEA8gBCkDGEEAEN8BDQQLIAFCAX0hAQwACwALIAAgBRAMQoCAgIDgAAwCC0J/IQELIAAgBRAMIAFC/////w+DIAFCgICAgAh8Qv////8PWA0AGiABuRAXCyEBIARBIGokACABC/YCAgF/BH4jAEEgayIEJAACfgJAAkAgACAEQRBqIAAgARArIgcQQQ0AQn8hBiAEKQMQIghCAFcNASAEQgA3AwggAkECTgRAIAAgBEEIaiADKQMIQgAgCCAIEIEBDQELAkAgByAEQQRqIAQQjgJFBEAgBCkDCCEBDAELIAQpAwgiBSAENQIAIgEgASAFUxshASAEKAIEIQIDQCABIAVSBEAgACADKQMAEA8gAiAFp0EDdGopAwAQD0EAEN8BBEAgBSEGDAUFIAVCAXwhBQwCCwALCyAEIAE3AwgLIAEgCCABIAhVGyEFA0AgASAFUQ0CIAAgByABIARBGGoQjAEiAkEASA0BAkAgAkUNACAAIAMpAwAQDyAEKQMYQQAQ3wFFDQAgASEGDAMLIAFCAXwhAQwACwALIAAgBxAMQoCAgIDgAAwBCyAAIAcQDCAGQv////8PgyAGQoCAgIAIfEL/////D1gNABogBrkQFwshASAEQSBqJAAgAQvZAgIIfgF/IwBBMGsiDSQAQoCAgIAwIQYCQAJAIAAgDUEIaiAAIAEQKyIHEEEEQEKAgICAMCEFDAELQoCAgIAwIQUgACADKQMAIgoQaQ0AQoCAgIAwIQkgAkECTgRAIAMpAwghCQsgDSkDCCIFQgAgBUIAVRshCwNAIAggC1IEQCAIIgVCgICAgAhaBEAgCLkQFyEFCyAFEA0NAiAAIAcgBRChASIGEA0NAiANIAE3AyAgDSAFNwMYIA0gBjcDECAAIAogCUEDIA1BEGoQJCIMEA0NAiAAIAwQLQRAIAQEQCAAIAYQDCAAIAcQDAwFCyAAIAUQDCAAIAcQDCAGIQUMBAUgACAGEAwgACAFEAwgCEIBfCEIDAILAAsLIAAgBxAMQv////8PQoCAgIAwIAQbIQUMAQsgACAFEAwgACAGEAwgACAHEAxCgICAgOAAIQULIA1BMGokACAFC/cBAgF/An4jAEEgayIEJAACQAJAIAAgBEEYaiAAIAEQKyIGEEENACAEQgA3AxACQCACQQFMBEAgBCAEKQMYIgU3AwgMAQsgBCkDGCEFIAMpAwgiARASRQRAIAAgBEEQaiABQgAgBSAFEIEBDQILIAQgBTcDCCACQQNJDQAgAykDECIBEBINACAAIARBCGogAUIAIAUgBRCBAQ0BIAQpAwghBQsgBCkDECIBIAUgASAFVRshBQNAIAEgBVENAiAAIAYgASADKQMAEA8QkQEhAiABQgF8IQEgAkEATg0ACwsgACAGEAxCgICAgOAAIQYLIARBIGokACAGC9EEAgN/CH4jAEFAaiIFJABCgICAgDAhCiAFQoCAgIAwNwM4IAVCgICAgDA3AzACQAJAAkAgBEEIcSIGBEAgBSAAIAEQDyILEJgBIgesNwMIIAdBAE4NAQwCCyAAIAVBCGogACABECsiCxBBDQELIAAgAykDACINEGkNAAJAIAJBAUwEQEIAIQEgBSkDCCIMQgAgDEIAVRshCSAEQQFxIQQDQCABIAlRBEAgAEHxDEEAEBYMBAsgDCABQn+FfCABIAQbIQggAUIBfCEBIAYEQCAFIAAgCyAIEGQiCDcDMCAIEA0NBAwDCyAAIAsgCCAFQTBqEIwBIgJBAEgNAyACRQ0ACyAFKQMwIQgMAQsgBEEBcSEEQgAhASADKQMIEA8hCCAFKQMIIQwLIAEgDCABIAxVGyEOA0AgASAOUQ0CIAwgAUJ/hXwgASAEGyEJAkACQAJAIAYEQCAFIAAgCyAJEGQiCjcDOCAKEA1FDQEMAwsgACALIAkgBUE4ahCMASICQQBIDQIgAkUNAQsgCUKAgICACHxC/////w9YBH4gCUL/////D4MFIAm5EBcLIgoQDQ0BIAUgCDcDECAFIAs3AyggBSAKNwMgIAUgBSkDOCIPNwMYIAAgDUKAgICAMEEEIAVBEGoQJCEJIAAgChAMIAAgDxAMIAVCgICAgDA3AzggCRANDQEgACAIEAwgCSEICyABQgF8IQEMAQsLIAUgCDcDMCAFKQM4IQoLIAAgBSkDMBAMIAAgChAMQoCAgIDgACEICyAAIAsQDCAFQUBrJAAgCAuwBgIDfwl+IwBBMGsiBSQAQoCAgIAwIQggBUKAgICAMDcDKAJAAkACQAJAIARBCHEiBgRAIAUgACABEA8iCRCYASIHrDcDCCAHQQBODQEMAgsgACAFQQhqIAAgARArIgkQQQ0BCyADKQMAIQ5CgICAgDAhDSACQQJOBEAgAykDCCENCyAAIA4QaQ0AAkACQAJAAkACQAJAAkAgBA4NBQAGAQIGBgYFAAYDBAYLQoCAgIAQIQgMBQsgACAJAn4gBSkDCCIBQoCAgIAIfEL/////D1gEQCABQv////8PgwwBCyABuRAXCxCwAiIIEA1FDQQMBQsgACAJQgAQsAIiCBANRQ0DDAQLIAUgCTcDECAFIAU1Agg3AxggAEECIAVBEGoQ7AIiCBANRQ0CDAMLIAAQUSIIEA1FDQEMAgtCgYCAgBAhCAtCACEBIAUpAwgiCkIAIApCAFUbIRADQCABIBBSBEACQAJAIAYEQCAFIAAgCSABEGQiCjcDKCAKEA1FDQEMBQsgACAJIAEgBUEoahCMASICQQBIDQQgAkUNAQsgASEKIAFCgICAgAhaBEAgAbkQFyEKCyAKEA0NAyAFIAk3AyAgBSAKNwMYIAUgBSkDKCIPNwMQIAAgDiANQQMgBUEQahAkIQsgACAKEAwgCxANDQMCQAJAAkACQAJAAkACQCAEDg0AAQUCBAUFBQABBQMEBQsgACALEC0NBUKAgICAECEBDAsLIAAgCxAtRQ0EQoGAgIAQIQEMCgsgACAIIAEgCxBwQQBODQMMBwsgACAIIAFC/////w+DIAtBgIABEOEBQQBODQIMBgsgACALEC1FDQEgACAIIAwgDxAPEHBBAEgNBSAMQgF8IQwMAQsgACALEAwLIAAgDxAMIAVCgICAgDA3AygLIAFCAXwhAQwBCwsgBEEMRwRAIAghAQwDCyAFIAk3AxAgBSAMQv////8PgzcDGCAAQQIgBUEQahDsAiIBEA0NACAFIAg3AxAgACAAIAFBwgBBASAFQRBqEMYCEI0CRQ0BC0KAgICA4AAhAQsgACAIEAwLIAAgBSkDKBAMIAAgCRAMIAVBMGokACABC7kDAgV+A38jAEEQayIJJABCgICAgDAhBQJAAkAgACABECsiCBANDQAgACAIQgAQsAIiBRANDQBBfyEKIAJBfyACQQBOGyECAkADQCACIApHBEAgCCEBAn9BACAKQQBOBH4gAyAKQQN0aikDAAUgAQsiBhAiRQ0AGiAAIAZBygEgBkEAEBQiARANBH9BfwUgARASRQRAIAAgARAtDAILIAAgBhDCAQsLIgtBAEgNAwJAIAsEQCAAIAkgBhBBDQUgCSkDACIHIAR8Qv////////8PVQ0EQgAhASAHQgAgB0IAVRshBwNAIAEgB1ENAiAAIAYgASAJQQhqEIwBIgtBAEgNBiALBEAgACAFIAQgCSkDCBBwQQBIDQcLIARCAXwhBCABQgF8IQEMAAsACyAEQv7///////8PVQ0DIAAgBSAEIAYQDxBwQQBIDQQgBEIBfCEECyAKQQFqIQoMAQsLIAAgBUEwIARCgICAgAh8Qv////8PWAR+IARC/////w+DBSAEuRAXCxBIQQBIDQEMAgsgAEGqwwBBABAWCyAAIAUQDEKAgICA4AAhBQsgACAIEAwgCUEQaiQAIAULLQEBfkKAgICAMCECAkAgARCoAyIARQ0AIAAtABJBBHFFDQAgADUCRCECCyACCzMCAX4Bf0KAgICAMCECAkAgARCoAyIDRQ0AIAMtABJBBHFFDQAgACADKAJAEDIhAgsgAgsoAEKAgICA4AAgACADKQMAIAEQ2QUiAEEAR61CgICAgBCEIABBAEgbC6sBAgF+An9CgICAgOAAIQQgACABEGkEfkKAgICA4AAFQeb+ACECAkAgAaciAy8BBhD4AUUNAAJAIAMoAiAiAy8AESIFQYAIcUUNACADKAJUIgZFDQAgACAGIAMoAkgQ/gEPCyAFQQR2QQNxQQFrIgNBAksNACADQQJ0QaDdAWooAgAhAgsgACACIAAgAUE2IAFBABAUIgEQEgR+IABBLxAyBSABC0GeCBC/AQsLhwQDA34EfwN8AkAgACABEGkNACAAIAApAzBBDhBTIgUQDQ0AIAWnIgkgARC1AUEEdEEQcSAJLQAFQe8BcXI6AAUCQCAAQQAgAkEBaxBKIgJBA3RBGGoQLyIHRQ0AIAcgARAPIgE3AwAgAykDABAPIQQgByACNgIQIAcgBDcDCCACQQAgAkEAShshCgNAIAggCkcEQCAHIAhBA3RqIAMgCEEBaiIIQQN0aikDABAPNwMYDAELCyAJIAc2AiACfyABQv////9vWARAIAAQKUF/DAELIABBACABp0EwEE8LIgNBAEgNAAJAIANFDQAgACABQTAgAUEAEBQiBBANDQEgBEL/////D1gEQCAEpyIDIAJrQQAgAiADSButIQYMAQsgBBBWQQdGBEACQCAEEEkiDL1C////////////AINCgICAgICAgPj/AFYNACAMnSIMIAK3Ig1lDQAgDCANoSELCyALvQJ/IAuZRAAAAAAAAOBBYwRAIAuqDAELQYCAgIB4CyICt71RBEAgAq0hBgwCCyALEBchBgwBCyAAIAQQDAsgACAFQTAgBkEBEBsaIAAgAUE2IAFBABAUIgEQDQ0AIABB8P4AIAEQngEEfiABBSAAIAEQDCAAQS8QMgtB3IMBEL8BIgEQDQ0AIAAgBUE2IAFBARAbGiAFDwsgACAFEAwLQoCAgIDgAAswACACQQBMBEAgACABQoCAgIAwQQBBABAkDwsgACABIAMpAwAgAkEBayADQQhqECQLvwECAX4BfyMAQSBrIgIkAEKAgICA4AAhBQJAAkAgACABECsiARANDQAgACADKQMAEDgiA0UNAANAIAAgAiABpyADEE8iBkEASA0CIAYEQEKAgICAMCEFIAItAABBEHEEQCACQRhBECAEG2opAwAQDyEFCyAAIAIQTgwDCyAAIAEQmQIiARANDQIgARAoBEBCgICAgDAhBQwDCyAAEIIBRQ0ACwwBC0EAIQMLIAAgAxATIAAgARAMIAJBIGokACAFC6QBAQN+IAMpAwghBSADKQMAIQZCgICAgOAAIQcCQCAAIAEQKyIBEA0EfkKAgICA4AAFIAAgBRBpDQEgACAGEDgiAkUNASAAIAEgAkKAgICAMEKAgICAMCAFIAQbIAVCgICAgDAgBBtBhaoBQYWaASAEGxB4IQMgACABEAwgACACEBNCgICAgOAAQoCAgIAwIANBAEgbCw8LIAAgARAMQoCAgIDgAAtSAAJAIAEQEkUEQCABEChFDQELIAAQKUKAgICA4AAPCwJAIAIQIg0AIAIQKA0AQoCAgIAwDwtCgICAgOAAQoCAgIAwIAAgASACQQEQmwJBAEgbCyUBAX4gACABECsiARANBEAgAQ8LIAAgARD8ASECIAAgARAMIAILkwECAX4BfyMAQSBrIgIkAEKAgICA4AAhBAJAAkAgACABECsiARANDQAgACADKQMAEDgiA0UNACAAIAIgAacgAxBPIgVBAEgNASAFRQRAQoCAgIAQIQQMAgsgAjUCACEEIAAgAhBOIARCAohCAYNCgICAgBCEIQQMAQtBACEDCyAAIAMQEyAAIAEQDCACQSBqJAAgBAuIAQECfiADKQMAIgUQIkUEQEKAgICAEA8LAkAgACABECsiBBANRQRAIASnIQIgBRAPIQEDQCAAIAEQmQIiARANRQRAIAEQKCIDIAIgAadGcg0DIAAQggFFDQELCyAAIAEQDCAAIAQQDAtCgICAgOAADwsgACABEAwgACAEEAwgA0WtQoCAgIAQhAtlAQF+QoCAgIDgACEEAkAgACADKQMAEDgiAkUNACAAIAEQKyIBEA0EQCAAIAIQEyABDwsgAEEAIAGnIAIQTyEDIAAgAhATIAAgARAMIANBAEgNACADQQBHrUKAgICAEIQhBAsgBAtAAAJ+AkAgARCoAyICRQ0AIAItABBBAXENAEKAgICAMCACLQARQQFxDQEaCyAAIAFBAEEAENsEGkKAgICA4AALCwgAIAAgARArCw8AIAAgAUE3QQBBABDGAgtnACAAIAMpAwAQKyIBEA0EfiABBQJAAkAgACADKQMIEDgiAkUEQCAAIAEQDAwBCyAAQQAgAacgAhBPIQMgACACEBMgACABEAwgA0EATg0BC0KAgICA4AAPCyADQQBHrUKAgICAEIQLC5wCAQV+IwBBEGsiAiQAIAMpAwAhBQJAIAAQPCIBEA0EQCABIQUMAQtCgICAgDAhBwJAAkAgACAFQQAQ9gEiBBANDQAgACAEQeoAIARBABAUIgcQDQ0AA0AgACAEIAcgAkEMahCvASIGEA0NASACKAIMBEAgASEFDAMLAkACQCAGECJFBEAgABApDAELIAAgBkEAEHsiCBANDQAgACAGQQEQeyIFEA0EQCAAIAgQDAwBCyAAIAEgCCAFQYeAARDNAkEATg0BCyAAIAYQDAwCCyAAIAYQDAwACwALQoCAgIDgACEFIAQQIgRAIAAgBEEBELMBGgsgByEGIAQhByABIQQLIAAgBhAMIAAgBxAMIAAgBBAMCyACQRBqJAAgBQtIAEEvIQIgACADKQMAIgEQVkF/RgR/IAGnLwEGIgJBKUYEQEENQSkgACABEDsbIQILIAAoAhAoAkQgAkEYbGooAgQFQS8LEDIL8QECBH8BfiMAQTBrIgIkAAJAIAMpAwAiCRAiRQRAQoGAgIAQIQEMAQtCgICAgOAAIQEgACACQSxqIAJBKGogCaciCEEDEJIBDQAgAigCLCEGIAIoAighB0EAIQMCQANAIAMgB0cEQCAAIAJBCGogCCAGIANBA3RqKAIEEE8iBUEASA0CAkAgBUUNACAAIAJBCGoQTiACKAIIIgVBAXFFIARFIAVBAnFFcnENAEKAgICAECEBDAMLIANBAWohAwwBCwsgACAJEKIBIgNBAEgNASADQQFHrUKAgICAEIQhAQsgACAGIAcQZgsgAkEwaiQAIAELnQECAX4Bf0KAgICAMCEBAkACQCAAIAMpAwAQKyIEEA0NACACQQEgAkEBShshBUEBIQIDQCACIAVGDQICQCADIAJBA3RqKQMAIgEQKA0AIAEQEg0AIAAgARArIgEQDQ0CIAAgBCABQoCAgIAwQQEQxgUNAiAAIAEQDAsgAkEBaiECDAALAAsgACAEEAwgACABEAxCgICAgOAAIQQLIAQLGAAgACADKQMAIAMpAwgQWq1CgICAgBCEC5sCAgN+A38jAEEgayICJABCgICAgOAAIQQgACADKQMAECsiBRANRQRAQoCAgIAwIQECfgJAIAAgAkEcaiACQRhqIAWnQQMQkgENACAAEDwiARANDQAgAigCHCEHIAIoAhghCEEAIQMDQCADIAhHBEAgACAHIANBA3RqIgkoAgQQYCIGEA0NAiACIAY3AwggAiAFNwMAIABCgICAgDBBAiACQQAQ2QQhBCAAIAYQDCAEEA0NAiAEEBJFBEAgACABIAkoAgQgBEGHgAEQG0EASA0DCyADQQFqIQMMAQsLIAAgByAIEGYgAQwBCyAAIAIoAhwgAigCGBBmIAAgBRAMIAEhBUKAgICA4AALIQQgACAFEAwLIAJBIGokACAEC20AAn4CQCADKQMAIgFC/////29YBEAgBEUNASAAEClCgICAgOAADwtCgICAgOAAIAAgARCZBCICQQBIDQEaIAQEQCACQQBHrUKAgICAEIQPCyACDQAgAEHdygBBABAWQoCAgIDgAA8LIAEQDwsLTwACQAJAIAMpAwAiAUL/////b1gEQCAERQRAQoCAgIAQDwsgABApDAELIAAgARCiASIAQQBODQELQoCAgIDgAA8LIABBAEetQoCAgIAQhAsQACAAIAMpAwBBAkEAEIEDCxAAIAAgAykDAEEBQQAQgQMLLQEBfkKAgICA4AAhASAAIAMpAwAiBCADKQMIENwEBH5CgICAgOAABSAEEA8LC30BAn4gAykDACIBQv////9vWARAIAAQKUKAgICA4AAPCyADKQMQIQZCgICAgOAAIQUCQCAAIAMpAwgQOCICRQ0AIAAgASACIAYgBEVBDnQQ2gQhAyAAIAIQEyADQQBIDQAgBARAIANBAEetQoCAgIAQhA8LIAEQDyEFCyAFCycAIAAgAykDACIBIAMpAwhBARCbAkEASARAQoCAgIDgAA8LIAEQDws2ACADKQMAIgFCIIinIgJBf0YgBEUgAkF+cUECR3FyRQRAIAAQKUKAgICA4AAPCyAAIAEQ/AELYgEBfgJAIAMpAwAiARAiDQAgARAoDQAgAEGczABBABAWQoCAgIDgAA8LAkAgACABEFUiARANRQRAIAMpAwgiBBASDQEgACABIAQQ3ARFDQEgACABEAwLQoCAgIDgAA8LIAELuQEBAn4gARAiRQRAIAAQKUKAgICA4AAPC0KAgICA4AAhBQJ+IAAgAUE2IAFBABAUIgQQEgRAIABBjgEQMgwBCyAAIAQQPQsiBBANBH5CgICAgOAABQJ+IAAgAUEzIAFBABAUIgEQEgRAIABBLxAyDAELIAAgARA9CyIBEA0EQCAAIAQQDEKAgICA4AAPCwJAIAQQ9wENACABEPcBDQAgAEHcgwEgBEGU/wAQvwEhBAsgACAEIAEQyQILC2oCAX8BfkGwswQoAgAEQBCBBQtBsLMEENYFIgI2AgAgAhDgBCECQcCzBCABNgIAQbSzBCACNgIAIAIgACAAEENBoO8AELYFIgMgARCnAwRAQbSzBCgCACADEAxBAA8LQbizBCADNwMAQQELvgICA38BfCMAQdAAayIEJAAgBEEQakEAQTgQSxogBEKAgICAgICA+D83AyBCgICAgMB+IQECQCACRQ0AIAJBByACQQdIGyICQQAgAkEAShshAgNAIAIgBUcEQCAAIARBCGogAyAFQQN0IgZqKQMAEEcEQEKAgICA4AAhAQwDCyAEKwMIIge9QoCAgICAgID4/wCDQoCAgICAgID4/wBRDQIgBEEQaiAGaiAHnTkDAAJAIAUNACAEKwMQIgdEAAAAAAAAAABmRSAHRAAAAAAAAFlAY0VyDQAgBCAHRAAAAAAAsJ1AoDkDEAsgBUEBaiEFDAELCyAEQRBqQQAQ+QMiB70CfyAHmUQAAAAAAADgQWMEQCAHqgwBC0GAgICAeAsiALe9UQRAIACtIQEMAQsgBxAXIQELIARB0ABqJAAgAQsIAEKAgICAMAsnABCrBSIBQoCAgIAIfEL/////D1gEQCABQv////8Pgw8LIAG5EBcLvwEBAn4jAEEQayICJAACfgJAIAAgACABECsiAUEBEJsDIgUQDQ0AIAUQkAEEQCAAIAJBCGogBRBHQQBIDQFCgICAgCAgAikDCEKAgICAgICA+P8Ag0KAgICAgICA+P8AUQ0CGgsgACABQZnFABDPAiIEEA0NACAAIAQQO0UEQCAAQcDZAEEAEBYgACAEEAwMAQsgACAEIAFBAEEAEDYMAQtCgICAgOAACyEEIAAgARAMIAAgBRAMIAJBEGokACAEC90BAgF8AX4jAEEQayICJABCgICAgOAAIQUCQCAAIAJBCGogARC5Ag0AIAAgAkEIaiADKQMAEEcNACACAn4gAisDCCIEvUKAgICAgICA+P8Ag0KAgICAgICA+P8AUgRAIASdIgREAAAAAACwnUCgIAQgBEQAAAAAAABZQGMbIAQgBEQAAAAAAAAAAGYbIQQLIAS9An8gBJlEAAAAAAAA4EFjBEAgBKoMAQtBgICAgHgLIgO3vVEEQCADrQwBCyAEEBcLNwMAIAAgAUEBIAJBERD9BCEFCyACQRBqJAAgBQtRAQF+IwBBEGsiAiQAQoCAgIDgACEEAkAgACACQQhqIAEQuQINACAAIAJBCGogAykDABBHDQAgACABIAIrAwgQ+AMQ/gQhBAsgAkEQaiQAIAQLqQEBAXwjAEHQAGsiAiQAAn5CgICAgOAAIAAgASACIARBD3FBABDdAyIAQQBIDQAaQoCAgIDAfiAARQ0AGiAEQYACcQRAIAIgAisDAEQAAAAAALCdwKA5AwALIAIgBEEEdkEPcUEDdGorAwAiBb0CfyAFmUQAAAAAAADgQWMEQCAFqgwBC0GAgICAeAsiBLe9UQRAIAStDAELIAUQFwshASACQdAAaiQAIAELhQEBAXwjAEEQayICJAACfkKAgICA4AAgACACQQhqIAEQuQINABpCgICAgMB+IAIrAwgiBL1C////////////AINCgICAgICAgPj/AFYNABoCfiAEnSIEmUQAAAAAAADgQ2MEQCAEsAwBC0KAgICAgICAgIB/CxDcA60LIQEgAkEQaiQAIAELdAEBfgJAIAEQIkUEQCAAECkMAQsCQCADKQMAIgQQngFFDQAgACAEEDgiAkUNASAAIAIQE0ERIQMCQAJAAkAgAkHGAGsOAwIDAQALIAJBFkcNAgtBECEDCyAAIAEgAxCbAw8LIABBqhhBABAWC0KAgICA4AALaAEBfCMAQRBrIgIkAAJ+QoCAgIDgACAAIAJBCGogARC5Ag0AGiACKwMIIgS9An8gBJlEAAAAAAAA4EFjBEAgBKoMAQtBgICAgHgLIgC3vVEEQCAArQwBCyAEEBcLIQEgAkEQaiQAIAELxQEBAX8gBEEBcSEGIAUpAwBBMhBAIgIoAgQhBSADKQMAIQECQAJAAkAgBEECTgRAIAVBfnFBBEcNAiACQQU2AgQgBgRAIAAgAiABEN4DDAILIAAgAiABQQEQ/gIMAQsgBUEDRw0CIAIgBjYCFCABEA8hAQJAIAYEQCAAIAEQlAEMAQsgAigCREEIayABNwMACyAAIAIQggULQoCAgIAwDwtBre4AQb7jAEHTmQFB5zUQAAALQZjsAEG+4wBB3JkBQec1EAAAC4MCAgJ/An4jAEEgayICJAAgAUEyEEAhBgJAIAAgAkEQahCQAyIBEA1FBEAgBkUEQCAAQewbQQAQFiACIAAQkwE3AwggACACKQMYIgdCgICAgDBBASACQQhqECQhCCAAIAIpAwgQDCAAIAgQDCAAIAIpAxAQDCAAIAcQDAwCCyAAQTAQbCIFBEAgBSAENgIIIAUgAykDABAPNwMQIAUgARAPIgE3AxggBSACKQMQNwMgIAUgAikDGDcDKCAFIAZByABqEEwgBigCBEEDRg0CIAAgBhCCBQwCCyAAIAIpAxAQDCAAIAIpAxgQDCAAIAEQDAtCgICAgOAAIQELIAJBIGokACABCxgAIAAgAykDABAPIAAgBSkDABD5ARCTAwvdBAICfwJ+IwBBMGsiBSQAAkACQAJAIAAgBUEgahCQAyIIEA1FBEAgAUEwEEAiBkUEQCAAQbEqQQAQFgwCCwJAIARFBEAgBikDCBAPIQEMAQsgACAGKQMAIgFBBkEXIARBAUYbIAFBABAUIgEQDQ0CIAEQEkUEQCABEChFDQELQQEhAiADKQMAEA8hASAEQQFGBEAgBSAAIAFBARCTAzcDAEEAIQIMBAsgBSABNwMADAMLIAUgACAGKQMAIAEgAkEASiADIAVBFGoQlQUiBzcDGCAAIAEQDCAHEA0NAQJAIAUoAhRBAkcEQCAHIQEMAQsgBSAAIAcgBUEUahCfBSIBNwMYIAAgBxAMIAEQDQ0CCyABEA0NASAAIAApA1BBASAFQRhqQQAQjAIiARANBEAgACAFKQMYEAwMAgsgBSgCFCEDIwBBEGsiAiQAIAIgA0EAR61CgICAgBCENwMIIABBNkEBQQBBASACQQhqEOYBIQcgAkEQaiQAIAUgBzcDAAJAIAcQDUUEQCAAIAUpAxgQDCAFQoCAgIAwNwMIIAAgASAFIAVBIGoQuwIhAiAAIAcQDCAAIAEQDCAAIAUpAyAQDCAAIAUpAygQDCACDQEMBQsgACABEAwgACAFKQMYEAwgACAFKQMgEAwgACAFKQMoEAwLIAAgCBAMC0KAgICA4AAhCAwCCyAFIAAQkwE3AwBBASECCyAAIAVBIGogAkEDdHIpAwBCgICAgDBBASAFECQhASAAIAUpAwAQDCAAIAEQDCAAIAUpAyAQDCAAIAUpAygQDAsgBUEwaiQAIAgLBgAgARAPC/ECAQV+IwBBMGsiAiQAAkAgARAiRQRAIAAQKUKAgICA4AAhBQwBCyAAIAJBIGogARDDAiIFEA0NAEKAgICAMCEGQoCAgIAwIQQCQAJAIAAgAUGAASABQQAQFCIIEA0NACAAIAgQaQ0AIAAgAykDAEEAEPYBIgQQDQRADAELIAAgBEHqACAEQQAQFCIGEA0NAANAIAIgACAEIAYgAkEUahCvASIHNwMYIAcQDQ0BIAIoAhQNAiAAIAggAUEBIAJBGGoQJCEHIAAgAikDGBAMIAcQDUUEQCAAIAAgB0H/AEECIAJBIGoQugIQjQJFDQELCyAAIARBARCzARoLIAIgABCTATcDCCAAIAIpAyhCgICAgDBBASACQQhqECQhASAAIAIpAwgQDCAAIAUgASABEA0iAxsQDEKAgICA4AAgBSADGyEFCyAAIAgQDCAAIAYQDCAAIAQQDCAAIAIpAyAQDCAAIAIpAygQDAsgAkEwaiQAIAUL9gICBX4BfyMAQSBrIgIkACAAIAUpAwAQ+QEhCyACIAUpAxAiBzcDGCAFKQMgIQkgBSkDGCEIQoCAgIDgACEBAkAgACACQRRqIAUpAwgQkwINAAJAIAsNACAFQoGAgIAQNwMAAkAgBEEDcSIFQQFGBEAgABA8IgYQDQ0DAkAgAEG33wBB5uEAIARBBHEiBBsQdiIKEA0NACAAIAZBiAEgCkEHEBtBAEgNACAAIAZBiQFBwAAgBBsgAykDABAPQQcQG0EATg0CCyAAIAYQDAwDCyADKQMAEA8hBgsgACAHIAIoAhQgBkEHEJ8BQQBIDQEgACAJQX8Q4AMiA0EASA0BIANFDQACQCAFQQJGBEAgAiAAIAcQgwUiBjcDCCAGEA0NAyAAIAhCgICAgDBBASACQQhqECQhASAAIAIpAwgQDAwBCyAAIAhCgICAgDBBASACQRhqECQhAQsgARANDQEgACABEAwLQoCAgIAwIQELIAJBIGokACABC8AGAg5+AX8jAEHwAGsiAiQAIAJCgICAgDA3A1ACQCABECJFBEAgABApQoCAgIDgACEIDAELIAAgAkHgAGogARDDAiIIEA0NAEKAgICAMCEJQoCAgIAwIQZCgICAgDAhBwJAAkAgACABQYABIAFBABAUIg8QDQ0AIAAgDxBpDQACQCAAIAMpAwBBABD2ASIHEA0EQAwBCyAAIAdB6gAgB0EAEBQiCRANDQAgAiAAEFEiCjcDUCAKEA0NACAAEFEiBhANDQEgACAGQQBCAUEHEJ8BQQBIDQEgAkHgAGogBEECRkEDdHIhAyACKQNgIRIgAikDaCEQAkACQAJAA0AgAiAAIAcgCSACQQxqEK8BIgU3A1ggBRANDQUgAigCDEUEQCAAIA8gAUEBIAJB2ABqECQhDSAAIAIpA1gQDCANEA0NBCACIAo3AyAgAiAMNwMYIAJCgICAgBA3AxAgAykDACEFIAIgBjcDMCACIAU3AyggAEE1QQEgBEEFIAJBEGoQ5gEiBRANDQICQCAEQQFGBEAgAEE1QQFBBUEFIAJBEGoQ5gEiCxANDQQMAQsCQCAEQQJHBEAgBSERIBAiDiEFDAELIBIiDiERIAAgCiAMp0KAgICAMEEHEJ8BQQBIDQYLIAUhCyAOEA8aIBEhBQsgACAGQQEQ4ANBAEgEQCAAIA0QDCAAIAUQDAwECyACIAs3A0ggAiAFNwNAIAAgDUH/AEECIAJBQGsQugIhDiAAIAUQDCAAIAsQDCAMQgF8IQwgACAOEI0CRQ0BDAQLCyAAIAZBfxDgAyITQQBIDQQgE0UNBSAEQQJGBEAgACAKEIMFIgEQDQ0FIAAgChAMIAIgATcDUAsgACAAIAMpAwBCgICAgDBBASACQdAAahAkEI0CDQQMBQsgDSELCyAAIAsQDAsgACAHQQEQswEaDAELCyACIAAQkwE3AwAgACACKQNoIhBCgICAgDBBASACECQhASAAIAIpAwAQDCAAIAggASABEA0iAxsQDEKAgICA4AAgCCADGyEICyAAIA8QDCAAIAYQDCAAIAIpA1AQDCAAIAkQDCAAIAcQDCAAIAIpA2AQDCAAIBAQDAsgAkHwAGokACAICwkAIAUpAwAQDwsVACAAIAUpAwAQDxCUAUKAgICA4AALpgEBAX4jAEEQayICJAAgBSkDACEGIAIgACAFKQMIQoCAgIAwQQBBABAkIgE3AwgCQCABEA0NACAAIAZBASACQQhqQQAQjAIhBiAAIAIpAwgQDCAGEA0EQCAGIQEMAQsgAiAAQTNBNCAEG0EAQQBBASADEOYBIgE3AwAgACABEA0EfiAGBSAAIAZB/wBBASACELoCIQEgAikDAAsQDAsgAkEQaiQAIAEL8QEBAn4jAEEgayICJAAgAykDACEEAkAgACABQoCAgIAwEPMBIgUQDQ0AAkAgACAEEDtFBEAgAiAEEA8iBDcDECACIAQQDzcDGAwBCyACIAQ3AwggAiAFNwMAQQAhAwNAIANBAkYNASACQRBqIANBA3RqIABBMkEBIANBAiACEOYBIgQ3AwAgBBANBEAgA0EBRgRAIAAgAikDEBAMCyAAIAUQDEKAgICA4AAhBQwDBSADQQFqIQMMAQsACwALIAAgBRAMIAAgAUH/AEECIAJBEGoQxgIhBSAAIAIpAxAQDCAAIAIpAxgQDAsgAkEgaiQAIAULOQAjAEEQayICJAAgAkKAgICAMDcDACACIAMpAwA3AwggACABQf8AQQIgAhDGAiEBIAJBEGokACABC6UBAgF/A34jAEEQayICJABCgICAgOAAIQUCQCAAIAFBKhBqRQ0AIAAgAUKAgICAMBDzASIGEA0EQCAGIQUMAQsgACACIAYQwwIhByAAIAYQDAJAIAcQDQ0AIAAgASADIAIQuwIhAwNAIARBAkZFBEAgACACIARBA3RqKQMAEAwgBEEBaiEEDAELCyADRQ0AIAAgBxAMDAELIAchBQsgAkEQaiQAIAUL4AECA34BfyMAQRBrIgYkACABQQVGBEAgAikDECEDIAAgAikDGBD5ASEBIAYgAikDICIENwMIIAYCfiADEBIEQCAEEA8iAyABRQ0BGiAAIAMQlAFCgICAgOAADAELIAAgA0KAgICAMEEBIAZBCGoQJAsiAzcDACADEA0iAQRAIAYgABCTASIDNwMAC0KAgICAMCEEIAAgAiABQQN0aikDACIFEBIEfiADBSAAIAVCgICAgDBBASAGECQhBCAGKQMACxAMIAZBEGokACAEDwtB8vAAQb7jAEHw6QJBjOQAEAAAC4EBAQN/AkAgAUEyEEAiBEUNACAEQcwAaiEDIARByABqIQUDQCADKAIAIgMgBUZFBEAgACADKQMQIAIQIyAAIAMpAxggAhAjIAAgAykDICACECMgACADKQMoIAIQIyADQQRqIQMMAQsLIAQoAgRBfnFBBEYNACAAIARBCGogAhDvAwsLFgEBfyABQTIQQCICBEAgACACEK0FCwslAQF/IAFBMBBAIgMEQCAAIAMpAwAgAhAjIAAgAykDCCACECMLCycBAX8gAUEwEEAiAgRAIAAgAikDABAnIAAgAikDCBAnIAAgAhAhCwsWAQF/IAGnKAIgIgIEQCAAIAIQrgULCygBAX8gAacoAiAiAgRAIAAgAigCCBCFBSAAIAIpAwAQJyAAIAIQIQsLgAEBBH8gAUEqEEAiBgRAA0AgBEECRkUEQCAGIARBA3RqIgVBCGohAyAFQQRqIQUDQCADKAIAIgMgBUZFBEAgACADKQMIIAIQIyAAIAMpAxAgAhAjIAAgAykDGCACECMgA0EEaiEDDAELCyAEQQFqIQQMAQsLIAAgBikDGCACECMLC2kBBX8gAUEqEEAiBARAA0AgA0ECRkUEQCAEIANBA3RqIgJBBGohBSACKAIIIQIDQCACIAVGRQRAIAIoAgQhBiAAIAIQvAIgBiECDAELCyADQQFqIQMMAQsLIAAgBCkDGBAnIAAgBBAhCwtXAQF/QQAhAgN+IAJBAkYEQEKAgICAMA8LIAUgAkEDdCIEaiIGKQMAEBIEfiAGIAMgBGopAwAQDzcDACACQQFqIQIMAQUgAEGgGkEAEBZCgICAgOAACwsL0QIBA38jAEEQayIHJAACfiAAIAEgBUEjahBqIgJFBEAgBEEANgIAQoCAgIDgAAwBCwJAIAIpAwAiARASDQAgASAFQR9qEEAiAwRAAkAgAigCDCIIRQRAIAMoAgghBgwBCyAIKAIUIQYgACgCECAIEPEDCyADQQRqIQgDQCAGIAhGBEAgAkEANgIMIAAgAikDABAMIAJCgICAgDA3AwAMAwsgBkEQayEDIAZBDGsoAgAEQCADKAIUIQYMAQsLIAMgAygCAEEBajYCACACIAM2AgwgBEEANgIAIAIoAggiAkUEQCADKQMgEA8MAwsgByADKQMgIgE3AwAgBUUEQCADKQMoIQELIAcgATcDCCACQQFGBEAgARAPDAMLIABBAiAHEJEDDAILQdHqAEG+4wBBlugCQd0TEAAACyAEQQE2AgBCgICAgDALIQEgB0EQaiQAIAELeAECfkKAgICA4AAhBgJAIAAgASAEQQNxIgJBH2oQakUNACAAIAJBI2oQpAEiBRANDQAgAEEQEC8iAkUEQCAAIAUQDEKAgICA4AAPCyABEA8hASACQQA2AgwgAiAEQQJ1NgIIIAIgATcDACAFIAIQjQEgBSEGCyAGC5MCAgN+An8jAEEgayIIJABCgICAgOAAIQUCQCAAIAEgBEEfahBqIglFDQAgAykDACEHQoCAgIAwIQYgAkECTgRAIAMpAwghBgsgACAHEGkNACAJQQRqIQIgCSgCCCEDA0AgAiADRgRAQoCAgIAwIQUMAgsgA0EMaygCAARAIAMoAgQhAwUgA0EQayIJIAkoAgBBAWo2AgAgCCAJKQMgEA8iBTcDCCAERQRAIAkpAygQDyEFCyAIIAE3AxAgCCAFNwMAIAAgByAGQQMgCBAkIQUgACAIKQMAEAwgBEUEQCAAIAgpAwgQDAsgAygCBCEDIAAoAhAgCRDxAyAFEA0NAiAAIAUQDAsMAAsACyAIQSBqJAAgBQsxACAAIAEgAkEfahBqIgBFBEBCgICAgOAADwsgACgCDCIAQQBOBEAgAK0PCyAAuBAXC1kBAX8gACABIARBH2oQaiICRQRAQoCAgIDgAA8LIAJBBGohAyACKAIIIQQDfiADIARGBH5CgICAgDAFIARBEGshBSAEKAIEIQQgACgCECACIAUQhwUMAQsLC0kAIAAgASAEQR9qEGoiAkUEQEKAgICA4AAPCyAAIAIgAykDABCAAxD/AiIDRQRAQoCAgIAQDwsgACgCECACIAMQhwVCgYCAgBALNQAgACABIARBH2oQaiICRQRAQoCAgIDgAA8LIAAgAiADKQMAEIADEP8CQQBHrUKAgICAEIQLPgAgACABIARBH2oQaiICRQRAQoCAgIDgAA8LIAAgAiADKQMAEIADEP8CIgBFBEBCgICAgDAPCyAAKQMoEA8L+AMCA34Ff0KAgICA4AAhBwJAIAAgASAEQR9qEGoiAkUNACADKQMAEIADIQUCQCACKAIARQ0AIAUQIg0AIAAQKUKAgICA4AAPC0KAgICAMCEGIARBAXFFBEAgAykDCCEGCwJAIAAgAiAFEP8CIgQEQCAAIAQpAygQDAwBCwJAIABBMBAvIgRFDQAgBCACNgIIIARCATcDAAJAIAIoAgAEQCAEIAWnIgMoAhg2AgwgAyAENgIYDAELIAUQDxoLIAQgBTcDICAEQRhqIAIoAhAgAigCFEEBayAFEOIDcUEDdGoQTCAEQRBqIAJBBGoQTCACIAIoAgxBAWoiAzYCDCADIAIoAhhJDQAjAEEQayIIJAAgACACKAIQQQQgAigCFCIAQQF0IABBAUYbIgBBA3QgCEEMahC3ASIJBEAgCCgCDEEDdiAAaiEDQQAhAANAIAAgA0cEQCAJIABBA3RqEHEgAEEBaiEADAELCyADQQFrIQogAkEIaiEAIAJBBGohCwNAIAsgACgCACIARwRAIABBDGsoAgBFBEAgAEEQayIMQRhqIAkgDCkDIBDiAyAKcUEDdGoQTAsgAEEEaiEADAELCyACIAM2AhQgAiAJNgIQIAIgA0EBdDYCGAsgCEEQaiQACyAERQ0BCyAEIAYQDzcDKCABEA8hBwsgBwswACAFKQMAIgFBKRBAIgIEQCACQQE6ABEgACABEAwgBUKAgICAIDcDAAtCgICAgDALkQEBAn5CgICAgDAhAQJAIABCgICAgDAgAiADEIkGIgQQDQ0AIwBBEGsiAiQAIAIgBDcDCCAAQTFBAEEAQQEgAkEIahDmASEBIAJBEGokACABEA0NACAAEDwiBRANDQAgACAFQYMBIARBBxAbGiAAIAVBhAEgAUEHEBsaIAUPCyAAIAQQDCAAIAEQDEKAgICA4AAL2AICA38CfiMAQdAAayIGJABBfyEHAkAgACAGQcgAaiABQcIAEIcBIghFDQAgBikDSCIBEBIEQCAAIAgpAwAgAiADEA8gBCAFEIgEIQcMAQsCQAJAIAAgAhBgIgkQDQRAIAAgARAMDAELIAgpAwAhCiAGIAQ3AzggBiADNwMwIAYgCTcDKCAGIAo3AyAgACABIAgpAwhBBCAGQSBqEDYhASAAIAkQDCABEA0NAiAAIAEQLSIHBEAgACAGIAgoAgAgAhBPIgJBAEgNASACRQ0DAkAgBigCACICQRNxRQRAIAAgBikDCCADEFpFDQEMBAsgAkERcUEQRw0DIAYpAxgQEkUNAwsgACAGEE4gAEG/GkEAEBYMAQsgBUGAgAFxRQRAQQAhByAFQYCAAnFFDQMgABD7AUUNAwsgAEHACUEAEBYLQX8hBwwBCyAAIAYQTgsgBkHQAGokACAHC6ECAgJ/An4jAEFAaiIEJAACQAJAIAAgBEE4aiABQcEAEIcBIgVFDQAgBCkDOCIBEBIEQCAAIAUpAwAgAiADQQAQFCEBDAILIAAgAhBgIgYQDQRAIAAgARAMDAELIAUpAwAhByAEIAM3AzAgBCAGNwMoIAQgBzcDICAAIAEgBSkDCEEDIARBIGoQNiEBIAAgBhAMIAEQDQ0AIAAgBCAFKAIAIAIQTyICQQBIDQAgAkUNAQJAAkAgBCgCACICQRNxRQRAIAAgBCkDCCABEFpFDQEMAgsgAkERcUEQRw0BIAQpAxAQEkUNASABEBINAQsgACAEEE4gACABEAwgAEGWG0EAEBYMAQsgACAEEE4MAQtCgICAgOAAIQELIARBQGskACABC/UBAgN/An4jAEFAaiIDJABBfyEEAkAgACADQThqIAFB4wAQhwEiBUUNACADKQM4IgEQEgRAIAAgBSkDACACEHohBAwBCwJAAkAgACACEGAiBhANBEAgACABEAwMAQsgBSkDACEHIAMgBjcDKCADIAc3AyAgACABIAUpAwhBAiADQSBqEDYhASAAIAYQDCABEA0NAiAAIAEQLSIEDQIgACADIAUoAgAiBCACEE8iAkEASA0AIAJFDQEgAygCACECIAAgAxBOIAJBAXEEQCAELQAFQQFxDQILIABB+idBABAWC0F/IQQMAQtBACEECyADQUBrJAAgBAuyBQIDfwN+IwBBQGoiByQAQX8hCAJAIAAgB0E4aiABQeUAEIcBIglFDQAgBykDOCIKEBIEQCAAIAkpAwAgAiADIAQgBSAGEHghCAwBCwJAIAAgAhBgIgsQDQ0AAkAgABA8IgEQDQ0AIAZBgBBxBEAgACABQcEAIAQQD0EHEBsaCyAGQYAgcQRAIAAgAUHCACAFEA9BBxAbGgsgBkGAwABxBEAgACABQcAAIAMQD0EHEBsaCyAGQYAEcQRAIAAgAUE+IAZBAXZBAXGtQoCAgIAQhEEHEBsaCyAGQYAIcQRAIAAgAUE/IAZBAnZBAXGtQoCAgIAQhEEHEBsaCyAGQYACcUUNACAAIAFBPSAGQQFxrUKAgICAEIRBBxAbGgsgARANBEAgACALEAwMAQsgCSkDACEMIAcgATcDMCAHIAs3AyggByAMNwMgIAAgCiAJKQMIQQMgB0EgahA2IQogACALEAwgACABEAwgChANDQEgACAKEC1FBEBBACEIIAZBgIABcUUNAiAAQcc1QQAQFkF/IQgMAgsgACAHIAkoAgAiCSACEE8iAkEASA0BIAZBgQJxIQgCQAJAIAJFBEAgCEGAAkYNAUEBIQggCS0ABUEBcUUNAQwECwJAIAcoAgAiAiAGEKEDRSACQQFxIAhBgAJGcXINAAJAIAZBgDBxBEAgAkERcUEQRw0BIAZBgBBxBEAgACAEIAcpAxAQWkUNAwsgBkGAIHFFDQEgACAFIAcpAxgQWg0BDAILIAZBgMAAcUUNACAGQQJxRSACQQNxIgJBAkZxDQEgAg0AIAAgAyAHKQMIEFpFDQELIAZBgARxRQ0CIAcoAgBBE3FBAkcNAgsgACAHEE4LIABBiAtBABAWQX8hCAwCCyAAIAcQTkEBIQgMAQsgACAKEAwLIAdBQGskACAIC4cCAgR/An4jAEFAaiIDJABBfyEFAkAgACADQThqIAFB5AAQhwEiBEUNACADKQM4IgEQEgRAIAAgBCkDACACQQAQ3gEhBQwBCyAAIAIQYCIHEA0EQCAAIAEQDAwBCyAEKQMAIQggAyAHNwMoIAMgCDcDICAAIAEgBCkDCEECIANBIGoQNiEBIAAgBxAMIAEQDQ0AIAAgARAtIgZFBEBBACEFDAELIAAgAyAEKAIAIAIQTyICQQBIDQAgAgRAAkACQCADLQAAQQFxBEAgACAEKQMAEKIBIgJBAEgNASACDQILIABB5QpBABAWCyAAIAMQTgwCCyAAIAMQTgsgBiEFCyADQUBrJAAgBQvgBQILfwF+IwBBQGoiBSQAQX8hCwJAIAAgBUE4aiADQecAEIcBIgZFDQAgBSkDOCIDEBIEQCAAIAEgAiAGKAIAQQMQkgEhCwwBCyAAIAMgBikDCEEBIAYQNiIPEA0NACAFQQA2AiwgBUEANgI0IAVBADYCMCAAIAVBNGogDxDcASEHIAUoAjQhCgJAIAcNAAJAIApFDQAgACAKQQN0EGwiCQ0AQQAhCQwBCwJ/AkADQAJAIAQgCkYEQCAKQQEgCkEBSxshCEEBIQQDQCAEIAhGDQIgCSAEIAkgBEEDdGooAgQQiQUhByAEQQFqIQQgB0EASA0ACyAAQaIKQQAQFkEADAQLIAAgDyAEEHsiAxANDQICQCADEJ4BDQAgAxD2Aw0AIAAgAxAMIABBqCNBABAWQQAMBAsgACADEDghCCAAIAMQDCAIRQ0CIAkgBEEDdGoiB0EANgIAIAcgCDYCBCAEQQFqIQQMAQsLQQAgACAGKQMAEKIBIgxBAEgNARogBi0AEQRAIAAQywIMAQsgACAFQSxqIAVBMGogBigCAEEDEJIBBEAgBSgCMCEEIAUoAiwhCAwDCyAFKAIsIQggBSgCMCEEQQAhBwNAIAQgB0cEQCAGLQARBEAgABDLAgwFCyAAIAVBCGogBigCACAIIAdBA3RqIg0oAgQQTyIOQQBIDQQCQCAORQ0AIAAgBUEIahBOIAUtAAhBAXFBACAMGw0AIAkgCiANKAIEEIkFIg1BAEgEQCAAQZUeQQAQFgwGCyAMDQAgCSANQQN0akEBNgIACyAHQQFqIQcMAQsLAkAgDA0AQQAhBgNAIAYgCkYNASAGQQN0IQcgBkEBaiEGIAcgCWooAgANAAsgAEHTCEEAEBYMAwsgACAIIAQQZiAAIA8QDCABIAk2AgAgAiAKNgIAQQAhCwwDC0EACyEEQQAhCAsgACAIIAQQZiAAIAkgChBmIAAgDxAMCyAFQUBrJAAgCwvnAwIEfwJ+IwBB4ABrIgQkAEF/IQUCQCAAIARB2ABqIAJB5gAQhwEiBkUNACAGKAIAIQcgBCkDWCICEBIEQCAAIAEgByADEE8hBQwBCyAAIAMQYCIIEA0EQCAAIAIQDAwBCyAGKQMAIQkgBCAINwNIIAQgCTcDQCAAIAIgBikDCEECIARBQGsQNiECIAAgCBAMIAIQDQ0AAkACQAJAAkAgAhAiDQAgAhASDQAgACACEAwMAQsgACAEIAcgAxBPIgNBAEgNAiADBEAgACAEEE4LIAIQEgRAQQAhBSADRQ0EIAQtAABBAXFFDQEgBy0ABUEBcUUNAQwECyAAIAYpAwAQogEiBkEASA0CIAAgBEEgaiACEIoFIQcgACACEAwgB0EASA0DAkAgAwRAIAQoAgAiBUGAOkGAzgAgBCgCICIDQRBxGyADchChA0UNASADQQFxDQMgBUEBcQ0BIANBEnENAyAFQQJxDQEMAwsgBkUNACAELQAgQQFxDQILIAAgBEEgahBOCyAAQdwoQQAQFkF/IQUMAgsCQCABBEAgASAEKQMgNwMAIAEgBCkDODcDGCABIAQpAzA3AxAgASAEKQMoNwMIDAELIAAgBEEgahBOC0EBIQUMAQsgACACEAwLIARB4ABqJAAgBQslAQF/IAFBKRBAIgMEQCAAIAMpAwAgAhAjIAAgAykDCCACECMLCycBAX8gAUEpEEAiAgRAIAAgAikDABAnIAAgAikDCBAnIAAgAhAhCwsWACAAIAMpAwAgAykDCCADKQMQEP4DC7cBAgN+An8jAEEQayIHJAACQCAAIAdBDGogAykDABCQAiIIRQRAQoCAgIDgACEEDAELIAAgCCAHKAIMQZjvABD/AyEBIAAgCBA3AkAgARANIAJBAkhyDQAgACADKQMIIgYQO0UNAEKAgICA4AAhBAJAIAAQPCIFEA0EQCABIQUMAQsgACAFQS8gAUEHEBtBAEgNACAAIAVBLyAGEIsFIQQLIAAgBRAMDAELIAEhBAsgB0EQaiQAIAQLvQIBA34jAEEQayIDJAAgBAJ/AkACQCAAIAFBJxBqIgJFBEBCgICAgDAhAUKAgICAMCEGDAELIAIoAhgEQEKAgICAMCEBQQEMAwtCgICAgDAhBiAAIAIpAwAiCCACKQMIIgcQ2gEiARANDQAgARAoBEAgAkEBNgIYQoCAgIAwIQFBAQwDCyACKAIQBEAgACAAIAFCABBkED0iBhANDQEgBhD3AQRAIAAgA0EIaiAAIAhB1QAgCEEAEBQQsAFBAEgNAiAAIAhB1QACfiAHpyADKQMIIAIoAhQQhAMiB0KAgICACHxC/////w9YBEAgB0L/////D4MMAQsgB7kQFwsQSEEASA0CCyAAIAYQDAwCCyACQQE2AhgMAQsgACABEAwgACAGEAxCgICAgOAAIQELQQALNgIAIANBEGokACABCwYAIAEQDwuuBgIEfwt+IwBBMGsiBCQAAkAgARAiRQRAIAAQKUKAgICA4AAhAQwBC0KAgICAMCEIAkACQCAAIAMpAwAQLiIPEA0EQEKAgICAMCEKQoCAgIAwIQFCgICAgDAhDUKAgICAMCEQDAELIAAgASAAKQNIEPMBIhAQDQRAQoCAgIAwIQpCgICAgDAhAUKAgICAMCENDAELAkACQCAAIAAgAUHtACABQQAQFBA9Ig0QDQ0AIA2nIgJB9QBBABDZASEGIAJB+QBBABDZAUEASARAIABB3IMBIA1Bqw4QvwEiDRANDQELIAQgDTcDKCAEIAE3AyAgACAQQQIgBEEgahCyASIKEA0NASAAEFEiARANDQICQCADKQMIIgsQEgRAQX8hAyAEQX82AhwMAQsgACAEQRxqIAsQxwFBAEgNAyAEKAIcIgMNAAwECwJAIA+nIgcoAgRB/////wdxIgUEQCAGQX9zQR92IQYgA60hESAFrSESQgAhC0EAIQIDQCACrSEJIAIhAwNAIAMgBU8NAyAAIApB1QAgA60iDhBIQQBIDQYgACAIEAwgACAKIA8Q2gEiCBANDQYCQCAIECgNACAAIARBEGogACAKQdUAIApBABAUELABDQcgBCAEKQMQIgwgEiAMIBJTGyIMNwMQIAkgDFENACAAIAcgAiADEJ0BIgkQDQ0HIAAgASALIAkQcEEASA0HIAtCAXwiCSARUQ0IIAAgBEEIaiAIEEENByAMpyECQgEhDCALIAQpAwgiDkIBIA5CAVUbfCELA0AgCSALUQ0DIAAgACAIIAwQZBA9Ig4QDQ0IIAAgASAJIA4QcEEASA0IIAxCAXwhDCAJQgF8IgkgEVINAAsMCAsgByAOIAYQhAOnIQMMAAsACwALIAAgCiAPENoBIggQDQ0DIAgQKEUNBEIAIQtBACECCyAAIAcgAiAFIAIgBUkbIAUQnQEiCRANDQIgACABIAsgCRBwQQBODQMMAgtCgICAgDAhCgtCgICAgDAhAQsgACABEAxCgICAgOAAIQELIAAgDxAMIAAgEBAMIAAgChAMIAAgDRAMIAAgCBAMCyAEQTBqJAAgAQuZAgEEfgJ+AkAgARAiRQRAIAAQKQwBC0KAgICAMCEGAkACQCAAIAMpAwAQLiIHEA0EQEKAgICAMCEEDAELIAAgAUHVACABQQAQFCIEEA0NACAAIARCABBaRQRAIAAgAUHVAEIAEEhBAEgNAQsgACABIAcQ2gEiBRANDQEgACABQdUAIAFBABAUIgYQDQ0BAkAgACAGIAQQWgRAIAAgBBAMDAELIAAgAUHVACAEEEhBAE4NAEKAgICAMCEEDAILIAAgBxAMIAAgBhAMQv////8PIAUQKA0DGiAAIAVB1wAgBUEAEBQhASAAIAUQDCABDwtCgICAgDAhBQsgACAFEAwgACAHEAwgACAGEAwgACAEEAwLQoCAgIDgAAsLtAMCBX4BfyMAQSBrIgIkAAJAAkAgARAiRQRAIAAQKQwBC0KAgICAMCEFAkAgACADKQMAEC4iCBANBEBCgICAgDAhBEKAgICAMCEGQoCAgIAwIQcMAQsCQAJAIAAgASAAKQNIEPMBIgcQDQRAQoCAgIAwIQQMAQsgACAAIAFB7QAgAUEAEBQQPSIEEA1FDQELQoCAgIAwIQYMAQsgAiAENwMYIAIgATcDECAAIAdBAiACQRBqELIBIgYQDQ0AIAAgAkEIaiAAIAFB1QAgAUEAEBQQsAENACAAIAZB1QACfiACKQMIIgFCgICAgAh8Qv////8PWARAIAFC/////w+DDAELIAG5EBcLEEhBAEgNACAAQScQpAEiBRANDQAgAEEgEC8iA0UNACADIAg3AwggAyAGNwMAIAMgBKciCUHnAEEAENkBQX9zQR92NgIQIAlB9QBBABDZASEJIANBADYCGCADIAlBf3NBH3Y2AhQgBSADEI0BIAAgBxAMIAAgBBAMDAILIAAgCBAMIAAgBxAMIAAgBBAMIAAgBhAMIAAgBRAMC0KAgICA4AAhBQsgAkEgaiQAIAULpQMCBX4CfyMAQRBrIgIkAAJAAkAgARAiRQRAIAAQKQwBC0KAgICAMCEEAkACQCAAIAMpAwAQLiIGEA0NACAAIAAgAUHuACABQQAQFBAtIgNBAEgNAAJAIANFBEAgACABIAYQ2gEhBQwBCyAAIAAgAUHvACABQQAQFBAtIgNBAEgNASAAIAFB1QBCABBIQQBIDQEgABBRIgUQDQ0CIAanIQkDQCAAIAQQDCAAIAEgBhDaASIEEA0NAyAEEChFBEAgACAAIARCABBkED0iBxANDQQgBxD3ASEKIAAgBSAIIAcQkQFBAEgNBCAIQgF8IQggCkUNASAAIAJBCGogACABQdUAIAFBABAUELABQQBIDQQgACABQdUAAn4gCSACKQMIIAMQhAMiB0KAgICACHxC/////w9YBEAgB0L/////D4MMAQsgB7kQFwsQSEEASA0EDAELCyAIpw0AIAAgBRAMQoCAgIAgIQULIAAgBBAMIAAgBhAMDAMLQoCAgIAwIQULIAAgBRAMIAAgBBAMIAAgBhAMC0KAgICA4AAhBQsgAkEQaiQAIAULlRICC38MfiMAQZABayICJAAgAykDCCEZAkAgARAiRQRAIAAQKUKAgICA4AAhEwwBCyAAIAJByABqQQAQQhogAkEQaiIHQQA2AjAgB0KAgICAwAA3AyggByAANgIAIAcgB0EIajYCBEKAgICAMCEUQoCAgIDgACETAkACQCAAIAMpAwAQLiIVEA0EQEKAgICAMCEPQoCAgIAwIRJCgICAgDAhEUKAgICAMCEXDAELQoCAgIAwIRcCQCAAIBkQOyIKRQRAIAAgGRAuIhcQDQ0BIBenIQQLIAAgACABQe4AIAFBABAUEC0iA0EASA0AIAMEQCAAIAAgAUHvACABQQAQFBAtIgZBAEgNASAAIAFB1QBCABBIQQBIDQELAkAgBEUNACADRSAEKAIEQf////8HcXINAAJ/QQAhBEF/IAAgAUE8IAFBABAUIg8QDQ0AGiAAIA8gACkDSBBaIQcgACAPEAwCQCAHRQ0AQX8hBCAAIAFBhgEgAUEAEBQiDxANDQAgD0EwQQAQjwQhBCAAIA8QDAsgBAtFDQBBACEHIwBBMGsiBiQAQoCAgIDgACEPAkAgACABQQEQ3QEiA0UNACAAIAZBCGpBABBCGgJAIAAgFRAuIhMQDQ0AAkAgAygCBEEQaiILLQAAIgRBIXEiDEUEQCAGQgA3AyAMAQsgACABQdUAIAFBABAUIhAQDQ0BIAAgBkEgaiAQELABDQELQQAhAwJAIAstAAEiBUEATQ0AIAAgBUEDdBAvIgcNAEEAIQcMAQsgBEEQcSENIARBAXEhDiATpyIFQRBqIQkgBSkCBCIRp0EfdiEKIAYpAyAhEAJAAkADQCARQv////8HgyAQWQRAIAcgCyAJIBCnIBGnQf////8HcSAKIAAQxQQiBEEBRwRAIARBAE4EQEEAIARBAkcgDBsNBCAAIAFB1QBCABBIQQBODQQMBgsgAEGZNUEAEFAMBQsgBygCACEIIAYgBygCBCAJayAKdSIENgIsIAggCWsgCnUiCCADSgRAIAZBCGogBSADIAgQWQ0ECyAOBEACQCAIIAQiA0cNAAJAAkAgDUUNACAIIAUpAgQiEKdB/////wdxTw0AIBBCgICAgAiDQgBSDQELIAYgCEEBaiIDNgIsDAELIAUgBkEsahDbARogBigCLCEDCyAFKQIEIREgA6whECAEIQMMAgUgACABQdUAIAQiA60QSEEATg0DDAULAAsLIAYgEDcDIAsgBkEIaiAFIAMgBSgCBEH/////B3EQWQ0BIAAgExAMIAAgBxAaIAZBCGoQOSEPDAILIAYgBKw3AyALIAAgExAMIAAgBxAaIAZBCGoQRAsgBkEwaiQAIA8hEwwBCyAVpyEHIANFIQtCgICAgDAhEQNAAkACfwJAAkACQCAAIAEgFRDaASIPEA0NACAPECgNBCMAQRBrIggkAAJ/QX8gAkEQaiIDKAIwDQAaAkAgAygCKCIFIAMoAixIBEAgAygCBCEEDAELIAUgBUEBdWpBH2pBb3EiDEEDdCEEIAMoAgAhCQJAAkAgAygCBCIFIANBCGpGBEAgCUEAIAQgCEEMahC3ASIERQ0BIAQgBSkDADcDACAEIAUpAxg3AxggBCAFKQMQNwMQIAQgBSkDCDcDCAwCCyAJIAUgBCAIQQxqELcBIgQNAQsgAxCOBSADKAIAIA8QDCADQX82AjBBfwwCCyAIKAIMIQUgAyAENgIEIAMgBUEDdiAMajYCLCADKAIoIQULIAMgBUEBajYCKCAEIAVBA3RqIA83AwBBAAshAyAIQRBqJAAgCyADQQBIIgNyBEBBAkEEIAMbDAQLIAAgERAMIAAgACAPQgAQZBA9IhEQDQ0AIBEQ9wFFDQIgACACQeAAaiAAIAFB1QAgAUEAEBQQsAFBAE4NAQtCgICAgDAhD0KAgICAMCESDAYLIAAgAUHVAAJ+IAcgAikDYCAGEIQDIg9CgICAgAh8Qv////8PWARAIA9C/////w+DDAELIA+5EBcLEEgiA0EATg0AIANBHnZBAnEMAQtBAAshA0KAgICAMCEPQoCAgIAwIRIgAw4FAQUDBQAFCwtBACEGQQAhBEKAgICAMCEPQoCAgIAwIRIDQCACKAI4IARKBEAgACACQQxqIAIoAhQgBEEDdGopAwAiFhDcAUEASA0DIAAgERAMIAAgACAWQgAQZBA9IhEQDQ0DIAAgAiAAIBZB1wAgFkEAEBQQsAENAwJAIAIpAwAiECAHKQIEQv////8HgyIBVQRAIAIgATcDACABIRAMAQsgEEIAWQ0AQgAhECACQgA3AwALIAAgEhAMIAAQUSISEA0NAyAAIBJCACAREA8iERBwQQBIDQMgAigCDCIDQQEgA0EBSxsiA60hGkIBIQEDQCABIBpSBEAgACAWIAEQZCIYEA0NBSAYEBJFBEAgACAYED0iGBANDQYLIAAgEiABIBgQcCEFIAFCAXwhASAFQQBODQEMBQsLIAAgFBAMIAAgFkGHASAWQQAQFCIUEA0NAwJAIAoEQCAAIBIgGiAQQv////8PgxBwQQBIDQUgACASIANBAWqtIBUQDxBwQQBIDQUCQCAUEBINACAAIBIgA0ECaq0gFBAPIgEQcEEATg0AIAEhFAwGCyACIBI3A2ggAkKAgICAMDcDYCAAIA8QDCAAIAAgGUECIAJB4ABqQQAQmgMQPSEPDAELQoCAgIAwIQEgFBASRQRAIAAgFBArIgEQDQ0FCyACIBc3A4gBIAIgATcDgAEgAiASNwN4IAIgFTcDaCACIBE3A2AgAiAQQv////8PgzcDcCAAIA8QDCAAIAJB4ABqEI8FIQ8gACABEAwLIA8QDQ0DIAasIBBXBEAgAkHIAGoiAyAHIAYgEKcQWRogAyAPEJwBGiARpykCBEL/////B4MgEHynIQYLIARBAWohBAwBCwsgAkHIAGoiAyAHIAYgBygCBEH/////B3EQWRogAxA5IRMMAgtCgICAgDAhD0KAgICAMCESQoCAgIAwIRELIAJByABqEEQLIAJBEGoQjgUgACAXEAwgACAREAwgACASEAwgACAPEAwgACAUEAwgACAVEAwLIAJBkAFqJAAgEwuNAQAjAEEgayICJAACfgJAIAEQIkUEQCAAECkMAQsgACACQQhqIgNBABBCGiADQS8QPhoCQCADIAAgAUHsACABQQAQFBCPAQ0AIAJBCGoiA0EvED4aIAMgACABQe0AIAFBABAUEI8BDQAgAkEIahA5DAILIAJBCGoQRAtCgICAgOAACyEBIAJBIGokACABCz8BAX5CgICAgOAAIQQgACABIAMpAwAQ2gEiARANBH5CgICAgOAABSABECghAiAAIAEQDCACRa1CgICAgBCECwuAAgEDfgJAIAAgAUEBEN0BIgJFDQAgAykDCCEGAkACQCAAIAMpAwAiBEEAEN0BIgMEQCAGEBJFBEAgAEHn4ABBABAWQoCAgIDgAA8LIAM1AgBCgICAgJB/hBAPIQQgAzUCBEKAgICAkH+EEA8hBQwBC0KAgICAMCEFAn4gBBASBEAgAEEvEDIMAQsgACAEEC4LIgQQDQ0BIAAgBCAGEIQEIgUQDQ0BCyAAIAI1AgBCgICAgJB/hBAMIAAgAjUCBEKAgICAkH+EEAwgAiAFPgIEIAIgBD4CACAAIAFB1QBCABBIQQBIDQEgARAPDwsgACAEEAwgACAFEAwLQoCAgIDgAAtrAQF/IAFC/////29YBEAgABApQoCAgIDgAA8LAn4gACABQQAQ3QEiA0UEQEKAgICAMCAAIAEgACgCKCkDkAEQWg0BGiAAQRIQnANCgICAgOAADwsgAiADKAIELQAQcUEAR61CgICAgBCECwvGAwEHfyMAQSBrIgUkAAJAAkACQAJAAkAgAUL/////b1gEQCAAECkMAQsgACABIAAoAigpA5ABEFoNAiAAIAFBARDdASICDQELQoCAgIDgACEBDAMLIAIoAgAiBygCBCICQf////8HcSIDDQELIABBmvkAEHYhAQwBCyAAIAVBCGogAyACQR92EKoDGiAHKAIEQf////8HcSEIQQAhAANAAkACQCAAIAhIBEAgAEEBaiECQX8hBgJAAn8CQAJAAkACQAJAAkACQCAHIAAQTSIDQdsAaw4DAwECAAsgAiEAAkAgA0EKaw4EBAsLBQALIANBL0cNByAERQ0FQQEhBEEvIQMMBwtB3AAhAyACIAhODQYgAEECaiEAIAcgAhBNIQYMCQtBACEEQd0AIQMMBQtB2wAhAyAEIAIgCE5yDQYgAEECaiACIAcgAhBNQd0ARiICGyEAQd0AQX8gAhshBkEBIQQMBwtB7gAMAgtB8gAMAQtBACEEQS8LIQZB3AAhAwsgAiEADAILIAVBCGoQOSEBDAMLIAIhAEEBIQQLIAVBCGogAxCWARogBkEASA0AIAVBCGogBhCWARoMAAsACyAFQSBqJAAgAQvVAgIDfwF+IwBBEGsiBCQAAkAgAUL/////b1gEQCAAEClCgICAgOAAIQUMAQtCgICAgOAAIQUgACAAIAFB7gAgAUEAEBQQLSICQQBIDQAgAgR/IARB5wA6AAggBEEJagUgBEEIagshAiAAIAAgAUHQywAQzwIQLSIDQQBIDQAgAwRAIAJB6QA6AAAgAkEBaiECCyAAIAAgAUHwzAAQzwIQLSIDQQBIDQAgAwRAIAJB7QA6AAAgAkEBaiECCyAAIAAgAUG0PhDPAhAtIgNBAEgNACADBEAgAkHzADoAACACQQFqIQILIAAgACABQe8AIAFBABAUEC0iA0EASA0AIAMEQCACQfUAOgAAIAJBAWohAgsgACAAIAFB1wwQzwIQLSIDQQBIDQAgACAEQQhqIgAgAwR/IAJB+QA6AAAgAkEBagUgAgsgAGsQ/gEhBQsgBEEQaiQAIAUL1goCEX8BfiMAQRBrIgokAAJAIAAgARBjIgEQDQ0AIwBBEGsiByQAQX8hBCAAIgYgARAuIhUQDUUEQAJAIAYgFaciDSgCBEH/////B3EiCUEBEEpBAnQQLyIARQRAQQAhAAwBCyAHQQA2AgxBACEEA0AgCCAJTg0BIAAgBEECdGogDSAHQQxqENsBNgIAIARBAWohBCAHKAIMIQgMAAsACyAGIBUQDCAKIAA2AggLIAdBEGokACAGIAEQDEKAgICA4AAhASAEIgBBAEgNAAJAIAJFDQAgAykDACIVEBINAAJAIAYgCkEMaiAVEJACIgIEQAJAIAItAABBzgBHDQAgAi0AAUHGAEcNACACQQNBAiACLQACQcsARiIDG2otAAAiBEHDAGtB/wFxQQFLDQAgCigCDCACQQNqIAJBAmogAxsgAmtBAWpGDQILIAYgAhA3IAZB9DsQawsgBiAKKAIIEBoMAgsgBiACEDcgBCADQQF0akHDAGshBQsgCigCCCENIAYoAhAhAiMAQSBrIgckACAHQQhqIgMgAkErEOcCQX8hAgJAIAMgAEECdCIEEM4BDQACQCAFRQRAQQAhAyAAQQAgAEEAShshCQNAIAMgCUYNAiADQQJ0IQggA0EBaiEDIAggDWooAgBB/wFNDQALCyAHQQhqIgMgDSAAIAVBAXYQuAQgAygCDA0BIAcoAgghC0EAIQAgBygCDCIJQQJ2IgJBAWshCANAAkACQCAAIAJIBEAgCyAAIgNBAnRqKAIAEKgCRQ0BA0AgAyAIRgRAIAIhAAwDCyALIANBAWoiBEECdGooAgAiDhCoAiIPBEADQAJAIAAgA0oNACALIANBAnRqIgwoAgAiEBCoAiAPTA0AIAwgEDYCBCADQQFrIQMMAQsLIANBAnQgC2ogDjYCBCAEIQMMAQUgBCEADAMLAAsACwwBCyAAQQFqIQAMAQsLIAVBAXEgCUEISXINASACQQEgAkEBSxshEEEBIQlBASECA0AgCSAQRg0CIAsgCUECdGoiESgCACIFEKgCIQQgAiEDAkACQAJAA0AgA0EATA0BIAsgA0EBayIDQQJ0aiISKAIAIgAQqAIiCARAIAQgCEohAEGAAiEEIAANAQwCCwsCf0EAIQQgAEHMBGwgBUEcbGpBnI2hAWsgAEGAImtBEksgBUHhImtBFEtyRQ0AGgJAIABBgNgCayIDQaPXAEsNACADQf//A3FBHHAgBUGnI2siA0EbS3INACAAIANqDAELIwBBEGsiAyQAQbAHIQgDQAJAIAQgCEoEQEEAIQ4MAQsgA0EIaiAEIAhqQQJtIg9BAXRB4LQDai8BACIOQQZ2IhNBAnRB4MMCaigCACIMQQ52IhQgDkE/cWoiDiATIBQgDEEHdkH/AHEgDEEBdkE/cRC0BBogBSADKAIMayAAIAMoAggiDGsgACAMRhsiDEEASARAIA9BAWshCAwCCyAMRQ0AIA9BAWohBAwBCwsgA0EQaiQAIA4LIgANASARKAIAIQULIAsgAkECdGogBTYCACACQQFqIQIMAQsgEiAANgIACyAJQQFqIQkMAAsACyAHKAIIIgsgDSAEECUaIAAhAgsgCiALNgIEIAdBIGokACAGIA0QGiACQQBIDQAgCigCBCEDIwBBIGsiACQAAkAgBiAAQQhqIAIQQg0AQQAhBSACQQAgAkEAShshAgJAA0AgAiAFRg0BIAVBAnQhBCAFQQFqIQUgAEEIaiADIARqKAIAEMABRQ0ACyAAQQhqEEQMAQsgAEEIahA5IQELIABBIGokACAGIAooAgQQGgsgCkEQaiQAIAELuwECA38BfgJAAkAgAhBeRQ0AIAIQfCEHIAGnKQMgIgpCgICAgHCDQoCAgICQf1INACAHIAqnIggoAgRB/////wdxTw0AAkBBBCAGEKEDRQ0AQQEhAiAGQYDAAHFFDQIgA0KAgICAcINCgICAgJB/Ug0AIAOnIgkpAgRC/////weDQgFSDQAgCCAHEE0gCUEAEE1GDQILIAAgBkHG0QAQeQ8LIAAgASACIAMgBCAFIAZBgIAIchB4IQILIAILHQACfyACEF4EQEEAIAIQfCABEJoESQ0BGgtBAQsLrgEBAn8CQCADEF5FDQAgAqcpAyAiAkKAgICAcINCgICAgJB/Ug0AIAMQfCIDIAKnIgQpAgQiAqdB/////wdxTw0AQQEhBSABRQ0AIARBEGohBAJ/IAJCgICAgAiDUEUEQCAEIANBAXRqLwEADAELIAMgBGotAAALIQMgAUEENgIAIAAgA0H//wNxEKYDIQIgAUKAgICAMDcDGCABQoCAgIAwNwMQIAEgAjcDCAsgBQtoAQJ/IAGnKAIQIgMgAygCGCACcUF/c0ECdGooAgAhACADECohAwNAAkAgAEUEQEEAIQAMAQsgAEEDdCADaiIEQQhrIQAgBEEEaygCACACRg0AIAAoAgBB////H3EhAAwBCwsgAEEARwveAgECfiMAQSBrIgUkAAJAAkAgACABQSUQaiICRQ0AAkAgAikDACIBEBJFBEACQAJAIAGnIgMvAQZBFWtB//8DcUEITQRAIAMQmgFFDQEgABB1DAULIAAgBUEcaiABENwBDQQgBSgCHCEDDAELIAUgAygCKCIDNgIcCyADIAIoAgwiA0sNASAAIAIpAwAQDCACQoCAgIAwNwMACyAEQQE2AgBCgICAgDAhAQwCCyACIANBAWo2AgwgBEEANgIAIAIoAghFBEAgA0EATgRAIAOtIQEMAwsgA7gQFyEBDAILQoCAgIDgACEBIAAgAikDACADEHsiBhANDQEgAigCCEEBRgRAIAYhAQwCCyADQQBOBH4gA60FIAO4EBcLIQcgBSAGNwMIIAUgBzcDACAAQQIgBRCRAyEBIAAgBhAMIAAgBxAMDAELIARBADYCAEKAgICA4AAhAQsgBUEgaiQAIAELsQICBH8CfiMAQRBrIgEkACACKQMYIQcCQAJAIAIpAxAiCBCeAUUEQCAAQZ/5AEEAEBYMAQsgACAIEKYBIgRFBEBBACEEDAELIAAgBxCmASIFRQ0AAn8CQCAAIAQgBRC9BSIDRQ0AIAAgAxCGBEEASARAIABBARCmBEEADAILIAAgA61CgICAgFCEEA8gACkDwAFBAEEAELsFIgcQDQ0AIAAgBxAMIAMhBgsgBgshAyAAIAUQNyADRQ0AIAEgACADEIkDIgc3AwAgBxANDQAgACAAIAIpAwBCgICAgDBBASABECQQDCAAIAEpAwAQDAwBCyABIAAQkwE3AwggACAAIAIpAwhCgICAgDBBASABQQhqECQQDCAAIAEpAwgQDAsgACAEEDcgAUEQaiQAQoCAgIAwC2kBAn8jAEEQayIHJAACfwJAIAGnIggtAAVBCHFFDQAgACAHQQxqIAIQtgFFDQAgBygCDCAIKAIoTw0AQX8gACAIEKADDQEaCyAAIAEgAiADIAQgBSAGQYCACHIQeAshACAHQRBqJAAgAAtGAQJ+IAIgACgCABAyIQNBACEAIAIgASgCABAyIQQCQCADEA0NACAEEA0NACADpyAEpxCVAiEACyACIAMQDCACIAQQDCAAC2sBAX4CQAJAAkACQAJAIAMtAAUiAQ4EAwICAAELIAAgAygCCBD2BA8LIAFBCEYNAgsQAQALIAAgAygCDCADKAIAIAMtAAggAy0ACSADLgEGEMsBDwsgACAAEDwiBCADKAIIIAMoAgwQJiAECwkAIAAgAxCJAws8AQF+IAAQPCIEEA1FBEAgACAEQTwgAa1CgICAgHCEEA9BAxAbQQBOBEAgBA8LIAAgBBAMC0KAgICA4AALXwEBfwJAIAFFBEAgAkUNASAAIAIQpQUPCyACRQRAIAAgACgCAEEBazYCACAAIAAoAgRBCGs2AgQgARDpAQwBCyAAKAIIIAAoAgQgAmpPBH8gASACEPIFBUEACw8LQQALJgAgAQRAIAAgACgCAEEBazYCACAAIAAoAgRBCGs2AgQgARDpAQsLKAEBfwJAIAGnKAIgIgNFDQAgAygCAEEERg0AIAAgA0EIaiACEO8DCwscAQF/IAFBKBBAIgIEQCAAIAIQ7QMgACACECELCyUBAX8gAacoAiAiAwRAIAAgAykDACACECMgACADKQMIIAIQIwsLJwEBfyABpygCICICBEAgACACKQMAECcgACACKQMIECcgACACECELCx4BAX8gAacoAiAiAgRAIAAgAikDABAnIAAgAhAhCwtDAQJ/IAGnKAIgIgIEQAJAIAIpAwAiARDdBUUNACACKAIMIgNFDQAgACADEPEDIAIpAwAhAQsgACABECcgACACECELC1gBA38CQCABpygCICIERQ0AIARBCGohAyAEQQRqIQUDQCADKAIAIgMgBUYNASAEKAIARQRAIAAgAykDECACECMLIAAgAykDGCACECMgA0EEaiEDDAALAAsLgQEBBX8gAacoAiAiAgRAIAJBBGohBSACKAIIIQMDQCADIAVHBEAgAygCBCEGIANBEGshBCADQQxrKAIARQRAAkAgAigCAARAIAQQpgUMAQsgACAEKQMgECcLIAAgBCkDKBAnCyAAIAQQISAGIQMMAQsLIAAgAigCEBAhIAAgAhAhCwshAQF/IAGnKAIgIgMEQCAAIAM1AgxCgICAgHCEIAIQIwsLQAEBfyABpygCICICBEAgACACNQIMQoCAgIBwhCIBEN0FBH4gAhBGIAI1AgxCgICAgHCEBSABCxAnIAAgAhAhCwtbAQJ/IAGnKAIgIgIEQAJAAkAgAi0ABUUNACAAKAK8ASIDRQ0AIAAoAsQBIAIoAgggAxEDAAwBCyACKAIYIgNFDQAgACACKAIUIAIoAgggAxEGAAsgACACECELCykBAX8gACABpyICNQIkQoCAgICQf4QQJyAAIAI1AiBCgICAgJB/hBAnCxEAIAAgAacoAiApAwAgAhAjCxkBAX8gACABpygCICICKQMAECcgACACECELOgECfwJAIAFBDxBAIgRFDQADQCADIAQtAAVPDQEgACAEIANBA3RqKQMIIAIQIyADQQFqIQMMAAsACws8AQJ/IAFBDxBAIgMEQANAIAIgAy0ABU9FBEAgACADIAJBA3RqKQMIECcgAkEBaiECDAELCyAAIAMQIQsLSQECfyAAIAGnKAIgIgQpAwAgAhAjIAAgBCkDCCACECMDQCADIAQoAhBORQRAIAAgBCADQQN0aikDGCACECMgA0EBaiEDDAELCwtJAQJ/IAAgAacoAiAiAikDABAnIAAgAikDCBAnA0AgAyACKAIQTkUEQCAAIAIgA0EDdGopAxgQJyADQQFqIQMMAQsLIAAgAhAhC44BAQR/IAGnIgMoAiQhBSADKAIgIQQgAygCKCIDBEAgACADrUKAgICAcIQgAhAjCyAEBEACQCAFRQ0AQQAhAwNAIAMgBCgCPE4NAQJAIAUgA0ECdGooAgAiBkUNACAGLQAFQQFxRQ0AIAAgBiACEQMACyADQQFqIQMMAAsACyAAIAStQoCAgIBghCACECMLC3MBA38gAaciAigCKCIDBEAgACADrUKAgICAcIQQJwsgAigCICIDBEAgAigCJCIEBEBBACECA0AgAiADKAI8TkUEQCAAIAQgAkECdGooAgAQ+gEgAkEBaiECDAELCyAAIAQQIQsgACADrUKAgICAYIQQJwsLEgAgAacoAiAiAARAIAAQrgMLCw4AIAAgAacpAyAgAhAjCxkAIAAgAaciACkDIBAnIABCgICAgDA3AyALNQECfyABpyEEA0AgAyAEKAIoT0UEQCAAIAQoAiQgA0EDdGopAwAgAhAjIANBAWohAwwBCwsLPQEDfyABpyEDA0AgAygCJCEEIAIgAygCKE9FBEAgACAEIAJBA3RqKQMAECcgAkEBaiECDAELCyAAIAQQIQsIACAAIAIQIQu4AQIBfwJ+IwBBIGsiAyQAIAFBA0YEQCACKQMQIQQgAikDCCEFAkAgACADQRBqIAIpAwAQrAVBAEgEQEKAgICA4AAhBAwBCyAAIAQgBUECIANBEGoQJCIEEA0EQCADIAAQkwE3AwggACADKQMYQoCAgIAwQQEgA0EIahAkIQQgACADKQMIEAwLIAAgAykDEBAMIAAgAykDGBAMCyADQSBqJAAgBA8LQZLxAEG+4wBB1OoCQaHkABAAAAvoAQEIfyMAIgchCyABpygCICIIKAIQIglBACAJQQBKGyEMIAcgAyAJaiIKQQN0QQ9qQXBxayIHJAADfiAGIAxGBH5BACEGIANBACADQQBKGyEDA0AgAyAGRkUEQCAHIAYgCWpBA3RqIAQgBkEDdGopAwA3AwAgBkEBaiEGDAELCwJ+IAVBAXEEQCAAIAEgAhBaIQMgACAIKQMAIgEgASACIAMbIAogBxCOAwwBCyAAIAgpAwAgCCkDCCAKIAcQJAshASALJAAgAQUgByAGQQN0Ig1qIAggDWopAxg3AwAgBkEBaiEGDAELCwuHAQIBfgF/QoCAgIDgACEGAkAgAEHIABBsIgUEQCAFQQA2AgAgACAFQQhqIgcgASACIAMgBBDyAwRAIAVBBDYCAAwCCyAAIAcQwgIiAhANDQEgACACEAwgACABQSgQbyIGEA0NASAGIAUQjQELIAYPCyAAKAIQIAUQ7QMgACAFEBpCgICAgOAAC+oFAgl/AXwjAEFAaiIGJAAgAaciCC0AKSELIAgtACghCSAGIAAoAhAiDCgCjAE2AhAgDCAGQRBqNgKMASAIKAIgIQcgBiADNgI0IAYgATcDGCAGQQA2AjgCQCADIAlOBEAgBCEADAELIANBACADQQBKGyENIAYgCUEDdEEPakHwH3FrIgAkAANAIAogDUYEQCADIQQDQCAEIAlGRQRAIAAgBEEDdGpCgICAgDA3AwAgBEEBaiEEDAELCyAGIAk2AjQFIAAgCkEDdCIOaiAEIA5qKQMANwMAIApBAWohCgwBCwsLIAYgADYCICAIKAIkIQQCQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgCw4NCwIAAQABBwgDBAUGCQoLIAVBAXENCkKAgICAMCECIAtBAkcNCgwLCyAFQQFxDQBCgICAgDAhAiALQQNGDQoLIAcgAiADIAAgCC4BKiAEEQUAIQEMCwsgByACIAQRCQAhAQwKCyAHIAIgACkDACAEERYAIQEMCQsgByACIAguASogBBEPACEBDAgLIAcgAiAAKQMAIAguASogBBEvACEBDAcLIAcgBkEIaiAAKQMAEEcNBSAGKwMIIAQRCAAiD70CfyAPmUQAAAAAAADgQWMEQCAPqgwBC0GAgICAeAsiALe9UQRAIACtIQEMBwsgDxAXIQEMBgtCgICAgOAAIQEgByAGQQhqIAApAwAQRw0FIAcgBiAAKQMIEEcNBSAGKwMIIAYrAwAgBBEfACIPvQJ/IA+ZRAAAAAAAAOBBYwRAIA+qDAELQYCAgIB4CyIAt71RBEAgAK0hAQwGCyAPEBchAQwFCyAHIAIgAyAAIAZBCGogCC4BKiAEERIAIgEQDQ0EIAYoAggiAEECRg0EIAcgASAAEJMDIQEMBAsQAQALIAcgAiADIAAgBBEAACEBDAILIAdBxxBBABAWC0KAgICA4AAhAQsgDCAGKAIQNgKMASAGQUBrJAAgAQu5AQEFfyMAIgUhCCAAIAIgAyADIAFBDxBAIgYtAAQiB0gEf0EAIQAgA0EAIANBAEobIQkgBSAHQQN0QQ9qQfAfcWsiBSQAA38gACAJRgR/IAMhBAN/IAQgB0YEfyAFBSAFIARBA3RqQoCAgIAwNwMAIARBAWohBAwBCwsFIAUgAEEDdCIKaiAEIApqKQMANwMAIABBAWohAAwBCwsFIAQLIAYvAQYgBkEIaiAGKAIAERIAIQEgCCQAIAELaAEBfyMAQRBrIgMkACABKAIEIQEgAiADQQxqIAAoAgQQtgFBACACIANBCGogARC2ARtFBEBBjTFBvuMAQYM6QZw0EAAACyADKAIIIQAgAygCDCEBIANBEGokAEF/IAAgAUcgACABSxsLDwAgASABKAIAQQFqNgIACzkBAX8gASABKAIAIgJBAWo2AgAgAkUEQCABQQhqIgIQRiACIABB0ABqEEwgASABLQAEQQ9xOgAECwtYAQF/IAEoAgAiAkEASgRAIAEgAkEBayICNgIAAkAgAg0AIAEtAARB8AFxQRBHDQAgAUEIaiIBEEYgASAAQeAAahBMCw8LQZfzAEG+4wBBsCxBmNwAEAAAC4sCAgN/AX4jAEEgayIFJAACQCABpyIHKAIgIgZFDQAgBigCCCIIKAIEDQAgCEEBNgIEIAcvAQZBK2shByADQQBMBH5CgICAgDAFIAQpAwALIQECQAJAIAcNACABECJFDQACQAJAIAAgASAGKQMAEFoEQCAAQYE1QQAQFgwBCyAAIAFB/wAgAUEAEBQiAhANRQ0BCyAAEJMBIQEgACAGKQMAIAFBARCwBSAAIAEQDAwDCyAAIAIQOw0BIAAgAhAMCyAAIAYpAwAgASAHELAFDAELIAYpAwAhCSAFIAI3AxAgBSABNwMIIAUgCTcDACAAQSZBAyAFEIMDIAAgAhAMCyAFQSBqJABCgICAgDALoQEBAX4gAEHoABBsIgVFBEBCgICAgOAADwsgBUEBNgIAIAAoAhAgBUEEEL4BIAVCgICAgDA3AxggBUKAgICAMDcDECAFQQA2AiACQAJAIAAgBUEQahCQAyIGEA1FBEAgACAFQShqIAEgAiADIAQQ8gNFDQELIAAgBhAMQoCAgIDgACEGDAELIAVBATYCICAAIAUQrwULIAAoAhAgBRCuBSAGC2YBAX8gAaciBS8BBkEuayEGIAUoAiAhBSADQQBMBH5CgICAgDAFIAQpAwALIQEgBSAGNgI0IAEQDyEBAkAgBgRAIAAgARCUAQwBCyAFKAJkQQhrIAE3AwALIAAgBRCvBUKAgICAMAuQAQIBfwF+QoCAgIDgACEHAkAgAEHQABBsIgYEQCAGQQA2AgQgBkHIAGoQcSAAIAZBCGoiBSABIAIgAyAEEPIDBEAgBkEFNgIEDAILIAAgBRDCAiICEA0NASAAIAIQDCAAIAFBMhBvIgcQDQ0BIAYgBz4CACAHIAYQjQELIAcPCyAAKAIQIAYQrQVCgICAgOAAC+MCAgR/A34jAEEQayIEJABCgICAgOAAIQkCQAJ/AkAgAykDACIKQoCAgIBwWgRAIAqnIgUvAQZBE2tB//8DcUECSQ0BCyAAQRMQnANBAAwBCyAFKAIgCyIFRQ0AIARCADcDCCACQQJOBEAgACAEQQhqIAMpAwgQxAENAQsgBS0ABARAIAAQdQwBCyAEKQMIIgggBSgCACIGrFYEQCAAQfsZEGsMAQsgBiAIpyIHayEGAkAgAkEDSA0AIAMpAxAiCBASDQAgACAEIAgQxAENASAEKQMAIgggBq1WBEAgAEGHwgAQawwCCyAIpyEGCyAAIAFBHhBvIgEQDQ0AAkACQCAFLQAEBEAgABB1DAELIABBGBAvIgINAQsgACABEAwMAQsgAiABpyIANgIIIAoQDyEJIAIgBjYCFCACIAc2AhAgAiAJPgIMIAIgBUEMahBMIAAgAjYCICABIQkLIARBEGokACAJCxAAIwAgAGtBcHEiACQAIAALBgAgACQACwQAIwALEwAgAEHw4QBBABAWQoCAgIDgAAupAQEEfyAAKAJUIgMoAgQiBSAAKAIUIAAoAhwiBmsiBCAEIAVLGyIEBEAgAygCACAGIAQQJRogAyADKAIAIARqNgIAIAMgAygCBCAEayIFNgIECyADKAIAIQQgBSACIAIgBUsbIgUEQCAEIAEgBRAlGiADIAMoAgAgBWoiBDYCACADIAMoAgQgBWs2AgQLIARBADoAACAAIAAoAiwiATYCHCAAIAE2AhQgAgtCAQF+IwBBEGsiAiQAQoCAgIDgACEEIAAgAkEIaiADKQMAEMQBRQRAIAAgASACKQMIQRQQ9AMhBAsgAkEQaiQAIAQLKQAgASABKAIAQQdqQXhxIgFBEGo2AgAgACABKQMAIAEpAwgQ7QU5AwALqhgDEn8BfAJ+IwBBsARrIgskACALQQA2AiwCQCABvSIZQgBTBEBBASEQQfUPIRMgAZoiAb0hGQwBCyAEQYAQcQRAQQEhEEH4DyETDAELQfsPQfYPIARBAXEiEBshEyAQRSEVCwJAIBlCgICAgICAgPj/AINCgICAgICAgPj/AFEEQCAAQSAgAiAQQQNqIgMgBEH//3txEG0gACATIBAQZyAAQdI7QevpACAFQSBxIgUbQYbGAEHH7AAgBRsgASABYhtBAxBnIABBICACIAMgBEGAwABzEG0gAyACIAIgA0gbIQkMAQsgC0EQaiERAkACfwJAIAEgC0EsahD4BSIBIAGgIgFEAAAAAAAAAABiBEAgCyALKAIsIgZBAWs2AiwgBUEgciIOQeEARw0BDAMLIAVBIHIiDkHhAEYNAiALKAIsIQpBBiADIANBAEgbDAELIAsgBkEdayIKNgIsIAFEAAAAAAAAsEGiIQFBBiADIANBAEgbCyEMIAtBMGpBAEGgAiAKQQBIG2oiDSEHA0AgBwJ/IAFEAAAAAAAA8EFjIAFEAAAAAAAAAABmcQRAIAGrDAELQQALIgM2AgAgB0EEaiEHIAEgA7ihRAAAAABlzc1BoiIBRAAAAAAAAAAAYg0ACwJAIApBAEwEQCAKIQMgByEGIA0hCAwBCyANIQggCiEDA0AgA0EdIANBHUgbIQMCQCAHQQRrIgYgCEkNACADrSEaQgAhGQNAIAYgGUL/////D4MgBjUCACAahnwiGSAZQoCU69wDgCIZQoCU69wDfn0+AgAgBkEEayIGIAhPDQALIBmnIgZFDQAgCEEEayIIIAY2AgALA0AgCCAHIgZJBEAgBkEEayIHKAIARQ0BCwsgCyALKAIsIANrIgM2AiwgBiEHIANBAEoNAAsLIANBAEgEQCAMQRlqQQluQQFqIQ8gDkHmAEYhEgNAQQAgA2siA0EJIANBCUgbIQkCQCAGIAhNBEAgCCgCACEHDAELQYCU69wDIAl2IRRBfyAJdEF/cyEWQQAhAyAIIQcDQCAHIAMgBygCACIXIAl2ajYCACAWIBdxIBRsIQMgB0EEaiIHIAZJDQALIAgoAgAhByADRQ0AIAYgAzYCACAGQQRqIQYLIAsgCygCLCAJaiIDNgIsIA0gCCAHRUECdGoiCCASGyIHIA9BAnRqIAYgBiAHa0ECdSAPShshBiADQQBIDQALC0EAIQMCQCAGIAhNDQAgDSAIa0ECdUEJbCEDQQohByAIKAIAIglBCkkNAANAIANBAWohAyAJIAdBCmwiB08NAAsLIAxBACADIA5B5gBGG2sgDkHnAEYgDEEAR3FrIgcgBiANa0ECdUEJbEEJa0gEQEEEQaQCIApBAEgbIAtqIAdBgMgAaiIJQQltIg9BAnRqQdAfayEKQQohByAJIA9BCWxrIglBB0wEQANAIAdBCmwhByAJQQFqIglBCEcNAAsLAkAgCigCACISIBIgB24iDyAHbGsiCUUgCkEEaiIUIAZGcQ0AAkAgD0EBcUUEQEQAAAAAAABAQyEBIAdBgJTr3ANHIAggCk9yDQEgCkEEay0AAEEBcUUNAQtEAQAAAAAAQEMhAQtEAAAAAAAA4D9EAAAAAAAA8D9EAAAAAAAA+D8gBiAURhtEAAAAAAAA+D8gCSAHQQF2IhRGGyAJIBRJGyEYAkAgFQ0AIBMtAABBLUcNACAYmiEYIAGaIQELIAogEiAJayIJNgIAIAEgGKAgAWENACAKIAcgCWoiAzYCACADQYCU69wDTwRAA0AgCkEANgIAIAggCkEEayIKSwRAIAhBBGsiCEEANgIACyAKIAooAgBBAWoiAzYCACADQf+T69wDSw0ACwsgDSAIa0ECdUEJbCEDQQohByAIKAIAIglBCkkNAANAIANBAWohAyAJIAdBCmwiB08NAAsLIApBBGoiByAGIAYgB0sbIQYLA0AgBiIHIAhNIglFBEAgB0EEayIGKAIARQ0BCwsCQCAOQecARwRAIARBCHEhCgwBCyADQX9zQX8gDEEBIAwbIgYgA0ogA0F7SnEiChsgBmohDEF/QX4gChsgBWohBSAEQQhxIgoNAEF3IQYCQCAJDQAgB0EEaygCACIORQ0AQQohCUEAIQYgDkEKcA0AA0AgBiIKQQFqIQYgDiAJQQpsIglwRQ0ACyAKQX9zIQYLIAcgDWtBAnVBCWwhCSAFQV9xQcYARgRAQQAhCiAMIAYgCWpBCWsiBkEAIAZBAEobIgYgBiAMShshDAwBC0EAIQogDCADIAlqIAZqQQlrIgZBACAGQQBKGyIGIAYgDEobIQwLQX8hCSAMQf3///8HQf7///8HIAogDHIiEhtKDQEgDCASQQBHakEBaiEOAkAgBUFfcSIVQcYARgRAIANB/////wcgDmtKDQMgA0EAIANBAEobIQYMAQsgESADIANBH3UiBnMgBmutIBEQpAIiBmtBAUwEQANAIAZBAWsiBkEwOgAAIBEgBmtBAkgNAAsLIAZBAmsiDyAFOgAAIAZBAWtBLUErIANBAEgbOgAAIBEgD2siBkH/////ByAOa0oNAgsgBiAOaiIDIBBB/////wdzSg0BIABBICACIAMgEGoiBSAEEG0gACATIBAQZyAAQTAgAiAFIARBgIAEcxBtAkACQAJAIBVBxgBGBEAgC0EQaiIGQQhyIQMgBkEJciEKIA0gCCAIIA1LGyIJIQgDQCAINQIAIAoQpAIhBgJAIAggCUcEQCAGIAtBEGpNDQEDQCAGQQFrIgZBMDoAACAGIAtBEGpLDQALDAELIAYgCkcNACALQTA6ABggAyEGCyAAIAYgCiAGaxBnIAhBBGoiCCANTQ0ACyASBEAgAEGS9gBBARBnCyAMQQBMIAcgCE1yDQEDQCAINQIAIAoQpAIiBiALQRBqSwRAA0AgBkEBayIGQTA6AAAgBiALQRBqSw0ACwsgACAGIAxBCSAMQQlIGxBnIAxBCWshBiAIQQRqIgggB08NAyAMQQlKIQMgBiEMIAMNAAsMAgsCQCAMQQBIDQAgByAIQQRqIAcgCEsbIQkgC0EQaiIGQQhyIQMgBkEJciENIAghBwNAIA0gBzUCACANEKQCIgZGBEAgC0EwOgAYIAMhBgsCQCAHIAhHBEAgBiALQRBqTQ0BA0AgBkEBayIGQTA6AAAgBiALQRBqSw0ACwwBCyAAIAZBARBnIAZBAWohBiAKIAxyRQ0AIABBkvYAQQEQZwsgACAGIAwgDSAGayIGIAYgDEobEGcgDCAGayEMIAdBBGoiByAJTw0BIAxBAE4NAAsLIABBMCAMQRJqQRJBABBtIAAgDyARIA9rEGcMAgsgDCEGCyAAQTAgBkEJakEJQQAQbQsgAEEgIAIgBSAEQYDAAHMQbSAFIAIgAiAFSBshCQwBCyATIAVBGnRBH3VBCXFqIQwCQCADQQtLDQBBDCADayEGRAAAAAAAADBAIRgDQCAYRAAAAAAAADBAoiEYIAZBAWsiBg0ACyAMLQAAQS1GBEAgGCABmiAYoaCaIQEMAQsgASAYoCAYoSEBCyARIAsoAiwiBiAGQR91IgZzIAZrrSAREKQCIgZGBEAgC0EwOgAPIAtBD2ohBgsgEEECciEKIAVBIHEhCCALKAIsIQcgBkECayINIAVBD2o6AAAgBkEBa0EtQSsgB0EASBs6AAAgBEEIcSEGIAtBEGohBwNAIAciBQJ/IAGZRAAAAAAAAOBBYwRAIAGqDAELQYCAgIB4CyIHQfCwBGotAAAgCHI6AAAgBiADQQBKckUgASAHt6FEAAAAAAAAMECiIgFEAAAAAAAAAABhcSAFQQFqIgcgC0EQamtBAUdyRQRAIAVBLjoAASAFQQJqIQcLIAFEAAAAAAAAAABiDQALQX8hCUH9////ByAKIBEgDWsiBWoiBmsgA0gNACAAQSAgAiAGAn8CQCADRQ0AIAcgC0EQamsiCEECayADTg0AIANBAmoMAQsgByALQRBqayIICyIHaiIDIAQQbSAAIAwgChBnIABBMCACIAMgBEGAgARzEG0gACALQRBqIAgQZyAAQTAgByAIa0EAQQAQbSAAIA0gBRBnIABBICACIAMgBEGAwABzEG0gAyACIAIgA0gbIQkLIAtBsARqJAAgCQsFACAAnQveAQIBfwJ+IAC9IgJC////////////AIMiA78hAAJAIANCIIinIgFB66eG/wNPBEAgAUGBgNCBBE8EQEQAAAAAAAAAgCAAo0QAAAAAAADwP6AhAAwCC0QAAAAAAADwP0QAAAAAAAAAQCAAIACgEKYCRAAAAAAAAABAoKOhIQAMAQsgAUGvscH+A08EQCAAIACgEKYCIgAgAEQAAAAAAAAAQKCjIQAMAQsgAUGAgMAASQ0AIABEAAAAAAAAAMCiEKYCIgCaIABEAAAAAAAAAECgoyEACyAAmiAAIAJCAFMbC4QBAQJ/IwBBEGsiASQAAkAgAL1CIIinQf////8HcSICQfvDpP8DTQRAIAJBgICA8gNJDQEgAEQAAAAAAAAAAEEAEPkFIQAMAQsgAkGAgMD/B08EQCAAIAChIQAMAQsgACABELEEIQIgASsDACABKwMIIAJBAXEQ+QUhAAsgAUEQaiQAIAALQAEBfiMAQRBrIgIkAEKAgICA4AAhBCAAIAJBCGogAykDABDEAUUEQCAAIAEgAikDCBCMAyEECyACQRBqJAAgBAsEAEIAC9gCAQd/IwBBIGsiAyQAIAMgACgCHCIENgIQIAAoAhQhBSADIAI2AhwgAyABNgIYIAMgBSAEayIBNgIUIAEgAmohBSADQRBqIQFBAiEHAn8CQAJAAkAgACgCPCABQQIgA0EMahACEPQFBEAgASEEDAELA0AgBSADKAIMIgZGDQIgBkEASARAIAEhBAwECyABIAYgASgCBCIISyIJQQN0aiIEIAYgCEEAIAkbayIIIAQoAgBqNgIAIAFBDEEEIAkbaiIBIAEoAgAgCGs2AgAgBSAGayEFIAAoAjwgBCIBIAcgCWsiByADQQxqEAIQ9AVFDQALCyAFQX9HDQELIAAgACgCLCIBNgIcIAAgATYCFCAAIAEgACgCMGo2AhAgAgwBCyAAQQA2AhwgAEIANwMQIAAgACgCAEEgcjYCAEEAIAdBAkYNABogAiAEKAIEawshASADQSBqJAAgAQvoBAIDfwd+IwBBIGsiBSQAQoCAgIDgACENAkAgACABIARBH2oQbyIBEA0NAEKAgICAMCEIAkAgAEEcEGwiBkUEQEKAgICAMCELQoCAgIAwIQoMAQsgBkEEahBxIAYgBEEBdkEBcTYCACABIAYQjQEgBkEBNgIUIAYgAEEIEC8iBzYCEEKAgICAMCELQoCAgIAwIQogB0UNACAHEHEgBkEENgIYAkAgAkEATAR+QoCAgIAwBSADKQMACyIIEBINACAIECgNAAJAIAAgAUHoAEHCACAEQQFxIgIbIAFBABAUIgoQDQ0AIAAgChA7RQRAIABBnjZBABAWDAELIAAgCEEAEPYBIggQDQ0CIAAgCEHqACAIQQAQFCILEA0NAgJAA0AgBSAAIAggCyAFQRRqEK8BIgk3AxggCRANDQQgBSgCFEUEQAJAIAIEQCAAIAogAUEBIAVBGGoQJCIOEA1FDQEgACAFKQMYEAwMBwsCQAJAIAkQIkUEQCAAEClCgICAgDAhCQwBCyAAIAlBABB7IgkQDUUNAQtCgICAgDAhDAwECyAAIAUpAxhBARB7IgwQDQ0DIAUgDDcDCCAFIAk3AwAgACAKIAFBAiAFECQiDhANDQMgACAJEAwgACAMEAwLIAAgDhAMIAAgBSkDGBAMDAELCyAAIAkQDCAAIAsQDCAAIAgQDCAAIAoQDAwCCyAAIAUpAxgQDCAAIAkQDCAAIAwQDAwCC0KAgICAMCEIDAELIAEhDQwBCyAIECIEQCAAIAhBARCzARoLIAAgCxAMIAAgCBAMIAAgChAMIAAgARAMCyAFQSBqJAAgDQsFACAAnwudAQMCfAF/AX5EAAAAAAAA4D8gAKYhAiAAvUL///////////8AgyIEvyEBAkAgBEIgiKciA0HB3JiEBE0EQCABEKYCIQEgA0H//7//A00EQCADQYCAwPIDSQ0CIAIgASABoCABIAGiIAFEAAAAAAAA8D+go6GiDwsgAiABIAEgAUQAAAAAAADwP6CjoKIPCyABIAIgAqAQiwYhAAsgAAvLAQECfyMAQRBrIgEkAAJAIAC9QiCIp0H/////B3EiAkH7w6T/A00EQCACQYCAwPIDSQ0BIABEAAAAAAAAAABBABDbAiEADAELIAJBgIDA/wdPBEAgACAAoSEADAELAkACQAJAAkAgACABELEEQQNxDgMAAQIDCyABKwMAIAErAwhBARDbAiEADAMLIAErAwAgASsDCBDcAiEADAILIAErAwAgASsDCEEBENsCmiEADAELIAErAwAgASsDCBDcApohAAsgAUEQaiQAIAALzQMDBXwBfgN/AkACQAJAAkAgAL0iBkIAWQRAIAZCIIinIgdB//8/Sw0BCyAGQv///////////wCDUARARAAAAAAAAPC/IAAgAKKjDwsgBkIAWQ0BIAAgAKFEAAAAAAAAAACjDwsgB0H//7//B0sNAkGAgMD/AyEIQYF4IQkgB0GAgMD/A0cEQCAHIQgMAgsgBqcNAUQAAAAAAAAAAA8LIABEAAAAAAAAUEOivSIGQiCIpyEIQct3IQkLIAZC/////w+DIAhB4r4laiIHQf//P3FBnsGa/wNqrUIghoS/RAAAAAAAAPC/oCIAIAAgAEQAAAAAAADgP6KiIgOhvUKAgICAcIO/IgREAAAgZUcV9z+iIgEgCSAHQRR2arciAqAiBSABIAIgBaGgIAAgAEQAAAAAAAAAQKCjIgEgAyABIAGiIgIgAqIiASABIAFEn8Z40Amawz+iRK94jh3Fccw/oKJEBPqXmZmZ2T+goiACIAEgASABRERSPt8S8cI/okTeA8uWZEbHP6CiRFmTIpQkSdI/oKJEk1VVVVVV5T+goqCgoiAAIAShIAOhoCIAIASgRACi7y78Bec9oiAARAAAIGVHFfc/oqCgoCEACyAAC+YDAwZ8AX4DfwJAAkACQAJAIAC9IgdCAFkEQCAHQiCIpyIIQf//P0sNAQsgB0L///////////8Ag1AEQEQAAAAAAADwvyAAIACiow8LIAdCAFkNASAAIAChRAAAAAAAAAAAow8LIAhB//+//wdLDQJBgIDA/wMhCUGBeCEKIAhBgIDA/wNHBEAgCCEJDAILIAenDQFEAAAAAAAAAAAPCyAARAAAAAAAAFBDor0iB0IgiKchCUHLdyEKCyAKIAlB4r4laiIIQRR2arciBUQAYJ9QE0TTP6IiASAHQv////8PgyAIQf//P3FBnsGa/wNqrUIghoS/RAAAAAAAAPC/oCIAIAAgAEQAAAAAAADgP6KiIgOhvUKAgICAcIO/IgREAAAgFXvL2z+iIgKgIgYgAiABIAahoCAAIABEAAAAAAAAAECgoyIBIAMgASABoiICIAKiIgEgASABRJ/GeNAJmsM/okSveI4dxXHMP6CiRAT6l5mZmdk/oKIgAiABIAEgAUREUj7fEvHCP6JE3gPLlmRGxz+gokRZkyKUJEnSP6CiRJNVVVVVVeU/oKKgoKIgACAEoSADoaAiAEQAACAVe8vbP6IgBUQ2K/ER8/5ZPaIgACAEoETVrZrKOJS7PaKgoKCgIQALIAALoQEBBH8gAkEAIAAoAlQiAygCBCIEIAMoAgAiBWsiBiAEIAZJGyIESwRAIAAgACgCAEEQcjYCACAEIQILIAEgAygCDCAFaiACECUaIAMgAygCACACaiIFNgIAIAAgACgCLCIBNgIEIAAgASAEIAJrIgQgACgCMCIAIAAgBEsbIgBqNgIIIAEgAygCDCAFaiAAECUaIAMgAygCACAAajYCACACC4sBAQF/IwBBEGsiAyQAAn4CQCACQQNPDQAgACgCVCEAIANBADYCBCADIAAoAgA2AgggAyAAKAIENgIMQQAgA0EEaiACQQJ0aigCACICa6wgAVUNACAAKAIIIAJrrCABUw0AIAAgAiABp2oiADYCACAArQwBC0HEswRBHDYCAEJ/CyEBIANBEGokACABCwUAIACcCwUAIACZC6QBAgF/AX4gAL1C////////////AIMiAr8hAAJ8IAJCIIinIgFBwdyY/wNNBEBEAAAAAAAA8D8gAUGAgMDyA0kNARogABCmAiIAIACiIABEAAAAAAAA8D+gIgAgAKCjRAAAAAAAAPA/oA8LIAFBwdyYhARNBEAgABCvBCIARAAAAAAAAPA/IACjoEQAAAAAAADgP6IPCyAARAAAAAAAAPA/EIsGCwvHAQECfyMAQRBrIgEkAAJ8IAC9QiCIp0H/////B3EiAkH7w6T/A00EQEQAAAAAAADwPyACQZ7BmvIDSQ0BGiAARAAAAAAAAAAAENwCDAELIAAgAKEgAkGAgMD/B08NABoCQAJAAkACQCAAIAEQsQRBA3EOAwABAgMLIAErAwAgASsDCBDcAgwDCyABKwMAIAErAwhBARDbApoMAgsgASsDACABKwMIENwCmgwBCyABKwMAIAErAwhBARDbAgshACABQRBqJAAgAAucAwIDfgJ/IwBBIGsiCSQAAkAgBUEBcQRAIwBBIGsiCiQAQoCAgIDgACEIAkAgACAKQRhqIAFB3gAQhwEiBUUNACAFKQMAIgEQtQFFBEAgAEHfKUEAEBYMAQsgCikDGCIGEBIEQCAAIAEgAiADIAQQjgMhCAwBCwJAIAAgAyAEEJEDIgcQDQ0AIAUpAwAhASAKIAI3AxAgCiAHNwMIIAogATcDACAAIAYgBSkDCEEDIAoQJCIBEA0gAUL/////b1ZyRQRAIAAgARAMIAAQKQwBCyABIQgLIAAgBhAMIAAgBxAMCyAKQSBqJAAgCCEGDAELQoCAgIDgACEGIAAgCUEYaiABQdoAEIcBIgVFDQAgCSkDGCEHIAUtABBFBEAgACAHEAwgAEGpNkEAEBYMAQsgBxASBEAgACAFKQMAIAIgAyAEECQhBgwBCyAAIAMgBBCRAyIIEA1FBEAgBSkDACEBIAkgCDcDECAJIAI3AwggCSABNwMAIAAgByAFKQMIQQMgCRAkIQYLIAAgBxAMIAAgCBAMCyAJQSBqJAAgBgsFACAAmwuDAgMCfAJ/AX4gAL0iBUIgiKdB/////wdxIgNBgIDA/wdPBEAgACAAoA8LQZPx/dQCIQQCQCADQf//P00EQEGT8f3LAiEEIABEAAAAAAAAUEOivSIFQiCIp0H/////B3EiA0UNAQsgBUKAgICAgICAgIB/gyADQQNuIARqrUIghoS/IgIgAqIgAiAAo6IiASABIAGioiABRNft5NQAsMI/okTZUee+y0Tov6CiIAEgAUTC1klKYPH5P6JEICTwkuAo/r+gokSS5mEP5gP+P6CgIAKivUKAgICACHxCgICAgHyDvyIBIAAgASABoqMiACABoSABIAGgIACgo6IgAaAhAAsgAAuHAQMBfgF/AXwgAL0iAUL///////////8Ag78hAAJAAnwgAUI0iKdB/w9xIgJB/QdNBEAgAkHfB0kNAiAAIACgIgMgAyAAokQAAAAAAADwPyAAoaOgDAELIABEAAAAAAAA8D8gAKGjIgAgAKALELMDRAAAAAAAAOA/oiEACyAAmiAAIAFCAFMbC6gDAgV/AX4gAL1C////////////AINCgYCAgICAgPj/AFQgAb1C////////////AINCgICAgICAgPj/AFhxRQRAIAAgAaAPCyABvSIHQiCIpyICQYCAwP8DayAHpyIFckUEQCAAELIEDwsgAkEedkECcSIGIAC9IgdCP4inciEDAkAgB0IgiKdB/////wdxIgQgB6dyRQRAAkACQCADQQJrDgIAAQMLRBgtRFT7IQlADwtEGC1EVPshCcAPCyACQf////8HcSICIAVyRQRARBgtRFT7Ifk/IACmDwsCQCACQYCAwP8HRgRAIARBgIDA/wdHDQEgA0EDdEHQhARqKwMADwsgBEGAgMD/B0cgAkGAgIAgaiAET3FFBEBEGC1EVPsh+T8gAKYPCwJ8IAYEQEQAAAAAAAAAACAEQYCAgCBqIAJJDQEaCyAAIAGjmRCyBAshAAJAAkACQCADDgMEAAECCyAAmg8LRBgtRFT7IQlAIABEB1wUMyamobygoQ8LIABEB1wUMyamobygRBgtRFT7IQnAoA8LIANBA3RB8IQEaisDACEACyAAC7IBAwF+AX8BfCAAvSIBQv///////////wCDvyEAAkAgAUI0iKdB/w9xIgJBmQhPBEAgABDaAkTvOfr+Qi7mP6AhAAwBCyACQYAITwRAIAAgAKBEAAAAAAAA8D8gACAAokQAAAAAAADwP6CfIACgo6AQ2gIhAAwBCyACQeUHSQ0AIAAgAKIiAyADRAAAAAAAAPA/oJ9EAAAAAAAA8D+goyAAoBCzAyEACyAAmiAAIAFCAFMbC7kCAwF/A3wBfiAAvSIFQiCIp0H/////B3EiAUGAgMD/A08EQCAFpyABQYCAwP8Da3JFBEAgAEQYLURU+yH5P6JEAAAAAAAAcDigDwtEAAAAAAAAAAAgACAAoaMPCwJAIAFB/////gNNBEAgAUGAgEBqQYCAgPIDSQ0BIAAgACAAohCnAqIgAKAPC0QAAAAAAADwPyAAmaFEAAAAAAAA4D+iIgOfIQAgAxCnAiEEAnwgAUGz5rz/A08EQEQYLURU+yH5PyAAIASiIACgIgAgAKBEB1wUMyamkbygoQwBC0QYLURU+yHpPyAAvUKAgICAcIO/IgIgAqChIAAgAKAgBKJEB1wUMyamkTwgAyACIAKioSAAIAKgoyIAIACgoaGhRBgtRFT7Iek/oAsiAJogACAFQgBTGyEACyAAC3YBAX8gAL1CNIinQf8PcSIBQf8HTQRAIABEAAAAAAAA8L+gIgAgACAAoiAAIACgoJ+gELMDDwsgAUGYCE0EQCAAIACgRAAAAAAAAPC/IAAgAKJEAAAAAAAA8L+gnyAAoKOgENoCDwsgABDaAkTvOfr+Qi7mP6ALWgIBfwF+AkBBsLMEKAIABEBBtLMEKAIAIQIMAQtBsLMEENYFIgI2AgBBtLMEIAIQ4AQiAjYCAAsgAiAAIAAQQ0Gt7wAQtgUiAyABEKcDGkG0swQoAgAgAxAMCwuvpgRRAEGACAvheygpe30AKCl7c3VwZXIoLi4uYXJndW1lbnRzKTt9ACgpIHsKICAgIFtuYXRpdmUgY29kZV0KfQBjYW5ub3QgbWl4ID8/IHdpdGggJiYgb3IgfHwAcHJveHk6IHByb3BlcnR5IG5vdCBwcmVzZW50IGluIHRhcmdldCB3ZXJlIHJldHVybmVkIGJ5IG5vbiBleHRlbnNpYmxlIHByb3h5AHJldm9rZWQgcHJveHkAUHJveHkAYWRkX3Byb3BlcnR5AHByb3h5OiBjYW5ub3Qgc2V0IHByb3BlcnR5AG5vIHNldHRlciBmb3IgcHJvcGVydHkAdmFsdWUgaGFzIG5vIHByb3BlcnR5AGNvdWxkIG5vdCBkZWxldGUgcHJvcGVydHkAcHJveHk6IGR1cGxpY2F0ZSBwcm9wZXJ0eQBKU19EZWZpbmVBdXRvSW5pdFByb3BlcnR5AGhhc093blByb3BlcnR5AHByb3h5OiBpbmNvbnNpc3RlbnQgZGVsZXRlUHJvcGVydHkAcHJveHk6IGluY29uc2lzdGVudCBkZWZpbmVQcm9wZXJ0eQBKU19EZWZpbmVQcm9wZXJ0eQAhbXItPmVtcHR5AGluZmluaXR5AEluZmluaXR5AG91dCBvZiBtZW1vcnkAdW5rbm93biB1bmljb2RlIGdlbmVyYWwgY2F0ZWdvcnkAR2VuZXJhbF9DYXRlZ29yeQBldmVyeQBhbnkAYXBwbHkAJyVzJyBpcyByZWFkLW9ubHkAZXhwZWN0aW5nIGNhdGNoIG9yIGZpbmFsbHkAc3RpY2t5AHN0cmluZ2lmeQBzdWJhcnJheQBlbXB0eSBhcnJheQBub24gaW50ZWdlciBpbmRleCBpbiB0eXBlZCBhcnJheQBuZWdhdGl2ZSBpbmRleCBpbiB0eXBlZCBhcnJheQBvdXQtb2YtYm91bmQgaW5kZXggaW4gdHlwZWQgYXJyYXkAY2Fubm90IGNyZWF0ZSBudW1lcmljIGluZGV4IGluIHR5cGVkIGFycmF5AGlzQXJyYXkAVHlwZWRBcnJheQBnZXREYXkAZ2V0VVRDRGF5AGpzX2dldF9hdG9tX2luZGV4AGludmFsaWQgYXJyYXkgaW5kZXgAb3V0LW9mLWJvdW5kIG51bWVyaWMgaW5kZXgASlNfQXRvbUlzQXJyYXlJbmRleABmaW5kSW5kZXgAaW52YWxpZCBleHBvcnQgc3ludGF4AGludmFsaWQgYXNzaWdubWVudCBzeW50YXgAbWF4AFx1JTA0eABpbnZhbGlkIG9wY29kZTogcGM9JXUgb3Bjb2RlPTB4JTAyeAAtKyAgIDBYMHgALTBYKzBYIDBYLTB4KzB4IDB4AGxpbmUgdGVybWluYXRvciBub3QgYWxsb3dlZCBhZnRlciB0aHJvdwBwb3cAbm93AHN0YWNrIG92ZXJmbG93AG11c3QgYmUgY2FsbGVkIHdpdGggbmV3AGlzVmlldwBEYXRhVmlldwByYXcAJXUAY2xhc3MgZGVjbGFyYXRpb25zIGNhbid0IGFwcGVhciBpbiBzaW5nbGUtc3RhdGVtZW50IGNvbnRleHQAZnVuY3Rpb24gZGVjbGFyYXRpb25zIGNhbid0IGFwcGVhciBpbiBzaW5nbGUtc3RhdGVtZW50IGNvbnRleHQAbGV4aWNhbCBkZWNsYXJhdGlvbnMgY2FuJ3QgYXBwZWFyIGluIHNpbmdsZS1zdGF0ZW1lbnQgY29udGV4dABkdXBsaWNhdGUgYXJndW1lbnQgbmFtZXMgbm90IGFsbG93ZWQgaW4gdGhpcyBjb250ZXh0AGR1cGxpY2F0ZSBwYXJhbWV0ZXIgbmFtZXMgbm90IGFsbG93ZWQgaW4gdGhpcyBjb250ZXh0AGltcG9ydC5tZXRhIG5vdCBzdXBwb3J0ZWQgaW4gdGhpcyBjb250ZXh0AEpTX0ZyZWVDb250ZXh0AEpTQ29udGV4dABqc19tYXBfaXRlcmF0b3JfbmV4dABqc19hc3luY19nZW5lcmF0b3JfcmVzdW1lX25leHQAdW5leHBlY3RlZCBlbmQgb2YgaW5wdXQAdHQAZXhwb3J0ZWQgdmFyaWFibGUgJyVzJyBkb2VzIG5vdCBleGlzdABwcml2YXRlIGNsYXNzIGZpZWxkICclcycgZG9lcyBub3QgZXhpc3QAdGVzdABhc3NpZ25tZW50IHJlc3QgcHJvcGVydHkgbXVzdCBiZSBsYXN0AHNxcnQAc29ydABjYnJ0AHRyaW1TdGFydABwYWRTdGFydAB1bmtub3duIHVuaWNvZGUgc2NyaXB0AFNjcmlwdABoeXBvdABmcmVlX3plcm9fcmVmY291bnQAc3RyX2luZGV4ID09IG51bV9rZXlzX2NvdW50ICsgc3RyX2tleXNfY291bnQAbnVtX2luZGV4ID09IG51bV9rZXlzX2NvdW50AHN5bV9pbmRleCA9PSBhdG9tX2NvdW50AGxhYmVsID49IDAgJiYgbGFiZWwgPCBzLT5sYWJlbF9jb3VudABsYWIxID49IDAgJiYgbGFiMSA8IHMtPmxhYmVsX2NvdW50AHZhbCA8IHMtPmNhcHR1cmVfY291bnQAdmFsMiA8IHMtPmNhcHR1cmVfY291bnQAaW52YWxpZCByZXBlYXQgY291bnQAaW52YWxpZCByZXBldGl0aW9uIGNvdW50AGZvbnQAaW52YWxpZCBjb2RlIHBvaW50AGZyb21Db2RlUG9pbnQAaW52YWxpZCBoaW50AGVuY29kZVVSSUNvbXBvbmVudABkZWNvZGVVUklDb21wb25lbnQAdW5leHBlY3RlZCBlbmQgb2YgY29tbWVudABpbnZhbGlkIHN3aXRjaCBzdGF0ZW1lbnQAcGFyc2VJbnQAZHVwbGljYXRlIGRlZmF1bHQAc3BsaXQAZXhwZWN0aW5nIGhleCBkaWdpdAB0cmltUmlnaHQAcmVkdWNlUmlnaHQAdW5zaGlmdAB0cmltTGVmdABpbnZhbGlkIG9mZnNldABpbnZhbGlkIGJ5dGVPZmZzZXQAZ2V0VGltZXpvbmVPZmZzZXQAcmVzb2x2aW5nIGZ1bmN0aW9uIGFscmVhZHkgc2V0AHByb3h5OiBpbmNvbnNpc3RlbnQgc2V0AGZpbmRfanVtcF90YXJnZXQAZXhwZWN0aW5nIHRhcmdldABpbnZhbGlkIGRlc3RydWN0dXJpbmcgdGFyZ2V0AHByb3h5OiBpbmNvbnNpc3RlbnQgZ2V0AFdlYWtTZXQAY29uc3RydWN0AEpTX0ZyZWVBdG9tU3RydWN0AHVzZSBzdHJpY3QAUmVmbGVjdAByZWplY3QAbm90IGFuIEFzeW5jR2VuZXJhdG9yIG9iamVjdABjYW5ub3QgY29udmVydCB0byBvYmplY3QAaW52YWxpZCBicmFuZCBvbiBvYmplY3QAb3BlcmFuZCAncHJvdG90eXBlJyBwcm9wZXJ0eSBpcyBub3QgYW4gb2JqZWN0AHJlY2VpdmVyIGlzIG5vdCBhbiBvYmplY3QAaXRlcmF0b3IgbXVzdCByZXR1cm4gYW4gb2JqZWN0AG5vdCBhIERhdGUgb2JqZWN0AG5vdCBhIG9iamVjdABKU09iamVjdABwYXJzZUZsb2F0AGZsYXQAbm90aGluZyB0byByZXBlYXQAY29uY2F0AGNvZGVQb2ludEF0AGNoYXJBdABjaGFyQ29kZUF0AGtleXMAcHJveHk6IHRhcmdldCBwcm9wZXJ0eSBtdXN0IGJlIHByZXNlbnQgaW4gcHJveHkgb3duS2V5cwAgIGZhc3QgYXJyYXlzAGV4cG9ydCAnJXMnIGluIG1vZHVsZSAnJXMnIGlzIGFtYmlndW91cwBwcml2YXRlIGNsYXNzIGZpZWxkICclcycgYWxyZWFkeSBleGlzdHMAdG9vIG1hbnkgYXJndW1lbnRzAFRvbyBtYW55IGNhbGwgYXJndW1lbnRzACAgZWxlbWVudHMAaW52YWxpZCBudW1iZXIgb2YgZGlnaXRzAGJpbmFyeSBvYmplY3RzAGludmFsaWQgcHJvcGVydHkgYWNjZXNzAGpzX29wX2RlZmluZV9jbGFzcwBmZC0+Ynl0ZV9jb2RlLmJ1ZltkZWZpbmVfY2xhc3NfcG9zXSA9PSBPUF9kZWZpbmVfY2xhc3MAX19nZXRDbGFzcwBzZXRIb3VycwBnZXRIb3VycwBzZXRVVENIb3VycwBnZXRVVENIb3VycwBnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzAHRvbyBtYW55IGltYnJpY2F0ZWQgcXVhbnRpZmllcnMAdW5pY29kZV9wcm9wX29wcwBhY29zAGZvciBhd2FpdCBpcyBvbmx5IHZhbGlkIGluIGFzeW5jaHJvbm91cyBmdW5jdGlvbnMAbmV3LnRhcmdldCBvbmx5IGFsbG93ZWQgd2l0aGluIGZ1bmN0aW9ucwBieXRlY29kZSBmdW5jdGlvbnMAQyBmdW5jdGlvbnMAcHJveHk6IGluY29uc2lzdGVudCBwcmV2ZW50RXh0ZW5zaW9ucwBTY3JpcHRfRXh0ZW5zaW9ucwBhdG9tcwBwcm94eTogcHJvcGVydGllcyBtdXN0IGJlIHN0cmluZ3Mgb3Igc3ltYm9scwBnZXRPd25Qcm9wZXJ0eVN5bWJvbHMAcmVzb2x2ZV9sYWJlbHMASlNfRXZhbFRoaXMAc3RyaW5ncwBpbnZhbGlkIGRlc2NyaXB0b3IgZmxhZ3MAaW52YWxpZCByZWd1bGFyIGV4cHJlc3Npb24gZmxhZ3MAdmFsdWVzAHNldE1pbnV0ZXMAZ2V0TWludXRlcwBzZXRVVENNaW51dGVzAGdldFVUQ01pbnV0ZXMAdG9vIG1hbnkgY2FwdHVyZXMAICBzaGFwZXMAZ2V0T3duUHJvcGVydHlOYW1lcwBnY19mcmVlX2N5Y2xlcwBhZGRfZXZhbF92YXJpYWJsZXMAcmVzb2x2ZV92YXJpYWJsZXMAdG9vIG1hbnkgbG9jYWwgdmFyaWFibGVzAHRvbyBtYW55IGNsb3N1cmUgdmFyaWFibGVzAGNvbXBhY3RfcHJvcGVydGllcwAgIHByb3BlcnRpZXMAZGVmaW5lUHJvcGVydGllcwBlbnRyaWVzAGZyb21FbnRyaWVzAHRvbyBtYW55IHJhbmdlcwBpbmNsdWRlcwBzZXRNaWxsaXNlY29uZHMAZ2V0TWlsbGlzZWNvbmRzAHNldFVUQ01pbGxpc2Vjb25kcwBnZXRVVENNaWxsaXNlY29uZHMAc2V0U2Vjb25kcwBnZXRTZWNvbmRzAHNldFVUQ1NlY29uZHMAZ2V0VVRDU2Vjb25kcwBpdGFsaWNzAGFicwBwcm94eTogaW5jb25zaXN0ZW50IGhhcwAlLipzACAoJXMAc2V0ICVzAGdldCAlcwAgICAgYXQgJXMAbm90IGEgJXMAdW5zdXBwb3J0ZWQga2V5d29yZDogJXMAc3Vic3RyAHByb3h5OiBpbmNvbnNpc3RlbnQgZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yAHN1cGVyKCkgaXMgb25seSB2YWxpZCBpbiBhIGRlcml2ZWQgY2xhc3MgY29uc3RydWN0b3IAcGFyZW50IGNsYXNzIG11c3QgYmUgY29uc3RydWN0b3IAbm90IGEgY29uc3RydWN0b3IAQXJyYXkgSXRlcmF0b3IAU2V0IEl0ZXJhdG9yAE1hcCBJdGVyYXRvcgBSZWdFeHAgU3RyaW5nIEl0ZXJhdG9yAG5vdCBhbiBBc3luYy1mcm9tLVN5bmMgSXRlcmF0b3IAY2Fubm90IGludm9rZSBhIHJ1bm5pbmcgZ2VuZXJhdG9yAG5vdCBhIGdlbmVyYXRvcgBBc3luY0dlbmVyYXRvcgBzeW50YXggZXJyb3IAU3ludGF4RXJyb3IARXZhbEVycm9yAEludGVybmFsRXJyb3IAQWdncmVnYXRlRXJyb3IAVHlwZUVycm9yAFJhbmdlRXJyb3IAUmVmZXJlbmNlRXJyb3IAVVJJRXJyb3IAZmxvb3IAZm9udGNvbG9yAGFuY2hvcgBmb3IAa2V5Rm9yAGV4cGVjdGluZyBzdXJyb2dhdGUgcGFpcgBhIGRlY2xhcmF0aW9uIGluIHRoZSBoZWFkIG9mIGEgZm9yLSVzIGxvb3AgY2FuJ3QgaGF2ZSBhbiBpbml0aWFsaXplcgAnYXJndW1lbnRzJyBpZGVudGlmaWVyIGlzIG5vdCBhbGxvd2VkIGluIGNsYXNzIGZpZWxkIGluaXRpYWxpemVyAGludmFsaWQgbnVtYmVyIG9mIGFyZ3VtZW50cyBmb3IgZ2V0dGVyIG9yIHNldHRlcgBpbnZhbGlkIHNldHRlcgBpbnZhbGlkIGdldHRlcgBmaWx0ZXIAbWlzc2luZyBmb3JtYWwgcGFyYW1ldGVyACJ1c2Ugc3RyaWN0IiBub3QgYWxsb3dlZCBpbiBmdW5jdGlvbiB3aXRoIGRlZmF1bHQgb3IgZGVzdHJ1Y3R1cmluZyBwYXJhbWV0ZXIAaW52YWxpZCBjaGFyYWN0ZXIAdW5leHBlY3RlZCBjaGFyYWN0ZXIAcHJpdmF0ZSBjbGFzcyBmaWVsZCBmb3JiaWRkZW4gYWZ0ZXIgc3VwZXIAaW52YWxpZCByZWRlZmluaXRpb24gb2YgbGV4aWNhbCBpZGVudGlmaWVyACdsZXQnIGlzIG5vdCBhIHZhbGlkIGxleGljYWwgaWRlbnRpZmllcgBpbnZhbGlkIHJlZGVmaW5pdGlvbiBvZiBnbG9iYWwgaWRlbnRpZmllcgB5aWVsZCBpcyBhIHJlc2VydmVkIGlkZW50aWZpZXIAJyVzJyBpcyBhIHJlc2VydmVkIGlkZW50aWZpZXIAb3RoZXIAYXRvbTFfaXNfaW50ZWdlciAmJiBhdG9tMl9pc19pbnRlZ2VyAGlzSW50ZWdlcgBpc1NhZmVJbnRlZ2VyAGJ1ZmZlcgBTaGFyZWRBcnJheUJ1ZmZlcgBjYW5ub3QgdXNlIGlkZW50aWNhbCBBcnJheUJ1ZmZlcgBjYW5ub3QgY29udmVydCBzeW1ib2wgdG8gbnVtYmVyAG5vdCBhIG51bWJlcgBsaW5lTnVtYmVyAG1hbGZvcm1lZCB1bmljb2RlIGNoYXIAY2xlYXIAc2V0WWVhcgBnZXRZZWFyAHNldEZ1bGxZZWFyAGdldEZ1bGxZZWFyAHNldFVUQ0Z1bGxZZWFyAGdldFVUQ0Z1bGxZZWFyAHVuZXhwZWN0ZWQgbGluZSB0ZXJtaW5hdG9yIGluIHJlZ2V4cAB1bmV4cGVjdGVkIGVuZCBvZiByZWdleHAAUmVnRXhwAHN1cABpbnZhbGlkIGdyb3VwAHBvcABjb250aW51ZSBtdXN0IGJlIGluc2lkZSBsb29wAGR1bXAAbnVtX2tleXNfY21wAHVzZSBzdHJpcABtYXAAZmxhdE1hcABXZWFrTWFwAGV4cGVjdGluZyAneycgYWZ0ZXIgXHAAbG9nMXAAaGFzT3duAGl0ZXJhdG9yX2Nsb3NlX3JldHVybgBwcm9taXNlIHNlbGYgcmVzb2x1dGlvbgBvdXQgb2YgbWVtb3J5IGluIHJlZ2V4cCBleGVjdXRpb24AZGVzY3JpcHRpb24AcHJveHk6IGRlZmluZVByb3BlcnR5IGV4Y2VwdGlvbgBqc19hc3luY19nZW5lcmF0b3JfcmVzb2x2ZV9mdW5jdGlvbgBqc19jcmVhdGVfZnVuY3Rpb24Ac2V0L2FkZCBpcyBub3QgYSBmdW5jdGlvbgByZXR1cm4gbm90IGluIGEgZnVuY3Rpb24AQXN5bmNHZW5lcmF0b3JGdW5jdGlvbgBjYWxsRXh0ZXJuYWxGdW5jdGlvbgBBc3luY0Z1bmN0aW9uAGF3YWl0IGluIGRlZmF1bHQgZXhwcmVzc2lvbgB5aWVsZCBpbiBkZWZhdWx0IGV4cHJlc3Npb24AaW52YWxpZCBkZWNpbWFsIGVzY2FwZSBpbiByZWd1bGFyIGV4cHJlc3Npb24AYmFjayByZWZlcmVuY2Ugb3V0IG9mIHJhbmdlIGluIHJlZ3VsYXIgZXhwcmVzc2lvbgBpbnZhbGlkIGVzY2FwZSBzZXF1ZW5jZSBpbiByZWd1bGFyIGV4cHJlc3Npb24AZXhwZWN0ZWQgJ29mJyBvciAnaW4nIGluIGZvciBjb250cm9sIGV4cHJlc3Npb24AdG9vIGNvbXBsaWNhdGVkIGRlc3RydWN0dXJpbmcgZXhwcmVzc2lvbgBleHBlY3RlZCAnfScgYWZ0ZXIgdGVtcGxhdGUgZXhwcmVzc2lvbgB0b1ByZWNpc2lvbgBhc2luAGpvaW4AbWluAGNvcHlXaXRoaW4AdGVtcGxhdGUgbGl0ZXJhbCBjYW5ub3QgYXBwZWFyIGluIGFuIG9wdGlvbmFsIGNoYWluAGNpcmN1bGFyIHByb3RvdHlwZSBjaGFpbgBhc3NpZ24AaXNGcm96ZW4AbWFya19jaGlsZHJlbgAocG9zICsgbGVuKSA8PSBiY19idWZfbGVuAHVuZXhwZWN0ZWQgZWxsaXBzaXMgdG9rZW4AdGhlbgBzZXR0ZXIgaXMgZm9yYmlkZGVuAG51bGwgb3IgdW5kZWZpbmVkIGFyZSBmb3JiaWRkZW4AYXRhbgBuYW4Abm90IGEgYm9vbGVhbgBCb29sZWFuAGdjX3NjYW4AYmFkIG5vcm1hbGl6YXRpb24gZm9ybQBKU19OZXdTeW1ib2xGcm9tQXRvbQBmcm9tAHJhbmRvbQB0cmltAGltdWwAbm90IGEgc3ltYm9sAFN5bWJvbABSZWdFeHAgZXhlYyBtZXRob2QgbXVzdCByZXR1cm4gYW4gb2JqZWN0IG9yIG51bGwAcGFyZW50IHByb3RvdHlwZSBtdXN0IGJlIGFuIG9iamVjdCBvciBudWxsAGNhbm5vdCBzZXQgcHJvcGVydHkgJyVzJyBvZiBudWxsAGNhbm5vdCByZWFkIHByb3BlcnR5ICclcycgb2YgbnVsbABOdWxsAGZpbGwAbmV3IEFycmF5QnVmZmVyIGlzIHRvbyBzbWFsbABUeXBlZEFycmF5IGxlbmd0aCBpcyB0b28gc21hbGwAY2FsbABkb3RBbGwAbWF0Y2hBbGwAcmVwbGFjZUFsbABjZWlsAHVwZGF0ZV9sYWJlbABiY19idWZbcG9zXSA9PSBPUF9sYWJlbABldmFsAGludmFsaWQgbnVtYmVyIGxpdGVyYWwAbWFsZm9ybWVkIGVzY2FwZSBzZXF1ZW5jZSBpbiBzdHJpbmcgbGl0ZXJhbABKU19TZXRQcm9wZXJ0eUludGVybmFsAEpTX0dldE93blByb3BlcnR5TmFtZXNJbnRlcm5hbABfX0pTX0V2YWxJbnRlcm5hbAB0b0V4cG9uZW50aWFsAHNlYWwAZ2xvYmFsAGJsaW5rAF9fZGF0ZV9jbG9jawBzdGFjawBscmVfZXhlY19iYWNrdHJhY2sAcy0+aXNfd2VhawBpAHNldE1vbnRoAGdldE1vbnRoAHNldFVUQ01vbnRoAGdldFVUQ01vbnRoAGludmFsaWQga2V5d29yZDogd2l0aABzdGFydHNXaXRoAGVuZHNXaXRoAHByb3AgPT0gSlNfQVRPTV9sZW5ndGgAaW52YWxpZCBhcnJheSBsZW5ndGgAaW52YWxpZCBhcnJheSBidWZmZXIgbGVuZ3RoAGludmFsaWQgbGVuZ3RoAGludmFsaWQgYnl0ZUxlbmd0aABNYXRoAHB1c2gAYWNvc2gASlNfUmVzaXplQXRvbUhhc2gAYXNpbmgAYXRhbmgAYnJlYWsgbXVzdCBiZSBpbnNpZGUgbG9vcCBvciBzd2l0Y2gAbWF0Y2gAY2F0Y2gAc2VhcmNoAGZvckVhY2gAbG9nAEFycmF5IHRvbyBsb25nAHN0cmluZyB0b28gbG9uZwBBcnJheSBsb28gbG9uZwBzdWJzdHJpbmcAY2Fubm90IGNvbnZlcnQgc3ltYm9sIHRvIHN0cmluZwB1bmV4cGVjdGVkIGVuZCBvZiBzdHJpbmcAbm90IGEgc3RyaW5nAGludmFsaWQgY2hhcmFjdGVyIGluIGEgSlNPTiBzdHJpbmcAdG9TdHJpbmcAdG9EYXRlU3RyaW5nAHRvTG9jYWxlRGF0ZVN0cmluZwB0b1RpbWVTdHJpbmcAdG9Mb2NhbGVUaW1lU3RyaW5nAHRvTG9jYWxlU3RyaW5nAHRvR01UU3RyaW5nAEpTU3RyaW5nAHRvSVNPU3RyaW5nAHRvVVRDU3RyaW5nAGR1cGxpY2F0ZSBpbXBvcnQgYmluZGluZwBpbnZhbGlkIGltcG9ydCBiaW5kaW5nAGJpZwByZWdleHAgbXVzdCBoYXZlIHRoZSAnZycgZmxhZwBvZgBpbmYAZGlmZiA9PSAoaW50OF90KWRpZmYAZGlmZiA9PSAoaW50MTZfdClkaWZmAGhyZWYAZ2NfZGVjcmVmAGZyZWVfdmFyX3JlZgBvcHRpbWl6ZV9zY29wZV9tYWtlX2dsb2JhbF9yZWYAcmVzZXRfd2Vha19yZWYAZGVsZXRlX3dlYWtfcmVmAG9wdGltaXplX3Njb3BlX21ha2VfcmVmAGluZGV4T2YAbGFzdEluZGV4T2YAdmFsdWVPZgBzZXRQcm90b3R5cGVPZgBnZXRQcm90b3R5cGVPZgBpc1Byb3RvdHlwZU9mACUuKmYAZm9udHNpemUAbmV3X3NpemUgPD0gc2gtPnByb3Bfc2l6ZQBkZXNjciA8IHJ0LT5hdG9tX3NpemUAYXRvbSA8IHJ0LT5hdG9tX3NpemUAY29tcHV0ZV9zdGFja19zaXplAG4gPCBidWZfc2l6ZQBub3JtYWxpemUAZnJlZXplAHJlc29sdmUAdG9QcmltaXRpdmUAcHV0X2x2YWx1ZQB1bmtub3duIHVuaWNvZGUgcHJvcGVydHkgdmFsdWUAcmVzdCBlbGVtZW50IGNhbm5vdCBoYXZlIGEgZGVmYXVsdCB2YWx1ZQBpbnZhbGlkIHJldCB2YWx1ZQBfX0pTX0F0b21Ub1ZhbHVlAF9fcXVvdGUAaXNGaW5pdGUAZGVsZXRlAGNyZWF0ZQBzZXREYXRlAGdldERhdGUAc2V0VVRDRGF0ZQBnZXRVVENEYXRlAEludmFsaWQgRGF0ZQByZXZlcnNlAHBhcnNlAHByb3h5IHByZXZlbnRFeHRlbnNpb25zIGhhbmRsZXIgcmV0dXJuZWQgZmFsc2UAUHJvbWlzZQB0b0xvd2VyQ2FzZQB0b0xvY2FsZUxvd2VyQ2FzZQB0b1VwcGVyQ2FzZQB0b0xvY2FsZVVwcGVyQ2FzZQBpZ25vcmVDYXNlAGxvY2FsZUNvbXBhcmUAcHJveHk6IGluY29uc2lzdGVudCBwcm90b3R5cGUAcHJveHk6IGJhZCBwcm90b3R5cGUAbm90IGEgcHJvdG90eXBlAGludmFsaWQgb2JqZWN0IHR5cGUAdW5lc2NhcGUAbm9uZQByZXN0IGVsZW1lbnQgbXVzdCBiZSB0aGUgbGFzdCBvbmUAbXVsdGlsaW5lACAgcGMybGluZQBzb21lAEpTX0ZyZWVSdW50aW1lAEpTUnVudGltZQBzZXRUaW1lAGdldFRpbWUAc2V0X29iamVjdF9uYW1lAGV4cGVjdGluZyBwcm9wZXJ0eSBuYW1lAHVua25vd24gdW5pY29kZSBwcm9wZXJ0eSBuYW1lAGludmFsaWQgcHJvcGVydHkgbmFtZQBkdXBsaWNhdGUgX19wcm90b19fIHByb3BlcnR5IG5hbWUAaW52YWxpZCByZWRlZmluaXRpb24gb2YgcGFyYW1ldGVyIG5hbWUAZXhwZWN0aW5nIGdyb3VwIG5hbWUAZHVwbGljYXRlIGdyb3VwIG5hbWUAaW52YWxpZCBncm91cCBuYW1lAGR1cGxpY2F0ZSBsYWJlbCBuYW1lAGludmFsaWQgZmlyc3QgY2hhcmFjdGVyIG9mIHByaXZhdGUgbmFtZQBpbnZhbGlkIGxleGljYWwgdmFyaWFibGUgbmFtZQBpbnZhbGlkIG1ldGhvZCBuYW1lAGV4cGVjdGluZyBmaWVsZCBuYW1lAGludmFsaWQgZmllbGQgbmFtZQBjbGFzcyBzdGF0ZW1lbnQgcmVxdWlyZXMgYSBuYW1lAGZpbGVOYW1lAGNvbXBpbGUAb2JqZWN0IGlzIG5vdCBleHRlbnNpYmxlAHByb3h5OiBpbmNvbnNpc3RlbnQgaXNFeHRlbnNpYmxlAGNhbm5vdCBoYXZlIHNldHRlci9nZXR0ZXIgYW5kIHZhbHVlIG9yIHdyaXRhYmxlAHByb3BlcnR5IGlzIG5vdCBjb25maWd1cmFibGUAdmFsdWUgaXMgbm90IGl0ZXJhYmxlAHByb3BlcnR5SXNFbnVtZXJhYmxlAG1pc3NpbmcgaW5pdGlhbGl6ZXIgZm9yIGNvbnN0IHZhcmlhYmxlAGxleGljYWwgdmFyaWFibGUAaW52YWxpZCByZWRlZmluaXRpb24gb2YgYSB2YXJpYWJsZQByZXZvY2FibGUAc3RyaWtlAGludmFsaWQgY2xhc3MgcmFuZ2UAbWVzc2FnZQBhc3luY19mdW5jX2ZyZWUAaW52YWxpZCBsdmFsdWUgaW4gc3RyaWN0IG1vZGUAaW52YWxpZCB2YXJpYWJsZSBuYW1lIGluIHN0cmljdCBtb2RlAGNhbm5vdCBkZWxldGUgYSBkaXJlY3QgcmVmZXJlbmNlIGluIHN0cmljdCBtb2RlAG9jdGFsIGVzY2FwZSBzZXF1ZW5jZXMgYXJlIG5vdCBhbGxvd2VkIGluIHN0cmljdCBtb2RlAG9jdGFsIGxpdGVyYWxzIGFyZSBkZXByZWNhdGVkIGluIHN0cmljdCBtb2RlAHVuaWNvZGUAICBieXRlY29kZQBKU0Z1bmN0aW9uQnl0ZWNvZGUAc2tpcF9kZWFkX2NvZGUAaW52YWxpZCBhcmd1bWVudCBuYW1lIGluIHN0cmljdCBjb2RlAGludmFsaWQgZnVuY3Rpb24gbmFtZSBpbiBzdHJpY3QgY29kZQBpbnZhbGlkIHJlZGVmaW5pdGlvbiBvZiBnbG9iYWwgaWRlbnRpZmllciBpbiBtb2R1bGUgY29kZQBpbXBvcnQubWV0YSBvbmx5IHZhbGlkIGluIG1vZHVsZSBjb2RlAGZyb21DaGFyQ29kZQBpbnZhbGlkIGZvciBpbi9vZiBsZWZ0IGhhbmQtc2lkZQBpbnZhbGlkIGFzc2lnbm1lbnQgbGVmdC1oYW5kIHNpZGUAcmVkdWNlAHNvdXJjZQAndGhpcycgY2FuIGJlIGluaXRpYWxpemVkIG9ubHkgb25jZQBwcm9wZXJ0eSBjb25zdHJ1Y3RvciBhcHBlYXJzIG1vcmUgdGhhbiBvbmNlAGludmFsaWQgVVRGLTggc2VxdWVuY2UAY2lyY3VsYXIgcmVmZXJlbmNlAHNsaWNlAHNwbGljZQByYWNlAHJlcGxhY2UAJSsuKmUAdW5leHBlY3RlZCAnYXdhaXQnIGtleXdvcmQAdW5leHBlY3RlZCAneWllbGQnIGtleXdvcmQAbWFwX2RlY3JlZl9yZWNvcmQAaXRlcmF0b3IgZG9lcyBub3QgaGF2ZSBhIHRocm93IG1ldGhvZABvYmplY3QgbmVlZHMgdG9JU09TdHJpbmcgbWV0aG9kACdzdXBlcicgaXMgb25seSB2YWxpZCBpbiBhIG1ldGhvZABmcm91bmQAYnJlYWsvY29udGludWUgbGFiZWwgbm90IGZvdW5kAG91dCBvZiBib3VuZABmaW5kAGJpbmQAaW52YWxpZCBpbmRleCBmb3IgYXBwZW5kAGV4dHJhbmVvdXMgY2hhcmFjdGVycyBhdCB0aGUgZW5kAHVuZXhwZWN0ZWQgZGF0YSBhdCB0aGUgZW5kAHVuZXhwZWN0ZWQgZW5kAGludmFsaWQgaW5jcmVtZW50L2RlY3JlbWVudCBvcGVyYW5kAGludmFsaWQgJ2luc3RhbmNlb2YnIHJpZ2h0IG9wZXJhbmQAaW52YWxpZCAnaW4nIG9wZXJhbmQAdHJpbUVuZABwYWRFbmQAYm9sZAAlbGxkAGdjX2RlY3JlZl9jaGlsZAByZXNvbHZlX3Njb3BlX3ByaXZhdGVfZmllbGQAY2Fubm90IGRlbGV0ZSBhIHByaXZhdGUgY2xhc3MgZmllbGQAZXhwZWN0aW5nIDxicmFuZD4gcHJpdmF0ZSBmaWVsZAAlcyBpcyBub3QgaW5pdGlhbGl6ZWQAZml4ZWQAdG9GaXhlZABzZXRfb2JqZWN0X25hbWVfY29tcHV0ZWQAcmVnZXggbm90IHN1cHBvcnRlZABldmFsIGlzIG5vdCBzdXBwb3J0ZWQAUmVnRXhwIGFyZSBub3Qgc3VwcG9ydGVkAGludGVycnVwdGVkACVzIG9iamVjdCBleHBlY3RlZABpZGVudGlmaWVyIGV4cGVjdGVkAGJ5dGVjb2RlIGZ1bmN0aW9uIGV4cGVjdGVkAHN0cmluZyBleHBlY3RlZABmcm9tIGNsYXVzZSBleHBlY3RlZABmdW5jdGlvbiBuYW1lIGV4cGVjdGVkAHZhcmlhYmxlIG5hbWUgZXhwZWN0ZWQAbWV0YSBleHBlY3RlZAByZWplY3RlZABtZW1vcnkgYWxsb2NhdGVkAG1lbW9yeSB1c2VkAGRlcml2ZWQgY2xhc3MgY29uc3RydWN0b3IgbXVzdCByZXR1cm4gYW4gb2JqZWN0IG9yIHVuZGVmaW5lZABjYW5ub3Qgc2V0IHByb3BlcnR5ICclcycgb2YgdW5kZWZpbmVkAGNhbm5vdCByZWFkIHByb3BlcnR5ICclcycgb2YgdW5kZWZpbmVkAGZsYWdzIG11c3QgYmUgdW5kZWZpbmVkAFVuZGVmaW5lZABwcml2YXRlIGNsYXNzIGZpZWxkIGlzIGFscmVhZHkgZGVmaW5lZAAnJXMnIGlzIG5vdCBkZWZpbmVkAGdyb3VwIG5hbWUgbm90IGRlZmluZWQAYWxsU2V0dGxlZABmdWxmaWxsZWQAY2Fubm90IGJlIGNhbGxlZABpc1NlYWxlZAAhc2gtPmlzX2hhc2hlZAB2YXJfcmVmLT5pc19kZXRhY2hlZABBcnJheUJ1ZmZlciBpcyBkZXRhY2hlZABhZGQAJSswN2QAJTA0ZAAlMDJkJTAyZAAlMDJkLyUwMmQvJTAqZAAlLjNzICUuM3MgJTAyZCAlMCpkADolZABpbnZhbGlkIHRocm93IHZhciB0eXBlICVkAHNjAGpzX2RlZl9tYWxsb2MAdHJ1bmMAZ2MAZXhlYwAvdG1wL3F1aWNranMvcXVpY2tqcy5jAC90bXAvcXVpY2tqcy9saWJyZWdleHAuYwAvdG1wL3F1aWNranMvbGlidW5pY29kZS5jAHN1YgBwcm9taXNlX3JlYWN0aW9uX2pvYgBqc19wcm9taXNlX3Jlc29sdmVfdGhlbmFibGVfam9iAHJ3YQBfX2xvb2t1cFNldHRlcl9fAF9fZGVmaW5lU2V0dGVyX18AX19sb29rdXBHZXR0ZXJfXwBfX2RlZmluZUdldHRlcl9fAF9fcHJvdG9fXwBbU3ltYm9sLnNwbGl0XQBbU3ltYm9sLnNwZWNpZXNdAFtTeW1ib2wuaXRlcmF0b3JdAFtTeW1ib2wuYXN5bmNJdGVyYXRvcl0AW1N5bWJvbC5tYXRjaEFsbF0AW1N5bWJvbC5tYXRjaF0AW1N5bWJvbC5zZWFyY2hdAFtTeW1ib2wudG9TdHJpbmdUYWddAFtTeW1ib2wudG9QcmltaXRpdmVdAFt1bnN1cHBvcnRlZCB0eXBlXQBbZnVuY3Rpb24gYnl0ZWNvZGVdAFtTeW1ib2wuaGFzSW5zdGFuY2VdAFtTeW1ib2wucmVwbGFjZV0AWwAlMDJkOiUwMmQ6JTAyZC4lMDNkWgBQT1NJVElWRV9JTkZJTklUWQBORUdBVElWRV9JTkZJTklUWQBwLT5jbGFzc19pZCA9PSBKU19DTEFTU19BUlJBWQBzdGFja19sZW4gPCBQT1BfU1RBQ0tfTEVOX01BWAAtJTAyZC0lMDJkVABKU19BdG9tR2V0U3RyUlQAb3Bjb2RlIDwgUkVPUF9DT1VOVABCWVRFU19QRVJfRUxFTUVOVAAlMDJkOiUwMmQ6JTAyZCBHTVQASlNfVkFMVUVfR0VUX1RBRyhzZi0+Y3VyX2Z1bmMpID09IEpTX1RBR19PQkpFQ1QAdmFyX2tpbmQgPT0gSlNfVkFSX1BSSVZBVEVfU0VUVEVSAE1BWF9TQUZFX0lOVEVHRVIATUlOX1NBRkVfSU5URUdFUgBpc05hTgBEYXRlIHZhbHVlIGlzIE5hTgB0b0pTT04ARVBTSUxPTgBOQU4AJTAyZDolMDJkOiUwMmQgJWNNAHMtPmxhYmVsX3Nsb3RzW2xhYmVsXS5maXJzdF9yZWxvYyA9PSBOVUxMAGxhYmVsX3Nsb3RzW2ldLmZpcnN0X3JlbG9jID09IE5VTEwAcHJzICE9IE5VTEwAc2YtPmN1cl9zcCAhPSBOVUxMAHNmICE9IE5VTEwAbXIxICE9IE5VTEwAdmFyX2tpbmQgIT0gSlNfVkFSX05PUk1BTABiLT5mdW5jX2tpbmQgPT0gSlNfRlVOQ19OT1JNQUwAZW5jb2RlVVJJAGRlY29kZVVSSQBQSQBzcGVjaWFsID09IFBVVF9MVkFMVUVfTk9LRUVQIHx8IHNwZWNpYWwgPT0gUFVUX0xWQUxVRV9OT0tFRVBfREVQVEgAcy0+c3RhdGUgPT0gSlNfQVNZTkNfR0VORVJBVE9SX1NUQVRFX0VYRUNVVElORwBJTkYAMDEyMzQ1Njc4OUFCQ0RFRgBTSVpFAE1BWF9WQUxVRQBNSU5fVkFMVUUATkFNRQBldmFsX3R5cGUgPT0gSlNfRVZBTF9UWVBFX0dMT0JBTCB8fCBldmFsX3R5cGUgPT0gSlNfRVZBTF9UWVBFX01PRFVMRQBwLT5nY19vYmpfdHlwZSA9PSBKU19HQ19PQkpfVFlQRV9KU19PQkpFQ1QgfHwgcC0+Z2Nfb2JqX3R5cGUgPT0gSlNfR0NfT0JKX1RZUEVfRlVOQ1RJT05fQllURUNPREUATE9HMkUATE9HMTBFAHMtPnN0YXRlID09IEpTX0FTWU5DX0dFTkVSQVRPUl9TVEFURV9BV0FJVElOR19SRVRVUk4gfHwgcy0+c3RhdGUgPT0gSlNfQVNZTkNfR0VORVJBVE9SX1NUQVRFX0NPTVBMRVRFRABVVEMAPGlucHV0PgA8aW5pdFNjcmlwdD4APGV2YWxTY3JpcHQ+ADxzZXQ+ADxhbm9ueW1vdXM+ADxjb21tRnVuPgA8Y2FsbEV4dGVybmFsRnVuY3Rpb24+ADxudWxsPgAmcXVvdDsAc2V0VWludDgAZ2V0VWludDgAc2V0SW50OABnZXRJbnQ4AG1hbGZvcm1lZCBVVEYtOAByYWRpeCBtdXN0IGJlIGJldHdlZW4gMiBhbmQgMzYAc2V0VWludDE2AGdldFVpbnQxNgBzZXRJbnQxNgBnZXRJbnQxNgBhcmdjID09IDUAc2V0RmxvYXQ2NABnZXRGbG9hdDY0AGFyZ2MgPT0gMwBhdGFuMgBsb2cyAFNRUlQxXzIAU1FSVDIATE4yAGNsejMyAHNldFVpbnQzMgBnZXRVaW50MzIAc2V0SW50MzIAZ2V0SW50MzIAc2V0RmxvYXQzMgBnZXRGbG9hdDMyAHN0YWNrX2xlbiA+PSAyAEpTX0F0b21Jc051bWVyaWNJbmRleDEAanNfZmN2dDEAZXhwbTEAbHMtPmFkZHIgPT0gLTEAc3RhY2tfbGVuID49IDEAcC0+c2hhcGUtPmhlYWRlci5yZWZfY291bnQgPT0gMQBzdGFja19sZW4gPT0gMQBqc19mcmVlX3NoYXBlMABsb2cxMABMTjEwAHAtPnJlZl9jb3VudCA+IDAAdmFyX3JlZi0+aGVhZGVyLnJlZl9jb3VudCA+IDAAc3RhY2tfc2l6ZSA+IDAAY3Bvb2xfaWR4ID49IDAAcnQtPmF0b21fY291bnQgPj0gMABscy0+cmVmX2NvdW50ID49IDAAcy0+aXNfZXZhbCB8fCBzLT5jbG9zdXJlX3Zhcl9jb3VudCA9PSAwAHAtPnJlZl9jb3VudCA9PSAwAGN0eC0+aGVhZGVyLnJlZl9jb3VudCA9PSAwAHNoLT5oZWFkZXIucmVmX2NvdW50ID09IDAAcC0+bWFyayA9PSAwAChwci0+dS5pbml0LnJlYWxtX2FuZF9pZCAmIDMpID09IDAAKG5ld19oYXNoX3NpemUgJiAobmV3X2hhc2hfc2l6ZSAtIDEpKSA9PSAwAGkgIT0gMABzaXplICE9IDAAXiRcLiorPygpW117fXwvADwvAG1pc3NpbmcgYmluZGluZyBwYXR0ZXJuLi4uAGFzeW5jIGZ1bmN0aW9uICoACn0pAGxpc3RfZW1wdHkoJnJ0LT5nY19vYmpfbGlzdCkAaiA9PSAoc2gtPnByb3BfY291bnQgLSBzaC0+ZGVsZXRlZF9wcm9wX2NvdW50KQBKU19Jc1VuZGVmaW5lZChmdW5jX3JldCkAIV9fSlNfQXRvbUlzVGFnZ2VkSW50KGRlc2NyKQAhYXRvbV9pc19mcmVlKHApAChudWxsKQAgKG5hdGl2ZSkAanNfY2xhc3NfaGFzX2J5dGVjb2RlKHAtPmNsYXNzX2lkKQB1bmNvbnNpc3RlbnQgc3RhY2sgc2l6ZTogJWQgJWQgKHBjPSVkKQBieXRlY29kZSBidWZmZXIgb3ZlcmZsb3cgKG9wPSVkLCBwYz0lZCkAc3RhY2sgb3ZlcmZsb3cgKG9wPSVkLCBwYz0lZCkAc3RhY2sgdW5kZXJmbG93IChvcD0lZCwgcGM9JWQpAGludmFsaWQgb3Bjb2RlIChvcD0lZCwgcGM9JWQpACg/OikAbm8gZnVuY3Rpb24gZmlsZW5hbWUgZm9yIGltcG9ydCgpAC1fLiF+KicoKQAgYW5vbnltb3VzKABTeW1ib2woAGV4cGVjdGluZyAnfScAY2xhc3MgY29uc3RydWN0b3JzIG11c3QgYmUgaW52b2tlZCB3aXRoICduZXcnAGV4cGVjdGluZyAnYXMnAHVuZXhwZWN0ZWQgdG9rZW4gaW4gZXhwcmVzc2lvbjogJyUuKnMnAHVuZXhwZWN0ZWQgdG9rZW46ICclLipzJwByZWRlY2xhcmF0aW9uIG9mICclcycAZHVwbGljYXRlIGV4cG9ydGVkIG5hbWUgJyVzJwBjaXJjdWxhciByZWZlcmVuY2Ugd2hlbiBsb29raW5nIGZvciBleHBvcnQgJyVzJyBpbiBtb2R1bGUgJyVzJwBDb3VsZCBub3QgZmluZCBleHBvcnQgJyVzJyBpbiBtb2R1bGUgJyVzJwBjb3VsZCBub3QgbG9hZCBtb2R1bGUgJyVzJwBjYW5ub3QgZGVmaW5lIHZhcmlhYmxlICclcycAdW5kZWZpbmVkIHByaXZhdGUgZmllbGQgJyVzJwB1bnN1cHBvcnRlZCByZWZlcmVuY2UgdG8gJ3N1cGVyJwBpbnZhbGlkIHVzZSBvZiAnc3VwZXInACdmb3IgYXdhaXQnIGxvb3Agc2hvdWxkIGJlIHVzZWQgd2l0aCAnb2YnAGV4cGVjdGluZyAnJWMnAHVucGFyZW50aGVzaXplZCB1bmFyeSBleHByZXNzaW9uIGNhbid0IGFwcGVhciBvbiB0aGUgbGVmdC1oYW5kIHNpZGUgb2YgJyoqJwBpbnZhbGlkIHVzZSBvZiAnaW1wb3J0KCknAGV4cGVjdGluZyAlJQA7Lz86QCY9KyQsIwA9IgBzZXQgAGdldCAAW29iamVjdCAAYXN5bmMgZnVuY3Rpb24gAGJvdW5kIAAlLjNzLCAlMDJkICUuM3MgJTAqZCAAYXN5bmMgADogACAgICAgICAgICAACikgewoACkpTT2JqZWN0IGNsYXNzZXMKACUtMjBzICU4cyAlOHMKACAgJTVkICAlMi4wZCAlcwoAICAlM3UgKyAlLTJ1ICAlcwoAICBtYWxsb2NfdXNhYmxlX3NpemUgdW5hdmFpbGFibGUKACUtMjBzICU4bGxkCgAlLTIwcyAlOGxsZCAlOGxsZAoAX19KU19GcmVlVmFsdWU6IHVua25vd24gdGFnPSVkCgAlLTIwcyAlOGxsZCAlOGxsZCAgKCUwLjFmIHBlciBmYXN0IGFycmF5KQoAJS0yMHMgJThsbGQgJThsbGQgICglMC4xZiBwZXIgb2JqZWN0KQoAJS0yMHMgJThsbGQgJThsbGQgICglMC4xZiBwZXIgZnVuY3Rpb24pCgAlLTIwcyAlOGxsZCAlOGxsZCAgKCUwLjFmIHBlciBhdG9tKQoAJS0yMHMgJThsbGQgJThsbGQgICglMC4xZiBwZXIgYmxvY2spCgAlLTIwcyAlOGxsZCAlOGxsZCAgKCVkIG92ZXJoZWFkLCAlMC4xZiBhdmVyYWdlIHNsYWNrKQoAJS0yMHMgJThsbGQgJThsbGQgICglMC4xZiBwZXIgc3RyaW5nKQoAJS0yMHMgJThsbGQgJThsbGQgICglMC4xZiBwZXIgc2hhcGUpCgBRdWlja0pTIG1lbW9yeSB1c2FnZSAtLSAxLjAuMCB2ZXJzaW9uLCAlZC1iaXQsIG1hbGxvYyBsaW1pdDogJWxsZAoKAAAAAIwAQeyDAQsNjQAAADoAAAA7AAAAjgBBhIQBCz2PAAAAPAAAAD0AAACQAAAAPAAAAD0AAACRAAAAPAAAAD0AAACSAAAAPAAAAD0AAACTAAAAOgAAADsAAACTAEHMhAELDZYAAAA8AAAAPQAAAIwAQeSEAQvZApcAAAA+AAAAPwAAAJcAAABAAAAAQQAAAJcAAABCAAAAQwAAAJcAAABEAAAARQAAAJgAAABAAAAAQQAAAJkAAABGAAAARwAAAJoAAABIAAAAAAAAAJsAAABJAAAAAAAAAJwAAABJAAAAAAAAAJ0AAABKAAAASwAAAJ4AAABKAAAASwAAAJ8AAABKAAAASwAAAKAAAABKAAAASwAAAKEAAABKAAAASwAAAKIAAABKAAAASwAAAKMAAABKAAAASwAAAKQAAABKAAAASwAAAKUAAABKAAAASwAAAKYAAABKAAAASwAAAKcAAABMAAAATQAAAKgAAABMAAAATQAAAKkAAABMAAAATQAAAKoAAABMAAAATQAAAKsAAABOAAAATwAAAKwAAABOAAAATwAAAK0AAABQAAAAUQAAAK4AAABQAAAAUQAAAK8AAABSAAAAUwAAALAAAABUAAAAVQBBzIcBCwFWAEHchwELDVcAAAAAAAAAWAAAAFkAQYiIAQsBWgBBlIgBCwlbAAAAXAAAAF0AQbCIAQvTApgmAADgAAAA0wkAAPgAAADADgAAMAAAAJAiAAAQAAAAjyoAAFgAAACMAAAAXgAAAF8AAABgAAAAYQAAAGIAAABjAAAAZAAAAGUAAABmAAAAAFMAAMBTAABwVAAAwFQAAABVAAAgVQAADAsFBAICAACyAAAAZwAAAGgAAACzAAAAaQAAAGoAAAC0AAAAaQAAAGoAAAC1AAAAQAAAAEEAAAC2AAAAawAAAGwAAAC3AAAAawAAAGwAAAAvAAAAbQAAAG4AAAC4AAAAQAAAAEEAAAC5AAAAbwAAAHAAAAAAAAAAqxUAANwVAADnFQAAnxUAANIVAAD2FQAAtRUAAMMVAABjb3B5V2l0aGluAGVudHJpZXMAZmlsbABmaW5kAGZpbmRJbmRleABmbGF0AGZsYXRNYXAAaW5jbHVkZXMAa2V5cwB2YWx1ZXMAAAAAAAEBAgICAwBBkIsBC5UobnVsbABmYWxzZQB0cnVlAGlmAGVsc2UAcmV0dXJuAHZhcgB0aGlzAGRlbGV0ZQB2b2lkAHR5cGVvZgBuZXcAaW4AaW5zdGFuY2VvZgBkbwB3aGlsZQBmb3IAYnJlYWsAY29udGludWUAc3dpdGNoAGNhc2UAZGVmYXVsdAB0aHJvdwB0cnkAY2F0Y2gAZmluYWxseQBmdW5jdGlvbgBkZWJ1Z2dlcgB3aXRoAGNsYXNzAGNvbnN0AGVudW0AZXhwb3J0AGV4dGVuZHMAaW1wb3J0AHN1cGVyAGltcGxlbWVudHMAaW50ZXJmYWNlAGxldABwYWNrYWdlAHByaXZhdGUAcHJvdGVjdGVkAHB1YmxpYwBzdGF0aWMAeWllbGQAYXdhaXQAAGxlbmd0aABmaWxlTmFtZQBsaW5lTnVtYmVyAG1lc3NhZ2UAZXJyb3JzAHN0YWNrAG5hbWUAdG9TdHJpbmcAdG9Mb2NhbGVTdHJpbmcAdmFsdWVPZgBldmFsAHByb3RvdHlwZQBjb25zdHJ1Y3RvcgBjb25maWd1cmFibGUAd3JpdGFibGUAZW51bWVyYWJsZQB2YWx1ZQBnZXQAc2V0AG9mAF9fcHJvdG9fXwB1bmRlZmluZWQAbnVtYmVyAGJvb2xlYW4Ac3RyaW5nAG9iamVjdABzeW1ib2wAaW50ZWdlcgB1bmtub3duAGFyZ3VtZW50cwBjYWxsZWUAY2FsbGVyADxldmFsPgA8cmV0PgA8dmFyPgA8YXJnX3Zhcj4APHdpdGg+AGxhc3RJbmRleAB0YXJnZXQAaW5kZXgAaW5wdXQAZGVmaW5lUHJvcGVydGllcwBhcHBseQBqb2luAGNvbmNhdABzcGxpdABjb25zdHJ1Y3QAZ2V0UHJvdG90eXBlT2YAc2V0UHJvdG90eXBlT2YAaXNFeHRlbnNpYmxlAHByZXZlbnRFeHRlbnNpb25zAGhhcwBkZWxldGVQcm9wZXJ0eQBkZWZpbmVQcm9wZXJ0eQBnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IAb3duS2V5cwBhZGQAZG9uZQBuZXh0AHZhbHVlcwBzb3VyY2UAZmxhZ3MAZ2xvYmFsAHVuaWNvZGUAcmF3AG5ldy50YXJnZXQAdGhpcy5hY3RpdmVfZnVuYwA8aG9tZV9vYmplY3Q+ADxjb21wdXRlZF9maWVsZD4APHN0YXRpY19jb21wdXRlZF9maWVsZD4APGNsYXNzX2ZpZWxkc19pbml0PgA8YnJhbmQ+ACNjb25zdHJ1Y3RvcgBhcwBmcm9tAG1ldGEAKmRlZmF1bHQqACoATW9kdWxlAHRoZW4AcmVzb2x2ZQByZWplY3QAcHJvbWlzZQBwcm94eQByZXZva2UAYXN5bmMAZXhlYwBncm91cHMAc3RhdHVzAHJlYXNvbgBnbG9iYWxUaGlzAHRvSlNPTgBPYmplY3QAQXJyYXkARXJyb3IATnVtYmVyAFN0cmluZwBCb29sZWFuAFN5bWJvbABBcmd1bWVudHMATWF0aABKU09OAERhdGUARnVuY3Rpb24AR2VuZXJhdG9yRnVuY3Rpb24ARm9ySW5JdGVyYXRvcgBSZWdFeHAAQXJyYXlCdWZmZXIAU2hhcmVkQXJyYXlCdWZmZXIAVWludDhDbGFtcGVkQXJyYXkASW50OEFycmF5AFVpbnQ4QXJyYXkASW50MTZBcnJheQBVaW50MTZBcnJheQBJbnQzMkFycmF5AFVpbnQzMkFycmF5AEZsb2F0MzJBcnJheQBGbG9hdDY0QXJyYXkARGF0YVZpZXcATWFwAFNldABXZWFrTWFwAFdlYWtTZXQATWFwIEl0ZXJhdG9yAFNldCBJdGVyYXRvcgBBcnJheSBJdGVyYXRvcgBTdHJpbmcgSXRlcmF0b3IAUmVnRXhwIFN0cmluZyBJdGVyYXRvcgBHZW5lcmF0b3IAUHJveHkAUHJvbWlzZQBQcm9taXNlUmVzb2x2ZUZ1bmN0aW9uAFByb21pc2VSZWplY3RGdW5jdGlvbgBBc3luY0Z1bmN0aW9uAEFzeW5jRnVuY3Rpb25SZXNvbHZlAEFzeW5jRnVuY3Rpb25SZWplY3QAQXN5bmNHZW5lcmF0b3JGdW5jdGlvbgBBc3luY0dlbmVyYXRvcgBFdmFsRXJyb3IAUmFuZ2VFcnJvcgBSZWZlcmVuY2VFcnJvcgBTeW50YXhFcnJvcgBUeXBlRXJyb3IAVVJJRXJyb3IASW50ZXJuYWxFcnJvcgA8YnJhbmQ+AFN5bWJvbC50b1ByaW1pdGl2ZQBTeW1ib2wuaXRlcmF0b3IAU3ltYm9sLm1hdGNoAFN5bWJvbC5tYXRjaEFsbABTeW1ib2wucmVwbGFjZQBTeW1ib2wuc2VhcmNoAFN5bWJvbC5zcGxpdABTeW1ib2wudG9TdHJpbmdUYWcAU3ltYm9sLmlzQ29uY2F0U3ByZWFkYWJsZQBTeW1ib2wuaGFzSW5zdGFuY2UAU3ltYm9sLnNwZWNpZXMAU3ltYm9sLnVuc2NvcGFibGVzAFN5bWJvbC5hc3luY0l0ZXJhdG9yAAAAAAABAAAABQABFAUAARUFAAEVBQABFwUAARcBAAEAAQABAAEAAQABAAEAAQABAAEAAQACAAEFAwABCgEBAAABAgEAAQMCAAEBAgABAgMAAQIEAAEDBgABAgMAAQMEAAEEBQABAwMAAQQEAAEFBQABAgIAAQQEAAEDAwABAwMAAQQEAAEFBQADAgENAwEBDQMBAA0DAgENAwIADQMAAQ0DAwEKAQEAAAEAAAABAQIAAQAAAAECAgABAgAAAQEAAAEBAAAGAAAYBQEBDwMCAQoBAgEAAQEBAAEBAQAFAAEXBQABFwUAARcFAQAXBQEAFwUCABcBAgMAAQMAAAYAABgGAAAYBgEAGAUBARcFAQIXBQIAFwECAQABAwAAAQMBAAECAQABAgIAAQMAAAEDAQABBAAABQIBFwUBARcBAgIAAQIBAAECAgABAwIAAQMCAAIDAwUGAgEYAgMBBQYCAhgGAwMYAwABEAMBABADAQEQAwABEQMBABEDAQERAwABEgMBABIDAQESAwAAEAMAARADAQAQAwEAEAMAARIDAQASAwEAEgMAABAFAQAWBQEAFgUAABYFAAEWBQAAFgEBAAABAQEAAQEBAAECAgAKAQAaCgIBGgoBABoKAQAaCgEAGgoBABoHAAIZBwACGQcAAhkFAAIXAQEBAAEBAwABAQMAAQEDAAIDBQUBAQEAAQECAAEDAAABBAQAAQQEAAIEBQUBAAAAAQECAAEBAgABAQIAAQEBAAEBAQABAQEAAQEBAAEBAQABAQIAAQECAAIAAAcCAAAHAgEABwEBAQABAQEAAQEBAAECAQAFAAEXAQIBAAECAQABAgEAAQIBAAECAQABAgEAAQIBAAECAQABAgEAAQIBAAECAQABAgEAAQIBAAECAQABAgEAAQIBAAECAQABAgEAAQIBAAECAQABAgEAAQIBAAEBAQABAAAAAwAACgMAAAoFAAAWBwABGQcAARkHAQAZBwABGQsAAhsHAAIZBwACGQcBARkHAQIZBwEBGQUBARMFAAATAQABAQEAAQEBAAEBAQABAQEAAQEBAAEBAQABAQEAAQEBAAEBAgABBgMAAQsCAAEIAgABCAEAAQACAAEHAgEABwIBAQcBAAECAQABAgEAAQIBAAECAQEAAgEBAAIBAQACAQEAAgEBAQIBAQECAQEBAgEBAQIBAAEDAQABAwEAAQMBAAEDAQEAAwEBAAMBAQADAQEAAwEBAQMBAQEDAQEBAwEBAQMBAAEEAQABBAEAAQQBAAEEAQEABAEBAAQBAQAEAQEABAEBAQQBAQEEAQEBBAEBAQQBAQEAAgEACQIBAAkCAAAJAwAADAEBAQ4BAQEOAQEBDgEBAQ4BAQEAAQEBAAEBAQABAQEAcQAAAHIAAABzAAAAbgBmAGkAbgBpAHQAeQANABAALQAxAAAAYiQAAAMAAAAAAAAAdAAAAEISAAABAQAAdQAAAAAAAACxKwAAAQEAAHYAAAAAAAAAHCAAAAECAQB3AAAAAAAAANAlAAABAgIAdwAAAAAAAABwJgAAAQIEAHcAAAAAAAAANB8AAAECCAB3AAAAAAAAAHwqAAABAhAAdwAAAAAAAABXBgAAAQIgAHcAAAAAAAAAuTEAAAMAAAABAAAAMAAAAFQoAAADAAAAAgAAAHgAAAB6CgAAAwAAAAEAAAB5AAAALCIAAAMAAAAAAAAAegAAAHMzAAADAAAAAgAAAHsAAADuMgAAAwAAAAEAAAB8AAAA3DIAAAMAAAABAAAAfQAAAP0yAAADAAAAAQAAAH4AAACTMgAAAwAAAAIAAAB/AAAAojIAAAEBAACAAAAAAAAAAAwKAAADAAAAAAwAAIEAAAANMwAAAQMAABoVAAAAAAAA3jQAAAMIAADAUgAAAwAAAFclAAADAAAAAgAAAIIAAABeBgAAAwAAAAMAAACDAAAADTMAAAEDAADeNAAAAAAAAGkpAAADAAAAAgAAAIQAAABTDQAAAwAAAAIBAACFAAAAqg0AAAMAAAABAQAAhgAAAA4UAAADAAAAAQEAAIcAAAAOJQAAAwAAAAEBAACIAAAAUxkAAAMAAAAAAQAAiQAAAF0kAAABAgAAigAAAAAAAAB/IQAAAwAAAAEBAACLAAAASBIAAAMABAAAAQAAjAAAABAPAAADAAAAAAEAAIwAAABJEwAAAwAIAAABAACMAAAAszIAAAMJAABJEwAA/////w0zAAABAwAAQxoAAAAAAABGMQAAAwABAAEBAACFAAAADhQAAAMAAQABAQAAhwAAAA4lAAADAAEAAQEAAIgAAABTGQAAAwABAAABAACJAAAAXSQAAAECAQCKAAAAAAAAAH8hAAADAAEAAQEAAIsAAABIEgAAAwABAAABAACMAAAAEA8AAAMJAABIEgAA/////7MyAAADCQAASBIAAP////9JEwAAAwAJAAABAACMAAAADTMAAAEDAACyDQAAAAAAAFMNAAADAAIAAgEAAIUAAACqDQAAAwACAAEBAACGAAAADhQAAAMAAgABAQAAhwAAAA4lAAADAAIAAQEAAIgAAAANMwAAAQMAAD8aAAAAAAAARjEAAAMAAwABAQAAhQAAAA4UAAADAAMAAQEAAIcAAAAOJQAAAwADAAEBAACIAAAADTMAAAEDAACuDQAAAAAAAAwKAAADAAAAAAwAAI0AAAANMwAAAQMAAA0VAAAAAAAADAoAAAMAAQAADAAAjQAAAA0zAAABAwAAABUAAAAAAACiMgAAAQEAAIAAAAAAAAAAlB0AAAMAAAACAAAAjgAAAHIhAAADAAAAAQAAAI8AAABPBgAAAwAAAAEAAACQAAAADTMAAAEDAACMJQAAAAAAAHMkAAADAAAAAQEAAJEAAADlDQAAAwABAAEBAACRAAAAMB8AAAMAAAABAQAAkgAAANswAAADAAEAAQEAAJIAAAAgBgAAAwACAAEBAACSAAAAPywAAAMAAAABAAAAkwAAAKIyAAABAQAAgAAAAAAAAAANMwAAAQMAAH0bAAAAAAAAxTIAAAMAAAAAAAAAlAAAAAwKAAADAAAAAQEAAJUAAAB6GgAAAwABAAEBAACVAAAAKggAAAMAAgABAQAAlQAAAAwKAAADAAAAAQEAAJYAAAB6GgAAAwABAAEBAACWAAAAKggAAAMAAgABAQAAlgAAAA0zAAABAwAAgxUAAAAAAAANMwAAAQMAAFEbAAAAAAAAuyMAAAMAAAAAAAAAlwAAACwiAAADABMAAAEAAJgAAAAiMwAAAwAAAAEAAACZAAAApSIAAAMAAwAAAQAAmAAAAIQiAAADCQAApSIAAP////+ZIgAAAwAjAAABAACYAAAANSIAAAMAEQAAAQAAmAAAAFUiAAADABIAAAEAAJgAAAB1IgAAAwAzAAABAACYAAAAQiIAAAMAMQAAAQAAmAAAAGIiAAADADIAAAEAAJgAAAAODQAAAwAAAAAAAACaAAAAqiYAAAMAAAAAAAAAlwAAAGEZAAADAAEBAAEAAJsAAAB1GQAAAwABAAABAACbAAAAkBkAAAMAAAAAAQAAmwAAAGUgAAADABEAAAEAAJsAAAB6IAAAAwAQAAABAACbAAAAJCUAAAMAIQAAAQAAmwAAADclAAADACAAAAEAAJsAAAB/EAAAAwAxAAABAACbAAAAlBAAAAMAMAAAAQAAmwAAAFoSAAADAEEAAAEAAJsAAABzEgAAAwBAAAABAACbAAAAxxMAAAMAUQAAAQAAmwAAAOATAAADAFAAAAEAAJsAAACGEwAAAwBhAAABAACbAAAAqRMAAAMAYAAAAQAAmwAAABwHAAADAHEAAAEAAJsAAAAjBwAAAwBwAAABAACbAAAAoiYAAAMAAAABAAAAnAAAAHYTAAADAHEGAQEAAJ0AAACWEwAAAwBwBgEBAACdAAAAvBMAAAMAcQUCAQAAnQAAANITAAADAHAFAgEAAJ0AAABPEgAAAwBxBAMBAACdAAAAZRIAAAMAcAQDAQAAnQAAAHYQAAADAHEDBAEAAJ0AAACIEAAAAwBwAwQBAACdAAAAHCUAAAMAMQIBAQAAnQAAACwlAAADADACAQEAAJ0AAABcIAAAAwAxAQIBAACdAAAAbiAAAAMAMAECAQAAnQAAAFkZAAADAAAAAQAAAJ4AAABpGQAAAwAxAAMBAACdAAAAgRkAAAMAMAADAQAAnQAAANw0AAADAAAAAQAAAJ8AAABTdW5Nb25UdWVXZWRUaHVGcmlTYXQAQbCzAQskSmFuRmViTWFyQXByTWF5SnVuSnVsQXVnU2VwT2N0Tm92RGVjAEHgswEL5gwfAAAAHAAAAB8AAAAeAAAAHwAAAB4AAAAfAAAAHwAAAB4AAAAfAAAAHgAAAB8AAAA0CAAAAwAAAAAAAACgAAAAVyUAAAMAAAABAAAAoQAAAJQ3AAADAAAABwAAAKIAAACam5ydnqChoq2ur58AAAAALCIAAAMAAAAAAAAAowAAAEYoAAADAwAA+RUAAAAAAACOKQAAAwMAANxBAAAAAAAAFSUAAAMAAAACAAAApAAAANIjAAADAAAAAQEAAKUAAADDIwAAAwAAAAIAAACmAAAAnAUAAAMAAAADAQAApwAAADgTAAADAAAAAgAAAKgAAACcEgAAAwAAAAEAAACpAAAA1REAAAMAAAABAAAAqgAAABAPAAADAAAAAQEAAKsAAABIEgAAAwABAAEBAACrAAAASRMAAAMAAgABAQAAqwAAAIkoAAADAAAAAQEAAKwAAAB+EQAAAwAAAAEBAACtAAAAcBQAAAMAAAACAQAArgAAAKAQAAADAAAAAQAAAK8AAAADEgAAAwAAAAIAAACwAAAAQh0AAAMAAAACAAAAsQAAABcgAAADAAAAAQEAALIAAABsJAAAAwABAAEBAACyAAAAATEAAAMAAAABAQAAswAAAEkdAAADAAEAAQEAALMAAABrEAAAAwAAAAEAAAC0AAAAURMAAAMAAAABAAAAtQAAAGQaAAADAAAAAgAAALYAAAAsIgAAAwAAAAAAAAC3AAAAdSIAAAMAAAAAAAAAuAAAALsjAAADAAAAAAAAALkAAABWBQAAAwAAAAEAAAC6AAAA4SMAAAMAAAABAAAAuwAAAPkoAAADAAAAAQAAALwAAACJMgAAAQEAAL0AAAC+AAAAeDIAAAMAAAACAQAAvwAAAFYyAAADAAEAAgEAAL8AAABnMgAAAwAAAAEBAADAAAAARTIAAAMAAQABAQAAwAAAAC8fAAADAAAAAQAAAMEAAAAkBgAAAwAAAAIBAADCAAAAOi0AAAMAAAABAAAAwwAAACwiAAADAAAAAAAAAMQAAABeMwAAAwAAAAEAAADFAAAASygAAAEBAADGAAAAAAAAADEZAAABAQAAxwAAAAAAAACzMgAAAwAAAAAAAACUAAAA6w4AAAMAAAABAAAAyAAAABoGAAADAAAAAQEAAMkAAACEJgAAAwABAAEBAADJAAAAfyEAAAMAAgABAQAAyQAAADMaAAADAAMAAQEAAMkAAAAPFwAAAwAEAAEBAADJAAAAqisAAAMAAAABAQAAygAAAM8MAAADAAEAAQEAAMoAAADuHgAAAwAAAAEAAADLAAAANS0AAAMAAAABAQAAzAAAAIIHAAADAAEAAQEAAMwAAACnIwAAAwAAAAEAAADNAAAAryMAAAMAAAABAAAAzgAAAG0TAAADAAAAAQAAAM8AAADhHAAAAwAAAAEBAADQAAAALCIAAAMAAAAAAAAA0QAAAHUiAAADAAEAAAEAANAAAAD2GQAAAwAAAAABAADSAAAAHyEAAAMAAAABAQAA0wAAAN0MAAADAAEAAAEAANIAAADbDAAAAwABAAEBAADTAAAATyUAAAMAAAAAAAAA1AAAAKoKAAADAAAAAQAAANUAAAAyLAAAAwAAAAIBAADWAAAAOCwAAAMAAQACAQAA1gAAAOocAAADAAAAAgAAANcAAAA3GgAAAwABAAEBAADYAAAA1A4AAAMAAAAAAQAA2AAAAEgSAAADAAEAAAEAACkAAACzMgAAAwkAAEgSAAD/////EA8AAAMAAAAAAQAAKQAAAEkTAAADAAIAAAEAACkAAAAJBwAAAwAAAAEAAADZAAAAIB4AAAMAAAABAAAA2gAAAAMjAAADAAAAAAAAANsAAACiMgAAAQEAAIAAAAAAAAAADAoAAAMAAAAADAAAKgAAAA0zAAABAwAA8RQAAAAAAACQDAAAAwAAAAIAAADcAAAAyQ4AAAMAAAABAAAA3QAAAMQ0AAADAAAAAQAAAN4AAAAFJQAAAwAAAAEAAADfAAAAyDUAAAMAAAABAQAA4AAAAEoMAAADAAEAAQEAAOAAAAC+NQAAAwAAAAEBAADhAAAANwwAAAMAAQABAQAA4QAAAEImAAADAAAAAQAAAOIAAABAJgAAAwAAAAEAAADjAAAA0QUAAAAGAAAAAAAAAADwf9g0AAAABgAAAAAAAAAA+H91MAAAAAcAQdDAAQt1KSAAAAMAAAAAAAAA5AAAAGgbAAADAAAAAgAAAOUAAAAXGgAAAwAAAAIAAADmAAAAQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODlAKl8rLS4vAEHQwQELlgMJIAAAAwAAAAEAAADnAAAApC4AAAMAAAABAAAA6AAAANAcAAADAAAAAQAAAOkAAAAsIgAAAwAAAAEBAADqAAAAdSIAAAMAAQAAAQAA6gAAALsjAAADAAAAAAAAAOsAAACQDAAAAwkAAJAMAAAAAAAAyQ4AAAMJAADJDgAAAAAAAMQ0AAADAAAAAQAAAOwAAAAFJQAAAwAAAAEAAADtAAAAshgAAAMAAAABAAAA7gAAALwYAAADAAAAAQAAAO8AAABhNgAAAAYAAP///////+9/azYAAAAGAAABAAAAAAAAANg0AAAABgAAAAAAAAAA+H+tMwAAAAYAAAAAAAAAAPD/mzMAAAAGAAAAAAAAAADwf+M0AAAABgAAAAAAAAAAsDyiNAAAAAYAAP///////z9DszQAAAAGAAD///////8/wywiAAADAAAAAAAAAPAAAAC7IwAAAwAAAAAAAADxAAAAWisAAAMAAAABAAAA8gAAABwMAAADAAAAAQAAAPMAAABvCAAAAwAAAAEAAAD0AAAAACEAAAEEAEHwxAEL4gYFDwAAAwAAAAEAAAD1AAAA/g4AAAMAAAABAAAA9gAAAOsOAAADAAAAAQAAAPcAAADyDgAAAwAAAAEAAAD4AAAApyMAAAMAAAABAQAA+QAAAK8jAAADAAEAAQEAAPkAAABtEwAAAwAAAAEBAAD6AAAApyAAAAMAAgABAQAA+gAAAJwgAAADAAEAAQEAAPoAAABsIQAAAwDEAAEBAAD7AAAAOx8AAAMAxQABAQAA+wAAAHghAAADAMcAAQEAAPsAAACrDAAAAwAAAAIAAAD8AAAAuSEAAAMAAAACAAAA/QAAAFUUAAADAAAAAgAAAP4AAAAyLAAAAwAAAAIAAAD/AAAA5A4AAAMAAAABAAAAAAEAAEQsAAADAAAAAgEAAAEBAABEHwAAAwABAAIBAAABAQAABy4AAAMAAQABAQAAAgEAAL4KAAADAAAAAQEAAAIBAAAsHgAAAwADAAABAAADAQAA/y0AAAMAAgAAAQAAAwEAAMUMAAADCQAA/y0AAP////+0CgAAAwABAAABAAADAQAA4wwAAAMJAAC0CgAA/////ywiAAADAAAAAAAAAAQBAAC7IwAAAwAAAAAAAAAEAQAA/SQAAAMAAAABAAAABQEAANslAAADAAAAAQAAAAYBAACUJQAAAwABAAABAAAHAQAAsiUAAAMAAAAAAQAABwEAAKAlAAADAAEAAAEAAAcBAAC+JQAAAwAAAAABAAAHAQAAszIAAAMABQAAAQAAKQAAAA8WAAADAAAAAQEAAAgBAADhIgAAAwABAAABAAAIAQAAIyAAAAMAAgAAAQAACAEAAA4uAAADAAMAAAEAAAgBAACeLgAAAwAEAAABAAAIAQAABRYAAAMABQABAQAACAEAAPQjAAADAAYAAQEAAAgBAADuEwAAAwAHAAABAAAIAQAAJCAAAAMACAABAQAACAEAACkfAAADAAkAAAEAAAgBAABzKQAAAwAKAAABAAAIAQAACDIAAAMACwAAAQAACAEAAOQZAAADAAwAAAEAAAgBAABDMgAARigAAOEiAAAAAAAAIyAAAAAAAAA/MgAAAAAAACkKAAAAAAAABAwAAAkWAAAEDAAAXSQAAFogAAAAAAAAQzIAADUjAAApHwAAAAAAAHMpAAAAAAAACDIAAAAAAADkGQBB4MsBC9oUDAoAAAMAAAAADAAACQEAAA0zAAABAwAAIRUAAAAAAAAaIQAAAwgAABBmAAAsAAAA5hwAAAMAAAACAQAACgEAALwHAAADAAEAAgEAAAoBAAD2EwAAAwAAAAEGAAALAQAA/xUAAAMAAAABBgAADAEAAE8fAAADAAAAAQYAAA0BAAADLQAAAwAAAAEGAAAOAQAApQoAAAMAAAABBgAADwEAAOsQAAADAAAAAQYAABABAADcHAAAAwAAAAEGAAARAQAAzR0AAAMAAAABBgAAEgEAAJw4AAADAAAAAgcAABMBAADsEAAAAwAAAAEGAAAUAQAA2RkAAAMAAAABBgAAFQEAAIchAAADAAAAAQYAABYBAAAwCAAAAwAAAAIHAAAXAQAA3RwAAAMAAAABBgAAGAEAAM4dAAADAAAAAQYAABkBAACwMQAAAwAAAAEGAAAaAQAARB0AAAMAAAABBgAAGwEAACUhAAADAAAAAQYAABwBAAA9IQAAAwAAAAEGAAAdAQAAQyEAAAMAAAABBgAAHgEAACQhAAADAAAAAQYAAB8BAAA8IQAAAwAAAAEGAAAgAQAAQiEAAAMAAAABBgAAIQEAACo5AAADAAAAAQYAACIBAABeGgAAAwAAAAEGAAAjAQAAojgAAAMAAAABBgAAJAEAAIw5AAADAAAAAQYAACUBAACvCgAAAwAAAAEGAAAmAQAA5QoAAAMAAAACAAAAJwEAACUeAAADAAAAAAAAACgBAAACLQAAAwAAAAEGAAApAQAAMR4AAAMAAAACAAAAKgEAALk4AAADAAAAAQAAACsBAAANMwAAAQMAABohAAAAAAAAKzcAAAAGAABpVxSLCr8FQJI5AAAABgAAFlW1u7FrAkC1OAAAAAYAAO85+v5CLuY/IDcAAAAGAAD+gitlRxX3PyY3AAAABgAADuUmFXvL2z/SNQAAAAYAABgtRFT7IQlApzgAAAAGAADNO39mnqDmP684AAAABgAAzTt/Zp6g9j/dDQAAAwgAAOBoAAAOAAAAJAYAAAMAAAADAAAALAEAALYNAAADAAAAAgAAAC0BAACcBQAAAwABAAMBAACnAAAAeQUAAAMAAAACAAAALgEAAKoNAAADAAAAAgAAAC8BAABwFAAAAwABAAIBAACuAAAA0iMAAAMAAQABAQAApQAAAA4UAAADAAAAAgAAADABAACJKAAAAwABAAEBAACsAAAARQ8AAAMAAAABAAAAMQEAAH4RAAADAAEAAQEAAK0AAABTDQAAAwAAAAMAAAAyAQAAwyMAAAMAAAACAAAAMwEAAA0zAAABAwAA3Q0AAAAAAAAsIgAAAwAAAAAAAAA0AQAAuyMAAAMAAAAAAAAANQEAACIzAAADAAAAAQAAADUBAAANMwAAAQMAAEMeAAAAAAAAuxoAAAEBAAA2AQAAAAAAABYWAAADAAAAAQAAADcBAAAaFgAAAwAAAAEAAAA4AQAADAoAAAMAAAABDAAAOQEAAHoaAAADAAEAAQwAADkBAAAqCAAAAwACAAEMAAA5AQAADTMAAAEDAACIFQAAAAAAAA0zAAABAwAAVhsAAAAAAAAPIQAAAQITADoBAAAAAAAAMiwAAAMAEwACAQAAOwEAAA0zAAABAwAA+BgAAAAAAABfCAAAAwAAAAEAAAA8AQAAojIAAAEBAACAAAAAAAAAAA8hAAABAhQAOgEAAAAAAAAyLAAAAwAUAAIBAAA7AQAADTMAAAEDAADRGAAAAAAAAKIyAAABAQAAgAAAAAAAAAAAIQAAAQEAAD0BAAAAAAAAyhgAAAECAAA+AQAAAAAAAA8hAAABAgAAPwEAAAAAAAADDQAAAQIAAEABAAAAAAAAUw0AAAMAAAABAAAAQQEAAEgSAAADAAEAAAEAAEIBAACzMgAAAwkAAEgSAAD/////EA8AAAMAAAAAAQAAQgEAAEkTAAADAAIAAAEAAEIBAAANMwAAAQEAAEMBAAAAAAAA6hwAAAMAAAACAAAARAEAABoGAAADAAgAAQEAAMkAAACEJgAAAwAJAAEBAADJAAAAfyEAAAMACgABAQAAyQAAADMaAAADAAsAAQEAAMkAAAAPFwAAAwAMAAEBAADJAAAAqisAAAMACAABAQAAygAAAM8MAAADAAkAAQEAAMoAAADuHgAAAwAAAAEAAABFAQAANS0AAAMAAAABAQAARgEAAIIHAAADAAEAAQEAAEYBAABPJQAAAwAAAAAAAABHAQAAMiwAAAMAAAACAAAASAEAAGgGAAADAAAAAgAAAEkBAACqCgAAAwAAAAEAAABKAQAA4RwAAAMAAAABAQAASwEAAHUiAAADAAEAAAEAAEsBAACnIwAAAwAAAAEBAABMAQAAryMAAAMAAQABAQAATAEAAG0TAAADAP//AQEAAEwBAAAgHgAAAwAAAAEAAABNAQAAAyMAAAMAAAAAAAAATgEAAKIyAAABAQAAgAAAAAAAAADKGAAAAQIBAD4BAAAAAAAADyEAAAECAQA/AQAAAAAAAAMNAAABAgEAQAEAAAAAAAAVOAAAAwAWAAEBAABPAQAABDgAAAMAFwABAQAATwEAAGk4AAADABgAAQEAAE8BAABWOAAAAwAZAAEBAABPAQAA3DgAAAMAGgABAQAATwEAAMk4AAADABsAAQEAAE8BAADwOAAAAwAcAAEBAABPAQAAhzgAAAMAHQABAQAATwEAAA04AAADABYAAgEAAFABAAD7NwAAAwAXAAIBAABQAQAAYDgAAAMAGAACAQAAUAEAAEw4AAADABkAAgEAAFABAADTOAAAAwAaAAIBAABQAQAAvzgAAAMAGwACAQAAUAEAAOU4AAADABwAAgEAAFABAAB8OAAAAwAdAAIBAABQAQAADTMAAAEDAABmCAAAAAAAAAEAAAACAAAAAQAAAAQAAAABAAAAAQAAAAgAAAAQAAAAAQAAACAAAAAAAAAAAgAAAAAAAAABAAAAAQAAAAEAAAAaOwAAYD8AABQ7AABRAQAAUgEAAFEBAABTAQAAVAEAAFUBAABWAQAAVwEAAFgBAABZAQAAWgEAAFkBAABbAQAAXAEAAF0BAABeAQAAXwEAAGABAAAfDwcDAQAAAAAAAACAAAAAAAgAAAAAAQAAACAAAAAABAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAABAAAAAQAAAAEAAAABAAAAAUAAAAFAAAAAAAAAAoACQAOACAAIQCgAKEAgBaBFgAgCyAoICogLyAwIF8gYCAAMAEw//4A/wBBxOABCy0QAAAA/v//h/7//wcAAAAAEAD/A/7//4f+//8HbHAAABBwAACAcAAAAQAwADoAQYDhAQsRBAAwADoAQQBbAF8AYABhAHsAQaDhAQviDgEDBQEBAQEFBQUBAgIDBQUBAQECAgMDBQUBBQERAAAAMJogAACaMABzgVoAMBdgADAHbACzgW8AABdwAAAHfAAAgX8AQDCAAMMBmACQgZgAQAaZAECQnAC0gaQAQC6lADABvABAhrwAcIG/AAABwAAwgcAAQATBADABwwBAgsMAMILEAECCxQAwAccAMIHHADAByABAgsgAMIHJADABygAAgcoAMAHLADCBywBAAswAAAHNADABzgAwgc4AAAHPADCBzwBABtAAMAHTAECC0wAwgdQAQALWADAB1wBAgtcAMILYAECE2QAwgdsAQALcAEAC3gAAgd8AUAPiAFCD4wBQA+UAQJDmAACB7gBAEu8AtAH4AFCD+ABAAvoAMAH7ADCB+wBAKPwAMAEQAUASEQExAR0BQIIdATCBHgExAR8BAYIfAUCCIAEwgSEBMAEiATCBIgFACiMBAQEoAQGBKAEBASkBAIEpAQABKgEAAisBAIEsAQCBLQEBAS4BAAEwAQGBMAEAgTEBAYEyAQEBMwEAATQBAIE0AQEBNQEBgTUBAQE2AQCBNwEBgTgBAAE5AQCBOgEBgT4BAAFAAQEBQQEAgUEBAYFDAQABRAEAgUQBAAJFAQABRgEAAUkBAYFOAQEBTwFzgaIBQAS4AUACuwEAg70BMIG/ATABwwEwA8QBMAHGATACxwHQAcgBMJHIATCJ0QEAAdYBAIPWAdMB2AEAkdgBcwHhAQCJ4QEAAeYBAILmATCB5wFzAegBc4HoAXOB6gFzAesBAIHrAUAY7AFzAfgBc4H4AQAB+QEAgfkBoAH6AXOB+gFAgvsBMIH8AUAC/QEwg/4BMBAAAjAgCAIAIBgCABAoAkAiMAJANkUCMAFgAkCOYAIAgWcCQGBoAjCmmAIAprACtYHDAjEmUAgxgWMIMYFmCAAraAgAg34IEVDQCRAG+AkgBvwJdAFADnSBQA50AUEOdIFBDnQBQg50gUIOdAFDDoCBQw6AAUQOMCtIDjCDXg4BgbwOAYG+DgEBxw5AfgAPQBg/D7UBSw+2gUsPtgFMD7aBTA+3AU0PgIFNDzABTw9AYFAPAAiADzAIhA8ABogPMAaMDwAIkA8wCJQPAAiYDzAInA8ABqAPMAakD7ABqA8AgagP0wGpDwCBqQ/TAaoPAIGqD9MBqw8AgasPMIGsDzCBrQ8wga4PMIGvDwAIsA8wCLQPAAK4DwAEuQ8AArsPAQK8DwECvQ8BAr4PtwjAD2cIxA+4CMgPaAjMD7gI0A9oCNQPAALYD7kB2Q+xgdkPuQHaD7EB2w/XgdsPMALcDzAC3Q9hAd4PcwHfD7kB4Q+ygeEPugHiD7IB4w/YgeMPMATkD2IB5g8AAugP0AHpD9CB6Q+wAesP0IHrDzAC7A8wAu0PAQLwD9MB8Q/TgfEPugHyDwGB8g+wAfMP04HzDzAC9A8wAvUPMQH2D7oB+Q+ygfkPuwH6D7IB+w/ZgfsPMAL8DzAC/Q9iAf4PoAGTEKABlRCggZUQMQGZEAEBpxAxELAQARC4EECCwRAxGlsSARpoEjEwABYBMBgWQAIwFjABMRYwgTEWMAEyFgCBMhYAATMWQIYzFjCBNhYwATcWMIE3FjABOBZAAjkWQII6FjACPxZAZEAWQIR1FkACeRYAJoAWAIGTFgCBlhZALiBTQBxAU0AOkVNAPplTQIS8UzCBvlNACr9TQILFUzCBxlNABMhTAQHKU0AUy1MwAdVTMIHVUzAB1lMwgdZTMAHXUzAB2FMwgdhTMAHZUzGB2VNAENpTMQHiUzCB4lMwAeNTQITjU0AC6FNABOtTQIL6UwGBqVUgULhVsgGAfbKBgH2yAYF92oGBfdoBgn2zgYJ9swGDfbuBiX27AYp9u4GKfbwBi327gYt9MZqQfwGaoH8xKACCASgUgjEkWIIBJGyCMQu4gjEPvoIxB8aCMQLKggGLy4IBj9GCAYfZggGC3YIxM0CGATNghjEgUIwBIGCMMSAgtwEgMLcxIoD0ASKR9AAAAAAAAAAAAQCcBgdNAwQQAI8LAAARAAgAU0pRAFIAUwA6VFUAV1k/XVwARmFjQmQAZgBoAGoAbABuAABAAAAAABoAkwAAIDUAJwAhACQiKgATa20AJiQnFBYYGxw+Hj8fOT0iIUEeQCUlJiggKkgsQy5LMEwyREKZAACVj31+g4QSgIJ2dxJ7o3x4eYqSmKaghQCaoZN1M5UAjgB0mZiXlgAAngCcAKGgFS4vMLS1T6qpEhQeISIiKjQ1pqc2H0kAAJcBWtodNgUAxMPGxcjHysnMy8TVRdZC10bYztDS1NrZ7vb+DgcPgJ8AIYCj7QDAQMZg59vmmcAAAAZg3Cn9FRIGFvjdBhUShAjGFv/fA8BAAEZg3uBtNzg5FRQXFgAaGRwbAF+3ZURHAE9iTlAAAEgAAACjpKUAAAAAALYAAFoARwBbVlhgXnBpb04AO2e4AAAAAEWoiouMq6xYWK+UsG+yXVxfXmFgZmdoaWJjZGVram1sb25xcABBkPABC3OZAwgDAQOlAxMDAANCA5EDlwOpA0YASQBMAFMAaQAHA7wCTgBKAAwDNQVSBUgAMQNUAFcACgNZAEEAvgIIH4AfKB+QH2gfoB+6H4YDsx/KH4kDwx+hA/ofjwPzH0QFRgU7BU4FPQW4A2IESqZgHskDawDlAEGQ8QEL0gFAqYCOgPyA04CMgI2BjQKA4YCRhZoBAAERAAEECAEIMAgBFSAAOZkxnYRAlIDWgqaAQWKApoBXdvgCgI+AsEDbCIBB0ICMgI+M5AMBiQAUKBARAgEYCyRLJgEBhuWAYHm2gUCRgb2IlAWAmICiAIChgkM0ogaAjGBcFgEQqYCIYMxE1IDGAQgJC4CLAAaAwAMPBoCbAwQAFoBBU4GYgJiAnoCYgJ6AmICegJiAnoCYB0cziYCTUhCZhZmFmQAAAAC5AuCgHkCepkBV1GH71iGK8QEAQfDyAQuVBqYFgIqAogCAxgMAAwGBQfZAvxkYiAiAQPqGQM4EgLCsAAEBAKuAioWJigCigImUj4DkOIkDoACAnZrairmKGAiXl6qCqwYNh6i5tgADOwKGiYGMgI6AuQMfgJOBmQGBuAMLCRKAnQqAioG4AyALgJOBlSiAuQEAHwaBioGdgLyAi4CxAoC2ABQQHoGKgZyAuQEFBIGTgZuBuAsfgJOBnIDHBhCA2QGGiojhAYiIAIXJgZoAAIC2jQQBhIqAo4iA5RgoCYGYC4KPg4wBDYCOgN2AQl+CQ7GCnIGdgZ2Bvwg3AYoQIKyEsoDAgaGA9ROBiAWCQNoJgLkAMAABPYkIpgeesIOvACAEgKeIi4GfGQiCtwAKAIK5OYG/hdEQjAYYKBGxvoyAoeRBvACCioKMgoyCjIGLJ4GJAQGEsCCJAIyAj4yyoEuKgfCC/ICOgN+froBB1ICjGiSA3IXcgmBvFYBE4YVBDYDhGIkAm4PPgY2hzYCWguYSDwIDgJgMgECWgZmRjIClh5iKrYKvARmBkICUgcEpCYGLB4CigIqAsgARDAiAmoCNDAiA44SIgvgBA4BgTy+AQJKQQjyPEIuPoQGAQKgGBYCKgKIAgK6ArIHCgJSCQgCAQOGAQJSERAQoqYCIQkUQDIOnE4BApIFCPINBgoFAmIqwg/qAtY6oAYGJgrAZCQOAiYCxgqMgh72Ai4GziIkZgN4RAA2AQJ8Ch5SBuAqApDKEQMI5EICWgNMoAwiBQO0dCIGagdQ5AIHpAAEogOQRGIRBAogBQP8IA4BAjxkLgJ+JpykfgIgpgq2MAUGVMCiA0ZUOAQH5KgAIMIDHCgCAQVqBVTqIYDa2hLqGiINECoC+kL8IgWBAChgwgUydCINSW62BlkIfgoiPDp2DQJOCR7q2g7E4jYCVII5FTzCQDgEEQQSNQW+AvINF34bsh0quhGwMAICd3/9A774FAP4HAFIKoMELAIINAD8QgNQXQM8aIPUcAIAgABagAMaoAMKqYFb+ILEHAYIQIQITIbgWYZcaATdrIYzRAdfoQfABDgBBkPkBC7cIwJmFma6AiQMEloCegEHJg4uNJgCAQIAgCRgFABAAk4DSgECKh0ClgKUIhajGmhusqqII4gCODoGJEYCPAJ2c2IqAl6CICwSVGIgCgJaYhoqElwWQqbm1EJEGiY6PHwmBlQYAExCPgIwIgo2BiQcrCZUGAQEBnhiAkoKPiAKAlQYBBBCRgI6BloCKOQmVBgEEEJ0Igo6AkAAqEBoIAAoKEouVgLM4EJaAjxCZEQGBnQM4EJaAiQQQngiBjoGQiAKAqAiPBBeClyyRgpeAiAAOua8Bi4a5CAAglwCAiQGIASCAlIOfgL44o5qE8qqTgI8rGgIOE4yLgJClACCBqoBBTAMOAAOBqAOBoAMOAAOBjoC4A4HCpI+P1Q2CQmuBkICZhMqCioaRjJKNkY2MAo6zogOAwtiGqACExYmesJ0MiquDmbWWiLTRgNyukIe1nYyBiauZo6iCiaOBiIaqCqgYKAoEQL+/QRUNgaUNDwAAAICegbQGABIGEw2DjCIG84CMgI+M5AMBiQANKAAAgI8LJBiQqEp2QOQrEYulACCBtzCPlogwMDAwMDAwhkIlgpiINAyD1RyA2QOEqoDdkJ+vj0H/Wb+/YFaMwq2BQQyCj4mBk66PnoHPpoiB5oG/IQAEl48CA4CWnLONsb0qAIGKm4mWmJyGrpuAjyCJiSColhCHk5YQgrEAEQwIAJcRijKLKSmFiDAwqoCNhfKcYCuji5aDsGAhA0FtgemlhoskAImAjAQAAQGA66BBapG/gbWni/MgQIajmYWZitgVDQ0KoouAmYCSAYCOgY2h+sS0QQqcgrCun4ydhKWJnYGjHwSpQJ2Ro4Ojg6eHs4uKgI4GAYCKgI4GAcJBNoiViYeXKKmAiMQpAKsBEIGWiZaInsCSAYmViZnFtym/gI4YEJypnIKcojibmrWJlYmSjJHtyLayjLKMo0FbqSnNnIkHlamRrZSalou0uAmAjKyfmJmjnAEHohCLr42DlACAopGAmNMwABiOgImGrqU5CZUGAQQQkYCLhECdtJGDk4Kdr5MIgEC3rqiDo6+TgLqqjIDGmqSGQLir87+eOQE4CJeOAIDdOaaPAICbgImnMJSAiq2SgJHIQQaIgKSQgLCd7zAIpZSAmCgIn42AQUaSQLyAzkOZ5e6QQMNKS+CORC5P0EJGYCG4QjiGnpDOkJ2Rr4+DnpSEkkKvv//KIMGMvwiAm1f3h0TVqYhgIuYYMAhBIqyCkB9Bi0kD6oSMgoiGiVdl1IDGAQgJC4CLAAaAwAMPBoCbAwQAFoBBU4GYgJiAnoCYgJ6AmICegJiAnoCYB0cznkHgrImGj4BBQJ2Rq0TzMBgIjoBAxLrDMESzGJoBAAiAiQMAACgYAAACAQAIAAAAAAEACwYDAwCAiYCQIgSAkFFDYKbfn1A4hkDdgVaBjV0wTB5CHUXhU0oAQdCBAgtm9gMgpgcAqQkgsQoAugsgOw0gxw4gSRIAmxYArBkAwB2AgCAgcC0AADIA2qcATKogx9cg/P0gnQIhlgUB8wgBswwhcxFhPhMBRxchnhoBmiMBeGsB/LJhOtUBLeFBM+4B4KZiSxMDAEHAggIL8iyviaSA1oBCR++WgED6hEEIrAABAQDHiq+eKOQxKQgZiZaAnZraio6JoIiIgJcYiAIEqoK7h6mXgKC1EJEGiQmJkIK3ADEJgoiAiQmJjQGCtwAjCRKAk4sQioK3ADgQgpMJiYkogrcAMQkWgokJiZGAuiIQg4iAjYmPhLYAMBAegYoJiZCCtwAwEB6BigmJj4O2CDAQg4iAiQmJkILFAygAPYkJvAGGiziJ1gGIiimJvQ2JigAAA4GwkwGEioCjiIDjk4CJixsQETKDjIuAjkK+goiIQ5+Dm4KcgZ2Bv5+IAYmgEIpAjoD1i4OLiYn/iruEuImAnIGKhYmVjYCPsISukIqJkIiLgp2MgYmrja+Th4mFifUQlBgoCkDFv0I+gZKA+owYgotL/YJAjIDfn0IpheiBYHWEicQDiZ+Bz4FBDwIDgJYjgNKBsZGJiYWRjIqbh5iMq4OujY6JioCJia6NiwcJiaCCsQARDAiAqCSBQOs4CYlgTyOAQuCPj48Rl4JAv4mkgEK8gEDhgECUhEEkiUVWEAyDpxOAQKSBQjwfiUFwgUCYirCD+YK0jp6KCYmDrIowrIkqo42AiSGrgIuCr407gIvRiyhAn4uEiSu2CDEJgoiAiQkyhEC/kYiJGNCTi4lA1DGImoHRkI6J0IyHidKOg4lA8Y5ApInFKAkYAIGLifYxMoCbiacwH4CIiq2PQZQ4h4+Jt5WAjfkqAAgwB4mvIAgniUFIg2BLaInViaWEuoaYiUP0ALYz0ICKgWBMqoFSYK2BlkIdIi85hp2DQJOCRYixQf+2g7E4jYCVII5FTzCQDgEEQQSGiIlBY4C8jUXVhuw0iVKViWwFBUDv+gYAcAkA8ApAVwwA8A1Axw8A6hcgRRsgVSAgDKhgN6oAUP4AOg0BgxEBxBQhRBkhWh1Bn7xhsNoh8AEOAAAAAEFkbGFtLEFkbG0AQWhvbSxBaG9tAEFuYXRvbGlhbl9IaWVyb2dseXBocyxIbHV3AEFyYWJpYyxBcmFiAEFybWVuaWFuLEFybW4AQXZlc3RhbixBdnN0AEJhbGluZXNlLEJhbGkAQmFtdW0sQmFtdQBCYXNzYV9WYWgsQmFzcwBCYXRhayxCYXRrAEJlbmdhbGksQmVuZwBCaGFpa3N1a2ksQmhrcwBCb3BvbW9mbyxCb3BvAEJyYWhtaSxCcmFoAEJyYWlsbGUsQnJhaQBCdWdpbmVzZSxCdWdpAEJ1aGlkLEJ1aGQAQ2FuYWRpYW5fQWJvcmlnaW5hbCxDYW5zAENhcmlhbixDYXJpAENhdWNhc2lhbl9BbGJhbmlhbixBZ2hiAENoYWttYSxDYWttAENoYW0sQ2hhbQBDaGVyb2tlZSxDaGVyAENob3Jhc21pYW4sQ2hycwBDb21tb24sWnl5eQBDb3B0aWMsQ29wdCxRYWFjAEN1bmVpZm9ybSxYc3V4AEN5cHJpb3QsQ3BydABDeXJpbGxpYyxDeXJsAEN5cHJvX01pbm9hbixDcG1uAERlc2VyZXQsRHNydABEZXZhbmFnYXJpLERldmEARGl2ZXNfQWt1cnUsRGlhawBEb2dyYSxEb2dyAER1cGxveWFuLER1cGwARWd5cHRpYW5fSGllcm9nbHlwaHMsRWd5cABFbGJhc2FuLEVsYmEARWx5bWFpYyxFbHltAEV0aGlvcGljLEV0aGkAR2VvcmdpYW4sR2VvcgBHbGFnb2xpdGljLEdsYWcAR290aGljLEdvdGgAR3JhbnRoYSxHcmFuAEdyZWVrLEdyZWsAR3VqYXJhdGksR3VqcgBHdW5qYWxhX0dvbmRpLEdvbmcAR3VybXVraGksR3VydQBIYW4sSGFuaQBIYW5ndWwsSGFuZwBIYW5pZmlfUm9oaW5neWEsUm9oZwBIYW51bm9vLEhhbm8ASGF0cmFuLEhhdHIASGVicmV3LEhlYnIASGlyYWdhbmEsSGlyYQBJbXBlcmlhbF9BcmFtYWljLEFybWkASW5oZXJpdGVkLFppbmgsUWFhaQBJbnNjcmlwdGlvbmFsX1BhaGxhdmksUGhsaQBJbnNjcmlwdGlvbmFsX1BhcnRoaWFuLFBydGkASmF2YW5lc2UsSmF2YQBLYWl0aGksS3RoaQBLYW5uYWRhLEtuZGEAS2F0YWthbmEsS2FuYQBLYXlhaF9MaSxLYWxpAEtoYXJvc2h0aGksS2hhcgBLaG1lcixLaG1yAEtob2praSxLaG9qAEtoaXRhbl9TbWFsbF9TY3JpcHQsS2l0cwBLaHVkYXdhZGksU2luZABMYW8sTGFvbwBMYXRpbixMYXRuAExlcGNoYSxMZXBjAExpbWJ1LExpbWIATGluZWFyX0EsTGluYQBMaW5lYXJfQixMaW5iAExpc3UsTGlzdQBMeWNpYW4sTHljaQBMeWRpYW4sTHlkaQBNYWthc2FyLE1ha2EATWFoYWphbmksTWFoagBNYWxheWFsYW0sTWx5bQBNYW5kYWljLE1hbmQATWFuaWNoYWVhbixNYW5pAE1hcmNoZW4sTWFyYwBNYXNhcmFtX0dvbmRpLEdvbm0ATWVkZWZhaWRyaW4sTWVkZgBNZWV0ZWlfTWF5ZWssTXRlaQBNZW5kZV9LaWtha3VpLE1lbmQATWVyb2l0aWNfQ3Vyc2l2ZSxNZXJjAE1lcm9pdGljX0hpZXJvZ2x5cGhzLE1lcm8ATWlhbyxQbHJkAE1vZGksTW9kaQBNb25nb2xpYW4sTW9uZwBNcm8sTXJvbwBNdWx0YW5pLE11bHQATXlhbm1hcixNeW1yAE5hYmF0YWVhbixOYmF0AE5hbmRpbmFnYXJpLE5hbmQATmV3X1RhaV9MdWUsVGFsdQBOZXdhLE5ld2EATmtvLE5rb28ATnVzaHUsTnNodQBOeWlha2VuZ19QdWFjaHVlX0htb25nLEhtbnAAT2doYW0sT2dhbQBPbF9DaGlraSxPbGNrAE9sZF9IdW5nYXJpYW4sSHVuZwBPbGRfSXRhbGljLEl0YWwAT2xkX05vcnRoX0FyYWJpYW4sTmFyYgBPbGRfUGVybWljLFBlcm0AT2xkX1BlcnNpYW4sWHBlbwBPbGRfU29nZGlhbixTb2dvAE9sZF9Tb3V0aF9BcmFiaWFuLFNhcmIAT2xkX1R1cmtpYyxPcmtoAE9sZF9VeWdodXIsT3VncgBPcml5YSxPcnlhAE9zYWdlLE9zZ2UAT3NtYW55YSxPc21hAFBhaGF3aF9IbW9uZyxIbW5nAFBhbG15cmVuZSxQYWxtAFBhdV9DaW5fSGF1LFBhdWMAUGhhZ3NfUGEsUGhhZwBQaG9lbmljaWFuLFBobngAUHNhbHRlcl9QYWhsYXZpLFBobHAAUmVqYW5nLFJqbmcAUnVuaWMsUnVucgBTYW1hcml0YW4sU2FtcgBTYXVyYXNodHJhLFNhdXIAU2hhcmFkYSxTaHJkAFNoYXZpYW4sU2hhdwBTaWRkaGFtLFNpZGQAU2lnbldyaXRpbmcsU2dudwBTaW5oYWxhLFNpbmgAU29nZGlhbixTb2dkAFNvcmFfU29tcGVuZyxTb3JhAFNveW9tYm8sU295bwBTdW5kYW5lc2UsU3VuZABTeWxvdGlfTmFncmksU3lsbwBTeXJpYWMsU3lyYwBUYWdhbG9nLFRnbGcAVGFnYmFud2EsVGFnYgBUYWlfTGUsVGFsZQBUYWlfVGhhbSxMYW5hAFRhaV9WaWV0LFRhdnQAVGFrcmksVGFrcgBUYW1pbCxUYW1sAFRhbmd1dCxUYW5nAFRlbHVndSxUZWx1AFRoYWFuYSxUaGFhAFRoYWksVGhhaQBUaWJldGFuLFRpYnQAVGlmaW5hZ2gsVGZuZwBUaXJodXRhLFRpcmgAVGFuZ3NhLFRuc2EAVG90byxUb3RvAFVnYXJpdGljLFVnYXIAVmFpLFZhaWkAVml0aGt1cWksVml0aABXYW5jaG8sV2NobwBXYXJhbmdfQ2l0aSxXYXJhAFllemlkaSxZZXppAFlpLFlpaWkAWmFuYWJhemFyX1NxdWFyZSxaYW5iAAAAAAAAAMAZmUaFGZlGrhmARo4ZgEaEGZZGgBmeRoAZ4WBGphmERoQZgQ2TGeAPOIMsgBmCLAGDLIAZgCwDgCyAGYAsgBmCLACALACTLAC+LI0ajyzgJB2BOOBIHQClBQGxBQGCBQC2NQeaNQOFNQqEBIAZhQSAGY0EgBmCBIAZnwSAGYkEijiZBIA44AsEgBmhBI2JALuJAYKJrwSxkw26ZAGCZK19AY59AJtRAYBRAIqJBJ4EAIEEBckEgBmcBNAggziOIIEZmSCDCwCHCwGBCwGVCwCGCwCACwKDCwGICwGBCwGDCweACwOBCwCECwGYCwGCLwCFLwOBLwGVLwCGLwCBLwCBLwCBLwGALwCELwOBLwGCLwKALwaDLwCALwaQLwmCLQCILQCCLQCVLQCGLQCBLQCELQGJLQCCLQCCLQGALQ6DLQGLLQaGLQCCcgCHcgGBcgGVcgCGcgCBcgCEcgGIcgGBcgGCcgaCcgOBcgCEcgGRcgmBkACFkAKCkACDkAKBkACAkACBkAKBkAKCkAKLkAOEkAKCkACDkAGAkAWAkA2UkASMkgCCkgCWkgCPkgGIkgCCkgCDkgaBkgCCkgGAkgGDkgGJkgaIkow9AII9AJY9AIk9AIQ9AYg9AII9AIM9BoE9BYE9AIM9AYk9AIE9DIxQAIJQALJQAIJQAIVQA49QAZlQAIKDAJGDApeDAIiDAICDAYaDAoCDA4WDAICDAIeDBYmDAYKDC7mUA4AZm5QkgUUAgEUAhEUAl0UAgEUAlkUBhEUAgEUAhUUBiUUBg0Ufx5UAo5UDppUAo5UAjpUAhpWDGYGVJOA/X6UoAIAoBIAoAaoogBmDKOCfMcgnAIMnAYYnAIAnAIMnAagnAIMnAaAnAIMnAYYnAIAnAIMnAY4nALgnAIMnAcInAZ8nApknBdUXAYUXAeIfEpxnAsp8ghmKfAaVigiAipQzgRkIkxELjIsAgosAgYsL3UEBiUEFiUEFgVyBGYBcgBmTXAXYXAaqXATFEgmeSACLSAOLSAOASAKLSJ2MAYSMCqtiA5liBYpiAoFin0GbEAGBEL6NAJyNAYqNBYmNBY2NAZ44MMwHAq4HAL+HswoHgwq3RwKORwKCR69oiB0GqigBgiiHhweCOIAZjDiAGYY4gxmAOIUZgDiCGYE4gBkEpUaELIAdsEaELINGhCyMRoAdxUaALL844J9GlSwBhSwBpSwBhSwBhywAgCwAgCwAgCwAniwBtCwAjiwAjSwBhSwAkiwBgiwAiCwAixmBONYZAIoZgEYBihmARo4ZAIxGAqAZDqA4DqUZgCyCGYFGhRmARpoZgEaQGahGghkD4jYZGIoZFOM/GeCfD+ITGQGfGQDgCBnfKZ9G4BMaBIYapSgAgCgEgCgBt5YGgZYNgJaWJwiGJwCGJwCGJwCGJwCGJwCGJwCGJwCGJwCfHd0ZIZkwANgwC+B1MBmLGQOEGYAwgBmAMJgZiDCDOIExhxmDMIMZANU2AYE4gRmCNoAZ2T6BGYI+BKoNAN0xAI8Znw2jGQuPPp4xAL8ZnjHQGa4+gBnXPuBHGfAJXzC/GfBBnzDkLKACtqAIr0vgy5sT3x3XCAehGeAFRoIZv0YEgUYAgEYAhEYXjUasiAKJGQW3eAfFfgeLfgWfIK0/gBmAP6N7CoB7nDECzTsAgBmJOwOBO55fALYWCI0WAYkWAYMWn1/CjheEjpZWCYUnAYUnAYUnCIYnAIYnAKpGgBmIRoAsg0aBGQPPF61WAYlWBfAbQzELljEDsDFwEKPhDTAB4AkwJYZGC4QFBJk1AIQ1AIA1AIE1AIE1AIk14BIED+EKBIEZzwQBtQQGgAQfjwSPOIkZBY04gR2iGQCSGQCDGQOEBADgJgQBgBkAnxmZRoUZmUaKGYk+gBmsPoEZnjEChTEBhTEBhTEBgjEChhkAhhkJhBkBi0oAmUoAkkoAgUoAjkoBjUoh4BpKBIIZA6wZAogZziwAjBkCgCwurBmAOGAhnEwCsBMOgDiaGQOjagiCapoqBKpsBJ2aAICao20DjW0pzx+vgJ10AYl0BaNzA6NzA6clB7MUCoAUipwAjpwAhpwAgZwAipwAjpwAhpwAgZxC4NZJCJVJCYdJF4VGAKlGAIhGRIUcAYAcAKscAIEcAoAcAYAclTcAiDefdp5gB4hgL5I0AIE0BIQ0m3kCgHmZTQSATT+fWZdYA5NYAa1Yg0AAgUAEh0AAgkAAnEABgkADiUAGiEAGn2+fax+mUgOLUgi1BgKGBpU6AYc6kjkEhzmRegaDeguGek/IcDayaQyyaQaFaacyB4kyYMWeBACpnwCCnwGBn02nbgephBWZcSWbGBOWJgjNDgOjDgiADsI8CYA8AZiFBomFBbQVAJEVB6ZPCN9/AJODCpFCAKtCQIZeAIBeAINeAI5eAIpeBbpEBIlEBYMrAIcrAYErAZUrAIYrAIErAIQrAIA4iCsBgSsBgisBgCsFgCsEhisBhisChCtgKttjAIRjHceXB4mXYEW1gQGlgSHEWwqJWwWMXBK5jwWJjzWaAgGOAgOWAmBYuyJgA9KeC4CehiEBgCEBhyEAgSEAnSEAgSEBiyEIiSFFh2EBrWEBimEax6EH0oYMjxK4d2CmiAwArAwAjQwJnAwCn1MBlVMAjVNIhlQAgVQAq1QCgFQAgVQAiFQHiVQFhS4AgS4ApC4AgS4AhS4GiS5g1ZhOYFaASw6xkAyAkOM5G2AF4A4bAIQbCuBjG2nr4AIeDOPOJACIJG9m4eYDcBFY4dgIBp5dAIldA4FdzpgAiZgFnQkBhQkJxXUJiXUAhnUAlHUEknViT9pVYATKWgO4WgaQWj+AkYBlgTCAQwqBMA3wB5eRB+KfkeF1QymIkXAShoM+AIY+AIE+AIA+4L42gj4sgjYQgz4H4StlaKPgCiMEjCMCiCMGiSMBgyODGXAB+604AZY4COATGTvglRkJphkBvRmCOJAZhziBGYY4nRmDOLwZFMUsYDmTGQvWGQiYGWAm1BkAxhkAgRkBgBkBgRkBgxkAixkAgBkAhhkAwBkAgxkBhxkAhhkAmxkAgxkAhBkAgBkChhkA4PMZAeDDGQGxGeIrgg6EggCOgmPvnkZggIYpAJApAYYpAIEpAIQpYHSsZgKNZgGJZgOBZmDfnpkQuZ0EgJ1kf4YnAIMnAIEnAI4nAOBkVwGPVyjLAQOJAQOBAWKwwxlLvBlgYYMEAJoEAIEEAIAEAYAEAIkEAIMEAIAEAIAEBYAEA4AEAIAEAIAEAIIEAIEEAIAEAYAEAIAEAIAEAIAEAIAEAIEEAIAEAYMEAIYEAIMEAIMEAIAEAIkEAJAEBIIEAIQEAJAEM4EEYK2rGQPgAxkLjhkBjhkAjhkApBkJ4E0ZN5kZgDaBGQyrGQOIGQaBGQ2FGWA543cZBI8ZAowZAuATGQvYGQaLGQOAGQ6LGQO3GQeJGQWnGQedGQGBGU3g8xkLjRkBhBkChBkChhkInBkCihkEhRkJiRkFhxkHhhkI4DIZALYZJIkZY6Xwln8wH+/YMAbgfTAB8AYhMA3wDNAwa77hvTBlgfAC6jB63FWAGR3fGWAf4I84AEHArwIL0guCwQAAASwBAAABLBwADAFGgJIAAAIdbAACHSkBAh1GAAIdKYEDAAAGBGQyiZOfDQAABgRkMomTnwADBImTAQAABwEEZDKJk58fAAAJAQRRUnF6MoSJCQAKAgSJCQAJAwSTnwUAAAIEiWIAAAIEMoH7AAANCyArLS89RlByf5CSlwAMCyArLS89RlBykJKXEAAAFAsgIi5UKy0vPU9QYXJEg4iPkJKXABULICIuVCstLz1IT1BhckSDiI+QkpcJBCAiPE91AAkDCxWIdQAJAi9edQAJAi1CgHUADQIrkIBxAAkCPWGCzwAJAxVfjIAwAAACKEaFuAABBBEzi4qASgABAlx4AAAAAlx4hEkAAAQLICs9AAEgAAQLICs9AAIgKwABIAECCyAAAiB/AAILIAACIH8ABiA9UHKQkgABIAECIH8BASAAAiB/AAILIAYBIAACIGEAAgsgAQEgAAILIAMBIAAICyArPWFykpcAAiArAAMgKz0BAgsgAAELAQIgKwABYYBEAAEBLDUAAAIdiQAAAAGJgbMAAAJGXIA/AAADICtGjNEAAAIdKYE8AAEGDTEwNj6gAAUNMTA2PgEAAAEwAAAJBg0xMDY+oAAAAAUNMTA2PgcGDTEwNj6gAwUNMTA2PgkAAwINMAEAAAUNMTA2PgQCNj4AAAAFDTEwNj4DAAEDMDY+AQEwWAADAjY+AgAAAjY+WQAABg0xMDY+oAACNj6AEgAPATAfACMBMDsAJwEwNwAwATAOAAsBMDIAAAEwVwAYATAJAAQBMF8AHgEwwDHvAAACHSmADwAHAjBGgKcAAg4gIi0vQj08T1BbYUSPlwINICItL0I9PE9bYUSPlwMLICItL0I8T1tEj5eANgAAAgsgAAAAAiCQOQAAAz9GX4AfAAACEDvAEu0AAQIEZIAxAAACBJMJAAACBJNGAAEFDTEwNj6AmQAEBg0xMDY+oAkAAAI2PiwAAQI2PoDfAAEDHhxKAAIcSgMALAMcSUoCAAgCHEqBHwAbAgQah3UAAAJScYeNAAACK5AAAAACK5A2AAECK5CMEgABAiuQAAAAAiuQwFxLAAMBI5Y7ABEBMJ5dAAEBMM7NLQAAAAAAQ24sVW5hc3NpZ25lZABMdSxVcHBlcmNhc2VfTGV0dGVyAExsLExvd2VyY2FzZV9MZXR0ZXIATHQsVGl0bGVjYXNlX0xldHRlcgBMbSxNb2RpZmllcl9MZXR0ZXIATG8sT3RoZXJfTGV0dGVyAE1uLE5vbnNwYWNpbmdfTWFyawBNYyxTcGFjaW5nX01hcmsATWUsRW5jbG9zaW5nX01hcmsATmQsRGVjaW1hbF9OdW1iZXIsZGlnaXQATmwsTGV0dGVyX051bWJlcgBObyxPdGhlcl9OdW1iZXIAU20sTWF0aF9TeW1ib2wAU2MsQ3VycmVuY3lfU3ltYm9sAFNrLE1vZGlmaWVyX1N5bWJvbABTbyxPdGhlcl9TeW1ib2wAUGMsQ29ubmVjdG9yX1B1bmN0dWF0aW9uAFBkLERhc2hfUHVuY3R1YXRpb24AUHMsT3Blbl9QdW5jdHVhdGlvbgBQZSxDbG9zZV9QdW5jdHVhdGlvbgBQaSxJbml0aWFsX1B1bmN0dWF0aW9uAFBmLEZpbmFsX1B1bmN0dWF0aW9uAFBvLE90aGVyX1B1bmN0dWF0aW9uAFpzLFNwYWNlX1NlcGFyYXRvcgBabCxMaW5lX1NlcGFyYXRvcgBacCxQYXJhZ3JhcGhfU2VwYXJhdG9yAENjLENvbnRyb2wsY250cmwAQ2YsRm9ybWF0AENzLFN1cnJvZ2F0ZQBDbyxQcml2YXRlX1VzZQBMQyxDYXNlZF9MZXR0ZXIATCxMZXR0ZXIATSxNYXJrLENvbWJpbmluZ19NYXJrAE4sTnVtYmVyAFMsU3ltYm9sAFAsUHVuY3R1YXRpb24scHVuY3QAWixTZXBhcmF0b3IAQyxPdGhlcgBBoLsCC7AIDgAAAD4AAADAAQAAAA4AAADwAAAAAH8AAACAAwEAADxBU0NJSV9IZXhfRGlnaXQsQUhleABCaWRpX0NvbnRyb2wsQmlkaV9DAERhc2gARGVwcmVjYXRlZCxEZXAARGlhY3JpdGljLERpYQBFeHRlbmRlcixFeHQASGV4X0RpZ2l0LEhleABJRFNfQmluYXJ5X09wZXJhdG9yLElEU0IASURTX1RyaW5hcnlfT3BlcmF0b3IsSURTVABJZGVvZ3JhcGhpYyxJZGVvAEpvaW5fQ29udHJvbCxKb2luX0MATG9naWNhbF9PcmRlcl9FeGNlcHRpb24sTE9FAE5vbmNoYXJhY3Rlcl9Db2RlX1BvaW50LE5DaGFyAFBhdHRlcm5fU3ludGF4LFBhdF9TeW4AUGF0dGVybl9XaGl0ZV9TcGFjZSxQYXRfV1MAUXVvdGF0aW9uX01hcmssUU1hcmsAUmFkaWNhbABSZWdpb25hbF9JbmRpY2F0b3IsUkkAU2VudGVuY2VfVGVybWluYWwsU1Rlcm0AU29mdF9Eb3R0ZWQsU0QAVGVybWluYWxfUHVuY3R1YXRpb24sVGVybQBVbmlmaWVkX0lkZW9ncmFwaCxVSWRlbwBWYXJpYXRpb25fU2VsZWN0b3IsVlMAV2hpdGVfU3BhY2Usc3BhY2UAQmlkaV9NaXJyb3JlZCxCaWRpX00ARW1vamkARW1vamlfQ29tcG9uZW50LEVDb21wAEVtb2ppX01vZGlmaWVyLEVNb2QARW1vamlfTW9kaWZpZXJfQmFzZSxFQmFzZQBFbW9qaV9QcmVzZW50YXRpb24sRVByZXMARXh0ZW5kZWRfUGljdG9ncmFwaGljLEV4dFBpY3QARGVmYXVsdF9JZ25vcmFibGVfQ29kZV9Qb2ludCxESQBJRF9TdGFydCxJRFMAQ2FzZV9JZ25vcmFibGUsQ0kAQVNDSUkAQWxwaGFiZXRpYyxBbHBoYQBBbnkAQXNzaWduZWQAQ2FzZWQAQ2hhbmdlc19XaGVuX0Nhc2Vmb2xkZWQsQ1dDRgBDaGFuZ2VzX1doZW5fQ2FzZW1hcHBlZCxDV0NNAENoYW5nZXNfV2hlbl9Mb3dlcmNhc2VkLENXTABDaGFuZ2VzX1doZW5fTkZLQ19DYXNlZm9sZGVkLENXS0NGAENoYW5nZXNfV2hlbl9UaXRsZWNhc2VkLENXVABDaGFuZ2VzX1doZW5fVXBwZXJjYXNlZCxDV1UAR3JhcGhlbWVfQmFzZSxHcl9CYXNlAEdyYXBoZW1lX0V4dGVuZCxHcl9FeHQASURfQ29udGludWUsSURDAExvd2VyY2FzZSxMb3dlcgBNYXRoAFVwcGVyY2FzZSxVcHBlcgBYSURfQ29udGludWUsWElEQwBYSURfU3RhcnQsWElEUwBB4MMCC9QVgQAoAJcAKgCBgCoAl8ArABWBLACXAC0AgUAtAJcALgAVQS4AmQEvABYgMABCCEAAQopEAEIESgCWAEwAF4FMAEICTQBCQ04AL8FPAELDUAC/QFIAQgNTAEIJVQBCCFoAlgBeAEJDXgCBwF8AQgFoAELBawCFAXEAF8NxAERIcwBEg3cAQoN5AL4CewCXQXwAQgF9AEQEfgBCDoAAQoGHAESHiQCDBKwAFwO2AIMCuAAUAtAAlgDRAIAA3QCXgN4AgIDfAJcA4QA+QeEAgMDhAL4E4gCug+oAroLyAK0B9AAuwfQAA0H1AAMD/ACBQP4APgIAAb7AAQG+AQMBvkAGAb5ADgE+AhQBvsAVAb4BFwFEgR0BREEwAUQCNAFEgTUBRIM2AUSDOAFEhjoBRAE+AYXAYQGugogBL0KdAYQBsAGEwLQBhEBKAoRATAKEAE0CLgRWAi7BcgIgAXcChMB3AoTAjAKEgI0CrkGWAoSAlwKEANICLsHSAiAB1wKEAOUCroHyAoQAEgOEADADIsExAy6BMgOugVIDhIB2A64BdwOFwIwDhcCsAy8BtwOBAMMDhMDQA4RA0wOEgNQDhMDVA4QA1wOEQNoDhMDcAy5B3QOFwN0DhADeA4VA3gOEQOADhMDkA4RA5wOEgOgDhMDpA4QA6wOEQO4DhIAJBIEAPwSEhMEGhIDEBoTBzgYgAdAGhMDQBoMDSwcfxEwHgxdPB4EAXgeD0mYHRB2AB0KJjgdEGJMHQg2fBxaCpQeFgKYHvsCmB0QNqAdEoK4HIgHAB0SDwAciAcIHRIPCByIBxAdEgsQHIgHGB0SCxgc+EcgHRILQByIB0gdEgtIHIgHUB0SD1Ac+TNYHgEDcB76A3AeAwNwHvgDdB4BA3Qe+gN0HgMDdB74A3geAQN4HvoDeB4DA3ge+AN8HgEDfByAI4AcgCOQHIAjoB74F7AeAwO4HvgDvB5dA7weAgO8HF8HvBz5E8AeAQPIHvoDyB4DA8ge+A/MHgMD0B66C9QeAwPYHPkP3B4DA+AeuA/kHgMD6Bz4B+wcCgfsHvoP8B4BA/ge+gP4HgMD+B74A/weAQP8Hl4D/Bx4BAAiVhAAIgUAECJfABQiBAAkIl0AJCJmACQiBwAsIhcAMCLEADQiFgA0IscANCJcBDwiXwREIs8AVCIHAFwiVBRwIgcAeCBUCHwgfBSAIg4UiCBVEJQiXACoIGQFACIGAQAi/wEAIGUFBCIHAQQi/QEIILYVCCIFARQiXgEUIlUJGCJcASAiZQEgIl4BICIEASQiAgEkIgQBKCAKBSgiVBEsIH0JNCIFATgiZwE4IgwJPCJVCUQgZAVQIm4BUCBnGVAiXwFcIgQBYCJdAWAiZgFgIl8BYCIEAWQiXQFkImYBZCJvAWQiXAFoIgUBaCJeAWgiZwFoIlQJbCJdAXAiZgFwIl8BcCIEAXQiXQF0ImYBdCJvAXQiXAF4IgUBeCJeAXgiZwF4IFQJfCJlAYgg+gWYIvoBrCL5Bcwi+AIEIvkCCCL4Agwi+AYkIhQCLCLFAiwiFwIsIsQCMCL5AkAi+AJEIvsGRCL4BmAi+QpsIRAGdCEQBnghEAaAIRAGhCEQBogg+AqsIRAK4CCCCuggeQcoInwQYCSNFGgmXwBwJpQQdCStFHwmbwCEJoQQiCSVFJAmZwCYJJQ0nCR+NLQkfDTQJgYA6CbMAgwqZAJ0Kl0CdCpmAnQq+ALcKFQEfC4HAWwuBwKcLgcC8C60EwAutRMILrYTEC4PzxgstheALAx3jCy2I8QuBAAAMg4INDIQLEwyEQhkMIgEcDCLBHAwigR0MIkEeDCIBHwyEACUMI8EmDISAJwyFwCcMhAsrDIRCMQwiATQMIsE0DCKBNQwiQTYMIgE3DIQAPQwgwj0MhIA/DIXAPwwtSkwMH0VRDJ/KUwytFVkMA4dkDEEHgAyJgIMMKcGDDKlBhAyJAIUMKUGFDKnChQyJAIcMj0CHDI2AhwxBEogMAwKRDJkAlAyjRJQMI4OWDC0HmAyvhJsMocKdDLUAnwyzQJ8MhYCfDIMYoAwjQqwMI0WtDJfArwyhBLAMpUGyDJcAswyZQLMMl4CzDJnAswytF7QMhcC/DLMBwAyxwMAMswDBDDFBwQy1wMEMswDCDLFBwgwzAcMMMYHDDIUAxAyxQMQMM4HEDIUAxQy1QMUMt4DFDLXAxQyxAMYMNUHGDLPAxgyxAccMs8DHDLUAyAyzQMgMsYHIDC9CyQwxQcoMtcDKDLEAywyzQMsMtYDLDLHAywwvAcwMtYDMDLPAzAy1AM0MsUDNDLWAzQyFwM0MsQLODLNAzwyxgM8MhcDPDLEB0AyzwNAMsQHRDLXA0QyzANIMhUDSDLWA0gyFwNIMMwHTDLGB0wyzQNQMhYDUDLHA1AyzANUMhUDVDLWA1QyxwNUMIQXWDCWF2AylAtsMmUDcDBeB3AyZAN0Ml0HdDCcB3gyFgt4MicDfDD8E4AyZAOIMm0DiDL+D4gwZQuQMBULlDD9D5gwxwecMhUDoDLGB6AyFQOkMB4HpDIkA6gyXQOoMGYLqDJ2A6wyNwOsMPwjsDAUB8AybgPAMl8HwDJuA8QyZwPEMFwXyDJmA9AwXwfQMGUH1DJfA9QybAPYMmUD2DBeC9gwZgfcMoQT4DCVF+gwlxfwMJUH/DJnA/wwDAacpgQDcKZWB/CkDAf4pAwLXKoFA2iqCFEA+gn9KPoI/aj4CoYo+EAGbPoIvnD6QxbM+lwHAPhnBwD4/QcE+r8LEPoRBxz6tBMg+gUDKPgSDyj6gA8w+oALOPoSAzz4gAdA+IMHQPq6E0T6FwNM+LTHUPq3L9D4vifo+LQL/Pi8vAD+lghc/scAYP68HGT+v/xw/pYE8P69kPT8xIFQ/MZtkPzEBfD+zg3w/sUB+P72Afj+7wH4/swB/PwMFhD+tAYw/FcOMPy1Gjj8DzJE/lcaXP68BnD+FAJ0/L4WdP606oD8vRL0/H2/APx/B1z+tX9g/gQDoPx9P6D8fg/A/H4PyPx+D9D+fgfY/gwf4P4NN4EGRD+dBkoEmRJLAKkQSgUtEEsHSRBLCLkUSgW5FkgBORpKDV3QSw250Hw0AdR+NBnUfDQ11n4MTdR+JFXUfDRp1H40gdRUQJ3WfQy91n0UxdR8NNHUfjTp1lQNBdR9EQ3Wfg0V1H41HdZUHTnWfg1J1H41UdR8NW3UfjWF1Hw1odR+NbnUfDXV1H417dR8NgnUfjYh1Hw2PdR+NlXUfDZx1H42idQMBqXWfCKp1gUCudZ+DrnWBQLB1n4ywdYHAtnUtA7d1n4i4dYHAvHWfA711gcC+dZ8Mv3WBQMV1LYPFdZ8Ix3WBQMt1n4PLdYFAzXWfjM11gcDTdS0D1HWfiNV1gcDZdZ8D2nWBwNt1nwzcdYFA4nUtg+J1nwjkdYFA6HWfg+h1gUDqdZ+M6nWBwPB1LQTxdR+F83UfBfZ1H4X4dR8F+3Ufhf11LQKAe61NgXsDQoh7gcCJey1FinsDBI17gYCQewPckXstBaB7rciie4NEqHutyKp7lwBAfCFFQHwlDUR8h4BKfBXBSnwXQUt8Hw1MfBeCUnyZgFN8l8BTfJeBWnyXAGR8LwGAfIGAgHwDFoR8wQSQfAMBlHwfBfx+rAEAvhDRAL6sRwm+EDkNviyHKb4sAi2+kDcuvpD/Sb4QvGm+AEHA2QILlFQgAAAAYQACAAQABgC8AwgACgAMABUAlQClALkAwQDDAMcAywDRANcA3QDgAOYA+AAIAQoBcwAQARIBFAEgASwBRAFNAVMBYgFoAWoBdgGSAZQBqQG7AccB0QHVAbkC1wE7ANkB2wG3AOEB/AEMAhgCHQIjAicCowMzAj8CQgJLAk4CUQJdAmACaQJsAm8CdQJ4AoECigKcAp8CowKvArkCxQLJAs0C0QLVAucC7QLxAvUC+QL9AgUDCQMNAxMDFwMbAyMDJwMrAy8DNQM9A0EDSQNNA1EDCw9XA1sDXwNjA2cDawNvA3MDeQN9A4EDhQOJA40DkQOVA5kDnQOhA9wQpQPJA80D2QPdA+ED7wPxAz0ETwSZBPAEAgVKBWQFbAVwBXMFmgX6Bf4FBwYLBhQGGAYeBiIGKAaOBpQGmAaeBqIGqwasA/MGrQP2Bq4D+QavA/wGzAP/Bs0DAgfOAwUHCQcNBxEHhgMyBzUHuQM3BzsHiANTB4kDVgeQA2sHigN3B7ADiQeOA5kHnwejB4wDuAePA7sHtAC+B8AHwgcQIMsHLgDNB88HIADSB9YH2wffB+QH6gfwByAA9gcSIgEIBQgHCB0IJQgnCEMALQgwCJABNgg5CE4ARQhHCEwITghRCFoAqQNaAFMIVwhgCGkAYghlCG8IdAh6CH4IoghJAKQIpgipCFYAqwitCLAItAhYALYIuAi7CMAIwgjFCHYAxwjJCMwI0Ah4ANII1AjXCNsI3gjkCOcI8AjzCPYI+QgCCQYJCwkPCRQJFwkaCSMJLAk7CT4JQQlECUcJSglWCVwJYAliCWQJaAlqCXAJeAl8CYAJhgmJCY8JkQkwAJMJmQmcCZ4JoQmkCWEtzWufn6YJsQm8CccJlQqhChULIAAnCzELjQuhC6ULqQutC7ELtQu5C70LwQvFCyEMNQw5DD0MQQxFDEkMTQxRDFUMWQxvDHEMcwygDLwM3AzkDOwM9Az8DAQNDA0UDSINLg16DYINhQ2JDY0NnQ2xDbUNvA3CDcYNKA4sDjAOMg42DjwOPg5BDkMORg53DnsOiQ6ODpQOnA6jDqkOtA6+DsYOyg7PDtkO3Q7kDuwO8w74DgQPCg8VDxsPIg8oDzMPPQ9FD0wPUQ9XD14PYw9pD3APdg99D4IPiQ+ND54PpA+pD60PuA++D8kP0A/WD9oP4Q/lD+8P+g8AEAQQCRAPEBMQGhAfECMQKRAvEDIQNhA5ED8QRRBZEGEQeRB8EIAQlRChELEQwxDLEM8Q2hDeEOoQ8hD0EAARBREREUERSRFNEVMRVxFaEW4RcRF1EXsRfRGBEYQRjBGSEZYRnBGiEagRqxFvp68RshG2EY0CvhEQEg4TDBSQFJUUUxVsFXIVeBV+FYoVlhUrAKEVuRW9FcEVxRXJFc0V4RXlFUkWYhaIFo4WTBdSF1cXdxd3GH0YERnTGXcafxqdGqIathrAGsYa2hrfGuUa8xojGzAbOBs8G1IbyRvbG90b3xtkMSAcIhwkHCYcKBwqHEgcfhzEHNIc1xzgHOkc+xwEHQkdKR1EHUYdSB1KHUwdTh1QHVIdch10HXYdeB16HYEdgx2FHYcdlh2YHZodnB2eHaAdoh2kHaYdqB2qHawdrh2wHbIdth30A7gdByK6HQIivB3EHfQDxh0HIsgdAiLKHdId9APUHQci1h0CItgd4B30A+IdByLkHQIi5h3uHfQD8B0HIvIdAiL0Hf4dAB4CHgQeBh4IHg4eKx4tBjMePx4sBk8evx7LHt4e8B4DHwUfCR8PHxUfFx8bHx0fJR8oHyofMB8yH7UwOB+QH6Yfqh+sH7Ef/h8PIBAhICEmISAiPiMAAAAAAAAgiCCEMjMggSCnMW8x0DQx0DIz0DRBgEGBQYJBg0GIQYoAAEOnRYBFgUWCRYhJgEmBSYJJiAAAToNPgE+BT4JPg0+IAAAAAFWAVYFVglWIWYEAAAAAYYBhgWGCYYNhiGGKAABjp2WAZYFlgmWIaYBpgWmCaYgAAG6Db4BvgW+Cb4NviAAAAAB1gHWBdYJ1iHmBAAB5iEGEQYZBqEOBQ4JDh0OMRIxFhEWGRYdFqEWMR4JHhkeHR6dIgkmDSYRJhkmoSYdJSmlqSoJLp0yBTKdMjEwAAGsga06BTqdOjLwCbk+ET4ZPi1KBUqdSjFOBU4JTp1OMVKdUjFWDVYRVhlWKVYtVqFeCWYJZiFqBWodajE+bVZtEAH0BRAB+AWQAfgFMSkxqbGpOSk5qbmpBAIxJAIxPAIxVAIzcAITcAIHcAIzcAIDEAIQmAoTGAIRHjEuMT6jqAYTrAYS3AYySAoxqAIxEWkR6ZHpHgU4AgMUAgcYAgdgAgUGPQZFFj0WRSY9JkU+PT5FSj1KRVY9VkVOmVKZIjEEAh0UAp9YAhNUAhE8Ahy4ChFkAhGgAZgJqAHIAeQJ7AoECdwB5ACCGIIcgiiCoIIMgi2MCbABzAHgAlQKAgQCTiIEgxSCBqACBkQOBlQOBlwOBmQOBAAAAnwOBAAAApQOBqQOBygOBAQOYB6QHsAC0ALYAuADKAAEDuAfEB74AxADIAKUDDRMAAQPRANEHxgPAA7oDwQPCAwAAmAO1AxUEgBUEiAAAABMEgQYEiBoEgRgEgCMEhhgEhjgEhjUEgDUEiAAAADMEgVYEiDoEgTgEgEMEhnQEjxYEhhAEhhAEiBUEhtgEiBYEiBcEiBgEhBgEiB4EiOgEiC0EiCMEhCMEiCMEiycEiCsEiGUFggUnBgAsAC0hLQAuIy0nBgBNIU2gTSNN1QZUBgAAAADBBlQG0gZUBigJPAkwCTwJMwk8CRUJACcBJwInBycMJw0nFicaJ74JCQAJGaEJvAmvCbwJMgo8CjgKPAoWCgAmASYGJisKPApHC1YLPgsJAAkZIQs8C5IL1wu+CwgACQAIGUYMVgy/DNUMxgzVDMIMBAAIEz4NCAAJAAgZ2Q3KDcoNDwUSAA8VTQ4yDs0Osg6ZDhIAEghCD7cPTA+3D1EPtw9WD7cPWw+3D0APtQ9xD3IPcQ8AA0EPsg+BD7MPgA+zD4EPcQ+AD5IPtw+cD7cPoQ+3D6YPtw+rD7cPkA+1DyUQLhAFGzUbAAAAAAcbNRsAAAAACRs1GwAAAAALGzUbAAAAAA0bNRsRGzUbOhs1GwAAAAA8GzUbPhs1G0IbNRtBAMYAQgAAAEQARQCOAUcATwAiAlAAUgBUAFUAVwBhAFACUQICHWIAZABlAFkCWwJcAmcAAABrAG0ASwFvAFQCFh0XHXAAdAB1AB0dbwJ2ACUdsgOzA7QDxgPHA2kAcgB1AHYAsgOzA8EDxgPHA1ICYwBVAvAAXAJmAF8CYQJlAmgCaQJqAnsdnQJtAoUdnwJxAnACcgJzAnQCdQJ4AoICgwKrAYkCigIcHYsCjAJ6AJACkQKSArgDQQClQgCHQgCjQgCxxwCBRACHRACjRACxRACnRACtEgGAEgGBRQCtRQCwKAKGRgCHRwCESACHSACjSACISACnSACuSQCwzwCBSwCBSwCjSwCxTACjNh6ETLFMrU2BTYdNo06HTqNOsU6t1QCB1QCITAGATAGBUACBUACHUgCHUgCjWh6EUgCxUwCHUwCjWgGHYAGHYh6HVACHVACjVACxVACtVQCkVQCwVQCtaAGBagGIVoNWo1eAV4FXiFeHV6NYh1iIWYdaglqjWrFosXSId4p5imEAvgJ/AYdBAKNBAInCAIHCAIDCAInCAIOgHoICAYECAYACAYkCAYOgHoZFAKNFAIlFAIPKAIHKAIDKAInKAIO4HoJJAIlJAKNPAKNPAInUAIHUAIDUAInUAIPMHoKgAYGgAYCgAYmgAYOgAaNVAKNVAImvAYGvAYCvAYmvAYOvAaNZAIBZAKNZAIlZAIOxAxMDAB+AAB+BAB/CkQMTAwgfgAgfgQgfwrUDEwMQH4AQH4GVAxMDGB+AGB+BtwOTtwOUIB+AIR+AIB+BIR+BIB/CIR/ClwOTlwOUKB+AKR+AKB+BKR+BKB/CKR/CuQOTuQOUMB+AMR+AMB+BMR+BMB/CMR/CmQOTmQOUOB+AOR+AOB+BOR+BOB/COR/CvwOTvwOUQB+AQB+BnwMTA0gfgEgfgcUDEwNQH4BQH4FQH8KlA5QAAABZH4AAAABZH4EAAABZH8LJA5PJA5RgH4BhH4BgH4FhH4FgH8JhH8KpA5OpA5RoH4BpH4BoH4FpH4FoH8JpH8KxA4C1A4C3A4C5A4C/A4DFA4DJA4AAH0UDIB9FA2AfRQOxA4axA4RwH8WxA8WsA8UAAACxA8K2H8WRA4aRA4SRA4CRA8UgkyCTIMKoAMJ0H8W3A8WuA8UAAAC3A8LGH8WVA4CXA4CXA8W/H4C/H4G/H8K5A4a5A4TKA4AAA7lCykKZBpkEmQD+H4D+H4H+H8LFA4bFA4TLA4AAA8ETwRTFQstCpQalBKUAoQOUqACAhQNgAHwfxckDxc4DxQAAAMkDwvYfxZ8DgKkDgKkDxSCUAiAgICAgICAgICAgsy4uLi4uMiAyIDIgAAAANSA1IDUgAAAAISEAACCFPz8/ISE/MiAAAAAAMGkAADQ1Njc4OSs9KCluMAArABIiPQAoACkAAABhAGUAbwB4AFkCaGtsbW5wc3RSc2EvY2Evc7AAQ2Mvb2MvdbAARkgAHwAAACDfAQEEJE5vUFFSUlJTTVRFTFRNSwDFAEJDAGVFRgBNb9AFRkFYwAOzA5MDoAMRIkRkZWlqMdA3MdA5MdAxMDHQMzLQMzHQNTLQNTPQNTTQNTHQNjXQNjHQODPQODXQODfQODHQSUlJSUlJVlZJVklJVklJSUlYWElYSUlMQ0RNaWlpaWlpaXZ2aXZpaXZpaWlpeHhpeGlpbGNkbTDQM5AhuJIhuJQhuNAhuNQhuNIhuAMiuAgiuAsiuCMiuAAAACUiuCsiKyIrIgAAAC4iLiIuIgAAADwiuEMiuEUiuAAAAEgiuD0AuAAAAGEiuE0iuDwAuD4AuGQiuGUiuHIiuHYiuHoiuIIiuIYiuKIiuKgiuKkiuKsiuHwiuJEiuLIiOAMIMDEAMQAwADIwKAAxACkAKAAxADAAKQAoMjApMQAuADEAMAAuADIwLigAYQApAEEAYQArIgAAAAA6Oj09PT09Pd0quGpWAE4AKDY/WYWMoLo/UQAmLENXbKG2wZtSAF56f52mwc7ntlPIU+NT11YfV+tYAlkKWRVZJ1lzWVBbgFv4Ww9cIlw4XG5ccVzbXeVd8V3+XXJeel5/XvRe/l4LXxNfUF9hX3Nfw18IYjZiS2IvZTRlh2WXZaRluWXgZeVl8GYIZyhnIGtia3lrs2vLa9Rr22sPbBRsNGxrcCpyNnI7cj9yR3JZcltyrHKEc4lz3HTmdBh1H3UodTB1i3WSdXZ2fXaudr927nbbd+J383c6ebh5vnl0est6+XpzfPh8Nn9Rf4p/vX8BgAyAEoAzgH+AiYDjgQAHEBkpODyLj5VNhmuGQIhMiGOIfomLidKJAIo3jEaMVYx4jJ2MZI1wjbONq47KjpuPsI+1j5GQSZHGkcyR0ZF3lYCVHJa2lrmW6JZRl16XYpdpl8uX7ZfzlwGYqJjbmN+YlpmZmayZqJrYmt+aJZsvmzKbPJtam+WcdZ5/nqWeABYeKCxUWGlue5alrej3+xIwAABBU0RTRVNLMJkwAAAAAE0wmTAAAAAATzCZMAAAAABRMJkwAAAAAFMwmTAAAAAAVTCZMAAAAABXMJkwAAAAAFkwmTAAAAAAWzCZMAAAAABdMJkwAAAAAF8wmTAAAAAAYTCZMGQwmTAAAAAAZjCZMAAAAABoMJkwbzCZMHIwmTB1MJkweDCZMHswmTBGMJkwIACZMJ0wmTCIMIowqzCZMAAAAACtMJkwAAAAAK8wmTAAAAAAsTCZMAAAAACzMJkwAAAAALUwmTAAAAAAtzCZMAAAAAC5MJkwAAAAALswmTAAAAAAvTCZMAAAAAC/MJkwAAAAAMEwmTDEMJkwAAAAAMYwmTAAAAAAyDCZMM8wmTDSMJkw1TCZMNgwmTDbMJkwpjCZMO8wmTD9MJkwszDIMAARAAGqAqytAwQFsLGys7S1GgYHCCEJEWERFBFMAAGztLi6v8PFCMnLCQoMDg8TFRcYGRobHiIsMzjd3kNERXBxdH1+gIqNAE6MTglO21YKTi1OC04ydVlOGU4BTilZMFe6TigAKQAAEQIRAxEFEQYRBxEJEQsRDBEOEQ8REBERERIRKAAAEWERKQAoAAIRYREpACgABRFhESkAKAAJEWERKQAoAAsRYREpACgADhFhESkAKAAMEW4RKQAoAAsRaREMEWURqxEpACgACxFpERIRbhEpACgAKQAAToxOCU7bVpRObVEDTmtRXU5BUwhna3A0bChn0ZEfV+VlKmgJZz55DVR5cqGMXXm0UuNOfFRmW+N2AU/HjFRTbXkRT+qB84FPVXxeh2WPe1BURTIAMQAzADAAABEAAgMFBgcJCwwODxAREgARAGECYQNhBWEGYQdhCWELYQxhDhFhEQARDmG3AGkLEQFjAGkLEW4RAE6MTglO21aUTm1RA05rUV1OQVMIZ2twNGwoZ9GRH1flZSpoCWc+eQ1UeXKhjF15tFLYeTd1c1lpkCpRcFPobAWYEU+ZUWNrCk4tTgtO5l3zUztTl1tmW+N2AU/HjFRTHFkzADYANAAwADUwMQAIZzEAMAAIZ0hnZXJnZVZMVESiMAACBAYICQsNDxETFRcZGx0fIiQmKCkqKywtMDM2OTw9Pj9AQkRGR0hJSktNTk9Q5E6MVKEwATBbJwFKNAABUjkBojAAWkmkMAAnTwykMABPHQIFT6gwABEHVCGoMABUA1SkMAZPFQZYPAcARqswAD4YHQBCP1GsMABBRwBHMq4wrDCuMAAdTq0wADg9TwE+E0+tMO0wrTAAQAM8M60wAEA0Txs+rTAAQEIWG7AwADkwpDAMRTwkTwtHGABJrzAAPk0esTAASwgCOhkCSyykMBEAC0e1MAA+DEcrsDAHOkMAuTACOggCOg8HQwC3MBAAEjQRPBMXpDAqHyQrACC7MBZBADgNxDANOADQMAAsHBuiMDIAFyZJrzAlADyzMCEAIDihMDQASCIoozAyAFklpzAvHBAARNUwABQerzApABBNPNowvTC4MCITGiAzDCI7ASJEACFEB6QwOQBPJMgwFCMA2zDzMMkwFCoAEjMiEjMqpDA6AAtJpDA6AEc6Hys6Rwu3MCc8ADA8rzAwAD5E3zDqMNAwDxoALBvhMKwwrDA1ABxHNVAcP6IwQlonQlpJRABRwzAnAAUo6jDpMNQwFwAo1jAVJgAV7DDgMLIwOkEWAEHDMCwABTAAuXAxADAAuXAyADAAuXBoUGFkYUFVYmFyb1ZwY2RtZABtALIASQBVAHNeEGItZoxUJ1ljaw5mu2wqaA9fGk8+eXAAQW4AQbwDQW0AQWsAQUsAQk0AQkcAQmNhbGtjYWxwAEZuAEa8A0a8A2dtAGdrAGdIAHprSHpNSHpHSHpUSHq8AxMhbQATIWQAEyFrABMhZgBtbgBtvANtbQBtYwBtawBtYwAKCk8ACk9tALIAYwAICk8KClAAClBtALMAawBtALMAbQAVInMAbQAVInMAsgBQYWtQYU1QYUdQYXJhZHJhZNFzcgBhAGQAFSJzALIAcABzbgBzvANzbQBzcABWbgBWvANWbQBWawBWTQBWcABXbgBXvANXbQBXawBXTQBXawCpA00AqQNhLm0uQnFjY2NkQ9FrZ0NvLmRCR3loYUhQaW5LS0tNa3RsbWxubG9nbHhtYm1pbG1vbFBIcC5tLlBQTVBSc3JTdldiVtFtQdFtMQDlZTEAMADlZTIAMADlZTMAMADlZWdhbEoETARDRlEmAVMBJ6c3q2sCUqtIjPRmyo7IjNFuMk7lU5yfnJ9RWdGRh1VIWfZhaXaFfz+Guof4iI+QAmobbdlw3nM9hGqR8ZmCTnVTBGsbci2GHp5QXetvzYVkicli2IEfiMpeF2dqbfxyzpCGT7dR3lLEZNNqEHLndgGABoZchu+NMpdvm/qdjHh/eaB9yYMEk3+e1orfWARfYHx+gGJyynjCjPeW2FhiXBNq2m0Pby99N35LltJSi4DcUcxRHHq+ffGDdZaAi89iAmr+ijlO51sSYIdzcHUXU/t4v0+pXw1OzGx4ZSJ9w1NeWAF3SYSqirprsI+IbP5i5YKgY2V1rk5pUclRgWjnfG+C0orPkfVSQlRzWexexWX+byp5rZVqmpeezp6bUsZmd2tij3RekGEAYppkI29JcYl0ynn0fW+AJo/uhCOQSpMXUqNSvVTIcMKIqorJXvVfe2Ouaz58dXPkTvlW51u6XRxgsnNpdJp/RoA0kvaWSJcYmItPrnm0kbiW4WCGTtpQ7ls/XJllAmrOcUJ2/IR8kI2fiGYulolSe2fzZ0FtnG4JdFl1a3gQfV6YbVEuYniWK1AZXeptKo+LX0RhF2iHc4aWKVIPVGVcE2ZOZ6ho5WwGdOJ1eX/PiOGIzJHilj9Tum4dVNBxmHT6haOWV5yfnpdny23ogct6IHuSfMBymXBYi8BONoM6UgdSpl7TYtZ8hVsebbRmO49MiE2Wi4nTXkBRwFUAAAAAWlgAAHRmAAAAAN5RKnPKdjx5XnlleY95Vpe+fL1/AAAShgAA+IoAAAAAOJD9kO+Y/JgombSd3pC3lq5P51BNUclS5FJRU51VBlZoVkBYqFhkXG5clGBoYY5h8mFPZeJlkWaFaHdtGm4ib25xK3IidJF4PnlJeUh5UHlWeV15jXmOeUB6gXrAe/R9CX5BfnJ/BYDtgXmCeYJXhBCJlokBizmL04wIjbaPOJDjlv+XO5h1YO5CGIICJk61UWhRgE9FUYBRx1L6Up1VVVWZVeJVWlizWERZVFliWihb0l7ZXmlfrV/YYE5hCGGOYWBh8mE0YsRjHGRSZFZldGYXZxtnVmd5a7prQW3bbstuIm8ecG5xp3c1cq9yKnNxdAZ1O3Uddh92ynbbdvR2SndAd8x4sXrAe3t8W330fT5/BYBSg++DeYdBiYaJlom/iviKy4oBi/6K7Yo5i4qLCI04j3KQmZF2knyW45ZWl9uX/5cLmDuYEpucn0ooRCjVM507GEA5QElS0FzTfkOfjp8qoAJmZmZpZmxmZmlmZmx/AXRzAHRlBQ8RDwAPBhkRDwjZBbQFAAAAAPIFtwXQBRIAAwQLDA0YGukFwQXpBcIFSfvBBUn7wgXQBbcF0AW4BdAFvAXYBbwF3gW8BeAFvAXjBbwFuQUtAy4DLwMwAzEDHAAYBiIGKwbQBdwFcQYAAAoKCgoNDQ0NDw8PDwkJCQkODg4OCAgICDMzMzM1NTU1ExMTExISEhIVFRUVFhYWFhwcGxsdHRcXJycgIDg4ODg+Pj4+QkJCQkBAQEBJSUpKSkpPT1BQUFBNTU1NYWFiYkkGZGRkZH5+fX1/fy6Cgnx8gICHh4eHAAAmBgABAAEArwCvACIAIgChAKEAoACgAKIAogCqAKoAqgAjACMAI8wGAAAAACYGAAYABwAfACMAJAIGAgcCCAIfAiMCJAQGBAcECAQfBCMEJAUGBR8FIwUkBgcGHwcGBx8IBggHCB8NBg0HDQgNHw8HDx8QBhAHEAgQHxEHER8SHxMGEx8UBhQfGwYbBxsIGx8bIxskHAccHxwjHCQdAR0GHQcdCB0eHR8dIx0kHgYeBx4IHh8eIx4kHwYfBx8IHx8fIx8kIAYgByAIIB8gIyAkIQYhHyEjISQkBiQHJAgkHyQjJCQKSgtKI0ogAEwGUQZRBv8AHyYGAAsADAAfACAAIwAkAgsCDAIfAiACIwIkBAsEDAQfJgYEIAQjBCQFCwUMBR8FIAUjBSQbIxskHCMcJB0BHR4dHx0jHSQeHx4jHiQfAR8fIAsgDCAfICAgIyAkI0okCyQMJB8kICQjJCQABgAHAAgAHwAhAgYCBwIIAh8CIQQGBAcECAQfBCEFHwYHBh8HBgcfCAYIHw0GDQcNCA0fDwcPCA8fEAYQBxAIEB8RBxIfEwYTHxQGFB8bBhsHGwgbHxwHHB8dBh0HHQgdHh0fHgYeBx4IHh8eIR8GHwcfCB8fIAYgByAIIB8gISEGIR8hSiQGJAckCCQfJCEAHwAhAh8CIQQfBCEFHwUhDR8NIQ4fDiEdHh0fHh8gHyAhJB8kIUAGTgZRBicGECIQIxIiEiMTIhMjDCIMIw0iDSMGIgYjBSIFIwciByMOIg4jDyIPIw0FDQYNBw0eDQoMCg4KDwoQIhAjEiISIxMiEyMMIgwjDSINIwYiBiMFIgUjByIHIw4iDiMPIg8jDQUNBg0HDR4NCgwKDgoPCg0FDQYNBw0eDCANIBAeDAUMBgwHDQUNBg0HEB4RHgAkACQqBgACGwADAgADAgADGwAEGwAbAgAbAwAbBAIbAwIbAwMbIAMbHwkDAgkCAwkCHwkbAwkbAwkbAgkbGwkbGwsDAwsDAwsbGwoDGwoDGwoCIAobBAobBAobGwobGwwDHwwEGwwEGw0bAw0bAw0bGw0bIA8CGw8bGw8bGw8bHxAbGxAbIBAbHxcEGxcEGxgbAxgbGxoDGxoDIBoDHxoCAhoCAhoEGxoEGxobAxobAxsDAhsDGxsDIBsCAxsCGxsEAhsEGygGHQQGHx0EHx0dHgUdHgUhHgQdHgQdHgQhHh0iHh0hIh0dIh0dAAYiAgQiAgQhAgYiAgYhAh0iAh0hBB0iBAUhBB0hCwYhDQUiDAUiDgUiHAQiHB0iIgUiIgQiIh0iHR0iGh0iHgUiGh0FHAUdER0iGx0iHgQFHQYiHAQdGx0dHAQdHgQFBAUiBQQiHQQiGR0iAAUiGx0dEQQdDR0dCwYiHgQiNQYAD50ND50nBgAdHSAAHAEKHgYeCA4dEh4KDCEdEh0jICEMHR41BgAPFCcGDh0i/wAdHSD/Eh0jIP8hDB0eJwYFHf8FHQAdICcGCqUAHSwAATACMDoAOwAhAD8AFjAXMCYgEyASAQBfXygpe30IMAwNCAkCAwABBAUGB1sAXQA+ID4gPiA+IF8AXwBfACwAATAuAAAAOwA6AD8AIQAUICgAKQB7AH0AFDAVMCMmKistPD49AFwkJUBABv8LAAv/DCAATQZABv8OAA7/DwAP/xAAEP8RABH/EgASIQYAAQECAgMDBAQFBQUFBgYHBwcHCAgJCQkJCgoKCgsLCwsMDAwMDQ0NDQ4ODw8QEBEREhISEhMTExMUFBQUFRUVFRYWFhYXFxcXGBgYGBkZGRkgICAgISEhISIiIiIjIyMjJCQkJCUlJSUmJiYmJycoKCkpKSkiBiIAIgAiASIBIgMiAyIFIgUhAIUpATABCwwA+vGgoqSmqOLk5sL7oaOlp6mqrK6wsrS2uLq8vsDDxcfJysvMzc7R1Nfa3d7f4OHj5efo6err7O7ymJkxMU8xVTFbMWExogCjAKwArwCmAKUAqSAAAAIlkCGRIZIhkyGgJcsl0ALRAuYAmQJTAgAAowJmq6UCpAJWAlcCkR1YAl4CqQJkAmICYAKbAicBnAJnAoQCqgKrAmwCBN+Op24CBd+OAgbf+AB2AncCcQB6AgjffQJ+AoACqAKmAmerpwKIAnEsAACPAqECogKYAsABwQHCAQrfHt9BBEAAAAAAFJkQuhAAAAAAmxC6EAUFpRC6EAUxEScRMhEnEVVHEz4TRxNXE1W5FLoUuRSwFAAAAAC5FL0UVVC4Fa8VuRWvFVU1GTAZBVfRZdFY0WXRX9Fu0V/Rb9Ff0XDRX9Fx0V/RctFVVVUFudFl0brRZdG70W7RvNFu0bvRb9G80W/RVVVVQQBhAEEAYQBpAEEAYQBBAENEAABHAABKSwAATk9QUQBTVFVWV1hZWmFiY2QAZmgAcABBAGEAQUIAREVGR0oAUwBhAEFCAERFRkcASUpLTE0AT1MAYQBBAGEAQQBhAEEAYQBBAGEAQQBhAEEAYQAxATcCkQOjA7ED0QMkAB8EIAWRA6MDsQPRAyQAHwQgBZEDowOxA9EDJAAfBCAFkQOjA7ED0QMkAB8EIAWRA6MDsQPRAyQAHwQgBQsMMAAwADAAMAAwACcGAAEFCCoGHggDDSAZGhscCQ8XCxgHCgABBAYMDhBEkHdFKAYsBgAARwYzBhcQERITAAYOAg80BioGKwYuBgAANgYAADoGLQYAAEoGAABEBgAARgYzBjkGAAA1BkIGAAA0BgAAAAAuBgAANgYAADoGAAC6BgAAbwYAACgGLAYAAEcGAAAAAC0GNwZKBkMGAABFBkYGMwY5BkEGNQZCBgAANAYqBisGLgYAADYGOAY6Bm4GAAChBicGAAEFCCAhCwYQIyoGGhscCQ8XCxgHCgABBAYMDhAoBiwGLwYAAEgGMgYtBjcGSgYqBhobHAkPFwsYBwoAAQQGDA4QMC4wACwAKABBACkAFDBTABUwQ1JDRFdaQQBIVk1WU0RTU1BQVldDTUNNRE1SREpLMDAAaGhLYldbzFPHMIxOGlnjiSlZpE4gZiFxmWVNUoxfjVGwZR1SQn0fdamM8Fg5VBRvlWJVYwBOCU5KkOZdLU7zUwdjcI1TYoF5enoIVIBuCWcIZzN1clK2VU2RFDAVMCxnCU6MTolbuXBTYtd23VJXZZdf71MwADhOBQAJIgFgT65Pu08CUHpQmVDnUM9QnjQ6Bk1RVFFkUXdRHAW5NGdRjVFLBZdRpFHMTqxRtVHfkfVRA1LfNDtSRlJyUndSFTUCACCAgAAIAADHUgACHTM+P1CCipOstri4uCwKcHDKU99TYwvrU/FTBlSeVDhUSFRoVKJU9lQQVVNVY1WEVYRVmVWrVbNVwlUWVwZWF1dRVnRWB1LuWM5X9FcNWItXMlgxWKxY5BTyWPdYBlkaWSJZYlmoFuoW7FkbWida2FlmWu42/DYIWz5bPlvIGcNb2FvnW/NbGBv/WwZcU18iXIE3YFxuXMBcjVzkHUNd5h1uXWtdfF3hXeJdLzj9XShePV5pXmI4gyF8OLBes162XspekqP+XjEjMSMBgiJfIl/HOLgy2mFiX2tf4ziaX81f11/5X4FgOjkcOZRg1CbHYAICAAAAAAAAAAgACgAAAggAgAgAAAiAKIACAAACSGEABAYEMkZqXGeWqq7I011iAFR38wwrPWP8Ymhjg2PkY/ErImTFY6ljLjppZH5knWR3ZGw6T2VsZQow42X4ZklmGTuRZgg75DqSUZVRAGecZq2A2UMXZxtnIWdeZ1NnwzNJO/pnhWdSaIVobTSOaB9oFGmdO0Jpo2nqaahqozbbahg8IWunOFRrTjxya59rumu7a406Cx36Ok5svDy/bM1sZ2wWbT5td21BbWlteG2FbR49NG0vbm5uMz3Lbsdu0T75bW5vXj+OP8ZvOXAecBtwlj1KcH1wd3CtcCUFRXFjQpxxq0MocjVyUHIIRoBylXI1RwIgAAAgAAAAAAiAAAACAoCKAAAgAAgKAICIgCAUSHpzi3OsPqVzuD64Pkd0XHRxdIV0ynQbPyR1Nkw+dZJMcHWfIRB2oU+4T0RQ/D8IQPR281DyUBlRM1Eedx93H3dKdzlAi3dGQJZAHVROeIx4zHjjQCZWVnmaVsVWj3nreS9BQHpKek96fFmnWqda7noCQqtbxnvJeydCgFzSfKBC6HzjfAB9hl9jfQFDx30CfkV+NEMoYkdiWUPZYnp/PmOVf/p/BYDaZCNlYICoZXCAXzPVQ7KAA4ELRD6BtVqnZ7VnkzOcMwGCBIKej2tEkYKLgp2Cs1KxgrOCvYLmgjxr5YIdg2ODrYMjg72D54NXhFODyoPMg9yDNmxrbQIAACAiKqAKACCAKACoICAAAoAiAooIAKoAAAACAAAo1WwrRfGE84QWhcpzZIUsb11FYUWxb9Jwa0VQhlyGZ4ZphqmGiIYOh+KGeYcoh2uHhofXReGHAYj5RWCIY4hndteI3og1RvqIuzSueGZ5vkbHRqCK7YqKi1WMqHyrjMGMG413jS9/BAjLjbyN8I3eCNSOOI/She2FlJDxkBGRLocbkTiS15LYknyS+ZMVlPqLi5WVSbeVd43mScOWsl0jl0WRGpJuSnZK4JcKlLJKlpQLmAuYKZi2leKYM0spmaeZwpn+mc5LMJsSm0Cc/ZzOTO1MZ53OoPhMBaEOopGiu55WTfme/p4Fnw+fFp87nwCmAoigAAAAAIAAKAAIoICggACAgAAKiIAAgAAgKgCARCAVIk0DAJcFIMYFAOcGAEUHAJwIAE0JADwLAD0NADYPADgQIDoZAMsaINMcAM8dAOIgAC4wICupIO2rADkKAYQPIcARAUMUATkYIUIdIWfRATDhIUvpAQBB4K0DC/EGss/UAOgD3ADoANgE3AHKA9wBygrcBAED3McA8MAC3MIB3IDCA9zAAOgB3MBB6QDqQekA6gDpzLDixLDYANzDANzCAN4A3MUF3MEA3MEA3gDkwEkKQxOAABeAQRiAwADcgAASsBfHQh6vRxvBAdzEANzBANyPACOwNMaBwwDcwIHBgADcwQDcogAkncAA3MEA3MEC3MAB3MAA3MIA3MAA3MAA3MAA3MGwb8YA3MCIANyXw4DIgMKAxKoC3LALwALcw6nEBNzNgADcwQDcwQDcwgLcQhvCANzBAdzEsAsAB48ACYLAANzBsDYAB48ACa/AsAwAB48ACbA9AAePAAmwPQAHjwAJsE4ACbA9AAePAAmGAFQAW7A0AAePAAmwPAEJjwAJsEsACbA8AWcACYwDa7A7AXYACYwDerAbAdyaANyAANyAANiwBkGBgACEhAOCgQCCgMEACYDBsA0A3LA/AAeAAQmwIQDcsp7Cs4MBCZ0ACbBsAAmJwLCaAOSwXgDewADcsKrAANywFgAJk8eBANyvxAXcwQDcgAHcwQHcxADcw7A0AAeOAAmlwADcxrAFAQmwCQAHigEJsBIAB7BnwkEABNzBA9zAQQAFAYMA3IXAgsGwlcEA3MYA3MEA6gDWANwAyuQA6AHkANwA2sAA6QDcwADcsp/BAQHDAgHBg8CCAQHAANzAAQED3MC4A83CsFwACbAv37H5ANoA5ADoAN4B4LA4AQi4baPAg8mfwbAfwbDjAAmkAAmwZgAJmtGwCALcpAAJsC4AB4sACbC+wIDBANyBwYTBgMCwAwAJsMUACbhG/wAastDGBtzBs5wA3LCxANywZMS2YQDcgMCnwAABANyDAAmwdMAA3LIMw7FSwbBoAdzCANzAA9ywAMAA3MAA3LCPAAmoAAmNAAmwCAAJAAewFMKvAQmwDQAHsBsACYgAB7A5AAkAB7CBAAcACbAfAQePAAmXxoLEsJwACYIAB5bAsDIACQAHsMoACQAHsE0ACbBFAAkAB7BCAAmw3AAJAAew0QEJgwAHsGsACbAiAAmRAAmwIAAJsXQACbDRAAeAAQmwIAAJuEUnBAGwCsa0iAEGuER7AAG4DJUB2AIBggDiBNiHB9yBxAHcncOwY8K4BYrGgNCBxoDBgMSw1MaxRsCwDMO1rwbcsDzFAAcAQeC0AwviDgFKwEkCSoACgQKCAoMCwALCAgAKhAJCJIUCwAeACYIJQCSAIsQCgiKEIoYixgLIAsoCzAKHAooizgKMIpAikiKOIogCiQKKAoIkAAMCAwQDiwKAJAgDhAmGCVgkAgoGA5gimiKeIgAJCgOgIgwDDgNACBADEgOiIqYiwAmkIqgiqiKMAo0CjgJAA0IDRAOAA48CjiTCB4gJigmQJEYDrCIABLAiQgiyIgIEtCJABEQEtiJCBMIiwCLEIsYiyCJACcAEkQLKIsQEzCLCBNAiziKSApMClAKVAkAFQgUICpYClCREBcQHjAmOCcAGkiRECAgjCiOABQwjhAWQCZIJDiOCBRIjhgWIBRQjjAUWI5gJigUeI5AFICOaCY4FJCMiI5kCmgKbAsAFwgXEBZwCrCTGBcgFxgeUCZYJAAeqJCYjygUqIygjQCNCI0QjRiPMBUojSCNMI04jUCO4JJ0CzgW+JAwKUiMABrwkuiRABlQjQgZEBlYjWCOgAqECogKjAsECwwIBCqQCQySlAsEHgQmDCUEkgSLFAoMihSKHIscCyQLLAs0CpwKLIs8CjSKRIpMijyKoAqkCqgKDJAEDAwMFA6sCgSQJA4UJhwlZJAMKBwOZIpsinyIBCQsDoSINAw8DQQgRAxMDoyKnIsEJpSKpIqsigCOsAq0CrgJBA0MDRQOvAo8kwweJCYsJkSRHA60iAQSECLEiQwizIgMEtSJBBEUEtyJDBMMiwSLFIsciySJBCcEEsQLLIsUEzSLDBNEizyKyArMCtAK1AkEFQwUJCrYClSRFBcUHjQmPCcEGkyRFCAkjCyOBBQ0jhQWRCZMJDyODBRMjhwWJBRUjjQUXI5kJiwUfI4EjkQUhI5sJjwUlIyMjuQK6ArsCwQXDBcUFvAKtJMcFyQXHB5UJlwkBB6skJyPLBSsjKSNBI0MjRSNHI80FSyNJI4IjTSNPI1EjuSS9As8FvyQNClMjvwK9JIMjuyRBBlUjQwZFBlcjWSMBMYAMAC5GJEQkSiRIJAAIQglECQQIiCKGJIQkiiSIJK4imCSWJJwkmiQAIwYKAiMECkYJzgfKB8gHzAdHJEUkSyRJJAEIQwlFCQUIiSKHJIUkiySJJK8imSSXJJ0kmyQBIwcKAyMFCkcJzwfLB8kHzQdQJE4kVCRSJFEkTyRVJFMklCKWIpUilyIEIwYjBSMHIxgjGSMaIxsjLCMtIy4jLyMAJKIkoCSmJKQkqCSjJKEkpySlJKkksCSuJLQksiS2JLEkryS1JLMktySCCIAIgQgCCAMInCKdIgoKCwqDCEALiiyBDIksiCxAJUElAC0HLgANQCZBJoAuAQ3IJskmAC+ELwINgy+CL0AN2CbZJoYxBA1AJ0EnADGGMAYNhTCEMEENQCgAMgcNTyhQKIAyhCwDLlcoQg2BLIAswCTBJIYsgyzAKEMNwCXBJUApRA3AJsEmBS4CLsApRQ0FLwQvgA3QJtEmgC9AKoIN4CbhJoAwgTDAKoMNBDADMIENwCfBJ4IwQCuEDUcoSCiEMYExBi8IDYEvBTBGDYMwgjEADgEOQA+AEYIRAw8AD8ARAQ9AEQISBBKBD0ASwA9CEoAPRBKEEoIPhhKIEooSwBKCEoERgxFDEEAQwRFBEEERAxIFEsEQQRIAEEMSwBBFEoUSwhCHEokSixLBEoMSgBAAEQERABIBEoASgRJAE0ETQxNCE0QTwhMAFMATQBSAFMAUQBVBFUAXABdBF8AXABgCGAEYQBiAGAAZwBjBGAEZQBlCGUEZgBnAGcIZwRmAHMAcwB2AHwAgAiAEIAYgCCBAIIAggiDAIMEgACG4IrkiECMRIxwjHSNMJFYkTSRXJIwkjSSeJJ8kACUCJQQlwCsBJQMlBSXBK8IrwyvEK8UrxivHK4AlgiWEJcgrgSWDJYUlySvKK8srzCvNK84rzysAJgImASYDJoAmgiaBJoMmwibEJsYmACzDJsUmxyYBLAIsAywELAUsBiwHLMomzCbOJggsyybNJs8mCSwKLAssDCwNLA4sDyzSJtQm1ibTJtUm1ybaJtwm3ibbJt0m3yYAJwInAScDJ4AngieBJ4MnACgCKAQoASgDKAUoQihEKEYoSShLKE0oQCxKKEwoTihBLEIsQyxELEUsRixHLFEoUyhVKEgsUihUKFYoSSxKLEssTCxNLE4sTyyCLAEugDGHLAEvAi8DLwYuhTEAMAEwAjBARkFGgEbARsJGwUYAR0BHgEfAR8JHAElASYBJgkkASsJJA0oESkBKQUqASoFKwErBSsBLwUsASwFLQEtBS8JLw0uAS4FLgkuDSwBMAUwCTANMAFZAVEJURFRGVEhUSlRMVE5UUFRSVFRUVlSAVIJUhFTAVMFUAFUBVUBVQVWAVYFVwFXBVYBWwFgAVwJXBFcGVwhXClcMVw5XEFcSVxRXFldAV0JXRFeAV4FXwFfBVwBYAVhAWEFYgFiBWABZAVkCWQNZQFlAj0KPgI/Aj8GPAJABkEGQQJBDkICQgZDAkABB0MMDC8Ye+hgXVg1WEhMWDBYRNukCNkw24RISFhMOEA7iEhIMEwz6GRcWbQ8WDg8FFAwbDw4PDCsOAjYOCwUVSxbhDwzB4hAM4gD/MAL/CAL/J78iIQJfXyEiYQIhAkFCIQIhAp9/Al9fIQJfPwIFPyJlAQMCAQMCAQMC/wgC/woCAQMCXyEC/zKiIQIhIl9BAv8A4jwF4hPkCm7kBO4GhM4EDgTuCeZofwQOPyAEQhYBYC4BFkEAAQAhAuEJAOEB4hs/AkFC/xBiPwxfPwLhK+Io/xoPhij/L/8GAv9YAOEeIAS24iEWESAvDQDmJREGFiYWJhYG4ADlE2BlNuADu0w2DTYv5gMWG1blGATlAuYN6QJ2JQblWxYFxhsPpiQmD2Yl6QJFLwX2BgAbBQblFuYTIOVR5gMF4AbpAuUZ5gEkD1YEIAYt5Q5mBOYBBEYEhiD2BwDlEUYgFgDlA4DlEA6lADug5gDlIQTmEBvmGAflLgYHBgVH5gBnBicFxuUCJjbpAhYE5QcGJwDlACAlIOUOAMUABUBlIAYFR2YgJyAnBgXgAAdgJQBFJiDpAiUtqw8NBRYGICYHAKVgJSDlDgDFACUAJQAlIAYARyZgJiBGQAbAZQAFwOkCJkUGFuACJgcA5QEARQDlDgDFACUAhSAGBUeGACYHACcGIAXgByUmIOkCFg3ABaYABicA5QAgJSDlDgDFACUAhSAGBQcGB2YgJyAnBsAmB2AlAEUmIOkCDwWr4AIGBQClQEUAZUAlAAUAJUAlQEVA5QRgJwYnQEcARwYgBaAH4AbpAkuvDQ+ABkcG5QAARQDlDwDlCCAGBUZnAEYAZsAmAEUgBSAlJiDpAsAWyw8FBicW5QAARQDlDwDlAgCFIAYFBwaHAAYnACcmwCegJQAlJiDpAgAl4AUmJ+UBAEUA5SEmBUdmAEcARwYFD2BFB8tFJiDpAusBD6UABicA5QpA5RAA5QEABSDFQAZgR0YABgDnAKDpAiAnFuAE5SgGJcZgDaUE5gAW6QI24B0lAAUAhQDlEAAFAOUCBiXmAQUghQAEAKYg6QIgZeAYBU/2Bw8WTyav6QLrAg8GDwYPBhITEhMn5QAA5Rxg5gYHhhYmheYDAOYcAO8ABq8AL5ZvNuAd5SMnZgemByYnJgXpAralJyZlRgVHJcdFZuUFBicmpwYFB+kCRwYv4R4AAYABIOIjFgRC5YDBAGUgxQAFAGUg5SEAZSDlGQBlIMUABQBlIOUHAOUxAGUg5TsgRvYB6wxA5QjvAqDhTiCiIBHlgeQPFuUJF+USEhNA5UNWSuUAwOUKRgfgAeULJgc24AHlCibgBOUFAEUAJuAE5SwmB8bnAAYn5gNWBFYNBQYg6QKg6wKgthF2RhsG6QKg5RsE5S3AhSblGgYFgOU+4ALlFwBGZyZHYCcGp0ZgD0A26QLlFiCF4APlJGDlEqDpAgtA7xrlDyYnBiA25S0HBgfGAAYHBifmAKfmAiAG6QKg6QKg1gS2IOYGCOYI4ClmB+UnBgeGBwaHBiflAEDpAtbvAuYB7wE2ACYH5RYHZicmB0Yl6QLlJAYHJkcGB0Yn4AB25RznAOYAJyZAlukCQEXpAuUWpDbiAcDhIyBB9gDgAEYW5gUHxmUGpQYlByYFgOIk5DfiBQTiGuQd5jj/gA7iAP9a4gDhAKIgoSDiAOEA4gDhAKIgoSDiAAABAAEAAQA/wuEA4gYg4gDjAOIA4wDiAOMAggAiYQMOAk5CACJhA05iICJhAE7iAIFOIEIAImEDLgD3A5uxNhQVEjQVEhT2ABgZmxf2ARQVdjBWDBIT9gMMFhD2AhebAPsCCwQgq0wSEwTrAkwSEwDkBUDtGeAH5gVoBkjmBOAHLwFvAS8CQSJBAg8BLwyBrwEPAQ8BD2EPAmECZQIvIiGMP0IPDC8CD+sI6hs/agsvYIyPLG8MLwwvDM8M7xcsLwwPDO8X7ICE7wASExIT7wwszxIT70kM7xbsEe8grO894BHvA+AN6zTvRusO74AvDO8BDO8u7ADvZwzvgHASExITEhMSExITEhMSE+sW7ySMEhPsFxITEhMSExITEhPsCO+AeOx7EhMSExITEhMSExITEhMSExITEhMSE+w3EhMSE+wYEhPsgHrvKOwNL6zvHyDvGADvYeEo4ihfISLfQQI/Aj+CJEEC/1oCr39GP4B2CzbiHgACgAIg5TDABBbgBgblD+ABxQDFAMUAxQDFAMUAxQDFAOYYNhQVFBVWFBUWFBX2ARE2ERYUFTYUFRITEhMSExITlgT2AjF2ERYS9gUvVhITEhMSExITEeAa7xIA71HgBO+ATuAS7wRgF1YPBAUKEhMSExITEhMSEy8SExITEhMSExESMw/qAWYnEYQvSgQFFi8A5U4gJi4kBRHlUhZEBYDlIwDlVgAva+8C5RjvHOAE5QjvFwDrAu8W6wAP6wfvGOsC7x/rB++AuOWZOO845cARjQTlg+9A7y/gAeUgpDblgIQEVuUI6QIl4Az/JgUGSBbmAhYE/xQkJuU+6gImtuAA7g/kAS7/BiL/NgTiAJ//AgQufwV/Iv8NYQKBAv8HQQI/gD8AAgACf+AQRD8FJALFBkUGZQblDycmB28GQKsvDQ+g5Sx24AAn5SrnCCbgADbpAqDmCqVWBRYlBukC5RTmADblD+YDJ+ADFuUVQEYH5ScGJ2YnJkf2BQAE6QJgNoUGBOUB6QKFAOUhpicmJybgAUUG5QAGByDpAiB25QgEpU8FBwYH5SoGBUYlJoUmBQYF4BAlBDblAwcmJzYFJAcG4AKlIKUgpeABxQDFAOIjDmTiAQQuYOJI5RsnBicGJxYHBiDpAqDlqxzgBOUPYOUpYPyHeP2YeOWA5iDlYuAewuAEgoAFBuUCDOUFAIUABQAlACUA5WTuCeAI5YDjExLvCOU4IOUuwA/gGOUEDU/mCNYSExag5ggWMTASExITEhMSExITEhMSExITNhITdlBWAHYREhMSExITVgwRTAAWDTZghQDlfyAbAFYNVhITFgwWETbpAjZMNuESEhYTDhAO4hISDBMMEhMWEhM25QIE5SUk5RdApSClIKUgRUAtDA4PLQAPbC/gAlsvIOUEAOUSAOULACUA5Qcg5QbgGuVzgFZg6yVA7wHqLWvvCStPAO8FQA/gJ+8lBuB65RVA5SngBwbrE2DlGGvgAeUMCuUACoDlHoaA5RYAFuUcYOUAForgIuEg4iDlRiDpAqDhHGDiHGDlIOAA5SzgAxbhAwDhBwDBACEA4gMA4gcAwgAi4DvlgK/gAeUO4ALlAOAQpADkIgDkAeA9pSAFAOUkACVABSDlDwAW6wDlDy/L5RfgAOsB4CjlCwAlgIvlDqtAFuUSgBbgOOUwYCsl6wgg6yYFRgAmgGZlAEUA5RUgRmAG6wHA9gHA5RUrFuUVS+AY5QAP5RQmYIvW4AHlLkDW5Q4g6wDlC4DrAOUKwHbgBMvgSOVB4C/hK+AF4ivAq+UcZuAA6QLggJ7rFwDlIgAmESAl4EblFesCBeAA5Q7mA2uW4A7lCmZ24B7lDcvgDOUP4AEHBgflLeYH1mDrDOkCBiUmBeABRgflJUdmJyY2G3YG4AIbIOURwOkCoEblHIYH5gAA6QJ2BScF4ADlGwY2BeABJgflKEfmASdldmYWBwbpAgUWBVYA6wzgA+UKAOURR0YnBgcmtgbgOcUABQBlAOUHAOUCFqDlJwZH5gCA6QKgJicA5QAgJSDlDgDFACUAhQAmBScGZyAnIEcgBaAHgIUnIMZAhuCAA+UtR+YAJ0YHBmWW6QI2ABYGReAW5ShHpgcGZyYHJiUWBeAA6QLggB7lJ0dmIGcmByb2D2Um4BrlKEfmACcGByZWBeAD6QKg9gXgC+UjBgcGJ6YHBgUWoOkC4C7lEyBGJ2YHhmDpAitWD8XggDHlJEfmAQcmFuBc4RjiGOkC6wHgBOUAIAUg5QAAJQDlEKcAJyAmBwYFBwUHBlbgAekC4D7lACDlH0dmICZnBgUWBQfgEwXmAuUgpgcFZvYABuAABaYnRuUm5gUHJlYFluAF5UHggH/lAQDlHQfGAKYHBgWW4ALpAusLQDblFiDmDgAHxgcmBybgQcUAJQDlHqZABgAmAMYFBuAA6QKgpQAlAOUYhwAmACcGBwYFwOkC4ICu5QsmJzbggC8F4AfrDe8Abe8J4AUW5YMS4F7qZwCW4APlgDzgicTlWTbgBeWDpwD7AeCPP+WBv+ChMeWBscDlFwDpAmA25UcA6QKg5RYghhbgAuUoxpZvZBYP4ALpAgDLAOUNgOUL4IIo4RjiGOsPduBd5UNgBgXnL8Bm5AXgOCQWBAbgAyfgBuWXcOAA5YRO4CLlAeCiX2QAxAAkAOWAm+AlReAJZeAA5YEE4Ih85WOA5QVA5QHA5QIgDyYWe+CR1OYmIOYP4AHvbOA074Bu4ALvHyDvNCdGT6f7AOYAL8bvFmbvNeAN7zpGD+CAEusM4ATvT+AB6xHgf+ES4hLhEsIA4grhEuISAQAhIAEgISBhAOEAYgACAMIA4gPhEuISIQBhIOEAAMEA4hIhAGEAgQABQMEA4hLhEuIS4RLiEuES4hLhEuIS4RLiEuES4hQg4REM4hEMouERDOIRDKLhEQziEQyi4REM4hEMouERDOIRDKI/IOkq74F45i9v5irvAAbvBgYvluAHhgDmB+CDyOICBeIM4IBZxgDmCSDGACYAhuCATeUlQMbEIOkCYAUP4IC45RYG4AnlJGbpAoAN4IRYxQBlACUA5QcA5YA9IOsBxuAh4RriGsYEYOkCYDbggonrMw9LDWvgROslD+sH4IA6ZQDlEwAlAAUgBQDlAgBlAAUABaAFYAUABQAFAEUAJQAFIAUABQAFAAUABQAlAAUgZQDFAGUAZQAFAOUCAOUJgEUAhQDlCeAsLOCAhu8kYO9c4ATvByDvBwDvBwDvHeAC6wXvgBngMO8V4AXvJGDvAcAv4Aav4IAS74Bzju+CUIDvCEDvBUDvbOAE71HA7wRgD+AH7wRg7zDgAO8CoO8g4ADvFiAv4EbvgMzgBO8GII9Aj0DP4AHvFUDvA4Cv4ALvAqDvAOAAz+AB74ALAO8v4B3pAuCDfuXAZljgGOWPscDlgFYg5ZX64AblnKngi5flgZbghVrlksPgyqwuG+AW+1jgeOaAaODAvYj9wL92IP3Av3YgAAAA9SsAAHoUAAD8BQBBoOIDC8YBYPIAAIDyAABQ8wAAAPUAADv1AABQ9QAAoPUAAMD1AADL9QAA4PUAAECBAAAA9gAAIPYAAED2AABg9gAAkPYAAFD4AABV+AAAYPgAAKD4AADA+AAAUPoAAKz6AAC4+gAAvfoAAND6AAAS+wAAFvsAADD7AACA+wAAuvsAAND7AADv+wAA+PsAAAD8AADQ/AAAIP0AACD+AABK/gAAYP4AAID+AAAw/wAAIAABADwAAQBAAAEAkAABADABAQDQAQEAkHwAAHB5AEHw4wMLZBwAyAChATsADwBBACAACwAMABMAgAIfABcAFgAhAMABBQAKADcAFwCHAVwADAAFAAQAQgAEAA8ARwA6AAsAHwAJAAQAwgBKAPYAKgANABYArQDvABwABABHAJEAnAAzADcE0AIAQeDkAwuRBayA/oBE24BSeoBICIFOBIBC4oBgzWaAQKiA1oAAAAAA3YBDcBGAmQmBXB+AmoKKgJ+Dl4GNgcCMGBEckQMBiQAUKBEJAgUTJMohGAgIACELC5EJAAYAKUEhg0CnCICXgJCAQbyBi4gkIQkUjQABhZeBuACAnIOIgUFVgZ6JQZKVvoOfgWDUYgADgEDSAIBg1MDUgMYBCAkLgIsABoDAAw8GgJsDBAAWgEFTgZiAmICegJiAnoCYgJ6AmICegJgHgbFV/xiaAQAIgIkDAAAoGAAAAgEACAAAAAABAAsGAwMAgImAkCIEgJAAAAAAAAAAAENEgEJpjQABAQDHiq+MBo+A5DMZC4CigJ2P5YrkCogCA0CmixaFk7UJjgEiiYGcgrkxCYGJgImBnIK5IwkLgJ0KgIqCuTgQgZSBlROCuTEJgYiBiYGdgLoiEIKJgKeDuTAQF4GKgZyCuTAQF4GKgZuDuTAQgomAiYGcgsooAIeRgbwBhpGA4gEogY+AQKKQioqAo+2LAAuWGxARMoOMiwCJg0ZzgZ2BnYGdgcGSQLuBoYD1i4OIQN2EuImBk8mBioKwhK+Ou4KdiAm4irGSQa+NRsCzSPWfYHhzh6GBQWEHgJaE14GxjwC4gKWEm4usg6+LpIDCjYsHgayCsQARDICrJIBA7IdgTzKASFaERoUQDINDE4NBgoFBUoK0jayBjICsiIiAvIKji5GBuIKvjI2B24gIKECfiZaDuTEJgYmAiYFA0IwC6ZFA7DGGnIHRjgDpiuaNQQCMQPYoCQoAgECNMSuAm4mpIIORiq2NQZY4htKVgI35KgAIEAKAwSAIg0Fbg2BQVwC2M9yBYEyrgGAjYDCQDgEESRuAR+eZhZmFmQBBgOoDC5EBQKmAjoBB9IgxnYTfgLOAWbC+jIChpEKwgIyAj4xA0o9DT5lHkYFgeh2BQNGAQIaBQ2GDYFwfARCpgIhgIV+PQ0WZYcxfmYWZhZkAAAAAAABJvYCXgEFlgJeA5YCXgEDpgJGB5oCXgPaAjoBNVIBE1YBQIIFgz22BU52Al4BBV4CLgEDwgEN/gGC4MweEbC6s3wBBoOsDCzdDToBODoFGUoFIroBQ/YBgzjqAzohtAAYAnd//QO9OD1iEgUiQgJSAT2uBQLaAQs6AT+CIRmeAAEHg6wMLE0X/hUDWgLCAQX+Bz4BhB9mAjoAAQYDsAws3Q3mASreA/oBgIeaBYMvAhUGVgfMAAAAAAAAAgEEegQBDeYBgLR+BYMvAhUGVgfMAAAAAAAAAgABBwOwDCxZBwwgIgaSBTtyqCk6HPz+Hi4COgK6AAEHg7AMLIUDegM+Al4BEPIBZEYBA5D8/h4kRBQIRgKkRgGDbB4aLhABBkO0DC4cEQJ8GAAEAARIQgp+AzwGAiweA+wEBgKWAQLuInimE2giBiYCjBAIECIDJgpyAQZOAQJOA14NC3of7CIDSAYChEYBA/IFC1ID+gKeBrYC1gIgDAwOAi4CIACaAkICIAwMDgIuAQUGA4YFGUoHUhEUbEIqAkYCbjIChpEDZgEDVAAAAAAAAAT8/h4kRBAApBBKAiBKAiBERBAiPACCLEioICwAHgowGkoGagIyKgNYYEIoBDAoAEBECBgUchY+Pj4iAQKEIgUD3gUE01ZmaRSCA5oLkgEGegUDwgEEugNKAi0DVqYC0AILfCYDegLDdgo3fnoCnh66AQX9gcpuBQNGAQIASgUNhg4iAYE2VQQ0IAIGJAAAJgsOB6aWGiyQAlwQAAQGA66BBapG/gbWnjIKZlZSBi4CSAxoAgECGCICfmUCDFQ0NChYGgIhHhyCpgIhgtOSDVLmGjYe/hUI+1IDGAQgJC4CLAAaAwAMPBoCbAwQAFoBBU4FBI4GxVf8YmgEACICJAwAAKBgAAAIBAAgAAAAAAQALBgMDAICJgJAiBICQQkOKhJ6An5mCooDugoyrg4gxSZ2JYPwFQh1rBeFP/6+JNZmFRhuAWfCBmYS2gwCsgEVbgLKATkCARASASAiFvICmgI6AQYWATAMBgJ4LgJuAQb2AkoDugGDNj4GkgImAQKiAT56AAEGg8QMLF0FIgEUogEkCAIBIKIFIxIVCuIFt3NWAAEHA8QMLhwPdAIDGBQMBgUH2QJ4HJZALgIiBQPyEQNCAtpCAmgABAECFO4FAhQsKgsKa2oq5iqGB/YeoiY+bvICPAoObgMmAj4DtgI+A7YCPgK6Cu4CPBoD2gO2Aj4DtgI+A7IGPgPuA+yiA6oCMhMqBmgAAA4HBEIG9gO8AgacLhJgwgImBQsCCQ7OBQLKKiIBBWoJBODmAr46BiueAjoCliLWBQImBv4XRmBgoCrG+2IukikG8AIKKgoyCjIKMgUzvgkE8gEH5heiD3oBgdXGAiwiAm4HRgY2h5YLsgUDJgJqRuIOjgN6Ai4CjgECUgsCDsoDjhIiC/4FgTy+AQwCPQQ0AgK6ArIHCgEL7gESeKKmAiEMpgUI6hUIdirCDQL+AqIDHgfeBvYDLgIiC54FAsYHQgI+AlzKEQMwCgPqBQPqB/YD1gfKAQQyBQQELgECbgNKAkYDQgEGkgEEBAIHQgGBNV4S6hkRXkM+BYD/9GDCBXwCtgZZCHxIvOYadg0+BhkF2gLyDRd+G7BCCAEHQ9AMLcUC2gEIXgUNtgEG4gENZgELvgP6ASUKAt4BCYoBBjYDDgFOIgKqE5oHcgmBvFYBF9YBDwYCVgECIgOuAlIFgVHqASA+BS9mAQmeCRM6AYFCogUSbCIBgcVeBSAWCr4k1mYVg/qiJNZmFYC/vCYdgL/GBAEHQ9QMLVWAwBYGYiI2CQ8RZv79gUf9gWP9BbYHpYHUJgJpX94dE1amIYCRmQYtgTQNgpt+fUDiGQN2BVoGNXTBMHkIdReFTSmAgC4FOP4T6hErvEYBgkPkJAIEAQbD2AwtHYP3Pn0INgWD//YFg//2BYP/9gWD//YFg//2BYP/9gWD//YFg//2BYP/9gWD//YFg//2BYP/9gWD//YFg//2BYP/9gWD//YEAQYD3AwtFoI6JhpkYgJmDoTAACAALAwKAloCegF8Xl4eOgZKAiUEwQs9An0J1nURrQf//QYATmI6AYM0MgUEEgYiEkYDjgF+HgZeBAEHQ9wML8gGhA4BAgoCOgF9bh5iBTgaAQciDjIJgziCDQLwDgNmBYC5/mYDYi0DVYfHlmQAAAACggIuAj4BFSIBAkoJAs4CqgkD1gLwAAoFBJIFG44FDFQOBQwSAQMWBQMsEgEE5gUFhg0CtCYGcgUC7gcCBQ7uBiIJN44CMgJWBQayAYHT7gEENgUDiAoBBfYHVgd6AQJeBQJKCQI+BQPiAYFJlAoFAqICLgI+AwIBK84FE/ISrg0C8gfSD/oJAgA2Aj4HXCIHrgEGggUF0DI7ogUD4gkIEAIBA+oHWgUGjgUKzgWBLdIFAhIDAgYqAQ1KAYE4FgF3ngABB0PkDC8YC6IFAw4BBGICdgLOAk4BBP4DhAIBZCICygIwCgECDgECcgEGkgEDVgUsxgGGnpIGxgbGBsYGxgbGBsYGxgbGBsYGxgbGBsYFIhYAAAAAAAACggIkAgIoKgEM9B4BCAIC4gMeAjQCCQLOAqooAQOqBtY6egEEEgUTzgUCrA4VBNoFDFIdDBID7gsaBQJwSgKYZgUE5gUFhg0CtCIKcgUC7hL2BQ7uBiIJN44CMA4CJAAqBQauBYHT6gUEMgkDihEF9gdWB3oBAloJAkoL+gI+BQPiAYFJjEINAqICJAICKCoDAAYBEOYCvgESFgEDGgEE1gUCXhcOF2INDt4Srg0C8hu+D/oJAgA2Aj4HXhOuAQaCCi4FBZRqO6IFA+IJCBACAQPqB1guBQZ2CrIBChIFFdoRgRfiBQISAwIKJgENRgWBOBYBd5oMAQaD8Aws3YDP/Wb+/YFH/YFoNCACBiQAACYJhBdVgpt+fUDiGQN2BVoGNXTBUHlNKWAoQgGDl749tAu9A7wBB4PwDCxaIhJGA44CZgFXegEl+ipwMgK6AT5+AAEGA/QMLhwSngZEAgJsAgJwAgKyAjoBOfYNHXIFJm4GJgbWBjYFAsIBAvxoqAgoYGAADiCCAkSOICAA5ngsgiAmSIYghC5eBjzuTDoFEPI3JARgIFBwSjUGSlQ2AjTg1EBwBDBgCCYkpgYuSAwgACAMhKpeBigsYCQuqD4CnIAAUIhgUAED/gEICGgiBjQmJqodBqokPYM48LIFAoYGRAICbAICcAAAIgWDXdoC4gLiAuIC4gAAAAKIFBInuA4BfjICLgEDXgJWA2YWOgUFugYuAQKWAmIoaQMaAQOaBiYCIgLkYhIgBAQkDAQAJAgIPFAAEi4oJAAiAkQGBkSgACgwBC4GKDAkECACBkwwoGQMBASgBAAAFAgWAiYGOAQMAAxCAioGvgoiAjYCNgEFzgUHOgpKBsgOARNmAi4BCWACAYb1pgEDJgECfgYuBjQGJypkBloCTAYiUgUCtoYHvCQKB0gqAQQaAvooolzEPiwEZA4GMCQeBiASCixcRAAMFAgXVr8UnCoSIEAEQgYlA4osYQRqugImAQLjvIiKGiJyCiiWJiS8+AKIFBIlf0oBA1IBg3SqAYPPVmUH6hEWvg2wGa99h8/qEYCYcgEDagI+DYcx2gLsRAYL0CYqUkhAaAjAAl4BAyAuAlAOBQK0ShNKAj4KIgIqAQj4BBz2AiIkKt4C8CAiAkBCMQOSCqYYAQZCBBAuRAWAjGYFAzBoBgEIIgZSBsYuqgJKAjAeBkAwPBICUBggDAQYDgZuAogADEIC8gpeAjYBDWoGyA4BhxK2AQMmAQL0BicqZAJeAkwEggpSBQK2gi4iAxYCVi6oci5AQgsYAgEC6gb6MGJeRgJmBjIDV1K/FKBIKIooOiEDiixhBGq6AiYBAuO8iIoaInIKKJYmJLz4AQbCCBAvTAUCoA4BfjICLgEDXgJWA2YWOgUFugYuA3oDFgJiKGkDGgEDmgYmAiIC5GCiLgPGJ9YGKAAAoECiJgY4BAwADEICKhKyCiICNgI2AQXOBQc6CkoGyA4BE2YCLgEJYAIBhvWVA/4yCnoC7hYuBjQGJkbiajomAkwGIA4hBsYRBPYdBCa//84vUqouDt4eJhaeHndGLroCJgEG4QP9D/QAAAABArIBCoIBCy4BLQYFGUoHUhEf6hJmEsI9Q84BgzJqPQO6AQJ+AzohgvKaDVM6HbC6ET/8AQZCEBAtgT7thBWes3T8YLURU+yHpP5v2gdILc+8/GC1EVPsh+T/iZS8ifyt6PAdcFDMmpoE8vcvweogHcDwHXBQzJqaRPBgtRFT7Iek/GC1EVPsh6b/SITN/fNkCQNIhM3982QLAAEH/hAQL6BWAGC1EVPshCUAYLURU+yEJwAMAAAAEAAAABAAAAAYAAACD+aIARE5uAPwpFQDRVycA3TT1AGLbwAA8mZUAQZBDAGNR/gC73qsAt2HFADpuJADSTUIASQbgAAnqLgAcktEA6x3+ACmxHADoPqcA9TWCAES7LgCc6YQAtCZwAEF+XwDWkTkAU4M5AJz0OQCLX4QAKPm9APgfOwDe/5cAD5gFABEv7wAKWosAbR9tAM9+NgAJyycARk+3AJ5mPwAt6l8Auid1AOXrxwA9e/EA9zkHAJJSigD7a+oAH7FfAAhdjQAwA1YAe/xGAPCrawAgvM8ANvSaAOOpHQBeYZEACBvmAIWZZQCgFF8AjUBoAIDY/wAnc00ABgYxAMpWFQDJqHMAe+JgAGuMwAAZxEcAzWfDAAno3ABZgyoAi3bEAKYclgBEr90AGVfRAKU+BQAFB/8AM34/AMIy6ACYT94Au30yACY9wwAea+8An/heADUfOgB/8soA8YcdAHyQIQBqJHwA1W76ADAtdwAVO0MAtRTGAMMZnQCtxMIALE1BAAwAXQCGfUYA43EtAJvGmgAzYgAAtNJ8ALSnlwA3VdUA1z72AKMQGABNdvwAZJ0qAHDXqwBjfPgAerBXABcV5wDASVYAO9bZAKeEOAAkI8sA1op3AFpUIwAAH7kA8QobABnO3wCfMf8AZh5qAJlXYQCs+0cAfn/YACJltwAy6IkA5r9gAO/EzQBsNgkAXT/UABbe1wBYO94A3puSANIiKAAohugA4lhNAMbKMgAI4xYA4H3LABfAUADzHacAGOBbAC4TNACDEmIAg0gBAPWOWwCtsH8AHunyAEhKQwAQZ9MAqt3YAK5fQgBqYc4ACiikANOZtAAGpvIAXHd/AKPCgwBhPIgAinN4AK+MWgBv170ALaZjAPS/ywCNge8AJsFnAFXKRQDK2TYAKKjSAMJhjQASyXcABCYUABJGmwDEWcQAyMVEAE2ykQAAF/MA1EOtAClJ5QD91RAAAL78AB6UzABwzu4AEz71AOzxgACz58MAx/goAJMFlADBcT4ALgmzAAtF8wCIEpwAqyB7AC61nwBHksIAezIvAAxVbQByp5AAa+cfADHLlgB5FkoAQXniAPTfiQDolJcA4uaEAJkxlwCI7WsAX182ALv9DgBImrQAZ6RsAHFyQgCNXTIAnxW4ALzlCQCNMSUA93Q5ADAFHAANDAEASwhoACzuWABHqpAAdOcCAL3WJAD3faYAbkhyAJ8W7wCOlKYAtJH2ANFTUQDPCvIAIJgzAPVLfgCyY2gA3T5fAEBdAwCFiX8AVVIpADdkwABt2BAAMkgyAFtMdQBOcdQARVRuAAsJwQAq9WkAFGbVACcHnQBdBFAAtDvbAOp2xQCH+RcASWt9AB0nugCWaSkAxsysAK0UVACQ4moAiNmJACxyUAAEpL4AdweUAPMwcAAA/CcA6nGoAGbCSQBk4D0Al92DAKM/lwBDlP0ADYaMADFB3gCSOZ0A3XCMABe35wAI3zsAFTcrAFyAoABagJMAEBGSAA/o2ABsgK8A2/9LADiQDwBZGHYAYqUVAGHLuwDHibkAEEC9ANLyBABJdScA67b2ANsiuwAKFKoAiSYvAGSDdgAJOzMADpQaAFE6qgAdo8IAr+2uAFwmEgBtwk0ALXqcAMBWlwADP4MACfD2ACtAjABtMZkAObQHAAwgFQDYw1sA9ZLEAMatSwBOyqUApzfNAOapNgCrkpQA3UJoABlj3gB2jO8AaItSAPzbNwCuoasA3xUxAACuoQAM+9oAZE1mAO0FtwApZTAAV1a/AEf/OgBq+bkAdb7zACiT3wCrgDAAZoz2AATLFQD6IgYA2eQdAD2zpABXG48ANs0JAE5C6QATvqQAMyO1APCqGgBPZagA0sGlAAs/DwBbeM0AI/l2AHuLBACJF3IAxqZTAG9u4gDv6wAAm0pYAMTatwCqZroAds/PANECHQCx8S0AjJnBAMOtdwCGSNoA912gAMaA9ACs8C8A3eyaAD9cvADQ3m0AkMcfACrbtgCjJToAAK+aAK1TkwC2VwQAKS20AEuAfgDaB6cAdqoOAHtZoQAWEioA3LctAPrl/QCJ2/4Aib79AOR2bAAGqfwAPoBwAIVuFQD9h/8AKD4HAGFnMwAqGIYATb3qALPnrwCPbW4AlWc5ADG/WwCE10gAMN8WAMctQwAlYTUAyXDOADDLuAC/bP0ApACiAAVs5ABa3aAAIW9HAGIS0gC5XIQAcGFJAGtW4ACZUgEAUFU3AB7VtwAz8cQAE25fAF0w5ACFLqkAHbLDAKEyNgAIt6QA6rHUABb3IQCPaeQAJ/93AAwDgACNQC0AT82gACClmQCzotMAL10KALT5QgAR2ssAfb7QAJvbwQCrF70AyqKBAAhqXAAuVRcAJwBVAH8U8ADhB4YAFAtkAJZBjQCHvt4A2v0qAGsltgB7iTQABfP+ALm/ngBoak8ASiqoAE/EWgAt+LwA11qYAPTHlQANTY0AIDqmAKRXXwAUP7EAgDiVAMwgAQBx3YYAyd62AL9g9QBNZREAAQdrAIywrACywNAAUVVIAB77DgCVcsMAowY7AMBANQAG3HsA4EXMAE4p+gDWysgA6PNBAHxk3gCbZNgA2b4xAKSXwwB3WNQAaePFAPDaEwC6OjwARhhGAFV1XwDSvfUAbpLGAKwuXQAORO0AHD5CAGHEhwAp/ekA59bzACJ8ygBvkTUACODFAP/XjQBuauIAsP3GAJMIwQB8XXQAa62yAM1unQA+cnsAxhFqAPfPqQApc98Atcm6ALcAUQDisg0AdLokAOV9YAB02IoADRUsAIEYDAB+ZpQAASkWAJ96dgD9/b4AVkXvANl+NgDs2RMAi7q5AMSX/AAxqCcA8W7DAJTFNgDYqFYAtKi1AM/MDgASiS0Ab1c0ACxWiQCZzuMA1iC5AGteqgA+KpwAEV/MAP0LSgDh9PsAjjttAOKGLADp1IQA/LSpAO/u0QAuNckALzlhADghRAAb2cgAgfwKAPtKagAvHNgAU7SEAE6ZjABUIswAKlXcAMDG1gALGZYAGnC4AGmVZAAmWmAAP1LuAH8RDwD0tREA/Mv1ADS8LQA0vO4A6F3MAN1eYABnjpsAkjPvAMkXuABhWJsA4Ve8AFGDxgDYPhAA3XFIAC0c3QCvGKEAISxGAFnz1wDZepgAnlTAAE+G+gBWBvwA5XmuAIkiNgA4rSIAZ5PcAFXoqgCCJjgAyuebAFENpACZM7EAqdcOAGkFSABlsvAAf4inAIhMlwD50TYAIZKzAHuCSgCYzyEAQJ/cANxHVQDhdDoAZ+tCAP6d3wBe1F8Ae2ekALqsegBV9qIAK4gjAEG6VQBZbggAISqGADlHgwCJ4+YA5Z7UAEn7QAD/VukAHA/KAMVZigCU+isA08HFAA/FzwDbWq4AR8WGAIVDYgAhhjsALHmUABBhhwAqTHsAgCwaAEO/EgCIJpAAeDyJAKjE5ADl23sAxDrCACb06gD3Z4oADZK/AGWjKwA9k7EAvXwLAKRR3AAn3WMAaeHdAJqUGQCoKZUAaM4oAAnttABEnyAATpjKAHCCYwB+fCMAD7kyAKf1jgAUVucAIfEIALWdKgBvfk0ApRlRALX5qwCC39YAlt1hABY2AgDEOp8Ag6KhAHLtbQA5jXoAgripAGsyXABGJ1sAADTtANIAdwD89FUAAVlNAOBxgABB85oEC60BQPsh+T8AAAAALUR0PgAAAICYRvg8AAAAYFHMeDsAAACAgxvwOQAAAEAgJXo4AAAAgCKC4zYAAAAAHfNpNf6CK2VHFWdAAAAAAAAAOEMAAPr+Qi52vzo7nrya9wy9vf3/////3z88VFVVVVXFP5ErF89VVaU/F9CkZxERgT8AAAAAAADIQu85+v5CLuY/JMSC/72/zj+19AzXCGusP8xQRtKrsoM/hDpOm+DXVT8AQa6cBAuSEPA/br+IGk87mzw1M/upPfbvP13c2JwTYHG8YYB3Pprs7z/RZocQel6QvIV/bugV4+8/E/ZnNVLSjDx0hRXTsNnvP/qO+SOAzou83vbdKWvQ7z9hyOZhTvdgPMibdRhFx+8/mdMzW+SjkDyD88bKPr7vP217g12mmpc8D4n5bFi17z/87/2SGrWOPPdHciuSrO8/0ZwvcD2+Pjyi0dMy7KPvPwtukIk0A2q8G9P+r2ab7z8OvS8qUlaVvFFbEtABk+8/VepOjO+AULzMMWzAvYrvPxb01bkjyZG84C2prpqC7z+vVVzp49OAPFGOpciYeu8/SJOl6hUbgLx7UX08uHLvPz0y3lXwH4+86o2MOPlq7z+/UxM/jImLPHXLb+tbY+8/JusRdpzZlrzUXASE4FvvP2AvOj737Jo8qrloMYdU7z+dOIbLguePvB3Z/CJQTe8/jcOmREFvijzWjGKIO0bvP30E5LAFeoA8ltx9kUk/7z+UqKjj/Y6WPDhidW56OO8/fUh08hhehzw/prJPzjHvP/LnH5grR4A83XziZUUr7z9eCHE/e7iWvIFj9eHfJO8/MasJbeH3gjzh3h/1nR7vP/q/bxqbIT28kNna0H8Y7z+0CgxygjeLPAsD5KaFEu8/j8vOiZIUbjxWLz6prwzvP7arsE11TYM8FbcxCv4G7z9MdKziAUKGPDHYTPxwAe8/SvjTXTndjzz/FmSyCPzuPwRbjjuAo4a88Z+SX8X27j9oUEvM7UqSvMupOjen8e4/ji1RG/gHmbxm2AVtruzuP9I2lD7o0XG895/lNNvn7j8VG86zGRmZvOWoE8Mt4+4/bUwqp0ifhTwiNBJMpt7uP4ppKHpgEpO8HICsBEXa7j9biRdIj6dYvCou9yEK1u4/G5pJZ5ssfLyXqFDZ9dHuPxGswmDtY0M8LYlhYAjO7j/vZAY7CWaWPFcAHe1Byu4/eQOh2uHMbjzQPMG1osbuPzASDz+O/5M83tPX8CrD7j+wr3q7zpB2PCcqNtXav+4/d+BU670dkzwN3f2ZsrzuP46jcQA0lI+8pyyddrK57j9Jo5PczN6HvEJmz6Latu4/XzgPvcbeeLyCT51WK7TuP/Zce+xGEoa8D5JdyqSx7j+O1/0YBTWTPNontTZHr+4/BZuKL7eYezz9x5fUEq3uPwlUHOLhY5A8KVRI3Qer7j/qxhlQhcc0PLdGWYomqe4/NcBkK+YylDxIIa0Vb6fuP592mWFK5Iy8Cdx2ueGl7j+oTe87xTOMvIVVOrB+pO4/rukriXhThLwgw8w0RqPuP1hYVnjdzpO8JSJVgjii7j9kGX6AqhBXPHOpTNRVoe4/KCJev++zk7zNO39mnqDuP4K5NIetEmq8v9oLdRKg7j/uqW2472djvC8aZTyyn+4/UYjgVD3cgLyElFH5fZ/uP88+Wn5kH3i8dF/s6HWf7j+wfYvASu6GvHSBpUian+4/iuZVHjIZhrzJZ0JW65/uP9PUCV7LnJA8P13eT2mg7j8dpU253DJ7vIcB63MUoe4/a8BnVP3slDwywTAB7aHuP1Vs1qvh62U8Yk7PNvOi7j9Cz7MvxaGIvBIaPlQnpO4/NDc78bZpk7wTzkyZiaXuPx7/GTqEXoC8rccjRhqn7j9uV3LYUNSUvO2SRJvZqO4/AIoOW2etkDyZZorZx6ruP7Tq8MEvt40826AqQuWs7j//58WcYLZlvIxEtRYyr+4/RF/zWYP2ezw2dxWZrrHuP4M9HqcfCZO8xv+RC1u07j8pHmyLuKldvOXFzbA3t+4/WbmQfPkjbLwPUsjLRLruP6r59CJDQ5K8UE7en4K97j9LjmbXbMqFvLoHynDxwO4/J86RK/yvcTyQ8KOCkcTuP7tzCuE10m08IyPjGWPI7j9jImIiBMWHvGXlXXtmzO4/1THi44YcizwzLUrsm9DuPxW7vNPRu5G8XSU+sgPV7j/SMe6cMcyQPFizMBOe2e4/s1pzboRphDy//XlVa97uP7SdjpfN34K8evPTv2vj7j+HM8uSdxqMPK3TWpmf6O4/+tnRSo97kLxmto0pB+7uP7qu3FbZw1W8+xVPuKLz7j9A9qY9DqSQvDpZ5Y1y+e4/NJOtOPTWaLxHXvvydv/uPzWKWGvi7pG8SgahMLAF7z/N3V8K1/90PNLBS5AeDO8/rJiS+vu9kbwJHtdbwhLvP7MMrzCubnM8nFKF3ZsZ7z+U/Z9cMuOOPHrQ/1+rIO8/rFkJ0Y/ghDxL0Vcu8SfvP2caTjivzWM8tecGlG0v7z9oGZJsLGtnPGmQ79wgN+8/0rXMgxiKgLz6w11VCz/vP2/6/z9drY+8fIkHSi1H7z9JqXU4rg2QvPKJDQiHT+8/pwc9poWjdDyHpPvcGFjvPw8iQCCekYK8mIPJFuNg7z+sksHVUFqOPIUy2wPmae8/S2sBrFk6hDxgtAHzIXPvPx8+tAch1YK8X5t7M5d87z/JDUc7uSqJvCmh9RRGhu8/04g6YAS2dDz2P4vnLpDvP3FynVHsxYM8g0zH+1Ga7z/wkdOPEvePvNqQpKKvpO8/fXQj4piujbzxZ44tSK/vPwggqkG8w448J1ph7hu67z8y66nDlCuEPJe6azcrxe8/7oXRMalkijxARW5bdtDvP+3jO+S6N468FL6crf3b7z+dzZFNO4l3PNiQnoHB5+8/icxgQcEFUzzxcY8rwvPvPwAAAAAAAPA/AAAAAAAA+D8AAAAAAAAAAAbQz0Pr/Uw+AEHLrAQLlgFAA7jiP9F0ngBXnb0qgHBSD///PicKAAAAZAAAAOgDAAAQJwAAoIYBAEBCDwCAlpgAAOH1BRgAAAA1AAAAcQAAAGv////O+///kr///wAAAAAAAAAAGQAKABkZGQAAAAAFAAAAAAAACQAAAAALAAAAAAAAAAAZABEKGRkZAwoHAAEACQsYAAAJBgsAAAsABhkAAAAZGRkAQfGtBAshDgAAAAAAAAAAGQAKDRkZGQANAAACAAkOAAAACQAOAAAOAEGrrgQLAQwAQbeuBAsVEwAAAAATAAAAAAkMAAAAAAAMAAAMAEHlrgQLARAAQfGuBAsVDwAAAAQPAAAAAAkQAAAAAAAQAAAQAEGfrwQLARIAQauvBAseEQAAAAARAAAAAAkSAAAAAAASAAASAAAaAAAAGhoaAEHirwQLDhoAAAAaGhoAAAAAAAAJAEGTsAQLARQAQZ+wBAsVFwAAAAAXAAAAAAkUAAAAAAAUAAAUAEHNsAQLARYAQdmwBAsnFQAAAAAVAAAAAAkWAAAAAAAWAAAWAAAwMTIzNDU2Nzg5QUJDREVGAEGksQQLAncBAEHMsQQLCP//////////AEGQsgQLAQUAQZyyBAsCcgEAQbSyBAsOcwEAAHQBAACYGgEAAAQAQcyyBAsBAQBB3LIECwX/////CgBBoLMECwcQGQEAkCBR\";if(!T(U)){var ha=U;U=c.locateFile?c.locateFile(ha,t):t+ha;}function ia(){var a=U;try{if(a==U&&v)return new Uint8Array(v);if(T(a))try{var b=ja(a.slice(37)),d=new Uint8Array(b.length);for(a=0;a<b.length;++a)d[a]=b.charCodeAt(a);var e=d;}catch(g){throw Error(\"Converting base64 string to bytes failed.\");}else e=void 0;var f=e;if(f)return f;throw\"both async and sync fetching of the wasm failed\";}catch(g){w(g);}}function ka(){return v||\"function\"!=typeof fetch?Promise.resolve().then(function(){return ia();}):fetch(U,{credentials:\"same-origin\"}).then(function(a){if(!a.ok)throw\"failed to load wasm binary file at '\"+U+\"'\";return a.arrayBuffer();}).catch(function(){return ia();});}function V(a){for(;0<a.length;){var b=a.shift();if(\"function\"==typeof b)b(c);else{var d=b.C;\"number\"==typeof d?void 0===b.A?O.get(d)():O.get(d)(b.A):d(void 0===b.A?null:b.A);}}}function la(a,b,d){function e(q){return(q=q.toTimeString().match(/\\(([A-Za-z ]+)\\)$/))?q[1]:\"GMT\";}var f=new Date().getFullYear(),g=new Date(f,0,1),k=new Date(f,6,1);f=g.getTimezoneOffset();var m=k.getTimezoneOffset();N[a>>2]=60*Math.max(f,m);N[b>>2]=Number(f!=m);a=e(g);b=e(k);a=K(a);b=K(b);m<f?(N[d>>2]=a,N[d+4>>2]=b):(N[d>>2]=b,N[d+4>>2]=a);}function W(a,b,d){W.B||(W.B=!0,la(a,b,d));}function X(a){var b=J(a)+1,d=L(b);B(a,C,d,b);return d;}function ma(){}var na=[null,[],[]];ma=(a,b,d)=>{a=G(a);b=null!==b?JSON.parse(G(b)):[];try{const e=c.externalCall(a,b);return e?X(e):null;}catch(e){return c.HEAPU8[d]=1,X(e.message);}};var ja=\"function\"==typeof atob?atob:function(a){var b=\"\",d=0;a=a.replace(/[^A-Za-z0-9\\+\\/=]/g,\"\");do{var e=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".indexOf(a.charAt(d++));var f=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".indexOf(a.charAt(d++));var g=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".indexOf(a.charAt(d++));var k=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".indexOf(a.charAt(d++));e=e<<2|f>>4;f=(f&15)<<4|g>>2;var m=(g&3)<<6|k;b+=String.fromCharCode(e);64!==g&&(b+=String.fromCharCode(f));64!==k&&(b+=String.fromCharCode(m));}while(d<a.length);return b;},oa={a:function(a,b,d,e){w(\"Assertion failed: \"+G(a)+\", at: \"+[b?G(b):\"unknown filename\",d,e?G(e):\"unknown function\"]);},d:function(){return Date.now();},e:function(a,b){a=new Date(1E3*N[a>>2]);N[b>>2]=a.getSeconds();N[b+4>>2]=a.getMinutes();N[b+8>>2]=a.getHours();N[b+12>>2]=a.getDate();N[b+16>>2]=a.getMonth();N[b+20>>2]=a.getFullYear()-1900;N[b+24>>2]=a.getDay();var d=new Date(a.getFullYear(),0,1);N[b+28>>2]=(a.getTime()-d.getTime())/864E5|0;N[b+36>>2]=-(60*a.getTimezoneOffset());var e=new Date(a.getFullYear(),6,1).getTimezoneOffset();d=d.getTimezoneOffset();N[b+32>>2]=(e!=d&&a.getTimezoneOffset()==Math.min(d,e))|0;},f:W,b:function(){w(\"\");},h:ma,g:function(a,b){a=G(a);let d;try{d=window.JSON.parse(a);}catch(e){d=a;}0!==b?window.alert(a):window.console.log(\"DUMP\",d);},j:function(a){var b=C.length;a>>>=0;if(2147483648<a)return!1;for(var d=1;4>=d;d*=2){var e=b*(1+.2/d);e=Math.min(e,a+100663296);var f=Math;e=Math.max(a,e);f=f.min.call(f,2147483648,e+(65536-e%65536)%65536);a:{try{x.grow(f-M.byteLength+65535>>>16);ba();var g=1;break a;}catch(k){}g=void 0;}if(g)return!0;}return!1;},c:function(a,b,d,e){for(var f=0,g=0;g<d;g++){var k=N[b>>2],m=N[b+4>>2];b+=8;for(var q=0;q<m;q++){var l=C[k+q],p=na[a];0===l||10===l?((1===a?aa:u)(I(p,0)),p.length=0):p.push(l);}f+=m;}N[e>>2]=f;return 0;},k:function(a){a=G(a);window.console.log(a);},i:function(a){a=G(a);return Date.parse(a);},l:function(a,b,d,e){a=G(a);b=G(b);d=G(d);d=`Quickjs -- ${a}: ${b}\\n${d}`;0!==e?window.alert(d):window.console.error(d);}};(function(){function a(f){c.asm=f.exports;x=c.asm.m;ba();O=c.asm.v;da.unshift(c.asm.n);P--;c.monitorRunDependencies&&c.monitorRunDependencies(P);0==P&&(null!==Q&&(clearInterval(Q),Q=null),R&&(f=R,R=null,f()));}function b(f){a(f.instance);}function d(f){return ka().then(function(g){return WebAssembly.instantiate(g,e);}).then(function(g){return g;}).then(f,function(g){u(\"failed to asynchronously prepare wasm: \"+g);w(g);});}var e={a:oa};P++;c.monitorRunDependencies&&c.monitorRunDependencies(P);if(c.instantiateWasm)try{return c.instantiateWasm(e,a);}catch(f){return u(\"Module.instantiateWasm callback failed with error: \"+f),!1;}(function(){return v||\"function\"!=typeof WebAssembly.instantiateStreaming||T(U)||\"function\"!=typeof fetch?d(b):fetch(U,{credentials:\"same-origin\"}).then(function(f){return WebAssembly.instantiateStreaming(f,e).then(b,function(g){u(\"wasm streaming compile failed: \"+g);u(\"falling back to ArrayBuffer instantiation\");return d(b);});});})().catch(n);return{};})();c.___wasm_call_ctors=function(){return(c.___wasm_call_ctors=c.asm.n).apply(null,arguments);};c._evalInSandbox=function(){return(c._evalInSandbox=c.asm.o).apply(null,arguments);};c._nukeSandbox=function(){return(c._nukeSandbox=c.asm.p).apply(null,arguments);};c._init=function(){return(c._init=c.asm.q).apply(null,arguments);};c._commFun=function(){return(c._commFun=c.asm.r).apply(null,arguments);};c._dumpMemoryUse=function(){return(c._dumpMemoryUse=c.asm.s).apply(null,arguments);};var L=c._malloc=function(){return(L=c._malloc=c.asm.t).apply(null,arguments);};c._free=function(){return(c._free=c.asm.u).apply(null,arguments);};var E=c.stackSave=function(){return(E=c.stackSave=c.asm.w).apply(null,arguments);},F=c.stackRestore=function(){return(F=c.stackRestore=c.asm.x).apply(null,arguments);},A=c.stackAlloc=function(){return(A=c.stackAlloc=c.asm.y).apply(null,arguments);};c.ccall=z;c.cwrap=function(a,b,d,e){d=d||[];var f=d.every(function(g){return\"number\"===g;});return\"string\"!==b&&f&&!e?c[\"_\"+a]:function(){return z(a,b,d,arguments,e);};};c.stringToNewUTF8=X;var Y;R=function pa(){Y||Z();Y||(R=pa);};function Z(){function a(){if(!Y&&(Y=!0,c.calledRun=!0,!y)){V(da);h(c);if(c.onRuntimeInitialized)c.onRuntimeInitialized();if(c.postRun)for(\"function\"==typeof c.postRun&&(c.postRun=[c.postRun]);c.postRun.length;){var b=c.postRun.shift();ea.unshift(b);}V(ea);}}if(!(0<P)){if(c.preRun)for(\"function\"==typeof c.preRun&&(c.preRun=[c.preRun]);c.preRun.length;)fa();V(ca);0<P||(c.setStatus?(c.setStatus(\"Running...\"),setTimeout(function(){setTimeout(function(){c.setStatus(\"\");},1);a();},1)):a());}}c.run=Z;if(c.preInit)for(\"function\"==typeof c.preInit&&(c.preInit=[c.preInit]);0<c.preInit.length;)c.preInit.pop()();Z();return Module.ready;};})();/* harmony default export */ const quickjs_eval = (Module);\n;// CONCATENATED MODULE: ./src/pdf.sandbox.external.js\nclass SandboxSupportBase {\n  constructor(win) {\n    this.win = win;\n    this.timeoutIds = new Map();\n    this.commFun = null;\n  }\n  destroy() {\n    this.commFun = null;\n    for (const id of this.timeoutIds.values()) {\n      this.win.clearTimeout(id);\n    }\n    this.timeoutIds = null;\n  }\n  exportValueToSandbox(val) {\n    throw new Error(\"Not implemented\");\n  }\n  importValueFromSandbox(val) {\n    throw new Error(\"Not implemented\");\n  }\n  createErrorForSandbox(errorMessage) {\n    throw new Error(\"Not implemented\");\n  }\n  callSandboxFunction(name, args) {\n    try {\n      args = this.exportValueToSandbox(args);\n      this.commFun(name, args);\n    } catch (e) {\n      this.win.console.error(e);\n    }\n  }\n  createSandboxExternals() {\n    const externals = {\n      setTimeout: (callbackId, nMilliseconds) => {\n        if (typeof callbackId !== \"number\" || typeof nMilliseconds !== \"number\") {\n          return;\n        }\n        if (callbackId === 0) {\n          this.win.clearTimeout(this.timeoutIds.get(callbackId));\n        }\n        const id = this.win.setTimeout(() => {\n          this.timeoutIds.delete(callbackId);\n          this.callSandboxFunction(\"timeoutCb\", {\n            callbackId,\n            interval: false\n          });\n        }, nMilliseconds);\n        this.timeoutIds.set(callbackId, id);\n      },\n      clearTimeout: callbackId => {\n        this.win.clearTimeout(this.timeoutIds.get(callbackId));\n        this.timeoutIds.delete(callbackId);\n      },\n      setInterval: (callbackId, nMilliseconds) => {\n        if (typeof callbackId !== \"number\" || typeof nMilliseconds !== \"number\") {\n          return;\n        }\n        const id = this.win.setInterval(() => {\n          this.callSandboxFunction(\"timeoutCb\", {\n            callbackId,\n            interval: true\n          });\n        }, nMilliseconds);\n        this.timeoutIds.set(callbackId, id);\n      },\n      clearInterval: callbackId => {\n        this.win.clearInterval(this.timeoutIds.get(callbackId));\n        this.timeoutIds.delete(callbackId);\n      },\n      alert: cMsg => {\n        if (typeof cMsg !== \"string\") {\n          return;\n        }\n        this.win.alert(cMsg);\n      },\n      confirm: cMsg => {\n        if (typeof cMsg !== \"string\") {\n          return false;\n        }\n        return this.win.confirm(cMsg);\n      },\n      prompt: (cQuestion, cDefault) => {\n        if (typeof cQuestion !== \"string\" || typeof cDefault !== \"string\") {\n          return null;\n        }\n        return this.win.prompt(cQuestion, cDefault);\n      },\n      parseURL: cUrl => {\n        const url = new this.win.URL(cUrl);\n        const props = [\"hash\", \"host\", \"hostname\", \"href\", \"origin\", \"password\", \"pathname\", \"port\", \"protocol\", \"search\", \"searchParams\", \"username\"];\n        return Object.fromEntries(props.map(name => [name, url[name].toString()]));\n      },\n      send: data => {\n        if (!data) {\n          return;\n        }\n        const event = new this.win.CustomEvent(\"updatefromsandbox\", {\n          detail: this.importValueFromSandbox(data)\n        });\n        this.win.dispatchEvent(event);\n      }\n    };\n    Object.setPrototypeOf(externals, null);\n    return (name, args) => {\n      try {\n        const result = externals[name](...args);\n        return this.exportValueToSandbox(result);\n      } catch (error) {\n        throw this.createErrorForSandbox(error?.toString() ?? \"\");\n      }\n    };\n  }\n}\n;// CONCATENATED MODULE: ./src/pdf.sandbox.js\n\n\nconst pdfjsVersion = '4.0.269';\nconst pdfjsBuild = 'f4b396f6c';\nclass SandboxSupport extends SandboxSupportBase {\n  exportValueToSandbox(val) {\n    return JSON.stringify(val);\n  }\n  importValueFromSandbox(val) {\n    return val;\n  }\n  createErrorForSandbox(errorMessage) {\n    return new Error(errorMessage);\n  }\n}\nclass Sandbox {\n  constructor(win, module) {\n    this.support = new SandboxSupport(win, this);\n    module.externalCall = this.support.createSandboxExternals();\n    this._module = module;\n    this._alertOnError = 0;\n  }\n  create(data) {\n    const code = ['var __webpack_exports__ = globalThis.pdfjsSandbox = {};\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/constants.js\\nconst Border = Object.freeze({\\n  s: \"solid\",\\n  d: \"dashed\",\\n  b: \"beveled\",\\n  i: \"inset\",\\n  u: \"underline\"\\n});\\nconst Cursor = Object.freeze({\\n  visible: 0,\\n  hidden: 1,\\n  delay: 2\\n});\\nconst Display = Object.freeze({\\n  visible: 0,\\n  hidden: 1,\\n  noPrint: 2,\\n  noView: 3\\n});\\nconst Font = Object.freeze({\\n  Times: \"Times-Roman\",\\n  TimesB: \"Times-Bold\",\\n  TimesI: \"Times-Italic\",\\n  TimesBI: \"Times-BoldItalic\",\\n  Helv: \"Helvetica\",\\n  HelvB: \"Helvetica-Bold\",\\n  HelvI: \"Helvetica-Oblique\",\\n  HelvBI: \"Helvetica-BoldOblique\",\\n  Cour: \"Courier\",\\n  CourB: \"Courier-Bold\",\\n  CourI: \"Courier-Oblique\",\\n  CourBI: \"Courier-BoldOblique\",\\n  Symbol: \"Symbol\",\\n  ZapfD: \"ZapfDingbats\",\\n  KaGo: \"HeiseiKakuGo-W5-UniJIS-UCS2-H\",\\n  KaMi: \"HeiseiMin-W3-UniJIS-UCS2-H\"\\n});\\nconst Highlight = Object.freeze({\\n  n: \"none\",\\n  i: \"invert\",\\n  p: \"push\",\\n  o: \"outline\"\\n});\\nconst Position = Object.freeze({\\n  textOnly: 0,\\n  iconOnly: 1,\\n  iconTextV: 2,\\n  textIconV: 3,\\n  iconTextH: 4,\\n  textIconH: 5,\\n  overlay: 6\\n});\\nconst ScaleHow = Object.freeze({\\n  proportional: 0,\\n  anamorphic: 1\\n});\\nconst ScaleWhen = Object.freeze({\\n  always: 0,\\n  never: 1,\\n  tooBig: 2,\\n  tooSmall: 3\\n});\\nconst Style = Object.freeze({\\n  ch: \"check\",\\n  cr: \"cross\",\\n  di: \"diamond\",\\n  ci: \"circle\",\\n  st: \"star\",\\n  sq: \"square\"\\n});\\nconst Trans = Object.freeze({\\n  blindsH: \"BlindsHorizontal\",\\n  blindsV: \"BlindsVertical\",\\n  boxI: \"BoxIn\",\\n  boxO: \"BoxOut\",\\n  dissolve: \"Dissolve\",\\n  glitterD: \"GlitterDown\",\\n  glitterR: \"GlitterRight\",\\n  glitterRD: \"GlitterRightDown\",\\n  random: \"Random\",\\n  replace: \"Replace\",\\n  splitHI: \"SplitHorizontalIn\",\\n  splitHO: \"SplitHorizontalOut\",\\n  splitVI: \"SplitVerticalIn\",\\n  splitVO: \"SplitVerticalOut\",\\n  wipeD: \"WipeDown\",\\n  wipeL: \"WipeLeft\",\\n  wipeR: \"WipeRight\",\\n  wipeU: \"WipeUp\"\\n});\\nconst ZoomType = Object.freeze({\\n  none: \"NoVary\",\\n  fitP: \"FitPage\",\\n  fitW: \"FitWidth\",\\n  fitH: \"FitHeight\",\\n  fitV: \"FitVisibleWidth\",\\n  pref: \"Preferred\",\\n  refW: \"ReflowWidth\"\\n});\\nconst GlobalConstants = Object.freeze({\\n  IDS_GREATER_THAN: \"Invalid value: must be greater than or equal to % s.\",\\n  IDS_GT_AND_LT: \"Invalid value: must be greater than or equal to % s \" + \"and less than or equal to % s.\",\\n  IDS_LESS_THAN: \"Invalid value: must be less than or equal to % s.\",\\n  IDS_INVALID_MONTH: \"** Invalid **\",\\n  IDS_INVALID_DATE: \"Invalid date / time: please ensure that the date / time exists.Field\",\\n  IDS_INVALID_DATE2: \" should match format \",\\n  IDS_INVALID_VALUE: \"The value entered does not match the format of the field\",\\n  IDS_AM: \"am\",\\n  IDS_PM: \"pm\",\\n  IDS_MONTH_INFO: \"January[1] February[2] March[3] April[4] May[5] \" + \"June[6] July[7] August[8] September[9] October[10] \" + \"November[11] December[12] Sept[9] Jan[1] Feb[2] Mar[3] \" + \"Apr[4] Jun[6] Jul[7] Aug[8] Sep[9] Oct[10] Nov[11] Dec[12]\",\\n  IDS_STARTUP_CONSOLE_MSG: \"** ^ _ ^ **\",\\n  RE_NUMBER_ENTRY_DOT_SEP: [\"[+-]?\\\\\\\\d*\\\\\\\\.?\\\\\\\\d*\"],\\n  RE_NUMBER_COMMIT_DOT_SEP: [\"[+-]?\\\\\\\\d+(\\\\\\\\.\\\\\\\\d+)?\", \"[+-]?\\\\\\\\.\\\\\\\\d+\", \"[+-]?\\\\\\\\d+\\\\\\\\.\"],\\n  RE_NUMBER_ENTRY_COMMA_SEP: [\"[+-]?\\\\\\\\d*,?\\\\\\\\d*\"],\\n  RE_NUMBER_COMMIT_COMMA_SEP: [\"[+-]?\\\\\\\\d+([.,]\\\\\\\\d+)?\", \"[+-]?[.,]\\\\\\\\d+\", \"[+-]?\\\\\\\\d+[.,]\"],\\n  RE_ZIP_ENTRY: [\"\\\\\\\\d{0,5}\"],\\n  RE_ZIP_COMMIT: [\"\\\\\\\\d{5}\"],\\n  RE_ZIP4_ENTRY: [\"\\\\\\\\d{0,5}(\\\\\\\\.|[- ])?\\\\\\\\d{0,4}\"],\\n  RE_ZIP4_COMMIT: [\"\\\\\\\\d{5}(\\\\\\\\.|[- ])?\\\\\\\\d{4}\"],\\n  RE_PHONE_ENTRY: [\"\\\\\\\\d{0,3}(\\\\\\\\.|[- ])?\\\\\\\\d{0,3}(\\\\\\\\.|[- ])?\\\\\\\\d{0,4}\", \"\\\\\\\\(\\\\\\\\d{0,3}\", \"\\\\\\\\(\\\\\\\\d{0,3}\\\\\\\\)(\\\\\\\\.|[- ])?\\\\\\\\d{0,3}(\\\\\\\\.|[- ])?\\\\\\\\d{0,4}\", \"\\\\\\\\(\\\\\\\\d{0,3}(\\\\\\\\.|[- ])?\\\\\\\\d{0,3}(\\\\\\\\.|[- ])?\\\\\\\\d{0,4}\", \"\\\\\\\\d{0,3}\\\\\\\\)(\\\\\\\\.|[- ])?\\\\\\\\d{0,3}(\\\\\\\\.|[- ])?\\\\\\\\d{0,4}\", \"011(\\\\\\\\.|[- \\\\\\\\d])*\"],\\n  RE_PHONE_COMMIT: [\"\\\\\\\\d{3}(\\\\\\\\.|[- ])?\\\\\\\\d{4}\", \"\\\\\\\\d{3}(\\\\\\\\.|[- ])?\\\\\\\\d{3}(\\\\\\\\.|[- ])?\\\\\\\\d{4}\", \"\\\\\\\\(\\\\\\\\d{3}\\\\\\\\)(\\\\\\\\.|[- ])?\\\\\\\\d{3}(\\\\\\\\.|[- ])?\\\\\\\\d{4}\", \"011(\\\\\\\\.|[- \\\\\\\\d])*\"],\\n  RE_SSN_ENTRY: [\"\\\\\\\\d{0,3}(\\\\\\\\.|[- ])?\\\\\\\\d{0,2}(\\\\\\\\.|[- ])?\\\\\\\\d{0,4}\"],\\n  RE_SSN_COMMIT: [\"\\\\\\\\d{3}(\\\\\\\\.|[- ])?\\\\\\\\d{2}(\\\\\\\\.|[- ])?\\\\\\\\d{4}\"]\\n});\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/common.js\\nconst FieldType = {\\n  none: 0,\\n  number: 1,\\n  percent: 2,\\n  date: 3,\\n  time: 4\\n};\\nfunction createActionsMap(actions) {\\n  const actionsMap = new Map();\\n  if (actions) {\\n    for (const [eventType, actionsForEvent] of Object.entries(actions)) {\\n      actionsMap.set(eventType, actionsForEvent);\\n    }\\n  }\\n  return actionsMap;\\n}\\nfunction getFieldType(actions) {\\n  let format = actions.get(\"Format\");\\n  if (!format) {\\n    return FieldType.none;\\n  }\\n  format = format[0];\\n  format = format.trim();\\n  if (format.startsWith(\"AFNumber_\")) {\\n    return FieldType.number;\\n  }\\n  if (format.startsWith(\"AFPercent_\")) {\\n    return FieldType.percent;\\n  }\\n  if (format.startsWith(\"AFDate_\")) {\\n    return FieldType.date;\\n  }\\n  if (format.startsWith(\"AFTime__\")) {\\n    return FieldType.time;\\n  }\\n  return FieldType.none;\\n}\\n\\n;// CONCATENATED MODULE: ./src/shared/scripting_utils.js\\nfunction makeColorComp(n) {\\n  return Math.floor(Math.max(0, Math.min(1, n)) * 255).toString(16).padStart(2, \"0\");\\n}\\nfunction scaleAndClamp(x) {\\n  return Math.max(0, Math.min(255, 255 * x));\\n}\\nclass ColorConverters {\\n  static CMYK_G([c, y, m, k]) {\\n    return [\"G\", 1 - Math.min(1, 0.3 * c + 0.59 * m + 0.11 * y + k)];\\n  }\\n  static G_CMYK([g]) {\\n    return [\"CMYK\", 0, 0, 0, 1 - g];\\n  }\\n  static G_RGB([g]) {\\n    return [\"RGB\", g, g, g];\\n  }\\n  static G_rgb([g]) {\\n    g = scaleAndClamp(g);\\n    return [g, g, g];\\n  }\\n  static G_HTML([g]) {\\n    const G = makeColorComp(g);\\n    return `#${G}${G}${G}`;\\n  }\\n  static RGB_G([r, g, b]) {\\n    return [\"G\", 0.3 * r + 0.59 * g + 0.11 * b];\\n  }\\n  static RGB_rgb(color) {\\n    return color.map(scaleAndClamp);\\n  }\\n  static RGB_HTML(color) {\\n    return `#${color.map(makeColorComp).join(\"\")}`;\\n  }\\n  static T_HTML() {\\n    return \"#00000000\";\\n  }\\n  static T_rgb() {\\n    return [null];\\n  }\\n  static CMYK_RGB([c, y, m, k]) {\\n    return [\"RGB\", 1 - Math.min(1, c + k), 1 - Math.min(1, m + k), 1 - Math.min(1, y + k)];\\n  }\\n  static CMYK_rgb([c, y, m, k]) {\\n    return [scaleAndClamp(1 - Math.min(1, c + k)), scaleAndClamp(1 - Math.min(1, m + k)), scaleAndClamp(1 - Math.min(1, y + k))];\\n  }\\n  static CMYK_HTML(components) {\\n    const rgb = this.CMYK_RGB(components).slice(1);\\n    return this.RGB_HTML(rgb);\\n  }\\n  static RGB_CMYK([r, g, b]) {\\n    const c = 1 - r;\\n    const m = 1 - g;\\n    const y = 1 - b;\\n    const k = Math.min(c, m, y);\\n    return [\"CMYK\", c, m, y, k];\\n  }\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/pdf_object.js\\nclass PDFObject {\\n  constructor(data) {\\n    this._expandos = Object.create(null);\\n    this._send = data.send || null;\\n    this._id = data.id || null;\\n  }\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/color.js\\n\\n\\nclass Color extends PDFObject {\\n  constructor() {\\n    super({});\\n    this.transparent = [\"T\"];\\n    this.black = [\"G\", 0];\\n    this.white = [\"G\", 1];\\n    this.red = [\"RGB\", 1, 0, 0];\\n    this.green = [\"RGB\", 0, 1, 0];\\n    this.blue = [\"RGB\", 0, 0, 1];\\n    this.cyan = [\"CMYK\", 1, 0, 0, 0];\\n    this.magenta = [\"CMYK\", 0, 1, 0, 0];\\n    this.yellow = [\"CMYK\", 0, 0, 1, 0];\\n    this.dkGray = [\"G\", 0.25];\\n    this.gray = [\"G\", 0.5];\\n    this.ltGray = [\"G\", 0.75];\\n  }\\n  static _isValidSpace(cColorSpace) {\\n    return typeof cColorSpace === \"string\" && (cColorSpace === \"T\" || cColorSpace === \"G\" || cColorSpace === \"RGB\" || cColorSpace === \"CMYK\");\\n  }\\n  static _isValidColor(colorArray) {\\n    if (!Array.isArray(colorArray) || colorArray.length === 0) {\\n      return false;\\n    }\\n    const space = colorArray[0];\\n    if (!Color._isValidSpace(space)) {\\n      return false;\\n    }\\n    switch (space) {\\n      case \"T\":\\n        if (colorArray.length !== 1) {\\n          return false;\\n        }\\n        break;\\n      case \"G\":\\n        if (colorArray.length !== 2) {\\n          return false;\\n        }\\n        break;\\n      case \"RGB\":\\n        if (colorArray.length !== 4) {\\n          return false;\\n        }\\n        break;\\n      case \"CMYK\":\\n        if (colorArray.length !== 5) {\\n          return false;\\n        }\\n        break;\\n      default:\\n        return false;\\n    }\\n    return colorArray.slice(1).every(c => typeof c === \"number\" && c >= 0 && c <= 1);\\n  }\\n  static _getCorrectColor(colorArray) {\\n    return Color._isValidColor(colorArray) ? colorArray : [\"G\", 0];\\n  }\\n  convert(colorArray, cColorSpace) {\\n    if (!Color._isValidSpace(cColorSpace)) {\\n      return this.black;\\n    }\\n    if (cColorSpace === \"T\") {\\n      return [\"T\"];\\n    }\\n    colorArray = Color._getCorrectColor(colorArray);\\n    if (colorArray[0] === cColorSpace) {\\n      return colorArray;\\n    }\\n    if (colorArray[0] === \"T\") {\\n      return this.convert(this.black, cColorSpace);\\n    }\\n    return ColorConverters[`${colorArray[0]}_${cColorSpace}`](colorArray.slice(1));\\n  }\\n  equal(colorArray1, colorArray2) {\\n    colorArray1 = Color._getCorrectColor(colorArray1);\\n    colorArray2 = Color._getCorrectColor(colorArray2);\\n    if (colorArray1[0] === \"T\" || colorArray2[0] === \"T\") {\\n      return colorArray1[0] === \"T\" && colorArray2[0] === \"T\";\\n    }\\n    if (colorArray1[0] !== colorArray2[0]) {\\n      colorArray2 = this.convert(colorArray2, colorArray1[0]);\\n    }\\n    return colorArray1.slice(1).every((c, i) => c === colorArray2[i + 1]);\\n  }\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/field.js\\n\\n\\n\\nclass Field extends PDFObject {\\n  constructor(data) {\\n    super(data);\\n    this.alignment = data.alignment || \"left\";\\n    this.borderStyle = data.borderStyle || \"\";\\n    this.buttonAlignX = data.buttonAlignX || 50;\\n    this.buttonAlignY = data.buttonAlignY || 50;\\n    this.buttonFitBounds = data.buttonFitBounds;\\n    this.buttonPosition = data.buttonPosition;\\n    this.buttonScaleHow = data.buttonScaleHow;\\n    this.ButtonScaleWhen = data.buttonScaleWhen;\\n    this.calcOrderIndex = data.calcOrderIndex;\\n    this.comb = data.comb;\\n    this.commitOnSelChange = data.commitOnSelChange;\\n    this.currentValueIndices = data.currentValueIndices;\\n    this.defaultStyle = data.defaultStyle;\\n    this.defaultValue = data.defaultValue;\\n    this.doNotScroll = data.doNotScroll;\\n    this.doNotSpellCheck = data.doNotSpellCheck;\\n    this.delay = data.delay;\\n    this.display = data.display;\\n    this.doc = data.doc.wrapped;\\n    this.editable = data.editable;\\n    this.exportValues = data.exportValues;\\n    this.fileSelect = data.fileSelect;\\n    this.hidden = data.hidden;\\n    this.highlight = data.highlight;\\n    this.lineWidth = data.lineWidth;\\n    this.multiline = data.multiline;\\n    this.multipleSelection = !!data.multipleSelection;\\n    this.name = data.name;\\n    this.password = data.password;\\n    this.print = data.print;\\n    this.radiosInUnison = data.radiosInUnison;\\n    this.readonly = data.readonly;\\n    this.rect = data.rect;\\n    this.required = data.required;\\n    this.richText = data.richText;\\n    this.richValue = data.richValue;\\n    this.style = data.style;\\n    this.submitName = data.submitName;\\n    this.textFont = data.textFont;\\n    this.textSize = data.textSize;\\n    this.type = data.type;\\n    this.userName = data.userName;\\n    this._actions = createActionsMap(data.actions);\\n    this._browseForFileToSubmit = data.browseForFileToSubmit || null;\\n    this._buttonCaption = null;\\n    this._buttonIcon = null;\\n    this._charLimit = data.charLimit;\\n    this._children = null;\\n    this._currentValueIndices = data.currentValueIndices || 0;\\n    this._document = data.doc;\\n    this._fieldPath = data.fieldPath;\\n    this._fillColor = data.fillColor || [\"T\"];\\n    this._isChoice = Array.isArray(data.items);\\n    this._items = data.items || [];\\n    this._hasValue = data.hasOwnProperty(\"value\");\\n    this._page = data.page || 0;\\n    this._strokeColor = data.strokeColor || [\"G\", 0];\\n    this._textColor = data.textColor || [\"G\", 0];\\n    this._value = null;\\n    this._kidIds = data.kidIds || null;\\n    this._fieldType = getFieldType(this._actions);\\n    this._siblings = data.siblings || null;\\n    this._rotation = data.rotation || 0;\\n    this._globalEval = data.globalEval;\\n    this._appObjects = data.appObjects;\\n    this.value = data.value || \"\";\\n  }\\n  get currentValueIndices() {\\n    if (!this._isChoice) {\\n      return 0;\\n    }\\n    return this._currentValueIndices;\\n  }\\n  set currentValueIndices(indices) {\\n    if (!this._isChoice) {\\n      return;\\n    }\\n    if (!Array.isArray(indices)) {\\n      indices = [indices];\\n    }\\n    if (!indices.every(i => typeof i === \"number\" && Number.isInteger(i) && i >= 0 && i < this.numItems)) {\\n      return;\\n    }\\n    indices.sort();\\n    if (this.multipleSelection) {\\n      this._currentValueIndices = indices;\\n      this._value = [];\\n      indices.forEach(i => {\\n        this._value.push(this._items[i].displayValue);\\n      });\\n    } else if (indices.length > 0) {\\n      indices = indices.splice(1, indices.length - 1);\\n      this._currentValueIndices = indices[0];\\n      this._value = this._items[this._currentValueIndices];\\n    }\\n    this._send({\\n      id: this._id,\\n      indices\\n    });\\n  }\\n  get fillColor() {\\n    return this._fillColor;\\n  }\\n  set fillColor(color) {\\n    if (Color._isValidColor(color)) {\\n      this._fillColor = color;\\n    }\\n  }\\n  get bgColor() {\\n    return this.fillColor;\\n  }\\n  set bgColor(color) {\\n    this.fillColor = color;\\n  }\\n  get charLimit() {\\n    return this._charLimit;\\n  }\\n  set charLimit(limit) {\\n    if (typeof limit !== \"number\") {\\n      throw new Error(\"Invalid argument value\");\\n    }\\n    this._charLimit = Math.max(0, Math.floor(limit));\\n  }\\n  get numItems() {\\n    if (!this._isChoice) {\\n      throw new Error(\"Not a choice widget\");\\n    }\\n    return this._items.length;\\n  }\\n  set numItems(_) {\\n    throw new Error(\"field.numItems is read-only\");\\n  }\\n  get strokeColor() {\\n    return this._strokeColor;\\n  }\\n  set strokeColor(color) {\\n    if (Color._isValidColor(color)) {\\n      this._strokeColor = color;\\n    }\\n  }\\n  get borderColor() {\\n    return this.strokeColor;\\n  }\\n  set borderColor(color) {\\n    this.strokeColor = color;\\n  }\\n  get page() {\\n    return this._page;\\n  }\\n  set page(_) {\\n    throw new Error(\"field.page is read-only\");\\n  }\\n  get rotation() {\\n    return this._rotation;\\n  }\\n  set rotation(angle) {\\n    angle = Math.floor(angle);\\n    if (angle % 90 !== 0) {\\n      throw new Error(\"Invalid rotation: must be a multiple of 90\");\\n    }\\n    angle %= 360;\\n    if (angle < 0) {\\n      angle += 360;\\n    }\\n    this._rotation = angle;\\n  }\\n  get textColor() {\\n    return this._textColor;\\n  }\\n  set textColor(color) {\\n    if (Color._isValidColor(color)) {\\n      this._textColor = color;\\n    }\\n  }\\n  get fgColor() {\\n    return this.textColor;\\n  }\\n  set fgColor(color) {\\n    this.textColor = color;\\n  }\\n  get value() {\\n    return this._value;\\n  }\\n  set value(value) {\\n    if (this._isChoice) {\\n      this._setChoiceValue(value);\\n      return;\\n    }\\n    if (value === \"\" || typeof value !== \"string\") {\\n      this._originalValue = undefined;\\n      this._value = value;\\n      return;\\n    }\\n    this._originalValue = value;\\n    const _value = value.trim().replace(\",\", \".\");\\n    this._value = !isNaN(_value) ? parseFloat(_value) : value;\\n  }\\n  _getValue() {\\n    return this._originalValue ?? this.value;\\n  }\\n  _setChoiceValue(value) {\\n    if (this.multipleSelection) {\\n      if (!Array.isArray(value)) {\\n        value = [value];\\n      }\\n      const values = new Set(value);\\n      if (Array.isArray(this._currentValueIndices)) {\\n        this._currentValueIndices.length = 0;\\n        this._value.length = 0;\\n      } else {\\n        this._currentValueIndices = [];\\n        this._value = [];\\n      }\\n      this._items.forEach((item, i) => {\\n        if (values.has(item.exportValue)) {\\n          this._currentValueIndices.push(i);\\n          this._value.push(item.exportValue);\\n        }\\n      });\\n    } else {\\n      if (Array.isArray(value)) {\\n        value = value[0];\\n      }\\n      const index = this._items.findIndex(({\\n        exportValue\\n      }) => value === exportValue);\\n      if (index !== -1) {\\n        this._currentValueIndices = index;\\n        this._value = this._items[index].exportValue;\\n      }\\n    }\\n  }\\n  get valueAsString() {\\n    return (this._value ?? \"\").toString();\\n  }\\n  set valueAsString(_) {}\\n  browseForFileToSubmit() {\\n    if (this._browseForFileToSubmit) {\\n      this._browseForFileToSubmit();\\n    }\\n  }\\n  buttonGetCaption(nFace = 0) {\\n    if (this._buttonCaption) {\\n      return this._buttonCaption[nFace];\\n    }\\n    return \"\";\\n  }\\n  buttonGetIcon(nFace = 0) {\\n    if (this._buttonIcon) {\\n      return this._buttonIcon[nFace];\\n    }\\n    return null;\\n  }\\n  buttonImportIcon(cPath = null, nPave = 0) {}\\n  buttonSetCaption(cCaption, nFace = 0) {\\n    if (!this._buttonCaption) {\\n      this._buttonCaption = [\"\", \"\", \"\"];\\n    }\\n    this._buttonCaption[nFace] = cCaption;\\n  }\\n  buttonSetIcon(oIcon, nFace = 0) {\\n    if (!this._buttonIcon) {\\n      this._buttonIcon = [null, null, null];\\n    }\\n    this._buttonIcon[nFace] = oIcon;\\n  }\\n  checkThisBox(nWidget, bCheckIt = true) {}\\n  clearItems() {\\n    if (!this._isChoice) {\\n      throw new Error(\"Not a choice widget\");\\n    }\\n    this._items = [];\\n    this._send({\\n      id: this._id,\\n      clear: null\\n    });\\n  }\\n  deleteItemAt(nIdx = null) {\\n    if (!this._isChoice) {\\n      throw new Error(\"Not a choice widget\");\\n    }\\n    if (!this.numItems) {\\n      return;\\n    }\\n    if (nIdx === null) {\\n      nIdx = Array.isArray(this._currentValueIndices) ? this._currentValueIndices[0] : this._currentValueIndices;\\n      nIdx ||= 0;\\n    }\\n    if (nIdx < 0 || nIdx >= this.numItems) {\\n      nIdx = this.numItems - 1;\\n    }\\n    this._items.splice(nIdx, 1);\\n    if (Array.isArray(this._currentValueIndices)) {\\n      let index = this._currentValueIndices.findIndex(i => i >= nIdx);\\n      if (index !== -1) {\\n        if (this._currentValueIndices[index] === nIdx) {\\n          this._currentValueIndices.splice(index, 1);\\n        }\\n        for (const ii = this._currentValueIndices.length; index < ii; index++) {\\n          --this._currentValueIndices[index];\\n        }\\n      }\\n    } else if (this._currentValueIndices === nIdx) {\\n      this._currentValueIndices = this.numItems > 0 ? 0 : -1;\\n    } else if (this._currentValueIndices > nIdx) {\\n      --this._currentValueIndices;\\n    }\\n    this._send({\\n      id: this._id,\\n      remove: nIdx\\n    });\\n  }\\n  getItemAt(nIdx = -1, bExportValue = false) {\\n    if (!this._isChoice) {\\n      throw new Error(\"Not a choice widget\");\\n    }\\n    if (nIdx < 0 || nIdx >= this.numItems) {\\n      nIdx = this.numItems - 1;\\n    }\\n    const item = this._items[nIdx];\\n    return bExportValue ? item.exportValue : item.displayValue;\\n  }\\n  getArray() {\\n    if (this._kidIds) {\\n      const array = [];\\n      const fillArrayWithKids = kidIds => {\\n        for (const id of kidIds) {\\n          const obj = this._appObjects[id];\\n          if (!obj) {\\n            continue;\\n          }\\n          if (obj.obj._hasValue) {\\n            array.push(obj.wrapped);\\n          }\\n          if (obj.obj._kidIds) {\\n            fillArrayWithKids(obj.obj._kidIds);\\n          }\\n        }\\n      };\\n      fillArrayWithKids(this._kidIds);\\n      return array;\\n    }\\n    if (this._children === null) {\\n      this._children = this._document.obj._getTerminalChildren(this._fieldPath);\\n    }\\n    return this._children;\\n  }\\n  getLock() {\\n    return undefined;\\n  }\\n  isBoxChecked(nWidget) {\\n    return false;\\n  }\\n  isDefaultChecked(nWidget) {\\n    return false;\\n  }\\n  insertItemAt(cName, cExport = undefined, nIdx = 0) {\\n    if (!this._isChoice) {\\n      throw new Error(\"Not a choice widget\");\\n    }\\n    if (!cName) {\\n      return;\\n    }\\n    if (nIdx < 0 || nIdx > this.numItems) {\\n      nIdx = this.numItems;\\n    }\\n    if (this._items.some(({\\n      displayValue\\n    }) => displayValue === cName)) {\\n      return;\\n    }\\n    if (cExport === undefined) {\\n      cExport = cName;\\n    }\\n    const data = {\\n      displayValue: cName,\\n      exportValue: cExport\\n    };\\n    this._items.splice(nIdx, 0, data);\\n    if (Array.isArray(this._currentValueIndices)) {\\n      let index = this._currentValueIndices.findIndex(i => i >= nIdx);\\n      if (index !== -1) {\\n        for (const ii = this._currentValueIndices.length; index < ii; index++) {\\n          ++this._currentValueIndices[index];\\n        }\\n      }\\n    } else if (this._currentValueIndices >= nIdx) {\\n      ++this._currentValueIndices;\\n    }\\n    this._send({\\n      id: this._id,\\n      insert: {\\n        index: nIdx,\\n        ...data\\n      }\\n    });\\n  }\\n  setAction(cTrigger, cScript) {\\n    if (typeof cTrigger !== \"string\" || typeof cScript !== \"string\") {\\n      return;\\n    }\\n    if (!(cTrigger in this._actions)) {\\n      this._actions[cTrigger] = [];\\n    }\\n    this._actions[cTrigger].push(cScript);\\n  }\\n  setFocus() {\\n    this._send({\\n      id: this._id,\\n      focus: true\\n    });\\n  }\\n  setItems(oArray) {\\n    if (!this._isChoice) {\\n      throw new Error(\"Not a choice widget\");\\n    }\\n    this._items.length = 0;\\n    for (const element of oArray) {\\n      let displayValue, exportValue;\\n      if (Array.isArray(element)) {\\n        displayValue = element[0]?.toString() || \"\";\\n        exportValue = element[1]?.toString() || \"\";\\n      } else {\\n        displayValue = exportValue = element?.toString() || \"\";\\n      }\\n      this._items.push({\\n        displayValue,\\n        exportValue\\n      });\\n    }\\n    this._currentValueIndices = 0;\\n    this._send({\\n      id: this._id,\\n      items: this._items\\n    });\\n  }\\n  setLock() {}\\n  signatureGetModifications() {}\\n  signatureGetSeedValue() {}\\n  signatureInfo() {}\\n  signatureSetSeedValue() {}\\n  signatureSign() {}\\n  signatureValidate() {}\\n  _isButton() {\\n    return false;\\n  }\\n  _reset() {\\n    this.value = this.defaultValue;\\n  }\\n  _runActions(event) {\\n    const eventName = event.name;\\n    if (!this._actions.has(eventName)) {\\n      return false;\\n    }\\n    const actions = this._actions.get(eventName);\\n    try {\\n      for (const action of actions) {\\n        this._globalEval(action);\\n      }\\n    } catch (error) {\\n      event.rc = false;\\n      throw error;\\n    }\\n    return true;\\n  }\\n}\\nclass RadioButtonField extends Field {\\n  constructor(otherButtons, data) {\\n    super(data);\\n    this.exportValues = [this.exportValues];\\n    this._radioIds = [this._id];\\n    this._radioActions = [this._actions];\\n    for (const radioData of otherButtons) {\\n      this.exportValues.push(radioData.exportValues);\\n      this._radioIds.push(radioData.id);\\n      this._radioActions.push(createActionsMap(radioData.actions));\\n      if (this._value === radioData.exportValues) {\\n        this._id = radioData.id;\\n      }\\n    }\\n    this._hasBeenInitialized = true;\\n    this._value = data.value || \"\";\\n  }\\n  get value() {\\n    return this._value;\\n  }\\n  set value(value) {\\n    if (!this._hasBeenInitialized) {\\n      return;\\n    }\\n    if (value === null || value === undefined) {\\n      this._value = \"\";\\n    }\\n    const i = this.exportValues.indexOf(value);\\n    if (0 <= i && i < this._radioIds.length) {\\n      this._id = this._radioIds[i];\\n      this._value = value;\\n    } else if (value === \"Off\" && this._radioIds.length === 2) {\\n      const nextI = (1 + this._radioIds.indexOf(this._id)) % 2;\\n      this._id = this._radioIds[nextI];\\n      this._value = this.exportValues[nextI];\\n    }\\n  }\\n  checkThisBox(nWidget, bCheckIt = true) {\\n    if (nWidget < 0 || nWidget >= this._radioIds.length || !bCheckIt) {\\n      return;\\n    }\\n    this._id = this._radioIds[nWidget];\\n    this._value = this.exportValues[nWidget];\\n    this._send({\\n      id: this._id,\\n      value: this._value\\n    });\\n  }\\n  isBoxChecked(nWidget) {\\n    return nWidget >= 0 && nWidget < this._radioIds.length && this._id === this._radioIds[nWidget];\\n  }\\n  isDefaultChecked(nWidget) {\\n    return nWidget >= 0 && nWidget < this.exportValues.length && this.defaultValue === this.exportValues[nWidget];\\n  }\\n  _getExportValue(state) {\\n    const i = this._radioIds.indexOf(this._id);\\n    return this.exportValues[i];\\n  }\\n  _runActions(event) {\\n    const i = this._radioIds.indexOf(this._id);\\n    this._actions = this._radioActions[i];\\n    return super._runActions(event);\\n  }\\n  _isButton() {\\n    return true;\\n  }\\n}\\nclass CheckboxField extends RadioButtonField {\\n  get value() {\\n    return this._value;\\n  }\\n  set value(value) {\\n    if (!value || value === \"Off\") {\\n      this._value = \"Off\";\\n    } else {\\n      super.value = value;\\n    }\\n  }\\n  _getExportValue(state) {\\n    return state ? super._getExportValue(state) : \"Off\";\\n  }\\n  isBoxChecked(nWidget) {\\n    if (this._value === \"Off\") {\\n      return false;\\n    }\\n    return super.isBoxChecked(nWidget);\\n  }\\n  isDefaultChecked(nWidget) {\\n    if (this.defaultValue === \"Off\") {\\n      return this._value === \"Off\";\\n    }\\n    return super.isDefaultChecked(nWidget);\\n  }\\n  checkThisBox(nWidget, bCheckIt = true) {\\n    if (nWidget < 0 || nWidget >= this._radioIds.length) {\\n      return;\\n    }\\n    this._id = this._radioIds[nWidget];\\n    this._value = bCheckIt ? this.exportValues[nWidget] : \"Off\";\\n    this._send({\\n      id: this._id,\\n      value: this._value\\n    });\\n  }\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/aform.js\\n\\nclass AForm {\\n  constructor(document, app, util, color) {\\n    this._document = document;\\n    this._app = app;\\n    this._util = util;\\n    this._color = color;\\n    this._dateFormats = [\"m/d\", \"m/d/yy\", \"mm/dd/yy\", \"mm/yy\", \"d-mmm\", \"d-mmm-yy\", \"dd-mmm-yy\", \"yy-mm-dd\", \"mmm-yy\", \"mmmm-yy\", \"mmm d, yyyy\", \"mmmm d, yyyy\", \"m/d/yy h:MM tt\", \"m/d/yy HH:MM\"];\\n    this._timeFormats = [\"HH:MM\", \"h:MM tt\", \"HH:MM:ss\", \"h:MM:ss tt\"];\\n    this._dateActionsCache = new Map();\\n    this._emailRegex = new RegExp(\"^[a-zA-Z0-9.!#$%&\\'*+\\\\\\\\/=?^_`{|}~-]+\" + \"@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\" + \"(?:\\\\\\\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$\");\\n  }\\n  _mkTargetName(event) {\\n    return event.target ? `[ ${event.target.name} ]` : \"\";\\n  }\\n  _tryToGuessDate(cFormat, cDate) {\\n    let actions = this._dateActionsCache.get(cFormat);\\n    if (!actions) {\\n      actions = [];\\n      this._dateActionsCache.set(cFormat, actions);\\n      cFormat.replaceAll(/(d+)|(m+)|(y+)|(H+)|(M+)|(s+)/g, function (match, d, m, y, H, M, s) {\\n        if (d) {\\n          actions.push((n, date) => {\\n            if (n >= 1 && n <= 31) {\\n              date.setDate(n);\\n              return true;\\n            }\\n            return false;\\n          });\\n        } else if (m) {\\n          actions.push((n, date) => {\\n            if (n >= 1 && n <= 12) {\\n              date.setMonth(n - 1);\\n              return true;\\n            }\\n            return false;\\n          });\\n        } else if (y) {\\n          actions.push((n, date) => {\\n            if (n < 50) {\\n              n += 2000;\\n            } else if (n < 100) {\\n              n += 1900;\\n            }\\n            date.setYear(n);\\n            return true;\\n          });\\n        } else if (H) {\\n          actions.push((n, date) => {\\n            if (n >= 0 && n <= 23) {\\n              date.setHours(n);\\n              return true;\\n            }\\n            return false;\\n          });\\n        } else if (M) {\\n          actions.push((n, date) => {\\n            if (n >= 0 && n <= 59) {\\n              date.setMinutes(n);\\n              return true;\\n            }\\n            return false;\\n          });\\n        } else if (s) {\\n          actions.push((n, date) => {\\n            if (n >= 0 && n <= 59) {\\n              date.setSeconds(n);\\n              return true;\\n            }\\n            return false;\\n          });\\n        }\\n        return \"\";\\n      });\\n    }\\n    const number = /\\\\d+/g;\\n    let i = 0;\\n    let array;\\n    const date = new Date();\\n    while ((array = number.exec(cDate)) !== null) {\\n      if (i < actions.length) {\\n        if (!actions[i++](parseInt(array[0]), date)) {\\n          return null;\\n        }\\n      } else {\\n        break;\\n      }\\n    }\\n    if (i === 0) {\\n      return null;\\n    }\\n    return date;\\n  }\\n  _parseDate(cFormat, cDate) {\\n    let date = null;\\n    try {\\n      date = this._util.scand(cFormat, cDate);\\n    } catch {}\\n    if (!date) {\\n      date = Date.parse(cDate);\\n      date = isNaN(date) ? this._tryToGuessDate(cFormat, cDate) : new Date(date);\\n    }\\n    return date;\\n  }\\n  AFMergeChange(event = globalThis.event) {\\n    if (event.willCommit) {\\n      return event.value.toString();\\n    }\\n    return this._app._eventDispatcher.mergeChange(event);\\n  }\\n  AFParseDateEx(cString, cOrder) {\\n    return this._parseDate(cOrder, cString);\\n  }\\n  AFExtractNums(str) {\\n    if (typeof str === \"number\") {\\n      return [str];\\n    }\\n    if (!str || typeof str !== \"string\") {\\n      return null;\\n    }\\n    const first = str.charAt(0);\\n    if (first === \".\" || first === \",\") {\\n      str = `0${str}`;\\n    }\\n    const numbers = str.match(/(\\\\d+)/g);\\n    if (numbers.length === 0) {\\n      return null;\\n    }\\n    return numbers;\\n  }\\n  AFMakeNumber(str) {\\n    if (typeof str === \"number\") {\\n      return str;\\n    }\\n    if (typeof str !== \"string\") {\\n      return null;\\n    }\\n    str = str.trim().replace(\",\", \".\");\\n    const number = parseFloat(str);\\n    if (isNaN(number) || !isFinite(number)) {\\n      return null;\\n    }\\n    return number;\\n  }\\n  AFMakeArrayFromList(string) {\\n    if (typeof string === \"string\") {\\n      return string.split(/, ?/g);\\n    }\\n    return string;\\n  }\\n  AFNumber_Format(nDec, sepStyle, negStyle, currStyle, strCurrency, bCurrencyPrepend) {\\n    const event = globalThis.event;\\n    if (!event.value) {\\n      return;\\n    }\\n    let value = this.AFMakeNumber(event.value);\\n    if (value === null) {\\n      event.value = \"\";\\n      return;\\n    }\\n    const sign = Math.sign(value);\\n    const buf = [];\\n    let hasParen = false;\\n    if (sign === -1 && bCurrencyPrepend && negStyle === 0) {\\n      buf.push(\"-\");\\n    }\\n    if ((negStyle === 2 || negStyle === 3) && sign === -1) {\\n      buf.push(\"(\");\\n      hasParen = true;\\n    }\\n    if (bCurrencyPrepend) {\\n      buf.push(strCurrency);\\n    }\\n    sepStyle = Math.min(Math.max(0, Math.floor(sepStyle)), 4);\\n    buf.push(\"%,\", sepStyle, \".\", nDec.toString(), \"f\");\\n    if (!bCurrencyPrepend) {\\n      buf.push(strCurrency);\\n    }\\n    if (hasParen) {\\n      buf.push(\")\");\\n    }\\n    if (negStyle === 1 || negStyle === 3) {\\n      event.target.textColor = sign === 1 ? this._color.black : this._color.red;\\n    }\\n    if ((negStyle !== 0 || bCurrencyPrepend) && sign === -1) {\\n      value = -value;\\n    }\\n    const formatStr = buf.join(\"\");\\n    event.value = this._util.printf(formatStr, value);\\n  }\\n  AFNumber_Keystroke(nDec, sepStyle, negStyle, currStyle, strCurrency, bCurrencyPrepend) {\\n    const event = globalThis.event;\\n    let value = this.AFMergeChange(event);\\n    if (!value) {\\n      return;\\n    }\\n    value = value.trim();\\n    let pattern;\\n    if (sepStyle > 1) {\\n      pattern = event.willCommit ? /^[+-]?(\\\\d+(,\\\\d*)?|,\\\\d+)$/ : /^[+-]?\\\\d*,?\\\\d*$/;\\n    } else {\\n      pattern = event.willCommit ? /^[+-]?(\\\\d+(\\\\.\\\\d*)?|\\\\.\\\\d+)$/ : /^[+-]?\\\\d*\\\\.?\\\\d*$/;\\n    }\\n    if (!pattern.test(value)) {\\n      if (event.willCommit) {\\n        const err = `${GlobalConstants.IDS_INVALID_VALUE} ${this._mkTargetName(event)}`;\\n        this._app.alert(err);\\n      }\\n      event.rc = false;\\n    }\\n    if (event.willCommit && sepStyle > 1) {\\n      event.value = parseFloat(value.replace(\",\", \".\"));\\n    }\\n  }\\n  AFPercent_Format(nDec, sepStyle, percentPrepend = false) {\\n    if (typeof nDec !== \"number\") {\\n      return;\\n    }\\n    if (typeof sepStyle !== \"number\") {\\n      return;\\n    }\\n    if (nDec < 0) {\\n      throw new Error(\"Invalid nDec value in AFPercent_Format\");\\n    }\\n    const event = globalThis.event;\\n    if (nDec > 512) {\\n      event.value = \"%\";\\n      return;\\n    }\\n    nDec = Math.floor(nDec);\\n    sepStyle = Math.min(Math.max(0, Math.floor(sepStyle)), 4);\\n    let value = this.AFMakeNumber(event.value);\\n    if (value === null) {\\n      event.value = \"%\";\\n      return;\\n    }\\n    const formatStr = `%,${sepStyle}.${nDec}f`;\\n    value = this._util.printf(formatStr, value * 100);\\n    event.value = percentPrepend ? `%${value}` : `${value}%`;\\n  }\\n  AFPercent_Keystroke(nDec, sepStyle) {\\n    this.AFNumber_Keystroke(nDec, sepStyle, 0, 0, \"\", true);\\n  }\\n  AFDate_FormatEx(cFormat) {\\n    const event = globalThis.event;\\n    const value = event.value;\\n    if (!value) {\\n      return;\\n    }\\n    const date = this._parseDate(cFormat, value);\\n    if (date !== null) {\\n      event.value = this._util.printd(cFormat, date);\\n    }\\n  }\\n  AFDate_Format(pdf) {\\n    if (pdf >= 0 && pdf < this._dateFormats.length) {\\n      this.AFDate_FormatEx(this._dateFormats[pdf]);\\n    }\\n  }\\n  AFDate_KeystrokeEx(cFormat) {\\n    const event = globalThis.event;\\n    if (!event.willCommit) {\\n      return;\\n    }\\n    const value = this.AFMergeChange(event);\\n    if (!value) {\\n      return;\\n    }\\n    if (this._parseDate(cFormat, value) === null) {\\n      const invalid = GlobalConstants.IDS_INVALID_DATE;\\n      const invalid2 = GlobalConstants.IDS_INVALID_DATE2;\\n      const err = `${invalid} ${this._mkTargetName(event)}${invalid2}${cFormat}`;\\n      this._app.alert(err);\\n      event.rc = false;\\n    }\\n  }\\n  AFDate_Keystroke(pdf) {\\n    if (pdf >= 0 && pdf < this._dateFormats.length) {\\n      this.AFDate_KeystrokeEx(this._dateFormats[pdf]);\\n    }\\n  }\\n  AFRange_Validate(bGreaterThan, nGreaterThan, bLessThan, nLessThan) {\\n    const event = globalThis.event;\\n    if (!event.value) {\\n      return;\\n    }\\n    const value = this.AFMakeNumber(event.value);\\n    if (value === null) {\\n      return;\\n    }\\n    bGreaterThan = !!bGreaterThan;\\n    bLessThan = !!bLessThan;\\n    if (bGreaterThan) {\\n      nGreaterThan = this.AFMakeNumber(nGreaterThan);\\n      if (nGreaterThan === null) {\\n        return;\\n      }\\n    }\\n    if (bLessThan) {\\n      nLessThan = this.AFMakeNumber(nLessThan);\\n      if (nLessThan === null) {\\n        return;\\n      }\\n    }\\n    let err = \"\";\\n    if (bGreaterThan && bLessThan) {\\n      if (value < nGreaterThan || value > nLessThan) {\\n        err = this._util.printf(GlobalConstants.IDS_GT_AND_LT, nGreaterThan, nLessThan);\\n      }\\n    } else if (bGreaterThan) {\\n      if (value < nGreaterThan) {\\n        err = this._util.printf(GlobalConstants.IDS_GREATER_THAN, nGreaterThan);\\n      }\\n    } else if (value > nLessThan) {\\n      err = this._util.printf(GlobalConstants.IDS_LESS_THAN, nLessThan);\\n    }\\n    if (err) {\\n      this._app.alert(err);\\n      event.rc = false;\\n    }\\n  }\\n  AFSimple(cFunction, nValue1, nValue2) {\\n    const value1 = this.AFMakeNumber(nValue1);\\n    if (value1 === null) {\\n      throw new Error(\"Invalid nValue1 in AFSimple\");\\n    }\\n    const value2 = this.AFMakeNumber(nValue2);\\n    if (value2 === null) {\\n      throw new Error(\"Invalid nValue2 in AFSimple\");\\n    }\\n    switch (cFunction) {\\n      case \"AVG\":\\n        return (value1 + value2) / 2;\\n      case \"SUM\":\\n        return value1 + value2;\\n      case \"PRD\":\\n        return value1 * value2;\\n      case \"MIN\":\\n        return Math.min(value1, value2);\\n      case \"MAX\":\\n        return Math.max(value1, value2);\\n    }\\n    throw new Error(\"Invalid cFunction in AFSimple\");\\n  }\\n  AFSimple_Calculate(cFunction, cFields) {\\n    const actions = {\\n      AVG: args => args.reduce((acc, value) => acc + value, 0) / args.length,\\n      SUM: args => args.reduce((acc, value) => acc + value, 0),\\n      PRD: args => args.reduce((acc, value) => acc * value, 1),\\n      MIN: args => args.reduce((acc, value) => Math.min(acc, value), Number.MAX_VALUE),\\n      MAX: args => args.reduce((acc, value) => Math.max(acc, value), Number.MIN_VALUE)\\n    };\\n    if (!(cFunction in actions)) {\\n      throw new TypeError(\"Invalid function in AFSimple_Calculate\");\\n    }\\n    const event = globalThis.event;\\n    const values = [];\\n    cFields = this.AFMakeArrayFromList(cFields);\\n    for (const cField of cFields) {\\n      const field = this._document.getField(cField);\\n      if (!field) {\\n        continue;\\n      }\\n      for (const child of field.getArray()) {\\n        const number = this.AFMakeNumber(child.value);\\n        if (number !== null) {\\n          values.push(number);\\n        }\\n      }\\n    }\\n    if (values.length === 0) {\\n      event.value = cFunction === \"PRD\" ? 1 : 0;\\n      return;\\n    }\\n    const res = actions[cFunction](values);\\n    event.value = Math.round(1e6 * res) / 1e6;\\n  }\\n  AFSpecial_Format(psf) {\\n    const event = globalThis.event;\\n    if (!event.value) {\\n      return;\\n    }\\n    psf = this.AFMakeNumber(psf);\\n    let formatStr;\\n    switch (psf) {\\n      case 0:\\n        formatStr = \"99999\";\\n        break;\\n      case 1:\\n        formatStr = \"99999-9999\";\\n        break;\\n      case 2:\\n        formatStr = this._util.printx(\"9999999999\", event.value).length >= 10 ? \"(999) 999-9999\" : \"999-9999\";\\n        break;\\n      case 3:\\n        formatStr = \"999-99-9999\";\\n        break;\\n      default:\\n        throw new Error(\"Invalid psf in AFSpecial_Format\");\\n    }\\n    event.value = this._util.printx(formatStr, event.value);\\n  }\\n  AFSpecial_KeystrokeEx(cMask) {\\n    if (!cMask) {\\n      return;\\n    }\\n    const event = globalThis.event;\\n    const value = this.AFMergeChange(event);\\n    if (!value) {\\n      return;\\n    }\\n    const checkers = new Map([[\"9\", char => char >= \"0\" && char <= \"9\"], [\"A\", char => \"a\" <= char && char <= \"z\" || \"A\" <= char && char <= \"Z\"], [\"O\", char => \"a\" <= char && char <= \"z\" || \"A\" <= char && char <= \"Z\" || \"0\" <= char && char <= \"9\"], [\"X\", char => true]]);\\n    function _checkValidity(_value, _cMask) {\\n      for (let i = 0, ii = _value.length; i < ii; i++) {\\n        const mask = _cMask.charAt(i);\\n        const char = _value.charAt(i);\\n        const checker = checkers.get(mask);\\n        if (checker) {\\n          if (!checker(char)) {\\n            return false;\\n          }\\n        } else if (mask !== char) {\\n          return false;\\n        }\\n      }\\n      return true;\\n    }\\n    const err = `${GlobalConstants.IDS_INVALID_VALUE} = \"${cMask}\"`;\\n    if (value.length > cMask.length) {\\n      this._app.alert(err);\\n      event.rc = false;\\n      return;\\n    }\\n    if (event.willCommit) {\\n      if (value.length < cMask.length) {\\n        this._app.alert(err);\\n        event.rc = false;\\n        return;\\n      }\\n      if (!_checkValidity(value, cMask)) {\\n        this._app.alert(err);\\n        event.rc = false;\\n        return;\\n      }\\n      event.value += cMask.substring(value.length);\\n      return;\\n    }\\n    if (value.length < cMask.length) {\\n      cMask = cMask.substring(0, value.length);\\n    }\\n    if (!_checkValidity(value, cMask)) {\\n      this._app.alert(err);\\n      event.rc = false;\\n    }\\n  }\\n  AFSpecial_Keystroke(psf) {\\n    const event = globalThis.event;\\n    psf = this.AFMakeNumber(psf);\\n    let formatStr;\\n    switch (psf) {\\n      case 0:\\n        formatStr = \"99999\";\\n        break;\\n      case 1:\\n        formatStr = \"99999-9999\";\\n        break;\\n      case 2:\\n        const value = this.AFMergeChange(event);\\n        formatStr = value.length > 8 || value.startsWith(\"(\") ? \"(999) 999-9999\" : \"999-9999\";\\n        break;\\n      case 3:\\n        formatStr = \"999-99-9999\";\\n        break;\\n      default:\\n        throw new Error(\"Invalid psf in AFSpecial_Keystroke\");\\n    }\\n    this.AFSpecial_KeystrokeEx(formatStr);\\n  }\\n  AFTime_FormatEx(cFormat) {\\n    this.AFDate_FormatEx(cFormat);\\n  }\\n  AFTime_Format(pdf) {\\n    if (pdf >= 0 && pdf < this._timeFormats.length) {\\n      this.AFDate_FormatEx(this._timeFormats[pdf]);\\n    }\\n  }\\n  AFTime_KeystrokeEx(cFormat) {\\n    this.AFDate_KeystrokeEx(cFormat);\\n  }\\n  AFTime_Keystroke(pdf) {\\n    if (pdf >= 0 && pdf < this._timeFormats.length) {\\n      this.AFDate_KeystrokeEx(this._timeFormats[pdf]);\\n    }\\n  }\\n  eMailValidate(str) {\\n    return this._emailRegex.test(str);\\n  }\\n  AFExactMatch(rePatterns, str) {\\n    if (rePatterns instanceof RegExp) {\\n      return str.match(rePatterns)?.[0] === str || 0;\\n    }\\n    return rePatterns.findIndex(re => str.match(re)?.[0] === str) + 1;\\n  }\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/app_utils.js\\nconst VIEWER_TYPE = \"PDF.js\";\\nconst VIEWER_VARIATION = \"Full\";\\nconst VIEWER_VERSION = 21.00720099;\\nconst FORMS_VERSION = 21.00720099;\\nconst USERACTIVATION_CALLBACKID = 0;\\nconst USERACTIVATION_MAXTIME_VALIDITY = 5000;\\nfunction serializeError(error) {\\n  const value = `${error.toString()}\\\\n${error.stack}`;\\n  return {\\n    command: \"error\",\\n    value\\n  };\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/event.js\\n\\nclass Event {\\n  constructor(data) {\\n    this.change = data.change || \"\";\\n    this.changeEx = data.changeEx || null;\\n    this.commitKey = data.commitKey || 0;\\n    this.fieldFull = data.fieldFull || false;\\n    this.keyDown = data.keyDown || false;\\n    this.modifier = data.modifier || false;\\n    this.name = data.name;\\n    this.rc = true;\\n    this.richChange = data.richChange || [];\\n    this.richChangeEx = data.richChangeEx || [];\\n    this.richValue = data.richValue || [];\\n    this.selEnd = data.selEnd ?? -1;\\n    this.selStart = data.selStart ?? -1;\\n    this.shift = data.shift || false;\\n    this.source = data.source || null;\\n    this.target = data.target || null;\\n    this.targetName = \"\";\\n    this.type = \"Field\";\\n    this.value = data.value || \"\";\\n    this.willCommit = data.willCommit || false;\\n  }\\n}\\nclass EventDispatcher {\\n  constructor(document, calculationOrder, objects, externalCall) {\\n    this._document = document;\\n    this._calculationOrder = calculationOrder;\\n    this._objects = objects;\\n    this._externalCall = externalCall;\\n    this._document.obj._eventDispatcher = this;\\n    this._isCalculating = false;\\n  }\\n  mergeChange(event) {\\n    let value = event.value;\\n    if (Array.isArray(value)) {\\n      return value;\\n    }\\n    if (typeof value !== \"string\") {\\n      value = value.toString();\\n    }\\n    const prefix = event.selStart >= 0 ? value.substring(0, event.selStart) : \"\";\\n    const postfix = event.selEnd >= 0 && event.selEnd <= value.length ? value.substring(event.selEnd) : \"\";\\n    return `${prefix}${event.change}${postfix}`;\\n  }\\n  userActivation() {\\n    this._document.obj._userActivation = true;\\n    this._externalCall(\"setTimeout\", [USERACTIVATION_CALLBACKID, USERACTIVATION_MAXTIME_VALIDITY]);\\n  }\\n  dispatch(baseEvent) {\\n    const id = baseEvent.id;\\n    if (!(id in this._objects)) {\\n      let event;\\n      if (id === \"doc\" || id === \"page\") {\\n        event = globalThis.event = new Event(baseEvent);\\n        event.source = event.target = this._document.wrapped;\\n        event.name = baseEvent.name;\\n      }\\n      if (id === \"doc\") {\\n        const eventName = event.name;\\n        if (eventName === \"Open\") {\\n          this.userActivation();\\n          this._document.obj._initActions();\\n          this.formatAll();\\n        }\\n        if (![\"DidPrint\", \"DidSave\", \"WillPrint\", \"WillSave\"].includes(eventName)) {\\n          this.userActivation();\\n        }\\n        this._document.obj._dispatchDocEvent(event.name);\\n      } else if (id === \"page\") {\\n        this.userActivation();\\n        this._document.obj._dispatchPageEvent(event.name, baseEvent.actions, baseEvent.pageNumber);\\n      } else if (id === \"app\" && baseEvent.name === \"ResetForm\") {\\n        this.userActivation();\\n        for (const fieldId of baseEvent.ids) {\\n          const obj = this._objects[fieldId];\\n          obj?.obj._reset();\\n        }\\n      }\\n      return;\\n    }\\n    const name = baseEvent.name;\\n    const source = this._objects[id];\\n    const event = globalThis.event = new Event(baseEvent);\\n    let savedChange;\\n    this.userActivation();\\n    if (source.obj._isButton()) {\\n      source.obj._id = id;\\n      event.value = source.obj._getExportValue(event.value);\\n      if (name === \"Action\") {\\n        source.obj._value = event.value;\\n      }\\n    }\\n    switch (name) {\\n      case \"Keystroke\":\\n        savedChange = {\\n          value: event.value,\\n          changeEx: event.changeEx,\\n          change: event.change,\\n          selStart: event.selStart,\\n          selEnd: event.selEnd\\n        };\\n        break;\\n      case \"Blur\":\\n      case \"Focus\":\\n        Object.defineProperty(event, \"value\", {\\n          configurable: false,\\n          writable: false,\\n          enumerable: true,\\n          value: event.value\\n        });\\n        break;\\n      case \"Validate\":\\n        this.runValidation(source, event);\\n        return;\\n      case \"Action\":\\n        this.runActions(source, source, event, name);\\n        this.runCalculate(source, event);\\n        return;\\n    }\\n    this.runActions(source, source, event, name);\\n    if (name !== \"Keystroke\") {\\n      return;\\n    }\\n    if (event.rc) {\\n      if (event.willCommit) {\\n        this.runValidation(source, event);\\n      } else {\\n        if (source.obj._isChoice) {\\n          source.obj.value = savedChange.changeEx;\\n          source.obj._send({\\n            id: source.obj._id,\\n            siblings: source.obj._siblings,\\n            value: source.obj.value\\n          });\\n          return;\\n        }\\n        const value = source.obj.value = this.mergeChange(event);\\n        let selStart, selEnd;\\n        if (event.selStart !== savedChange.selStart || event.selEnd !== savedChange.selEnd) {\\n          selStart = event.selStart;\\n          selEnd = event.selEnd;\\n        } else {\\n          selEnd = selStart = savedChange.selStart + event.change.length;\\n        }\\n        source.obj._send({\\n          id: source.obj._id,\\n          siblings: source.obj._siblings,\\n          value,\\n          selRange: [selStart, selEnd]\\n        });\\n      }\\n    } else if (!event.willCommit) {\\n      source.obj._send({\\n        id: source.obj._id,\\n        siblings: source.obj._siblings,\\n        value: savedChange.value,\\n        selRange: [savedChange.selStart, savedChange.selEnd]\\n      });\\n    } else {\\n      source.obj._send({\\n        id: source.obj._id,\\n        siblings: source.obj._siblings,\\n        value: \"\",\\n        formattedValue: null,\\n        selRange: [0, 0]\\n      });\\n    }\\n  }\\n  formatAll() {\\n    const event = globalThis.event = new Event({});\\n    for (const source of Object.values(this._objects)) {\\n      event.value = source.obj.value;\\n      this.runActions(source, source, event, \"Format\");\\n    }\\n  }\\n  runValidation(source, event) {\\n    const didValidateRun = this.runActions(source, source, event, \"Validate\");\\n    if (event.rc) {\\n      source.obj.value = event.value;\\n      this.runCalculate(source, event);\\n      const savedValue = source.obj._getValue();\\n      event.value = source.obj.value;\\n      let formattedValue = null;\\n      if (this.runActions(source, source, event, \"Format\")) {\\n        formattedValue = event.value?.toString?.();\\n      }\\n      source.obj._send({\\n        id: source.obj._id,\\n        siblings: source.obj._siblings,\\n        value: savedValue,\\n        formattedValue\\n      });\\n      event.value = savedValue;\\n    } else if (didValidateRun) {\\n      source.obj._send({\\n        id: source.obj._id,\\n        siblings: source.obj._siblings,\\n        value: \"\",\\n        formattedValue: null,\\n        selRange: [0, 0],\\n        focus: true\\n      });\\n    }\\n  }\\n  runActions(source, target, event, eventName) {\\n    event.source = source.wrapped;\\n    event.target = target.wrapped;\\n    event.name = eventName;\\n    event.targetName = target.obj.name;\\n    event.rc = true;\\n    return target.obj._runActions(event);\\n  }\\n  calculateNow() {\\n    if (!this._calculationOrder || this._isCalculating || !this._document.obj.calculate) {\\n      return;\\n    }\\n    this._isCalculating = true;\\n    const first = this._calculationOrder[0];\\n    const source = this._objects[first];\\n    globalThis.event = new Event({});\\n    try {\\n      this.runCalculate(source, globalThis.event);\\n    } catch (error) {\\n      this._isCalculating = false;\\n      throw error;\\n    }\\n    this._isCalculating = false;\\n  }\\n  runCalculate(source, event) {\\n    if (!this._calculationOrder || !this._document.obj.calculate) {\\n      return;\\n    }\\n    for (const targetId of this._calculationOrder) {\\n      if (!(targetId in this._objects)) {\\n        continue;\\n      }\\n      if (!this._document.obj.calculate) {\\n        break;\\n      }\\n      event.value = null;\\n      const target = this._objects[targetId];\\n      let savedValue = target.obj.value;\\n      this.runActions(source, target, event, \"Calculate\");\\n      if (!event.rc) {\\n        continue;\\n      }\\n      if (event.value !== null) {\\n        target.obj.value = event.value;\\n      }\\n      event.value = target.obj.value;\\n      this.runActions(target, target, event, \"Validate\");\\n      if (!event.rc) {\\n        if (target.obj.value !== savedValue) {\\n          target.wrapped.value = savedValue;\\n        }\\n        continue;\\n      }\\n      savedValue = event.value = target.obj.value;\\n      let formattedValue = null;\\n      if (this.runActions(target, target, event, \"Format\")) {\\n        formattedValue = event.value?.toString?.();\\n      }\\n      target.obj._send({\\n        id: target.obj._id,\\n        siblings: target.obj._siblings,\\n        value: savedValue,\\n        formattedValue\\n      });\\n    }\\n  }\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/fullscreen.js\\n\\n\\nclass FullScreen extends PDFObject {\\n  constructor(data) {\\n    super(data);\\n    this._backgroundColor = [];\\n    this._clickAdvances = true;\\n    this._cursor = Cursor.hidden;\\n    this._defaultTransition = \"\";\\n    this._escapeExits = true;\\n    this._isFullScreen = true;\\n    this._loop = false;\\n    this._timeDelay = 3600;\\n    this._usePageTiming = false;\\n    this._useTimer = false;\\n  }\\n  get backgroundColor() {\\n    return this._backgroundColor;\\n  }\\n  set backgroundColor(_) {}\\n  get clickAdvances() {\\n    return this._clickAdvances;\\n  }\\n  set clickAdvances(_) {}\\n  get cursor() {\\n    return this._cursor;\\n  }\\n  set cursor(_) {}\\n  get defaultTransition() {\\n    return this._defaultTransition;\\n  }\\n  set defaultTransition(_) {}\\n  get escapeExits() {\\n    return this._escapeExits;\\n  }\\n  set escapeExits(_) {}\\n  get isFullScreen() {\\n    return this._isFullScreen;\\n  }\\n  set isFullScreen(_) {}\\n  get loop() {\\n    return this._loop;\\n  }\\n  set loop(_) {}\\n  get timeDelay() {\\n    return this._timeDelay;\\n  }\\n  set timeDelay(_) {}\\n  get transitions() {\\n    return [\"Replace\", \"WipeRight\", \"WipeLeft\", \"WipeDown\", \"WipeUp\", \"SplitHorizontalIn\", \"SplitHorizontalOut\", \"SplitVerticalIn\", \"SplitVerticalOut\", \"BlindsHorizontal\", \"BlindsVertical\", \"BoxIn\", \"BoxOut\", \"GlitterRight\", \"GlitterDown\", \"GlitterRightDown\", \"Dissolve\", \"Random\"];\\n  }\\n  set transitions(_) {\\n    throw new Error(\"fullscreen.transitions is read-only\");\\n  }\\n  get usePageTiming() {\\n    return this._usePageTiming;\\n  }\\n  set usePageTiming(_) {}\\n  get useTimer() {\\n    return this._useTimer;\\n  }\\n  set useTimer(_) {}\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/thermometer.js\\n\\nclass Thermometer extends PDFObject {\\n  constructor(data) {\\n    super(data);\\n    this._cancelled = false;\\n    this._duration = 100;\\n    this._text = \"\";\\n    this._value = 0;\\n  }\\n  get cancelled() {\\n    return this._cancelled;\\n  }\\n  set cancelled(_) {\\n    throw new Error(\"thermometer.cancelled is read-only\");\\n  }\\n  get duration() {\\n    return this._duration;\\n  }\\n  set duration(val) {\\n    this._duration = val;\\n  }\\n  get text() {\\n    return this._text;\\n  }\\n  set text(val) {\\n    this._text = val;\\n  }\\n  get value() {\\n    return this._value;\\n  }\\n  set value(val) {\\n    this._value = val;\\n  }\\n  begin() {}\\n  end() {}\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/app.js\\n\\n\\n\\n\\n\\n\\nclass App extends PDFObject {\\n  constructor(data) {\\n    super(data);\\n    this._constants = null;\\n    this._focusRect = true;\\n    this._fs = null;\\n    this._language = App._getLanguage(data.language);\\n    this._openInPlace = false;\\n    this._platform = App._getPlatform(data.platform);\\n    this._runtimeHighlight = false;\\n    this._runtimeHighlightColor = [\"T\"];\\n    this._thermometer = null;\\n    this._toolbar = false;\\n    this._document = data._document;\\n    this._proxyHandler = data.proxyHandler;\\n    this._objects = Object.create(null);\\n    this._eventDispatcher = new EventDispatcher(this._document, data.calculationOrder, this._objects, data.externalCall);\\n    this._timeoutIds = new WeakMap();\\n    if (typeof FinalizationRegistry !== \"undefined\") {\\n      this._timeoutIdsRegistry = new FinalizationRegistry(this._cleanTimeout.bind(this));\\n    } else {\\n      this._timeoutIdsRegistry = null;\\n    }\\n    this._timeoutCallbackIds = new Map();\\n    this._timeoutCallbackId = USERACTIVATION_CALLBACKID + 1;\\n    this._globalEval = data.globalEval;\\n    this._externalCall = data.externalCall;\\n  }\\n  _dispatchEvent(pdfEvent) {\\n    this._eventDispatcher.dispatch(pdfEvent);\\n  }\\n  _registerTimeoutCallback(cExpr) {\\n    const id = this._timeoutCallbackId++;\\n    this._timeoutCallbackIds.set(id, cExpr);\\n    return id;\\n  }\\n  _unregisterTimeoutCallback(id) {\\n    this._timeoutCallbackIds.delete(id);\\n  }\\n  _evalCallback({\\n    callbackId,\\n    interval\\n  }) {\\n    if (callbackId === USERACTIVATION_CALLBACKID) {\\n      this._document.obj._userActivation = false;\\n      return;\\n    }\\n    const expr = this._timeoutCallbackIds.get(callbackId);\\n    if (!interval) {\\n      this._unregisterTimeoutCallback(callbackId);\\n    }\\n    if (expr) {\\n      this._globalEval(expr);\\n    }\\n  }\\n  _registerTimeout(callbackId, interval) {\\n    const timeout = Object.create(null);\\n    const id = {\\n      callbackId,\\n      interval\\n    };\\n    this._timeoutIds.set(timeout, id);\\n    this._timeoutIdsRegistry?.register(timeout, id);\\n    return timeout;\\n  }\\n  _unregisterTimeout(timeout) {\\n    this._timeoutIdsRegistry?.unregister(timeout);\\n    const data = this._timeoutIds.get(timeout);\\n    if (!data) {\\n      return;\\n    }\\n    this._timeoutIds.delete(timeout);\\n    this._cleanTimeout(data);\\n  }\\n  _cleanTimeout({\\n    callbackId,\\n    interval\\n  }) {\\n    this._unregisterTimeoutCallback(callbackId);\\n    if (interval) {\\n      this._externalCall(\"clearInterval\", [callbackId]);\\n    } else {\\n      this._externalCall(\"clearTimeout\", [callbackId]);\\n    }\\n  }\\n  static _getPlatform(platform) {\\n    if (typeof platform === \"string\") {\\n      platform = platform.toLowerCase();\\n      if (platform.includes(\"win\")) {\\n        return \"WIN\";\\n      } else if (platform.includes(\"mac\")) {\\n        return \"MAC\";\\n      }\\n    }\\n    return \"UNIX\";\\n  }\\n  static _getLanguage(language) {\\n    const [main, sub] = language.toLowerCase().split(/[-_]/);\\n    switch (main) {\\n      case \"zh\":\\n        if (sub === \"cn\" || sub === \"sg\") {\\n          return \"CHS\";\\n        }\\n        return \"CHT\";\\n      case \"da\":\\n        return \"DAN\";\\n      case \"de\":\\n        return \"DEU\";\\n      case \"es\":\\n        return \"ESP\";\\n      case \"fr\":\\n        return \"FRA\";\\n      case \"it\":\\n        return \"ITA\";\\n      case \"ko\":\\n        return \"KOR\";\\n      case \"ja\":\\n        return \"JPN\";\\n      case \"nl\":\\n        return \"NLD\";\\n      case \"no\":\\n        return \"NOR\";\\n      case \"pt\":\\n        if (sub === \"br\") {\\n          return \"PTB\";\\n        }\\n        return \"ENU\";\\n      case \"fi\":\\n        return \"SUO\";\\n      case \"SV\":\\n        return \"SVE\";\\n      default:\\n        return \"ENU\";\\n    }\\n  }\\n  get activeDocs() {\\n    return [this._document.wrapped];\\n  }\\n  set activeDocs(_) {\\n    throw new Error(\"app.activeDocs is read-only\");\\n  }\\n  get calculate() {\\n    return this._document.obj.calculate;\\n  }\\n  set calculate(calculate) {\\n    this._document.obj.calculate = calculate;\\n  }\\n  get constants() {\\n    if (!this._constants) {\\n      this._constants = Object.freeze({\\n        align: Object.freeze({\\n          left: 0,\\n          center: 1,\\n          right: 2,\\n          top: 3,\\n          bottom: 4\\n        })\\n      });\\n    }\\n    return this._constants;\\n  }\\n  set constants(_) {\\n    throw new Error(\"app.constants is read-only\");\\n  }\\n  get focusRect() {\\n    return this._focusRect;\\n  }\\n  set focusRect(val) {\\n    this._focusRect = val;\\n  }\\n  get formsVersion() {\\n    return FORMS_VERSION;\\n  }\\n  set formsVersion(_) {\\n    throw new Error(\"app.formsVersion is read-only\");\\n  }\\n  get fromPDFConverters() {\\n    return [];\\n  }\\n  set fromPDFConverters(_) {\\n    throw new Error(\"app.fromPDFConverters is read-only\");\\n  }\\n  get fs() {\\n    if (this._fs === null) {\\n      this._fs = new Proxy(new FullScreen({\\n        send: this._send\\n      }), this._proxyHandler);\\n    }\\n    return this._fs;\\n  }\\n  set fs(_) {\\n    throw new Error(\"app.fs is read-only\");\\n  }\\n  get language() {\\n    return this._language;\\n  }\\n  set language(_) {\\n    throw new Error(\"app.language is read-only\");\\n  }\\n  get media() {\\n    return undefined;\\n  }\\n  set media(_) {\\n    throw new Error(\"app.media is read-only\");\\n  }\\n  get monitors() {\\n    return [];\\n  }\\n  set monitors(_) {\\n    throw new Error(\"app.monitors is read-only\");\\n  }\\n  get numPlugins() {\\n    return 0;\\n  }\\n  set numPlugins(_) {\\n    throw new Error(\"app.numPlugins is read-only\");\\n  }\\n  get openInPlace() {\\n    return this._openInPlace;\\n  }\\n  set openInPlace(val) {\\n    this._openInPlace = val;\\n  }\\n  get platform() {\\n    return this._platform;\\n  }\\n  set platform(_) {\\n    throw new Error(\"app.platform is read-only\");\\n  }\\n  get plugins() {\\n    return [];\\n  }\\n  set plugins(_) {\\n    throw new Error(\"app.plugins is read-only\");\\n  }\\n  get printColorProfiles() {\\n    return [];\\n  }\\n  set printColorProfiles(_) {\\n    throw new Error(\"app.printColorProfiles is read-only\");\\n  }\\n  get printerNames() {\\n    return [];\\n  }\\n  set printerNames(_) {\\n    throw new Error(\"app.printerNames is read-only\");\\n  }\\n  get runtimeHighlight() {\\n    return this._runtimeHighlight;\\n  }\\n  set runtimeHighlight(val) {\\n    this._runtimeHighlight = val;\\n  }\\n  get runtimeHighlightColor() {\\n    return this._runtimeHighlightColor;\\n  }\\n  set runtimeHighlightColor(val) {\\n    if (Color._isValidColor(val)) {\\n      this._runtimeHighlightColor = val;\\n    }\\n  }\\n  get thermometer() {\\n    if (this._thermometer === null) {\\n      this._thermometer = new Proxy(new Thermometer({\\n        send: this._send\\n      }), this._proxyHandler);\\n    }\\n    return this._thermometer;\\n  }\\n  set thermometer(_) {\\n    throw new Error(\"app.thermometer is read-only\");\\n  }\\n  get toolbar() {\\n    return this._toolbar;\\n  }\\n  set toolbar(val) {\\n    this._toolbar = val;\\n  }\\n  get toolbarHorizontal() {\\n    return this.toolbar;\\n  }\\n  set toolbarHorizontal(value) {\\n    this.toolbar = value;\\n  }\\n  get toolbarVertical() {\\n    return this.toolbar;\\n  }\\n  set toolbarVertical(value) {\\n    this.toolbar = value;\\n  }\\n  get viewerType() {\\n    return VIEWER_TYPE;\\n  }\\n  set viewerType(_) {\\n    throw new Error(\"app.viewerType is read-only\");\\n  }\\n  get viewerVariation() {\\n    return VIEWER_VARIATION;\\n  }\\n  set viewerVariation(_) {\\n    throw new Error(\"app.viewerVariation is read-only\");\\n  }\\n  get viewerVersion() {\\n    return VIEWER_VERSION;\\n  }\\n  set viewerVersion(_) {\\n    throw new Error(\"app.viewerVersion is read-only\");\\n  }\\n  addMenuItem() {}\\n  addSubMenu() {}\\n  addToolButton() {}\\n  alert(cMsg, nIcon = 0, nType = 0, cTitle = \"PDF.js\", oDoc = null, oCheckbox = null) {\\n    if (!this._document.obj._userActivation) {\\n      return 0;\\n    }\\n    this._document.obj._userActivation = false;\\n    if (cMsg && typeof cMsg === \"object\") {\\n      nType = cMsg.nType;\\n      cMsg = cMsg.cMsg;\\n    }\\n    cMsg = (cMsg || \"\").toString();\\n    nType = typeof nType !== \"number\" || isNaN(nType) || nType < 0 || nType > 3 ? 0 : nType;\\n    if (nType >= 2) {\\n      return this._externalCall(\"confirm\", [cMsg]) ? 4 : 3;\\n    }\\n    this._externalCall(\"alert\", [cMsg]);\\n    return 1;\\n  }\\n  beep() {}\\n  beginPriv() {}\\n  browseForDoc() {}\\n  clearInterval(oInterval) {\\n    this._unregisterTimeout(oInterval);\\n  }\\n  clearTimeOut(oTime) {\\n    this._unregisterTimeout(oTime);\\n  }\\n  endPriv() {}\\n  execDialog() {}\\n  execMenuItem(item) {\\n    if (!this._document.obj._userActivation) {\\n      return;\\n    }\\n    this._document.obj._userActivation = false;\\n    switch (item) {\\n      case \"SaveAs\":\\n        if (this._document.obj._disableSaving) {\\n          return;\\n        }\\n        this._send({\\n          command: item\\n        });\\n        break;\\n      case \"FirstPage\":\\n      case \"LastPage\":\\n      case \"NextPage\":\\n      case \"PrevPage\":\\n      case \"ZoomViewIn\":\\n      case \"ZoomViewOut\":\\n        this._send({\\n          command: item\\n        });\\n        break;\\n      case \"FitPage\":\\n        this._send({\\n          command: \"zoom\",\\n          value: \"page-fit\"\\n        });\\n        break;\\n      case \"Print\":\\n        if (this._document.obj._disablePrinting) {\\n          return;\\n        }\\n        this._send({\\n          command: \"print\"\\n        });\\n        break;\\n    }\\n  }\\n  getNthPlugInName() {}\\n  getPath() {}\\n  goBack() {}\\n  goForward() {}\\n  hideMenuItem() {}\\n  hideToolbarButton() {}\\n  launchURL() {}\\n  listMenuItems() {}\\n  listToolbarButtons() {}\\n  loadPolicyFile() {}\\n  mailGetAddrs() {}\\n  mailMsg() {}\\n  newDoc() {}\\n  newCollection() {}\\n  newFDF() {}\\n  openDoc() {}\\n  openFDF() {}\\n  popUpMenu() {}\\n  popUpMenuEx() {}\\n  removeToolButton() {}\\n  response(cQuestion, cTitle = \"\", cDefault = \"\", bPassword = \"\", cLabel = \"\") {\\n    if (cQuestion && typeof cQuestion === \"object\") {\\n      cDefault = cQuestion.cDefault;\\n      cQuestion = cQuestion.cQuestion;\\n    }\\n    cQuestion = (cQuestion || \"\").toString();\\n    cDefault = (cDefault || \"\").toString();\\n    return this._externalCall(\"prompt\", [cQuestion, cDefault || \"\"]);\\n  }\\n  setInterval(cExpr, nMilliseconds = 0) {\\n    if (cExpr && typeof cExpr === \"object\") {\\n      nMilliseconds = cExpr.nMilliseconds || 0;\\n      cExpr = cExpr.cExpr;\\n    }\\n    if (typeof cExpr !== \"string\") {\\n      throw new TypeError(\"First argument of app.setInterval must be a string\");\\n    }\\n    if (typeof nMilliseconds !== \"number\") {\\n      throw new TypeError(\"Second argument of app.setInterval must be a number\");\\n    }\\n    const callbackId = this._registerTimeoutCallback(cExpr);\\n    this._externalCall(\"setInterval\", [callbackId, nMilliseconds]);\\n    return this._registerTimeout(callbackId, true);\\n  }\\n  setTimeOut(cExpr, nMilliseconds = 0) {\\n    if (cExpr && typeof cExpr === \"object\") {\\n      nMilliseconds = cExpr.nMilliseconds || 0;\\n      cExpr = cExpr.cExpr;\\n    }\\n    if (typeof cExpr !== \"string\") {\\n      throw new TypeError(\"First argument of app.setTimeOut must be a string\");\\n    }\\n    if (typeof nMilliseconds !== \"number\") {\\n      throw new TypeError(\"Second argument of app.setTimeOut must be a number\");\\n    }\\n    const callbackId = this._registerTimeoutCallback(cExpr);\\n    this._externalCall(\"setTimeout\", [callbackId, nMilliseconds]);\\n    return this._registerTimeout(callbackId, false);\\n  }\\n  trustedFunction() {}\\n  trustPropagatorFunction() {}\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/console.js\\n\\nclass Console extends PDFObject {\\n  clear() {\\n    this._send({\\n      id: \"clear\"\\n    });\\n  }\\n  hide() {}\\n  println(msg) {\\n    if (typeof msg === \"string\") {\\n      this._send({\\n        command: \"println\",\\n        value: \"PDF.js Console:: \" + msg\\n      });\\n    }\\n  }\\n  show() {}\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/print_params.js\\nclass PrintParams {\\n  constructor(data) {\\n    this.binaryOk = true;\\n    this.bitmapDPI = 150;\\n    this.booklet = {\\n      binding: 0,\\n      duplexMode: 0,\\n      subsetFrom: 0,\\n      subsetTo: -1\\n    };\\n    this.colorOverride = 0;\\n    this.colorProfile = \"\";\\n    this.constants = Object.freeze({\\n      bookletBindings: Object.freeze({\\n        Left: 0,\\n        Right: 1,\\n        LeftTall: 2,\\n        RightTall: 3\\n      }),\\n      bookletDuplexMode: Object.freeze({\\n        BothSides: 0,\\n        FrontSideOnly: 1,\\n        BasicSideOnly: 2\\n      }),\\n      colorOverrides: Object.freeze({\\n        auto: 0,\\n        gray: 1,\\n        mono: 2\\n      }),\\n      fontPolicies: Object.freeze({\\n        everyPage: 0,\\n        jobStart: 1,\\n        pageRange: 2\\n      }),\\n      handling: Object.freeze({\\n        none: 0,\\n        fit: 1,\\n        shrink: 2,\\n        tileAll: 3,\\n        tileLarge: 4,\\n        nUp: 5,\\n        booklet: 6\\n      }),\\n      interactionLevel: Object.freeze({\\n        automatic: 0,\\n        full: 1,\\n        silent: 2\\n      }),\\n      nUpPageOrders: Object.freeze({\\n        Horizontal: 0,\\n        HorizontalReversed: 1,\\n        Vertical: 2\\n      }),\\n      printContents: Object.freeze({\\n        doc: 0,\\n        docAndComments: 1,\\n        formFieldsOnly: 2\\n      }),\\n      flagValues: Object.freeze({\\n        applyOverPrint: 1,\\n        applySoftProofSettings: 1 << 1,\\n        applyWorkingColorSpaces: 1 << 2,\\n        emitHalftones: 1 << 3,\\n        emitPostScriptXObjects: 1 << 4,\\n        emitFormsAsPSForms: 1 << 5,\\n        maxJP2KRes: 1 << 6,\\n        setPageSize: 1 << 7,\\n        suppressBG: 1 << 8,\\n        suppressCenter: 1 << 9,\\n        suppressCJKFontSubst: 1 << 10,\\n        suppressCropClip: 1 << 1,\\n        suppressRotate: 1 << 12,\\n        suppressTransfer: 1 << 13,\\n        suppressUCR: 1 << 14,\\n        useTrapAnnots: 1 << 15,\\n        usePrintersMarks: 1 << 16\\n      }),\\n      rasterFlagValues: Object.freeze({\\n        textToOutline: 1,\\n        strokesToOutline: 1 << 1,\\n        allowComplexClip: 1 << 2,\\n        preserveOverprint: 1 << 3\\n      }),\\n      subsets: Object.freeze({\\n        all: 0,\\n        even: 1,\\n        odd: 2\\n      }),\\n      tileMarks: Object.freeze({\\n        none: 0,\\n        west: 1,\\n        east: 2\\n      }),\\n      usages: Object.freeze({\\n        auto: 0,\\n        use: 1,\\n        noUse: 2\\n      })\\n    });\\n    this.downloadFarEastFonts = false;\\n    this.fileName = \"\";\\n    this.firstPage = 0;\\n    this.flags = 0;\\n    this.fontPolicy = 0;\\n    this.gradientDPI = 150;\\n    this.interactive = 1;\\n    this.lastPage = data.lastPage;\\n    this.npUpAutoRotate = false;\\n    this.npUpNumPagesH = 2;\\n    this.npUpNumPagesV = 2;\\n    this.npUpPageBorder = false;\\n    this.npUpPageOrder = 0;\\n    this.pageHandling = 0;\\n    this.pageSubset = 0;\\n    this.printAsImage = false;\\n    this.printContent = 0;\\n    this.printerName = \"\";\\n    this.psLevel = 0;\\n    this.rasterFlags = 0;\\n    this.reversePages = false;\\n    this.tileLabel = false;\\n    this.tileMark = 0;\\n    this.tileOverlap = 0;\\n    this.tileScale = 1.0;\\n    this.transparencyLevel = 75;\\n    this.usePrinterCRD = 0;\\n    this.useT1Conversion = 0;\\n  }\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/doc.js\\n\\n\\n\\n\\n\\nconst DOC_EXTERNAL = false;\\nclass InfoProxyHandler {\\n  static get(obj, prop) {\\n    return obj[prop.toLowerCase()];\\n  }\\n  static set(obj, prop, value) {\\n    throw new Error(`doc.info.${prop} is read-only`);\\n  }\\n}\\nclass Doc extends PDFObject {\\n  constructor(data) {\\n    super(data);\\n    this._expandos = globalThis;\\n    this._baseURL = data.baseURL || \"\";\\n    this._calculate = true;\\n    this._delay = false;\\n    this._dirty = false;\\n    this._disclosed = false;\\n    this._media = undefined;\\n    this._metadata = data.metadata || \"\";\\n    this._noautocomplete = undefined;\\n    this._nocache = undefined;\\n    this._spellDictionaryOrder = [];\\n    this._spellLanguageOrder = [];\\n    this._printParams = null;\\n    this._fields = new Map();\\n    this._fieldNames = [];\\n    this._event = null;\\n    this._author = data.Author || \"\";\\n    this._creator = data.Creator || \"\";\\n    this._creationDate = this._getDate(data.CreationDate) || null;\\n    this._docID = data.docID || [\"\", \"\"];\\n    this._documentFileName = data.filename || \"\";\\n    this._filesize = data.filesize || 0;\\n    this._keywords = data.Keywords || \"\";\\n    this._layout = data.layout || \"\";\\n    this._modDate = this._getDate(data.ModDate) || null;\\n    this._numFields = 0;\\n    this._numPages = data.numPages || 1;\\n    this._pageNum = data.pageNum || 0;\\n    this._producer = data.Producer || \"\";\\n    this._securityHandler = data.EncryptFilterName || null;\\n    this._subject = data.Subject || \"\";\\n    this._title = data.Title || \"\";\\n    this._URL = data.URL || \"\";\\n    this._info = new Proxy({\\n      title: this._title,\\n      author: this._author,\\n      authors: data.authors || [this._author],\\n      subject: this._subject,\\n      keywords: this._keywords,\\n      creator: this._creator,\\n      producer: this._producer,\\n      creationdate: this._creationDate,\\n      moddate: this._modDate,\\n      trapped: data.Trapped || \"Unknown\"\\n    }, InfoProxyHandler);\\n    this._zoomType = ZoomType.none;\\n    this._zoom = data.zoom || 100;\\n    this._actions = createActionsMap(data.actions);\\n    this._globalEval = data.globalEval;\\n    this._pageActions = new Map();\\n    this._userActivation = false;\\n    this._disablePrinting = false;\\n    this._disableSaving = false;\\n  }\\n  _initActions() {\\n    const dontRun = new Set([\"WillClose\", \"WillSave\", \"DidSave\", \"WillPrint\", \"DidPrint\", \"OpenAction\"]);\\n    this._disableSaving = true;\\n    for (const actionName of this._actions.keys()) {\\n      if (!dontRun.has(actionName)) {\\n        this._runActions(actionName);\\n      }\\n    }\\n    this._runActions(\"OpenAction\");\\n    this._disableSaving = false;\\n  }\\n  _dispatchDocEvent(name) {\\n    switch (name) {\\n      case \"Open\":\\n        this._disableSaving = true;\\n        this._runActions(\"OpenAction\");\\n        this._disableSaving = false;\\n        break;\\n      case \"WillPrint\":\\n        this._disablePrinting = true;\\n        try {\\n          this._runActions(name);\\n        } catch (error) {\\n          this._send(serializeError(error));\\n        }\\n        this._send({\\n          command: \"WillPrintFinished\"\\n        });\\n        this._disablePrinting = false;\\n        break;\\n      case \"WillSave\":\\n        this._disableSaving = true;\\n        this._runActions(name);\\n        this._disableSaving = false;\\n        break;\\n      default:\\n        this._runActions(name);\\n    }\\n  }\\n  _dispatchPageEvent(name, actions, pageNumber) {\\n    if (name === \"PageOpen\") {\\n      if (!this._pageActions.has(pageNumber)) {\\n        this._pageActions.set(pageNumber, createActionsMap(actions));\\n      }\\n      this._pageNum = pageNumber - 1;\\n    }\\n    actions = this._pageActions.get(pageNumber)?.get(name);\\n    if (actions) {\\n      for (const action of actions) {\\n        this._globalEval(action);\\n      }\\n    }\\n  }\\n  _runActions(name) {\\n    const actions = this._actions.get(name);\\n    if (actions) {\\n      for (const action of actions) {\\n        this._globalEval(action);\\n      }\\n    }\\n  }\\n  _addField(name, field) {\\n    this._fields.set(name, field);\\n    this._fieldNames.push(name);\\n    this._numFields++;\\n  }\\n  _getDate(date) {\\n    if (!date || date.length < 15 || !date.startsWith(\"D:\")) {\\n      return date;\\n    }\\n    date = date.substring(2);\\n    const year = date.substring(0, 4);\\n    const month = date.substring(4, 6);\\n    const day = date.substring(6, 8);\\n    const hour = date.substring(8, 10);\\n    const minute = date.substring(10, 12);\\n    const o = date.charAt(12);\\n    let second, offsetPos;\\n    if (o === \"Z\" || o === \"+\" || o === \"-\") {\\n      second = \"00\";\\n      offsetPos = 12;\\n    } else {\\n      second = date.substring(12, 14);\\n      offsetPos = 14;\\n    }\\n    const offset = date.substring(offsetPos).replaceAll(\"\\'\", \"\");\\n    return new Date(`${year}-${month}-${day}T${hour}:${minute}:${second}${offset}`);\\n  }\\n  get author() {\\n    return this._author;\\n  }\\n  set author(_) {\\n    throw new Error(\"doc.author is read-only\");\\n  }\\n  get baseURL() {\\n    return this._baseURL;\\n  }\\n  set baseURL(baseURL) {\\n    this._baseURL = baseURL;\\n  }\\n  get bookmarkRoot() {\\n    return undefined;\\n  }\\n  set bookmarkRoot(_) {\\n    throw new Error(\"doc.bookmarkRoot is read-only\");\\n  }\\n  get calculate() {\\n    return this._calculate;\\n  }\\n  set calculate(calculate) {\\n    this._calculate = calculate;\\n  }\\n  get creator() {\\n    return this._creator;\\n  }\\n  set creator(_) {\\n    throw new Error(\"doc.creator is read-only\");\\n  }\\n  get dataObjects() {\\n    return [];\\n  }\\n  set dataObjects(_) {\\n    throw new Error(\"doc.dataObjects is read-only\");\\n  }\\n  get delay() {\\n    return this._delay;\\n  }\\n  set delay(delay) {\\n    this._delay = delay;\\n  }\\n  get dirty() {\\n    return this._dirty;\\n  }\\n  set dirty(dirty) {\\n    this._dirty = dirty;\\n  }\\n  get disclosed() {\\n    return this._disclosed;\\n  }\\n  set disclosed(disclosed) {\\n    this._disclosed = disclosed;\\n  }\\n  get docID() {\\n    return this._docID;\\n  }\\n  set docID(_) {\\n    throw new Error(\"doc.docID is read-only\");\\n  }\\n  get documentFileName() {\\n    return this._documentFileName;\\n  }\\n  set documentFileName(_) {\\n    throw new Error(\"doc.documentFileName is read-only\");\\n  }\\n  get dynamicXFAForm() {\\n    return false;\\n  }\\n  set dynamicXFAForm(_) {\\n    throw new Error(\"doc.dynamicXFAForm is read-only\");\\n  }\\n  get external() {\\n    return DOC_EXTERNAL;\\n  }\\n  set external(_) {\\n    throw new Error(\"doc.external is read-only\");\\n  }\\n  get filesize() {\\n    return this._filesize;\\n  }\\n  set filesize(_) {\\n    throw new Error(\"doc.filesize is read-only\");\\n  }\\n  get hidden() {\\n    return false;\\n  }\\n  set hidden(_) {\\n    throw new Error(\"doc.hidden is read-only\");\\n  }\\n  get hostContainer() {\\n    return undefined;\\n  }\\n  set hostContainer(_) {\\n    throw new Error(\"doc.hostContainer is read-only\");\\n  }\\n  get icons() {\\n    return undefined;\\n  }\\n  set icons(_) {\\n    throw new Error(\"doc.icons is read-only\");\\n  }\\n  get info() {\\n    return this._info;\\n  }\\n  set info(_) {\\n    throw new Error(\"doc.info is read-only\");\\n  }\\n  get innerAppWindowRect() {\\n    return [0, 0, 0, 0];\\n  }\\n  set innerAppWindowRect(_) {\\n    throw new Error(\"doc.innerAppWindowRect is read-only\");\\n  }\\n  get innerDocWindowRect() {\\n    return [0, 0, 0, 0];\\n  }\\n  set innerDocWindowRect(_) {\\n    throw new Error(\"doc.innerDocWindowRect is read-only\");\\n  }\\n  get isModal() {\\n    return false;\\n  }\\n  set isModal(_) {\\n    throw new Error(\"doc.isModal is read-only\");\\n  }\\n  get keywords() {\\n    return this._keywords;\\n  }\\n  set keywords(_) {\\n    throw new Error(\"doc.keywords is read-only\");\\n  }\\n  get layout() {\\n    return this._layout;\\n  }\\n  set layout(value) {\\n    if (!this._userActivation) {\\n      return;\\n    }\\n    this._userActivation = false;\\n    if (typeof value !== \"string\") {\\n      return;\\n    }\\n    if (value !== \"SinglePage\" && value !== \"OneColumn\" && value !== \"TwoColumnLeft\" && value !== \"TwoPageLeft\" && value !== \"TwoColumnRight\" && value !== \"TwoPageRight\") {\\n      value = \"SinglePage\";\\n    }\\n    this._send({\\n      command: \"layout\",\\n      value\\n    });\\n    this._layout = value;\\n  }\\n  get media() {\\n    return this._media;\\n  }\\n  set media(media) {\\n    this._media = media;\\n  }\\n  get metadata() {\\n    return this._metadata;\\n  }\\n  set metadata(metadata) {\\n    this._metadata = metadata;\\n  }\\n  get modDate() {\\n    return this._modDate;\\n  }\\n  set modDate(_) {\\n    throw new Error(\"doc.modDate is read-only\");\\n  }\\n  get mouseX() {\\n    return 0;\\n  }\\n  set mouseX(_) {\\n    throw new Error(\"doc.mouseX is read-only\");\\n  }\\n  get mouseY() {\\n    return 0;\\n  }\\n  set mouseY(_) {\\n    throw new Error(\"doc.mouseY is read-only\");\\n  }\\n  get noautocomplete() {\\n    return this._noautocomplete;\\n  }\\n  set noautocomplete(noautocomplete) {\\n    this._noautocomplete = noautocomplete;\\n  }\\n  get nocache() {\\n    return this._nocache;\\n  }\\n  set nocache(nocache) {\\n    this._nocache = nocache;\\n  }\\n  get numFields() {\\n    return this._numFields;\\n  }\\n  set numFields(_) {\\n    throw new Error(\"doc.numFields is read-only\");\\n  }\\n  get numPages() {\\n    return this._numPages;\\n  }\\n  set numPages(_) {\\n    throw new Error(\"doc.numPages is read-only\");\\n  }\\n  get numTemplates() {\\n    return 0;\\n  }\\n  set numTemplates(_) {\\n    throw new Error(\"doc.numTemplates is read-only\");\\n  }\\n  get outerAppWindowRect() {\\n    return [0, 0, 0, 0];\\n  }\\n  set outerAppWindowRect(_) {\\n    throw new Error(\"doc.outerAppWindowRect is read-only\");\\n  }\\n  get outerDocWindowRect() {\\n    return [0, 0, 0, 0];\\n  }\\n  set outerDocWindowRect(_) {\\n    throw new Error(\"doc.outerDocWindowRect is read-only\");\\n  }\\n  get pageNum() {\\n    return this._pageNum;\\n  }\\n  set pageNum(value) {\\n    if (!this._userActivation) {\\n      return;\\n    }\\n    this._userActivation = false;\\n    if (typeof value !== \"number\" || value < 0 || value >= this._numPages) {\\n      return;\\n    }\\n    this._send({\\n      command: \"page-num\",\\n      value\\n    });\\n    this._pageNum = value;\\n  }\\n  get pageWindowRect() {\\n    return [0, 0, 0, 0];\\n  }\\n  set pageWindowRect(_) {\\n    throw new Error(\"doc.pageWindowRect is read-only\");\\n  }\\n  get path() {\\n    return \"\";\\n  }\\n  set path(_) {\\n    throw new Error(\"doc.path is read-only\");\\n  }\\n  get permStatusReady() {\\n    return true;\\n  }\\n  set permStatusReady(_) {\\n    throw new Error(\"doc.permStatusReady is read-only\");\\n  }\\n  get producer() {\\n    return this._producer;\\n  }\\n  set producer(_) {\\n    throw new Error(\"doc.producer is read-only\");\\n  }\\n  get requiresFullSave() {\\n    return false;\\n  }\\n  set requiresFullSave(_) {\\n    throw new Error(\"doc.requiresFullSave is read-only\");\\n  }\\n  get securityHandler() {\\n    return this._securityHandler;\\n  }\\n  set securityHandler(_) {\\n    throw new Error(\"doc.securityHandler is read-only\");\\n  }\\n  get selectedAnnots() {\\n    return [];\\n  }\\n  set selectedAnnots(_) {\\n    throw new Error(\"doc.selectedAnnots is read-only\");\\n  }\\n  get sounds() {\\n    return [];\\n  }\\n  set sounds(_) {\\n    throw new Error(\"doc.sounds is read-only\");\\n  }\\n  get spellDictionaryOrder() {\\n    return this._spellDictionaryOrder;\\n  }\\n  set spellDictionaryOrder(spellDictionaryOrder) {\\n    this._spellDictionaryOrder = spellDictionaryOrder;\\n  }\\n  get spellLanguageOrder() {\\n    return this._spellLanguageOrder;\\n  }\\n  set spellLanguageOrder(spellLanguageOrder) {\\n    this._spellLanguageOrder = spellLanguageOrder;\\n  }\\n  get subject() {\\n    return this._subject;\\n  }\\n  set subject(_) {\\n    throw new Error(\"doc.subject is read-only\");\\n  }\\n  get templates() {\\n    return [];\\n  }\\n  set templates(_) {\\n    throw new Error(\"doc.templates is read-only\");\\n  }\\n  get title() {\\n    return this._title;\\n  }\\n  set title(_) {\\n    throw new Error(\"doc.title is read-only\");\\n  }\\n  get URL() {\\n    return this._URL;\\n  }\\n  set URL(_) {\\n    throw new Error(\"doc.URL is read-only\");\\n  }\\n  get viewState() {\\n    return undefined;\\n  }\\n  set viewState(_) {\\n    throw new Error(\"doc.viewState is read-only\");\\n  }\\n  get xfa() {\\n    return this._xfa;\\n  }\\n  set xfa(_) {\\n    throw new Error(\"doc.xfa is read-only\");\\n  }\\n  get XFAForeground() {\\n    return false;\\n  }\\n  set XFAForeground(_) {\\n    throw new Error(\"doc.XFAForeground is read-only\");\\n  }\\n  get zoomType() {\\n    return this._zoomType;\\n  }\\n  set zoomType(type) {\\n    if (!this._userActivation) {\\n      return;\\n    }\\n    this._userActivation = false;\\n    if (typeof type !== \"string\") {\\n      return;\\n    }\\n    switch (type) {\\n      case ZoomType.none:\\n        this._send({\\n          command: \"zoom\",\\n          value: 1\\n        });\\n        break;\\n      case ZoomType.fitP:\\n        this._send({\\n          command: \"zoom\",\\n          value: \"page-fit\"\\n        });\\n        break;\\n      case ZoomType.fitW:\\n        this._send({\\n          command: \"zoom\",\\n          value: \"page-width\"\\n        });\\n        break;\\n      case ZoomType.fitH:\\n        this._send({\\n          command: \"zoom\",\\n          value: \"page-height\"\\n        });\\n        break;\\n      case ZoomType.fitV:\\n        this._send({\\n          command: \"zoom\",\\n          value: \"auto\"\\n        });\\n        break;\\n      case ZoomType.pref:\\n      case ZoomType.refW:\\n        break;\\n      default:\\n        return;\\n    }\\n    this._zoomType = type;\\n  }\\n  get zoom() {\\n    return this._zoom;\\n  }\\n  set zoom(value) {\\n    if (!this._userActivation) {\\n      return;\\n    }\\n    this._userActivation = false;\\n    if (typeof value !== \"number\" || value < 8.33 || value > 6400) {\\n      return;\\n    }\\n    this._send({\\n      command: \"zoom\",\\n      value: value / 100\\n    });\\n  }\\n  addAnnot() {}\\n  addField() {}\\n  addIcon() {}\\n  addLink() {}\\n  addRecipientListCryptFilter() {}\\n  addRequirement() {}\\n  addScript() {}\\n  addThumbnails() {}\\n  addWatermarkFromFile() {}\\n  addWatermarkFromText() {}\\n  addWeblinks() {}\\n  bringToFront() {}\\n  calculateNow() {\\n    this._eventDispatcher.calculateNow();\\n  }\\n  closeDoc() {}\\n  colorConvertPage() {}\\n  createDataObject() {}\\n  createTemplate() {}\\n  deletePages() {}\\n  deleteSound() {}\\n  embedDocAsDataObject() {}\\n  embedOutputIntent() {}\\n  encryptForRecipients() {}\\n  encryptUsingPolicy() {}\\n  exportAsFDF() {}\\n  exportAsFDFStr() {}\\n  exportAsText() {}\\n  exportAsXFDF() {}\\n  exportAsXFDFStr() {}\\n  exportDataObject() {}\\n  exportXFAData() {}\\n  extractPages() {}\\n  flattenPages() {}\\n  getAnnot() {}\\n  getAnnots() {}\\n  getAnnot3D() {}\\n  getAnnots3D() {}\\n  getColorConvertAction() {}\\n  getDataObject() {}\\n  getDataObjectContents() {}\\n  _getField(cName) {\\n    if (cName && typeof cName === \"object\") {\\n      cName = cName.cName;\\n    }\\n    if (typeof cName !== \"string\") {\\n      throw new TypeError(\"Invalid field name: must be a string\");\\n    }\\n    const searchedField = this._fields.get(cName);\\n    if (searchedField) {\\n      return searchedField;\\n    }\\n    const parts = cName.split(\"#\");\\n    let childIndex = NaN;\\n    if (parts.length === 2) {\\n      childIndex = Math.floor(parseFloat(parts[1]));\\n      cName = parts[0];\\n    }\\n    for (const [name, field] of this._fields.entries()) {\\n      if (name.endsWith(cName)) {\\n        if (!isNaN(childIndex)) {\\n          const children = this._getChildren(name);\\n          if (childIndex < 0 || childIndex >= children.length) {\\n            childIndex = 0;\\n          }\\n          if (childIndex < children.length) {\\n            this._fields.set(cName, children[childIndex]);\\n            return children[childIndex];\\n          }\\n        }\\n        this._fields.set(cName, field);\\n        return field;\\n      }\\n    }\\n    return null;\\n  }\\n  getField(cName) {\\n    const field = this._getField(cName);\\n    if (!field) {\\n      return null;\\n    }\\n    return field.wrapped;\\n  }\\n  _getChildren(fieldName) {\\n    const len = fieldName.length;\\n    const children = [];\\n    const pattern = /^\\\\.[^.]+$/;\\n    for (const [name, field] of this._fields.entries()) {\\n      if (name.startsWith(fieldName)) {\\n        const finalPart = name.slice(len);\\n        if (pattern.test(finalPart)) {\\n          children.push(field);\\n        }\\n      }\\n    }\\n    return children;\\n  }\\n  _getTerminalChildren(fieldName) {\\n    const children = [];\\n    const len = fieldName.length;\\n    for (const [name, field] of this._fields.entries()) {\\n      if (name.startsWith(fieldName)) {\\n        const finalPart = name.slice(len);\\n        if (field.obj._hasValue && (finalPart === \"\" || finalPart.startsWith(\".\"))) {\\n          children.push(field.wrapped);\\n        }\\n      }\\n    }\\n    return children;\\n  }\\n  getIcon() {}\\n  getLegalWarnings() {}\\n  getLinks() {}\\n  getNthFieldName(nIndex) {\\n    if (nIndex && typeof nIndex === \"object\") {\\n      nIndex = nIndex.nIndex;\\n    }\\n    if (typeof nIndex !== \"number\") {\\n      throw new TypeError(\"Invalid field index: must be a number\");\\n    }\\n    if (0 <= nIndex && nIndex < this.numFields) {\\n      return this._fieldNames[Math.trunc(nIndex)];\\n    }\\n    return null;\\n  }\\n  getNthTemplate() {\\n    return null;\\n  }\\n  getOCGs() {}\\n  getOCGOrder() {}\\n  getPageBox() {}\\n  getPageLabel() {}\\n  getPageNthWord() {}\\n  getPageNthWordQuads() {}\\n  getPageNumWords() {}\\n  getPageRotation() {}\\n  getPageTransition() {}\\n  getPrintParams() {\\n    return this._printParams ||= new PrintParams({\\n      lastPage: this._numPages - 1\\n    });\\n  }\\n  getSound() {}\\n  getTemplate() {}\\n  getURL() {}\\n  gotoNamedDest() {}\\n  importAnFDF() {}\\n  importAnXFDF() {}\\n  importDataObject() {}\\n  importIcon() {}\\n  importSound() {}\\n  importTextData() {}\\n  importXFAData() {}\\n  insertPages() {}\\n  mailDoc() {}\\n  mailForm() {}\\n  movePage() {}\\n  newPage() {}\\n  openDataObject() {}\\n  print(bUI = true, nStart = 0, nEnd = -1, bSilent = false, bShrinkToFit = false, bPrintAsImage = false, bReverse = false, bAnnotations = true, printParams = null) {\\n    if (this._disablePrinting || !this._userActivation) {\\n      return;\\n    }\\n    this._userActivation = false;\\n    if (bUI && typeof bUI === \"object\") {\\n      nStart = bUI.nStart;\\n      nEnd = bUI.nEnd;\\n      bSilent = bUI.bSilent;\\n      bShrinkToFit = bUI.bShrinkToFit;\\n      bPrintAsImage = bUI.bPrintAsImage;\\n      bReverse = bUI.bReverse;\\n      bAnnotations = bUI.bAnnotations;\\n      printParams = bUI.printParams;\\n      bUI = bUI.bUI;\\n    }\\n    if (printParams) {\\n      nStart = printParams.firstPage;\\n      nEnd = printParams.lastPage;\\n    }\\n    nStart = typeof nStart === \"number\" ? Math.max(0, Math.trunc(nStart)) : 0;\\n    nEnd = typeof nEnd === \"number\" ? Math.max(0, Math.trunc(nEnd)) : -1;\\n    this._send({\\n      command: \"print\",\\n      start: nStart,\\n      end: nEnd\\n    });\\n  }\\n  removeDataObject() {}\\n  removeField() {}\\n  removeIcon() {}\\n  removeLinks() {}\\n  removeRequirement() {}\\n  removeScript() {}\\n  removeTemplate() {}\\n  removeThumbnails() {}\\n  removeWeblinks() {}\\n  replacePages() {}\\n  resetForm(aFields = null) {\\n    if (aFields && typeof aFields === \"object\" && !Array.isArray(aFields)) {\\n      aFields = aFields.aFields;\\n    }\\n    if (aFields && !Array.isArray(aFields)) {\\n      aFields = [aFields];\\n    }\\n    let mustCalculate = false;\\n    let fieldsToReset;\\n    if (aFields) {\\n      fieldsToReset = [];\\n      for (const fieldName of aFields) {\\n        if (!fieldName) {\\n          continue;\\n        }\\n        if (typeof fieldName !== \"string\") {\\n          fieldsToReset = null;\\n          break;\\n        }\\n        const field = this._getField(fieldName);\\n        if (!field) {\\n          continue;\\n        }\\n        fieldsToReset.push(field);\\n        mustCalculate = true;\\n      }\\n    }\\n    if (!fieldsToReset) {\\n      fieldsToReset = this._fields.values();\\n      mustCalculate = this._fields.size !== 0;\\n    }\\n    for (const field of fieldsToReset) {\\n      field.obj.value = field.obj.defaultValue;\\n      this._send({\\n        id: field.obj._id,\\n        siblings: field.obj._siblings,\\n        value: field.obj.defaultValue,\\n        formattedValue: null,\\n        selRange: [0, 0]\\n      });\\n    }\\n    if (mustCalculate) {\\n      this.calculateNow();\\n    }\\n  }\\n  saveAs() {}\\n  scroll() {}\\n  selectPageNthWord() {}\\n  setAction() {}\\n  setDataObjectContents() {}\\n  setOCGOrder() {}\\n  setPageAction() {}\\n  setPageBoxes() {}\\n  setPageLabels() {}\\n  setPageRotations() {}\\n  setPageTabOrder() {}\\n  setPageTransitions() {}\\n  spawnPageFromTemplate() {}\\n  submitForm() {}\\n  syncAnnotScan() {}\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/proxy.js\\nclass ProxyHandler {\\n  constructor() {\\n    this.nosend = new Set([\"delay\"]);\\n  }\\n  get(obj, prop) {\\n    if (prop in obj._expandos) {\\n      const val = obj._expandos[prop];\\n      if (typeof val === \"function\") {\\n        return val.bind(obj);\\n      }\\n      return val;\\n    }\\n    if (typeof prop === \"string\" && !prop.startsWith(\"_\") && prop in obj) {\\n      const val = obj[prop];\\n      if (typeof val === \"function\") {\\n        return val.bind(obj);\\n      }\\n      return val;\\n    }\\n    return undefined;\\n  }\\n  set(obj, prop, value) {\\n    if (obj._kidIds) {\\n      obj._kidIds.forEach(id => {\\n        obj._appObjects[id].wrapped[prop] = value;\\n      });\\n    }\\n    if (typeof prop === \"string\" && !prop.startsWith(\"_\") && prop in obj) {\\n      const old = obj[prop];\\n      obj[prop] = value;\\n      if (!this.nosend.has(prop) && obj._send && obj._id !== null && typeof old !== \"function\") {\\n        const data = {\\n          id: obj._id\\n        };\\n        data[prop] = prop === \"value\" ? obj._getValue() : obj[prop];\\n        if (!obj._siblings) {\\n          obj._send(data);\\n        } else {\\n          data.siblings = obj._siblings;\\n          obj._send(data);\\n        }\\n      }\\n    } else {\\n      obj._expandos[prop] = value;\\n    }\\n    return true;\\n  }\\n  has(obj, prop) {\\n    return prop in obj._expandos || typeof prop === \"string\" && !prop.startsWith(\"_\") && prop in obj;\\n  }\\n  getPrototypeOf(obj) {\\n    return null;\\n  }\\n  setPrototypeOf(obj, proto) {\\n    return false;\\n  }\\n  isExtensible(obj) {\\n    return true;\\n  }\\n  preventExtensions(obj) {\\n    return false;\\n  }\\n  getOwnPropertyDescriptor(obj, prop) {\\n    if (prop in obj._expandos) {\\n      return {\\n        configurable: true,\\n        enumerable: true,\\n        value: obj._expandos[prop]\\n      };\\n    }\\n    if (typeof prop === \"string\" && !prop.startsWith(\"_\") && prop in obj) {\\n      return {\\n        configurable: true,\\n        enumerable: true,\\n        value: obj[prop]\\n      };\\n    }\\n    return undefined;\\n  }\\n  defineProperty(obj, key, descriptor) {\\n    Object.defineProperty(obj._expandos, key, descriptor);\\n    return true;\\n  }\\n  deleteProperty(obj, prop) {\\n    if (prop in obj._expandos) {\\n      delete obj._expandos[prop];\\n    }\\n  }\\n  ownKeys(obj) {\\n    const fromExpandos = Reflect.ownKeys(obj._expandos);\\n    const fromObj = Reflect.ownKeys(obj).filter(k => !k.startsWith(\"_\"));\\n    return fromExpandos.concat(fromObj);\\n  }\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/util.js\\n\\nclass Util extends PDFObject {\\n  constructor(data) {\\n    super(data);\\n    this._scandCache = new Map();\\n    this._months = [\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"];\\n    this._days = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\\n    this.MILLISECONDS_IN_DAY = 86400000;\\n    this.MILLISECONDS_IN_WEEK = 604800000;\\n    this._externalCall = data.externalCall;\\n  }\\n  printf(...args) {\\n    if (args.length === 0) {\\n      throw new Error(\"Invalid number of params in printf\");\\n    }\\n    if (typeof args[0] !== \"string\") {\\n      throw new TypeError(\"First argument of printf must be a string\");\\n    }\\n    const pattern = /%(,[0-4])?([+ 0#]+)?(\\\\d+)?(\\\\.\\\\d+)?(.)/g;\\n    const PLUS = 1;\\n    const SPACE = 2;\\n    const ZERO = 4;\\n    const HASH = 8;\\n    let i = 0;\\n    return args[0].replaceAll(pattern, function (match, nDecSep, cFlags, nWidth, nPrecision, cConvChar) {\\n      if (cConvChar !== \"d\" && cConvChar !== \"f\" && cConvChar !== \"s\" && cConvChar !== \"x\") {\\n        const buf = [\"%\"];\\n        for (const str of [nDecSep, cFlags, nWidth, nPrecision, cConvChar]) {\\n          if (str) {\\n            buf.push(str);\\n          }\\n        }\\n        return buf.join(\"\");\\n      }\\n      i++;\\n      if (i === args.length) {\\n        throw new Error(\"Not enough arguments in printf\");\\n      }\\n      const arg = args[i];\\n      if (cConvChar === \"s\") {\\n        return arg.toString();\\n      }\\n      let flags = 0;\\n      if (cFlags) {\\n        for (const flag of cFlags) {\\n          switch (flag) {\\n            case \"+\":\\n              flags |= PLUS;\\n              break;\\n            case \" \":\\n              flags |= SPACE;\\n              break;\\n            case \"0\":\\n              flags |= ZERO;\\n              break;\\n            case \"#\":\\n              flags |= HASH;\\n              break;\\n          }\\n        }\\n      }\\n      cFlags = flags;\\n      if (nWidth) {\\n        nWidth = parseInt(nWidth);\\n      }\\n      let intPart = Math.trunc(arg);\\n      if (cConvChar === \"x\") {\\n        let hex = Math.abs(intPart).toString(16).toUpperCase();\\n        if (nWidth !== undefined) {\\n          hex = hex.padStart(nWidth, cFlags & ZERO ? \"0\" : \" \");\\n        }\\n        if (cFlags & HASH) {\\n          hex = `0x${hex}`;\\n        }\\n        return hex;\\n      }\\n      if (nPrecision) {\\n        nPrecision = parseInt(nPrecision.substring(1));\\n      }\\n      nDecSep = nDecSep ? nDecSep.substring(1) : \"0\";\\n      const separators = {\\n        0: [\",\", \".\"],\\n        1: [\"\", \".\"],\\n        2: [\".\", \",\"],\\n        3: [\"\", \",\"],\\n        4: [\"\\'\", \".\"]\\n      };\\n      const [thousandSep, decimalSep] = separators[nDecSep];\\n      let decPart = \"\";\\n      if (cConvChar === \"f\") {\\n        decPart = nPrecision !== undefined ? Math.abs(arg - intPart).toFixed(nPrecision) : Math.abs(arg - intPart).toString();\\n        if (decPart.length > 2) {\\n          decPart = `${decimalSep}${decPart.substring(2)}`;\\n        } else {\\n          if (decPart === \"1\") {\\n            intPart += Math.sign(arg);\\n          }\\n          decPart = cFlags & HASH ? \".\" : \"\";\\n        }\\n      }\\n      let sign = \"\";\\n      if (intPart < 0) {\\n        sign = \"-\";\\n        intPart = -intPart;\\n      } else if (cFlags & PLUS) {\\n        sign = \"+\";\\n      } else if (cFlags & SPACE) {\\n        sign = \" \";\\n      }\\n      if (thousandSep && intPart >= 1000) {\\n        const buf = [];\\n        while (true) {\\n          buf.push((intPart % 1000).toString().padStart(3, \"0\"));\\n          intPart = Math.trunc(intPart / 1000);\\n          if (intPart < 1000) {\\n            buf.push(intPart.toString());\\n            break;\\n          }\\n        }\\n        intPart = buf.reverse().join(thousandSep);\\n      } else {\\n        intPart = intPart.toString();\\n      }\\n      let n = `${intPart}${decPart}`;\\n      if (nWidth !== undefined) {\\n        n = n.padStart(nWidth - sign.length, cFlags & ZERO ? \"0\" : \" \");\\n      }\\n      return `${sign}${n}`;\\n    });\\n  }\\n  iconStreamFromIcon() {}\\n  printd(cFormat, oDate) {\\n    switch (cFormat) {\\n      case 0:\\n        return this.printd(\"D:yyyymmddHHMMss\", oDate);\\n      case 1:\\n        return this.printd(\"yyyy.mm.dd HH:MM:ss\", oDate);\\n      case 2:\\n        return this.printd(\"m/d/yy h:MM:ss tt\", oDate);\\n    }\\n    const handlers = {\\n      mmmm: data => {\\n        return this._months[data.month];\\n      },\\n      mmm: data => {\\n        return this._months[data.month].substring(0, 3);\\n      },\\n      mm: data => {\\n        return (data.month + 1).toString().padStart(2, \"0\");\\n      },\\n      m: data => {\\n        return (data.month + 1).toString();\\n      },\\n      dddd: data => {\\n        return this._days[data.dayOfWeek];\\n      },\\n      ddd: data => {\\n        return this._days[data.dayOfWeek].substring(0, 3);\\n      },\\n      dd: data => {\\n        return data.day.toString().padStart(2, \"0\");\\n      },\\n      d: data => {\\n        return data.day.toString();\\n      },\\n      yyyy: data => {\\n        return data.year.toString();\\n      },\\n      yy: data => {\\n        return (data.year % 100).toString().padStart(2, \"0\");\\n      },\\n      HH: data => {\\n        return data.hours.toString().padStart(2, \"0\");\\n      },\\n      H: data => {\\n        return data.hours.toString();\\n      },\\n      hh: data => {\\n        return (1 + (data.hours + 11) % 12).toString().padStart(2, \"0\");\\n      },\\n      h: data => {\\n        return (1 + (data.hours + 11) % 12).toString();\\n      },\\n      MM: data => {\\n        return data.minutes.toString().padStart(2, \"0\");\\n      },\\n      M: data => {\\n        return data.minutes.toString();\\n      },\\n      ss: data => {\\n        return data.seconds.toString().padStart(2, \"0\");\\n      },\\n      s: data => {\\n        return data.seconds.toString();\\n      },\\n      tt: data => {\\n        return data.hours < 12 ? \"am\" : \"pm\";\\n      },\\n      t: data => {\\n        return data.hours < 12 ? \"a\" : \"p\";\\n      }\\n    };\\n    const data = {\\n      year: oDate.getFullYear(),\\n      month: oDate.getMonth(),\\n      day: oDate.getDate(),\\n      dayOfWeek: oDate.getDay(),\\n      hours: oDate.getHours(),\\n      minutes: oDate.getMinutes(),\\n      seconds: oDate.getSeconds()\\n    };\\n    const patterns = /(mmmm|mmm|mm|m|dddd|ddd|dd|d|yyyy|yy|HH|H|hh|h|MM|M|ss|s|tt|t|\\\\\\\\.)/g;\\n    return cFormat.replaceAll(patterns, function (match, pattern) {\\n      if (pattern in handlers) {\\n        return handlers[pattern](data);\\n      }\\n      return pattern.charCodeAt(1);\\n    });\\n  }\\n  printx(cFormat, cSource) {\\n    cSource = (cSource ?? \"\").toString();\\n    const handlers = [x => x, x => x.toUpperCase(), x => x.toLowerCase()];\\n    const buf = [];\\n    let i = 0;\\n    const ii = cSource.length;\\n    let currCase = handlers[0];\\n    let escaped = false;\\n    for (const command of cFormat) {\\n      if (escaped) {\\n        buf.push(command);\\n        escaped = false;\\n        continue;\\n      }\\n      if (i >= ii) {\\n        break;\\n      }\\n      switch (command) {\\n        case \"?\":\\n          buf.push(currCase(cSource.charAt(i++)));\\n          break;\\n        case \"X\":\\n          while (i < ii) {\\n            const char = cSource.charAt(i++);\\n            if (\"a\" <= char && char <= \"z\" || \"A\" <= char && char <= \"Z\" || \"0\" <= char && char <= \"9\") {\\n              buf.push(currCase(char));\\n              break;\\n            }\\n          }\\n          break;\\n        case \"A\":\\n          while (i < ii) {\\n            const char = cSource.charAt(i++);\\n            if (\"a\" <= char && char <= \"z\" || \"A\" <= char && char <= \"Z\") {\\n              buf.push(currCase(char));\\n              break;\\n            }\\n          }\\n          break;\\n        case \"9\":\\n          while (i < ii) {\\n            const char = cSource.charAt(i++);\\n            if (\"0\" <= char && char <= \"9\") {\\n              buf.push(char);\\n              break;\\n            }\\n          }\\n          break;\\n        case \"*\":\\n          while (i < ii) {\\n            buf.push(currCase(cSource.charAt(i++)));\\n          }\\n          break;\\n        case \"\\\\\\\\\":\\n          escaped = true;\\n          break;\\n        case \">\":\\n          currCase = handlers[1];\\n          break;\\n        case \"<\":\\n          currCase = handlers[2];\\n          break;\\n        case \"=\":\\n          currCase = handlers[0];\\n          break;\\n        default:\\n          buf.push(command);\\n      }\\n    }\\n    return buf.join(\"\");\\n  }\\n  scand(cFormat, cDate) {\\n    if (typeof cDate !== \"string\") {\\n      return new Date(cDate);\\n    }\\n    if (cDate === \"\") {\\n      return new Date();\\n    }\\n    switch (cFormat) {\\n      case 0:\\n        return this.scand(\"D:yyyymmddHHMMss\", cDate);\\n      case 1:\\n        return this.scand(\"yyyy.mm.dd HH:MM:ss\", cDate);\\n      case 2:\\n        return this.scand(\"m/d/yy h:MM:ss tt\", cDate);\\n    }\\n    if (!this._scandCache.has(cFormat)) {\\n      const months = this._months;\\n      const days = this._days;\\n      const handlers = {\\n        mmmm: {\\n          pattern: `(${months.join(\"|\")})`,\\n          action: (value, data) => {\\n            data.month = months.indexOf(value);\\n          }\\n        },\\n        mmm: {\\n          pattern: `(${months.map(month => month.substring(0, 3)).join(\"|\")})`,\\n          action: (value, data) => {\\n            data.month = months.findIndex(month => month.substring(0, 3) === value);\\n          }\\n        },\\n        mm: {\\n          pattern: `(\\\\\\\\d{2})`,\\n          action: (value, data) => {\\n            data.month = parseInt(value) - 1;\\n          }\\n        },\\n        m: {\\n          pattern: `(\\\\\\\\d{1,2})`,\\n          action: (value, data) => {\\n            data.month = parseInt(value) - 1;\\n          }\\n        },\\n        dddd: {\\n          pattern: `(${days.join(\"|\")})`,\\n          action: (value, data) => {\\n            data.day = days.indexOf(value);\\n          }\\n        },\\n        ddd: {\\n          pattern: `(${days.map(day => day.substring(0, 3)).join(\"|\")})`,\\n          action: (value, data) => {\\n            data.day = days.findIndex(day => day.substring(0, 3) === value);\\n          }\\n        },\\n        dd: {\\n          pattern: \"(\\\\\\\\d{2})\",\\n          action: (value, data) => {\\n            data.day = parseInt(value);\\n          }\\n        },\\n        d: {\\n          pattern: \"(\\\\\\\\d{1,2})\",\\n          action: (value, data) => {\\n            data.day = parseInt(value);\\n          }\\n        },\\n        yyyy: {\\n          pattern: \"(\\\\\\\\d{4})\",\\n          action: (value, data) => {\\n            data.year = parseInt(value);\\n          }\\n        },\\n        yy: {\\n          pattern: \"(\\\\\\\\d{2})\",\\n          action: (value, data) => {\\n            data.year = 2000 + parseInt(value);\\n          }\\n        },\\n        HH: {\\n          pattern: \"(\\\\\\\\d{2})\",\\n          action: (value, data) => {\\n            data.hours = parseInt(value);\\n          }\\n        },\\n        H: {\\n          pattern: \"(\\\\\\\\d{1,2})\",\\n          action: (value, data) => {\\n            data.hours = parseInt(value);\\n          }\\n        },\\n        hh: {\\n          pattern: \"(\\\\\\\\d{2})\",\\n          action: (value, data) => {\\n            data.hours = parseInt(value);\\n          }\\n        },\\n        h: {\\n          pattern: \"(\\\\\\\\d{1,2})\",\\n          action: (value, data) => {\\n            data.hours = parseInt(value);\\n          }\\n        },\\n        MM: {\\n          pattern: \"(\\\\\\\\d{2})\",\\n          action: (value, data) => {\\n            data.minutes = parseInt(value);\\n          }\\n        },\\n        M: {\\n          pattern: \"(\\\\\\\\d{1,2})\",\\n          action: (value, data) => {\\n            data.minutes = parseInt(value);\\n          }\\n        },\\n        ss: {\\n          pattern: \"(\\\\\\\\d{2})\",\\n          action: (value, data) => {\\n            data.seconds = parseInt(value);\\n          }\\n        },\\n        s: {\\n          pattern: \"(\\\\\\\\d{1,2})\",\\n          action: (value, data) => {\\n            data.seconds = parseInt(value);\\n          }\\n        },\\n        tt: {\\n          pattern: \"([aApP][mM])\",\\n          action: (value, data) => {\\n            const char = value.charAt(0);\\n            data.am = char === \"a\" || char === \"A\";\\n          }\\n        },\\n        t: {\\n          pattern: \"([aApP])\",\\n          action: (value, data) => {\\n            data.am = value === \"a\" || value === \"A\";\\n          }\\n        }\\n      };\\n      const escapedFormat = cFormat.replaceAll(/[.*+\\\\-?^${}()|[\\\\]\\\\\\\\]/g, \"\\\\\\\\$&\");\\n      const patterns = /(mmmm|mmm|mm|m|dddd|ddd|dd|d|yyyy|yy|HH|H|hh|h|MM|M|ss|s|tt|t)/g;\\n      const actions = [];\\n      const re = escapedFormat.replaceAll(patterns, function (match, patternElement) {\\n        const {\\n          pattern,\\n          action\\n        } = handlers[patternElement];\\n        actions.push(action);\\n        return pattern;\\n      });\\n      this._scandCache.set(cFormat, [re, actions]);\\n    }\\n    const [re, actions] = this._scandCache.get(cFormat);\\n    const matches = new RegExp(`^${re}$`, \"g\").exec(cDate);\\n    if (!matches || matches.length !== actions.length + 1) {\\n      return null;\\n    }\\n    const data = {\\n      year: 2000,\\n      month: 0,\\n      day: 1,\\n      hours: 0,\\n      minutes: 0,\\n      seconds: 0,\\n      am: null\\n    };\\n    actions.forEach((action, i) => action(matches[i + 1], data));\\n    if (data.am !== null) {\\n      data.hours = data.hours % 12 + (data.am ? 0 : 12);\\n    }\\n    return new Date(data.year, data.month, data.day, data.hours, data.minutes, data.seconds);\\n  }\\n  spansToXML() {}\\n  stringFromStream() {}\\n  xmlToSpans() {}\\n}\\n\\n;// CONCATENATED MODULE: ./src/scripting_api/initialization.js\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nfunction initSandbox(params) {\\n  delete globalThis.pdfjsScripting;\\n  const externalCall = globalThis.callExternalFunction;\\n  delete globalThis.callExternalFunction;\\n  const globalEval = code => globalThis.eval(code);\\n  const send = data => externalCall(\"send\", [data]);\\n  const proxyHandler = new ProxyHandler();\\n  const {\\n    data\\n  } = params;\\n  const doc = new Doc({\\n    send,\\n    globalEval,\\n    ...data.docInfo\\n  });\\n  const _document = {\\n    obj: doc,\\n    wrapped: new Proxy(doc, proxyHandler)\\n  };\\n  const app = new App({\\n    send,\\n    globalEval,\\n    externalCall,\\n    _document,\\n    calculationOrder: data.calculationOrder,\\n    proxyHandler,\\n    ...data.appInfo\\n  });\\n  const util = new Util({\\n    externalCall\\n  });\\n  const appObjects = app._objects;\\n  if (data.objects) {\\n    const annotations = [];\\n    for (const [name, objs] of Object.entries(data.objects)) {\\n      annotations.length = 0;\\n      let container = null;\\n      for (const obj of objs) {\\n        if (obj.type !== \"\") {\\n          annotations.push(obj);\\n        } else {\\n          container = obj;\\n        }\\n      }\\n      let obj = container;\\n      if (annotations.length > 0) {\\n        obj = annotations[0];\\n        obj.send = send;\\n      }\\n      obj.globalEval = globalEval;\\n      obj.doc = _document;\\n      obj.fieldPath = name;\\n      obj.appObjects = appObjects;\\n      let field;\\n      switch (obj.type) {\\n        case \"radiobutton\":\\n          {\\n            const otherButtons = annotations.slice(1);\\n            field = new RadioButtonField(otherButtons, obj);\\n            break;\\n          }\\n        case \"checkbox\":\\n          {\\n            const otherButtons = annotations.slice(1);\\n            field = new CheckboxField(otherButtons, obj);\\n            break;\\n          }\\n        case \"text\":\\n          if (annotations.length <= 1) {\\n            field = new Field(obj);\\n            break;\\n          }\\n          obj.siblings = annotations.map(x => x.id).slice(1);\\n          field = new Field(obj);\\n          break;\\n        default:\\n          field = new Field(obj);\\n      }\\n      const wrapped = new Proxy(field, proxyHandler);\\n      const _object = {\\n        obj: field,\\n        wrapped\\n      };\\n      doc._addField(name, _object);\\n      for (const object of objs) {\\n        appObjects[object.id] = _object;\\n      }\\n      if (container) {\\n        appObjects[container.id] = _object;\\n      }\\n    }\\n  }\\n  const color = new Color();\\n  globalThis.event = null;\\n  globalThis.global = Object.create(null);\\n  globalThis.app = new Proxy(app, proxyHandler);\\n  globalThis.color = new Proxy(color, proxyHandler);\\n  globalThis.console = new Proxy(new Console({\\n    send\\n  }), proxyHandler);\\n  globalThis.util = new Proxy(util, proxyHandler);\\n  globalThis.border = Border;\\n  globalThis.cursor = Cursor;\\n  globalThis.display = Display;\\n  globalThis.font = Font;\\n  globalThis.highlight = Highlight;\\n  globalThis.position = Position;\\n  globalThis.scaleHow = ScaleHow;\\n  globalThis.scaleWhen = ScaleWhen;\\n  globalThis.style = Style;\\n  globalThis.trans = Trans;\\n  globalThis.zoomtype = ZoomType;\\n  globalThis.ADBE = {\\n    Reader_Value_Asked: true,\\n    Viewer_Value_Asked: true\\n  };\\n  const aform = new AForm(doc, app, util, color);\\n  for (const name of Object.getOwnPropertyNames(AForm.prototype)) {\\n    if (name !== \"constructor\" && !name.startsWith(\"_\")) {\\n      globalThis[name] = aform[name].bind(aform);\\n    }\\n  }\\n  for (const [name, value] of Object.entries(GlobalConstants)) {\\n    Object.defineProperty(globalThis, name, {\\n      value,\\n      writable: false\\n    });\\n  }\\n  Object.defineProperties(globalThis, {\\n    ColorConvert: {\\n      value: color.convert.bind(color),\\n      writable: true\\n    },\\n    ColorEqual: {\\n      value: color.equal.bind(color),\\n      writable: true\\n    }\\n  });\\n  const properties = Object.create(null);\\n  for (const name of Object.getOwnPropertyNames(Doc.prototype)) {\\n    if (name === \"constructor\" || name.startsWith(\"_\")) {\\n      continue;\\n    }\\n    const descriptor = Object.getOwnPropertyDescriptor(Doc.prototype, name);\\n    if (descriptor.get) {\\n      properties[name] = {\\n        get: descriptor.get.bind(doc),\\n        set: descriptor.set.bind(doc)\\n      };\\n    } else {\\n      properties[name] = {\\n        value: Doc.prototype[name].bind(doc)\\n      };\\n    }\\n  }\\n  Object.defineProperties(globalThis, properties);\\n  const functions = {\\n    dispatchEvent: app._dispatchEvent.bind(app),\\n    timeoutCb: app._evalCallback.bind(app)\\n  };\\n  return (name, args) => {\\n    try {\\n      functions[name](args);\\n    } catch (error) {\\n      send(serializeError(error));\\n    }\\n  };\\n}\\n\\n;// CONCATENATED MODULE: ./src/pdf.scripting.js\\n\\nconst pdfjsVersion = \\'4.0.269\\';\\nconst pdfjsBuild = \\'f4b396f6c\\';\\nglobalThis.pdfjsScripting = {\\n  initSandbox: initSandbox\\n};\\n'];\n    code.push(\"delete dump;\");\n    let success = false;\n    let buf = 0;\n    try {\n      const sandboxData = JSON.stringify(data);\n      code.push(`pdfjsScripting.initSandbox({ data: ${sandboxData} })`);\n      buf = this._module.stringToNewUTF8(code.join(\"\\n\"));\n      success = !!this._module.ccall(\"init\", \"number\", [\"number\", \"number\"], [buf, this._alertOnError]);\n    } catch (error) {\n      console.error(error);\n    } finally {\n      if (buf) {\n        this._module.ccall(\"free\", \"number\", [\"number\"], [buf]);\n      }\n    }\n    if (success) {\n      this.support.commFun = this._module.cwrap(\"commFun\", null, [\"string\", \"string\"]);\n    } else {\n      this.nukeSandbox();\n      throw new Error(\"Cannot start sandbox\");\n    }\n  }\n  dispatchEvent(event) {\n    this.support?.callSandboxFunction(\"dispatchEvent\", event);\n  }\n  dumpMemoryUse() {\n    this._module?.ccall(\"dumpMemoryUse\", null, []);\n  }\n  nukeSandbox() {\n    if (this._module !== null) {\n      this.support.destroy();\n      this.support = null;\n      this._module.ccall(\"nukeSandbox\", null, []);\n      this._module = null;\n    }\n  }\n  evalForTesting(code, key) {\n    throw new Error(\"Not implemented: evalForTesting\");\n  }\n}\nfunction QuickJSSandbox() {\n  return quickjs_eval().then(module => {\n    return new Sandbox(window, module);\n  });\n}\n\nvar __webpack_exports__QuickJSSandbox = __webpack_exports__.QuickJSSandbox;\nexport { __webpack_exports__QuickJSSandbox as QuickJSSandbox };\n\n//# sourceMappingURL=pdf.sandbox.mjs.map"
  },
  {
    "path": "public/assets/lib/vendor/pdfjs/pdf.worker.js",
    "content": "/**\n * @licstart The following is the entire license notice for the\n * JavaScript code in this page\n *\n * Copyright 2023 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * @licend The above is the entire license notice for the\n * JavaScript code in this page\n */\n\n/******/ // The require scope\n/******/ var __webpack_require__ = {};\n/******/ \n/************************************************************************/\n/******/ /* webpack/runtime/define property getters */\n/******/ (() => {\n/******/ \t// define getter functions for harmony exports\n/******/ \t__webpack_require__.d = (exports, definition) => {\n/******/ \t\tfor(var key in definition) {\n/******/ \t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t}\n/******/ \t\t}\n/******/ \t};\n/******/ })();\n/******/ \n/******/ /* webpack/runtime/hasOwnProperty shorthand */\n/******/ (() => {\n/******/ \t__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))\n/******/ })();\n/******/ \n/************************************************************************/\nvar __webpack_exports__ = globalThis.pdfjsWorker = {};\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n  WorkerMessageHandler: () => (/* reexport */ WorkerMessageHandler)\n});\n\n;// CONCATENATED MODULE: ./src/shared/util.js\nconst isNodeJS = typeof process === \"object\" && process + \"\" === \"[object process]\" && !process.versions.nw && !(process.versions.electron && process.type && process.type !== \"browser\");\nconst IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];\nconst FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];\nconst MAX_IMAGE_SIZE_TO_CACHE = 10e6;\nconst LINE_FACTOR = 1.35;\nconst LINE_DESCENT_FACTOR = 0.35;\nconst BASELINE_FACTOR = LINE_DESCENT_FACTOR / LINE_FACTOR;\nconst RenderingIntentFlag = {\n  ANY: 0x01,\n  DISPLAY: 0x02,\n  PRINT: 0x04,\n  SAVE: 0x08,\n  ANNOTATIONS_FORMS: 0x10,\n  ANNOTATIONS_STORAGE: 0x20,\n  ANNOTATIONS_DISABLE: 0x40,\n  OPLIST: 0x100\n};\nconst AnnotationMode = {\n  DISABLE: 0,\n  ENABLE: 1,\n  ENABLE_FORMS: 2,\n  ENABLE_STORAGE: 3\n};\nconst AnnotationEditorPrefix = \"pdfjs_internal_editor_\";\nconst AnnotationEditorType = {\n  DISABLE: -1,\n  NONE: 0,\n  FREETEXT: 3,\n  HIGHLIGHT: 9,\n  STAMP: 13,\n  INK: 15\n};\nconst AnnotationEditorParamsType = {\n  RESIZE: 1,\n  CREATE: 2,\n  FREETEXT_SIZE: 11,\n  FREETEXT_COLOR: 12,\n  FREETEXT_OPACITY: 13,\n  INK_COLOR: 21,\n  INK_THICKNESS: 22,\n  INK_OPACITY: 23\n};\nconst PermissionFlag = {\n  PRINT: 0x04,\n  MODIFY_CONTENTS: 0x08,\n  COPY: 0x10,\n  MODIFY_ANNOTATIONS: 0x20,\n  FILL_INTERACTIVE_FORMS: 0x100,\n  COPY_FOR_ACCESSIBILITY: 0x200,\n  ASSEMBLE: 0x400,\n  PRINT_HIGH_QUALITY: 0x800\n};\nconst TextRenderingMode = {\n  FILL: 0,\n  STROKE: 1,\n  FILL_STROKE: 2,\n  INVISIBLE: 3,\n  FILL_ADD_TO_PATH: 4,\n  STROKE_ADD_TO_PATH: 5,\n  FILL_STROKE_ADD_TO_PATH: 6,\n  ADD_TO_PATH: 7,\n  FILL_STROKE_MASK: 3,\n  ADD_TO_PATH_FLAG: 4\n};\nconst ImageKind = {\n  GRAYSCALE_1BPP: 1,\n  RGB_24BPP: 2,\n  RGBA_32BPP: 3\n};\nconst AnnotationType = {\n  TEXT: 1,\n  LINK: 2,\n  FREETEXT: 3,\n  LINE: 4,\n  SQUARE: 5,\n  CIRCLE: 6,\n  POLYGON: 7,\n  POLYLINE: 8,\n  HIGHLIGHT: 9,\n  UNDERLINE: 10,\n  SQUIGGLY: 11,\n  STRIKEOUT: 12,\n  STAMP: 13,\n  CARET: 14,\n  INK: 15,\n  POPUP: 16,\n  FILEATTACHMENT: 17,\n  SOUND: 18,\n  MOVIE: 19,\n  WIDGET: 20,\n  SCREEN: 21,\n  PRINTERMARK: 22,\n  TRAPNET: 23,\n  WATERMARK: 24,\n  THREED: 25,\n  REDACT: 26\n};\nconst AnnotationReplyType = {\n  GROUP: \"Group\",\n  REPLY: \"R\"\n};\nconst AnnotationFlag = {\n  INVISIBLE: 0x01,\n  HIDDEN: 0x02,\n  PRINT: 0x04,\n  NOZOOM: 0x08,\n  NOROTATE: 0x10,\n  NOVIEW: 0x20,\n  READONLY: 0x40,\n  LOCKED: 0x80,\n  TOGGLENOVIEW: 0x100,\n  LOCKEDCONTENTS: 0x200\n};\nconst AnnotationFieldFlag = {\n  READONLY: 0x0000001,\n  REQUIRED: 0x0000002,\n  NOEXPORT: 0x0000004,\n  MULTILINE: 0x0001000,\n  PASSWORD: 0x0002000,\n  NOTOGGLETOOFF: 0x0004000,\n  RADIO: 0x0008000,\n  PUSHBUTTON: 0x0010000,\n  COMBO: 0x0020000,\n  EDIT: 0x0040000,\n  SORT: 0x0080000,\n  FILESELECT: 0x0100000,\n  MULTISELECT: 0x0200000,\n  DONOTSPELLCHECK: 0x0400000,\n  DONOTSCROLL: 0x0800000,\n  COMB: 0x1000000,\n  RICHTEXT: 0x2000000,\n  RADIOSINUNISON: 0x2000000,\n  COMMITONSELCHANGE: 0x4000000\n};\nconst AnnotationBorderStyleType = {\n  SOLID: 1,\n  DASHED: 2,\n  BEVELED: 3,\n  INSET: 4,\n  UNDERLINE: 5\n};\nconst AnnotationActionEventType = {\n  E: \"Mouse Enter\",\n  X: \"Mouse Exit\",\n  D: \"Mouse Down\",\n  U: \"Mouse Up\",\n  Fo: \"Focus\",\n  Bl: \"Blur\",\n  PO: \"PageOpen\",\n  PC: \"PageClose\",\n  PV: \"PageVisible\",\n  PI: \"PageInvisible\",\n  K: \"Keystroke\",\n  F: \"Format\",\n  V: \"Validate\",\n  C: \"Calculate\"\n};\nconst DocumentActionEventType = {\n  WC: \"WillClose\",\n  WS: \"WillSave\",\n  DS: \"DidSave\",\n  WP: \"WillPrint\",\n  DP: \"DidPrint\"\n};\nconst PageActionEventType = {\n  O: \"PageOpen\",\n  C: \"PageClose\"\n};\nconst VerbosityLevel = {\n  ERRORS: 0,\n  WARNINGS: 1,\n  INFOS: 5\n};\nconst CMapCompressionType = {\n  NONE: 0,\n  BINARY: 1\n};\nconst OPS = {\n  dependency: 1,\n  setLineWidth: 2,\n  setLineCap: 3,\n  setLineJoin: 4,\n  setMiterLimit: 5,\n  setDash: 6,\n  setRenderingIntent: 7,\n  setFlatness: 8,\n  setGState: 9,\n  save: 10,\n  restore: 11,\n  transform: 12,\n  moveTo: 13,\n  lineTo: 14,\n  curveTo: 15,\n  curveTo2: 16,\n  curveTo3: 17,\n  closePath: 18,\n  rectangle: 19,\n  stroke: 20,\n  closeStroke: 21,\n  fill: 22,\n  eoFill: 23,\n  fillStroke: 24,\n  eoFillStroke: 25,\n  closeFillStroke: 26,\n  closeEOFillStroke: 27,\n  endPath: 28,\n  clip: 29,\n  eoClip: 30,\n  beginText: 31,\n  endText: 32,\n  setCharSpacing: 33,\n  setWordSpacing: 34,\n  setHScale: 35,\n  setLeading: 36,\n  setFont: 37,\n  setTextRenderingMode: 38,\n  setTextRise: 39,\n  moveText: 40,\n  setLeadingMoveText: 41,\n  setTextMatrix: 42,\n  nextLine: 43,\n  showText: 44,\n  showSpacedText: 45,\n  nextLineShowText: 46,\n  nextLineSetSpacingShowText: 47,\n  setCharWidth: 48,\n  setCharWidthAndBounds: 49,\n  setStrokeColorSpace: 50,\n  setFillColorSpace: 51,\n  setStrokeColor: 52,\n  setStrokeColorN: 53,\n  setFillColor: 54,\n  setFillColorN: 55,\n  setStrokeGray: 56,\n  setFillGray: 57,\n  setStrokeRGBColor: 58,\n  setFillRGBColor: 59,\n  setStrokeCMYKColor: 60,\n  setFillCMYKColor: 61,\n  shadingFill: 62,\n  beginInlineImage: 63,\n  beginImageData: 64,\n  endInlineImage: 65,\n  paintXObject: 66,\n  markPoint: 67,\n  markPointProps: 68,\n  beginMarkedContent: 69,\n  beginMarkedContentProps: 70,\n  endMarkedContent: 71,\n  beginCompat: 72,\n  endCompat: 73,\n  paintFormXObjectBegin: 74,\n  paintFormXObjectEnd: 75,\n  beginGroup: 76,\n  endGroup: 77,\n  beginAnnotation: 80,\n  endAnnotation: 81,\n  paintImageMaskXObject: 83,\n  paintImageMaskXObjectGroup: 84,\n  paintImageXObject: 85,\n  paintInlineImageXObject: 86,\n  paintInlineImageXObjectGroup: 87,\n  paintImageXObjectRepeat: 88,\n  paintImageMaskXObjectRepeat: 89,\n  paintSolidColorImageMask: 90,\n  constructPath: 91\n};\nconst PasswordResponses = {\n  NEED_PASSWORD: 1,\n  INCORRECT_PASSWORD: 2\n};\nlet verbosity = VerbosityLevel.WARNINGS;\nfunction setVerbosityLevel(level) {\n  if (Number.isInteger(level)) {\n    verbosity = level;\n  }\n}\nfunction getVerbosityLevel() {\n  return verbosity;\n}\nfunction info(msg) {\n  if (verbosity >= VerbosityLevel.INFOS) {\n    console.log(`Info: ${msg}`);\n  }\n}\nfunction warn(msg) {\n  if (verbosity >= VerbosityLevel.WARNINGS) {\n    console.log(`Warning: ${msg}`);\n  }\n}\nfunction unreachable(msg) {\n  throw new Error(msg);\n}\nfunction assert(cond, msg) {\n  if (!cond) {\n    unreachable(msg);\n  }\n}\nfunction _isValidProtocol(url) {\n  switch (url?.protocol) {\n    case \"http:\":\n    case \"https:\":\n    case \"ftp:\":\n    case \"mailto:\":\n    case \"tel:\":\n      return true;\n    default:\n      return false;\n  }\n}\nfunction createValidAbsoluteUrl(url, baseUrl = null, options = null) {\n  if (!url) {\n    return null;\n  }\n  try {\n    if (options && typeof url === \"string\") {\n      if (options.addDefaultProtocol && url.startsWith(\"www.\")) {\n        const dots = url.match(/\\./g);\n        if (dots?.length >= 2) {\n          url = `http://${url}`;\n        }\n      }\n      if (options.tryConvertEncoding) {\n        try {\n          url = stringToUTF8String(url);\n        } catch {}\n      }\n    }\n    const absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url);\n    if (_isValidProtocol(absoluteUrl)) {\n      return absoluteUrl;\n    }\n  } catch {}\n  return null;\n}\nfunction shadow(obj, prop, value, nonSerializable = false) {\n  Object.defineProperty(obj, prop, {\n    value,\n    enumerable: !nonSerializable,\n    configurable: true,\n    writable: false\n  });\n  return value;\n}\nconst BaseException = function BaseExceptionClosure() {\n  function BaseException(message, name) {\n    if (this.constructor === BaseException) {\n      unreachable(\"Cannot initialize BaseException.\");\n    }\n    this.message = message;\n    this.name = name;\n  }\n  BaseException.prototype = new Error();\n  BaseException.constructor = BaseException;\n  return BaseException;\n}();\nclass PasswordException extends BaseException {\n  constructor(msg, code) {\n    super(msg, \"PasswordException\");\n    this.code = code;\n  }\n}\nclass UnknownErrorException extends BaseException {\n  constructor(msg, details) {\n    super(msg, \"UnknownErrorException\");\n    this.details = details;\n  }\n}\nclass InvalidPDFException extends BaseException {\n  constructor(msg) {\n    super(msg, \"InvalidPDFException\");\n  }\n}\nclass MissingPDFException extends BaseException {\n  constructor(msg) {\n    super(msg, \"MissingPDFException\");\n  }\n}\nclass UnexpectedResponseException extends BaseException {\n  constructor(msg, status) {\n    super(msg, \"UnexpectedResponseException\");\n    this.status = status;\n  }\n}\nclass FormatError extends BaseException {\n  constructor(msg) {\n    super(msg, \"FormatError\");\n  }\n}\nclass AbortException extends BaseException {\n  constructor(msg) {\n    super(msg, \"AbortException\");\n  }\n}\nfunction bytesToString(bytes) {\n  if (typeof bytes !== \"object\" || bytes?.length === undefined) {\n    unreachable(\"Invalid argument for bytesToString\");\n  }\n  const length = bytes.length;\n  const MAX_ARGUMENT_COUNT = 8192;\n  if (length < MAX_ARGUMENT_COUNT) {\n    return String.fromCharCode.apply(null, bytes);\n  }\n  const strBuf = [];\n  for (let i = 0; i < length; i += MAX_ARGUMENT_COUNT) {\n    const chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);\n    const chunk = bytes.subarray(i, chunkEnd);\n    strBuf.push(String.fromCharCode.apply(null, chunk));\n  }\n  return strBuf.join(\"\");\n}\nfunction stringToBytes(str) {\n  if (typeof str !== \"string\") {\n    unreachable(\"Invalid argument for stringToBytes\");\n  }\n  const length = str.length;\n  const bytes = new Uint8Array(length);\n  for (let i = 0; i < length; ++i) {\n    bytes[i] = str.charCodeAt(i) & 0xff;\n  }\n  return bytes;\n}\nfunction string32(value) {\n  return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff);\n}\nfunction objectSize(obj) {\n  return Object.keys(obj).length;\n}\nfunction objectFromMap(map) {\n  const obj = Object.create(null);\n  for (const [key, value] of map) {\n    obj[key] = value;\n  }\n  return obj;\n}\nfunction isLittleEndian() {\n  const buffer8 = new Uint8Array(4);\n  buffer8[0] = 1;\n  const view32 = new Uint32Array(buffer8.buffer, 0, 1);\n  return view32[0] === 1;\n}\nfunction isEvalSupported() {\n  try {\n    new Function(\"\");\n    return true;\n  } catch {\n    return false;\n  }\n}\nclass FeatureTest {\n  static get isLittleEndian() {\n    return shadow(this, \"isLittleEndian\", isLittleEndian());\n  }\n  static get isEvalSupported() {\n    return shadow(this, \"isEvalSupported\", isEvalSupported());\n  }\n  static get isOffscreenCanvasSupported() {\n    return shadow(this, \"isOffscreenCanvasSupported\", typeof OffscreenCanvas !== \"undefined\");\n  }\n  static get platform() {\n    if (typeof navigator !== \"undefined\" && typeof navigator?.platform === \"string\") {\n      return shadow(this, \"platform\", {\n        isMac: navigator.platform.includes(\"Mac\")\n      });\n    }\n    return shadow(this, \"platform\", {\n      isMac: false\n    });\n  }\n  static get isCSSRoundSupported() {\n    return shadow(this, \"isCSSRoundSupported\", globalThis.CSS?.supports?.(\"width: round(1.5px, 1px)\"));\n  }\n}\nconst hexNumbers = [...Array(256).keys()].map(n => n.toString(16).padStart(2, \"0\"));\nclass Util {\n  static makeHexColor(r, g, b) {\n    return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`;\n  }\n  static scaleMinMax(transform, minMax) {\n    let temp;\n    if (transform[0]) {\n      if (transform[0] < 0) {\n        temp = minMax[0];\n        minMax[0] = minMax[1];\n        minMax[1] = temp;\n      }\n      minMax[0] *= transform[0];\n      minMax[1] *= transform[0];\n      if (transform[3] < 0) {\n        temp = minMax[2];\n        minMax[2] = minMax[3];\n        minMax[3] = temp;\n      }\n      minMax[2] *= transform[3];\n      minMax[3] *= transform[3];\n    } else {\n      temp = minMax[0];\n      minMax[0] = minMax[2];\n      minMax[2] = temp;\n      temp = minMax[1];\n      minMax[1] = minMax[3];\n      minMax[3] = temp;\n      if (transform[1] < 0) {\n        temp = minMax[2];\n        minMax[2] = minMax[3];\n        minMax[3] = temp;\n      }\n      minMax[2] *= transform[1];\n      minMax[3] *= transform[1];\n      if (transform[2] < 0) {\n        temp = minMax[0];\n        minMax[0] = minMax[1];\n        minMax[1] = temp;\n      }\n      minMax[0] *= transform[2];\n      minMax[1] *= transform[2];\n    }\n    minMax[0] += transform[4];\n    minMax[1] += transform[4];\n    minMax[2] += transform[5];\n    minMax[3] += transform[5];\n  }\n  static transform(m1, m2) {\n    return [m1[0] * m2[0] + m1[2] * m2[1], m1[1] * m2[0] + m1[3] * m2[1], m1[0] * m2[2] + m1[2] * m2[3], m1[1] * m2[2] + m1[3] * m2[3], m1[0] * m2[4] + m1[2] * m2[5] + m1[4], m1[1] * m2[4] + m1[3] * m2[5] + m1[5]];\n  }\n  static applyTransform(p, m) {\n    const xt = p[0] * m[0] + p[1] * m[2] + m[4];\n    const yt = p[0] * m[1] + p[1] * m[3] + m[5];\n    return [xt, yt];\n  }\n  static applyInverseTransform(p, m) {\n    const d = m[0] * m[3] - m[1] * m[2];\n    const xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;\n    const yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;\n    return [xt, yt];\n  }\n  static getAxialAlignedBoundingBox(r, m) {\n    const p1 = this.applyTransform(r, m);\n    const p2 = this.applyTransform(r.slice(2, 4), m);\n    const p3 = this.applyTransform([r[0], r[3]], m);\n    const p4 = this.applyTransform([r[2], r[1]], m);\n    return [Math.min(p1[0], p2[0], p3[0], p4[0]), Math.min(p1[1], p2[1], p3[1], p4[1]), Math.max(p1[0], p2[0], p3[0], p4[0]), Math.max(p1[1], p2[1], p3[1], p4[1])];\n  }\n  static inverseTransform(m) {\n    const d = m[0] * m[3] - m[1] * m[2];\n    return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d];\n  }\n  static singularValueDecompose2dScale(m) {\n    const transpose = [m[0], m[2], m[1], m[3]];\n    const a = m[0] * transpose[0] + m[1] * transpose[2];\n    const b = m[0] * transpose[1] + m[1] * transpose[3];\n    const c = m[2] * transpose[0] + m[3] * transpose[2];\n    const d = m[2] * transpose[1] + m[3] * transpose[3];\n    const first = (a + d) / 2;\n    const second = Math.sqrt((a + d) ** 2 - 4 * (a * d - c * b)) / 2;\n    const sx = first + second || 1;\n    const sy = first - second || 1;\n    return [Math.sqrt(sx), Math.sqrt(sy)];\n  }\n  static normalizeRect(rect) {\n    const r = rect.slice(0);\n    if (rect[0] > rect[2]) {\n      r[0] = rect[2];\n      r[2] = rect[0];\n    }\n    if (rect[1] > rect[3]) {\n      r[1] = rect[3];\n      r[3] = rect[1];\n    }\n    return r;\n  }\n  static intersect(rect1, rect2) {\n    const xLow = Math.max(Math.min(rect1[0], rect1[2]), Math.min(rect2[0], rect2[2]));\n    const xHigh = Math.min(Math.max(rect1[0], rect1[2]), Math.max(rect2[0], rect2[2]));\n    if (xLow > xHigh) {\n      return null;\n    }\n    const yLow = Math.max(Math.min(rect1[1], rect1[3]), Math.min(rect2[1], rect2[3]));\n    const yHigh = Math.min(Math.max(rect1[1], rect1[3]), Math.max(rect2[1], rect2[3]));\n    if (yLow > yHigh) {\n      return null;\n    }\n    return [xLow, yLow, xHigh, yHigh];\n  }\n  static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3) {\n    const tvalues = [],\n      bounds = [[], []];\n    let a, b, c, t, t1, t2, b2ac, sqrtb2ac;\n    for (let i = 0; i < 2; ++i) {\n      if (i === 0) {\n        b = 6 * x0 - 12 * x1 + 6 * x2;\n        a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;\n        c = 3 * x1 - 3 * x0;\n      } else {\n        b = 6 * y0 - 12 * y1 + 6 * y2;\n        a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;\n        c = 3 * y1 - 3 * y0;\n      }\n      if (Math.abs(a) < 1e-12) {\n        if (Math.abs(b) < 1e-12) {\n          continue;\n        }\n        t = -c / b;\n        if (0 < t && t < 1) {\n          tvalues.push(t);\n        }\n        continue;\n      }\n      b2ac = b * b - 4 * c * a;\n      sqrtb2ac = Math.sqrt(b2ac);\n      if (b2ac < 0) {\n        continue;\n      }\n      t1 = (-b + sqrtb2ac) / (2 * a);\n      if (0 < t1 && t1 < 1) {\n        tvalues.push(t1);\n      }\n      t2 = (-b - sqrtb2ac) / (2 * a);\n      if (0 < t2 && t2 < 1) {\n        tvalues.push(t2);\n      }\n    }\n    let j = tvalues.length,\n      mt;\n    const jlen = j;\n    while (j--) {\n      t = tvalues[j];\n      mt = 1 - t;\n      bounds[0][j] = mt * mt * mt * x0 + 3 * mt * mt * t * x1 + 3 * mt * t * t * x2 + t * t * t * x3;\n      bounds[1][j] = mt * mt * mt * y0 + 3 * mt * mt * t * y1 + 3 * mt * t * t * y2 + t * t * t * y3;\n    }\n    bounds[0][jlen] = x0;\n    bounds[1][jlen] = y0;\n    bounds[0][jlen + 1] = x3;\n    bounds[1][jlen + 1] = y3;\n    bounds[0].length = bounds[1].length = jlen + 2;\n    return [Math.min(...bounds[0]), Math.min(...bounds[1]), Math.max(...bounds[0]), Math.max(...bounds[1])];\n  }\n}\nconst PDFStringTranslateTable = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2d8, 0x2c7, 0x2c6, 0x2d9, 0x2dd, 0x2db, 0x2da, 0x2dc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x192, 0x2044, 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018, 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x141, 0x152, 0x160, 0x178, 0x17d, 0x131, 0x142, 0x153, 0x161, 0x17e, 0, 0x20ac];\nfunction stringToPDFString(str) {\n  if (str[0] >= \"\\xEF\") {\n    let encoding;\n    if (str[0] === \"\\xFE\" && str[1] === \"\\xFF\") {\n      encoding = \"utf-16be\";\n      if (str.length % 2 === 1) {\n        str = str.slice(0, -1);\n      }\n    } else if (str[0] === \"\\xFF\" && str[1] === \"\\xFE\") {\n      encoding = \"utf-16le\";\n      if (str.length % 2 === 1) {\n        str = str.slice(0, -1);\n      }\n    } else if (str[0] === \"\\xEF\" && str[1] === \"\\xBB\" && str[2] === \"\\xBF\") {\n      encoding = \"utf-8\";\n    }\n    if (encoding) {\n      try {\n        const decoder = new TextDecoder(encoding, {\n          fatal: true\n        });\n        const buffer = stringToBytes(str);\n        const decoded = decoder.decode(buffer);\n        if (!decoded.includes(\"\\x1b\")) {\n          return decoded;\n        }\n        return decoded.replaceAll(/\\x1b[^\\x1b]*(?:\\x1b|$)/g, \"\");\n      } catch (ex) {\n        warn(`stringToPDFString: \"${ex}\".`);\n      }\n    }\n  }\n  const strBuf = [];\n  for (let i = 0, ii = str.length; i < ii; i++) {\n    const charCode = str.charCodeAt(i);\n    if (charCode === 0x1b) {\n      while (++i < ii && str.charCodeAt(i) !== 0x1b) {}\n      continue;\n    }\n    const code = PDFStringTranslateTable[charCode];\n    strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));\n  }\n  return strBuf.join(\"\");\n}\nfunction stringToUTF8String(str) {\n  return decodeURIComponent(escape(str));\n}\nfunction utf8StringToString(str) {\n  return unescape(encodeURIComponent(str));\n}\nfunction isArrayBuffer(v) {\n  return typeof v === \"object\" && v?.byteLength !== undefined;\n}\nfunction isArrayEqual(arr1, arr2) {\n  if (arr1.length !== arr2.length) {\n    return false;\n  }\n  for (let i = 0, ii = arr1.length; i < ii; i++) {\n    if (arr1[i] !== arr2[i]) {\n      return false;\n    }\n  }\n  return true;\n}\nfunction getModificationDate(date = new Date()) {\n  const buffer = [date.getUTCFullYear().toString(), (date.getUTCMonth() + 1).toString().padStart(2, \"0\"), date.getUTCDate().toString().padStart(2, \"0\"), date.getUTCHours().toString().padStart(2, \"0\"), date.getUTCMinutes().toString().padStart(2, \"0\"), date.getUTCSeconds().toString().padStart(2, \"0\")];\n  return buffer.join(\"\");\n}\nclass PromiseCapability {\n  #settled = false;\n  constructor() {\n    this.promise = new Promise((resolve, reject) => {\n      this.resolve = data => {\n        this.#settled = true;\n        resolve(data);\n      };\n      this.reject = reason => {\n        this.#settled = true;\n        reject(reason);\n      };\n    });\n  }\n  get settled() {\n    return this.#settled;\n  }\n}\nlet NormalizeRegex = null;\nlet NormalizationMap = null;\nfunction normalizeUnicode(str) {\n  if (!NormalizeRegex) {\n    NormalizeRegex = /([\\u00a0\\u00b5\\u037e\\u0eb3\\u2000-\\u200a\\u202f\\u2126\\ufb00-\\ufb04\\ufb06\\ufb20-\\ufb36\\ufb38-\\ufb3c\\ufb3e\\ufb40-\\ufb41\\ufb43-\\ufb44\\ufb46-\\ufba1\\ufba4-\\ufba9\\ufbae-\\ufbb1\\ufbd3-\\ufbdc\\ufbde-\\ufbe7\\ufbea-\\ufbf8\\ufbfc-\\ufbfd\\ufc00-\\ufc5d\\ufc64-\\ufcf1\\ufcf5-\\ufd3d\\ufd88\\ufdf4\\ufdfa-\\ufdfb\\ufe71\\ufe77\\ufe79\\ufe7b\\ufe7d]+)|(\\ufb05+)/gu;\n    NormalizationMap = new Map([[\"ﬅ\", \"ſt\"]]);\n  }\n  return str.replaceAll(NormalizeRegex, (_, p1, p2) => {\n    return p1 ? p1.normalize(\"NFKC\") : NormalizationMap.get(p2);\n  });\n}\nfunction getUuid() {\n  if (typeof crypto !== \"undefined\" && typeof crypto?.randomUUID === \"function\") {\n    return crypto.randomUUID();\n  }\n  const buf = new Uint8Array(32);\n  if (typeof crypto !== \"undefined\" && typeof crypto?.getRandomValues === \"function\") {\n    crypto.getRandomValues(buf);\n  } else {\n    for (let i = 0; i < 32; i++) {\n      buf[i] = Math.floor(Math.random() * 255);\n    }\n  }\n  return bytesToString(buf);\n}\nconst AnnotationPrefix = \"pdfjs_internal_id_\";\n\n;// CONCATENATED MODULE: ./src/core/primitives.js\n\nconst CIRCULAR_REF = Symbol(\"CIRCULAR_REF\");\nconst EOF = Symbol(\"EOF\");\nlet CmdCache = Object.create(null);\nlet NameCache = Object.create(null);\nlet RefCache = Object.create(null);\nfunction clearPrimitiveCaches() {\n  CmdCache = Object.create(null);\n  NameCache = Object.create(null);\n  RefCache = Object.create(null);\n}\nclass Name {\n  constructor(name) {\n    this.name = name;\n  }\n  static get(name) {\n    return NameCache[name] ||= new Name(name);\n  }\n}\nclass Cmd {\n  constructor(cmd) {\n    this.cmd = cmd;\n  }\n  static get(cmd) {\n    return CmdCache[cmd] ||= new Cmd(cmd);\n  }\n}\nconst nonSerializable = function nonSerializableClosure() {\n  return nonSerializable;\n};\nclass Dict {\n  constructor(xref = null) {\n    this._map = Object.create(null);\n    this.xref = xref;\n    this.objId = null;\n    this.suppressEncryption = false;\n    this.__nonSerializable__ = nonSerializable;\n  }\n  assignXref(newXref) {\n    this.xref = newXref;\n  }\n  get size() {\n    return Object.keys(this._map).length;\n  }\n  get(key1, key2, key3) {\n    let value = this._map[key1];\n    if (value === undefined && key2 !== undefined) {\n      value = this._map[key2];\n      if (value === undefined && key3 !== undefined) {\n        value = this._map[key3];\n      }\n    }\n    if (value instanceof Ref && this.xref) {\n      return this.xref.fetch(value, this.suppressEncryption);\n    }\n    return value;\n  }\n  async getAsync(key1, key2, key3) {\n    let value = this._map[key1];\n    if (value === undefined && key2 !== undefined) {\n      value = this._map[key2];\n      if (value === undefined && key3 !== undefined) {\n        value = this._map[key3];\n      }\n    }\n    if (value instanceof Ref && this.xref) {\n      return this.xref.fetchAsync(value, this.suppressEncryption);\n    }\n    return value;\n  }\n  getArray(key1, key2, key3) {\n    let value = this._map[key1];\n    if (value === undefined && key2 !== undefined) {\n      value = this._map[key2];\n      if (value === undefined && key3 !== undefined) {\n        value = this._map[key3];\n      }\n    }\n    if (value instanceof Ref && this.xref) {\n      value = this.xref.fetch(value, this.suppressEncryption);\n    }\n    if (Array.isArray(value)) {\n      value = value.slice();\n      for (let i = 0, ii = value.length; i < ii; i++) {\n        if (value[i] instanceof Ref && this.xref) {\n          value[i] = this.xref.fetch(value[i], this.suppressEncryption);\n        }\n      }\n    }\n    return value;\n  }\n  getRaw(key) {\n    return this._map[key];\n  }\n  getKeys() {\n    return Object.keys(this._map);\n  }\n  getRawValues() {\n    return Object.values(this._map);\n  }\n  set(key, value) {\n    this._map[key] = value;\n  }\n  has(key) {\n    return this._map[key] !== undefined;\n  }\n  forEach(callback) {\n    for (const key in this._map) {\n      callback(key, this.get(key));\n    }\n  }\n  static get empty() {\n    const emptyDict = new Dict(null);\n    emptyDict.set = (key, value) => {\n      unreachable(\"Should not call `set` on the empty dictionary.\");\n    };\n    return shadow(this, \"empty\", emptyDict);\n  }\n  static merge({\n    xref,\n    dictArray,\n    mergeSubDicts = false\n  }) {\n    const mergedDict = new Dict(xref),\n      properties = new Map();\n    for (const dict of dictArray) {\n      if (!(dict instanceof Dict)) {\n        continue;\n      }\n      for (const [key, value] of Object.entries(dict._map)) {\n        let property = properties.get(key);\n        if (property === undefined) {\n          property = [];\n          properties.set(key, property);\n        } else if (!mergeSubDicts || !(value instanceof Dict)) {\n          continue;\n        }\n        property.push(value);\n      }\n    }\n    for (const [name, values] of properties) {\n      if (values.length === 1 || !(values[0] instanceof Dict)) {\n        mergedDict._map[name] = values[0];\n        continue;\n      }\n      const subDict = new Dict(xref);\n      for (const dict of values) {\n        for (const [key, value] of Object.entries(dict._map)) {\n          if (subDict._map[key] === undefined) {\n            subDict._map[key] = value;\n          }\n        }\n      }\n      if (subDict.size > 0) {\n        mergedDict._map[name] = subDict;\n      }\n    }\n    properties.clear();\n    return mergedDict.size > 0 ? mergedDict : Dict.empty;\n  }\n  clone() {\n    const dict = new Dict(this.xref);\n    for (const key of this.getKeys()) {\n      dict.set(key, this.getRaw(key));\n    }\n    return dict;\n  }\n}\nclass Ref {\n  constructor(num, gen) {\n    this.num = num;\n    this.gen = gen;\n  }\n  toString() {\n    if (this.gen === 0) {\n      return `${this.num}R`;\n    }\n    return `${this.num}R${this.gen}`;\n  }\n  static fromString(str) {\n    const ref = RefCache[str];\n    if (ref) {\n      return ref;\n    }\n    const m = /^(\\d+)R(\\d*)$/.exec(str);\n    if (!m || m[1] === \"0\") {\n      return null;\n    }\n    return RefCache[str] = new Ref(parseInt(m[1]), !m[2] ? 0 : parseInt(m[2]));\n  }\n  static get(num, gen) {\n    const key = gen === 0 ? `${num}R` : `${num}R${gen}`;\n    return RefCache[key] ||= new Ref(num, gen);\n  }\n}\nclass RefSet {\n  constructor(parent = null) {\n    this._set = new Set(parent?._set);\n  }\n  has(ref) {\n    return this._set.has(ref.toString());\n  }\n  put(ref) {\n    this._set.add(ref.toString());\n  }\n  remove(ref) {\n    this._set.delete(ref.toString());\n  }\n  [Symbol.iterator]() {\n    return this._set.values();\n  }\n  clear() {\n    this._set.clear();\n  }\n}\nclass RefSetCache {\n  constructor() {\n    this._map = new Map();\n  }\n  get size() {\n    return this._map.size;\n  }\n  get(ref) {\n    return this._map.get(ref.toString());\n  }\n  has(ref) {\n    return this._map.has(ref.toString());\n  }\n  put(ref, obj) {\n    this._map.set(ref.toString(), obj);\n  }\n  putAlias(ref, aliasRef) {\n    this._map.set(ref.toString(), this.get(aliasRef));\n  }\n  [Symbol.iterator]() {\n    return this._map.values();\n  }\n  clear() {\n    this._map.clear();\n  }\n}\nfunction isName(v, name) {\n  return v instanceof Name && (name === undefined || v.name === name);\n}\nfunction isCmd(v, cmd) {\n  return v instanceof Cmd && (cmd === undefined || v.cmd === cmd);\n}\nfunction isDict(v, type) {\n  return v instanceof Dict && (type === undefined || isName(v.get(\"Type\"), type));\n}\nfunction isRefsEqual(v1, v2) {\n  return v1.num === v2.num && v1.gen === v2.gen;\n}\n\n;// CONCATENATED MODULE: ./src/core/base_stream.js\n\nclass BaseStream {\n  constructor() {\n    if (this.constructor === BaseStream) {\n      unreachable(\"Cannot initialize BaseStream.\");\n    }\n  }\n  get length() {\n    unreachable(\"Abstract getter `length` accessed\");\n  }\n  get isEmpty() {\n    unreachable(\"Abstract getter `isEmpty` accessed\");\n  }\n  get isDataLoaded() {\n    return shadow(this, \"isDataLoaded\", true);\n  }\n  getByte() {\n    unreachable(\"Abstract method `getByte` called\");\n  }\n  getBytes(length) {\n    unreachable(\"Abstract method `getBytes` called\");\n  }\n  peekByte() {\n    const peekedByte = this.getByte();\n    if (peekedByte !== -1) {\n      this.pos--;\n    }\n    return peekedByte;\n  }\n  peekBytes(length) {\n    const bytes = this.getBytes(length);\n    this.pos -= bytes.length;\n    return bytes;\n  }\n  getUint16() {\n    const b0 = this.getByte();\n    const b1 = this.getByte();\n    if (b0 === -1 || b1 === -1) {\n      return -1;\n    }\n    return (b0 << 8) + b1;\n  }\n  getInt32() {\n    const b0 = this.getByte();\n    const b1 = this.getByte();\n    const b2 = this.getByte();\n    const b3 = this.getByte();\n    return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;\n  }\n  getByteRange(begin, end) {\n    unreachable(\"Abstract method `getByteRange` called\");\n  }\n  getString(length) {\n    return bytesToString(this.getBytes(length));\n  }\n  skip(n) {\n    this.pos += n || 1;\n  }\n  reset() {\n    unreachable(\"Abstract method `reset` called\");\n  }\n  moveStart() {\n    unreachable(\"Abstract method `moveStart` called\");\n  }\n  makeSubStream(start, length, dict = null) {\n    unreachable(\"Abstract method `makeSubStream` called\");\n  }\n  getBaseStreams() {\n    return null;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/core_utils.js\n\n\n\nconst PDF_VERSION_REGEXP = /^[1-9]\\.\\d$/;\nfunction getLookupTableFactory(initializer) {\n  let lookup;\n  return function () {\n    if (initializer) {\n      lookup = Object.create(null);\n      initializer(lookup);\n      initializer = null;\n    }\n    return lookup;\n  };\n}\nclass MissingDataException extends BaseException {\n  constructor(begin, end) {\n    super(`Missing data [${begin}, ${end})`, \"MissingDataException\");\n    this.begin = begin;\n    this.end = end;\n  }\n}\nclass ParserEOFException extends BaseException {\n  constructor(msg) {\n    super(msg, \"ParserEOFException\");\n  }\n}\nclass XRefEntryException extends BaseException {\n  constructor(msg) {\n    super(msg, \"XRefEntryException\");\n  }\n}\nclass XRefParseException extends BaseException {\n  constructor(msg) {\n    super(msg, \"XRefParseException\");\n  }\n}\nfunction arrayBuffersToBytes(arr) {\n  const length = arr.length;\n  if (length === 0) {\n    return new Uint8Array(0);\n  }\n  if (length === 1) {\n    return new Uint8Array(arr[0]);\n  }\n  let dataLength = 0;\n  for (let i = 0; i < length; i++) {\n    dataLength += arr[i].byteLength;\n  }\n  const data = new Uint8Array(dataLength);\n  let pos = 0;\n  for (let i = 0; i < length; i++) {\n    const item = new Uint8Array(arr[i]);\n    data.set(item, pos);\n    pos += item.byteLength;\n  }\n  return data;\n}\nfunction getInheritableProperty({\n  dict,\n  key,\n  getArray = false,\n  stopWhenFound = true\n}) {\n  let values;\n  const visited = new RefSet();\n  while (dict instanceof Dict && !(dict.objId && visited.has(dict.objId))) {\n    if (dict.objId) {\n      visited.put(dict.objId);\n    }\n    const value = getArray ? dict.getArray(key) : dict.get(key);\n    if (value !== undefined) {\n      if (stopWhenFound) {\n        return value;\n      }\n      (values ||= []).push(value);\n    }\n    dict = dict.get(\"Parent\");\n  }\n  return values;\n}\nconst ROMAN_NUMBER_MAP = [\"\", \"C\", \"CC\", \"CCC\", \"CD\", \"D\", \"DC\", \"DCC\", \"DCCC\", \"CM\", \"\", \"X\", \"XX\", \"XXX\", \"XL\", \"L\", \"LX\", \"LXX\", \"LXXX\", \"XC\", \"\", \"I\", \"II\", \"III\", \"IV\", \"V\", \"VI\", \"VII\", \"VIII\", \"IX\"];\nfunction toRomanNumerals(number, lowerCase = false) {\n  assert(Number.isInteger(number) && number > 0, \"The number should be a positive integer.\");\n  const romanBuf = [];\n  let pos;\n  while (number >= 1000) {\n    number -= 1000;\n    romanBuf.push(\"M\");\n  }\n  pos = number / 100 | 0;\n  number %= 100;\n  romanBuf.push(ROMAN_NUMBER_MAP[pos]);\n  pos = number / 10 | 0;\n  number %= 10;\n  romanBuf.push(ROMAN_NUMBER_MAP[10 + pos]);\n  romanBuf.push(ROMAN_NUMBER_MAP[20 + number]);\n  const romanStr = romanBuf.join(\"\");\n  return lowerCase ? romanStr.toLowerCase() : romanStr;\n}\nfunction log2(x) {\n  if (x <= 0) {\n    return 0;\n  }\n  return Math.ceil(Math.log2(x));\n}\nfunction readInt8(data, offset) {\n  return data[offset] << 24 >> 24;\n}\nfunction readUint16(data, offset) {\n  return data[offset] << 8 | data[offset + 1];\n}\nfunction readUint32(data, offset) {\n  return (data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3]) >>> 0;\n}\nfunction isWhiteSpace(ch) {\n  return ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a;\n}\nfunction parseXFAPath(path) {\n  const positionPattern = /(.+)\\[(\\d+)\\]$/;\n  return path.split(\".\").map(component => {\n    const m = component.match(positionPattern);\n    if (m) {\n      return {\n        name: m[1],\n        pos: parseInt(m[2], 10)\n      };\n    }\n    return {\n      name: component,\n      pos: 0\n    };\n  });\n}\nfunction escapePDFName(str) {\n  const buffer = [];\n  let start = 0;\n  for (let i = 0, ii = str.length; i < ii; i++) {\n    const char = str.charCodeAt(i);\n    if (char < 0x21 || char > 0x7e || char === 0x23 || char === 0x28 || char === 0x29 || char === 0x3c || char === 0x3e || char === 0x5b || char === 0x5d || char === 0x7b || char === 0x7d || char === 0x2f || char === 0x25) {\n      if (start < i) {\n        buffer.push(str.substring(start, i));\n      }\n      buffer.push(`#${char.toString(16)}`);\n      start = i + 1;\n    }\n  }\n  if (buffer.length === 0) {\n    return str;\n  }\n  if (start < str.length) {\n    buffer.push(str.substring(start, str.length));\n  }\n  return buffer.join(\"\");\n}\nfunction escapeString(str) {\n  return str.replaceAll(/([()\\\\\\n\\r])/g, match => {\n    if (match === \"\\n\") {\n      return \"\\\\n\";\n    } else if (match === \"\\r\") {\n      return \"\\\\r\";\n    }\n    return `\\\\${match}`;\n  });\n}\nfunction _collectJS(entry, xref, list, parents) {\n  if (!entry) {\n    return;\n  }\n  let parent = null;\n  if (entry instanceof Ref) {\n    if (parents.has(entry)) {\n      return;\n    }\n    parent = entry;\n    parents.put(parent);\n    entry = xref.fetch(entry);\n  }\n  if (Array.isArray(entry)) {\n    for (const element of entry) {\n      _collectJS(element, xref, list, parents);\n    }\n  } else if (entry instanceof Dict) {\n    if (isName(entry.get(\"S\"), \"JavaScript\")) {\n      const js = entry.get(\"JS\");\n      let code;\n      if (js instanceof BaseStream) {\n        code = js.getString();\n      } else if (typeof js === \"string\") {\n        code = js;\n      }\n      code &&= stringToPDFString(code).replaceAll(\"\\x00\", \"\");\n      if (code) {\n        list.push(code);\n      }\n    }\n    _collectJS(entry.getRaw(\"Next\"), xref, list, parents);\n  }\n  if (parent) {\n    parents.remove(parent);\n  }\n}\nfunction collectActions(xref, dict, eventType) {\n  const actions = Object.create(null);\n  const additionalActionsDicts = getInheritableProperty({\n    dict,\n    key: \"AA\",\n    stopWhenFound: false\n  });\n  if (additionalActionsDicts) {\n    for (let i = additionalActionsDicts.length - 1; i >= 0; i--) {\n      const additionalActions = additionalActionsDicts[i];\n      if (!(additionalActions instanceof Dict)) {\n        continue;\n      }\n      for (const key of additionalActions.getKeys()) {\n        const action = eventType[key];\n        if (!action) {\n          continue;\n        }\n        const actionDict = additionalActions.getRaw(key);\n        const parents = new RefSet();\n        const list = [];\n        _collectJS(actionDict, xref, list, parents);\n        if (list.length > 0) {\n          actions[action] = list;\n        }\n      }\n    }\n  }\n  if (dict.has(\"A\")) {\n    const actionDict = dict.get(\"A\");\n    const parents = new RefSet();\n    const list = [];\n    _collectJS(actionDict, xref, list, parents);\n    if (list.length > 0) {\n      actions.Action = list;\n    }\n  }\n  return objectSize(actions) > 0 ? actions : null;\n}\nconst XMLEntities = {\n  0x3c: \"&lt;\",\n  0x3e: \"&gt;\",\n  0x26: \"&amp;\",\n  0x22: \"&quot;\",\n  0x27: \"&apos;\"\n};\nfunction encodeToXmlString(str) {\n  const buffer = [];\n  let start = 0;\n  for (let i = 0, ii = str.length; i < ii; i++) {\n    const char = str.codePointAt(i);\n    if (0x20 <= char && char <= 0x7e) {\n      const entity = XMLEntities[char];\n      if (entity) {\n        if (start < i) {\n          buffer.push(str.substring(start, i));\n        }\n        buffer.push(entity);\n        start = i + 1;\n      }\n    } else {\n      if (start < i) {\n        buffer.push(str.substring(start, i));\n      }\n      buffer.push(`&#x${char.toString(16).toUpperCase()};`);\n      if (char > 0xd7ff && (char < 0xe000 || char > 0xfffd)) {\n        i++;\n      }\n      start = i + 1;\n    }\n  }\n  if (buffer.length === 0) {\n    return str;\n  }\n  if (start < str.length) {\n    buffer.push(str.substring(start, str.length));\n  }\n  return buffer.join(\"\");\n}\nfunction validateFontName(fontFamily, mustWarn = false) {\n  const m = /^(\"|').*(\"|')$/.exec(fontFamily);\n  if (m && m[1] === m[2]) {\n    const re = new RegExp(`[^\\\\\\\\]${m[1]}`);\n    if (re.test(fontFamily.slice(1, -1))) {\n      if (mustWarn) {\n        warn(`FontFamily contains unescaped ${m[1]}: ${fontFamily}.`);\n      }\n      return false;\n    }\n  } else {\n    for (const ident of fontFamily.split(/[ \\t]+/)) {\n      if (/^(\\d|(-(\\d|-)))/.test(ident) || !/^[\\w-\\\\]+$/.test(ident)) {\n        if (mustWarn) {\n          warn(`FontFamily contains invalid <custom-ident>: ${fontFamily}.`);\n        }\n        return false;\n      }\n    }\n  }\n  return true;\n}\nfunction validateCSSFont(cssFontInfo) {\n  const DEFAULT_CSS_FONT_OBLIQUE = \"14\";\n  const DEFAULT_CSS_FONT_WEIGHT = \"400\";\n  const CSS_FONT_WEIGHT_VALUES = new Set([\"100\", \"200\", \"300\", \"400\", \"500\", \"600\", \"700\", \"800\", \"900\", \"1000\", \"normal\", \"bold\", \"bolder\", \"lighter\"]);\n  const {\n    fontFamily,\n    fontWeight,\n    italicAngle\n  } = cssFontInfo;\n  if (!validateFontName(fontFamily, true)) {\n    return false;\n  }\n  const weight = fontWeight ? fontWeight.toString() : \"\";\n  cssFontInfo.fontWeight = CSS_FONT_WEIGHT_VALUES.has(weight) ? weight : DEFAULT_CSS_FONT_WEIGHT;\n  const angle = parseFloat(italicAngle);\n  cssFontInfo.italicAngle = isNaN(angle) || angle < -90 || angle > 90 ? DEFAULT_CSS_FONT_OBLIQUE : italicAngle.toString();\n  return true;\n}\nfunction recoverJsURL(str) {\n  const URL_OPEN_METHODS = [\"app.launchURL\", \"window.open\", \"xfa.host.gotoURL\"];\n  const regex = new RegExp(\"^\\\\s*(\" + URL_OPEN_METHODS.join(\"|\").replaceAll(\".\", \"\\\\.\") + \")\\\\((?:'|\\\")([^'\\\"]*)(?:'|\\\")(?:,\\\\s*(\\\\w+)\\\\)|\\\\))\", \"i\");\n  const jsUrl = regex.exec(str);\n  if (jsUrl?.[2]) {\n    const url = jsUrl[2];\n    let newWindow = false;\n    if (jsUrl[3] === \"true\" && jsUrl[1] === \"app.launchURL\") {\n      newWindow = true;\n    }\n    return {\n      url,\n      newWindow\n    };\n  }\n  return null;\n}\nfunction numberToString(value) {\n  if (Number.isInteger(value)) {\n    return value.toString();\n  }\n  const roundedValue = Math.round(value * 100);\n  if (roundedValue % 100 === 0) {\n    return (roundedValue / 100).toString();\n  }\n  if (roundedValue % 10 === 0) {\n    return value.toFixed(1);\n  }\n  return value.toFixed(2);\n}\nfunction getNewAnnotationsMap(annotationStorage) {\n  if (!annotationStorage) {\n    return null;\n  }\n  const newAnnotationsByPage = new Map();\n  for (const [key, value] of annotationStorage) {\n    if (!key.startsWith(AnnotationEditorPrefix)) {\n      continue;\n    }\n    let annotations = newAnnotationsByPage.get(value.pageIndex);\n    if (!annotations) {\n      annotations = [];\n      newAnnotationsByPage.set(value.pageIndex, annotations);\n    }\n    annotations.push(value);\n  }\n  return newAnnotationsByPage.size > 0 ? newAnnotationsByPage : null;\n}\nfunction isAscii(str) {\n  return /^[\\x00-\\x7F]*$/.test(str);\n}\nfunction stringToUTF16HexString(str) {\n  const buf = [];\n  for (let i = 0, ii = str.length; i < ii; i++) {\n    const char = str.charCodeAt(i);\n    buf.push((char >> 8 & 0xff).toString(16).padStart(2, \"0\"), (char & 0xff).toString(16).padStart(2, \"0\"));\n  }\n  return buf.join(\"\");\n}\nfunction stringToUTF16String(str, bigEndian = false) {\n  const buf = [];\n  if (bigEndian) {\n    buf.push(\"\\xFE\\xFF\");\n  }\n  for (let i = 0, ii = str.length; i < ii; i++) {\n    const char = str.charCodeAt(i);\n    buf.push(String.fromCharCode(char >> 8 & 0xff), String.fromCharCode(char & 0xff));\n  }\n  return buf.join(\"\");\n}\nfunction getRotationMatrix(rotation, width, height) {\n  switch (rotation) {\n    case 90:\n      return [0, 1, -1, 0, width, 0];\n    case 180:\n      return [-1, 0, 0, -1, width, height];\n    case 270:\n      return [0, -1, 1, 0, 0, height];\n    default:\n      throw new Error(\"Invalid rotation\");\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/stream.js\n\n\nclass Stream extends BaseStream {\n  constructor(arrayBuffer, start, length, dict) {\n    super();\n    this.bytes = arrayBuffer instanceof Uint8Array ? arrayBuffer : new Uint8Array(arrayBuffer);\n    this.start = start || 0;\n    this.pos = this.start;\n    this.end = start + length || this.bytes.length;\n    this.dict = dict;\n  }\n  get length() {\n    return this.end - this.start;\n  }\n  get isEmpty() {\n    return this.length === 0;\n  }\n  getByte() {\n    if (this.pos >= this.end) {\n      return -1;\n    }\n    return this.bytes[this.pos++];\n  }\n  getBytes(length) {\n    const bytes = this.bytes;\n    const pos = this.pos;\n    const strEnd = this.end;\n    if (!length) {\n      return bytes.subarray(pos, strEnd);\n    }\n    let end = pos + length;\n    if (end > strEnd) {\n      end = strEnd;\n    }\n    this.pos = end;\n    return bytes.subarray(pos, end);\n  }\n  getByteRange(begin, end) {\n    if (begin < 0) {\n      begin = 0;\n    }\n    if (end > this.end) {\n      end = this.end;\n    }\n    return this.bytes.subarray(begin, end);\n  }\n  reset() {\n    this.pos = this.start;\n  }\n  moveStart() {\n    this.start = this.pos;\n  }\n  makeSubStream(start, length, dict = null) {\n    return new Stream(this.bytes.buffer, start, length, dict);\n  }\n}\nclass StringStream extends Stream {\n  constructor(str) {\n    super(stringToBytes(str));\n  }\n}\nclass NullStream extends Stream {\n  constructor() {\n    super(new Uint8Array(0));\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/chunked_stream.js\n\n\n\nclass ChunkedStream extends Stream {\n  constructor(length, chunkSize, manager) {\n    super(new Uint8Array(length), 0, length, null);\n    this.chunkSize = chunkSize;\n    this._loadedChunks = new Set();\n    this.numChunks = Math.ceil(length / chunkSize);\n    this.manager = manager;\n    this.progressiveDataLength = 0;\n    this.lastSuccessfulEnsureByteChunk = -1;\n  }\n  getMissingChunks() {\n    const chunks = [];\n    for (let chunk = 0, n = this.numChunks; chunk < n; ++chunk) {\n      if (!this._loadedChunks.has(chunk)) {\n        chunks.push(chunk);\n      }\n    }\n    return chunks;\n  }\n  get numChunksLoaded() {\n    return this._loadedChunks.size;\n  }\n  get isDataLoaded() {\n    return this.numChunksLoaded === this.numChunks;\n  }\n  onReceiveData(begin, chunk) {\n    const chunkSize = this.chunkSize;\n    if (begin % chunkSize !== 0) {\n      throw new Error(`Bad begin offset: ${begin}`);\n    }\n    const end = begin + chunk.byteLength;\n    if (end % chunkSize !== 0 && end !== this.bytes.length) {\n      throw new Error(`Bad end offset: ${end}`);\n    }\n    this.bytes.set(new Uint8Array(chunk), begin);\n    const beginChunk = Math.floor(begin / chunkSize);\n    const endChunk = Math.floor((end - 1) / chunkSize) + 1;\n    for (let curChunk = beginChunk; curChunk < endChunk; ++curChunk) {\n      this._loadedChunks.add(curChunk);\n    }\n  }\n  onReceiveProgressiveData(data) {\n    let position = this.progressiveDataLength;\n    const beginChunk = Math.floor(position / this.chunkSize);\n    this.bytes.set(new Uint8Array(data), position);\n    position += data.byteLength;\n    this.progressiveDataLength = position;\n    const endChunk = position >= this.end ? this.numChunks : Math.floor(position / this.chunkSize);\n    for (let curChunk = beginChunk; curChunk < endChunk; ++curChunk) {\n      this._loadedChunks.add(curChunk);\n    }\n  }\n  ensureByte(pos) {\n    if (pos < this.progressiveDataLength) {\n      return;\n    }\n    const chunk = Math.floor(pos / this.chunkSize);\n    if (chunk > this.numChunks) {\n      return;\n    }\n    if (chunk === this.lastSuccessfulEnsureByteChunk) {\n      return;\n    }\n    if (!this._loadedChunks.has(chunk)) {\n      throw new MissingDataException(pos, pos + 1);\n    }\n    this.lastSuccessfulEnsureByteChunk = chunk;\n  }\n  ensureRange(begin, end) {\n    if (begin >= end) {\n      return;\n    }\n    if (end <= this.progressiveDataLength) {\n      return;\n    }\n    const beginChunk = Math.floor(begin / this.chunkSize);\n    if (beginChunk > this.numChunks) {\n      return;\n    }\n    const endChunk = Math.min(Math.floor((end - 1) / this.chunkSize) + 1, this.numChunks);\n    for (let chunk = beginChunk; chunk < endChunk; ++chunk) {\n      if (!this._loadedChunks.has(chunk)) {\n        throw new MissingDataException(begin, end);\n      }\n    }\n  }\n  nextEmptyChunk(beginChunk) {\n    const numChunks = this.numChunks;\n    for (let i = 0; i < numChunks; ++i) {\n      const chunk = (beginChunk + i) % numChunks;\n      if (!this._loadedChunks.has(chunk)) {\n        return chunk;\n      }\n    }\n    return null;\n  }\n  hasChunk(chunk) {\n    return this._loadedChunks.has(chunk);\n  }\n  getByte() {\n    const pos = this.pos;\n    if (pos >= this.end) {\n      return -1;\n    }\n    if (pos >= this.progressiveDataLength) {\n      this.ensureByte(pos);\n    }\n    return this.bytes[this.pos++];\n  }\n  getBytes(length) {\n    const bytes = this.bytes;\n    const pos = this.pos;\n    const strEnd = this.end;\n    if (!length) {\n      if (strEnd > this.progressiveDataLength) {\n        this.ensureRange(pos, strEnd);\n      }\n      return bytes.subarray(pos, strEnd);\n    }\n    let end = pos + length;\n    if (end > strEnd) {\n      end = strEnd;\n    }\n    if (end > this.progressiveDataLength) {\n      this.ensureRange(pos, end);\n    }\n    this.pos = end;\n    return bytes.subarray(pos, end);\n  }\n  getByteRange(begin, end) {\n    if (begin < 0) {\n      begin = 0;\n    }\n    if (end > this.end) {\n      end = this.end;\n    }\n    if (end > this.progressiveDataLength) {\n      this.ensureRange(begin, end);\n    }\n    return this.bytes.subarray(begin, end);\n  }\n  makeSubStream(start, length, dict = null) {\n    if (length) {\n      if (start + length > this.progressiveDataLength) {\n        this.ensureRange(start, start + length);\n      }\n    } else if (start >= this.progressiveDataLength) {\n      this.ensureByte(start);\n    }\n    function ChunkedStreamSubstream() {}\n    ChunkedStreamSubstream.prototype = Object.create(this);\n    ChunkedStreamSubstream.prototype.getMissingChunks = function () {\n      const chunkSize = this.chunkSize;\n      const beginChunk = Math.floor(this.start / chunkSize);\n      const endChunk = Math.floor((this.end - 1) / chunkSize) + 1;\n      const missingChunks = [];\n      for (let chunk = beginChunk; chunk < endChunk; ++chunk) {\n        if (!this._loadedChunks.has(chunk)) {\n          missingChunks.push(chunk);\n        }\n      }\n      return missingChunks;\n    };\n    Object.defineProperty(ChunkedStreamSubstream.prototype, \"isDataLoaded\", {\n      get() {\n        if (this.numChunksLoaded === this.numChunks) {\n          return true;\n        }\n        return this.getMissingChunks().length === 0;\n      },\n      configurable: true\n    });\n    const subStream = new ChunkedStreamSubstream();\n    subStream.pos = subStream.start = start;\n    subStream.end = start + length || this.end;\n    subStream.dict = dict;\n    return subStream;\n  }\n  getBaseStreams() {\n    return [this];\n  }\n}\nclass ChunkedStreamManager {\n  constructor(pdfNetworkStream, args) {\n    this.length = args.length;\n    this.chunkSize = args.rangeChunkSize;\n    this.stream = new ChunkedStream(this.length, this.chunkSize, this);\n    this.pdfNetworkStream = pdfNetworkStream;\n    this.disableAutoFetch = args.disableAutoFetch;\n    this.msgHandler = args.msgHandler;\n    this.currRequestId = 0;\n    this._chunksNeededByRequest = new Map();\n    this._requestsByChunk = new Map();\n    this._promisesByRequest = new Map();\n    this.progressiveDataLength = 0;\n    this.aborted = false;\n    this._loadedStreamCapability = new PromiseCapability();\n  }\n  sendRequest(begin, end) {\n    const rangeReader = this.pdfNetworkStream.getRangeReader(begin, end);\n    if (!rangeReader.isStreamingSupported) {\n      rangeReader.onProgress = this.onProgress.bind(this);\n    }\n    let chunks = [],\n      loaded = 0;\n    return new Promise((resolve, reject) => {\n      const readChunk = ({\n        value,\n        done\n      }) => {\n        try {\n          if (done) {\n            const chunkData = arrayBuffersToBytes(chunks);\n            chunks = null;\n            resolve(chunkData);\n            return;\n          }\n          loaded += value.byteLength;\n          if (rangeReader.isStreamingSupported) {\n            this.onProgress({\n              loaded\n            });\n          }\n          chunks.push(value);\n          rangeReader.read().then(readChunk, reject);\n        } catch (e) {\n          reject(e);\n        }\n      };\n      rangeReader.read().then(readChunk, reject);\n    }).then(data => {\n      if (this.aborted) {\n        return;\n      }\n      this.onReceiveData({\n        chunk: data,\n        begin\n      });\n    });\n  }\n  requestAllChunks(noFetch = false) {\n    if (!noFetch) {\n      const missingChunks = this.stream.getMissingChunks();\n      this._requestChunks(missingChunks);\n    }\n    return this._loadedStreamCapability.promise;\n  }\n  _requestChunks(chunks) {\n    const requestId = this.currRequestId++;\n    const chunksNeeded = new Set();\n    this._chunksNeededByRequest.set(requestId, chunksNeeded);\n    for (const chunk of chunks) {\n      if (!this.stream.hasChunk(chunk)) {\n        chunksNeeded.add(chunk);\n      }\n    }\n    if (chunksNeeded.size === 0) {\n      return Promise.resolve();\n    }\n    const capability = new PromiseCapability();\n    this._promisesByRequest.set(requestId, capability);\n    const chunksToRequest = [];\n    for (const chunk of chunksNeeded) {\n      let requestIds = this._requestsByChunk.get(chunk);\n      if (!requestIds) {\n        requestIds = [];\n        this._requestsByChunk.set(chunk, requestIds);\n        chunksToRequest.push(chunk);\n      }\n      requestIds.push(requestId);\n    }\n    if (chunksToRequest.length > 0) {\n      const groupedChunksToRequest = this.groupChunks(chunksToRequest);\n      for (const groupedChunk of groupedChunksToRequest) {\n        const begin = groupedChunk.beginChunk * this.chunkSize;\n        const end = Math.min(groupedChunk.endChunk * this.chunkSize, this.length);\n        this.sendRequest(begin, end).catch(capability.reject);\n      }\n    }\n    return capability.promise.catch(reason => {\n      if (this.aborted) {\n        return;\n      }\n      throw reason;\n    });\n  }\n  getStream() {\n    return this.stream;\n  }\n  requestRange(begin, end) {\n    end = Math.min(end, this.length);\n    const beginChunk = this.getBeginChunk(begin);\n    const endChunk = this.getEndChunk(end);\n    const chunks = [];\n    for (let chunk = beginChunk; chunk < endChunk; ++chunk) {\n      chunks.push(chunk);\n    }\n    return this._requestChunks(chunks);\n  }\n  requestRanges(ranges = []) {\n    const chunksToRequest = [];\n    for (const range of ranges) {\n      const beginChunk = this.getBeginChunk(range.begin);\n      const endChunk = this.getEndChunk(range.end);\n      for (let chunk = beginChunk; chunk < endChunk; ++chunk) {\n        if (!chunksToRequest.includes(chunk)) {\n          chunksToRequest.push(chunk);\n        }\n      }\n    }\n    chunksToRequest.sort(function (a, b) {\n      return a - b;\n    });\n    return this._requestChunks(chunksToRequest);\n  }\n  groupChunks(chunks) {\n    const groupedChunks = [];\n    let beginChunk = -1;\n    let prevChunk = -1;\n    for (let i = 0, ii = chunks.length; i < ii; ++i) {\n      const chunk = chunks[i];\n      if (beginChunk < 0) {\n        beginChunk = chunk;\n      }\n      if (prevChunk >= 0 && prevChunk + 1 !== chunk) {\n        groupedChunks.push({\n          beginChunk,\n          endChunk: prevChunk + 1\n        });\n        beginChunk = chunk;\n      }\n      if (i + 1 === chunks.length) {\n        groupedChunks.push({\n          beginChunk,\n          endChunk: chunk + 1\n        });\n      }\n      prevChunk = chunk;\n    }\n    return groupedChunks;\n  }\n  onProgress(args) {\n    this.msgHandler.send(\"DocProgress\", {\n      loaded: this.stream.numChunksLoaded * this.chunkSize + args.loaded,\n      total: this.length\n    });\n  }\n  onReceiveData(args) {\n    const chunk = args.chunk;\n    const isProgressive = args.begin === undefined;\n    const begin = isProgressive ? this.progressiveDataLength : args.begin;\n    const end = begin + chunk.byteLength;\n    const beginChunk = Math.floor(begin / this.chunkSize);\n    const endChunk = end < this.length ? Math.floor(end / this.chunkSize) : Math.ceil(end / this.chunkSize);\n    if (isProgressive) {\n      this.stream.onReceiveProgressiveData(chunk);\n      this.progressiveDataLength = end;\n    } else {\n      this.stream.onReceiveData(begin, chunk);\n    }\n    if (this.stream.isDataLoaded) {\n      this._loadedStreamCapability.resolve(this.stream);\n    }\n    const loadedRequests = [];\n    for (let curChunk = beginChunk; curChunk < endChunk; ++curChunk) {\n      const requestIds = this._requestsByChunk.get(curChunk);\n      if (!requestIds) {\n        continue;\n      }\n      this._requestsByChunk.delete(curChunk);\n      for (const requestId of requestIds) {\n        const chunksNeeded = this._chunksNeededByRequest.get(requestId);\n        if (chunksNeeded.has(curChunk)) {\n          chunksNeeded.delete(curChunk);\n        }\n        if (chunksNeeded.size > 0) {\n          continue;\n        }\n        loadedRequests.push(requestId);\n      }\n    }\n    if (!this.disableAutoFetch && this._requestsByChunk.size === 0) {\n      let nextEmptyChunk;\n      if (this.stream.numChunksLoaded === 1) {\n        const lastChunk = this.stream.numChunks - 1;\n        if (!this.stream.hasChunk(lastChunk)) {\n          nextEmptyChunk = lastChunk;\n        }\n      } else {\n        nextEmptyChunk = this.stream.nextEmptyChunk(endChunk);\n      }\n      if (Number.isInteger(nextEmptyChunk)) {\n        this._requestChunks([nextEmptyChunk]);\n      }\n    }\n    for (const requestId of loadedRequests) {\n      const capability = this._promisesByRequest.get(requestId);\n      this._promisesByRequest.delete(requestId);\n      capability.resolve();\n    }\n    this.msgHandler.send(\"DocProgress\", {\n      loaded: this.stream.numChunksLoaded * this.chunkSize,\n      total: this.length\n    });\n  }\n  onError(err) {\n    this._loadedStreamCapability.reject(err);\n  }\n  getBeginChunk(begin) {\n    return Math.floor(begin / this.chunkSize);\n  }\n  getEndChunk(end) {\n    return Math.floor((end - 1) / this.chunkSize) + 1;\n  }\n  abort(reason) {\n    this.aborted = true;\n    this.pdfNetworkStream?.cancelAllRequests(reason);\n    for (const capability of this._promisesByRequest.values()) {\n      capability.reject(reason);\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/colorspace.js\n\n\n\n\nfunction resizeRgbImage(src, dest, w1, h1, w2, h2, alpha01) {\n  const COMPONENTS = 3;\n  alpha01 = alpha01 !== 1 ? 0 : alpha01;\n  const xRatio = w1 / w2;\n  const yRatio = h1 / h2;\n  let newIndex = 0,\n    oldIndex;\n  const xScaled = new Uint16Array(w2);\n  const w1Scanline = w1 * COMPONENTS;\n  for (let i = 0; i < w2; i++) {\n    xScaled[i] = Math.floor(i * xRatio) * COMPONENTS;\n  }\n  for (let i = 0; i < h2; i++) {\n    const py = Math.floor(i * yRatio) * w1Scanline;\n    for (let j = 0; j < w2; j++) {\n      oldIndex = py + xScaled[j];\n      dest[newIndex++] = src[oldIndex++];\n      dest[newIndex++] = src[oldIndex++];\n      dest[newIndex++] = src[oldIndex++];\n      newIndex += alpha01;\n    }\n  }\n}\nclass ColorSpace {\n  constructor(name, numComps) {\n    if (this.constructor === ColorSpace) {\n      unreachable(\"Cannot initialize ColorSpace.\");\n    }\n    this.name = name;\n    this.numComps = numComps;\n  }\n  getRgb(src, srcOffset) {\n    const rgb = new Uint8ClampedArray(3);\n    this.getRgbItem(src, srcOffset, rgb, 0);\n    return rgb;\n  }\n  getRgbItem(src, srcOffset, dest, destOffset) {\n    unreachable(\"Should not call ColorSpace.getRgbItem\");\n  }\n  getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {\n    unreachable(\"Should not call ColorSpace.getRgbBuffer\");\n  }\n  getOutputLength(inputLength, alpha01) {\n    unreachable(\"Should not call ColorSpace.getOutputLength\");\n  }\n  isPassthrough(bits) {\n    return false;\n  }\n  isDefaultDecode(decodeMap, bpc) {\n    return ColorSpace.isDefaultDecode(decodeMap, this.numComps);\n  }\n  fillRgb(dest, originalWidth, originalHeight, width, height, actualHeight, bpc, comps, alpha01) {\n    const count = originalWidth * originalHeight;\n    let rgbBuf = null;\n    const numComponentColors = 1 << bpc;\n    const needsResizing = originalHeight !== height || originalWidth !== width;\n    if (this.isPassthrough(bpc)) {\n      rgbBuf = comps;\n    } else if (this.numComps === 1 && count > numComponentColors && this.name !== \"DeviceGray\" && this.name !== \"DeviceRGB\") {\n      const allColors = bpc <= 8 ? new Uint8Array(numComponentColors) : new Uint16Array(numComponentColors);\n      for (let i = 0; i < numComponentColors; i++) {\n        allColors[i] = i;\n      }\n      const colorMap = new Uint8ClampedArray(numComponentColors * 3);\n      this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc, 0);\n      if (!needsResizing) {\n        let destPos = 0;\n        for (let i = 0; i < count; ++i) {\n          const key = comps[i] * 3;\n          dest[destPos++] = colorMap[key];\n          dest[destPos++] = colorMap[key + 1];\n          dest[destPos++] = colorMap[key + 2];\n          destPos += alpha01;\n        }\n      } else {\n        rgbBuf = new Uint8Array(count * 3);\n        let rgbPos = 0;\n        for (let i = 0; i < count; ++i) {\n          const key = comps[i] * 3;\n          rgbBuf[rgbPos++] = colorMap[key];\n          rgbBuf[rgbPos++] = colorMap[key + 1];\n          rgbBuf[rgbPos++] = colorMap[key + 2];\n        }\n      }\n    } else if (!needsResizing) {\n      this.getRgbBuffer(comps, 0, width * actualHeight, dest, 0, bpc, alpha01);\n    } else {\n      rgbBuf = new Uint8ClampedArray(count * 3);\n      this.getRgbBuffer(comps, 0, count, rgbBuf, 0, bpc, 0);\n    }\n    if (rgbBuf) {\n      if (needsResizing) {\n        resizeRgbImage(rgbBuf, dest, originalWidth, originalHeight, width, height, alpha01);\n      } else {\n        let destPos = 0,\n          rgbPos = 0;\n        for (let i = 0, ii = width * actualHeight; i < ii; i++) {\n          dest[destPos++] = rgbBuf[rgbPos++];\n          dest[destPos++] = rgbBuf[rgbPos++];\n          dest[destPos++] = rgbBuf[rgbPos++];\n          destPos += alpha01;\n        }\n      }\n    }\n  }\n  get usesZeroToOneRange() {\n    return shadow(this, \"usesZeroToOneRange\", true);\n  }\n  static _cache(cacheKey, xref, localColorSpaceCache, parsedColorSpace) {\n    if (!localColorSpaceCache) {\n      throw new Error('ColorSpace._cache - expected \"localColorSpaceCache\" argument.');\n    }\n    if (!parsedColorSpace) {\n      throw new Error('ColorSpace._cache - expected \"parsedColorSpace\" argument.');\n    }\n    let csName, csRef;\n    if (cacheKey instanceof Ref) {\n      csRef = cacheKey;\n      cacheKey = xref.fetch(cacheKey);\n    }\n    if (cacheKey instanceof Name) {\n      csName = cacheKey.name;\n    }\n    if (csName || csRef) {\n      localColorSpaceCache.set(csName, csRef, parsedColorSpace);\n    }\n  }\n  static getCached(cacheKey, xref, localColorSpaceCache) {\n    if (!localColorSpaceCache) {\n      throw new Error('ColorSpace.getCached - expected \"localColorSpaceCache\" argument.');\n    }\n    if (cacheKey instanceof Ref) {\n      const localColorSpace = localColorSpaceCache.getByRef(cacheKey);\n      if (localColorSpace) {\n        return localColorSpace;\n      }\n      try {\n        cacheKey = xref.fetch(cacheKey);\n      } catch (ex) {\n        if (ex instanceof MissingDataException) {\n          throw ex;\n        }\n      }\n    }\n    if (cacheKey instanceof Name) {\n      const localColorSpace = localColorSpaceCache.getByName(cacheKey.name);\n      if (localColorSpace) {\n        return localColorSpace;\n      }\n    }\n    return null;\n  }\n  static async parseAsync({\n    cs,\n    xref,\n    resources = null,\n    pdfFunctionFactory,\n    localColorSpaceCache\n  }) {\n    const parsedColorSpace = this._parse(cs, xref, resources, pdfFunctionFactory);\n    this._cache(cs, xref, localColorSpaceCache, parsedColorSpace);\n    return parsedColorSpace;\n  }\n  static parse({\n    cs,\n    xref,\n    resources = null,\n    pdfFunctionFactory,\n    localColorSpaceCache\n  }) {\n    const cachedColorSpace = this.getCached(cs, xref, localColorSpaceCache);\n    if (cachedColorSpace) {\n      return cachedColorSpace;\n    }\n    const parsedColorSpace = this._parse(cs, xref, resources, pdfFunctionFactory);\n    this._cache(cs, xref, localColorSpaceCache, parsedColorSpace);\n    return parsedColorSpace;\n  }\n  static _parse(cs, xref, resources = null, pdfFunctionFactory) {\n    cs = xref.fetchIfRef(cs);\n    if (cs instanceof Name) {\n      switch (cs.name) {\n        case \"G\":\n        case \"DeviceGray\":\n          return this.singletons.gray;\n        case \"RGB\":\n        case \"DeviceRGB\":\n          return this.singletons.rgb;\n        case \"CMYK\":\n        case \"DeviceCMYK\":\n          return this.singletons.cmyk;\n        case \"Pattern\":\n          return new PatternCS(null);\n        default:\n          if (resources instanceof Dict) {\n            const colorSpaces = resources.get(\"ColorSpace\");\n            if (colorSpaces instanceof Dict) {\n              const resourcesCS = colorSpaces.get(cs.name);\n              if (resourcesCS) {\n                if (resourcesCS instanceof Name) {\n                  return this._parse(resourcesCS, xref, resources, pdfFunctionFactory);\n                }\n                cs = resourcesCS;\n                break;\n              }\n            }\n          }\n          throw new FormatError(`Unrecognized ColorSpace: ${cs.name}`);\n      }\n    }\n    if (Array.isArray(cs)) {\n      const mode = xref.fetchIfRef(cs[0]).name;\n      let params, numComps, baseCS, whitePoint, blackPoint, gamma;\n      switch (mode) {\n        case \"G\":\n        case \"DeviceGray\":\n          return this.singletons.gray;\n        case \"RGB\":\n        case \"DeviceRGB\":\n          return this.singletons.rgb;\n        case \"CMYK\":\n        case \"DeviceCMYK\":\n          return this.singletons.cmyk;\n        case \"CalGray\":\n          params = xref.fetchIfRef(cs[1]);\n          whitePoint = params.getArray(\"WhitePoint\");\n          blackPoint = params.getArray(\"BlackPoint\");\n          gamma = params.get(\"Gamma\");\n          return new CalGrayCS(whitePoint, blackPoint, gamma);\n        case \"CalRGB\":\n          params = xref.fetchIfRef(cs[1]);\n          whitePoint = params.getArray(\"WhitePoint\");\n          blackPoint = params.getArray(\"BlackPoint\");\n          gamma = params.getArray(\"Gamma\");\n          const matrix = params.getArray(\"Matrix\");\n          return new CalRGBCS(whitePoint, blackPoint, gamma, matrix);\n        case \"ICCBased\":\n          const stream = xref.fetchIfRef(cs[1]);\n          const dict = stream.dict;\n          numComps = dict.get(\"N\");\n          const alt = dict.get(\"Alternate\");\n          if (alt) {\n            const altCS = this._parse(alt, xref, resources, pdfFunctionFactory);\n            if (altCS.numComps === numComps) {\n              return altCS;\n            }\n            warn(\"ICCBased color space: Ignoring incorrect /Alternate entry.\");\n          }\n          if (numComps === 1) {\n            return this.singletons.gray;\n          } else if (numComps === 3) {\n            return this.singletons.rgb;\n          } else if (numComps === 4) {\n            return this.singletons.cmyk;\n          }\n          break;\n        case \"Pattern\":\n          baseCS = cs[1] || null;\n          if (baseCS) {\n            baseCS = this._parse(baseCS, xref, resources, pdfFunctionFactory);\n          }\n          return new PatternCS(baseCS);\n        case \"I\":\n        case \"Indexed\":\n          baseCS = this._parse(cs[1], xref, resources, pdfFunctionFactory);\n          const hiVal = xref.fetchIfRef(cs[2]) + 1;\n          const lookup = xref.fetchIfRef(cs[3]);\n          return new IndexedCS(baseCS, hiVal, lookup);\n        case \"Separation\":\n        case \"DeviceN\":\n          const name = xref.fetchIfRef(cs[1]);\n          numComps = Array.isArray(name) ? name.length : 1;\n          baseCS = this._parse(cs[2], xref, resources, pdfFunctionFactory);\n          const tintFn = pdfFunctionFactory.create(cs[3]);\n          return new AlternateCS(numComps, baseCS, tintFn);\n        case \"Lab\":\n          params = xref.fetchIfRef(cs[1]);\n          whitePoint = params.getArray(\"WhitePoint\");\n          blackPoint = params.getArray(\"BlackPoint\");\n          const range = params.getArray(\"Range\");\n          return new LabCS(whitePoint, blackPoint, range);\n        default:\n          throw new FormatError(`Unimplemented ColorSpace object: ${mode}`);\n      }\n    }\n    throw new FormatError(`Unrecognized ColorSpace object: ${cs}`);\n  }\n  static isDefaultDecode(decode, numComps) {\n    if (!Array.isArray(decode)) {\n      return true;\n    }\n    if (numComps * 2 !== decode.length) {\n      warn(\"The decode map is not the correct length\");\n      return true;\n    }\n    for (let i = 0, ii = decode.length; i < ii; i += 2) {\n      if (decode[i] !== 0 || decode[i + 1] !== 1) {\n        return false;\n      }\n    }\n    return true;\n  }\n  static get singletons() {\n    return shadow(this, \"singletons\", {\n      get gray() {\n        return shadow(this, \"gray\", new DeviceGrayCS());\n      },\n      get rgb() {\n        return shadow(this, \"rgb\", new DeviceRgbCS());\n      },\n      get cmyk() {\n        return shadow(this, \"cmyk\", new DeviceCmykCS());\n      }\n    });\n  }\n}\nclass AlternateCS extends ColorSpace {\n  constructor(numComps, base, tintFn) {\n    super(\"Alternate\", numComps);\n    this.base = base;\n    this.tintFn = tintFn;\n    this.tmpBuf = new Float32Array(base.numComps);\n  }\n  getRgbItem(src, srcOffset, dest, destOffset) {\n    const tmpBuf = this.tmpBuf;\n    this.tintFn(src, srcOffset, tmpBuf, 0);\n    this.base.getRgbItem(tmpBuf, 0, dest, destOffset);\n  }\n  getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {\n    const tintFn = this.tintFn;\n    const base = this.base;\n    const scale = 1 / ((1 << bits) - 1);\n    const baseNumComps = base.numComps;\n    const usesZeroToOneRange = base.usesZeroToOneRange;\n    const isPassthrough = (base.isPassthrough(8) || !usesZeroToOneRange) && alpha01 === 0;\n    let pos = isPassthrough ? destOffset : 0;\n    const baseBuf = isPassthrough ? dest : new Uint8ClampedArray(baseNumComps * count);\n    const numComps = this.numComps;\n    const scaled = new Float32Array(numComps);\n    const tinted = new Float32Array(baseNumComps);\n    let i, j;\n    for (i = 0; i < count; i++) {\n      for (j = 0; j < numComps; j++) {\n        scaled[j] = src[srcOffset++] * scale;\n      }\n      tintFn(scaled, 0, tinted, 0);\n      if (usesZeroToOneRange) {\n        for (j = 0; j < baseNumComps; j++) {\n          baseBuf[pos++] = tinted[j] * 255;\n        }\n      } else {\n        base.getRgbItem(tinted, 0, baseBuf, pos);\n        pos += baseNumComps;\n      }\n    }\n    if (!isPassthrough) {\n      base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8, alpha01);\n    }\n  }\n  getOutputLength(inputLength, alpha01) {\n    return this.base.getOutputLength(inputLength * this.base.numComps / this.numComps, alpha01);\n  }\n}\nclass PatternCS extends ColorSpace {\n  constructor(baseCS) {\n    super(\"Pattern\", null);\n    this.base = baseCS;\n  }\n  isDefaultDecode(decodeMap, bpc) {\n    unreachable(\"Should not call PatternCS.isDefaultDecode\");\n  }\n}\nclass IndexedCS extends ColorSpace {\n  constructor(base, highVal, lookup) {\n    super(\"Indexed\", 1);\n    this.base = base;\n    this.highVal = highVal;\n    const length = base.numComps * highVal;\n    this.lookup = new Uint8Array(length);\n    if (lookup instanceof BaseStream) {\n      const bytes = lookup.getBytes(length);\n      this.lookup.set(bytes);\n    } else if (typeof lookup === \"string\") {\n      for (let i = 0; i < length; ++i) {\n        this.lookup[i] = lookup.charCodeAt(i) & 0xff;\n      }\n    } else {\n      throw new FormatError(`IndexedCS - unrecognized lookup table: ${lookup}`);\n    }\n  }\n  getRgbItem(src, srcOffset, dest, destOffset) {\n    const numComps = this.base.numComps;\n    const start = src[srcOffset] * numComps;\n    this.base.getRgbBuffer(this.lookup, start, 1, dest, destOffset, 8, 0);\n  }\n  getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {\n    const base = this.base;\n    const numComps = base.numComps;\n    const outputDelta = base.getOutputLength(numComps, alpha01);\n    const lookup = this.lookup;\n    for (let i = 0; i < count; ++i) {\n      const lookupPos = src[srcOffset++] * numComps;\n      base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8, alpha01);\n      destOffset += outputDelta;\n    }\n  }\n  getOutputLength(inputLength, alpha01) {\n    return this.base.getOutputLength(inputLength * this.base.numComps, alpha01);\n  }\n  isDefaultDecode(decodeMap, bpc) {\n    if (!Array.isArray(decodeMap)) {\n      return true;\n    }\n    if (decodeMap.length !== 2) {\n      warn(\"Decode map length is not correct\");\n      return true;\n    }\n    if (!Number.isInteger(bpc) || bpc < 1) {\n      warn(\"Bits per component is not correct\");\n      return true;\n    }\n    return decodeMap[0] === 0 && decodeMap[1] === (1 << bpc) - 1;\n  }\n}\nclass DeviceGrayCS extends ColorSpace {\n  constructor() {\n    super(\"DeviceGray\", 1);\n  }\n  getRgbItem(src, srcOffset, dest, destOffset) {\n    const c = src[srcOffset] * 255;\n    dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c;\n  }\n  getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {\n    const scale = 255 / ((1 << bits) - 1);\n    let j = srcOffset,\n      q = destOffset;\n    for (let i = 0; i < count; ++i) {\n      const c = scale * src[j++];\n      dest[q++] = c;\n      dest[q++] = c;\n      dest[q++] = c;\n      q += alpha01;\n    }\n  }\n  getOutputLength(inputLength, alpha01) {\n    return inputLength * (3 + alpha01);\n  }\n}\nclass DeviceRgbCS extends ColorSpace {\n  constructor() {\n    super(\"DeviceRGB\", 3);\n  }\n  getRgbItem(src, srcOffset, dest, destOffset) {\n    dest[destOffset] = src[srcOffset] * 255;\n    dest[destOffset + 1] = src[srcOffset + 1] * 255;\n    dest[destOffset + 2] = src[srcOffset + 2] * 255;\n  }\n  getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {\n    if (bits === 8 && alpha01 === 0) {\n      dest.set(src.subarray(srcOffset, srcOffset + count * 3), destOffset);\n      return;\n    }\n    const scale = 255 / ((1 << bits) - 1);\n    let j = srcOffset,\n      q = destOffset;\n    for (let i = 0; i < count; ++i) {\n      dest[q++] = scale * src[j++];\n      dest[q++] = scale * src[j++];\n      dest[q++] = scale * src[j++];\n      q += alpha01;\n    }\n  }\n  getOutputLength(inputLength, alpha01) {\n    return inputLength * (3 + alpha01) / 3 | 0;\n  }\n  isPassthrough(bits) {\n    return bits === 8;\n  }\n}\nclass DeviceCmykCS extends ColorSpace {\n  constructor() {\n    super(\"DeviceCMYK\", 4);\n  }\n  #toRgb(src, srcOffset, srcScale, dest, destOffset) {\n    const c = src[srcOffset] * srcScale;\n    const m = src[srcOffset + 1] * srcScale;\n    const y = src[srcOffset + 2] * srcScale;\n    const k = src[srcOffset + 3] * srcScale;\n    dest[destOffset] = 255 + c * (-4.387332384609988 * c + 54.48615194189176 * m + 18.82290502165302 * y + 212.25662451639585 * k + -285.2331026137004) + m * (1.7149763477362134 * m - 5.6096736904047315 * y + -17.873870861415444 * k - 5.497006427196366) + y * (-2.5217340131683033 * y - 21.248923337353073 * k + 17.5119270841813) + k * (-21.86122147463605 * k - 189.48180835922747);\n    dest[destOffset + 1] = 255 + c * (8.841041422036149 * c + 60.118027045597366 * m + 6.871425592049007 * y + 31.159100130055922 * k + -79.2970844816548) + m * (-15.310361306967817 * m + 17.575251261109482 * y + 131.35250912493976 * k - 190.9453302588951) + y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) + k * (-20.737325471181034 * k - 187.80453709719578);\n    dest[destOffset + 2] = 255 + c * (0.8842522430003296 * c + 8.078677503112928 * m + 30.89978309703729 * y - 0.23883238689178934 * k + -14.183576799673286) + m * (10.49593273432072 * m + 63.02378494754052 * y + 50.606957656360734 * k - 112.23884253719248) + y * (0.03296041114873217 * y + 115.60384449646641 * k + -193.58209356861505) + k * (-22.33816807309886 * k - 180.12613974708367);\n  }\n  getRgbItem(src, srcOffset, dest, destOffset) {\n    this.#toRgb(src, srcOffset, 1, dest, destOffset);\n  }\n  getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {\n    const scale = 1 / ((1 << bits) - 1);\n    for (let i = 0; i < count; i++) {\n      this.#toRgb(src, srcOffset, scale, dest, destOffset);\n      srcOffset += 4;\n      destOffset += 3 + alpha01;\n    }\n  }\n  getOutputLength(inputLength, alpha01) {\n    return inputLength / 4 * (3 + alpha01) | 0;\n  }\n}\nclass CalGrayCS extends ColorSpace {\n  constructor(whitePoint, blackPoint, gamma) {\n    super(\"CalGray\", 1);\n    if (!whitePoint) {\n      throw new FormatError(\"WhitePoint missing - required for color space CalGray\");\n    }\n    [this.XW, this.YW, this.ZW] = whitePoint;\n    [this.XB, this.YB, this.ZB] = blackPoint || [0, 0, 0];\n    this.G = gamma || 1;\n    if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) {\n      throw new FormatError(`Invalid WhitePoint components for ${this.name}, no fallback available`);\n    }\n    if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {\n      info(`Invalid BlackPoint for ${this.name}, falling back to default.`);\n      this.XB = this.YB = this.ZB = 0;\n    }\n    if (this.XB !== 0 || this.YB !== 0 || this.ZB !== 0) {\n      warn(`${this.name}, BlackPoint: XB: ${this.XB}, YB: ${this.YB}, ` + `ZB: ${this.ZB}, only default values are supported.`);\n    }\n    if (this.G < 1) {\n      info(`Invalid Gamma: ${this.G} for ${this.name}, falling back to default.`);\n      this.G = 1;\n    }\n  }\n  #toRgb(src, srcOffset, dest, destOffset, scale) {\n    const A = src[srcOffset] * scale;\n    const AG = A ** this.G;\n    const L = this.YW * AG;\n    const val = Math.max(295.8 * L ** 0.3333333333333333 - 40.8, 0);\n    dest[destOffset] = val;\n    dest[destOffset + 1] = val;\n    dest[destOffset + 2] = val;\n  }\n  getRgbItem(src, srcOffset, dest, destOffset) {\n    this.#toRgb(src, srcOffset, dest, destOffset, 1);\n  }\n  getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {\n    const scale = 1 / ((1 << bits) - 1);\n    for (let i = 0; i < count; ++i) {\n      this.#toRgb(src, srcOffset, dest, destOffset, scale);\n      srcOffset += 1;\n      destOffset += 3 + alpha01;\n    }\n  }\n  getOutputLength(inputLength, alpha01) {\n    return inputLength * (3 + alpha01);\n  }\n}\nclass CalRGBCS extends ColorSpace {\n  static #BRADFORD_SCALE_MATRIX = new Float32Array([0.8951, 0.2664, -0.1614, -0.7502, 1.7135, 0.0367, 0.0389, -0.0685, 1.0296]);\n  static #BRADFORD_SCALE_INVERSE_MATRIX = new Float32Array([0.9869929, -0.1470543, 0.1599627, 0.4323053, 0.5183603, 0.0492912, -0.0085287, 0.0400428, 0.9684867]);\n  static #SRGB_D65_XYZ_TO_RGB_MATRIX = new Float32Array([3.2404542, -1.5371385, -0.4985314, -0.9692660, 1.8760108, 0.0415560, 0.0556434, -0.2040259, 1.0572252]);\n  static #FLAT_WHITEPOINT_MATRIX = new Float32Array([1, 1, 1]);\n  static #tempNormalizeMatrix = new Float32Array(3);\n  static #tempConvertMatrix1 = new Float32Array(3);\n  static #tempConvertMatrix2 = new Float32Array(3);\n  static #DECODE_L_CONSTANT = ((8 + 16) / 116) ** 3 / 8.0;\n  constructor(whitePoint, blackPoint, gamma, matrix) {\n    super(\"CalRGB\", 3);\n    if (!whitePoint) {\n      throw new FormatError(\"WhitePoint missing - required for color space CalRGB\");\n    }\n    const [XW, YW, ZW] = this.whitePoint = whitePoint;\n    const [XB, YB, ZB] = this.blackPoint = blackPoint || new Float32Array(3);\n    [this.GR, this.GG, this.GB] = gamma || new Float32Array([1, 1, 1]);\n    [this.MXA, this.MYA, this.MZA, this.MXB, this.MYB, this.MZB, this.MXC, this.MYC, this.MZC] = matrix || new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]);\n    if (XW < 0 || ZW < 0 || YW !== 1) {\n      throw new FormatError(`Invalid WhitePoint components for ${this.name}, no fallback available`);\n    }\n    if (XB < 0 || YB < 0 || ZB < 0) {\n      info(`Invalid BlackPoint for ${this.name} [${XB}, ${YB}, ${ZB}], ` + \"falling back to default.\");\n      this.blackPoint = new Float32Array(3);\n    }\n    if (this.GR < 0 || this.GG < 0 || this.GB < 0) {\n      info(`Invalid Gamma [${this.GR}, ${this.GG}, ${this.GB}] for ` + `${this.name}, falling back to default.`);\n      this.GR = this.GG = this.GB = 1;\n    }\n  }\n  #matrixProduct(a, b, result) {\n    result[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\n    result[1] = a[3] * b[0] + a[4] * b[1] + a[5] * b[2];\n    result[2] = a[6] * b[0] + a[7] * b[1] + a[8] * b[2];\n  }\n  #toFlat(sourceWhitePoint, LMS, result) {\n    result[0] = LMS[0] * 1 / sourceWhitePoint[0];\n    result[1] = LMS[1] * 1 / sourceWhitePoint[1];\n    result[2] = LMS[2] * 1 / sourceWhitePoint[2];\n  }\n  #toD65(sourceWhitePoint, LMS, result) {\n    const D65X = 0.95047;\n    const D65Y = 1;\n    const D65Z = 1.08883;\n    result[0] = LMS[0] * D65X / sourceWhitePoint[0];\n    result[1] = LMS[1] * D65Y / sourceWhitePoint[1];\n    result[2] = LMS[2] * D65Z / sourceWhitePoint[2];\n  }\n  #sRGBTransferFunction(color) {\n    if (color <= 0.0031308) {\n      return this.#adjustToRange(0, 1, 12.92 * color);\n    }\n    if (color >= 0.99554525) {\n      return 1;\n    }\n    return this.#adjustToRange(0, 1, (1 + 0.055) * color ** (1 / 2.4) - 0.055);\n  }\n  #adjustToRange(min, max, value) {\n    return Math.max(min, Math.min(max, value));\n  }\n  #decodeL(L) {\n    if (L < 0) {\n      return -this.#decodeL(-L);\n    }\n    if (L > 8.0) {\n      return ((L + 16) / 116) ** 3;\n    }\n    return L * CalRGBCS.#DECODE_L_CONSTANT;\n  }\n  #compensateBlackPoint(sourceBlackPoint, XYZ_Flat, result) {\n    if (sourceBlackPoint[0] === 0 && sourceBlackPoint[1] === 0 && sourceBlackPoint[2] === 0) {\n      result[0] = XYZ_Flat[0];\n      result[1] = XYZ_Flat[1];\n      result[2] = XYZ_Flat[2];\n      return;\n    }\n    const zeroDecodeL = this.#decodeL(0);\n    const X_DST = zeroDecodeL;\n    const X_SRC = this.#decodeL(sourceBlackPoint[0]);\n    const Y_DST = zeroDecodeL;\n    const Y_SRC = this.#decodeL(sourceBlackPoint[1]);\n    const Z_DST = zeroDecodeL;\n    const Z_SRC = this.#decodeL(sourceBlackPoint[2]);\n    const X_Scale = (1 - X_DST) / (1 - X_SRC);\n    const X_Offset = 1 - X_Scale;\n    const Y_Scale = (1 - Y_DST) / (1 - Y_SRC);\n    const Y_Offset = 1 - Y_Scale;\n    const Z_Scale = (1 - Z_DST) / (1 - Z_SRC);\n    const Z_Offset = 1 - Z_Scale;\n    result[0] = XYZ_Flat[0] * X_Scale + X_Offset;\n    result[1] = XYZ_Flat[1] * Y_Scale + Y_Offset;\n    result[2] = XYZ_Flat[2] * Z_Scale + Z_Offset;\n  }\n  #normalizeWhitePointToFlat(sourceWhitePoint, XYZ_In, result) {\n    if (sourceWhitePoint[0] === 1 && sourceWhitePoint[2] === 1) {\n      result[0] = XYZ_In[0];\n      result[1] = XYZ_In[1];\n      result[2] = XYZ_In[2];\n      return;\n    }\n    const LMS = result;\n    this.#matrixProduct(CalRGBCS.#BRADFORD_SCALE_MATRIX, XYZ_In, LMS);\n    const LMS_Flat = CalRGBCS.#tempNormalizeMatrix;\n    this.#toFlat(sourceWhitePoint, LMS, LMS_Flat);\n    this.#matrixProduct(CalRGBCS.#BRADFORD_SCALE_INVERSE_MATRIX, LMS_Flat, result);\n  }\n  #normalizeWhitePointToD65(sourceWhitePoint, XYZ_In, result) {\n    const LMS = result;\n    this.#matrixProduct(CalRGBCS.#BRADFORD_SCALE_MATRIX, XYZ_In, LMS);\n    const LMS_D65 = CalRGBCS.#tempNormalizeMatrix;\n    this.#toD65(sourceWhitePoint, LMS, LMS_D65);\n    this.#matrixProduct(CalRGBCS.#BRADFORD_SCALE_INVERSE_MATRIX, LMS_D65, result);\n  }\n  #toRgb(src, srcOffset, dest, destOffset, scale) {\n    const A = this.#adjustToRange(0, 1, src[srcOffset] * scale);\n    const B = this.#adjustToRange(0, 1, src[srcOffset + 1] * scale);\n    const C = this.#adjustToRange(0, 1, src[srcOffset + 2] * scale);\n    const AGR = A === 1 ? 1 : A ** this.GR;\n    const BGG = B === 1 ? 1 : B ** this.GG;\n    const CGB = C === 1 ? 1 : C ** this.GB;\n    const X = this.MXA * AGR + this.MXB * BGG + this.MXC * CGB;\n    const Y = this.MYA * AGR + this.MYB * BGG + this.MYC * CGB;\n    const Z = this.MZA * AGR + this.MZB * BGG + this.MZC * CGB;\n    const XYZ = CalRGBCS.#tempConvertMatrix1;\n    XYZ[0] = X;\n    XYZ[1] = Y;\n    XYZ[2] = Z;\n    const XYZ_Flat = CalRGBCS.#tempConvertMatrix2;\n    this.#normalizeWhitePointToFlat(this.whitePoint, XYZ, XYZ_Flat);\n    const XYZ_Black = CalRGBCS.#tempConvertMatrix1;\n    this.#compensateBlackPoint(this.blackPoint, XYZ_Flat, XYZ_Black);\n    const XYZ_D65 = CalRGBCS.#tempConvertMatrix2;\n    this.#normalizeWhitePointToD65(CalRGBCS.#FLAT_WHITEPOINT_MATRIX, XYZ_Black, XYZ_D65);\n    const SRGB = CalRGBCS.#tempConvertMatrix1;\n    this.#matrixProduct(CalRGBCS.#SRGB_D65_XYZ_TO_RGB_MATRIX, XYZ_D65, SRGB);\n    dest[destOffset] = this.#sRGBTransferFunction(SRGB[0]) * 255;\n    dest[destOffset + 1] = this.#sRGBTransferFunction(SRGB[1]) * 255;\n    dest[destOffset + 2] = this.#sRGBTransferFunction(SRGB[2]) * 255;\n  }\n  getRgbItem(src, srcOffset, dest, destOffset) {\n    this.#toRgb(src, srcOffset, dest, destOffset, 1);\n  }\n  getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {\n    const scale = 1 / ((1 << bits) - 1);\n    for (let i = 0; i < count; ++i) {\n      this.#toRgb(src, srcOffset, dest, destOffset, scale);\n      srcOffset += 3;\n      destOffset += 3 + alpha01;\n    }\n  }\n  getOutputLength(inputLength, alpha01) {\n    return inputLength * (3 + alpha01) / 3 | 0;\n  }\n}\nclass LabCS extends ColorSpace {\n  constructor(whitePoint, blackPoint, range) {\n    super(\"Lab\", 3);\n    if (!whitePoint) {\n      throw new FormatError(\"WhitePoint missing - required for color space Lab\");\n    }\n    [this.XW, this.YW, this.ZW] = whitePoint;\n    [this.amin, this.amax, this.bmin, this.bmax] = range || [-100, 100, -100, 100];\n    [this.XB, this.YB, this.ZB] = blackPoint || [0, 0, 0];\n    if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) {\n      throw new FormatError(\"Invalid WhitePoint components, no fallback available\");\n    }\n    if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {\n      info(\"Invalid BlackPoint, falling back to default\");\n      this.XB = this.YB = this.ZB = 0;\n    }\n    if (this.amin > this.amax || this.bmin > this.bmax) {\n      info(\"Invalid Range, falling back to defaults\");\n      this.amin = -100;\n      this.amax = 100;\n      this.bmin = -100;\n      this.bmax = 100;\n    }\n  }\n  #fn_g(x) {\n    return x >= 6 / 29 ? x ** 3 : 108 / 841 * (x - 4 / 29);\n  }\n  #decode(value, high1, low2, high2) {\n    return low2 + value * (high2 - low2) / high1;\n  }\n  #toRgb(src, srcOffset, maxVal, dest, destOffset) {\n    let Ls = src[srcOffset];\n    let as = src[srcOffset + 1];\n    let bs = src[srcOffset + 2];\n    if (maxVal !== false) {\n      Ls = this.#decode(Ls, maxVal, 0, 100);\n      as = this.#decode(as, maxVal, this.amin, this.amax);\n      bs = this.#decode(bs, maxVal, this.bmin, this.bmax);\n    }\n    if (as > this.amax) {\n      as = this.amax;\n    } else if (as < this.amin) {\n      as = this.amin;\n    }\n    if (bs > this.bmax) {\n      bs = this.bmax;\n    } else if (bs < this.bmin) {\n      bs = this.bmin;\n    }\n    const M = (Ls + 16) / 116;\n    const L = M + as / 500;\n    const N = M - bs / 200;\n    const X = this.XW * this.#fn_g(L);\n    const Y = this.YW * this.#fn_g(M);\n    const Z = this.ZW * this.#fn_g(N);\n    let r, g, b;\n    if (this.ZW < 1) {\n      r = X * 3.1339 + Y * -1.617 + Z * -0.4906;\n      g = X * -0.9785 + Y * 1.916 + Z * 0.0333;\n      b = X * 0.072 + Y * -0.229 + Z * 1.4057;\n    } else {\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.204 + Z * 1.057;\n    }\n    dest[destOffset] = Math.sqrt(r) * 255;\n    dest[destOffset + 1] = Math.sqrt(g) * 255;\n    dest[destOffset + 2] = Math.sqrt(b) * 255;\n  }\n  getRgbItem(src, srcOffset, dest, destOffset) {\n    this.#toRgb(src, srcOffset, false, dest, destOffset);\n  }\n  getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) {\n    const maxVal = (1 << bits) - 1;\n    for (let i = 0; i < count; i++) {\n      this.#toRgb(src, srcOffset, maxVal, dest, destOffset);\n      srcOffset += 3;\n      destOffset += 3 + alpha01;\n    }\n  }\n  getOutputLength(inputLength, alpha01) {\n    return inputLength * (3 + alpha01) / 3 | 0;\n  }\n  isDefaultDecode(decodeMap, bpc) {\n    return true;\n  }\n  get usesZeroToOneRange() {\n    return shadow(this, \"usesZeroToOneRange\", false);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/binary_cmap.js\n\nfunction hexToInt(a, size) {\n  let n = 0;\n  for (let i = 0; i <= size; i++) {\n    n = n << 8 | a[i];\n  }\n  return n >>> 0;\n}\nfunction hexToStr(a, size) {\n  if (size === 1) {\n    return String.fromCharCode(a[0], a[1]);\n  }\n  if (size === 3) {\n    return String.fromCharCode(a[0], a[1], a[2], a[3]);\n  }\n  return String.fromCharCode(...a.subarray(0, size + 1));\n}\nfunction addHex(a, b, size) {\n  let c = 0;\n  for (let i = size; i >= 0; i--) {\n    c += a[i] + b[i];\n    a[i] = c & 255;\n    c >>= 8;\n  }\n}\nfunction incHex(a, size) {\n  let c = 1;\n  for (let i = size; i >= 0 && c > 0; i--) {\n    c += a[i];\n    a[i] = c & 255;\n    c >>= 8;\n  }\n}\nconst MAX_NUM_SIZE = 16;\nconst MAX_ENCODED_NUM_SIZE = 19;\nclass BinaryCMapStream {\n  constructor(data) {\n    this.buffer = data;\n    this.pos = 0;\n    this.end = data.length;\n    this.tmpBuf = new Uint8Array(MAX_ENCODED_NUM_SIZE);\n  }\n  readByte() {\n    if (this.pos >= this.end) {\n      return -1;\n    }\n    return this.buffer[this.pos++];\n  }\n  readNumber() {\n    let n = 0;\n    let last;\n    do {\n      const b = this.readByte();\n      if (b < 0) {\n        throw new FormatError(\"unexpected EOF in bcmap\");\n      }\n      last = !(b & 0x80);\n      n = n << 7 | b & 0x7f;\n    } while (!last);\n    return n;\n  }\n  readSigned() {\n    const n = this.readNumber();\n    return n & 1 ? ~(n >>> 1) : n >>> 1;\n  }\n  readHex(num, size) {\n    num.set(this.buffer.subarray(this.pos, this.pos + size + 1));\n    this.pos += size + 1;\n  }\n  readHexNumber(num, size) {\n    let last;\n    const stack = this.tmpBuf;\n    let sp = 0;\n    do {\n      const b = this.readByte();\n      if (b < 0) {\n        throw new FormatError(\"unexpected EOF in bcmap\");\n      }\n      last = !(b & 0x80);\n      stack[sp++] = b & 0x7f;\n    } while (!last);\n    let i = size,\n      buffer = 0,\n      bufferSize = 0;\n    while (i >= 0) {\n      while (bufferSize < 8 && stack.length > 0) {\n        buffer |= stack[--sp] << bufferSize;\n        bufferSize += 7;\n      }\n      num[i] = buffer & 255;\n      i--;\n      buffer >>= 8;\n      bufferSize -= 8;\n    }\n  }\n  readHexSigned(num, size) {\n    this.readHexNumber(num, size);\n    const sign = num[size] & 1 ? 255 : 0;\n    let c = 0;\n    for (let i = 0; i <= size; i++) {\n      c = (c & 1) << 8 | num[i];\n      num[i] = c >> 1 ^ sign;\n    }\n  }\n  readString() {\n    const len = this.readNumber(),\n      buf = new Array(len);\n    for (let i = 0; i < len; i++) {\n      buf[i] = this.readNumber();\n    }\n    return String.fromCharCode(...buf);\n  }\n}\nclass BinaryCMapReader {\n  async process(data, cMap, extend) {\n    const stream = new BinaryCMapStream(data);\n    const header = stream.readByte();\n    cMap.vertical = !!(header & 1);\n    let useCMap = null;\n    const start = new Uint8Array(MAX_NUM_SIZE);\n    const end = new Uint8Array(MAX_NUM_SIZE);\n    const char = new Uint8Array(MAX_NUM_SIZE);\n    const charCode = new Uint8Array(MAX_NUM_SIZE);\n    const tmp = new Uint8Array(MAX_NUM_SIZE);\n    let code;\n    let b;\n    while ((b = stream.readByte()) >= 0) {\n      const type = b >> 5;\n      if (type === 7) {\n        switch (b & 0x1f) {\n          case 0:\n            stream.readString();\n            break;\n          case 1:\n            useCMap = stream.readString();\n            break;\n        }\n        continue;\n      }\n      const sequence = !!(b & 0x10);\n      const dataSize = b & 15;\n      if (dataSize + 1 > MAX_NUM_SIZE) {\n        throw new Error(\"BinaryCMapReader.process: Invalid dataSize.\");\n      }\n      const ucs2DataSize = 1;\n      const subitemsCount = stream.readNumber();\n      switch (type) {\n        case 0:\n          stream.readHex(start, dataSize);\n          stream.readHexNumber(end, dataSize);\n          addHex(end, start, dataSize);\n          cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize), hexToInt(end, dataSize));\n          for (let i = 1; i < subitemsCount; i++) {\n            incHex(end, dataSize);\n            stream.readHexNumber(start, dataSize);\n            addHex(start, end, dataSize);\n            stream.readHexNumber(end, dataSize);\n            addHex(end, start, dataSize);\n            cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize), hexToInt(end, dataSize));\n          }\n          break;\n        case 1:\n          stream.readHex(start, dataSize);\n          stream.readHexNumber(end, dataSize);\n          addHex(end, start, dataSize);\n          stream.readNumber();\n          for (let i = 1; i < subitemsCount; i++) {\n            incHex(end, dataSize);\n            stream.readHexNumber(start, dataSize);\n            addHex(start, end, dataSize);\n            stream.readHexNumber(end, dataSize);\n            addHex(end, start, dataSize);\n            stream.readNumber();\n          }\n          break;\n        case 2:\n          stream.readHex(char, dataSize);\n          code = stream.readNumber();\n          cMap.mapOne(hexToInt(char, dataSize), code);\n          for (let i = 1; i < subitemsCount; i++) {\n            incHex(char, dataSize);\n            if (!sequence) {\n              stream.readHexNumber(tmp, dataSize);\n              addHex(char, tmp, dataSize);\n            }\n            code = stream.readSigned() + (code + 1);\n            cMap.mapOne(hexToInt(char, dataSize), code);\n          }\n          break;\n        case 3:\n          stream.readHex(start, dataSize);\n          stream.readHexNumber(end, dataSize);\n          addHex(end, start, dataSize);\n          code = stream.readNumber();\n          cMap.mapCidRange(hexToInt(start, dataSize), hexToInt(end, dataSize), code);\n          for (let i = 1; i < subitemsCount; i++) {\n            incHex(end, dataSize);\n            if (!sequence) {\n              stream.readHexNumber(start, dataSize);\n              addHex(start, end, dataSize);\n            } else {\n              start.set(end);\n            }\n            stream.readHexNumber(end, dataSize);\n            addHex(end, start, dataSize);\n            code = stream.readNumber();\n            cMap.mapCidRange(hexToInt(start, dataSize), hexToInt(end, dataSize), code);\n          }\n          break;\n        case 4:\n          stream.readHex(char, ucs2DataSize);\n          stream.readHex(charCode, dataSize);\n          cMap.mapOne(hexToInt(char, ucs2DataSize), hexToStr(charCode, dataSize));\n          for (let i = 1; i < subitemsCount; i++) {\n            incHex(char, ucs2DataSize);\n            if (!sequence) {\n              stream.readHexNumber(tmp, ucs2DataSize);\n              addHex(char, tmp, ucs2DataSize);\n            }\n            incHex(charCode, dataSize);\n            stream.readHexSigned(tmp, dataSize);\n            addHex(charCode, tmp, dataSize);\n            cMap.mapOne(hexToInt(char, ucs2DataSize), hexToStr(charCode, dataSize));\n          }\n          break;\n        case 5:\n          stream.readHex(start, ucs2DataSize);\n          stream.readHexNumber(end, ucs2DataSize);\n          addHex(end, start, ucs2DataSize);\n          stream.readHex(charCode, dataSize);\n          cMap.mapBfRange(hexToInt(start, ucs2DataSize), hexToInt(end, ucs2DataSize), hexToStr(charCode, dataSize));\n          for (let i = 1; i < subitemsCount; i++) {\n            incHex(end, ucs2DataSize);\n            if (!sequence) {\n              stream.readHexNumber(start, ucs2DataSize);\n              addHex(start, end, ucs2DataSize);\n            } else {\n              start.set(end);\n            }\n            stream.readHexNumber(end, ucs2DataSize);\n            addHex(end, start, ucs2DataSize);\n            stream.readHex(charCode, dataSize);\n            cMap.mapBfRange(hexToInt(start, ucs2DataSize), hexToInt(end, ucs2DataSize), hexToStr(charCode, dataSize));\n          }\n          break;\n        default:\n          throw new Error(`BinaryCMapReader.process - unknown type: ${type}`);\n      }\n    }\n    if (useCMap) {\n      return extend(useCMap);\n    }\n    return cMap;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/decode_stream.js\n\n\nconst emptyBuffer = new Uint8Array(0);\nclass DecodeStream extends BaseStream {\n  constructor(maybeMinBufferLength) {\n    super();\n    this._rawMinBufferLength = maybeMinBufferLength || 0;\n    this.pos = 0;\n    this.bufferLength = 0;\n    this.eof = false;\n    this.buffer = emptyBuffer;\n    this.minBufferLength = 512;\n    if (maybeMinBufferLength) {\n      while (this.minBufferLength < maybeMinBufferLength) {\n        this.minBufferLength *= 2;\n      }\n    }\n  }\n  get isEmpty() {\n    while (!this.eof && this.bufferLength === 0) {\n      this.readBlock();\n    }\n    return this.bufferLength === 0;\n  }\n  ensureBuffer(requested) {\n    const buffer = this.buffer;\n    if (requested <= buffer.byteLength) {\n      return buffer;\n    }\n    let size = this.minBufferLength;\n    while (size < requested) {\n      size *= 2;\n    }\n    const buffer2 = new Uint8Array(size);\n    buffer2.set(buffer);\n    return this.buffer = buffer2;\n  }\n  getByte() {\n    const pos = this.pos;\n    while (this.bufferLength <= pos) {\n      if (this.eof) {\n        return -1;\n      }\n      this.readBlock();\n    }\n    return this.buffer[this.pos++];\n  }\n  getBytes(length) {\n    const pos = this.pos;\n    let end;\n    if (length) {\n      this.ensureBuffer(pos + length);\n      end = pos + length;\n      while (!this.eof && this.bufferLength < end) {\n        this.readBlock();\n      }\n      const bufEnd = this.bufferLength;\n      if (end > bufEnd) {\n        end = bufEnd;\n      }\n    } else {\n      while (!this.eof) {\n        this.readBlock();\n      }\n      end = this.bufferLength;\n    }\n    this.pos = end;\n    return this.buffer.subarray(pos, end);\n  }\n  reset() {\n    this.pos = 0;\n  }\n  makeSubStream(start, length, dict = null) {\n    if (length === undefined) {\n      while (!this.eof) {\n        this.readBlock();\n      }\n    } else {\n      const end = start + length;\n      while (this.bufferLength <= end && !this.eof) {\n        this.readBlock();\n      }\n    }\n    return new Stream(this.buffer, start, length, dict);\n  }\n  getBaseStreams() {\n    return this.str ? this.str.getBaseStreams() : null;\n  }\n}\nclass StreamsSequenceStream extends DecodeStream {\n  constructor(streams, onError = null) {\n    let maybeLength = 0;\n    for (const stream of streams) {\n      maybeLength += stream instanceof DecodeStream ? stream._rawMinBufferLength : stream.length;\n    }\n    super(maybeLength);\n    this.streams = streams;\n    this._onError = onError;\n  }\n  readBlock() {\n    const streams = this.streams;\n    if (streams.length === 0) {\n      this.eof = true;\n      return;\n    }\n    const stream = streams.shift();\n    let chunk;\n    try {\n      chunk = stream.getBytes();\n    } catch (reason) {\n      if (this._onError) {\n        this._onError(reason, stream.dict?.objId);\n        return;\n      }\n      throw reason;\n    }\n    const bufferLength = this.bufferLength;\n    const newLength = bufferLength + chunk.length;\n    const buffer = this.ensureBuffer(newLength);\n    buffer.set(chunk, bufferLength);\n    this.bufferLength = newLength;\n  }\n  getBaseStreams() {\n    const baseStreamsBuf = [];\n    for (const stream of this.streams) {\n      const baseStreams = stream.getBaseStreams();\n      if (baseStreams) {\n        baseStreamsBuf.push(...baseStreams);\n      }\n    }\n    return baseStreamsBuf.length > 0 ? baseStreamsBuf : null;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/ascii_85_stream.js\n\n\nclass Ascii85Stream extends DecodeStream {\n  constructor(str, maybeLength) {\n    if (maybeLength) {\n      maybeLength *= 0.8;\n    }\n    super(maybeLength);\n    this.str = str;\n    this.dict = str.dict;\n    this.input = new Uint8Array(5);\n  }\n  readBlock() {\n    const TILDA_CHAR = 0x7e;\n    const Z_LOWER_CHAR = 0x7a;\n    const EOF = -1;\n    const str = this.str;\n    let c = str.getByte();\n    while (isWhiteSpace(c)) {\n      c = str.getByte();\n    }\n    if (c === EOF || c === TILDA_CHAR) {\n      this.eof = true;\n      return;\n    }\n    const bufferLength = this.bufferLength;\n    let buffer, i;\n    if (c === Z_LOWER_CHAR) {\n      buffer = this.ensureBuffer(bufferLength + 4);\n      for (i = 0; i < 4; ++i) {\n        buffer[bufferLength + i] = 0;\n      }\n      this.bufferLength += 4;\n    } else {\n      const input = this.input;\n      input[0] = c;\n      for (i = 1; i < 5; ++i) {\n        c = str.getByte();\n        while (isWhiteSpace(c)) {\n          c = str.getByte();\n        }\n        input[i] = c;\n        if (c === EOF || c === TILDA_CHAR) {\n          break;\n        }\n      }\n      buffer = this.ensureBuffer(bufferLength + i - 1);\n      this.bufferLength += i - 1;\n      if (i < 5) {\n        for (; i < 5; ++i) {\n          input[i] = 0x21 + 84;\n        }\n        this.eof = true;\n      }\n      let t = 0;\n      for (i = 0; i < 5; ++i) {\n        t = t * 85 + (input[i] - 0x21);\n      }\n      for (i = 3; i >= 0; --i) {\n        buffer[bufferLength + i] = t & 0xff;\n        t >>= 8;\n      }\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/ascii_hex_stream.js\n\nclass AsciiHexStream extends DecodeStream {\n  constructor(str, maybeLength) {\n    if (maybeLength) {\n      maybeLength *= 0.5;\n    }\n    super(maybeLength);\n    this.str = str;\n    this.dict = str.dict;\n    this.firstDigit = -1;\n  }\n  readBlock() {\n    const UPSTREAM_BLOCK_SIZE = 8000;\n    const bytes = this.str.getBytes(UPSTREAM_BLOCK_SIZE);\n    if (!bytes.length) {\n      this.eof = true;\n      return;\n    }\n    const maxDecodeLength = bytes.length + 1 >> 1;\n    const buffer = this.ensureBuffer(this.bufferLength + maxDecodeLength);\n    let bufferLength = this.bufferLength;\n    let firstDigit = this.firstDigit;\n    for (const ch of bytes) {\n      let digit;\n      if (ch >= 0x30 && ch <= 0x39) {\n        digit = ch & 0x0f;\n      } else if (ch >= 0x41 && ch <= 0x46 || ch >= 0x61 && ch <= 0x66) {\n        digit = (ch & 0x0f) + 9;\n      } else if (ch === 0x3e) {\n        this.eof = true;\n        break;\n      } else {\n        continue;\n      }\n      if (firstDigit < 0) {\n        firstDigit = digit;\n      } else {\n        buffer[bufferLength++] = firstDigit << 4 | digit;\n        firstDigit = -1;\n      }\n    }\n    if (firstDigit >= 0 && this.eof) {\n      buffer[bufferLength++] = firstDigit << 4;\n      firstDigit = -1;\n    }\n    this.firstDigit = firstDigit;\n    this.bufferLength = bufferLength;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/ccitt.js\n\nconst ccittEOL = -2;\nconst ccittEOF = -1;\nconst twoDimPass = 0;\nconst twoDimHoriz = 1;\nconst twoDimVert0 = 2;\nconst twoDimVertR1 = 3;\nconst twoDimVertL1 = 4;\nconst twoDimVertR2 = 5;\nconst twoDimVertL2 = 6;\nconst twoDimVertR3 = 7;\nconst twoDimVertL3 = 8;\nconst twoDimTable = [[-1, -1], [-1, -1], [7, twoDimVertL3], [7, twoDimVertR3], [6, twoDimVertL2], [6, twoDimVertL2], [6, twoDimVertR2], [6, twoDimVertR2], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0]];\nconst whiteTable1 = [[-1, -1], [12, ccittEOL], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [11, 1792], [11, 1792], [12, 1984], [12, 2048], [12, 2112], [12, 2176], [12, 2240], [12, 2304], [11, 1856], [11, 1856], [11, 1920], [11, 1920], [12, 2368], [12, 2432], [12, 2496], [12, 2560]];\nconst whiteTable2 = [[-1, -1], [-1, -1], [-1, -1], [-1, -1], [8, 29], [8, 29], [8, 30], [8, 30], [8, 45], [8, 45], [8, 46], [8, 46], [7, 22], [7, 22], [7, 22], [7, 22], [7, 23], [7, 23], [7, 23], [7, 23], [8, 47], [8, 47], [8, 48], [8, 48], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [7, 20], [7, 20], [7, 20], [7, 20], [8, 33], [8, 33], [8, 34], [8, 34], [8, 35], [8, 35], [8, 36], [8, 36], [8, 37], [8, 37], [8, 38], [8, 38], [7, 19], [7, 19], [7, 19], [7, 19], [8, 31], [8, 31], [8, 32], [8, 32], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [8, 53], [8, 53], [8, 54], [8, 54], [7, 26], [7, 26], [7, 26], [7, 26], [8, 39], [8, 39], [8, 40], [8, 40], [8, 41], [8, 41], [8, 42], [8, 42], [8, 43], [8, 43], [8, 44], [8, 44], [7, 21], [7, 21], [7, 21], [7, 21], [7, 28], [7, 28], [7, 28], [7, 28], [8, 61], [8, 61], [8, 62], [8, 62], [8, 63], [8, 63], [8, 0], [8, 0], [8, 320], [8, 320], [8, 384], [8, 384], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [7, 27], [7, 27], [7, 27], [7, 27], [8, 59], [8, 59], [8, 60], [8, 60], [9, 1472], [9, 1536], [9, 1600], [9, 1728], [7, 18], [7, 18], [7, 18], [7, 18], [7, 24], [7, 24], [7, 24], [7, 24], [8, 49], [8, 49], [8, 50], [8, 50], [8, 51], [8, 51], [8, 52], [8, 52], [7, 25], [7, 25], [7, 25], [7, 25], [8, 55], [8, 55], [8, 56], [8, 56], [8, 57], [8, 57], [8, 58], [8, 58], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [8, 448], [8, 448], [8, 512], [8, 512], [9, 704], [9, 768], [8, 640], [8, 640], [8, 576], [8, 576], [9, 832], [9, 896], [9, 960], [9, 1024], [9, 1088], [9, 1152], [9, 1216], [9, 1280], [9, 1344], [9, 1408], [7, 256], [7, 256], [7, 256], [7, 256], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7]];\nconst blackTable1 = [[-1, -1], [-1, -1], [12, ccittEOL], [12, ccittEOL], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [11, 1792], [11, 1792], [11, 1792], [11, 1792], [12, 1984], [12, 1984], [12, 2048], [12, 2048], [12, 2112], [12, 2112], [12, 2176], [12, 2176], [12, 2240], [12, 2240], [12, 2304], [12, 2304], [11, 1856], [11, 1856], [11, 1856], [11, 1856], [11, 1920], [11, 1920], [11, 1920], [11, 1920], [12, 2368], [12, 2368], [12, 2432], [12, 2432], [12, 2496], [12, 2496], [12, 2560], [12, 2560], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [12, 52], [12, 52], [13, 640], [13, 704], [13, 768], [13, 832], [12, 55], [12, 55], [12, 56], [12, 56], [13, 1280], [13, 1344], [13, 1408], [13, 1472], [12, 59], [12, 59], [12, 60], [12, 60], [13, 1536], [13, 1600], [11, 24], [11, 24], [11, 24], [11, 24], [11, 25], [11, 25], [11, 25], [11, 25], [13, 1664], [13, 1728], [12, 320], [12, 320], [12, 384], [12, 384], [12, 448], [12, 448], [13, 512], [13, 576], [12, 53], [12, 53], [12, 54], [12, 54], [13, 896], [13, 960], [13, 1024], [13, 1088], [13, 1152], [13, 1216], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64]];\nconst blackTable2 = [[8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [11, 23], [11, 23], [12, 50], [12, 51], [12, 44], [12, 45], [12, 46], [12, 47], [12, 57], [12, 58], [12, 61], [12, 256], [10, 16], [10, 16], [10, 16], [10, 16], [10, 17], [10, 17], [10, 17], [10, 17], [12, 48], [12, 49], [12, 62], [12, 63], [12, 30], [12, 31], [12, 32], [12, 33], [12, 40], [12, 41], [11, 22], [11, 22], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [12, 128], [12, 192], [12, 26], [12, 27], [12, 28], [12, 29], [11, 19], [11, 19], [11, 20], [11, 20], [12, 34], [12, 35], [12, 36], [12, 37], [12, 38], [12, 39], [11, 21], [11, 21], [12, 42], [12, 43], [10, 0], [10, 0], [10, 0], [10, 0], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12]];\nconst blackTable3 = [[-1, -1], [-1, -1], [-1, -1], [-1, -1], [6, 9], [6, 8], [5, 7], [5, 7], [4, 6], [4, 6], [4, 6], [4, 6], [4, 5], [4, 5], [4, 5], [4, 5], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2]];\nclass CCITTFaxDecoder {\n  constructor(source, options = {}) {\n    if (!source || typeof source.next !== \"function\") {\n      throw new Error('CCITTFaxDecoder - invalid \"source\" parameter.');\n    }\n    this.source = source;\n    this.eof = false;\n    this.encoding = options.K || 0;\n    this.eoline = options.EndOfLine || false;\n    this.byteAlign = options.EncodedByteAlign || false;\n    this.columns = options.Columns || 1728;\n    this.rows = options.Rows || 0;\n    this.eoblock = options.EndOfBlock ?? true;\n    this.black = options.BlackIs1 || false;\n    this.codingLine = new Uint32Array(this.columns + 1);\n    this.refLine = new Uint32Array(this.columns + 2);\n    this.codingLine[0] = this.columns;\n    this.codingPos = 0;\n    this.row = 0;\n    this.nextLine2D = this.encoding < 0;\n    this.inputBits = 0;\n    this.inputBuf = 0;\n    this.outputBits = 0;\n    this.rowsDone = false;\n    let code1;\n    while ((code1 = this._lookBits(12)) === 0) {\n      this._eatBits(1);\n    }\n    if (code1 === 1) {\n      this._eatBits(12);\n    }\n    if (this.encoding > 0) {\n      this.nextLine2D = !this._lookBits(1);\n      this._eatBits(1);\n    }\n  }\n  readNextChar() {\n    if (this.eof) {\n      return -1;\n    }\n    const refLine = this.refLine;\n    const codingLine = this.codingLine;\n    const columns = this.columns;\n    let refPos, blackPixels, bits, i;\n    if (this.outputBits === 0) {\n      if (this.rowsDone) {\n        this.eof = true;\n      }\n      if (this.eof) {\n        return -1;\n      }\n      this.err = false;\n      let code1, code2, code3;\n      if (this.nextLine2D) {\n        for (i = 0; codingLine[i] < columns; ++i) {\n          refLine[i] = codingLine[i];\n        }\n        refLine[i++] = columns;\n        refLine[i] = columns;\n        codingLine[0] = 0;\n        this.codingPos = 0;\n        refPos = 0;\n        blackPixels = 0;\n        while (codingLine[this.codingPos] < columns) {\n          code1 = this._getTwoDimCode();\n          switch (code1) {\n            case twoDimPass:\n              this._addPixels(refLine[refPos + 1], blackPixels);\n              if (refLine[refPos + 1] < columns) {\n                refPos += 2;\n              }\n              break;\n            case twoDimHoriz:\n              code1 = code2 = 0;\n              if (blackPixels) {\n                do {\n                  code1 += code3 = this._getBlackCode();\n                } while (code3 >= 64);\n                do {\n                  code2 += code3 = this._getWhiteCode();\n                } while (code3 >= 64);\n              } else {\n                do {\n                  code1 += code3 = this._getWhiteCode();\n                } while (code3 >= 64);\n                do {\n                  code2 += code3 = this._getBlackCode();\n                } while (code3 >= 64);\n              }\n              this._addPixels(codingLine[this.codingPos] + code1, blackPixels);\n              if (codingLine[this.codingPos] < columns) {\n                this._addPixels(codingLine[this.codingPos] + code2, blackPixels ^ 1);\n              }\n              while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {\n                refPos += 2;\n              }\n              break;\n            case twoDimVertR3:\n              this._addPixels(refLine[refPos] + 3, blackPixels);\n              blackPixels ^= 1;\n              if (codingLine[this.codingPos] < columns) {\n                ++refPos;\n                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {\n                  refPos += 2;\n                }\n              }\n              break;\n            case twoDimVertR2:\n              this._addPixels(refLine[refPos] + 2, blackPixels);\n              blackPixels ^= 1;\n              if (codingLine[this.codingPos] < columns) {\n                ++refPos;\n                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {\n                  refPos += 2;\n                }\n              }\n              break;\n            case twoDimVertR1:\n              this._addPixels(refLine[refPos] + 1, blackPixels);\n              blackPixels ^= 1;\n              if (codingLine[this.codingPos] < columns) {\n                ++refPos;\n                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {\n                  refPos += 2;\n                }\n              }\n              break;\n            case twoDimVert0:\n              this._addPixels(refLine[refPos], blackPixels);\n              blackPixels ^= 1;\n              if (codingLine[this.codingPos] < columns) {\n                ++refPos;\n                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {\n                  refPos += 2;\n                }\n              }\n              break;\n            case twoDimVertL3:\n              this._addPixelsNeg(refLine[refPos] - 3, blackPixels);\n              blackPixels ^= 1;\n              if (codingLine[this.codingPos] < columns) {\n                if (refPos > 0) {\n                  --refPos;\n                } else {\n                  ++refPos;\n                }\n                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {\n                  refPos += 2;\n                }\n              }\n              break;\n            case twoDimVertL2:\n              this._addPixelsNeg(refLine[refPos] - 2, blackPixels);\n              blackPixels ^= 1;\n              if (codingLine[this.codingPos] < columns) {\n                if (refPos > 0) {\n                  --refPos;\n                } else {\n                  ++refPos;\n                }\n                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {\n                  refPos += 2;\n                }\n              }\n              break;\n            case twoDimVertL1:\n              this._addPixelsNeg(refLine[refPos] - 1, blackPixels);\n              blackPixels ^= 1;\n              if (codingLine[this.codingPos] < columns) {\n                if (refPos > 0) {\n                  --refPos;\n                } else {\n                  ++refPos;\n                }\n                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {\n                  refPos += 2;\n                }\n              }\n              break;\n            case ccittEOF:\n              this._addPixels(columns, 0);\n              this.eof = true;\n              break;\n            default:\n              info(\"bad 2d code\");\n              this._addPixels(columns, 0);\n              this.err = true;\n          }\n        }\n      } else {\n        codingLine[0] = 0;\n        this.codingPos = 0;\n        blackPixels = 0;\n        while (codingLine[this.codingPos] < columns) {\n          code1 = 0;\n          if (blackPixels) {\n            do {\n              code1 += code3 = this._getBlackCode();\n            } while (code3 >= 64);\n          } else {\n            do {\n              code1 += code3 = this._getWhiteCode();\n            } while (code3 >= 64);\n          }\n          this._addPixels(codingLine[this.codingPos] + code1, blackPixels);\n          blackPixels ^= 1;\n        }\n      }\n      let gotEOL = false;\n      if (this.byteAlign) {\n        this.inputBits &= ~7;\n      }\n      if (!this.eoblock && this.row === this.rows - 1) {\n        this.rowsDone = true;\n      } else {\n        code1 = this._lookBits(12);\n        if (this.eoline) {\n          while (code1 !== ccittEOF && code1 !== 1) {\n            this._eatBits(1);\n            code1 = this._lookBits(12);\n          }\n        } else {\n          while (code1 === 0) {\n            this._eatBits(1);\n            code1 = this._lookBits(12);\n          }\n        }\n        if (code1 === 1) {\n          this._eatBits(12);\n          gotEOL = true;\n        } else if (code1 === ccittEOF) {\n          this.eof = true;\n        }\n      }\n      if (!this.eof && this.encoding > 0 && !this.rowsDone) {\n        this.nextLine2D = !this._lookBits(1);\n        this._eatBits(1);\n      }\n      if (this.eoblock && gotEOL && this.byteAlign) {\n        code1 = this._lookBits(12);\n        if (code1 === 1) {\n          this._eatBits(12);\n          if (this.encoding > 0) {\n            this._lookBits(1);\n            this._eatBits(1);\n          }\n          if (this.encoding >= 0) {\n            for (i = 0; i < 4; ++i) {\n              code1 = this._lookBits(12);\n              if (code1 !== 1) {\n                info(\"bad rtc code: \" + code1);\n              }\n              this._eatBits(12);\n              if (this.encoding > 0) {\n                this._lookBits(1);\n                this._eatBits(1);\n              }\n            }\n          }\n          this.eof = true;\n        }\n      } else if (this.err && this.eoline) {\n        while (true) {\n          code1 = this._lookBits(13);\n          if (code1 === ccittEOF) {\n            this.eof = true;\n            return -1;\n          }\n          if (code1 >> 1 === 1) {\n            break;\n          }\n          this._eatBits(1);\n        }\n        this._eatBits(12);\n        if (this.encoding > 0) {\n          this._eatBits(1);\n          this.nextLine2D = !(code1 & 1);\n        }\n      }\n      this.outputBits = codingLine[0] > 0 ? codingLine[this.codingPos = 0] : codingLine[this.codingPos = 1];\n      this.row++;\n    }\n    let c;\n    if (this.outputBits >= 8) {\n      c = this.codingPos & 1 ? 0 : 0xff;\n      this.outputBits -= 8;\n      if (this.outputBits === 0 && codingLine[this.codingPos] < columns) {\n        this.codingPos++;\n        this.outputBits = codingLine[this.codingPos] - codingLine[this.codingPos - 1];\n      }\n    } else {\n      bits = 8;\n      c = 0;\n      do {\n        if (typeof this.outputBits !== \"number\") {\n          throw new FormatError('Invalid /CCITTFaxDecode data, \"outputBits\" must be a number.');\n        }\n        if (this.outputBits > bits) {\n          c <<= bits;\n          if (!(this.codingPos & 1)) {\n            c |= 0xff >> 8 - bits;\n          }\n          this.outputBits -= bits;\n          bits = 0;\n        } else {\n          c <<= this.outputBits;\n          if (!(this.codingPos & 1)) {\n            c |= 0xff >> 8 - this.outputBits;\n          }\n          bits -= this.outputBits;\n          this.outputBits = 0;\n          if (codingLine[this.codingPos] < columns) {\n            this.codingPos++;\n            this.outputBits = codingLine[this.codingPos] - codingLine[this.codingPos - 1];\n          } else if (bits > 0) {\n            c <<= bits;\n            bits = 0;\n          }\n        }\n      } while (bits);\n    }\n    if (this.black) {\n      c ^= 0xff;\n    }\n    return c;\n  }\n  _addPixels(a1, blackPixels) {\n    const codingLine = this.codingLine;\n    let codingPos = this.codingPos;\n    if (a1 > codingLine[codingPos]) {\n      if (a1 > this.columns) {\n        info(\"row is wrong length\");\n        this.err = true;\n        a1 = this.columns;\n      }\n      if (codingPos & 1 ^ blackPixels) {\n        ++codingPos;\n      }\n      codingLine[codingPos] = a1;\n    }\n    this.codingPos = codingPos;\n  }\n  _addPixelsNeg(a1, blackPixels) {\n    const codingLine = this.codingLine;\n    let codingPos = this.codingPos;\n    if (a1 > codingLine[codingPos]) {\n      if (a1 > this.columns) {\n        info(\"row is wrong length\");\n        this.err = true;\n        a1 = this.columns;\n      }\n      if (codingPos & 1 ^ blackPixels) {\n        ++codingPos;\n      }\n      codingLine[codingPos] = a1;\n    } else if (a1 < codingLine[codingPos]) {\n      if (a1 < 0) {\n        info(\"invalid code\");\n        this.err = true;\n        a1 = 0;\n      }\n      while (codingPos > 0 && a1 < codingLine[codingPos - 1]) {\n        --codingPos;\n      }\n      codingLine[codingPos] = a1;\n    }\n    this.codingPos = codingPos;\n  }\n  _findTableCode(start, end, table, limit) {\n    const limitValue = limit || 0;\n    for (let i = start; i <= end; ++i) {\n      let code = this._lookBits(i);\n      if (code === ccittEOF) {\n        return [true, 1, false];\n      }\n      if (i < end) {\n        code <<= end - i;\n      }\n      if (!limitValue || code >= limitValue) {\n        const p = table[code - limitValue];\n        if (p[0] === i) {\n          this._eatBits(i);\n          return [true, p[1], true];\n        }\n      }\n    }\n    return [false, 0, false];\n  }\n  _getTwoDimCode() {\n    let code = 0;\n    let p;\n    if (this.eoblock) {\n      code = this._lookBits(7);\n      p = twoDimTable[code];\n      if (p?.[0] > 0) {\n        this._eatBits(p[0]);\n        return p[1];\n      }\n    } else {\n      const result = this._findTableCode(1, 7, twoDimTable);\n      if (result[0] && result[2]) {\n        return result[1];\n      }\n    }\n    info(\"Bad two dim code\");\n    return ccittEOF;\n  }\n  _getWhiteCode() {\n    let code = 0;\n    let p;\n    if (this.eoblock) {\n      code = this._lookBits(12);\n      if (code === ccittEOF) {\n        return 1;\n      }\n      p = code >> 5 === 0 ? whiteTable1[code] : whiteTable2[code >> 3];\n      if (p[0] > 0) {\n        this._eatBits(p[0]);\n        return p[1];\n      }\n    } else {\n      let result = this._findTableCode(1, 9, whiteTable2);\n      if (result[0]) {\n        return result[1];\n      }\n      result = this._findTableCode(11, 12, whiteTable1);\n      if (result[0]) {\n        return result[1];\n      }\n    }\n    info(\"bad white code\");\n    this._eatBits(1);\n    return 1;\n  }\n  _getBlackCode() {\n    let code, p;\n    if (this.eoblock) {\n      code = this._lookBits(13);\n      if (code === ccittEOF) {\n        return 1;\n      }\n      if (code >> 7 === 0) {\n        p = blackTable1[code];\n      } else if (code >> 9 === 0 && code >> 7 !== 0) {\n        p = blackTable2[(code >> 1) - 64];\n      } else {\n        p = blackTable3[code >> 7];\n      }\n      if (p[0] > 0) {\n        this._eatBits(p[0]);\n        return p[1];\n      }\n    } else {\n      let result = this._findTableCode(2, 6, blackTable3);\n      if (result[0]) {\n        return result[1];\n      }\n      result = this._findTableCode(7, 12, blackTable2, 64);\n      if (result[0]) {\n        return result[1];\n      }\n      result = this._findTableCode(10, 13, blackTable1);\n      if (result[0]) {\n        return result[1];\n      }\n    }\n    info(\"bad black code\");\n    this._eatBits(1);\n    return 1;\n  }\n  _lookBits(n) {\n    let c;\n    while (this.inputBits < n) {\n      if ((c = this.source.next()) === -1) {\n        if (this.inputBits === 0) {\n          return ccittEOF;\n        }\n        return this.inputBuf << n - this.inputBits & 0xffff >> 16 - n;\n      }\n      this.inputBuf = this.inputBuf << 8 | c;\n      this.inputBits += 8;\n    }\n    return this.inputBuf >> this.inputBits - n & 0xffff >> 16 - n;\n  }\n  _eatBits(n) {\n    if ((this.inputBits -= n) < 0) {\n      this.inputBits = 0;\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/ccitt_stream.js\n\n\n\nclass CCITTFaxStream extends DecodeStream {\n  constructor(str, maybeLength, params) {\n    super(maybeLength);\n    this.str = str;\n    this.dict = str.dict;\n    if (!(params instanceof Dict)) {\n      params = Dict.empty;\n    }\n    const source = {\n      next() {\n        return str.getByte();\n      }\n    };\n    this.ccittFaxDecoder = new CCITTFaxDecoder(source, {\n      K: params.get(\"K\"),\n      EndOfLine: params.get(\"EndOfLine\"),\n      EncodedByteAlign: params.get(\"EncodedByteAlign\"),\n      Columns: params.get(\"Columns\"),\n      Rows: params.get(\"Rows\"),\n      EndOfBlock: params.get(\"EndOfBlock\"),\n      BlackIs1: params.get(\"BlackIs1\")\n    });\n  }\n  readBlock() {\n    while (!this.eof) {\n      const c = this.ccittFaxDecoder.readNextChar();\n      if (c === -1) {\n        this.eof = true;\n        return;\n      }\n      this.ensureBuffer(this.bufferLength + 1);\n      this.buffer[this.bufferLength++] = c;\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/flate_stream.js\n\n\nconst codeLenCodeMap = new Int32Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);\nconst lengthDecode = new Int32Array([0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009, 0x0000a, 0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017, 0x2001b, 0x2001f, 0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043, 0x40053, 0x40063, 0x40073, 0x50083, 0x500a3, 0x500c3, 0x500e3, 0x00102, 0x00102, 0x00102]);\nconst distDecode = new Int32Array([0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009, 0x2000d, 0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061, 0x60081, 0x600c1, 0x70101, 0x70181, 0x80201, 0x80301, 0x90401, 0x90601, 0xa0801, 0xa0c01, 0xb1001, 0xb1801, 0xc2001, 0xc3001, 0xd4001, 0xd6001]);\nconst fixedLitCodeTab = [new Int32Array([0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c0, 0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080, 0x80040, 0x900e0, 0x70104, 0x80058, 0x80018, 0x90090, 0x70114, 0x80078, 0x80038, 0x900d0, 0x7010c, 0x80068, 0x80028, 0x900b0, 0x80008, 0x80088, 0x80048, 0x900f0, 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c8, 0x7010a, 0x80064, 0x80024, 0x900a8, 0x80004, 0x80084, 0x80044, 0x900e8, 0x70106, 0x8005c, 0x8001c, 0x90098, 0x70116, 0x8007c, 0x8003c, 0x900d8, 0x7010e, 0x8006c, 0x8002c, 0x900b8, 0x8000c, 0x8008c, 0x8004c, 0x900f8, 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c4, 0x70109, 0x80062, 0x80022, 0x900a4, 0x80002, 0x80082, 0x80042, 0x900e4, 0x70105, 0x8005a, 0x8001a, 0x90094, 0x70115, 0x8007a, 0x8003a, 0x900d4, 0x7010d, 0x8006a, 0x8002a, 0x900b4, 0x8000a, 0x8008a, 0x8004a, 0x900f4, 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cc, 0x7010b, 0x80066, 0x80026, 0x900ac, 0x80006, 0x80086, 0x80046, 0x900ec, 0x70107, 0x8005e, 0x8001e, 0x9009c, 0x70117, 0x8007e, 0x8003e, 0x900dc, 0x7010f, 0x8006e, 0x8002e, 0x900bc, 0x8000e, 0x8008e, 0x8004e, 0x900fc, 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c2, 0x70108, 0x80061, 0x80021, 0x900a2, 0x80001, 0x80081, 0x80041, 0x900e2, 0x70104, 0x80059, 0x80019, 0x90092, 0x70114, 0x80079, 0x80039, 0x900d2, 0x7010c, 0x80069, 0x80029, 0x900b2, 0x80009, 0x80089, 0x80049, 0x900f2, 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900ca, 0x7010a, 0x80065, 0x80025, 0x900aa, 0x80005, 0x80085, 0x80045, 0x900ea, 0x70106, 0x8005d, 0x8001d, 0x9009a, 0x70116, 0x8007d, 0x8003d, 0x900da, 0x7010e, 0x8006d, 0x8002d, 0x900ba, 0x8000d, 0x8008d, 0x8004d, 0x900fa, 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c6, 0x70109, 0x80063, 0x80023, 0x900a6, 0x80003, 0x80083, 0x80043, 0x900e6, 0x70105, 0x8005b, 0x8001b, 0x90096, 0x70115, 0x8007b, 0x8003b, 0x900d6, 0x7010d, 0x8006b, 0x8002b, 0x900b6, 0x8000b, 0x8008b, 0x8004b, 0x900f6, 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900ce, 0x7010b, 0x80067, 0x80027, 0x900ae, 0x80007, 0x80087, 0x80047, 0x900ee, 0x70107, 0x8005f, 0x8001f, 0x9009e, 0x70117, 0x8007f, 0x8003f, 0x900de, 0x7010f, 0x8006f, 0x8002f, 0x900be, 0x8000f, 0x8008f, 0x8004f, 0x900fe, 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c1, 0x70108, 0x80060, 0x80020, 0x900a1, 0x80000, 0x80080, 0x80040, 0x900e1, 0x70104, 0x80058, 0x80018, 0x90091, 0x70114, 0x80078, 0x80038, 0x900d1, 0x7010c, 0x80068, 0x80028, 0x900b1, 0x80008, 0x80088, 0x80048, 0x900f1, 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c9, 0x7010a, 0x80064, 0x80024, 0x900a9, 0x80004, 0x80084, 0x80044, 0x900e9, 0x70106, 0x8005c, 0x8001c, 0x90099, 0x70116, 0x8007c, 0x8003c, 0x900d9, 0x7010e, 0x8006c, 0x8002c, 0x900b9, 0x8000c, 0x8008c, 0x8004c, 0x900f9, 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c5, 0x70109, 0x80062, 0x80022, 0x900a5, 0x80002, 0x80082, 0x80042, 0x900e5, 0x70105, 0x8005a, 0x8001a, 0x90095, 0x70115, 0x8007a, 0x8003a, 0x900d5, 0x7010d, 0x8006a, 0x8002a, 0x900b5, 0x8000a, 0x8008a, 0x8004a, 0x900f5, 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cd, 0x7010b, 0x80066, 0x80026, 0x900ad, 0x80006, 0x80086, 0x80046, 0x900ed, 0x70107, 0x8005e, 0x8001e, 0x9009d, 0x70117, 0x8007e, 0x8003e, 0x900dd, 0x7010f, 0x8006e, 0x8002e, 0x900bd, 0x8000e, 0x8008e, 0x8004e, 0x900fd, 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c3, 0x70108, 0x80061, 0x80021, 0x900a3, 0x80001, 0x80081, 0x80041, 0x900e3, 0x70104, 0x80059, 0x80019, 0x90093, 0x70114, 0x80079, 0x80039, 0x900d3, 0x7010c, 0x80069, 0x80029, 0x900b3, 0x80009, 0x80089, 0x80049, 0x900f3, 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900cb, 0x7010a, 0x80065, 0x80025, 0x900ab, 0x80005, 0x80085, 0x80045, 0x900eb, 0x70106, 0x8005d, 0x8001d, 0x9009b, 0x70116, 0x8007d, 0x8003d, 0x900db, 0x7010e, 0x8006d, 0x8002d, 0x900bb, 0x8000d, 0x8008d, 0x8004d, 0x900fb, 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c7, 0x70109, 0x80063, 0x80023, 0x900a7, 0x80003, 0x80083, 0x80043, 0x900e7, 0x70105, 0x8005b, 0x8001b, 0x90097, 0x70115, 0x8007b, 0x8003b, 0x900d7, 0x7010d, 0x8006b, 0x8002b, 0x900b7, 0x8000b, 0x8008b, 0x8004b, 0x900f7, 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900cf, 0x7010b, 0x80067, 0x80027, 0x900af, 0x80007, 0x80087, 0x80047, 0x900ef, 0x70107, 0x8005f, 0x8001f, 0x9009f, 0x70117, 0x8007f, 0x8003f, 0x900df, 0x7010f, 0x8006f, 0x8002f, 0x900bf, 0x8000f, 0x8008f, 0x8004f, 0x900ff]), 9];\nconst fixedDistCodeTab = [new Int32Array([0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c, 0x5001c, 0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016, 0x5000e, 0x00000, 0x50001, 0x50011, 0x50009, 0x50019, 0x50005, 0x50015, 0x5000d, 0x5001d, 0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000]), 5];\nclass FlateStream extends DecodeStream {\n  constructor(str, maybeLength) {\n    super(maybeLength);\n    this.str = str;\n    this.dict = str.dict;\n    const cmf = str.getByte();\n    const flg = str.getByte();\n    if (cmf === -1 || flg === -1) {\n      throw new FormatError(`Invalid header in flate stream: ${cmf}, ${flg}`);\n    }\n    if ((cmf & 0x0f) !== 0x08) {\n      throw new FormatError(`Unknown compression method in flate stream: ${cmf}, ${flg}`);\n    }\n    if (((cmf << 8) + flg) % 31 !== 0) {\n      throw new FormatError(`Bad FCHECK in flate stream: ${cmf}, ${flg}`);\n    }\n    if (flg & 0x20) {\n      throw new FormatError(`FDICT bit set in flate stream: ${cmf}, ${flg}`);\n    }\n    this.codeSize = 0;\n    this.codeBuf = 0;\n  }\n  getBits(bits) {\n    const str = this.str;\n    let codeSize = this.codeSize;\n    let codeBuf = this.codeBuf;\n    let b;\n    while (codeSize < bits) {\n      if ((b = str.getByte()) === -1) {\n        throw new FormatError(\"Bad encoding in flate stream\");\n      }\n      codeBuf |= b << codeSize;\n      codeSize += 8;\n    }\n    b = codeBuf & (1 << bits) - 1;\n    this.codeBuf = codeBuf >> bits;\n    this.codeSize = codeSize -= bits;\n    return b;\n  }\n  getCode(table) {\n    const str = this.str;\n    const codes = table[0];\n    const maxLen = table[1];\n    let codeSize = this.codeSize;\n    let codeBuf = this.codeBuf;\n    let b;\n    while (codeSize < maxLen) {\n      if ((b = str.getByte()) === -1) {\n        break;\n      }\n      codeBuf |= b << codeSize;\n      codeSize += 8;\n    }\n    const code = codes[codeBuf & (1 << maxLen) - 1];\n    const codeLen = code >> 16;\n    const codeVal = code & 0xffff;\n    if (codeLen < 1 || codeSize < codeLen) {\n      throw new FormatError(\"Bad encoding in flate stream\");\n    }\n    this.codeBuf = codeBuf >> codeLen;\n    this.codeSize = codeSize - codeLen;\n    return codeVal;\n  }\n  generateHuffmanTable(lengths) {\n    const n = lengths.length;\n    let maxLen = 0;\n    let i;\n    for (i = 0; i < n; ++i) {\n      if (lengths[i] > maxLen) {\n        maxLen = lengths[i];\n      }\n    }\n    const size = 1 << maxLen;\n    const codes = new Int32Array(size);\n    for (let len = 1, code = 0, skip = 2; len <= maxLen; ++len, code <<= 1, skip <<= 1) {\n      for (let val = 0; val < n; ++val) {\n        if (lengths[val] === len) {\n          let code2 = 0;\n          let t = code;\n          for (i = 0; i < len; ++i) {\n            code2 = code2 << 1 | t & 1;\n            t >>= 1;\n          }\n          for (i = code2; i < size; i += skip) {\n            codes[i] = len << 16 | val;\n          }\n          ++code;\n        }\n      }\n    }\n    return [codes, maxLen];\n  }\n  readBlock() {\n    let buffer, len;\n    const str = this.str;\n    let hdr = this.getBits(3);\n    if (hdr & 1) {\n      this.eof = true;\n    }\n    hdr >>= 1;\n    if (hdr === 0) {\n      let b;\n      if ((b = str.getByte()) === -1) {\n        throw new FormatError(\"Bad block header in flate stream\");\n      }\n      let blockLen = b;\n      if ((b = str.getByte()) === -1) {\n        throw new FormatError(\"Bad block header in flate stream\");\n      }\n      blockLen |= b << 8;\n      if ((b = str.getByte()) === -1) {\n        throw new FormatError(\"Bad block header in flate stream\");\n      }\n      let check = b;\n      if ((b = str.getByte()) === -1) {\n        throw new FormatError(\"Bad block header in flate stream\");\n      }\n      check |= b << 8;\n      if (check !== (~blockLen & 0xffff) && (blockLen !== 0 || check !== 0)) {\n        throw new FormatError(\"Bad uncompressed block length in flate stream\");\n      }\n      this.codeBuf = 0;\n      this.codeSize = 0;\n      const bufferLength = this.bufferLength,\n        end = bufferLength + blockLen;\n      buffer = this.ensureBuffer(end);\n      this.bufferLength = end;\n      if (blockLen === 0) {\n        if (str.peekByte() === -1) {\n          this.eof = true;\n        }\n      } else {\n        const block = str.getBytes(blockLen);\n        buffer.set(block, bufferLength);\n        if (block.length < blockLen) {\n          this.eof = true;\n        }\n      }\n      return;\n    }\n    let litCodeTable;\n    let distCodeTable;\n    if (hdr === 1) {\n      litCodeTable = fixedLitCodeTab;\n      distCodeTable = fixedDistCodeTab;\n    } else if (hdr === 2) {\n      const numLitCodes = this.getBits(5) + 257;\n      const numDistCodes = this.getBits(5) + 1;\n      const numCodeLenCodes = this.getBits(4) + 4;\n      const codeLenCodeLengths = new Uint8Array(codeLenCodeMap.length);\n      let i;\n      for (i = 0; i < numCodeLenCodes; ++i) {\n        codeLenCodeLengths[codeLenCodeMap[i]] = this.getBits(3);\n      }\n      const codeLenCodeTab = this.generateHuffmanTable(codeLenCodeLengths);\n      len = 0;\n      i = 0;\n      const codes = numLitCodes + numDistCodes;\n      const codeLengths = new Uint8Array(codes);\n      let bitsLength, bitsOffset, what;\n      while (i < codes) {\n        const code = this.getCode(codeLenCodeTab);\n        if (code === 16) {\n          bitsLength = 2;\n          bitsOffset = 3;\n          what = len;\n        } else if (code === 17) {\n          bitsLength = 3;\n          bitsOffset = 3;\n          what = len = 0;\n        } else if (code === 18) {\n          bitsLength = 7;\n          bitsOffset = 11;\n          what = len = 0;\n        } else {\n          codeLengths[i++] = len = code;\n          continue;\n        }\n        let repeatLength = this.getBits(bitsLength) + bitsOffset;\n        while (repeatLength-- > 0) {\n          codeLengths[i++] = what;\n        }\n      }\n      litCodeTable = this.generateHuffmanTable(codeLengths.subarray(0, numLitCodes));\n      distCodeTable = this.generateHuffmanTable(codeLengths.subarray(numLitCodes, codes));\n    } else {\n      throw new FormatError(\"Unknown block type in flate stream\");\n    }\n    buffer = this.buffer;\n    let limit = buffer ? buffer.length : 0;\n    let pos = this.bufferLength;\n    while (true) {\n      let code1 = this.getCode(litCodeTable);\n      if (code1 < 256) {\n        if (pos + 1 >= limit) {\n          buffer = this.ensureBuffer(pos + 1);\n          limit = buffer.length;\n        }\n        buffer[pos++] = code1;\n        continue;\n      }\n      if (code1 === 256) {\n        this.bufferLength = pos;\n        return;\n      }\n      code1 -= 257;\n      code1 = lengthDecode[code1];\n      let code2 = code1 >> 16;\n      if (code2 > 0) {\n        code2 = this.getBits(code2);\n      }\n      len = (code1 & 0xffff) + code2;\n      code1 = this.getCode(distCodeTable);\n      code1 = distDecode[code1];\n      code2 = code1 >> 16;\n      if (code2 > 0) {\n        code2 = this.getBits(code2);\n      }\n      const dist = (code1 & 0xffff) + code2;\n      if (pos + len >= limit) {\n        buffer = this.ensureBuffer(pos + len);\n        limit = buffer.length;\n      }\n      for (let k = 0; k < len; ++k, ++pos) {\n        buffer[pos] = buffer[pos - dist];\n      }\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/arithmetic_decoder.js\nconst QeTable = [{\n  qe: 0x5601,\n  nmps: 1,\n  nlps: 1,\n  switchFlag: 1\n}, {\n  qe: 0x3401,\n  nmps: 2,\n  nlps: 6,\n  switchFlag: 0\n}, {\n  qe: 0x1801,\n  nmps: 3,\n  nlps: 9,\n  switchFlag: 0\n}, {\n  qe: 0x0ac1,\n  nmps: 4,\n  nlps: 12,\n  switchFlag: 0\n}, {\n  qe: 0x0521,\n  nmps: 5,\n  nlps: 29,\n  switchFlag: 0\n}, {\n  qe: 0x0221,\n  nmps: 38,\n  nlps: 33,\n  switchFlag: 0\n}, {\n  qe: 0x5601,\n  nmps: 7,\n  nlps: 6,\n  switchFlag: 1\n}, {\n  qe: 0x5401,\n  nmps: 8,\n  nlps: 14,\n  switchFlag: 0\n}, {\n  qe: 0x4801,\n  nmps: 9,\n  nlps: 14,\n  switchFlag: 0\n}, {\n  qe: 0x3801,\n  nmps: 10,\n  nlps: 14,\n  switchFlag: 0\n}, {\n  qe: 0x3001,\n  nmps: 11,\n  nlps: 17,\n  switchFlag: 0\n}, {\n  qe: 0x2401,\n  nmps: 12,\n  nlps: 18,\n  switchFlag: 0\n}, {\n  qe: 0x1c01,\n  nmps: 13,\n  nlps: 20,\n  switchFlag: 0\n}, {\n  qe: 0x1601,\n  nmps: 29,\n  nlps: 21,\n  switchFlag: 0\n}, {\n  qe: 0x5601,\n  nmps: 15,\n  nlps: 14,\n  switchFlag: 1\n}, {\n  qe: 0x5401,\n  nmps: 16,\n  nlps: 14,\n  switchFlag: 0\n}, {\n  qe: 0x5101,\n  nmps: 17,\n  nlps: 15,\n  switchFlag: 0\n}, {\n  qe: 0x4801,\n  nmps: 18,\n  nlps: 16,\n  switchFlag: 0\n}, {\n  qe: 0x3801,\n  nmps: 19,\n  nlps: 17,\n  switchFlag: 0\n}, {\n  qe: 0x3401,\n  nmps: 20,\n  nlps: 18,\n  switchFlag: 0\n}, {\n  qe: 0x3001,\n  nmps: 21,\n  nlps: 19,\n  switchFlag: 0\n}, {\n  qe: 0x2801,\n  nmps: 22,\n  nlps: 19,\n  switchFlag: 0\n}, {\n  qe: 0x2401,\n  nmps: 23,\n  nlps: 20,\n  switchFlag: 0\n}, {\n  qe: 0x2201,\n  nmps: 24,\n  nlps: 21,\n  switchFlag: 0\n}, {\n  qe: 0x1c01,\n  nmps: 25,\n  nlps: 22,\n  switchFlag: 0\n}, {\n  qe: 0x1801,\n  nmps: 26,\n  nlps: 23,\n  switchFlag: 0\n}, {\n  qe: 0x1601,\n  nmps: 27,\n  nlps: 24,\n  switchFlag: 0\n}, {\n  qe: 0x1401,\n  nmps: 28,\n  nlps: 25,\n  switchFlag: 0\n}, {\n  qe: 0x1201,\n  nmps: 29,\n  nlps: 26,\n  switchFlag: 0\n}, {\n  qe: 0x1101,\n  nmps: 30,\n  nlps: 27,\n  switchFlag: 0\n}, {\n  qe: 0x0ac1,\n  nmps: 31,\n  nlps: 28,\n  switchFlag: 0\n}, {\n  qe: 0x09c1,\n  nmps: 32,\n  nlps: 29,\n  switchFlag: 0\n}, {\n  qe: 0x08a1,\n  nmps: 33,\n  nlps: 30,\n  switchFlag: 0\n}, {\n  qe: 0x0521,\n  nmps: 34,\n  nlps: 31,\n  switchFlag: 0\n}, {\n  qe: 0x0441,\n  nmps: 35,\n  nlps: 32,\n  switchFlag: 0\n}, {\n  qe: 0x02a1,\n  nmps: 36,\n  nlps: 33,\n  switchFlag: 0\n}, {\n  qe: 0x0221,\n  nmps: 37,\n  nlps: 34,\n  switchFlag: 0\n}, {\n  qe: 0x0141,\n  nmps: 38,\n  nlps: 35,\n  switchFlag: 0\n}, {\n  qe: 0x0111,\n  nmps: 39,\n  nlps: 36,\n  switchFlag: 0\n}, {\n  qe: 0x0085,\n  nmps: 40,\n  nlps: 37,\n  switchFlag: 0\n}, {\n  qe: 0x0049,\n  nmps: 41,\n  nlps: 38,\n  switchFlag: 0\n}, {\n  qe: 0x0025,\n  nmps: 42,\n  nlps: 39,\n  switchFlag: 0\n}, {\n  qe: 0x0015,\n  nmps: 43,\n  nlps: 40,\n  switchFlag: 0\n}, {\n  qe: 0x0009,\n  nmps: 44,\n  nlps: 41,\n  switchFlag: 0\n}, {\n  qe: 0x0005,\n  nmps: 45,\n  nlps: 42,\n  switchFlag: 0\n}, {\n  qe: 0x0001,\n  nmps: 45,\n  nlps: 43,\n  switchFlag: 0\n}, {\n  qe: 0x5601,\n  nmps: 46,\n  nlps: 46,\n  switchFlag: 0\n}];\nclass ArithmeticDecoder {\n  constructor(data, start, end) {\n    this.data = data;\n    this.bp = start;\n    this.dataEnd = end;\n    this.chigh = data[start];\n    this.clow = 0;\n    this.byteIn();\n    this.chigh = this.chigh << 7 & 0xffff | this.clow >> 9 & 0x7f;\n    this.clow = this.clow << 7 & 0xffff;\n    this.ct -= 7;\n    this.a = 0x8000;\n  }\n  byteIn() {\n    const data = this.data;\n    let bp = this.bp;\n    if (data[bp] === 0xff) {\n      if (data[bp + 1] > 0x8f) {\n        this.clow += 0xff00;\n        this.ct = 8;\n      } else {\n        bp++;\n        this.clow += data[bp] << 9;\n        this.ct = 7;\n        this.bp = bp;\n      }\n    } else {\n      bp++;\n      this.clow += bp < this.dataEnd ? data[bp] << 8 : 0xff00;\n      this.ct = 8;\n      this.bp = bp;\n    }\n    if (this.clow > 0xffff) {\n      this.chigh += this.clow >> 16;\n      this.clow &= 0xffff;\n    }\n  }\n  readBit(contexts, pos) {\n    let cx_index = contexts[pos] >> 1,\n      cx_mps = contexts[pos] & 1;\n    const qeTableIcx = QeTable[cx_index];\n    const qeIcx = qeTableIcx.qe;\n    let d;\n    let a = this.a - qeIcx;\n    if (this.chigh < qeIcx) {\n      if (a < qeIcx) {\n        a = qeIcx;\n        d = cx_mps;\n        cx_index = qeTableIcx.nmps;\n      } else {\n        a = qeIcx;\n        d = 1 ^ cx_mps;\n        if (qeTableIcx.switchFlag === 1) {\n          cx_mps = d;\n        }\n        cx_index = qeTableIcx.nlps;\n      }\n    } else {\n      this.chigh -= qeIcx;\n      if ((a & 0x8000) !== 0) {\n        this.a = a;\n        return cx_mps;\n      }\n      if (a < qeIcx) {\n        d = 1 ^ cx_mps;\n        if (qeTableIcx.switchFlag === 1) {\n          cx_mps = d;\n        }\n        cx_index = qeTableIcx.nlps;\n      } else {\n        d = cx_mps;\n        cx_index = qeTableIcx.nmps;\n      }\n    }\n    do {\n      if (this.ct === 0) {\n        this.byteIn();\n      }\n      a <<= 1;\n      this.chigh = this.chigh << 1 & 0xffff | this.clow >> 15 & 1;\n      this.clow = this.clow << 1 & 0xffff;\n      this.ct--;\n    } while ((a & 0x8000) === 0);\n    this.a = a;\n    contexts[pos] = cx_index << 1 | cx_mps;\n    return d;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/jbig2.js\n\n\n\n\nclass Jbig2Error extends BaseException {\n  constructor(msg) {\n    super(`JBIG2 error: ${msg}`, \"Jbig2Error\");\n  }\n}\nclass ContextCache {\n  getContexts(id) {\n    if (id in this) {\n      return this[id];\n    }\n    return this[id] = new Int8Array(1 << 16);\n  }\n}\nclass DecodingContext {\n  constructor(data, start, end) {\n    this.data = data;\n    this.start = start;\n    this.end = end;\n  }\n  get decoder() {\n    const decoder = new ArithmeticDecoder(this.data, this.start, this.end);\n    return shadow(this, \"decoder\", decoder);\n  }\n  get contextCache() {\n    const cache = new ContextCache();\n    return shadow(this, \"contextCache\", cache);\n  }\n}\nconst MAX_INT_32 = 2 ** 31 - 1;\nconst MIN_INT_32 = -(2 ** 31);\nfunction decodeInteger(contextCache, procedure, decoder) {\n  const contexts = contextCache.getContexts(procedure);\n  let prev = 1;\n  function readBits(length) {\n    let v = 0;\n    for (let i = 0; i < length; i++) {\n      const bit = decoder.readBit(contexts, prev);\n      prev = prev < 256 ? prev << 1 | bit : (prev << 1 | bit) & 511 | 256;\n      v = v << 1 | bit;\n    }\n    return v >>> 0;\n  }\n  const sign = readBits(1);\n  const value = readBits(1) ? readBits(1) ? readBits(1) ? readBits(1) ? readBits(1) ? readBits(32) + 4436 : readBits(12) + 340 : readBits(8) + 84 : readBits(6) + 20 : readBits(4) + 4 : readBits(2);\n  let signedValue;\n  if (sign === 0) {\n    signedValue = value;\n  } else if (value > 0) {\n    signedValue = -value;\n  }\n  if (signedValue >= MIN_INT_32 && signedValue <= MAX_INT_32) {\n    return signedValue;\n  }\n  return null;\n}\nfunction decodeIAID(contextCache, decoder, codeLength) {\n  const contexts = contextCache.getContexts(\"IAID\");\n  let prev = 1;\n  for (let i = 0; i < codeLength; i++) {\n    const bit = decoder.readBit(contexts, prev);\n    prev = prev << 1 | bit;\n  }\n  if (codeLength < 31) {\n    return prev & (1 << codeLength) - 1;\n  }\n  return prev & 0x7fffffff;\n}\nconst SegmentTypes = [\"SymbolDictionary\", null, null, null, \"IntermediateTextRegion\", null, \"ImmediateTextRegion\", \"ImmediateLosslessTextRegion\", null, null, null, null, null, null, null, null, \"PatternDictionary\", null, null, null, \"IntermediateHalftoneRegion\", null, \"ImmediateHalftoneRegion\", \"ImmediateLosslessHalftoneRegion\", null, null, null, null, null, null, null, null, null, null, null, null, \"IntermediateGenericRegion\", null, \"ImmediateGenericRegion\", \"ImmediateLosslessGenericRegion\", \"IntermediateGenericRefinementRegion\", null, \"ImmediateGenericRefinementRegion\", \"ImmediateLosslessGenericRefinementRegion\", null, null, null, null, \"PageInformation\", \"EndOfPage\", \"EndOfStripe\", \"EndOfFile\", \"Profiles\", \"Tables\", null, null, null, null, null, null, null, null, \"Extension\"];\nconst CodingTemplates = [[{\n  x: -1,\n  y: -2\n}, {\n  x: 0,\n  y: -2\n}, {\n  x: 1,\n  y: -2\n}, {\n  x: -2,\n  y: -1\n}, {\n  x: -1,\n  y: -1\n}, {\n  x: 0,\n  y: -1\n}, {\n  x: 1,\n  y: -1\n}, {\n  x: 2,\n  y: -1\n}, {\n  x: -4,\n  y: 0\n}, {\n  x: -3,\n  y: 0\n}, {\n  x: -2,\n  y: 0\n}, {\n  x: -1,\n  y: 0\n}], [{\n  x: -1,\n  y: -2\n}, {\n  x: 0,\n  y: -2\n}, {\n  x: 1,\n  y: -2\n}, {\n  x: 2,\n  y: -2\n}, {\n  x: -2,\n  y: -1\n}, {\n  x: -1,\n  y: -1\n}, {\n  x: 0,\n  y: -1\n}, {\n  x: 1,\n  y: -1\n}, {\n  x: 2,\n  y: -1\n}, {\n  x: -3,\n  y: 0\n}, {\n  x: -2,\n  y: 0\n}, {\n  x: -1,\n  y: 0\n}], [{\n  x: -1,\n  y: -2\n}, {\n  x: 0,\n  y: -2\n}, {\n  x: 1,\n  y: -2\n}, {\n  x: -2,\n  y: -1\n}, {\n  x: -1,\n  y: -1\n}, {\n  x: 0,\n  y: -1\n}, {\n  x: 1,\n  y: -1\n}, {\n  x: -2,\n  y: 0\n}, {\n  x: -1,\n  y: 0\n}], [{\n  x: -3,\n  y: -1\n}, {\n  x: -2,\n  y: -1\n}, {\n  x: -1,\n  y: -1\n}, {\n  x: 0,\n  y: -1\n}, {\n  x: 1,\n  y: -1\n}, {\n  x: -4,\n  y: 0\n}, {\n  x: -3,\n  y: 0\n}, {\n  x: -2,\n  y: 0\n}, {\n  x: -1,\n  y: 0\n}]];\nconst RefinementTemplates = [{\n  coding: [{\n    x: 0,\n    y: -1\n  }, {\n    x: 1,\n    y: -1\n  }, {\n    x: -1,\n    y: 0\n  }],\n  reference: [{\n    x: 0,\n    y: -1\n  }, {\n    x: 1,\n    y: -1\n  }, {\n    x: -1,\n    y: 0\n  }, {\n    x: 0,\n    y: 0\n  }, {\n    x: 1,\n    y: 0\n  }, {\n    x: -1,\n    y: 1\n  }, {\n    x: 0,\n    y: 1\n  }, {\n    x: 1,\n    y: 1\n  }]\n}, {\n  coding: [{\n    x: -1,\n    y: -1\n  }, {\n    x: 0,\n    y: -1\n  }, {\n    x: 1,\n    y: -1\n  }, {\n    x: -1,\n    y: 0\n  }],\n  reference: [{\n    x: 0,\n    y: -1\n  }, {\n    x: -1,\n    y: 0\n  }, {\n    x: 0,\n    y: 0\n  }, {\n    x: 1,\n    y: 0\n  }, {\n    x: 0,\n    y: 1\n  }, {\n    x: 1,\n    y: 1\n  }]\n}];\nconst ReusedContexts = [0x9b25, 0x0795, 0x00e5, 0x0195];\nconst RefinementReusedContexts = [0x0020, 0x0008];\nfunction decodeBitmapTemplate0(width, height, decodingContext) {\n  const decoder = decodingContext.decoder;\n  const contexts = decodingContext.contextCache.getContexts(\"GB\");\n  const bitmap = [];\n  let contextLabel, i, j, pixel, row, row1, row2;\n  const OLD_PIXEL_MASK = 0x7bf7;\n  for (i = 0; i < height; i++) {\n    row = bitmap[i] = new Uint8Array(width);\n    row1 = i < 1 ? row : bitmap[i - 1];\n    row2 = i < 2 ? row : bitmap[i - 2];\n    contextLabel = row2[0] << 13 | row2[1] << 12 | row2[2] << 11 | row1[0] << 7 | row1[1] << 6 | row1[2] << 5 | row1[3] << 4;\n    for (j = 0; j < width; j++) {\n      row[j] = pixel = decoder.readBit(contexts, contextLabel);\n      contextLabel = (contextLabel & OLD_PIXEL_MASK) << 1 | (j + 3 < width ? row2[j + 3] << 11 : 0) | (j + 4 < width ? row1[j + 4] << 4 : 0) | pixel;\n    }\n  }\n  return bitmap;\n}\nfunction decodeBitmap(mmr, width, height, templateIndex, prediction, skip, at, decodingContext) {\n  if (mmr) {\n    const input = new Reader(decodingContext.data, decodingContext.start, decodingContext.end);\n    return decodeMMRBitmap(input, width, height, false);\n  }\n  if (templateIndex === 0 && !skip && !prediction && at.length === 4 && at[0].x === 3 && at[0].y === -1 && at[1].x === -3 && at[1].y === -1 && at[2].x === 2 && at[2].y === -2 && at[3].x === -2 && at[3].y === -2) {\n    return decodeBitmapTemplate0(width, height, decodingContext);\n  }\n  const useskip = !!skip;\n  const template = CodingTemplates[templateIndex].concat(at);\n  template.sort(function (a, b) {\n    return a.y - b.y || a.x - b.x;\n  });\n  const templateLength = template.length;\n  const templateX = new Int8Array(templateLength);\n  const templateY = new Int8Array(templateLength);\n  const changingTemplateEntries = [];\n  let reuseMask = 0,\n    minX = 0,\n    maxX = 0,\n    minY = 0;\n  let c, k;\n  for (k = 0; k < templateLength; k++) {\n    templateX[k] = template[k].x;\n    templateY[k] = template[k].y;\n    minX = Math.min(minX, template[k].x);\n    maxX = Math.max(maxX, template[k].x);\n    minY = Math.min(minY, template[k].y);\n    if (k < templateLength - 1 && template[k].y === template[k + 1].y && template[k].x === template[k + 1].x - 1) {\n      reuseMask |= 1 << templateLength - 1 - k;\n    } else {\n      changingTemplateEntries.push(k);\n    }\n  }\n  const changingEntriesLength = changingTemplateEntries.length;\n  const changingTemplateX = new Int8Array(changingEntriesLength);\n  const changingTemplateY = new Int8Array(changingEntriesLength);\n  const changingTemplateBit = new Uint16Array(changingEntriesLength);\n  for (c = 0; c < changingEntriesLength; c++) {\n    k = changingTemplateEntries[c];\n    changingTemplateX[c] = template[k].x;\n    changingTemplateY[c] = template[k].y;\n    changingTemplateBit[c] = 1 << templateLength - 1 - k;\n  }\n  const sbb_left = -minX;\n  const sbb_top = -minY;\n  const sbb_right = width - maxX;\n  const pseudoPixelContext = ReusedContexts[templateIndex];\n  let row = new Uint8Array(width);\n  const bitmap = [];\n  const decoder = decodingContext.decoder;\n  const contexts = decodingContext.contextCache.getContexts(\"GB\");\n  let ltp = 0,\n    j,\n    i0,\n    j0,\n    contextLabel = 0,\n    bit,\n    shift;\n  for (let i = 0; i < height; i++) {\n    if (prediction) {\n      const sltp = decoder.readBit(contexts, pseudoPixelContext);\n      ltp ^= sltp;\n      if (ltp) {\n        bitmap.push(row);\n        continue;\n      }\n    }\n    row = new Uint8Array(row);\n    bitmap.push(row);\n    for (j = 0; j < width; j++) {\n      if (useskip && skip[i][j]) {\n        row[j] = 0;\n        continue;\n      }\n      if (j >= sbb_left && j < sbb_right && i >= sbb_top) {\n        contextLabel = contextLabel << 1 & reuseMask;\n        for (k = 0; k < changingEntriesLength; k++) {\n          i0 = i + changingTemplateY[k];\n          j0 = j + changingTemplateX[k];\n          bit = bitmap[i0][j0];\n          if (bit) {\n            bit = changingTemplateBit[k];\n            contextLabel |= bit;\n          }\n        }\n      } else {\n        contextLabel = 0;\n        shift = templateLength - 1;\n        for (k = 0; k < templateLength; k++, shift--) {\n          j0 = j + templateX[k];\n          if (j0 >= 0 && j0 < width) {\n            i0 = i + templateY[k];\n            if (i0 >= 0) {\n              bit = bitmap[i0][j0];\n              if (bit) {\n                contextLabel |= bit << shift;\n              }\n            }\n          }\n        }\n      }\n      const pixel = decoder.readBit(contexts, contextLabel);\n      row[j] = pixel;\n    }\n  }\n  return bitmap;\n}\nfunction decodeRefinement(width, height, templateIndex, referenceBitmap, offsetX, offsetY, prediction, at, decodingContext) {\n  let codingTemplate = RefinementTemplates[templateIndex].coding;\n  if (templateIndex === 0) {\n    codingTemplate = codingTemplate.concat([at[0]]);\n  }\n  const codingTemplateLength = codingTemplate.length;\n  const codingTemplateX = new Int32Array(codingTemplateLength);\n  const codingTemplateY = new Int32Array(codingTemplateLength);\n  let k;\n  for (k = 0; k < codingTemplateLength; k++) {\n    codingTemplateX[k] = codingTemplate[k].x;\n    codingTemplateY[k] = codingTemplate[k].y;\n  }\n  let referenceTemplate = RefinementTemplates[templateIndex].reference;\n  if (templateIndex === 0) {\n    referenceTemplate = referenceTemplate.concat([at[1]]);\n  }\n  const referenceTemplateLength = referenceTemplate.length;\n  const referenceTemplateX = new Int32Array(referenceTemplateLength);\n  const referenceTemplateY = new Int32Array(referenceTemplateLength);\n  for (k = 0; k < referenceTemplateLength; k++) {\n    referenceTemplateX[k] = referenceTemplate[k].x;\n    referenceTemplateY[k] = referenceTemplate[k].y;\n  }\n  const referenceWidth = referenceBitmap[0].length;\n  const referenceHeight = referenceBitmap.length;\n  const pseudoPixelContext = RefinementReusedContexts[templateIndex];\n  const bitmap = [];\n  const decoder = decodingContext.decoder;\n  const contexts = decodingContext.contextCache.getContexts(\"GR\");\n  let ltp = 0;\n  for (let i = 0; i < height; i++) {\n    if (prediction) {\n      const sltp = decoder.readBit(contexts, pseudoPixelContext);\n      ltp ^= sltp;\n      if (ltp) {\n        throw new Jbig2Error(\"prediction is not supported\");\n      }\n    }\n    const row = new Uint8Array(width);\n    bitmap.push(row);\n    for (let j = 0; j < width; j++) {\n      let i0, j0;\n      let contextLabel = 0;\n      for (k = 0; k < codingTemplateLength; k++) {\n        i0 = i + codingTemplateY[k];\n        j0 = j + codingTemplateX[k];\n        if (i0 < 0 || j0 < 0 || j0 >= width) {\n          contextLabel <<= 1;\n        } else {\n          contextLabel = contextLabel << 1 | bitmap[i0][j0];\n        }\n      }\n      for (k = 0; k < referenceTemplateLength; k++) {\n        i0 = i + referenceTemplateY[k] - offsetY;\n        j0 = j + referenceTemplateX[k] - offsetX;\n        if (i0 < 0 || i0 >= referenceHeight || j0 < 0 || j0 >= referenceWidth) {\n          contextLabel <<= 1;\n        } else {\n          contextLabel = contextLabel << 1 | referenceBitmap[i0][j0];\n        }\n      }\n      const pixel = decoder.readBit(contexts, contextLabel);\n      row[j] = pixel;\n    }\n  }\n  return bitmap;\n}\nfunction decodeSymbolDictionary(huffman, refinement, symbols, numberOfNewSymbols, numberOfExportedSymbols, huffmanTables, templateIndex, at, refinementTemplateIndex, refinementAt, decodingContext, huffmanInput) {\n  if (huffman && refinement) {\n    throw new Jbig2Error(\"symbol refinement with Huffman is not supported\");\n  }\n  const newSymbols = [];\n  let currentHeight = 0;\n  let symbolCodeLength = log2(symbols.length + numberOfNewSymbols);\n  const decoder = decodingContext.decoder;\n  const contextCache = decodingContext.contextCache;\n  let tableB1, symbolWidths;\n  if (huffman) {\n    tableB1 = getStandardTable(1);\n    symbolWidths = [];\n    symbolCodeLength = Math.max(symbolCodeLength, 1);\n  }\n  while (newSymbols.length < numberOfNewSymbols) {\n    const deltaHeight = huffman ? huffmanTables.tableDeltaHeight.decode(huffmanInput) : decodeInteger(contextCache, \"IADH\", decoder);\n    currentHeight += deltaHeight;\n    let currentWidth = 0,\n      totalWidth = 0;\n    const firstSymbol = huffman ? symbolWidths.length : 0;\n    while (true) {\n      const deltaWidth = huffman ? huffmanTables.tableDeltaWidth.decode(huffmanInput) : decodeInteger(contextCache, \"IADW\", decoder);\n      if (deltaWidth === null) {\n        break;\n      }\n      currentWidth += deltaWidth;\n      totalWidth += currentWidth;\n      let bitmap;\n      if (refinement) {\n        const numberOfInstances = decodeInteger(contextCache, \"IAAI\", decoder);\n        if (numberOfInstances > 1) {\n          bitmap = decodeTextRegion(huffman, refinement, currentWidth, currentHeight, 0, numberOfInstances, 1, symbols.concat(newSymbols), symbolCodeLength, 0, 0, 1, 0, huffmanTables, refinementTemplateIndex, refinementAt, decodingContext, 0, huffmanInput);\n        } else {\n          const symbolId = decodeIAID(contextCache, decoder, symbolCodeLength);\n          const rdx = decodeInteger(contextCache, \"IARDX\", decoder);\n          const rdy = decodeInteger(contextCache, \"IARDY\", decoder);\n          const symbol = symbolId < symbols.length ? symbols[symbolId] : newSymbols[symbolId - symbols.length];\n          bitmap = decodeRefinement(currentWidth, currentHeight, refinementTemplateIndex, symbol, rdx, rdy, false, refinementAt, decodingContext);\n        }\n        newSymbols.push(bitmap);\n      } else if (huffman) {\n        symbolWidths.push(currentWidth);\n      } else {\n        bitmap = decodeBitmap(false, currentWidth, currentHeight, templateIndex, false, null, at, decodingContext);\n        newSymbols.push(bitmap);\n      }\n    }\n    if (huffman && !refinement) {\n      const bitmapSize = huffmanTables.tableBitmapSize.decode(huffmanInput);\n      huffmanInput.byteAlign();\n      let collectiveBitmap;\n      if (bitmapSize === 0) {\n        collectiveBitmap = readUncompressedBitmap(huffmanInput, totalWidth, currentHeight);\n      } else {\n        const originalEnd = huffmanInput.end;\n        const bitmapEnd = huffmanInput.position + bitmapSize;\n        huffmanInput.end = bitmapEnd;\n        collectiveBitmap = decodeMMRBitmap(huffmanInput, totalWidth, currentHeight, false);\n        huffmanInput.end = originalEnd;\n        huffmanInput.position = bitmapEnd;\n      }\n      const numberOfSymbolsDecoded = symbolWidths.length;\n      if (firstSymbol === numberOfSymbolsDecoded - 1) {\n        newSymbols.push(collectiveBitmap);\n      } else {\n        let i,\n          y,\n          xMin = 0,\n          xMax,\n          bitmapWidth,\n          symbolBitmap;\n        for (i = firstSymbol; i < numberOfSymbolsDecoded; i++) {\n          bitmapWidth = symbolWidths[i];\n          xMax = xMin + bitmapWidth;\n          symbolBitmap = [];\n          for (y = 0; y < currentHeight; y++) {\n            symbolBitmap.push(collectiveBitmap[y].subarray(xMin, xMax));\n          }\n          newSymbols.push(symbolBitmap);\n          xMin = xMax;\n        }\n      }\n    }\n  }\n  const exportedSymbols = [],\n    flags = [];\n  let currentFlag = false,\n    i,\n    ii;\n  const totalSymbolsLength = symbols.length + numberOfNewSymbols;\n  while (flags.length < totalSymbolsLength) {\n    let runLength = huffman ? tableB1.decode(huffmanInput) : decodeInteger(contextCache, \"IAEX\", decoder);\n    while (runLength--) {\n      flags.push(currentFlag);\n    }\n    currentFlag = !currentFlag;\n  }\n  for (i = 0, ii = symbols.length; i < ii; i++) {\n    if (flags[i]) {\n      exportedSymbols.push(symbols[i]);\n    }\n  }\n  for (let j = 0; j < numberOfNewSymbols; i++, j++) {\n    if (flags[i]) {\n      exportedSymbols.push(newSymbols[j]);\n    }\n  }\n  return exportedSymbols;\n}\nfunction decodeTextRegion(huffman, refinement, width, height, defaultPixelValue, numberOfSymbolInstances, stripSize, inputSymbols, symbolCodeLength, transposed, dsOffset, referenceCorner, combinationOperator, huffmanTables, refinementTemplateIndex, refinementAt, decodingContext, logStripSize, huffmanInput) {\n  if (huffman && refinement) {\n    throw new Jbig2Error(\"refinement with Huffman is not supported\");\n  }\n  const bitmap = [];\n  let i, row;\n  for (i = 0; i < height; i++) {\n    row = new Uint8Array(width);\n    if (defaultPixelValue) {\n      for (let j = 0; j < width; j++) {\n        row[j] = defaultPixelValue;\n      }\n    }\n    bitmap.push(row);\n  }\n  const decoder = decodingContext.decoder;\n  const contextCache = decodingContext.contextCache;\n  let stripT = huffman ? -huffmanTables.tableDeltaT.decode(huffmanInput) : -decodeInteger(contextCache, \"IADT\", decoder);\n  let firstS = 0;\n  i = 0;\n  while (i < numberOfSymbolInstances) {\n    const deltaT = huffman ? huffmanTables.tableDeltaT.decode(huffmanInput) : decodeInteger(contextCache, \"IADT\", decoder);\n    stripT += deltaT;\n    const deltaFirstS = huffman ? huffmanTables.tableFirstS.decode(huffmanInput) : decodeInteger(contextCache, \"IAFS\", decoder);\n    firstS += deltaFirstS;\n    let currentS = firstS;\n    do {\n      let currentT = 0;\n      if (stripSize > 1) {\n        currentT = huffman ? huffmanInput.readBits(logStripSize) : decodeInteger(contextCache, \"IAIT\", decoder);\n      }\n      const t = stripSize * stripT + currentT;\n      const symbolId = huffman ? huffmanTables.symbolIDTable.decode(huffmanInput) : decodeIAID(contextCache, decoder, symbolCodeLength);\n      const applyRefinement = refinement && (huffman ? huffmanInput.readBit() : decodeInteger(contextCache, \"IARI\", decoder));\n      let symbolBitmap = inputSymbols[symbolId];\n      let symbolWidth = symbolBitmap[0].length;\n      let symbolHeight = symbolBitmap.length;\n      if (applyRefinement) {\n        const rdw = decodeInteger(contextCache, \"IARDW\", decoder);\n        const rdh = decodeInteger(contextCache, \"IARDH\", decoder);\n        const rdx = decodeInteger(contextCache, \"IARDX\", decoder);\n        const rdy = decodeInteger(contextCache, \"IARDY\", decoder);\n        symbolWidth += rdw;\n        symbolHeight += rdh;\n        symbolBitmap = decodeRefinement(symbolWidth, symbolHeight, refinementTemplateIndex, symbolBitmap, (rdw >> 1) + rdx, (rdh >> 1) + rdy, false, refinementAt, decodingContext);\n      }\n      const offsetT = t - (referenceCorner & 1 ? 0 : symbolHeight - 1);\n      const offsetS = currentS - (referenceCorner & 2 ? symbolWidth - 1 : 0);\n      let s2, t2, symbolRow;\n      if (transposed) {\n        for (s2 = 0; s2 < symbolHeight; s2++) {\n          row = bitmap[offsetS + s2];\n          if (!row) {\n            continue;\n          }\n          symbolRow = symbolBitmap[s2];\n          const maxWidth = Math.min(width - offsetT, symbolWidth);\n          switch (combinationOperator) {\n            case 0:\n              for (t2 = 0; t2 < maxWidth; t2++) {\n                row[offsetT + t2] |= symbolRow[t2];\n              }\n              break;\n            case 2:\n              for (t2 = 0; t2 < maxWidth; t2++) {\n                row[offsetT + t2] ^= symbolRow[t2];\n              }\n              break;\n            default:\n              throw new Jbig2Error(`operator ${combinationOperator} is not supported`);\n          }\n        }\n        currentS += symbolHeight - 1;\n      } else {\n        for (t2 = 0; t2 < symbolHeight; t2++) {\n          row = bitmap[offsetT + t2];\n          if (!row) {\n            continue;\n          }\n          symbolRow = symbolBitmap[t2];\n          switch (combinationOperator) {\n            case 0:\n              for (s2 = 0; s2 < symbolWidth; s2++) {\n                row[offsetS + s2] |= symbolRow[s2];\n              }\n              break;\n            case 2:\n              for (s2 = 0; s2 < symbolWidth; s2++) {\n                row[offsetS + s2] ^= symbolRow[s2];\n              }\n              break;\n            default:\n              throw new Jbig2Error(`operator ${combinationOperator} is not supported`);\n          }\n        }\n        currentS += symbolWidth - 1;\n      }\n      i++;\n      const deltaS = huffman ? huffmanTables.tableDeltaS.decode(huffmanInput) : decodeInteger(contextCache, \"IADS\", decoder);\n      if (deltaS === null) {\n        break;\n      }\n      currentS += deltaS + dsOffset;\n    } while (true);\n  }\n  return bitmap;\n}\nfunction decodePatternDictionary(mmr, patternWidth, patternHeight, maxPatternIndex, template, decodingContext) {\n  const at = [];\n  if (!mmr) {\n    at.push({\n      x: -patternWidth,\n      y: 0\n    });\n    if (template === 0) {\n      at.push({\n        x: -3,\n        y: -1\n      }, {\n        x: 2,\n        y: -2\n      }, {\n        x: -2,\n        y: -2\n      });\n    }\n  }\n  const collectiveWidth = (maxPatternIndex + 1) * patternWidth;\n  const collectiveBitmap = decodeBitmap(mmr, collectiveWidth, patternHeight, template, false, null, at, decodingContext);\n  const patterns = [];\n  for (let i = 0; i <= maxPatternIndex; i++) {\n    const patternBitmap = [];\n    const xMin = patternWidth * i;\n    const xMax = xMin + patternWidth;\n    for (let y = 0; y < patternHeight; y++) {\n      patternBitmap.push(collectiveBitmap[y].subarray(xMin, xMax));\n    }\n    patterns.push(patternBitmap);\n  }\n  return patterns;\n}\nfunction decodeHalftoneRegion(mmr, patterns, template, regionWidth, regionHeight, defaultPixelValue, enableSkip, combinationOperator, gridWidth, gridHeight, gridOffsetX, gridOffsetY, gridVectorX, gridVectorY, decodingContext) {\n  const skip = null;\n  if (enableSkip) {\n    throw new Jbig2Error(\"skip is not supported\");\n  }\n  if (combinationOperator !== 0) {\n    throw new Jbig2Error(`operator \"${combinationOperator}\" is not supported in halftone region`);\n  }\n  const regionBitmap = [];\n  let i, j, row;\n  for (i = 0; i < regionHeight; i++) {\n    row = new Uint8Array(regionWidth);\n    if (defaultPixelValue) {\n      for (j = 0; j < regionWidth; j++) {\n        row[j] = defaultPixelValue;\n      }\n    }\n    regionBitmap.push(row);\n  }\n  const numberOfPatterns = patterns.length;\n  const pattern0 = patterns[0];\n  const patternWidth = pattern0[0].length,\n    patternHeight = pattern0.length;\n  const bitsPerValue = log2(numberOfPatterns);\n  const at = [];\n  if (!mmr) {\n    at.push({\n      x: template <= 1 ? 3 : 2,\n      y: -1\n    });\n    if (template === 0) {\n      at.push({\n        x: -3,\n        y: -1\n      }, {\n        x: 2,\n        y: -2\n      }, {\n        x: -2,\n        y: -2\n      });\n    }\n  }\n  const grayScaleBitPlanes = [];\n  let mmrInput, bitmap;\n  if (mmr) {\n    mmrInput = new Reader(decodingContext.data, decodingContext.start, decodingContext.end);\n  }\n  for (i = bitsPerValue - 1; i >= 0; i--) {\n    if (mmr) {\n      bitmap = decodeMMRBitmap(mmrInput, gridWidth, gridHeight, true);\n    } else {\n      bitmap = decodeBitmap(false, gridWidth, gridHeight, template, false, skip, at, decodingContext);\n    }\n    grayScaleBitPlanes[i] = bitmap;\n  }\n  let mg, ng, bit, patternIndex, patternBitmap, x, y, patternRow, regionRow;\n  for (mg = 0; mg < gridHeight; mg++) {\n    for (ng = 0; ng < gridWidth; ng++) {\n      bit = 0;\n      patternIndex = 0;\n      for (j = bitsPerValue - 1; j >= 0; j--) {\n        bit ^= grayScaleBitPlanes[j][mg][ng];\n        patternIndex |= bit << j;\n      }\n      patternBitmap = patterns[patternIndex];\n      x = gridOffsetX + mg * gridVectorY + ng * gridVectorX >> 8;\n      y = gridOffsetY + mg * gridVectorX - ng * gridVectorY >> 8;\n      if (x >= 0 && x + patternWidth <= regionWidth && y >= 0 && y + patternHeight <= regionHeight) {\n        for (i = 0; i < patternHeight; i++) {\n          regionRow = regionBitmap[y + i];\n          patternRow = patternBitmap[i];\n          for (j = 0; j < patternWidth; j++) {\n            regionRow[x + j] |= patternRow[j];\n          }\n        }\n      } else {\n        let regionX, regionY;\n        for (i = 0; i < patternHeight; i++) {\n          regionY = y + i;\n          if (regionY < 0 || regionY >= regionHeight) {\n            continue;\n          }\n          regionRow = regionBitmap[regionY];\n          patternRow = patternBitmap[i];\n          for (j = 0; j < patternWidth; j++) {\n            regionX = x + j;\n            if (regionX >= 0 && regionX < regionWidth) {\n              regionRow[regionX] |= patternRow[j];\n            }\n          }\n        }\n      }\n    }\n  }\n  return regionBitmap;\n}\nfunction readSegmentHeader(data, start) {\n  const segmentHeader = {};\n  segmentHeader.number = readUint32(data, start);\n  const flags = data[start + 4];\n  const segmentType = flags & 0x3f;\n  if (!SegmentTypes[segmentType]) {\n    throw new Jbig2Error(\"invalid segment type: \" + segmentType);\n  }\n  segmentHeader.type = segmentType;\n  segmentHeader.typeName = SegmentTypes[segmentType];\n  segmentHeader.deferredNonRetain = !!(flags & 0x80);\n  const pageAssociationFieldSize = !!(flags & 0x40);\n  const referredFlags = data[start + 5];\n  let referredToCount = referredFlags >> 5 & 7;\n  const retainBits = [referredFlags & 31];\n  let position = start + 6;\n  if (referredFlags === 7) {\n    referredToCount = readUint32(data, position - 1) & 0x1fffffff;\n    position += 3;\n    let bytes = referredToCount + 7 >> 3;\n    retainBits[0] = data[position++];\n    while (--bytes > 0) {\n      retainBits.push(data[position++]);\n    }\n  } else if (referredFlags === 5 || referredFlags === 6) {\n    throw new Jbig2Error(\"invalid referred-to flags\");\n  }\n  segmentHeader.retainBits = retainBits;\n  let referredToSegmentNumberSize = 4;\n  if (segmentHeader.number <= 256) {\n    referredToSegmentNumberSize = 1;\n  } else if (segmentHeader.number <= 65536) {\n    referredToSegmentNumberSize = 2;\n  }\n  const referredTo = [];\n  let i, ii;\n  for (i = 0; i < referredToCount; i++) {\n    let number;\n    if (referredToSegmentNumberSize === 1) {\n      number = data[position];\n    } else if (referredToSegmentNumberSize === 2) {\n      number = readUint16(data, position);\n    } else {\n      number = readUint32(data, position);\n    }\n    referredTo.push(number);\n    position += referredToSegmentNumberSize;\n  }\n  segmentHeader.referredTo = referredTo;\n  if (!pageAssociationFieldSize) {\n    segmentHeader.pageAssociation = data[position++];\n  } else {\n    segmentHeader.pageAssociation = readUint32(data, position);\n    position += 4;\n  }\n  segmentHeader.length = readUint32(data, position);\n  position += 4;\n  if (segmentHeader.length === 0xffffffff) {\n    if (segmentType === 38) {\n      const genericRegionInfo = readRegionSegmentInformation(data, position);\n      const genericRegionSegmentFlags = data[position + RegionSegmentInformationFieldLength];\n      const genericRegionMmr = !!(genericRegionSegmentFlags & 1);\n      const searchPatternLength = 6;\n      const searchPattern = new Uint8Array(searchPatternLength);\n      if (!genericRegionMmr) {\n        searchPattern[0] = 0xff;\n        searchPattern[1] = 0xac;\n      }\n      searchPattern[2] = genericRegionInfo.height >>> 24 & 0xff;\n      searchPattern[3] = genericRegionInfo.height >> 16 & 0xff;\n      searchPattern[4] = genericRegionInfo.height >> 8 & 0xff;\n      searchPattern[5] = genericRegionInfo.height & 0xff;\n      for (i = position, ii = data.length; i < ii; i++) {\n        let j = 0;\n        while (j < searchPatternLength && searchPattern[j] === data[i + j]) {\n          j++;\n        }\n        if (j === searchPatternLength) {\n          segmentHeader.length = i + searchPatternLength;\n          break;\n        }\n      }\n      if (segmentHeader.length === 0xffffffff) {\n        throw new Jbig2Error(\"segment end was not found\");\n      }\n    } else {\n      throw new Jbig2Error(\"invalid unknown segment length\");\n    }\n  }\n  segmentHeader.headerEnd = position;\n  return segmentHeader;\n}\nfunction readSegments(header, data, start, end) {\n  const segments = [];\n  let position = start;\n  while (position < end) {\n    const segmentHeader = readSegmentHeader(data, position);\n    position = segmentHeader.headerEnd;\n    const segment = {\n      header: segmentHeader,\n      data\n    };\n    if (!header.randomAccess) {\n      segment.start = position;\n      position += segmentHeader.length;\n      segment.end = position;\n    }\n    segments.push(segment);\n    if (segmentHeader.type === 51) {\n      break;\n    }\n  }\n  if (header.randomAccess) {\n    for (let i = 0, ii = segments.length; i < ii; i++) {\n      segments[i].start = position;\n      position += segments[i].header.length;\n      segments[i].end = position;\n    }\n  }\n  return segments;\n}\nfunction readRegionSegmentInformation(data, start) {\n  return {\n    width: readUint32(data, start),\n    height: readUint32(data, start + 4),\n    x: readUint32(data, start + 8),\n    y: readUint32(data, start + 12),\n    combinationOperator: data[start + 16] & 7\n  };\n}\nconst RegionSegmentInformationFieldLength = 17;\nfunction processSegment(segment, visitor) {\n  const header = segment.header;\n  const data = segment.data,\n    end = segment.end;\n  let position = segment.start;\n  let args, at, i, atLength;\n  switch (header.type) {\n    case 0:\n      const dictionary = {};\n      const dictionaryFlags = readUint16(data, position);\n      dictionary.huffman = !!(dictionaryFlags & 1);\n      dictionary.refinement = !!(dictionaryFlags & 2);\n      dictionary.huffmanDHSelector = dictionaryFlags >> 2 & 3;\n      dictionary.huffmanDWSelector = dictionaryFlags >> 4 & 3;\n      dictionary.bitmapSizeSelector = dictionaryFlags >> 6 & 1;\n      dictionary.aggregationInstancesSelector = dictionaryFlags >> 7 & 1;\n      dictionary.bitmapCodingContextUsed = !!(dictionaryFlags & 256);\n      dictionary.bitmapCodingContextRetained = !!(dictionaryFlags & 512);\n      dictionary.template = dictionaryFlags >> 10 & 3;\n      dictionary.refinementTemplate = dictionaryFlags >> 12 & 1;\n      position += 2;\n      if (!dictionary.huffman) {\n        atLength = dictionary.template === 0 ? 4 : 1;\n        at = [];\n        for (i = 0; i < atLength; i++) {\n          at.push({\n            x: readInt8(data, position),\n            y: readInt8(data, position + 1)\n          });\n          position += 2;\n        }\n        dictionary.at = at;\n      }\n      if (dictionary.refinement && !dictionary.refinementTemplate) {\n        at = [];\n        for (i = 0; i < 2; i++) {\n          at.push({\n            x: readInt8(data, position),\n            y: readInt8(data, position + 1)\n          });\n          position += 2;\n        }\n        dictionary.refinementAt = at;\n      }\n      dictionary.numberOfExportedSymbols = readUint32(data, position);\n      position += 4;\n      dictionary.numberOfNewSymbols = readUint32(data, position);\n      position += 4;\n      args = [dictionary, header.number, header.referredTo, data, position, end];\n      break;\n    case 6:\n    case 7:\n      const textRegion = {};\n      textRegion.info = readRegionSegmentInformation(data, position);\n      position += RegionSegmentInformationFieldLength;\n      const textRegionSegmentFlags = readUint16(data, position);\n      position += 2;\n      textRegion.huffman = !!(textRegionSegmentFlags & 1);\n      textRegion.refinement = !!(textRegionSegmentFlags & 2);\n      textRegion.logStripSize = textRegionSegmentFlags >> 2 & 3;\n      textRegion.stripSize = 1 << textRegion.logStripSize;\n      textRegion.referenceCorner = textRegionSegmentFlags >> 4 & 3;\n      textRegion.transposed = !!(textRegionSegmentFlags & 64);\n      textRegion.combinationOperator = textRegionSegmentFlags >> 7 & 3;\n      textRegion.defaultPixelValue = textRegionSegmentFlags >> 9 & 1;\n      textRegion.dsOffset = textRegionSegmentFlags << 17 >> 27;\n      textRegion.refinementTemplate = textRegionSegmentFlags >> 15 & 1;\n      if (textRegion.huffman) {\n        const textRegionHuffmanFlags = readUint16(data, position);\n        position += 2;\n        textRegion.huffmanFS = textRegionHuffmanFlags & 3;\n        textRegion.huffmanDS = textRegionHuffmanFlags >> 2 & 3;\n        textRegion.huffmanDT = textRegionHuffmanFlags >> 4 & 3;\n        textRegion.huffmanRefinementDW = textRegionHuffmanFlags >> 6 & 3;\n        textRegion.huffmanRefinementDH = textRegionHuffmanFlags >> 8 & 3;\n        textRegion.huffmanRefinementDX = textRegionHuffmanFlags >> 10 & 3;\n        textRegion.huffmanRefinementDY = textRegionHuffmanFlags >> 12 & 3;\n        textRegion.huffmanRefinementSizeSelector = !!(textRegionHuffmanFlags & 0x4000);\n      }\n      if (textRegion.refinement && !textRegion.refinementTemplate) {\n        at = [];\n        for (i = 0; i < 2; i++) {\n          at.push({\n            x: readInt8(data, position),\n            y: readInt8(data, position + 1)\n          });\n          position += 2;\n        }\n        textRegion.refinementAt = at;\n      }\n      textRegion.numberOfSymbolInstances = readUint32(data, position);\n      position += 4;\n      args = [textRegion, header.referredTo, data, position, end];\n      break;\n    case 16:\n      const patternDictionary = {};\n      const patternDictionaryFlags = data[position++];\n      patternDictionary.mmr = !!(patternDictionaryFlags & 1);\n      patternDictionary.template = patternDictionaryFlags >> 1 & 3;\n      patternDictionary.patternWidth = data[position++];\n      patternDictionary.patternHeight = data[position++];\n      patternDictionary.maxPatternIndex = readUint32(data, position);\n      position += 4;\n      args = [patternDictionary, header.number, data, position, end];\n      break;\n    case 22:\n    case 23:\n      const halftoneRegion = {};\n      halftoneRegion.info = readRegionSegmentInformation(data, position);\n      position += RegionSegmentInformationFieldLength;\n      const halftoneRegionFlags = data[position++];\n      halftoneRegion.mmr = !!(halftoneRegionFlags & 1);\n      halftoneRegion.template = halftoneRegionFlags >> 1 & 3;\n      halftoneRegion.enableSkip = !!(halftoneRegionFlags & 8);\n      halftoneRegion.combinationOperator = halftoneRegionFlags >> 4 & 7;\n      halftoneRegion.defaultPixelValue = halftoneRegionFlags >> 7 & 1;\n      halftoneRegion.gridWidth = readUint32(data, position);\n      position += 4;\n      halftoneRegion.gridHeight = readUint32(data, position);\n      position += 4;\n      halftoneRegion.gridOffsetX = readUint32(data, position) & 0xffffffff;\n      position += 4;\n      halftoneRegion.gridOffsetY = readUint32(data, position) & 0xffffffff;\n      position += 4;\n      halftoneRegion.gridVectorX = readUint16(data, position);\n      position += 2;\n      halftoneRegion.gridVectorY = readUint16(data, position);\n      position += 2;\n      args = [halftoneRegion, header.referredTo, data, position, end];\n      break;\n    case 38:\n    case 39:\n      const genericRegion = {};\n      genericRegion.info = readRegionSegmentInformation(data, position);\n      position += RegionSegmentInformationFieldLength;\n      const genericRegionSegmentFlags = data[position++];\n      genericRegion.mmr = !!(genericRegionSegmentFlags & 1);\n      genericRegion.template = genericRegionSegmentFlags >> 1 & 3;\n      genericRegion.prediction = !!(genericRegionSegmentFlags & 8);\n      if (!genericRegion.mmr) {\n        atLength = genericRegion.template === 0 ? 4 : 1;\n        at = [];\n        for (i = 0; i < atLength; i++) {\n          at.push({\n            x: readInt8(data, position),\n            y: readInt8(data, position + 1)\n          });\n          position += 2;\n        }\n        genericRegion.at = at;\n      }\n      args = [genericRegion, data, position, end];\n      break;\n    case 48:\n      const pageInfo = {\n        width: readUint32(data, position),\n        height: readUint32(data, position + 4),\n        resolutionX: readUint32(data, position + 8),\n        resolutionY: readUint32(data, position + 12)\n      };\n      if (pageInfo.height === 0xffffffff) {\n        delete pageInfo.height;\n      }\n      const pageSegmentFlags = data[position + 16];\n      readUint16(data, position + 17);\n      pageInfo.lossless = !!(pageSegmentFlags & 1);\n      pageInfo.refinement = !!(pageSegmentFlags & 2);\n      pageInfo.defaultPixelValue = pageSegmentFlags >> 2 & 1;\n      pageInfo.combinationOperator = pageSegmentFlags >> 3 & 3;\n      pageInfo.requiresBuffer = !!(pageSegmentFlags & 32);\n      pageInfo.combinationOperatorOverride = !!(pageSegmentFlags & 64);\n      args = [pageInfo];\n      break;\n    case 49:\n      break;\n    case 50:\n      break;\n    case 51:\n      break;\n    case 53:\n      args = [header.number, data, position, end];\n      break;\n    case 62:\n      break;\n    default:\n      throw new Jbig2Error(`segment type ${header.typeName}(${header.type}) is not implemented`);\n  }\n  const callbackName = \"on\" + header.typeName;\n  if (callbackName in visitor) {\n    visitor[callbackName].apply(visitor, args);\n  }\n}\nfunction processSegments(segments, visitor) {\n  for (let i = 0, ii = segments.length; i < ii; i++) {\n    processSegment(segments[i], visitor);\n  }\n}\nfunction parseJbig2Chunks(chunks) {\n  const visitor = new SimpleSegmentVisitor();\n  for (let i = 0, ii = chunks.length; i < ii; i++) {\n    const chunk = chunks[i];\n    const segments = readSegments({}, chunk.data, chunk.start, chunk.end);\n    processSegments(segments, visitor);\n  }\n  return visitor.buffer;\n}\nfunction parseJbig2(data) {\n  throw new Error(\"Not implemented: parseJbig2\");\n}\nclass SimpleSegmentVisitor {\n  onPageInformation(info) {\n    this.currentPageInfo = info;\n    const rowSize = info.width + 7 >> 3;\n    const buffer = new Uint8ClampedArray(rowSize * info.height);\n    if (info.defaultPixelValue) {\n      buffer.fill(0xff);\n    }\n    this.buffer = buffer;\n  }\n  drawBitmap(regionInfo, bitmap) {\n    const pageInfo = this.currentPageInfo;\n    const width = regionInfo.width,\n      height = regionInfo.height;\n    const rowSize = pageInfo.width + 7 >> 3;\n    const combinationOperator = pageInfo.combinationOperatorOverride ? regionInfo.combinationOperator : pageInfo.combinationOperator;\n    const buffer = this.buffer;\n    const mask0 = 128 >> (regionInfo.x & 7);\n    let offset0 = regionInfo.y * rowSize + (regionInfo.x >> 3);\n    let i, j, mask, offset;\n    switch (combinationOperator) {\n      case 0:\n        for (i = 0; i < height; i++) {\n          mask = mask0;\n          offset = offset0;\n          for (j = 0; j < width; j++) {\n            if (bitmap[i][j]) {\n              buffer[offset] |= mask;\n            }\n            mask >>= 1;\n            if (!mask) {\n              mask = 128;\n              offset++;\n            }\n          }\n          offset0 += rowSize;\n        }\n        break;\n      case 2:\n        for (i = 0; i < height; i++) {\n          mask = mask0;\n          offset = offset0;\n          for (j = 0; j < width; j++) {\n            if (bitmap[i][j]) {\n              buffer[offset] ^= mask;\n            }\n            mask >>= 1;\n            if (!mask) {\n              mask = 128;\n              offset++;\n            }\n          }\n          offset0 += rowSize;\n        }\n        break;\n      default:\n        throw new Jbig2Error(`operator ${combinationOperator} is not supported`);\n    }\n  }\n  onImmediateGenericRegion(region, data, start, end) {\n    const regionInfo = region.info;\n    const decodingContext = new DecodingContext(data, start, end);\n    const bitmap = decodeBitmap(region.mmr, regionInfo.width, regionInfo.height, region.template, region.prediction, null, region.at, decodingContext);\n    this.drawBitmap(regionInfo, bitmap);\n  }\n  onImmediateLosslessGenericRegion() {\n    this.onImmediateGenericRegion(...arguments);\n  }\n  onSymbolDictionary(dictionary, currentSegment, referredSegments, data, start, end) {\n    let huffmanTables, huffmanInput;\n    if (dictionary.huffman) {\n      huffmanTables = getSymbolDictionaryHuffmanTables(dictionary, referredSegments, this.customTables);\n      huffmanInput = new Reader(data, start, end);\n    }\n    let symbols = this.symbols;\n    if (!symbols) {\n      this.symbols = symbols = {};\n    }\n    const inputSymbols = [];\n    for (const referredSegment of referredSegments) {\n      const referredSymbols = symbols[referredSegment];\n      if (referredSymbols) {\n        inputSymbols.push(...referredSymbols);\n      }\n    }\n    const decodingContext = new DecodingContext(data, start, end);\n    symbols[currentSegment] = decodeSymbolDictionary(dictionary.huffman, dictionary.refinement, inputSymbols, dictionary.numberOfNewSymbols, dictionary.numberOfExportedSymbols, huffmanTables, dictionary.template, dictionary.at, dictionary.refinementTemplate, dictionary.refinementAt, decodingContext, huffmanInput);\n  }\n  onImmediateTextRegion(region, referredSegments, data, start, end) {\n    const regionInfo = region.info;\n    let huffmanTables, huffmanInput;\n    const symbols = this.symbols;\n    const inputSymbols = [];\n    for (const referredSegment of referredSegments) {\n      const referredSymbols = symbols[referredSegment];\n      if (referredSymbols) {\n        inputSymbols.push(...referredSymbols);\n      }\n    }\n    const symbolCodeLength = log2(inputSymbols.length);\n    if (region.huffman) {\n      huffmanInput = new Reader(data, start, end);\n      huffmanTables = getTextRegionHuffmanTables(region, referredSegments, this.customTables, inputSymbols.length, huffmanInput);\n    }\n    const decodingContext = new DecodingContext(data, start, end);\n    const bitmap = decodeTextRegion(region.huffman, region.refinement, regionInfo.width, regionInfo.height, region.defaultPixelValue, region.numberOfSymbolInstances, region.stripSize, inputSymbols, symbolCodeLength, region.transposed, region.dsOffset, region.referenceCorner, region.combinationOperator, huffmanTables, region.refinementTemplate, region.refinementAt, decodingContext, region.logStripSize, huffmanInput);\n    this.drawBitmap(regionInfo, bitmap);\n  }\n  onImmediateLosslessTextRegion() {\n    this.onImmediateTextRegion(...arguments);\n  }\n  onPatternDictionary(dictionary, currentSegment, data, start, end) {\n    let patterns = this.patterns;\n    if (!patterns) {\n      this.patterns = patterns = {};\n    }\n    const decodingContext = new DecodingContext(data, start, end);\n    patterns[currentSegment] = decodePatternDictionary(dictionary.mmr, dictionary.patternWidth, dictionary.patternHeight, dictionary.maxPatternIndex, dictionary.template, decodingContext);\n  }\n  onImmediateHalftoneRegion(region, referredSegments, data, start, end) {\n    const patterns = this.patterns[referredSegments[0]];\n    const regionInfo = region.info;\n    const decodingContext = new DecodingContext(data, start, end);\n    const bitmap = decodeHalftoneRegion(region.mmr, patterns, region.template, regionInfo.width, regionInfo.height, region.defaultPixelValue, region.enableSkip, region.combinationOperator, region.gridWidth, region.gridHeight, region.gridOffsetX, region.gridOffsetY, region.gridVectorX, region.gridVectorY, decodingContext);\n    this.drawBitmap(regionInfo, bitmap);\n  }\n  onImmediateLosslessHalftoneRegion() {\n    this.onImmediateHalftoneRegion(...arguments);\n  }\n  onTables(currentSegment, data, start, end) {\n    let customTables = this.customTables;\n    if (!customTables) {\n      this.customTables = customTables = {};\n    }\n    customTables[currentSegment] = decodeTablesSegment(data, start, end);\n  }\n}\nclass HuffmanLine {\n  constructor(lineData) {\n    if (lineData.length === 2) {\n      this.isOOB = true;\n      this.rangeLow = 0;\n      this.prefixLength = lineData[0];\n      this.rangeLength = 0;\n      this.prefixCode = lineData[1];\n      this.isLowerRange = false;\n    } else {\n      this.isOOB = false;\n      this.rangeLow = lineData[0];\n      this.prefixLength = lineData[1];\n      this.rangeLength = lineData[2];\n      this.prefixCode = lineData[3];\n      this.isLowerRange = lineData[4] === \"lower\";\n    }\n  }\n}\nclass HuffmanTreeNode {\n  constructor(line) {\n    this.children = [];\n    if (line) {\n      this.isLeaf = true;\n      this.rangeLength = line.rangeLength;\n      this.rangeLow = line.rangeLow;\n      this.isLowerRange = line.isLowerRange;\n      this.isOOB = line.isOOB;\n    } else {\n      this.isLeaf = false;\n    }\n  }\n  buildTree(line, shift) {\n    const bit = line.prefixCode >> shift & 1;\n    if (shift <= 0) {\n      this.children[bit] = new HuffmanTreeNode(line);\n    } else {\n      let node = this.children[bit];\n      if (!node) {\n        this.children[bit] = node = new HuffmanTreeNode(null);\n      }\n      node.buildTree(line, shift - 1);\n    }\n  }\n  decodeNode(reader) {\n    if (this.isLeaf) {\n      if (this.isOOB) {\n        return null;\n      }\n      const htOffset = reader.readBits(this.rangeLength);\n      return this.rangeLow + (this.isLowerRange ? -htOffset : htOffset);\n    }\n    const node = this.children[reader.readBit()];\n    if (!node) {\n      throw new Jbig2Error(\"invalid Huffman data\");\n    }\n    return node.decodeNode(reader);\n  }\n}\nclass HuffmanTable {\n  constructor(lines, prefixCodesDone) {\n    if (!prefixCodesDone) {\n      this.assignPrefixCodes(lines);\n    }\n    this.rootNode = new HuffmanTreeNode(null);\n    for (let i = 0, ii = lines.length; i < ii; i++) {\n      const line = lines[i];\n      if (line.prefixLength > 0) {\n        this.rootNode.buildTree(line, line.prefixLength - 1);\n      }\n    }\n  }\n  decode(reader) {\n    return this.rootNode.decodeNode(reader);\n  }\n  assignPrefixCodes(lines) {\n    const linesLength = lines.length;\n    let prefixLengthMax = 0;\n    for (let i = 0; i < linesLength; i++) {\n      prefixLengthMax = Math.max(prefixLengthMax, lines[i].prefixLength);\n    }\n    const histogram = new Uint32Array(prefixLengthMax + 1);\n    for (let i = 0; i < linesLength; i++) {\n      histogram[lines[i].prefixLength]++;\n    }\n    let currentLength = 1,\n      firstCode = 0,\n      currentCode,\n      currentTemp,\n      line;\n    histogram[0] = 0;\n    while (currentLength <= prefixLengthMax) {\n      firstCode = firstCode + histogram[currentLength - 1] << 1;\n      currentCode = firstCode;\n      currentTemp = 0;\n      while (currentTemp < linesLength) {\n        line = lines[currentTemp];\n        if (line.prefixLength === currentLength) {\n          line.prefixCode = currentCode;\n          currentCode++;\n        }\n        currentTemp++;\n      }\n      currentLength++;\n    }\n  }\n}\nfunction decodeTablesSegment(data, start, end) {\n  const flags = data[start];\n  const lowestValue = readUint32(data, start + 1) & 0xffffffff;\n  const highestValue = readUint32(data, start + 5) & 0xffffffff;\n  const reader = new Reader(data, start + 9, end);\n  const prefixSizeBits = (flags >> 1 & 7) + 1;\n  const rangeSizeBits = (flags >> 4 & 7) + 1;\n  const lines = [];\n  let prefixLength,\n    rangeLength,\n    currentRangeLow = lowestValue;\n  do {\n    prefixLength = reader.readBits(prefixSizeBits);\n    rangeLength = reader.readBits(rangeSizeBits);\n    lines.push(new HuffmanLine([currentRangeLow, prefixLength, rangeLength, 0]));\n    currentRangeLow += 1 << rangeLength;\n  } while (currentRangeLow < highestValue);\n  prefixLength = reader.readBits(prefixSizeBits);\n  lines.push(new HuffmanLine([lowestValue - 1, prefixLength, 32, 0, \"lower\"]));\n  prefixLength = reader.readBits(prefixSizeBits);\n  lines.push(new HuffmanLine([highestValue, prefixLength, 32, 0]));\n  if (flags & 1) {\n    prefixLength = reader.readBits(prefixSizeBits);\n    lines.push(new HuffmanLine([prefixLength, 0]));\n  }\n  return new HuffmanTable(lines, false);\n}\nconst standardTablesCache = {};\nfunction getStandardTable(number) {\n  let table = standardTablesCache[number];\n  if (table) {\n    return table;\n  }\n  let lines;\n  switch (number) {\n    case 1:\n      lines = [[0, 1, 4, 0x0], [16, 2, 8, 0x2], [272, 3, 16, 0x6], [65808, 3, 32, 0x7]];\n      break;\n    case 2:\n      lines = [[0, 1, 0, 0x0], [1, 2, 0, 0x2], [2, 3, 0, 0x6], [3, 4, 3, 0xe], [11, 5, 6, 0x1e], [75, 6, 32, 0x3e], [6, 0x3f]];\n      break;\n    case 3:\n      lines = [[-256, 8, 8, 0xfe], [0, 1, 0, 0x0], [1, 2, 0, 0x2], [2, 3, 0, 0x6], [3, 4, 3, 0xe], [11, 5, 6, 0x1e], [-257, 8, 32, 0xff, \"lower\"], [75, 7, 32, 0x7e], [6, 0x3e]];\n      break;\n    case 4:\n      lines = [[1, 1, 0, 0x0], [2, 2, 0, 0x2], [3, 3, 0, 0x6], [4, 4, 3, 0xe], [12, 5, 6, 0x1e], [76, 5, 32, 0x1f]];\n      break;\n    case 5:\n      lines = [[-255, 7, 8, 0x7e], [1, 1, 0, 0x0], [2, 2, 0, 0x2], [3, 3, 0, 0x6], [4, 4, 3, 0xe], [12, 5, 6, 0x1e], [-256, 7, 32, 0x7f, \"lower\"], [76, 6, 32, 0x3e]];\n      break;\n    case 6:\n      lines = [[-2048, 5, 10, 0x1c], [-1024, 4, 9, 0x8], [-512, 4, 8, 0x9], [-256, 4, 7, 0xa], [-128, 5, 6, 0x1d], [-64, 5, 5, 0x1e], [-32, 4, 5, 0xb], [0, 2, 7, 0x0], [128, 3, 7, 0x2], [256, 3, 8, 0x3], [512, 4, 9, 0xc], [1024, 4, 10, 0xd], [-2049, 6, 32, 0x3e, \"lower\"], [2048, 6, 32, 0x3f]];\n      break;\n    case 7:\n      lines = [[-1024, 4, 9, 0x8], [-512, 3, 8, 0x0], [-256, 4, 7, 0x9], [-128, 5, 6, 0x1a], [-64, 5, 5, 0x1b], [-32, 4, 5, 0xa], [0, 4, 5, 0xb], [32, 5, 5, 0x1c], [64, 5, 6, 0x1d], [128, 4, 7, 0xc], [256, 3, 8, 0x1], [512, 3, 9, 0x2], [1024, 3, 10, 0x3], [-1025, 5, 32, 0x1e, \"lower\"], [2048, 5, 32, 0x1f]];\n      break;\n    case 8:\n      lines = [[-15, 8, 3, 0xfc], [-7, 9, 1, 0x1fc], [-5, 8, 1, 0xfd], [-3, 9, 0, 0x1fd], [-2, 7, 0, 0x7c], [-1, 4, 0, 0xa], [0, 2, 1, 0x0], [2, 5, 0, 0x1a], [3, 6, 0, 0x3a], [4, 3, 4, 0x4], [20, 6, 1, 0x3b], [22, 4, 4, 0xb], [38, 4, 5, 0xc], [70, 5, 6, 0x1b], [134, 5, 7, 0x1c], [262, 6, 7, 0x3c], [390, 7, 8, 0x7d], [646, 6, 10, 0x3d], [-16, 9, 32, 0x1fe, \"lower\"], [1670, 9, 32, 0x1ff], [2, 0x1]];\n      break;\n    case 9:\n      lines = [[-31, 8, 4, 0xfc], [-15, 9, 2, 0x1fc], [-11, 8, 2, 0xfd], [-7, 9, 1, 0x1fd], [-5, 7, 1, 0x7c], [-3, 4, 1, 0xa], [-1, 3, 1, 0x2], [1, 3, 1, 0x3], [3, 5, 1, 0x1a], [5, 6, 1, 0x3a], [7, 3, 5, 0x4], [39, 6, 2, 0x3b], [43, 4, 5, 0xb], [75, 4, 6, 0xc], [139, 5, 7, 0x1b], [267, 5, 8, 0x1c], [523, 6, 8, 0x3c], [779, 7, 9, 0x7d], [1291, 6, 11, 0x3d], [-32, 9, 32, 0x1fe, \"lower\"], [3339, 9, 32, 0x1ff], [2, 0x0]];\n      break;\n    case 10:\n      lines = [[-21, 7, 4, 0x7a], [-5, 8, 0, 0xfc], [-4, 7, 0, 0x7b], [-3, 5, 0, 0x18], [-2, 2, 2, 0x0], [2, 5, 0, 0x19], [3, 6, 0, 0x36], [4, 7, 0, 0x7c], [5, 8, 0, 0xfd], [6, 2, 6, 0x1], [70, 5, 5, 0x1a], [102, 6, 5, 0x37], [134, 6, 6, 0x38], [198, 6, 7, 0x39], [326, 6, 8, 0x3a], [582, 6, 9, 0x3b], [1094, 6, 10, 0x3c], [2118, 7, 11, 0x7d], [-22, 8, 32, 0xfe, \"lower\"], [4166, 8, 32, 0xff], [2, 0x2]];\n      break;\n    case 11:\n      lines = [[1, 1, 0, 0x0], [2, 2, 1, 0x2], [4, 4, 0, 0xc], [5, 4, 1, 0xd], [7, 5, 1, 0x1c], [9, 5, 2, 0x1d], [13, 6, 2, 0x3c], [17, 7, 2, 0x7a], [21, 7, 3, 0x7b], [29, 7, 4, 0x7c], [45, 7, 5, 0x7d], [77, 7, 6, 0x7e], [141, 7, 32, 0x7f]];\n      break;\n    case 12:\n      lines = [[1, 1, 0, 0x0], [2, 2, 0, 0x2], [3, 3, 1, 0x6], [5, 5, 0, 0x1c], [6, 5, 1, 0x1d], [8, 6, 1, 0x3c], [10, 7, 0, 0x7a], [11, 7, 1, 0x7b], [13, 7, 2, 0x7c], [17, 7, 3, 0x7d], [25, 7, 4, 0x7e], [41, 8, 5, 0xfe], [73, 8, 32, 0xff]];\n      break;\n    case 13:\n      lines = [[1, 1, 0, 0x0], [2, 3, 0, 0x4], [3, 4, 0, 0xc], [4, 5, 0, 0x1c], [5, 4, 1, 0xd], [7, 3, 3, 0x5], [15, 6, 1, 0x3a], [17, 6, 2, 0x3b], [21, 6, 3, 0x3c], [29, 6, 4, 0x3d], [45, 6, 5, 0x3e], [77, 7, 6, 0x7e], [141, 7, 32, 0x7f]];\n      break;\n    case 14:\n      lines = [[-2, 3, 0, 0x4], [-1, 3, 0, 0x5], [0, 1, 0, 0x0], [1, 3, 0, 0x6], [2, 3, 0, 0x7]];\n      break;\n    case 15:\n      lines = [[-24, 7, 4, 0x7c], [-8, 6, 2, 0x3c], [-4, 5, 1, 0x1c], [-2, 4, 0, 0xc], [-1, 3, 0, 0x4], [0, 1, 0, 0x0], [1, 3, 0, 0x5], [2, 4, 0, 0xd], [3, 5, 1, 0x1d], [5, 6, 2, 0x3d], [9, 7, 4, 0x7d], [-25, 7, 32, 0x7e, \"lower\"], [25, 7, 32, 0x7f]];\n      break;\n    default:\n      throw new Jbig2Error(`standard table B.${number} does not exist`);\n  }\n  for (let i = 0, ii = lines.length; i < ii; i++) {\n    lines[i] = new HuffmanLine(lines[i]);\n  }\n  table = new HuffmanTable(lines, true);\n  standardTablesCache[number] = table;\n  return table;\n}\nclass Reader {\n  constructor(data, start, end) {\n    this.data = data;\n    this.start = start;\n    this.end = end;\n    this.position = start;\n    this.shift = -1;\n    this.currentByte = 0;\n  }\n  readBit() {\n    if (this.shift < 0) {\n      if (this.position >= this.end) {\n        throw new Jbig2Error(\"end of data while reading bit\");\n      }\n      this.currentByte = this.data[this.position++];\n      this.shift = 7;\n    }\n    const bit = this.currentByte >> this.shift & 1;\n    this.shift--;\n    return bit;\n  }\n  readBits(numBits) {\n    let result = 0,\n      i;\n    for (i = numBits - 1; i >= 0; i--) {\n      result |= this.readBit() << i;\n    }\n    return result;\n  }\n  byteAlign() {\n    this.shift = -1;\n  }\n  next() {\n    if (this.position >= this.end) {\n      return -1;\n    }\n    return this.data[this.position++];\n  }\n}\nfunction getCustomHuffmanTable(index, referredTo, customTables) {\n  let currentIndex = 0;\n  for (let i = 0, ii = referredTo.length; i < ii; i++) {\n    const table = customTables[referredTo[i]];\n    if (table) {\n      if (index === currentIndex) {\n        return table;\n      }\n      currentIndex++;\n    }\n  }\n  throw new Jbig2Error(\"can't find custom Huffman table\");\n}\nfunction getTextRegionHuffmanTables(textRegion, referredTo, customTables, numberOfSymbols, reader) {\n  const codes = [];\n  for (let i = 0; i <= 34; i++) {\n    const codeLength = reader.readBits(4);\n    codes.push(new HuffmanLine([i, codeLength, 0, 0]));\n  }\n  const runCodesTable = new HuffmanTable(codes, false);\n  codes.length = 0;\n  for (let i = 0; i < numberOfSymbols;) {\n    const codeLength = runCodesTable.decode(reader);\n    if (codeLength >= 32) {\n      let repeatedLength, numberOfRepeats, j;\n      switch (codeLength) {\n        case 32:\n          if (i === 0) {\n            throw new Jbig2Error(\"no previous value in symbol ID table\");\n          }\n          numberOfRepeats = reader.readBits(2) + 3;\n          repeatedLength = codes[i - 1].prefixLength;\n          break;\n        case 33:\n          numberOfRepeats = reader.readBits(3) + 3;\n          repeatedLength = 0;\n          break;\n        case 34:\n          numberOfRepeats = reader.readBits(7) + 11;\n          repeatedLength = 0;\n          break;\n        default:\n          throw new Jbig2Error(\"invalid code length in symbol ID table\");\n      }\n      for (j = 0; j < numberOfRepeats; j++) {\n        codes.push(new HuffmanLine([i, repeatedLength, 0, 0]));\n        i++;\n      }\n    } else {\n      codes.push(new HuffmanLine([i, codeLength, 0, 0]));\n      i++;\n    }\n  }\n  reader.byteAlign();\n  const symbolIDTable = new HuffmanTable(codes, false);\n  let customIndex = 0,\n    tableFirstS,\n    tableDeltaS,\n    tableDeltaT;\n  switch (textRegion.huffmanFS) {\n    case 0:\n    case 1:\n      tableFirstS = getStandardTable(textRegion.huffmanFS + 6);\n      break;\n    case 3:\n      tableFirstS = getCustomHuffmanTable(customIndex, referredTo, customTables);\n      customIndex++;\n      break;\n    default:\n      throw new Jbig2Error(\"invalid Huffman FS selector\");\n  }\n  switch (textRegion.huffmanDS) {\n    case 0:\n    case 1:\n    case 2:\n      tableDeltaS = getStandardTable(textRegion.huffmanDS + 8);\n      break;\n    case 3:\n      tableDeltaS = getCustomHuffmanTable(customIndex, referredTo, customTables);\n      customIndex++;\n      break;\n    default:\n      throw new Jbig2Error(\"invalid Huffman DS selector\");\n  }\n  switch (textRegion.huffmanDT) {\n    case 0:\n    case 1:\n    case 2:\n      tableDeltaT = getStandardTable(textRegion.huffmanDT + 11);\n      break;\n    case 3:\n      tableDeltaT = getCustomHuffmanTable(customIndex, referredTo, customTables);\n      customIndex++;\n      break;\n    default:\n      throw new Jbig2Error(\"invalid Huffman DT selector\");\n  }\n  if (textRegion.refinement) {\n    throw new Jbig2Error(\"refinement with Huffman is not supported\");\n  }\n  return {\n    symbolIDTable,\n    tableFirstS,\n    tableDeltaS,\n    tableDeltaT\n  };\n}\nfunction getSymbolDictionaryHuffmanTables(dictionary, referredTo, customTables) {\n  let customIndex = 0,\n    tableDeltaHeight,\n    tableDeltaWidth;\n  switch (dictionary.huffmanDHSelector) {\n    case 0:\n    case 1:\n      tableDeltaHeight = getStandardTable(dictionary.huffmanDHSelector + 4);\n      break;\n    case 3:\n      tableDeltaHeight = getCustomHuffmanTable(customIndex, referredTo, customTables);\n      customIndex++;\n      break;\n    default:\n      throw new Jbig2Error(\"invalid Huffman DH selector\");\n  }\n  switch (dictionary.huffmanDWSelector) {\n    case 0:\n    case 1:\n      tableDeltaWidth = getStandardTable(dictionary.huffmanDWSelector + 2);\n      break;\n    case 3:\n      tableDeltaWidth = getCustomHuffmanTable(customIndex, referredTo, customTables);\n      customIndex++;\n      break;\n    default:\n      throw new Jbig2Error(\"invalid Huffman DW selector\");\n  }\n  let tableBitmapSize, tableAggregateInstances;\n  if (dictionary.bitmapSizeSelector) {\n    tableBitmapSize = getCustomHuffmanTable(customIndex, referredTo, customTables);\n    customIndex++;\n  } else {\n    tableBitmapSize = getStandardTable(1);\n  }\n  if (dictionary.aggregationInstancesSelector) {\n    tableAggregateInstances = getCustomHuffmanTable(customIndex, referredTo, customTables);\n  } else {\n    tableAggregateInstances = getStandardTable(1);\n  }\n  return {\n    tableDeltaHeight,\n    tableDeltaWidth,\n    tableBitmapSize,\n    tableAggregateInstances\n  };\n}\nfunction readUncompressedBitmap(reader, width, height) {\n  const bitmap = [];\n  for (let y = 0; y < height; y++) {\n    const row = new Uint8Array(width);\n    bitmap.push(row);\n    for (let x = 0; x < width; x++) {\n      row[x] = reader.readBit();\n    }\n    reader.byteAlign();\n  }\n  return bitmap;\n}\nfunction decodeMMRBitmap(input, width, height, endOfBlock) {\n  const params = {\n    K: -1,\n    Columns: width,\n    Rows: height,\n    BlackIs1: true,\n    EndOfBlock: endOfBlock\n  };\n  const decoder = new CCITTFaxDecoder(input, params);\n  const bitmap = [];\n  let currentByte,\n    eof = false;\n  for (let y = 0; y < height; y++) {\n    const row = new Uint8Array(width);\n    bitmap.push(row);\n    let shift = -1;\n    for (let x = 0; x < width; x++) {\n      if (shift < 0) {\n        currentByte = decoder.readNextChar();\n        if (currentByte === -1) {\n          currentByte = 0;\n          eof = true;\n        }\n        shift = 7;\n      }\n      row[x] = currentByte >> shift & 1;\n      shift--;\n    }\n  }\n  if (endOfBlock && !eof) {\n    const lookForEOFLimit = 5;\n    for (let i = 0; i < lookForEOFLimit; i++) {\n      if (decoder.readNextChar() === -1) {\n        break;\n      }\n    }\n  }\n  return bitmap;\n}\nclass Jbig2Image {\n  parseChunks(chunks) {\n    return parseJbig2Chunks(chunks);\n  }\n  parse(data) {\n    throw new Error(\"Not implemented: Jbig2Image.parse\");\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/jbig2_stream.js\n\n\n\n\n\nclass Jbig2Stream extends DecodeStream {\n  constructor(stream, maybeLength, params) {\n    super(maybeLength);\n    this.stream = stream;\n    this.dict = stream.dict;\n    this.maybeLength = maybeLength;\n    this.params = params;\n  }\n  get bytes() {\n    return shadow(this, \"bytes\", this.stream.getBytes(this.maybeLength));\n  }\n  ensureBuffer(requested) {}\n  readBlock() {\n    if (this.eof) {\n      return;\n    }\n    const jbig2Image = new Jbig2Image();\n    const chunks = [];\n    if (this.params instanceof Dict) {\n      const globalsStream = this.params.get(\"JBIG2Globals\");\n      if (globalsStream instanceof BaseStream) {\n        const globals = globalsStream.getBytes();\n        chunks.push({\n          data: globals,\n          start: 0,\n          end: globals.length\n        });\n      }\n    }\n    chunks.push({\n      data: this.bytes,\n      start: 0,\n      end: this.bytes.length\n    });\n    const data = jbig2Image.parseChunks(chunks);\n    const dataLength = data.length;\n    for (let i = 0; i < dataLength; i++) {\n      data[i] ^= 0xff;\n    }\n    this.buffer = data;\n    this.bufferLength = dataLength;\n    this.eof = true;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/shared/image_utils.js\n\nfunction convertToRGBA(params) {\n  switch (params.kind) {\n    case ImageKind.GRAYSCALE_1BPP:\n      return convertBlackAndWhiteToRGBA(params);\n    case ImageKind.RGB_24BPP:\n      return convertRGBToRGBA(params);\n  }\n  return null;\n}\nfunction convertBlackAndWhiteToRGBA({\n  src,\n  srcPos = 0,\n  dest,\n  width,\n  height,\n  nonBlackColor = 0xffffffff,\n  inverseDecode = false\n}) {\n  const black = FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff;\n  const [zeroMapping, oneMapping] = inverseDecode ? [nonBlackColor, black] : [black, nonBlackColor];\n  const widthInSource = width >> 3;\n  const widthRemainder = width & 7;\n  const srcLength = src.length;\n  dest = new Uint32Array(dest.buffer);\n  let destPos = 0;\n  for (let i = 0; i < height; i++) {\n    for (const max = srcPos + widthInSource; srcPos < max; srcPos++) {\n      const elem = srcPos < srcLength ? src[srcPos] : 255;\n      dest[destPos++] = elem & 0b10000000 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b1000000 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b100000 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b10000 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b1000 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b100 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b10 ? oneMapping : zeroMapping;\n      dest[destPos++] = elem & 0b1 ? oneMapping : zeroMapping;\n    }\n    if (widthRemainder === 0) {\n      continue;\n    }\n    const elem = srcPos < srcLength ? src[srcPos++] : 255;\n    for (let j = 0; j < widthRemainder; j++) {\n      dest[destPos++] = elem & 1 << 7 - j ? oneMapping : zeroMapping;\n    }\n  }\n  return {\n    srcPos,\n    destPos\n  };\n}\nfunction convertRGBToRGBA({\n  src,\n  srcPos = 0,\n  dest,\n  destPos = 0,\n  width,\n  height\n}) {\n  let i = 0;\n  const len32 = src.length >> 2;\n  const src32 = new Uint32Array(src.buffer, srcPos, len32);\n  if (FeatureTest.isLittleEndian) {\n    for (; i < len32 - 2; i += 3, destPos += 4) {\n      const s1 = src32[i];\n      const s2 = src32[i + 1];\n      const s3 = src32[i + 2];\n      dest[destPos] = s1 | 0xff000000;\n      dest[destPos + 1] = s1 >>> 24 | s2 << 8 | 0xff000000;\n      dest[destPos + 2] = s2 >>> 16 | s3 << 16 | 0xff000000;\n      dest[destPos + 3] = s3 >>> 8 | 0xff000000;\n    }\n    for (let j = i * 4, jj = src.length; j < jj; j += 3) {\n      dest[destPos++] = src[j] | src[j + 1] << 8 | src[j + 2] << 16 | 0xff000000;\n    }\n  } else {\n    for (; i < len32 - 2; i += 3, destPos += 4) {\n      const s1 = src32[i];\n      const s2 = src32[i + 1];\n      const s3 = src32[i + 2];\n      dest[destPos] = s1 | 0xff;\n      dest[destPos + 1] = s1 << 24 | s2 >>> 8 | 0xff;\n      dest[destPos + 2] = s2 << 16 | s3 >>> 16 | 0xff;\n      dest[destPos + 3] = s3 << 8 | 0xff;\n    }\n    for (let j = i * 4, jj = src.length; j < jj; j += 3) {\n      dest[destPos++] = src[j] << 24 | src[j + 1] << 16 | src[j + 2] << 8 | 0xff;\n    }\n  }\n  return {\n    srcPos,\n    destPos\n  };\n}\nfunction grayToRGBA(src, dest) {\n  if (FeatureTest.isLittleEndian) {\n    for (let i = 0, ii = src.length; i < ii; i++) {\n      dest[i] = src[i] * 0x10101 | 0xff000000;\n    }\n  } else {\n    for (let i = 0, ii = src.length; i < ii; i++) {\n      dest[i] = src[i] * 0x1010100 | 0x000000ff;\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/jpg.js\n\n\n\nclass JpegError extends BaseException {\n  constructor(msg) {\n    super(`JPEG error: ${msg}`, \"JpegError\");\n  }\n}\nclass DNLMarkerError extends BaseException {\n  constructor(message, scanLines) {\n    super(message, \"DNLMarkerError\");\n    this.scanLines = scanLines;\n  }\n}\nclass EOIMarkerError extends BaseException {\n  constructor(msg) {\n    super(msg, \"EOIMarkerError\");\n  }\n}\nconst dctZigZag = new Uint8Array([0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63]);\nconst dctCos1 = 4017;\nconst dctSin1 = 799;\nconst dctCos3 = 3406;\nconst dctSin3 = 2276;\nconst dctCos6 = 1567;\nconst dctSin6 = 3784;\nconst dctSqrt2 = 5793;\nconst dctSqrt1d2 = 2896;\nfunction buildHuffmanTable(codeLengths, values) {\n  let k = 0,\n    i,\n    j,\n    length = 16;\n  while (length > 0 && !codeLengths[length - 1]) {\n    length--;\n  }\n  const code = [{\n    children: [],\n    index: 0\n  }];\n  let p = code[0],\n    q;\n  for (i = 0; i < length; i++) {\n    for (j = 0; j < codeLengths[i]; j++) {\n      p = code.pop();\n      p.children[p.index] = values[k];\n      while (p.index > 0) {\n        p = code.pop();\n      }\n      p.index++;\n      code.push(p);\n      while (code.length <= i) {\n        code.push(q = {\n          children: [],\n          index: 0\n        });\n        p.children[p.index] = q.children;\n        p = q;\n      }\n      k++;\n    }\n    if (i + 1 < length) {\n      code.push(q = {\n        children: [],\n        index: 0\n      });\n      p.children[p.index] = q.children;\n      p = q;\n    }\n  }\n  return code[0].children;\n}\nfunction getBlockBufferOffset(component, row, col) {\n  return 64 * ((component.blocksPerLine + 1) * row + col);\n}\nfunction decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successivePrev, successive, parseDNLMarker = false) {\n  const mcusPerLine = frame.mcusPerLine;\n  const progressive = frame.progressive;\n  const startOffset = offset;\n  let bitsData = 0,\n    bitsCount = 0;\n  function readBit() {\n    if (bitsCount > 0) {\n      bitsCount--;\n      return bitsData >> bitsCount & 1;\n    }\n    bitsData = data[offset++];\n    if (bitsData === 0xff) {\n      const nextByte = data[offset++];\n      if (nextByte) {\n        if (nextByte === 0xdc && parseDNLMarker) {\n          offset += 2;\n          const scanLines = readUint16(data, offset);\n          offset += 2;\n          if (scanLines > 0 && scanLines !== frame.scanLines) {\n            throw new DNLMarkerError(\"Found DNL marker (0xFFDC) while parsing scan data\", scanLines);\n          }\n        } else if (nextByte === 0xd9) {\n          if (parseDNLMarker) {\n            const maybeScanLines = blockRow * (frame.precision === 8 ? 8 : 0);\n            if (maybeScanLines > 0 && Math.round(frame.scanLines / maybeScanLines) >= 5) {\n              throw new DNLMarkerError(\"Found EOI marker (0xFFD9) while parsing scan data, \" + \"possibly caused by incorrect `scanLines` parameter\", maybeScanLines);\n            }\n          }\n          throw new EOIMarkerError(\"Found EOI marker (0xFFD9) while parsing scan data\");\n        }\n        throw new JpegError(`unexpected marker ${(bitsData << 8 | nextByte).toString(16)}`);\n      }\n    }\n    bitsCount = 7;\n    return bitsData >>> 7;\n  }\n  function decodeHuffman(tree) {\n    let node = tree;\n    while (true) {\n      node = node[readBit()];\n      switch (typeof node) {\n        case \"number\":\n          return node;\n        case \"object\":\n          continue;\n      }\n      throw new JpegError(\"invalid huffman sequence\");\n    }\n  }\n  function receive(length) {\n    let n = 0;\n    while (length > 0) {\n      n = n << 1 | readBit();\n      length--;\n    }\n    return n;\n  }\n  function receiveAndExtend(length) {\n    if (length === 1) {\n      return readBit() === 1 ? 1 : -1;\n    }\n    const n = receive(length);\n    if (n >= 1 << length - 1) {\n      return n;\n    }\n    return n + (-1 << length) + 1;\n  }\n  function decodeBaseline(component, blockOffset) {\n    const t = decodeHuffman(component.huffmanTableDC);\n    const diff = t === 0 ? 0 : receiveAndExtend(t);\n    component.blockData[blockOffset] = component.pred += diff;\n    let k = 1;\n    while (k < 64) {\n      const rs = decodeHuffman(component.huffmanTableAC);\n      const s = rs & 15,\n        r = rs >> 4;\n      if (s === 0) {\n        if (r < 15) {\n          break;\n        }\n        k += 16;\n        continue;\n      }\n      k += r;\n      const z = dctZigZag[k];\n      component.blockData[blockOffset + z] = receiveAndExtend(s);\n      k++;\n    }\n  }\n  function decodeDCFirst(component, blockOffset) {\n    const t = decodeHuffman(component.huffmanTableDC);\n    const diff = t === 0 ? 0 : receiveAndExtend(t) << successive;\n    component.blockData[blockOffset] = component.pred += diff;\n  }\n  function decodeDCSuccessive(component, blockOffset) {\n    component.blockData[blockOffset] |= readBit() << successive;\n  }\n  let eobrun = 0;\n  function decodeACFirst(component, blockOffset) {\n    if (eobrun > 0) {\n      eobrun--;\n      return;\n    }\n    let k = spectralStart;\n    const e = spectralEnd;\n    while (k <= e) {\n      const rs = decodeHuffman(component.huffmanTableAC);\n      const s = rs & 15,\n        r = rs >> 4;\n      if (s === 0) {\n        if (r < 15) {\n          eobrun = receive(r) + (1 << r) - 1;\n          break;\n        }\n        k += 16;\n        continue;\n      }\n      k += r;\n      const z = dctZigZag[k];\n      component.blockData[blockOffset + z] = receiveAndExtend(s) * (1 << successive);\n      k++;\n    }\n  }\n  let successiveACState = 0,\n    successiveACNextValue;\n  function decodeACSuccessive(component, blockOffset) {\n    let k = spectralStart;\n    const e = spectralEnd;\n    let r = 0;\n    let s;\n    let rs;\n    while (k <= e) {\n      const offsetZ = blockOffset + dctZigZag[k];\n      const sign = component.blockData[offsetZ] < 0 ? -1 : 1;\n      switch (successiveACState) {\n        case 0:\n          rs = decodeHuffman(component.huffmanTableAC);\n          s = rs & 15;\n          r = rs >> 4;\n          if (s === 0) {\n            if (r < 15) {\n              eobrun = receive(r) + (1 << r);\n              successiveACState = 4;\n            } else {\n              r = 16;\n              successiveACState = 1;\n            }\n          } else {\n            if (s !== 1) {\n              throw new JpegError(\"invalid ACn encoding\");\n            }\n            successiveACNextValue = receiveAndExtend(s);\n            successiveACState = r ? 2 : 3;\n          }\n          continue;\n        case 1:\n        case 2:\n          if (component.blockData[offsetZ]) {\n            component.blockData[offsetZ] += sign * (readBit() << successive);\n          } else {\n            r--;\n            if (r === 0) {\n              successiveACState = successiveACState === 2 ? 3 : 0;\n            }\n          }\n          break;\n        case 3:\n          if (component.blockData[offsetZ]) {\n            component.blockData[offsetZ] += sign * (readBit() << successive);\n          } else {\n            component.blockData[offsetZ] = successiveACNextValue << successive;\n            successiveACState = 0;\n          }\n          break;\n        case 4:\n          if (component.blockData[offsetZ]) {\n            component.blockData[offsetZ] += sign * (readBit() << successive);\n          }\n          break;\n      }\n      k++;\n    }\n    if (successiveACState === 4) {\n      eobrun--;\n      if (eobrun === 0) {\n        successiveACState = 0;\n      }\n    }\n  }\n  let blockRow = 0;\n  function decodeMcu(component, decode, mcu, row, col) {\n    const mcuRow = mcu / mcusPerLine | 0;\n    const mcuCol = mcu % mcusPerLine;\n    blockRow = mcuRow * component.v + row;\n    const blockCol = mcuCol * component.h + col;\n    const blockOffset = getBlockBufferOffset(component, blockRow, blockCol);\n    decode(component, blockOffset);\n  }\n  function decodeBlock(component, decode, mcu) {\n    blockRow = mcu / component.blocksPerLine | 0;\n    const blockCol = mcu % component.blocksPerLine;\n    const blockOffset = getBlockBufferOffset(component, blockRow, blockCol);\n    decode(component, blockOffset);\n  }\n  const componentsLength = components.length;\n  let component, i, j, k, n;\n  let decodeFn;\n  if (progressive) {\n    if (spectralStart === 0) {\n      decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;\n    } else {\n      decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;\n    }\n  } else {\n    decodeFn = decodeBaseline;\n  }\n  let mcu = 0,\n    fileMarker;\n  const mcuExpected = componentsLength === 1 ? components[0].blocksPerLine * components[0].blocksPerColumn : mcusPerLine * frame.mcusPerColumn;\n  let h, v;\n  while (mcu <= mcuExpected) {\n    const mcuToRead = resetInterval ? Math.min(mcuExpected - mcu, resetInterval) : mcuExpected;\n    if (mcuToRead > 0) {\n      for (i = 0; i < componentsLength; i++) {\n        components[i].pred = 0;\n      }\n      eobrun = 0;\n      if (componentsLength === 1) {\n        component = components[0];\n        for (n = 0; n < mcuToRead; n++) {\n          decodeBlock(component, decodeFn, mcu);\n          mcu++;\n        }\n      } else {\n        for (n = 0; n < mcuToRead; n++) {\n          for (i = 0; i < componentsLength; i++) {\n            component = components[i];\n            h = component.h;\n            v = component.v;\n            for (j = 0; j < v; j++) {\n              for (k = 0; k < h; k++) {\n                decodeMcu(component, decodeFn, mcu, j, k);\n              }\n            }\n          }\n          mcu++;\n        }\n      }\n    }\n    bitsCount = 0;\n    fileMarker = findNextFileMarker(data, offset);\n    if (!fileMarker) {\n      break;\n    }\n    if (fileMarker.invalid) {\n      const partialMsg = mcuToRead > 0 ? \"unexpected\" : \"excessive\";\n      warn(`decodeScan - ${partialMsg} MCU data, current marker is: ${fileMarker.invalid}`);\n      offset = fileMarker.offset;\n    }\n    if (fileMarker.marker >= 0xffd0 && fileMarker.marker <= 0xffd7) {\n      offset += 2;\n    } else {\n      break;\n    }\n  }\n  return offset - startOffset;\n}\nfunction quantizeAndInverse(component, blockBufferOffset, p) {\n  const qt = component.quantizationTable,\n    blockData = component.blockData;\n  let v0, v1, v2, v3, v4, v5, v6, v7;\n  let p0, p1, p2, p3, p4, p5, p6, p7;\n  let t;\n  if (!qt) {\n    throw new JpegError(\"missing required Quantization Table.\");\n  }\n  for (let row = 0; row < 64; row += 8) {\n    p0 = blockData[blockBufferOffset + row];\n    p1 = blockData[blockBufferOffset + row + 1];\n    p2 = blockData[blockBufferOffset + row + 2];\n    p3 = blockData[blockBufferOffset + row + 3];\n    p4 = blockData[blockBufferOffset + row + 4];\n    p5 = blockData[blockBufferOffset + row + 5];\n    p6 = blockData[blockBufferOffset + row + 6];\n    p7 = blockData[blockBufferOffset + row + 7];\n    p0 *= qt[row];\n    if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {\n      t = dctSqrt2 * p0 + 512 >> 10;\n      p[row] = t;\n      p[row + 1] = t;\n      p[row + 2] = t;\n      p[row + 3] = t;\n      p[row + 4] = t;\n      p[row + 5] = t;\n      p[row + 6] = t;\n      p[row + 7] = t;\n      continue;\n    }\n    p1 *= qt[row + 1];\n    p2 *= qt[row + 2];\n    p3 *= qt[row + 3];\n    p4 *= qt[row + 4];\n    p5 *= qt[row + 5];\n    p6 *= qt[row + 6];\n    p7 *= qt[row + 7];\n    v0 = dctSqrt2 * p0 + 128 >> 8;\n    v1 = dctSqrt2 * p4 + 128 >> 8;\n    v2 = p2;\n    v3 = p6;\n    v4 = dctSqrt1d2 * (p1 - p7) + 128 >> 8;\n    v7 = dctSqrt1d2 * (p1 + p7) + 128 >> 8;\n    v5 = p3 << 4;\n    v6 = p5 << 4;\n    v0 = v0 + v1 + 1 >> 1;\n    v1 = v0 - v1;\n    t = v2 * dctSin6 + v3 * dctCos6 + 128 >> 8;\n    v2 = v2 * dctCos6 - v3 * dctSin6 + 128 >> 8;\n    v3 = t;\n    v4 = v4 + v6 + 1 >> 1;\n    v6 = v4 - v6;\n    v7 = v7 + v5 + 1 >> 1;\n    v5 = v7 - v5;\n    v0 = v0 + v3 + 1 >> 1;\n    v3 = v0 - v3;\n    v1 = v1 + v2 + 1 >> 1;\n    v2 = v1 - v2;\n    t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;\n    v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;\n    v7 = t;\n    t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;\n    v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;\n    v6 = t;\n    p[row] = v0 + v7;\n    p[row + 7] = v0 - v7;\n    p[row + 1] = v1 + v6;\n    p[row + 6] = v1 - v6;\n    p[row + 2] = v2 + v5;\n    p[row + 5] = v2 - v5;\n    p[row + 3] = v3 + v4;\n    p[row + 4] = v3 - v4;\n  }\n  for (let col = 0; col < 8; ++col) {\n    p0 = p[col];\n    p1 = p[col + 8];\n    p2 = p[col + 16];\n    p3 = p[col + 24];\n    p4 = p[col + 32];\n    p5 = p[col + 40];\n    p6 = p[col + 48];\n    p7 = p[col + 56];\n    if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {\n      t = dctSqrt2 * p0 + 8192 >> 14;\n      if (t < -2040) {\n        t = 0;\n      } else if (t >= 2024) {\n        t = 255;\n      } else {\n        t = t + 2056 >> 4;\n      }\n      blockData[blockBufferOffset + col] = t;\n      blockData[blockBufferOffset + col + 8] = t;\n      blockData[blockBufferOffset + col + 16] = t;\n      blockData[blockBufferOffset + col + 24] = t;\n      blockData[blockBufferOffset + col + 32] = t;\n      blockData[blockBufferOffset + col + 40] = t;\n      blockData[blockBufferOffset + col + 48] = t;\n      blockData[blockBufferOffset + col + 56] = t;\n      continue;\n    }\n    v0 = dctSqrt2 * p0 + 2048 >> 12;\n    v1 = dctSqrt2 * p4 + 2048 >> 12;\n    v2 = p2;\n    v3 = p6;\n    v4 = dctSqrt1d2 * (p1 - p7) + 2048 >> 12;\n    v7 = dctSqrt1d2 * (p1 + p7) + 2048 >> 12;\n    v5 = p3;\n    v6 = p5;\n    v0 = (v0 + v1 + 1 >> 1) + 4112;\n    v1 = v0 - v1;\n    t = v2 * dctSin6 + v3 * dctCos6 + 2048 >> 12;\n    v2 = v2 * dctCos6 - v3 * dctSin6 + 2048 >> 12;\n    v3 = t;\n    v4 = v4 + v6 + 1 >> 1;\n    v6 = v4 - v6;\n    v7 = v7 + v5 + 1 >> 1;\n    v5 = v7 - v5;\n    v0 = v0 + v3 + 1 >> 1;\n    v3 = v0 - v3;\n    v1 = v1 + v2 + 1 >> 1;\n    v2 = v1 - v2;\n    t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;\n    v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;\n    v7 = t;\n    t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;\n    v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;\n    v6 = t;\n    p0 = v0 + v7;\n    p7 = v0 - v7;\n    p1 = v1 + v6;\n    p6 = v1 - v6;\n    p2 = v2 + v5;\n    p5 = v2 - v5;\n    p3 = v3 + v4;\n    p4 = v3 - v4;\n    if (p0 < 16) {\n      p0 = 0;\n    } else if (p0 >= 4080) {\n      p0 = 255;\n    } else {\n      p0 >>= 4;\n    }\n    if (p1 < 16) {\n      p1 = 0;\n    } else if (p1 >= 4080) {\n      p1 = 255;\n    } else {\n      p1 >>= 4;\n    }\n    if (p2 < 16) {\n      p2 = 0;\n    } else if (p2 >= 4080) {\n      p2 = 255;\n    } else {\n      p2 >>= 4;\n    }\n    if (p3 < 16) {\n      p3 = 0;\n    } else if (p3 >= 4080) {\n      p3 = 255;\n    } else {\n      p3 >>= 4;\n    }\n    if (p4 < 16) {\n      p4 = 0;\n    } else if (p4 >= 4080) {\n      p4 = 255;\n    } else {\n      p4 >>= 4;\n    }\n    if (p5 < 16) {\n      p5 = 0;\n    } else if (p5 >= 4080) {\n      p5 = 255;\n    } else {\n      p5 >>= 4;\n    }\n    if (p6 < 16) {\n      p6 = 0;\n    } else if (p6 >= 4080) {\n      p6 = 255;\n    } else {\n      p6 >>= 4;\n    }\n    if (p7 < 16) {\n      p7 = 0;\n    } else if (p7 >= 4080) {\n      p7 = 255;\n    } else {\n      p7 >>= 4;\n    }\n    blockData[blockBufferOffset + col] = p0;\n    blockData[blockBufferOffset + col + 8] = p1;\n    blockData[blockBufferOffset + col + 16] = p2;\n    blockData[blockBufferOffset + col + 24] = p3;\n    blockData[blockBufferOffset + col + 32] = p4;\n    blockData[blockBufferOffset + col + 40] = p5;\n    blockData[blockBufferOffset + col + 48] = p6;\n    blockData[blockBufferOffset + col + 56] = p7;\n  }\n}\nfunction buildComponentData(frame, component) {\n  const blocksPerLine = component.blocksPerLine;\n  const blocksPerColumn = component.blocksPerColumn;\n  const computationBuffer = new Int16Array(64);\n  for (let blockRow = 0; blockRow < blocksPerColumn; blockRow++) {\n    for (let blockCol = 0; blockCol < blocksPerLine; blockCol++) {\n      const offset = getBlockBufferOffset(component, blockRow, blockCol);\n      quantizeAndInverse(component, offset, computationBuffer);\n    }\n  }\n  return component.blockData;\n}\nfunction findNextFileMarker(data, currentPos, startPos = currentPos) {\n  const maxPos = data.length - 1;\n  let newPos = startPos < currentPos ? startPos : currentPos;\n  if (currentPos >= maxPos) {\n    return null;\n  }\n  const currentMarker = readUint16(data, currentPos);\n  if (currentMarker >= 0xffc0 && currentMarker <= 0xfffe) {\n    return {\n      invalid: null,\n      marker: currentMarker,\n      offset: currentPos\n    };\n  }\n  let newMarker = readUint16(data, newPos);\n  while (!(newMarker >= 0xffc0 && newMarker <= 0xfffe)) {\n    if (++newPos >= maxPos) {\n      return null;\n    }\n    newMarker = readUint16(data, newPos);\n  }\n  return {\n    invalid: currentMarker.toString(16),\n    marker: newMarker,\n    offset: newPos\n  };\n}\nclass JpegImage {\n  constructor({\n    decodeTransform = null,\n    colorTransform = -1\n  } = {}) {\n    this._decodeTransform = decodeTransform;\n    this._colorTransform = colorTransform;\n  }\n  parse(data, {\n    dnlScanLines = null\n  } = {}) {\n    function readDataBlock() {\n      const length = readUint16(data, offset);\n      offset += 2;\n      let endOffset = offset + length - 2;\n      const fileMarker = findNextFileMarker(data, endOffset, offset);\n      if (fileMarker?.invalid) {\n        warn(\"readDataBlock - incorrect length, current marker is: \" + fileMarker.invalid);\n        endOffset = fileMarker.offset;\n      }\n      const array = data.subarray(offset, endOffset);\n      offset += array.length;\n      return array;\n    }\n    function prepareComponents(frame) {\n      const mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);\n      const mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);\n      for (const component of frame.components) {\n        const blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / frame.maxH);\n        const blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / frame.maxV);\n        const blocksPerLineForMcu = mcusPerLine * component.h;\n        const blocksPerColumnForMcu = mcusPerColumn * component.v;\n        const blocksBufferSize = 64 * blocksPerColumnForMcu * (blocksPerLineForMcu + 1);\n        component.blockData = new Int16Array(blocksBufferSize);\n        component.blocksPerLine = blocksPerLine;\n        component.blocksPerColumn = blocksPerColumn;\n      }\n      frame.mcusPerLine = mcusPerLine;\n      frame.mcusPerColumn = mcusPerColumn;\n    }\n    let offset = 0;\n    let jfif = null;\n    let adobe = null;\n    let frame, resetInterval;\n    let numSOSMarkers = 0;\n    const quantizationTables = [];\n    const huffmanTablesAC = [],\n      huffmanTablesDC = [];\n    let fileMarker = readUint16(data, offset);\n    offset += 2;\n    if (fileMarker !== 0xffd8) {\n      throw new JpegError(\"SOI not found\");\n    }\n    fileMarker = readUint16(data, offset);\n    offset += 2;\n    markerLoop: while (fileMarker !== 0xffd9) {\n      let i, j, l;\n      switch (fileMarker) {\n        case 0xffe0:\n        case 0xffe1:\n        case 0xffe2:\n        case 0xffe3:\n        case 0xffe4:\n        case 0xffe5:\n        case 0xffe6:\n        case 0xffe7:\n        case 0xffe8:\n        case 0xffe9:\n        case 0xffea:\n        case 0xffeb:\n        case 0xffec:\n        case 0xffed:\n        case 0xffee:\n        case 0xffef:\n        case 0xfffe:\n          const appData = readDataBlock();\n          if (fileMarker === 0xffe0) {\n            if (appData[0] === 0x4a && appData[1] === 0x46 && appData[2] === 0x49 && appData[3] === 0x46 && appData[4] === 0) {\n              jfif = {\n                version: {\n                  major: appData[5],\n                  minor: appData[6]\n                },\n                densityUnits: appData[7],\n                xDensity: appData[8] << 8 | appData[9],\n                yDensity: appData[10] << 8 | appData[11],\n                thumbWidth: appData[12],\n                thumbHeight: appData[13],\n                thumbData: appData.subarray(14, 14 + 3 * appData[12] * appData[13])\n              };\n            }\n          }\n          if (fileMarker === 0xffee) {\n            if (appData[0] === 0x41 && appData[1] === 0x64 && appData[2] === 0x6f && appData[3] === 0x62 && appData[4] === 0x65) {\n              adobe = {\n                version: appData[5] << 8 | appData[6],\n                flags0: appData[7] << 8 | appData[8],\n                flags1: appData[9] << 8 | appData[10],\n                transformCode: appData[11]\n              };\n            }\n          }\n          break;\n        case 0xffdb:\n          const quantizationTablesLength = readUint16(data, offset);\n          offset += 2;\n          const quantizationTablesEnd = quantizationTablesLength + offset - 2;\n          let z;\n          while (offset < quantizationTablesEnd) {\n            const quantizationTableSpec = data[offset++];\n            const tableData = new Uint16Array(64);\n            if (quantizationTableSpec >> 4 === 0) {\n              for (j = 0; j < 64; j++) {\n                z = dctZigZag[j];\n                tableData[z] = data[offset++];\n              }\n            } else if (quantizationTableSpec >> 4 === 1) {\n              for (j = 0; j < 64; j++) {\n                z = dctZigZag[j];\n                tableData[z] = readUint16(data, offset);\n                offset += 2;\n              }\n            } else {\n              throw new JpegError(\"DQT - invalid table spec\");\n            }\n            quantizationTables[quantizationTableSpec & 15] = tableData;\n          }\n          break;\n        case 0xffc0:\n        case 0xffc1:\n        case 0xffc2:\n          if (frame) {\n            throw new JpegError(\"Only single frame JPEGs supported\");\n          }\n          offset += 2;\n          frame = {};\n          frame.extended = fileMarker === 0xffc1;\n          frame.progressive = fileMarker === 0xffc2;\n          frame.precision = data[offset++];\n          const sofScanLines = readUint16(data, offset);\n          offset += 2;\n          frame.scanLines = dnlScanLines || sofScanLines;\n          frame.samplesPerLine = readUint16(data, offset);\n          offset += 2;\n          frame.components = [];\n          frame.componentIds = {};\n          const componentsCount = data[offset++];\n          let maxH = 0,\n            maxV = 0;\n          for (i = 0; i < componentsCount; i++) {\n            const componentId = data[offset];\n            const h = data[offset + 1] >> 4;\n            const v = data[offset + 1] & 15;\n            if (maxH < h) {\n              maxH = h;\n            }\n            if (maxV < v) {\n              maxV = v;\n            }\n            const qId = data[offset + 2];\n            l = frame.components.push({\n              h,\n              v,\n              quantizationId: qId,\n              quantizationTable: null\n            });\n            frame.componentIds[componentId] = l - 1;\n            offset += 3;\n          }\n          frame.maxH = maxH;\n          frame.maxV = maxV;\n          prepareComponents(frame);\n          break;\n        case 0xffc4:\n          const huffmanLength = readUint16(data, offset);\n          offset += 2;\n          for (i = 2; i < huffmanLength;) {\n            const huffmanTableSpec = data[offset++];\n            const codeLengths = new Uint8Array(16);\n            let codeLengthSum = 0;\n            for (j = 0; j < 16; j++, offset++) {\n              codeLengthSum += codeLengths[j] = data[offset];\n            }\n            const huffmanValues = new Uint8Array(codeLengthSum);\n            for (j = 0; j < codeLengthSum; j++, offset++) {\n              huffmanValues[j] = data[offset];\n            }\n            i += 17 + codeLengthSum;\n            (huffmanTableSpec >> 4 === 0 ? huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = buildHuffmanTable(codeLengths, huffmanValues);\n          }\n          break;\n        case 0xffdd:\n          offset += 2;\n          resetInterval = readUint16(data, offset);\n          offset += 2;\n          break;\n        case 0xffda:\n          const parseDNLMarker = ++numSOSMarkers === 1 && !dnlScanLines;\n          offset += 2;\n          const selectorsCount = data[offset++],\n            components = [];\n          for (i = 0; i < selectorsCount; i++) {\n            const index = data[offset++];\n            const componentIndex = frame.componentIds[index];\n            const component = frame.components[componentIndex];\n            component.index = index;\n            const tableSpec = data[offset++];\n            component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];\n            component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];\n            components.push(component);\n          }\n          const spectralStart = data[offset++],\n            spectralEnd = data[offset++],\n            successiveApproximation = data[offset++];\n          try {\n            const processed = decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successiveApproximation >> 4, successiveApproximation & 15, parseDNLMarker);\n            offset += processed;\n          } catch (ex) {\n            if (ex instanceof DNLMarkerError) {\n              warn(`${ex.message} -- attempting to re-parse the JPEG image.`);\n              return this.parse(data, {\n                dnlScanLines: ex.scanLines\n              });\n            } else if (ex instanceof EOIMarkerError) {\n              warn(`${ex.message} -- ignoring the rest of the image data.`);\n              break markerLoop;\n            }\n            throw ex;\n          }\n          break;\n        case 0xffdc:\n          offset += 4;\n          break;\n        case 0xffff:\n          if (data[offset] !== 0xff) {\n            offset--;\n          }\n          break;\n        default:\n          const nextFileMarker = findNextFileMarker(data, offset - 2, offset - 3);\n          if (nextFileMarker?.invalid) {\n            warn(\"JpegImage.parse - unexpected data, current marker is: \" + nextFileMarker.invalid);\n            offset = nextFileMarker.offset;\n            break;\n          }\n          if (!nextFileMarker || offset >= data.length - 1) {\n            warn(\"JpegImage.parse - reached the end of the image data \" + \"without finding an EOI marker (0xFFD9).\");\n            break markerLoop;\n          }\n          throw new JpegError(\"JpegImage.parse - unknown marker: \" + fileMarker.toString(16));\n      }\n      fileMarker = readUint16(data, offset);\n      offset += 2;\n    }\n    if (!frame) {\n      throw new JpegError(\"JpegImage.parse - no frame data found.\");\n    }\n    this.width = frame.samplesPerLine;\n    this.height = frame.scanLines;\n    this.jfif = jfif;\n    this.adobe = adobe;\n    this.components = [];\n    for (const component of frame.components) {\n      const quantizationTable = quantizationTables[component.quantizationId];\n      if (quantizationTable) {\n        component.quantizationTable = quantizationTable;\n      }\n      this.components.push({\n        index: component.index,\n        output: buildComponentData(frame, component),\n        scaleX: component.h / frame.maxH,\n        scaleY: component.v / frame.maxV,\n        blocksPerLine: component.blocksPerLine,\n        blocksPerColumn: component.blocksPerColumn\n      });\n    }\n    this.numComponents = this.components.length;\n    return undefined;\n  }\n  _getLinearizedBlockData(width, height, isSourcePDF = false) {\n    const scaleX = this.width / width,\n      scaleY = this.height / height;\n    let component, componentScaleX, componentScaleY, blocksPerScanline;\n    let x, y, i, j, k;\n    let index;\n    let offset = 0;\n    let output;\n    const numComponents = this.components.length;\n    const dataLength = width * height * numComponents;\n    const data = new Uint8ClampedArray(dataLength);\n    const xScaleBlockOffset = new Uint32Array(width);\n    const mask3LSB = 0xfffffff8;\n    let lastComponentScaleX;\n    for (i = 0; i < numComponents; i++) {\n      component = this.components[i];\n      componentScaleX = component.scaleX * scaleX;\n      componentScaleY = component.scaleY * scaleY;\n      offset = i;\n      output = component.output;\n      blocksPerScanline = component.blocksPerLine + 1 << 3;\n      if (componentScaleX !== lastComponentScaleX) {\n        for (x = 0; x < width; x++) {\n          j = 0 | x * componentScaleX;\n          xScaleBlockOffset[x] = (j & mask3LSB) << 3 | j & 7;\n        }\n        lastComponentScaleX = componentScaleX;\n      }\n      for (y = 0; y < height; y++) {\n        j = 0 | y * componentScaleY;\n        index = blocksPerScanline * (j & mask3LSB) | (j & 7) << 3;\n        for (x = 0; x < width; x++) {\n          data[offset] = output[index + xScaleBlockOffset[x]];\n          offset += numComponents;\n        }\n      }\n    }\n    let transform = this._decodeTransform;\n    if (!isSourcePDF && numComponents === 4 && !transform) {\n      transform = new Int32Array([-256, 255, -256, 255, -256, 255, -256, 255]);\n    }\n    if (transform) {\n      for (i = 0; i < dataLength;) {\n        for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) {\n          data[i] = (data[i] * transform[k] >> 8) + transform[k + 1];\n        }\n      }\n    }\n    return data;\n  }\n  get _isColorConversionNeeded() {\n    if (this.adobe) {\n      return !!this.adobe.transformCode;\n    }\n    if (this.numComponents === 3) {\n      if (this._colorTransform === 0) {\n        return false;\n      } else if (this.components[0].index === 0x52 && this.components[1].index === 0x47 && this.components[2].index === 0x42) {\n        return false;\n      }\n      return true;\n    }\n    if (this._colorTransform === 1) {\n      return true;\n    }\n    return false;\n  }\n  _convertYccToRgb(data) {\n    let Y, Cb, Cr;\n    for (let i = 0, length = data.length; i < length; i += 3) {\n      Y = data[i];\n      Cb = data[i + 1];\n      Cr = data[i + 2];\n      data[i] = Y - 179.456 + 1.402 * Cr;\n      data[i + 1] = Y + 135.459 - 0.344 * Cb - 0.714 * Cr;\n      data[i + 2] = Y - 226.816 + 1.772 * Cb;\n    }\n    return data;\n  }\n  _convertYccToRgba(data, out) {\n    for (let i = 0, j = 0, length = data.length; i < length; i += 3, j += 4) {\n      const Y = data[i];\n      const Cb = data[i + 1];\n      const Cr = data[i + 2];\n      out[j] = Y - 179.456 + 1.402 * Cr;\n      out[j + 1] = Y + 135.459 - 0.344 * Cb - 0.714 * Cr;\n      out[j + 2] = Y - 226.816 + 1.772 * Cb;\n      out[j + 3] = 255;\n    }\n    return out;\n  }\n  _convertYcckToRgb(data) {\n    let Y, Cb, Cr, k;\n    let offset = 0;\n    for (let i = 0, length = data.length; i < length; i += 4) {\n      Y = data[i];\n      Cb = data[i + 1];\n      Cr = data[i + 2];\n      k = data[i + 3];\n      data[offset++] = -122.67195406894 + Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr - 5.4080610064599e-5 * Y + 0.00048449797120281 * k - 0.154362151871126) + Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y - 0.00477271405408747 * k + 1.53380253221734) + Y * (0.000961250184130688 * Y - 0.00266257332283933 * k + 0.48357088451265) + k * (-0.000336197177618394 * k + 0.484791561490776);\n      data[offset++] = 107.268039397724 + Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr + 0.000659397001245577 * Y + 0.000426105652938837 * k - 0.176491792462875) + Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y + 0.000770482631801132 * k - 0.151051492775562) + Y * (0.00126935368114843 * Y - 0.00265090189010898 * k + 0.25802910206845) + k * (-0.000318913117588328 * k - 0.213742400323665);\n      data[offset++] = -20.810012546947 + Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr + 0.0020741088115012 * Y - 0.00288260236853442 * k + 0.814272968359295) + Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y + 0.000560833691242812 * k - 0.195152027534049) + Y * (0.00174418132927582 * Y - 0.00255243321439347 * k + 0.116935020465145) + k * (-0.000343531996510555 * k + 0.24165260232407);\n    }\n    return data.subarray(0, offset);\n  }\n  _convertYcckToRgba(data) {\n    for (let i = 0, length = data.length; i < length; i += 4) {\n      const Y = data[i];\n      const Cb = data[i + 1];\n      const Cr = data[i + 2];\n      const k = data[i + 3];\n      data[i] = -122.67195406894 + Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr - 5.4080610064599e-5 * Y + 0.00048449797120281 * k - 0.154362151871126) + Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y - 0.00477271405408747 * k + 1.53380253221734) + Y * (0.000961250184130688 * Y - 0.00266257332283933 * k + 0.48357088451265) + k * (-0.000336197177618394 * k + 0.484791561490776);\n      data[i + 1] = 107.268039397724 + Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr + 0.000659397001245577 * Y + 0.000426105652938837 * k - 0.176491792462875) + Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y + 0.000770482631801132 * k - 0.151051492775562) + Y * (0.00126935368114843 * Y - 0.00265090189010898 * k + 0.25802910206845) + k * (-0.000318913117588328 * k - 0.213742400323665);\n      data[i + 2] = -20.810012546947 + Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr + 0.0020741088115012 * Y - 0.00288260236853442 * k + 0.814272968359295) + Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y + 0.000560833691242812 * k - 0.195152027534049) + Y * (0.00174418132927582 * Y - 0.00255243321439347 * k + 0.116935020465145) + k * (-0.000343531996510555 * k + 0.24165260232407);\n      data[i + 3] = 255;\n    }\n    return data;\n  }\n  _convertYcckToCmyk(data) {\n    let Y, Cb, Cr;\n    for (let i = 0, length = data.length; i < length; i += 4) {\n      Y = data[i];\n      Cb = data[i + 1];\n      Cr = data[i + 2];\n      data[i] = 434.456 - Y - 1.402 * Cr;\n      data[i + 1] = 119.541 - Y + 0.344 * Cb + 0.714 * Cr;\n      data[i + 2] = 481.816 - Y - 1.772 * Cb;\n    }\n    return data;\n  }\n  _convertCmykToRgb(data) {\n    let c, m, y, k;\n    let offset = 0;\n    for (let i = 0, length = data.length; i < length; i += 4) {\n      c = data[i];\n      m = data[i + 1];\n      y = data[i + 2];\n      k = data[i + 3];\n      data[offset++] = 255 + c * (-0.00006747147073602441 * c + 0.0008379262121013727 * m + 0.0002894718188643294 * y + 0.003264231057537806 * k - 1.1185611867203937) + m * (0.000026374107616089405 * m - 0.00008626949158638572 * y - 0.0002748769067499491 * k - 0.02155688794978967) + y * (-0.00003878099212869363 * y - 0.0003267808279485286 * k + 0.0686742238595345) - k * (0.0003361971776183937 * k + 0.7430659151342254);\n      data[offset++] = 255 + c * (0.00013596372813588848 * c + 0.000924537132573585 * m + 0.00010567359618683593 * y + 0.0004791864687436512 * k - 0.3109689587515875) + m * (-0.00023545346108370344 * m + 0.0002702845253534714 * y + 0.0020200308977307156 * k - 0.7488052167015494) + y * (0.00006834815998235662 * y + 0.00015168452363460973 * k - 0.09751927774728933) - k * (0.0003189131175883281 * k + 0.7364883807733168);\n      data[offset++] = 255 + c * (0.000013598650411385307 * c + 0.00012423956175490851 * m + 0.0004751985097583589 * y - 0.0000036729317476630422 * k - 0.05562186980264034) + m * (0.00016141380598724676 * m + 0.0009692239130725186 * y + 0.0007782692450036253 * k - 0.44015232367526463) + y * (5.068882914068769e-7 * y + 0.0017778369011375071 * k - 0.7591454649749609) - k * (0.0003435319965105553 * k + 0.7063770186160144);\n    }\n    return data.subarray(0, offset);\n  }\n  _convertCmykToRgba(data) {\n    for (let i = 0, length = data.length; i < length; i += 4) {\n      const c = data[i];\n      const m = data[i + 1];\n      const y = data[i + 2];\n      const k = data[i + 3];\n      data[i] = 255 + c * (-0.00006747147073602441 * c + 0.0008379262121013727 * m + 0.0002894718188643294 * y + 0.003264231057537806 * k - 1.1185611867203937) + m * (0.000026374107616089405 * m - 0.00008626949158638572 * y - 0.0002748769067499491 * k - 0.02155688794978967) + y * (-0.00003878099212869363 * y - 0.0003267808279485286 * k + 0.0686742238595345) - k * (0.0003361971776183937 * k + 0.7430659151342254);\n      data[i + 1] = 255 + c * (0.00013596372813588848 * c + 0.000924537132573585 * m + 0.00010567359618683593 * y + 0.0004791864687436512 * k - 0.3109689587515875) + m * (-0.00023545346108370344 * m + 0.0002702845253534714 * y + 0.0020200308977307156 * k - 0.7488052167015494) + y * (0.00006834815998235662 * y + 0.00015168452363460973 * k - 0.09751927774728933) - k * (0.0003189131175883281 * k + 0.7364883807733168);\n      data[i + 2] = 255 + c * (0.000013598650411385307 * c + 0.00012423956175490851 * m + 0.0004751985097583589 * y - 0.0000036729317476630422 * k - 0.05562186980264034) + m * (0.00016141380598724676 * m + 0.0009692239130725186 * y + 0.0007782692450036253 * k - 0.44015232367526463) + y * (5.068882914068769e-7 * y + 0.0017778369011375071 * k - 0.7591454649749609) - k * (0.0003435319965105553 * k + 0.7063770186160144);\n      data[i + 3] = 255;\n    }\n    return data;\n  }\n  getData({\n    width,\n    height,\n    forceRGBA = false,\n    forceRGB = false,\n    isSourcePDF = false\n  }) {\n    if (this.numComponents > 4) {\n      throw new JpegError(\"Unsupported color mode\");\n    }\n    const data = this._getLinearizedBlockData(width, height, isSourcePDF);\n    if (this.numComponents === 1 && (forceRGBA || forceRGB)) {\n      const len = data.length * (forceRGBA ? 4 : 3);\n      const rgbaData = new Uint8ClampedArray(len);\n      let offset = 0;\n      if (forceRGBA) {\n        grayToRGBA(data, new Uint32Array(rgbaData.buffer));\n      } else {\n        for (const grayColor of data) {\n          rgbaData[offset++] = grayColor;\n          rgbaData[offset++] = grayColor;\n          rgbaData[offset++] = grayColor;\n        }\n      }\n      return rgbaData;\n    } else if (this.numComponents === 3 && this._isColorConversionNeeded) {\n      if (forceRGBA) {\n        const rgbaData = new Uint8ClampedArray(data.length / 3 * 4);\n        return this._convertYccToRgba(data, rgbaData);\n      }\n      return this._convertYccToRgb(data);\n    } else if (this.numComponents === 4) {\n      if (this._isColorConversionNeeded) {\n        if (forceRGBA) {\n          return this._convertYcckToRgba(data);\n        }\n        if (forceRGB) {\n          return this._convertYcckToRgb(data);\n        }\n        return this._convertYcckToCmyk(data);\n      } else if (forceRGBA) {\n        return this._convertCmykToRgba(data);\n      } else if (forceRGB) {\n        return this._convertCmykToRgb(data);\n      }\n    }\n    return data;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/jpeg_stream.js\n\n\n\n\nclass JpegStream extends DecodeStream {\n  constructor(stream, maybeLength, params) {\n    let ch;\n    while ((ch = stream.getByte()) !== -1) {\n      if (ch === 0xff) {\n        stream.skip(-1);\n        break;\n      }\n    }\n    super(maybeLength);\n    this.stream = stream;\n    this.dict = stream.dict;\n    this.maybeLength = maybeLength;\n    this.params = params;\n  }\n  get bytes() {\n    return shadow(this, \"bytes\", this.stream.getBytes(this.maybeLength));\n  }\n  ensureBuffer(requested) {}\n  readBlock() {\n    if (this.eof) {\n      return;\n    }\n    const jpegOptions = {\n      decodeTransform: undefined,\n      colorTransform: undefined\n    };\n    const decodeArr = this.dict.getArray(\"D\", \"Decode\");\n    if ((this.forceRGBA || this.forceRGB) && Array.isArray(decodeArr)) {\n      const bitsPerComponent = this.dict.get(\"BPC\", \"BitsPerComponent\") || 8;\n      const decodeArrLength = decodeArr.length;\n      const transform = new Int32Array(decodeArrLength);\n      let transformNeeded = false;\n      const maxValue = (1 << bitsPerComponent) - 1;\n      for (let i = 0; i < decodeArrLength; i += 2) {\n        transform[i] = (decodeArr[i + 1] - decodeArr[i]) * 256 | 0;\n        transform[i + 1] = decodeArr[i] * maxValue | 0;\n        if (transform[i] !== 256 || transform[i + 1] !== 0) {\n          transformNeeded = true;\n        }\n      }\n      if (transformNeeded) {\n        jpegOptions.decodeTransform = transform;\n      }\n    }\n    if (this.params instanceof Dict) {\n      const colorTransform = this.params.get(\"ColorTransform\");\n      if (Number.isInteger(colorTransform)) {\n        jpegOptions.colorTransform = colorTransform;\n      }\n    }\n    const jpegImage = new JpegImage(jpegOptions);\n    jpegImage.parse(this.bytes);\n    const data = jpegImage.getData({\n      width: this.drawWidth,\n      height: this.drawHeight,\n      forceRGBA: this.forceRGBA,\n      forceRGB: this.forceRGB,\n      isSourcePDF: true\n    });\n    this.buffer = data;\n    this.bufferLength = data.length;\n    this.eof = true;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/jpx.js\n\n\n\nclass JpxError extends BaseException {\n  constructor(msg) {\n    super(`JPX error: ${msg}`, \"JpxError\");\n  }\n}\nconst SubbandsGainLog2 = {\n  LL: 0,\n  LH: 1,\n  HL: 1,\n  HH: 2\n};\nclass JpxImage {\n  constructor() {\n    this.failOnCorruptedImage = false;\n  }\n  parse(data) {\n    const head = readUint16(data, 0);\n    if (head === 0xff4f) {\n      this.parseCodestream(data, 0, data.length);\n      return;\n    }\n    const length = data.length;\n    let position = 0;\n    while (position < length) {\n      let headerSize = 8;\n      let lbox = readUint32(data, position);\n      const tbox = readUint32(data, position + 4);\n      position += headerSize;\n      if (lbox === 1) {\n        lbox = readUint32(data, position) * 4294967296 + readUint32(data, position + 4);\n        position += 8;\n        headerSize += 8;\n      }\n      if (lbox === 0) {\n        lbox = length - position + headerSize;\n      }\n      if (lbox < headerSize) {\n        throw new JpxError(\"Invalid box field size\");\n      }\n      const dataLength = lbox - headerSize;\n      let jumpDataLength = true;\n      switch (tbox) {\n        case 0x6a703268:\n          jumpDataLength = false;\n          break;\n        case 0x636f6c72:\n          const method = data[position];\n          if (method === 1) {\n            const colorspace = readUint32(data, position + 3);\n            switch (colorspace) {\n              case 16:\n              case 17:\n              case 18:\n                break;\n              default:\n                warn(\"Unknown colorspace \" + colorspace);\n                break;\n            }\n          } else if (method === 2) {\n            info(\"ICC profile not supported\");\n          }\n          break;\n        case 0x6a703263:\n          this.parseCodestream(data, position, position + dataLength);\n          break;\n        case 0x6a502020:\n          if (readUint32(data, position) !== 0x0d0a870a) {\n            warn(\"Invalid JP2 signature\");\n          }\n          break;\n        case 0x6a501a1a:\n        case 0x66747970:\n        case 0x72726571:\n        case 0x72657320:\n        case 0x69686472:\n          break;\n        default:\n          const headerType = String.fromCharCode(tbox >> 24 & 0xff, tbox >> 16 & 0xff, tbox >> 8 & 0xff, tbox & 0xff);\n          warn(`Unsupported header type ${tbox} (${headerType}).`);\n          break;\n      }\n      if (jumpDataLength) {\n        position += dataLength;\n      }\n    }\n  }\n  parseImageProperties(stream) {\n    let newByte = stream.getByte();\n    while (newByte >= 0) {\n      const oldByte = newByte;\n      newByte = stream.getByte();\n      const code = oldByte << 8 | newByte;\n      if (code === 0xff51) {\n        stream.skip(4);\n        const Xsiz = stream.getInt32() >>> 0;\n        const Ysiz = stream.getInt32() >>> 0;\n        const XOsiz = stream.getInt32() >>> 0;\n        const YOsiz = stream.getInt32() >>> 0;\n        stream.skip(16);\n        const Csiz = stream.getUint16();\n        this.width = Xsiz - XOsiz;\n        this.height = Ysiz - YOsiz;\n        this.componentsCount = Csiz;\n        this.bitsPerComponent = 8;\n        return;\n      }\n    }\n    throw new JpxError(\"No size marker found in JPX stream\");\n  }\n  parseCodestream(data, start, end) {\n    const context = {};\n    let doNotRecover = false;\n    try {\n      let position = start;\n      while (position + 1 < end) {\n        const code = readUint16(data, position);\n        position += 2;\n        let length = 0,\n          j,\n          sqcd,\n          spqcds,\n          spqcdSize,\n          scalarExpounded,\n          tile;\n        switch (code) {\n          case 0xff4f:\n            context.mainHeader = true;\n            break;\n          case 0xffd9:\n            break;\n          case 0xff51:\n            length = readUint16(data, position);\n            const siz = {};\n            siz.Xsiz = readUint32(data, position + 4);\n            siz.Ysiz = readUint32(data, position + 8);\n            siz.XOsiz = readUint32(data, position + 12);\n            siz.YOsiz = readUint32(data, position + 16);\n            siz.XTsiz = readUint32(data, position + 20);\n            siz.YTsiz = readUint32(data, position + 24);\n            siz.XTOsiz = readUint32(data, position + 28);\n            siz.YTOsiz = readUint32(data, position + 32);\n            const componentsCount = readUint16(data, position + 36);\n            siz.Csiz = componentsCount;\n            const components = [];\n            j = position + 38;\n            for (let i = 0; i < componentsCount; i++) {\n              const component = {\n                precision: (data[j] & 0x7f) + 1,\n                isSigned: !!(data[j] & 0x80),\n                XRsiz: data[j + 1],\n                YRsiz: data[j + 2]\n              };\n              j += 3;\n              calculateComponentDimensions(component, siz);\n              components.push(component);\n            }\n            context.SIZ = siz;\n            context.components = components;\n            calculateTileGrids(context, components);\n            context.QCC = [];\n            context.COC = [];\n            break;\n          case 0xff5c:\n            length = readUint16(data, position);\n            const qcd = {};\n            j = position + 2;\n            sqcd = data[j++];\n            switch (sqcd & 0x1f) {\n              case 0:\n                spqcdSize = 8;\n                scalarExpounded = true;\n                break;\n              case 1:\n                spqcdSize = 16;\n                scalarExpounded = false;\n                break;\n              case 2:\n                spqcdSize = 16;\n                scalarExpounded = true;\n                break;\n              default:\n                throw new Error(\"Invalid SQcd value \" + sqcd);\n            }\n            qcd.noQuantization = spqcdSize === 8;\n            qcd.scalarExpounded = scalarExpounded;\n            qcd.guardBits = sqcd >> 5;\n            spqcds = [];\n            while (j < length + position) {\n              const spqcd = {};\n              if (spqcdSize === 8) {\n                spqcd.epsilon = data[j++] >> 3;\n                spqcd.mu = 0;\n              } else {\n                spqcd.epsilon = data[j] >> 3;\n                spqcd.mu = (data[j] & 0x7) << 8 | data[j + 1];\n                j += 2;\n              }\n              spqcds.push(spqcd);\n            }\n            qcd.SPqcds = spqcds;\n            if (context.mainHeader) {\n              context.QCD = qcd;\n            } else {\n              context.currentTile.QCD = qcd;\n              context.currentTile.QCC = [];\n            }\n            break;\n          case 0xff5d:\n            length = readUint16(data, position);\n            const qcc = {};\n            j = position + 2;\n            let cqcc;\n            if (context.SIZ.Csiz < 257) {\n              cqcc = data[j++];\n            } else {\n              cqcc = readUint16(data, j);\n              j += 2;\n            }\n            sqcd = data[j++];\n            switch (sqcd & 0x1f) {\n              case 0:\n                spqcdSize = 8;\n                scalarExpounded = true;\n                break;\n              case 1:\n                spqcdSize = 16;\n                scalarExpounded = false;\n                break;\n              case 2:\n                spqcdSize = 16;\n                scalarExpounded = true;\n                break;\n              default:\n                throw new Error(\"Invalid SQcd value \" + sqcd);\n            }\n            qcc.noQuantization = spqcdSize === 8;\n            qcc.scalarExpounded = scalarExpounded;\n            qcc.guardBits = sqcd >> 5;\n            spqcds = [];\n            while (j < length + position) {\n              const spqcd = {};\n              if (spqcdSize === 8) {\n                spqcd.epsilon = data[j++] >> 3;\n                spqcd.mu = 0;\n              } else {\n                spqcd.epsilon = data[j] >> 3;\n                spqcd.mu = (data[j] & 0x7) << 8 | data[j + 1];\n                j += 2;\n              }\n              spqcds.push(spqcd);\n            }\n            qcc.SPqcds = spqcds;\n            if (context.mainHeader) {\n              context.QCC[cqcc] = qcc;\n            } else {\n              context.currentTile.QCC[cqcc] = qcc;\n            }\n            break;\n          case 0xff52:\n            length = readUint16(data, position);\n            const cod = {};\n            j = position + 2;\n            const scod = data[j++];\n            cod.entropyCoderWithCustomPrecincts = !!(scod & 1);\n            cod.sopMarkerUsed = !!(scod & 2);\n            cod.ephMarkerUsed = !!(scod & 4);\n            cod.progressionOrder = data[j++];\n            cod.layersCount = readUint16(data, j);\n            j += 2;\n            cod.multipleComponentTransform = data[j++];\n            cod.decompositionLevelsCount = data[j++];\n            cod.xcb = (data[j++] & 0xf) + 2;\n            cod.ycb = (data[j++] & 0xf) + 2;\n            const blockStyle = data[j++];\n            cod.selectiveArithmeticCodingBypass = !!(blockStyle & 1);\n            cod.resetContextProbabilities = !!(blockStyle & 2);\n            cod.terminationOnEachCodingPass = !!(blockStyle & 4);\n            cod.verticallyStripe = !!(blockStyle & 8);\n            cod.predictableTermination = !!(blockStyle & 16);\n            cod.segmentationSymbolUsed = !!(blockStyle & 32);\n            cod.reversibleTransformation = data[j++];\n            if (cod.entropyCoderWithCustomPrecincts) {\n              const precinctsSizes = [];\n              while (j < length + position) {\n                const precinctsSize = data[j++];\n                precinctsSizes.push({\n                  PPx: precinctsSize & 0xf,\n                  PPy: precinctsSize >> 4\n                });\n              }\n              cod.precinctsSizes = precinctsSizes;\n            }\n            const unsupported = [];\n            if (cod.selectiveArithmeticCodingBypass) {\n              unsupported.push(\"selectiveArithmeticCodingBypass\");\n            }\n            if (cod.terminationOnEachCodingPass) {\n              unsupported.push(\"terminationOnEachCodingPass\");\n            }\n            if (cod.verticallyStripe) {\n              unsupported.push(\"verticallyStripe\");\n            }\n            if (cod.predictableTermination) {\n              unsupported.push(\"predictableTermination\");\n            }\n            if (unsupported.length > 0) {\n              doNotRecover = true;\n              warn(`JPX: Unsupported COD options (${unsupported.join(\", \")}).`);\n            }\n            if (context.mainHeader) {\n              context.COD = cod;\n            } else {\n              context.currentTile.COD = cod;\n              context.currentTile.COC = [];\n            }\n            break;\n          case 0xff90:\n            length = readUint16(data, position);\n            tile = {};\n            tile.index = readUint16(data, position + 2);\n            tile.length = readUint32(data, position + 4);\n            tile.dataEnd = tile.length + position - 2;\n            tile.partIndex = data[position + 8];\n            tile.partsCount = data[position + 9];\n            context.mainHeader = false;\n            if (tile.partIndex === 0) {\n              tile.COD = context.COD;\n              tile.COC = context.COC.slice(0);\n              tile.QCD = context.QCD;\n              tile.QCC = context.QCC.slice(0);\n            }\n            context.currentTile = tile;\n            break;\n          case 0xff93:\n            tile = context.currentTile;\n            if (tile.partIndex === 0) {\n              initializeTile(context, tile.index);\n              buildPackets(context);\n            }\n            length = tile.dataEnd - position;\n            parseTilePackets(context, data, position, length);\n            break;\n          case 0xff53:\n            warn(\"JPX: Codestream code 0xFF53 (COC) is not implemented.\");\n          case 0xff55:\n          case 0xff57:\n          case 0xff58:\n          case 0xff64:\n            length = readUint16(data, position);\n            break;\n          default:\n            throw new Error(\"Unknown codestream code: \" + code.toString(16));\n        }\n        position += length;\n      }\n    } catch (e) {\n      if (doNotRecover || this.failOnCorruptedImage) {\n        throw new JpxError(e.message);\n      } else {\n        warn(`JPX: Trying to recover from: \"${e.message}\".`);\n      }\n    }\n    this.tiles = transformComponents(context);\n    this.width = context.SIZ.Xsiz - context.SIZ.XOsiz;\n    this.height = context.SIZ.Ysiz - context.SIZ.YOsiz;\n    this.componentsCount = context.SIZ.Csiz;\n  }\n}\nfunction calculateComponentDimensions(component, siz) {\n  component.x0 = Math.ceil(siz.XOsiz / component.XRsiz);\n  component.x1 = Math.ceil(siz.Xsiz / component.XRsiz);\n  component.y0 = Math.ceil(siz.YOsiz / component.YRsiz);\n  component.y1 = Math.ceil(siz.Ysiz / component.YRsiz);\n  component.width = component.x1 - component.x0;\n  component.height = component.y1 - component.y0;\n}\nfunction calculateTileGrids(context, components) {\n  const siz = context.SIZ;\n  const tiles = [];\n  let tile;\n  const numXtiles = Math.ceil((siz.Xsiz - siz.XTOsiz) / siz.XTsiz);\n  const numYtiles = Math.ceil((siz.Ysiz - siz.YTOsiz) / siz.YTsiz);\n  for (let q = 0; q < numYtiles; q++) {\n    for (let p = 0; p < numXtiles; p++) {\n      tile = {};\n      tile.tx0 = Math.max(siz.XTOsiz + p * siz.XTsiz, siz.XOsiz);\n      tile.ty0 = Math.max(siz.YTOsiz + q * siz.YTsiz, siz.YOsiz);\n      tile.tx1 = Math.min(siz.XTOsiz + (p + 1) * siz.XTsiz, siz.Xsiz);\n      tile.ty1 = Math.min(siz.YTOsiz + (q + 1) * siz.YTsiz, siz.Ysiz);\n      tile.width = tile.tx1 - tile.tx0;\n      tile.height = tile.ty1 - tile.ty0;\n      tile.components = [];\n      tiles.push(tile);\n    }\n  }\n  context.tiles = tiles;\n  const componentsCount = siz.Csiz;\n  for (let i = 0, ii = componentsCount; i < ii; i++) {\n    const component = components[i];\n    for (let j = 0, jj = tiles.length; j < jj; j++) {\n      const tileComponent = {};\n      tile = tiles[j];\n      tileComponent.tcx0 = Math.ceil(tile.tx0 / component.XRsiz);\n      tileComponent.tcy0 = Math.ceil(tile.ty0 / component.YRsiz);\n      tileComponent.tcx1 = Math.ceil(tile.tx1 / component.XRsiz);\n      tileComponent.tcy1 = Math.ceil(tile.ty1 / component.YRsiz);\n      tileComponent.width = tileComponent.tcx1 - tileComponent.tcx0;\n      tileComponent.height = tileComponent.tcy1 - tileComponent.tcy0;\n      tile.components[i] = tileComponent;\n    }\n  }\n}\nfunction getBlocksDimensions(context, component, r) {\n  const codOrCoc = component.codingStyleParameters;\n  const result = {};\n  if (!codOrCoc.entropyCoderWithCustomPrecincts) {\n    result.PPx = 15;\n    result.PPy = 15;\n  } else {\n    result.PPx = codOrCoc.precinctsSizes[r].PPx;\n    result.PPy = codOrCoc.precinctsSizes[r].PPy;\n  }\n  result.xcb_ = r > 0 ? Math.min(codOrCoc.xcb, result.PPx - 1) : Math.min(codOrCoc.xcb, result.PPx);\n  result.ycb_ = r > 0 ? Math.min(codOrCoc.ycb, result.PPy - 1) : Math.min(codOrCoc.ycb, result.PPy);\n  return result;\n}\nfunction buildPrecincts(context, resolution, dimensions) {\n  const precinctWidth = 1 << dimensions.PPx;\n  const precinctHeight = 1 << dimensions.PPy;\n  const isZeroRes = resolution.resLevel === 0;\n  const precinctWidthInSubband = 1 << dimensions.PPx + (isZeroRes ? 0 : -1);\n  const precinctHeightInSubband = 1 << dimensions.PPy + (isZeroRes ? 0 : -1);\n  const numprecinctswide = resolution.trx1 > resolution.trx0 ? Math.ceil(resolution.trx1 / precinctWidth) - Math.floor(resolution.trx0 / precinctWidth) : 0;\n  const numprecinctshigh = resolution.try1 > resolution.try0 ? Math.ceil(resolution.try1 / precinctHeight) - Math.floor(resolution.try0 / precinctHeight) : 0;\n  const numprecincts = numprecinctswide * numprecinctshigh;\n  resolution.precinctParameters = {\n    precinctWidth,\n    precinctHeight,\n    numprecinctswide,\n    numprecinctshigh,\n    numprecincts,\n    precinctWidthInSubband,\n    precinctHeightInSubband\n  };\n}\nfunction buildCodeblocks(context, subband, dimensions) {\n  const xcb_ = dimensions.xcb_;\n  const ycb_ = dimensions.ycb_;\n  const codeblockWidth = 1 << xcb_;\n  const codeblockHeight = 1 << ycb_;\n  const cbx0 = subband.tbx0 >> xcb_;\n  const cby0 = subband.tby0 >> ycb_;\n  const cbx1 = subband.tbx1 + codeblockWidth - 1 >> xcb_;\n  const cby1 = subband.tby1 + codeblockHeight - 1 >> ycb_;\n  const precinctParameters = subband.resolution.precinctParameters;\n  const codeblocks = [];\n  const precincts = [];\n  let i, j, codeblock, precinctNumber;\n  for (j = cby0; j < cby1; j++) {\n    for (i = cbx0; i < cbx1; i++) {\n      codeblock = {\n        cbx: i,\n        cby: j,\n        tbx0: codeblockWidth * i,\n        tby0: codeblockHeight * j,\n        tbx1: codeblockWidth * (i + 1),\n        tby1: codeblockHeight * (j + 1)\n      };\n      codeblock.tbx0_ = Math.max(subband.tbx0, codeblock.tbx0);\n      codeblock.tby0_ = Math.max(subband.tby0, codeblock.tby0);\n      codeblock.tbx1_ = Math.min(subband.tbx1, codeblock.tbx1);\n      codeblock.tby1_ = Math.min(subband.tby1, codeblock.tby1);\n      const pi = Math.floor((codeblock.tbx0_ - subband.tbx0) / precinctParameters.precinctWidthInSubband);\n      const pj = Math.floor((codeblock.tby0_ - subband.tby0) / precinctParameters.precinctHeightInSubband);\n      precinctNumber = pi + pj * precinctParameters.numprecinctswide;\n      codeblock.precinctNumber = precinctNumber;\n      codeblock.subbandType = subband.type;\n      codeblock.Lblock = 3;\n      if (codeblock.tbx1_ <= codeblock.tbx0_ || codeblock.tby1_ <= codeblock.tby0_) {\n        continue;\n      }\n      codeblocks.push(codeblock);\n      let precinct = precincts[precinctNumber];\n      if (precinct !== undefined) {\n        if (i < precinct.cbxMin) {\n          precinct.cbxMin = i;\n        } else if (i > precinct.cbxMax) {\n          precinct.cbxMax = i;\n        }\n        if (j < precinct.cbyMin) {\n          precinct.cbxMin = j;\n        } else if (j > precinct.cbyMax) {\n          precinct.cbyMax = j;\n        }\n      } else {\n        precincts[precinctNumber] = precinct = {\n          cbxMin: i,\n          cbyMin: j,\n          cbxMax: i,\n          cbyMax: j\n        };\n      }\n      codeblock.precinct = precinct;\n    }\n  }\n  subband.codeblockParameters = {\n    codeblockWidth: xcb_,\n    codeblockHeight: ycb_,\n    numcodeblockwide: cbx1 - cbx0 + 1,\n    numcodeblockhigh: cby1 - cby0 + 1\n  };\n  subband.codeblocks = codeblocks;\n  subband.precincts = precincts;\n}\nfunction createPacket(resolution, precinctNumber, layerNumber) {\n  const precinctCodeblocks = [];\n  const subbands = resolution.subbands;\n  for (let i = 0, ii = subbands.length; i < ii; i++) {\n    const subband = subbands[i];\n    const codeblocks = subband.codeblocks;\n    for (let j = 0, jj = codeblocks.length; j < jj; j++) {\n      const codeblock = codeblocks[j];\n      if (codeblock.precinctNumber !== precinctNumber) {\n        continue;\n      }\n      precinctCodeblocks.push(codeblock);\n    }\n  }\n  return {\n    layerNumber,\n    codeblocks: precinctCodeblocks\n  };\n}\nfunction LayerResolutionComponentPositionIterator(context) {\n  const siz = context.SIZ;\n  const tileIndex = context.currentTile.index;\n  const tile = context.tiles[tileIndex];\n  const layersCount = tile.codingStyleDefaultParameters.layersCount;\n  const componentsCount = siz.Csiz;\n  let maxDecompositionLevelsCount = 0;\n  for (let q = 0; q < componentsCount; q++) {\n    maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount, tile.components[q].codingStyleParameters.decompositionLevelsCount);\n  }\n  let l = 0,\n    r = 0,\n    i = 0,\n    k = 0;\n  this.nextPacket = function JpxImage_nextPacket() {\n    for (; l < layersCount; l++) {\n      for (; r <= maxDecompositionLevelsCount; r++) {\n        for (; i < componentsCount; i++) {\n          const component = tile.components[i];\n          if (r > component.codingStyleParameters.decompositionLevelsCount) {\n            continue;\n          }\n          const resolution = component.resolutions[r];\n          const numprecincts = resolution.precinctParameters.numprecincts;\n          for (; k < numprecincts;) {\n            const packet = createPacket(resolution, k, l);\n            k++;\n            return packet;\n          }\n          k = 0;\n        }\n        i = 0;\n      }\n      r = 0;\n    }\n    throw new JpxError(\"Out of packets\");\n  };\n}\nfunction ResolutionLayerComponentPositionIterator(context) {\n  const siz = context.SIZ;\n  const tileIndex = context.currentTile.index;\n  const tile = context.tiles[tileIndex];\n  const layersCount = tile.codingStyleDefaultParameters.layersCount;\n  const componentsCount = siz.Csiz;\n  let maxDecompositionLevelsCount = 0;\n  for (let q = 0; q < componentsCount; q++) {\n    maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount, tile.components[q].codingStyleParameters.decompositionLevelsCount);\n  }\n  let r = 0,\n    l = 0,\n    i = 0,\n    k = 0;\n  this.nextPacket = function JpxImage_nextPacket() {\n    for (; r <= maxDecompositionLevelsCount; r++) {\n      for (; l < layersCount; l++) {\n        for (; i < componentsCount; i++) {\n          const component = tile.components[i];\n          if (r > component.codingStyleParameters.decompositionLevelsCount) {\n            continue;\n          }\n          const resolution = component.resolutions[r];\n          const numprecincts = resolution.precinctParameters.numprecincts;\n          for (; k < numprecincts;) {\n            const packet = createPacket(resolution, k, l);\n            k++;\n            return packet;\n          }\n          k = 0;\n        }\n        i = 0;\n      }\n      l = 0;\n    }\n    throw new JpxError(\"Out of packets\");\n  };\n}\nfunction ResolutionPositionComponentLayerIterator(context) {\n  const siz = context.SIZ;\n  const tileIndex = context.currentTile.index;\n  const tile = context.tiles[tileIndex];\n  const layersCount = tile.codingStyleDefaultParameters.layersCount;\n  const componentsCount = siz.Csiz;\n  let l, r, c, p;\n  let maxDecompositionLevelsCount = 0;\n  for (c = 0; c < componentsCount; c++) {\n    const component = tile.components[c];\n    maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount, component.codingStyleParameters.decompositionLevelsCount);\n  }\n  const maxNumPrecinctsInLevel = new Int32Array(maxDecompositionLevelsCount + 1);\n  for (r = 0; r <= maxDecompositionLevelsCount; ++r) {\n    let maxNumPrecincts = 0;\n    for (c = 0; c < componentsCount; ++c) {\n      const resolutions = tile.components[c].resolutions;\n      if (r < resolutions.length) {\n        maxNumPrecincts = Math.max(maxNumPrecincts, resolutions[r].precinctParameters.numprecincts);\n      }\n    }\n    maxNumPrecinctsInLevel[r] = maxNumPrecincts;\n  }\n  l = 0;\n  r = 0;\n  c = 0;\n  p = 0;\n  this.nextPacket = function JpxImage_nextPacket() {\n    for (; r <= maxDecompositionLevelsCount; r++) {\n      for (; p < maxNumPrecinctsInLevel[r]; p++) {\n        for (; c < componentsCount; c++) {\n          const component = tile.components[c];\n          if (r > component.codingStyleParameters.decompositionLevelsCount) {\n            continue;\n          }\n          const resolution = component.resolutions[r];\n          const numprecincts = resolution.precinctParameters.numprecincts;\n          if (p >= numprecincts) {\n            continue;\n          }\n          for (; l < layersCount;) {\n            const packet = createPacket(resolution, p, l);\n            l++;\n            return packet;\n          }\n          l = 0;\n        }\n        c = 0;\n      }\n      p = 0;\n    }\n    throw new JpxError(\"Out of packets\");\n  };\n}\nfunction PositionComponentResolutionLayerIterator(context) {\n  const siz = context.SIZ;\n  const tileIndex = context.currentTile.index;\n  const tile = context.tiles[tileIndex];\n  const layersCount = tile.codingStyleDefaultParameters.layersCount;\n  const componentsCount = siz.Csiz;\n  const precinctsSizes = getPrecinctSizesInImageScale(tile);\n  const precinctsIterationSizes = precinctsSizes;\n  let l = 0,\n    r = 0,\n    c = 0,\n    px = 0,\n    py = 0;\n  this.nextPacket = function JpxImage_nextPacket() {\n    for (; py < precinctsIterationSizes.maxNumHigh; py++) {\n      for (; px < precinctsIterationSizes.maxNumWide; px++) {\n        for (; c < componentsCount; c++) {\n          const component = tile.components[c];\n          const decompositionLevelsCount = component.codingStyleParameters.decompositionLevelsCount;\n          for (; r <= decompositionLevelsCount; r++) {\n            const resolution = component.resolutions[r];\n            const sizeInImageScale = precinctsSizes.components[c].resolutions[r];\n            const k = getPrecinctIndexIfExist(px, py, sizeInImageScale, precinctsIterationSizes, resolution);\n            if (k === null) {\n              continue;\n            }\n            for (; l < layersCount;) {\n              const packet = createPacket(resolution, k, l);\n              l++;\n              return packet;\n            }\n            l = 0;\n          }\n          r = 0;\n        }\n        c = 0;\n      }\n      px = 0;\n    }\n    throw new JpxError(\"Out of packets\");\n  };\n}\nfunction ComponentPositionResolutionLayerIterator(context) {\n  const siz = context.SIZ;\n  const tileIndex = context.currentTile.index;\n  const tile = context.tiles[tileIndex];\n  const layersCount = tile.codingStyleDefaultParameters.layersCount;\n  const componentsCount = siz.Csiz;\n  const precinctsSizes = getPrecinctSizesInImageScale(tile);\n  let l = 0,\n    r = 0,\n    c = 0,\n    px = 0,\n    py = 0;\n  this.nextPacket = function JpxImage_nextPacket() {\n    for (; c < componentsCount; ++c) {\n      const component = tile.components[c];\n      const precinctsIterationSizes = precinctsSizes.components[c];\n      const decompositionLevelsCount = component.codingStyleParameters.decompositionLevelsCount;\n      for (; py < precinctsIterationSizes.maxNumHigh; py++) {\n        for (; px < precinctsIterationSizes.maxNumWide; px++) {\n          for (; r <= decompositionLevelsCount; r++) {\n            const resolution = component.resolutions[r];\n            const sizeInImageScale = precinctsIterationSizes.resolutions[r];\n            const k = getPrecinctIndexIfExist(px, py, sizeInImageScale, precinctsIterationSizes, resolution);\n            if (k === null) {\n              continue;\n            }\n            for (; l < layersCount;) {\n              const packet = createPacket(resolution, k, l);\n              l++;\n              return packet;\n            }\n            l = 0;\n          }\n          r = 0;\n        }\n        px = 0;\n      }\n      py = 0;\n    }\n    throw new JpxError(\"Out of packets\");\n  };\n}\nfunction getPrecinctIndexIfExist(pxIndex, pyIndex, sizeInImageScale, precinctIterationSizes, resolution) {\n  const posX = pxIndex * precinctIterationSizes.minWidth;\n  const posY = pyIndex * precinctIterationSizes.minHeight;\n  if (posX % sizeInImageScale.width !== 0 || posY % sizeInImageScale.height !== 0) {\n    return null;\n  }\n  const startPrecinctRowIndex = posY / sizeInImageScale.width * resolution.precinctParameters.numprecinctswide;\n  return posX / sizeInImageScale.height + startPrecinctRowIndex;\n}\nfunction getPrecinctSizesInImageScale(tile) {\n  const componentsCount = tile.components.length;\n  let minWidth = Number.MAX_VALUE;\n  let minHeight = Number.MAX_VALUE;\n  let maxNumWide = 0;\n  let maxNumHigh = 0;\n  const sizePerComponent = new Array(componentsCount);\n  for (let c = 0; c < componentsCount; c++) {\n    const component = tile.components[c];\n    const decompositionLevelsCount = component.codingStyleParameters.decompositionLevelsCount;\n    const sizePerResolution = new Array(decompositionLevelsCount + 1);\n    let minWidthCurrentComponent = Number.MAX_VALUE;\n    let minHeightCurrentComponent = Number.MAX_VALUE;\n    let maxNumWideCurrentComponent = 0;\n    let maxNumHighCurrentComponent = 0;\n    let scale = 1;\n    for (let r = decompositionLevelsCount; r >= 0; --r) {\n      const resolution = component.resolutions[r];\n      const widthCurrentResolution = scale * resolution.precinctParameters.precinctWidth;\n      const heightCurrentResolution = scale * resolution.precinctParameters.precinctHeight;\n      minWidthCurrentComponent = Math.min(minWidthCurrentComponent, widthCurrentResolution);\n      minHeightCurrentComponent = Math.min(minHeightCurrentComponent, heightCurrentResolution);\n      maxNumWideCurrentComponent = Math.max(maxNumWideCurrentComponent, resolution.precinctParameters.numprecinctswide);\n      maxNumHighCurrentComponent = Math.max(maxNumHighCurrentComponent, resolution.precinctParameters.numprecinctshigh);\n      sizePerResolution[r] = {\n        width: widthCurrentResolution,\n        height: heightCurrentResolution\n      };\n      scale <<= 1;\n    }\n    minWidth = Math.min(minWidth, minWidthCurrentComponent);\n    minHeight = Math.min(minHeight, minHeightCurrentComponent);\n    maxNumWide = Math.max(maxNumWide, maxNumWideCurrentComponent);\n    maxNumHigh = Math.max(maxNumHigh, maxNumHighCurrentComponent);\n    sizePerComponent[c] = {\n      resolutions: sizePerResolution,\n      minWidth: minWidthCurrentComponent,\n      minHeight: minHeightCurrentComponent,\n      maxNumWide: maxNumWideCurrentComponent,\n      maxNumHigh: maxNumHighCurrentComponent\n    };\n  }\n  return {\n    components: sizePerComponent,\n    minWidth,\n    minHeight,\n    maxNumWide,\n    maxNumHigh\n  };\n}\nfunction buildPackets(context) {\n  const siz = context.SIZ;\n  const tileIndex = context.currentTile.index;\n  const tile = context.tiles[tileIndex];\n  const componentsCount = siz.Csiz;\n  for (let c = 0; c < componentsCount; c++) {\n    const component = tile.components[c];\n    const decompositionLevelsCount = component.codingStyleParameters.decompositionLevelsCount;\n    const resolutions = [];\n    const subbands = [];\n    for (let r = 0; r <= decompositionLevelsCount; r++) {\n      const blocksDimensions = getBlocksDimensions(context, component, r);\n      const resolution = {};\n      const scale = 1 << decompositionLevelsCount - r;\n      resolution.trx0 = Math.ceil(component.tcx0 / scale);\n      resolution.try0 = Math.ceil(component.tcy0 / scale);\n      resolution.trx1 = Math.ceil(component.tcx1 / scale);\n      resolution.try1 = Math.ceil(component.tcy1 / scale);\n      resolution.resLevel = r;\n      buildPrecincts(context, resolution, blocksDimensions);\n      resolutions.push(resolution);\n      let subband;\n      if (r === 0) {\n        subband = {};\n        subband.type = \"LL\";\n        subband.tbx0 = Math.ceil(component.tcx0 / scale);\n        subband.tby0 = Math.ceil(component.tcy0 / scale);\n        subband.tbx1 = Math.ceil(component.tcx1 / scale);\n        subband.tby1 = Math.ceil(component.tcy1 / scale);\n        subband.resolution = resolution;\n        buildCodeblocks(context, subband, blocksDimensions);\n        subbands.push(subband);\n        resolution.subbands = [subband];\n      } else {\n        const bscale = 1 << decompositionLevelsCount - r + 1;\n        const resolutionSubbands = [];\n        subband = {};\n        subband.type = \"HL\";\n        subband.tbx0 = Math.ceil(component.tcx0 / bscale - 0.5);\n        subband.tby0 = Math.ceil(component.tcy0 / bscale);\n        subband.tbx1 = Math.ceil(component.tcx1 / bscale - 0.5);\n        subband.tby1 = Math.ceil(component.tcy1 / bscale);\n        subband.resolution = resolution;\n        buildCodeblocks(context, subband, blocksDimensions);\n        subbands.push(subband);\n        resolutionSubbands.push(subband);\n        subband = {};\n        subband.type = \"LH\";\n        subband.tbx0 = Math.ceil(component.tcx0 / bscale);\n        subband.tby0 = Math.ceil(component.tcy0 / bscale - 0.5);\n        subband.tbx1 = Math.ceil(component.tcx1 / bscale);\n        subband.tby1 = Math.ceil(component.tcy1 / bscale - 0.5);\n        subband.resolution = resolution;\n        buildCodeblocks(context, subband, blocksDimensions);\n        subbands.push(subband);\n        resolutionSubbands.push(subband);\n        subband = {};\n        subband.type = \"HH\";\n        subband.tbx0 = Math.ceil(component.tcx0 / bscale - 0.5);\n        subband.tby0 = Math.ceil(component.tcy0 / bscale - 0.5);\n        subband.tbx1 = Math.ceil(component.tcx1 / bscale - 0.5);\n        subband.tby1 = Math.ceil(component.tcy1 / bscale - 0.5);\n        subband.resolution = resolution;\n        buildCodeblocks(context, subband, blocksDimensions);\n        subbands.push(subband);\n        resolutionSubbands.push(subband);\n        resolution.subbands = resolutionSubbands;\n      }\n    }\n    component.resolutions = resolutions;\n    component.subbands = subbands;\n  }\n  const progressionOrder = tile.codingStyleDefaultParameters.progressionOrder;\n  switch (progressionOrder) {\n    case 0:\n      tile.packetsIterator = new LayerResolutionComponentPositionIterator(context);\n      break;\n    case 1:\n      tile.packetsIterator = new ResolutionLayerComponentPositionIterator(context);\n      break;\n    case 2:\n      tile.packetsIterator = new ResolutionPositionComponentLayerIterator(context);\n      break;\n    case 3:\n      tile.packetsIterator = new PositionComponentResolutionLayerIterator(context);\n      break;\n    case 4:\n      tile.packetsIterator = new ComponentPositionResolutionLayerIterator(context);\n      break;\n    default:\n      throw new JpxError(`Unsupported progression order ${progressionOrder}`);\n  }\n}\nfunction parseTilePackets(context, data, offset, dataLength) {\n  let position = 0;\n  let buffer,\n    bufferSize = 0,\n    skipNextBit = false;\n  function readBits(count) {\n    while (bufferSize < count) {\n      const b = data[offset + position];\n      position++;\n      if (skipNextBit) {\n        buffer = buffer << 7 | b;\n        bufferSize += 7;\n        skipNextBit = false;\n      } else {\n        buffer = buffer << 8 | b;\n        bufferSize += 8;\n      }\n      if (b === 0xff) {\n        skipNextBit = true;\n      }\n    }\n    bufferSize -= count;\n    return buffer >>> bufferSize & (1 << count) - 1;\n  }\n  function skipMarkerIfEqual(value) {\n    if (data[offset + position - 1] === 0xff && data[offset + position] === value) {\n      skipBytes(1);\n      return true;\n    } else if (data[offset + position] === 0xff && data[offset + position + 1] === value) {\n      skipBytes(2);\n      return true;\n    }\n    return false;\n  }\n  function skipBytes(count) {\n    position += count;\n  }\n  function alignToByte() {\n    bufferSize = 0;\n    if (skipNextBit) {\n      position++;\n      skipNextBit = false;\n    }\n  }\n  function readCodingpasses() {\n    if (readBits(1) === 0) {\n      return 1;\n    }\n    if (readBits(1) === 0) {\n      return 2;\n    }\n    let value = readBits(2);\n    if (value < 3) {\n      return value + 3;\n    }\n    value = readBits(5);\n    if (value < 31) {\n      return value + 6;\n    }\n    value = readBits(7);\n    return value + 37;\n  }\n  const tileIndex = context.currentTile.index;\n  const tile = context.tiles[tileIndex];\n  const sopMarkerUsed = context.COD.sopMarkerUsed;\n  const ephMarkerUsed = context.COD.ephMarkerUsed;\n  const packetsIterator = tile.packetsIterator;\n  while (position < dataLength) {\n    alignToByte();\n    if (sopMarkerUsed && skipMarkerIfEqual(0x91)) {\n      skipBytes(4);\n    }\n    const packet = packetsIterator.nextPacket();\n    if (!readBits(1)) {\n      continue;\n    }\n    const layerNumber = packet.layerNumber,\n      queue = [];\n    let codeblock;\n    for (let i = 0, ii = packet.codeblocks.length; i < ii; i++) {\n      codeblock = packet.codeblocks[i];\n      let precinct = codeblock.precinct;\n      const codeblockColumn = codeblock.cbx - precinct.cbxMin;\n      const codeblockRow = codeblock.cby - precinct.cbyMin;\n      let codeblockIncluded = false;\n      let firstTimeInclusion = false;\n      let valueReady, zeroBitPlanesTree;\n      if (codeblock.included !== undefined) {\n        codeblockIncluded = !!readBits(1);\n      } else {\n        precinct = codeblock.precinct;\n        let inclusionTree;\n        if (precinct.inclusionTree !== undefined) {\n          inclusionTree = precinct.inclusionTree;\n        } else {\n          const width = precinct.cbxMax - precinct.cbxMin + 1;\n          const height = precinct.cbyMax - precinct.cbyMin + 1;\n          inclusionTree = new InclusionTree(width, height, layerNumber);\n          zeroBitPlanesTree = new TagTree(width, height);\n          precinct.inclusionTree = inclusionTree;\n          precinct.zeroBitPlanesTree = zeroBitPlanesTree;\n          for (let l = 0; l < layerNumber; l++) {\n            if (readBits(1) !== 0) {\n              throw new JpxError(\"Invalid tag tree\");\n            }\n          }\n        }\n        if (inclusionTree.reset(codeblockColumn, codeblockRow, layerNumber)) {\n          while (true) {\n            if (readBits(1)) {\n              valueReady = !inclusionTree.nextLevel();\n              if (valueReady) {\n                codeblock.included = true;\n                codeblockIncluded = firstTimeInclusion = true;\n                break;\n              }\n            } else {\n              inclusionTree.incrementValue(layerNumber);\n              break;\n            }\n          }\n        }\n      }\n      if (!codeblockIncluded) {\n        continue;\n      }\n      if (firstTimeInclusion) {\n        zeroBitPlanesTree = precinct.zeroBitPlanesTree;\n        zeroBitPlanesTree.reset(codeblockColumn, codeblockRow);\n        while (true) {\n          if (readBits(1)) {\n            valueReady = !zeroBitPlanesTree.nextLevel();\n            if (valueReady) {\n              break;\n            }\n          } else {\n            zeroBitPlanesTree.incrementValue();\n          }\n        }\n        codeblock.zeroBitPlanes = zeroBitPlanesTree.value;\n      }\n      const codingpasses = readCodingpasses();\n      while (readBits(1)) {\n        codeblock.Lblock++;\n      }\n      const codingpassesLog2 = log2(codingpasses);\n      const bits = (codingpasses < 1 << codingpassesLog2 ? codingpassesLog2 - 1 : codingpassesLog2) + codeblock.Lblock;\n      const codedDataLength = readBits(bits);\n      queue.push({\n        codeblock,\n        codingpasses,\n        dataLength: codedDataLength\n      });\n    }\n    alignToByte();\n    if (ephMarkerUsed) {\n      skipMarkerIfEqual(0x92);\n    }\n    while (queue.length > 0) {\n      const packetItem = queue.shift();\n      codeblock = packetItem.codeblock;\n      if (codeblock.data === undefined) {\n        codeblock.data = [];\n      }\n      codeblock.data.push({\n        data,\n        start: offset + position,\n        end: offset + position + packetItem.dataLength,\n        codingpasses: packetItem.codingpasses\n      });\n      position += packetItem.dataLength;\n    }\n  }\n  return position;\n}\nfunction copyCoefficients(coefficients, levelWidth, levelHeight, subband, delta, mb, reversible, segmentationSymbolUsed, resetContextProbabilities) {\n  const x0 = subband.tbx0;\n  const y0 = subband.tby0;\n  const width = subband.tbx1 - subband.tbx0;\n  const codeblocks = subband.codeblocks;\n  const right = subband.type.charAt(0) === \"H\" ? 1 : 0;\n  const bottom = subband.type.charAt(1) === \"H\" ? levelWidth : 0;\n  for (let i = 0, ii = codeblocks.length; i < ii; ++i) {\n    const codeblock = codeblocks[i];\n    const blockWidth = codeblock.tbx1_ - codeblock.tbx0_;\n    const blockHeight = codeblock.tby1_ - codeblock.tby0_;\n    if (blockWidth === 0 || blockHeight === 0) {\n      continue;\n    }\n    if (codeblock.data === undefined) {\n      continue;\n    }\n    const bitModel = new BitModel(blockWidth, blockHeight, codeblock.subbandType, codeblock.zeroBitPlanes, mb);\n    let currentCodingpassType = 2;\n    const data = codeblock.data;\n    let totalLength = 0,\n      codingpasses = 0;\n    let j, jj, dataItem;\n    for (j = 0, jj = data.length; j < jj; j++) {\n      dataItem = data[j];\n      totalLength += dataItem.end - dataItem.start;\n      codingpasses += dataItem.codingpasses;\n    }\n    const encodedData = new Uint8Array(totalLength);\n    let position = 0;\n    for (j = 0, jj = data.length; j < jj; j++) {\n      dataItem = data[j];\n      const chunk = dataItem.data.subarray(dataItem.start, dataItem.end);\n      encodedData.set(chunk, position);\n      position += chunk.length;\n    }\n    const decoder = new ArithmeticDecoder(encodedData, 0, totalLength);\n    bitModel.setDecoder(decoder);\n    for (j = 0; j < codingpasses; j++) {\n      switch (currentCodingpassType) {\n        case 0:\n          bitModel.runSignificancePropagationPass();\n          break;\n        case 1:\n          bitModel.runMagnitudeRefinementPass();\n          break;\n        case 2:\n          bitModel.runCleanupPass();\n          if (segmentationSymbolUsed) {\n            bitModel.checkSegmentationSymbol();\n          }\n          break;\n      }\n      if (resetContextProbabilities) {\n        bitModel.reset();\n      }\n      currentCodingpassType = (currentCodingpassType + 1) % 3;\n    }\n    let offset = codeblock.tbx0_ - x0 + (codeblock.tby0_ - y0) * width;\n    const sign = bitModel.coefficentsSign;\n    const magnitude = bitModel.coefficentsMagnitude;\n    const bitsDecoded = bitModel.bitsDecoded;\n    const magnitudeCorrection = reversible ? 0 : 0.5;\n    let k, n, nb;\n    position = 0;\n    const interleave = subband.type !== \"LL\";\n    for (j = 0; j < blockHeight; j++) {\n      const row = offset / width | 0;\n      const levelOffset = 2 * row * (levelWidth - width) + right + bottom;\n      for (k = 0; k < blockWidth; k++) {\n        n = magnitude[position];\n        if (n !== 0) {\n          n = (n + magnitudeCorrection) * delta;\n          if (sign[position] !== 0) {\n            n = -n;\n          }\n          nb = bitsDecoded[position];\n          const pos = interleave ? levelOffset + (offset << 1) : offset;\n          coefficients[pos] = reversible && nb >= mb ? n : n * (1 << mb - nb);\n        }\n        offset++;\n        position++;\n      }\n      offset += width - blockWidth;\n    }\n  }\n}\nfunction transformTile(context, tile, c) {\n  const component = tile.components[c];\n  const codingStyleParameters = component.codingStyleParameters;\n  const quantizationParameters = component.quantizationParameters;\n  const decompositionLevelsCount = codingStyleParameters.decompositionLevelsCount;\n  const spqcds = quantizationParameters.SPqcds;\n  const scalarExpounded = quantizationParameters.scalarExpounded;\n  const guardBits = quantizationParameters.guardBits;\n  const segmentationSymbolUsed = codingStyleParameters.segmentationSymbolUsed;\n  const resetContextProbabilities = codingStyleParameters.resetContextProbabilities;\n  const precision = context.components[c].precision;\n  const reversible = codingStyleParameters.reversibleTransformation;\n  const transform = reversible ? new ReversibleTransform() : new IrreversibleTransform();\n  const subbandCoefficients = [];\n  let b = 0;\n  for (let i = 0; i <= decompositionLevelsCount; i++) {\n    const resolution = component.resolutions[i];\n    const width = resolution.trx1 - resolution.trx0;\n    const height = resolution.try1 - resolution.try0;\n    const coefficients = new Float32Array(width * height);\n    for (let j = 0, jj = resolution.subbands.length; j < jj; j++) {\n      let mu, epsilon;\n      if (!scalarExpounded) {\n        mu = spqcds[0].mu;\n        epsilon = spqcds[0].epsilon + (i > 0 ? 1 - i : 0);\n      } else {\n        mu = spqcds[b].mu;\n        epsilon = spqcds[b].epsilon;\n        b++;\n      }\n      const subband = resolution.subbands[j];\n      const gainLog2 = SubbandsGainLog2[subband.type];\n      const delta = reversible ? 1 : 2 ** (precision + gainLog2 - epsilon) * (1 + mu / 2048);\n      const mb = guardBits + epsilon - 1;\n      copyCoefficients(coefficients, width, height, subband, delta, mb, reversible, segmentationSymbolUsed, resetContextProbabilities);\n    }\n    subbandCoefficients.push({\n      width,\n      height,\n      items: coefficients\n    });\n  }\n  const result = transform.calculate(subbandCoefficients, component.tcx0, component.tcy0);\n  return {\n    left: component.tcx0,\n    top: component.tcy0,\n    width: result.width,\n    height: result.height,\n    items: result.items\n  };\n}\nfunction transformComponents(context) {\n  const siz = context.SIZ;\n  const components = context.components;\n  const componentsCount = siz.Csiz;\n  const resultImages = [];\n  for (let i = 0, ii = context.tiles.length; i < ii; i++) {\n    const tile = context.tiles[i];\n    const transformedTiles = [];\n    for (let c = 0; c < componentsCount; c++) {\n      transformedTiles[c] = transformTile(context, tile, c);\n    }\n    const tile0 = transformedTiles[0];\n    const out = new Uint8ClampedArray(tile0.items.length * componentsCount);\n    const result = {\n      left: tile0.left,\n      top: tile0.top,\n      width: tile0.width,\n      height: tile0.height,\n      items: out\n    };\n    let shift, offset;\n    let pos = 0,\n      j,\n      jj,\n      y0,\n      y1,\n      y2;\n    if (tile.codingStyleDefaultParameters.multipleComponentTransform) {\n      const fourComponents = componentsCount === 4;\n      const y0items = transformedTiles[0].items;\n      const y1items = transformedTiles[1].items;\n      const y2items = transformedTiles[2].items;\n      const y3items = fourComponents ? transformedTiles[3].items : null;\n      shift = components[0].precision - 8;\n      offset = (128 << shift) + 0.5;\n      const component0 = tile.components[0];\n      const alpha01 = componentsCount - 3;\n      jj = y0items.length;\n      if (!component0.codingStyleParameters.reversibleTransformation) {\n        for (j = 0; j < jj; j++, pos += alpha01) {\n          y0 = y0items[j] + offset;\n          y1 = y1items[j];\n          y2 = y2items[j];\n          out[pos++] = y0 + 1.402 * y2 >> shift;\n          out[pos++] = y0 - 0.34413 * y1 - 0.71414 * y2 >> shift;\n          out[pos++] = y0 + 1.772 * y1 >> shift;\n        }\n      } else {\n        for (j = 0; j < jj; j++, pos += alpha01) {\n          y0 = y0items[j] + offset;\n          y1 = y1items[j];\n          y2 = y2items[j];\n          const g = y0 - (y2 + y1 >> 2);\n          out[pos++] = g + y2 >> shift;\n          out[pos++] = g >> shift;\n          out[pos++] = g + y1 >> shift;\n        }\n      }\n      if (fourComponents) {\n        for (j = 0, pos = 3; j < jj; j++, pos += 4) {\n          out[pos] = y3items[j] + offset >> shift;\n        }\n      }\n    } else {\n      for (let c = 0; c < componentsCount; c++) {\n        const items = transformedTiles[c].items;\n        shift = components[c].precision - 8;\n        offset = (128 << shift) + 0.5;\n        for (pos = c, j = 0, jj = items.length; j < jj; j++) {\n          out[pos] = items[j] + offset >> shift;\n          pos += componentsCount;\n        }\n      }\n    }\n    resultImages.push(result);\n  }\n  return resultImages;\n}\nfunction initializeTile(context, tileIndex) {\n  const siz = context.SIZ;\n  const componentsCount = siz.Csiz;\n  const tile = context.tiles[tileIndex];\n  for (let c = 0; c < componentsCount; c++) {\n    const component = tile.components[c];\n    const qcdOrQcc = context.currentTile.QCC[c] !== undefined ? context.currentTile.QCC[c] : context.currentTile.QCD;\n    component.quantizationParameters = qcdOrQcc;\n    const codOrCoc = context.currentTile.COC[c] !== undefined ? context.currentTile.COC[c] : context.currentTile.COD;\n    component.codingStyleParameters = codOrCoc;\n  }\n  tile.codingStyleDefaultParameters = context.currentTile.COD;\n}\nclass TagTree {\n  constructor(width, height) {\n    const levelsLength = log2(Math.max(width, height)) + 1;\n    this.levels = [];\n    for (let i = 0; i < levelsLength; i++) {\n      const level = {\n        width,\n        height,\n        items: []\n      };\n      this.levels.push(level);\n      width = Math.ceil(width / 2);\n      height = Math.ceil(height / 2);\n    }\n  }\n  reset(i, j) {\n    let currentLevel = 0,\n      value = 0,\n      level;\n    while (currentLevel < this.levels.length) {\n      level = this.levels[currentLevel];\n      const index = i + j * level.width;\n      if (level.items[index] !== undefined) {\n        value = level.items[index];\n        break;\n      }\n      level.index = index;\n      i >>= 1;\n      j >>= 1;\n      currentLevel++;\n    }\n    currentLevel--;\n    level = this.levels[currentLevel];\n    level.items[level.index] = value;\n    this.currentLevel = currentLevel;\n    delete this.value;\n  }\n  incrementValue() {\n    const level = this.levels[this.currentLevel];\n    level.items[level.index]++;\n  }\n  nextLevel() {\n    let currentLevel = this.currentLevel;\n    let level = this.levels[currentLevel];\n    const value = level.items[level.index];\n    currentLevel--;\n    if (currentLevel < 0) {\n      this.value = value;\n      return false;\n    }\n    this.currentLevel = currentLevel;\n    level = this.levels[currentLevel];\n    level.items[level.index] = value;\n    return true;\n  }\n}\nclass InclusionTree {\n  constructor(width, height, defaultValue) {\n    const levelsLength = log2(Math.max(width, height)) + 1;\n    this.levels = [];\n    for (let i = 0; i < levelsLength; i++) {\n      const items = new Uint8Array(width * height);\n      for (let j = 0, jj = items.length; j < jj; j++) {\n        items[j] = defaultValue;\n      }\n      const level = {\n        width,\n        height,\n        items\n      };\n      this.levels.push(level);\n      width = Math.ceil(width / 2);\n      height = Math.ceil(height / 2);\n    }\n  }\n  reset(i, j, stopValue) {\n    let currentLevel = 0;\n    while (currentLevel < this.levels.length) {\n      const level = this.levels[currentLevel];\n      const index = i + j * level.width;\n      level.index = index;\n      const value = level.items[index];\n      if (value === 0xff) {\n        break;\n      }\n      if (value > stopValue) {\n        this.currentLevel = currentLevel;\n        this.propagateValues();\n        return false;\n      }\n      i >>= 1;\n      j >>= 1;\n      currentLevel++;\n    }\n    this.currentLevel = currentLevel - 1;\n    return true;\n  }\n  incrementValue(stopValue) {\n    const level = this.levels[this.currentLevel];\n    level.items[level.index] = stopValue + 1;\n    this.propagateValues();\n  }\n  propagateValues() {\n    let levelIndex = this.currentLevel;\n    let level = this.levels[levelIndex];\n    const currentValue = level.items[level.index];\n    while (--levelIndex >= 0) {\n      level = this.levels[levelIndex];\n      level.items[level.index] = currentValue;\n    }\n  }\n  nextLevel() {\n    let currentLevel = this.currentLevel;\n    let level = this.levels[currentLevel];\n    const value = level.items[level.index];\n    level.items[level.index] = 0xff;\n    currentLevel--;\n    if (currentLevel < 0) {\n      return false;\n    }\n    this.currentLevel = currentLevel;\n    level = this.levels[currentLevel];\n    level.items[level.index] = value;\n    return true;\n  }\n}\nclass BitModel {\n  static UNIFORM_CONTEXT = 17;\n  static RUNLENGTH_CONTEXT = 18;\n  static LLAndLHContextsLabel = new Uint8Array([0, 5, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 1, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8]);\n  static HLContextLabel = new Uint8Array([0, 3, 4, 0, 5, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 1, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8]);\n  static HHContextLabel = new Uint8Array([0, 1, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 3, 4, 5, 0, 4, 5, 5, 0, 5, 5, 5, 0, 0, 0, 0, 0, 6, 7, 7, 0, 7, 7, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 8, 8, 8, 0, 8, 8, 8, 0, 8, 8, 8, 0, 0, 0, 0, 0, 8, 8, 8, 0, 8, 8, 8, 0, 8, 8, 8]);\n  constructor(width, height, subband, zeroBitPlanes, mb) {\n    this.width = width;\n    this.height = height;\n    let contextLabelTable;\n    if (subband === \"HH\") {\n      contextLabelTable = BitModel.HHContextLabel;\n    } else if (subband === \"HL\") {\n      contextLabelTable = BitModel.HLContextLabel;\n    } else {\n      contextLabelTable = BitModel.LLAndLHContextsLabel;\n    }\n    this.contextLabelTable = contextLabelTable;\n    const coefficientCount = width * height;\n    this.neighborsSignificance = new Uint8Array(coefficientCount);\n    this.coefficentsSign = new Uint8Array(coefficientCount);\n    let coefficentsMagnitude;\n    if (mb > 14) {\n      coefficentsMagnitude = new Uint32Array(coefficientCount);\n    } else if (mb > 6) {\n      coefficentsMagnitude = new Uint16Array(coefficientCount);\n    } else {\n      coefficentsMagnitude = new Uint8Array(coefficientCount);\n    }\n    this.coefficentsMagnitude = coefficentsMagnitude;\n    this.processingFlags = new Uint8Array(coefficientCount);\n    const bitsDecoded = new Uint8Array(coefficientCount);\n    if (zeroBitPlanes !== 0) {\n      for (let i = 0; i < coefficientCount; i++) {\n        bitsDecoded[i] = zeroBitPlanes;\n      }\n    }\n    this.bitsDecoded = bitsDecoded;\n    this.reset();\n  }\n  setDecoder(decoder) {\n    this.decoder = decoder;\n  }\n  reset() {\n    this.contexts = new Int8Array(19);\n    this.contexts[0] = 4 << 1 | 0;\n    this.contexts[BitModel.UNIFORM_CONTEXT] = 46 << 1 | 0;\n    this.contexts[BitModel.RUNLENGTH_CONTEXT] = 3 << 1 | 0;\n  }\n  setNeighborsSignificance(row, column, index) {\n    const neighborsSignificance = this.neighborsSignificance;\n    const width = this.width,\n      height = this.height;\n    const left = column > 0;\n    const right = column + 1 < width;\n    let i;\n    if (row > 0) {\n      i = index - width;\n      if (left) {\n        neighborsSignificance[i - 1] += 0x10;\n      }\n      if (right) {\n        neighborsSignificance[i + 1] += 0x10;\n      }\n      neighborsSignificance[i] += 0x04;\n    }\n    if (row + 1 < height) {\n      i = index + width;\n      if (left) {\n        neighborsSignificance[i - 1] += 0x10;\n      }\n      if (right) {\n        neighborsSignificance[i + 1] += 0x10;\n      }\n      neighborsSignificance[i] += 0x04;\n    }\n    if (left) {\n      neighborsSignificance[index - 1] += 0x01;\n    }\n    if (right) {\n      neighborsSignificance[index + 1] += 0x01;\n    }\n    neighborsSignificance[index] |= 0x80;\n  }\n  runSignificancePropagationPass() {\n    const decoder = this.decoder;\n    const width = this.width,\n      height = this.height;\n    const coefficentsMagnitude = this.coefficentsMagnitude;\n    const coefficentsSign = this.coefficentsSign;\n    const neighborsSignificance = this.neighborsSignificance;\n    const processingFlags = this.processingFlags;\n    const contexts = this.contexts;\n    const labels = this.contextLabelTable;\n    const bitsDecoded = this.bitsDecoded;\n    const processedInverseMask = ~1;\n    const processedMask = 1;\n    const firstMagnitudeBitMask = 2;\n    for (let i0 = 0; i0 < height; i0 += 4) {\n      for (let j = 0; j < width; j++) {\n        let index = i0 * width + j;\n        for (let i1 = 0; i1 < 4; i1++, index += width) {\n          const i = i0 + i1;\n          if (i >= height) {\n            break;\n          }\n          processingFlags[index] &= processedInverseMask;\n          if (coefficentsMagnitude[index] || !neighborsSignificance[index]) {\n            continue;\n          }\n          const contextLabel = labels[neighborsSignificance[index]];\n          const decision = decoder.readBit(contexts, contextLabel);\n          if (decision) {\n            const sign = this.decodeSignBit(i, j, index);\n            coefficentsSign[index] = sign;\n            coefficentsMagnitude[index] = 1;\n            this.setNeighborsSignificance(i, j, index);\n            processingFlags[index] |= firstMagnitudeBitMask;\n          }\n          bitsDecoded[index]++;\n          processingFlags[index] |= processedMask;\n        }\n      }\n    }\n  }\n  decodeSignBit(row, column, index) {\n    const width = this.width,\n      height = this.height;\n    const coefficentsMagnitude = this.coefficentsMagnitude;\n    const coefficentsSign = this.coefficentsSign;\n    let contribution, sign0, sign1, significance1;\n    let contextLabel, decoded;\n    significance1 = column > 0 && coefficentsMagnitude[index - 1] !== 0;\n    if (column + 1 < width && coefficentsMagnitude[index + 1] !== 0) {\n      sign1 = coefficentsSign[index + 1];\n      if (significance1) {\n        sign0 = coefficentsSign[index - 1];\n        contribution = 1 - sign1 - sign0;\n      } else {\n        contribution = 1 - sign1 - sign1;\n      }\n    } else if (significance1) {\n      sign0 = coefficentsSign[index - 1];\n      contribution = 1 - sign0 - sign0;\n    } else {\n      contribution = 0;\n    }\n    const horizontalContribution = 3 * contribution;\n    significance1 = row > 0 && coefficentsMagnitude[index - width] !== 0;\n    if (row + 1 < height && coefficentsMagnitude[index + width] !== 0) {\n      sign1 = coefficentsSign[index + width];\n      if (significance1) {\n        sign0 = coefficentsSign[index - width];\n        contribution = 1 - sign1 - sign0 + horizontalContribution;\n      } else {\n        contribution = 1 - sign1 - sign1 + horizontalContribution;\n      }\n    } else if (significance1) {\n      sign0 = coefficentsSign[index - width];\n      contribution = 1 - sign0 - sign0 + horizontalContribution;\n    } else {\n      contribution = horizontalContribution;\n    }\n    if (contribution >= 0) {\n      contextLabel = 9 + contribution;\n      decoded = this.decoder.readBit(this.contexts, contextLabel);\n    } else {\n      contextLabel = 9 - contribution;\n      decoded = this.decoder.readBit(this.contexts, contextLabel) ^ 1;\n    }\n    return decoded;\n  }\n  runMagnitudeRefinementPass() {\n    const decoder = this.decoder;\n    const width = this.width,\n      height = this.height;\n    const coefficentsMagnitude = this.coefficentsMagnitude;\n    const neighborsSignificance = this.neighborsSignificance;\n    const contexts = this.contexts;\n    const bitsDecoded = this.bitsDecoded;\n    const processingFlags = this.processingFlags;\n    const processedMask = 1;\n    const firstMagnitudeBitMask = 2;\n    const length = width * height;\n    const width4 = width * 4;\n    for (let index0 = 0, indexNext; index0 < length; index0 = indexNext) {\n      indexNext = Math.min(length, index0 + width4);\n      for (let j = 0; j < width; j++) {\n        for (let index = index0 + j; index < indexNext; index += width) {\n          if (!coefficentsMagnitude[index] || (processingFlags[index] & processedMask) !== 0) {\n            continue;\n          }\n          let contextLabel = 16;\n          if ((processingFlags[index] & firstMagnitudeBitMask) !== 0) {\n            processingFlags[index] ^= firstMagnitudeBitMask;\n            const significance = neighborsSignificance[index] & 127;\n            contextLabel = significance === 0 ? 15 : 14;\n          }\n          const bit = decoder.readBit(contexts, contextLabel);\n          coefficentsMagnitude[index] = coefficentsMagnitude[index] << 1 | bit;\n          bitsDecoded[index]++;\n          processingFlags[index] |= processedMask;\n        }\n      }\n    }\n  }\n  runCleanupPass() {\n    const decoder = this.decoder;\n    const width = this.width,\n      height = this.height;\n    const neighborsSignificance = this.neighborsSignificance;\n    const coefficentsMagnitude = this.coefficentsMagnitude;\n    const coefficentsSign = this.coefficentsSign;\n    const contexts = this.contexts;\n    const labels = this.contextLabelTable;\n    const bitsDecoded = this.bitsDecoded;\n    const processingFlags = this.processingFlags;\n    const processedMask = 1;\n    const firstMagnitudeBitMask = 2;\n    const oneRowDown = width;\n    const twoRowsDown = width * 2;\n    const threeRowsDown = width * 3;\n    let iNext;\n    for (let i0 = 0; i0 < height; i0 = iNext) {\n      iNext = Math.min(i0 + 4, height);\n      const indexBase = i0 * width;\n      const checkAllEmpty = i0 + 3 < height;\n      for (let j = 0; j < width; j++) {\n        const index0 = indexBase + j;\n        const allEmpty = checkAllEmpty && processingFlags[index0] === 0 && processingFlags[index0 + oneRowDown] === 0 && processingFlags[index0 + twoRowsDown] === 0 && processingFlags[index0 + threeRowsDown] === 0 && neighborsSignificance[index0] === 0 && neighborsSignificance[index0 + oneRowDown] === 0 && neighborsSignificance[index0 + twoRowsDown] === 0 && neighborsSignificance[index0 + threeRowsDown] === 0;\n        let i1 = 0,\n          index = index0;\n        let i = i0,\n          sign;\n        if (allEmpty) {\n          const hasSignificantCoefficent = decoder.readBit(contexts, BitModel.RUNLENGTH_CONTEXT);\n          if (!hasSignificantCoefficent) {\n            bitsDecoded[index0]++;\n            bitsDecoded[index0 + oneRowDown]++;\n            bitsDecoded[index0 + twoRowsDown]++;\n            bitsDecoded[index0 + threeRowsDown]++;\n            continue;\n          }\n          i1 = decoder.readBit(contexts, BitModel.UNIFORM_CONTEXT) << 1 | decoder.readBit(contexts, BitModel.UNIFORM_CONTEXT);\n          if (i1 !== 0) {\n            i = i0 + i1;\n            index += i1 * width;\n          }\n          sign = this.decodeSignBit(i, j, index);\n          coefficentsSign[index] = sign;\n          coefficentsMagnitude[index] = 1;\n          this.setNeighborsSignificance(i, j, index);\n          processingFlags[index] |= firstMagnitudeBitMask;\n          index = index0;\n          for (let i2 = i0; i2 <= i; i2++, index += width) {\n            bitsDecoded[index]++;\n          }\n          i1++;\n        }\n        for (i = i0 + i1; i < iNext; i++, index += width) {\n          if (coefficentsMagnitude[index] || (processingFlags[index] & processedMask) !== 0) {\n            continue;\n          }\n          const contextLabel = labels[neighborsSignificance[index]];\n          const decision = decoder.readBit(contexts, contextLabel);\n          if (decision === 1) {\n            sign = this.decodeSignBit(i, j, index);\n            coefficentsSign[index] = sign;\n            coefficentsMagnitude[index] = 1;\n            this.setNeighborsSignificance(i, j, index);\n            processingFlags[index] |= firstMagnitudeBitMask;\n          }\n          bitsDecoded[index]++;\n        }\n      }\n    }\n  }\n  checkSegmentationSymbol() {\n    const decoder = this.decoder;\n    const contexts = this.contexts;\n    const symbol = decoder.readBit(contexts, BitModel.UNIFORM_CONTEXT) << 3 | decoder.readBit(contexts, BitModel.UNIFORM_CONTEXT) << 2 | decoder.readBit(contexts, BitModel.UNIFORM_CONTEXT) << 1 | decoder.readBit(contexts, BitModel.UNIFORM_CONTEXT);\n    if (symbol !== 0xa) {\n      throw new JpxError(\"Invalid segmentation symbol\");\n    }\n  }\n}\nclass Transform {\n  constructor() {\n    if (this.constructor === Transform) {\n      unreachable(\"Cannot initialize Transform.\");\n    }\n  }\n  calculate(subbands, u0, v0) {\n    let ll = subbands[0];\n    for (let i = 1, ii = subbands.length; i < ii; i++) {\n      ll = this.iterate(ll, subbands[i], u0, v0);\n    }\n    return ll;\n  }\n  extend(buffer, offset, size) {\n    let i1 = offset - 1,\n      j1 = offset + 1;\n    let i2 = offset + size - 2,\n      j2 = offset + size;\n    buffer[i1--] = buffer[j1++];\n    buffer[j2++] = buffer[i2--];\n    buffer[i1--] = buffer[j1++];\n    buffer[j2++] = buffer[i2--];\n    buffer[i1--] = buffer[j1++];\n    buffer[j2++] = buffer[i2--];\n    buffer[i1] = buffer[j1];\n    buffer[j2] = buffer[i2];\n  }\n  filter(x, offset, length) {\n    unreachable(\"Abstract method `filter` called\");\n  }\n  iterate(ll, hl_lh_hh, u0, v0) {\n    const llWidth = ll.width,\n      llHeight = ll.height;\n    let llItems = ll.items;\n    const width = hl_lh_hh.width;\n    const height = hl_lh_hh.height;\n    const items = hl_lh_hh.items;\n    let i, j, k, l, u, v;\n    for (k = 0, i = 0; i < llHeight; i++) {\n      l = i * 2 * width;\n      for (j = 0; j < llWidth; j++, k++, l += 2) {\n        items[l] = llItems[k];\n      }\n    }\n    llItems = ll.items = null;\n    const bufferPadding = 4;\n    const rowBuffer = new Float32Array(width + 2 * bufferPadding);\n    if (width === 1) {\n      if ((u0 & 1) !== 0) {\n        for (v = 0, k = 0; v < height; v++, k += width) {\n          items[k] *= 0.5;\n        }\n      }\n    } else {\n      for (v = 0, k = 0; v < height; v++, k += width) {\n        rowBuffer.set(items.subarray(k, k + width), bufferPadding);\n        this.extend(rowBuffer, bufferPadding, width);\n        this.filter(rowBuffer, bufferPadding, width);\n        items.set(rowBuffer.subarray(bufferPadding, bufferPadding + width), k);\n      }\n    }\n    let numBuffers = 16;\n    const colBuffers = [];\n    for (i = 0; i < numBuffers; i++) {\n      colBuffers.push(new Float32Array(height + 2 * bufferPadding));\n    }\n    let b,\n      currentBuffer = 0;\n    ll = bufferPadding + height;\n    if (height === 1) {\n      if ((v0 & 1) !== 0) {\n        for (u = 0; u < width; u++) {\n          items[u] *= 0.5;\n        }\n      }\n    } else {\n      for (u = 0; u < width; u++) {\n        if (currentBuffer === 0) {\n          numBuffers = Math.min(width - u, numBuffers);\n          for (k = u, l = bufferPadding; l < ll; k += width, l++) {\n            for (b = 0; b < numBuffers; b++) {\n              colBuffers[b][l] = items[k + b];\n            }\n          }\n          currentBuffer = numBuffers;\n        }\n        currentBuffer--;\n        const buffer = colBuffers[currentBuffer];\n        this.extend(buffer, bufferPadding, height);\n        this.filter(buffer, bufferPadding, height);\n        if (currentBuffer === 0) {\n          k = u - numBuffers + 1;\n          for (l = bufferPadding; l < ll; k += width, l++) {\n            for (b = 0; b < numBuffers; b++) {\n              items[k + b] = colBuffers[b][l];\n            }\n          }\n        }\n      }\n    }\n    return {\n      width,\n      height,\n      items\n    };\n  }\n}\nclass IrreversibleTransform extends Transform {\n  filter(x, offset, length) {\n    const len = length >> 1;\n    offset |= 0;\n    let j, n, current, next;\n    const alpha = -1.586134342059924;\n    const beta = -0.052980118572961;\n    const gamma = 0.882911075530934;\n    const delta = 0.443506852043971;\n    const K = 1.230174104914001;\n    const K_ = 1 / K;\n    j = offset - 3;\n    for (n = len + 4; n--; j += 2) {\n      x[j] *= K_;\n    }\n    j = offset - 2;\n    current = delta * x[j - 1];\n    for (n = len + 3; n--; j += 2) {\n      next = delta * x[j + 1];\n      x[j] = K * x[j] - current - next;\n      if (n--) {\n        j += 2;\n        current = delta * x[j + 1];\n        x[j] = K * x[j] - current - next;\n      } else {\n        break;\n      }\n    }\n    j = offset - 1;\n    current = gamma * x[j - 1];\n    for (n = len + 2; n--; j += 2) {\n      next = gamma * x[j + 1];\n      x[j] -= current + next;\n      if (n--) {\n        j += 2;\n        current = gamma * x[j + 1];\n        x[j] -= current + next;\n      } else {\n        break;\n      }\n    }\n    j = offset;\n    current = beta * x[j - 1];\n    for (n = len + 1; n--; j += 2) {\n      next = beta * x[j + 1];\n      x[j] -= current + next;\n      if (n--) {\n        j += 2;\n        current = beta * x[j + 1];\n        x[j] -= current + next;\n      } else {\n        break;\n      }\n    }\n    if (len !== 0) {\n      j = offset + 1;\n      current = alpha * x[j - 1];\n      for (n = len; n--; j += 2) {\n        next = alpha * x[j + 1];\n        x[j] -= current + next;\n        if (n--) {\n          j += 2;\n          current = alpha * x[j + 1];\n          x[j] -= current + next;\n        } else {\n          break;\n        }\n      }\n    }\n  }\n}\nclass ReversibleTransform extends Transform {\n  filter(x, offset, length) {\n    const len = length >> 1;\n    offset |= 0;\n    let j, n;\n    for (j = offset, n = len + 1; n--; j += 2) {\n      x[j] -= x[j - 1] + x[j + 1] + 2 >> 2;\n    }\n    for (j = offset + 1, n = len; n--; j += 2) {\n      x[j] += x[j - 1] + x[j + 1] >> 1;\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/jpx_stream.js\n\n\n\nclass JpxStream extends DecodeStream {\n  constructor(stream, maybeLength, params) {\n    super(maybeLength);\n    this.stream = stream;\n    this.dict = stream.dict;\n    this.maybeLength = maybeLength;\n    this.params = params;\n  }\n  get bytes() {\n    return shadow(this, \"bytes\", this.stream.getBytes(this.maybeLength));\n  }\n  ensureBuffer(requested) {}\n  readBlock() {\n    if (this.eof) {\n      return;\n    }\n    const jpxImage = new JpxImage();\n    jpxImage.parse(this.bytes);\n    const width = jpxImage.width;\n    const height = jpxImage.height;\n    const componentsCount = jpxImage.componentsCount;\n    const tileCount = jpxImage.tiles.length;\n    if (tileCount === 1) {\n      this.buffer = jpxImage.tiles[0].items;\n    } else {\n      const data = new Uint8ClampedArray(width * height * componentsCount);\n      for (let k = 0; k < tileCount; k++) {\n        const tileComponents = jpxImage.tiles[k];\n        const tileWidth = tileComponents.width;\n        const tileHeight = tileComponents.height;\n        const tileLeft = tileComponents.left;\n        const tileTop = tileComponents.top;\n        const src = tileComponents.items;\n        let srcPosition = 0;\n        let dataPosition = (width * tileTop + tileLeft) * componentsCount;\n        const imgRowSize = width * componentsCount;\n        const tileRowSize = tileWidth * componentsCount;\n        for (let j = 0; j < tileHeight; j++) {\n          const rowBytes = src.subarray(srcPosition, srcPosition + tileRowSize);\n          data.set(rowBytes, dataPosition);\n          srcPosition += tileRowSize;\n          dataPosition += imgRowSize;\n        }\n      }\n      this.buffer = data;\n    }\n    this.bufferLength = this.buffer.length;\n    this.eof = true;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/lzw_stream.js\n\nclass LZWStream extends DecodeStream {\n  constructor(str, maybeLength, earlyChange) {\n    super(maybeLength);\n    this.str = str;\n    this.dict = str.dict;\n    this.cachedData = 0;\n    this.bitsCached = 0;\n    const maxLzwDictionarySize = 4096;\n    const lzwState = {\n      earlyChange,\n      codeLength: 9,\n      nextCode: 258,\n      dictionaryValues: new Uint8Array(maxLzwDictionarySize),\n      dictionaryLengths: new Uint16Array(maxLzwDictionarySize),\n      dictionaryPrevCodes: new Uint16Array(maxLzwDictionarySize),\n      currentSequence: new Uint8Array(maxLzwDictionarySize),\n      currentSequenceLength: 0\n    };\n    for (let i = 0; i < 256; ++i) {\n      lzwState.dictionaryValues[i] = i;\n      lzwState.dictionaryLengths[i] = 1;\n    }\n    this.lzwState = lzwState;\n  }\n  readBits(n) {\n    let bitsCached = this.bitsCached;\n    let cachedData = this.cachedData;\n    while (bitsCached < n) {\n      const c = this.str.getByte();\n      if (c === -1) {\n        this.eof = true;\n        return null;\n      }\n      cachedData = cachedData << 8 | c;\n      bitsCached += 8;\n    }\n    this.bitsCached = bitsCached -= n;\n    this.cachedData = cachedData;\n    this.lastCode = null;\n    return cachedData >>> bitsCached & (1 << n) - 1;\n  }\n  readBlock() {\n    const blockSize = 512,\n      decodedSizeDelta = blockSize;\n    let estimatedDecodedSize = blockSize * 2;\n    let i, j, q;\n    const lzwState = this.lzwState;\n    if (!lzwState) {\n      return;\n    }\n    const earlyChange = lzwState.earlyChange;\n    let nextCode = lzwState.nextCode;\n    const dictionaryValues = lzwState.dictionaryValues;\n    const dictionaryLengths = lzwState.dictionaryLengths;\n    const dictionaryPrevCodes = lzwState.dictionaryPrevCodes;\n    let codeLength = lzwState.codeLength;\n    let prevCode = lzwState.prevCode;\n    const currentSequence = lzwState.currentSequence;\n    let currentSequenceLength = lzwState.currentSequenceLength;\n    let decodedLength = 0;\n    let currentBufferLength = this.bufferLength;\n    let buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize);\n    for (i = 0; i < blockSize; i++) {\n      const code = this.readBits(codeLength);\n      const hasPrev = currentSequenceLength > 0;\n      if (code < 256) {\n        currentSequence[0] = code;\n        currentSequenceLength = 1;\n      } else if (code >= 258) {\n        if (code < nextCode) {\n          currentSequenceLength = dictionaryLengths[code];\n          for (j = currentSequenceLength - 1, q = code; j >= 0; j--) {\n            currentSequence[j] = dictionaryValues[q];\n            q = dictionaryPrevCodes[q];\n          }\n        } else {\n          currentSequence[currentSequenceLength++] = currentSequence[0];\n        }\n      } else if (code === 256) {\n        codeLength = 9;\n        nextCode = 258;\n        currentSequenceLength = 0;\n        continue;\n      } else {\n        this.eof = true;\n        delete this.lzwState;\n        break;\n      }\n      if (hasPrev) {\n        dictionaryPrevCodes[nextCode] = prevCode;\n        dictionaryLengths[nextCode] = dictionaryLengths[prevCode] + 1;\n        dictionaryValues[nextCode] = currentSequence[0];\n        nextCode++;\n        codeLength = nextCode + earlyChange & nextCode + earlyChange - 1 ? codeLength : Math.min(Math.log(nextCode + earlyChange) / 0.6931471805599453 + 1, 12) | 0;\n      }\n      prevCode = code;\n      decodedLength += currentSequenceLength;\n      if (estimatedDecodedSize < decodedLength) {\n        do {\n          estimatedDecodedSize += decodedSizeDelta;\n        } while (estimatedDecodedSize < decodedLength);\n        buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize);\n      }\n      for (j = 0; j < currentSequenceLength; j++) {\n        buffer[currentBufferLength++] = currentSequence[j];\n      }\n    }\n    lzwState.nextCode = nextCode;\n    lzwState.codeLength = codeLength;\n    lzwState.prevCode = prevCode;\n    lzwState.currentSequenceLength = currentSequenceLength;\n    this.bufferLength = currentBufferLength;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/predictor_stream.js\n\n\n\nclass PredictorStream extends DecodeStream {\n  constructor(str, maybeLength, params) {\n    super(maybeLength);\n    if (!(params instanceof Dict)) {\n      return str;\n    }\n    const predictor = this.predictor = params.get(\"Predictor\") || 1;\n    if (predictor <= 1) {\n      return str;\n    }\n    if (predictor !== 2 && (predictor < 10 || predictor > 15)) {\n      throw new FormatError(`Unsupported predictor: ${predictor}`);\n    }\n    this.readBlock = predictor === 2 ? this.readBlockTiff : this.readBlockPng;\n    this.str = str;\n    this.dict = str.dict;\n    const colors = this.colors = params.get(\"Colors\") || 1;\n    const bits = this.bits = params.get(\"BPC\", \"BitsPerComponent\") || 8;\n    const columns = this.columns = params.get(\"Columns\") || 1;\n    this.pixBytes = colors * bits + 7 >> 3;\n    this.rowBytes = columns * colors * bits + 7 >> 3;\n    return this;\n  }\n  readBlockTiff() {\n    const rowBytes = this.rowBytes;\n    const bufferLength = this.bufferLength;\n    const buffer = this.ensureBuffer(bufferLength + rowBytes);\n    const bits = this.bits;\n    const colors = this.colors;\n    const rawBytes = this.str.getBytes(rowBytes);\n    this.eof = !rawBytes.length;\n    if (this.eof) {\n      return;\n    }\n    let inbuf = 0,\n      outbuf = 0;\n    let inbits = 0,\n      outbits = 0;\n    let pos = bufferLength;\n    let i;\n    if (bits === 1 && colors === 1) {\n      for (i = 0; i < rowBytes; ++i) {\n        let c = rawBytes[i] ^ inbuf;\n        c ^= c >> 1;\n        c ^= c >> 2;\n        c ^= c >> 4;\n        inbuf = (c & 1) << 7;\n        buffer[pos++] = c;\n      }\n    } else if (bits === 8) {\n      for (i = 0; i < colors; ++i) {\n        buffer[pos++] = rawBytes[i];\n      }\n      for (; i < rowBytes; ++i) {\n        buffer[pos] = buffer[pos - colors] + rawBytes[i];\n        pos++;\n      }\n    } else if (bits === 16) {\n      const bytesPerPixel = colors * 2;\n      for (i = 0; i < bytesPerPixel; ++i) {\n        buffer[pos++] = rawBytes[i];\n      }\n      for (; i < rowBytes; i += 2) {\n        const sum = ((rawBytes[i] & 0xff) << 8) + (rawBytes[i + 1] & 0xff) + ((buffer[pos - bytesPerPixel] & 0xff) << 8) + (buffer[pos - bytesPerPixel + 1] & 0xff);\n        buffer[pos++] = sum >> 8 & 0xff;\n        buffer[pos++] = sum & 0xff;\n      }\n    } else {\n      const compArray = new Uint8Array(colors + 1);\n      const bitMask = (1 << bits) - 1;\n      let j = 0,\n        k = bufferLength;\n      const columns = this.columns;\n      for (i = 0; i < columns; ++i) {\n        for (let kk = 0; kk < colors; ++kk) {\n          if (inbits < bits) {\n            inbuf = inbuf << 8 | rawBytes[j++] & 0xff;\n            inbits += 8;\n          }\n          compArray[kk] = compArray[kk] + (inbuf >> inbits - bits) & bitMask;\n          inbits -= bits;\n          outbuf = outbuf << bits | compArray[kk];\n          outbits += bits;\n          if (outbits >= 8) {\n            buffer[k++] = outbuf >> outbits - 8 & 0xff;\n            outbits -= 8;\n          }\n        }\n      }\n      if (outbits > 0) {\n        buffer[k++] = (outbuf << 8 - outbits) + (inbuf & (1 << 8 - outbits) - 1);\n      }\n    }\n    this.bufferLength += rowBytes;\n  }\n  readBlockPng() {\n    const rowBytes = this.rowBytes;\n    const pixBytes = this.pixBytes;\n    const predictor = this.str.getByte();\n    const rawBytes = this.str.getBytes(rowBytes);\n    this.eof = !rawBytes.length;\n    if (this.eof) {\n      return;\n    }\n    const bufferLength = this.bufferLength;\n    const buffer = this.ensureBuffer(bufferLength + rowBytes);\n    let prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength);\n    if (prevRow.length === 0) {\n      prevRow = new Uint8Array(rowBytes);\n    }\n    let i,\n      j = bufferLength,\n      up,\n      c;\n    switch (predictor) {\n      case 0:\n        for (i = 0; i < rowBytes; ++i) {\n          buffer[j++] = rawBytes[i];\n        }\n        break;\n      case 1:\n        for (i = 0; i < pixBytes; ++i) {\n          buffer[j++] = rawBytes[i];\n        }\n        for (; i < rowBytes; ++i) {\n          buffer[j] = buffer[j - pixBytes] + rawBytes[i] & 0xff;\n          j++;\n        }\n        break;\n      case 2:\n        for (i = 0; i < rowBytes; ++i) {\n          buffer[j++] = prevRow[i] + rawBytes[i] & 0xff;\n        }\n        break;\n      case 3:\n        for (i = 0; i < pixBytes; ++i) {\n          buffer[j++] = (prevRow[i] >> 1) + rawBytes[i];\n        }\n        for (; i < rowBytes; ++i) {\n          buffer[j] = (prevRow[i] + buffer[j - pixBytes] >> 1) + rawBytes[i] & 0xff;\n          j++;\n        }\n        break;\n      case 4:\n        for (i = 0; i < pixBytes; ++i) {\n          up = prevRow[i];\n          c = rawBytes[i];\n          buffer[j++] = up + c;\n        }\n        for (; i < rowBytes; ++i) {\n          up = prevRow[i];\n          const upLeft = prevRow[i - pixBytes];\n          const left = buffer[j - pixBytes];\n          const p = left + up - upLeft;\n          let pa = p - left;\n          if (pa < 0) {\n            pa = -pa;\n          }\n          let pb = p - up;\n          if (pb < 0) {\n            pb = -pb;\n          }\n          let pc = p - upLeft;\n          if (pc < 0) {\n            pc = -pc;\n          }\n          c = rawBytes[i];\n          if (pa <= pb && pa <= pc) {\n            buffer[j++] = left + c;\n          } else if (pb <= pc) {\n            buffer[j++] = up + c;\n          } else {\n            buffer[j++] = upLeft + c;\n          }\n        }\n        break;\n      default:\n        throw new FormatError(`Unsupported predictor: ${predictor}`);\n    }\n    this.bufferLength += rowBytes;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/run_length_stream.js\n\nclass RunLengthStream extends DecodeStream {\n  constructor(str, maybeLength) {\n    super(maybeLength);\n    this.str = str;\n    this.dict = str.dict;\n  }\n  readBlock() {\n    const repeatHeader = this.str.getBytes(2);\n    if (!repeatHeader || repeatHeader.length < 2 || repeatHeader[0] === 128) {\n      this.eof = true;\n      return;\n    }\n    let buffer;\n    let bufferLength = this.bufferLength;\n    let n = repeatHeader[0];\n    if (n < 128) {\n      buffer = this.ensureBuffer(bufferLength + n + 1);\n      buffer[bufferLength++] = repeatHeader[1];\n      if (n > 0) {\n        const source = this.str.getBytes(n);\n        buffer.set(source, bufferLength);\n        bufferLength += n;\n      }\n    } else {\n      n = 257 - n;\n      const b = repeatHeader[1];\n      buffer = this.ensureBuffer(bufferLength + n + 1);\n      for (let i = 0; i < n; i++) {\n        buffer[bufferLength++] = b;\n      }\n    }\n    this.bufferLength = bufferLength;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/parser.js\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nconst MAX_LENGTH_TO_CACHE = 1000;\nfunction getInlineImageCacheKey(bytes) {\n  const strBuf = [],\n    ii = bytes.length;\n  let i = 0;\n  while (i < ii - 1) {\n    strBuf.push(bytes[i++] << 8 | bytes[i++]);\n  }\n  if (i < ii) {\n    strBuf.push(bytes[i]);\n  }\n  return ii + \"_\" + String.fromCharCode.apply(null, strBuf);\n}\nclass Parser {\n  constructor({\n    lexer,\n    xref,\n    allowStreams = false,\n    recoveryMode = false\n  }) {\n    this.lexer = lexer;\n    this.xref = xref;\n    this.allowStreams = allowStreams;\n    this.recoveryMode = recoveryMode;\n    this.imageCache = Object.create(null);\n    this._imageId = 0;\n    this.refill();\n  }\n  refill() {\n    this.buf1 = this.lexer.getObj();\n    this.buf2 = this.lexer.getObj();\n  }\n  shift() {\n    if (this.buf2 instanceof Cmd && this.buf2.cmd === \"ID\") {\n      this.buf1 = this.buf2;\n      this.buf2 = null;\n    } else {\n      this.buf1 = this.buf2;\n      this.buf2 = this.lexer.getObj();\n    }\n  }\n  tryShift() {\n    try {\n      this.shift();\n      return true;\n    } catch (e) {\n      if (e instanceof MissingDataException) {\n        throw e;\n      }\n      return false;\n    }\n  }\n  getObj(cipherTransform = null) {\n    const buf1 = this.buf1;\n    this.shift();\n    if (buf1 instanceof Cmd) {\n      switch (buf1.cmd) {\n        case \"BI\":\n          return this.makeInlineImage(cipherTransform);\n        case \"[\":\n          const array = [];\n          while (!isCmd(this.buf1, \"]\") && this.buf1 !== EOF) {\n            array.push(this.getObj(cipherTransform));\n          }\n          if (this.buf1 === EOF) {\n            if (this.recoveryMode) {\n              return array;\n            }\n            throw new ParserEOFException(\"End of file inside array.\");\n          }\n          this.shift();\n          return array;\n        case \"<<\":\n          const dict = new Dict(this.xref);\n          while (!isCmd(this.buf1, \">>\") && this.buf1 !== EOF) {\n            if (!(this.buf1 instanceof Name)) {\n              info(\"Malformed dictionary: key must be a name object\");\n              this.shift();\n              continue;\n            }\n            const key = this.buf1.name;\n            this.shift();\n            if (this.buf1 === EOF) {\n              break;\n            }\n            dict.set(key, this.getObj(cipherTransform));\n          }\n          if (this.buf1 === EOF) {\n            if (this.recoveryMode) {\n              return dict;\n            }\n            throw new ParserEOFException(\"End of file inside dictionary.\");\n          }\n          if (isCmd(this.buf2, \"stream\")) {\n            return this.allowStreams ? this.makeStream(dict, cipherTransform) : dict;\n          }\n          this.shift();\n          return dict;\n        default:\n          return buf1;\n      }\n    }\n    if (Number.isInteger(buf1)) {\n      if (Number.isInteger(this.buf1) && isCmd(this.buf2, \"R\")) {\n        const ref = Ref.get(buf1, this.buf1);\n        this.shift();\n        this.shift();\n        return ref;\n      }\n      return buf1;\n    }\n    if (typeof buf1 === \"string\") {\n      if (cipherTransform) {\n        return cipherTransform.decryptString(buf1);\n      }\n      return buf1;\n    }\n    return buf1;\n  }\n  findDefaultInlineStreamEnd(stream) {\n    const E = 0x45,\n      I = 0x49,\n      SPACE = 0x20,\n      LF = 0xa,\n      CR = 0xd,\n      NUL = 0x0;\n    const {\n        knownCommands\n      } = this.lexer,\n      startPos = stream.pos,\n      n = 15;\n    let state = 0,\n      ch,\n      maybeEIPos;\n    while ((ch = stream.getByte()) !== -1) {\n      if (state === 0) {\n        state = ch === E ? 1 : 0;\n      } else if (state === 1) {\n        state = ch === I ? 2 : 0;\n      } else {\n        if (ch === SPACE || ch === LF || ch === CR) {\n          maybeEIPos = stream.pos;\n          const followingBytes = stream.peekBytes(n);\n          const ii = followingBytes.length;\n          if (ii === 0) {\n            break;\n          }\n          for (let i = 0; i < ii; i++) {\n            ch = followingBytes[i];\n            if (ch === NUL && followingBytes[i + 1] !== NUL) {\n              continue;\n            }\n            if (ch !== LF && ch !== CR && (ch < SPACE || ch > 0x7f)) {\n              state = 0;\n              break;\n            }\n          }\n          if (state !== 2) {\n            continue;\n          }\n          if (!knownCommands) {\n            warn(\"findDefaultInlineStreamEnd - `lexer.knownCommands` is undefined.\");\n            continue;\n          }\n          const tmpLexer = new Lexer(new Stream(followingBytes.slice()), knownCommands);\n          tmpLexer._hexStringWarn = () => {};\n          let numArgs = 0;\n          while (true) {\n            const nextObj = tmpLexer.getObj();\n            if (nextObj === EOF) {\n              state = 0;\n              break;\n            }\n            if (nextObj instanceof Cmd) {\n              const knownCommand = knownCommands[nextObj.cmd];\n              if (!knownCommand) {\n                state = 0;\n                break;\n              } else if (knownCommand.variableArgs ? numArgs <= knownCommand.numArgs : numArgs === knownCommand.numArgs) {\n                break;\n              }\n              numArgs = 0;\n              continue;\n            }\n            numArgs++;\n          }\n          if (state === 2) {\n            break;\n          }\n        } else {\n          state = 0;\n        }\n      }\n    }\n    if (ch === -1) {\n      warn(\"findDefaultInlineStreamEnd: \" + \"Reached the end of the stream without finding a valid EI marker\");\n      if (maybeEIPos) {\n        warn('... trying to recover by using the last \"EI\" occurrence.');\n        stream.skip(-(stream.pos - maybeEIPos));\n      }\n    }\n    let endOffset = 4;\n    stream.skip(-endOffset);\n    ch = stream.peekByte();\n    stream.skip(endOffset);\n    if (!isWhiteSpace(ch)) {\n      endOffset--;\n    }\n    return stream.pos - endOffset - startPos;\n  }\n  findDCTDecodeInlineStreamEnd(stream) {\n    const startPos = stream.pos;\n    let foundEOI = false,\n      b,\n      markerLength;\n    while ((b = stream.getByte()) !== -1) {\n      if (b !== 0xff) {\n        continue;\n      }\n      switch (stream.getByte()) {\n        case 0x00:\n          break;\n        case 0xff:\n          stream.skip(-1);\n          break;\n        case 0xd9:\n          foundEOI = true;\n          break;\n        case 0xc0:\n        case 0xc1:\n        case 0xc2:\n        case 0xc3:\n        case 0xc5:\n        case 0xc6:\n        case 0xc7:\n        case 0xc9:\n        case 0xca:\n        case 0xcb:\n        case 0xcd:\n        case 0xce:\n        case 0xcf:\n        case 0xc4:\n        case 0xcc:\n        case 0xda:\n        case 0xdb:\n        case 0xdc:\n        case 0xdd:\n        case 0xde:\n        case 0xdf:\n        case 0xe0:\n        case 0xe1:\n        case 0xe2:\n        case 0xe3:\n        case 0xe4:\n        case 0xe5:\n        case 0xe6:\n        case 0xe7:\n        case 0xe8:\n        case 0xe9:\n        case 0xea:\n        case 0xeb:\n        case 0xec:\n        case 0xed:\n        case 0xee:\n        case 0xef:\n        case 0xfe:\n          markerLength = stream.getUint16();\n          if (markerLength > 2) {\n            stream.skip(markerLength - 2);\n          } else {\n            stream.skip(-2);\n          }\n          break;\n      }\n      if (foundEOI) {\n        break;\n      }\n    }\n    const length = stream.pos - startPos;\n    if (b === -1) {\n      warn(\"Inline DCTDecode image stream: \" + \"EOI marker not found, searching for /EI/ instead.\");\n      stream.skip(-length);\n      return this.findDefaultInlineStreamEnd(stream);\n    }\n    this.inlineStreamSkipEI(stream);\n    return length;\n  }\n  findASCII85DecodeInlineStreamEnd(stream) {\n    const TILDE = 0x7e,\n      GT = 0x3e;\n    const startPos = stream.pos;\n    let ch;\n    while ((ch = stream.getByte()) !== -1) {\n      if (ch === TILDE) {\n        const tildePos = stream.pos;\n        ch = stream.peekByte();\n        while (isWhiteSpace(ch)) {\n          stream.skip();\n          ch = stream.peekByte();\n        }\n        if (ch === GT) {\n          stream.skip();\n          break;\n        }\n        if (stream.pos > tildePos) {\n          const maybeEI = stream.peekBytes(2);\n          if (maybeEI[0] === 0x45 && maybeEI[1] === 0x49) {\n            break;\n          }\n        }\n      }\n    }\n    const length = stream.pos - startPos;\n    if (ch === -1) {\n      warn(\"Inline ASCII85Decode image stream: \" + \"EOD marker not found, searching for /EI/ instead.\");\n      stream.skip(-length);\n      return this.findDefaultInlineStreamEnd(stream);\n    }\n    this.inlineStreamSkipEI(stream);\n    return length;\n  }\n  findASCIIHexDecodeInlineStreamEnd(stream) {\n    const GT = 0x3e;\n    const startPos = stream.pos;\n    let ch;\n    while ((ch = stream.getByte()) !== -1) {\n      if (ch === GT) {\n        break;\n      }\n    }\n    const length = stream.pos - startPos;\n    if (ch === -1) {\n      warn(\"Inline ASCIIHexDecode image stream: \" + \"EOD marker not found, searching for /EI/ instead.\");\n      stream.skip(-length);\n      return this.findDefaultInlineStreamEnd(stream);\n    }\n    this.inlineStreamSkipEI(stream);\n    return length;\n  }\n  inlineStreamSkipEI(stream) {\n    const E = 0x45,\n      I = 0x49;\n    let state = 0,\n      ch;\n    while ((ch = stream.getByte()) !== -1) {\n      if (state === 0) {\n        state = ch === E ? 1 : 0;\n      } else if (state === 1) {\n        state = ch === I ? 2 : 0;\n      } else if (state === 2) {\n        break;\n      }\n    }\n  }\n  makeInlineImage(cipherTransform) {\n    const lexer = this.lexer;\n    const stream = lexer.stream;\n    const dictMap = Object.create(null);\n    let dictLength;\n    while (!isCmd(this.buf1, \"ID\") && this.buf1 !== EOF) {\n      if (!(this.buf1 instanceof Name)) {\n        throw new FormatError(\"Dictionary key must be a name object\");\n      }\n      const key = this.buf1.name;\n      this.shift();\n      if (this.buf1 === EOF) {\n        break;\n      }\n      dictMap[key] = this.getObj(cipherTransform);\n    }\n    if (lexer.beginInlineImagePos !== -1) {\n      dictLength = stream.pos - lexer.beginInlineImagePos;\n    }\n    const filter = this.xref.fetchIfRef(dictMap.F || dictMap.Filter);\n    let filterName;\n    if (filter instanceof Name) {\n      filterName = filter.name;\n    } else if (Array.isArray(filter)) {\n      const filterZero = this.xref.fetchIfRef(filter[0]);\n      if (filterZero instanceof Name) {\n        filterName = filterZero.name;\n      }\n    }\n    const startPos = stream.pos;\n    let length;\n    switch (filterName) {\n      case \"DCT\":\n      case \"DCTDecode\":\n        length = this.findDCTDecodeInlineStreamEnd(stream);\n        break;\n      case \"A85\":\n      case \"ASCII85Decode\":\n        length = this.findASCII85DecodeInlineStreamEnd(stream);\n        break;\n      case \"AHx\":\n      case \"ASCIIHexDecode\":\n        length = this.findASCIIHexDecodeInlineStreamEnd(stream);\n        break;\n      default:\n        length = this.findDefaultInlineStreamEnd(stream);\n    }\n    let cacheKey;\n    if (length < MAX_LENGTH_TO_CACHE && dictLength > 0) {\n      const initialStreamPos = stream.pos;\n      stream.pos = lexer.beginInlineImagePos;\n      cacheKey = getInlineImageCacheKey(stream.getBytes(dictLength + length));\n      stream.pos = initialStreamPos;\n      const cacheEntry = this.imageCache[cacheKey];\n      if (cacheEntry !== undefined) {\n        this.buf2 = Cmd.get(\"EI\");\n        this.shift();\n        cacheEntry.reset();\n        return cacheEntry;\n      }\n    }\n    const dict = new Dict(this.xref);\n    for (const key in dictMap) {\n      dict.set(key, dictMap[key]);\n    }\n    let imageStream = stream.makeSubStream(startPos, length, dict);\n    if (cipherTransform) {\n      imageStream = cipherTransform.createStream(imageStream, length);\n    }\n    imageStream = this.filter(imageStream, dict, length);\n    imageStream.dict = dict;\n    if (cacheKey !== undefined) {\n      imageStream.cacheKey = `inline_img_${++this._imageId}`;\n      this.imageCache[cacheKey] = imageStream;\n    }\n    this.buf2 = Cmd.get(\"EI\");\n    this.shift();\n    return imageStream;\n  }\n  _findStreamLength(startPos, signature) {\n    const {\n      stream\n    } = this.lexer;\n    stream.pos = startPos;\n    const SCAN_BLOCK_LENGTH = 2048;\n    const signatureLength = signature.length;\n    while (stream.pos < stream.end) {\n      const scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH);\n      const scanLength = scanBytes.length - signatureLength;\n      if (scanLength <= 0) {\n        break;\n      }\n      let pos = 0;\n      while (pos < scanLength) {\n        let j = 0;\n        while (j < signatureLength && scanBytes[pos + j] === signature[j]) {\n          j++;\n        }\n        if (j >= signatureLength) {\n          stream.pos += pos;\n          return stream.pos - startPos;\n        }\n        pos++;\n      }\n      stream.pos += scanLength;\n    }\n    return -1;\n  }\n  makeStream(dict, cipherTransform) {\n    const lexer = this.lexer;\n    let stream = lexer.stream;\n    lexer.skipToNextLine();\n    const startPos = stream.pos - 1;\n    let length = dict.get(\"Length\");\n    if (!Number.isInteger(length)) {\n      info(`Bad length \"${length && length.toString()}\" in stream.`);\n      length = 0;\n    }\n    stream.pos = startPos + length;\n    lexer.nextChar();\n    if (this.tryShift() && isCmd(this.buf2, \"endstream\")) {\n      this.shift();\n    } else {\n      const ENDSTREAM_SIGNATURE = new Uint8Array([0x65, 0x6e, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d]);\n      let actualLength = this._findStreamLength(startPos, ENDSTREAM_SIGNATURE);\n      if (actualLength < 0) {\n        const MAX_TRUNCATION = 1;\n        for (let i = 1; i <= MAX_TRUNCATION; i++) {\n          const end = ENDSTREAM_SIGNATURE.length - i;\n          const TRUNCATED_SIGNATURE = ENDSTREAM_SIGNATURE.slice(0, end);\n          const maybeLength = this._findStreamLength(startPos, TRUNCATED_SIGNATURE);\n          if (maybeLength >= 0) {\n            const lastByte = stream.peekBytes(end + 1)[end];\n            if (!isWhiteSpace(lastByte)) {\n              break;\n            }\n            info(`Found \"${bytesToString(TRUNCATED_SIGNATURE)}\" when ` + \"searching for endstream command.\");\n            actualLength = maybeLength;\n            break;\n          }\n        }\n        if (actualLength < 0) {\n          throw new FormatError(\"Missing endstream command.\");\n        }\n      }\n      length = actualLength;\n      lexer.nextChar();\n      this.shift();\n      this.shift();\n    }\n    this.shift();\n    stream = stream.makeSubStream(startPos, length, dict);\n    if (cipherTransform) {\n      stream = cipherTransform.createStream(stream, length);\n    }\n    stream = this.filter(stream, dict, length);\n    stream.dict = dict;\n    return stream;\n  }\n  filter(stream, dict, length) {\n    let filter = dict.get(\"F\", \"Filter\");\n    let params = dict.get(\"DP\", \"DecodeParms\");\n    if (filter instanceof Name) {\n      if (Array.isArray(params)) {\n        warn(\"/DecodeParms should not be an Array, when /Filter is a Name.\");\n      }\n      return this.makeFilter(stream, filter.name, length, params);\n    }\n    let maybeLength = length;\n    if (Array.isArray(filter)) {\n      const filterArray = filter;\n      const paramsArray = params;\n      for (let i = 0, ii = filterArray.length; i < ii; ++i) {\n        filter = this.xref.fetchIfRef(filterArray[i]);\n        if (!(filter instanceof Name)) {\n          throw new FormatError(`Bad filter name \"${filter}\"`);\n        }\n        params = null;\n        if (Array.isArray(paramsArray) && i in paramsArray) {\n          params = this.xref.fetchIfRef(paramsArray[i]);\n        }\n        stream = this.makeFilter(stream, filter.name, maybeLength, params);\n        maybeLength = null;\n      }\n    }\n    return stream;\n  }\n  makeFilter(stream, name, maybeLength, params) {\n    if (maybeLength === 0) {\n      warn(`Empty \"${name}\" stream.`);\n      return new NullStream();\n    }\n    try {\n      switch (name) {\n        case \"Fl\":\n        case \"FlateDecode\":\n          if (params) {\n            return new PredictorStream(new FlateStream(stream, maybeLength), maybeLength, params);\n          }\n          return new FlateStream(stream, maybeLength);\n        case \"LZW\":\n        case \"LZWDecode\":\n          let earlyChange = 1;\n          if (params) {\n            if (params.has(\"EarlyChange\")) {\n              earlyChange = params.get(\"EarlyChange\");\n            }\n            return new PredictorStream(new LZWStream(stream, maybeLength, earlyChange), maybeLength, params);\n          }\n          return new LZWStream(stream, maybeLength, earlyChange);\n        case \"DCT\":\n        case \"DCTDecode\":\n          return new JpegStream(stream, maybeLength, params);\n        case \"JPX\":\n        case \"JPXDecode\":\n          return new JpxStream(stream, maybeLength, params);\n        case \"A85\":\n        case \"ASCII85Decode\":\n          return new Ascii85Stream(stream, maybeLength);\n        case \"AHx\":\n        case \"ASCIIHexDecode\":\n          return new AsciiHexStream(stream, maybeLength);\n        case \"CCF\":\n        case \"CCITTFaxDecode\":\n          return new CCITTFaxStream(stream, maybeLength, params);\n        case \"RL\":\n        case \"RunLengthDecode\":\n          return new RunLengthStream(stream, maybeLength);\n        case \"JBIG2Decode\":\n          return new Jbig2Stream(stream, maybeLength, params);\n      }\n      warn(`Filter \"${name}\" is not supported.`);\n      return stream;\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(`Invalid stream: \"${ex}\"`);\n      return new NullStream();\n    }\n  }\n}\nconst specialChars = [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\nfunction toHexDigit(ch) {\n  if (ch >= 0x30 && ch <= 0x39) {\n    return ch & 0x0f;\n  }\n  if (ch >= 0x41 && ch <= 0x46 || ch >= 0x61 && ch <= 0x66) {\n    return (ch & 0x0f) + 9;\n  }\n  return -1;\n}\nclass Lexer {\n  constructor(stream, knownCommands = null) {\n    this.stream = stream;\n    this.nextChar();\n    this.strBuf = [];\n    this.knownCommands = knownCommands;\n    this._hexStringNumWarn = 0;\n    this.beginInlineImagePos = -1;\n  }\n  nextChar() {\n    return this.currentChar = this.stream.getByte();\n  }\n  peekChar() {\n    return this.stream.peekByte();\n  }\n  getNumber() {\n    let ch = this.currentChar;\n    let eNotation = false;\n    let divideBy = 0;\n    let sign = 1;\n    if (ch === 0x2d) {\n      sign = -1;\n      ch = this.nextChar();\n      if (ch === 0x2d) {\n        ch = this.nextChar();\n      }\n    } else if (ch === 0x2b) {\n      ch = this.nextChar();\n    }\n    if (ch === 0x0a || ch === 0x0d) {\n      do {\n        ch = this.nextChar();\n      } while (ch === 0x0a || ch === 0x0d);\n    }\n    if (ch === 0x2e) {\n      divideBy = 10;\n      ch = this.nextChar();\n    }\n    if (ch < 0x30 || ch > 0x39) {\n      const msg = `Invalid number: ${String.fromCharCode(ch)} (charCode ${ch})`;\n      if (isWhiteSpace(ch) || ch === -1) {\n        info(`Lexer.getNumber - \"${msg}\".`);\n        return 0;\n      }\n      throw new FormatError(msg);\n    }\n    let baseValue = ch - 0x30;\n    let powerValue = 0;\n    let powerValueSign = 1;\n    while ((ch = this.nextChar()) >= 0) {\n      if (ch >= 0x30 && ch <= 0x39) {\n        const currentDigit = ch - 0x30;\n        if (eNotation) {\n          powerValue = powerValue * 10 + currentDigit;\n        } else {\n          if (divideBy !== 0) {\n            divideBy *= 10;\n          }\n          baseValue = baseValue * 10 + currentDigit;\n        }\n      } else if (ch === 0x2e) {\n        if (divideBy === 0) {\n          divideBy = 1;\n        } else {\n          break;\n        }\n      } else if (ch === 0x2d) {\n        warn(\"Badly formatted number: minus sign in the middle\");\n      } else if (ch === 0x45 || ch === 0x65) {\n        ch = this.peekChar();\n        if (ch === 0x2b || ch === 0x2d) {\n          powerValueSign = ch === 0x2d ? -1 : 1;\n          this.nextChar();\n        } else if (ch < 0x30 || ch > 0x39) {\n          break;\n        }\n        eNotation = true;\n      } else {\n        break;\n      }\n    }\n    if (divideBy !== 0) {\n      baseValue /= divideBy;\n    }\n    if (eNotation) {\n      baseValue *= 10 ** (powerValueSign * powerValue);\n    }\n    return sign * baseValue;\n  }\n  getString() {\n    let numParen = 1;\n    let done = false;\n    const strBuf = this.strBuf;\n    strBuf.length = 0;\n    let ch = this.nextChar();\n    while (true) {\n      let charBuffered = false;\n      switch (ch | 0) {\n        case -1:\n          warn(\"Unterminated string\");\n          done = true;\n          break;\n        case 0x28:\n          ++numParen;\n          strBuf.push(\"(\");\n          break;\n        case 0x29:\n          if (--numParen === 0) {\n            this.nextChar();\n            done = true;\n          } else {\n            strBuf.push(\")\");\n          }\n          break;\n        case 0x5c:\n          ch = this.nextChar();\n          switch (ch) {\n            case -1:\n              warn(\"Unterminated string\");\n              done = true;\n              break;\n            case 0x6e:\n              strBuf.push(\"\\n\");\n              break;\n            case 0x72:\n              strBuf.push(\"\\r\");\n              break;\n            case 0x74:\n              strBuf.push(\"\\t\");\n              break;\n            case 0x62:\n              strBuf.push(\"\\b\");\n              break;\n            case 0x66:\n              strBuf.push(\"\\f\");\n              break;\n            case 0x5c:\n            case 0x28:\n            case 0x29:\n              strBuf.push(String.fromCharCode(ch));\n              break;\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n              let x = ch & 0x0f;\n              ch = this.nextChar();\n              charBuffered = true;\n              if (ch >= 0x30 && ch <= 0x37) {\n                x = (x << 3) + (ch & 0x0f);\n                ch = this.nextChar();\n                if (ch >= 0x30 && ch <= 0x37) {\n                  charBuffered = false;\n                  x = (x << 3) + (ch & 0x0f);\n                }\n              }\n              strBuf.push(String.fromCharCode(x));\n              break;\n            case 0x0d:\n              if (this.peekChar() === 0x0a) {\n                this.nextChar();\n              }\n              break;\n            case 0x0a:\n              break;\n            default:\n              strBuf.push(String.fromCharCode(ch));\n              break;\n          }\n          break;\n        default:\n          strBuf.push(String.fromCharCode(ch));\n          break;\n      }\n      if (done) {\n        break;\n      }\n      if (!charBuffered) {\n        ch = this.nextChar();\n      }\n    }\n    return strBuf.join(\"\");\n  }\n  getName() {\n    let ch, previousCh;\n    const strBuf = this.strBuf;\n    strBuf.length = 0;\n    while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {\n      if (ch === 0x23) {\n        ch = this.nextChar();\n        if (specialChars[ch]) {\n          warn(\"Lexer_getName: \" + \"NUMBER SIGN (#) should be followed by a hexadecimal number.\");\n          strBuf.push(\"#\");\n          break;\n        }\n        const x = toHexDigit(ch);\n        if (x !== -1) {\n          previousCh = ch;\n          ch = this.nextChar();\n          const x2 = toHexDigit(ch);\n          if (x2 === -1) {\n            warn(`Lexer_getName: Illegal digit (${String.fromCharCode(ch)}) ` + \"in hexadecimal number.\");\n            strBuf.push(\"#\", String.fromCharCode(previousCh));\n            if (specialChars[ch]) {\n              break;\n            }\n            strBuf.push(String.fromCharCode(ch));\n            continue;\n          }\n          strBuf.push(String.fromCharCode(x << 4 | x2));\n        } else {\n          strBuf.push(\"#\", String.fromCharCode(ch));\n        }\n      } else {\n        strBuf.push(String.fromCharCode(ch));\n      }\n    }\n    if (strBuf.length > 127) {\n      warn(`Name token is longer than allowed by the spec: ${strBuf.length}`);\n    }\n    return Name.get(strBuf.join(\"\"));\n  }\n  _hexStringWarn(ch) {\n    const MAX_HEX_STRING_NUM_WARN = 5;\n    if (this._hexStringNumWarn++ === MAX_HEX_STRING_NUM_WARN) {\n      warn(\"getHexString - ignoring additional invalid characters.\");\n      return;\n    }\n    if (this._hexStringNumWarn > MAX_HEX_STRING_NUM_WARN) {\n      return;\n    }\n    warn(`getHexString - ignoring invalid character: ${ch}`);\n  }\n  getHexString() {\n    const strBuf = this.strBuf;\n    strBuf.length = 0;\n    let ch = this.currentChar;\n    let isFirstHex = true;\n    let firstDigit, secondDigit;\n    this._hexStringNumWarn = 0;\n    while (true) {\n      if (ch < 0) {\n        warn(\"Unterminated hex string\");\n        break;\n      } else if (ch === 0x3e) {\n        this.nextChar();\n        break;\n      } else if (specialChars[ch] === 1) {\n        ch = this.nextChar();\n        continue;\n      } else {\n        if (isFirstHex) {\n          firstDigit = toHexDigit(ch);\n          if (firstDigit === -1) {\n            this._hexStringWarn(ch);\n            ch = this.nextChar();\n            continue;\n          }\n        } else {\n          secondDigit = toHexDigit(ch);\n          if (secondDigit === -1) {\n            this._hexStringWarn(ch);\n            ch = this.nextChar();\n            continue;\n          }\n          strBuf.push(String.fromCharCode(firstDigit << 4 | secondDigit));\n        }\n        isFirstHex = !isFirstHex;\n        ch = this.nextChar();\n      }\n    }\n    return strBuf.join(\"\");\n  }\n  getObj() {\n    let comment = false;\n    let ch = this.currentChar;\n    while (true) {\n      if (ch < 0) {\n        return EOF;\n      }\n      if (comment) {\n        if (ch === 0x0a || ch === 0x0d) {\n          comment = false;\n        }\n      } else if (ch === 0x25) {\n        comment = true;\n      } else if (specialChars[ch] !== 1) {\n        break;\n      }\n      ch = this.nextChar();\n    }\n    switch (ch | 0) {\n      case 0x30:\n      case 0x31:\n      case 0x32:\n      case 0x33:\n      case 0x34:\n      case 0x35:\n      case 0x36:\n      case 0x37:\n      case 0x38:\n      case 0x39:\n      case 0x2b:\n      case 0x2d:\n      case 0x2e:\n        return this.getNumber();\n      case 0x28:\n        return this.getString();\n      case 0x2f:\n        return this.getName();\n      case 0x5b:\n        this.nextChar();\n        return Cmd.get(\"[\");\n      case 0x5d:\n        this.nextChar();\n        return Cmd.get(\"]\");\n      case 0x3c:\n        ch = this.nextChar();\n        if (ch === 0x3c) {\n          this.nextChar();\n          return Cmd.get(\"<<\");\n        }\n        return this.getHexString();\n      case 0x3e:\n        ch = this.nextChar();\n        if (ch === 0x3e) {\n          this.nextChar();\n          return Cmd.get(\">>\");\n        }\n        return Cmd.get(\">\");\n      case 0x7b:\n        this.nextChar();\n        return Cmd.get(\"{\");\n      case 0x7d:\n        this.nextChar();\n        return Cmd.get(\"}\");\n      case 0x29:\n        this.nextChar();\n        throw new FormatError(`Illegal character: ${ch}`);\n    }\n    let str = String.fromCharCode(ch);\n    if (ch < 0x20 || ch > 0x7f) {\n      const nextCh = this.peekChar();\n      if (nextCh >= 0x20 && nextCh <= 0x7f) {\n        this.nextChar();\n        return Cmd.get(str);\n      }\n    }\n    const knownCommands = this.knownCommands;\n    let knownCommandFound = knownCommands?.[str] !== undefined;\n    while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {\n      const possibleCommand = str + String.fromCharCode(ch);\n      if (knownCommandFound && knownCommands[possibleCommand] === undefined) {\n        break;\n      }\n      if (str.length === 128) {\n        throw new FormatError(`Command token too long: ${str.length}`);\n      }\n      str = possibleCommand;\n      knownCommandFound = knownCommands?.[str] !== undefined;\n    }\n    if (str === \"true\") {\n      return true;\n    }\n    if (str === \"false\") {\n      return false;\n    }\n    if (str === \"null\") {\n      return null;\n    }\n    if (str === \"BI\") {\n      this.beginInlineImagePos = this.stream.pos;\n    }\n    return Cmd.get(str);\n  }\n  skipToNextLine() {\n    let ch = this.currentChar;\n    while (ch >= 0) {\n      if (ch === 0x0d) {\n        ch = this.nextChar();\n        if (ch === 0x0a) {\n          this.nextChar();\n        }\n        break;\n      } else if (ch === 0x0a) {\n        this.nextChar();\n        break;\n      }\n      ch = this.nextChar();\n    }\n  }\n}\nclass Linearization {\n  static create(stream) {\n    function getInt(linDict, name, allowZeroValue = false) {\n      const obj = linDict.get(name);\n      if (Number.isInteger(obj) && (allowZeroValue ? obj >= 0 : obj > 0)) {\n        return obj;\n      }\n      throw new Error(`The \"${name}\" parameter in the linearization ` + \"dictionary is invalid.\");\n    }\n    function getHints(linDict) {\n      const hints = linDict.get(\"H\");\n      let hintsLength;\n      if (Array.isArray(hints) && ((hintsLength = hints.length) === 2 || hintsLength === 4)) {\n        for (let index = 0; index < hintsLength; index++) {\n          const hint = hints[index];\n          if (!(Number.isInteger(hint) && hint > 0)) {\n            throw new Error(`Hint (${index}) in the linearization dictionary is invalid.`);\n          }\n        }\n        return hints;\n      }\n      throw new Error(\"Hint array in the linearization dictionary is invalid.\");\n    }\n    const parser = new Parser({\n      lexer: new Lexer(stream),\n      xref: null\n    });\n    const obj1 = parser.getObj();\n    const obj2 = parser.getObj();\n    const obj3 = parser.getObj();\n    const linDict = parser.getObj();\n    let obj, length;\n    if (!(Number.isInteger(obj1) && Number.isInteger(obj2) && isCmd(obj3, \"obj\") && linDict instanceof Dict && typeof (obj = linDict.get(\"Linearized\")) === \"number\" && obj > 0)) {\n      return null;\n    } else if ((length = getInt(linDict, \"L\")) !== stream.length) {\n      throw new Error('The \"L\" parameter in the linearization dictionary ' + \"does not equal the stream length.\");\n    }\n    return {\n      length,\n      hints: getHints(linDict),\n      objectNumberFirst: getInt(linDict, \"O\"),\n      endFirst: getInt(linDict, \"E\"),\n      numPages: getInt(linDict, \"N\"),\n      mainXRefEntriesOffset: getInt(linDict, \"T\"),\n      pageFirst: linDict.has(\"P\") ? getInt(linDict, \"P\", true) : 0\n    };\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/cmap.js\n\n\n\n\n\n\n\nconst BUILT_IN_CMAPS = [\"Adobe-GB1-UCS2\", \"Adobe-CNS1-UCS2\", \"Adobe-Japan1-UCS2\", \"Adobe-Korea1-UCS2\", \"78-EUC-H\", \"78-EUC-V\", \"78-H\", \"78-RKSJ-H\", \"78-RKSJ-V\", \"78-V\", \"78ms-RKSJ-H\", \"78ms-RKSJ-V\", \"83pv-RKSJ-H\", \"90ms-RKSJ-H\", \"90ms-RKSJ-V\", \"90msp-RKSJ-H\", \"90msp-RKSJ-V\", \"90pv-RKSJ-H\", \"90pv-RKSJ-V\", \"Add-H\", \"Add-RKSJ-H\", \"Add-RKSJ-V\", \"Add-V\", \"Adobe-CNS1-0\", \"Adobe-CNS1-1\", \"Adobe-CNS1-2\", \"Adobe-CNS1-3\", \"Adobe-CNS1-4\", \"Adobe-CNS1-5\", \"Adobe-CNS1-6\", \"Adobe-GB1-0\", \"Adobe-GB1-1\", \"Adobe-GB1-2\", \"Adobe-GB1-3\", \"Adobe-GB1-4\", \"Adobe-GB1-5\", \"Adobe-Japan1-0\", \"Adobe-Japan1-1\", \"Adobe-Japan1-2\", \"Adobe-Japan1-3\", \"Adobe-Japan1-4\", \"Adobe-Japan1-5\", \"Adobe-Japan1-6\", \"Adobe-Korea1-0\", \"Adobe-Korea1-1\", \"Adobe-Korea1-2\", \"B5-H\", \"B5-V\", \"B5pc-H\", \"B5pc-V\", \"CNS-EUC-H\", \"CNS-EUC-V\", \"CNS1-H\", \"CNS1-V\", \"CNS2-H\", \"CNS2-V\", \"ETHK-B5-H\", \"ETHK-B5-V\", \"ETen-B5-H\", \"ETen-B5-V\", \"ETenms-B5-H\", \"ETenms-B5-V\", \"EUC-H\", \"EUC-V\", \"Ext-H\", \"Ext-RKSJ-H\", \"Ext-RKSJ-V\", \"Ext-V\", \"GB-EUC-H\", \"GB-EUC-V\", \"GB-H\", \"GB-V\", \"GBK-EUC-H\", \"GBK-EUC-V\", \"GBK2K-H\", \"GBK2K-V\", \"GBKp-EUC-H\", \"GBKp-EUC-V\", \"GBT-EUC-H\", \"GBT-EUC-V\", \"GBT-H\", \"GBT-V\", \"GBTpc-EUC-H\", \"GBTpc-EUC-V\", \"GBpc-EUC-H\", \"GBpc-EUC-V\", \"H\", \"HKdla-B5-H\", \"HKdla-B5-V\", \"HKdlb-B5-H\", \"HKdlb-B5-V\", \"HKgccs-B5-H\", \"HKgccs-B5-V\", \"HKm314-B5-H\", \"HKm314-B5-V\", \"HKm471-B5-H\", \"HKm471-B5-V\", \"HKscs-B5-H\", \"HKscs-B5-V\", \"Hankaku\", \"Hiragana\", \"KSC-EUC-H\", \"KSC-EUC-V\", \"KSC-H\", \"KSC-Johab-H\", \"KSC-Johab-V\", \"KSC-V\", \"KSCms-UHC-H\", \"KSCms-UHC-HW-H\", \"KSCms-UHC-HW-V\", \"KSCms-UHC-V\", \"KSCpc-EUC-H\", \"KSCpc-EUC-V\", \"Katakana\", \"NWP-H\", \"NWP-V\", \"RKSJ-H\", \"RKSJ-V\", \"Roman\", \"UniCNS-UCS2-H\", \"UniCNS-UCS2-V\", \"UniCNS-UTF16-H\", \"UniCNS-UTF16-V\", \"UniCNS-UTF32-H\", \"UniCNS-UTF32-V\", \"UniCNS-UTF8-H\", \"UniCNS-UTF8-V\", \"UniGB-UCS2-H\", \"UniGB-UCS2-V\", \"UniGB-UTF16-H\", \"UniGB-UTF16-V\", \"UniGB-UTF32-H\", \"UniGB-UTF32-V\", \"UniGB-UTF8-H\", \"UniGB-UTF8-V\", \"UniJIS-UCS2-H\", \"UniJIS-UCS2-HW-H\", \"UniJIS-UCS2-HW-V\", \"UniJIS-UCS2-V\", \"UniJIS-UTF16-H\", \"UniJIS-UTF16-V\", \"UniJIS-UTF32-H\", \"UniJIS-UTF32-V\", \"UniJIS-UTF8-H\", \"UniJIS-UTF8-V\", \"UniJIS2004-UTF16-H\", \"UniJIS2004-UTF16-V\", \"UniJIS2004-UTF32-H\", \"UniJIS2004-UTF32-V\", \"UniJIS2004-UTF8-H\", \"UniJIS2004-UTF8-V\", \"UniJISPro-UCS2-HW-V\", \"UniJISPro-UCS2-V\", \"UniJISPro-UTF8-V\", \"UniJISX0213-UTF32-H\", \"UniJISX0213-UTF32-V\", \"UniJISX02132004-UTF32-H\", \"UniJISX02132004-UTF32-V\", \"UniKS-UCS2-H\", \"UniKS-UCS2-V\", \"UniKS-UTF16-H\", \"UniKS-UTF16-V\", \"UniKS-UTF32-H\", \"UniKS-UTF32-V\", \"UniKS-UTF8-H\", \"UniKS-UTF8-V\", \"V\", \"WP-Symbol\"];\nconst MAX_MAP_RANGE = 2 ** 24 - 1;\nclass CMap {\n  constructor(builtInCMap = false) {\n    this.codespaceRanges = [[], [], [], []];\n    this.numCodespaceRanges = 0;\n    this._map = [];\n    this.name = \"\";\n    this.vertical = false;\n    this.useCMap = null;\n    this.builtInCMap = builtInCMap;\n  }\n  addCodespaceRange(n, low, high) {\n    this.codespaceRanges[n - 1].push(low, high);\n    this.numCodespaceRanges++;\n  }\n  mapCidRange(low, high, dstLow) {\n    if (high - low > MAX_MAP_RANGE) {\n      throw new Error(\"mapCidRange - ignoring data above MAX_MAP_RANGE.\");\n    }\n    while (low <= high) {\n      this._map[low++] = dstLow++;\n    }\n  }\n  mapBfRange(low, high, dstLow) {\n    if (high - low > MAX_MAP_RANGE) {\n      throw new Error(\"mapBfRange - ignoring data above MAX_MAP_RANGE.\");\n    }\n    const lastByte = dstLow.length - 1;\n    while (low <= high) {\n      this._map[low++] = dstLow;\n      const nextCharCode = dstLow.charCodeAt(lastByte) + 1;\n      if (nextCharCode > 0xff) {\n        dstLow = dstLow.substring(0, lastByte - 1) + String.fromCharCode(dstLow.charCodeAt(lastByte - 1) + 1) + \"\\x00\";\n        continue;\n      }\n      dstLow = dstLow.substring(0, lastByte) + String.fromCharCode(nextCharCode);\n    }\n  }\n  mapBfRangeToArray(low, high, array) {\n    if (high - low > MAX_MAP_RANGE) {\n      throw new Error(\"mapBfRangeToArray - ignoring data above MAX_MAP_RANGE.\");\n    }\n    const ii = array.length;\n    let i = 0;\n    while (low <= high && i < ii) {\n      this._map[low] = array[i++];\n      ++low;\n    }\n  }\n  mapOne(src, dst) {\n    this._map[src] = dst;\n  }\n  lookup(code) {\n    return this._map[code];\n  }\n  contains(code) {\n    return this._map[code] !== undefined;\n  }\n  forEach(callback) {\n    const map = this._map;\n    const length = map.length;\n    if (length <= 0x10000) {\n      for (let i = 0; i < length; i++) {\n        if (map[i] !== undefined) {\n          callback(i, map[i]);\n        }\n      }\n    } else {\n      for (const i in map) {\n        callback(i, map[i]);\n      }\n    }\n  }\n  charCodeOf(value) {\n    const map = this._map;\n    if (map.length <= 0x10000) {\n      return map.indexOf(value);\n    }\n    for (const charCode in map) {\n      if (map[charCode] === value) {\n        return charCode | 0;\n      }\n    }\n    return -1;\n  }\n  getMap() {\n    return this._map;\n  }\n  readCharCode(str, offset, out) {\n    let c = 0;\n    const codespaceRanges = this.codespaceRanges;\n    for (let n = 0, nn = codespaceRanges.length; n < nn; n++) {\n      c = (c << 8 | str.charCodeAt(offset + n)) >>> 0;\n      const codespaceRange = codespaceRanges[n];\n      for (let k = 0, kk = codespaceRange.length; k < kk;) {\n        const low = codespaceRange[k++];\n        const high = codespaceRange[k++];\n        if (c >= low && c <= high) {\n          out.charcode = c;\n          out.length = n + 1;\n          return;\n        }\n      }\n    }\n    out.charcode = 0;\n    out.length = 1;\n  }\n  getCharCodeLength(charCode) {\n    const codespaceRanges = this.codespaceRanges;\n    for (let n = 0, nn = codespaceRanges.length; n < nn; n++) {\n      const codespaceRange = codespaceRanges[n];\n      for (let k = 0, kk = codespaceRange.length; k < kk;) {\n        const low = codespaceRange[k++];\n        const high = codespaceRange[k++];\n        if (charCode >= low && charCode <= high) {\n          return n + 1;\n        }\n      }\n    }\n    return 1;\n  }\n  get length() {\n    return this._map.length;\n  }\n  get isIdentityCMap() {\n    if (!(this.name === \"Identity-H\" || this.name === \"Identity-V\")) {\n      return false;\n    }\n    if (this._map.length !== 0x10000) {\n      return false;\n    }\n    for (let i = 0; i < 0x10000; i++) {\n      if (this._map[i] !== i) {\n        return false;\n      }\n    }\n    return true;\n  }\n}\nclass IdentityCMap extends CMap {\n  constructor(vertical, n) {\n    super();\n    this.vertical = vertical;\n    this.addCodespaceRange(n, 0, 0xffff);\n  }\n  mapCidRange(low, high, dstLow) {\n    unreachable(\"should not call mapCidRange\");\n  }\n  mapBfRange(low, high, dstLow) {\n    unreachable(\"should not call mapBfRange\");\n  }\n  mapBfRangeToArray(low, high, array) {\n    unreachable(\"should not call mapBfRangeToArray\");\n  }\n  mapOne(src, dst) {\n    unreachable(\"should not call mapCidOne\");\n  }\n  lookup(code) {\n    return Number.isInteger(code) && code <= 0xffff ? code : undefined;\n  }\n  contains(code) {\n    return Number.isInteger(code) && code <= 0xffff;\n  }\n  forEach(callback) {\n    for (let i = 0; i <= 0xffff; i++) {\n      callback(i, i);\n    }\n  }\n  charCodeOf(value) {\n    return Number.isInteger(value) && value <= 0xffff ? value : -1;\n  }\n  getMap() {\n    const map = new Array(0x10000);\n    for (let i = 0; i <= 0xffff; i++) {\n      map[i] = i;\n    }\n    return map;\n  }\n  get length() {\n    return 0x10000;\n  }\n  get isIdentityCMap() {\n    unreachable(\"should not access .isIdentityCMap\");\n  }\n}\nfunction strToInt(str) {\n  let a = 0;\n  for (let i = 0; i < str.length; i++) {\n    a = a << 8 | str.charCodeAt(i);\n  }\n  return a >>> 0;\n}\nfunction expectString(obj) {\n  if (typeof obj !== \"string\") {\n    throw new FormatError(\"Malformed CMap: expected string.\");\n  }\n}\nfunction expectInt(obj) {\n  if (!Number.isInteger(obj)) {\n    throw new FormatError(\"Malformed CMap: expected int.\");\n  }\n}\nfunction parseBfChar(cMap, lexer) {\n  while (true) {\n    let obj = lexer.getObj();\n    if (obj === EOF) {\n      break;\n    }\n    if (isCmd(obj, \"endbfchar\")) {\n      return;\n    }\n    expectString(obj);\n    const src = strToInt(obj);\n    obj = lexer.getObj();\n    expectString(obj);\n    const dst = obj;\n    cMap.mapOne(src, dst);\n  }\n}\nfunction parseBfRange(cMap, lexer) {\n  while (true) {\n    let obj = lexer.getObj();\n    if (obj === EOF) {\n      break;\n    }\n    if (isCmd(obj, \"endbfrange\")) {\n      return;\n    }\n    expectString(obj);\n    const low = strToInt(obj);\n    obj = lexer.getObj();\n    expectString(obj);\n    const high = strToInt(obj);\n    obj = lexer.getObj();\n    if (Number.isInteger(obj) || typeof obj === \"string\") {\n      const dstLow = Number.isInteger(obj) ? String.fromCharCode(obj) : obj;\n      cMap.mapBfRange(low, high, dstLow);\n    } else if (isCmd(obj, \"[\")) {\n      obj = lexer.getObj();\n      const array = [];\n      while (!isCmd(obj, \"]\") && obj !== EOF) {\n        array.push(obj);\n        obj = lexer.getObj();\n      }\n      cMap.mapBfRangeToArray(low, high, array);\n    } else {\n      break;\n    }\n  }\n  throw new FormatError(\"Invalid bf range.\");\n}\nfunction parseCidChar(cMap, lexer) {\n  while (true) {\n    let obj = lexer.getObj();\n    if (obj === EOF) {\n      break;\n    }\n    if (isCmd(obj, \"endcidchar\")) {\n      return;\n    }\n    expectString(obj);\n    const src = strToInt(obj);\n    obj = lexer.getObj();\n    expectInt(obj);\n    const dst = obj;\n    cMap.mapOne(src, dst);\n  }\n}\nfunction parseCidRange(cMap, lexer) {\n  while (true) {\n    let obj = lexer.getObj();\n    if (obj === EOF) {\n      break;\n    }\n    if (isCmd(obj, \"endcidrange\")) {\n      return;\n    }\n    expectString(obj);\n    const low = strToInt(obj);\n    obj = lexer.getObj();\n    expectString(obj);\n    const high = strToInt(obj);\n    obj = lexer.getObj();\n    expectInt(obj);\n    const dstLow = obj;\n    cMap.mapCidRange(low, high, dstLow);\n  }\n}\nfunction parseCodespaceRange(cMap, lexer) {\n  while (true) {\n    let obj = lexer.getObj();\n    if (obj === EOF) {\n      break;\n    }\n    if (isCmd(obj, \"endcodespacerange\")) {\n      return;\n    }\n    if (typeof obj !== \"string\") {\n      break;\n    }\n    const low = strToInt(obj);\n    obj = lexer.getObj();\n    if (typeof obj !== \"string\") {\n      break;\n    }\n    const high = strToInt(obj);\n    cMap.addCodespaceRange(obj.length, low, high);\n  }\n  throw new FormatError(\"Invalid codespace range.\");\n}\nfunction parseWMode(cMap, lexer) {\n  const obj = lexer.getObj();\n  if (Number.isInteger(obj)) {\n    cMap.vertical = !!obj;\n  }\n}\nfunction parseCMapName(cMap, lexer) {\n  const obj = lexer.getObj();\n  if (obj instanceof Name) {\n    cMap.name = obj.name;\n  }\n}\nasync function parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap) {\n  let previous, embeddedUseCMap;\n  objLoop: while (true) {\n    try {\n      const obj = lexer.getObj();\n      if (obj === EOF) {\n        break;\n      } else if (obj instanceof Name) {\n        if (obj.name === \"WMode\") {\n          parseWMode(cMap, lexer);\n        } else if (obj.name === \"CMapName\") {\n          parseCMapName(cMap, lexer);\n        }\n        previous = obj;\n      } else if (obj instanceof Cmd) {\n        switch (obj.cmd) {\n          case \"endcmap\":\n            break objLoop;\n          case \"usecmap\":\n            if (previous instanceof Name) {\n              embeddedUseCMap = previous.name;\n            }\n            break;\n          case \"begincodespacerange\":\n            parseCodespaceRange(cMap, lexer);\n            break;\n          case \"beginbfchar\":\n            parseBfChar(cMap, lexer);\n            break;\n          case \"begincidchar\":\n            parseCidChar(cMap, lexer);\n            break;\n          case \"beginbfrange\":\n            parseBfRange(cMap, lexer);\n            break;\n          case \"begincidrange\":\n            parseCidRange(cMap, lexer);\n            break;\n        }\n      }\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(\"Invalid cMap data: \" + ex);\n      continue;\n    }\n  }\n  if (!useCMap && embeddedUseCMap) {\n    useCMap = embeddedUseCMap;\n  }\n  if (useCMap) {\n    return extendCMap(cMap, fetchBuiltInCMap, useCMap);\n  }\n  return cMap;\n}\nasync function extendCMap(cMap, fetchBuiltInCMap, useCMap) {\n  cMap.useCMap = await createBuiltInCMap(useCMap, fetchBuiltInCMap);\n  if (cMap.numCodespaceRanges === 0) {\n    const useCodespaceRanges = cMap.useCMap.codespaceRanges;\n    for (let i = 0; i < useCodespaceRanges.length; i++) {\n      cMap.codespaceRanges[i] = useCodespaceRanges[i].slice();\n    }\n    cMap.numCodespaceRanges = cMap.useCMap.numCodespaceRanges;\n  }\n  cMap.useCMap.forEach(function (key, value) {\n    if (!cMap.contains(key)) {\n      cMap.mapOne(key, cMap.useCMap.lookup(key));\n    }\n  });\n  return cMap;\n}\nasync function createBuiltInCMap(name, fetchBuiltInCMap) {\n  if (name === \"Identity-H\") {\n    return new IdentityCMap(false, 2);\n  } else if (name === \"Identity-V\") {\n    return new IdentityCMap(true, 2);\n  }\n  if (!BUILT_IN_CMAPS.includes(name)) {\n    throw new Error(\"Unknown CMap name: \" + name);\n  }\n  if (!fetchBuiltInCMap) {\n    throw new Error(\"Built-in CMap parameters are not provided.\");\n  }\n  const {\n    cMapData,\n    compressionType\n  } = await fetchBuiltInCMap(name);\n  const cMap = new CMap(true);\n  if (compressionType === CMapCompressionType.BINARY) {\n    return new BinaryCMapReader().process(cMapData, cMap, useCMap => {\n      return extendCMap(cMap, fetchBuiltInCMap, useCMap);\n    });\n  }\n  if (compressionType === CMapCompressionType.NONE) {\n    const lexer = new Lexer(new Stream(cMapData));\n    return parseCMap(cMap, lexer, fetchBuiltInCMap, null);\n  }\n  throw new Error(`Invalid CMap \"compressionType\" value: ${compressionType}`);\n}\nclass CMapFactory {\n  static async create({\n    encoding,\n    fetchBuiltInCMap,\n    useCMap\n  }) {\n    if (encoding instanceof Name) {\n      return createBuiltInCMap(encoding.name, fetchBuiltInCMap);\n    } else if (encoding instanceof BaseStream) {\n      const parsedCMap = await parseCMap(new CMap(), new Lexer(encoding), fetchBuiltInCMap, useCMap);\n      if (parsedCMap.isIdentityCMap) {\n        return createBuiltInCMap(parsedCMap.name, fetchBuiltInCMap);\n      }\n      return parsedCMap;\n    }\n    throw new Error(\"Encoding required.\");\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/charsets.js\nconst ISOAdobeCharset = [\".notdef\", \"space\", \"exclam\", \"quotedbl\", \"numbersign\", \"dollar\", \"percent\", \"ampersand\", \"quoteright\", \"parenleft\", \"parenright\", \"asterisk\", \"plus\", \"comma\", \"hyphen\", \"period\", \"slash\", \"zero\", \"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\", \"colon\", \"semicolon\", \"less\", \"equal\", \"greater\", \"question\", \"at\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\", \"bracketleft\", \"backslash\", \"bracketright\", \"asciicircum\", \"underscore\", \"quoteleft\", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"braceleft\", \"bar\", \"braceright\", \"asciitilde\", \"exclamdown\", \"cent\", \"sterling\", \"fraction\", \"yen\", \"florin\", \"section\", \"currency\", \"quotesingle\", \"quotedblleft\", \"guillemotleft\", \"guilsinglleft\", \"guilsinglright\", \"fi\", \"fl\", \"endash\", \"dagger\", \"daggerdbl\", \"periodcentered\", \"paragraph\", \"bullet\", \"quotesinglbase\", \"quotedblbase\", \"quotedblright\", \"guillemotright\", \"ellipsis\", \"perthousand\", \"questiondown\", \"grave\", \"acute\", \"circumflex\", \"tilde\", \"macron\", \"breve\", \"dotaccent\", \"dieresis\", \"ring\", \"cedilla\", \"hungarumlaut\", \"ogonek\", \"caron\", \"emdash\", \"AE\", \"ordfeminine\", \"Lslash\", \"Oslash\", \"OE\", \"ordmasculine\", \"ae\", \"dotlessi\", \"lslash\", \"oslash\", \"oe\", \"germandbls\", \"onesuperior\", \"logicalnot\", \"mu\", \"trademark\", \"Eth\", \"onehalf\", \"plusminus\", \"Thorn\", \"onequarter\", \"divide\", \"brokenbar\", \"degree\", \"thorn\", \"threequarters\", \"twosuperior\", \"registered\", \"minus\", \"eth\", \"multiply\", \"threesuperior\", \"copyright\", \"Aacute\", \"Acircumflex\", \"Adieresis\", \"Agrave\", \"Aring\", \"Atilde\", \"Ccedilla\", \"Eacute\", \"Ecircumflex\", \"Edieresis\", \"Egrave\", \"Iacute\", \"Icircumflex\", \"Idieresis\", \"Igrave\", \"Ntilde\", \"Oacute\", \"Ocircumflex\", \"Odieresis\", \"Ograve\", \"Otilde\", \"Scaron\", \"Uacute\", \"Ucircumflex\", \"Udieresis\", \"Ugrave\", \"Yacute\", \"Ydieresis\", \"Zcaron\", \"aacute\", \"acircumflex\", \"adieresis\", \"agrave\", \"aring\", \"atilde\", \"ccedilla\", \"eacute\", \"ecircumflex\", \"edieresis\", \"egrave\", \"iacute\", \"icircumflex\", \"idieresis\", \"igrave\", \"ntilde\", \"oacute\", \"ocircumflex\", \"odieresis\", \"ograve\", \"otilde\", \"scaron\", \"uacute\", \"ucircumflex\", \"udieresis\", \"ugrave\", \"yacute\", \"ydieresis\", \"zcaron\"];\nconst ExpertCharset = [\".notdef\", \"space\", \"exclamsmall\", \"Hungarumlautsmall\", \"dollaroldstyle\", \"dollarsuperior\", \"ampersandsmall\", \"Acutesmall\", \"parenleftsuperior\", \"parenrightsuperior\", \"twodotenleader\", \"onedotenleader\", \"comma\", \"hyphen\", \"period\", \"fraction\", \"zerooldstyle\", \"oneoldstyle\", \"twooldstyle\", \"threeoldstyle\", \"fouroldstyle\", \"fiveoldstyle\", \"sixoldstyle\", \"sevenoldstyle\", \"eightoldstyle\", \"nineoldstyle\", \"colon\", \"semicolon\", \"commasuperior\", \"threequartersemdash\", \"periodsuperior\", \"questionsmall\", \"asuperior\", \"bsuperior\", \"centsuperior\", \"dsuperior\", \"esuperior\", \"isuperior\", \"lsuperior\", \"msuperior\", \"nsuperior\", \"osuperior\", \"rsuperior\", \"ssuperior\", \"tsuperior\", \"ff\", \"fi\", \"fl\", \"ffi\", \"ffl\", \"parenleftinferior\", \"parenrightinferior\", \"Circumflexsmall\", \"hyphensuperior\", \"Gravesmall\", \"Asmall\", \"Bsmall\", \"Csmall\", \"Dsmall\", \"Esmall\", \"Fsmall\", \"Gsmall\", \"Hsmall\", \"Ismall\", \"Jsmall\", \"Ksmall\", \"Lsmall\", \"Msmall\", \"Nsmall\", \"Osmall\", \"Psmall\", \"Qsmall\", \"Rsmall\", \"Ssmall\", \"Tsmall\", \"Usmall\", \"Vsmall\", \"Wsmall\", \"Xsmall\", \"Ysmall\", \"Zsmall\", \"colonmonetary\", \"onefitted\", \"rupiah\", \"Tildesmall\", \"exclamdownsmall\", \"centoldstyle\", \"Lslashsmall\", \"Scaronsmall\", \"Zcaronsmall\", \"Dieresissmall\", \"Brevesmall\", \"Caronsmall\", \"Dotaccentsmall\", \"Macronsmall\", \"figuredash\", \"hypheninferior\", \"Ogoneksmall\", \"Ringsmall\", \"Cedillasmall\", \"onequarter\", \"onehalf\", \"threequarters\", \"questiondownsmall\", \"oneeighth\", \"threeeighths\", \"fiveeighths\", \"seveneighths\", \"onethird\", \"twothirds\", \"zerosuperior\", \"onesuperior\", \"twosuperior\", \"threesuperior\", \"foursuperior\", \"fivesuperior\", \"sixsuperior\", \"sevensuperior\", \"eightsuperior\", \"ninesuperior\", \"zeroinferior\", \"oneinferior\", \"twoinferior\", \"threeinferior\", \"fourinferior\", \"fiveinferior\", \"sixinferior\", \"seveninferior\", \"eightinferior\", \"nineinferior\", \"centinferior\", \"dollarinferior\", \"periodinferior\", \"commainferior\", \"Agravesmall\", \"Aacutesmall\", \"Acircumflexsmall\", \"Atildesmall\", \"Adieresissmall\", \"Aringsmall\", \"AEsmall\", \"Ccedillasmall\", \"Egravesmall\", \"Eacutesmall\", \"Ecircumflexsmall\", \"Edieresissmall\", \"Igravesmall\", \"Iacutesmall\", \"Icircumflexsmall\", \"Idieresissmall\", \"Ethsmall\", \"Ntildesmall\", \"Ogravesmall\", \"Oacutesmall\", \"Ocircumflexsmall\", \"Otildesmall\", \"Odieresissmall\", \"OEsmall\", \"Oslashsmall\", \"Ugravesmall\", \"Uacutesmall\", \"Ucircumflexsmall\", \"Udieresissmall\", \"Yacutesmall\", \"Thornsmall\", \"Ydieresissmall\"];\nconst ExpertSubsetCharset = [\".notdef\", \"space\", \"dollaroldstyle\", \"dollarsuperior\", \"parenleftsuperior\", \"parenrightsuperior\", \"twodotenleader\", \"onedotenleader\", \"comma\", \"hyphen\", \"period\", \"fraction\", \"zerooldstyle\", \"oneoldstyle\", \"twooldstyle\", \"threeoldstyle\", \"fouroldstyle\", \"fiveoldstyle\", \"sixoldstyle\", \"sevenoldstyle\", \"eightoldstyle\", \"nineoldstyle\", \"colon\", \"semicolon\", \"commasuperior\", \"threequartersemdash\", \"periodsuperior\", \"asuperior\", \"bsuperior\", \"centsuperior\", \"dsuperior\", \"esuperior\", \"isuperior\", \"lsuperior\", \"msuperior\", \"nsuperior\", \"osuperior\", \"rsuperior\", \"ssuperior\", \"tsuperior\", \"ff\", \"fi\", \"fl\", \"ffi\", \"ffl\", \"parenleftinferior\", \"parenrightinferior\", \"hyphensuperior\", \"colonmonetary\", \"onefitted\", \"rupiah\", \"centoldstyle\", \"figuredash\", \"hypheninferior\", \"onequarter\", \"onehalf\", \"threequarters\", \"oneeighth\", \"threeeighths\", \"fiveeighths\", \"seveneighths\", \"onethird\", \"twothirds\", \"zerosuperior\", \"onesuperior\", \"twosuperior\", \"threesuperior\", \"foursuperior\", \"fivesuperior\", \"sixsuperior\", \"sevensuperior\", \"eightsuperior\", \"ninesuperior\", \"zeroinferior\", \"oneinferior\", \"twoinferior\", \"threeinferior\", \"fourinferior\", \"fiveinferior\", \"sixinferior\", \"seveninferior\", \"eightinferior\", \"nineinferior\", \"centinferior\", \"dollarinferior\", \"periodinferior\", \"commainferior\"];\n\n;// CONCATENATED MODULE: ./src/core/encodings.js\nconst ExpertEncoding = [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"space\", \"exclamsmall\", \"Hungarumlautsmall\", \"\", \"dollaroldstyle\", \"dollarsuperior\", \"ampersandsmall\", \"Acutesmall\", \"parenleftsuperior\", \"parenrightsuperior\", \"twodotenleader\", \"onedotenleader\", \"comma\", \"hyphen\", \"period\", \"fraction\", \"zerooldstyle\", \"oneoldstyle\", \"twooldstyle\", \"threeoldstyle\", \"fouroldstyle\", \"fiveoldstyle\", \"sixoldstyle\", \"sevenoldstyle\", \"eightoldstyle\", \"nineoldstyle\", \"colon\", \"semicolon\", \"commasuperior\", \"threequartersemdash\", \"periodsuperior\", \"questionsmall\", \"\", \"asuperior\", \"bsuperior\", \"centsuperior\", \"dsuperior\", \"esuperior\", \"\", \"\", \"\", \"isuperior\", \"\", \"\", \"lsuperior\", \"msuperior\", \"nsuperior\", \"osuperior\", \"\", \"\", \"rsuperior\", \"ssuperior\", \"tsuperior\", \"\", \"ff\", \"fi\", \"fl\", \"ffi\", \"ffl\", \"parenleftinferior\", \"\", \"parenrightinferior\", \"Circumflexsmall\", \"hyphensuperior\", \"Gravesmall\", \"Asmall\", \"Bsmall\", \"Csmall\", \"Dsmall\", \"Esmall\", \"Fsmall\", \"Gsmall\", \"Hsmall\", \"Ismall\", \"Jsmall\", \"Ksmall\", \"Lsmall\", \"Msmall\", \"Nsmall\", \"Osmall\", \"Psmall\", \"Qsmall\", \"Rsmall\", \"Ssmall\", \"Tsmall\", \"Usmall\", \"Vsmall\", \"Wsmall\", \"Xsmall\", \"Ysmall\", \"Zsmall\", \"colonmonetary\", \"onefitted\", \"rupiah\", \"Tildesmall\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"exclamdownsmall\", \"centoldstyle\", \"Lslashsmall\", \"\", \"\", \"Scaronsmall\", \"Zcaronsmall\", \"Dieresissmall\", \"Brevesmall\", \"Caronsmall\", \"\", \"Dotaccentsmall\", \"\", \"\", \"Macronsmall\", \"\", \"\", \"figuredash\", \"hypheninferior\", \"\", \"\", \"Ogoneksmall\", \"Ringsmall\", \"Cedillasmall\", \"\", \"\", \"\", \"onequarter\", \"onehalf\", \"threequarters\", \"questiondownsmall\", \"oneeighth\", \"threeeighths\", \"fiveeighths\", \"seveneighths\", \"onethird\", \"twothirds\", \"\", \"\", \"zerosuperior\", \"onesuperior\", \"twosuperior\", \"threesuperior\", \"foursuperior\", \"fivesuperior\", \"sixsuperior\", \"sevensuperior\", \"eightsuperior\", \"ninesuperior\", \"zeroinferior\", \"oneinferior\", \"twoinferior\", \"threeinferior\", \"fourinferior\", \"fiveinferior\", \"sixinferior\", \"seveninferior\", \"eightinferior\", \"nineinferior\", \"centinferior\", \"dollarinferior\", \"periodinferior\", \"commainferior\", \"Agravesmall\", \"Aacutesmall\", \"Acircumflexsmall\", \"Atildesmall\", \"Adieresissmall\", \"Aringsmall\", \"AEsmall\", \"Ccedillasmall\", \"Egravesmall\", \"Eacutesmall\", \"Ecircumflexsmall\", \"Edieresissmall\", \"Igravesmall\", \"Iacutesmall\", \"Icircumflexsmall\", \"Idieresissmall\", \"Ethsmall\", \"Ntildesmall\", \"Ogravesmall\", \"Oacutesmall\", \"Ocircumflexsmall\", \"Otildesmall\", \"Odieresissmall\", \"OEsmall\", \"Oslashsmall\", \"Ugravesmall\", \"Uacutesmall\", \"Ucircumflexsmall\", \"Udieresissmall\", \"Yacutesmall\", \"Thornsmall\", \"Ydieresissmall\"];\nconst MacExpertEncoding = [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"space\", \"exclamsmall\", \"Hungarumlautsmall\", \"centoldstyle\", \"dollaroldstyle\", \"dollarsuperior\", \"ampersandsmall\", \"Acutesmall\", \"parenleftsuperior\", \"parenrightsuperior\", \"twodotenleader\", \"onedotenleader\", \"comma\", \"hyphen\", \"period\", \"fraction\", \"zerooldstyle\", \"oneoldstyle\", \"twooldstyle\", \"threeoldstyle\", \"fouroldstyle\", \"fiveoldstyle\", \"sixoldstyle\", \"sevenoldstyle\", \"eightoldstyle\", \"nineoldstyle\", \"colon\", \"semicolon\", \"\", \"threequartersemdash\", \"\", \"questionsmall\", \"\", \"\", \"\", \"\", \"Ethsmall\", \"\", \"\", \"onequarter\", \"onehalf\", \"threequarters\", \"oneeighth\", \"threeeighths\", \"fiveeighths\", \"seveneighths\", \"onethird\", \"twothirds\", \"\", \"\", \"\", \"\", \"\", \"\", \"ff\", \"fi\", \"fl\", \"ffi\", \"ffl\", \"parenleftinferior\", \"\", \"parenrightinferior\", \"Circumflexsmall\", \"hypheninferior\", \"Gravesmall\", \"Asmall\", \"Bsmall\", \"Csmall\", \"Dsmall\", \"Esmall\", \"Fsmall\", \"Gsmall\", \"Hsmall\", \"Ismall\", \"Jsmall\", \"Ksmall\", \"Lsmall\", \"Msmall\", \"Nsmall\", \"Osmall\", \"Psmall\", \"Qsmall\", \"Rsmall\", \"Ssmall\", \"Tsmall\", \"Usmall\", \"Vsmall\", \"Wsmall\", \"Xsmall\", \"Ysmall\", \"Zsmall\", \"colonmonetary\", \"onefitted\", \"rupiah\", \"Tildesmall\", \"\", \"\", \"asuperior\", \"centsuperior\", \"\", \"\", \"\", \"\", \"Aacutesmall\", \"Agravesmall\", \"Acircumflexsmall\", \"Adieresissmall\", \"Atildesmall\", \"Aringsmall\", \"Ccedillasmall\", \"Eacutesmall\", \"Egravesmall\", \"Ecircumflexsmall\", \"Edieresissmall\", \"Iacutesmall\", \"Igravesmall\", \"Icircumflexsmall\", \"Idieresissmall\", \"Ntildesmall\", \"Oacutesmall\", \"Ogravesmall\", \"Ocircumflexsmall\", \"Odieresissmall\", \"Otildesmall\", \"Uacutesmall\", \"Ugravesmall\", \"Ucircumflexsmall\", \"Udieresissmall\", \"\", \"eightsuperior\", \"fourinferior\", \"threeinferior\", \"sixinferior\", \"eightinferior\", \"seveninferior\", \"Scaronsmall\", \"\", \"centinferior\", \"twoinferior\", \"\", \"Dieresissmall\", \"\", \"Caronsmall\", \"osuperior\", \"fiveinferior\", \"\", \"commainferior\", \"periodinferior\", \"Yacutesmall\", \"\", \"dollarinferior\", \"\", \"\", \"Thornsmall\", \"\", \"nineinferior\", \"zeroinferior\", \"Zcaronsmall\", \"AEsmall\", \"Oslashsmall\", \"questiondownsmall\", \"oneinferior\", \"Lslashsmall\", \"\", \"\", \"\", \"\", \"\", \"\", \"Cedillasmall\", \"\", \"\", \"\", \"\", \"\", \"OEsmall\", \"figuredash\", \"hyphensuperior\", \"\", \"\", \"\", \"\", \"exclamdownsmall\", \"\", \"Ydieresissmall\", \"\", \"onesuperior\", \"twosuperior\", \"threesuperior\", \"foursuperior\", \"fivesuperior\", \"sixsuperior\", \"sevensuperior\", \"ninesuperior\", \"zerosuperior\", \"\", \"esuperior\", \"rsuperior\", \"tsuperior\", \"\", \"\", \"isuperior\", \"ssuperior\", \"dsuperior\", \"\", \"\", \"\", \"\", \"\", \"lsuperior\", \"Ogoneksmall\", \"Brevesmall\", \"Macronsmall\", \"bsuperior\", \"nsuperior\", \"msuperior\", \"commasuperior\", \"periodsuperior\", \"Dotaccentsmall\", \"Ringsmall\", \"\", \"\", \"\", \"\"];\nconst MacRomanEncoding = [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"space\", \"exclam\", \"quotedbl\", \"numbersign\", \"dollar\", \"percent\", \"ampersand\", \"quotesingle\", \"parenleft\", \"parenright\", \"asterisk\", \"plus\", \"comma\", \"hyphen\", \"period\", \"slash\", \"zero\", \"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\", \"colon\", \"semicolon\", \"less\", \"equal\", \"greater\", \"question\", \"at\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\", \"bracketleft\", \"backslash\", \"bracketright\", \"asciicircum\", \"underscore\", \"grave\", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"braceleft\", \"bar\", \"braceright\", \"asciitilde\", \"\", \"Adieresis\", \"Aring\", \"Ccedilla\", \"Eacute\", \"Ntilde\", \"Odieresis\", \"Udieresis\", \"aacute\", \"agrave\", \"acircumflex\", \"adieresis\", \"atilde\", \"aring\", \"ccedilla\", \"eacute\", \"egrave\", \"ecircumflex\", \"edieresis\", \"iacute\", \"igrave\", \"icircumflex\", \"idieresis\", \"ntilde\", \"oacute\", \"ograve\", \"ocircumflex\", \"odieresis\", \"otilde\", \"uacute\", \"ugrave\", \"ucircumflex\", \"udieresis\", \"dagger\", \"degree\", \"cent\", \"sterling\", \"section\", \"bullet\", \"paragraph\", \"germandbls\", \"registered\", \"copyright\", \"trademark\", \"acute\", \"dieresis\", \"notequal\", \"AE\", \"Oslash\", \"infinity\", \"plusminus\", \"lessequal\", \"greaterequal\", \"yen\", \"mu\", \"partialdiff\", \"summation\", \"product\", \"pi\", \"integral\", \"ordfeminine\", \"ordmasculine\", \"Omega\", \"ae\", \"oslash\", \"questiondown\", \"exclamdown\", \"logicalnot\", \"radical\", \"florin\", \"approxequal\", \"Delta\", \"guillemotleft\", \"guillemotright\", \"ellipsis\", \"space\", \"Agrave\", \"Atilde\", \"Otilde\", \"OE\", \"oe\", \"endash\", \"emdash\", \"quotedblleft\", \"quotedblright\", \"quoteleft\", \"quoteright\", \"divide\", \"lozenge\", \"ydieresis\", \"Ydieresis\", \"fraction\", \"currency\", \"guilsinglleft\", \"guilsinglright\", \"fi\", \"fl\", \"daggerdbl\", \"periodcentered\", \"quotesinglbase\", \"quotedblbase\", \"perthousand\", \"Acircumflex\", \"Ecircumflex\", \"Aacute\", \"Edieresis\", \"Egrave\", \"Iacute\", \"Icircumflex\", \"Idieresis\", \"Igrave\", \"Oacute\", \"Ocircumflex\", \"apple\", \"Ograve\", \"Uacute\", \"Ucircumflex\", \"Ugrave\", \"dotlessi\", \"circumflex\", \"tilde\", \"macron\", \"breve\", \"dotaccent\", \"ring\", \"cedilla\", \"hungarumlaut\", \"ogonek\", \"caron\"];\nconst StandardEncoding = [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"space\", \"exclam\", \"quotedbl\", \"numbersign\", \"dollar\", \"percent\", \"ampersand\", \"quoteright\", \"parenleft\", \"parenright\", \"asterisk\", \"plus\", \"comma\", \"hyphen\", \"period\", \"slash\", \"zero\", \"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\", \"colon\", \"semicolon\", \"less\", \"equal\", \"greater\", \"question\", \"at\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\", \"bracketleft\", \"backslash\", \"bracketright\", \"asciicircum\", \"underscore\", \"quoteleft\", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"braceleft\", \"bar\", \"braceright\", \"asciitilde\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"exclamdown\", \"cent\", \"sterling\", \"fraction\", \"yen\", \"florin\", \"section\", \"currency\", \"quotesingle\", \"quotedblleft\", \"guillemotleft\", \"guilsinglleft\", \"guilsinglright\", \"fi\", \"fl\", \"\", \"endash\", \"dagger\", \"daggerdbl\", \"periodcentered\", \"\", \"paragraph\", \"bullet\", \"quotesinglbase\", \"quotedblbase\", \"quotedblright\", \"guillemotright\", \"ellipsis\", \"perthousand\", \"\", \"questiondown\", \"\", \"grave\", \"acute\", \"circumflex\", \"tilde\", \"macron\", \"breve\", \"dotaccent\", \"dieresis\", \"\", \"ring\", \"cedilla\", \"\", \"hungarumlaut\", \"ogonek\", \"caron\", \"emdash\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"AE\", \"\", \"ordfeminine\", \"\", \"\", \"\", \"\", \"Lslash\", \"Oslash\", \"OE\", \"ordmasculine\", \"\", \"\", \"\", \"\", \"\", \"ae\", \"\", \"\", \"\", \"dotlessi\", \"\", \"\", \"lslash\", \"oslash\", \"oe\", \"germandbls\", \"\", \"\", \"\", \"\"];\nconst WinAnsiEncoding = [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"space\", \"exclam\", \"quotedbl\", \"numbersign\", \"dollar\", \"percent\", \"ampersand\", \"quotesingle\", \"parenleft\", \"parenright\", \"asterisk\", \"plus\", \"comma\", \"hyphen\", \"period\", \"slash\", \"zero\", \"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\", \"colon\", \"semicolon\", \"less\", \"equal\", \"greater\", \"question\", \"at\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\", \"bracketleft\", \"backslash\", \"bracketright\", \"asciicircum\", \"underscore\", \"grave\", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"braceleft\", \"bar\", \"braceright\", \"asciitilde\", \"bullet\", \"Euro\", \"bullet\", \"quotesinglbase\", \"florin\", \"quotedblbase\", \"ellipsis\", \"dagger\", \"daggerdbl\", \"circumflex\", \"perthousand\", \"Scaron\", \"guilsinglleft\", \"OE\", \"bullet\", \"Zcaron\", \"bullet\", \"bullet\", \"quoteleft\", \"quoteright\", \"quotedblleft\", \"quotedblright\", \"bullet\", \"endash\", \"emdash\", \"tilde\", \"trademark\", \"scaron\", \"guilsinglright\", \"oe\", \"bullet\", \"zcaron\", \"Ydieresis\", \"space\", \"exclamdown\", \"cent\", \"sterling\", \"currency\", \"yen\", \"brokenbar\", \"section\", \"dieresis\", \"copyright\", \"ordfeminine\", \"guillemotleft\", \"logicalnot\", \"hyphen\", \"registered\", \"macron\", \"degree\", \"plusminus\", \"twosuperior\", \"threesuperior\", \"acute\", \"mu\", \"paragraph\", \"periodcentered\", \"cedilla\", \"onesuperior\", \"ordmasculine\", \"guillemotright\", \"onequarter\", \"onehalf\", \"threequarters\", \"questiondown\", \"Agrave\", \"Aacute\", \"Acircumflex\", \"Atilde\", \"Adieresis\", \"Aring\", \"AE\", \"Ccedilla\", \"Egrave\", \"Eacute\", \"Ecircumflex\", \"Edieresis\", \"Igrave\", \"Iacute\", \"Icircumflex\", \"Idieresis\", \"Eth\", \"Ntilde\", \"Ograve\", \"Oacute\", \"Ocircumflex\", \"Otilde\", \"Odieresis\", \"multiply\", \"Oslash\", \"Ugrave\", \"Uacute\", \"Ucircumflex\", \"Udieresis\", \"Yacute\", \"Thorn\", \"germandbls\", \"agrave\", \"aacute\", \"acircumflex\", \"atilde\", \"adieresis\", \"aring\", \"ae\", \"ccedilla\", \"egrave\", \"eacute\", \"ecircumflex\", \"edieresis\", \"igrave\", \"iacute\", \"icircumflex\", \"idieresis\", \"eth\", \"ntilde\", \"ograve\", \"oacute\", \"ocircumflex\", \"otilde\", \"odieresis\", \"divide\", \"oslash\", \"ugrave\", \"uacute\", \"ucircumflex\", \"udieresis\", \"yacute\", \"thorn\", \"ydieresis\"];\nconst SymbolSetEncoding = [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"space\", \"exclam\", \"universal\", \"numbersign\", \"existential\", \"percent\", \"ampersand\", \"suchthat\", \"parenleft\", \"parenright\", \"asteriskmath\", \"plus\", \"comma\", \"minus\", \"period\", \"slash\", \"zero\", \"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\", \"colon\", \"semicolon\", \"less\", \"equal\", \"greater\", \"question\", \"congruent\", \"Alpha\", \"Beta\", \"Chi\", \"Delta\", \"Epsilon\", \"Phi\", \"Gamma\", \"Eta\", \"Iota\", \"theta1\", \"Kappa\", \"Lambda\", \"Mu\", \"Nu\", \"Omicron\", \"Pi\", \"Theta\", \"Rho\", \"Sigma\", \"Tau\", \"Upsilon\", \"sigma1\", \"Omega\", \"Xi\", \"Psi\", \"Zeta\", \"bracketleft\", \"therefore\", \"bracketright\", \"perpendicular\", \"underscore\", \"radicalex\", \"alpha\", \"beta\", \"chi\", \"delta\", \"epsilon\", \"phi\", \"gamma\", \"eta\", \"iota\", \"phi1\", \"kappa\", \"lambda\", \"mu\", \"nu\", \"omicron\", \"pi\", \"theta\", \"rho\", \"sigma\", \"tau\", \"upsilon\", \"omega1\", \"omega\", \"xi\", \"psi\", \"zeta\", \"braceleft\", \"bar\", \"braceright\", \"similar\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"Euro\", \"Upsilon1\", \"minute\", \"lessequal\", \"fraction\", \"infinity\", \"florin\", \"club\", \"diamond\", \"heart\", \"spade\", \"arrowboth\", \"arrowleft\", \"arrowup\", \"arrowright\", \"arrowdown\", \"degree\", \"plusminus\", \"second\", \"greaterequal\", \"multiply\", \"proportional\", \"partialdiff\", \"bullet\", \"divide\", \"notequal\", \"equivalence\", \"approxequal\", \"ellipsis\", \"arrowvertex\", \"arrowhorizex\", \"carriagereturn\", \"aleph\", \"Ifraktur\", \"Rfraktur\", \"weierstrass\", \"circlemultiply\", \"circleplus\", \"emptyset\", \"intersection\", \"union\", \"propersuperset\", \"reflexsuperset\", \"notsubset\", \"propersubset\", \"reflexsubset\", \"element\", \"notelement\", \"angle\", \"gradient\", \"registerserif\", \"copyrightserif\", \"trademarkserif\", \"product\", \"radical\", \"dotmath\", \"logicalnot\", \"logicaland\", \"logicalor\", \"arrowdblboth\", \"arrowdblleft\", \"arrowdblup\", \"arrowdblright\", \"arrowdbldown\", \"lozenge\", \"angleleft\", \"registersans\", \"copyrightsans\", \"trademarksans\", \"summation\", \"parenlefttp\", \"parenleftex\", \"parenleftbt\", \"bracketlefttp\", \"bracketleftex\", \"bracketleftbt\", \"bracelefttp\", \"braceleftmid\", \"braceleftbt\", \"braceex\", \"\", \"angleright\", \"integral\", \"integraltp\", \"integralex\", \"integralbt\", \"parenrighttp\", \"parenrightex\", \"parenrightbt\", \"bracketrighttp\", \"bracketrightex\", \"bracketrightbt\", \"bracerighttp\", \"bracerightmid\", \"bracerightbt\", \"\"];\nconst ZapfDingbatsEncoding = [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"space\", \"a1\", \"a2\", \"a202\", \"a3\", \"a4\", \"a5\", \"a119\", \"a118\", \"a117\", \"a11\", \"a12\", \"a13\", \"a14\", \"a15\", \"a16\", \"a105\", \"a17\", \"a18\", \"a19\", \"a20\", \"a21\", \"a22\", \"a23\", \"a24\", \"a25\", \"a26\", \"a27\", \"a28\", \"a6\", \"a7\", \"a8\", \"a9\", \"a10\", \"a29\", \"a30\", \"a31\", \"a32\", \"a33\", \"a34\", \"a35\", \"a36\", \"a37\", \"a38\", \"a39\", \"a40\", \"a41\", \"a42\", \"a43\", \"a44\", \"a45\", \"a46\", \"a47\", \"a48\", \"a49\", \"a50\", \"a51\", \"a52\", \"a53\", \"a54\", \"a55\", \"a56\", \"a57\", \"a58\", \"a59\", \"a60\", \"a61\", \"a62\", \"a63\", \"a64\", \"a65\", \"a66\", \"a67\", \"a68\", \"a69\", \"a70\", \"a71\", \"a72\", \"a73\", \"a74\", \"a203\", \"a75\", \"a204\", \"a76\", \"a77\", \"a78\", \"a79\", \"a81\", \"a82\", \"a83\", \"a84\", \"a97\", \"a98\", \"a99\", \"a100\", \"\", \"a89\", \"a90\", \"a93\", \"a94\", \"a91\", \"a92\", \"a205\", \"a85\", \"a206\", \"a86\", \"a87\", \"a88\", \"a95\", \"a96\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"a101\", \"a102\", \"a103\", \"a104\", \"a106\", \"a107\", \"a108\", \"a112\", \"a111\", \"a110\", \"a109\", \"a120\", \"a121\", \"a122\", \"a123\", \"a124\", \"a125\", \"a126\", \"a127\", \"a128\", \"a129\", \"a130\", \"a131\", \"a132\", \"a133\", \"a134\", \"a135\", \"a136\", \"a137\", \"a138\", \"a139\", \"a140\", \"a141\", \"a142\", \"a143\", \"a144\", \"a145\", \"a146\", \"a147\", \"a148\", \"a149\", \"a150\", \"a151\", \"a152\", \"a153\", \"a154\", \"a155\", \"a156\", \"a157\", \"a158\", \"a159\", \"a160\", \"a161\", \"a163\", \"a164\", \"a196\", \"a165\", \"a192\", \"a166\", \"a167\", \"a168\", \"a169\", \"a170\", \"a171\", \"a172\", \"a173\", \"a162\", \"a174\", \"a175\", \"a176\", \"a177\", \"a178\", \"a179\", \"a193\", \"a180\", \"a199\", \"a181\", \"a200\", \"a182\", \"\", \"a201\", \"a183\", \"a184\", \"a197\", \"a185\", \"a194\", \"a198\", \"a186\", \"a195\", \"a187\", \"a188\", \"a189\", \"a190\", \"a191\", \"\"];\nfunction getEncoding(encodingName) {\n  switch (encodingName) {\n    case \"WinAnsiEncoding\":\n      return WinAnsiEncoding;\n    case \"StandardEncoding\":\n      return StandardEncoding;\n    case \"MacRomanEncoding\":\n      return MacRomanEncoding;\n    case \"SymbolSetEncoding\":\n      return SymbolSetEncoding;\n    case \"ZapfDingbatsEncoding\":\n      return ZapfDingbatsEncoding;\n    case \"ExpertEncoding\":\n      return ExpertEncoding;\n    case \"MacExpertEncoding\":\n      return MacExpertEncoding;\n    default:\n      return null;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/cff_parser.js\n\n\n\nconst MAX_SUBR_NESTING = 10;\nconst CFFStandardStrings = [\".notdef\", \"space\", \"exclam\", \"quotedbl\", \"numbersign\", \"dollar\", \"percent\", \"ampersand\", \"quoteright\", \"parenleft\", \"parenright\", \"asterisk\", \"plus\", \"comma\", \"hyphen\", \"period\", \"slash\", \"zero\", \"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\", \"colon\", \"semicolon\", \"less\", \"equal\", \"greater\", \"question\", \"at\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\", \"bracketleft\", \"backslash\", \"bracketright\", \"asciicircum\", \"underscore\", \"quoteleft\", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"braceleft\", \"bar\", \"braceright\", \"asciitilde\", \"exclamdown\", \"cent\", \"sterling\", \"fraction\", \"yen\", \"florin\", \"section\", \"currency\", \"quotesingle\", \"quotedblleft\", \"guillemotleft\", \"guilsinglleft\", \"guilsinglright\", \"fi\", \"fl\", \"endash\", \"dagger\", \"daggerdbl\", \"periodcentered\", \"paragraph\", \"bullet\", \"quotesinglbase\", \"quotedblbase\", \"quotedblright\", \"guillemotright\", \"ellipsis\", \"perthousand\", \"questiondown\", \"grave\", \"acute\", \"circumflex\", \"tilde\", \"macron\", \"breve\", \"dotaccent\", \"dieresis\", \"ring\", \"cedilla\", \"hungarumlaut\", \"ogonek\", \"caron\", \"emdash\", \"AE\", \"ordfeminine\", \"Lslash\", \"Oslash\", \"OE\", \"ordmasculine\", \"ae\", \"dotlessi\", \"lslash\", \"oslash\", \"oe\", \"germandbls\", \"onesuperior\", \"logicalnot\", \"mu\", \"trademark\", \"Eth\", \"onehalf\", \"plusminus\", \"Thorn\", \"onequarter\", \"divide\", \"brokenbar\", \"degree\", \"thorn\", \"threequarters\", \"twosuperior\", \"registered\", \"minus\", \"eth\", \"multiply\", \"threesuperior\", \"copyright\", \"Aacute\", \"Acircumflex\", \"Adieresis\", \"Agrave\", \"Aring\", \"Atilde\", \"Ccedilla\", \"Eacute\", \"Ecircumflex\", \"Edieresis\", \"Egrave\", \"Iacute\", \"Icircumflex\", \"Idieresis\", \"Igrave\", \"Ntilde\", \"Oacute\", \"Ocircumflex\", \"Odieresis\", \"Ograve\", \"Otilde\", \"Scaron\", \"Uacute\", \"Ucircumflex\", \"Udieresis\", \"Ugrave\", \"Yacute\", \"Ydieresis\", \"Zcaron\", \"aacute\", \"acircumflex\", \"adieresis\", \"agrave\", \"aring\", \"atilde\", \"ccedilla\", \"eacute\", \"ecircumflex\", \"edieresis\", \"egrave\", \"iacute\", \"icircumflex\", \"idieresis\", \"igrave\", \"ntilde\", \"oacute\", \"ocircumflex\", \"odieresis\", \"ograve\", \"otilde\", \"scaron\", \"uacute\", \"ucircumflex\", \"udieresis\", \"ugrave\", \"yacute\", \"ydieresis\", \"zcaron\", \"exclamsmall\", \"Hungarumlautsmall\", \"dollaroldstyle\", \"dollarsuperior\", \"ampersandsmall\", \"Acutesmall\", \"parenleftsuperior\", \"parenrightsuperior\", \"twodotenleader\", \"onedotenleader\", \"zerooldstyle\", \"oneoldstyle\", \"twooldstyle\", \"threeoldstyle\", \"fouroldstyle\", \"fiveoldstyle\", \"sixoldstyle\", \"sevenoldstyle\", \"eightoldstyle\", \"nineoldstyle\", \"commasuperior\", \"threequartersemdash\", \"periodsuperior\", \"questionsmall\", \"asuperior\", \"bsuperior\", \"centsuperior\", \"dsuperior\", \"esuperior\", \"isuperior\", \"lsuperior\", \"msuperior\", \"nsuperior\", \"osuperior\", \"rsuperior\", \"ssuperior\", \"tsuperior\", \"ff\", \"ffi\", \"ffl\", \"parenleftinferior\", \"parenrightinferior\", \"Circumflexsmall\", \"hyphensuperior\", \"Gravesmall\", \"Asmall\", \"Bsmall\", \"Csmall\", \"Dsmall\", \"Esmall\", \"Fsmall\", \"Gsmall\", \"Hsmall\", \"Ismall\", \"Jsmall\", \"Ksmall\", \"Lsmall\", \"Msmall\", \"Nsmall\", \"Osmall\", \"Psmall\", \"Qsmall\", \"Rsmall\", \"Ssmall\", \"Tsmall\", \"Usmall\", \"Vsmall\", \"Wsmall\", \"Xsmall\", \"Ysmall\", \"Zsmall\", \"colonmonetary\", \"onefitted\", \"rupiah\", \"Tildesmall\", \"exclamdownsmall\", \"centoldstyle\", \"Lslashsmall\", \"Scaronsmall\", \"Zcaronsmall\", \"Dieresissmall\", \"Brevesmall\", \"Caronsmall\", \"Dotaccentsmall\", \"Macronsmall\", \"figuredash\", \"hypheninferior\", \"Ogoneksmall\", \"Ringsmall\", \"Cedillasmall\", \"questiondownsmall\", \"oneeighth\", \"threeeighths\", \"fiveeighths\", \"seveneighths\", \"onethird\", \"twothirds\", \"zerosuperior\", \"foursuperior\", \"fivesuperior\", \"sixsuperior\", \"sevensuperior\", \"eightsuperior\", \"ninesuperior\", \"zeroinferior\", \"oneinferior\", \"twoinferior\", \"threeinferior\", \"fourinferior\", \"fiveinferior\", \"sixinferior\", \"seveninferior\", \"eightinferior\", \"nineinferior\", \"centinferior\", \"dollarinferior\", \"periodinferior\", \"commainferior\", \"Agravesmall\", \"Aacutesmall\", \"Acircumflexsmall\", \"Atildesmall\", \"Adieresissmall\", \"Aringsmall\", \"AEsmall\", \"Ccedillasmall\", \"Egravesmall\", \"Eacutesmall\", \"Ecircumflexsmall\", \"Edieresissmall\", \"Igravesmall\", \"Iacutesmall\", \"Icircumflexsmall\", \"Idieresissmall\", \"Ethsmall\", \"Ntildesmall\", \"Ogravesmall\", \"Oacutesmall\", \"Ocircumflexsmall\", \"Otildesmall\", \"Odieresissmall\", \"OEsmall\", \"Oslashsmall\", \"Ugravesmall\", \"Uacutesmall\", \"Ucircumflexsmall\", \"Udieresissmall\", \"Yacutesmall\", \"Thornsmall\", \"Ydieresissmall\", \"001.000\", \"001.001\", \"001.002\", \"001.003\", \"Black\", \"Bold\", \"Book\", \"Light\", \"Medium\", \"Regular\", \"Roman\", \"Semibold\"];\nconst NUM_STANDARD_CFF_STRINGS = 391;\nconst CharstringValidationData = [null, {\n  id: \"hstem\",\n  min: 2,\n  stackClearing: true,\n  stem: true\n}, null, {\n  id: \"vstem\",\n  min: 2,\n  stackClearing: true,\n  stem: true\n}, {\n  id: \"vmoveto\",\n  min: 1,\n  stackClearing: true\n}, {\n  id: \"rlineto\",\n  min: 2,\n  resetStack: true\n}, {\n  id: \"hlineto\",\n  min: 1,\n  resetStack: true\n}, {\n  id: \"vlineto\",\n  min: 1,\n  resetStack: true\n}, {\n  id: \"rrcurveto\",\n  min: 6,\n  resetStack: true\n}, null, {\n  id: \"callsubr\",\n  min: 1,\n  undefStack: true\n}, {\n  id: \"return\",\n  min: 0,\n  undefStack: true\n}, null, null, {\n  id: \"endchar\",\n  min: 0,\n  stackClearing: true\n}, null, null, null, {\n  id: \"hstemhm\",\n  min: 2,\n  stackClearing: true,\n  stem: true\n}, {\n  id: \"hintmask\",\n  min: 0,\n  stackClearing: true\n}, {\n  id: \"cntrmask\",\n  min: 0,\n  stackClearing: true\n}, {\n  id: \"rmoveto\",\n  min: 2,\n  stackClearing: true\n}, {\n  id: \"hmoveto\",\n  min: 1,\n  stackClearing: true\n}, {\n  id: \"vstemhm\",\n  min: 2,\n  stackClearing: true,\n  stem: true\n}, {\n  id: \"rcurveline\",\n  min: 8,\n  resetStack: true\n}, {\n  id: \"rlinecurve\",\n  min: 8,\n  resetStack: true\n}, {\n  id: \"vvcurveto\",\n  min: 4,\n  resetStack: true\n}, {\n  id: \"hhcurveto\",\n  min: 4,\n  resetStack: true\n}, null, {\n  id: \"callgsubr\",\n  min: 1,\n  undefStack: true\n}, {\n  id: \"vhcurveto\",\n  min: 4,\n  resetStack: true\n}, {\n  id: \"hvcurveto\",\n  min: 4,\n  resetStack: true\n}];\nconst CharstringValidationData12 = [null, null, null, {\n  id: \"and\",\n  min: 2,\n  stackDelta: -1\n}, {\n  id: \"or\",\n  min: 2,\n  stackDelta: -1\n}, {\n  id: \"not\",\n  min: 1,\n  stackDelta: 0\n}, null, null, null, {\n  id: \"abs\",\n  min: 1,\n  stackDelta: 0\n}, {\n  id: \"add\",\n  min: 2,\n  stackDelta: -1,\n  stackFn(stack, index) {\n    stack[index - 2] = stack[index - 2] + stack[index - 1];\n  }\n}, {\n  id: \"sub\",\n  min: 2,\n  stackDelta: -1,\n  stackFn(stack, index) {\n    stack[index - 2] = stack[index - 2] - stack[index - 1];\n  }\n}, {\n  id: \"div\",\n  min: 2,\n  stackDelta: -1,\n  stackFn(stack, index) {\n    stack[index - 2] = stack[index - 2] / stack[index - 1];\n  }\n}, null, {\n  id: \"neg\",\n  min: 1,\n  stackDelta: 0,\n  stackFn(stack, index) {\n    stack[index - 1] = -stack[index - 1];\n  }\n}, {\n  id: \"eq\",\n  min: 2,\n  stackDelta: -1\n}, null, null, {\n  id: \"drop\",\n  min: 1,\n  stackDelta: -1\n}, null, {\n  id: \"put\",\n  min: 2,\n  stackDelta: -2\n}, {\n  id: \"get\",\n  min: 1,\n  stackDelta: 0\n}, {\n  id: \"ifelse\",\n  min: 4,\n  stackDelta: -3\n}, {\n  id: \"random\",\n  min: 0,\n  stackDelta: 1\n}, {\n  id: \"mul\",\n  min: 2,\n  stackDelta: -1,\n  stackFn(stack, index) {\n    stack[index - 2] = stack[index - 2] * stack[index - 1];\n  }\n}, null, {\n  id: \"sqrt\",\n  min: 1,\n  stackDelta: 0\n}, {\n  id: \"dup\",\n  min: 1,\n  stackDelta: 1\n}, {\n  id: \"exch\",\n  min: 2,\n  stackDelta: 0\n}, {\n  id: \"index\",\n  min: 2,\n  stackDelta: 0\n}, {\n  id: \"roll\",\n  min: 3,\n  stackDelta: -2\n}, null, null, null, {\n  id: \"hflex\",\n  min: 7,\n  resetStack: true\n}, {\n  id: \"flex\",\n  min: 13,\n  resetStack: true\n}, {\n  id: \"hflex1\",\n  min: 9,\n  resetStack: true\n}, {\n  id: \"flex1\",\n  min: 11,\n  resetStack: true\n}];\nclass CFFParser {\n  constructor(file, properties, seacAnalysisEnabled) {\n    this.bytes = file.getBytes();\n    this.properties = properties;\n    this.seacAnalysisEnabled = !!seacAnalysisEnabled;\n  }\n  parse() {\n    const properties = this.properties;\n    const cff = new CFF();\n    this.cff = cff;\n    const header = this.parseHeader();\n    const nameIndex = this.parseIndex(header.endPos);\n    const topDictIndex = this.parseIndex(nameIndex.endPos);\n    const stringIndex = this.parseIndex(topDictIndex.endPos);\n    const globalSubrIndex = this.parseIndex(stringIndex.endPos);\n    const topDictParsed = this.parseDict(topDictIndex.obj.get(0));\n    const topDict = this.createDict(CFFTopDict, topDictParsed, cff.strings);\n    cff.header = header.obj;\n    cff.names = this.parseNameIndex(nameIndex.obj);\n    cff.strings = this.parseStringIndex(stringIndex.obj);\n    cff.topDict = topDict;\n    cff.globalSubrIndex = globalSubrIndex.obj;\n    this.parsePrivateDict(cff.topDict);\n    cff.isCIDFont = topDict.hasName(\"ROS\");\n    const charStringOffset = topDict.getByName(\"CharStrings\");\n    const charStringIndex = this.parseIndex(charStringOffset).obj;\n    const fontMatrix = topDict.getByName(\"FontMatrix\");\n    if (fontMatrix) {\n      properties.fontMatrix = fontMatrix;\n    }\n    const fontBBox = topDict.getByName(\"FontBBox\");\n    if (fontBBox) {\n      properties.ascent = Math.max(fontBBox[3], fontBBox[1]);\n      properties.descent = Math.min(fontBBox[1], fontBBox[3]);\n      properties.ascentScaled = true;\n    }\n    let charset, encoding;\n    if (cff.isCIDFont) {\n      const fdArrayIndex = this.parseIndex(topDict.getByName(\"FDArray\")).obj;\n      for (let i = 0, ii = fdArrayIndex.count; i < ii; ++i) {\n        const dictRaw = fdArrayIndex.get(i);\n        const fontDict = this.createDict(CFFTopDict, this.parseDict(dictRaw), cff.strings);\n        this.parsePrivateDict(fontDict);\n        cff.fdArray.push(fontDict);\n      }\n      encoding = null;\n      charset = this.parseCharsets(topDict.getByName(\"charset\"), charStringIndex.count, cff.strings, true);\n      cff.fdSelect = this.parseFDSelect(topDict.getByName(\"FDSelect\"), charStringIndex.count);\n    } else {\n      charset = this.parseCharsets(topDict.getByName(\"charset\"), charStringIndex.count, cff.strings, false);\n      encoding = this.parseEncoding(topDict.getByName(\"Encoding\"), properties, cff.strings, charset.charset);\n    }\n    cff.charset = charset;\n    cff.encoding = encoding;\n    const charStringsAndSeacs = this.parseCharStrings({\n      charStrings: charStringIndex,\n      localSubrIndex: topDict.privateDict.subrsIndex,\n      globalSubrIndex: globalSubrIndex.obj,\n      fdSelect: cff.fdSelect,\n      fdArray: cff.fdArray,\n      privateDict: topDict.privateDict\n    });\n    cff.charStrings = charStringsAndSeacs.charStrings;\n    cff.seacs = charStringsAndSeacs.seacs;\n    cff.widths = charStringsAndSeacs.widths;\n    return cff;\n  }\n  parseHeader() {\n    let bytes = this.bytes;\n    const bytesLength = bytes.length;\n    let offset = 0;\n    while (offset < bytesLength && bytes[offset] !== 1) {\n      ++offset;\n    }\n    if (offset >= bytesLength) {\n      throw new FormatError(\"Invalid CFF header\");\n    }\n    if (offset !== 0) {\n      info(\"cff data is shifted\");\n      bytes = bytes.subarray(offset);\n      this.bytes = bytes;\n    }\n    const major = bytes[0];\n    const minor = bytes[1];\n    const hdrSize = bytes[2];\n    const offSize = bytes[3];\n    const header = new CFFHeader(major, minor, hdrSize, offSize);\n    return {\n      obj: header,\n      endPos: hdrSize\n    };\n  }\n  parseDict(dict) {\n    let pos = 0;\n    function parseOperand() {\n      let value = dict[pos++];\n      if (value === 30) {\n        return parseFloatOperand();\n      } else if (value === 28) {\n        value = dict[pos++];\n        value = (value << 24 | dict[pos++] << 16) >> 16;\n        return value;\n      } else if (value === 29) {\n        value = dict[pos++];\n        value = value << 8 | dict[pos++];\n        value = value << 8 | dict[pos++];\n        value = value << 8 | dict[pos++];\n        return value;\n      } else if (value >= 32 && value <= 246) {\n        return value - 139;\n      } else if (value >= 247 && value <= 250) {\n        return (value - 247) * 256 + dict[pos++] + 108;\n      } else if (value >= 251 && value <= 254) {\n        return -((value - 251) * 256) - dict[pos++] - 108;\n      }\n      warn('CFFParser_parseDict: \"' + value + '\" is a reserved command.');\n      return NaN;\n    }\n    function parseFloatOperand() {\n      let str = \"\";\n      const eof = 15;\n      const lookup = [\"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \".\", \"E\", \"E-\", null, \"-\"];\n      const length = dict.length;\n      while (pos < length) {\n        const b = dict[pos++];\n        const b1 = b >> 4;\n        const b2 = b & 15;\n        if (b1 === eof) {\n          break;\n        }\n        str += lookup[b1];\n        if (b2 === eof) {\n          break;\n        }\n        str += lookup[b2];\n      }\n      return parseFloat(str);\n    }\n    let operands = [];\n    const entries = [];\n    pos = 0;\n    const end = dict.length;\n    while (pos < end) {\n      let b = dict[pos];\n      if (b <= 21) {\n        if (b === 12) {\n          b = b << 8 | dict[++pos];\n        }\n        entries.push([b, operands]);\n        operands = [];\n        ++pos;\n      } else {\n        operands.push(parseOperand());\n      }\n    }\n    return entries;\n  }\n  parseIndex(pos) {\n    const cffIndex = new CFFIndex();\n    const bytes = this.bytes;\n    const count = bytes[pos++] << 8 | bytes[pos++];\n    const offsets = [];\n    let end = pos;\n    let i, ii;\n    if (count !== 0) {\n      const offsetSize = bytes[pos++];\n      const startPos = pos + (count + 1) * offsetSize - 1;\n      for (i = 0, ii = count + 1; i < ii; ++i) {\n        let offset = 0;\n        for (let j = 0; j < offsetSize; ++j) {\n          offset <<= 8;\n          offset += bytes[pos++];\n        }\n        offsets.push(startPos + offset);\n      }\n      end = offsets[count];\n    }\n    for (i = 0, ii = offsets.length - 1; i < ii; ++i) {\n      const offsetStart = offsets[i];\n      const offsetEnd = offsets[i + 1];\n      cffIndex.add(bytes.subarray(offsetStart, offsetEnd));\n    }\n    return {\n      obj: cffIndex,\n      endPos: end\n    };\n  }\n  parseNameIndex(index) {\n    const names = [];\n    for (let i = 0, ii = index.count; i < ii; ++i) {\n      const name = index.get(i);\n      names.push(bytesToString(name));\n    }\n    return names;\n  }\n  parseStringIndex(index) {\n    const strings = new CFFStrings();\n    for (let i = 0, ii = index.count; i < ii; ++i) {\n      const data = index.get(i);\n      strings.add(bytesToString(data));\n    }\n    return strings;\n  }\n  createDict(Type, dict, strings) {\n    const cffDict = new Type(strings);\n    for (const [key, value] of dict) {\n      cffDict.setByKey(key, value);\n    }\n    return cffDict;\n  }\n  parseCharString(state, data, localSubrIndex, globalSubrIndex) {\n    if (!data || state.callDepth > MAX_SUBR_NESTING) {\n      return false;\n    }\n    let stackSize = state.stackSize;\n    const stack = state.stack;\n    let length = data.length;\n    for (let j = 0; j < length;) {\n      const value = data[j++];\n      let validationCommand = null;\n      if (value === 12) {\n        const q = data[j++];\n        if (q === 0) {\n          data[j - 2] = 139;\n          data[j - 1] = 22;\n          stackSize = 0;\n        } else {\n          validationCommand = CharstringValidationData12[q];\n        }\n      } else if (value === 28) {\n        stack[stackSize] = (data[j] << 24 | data[j + 1] << 16) >> 16;\n        j += 2;\n        stackSize++;\n      } else if (value === 14) {\n        if (stackSize >= 4) {\n          stackSize -= 4;\n          if (this.seacAnalysisEnabled) {\n            state.seac = stack.slice(stackSize, stackSize + 4);\n            return false;\n          }\n        }\n        validationCommand = CharstringValidationData[value];\n      } else if (value >= 32 && value <= 246) {\n        stack[stackSize] = value - 139;\n        stackSize++;\n      } else if (value >= 247 && value <= 254) {\n        stack[stackSize] = value < 251 ? (value - 247 << 8) + data[j] + 108 : -(value - 251 << 8) - data[j] - 108;\n        j++;\n        stackSize++;\n      } else if (value === 255) {\n        stack[stackSize] = (data[j] << 24 | data[j + 1] << 16 | data[j + 2] << 8 | data[j + 3]) / 65536;\n        j += 4;\n        stackSize++;\n      } else if (value === 19 || value === 20) {\n        state.hints += stackSize >> 1;\n        if (state.hints === 0) {\n          data.copyWithin(j - 1, j, -1);\n          j -= 1;\n          length -= 1;\n          continue;\n        }\n        j += state.hints + 7 >> 3;\n        stackSize %= 2;\n        validationCommand = CharstringValidationData[value];\n      } else if (value === 10 || value === 29) {\n        const subrsIndex = value === 10 ? localSubrIndex : globalSubrIndex;\n        if (!subrsIndex) {\n          validationCommand = CharstringValidationData[value];\n          warn(\"Missing subrsIndex for \" + validationCommand.id);\n          return false;\n        }\n        let bias = 32768;\n        if (subrsIndex.count < 1240) {\n          bias = 107;\n        } else if (subrsIndex.count < 33900) {\n          bias = 1131;\n        }\n        const subrNumber = stack[--stackSize] + bias;\n        if (subrNumber < 0 || subrNumber >= subrsIndex.count || isNaN(subrNumber)) {\n          validationCommand = CharstringValidationData[value];\n          warn(\"Out of bounds subrIndex for \" + validationCommand.id);\n          return false;\n        }\n        state.stackSize = stackSize;\n        state.callDepth++;\n        const valid = this.parseCharString(state, subrsIndex.get(subrNumber), localSubrIndex, globalSubrIndex);\n        if (!valid) {\n          return false;\n        }\n        state.callDepth--;\n        stackSize = state.stackSize;\n        continue;\n      } else if (value === 11) {\n        state.stackSize = stackSize;\n        return true;\n      } else if (value === 0 && j === data.length) {\n        data[j - 1] = 14;\n        validationCommand = CharstringValidationData[14];\n      } else if (value === 9) {\n        data.copyWithin(j - 1, j, -1);\n        j -= 1;\n        length -= 1;\n        continue;\n      } else {\n        validationCommand = CharstringValidationData[value];\n      }\n      if (validationCommand) {\n        if (validationCommand.stem) {\n          state.hints += stackSize >> 1;\n          if (value === 3 || value === 23) {\n            state.hasVStems = true;\n          } else if (state.hasVStems && (value === 1 || value === 18)) {\n            warn(\"CFF stem hints are in wrong order\");\n            data[j - 1] = value === 1 ? 3 : 23;\n          }\n        }\n        if (\"min\" in validationCommand) {\n          if (!state.undefStack && stackSize < validationCommand.min) {\n            warn(\"Not enough parameters for \" + validationCommand.id + \"; actual: \" + stackSize + \", expected: \" + validationCommand.min);\n            if (stackSize === 0) {\n              data[j - 1] = 14;\n              return true;\n            }\n            return false;\n          }\n        }\n        if (state.firstStackClearing && validationCommand.stackClearing) {\n          state.firstStackClearing = false;\n          stackSize -= validationCommand.min;\n          if (stackSize >= 2 && validationCommand.stem) {\n            stackSize %= 2;\n          } else if (stackSize > 1) {\n            warn(\"Found too many parameters for stack-clearing command\");\n          }\n          if (stackSize > 0) {\n            state.width = stack[stackSize - 1];\n          }\n        }\n        if (\"stackDelta\" in validationCommand) {\n          if (\"stackFn\" in validationCommand) {\n            validationCommand.stackFn(stack, stackSize);\n          }\n          stackSize += validationCommand.stackDelta;\n        } else if (validationCommand.stackClearing) {\n          stackSize = 0;\n        } else if (validationCommand.resetStack) {\n          stackSize = 0;\n          state.undefStack = false;\n        } else if (validationCommand.undefStack) {\n          stackSize = 0;\n          state.undefStack = true;\n          state.firstStackClearing = false;\n        }\n      }\n    }\n    if (length < data.length) {\n      data.fill(14, length);\n    }\n    state.stackSize = stackSize;\n    return true;\n  }\n  parseCharStrings({\n    charStrings,\n    localSubrIndex,\n    globalSubrIndex,\n    fdSelect,\n    fdArray,\n    privateDict\n  }) {\n    const seacs = [];\n    const widths = [];\n    const count = charStrings.count;\n    for (let i = 0; i < count; i++) {\n      const charstring = charStrings.get(i);\n      const state = {\n        callDepth: 0,\n        stackSize: 0,\n        stack: [],\n        undefStack: true,\n        hints: 0,\n        firstStackClearing: true,\n        seac: null,\n        width: null,\n        hasVStems: false\n      };\n      let valid = true;\n      let localSubrToUse = null;\n      let privateDictToUse = privateDict;\n      if (fdSelect && fdArray.length) {\n        const fdIndex = fdSelect.getFDIndex(i);\n        if (fdIndex === -1) {\n          warn(\"Glyph index is not in fd select.\");\n          valid = false;\n        }\n        if (fdIndex >= fdArray.length) {\n          warn(\"Invalid fd index for glyph index.\");\n          valid = false;\n        }\n        if (valid) {\n          privateDictToUse = fdArray[fdIndex].privateDict;\n          localSubrToUse = privateDictToUse.subrsIndex;\n        }\n      } else if (localSubrIndex) {\n        localSubrToUse = localSubrIndex;\n      }\n      if (valid) {\n        valid = this.parseCharString(state, charstring, localSubrToUse, globalSubrIndex);\n      }\n      if (state.width !== null) {\n        const nominalWidth = privateDictToUse.getByName(\"nominalWidthX\");\n        widths[i] = nominalWidth + state.width;\n      } else {\n        const defaultWidth = privateDictToUse.getByName(\"defaultWidthX\");\n        widths[i] = defaultWidth;\n      }\n      if (state.seac !== null) {\n        seacs[i] = state.seac;\n      }\n      if (!valid) {\n        charStrings.set(i, new Uint8Array([14]));\n      }\n    }\n    return {\n      charStrings,\n      seacs,\n      widths\n    };\n  }\n  emptyPrivateDictionary(parentDict) {\n    const privateDict = this.createDict(CFFPrivateDict, [], parentDict.strings);\n    parentDict.setByKey(18, [0, 0]);\n    parentDict.privateDict = privateDict;\n  }\n  parsePrivateDict(parentDict) {\n    if (!parentDict.hasName(\"Private\")) {\n      this.emptyPrivateDictionary(parentDict);\n      return;\n    }\n    const privateOffset = parentDict.getByName(\"Private\");\n    if (!Array.isArray(privateOffset) || privateOffset.length !== 2) {\n      parentDict.removeByName(\"Private\");\n      return;\n    }\n    const size = privateOffset[0];\n    const offset = privateOffset[1];\n    if (size === 0 || offset >= this.bytes.length) {\n      this.emptyPrivateDictionary(parentDict);\n      return;\n    }\n    const privateDictEnd = offset + size;\n    const dictData = this.bytes.subarray(offset, privateDictEnd);\n    const dict = this.parseDict(dictData);\n    const privateDict = this.createDict(CFFPrivateDict, dict, parentDict.strings);\n    parentDict.privateDict = privateDict;\n    if (privateDict.getByName(\"ExpansionFactor\") === 0) {\n      privateDict.setByName(\"ExpansionFactor\", 0.06);\n    }\n    if (!privateDict.getByName(\"Subrs\")) {\n      return;\n    }\n    const subrsOffset = privateDict.getByName(\"Subrs\");\n    const relativeOffset = offset + subrsOffset;\n    if (subrsOffset === 0 || relativeOffset >= this.bytes.length) {\n      this.emptyPrivateDictionary(parentDict);\n      return;\n    }\n    const subrsIndex = this.parseIndex(relativeOffset);\n    privateDict.subrsIndex = subrsIndex.obj;\n  }\n  parseCharsets(pos, length, strings, cid) {\n    if (pos === 0) {\n      return new CFFCharset(true, CFFCharsetPredefinedTypes.ISO_ADOBE, ISOAdobeCharset);\n    } else if (pos === 1) {\n      return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT, ExpertCharset);\n    } else if (pos === 2) {\n      return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT_SUBSET, ExpertSubsetCharset);\n    }\n    const bytes = this.bytes;\n    const start = pos;\n    const format = bytes[pos++];\n    const charset = [cid ? 0 : \".notdef\"];\n    let id, count, i;\n    length -= 1;\n    switch (format) {\n      case 0:\n        for (i = 0; i < length; i++) {\n          id = bytes[pos++] << 8 | bytes[pos++];\n          charset.push(cid ? id : strings.get(id));\n        }\n        break;\n      case 1:\n        while (charset.length <= length) {\n          id = bytes[pos++] << 8 | bytes[pos++];\n          count = bytes[pos++];\n          for (i = 0; i <= count; i++) {\n            charset.push(cid ? id++ : strings.get(id++));\n          }\n        }\n        break;\n      case 2:\n        while (charset.length <= length) {\n          id = bytes[pos++] << 8 | bytes[pos++];\n          count = bytes[pos++] << 8 | bytes[pos++];\n          for (i = 0; i <= count; i++) {\n            charset.push(cid ? id++ : strings.get(id++));\n          }\n        }\n        break;\n      default:\n        throw new FormatError(\"Unknown charset format\");\n    }\n    const end = pos;\n    const raw = bytes.subarray(start, end);\n    return new CFFCharset(false, format, charset, raw);\n  }\n  parseEncoding(pos, properties, strings, charset) {\n    const encoding = Object.create(null);\n    const bytes = this.bytes;\n    let predefined = false;\n    let format, i, ii;\n    let raw = null;\n    function readSupplement() {\n      const supplementsCount = bytes[pos++];\n      for (i = 0; i < supplementsCount; i++) {\n        const code = bytes[pos++];\n        const sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff);\n        encoding[code] = charset.indexOf(strings.get(sid));\n      }\n    }\n    if (pos === 0 || pos === 1) {\n      predefined = true;\n      format = pos;\n      const baseEncoding = pos ? ExpertEncoding : StandardEncoding;\n      for (i = 0, ii = charset.length; i < ii; i++) {\n        const index = baseEncoding.indexOf(charset[i]);\n        if (index !== -1) {\n          encoding[index] = i;\n        }\n      }\n    } else {\n      const dataStart = pos;\n      format = bytes[pos++];\n      switch (format & 0x7f) {\n        case 0:\n          const glyphsCount = bytes[pos++];\n          for (i = 1; i <= glyphsCount; i++) {\n            encoding[bytes[pos++]] = i;\n          }\n          break;\n        case 1:\n          const rangesCount = bytes[pos++];\n          let gid = 1;\n          for (i = 0; i < rangesCount; i++) {\n            const start = bytes[pos++];\n            const left = bytes[pos++];\n            for (let j = start; j <= start + left; j++) {\n              encoding[j] = gid++;\n            }\n          }\n          break;\n        default:\n          throw new FormatError(`Unknown encoding format: ${format} in CFF`);\n      }\n      const dataEnd = pos;\n      if (format & 0x80) {\n        bytes[dataStart] &= 0x7f;\n        readSupplement();\n      }\n      raw = bytes.subarray(dataStart, dataEnd);\n    }\n    format &= 0x7f;\n    return new CFFEncoding(predefined, format, encoding, raw);\n  }\n  parseFDSelect(pos, length) {\n    const bytes = this.bytes;\n    const format = bytes[pos++];\n    const fdSelect = [];\n    let i;\n    switch (format) {\n      case 0:\n        for (i = 0; i < length; ++i) {\n          const id = bytes[pos++];\n          fdSelect.push(id);\n        }\n        break;\n      case 3:\n        const rangesCount = bytes[pos++] << 8 | bytes[pos++];\n        for (i = 0; i < rangesCount; ++i) {\n          let first = bytes[pos++] << 8 | bytes[pos++];\n          if (i === 0 && first !== 0) {\n            warn(\"parseFDSelect: The first range must have a first GID of 0\" + \" -- trying to recover.\");\n            first = 0;\n          }\n          const fdIndex = bytes[pos++];\n          const next = bytes[pos] << 8 | bytes[pos + 1];\n          for (let j = first; j < next; ++j) {\n            fdSelect.push(fdIndex);\n          }\n        }\n        pos += 2;\n        break;\n      default:\n        throw new FormatError(`parseFDSelect: Unknown format \"${format}\".`);\n    }\n    if (fdSelect.length !== length) {\n      throw new FormatError(\"parseFDSelect: Invalid font data.\");\n    }\n    return new CFFFDSelect(format, fdSelect);\n  }\n}\nclass CFF {\n  constructor() {\n    this.header = null;\n    this.names = [];\n    this.topDict = null;\n    this.strings = new CFFStrings();\n    this.globalSubrIndex = null;\n    this.encoding = null;\n    this.charset = null;\n    this.charStrings = null;\n    this.fdArray = [];\n    this.fdSelect = null;\n    this.isCIDFont = false;\n  }\n  duplicateFirstGlyph() {\n    if (this.charStrings.count >= 65535) {\n      warn(\"Not enough space in charstrings to duplicate first glyph.\");\n      return;\n    }\n    const glyphZero = this.charStrings.get(0);\n    this.charStrings.add(glyphZero);\n    if (this.isCIDFont) {\n      this.fdSelect.fdSelect.push(this.fdSelect.fdSelect[0]);\n    }\n  }\n  hasGlyphId(id) {\n    if (id < 0 || id >= this.charStrings.count) {\n      return false;\n    }\n    const glyph = this.charStrings.get(id);\n    return glyph.length > 0;\n  }\n}\nclass CFFHeader {\n  constructor(major, minor, hdrSize, offSize) {\n    this.major = major;\n    this.minor = minor;\n    this.hdrSize = hdrSize;\n    this.offSize = offSize;\n  }\n}\nclass CFFStrings {\n  constructor() {\n    this.strings = [];\n  }\n  get(index) {\n    if (index >= 0 && index <= NUM_STANDARD_CFF_STRINGS - 1) {\n      return CFFStandardStrings[index];\n    }\n    if (index - NUM_STANDARD_CFF_STRINGS <= this.strings.length) {\n      return this.strings[index - NUM_STANDARD_CFF_STRINGS];\n    }\n    return CFFStandardStrings[0];\n  }\n  getSID(str) {\n    let index = CFFStandardStrings.indexOf(str);\n    if (index !== -1) {\n      return index;\n    }\n    index = this.strings.indexOf(str);\n    if (index !== -1) {\n      return index + NUM_STANDARD_CFF_STRINGS;\n    }\n    return -1;\n  }\n  add(value) {\n    this.strings.push(value);\n  }\n  get count() {\n    return this.strings.length;\n  }\n}\nclass CFFIndex {\n  constructor() {\n    this.objects = [];\n    this.length = 0;\n  }\n  add(data) {\n    this.length += data.length;\n    this.objects.push(data);\n  }\n  set(index, data) {\n    this.length += data.length - this.objects[index].length;\n    this.objects[index] = data;\n  }\n  get(index) {\n    return this.objects[index];\n  }\n  get count() {\n    return this.objects.length;\n  }\n}\nclass CFFDict {\n  constructor(tables, strings) {\n    this.keyToNameMap = tables.keyToNameMap;\n    this.nameToKeyMap = tables.nameToKeyMap;\n    this.defaults = tables.defaults;\n    this.types = tables.types;\n    this.opcodes = tables.opcodes;\n    this.order = tables.order;\n    this.strings = strings;\n    this.values = Object.create(null);\n  }\n  setByKey(key, value) {\n    if (!(key in this.keyToNameMap)) {\n      return false;\n    }\n    if (value.length === 0) {\n      return true;\n    }\n    for (const val of value) {\n      if (isNaN(val)) {\n        warn(`Invalid CFFDict value: \"${value}\" for key \"${key}\".`);\n        return true;\n      }\n    }\n    const type = this.types[key];\n    if (type === \"num\" || type === \"sid\" || type === \"offset\") {\n      value = value[0];\n    }\n    this.values[key] = value;\n    return true;\n  }\n  setByName(name, value) {\n    if (!(name in this.nameToKeyMap)) {\n      throw new FormatError(`Invalid dictionary name \"${name}\"`);\n    }\n    this.values[this.nameToKeyMap[name]] = value;\n  }\n  hasName(name) {\n    return this.nameToKeyMap[name] in this.values;\n  }\n  getByName(name) {\n    if (!(name in this.nameToKeyMap)) {\n      throw new FormatError(`Invalid dictionary name ${name}\"`);\n    }\n    const key = this.nameToKeyMap[name];\n    if (!(key in this.values)) {\n      return this.defaults[key];\n    }\n    return this.values[key];\n  }\n  removeByName(name) {\n    delete this.values[this.nameToKeyMap[name]];\n  }\n  static createTables(layout) {\n    const tables = {\n      keyToNameMap: {},\n      nameToKeyMap: {},\n      defaults: {},\n      types: {},\n      opcodes: {},\n      order: []\n    };\n    for (const entry of layout) {\n      const key = Array.isArray(entry[0]) ? (entry[0][0] << 8) + entry[0][1] : entry[0];\n      tables.keyToNameMap[key] = entry[1];\n      tables.nameToKeyMap[entry[1]] = key;\n      tables.types[key] = entry[2];\n      tables.defaults[key] = entry[3];\n      tables.opcodes[key] = Array.isArray(entry[0]) ? entry[0] : [entry[0]];\n      tables.order.push(key);\n    }\n    return tables;\n  }\n}\nconst CFFTopDictLayout = [[[12, 30], \"ROS\", [\"sid\", \"sid\", \"num\"], null], [[12, 20], \"SyntheticBase\", \"num\", null], [0, \"version\", \"sid\", null], [1, \"Notice\", \"sid\", null], [[12, 0], \"Copyright\", \"sid\", null], [2, \"FullName\", \"sid\", null], [3, \"FamilyName\", \"sid\", null], [4, \"Weight\", \"sid\", null], [[12, 1], \"isFixedPitch\", \"num\", 0], [[12, 2], \"ItalicAngle\", \"num\", 0], [[12, 3], \"UnderlinePosition\", \"num\", -100], [[12, 4], \"UnderlineThickness\", \"num\", 50], [[12, 5], \"PaintType\", \"num\", 0], [[12, 6], \"CharstringType\", \"num\", 2], [[12, 7], \"FontMatrix\", [\"num\", \"num\", \"num\", \"num\", \"num\", \"num\"], [0.001, 0, 0, 0.001, 0, 0]], [13, \"UniqueID\", \"num\", null], [5, \"FontBBox\", [\"num\", \"num\", \"num\", \"num\"], [0, 0, 0, 0]], [[12, 8], \"StrokeWidth\", \"num\", 0], [14, \"XUID\", \"array\", null], [15, \"charset\", \"offset\", 0], [16, \"Encoding\", \"offset\", 0], [17, \"CharStrings\", \"offset\", 0], [18, \"Private\", [\"offset\", \"offset\"], null], [[12, 21], \"PostScript\", \"sid\", null], [[12, 22], \"BaseFontName\", \"sid\", null], [[12, 23], \"BaseFontBlend\", \"delta\", null], [[12, 31], \"CIDFontVersion\", \"num\", 0], [[12, 32], \"CIDFontRevision\", \"num\", 0], [[12, 33], \"CIDFontType\", \"num\", 0], [[12, 34], \"CIDCount\", \"num\", 8720], [[12, 35], \"UIDBase\", \"num\", null], [[12, 37], \"FDSelect\", \"offset\", null], [[12, 36], \"FDArray\", \"offset\", null], [[12, 38], \"FontName\", \"sid\", null]];\nclass CFFTopDict extends CFFDict {\n  static get tables() {\n    return shadow(this, \"tables\", this.createTables(CFFTopDictLayout));\n  }\n  constructor(strings) {\n    super(CFFTopDict.tables, strings);\n    this.privateDict = null;\n  }\n}\nconst CFFPrivateDictLayout = [[6, \"BlueValues\", \"delta\", null], [7, \"OtherBlues\", \"delta\", null], [8, \"FamilyBlues\", \"delta\", null], [9, \"FamilyOtherBlues\", \"delta\", null], [[12, 9], \"BlueScale\", \"num\", 0.039625], [[12, 10], \"BlueShift\", \"num\", 7], [[12, 11], \"BlueFuzz\", \"num\", 1], [10, \"StdHW\", \"num\", null], [11, \"StdVW\", \"num\", null], [[12, 12], \"StemSnapH\", \"delta\", null], [[12, 13], \"StemSnapV\", \"delta\", null], [[12, 14], \"ForceBold\", \"num\", 0], [[12, 17], \"LanguageGroup\", \"num\", 0], [[12, 18], \"ExpansionFactor\", \"num\", 0.06], [[12, 19], \"initialRandomSeed\", \"num\", 0], [20, \"defaultWidthX\", \"num\", 0], [21, \"nominalWidthX\", \"num\", 0], [19, \"Subrs\", \"offset\", null]];\nclass CFFPrivateDict extends CFFDict {\n  static get tables() {\n    return shadow(this, \"tables\", this.createTables(CFFPrivateDictLayout));\n  }\n  constructor(strings) {\n    super(CFFPrivateDict.tables, strings);\n    this.subrsIndex = null;\n  }\n}\nconst CFFCharsetPredefinedTypes = {\n  ISO_ADOBE: 0,\n  EXPERT: 1,\n  EXPERT_SUBSET: 2\n};\nclass CFFCharset {\n  constructor(predefined, format, charset, raw) {\n    this.predefined = predefined;\n    this.format = format;\n    this.charset = charset;\n    this.raw = raw;\n  }\n}\nclass CFFEncoding {\n  constructor(predefined, format, encoding, raw) {\n    this.predefined = predefined;\n    this.format = format;\n    this.encoding = encoding;\n    this.raw = raw;\n  }\n}\nclass CFFFDSelect {\n  constructor(format, fdSelect) {\n    this.format = format;\n    this.fdSelect = fdSelect;\n  }\n  getFDIndex(glyphIndex) {\n    if (glyphIndex < 0 || glyphIndex >= this.fdSelect.length) {\n      return -1;\n    }\n    return this.fdSelect[glyphIndex];\n  }\n}\nclass CFFOffsetTracker {\n  constructor() {\n    this.offsets = Object.create(null);\n  }\n  isTracking(key) {\n    return key in this.offsets;\n  }\n  track(key, location) {\n    if (key in this.offsets) {\n      throw new FormatError(`Already tracking location of ${key}`);\n    }\n    this.offsets[key] = location;\n  }\n  offset(value) {\n    for (const key in this.offsets) {\n      this.offsets[key] += value;\n    }\n  }\n  setEntryLocation(key, values, output) {\n    if (!(key in this.offsets)) {\n      throw new FormatError(`Not tracking location of ${key}`);\n    }\n    const data = output.data;\n    const dataOffset = this.offsets[key];\n    const size = 5;\n    for (let i = 0, ii = values.length; i < ii; ++i) {\n      const offset0 = i * size + dataOffset;\n      const offset1 = offset0 + 1;\n      const offset2 = offset0 + 2;\n      const offset3 = offset0 + 3;\n      const offset4 = offset0 + 4;\n      if (data[offset0] !== 0x1d || data[offset1] !== 0 || data[offset2] !== 0 || data[offset3] !== 0 || data[offset4] !== 0) {\n        throw new FormatError(\"writing to an offset that is not empty\");\n      }\n      const value = values[i];\n      data[offset0] = 0x1d;\n      data[offset1] = value >> 24 & 0xff;\n      data[offset2] = value >> 16 & 0xff;\n      data[offset3] = value >> 8 & 0xff;\n      data[offset4] = value & 0xff;\n    }\n  }\n}\nclass CFFCompiler {\n  constructor(cff) {\n    this.cff = cff;\n  }\n  compile() {\n    const cff = this.cff;\n    const output = {\n      data: [],\n      length: 0,\n      add(data) {\n        try {\n          this.data.push(...data);\n        } catch {\n          this.data = this.data.concat(data);\n        }\n        this.length = this.data.length;\n      }\n    };\n    const header = this.compileHeader(cff.header);\n    output.add(header);\n    const nameIndex = this.compileNameIndex(cff.names);\n    output.add(nameIndex);\n    if (cff.isCIDFont) {\n      if (cff.topDict.hasName(\"FontMatrix\")) {\n        const base = cff.topDict.getByName(\"FontMatrix\");\n        cff.topDict.removeByName(\"FontMatrix\");\n        for (const subDict of cff.fdArray) {\n          let matrix = base.slice(0);\n          if (subDict.hasName(\"FontMatrix\")) {\n            matrix = Util.transform(matrix, subDict.getByName(\"FontMatrix\"));\n          }\n          subDict.setByName(\"FontMatrix\", matrix);\n        }\n      }\n    }\n    const xuid = cff.topDict.getByName(\"XUID\");\n    if (xuid?.length > 16) {\n      cff.topDict.removeByName(\"XUID\");\n    }\n    cff.topDict.setByName(\"charset\", 0);\n    let compiled = this.compileTopDicts([cff.topDict], output.length, cff.isCIDFont);\n    output.add(compiled.output);\n    const topDictTracker = compiled.trackers[0];\n    const stringIndex = this.compileStringIndex(cff.strings.strings);\n    output.add(stringIndex);\n    const globalSubrIndex = this.compileIndex(cff.globalSubrIndex);\n    output.add(globalSubrIndex);\n    if (cff.encoding && cff.topDict.hasName(\"Encoding\")) {\n      if (cff.encoding.predefined) {\n        topDictTracker.setEntryLocation(\"Encoding\", [cff.encoding.format], output);\n      } else {\n        const encoding = this.compileEncoding(cff.encoding);\n        topDictTracker.setEntryLocation(\"Encoding\", [output.length], output);\n        output.add(encoding);\n      }\n    }\n    const charset = this.compileCharset(cff.charset, cff.charStrings.count, cff.strings, cff.isCIDFont);\n    topDictTracker.setEntryLocation(\"charset\", [output.length], output);\n    output.add(charset);\n    const charStrings = this.compileCharStrings(cff.charStrings);\n    topDictTracker.setEntryLocation(\"CharStrings\", [output.length], output);\n    output.add(charStrings);\n    if (cff.isCIDFont) {\n      topDictTracker.setEntryLocation(\"FDSelect\", [output.length], output);\n      const fdSelect = this.compileFDSelect(cff.fdSelect);\n      output.add(fdSelect);\n      compiled = this.compileTopDicts(cff.fdArray, output.length, true);\n      topDictTracker.setEntryLocation(\"FDArray\", [output.length], output);\n      output.add(compiled.output);\n      const fontDictTrackers = compiled.trackers;\n      this.compilePrivateDicts(cff.fdArray, fontDictTrackers, output);\n    }\n    this.compilePrivateDicts([cff.topDict], [topDictTracker], output);\n    output.add([0]);\n    return output.data;\n  }\n  encodeNumber(value) {\n    if (Number.isInteger(value)) {\n      return this.encodeInteger(value);\n    }\n    return this.encodeFloat(value);\n  }\n  static get EncodeFloatRegExp() {\n    return shadow(this, \"EncodeFloatRegExp\", /\\.(\\d*?)(?:9{5,20}|0{5,20})\\d{0,2}(?:e(.+)|$)/);\n  }\n  encodeFloat(num) {\n    let value = num.toString();\n    const m = CFFCompiler.EncodeFloatRegExp.exec(value);\n    if (m) {\n      const epsilon = parseFloat(\"1e\" + ((m[2] ? +m[2] : 0) + m[1].length));\n      value = (Math.round(num * epsilon) / epsilon).toString();\n    }\n    let nibbles = \"\";\n    let i, ii;\n    for (i = 0, ii = value.length; i < ii; ++i) {\n      const a = value[i];\n      if (a === \"e\") {\n        nibbles += value[++i] === \"-\" ? \"c\" : \"b\";\n      } else if (a === \".\") {\n        nibbles += \"a\";\n      } else if (a === \"-\") {\n        nibbles += \"e\";\n      } else {\n        nibbles += a;\n      }\n    }\n    nibbles += nibbles.length & 1 ? \"f\" : \"ff\";\n    const out = [30];\n    for (i = 0, ii = nibbles.length; i < ii; i += 2) {\n      out.push(parseInt(nibbles.substring(i, i + 2), 16));\n    }\n    return out;\n  }\n  encodeInteger(value) {\n    let code;\n    if (value >= -107 && value <= 107) {\n      code = [value + 139];\n    } else if (value >= 108 && value <= 1131) {\n      value -= 108;\n      code = [(value >> 8) + 247, value & 0xff];\n    } else if (value >= -1131 && value <= -108) {\n      value = -value - 108;\n      code = [(value >> 8) + 251, value & 0xff];\n    } else if (value >= -32768 && value <= 32767) {\n      code = [0x1c, value >> 8 & 0xff, value & 0xff];\n    } else {\n      code = [0x1d, value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff];\n    }\n    return code;\n  }\n  compileHeader(header) {\n    return [header.major, header.minor, 4, header.offSize];\n  }\n  compileNameIndex(names) {\n    const nameIndex = new CFFIndex();\n    for (const name of names) {\n      const length = Math.min(name.length, 127);\n      let sanitizedName = new Array(length);\n      for (let j = 0; j < length; j++) {\n        let char = name[j];\n        if (char < \"!\" || char > \"~\" || char === \"[\" || char === \"]\" || char === \"(\" || char === \")\" || char === \"{\" || char === \"}\" || char === \"<\" || char === \">\" || char === \"/\" || char === \"%\") {\n          char = \"_\";\n        }\n        sanitizedName[j] = char;\n      }\n      sanitizedName = sanitizedName.join(\"\");\n      if (sanitizedName === \"\") {\n        sanitizedName = \"Bad_Font_Name\";\n      }\n      nameIndex.add(stringToBytes(sanitizedName));\n    }\n    return this.compileIndex(nameIndex);\n  }\n  compileTopDicts(dicts, length, removeCidKeys) {\n    const fontDictTrackers = [];\n    let fdArrayIndex = new CFFIndex();\n    for (const fontDict of dicts) {\n      if (removeCidKeys) {\n        fontDict.removeByName(\"CIDFontVersion\");\n        fontDict.removeByName(\"CIDFontRevision\");\n        fontDict.removeByName(\"CIDFontType\");\n        fontDict.removeByName(\"CIDCount\");\n        fontDict.removeByName(\"UIDBase\");\n      }\n      const fontDictTracker = new CFFOffsetTracker();\n      const fontDictData = this.compileDict(fontDict, fontDictTracker);\n      fontDictTrackers.push(fontDictTracker);\n      fdArrayIndex.add(fontDictData);\n      fontDictTracker.offset(length);\n    }\n    fdArrayIndex = this.compileIndex(fdArrayIndex, fontDictTrackers);\n    return {\n      trackers: fontDictTrackers,\n      output: fdArrayIndex\n    };\n  }\n  compilePrivateDicts(dicts, trackers, output) {\n    for (let i = 0, ii = dicts.length; i < ii; ++i) {\n      const fontDict = dicts[i];\n      const privateDict = fontDict.privateDict;\n      if (!privateDict || !fontDict.hasName(\"Private\")) {\n        throw new FormatError(\"There must be a private dictionary.\");\n      }\n      const privateDictTracker = new CFFOffsetTracker();\n      const privateDictData = this.compileDict(privateDict, privateDictTracker);\n      let outputLength = output.length;\n      privateDictTracker.offset(outputLength);\n      if (!privateDictData.length) {\n        outputLength = 0;\n      }\n      trackers[i].setEntryLocation(\"Private\", [privateDictData.length, outputLength], output);\n      output.add(privateDictData);\n      if (privateDict.subrsIndex && privateDict.hasName(\"Subrs\")) {\n        const subrs = this.compileIndex(privateDict.subrsIndex);\n        privateDictTracker.setEntryLocation(\"Subrs\", [privateDictData.length], output);\n        output.add(subrs);\n      }\n    }\n  }\n  compileDict(dict, offsetTracker) {\n    const out = [];\n    for (const key of dict.order) {\n      if (!(key in dict.values)) {\n        continue;\n      }\n      let values = dict.values[key];\n      let types = dict.types[key];\n      if (!Array.isArray(types)) {\n        types = [types];\n      }\n      if (!Array.isArray(values)) {\n        values = [values];\n      }\n      if (values.length === 0) {\n        continue;\n      }\n      for (let j = 0, jj = types.length; j < jj; ++j) {\n        const type = types[j];\n        const value = values[j];\n        switch (type) {\n          case \"num\":\n          case \"sid\":\n            out.push(...this.encodeNumber(value));\n            break;\n          case \"offset\":\n            const name = dict.keyToNameMap[key];\n            if (!offsetTracker.isTracking(name)) {\n              offsetTracker.track(name, out.length);\n            }\n            out.push(0x1d, 0, 0, 0, 0);\n            break;\n          case \"array\":\n          case \"delta\":\n            out.push(...this.encodeNumber(value));\n            for (let k = 1, kk = values.length; k < kk; ++k) {\n              out.push(...this.encodeNumber(values[k]));\n            }\n            break;\n          default:\n            throw new FormatError(`Unknown data type of ${type}`);\n        }\n      }\n      out.push(...dict.opcodes[key]);\n    }\n    return out;\n  }\n  compileStringIndex(strings) {\n    const stringIndex = new CFFIndex();\n    for (const string of strings) {\n      stringIndex.add(stringToBytes(string));\n    }\n    return this.compileIndex(stringIndex);\n  }\n  compileCharStrings(charStrings) {\n    const charStringsIndex = new CFFIndex();\n    for (let i = 0; i < charStrings.count; i++) {\n      const glyph = charStrings.get(i);\n      if (glyph.length === 0) {\n        charStringsIndex.add(new Uint8Array([0x8b, 0x0e]));\n        continue;\n      }\n      charStringsIndex.add(glyph);\n    }\n    return this.compileIndex(charStringsIndex);\n  }\n  compileCharset(charset, numGlyphs, strings, isCIDFont) {\n    let out;\n    const numGlyphsLessNotDef = numGlyphs - 1;\n    if (isCIDFont) {\n      out = new Uint8Array([2, 0, 0, numGlyphsLessNotDef >> 8 & 0xff, numGlyphsLessNotDef & 0xff]);\n    } else {\n      const length = 1 + numGlyphsLessNotDef * 2;\n      out = new Uint8Array(length);\n      out[0] = 0;\n      let charsetIndex = 0;\n      const numCharsets = charset.charset.length;\n      let warned = false;\n      for (let i = 1; i < out.length; i += 2) {\n        let sid = 0;\n        if (charsetIndex < numCharsets) {\n          const name = charset.charset[charsetIndex++];\n          sid = strings.getSID(name);\n          if (sid === -1) {\n            sid = 0;\n            if (!warned) {\n              warned = true;\n              warn(`Couldn't find ${name} in CFF strings`);\n            }\n          }\n        }\n        out[i] = sid >> 8 & 0xff;\n        out[i + 1] = sid & 0xff;\n      }\n    }\n    return this.compileTypedArray(out);\n  }\n  compileEncoding(encoding) {\n    return this.compileTypedArray(encoding.raw);\n  }\n  compileFDSelect(fdSelect) {\n    const format = fdSelect.format;\n    let out, i;\n    switch (format) {\n      case 0:\n        out = new Uint8Array(1 + fdSelect.fdSelect.length);\n        out[0] = format;\n        for (i = 0; i < fdSelect.fdSelect.length; i++) {\n          out[i + 1] = fdSelect.fdSelect[i];\n        }\n        break;\n      case 3:\n        const start = 0;\n        let lastFD = fdSelect.fdSelect[0];\n        const ranges = [format, 0, 0, start >> 8 & 0xff, start & 0xff, lastFD];\n        for (i = 1; i < fdSelect.fdSelect.length; i++) {\n          const currentFD = fdSelect.fdSelect[i];\n          if (currentFD !== lastFD) {\n            ranges.push(i >> 8 & 0xff, i & 0xff, currentFD);\n            lastFD = currentFD;\n          }\n        }\n        const numRanges = (ranges.length - 3) / 3;\n        ranges[1] = numRanges >> 8 & 0xff;\n        ranges[2] = numRanges & 0xff;\n        ranges.push(i >> 8 & 0xff, i & 0xff);\n        out = new Uint8Array(ranges);\n        break;\n    }\n    return this.compileTypedArray(out);\n  }\n  compileTypedArray(data) {\n    return Array.from(data);\n  }\n  compileIndex(index, trackers = []) {\n    const objects = index.objects;\n    const count = objects.length;\n    if (count === 0) {\n      return [0, 0];\n    }\n    const data = [count >> 8 & 0xff, count & 0xff];\n    let lastOffset = 1,\n      i;\n    for (i = 0; i < count; ++i) {\n      lastOffset += objects[i].length;\n    }\n    let offsetSize;\n    if (lastOffset < 0x100) {\n      offsetSize = 1;\n    } else if (lastOffset < 0x10000) {\n      offsetSize = 2;\n    } else if (lastOffset < 0x1000000) {\n      offsetSize = 3;\n    } else {\n      offsetSize = 4;\n    }\n    data.push(offsetSize);\n    let relativeOffset = 1;\n    for (i = 0; i < count + 1; i++) {\n      if (offsetSize === 1) {\n        data.push(relativeOffset & 0xff);\n      } else if (offsetSize === 2) {\n        data.push(relativeOffset >> 8 & 0xff, relativeOffset & 0xff);\n      } else if (offsetSize === 3) {\n        data.push(relativeOffset >> 16 & 0xff, relativeOffset >> 8 & 0xff, relativeOffset & 0xff);\n      } else {\n        data.push(relativeOffset >>> 24 & 0xff, relativeOffset >> 16 & 0xff, relativeOffset >> 8 & 0xff, relativeOffset & 0xff);\n      }\n      if (objects[i]) {\n        relativeOffset += objects[i].length;\n      }\n    }\n    for (i = 0; i < count; i++) {\n      if (trackers[i]) {\n        trackers[i].offset(data.length);\n      }\n      data.push(...objects[i]);\n    }\n    return data;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/glyphlist.js\n\nconst getGlyphsUnicode = getLookupTableFactory(function (t) {\n  t.A = 0x0041;\n  t.AE = 0x00c6;\n  t.AEacute = 0x01fc;\n  t.AEmacron = 0x01e2;\n  t.AEsmall = 0xf7e6;\n  t.Aacute = 0x00c1;\n  t.Aacutesmall = 0xf7e1;\n  t.Abreve = 0x0102;\n  t.Abreveacute = 0x1eae;\n  t.Abrevecyrillic = 0x04d0;\n  t.Abrevedotbelow = 0x1eb6;\n  t.Abrevegrave = 0x1eb0;\n  t.Abrevehookabove = 0x1eb2;\n  t.Abrevetilde = 0x1eb4;\n  t.Acaron = 0x01cd;\n  t.Acircle = 0x24b6;\n  t.Acircumflex = 0x00c2;\n  t.Acircumflexacute = 0x1ea4;\n  t.Acircumflexdotbelow = 0x1eac;\n  t.Acircumflexgrave = 0x1ea6;\n  t.Acircumflexhookabove = 0x1ea8;\n  t.Acircumflexsmall = 0xf7e2;\n  t.Acircumflextilde = 0x1eaa;\n  t.Acute = 0xf6c9;\n  t.Acutesmall = 0xf7b4;\n  t.Acyrillic = 0x0410;\n  t.Adblgrave = 0x0200;\n  t.Adieresis = 0x00c4;\n  t.Adieresiscyrillic = 0x04d2;\n  t.Adieresismacron = 0x01de;\n  t.Adieresissmall = 0xf7e4;\n  t.Adotbelow = 0x1ea0;\n  t.Adotmacron = 0x01e0;\n  t.Agrave = 0x00c0;\n  t.Agravesmall = 0xf7e0;\n  t.Ahookabove = 0x1ea2;\n  t.Aiecyrillic = 0x04d4;\n  t.Ainvertedbreve = 0x0202;\n  t.Alpha = 0x0391;\n  t.Alphatonos = 0x0386;\n  t.Amacron = 0x0100;\n  t.Amonospace = 0xff21;\n  t.Aogonek = 0x0104;\n  t.Aring = 0x00c5;\n  t.Aringacute = 0x01fa;\n  t.Aringbelow = 0x1e00;\n  t.Aringsmall = 0xf7e5;\n  t.Asmall = 0xf761;\n  t.Atilde = 0x00c3;\n  t.Atildesmall = 0xf7e3;\n  t.Aybarmenian = 0x0531;\n  t.B = 0x0042;\n  t.Bcircle = 0x24b7;\n  t.Bdotaccent = 0x1e02;\n  t.Bdotbelow = 0x1e04;\n  t.Becyrillic = 0x0411;\n  t.Benarmenian = 0x0532;\n  t.Beta = 0x0392;\n  t.Bhook = 0x0181;\n  t.Blinebelow = 0x1e06;\n  t.Bmonospace = 0xff22;\n  t.Brevesmall = 0xf6f4;\n  t.Bsmall = 0xf762;\n  t.Btopbar = 0x0182;\n  t.C = 0x0043;\n  t.Caarmenian = 0x053e;\n  t.Cacute = 0x0106;\n  t.Caron = 0xf6ca;\n  t.Caronsmall = 0xf6f5;\n  t.Ccaron = 0x010c;\n  t.Ccedilla = 0x00c7;\n  t.Ccedillaacute = 0x1e08;\n  t.Ccedillasmall = 0xf7e7;\n  t.Ccircle = 0x24b8;\n  t.Ccircumflex = 0x0108;\n  t.Cdot = 0x010a;\n  t.Cdotaccent = 0x010a;\n  t.Cedillasmall = 0xf7b8;\n  t.Chaarmenian = 0x0549;\n  t.Cheabkhasiancyrillic = 0x04bc;\n  t.Checyrillic = 0x0427;\n  t.Chedescenderabkhasiancyrillic = 0x04be;\n  t.Chedescendercyrillic = 0x04b6;\n  t.Chedieresiscyrillic = 0x04f4;\n  t.Cheharmenian = 0x0543;\n  t.Chekhakassiancyrillic = 0x04cb;\n  t.Cheverticalstrokecyrillic = 0x04b8;\n  t.Chi = 0x03a7;\n  t.Chook = 0x0187;\n  t.Circumflexsmall = 0xf6f6;\n  t.Cmonospace = 0xff23;\n  t.Coarmenian = 0x0551;\n  t.Csmall = 0xf763;\n  t.D = 0x0044;\n  t.DZ = 0x01f1;\n  t.DZcaron = 0x01c4;\n  t.Daarmenian = 0x0534;\n  t.Dafrican = 0x0189;\n  t.Dcaron = 0x010e;\n  t.Dcedilla = 0x1e10;\n  t.Dcircle = 0x24b9;\n  t.Dcircumflexbelow = 0x1e12;\n  t.Dcroat = 0x0110;\n  t.Ddotaccent = 0x1e0a;\n  t.Ddotbelow = 0x1e0c;\n  t.Decyrillic = 0x0414;\n  t.Deicoptic = 0x03ee;\n  t.Delta = 0x2206;\n  t.Deltagreek = 0x0394;\n  t.Dhook = 0x018a;\n  t.Dieresis = 0xf6cb;\n  t.DieresisAcute = 0xf6cc;\n  t.DieresisGrave = 0xf6cd;\n  t.Dieresissmall = 0xf7a8;\n  t.Digammagreek = 0x03dc;\n  t.Djecyrillic = 0x0402;\n  t.Dlinebelow = 0x1e0e;\n  t.Dmonospace = 0xff24;\n  t.Dotaccentsmall = 0xf6f7;\n  t.Dslash = 0x0110;\n  t.Dsmall = 0xf764;\n  t.Dtopbar = 0x018b;\n  t.Dz = 0x01f2;\n  t.Dzcaron = 0x01c5;\n  t.Dzeabkhasiancyrillic = 0x04e0;\n  t.Dzecyrillic = 0x0405;\n  t.Dzhecyrillic = 0x040f;\n  t.E = 0x0045;\n  t.Eacute = 0x00c9;\n  t.Eacutesmall = 0xf7e9;\n  t.Ebreve = 0x0114;\n  t.Ecaron = 0x011a;\n  t.Ecedillabreve = 0x1e1c;\n  t.Echarmenian = 0x0535;\n  t.Ecircle = 0x24ba;\n  t.Ecircumflex = 0x00ca;\n  t.Ecircumflexacute = 0x1ebe;\n  t.Ecircumflexbelow = 0x1e18;\n  t.Ecircumflexdotbelow = 0x1ec6;\n  t.Ecircumflexgrave = 0x1ec0;\n  t.Ecircumflexhookabove = 0x1ec2;\n  t.Ecircumflexsmall = 0xf7ea;\n  t.Ecircumflextilde = 0x1ec4;\n  t.Ecyrillic = 0x0404;\n  t.Edblgrave = 0x0204;\n  t.Edieresis = 0x00cb;\n  t.Edieresissmall = 0xf7eb;\n  t.Edot = 0x0116;\n  t.Edotaccent = 0x0116;\n  t.Edotbelow = 0x1eb8;\n  t.Efcyrillic = 0x0424;\n  t.Egrave = 0x00c8;\n  t.Egravesmall = 0xf7e8;\n  t.Eharmenian = 0x0537;\n  t.Ehookabove = 0x1eba;\n  t.Eightroman = 0x2167;\n  t.Einvertedbreve = 0x0206;\n  t.Eiotifiedcyrillic = 0x0464;\n  t.Elcyrillic = 0x041b;\n  t.Elevenroman = 0x216a;\n  t.Emacron = 0x0112;\n  t.Emacronacute = 0x1e16;\n  t.Emacrongrave = 0x1e14;\n  t.Emcyrillic = 0x041c;\n  t.Emonospace = 0xff25;\n  t.Encyrillic = 0x041d;\n  t.Endescendercyrillic = 0x04a2;\n  t.Eng = 0x014a;\n  t.Enghecyrillic = 0x04a4;\n  t.Enhookcyrillic = 0x04c7;\n  t.Eogonek = 0x0118;\n  t.Eopen = 0x0190;\n  t.Epsilon = 0x0395;\n  t.Epsilontonos = 0x0388;\n  t.Ercyrillic = 0x0420;\n  t.Ereversed = 0x018e;\n  t.Ereversedcyrillic = 0x042d;\n  t.Escyrillic = 0x0421;\n  t.Esdescendercyrillic = 0x04aa;\n  t.Esh = 0x01a9;\n  t.Esmall = 0xf765;\n  t.Eta = 0x0397;\n  t.Etarmenian = 0x0538;\n  t.Etatonos = 0x0389;\n  t.Eth = 0x00d0;\n  t.Ethsmall = 0xf7f0;\n  t.Etilde = 0x1ebc;\n  t.Etildebelow = 0x1e1a;\n  t.Euro = 0x20ac;\n  t.Ezh = 0x01b7;\n  t.Ezhcaron = 0x01ee;\n  t.Ezhreversed = 0x01b8;\n  t.F = 0x0046;\n  t.Fcircle = 0x24bb;\n  t.Fdotaccent = 0x1e1e;\n  t.Feharmenian = 0x0556;\n  t.Feicoptic = 0x03e4;\n  t.Fhook = 0x0191;\n  t.Fitacyrillic = 0x0472;\n  t.Fiveroman = 0x2164;\n  t.Fmonospace = 0xff26;\n  t.Fourroman = 0x2163;\n  t.Fsmall = 0xf766;\n  t.G = 0x0047;\n  t.GBsquare = 0x3387;\n  t.Gacute = 0x01f4;\n  t.Gamma = 0x0393;\n  t.Gammaafrican = 0x0194;\n  t.Gangiacoptic = 0x03ea;\n  t.Gbreve = 0x011e;\n  t.Gcaron = 0x01e6;\n  t.Gcedilla = 0x0122;\n  t.Gcircle = 0x24bc;\n  t.Gcircumflex = 0x011c;\n  t.Gcommaaccent = 0x0122;\n  t.Gdot = 0x0120;\n  t.Gdotaccent = 0x0120;\n  t.Gecyrillic = 0x0413;\n  t.Ghadarmenian = 0x0542;\n  t.Ghemiddlehookcyrillic = 0x0494;\n  t.Ghestrokecyrillic = 0x0492;\n  t.Gheupturncyrillic = 0x0490;\n  t.Ghook = 0x0193;\n  t.Gimarmenian = 0x0533;\n  t.Gjecyrillic = 0x0403;\n  t.Gmacron = 0x1e20;\n  t.Gmonospace = 0xff27;\n  t.Grave = 0xf6ce;\n  t.Gravesmall = 0xf760;\n  t.Gsmall = 0xf767;\n  t.Gsmallhook = 0x029b;\n  t.Gstroke = 0x01e4;\n  t.H = 0x0048;\n  t.H18533 = 0x25cf;\n  t.H18543 = 0x25aa;\n  t.H18551 = 0x25ab;\n  t.H22073 = 0x25a1;\n  t.HPsquare = 0x33cb;\n  t.Haabkhasiancyrillic = 0x04a8;\n  t.Hadescendercyrillic = 0x04b2;\n  t.Hardsigncyrillic = 0x042a;\n  t.Hbar = 0x0126;\n  t.Hbrevebelow = 0x1e2a;\n  t.Hcedilla = 0x1e28;\n  t.Hcircle = 0x24bd;\n  t.Hcircumflex = 0x0124;\n  t.Hdieresis = 0x1e26;\n  t.Hdotaccent = 0x1e22;\n  t.Hdotbelow = 0x1e24;\n  t.Hmonospace = 0xff28;\n  t.Hoarmenian = 0x0540;\n  t.Horicoptic = 0x03e8;\n  t.Hsmall = 0xf768;\n  t.Hungarumlaut = 0xf6cf;\n  t.Hungarumlautsmall = 0xf6f8;\n  t.Hzsquare = 0x3390;\n  t.I = 0x0049;\n  t.IAcyrillic = 0x042f;\n  t.IJ = 0x0132;\n  t.IUcyrillic = 0x042e;\n  t.Iacute = 0x00cd;\n  t.Iacutesmall = 0xf7ed;\n  t.Ibreve = 0x012c;\n  t.Icaron = 0x01cf;\n  t.Icircle = 0x24be;\n  t.Icircumflex = 0x00ce;\n  t.Icircumflexsmall = 0xf7ee;\n  t.Icyrillic = 0x0406;\n  t.Idblgrave = 0x0208;\n  t.Idieresis = 0x00cf;\n  t.Idieresisacute = 0x1e2e;\n  t.Idieresiscyrillic = 0x04e4;\n  t.Idieresissmall = 0xf7ef;\n  t.Idot = 0x0130;\n  t.Idotaccent = 0x0130;\n  t.Idotbelow = 0x1eca;\n  t.Iebrevecyrillic = 0x04d6;\n  t.Iecyrillic = 0x0415;\n  t.Ifraktur = 0x2111;\n  t.Igrave = 0x00cc;\n  t.Igravesmall = 0xf7ec;\n  t.Ihookabove = 0x1ec8;\n  t.Iicyrillic = 0x0418;\n  t.Iinvertedbreve = 0x020a;\n  t.Iishortcyrillic = 0x0419;\n  t.Imacron = 0x012a;\n  t.Imacroncyrillic = 0x04e2;\n  t.Imonospace = 0xff29;\n  t.Iniarmenian = 0x053b;\n  t.Iocyrillic = 0x0401;\n  t.Iogonek = 0x012e;\n  t.Iota = 0x0399;\n  t.Iotaafrican = 0x0196;\n  t.Iotadieresis = 0x03aa;\n  t.Iotatonos = 0x038a;\n  t.Ismall = 0xf769;\n  t.Istroke = 0x0197;\n  t.Itilde = 0x0128;\n  t.Itildebelow = 0x1e2c;\n  t.Izhitsacyrillic = 0x0474;\n  t.Izhitsadblgravecyrillic = 0x0476;\n  t.J = 0x004a;\n  t.Jaarmenian = 0x0541;\n  t.Jcircle = 0x24bf;\n  t.Jcircumflex = 0x0134;\n  t.Jecyrillic = 0x0408;\n  t.Jheharmenian = 0x054b;\n  t.Jmonospace = 0xff2a;\n  t.Jsmall = 0xf76a;\n  t.K = 0x004b;\n  t.KBsquare = 0x3385;\n  t.KKsquare = 0x33cd;\n  t.Kabashkircyrillic = 0x04a0;\n  t.Kacute = 0x1e30;\n  t.Kacyrillic = 0x041a;\n  t.Kadescendercyrillic = 0x049a;\n  t.Kahookcyrillic = 0x04c3;\n  t.Kappa = 0x039a;\n  t.Kastrokecyrillic = 0x049e;\n  t.Kaverticalstrokecyrillic = 0x049c;\n  t.Kcaron = 0x01e8;\n  t.Kcedilla = 0x0136;\n  t.Kcircle = 0x24c0;\n  t.Kcommaaccent = 0x0136;\n  t.Kdotbelow = 0x1e32;\n  t.Keharmenian = 0x0554;\n  t.Kenarmenian = 0x053f;\n  t.Khacyrillic = 0x0425;\n  t.Kheicoptic = 0x03e6;\n  t.Khook = 0x0198;\n  t.Kjecyrillic = 0x040c;\n  t.Klinebelow = 0x1e34;\n  t.Kmonospace = 0xff2b;\n  t.Koppacyrillic = 0x0480;\n  t.Koppagreek = 0x03de;\n  t.Ksicyrillic = 0x046e;\n  t.Ksmall = 0xf76b;\n  t.L = 0x004c;\n  t.LJ = 0x01c7;\n  t.LL = 0xf6bf;\n  t.Lacute = 0x0139;\n  t.Lambda = 0x039b;\n  t.Lcaron = 0x013d;\n  t.Lcedilla = 0x013b;\n  t.Lcircle = 0x24c1;\n  t.Lcircumflexbelow = 0x1e3c;\n  t.Lcommaaccent = 0x013b;\n  t.Ldot = 0x013f;\n  t.Ldotaccent = 0x013f;\n  t.Ldotbelow = 0x1e36;\n  t.Ldotbelowmacron = 0x1e38;\n  t.Liwnarmenian = 0x053c;\n  t.Lj = 0x01c8;\n  t.Ljecyrillic = 0x0409;\n  t.Llinebelow = 0x1e3a;\n  t.Lmonospace = 0xff2c;\n  t.Lslash = 0x0141;\n  t.Lslashsmall = 0xf6f9;\n  t.Lsmall = 0xf76c;\n  t.M = 0x004d;\n  t.MBsquare = 0x3386;\n  t.Macron = 0xf6d0;\n  t.Macronsmall = 0xf7af;\n  t.Macute = 0x1e3e;\n  t.Mcircle = 0x24c2;\n  t.Mdotaccent = 0x1e40;\n  t.Mdotbelow = 0x1e42;\n  t.Menarmenian = 0x0544;\n  t.Mmonospace = 0xff2d;\n  t.Msmall = 0xf76d;\n  t.Mturned = 0x019c;\n  t.Mu = 0x039c;\n  t.N = 0x004e;\n  t.NJ = 0x01ca;\n  t.Nacute = 0x0143;\n  t.Ncaron = 0x0147;\n  t.Ncedilla = 0x0145;\n  t.Ncircle = 0x24c3;\n  t.Ncircumflexbelow = 0x1e4a;\n  t.Ncommaaccent = 0x0145;\n  t.Ndotaccent = 0x1e44;\n  t.Ndotbelow = 0x1e46;\n  t.Nhookleft = 0x019d;\n  t.Nineroman = 0x2168;\n  t.Nj = 0x01cb;\n  t.Njecyrillic = 0x040a;\n  t.Nlinebelow = 0x1e48;\n  t.Nmonospace = 0xff2e;\n  t.Nowarmenian = 0x0546;\n  t.Nsmall = 0xf76e;\n  t.Ntilde = 0x00d1;\n  t.Ntildesmall = 0xf7f1;\n  t.Nu = 0x039d;\n  t.O = 0x004f;\n  t.OE = 0x0152;\n  t.OEsmall = 0xf6fa;\n  t.Oacute = 0x00d3;\n  t.Oacutesmall = 0xf7f3;\n  t.Obarredcyrillic = 0x04e8;\n  t.Obarreddieresiscyrillic = 0x04ea;\n  t.Obreve = 0x014e;\n  t.Ocaron = 0x01d1;\n  t.Ocenteredtilde = 0x019f;\n  t.Ocircle = 0x24c4;\n  t.Ocircumflex = 0x00d4;\n  t.Ocircumflexacute = 0x1ed0;\n  t.Ocircumflexdotbelow = 0x1ed8;\n  t.Ocircumflexgrave = 0x1ed2;\n  t.Ocircumflexhookabove = 0x1ed4;\n  t.Ocircumflexsmall = 0xf7f4;\n  t.Ocircumflextilde = 0x1ed6;\n  t.Ocyrillic = 0x041e;\n  t.Odblacute = 0x0150;\n  t.Odblgrave = 0x020c;\n  t.Odieresis = 0x00d6;\n  t.Odieresiscyrillic = 0x04e6;\n  t.Odieresissmall = 0xf7f6;\n  t.Odotbelow = 0x1ecc;\n  t.Ogoneksmall = 0xf6fb;\n  t.Ograve = 0x00d2;\n  t.Ogravesmall = 0xf7f2;\n  t.Oharmenian = 0x0555;\n  t.Ohm = 0x2126;\n  t.Ohookabove = 0x1ece;\n  t.Ohorn = 0x01a0;\n  t.Ohornacute = 0x1eda;\n  t.Ohorndotbelow = 0x1ee2;\n  t.Ohorngrave = 0x1edc;\n  t.Ohornhookabove = 0x1ede;\n  t.Ohorntilde = 0x1ee0;\n  t.Ohungarumlaut = 0x0150;\n  t.Oi = 0x01a2;\n  t.Oinvertedbreve = 0x020e;\n  t.Omacron = 0x014c;\n  t.Omacronacute = 0x1e52;\n  t.Omacrongrave = 0x1e50;\n  t.Omega = 0x2126;\n  t.Omegacyrillic = 0x0460;\n  t.Omegagreek = 0x03a9;\n  t.Omegaroundcyrillic = 0x047a;\n  t.Omegatitlocyrillic = 0x047c;\n  t.Omegatonos = 0x038f;\n  t.Omicron = 0x039f;\n  t.Omicrontonos = 0x038c;\n  t.Omonospace = 0xff2f;\n  t.Oneroman = 0x2160;\n  t.Oogonek = 0x01ea;\n  t.Oogonekmacron = 0x01ec;\n  t.Oopen = 0x0186;\n  t.Oslash = 0x00d8;\n  t.Oslashacute = 0x01fe;\n  t.Oslashsmall = 0xf7f8;\n  t.Osmall = 0xf76f;\n  t.Ostrokeacute = 0x01fe;\n  t.Otcyrillic = 0x047e;\n  t.Otilde = 0x00d5;\n  t.Otildeacute = 0x1e4c;\n  t.Otildedieresis = 0x1e4e;\n  t.Otildesmall = 0xf7f5;\n  t.P = 0x0050;\n  t.Pacute = 0x1e54;\n  t.Pcircle = 0x24c5;\n  t.Pdotaccent = 0x1e56;\n  t.Pecyrillic = 0x041f;\n  t.Peharmenian = 0x054a;\n  t.Pemiddlehookcyrillic = 0x04a6;\n  t.Phi = 0x03a6;\n  t.Phook = 0x01a4;\n  t.Pi = 0x03a0;\n  t.Piwrarmenian = 0x0553;\n  t.Pmonospace = 0xff30;\n  t.Psi = 0x03a8;\n  t.Psicyrillic = 0x0470;\n  t.Psmall = 0xf770;\n  t.Q = 0x0051;\n  t.Qcircle = 0x24c6;\n  t.Qmonospace = 0xff31;\n  t.Qsmall = 0xf771;\n  t.R = 0x0052;\n  t.Raarmenian = 0x054c;\n  t.Racute = 0x0154;\n  t.Rcaron = 0x0158;\n  t.Rcedilla = 0x0156;\n  t.Rcircle = 0x24c7;\n  t.Rcommaaccent = 0x0156;\n  t.Rdblgrave = 0x0210;\n  t.Rdotaccent = 0x1e58;\n  t.Rdotbelow = 0x1e5a;\n  t.Rdotbelowmacron = 0x1e5c;\n  t.Reharmenian = 0x0550;\n  t.Rfraktur = 0x211c;\n  t.Rho = 0x03a1;\n  t.Ringsmall = 0xf6fc;\n  t.Rinvertedbreve = 0x0212;\n  t.Rlinebelow = 0x1e5e;\n  t.Rmonospace = 0xff32;\n  t.Rsmall = 0xf772;\n  t.Rsmallinverted = 0x0281;\n  t.Rsmallinvertedsuperior = 0x02b6;\n  t.S = 0x0053;\n  t.SF010000 = 0x250c;\n  t.SF020000 = 0x2514;\n  t.SF030000 = 0x2510;\n  t.SF040000 = 0x2518;\n  t.SF050000 = 0x253c;\n  t.SF060000 = 0x252c;\n  t.SF070000 = 0x2534;\n  t.SF080000 = 0x251c;\n  t.SF090000 = 0x2524;\n  t.SF100000 = 0x2500;\n  t.SF110000 = 0x2502;\n  t.SF190000 = 0x2561;\n  t.SF200000 = 0x2562;\n  t.SF210000 = 0x2556;\n  t.SF220000 = 0x2555;\n  t.SF230000 = 0x2563;\n  t.SF240000 = 0x2551;\n  t.SF250000 = 0x2557;\n  t.SF260000 = 0x255d;\n  t.SF270000 = 0x255c;\n  t.SF280000 = 0x255b;\n  t.SF360000 = 0x255e;\n  t.SF370000 = 0x255f;\n  t.SF380000 = 0x255a;\n  t.SF390000 = 0x2554;\n  t.SF400000 = 0x2569;\n  t.SF410000 = 0x2566;\n  t.SF420000 = 0x2560;\n  t.SF430000 = 0x2550;\n  t.SF440000 = 0x256c;\n  t.SF450000 = 0x2567;\n  t.SF460000 = 0x2568;\n  t.SF470000 = 0x2564;\n  t.SF480000 = 0x2565;\n  t.SF490000 = 0x2559;\n  t.SF500000 = 0x2558;\n  t.SF510000 = 0x2552;\n  t.SF520000 = 0x2553;\n  t.SF530000 = 0x256b;\n  t.SF540000 = 0x256a;\n  t.Sacute = 0x015a;\n  t.Sacutedotaccent = 0x1e64;\n  t.Sampigreek = 0x03e0;\n  t.Scaron = 0x0160;\n  t.Scarondotaccent = 0x1e66;\n  t.Scaronsmall = 0xf6fd;\n  t.Scedilla = 0x015e;\n  t.Schwa = 0x018f;\n  t.Schwacyrillic = 0x04d8;\n  t.Schwadieresiscyrillic = 0x04da;\n  t.Scircle = 0x24c8;\n  t.Scircumflex = 0x015c;\n  t.Scommaaccent = 0x0218;\n  t.Sdotaccent = 0x1e60;\n  t.Sdotbelow = 0x1e62;\n  t.Sdotbelowdotaccent = 0x1e68;\n  t.Seharmenian = 0x054d;\n  t.Sevenroman = 0x2166;\n  t.Shaarmenian = 0x0547;\n  t.Shacyrillic = 0x0428;\n  t.Shchacyrillic = 0x0429;\n  t.Sheicoptic = 0x03e2;\n  t.Shhacyrillic = 0x04ba;\n  t.Shimacoptic = 0x03ec;\n  t.Sigma = 0x03a3;\n  t.Sixroman = 0x2165;\n  t.Smonospace = 0xff33;\n  t.Softsigncyrillic = 0x042c;\n  t.Ssmall = 0xf773;\n  t.Stigmagreek = 0x03da;\n  t.T = 0x0054;\n  t.Tau = 0x03a4;\n  t.Tbar = 0x0166;\n  t.Tcaron = 0x0164;\n  t.Tcedilla = 0x0162;\n  t.Tcircle = 0x24c9;\n  t.Tcircumflexbelow = 0x1e70;\n  t.Tcommaaccent = 0x0162;\n  t.Tdotaccent = 0x1e6a;\n  t.Tdotbelow = 0x1e6c;\n  t.Tecyrillic = 0x0422;\n  t.Tedescendercyrillic = 0x04ac;\n  t.Tenroman = 0x2169;\n  t.Tetsecyrillic = 0x04b4;\n  t.Theta = 0x0398;\n  t.Thook = 0x01ac;\n  t.Thorn = 0x00de;\n  t.Thornsmall = 0xf7fe;\n  t.Threeroman = 0x2162;\n  t.Tildesmall = 0xf6fe;\n  t.Tiwnarmenian = 0x054f;\n  t.Tlinebelow = 0x1e6e;\n  t.Tmonospace = 0xff34;\n  t.Toarmenian = 0x0539;\n  t.Tonefive = 0x01bc;\n  t.Tonesix = 0x0184;\n  t.Tonetwo = 0x01a7;\n  t.Tretroflexhook = 0x01ae;\n  t.Tsecyrillic = 0x0426;\n  t.Tshecyrillic = 0x040b;\n  t.Tsmall = 0xf774;\n  t.Twelveroman = 0x216b;\n  t.Tworoman = 0x2161;\n  t.U = 0x0055;\n  t.Uacute = 0x00da;\n  t.Uacutesmall = 0xf7fa;\n  t.Ubreve = 0x016c;\n  t.Ucaron = 0x01d3;\n  t.Ucircle = 0x24ca;\n  t.Ucircumflex = 0x00db;\n  t.Ucircumflexbelow = 0x1e76;\n  t.Ucircumflexsmall = 0xf7fb;\n  t.Ucyrillic = 0x0423;\n  t.Udblacute = 0x0170;\n  t.Udblgrave = 0x0214;\n  t.Udieresis = 0x00dc;\n  t.Udieresisacute = 0x01d7;\n  t.Udieresisbelow = 0x1e72;\n  t.Udieresiscaron = 0x01d9;\n  t.Udieresiscyrillic = 0x04f0;\n  t.Udieresisgrave = 0x01db;\n  t.Udieresismacron = 0x01d5;\n  t.Udieresissmall = 0xf7fc;\n  t.Udotbelow = 0x1ee4;\n  t.Ugrave = 0x00d9;\n  t.Ugravesmall = 0xf7f9;\n  t.Uhookabove = 0x1ee6;\n  t.Uhorn = 0x01af;\n  t.Uhornacute = 0x1ee8;\n  t.Uhorndotbelow = 0x1ef0;\n  t.Uhorngrave = 0x1eea;\n  t.Uhornhookabove = 0x1eec;\n  t.Uhorntilde = 0x1eee;\n  t.Uhungarumlaut = 0x0170;\n  t.Uhungarumlautcyrillic = 0x04f2;\n  t.Uinvertedbreve = 0x0216;\n  t.Ukcyrillic = 0x0478;\n  t.Umacron = 0x016a;\n  t.Umacroncyrillic = 0x04ee;\n  t.Umacrondieresis = 0x1e7a;\n  t.Umonospace = 0xff35;\n  t.Uogonek = 0x0172;\n  t.Upsilon = 0x03a5;\n  t.Upsilon1 = 0x03d2;\n  t.Upsilonacutehooksymbolgreek = 0x03d3;\n  t.Upsilonafrican = 0x01b1;\n  t.Upsilondieresis = 0x03ab;\n  t.Upsilondieresishooksymbolgreek = 0x03d4;\n  t.Upsilonhooksymbol = 0x03d2;\n  t.Upsilontonos = 0x038e;\n  t.Uring = 0x016e;\n  t.Ushortcyrillic = 0x040e;\n  t.Usmall = 0xf775;\n  t.Ustraightcyrillic = 0x04ae;\n  t.Ustraightstrokecyrillic = 0x04b0;\n  t.Utilde = 0x0168;\n  t.Utildeacute = 0x1e78;\n  t.Utildebelow = 0x1e74;\n  t.V = 0x0056;\n  t.Vcircle = 0x24cb;\n  t.Vdotbelow = 0x1e7e;\n  t.Vecyrillic = 0x0412;\n  t.Vewarmenian = 0x054e;\n  t.Vhook = 0x01b2;\n  t.Vmonospace = 0xff36;\n  t.Voarmenian = 0x0548;\n  t.Vsmall = 0xf776;\n  t.Vtilde = 0x1e7c;\n  t.W = 0x0057;\n  t.Wacute = 0x1e82;\n  t.Wcircle = 0x24cc;\n  t.Wcircumflex = 0x0174;\n  t.Wdieresis = 0x1e84;\n  t.Wdotaccent = 0x1e86;\n  t.Wdotbelow = 0x1e88;\n  t.Wgrave = 0x1e80;\n  t.Wmonospace = 0xff37;\n  t.Wsmall = 0xf777;\n  t.X = 0x0058;\n  t.Xcircle = 0x24cd;\n  t.Xdieresis = 0x1e8c;\n  t.Xdotaccent = 0x1e8a;\n  t.Xeharmenian = 0x053d;\n  t.Xi = 0x039e;\n  t.Xmonospace = 0xff38;\n  t.Xsmall = 0xf778;\n  t.Y = 0x0059;\n  t.Yacute = 0x00dd;\n  t.Yacutesmall = 0xf7fd;\n  t.Yatcyrillic = 0x0462;\n  t.Ycircle = 0x24ce;\n  t.Ycircumflex = 0x0176;\n  t.Ydieresis = 0x0178;\n  t.Ydieresissmall = 0xf7ff;\n  t.Ydotaccent = 0x1e8e;\n  t.Ydotbelow = 0x1ef4;\n  t.Yericyrillic = 0x042b;\n  t.Yerudieresiscyrillic = 0x04f8;\n  t.Ygrave = 0x1ef2;\n  t.Yhook = 0x01b3;\n  t.Yhookabove = 0x1ef6;\n  t.Yiarmenian = 0x0545;\n  t.Yicyrillic = 0x0407;\n  t.Yiwnarmenian = 0x0552;\n  t.Ymonospace = 0xff39;\n  t.Ysmall = 0xf779;\n  t.Ytilde = 0x1ef8;\n  t.Yusbigcyrillic = 0x046a;\n  t.Yusbigiotifiedcyrillic = 0x046c;\n  t.Yuslittlecyrillic = 0x0466;\n  t.Yuslittleiotifiedcyrillic = 0x0468;\n  t.Z = 0x005a;\n  t.Zaarmenian = 0x0536;\n  t.Zacute = 0x0179;\n  t.Zcaron = 0x017d;\n  t.Zcaronsmall = 0xf6ff;\n  t.Zcircle = 0x24cf;\n  t.Zcircumflex = 0x1e90;\n  t.Zdot = 0x017b;\n  t.Zdotaccent = 0x017b;\n  t.Zdotbelow = 0x1e92;\n  t.Zecyrillic = 0x0417;\n  t.Zedescendercyrillic = 0x0498;\n  t.Zedieresiscyrillic = 0x04de;\n  t.Zeta = 0x0396;\n  t.Zhearmenian = 0x053a;\n  t.Zhebrevecyrillic = 0x04c1;\n  t.Zhecyrillic = 0x0416;\n  t.Zhedescendercyrillic = 0x0496;\n  t.Zhedieresiscyrillic = 0x04dc;\n  t.Zlinebelow = 0x1e94;\n  t.Zmonospace = 0xff3a;\n  t.Zsmall = 0xf77a;\n  t.Zstroke = 0x01b5;\n  t.a = 0x0061;\n  t.aabengali = 0x0986;\n  t.aacute = 0x00e1;\n  t.aadeva = 0x0906;\n  t.aagujarati = 0x0a86;\n  t.aagurmukhi = 0x0a06;\n  t.aamatragurmukhi = 0x0a3e;\n  t.aarusquare = 0x3303;\n  t.aavowelsignbengali = 0x09be;\n  t.aavowelsigndeva = 0x093e;\n  t.aavowelsigngujarati = 0x0abe;\n  t.abbreviationmarkarmenian = 0x055f;\n  t.abbreviationsigndeva = 0x0970;\n  t.abengali = 0x0985;\n  t.abopomofo = 0x311a;\n  t.abreve = 0x0103;\n  t.abreveacute = 0x1eaf;\n  t.abrevecyrillic = 0x04d1;\n  t.abrevedotbelow = 0x1eb7;\n  t.abrevegrave = 0x1eb1;\n  t.abrevehookabove = 0x1eb3;\n  t.abrevetilde = 0x1eb5;\n  t.acaron = 0x01ce;\n  t.acircle = 0x24d0;\n  t.acircumflex = 0x00e2;\n  t.acircumflexacute = 0x1ea5;\n  t.acircumflexdotbelow = 0x1ead;\n  t.acircumflexgrave = 0x1ea7;\n  t.acircumflexhookabove = 0x1ea9;\n  t.acircumflextilde = 0x1eab;\n  t.acute = 0x00b4;\n  t.acutebelowcmb = 0x0317;\n  t.acutecmb = 0x0301;\n  t.acutecomb = 0x0301;\n  t.acutedeva = 0x0954;\n  t.acutelowmod = 0x02cf;\n  t.acutetonecmb = 0x0341;\n  t.acyrillic = 0x0430;\n  t.adblgrave = 0x0201;\n  t.addakgurmukhi = 0x0a71;\n  t.adeva = 0x0905;\n  t.adieresis = 0x00e4;\n  t.adieresiscyrillic = 0x04d3;\n  t.adieresismacron = 0x01df;\n  t.adotbelow = 0x1ea1;\n  t.adotmacron = 0x01e1;\n  t.ae = 0x00e6;\n  t.aeacute = 0x01fd;\n  t.aekorean = 0x3150;\n  t.aemacron = 0x01e3;\n  t.afii00208 = 0x2015;\n  t.afii08941 = 0x20a4;\n  t.afii10017 = 0x0410;\n  t.afii10018 = 0x0411;\n  t.afii10019 = 0x0412;\n  t.afii10020 = 0x0413;\n  t.afii10021 = 0x0414;\n  t.afii10022 = 0x0415;\n  t.afii10023 = 0x0401;\n  t.afii10024 = 0x0416;\n  t.afii10025 = 0x0417;\n  t.afii10026 = 0x0418;\n  t.afii10027 = 0x0419;\n  t.afii10028 = 0x041a;\n  t.afii10029 = 0x041b;\n  t.afii10030 = 0x041c;\n  t.afii10031 = 0x041d;\n  t.afii10032 = 0x041e;\n  t.afii10033 = 0x041f;\n  t.afii10034 = 0x0420;\n  t.afii10035 = 0x0421;\n  t.afii10036 = 0x0422;\n  t.afii10037 = 0x0423;\n  t.afii10038 = 0x0424;\n  t.afii10039 = 0x0425;\n  t.afii10040 = 0x0426;\n  t.afii10041 = 0x0427;\n  t.afii10042 = 0x0428;\n  t.afii10043 = 0x0429;\n  t.afii10044 = 0x042a;\n  t.afii10045 = 0x042b;\n  t.afii10046 = 0x042c;\n  t.afii10047 = 0x042d;\n  t.afii10048 = 0x042e;\n  t.afii10049 = 0x042f;\n  t.afii10050 = 0x0490;\n  t.afii10051 = 0x0402;\n  t.afii10052 = 0x0403;\n  t.afii10053 = 0x0404;\n  t.afii10054 = 0x0405;\n  t.afii10055 = 0x0406;\n  t.afii10056 = 0x0407;\n  t.afii10057 = 0x0408;\n  t.afii10058 = 0x0409;\n  t.afii10059 = 0x040a;\n  t.afii10060 = 0x040b;\n  t.afii10061 = 0x040c;\n  t.afii10062 = 0x040e;\n  t.afii10063 = 0xf6c4;\n  t.afii10064 = 0xf6c5;\n  t.afii10065 = 0x0430;\n  t.afii10066 = 0x0431;\n  t.afii10067 = 0x0432;\n  t.afii10068 = 0x0433;\n  t.afii10069 = 0x0434;\n  t.afii10070 = 0x0435;\n  t.afii10071 = 0x0451;\n  t.afii10072 = 0x0436;\n  t.afii10073 = 0x0437;\n  t.afii10074 = 0x0438;\n  t.afii10075 = 0x0439;\n  t.afii10076 = 0x043a;\n  t.afii10077 = 0x043b;\n  t.afii10078 = 0x043c;\n  t.afii10079 = 0x043d;\n  t.afii10080 = 0x043e;\n  t.afii10081 = 0x043f;\n  t.afii10082 = 0x0440;\n  t.afii10083 = 0x0441;\n  t.afii10084 = 0x0442;\n  t.afii10085 = 0x0443;\n  t.afii10086 = 0x0444;\n  t.afii10087 = 0x0445;\n  t.afii10088 = 0x0446;\n  t.afii10089 = 0x0447;\n  t.afii10090 = 0x0448;\n  t.afii10091 = 0x0449;\n  t.afii10092 = 0x044a;\n  t.afii10093 = 0x044b;\n  t.afii10094 = 0x044c;\n  t.afii10095 = 0x044d;\n  t.afii10096 = 0x044e;\n  t.afii10097 = 0x044f;\n  t.afii10098 = 0x0491;\n  t.afii10099 = 0x0452;\n  t.afii10100 = 0x0453;\n  t.afii10101 = 0x0454;\n  t.afii10102 = 0x0455;\n  t.afii10103 = 0x0456;\n  t.afii10104 = 0x0457;\n  t.afii10105 = 0x0458;\n  t.afii10106 = 0x0459;\n  t.afii10107 = 0x045a;\n  t.afii10108 = 0x045b;\n  t.afii10109 = 0x045c;\n  t.afii10110 = 0x045e;\n  t.afii10145 = 0x040f;\n  t.afii10146 = 0x0462;\n  t.afii10147 = 0x0472;\n  t.afii10148 = 0x0474;\n  t.afii10192 = 0xf6c6;\n  t.afii10193 = 0x045f;\n  t.afii10194 = 0x0463;\n  t.afii10195 = 0x0473;\n  t.afii10196 = 0x0475;\n  t.afii10831 = 0xf6c7;\n  t.afii10832 = 0xf6c8;\n  t.afii10846 = 0x04d9;\n  t.afii299 = 0x200e;\n  t.afii300 = 0x200f;\n  t.afii301 = 0x200d;\n  t.afii57381 = 0x066a;\n  t.afii57388 = 0x060c;\n  t.afii57392 = 0x0660;\n  t.afii57393 = 0x0661;\n  t.afii57394 = 0x0662;\n  t.afii57395 = 0x0663;\n  t.afii57396 = 0x0664;\n  t.afii57397 = 0x0665;\n  t.afii57398 = 0x0666;\n  t.afii57399 = 0x0667;\n  t.afii57400 = 0x0668;\n  t.afii57401 = 0x0669;\n  t.afii57403 = 0x061b;\n  t.afii57407 = 0x061f;\n  t.afii57409 = 0x0621;\n  t.afii57410 = 0x0622;\n  t.afii57411 = 0x0623;\n  t.afii57412 = 0x0624;\n  t.afii57413 = 0x0625;\n  t.afii57414 = 0x0626;\n  t.afii57415 = 0x0627;\n  t.afii57416 = 0x0628;\n  t.afii57417 = 0x0629;\n  t.afii57418 = 0x062a;\n  t.afii57419 = 0x062b;\n  t.afii57420 = 0x062c;\n  t.afii57421 = 0x062d;\n  t.afii57422 = 0x062e;\n  t.afii57423 = 0x062f;\n  t.afii57424 = 0x0630;\n  t.afii57425 = 0x0631;\n  t.afii57426 = 0x0632;\n  t.afii57427 = 0x0633;\n  t.afii57428 = 0x0634;\n  t.afii57429 = 0x0635;\n  t.afii57430 = 0x0636;\n  t.afii57431 = 0x0637;\n  t.afii57432 = 0x0638;\n  t.afii57433 = 0x0639;\n  t.afii57434 = 0x063a;\n  t.afii57440 = 0x0640;\n  t.afii57441 = 0x0641;\n  t.afii57442 = 0x0642;\n  t.afii57443 = 0x0643;\n  t.afii57444 = 0x0644;\n  t.afii57445 = 0x0645;\n  t.afii57446 = 0x0646;\n  t.afii57448 = 0x0648;\n  t.afii57449 = 0x0649;\n  t.afii57450 = 0x064a;\n  t.afii57451 = 0x064b;\n  t.afii57452 = 0x064c;\n  t.afii57453 = 0x064d;\n  t.afii57454 = 0x064e;\n  t.afii57455 = 0x064f;\n  t.afii57456 = 0x0650;\n  t.afii57457 = 0x0651;\n  t.afii57458 = 0x0652;\n  t.afii57470 = 0x0647;\n  t.afii57505 = 0x06a4;\n  t.afii57506 = 0x067e;\n  t.afii57507 = 0x0686;\n  t.afii57508 = 0x0698;\n  t.afii57509 = 0x06af;\n  t.afii57511 = 0x0679;\n  t.afii57512 = 0x0688;\n  t.afii57513 = 0x0691;\n  t.afii57514 = 0x06ba;\n  t.afii57519 = 0x06d2;\n  t.afii57534 = 0x06d5;\n  t.afii57636 = 0x20aa;\n  t.afii57645 = 0x05be;\n  t.afii57658 = 0x05c3;\n  t.afii57664 = 0x05d0;\n  t.afii57665 = 0x05d1;\n  t.afii57666 = 0x05d2;\n  t.afii57667 = 0x05d3;\n  t.afii57668 = 0x05d4;\n  t.afii57669 = 0x05d5;\n  t.afii57670 = 0x05d6;\n  t.afii57671 = 0x05d7;\n  t.afii57672 = 0x05d8;\n  t.afii57673 = 0x05d9;\n  t.afii57674 = 0x05da;\n  t.afii57675 = 0x05db;\n  t.afii57676 = 0x05dc;\n  t.afii57677 = 0x05dd;\n  t.afii57678 = 0x05de;\n  t.afii57679 = 0x05df;\n  t.afii57680 = 0x05e0;\n  t.afii57681 = 0x05e1;\n  t.afii57682 = 0x05e2;\n  t.afii57683 = 0x05e3;\n  t.afii57684 = 0x05e4;\n  t.afii57685 = 0x05e5;\n  t.afii57686 = 0x05e6;\n  t.afii57687 = 0x05e7;\n  t.afii57688 = 0x05e8;\n  t.afii57689 = 0x05e9;\n  t.afii57690 = 0x05ea;\n  t.afii57694 = 0xfb2a;\n  t.afii57695 = 0xfb2b;\n  t.afii57700 = 0xfb4b;\n  t.afii57705 = 0xfb1f;\n  t.afii57716 = 0x05f0;\n  t.afii57717 = 0x05f1;\n  t.afii57718 = 0x05f2;\n  t.afii57723 = 0xfb35;\n  t.afii57793 = 0x05b4;\n  t.afii57794 = 0x05b5;\n  t.afii57795 = 0x05b6;\n  t.afii57796 = 0x05bb;\n  t.afii57797 = 0x05b8;\n  t.afii57798 = 0x05b7;\n  t.afii57799 = 0x05b0;\n  t.afii57800 = 0x05b2;\n  t.afii57801 = 0x05b1;\n  t.afii57802 = 0x05b3;\n  t.afii57803 = 0x05c2;\n  t.afii57804 = 0x05c1;\n  t.afii57806 = 0x05b9;\n  t.afii57807 = 0x05bc;\n  t.afii57839 = 0x05bd;\n  t.afii57841 = 0x05bf;\n  t.afii57842 = 0x05c0;\n  t.afii57929 = 0x02bc;\n  t.afii61248 = 0x2105;\n  t.afii61289 = 0x2113;\n  t.afii61352 = 0x2116;\n  t.afii61573 = 0x202c;\n  t.afii61574 = 0x202d;\n  t.afii61575 = 0x202e;\n  t.afii61664 = 0x200c;\n  t.afii63167 = 0x066d;\n  t.afii64937 = 0x02bd;\n  t.agrave = 0x00e0;\n  t.agujarati = 0x0a85;\n  t.agurmukhi = 0x0a05;\n  t.ahiragana = 0x3042;\n  t.ahookabove = 0x1ea3;\n  t.aibengali = 0x0990;\n  t.aibopomofo = 0x311e;\n  t.aideva = 0x0910;\n  t.aiecyrillic = 0x04d5;\n  t.aigujarati = 0x0a90;\n  t.aigurmukhi = 0x0a10;\n  t.aimatragurmukhi = 0x0a48;\n  t.ainarabic = 0x0639;\n  t.ainfinalarabic = 0xfeca;\n  t.aininitialarabic = 0xfecb;\n  t.ainmedialarabic = 0xfecc;\n  t.ainvertedbreve = 0x0203;\n  t.aivowelsignbengali = 0x09c8;\n  t.aivowelsigndeva = 0x0948;\n  t.aivowelsigngujarati = 0x0ac8;\n  t.akatakana = 0x30a2;\n  t.akatakanahalfwidth = 0xff71;\n  t.akorean = 0x314f;\n  t.alef = 0x05d0;\n  t.alefarabic = 0x0627;\n  t.alefdageshhebrew = 0xfb30;\n  t.aleffinalarabic = 0xfe8e;\n  t.alefhamzaabovearabic = 0x0623;\n  t.alefhamzaabovefinalarabic = 0xfe84;\n  t.alefhamzabelowarabic = 0x0625;\n  t.alefhamzabelowfinalarabic = 0xfe88;\n  t.alefhebrew = 0x05d0;\n  t.aleflamedhebrew = 0xfb4f;\n  t.alefmaddaabovearabic = 0x0622;\n  t.alefmaddaabovefinalarabic = 0xfe82;\n  t.alefmaksuraarabic = 0x0649;\n  t.alefmaksurafinalarabic = 0xfef0;\n  t.alefmaksurainitialarabic = 0xfef3;\n  t.alefmaksuramedialarabic = 0xfef4;\n  t.alefpatahhebrew = 0xfb2e;\n  t.alefqamatshebrew = 0xfb2f;\n  t.aleph = 0x2135;\n  t.allequal = 0x224c;\n  t.alpha = 0x03b1;\n  t.alphatonos = 0x03ac;\n  t.amacron = 0x0101;\n  t.amonospace = 0xff41;\n  t.ampersand = 0x0026;\n  t.ampersandmonospace = 0xff06;\n  t.ampersandsmall = 0xf726;\n  t.amsquare = 0x33c2;\n  t.anbopomofo = 0x3122;\n  t.angbopomofo = 0x3124;\n  t.angbracketleft = 0x3008;\n  t.angbracketright = 0x3009;\n  t.angkhankhuthai = 0x0e5a;\n  t.angle = 0x2220;\n  t.anglebracketleft = 0x3008;\n  t.anglebracketleftvertical = 0xfe3f;\n  t.anglebracketright = 0x3009;\n  t.anglebracketrightvertical = 0xfe40;\n  t.angleleft = 0x2329;\n  t.angleright = 0x232a;\n  t.angstrom = 0x212b;\n  t.anoteleia = 0x0387;\n  t.anudattadeva = 0x0952;\n  t.anusvarabengali = 0x0982;\n  t.anusvaradeva = 0x0902;\n  t.anusvaragujarati = 0x0a82;\n  t.aogonek = 0x0105;\n  t.apaatosquare = 0x3300;\n  t.aparen = 0x249c;\n  t.apostrophearmenian = 0x055a;\n  t.apostrophemod = 0x02bc;\n  t.apple = 0xf8ff;\n  t.approaches = 0x2250;\n  t.approxequal = 0x2248;\n  t.approxequalorimage = 0x2252;\n  t.approximatelyequal = 0x2245;\n  t.araeaekorean = 0x318e;\n  t.araeakorean = 0x318d;\n  t.arc = 0x2312;\n  t.arighthalfring = 0x1e9a;\n  t.aring = 0x00e5;\n  t.aringacute = 0x01fb;\n  t.aringbelow = 0x1e01;\n  t.arrowboth = 0x2194;\n  t.arrowdashdown = 0x21e3;\n  t.arrowdashleft = 0x21e0;\n  t.arrowdashright = 0x21e2;\n  t.arrowdashup = 0x21e1;\n  t.arrowdblboth = 0x21d4;\n  t.arrowdbldown = 0x21d3;\n  t.arrowdblleft = 0x21d0;\n  t.arrowdblright = 0x21d2;\n  t.arrowdblup = 0x21d1;\n  t.arrowdown = 0x2193;\n  t.arrowdownleft = 0x2199;\n  t.arrowdownright = 0x2198;\n  t.arrowdownwhite = 0x21e9;\n  t.arrowheaddownmod = 0x02c5;\n  t.arrowheadleftmod = 0x02c2;\n  t.arrowheadrightmod = 0x02c3;\n  t.arrowheadupmod = 0x02c4;\n  t.arrowhorizex = 0xf8e7;\n  t.arrowleft = 0x2190;\n  t.arrowleftdbl = 0x21d0;\n  t.arrowleftdblstroke = 0x21cd;\n  t.arrowleftoverright = 0x21c6;\n  t.arrowleftwhite = 0x21e6;\n  t.arrowright = 0x2192;\n  t.arrowrightdblstroke = 0x21cf;\n  t.arrowrightheavy = 0x279e;\n  t.arrowrightoverleft = 0x21c4;\n  t.arrowrightwhite = 0x21e8;\n  t.arrowtableft = 0x21e4;\n  t.arrowtabright = 0x21e5;\n  t.arrowup = 0x2191;\n  t.arrowupdn = 0x2195;\n  t.arrowupdnbse = 0x21a8;\n  t.arrowupdownbase = 0x21a8;\n  t.arrowupleft = 0x2196;\n  t.arrowupleftofdown = 0x21c5;\n  t.arrowupright = 0x2197;\n  t.arrowupwhite = 0x21e7;\n  t.arrowvertex = 0xf8e6;\n  t.asciicircum = 0x005e;\n  t.asciicircummonospace = 0xff3e;\n  t.asciitilde = 0x007e;\n  t.asciitildemonospace = 0xff5e;\n  t.ascript = 0x0251;\n  t.ascriptturned = 0x0252;\n  t.asmallhiragana = 0x3041;\n  t.asmallkatakana = 0x30a1;\n  t.asmallkatakanahalfwidth = 0xff67;\n  t.asterisk = 0x002a;\n  t.asteriskaltonearabic = 0x066d;\n  t.asteriskarabic = 0x066d;\n  t.asteriskmath = 0x2217;\n  t.asteriskmonospace = 0xff0a;\n  t.asterisksmall = 0xfe61;\n  t.asterism = 0x2042;\n  t.asuperior = 0xf6e9;\n  t.asymptoticallyequal = 0x2243;\n  t.at = 0x0040;\n  t.atilde = 0x00e3;\n  t.atmonospace = 0xff20;\n  t.atsmall = 0xfe6b;\n  t.aturned = 0x0250;\n  t.aubengali = 0x0994;\n  t.aubopomofo = 0x3120;\n  t.audeva = 0x0914;\n  t.augujarati = 0x0a94;\n  t.augurmukhi = 0x0a14;\n  t.aulengthmarkbengali = 0x09d7;\n  t.aumatragurmukhi = 0x0a4c;\n  t.auvowelsignbengali = 0x09cc;\n  t.auvowelsigndeva = 0x094c;\n  t.auvowelsigngujarati = 0x0acc;\n  t.avagrahadeva = 0x093d;\n  t.aybarmenian = 0x0561;\n  t.ayin = 0x05e2;\n  t.ayinaltonehebrew = 0xfb20;\n  t.ayinhebrew = 0x05e2;\n  t.b = 0x0062;\n  t.babengali = 0x09ac;\n  t.backslash = 0x005c;\n  t.backslashmonospace = 0xff3c;\n  t.badeva = 0x092c;\n  t.bagujarati = 0x0aac;\n  t.bagurmukhi = 0x0a2c;\n  t.bahiragana = 0x3070;\n  t.bahtthai = 0x0e3f;\n  t.bakatakana = 0x30d0;\n  t.bar = 0x007c;\n  t.barmonospace = 0xff5c;\n  t.bbopomofo = 0x3105;\n  t.bcircle = 0x24d1;\n  t.bdotaccent = 0x1e03;\n  t.bdotbelow = 0x1e05;\n  t.beamedsixteenthnotes = 0x266c;\n  t.because = 0x2235;\n  t.becyrillic = 0x0431;\n  t.beharabic = 0x0628;\n  t.behfinalarabic = 0xfe90;\n  t.behinitialarabic = 0xfe91;\n  t.behiragana = 0x3079;\n  t.behmedialarabic = 0xfe92;\n  t.behmeeminitialarabic = 0xfc9f;\n  t.behmeemisolatedarabic = 0xfc08;\n  t.behnoonfinalarabic = 0xfc6d;\n  t.bekatakana = 0x30d9;\n  t.benarmenian = 0x0562;\n  t.bet = 0x05d1;\n  t.beta = 0x03b2;\n  t.betasymbolgreek = 0x03d0;\n  t.betdagesh = 0xfb31;\n  t.betdageshhebrew = 0xfb31;\n  t.bethebrew = 0x05d1;\n  t.betrafehebrew = 0xfb4c;\n  t.bhabengali = 0x09ad;\n  t.bhadeva = 0x092d;\n  t.bhagujarati = 0x0aad;\n  t.bhagurmukhi = 0x0a2d;\n  t.bhook = 0x0253;\n  t.bihiragana = 0x3073;\n  t.bikatakana = 0x30d3;\n  t.bilabialclick = 0x0298;\n  t.bindigurmukhi = 0x0a02;\n  t.birusquare = 0x3331;\n  t.blackcircle = 0x25cf;\n  t.blackdiamond = 0x25c6;\n  t.blackdownpointingtriangle = 0x25bc;\n  t.blackleftpointingpointer = 0x25c4;\n  t.blackleftpointingtriangle = 0x25c0;\n  t.blacklenticularbracketleft = 0x3010;\n  t.blacklenticularbracketleftvertical = 0xfe3b;\n  t.blacklenticularbracketright = 0x3011;\n  t.blacklenticularbracketrightvertical = 0xfe3c;\n  t.blacklowerlefttriangle = 0x25e3;\n  t.blacklowerrighttriangle = 0x25e2;\n  t.blackrectangle = 0x25ac;\n  t.blackrightpointingpointer = 0x25ba;\n  t.blackrightpointingtriangle = 0x25b6;\n  t.blacksmallsquare = 0x25aa;\n  t.blacksmilingface = 0x263b;\n  t.blacksquare = 0x25a0;\n  t.blackstar = 0x2605;\n  t.blackupperlefttriangle = 0x25e4;\n  t.blackupperrighttriangle = 0x25e5;\n  t.blackuppointingsmalltriangle = 0x25b4;\n  t.blackuppointingtriangle = 0x25b2;\n  t.blank = 0x2423;\n  t.blinebelow = 0x1e07;\n  t.block = 0x2588;\n  t.bmonospace = 0xff42;\n  t.bobaimaithai = 0x0e1a;\n  t.bohiragana = 0x307c;\n  t.bokatakana = 0x30dc;\n  t.bparen = 0x249d;\n  t.bqsquare = 0x33c3;\n  t.braceex = 0xf8f4;\n  t.braceleft = 0x007b;\n  t.braceleftbt = 0xf8f3;\n  t.braceleftmid = 0xf8f2;\n  t.braceleftmonospace = 0xff5b;\n  t.braceleftsmall = 0xfe5b;\n  t.bracelefttp = 0xf8f1;\n  t.braceleftvertical = 0xfe37;\n  t.braceright = 0x007d;\n  t.bracerightbt = 0xf8fe;\n  t.bracerightmid = 0xf8fd;\n  t.bracerightmonospace = 0xff5d;\n  t.bracerightsmall = 0xfe5c;\n  t.bracerighttp = 0xf8fc;\n  t.bracerightvertical = 0xfe38;\n  t.bracketleft = 0x005b;\n  t.bracketleftbt = 0xf8f0;\n  t.bracketleftex = 0xf8ef;\n  t.bracketleftmonospace = 0xff3b;\n  t.bracketlefttp = 0xf8ee;\n  t.bracketright = 0x005d;\n  t.bracketrightbt = 0xf8fb;\n  t.bracketrightex = 0xf8fa;\n  t.bracketrightmonospace = 0xff3d;\n  t.bracketrighttp = 0xf8f9;\n  t.breve = 0x02d8;\n  t.brevebelowcmb = 0x032e;\n  t.brevecmb = 0x0306;\n  t.breveinvertedbelowcmb = 0x032f;\n  t.breveinvertedcmb = 0x0311;\n  t.breveinverteddoublecmb = 0x0361;\n  t.bridgebelowcmb = 0x032a;\n  t.bridgeinvertedbelowcmb = 0x033a;\n  t.brokenbar = 0x00a6;\n  t.bstroke = 0x0180;\n  t.bsuperior = 0xf6ea;\n  t.btopbar = 0x0183;\n  t.buhiragana = 0x3076;\n  t.bukatakana = 0x30d6;\n  t.bullet = 0x2022;\n  t.bulletinverse = 0x25d8;\n  t.bulletoperator = 0x2219;\n  t.bullseye = 0x25ce;\n  t.c = 0x0063;\n  t.caarmenian = 0x056e;\n  t.cabengali = 0x099a;\n  t.cacute = 0x0107;\n  t.cadeva = 0x091a;\n  t.cagujarati = 0x0a9a;\n  t.cagurmukhi = 0x0a1a;\n  t.calsquare = 0x3388;\n  t.candrabindubengali = 0x0981;\n  t.candrabinducmb = 0x0310;\n  t.candrabindudeva = 0x0901;\n  t.candrabindugujarati = 0x0a81;\n  t.capslock = 0x21ea;\n  t.careof = 0x2105;\n  t.caron = 0x02c7;\n  t.caronbelowcmb = 0x032c;\n  t.caroncmb = 0x030c;\n  t.carriagereturn = 0x21b5;\n  t.cbopomofo = 0x3118;\n  t.ccaron = 0x010d;\n  t.ccedilla = 0x00e7;\n  t.ccedillaacute = 0x1e09;\n  t.ccircle = 0x24d2;\n  t.ccircumflex = 0x0109;\n  t.ccurl = 0x0255;\n  t.cdot = 0x010b;\n  t.cdotaccent = 0x010b;\n  t.cdsquare = 0x33c5;\n  t.cedilla = 0x00b8;\n  t.cedillacmb = 0x0327;\n  t.cent = 0x00a2;\n  t.centigrade = 0x2103;\n  t.centinferior = 0xf6df;\n  t.centmonospace = 0xffe0;\n  t.centoldstyle = 0xf7a2;\n  t.centsuperior = 0xf6e0;\n  t.chaarmenian = 0x0579;\n  t.chabengali = 0x099b;\n  t.chadeva = 0x091b;\n  t.chagujarati = 0x0a9b;\n  t.chagurmukhi = 0x0a1b;\n  t.chbopomofo = 0x3114;\n  t.cheabkhasiancyrillic = 0x04bd;\n  t.checkmark = 0x2713;\n  t.checyrillic = 0x0447;\n  t.chedescenderabkhasiancyrillic = 0x04bf;\n  t.chedescendercyrillic = 0x04b7;\n  t.chedieresiscyrillic = 0x04f5;\n  t.cheharmenian = 0x0573;\n  t.chekhakassiancyrillic = 0x04cc;\n  t.cheverticalstrokecyrillic = 0x04b9;\n  t.chi = 0x03c7;\n  t.chieuchacirclekorean = 0x3277;\n  t.chieuchaparenkorean = 0x3217;\n  t.chieuchcirclekorean = 0x3269;\n  t.chieuchkorean = 0x314a;\n  t.chieuchparenkorean = 0x3209;\n  t.chochangthai = 0x0e0a;\n  t.chochanthai = 0x0e08;\n  t.chochingthai = 0x0e09;\n  t.chochoethai = 0x0e0c;\n  t.chook = 0x0188;\n  t.cieucacirclekorean = 0x3276;\n  t.cieucaparenkorean = 0x3216;\n  t.cieuccirclekorean = 0x3268;\n  t.cieuckorean = 0x3148;\n  t.cieucparenkorean = 0x3208;\n  t.cieucuparenkorean = 0x321c;\n  t.circle = 0x25cb;\n  t.circlecopyrt = 0x00a9;\n  t.circlemultiply = 0x2297;\n  t.circleot = 0x2299;\n  t.circleplus = 0x2295;\n  t.circlepostalmark = 0x3036;\n  t.circlewithlefthalfblack = 0x25d0;\n  t.circlewithrighthalfblack = 0x25d1;\n  t.circumflex = 0x02c6;\n  t.circumflexbelowcmb = 0x032d;\n  t.circumflexcmb = 0x0302;\n  t.clear = 0x2327;\n  t.clickalveolar = 0x01c2;\n  t.clickdental = 0x01c0;\n  t.clicklateral = 0x01c1;\n  t.clickretroflex = 0x01c3;\n  t.club = 0x2663;\n  t.clubsuitblack = 0x2663;\n  t.clubsuitwhite = 0x2667;\n  t.cmcubedsquare = 0x33a4;\n  t.cmonospace = 0xff43;\n  t.cmsquaredsquare = 0x33a0;\n  t.coarmenian = 0x0581;\n  t.colon = 0x003a;\n  t.colonmonetary = 0x20a1;\n  t.colonmonospace = 0xff1a;\n  t.colonsign = 0x20a1;\n  t.colonsmall = 0xfe55;\n  t.colontriangularhalfmod = 0x02d1;\n  t.colontriangularmod = 0x02d0;\n  t.comma = 0x002c;\n  t.commaabovecmb = 0x0313;\n  t.commaaboverightcmb = 0x0315;\n  t.commaaccent = 0xf6c3;\n  t.commaarabic = 0x060c;\n  t.commaarmenian = 0x055d;\n  t.commainferior = 0xf6e1;\n  t.commamonospace = 0xff0c;\n  t.commareversedabovecmb = 0x0314;\n  t.commareversedmod = 0x02bd;\n  t.commasmall = 0xfe50;\n  t.commasuperior = 0xf6e2;\n  t.commaturnedabovecmb = 0x0312;\n  t.commaturnedmod = 0x02bb;\n  t.compass = 0x263c;\n  t.congruent = 0x2245;\n  t.contourintegral = 0x222e;\n  t.control = 0x2303;\n  t.controlACK = 0x0006;\n  t.controlBEL = 0x0007;\n  t.controlBS = 0x0008;\n  t.controlCAN = 0x0018;\n  t.controlCR = 0x000d;\n  t.controlDC1 = 0x0011;\n  t.controlDC2 = 0x0012;\n  t.controlDC3 = 0x0013;\n  t.controlDC4 = 0x0014;\n  t.controlDEL = 0x007f;\n  t.controlDLE = 0x0010;\n  t.controlEM = 0x0019;\n  t.controlENQ = 0x0005;\n  t.controlEOT = 0x0004;\n  t.controlESC = 0x001b;\n  t.controlETB = 0x0017;\n  t.controlETX = 0x0003;\n  t.controlFF = 0x000c;\n  t.controlFS = 0x001c;\n  t.controlGS = 0x001d;\n  t.controlHT = 0x0009;\n  t.controlLF = 0x000a;\n  t.controlNAK = 0x0015;\n  t.controlNULL = 0x0000;\n  t.controlRS = 0x001e;\n  t.controlSI = 0x000f;\n  t.controlSO = 0x000e;\n  t.controlSOT = 0x0002;\n  t.controlSTX = 0x0001;\n  t.controlSUB = 0x001a;\n  t.controlSYN = 0x0016;\n  t.controlUS = 0x001f;\n  t.controlVT = 0x000b;\n  t.copyright = 0x00a9;\n  t.copyrightsans = 0xf8e9;\n  t.copyrightserif = 0xf6d9;\n  t.cornerbracketleft = 0x300c;\n  t.cornerbracketlefthalfwidth = 0xff62;\n  t.cornerbracketleftvertical = 0xfe41;\n  t.cornerbracketright = 0x300d;\n  t.cornerbracketrighthalfwidth = 0xff63;\n  t.cornerbracketrightvertical = 0xfe42;\n  t.corporationsquare = 0x337f;\n  t.cosquare = 0x33c7;\n  t.coverkgsquare = 0x33c6;\n  t.cparen = 0x249e;\n  t.cruzeiro = 0x20a2;\n  t.cstretched = 0x0297;\n  t.curlyand = 0x22cf;\n  t.curlyor = 0x22ce;\n  t.currency = 0x00a4;\n  t.cyrBreve = 0xf6d1;\n  t.cyrFlex = 0xf6d2;\n  t.cyrbreve = 0xf6d4;\n  t.cyrflex = 0xf6d5;\n  t.d = 0x0064;\n  t.daarmenian = 0x0564;\n  t.dabengali = 0x09a6;\n  t.dadarabic = 0x0636;\n  t.dadeva = 0x0926;\n  t.dadfinalarabic = 0xfebe;\n  t.dadinitialarabic = 0xfebf;\n  t.dadmedialarabic = 0xfec0;\n  t.dagesh = 0x05bc;\n  t.dageshhebrew = 0x05bc;\n  t.dagger = 0x2020;\n  t.daggerdbl = 0x2021;\n  t.dagujarati = 0x0aa6;\n  t.dagurmukhi = 0x0a26;\n  t.dahiragana = 0x3060;\n  t.dakatakana = 0x30c0;\n  t.dalarabic = 0x062f;\n  t.dalet = 0x05d3;\n  t.daletdagesh = 0xfb33;\n  t.daletdageshhebrew = 0xfb33;\n  t.dalethebrew = 0x05d3;\n  t.dalfinalarabic = 0xfeaa;\n  t.dammaarabic = 0x064f;\n  t.dammalowarabic = 0x064f;\n  t.dammatanaltonearabic = 0x064c;\n  t.dammatanarabic = 0x064c;\n  t.danda = 0x0964;\n  t.dargahebrew = 0x05a7;\n  t.dargalefthebrew = 0x05a7;\n  t.dasiapneumatacyrilliccmb = 0x0485;\n  t.dblGrave = 0xf6d3;\n  t.dblanglebracketleft = 0x300a;\n  t.dblanglebracketleftvertical = 0xfe3d;\n  t.dblanglebracketright = 0x300b;\n  t.dblanglebracketrightvertical = 0xfe3e;\n  t.dblarchinvertedbelowcmb = 0x032b;\n  t.dblarrowleft = 0x21d4;\n  t.dblarrowright = 0x21d2;\n  t.dbldanda = 0x0965;\n  t.dblgrave = 0xf6d6;\n  t.dblgravecmb = 0x030f;\n  t.dblintegral = 0x222c;\n  t.dbllowline = 0x2017;\n  t.dbllowlinecmb = 0x0333;\n  t.dbloverlinecmb = 0x033f;\n  t.dblprimemod = 0x02ba;\n  t.dblverticalbar = 0x2016;\n  t.dblverticallineabovecmb = 0x030e;\n  t.dbopomofo = 0x3109;\n  t.dbsquare = 0x33c8;\n  t.dcaron = 0x010f;\n  t.dcedilla = 0x1e11;\n  t.dcircle = 0x24d3;\n  t.dcircumflexbelow = 0x1e13;\n  t.dcroat = 0x0111;\n  t.ddabengali = 0x09a1;\n  t.ddadeva = 0x0921;\n  t.ddagujarati = 0x0aa1;\n  t.ddagurmukhi = 0x0a21;\n  t.ddalarabic = 0x0688;\n  t.ddalfinalarabic = 0xfb89;\n  t.dddhadeva = 0x095c;\n  t.ddhabengali = 0x09a2;\n  t.ddhadeva = 0x0922;\n  t.ddhagujarati = 0x0aa2;\n  t.ddhagurmukhi = 0x0a22;\n  t.ddotaccent = 0x1e0b;\n  t.ddotbelow = 0x1e0d;\n  t.decimalseparatorarabic = 0x066b;\n  t.decimalseparatorpersian = 0x066b;\n  t.decyrillic = 0x0434;\n  t.degree = 0x00b0;\n  t.dehihebrew = 0x05ad;\n  t.dehiragana = 0x3067;\n  t.deicoptic = 0x03ef;\n  t.dekatakana = 0x30c7;\n  t.deleteleft = 0x232b;\n  t.deleteright = 0x2326;\n  t.delta = 0x03b4;\n  t.deltaturned = 0x018d;\n  t.denominatorminusonenumeratorbengali = 0x09f8;\n  t.dezh = 0x02a4;\n  t.dhabengali = 0x09a7;\n  t.dhadeva = 0x0927;\n  t.dhagujarati = 0x0aa7;\n  t.dhagurmukhi = 0x0a27;\n  t.dhook = 0x0257;\n  t.dialytikatonos = 0x0385;\n  t.dialytikatonoscmb = 0x0344;\n  t.diamond = 0x2666;\n  t.diamondsuitwhite = 0x2662;\n  t.dieresis = 0x00a8;\n  t.dieresisacute = 0xf6d7;\n  t.dieresisbelowcmb = 0x0324;\n  t.dieresiscmb = 0x0308;\n  t.dieresisgrave = 0xf6d8;\n  t.dieresistonos = 0x0385;\n  t.dihiragana = 0x3062;\n  t.dikatakana = 0x30c2;\n  t.dittomark = 0x3003;\n  t.divide = 0x00f7;\n  t.divides = 0x2223;\n  t.divisionslash = 0x2215;\n  t.djecyrillic = 0x0452;\n  t.dkshade = 0x2593;\n  t.dlinebelow = 0x1e0f;\n  t.dlsquare = 0x3397;\n  t.dmacron = 0x0111;\n  t.dmonospace = 0xff44;\n  t.dnblock = 0x2584;\n  t.dochadathai = 0x0e0e;\n  t.dodekthai = 0x0e14;\n  t.dohiragana = 0x3069;\n  t.dokatakana = 0x30c9;\n  t.dollar = 0x0024;\n  t.dollarinferior = 0xf6e3;\n  t.dollarmonospace = 0xff04;\n  t.dollaroldstyle = 0xf724;\n  t.dollarsmall = 0xfe69;\n  t.dollarsuperior = 0xf6e4;\n  t.dong = 0x20ab;\n  t.dorusquare = 0x3326;\n  t.dotaccent = 0x02d9;\n  t.dotaccentcmb = 0x0307;\n  t.dotbelowcmb = 0x0323;\n  t.dotbelowcomb = 0x0323;\n  t.dotkatakana = 0x30fb;\n  t.dotlessi = 0x0131;\n  t.dotlessj = 0xf6be;\n  t.dotlessjstrokehook = 0x0284;\n  t.dotmath = 0x22c5;\n  t.dottedcircle = 0x25cc;\n  t.doubleyodpatah = 0xfb1f;\n  t.doubleyodpatahhebrew = 0xfb1f;\n  t.downtackbelowcmb = 0x031e;\n  t.downtackmod = 0x02d5;\n  t.dparen = 0x249f;\n  t.dsuperior = 0xf6eb;\n  t.dtail = 0x0256;\n  t.dtopbar = 0x018c;\n  t.duhiragana = 0x3065;\n  t.dukatakana = 0x30c5;\n  t.dz = 0x01f3;\n  t.dzaltone = 0x02a3;\n  t.dzcaron = 0x01c6;\n  t.dzcurl = 0x02a5;\n  t.dzeabkhasiancyrillic = 0x04e1;\n  t.dzecyrillic = 0x0455;\n  t.dzhecyrillic = 0x045f;\n  t.e = 0x0065;\n  t.eacute = 0x00e9;\n  t.earth = 0x2641;\n  t.ebengali = 0x098f;\n  t.ebopomofo = 0x311c;\n  t.ebreve = 0x0115;\n  t.ecandradeva = 0x090d;\n  t.ecandragujarati = 0x0a8d;\n  t.ecandravowelsigndeva = 0x0945;\n  t.ecandravowelsigngujarati = 0x0ac5;\n  t.ecaron = 0x011b;\n  t.ecedillabreve = 0x1e1d;\n  t.echarmenian = 0x0565;\n  t.echyiwnarmenian = 0x0587;\n  t.ecircle = 0x24d4;\n  t.ecircumflex = 0x00ea;\n  t.ecircumflexacute = 0x1ebf;\n  t.ecircumflexbelow = 0x1e19;\n  t.ecircumflexdotbelow = 0x1ec7;\n  t.ecircumflexgrave = 0x1ec1;\n  t.ecircumflexhookabove = 0x1ec3;\n  t.ecircumflextilde = 0x1ec5;\n  t.ecyrillic = 0x0454;\n  t.edblgrave = 0x0205;\n  t.edeva = 0x090f;\n  t.edieresis = 0x00eb;\n  t.edot = 0x0117;\n  t.edotaccent = 0x0117;\n  t.edotbelow = 0x1eb9;\n  t.eegurmukhi = 0x0a0f;\n  t.eematragurmukhi = 0x0a47;\n  t.efcyrillic = 0x0444;\n  t.egrave = 0x00e8;\n  t.egujarati = 0x0a8f;\n  t.eharmenian = 0x0567;\n  t.ehbopomofo = 0x311d;\n  t.ehiragana = 0x3048;\n  t.ehookabove = 0x1ebb;\n  t.eibopomofo = 0x311f;\n  t.eight = 0x0038;\n  t.eightarabic = 0x0668;\n  t.eightbengali = 0x09ee;\n  t.eightcircle = 0x2467;\n  t.eightcircleinversesansserif = 0x2791;\n  t.eightdeva = 0x096e;\n  t.eighteencircle = 0x2471;\n  t.eighteenparen = 0x2485;\n  t.eighteenperiod = 0x2499;\n  t.eightgujarati = 0x0aee;\n  t.eightgurmukhi = 0x0a6e;\n  t.eighthackarabic = 0x0668;\n  t.eighthangzhou = 0x3028;\n  t.eighthnotebeamed = 0x266b;\n  t.eightideographicparen = 0x3227;\n  t.eightinferior = 0x2088;\n  t.eightmonospace = 0xff18;\n  t.eightoldstyle = 0xf738;\n  t.eightparen = 0x247b;\n  t.eightperiod = 0x248f;\n  t.eightpersian = 0x06f8;\n  t.eightroman = 0x2177;\n  t.eightsuperior = 0x2078;\n  t.eightthai = 0x0e58;\n  t.einvertedbreve = 0x0207;\n  t.eiotifiedcyrillic = 0x0465;\n  t.ekatakana = 0x30a8;\n  t.ekatakanahalfwidth = 0xff74;\n  t.ekonkargurmukhi = 0x0a74;\n  t.ekorean = 0x3154;\n  t.elcyrillic = 0x043b;\n  t.element = 0x2208;\n  t.elevencircle = 0x246a;\n  t.elevenparen = 0x247e;\n  t.elevenperiod = 0x2492;\n  t.elevenroman = 0x217a;\n  t.ellipsis = 0x2026;\n  t.ellipsisvertical = 0x22ee;\n  t.emacron = 0x0113;\n  t.emacronacute = 0x1e17;\n  t.emacrongrave = 0x1e15;\n  t.emcyrillic = 0x043c;\n  t.emdash = 0x2014;\n  t.emdashvertical = 0xfe31;\n  t.emonospace = 0xff45;\n  t.emphasismarkarmenian = 0x055b;\n  t.emptyset = 0x2205;\n  t.enbopomofo = 0x3123;\n  t.encyrillic = 0x043d;\n  t.endash = 0x2013;\n  t.endashvertical = 0xfe32;\n  t.endescendercyrillic = 0x04a3;\n  t.eng = 0x014b;\n  t.engbopomofo = 0x3125;\n  t.enghecyrillic = 0x04a5;\n  t.enhookcyrillic = 0x04c8;\n  t.enspace = 0x2002;\n  t.eogonek = 0x0119;\n  t.eokorean = 0x3153;\n  t.eopen = 0x025b;\n  t.eopenclosed = 0x029a;\n  t.eopenreversed = 0x025c;\n  t.eopenreversedclosed = 0x025e;\n  t.eopenreversedhook = 0x025d;\n  t.eparen = 0x24a0;\n  t.epsilon = 0x03b5;\n  t.epsilontonos = 0x03ad;\n  t.equal = 0x003d;\n  t.equalmonospace = 0xff1d;\n  t.equalsmall = 0xfe66;\n  t.equalsuperior = 0x207c;\n  t.equivalence = 0x2261;\n  t.erbopomofo = 0x3126;\n  t.ercyrillic = 0x0440;\n  t.ereversed = 0x0258;\n  t.ereversedcyrillic = 0x044d;\n  t.escyrillic = 0x0441;\n  t.esdescendercyrillic = 0x04ab;\n  t.esh = 0x0283;\n  t.eshcurl = 0x0286;\n  t.eshortdeva = 0x090e;\n  t.eshortvowelsigndeva = 0x0946;\n  t.eshreversedloop = 0x01aa;\n  t.eshsquatreversed = 0x0285;\n  t.esmallhiragana = 0x3047;\n  t.esmallkatakana = 0x30a7;\n  t.esmallkatakanahalfwidth = 0xff6a;\n  t.estimated = 0x212e;\n  t.esuperior = 0xf6ec;\n  t.eta = 0x03b7;\n  t.etarmenian = 0x0568;\n  t.etatonos = 0x03ae;\n  t.eth = 0x00f0;\n  t.etilde = 0x1ebd;\n  t.etildebelow = 0x1e1b;\n  t.etnahtafoukhhebrew = 0x0591;\n  t.etnahtafoukhlefthebrew = 0x0591;\n  t.etnahtahebrew = 0x0591;\n  t.etnahtalefthebrew = 0x0591;\n  t.eturned = 0x01dd;\n  t.eukorean = 0x3161;\n  t.euro = 0x20ac;\n  t.evowelsignbengali = 0x09c7;\n  t.evowelsigndeva = 0x0947;\n  t.evowelsigngujarati = 0x0ac7;\n  t.exclam = 0x0021;\n  t.exclamarmenian = 0x055c;\n  t.exclamdbl = 0x203c;\n  t.exclamdown = 0x00a1;\n  t.exclamdownsmall = 0xf7a1;\n  t.exclammonospace = 0xff01;\n  t.exclamsmall = 0xf721;\n  t.existential = 0x2203;\n  t.ezh = 0x0292;\n  t.ezhcaron = 0x01ef;\n  t.ezhcurl = 0x0293;\n  t.ezhreversed = 0x01b9;\n  t.ezhtail = 0x01ba;\n  t.f = 0x0066;\n  t.fadeva = 0x095e;\n  t.fagurmukhi = 0x0a5e;\n  t.fahrenheit = 0x2109;\n  t.fathaarabic = 0x064e;\n  t.fathalowarabic = 0x064e;\n  t.fathatanarabic = 0x064b;\n  t.fbopomofo = 0x3108;\n  t.fcircle = 0x24d5;\n  t.fdotaccent = 0x1e1f;\n  t.feharabic = 0x0641;\n  t.feharmenian = 0x0586;\n  t.fehfinalarabic = 0xfed2;\n  t.fehinitialarabic = 0xfed3;\n  t.fehmedialarabic = 0xfed4;\n  t.feicoptic = 0x03e5;\n  t.female = 0x2640;\n  t.ff = 0xfb00;\n  t.f_f = 0xfb00;\n  t.ffi = 0xfb03;\n  t.f_f_i = 0xfb03;\n  t.ffl = 0xfb04;\n  t.f_f_l = 0xfb04;\n  t.fi = 0xfb01;\n  t.f_i = 0xfb01;\n  t.fifteencircle = 0x246e;\n  t.fifteenparen = 0x2482;\n  t.fifteenperiod = 0x2496;\n  t.figuredash = 0x2012;\n  t.filledbox = 0x25a0;\n  t.filledrect = 0x25ac;\n  t.finalkaf = 0x05da;\n  t.finalkafdagesh = 0xfb3a;\n  t.finalkafdageshhebrew = 0xfb3a;\n  t.finalkafhebrew = 0x05da;\n  t.finalmem = 0x05dd;\n  t.finalmemhebrew = 0x05dd;\n  t.finalnun = 0x05df;\n  t.finalnunhebrew = 0x05df;\n  t.finalpe = 0x05e3;\n  t.finalpehebrew = 0x05e3;\n  t.finaltsadi = 0x05e5;\n  t.finaltsadihebrew = 0x05e5;\n  t.firsttonechinese = 0x02c9;\n  t.fisheye = 0x25c9;\n  t.fitacyrillic = 0x0473;\n  t.five = 0x0035;\n  t.fivearabic = 0x0665;\n  t.fivebengali = 0x09eb;\n  t.fivecircle = 0x2464;\n  t.fivecircleinversesansserif = 0x278e;\n  t.fivedeva = 0x096b;\n  t.fiveeighths = 0x215d;\n  t.fivegujarati = 0x0aeb;\n  t.fivegurmukhi = 0x0a6b;\n  t.fivehackarabic = 0x0665;\n  t.fivehangzhou = 0x3025;\n  t.fiveideographicparen = 0x3224;\n  t.fiveinferior = 0x2085;\n  t.fivemonospace = 0xff15;\n  t.fiveoldstyle = 0xf735;\n  t.fiveparen = 0x2478;\n  t.fiveperiod = 0x248c;\n  t.fivepersian = 0x06f5;\n  t.fiveroman = 0x2174;\n  t.fivesuperior = 0x2075;\n  t.fivethai = 0x0e55;\n  t.fl = 0xfb02;\n  t.f_l = 0xfb02;\n  t.florin = 0x0192;\n  t.fmonospace = 0xff46;\n  t.fmsquare = 0x3399;\n  t.fofanthai = 0x0e1f;\n  t.fofathai = 0x0e1d;\n  t.fongmanthai = 0x0e4f;\n  t.forall = 0x2200;\n  t.four = 0x0034;\n  t.fourarabic = 0x0664;\n  t.fourbengali = 0x09ea;\n  t.fourcircle = 0x2463;\n  t.fourcircleinversesansserif = 0x278d;\n  t.fourdeva = 0x096a;\n  t.fourgujarati = 0x0aea;\n  t.fourgurmukhi = 0x0a6a;\n  t.fourhackarabic = 0x0664;\n  t.fourhangzhou = 0x3024;\n  t.fourideographicparen = 0x3223;\n  t.fourinferior = 0x2084;\n  t.fourmonospace = 0xff14;\n  t.fournumeratorbengali = 0x09f7;\n  t.fouroldstyle = 0xf734;\n  t.fourparen = 0x2477;\n  t.fourperiod = 0x248b;\n  t.fourpersian = 0x06f4;\n  t.fourroman = 0x2173;\n  t.foursuperior = 0x2074;\n  t.fourteencircle = 0x246d;\n  t.fourteenparen = 0x2481;\n  t.fourteenperiod = 0x2495;\n  t.fourthai = 0x0e54;\n  t.fourthtonechinese = 0x02cb;\n  t.fparen = 0x24a1;\n  t.fraction = 0x2044;\n  t.franc = 0x20a3;\n  t.g = 0x0067;\n  t.gabengali = 0x0997;\n  t.gacute = 0x01f5;\n  t.gadeva = 0x0917;\n  t.gafarabic = 0x06af;\n  t.gaffinalarabic = 0xfb93;\n  t.gafinitialarabic = 0xfb94;\n  t.gafmedialarabic = 0xfb95;\n  t.gagujarati = 0x0a97;\n  t.gagurmukhi = 0x0a17;\n  t.gahiragana = 0x304c;\n  t.gakatakana = 0x30ac;\n  t.gamma = 0x03b3;\n  t.gammalatinsmall = 0x0263;\n  t.gammasuperior = 0x02e0;\n  t.gangiacoptic = 0x03eb;\n  t.gbopomofo = 0x310d;\n  t.gbreve = 0x011f;\n  t.gcaron = 0x01e7;\n  t.gcedilla = 0x0123;\n  t.gcircle = 0x24d6;\n  t.gcircumflex = 0x011d;\n  t.gcommaaccent = 0x0123;\n  t.gdot = 0x0121;\n  t.gdotaccent = 0x0121;\n  t.gecyrillic = 0x0433;\n  t.gehiragana = 0x3052;\n  t.gekatakana = 0x30b2;\n  t.geometricallyequal = 0x2251;\n  t.gereshaccenthebrew = 0x059c;\n  t.gereshhebrew = 0x05f3;\n  t.gereshmuqdamhebrew = 0x059d;\n  t.germandbls = 0x00df;\n  t.gershayimaccenthebrew = 0x059e;\n  t.gershayimhebrew = 0x05f4;\n  t.getamark = 0x3013;\n  t.ghabengali = 0x0998;\n  t.ghadarmenian = 0x0572;\n  t.ghadeva = 0x0918;\n  t.ghagujarati = 0x0a98;\n  t.ghagurmukhi = 0x0a18;\n  t.ghainarabic = 0x063a;\n  t.ghainfinalarabic = 0xfece;\n  t.ghaininitialarabic = 0xfecf;\n  t.ghainmedialarabic = 0xfed0;\n  t.ghemiddlehookcyrillic = 0x0495;\n  t.ghestrokecyrillic = 0x0493;\n  t.gheupturncyrillic = 0x0491;\n  t.ghhadeva = 0x095a;\n  t.ghhagurmukhi = 0x0a5a;\n  t.ghook = 0x0260;\n  t.ghzsquare = 0x3393;\n  t.gihiragana = 0x304e;\n  t.gikatakana = 0x30ae;\n  t.gimarmenian = 0x0563;\n  t.gimel = 0x05d2;\n  t.gimeldagesh = 0xfb32;\n  t.gimeldageshhebrew = 0xfb32;\n  t.gimelhebrew = 0x05d2;\n  t.gjecyrillic = 0x0453;\n  t.glottalinvertedstroke = 0x01be;\n  t.glottalstop = 0x0294;\n  t.glottalstopinverted = 0x0296;\n  t.glottalstopmod = 0x02c0;\n  t.glottalstopreversed = 0x0295;\n  t.glottalstopreversedmod = 0x02c1;\n  t.glottalstopreversedsuperior = 0x02e4;\n  t.glottalstopstroke = 0x02a1;\n  t.glottalstopstrokereversed = 0x02a2;\n  t.gmacron = 0x1e21;\n  t.gmonospace = 0xff47;\n  t.gohiragana = 0x3054;\n  t.gokatakana = 0x30b4;\n  t.gparen = 0x24a2;\n  t.gpasquare = 0x33ac;\n  t.gradient = 0x2207;\n  t.grave = 0x0060;\n  t.gravebelowcmb = 0x0316;\n  t.gravecmb = 0x0300;\n  t.gravecomb = 0x0300;\n  t.gravedeva = 0x0953;\n  t.gravelowmod = 0x02ce;\n  t.gravemonospace = 0xff40;\n  t.gravetonecmb = 0x0340;\n  t.greater = 0x003e;\n  t.greaterequal = 0x2265;\n  t.greaterequalorless = 0x22db;\n  t.greatermonospace = 0xff1e;\n  t.greaterorequivalent = 0x2273;\n  t.greaterorless = 0x2277;\n  t.greateroverequal = 0x2267;\n  t.greatersmall = 0xfe65;\n  t.gscript = 0x0261;\n  t.gstroke = 0x01e5;\n  t.guhiragana = 0x3050;\n  t.guillemotleft = 0x00ab;\n  t.guillemotright = 0x00bb;\n  t.guilsinglleft = 0x2039;\n  t.guilsinglright = 0x203a;\n  t.gukatakana = 0x30b0;\n  t.guramusquare = 0x3318;\n  t.gysquare = 0x33c9;\n  t.h = 0x0068;\n  t.haabkhasiancyrillic = 0x04a9;\n  t.haaltonearabic = 0x06c1;\n  t.habengali = 0x09b9;\n  t.hadescendercyrillic = 0x04b3;\n  t.hadeva = 0x0939;\n  t.hagujarati = 0x0ab9;\n  t.hagurmukhi = 0x0a39;\n  t.haharabic = 0x062d;\n  t.hahfinalarabic = 0xfea2;\n  t.hahinitialarabic = 0xfea3;\n  t.hahiragana = 0x306f;\n  t.hahmedialarabic = 0xfea4;\n  t.haitusquare = 0x332a;\n  t.hakatakana = 0x30cf;\n  t.hakatakanahalfwidth = 0xff8a;\n  t.halantgurmukhi = 0x0a4d;\n  t.hamzaarabic = 0x0621;\n  t.hamzalowarabic = 0x0621;\n  t.hangulfiller = 0x3164;\n  t.hardsigncyrillic = 0x044a;\n  t.harpoonleftbarbup = 0x21bc;\n  t.harpoonrightbarbup = 0x21c0;\n  t.hasquare = 0x33ca;\n  t.hatafpatah = 0x05b2;\n  t.hatafpatah16 = 0x05b2;\n  t.hatafpatah23 = 0x05b2;\n  t.hatafpatah2f = 0x05b2;\n  t.hatafpatahhebrew = 0x05b2;\n  t.hatafpatahnarrowhebrew = 0x05b2;\n  t.hatafpatahquarterhebrew = 0x05b2;\n  t.hatafpatahwidehebrew = 0x05b2;\n  t.hatafqamats = 0x05b3;\n  t.hatafqamats1b = 0x05b3;\n  t.hatafqamats28 = 0x05b3;\n  t.hatafqamats34 = 0x05b3;\n  t.hatafqamatshebrew = 0x05b3;\n  t.hatafqamatsnarrowhebrew = 0x05b3;\n  t.hatafqamatsquarterhebrew = 0x05b3;\n  t.hatafqamatswidehebrew = 0x05b3;\n  t.hatafsegol = 0x05b1;\n  t.hatafsegol17 = 0x05b1;\n  t.hatafsegol24 = 0x05b1;\n  t.hatafsegol30 = 0x05b1;\n  t.hatafsegolhebrew = 0x05b1;\n  t.hatafsegolnarrowhebrew = 0x05b1;\n  t.hatafsegolquarterhebrew = 0x05b1;\n  t.hatafsegolwidehebrew = 0x05b1;\n  t.hbar = 0x0127;\n  t.hbopomofo = 0x310f;\n  t.hbrevebelow = 0x1e2b;\n  t.hcedilla = 0x1e29;\n  t.hcircle = 0x24d7;\n  t.hcircumflex = 0x0125;\n  t.hdieresis = 0x1e27;\n  t.hdotaccent = 0x1e23;\n  t.hdotbelow = 0x1e25;\n  t.he = 0x05d4;\n  t.heart = 0x2665;\n  t.heartsuitblack = 0x2665;\n  t.heartsuitwhite = 0x2661;\n  t.hedagesh = 0xfb34;\n  t.hedageshhebrew = 0xfb34;\n  t.hehaltonearabic = 0x06c1;\n  t.heharabic = 0x0647;\n  t.hehebrew = 0x05d4;\n  t.hehfinalaltonearabic = 0xfba7;\n  t.hehfinalalttwoarabic = 0xfeea;\n  t.hehfinalarabic = 0xfeea;\n  t.hehhamzaabovefinalarabic = 0xfba5;\n  t.hehhamzaaboveisolatedarabic = 0xfba4;\n  t.hehinitialaltonearabic = 0xfba8;\n  t.hehinitialarabic = 0xfeeb;\n  t.hehiragana = 0x3078;\n  t.hehmedialaltonearabic = 0xfba9;\n  t.hehmedialarabic = 0xfeec;\n  t.heiseierasquare = 0x337b;\n  t.hekatakana = 0x30d8;\n  t.hekatakanahalfwidth = 0xff8d;\n  t.hekutaarusquare = 0x3336;\n  t.henghook = 0x0267;\n  t.herutusquare = 0x3339;\n  t.het = 0x05d7;\n  t.hethebrew = 0x05d7;\n  t.hhook = 0x0266;\n  t.hhooksuperior = 0x02b1;\n  t.hieuhacirclekorean = 0x327b;\n  t.hieuhaparenkorean = 0x321b;\n  t.hieuhcirclekorean = 0x326d;\n  t.hieuhkorean = 0x314e;\n  t.hieuhparenkorean = 0x320d;\n  t.hihiragana = 0x3072;\n  t.hikatakana = 0x30d2;\n  t.hikatakanahalfwidth = 0xff8b;\n  t.hiriq = 0x05b4;\n  t.hiriq14 = 0x05b4;\n  t.hiriq21 = 0x05b4;\n  t.hiriq2d = 0x05b4;\n  t.hiriqhebrew = 0x05b4;\n  t.hiriqnarrowhebrew = 0x05b4;\n  t.hiriqquarterhebrew = 0x05b4;\n  t.hiriqwidehebrew = 0x05b4;\n  t.hlinebelow = 0x1e96;\n  t.hmonospace = 0xff48;\n  t.hoarmenian = 0x0570;\n  t.hohipthai = 0x0e2b;\n  t.hohiragana = 0x307b;\n  t.hokatakana = 0x30db;\n  t.hokatakanahalfwidth = 0xff8e;\n  t.holam = 0x05b9;\n  t.holam19 = 0x05b9;\n  t.holam26 = 0x05b9;\n  t.holam32 = 0x05b9;\n  t.holamhebrew = 0x05b9;\n  t.holamnarrowhebrew = 0x05b9;\n  t.holamquarterhebrew = 0x05b9;\n  t.holamwidehebrew = 0x05b9;\n  t.honokhukthai = 0x0e2e;\n  t.hookabovecomb = 0x0309;\n  t.hookcmb = 0x0309;\n  t.hookpalatalizedbelowcmb = 0x0321;\n  t.hookretroflexbelowcmb = 0x0322;\n  t.hoonsquare = 0x3342;\n  t.horicoptic = 0x03e9;\n  t.horizontalbar = 0x2015;\n  t.horncmb = 0x031b;\n  t.hotsprings = 0x2668;\n  t.house = 0x2302;\n  t.hparen = 0x24a3;\n  t.hsuperior = 0x02b0;\n  t.hturned = 0x0265;\n  t.huhiragana = 0x3075;\n  t.huiitosquare = 0x3333;\n  t.hukatakana = 0x30d5;\n  t.hukatakanahalfwidth = 0xff8c;\n  t.hungarumlaut = 0x02dd;\n  t.hungarumlautcmb = 0x030b;\n  t.hv = 0x0195;\n  t.hyphen = 0x002d;\n  t.hypheninferior = 0xf6e5;\n  t.hyphenmonospace = 0xff0d;\n  t.hyphensmall = 0xfe63;\n  t.hyphensuperior = 0xf6e6;\n  t.hyphentwo = 0x2010;\n  t.i = 0x0069;\n  t.iacute = 0x00ed;\n  t.iacyrillic = 0x044f;\n  t.ibengali = 0x0987;\n  t.ibopomofo = 0x3127;\n  t.ibreve = 0x012d;\n  t.icaron = 0x01d0;\n  t.icircle = 0x24d8;\n  t.icircumflex = 0x00ee;\n  t.icyrillic = 0x0456;\n  t.idblgrave = 0x0209;\n  t.ideographearthcircle = 0x328f;\n  t.ideographfirecircle = 0x328b;\n  t.ideographicallianceparen = 0x323f;\n  t.ideographiccallparen = 0x323a;\n  t.ideographiccentrecircle = 0x32a5;\n  t.ideographicclose = 0x3006;\n  t.ideographiccomma = 0x3001;\n  t.ideographiccommaleft = 0xff64;\n  t.ideographiccongratulationparen = 0x3237;\n  t.ideographiccorrectcircle = 0x32a3;\n  t.ideographicearthparen = 0x322f;\n  t.ideographicenterpriseparen = 0x323d;\n  t.ideographicexcellentcircle = 0x329d;\n  t.ideographicfestivalparen = 0x3240;\n  t.ideographicfinancialcircle = 0x3296;\n  t.ideographicfinancialparen = 0x3236;\n  t.ideographicfireparen = 0x322b;\n  t.ideographichaveparen = 0x3232;\n  t.ideographichighcircle = 0x32a4;\n  t.ideographiciterationmark = 0x3005;\n  t.ideographiclaborcircle = 0x3298;\n  t.ideographiclaborparen = 0x3238;\n  t.ideographicleftcircle = 0x32a7;\n  t.ideographiclowcircle = 0x32a6;\n  t.ideographicmedicinecircle = 0x32a9;\n  t.ideographicmetalparen = 0x322e;\n  t.ideographicmoonparen = 0x322a;\n  t.ideographicnameparen = 0x3234;\n  t.ideographicperiod = 0x3002;\n  t.ideographicprintcircle = 0x329e;\n  t.ideographicreachparen = 0x3243;\n  t.ideographicrepresentparen = 0x3239;\n  t.ideographicresourceparen = 0x323e;\n  t.ideographicrightcircle = 0x32a8;\n  t.ideographicsecretcircle = 0x3299;\n  t.ideographicselfparen = 0x3242;\n  t.ideographicsocietyparen = 0x3233;\n  t.ideographicspace = 0x3000;\n  t.ideographicspecialparen = 0x3235;\n  t.ideographicstockparen = 0x3231;\n  t.ideographicstudyparen = 0x323b;\n  t.ideographicsunparen = 0x3230;\n  t.ideographicsuperviseparen = 0x323c;\n  t.ideographicwaterparen = 0x322c;\n  t.ideographicwoodparen = 0x322d;\n  t.ideographiczero = 0x3007;\n  t.ideographmetalcircle = 0x328e;\n  t.ideographmooncircle = 0x328a;\n  t.ideographnamecircle = 0x3294;\n  t.ideographsuncircle = 0x3290;\n  t.ideographwatercircle = 0x328c;\n  t.ideographwoodcircle = 0x328d;\n  t.ideva = 0x0907;\n  t.idieresis = 0x00ef;\n  t.idieresisacute = 0x1e2f;\n  t.idieresiscyrillic = 0x04e5;\n  t.idotbelow = 0x1ecb;\n  t.iebrevecyrillic = 0x04d7;\n  t.iecyrillic = 0x0435;\n  t.ieungacirclekorean = 0x3275;\n  t.ieungaparenkorean = 0x3215;\n  t.ieungcirclekorean = 0x3267;\n  t.ieungkorean = 0x3147;\n  t.ieungparenkorean = 0x3207;\n  t.igrave = 0x00ec;\n  t.igujarati = 0x0a87;\n  t.igurmukhi = 0x0a07;\n  t.ihiragana = 0x3044;\n  t.ihookabove = 0x1ec9;\n  t.iibengali = 0x0988;\n  t.iicyrillic = 0x0438;\n  t.iideva = 0x0908;\n  t.iigujarati = 0x0a88;\n  t.iigurmukhi = 0x0a08;\n  t.iimatragurmukhi = 0x0a40;\n  t.iinvertedbreve = 0x020b;\n  t.iishortcyrillic = 0x0439;\n  t.iivowelsignbengali = 0x09c0;\n  t.iivowelsigndeva = 0x0940;\n  t.iivowelsigngujarati = 0x0ac0;\n  t.ij = 0x0133;\n  t.ikatakana = 0x30a4;\n  t.ikatakanahalfwidth = 0xff72;\n  t.ikorean = 0x3163;\n  t.ilde = 0x02dc;\n  t.iluyhebrew = 0x05ac;\n  t.imacron = 0x012b;\n  t.imacroncyrillic = 0x04e3;\n  t.imageorapproximatelyequal = 0x2253;\n  t.imatragurmukhi = 0x0a3f;\n  t.imonospace = 0xff49;\n  t.increment = 0x2206;\n  t.infinity = 0x221e;\n  t.iniarmenian = 0x056b;\n  t.integral = 0x222b;\n  t.integralbottom = 0x2321;\n  t.integralbt = 0x2321;\n  t.integralex = 0xf8f5;\n  t.integraltop = 0x2320;\n  t.integraltp = 0x2320;\n  t.intersection = 0x2229;\n  t.intisquare = 0x3305;\n  t.invbullet = 0x25d8;\n  t.invcircle = 0x25d9;\n  t.invsmileface = 0x263b;\n  t.iocyrillic = 0x0451;\n  t.iogonek = 0x012f;\n  t.iota = 0x03b9;\n  t.iotadieresis = 0x03ca;\n  t.iotadieresistonos = 0x0390;\n  t.iotalatin = 0x0269;\n  t.iotatonos = 0x03af;\n  t.iparen = 0x24a4;\n  t.irigurmukhi = 0x0a72;\n  t.ismallhiragana = 0x3043;\n  t.ismallkatakana = 0x30a3;\n  t.ismallkatakanahalfwidth = 0xff68;\n  t.issharbengali = 0x09fa;\n  t.istroke = 0x0268;\n  t.isuperior = 0xf6ed;\n  t.iterationhiragana = 0x309d;\n  t.iterationkatakana = 0x30fd;\n  t.itilde = 0x0129;\n  t.itildebelow = 0x1e2d;\n  t.iubopomofo = 0x3129;\n  t.iucyrillic = 0x044e;\n  t.ivowelsignbengali = 0x09bf;\n  t.ivowelsigndeva = 0x093f;\n  t.ivowelsigngujarati = 0x0abf;\n  t.izhitsacyrillic = 0x0475;\n  t.izhitsadblgravecyrillic = 0x0477;\n  t.j = 0x006a;\n  t.jaarmenian = 0x0571;\n  t.jabengali = 0x099c;\n  t.jadeva = 0x091c;\n  t.jagujarati = 0x0a9c;\n  t.jagurmukhi = 0x0a1c;\n  t.jbopomofo = 0x3110;\n  t.jcaron = 0x01f0;\n  t.jcircle = 0x24d9;\n  t.jcircumflex = 0x0135;\n  t.jcrossedtail = 0x029d;\n  t.jdotlessstroke = 0x025f;\n  t.jecyrillic = 0x0458;\n  t.jeemarabic = 0x062c;\n  t.jeemfinalarabic = 0xfe9e;\n  t.jeeminitialarabic = 0xfe9f;\n  t.jeemmedialarabic = 0xfea0;\n  t.jeharabic = 0x0698;\n  t.jehfinalarabic = 0xfb8b;\n  t.jhabengali = 0x099d;\n  t.jhadeva = 0x091d;\n  t.jhagujarati = 0x0a9d;\n  t.jhagurmukhi = 0x0a1d;\n  t.jheharmenian = 0x057b;\n  t.jis = 0x3004;\n  t.jmonospace = 0xff4a;\n  t.jparen = 0x24a5;\n  t.jsuperior = 0x02b2;\n  t.k = 0x006b;\n  t.kabashkircyrillic = 0x04a1;\n  t.kabengali = 0x0995;\n  t.kacute = 0x1e31;\n  t.kacyrillic = 0x043a;\n  t.kadescendercyrillic = 0x049b;\n  t.kadeva = 0x0915;\n  t.kaf = 0x05db;\n  t.kafarabic = 0x0643;\n  t.kafdagesh = 0xfb3b;\n  t.kafdageshhebrew = 0xfb3b;\n  t.kaffinalarabic = 0xfeda;\n  t.kafhebrew = 0x05db;\n  t.kafinitialarabic = 0xfedb;\n  t.kafmedialarabic = 0xfedc;\n  t.kafrafehebrew = 0xfb4d;\n  t.kagujarati = 0x0a95;\n  t.kagurmukhi = 0x0a15;\n  t.kahiragana = 0x304b;\n  t.kahookcyrillic = 0x04c4;\n  t.kakatakana = 0x30ab;\n  t.kakatakanahalfwidth = 0xff76;\n  t.kappa = 0x03ba;\n  t.kappasymbolgreek = 0x03f0;\n  t.kapyeounmieumkorean = 0x3171;\n  t.kapyeounphieuphkorean = 0x3184;\n  t.kapyeounpieupkorean = 0x3178;\n  t.kapyeounssangpieupkorean = 0x3179;\n  t.karoriisquare = 0x330d;\n  t.kashidaautoarabic = 0x0640;\n  t.kashidaautonosidebearingarabic = 0x0640;\n  t.kasmallkatakana = 0x30f5;\n  t.kasquare = 0x3384;\n  t.kasraarabic = 0x0650;\n  t.kasratanarabic = 0x064d;\n  t.kastrokecyrillic = 0x049f;\n  t.katahiraprolongmarkhalfwidth = 0xff70;\n  t.kaverticalstrokecyrillic = 0x049d;\n  t.kbopomofo = 0x310e;\n  t.kcalsquare = 0x3389;\n  t.kcaron = 0x01e9;\n  t.kcedilla = 0x0137;\n  t.kcircle = 0x24da;\n  t.kcommaaccent = 0x0137;\n  t.kdotbelow = 0x1e33;\n  t.keharmenian = 0x0584;\n  t.kehiragana = 0x3051;\n  t.kekatakana = 0x30b1;\n  t.kekatakanahalfwidth = 0xff79;\n  t.kenarmenian = 0x056f;\n  t.kesmallkatakana = 0x30f6;\n  t.kgreenlandic = 0x0138;\n  t.khabengali = 0x0996;\n  t.khacyrillic = 0x0445;\n  t.khadeva = 0x0916;\n  t.khagujarati = 0x0a96;\n  t.khagurmukhi = 0x0a16;\n  t.khaharabic = 0x062e;\n  t.khahfinalarabic = 0xfea6;\n  t.khahinitialarabic = 0xfea7;\n  t.khahmedialarabic = 0xfea8;\n  t.kheicoptic = 0x03e7;\n  t.khhadeva = 0x0959;\n  t.khhagurmukhi = 0x0a59;\n  t.khieukhacirclekorean = 0x3278;\n  t.khieukhaparenkorean = 0x3218;\n  t.khieukhcirclekorean = 0x326a;\n  t.khieukhkorean = 0x314b;\n  t.khieukhparenkorean = 0x320a;\n  t.khokhaithai = 0x0e02;\n  t.khokhonthai = 0x0e05;\n  t.khokhuatthai = 0x0e03;\n  t.khokhwaithai = 0x0e04;\n  t.khomutthai = 0x0e5b;\n  t.khook = 0x0199;\n  t.khorakhangthai = 0x0e06;\n  t.khzsquare = 0x3391;\n  t.kihiragana = 0x304d;\n  t.kikatakana = 0x30ad;\n  t.kikatakanahalfwidth = 0xff77;\n  t.kiroguramusquare = 0x3315;\n  t.kiromeetorusquare = 0x3316;\n  t.kirosquare = 0x3314;\n  t.kiyeokacirclekorean = 0x326e;\n  t.kiyeokaparenkorean = 0x320e;\n  t.kiyeokcirclekorean = 0x3260;\n  t.kiyeokkorean = 0x3131;\n  t.kiyeokparenkorean = 0x3200;\n  t.kiyeoksioskorean = 0x3133;\n  t.kjecyrillic = 0x045c;\n  t.klinebelow = 0x1e35;\n  t.klsquare = 0x3398;\n  t.kmcubedsquare = 0x33a6;\n  t.kmonospace = 0xff4b;\n  t.kmsquaredsquare = 0x33a2;\n  t.kohiragana = 0x3053;\n  t.kohmsquare = 0x33c0;\n  t.kokaithai = 0x0e01;\n  t.kokatakana = 0x30b3;\n  t.kokatakanahalfwidth = 0xff7a;\n  t.kooposquare = 0x331e;\n  t.koppacyrillic = 0x0481;\n  t.koreanstandardsymbol = 0x327f;\n  t.koroniscmb = 0x0343;\n  t.kparen = 0x24a6;\n  t.kpasquare = 0x33aa;\n  t.ksicyrillic = 0x046f;\n  t.ktsquare = 0x33cf;\n  t.kturned = 0x029e;\n  t.kuhiragana = 0x304f;\n  t.kukatakana = 0x30af;\n  t.kukatakanahalfwidth = 0xff78;\n  t.kvsquare = 0x33b8;\n  t.kwsquare = 0x33be;\n  t.l = 0x006c;\n  t.labengali = 0x09b2;\n  t.lacute = 0x013a;\n  t.ladeva = 0x0932;\n  t.lagujarati = 0x0ab2;\n  t.lagurmukhi = 0x0a32;\n  t.lakkhangyaothai = 0x0e45;\n  t.lamaleffinalarabic = 0xfefc;\n  t.lamalefhamzaabovefinalarabic = 0xfef8;\n  t.lamalefhamzaaboveisolatedarabic = 0xfef7;\n  t.lamalefhamzabelowfinalarabic = 0xfefa;\n  t.lamalefhamzabelowisolatedarabic = 0xfef9;\n  t.lamalefisolatedarabic = 0xfefb;\n  t.lamalefmaddaabovefinalarabic = 0xfef6;\n  t.lamalefmaddaaboveisolatedarabic = 0xfef5;\n  t.lamarabic = 0x0644;\n  t.lambda = 0x03bb;\n  t.lambdastroke = 0x019b;\n  t.lamed = 0x05dc;\n  t.lameddagesh = 0xfb3c;\n  t.lameddageshhebrew = 0xfb3c;\n  t.lamedhebrew = 0x05dc;\n  t.lamfinalarabic = 0xfede;\n  t.lamhahinitialarabic = 0xfcca;\n  t.laminitialarabic = 0xfedf;\n  t.lamjeeminitialarabic = 0xfcc9;\n  t.lamkhahinitialarabic = 0xfccb;\n  t.lamlamhehisolatedarabic = 0xfdf2;\n  t.lammedialarabic = 0xfee0;\n  t.lammeemhahinitialarabic = 0xfd88;\n  t.lammeeminitialarabic = 0xfccc;\n  t.largecircle = 0x25ef;\n  t.lbar = 0x019a;\n  t.lbelt = 0x026c;\n  t.lbopomofo = 0x310c;\n  t.lcaron = 0x013e;\n  t.lcedilla = 0x013c;\n  t.lcircle = 0x24db;\n  t.lcircumflexbelow = 0x1e3d;\n  t.lcommaaccent = 0x013c;\n  t.ldot = 0x0140;\n  t.ldotaccent = 0x0140;\n  t.ldotbelow = 0x1e37;\n  t.ldotbelowmacron = 0x1e39;\n  t.leftangleabovecmb = 0x031a;\n  t.lefttackbelowcmb = 0x0318;\n  t.less = 0x003c;\n  t.lessequal = 0x2264;\n  t.lessequalorgreater = 0x22da;\n  t.lessmonospace = 0xff1c;\n  t.lessorequivalent = 0x2272;\n  t.lessorgreater = 0x2276;\n  t.lessoverequal = 0x2266;\n  t.lesssmall = 0xfe64;\n  t.lezh = 0x026e;\n  t.lfblock = 0x258c;\n  t.lhookretroflex = 0x026d;\n  t.lira = 0x20a4;\n  t.liwnarmenian = 0x056c;\n  t.lj = 0x01c9;\n  t.ljecyrillic = 0x0459;\n  t.ll = 0xf6c0;\n  t.lladeva = 0x0933;\n  t.llagujarati = 0x0ab3;\n  t.llinebelow = 0x1e3b;\n  t.llladeva = 0x0934;\n  t.llvocalicbengali = 0x09e1;\n  t.llvocalicdeva = 0x0961;\n  t.llvocalicvowelsignbengali = 0x09e3;\n  t.llvocalicvowelsigndeva = 0x0963;\n  t.lmiddletilde = 0x026b;\n  t.lmonospace = 0xff4c;\n  t.lmsquare = 0x33d0;\n  t.lochulathai = 0x0e2c;\n  t.logicaland = 0x2227;\n  t.logicalnot = 0x00ac;\n  t.logicalnotreversed = 0x2310;\n  t.logicalor = 0x2228;\n  t.lolingthai = 0x0e25;\n  t.longs = 0x017f;\n  t.lowlinecenterline = 0xfe4e;\n  t.lowlinecmb = 0x0332;\n  t.lowlinedashed = 0xfe4d;\n  t.lozenge = 0x25ca;\n  t.lparen = 0x24a7;\n  t.lslash = 0x0142;\n  t.lsquare = 0x2113;\n  t.lsuperior = 0xf6ee;\n  t.ltshade = 0x2591;\n  t.luthai = 0x0e26;\n  t.lvocalicbengali = 0x098c;\n  t.lvocalicdeva = 0x090c;\n  t.lvocalicvowelsignbengali = 0x09e2;\n  t.lvocalicvowelsigndeva = 0x0962;\n  t.lxsquare = 0x33d3;\n  t.m = 0x006d;\n  t.mabengali = 0x09ae;\n  t.macron = 0x00af;\n  t.macronbelowcmb = 0x0331;\n  t.macroncmb = 0x0304;\n  t.macronlowmod = 0x02cd;\n  t.macronmonospace = 0xffe3;\n  t.macute = 0x1e3f;\n  t.madeva = 0x092e;\n  t.magujarati = 0x0aae;\n  t.magurmukhi = 0x0a2e;\n  t.mahapakhhebrew = 0x05a4;\n  t.mahapakhlefthebrew = 0x05a4;\n  t.mahiragana = 0x307e;\n  t.maichattawalowleftthai = 0xf895;\n  t.maichattawalowrightthai = 0xf894;\n  t.maichattawathai = 0x0e4b;\n  t.maichattawaupperleftthai = 0xf893;\n  t.maieklowleftthai = 0xf88c;\n  t.maieklowrightthai = 0xf88b;\n  t.maiekthai = 0x0e48;\n  t.maiekupperleftthai = 0xf88a;\n  t.maihanakatleftthai = 0xf884;\n  t.maihanakatthai = 0x0e31;\n  t.maitaikhuleftthai = 0xf889;\n  t.maitaikhuthai = 0x0e47;\n  t.maitholowleftthai = 0xf88f;\n  t.maitholowrightthai = 0xf88e;\n  t.maithothai = 0x0e49;\n  t.maithoupperleftthai = 0xf88d;\n  t.maitrilowleftthai = 0xf892;\n  t.maitrilowrightthai = 0xf891;\n  t.maitrithai = 0x0e4a;\n  t.maitriupperleftthai = 0xf890;\n  t.maiyamokthai = 0x0e46;\n  t.makatakana = 0x30de;\n  t.makatakanahalfwidth = 0xff8f;\n  t.male = 0x2642;\n  t.mansyonsquare = 0x3347;\n  t.maqafhebrew = 0x05be;\n  t.mars = 0x2642;\n  t.masoracirclehebrew = 0x05af;\n  t.masquare = 0x3383;\n  t.mbopomofo = 0x3107;\n  t.mbsquare = 0x33d4;\n  t.mcircle = 0x24dc;\n  t.mcubedsquare = 0x33a5;\n  t.mdotaccent = 0x1e41;\n  t.mdotbelow = 0x1e43;\n  t.meemarabic = 0x0645;\n  t.meemfinalarabic = 0xfee2;\n  t.meeminitialarabic = 0xfee3;\n  t.meemmedialarabic = 0xfee4;\n  t.meemmeeminitialarabic = 0xfcd1;\n  t.meemmeemisolatedarabic = 0xfc48;\n  t.meetorusquare = 0x334d;\n  t.mehiragana = 0x3081;\n  t.meizierasquare = 0x337e;\n  t.mekatakana = 0x30e1;\n  t.mekatakanahalfwidth = 0xff92;\n  t.mem = 0x05de;\n  t.memdagesh = 0xfb3e;\n  t.memdageshhebrew = 0xfb3e;\n  t.memhebrew = 0x05de;\n  t.menarmenian = 0x0574;\n  t.merkhahebrew = 0x05a5;\n  t.merkhakefulahebrew = 0x05a6;\n  t.merkhakefulalefthebrew = 0x05a6;\n  t.merkhalefthebrew = 0x05a5;\n  t.mhook = 0x0271;\n  t.mhzsquare = 0x3392;\n  t.middledotkatakanahalfwidth = 0xff65;\n  t.middot = 0x00b7;\n  t.mieumacirclekorean = 0x3272;\n  t.mieumaparenkorean = 0x3212;\n  t.mieumcirclekorean = 0x3264;\n  t.mieumkorean = 0x3141;\n  t.mieumpansioskorean = 0x3170;\n  t.mieumparenkorean = 0x3204;\n  t.mieumpieupkorean = 0x316e;\n  t.mieumsioskorean = 0x316f;\n  t.mihiragana = 0x307f;\n  t.mikatakana = 0x30df;\n  t.mikatakanahalfwidth = 0xff90;\n  t.minus = 0x2212;\n  t.minusbelowcmb = 0x0320;\n  t.minuscircle = 0x2296;\n  t.minusmod = 0x02d7;\n  t.minusplus = 0x2213;\n  t.minute = 0x2032;\n  t.miribaarusquare = 0x334a;\n  t.mirisquare = 0x3349;\n  t.mlonglegturned = 0x0270;\n  t.mlsquare = 0x3396;\n  t.mmcubedsquare = 0x33a3;\n  t.mmonospace = 0xff4d;\n  t.mmsquaredsquare = 0x339f;\n  t.mohiragana = 0x3082;\n  t.mohmsquare = 0x33c1;\n  t.mokatakana = 0x30e2;\n  t.mokatakanahalfwidth = 0xff93;\n  t.molsquare = 0x33d6;\n  t.momathai = 0x0e21;\n  t.moverssquare = 0x33a7;\n  t.moverssquaredsquare = 0x33a8;\n  t.mparen = 0x24a8;\n  t.mpasquare = 0x33ab;\n  t.mssquare = 0x33b3;\n  t.msuperior = 0xf6ef;\n  t.mturned = 0x026f;\n  t.mu = 0x00b5;\n  t.mu1 = 0x00b5;\n  t.muasquare = 0x3382;\n  t.muchgreater = 0x226b;\n  t.muchless = 0x226a;\n  t.mufsquare = 0x338c;\n  t.mugreek = 0x03bc;\n  t.mugsquare = 0x338d;\n  t.muhiragana = 0x3080;\n  t.mukatakana = 0x30e0;\n  t.mukatakanahalfwidth = 0xff91;\n  t.mulsquare = 0x3395;\n  t.multiply = 0x00d7;\n  t.mumsquare = 0x339b;\n  t.munahhebrew = 0x05a3;\n  t.munahlefthebrew = 0x05a3;\n  t.musicalnote = 0x266a;\n  t.musicalnotedbl = 0x266b;\n  t.musicflatsign = 0x266d;\n  t.musicsharpsign = 0x266f;\n  t.mussquare = 0x33b2;\n  t.muvsquare = 0x33b6;\n  t.muwsquare = 0x33bc;\n  t.mvmegasquare = 0x33b9;\n  t.mvsquare = 0x33b7;\n  t.mwmegasquare = 0x33bf;\n  t.mwsquare = 0x33bd;\n  t.n = 0x006e;\n  t.nabengali = 0x09a8;\n  t.nabla = 0x2207;\n  t.nacute = 0x0144;\n  t.nadeva = 0x0928;\n  t.nagujarati = 0x0aa8;\n  t.nagurmukhi = 0x0a28;\n  t.nahiragana = 0x306a;\n  t.nakatakana = 0x30ca;\n  t.nakatakanahalfwidth = 0xff85;\n  t.napostrophe = 0x0149;\n  t.nasquare = 0x3381;\n  t.nbopomofo = 0x310b;\n  t.nbspace = 0x00a0;\n  t.ncaron = 0x0148;\n  t.ncedilla = 0x0146;\n  t.ncircle = 0x24dd;\n  t.ncircumflexbelow = 0x1e4b;\n  t.ncommaaccent = 0x0146;\n  t.ndotaccent = 0x1e45;\n  t.ndotbelow = 0x1e47;\n  t.nehiragana = 0x306d;\n  t.nekatakana = 0x30cd;\n  t.nekatakanahalfwidth = 0xff88;\n  t.newsheqelsign = 0x20aa;\n  t.nfsquare = 0x338b;\n  t.ngabengali = 0x0999;\n  t.ngadeva = 0x0919;\n  t.ngagujarati = 0x0a99;\n  t.ngagurmukhi = 0x0a19;\n  t.ngonguthai = 0x0e07;\n  t.nhiragana = 0x3093;\n  t.nhookleft = 0x0272;\n  t.nhookretroflex = 0x0273;\n  t.nieunacirclekorean = 0x326f;\n  t.nieunaparenkorean = 0x320f;\n  t.nieuncieuckorean = 0x3135;\n  t.nieuncirclekorean = 0x3261;\n  t.nieunhieuhkorean = 0x3136;\n  t.nieunkorean = 0x3134;\n  t.nieunpansioskorean = 0x3168;\n  t.nieunparenkorean = 0x3201;\n  t.nieunsioskorean = 0x3167;\n  t.nieuntikeutkorean = 0x3166;\n  t.nihiragana = 0x306b;\n  t.nikatakana = 0x30cb;\n  t.nikatakanahalfwidth = 0xff86;\n  t.nikhahitleftthai = 0xf899;\n  t.nikhahitthai = 0x0e4d;\n  t.nine = 0x0039;\n  t.ninearabic = 0x0669;\n  t.ninebengali = 0x09ef;\n  t.ninecircle = 0x2468;\n  t.ninecircleinversesansserif = 0x2792;\n  t.ninedeva = 0x096f;\n  t.ninegujarati = 0x0aef;\n  t.ninegurmukhi = 0x0a6f;\n  t.ninehackarabic = 0x0669;\n  t.ninehangzhou = 0x3029;\n  t.nineideographicparen = 0x3228;\n  t.nineinferior = 0x2089;\n  t.ninemonospace = 0xff19;\n  t.nineoldstyle = 0xf739;\n  t.nineparen = 0x247c;\n  t.nineperiod = 0x2490;\n  t.ninepersian = 0x06f9;\n  t.nineroman = 0x2178;\n  t.ninesuperior = 0x2079;\n  t.nineteencircle = 0x2472;\n  t.nineteenparen = 0x2486;\n  t.nineteenperiod = 0x249a;\n  t.ninethai = 0x0e59;\n  t.nj = 0x01cc;\n  t.njecyrillic = 0x045a;\n  t.nkatakana = 0x30f3;\n  t.nkatakanahalfwidth = 0xff9d;\n  t.nlegrightlong = 0x019e;\n  t.nlinebelow = 0x1e49;\n  t.nmonospace = 0xff4e;\n  t.nmsquare = 0x339a;\n  t.nnabengali = 0x09a3;\n  t.nnadeva = 0x0923;\n  t.nnagujarati = 0x0aa3;\n  t.nnagurmukhi = 0x0a23;\n  t.nnnadeva = 0x0929;\n  t.nohiragana = 0x306e;\n  t.nokatakana = 0x30ce;\n  t.nokatakanahalfwidth = 0xff89;\n  t.nonbreakingspace = 0x00a0;\n  t.nonenthai = 0x0e13;\n  t.nonuthai = 0x0e19;\n  t.noonarabic = 0x0646;\n  t.noonfinalarabic = 0xfee6;\n  t.noonghunnaarabic = 0x06ba;\n  t.noonghunnafinalarabic = 0xfb9f;\n  t.nooninitialarabic = 0xfee7;\n  t.noonjeeminitialarabic = 0xfcd2;\n  t.noonjeemisolatedarabic = 0xfc4b;\n  t.noonmedialarabic = 0xfee8;\n  t.noonmeeminitialarabic = 0xfcd5;\n  t.noonmeemisolatedarabic = 0xfc4e;\n  t.noonnoonfinalarabic = 0xfc8d;\n  t.notcontains = 0x220c;\n  t.notelement = 0x2209;\n  t.notelementof = 0x2209;\n  t.notequal = 0x2260;\n  t.notgreater = 0x226f;\n  t.notgreaternorequal = 0x2271;\n  t.notgreaternorless = 0x2279;\n  t.notidentical = 0x2262;\n  t.notless = 0x226e;\n  t.notlessnorequal = 0x2270;\n  t.notparallel = 0x2226;\n  t.notprecedes = 0x2280;\n  t.notsubset = 0x2284;\n  t.notsucceeds = 0x2281;\n  t.notsuperset = 0x2285;\n  t.nowarmenian = 0x0576;\n  t.nparen = 0x24a9;\n  t.nssquare = 0x33b1;\n  t.nsuperior = 0x207f;\n  t.ntilde = 0x00f1;\n  t.nu = 0x03bd;\n  t.nuhiragana = 0x306c;\n  t.nukatakana = 0x30cc;\n  t.nukatakanahalfwidth = 0xff87;\n  t.nuktabengali = 0x09bc;\n  t.nuktadeva = 0x093c;\n  t.nuktagujarati = 0x0abc;\n  t.nuktagurmukhi = 0x0a3c;\n  t.numbersign = 0x0023;\n  t.numbersignmonospace = 0xff03;\n  t.numbersignsmall = 0xfe5f;\n  t.numeralsigngreek = 0x0374;\n  t.numeralsignlowergreek = 0x0375;\n  t.numero = 0x2116;\n  t.nun = 0x05e0;\n  t.nundagesh = 0xfb40;\n  t.nundageshhebrew = 0xfb40;\n  t.nunhebrew = 0x05e0;\n  t.nvsquare = 0x33b5;\n  t.nwsquare = 0x33bb;\n  t.nyabengali = 0x099e;\n  t.nyadeva = 0x091e;\n  t.nyagujarati = 0x0a9e;\n  t.nyagurmukhi = 0x0a1e;\n  t.o = 0x006f;\n  t.oacute = 0x00f3;\n  t.oangthai = 0x0e2d;\n  t.obarred = 0x0275;\n  t.obarredcyrillic = 0x04e9;\n  t.obarreddieresiscyrillic = 0x04eb;\n  t.obengali = 0x0993;\n  t.obopomofo = 0x311b;\n  t.obreve = 0x014f;\n  t.ocandradeva = 0x0911;\n  t.ocandragujarati = 0x0a91;\n  t.ocandravowelsigndeva = 0x0949;\n  t.ocandravowelsigngujarati = 0x0ac9;\n  t.ocaron = 0x01d2;\n  t.ocircle = 0x24de;\n  t.ocircumflex = 0x00f4;\n  t.ocircumflexacute = 0x1ed1;\n  t.ocircumflexdotbelow = 0x1ed9;\n  t.ocircumflexgrave = 0x1ed3;\n  t.ocircumflexhookabove = 0x1ed5;\n  t.ocircumflextilde = 0x1ed7;\n  t.ocyrillic = 0x043e;\n  t.odblacute = 0x0151;\n  t.odblgrave = 0x020d;\n  t.odeva = 0x0913;\n  t.odieresis = 0x00f6;\n  t.odieresiscyrillic = 0x04e7;\n  t.odotbelow = 0x1ecd;\n  t.oe = 0x0153;\n  t.oekorean = 0x315a;\n  t.ogonek = 0x02db;\n  t.ogonekcmb = 0x0328;\n  t.ograve = 0x00f2;\n  t.ogujarati = 0x0a93;\n  t.oharmenian = 0x0585;\n  t.ohiragana = 0x304a;\n  t.ohookabove = 0x1ecf;\n  t.ohorn = 0x01a1;\n  t.ohornacute = 0x1edb;\n  t.ohorndotbelow = 0x1ee3;\n  t.ohorngrave = 0x1edd;\n  t.ohornhookabove = 0x1edf;\n  t.ohorntilde = 0x1ee1;\n  t.ohungarumlaut = 0x0151;\n  t.oi = 0x01a3;\n  t.oinvertedbreve = 0x020f;\n  t.okatakana = 0x30aa;\n  t.okatakanahalfwidth = 0xff75;\n  t.okorean = 0x3157;\n  t.olehebrew = 0x05ab;\n  t.omacron = 0x014d;\n  t.omacronacute = 0x1e53;\n  t.omacrongrave = 0x1e51;\n  t.omdeva = 0x0950;\n  t.omega = 0x03c9;\n  t.omega1 = 0x03d6;\n  t.omegacyrillic = 0x0461;\n  t.omegalatinclosed = 0x0277;\n  t.omegaroundcyrillic = 0x047b;\n  t.omegatitlocyrillic = 0x047d;\n  t.omegatonos = 0x03ce;\n  t.omgujarati = 0x0ad0;\n  t.omicron = 0x03bf;\n  t.omicrontonos = 0x03cc;\n  t.omonospace = 0xff4f;\n  t.one = 0x0031;\n  t.onearabic = 0x0661;\n  t.onebengali = 0x09e7;\n  t.onecircle = 0x2460;\n  t.onecircleinversesansserif = 0x278a;\n  t.onedeva = 0x0967;\n  t.onedotenleader = 0x2024;\n  t.oneeighth = 0x215b;\n  t.onefitted = 0xf6dc;\n  t.onegujarati = 0x0ae7;\n  t.onegurmukhi = 0x0a67;\n  t.onehackarabic = 0x0661;\n  t.onehalf = 0x00bd;\n  t.onehangzhou = 0x3021;\n  t.oneideographicparen = 0x3220;\n  t.oneinferior = 0x2081;\n  t.onemonospace = 0xff11;\n  t.onenumeratorbengali = 0x09f4;\n  t.oneoldstyle = 0xf731;\n  t.oneparen = 0x2474;\n  t.oneperiod = 0x2488;\n  t.onepersian = 0x06f1;\n  t.onequarter = 0x00bc;\n  t.oneroman = 0x2170;\n  t.onesuperior = 0x00b9;\n  t.onethai = 0x0e51;\n  t.onethird = 0x2153;\n  t.oogonek = 0x01eb;\n  t.oogonekmacron = 0x01ed;\n  t.oogurmukhi = 0x0a13;\n  t.oomatragurmukhi = 0x0a4b;\n  t.oopen = 0x0254;\n  t.oparen = 0x24aa;\n  t.openbullet = 0x25e6;\n  t.option = 0x2325;\n  t.ordfeminine = 0x00aa;\n  t.ordmasculine = 0x00ba;\n  t.orthogonal = 0x221f;\n  t.oshortdeva = 0x0912;\n  t.oshortvowelsigndeva = 0x094a;\n  t.oslash = 0x00f8;\n  t.oslashacute = 0x01ff;\n  t.osmallhiragana = 0x3049;\n  t.osmallkatakana = 0x30a9;\n  t.osmallkatakanahalfwidth = 0xff6b;\n  t.ostrokeacute = 0x01ff;\n  t.osuperior = 0xf6f0;\n  t.otcyrillic = 0x047f;\n  t.otilde = 0x00f5;\n  t.otildeacute = 0x1e4d;\n  t.otildedieresis = 0x1e4f;\n  t.oubopomofo = 0x3121;\n  t.overline = 0x203e;\n  t.overlinecenterline = 0xfe4a;\n  t.overlinecmb = 0x0305;\n  t.overlinedashed = 0xfe49;\n  t.overlinedblwavy = 0xfe4c;\n  t.overlinewavy = 0xfe4b;\n  t.overscore = 0x00af;\n  t.ovowelsignbengali = 0x09cb;\n  t.ovowelsigndeva = 0x094b;\n  t.ovowelsigngujarati = 0x0acb;\n  t.p = 0x0070;\n  t.paampssquare = 0x3380;\n  t.paasentosquare = 0x332b;\n  t.pabengali = 0x09aa;\n  t.pacute = 0x1e55;\n  t.padeva = 0x092a;\n  t.pagedown = 0x21df;\n  t.pageup = 0x21de;\n  t.pagujarati = 0x0aaa;\n  t.pagurmukhi = 0x0a2a;\n  t.pahiragana = 0x3071;\n  t.paiyannoithai = 0x0e2f;\n  t.pakatakana = 0x30d1;\n  t.palatalizationcyrilliccmb = 0x0484;\n  t.palochkacyrillic = 0x04c0;\n  t.pansioskorean = 0x317f;\n  t.paragraph = 0x00b6;\n  t.parallel = 0x2225;\n  t.parenleft = 0x0028;\n  t.parenleftaltonearabic = 0xfd3e;\n  t.parenleftbt = 0xf8ed;\n  t.parenleftex = 0xf8ec;\n  t.parenleftinferior = 0x208d;\n  t.parenleftmonospace = 0xff08;\n  t.parenleftsmall = 0xfe59;\n  t.parenleftsuperior = 0x207d;\n  t.parenlefttp = 0xf8eb;\n  t.parenleftvertical = 0xfe35;\n  t.parenright = 0x0029;\n  t.parenrightaltonearabic = 0xfd3f;\n  t.parenrightbt = 0xf8f8;\n  t.parenrightex = 0xf8f7;\n  t.parenrightinferior = 0x208e;\n  t.parenrightmonospace = 0xff09;\n  t.parenrightsmall = 0xfe5a;\n  t.parenrightsuperior = 0x207e;\n  t.parenrighttp = 0xf8f6;\n  t.parenrightvertical = 0xfe36;\n  t.partialdiff = 0x2202;\n  t.paseqhebrew = 0x05c0;\n  t.pashtahebrew = 0x0599;\n  t.pasquare = 0x33a9;\n  t.patah = 0x05b7;\n  t.patah11 = 0x05b7;\n  t.patah1d = 0x05b7;\n  t.patah2a = 0x05b7;\n  t.patahhebrew = 0x05b7;\n  t.patahnarrowhebrew = 0x05b7;\n  t.patahquarterhebrew = 0x05b7;\n  t.patahwidehebrew = 0x05b7;\n  t.pazerhebrew = 0x05a1;\n  t.pbopomofo = 0x3106;\n  t.pcircle = 0x24df;\n  t.pdotaccent = 0x1e57;\n  t.pe = 0x05e4;\n  t.pecyrillic = 0x043f;\n  t.pedagesh = 0xfb44;\n  t.pedageshhebrew = 0xfb44;\n  t.peezisquare = 0x333b;\n  t.pefinaldageshhebrew = 0xfb43;\n  t.peharabic = 0x067e;\n  t.peharmenian = 0x057a;\n  t.pehebrew = 0x05e4;\n  t.pehfinalarabic = 0xfb57;\n  t.pehinitialarabic = 0xfb58;\n  t.pehiragana = 0x307a;\n  t.pehmedialarabic = 0xfb59;\n  t.pekatakana = 0x30da;\n  t.pemiddlehookcyrillic = 0x04a7;\n  t.perafehebrew = 0xfb4e;\n  t.percent = 0x0025;\n  t.percentarabic = 0x066a;\n  t.percentmonospace = 0xff05;\n  t.percentsmall = 0xfe6a;\n  t.period = 0x002e;\n  t.periodarmenian = 0x0589;\n  t.periodcentered = 0x00b7;\n  t.periodhalfwidth = 0xff61;\n  t.periodinferior = 0xf6e7;\n  t.periodmonospace = 0xff0e;\n  t.periodsmall = 0xfe52;\n  t.periodsuperior = 0xf6e8;\n  t.perispomenigreekcmb = 0x0342;\n  t.perpendicular = 0x22a5;\n  t.perthousand = 0x2030;\n  t.peseta = 0x20a7;\n  t.pfsquare = 0x338a;\n  t.phabengali = 0x09ab;\n  t.phadeva = 0x092b;\n  t.phagujarati = 0x0aab;\n  t.phagurmukhi = 0x0a2b;\n  t.phi = 0x03c6;\n  t.phi1 = 0x03d5;\n  t.phieuphacirclekorean = 0x327a;\n  t.phieuphaparenkorean = 0x321a;\n  t.phieuphcirclekorean = 0x326c;\n  t.phieuphkorean = 0x314d;\n  t.phieuphparenkorean = 0x320c;\n  t.philatin = 0x0278;\n  t.phinthuthai = 0x0e3a;\n  t.phisymbolgreek = 0x03d5;\n  t.phook = 0x01a5;\n  t.phophanthai = 0x0e1e;\n  t.phophungthai = 0x0e1c;\n  t.phosamphaothai = 0x0e20;\n  t.pi = 0x03c0;\n  t.pieupacirclekorean = 0x3273;\n  t.pieupaparenkorean = 0x3213;\n  t.pieupcieuckorean = 0x3176;\n  t.pieupcirclekorean = 0x3265;\n  t.pieupkiyeokkorean = 0x3172;\n  t.pieupkorean = 0x3142;\n  t.pieupparenkorean = 0x3205;\n  t.pieupsioskiyeokkorean = 0x3174;\n  t.pieupsioskorean = 0x3144;\n  t.pieupsiostikeutkorean = 0x3175;\n  t.pieupthieuthkorean = 0x3177;\n  t.pieuptikeutkorean = 0x3173;\n  t.pihiragana = 0x3074;\n  t.pikatakana = 0x30d4;\n  t.pisymbolgreek = 0x03d6;\n  t.piwrarmenian = 0x0583;\n  t.planckover2pi = 0x210f;\n  t.planckover2pi1 = 0x210f;\n  t.plus = 0x002b;\n  t.plusbelowcmb = 0x031f;\n  t.pluscircle = 0x2295;\n  t.plusminus = 0x00b1;\n  t.plusmod = 0x02d6;\n  t.plusmonospace = 0xff0b;\n  t.plussmall = 0xfe62;\n  t.plussuperior = 0x207a;\n  t.pmonospace = 0xff50;\n  t.pmsquare = 0x33d8;\n  t.pohiragana = 0x307d;\n  t.pointingindexdownwhite = 0x261f;\n  t.pointingindexleftwhite = 0x261c;\n  t.pointingindexrightwhite = 0x261e;\n  t.pointingindexupwhite = 0x261d;\n  t.pokatakana = 0x30dd;\n  t.poplathai = 0x0e1b;\n  t.postalmark = 0x3012;\n  t.postalmarkface = 0x3020;\n  t.pparen = 0x24ab;\n  t.precedes = 0x227a;\n  t.prescription = 0x211e;\n  t.primemod = 0x02b9;\n  t.primereversed = 0x2035;\n  t.product = 0x220f;\n  t.projective = 0x2305;\n  t.prolongedkana = 0x30fc;\n  t.propellor = 0x2318;\n  t.propersubset = 0x2282;\n  t.propersuperset = 0x2283;\n  t.proportion = 0x2237;\n  t.proportional = 0x221d;\n  t.psi = 0x03c8;\n  t.psicyrillic = 0x0471;\n  t.psilipneumatacyrilliccmb = 0x0486;\n  t.pssquare = 0x33b0;\n  t.puhiragana = 0x3077;\n  t.pukatakana = 0x30d7;\n  t.pvsquare = 0x33b4;\n  t.pwsquare = 0x33ba;\n  t.q = 0x0071;\n  t.qadeva = 0x0958;\n  t.qadmahebrew = 0x05a8;\n  t.qafarabic = 0x0642;\n  t.qaffinalarabic = 0xfed6;\n  t.qafinitialarabic = 0xfed7;\n  t.qafmedialarabic = 0xfed8;\n  t.qamats = 0x05b8;\n  t.qamats10 = 0x05b8;\n  t.qamats1a = 0x05b8;\n  t.qamats1c = 0x05b8;\n  t.qamats27 = 0x05b8;\n  t.qamats29 = 0x05b8;\n  t.qamats33 = 0x05b8;\n  t.qamatsde = 0x05b8;\n  t.qamatshebrew = 0x05b8;\n  t.qamatsnarrowhebrew = 0x05b8;\n  t.qamatsqatanhebrew = 0x05b8;\n  t.qamatsqatannarrowhebrew = 0x05b8;\n  t.qamatsqatanquarterhebrew = 0x05b8;\n  t.qamatsqatanwidehebrew = 0x05b8;\n  t.qamatsquarterhebrew = 0x05b8;\n  t.qamatswidehebrew = 0x05b8;\n  t.qarneyparahebrew = 0x059f;\n  t.qbopomofo = 0x3111;\n  t.qcircle = 0x24e0;\n  t.qhook = 0x02a0;\n  t.qmonospace = 0xff51;\n  t.qof = 0x05e7;\n  t.qofdagesh = 0xfb47;\n  t.qofdageshhebrew = 0xfb47;\n  t.qofhebrew = 0x05e7;\n  t.qparen = 0x24ac;\n  t.quarternote = 0x2669;\n  t.qubuts = 0x05bb;\n  t.qubuts18 = 0x05bb;\n  t.qubuts25 = 0x05bb;\n  t.qubuts31 = 0x05bb;\n  t.qubutshebrew = 0x05bb;\n  t.qubutsnarrowhebrew = 0x05bb;\n  t.qubutsquarterhebrew = 0x05bb;\n  t.qubutswidehebrew = 0x05bb;\n  t.question = 0x003f;\n  t.questionarabic = 0x061f;\n  t.questionarmenian = 0x055e;\n  t.questiondown = 0x00bf;\n  t.questiondownsmall = 0xf7bf;\n  t.questiongreek = 0x037e;\n  t.questionmonospace = 0xff1f;\n  t.questionsmall = 0xf73f;\n  t.quotedbl = 0x0022;\n  t.quotedblbase = 0x201e;\n  t.quotedblleft = 0x201c;\n  t.quotedblmonospace = 0xff02;\n  t.quotedblprime = 0x301e;\n  t.quotedblprimereversed = 0x301d;\n  t.quotedblright = 0x201d;\n  t.quoteleft = 0x2018;\n  t.quoteleftreversed = 0x201b;\n  t.quotereversed = 0x201b;\n  t.quoteright = 0x2019;\n  t.quoterightn = 0x0149;\n  t.quotesinglbase = 0x201a;\n  t.quotesingle = 0x0027;\n  t.quotesinglemonospace = 0xff07;\n  t.r = 0x0072;\n  t.raarmenian = 0x057c;\n  t.rabengali = 0x09b0;\n  t.racute = 0x0155;\n  t.radeva = 0x0930;\n  t.radical = 0x221a;\n  t.radicalex = 0xf8e5;\n  t.radoverssquare = 0x33ae;\n  t.radoverssquaredsquare = 0x33af;\n  t.radsquare = 0x33ad;\n  t.rafe = 0x05bf;\n  t.rafehebrew = 0x05bf;\n  t.ragujarati = 0x0ab0;\n  t.ragurmukhi = 0x0a30;\n  t.rahiragana = 0x3089;\n  t.rakatakana = 0x30e9;\n  t.rakatakanahalfwidth = 0xff97;\n  t.ralowerdiagonalbengali = 0x09f1;\n  t.ramiddlediagonalbengali = 0x09f0;\n  t.ramshorn = 0x0264;\n  t.ratio = 0x2236;\n  t.rbopomofo = 0x3116;\n  t.rcaron = 0x0159;\n  t.rcedilla = 0x0157;\n  t.rcircle = 0x24e1;\n  t.rcommaaccent = 0x0157;\n  t.rdblgrave = 0x0211;\n  t.rdotaccent = 0x1e59;\n  t.rdotbelow = 0x1e5b;\n  t.rdotbelowmacron = 0x1e5d;\n  t.referencemark = 0x203b;\n  t.reflexsubset = 0x2286;\n  t.reflexsuperset = 0x2287;\n  t.registered = 0x00ae;\n  t.registersans = 0xf8e8;\n  t.registerserif = 0xf6da;\n  t.reharabic = 0x0631;\n  t.reharmenian = 0x0580;\n  t.rehfinalarabic = 0xfeae;\n  t.rehiragana = 0x308c;\n  t.rekatakana = 0x30ec;\n  t.rekatakanahalfwidth = 0xff9a;\n  t.resh = 0x05e8;\n  t.reshdageshhebrew = 0xfb48;\n  t.reshhebrew = 0x05e8;\n  t.reversedtilde = 0x223d;\n  t.reviahebrew = 0x0597;\n  t.reviamugrashhebrew = 0x0597;\n  t.revlogicalnot = 0x2310;\n  t.rfishhook = 0x027e;\n  t.rfishhookreversed = 0x027f;\n  t.rhabengali = 0x09dd;\n  t.rhadeva = 0x095d;\n  t.rho = 0x03c1;\n  t.rhook = 0x027d;\n  t.rhookturned = 0x027b;\n  t.rhookturnedsuperior = 0x02b5;\n  t.rhosymbolgreek = 0x03f1;\n  t.rhotichookmod = 0x02de;\n  t.rieulacirclekorean = 0x3271;\n  t.rieulaparenkorean = 0x3211;\n  t.rieulcirclekorean = 0x3263;\n  t.rieulhieuhkorean = 0x3140;\n  t.rieulkiyeokkorean = 0x313a;\n  t.rieulkiyeoksioskorean = 0x3169;\n  t.rieulkorean = 0x3139;\n  t.rieulmieumkorean = 0x313b;\n  t.rieulpansioskorean = 0x316c;\n  t.rieulparenkorean = 0x3203;\n  t.rieulphieuphkorean = 0x313f;\n  t.rieulpieupkorean = 0x313c;\n  t.rieulpieupsioskorean = 0x316b;\n  t.rieulsioskorean = 0x313d;\n  t.rieulthieuthkorean = 0x313e;\n  t.rieultikeutkorean = 0x316a;\n  t.rieulyeorinhieuhkorean = 0x316d;\n  t.rightangle = 0x221f;\n  t.righttackbelowcmb = 0x0319;\n  t.righttriangle = 0x22bf;\n  t.rihiragana = 0x308a;\n  t.rikatakana = 0x30ea;\n  t.rikatakanahalfwidth = 0xff98;\n  t.ring = 0x02da;\n  t.ringbelowcmb = 0x0325;\n  t.ringcmb = 0x030a;\n  t.ringhalfleft = 0x02bf;\n  t.ringhalfleftarmenian = 0x0559;\n  t.ringhalfleftbelowcmb = 0x031c;\n  t.ringhalfleftcentered = 0x02d3;\n  t.ringhalfright = 0x02be;\n  t.ringhalfrightbelowcmb = 0x0339;\n  t.ringhalfrightcentered = 0x02d2;\n  t.rinvertedbreve = 0x0213;\n  t.rittorusquare = 0x3351;\n  t.rlinebelow = 0x1e5f;\n  t.rlongleg = 0x027c;\n  t.rlonglegturned = 0x027a;\n  t.rmonospace = 0xff52;\n  t.rohiragana = 0x308d;\n  t.rokatakana = 0x30ed;\n  t.rokatakanahalfwidth = 0xff9b;\n  t.roruathai = 0x0e23;\n  t.rparen = 0x24ad;\n  t.rrabengali = 0x09dc;\n  t.rradeva = 0x0931;\n  t.rragurmukhi = 0x0a5c;\n  t.rreharabic = 0x0691;\n  t.rrehfinalarabic = 0xfb8d;\n  t.rrvocalicbengali = 0x09e0;\n  t.rrvocalicdeva = 0x0960;\n  t.rrvocalicgujarati = 0x0ae0;\n  t.rrvocalicvowelsignbengali = 0x09c4;\n  t.rrvocalicvowelsigndeva = 0x0944;\n  t.rrvocalicvowelsigngujarati = 0x0ac4;\n  t.rsuperior = 0xf6f1;\n  t.rtblock = 0x2590;\n  t.rturned = 0x0279;\n  t.rturnedsuperior = 0x02b4;\n  t.ruhiragana = 0x308b;\n  t.rukatakana = 0x30eb;\n  t.rukatakanahalfwidth = 0xff99;\n  t.rupeemarkbengali = 0x09f2;\n  t.rupeesignbengali = 0x09f3;\n  t.rupiah = 0xf6dd;\n  t.ruthai = 0x0e24;\n  t.rvocalicbengali = 0x098b;\n  t.rvocalicdeva = 0x090b;\n  t.rvocalicgujarati = 0x0a8b;\n  t.rvocalicvowelsignbengali = 0x09c3;\n  t.rvocalicvowelsigndeva = 0x0943;\n  t.rvocalicvowelsigngujarati = 0x0ac3;\n  t.s = 0x0073;\n  t.sabengali = 0x09b8;\n  t.sacute = 0x015b;\n  t.sacutedotaccent = 0x1e65;\n  t.sadarabic = 0x0635;\n  t.sadeva = 0x0938;\n  t.sadfinalarabic = 0xfeba;\n  t.sadinitialarabic = 0xfebb;\n  t.sadmedialarabic = 0xfebc;\n  t.sagujarati = 0x0ab8;\n  t.sagurmukhi = 0x0a38;\n  t.sahiragana = 0x3055;\n  t.sakatakana = 0x30b5;\n  t.sakatakanahalfwidth = 0xff7b;\n  t.sallallahoualayhewasallamarabic = 0xfdfa;\n  t.samekh = 0x05e1;\n  t.samekhdagesh = 0xfb41;\n  t.samekhdageshhebrew = 0xfb41;\n  t.samekhhebrew = 0x05e1;\n  t.saraaathai = 0x0e32;\n  t.saraaethai = 0x0e41;\n  t.saraaimaimalaithai = 0x0e44;\n  t.saraaimaimuanthai = 0x0e43;\n  t.saraamthai = 0x0e33;\n  t.saraathai = 0x0e30;\n  t.saraethai = 0x0e40;\n  t.saraiileftthai = 0xf886;\n  t.saraiithai = 0x0e35;\n  t.saraileftthai = 0xf885;\n  t.saraithai = 0x0e34;\n  t.saraothai = 0x0e42;\n  t.saraueeleftthai = 0xf888;\n  t.saraueethai = 0x0e37;\n  t.saraueleftthai = 0xf887;\n  t.sarauethai = 0x0e36;\n  t.sarauthai = 0x0e38;\n  t.sarauuthai = 0x0e39;\n  t.sbopomofo = 0x3119;\n  t.scaron = 0x0161;\n  t.scarondotaccent = 0x1e67;\n  t.scedilla = 0x015f;\n  t.schwa = 0x0259;\n  t.schwacyrillic = 0x04d9;\n  t.schwadieresiscyrillic = 0x04db;\n  t.schwahook = 0x025a;\n  t.scircle = 0x24e2;\n  t.scircumflex = 0x015d;\n  t.scommaaccent = 0x0219;\n  t.sdotaccent = 0x1e61;\n  t.sdotbelow = 0x1e63;\n  t.sdotbelowdotaccent = 0x1e69;\n  t.seagullbelowcmb = 0x033c;\n  t.second = 0x2033;\n  t.secondtonechinese = 0x02ca;\n  t.section = 0x00a7;\n  t.seenarabic = 0x0633;\n  t.seenfinalarabic = 0xfeb2;\n  t.seeninitialarabic = 0xfeb3;\n  t.seenmedialarabic = 0xfeb4;\n  t.segol = 0x05b6;\n  t.segol13 = 0x05b6;\n  t.segol1f = 0x05b6;\n  t.segol2c = 0x05b6;\n  t.segolhebrew = 0x05b6;\n  t.segolnarrowhebrew = 0x05b6;\n  t.segolquarterhebrew = 0x05b6;\n  t.segoltahebrew = 0x0592;\n  t.segolwidehebrew = 0x05b6;\n  t.seharmenian = 0x057d;\n  t.sehiragana = 0x305b;\n  t.sekatakana = 0x30bb;\n  t.sekatakanahalfwidth = 0xff7e;\n  t.semicolon = 0x003b;\n  t.semicolonarabic = 0x061b;\n  t.semicolonmonospace = 0xff1b;\n  t.semicolonsmall = 0xfe54;\n  t.semivoicedmarkkana = 0x309c;\n  t.semivoicedmarkkanahalfwidth = 0xff9f;\n  t.sentisquare = 0x3322;\n  t.sentosquare = 0x3323;\n  t.seven = 0x0037;\n  t.sevenarabic = 0x0667;\n  t.sevenbengali = 0x09ed;\n  t.sevencircle = 0x2466;\n  t.sevencircleinversesansserif = 0x2790;\n  t.sevendeva = 0x096d;\n  t.seveneighths = 0x215e;\n  t.sevengujarati = 0x0aed;\n  t.sevengurmukhi = 0x0a6d;\n  t.sevenhackarabic = 0x0667;\n  t.sevenhangzhou = 0x3027;\n  t.sevenideographicparen = 0x3226;\n  t.seveninferior = 0x2087;\n  t.sevenmonospace = 0xff17;\n  t.sevenoldstyle = 0xf737;\n  t.sevenparen = 0x247a;\n  t.sevenperiod = 0x248e;\n  t.sevenpersian = 0x06f7;\n  t.sevenroman = 0x2176;\n  t.sevensuperior = 0x2077;\n  t.seventeencircle = 0x2470;\n  t.seventeenparen = 0x2484;\n  t.seventeenperiod = 0x2498;\n  t.seventhai = 0x0e57;\n  t.sfthyphen = 0x00ad;\n  t.shaarmenian = 0x0577;\n  t.shabengali = 0x09b6;\n  t.shacyrillic = 0x0448;\n  t.shaddaarabic = 0x0651;\n  t.shaddadammaarabic = 0xfc61;\n  t.shaddadammatanarabic = 0xfc5e;\n  t.shaddafathaarabic = 0xfc60;\n  t.shaddakasraarabic = 0xfc62;\n  t.shaddakasratanarabic = 0xfc5f;\n  t.shade = 0x2592;\n  t.shadedark = 0x2593;\n  t.shadelight = 0x2591;\n  t.shademedium = 0x2592;\n  t.shadeva = 0x0936;\n  t.shagujarati = 0x0ab6;\n  t.shagurmukhi = 0x0a36;\n  t.shalshelethebrew = 0x0593;\n  t.shbopomofo = 0x3115;\n  t.shchacyrillic = 0x0449;\n  t.sheenarabic = 0x0634;\n  t.sheenfinalarabic = 0xfeb6;\n  t.sheeninitialarabic = 0xfeb7;\n  t.sheenmedialarabic = 0xfeb8;\n  t.sheicoptic = 0x03e3;\n  t.sheqel = 0x20aa;\n  t.sheqelhebrew = 0x20aa;\n  t.sheva = 0x05b0;\n  t.sheva115 = 0x05b0;\n  t.sheva15 = 0x05b0;\n  t.sheva22 = 0x05b0;\n  t.sheva2e = 0x05b0;\n  t.shevahebrew = 0x05b0;\n  t.shevanarrowhebrew = 0x05b0;\n  t.shevaquarterhebrew = 0x05b0;\n  t.shevawidehebrew = 0x05b0;\n  t.shhacyrillic = 0x04bb;\n  t.shimacoptic = 0x03ed;\n  t.shin = 0x05e9;\n  t.shindagesh = 0xfb49;\n  t.shindageshhebrew = 0xfb49;\n  t.shindageshshindot = 0xfb2c;\n  t.shindageshshindothebrew = 0xfb2c;\n  t.shindageshsindot = 0xfb2d;\n  t.shindageshsindothebrew = 0xfb2d;\n  t.shindothebrew = 0x05c1;\n  t.shinhebrew = 0x05e9;\n  t.shinshindot = 0xfb2a;\n  t.shinshindothebrew = 0xfb2a;\n  t.shinsindot = 0xfb2b;\n  t.shinsindothebrew = 0xfb2b;\n  t.shook = 0x0282;\n  t.sigma = 0x03c3;\n  t.sigma1 = 0x03c2;\n  t.sigmafinal = 0x03c2;\n  t.sigmalunatesymbolgreek = 0x03f2;\n  t.sihiragana = 0x3057;\n  t.sikatakana = 0x30b7;\n  t.sikatakanahalfwidth = 0xff7c;\n  t.siluqhebrew = 0x05bd;\n  t.siluqlefthebrew = 0x05bd;\n  t.similar = 0x223c;\n  t.sindothebrew = 0x05c2;\n  t.siosacirclekorean = 0x3274;\n  t.siosaparenkorean = 0x3214;\n  t.sioscieuckorean = 0x317e;\n  t.sioscirclekorean = 0x3266;\n  t.sioskiyeokkorean = 0x317a;\n  t.sioskorean = 0x3145;\n  t.siosnieunkorean = 0x317b;\n  t.siosparenkorean = 0x3206;\n  t.siospieupkorean = 0x317d;\n  t.siostikeutkorean = 0x317c;\n  t.six = 0x0036;\n  t.sixarabic = 0x0666;\n  t.sixbengali = 0x09ec;\n  t.sixcircle = 0x2465;\n  t.sixcircleinversesansserif = 0x278f;\n  t.sixdeva = 0x096c;\n  t.sixgujarati = 0x0aec;\n  t.sixgurmukhi = 0x0a6c;\n  t.sixhackarabic = 0x0666;\n  t.sixhangzhou = 0x3026;\n  t.sixideographicparen = 0x3225;\n  t.sixinferior = 0x2086;\n  t.sixmonospace = 0xff16;\n  t.sixoldstyle = 0xf736;\n  t.sixparen = 0x2479;\n  t.sixperiod = 0x248d;\n  t.sixpersian = 0x06f6;\n  t.sixroman = 0x2175;\n  t.sixsuperior = 0x2076;\n  t.sixteencircle = 0x246f;\n  t.sixteencurrencydenominatorbengali = 0x09f9;\n  t.sixteenparen = 0x2483;\n  t.sixteenperiod = 0x2497;\n  t.sixthai = 0x0e56;\n  t.slash = 0x002f;\n  t.slashmonospace = 0xff0f;\n  t.slong = 0x017f;\n  t.slongdotaccent = 0x1e9b;\n  t.smileface = 0x263a;\n  t.smonospace = 0xff53;\n  t.sofpasuqhebrew = 0x05c3;\n  t.softhyphen = 0x00ad;\n  t.softsigncyrillic = 0x044c;\n  t.sohiragana = 0x305d;\n  t.sokatakana = 0x30bd;\n  t.sokatakanahalfwidth = 0xff7f;\n  t.soliduslongoverlaycmb = 0x0338;\n  t.solidusshortoverlaycmb = 0x0337;\n  t.sorusithai = 0x0e29;\n  t.sosalathai = 0x0e28;\n  t.sosothai = 0x0e0b;\n  t.sosuathai = 0x0e2a;\n  t.space = 0x0020;\n  t.spacehackarabic = 0x0020;\n  t.spade = 0x2660;\n  t.spadesuitblack = 0x2660;\n  t.spadesuitwhite = 0x2664;\n  t.sparen = 0x24ae;\n  t.squarebelowcmb = 0x033b;\n  t.squarecc = 0x33c4;\n  t.squarecm = 0x339d;\n  t.squarediagonalcrosshatchfill = 0x25a9;\n  t.squarehorizontalfill = 0x25a4;\n  t.squarekg = 0x338f;\n  t.squarekm = 0x339e;\n  t.squarekmcapital = 0x33ce;\n  t.squareln = 0x33d1;\n  t.squarelog = 0x33d2;\n  t.squaremg = 0x338e;\n  t.squaremil = 0x33d5;\n  t.squaremm = 0x339c;\n  t.squaremsquared = 0x33a1;\n  t.squareorthogonalcrosshatchfill = 0x25a6;\n  t.squareupperlefttolowerrightfill = 0x25a7;\n  t.squareupperrighttolowerleftfill = 0x25a8;\n  t.squareverticalfill = 0x25a5;\n  t.squarewhitewithsmallblack = 0x25a3;\n  t.srsquare = 0x33db;\n  t.ssabengali = 0x09b7;\n  t.ssadeva = 0x0937;\n  t.ssagujarati = 0x0ab7;\n  t.ssangcieuckorean = 0x3149;\n  t.ssanghieuhkorean = 0x3185;\n  t.ssangieungkorean = 0x3180;\n  t.ssangkiyeokkorean = 0x3132;\n  t.ssangnieunkorean = 0x3165;\n  t.ssangpieupkorean = 0x3143;\n  t.ssangsioskorean = 0x3146;\n  t.ssangtikeutkorean = 0x3138;\n  t.ssuperior = 0xf6f2;\n  t.sterling = 0x00a3;\n  t.sterlingmonospace = 0xffe1;\n  t.strokelongoverlaycmb = 0x0336;\n  t.strokeshortoverlaycmb = 0x0335;\n  t.subset = 0x2282;\n  t.subsetnotequal = 0x228a;\n  t.subsetorequal = 0x2286;\n  t.succeeds = 0x227b;\n  t.suchthat = 0x220b;\n  t.suhiragana = 0x3059;\n  t.sukatakana = 0x30b9;\n  t.sukatakanahalfwidth = 0xff7d;\n  t.sukunarabic = 0x0652;\n  t.summation = 0x2211;\n  t.sun = 0x263c;\n  t.superset = 0x2283;\n  t.supersetnotequal = 0x228b;\n  t.supersetorequal = 0x2287;\n  t.svsquare = 0x33dc;\n  t.syouwaerasquare = 0x337c;\n  t.t = 0x0074;\n  t.tabengali = 0x09a4;\n  t.tackdown = 0x22a4;\n  t.tackleft = 0x22a3;\n  t.tadeva = 0x0924;\n  t.tagujarati = 0x0aa4;\n  t.tagurmukhi = 0x0a24;\n  t.taharabic = 0x0637;\n  t.tahfinalarabic = 0xfec2;\n  t.tahinitialarabic = 0xfec3;\n  t.tahiragana = 0x305f;\n  t.tahmedialarabic = 0xfec4;\n  t.taisyouerasquare = 0x337d;\n  t.takatakana = 0x30bf;\n  t.takatakanahalfwidth = 0xff80;\n  t.tatweelarabic = 0x0640;\n  t.tau = 0x03c4;\n  t.tav = 0x05ea;\n  t.tavdages = 0xfb4a;\n  t.tavdagesh = 0xfb4a;\n  t.tavdageshhebrew = 0xfb4a;\n  t.tavhebrew = 0x05ea;\n  t.tbar = 0x0167;\n  t.tbopomofo = 0x310a;\n  t.tcaron = 0x0165;\n  t.tccurl = 0x02a8;\n  t.tcedilla = 0x0163;\n  t.tcheharabic = 0x0686;\n  t.tchehfinalarabic = 0xfb7b;\n  t.tchehinitialarabic = 0xfb7c;\n  t.tchehmedialarabic = 0xfb7d;\n  t.tcircle = 0x24e3;\n  t.tcircumflexbelow = 0x1e71;\n  t.tcommaaccent = 0x0163;\n  t.tdieresis = 0x1e97;\n  t.tdotaccent = 0x1e6b;\n  t.tdotbelow = 0x1e6d;\n  t.tecyrillic = 0x0442;\n  t.tedescendercyrillic = 0x04ad;\n  t.teharabic = 0x062a;\n  t.tehfinalarabic = 0xfe96;\n  t.tehhahinitialarabic = 0xfca2;\n  t.tehhahisolatedarabic = 0xfc0c;\n  t.tehinitialarabic = 0xfe97;\n  t.tehiragana = 0x3066;\n  t.tehjeeminitialarabic = 0xfca1;\n  t.tehjeemisolatedarabic = 0xfc0b;\n  t.tehmarbutaarabic = 0x0629;\n  t.tehmarbutafinalarabic = 0xfe94;\n  t.tehmedialarabic = 0xfe98;\n  t.tehmeeminitialarabic = 0xfca4;\n  t.tehmeemisolatedarabic = 0xfc0e;\n  t.tehnoonfinalarabic = 0xfc73;\n  t.tekatakana = 0x30c6;\n  t.tekatakanahalfwidth = 0xff83;\n  t.telephone = 0x2121;\n  t.telephoneblack = 0x260e;\n  t.telishagedolahebrew = 0x05a0;\n  t.telishaqetanahebrew = 0x05a9;\n  t.tencircle = 0x2469;\n  t.tenideographicparen = 0x3229;\n  t.tenparen = 0x247d;\n  t.tenperiod = 0x2491;\n  t.tenroman = 0x2179;\n  t.tesh = 0x02a7;\n  t.tet = 0x05d8;\n  t.tetdagesh = 0xfb38;\n  t.tetdageshhebrew = 0xfb38;\n  t.tethebrew = 0x05d8;\n  t.tetsecyrillic = 0x04b5;\n  t.tevirhebrew = 0x059b;\n  t.tevirlefthebrew = 0x059b;\n  t.thabengali = 0x09a5;\n  t.thadeva = 0x0925;\n  t.thagujarati = 0x0aa5;\n  t.thagurmukhi = 0x0a25;\n  t.thalarabic = 0x0630;\n  t.thalfinalarabic = 0xfeac;\n  t.thanthakhatlowleftthai = 0xf898;\n  t.thanthakhatlowrightthai = 0xf897;\n  t.thanthakhatthai = 0x0e4c;\n  t.thanthakhatupperleftthai = 0xf896;\n  t.theharabic = 0x062b;\n  t.thehfinalarabic = 0xfe9a;\n  t.thehinitialarabic = 0xfe9b;\n  t.thehmedialarabic = 0xfe9c;\n  t.thereexists = 0x2203;\n  t.therefore = 0x2234;\n  t.theta = 0x03b8;\n  t.theta1 = 0x03d1;\n  t.thetasymbolgreek = 0x03d1;\n  t.thieuthacirclekorean = 0x3279;\n  t.thieuthaparenkorean = 0x3219;\n  t.thieuthcirclekorean = 0x326b;\n  t.thieuthkorean = 0x314c;\n  t.thieuthparenkorean = 0x320b;\n  t.thirteencircle = 0x246c;\n  t.thirteenparen = 0x2480;\n  t.thirteenperiod = 0x2494;\n  t.thonangmonthothai = 0x0e11;\n  t.thook = 0x01ad;\n  t.thophuthaothai = 0x0e12;\n  t.thorn = 0x00fe;\n  t.thothahanthai = 0x0e17;\n  t.thothanthai = 0x0e10;\n  t.thothongthai = 0x0e18;\n  t.thothungthai = 0x0e16;\n  t.thousandcyrillic = 0x0482;\n  t.thousandsseparatorarabic = 0x066c;\n  t.thousandsseparatorpersian = 0x066c;\n  t.three = 0x0033;\n  t.threearabic = 0x0663;\n  t.threebengali = 0x09e9;\n  t.threecircle = 0x2462;\n  t.threecircleinversesansserif = 0x278c;\n  t.threedeva = 0x0969;\n  t.threeeighths = 0x215c;\n  t.threegujarati = 0x0ae9;\n  t.threegurmukhi = 0x0a69;\n  t.threehackarabic = 0x0663;\n  t.threehangzhou = 0x3023;\n  t.threeideographicparen = 0x3222;\n  t.threeinferior = 0x2083;\n  t.threemonospace = 0xff13;\n  t.threenumeratorbengali = 0x09f6;\n  t.threeoldstyle = 0xf733;\n  t.threeparen = 0x2476;\n  t.threeperiod = 0x248a;\n  t.threepersian = 0x06f3;\n  t.threequarters = 0x00be;\n  t.threequartersemdash = 0xf6de;\n  t.threeroman = 0x2172;\n  t.threesuperior = 0x00b3;\n  t.threethai = 0x0e53;\n  t.thzsquare = 0x3394;\n  t.tihiragana = 0x3061;\n  t.tikatakana = 0x30c1;\n  t.tikatakanahalfwidth = 0xff81;\n  t.tikeutacirclekorean = 0x3270;\n  t.tikeutaparenkorean = 0x3210;\n  t.tikeutcirclekorean = 0x3262;\n  t.tikeutkorean = 0x3137;\n  t.tikeutparenkorean = 0x3202;\n  t.tilde = 0x02dc;\n  t.tildebelowcmb = 0x0330;\n  t.tildecmb = 0x0303;\n  t.tildecomb = 0x0303;\n  t.tildedoublecmb = 0x0360;\n  t.tildeoperator = 0x223c;\n  t.tildeoverlaycmb = 0x0334;\n  t.tildeverticalcmb = 0x033e;\n  t.timescircle = 0x2297;\n  t.tipehahebrew = 0x0596;\n  t.tipehalefthebrew = 0x0596;\n  t.tippigurmukhi = 0x0a70;\n  t.titlocyrilliccmb = 0x0483;\n  t.tiwnarmenian = 0x057f;\n  t.tlinebelow = 0x1e6f;\n  t.tmonospace = 0xff54;\n  t.toarmenian = 0x0569;\n  t.tohiragana = 0x3068;\n  t.tokatakana = 0x30c8;\n  t.tokatakanahalfwidth = 0xff84;\n  t.tonebarextrahighmod = 0x02e5;\n  t.tonebarextralowmod = 0x02e9;\n  t.tonebarhighmod = 0x02e6;\n  t.tonebarlowmod = 0x02e8;\n  t.tonebarmidmod = 0x02e7;\n  t.tonefive = 0x01bd;\n  t.tonesix = 0x0185;\n  t.tonetwo = 0x01a8;\n  t.tonos = 0x0384;\n  t.tonsquare = 0x3327;\n  t.topatakthai = 0x0e0f;\n  t.tortoiseshellbracketleft = 0x3014;\n  t.tortoiseshellbracketleftsmall = 0xfe5d;\n  t.tortoiseshellbracketleftvertical = 0xfe39;\n  t.tortoiseshellbracketright = 0x3015;\n  t.tortoiseshellbracketrightsmall = 0xfe5e;\n  t.tortoiseshellbracketrightvertical = 0xfe3a;\n  t.totaothai = 0x0e15;\n  t.tpalatalhook = 0x01ab;\n  t.tparen = 0x24af;\n  t.trademark = 0x2122;\n  t.trademarksans = 0xf8ea;\n  t.trademarkserif = 0xf6db;\n  t.tretroflexhook = 0x0288;\n  t.triagdn = 0x25bc;\n  t.triaglf = 0x25c4;\n  t.triagrt = 0x25ba;\n  t.triagup = 0x25b2;\n  t.ts = 0x02a6;\n  t.tsadi = 0x05e6;\n  t.tsadidagesh = 0xfb46;\n  t.tsadidageshhebrew = 0xfb46;\n  t.tsadihebrew = 0x05e6;\n  t.tsecyrillic = 0x0446;\n  t.tsere = 0x05b5;\n  t.tsere12 = 0x05b5;\n  t.tsere1e = 0x05b5;\n  t.tsere2b = 0x05b5;\n  t.tserehebrew = 0x05b5;\n  t.tserenarrowhebrew = 0x05b5;\n  t.tserequarterhebrew = 0x05b5;\n  t.tserewidehebrew = 0x05b5;\n  t.tshecyrillic = 0x045b;\n  t.tsuperior = 0xf6f3;\n  t.ttabengali = 0x099f;\n  t.ttadeva = 0x091f;\n  t.ttagujarati = 0x0a9f;\n  t.ttagurmukhi = 0x0a1f;\n  t.tteharabic = 0x0679;\n  t.ttehfinalarabic = 0xfb67;\n  t.ttehinitialarabic = 0xfb68;\n  t.ttehmedialarabic = 0xfb69;\n  t.tthabengali = 0x09a0;\n  t.tthadeva = 0x0920;\n  t.tthagujarati = 0x0aa0;\n  t.tthagurmukhi = 0x0a20;\n  t.tturned = 0x0287;\n  t.tuhiragana = 0x3064;\n  t.tukatakana = 0x30c4;\n  t.tukatakanahalfwidth = 0xff82;\n  t.tusmallhiragana = 0x3063;\n  t.tusmallkatakana = 0x30c3;\n  t.tusmallkatakanahalfwidth = 0xff6f;\n  t.twelvecircle = 0x246b;\n  t.twelveparen = 0x247f;\n  t.twelveperiod = 0x2493;\n  t.twelveroman = 0x217b;\n  t.twentycircle = 0x2473;\n  t.twentyhangzhou = 0x5344;\n  t.twentyparen = 0x2487;\n  t.twentyperiod = 0x249b;\n  t.two = 0x0032;\n  t.twoarabic = 0x0662;\n  t.twobengali = 0x09e8;\n  t.twocircle = 0x2461;\n  t.twocircleinversesansserif = 0x278b;\n  t.twodeva = 0x0968;\n  t.twodotenleader = 0x2025;\n  t.twodotleader = 0x2025;\n  t.twodotleadervertical = 0xfe30;\n  t.twogujarati = 0x0ae8;\n  t.twogurmukhi = 0x0a68;\n  t.twohackarabic = 0x0662;\n  t.twohangzhou = 0x3022;\n  t.twoideographicparen = 0x3221;\n  t.twoinferior = 0x2082;\n  t.twomonospace = 0xff12;\n  t.twonumeratorbengali = 0x09f5;\n  t.twooldstyle = 0xf732;\n  t.twoparen = 0x2475;\n  t.twoperiod = 0x2489;\n  t.twopersian = 0x06f2;\n  t.tworoman = 0x2171;\n  t.twostroke = 0x01bb;\n  t.twosuperior = 0x00b2;\n  t.twothai = 0x0e52;\n  t.twothirds = 0x2154;\n  t.u = 0x0075;\n  t.uacute = 0x00fa;\n  t.ubar = 0x0289;\n  t.ubengali = 0x0989;\n  t.ubopomofo = 0x3128;\n  t.ubreve = 0x016d;\n  t.ucaron = 0x01d4;\n  t.ucircle = 0x24e4;\n  t.ucircumflex = 0x00fb;\n  t.ucircumflexbelow = 0x1e77;\n  t.ucyrillic = 0x0443;\n  t.udattadeva = 0x0951;\n  t.udblacute = 0x0171;\n  t.udblgrave = 0x0215;\n  t.udeva = 0x0909;\n  t.udieresis = 0x00fc;\n  t.udieresisacute = 0x01d8;\n  t.udieresisbelow = 0x1e73;\n  t.udieresiscaron = 0x01da;\n  t.udieresiscyrillic = 0x04f1;\n  t.udieresisgrave = 0x01dc;\n  t.udieresismacron = 0x01d6;\n  t.udotbelow = 0x1ee5;\n  t.ugrave = 0x00f9;\n  t.ugujarati = 0x0a89;\n  t.ugurmukhi = 0x0a09;\n  t.uhiragana = 0x3046;\n  t.uhookabove = 0x1ee7;\n  t.uhorn = 0x01b0;\n  t.uhornacute = 0x1ee9;\n  t.uhorndotbelow = 0x1ef1;\n  t.uhorngrave = 0x1eeb;\n  t.uhornhookabove = 0x1eed;\n  t.uhorntilde = 0x1eef;\n  t.uhungarumlaut = 0x0171;\n  t.uhungarumlautcyrillic = 0x04f3;\n  t.uinvertedbreve = 0x0217;\n  t.ukatakana = 0x30a6;\n  t.ukatakanahalfwidth = 0xff73;\n  t.ukcyrillic = 0x0479;\n  t.ukorean = 0x315c;\n  t.umacron = 0x016b;\n  t.umacroncyrillic = 0x04ef;\n  t.umacrondieresis = 0x1e7b;\n  t.umatragurmukhi = 0x0a41;\n  t.umonospace = 0xff55;\n  t.underscore = 0x005f;\n  t.underscoredbl = 0x2017;\n  t.underscoremonospace = 0xff3f;\n  t.underscorevertical = 0xfe33;\n  t.underscorewavy = 0xfe4f;\n  t.union = 0x222a;\n  t.universal = 0x2200;\n  t.uogonek = 0x0173;\n  t.uparen = 0x24b0;\n  t.upblock = 0x2580;\n  t.upperdothebrew = 0x05c4;\n  t.upsilon = 0x03c5;\n  t.upsilondieresis = 0x03cb;\n  t.upsilondieresistonos = 0x03b0;\n  t.upsilonlatin = 0x028a;\n  t.upsilontonos = 0x03cd;\n  t.uptackbelowcmb = 0x031d;\n  t.uptackmod = 0x02d4;\n  t.uragurmukhi = 0x0a73;\n  t.uring = 0x016f;\n  t.ushortcyrillic = 0x045e;\n  t.usmallhiragana = 0x3045;\n  t.usmallkatakana = 0x30a5;\n  t.usmallkatakanahalfwidth = 0xff69;\n  t.ustraightcyrillic = 0x04af;\n  t.ustraightstrokecyrillic = 0x04b1;\n  t.utilde = 0x0169;\n  t.utildeacute = 0x1e79;\n  t.utildebelow = 0x1e75;\n  t.uubengali = 0x098a;\n  t.uudeva = 0x090a;\n  t.uugujarati = 0x0a8a;\n  t.uugurmukhi = 0x0a0a;\n  t.uumatragurmukhi = 0x0a42;\n  t.uuvowelsignbengali = 0x09c2;\n  t.uuvowelsigndeva = 0x0942;\n  t.uuvowelsigngujarati = 0x0ac2;\n  t.uvowelsignbengali = 0x09c1;\n  t.uvowelsigndeva = 0x0941;\n  t.uvowelsigngujarati = 0x0ac1;\n  t.v = 0x0076;\n  t.vadeva = 0x0935;\n  t.vagujarati = 0x0ab5;\n  t.vagurmukhi = 0x0a35;\n  t.vakatakana = 0x30f7;\n  t.vav = 0x05d5;\n  t.vavdagesh = 0xfb35;\n  t.vavdagesh65 = 0xfb35;\n  t.vavdageshhebrew = 0xfb35;\n  t.vavhebrew = 0x05d5;\n  t.vavholam = 0xfb4b;\n  t.vavholamhebrew = 0xfb4b;\n  t.vavvavhebrew = 0x05f0;\n  t.vavyodhebrew = 0x05f1;\n  t.vcircle = 0x24e5;\n  t.vdotbelow = 0x1e7f;\n  t.vecyrillic = 0x0432;\n  t.veharabic = 0x06a4;\n  t.vehfinalarabic = 0xfb6b;\n  t.vehinitialarabic = 0xfb6c;\n  t.vehmedialarabic = 0xfb6d;\n  t.vekatakana = 0x30f9;\n  t.venus = 0x2640;\n  t.verticalbar = 0x007c;\n  t.verticallineabovecmb = 0x030d;\n  t.verticallinebelowcmb = 0x0329;\n  t.verticallinelowmod = 0x02cc;\n  t.verticallinemod = 0x02c8;\n  t.vewarmenian = 0x057e;\n  t.vhook = 0x028b;\n  t.vikatakana = 0x30f8;\n  t.viramabengali = 0x09cd;\n  t.viramadeva = 0x094d;\n  t.viramagujarati = 0x0acd;\n  t.visargabengali = 0x0983;\n  t.visargadeva = 0x0903;\n  t.visargagujarati = 0x0a83;\n  t.vmonospace = 0xff56;\n  t.voarmenian = 0x0578;\n  t.voicediterationhiragana = 0x309e;\n  t.voicediterationkatakana = 0x30fe;\n  t.voicedmarkkana = 0x309b;\n  t.voicedmarkkanahalfwidth = 0xff9e;\n  t.vokatakana = 0x30fa;\n  t.vparen = 0x24b1;\n  t.vtilde = 0x1e7d;\n  t.vturned = 0x028c;\n  t.vuhiragana = 0x3094;\n  t.vukatakana = 0x30f4;\n  t.w = 0x0077;\n  t.wacute = 0x1e83;\n  t.waekorean = 0x3159;\n  t.wahiragana = 0x308f;\n  t.wakatakana = 0x30ef;\n  t.wakatakanahalfwidth = 0xff9c;\n  t.wakorean = 0x3158;\n  t.wasmallhiragana = 0x308e;\n  t.wasmallkatakana = 0x30ee;\n  t.wattosquare = 0x3357;\n  t.wavedash = 0x301c;\n  t.wavyunderscorevertical = 0xfe34;\n  t.wawarabic = 0x0648;\n  t.wawfinalarabic = 0xfeee;\n  t.wawhamzaabovearabic = 0x0624;\n  t.wawhamzaabovefinalarabic = 0xfe86;\n  t.wbsquare = 0x33dd;\n  t.wcircle = 0x24e6;\n  t.wcircumflex = 0x0175;\n  t.wdieresis = 0x1e85;\n  t.wdotaccent = 0x1e87;\n  t.wdotbelow = 0x1e89;\n  t.wehiragana = 0x3091;\n  t.weierstrass = 0x2118;\n  t.wekatakana = 0x30f1;\n  t.wekorean = 0x315e;\n  t.weokorean = 0x315d;\n  t.wgrave = 0x1e81;\n  t.whitebullet = 0x25e6;\n  t.whitecircle = 0x25cb;\n  t.whitecircleinverse = 0x25d9;\n  t.whitecornerbracketleft = 0x300e;\n  t.whitecornerbracketleftvertical = 0xfe43;\n  t.whitecornerbracketright = 0x300f;\n  t.whitecornerbracketrightvertical = 0xfe44;\n  t.whitediamond = 0x25c7;\n  t.whitediamondcontainingblacksmalldiamond = 0x25c8;\n  t.whitedownpointingsmalltriangle = 0x25bf;\n  t.whitedownpointingtriangle = 0x25bd;\n  t.whiteleftpointingsmalltriangle = 0x25c3;\n  t.whiteleftpointingtriangle = 0x25c1;\n  t.whitelenticularbracketleft = 0x3016;\n  t.whitelenticularbracketright = 0x3017;\n  t.whiterightpointingsmalltriangle = 0x25b9;\n  t.whiterightpointingtriangle = 0x25b7;\n  t.whitesmallsquare = 0x25ab;\n  t.whitesmilingface = 0x263a;\n  t.whitesquare = 0x25a1;\n  t.whitestar = 0x2606;\n  t.whitetelephone = 0x260f;\n  t.whitetortoiseshellbracketleft = 0x3018;\n  t.whitetortoiseshellbracketright = 0x3019;\n  t.whiteuppointingsmalltriangle = 0x25b5;\n  t.whiteuppointingtriangle = 0x25b3;\n  t.wihiragana = 0x3090;\n  t.wikatakana = 0x30f0;\n  t.wikorean = 0x315f;\n  t.wmonospace = 0xff57;\n  t.wohiragana = 0x3092;\n  t.wokatakana = 0x30f2;\n  t.wokatakanahalfwidth = 0xff66;\n  t.won = 0x20a9;\n  t.wonmonospace = 0xffe6;\n  t.wowaenthai = 0x0e27;\n  t.wparen = 0x24b2;\n  t.wring = 0x1e98;\n  t.wsuperior = 0x02b7;\n  t.wturned = 0x028d;\n  t.wynn = 0x01bf;\n  t.x = 0x0078;\n  t.xabovecmb = 0x033d;\n  t.xbopomofo = 0x3112;\n  t.xcircle = 0x24e7;\n  t.xdieresis = 0x1e8d;\n  t.xdotaccent = 0x1e8b;\n  t.xeharmenian = 0x056d;\n  t.xi = 0x03be;\n  t.xmonospace = 0xff58;\n  t.xparen = 0x24b3;\n  t.xsuperior = 0x02e3;\n  t.y = 0x0079;\n  t.yaadosquare = 0x334e;\n  t.yabengali = 0x09af;\n  t.yacute = 0x00fd;\n  t.yadeva = 0x092f;\n  t.yaekorean = 0x3152;\n  t.yagujarati = 0x0aaf;\n  t.yagurmukhi = 0x0a2f;\n  t.yahiragana = 0x3084;\n  t.yakatakana = 0x30e4;\n  t.yakatakanahalfwidth = 0xff94;\n  t.yakorean = 0x3151;\n  t.yamakkanthai = 0x0e4e;\n  t.yasmallhiragana = 0x3083;\n  t.yasmallkatakana = 0x30e3;\n  t.yasmallkatakanahalfwidth = 0xff6c;\n  t.yatcyrillic = 0x0463;\n  t.ycircle = 0x24e8;\n  t.ycircumflex = 0x0177;\n  t.ydieresis = 0x00ff;\n  t.ydotaccent = 0x1e8f;\n  t.ydotbelow = 0x1ef5;\n  t.yeharabic = 0x064a;\n  t.yehbarreearabic = 0x06d2;\n  t.yehbarreefinalarabic = 0xfbaf;\n  t.yehfinalarabic = 0xfef2;\n  t.yehhamzaabovearabic = 0x0626;\n  t.yehhamzaabovefinalarabic = 0xfe8a;\n  t.yehhamzaaboveinitialarabic = 0xfe8b;\n  t.yehhamzaabovemedialarabic = 0xfe8c;\n  t.yehinitialarabic = 0xfef3;\n  t.yehmedialarabic = 0xfef4;\n  t.yehmeeminitialarabic = 0xfcdd;\n  t.yehmeemisolatedarabic = 0xfc58;\n  t.yehnoonfinalarabic = 0xfc94;\n  t.yehthreedotsbelowarabic = 0x06d1;\n  t.yekorean = 0x3156;\n  t.yen = 0x00a5;\n  t.yenmonospace = 0xffe5;\n  t.yeokorean = 0x3155;\n  t.yeorinhieuhkorean = 0x3186;\n  t.yerahbenyomohebrew = 0x05aa;\n  t.yerahbenyomolefthebrew = 0x05aa;\n  t.yericyrillic = 0x044b;\n  t.yerudieresiscyrillic = 0x04f9;\n  t.yesieungkorean = 0x3181;\n  t.yesieungpansioskorean = 0x3183;\n  t.yesieungsioskorean = 0x3182;\n  t.yetivhebrew = 0x059a;\n  t.ygrave = 0x1ef3;\n  t.yhook = 0x01b4;\n  t.yhookabove = 0x1ef7;\n  t.yiarmenian = 0x0575;\n  t.yicyrillic = 0x0457;\n  t.yikorean = 0x3162;\n  t.yinyang = 0x262f;\n  t.yiwnarmenian = 0x0582;\n  t.ymonospace = 0xff59;\n  t.yod = 0x05d9;\n  t.yoddagesh = 0xfb39;\n  t.yoddageshhebrew = 0xfb39;\n  t.yodhebrew = 0x05d9;\n  t.yodyodhebrew = 0x05f2;\n  t.yodyodpatahhebrew = 0xfb1f;\n  t.yohiragana = 0x3088;\n  t.yoikorean = 0x3189;\n  t.yokatakana = 0x30e8;\n  t.yokatakanahalfwidth = 0xff96;\n  t.yokorean = 0x315b;\n  t.yosmallhiragana = 0x3087;\n  t.yosmallkatakana = 0x30e7;\n  t.yosmallkatakanahalfwidth = 0xff6e;\n  t.yotgreek = 0x03f3;\n  t.yoyaekorean = 0x3188;\n  t.yoyakorean = 0x3187;\n  t.yoyakthai = 0x0e22;\n  t.yoyingthai = 0x0e0d;\n  t.yparen = 0x24b4;\n  t.ypogegrammeni = 0x037a;\n  t.ypogegrammenigreekcmb = 0x0345;\n  t.yr = 0x01a6;\n  t.yring = 0x1e99;\n  t.ysuperior = 0x02b8;\n  t.ytilde = 0x1ef9;\n  t.yturned = 0x028e;\n  t.yuhiragana = 0x3086;\n  t.yuikorean = 0x318c;\n  t.yukatakana = 0x30e6;\n  t.yukatakanahalfwidth = 0xff95;\n  t.yukorean = 0x3160;\n  t.yusbigcyrillic = 0x046b;\n  t.yusbigiotifiedcyrillic = 0x046d;\n  t.yuslittlecyrillic = 0x0467;\n  t.yuslittleiotifiedcyrillic = 0x0469;\n  t.yusmallhiragana = 0x3085;\n  t.yusmallkatakana = 0x30e5;\n  t.yusmallkatakanahalfwidth = 0xff6d;\n  t.yuyekorean = 0x318b;\n  t.yuyeokorean = 0x318a;\n  t.yyabengali = 0x09df;\n  t.yyadeva = 0x095f;\n  t.z = 0x007a;\n  t.zaarmenian = 0x0566;\n  t.zacute = 0x017a;\n  t.zadeva = 0x095b;\n  t.zagurmukhi = 0x0a5b;\n  t.zaharabic = 0x0638;\n  t.zahfinalarabic = 0xfec6;\n  t.zahinitialarabic = 0xfec7;\n  t.zahiragana = 0x3056;\n  t.zahmedialarabic = 0xfec8;\n  t.zainarabic = 0x0632;\n  t.zainfinalarabic = 0xfeb0;\n  t.zakatakana = 0x30b6;\n  t.zaqefgadolhebrew = 0x0595;\n  t.zaqefqatanhebrew = 0x0594;\n  t.zarqahebrew = 0x0598;\n  t.zayin = 0x05d6;\n  t.zayindagesh = 0xfb36;\n  t.zayindageshhebrew = 0xfb36;\n  t.zayinhebrew = 0x05d6;\n  t.zbopomofo = 0x3117;\n  t.zcaron = 0x017e;\n  t.zcircle = 0x24e9;\n  t.zcircumflex = 0x1e91;\n  t.zcurl = 0x0291;\n  t.zdot = 0x017c;\n  t.zdotaccent = 0x017c;\n  t.zdotbelow = 0x1e93;\n  t.zecyrillic = 0x0437;\n  t.zedescendercyrillic = 0x0499;\n  t.zedieresiscyrillic = 0x04df;\n  t.zehiragana = 0x305c;\n  t.zekatakana = 0x30bc;\n  t.zero = 0x0030;\n  t.zeroarabic = 0x0660;\n  t.zerobengali = 0x09e6;\n  t.zerodeva = 0x0966;\n  t.zerogujarati = 0x0ae6;\n  t.zerogurmukhi = 0x0a66;\n  t.zerohackarabic = 0x0660;\n  t.zeroinferior = 0x2080;\n  t.zeromonospace = 0xff10;\n  t.zerooldstyle = 0xf730;\n  t.zeropersian = 0x06f0;\n  t.zerosuperior = 0x2070;\n  t.zerothai = 0x0e50;\n  t.zerowidthjoiner = 0xfeff;\n  t.zerowidthnonjoiner = 0x200c;\n  t.zerowidthspace = 0x200b;\n  t.zeta = 0x03b6;\n  t.zhbopomofo = 0x3113;\n  t.zhearmenian = 0x056a;\n  t.zhebrevecyrillic = 0x04c2;\n  t.zhecyrillic = 0x0436;\n  t.zhedescendercyrillic = 0x0497;\n  t.zhedieresiscyrillic = 0x04dd;\n  t.zihiragana = 0x3058;\n  t.zikatakana = 0x30b8;\n  t.zinorhebrew = 0x05ae;\n  t.zlinebelow = 0x1e95;\n  t.zmonospace = 0xff5a;\n  t.zohiragana = 0x305e;\n  t.zokatakana = 0x30be;\n  t.zparen = 0x24b5;\n  t.zretroflexhook = 0x0290;\n  t.zstroke = 0x01b6;\n  t.zuhiragana = 0x305a;\n  t.zukatakana = 0x30ba;\n  t[\".notdef\"] = 0x0000;\n  t.angbracketleftbig = 0x2329;\n  t.angbracketleftBig = 0x2329;\n  t.angbracketleftbigg = 0x2329;\n  t.angbracketleftBigg = 0x2329;\n  t.angbracketrightBig = 0x232a;\n  t.angbracketrightbig = 0x232a;\n  t.angbracketrightBigg = 0x232a;\n  t.angbracketrightbigg = 0x232a;\n  t.arrowhookleft = 0x21aa;\n  t.arrowhookright = 0x21a9;\n  t.arrowlefttophalf = 0x21bc;\n  t.arrowleftbothalf = 0x21bd;\n  t.arrownortheast = 0x2197;\n  t.arrownorthwest = 0x2196;\n  t.arrowrighttophalf = 0x21c0;\n  t.arrowrightbothalf = 0x21c1;\n  t.arrowsoutheast = 0x2198;\n  t.arrowsouthwest = 0x2199;\n  t.backslashbig = 0x2216;\n  t.backslashBig = 0x2216;\n  t.backslashBigg = 0x2216;\n  t.backslashbigg = 0x2216;\n  t.bardbl = 0x2016;\n  t.bracehtipdownleft = 0xfe37;\n  t.bracehtipdownright = 0xfe37;\n  t.bracehtipupleft = 0xfe38;\n  t.bracehtipupright = 0xfe38;\n  t.braceleftBig = 0x007b;\n  t.braceleftbig = 0x007b;\n  t.braceleftbigg = 0x007b;\n  t.braceleftBigg = 0x007b;\n  t.bracerightBig = 0x007d;\n  t.bracerightbig = 0x007d;\n  t.bracerightbigg = 0x007d;\n  t.bracerightBigg = 0x007d;\n  t.bracketleftbig = 0x005b;\n  t.bracketleftBig = 0x005b;\n  t.bracketleftbigg = 0x005b;\n  t.bracketleftBigg = 0x005b;\n  t.bracketrightBig = 0x005d;\n  t.bracketrightbig = 0x005d;\n  t.bracketrightbigg = 0x005d;\n  t.bracketrightBigg = 0x005d;\n  t.ceilingleftbig = 0x2308;\n  t.ceilingleftBig = 0x2308;\n  t.ceilingleftBigg = 0x2308;\n  t.ceilingleftbigg = 0x2308;\n  t.ceilingrightbig = 0x2309;\n  t.ceilingrightBig = 0x2309;\n  t.ceilingrightbigg = 0x2309;\n  t.ceilingrightBigg = 0x2309;\n  t.circledotdisplay = 0x2299;\n  t.circledottext = 0x2299;\n  t.circlemultiplydisplay = 0x2297;\n  t.circlemultiplytext = 0x2297;\n  t.circleplusdisplay = 0x2295;\n  t.circleplustext = 0x2295;\n  t.contintegraldisplay = 0x222e;\n  t.contintegraltext = 0x222e;\n  t.coproductdisplay = 0x2210;\n  t.coproducttext = 0x2210;\n  t.floorleftBig = 0x230a;\n  t.floorleftbig = 0x230a;\n  t.floorleftbigg = 0x230a;\n  t.floorleftBigg = 0x230a;\n  t.floorrightbig = 0x230b;\n  t.floorrightBig = 0x230b;\n  t.floorrightBigg = 0x230b;\n  t.floorrightbigg = 0x230b;\n  t.hatwide = 0x0302;\n  t.hatwider = 0x0302;\n  t.hatwidest = 0x0302;\n  t.intercal = 0x1d40;\n  t.integraldisplay = 0x222b;\n  t.integraltext = 0x222b;\n  t.intersectiondisplay = 0x22c2;\n  t.intersectiontext = 0x22c2;\n  t.logicalanddisplay = 0x2227;\n  t.logicalandtext = 0x2227;\n  t.logicalordisplay = 0x2228;\n  t.logicalortext = 0x2228;\n  t.parenleftBig = 0x0028;\n  t.parenleftbig = 0x0028;\n  t.parenleftBigg = 0x0028;\n  t.parenleftbigg = 0x0028;\n  t.parenrightBig = 0x0029;\n  t.parenrightbig = 0x0029;\n  t.parenrightBigg = 0x0029;\n  t.parenrightbigg = 0x0029;\n  t.prime = 0x2032;\n  t.productdisplay = 0x220f;\n  t.producttext = 0x220f;\n  t.radicalbig = 0x221a;\n  t.radicalBig = 0x221a;\n  t.radicalBigg = 0x221a;\n  t.radicalbigg = 0x221a;\n  t.radicalbt = 0x221a;\n  t.radicaltp = 0x221a;\n  t.radicalvertex = 0x221a;\n  t.slashbig = 0x002f;\n  t.slashBig = 0x002f;\n  t.slashBigg = 0x002f;\n  t.slashbigg = 0x002f;\n  t.summationdisplay = 0x2211;\n  t.summationtext = 0x2211;\n  t.tildewide = 0x02dc;\n  t.tildewider = 0x02dc;\n  t.tildewidest = 0x02dc;\n  t.uniondisplay = 0x22c3;\n  t.unionmultidisplay = 0x228e;\n  t.unionmultitext = 0x228e;\n  t.unionsqdisplay = 0x2294;\n  t.unionsqtext = 0x2294;\n  t.uniontext = 0x22c3;\n  t.vextenddouble = 0x2225;\n  t.vextendsingle = 0x2223;\n});\nconst getDingbatsGlyphsUnicode = getLookupTableFactory(function (t) {\n  t.space = 0x0020;\n  t.a1 = 0x2701;\n  t.a2 = 0x2702;\n  t.a202 = 0x2703;\n  t.a3 = 0x2704;\n  t.a4 = 0x260e;\n  t.a5 = 0x2706;\n  t.a119 = 0x2707;\n  t.a118 = 0x2708;\n  t.a117 = 0x2709;\n  t.a11 = 0x261b;\n  t.a12 = 0x261e;\n  t.a13 = 0x270c;\n  t.a14 = 0x270d;\n  t.a15 = 0x270e;\n  t.a16 = 0x270f;\n  t.a105 = 0x2710;\n  t.a17 = 0x2711;\n  t.a18 = 0x2712;\n  t.a19 = 0x2713;\n  t.a20 = 0x2714;\n  t.a21 = 0x2715;\n  t.a22 = 0x2716;\n  t.a23 = 0x2717;\n  t.a24 = 0x2718;\n  t.a25 = 0x2719;\n  t.a26 = 0x271a;\n  t.a27 = 0x271b;\n  t.a28 = 0x271c;\n  t.a6 = 0x271d;\n  t.a7 = 0x271e;\n  t.a8 = 0x271f;\n  t.a9 = 0x2720;\n  t.a10 = 0x2721;\n  t.a29 = 0x2722;\n  t.a30 = 0x2723;\n  t.a31 = 0x2724;\n  t.a32 = 0x2725;\n  t.a33 = 0x2726;\n  t.a34 = 0x2727;\n  t.a35 = 0x2605;\n  t.a36 = 0x2729;\n  t.a37 = 0x272a;\n  t.a38 = 0x272b;\n  t.a39 = 0x272c;\n  t.a40 = 0x272d;\n  t.a41 = 0x272e;\n  t.a42 = 0x272f;\n  t.a43 = 0x2730;\n  t.a44 = 0x2731;\n  t.a45 = 0x2732;\n  t.a46 = 0x2733;\n  t.a47 = 0x2734;\n  t.a48 = 0x2735;\n  t.a49 = 0x2736;\n  t.a50 = 0x2737;\n  t.a51 = 0x2738;\n  t.a52 = 0x2739;\n  t.a53 = 0x273a;\n  t.a54 = 0x273b;\n  t.a55 = 0x273c;\n  t.a56 = 0x273d;\n  t.a57 = 0x273e;\n  t.a58 = 0x273f;\n  t.a59 = 0x2740;\n  t.a60 = 0x2741;\n  t.a61 = 0x2742;\n  t.a62 = 0x2743;\n  t.a63 = 0x2744;\n  t.a64 = 0x2745;\n  t.a65 = 0x2746;\n  t.a66 = 0x2747;\n  t.a67 = 0x2748;\n  t.a68 = 0x2749;\n  t.a69 = 0x274a;\n  t.a70 = 0x274b;\n  t.a71 = 0x25cf;\n  t.a72 = 0x274d;\n  t.a73 = 0x25a0;\n  t.a74 = 0x274f;\n  t.a203 = 0x2750;\n  t.a75 = 0x2751;\n  t.a204 = 0x2752;\n  t.a76 = 0x25b2;\n  t.a77 = 0x25bc;\n  t.a78 = 0x25c6;\n  t.a79 = 0x2756;\n  t.a81 = 0x25d7;\n  t.a82 = 0x2758;\n  t.a83 = 0x2759;\n  t.a84 = 0x275a;\n  t.a97 = 0x275b;\n  t.a98 = 0x275c;\n  t.a99 = 0x275d;\n  t.a100 = 0x275e;\n  t.a101 = 0x2761;\n  t.a102 = 0x2762;\n  t.a103 = 0x2763;\n  t.a104 = 0x2764;\n  t.a106 = 0x2765;\n  t.a107 = 0x2766;\n  t.a108 = 0x2767;\n  t.a112 = 0x2663;\n  t.a111 = 0x2666;\n  t.a110 = 0x2665;\n  t.a109 = 0x2660;\n  t.a120 = 0x2460;\n  t.a121 = 0x2461;\n  t.a122 = 0x2462;\n  t.a123 = 0x2463;\n  t.a124 = 0x2464;\n  t.a125 = 0x2465;\n  t.a126 = 0x2466;\n  t.a127 = 0x2467;\n  t.a128 = 0x2468;\n  t.a129 = 0x2469;\n  t.a130 = 0x2776;\n  t.a131 = 0x2777;\n  t.a132 = 0x2778;\n  t.a133 = 0x2779;\n  t.a134 = 0x277a;\n  t.a135 = 0x277b;\n  t.a136 = 0x277c;\n  t.a137 = 0x277d;\n  t.a138 = 0x277e;\n  t.a139 = 0x277f;\n  t.a140 = 0x2780;\n  t.a141 = 0x2781;\n  t.a142 = 0x2782;\n  t.a143 = 0x2783;\n  t.a144 = 0x2784;\n  t.a145 = 0x2785;\n  t.a146 = 0x2786;\n  t.a147 = 0x2787;\n  t.a148 = 0x2788;\n  t.a149 = 0x2789;\n  t.a150 = 0x278a;\n  t.a151 = 0x278b;\n  t.a152 = 0x278c;\n  t.a153 = 0x278d;\n  t.a154 = 0x278e;\n  t.a155 = 0x278f;\n  t.a156 = 0x2790;\n  t.a157 = 0x2791;\n  t.a158 = 0x2792;\n  t.a159 = 0x2793;\n  t.a160 = 0x2794;\n  t.a161 = 0x2192;\n  t.a163 = 0x2194;\n  t.a164 = 0x2195;\n  t.a196 = 0x2798;\n  t.a165 = 0x2799;\n  t.a192 = 0x279a;\n  t.a166 = 0x279b;\n  t.a167 = 0x279c;\n  t.a168 = 0x279d;\n  t.a169 = 0x279e;\n  t.a170 = 0x279f;\n  t.a171 = 0x27a0;\n  t.a172 = 0x27a1;\n  t.a173 = 0x27a2;\n  t.a162 = 0x27a3;\n  t.a174 = 0x27a4;\n  t.a175 = 0x27a5;\n  t.a176 = 0x27a6;\n  t.a177 = 0x27a7;\n  t.a178 = 0x27a8;\n  t.a179 = 0x27a9;\n  t.a193 = 0x27aa;\n  t.a180 = 0x27ab;\n  t.a199 = 0x27ac;\n  t.a181 = 0x27ad;\n  t.a200 = 0x27ae;\n  t.a182 = 0x27af;\n  t.a201 = 0x27b1;\n  t.a183 = 0x27b2;\n  t.a184 = 0x27b3;\n  t.a197 = 0x27b4;\n  t.a185 = 0x27b5;\n  t.a194 = 0x27b6;\n  t.a198 = 0x27b7;\n  t.a186 = 0x27b8;\n  t.a195 = 0x27b9;\n  t.a187 = 0x27ba;\n  t.a188 = 0x27bb;\n  t.a189 = 0x27bc;\n  t.a190 = 0x27bd;\n  t.a191 = 0x27be;\n  t.a89 = 0x2768;\n  t.a90 = 0x2769;\n  t.a93 = 0x276a;\n  t.a94 = 0x276b;\n  t.a91 = 0x276c;\n  t.a92 = 0x276d;\n  t.a205 = 0x276e;\n  t.a85 = 0x276f;\n  t.a206 = 0x2770;\n  t.a86 = 0x2771;\n  t.a87 = 0x2772;\n  t.a88 = 0x2773;\n  t.a95 = 0x2774;\n  t.a96 = 0x2775;\n  t[\".notdef\"] = 0x0000;\n});\n\n;// CONCATENATED MODULE: ./src/core/unicode.js\n\nconst getSpecialPUASymbols = getLookupTableFactory(function (t) {\n  t[63721] = 0x00a9;\n  t[63193] = 0x00a9;\n  t[63720] = 0x00ae;\n  t[63194] = 0x00ae;\n  t[63722] = 0x2122;\n  t[63195] = 0x2122;\n  t[63729] = 0x23a7;\n  t[63730] = 0x23a8;\n  t[63731] = 0x23a9;\n  t[63740] = 0x23ab;\n  t[63741] = 0x23ac;\n  t[63742] = 0x23ad;\n  t[63726] = 0x23a1;\n  t[63727] = 0x23a2;\n  t[63728] = 0x23a3;\n  t[63737] = 0x23a4;\n  t[63738] = 0x23a5;\n  t[63739] = 0x23a6;\n  t[63723] = 0x239b;\n  t[63724] = 0x239c;\n  t[63725] = 0x239d;\n  t[63734] = 0x239e;\n  t[63735] = 0x239f;\n  t[63736] = 0x23a0;\n});\nfunction mapSpecialUnicodeValues(code) {\n  if (code >= 0xfff0 && code <= 0xffff) {\n    return 0;\n  } else if (code >= 0xf600 && code <= 0xf8ff) {\n    return getSpecialPUASymbols()[code] || code;\n  } else if (code === 0x00ad) {\n    return 0x002d;\n  }\n  return code;\n}\nfunction getUnicodeForGlyph(name, glyphsUnicodeMap) {\n  let unicode = glyphsUnicodeMap[name];\n  if (unicode !== undefined) {\n    return unicode;\n  }\n  if (!name) {\n    return -1;\n  }\n  if (name[0] === \"u\") {\n    const nameLen = name.length;\n    let hexStr;\n    if (nameLen === 7 && name[1] === \"n\" && name[2] === \"i\") {\n      hexStr = name.substring(3);\n    } else if (nameLen >= 5 && nameLen <= 7) {\n      hexStr = name.substring(1);\n    } else {\n      return -1;\n    }\n    if (hexStr === hexStr.toUpperCase()) {\n      unicode = parseInt(hexStr, 16);\n      if (unicode >= 0) {\n        return unicode;\n      }\n    }\n  }\n  return -1;\n}\nconst UnicodeRanges = [[0x0000, 0x007f], [0x0080, 0x00ff], [0x0100, 0x017f], [0x0180, 0x024f], [0x0250, 0x02af, 0x1d00, 0x1d7f, 0x1d80, 0x1dbf], [0x02b0, 0x02ff, 0xa700, 0xa71f], [0x0300, 0x036f, 0x1dc0, 0x1dff], [0x0370, 0x03ff], [0x2c80, 0x2cff], [0x0400, 0x04ff, 0x0500, 0x052f, 0x2de0, 0x2dff, 0xa640, 0xa69f], [0x0530, 0x058f], [0x0590, 0x05ff], [0xa500, 0xa63f], [0x0600, 0x06ff, 0x0750, 0x077f], [0x07c0, 0x07ff], [0x0900, 0x097f], [0x0980, 0x09ff], [0x0a00, 0x0a7f], [0x0a80, 0x0aff], [0x0b00, 0x0b7f], [0x0b80, 0x0bff], [0x0c00, 0x0c7f], [0x0c80, 0x0cff], [0x0d00, 0x0d7f], [0x0e00, 0x0e7f], [0x0e80, 0x0eff], [0x10a0, 0x10ff, 0x2d00, 0x2d2f], [0x1b00, 0x1b7f], [0x1100, 0x11ff], [0x1e00, 0x1eff, 0x2c60, 0x2c7f, 0xa720, 0xa7ff], [0x1f00, 0x1fff], [0x2000, 0x206f, 0x2e00, 0x2e7f], [0x2070, 0x209f], [0x20a0, 0x20cf], [0x20d0, 0x20ff], [0x2100, 0x214f], [0x2150, 0x218f], [0x2190, 0x21ff, 0x27f0, 0x27ff, 0x2900, 0x297f, 0x2b00, 0x2bff], [0x2200, 0x22ff, 0x2a00, 0x2aff, 0x27c0, 0x27ef, 0x2980, 0x29ff], [0x2300, 0x23ff], [0x2400, 0x243f], [0x2440, 0x245f], [0x2460, 0x24ff], [0x2500, 0x257f], [0x2580, 0x259f], [0x25a0, 0x25ff], [0x2600, 0x26ff], [0x2700, 0x27bf], [0x3000, 0x303f], [0x3040, 0x309f], [0x30a0, 0x30ff, 0x31f0, 0x31ff], [0x3100, 0x312f, 0x31a0, 0x31bf], [0x3130, 0x318f], [0xa840, 0xa87f], [0x3200, 0x32ff], [0x3300, 0x33ff], [0xac00, 0xd7af], [0xd800, 0xdfff], [0x10900, 0x1091f], [0x4e00, 0x9fff, 0x2e80, 0x2eff, 0x2f00, 0x2fdf, 0x2ff0, 0x2fff, 0x3400, 0x4dbf, 0x20000, 0x2a6df, 0x3190, 0x319f], [0xe000, 0xf8ff], [0x31c0, 0x31ef, 0xf900, 0xfaff, 0x2f800, 0x2fa1f], [0xfb00, 0xfb4f], [0xfb50, 0xfdff], [0xfe20, 0xfe2f], [0xfe10, 0xfe1f], [0xfe50, 0xfe6f], [0xfe70, 0xfeff], [0xff00, 0xffef], [0xfff0, 0xffff], [0x0f00, 0x0fff], [0x0700, 0x074f], [0x0780, 0x07bf], [0x0d80, 0x0dff], [0x1000, 0x109f], [0x1200, 0x137f, 0x1380, 0x139f, 0x2d80, 0x2ddf], [0x13a0, 0x13ff], [0x1400, 0x167f], [0x1680, 0x169f], [0x16a0, 0x16ff], [0x1780, 0x17ff], [0x1800, 0x18af], [0x2800, 0x28ff], [0xa000, 0xa48f], [0x1700, 0x171f, 0x1720, 0x173f, 0x1740, 0x175f, 0x1760, 0x177f], [0x10300, 0x1032f], [0x10330, 0x1034f], [0x10400, 0x1044f], [0x1d000, 0x1d0ff, 0x1d100, 0x1d1ff, 0x1d200, 0x1d24f], [0x1d400, 0x1d7ff], [0xff000, 0xffffd], [0xfe00, 0xfe0f, 0xe0100, 0xe01ef], [0xe0000, 0xe007f], [0x1900, 0x194f], [0x1950, 0x197f], [0x1980, 0x19df], [0x1a00, 0x1a1f], [0x2c00, 0x2c5f], [0x2d30, 0x2d7f], [0x4dc0, 0x4dff], [0xa800, 0xa82f], [0x10000, 0x1007f, 0x10080, 0x100ff, 0x10100, 0x1013f], [0x10140, 0x1018f], [0x10380, 0x1039f], [0x103a0, 0x103df], [0x10450, 0x1047f], [0x10480, 0x104af], [0x10800, 0x1083f], [0x10a00, 0x10a5f], [0x1d300, 0x1d35f], [0x12000, 0x123ff, 0x12400, 0x1247f], [0x1d360, 0x1d37f], [0x1b80, 0x1bbf], [0x1c00, 0x1c4f], [0x1c50, 0x1c7f], [0xa880, 0xa8df], [0xa900, 0xa92f], [0xa930, 0xa95f], [0xaa00, 0xaa5f], [0x10190, 0x101cf], [0x101d0, 0x101ff], [0x102a0, 0x102df, 0x10280, 0x1029f, 0x10920, 0x1093f], [0x1f030, 0x1f09f, 0x1f000, 0x1f02f]];\nfunction getUnicodeRangeFor(value, lastPosition = -1) {\n  if (lastPosition !== -1) {\n    const range = UnicodeRanges[lastPosition];\n    for (let i = 0, ii = range.length; i < ii; i += 2) {\n      if (value >= range[i] && value <= range[i + 1]) {\n        return lastPosition;\n      }\n    }\n  }\n  for (let i = 0, ii = UnicodeRanges.length; i < ii; i++) {\n    const range = UnicodeRanges[i];\n    for (let j = 0, jj = range.length; j < jj; j += 2) {\n      if (value >= range[j] && value <= range[j + 1]) {\n        return i;\n      }\n    }\n  }\n  return -1;\n}\nconst SpecialCharRegExp = new RegExp(\"^(\\\\s)|(\\\\p{Mn})|(\\\\p{Cf})$\", \"u\");\nconst CategoryCache = new Map();\nfunction getCharUnicodeCategory(char) {\n  const cachedCategory = CategoryCache.get(char);\n  if (cachedCategory) {\n    return cachedCategory;\n  }\n  const groups = char.match(SpecialCharRegExp);\n  const category = {\n    isWhitespace: !!groups?.[1],\n    isZeroWidthDiacritic: !!groups?.[2],\n    isInvisibleFormatMark: !!groups?.[3]\n  };\n  CategoryCache.set(char, category);\n  return category;\n}\nfunction clearUnicodeCaches() {\n  CategoryCache.clear();\n}\n\n;// CONCATENATED MODULE: ./src/core/fonts_utils.js\n\n\n\n\nconst SEAC_ANALYSIS_ENABLED = true;\nconst FontFlags = {\n  FixedPitch: 1,\n  Serif: 2,\n  Symbolic: 4,\n  Script: 8,\n  Nonsymbolic: 32,\n  Italic: 64,\n  AllCap: 65536,\n  SmallCap: 131072,\n  ForceBold: 262144\n};\nconst MacStandardGlyphOrdering = [\".notdef\", \".null\", \"nonmarkingreturn\", \"space\", \"exclam\", \"quotedbl\", \"numbersign\", \"dollar\", \"percent\", \"ampersand\", \"quotesingle\", \"parenleft\", \"parenright\", \"asterisk\", \"plus\", \"comma\", \"hyphen\", \"period\", \"slash\", \"zero\", \"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\", \"colon\", \"semicolon\", \"less\", \"equal\", \"greater\", \"question\", \"at\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\", \"bracketleft\", \"backslash\", \"bracketright\", \"asciicircum\", \"underscore\", \"grave\", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"braceleft\", \"bar\", \"braceright\", \"asciitilde\", \"Adieresis\", \"Aring\", \"Ccedilla\", \"Eacute\", \"Ntilde\", \"Odieresis\", \"Udieresis\", \"aacute\", \"agrave\", \"acircumflex\", \"adieresis\", \"atilde\", \"aring\", \"ccedilla\", \"eacute\", \"egrave\", \"ecircumflex\", \"edieresis\", \"iacute\", \"igrave\", \"icircumflex\", \"idieresis\", \"ntilde\", \"oacute\", \"ograve\", \"ocircumflex\", \"odieresis\", \"otilde\", \"uacute\", \"ugrave\", \"ucircumflex\", \"udieresis\", \"dagger\", \"degree\", \"cent\", \"sterling\", \"section\", \"bullet\", \"paragraph\", \"germandbls\", \"registered\", \"copyright\", \"trademark\", \"acute\", \"dieresis\", \"notequal\", \"AE\", \"Oslash\", \"infinity\", \"plusminus\", \"lessequal\", \"greaterequal\", \"yen\", \"mu\", \"partialdiff\", \"summation\", \"product\", \"pi\", \"integral\", \"ordfeminine\", \"ordmasculine\", \"Omega\", \"ae\", \"oslash\", \"questiondown\", \"exclamdown\", \"logicalnot\", \"radical\", \"florin\", \"approxequal\", \"Delta\", \"guillemotleft\", \"guillemotright\", \"ellipsis\", \"nonbreakingspace\", \"Agrave\", \"Atilde\", \"Otilde\", \"OE\", \"oe\", \"endash\", \"emdash\", \"quotedblleft\", \"quotedblright\", \"quoteleft\", \"quoteright\", \"divide\", \"lozenge\", \"ydieresis\", \"Ydieresis\", \"fraction\", \"currency\", \"guilsinglleft\", \"guilsinglright\", \"fi\", \"fl\", \"daggerdbl\", \"periodcentered\", \"quotesinglbase\", \"quotedblbase\", \"perthousand\", \"Acircumflex\", \"Ecircumflex\", \"Aacute\", \"Edieresis\", \"Egrave\", \"Iacute\", \"Icircumflex\", \"Idieresis\", \"Igrave\", \"Oacute\", \"Ocircumflex\", \"apple\", \"Ograve\", \"Uacute\", \"Ucircumflex\", \"Ugrave\", \"dotlessi\", \"circumflex\", \"tilde\", \"macron\", \"breve\", \"dotaccent\", \"ring\", \"cedilla\", \"hungarumlaut\", \"ogonek\", \"caron\", \"Lslash\", \"lslash\", \"Scaron\", \"scaron\", \"Zcaron\", \"zcaron\", \"brokenbar\", \"Eth\", \"eth\", \"Yacute\", \"yacute\", \"Thorn\", \"thorn\", \"minus\", \"multiply\", \"onesuperior\", \"twosuperior\", \"threesuperior\", \"onehalf\", \"onequarter\", \"threequarters\", \"franc\", \"Gbreve\", \"gbreve\", \"Idotaccent\", \"Scedilla\", \"scedilla\", \"Cacute\", \"cacute\", \"Ccaron\", \"ccaron\", \"dcroat\"];\nfunction recoverGlyphName(name, glyphsUnicodeMap) {\n  if (glyphsUnicodeMap[name] !== undefined) {\n    return name;\n  }\n  const unicode = getUnicodeForGlyph(name, glyphsUnicodeMap);\n  if (unicode !== -1) {\n    for (const key in glyphsUnicodeMap) {\n      if (glyphsUnicodeMap[key] === unicode) {\n        return key;\n      }\n    }\n  }\n  info(\"Unable to recover a standard glyph name for: \" + name);\n  return name;\n}\nfunction type1FontGlyphMapping(properties, builtInEncoding, glyphNames) {\n  const charCodeToGlyphId = Object.create(null);\n  let glyphId, charCode, baseEncoding;\n  const isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);\n  if (properties.isInternalFont) {\n    baseEncoding = builtInEncoding;\n    for (charCode = 0; charCode < baseEncoding.length; charCode++) {\n      glyphId = glyphNames.indexOf(baseEncoding[charCode]);\n      charCodeToGlyphId[charCode] = glyphId >= 0 ? glyphId : 0;\n    }\n  } else if (properties.baseEncodingName) {\n    baseEncoding = getEncoding(properties.baseEncodingName);\n    for (charCode = 0; charCode < baseEncoding.length; charCode++) {\n      glyphId = glyphNames.indexOf(baseEncoding[charCode]);\n      charCodeToGlyphId[charCode] = glyphId >= 0 ? glyphId : 0;\n    }\n  } else if (isSymbolicFont) {\n    for (charCode in builtInEncoding) {\n      charCodeToGlyphId[charCode] = builtInEncoding[charCode];\n    }\n  } else {\n    baseEncoding = StandardEncoding;\n    for (charCode = 0; charCode < baseEncoding.length; charCode++) {\n      glyphId = glyphNames.indexOf(baseEncoding[charCode]);\n      charCodeToGlyphId[charCode] = glyphId >= 0 ? glyphId : 0;\n    }\n  }\n  const differences = properties.differences;\n  let glyphsUnicodeMap;\n  if (differences) {\n    for (charCode in differences) {\n      const glyphName = differences[charCode];\n      glyphId = glyphNames.indexOf(glyphName);\n      if (glyphId === -1) {\n        if (!glyphsUnicodeMap) {\n          glyphsUnicodeMap = getGlyphsUnicode();\n        }\n        const standardGlyphName = recoverGlyphName(glyphName, glyphsUnicodeMap);\n        if (standardGlyphName !== glyphName) {\n          glyphId = glyphNames.indexOf(standardGlyphName);\n        }\n      }\n      charCodeToGlyphId[charCode] = glyphId >= 0 ? glyphId : 0;\n    }\n  }\n  return charCodeToGlyphId;\n}\nfunction normalizeFontName(name) {\n  return name.replaceAll(/[,_]/g, \"-\").replaceAll(/\\s/g, \"\");\n}\n\n;// CONCATENATED MODULE: ./src/core/standard_fonts.js\n\n\nconst getStdFontMap = getLookupTableFactory(function (t) {\n  t[\"Times-Roman\"] = \"Times-Roman\";\n  t.Helvetica = \"Helvetica\";\n  t.Courier = \"Courier\";\n  t.Symbol = \"Symbol\";\n  t[\"Times-Bold\"] = \"Times-Bold\";\n  t[\"Helvetica-Bold\"] = \"Helvetica-Bold\";\n  t[\"Courier-Bold\"] = \"Courier-Bold\";\n  t.ZapfDingbats = \"ZapfDingbats\";\n  t[\"Times-Italic\"] = \"Times-Italic\";\n  t[\"Helvetica-Oblique\"] = \"Helvetica-Oblique\";\n  t[\"Courier-Oblique\"] = \"Courier-Oblique\";\n  t[\"Times-BoldItalic\"] = \"Times-BoldItalic\";\n  t[\"Helvetica-BoldOblique\"] = \"Helvetica-BoldOblique\";\n  t[\"Courier-BoldOblique\"] = \"Courier-BoldOblique\";\n  t.ArialNarrow = \"Helvetica\";\n  t[\"ArialNarrow-Bold\"] = \"Helvetica-Bold\";\n  t[\"ArialNarrow-BoldItalic\"] = \"Helvetica-BoldOblique\";\n  t[\"ArialNarrow-Italic\"] = \"Helvetica-Oblique\";\n  t.ArialBlack = \"Helvetica\";\n  t[\"ArialBlack-Bold\"] = \"Helvetica-Bold\";\n  t[\"ArialBlack-BoldItalic\"] = \"Helvetica-BoldOblique\";\n  t[\"ArialBlack-Italic\"] = \"Helvetica-Oblique\";\n  t[\"Arial-Black\"] = \"Helvetica\";\n  t[\"Arial-Black-Bold\"] = \"Helvetica-Bold\";\n  t[\"Arial-Black-BoldItalic\"] = \"Helvetica-BoldOblique\";\n  t[\"Arial-Black-Italic\"] = \"Helvetica-Oblique\";\n  t.Arial = \"Helvetica\";\n  t[\"Arial-Bold\"] = \"Helvetica-Bold\";\n  t[\"Arial-BoldItalic\"] = \"Helvetica-BoldOblique\";\n  t[\"Arial-Italic\"] = \"Helvetica-Oblique\";\n  t.ArialMT = \"Helvetica\";\n  t[\"Arial-BoldItalicMT\"] = \"Helvetica-BoldOblique\";\n  t[\"Arial-BoldMT\"] = \"Helvetica-Bold\";\n  t[\"Arial-ItalicMT\"] = \"Helvetica-Oblique\";\n  t[\"Arial-BoldItalicMT-BoldItalic\"] = \"Helvetica-BoldOblique\";\n  t[\"Arial-BoldMT-Bold\"] = \"Helvetica-Bold\";\n  t[\"Arial-ItalicMT-Italic\"] = \"Helvetica-Oblique\";\n  t.ArialUnicodeMS = \"Helvetica\";\n  t[\"ArialUnicodeMS-Bold\"] = \"Helvetica-Bold\";\n  t[\"ArialUnicodeMS-BoldItalic\"] = \"Helvetica-BoldOblique\";\n  t[\"ArialUnicodeMS-Italic\"] = \"Helvetica-Oblique\";\n  t[\"Courier-BoldItalic\"] = \"Courier-BoldOblique\";\n  t[\"Courier-Italic\"] = \"Courier-Oblique\";\n  t.CourierNew = \"Courier\";\n  t[\"CourierNew-Bold\"] = \"Courier-Bold\";\n  t[\"CourierNew-BoldItalic\"] = \"Courier-BoldOblique\";\n  t[\"CourierNew-Italic\"] = \"Courier-Oblique\";\n  t[\"CourierNewPS-BoldItalicMT\"] = \"Courier-BoldOblique\";\n  t[\"CourierNewPS-BoldMT\"] = \"Courier-Bold\";\n  t[\"CourierNewPS-ItalicMT\"] = \"Courier-Oblique\";\n  t.CourierNewPSMT = \"Courier\";\n  t[\"Helvetica-BoldItalic\"] = \"Helvetica-BoldOblique\";\n  t[\"Helvetica-Italic\"] = \"Helvetica-Oblique\";\n  t[\"Symbol-Bold\"] = \"Symbol\";\n  t[\"Symbol-BoldItalic\"] = \"Symbol\";\n  t[\"Symbol-Italic\"] = \"Symbol\";\n  t.TimesNewRoman = \"Times-Roman\";\n  t[\"TimesNewRoman-Bold\"] = \"Times-Bold\";\n  t[\"TimesNewRoman-BoldItalic\"] = \"Times-BoldItalic\";\n  t[\"TimesNewRoman-Italic\"] = \"Times-Italic\";\n  t.TimesNewRomanPS = \"Times-Roman\";\n  t[\"TimesNewRomanPS-Bold\"] = \"Times-Bold\";\n  t[\"TimesNewRomanPS-BoldItalic\"] = \"Times-BoldItalic\";\n  t[\"TimesNewRomanPS-BoldItalicMT\"] = \"Times-BoldItalic\";\n  t[\"TimesNewRomanPS-BoldMT\"] = \"Times-Bold\";\n  t[\"TimesNewRomanPS-Italic\"] = \"Times-Italic\";\n  t[\"TimesNewRomanPS-ItalicMT\"] = \"Times-Italic\";\n  t.TimesNewRomanPSMT = \"Times-Roman\";\n  t[\"TimesNewRomanPSMT-Bold\"] = \"Times-Bold\";\n  t[\"TimesNewRomanPSMT-BoldItalic\"] = \"Times-BoldItalic\";\n  t[\"TimesNewRomanPSMT-Italic\"] = \"Times-Italic\";\n});\nconst getFontNameToFileMap = getLookupTableFactory(function (t) {\n  t.Courier = \"FoxitFixed.pfb\";\n  t[\"Courier-Bold\"] = \"FoxitFixedBold.pfb\";\n  t[\"Courier-BoldOblique\"] = \"FoxitFixedBoldItalic.pfb\";\n  t[\"Courier-Oblique\"] = \"FoxitFixedItalic.pfb\";\n  t.Helvetica = \"LiberationSans-Regular.ttf\";\n  t[\"Helvetica-Bold\"] = \"LiberationSans-Bold.ttf\";\n  t[\"Helvetica-BoldOblique\"] = \"LiberationSans-BoldItalic.ttf\";\n  t[\"Helvetica-Oblique\"] = \"LiberationSans-Italic.ttf\";\n  t[\"Times-Roman\"] = \"FoxitSerif.pfb\";\n  t[\"Times-Bold\"] = \"FoxitSerifBold.pfb\";\n  t[\"Times-BoldItalic\"] = \"FoxitSerifBoldItalic.pfb\";\n  t[\"Times-Italic\"] = \"FoxitSerifItalic.pfb\";\n  t.Symbol = \"FoxitSymbol.pfb\";\n  t.ZapfDingbats = \"FoxitDingbats.pfb\";\n  t[\"LiberationSans-Regular\"] = \"LiberationSans-Regular.ttf\";\n  t[\"LiberationSans-Bold\"] = \"LiberationSans-Bold.ttf\";\n  t[\"LiberationSans-Italic\"] = \"LiberationSans-Italic.ttf\";\n  t[\"LiberationSans-BoldItalic\"] = \"LiberationSans-BoldItalic.ttf\";\n});\nconst getNonStdFontMap = getLookupTableFactory(function (t) {\n  t.Calibri = \"Helvetica\";\n  t[\"Calibri-Bold\"] = \"Helvetica-Bold\";\n  t[\"Calibri-BoldItalic\"] = \"Helvetica-BoldOblique\";\n  t[\"Calibri-Italic\"] = \"Helvetica-Oblique\";\n  t.CenturyGothic = \"Helvetica\";\n  t[\"CenturyGothic-Bold\"] = \"Helvetica-Bold\";\n  t[\"CenturyGothic-BoldItalic\"] = \"Helvetica-BoldOblique\";\n  t[\"CenturyGothic-Italic\"] = \"Helvetica-Oblique\";\n  t.ComicSansMS = \"Comic Sans MS\";\n  t[\"ComicSansMS-Bold\"] = \"Comic Sans MS-Bold\";\n  t[\"ComicSansMS-BoldItalic\"] = \"Comic Sans MS-BoldItalic\";\n  t[\"ComicSansMS-Italic\"] = \"Comic Sans MS-Italic\";\n  t.Impact = \"Helvetica\";\n  t[\"ItcSymbol-Bold\"] = \"Helvetica-Bold\";\n  t[\"ItcSymbol-BoldItalic\"] = \"Helvetica-BoldOblique\";\n  t[\"ItcSymbol-Book\"] = \"Helvetica\";\n  t[\"ItcSymbol-BookItalic\"] = \"Helvetica-Oblique\";\n  t[\"ItcSymbol-Medium\"] = \"Helvetica\";\n  t[\"ItcSymbol-MediumItalic\"] = \"Helvetica-Oblique\";\n  t.LucidaConsole = \"Courier\";\n  t[\"LucidaConsole-Bold\"] = \"Courier-Bold\";\n  t[\"LucidaConsole-BoldItalic\"] = \"Courier-BoldOblique\";\n  t[\"LucidaConsole-Italic\"] = \"Courier-Oblique\";\n  t[\"LucidaSans-Demi\"] = \"Helvetica-Bold\";\n  t[\"MS-Gothic\"] = \"MS Gothic\";\n  t[\"MS-Gothic-Bold\"] = \"MS Gothic-Bold\";\n  t[\"MS-Gothic-BoldItalic\"] = \"MS Gothic-BoldItalic\";\n  t[\"MS-Gothic-Italic\"] = \"MS Gothic-Italic\";\n  t[\"MS-Mincho\"] = \"MS Mincho\";\n  t[\"MS-Mincho-Bold\"] = \"MS Mincho-Bold\";\n  t[\"MS-Mincho-BoldItalic\"] = \"MS Mincho-BoldItalic\";\n  t[\"MS-Mincho-Italic\"] = \"MS Mincho-Italic\";\n  t[\"MS-PGothic\"] = \"MS PGothic\";\n  t[\"MS-PGothic-Bold\"] = \"MS PGothic-Bold\";\n  t[\"MS-PGothic-BoldItalic\"] = \"MS PGothic-BoldItalic\";\n  t[\"MS-PGothic-Italic\"] = \"MS PGothic-Italic\";\n  t[\"MS-PMincho\"] = \"MS PMincho\";\n  t[\"MS-PMincho-Bold\"] = \"MS PMincho-Bold\";\n  t[\"MS-PMincho-BoldItalic\"] = \"MS PMincho-BoldItalic\";\n  t[\"MS-PMincho-Italic\"] = \"MS PMincho-Italic\";\n  t.NuptialScript = \"Times-Italic\";\n  t.SegoeUISymbol = \"Helvetica\";\n});\nconst getSerifFonts = getLookupTableFactory(function (t) {\n  t[\"Adobe Jenson\"] = true;\n  t[\"Adobe Text\"] = true;\n  t.Albertus = true;\n  t.Aldus = true;\n  t.Alexandria = true;\n  t.Algerian = true;\n  t[\"American Typewriter\"] = true;\n  t.Antiqua = true;\n  t.Apex = true;\n  t.Arno = true;\n  t.Aster = true;\n  t.Aurora = true;\n  t.Baskerville = true;\n  t.Bell = true;\n  t.Bembo = true;\n  t[\"Bembo Schoolbook\"] = true;\n  t.Benguiat = true;\n  t[\"Berkeley Old Style\"] = true;\n  t[\"Bernhard Modern\"] = true;\n  t[\"Berthold City\"] = true;\n  t.Bodoni = true;\n  t[\"Bauer Bodoni\"] = true;\n  t[\"Book Antiqua\"] = true;\n  t.Bookman = true;\n  t[\"Bordeaux Roman\"] = true;\n  t[\"Californian FB\"] = true;\n  t.Calisto = true;\n  t.Calvert = true;\n  t.Capitals = true;\n  t.Cambria = true;\n  t.Cartier = true;\n  t.Caslon = true;\n  t.Catull = true;\n  t.Centaur = true;\n  t[\"Century Old Style\"] = true;\n  t[\"Century Schoolbook\"] = true;\n  t.Chaparral = true;\n  t[\"Charis SIL\"] = true;\n  t.Cheltenham = true;\n  t[\"Cholla Slab\"] = true;\n  t.Clarendon = true;\n  t.Clearface = true;\n  t.Cochin = true;\n  t.Colonna = true;\n  t[\"Computer Modern\"] = true;\n  t[\"Concrete Roman\"] = true;\n  t.Constantia = true;\n  t[\"Cooper Black\"] = true;\n  t.Corona = true;\n  t.Ecotype = true;\n  t.Egyptienne = true;\n  t.Elephant = true;\n  t.Excelsior = true;\n  t.Fairfield = true;\n  t[\"FF Scala\"] = true;\n  t.Folkard = true;\n  t.Footlight = true;\n  t.FreeSerif = true;\n  t[\"Friz Quadrata\"] = true;\n  t.Garamond = true;\n  t.Gentium = true;\n  t.Georgia = true;\n  t.Gloucester = true;\n  t[\"Goudy Old Style\"] = true;\n  t[\"Goudy Schoolbook\"] = true;\n  t[\"Goudy Pro Font\"] = true;\n  t.Granjon = true;\n  t[\"Guardian Egyptian\"] = true;\n  t.Heather = true;\n  t.Hercules = true;\n  t[\"High Tower Text\"] = true;\n  t.Hiroshige = true;\n  t[\"Hoefler Text\"] = true;\n  t[\"Humana Serif\"] = true;\n  t.Imprint = true;\n  t[\"Ionic No. 5\"] = true;\n  t.Janson = true;\n  t.Joanna = true;\n  t.Korinna = true;\n  t.Lexicon = true;\n  t.LiberationSerif = true;\n  t[\"Liberation Serif\"] = true;\n  t[\"Linux Libertine\"] = true;\n  t.Literaturnaya = true;\n  t.Lucida = true;\n  t[\"Lucida Bright\"] = true;\n  t.Melior = true;\n  t.Memphis = true;\n  t.Miller = true;\n  t.Minion = true;\n  t.Modern = true;\n  t[\"Mona Lisa\"] = true;\n  t[\"Mrs Eaves\"] = true;\n  t[\"MS Serif\"] = true;\n  t[\"Museo Slab\"] = true;\n  t[\"New York\"] = true;\n  t[\"Nimbus Roman\"] = true;\n  t[\"NPS Rawlinson Roadway\"] = true;\n  t.NuptialScript = true;\n  t.Palatino = true;\n  t.Perpetua = true;\n  t.Plantin = true;\n  t[\"Plantin Schoolbook\"] = true;\n  t.Playbill = true;\n  t[\"Poor Richard\"] = true;\n  t[\"Rawlinson Roadway\"] = true;\n  t.Renault = true;\n  t.Requiem = true;\n  t.Rockwell = true;\n  t.Roman = true;\n  t[\"Rotis Serif\"] = true;\n  t.Sabon = true;\n  t.Scala = true;\n  t.Seagull = true;\n  t.Sistina = true;\n  t.Souvenir = true;\n  t.STIX = true;\n  t[\"Stone Informal\"] = true;\n  t[\"Stone Serif\"] = true;\n  t.Sylfaen = true;\n  t.Times = true;\n  t.Trajan = true;\n  t[\"Trinité\"] = true;\n  t[\"Trump Mediaeval\"] = true;\n  t.Utopia = true;\n  t[\"Vale Type\"] = true;\n  t[\"Bitstream Vera\"] = true;\n  t[\"Vera Serif\"] = true;\n  t.Versailles = true;\n  t.Wanted = true;\n  t.Weiss = true;\n  t[\"Wide Latin\"] = true;\n  t.Windsor = true;\n  t.XITS = true;\n});\nconst getSymbolsFonts = getLookupTableFactory(function (t) {\n  t.Dingbats = true;\n  t.Symbol = true;\n  t.ZapfDingbats = true;\n  t.Wingdings = true;\n  t[\"Wingdings-Bold\"] = true;\n  t[\"Wingdings-Regular\"] = true;\n});\nconst getGlyphMapForStandardFonts = getLookupTableFactory(function (t) {\n  t[2] = 10;\n  t[3] = 32;\n  t[4] = 33;\n  t[5] = 34;\n  t[6] = 35;\n  t[7] = 36;\n  t[8] = 37;\n  t[9] = 38;\n  t[10] = 39;\n  t[11] = 40;\n  t[12] = 41;\n  t[13] = 42;\n  t[14] = 43;\n  t[15] = 44;\n  t[16] = 45;\n  t[17] = 46;\n  t[18] = 47;\n  t[19] = 48;\n  t[20] = 49;\n  t[21] = 50;\n  t[22] = 51;\n  t[23] = 52;\n  t[24] = 53;\n  t[25] = 54;\n  t[26] = 55;\n  t[27] = 56;\n  t[28] = 57;\n  t[29] = 58;\n  t[30] = 894;\n  t[31] = 60;\n  t[32] = 61;\n  t[33] = 62;\n  t[34] = 63;\n  t[35] = 64;\n  t[36] = 65;\n  t[37] = 66;\n  t[38] = 67;\n  t[39] = 68;\n  t[40] = 69;\n  t[41] = 70;\n  t[42] = 71;\n  t[43] = 72;\n  t[44] = 73;\n  t[45] = 74;\n  t[46] = 75;\n  t[47] = 76;\n  t[48] = 77;\n  t[49] = 78;\n  t[50] = 79;\n  t[51] = 80;\n  t[52] = 81;\n  t[53] = 82;\n  t[54] = 83;\n  t[55] = 84;\n  t[56] = 85;\n  t[57] = 86;\n  t[58] = 87;\n  t[59] = 88;\n  t[60] = 89;\n  t[61] = 90;\n  t[62] = 91;\n  t[63] = 92;\n  t[64] = 93;\n  t[65] = 94;\n  t[66] = 95;\n  t[67] = 96;\n  t[68] = 97;\n  t[69] = 98;\n  t[70] = 99;\n  t[71] = 100;\n  t[72] = 101;\n  t[73] = 102;\n  t[74] = 103;\n  t[75] = 104;\n  t[76] = 105;\n  t[77] = 106;\n  t[78] = 107;\n  t[79] = 108;\n  t[80] = 109;\n  t[81] = 110;\n  t[82] = 111;\n  t[83] = 112;\n  t[84] = 113;\n  t[85] = 114;\n  t[86] = 115;\n  t[87] = 116;\n  t[88] = 117;\n  t[89] = 118;\n  t[90] = 119;\n  t[91] = 120;\n  t[92] = 121;\n  t[93] = 122;\n  t[94] = 123;\n  t[95] = 124;\n  t[96] = 125;\n  t[97] = 126;\n  t[98] = 196;\n  t[99] = 197;\n  t[100] = 199;\n  t[101] = 201;\n  t[102] = 209;\n  t[103] = 214;\n  t[104] = 220;\n  t[105] = 225;\n  t[106] = 224;\n  t[107] = 226;\n  t[108] = 228;\n  t[109] = 227;\n  t[110] = 229;\n  t[111] = 231;\n  t[112] = 233;\n  t[113] = 232;\n  t[114] = 234;\n  t[115] = 235;\n  t[116] = 237;\n  t[117] = 236;\n  t[118] = 238;\n  t[119] = 239;\n  t[120] = 241;\n  t[121] = 243;\n  t[122] = 242;\n  t[123] = 244;\n  t[124] = 246;\n  t[125] = 245;\n  t[126] = 250;\n  t[127] = 249;\n  t[128] = 251;\n  t[129] = 252;\n  t[130] = 8224;\n  t[131] = 176;\n  t[132] = 162;\n  t[133] = 163;\n  t[134] = 167;\n  t[135] = 8226;\n  t[136] = 182;\n  t[137] = 223;\n  t[138] = 174;\n  t[139] = 169;\n  t[140] = 8482;\n  t[141] = 180;\n  t[142] = 168;\n  t[143] = 8800;\n  t[144] = 198;\n  t[145] = 216;\n  t[146] = 8734;\n  t[147] = 177;\n  t[148] = 8804;\n  t[149] = 8805;\n  t[150] = 165;\n  t[151] = 181;\n  t[152] = 8706;\n  t[153] = 8721;\n  t[154] = 8719;\n  t[156] = 8747;\n  t[157] = 170;\n  t[158] = 186;\n  t[159] = 8486;\n  t[160] = 230;\n  t[161] = 248;\n  t[162] = 191;\n  t[163] = 161;\n  t[164] = 172;\n  t[165] = 8730;\n  t[166] = 402;\n  t[167] = 8776;\n  t[168] = 8710;\n  t[169] = 171;\n  t[170] = 187;\n  t[171] = 8230;\n  t[179] = 8220;\n  t[180] = 8221;\n  t[181] = 8216;\n  t[182] = 8217;\n  t[200] = 193;\n  t[203] = 205;\n  t[207] = 211;\n  t[210] = 218;\n  t[223] = 711;\n  t[224] = 321;\n  t[225] = 322;\n  t[226] = 352;\n  t[227] = 353;\n  t[228] = 381;\n  t[229] = 382;\n  t[233] = 221;\n  t[234] = 253;\n  t[252] = 263;\n  t[253] = 268;\n  t[254] = 269;\n  t[258] = 258;\n  t[260] = 260;\n  t[261] = 261;\n  t[265] = 280;\n  t[266] = 281;\n  t[267] = 282;\n  t[268] = 283;\n  t[269] = 313;\n  t[275] = 323;\n  t[276] = 324;\n  t[278] = 328;\n  t[283] = 344;\n  t[284] = 345;\n  t[285] = 346;\n  t[286] = 347;\n  t[292] = 367;\n  t[295] = 377;\n  t[296] = 378;\n  t[298] = 380;\n  t[305] = 963;\n  t[306] = 964;\n  t[307] = 966;\n  t[308] = 8215;\n  t[309] = 8252;\n  t[310] = 8319;\n  t[311] = 8359;\n  t[312] = 8592;\n  t[313] = 8593;\n  t[337] = 9552;\n  t[493] = 1039;\n  t[494] = 1040;\n  t[672] = 1488;\n  t[673] = 1489;\n  t[674] = 1490;\n  t[675] = 1491;\n  t[676] = 1492;\n  t[677] = 1493;\n  t[678] = 1494;\n  t[679] = 1495;\n  t[680] = 1496;\n  t[681] = 1497;\n  t[682] = 1498;\n  t[683] = 1499;\n  t[684] = 1500;\n  t[685] = 1501;\n  t[686] = 1502;\n  t[687] = 1503;\n  t[688] = 1504;\n  t[689] = 1505;\n  t[690] = 1506;\n  t[691] = 1507;\n  t[692] = 1508;\n  t[693] = 1509;\n  t[694] = 1510;\n  t[695] = 1511;\n  t[696] = 1512;\n  t[697] = 1513;\n  t[698] = 1514;\n  t[705] = 1524;\n  t[706] = 8362;\n  t[710] = 64288;\n  t[711] = 64298;\n  t[759] = 1617;\n  t[761] = 1776;\n  t[763] = 1778;\n  t[775] = 1652;\n  t[777] = 1764;\n  t[778] = 1780;\n  t[779] = 1781;\n  t[780] = 1782;\n  t[782] = 771;\n  t[783] = 64726;\n  t[786] = 8363;\n  t[788] = 8532;\n  t[790] = 768;\n  t[791] = 769;\n  t[792] = 768;\n  t[795] = 803;\n  t[797] = 64336;\n  t[798] = 64337;\n  t[799] = 64342;\n  t[800] = 64343;\n  t[801] = 64344;\n  t[802] = 64345;\n  t[803] = 64362;\n  t[804] = 64363;\n  t[805] = 64364;\n  t[2424] = 7821;\n  t[2425] = 7822;\n  t[2426] = 7823;\n  t[2427] = 7824;\n  t[2428] = 7825;\n  t[2429] = 7826;\n  t[2430] = 7827;\n  t[2433] = 7682;\n  t[2678] = 8045;\n  t[2679] = 8046;\n  t[2830] = 1552;\n  t[2838] = 686;\n  t[2840] = 751;\n  t[2842] = 753;\n  t[2843] = 754;\n  t[2844] = 755;\n  t[2846] = 757;\n  t[2856] = 767;\n  t[2857] = 848;\n  t[2858] = 849;\n  t[2862] = 853;\n  t[2863] = 854;\n  t[2864] = 855;\n  t[2865] = 861;\n  t[2866] = 862;\n  t[2906] = 7460;\n  t[2908] = 7462;\n  t[2909] = 7463;\n  t[2910] = 7464;\n  t[2912] = 7466;\n  t[2913] = 7467;\n  t[2914] = 7468;\n  t[2916] = 7470;\n  t[2917] = 7471;\n  t[2918] = 7472;\n  t[2920] = 7474;\n  t[2921] = 7475;\n  t[2922] = 7476;\n  t[2924] = 7478;\n  t[2925] = 7479;\n  t[2926] = 7480;\n  t[2928] = 7482;\n  t[2929] = 7483;\n  t[2930] = 7484;\n  t[2932] = 7486;\n  t[2933] = 7487;\n  t[2934] = 7488;\n  t[2936] = 7490;\n  t[2937] = 7491;\n  t[2938] = 7492;\n  t[2940] = 7494;\n  t[2941] = 7495;\n  t[2942] = 7496;\n  t[2944] = 7498;\n  t[2946] = 7500;\n  t[2948] = 7502;\n  t[2950] = 7504;\n  t[2951] = 7505;\n  t[2952] = 7506;\n  t[2954] = 7508;\n  t[2955] = 7509;\n  t[2956] = 7510;\n  t[2958] = 7512;\n  t[2959] = 7513;\n  t[2960] = 7514;\n  t[2962] = 7516;\n  t[2963] = 7517;\n  t[2964] = 7518;\n  t[2966] = 7520;\n  t[2967] = 7521;\n  t[2968] = 7522;\n  t[2970] = 7524;\n  t[2971] = 7525;\n  t[2972] = 7526;\n  t[2974] = 7528;\n  t[2975] = 7529;\n  t[2976] = 7530;\n  t[2978] = 1537;\n  t[2979] = 1538;\n  t[2980] = 1539;\n  t[2982] = 1549;\n  t[2983] = 1551;\n  t[2984] = 1552;\n  t[2986] = 1554;\n  t[2987] = 1555;\n  t[2988] = 1556;\n  t[2990] = 1623;\n  t[2991] = 1624;\n  t[2995] = 1775;\n  t[2999] = 1791;\n  t[3002] = 64290;\n  t[3003] = 64291;\n  t[3004] = 64292;\n  t[3006] = 64294;\n  t[3007] = 64295;\n  t[3008] = 64296;\n  t[3011] = 1900;\n  t[3014] = 8223;\n  t[3015] = 8244;\n  t[3017] = 7532;\n  t[3018] = 7533;\n  t[3019] = 7534;\n  t[3075] = 7590;\n  t[3076] = 7591;\n  t[3079] = 7594;\n  t[3080] = 7595;\n  t[3083] = 7598;\n  t[3084] = 7599;\n  t[3087] = 7602;\n  t[3088] = 7603;\n  t[3091] = 7606;\n  t[3092] = 7607;\n  t[3095] = 7610;\n  t[3096] = 7611;\n  t[3099] = 7614;\n  t[3100] = 7615;\n  t[3103] = 7618;\n  t[3104] = 7619;\n  t[3107] = 8337;\n  t[3108] = 8338;\n  t[3116] = 1884;\n  t[3119] = 1885;\n  t[3120] = 1885;\n  t[3123] = 1886;\n  t[3124] = 1886;\n  t[3127] = 1887;\n  t[3128] = 1887;\n  t[3131] = 1888;\n  t[3132] = 1888;\n  t[3135] = 1889;\n  t[3136] = 1889;\n  t[3139] = 1890;\n  t[3140] = 1890;\n  t[3143] = 1891;\n  t[3144] = 1891;\n  t[3147] = 1892;\n  t[3148] = 1892;\n  t[3153] = 580;\n  t[3154] = 581;\n  t[3157] = 584;\n  t[3158] = 585;\n  t[3161] = 588;\n  t[3162] = 589;\n  t[3165] = 891;\n  t[3166] = 892;\n  t[3169] = 1274;\n  t[3170] = 1275;\n  t[3173] = 1278;\n  t[3174] = 1279;\n  t[3181] = 7622;\n  t[3182] = 7623;\n  t[3282] = 11799;\n  t[3316] = 578;\n  t[3379] = 42785;\n  t[3393] = 1159;\n  t[3416] = 8377;\n});\nconst getSupplementalGlyphMapForArialBlack = getLookupTableFactory(function (t) {\n  t[227] = 322;\n  t[264] = 261;\n  t[291] = 346;\n});\nconst getSupplementalGlyphMapForCalibri = getLookupTableFactory(function (t) {\n  t[1] = 32;\n  t[4] = 65;\n  t[5] = 192;\n  t[6] = 193;\n  t[9] = 196;\n  t[17] = 66;\n  t[18] = 67;\n  t[21] = 268;\n  t[24] = 68;\n  t[28] = 69;\n  t[29] = 200;\n  t[30] = 201;\n  t[32] = 282;\n  t[38] = 70;\n  t[39] = 71;\n  t[44] = 72;\n  t[47] = 73;\n  t[48] = 204;\n  t[49] = 205;\n  t[58] = 74;\n  t[60] = 75;\n  t[62] = 76;\n  t[68] = 77;\n  t[69] = 78;\n  t[75] = 79;\n  t[76] = 210;\n  t[80] = 214;\n  t[87] = 80;\n  t[89] = 81;\n  t[90] = 82;\n  t[92] = 344;\n  t[94] = 83;\n  t[97] = 352;\n  t[100] = 84;\n  t[104] = 85;\n  t[109] = 220;\n  t[115] = 86;\n  t[116] = 87;\n  t[121] = 88;\n  t[122] = 89;\n  t[124] = 221;\n  t[127] = 90;\n  t[129] = 381;\n  t[258] = 97;\n  t[259] = 224;\n  t[260] = 225;\n  t[263] = 228;\n  t[268] = 261;\n  t[271] = 98;\n  t[272] = 99;\n  t[273] = 263;\n  t[275] = 269;\n  t[282] = 100;\n  t[286] = 101;\n  t[287] = 232;\n  t[288] = 233;\n  t[290] = 283;\n  t[295] = 281;\n  t[296] = 102;\n  t[336] = 103;\n  t[346] = 104;\n  t[349] = 105;\n  t[350] = 236;\n  t[351] = 237;\n  t[361] = 106;\n  t[364] = 107;\n  t[367] = 108;\n  t[371] = 322;\n  t[373] = 109;\n  t[374] = 110;\n  t[381] = 111;\n  t[382] = 242;\n  t[383] = 243;\n  t[386] = 246;\n  t[393] = 112;\n  t[395] = 113;\n  t[396] = 114;\n  t[398] = 345;\n  t[400] = 115;\n  t[401] = 347;\n  t[403] = 353;\n  t[410] = 116;\n  t[437] = 117;\n  t[442] = 252;\n  t[448] = 118;\n  t[449] = 119;\n  t[454] = 120;\n  t[455] = 121;\n  t[457] = 253;\n  t[460] = 122;\n  t[462] = 382;\n  t[463] = 380;\n  t[853] = 44;\n  t[855] = 58;\n  t[856] = 46;\n  t[876] = 47;\n  t[878] = 45;\n  t[882] = 45;\n  t[894] = 40;\n  t[895] = 41;\n  t[896] = 91;\n  t[897] = 93;\n  t[923] = 64;\n  t[1004] = 48;\n  t[1005] = 49;\n  t[1006] = 50;\n  t[1007] = 51;\n  t[1008] = 52;\n  t[1009] = 53;\n  t[1010] = 54;\n  t[1011] = 55;\n  t[1012] = 56;\n  t[1013] = 57;\n  t[1081] = 37;\n  t[1085] = 43;\n  t[1086] = 45;\n});\nfunction getStandardFontName(name) {\n  const fontName = normalizeFontName(name);\n  const stdFontMap = getStdFontMap();\n  return stdFontMap[fontName];\n}\nfunction isKnownFontName(name) {\n  const fontName = normalizeFontName(name);\n  return !!(getStdFontMap()[fontName] || getNonStdFontMap()[fontName] || getSerifFonts()[fontName] || getSymbolsFonts()[fontName]);\n}\n\n;// CONCATENATED MODULE: ./src/core/to_unicode_map.js\n\nclass ToUnicodeMap {\n  constructor(cmap = []) {\n    this._map = cmap;\n  }\n  get length() {\n    return this._map.length;\n  }\n  forEach(callback) {\n    for (const charCode in this._map) {\n      callback(charCode, this._map[charCode].charCodeAt(0));\n    }\n  }\n  has(i) {\n    return this._map[i] !== undefined;\n  }\n  get(i) {\n    return this._map[i];\n  }\n  charCodeOf(value) {\n    const map = this._map;\n    if (map.length <= 0x10000) {\n      return map.indexOf(value);\n    }\n    for (const charCode in map) {\n      if (map[charCode] === value) {\n        return charCode | 0;\n      }\n    }\n    return -1;\n  }\n  amend(map) {\n    for (const charCode in map) {\n      this._map[charCode] = map[charCode];\n    }\n  }\n}\nclass IdentityToUnicodeMap {\n  constructor(firstChar, lastChar) {\n    this.firstChar = firstChar;\n    this.lastChar = lastChar;\n  }\n  get length() {\n    return this.lastChar + 1 - this.firstChar;\n  }\n  forEach(callback) {\n    for (let i = this.firstChar, ii = this.lastChar; i <= ii; i++) {\n      callback(i, i);\n    }\n  }\n  has(i) {\n    return this.firstChar <= i && i <= this.lastChar;\n  }\n  get(i) {\n    if (this.firstChar <= i && i <= this.lastChar) {\n      return String.fromCharCode(i);\n    }\n    return undefined;\n  }\n  charCodeOf(v) {\n    return Number.isInteger(v) && v >= this.firstChar && v <= this.lastChar ? v : -1;\n  }\n  amend(map) {\n    unreachable(\"Should not call amend()\");\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/cff_font.js\n\n\n\nclass CFFFont {\n  constructor(file, properties) {\n    this.properties = properties;\n    const parser = new CFFParser(file, properties, SEAC_ANALYSIS_ENABLED);\n    this.cff = parser.parse();\n    this.cff.duplicateFirstGlyph();\n    const compiler = new CFFCompiler(this.cff);\n    this.seacs = this.cff.seacs;\n    try {\n      this.data = compiler.compile();\n    } catch {\n      warn(\"Failed to compile font \" + properties.loadedName);\n      this.data = file;\n    }\n    this._createBuiltInEncoding();\n  }\n  get numGlyphs() {\n    return this.cff.charStrings.count;\n  }\n  getCharset() {\n    return this.cff.charset.charset;\n  }\n  getGlyphMapping() {\n    const cff = this.cff;\n    const properties = this.properties;\n    const {\n      cidToGidMap,\n      cMap\n    } = properties;\n    const charsets = cff.charset.charset;\n    let charCodeToGlyphId;\n    let glyphId;\n    if (properties.composite) {\n      let invCidToGidMap;\n      if (cidToGidMap?.length > 0) {\n        invCidToGidMap = Object.create(null);\n        for (let i = 0, ii = cidToGidMap.length; i < ii; i++) {\n          const gid = cidToGidMap[i];\n          if (gid !== undefined) {\n            invCidToGidMap[gid] = i;\n          }\n        }\n      }\n      charCodeToGlyphId = Object.create(null);\n      let charCode;\n      if (cff.isCIDFont) {\n        for (glyphId = 0; glyphId < charsets.length; glyphId++) {\n          const cid = charsets[glyphId];\n          charCode = cMap.charCodeOf(cid);\n          if (invCidToGidMap?.[charCode] !== undefined) {\n            charCode = invCidToGidMap[charCode];\n          }\n          charCodeToGlyphId[charCode] = glyphId;\n        }\n      } else {\n        for (glyphId = 0; glyphId < cff.charStrings.count; glyphId++) {\n          charCode = cMap.charCodeOf(glyphId);\n          charCodeToGlyphId[charCode] = glyphId;\n        }\n      }\n      return charCodeToGlyphId;\n    }\n    let encoding = cff.encoding ? cff.encoding.encoding : null;\n    if (properties.isInternalFont) {\n      encoding = properties.defaultEncoding;\n    }\n    charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets);\n    return charCodeToGlyphId;\n  }\n  hasGlyphId(id) {\n    return this.cff.hasGlyphId(id);\n  }\n  _createBuiltInEncoding() {\n    const {\n      charset,\n      encoding\n    } = this.cff;\n    if (!charset || !encoding) {\n      return;\n    }\n    const charsets = charset.charset,\n      encodings = encoding.encoding;\n    const map = [];\n    for (const charCode in encodings) {\n      const glyphId = encodings[charCode];\n      if (glyphId >= 0) {\n        const glyphName = charsets[glyphId];\n        if (glyphName) {\n          map[charCode] = glyphName;\n        }\n      }\n    }\n    if (map.length > 0) {\n      this.properties.builtInEncoding = map;\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/font_renderer.js\n\n\n\n\n\nfunction getUint32(data, offset) {\n  return (data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3]) >>> 0;\n}\nfunction getUint16(data, offset) {\n  return data[offset] << 8 | data[offset + 1];\n}\nfunction getInt16(data, offset) {\n  return (data[offset] << 24 | data[offset + 1] << 16) >> 16;\n}\nfunction getInt8(data, offset) {\n  return data[offset] << 24 >> 24;\n}\nfunction getFloat214(data, offset) {\n  return getInt16(data, offset) / 16384;\n}\nfunction getSubroutineBias(subrs) {\n  const numSubrs = subrs.length;\n  let bias = 32768;\n  if (numSubrs < 1240) {\n    bias = 107;\n  } else if (numSubrs < 33900) {\n    bias = 1131;\n  }\n  return bias;\n}\nfunction parseCmap(data, start, end) {\n  const offset = getUint16(data, start + 2) === 1 ? getUint32(data, start + 8) : getUint32(data, start + 16);\n  const format = getUint16(data, start + offset);\n  let ranges, p, i;\n  if (format === 4) {\n    getUint16(data, start + offset + 2);\n    const segCount = getUint16(data, start + offset + 6) >> 1;\n    p = start + offset + 14;\n    ranges = [];\n    for (i = 0; i < segCount; i++, p += 2) {\n      ranges[i] = {\n        end: getUint16(data, p)\n      };\n    }\n    p += 2;\n    for (i = 0; i < segCount; i++, p += 2) {\n      ranges[i].start = getUint16(data, p);\n    }\n    for (i = 0; i < segCount; i++, p += 2) {\n      ranges[i].idDelta = getUint16(data, p);\n    }\n    for (i = 0; i < segCount; i++, p += 2) {\n      let idOffset = getUint16(data, p);\n      if (idOffset === 0) {\n        continue;\n      }\n      ranges[i].ids = [];\n      for (let j = 0, jj = ranges[i].end - ranges[i].start + 1; j < jj; j++) {\n        ranges[i].ids[j] = getUint16(data, p + idOffset);\n        idOffset += 2;\n      }\n    }\n    return ranges;\n  } else if (format === 12) {\n    const groups = getUint32(data, start + offset + 12);\n    p = start + offset + 16;\n    ranges = [];\n    for (i = 0; i < groups; i++) {\n      start = getUint32(data, p);\n      ranges.push({\n        start,\n        end: getUint32(data, p + 4),\n        idDelta: getUint32(data, p + 8) - start\n      });\n      p += 12;\n    }\n    return ranges;\n  }\n  throw new FormatError(`unsupported cmap: ${format}`);\n}\nfunction parseCff(data, start, end, seacAnalysisEnabled) {\n  const properties = {};\n  const parser = new CFFParser(new Stream(data, start, end - start), properties, seacAnalysisEnabled);\n  const cff = parser.parse();\n  return {\n    glyphs: cff.charStrings.objects,\n    subrs: cff.topDict.privateDict?.subrsIndex?.objects,\n    gsubrs: cff.globalSubrIndex?.objects,\n    isCFFCIDFont: cff.isCIDFont,\n    fdSelect: cff.fdSelect,\n    fdArray: cff.fdArray\n  };\n}\nfunction parseGlyfTable(glyf, loca, isGlyphLocationsLong) {\n  let itemSize, itemDecode;\n  if (isGlyphLocationsLong) {\n    itemSize = 4;\n    itemDecode = getUint32;\n  } else {\n    itemSize = 2;\n    itemDecode = (data, offset) => 2 * getUint16(data, offset);\n  }\n  const glyphs = [];\n  let startOffset = itemDecode(loca, 0);\n  for (let j = itemSize; j < loca.length; j += itemSize) {\n    const endOffset = itemDecode(loca, j);\n    glyphs.push(glyf.subarray(startOffset, endOffset));\n    startOffset = endOffset;\n  }\n  return glyphs;\n}\nfunction lookupCmap(ranges, unicode) {\n  const code = unicode.codePointAt(0);\n  let gid = 0,\n    l = 0,\n    r = ranges.length - 1;\n  while (l < r) {\n    const c = l + r + 1 >> 1;\n    if (code < ranges[c].start) {\n      r = c - 1;\n    } else {\n      l = c;\n    }\n  }\n  if (ranges[l].start <= code && code <= ranges[l].end) {\n    gid = ranges[l].idDelta + (ranges[l].ids ? ranges[l].ids[code - ranges[l].start] : code) & 0xffff;\n  }\n  return {\n    charCode: code,\n    glyphId: gid\n  };\n}\nfunction compileGlyf(code, cmds, font) {\n  function moveTo(x, y) {\n    cmds.push({\n      cmd: \"moveTo\",\n      args: [x, y]\n    });\n  }\n  function lineTo(x, y) {\n    cmds.push({\n      cmd: \"lineTo\",\n      args: [x, y]\n    });\n  }\n  function quadraticCurveTo(xa, ya, x, y) {\n    cmds.push({\n      cmd: \"quadraticCurveTo\",\n      args: [xa, ya, x, y]\n    });\n  }\n  let i = 0;\n  const numberOfContours = getInt16(code, i);\n  let flags;\n  let x = 0,\n    y = 0;\n  i += 10;\n  if (numberOfContours < 0) {\n    do {\n      flags = getUint16(code, i);\n      const glyphIndex = getUint16(code, i + 2);\n      i += 4;\n      let arg1, arg2;\n      if (flags & 0x01) {\n        if (flags & 0x02) {\n          arg1 = getInt16(code, i);\n          arg2 = getInt16(code, i + 2);\n        } else {\n          arg1 = getUint16(code, i);\n          arg2 = getUint16(code, i + 2);\n        }\n        i += 4;\n      } else if (flags & 0x02) {\n        arg1 = getInt8(code, i++);\n        arg2 = getInt8(code, i++);\n      } else {\n        arg1 = code[i++];\n        arg2 = code[i++];\n      }\n      if (flags & 0x02) {\n        x = arg1;\n        y = arg2;\n      } else {\n        x = 0;\n        y = 0;\n      }\n      let scaleX = 1,\n        scaleY = 1,\n        scale01 = 0,\n        scale10 = 0;\n      if (flags & 0x08) {\n        scaleX = scaleY = getFloat214(code, i);\n        i += 2;\n      } else if (flags & 0x40) {\n        scaleX = getFloat214(code, i);\n        scaleY = getFloat214(code, i + 2);\n        i += 4;\n      } else if (flags & 0x80) {\n        scaleX = getFloat214(code, i);\n        scale01 = getFloat214(code, i + 2);\n        scale10 = getFloat214(code, i + 4);\n        scaleY = getFloat214(code, i + 6);\n        i += 8;\n      }\n      const subglyph = font.glyphs[glyphIndex];\n      if (subglyph) {\n        cmds.push({\n          cmd: \"save\"\n        }, {\n          cmd: \"transform\",\n          args: [scaleX, scale01, scale10, scaleY, x, y]\n        });\n        if (!(flags & 0x02)) {}\n        compileGlyf(subglyph, cmds, font);\n        cmds.push({\n          cmd: \"restore\"\n        });\n      }\n    } while (flags & 0x20);\n  } else {\n    const endPtsOfContours = [];\n    let j, jj;\n    for (j = 0; j < numberOfContours; j++) {\n      endPtsOfContours.push(getUint16(code, i));\n      i += 2;\n    }\n    const instructionLength = getUint16(code, i);\n    i += 2 + instructionLength;\n    const numberOfPoints = endPtsOfContours.at(-1) + 1;\n    const points = [];\n    while (points.length < numberOfPoints) {\n      flags = code[i++];\n      let repeat = 1;\n      if (flags & 0x08) {\n        repeat += code[i++];\n      }\n      while (repeat-- > 0) {\n        points.push({\n          flags\n        });\n      }\n    }\n    for (j = 0; j < numberOfPoints; j++) {\n      switch (points[j].flags & 0x12) {\n        case 0x00:\n          x += getInt16(code, i);\n          i += 2;\n          break;\n        case 0x02:\n          x -= code[i++];\n          break;\n        case 0x12:\n          x += code[i++];\n          break;\n      }\n      points[j].x = x;\n    }\n    for (j = 0; j < numberOfPoints; j++) {\n      switch (points[j].flags & 0x24) {\n        case 0x00:\n          y += getInt16(code, i);\n          i += 2;\n          break;\n        case 0x04:\n          y -= code[i++];\n          break;\n        case 0x24:\n          y += code[i++];\n          break;\n      }\n      points[j].y = y;\n    }\n    let startPoint = 0;\n    for (i = 0; i < numberOfContours; i++) {\n      const endPoint = endPtsOfContours[i];\n      const contour = points.slice(startPoint, endPoint + 1);\n      if (contour[0].flags & 1) {\n        contour.push(contour[0]);\n      } else if (contour.at(-1).flags & 1) {\n        contour.unshift(contour.at(-1));\n      } else {\n        const p = {\n          flags: 1,\n          x: (contour[0].x + contour.at(-1).x) / 2,\n          y: (contour[0].y + contour.at(-1).y) / 2\n        };\n        contour.unshift(p);\n        contour.push(p);\n      }\n      moveTo(contour[0].x, contour[0].y);\n      for (j = 1, jj = contour.length; j < jj; j++) {\n        if (contour[j].flags & 1) {\n          lineTo(contour[j].x, contour[j].y);\n        } else if (contour[j + 1].flags & 1) {\n          quadraticCurveTo(contour[j].x, contour[j].y, contour[j + 1].x, contour[j + 1].y);\n          j++;\n        } else {\n          quadraticCurveTo(contour[j].x, contour[j].y, (contour[j].x + contour[j + 1].x) / 2, (contour[j].y + contour[j + 1].y) / 2);\n        }\n      }\n      startPoint = endPoint + 1;\n    }\n  }\n}\nfunction compileCharString(charStringCode, cmds, font, glyphId) {\n  function moveTo(x, y) {\n    cmds.push({\n      cmd: \"moveTo\",\n      args: [x, y]\n    });\n  }\n  function lineTo(x, y) {\n    cmds.push({\n      cmd: \"lineTo\",\n      args: [x, y]\n    });\n  }\n  function bezierCurveTo(x1, y1, x2, y2, x, y) {\n    cmds.push({\n      cmd: \"bezierCurveTo\",\n      args: [x1, y1, x2, y2, x, y]\n    });\n  }\n  const stack = [];\n  let x = 0,\n    y = 0;\n  let stems = 0;\n  function parse(code) {\n    let i = 0;\n    while (i < code.length) {\n      let stackClean = false;\n      let v = code[i++];\n      let xa, xb, ya, yb, y1, y2, y3, n, subrCode;\n      switch (v) {\n        case 1:\n          stems += stack.length >> 1;\n          stackClean = true;\n          break;\n        case 3:\n          stems += stack.length >> 1;\n          stackClean = true;\n          break;\n        case 4:\n          y += stack.pop();\n          moveTo(x, y);\n          stackClean = true;\n          break;\n        case 5:\n          while (stack.length > 0) {\n            x += stack.shift();\n            y += stack.shift();\n            lineTo(x, y);\n          }\n          break;\n        case 6:\n          while (stack.length > 0) {\n            x += stack.shift();\n            lineTo(x, y);\n            if (stack.length === 0) {\n              break;\n            }\n            y += stack.shift();\n            lineTo(x, y);\n          }\n          break;\n        case 7:\n          while (stack.length > 0) {\n            y += stack.shift();\n            lineTo(x, y);\n            if (stack.length === 0) {\n              break;\n            }\n            x += stack.shift();\n            lineTo(x, y);\n          }\n          break;\n        case 8:\n          while (stack.length > 0) {\n            xa = x + stack.shift();\n            ya = y + stack.shift();\n            xb = xa + stack.shift();\n            yb = ya + stack.shift();\n            x = xb + stack.shift();\n            y = yb + stack.shift();\n            bezierCurveTo(xa, ya, xb, yb, x, y);\n          }\n          break;\n        case 10:\n          n = stack.pop();\n          subrCode = null;\n          if (font.isCFFCIDFont) {\n            const fdIndex = font.fdSelect.getFDIndex(glyphId);\n            if (fdIndex >= 0 && fdIndex < font.fdArray.length) {\n              const fontDict = font.fdArray[fdIndex];\n              let subrs;\n              if (fontDict.privateDict?.subrsIndex) {\n                subrs = fontDict.privateDict.subrsIndex.objects;\n              }\n              if (subrs) {\n                n += getSubroutineBias(subrs);\n                subrCode = subrs[n];\n              }\n            } else {\n              warn(\"Invalid fd index for glyph index.\");\n            }\n          } else {\n            subrCode = font.subrs[n + font.subrsBias];\n          }\n          if (subrCode) {\n            parse(subrCode);\n          }\n          break;\n        case 11:\n          return;\n        case 12:\n          v = code[i++];\n          switch (v) {\n            case 34:\n              xa = x + stack.shift();\n              xb = xa + stack.shift();\n              y1 = y + stack.shift();\n              x = xb + stack.shift();\n              bezierCurveTo(xa, y, xb, y1, x, y1);\n              xa = x + stack.shift();\n              xb = xa + stack.shift();\n              x = xb + stack.shift();\n              bezierCurveTo(xa, y1, xb, y, x, y);\n              break;\n            case 35:\n              xa = x + stack.shift();\n              ya = y + stack.shift();\n              xb = xa + stack.shift();\n              yb = ya + stack.shift();\n              x = xb + stack.shift();\n              y = yb + stack.shift();\n              bezierCurveTo(xa, ya, xb, yb, x, y);\n              xa = x + stack.shift();\n              ya = y + stack.shift();\n              xb = xa + stack.shift();\n              yb = ya + stack.shift();\n              x = xb + stack.shift();\n              y = yb + stack.shift();\n              bezierCurveTo(xa, ya, xb, yb, x, y);\n              stack.pop();\n              break;\n            case 36:\n              xa = x + stack.shift();\n              y1 = y + stack.shift();\n              xb = xa + stack.shift();\n              y2 = y1 + stack.shift();\n              x = xb + stack.shift();\n              bezierCurveTo(xa, y1, xb, y2, x, y2);\n              xa = x + stack.shift();\n              xb = xa + stack.shift();\n              y3 = y2 + stack.shift();\n              x = xb + stack.shift();\n              bezierCurveTo(xa, y2, xb, y3, x, y);\n              break;\n            case 37:\n              const x0 = x,\n                y0 = y;\n              xa = x + stack.shift();\n              ya = y + stack.shift();\n              xb = xa + stack.shift();\n              yb = ya + stack.shift();\n              x = xb + stack.shift();\n              y = yb + stack.shift();\n              bezierCurveTo(xa, ya, xb, yb, x, y);\n              xa = x + stack.shift();\n              ya = y + stack.shift();\n              xb = xa + stack.shift();\n              yb = ya + stack.shift();\n              x = xb;\n              y = yb;\n              if (Math.abs(x - x0) > Math.abs(y - y0)) {\n                x += stack.shift();\n              } else {\n                y += stack.shift();\n              }\n              bezierCurveTo(xa, ya, xb, yb, x, y);\n              break;\n            default:\n              throw new FormatError(`unknown operator: 12 ${v}`);\n          }\n          break;\n        case 14:\n          if (stack.length >= 4) {\n            const achar = stack.pop();\n            const bchar = stack.pop();\n            y = stack.pop();\n            x = stack.pop();\n            cmds.push({\n              cmd: \"save\"\n            }, {\n              cmd: \"translate\",\n              args: [x, y]\n            });\n            let cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[StandardEncoding[achar]]));\n            compileCharString(font.glyphs[cmap.glyphId], cmds, font, cmap.glyphId);\n            cmds.push({\n              cmd: \"restore\"\n            });\n            cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[StandardEncoding[bchar]]));\n            compileCharString(font.glyphs[cmap.glyphId], cmds, font, cmap.glyphId);\n          }\n          return;\n        case 18:\n          stems += stack.length >> 1;\n          stackClean = true;\n          break;\n        case 19:\n          stems += stack.length >> 1;\n          i += stems + 7 >> 3;\n          stackClean = true;\n          break;\n        case 20:\n          stems += stack.length >> 1;\n          i += stems + 7 >> 3;\n          stackClean = true;\n          break;\n        case 21:\n          y += stack.pop();\n          x += stack.pop();\n          moveTo(x, y);\n          stackClean = true;\n          break;\n        case 22:\n          x += stack.pop();\n          moveTo(x, y);\n          stackClean = true;\n          break;\n        case 23:\n          stems += stack.length >> 1;\n          stackClean = true;\n          break;\n        case 24:\n          while (stack.length > 2) {\n            xa = x + stack.shift();\n            ya = y + stack.shift();\n            xb = xa + stack.shift();\n            yb = ya + stack.shift();\n            x = xb + stack.shift();\n            y = yb + stack.shift();\n            bezierCurveTo(xa, ya, xb, yb, x, y);\n          }\n          x += stack.shift();\n          y += stack.shift();\n          lineTo(x, y);\n          break;\n        case 25:\n          while (stack.length > 6) {\n            x += stack.shift();\n            y += stack.shift();\n            lineTo(x, y);\n          }\n          xa = x + stack.shift();\n          ya = y + stack.shift();\n          xb = xa + stack.shift();\n          yb = ya + stack.shift();\n          x = xb + stack.shift();\n          y = yb + stack.shift();\n          bezierCurveTo(xa, ya, xb, yb, x, y);\n          break;\n        case 26:\n          if (stack.length % 2) {\n            x += stack.shift();\n          }\n          while (stack.length > 0) {\n            xa = x;\n            ya = y + stack.shift();\n            xb = xa + stack.shift();\n            yb = ya + stack.shift();\n            x = xb;\n            y = yb + stack.shift();\n            bezierCurveTo(xa, ya, xb, yb, x, y);\n          }\n          break;\n        case 27:\n          if (stack.length % 2) {\n            y += stack.shift();\n          }\n          while (stack.length > 0) {\n            xa = x + stack.shift();\n            ya = y;\n            xb = xa + stack.shift();\n            yb = ya + stack.shift();\n            x = xb + stack.shift();\n            y = yb;\n            bezierCurveTo(xa, ya, xb, yb, x, y);\n          }\n          break;\n        case 28:\n          stack.push((code[i] << 24 | code[i + 1] << 16) >> 16);\n          i += 2;\n          break;\n        case 29:\n          n = stack.pop() + font.gsubrsBias;\n          subrCode = font.gsubrs[n];\n          if (subrCode) {\n            parse(subrCode);\n          }\n          break;\n        case 30:\n          while (stack.length > 0) {\n            xa = x;\n            ya = y + stack.shift();\n            xb = xa + stack.shift();\n            yb = ya + stack.shift();\n            x = xb + stack.shift();\n            y = yb + (stack.length === 1 ? stack.shift() : 0);\n            bezierCurveTo(xa, ya, xb, yb, x, y);\n            if (stack.length === 0) {\n              break;\n            }\n            xa = x + stack.shift();\n            ya = y;\n            xb = xa + stack.shift();\n            yb = ya + stack.shift();\n            y = yb + stack.shift();\n            x = xb + (stack.length === 1 ? stack.shift() : 0);\n            bezierCurveTo(xa, ya, xb, yb, x, y);\n          }\n          break;\n        case 31:\n          while (stack.length > 0) {\n            xa = x + stack.shift();\n            ya = y;\n            xb = xa + stack.shift();\n            yb = ya + stack.shift();\n            y = yb + stack.shift();\n            x = xb + (stack.length === 1 ? stack.shift() : 0);\n            bezierCurveTo(xa, ya, xb, yb, x, y);\n            if (stack.length === 0) {\n              break;\n            }\n            xa = x;\n            ya = y + stack.shift();\n            xb = xa + stack.shift();\n            yb = ya + stack.shift();\n            x = xb + stack.shift();\n            y = yb + (stack.length === 1 ? stack.shift() : 0);\n            bezierCurveTo(xa, ya, xb, yb, x, y);\n          }\n          break;\n        default:\n          if (v < 32) {\n            throw new FormatError(`unknown operator: ${v}`);\n          }\n          if (v < 247) {\n            stack.push(v - 139);\n          } else if (v < 251) {\n            stack.push((v - 247) * 256 + code[i++] + 108);\n          } else if (v < 255) {\n            stack.push(-(v - 251) * 256 - code[i++] - 108);\n          } else {\n            stack.push((code[i] << 24 | code[i + 1] << 16 | code[i + 2] << 8 | code[i + 3]) / 65536);\n            i += 4;\n          }\n          break;\n      }\n      if (stackClean) {\n        stack.length = 0;\n      }\n    }\n  }\n  parse(charStringCode);\n}\nconst NOOP = [];\nclass CompiledFont {\n  constructor(fontMatrix) {\n    if (this.constructor === CompiledFont) {\n      unreachable(\"Cannot initialize CompiledFont.\");\n    }\n    this.fontMatrix = fontMatrix;\n    this.compiledGlyphs = Object.create(null);\n    this.compiledCharCodeToGlyphId = Object.create(null);\n  }\n  getPathJs(unicode) {\n    const {\n      charCode,\n      glyphId\n    } = lookupCmap(this.cmap, unicode);\n    let fn = this.compiledGlyphs[glyphId];\n    if (!fn) {\n      try {\n        fn = this.compileGlyph(this.glyphs[glyphId], glyphId);\n        this.compiledGlyphs[glyphId] = fn;\n      } catch (ex) {\n        this.compiledGlyphs[glyphId] = NOOP;\n        if (this.compiledCharCodeToGlyphId[charCode] === undefined) {\n          this.compiledCharCodeToGlyphId[charCode] = glyphId;\n        }\n        throw ex;\n      }\n    }\n    if (this.compiledCharCodeToGlyphId[charCode] === undefined) {\n      this.compiledCharCodeToGlyphId[charCode] = glyphId;\n    }\n    return fn;\n  }\n  compileGlyph(code, glyphId) {\n    if (!code || code.length === 0 || code[0] === 14) {\n      return NOOP;\n    }\n    let fontMatrix = this.fontMatrix;\n    if (this.isCFFCIDFont) {\n      const fdIndex = this.fdSelect.getFDIndex(glyphId);\n      if (fdIndex >= 0 && fdIndex < this.fdArray.length) {\n        const fontDict = this.fdArray[fdIndex];\n        fontMatrix = fontDict.getByName(\"FontMatrix\") || FONT_IDENTITY_MATRIX;\n      } else {\n        warn(\"Invalid fd index for glyph index.\");\n      }\n    }\n    const cmds = [{\n      cmd: \"save\"\n    }, {\n      cmd: \"transform\",\n      args: fontMatrix.slice()\n    }, {\n      cmd: \"scale\",\n      args: [\"size\", \"-size\"]\n    }];\n    this.compileGlyphImpl(code, cmds, glyphId);\n    cmds.push({\n      cmd: \"restore\"\n    });\n    return cmds;\n  }\n  compileGlyphImpl() {\n    unreachable(\"Children classes should implement this.\");\n  }\n  hasBuiltPath(unicode) {\n    const {\n      charCode,\n      glyphId\n    } = lookupCmap(this.cmap, unicode);\n    return this.compiledGlyphs[glyphId] !== undefined && this.compiledCharCodeToGlyphId[charCode] !== undefined;\n  }\n}\nclass TrueTypeCompiled extends CompiledFont {\n  constructor(glyphs, cmap, fontMatrix) {\n    super(fontMatrix || [0.000488, 0, 0, 0.000488, 0, 0]);\n    this.glyphs = glyphs;\n    this.cmap = cmap;\n  }\n  compileGlyphImpl(code, cmds) {\n    compileGlyf(code, cmds, this);\n  }\n}\nclass Type2Compiled extends CompiledFont {\n  constructor(cffInfo, cmap, fontMatrix, glyphNameMap) {\n    super(fontMatrix || [0.001, 0, 0, 0.001, 0, 0]);\n    this.glyphs = cffInfo.glyphs;\n    this.gsubrs = cffInfo.gsubrs || [];\n    this.subrs = cffInfo.subrs || [];\n    this.cmap = cmap;\n    this.glyphNameMap = glyphNameMap || getGlyphsUnicode();\n    this.gsubrsBias = getSubroutineBias(this.gsubrs);\n    this.subrsBias = getSubroutineBias(this.subrs);\n    this.isCFFCIDFont = cffInfo.isCFFCIDFont;\n    this.fdSelect = cffInfo.fdSelect;\n    this.fdArray = cffInfo.fdArray;\n  }\n  compileGlyphImpl(code, cmds, glyphId) {\n    compileCharString(code, cmds, this, glyphId);\n  }\n}\nclass FontRendererFactory {\n  static create(font, seacAnalysisEnabled) {\n    const data = new Uint8Array(font.data);\n    let cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm;\n    const numTables = getUint16(data, 4);\n    for (let i = 0, p = 12; i < numTables; i++, p += 16) {\n      const tag = bytesToString(data.subarray(p, p + 4));\n      const offset = getUint32(data, p + 8);\n      const length = getUint32(data, p + 12);\n      switch (tag) {\n        case \"cmap\":\n          cmap = parseCmap(data, offset, offset + length);\n          break;\n        case \"glyf\":\n          glyf = data.subarray(offset, offset + length);\n          break;\n        case \"loca\":\n          loca = data.subarray(offset, offset + length);\n          break;\n        case \"head\":\n          unitsPerEm = getUint16(data, offset + 18);\n          indexToLocFormat = getUint16(data, offset + 50);\n          break;\n        case \"CFF \":\n          cff = parseCff(data, offset, offset + length, seacAnalysisEnabled);\n          break;\n      }\n    }\n    if (glyf) {\n      const fontMatrix = !unitsPerEm ? font.fontMatrix : [1 / unitsPerEm, 0, 0, 1 / unitsPerEm, 0, 0];\n      return new TrueTypeCompiled(parseGlyfTable(glyf, loca, indexToLocFormat), cmap, fontMatrix);\n    }\n    return new Type2Compiled(cff, cmap, font.fontMatrix, font.glyphNameMap);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/metrics.js\n\nconst getMetrics = getLookupTableFactory(function (t) {\n  t.Courier = 600;\n  t[\"Courier-Bold\"] = 600;\n  t[\"Courier-BoldOblique\"] = 600;\n  t[\"Courier-Oblique\"] = 600;\n  t.Helvetica = getLookupTableFactory(function (t) {\n    t.space = 278;\n    t.exclam = 278;\n    t.quotedbl = 355;\n    t.numbersign = 556;\n    t.dollar = 556;\n    t.percent = 889;\n    t.ampersand = 667;\n    t.quoteright = 222;\n    t.parenleft = 333;\n    t.parenright = 333;\n    t.asterisk = 389;\n    t.plus = 584;\n    t.comma = 278;\n    t.hyphen = 333;\n    t.period = 278;\n    t.slash = 278;\n    t.zero = 556;\n    t.one = 556;\n    t.two = 556;\n    t.three = 556;\n    t.four = 556;\n    t.five = 556;\n    t.six = 556;\n    t.seven = 556;\n    t.eight = 556;\n    t.nine = 556;\n    t.colon = 278;\n    t.semicolon = 278;\n    t.less = 584;\n    t.equal = 584;\n    t.greater = 584;\n    t.question = 556;\n    t.at = 1015;\n    t.A = 667;\n    t.B = 667;\n    t.C = 722;\n    t.D = 722;\n    t.E = 667;\n    t.F = 611;\n    t.G = 778;\n    t.H = 722;\n    t.I = 278;\n    t.J = 500;\n    t.K = 667;\n    t.L = 556;\n    t.M = 833;\n    t.N = 722;\n    t.O = 778;\n    t.P = 667;\n    t.Q = 778;\n    t.R = 722;\n    t.S = 667;\n    t.T = 611;\n    t.U = 722;\n    t.V = 667;\n    t.W = 944;\n    t.X = 667;\n    t.Y = 667;\n    t.Z = 611;\n    t.bracketleft = 278;\n    t.backslash = 278;\n    t.bracketright = 278;\n    t.asciicircum = 469;\n    t.underscore = 556;\n    t.quoteleft = 222;\n    t.a = 556;\n    t.b = 556;\n    t.c = 500;\n    t.d = 556;\n    t.e = 556;\n    t.f = 278;\n    t.g = 556;\n    t.h = 556;\n    t.i = 222;\n    t.j = 222;\n    t.k = 500;\n    t.l = 222;\n    t.m = 833;\n    t.n = 556;\n    t.o = 556;\n    t.p = 556;\n    t.q = 556;\n    t.r = 333;\n    t.s = 500;\n    t.t = 278;\n    t.u = 556;\n    t.v = 500;\n    t.w = 722;\n    t.x = 500;\n    t.y = 500;\n    t.z = 500;\n    t.braceleft = 334;\n    t.bar = 260;\n    t.braceright = 334;\n    t.asciitilde = 584;\n    t.exclamdown = 333;\n    t.cent = 556;\n    t.sterling = 556;\n    t.fraction = 167;\n    t.yen = 556;\n    t.florin = 556;\n    t.section = 556;\n    t.currency = 556;\n    t.quotesingle = 191;\n    t.quotedblleft = 333;\n    t.guillemotleft = 556;\n    t.guilsinglleft = 333;\n    t.guilsinglright = 333;\n    t.fi = 500;\n    t.fl = 500;\n    t.endash = 556;\n    t.dagger = 556;\n    t.daggerdbl = 556;\n    t.periodcentered = 278;\n    t.paragraph = 537;\n    t.bullet = 350;\n    t.quotesinglbase = 222;\n    t.quotedblbase = 333;\n    t.quotedblright = 333;\n    t.guillemotright = 556;\n    t.ellipsis = 1000;\n    t.perthousand = 1000;\n    t.questiondown = 611;\n    t.grave = 333;\n    t.acute = 333;\n    t.circumflex = 333;\n    t.tilde = 333;\n    t.macron = 333;\n    t.breve = 333;\n    t.dotaccent = 333;\n    t.dieresis = 333;\n    t.ring = 333;\n    t.cedilla = 333;\n    t.hungarumlaut = 333;\n    t.ogonek = 333;\n    t.caron = 333;\n    t.emdash = 1000;\n    t.AE = 1000;\n    t.ordfeminine = 370;\n    t.Lslash = 556;\n    t.Oslash = 778;\n    t.OE = 1000;\n    t.ordmasculine = 365;\n    t.ae = 889;\n    t.dotlessi = 278;\n    t.lslash = 222;\n    t.oslash = 611;\n    t.oe = 944;\n    t.germandbls = 611;\n    t.Idieresis = 278;\n    t.eacute = 556;\n    t.abreve = 556;\n    t.uhungarumlaut = 556;\n    t.ecaron = 556;\n    t.Ydieresis = 667;\n    t.divide = 584;\n    t.Yacute = 667;\n    t.Acircumflex = 667;\n    t.aacute = 556;\n    t.Ucircumflex = 722;\n    t.yacute = 500;\n    t.scommaaccent = 500;\n    t.ecircumflex = 556;\n    t.Uring = 722;\n    t.Udieresis = 722;\n    t.aogonek = 556;\n    t.Uacute = 722;\n    t.uogonek = 556;\n    t.Edieresis = 667;\n    t.Dcroat = 722;\n    t.commaaccent = 250;\n    t.copyright = 737;\n    t.Emacron = 667;\n    t.ccaron = 500;\n    t.aring = 556;\n    t.Ncommaaccent = 722;\n    t.lacute = 222;\n    t.agrave = 556;\n    t.Tcommaaccent = 611;\n    t.Cacute = 722;\n    t.atilde = 556;\n    t.Edotaccent = 667;\n    t.scaron = 500;\n    t.scedilla = 500;\n    t.iacute = 278;\n    t.lozenge = 471;\n    t.Rcaron = 722;\n    t.Gcommaaccent = 778;\n    t.ucircumflex = 556;\n    t.acircumflex = 556;\n    t.Amacron = 667;\n    t.rcaron = 333;\n    t.ccedilla = 500;\n    t.Zdotaccent = 611;\n    t.Thorn = 667;\n    t.Omacron = 778;\n    t.Racute = 722;\n    t.Sacute = 667;\n    t.dcaron = 643;\n    t.Umacron = 722;\n    t.uring = 556;\n    t.threesuperior = 333;\n    t.Ograve = 778;\n    t.Agrave = 667;\n    t.Abreve = 667;\n    t.multiply = 584;\n    t.uacute = 556;\n    t.Tcaron = 611;\n    t.partialdiff = 476;\n    t.ydieresis = 500;\n    t.Nacute = 722;\n    t.icircumflex = 278;\n    t.Ecircumflex = 667;\n    t.adieresis = 556;\n    t.edieresis = 556;\n    t.cacute = 500;\n    t.nacute = 556;\n    t.umacron = 556;\n    t.Ncaron = 722;\n    t.Iacute = 278;\n    t.plusminus = 584;\n    t.brokenbar = 260;\n    t.registered = 737;\n    t.Gbreve = 778;\n    t.Idotaccent = 278;\n    t.summation = 600;\n    t.Egrave = 667;\n    t.racute = 333;\n    t.omacron = 556;\n    t.Zacute = 611;\n    t.Zcaron = 611;\n    t.greaterequal = 549;\n    t.Eth = 722;\n    t.Ccedilla = 722;\n    t.lcommaaccent = 222;\n    t.tcaron = 317;\n    t.eogonek = 556;\n    t.Uogonek = 722;\n    t.Aacute = 667;\n    t.Adieresis = 667;\n    t.egrave = 556;\n    t.zacute = 500;\n    t.iogonek = 222;\n    t.Oacute = 778;\n    t.oacute = 556;\n    t.amacron = 556;\n    t.sacute = 500;\n    t.idieresis = 278;\n    t.Ocircumflex = 778;\n    t.Ugrave = 722;\n    t.Delta = 612;\n    t.thorn = 556;\n    t.twosuperior = 333;\n    t.Odieresis = 778;\n    t.mu = 556;\n    t.igrave = 278;\n    t.ohungarumlaut = 556;\n    t.Eogonek = 667;\n    t.dcroat = 556;\n    t.threequarters = 834;\n    t.Scedilla = 667;\n    t.lcaron = 299;\n    t.Kcommaaccent = 667;\n    t.Lacute = 556;\n    t.trademark = 1000;\n    t.edotaccent = 556;\n    t.Igrave = 278;\n    t.Imacron = 278;\n    t.Lcaron = 556;\n    t.onehalf = 834;\n    t.lessequal = 549;\n    t.ocircumflex = 556;\n    t.ntilde = 556;\n    t.Uhungarumlaut = 722;\n    t.Eacute = 667;\n    t.emacron = 556;\n    t.gbreve = 556;\n    t.onequarter = 834;\n    t.Scaron = 667;\n    t.Scommaaccent = 667;\n    t.Ohungarumlaut = 778;\n    t.degree = 400;\n    t.ograve = 556;\n    t.Ccaron = 722;\n    t.ugrave = 556;\n    t.radical = 453;\n    t.Dcaron = 722;\n    t.rcommaaccent = 333;\n    t.Ntilde = 722;\n    t.otilde = 556;\n    t.Rcommaaccent = 722;\n    t.Lcommaaccent = 556;\n    t.Atilde = 667;\n    t.Aogonek = 667;\n    t.Aring = 667;\n    t.Otilde = 778;\n    t.zdotaccent = 500;\n    t.Ecaron = 667;\n    t.Iogonek = 278;\n    t.kcommaaccent = 500;\n    t.minus = 584;\n    t.Icircumflex = 278;\n    t.ncaron = 556;\n    t.tcommaaccent = 278;\n    t.logicalnot = 584;\n    t.odieresis = 556;\n    t.udieresis = 556;\n    t.notequal = 549;\n    t.gcommaaccent = 556;\n    t.eth = 556;\n    t.zcaron = 500;\n    t.ncommaaccent = 556;\n    t.onesuperior = 333;\n    t.imacron = 278;\n    t.Euro = 556;\n  });\n  t[\"Helvetica-Bold\"] = getLookupTableFactory(function (t) {\n    t.space = 278;\n    t.exclam = 333;\n    t.quotedbl = 474;\n    t.numbersign = 556;\n    t.dollar = 556;\n    t.percent = 889;\n    t.ampersand = 722;\n    t.quoteright = 278;\n    t.parenleft = 333;\n    t.parenright = 333;\n    t.asterisk = 389;\n    t.plus = 584;\n    t.comma = 278;\n    t.hyphen = 333;\n    t.period = 278;\n    t.slash = 278;\n    t.zero = 556;\n    t.one = 556;\n    t.two = 556;\n    t.three = 556;\n    t.four = 556;\n    t.five = 556;\n    t.six = 556;\n    t.seven = 556;\n    t.eight = 556;\n    t.nine = 556;\n    t.colon = 333;\n    t.semicolon = 333;\n    t.less = 584;\n    t.equal = 584;\n    t.greater = 584;\n    t.question = 611;\n    t.at = 975;\n    t.A = 722;\n    t.B = 722;\n    t.C = 722;\n    t.D = 722;\n    t.E = 667;\n    t.F = 611;\n    t.G = 778;\n    t.H = 722;\n    t.I = 278;\n    t.J = 556;\n    t.K = 722;\n    t.L = 611;\n    t.M = 833;\n    t.N = 722;\n    t.O = 778;\n    t.P = 667;\n    t.Q = 778;\n    t.R = 722;\n    t.S = 667;\n    t.T = 611;\n    t.U = 722;\n    t.V = 667;\n    t.W = 944;\n    t.X = 667;\n    t.Y = 667;\n    t.Z = 611;\n    t.bracketleft = 333;\n    t.backslash = 278;\n    t.bracketright = 333;\n    t.asciicircum = 584;\n    t.underscore = 556;\n    t.quoteleft = 278;\n    t.a = 556;\n    t.b = 611;\n    t.c = 556;\n    t.d = 611;\n    t.e = 556;\n    t.f = 333;\n    t.g = 611;\n    t.h = 611;\n    t.i = 278;\n    t.j = 278;\n    t.k = 556;\n    t.l = 278;\n    t.m = 889;\n    t.n = 611;\n    t.o = 611;\n    t.p = 611;\n    t.q = 611;\n    t.r = 389;\n    t.s = 556;\n    t.t = 333;\n    t.u = 611;\n    t.v = 556;\n    t.w = 778;\n    t.x = 556;\n    t.y = 556;\n    t.z = 500;\n    t.braceleft = 389;\n    t.bar = 280;\n    t.braceright = 389;\n    t.asciitilde = 584;\n    t.exclamdown = 333;\n    t.cent = 556;\n    t.sterling = 556;\n    t.fraction = 167;\n    t.yen = 556;\n    t.florin = 556;\n    t.section = 556;\n    t.currency = 556;\n    t.quotesingle = 238;\n    t.quotedblleft = 500;\n    t.guillemotleft = 556;\n    t.guilsinglleft = 333;\n    t.guilsinglright = 333;\n    t.fi = 611;\n    t.fl = 611;\n    t.endash = 556;\n    t.dagger = 556;\n    t.daggerdbl = 556;\n    t.periodcentered = 278;\n    t.paragraph = 556;\n    t.bullet = 350;\n    t.quotesinglbase = 278;\n    t.quotedblbase = 500;\n    t.quotedblright = 500;\n    t.guillemotright = 556;\n    t.ellipsis = 1000;\n    t.perthousand = 1000;\n    t.questiondown = 611;\n    t.grave = 333;\n    t.acute = 333;\n    t.circumflex = 333;\n    t.tilde = 333;\n    t.macron = 333;\n    t.breve = 333;\n    t.dotaccent = 333;\n    t.dieresis = 333;\n    t.ring = 333;\n    t.cedilla = 333;\n    t.hungarumlaut = 333;\n    t.ogonek = 333;\n    t.caron = 333;\n    t.emdash = 1000;\n    t.AE = 1000;\n    t.ordfeminine = 370;\n    t.Lslash = 611;\n    t.Oslash = 778;\n    t.OE = 1000;\n    t.ordmasculine = 365;\n    t.ae = 889;\n    t.dotlessi = 278;\n    t.lslash = 278;\n    t.oslash = 611;\n    t.oe = 944;\n    t.germandbls = 611;\n    t.Idieresis = 278;\n    t.eacute = 556;\n    t.abreve = 556;\n    t.uhungarumlaut = 611;\n    t.ecaron = 556;\n    t.Ydieresis = 667;\n    t.divide = 584;\n    t.Yacute = 667;\n    t.Acircumflex = 722;\n    t.aacute = 556;\n    t.Ucircumflex = 722;\n    t.yacute = 556;\n    t.scommaaccent = 556;\n    t.ecircumflex = 556;\n    t.Uring = 722;\n    t.Udieresis = 722;\n    t.aogonek = 556;\n    t.Uacute = 722;\n    t.uogonek = 611;\n    t.Edieresis = 667;\n    t.Dcroat = 722;\n    t.commaaccent = 250;\n    t.copyright = 737;\n    t.Emacron = 667;\n    t.ccaron = 556;\n    t.aring = 556;\n    t.Ncommaaccent = 722;\n    t.lacute = 278;\n    t.agrave = 556;\n    t.Tcommaaccent = 611;\n    t.Cacute = 722;\n    t.atilde = 556;\n    t.Edotaccent = 667;\n    t.scaron = 556;\n    t.scedilla = 556;\n    t.iacute = 278;\n    t.lozenge = 494;\n    t.Rcaron = 722;\n    t.Gcommaaccent = 778;\n    t.ucircumflex = 611;\n    t.acircumflex = 556;\n    t.Amacron = 722;\n    t.rcaron = 389;\n    t.ccedilla = 556;\n    t.Zdotaccent = 611;\n    t.Thorn = 667;\n    t.Omacron = 778;\n    t.Racute = 722;\n    t.Sacute = 667;\n    t.dcaron = 743;\n    t.Umacron = 722;\n    t.uring = 611;\n    t.threesuperior = 333;\n    t.Ograve = 778;\n    t.Agrave = 722;\n    t.Abreve = 722;\n    t.multiply = 584;\n    t.uacute = 611;\n    t.Tcaron = 611;\n    t.partialdiff = 494;\n    t.ydieresis = 556;\n    t.Nacute = 722;\n    t.icircumflex = 278;\n    t.Ecircumflex = 667;\n    t.adieresis = 556;\n    t.edieresis = 556;\n    t.cacute = 556;\n    t.nacute = 611;\n    t.umacron = 611;\n    t.Ncaron = 722;\n    t.Iacute = 278;\n    t.plusminus = 584;\n    t.brokenbar = 280;\n    t.registered = 737;\n    t.Gbreve = 778;\n    t.Idotaccent = 278;\n    t.summation = 600;\n    t.Egrave = 667;\n    t.racute = 389;\n    t.omacron = 611;\n    t.Zacute = 611;\n    t.Zcaron = 611;\n    t.greaterequal = 549;\n    t.Eth = 722;\n    t.Ccedilla = 722;\n    t.lcommaaccent = 278;\n    t.tcaron = 389;\n    t.eogonek = 556;\n    t.Uogonek = 722;\n    t.Aacute = 722;\n    t.Adieresis = 722;\n    t.egrave = 556;\n    t.zacute = 500;\n    t.iogonek = 278;\n    t.Oacute = 778;\n    t.oacute = 611;\n    t.amacron = 556;\n    t.sacute = 556;\n    t.idieresis = 278;\n    t.Ocircumflex = 778;\n    t.Ugrave = 722;\n    t.Delta = 612;\n    t.thorn = 611;\n    t.twosuperior = 333;\n    t.Odieresis = 778;\n    t.mu = 611;\n    t.igrave = 278;\n    t.ohungarumlaut = 611;\n    t.Eogonek = 667;\n    t.dcroat = 611;\n    t.threequarters = 834;\n    t.Scedilla = 667;\n    t.lcaron = 400;\n    t.Kcommaaccent = 722;\n    t.Lacute = 611;\n    t.trademark = 1000;\n    t.edotaccent = 556;\n    t.Igrave = 278;\n    t.Imacron = 278;\n    t.Lcaron = 611;\n    t.onehalf = 834;\n    t.lessequal = 549;\n    t.ocircumflex = 611;\n    t.ntilde = 611;\n    t.Uhungarumlaut = 722;\n    t.Eacute = 667;\n    t.emacron = 556;\n    t.gbreve = 611;\n    t.onequarter = 834;\n    t.Scaron = 667;\n    t.Scommaaccent = 667;\n    t.Ohungarumlaut = 778;\n    t.degree = 400;\n    t.ograve = 611;\n    t.Ccaron = 722;\n    t.ugrave = 611;\n    t.radical = 549;\n    t.Dcaron = 722;\n    t.rcommaaccent = 389;\n    t.Ntilde = 722;\n    t.otilde = 611;\n    t.Rcommaaccent = 722;\n    t.Lcommaaccent = 611;\n    t.Atilde = 722;\n    t.Aogonek = 722;\n    t.Aring = 722;\n    t.Otilde = 778;\n    t.zdotaccent = 500;\n    t.Ecaron = 667;\n    t.Iogonek = 278;\n    t.kcommaaccent = 556;\n    t.minus = 584;\n    t.Icircumflex = 278;\n    t.ncaron = 611;\n    t.tcommaaccent = 333;\n    t.logicalnot = 584;\n    t.odieresis = 611;\n    t.udieresis = 611;\n    t.notequal = 549;\n    t.gcommaaccent = 611;\n    t.eth = 611;\n    t.zcaron = 500;\n    t.ncommaaccent = 611;\n    t.onesuperior = 333;\n    t.imacron = 278;\n    t.Euro = 556;\n  });\n  t[\"Helvetica-BoldOblique\"] = getLookupTableFactory(function (t) {\n    t.space = 278;\n    t.exclam = 333;\n    t.quotedbl = 474;\n    t.numbersign = 556;\n    t.dollar = 556;\n    t.percent = 889;\n    t.ampersand = 722;\n    t.quoteright = 278;\n    t.parenleft = 333;\n    t.parenright = 333;\n    t.asterisk = 389;\n    t.plus = 584;\n    t.comma = 278;\n    t.hyphen = 333;\n    t.period = 278;\n    t.slash = 278;\n    t.zero = 556;\n    t.one = 556;\n    t.two = 556;\n    t.three = 556;\n    t.four = 556;\n    t.five = 556;\n    t.six = 556;\n    t.seven = 556;\n    t.eight = 556;\n    t.nine = 556;\n    t.colon = 333;\n    t.semicolon = 333;\n    t.less = 584;\n    t.equal = 584;\n    t.greater = 584;\n    t.question = 611;\n    t.at = 975;\n    t.A = 722;\n    t.B = 722;\n    t.C = 722;\n    t.D = 722;\n    t.E = 667;\n    t.F = 611;\n    t.G = 778;\n    t.H = 722;\n    t.I = 278;\n    t.J = 556;\n    t.K = 722;\n    t.L = 611;\n    t.M = 833;\n    t.N = 722;\n    t.O = 778;\n    t.P = 667;\n    t.Q = 778;\n    t.R = 722;\n    t.S = 667;\n    t.T = 611;\n    t.U = 722;\n    t.V = 667;\n    t.W = 944;\n    t.X = 667;\n    t.Y = 667;\n    t.Z = 611;\n    t.bracketleft = 333;\n    t.backslash = 278;\n    t.bracketright = 333;\n    t.asciicircum = 584;\n    t.underscore = 556;\n    t.quoteleft = 278;\n    t.a = 556;\n    t.b = 611;\n    t.c = 556;\n    t.d = 611;\n    t.e = 556;\n    t.f = 333;\n    t.g = 611;\n    t.h = 611;\n    t.i = 278;\n    t.j = 278;\n    t.k = 556;\n    t.l = 278;\n    t.m = 889;\n    t.n = 611;\n    t.o = 611;\n    t.p = 611;\n    t.q = 611;\n    t.r = 389;\n    t.s = 556;\n    t.t = 333;\n    t.u = 611;\n    t.v = 556;\n    t.w = 778;\n    t.x = 556;\n    t.y = 556;\n    t.z = 500;\n    t.braceleft = 389;\n    t.bar = 280;\n    t.braceright = 389;\n    t.asciitilde = 584;\n    t.exclamdown = 333;\n    t.cent = 556;\n    t.sterling = 556;\n    t.fraction = 167;\n    t.yen = 556;\n    t.florin = 556;\n    t.section = 556;\n    t.currency = 556;\n    t.quotesingle = 238;\n    t.quotedblleft = 500;\n    t.guillemotleft = 556;\n    t.guilsinglleft = 333;\n    t.guilsinglright = 333;\n    t.fi = 611;\n    t.fl = 611;\n    t.endash = 556;\n    t.dagger = 556;\n    t.daggerdbl = 556;\n    t.periodcentered = 278;\n    t.paragraph = 556;\n    t.bullet = 350;\n    t.quotesinglbase = 278;\n    t.quotedblbase = 500;\n    t.quotedblright = 500;\n    t.guillemotright = 556;\n    t.ellipsis = 1000;\n    t.perthousand = 1000;\n    t.questiondown = 611;\n    t.grave = 333;\n    t.acute = 333;\n    t.circumflex = 333;\n    t.tilde = 333;\n    t.macron = 333;\n    t.breve = 333;\n    t.dotaccent = 333;\n    t.dieresis = 333;\n    t.ring = 333;\n    t.cedilla = 333;\n    t.hungarumlaut = 333;\n    t.ogonek = 333;\n    t.caron = 333;\n    t.emdash = 1000;\n    t.AE = 1000;\n    t.ordfeminine = 370;\n    t.Lslash = 611;\n    t.Oslash = 778;\n    t.OE = 1000;\n    t.ordmasculine = 365;\n    t.ae = 889;\n    t.dotlessi = 278;\n    t.lslash = 278;\n    t.oslash = 611;\n    t.oe = 944;\n    t.germandbls = 611;\n    t.Idieresis = 278;\n    t.eacute = 556;\n    t.abreve = 556;\n    t.uhungarumlaut = 611;\n    t.ecaron = 556;\n    t.Ydieresis = 667;\n    t.divide = 584;\n    t.Yacute = 667;\n    t.Acircumflex = 722;\n    t.aacute = 556;\n    t.Ucircumflex = 722;\n    t.yacute = 556;\n    t.scommaaccent = 556;\n    t.ecircumflex = 556;\n    t.Uring = 722;\n    t.Udieresis = 722;\n    t.aogonek = 556;\n    t.Uacute = 722;\n    t.uogonek = 611;\n    t.Edieresis = 667;\n    t.Dcroat = 722;\n    t.commaaccent = 250;\n    t.copyright = 737;\n    t.Emacron = 667;\n    t.ccaron = 556;\n    t.aring = 556;\n    t.Ncommaaccent = 722;\n    t.lacute = 278;\n    t.agrave = 556;\n    t.Tcommaaccent = 611;\n    t.Cacute = 722;\n    t.atilde = 556;\n    t.Edotaccent = 667;\n    t.scaron = 556;\n    t.scedilla = 556;\n    t.iacute = 278;\n    t.lozenge = 494;\n    t.Rcaron = 722;\n    t.Gcommaaccent = 778;\n    t.ucircumflex = 611;\n    t.acircumflex = 556;\n    t.Amacron = 722;\n    t.rcaron = 389;\n    t.ccedilla = 556;\n    t.Zdotaccent = 611;\n    t.Thorn = 667;\n    t.Omacron = 778;\n    t.Racute = 722;\n    t.Sacute = 667;\n    t.dcaron = 743;\n    t.Umacron = 722;\n    t.uring = 611;\n    t.threesuperior = 333;\n    t.Ograve = 778;\n    t.Agrave = 722;\n    t.Abreve = 722;\n    t.multiply = 584;\n    t.uacute = 611;\n    t.Tcaron = 611;\n    t.partialdiff = 494;\n    t.ydieresis = 556;\n    t.Nacute = 722;\n    t.icircumflex = 278;\n    t.Ecircumflex = 667;\n    t.adieresis = 556;\n    t.edieresis = 556;\n    t.cacute = 556;\n    t.nacute = 611;\n    t.umacron = 611;\n    t.Ncaron = 722;\n    t.Iacute = 278;\n    t.plusminus = 584;\n    t.brokenbar = 280;\n    t.registered = 737;\n    t.Gbreve = 778;\n    t.Idotaccent = 278;\n    t.summation = 600;\n    t.Egrave = 667;\n    t.racute = 389;\n    t.omacron = 611;\n    t.Zacute = 611;\n    t.Zcaron = 611;\n    t.greaterequal = 549;\n    t.Eth = 722;\n    t.Ccedilla = 722;\n    t.lcommaaccent = 278;\n    t.tcaron = 389;\n    t.eogonek = 556;\n    t.Uogonek = 722;\n    t.Aacute = 722;\n    t.Adieresis = 722;\n    t.egrave = 556;\n    t.zacute = 500;\n    t.iogonek = 278;\n    t.Oacute = 778;\n    t.oacute = 611;\n    t.amacron = 556;\n    t.sacute = 556;\n    t.idieresis = 278;\n    t.Ocircumflex = 778;\n    t.Ugrave = 722;\n    t.Delta = 612;\n    t.thorn = 611;\n    t.twosuperior = 333;\n    t.Odieresis = 778;\n    t.mu = 611;\n    t.igrave = 278;\n    t.ohungarumlaut = 611;\n    t.Eogonek = 667;\n    t.dcroat = 611;\n    t.threequarters = 834;\n    t.Scedilla = 667;\n    t.lcaron = 400;\n    t.Kcommaaccent = 722;\n    t.Lacute = 611;\n    t.trademark = 1000;\n    t.edotaccent = 556;\n    t.Igrave = 278;\n    t.Imacron = 278;\n    t.Lcaron = 611;\n    t.onehalf = 834;\n    t.lessequal = 549;\n    t.ocircumflex = 611;\n    t.ntilde = 611;\n    t.Uhungarumlaut = 722;\n    t.Eacute = 667;\n    t.emacron = 556;\n    t.gbreve = 611;\n    t.onequarter = 834;\n    t.Scaron = 667;\n    t.Scommaaccent = 667;\n    t.Ohungarumlaut = 778;\n    t.degree = 400;\n    t.ograve = 611;\n    t.Ccaron = 722;\n    t.ugrave = 611;\n    t.radical = 549;\n    t.Dcaron = 722;\n    t.rcommaaccent = 389;\n    t.Ntilde = 722;\n    t.otilde = 611;\n    t.Rcommaaccent = 722;\n    t.Lcommaaccent = 611;\n    t.Atilde = 722;\n    t.Aogonek = 722;\n    t.Aring = 722;\n    t.Otilde = 778;\n    t.zdotaccent = 500;\n    t.Ecaron = 667;\n    t.Iogonek = 278;\n    t.kcommaaccent = 556;\n    t.minus = 584;\n    t.Icircumflex = 278;\n    t.ncaron = 611;\n    t.tcommaaccent = 333;\n    t.logicalnot = 584;\n    t.odieresis = 611;\n    t.udieresis = 611;\n    t.notequal = 549;\n    t.gcommaaccent = 611;\n    t.eth = 611;\n    t.zcaron = 500;\n    t.ncommaaccent = 611;\n    t.onesuperior = 333;\n    t.imacron = 278;\n    t.Euro = 556;\n  });\n  t[\"Helvetica-Oblique\"] = getLookupTableFactory(function (t) {\n    t.space = 278;\n    t.exclam = 278;\n    t.quotedbl = 355;\n    t.numbersign = 556;\n    t.dollar = 556;\n    t.percent = 889;\n    t.ampersand = 667;\n    t.quoteright = 222;\n    t.parenleft = 333;\n    t.parenright = 333;\n    t.asterisk = 389;\n    t.plus = 584;\n    t.comma = 278;\n    t.hyphen = 333;\n    t.period = 278;\n    t.slash = 278;\n    t.zero = 556;\n    t.one = 556;\n    t.two = 556;\n    t.three = 556;\n    t.four = 556;\n    t.five = 556;\n    t.six = 556;\n    t.seven = 556;\n    t.eight = 556;\n    t.nine = 556;\n    t.colon = 278;\n    t.semicolon = 278;\n    t.less = 584;\n    t.equal = 584;\n    t.greater = 584;\n    t.question = 556;\n    t.at = 1015;\n    t.A = 667;\n    t.B = 667;\n    t.C = 722;\n    t.D = 722;\n    t.E = 667;\n    t.F = 611;\n    t.G = 778;\n    t.H = 722;\n    t.I = 278;\n    t.J = 500;\n    t.K = 667;\n    t.L = 556;\n    t.M = 833;\n    t.N = 722;\n    t.O = 778;\n    t.P = 667;\n    t.Q = 778;\n    t.R = 722;\n    t.S = 667;\n    t.T = 611;\n    t.U = 722;\n    t.V = 667;\n    t.W = 944;\n    t.X = 667;\n    t.Y = 667;\n    t.Z = 611;\n    t.bracketleft = 278;\n    t.backslash = 278;\n    t.bracketright = 278;\n    t.asciicircum = 469;\n    t.underscore = 556;\n    t.quoteleft = 222;\n    t.a = 556;\n    t.b = 556;\n    t.c = 500;\n    t.d = 556;\n    t.e = 556;\n    t.f = 278;\n    t.g = 556;\n    t.h = 556;\n    t.i = 222;\n    t.j = 222;\n    t.k = 500;\n    t.l = 222;\n    t.m = 833;\n    t.n = 556;\n    t.o = 556;\n    t.p = 556;\n    t.q = 556;\n    t.r = 333;\n    t.s = 500;\n    t.t = 278;\n    t.u = 556;\n    t.v = 500;\n    t.w = 722;\n    t.x = 500;\n    t.y = 500;\n    t.z = 500;\n    t.braceleft = 334;\n    t.bar = 260;\n    t.braceright = 334;\n    t.asciitilde = 584;\n    t.exclamdown = 333;\n    t.cent = 556;\n    t.sterling = 556;\n    t.fraction = 167;\n    t.yen = 556;\n    t.florin = 556;\n    t.section = 556;\n    t.currency = 556;\n    t.quotesingle = 191;\n    t.quotedblleft = 333;\n    t.guillemotleft = 556;\n    t.guilsinglleft = 333;\n    t.guilsinglright = 333;\n    t.fi = 500;\n    t.fl = 500;\n    t.endash = 556;\n    t.dagger = 556;\n    t.daggerdbl = 556;\n    t.periodcentered = 278;\n    t.paragraph = 537;\n    t.bullet = 350;\n    t.quotesinglbase = 222;\n    t.quotedblbase = 333;\n    t.quotedblright = 333;\n    t.guillemotright = 556;\n    t.ellipsis = 1000;\n    t.perthousand = 1000;\n    t.questiondown = 611;\n    t.grave = 333;\n    t.acute = 333;\n    t.circumflex = 333;\n    t.tilde = 333;\n    t.macron = 333;\n    t.breve = 333;\n    t.dotaccent = 333;\n    t.dieresis = 333;\n    t.ring = 333;\n    t.cedilla = 333;\n    t.hungarumlaut = 333;\n    t.ogonek = 333;\n    t.caron = 333;\n    t.emdash = 1000;\n    t.AE = 1000;\n    t.ordfeminine = 370;\n    t.Lslash = 556;\n    t.Oslash = 778;\n    t.OE = 1000;\n    t.ordmasculine = 365;\n    t.ae = 889;\n    t.dotlessi = 278;\n    t.lslash = 222;\n    t.oslash = 611;\n    t.oe = 944;\n    t.germandbls = 611;\n    t.Idieresis = 278;\n    t.eacute = 556;\n    t.abreve = 556;\n    t.uhungarumlaut = 556;\n    t.ecaron = 556;\n    t.Ydieresis = 667;\n    t.divide = 584;\n    t.Yacute = 667;\n    t.Acircumflex = 667;\n    t.aacute = 556;\n    t.Ucircumflex = 722;\n    t.yacute = 500;\n    t.scommaaccent = 500;\n    t.ecircumflex = 556;\n    t.Uring = 722;\n    t.Udieresis = 722;\n    t.aogonek = 556;\n    t.Uacute = 722;\n    t.uogonek = 556;\n    t.Edieresis = 667;\n    t.Dcroat = 722;\n    t.commaaccent = 250;\n    t.copyright = 737;\n    t.Emacron = 667;\n    t.ccaron = 500;\n    t.aring = 556;\n    t.Ncommaaccent = 722;\n    t.lacute = 222;\n    t.agrave = 556;\n    t.Tcommaaccent = 611;\n    t.Cacute = 722;\n    t.atilde = 556;\n    t.Edotaccent = 667;\n    t.scaron = 500;\n    t.scedilla = 500;\n    t.iacute = 278;\n    t.lozenge = 471;\n    t.Rcaron = 722;\n    t.Gcommaaccent = 778;\n    t.ucircumflex = 556;\n    t.acircumflex = 556;\n    t.Amacron = 667;\n    t.rcaron = 333;\n    t.ccedilla = 500;\n    t.Zdotaccent = 611;\n    t.Thorn = 667;\n    t.Omacron = 778;\n    t.Racute = 722;\n    t.Sacute = 667;\n    t.dcaron = 643;\n    t.Umacron = 722;\n    t.uring = 556;\n    t.threesuperior = 333;\n    t.Ograve = 778;\n    t.Agrave = 667;\n    t.Abreve = 667;\n    t.multiply = 584;\n    t.uacute = 556;\n    t.Tcaron = 611;\n    t.partialdiff = 476;\n    t.ydieresis = 500;\n    t.Nacute = 722;\n    t.icircumflex = 278;\n    t.Ecircumflex = 667;\n    t.adieresis = 556;\n    t.edieresis = 556;\n    t.cacute = 500;\n    t.nacute = 556;\n    t.umacron = 556;\n    t.Ncaron = 722;\n    t.Iacute = 278;\n    t.plusminus = 584;\n    t.brokenbar = 260;\n    t.registered = 737;\n    t.Gbreve = 778;\n    t.Idotaccent = 278;\n    t.summation = 600;\n    t.Egrave = 667;\n    t.racute = 333;\n    t.omacron = 556;\n    t.Zacute = 611;\n    t.Zcaron = 611;\n    t.greaterequal = 549;\n    t.Eth = 722;\n    t.Ccedilla = 722;\n    t.lcommaaccent = 222;\n    t.tcaron = 317;\n    t.eogonek = 556;\n    t.Uogonek = 722;\n    t.Aacute = 667;\n    t.Adieresis = 667;\n    t.egrave = 556;\n    t.zacute = 500;\n    t.iogonek = 222;\n    t.Oacute = 778;\n    t.oacute = 556;\n    t.amacron = 556;\n    t.sacute = 500;\n    t.idieresis = 278;\n    t.Ocircumflex = 778;\n    t.Ugrave = 722;\n    t.Delta = 612;\n    t.thorn = 556;\n    t.twosuperior = 333;\n    t.Odieresis = 778;\n    t.mu = 556;\n    t.igrave = 278;\n    t.ohungarumlaut = 556;\n    t.Eogonek = 667;\n    t.dcroat = 556;\n    t.threequarters = 834;\n    t.Scedilla = 667;\n    t.lcaron = 299;\n    t.Kcommaaccent = 667;\n    t.Lacute = 556;\n    t.trademark = 1000;\n    t.edotaccent = 556;\n    t.Igrave = 278;\n    t.Imacron = 278;\n    t.Lcaron = 556;\n    t.onehalf = 834;\n    t.lessequal = 549;\n    t.ocircumflex = 556;\n    t.ntilde = 556;\n    t.Uhungarumlaut = 722;\n    t.Eacute = 667;\n    t.emacron = 556;\n    t.gbreve = 556;\n    t.onequarter = 834;\n    t.Scaron = 667;\n    t.Scommaaccent = 667;\n    t.Ohungarumlaut = 778;\n    t.degree = 400;\n    t.ograve = 556;\n    t.Ccaron = 722;\n    t.ugrave = 556;\n    t.radical = 453;\n    t.Dcaron = 722;\n    t.rcommaaccent = 333;\n    t.Ntilde = 722;\n    t.otilde = 556;\n    t.Rcommaaccent = 722;\n    t.Lcommaaccent = 556;\n    t.Atilde = 667;\n    t.Aogonek = 667;\n    t.Aring = 667;\n    t.Otilde = 778;\n    t.zdotaccent = 500;\n    t.Ecaron = 667;\n    t.Iogonek = 278;\n    t.kcommaaccent = 500;\n    t.minus = 584;\n    t.Icircumflex = 278;\n    t.ncaron = 556;\n    t.tcommaaccent = 278;\n    t.logicalnot = 584;\n    t.odieresis = 556;\n    t.udieresis = 556;\n    t.notequal = 549;\n    t.gcommaaccent = 556;\n    t.eth = 556;\n    t.zcaron = 500;\n    t.ncommaaccent = 556;\n    t.onesuperior = 333;\n    t.imacron = 278;\n    t.Euro = 556;\n  });\n  t.Symbol = getLookupTableFactory(function (t) {\n    t.space = 250;\n    t.exclam = 333;\n    t.universal = 713;\n    t.numbersign = 500;\n    t.existential = 549;\n    t.percent = 833;\n    t.ampersand = 778;\n    t.suchthat = 439;\n    t.parenleft = 333;\n    t.parenright = 333;\n    t.asteriskmath = 500;\n    t.plus = 549;\n    t.comma = 250;\n    t.minus = 549;\n    t.period = 250;\n    t.slash = 278;\n    t.zero = 500;\n    t.one = 500;\n    t.two = 500;\n    t.three = 500;\n    t.four = 500;\n    t.five = 500;\n    t.six = 500;\n    t.seven = 500;\n    t.eight = 500;\n    t.nine = 500;\n    t.colon = 278;\n    t.semicolon = 278;\n    t.less = 549;\n    t.equal = 549;\n    t.greater = 549;\n    t.question = 444;\n    t.congruent = 549;\n    t.Alpha = 722;\n    t.Beta = 667;\n    t.Chi = 722;\n    t.Delta = 612;\n    t.Epsilon = 611;\n    t.Phi = 763;\n    t.Gamma = 603;\n    t.Eta = 722;\n    t.Iota = 333;\n    t.theta1 = 631;\n    t.Kappa = 722;\n    t.Lambda = 686;\n    t.Mu = 889;\n    t.Nu = 722;\n    t.Omicron = 722;\n    t.Pi = 768;\n    t.Theta = 741;\n    t.Rho = 556;\n    t.Sigma = 592;\n    t.Tau = 611;\n    t.Upsilon = 690;\n    t.sigma1 = 439;\n    t.Omega = 768;\n    t.Xi = 645;\n    t.Psi = 795;\n    t.Zeta = 611;\n    t.bracketleft = 333;\n    t.therefore = 863;\n    t.bracketright = 333;\n    t.perpendicular = 658;\n    t.underscore = 500;\n    t.radicalex = 500;\n    t.alpha = 631;\n    t.beta = 549;\n    t.chi = 549;\n    t.delta = 494;\n    t.epsilon = 439;\n    t.phi = 521;\n    t.gamma = 411;\n    t.eta = 603;\n    t.iota = 329;\n    t.phi1 = 603;\n    t.kappa = 549;\n    t.lambda = 549;\n    t.mu = 576;\n    t.nu = 521;\n    t.omicron = 549;\n    t.pi = 549;\n    t.theta = 521;\n    t.rho = 549;\n    t.sigma = 603;\n    t.tau = 439;\n    t.upsilon = 576;\n    t.omega1 = 713;\n    t.omega = 686;\n    t.xi = 493;\n    t.psi = 686;\n    t.zeta = 494;\n    t.braceleft = 480;\n    t.bar = 200;\n    t.braceright = 480;\n    t.similar = 549;\n    t.Euro = 750;\n    t.Upsilon1 = 620;\n    t.minute = 247;\n    t.lessequal = 549;\n    t.fraction = 167;\n    t.infinity = 713;\n    t.florin = 500;\n    t.club = 753;\n    t.diamond = 753;\n    t.heart = 753;\n    t.spade = 753;\n    t.arrowboth = 1042;\n    t.arrowleft = 987;\n    t.arrowup = 603;\n    t.arrowright = 987;\n    t.arrowdown = 603;\n    t.degree = 400;\n    t.plusminus = 549;\n    t.second = 411;\n    t.greaterequal = 549;\n    t.multiply = 549;\n    t.proportional = 713;\n    t.partialdiff = 494;\n    t.bullet = 460;\n    t.divide = 549;\n    t.notequal = 549;\n    t.equivalence = 549;\n    t.approxequal = 549;\n    t.ellipsis = 1000;\n    t.arrowvertex = 603;\n    t.arrowhorizex = 1000;\n    t.carriagereturn = 658;\n    t.aleph = 823;\n    t.Ifraktur = 686;\n    t.Rfraktur = 795;\n    t.weierstrass = 987;\n    t.circlemultiply = 768;\n    t.circleplus = 768;\n    t.emptyset = 823;\n    t.intersection = 768;\n    t.union = 768;\n    t.propersuperset = 713;\n    t.reflexsuperset = 713;\n    t.notsubset = 713;\n    t.propersubset = 713;\n    t.reflexsubset = 713;\n    t.element = 713;\n    t.notelement = 713;\n    t.angle = 768;\n    t.gradient = 713;\n    t.registerserif = 790;\n    t.copyrightserif = 790;\n    t.trademarkserif = 890;\n    t.product = 823;\n    t.radical = 549;\n    t.dotmath = 250;\n    t.logicalnot = 713;\n    t.logicaland = 603;\n    t.logicalor = 603;\n    t.arrowdblboth = 1042;\n    t.arrowdblleft = 987;\n    t.arrowdblup = 603;\n    t.arrowdblright = 987;\n    t.arrowdbldown = 603;\n    t.lozenge = 494;\n    t.angleleft = 329;\n    t.registersans = 790;\n    t.copyrightsans = 790;\n    t.trademarksans = 786;\n    t.summation = 713;\n    t.parenlefttp = 384;\n    t.parenleftex = 384;\n    t.parenleftbt = 384;\n    t.bracketlefttp = 384;\n    t.bracketleftex = 384;\n    t.bracketleftbt = 384;\n    t.bracelefttp = 494;\n    t.braceleftmid = 494;\n    t.braceleftbt = 494;\n    t.braceex = 494;\n    t.angleright = 329;\n    t.integral = 274;\n    t.integraltp = 686;\n    t.integralex = 686;\n    t.integralbt = 686;\n    t.parenrighttp = 384;\n    t.parenrightex = 384;\n    t.parenrightbt = 384;\n    t.bracketrighttp = 384;\n    t.bracketrightex = 384;\n    t.bracketrightbt = 384;\n    t.bracerighttp = 494;\n    t.bracerightmid = 494;\n    t.bracerightbt = 494;\n    t.apple = 790;\n  });\n  t[\"Times-Roman\"] = getLookupTableFactory(function (t) {\n    t.space = 250;\n    t.exclam = 333;\n    t.quotedbl = 408;\n    t.numbersign = 500;\n    t.dollar = 500;\n    t.percent = 833;\n    t.ampersand = 778;\n    t.quoteright = 333;\n    t.parenleft = 333;\n    t.parenright = 333;\n    t.asterisk = 500;\n    t.plus = 564;\n    t.comma = 250;\n    t.hyphen = 333;\n    t.period = 250;\n    t.slash = 278;\n    t.zero = 500;\n    t.one = 500;\n    t.two = 500;\n    t.three = 500;\n    t.four = 500;\n    t.five = 500;\n    t.six = 500;\n    t.seven = 500;\n    t.eight = 500;\n    t.nine = 500;\n    t.colon = 278;\n    t.semicolon = 278;\n    t.less = 564;\n    t.equal = 564;\n    t.greater = 564;\n    t.question = 444;\n    t.at = 921;\n    t.A = 722;\n    t.B = 667;\n    t.C = 667;\n    t.D = 722;\n    t.E = 611;\n    t.F = 556;\n    t.G = 722;\n    t.H = 722;\n    t.I = 333;\n    t.J = 389;\n    t.K = 722;\n    t.L = 611;\n    t.M = 889;\n    t.N = 722;\n    t.O = 722;\n    t.P = 556;\n    t.Q = 722;\n    t.R = 667;\n    t.S = 556;\n    t.T = 611;\n    t.U = 722;\n    t.V = 722;\n    t.W = 944;\n    t.X = 722;\n    t.Y = 722;\n    t.Z = 611;\n    t.bracketleft = 333;\n    t.backslash = 278;\n    t.bracketright = 333;\n    t.asciicircum = 469;\n    t.underscore = 500;\n    t.quoteleft = 333;\n    t.a = 444;\n    t.b = 500;\n    t.c = 444;\n    t.d = 500;\n    t.e = 444;\n    t.f = 333;\n    t.g = 500;\n    t.h = 500;\n    t.i = 278;\n    t.j = 278;\n    t.k = 500;\n    t.l = 278;\n    t.m = 778;\n    t.n = 500;\n    t.o = 500;\n    t.p = 500;\n    t.q = 500;\n    t.r = 333;\n    t.s = 389;\n    t.t = 278;\n    t.u = 500;\n    t.v = 500;\n    t.w = 722;\n    t.x = 500;\n    t.y = 500;\n    t.z = 444;\n    t.braceleft = 480;\n    t.bar = 200;\n    t.braceright = 480;\n    t.asciitilde = 541;\n    t.exclamdown = 333;\n    t.cent = 500;\n    t.sterling = 500;\n    t.fraction = 167;\n    t.yen = 500;\n    t.florin = 500;\n    t.section = 500;\n    t.currency = 500;\n    t.quotesingle = 180;\n    t.quotedblleft = 444;\n    t.guillemotleft = 500;\n    t.guilsinglleft = 333;\n    t.guilsinglright = 333;\n    t.fi = 556;\n    t.fl = 556;\n    t.endash = 500;\n    t.dagger = 500;\n    t.daggerdbl = 500;\n    t.periodcentered = 250;\n    t.paragraph = 453;\n    t.bullet = 350;\n    t.quotesinglbase = 333;\n    t.quotedblbase = 444;\n    t.quotedblright = 444;\n    t.guillemotright = 500;\n    t.ellipsis = 1000;\n    t.perthousand = 1000;\n    t.questiondown = 444;\n    t.grave = 333;\n    t.acute = 333;\n    t.circumflex = 333;\n    t.tilde = 333;\n    t.macron = 333;\n    t.breve = 333;\n    t.dotaccent = 333;\n    t.dieresis = 333;\n    t.ring = 333;\n    t.cedilla = 333;\n    t.hungarumlaut = 333;\n    t.ogonek = 333;\n    t.caron = 333;\n    t.emdash = 1000;\n    t.AE = 889;\n    t.ordfeminine = 276;\n    t.Lslash = 611;\n    t.Oslash = 722;\n    t.OE = 889;\n    t.ordmasculine = 310;\n    t.ae = 667;\n    t.dotlessi = 278;\n    t.lslash = 278;\n    t.oslash = 500;\n    t.oe = 722;\n    t.germandbls = 500;\n    t.Idieresis = 333;\n    t.eacute = 444;\n    t.abreve = 444;\n    t.uhungarumlaut = 500;\n    t.ecaron = 444;\n    t.Ydieresis = 722;\n    t.divide = 564;\n    t.Yacute = 722;\n    t.Acircumflex = 722;\n    t.aacute = 444;\n    t.Ucircumflex = 722;\n    t.yacute = 500;\n    t.scommaaccent = 389;\n    t.ecircumflex = 444;\n    t.Uring = 722;\n    t.Udieresis = 722;\n    t.aogonek = 444;\n    t.Uacute = 722;\n    t.uogonek = 500;\n    t.Edieresis = 611;\n    t.Dcroat = 722;\n    t.commaaccent = 250;\n    t.copyright = 760;\n    t.Emacron = 611;\n    t.ccaron = 444;\n    t.aring = 444;\n    t.Ncommaaccent = 722;\n    t.lacute = 278;\n    t.agrave = 444;\n    t.Tcommaaccent = 611;\n    t.Cacute = 667;\n    t.atilde = 444;\n    t.Edotaccent = 611;\n    t.scaron = 389;\n    t.scedilla = 389;\n    t.iacute = 278;\n    t.lozenge = 471;\n    t.Rcaron = 667;\n    t.Gcommaaccent = 722;\n    t.ucircumflex = 500;\n    t.acircumflex = 444;\n    t.Amacron = 722;\n    t.rcaron = 333;\n    t.ccedilla = 444;\n    t.Zdotaccent = 611;\n    t.Thorn = 556;\n    t.Omacron = 722;\n    t.Racute = 667;\n    t.Sacute = 556;\n    t.dcaron = 588;\n    t.Umacron = 722;\n    t.uring = 500;\n    t.threesuperior = 300;\n    t.Ograve = 722;\n    t.Agrave = 722;\n    t.Abreve = 722;\n    t.multiply = 564;\n    t.uacute = 500;\n    t.Tcaron = 611;\n    t.partialdiff = 476;\n    t.ydieresis = 500;\n    t.Nacute = 722;\n    t.icircumflex = 278;\n    t.Ecircumflex = 611;\n    t.adieresis = 444;\n    t.edieresis = 444;\n    t.cacute = 444;\n    t.nacute = 500;\n    t.umacron = 500;\n    t.Ncaron = 722;\n    t.Iacute = 333;\n    t.plusminus = 564;\n    t.brokenbar = 200;\n    t.registered = 760;\n    t.Gbreve = 722;\n    t.Idotaccent = 333;\n    t.summation = 600;\n    t.Egrave = 611;\n    t.racute = 333;\n    t.omacron = 500;\n    t.Zacute = 611;\n    t.Zcaron = 611;\n    t.greaterequal = 549;\n    t.Eth = 722;\n    t.Ccedilla = 667;\n    t.lcommaaccent = 278;\n    t.tcaron = 326;\n    t.eogonek = 444;\n    t.Uogonek = 722;\n    t.Aacute = 722;\n    t.Adieresis = 722;\n    t.egrave = 444;\n    t.zacute = 444;\n    t.iogonek = 278;\n    t.Oacute = 722;\n    t.oacute = 500;\n    t.amacron = 444;\n    t.sacute = 389;\n    t.idieresis = 278;\n    t.Ocircumflex = 722;\n    t.Ugrave = 722;\n    t.Delta = 612;\n    t.thorn = 500;\n    t.twosuperior = 300;\n    t.Odieresis = 722;\n    t.mu = 500;\n    t.igrave = 278;\n    t.ohungarumlaut = 500;\n    t.Eogonek = 611;\n    t.dcroat = 500;\n    t.threequarters = 750;\n    t.Scedilla = 556;\n    t.lcaron = 344;\n    t.Kcommaaccent = 722;\n    t.Lacute = 611;\n    t.trademark = 980;\n    t.edotaccent = 444;\n    t.Igrave = 333;\n    t.Imacron = 333;\n    t.Lcaron = 611;\n    t.onehalf = 750;\n    t.lessequal = 549;\n    t.ocircumflex = 500;\n    t.ntilde = 500;\n    t.Uhungarumlaut = 722;\n    t.Eacute = 611;\n    t.emacron = 444;\n    t.gbreve = 500;\n    t.onequarter = 750;\n    t.Scaron = 556;\n    t.Scommaaccent = 556;\n    t.Ohungarumlaut = 722;\n    t.degree = 400;\n    t.ograve = 500;\n    t.Ccaron = 667;\n    t.ugrave = 500;\n    t.radical = 453;\n    t.Dcaron = 722;\n    t.rcommaaccent = 333;\n    t.Ntilde = 722;\n    t.otilde = 500;\n    t.Rcommaaccent = 667;\n    t.Lcommaaccent = 611;\n    t.Atilde = 722;\n    t.Aogonek = 722;\n    t.Aring = 722;\n    t.Otilde = 722;\n    t.zdotaccent = 444;\n    t.Ecaron = 611;\n    t.Iogonek = 333;\n    t.kcommaaccent = 500;\n    t.minus = 564;\n    t.Icircumflex = 333;\n    t.ncaron = 500;\n    t.tcommaaccent = 278;\n    t.logicalnot = 564;\n    t.odieresis = 500;\n    t.udieresis = 500;\n    t.notequal = 549;\n    t.gcommaaccent = 500;\n    t.eth = 500;\n    t.zcaron = 444;\n    t.ncommaaccent = 500;\n    t.onesuperior = 300;\n    t.imacron = 278;\n    t.Euro = 500;\n  });\n  t[\"Times-Bold\"] = getLookupTableFactory(function (t) {\n    t.space = 250;\n    t.exclam = 333;\n    t.quotedbl = 555;\n    t.numbersign = 500;\n    t.dollar = 500;\n    t.percent = 1000;\n    t.ampersand = 833;\n    t.quoteright = 333;\n    t.parenleft = 333;\n    t.parenright = 333;\n    t.asterisk = 500;\n    t.plus = 570;\n    t.comma = 250;\n    t.hyphen = 333;\n    t.period = 250;\n    t.slash = 278;\n    t.zero = 500;\n    t.one = 500;\n    t.two = 500;\n    t.three = 500;\n    t.four = 500;\n    t.five = 500;\n    t.six = 500;\n    t.seven = 500;\n    t.eight = 500;\n    t.nine = 500;\n    t.colon = 333;\n    t.semicolon = 333;\n    t.less = 570;\n    t.equal = 570;\n    t.greater = 570;\n    t.question = 500;\n    t.at = 930;\n    t.A = 722;\n    t.B = 667;\n    t.C = 722;\n    t.D = 722;\n    t.E = 667;\n    t.F = 611;\n    t.G = 778;\n    t.H = 778;\n    t.I = 389;\n    t.J = 500;\n    t.K = 778;\n    t.L = 667;\n    t.M = 944;\n    t.N = 722;\n    t.O = 778;\n    t.P = 611;\n    t.Q = 778;\n    t.R = 722;\n    t.S = 556;\n    t.T = 667;\n    t.U = 722;\n    t.V = 722;\n    t.W = 1000;\n    t.X = 722;\n    t.Y = 722;\n    t.Z = 667;\n    t.bracketleft = 333;\n    t.backslash = 278;\n    t.bracketright = 333;\n    t.asciicircum = 581;\n    t.underscore = 500;\n    t.quoteleft = 333;\n    t.a = 500;\n    t.b = 556;\n    t.c = 444;\n    t.d = 556;\n    t.e = 444;\n    t.f = 333;\n    t.g = 500;\n    t.h = 556;\n    t.i = 278;\n    t.j = 333;\n    t.k = 556;\n    t.l = 278;\n    t.m = 833;\n    t.n = 556;\n    t.o = 500;\n    t.p = 556;\n    t.q = 556;\n    t.r = 444;\n    t.s = 389;\n    t.t = 333;\n    t.u = 556;\n    t.v = 500;\n    t.w = 722;\n    t.x = 500;\n    t.y = 500;\n    t.z = 444;\n    t.braceleft = 394;\n    t.bar = 220;\n    t.braceright = 394;\n    t.asciitilde = 520;\n    t.exclamdown = 333;\n    t.cent = 500;\n    t.sterling = 500;\n    t.fraction = 167;\n    t.yen = 500;\n    t.florin = 500;\n    t.section = 500;\n    t.currency = 500;\n    t.quotesingle = 278;\n    t.quotedblleft = 500;\n    t.guillemotleft = 500;\n    t.guilsinglleft = 333;\n    t.guilsinglright = 333;\n    t.fi = 556;\n    t.fl = 556;\n    t.endash = 500;\n    t.dagger = 500;\n    t.daggerdbl = 500;\n    t.periodcentered = 250;\n    t.paragraph = 540;\n    t.bullet = 350;\n    t.quotesinglbase = 333;\n    t.quotedblbase = 500;\n    t.quotedblright = 500;\n    t.guillemotright = 500;\n    t.ellipsis = 1000;\n    t.perthousand = 1000;\n    t.questiondown = 500;\n    t.grave = 333;\n    t.acute = 333;\n    t.circumflex = 333;\n    t.tilde = 333;\n    t.macron = 333;\n    t.breve = 333;\n    t.dotaccent = 333;\n    t.dieresis = 333;\n    t.ring = 333;\n    t.cedilla = 333;\n    t.hungarumlaut = 333;\n    t.ogonek = 333;\n    t.caron = 333;\n    t.emdash = 1000;\n    t.AE = 1000;\n    t.ordfeminine = 300;\n    t.Lslash = 667;\n    t.Oslash = 778;\n    t.OE = 1000;\n    t.ordmasculine = 330;\n    t.ae = 722;\n    t.dotlessi = 278;\n    t.lslash = 278;\n    t.oslash = 500;\n    t.oe = 722;\n    t.germandbls = 556;\n    t.Idieresis = 389;\n    t.eacute = 444;\n    t.abreve = 500;\n    t.uhungarumlaut = 556;\n    t.ecaron = 444;\n    t.Ydieresis = 722;\n    t.divide = 570;\n    t.Yacute = 722;\n    t.Acircumflex = 722;\n    t.aacute = 500;\n    t.Ucircumflex = 722;\n    t.yacute = 500;\n    t.scommaaccent = 389;\n    t.ecircumflex = 444;\n    t.Uring = 722;\n    t.Udieresis = 722;\n    t.aogonek = 500;\n    t.Uacute = 722;\n    t.uogonek = 556;\n    t.Edieresis = 667;\n    t.Dcroat = 722;\n    t.commaaccent = 250;\n    t.copyright = 747;\n    t.Emacron = 667;\n    t.ccaron = 444;\n    t.aring = 500;\n    t.Ncommaaccent = 722;\n    t.lacute = 278;\n    t.agrave = 500;\n    t.Tcommaaccent = 667;\n    t.Cacute = 722;\n    t.atilde = 500;\n    t.Edotaccent = 667;\n    t.scaron = 389;\n    t.scedilla = 389;\n    t.iacute = 278;\n    t.lozenge = 494;\n    t.Rcaron = 722;\n    t.Gcommaaccent = 778;\n    t.ucircumflex = 556;\n    t.acircumflex = 500;\n    t.Amacron = 722;\n    t.rcaron = 444;\n    t.ccedilla = 444;\n    t.Zdotaccent = 667;\n    t.Thorn = 611;\n    t.Omacron = 778;\n    t.Racute = 722;\n    t.Sacute = 556;\n    t.dcaron = 672;\n    t.Umacron = 722;\n    t.uring = 556;\n    t.threesuperior = 300;\n    t.Ograve = 778;\n    t.Agrave = 722;\n    t.Abreve = 722;\n    t.multiply = 570;\n    t.uacute = 556;\n    t.Tcaron = 667;\n    t.partialdiff = 494;\n    t.ydieresis = 500;\n    t.Nacute = 722;\n    t.icircumflex = 278;\n    t.Ecircumflex = 667;\n    t.adieresis = 500;\n    t.edieresis = 444;\n    t.cacute = 444;\n    t.nacute = 556;\n    t.umacron = 556;\n    t.Ncaron = 722;\n    t.Iacute = 389;\n    t.plusminus = 570;\n    t.brokenbar = 220;\n    t.registered = 747;\n    t.Gbreve = 778;\n    t.Idotaccent = 389;\n    t.summation = 600;\n    t.Egrave = 667;\n    t.racute = 444;\n    t.omacron = 500;\n    t.Zacute = 667;\n    t.Zcaron = 667;\n    t.greaterequal = 549;\n    t.Eth = 722;\n    t.Ccedilla = 722;\n    t.lcommaaccent = 278;\n    t.tcaron = 416;\n    t.eogonek = 444;\n    t.Uogonek = 722;\n    t.Aacute = 722;\n    t.Adieresis = 722;\n    t.egrave = 444;\n    t.zacute = 444;\n    t.iogonek = 278;\n    t.Oacute = 778;\n    t.oacute = 500;\n    t.amacron = 500;\n    t.sacute = 389;\n    t.idieresis = 278;\n    t.Ocircumflex = 778;\n    t.Ugrave = 722;\n    t.Delta = 612;\n    t.thorn = 556;\n    t.twosuperior = 300;\n    t.Odieresis = 778;\n    t.mu = 556;\n    t.igrave = 278;\n    t.ohungarumlaut = 500;\n    t.Eogonek = 667;\n    t.dcroat = 556;\n    t.threequarters = 750;\n    t.Scedilla = 556;\n    t.lcaron = 394;\n    t.Kcommaaccent = 778;\n    t.Lacute = 667;\n    t.trademark = 1000;\n    t.edotaccent = 444;\n    t.Igrave = 389;\n    t.Imacron = 389;\n    t.Lcaron = 667;\n    t.onehalf = 750;\n    t.lessequal = 549;\n    t.ocircumflex = 500;\n    t.ntilde = 556;\n    t.Uhungarumlaut = 722;\n    t.Eacute = 667;\n    t.emacron = 444;\n    t.gbreve = 500;\n    t.onequarter = 750;\n    t.Scaron = 556;\n    t.Scommaaccent = 556;\n    t.Ohungarumlaut = 778;\n    t.degree = 400;\n    t.ograve = 500;\n    t.Ccaron = 722;\n    t.ugrave = 556;\n    t.radical = 549;\n    t.Dcaron = 722;\n    t.rcommaaccent = 444;\n    t.Ntilde = 722;\n    t.otilde = 500;\n    t.Rcommaaccent = 722;\n    t.Lcommaaccent = 667;\n    t.Atilde = 722;\n    t.Aogonek = 722;\n    t.Aring = 722;\n    t.Otilde = 778;\n    t.zdotaccent = 444;\n    t.Ecaron = 667;\n    t.Iogonek = 389;\n    t.kcommaaccent = 556;\n    t.minus = 570;\n    t.Icircumflex = 389;\n    t.ncaron = 556;\n    t.tcommaaccent = 333;\n    t.logicalnot = 570;\n    t.odieresis = 500;\n    t.udieresis = 556;\n    t.notequal = 549;\n    t.gcommaaccent = 500;\n    t.eth = 500;\n    t.zcaron = 444;\n    t.ncommaaccent = 556;\n    t.onesuperior = 300;\n    t.imacron = 278;\n    t.Euro = 500;\n  });\n  t[\"Times-BoldItalic\"] = getLookupTableFactory(function (t) {\n    t.space = 250;\n    t.exclam = 389;\n    t.quotedbl = 555;\n    t.numbersign = 500;\n    t.dollar = 500;\n    t.percent = 833;\n    t.ampersand = 778;\n    t.quoteright = 333;\n    t.parenleft = 333;\n    t.parenright = 333;\n    t.asterisk = 500;\n    t.plus = 570;\n    t.comma = 250;\n    t.hyphen = 333;\n    t.period = 250;\n    t.slash = 278;\n    t.zero = 500;\n    t.one = 500;\n    t.two = 500;\n    t.three = 500;\n    t.four = 500;\n    t.five = 500;\n    t.six = 500;\n    t.seven = 500;\n    t.eight = 500;\n    t.nine = 500;\n    t.colon = 333;\n    t.semicolon = 333;\n    t.less = 570;\n    t.equal = 570;\n    t.greater = 570;\n    t.question = 500;\n    t.at = 832;\n    t.A = 667;\n    t.B = 667;\n    t.C = 667;\n    t.D = 722;\n    t.E = 667;\n    t.F = 667;\n    t.G = 722;\n    t.H = 778;\n    t.I = 389;\n    t.J = 500;\n    t.K = 667;\n    t.L = 611;\n    t.M = 889;\n    t.N = 722;\n    t.O = 722;\n    t.P = 611;\n    t.Q = 722;\n    t.R = 667;\n    t.S = 556;\n    t.T = 611;\n    t.U = 722;\n    t.V = 667;\n    t.W = 889;\n    t.X = 667;\n    t.Y = 611;\n    t.Z = 611;\n    t.bracketleft = 333;\n    t.backslash = 278;\n    t.bracketright = 333;\n    t.asciicircum = 570;\n    t.underscore = 500;\n    t.quoteleft = 333;\n    t.a = 500;\n    t.b = 500;\n    t.c = 444;\n    t.d = 500;\n    t.e = 444;\n    t.f = 333;\n    t.g = 500;\n    t.h = 556;\n    t.i = 278;\n    t.j = 278;\n    t.k = 500;\n    t.l = 278;\n    t.m = 778;\n    t.n = 556;\n    t.o = 500;\n    t.p = 500;\n    t.q = 500;\n    t.r = 389;\n    t.s = 389;\n    t.t = 278;\n    t.u = 556;\n    t.v = 444;\n    t.w = 667;\n    t.x = 500;\n    t.y = 444;\n    t.z = 389;\n    t.braceleft = 348;\n    t.bar = 220;\n    t.braceright = 348;\n    t.asciitilde = 570;\n    t.exclamdown = 389;\n    t.cent = 500;\n    t.sterling = 500;\n    t.fraction = 167;\n    t.yen = 500;\n    t.florin = 500;\n    t.section = 500;\n    t.currency = 500;\n    t.quotesingle = 278;\n    t.quotedblleft = 500;\n    t.guillemotleft = 500;\n    t.guilsinglleft = 333;\n    t.guilsinglright = 333;\n    t.fi = 556;\n    t.fl = 556;\n    t.endash = 500;\n    t.dagger = 500;\n    t.daggerdbl = 500;\n    t.periodcentered = 250;\n    t.paragraph = 500;\n    t.bullet = 350;\n    t.quotesinglbase = 333;\n    t.quotedblbase = 500;\n    t.quotedblright = 500;\n    t.guillemotright = 500;\n    t.ellipsis = 1000;\n    t.perthousand = 1000;\n    t.questiondown = 500;\n    t.grave = 333;\n    t.acute = 333;\n    t.circumflex = 333;\n    t.tilde = 333;\n    t.macron = 333;\n    t.breve = 333;\n    t.dotaccent = 333;\n    t.dieresis = 333;\n    t.ring = 333;\n    t.cedilla = 333;\n    t.hungarumlaut = 333;\n    t.ogonek = 333;\n    t.caron = 333;\n    t.emdash = 1000;\n    t.AE = 944;\n    t.ordfeminine = 266;\n    t.Lslash = 611;\n    t.Oslash = 722;\n    t.OE = 944;\n    t.ordmasculine = 300;\n    t.ae = 722;\n    t.dotlessi = 278;\n    t.lslash = 278;\n    t.oslash = 500;\n    t.oe = 722;\n    t.germandbls = 500;\n    t.Idieresis = 389;\n    t.eacute = 444;\n    t.abreve = 500;\n    t.uhungarumlaut = 556;\n    t.ecaron = 444;\n    t.Ydieresis = 611;\n    t.divide = 570;\n    t.Yacute = 611;\n    t.Acircumflex = 667;\n    t.aacute = 500;\n    t.Ucircumflex = 722;\n    t.yacute = 444;\n    t.scommaaccent = 389;\n    t.ecircumflex = 444;\n    t.Uring = 722;\n    t.Udieresis = 722;\n    t.aogonek = 500;\n    t.Uacute = 722;\n    t.uogonek = 556;\n    t.Edieresis = 667;\n    t.Dcroat = 722;\n    t.commaaccent = 250;\n    t.copyright = 747;\n    t.Emacron = 667;\n    t.ccaron = 444;\n    t.aring = 500;\n    t.Ncommaaccent = 722;\n    t.lacute = 278;\n    t.agrave = 500;\n    t.Tcommaaccent = 611;\n    t.Cacute = 667;\n    t.atilde = 500;\n    t.Edotaccent = 667;\n    t.scaron = 389;\n    t.scedilla = 389;\n    t.iacute = 278;\n    t.lozenge = 494;\n    t.Rcaron = 667;\n    t.Gcommaaccent = 722;\n    t.ucircumflex = 556;\n    t.acircumflex = 500;\n    t.Amacron = 667;\n    t.rcaron = 389;\n    t.ccedilla = 444;\n    t.Zdotaccent = 611;\n    t.Thorn = 611;\n    t.Omacron = 722;\n    t.Racute = 667;\n    t.Sacute = 556;\n    t.dcaron = 608;\n    t.Umacron = 722;\n    t.uring = 556;\n    t.threesuperior = 300;\n    t.Ograve = 722;\n    t.Agrave = 667;\n    t.Abreve = 667;\n    t.multiply = 570;\n    t.uacute = 556;\n    t.Tcaron = 611;\n    t.partialdiff = 494;\n    t.ydieresis = 444;\n    t.Nacute = 722;\n    t.icircumflex = 278;\n    t.Ecircumflex = 667;\n    t.adieresis = 500;\n    t.edieresis = 444;\n    t.cacute = 444;\n    t.nacute = 556;\n    t.umacron = 556;\n    t.Ncaron = 722;\n    t.Iacute = 389;\n    t.plusminus = 570;\n    t.brokenbar = 220;\n    t.registered = 747;\n    t.Gbreve = 722;\n    t.Idotaccent = 389;\n    t.summation = 600;\n    t.Egrave = 667;\n    t.racute = 389;\n    t.omacron = 500;\n    t.Zacute = 611;\n    t.Zcaron = 611;\n    t.greaterequal = 549;\n    t.Eth = 722;\n    t.Ccedilla = 667;\n    t.lcommaaccent = 278;\n    t.tcaron = 366;\n    t.eogonek = 444;\n    t.Uogonek = 722;\n    t.Aacute = 667;\n    t.Adieresis = 667;\n    t.egrave = 444;\n    t.zacute = 389;\n    t.iogonek = 278;\n    t.Oacute = 722;\n    t.oacute = 500;\n    t.amacron = 500;\n    t.sacute = 389;\n    t.idieresis = 278;\n    t.Ocircumflex = 722;\n    t.Ugrave = 722;\n    t.Delta = 612;\n    t.thorn = 500;\n    t.twosuperior = 300;\n    t.Odieresis = 722;\n    t.mu = 576;\n    t.igrave = 278;\n    t.ohungarumlaut = 500;\n    t.Eogonek = 667;\n    t.dcroat = 500;\n    t.threequarters = 750;\n    t.Scedilla = 556;\n    t.lcaron = 382;\n    t.Kcommaaccent = 667;\n    t.Lacute = 611;\n    t.trademark = 1000;\n    t.edotaccent = 444;\n    t.Igrave = 389;\n    t.Imacron = 389;\n    t.Lcaron = 611;\n    t.onehalf = 750;\n    t.lessequal = 549;\n    t.ocircumflex = 500;\n    t.ntilde = 556;\n    t.Uhungarumlaut = 722;\n    t.Eacute = 667;\n    t.emacron = 444;\n    t.gbreve = 500;\n    t.onequarter = 750;\n    t.Scaron = 556;\n    t.Scommaaccent = 556;\n    t.Ohungarumlaut = 722;\n    t.degree = 400;\n    t.ograve = 500;\n    t.Ccaron = 667;\n    t.ugrave = 556;\n    t.radical = 549;\n    t.Dcaron = 722;\n    t.rcommaaccent = 389;\n    t.Ntilde = 722;\n    t.otilde = 500;\n    t.Rcommaaccent = 667;\n    t.Lcommaaccent = 611;\n    t.Atilde = 667;\n    t.Aogonek = 667;\n    t.Aring = 667;\n    t.Otilde = 722;\n    t.zdotaccent = 389;\n    t.Ecaron = 667;\n    t.Iogonek = 389;\n    t.kcommaaccent = 500;\n    t.minus = 606;\n    t.Icircumflex = 389;\n    t.ncaron = 556;\n    t.tcommaaccent = 278;\n    t.logicalnot = 606;\n    t.odieresis = 500;\n    t.udieresis = 556;\n    t.notequal = 549;\n    t.gcommaaccent = 500;\n    t.eth = 500;\n    t.zcaron = 389;\n    t.ncommaaccent = 556;\n    t.onesuperior = 300;\n    t.imacron = 278;\n    t.Euro = 500;\n  });\n  t[\"Times-Italic\"] = getLookupTableFactory(function (t) {\n    t.space = 250;\n    t.exclam = 333;\n    t.quotedbl = 420;\n    t.numbersign = 500;\n    t.dollar = 500;\n    t.percent = 833;\n    t.ampersand = 778;\n    t.quoteright = 333;\n    t.parenleft = 333;\n    t.parenright = 333;\n    t.asterisk = 500;\n    t.plus = 675;\n    t.comma = 250;\n    t.hyphen = 333;\n    t.period = 250;\n    t.slash = 278;\n    t.zero = 500;\n    t.one = 500;\n    t.two = 500;\n    t.three = 500;\n    t.four = 500;\n    t.five = 500;\n    t.six = 500;\n    t.seven = 500;\n    t.eight = 500;\n    t.nine = 500;\n    t.colon = 333;\n    t.semicolon = 333;\n    t.less = 675;\n    t.equal = 675;\n    t.greater = 675;\n    t.question = 500;\n    t.at = 920;\n    t.A = 611;\n    t.B = 611;\n    t.C = 667;\n    t.D = 722;\n    t.E = 611;\n    t.F = 611;\n    t.G = 722;\n    t.H = 722;\n    t.I = 333;\n    t.J = 444;\n    t.K = 667;\n    t.L = 556;\n    t.M = 833;\n    t.N = 667;\n    t.O = 722;\n    t.P = 611;\n    t.Q = 722;\n    t.R = 611;\n    t.S = 500;\n    t.T = 556;\n    t.U = 722;\n    t.V = 611;\n    t.W = 833;\n    t.X = 611;\n    t.Y = 556;\n    t.Z = 556;\n    t.bracketleft = 389;\n    t.backslash = 278;\n    t.bracketright = 389;\n    t.asciicircum = 422;\n    t.underscore = 500;\n    t.quoteleft = 333;\n    t.a = 500;\n    t.b = 500;\n    t.c = 444;\n    t.d = 500;\n    t.e = 444;\n    t.f = 278;\n    t.g = 500;\n    t.h = 500;\n    t.i = 278;\n    t.j = 278;\n    t.k = 444;\n    t.l = 278;\n    t.m = 722;\n    t.n = 500;\n    t.o = 500;\n    t.p = 500;\n    t.q = 500;\n    t.r = 389;\n    t.s = 389;\n    t.t = 278;\n    t.u = 500;\n    t.v = 444;\n    t.w = 667;\n    t.x = 444;\n    t.y = 444;\n    t.z = 389;\n    t.braceleft = 400;\n    t.bar = 275;\n    t.braceright = 400;\n    t.asciitilde = 541;\n    t.exclamdown = 389;\n    t.cent = 500;\n    t.sterling = 500;\n    t.fraction = 167;\n    t.yen = 500;\n    t.florin = 500;\n    t.section = 500;\n    t.currency = 500;\n    t.quotesingle = 214;\n    t.quotedblleft = 556;\n    t.guillemotleft = 500;\n    t.guilsinglleft = 333;\n    t.guilsinglright = 333;\n    t.fi = 500;\n    t.fl = 500;\n    t.endash = 500;\n    t.dagger = 500;\n    t.daggerdbl = 500;\n    t.periodcentered = 250;\n    t.paragraph = 523;\n    t.bullet = 350;\n    t.quotesinglbase = 333;\n    t.quotedblbase = 556;\n    t.quotedblright = 556;\n    t.guillemotright = 500;\n    t.ellipsis = 889;\n    t.perthousand = 1000;\n    t.questiondown = 500;\n    t.grave = 333;\n    t.acute = 333;\n    t.circumflex = 333;\n    t.tilde = 333;\n    t.macron = 333;\n    t.breve = 333;\n    t.dotaccent = 333;\n    t.dieresis = 333;\n    t.ring = 333;\n    t.cedilla = 333;\n    t.hungarumlaut = 333;\n    t.ogonek = 333;\n    t.caron = 333;\n    t.emdash = 889;\n    t.AE = 889;\n    t.ordfeminine = 276;\n    t.Lslash = 556;\n    t.Oslash = 722;\n    t.OE = 944;\n    t.ordmasculine = 310;\n    t.ae = 667;\n    t.dotlessi = 278;\n    t.lslash = 278;\n    t.oslash = 500;\n    t.oe = 667;\n    t.germandbls = 500;\n    t.Idieresis = 333;\n    t.eacute = 444;\n    t.abreve = 500;\n    t.uhungarumlaut = 500;\n    t.ecaron = 444;\n    t.Ydieresis = 556;\n    t.divide = 675;\n    t.Yacute = 556;\n    t.Acircumflex = 611;\n    t.aacute = 500;\n    t.Ucircumflex = 722;\n    t.yacute = 444;\n    t.scommaaccent = 389;\n    t.ecircumflex = 444;\n    t.Uring = 722;\n    t.Udieresis = 722;\n    t.aogonek = 500;\n    t.Uacute = 722;\n    t.uogonek = 500;\n    t.Edieresis = 611;\n    t.Dcroat = 722;\n    t.commaaccent = 250;\n    t.copyright = 760;\n    t.Emacron = 611;\n    t.ccaron = 444;\n    t.aring = 500;\n    t.Ncommaaccent = 667;\n    t.lacute = 278;\n    t.agrave = 500;\n    t.Tcommaaccent = 556;\n    t.Cacute = 667;\n    t.atilde = 500;\n    t.Edotaccent = 611;\n    t.scaron = 389;\n    t.scedilla = 389;\n    t.iacute = 278;\n    t.lozenge = 471;\n    t.Rcaron = 611;\n    t.Gcommaaccent = 722;\n    t.ucircumflex = 500;\n    t.acircumflex = 500;\n    t.Amacron = 611;\n    t.rcaron = 389;\n    t.ccedilla = 444;\n    t.Zdotaccent = 556;\n    t.Thorn = 611;\n    t.Omacron = 722;\n    t.Racute = 611;\n    t.Sacute = 500;\n    t.dcaron = 544;\n    t.Umacron = 722;\n    t.uring = 500;\n    t.threesuperior = 300;\n    t.Ograve = 722;\n    t.Agrave = 611;\n    t.Abreve = 611;\n    t.multiply = 675;\n    t.uacute = 500;\n    t.Tcaron = 556;\n    t.partialdiff = 476;\n    t.ydieresis = 444;\n    t.Nacute = 667;\n    t.icircumflex = 278;\n    t.Ecircumflex = 611;\n    t.adieresis = 500;\n    t.edieresis = 444;\n    t.cacute = 444;\n    t.nacute = 500;\n    t.umacron = 500;\n    t.Ncaron = 667;\n    t.Iacute = 333;\n    t.plusminus = 675;\n    t.brokenbar = 275;\n    t.registered = 760;\n    t.Gbreve = 722;\n    t.Idotaccent = 333;\n    t.summation = 600;\n    t.Egrave = 611;\n    t.racute = 389;\n    t.omacron = 500;\n    t.Zacute = 556;\n    t.Zcaron = 556;\n    t.greaterequal = 549;\n    t.Eth = 722;\n    t.Ccedilla = 667;\n    t.lcommaaccent = 278;\n    t.tcaron = 300;\n    t.eogonek = 444;\n    t.Uogonek = 722;\n    t.Aacute = 611;\n    t.Adieresis = 611;\n    t.egrave = 444;\n    t.zacute = 389;\n    t.iogonek = 278;\n    t.Oacute = 722;\n    t.oacute = 500;\n    t.amacron = 500;\n    t.sacute = 389;\n    t.idieresis = 278;\n    t.Ocircumflex = 722;\n    t.Ugrave = 722;\n    t.Delta = 612;\n    t.thorn = 500;\n    t.twosuperior = 300;\n    t.Odieresis = 722;\n    t.mu = 500;\n    t.igrave = 278;\n    t.ohungarumlaut = 500;\n    t.Eogonek = 611;\n    t.dcroat = 500;\n    t.threequarters = 750;\n    t.Scedilla = 500;\n    t.lcaron = 300;\n    t.Kcommaaccent = 667;\n    t.Lacute = 556;\n    t.trademark = 980;\n    t.edotaccent = 444;\n    t.Igrave = 333;\n    t.Imacron = 333;\n    t.Lcaron = 611;\n    t.onehalf = 750;\n    t.lessequal = 549;\n    t.ocircumflex = 500;\n    t.ntilde = 500;\n    t.Uhungarumlaut = 722;\n    t.Eacute = 611;\n    t.emacron = 444;\n    t.gbreve = 500;\n    t.onequarter = 750;\n    t.Scaron = 500;\n    t.Scommaaccent = 500;\n    t.Ohungarumlaut = 722;\n    t.degree = 400;\n    t.ograve = 500;\n    t.Ccaron = 667;\n    t.ugrave = 500;\n    t.radical = 453;\n    t.Dcaron = 722;\n    t.rcommaaccent = 389;\n    t.Ntilde = 667;\n    t.otilde = 500;\n    t.Rcommaaccent = 611;\n    t.Lcommaaccent = 556;\n    t.Atilde = 611;\n    t.Aogonek = 611;\n    t.Aring = 611;\n    t.Otilde = 722;\n    t.zdotaccent = 389;\n    t.Ecaron = 611;\n    t.Iogonek = 333;\n    t.kcommaaccent = 444;\n    t.minus = 675;\n    t.Icircumflex = 333;\n    t.ncaron = 500;\n    t.tcommaaccent = 278;\n    t.logicalnot = 675;\n    t.odieresis = 500;\n    t.udieresis = 500;\n    t.notequal = 549;\n    t.gcommaaccent = 500;\n    t.eth = 500;\n    t.zcaron = 389;\n    t.ncommaaccent = 500;\n    t.onesuperior = 300;\n    t.imacron = 278;\n    t.Euro = 500;\n  });\n  t.ZapfDingbats = getLookupTableFactory(function (t) {\n    t.space = 278;\n    t.a1 = 974;\n    t.a2 = 961;\n    t.a202 = 974;\n    t.a3 = 980;\n    t.a4 = 719;\n    t.a5 = 789;\n    t.a119 = 790;\n    t.a118 = 791;\n    t.a117 = 690;\n    t.a11 = 960;\n    t.a12 = 939;\n    t.a13 = 549;\n    t.a14 = 855;\n    t.a15 = 911;\n    t.a16 = 933;\n    t.a105 = 911;\n    t.a17 = 945;\n    t.a18 = 974;\n    t.a19 = 755;\n    t.a20 = 846;\n    t.a21 = 762;\n    t.a22 = 761;\n    t.a23 = 571;\n    t.a24 = 677;\n    t.a25 = 763;\n    t.a26 = 760;\n    t.a27 = 759;\n    t.a28 = 754;\n    t.a6 = 494;\n    t.a7 = 552;\n    t.a8 = 537;\n    t.a9 = 577;\n    t.a10 = 692;\n    t.a29 = 786;\n    t.a30 = 788;\n    t.a31 = 788;\n    t.a32 = 790;\n    t.a33 = 793;\n    t.a34 = 794;\n    t.a35 = 816;\n    t.a36 = 823;\n    t.a37 = 789;\n    t.a38 = 841;\n    t.a39 = 823;\n    t.a40 = 833;\n    t.a41 = 816;\n    t.a42 = 831;\n    t.a43 = 923;\n    t.a44 = 744;\n    t.a45 = 723;\n    t.a46 = 749;\n    t.a47 = 790;\n    t.a48 = 792;\n    t.a49 = 695;\n    t.a50 = 776;\n    t.a51 = 768;\n    t.a52 = 792;\n    t.a53 = 759;\n    t.a54 = 707;\n    t.a55 = 708;\n    t.a56 = 682;\n    t.a57 = 701;\n    t.a58 = 826;\n    t.a59 = 815;\n    t.a60 = 789;\n    t.a61 = 789;\n    t.a62 = 707;\n    t.a63 = 687;\n    t.a64 = 696;\n    t.a65 = 689;\n    t.a66 = 786;\n    t.a67 = 787;\n    t.a68 = 713;\n    t.a69 = 791;\n    t.a70 = 785;\n    t.a71 = 791;\n    t.a72 = 873;\n    t.a73 = 761;\n    t.a74 = 762;\n    t.a203 = 762;\n    t.a75 = 759;\n    t.a204 = 759;\n    t.a76 = 892;\n    t.a77 = 892;\n    t.a78 = 788;\n    t.a79 = 784;\n    t.a81 = 438;\n    t.a82 = 138;\n    t.a83 = 277;\n    t.a84 = 415;\n    t.a97 = 392;\n    t.a98 = 392;\n    t.a99 = 668;\n    t.a100 = 668;\n    t.a89 = 390;\n    t.a90 = 390;\n    t.a93 = 317;\n    t.a94 = 317;\n    t.a91 = 276;\n    t.a92 = 276;\n    t.a205 = 509;\n    t.a85 = 509;\n    t.a206 = 410;\n    t.a86 = 410;\n    t.a87 = 234;\n    t.a88 = 234;\n    t.a95 = 334;\n    t.a96 = 334;\n    t.a101 = 732;\n    t.a102 = 544;\n    t.a103 = 544;\n    t.a104 = 910;\n    t.a106 = 667;\n    t.a107 = 760;\n    t.a108 = 760;\n    t.a112 = 776;\n    t.a111 = 595;\n    t.a110 = 694;\n    t.a109 = 626;\n    t.a120 = 788;\n    t.a121 = 788;\n    t.a122 = 788;\n    t.a123 = 788;\n    t.a124 = 788;\n    t.a125 = 788;\n    t.a126 = 788;\n    t.a127 = 788;\n    t.a128 = 788;\n    t.a129 = 788;\n    t.a130 = 788;\n    t.a131 = 788;\n    t.a132 = 788;\n    t.a133 = 788;\n    t.a134 = 788;\n    t.a135 = 788;\n    t.a136 = 788;\n    t.a137 = 788;\n    t.a138 = 788;\n    t.a139 = 788;\n    t.a140 = 788;\n    t.a141 = 788;\n    t.a142 = 788;\n    t.a143 = 788;\n    t.a144 = 788;\n    t.a145 = 788;\n    t.a146 = 788;\n    t.a147 = 788;\n    t.a148 = 788;\n    t.a149 = 788;\n    t.a150 = 788;\n    t.a151 = 788;\n    t.a152 = 788;\n    t.a153 = 788;\n    t.a154 = 788;\n    t.a155 = 788;\n    t.a156 = 788;\n    t.a157 = 788;\n    t.a158 = 788;\n    t.a159 = 788;\n    t.a160 = 894;\n    t.a161 = 838;\n    t.a163 = 1016;\n    t.a164 = 458;\n    t.a196 = 748;\n    t.a165 = 924;\n    t.a192 = 748;\n    t.a166 = 918;\n    t.a167 = 927;\n    t.a168 = 928;\n    t.a169 = 928;\n    t.a170 = 834;\n    t.a171 = 873;\n    t.a172 = 828;\n    t.a173 = 924;\n    t.a162 = 924;\n    t.a174 = 917;\n    t.a175 = 930;\n    t.a176 = 931;\n    t.a177 = 463;\n    t.a178 = 883;\n    t.a179 = 836;\n    t.a193 = 836;\n    t.a180 = 867;\n    t.a199 = 867;\n    t.a181 = 696;\n    t.a200 = 696;\n    t.a182 = 874;\n    t.a201 = 874;\n    t.a183 = 760;\n    t.a184 = 946;\n    t.a197 = 771;\n    t.a185 = 865;\n    t.a194 = 771;\n    t.a198 = 888;\n    t.a186 = 967;\n    t.a195 = 888;\n    t.a187 = 831;\n    t.a188 = 873;\n    t.a189 = 927;\n    t.a190 = 970;\n    t.a191 = 918;\n  });\n});\nconst getFontBasicMetrics = getLookupTableFactory(function (t) {\n  t.Courier = {\n    ascent: 629,\n    descent: -157,\n    capHeight: 562,\n    xHeight: -426\n  };\n  t[\"Courier-Bold\"] = {\n    ascent: 629,\n    descent: -157,\n    capHeight: 562,\n    xHeight: 439\n  };\n  t[\"Courier-Oblique\"] = {\n    ascent: 629,\n    descent: -157,\n    capHeight: 562,\n    xHeight: 426\n  };\n  t[\"Courier-BoldOblique\"] = {\n    ascent: 629,\n    descent: -157,\n    capHeight: 562,\n    xHeight: 426\n  };\n  t.Helvetica = {\n    ascent: 718,\n    descent: -207,\n    capHeight: 718,\n    xHeight: 523\n  };\n  t[\"Helvetica-Bold\"] = {\n    ascent: 718,\n    descent: -207,\n    capHeight: 718,\n    xHeight: 532\n  };\n  t[\"Helvetica-Oblique\"] = {\n    ascent: 718,\n    descent: -207,\n    capHeight: 718,\n    xHeight: 523\n  };\n  t[\"Helvetica-BoldOblique\"] = {\n    ascent: 718,\n    descent: -207,\n    capHeight: 718,\n    xHeight: 532\n  };\n  t[\"Times-Roman\"] = {\n    ascent: 683,\n    descent: -217,\n    capHeight: 662,\n    xHeight: 450\n  };\n  t[\"Times-Bold\"] = {\n    ascent: 683,\n    descent: -217,\n    capHeight: 676,\n    xHeight: 461\n  };\n  t[\"Times-Italic\"] = {\n    ascent: 683,\n    descent: -217,\n    capHeight: 653,\n    xHeight: 441\n  };\n  t[\"Times-BoldItalic\"] = {\n    ascent: 683,\n    descent: -217,\n    capHeight: 669,\n    xHeight: 462\n  };\n  t.Symbol = {\n    ascent: Math.NaN,\n    descent: Math.NaN,\n    capHeight: Math.NaN,\n    xHeight: Math.NaN\n  };\n  t.ZapfDingbats = {\n    ascent: Math.NaN,\n    descent: Math.NaN,\n    capHeight: Math.NaN,\n    xHeight: Math.NaN\n  };\n});\n\n;// CONCATENATED MODULE: ./src/core/glyf.js\nconst ON_CURVE_POINT = 1 << 0;\nconst X_SHORT_VECTOR = 1 << 1;\nconst Y_SHORT_VECTOR = 1 << 2;\nconst REPEAT_FLAG = 1 << 3;\nconst X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 1 << 4;\nconst Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 1 << 5;\nconst OVERLAP_SIMPLE = 1 << 6;\nconst ARG_1_AND_2_ARE_WORDS = 1 << 0;\nconst ARGS_ARE_XY_VALUES = 1 << 1;\nconst WE_HAVE_A_SCALE = 1 << 3;\nconst MORE_COMPONENTS = 1 << 5;\nconst WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;\nconst WE_HAVE_A_TWO_BY_TWO = 1 << 7;\nconst WE_HAVE_INSTRUCTIONS = 1 << 8;\nclass GlyfTable {\n  constructor({\n    glyfTable,\n    isGlyphLocationsLong,\n    locaTable,\n    numGlyphs\n  }) {\n    this.glyphs = [];\n    const loca = new DataView(locaTable.buffer, locaTable.byteOffset, locaTable.byteLength);\n    const glyf = new DataView(glyfTable.buffer, glyfTable.byteOffset, glyfTable.byteLength);\n    const offsetSize = isGlyphLocationsLong ? 4 : 2;\n    let prev = isGlyphLocationsLong ? loca.getUint32(0) : 2 * loca.getUint16(0);\n    let pos = 0;\n    for (let i = 0; i < numGlyphs; i++) {\n      pos += offsetSize;\n      const next = isGlyphLocationsLong ? loca.getUint32(pos) : 2 * loca.getUint16(pos);\n      if (next === prev) {\n        this.glyphs.push(new Glyph({}));\n        continue;\n      }\n      const glyph = Glyph.parse(prev, glyf);\n      this.glyphs.push(glyph);\n      prev = next;\n    }\n  }\n  getSize() {\n    return this.glyphs.reduce((a, g) => {\n      const size = g.getSize();\n      return a + (size + 3 & ~3);\n    }, 0);\n  }\n  write() {\n    const totalSize = this.getSize();\n    const glyfTable = new DataView(new ArrayBuffer(totalSize));\n    const isLocationLong = totalSize > 0x1fffe;\n    const offsetSize = isLocationLong ? 4 : 2;\n    const locaTable = new DataView(new ArrayBuffer((this.glyphs.length + 1) * offsetSize));\n    if (isLocationLong) {\n      locaTable.setUint32(0, 0);\n    } else {\n      locaTable.setUint16(0, 0);\n    }\n    let pos = 0;\n    let locaIndex = 0;\n    for (const glyph of this.glyphs) {\n      pos += glyph.write(pos, glyfTable);\n      pos = pos + 3 & ~3;\n      locaIndex += offsetSize;\n      if (isLocationLong) {\n        locaTable.setUint32(locaIndex, pos);\n      } else {\n        locaTable.setUint16(locaIndex, pos >> 1);\n      }\n    }\n    return {\n      isLocationLong,\n      loca: new Uint8Array(locaTable.buffer),\n      glyf: new Uint8Array(glyfTable.buffer)\n    };\n  }\n  scale(factors) {\n    for (let i = 0, ii = this.glyphs.length; i < ii; i++) {\n      this.glyphs[i].scale(factors[i]);\n    }\n  }\n}\nclass Glyph {\n  constructor({\n    header = null,\n    simple = null,\n    composites = null\n  }) {\n    this.header = header;\n    this.simple = simple;\n    this.composites = composites;\n  }\n  static parse(pos, glyf) {\n    const [read, header] = GlyphHeader.parse(pos, glyf);\n    pos += read;\n    if (header.numberOfContours < 0) {\n      const composites = [];\n      while (true) {\n        const [n, composite] = CompositeGlyph.parse(pos, glyf);\n        pos += n;\n        composites.push(composite);\n        if (!(composite.flags & MORE_COMPONENTS)) {\n          break;\n        }\n      }\n      return new Glyph({\n        header,\n        composites\n      });\n    }\n    const simple = SimpleGlyph.parse(pos, glyf, header.numberOfContours);\n    return new Glyph({\n      header,\n      simple\n    });\n  }\n  getSize() {\n    if (!this.header) {\n      return 0;\n    }\n    const size = this.simple ? this.simple.getSize() : this.composites.reduce((a, c) => a + c.getSize(), 0);\n    return this.header.getSize() + size;\n  }\n  write(pos, buf) {\n    if (!this.header) {\n      return 0;\n    }\n    const spos = pos;\n    pos += this.header.write(pos, buf);\n    if (this.simple) {\n      pos += this.simple.write(pos, buf);\n    } else {\n      for (const composite of this.composites) {\n        pos += composite.write(pos, buf);\n      }\n    }\n    return pos - spos;\n  }\n  scale(factor) {\n    if (!this.header) {\n      return;\n    }\n    const xMiddle = (this.header.xMin + this.header.xMax) / 2;\n    this.header.scale(xMiddle, factor);\n    if (this.simple) {\n      this.simple.scale(xMiddle, factor);\n    } else {\n      for (const composite of this.composites) {\n        composite.scale(xMiddle, factor);\n      }\n    }\n  }\n}\nclass GlyphHeader {\n  constructor({\n    numberOfContours,\n    xMin,\n    yMin,\n    xMax,\n    yMax\n  }) {\n    this.numberOfContours = numberOfContours;\n    this.xMin = xMin;\n    this.yMin = yMin;\n    this.xMax = xMax;\n    this.yMax = yMax;\n  }\n  static parse(pos, glyf) {\n    return [10, new GlyphHeader({\n      numberOfContours: glyf.getInt16(pos),\n      xMin: glyf.getInt16(pos + 2),\n      yMin: glyf.getInt16(pos + 4),\n      xMax: glyf.getInt16(pos + 6),\n      yMax: glyf.getInt16(pos + 8)\n    })];\n  }\n  getSize() {\n    return 10;\n  }\n  write(pos, buf) {\n    buf.setInt16(pos, this.numberOfContours);\n    buf.setInt16(pos + 2, this.xMin);\n    buf.setInt16(pos + 4, this.yMin);\n    buf.setInt16(pos + 6, this.xMax);\n    buf.setInt16(pos + 8, this.yMax);\n    return 10;\n  }\n  scale(x, factor) {\n    this.xMin = Math.round(x + (this.xMin - x) * factor);\n    this.xMax = Math.round(x + (this.xMax - x) * factor);\n  }\n}\nclass Contour {\n  constructor({\n    flags,\n    xCoordinates,\n    yCoordinates\n  }) {\n    this.xCoordinates = xCoordinates;\n    this.yCoordinates = yCoordinates;\n    this.flags = flags;\n  }\n}\nclass SimpleGlyph {\n  constructor({\n    contours,\n    instructions\n  }) {\n    this.contours = contours;\n    this.instructions = instructions;\n  }\n  static parse(pos, glyf, numberOfContours) {\n    const endPtsOfContours = [];\n    for (let i = 0; i < numberOfContours; i++) {\n      const endPt = glyf.getUint16(pos);\n      pos += 2;\n      endPtsOfContours.push(endPt);\n    }\n    const numberOfPt = endPtsOfContours[numberOfContours - 1] + 1;\n    const instructionLength = glyf.getUint16(pos);\n    pos += 2;\n    const instructions = new Uint8Array(glyf).slice(pos, pos + instructionLength);\n    pos += instructionLength;\n    const flags = [];\n    for (let i = 0; i < numberOfPt; pos++, i++) {\n      let flag = glyf.getUint8(pos);\n      flags.push(flag);\n      if (flag & REPEAT_FLAG) {\n        const count = glyf.getUint8(++pos);\n        flag ^= REPEAT_FLAG;\n        for (let m = 0; m < count; m++) {\n          flags.push(flag);\n        }\n        i += count;\n      }\n    }\n    const allXCoordinates = [];\n    let xCoordinates = [];\n    let yCoordinates = [];\n    let pointFlags = [];\n    const contours = [];\n    let endPtsOfContoursIndex = 0;\n    let lastCoordinate = 0;\n    for (let i = 0; i < numberOfPt; i++) {\n      const flag = flags[i];\n      if (flag & X_SHORT_VECTOR) {\n        const x = glyf.getUint8(pos++);\n        lastCoordinate += flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR ? x : -x;\n        xCoordinates.push(lastCoordinate);\n      } else if (flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) {\n        xCoordinates.push(lastCoordinate);\n      } else {\n        lastCoordinate += glyf.getInt16(pos);\n        pos += 2;\n        xCoordinates.push(lastCoordinate);\n      }\n      if (endPtsOfContours[endPtsOfContoursIndex] === i) {\n        endPtsOfContoursIndex++;\n        allXCoordinates.push(xCoordinates);\n        xCoordinates = [];\n      }\n    }\n    lastCoordinate = 0;\n    endPtsOfContoursIndex = 0;\n    for (let i = 0; i < numberOfPt; i++) {\n      const flag = flags[i];\n      if (flag & Y_SHORT_VECTOR) {\n        const y = glyf.getUint8(pos++);\n        lastCoordinate += flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR ? y : -y;\n        yCoordinates.push(lastCoordinate);\n      } else if (flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) {\n        yCoordinates.push(lastCoordinate);\n      } else {\n        lastCoordinate += glyf.getInt16(pos);\n        pos += 2;\n        yCoordinates.push(lastCoordinate);\n      }\n      pointFlags.push(flag & ON_CURVE_POINT | flag & OVERLAP_SIMPLE);\n      if (endPtsOfContours[endPtsOfContoursIndex] === i) {\n        xCoordinates = allXCoordinates[endPtsOfContoursIndex];\n        endPtsOfContoursIndex++;\n        contours.push(new Contour({\n          flags: pointFlags,\n          xCoordinates,\n          yCoordinates\n        }));\n        yCoordinates = [];\n        pointFlags = [];\n      }\n    }\n    return new SimpleGlyph({\n      contours,\n      instructions\n    });\n  }\n  getSize() {\n    let size = this.contours.length * 2 + 2 + this.instructions.length;\n    let lastX = 0;\n    let lastY = 0;\n    for (const contour of this.contours) {\n      size += contour.flags.length;\n      for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) {\n        const x = contour.xCoordinates[i];\n        const y = contour.yCoordinates[i];\n        let abs = Math.abs(x - lastX);\n        if (abs > 255) {\n          size += 2;\n        } else if (abs > 0) {\n          size += 1;\n        }\n        lastX = x;\n        abs = Math.abs(y - lastY);\n        if (abs > 255) {\n          size += 2;\n        } else if (abs > 0) {\n          size += 1;\n        }\n        lastY = y;\n      }\n    }\n    return size;\n  }\n  write(pos, buf) {\n    const spos = pos;\n    const xCoordinates = [];\n    const yCoordinates = [];\n    const flags = [];\n    let lastX = 0;\n    let lastY = 0;\n    for (const contour of this.contours) {\n      for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) {\n        let flag = contour.flags[i];\n        const x = contour.xCoordinates[i];\n        let delta = x - lastX;\n        if (delta === 0) {\n          flag |= X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR;\n          xCoordinates.push(0);\n        } else {\n          const abs = Math.abs(delta);\n          if (abs <= 255) {\n            flag |= delta >= 0 ? X_SHORT_VECTOR | X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR : X_SHORT_VECTOR;\n            xCoordinates.push(abs);\n          } else {\n            xCoordinates.push(delta);\n          }\n        }\n        lastX = x;\n        const y = contour.yCoordinates[i];\n        delta = y - lastY;\n        if (delta === 0) {\n          flag |= Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR;\n          yCoordinates.push(0);\n        } else {\n          const abs = Math.abs(delta);\n          if (abs <= 255) {\n            flag |= delta >= 0 ? Y_SHORT_VECTOR | Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR : Y_SHORT_VECTOR;\n            yCoordinates.push(abs);\n          } else {\n            yCoordinates.push(delta);\n          }\n        }\n        lastY = y;\n        flags.push(flag);\n      }\n      buf.setUint16(pos, xCoordinates.length - 1);\n      pos += 2;\n    }\n    buf.setUint16(pos, this.instructions.length);\n    pos += 2;\n    if (this.instructions.length) {\n      new Uint8Array(buf.buffer, 0, buf.buffer.byteLength).set(this.instructions, pos);\n      pos += this.instructions.length;\n    }\n    for (const flag of flags) {\n      buf.setUint8(pos++, flag);\n    }\n    for (let i = 0, ii = xCoordinates.length; i < ii; i++) {\n      const x = xCoordinates[i];\n      const flag = flags[i];\n      if (flag & X_SHORT_VECTOR) {\n        buf.setUint8(pos++, x);\n      } else if (!(flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR)) {\n        buf.setInt16(pos, x);\n        pos += 2;\n      }\n    }\n    for (let i = 0, ii = yCoordinates.length; i < ii; i++) {\n      const y = yCoordinates[i];\n      const flag = flags[i];\n      if (flag & Y_SHORT_VECTOR) {\n        buf.setUint8(pos++, y);\n      } else if (!(flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR)) {\n        buf.setInt16(pos, y);\n        pos += 2;\n      }\n    }\n    return pos - spos;\n  }\n  scale(x, factor) {\n    for (const contour of this.contours) {\n      if (contour.xCoordinates.length === 0) {\n        continue;\n      }\n      for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) {\n        contour.xCoordinates[i] = Math.round(x + (contour.xCoordinates[i] - x) * factor);\n      }\n    }\n  }\n}\nclass CompositeGlyph {\n  constructor({\n    flags,\n    glyphIndex,\n    argument1,\n    argument2,\n    transf,\n    instructions\n  }) {\n    this.flags = flags;\n    this.glyphIndex = glyphIndex;\n    this.argument1 = argument1;\n    this.argument2 = argument2;\n    this.transf = transf;\n    this.instructions = instructions;\n  }\n  static parse(pos, glyf) {\n    const spos = pos;\n    const transf = [];\n    let flags = glyf.getUint16(pos);\n    const glyphIndex = glyf.getUint16(pos + 2);\n    pos += 4;\n    let argument1, argument2;\n    if (flags & ARG_1_AND_2_ARE_WORDS) {\n      if (flags & ARGS_ARE_XY_VALUES) {\n        argument1 = glyf.getInt16(pos);\n        argument2 = glyf.getInt16(pos + 2);\n      } else {\n        argument1 = glyf.getUint16(pos);\n        argument2 = glyf.getUint16(pos + 2);\n      }\n      pos += 4;\n      flags ^= ARG_1_AND_2_ARE_WORDS;\n    } else {\n      if (flags & ARGS_ARE_XY_VALUES) {\n        argument1 = glyf.getInt8(pos);\n        argument2 = glyf.getInt8(pos + 1);\n      } else {\n        argument1 = glyf.getUint8(pos);\n        argument2 = glyf.getUint8(pos + 1);\n      }\n      pos += 2;\n    }\n    if (flags & WE_HAVE_A_SCALE) {\n      transf.push(glyf.getUint16(pos));\n      pos += 2;\n    } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {\n      transf.push(glyf.getUint16(pos), glyf.getUint16(pos + 2));\n      pos += 4;\n    } else if (flags & WE_HAVE_A_TWO_BY_TWO) {\n      transf.push(glyf.getUint16(pos), glyf.getUint16(pos + 2), glyf.getUint16(pos + 4), glyf.getUint16(pos + 6));\n      pos += 8;\n    }\n    let instructions = null;\n    if (flags & WE_HAVE_INSTRUCTIONS) {\n      const instructionLength = glyf.getUint16(pos);\n      pos += 2;\n      instructions = new Uint8Array(glyf).slice(pos, pos + instructionLength);\n      pos += instructionLength;\n    }\n    return [pos - spos, new CompositeGlyph({\n      flags,\n      glyphIndex,\n      argument1,\n      argument2,\n      transf,\n      instructions\n    })];\n  }\n  getSize() {\n    let size = 2 + 2 + this.transf.length * 2;\n    if (this.flags & WE_HAVE_INSTRUCTIONS) {\n      size += 2 + this.instructions.length;\n    }\n    size += 2;\n    if (this.flags & 2) {\n      if (!(this.argument1 >= -128 && this.argument1 <= 127 && this.argument2 >= -128 && this.argument2 <= 127)) {\n        size += 2;\n      }\n    } else if (!(this.argument1 >= 0 && this.argument1 <= 255 && this.argument2 >= 0 && this.argument2 <= 255)) {\n      size += 2;\n    }\n    return size;\n  }\n  write(pos, buf) {\n    const spos = pos;\n    if (this.flags & ARGS_ARE_XY_VALUES) {\n      if (!(this.argument1 >= -128 && this.argument1 <= 127 && this.argument2 >= -128 && this.argument2 <= 127)) {\n        this.flags |= ARG_1_AND_2_ARE_WORDS;\n      }\n    } else if (!(this.argument1 >= 0 && this.argument1 <= 255 && this.argument2 >= 0 && this.argument2 <= 255)) {\n      this.flags |= ARG_1_AND_2_ARE_WORDS;\n    }\n    buf.setUint16(pos, this.flags);\n    buf.setUint16(pos + 2, this.glyphIndex);\n    pos += 4;\n    if (this.flags & ARG_1_AND_2_ARE_WORDS) {\n      if (this.flags & ARGS_ARE_XY_VALUES) {\n        buf.setInt16(pos, this.argument1);\n        buf.setInt16(pos + 2, this.argument2);\n      } else {\n        buf.setUint16(pos, this.argument1);\n        buf.setUint16(pos + 2, this.argument2);\n      }\n      pos += 4;\n    } else {\n      buf.setUint8(pos, this.argument1);\n      buf.setUint8(pos + 1, this.argument2);\n      pos += 2;\n    }\n    if (this.flags & WE_HAVE_INSTRUCTIONS) {\n      buf.setUint16(pos, this.instructions.length);\n      pos += 2;\n      if (this.instructions.length) {\n        new Uint8Array(buf.buffer, 0, buf.buffer.byteLength).set(this.instructions, pos);\n        pos += this.instructions.length;\n      }\n    }\n    return pos - spos;\n  }\n  scale(x, factor) {}\n}\n\n;// CONCATENATED MODULE: ./src/core/opentype_file_builder.js\n\n\nfunction writeInt16(dest, offset, num) {\n  dest[offset] = num >> 8 & 0xff;\n  dest[offset + 1] = num & 0xff;\n}\nfunction writeInt32(dest, offset, num) {\n  dest[offset] = num >> 24 & 0xff;\n  dest[offset + 1] = num >> 16 & 0xff;\n  dest[offset + 2] = num >> 8 & 0xff;\n  dest[offset + 3] = num & 0xff;\n}\nfunction writeData(dest, offset, data) {\n  if (data instanceof Uint8Array) {\n    dest.set(data, offset);\n  } else if (typeof data === \"string\") {\n    for (let i = 0, ii = data.length; i < ii; i++) {\n      dest[offset++] = data.charCodeAt(i) & 0xff;\n    }\n  } else {\n    for (const num of data) {\n      dest[offset++] = num & 0xff;\n    }\n  }\n}\nconst OTF_HEADER_SIZE = 12;\nconst OTF_TABLE_ENTRY_SIZE = 16;\nclass OpenTypeFileBuilder {\n  constructor(sfnt) {\n    this.sfnt = sfnt;\n    this.tables = Object.create(null);\n  }\n  static getSearchParams(entriesCount, entrySize) {\n    let maxPower2 = 1,\n      log2 = 0;\n    while ((maxPower2 ^ entriesCount) > maxPower2) {\n      maxPower2 <<= 1;\n      log2++;\n    }\n    const searchRange = maxPower2 * entrySize;\n    return {\n      range: searchRange,\n      entry: log2,\n      rangeShift: entrySize * entriesCount - searchRange\n    };\n  }\n  toArray() {\n    let sfnt = this.sfnt;\n    const tables = this.tables;\n    const tablesNames = Object.keys(tables);\n    tablesNames.sort();\n    const numTables = tablesNames.length;\n    let i, j, jj, table, tableName;\n    let offset = OTF_HEADER_SIZE + numTables * OTF_TABLE_ENTRY_SIZE;\n    const tableOffsets = [offset];\n    for (i = 0; i < numTables; i++) {\n      table = tables[tablesNames[i]];\n      const paddedLength = (table.length + 3 & ~3) >>> 0;\n      offset += paddedLength;\n      tableOffsets.push(offset);\n    }\n    const file = new Uint8Array(offset);\n    for (i = 0; i < numTables; i++) {\n      table = tables[tablesNames[i]];\n      writeData(file, tableOffsets[i], table);\n    }\n    if (sfnt === \"true\") {\n      sfnt = string32(0x00010000);\n    }\n    file[0] = sfnt.charCodeAt(0) & 0xff;\n    file[1] = sfnt.charCodeAt(1) & 0xff;\n    file[2] = sfnt.charCodeAt(2) & 0xff;\n    file[3] = sfnt.charCodeAt(3) & 0xff;\n    writeInt16(file, 4, numTables);\n    const searchParams = OpenTypeFileBuilder.getSearchParams(numTables, 16);\n    writeInt16(file, 6, searchParams.range);\n    writeInt16(file, 8, searchParams.entry);\n    writeInt16(file, 10, searchParams.rangeShift);\n    offset = OTF_HEADER_SIZE;\n    for (i = 0; i < numTables; i++) {\n      tableName = tablesNames[i];\n      file[offset] = tableName.charCodeAt(0) & 0xff;\n      file[offset + 1] = tableName.charCodeAt(1) & 0xff;\n      file[offset + 2] = tableName.charCodeAt(2) & 0xff;\n      file[offset + 3] = tableName.charCodeAt(3) & 0xff;\n      let checksum = 0;\n      for (j = tableOffsets[i], jj = tableOffsets[i + 1]; j < jj; j += 4) {\n        const quad = readUint32(file, j);\n        checksum = checksum + quad >>> 0;\n      }\n      writeInt32(file, offset + 4, checksum);\n      writeInt32(file, offset + 8, tableOffsets[i]);\n      writeInt32(file, offset + 12, tables[tableName].length);\n      offset += OTF_TABLE_ENTRY_SIZE;\n    }\n    return file;\n  }\n  addTable(tag, data) {\n    if (tag in this.tables) {\n      throw new Error(\"Table \" + tag + \" already exists\");\n    }\n    this.tables[tag] = data;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/type1_parser.js\n\n\n\n\nconst HINTING_ENABLED = false;\nconst COMMAND_MAP = {\n  hstem: [1],\n  vstem: [3],\n  vmoveto: [4],\n  rlineto: [5],\n  hlineto: [6],\n  vlineto: [7],\n  rrcurveto: [8],\n  callsubr: [10],\n  flex: [12, 35],\n  drop: [12, 18],\n  endchar: [14],\n  rmoveto: [21],\n  hmoveto: [22],\n  vhcurveto: [30],\n  hvcurveto: [31]\n};\nclass Type1CharString {\n  constructor() {\n    this.width = 0;\n    this.lsb = 0;\n    this.flexing = false;\n    this.output = [];\n    this.stack = [];\n  }\n  convert(encoded, subrs, seacAnalysisEnabled) {\n    const count = encoded.length;\n    let error = false;\n    let wx, sbx, subrNumber;\n    for (let i = 0; i < count; i++) {\n      let value = encoded[i];\n      if (value < 32) {\n        if (value === 12) {\n          value = (value << 8) + encoded[++i];\n        }\n        switch (value) {\n          case 1:\n            if (!HINTING_ENABLED) {\n              this.stack = [];\n              break;\n            }\n            error = this.executeCommand(2, COMMAND_MAP.hstem);\n            break;\n          case 3:\n            if (!HINTING_ENABLED) {\n              this.stack = [];\n              break;\n            }\n            error = this.executeCommand(2, COMMAND_MAP.vstem);\n            break;\n          case 4:\n            if (this.flexing) {\n              if (this.stack.length < 1) {\n                error = true;\n                break;\n              }\n              const dy = this.stack.pop();\n              this.stack.push(0, dy);\n              break;\n            }\n            error = this.executeCommand(1, COMMAND_MAP.vmoveto);\n            break;\n          case 5:\n            error = this.executeCommand(2, COMMAND_MAP.rlineto);\n            break;\n          case 6:\n            error = this.executeCommand(1, COMMAND_MAP.hlineto);\n            break;\n          case 7:\n            error = this.executeCommand(1, COMMAND_MAP.vlineto);\n            break;\n          case 8:\n            error = this.executeCommand(6, COMMAND_MAP.rrcurveto);\n            break;\n          case 9:\n            this.stack = [];\n            break;\n          case 10:\n            if (this.stack.length < 1) {\n              error = true;\n              break;\n            }\n            subrNumber = this.stack.pop();\n            if (!subrs[subrNumber]) {\n              error = true;\n              break;\n            }\n            error = this.convert(subrs[subrNumber], subrs, seacAnalysisEnabled);\n            break;\n          case 11:\n            return error;\n          case 13:\n            if (this.stack.length < 2) {\n              error = true;\n              break;\n            }\n            wx = this.stack.pop();\n            sbx = this.stack.pop();\n            this.lsb = sbx;\n            this.width = wx;\n            this.stack.push(wx, sbx);\n            error = this.executeCommand(2, COMMAND_MAP.hmoveto);\n            break;\n          case 14:\n            this.output.push(COMMAND_MAP.endchar[0]);\n            break;\n          case 21:\n            if (this.flexing) {\n              break;\n            }\n            error = this.executeCommand(2, COMMAND_MAP.rmoveto);\n            break;\n          case 22:\n            if (this.flexing) {\n              this.stack.push(0);\n              break;\n            }\n            error = this.executeCommand(1, COMMAND_MAP.hmoveto);\n            break;\n          case 30:\n            error = this.executeCommand(4, COMMAND_MAP.vhcurveto);\n            break;\n          case 31:\n            error = this.executeCommand(4, COMMAND_MAP.hvcurveto);\n            break;\n          case (12 << 8) + 0:\n            this.stack = [];\n            break;\n          case (12 << 8) + 1:\n            if (!HINTING_ENABLED) {\n              this.stack = [];\n              break;\n            }\n            error = this.executeCommand(2, COMMAND_MAP.vstem);\n            break;\n          case (12 << 8) + 2:\n            if (!HINTING_ENABLED) {\n              this.stack = [];\n              break;\n            }\n            error = this.executeCommand(2, COMMAND_MAP.hstem);\n            break;\n          case (12 << 8) + 6:\n            if (seacAnalysisEnabled) {\n              const asb = this.stack.at(-5);\n              this.seac = this.stack.splice(-4, 4);\n              this.seac[0] += this.lsb - asb;\n              error = this.executeCommand(0, COMMAND_MAP.endchar);\n            } else {\n              error = this.executeCommand(4, COMMAND_MAP.endchar);\n            }\n            break;\n          case (12 << 8) + 7:\n            if (this.stack.length < 4) {\n              error = true;\n              break;\n            }\n            this.stack.pop();\n            wx = this.stack.pop();\n            const sby = this.stack.pop();\n            sbx = this.stack.pop();\n            this.lsb = sbx;\n            this.width = wx;\n            this.stack.push(wx, sbx, sby);\n            error = this.executeCommand(3, COMMAND_MAP.rmoveto);\n            break;\n          case (12 << 8) + 12:\n            if (this.stack.length < 2) {\n              error = true;\n              break;\n            }\n            const num2 = this.stack.pop();\n            const num1 = this.stack.pop();\n            this.stack.push(num1 / num2);\n            break;\n          case (12 << 8) + 16:\n            if (this.stack.length < 2) {\n              error = true;\n              break;\n            }\n            subrNumber = this.stack.pop();\n            const numArgs = this.stack.pop();\n            if (subrNumber === 0 && numArgs === 3) {\n              const flexArgs = this.stack.splice(-17, 17);\n              this.stack.push(flexArgs[2] + flexArgs[0], flexArgs[3] + flexArgs[1], flexArgs[4], flexArgs[5], flexArgs[6], flexArgs[7], flexArgs[8], flexArgs[9], flexArgs[10], flexArgs[11], flexArgs[12], flexArgs[13], flexArgs[14]);\n              error = this.executeCommand(13, COMMAND_MAP.flex, true);\n              this.flexing = false;\n              this.stack.push(flexArgs[15], flexArgs[16]);\n            } else if (subrNumber === 1 && numArgs === 0) {\n              this.flexing = true;\n            }\n            break;\n          case (12 << 8) + 17:\n            break;\n          case (12 << 8) + 33:\n            this.stack = [];\n            break;\n          default:\n            warn('Unknown type 1 charstring command of \"' + value + '\"');\n            break;\n        }\n        if (error) {\n          break;\n        }\n        continue;\n      } else if (value <= 246) {\n        value -= 139;\n      } else if (value <= 250) {\n        value = (value - 247) * 256 + encoded[++i] + 108;\n      } else if (value <= 254) {\n        value = -((value - 251) * 256) - encoded[++i] - 108;\n      } else {\n        value = (encoded[++i] & 0xff) << 24 | (encoded[++i] & 0xff) << 16 | (encoded[++i] & 0xff) << 8 | (encoded[++i] & 0xff) << 0;\n      }\n      this.stack.push(value);\n    }\n    return error;\n  }\n  executeCommand(howManyArgs, command, keepStack) {\n    const stackLength = this.stack.length;\n    if (howManyArgs > stackLength) {\n      return true;\n    }\n    const start = stackLength - howManyArgs;\n    for (let i = start; i < stackLength; i++) {\n      let value = this.stack[i];\n      if (Number.isInteger(value)) {\n        this.output.push(28, value >> 8 & 0xff, value & 0xff);\n      } else {\n        value = 65536 * value | 0;\n        this.output.push(255, value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff);\n      }\n    }\n    this.output.push(...command);\n    if (keepStack) {\n      this.stack.splice(start, howManyArgs);\n    } else {\n      this.stack.length = 0;\n    }\n    return false;\n  }\n}\nconst EEXEC_ENCRYPT_KEY = 55665;\nconst CHAR_STRS_ENCRYPT_KEY = 4330;\nfunction isHexDigit(code) {\n  return code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102;\n}\nfunction decrypt(data, key, discardNumber) {\n  if (discardNumber >= data.length) {\n    return new Uint8Array(0);\n  }\n  const c1 = 52845,\n    c2 = 22719;\n  let r = key | 0,\n    i,\n    j;\n  for (i = 0; i < discardNumber; i++) {\n    r = (data[i] + r) * c1 + c2 & (1 << 16) - 1;\n  }\n  const count = data.length - discardNumber;\n  const decrypted = new Uint8Array(count);\n  for (i = discardNumber, j = 0; j < count; i++, j++) {\n    const value = data[i];\n    decrypted[j] = value ^ r >> 8;\n    r = (value + r) * c1 + c2 & (1 << 16) - 1;\n  }\n  return decrypted;\n}\nfunction decryptAscii(data, key, discardNumber) {\n  const c1 = 52845,\n    c2 = 22719;\n  let r = key | 0;\n  const count = data.length,\n    maybeLength = count >>> 1;\n  const decrypted = new Uint8Array(maybeLength);\n  let i, j;\n  for (i = 0, j = 0; i < count; i++) {\n    const digit1 = data[i];\n    if (!isHexDigit(digit1)) {\n      continue;\n    }\n    i++;\n    let digit2;\n    while (i < count && !isHexDigit(digit2 = data[i])) {\n      i++;\n    }\n    if (i < count) {\n      const value = parseInt(String.fromCharCode(digit1, digit2), 16);\n      decrypted[j++] = value ^ r >> 8;\n      r = (value + r) * c1 + c2 & (1 << 16) - 1;\n    }\n  }\n  return decrypted.slice(discardNumber, j);\n}\nfunction isSpecial(c) {\n  return c === 0x2f || c === 0x5b || c === 0x5d || c === 0x7b || c === 0x7d || c === 0x28 || c === 0x29;\n}\nclass Type1Parser {\n  constructor(stream, encrypted, seacAnalysisEnabled) {\n    if (encrypted) {\n      const data = stream.getBytes();\n      const isBinary = !((isHexDigit(data[0]) || isWhiteSpace(data[0])) && isHexDigit(data[1]) && isHexDigit(data[2]) && isHexDigit(data[3]) && isHexDigit(data[4]) && isHexDigit(data[5]) && isHexDigit(data[6]) && isHexDigit(data[7]));\n      stream = new Stream(isBinary ? decrypt(data, EEXEC_ENCRYPT_KEY, 4) : decryptAscii(data, EEXEC_ENCRYPT_KEY, 4));\n    }\n    this.seacAnalysisEnabled = !!seacAnalysisEnabled;\n    this.stream = stream;\n    this.nextChar();\n  }\n  readNumberArray() {\n    this.getToken();\n    const array = [];\n    while (true) {\n      const token = this.getToken();\n      if (token === null || token === \"]\" || token === \"}\") {\n        break;\n      }\n      array.push(parseFloat(token || 0));\n    }\n    return array;\n  }\n  readNumber() {\n    const token = this.getToken();\n    return parseFloat(token || 0);\n  }\n  readInt() {\n    const token = this.getToken();\n    return parseInt(token || 0, 10) | 0;\n  }\n  readBoolean() {\n    const token = this.getToken();\n    return token === \"true\" ? 1 : 0;\n  }\n  nextChar() {\n    return this.currentChar = this.stream.getByte();\n  }\n  prevChar() {\n    this.stream.skip(-2);\n    return this.currentChar = this.stream.getByte();\n  }\n  getToken() {\n    let comment = false;\n    let ch = this.currentChar;\n    while (true) {\n      if (ch === -1) {\n        return null;\n      }\n      if (comment) {\n        if (ch === 0x0a || ch === 0x0d) {\n          comment = false;\n        }\n      } else if (ch === 0x25) {\n        comment = true;\n      } else if (!isWhiteSpace(ch)) {\n        break;\n      }\n      ch = this.nextChar();\n    }\n    if (isSpecial(ch)) {\n      this.nextChar();\n      return String.fromCharCode(ch);\n    }\n    let token = \"\";\n    do {\n      token += String.fromCharCode(ch);\n      ch = this.nextChar();\n    } while (ch >= 0 && !isWhiteSpace(ch) && !isSpecial(ch));\n    return token;\n  }\n  readCharStrings(bytes, lenIV) {\n    if (lenIV === -1) {\n      return bytes;\n    }\n    return decrypt(bytes, CHAR_STRS_ENCRYPT_KEY, lenIV);\n  }\n  extractFontProgram(properties) {\n    const stream = this.stream;\n    const subrs = [],\n      charstrings = [];\n    const privateData = Object.create(null);\n    privateData.lenIV = 4;\n    const program = {\n      subrs: [],\n      charstrings: [],\n      properties: {\n        privateData\n      }\n    };\n    let token, length, data, lenIV;\n    while ((token = this.getToken()) !== null) {\n      if (token !== \"/\") {\n        continue;\n      }\n      token = this.getToken();\n      switch (token) {\n        case \"CharStrings\":\n          this.getToken();\n          this.getToken();\n          this.getToken();\n          this.getToken();\n          while (true) {\n            token = this.getToken();\n            if (token === null || token === \"end\") {\n              break;\n            }\n            if (token !== \"/\") {\n              continue;\n            }\n            const glyph = this.getToken();\n            length = this.readInt();\n            this.getToken();\n            data = length > 0 ? stream.getBytes(length) : new Uint8Array(0);\n            lenIV = program.properties.privateData.lenIV;\n            const encoded = this.readCharStrings(data, lenIV);\n            this.nextChar();\n            token = this.getToken();\n            if (token === \"noaccess\") {\n              this.getToken();\n            } else if (token === \"/\") {\n              this.prevChar();\n            }\n            charstrings.push({\n              glyph,\n              encoded\n            });\n          }\n          break;\n        case \"Subrs\":\n          this.readInt();\n          this.getToken();\n          while (this.getToken() === \"dup\") {\n            const index = this.readInt();\n            length = this.readInt();\n            this.getToken();\n            data = length > 0 ? stream.getBytes(length) : new Uint8Array(0);\n            lenIV = program.properties.privateData.lenIV;\n            const encoded = this.readCharStrings(data, lenIV);\n            this.nextChar();\n            token = this.getToken();\n            if (token === \"noaccess\") {\n              this.getToken();\n            }\n            subrs[index] = encoded;\n          }\n          break;\n        case \"BlueValues\":\n        case \"OtherBlues\":\n        case \"FamilyBlues\":\n        case \"FamilyOtherBlues\":\n          const blueArray = this.readNumberArray();\n          if (blueArray.length > 0 && blueArray.length % 2 === 0 && HINTING_ENABLED) {\n            program.properties.privateData[token] = blueArray;\n          }\n          break;\n        case \"StemSnapH\":\n        case \"StemSnapV\":\n          program.properties.privateData[token] = this.readNumberArray();\n          break;\n        case \"StdHW\":\n        case \"StdVW\":\n          program.properties.privateData[token] = this.readNumberArray()[0];\n          break;\n        case \"BlueShift\":\n        case \"lenIV\":\n        case \"BlueFuzz\":\n        case \"BlueScale\":\n        case \"LanguageGroup\":\n          program.properties.privateData[token] = this.readNumber();\n          break;\n        case \"ExpansionFactor\":\n          program.properties.privateData[token] = this.readNumber() || 0.06;\n          break;\n        case \"ForceBold\":\n          program.properties.privateData[token] = this.readBoolean();\n          break;\n      }\n    }\n    for (const {\n      encoded,\n      glyph\n    } of charstrings) {\n      const charString = new Type1CharString();\n      const error = charString.convert(encoded, subrs, this.seacAnalysisEnabled);\n      let output = charString.output;\n      if (error) {\n        output = [14];\n      }\n      const charStringObject = {\n        glyphName: glyph,\n        charstring: output,\n        width: charString.width,\n        lsb: charString.lsb,\n        seac: charString.seac\n      };\n      if (glyph === \".notdef\") {\n        program.charstrings.unshift(charStringObject);\n      } else {\n        program.charstrings.push(charStringObject);\n      }\n      if (properties.builtInEncoding) {\n        const index = properties.builtInEncoding.indexOf(glyph);\n        if (index > -1 && properties.widths[index] === undefined && index >= properties.firstChar && index <= properties.lastChar) {\n          properties.widths[index] = charString.width;\n        }\n      }\n    }\n    return program;\n  }\n  extractFontHeader(properties) {\n    let token;\n    while ((token = this.getToken()) !== null) {\n      if (token !== \"/\") {\n        continue;\n      }\n      token = this.getToken();\n      switch (token) {\n        case \"FontMatrix\":\n          const matrix = this.readNumberArray();\n          properties.fontMatrix = matrix;\n          break;\n        case \"Encoding\":\n          const encodingArg = this.getToken();\n          let encoding;\n          if (!/^\\d+$/.test(encodingArg)) {\n            encoding = getEncoding(encodingArg);\n          } else {\n            encoding = [];\n            const size = parseInt(encodingArg, 10) | 0;\n            this.getToken();\n            for (let j = 0; j < size; j++) {\n              token = this.getToken();\n              while (token !== \"dup\" && token !== \"def\") {\n                token = this.getToken();\n                if (token === null) {\n                  return;\n                }\n              }\n              if (token === \"def\") {\n                break;\n              }\n              const index = this.readInt();\n              this.getToken();\n              const glyph = this.getToken();\n              encoding[index] = glyph;\n              this.getToken();\n            }\n          }\n          properties.builtInEncoding = encoding;\n          break;\n        case \"FontBBox\":\n          const fontBBox = this.readNumberArray();\n          properties.ascent = Math.max(fontBBox[3], fontBBox[1]);\n          properties.descent = Math.min(fontBBox[1], fontBBox[3]);\n          properties.ascentScaled = true;\n          break;\n      }\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/type1_font.js\n\n\n\n\n\n\nfunction findBlock(streamBytes, signature, startIndex) {\n  const streamBytesLength = streamBytes.length;\n  const signatureLength = signature.length;\n  const scanLength = streamBytesLength - signatureLength;\n  let i = startIndex,\n    found = false;\n  while (i < scanLength) {\n    let j = 0;\n    while (j < signatureLength && streamBytes[i + j] === signature[j]) {\n      j++;\n    }\n    if (j >= signatureLength) {\n      i += j;\n      while (i < streamBytesLength && isWhiteSpace(streamBytes[i])) {\n        i++;\n      }\n      found = true;\n      break;\n    }\n    i++;\n  }\n  return {\n    found,\n    length: i\n  };\n}\nfunction getHeaderBlock(stream, suggestedLength) {\n  const EEXEC_SIGNATURE = [0x65, 0x65, 0x78, 0x65, 0x63];\n  const streamStartPos = stream.pos;\n  let headerBytes, headerBytesLength, block;\n  try {\n    headerBytes = stream.getBytes(suggestedLength);\n    headerBytesLength = headerBytes.length;\n  } catch {}\n  if (headerBytesLength === suggestedLength) {\n    block = findBlock(headerBytes, EEXEC_SIGNATURE, suggestedLength - 2 * EEXEC_SIGNATURE.length);\n    if (block.found && block.length === suggestedLength) {\n      return {\n        stream: new Stream(headerBytes),\n        length: suggestedLength\n      };\n    }\n  }\n  warn('Invalid \"Length1\" property in Type1 font -- trying to recover.');\n  stream.pos = streamStartPos;\n  const SCAN_BLOCK_LENGTH = 2048;\n  let actualLength;\n  while (true) {\n    const scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH);\n    block = findBlock(scanBytes, EEXEC_SIGNATURE, 0);\n    if (block.length === 0) {\n      break;\n    }\n    stream.pos += block.length;\n    if (block.found) {\n      actualLength = stream.pos - streamStartPos;\n      break;\n    }\n  }\n  stream.pos = streamStartPos;\n  if (actualLength) {\n    return {\n      stream: new Stream(stream.getBytes(actualLength)),\n      length: actualLength\n    };\n  }\n  warn('Unable to recover \"Length1\" property in Type1 font -- using as is.');\n  return {\n    stream: new Stream(stream.getBytes(suggestedLength)),\n    length: suggestedLength\n  };\n}\nfunction getEexecBlock(stream, suggestedLength) {\n  const eexecBytes = stream.getBytes();\n  if (eexecBytes.length === 0) {\n    throw new FormatError(\"getEexecBlock - no font program found.\");\n  }\n  return {\n    stream: new Stream(eexecBytes),\n    length: eexecBytes.length\n  };\n}\nclass Type1Font {\n  constructor(name, file, properties) {\n    const PFB_HEADER_SIZE = 6;\n    let headerBlockLength = properties.length1;\n    let eexecBlockLength = properties.length2;\n    let pfbHeader = file.peekBytes(PFB_HEADER_SIZE);\n    const pfbHeaderPresent = pfbHeader[0] === 0x80 && pfbHeader[1] === 0x01;\n    if (pfbHeaderPresent) {\n      file.skip(PFB_HEADER_SIZE);\n      headerBlockLength = pfbHeader[5] << 24 | pfbHeader[4] << 16 | pfbHeader[3] << 8 | pfbHeader[2];\n    }\n    const headerBlock = getHeaderBlock(file, headerBlockLength);\n    const headerBlockParser = new Type1Parser(headerBlock.stream, false, SEAC_ANALYSIS_ENABLED);\n    headerBlockParser.extractFontHeader(properties);\n    if (pfbHeaderPresent) {\n      pfbHeader = file.getBytes(PFB_HEADER_SIZE);\n      eexecBlockLength = pfbHeader[5] << 24 | pfbHeader[4] << 16 | pfbHeader[3] << 8 | pfbHeader[2];\n    }\n    const eexecBlock = getEexecBlock(file, eexecBlockLength);\n    const eexecBlockParser = new Type1Parser(eexecBlock.stream, true, SEAC_ANALYSIS_ENABLED);\n    const data = eexecBlockParser.extractFontProgram(properties);\n    for (const key in data.properties) {\n      properties[key] = data.properties[key];\n    }\n    const charstrings = data.charstrings;\n    const type2Charstrings = this.getType2Charstrings(charstrings);\n    const subrs = this.getType2Subrs(data.subrs);\n    this.charstrings = charstrings;\n    this.data = this.wrap(name, type2Charstrings, this.charstrings, subrs, properties);\n    this.seacs = this.getSeacs(data.charstrings);\n  }\n  get numGlyphs() {\n    return this.charstrings.length + 1;\n  }\n  getCharset() {\n    const charset = [\".notdef\"];\n    for (const {\n      glyphName\n    } of this.charstrings) {\n      charset.push(glyphName);\n    }\n    return charset;\n  }\n  getGlyphMapping(properties) {\n    const charstrings = this.charstrings;\n    if (properties.composite) {\n      const charCodeToGlyphId = Object.create(null);\n      for (let glyphId = 0, charstringsLen = charstrings.length; glyphId < charstringsLen; glyphId++) {\n        const charCode = properties.cMap.charCodeOf(glyphId);\n        charCodeToGlyphId[charCode] = glyphId + 1;\n      }\n      return charCodeToGlyphId;\n    }\n    const glyphNames = [\".notdef\"];\n    let builtInEncoding, glyphId;\n    for (glyphId = 0; glyphId < charstrings.length; glyphId++) {\n      glyphNames.push(charstrings[glyphId].glyphName);\n    }\n    const encoding = properties.builtInEncoding;\n    if (encoding) {\n      builtInEncoding = Object.create(null);\n      for (const charCode in encoding) {\n        glyphId = glyphNames.indexOf(encoding[charCode]);\n        if (glyphId >= 0) {\n          builtInEncoding[charCode] = glyphId;\n        }\n      }\n    }\n    return type1FontGlyphMapping(properties, builtInEncoding, glyphNames);\n  }\n  hasGlyphId(id) {\n    if (id < 0 || id >= this.numGlyphs) {\n      return false;\n    }\n    if (id === 0) {\n      return true;\n    }\n    const glyph = this.charstrings[id - 1];\n    return glyph.charstring.length > 0;\n  }\n  getSeacs(charstrings) {\n    const seacMap = [];\n    for (let i = 0, ii = charstrings.length; i < ii; i++) {\n      const charstring = charstrings[i];\n      if (charstring.seac) {\n        seacMap[i + 1] = charstring.seac;\n      }\n    }\n    return seacMap;\n  }\n  getType2Charstrings(type1Charstrings) {\n    const type2Charstrings = [];\n    for (const type1Charstring of type1Charstrings) {\n      type2Charstrings.push(type1Charstring.charstring);\n    }\n    return type2Charstrings;\n  }\n  getType2Subrs(type1Subrs) {\n    let bias = 0;\n    const count = type1Subrs.length;\n    if (count < 1133) {\n      bias = 107;\n    } else if (count < 33769) {\n      bias = 1131;\n    } else {\n      bias = 32768;\n    }\n    const type2Subrs = [];\n    let i;\n    for (i = 0; i < bias; i++) {\n      type2Subrs.push([0x0b]);\n    }\n    for (i = 0; i < count; i++) {\n      type2Subrs.push(type1Subrs[i]);\n    }\n    return type2Subrs;\n  }\n  wrap(name, glyphs, charstrings, subrs, properties) {\n    const cff = new CFF();\n    cff.header = new CFFHeader(1, 0, 4, 4);\n    cff.names = [name];\n    const topDict = new CFFTopDict();\n    topDict.setByName(\"version\", 391);\n    topDict.setByName(\"Notice\", 392);\n    topDict.setByName(\"FullName\", 393);\n    topDict.setByName(\"FamilyName\", 394);\n    topDict.setByName(\"Weight\", 395);\n    topDict.setByName(\"Encoding\", null);\n    topDict.setByName(\"FontMatrix\", properties.fontMatrix);\n    topDict.setByName(\"FontBBox\", properties.bbox);\n    topDict.setByName(\"charset\", null);\n    topDict.setByName(\"CharStrings\", null);\n    topDict.setByName(\"Private\", null);\n    cff.topDict = topDict;\n    const strings = new CFFStrings();\n    strings.add(\"Version 0.11\");\n    strings.add(\"See original notice\");\n    strings.add(name);\n    strings.add(name);\n    strings.add(\"Medium\");\n    cff.strings = strings;\n    cff.globalSubrIndex = new CFFIndex();\n    const count = glyphs.length;\n    const charsetArray = [\".notdef\"];\n    let i, ii;\n    for (i = 0; i < count; i++) {\n      const glyphName = charstrings[i].glyphName;\n      const index = CFFStandardStrings.indexOf(glyphName);\n      if (index === -1) {\n        strings.add(glyphName);\n      }\n      charsetArray.push(glyphName);\n    }\n    cff.charset = new CFFCharset(false, 0, charsetArray);\n    const charStringsIndex = new CFFIndex();\n    charStringsIndex.add([0x8b, 0x0e]);\n    for (i = 0; i < count; i++) {\n      charStringsIndex.add(glyphs[i]);\n    }\n    cff.charStrings = charStringsIndex;\n    const privateDict = new CFFPrivateDict();\n    privateDict.setByName(\"Subrs\", null);\n    const fields = [\"BlueValues\", \"OtherBlues\", \"FamilyBlues\", \"FamilyOtherBlues\", \"StemSnapH\", \"StemSnapV\", \"BlueShift\", \"BlueFuzz\", \"BlueScale\", \"LanguageGroup\", \"ExpansionFactor\", \"ForceBold\", \"StdHW\", \"StdVW\"];\n    for (i = 0, ii = fields.length; i < ii; i++) {\n      const field = fields[i];\n      if (!(field in properties.privateData)) {\n        continue;\n      }\n      const value = properties.privateData[field];\n      if (Array.isArray(value)) {\n        for (let j = value.length - 1; j > 0; j--) {\n          value[j] -= value[j - 1];\n        }\n      }\n      privateDict.setByName(field, value);\n    }\n    cff.topDict.privateDict = privateDict;\n    const subrIndex = new CFFIndex();\n    for (i = 0, ii = subrs.length; i < ii; i++) {\n      subrIndex.add(subrs[i]);\n    }\n    privateDict.subrsIndex = subrIndex;\n    const compiler = new CFFCompiler(cff);\n    return compiler.compile();\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/fonts.js\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nconst PRIVATE_USE_AREAS = [[0xe000, 0xf8ff], [0x100000, 0x10fffd]];\nconst PDF_GLYPH_SPACE_UNITS = 1000;\nconst EXPORT_DATA_PROPERTIES = [\"ascent\", \"bbox\", \"black\", \"bold\", \"charProcOperatorList\", \"composite\", \"cssFontInfo\", \"data\", \"defaultVMetrics\", \"defaultWidth\", \"descent\", \"fallbackName\", \"fontMatrix\", \"isInvalidPDFjsFont\", \"isType3Font\", \"italic\", \"loadedName\", \"mimetype\", \"missingFile\", \"name\", \"remeasure\", \"subtype\", \"systemFontInfo\", \"type\", \"vertical\"];\nconst EXPORT_DATA_EXTRA_PROPERTIES = [\"cMap\", \"defaultEncoding\", \"differences\", \"isMonospace\", \"isSerifFont\", \"isSymbolicFont\", \"seacMap\", \"toFontChar\", \"toUnicode\", \"vmetrics\", \"widths\"];\nfunction adjustWidths(properties) {\n  if (!properties.fontMatrix) {\n    return;\n  }\n  if (properties.fontMatrix[0] === FONT_IDENTITY_MATRIX[0]) {\n    return;\n  }\n  const scale = 0.001 / properties.fontMatrix[0];\n  const glyphsWidths = properties.widths;\n  for (const glyph in glyphsWidths) {\n    glyphsWidths[glyph] *= scale;\n  }\n  properties.defaultWidth *= scale;\n}\nfunction adjustTrueTypeToUnicode(properties, isSymbolicFont, nameRecords) {\n  if (properties.isInternalFont) {\n    return;\n  }\n  if (properties.hasIncludedToUnicodeMap) {\n    return;\n  }\n  if (properties.hasEncoding) {\n    return;\n  }\n  if (properties.toUnicode instanceof IdentityToUnicodeMap) {\n    return;\n  }\n  if (!isSymbolicFont) {\n    return;\n  }\n  if (nameRecords.length === 0) {\n    return;\n  }\n  if (properties.defaultEncoding === WinAnsiEncoding) {\n    return;\n  }\n  for (const r of nameRecords) {\n    if (!isWinNameRecord(r)) {\n      return;\n    }\n  }\n  const encoding = WinAnsiEncoding;\n  const toUnicode = [],\n    glyphsUnicodeMap = getGlyphsUnicode();\n  for (const charCode in encoding) {\n    const glyphName = encoding[charCode];\n    if (glyphName === \"\") {\n      continue;\n    }\n    const unicode = glyphsUnicodeMap[glyphName];\n    if (unicode === undefined) {\n      continue;\n    }\n    toUnicode[charCode] = String.fromCharCode(unicode);\n  }\n  if (toUnicode.length > 0) {\n    properties.toUnicode.amend(toUnicode);\n  }\n}\nfunction adjustType1ToUnicode(properties, builtInEncoding) {\n  if (properties.isInternalFont) {\n    return;\n  }\n  if (properties.hasIncludedToUnicodeMap) {\n    return;\n  }\n  if (builtInEncoding === properties.defaultEncoding) {\n    return;\n  }\n  if (properties.toUnicode instanceof IdentityToUnicodeMap) {\n    return;\n  }\n  const toUnicode = [],\n    glyphsUnicodeMap = getGlyphsUnicode();\n  for (const charCode in builtInEncoding) {\n    if (properties.hasEncoding) {\n      if (properties.baseEncodingName || properties.differences[charCode] !== undefined) {\n        continue;\n      }\n    }\n    const glyphName = builtInEncoding[charCode];\n    const unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap);\n    if (unicode !== -1) {\n      toUnicode[charCode] = String.fromCharCode(unicode);\n    }\n  }\n  if (toUnicode.length > 0) {\n    properties.toUnicode.amend(toUnicode);\n  }\n}\nfunction amendFallbackToUnicode(properties) {\n  if (!properties.fallbackToUnicode) {\n    return;\n  }\n  if (properties.toUnicode instanceof IdentityToUnicodeMap) {\n    return;\n  }\n  const toUnicode = [];\n  for (const charCode in properties.fallbackToUnicode) {\n    if (properties.toUnicode.has(charCode)) {\n      continue;\n    }\n    toUnicode[charCode] = properties.fallbackToUnicode[charCode];\n  }\n  if (toUnicode.length > 0) {\n    properties.toUnicode.amend(toUnicode);\n  }\n}\nclass fonts_Glyph {\n  constructor(originalCharCode, fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont) {\n    this.originalCharCode = originalCharCode;\n    this.fontChar = fontChar;\n    this.unicode = unicode;\n    this.accent = accent;\n    this.width = width;\n    this.vmetric = vmetric;\n    this.operatorListId = operatorListId;\n    this.isSpace = isSpace;\n    this.isInFont = isInFont;\n  }\n  get category() {\n    return shadow(this, \"category\", getCharUnicodeCategory(this.unicode), true);\n  }\n}\nfunction int16(b0, b1) {\n  return (b0 << 8) + b1;\n}\nfunction writeSignedInt16(bytes, index, value) {\n  bytes[index + 1] = value;\n  bytes[index] = value >>> 8;\n}\nfunction signedInt16(b0, b1) {\n  const value = (b0 << 8) + b1;\n  return value & 1 << 15 ? value - 0x10000 : value;\n}\nfunction writeUint32(bytes, index, value) {\n  bytes[index + 3] = value & 0xff;\n  bytes[index + 2] = value >>> 8;\n  bytes[index + 1] = value >>> 16;\n  bytes[index] = value >>> 24;\n}\nfunction int32(b0, b1, b2, b3) {\n  return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;\n}\nfunction string16(value) {\n  return String.fromCharCode(value >> 8 & 0xff, value & 0xff);\n}\nfunction safeString16(value) {\n  if (value > 0x7fff) {\n    value = 0x7fff;\n  } else if (value < -0x8000) {\n    value = -0x8000;\n  }\n  return String.fromCharCode(value >> 8 & 0xff, value & 0xff);\n}\nfunction isTrueTypeFile(file) {\n  const header = file.peekBytes(4);\n  return readUint32(header, 0) === 0x00010000 || bytesToString(header) === \"true\";\n}\nfunction isTrueTypeCollectionFile(file) {\n  const header = file.peekBytes(4);\n  return bytesToString(header) === \"ttcf\";\n}\nfunction isOpenTypeFile(file) {\n  const header = file.peekBytes(4);\n  return bytesToString(header) === \"OTTO\";\n}\nfunction isType1File(file) {\n  const header = file.peekBytes(2);\n  if (header[0] === 0x25 && header[1] === 0x21) {\n    return true;\n  }\n  if (header[0] === 0x80 && header[1] === 0x01) {\n    return true;\n  }\n  return false;\n}\nfunction isCFFFile(file) {\n  const header = file.peekBytes(4);\n  if (header[0] >= 1 && header[3] >= 1 && header[3] <= 4) {\n    return true;\n  }\n  return false;\n}\nfunction getFontFileType(file, {\n  type,\n  subtype,\n  composite\n}) {\n  let fileType, fileSubtype;\n  if (isTrueTypeFile(file) || isTrueTypeCollectionFile(file)) {\n    fileType = composite ? \"CIDFontType2\" : \"TrueType\";\n  } else if (isOpenTypeFile(file)) {\n    fileType = composite ? \"CIDFontType2\" : \"OpenType\";\n  } else if (isType1File(file)) {\n    if (composite) {\n      fileType = \"CIDFontType0\";\n    } else {\n      fileType = type === \"MMType1\" ? \"MMType1\" : \"Type1\";\n    }\n  } else if (isCFFFile(file)) {\n    if (composite) {\n      fileType = \"CIDFontType0\";\n      fileSubtype = \"CIDFontType0C\";\n    } else {\n      fileType = type === \"MMType1\" ? \"MMType1\" : \"Type1\";\n      fileSubtype = \"Type1C\";\n    }\n  } else {\n    warn(\"getFontFileType: Unable to detect correct font file Type/Subtype.\");\n    fileType = type;\n    fileSubtype = subtype;\n  }\n  return [fileType, fileSubtype];\n}\nfunction applyStandardFontGlyphMap(map, glyphMap) {\n  for (const charCode in glyphMap) {\n    map[+charCode] = glyphMap[charCode];\n  }\n}\nfunction buildToFontChar(encoding, glyphsUnicodeMap, differences) {\n  const toFontChar = [];\n  let unicode;\n  for (let i = 0, ii = encoding.length; i < ii; i++) {\n    unicode = getUnicodeForGlyph(encoding[i], glyphsUnicodeMap);\n    if (unicode !== -1) {\n      toFontChar[i] = unicode;\n    }\n  }\n  for (const charCode in differences) {\n    unicode = getUnicodeForGlyph(differences[charCode], glyphsUnicodeMap);\n    if (unicode !== -1) {\n      toFontChar[+charCode] = unicode;\n    }\n  }\n  return toFontChar;\n}\nfunction isMacNameRecord(r) {\n  return r.platform === 1 && r.encoding === 0 && r.language === 0;\n}\nfunction isWinNameRecord(r) {\n  return r.platform === 3 && r.encoding === 1 && r.language === 0x409;\n}\nfunction convertCidString(charCode, cid, shouldThrow = false) {\n  switch (cid.length) {\n    case 1:\n      return cid.charCodeAt(0);\n    case 2:\n      return cid.charCodeAt(0) << 8 | cid.charCodeAt(1);\n  }\n  const msg = `Unsupported CID string (charCode ${charCode}): \"${cid}\".`;\n  if (shouldThrow) {\n    throw new FormatError(msg);\n  }\n  warn(msg);\n  return cid;\n}\nfunction adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId, toUnicode) {\n  const newMap = Object.create(null);\n  const toUnicodeExtraMap = new Map();\n  const toFontChar = [];\n  const usedGlyphIds = new Set();\n  let privateUseAreaIndex = 0;\n  const privateUseOffetStart = PRIVATE_USE_AREAS[privateUseAreaIndex][0];\n  let nextAvailableFontCharCode = privateUseOffetStart;\n  let privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];\n  const isInPrivateArea = code => PRIVATE_USE_AREAS[0][0] <= code && code <= PRIVATE_USE_AREAS[0][1] || PRIVATE_USE_AREAS[1][0] <= code && code <= PRIVATE_USE_AREAS[1][1];\n  for (let originalCharCode in charCodeToGlyphId) {\n    originalCharCode |= 0;\n    let glyphId = charCodeToGlyphId[originalCharCode];\n    if (!hasGlyph(glyphId)) {\n      continue;\n    }\n    if (nextAvailableFontCharCode > privateUseOffetEnd) {\n      privateUseAreaIndex++;\n      if (privateUseAreaIndex >= PRIVATE_USE_AREAS.length) {\n        warn(\"Ran out of space in font private use area.\");\n        break;\n      }\n      nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];\n      privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];\n    }\n    const fontCharCode = nextAvailableFontCharCode++;\n    if (glyphId === 0) {\n      glyphId = newGlyphZeroId;\n    }\n    let unicode = toUnicode.get(originalCharCode);\n    if (typeof unicode === \"string\") {\n      unicode = unicode.codePointAt(0);\n    }\n    if (unicode && !isInPrivateArea(unicode) && !usedGlyphIds.has(glyphId)) {\n      toUnicodeExtraMap.set(unicode, glyphId);\n      usedGlyphIds.add(glyphId);\n    }\n    newMap[fontCharCode] = glyphId;\n    toFontChar[originalCharCode] = fontCharCode;\n  }\n  return {\n    toFontChar,\n    charCodeToGlyphId: newMap,\n    toUnicodeExtraMap,\n    nextAvailableFontCharCode\n  };\n}\nfunction getRanges(glyphs, toUnicodeExtraMap, numGlyphs) {\n  const codes = [];\n  for (const charCode in glyphs) {\n    if (glyphs[charCode] >= numGlyphs) {\n      continue;\n    }\n    codes.push({\n      fontCharCode: charCode | 0,\n      glyphId: glyphs[charCode]\n    });\n  }\n  if (toUnicodeExtraMap) {\n    for (const [unicode, glyphId] of toUnicodeExtraMap) {\n      if (glyphId >= numGlyphs) {\n        continue;\n      }\n      codes.push({\n        fontCharCode: unicode,\n        glyphId\n      });\n    }\n  }\n  if (codes.length === 0) {\n    codes.push({\n      fontCharCode: 0,\n      glyphId: 0\n    });\n  }\n  codes.sort(function fontGetRangesSort(a, b) {\n    return a.fontCharCode - b.fontCharCode;\n  });\n  const ranges = [];\n  const length = codes.length;\n  for (let n = 0; n < length;) {\n    const start = codes[n].fontCharCode;\n    const codeIndices = [codes[n].glyphId];\n    ++n;\n    let end = start;\n    while (n < length && end + 1 === codes[n].fontCharCode) {\n      codeIndices.push(codes[n].glyphId);\n      ++end;\n      ++n;\n      if (end === 0xffff) {\n        break;\n      }\n    }\n    ranges.push([start, end, codeIndices]);\n  }\n  return ranges;\n}\nfunction createCmapTable(glyphs, toUnicodeExtraMap, numGlyphs) {\n  const ranges = getRanges(glyphs, toUnicodeExtraMap, numGlyphs);\n  const numTables = ranges.at(-1)[1] > 0xffff ? 2 : 1;\n  let cmap = \"\\x00\\x00\" + string16(numTables) + \"\\x00\\x03\" + \"\\x00\\x01\" + string32(4 + numTables * 8);\n  let i, ii, j, jj;\n  for (i = ranges.length - 1; i >= 0; --i) {\n    if (ranges[i][0] <= 0xffff) {\n      break;\n    }\n  }\n  const bmpLength = i + 1;\n  if (ranges[i][0] < 0xffff && ranges[i][1] === 0xffff) {\n    ranges[i][1] = 0xfffe;\n  }\n  const trailingRangesCount = ranges[i][1] < 0xffff ? 1 : 0;\n  const segCount = bmpLength + trailingRangesCount;\n  const searchParams = OpenTypeFileBuilder.getSearchParams(segCount, 2);\n  let startCount = \"\";\n  let endCount = \"\";\n  let idDeltas = \"\";\n  let idRangeOffsets = \"\";\n  let glyphsIds = \"\";\n  let bias = 0;\n  let range, start, end, codes;\n  for (i = 0, ii = bmpLength; i < ii; i++) {\n    range = ranges[i];\n    start = range[0];\n    end = range[1];\n    startCount += string16(start);\n    endCount += string16(end);\n    codes = range[2];\n    let contiguous = true;\n    for (j = 1, jj = codes.length; j < jj; ++j) {\n      if (codes[j] !== codes[j - 1] + 1) {\n        contiguous = false;\n        break;\n      }\n    }\n    if (!contiguous) {\n      const offset = (segCount - i) * 2 + bias * 2;\n      bias += end - start + 1;\n      idDeltas += string16(0);\n      idRangeOffsets += string16(offset);\n      for (j = 0, jj = codes.length; j < jj; ++j) {\n        glyphsIds += string16(codes[j]);\n      }\n    } else {\n      const startCode = codes[0];\n      idDeltas += string16(startCode - start & 0xffff);\n      idRangeOffsets += string16(0);\n    }\n  }\n  if (trailingRangesCount > 0) {\n    endCount += \"\\xFF\\xFF\";\n    startCount += \"\\xFF\\xFF\";\n    idDeltas += \"\\x00\\x01\";\n    idRangeOffsets += \"\\x00\\x00\";\n  }\n  const format314 = \"\\x00\\x00\" + string16(2 * segCount) + string16(searchParams.range) + string16(searchParams.entry) + string16(searchParams.rangeShift) + endCount + \"\\x00\\x00\" + startCount + idDeltas + idRangeOffsets + glyphsIds;\n  let format31012 = \"\";\n  let header31012 = \"\";\n  if (numTables > 1) {\n    cmap += \"\\x00\\x03\" + \"\\x00\\x0A\" + string32(4 + numTables * 8 + 4 + format314.length);\n    format31012 = \"\";\n    for (i = 0, ii = ranges.length; i < ii; i++) {\n      range = ranges[i];\n      start = range[0];\n      codes = range[2];\n      let code = codes[0];\n      for (j = 1, jj = codes.length; j < jj; ++j) {\n        if (codes[j] !== codes[j - 1] + 1) {\n          end = range[0] + j - 1;\n          format31012 += string32(start) + string32(end) + string32(code);\n          start = end + 1;\n          code = codes[j];\n        }\n      }\n      format31012 += string32(start) + string32(range[1]) + string32(code);\n    }\n    header31012 = \"\\x00\\x0C\" + \"\\x00\\x00\" + string32(format31012.length + 16) + \"\\x00\\x00\\x00\\x00\" + string32(format31012.length / 12);\n  }\n  return cmap + \"\\x00\\x04\" + string16(format314.length + 4) + format314 + header31012 + format31012;\n}\nfunction validateOS2Table(os2, file) {\n  file.pos = (file.start || 0) + os2.offset;\n  const version = file.getUint16();\n  file.skip(60);\n  const selection = file.getUint16();\n  if (version < 4 && selection & 0x0300) {\n    return false;\n  }\n  const firstChar = file.getUint16();\n  const lastChar = file.getUint16();\n  if (firstChar > lastChar) {\n    return false;\n  }\n  file.skip(6);\n  const usWinAscent = file.getUint16();\n  if (usWinAscent === 0) {\n    return false;\n  }\n  os2.data[8] = os2.data[9] = 0;\n  return true;\n}\nfunction createOS2Table(properties, charstrings, override) {\n  override ||= {\n    unitsPerEm: 0,\n    yMax: 0,\n    yMin: 0,\n    ascent: 0,\n    descent: 0\n  };\n  let ulUnicodeRange1 = 0;\n  let ulUnicodeRange2 = 0;\n  let ulUnicodeRange3 = 0;\n  let ulUnicodeRange4 = 0;\n  let firstCharIndex = null;\n  let lastCharIndex = 0;\n  let position = -1;\n  if (charstrings) {\n    for (let code in charstrings) {\n      code |= 0;\n      if (firstCharIndex > code || !firstCharIndex) {\n        firstCharIndex = code;\n      }\n      if (lastCharIndex < code) {\n        lastCharIndex = code;\n      }\n      position = getUnicodeRangeFor(code, position);\n      if (position < 32) {\n        ulUnicodeRange1 |= 1 << position;\n      } else if (position < 64) {\n        ulUnicodeRange2 |= 1 << position - 32;\n      } else if (position < 96) {\n        ulUnicodeRange3 |= 1 << position - 64;\n      } else if (position < 123) {\n        ulUnicodeRange4 |= 1 << position - 96;\n      } else {\n        throw new FormatError(\"Unicode ranges Bits > 123 are reserved for internal usage\");\n      }\n    }\n    if (lastCharIndex > 0xffff) {\n      lastCharIndex = 0xffff;\n    }\n  } else {\n    firstCharIndex = 0;\n    lastCharIndex = 255;\n  }\n  const bbox = properties.bbox || [0, 0, 0, 0];\n  const unitsPerEm = override.unitsPerEm || 1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0];\n  const scale = properties.ascentScaled ? 1.0 : unitsPerEm / PDF_GLYPH_SPACE_UNITS;\n  const typoAscent = override.ascent || Math.round(scale * (properties.ascent || bbox[3]));\n  let typoDescent = override.descent || Math.round(scale * (properties.descent || bbox[1]));\n  if (typoDescent > 0 && properties.descent > 0 && bbox[1] < 0) {\n    typoDescent = -typoDescent;\n  }\n  const winAscent = override.yMax || typoAscent;\n  const winDescent = -override.yMin || -typoDescent;\n  return \"\\x00\\x03\" + \"\\x02\\x24\" + \"\\x01\\xF4\" + \"\\x00\\x05\" + \"\\x00\\x00\" + \"\\x02\\x8A\" + \"\\x02\\xBB\" + \"\\x00\\x00\" + \"\\x00\\x8C\" + \"\\x02\\x8A\" + \"\\x02\\xBB\" + \"\\x00\\x00\" + \"\\x01\\xDF\" + \"\\x00\\x31\" + \"\\x01\\x02\" + \"\\x00\\x00\" + \"\\x00\\x00\\x06\" + String.fromCharCode(properties.fixedPitch ? 0x09 : 0x00) + \"\\x00\\x00\\x00\\x00\\x00\\x00\" + string32(ulUnicodeRange1) + string32(ulUnicodeRange2) + string32(ulUnicodeRange3) + string32(ulUnicodeRange4) + \"\\x2A\\x32\\x31\\x2A\" + string16(properties.italicAngle ? 1 : 0) + string16(firstCharIndex || properties.firstChar) + string16(lastCharIndex || properties.lastChar) + string16(typoAscent) + string16(typoDescent) + \"\\x00\\x64\" + string16(winAscent) + string16(winDescent) + \"\\x00\\x00\\x00\\x00\" + \"\\x00\\x00\\x00\\x00\" + string16(properties.xHeight) + string16(properties.capHeight) + string16(0) + string16(firstCharIndex || properties.firstChar) + \"\\x00\\x03\";\n}\nfunction createPostTable(properties) {\n  const angle = Math.floor(properties.italicAngle * 2 ** 16);\n  return \"\\x00\\x03\\x00\\x00\" + string32(angle) + \"\\x00\\x00\" + \"\\x00\\x00\" + string32(properties.fixedPitch ? 1 : 0) + \"\\x00\\x00\\x00\\x00\" + \"\\x00\\x00\\x00\\x00\" + \"\\x00\\x00\\x00\\x00\" + \"\\x00\\x00\\x00\\x00\";\n}\nfunction createPostscriptName(name) {\n  return name.replaceAll(/[^\\x21-\\x7E]|[[\\](){}<>/%]/g, \"\").slice(0, 63);\n}\nfunction createNameTable(name, proto) {\n  if (!proto) {\n    proto = [[], []];\n  }\n  const strings = [proto[0][0] || \"Original licence\", proto[0][1] || name, proto[0][2] || \"Unknown\", proto[0][3] || \"uniqueID\", proto[0][4] || name, proto[0][5] || \"Version 0.11\", proto[0][6] || createPostscriptName(name), proto[0][7] || \"Unknown\", proto[0][8] || \"Unknown\", proto[0][9] || \"Unknown\"];\n  const stringsUnicode = [];\n  let i, ii, j, jj, str;\n  for (i = 0, ii = strings.length; i < ii; i++) {\n    str = proto[1][i] || strings[i];\n    const strBufUnicode = [];\n    for (j = 0, jj = str.length; j < jj; j++) {\n      strBufUnicode.push(string16(str.charCodeAt(j)));\n    }\n    stringsUnicode.push(strBufUnicode.join(\"\"));\n  }\n  const names = [strings, stringsUnicode];\n  const platforms = [\"\\x00\\x01\", \"\\x00\\x03\"];\n  const encodings = [\"\\x00\\x00\", \"\\x00\\x01\"];\n  const languages = [\"\\x00\\x00\", \"\\x04\\x09\"];\n  const namesRecordCount = strings.length * platforms.length;\n  let nameTable = \"\\x00\\x00\" + string16(namesRecordCount) + string16(namesRecordCount * 12 + 6);\n  let strOffset = 0;\n  for (i = 0, ii = platforms.length; i < ii; i++) {\n    const strs = names[i];\n    for (j = 0, jj = strs.length; j < jj; j++) {\n      str = strs[j];\n      const nameRecord = platforms[i] + encodings[i] + languages[i] + string16(j) + string16(str.length) + string16(strOffset);\n      nameTable += nameRecord;\n      strOffset += str.length;\n    }\n  }\n  nameTable += strings.join(\"\") + stringsUnicode.join(\"\");\n  return nameTable;\n}\nclass Font {\n  constructor(name, file, properties) {\n    this.name = name;\n    this.psName = null;\n    this.mimetype = null;\n    this.disableFontFace = false;\n    this.loadedName = properties.loadedName;\n    this.isType3Font = properties.isType3Font;\n    this.missingFile = false;\n    this.cssFontInfo = properties.cssFontInfo;\n    this._charsCache = Object.create(null);\n    this._glyphCache = Object.create(null);\n    let isSerifFont = !!(properties.flags & FontFlags.Serif);\n    if (!isSerifFont && !properties.isSimulatedFlags) {\n      const baseName = name.replaceAll(/[,_]/g, \"-\").split(\"-\")[0],\n        serifFonts = getSerifFonts();\n      for (const namePart of baseName.split(\"+\")) {\n        if (serifFonts[namePart]) {\n          isSerifFont = true;\n          break;\n        }\n      }\n    }\n    this.isSerifFont = isSerifFont;\n    this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);\n    this.isMonospace = !!(properties.flags & FontFlags.FixedPitch);\n    let {\n      type,\n      subtype\n    } = properties;\n    this.type = type;\n    this.subtype = subtype;\n    this.systemFontInfo = properties.systemFontInfo;\n    const matches = name.match(/^InvalidPDFjsFont_(.*)_\\d+$/);\n    this.isInvalidPDFjsFont = !!matches;\n    if (this.isInvalidPDFjsFont) {\n      this.fallbackName = matches[1];\n    } else if (this.isMonospace) {\n      this.fallbackName = \"monospace\";\n    } else if (this.isSerifFont) {\n      this.fallbackName = \"serif\";\n    } else {\n      this.fallbackName = \"sans-serif\";\n    }\n    if (this.systemFontInfo?.guessFallback) {\n      this.systemFontInfo.guessFallback = false;\n      this.systemFontInfo.css += `,${this.fallbackName}`;\n    }\n    this.differences = properties.differences;\n    this.widths = properties.widths;\n    this.defaultWidth = properties.defaultWidth;\n    this.composite = properties.composite;\n    this.cMap = properties.cMap;\n    this.capHeight = properties.capHeight / PDF_GLYPH_SPACE_UNITS;\n    this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;\n    this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;\n    this.lineHeight = this.ascent - this.descent;\n    this.fontMatrix = properties.fontMatrix;\n    this.bbox = properties.bbox;\n    this.defaultEncoding = properties.defaultEncoding;\n    this.toUnicode = properties.toUnicode;\n    this.toFontChar = [];\n    if (properties.type === \"Type3\") {\n      for (let charCode = 0; charCode < 256; charCode++) {\n        this.toFontChar[charCode] = this.differences[charCode] || properties.defaultEncoding[charCode];\n      }\n      return;\n    }\n    this.cidEncoding = properties.cidEncoding || \"\";\n    this.vertical = !!properties.vertical;\n    if (this.vertical) {\n      this.vmetrics = properties.vmetrics;\n      this.defaultVMetrics = properties.defaultVMetrics;\n    }\n    if (!file || file.isEmpty) {\n      if (file) {\n        warn('Font file is empty in \"' + name + '\" (' + this.loadedName + \")\");\n      }\n      this.fallbackToSystemFont(properties);\n      return;\n    }\n    [type, subtype] = getFontFileType(file, properties);\n    if (type !== this.type || subtype !== this.subtype) {\n      info(\"Inconsistent font file Type/SubType, expected: \" + `${this.type}/${this.subtype} but found: ${type}/${subtype}.`);\n    }\n    let data;\n    try {\n      switch (type) {\n        case \"MMType1\":\n          info(\"MMType1 font (\" + name + \"), falling back to Type1.\");\n        case \"Type1\":\n        case \"CIDFontType0\":\n          this.mimetype = \"font/opentype\";\n          const cff = subtype === \"Type1C\" || subtype === \"CIDFontType0C\" ? new CFFFont(file, properties) : new Type1Font(name, file, properties);\n          adjustWidths(properties);\n          data = this.convert(name, cff, properties);\n          break;\n        case \"OpenType\":\n        case \"TrueType\":\n        case \"CIDFontType2\":\n          this.mimetype = \"font/opentype\";\n          data = this.checkAndRepair(name, file, properties);\n          if (this.isOpenType) {\n            adjustWidths(properties);\n            type = \"OpenType\";\n          }\n          break;\n        default:\n          throw new FormatError(`Font ${type} is not supported`);\n      }\n    } catch (e) {\n      warn(e);\n      this.fallbackToSystemFont(properties);\n      return;\n    }\n    amendFallbackToUnicode(properties);\n    this.data = data;\n    this.type = type;\n    this.subtype = subtype;\n    this.fontMatrix = properties.fontMatrix;\n    this.widths = properties.widths;\n    this.defaultWidth = properties.defaultWidth;\n    this.toUnicode = properties.toUnicode;\n    this.seacMap = properties.seacMap;\n  }\n  get renderer() {\n    const renderer = FontRendererFactory.create(this, SEAC_ANALYSIS_ENABLED);\n    return shadow(this, \"renderer\", renderer);\n  }\n  exportData(extraProperties = false) {\n    const exportDataProperties = extraProperties ? [...EXPORT_DATA_PROPERTIES, ...EXPORT_DATA_EXTRA_PROPERTIES] : EXPORT_DATA_PROPERTIES;\n    const data = Object.create(null);\n    let property, value;\n    for (property of exportDataProperties) {\n      value = this[property];\n      if (value !== undefined) {\n        data[property] = value;\n      }\n    }\n    return data;\n  }\n  fallbackToSystemFont(properties) {\n    this.missingFile = true;\n    const {\n      name,\n      type\n    } = this;\n    let fontName = normalizeFontName(name);\n    const stdFontMap = getStdFontMap(),\n      nonStdFontMap = getNonStdFontMap();\n    const isStandardFont = !!stdFontMap[fontName];\n    const isMappedToStandardFont = !!(nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]);\n    fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;\n    const fontBasicMetricsMap = getFontBasicMetrics();\n    const metrics = fontBasicMetricsMap[fontName];\n    if (metrics) {\n      if (isNaN(this.ascent)) {\n        this.ascent = metrics.ascent / PDF_GLYPH_SPACE_UNITS;\n      }\n      if (isNaN(this.descent)) {\n        this.descent = metrics.descent / PDF_GLYPH_SPACE_UNITS;\n      }\n      if (isNaN(this.capHeight)) {\n        this.capHeight = metrics.capHeight / PDF_GLYPH_SPACE_UNITS;\n      }\n    }\n    this.bold = /bold/gi.test(fontName);\n    this.italic = /oblique|italic/gi.test(fontName);\n    this.black = /Black/g.test(name);\n    const isNarrow = /Narrow/g.test(name);\n    this.remeasure = (!isStandardFont || isNarrow) && Object.keys(this.widths).length > 0;\n    if ((isStandardFont || isMappedToStandardFont) && type === \"CIDFontType2\" && this.cidEncoding.startsWith(\"Identity-\")) {\n      const cidToGidMap = properties.cidToGidMap;\n      const map = [];\n      applyStandardFontGlyphMap(map, getGlyphMapForStandardFonts());\n      if (/Arial-?Black/i.test(name)) {\n        applyStandardFontGlyphMap(map, getSupplementalGlyphMapForArialBlack());\n      } else if (/Calibri/i.test(name)) {\n        applyStandardFontGlyphMap(map, getSupplementalGlyphMapForCalibri());\n      }\n      if (cidToGidMap) {\n        for (const charCode in map) {\n          const cid = map[charCode];\n          if (cidToGidMap[cid] !== undefined) {\n            map[+charCode] = cidToGidMap[cid];\n          }\n        }\n        if (cidToGidMap.length !== this.toUnicode.length && properties.hasIncludedToUnicodeMap && this.toUnicode instanceof IdentityToUnicodeMap) {\n          this.toUnicode.forEach(function (charCode, unicodeCharCode) {\n            const cid = map[charCode];\n            if (cidToGidMap[cid] === undefined) {\n              map[+charCode] = unicodeCharCode;\n            }\n          });\n        }\n      }\n      if (!(this.toUnicode instanceof IdentityToUnicodeMap)) {\n        this.toUnicode.forEach(function (charCode, unicodeCharCode) {\n          map[+charCode] = unicodeCharCode;\n        });\n      }\n      this.toFontChar = map;\n      this.toUnicode = new ToUnicodeMap(map);\n    } else if (/Symbol/i.test(fontName)) {\n      this.toFontChar = buildToFontChar(SymbolSetEncoding, getGlyphsUnicode(), this.differences);\n    } else if (/Dingbats/i.test(fontName)) {\n      this.toFontChar = buildToFontChar(ZapfDingbatsEncoding, getDingbatsGlyphsUnicode(), this.differences);\n    } else if (isStandardFont) {\n      const map = buildToFontChar(this.defaultEncoding, getGlyphsUnicode(), this.differences);\n      if (type === \"CIDFontType2\" && !this.cidEncoding.startsWith(\"Identity-\") && !(this.toUnicode instanceof IdentityToUnicodeMap)) {\n        this.toUnicode.forEach(function (charCode, unicodeCharCode) {\n          map[+charCode] = unicodeCharCode;\n        });\n      }\n      this.toFontChar = map;\n    } else {\n      const glyphsUnicodeMap = getGlyphsUnicode();\n      const map = [];\n      this.toUnicode.forEach((charCode, unicodeCharCode) => {\n        if (!this.composite) {\n          const glyphName = this.differences[charCode] || this.defaultEncoding[charCode];\n          const unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap);\n          if (unicode !== -1) {\n            unicodeCharCode = unicode;\n          }\n        }\n        map[+charCode] = unicodeCharCode;\n      });\n      if (this.composite && this.toUnicode instanceof IdentityToUnicodeMap) {\n        if (/Tahoma|Verdana/i.test(name)) {\n          applyStandardFontGlyphMap(map, getGlyphMapForStandardFonts());\n        }\n      }\n      this.toFontChar = map;\n    }\n    amendFallbackToUnicode(properties);\n    this.loadedName = fontName.split(\"-\")[0];\n  }\n  checkAndRepair(name, font, properties) {\n    const VALID_TABLES = [\"OS/2\", \"cmap\", \"head\", \"hhea\", \"hmtx\", \"maxp\", \"name\", \"post\", \"loca\", \"glyf\", \"fpgm\", \"prep\", \"cvt \", \"CFF \"];\n    function readTables(file, numTables) {\n      const tables = Object.create(null);\n      tables[\"OS/2\"] = null;\n      tables.cmap = null;\n      tables.head = null;\n      tables.hhea = null;\n      tables.hmtx = null;\n      tables.maxp = null;\n      tables.name = null;\n      tables.post = null;\n      for (let i = 0; i < numTables; i++) {\n        const table = readTableEntry(file);\n        if (!VALID_TABLES.includes(table.tag)) {\n          continue;\n        }\n        if (table.length === 0) {\n          continue;\n        }\n        tables[table.tag] = table;\n      }\n      return tables;\n    }\n    function readTableEntry(file) {\n      const tag = file.getString(4);\n      const checksum = file.getInt32() >>> 0;\n      const offset = file.getInt32() >>> 0;\n      const length = file.getInt32() >>> 0;\n      const previousPosition = file.pos;\n      file.pos = file.start || 0;\n      file.skip(offset);\n      const data = file.getBytes(length);\n      file.pos = previousPosition;\n      if (tag === \"head\") {\n        data[8] = data[9] = data[10] = data[11] = 0;\n        data[17] |= 0x20;\n      }\n      return {\n        tag,\n        checksum,\n        length,\n        offset,\n        data\n      };\n    }\n    function readOpenTypeHeader(ttf) {\n      return {\n        version: ttf.getString(4),\n        numTables: ttf.getUint16(),\n        searchRange: ttf.getUint16(),\n        entrySelector: ttf.getUint16(),\n        rangeShift: ttf.getUint16()\n      };\n    }\n    function readTrueTypeCollectionHeader(ttc) {\n      const ttcTag = ttc.getString(4);\n      assert(ttcTag === \"ttcf\", \"Must be a TrueType Collection font.\");\n      const majorVersion = ttc.getUint16();\n      const minorVersion = ttc.getUint16();\n      const numFonts = ttc.getInt32() >>> 0;\n      const offsetTable = [];\n      for (let i = 0; i < numFonts; i++) {\n        offsetTable.push(ttc.getInt32() >>> 0);\n      }\n      const header = {\n        ttcTag,\n        majorVersion,\n        minorVersion,\n        numFonts,\n        offsetTable\n      };\n      switch (majorVersion) {\n        case 1:\n          return header;\n        case 2:\n          header.dsigTag = ttc.getInt32() >>> 0;\n          header.dsigLength = ttc.getInt32() >>> 0;\n          header.dsigOffset = ttc.getInt32() >>> 0;\n          return header;\n      }\n      throw new FormatError(`Invalid TrueType Collection majorVersion: ${majorVersion}.`);\n    }\n    function readTrueTypeCollectionData(ttc, fontName) {\n      const {\n        numFonts,\n        offsetTable\n      } = readTrueTypeCollectionHeader(ttc);\n      const fontNameParts = fontName.split(\"+\");\n      let fallbackData;\n      for (let i = 0; i < numFonts; i++) {\n        ttc.pos = (ttc.start || 0) + offsetTable[i];\n        const potentialHeader = readOpenTypeHeader(ttc);\n        const potentialTables = readTables(ttc, potentialHeader.numTables);\n        if (!potentialTables.name) {\n          throw new FormatError('TrueType Collection font must contain a \"name\" table.');\n        }\n        const [nameTable] = readNameTable(potentialTables.name);\n        for (let j = 0, jj = nameTable.length; j < jj; j++) {\n          for (let k = 0, kk = nameTable[j].length; k < kk; k++) {\n            const nameEntry = nameTable[j][k]?.replaceAll(/\\s/g, \"\");\n            if (!nameEntry) {\n              continue;\n            }\n            if (nameEntry === fontName) {\n              return {\n                header: potentialHeader,\n                tables: potentialTables\n              };\n            }\n            if (fontNameParts.length < 2) {\n              continue;\n            }\n            for (const part of fontNameParts) {\n              if (nameEntry === part) {\n                fallbackData = {\n                  name: part,\n                  header: potentialHeader,\n                  tables: potentialTables\n                };\n              }\n            }\n          }\n        }\n      }\n      if (fallbackData) {\n        warn(`TrueType Collection does not contain \"${fontName}\" font, ` + `falling back to \"${fallbackData.name}\" font instead.`);\n        return {\n          header: fallbackData.header,\n          tables: fallbackData.tables\n        };\n      }\n      throw new FormatError(`TrueType Collection does not contain \"${fontName}\" font.`);\n    }\n    function readCmapTable(cmap, file, isSymbolicFont, hasEncoding) {\n      if (!cmap) {\n        warn(\"No cmap table available.\");\n        return {\n          platformId: -1,\n          encodingId: -1,\n          mappings: [],\n          hasShortCmap: false\n        };\n      }\n      let segment;\n      let start = (file.start || 0) + cmap.offset;\n      file.pos = start;\n      file.skip(2);\n      const numTables = file.getUint16();\n      let potentialTable;\n      let canBreak = false;\n      for (let i = 0; i < numTables; i++) {\n        const platformId = file.getUint16();\n        const encodingId = file.getUint16();\n        const offset = file.getInt32() >>> 0;\n        let useTable = false;\n        if (potentialTable?.platformId === platformId && potentialTable?.encodingId === encodingId) {\n          continue;\n        }\n        if (platformId === 0 && (encodingId === 0 || encodingId === 1 || encodingId === 3)) {\n          useTable = true;\n        } else if (platformId === 1 && encodingId === 0) {\n          useTable = true;\n        } else if (platformId === 3 && encodingId === 1 && (hasEncoding || !potentialTable)) {\n          useTable = true;\n          if (!isSymbolicFont) {\n            canBreak = true;\n          }\n        } else if (isSymbolicFont && platformId === 3 && encodingId === 0) {\n          useTable = true;\n          let correctlySorted = true;\n          if (i < numTables - 1) {\n            const nextBytes = file.peekBytes(2),\n              nextPlatformId = int16(nextBytes[0], nextBytes[1]);\n            if (nextPlatformId < platformId) {\n              correctlySorted = false;\n            }\n          }\n          if (correctlySorted) {\n            canBreak = true;\n          }\n        }\n        if (useTable) {\n          potentialTable = {\n            platformId,\n            encodingId,\n            offset\n          };\n        }\n        if (canBreak) {\n          break;\n        }\n      }\n      if (potentialTable) {\n        file.pos = start + potentialTable.offset;\n      }\n      if (!potentialTable || file.peekByte() === -1) {\n        warn(\"Could not find a preferred cmap table.\");\n        return {\n          platformId: -1,\n          encodingId: -1,\n          mappings: [],\n          hasShortCmap: false\n        };\n      }\n      const format = file.getUint16();\n      let hasShortCmap = false;\n      const mappings = [];\n      let j, glyphId;\n      if (format === 0) {\n        file.skip(2 + 2);\n        for (j = 0; j < 256; j++) {\n          const index = file.getByte();\n          if (!index) {\n            continue;\n          }\n          mappings.push({\n            charCode: j,\n            glyphId: index\n          });\n        }\n        hasShortCmap = true;\n      } else if (format === 2) {\n        file.skip(2 + 2);\n        const subHeaderKeys = [];\n        let maxSubHeaderKey = 0;\n        for (let i = 0; i < 256; i++) {\n          const subHeaderKey = file.getUint16() >> 3;\n          subHeaderKeys.push(subHeaderKey);\n          maxSubHeaderKey = Math.max(subHeaderKey, maxSubHeaderKey);\n        }\n        const subHeaders = [];\n        for (let i = 0; i <= maxSubHeaderKey; i++) {\n          subHeaders.push({\n            firstCode: file.getUint16(),\n            entryCount: file.getUint16(),\n            idDelta: signedInt16(file.getByte(), file.getByte()),\n            idRangePos: file.pos + file.getUint16()\n          });\n        }\n        for (let i = 0; i < 256; i++) {\n          if (subHeaderKeys[i] === 0) {\n            file.pos = subHeaders[0].idRangePos + 2 * i;\n            glyphId = file.getUint16();\n            mappings.push({\n              charCode: i,\n              glyphId\n            });\n          } else {\n            const s = subHeaders[subHeaderKeys[i]];\n            for (j = 0; j < s.entryCount; j++) {\n              const charCode = (i << 8) + j + s.firstCode;\n              file.pos = s.idRangePos + 2 * j;\n              glyphId = file.getUint16();\n              if (glyphId !== 0) {\n                glyphId = (glyphId + s.idDelta) % 65536;\n              }\n              mappings.push({\n                charCode,\n                glyphId\n              });\n            }\n          }\n        }\n      } else if (format === 4) {\n        file.skip(2 + 2);\n        const segCount = file.getUint16() >> 1;\n        file.skip(6);\n        const segments = [];\n        let segIndex;\n        for (segIndex = 0; segIndex < segCount; segIndex++) {\n          segments.push({\n            end: file.getUint16()\n          });\n        }\n        file.skip(2);\n        for (segIndex = 0; segIndex < segCount; segIndex++) {\n          segments[segIndex].start = file.getUint16();\n        }\n        for (segIndex = 0; segIndex < segCount; segIndex++) {\n          segments[segIndex].delta = file.getUint16();\n        }\n        let offsetsCount = 0,\n          offsetIndex;\n        for (segIndex = 0; segIndex < segCount; segIndex++) {\n          segment = segments[segIndex];\n          const rangeOffset = file.getUint16();\n          if (!rangeOffset) {\n            segment.offsetIndex = -1;\n            continue;\n          }\n          offsetIndex = (rangeOffset >> 1) - (segCount - segIndex);\n          segment.offsetIndex = offsetIndex;\n          offsetsCount = Math.max(offsetsCount, offsetIndex + segment.end - segment.start + 1);\n        }\n        const offsets = [];\n        for (j = 0; j < offsetsCount; j++) {\n          offsets.push(file.getUint16());\n        }\n        for (segIndex = 0; segIndex < segCount; segIndex++) {\n          segment = segments[segIndex];\n          start = segment.start;\n          const end = segment.end;\n          const delta = segment.delta;\n          offsetIndex = segment.offsetIndex;\n          for (j = start; j <= end; j++) {\n            if (j === 0xffff) {\n              continue;\n            }\n            glyphId = offsetIndex < 0 ? j : offsets[offsetIndex + j - start];\n            glyphId = glyphId + delta & 0xffff;\n            mappings.push({\n              charCode: j,\n              glyphId\n            });\n          }\n        }\n      } else if (format === 6) {\n        file.skip(2 + 2);\n        const firstCode = file.getUint16();\n        const entryCount = file.getUint16();\n        for (j = 0; j < entryCount; j++) {\n          glyphId = file.getUint16();\n          const charCode = firstCode + j;\n          mappings.push({\n            charCode,\n            glyphId\n          });\n        }\n      } else if (format === 12) {\n        file.skip(2 + 4 + 4);\n        const nGroups = file.getInt32() >>> 0;\n        for (j = 0; j < nGroups; j++) {\n          const startCharCode = file.getInt32() >>> 0;\n          const endCharCode = file.getInt32() >>> 0;\n          let glyphCode = file.getInt32() >>> 0;\n          for (let charCode = startCharCode; charCode <= endCharCode; charCode++) {\n            mappings.push({\n              charCode,\n              glyphId: glyphCode++\n            });\n          }\n        }\n      } else {\n        warn(\"cmap table has unsupported format: \" + format);\n        return {\n          platformId: -1,\n          encodingId: -1,\n          mappings: [],\n          hasShortCmap: false\n        };\n      }\n      mappings.sort(function (a, b) {\n        return a.charCode - b.charCode;\n      });\n      for (let i = 1; i < mappings.length; i++) {\n        if (mappings[i - 1].charCode === mappings[i].charCode) {\n          mappings.splice(i, 1);\n          i--;\n        }\n      }\n      return {\n        platformId: potentialTable.platformId,\n        encodingId: potentialTable.encodingId,\n        mappings,\n        hasShortCmap\n      };\n    }\n    function sanitizeMetrics(file, header, metrics, headTable, numGlyphs, dupFirstEntry) {\n      if (!header) {\n        if (metrics) {\n          metrics.data = null;\n        }\n        return;\n      }\n      file.pos = (file.start || 0) + header.offset;\n      file.pos += 4;\n      file.pos += 2;\n      file.pos += 2;\n      file.pos += 2;\n      file.pos += 2;\n      file.pos += 2;\n      file.pos += 2;\n      file.pos += 2;\n      file.pos += 2;\n      file.pos += 2;\n      const caretOffset = file.getUint16();\n      file.pos += 8;\n      file.pos += 2;\n      let numOfMetrics = file.getUint16();\n      if (caretOffset !== 0) {\n        const macStyle = int16(headTable.data[44], headTable.data[45]);\n        if (!(macStyle & 2)) {\n          header.data[22] = 0;\n          header.data[23] = 0;\n        }\n      }\n      if (numOfMetrics > numGlyphs) {\n        info(`The numOfMetrics (${numOfMetrics}) should not be ` + `greater than the numGlyphs (${numGlyphs}).`);\n        numOfMetrics = numGlyphs;\n        header.data[34] = (numOfMetrics & 0xff00) >> 8;\n        header.data[35] = numOfMetrics & 0x00ff;\n      }\n      const numOfSidebearings = numGlyphs - numOfMetrics;\n      const numMissing = numOfSidebearings - (metrics.length - numOfMetrics * 4 >> 1);\n      if (numMissing > 0) {\n        const entries = new Uint8Array(metrics.length + numMissing * 2);\n        entries.set(metrics.data);\n        if (dupFirstEntry) {\n          entries[metrics.length] = metrics.data[2];\n          entries[metrics.length + 1] = metrics.data[3];\n        }\n        metrics.data = entries;\n      }\n    }\n    function sanitizeGlyph(source, sourceStart, sourceEnd, dest, destStart, hintsValid) {\n      const glyphProfile = {\n        length: 0,\n        sizeOfInstructions: 0\n      };\n      if (sourceStart < 0 || sourceStart >= source.length || sourceEnd > source.length || sourceEnd - sourceStart <= 12) {\n        return glyphProfile;\n      }\n      const glyf = source.subarray(sourceStart, sourceEnd);\n      const xMin = signedInt16(glyf[2], glyf[3]);\n      const yMin = signedInt16(glyf[4], glyf[5]);\n      const xMax = signedInt16(glyf[6], glyf[7]);\n      const yMax = signedInt16(glyf[8], glyf[9]);\n      if (xMin > xMax) {\n        writeSignedInt16(glyf, 2, xMax);\n        writeSignedInt16(glyf, 6, xMin);\n      }\n      if (yMin > yMax) {\n        writeSignedInt16(glyf, 4, yMax);\n        writeSignedInt16(glyf, 8, yMin);\n      }\n      const contoursCount = signedInt16(glyf[0], glyf[1]);\n      if (contoursCount < 0) {\n        if (contoursCount < -1) {\n          return glyphProfile;\n        }\n        dest.set(glyf, destStart);\n        glyphProfile.length = glyf.length;\n        return glyphProfile;\n      }\n      let i,\n        j = 10,\n        flagsCount = 0;\n      for (i = 0; i < contoursCount; i++) {\n        const endPoint = glyf[j] << 8 | glyf[j + 1];\n        flagsCount = endPoint + 1;\n        j += 2;\n      }\n      const instructionsStart = j;\n      const instructionsLength = glyf[j] << 8 | glyf[j + 1];\n      glyphProfile.sizeOfInstructions = instructionsLength;\n      j += 2 + instructionsLength;\n      const instructionsEnd = j;\n      let coordinatesLength = 0;\n      for (i = 0; i < flagsCount; i++) {\n        const flag = glyf[j++];\n        if (flag & 0xc0) {\n          glyf[j - 1] = flag & 0x3f;\n        }\n        let xLength = 2;\n        if (flag & 2) {\n          xLength = 1;\n        } else if (flag & 16) {\n          xLength = 0;\n        }\n        let yLength = 2;\n        if (flag & 4) {\n          yLength = 1;\n        } else if (flag & 32) {\n          yLength = 0;\n        }\n        const xyLength = xLength + yLength;\n        coordinatesLength += xyLength;\n        if (flag & 8) {\n          const repeat = glyf[j++];\n          if (repeat === 0) {\n            glyf[j - 1] ^= 8;\n          }\n          i += repeat;\n          coordinatesLength += repeat * xyLength;\n        }\n      }\n      if (coordinatesLength === 0) {\n        return glyphProfile;\n      }\n      let glyphDataLength = j + coordinatesLength;\n      if (glyphDataLength > glyf.length) {\n        return glyphProfile;\n      }\n      if (!hintsValid && instructionsLength > 0) {\n        dest.set(glyf.subarray(0, instructionsStart), destStart);\n        dest.set([0, 0], destStart + instructionsStart);\n        dest.set(glyf.subarray(instructionsEnd, glyphDataLength), destStart + instructionsStart + 2);\n        glyphDataLength -= instructionsLength;\n        if (glyf.length - glyphDataLength > 3) {\n          glyphDataLength = glyphDataLength + 3 & ~3;\n        }\n        glyphProfile.length = glyphDataLength;\n        return glyphProfile;\n      }\n      if (glyf.length - glyphDataLength > 3) {\n        glyphDataLength = glyphDataLength + 3 & ~3;\n        dest.set(glyf.subarray(0, glyphDataLength), destStart);\n        glyphProfile.length = glyphDataLength;\n        return glyphProfile;\n      }\n      dest.set(glyf, destStart);\n      glyphProfile.length = glyf.length;\n      return glyphProfile;\n    }\n    function sanitizeHead(head, numGlyphs, locaLength) {\n      const data = head.data;\n      const version = int32(data[0], data[1], data[2], data[3]);\n      if (version >> 16 !== 1) {\n        info(\"Attempting to fix invalid version in head table: \" + version);\n        data[0] = 0;\n        data[1] = 1;\n        data[2] = 0;\n        data[3] = 0;\n      }\n      const indexToLocFormat = int16(data[50], data[51]);\n      if (indexToLocFormat < 0 || indexToLocFormat > 1) {\n        info(\"Attempting to fix invalid indexToLocFormat in head table: \" + indexToLocFormat);\n        const numGlyphsPlusOne = numGlyphs + 1;\n        if (locaLength === numGlyphsPlusOne << 1) {\n          data[50] = 0;\n          data[51] = 0;\n        } else if (locaLength === numGlyphsPlusOne << 2) {\n          data[50] = 0;\n          data[51] = 1;\n        } else {\n          throw new FormatError(\"Could not fix indexToLocFormat: \" + indexToLocFormat);\n        }\n      }\n    }\n    function sanitizeGlyphLocations(loca, glyf, numGlyphs, isGlyphLocationsLong, hintsValid, dupFirstEntry, maxSizeOfInstructions) {\n      let itemSize, itemDecode, itemEncode;\n      if (isGlyphLocationsLong) {\n        itemSize = 4;\n        itemDecode = function fontItemDecodeLong(data, offset) {\n          return data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3];\n        };\n        itemEncode = function fontItemEncodeLong(data, offset, value) {\n          data[offset] = value >>> 24 & 0xff;\n          data[offset + 1] = value >> 16 & 0xff;\n          data[offset + 2] = value >> 8 & 0xff;\n          data[offset + 3] = value & 0xff;\n        };\n      } else {\n        itemSize = 2;\n        itemDecode = function fontItemDecode(data, offset) {\n          return data[offset] << 9 | data[offset + 1] << 1;\n        };\n        itemEncode = function fontItemEncode(data, offset, value) {\n          data[offset] = value >> 9 & 0xff;\n          data[offset + 1] = value >> 1 & 0xff;\n        };\n      }\n      const numGlyphsOut = dupFirstEntry ? numGlyphs + 1 : numGlyphs;\n      const locaDataSize = itemSize * (1 + numGlyphsOut);\n      const locaData = new Uint8Array(locaDataSize);\n      locaData.set(loca.data.subarray(0, locaDataSize));\n      loca.data = locaData;\n      const oldGlyfData = glyf.data;\n      const oldGlyfDataLength = oldGlyfData.length;\n      const newGlyfData = new Uint8Array(oldGlyfDataLength);\n      let i, j;\n      const locaEntries = [];\n      for (i = 0, j = 0; i < numGlyphs + 1; i++, j += itemSize) {\n        let offset = itemDecode(locaData, j);\n        if (offset > oldGlyfDataLength) {\n          offset = oldGlyfDataLength;\n        }\n        locaEntries.push({\n          index: i,\n          offset,\n          endOffset: 0\n        });\n      }\n      locaEntries.sort((a, b) => {\n        return a.offset - b.offset;\n      });\n      for (i = 0; i < numGlyphs; i++) {\n        locaEntries[i].endOffset = locaEntries[i + 1].offset;\n      }\n      locaEntries.sort((a, b) => {\n        return a.index - b.index;\n      });\n      for (i = 0; i < numGlyphs; i++) {\n        const {\n          offset,\n          endOffset\n        } = locaEntries[i];\n        if (offset !== 0 || endOffset !== 0) {\n          break;\n        }\n        const nextOffset = locaEntries[i + 1].offset;\n        if (nextOffset === 0) {\n          continue;\n        }\n        locaEntries[i].endOffset = nextOffset;\n        break;\n      }\n      const missingGlyphs = Object.create(null);\n      let writeOffset = 0;\n      itemEncode(locaData, 0, writeOffset);\n      for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {\n        const glyphProfile = sanitizeGlyph(oldGlyfData, locaEntries[i].offset, locaEntries[i].endOffset, newGlyfData, writeOffset, hintsValid);\n        const newLength = glyphProfile.length;\n        if (newLength === 0) {\n          missingGlyphs[i] = true;\n        }\n        if (glyphProfile.sizeOfInstructions > maxSizeOfInstructions) {\n          maxSizeOfInstructions = glyphProfile.sizeOfInstructions;\n        }\n        writeOffset += newLength;\n        itemEncode(locaData, j, writeOffset);\n      }\n      if (writeOffset === 0) {\n        const simpleGlyph = new Uint8Array([0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0]);\n        for (i = 0, j = itemSize; i < numGlyphsOut; i++, j += itemSize) {\n          itemEncode(locaData, j, simpleGlyph.length);\n        }\n        glyf.data = simpleGlyph;\n      } else if (dupFirstEntry) {\n        const firstEntryLength = itemDecode(locaData, itemSize);\n        if (newGlyfData.length > firstEntryLength + writeOffset) {\n          glyf.data = newGlyfData.subarray(0, firstEntryLength + writeOffset);\n        } else {\n          glyf.data = new Uint8Array(firstEntryLength + writeOffset);\n          glyf.data.set(newGlyfData.subarray(0, writeOffset));\n        }\n        glyf.data.set(newGlyfData.subarray(0, firstEntryLength), writeOffset);\n        itemEncode(loca.data, locaData.length - itemSize, writeOffset + firstEntryLength);\n      } else {\n        glyf.data = newGlyfData.subarray(0, writeOffset);\n      }\n      return {\n        missingGlyphs,\n        maxSizeOfInstructions\n      };\n    }\n    function readPostScriptTable(post, propertiesObj, maxpNumGlyphs) {\n      const start = (font.start || 0) + post.offset;\n      font.pos = start;\n      const length = post.length,\n        end = start + length;\n      const version = font.getInt32();\n      font.skip(28);\n      let glyphNames;\n      let valid = true;\n      let i;\n      switch (version) {\n        case 0x00010000:\n          glyphNames = MacStandardGlyphOrdering;\n          break;\n        case 0x00020000:\n          const numGlyphs = font.getUint16();\n          if (numGlyphs !== maxpNumGlyphs) {\n            valid = false;\n            break;\n          }\n          const glyphNameIndexes = [];\n          for (i = 0; i < numGlyphs; ++i) {\n            const index = font.getUint16();\n            if (index >= 32768) {\n              valid = false;\n              break;\n            }\n            glyphNameIndexes.push(index);\n          }\n          if (!valid) {\n            break;\n          }\n          const customNames = [],\n            strBuf = [];\n          while (font.pos < end) {\n            const stringLength = font.getByte();\n            strBuf.length = stringLength;\n            for (i = 0; i < stringLength; ++i) {\n              strBuf[i] = String.fromCharCode(font.getByte());\n            }\n            customNames.push(strBuf.join(\"\"));\n          }\n          glyphNames = [];\n          for (i = 0; i < numGlyphs; ++i) {\n            const j = glyphNameIndexes[i];\n            if (j < 258) {\n              glyphNames.push(MacStandardGlyphOrdering[j]);\n              continue;\n            }\n            glyphNames.push(customNames[j - 258]);\n          }\n          break;\n        case 0x00030000:\n          break;\n        default:\n          warn(\"Unknown/unsupported post table version \" + version);\n          valid = false;\n          if (propertiesObj.defaultEncoding) {\n            glyphNames = propertiesObj.defaultEncoding;\n          }\n          break;\n      }\n      propertiesObj.glyphNames = glyphNames;\n      return valid;\n    }\n    function readNameTable(nameTable) {\n      const start = (font.start || 0) + nameTable.offset;\n      font.pos = start;\n      const names = [[], []],\n        records = [];\n      const length = nameTable.length,\n        end = start + length;\n      const format = font.getUint16();\n      const FORMAT_0_HEADER_LENGTH = 6;\n      if (format !== 0 || length < FORMAT_0_HEADER_LENGTH) {\n        return [names, records];\n      }\n      const numRecords = font.getUint16();\n      const stringsStart = font.getUint16();\n      const NAME_RECORD_LENGTH = 12;\n      let i, ii;\n      for (i = 0; i < numRecords && font.pos + NAME_RECORD_LENGTH <= end; i++) {\n        const r = {\n          platform: font.getUint16(),\n          encoding: font.getUint16(),\n          language: font.getUint16(),\n          name: font.getUint16(),\n          length: font.getUint16(),\n          offset: font.getUint16()\n        };\n        if (isMacNameRecord(r) || isWinNameRecord(r)) {\n          records.push(r);\n        }\n      }\n      for (i = 0, ii = records.length; i < ii; i++) {\n        const record = records[i];\n        if (record.length <= 0) {\n          continue;\n        }\n        const pos = start + stringsStart + record.offset;\n        if (pos + record.length > end) {\n          continue;\n        }\n        font.pos = pos;\n        const nameIndex = record.name;\n        if (record.encoding) {\n          let str = \"\";\n          for (let j = 0, jj = record.length; j < jj; j += 2) {\n            str += String.fromCharCode(font.getUint16());\n          }\n          names[1][nameIndex] = str;\n        } else {\n          names[0][nameIndex] = font.getString(record.length);\n        }\n      }\n      return [names, records];\n    }\n    const TTOpsStackDeltas = [0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, -1, -1, 1, -1, -999, 0, 1, 0, -1, -2, 0, -1, -2, -1, -1, 0, -1, -1, 0, 0, -999, -999, -1, -1, -1, -1, -2, -999, -2, -2, -999, 0, -2, -2, 0, 0, -2, 0, -2, 0, 0, 0, -2, -1, -1, 1, 1, 0, 0, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, 0, -999, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -999, -999, -999, -999, -999, -1, -1, -2, -2, 0, 0, 0, 0, -1, -1, -999, -2, -2, 0, 0, -1, -2, -2, 0, 0, 0, -1, -1, -1, -2];\n    function sanitizeTTProgram(table, ttContext) {\n      let data = table.data;\n      let i = 0,\n        j,\n        n,\n        b,\n        funcId,\n        pc,\n        lastEndf = 0,\n        lastDeff = 0;\n      const stack = [];\n      const callstack = [];\n      const functionsCalled = [];\n      let tooComplexToFollowFunctions = ttContext.tooComplexToFollowFunctions;\n      let inFDEF = false,\n        ifLevel = 0,\n        inELSE = 0;\n      for (let ii = data.length; i < ii;) {\n        const op = data[i++];\n        if (op === 0x40) {\n          n = data[i++];\n          if (inFDEF || inELSE) {\n            i += n;\n          } else {\n            for (j = 0; j < n; j++) {\n              stack.push(data[i++]);\n            }\n          }\n        } else if (op === 0x41) {\n          n = data[i++];\n          if (inFDEF || inELSE) {\n            i += n * 2;\n          } else {\n            for (j = 0; j < n; j++) {\n              b = data[i++];\n              stack.push(b << 8 | data[i++]);\n            }\n          }\n        } else if ((op & 0xf8) === 0xb0) {\n          n = op - 0xb0 + 1;\n          if (inFDEF || inELSE) {\n            i += n;\n          } else {\n            for (j = 0; j < n; j++) {\n              stack.push(data[i++]);\n            }\n          }\n        } else if ((op & 0xf8) === 0xb8) {\n          n = op - 0xb8 + 1;\n          if (inFDEF || inELSE) {\n            i += n * 2;\n          } else {\n            for (j = 0; j < n; j++) {\n              b = data[i++];\n              stack.push(b << 8 | data[i++]);\n            }\n          }\n        } else if (op === 0x2b && !tooComplexToFollowFunctions) {\n          if (!inFDEF && !inELSE) {\n            funcId = stack.at(-1);\n            if (isNaN(funcId)) {\n              info(\"TT: CALL empty stack (or invalid entry).\");\n            } else {\n              ttContext.functionsUsed[funcId] = true;\n              if (funcId in ttContext.functionsStackDeltas) {\n                const newStackLength = stack.length + ttContext.functionsStackDeltas[funcId];\n                if (newStackLength < 0) {\n                  warn(\"TT: CALL invalid functions stack delta.\");\n                  ttContext.hintsValid = false;\n                  return;\n                }\n                stack.length = newStackLength;\n              } else if (funcId in ttContext.functionsDefined && !functionsCalled.includes(funcId)) {\n                callstack.push({\n                  data,\n                  i,\n                  stackTop: stack.length - 1\n                });\n                functionsCalled.push(funcId);\n                pc = ttContext.functionsDefined[funcId];\n                if (!pc) {\n                  warn(\"TT: CALL non-existent function\");\n                  ttContext.hintsValid = false;\n                  return;\n                }\n                data = pc.data;\n                i = pc.i;\n              }\n            }\n          }\n        } else if (op === 0x2c && !tooComplexToFollowFunctions) {\n          if (inFDEF || inELSE) {\n            warn(\"TT: nested FDEFs not allowed\");\n            tooComplexToFollowFunctions = true;\n          }\n          inFDEF = true;\n          lastDeff = i;\n          funcId = stack.pop();\n          ttContext.functionsDefined[funcId] = {\n            data,\n            i\n          };\n        } else if (op === 0x2d) {\n          if (inFDEF) {\n            inFDEF = false;\n            lastEndf = i;\n          } else {\n            pc = callstack.pop();\n            if (!pc) {\n              warn(\"TT: ENDF bad stack\");\n              ttContext.hintsValid = false;\n              return;\n            }\n            funcId = functionsCalled.pop();\n            data = pc.data;\n            i = pc.i;\n            ttContext.functionsStackDeltas[funcId] = stack.length - pc.stackTop;\n          }\n        } else if (op === 0x89) {\n          if (inFDEF || inELSE) {\n            warn(\"TT: nested IDEFs not allowed\");\n            tooComplexToFollowFunctions = true;\n          }\n          inFDEF = true;\n          lastDeff = i;\n        } else if (op === 0x58) {\n          ++ifLevel;\n        } else if (op === 0x1b) {\n          inELSE = ifLevel;\n        } else if (op === 0x59) {\n          if (inELSE === ifLevel) {\n            inELSE = 0;\n          }\n          --ifLevel;\n        } else if (op === 0x1c) {\n          if (!inFDEF && !inELSE) {\n            const offset = stack.at(-1);\n            if (offset > 0) {\n              i += offset - 1;\n            }\n          }\n        }\n        if (!inFDEF && !inELSE) {\n          let stackDelta = 0;\n          if (op <= 0x8e) {\n            stackDelta = TTOpsStackDeltas[op];\n          } else if (op >= 0xc0 && op <= 0xdf) {\n            stackDelta = -1;\n          } else if (op >= 0xe0) {\n            stackDelta = -2;\n          }\n          if (op >= 0x71 && op <= 0x75) {\n            n = stack.pop();\n            if (!isNaN(n)) {\n              stackDelta = -n * 2;\n            }\n          }\n          while (stackDelta < 0 && stack.length > 0) {\n            stack.pop();\n            stackDelta++;\n          }\n          while (stackDelta > 0) {\n            stack.push(NaN);\n            stackDelta--;\n          }\n        }\n      }\n      ttContext.tooComplexToFollowFunctions = tooComplexToFollowFunctions;\n      const content = [data];\n      if (i > data.length) {\n        content.push(new Uint8Array(i - data.length));\n      }\n      if (lastDeff > lastEndf) {\n        warn(\"TT: complementing a missing function tail\");\n        content.push(new Uint8Array([0x22, 0x2d]));\n      }\n      foldTTTable(table, content);\n    }\n    function checkInvalidFunctions(ttContext, maxFunctionDefs) {\n      if (ttContext.tooComplexToFollowFunctions) {\n        return;\n      }\n      if (ttContext.functionsDefined.length > maxFunctionDefs) {\n        warn(\"TT: more functions defined than expected\");\n        ttContext.hintsValid = false;\n        return;\n      }\n      for (let j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) {\n        if (j > maxFunctionDefs) {\n          warn(\"TT: invalid function id: \" + j);\n          ttContext.hintsValid = false;\n          return;\n        }\n        if (ttContext.functionsUsed[j] && !ttContext.functionsDefined[j]) {\n          warn(\"TT: undefined function: \" + j);\n          ttContext.hintsValid = false;\n          return;\n        }\n      }\n    }\n    function foldTTTable(table, content) {\n      if (content.length > 1) {\n        let newLength = 0;\n        let j, jj;\n        for (j = 0, jj = content.length; j < jj; j++) {\n          newLength += content[j].length;\n        }\n        newLength = newLength + 3 & ~3;\n        const result = new Uint8Array(newLength);\n        let pos = 0;\n        for (j = 0, jj = content.length; j < jj; j++) {\n          result.set(content[j], pos);\n          pos += content[j].length;\n        }\n        table.data = result;\n        table.length = newLength;\n      }\n    }\n    function sanitizeTTPrograms(fpgm, prep, cvt, maxFunctionDefs) {\n      const ttContext = {\n        functionsDefined: [],\n        functionsUsed: [],\n        functionsStackDeltas: [],\n        tooComplexToFollowFunctions: false,\n        hintsValid: true\n      };\n      if (fpgm) {\n        sanitizeTTProgram(fpgm, ttContext);\n      }\n      if (prep) {\n        sanitizeTTProgram(prep, ttContext);\n      }\n      if (fpgm) {\n        checkInvalidFunctions(ttContext, maxFunctionDefs);\n      }\n      if (cvt && cvt.length & 1) {\n        const cvtData = new Uint8Array(cvt.length + 1);\n        cvtData.set(cvt.data);\n        cvt.data = cvtData;\n      }\n      return ttContext.hintsValid;\n    }\n    font = new Stream(new Uint8Array(font.getBytes()));\n    let header, tables;\n    if (isTrueTypeCollectionFile(font)) {\n      const ttcData = readTrueTypeCollectionData(font, this.name);\n      header = ttcData.header;\n      tables = ttcData.tables;\n    } else {\n      header = readOpenTypeHeader(font);\n      tables = readTables(font, header.numTables);\n    }\n    let cff, cffFile;\n    const isTrueType = !tables[\"CFF \"];\n    if (!isTrueType) {\n      const isComposite = properties.composite && (properties.cidToGidMap?.length > 0 || !(properties.cMap instanceof IdentityCMap));\n      if (header.version === \"OTTO\" && !isComposite || !tables.head || !tables.hhea || !tables.maxp || !tables.post) {\n        cffFile = new Stream(tables[\"CFF \"].data);\n        cff = new CFFFont(cffFile, properties);\n        adjustWidths(properties);\n        return this.convert(name, cff, properties);\n      }\n      delete tables.glyf;\n      delete tables.loca;\n      delete tables.fpgm;\n      delete tables.prep;\n      delete tables[\"cvt \"];\n      this.isOpenType = true;\n    } else {\n      if (!tables.loca) {\n        throw new FormatError('Required \"loca\" table is not found');\n      }\n      if (!tables.glyf) {\n        warn('Required \"glyf\" table is not found -- trying to recover.');\n        tables.glyf = {\n          tag: \"glyf\",\n          data: new Uint8Array(0)\n        };\n      }\n      this.isOpenType = false;\n    }\n    if (!tables.maxp) {\n      throw new FormatError('Required \"maxp\" table is not found');\n    }\n    font.pos = (font.start || 0) + tables.maxp.offset;\n    let version = font.getInt32();\n    const numGlyphs = font.getUint16();\n    if (version !== 0x00010000 && version !== 0x00005000) {\n      if (tables.maxp.length === 6) {\n        version = 0x0005000;\n      } else if (tables.maxp.length >= 32) {\n        version = 0x00010000;\n      } else {\n        throw new FormatError(`\"maxp\" table has a wrong version number`);\n      }\n      writeUint32(tables.maxp.data, 0, version);\n    }\n    if (properties.scaleFactors?.length === numGlyphs && isTrueType) {\n      const {\n        scaleFactors\n      } = properties;\n      const isGlyphLocationsLong = int16(tables.head.data[50], tables.head.data[51]);\n      const glyphs = new GlyfTable({\n        glyfTable: tables.glyf.data,\n        isGlyphLocationsLong,\n        locaTable: tables.loca.data,\n        numGlyphs\n      });\n      glyphs.scale(scaleFactors);\n      const {\n        glyf,\n        loca,\n        isLocationLong\n      } = glyphs.write();\n      tables.glyf.data = glyf;\n      tables.loca.data = loca;\n      if (isLocationLong !== !!isGlyphLocationsLong) {\n        tables.head.data[50] = 0;\n        tables.head.data[51] = isLocationLong ? 1 : 0;\n      }\n      const metrics = tables.hmtx.data;\n      for (let i = 0; i < numGlyphs; i++) {\n        const j = 4 * i;\n        const advanceWidth = Math.round(scaleFactors[i] * int16(metrics[j], metrics[j + 1]));\n        metrics[j] = advanceWidth >> 8 & 0xff;\n        metrics[j + 1] = advanceWidth & 0xff;\n        const lsb = Math.round(scaleFactors[i] * signedInt16(metrics[j + 2], metrics[j + 3]));\n        writeSignedInt16(metrics, j + 2, lsb);\n      }\n    }\n    let numGlyphsOut = numGlyphs + 1;\n    let dupFirstEntry = true;\n    if (numGlyphsOut > 0xffff) {\n      dupFirstEntry = false;\n      numGlyphsOut = numGlyphs;\n      warn(\"Not enough space in glyfs to duplicate first glyph.\");\n    }\n    let maxFunctionDefs = 0;\n    let maxSizeOfInstructions = 0;\n    if (version >= 0x00010000 && tables.maxp.length >= 32) {\n      font.pos += 8;\n      const maxZones = font.getUint16();\n      if (maxZones > 2) {\n        tables.maxp.data[14] = 0;\n        tables.maxp.data[15] = 2;\n      }\n      font.pos += 4;\n      maxFunctionDefs = font.getUint16();\n      font.pos += 4;\n      maxSizeOfInstructions = font.getUint16();\n    }\n    tables.maxp.data[4] = numGlyphsOut >> 8;\n    tables.maxp.data[5] = numGlyphsOut & 255;\n    const hintsValid = sanitizeTTPrograms(tables.fpgm, tables.prep, tables[\"cvt \"], maxFunctionDefs);\n    if (!hintsValid) {\n      delete tables.fpgm;\n      delete tables.prep;\n      delete tables[\"cvt \"];\n    }\n    sanitizeMetrics(font, tables.hhea, tables.hmtx, tables.head, numGlyphsOut, dupFirstEntry);\n    if (!tables.head) {\n      throw new FormatError('Required \"head\" table is not found');\n    }\n    sanitizeHead(tables.head, numGlyphs, isTrueType ? tables.loca.length : 0);\n    let missingGlyphs = Object.create(null);\n    if (isTrueType) {\n      const isGlyphLocationsLong = int16(tables.head.data[50], tables.head.data[51]);\n      const glyphsInfo = sanitizeGlyphLocations(tables.loca, tables.glyf, numGlyphs, isGlyphLocationsLong, hintsValid, dupFirstEntry, maxSizeOfInstructions);\n      missingGlyphs = glyphsInfo.missingGlyphs;\n      if (version >= 0x00010000 && tables.maxp.length >= 32) {\n        tables.maxp.data[26] = glyphsInfo.maxSizeOfInstructions >> 8;\n        tables.maxp.data[27] = glyphsInfo.maxSizeOfInstructions & 255;\n      }\n    }\n    if (!tables.hhea) {\n      throw new FormatError('Required \"hhea\" table is not found');\n    }\n    if (tables.hhea.data[10] === 0 && tables.hhea.data[11] === 0) {\n      tables.hhea.data[10] = 0xff;\n      tables.hhea.data[11] = 0xff;\n    }\n    const metricsOverride = {\n      unitsPerEm: int16(tables.head.data[18], tables.head.data[19]),\n      yMax: signedInt16(tables.head.data[42], tables.head.data[43]),\n      yMin: signedInt16(tables.head.data[38], tables.head.data[39]),\n      ascent: signedInt16(tables.hhea.data[4], tables.hhea.data[5]),\n      descent: signedInt16(tables.hhea.data[6], tables.hhea.data[7]),\n      lineGap: signedInt16(tables.hhea.data[8], tables.hhea.data[9])\n    };\n    this.ascent = metricsOverride.ascent / metricsOverride.unitsPerEm;\n    this.descent = metricsOverride.descent / metricsOverride.unitsPerEm;\n    this.lineGap = metricsOverride.lineGap / metricsOverride.unitsPerEm;\n    if (this.cssFontInfo?.lineHeight) {\n      this.lineHeight = this.cssFontInfo.metrics.lineHeight;\n      this.lineGap = this.cssFontInfo.metrics.lineGap;\n    } else {\n      this.lineHeight = this.ascent - this.descent + this.lineGap;\n    }\n    if (tables.post) {\n      readPostScriptTable(tables.post, properties, numGlyphs);\n    }\n    tables.post = {\n      tag: \"post\",\n      data: createPostTable(properties)\n    };\n    const charCodeToGlyphId = [];\n    function hasGlyph(glyphId) {\n      return !missingGlyphs[glyphId];\n    }\n    if (properties.composite) {\n      const cidToGidMap = properties.cidToGidMap || [];\n      const isCidToGidMapEmpty = cidToGidMap.length === 0;\n      properties.cMap.forEach(function (charCode, cid) {\n        if (typeof cid === \"string\") {\n          cid = convertCidString(charCode, cid, true);\n        }\n        if (cid > 0xffff) {\n          throw new FormatError(\"Max size of CID is 65,535\");\n        }\n        let glyphId = -1;\n        if (isCidToGidMapEmpty) {\n          glyphId = cid;\n        } else if (cidToGidMap[cid] !== undefined) {\n          glyphId = cidToGidMap[cid];\n        }\n        if (glyphId >= 0 && glyphId < numGlyphs && hasGlyph(glyphId)) {\n          charCodeToGlyphId[charCode] = glyphId;\n        }\n      });\n    } else {\n      const cmapTable = readCmapTable(tables.cmap, font, this.isSymbolicFont, properties.hasEncoding);\n      const cmapPlatformId = cmapTable.platformId;\n      const cmapEncodingId = cmapTable.encodingId;\n      const cmapMappings = cmapTable.mappings;\n      let baseEncoding = [],\n        forcePostTable = false;\n      if (properties.hasEncoding && (properties.baseEncodingName === \"MacRomanEncoding\" || properties.baseEncodingName === \"WinAnsiEncoding\")) {\n        baseEncoding = getEncoding(properties.baseEncodingName);\n      }\n      if (properties.hasEncoding && !this.isSymbolicFont && (cmapPlatformId === 3 && cmapEncodingId === 1 || cmapPlatformId === 1 && cmapEncodingId === 0)) {\n        const glyphsUnicodeMap = getGlyphsUnicode();\n        for (let charCode = 0; charCode < 256; charCode++) {\n          let glyphName;\n          if (this.differences[charCode] !== undefined) {\n            glyphName = this.differences[charCode];\n          } else if (baseEncoding.length && baseEncoding[charCode] !== \"\") {\n            glyphName = baseEncoding[charCode];\n          } else {\n            glyphName = StandardEncoding[charCode];\n          }\n          if (!glyphName) {\n            continue;\n          }\n          const standardGlyphName = recoverGlyphName(glyphName, glyphsUnicodeMap);\n          let unicodeOrCharCode;\n          if (cmapPlatformId === 3 && cmapEncodingId === 1) {\n            unicodeOrCharCode = glyphsUnicodeMap[standardGlyphName];\n          } else if (cmapPlatformId === 1 && cmapEncodingId === 0) {\n            unicodeOrCharCode = MacRomanEncoding.indexOf(standardGlyphName);\n          }\n          if (unicodeOrCharCode === undefined) {\n            if (!properties.glyphNames && properties.hasIncludedToUnicodeMap && !(this.toUnicode instanceof IdentityToUnicodeMap)) {\n              const unicode = this.toUnicode.get(charCode);\n              if (unicode) {\n                unicodeOrCharCode = unicode.codePointAt(0);\n              }\n            }\n            if (unicodeOrCharCode === undefined) {\n              continue;\n            }\n          }\n          for (const mapping of cmapMappings) {\n            if (mapping.charCode !== unicodeOrCharCode) {\n              continue;\n            }\n            charCodeToGlyphId[charCode] = mapping.glyphId;\n            break;\n          }\n        }\n      } else if (cmapPlatformId === 0) {\n        for (const mapping of cmapMappings) {\n          charCodeToGlyphId[mapping.charCode] = mapping.glyphId;\n        }\n        forcePostTable = true;\n      } else {\n        for (const mapping of cmapMappings) {\n          let charCode = mapping.charCode;\n          if (cmapPlatformId === 3 && charCode >= 0xf000 && charCode <= 0xf0ff) {\n            charCode &= 0xff;\n          }\n          charCodeToGlyphId[charCode] = mapping.glyphId;\n        }\n      }\n      if (properties.glyphNames && (baseEncoding.length || this.differences.length)) {\n        for (let i = 0; i < 256; ++i) {\n          if (!forcePostTable && charCodeToGlyphId[i] !== undefined) {\n            continue;\n          }\n          const glyphName = this.differences[i] || baseEncoding[i];\n          if (!glyphName) {\n            continue;\n          }\n          const glyphId = properties.glyphNames.indexOf(glyphName);\n          if (glyphId > 0 && hasGlyph(glyphId)) {\n            charCodeToGlyphId[i] = glyphId;\n          }\n        }\n      }\n    }\n    if (charCodeToGlyphId.length === 0) {\n      charCodeToGlyphId[0] = 0;\n    }\n    let glyphZeroId = numGlyphsOut - 1;\n    if (!dupFirstEntry) {\n      glyphZeroId = 0;\n    }\n    if (!properties.cssFontInfo) {\n      const newMapping = adjustMapping(charCodeToGlyphId, hasGlyph, glyphZeroId, this.toUnicode);\n      this.toFontChar = newMapping.toFontChar;\n      tables.cmap = {\n        tag: \"cmap\",\n        data: createCmapTable(newMapping.charCodeToGlyphId, newMapping.toUnicodeExtraMap, numGlyphsOut)\n      };\n      if (!tables[\"OS/2\"] || !validateOS2Table(tables[\"OS/2\"], font)) {\n        tables[\"OS/2\"] = {\n          tag: \"OS/2\",\n          data: createOS2Table(properties, newMapping.charCodeToGlyphId, metricsOverride)\n        };\n      }\n    }\n    if (!isTrueType) {\n      try {\n        cffFile = new Stream(tables[\"CFF \"].data);\n        const parser = new CFFParser(cffFile, properties, SEAC_ANALYSIS_ENABLED);\n        cff = parser.parse();\n        cff.duplicateFirstGlyph();\n        const compiler = new CFFCompiler(cff);\n        tables[\"CFF \"].data = compiler.compile();\n      } catch {\n        warn(\"Failed to compile font \" + properties.loadedName);\n      }\n    }\n    if (!tables.name) {\n      tables.name = {\n        tag: \"name\",\n        data: createNameTable(this.name)\n      };\n    } else {\n      const [namePrototype, nameRecords] = readNameTable(tables.name);\n      tables.name.data = createNameTable(name, namePrototype);\n      this.psName = namePrototype[0][6] || null;\n      if (!properties.composite) {\n        adjustTrueTypeToUnicode(properties, this.isSymbolicFont, nameRecords);\n      }\n    }\n    const builder = new OpenTypeFileBuilder(header.version);\n    for (const tableTag in tables) {\n      builder.addTable(tableTag, tables[tableTag].data);\n    }\n    return builder.toArray();\n  }\n  convert(fontName, font, properties) {\n    properties.fixedPitch = false;\n    if (properties.builtInEncoding) {\n      adjustType1ToUnicode(properties, properties.builtInEncoding);\n    }\n    let glyphZeroId = 1;\n    if (font instanceof CFFFont) {\n      glyphZeroId = font.numGlyphs - 1;\n    }\n    const mapping = font.getGlyphMapping(properties);\n    let newMapping = null;\n    let newCharCodeToGlyphId = mapping;\n    let toUnicodeExtraMap = null;\n    if (!properties.cssFontInfo) {\n      newMapping = adjustMapping(mapping, font.hasGlyphId.bind(font), glyphZeroId, this.toUnicode);\n      this.toFontChar = newMapping.toFontChar;\n      newCharCodeToGlyphId = newMapping.charCodeToGlyphId;\n      toUnicodeExtraMap = newMapping.toUnicodeExtraMap;\n    }\n    const numGlyphs = font.numGlyphs;\n    function getCharCodes(charCodeToGlyphId, glyphId) {\n      let charCodes = null;\n      for (const charCode in charCodeToGlyphId) {\n        if (glyphId === charCodeToGlyphId[charCode]) {\n          (charCodes ||= []).push(charCode | 0);\n        }\n      }\n      return charCodes;\n    }\n    function createCharCode(charCodeToGlyphId, glyphId) {\n      for (const charCode in charCodeToGlyphId) {\n        if (glyphId === charCodeToGlyphId[charCode]) {\n          return charCode | 0;\n        }\n      }\n      newMapping.charCodeToGlyphId[newMapping.nextAvailableFontCharCode] = glyphId;\n      return newMapping.nextAvailableFontCharCode++;\n    }\n    const seacs = font.seacs;\n    if (newMapping && SEAC_ANALYSIS_ENABLED && seacs?.length) {\n      const matrix = properties.fontMatrix || FONT_IDENTITY_MATRIX;\n      const charset = font.getCharset();\n      const seacMap = Object.create(null);\n      for (let glyphId in seacs) {\n        glyphId |= 0;\n        const seac = seacs[glyphId];\n        const baseGlyphName = StandardEncoding[seac[2]];\n        const accentGlyphName = StandardEncoding[seac[3]];\n        const baseGlyphId = charset.indexOf(baseGlyphName);\n        const accentGlyphId = charset.indexOf(accentGlyphName);\n        if (baseGlyphId < 0 || accentGlyphId < 0) {\n          continue;\n        }\n        const accentOffset = {\n          x: seac[0] * matrix[0] + seac[1] * matrix[2] + matrix[4],\n          y: seac[0] * matrix[1] + seac[1] * matrix[3] + matrix[5]\n        };\n        const charCodes = getCharCodes(mapping, glyphId);\n        if (!charCodes) {\n          continue;\n        }\n        for (const charCode of charCodes) {\n          const charCodeToGlyphId = newMapping.charCodeToGlyphId;\n          const baseFontCharCode = createCharCode(charCodeToGlyphId, baseGlyphId);\n          const accentFontCharCode = createCharCode(charCodeToGlyphId, accentGlyphId);\n          seacMap[charCode] = {\n            baseFontCharCode,\n            accentFontCharCode,\n            accentOffset\n          };\n        }\n      }\n      properties.seacMap = seacMap;\n    }\n    const unitsPerEm = 1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0];\n    const builder = new OpenTypeFileBuilder(\"\\x4F\\x54\\x54\\x4F\");\n    builder.addTable(\"CFF \", font.data);\n    builder.addTable(\"OS/2\", createOS2Table(properties, newCharCodeToGlyphId));\n    builder.addTable(\"cmap\", createCmapTable(newCharCodeToGlyphId, toUnicodeExtraMap, numGlyphs));\n    builder.addTable(\"head\", \"\\x00\\x01\\x00\\x00\" + \"\\x00\\x00\\x10\\x00\" + \"\\x00\\x00\\x00\\x00\" + \"\\x5F\\x0F\\x3C\\xF5\" + \"\\x00\\x00\" + safeString16(unitsPerEm) + \"\\x00\\x00\\x00\\x00\\x9e\\x0b\\x7e\\x27\" + \"\\x00\\x00\\x00\\x00\\x9e\\x0b\\x7e\\x27\" + \"\\x00\\x00\" + safeString16(properties.descent) + \"\\x0F\\xFF\" + safeString16(properties.ascent) + string16(properties.italicAngle ? 2 : 0) + \"\\x00\\x11\" + \"\\x00\\x00\" + \"\\x00\\x00\" + \"\\x00\\x00\");\n    builder.addTable(\"hhea\", \"\\x00\\x01\\x00\\x00\" + safeString16(properties.ascent) + safeString16(properties.descent) + \"\\x00\\x00\" + \"\\xFF\\xFF\" + \"\\x00\\x00\" + \"\\x00\\x00\" + \"\\x00\\x00\" + safeString16(properties.capHeight) + safeString16(Math.tan(properties.italicAngle) * properties.xHeight) + \"\\x00\\x00\" + \"\\x00\\x00\" + \"\\x00\\x00\" + \"\\x00\\x00\" + \"\\x00\\x00\" + \"\\x00\\x00\" + string16(numGlyphs));\n    builder.addTable(\"hmtx\", function fontFieldsHmtx() {\n      const charstrings = font.charstrings;\n      const cffWidths = font.cff ? font.cff.widths : null;\n      let hmtx = \"\\x00\\x00\\x00\\x00\";\n      for (let i = 1, ii = numGlyphs; i < ii; i++) {\n        let width = 0;\n        if (charstrings) {\n          const charstring = charstrings[i - 1];\n          width = \"width\" in charstring ? charstring.width : 0;\n        } else if (cffWidths) {\n          width = Math.ceil(cffWidths[i] || 0);\n        }\n        hmtx += string16(width) + string16(0);\n      }\n      return hmtx;\n    }());\n    builder.addTable(\"maxp\", \"\\x00\\x00\\x50\\x00\" + string16(numGlyphs));\n    builder.addTable(\"name\", createNameTable(fontName));\n    builder.addTable(\"post\", createPostTable(properties));\n    return builder.toArray();\n  }\n  get spaceWidth() {\n    const possibleSpaceReplacements = [\"space\", \"minus\", \"one\", \"i\", \"I\"];\n    let width;\n    for (const glyphName of possibleSpaceReplacements) {\n      if (glyphName in this.widths) {\n        width = this.widths[glyphName];\n        break;\n      }\n      const glyphsUnicodeMap = getGlyphsUnicode();\n      const glyphUnicode = glyphsUnicodeMap[glyphName];\n      let charcode = 0;\n      if (this.composite && this.cMap.contains(glyphUnicode)) {\n        charcode = this.cMap.lookup(glyphUnicode);\n        if (typeof charcode === \"string\") {\n          charcode = convertCidString(glyphUnicode, charcode);\n        }\n      }\n      if (!charcode && this.toUnicode) {\n        charcode = this.toUnicode.charCodeOf(glyphUnicode);\n      }\n      if (charcode <= 0) {\n        charcode = glyphUnicode;\n      }\n      width = this.widths[charcode];\n      if (width) {\n        break;\n      }\n    }\n    return shadow(this, \"spaceWidth\", width || this.defaultWidth);\n  }\n  _charToGlyph(charcode, isSpace = false) {\n    let glyph = this._glyphCache[charcode];\n    if (glyph?.isSpace === isSpace) {\n      return glyph;\n    }\n    let fontCharCode, width, operatorListId;\n    let widthCode = charcode;\n    if (this.cMap?.contains(charcode)) {\n      widthCode = this.cMap.lookup(charcode);\n      if (typeof widthCode === \"string\") {\n        widthCode = convertCidString(charcode, widthCode);\n      }\n    }\n    width = this.widths[widthCode];\n    if (typeof width !== \"number\") {\n      width = this.defaultWidth;\n    }\n    const vmetric = this.vmetrics?.[widthCode];\n    let unicode = this.toUnicode.get(charcode) || charcode;\n    if (typeof unicode === \"number\") {\n      unicode = String.fromCharCode(unicode);\n    }\n    let isInFont = this.toFontChar[charcode] !== undefined;\n    fontCharCode = this.toFontChar[charcode] || charcode;\n    if (this.missingFile) {\n      const glyphName = this.differences[charcode] || this.defaultEncoding[charcode];\n      if ((glyphName === \".notdef\" || glyphName === \"\") && this.type === \"Type1\") {\n        fontCharCode = 0x20;\n      }\n      fontCharCode = mapSpecialUnicodeValues(fontCharCode);\n    }\n    if (this.isType3Font) {\n      operatorListId = fontCharCode;\n    }\n    let accent = null;\n    if (this.seacMap?.[charcode]) {\n      isInFont = true;\n      const seac = this.seacMap[charcode];\n      fontCharCode = seac.baseFontCharCode;\n      accent = {\n        fontChar: String.fromCodePoint(seac.accentFontCharCode),\n        offset: seac.accentOffset\n      };\n    }\n    let fontChar = \"\";\n    if (typeof fontCharCode === \"number\") {\n      if (fontCharCode <= 0x10ffff) {\n        fontChar = String.fromCodePoint(fontCharCode);\n      } else {\n        warn(`charToGlyph - invalid fontCharCode: ${fontCharCode}`);\n      }\n    }\n    glyph = new fonts_Glyph(charcode, fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont);\n    return this._glyphCache[charcode] = glyph;\n  }\n  charsToGlyphs(chars) {\n    let glyphs = this._charsCache[chars];\n    if (glyphs) {\n      return glyphs;\n    }\n    glyphs = [];\n    if (this.cMap) {\n      const c = Object.create(null),\n        ii = chars.length;\n      let i = 0;\n      while (i < ii) {\n        this.cMap.readCharCode(chars, i, c);\n        const {\n          charcode,\n          length\n        } = c;\n        i += length;\n        const glyph = this._charToGlyph(charcode, length === 1 && chars.charCodeAt(i - 1) === 0x20);\n        glyphs.push(glyph);\n      }\n    } else {\n      for (let i = 0, ii = chars.length; i < ii; ++i) {\n        const charcode = chars.charCodeAt(i);\n        const glyph = this._charToGlyph(charcode, charcode === 0x20);\n        glyphs.push(glyph);\n      }\n    }\n    return this._charsCache[chars] = glyphs;\n  }\n  getCharPositions(chars) {\n    const positions = [];\n    if (this.cMap) {\n      const c = Object.create(null);\n      let i = 0;\n      while (i < chars.length) {\n        this.cMap.readCharCode(chars, i, c);\n        const length = c.length;\n        positions.push([i, i + length]);\n        i += length;\n      }\n    } else {\n      for (let i = 0, ii = chars.length; i < ii; ++i) {\n        positions.push([i, i + 1]);\n      }\n    }\n    return positions;\n  }\n  get glyphCacheValues() {\n    return Object.values(this._glyphCache);\n  }\n  encodeString(str) {\n    const buffers = [];\n    const currentBuf = [];\n    const hasCurrentBufErrors = () => buffers.length % 2 === 1;\n    const getCharCode = this.toUnicode instanceof IdentityToUnicodeMap ? unicode => this.toUnicode.charCodeOf(unicode) : unicode => this.toUnicode.charCodeOf(String.fromCodePoint(unicode));\n    for (let i = 0, ii = str.length; i < ii; i++) {\n      const unicode = str.codePointAt(i);\n      if (unicode > 0xd7ff && (unicode < 0xe000 || unicode > 0xfffd)) {\n        i++;\n      }\n      if (this.toUnicode) {\n        const charCode = getCharCode(unicode);\n        if (charCode !== -1) {\n          if (hasCurrentBufErrors()) {\n            buffers.push(currentBuf.join(\"\"));\n            currentBuf.length = 0;\n          }\n          const charCodeLength = this.cMap ? this.cMap.getCharCodeLength(charCode) : 1;\n          for (let j = charCodeLength - 1; j >= 0; j--) {\n            currentBuf.push(String.fromCharCode(charCode >> 8 * j & 0xff));\n          }\n          continue;\n        }\n      }\n      if (!hasCurrentBufErrors()) {\n        buffers.push(currentBuf.join(\"\"));\n        currentBuf.length = 0;\n      }\n      currentBuf.push(String.fromCodePoint(unicode));\n    }\n    buffers.push(currentBuf.join(\"\"));\n    return buffers;\n  }\n}\nclass ErrorFont {\n  constructor(error) {\n    this.error = error;\n    this.loadedName = \"g_font_error\";\n    this.missingFile = true;\n  }\n  charsToGlyphs() {\n    return [];\n  }\n  encodeString(chars) {\n    return [chars];\n  }\n  exportData(extraProperties = false) {\n    return {\n      error: this.error\n    };\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/pattern.js\n\n\n\n\nconst ShadingType = {\n  FUNCTION_BASED: 1,\n  AXIAL: 2,\n  RADIAL: 3,\n  FREE_FORM_MESH: 4,\n  LATTICE_FORM_MESH: 5,\n  COONS_PATCH_MESH: 6,\n  TENSOR_PATCH_MESH: 7\n};\nclass Pattern {\n  constructor() {\n    unreachable(\"Cannot initialize Pattern.\");\n  }\n  static parseShading(shading, xref, res, pdfFunctionFactory, localColorSpaceCache) {\n    const dict = shading instanceof BaseStream ? shading.dict : shading;\n    const type = dict.get(\"ShadingType\");\n    try {\n      switch (type) {\n        case ShadingType.AXIAL:\n        case ShadingType.RADIAL:\n          return new RadialAxialShading(dict, xref, res, pdfFunctionFactory, localColorSpaceCache);\n        case ShadingType.FREE_FORM_MESH:\n        case ShadingType.LATTICE_FORM_MESH:\n        case ShadingType.COONS_PATCH_MESH:\n        case ShadingType.TENSOR_PATCH_MESH:\n          return new MeshShading(shading, xref, res, pdfFunctionFactory, localColorSpaceCache);\n        default:\n          throw new FormatError(\"Unsupported ShadingType: \" + type);\n      }\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(ex);\n      return new DummyShading();\n    }\n  }\n}\nclass BaseShading {\n  static SMALL_NUMBER = 1e-6;\n  constructor() {\n    if (this.constructor === BaseShading) {\n      unreachable(\"Cannot initialize BaseShading.\");\n    }\n  }\n  getIR() {\n    unreachable(\"Abstract method `getIR` called.\");\n  }\n}\nclass RadialAxialShading extends BaseShading {\n  constructor(dict, xref, resources, pdfFunctionFactory, localColorSpaceCache) {\n    super();\n    this.coordsArr = dict.getArray(\"Coords\");\n    this.shadingType = dict.get(\"ShadingType\");\n    const cs = ColorSpace.parse({\n      cs: dict.getRaw(\"CS\") || dict.getRaw(\"ColorSpace\"),\n      xref,\n      resources,\n      pdfFunctionFactory,\n      localColorSpaceCache\n    });\n    const bbox = dict.getArray(\"BBox\");\n    this.bbox = Array.isArray(bbox) && bbox.length === 4 ? Util.normalizeRect(bbox) : null;\n    let t0 = 0.0,\n      t1 = 1.0;\n    if (dict.has(\"Domain\")) {\n      const domainArr = dict.getArray(\"Domain\");\n      t0 = domainArr[0];\n      t1 = domainArr[1];\n    }\n    let extendStart = false,\n      extendEnd = false;\n    if (dict.has(\"Extend\")) {\n      const extendArr = dict.getArray(\"Extend\");\n      extendStart = extendArr[0];\n      extendEnd = extendArr[1];\n    }\n    if (this.shadingType === ShadingType.RADIAL && (!extendStart || !extendEnd)) {\n      const [x1, y1, r1, x2, y2, r2] = this.coordsArr;\n      const distance = Math.hypot(x1 - x2, y1 - y2);\n      if (r1 <= r2 + distance && r2 <= r1 + distance) {\n        warn(\"Unsupported radial gradient.\");\n      }\n    }\n    this.extendStart = extendStart;\n    this.extendEnd = extendEnd;\n    const fnObj = dict.getRaw(\"Function\");\n    const fn = pdfFunctionFactory.createFromArray(fnObj);\n    const NUMBER_OF_SAMPLES = 840;\n    const step = (t1 - t0) / NUMBER_OF_SAMPLES;\n    const colorStops = this.colorStops = [];\n    if (t0 >= t1 || step <= 0) {\n      info(\"Bad shading domain.\");\n      return;\n    }\n    const color = new Float32Array(cs.numComps),\n      ratio = new Float32Array(1);\n    let rgbColor;\n    let iBase = 0;\n    ratio[0] = t0;\n    fn(ratio, 0, color, 0);\n    let rgbBase = cs.getRgb(color, 0);\n    const cssColorBase = Util.makeHexColor(rgbBase[0], rgbBase[1], rgbBase[2]);\n    colorStops.push([0, cssColorBase]);\n    let iPrev = 1;\n    ratio[0] = t0 + step;\n    fn(ratio, 0, color, 0);\n    let rgbPrev = cs.getRgb(color, 0);\n    let maxSlopeR = rgbPrev[0] - rgbBase[0] + 1;\n    let maxSlopeG = rgbPrev[1] - rgbBase[1] + 1;\n    let maxSlopeB = rgbPrev[2] - rgbBase[2] + 1;\n    let minSlopeR = rgbPrev[0] - rgbBase[0] - 1;\n    let minSlopeG = rgbPrev[1] - rgbBase[1] - 1;\n    let minSlopeB = rgbPrev[2] - rgbBase[2] - 1;\n    for (let i = 2; i < NUMBER_OF_SAMPLES; i++) {\n      ratio[0] = t0 + i * step;\n      fn(ratio, 0, color, 0);\n      rgbColor = cs.getRgb(color, 0);\n      const run = i - iBase;\n      maxSlopeR = Math.min(maxSlopeR, (rgbColor[0] - rgbBase[0] + 1) / run);\n      maxSlopeG = Math.min(maxSlopeG, (rgbColor[1] - rgbBase[1] + 1) / run);\n      maxSlopeB = Math.min(maxSlopeB, (rgbColor[2] - rgbBase[2] + 1) / run);\n      minSlopeR = Math.max(minSlopeR, (rgbColor[0] - rgbBase[0] - 1) / run);\n      minSlopeG = Math.max(minSlopeG, (rgbColor[1] - rgbBase[1] - 1) / run);\n      minSlopeB = Math.max(minSlopeB, (rgbColor[2] - rgbBase[2] - 1) / run);\n      const slopesExist = minSlopeR <= maxSlopeR && minSlopeG <= maxSlopeG && minSlopeB <= maxSlopeB;\n      if (!slopesExist) {\n        const cssColor = Util.makeHexColor(rgbPrev[0], rgbPrev[1], rgbPrev[2]);\n        colorStops.push([iPrev / NUMBER_OF_SAMPLES, cssColor]);\n        maxSlopeR = rgbColor[0] - rgbPrev[0] + 1;\n        maxSlopeG = rgbColor[1] - rgbPrev[1] + 1;\n        maxSlopeB = rgbColor[2] - rgbPrev[2] + 1;\n        minSlopeR = rgbColor[0] - rgbPrev[0] - 1;\n        minSlopeG = rgbColor[1] - rgbPrev[1] - 1;\n        minSlopeB = rgbColor[2] - rgbPrev[2] - 1;\n        iBase = iPrev;\n        rgbBase = rgbPrev;\n      }\n      iPrev = i;\n      rgbPrev = rgbColor;\n    }\n    const cssColor = Util.makeHexColor(rgbPrev[0], rgbPrev[1], rgbPrev[2]);\n    colorStops.push([1, cssColor]);\n    let background = \"transparent\";\n    if (dict.has(\"Background\")) {\n      rgbColor = cs.getRgb(dict.get(\"Background\"), 0);\n      background = Util.makeHexColor(rgbColor[0], rgbColor[1], rgbColor[2]);\n    }\n    if (!extendStart) {\n      colorStops.unshift([0, background]);\n      colorStops[1][0] += BaseShading.SMALL_NUMBER;\n    }\n    if (!extendEnd) {\n      colorStops.at(-1)[0] -= BaseShading.SMALL_NUMBER;\n      colorStops.push([1, background]);\n    }\n    this.colorStops = colorStops;\n  }\n  getIR() {\n    const coordsArr = this.coordsArr;\n    const shadingType = this.shadingType;\n    let type, p0, p1, r0, r1;\n    if (shadingType === ShadingType.AXIAL) {\n      p0 = [coordsArr[0], coordsArr[1]];\n      p1 = [coordsArr[2], coordsArr[3]];\n      r0 = null;\n      r1 = null;\n      type = \"axial\";\n    } else if (shadingType === ShadingType.RADIAL) {\n      p0 = [coordsArr[0], coordsArr[1]];\n      p1 = [coordsArr[3], coordsArr[4]];\n      r0 = coordsArr[2];\n      r1 = coordsArr[5];\n      type = \"radial\";\n    } else {\n      unreachable(`getPattern type unknown: ${shadingType}`);\n    }\n    return [\"RadialAxial\", type, this.bbox, this.colorStops, p0, p1, r0, r1];\n  }\n}\nclass MeshStreamReader {\n  constructor(stream, context) {\n    this.stream = stream;\n    this.context = context;\n    this.buffer = 0;\n    this.bufferLength = 0;\n    const numComps = context.numComps;\n    this.tmpCompsBuf = new Float32Array(numComps);\n    const csNumComps = context.colorSpace.numComps;\n    this.tmpCsCompsBuf = context.colorFn ? new Float32Array(csNumComps) : this.tmpCompsBuf;\n  }\n  get hasData() {\n    if (this.stream.end) {\n      return this.stream.pos < this.stream.end;\n    }\n    if (this.bufferLength > 0) {\n      return true;\n    }\n    const nextByte = this.stream.getByte();\n    if (nextByte < 0) {\n      return false;\n    }\n    this.buffer = nextByte;\n    this.bufferLength = 8;\n    return true;\n  }\n  readBits(n) {\n    let buffer = this.buffer;\n    let bufferLength = this.bufferLength;\n    if (n === 32) {\n      if (bufferLength === 0) {\n        return (this.stream.getByte() << 24 | this.stream.getByte() << 16 | this.stream.getByte() << 8 | this.stream.getByte()) >>> 0;\n      }\n      buffer = buffer << 24 | this.stream.getByte() << 16 | this.stream.getByte() << 8 | this.stream.getByte();\n      const nextByte = this.stream.getByte();\n      this.buffer = nextByte & (1 << bufferLength) - 1;\n      return (buffer << 8 - bufferLength | (nextByte & 0xff) >> bufferLength) >>> 0;\n    }\n    if (n === 8 && bufferLength === 0) {\n      return this.stream.getByte();\n    }\n    while (bufferLength < n) {\n      buffer = buffer << 8 | this.stream.getByte();\n      bufferLength += 8;\n    }\n    bufferLength -= n;\n    this.bufferLength = bufferLength;\n    this.buffer = buffer & (1 << bufferLength) - 1;\n    return buffer >> bufferLength;\n  }\n  align() {\n    this.buffer = 0;\n    this.bufferLength = 0;\n  }\n  readFlag() {\n    return this.readBits(this.context.bitsPerFlag);\n  }\n  readCoordinate() {\n    const bitsPerCoordinate = this.context.bitsPerCoordinate;\n    const xi = this.readBits(bitsPerCoordinate);\n    const yi = this.readBits(bitsPerCoordinate);\n    const decode = this.context.decode;\n    const scale = bitsPerCoordinate < 32 ? 1 / ((1 << bitsPerCoordinate) - 1) : 2.3283064365386963e-10;\n    return [xi * scale * (decode[1] - decode[0]) + decode[0], yi * scale * (decode[3] - decode[2]) + decode[2]];\n  }\n  readComponents() {\n    const numComps = this.context.numComps;\n    const bitsPerComponent = this.context.bitsPerComponent;\n    const scale = bitsPerComponent < 32 ? 1 / ((1 << bitsPerComponent) - 1) : 2.3283064365386963e-10;\n    const decode = this.context.decode;\n    const components = this.tmpCompsBuf;\n    for (let i = 0, j = 4; i < numComps; i++, j += 2) {\n      const ci = this.readBits(bitsPerComponent);\n      components[i] = ci * scale * (decode[j + 1] - decode[j]) + decode[j];\n    }\n    const color = this.tmpCsCompsBuf;\n    if (this.context.colorFn) {\n      this.context.colorFn(components, 0, color, 0);\n    }\n    return this.context.colorSpace.getRgb(color, 0);\n  }\n}\nlet bCache = Object.create(null);\nfunction buildB(count) {\n  const lut = [];\n  for (let i = 0; i <= count; i++) {\n    const t = i / count,\n      t_ = 1 - t;\n    lut.push(new Float32Array([t_ ** 3, 3 * t * t_ ** 2, 3 * t ** 2 * t_, t ** 3]));\n  }\n  return lut;\n}\nfunction getB(count) {\n  return bCache[count] ||= buildB(count);\n}\nfunction clearPatternCaches() {\n  bCache = Object.create(null);\n}\nclass MeshShading extends BaseShading {\n  static MIN_SPLIT_PATCH_CHUNKS_AMOUNT = 3;\n  static MAX_SPLIT_PATCH_CHUNKS_AMOUNT = 20;\n  static TRIANGLE_DENSITY = 20;\n  constructor(stream, xref, resources, pdfFunctionFactory, localColorSpaceCache) {\n    super();\n    if (!(stream instanceof BaseStream)) {\n      throw new FormatError(\"Mesh data is not a stream\");\n    }\n    const dict = stream.dict;\n    this.shadingType = dict.get(\"ShadingType\");\n    const bbox = dict.getArray(\"BBox\");\n    this.bbox = Array.isArray(bbox) && bbox.length === 4 ? Util.normalizeRect(bbox) : null;\n    const cs = ColorSpace.parse({\n      cs: dict.getRaw(\"CS\") || dict.getRaw(\"ColorSpace\"),\n      xref,\n      resources,\n      pdfFunctionFactory,\n      localColorSpaceCache\n    });\n    this.background = dict.has(\"Background\") ? cs.getRgb(dict.get(\"Background\"), 0) : null;\n    const fnObj = dict.getRaw(\"Function\");\n    const fn = fnObj ? pdfFunctionFactory.createFromArray(fnObj) : null;\n    this.coords = [];\n    this.colors = [];\n    this.figures = [];\n    const decodeContext = {\n      bitsPerCoordinate: dict.get(\"BitsPerCoordinate\"),\n      bitsPerComponent: dict.get(\"BitsPerComponent\"),\n      bitsPerFlag: dict.get(\"BitsPerFlag\"),\n      decode: dict.getArray(\"Decode\"),\n      colorFn: fn,\n      colorSpace: cs,\n      numComps: fn ? 1 : cs.numComps\n    };\n    const reader = new MeshStreamReader(stream, decodeContext);\n    let patchMesh = false;\n    switch (this.shadingType) {\n      case ShadingType.FREE_FORM_MESH:\n        this._decodeType4Shading(reader);\n        break;\n      case ShadingType.LATTICE_FORM_MESH:\n        const verticesPerRow = dict.get(\"VerticesPerRow\") | 0;\n        if (verticesPerRow < 2) {\n          throw new FormatError(\"Invalid VerticesPerRow\");\n        }\n        this._decodeType5Shading(reader, verticesPerRow);\n        break;\n      case ShadingType.COONS_PATCH_MESH:\n        this._decodeType6Shading(reader);\n        patchMesh = true;\n        break;\n      case ShadingType.TENSOR_PATCH_MESH:\n        this._decodeType7Shading(reader);\n        patchMesh = true;\n        break;\n      default:\n        unreachable(\"Unsupported mesh type.\");\n        break;\n    }\n    if (patchMesh) {\n      this._updateBounds();\n      for (let i = 0, ii = this.figures.length; i < ii; i++) {\n        this._buildFigureFromPatch(i);\n      }\n    }\n    this._updateBounds();\n    this._packData();\n  }\n  _decodeType4Shading(reader) {\n    const coords = this.coords;\n    const colors = this.colors;\n    const operators = [];\n    const ps = [];\n    let verticesLeft = 0;\n    while (reader.hasData) {\n      const f = reader.readFlag();\n      const coord = reader.readCoordinate();\n      const color = reader.readComponents();\n      if (verticesLeft === 0) {\n        if (!(0 <= f && f <= 2)) {\n          throw new FormatError(\"Unknown type4 flag\");\n        }\n        switch (f) {\n          case 0:\n            verticesLeft = 3;\n            break;\n          case 1:\n            ps.push(ps.at(-2), ps.at(-1));\n            verticesLeft = 1;\n            break;\n          case 2:\n            ps.push(ps.at(-3), ps.at(-1));\n            verticesLeft = 1;\n            break;\n        }\n        operators.push(f);\n      }\n      ps.push(coords.length);\n      coords.push(coord);\n      colors.push(color);\n      verticesLeft--;\n      reader.align();\n    }\n    this.figures.push({\n      type: \"triangles\",\n      coords: new Int32Array(ps),\n      colors: new Int32Array(ps)\n    });\n  }\n  _decodeType5Shading(reader, verticesPerRow) {\n    const coords = this.coords;\n    const colors = this.colors;\n    const ps = [];\n    while (reader.hasData) {\n      const coord = reader.readCoordinate();\n      const color = reader.readComponents();\n      ps.push(coords.length);\n      coords.push(coord);\n      colors.push(color);\n    }\n    this.figures.push({\n      type: \"lattice\",\n      coords: new Int32Array(ps),\n      colors: new Int32Array(ps),\n      verticesPerRow\n    });\n  }\n  _decodeType6Shading(reader) {\n    const coords = this.coords;\n    const colors = this.colors;\n    const ps = new Int32Array(16);\n    const cs = new Int32Array(4);\n    while (reader.hasData) {\n      const f = reader.readFlag();\n      if (!(0 <= f && f <= 3)) {\n        throw new FormatError(\"Unknown type6 flag\");\n      }\n      const pi = coords.length;\n      for (let i = 0, ii = f !== 0 ? 8 : 12; i < ii; i++) {\n        coords.push(reader.readCoordinate());\n      }\n      const ci = colors.length;\n      for (let i = 0, ii = f !== 0 ? 2 : 4; i < ii; i++) {\n        colors.push(reader.readComponents());\n      }\n      let tmp1, tmp2, tmp3, tmp4;\n      switch (f) {\n        case 0:\n          ps[12] = pi + 3;\n          ps[13] = pi + 4;\n          ps[14] = pi + 5;\n          ps[15] = pi + 6;\n          ps[8] = pi + 2;\n          ps[11] = pi + 7;\n          ps[4] = pi + 1;\n          ps[7] = pi + 8;\n          ps[0] = pi;\n          ps[1] = pi + 11;\n          ps[2] = pi + 10;\n          ps[3] = pi + 9;\n          cs[2] = ci + 1;\n          cs[3] = ci + 2;\n          cs[0] = ci;\n          cs[1] = ci + 3;\n          break;\n        case 1:\n          tmp1 = ps[12];\n          tmp2 = ps[13];\n          tmp3 = ps[14];\n          tmp4 = ps[15];\n          ps[12] = tmp4;\n          ps[13] = pi + 0;\n          ps[14] = pi + 1;\n          ps[15] = pi + 2;\n          ps[8] = tmp3;\n          ps[11] = pi + 3;\n          ps[4] = tmp2;\n          ps[7] = pi + 4;\n          ps[0] = tmp1;\n          ps[1] = pi + 7;\n          ps[2] = pi + 6;\n          ps[3] = pi + 5;\n          tmp1 = cs[2];\n          tmp2 = cs[3];\n          cs[2] = tmp2;\n          cs[3] = ci;\n          cs[0] = tmp1;\n          cs[1] = ci + 1;\n          break;\n        case 2:\n          tmp1 = ps[15];\n          tmp2 = ps[11];\n          ps[12] = ps[3];\n          ps[13] = pi + 0;\n          ps[14] = pi + 1;\n          ps[15] = pi + 2;\n          ps[8] = ps[7];\n          ps[11] = pi + 3;\n          ps[4] = tmp2;\n          ps[7] = pi + 4;\n          ps[0] = tmp1;\n          ps[1] = pi + 7;\n          ps[2] = pi + 6;\n          ps[3] = pi + 5;\n          tmp1 = cs[3];\n          cs[2] = cs[1];\n          cs[3] = ci;\n          cs[0] = tmp1;\n          cs[1] = ci + 1;\n          break;\n        case 3:\n          ps[12] = ps[0];\n          ps[13] = pi + 0;\n          ps[14] = pi + 1;\n          ps[15] = pi + 2;\n          ps[8] = ps[1];\n          ps[11] = pi + 3;\n          ps[4] = ps[2];\n          ps[7] = pi + 4;\n          ps[0] = ps[3];\n          ps[1] = pi + 7;\n          ps[2] = pi + 6;\n          ps[3] = pi + 5;\n          cs[2] = cs[0];\n          cs[3] = ci;\n          cs[0] = cs[1];\n          cs[1] = ci + 1;\n          break;\n      }\n      ps[5] = coords.length;\n      coords.push([(-4 * coords[ps[0]][0] - coords[ps[15]][0] + 6 * (coords[ps[4]][0] + coords[ps[1]][0]) - 2 * (coords[ps[12]][0] + coords[ps[3]][0]) + 3 * (coords[ps[13]][0] + coords[ps[7]][0])) / 9, (-4 * coords[ps[0]][1] - coords[ps[15]][1] + 6 * (coords[ps[4]][1] + coords[ps[1]][1]) - 2 * (coords[ps[12]][1] + coords[ps[3]][1]) + 3 * (coords[ps[13]][1] + coords[ps[7]][1])) / 9]);\n      ps[6] = coords.length;\n      coords.push([(-4 * coords[ps[3]][0] - coords[ps[12]][0] + 6 * (coords[ps[2]][0] + coords[ps[7]][0]) - 2 * (coords[ps[0]][0] + coords[ps[15]][0]) + 3 * (coords[ps[4]][0] + coords[ps[14]][0])) / 9, (-4 * coords[ps[3]][1] - coords[ps[12]][1] + 6 * (coords[ps[2]][1] + coords[ps[7]][1]) - 2 * (coords[ps[0]][1] + coords[ps[15]][1]) + 3 * (coords[ps[4]][1] + coords[ps[14]][1])) / 9]);\n      ps[9] = coords.length;\n      coords.push([(-4 * coords[ps[12]][0] - coords[ps[3]][0] + 6 * (coords[ps[8]][0] + coords[ps[13]][0]) - 2 * (coords[ps[0]][0] + coords[ps[15]][0]) + 3 * (coords[ps[11]][0] + coords[ps[1]][0])) / 9, (-4 * coords[ps[12]][1] - coords[ps[3]][1] + 6 * (coords[ps[8]][1] + coords[ps[13]][1]) - 2 * (coords[ps[0]][1] + coords[ps[15]][1]) + 3 * (coords[ps[11]][1] + coords[ps[1]][1])) / 9]);\n      ps[10] = coords.length;\n      coords.push([(-4 * coords[ps[15]][0] - coords[ps[0]][0] + 6 * (coords[ps[11]][0] + coords[ps[14]][0]) - 2 * (coords[ps[12]][0] + coords[ps[3]][0]) + 3 * (coords[ps[2]][0] + coords[ps[8]][0])) / 9, (-4 * coords[ps[15]][1] - coords[ps[0]][1] + 6 * (coords[ps[11]][1] + coords[ps[14]][1]) - 2 * (coords[ps[12]][1] + coords[ps[3]][1]) + 3 * (coords[ps[2]][1] + coords[ps[8]][1])) / 9]);\n      this.figures.push({\n        type: \"patch\",\n        coords: new Int32Array(ps),\n        colors: new Int32Array(cs)\n      });\n    }\n  }\n  _decodeType7Shading(reader) {\n    const coords = this.coords;\n    const colors = this.colors;\n    const ps = new Int32Array(16);\n    const cs = new Int32Array(4);\n    while (reader.hasData) {\n      const f = reader.readFlag();\n      if (!(0 <= f && f <= 3)) {\n        throw new FormatError(\"Unknown type7 flag\");\n      }\n      const pi = coords.length;\n      for (let i = 0, ii = f !== 0 ? 12 : 16; i < ii; i++) {\n        coords.push(reader.readCoordinate());\n      }\n      const ci = colors.length;\n      for (let i = 0, ii = f !== 0 ? 2 : 4; i < ii; i++) {\n        colors.push(reader.readComponents());\n      }\n      let tmp1, tmp2, tmp3, tmp4;\n      switch (f) {\n        case 0:\n          ps[12] = pi + 3;\n          ps[13] = pi + 4;\n          ps[14] = pi + 5;\n          ps[15] = pi + 6;\n          ps[8] = pi + 2;\n          ps[9] = pi + 13;\n          ps[10] = pi + 14;\n          ps[11] = pi + 7;\n          ps[4] = pi + 1;\n          ps[5] = pi + 12;\n          ps[6] = pi + 15;\n          ps[7] = pi + 8;\n          ps[0] = pi;\n          ps[1] = pi + 11;\n          ps[2] = pi + 10;\n          ps[3] = pi + 9;\n          cs[2] = ci + 1;\n          cs[3] = ci + 2;\n          cs[0] = ci;\n          cs[1] = ci + 3;\n          break;\n        case 1:\n          tmp1 = ps[12];\n          tmp2 = ps[13];\n          tmp3 = ps[14];\n          tmp4 = ps[15];\n          ps[12] = tmp4;\n          ps[13] = pi + 0;\n          ps[14] = pi + 1;\n          ps[15] = pi + 2;\n          ps[8] = tmp3;\n          ps[9] = pi + 9;\n          ps[10] = pi + 10;\n          ps[11] = pi + 3;\n          ps[4] = tmp2;\n          ps[5] = pi + 8;\n          ps[6] = pi + 11;\n          ps[7] = pi + 4;\n          ps[0] = tmp1;\n          ps[1] = pi + 7;\n          ps[2] = pi + 6;\n          ps[3] = pi + 5;\n          tmp1 = cs[2];\n          tmp2 = cs[3];\n          cs[2] = tmp2;\n          cs[3] = ci;\n          cs[0] = tmp1;\n          cs[1] = ci + 1;\n          break;\n        case 2:\n          tmp1 = ps[15];\n          tmp2 = ps[11];\n          ps[12] = ps[3];\n          ps[13] = pi + 0;\n          ps[14] = pi + 1;\n          ps[15] = pi + 2;\n          ps[8] = ps[7];\n          ps[9] = pi + 9;\n          ps[10] = pi + 10;\n          ps[11] = pi + 3;\n          ps[4] = tmp2;\n          ps[5] = pi + 8;\n          ps[6] = pi + 11;\n          ps[7] = pi + 4;\n          ps[0] = tmp1;\n          ps[1] = pi + 7;\n          ps[2] = pi + 6;\n          ps[3] = pi + 5;\n          tmp1 = cs[3];\n          cs[2] = cs[1];\n          cs[3] = ci;\n          cs[0] = tmp1;\n          cs[1] = ci + 1;\n          break;\n        case 3:\n          ps[12] = ps[0];\n          ps[13] = pi + 0;\n          ps[14] = pi + 1;\n          ps[15] = pi + 2;\n          ps[8] = ps[1];\n          ps[9] = pi + 9;\n          ps[10] = pi + 10;\n          ps[11] = pi + 3;\n          ps[4] = ps[2];\n          ps[5] = pi + 8;\n          ps[6] = pi + 11;\n          ps[7] = pi + 4;\n          ps[0] = ps[3];\n          ps[1] = pi + 7;\n          ps[2] = pi + 6;\n          ps[3] = pi + 5;\n          cs[2] = cs[0];\n          cs[3] = ci;\n          cs[0] = cs[1];\n          cs[1] = ci + 1;\n          break;\n      }\n      this.figures.push({\n        type: \"patch\",\n        coords: new Int32Array(ps),\n        colors: new Int32Array(cs)\n      });\n    }\n  }\n  _buildFigureFromPatch(index) {\n    const figure = this.figures[index];\n    assert(figure.type === \"patch\", \"Unexpected patch mesh figure\");\n    const coords = this.coords,\n      colors = this.colors;\n    const pi = figure.coords;\n    const ci = figure.colors;\n    const figureMinX = Math.min(coords[pi[0]][0], coords[pi[3]][0], coords[pi[12]][0], coords[pi[15]][0]);\n    const figureMinY = Math.min(coords[pi[0]][1], coords[pi[3]][1], coords[pi[12]][1], coords[pi[15]][1]);\n    const figureMaxX = Math.max(coords[pi[0]][0], coords[pi[3]][0], coords[pi[12]][0], coords[pi[15]][0]);\n    const figureMaxY = Math.max(coords[pi[0]][1], coords[pi[3]][1], coords[pi[12]][1], coords[pi[15]][1]);\n    let splitXBy = Math.ceil((figureMaxX - figureMinX) * MeshShading.TRIANGLE_DENSITY / (this.bounds[2] - this.bounds[0]));\n    splitXBy = Math.max(MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT, Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitXBy));\n    let splitYBy = Math.ceil((figureMaxY - figureMinY) * MeshShading.TRIANGLE_DENSITY / (this.bounds[3] - this.bounds[1]));\n    splitYBy = Math.max(MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT, Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitYBy));\n    const verticesPerRow = splitXBy + 1;\n    const figureCoords = new Int32Array((splitYBy + 1) * verticesPerRow);\n    const figureColors = new Int32Array((splitYBy + 1) * verticesPerRow);\n    let k = 0;\n    const cl = new Uint8Array(3),\n      cr = new Uint8Array(3);\n    const c0 = colors[ci[0]],\n      c1 = colors[ci[1]],\n      c2 = colors[ci[2]],\n      c3 = colors[ci[3]];\n    const bRow = getB(splitYBy),\n      bCol = getB(splitXBy);\n    for (let row = 0; row <= splitYBy; row++) {\n      cl[0] = (c0[0] * (splitYBy - row) + c2[0] * row) / splitYBy | 0;\n      cl[1] = (c0[1] * (splitYBy - row) + c2[1] * row) / splitYBy | 0;\n      cl[2] = (c0[2] * (splitYBy - row) + c2[2] * row) / splitYBy | 0;\n      cr[0] = (c1[0] * (splitYBy - row) + c3[0] * row) / splitYBy | 0;\n      cr[1] = (c1[1] * (splitYBy - row) + c3[1] * row) / splitYBy | 0;\n      cr[2] = (c1[2] * (splitYBy - row) + c3[2] * row) / splitYBy | 0;\n      for (let col = 0; col <= splitXBy; col++, k++) {\n        if ((row === 0 || row === splitYBy) && (col === 0 || col === splitXBy)) {\n          continue;\n        }\n        let x = 0,\n          y = 0;\n        let q = 0;\n        for (let i = 0; i <= 3; i++) {\n          for (let j = 0; j <= 3; j++, q++) {\n            const m = bRow[row][i] * bCol[col][j];\n            x += coords[pi[q]][0] * m;\n            y += coords[pi[q]][1] * m;\n          }\n        }\n        figureCoords[k] = coords.length;\n        coords.push([x, y]);\n        figureColors[k] = colors.length;\n        const newColor = new Uint8Array(3);\n        newColor[0] = (cl[0] * (splitXBy - col) + cr[0] * col) / splitXBy | 0;\n        newColor[1] = (cl[1] * (splitXBy - col) + cr[1] * col) / splitXBy | 0;\n        newColor[2] = (cl[2] * (splitXBy - col) + cr[2] * col) / splitXBy | 0;\n        colors.push(newColor);\n      }\n    }\n    figureCoords[0] = pi[0];\n    figureColors[0] = ci[0];\n    figureCoords[splitXBy] = pi[3];\n    figureColors[splitXBy] = ci[1];\n    figureCoords[verticesPerRow * splitYBy] = pi[12];\n    figureColors[verticesPerRow * splitYBy] = ci[2];\n    figureCoords[verticesPerRow * splitYBy + splitXBy] = pi[15];\n    figureColors[verticesPerRow * splitYBy + splitXBy] = ci[3];\n    this.figures[index] = {\n      type: \"lattice\",\n      coords: figureCoords,\n      colors: figureColors,\n      verticesPerRow\n    };\n  }\n  _updateBounds() {\n    let minX = this.coords[0][0],\n      minY = this.coords[0][1],\n      maxX = minX,\n      maxY = minY;\n    for (let i = 1, ii = this.coords.length; i < ii; i++) {\n      const x = this.coords[i][0],\n        y = this.coords[i][1];\n      minX = minX > x ? x : minX;\n      minY = minY > y ? y : minY;\n      maxX = maxX < x ? x : maxX;\n      maxY = maxY < y ? y : maxY;\n    }\n    this.bounds = [minX, minY, maxX, maxY];\n  }\n  _packData() {\n    let i, ii, j, jj;\n    const coords = this.coords;\n    const coordsPacked = new Float32Array(coords.length * 2);\n    for (i = 0, j = 0, ii = coords.length; i < ii; i++) {\n      const xy = coords[i];\n      coordsPacked[j++] = xy[0];\n      coordsPacked[j++] = xy[1];\n    }\n    this.coords = coordsPacked;\n    const colors = this.colors;\n    const colorsPacked = new Uint8Array(colors.length * 3);\n    for (i = 0, j = 0, ii = colors.length; i < ii; i++) {\n      const c = colors[i];\n      colorsPacked[j++] = c[0];\n      colorsPacked[j++] = c[1];\n      colorsPacked[j++] = c[2];\n    }\n    this.colors = colorsPacked;\n    const figures = this.figures;\n    for (i = 0, ii = figures.length; i < ii; i++) {\n      const figure = figures[i],\n        ps = figure.coords,\n        cs = figure.colors;\n      for (j = 0, jj = ps.length; j < jj; j++) {\n        ps[j] *= 2;\n        cs[j] *= 3;\n      }\n    }\n  }\n  getIR() {\n    return [\"Mesh\", this.shadingType, this.coords, this.colors, this.figures, this.bounds, this.bbox, this.background];\n  }\n}\nclass DummyShading extends BaseShading {\n  getIR() {\n    return [\"Dummy\"];\n  }\n}\nfunction getTilingPatternIR(operatorList, dict, color) {\n  const matrix = dict.getArray(\"Matrix\");\n  const bbox = Util.normalizeRect(dict.getArray(\"BBox\"));\n  const xstep = dict.get(\"XStep\");\n  const ystep = dict.get(\"YStep\");\n  const paintType = dict.get(\"PaintType\");\n  const tilingType = dict.get(\"TilingType\");\n  if (bbox[2] - bbox[0] === 0 || bbox[3] - bbox[1] === 0) {\n    throw new FormatError(`Invalid getTilingPatternIR /BBox array: [${bbox}].`);\n  }\n  return [\"TilingPattern\", color, operatorList, matrix, bbox, xstep, ystep, paintType, tilingType];\n}\n\n;// CONCATENATED MODULE: ./src/core/calibri_factors.js\nconst CalibriBoldFactors = [1.3877, 1, 1, 1, 0.97801, 0.92482, 0.89552, 0.91133, 0.81988, 0.97566, 0.98152, 0.93548, 0.93548, 1.2798, 0.85284, 0.92794, 1, 0.96134, 1.54657, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.82845, 0.82845, 0.85284, 0.85284, 0.85284, 0.75859, 0.92138, 0.83908, 0.7762, 0.73293, 0.87289, 0.73133, 0.7514, 0.81921, 0.87356, 0.95958, 0.59526, 0.75727, 0.69225, 1.04924, 0.9121, 0.86943, 0.79795, 0.88198, 0.77958, 0.70864, 0.81055, 0.90399, 0.88653, 0.96017, 0.82577, 0.77892, 0.78257, 0.97507, 1.54657, 0.97507, 0.85284, 0.89552, 0.90176, 0.88762, 0.8785, 0.75241, 0.8785, 0.90518, 0.95015, 0.77618, 0.8785, 0.88401, 0.91916, 0.86304, 0.88401, 0.91488, 0.8785, 0.8801, 0.8785, 0.8785, 0.91343, 0.7173, 1.04106, 0.8785, 0.85075, 0.95794, 0.82616, 0.85162, 0.79492, 0.88331, 1.69808, 0.88331, 0.85284, 0.97801, 0.89552, 0.91133, 0.89552, 0.91133, 1.7801, 0.89552, 1.24487, 1.13254, 1.12401, 0.96839, 0.85284, 0.68787, 0.70645, 0.85592, 0.90747, 1.01466, 1.0088, 0.90323, 1, 1.07463, 1, 0.91056, 0.75806, 1.19118, 0.96839, 0.78864, 0.82845, 0.84133, 0.75859, 0.83908, 0.83908, 0.83908, 0.83908, 0.83908, 0.83908, 0.77539, 0.73293, 0.73133, 0.73133, 0.73133, 0.73133, 0.95958, 0.95958, 0.95958, 0.95958, 0.88506, 0.9121, 0.86943, 0.86943, 0.86943, 0.86943, 0.86943, 0.85284, 0.87508, 0.90399, 0.90399, 0.90399, 0.90399, 0.77892, 0.79795, 0.90807, 0.88762, 0.88762, 0.88762, 0.88762, 0.88762, 0.88762, 0.8715, 0.75241, 0.90518, 0.90518, 0.90518, 0.90518, 0.88401, 0.88401, 0.88401, 0.88401, 0.8785, 0.8785, 0.8801, 0.8801, 0.8801, 0.8801, 0.8801, 0.90747, 0.89049, 0.8785, 0.8785, 0.8785, 0.8785, 0.85162, 0.8785, 0.85162, 0.83908, 0.88762, 0.83908, 0.88762, 0.83908, 0.88762, 0.73293, 0.75241, 0.73293, 0.75241, 0.73293, 0.75241, 0.73293, 0.75241, 0.87289, 0.83016, 0.88506, 0.93125, 0.73133, 0.90518, 0.73133, 0.90518, 0.73133, 0.90518, 0.73133, 0.90518, 0.73133, 0.90518, 0.81921, 0.77618, 0.81921, 0.77618, 0.81921, 0.77618, 1, 1, 0.87356, 0.8785, 0.91075, 0.89608, 0.95958, 0.88401, 0.95958, 0.88401, 0.95958, 0.88401, 0.95958, 0.88401, 0.95958, 0.88401, 0.76229, 0.90167, 0.59526, 0.91916, 1, 1, 0.86304, 0.69225, 0.88401, 1, 1, 0.70424, 0.79468, 0.91926, 0.88175, 0.70823, 0.94903, 0.9121, 0.8785, 1, 1, 0.9121, 0.8785, 0.87802, 0.88656, 0.8785, 0.86943, 0.8801, 0.86943, 0.8801, 0.86943, 0.8801, 0.87402, 0.89291, 0.77958, 0.91343, 1, 1, 0.77958, 0.91343, 0.70864, 0.7173, 0.70864, 0.7173, 0.70864, 0.7173, 0.70864, 0.7173, 1, 1, 0.81055, 0.75841, 0.81055, 1.06452, 0.90399, 0.8785, 0.90399, 0.8785, 0.90399, 0.8785, 0.90399, 0.8785, 0.90399, 0.8785, 0.90399, 0.8785, 0.96017, 0.95794, 0.77892, 0.85162, 0.77892, 0.78257, 0.79492, 0.78257, 0.79492, 0.78257, 0.79492, 0.9297, 0.56892, 0.83908, 0.88762, 0.77539, 0.8715, 0.87508, 0.89049, 1, 1, 0.81055, 1.04106, 1.20528, 1.20528, 1, 1.15543, 0.70674, 0.98387, 0.94721, 1.33431, 1.45894, 0.95161, 1.06303, 0.83908, 0.80352, 0.57184, 0.6965, 0.56289, 0.82001, 0.56029, 0.81235, 1.02988, 0.83908, 0.7762, 0.68156, 0.80367, 0.73133, 0.78257, 0.87356, 0.86943, 0.95958, 0.75727, 0.89019, 1.04924, 0.9121, 0.7648, 0.86943, 0.87356, 0.79795, 0.78275, 0.81055, 0.77892, 0.9762, 0.82577, 0.99819, 0.84896, 0.95958, 0.77892, 0.96108, 1.01407, 0.89049, 1.02988, 0.94211, 0.96108, 0.8936, 0.84021, 0.87842, 0.96399, 0.79109, 0.89049, 1.00813, 1.02988, 0.86077, 0.87445, 0.92099, 0.84723, 0.86513, 0.8801, 0.75638, 0.85714, 0.78216, 0.79586, 0.87965, 0.94211, 0.97747, 0.78287, 0.97926, 0.84971, 1.02988, 0.94211, 0.8801, 0.94211, 0.84971, 0.73133, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90264, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90518, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90548, 1, 1, 1, 1, 1, 1, 0.96017, 0.95794, 0.96017, 0.95794, 0.96017, 0.95794, 0.77892, 0.85162, 1, 1, 0.89552, 0.90527, 1, 0.90363, 0.92794, 0.92794, 0.92794, 0.92794, 0.87012, 0.87012, 0.87012, 0.89552, 0.89552, 1.42259, 0.71143, 1.06152, 1, 1, 1.03372, 1.03372, 0.97171, 1.4956, 2.2807, 0.93835, 0.83406, 0.91133, 0.84107, 0.91133, 1, 1, 1, 0.72021, 1, 1.23108, 0.83489, 0.88525, 0.88525, 0.81499, 0.90527, 1.81055, 0.90527, 1.81055, 1.31006, 1.53711, 0.94434, 1.08696, 1, 0.95018, 0.77192, 0.85284, 0.90747, 1.17534, 0.69825, 0.9716, 1.37077, 0.90747, 0.90747, 0.85356, 0.90747, 0.90747, 1.44947, 0.85284, 0.8941, 0.8941, 0.70572, 0.8, 0.70572, 0.70572, 0.70572, 0.70572, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.99862, 0.99862, 1, 1, 1, 1, 1, 1.08004, 0.91027, 1, 1, 1, 0.99862, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90727, 0.90727, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst CalibriBoldMetrics = {\n  lineHeight: 1.2207,\n  lineGap: 0.2207\n};\nconst CalibriBoldItalicFactors = [1.3877, 1, 1, 1, 0.97801, 0.92482, 0.89552, 0.91133, 0.81988, 0.97566, 0.98152, 0.93548, 0.93548, 1.2798, 0.85284, 0.92794, 1, 0.96134, 1.56239, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.82845, 0.82845, 0.85284, 0.85284, 0.85284, 0.75859, 0.92138, 0.83908, 0.7762, 0.71805, 0.87289, 0.73133, 0.7514, 0.81921, 0.87356, 0.95958, 0.59526, 0.75727, 0.69225, 1.04924, 0.90872, 0.85938, 0.79795, 0.87068, 0.77958, 0.69766, 0.81055, 0.90399, 0.88653, 0.96068, 0.82577, 0.77892, 0.78257, 0.97507, 1.529, 0.97507, 0.85284, 0.89552, 0.90176, 0.94908, 0.86411, 0.74012, 0.86411, 0.88323, 0.95015, 0.86411, 0.86331, 0.88401, 0.91916, 0.86304, 0.88401, 0.9039, 0.86331, 0.86331, 0.86411, 0.86411, 0.90464, 0.70852, 1.04106, 0.86331, 0.84372, 0.95794, 0.82616, 0.84548, 0.79492, 0.88331, 1.69808, 0.88331, 0.85284, 0.97801, 0.89552, 0.91133, 0.89552, 0.91133, 1.7801, 0.89552, 1.24487, 1.13254, 1.19129, 0.96839, 0.85284, 0.68787, 0.70645, 0.85592, 0.90747, 1.01466, 1.0088, 0.90323, 1, 1.07463, 1, 0.91056, 0.75806, 1.19118, 0.96839, 0.78864, 0.82845, 0.84133, 0.75859, 0.83908, 0.83908, 0.83908, 0.83908, 0.83908, 0.83908, 0.77539, 0.71805, 0.73133, 0.73133, 0.73133, 0.73133, 0.95958, 0.95958, 0.95958, 0.95958, 0.88506, 0.90872, 0.85938, 0.85938, 0.85938, 0.85938, 0.85938, 0.85284, 0.87068, 0.90399, 0.90399, 0.90399, 0.90399, 0.77892, 0.79795, 0.90807, 0.94908, 0.94908, 0.94908, 0.94908, 0.94908, 0.94908, 0.85887, 0.74012, 0.88323, 0.88323, 0.88323, 0.88323, 0.88401, 0.88401, 0.88401, 0.88401, 0.8785, 0.86331, 0.86331, 0.86331, 0.86331, 0.86331, 0.86331, 0.90747, 0.89049, 0.86331, 0.86331, 0.86331, 0.86331, 0.84548, 0.86411, 0.84548, 0.83908, 0.94908, 0.83908, 0.94908, 0.83908, 0.94908, 0.71805, 0.74012, 0.71805, 0.74012, 0.71805, 0.74012, 0.71805, 0.74012, 0.87289, 0.79538, 0.88506, 0.92726, 0.73133, 0.88323, 0.73133, 0.88323, 0.73133, 0.88323, 0.73133, 0.88323, 0.73133, 0.88323, 0.81921, 0.86411, 0.81921, 0.86411, 0.81921, 0.86411, 1, 1, 0.87356, 0.86331, 0.91075, 0.8777, 0.95958, 0.88401, 0.95958, 0.88401, 0.95958, 0.88401, 0.95958, 0.88401, 0.95958, 0.88401, 0.76467, 0.90167, 0.59526, 0.91916, 1, 1, 0.86304, 0.69225, 0.88401, 1, 1, 0.70424, 0.77312, 0.91926, 0.88175, 0.70823, 0.94903, 0.90872, 0.86331, 1, 1, 0.90872, 0.86331, 0.86906, 0.88116, 0.86331, 0.85938, 0.86331, 0.85938, 0.86331, 0.85938, 0.86331, 0.87402, 0.86549, 0.77958, 0.90464, 1, 1, 0.77958, 0.90464, 0.69766, 0.70852, 0.69766, 0.70852, 0.69766, 0.70852, 0.69766, 0.70852, 1, 1, 0.81055, 0.75841, 0.81055, 1.06452, 0.90399, 0.86331, 0.90399, 0.86331, 0.90399, 0.86331, 0.90399, 0.86331, 0.90399, 0.86331, 0.90399, 0.86331, 0.96068, 0.95794, 0.77892, 0.84548, 0.77892, 0.78257, 0.79492, 0.78257, 0.79492, 0.78257, 0.79492, 0.9297, 0.56892, 0.83908, 0.94908, 0.77539, 0.85887, 0.87068, 0.89049, 1, 1, 0.81055, 1.04106, 1.20528, 1.20528, 1, 1.15543, 0.70088, 0.98387, 0.94721, 1.33431, 1.45894, 0.95161, 1.48387, 0.83908, 0.80352, 0.57118, 0.6965, 0.56347, 0.79179, 0.55853, 0.80346, 1.02988, 0.83908, 0.7762, 0.67174, 0.86036, 0.73133, 0.78257, 0.87356, 0.86441, 0.95958, 0.75727, 0.89019, 1.04924, 0.90872, 0.74889, 0.85938, 0.87891, 0.79795, 0.7957, 0.81055, 0.77892, 0.97447, 0.82577, 0.97466, 0.87179, 0.95958, 0.77892, 0.94252, 0.95612, 0.8753, 1.02988, 0.92733, 0.94252, 0.87411, 0.84021, 0.8728, 0.95612, 0.74081, 0.8753, 1.02189, 1.02988, 0.84814, 0.87445, 0.91822, 0.84723, 0.85668, 0.86331, 0.81344, 0.87581, 0.76422, 0.82046, 0.96057, 0.92733, 0.99375, 0.78022, 0.95452, 0.86015, 1.02988, 0.92733, 0.86331, 0.92733, 0.86015, 0.73133, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90631, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.88323, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.85174, 1, 1, 1, 1, 1, 1, 0.96068, 0.95794, 0.96068, 0.95794, 0.96068, 0.95794, 0.77892, 0.84548, 1, 1, 0.89552, 0.90527, 1, 0.90363, 0.92794, 0.92794, 0.92794, 0.89807, 0.87012, 0.87012, 0.87012, 0.89552, 0.89552, 1.42259, 0.71094, 1.06152, 1, 1, 1.03372, 1.03372, 0.97171, 1.4956, 2.2807, 0.92972, 0.83406, 0.91133, 0.83326, 0.91133, 1, 1, 1, 0.72021, 1, 1.23108, 0.83489, 0.88525, 0.88525, 0.81499, 0.90616, 1.81055, 0.90527, 1.81055, 1.3107, 1.53711, 0.94434, 1.08696, 1, 0.95018, 0.77192, 0.85284, 0.90747, 1.17534, 0.69825, 0.9716, 1.37077, 0.90747, 0.90747, 0.85356, 0.90747, 0.90747, 1.44947, 0.85284, 0.8941, 0.8941, 0.70572, 0.8, 0.70572, 0.70572, 0.70572, 0.70572, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.99862, 0.99862, 1, 1, 1, 1, 1, 1.08004, 0.91027, 1, 1, 1, 0.99862, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90727, 0.90727, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst CalibriBoldItalicMetrics = {\n  lineHeight: 1.2207,\n  lineGap: 0.2207\n};\nconst CalibriItalicFactors = [1.3877, 1, 1, 1, 1.17223, 1.1293, 0.89552, 0.91133, 0.80395, 1.02269, 1.15601, 0.91056, 0.91056, 1.2798, 0.85284, 0.89807, 1, 0.90861, 1.39543, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.96309, 0.96309, 0.85284, 0.85284, 0.85284, 0.83319, 0.88071, 0.8675, 0.81552, 0.72346, 0.85193, 0.73206, 0.7522, 0.81105, 0.86275, 0.90685, 0.6377, 0.77892, 0.75593, 1.02638, 0.89249, 0.84118, 0.77452, 0.85374, 0.75186, 0.67789, 0.79776, 0.88844, 0.85066, 0.94309, 0.77818, 0.7306, 0.76659, 1.10369, 1.38313, 1.10369, 1.06139, 0.89552, 0.8739, 0.9245, 0.9245, 0.83203, 0.9245, 0.85865, 1.09842, 0.9245, 0.9245, 1.03297, 1.07692, 0.90918, 1.03297, 0.94959, 0.9245, 0.92274, 0.9245, 0.9245, 1.02933, 0.77832, 1.20562, 0.9245, 0.8916, 0.98986, 0.86621, 0.89453, 0.79004, 0.94152, 1.77256, 0.94152, 0.85284, 0.97801, 0.89552, 0.91133, 0.89552, 0.91133, 1.91729, 0.89552, 1.17889, 1.13254, 1.16359, 0.92098, 0.85284, 0.68787, 0.71353, 0.84737, 0.90747, 1.0088, 1.0044, 0.87683, 1, 1.09091, 1, 0.92229, 0.739, 1.15642, 0.92098, 0.76288, 0.80504, 0.80972, 0.75859, 0.8675, 0.8675, 0.8675, 0.8675, 0.8675, 0.8675, 0.76318, 0.72346, 0.73206, 0.73206, 0.73206, 0.73206, 0.90685, 0.90685, 0.90685, 0.90685, 0.86477, 0.89249, 0.84118, 0.84118, 0.84118, 0.84118, 0.84118, 0.85284, 0.84557, 0.88844, 0.88844, 0.88844, 0.88844, 0.7306, 0.77452, 0.86331, 0.9245, 0.9245, 0.9245, 0.9245, 0.9245, 0.9245, 0.84843, 0.83203, 0.85865, 0.85865, 0.85865, 0.85865, 0.82601, 0.82601, 0.82601, 0.82601, 0.94469, 0.9245, 0.92274, 0.92274, 0.92274, 0.92274, 0.92274, 0.90747, 0.86651, 0.9245, 0.9245, 0.9245, 0.9245, 0.89453, 0.9245, 0.89453, 0.8675, 0.9245, 0.8675, 0.9245, 0.8675, 0.9245, 0.72346, 0.83203, 0.72346, 0.83203, 0.72346, 0.83203, 0.72346, 0.83203, 0.85193, 0.8875, 0.86477, 0.99034, 0.73206, 0.85865, 0.73206, 0.85865, 0.73206, 0.85865, 0.73206, 0.85865, 0.73206, 0.85865, 0.81105, 0.9245, 0.81105, 0.9245, 0.81105, 0.9245, 1, 1, 0.86275, 0.9245, 0.90872, 0.93591, 0.90685, 0.82601, 0.90685, 0.82601, 0.90685, 0.82601, 0.90685, 1.03297, 0.90685, 0.82601, 0.77896, 1.05611, 0.6377, 1.07692, 1, 1, 0.90918, 0.75593, 1.03297, 1, 1, 0.76032, 0.9375, 0.98156, 0.93407, 0.77261, 1.11429, 0.89249, 0.9245, 1, 1, 0.89249, 0.9245, 0.92534, 0.86698, 0.9245, 0.84118, 0.92274, 0.84118, 0.92274, 0.84118, 0.92274, 0.8667, 0.86291, 0.75186, 1.02933, 1, 1, 0.75186, 1.02933, 0.67789, 0.77832, 0.67789, 0.77832, 0.67789, 0.77832, 0.67789, 0.77832, 1, 1, 0.79776, 0.97655, 0.79776, 1.23023, 0.88844, 0.9245, 0.88844, 0.9245, 0.88844, 0.9245, 0.88844, 0.9245, 0.88844, 0.9245, 0.88844, 0.9245, 0.94309, 0.98986, 0.7306, 0.89453, 0.7306, 0.76659, 0.79004, 0.76659, 0.79004, 0.76659, 0.79004, 1.09231, 0.54873, 0.8675, 0.9245, 0.76318, 0.84843, 0.84557, 0.86651, 1, 1, 0.79776, 1.20562, 1.18622, 1.18622, 1, 1.1437, 0.67009, 0.96334, 0.93695, 1.35191, 1.40909, 0.95161, 1.48387, 0.8675, 0.90861, 0.6192, 0.7363, 0.64824, 0.82411, 0.56321, 0.85696, 1.23516, 0.8675, 0.81552, 0.7286, 0.84134, 0.73206, 0.76659, 0.86275, 0.84369, 0.90685, 0.77892, 0.85871, 1.02638, 0.89249, 0.75828, 0.84118, 0.85984, 0.77452, 0.76466, 0.79776, 0.7306, 0.90782, 0.77818, 0.903, 0.87291, 0.90685, 0.7306, 0.99058, 1.03667, 0.94635, 1.23516, 0.9849, 0.99058, 0.92393, 0.8916, 0.942, 1.03667, 0.75026, 0.94635, 1.0297, 1.23516, 0.90918, 0.94048, 0.98217, 0.89746, 0.84153, 0.92274, 0.82507, 0.88832, 0.84438, 0.88178, 1.03525, 0.9849, 1.00225, 0.78086, 0.97248, 0.89404, 1.23516, 0.9849, 0.92274, 0.9849, 0.89404, 0.73206, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.89693, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.85865, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90933, 1, 1, 1, 1, 1, 1, 0.94309, 0.98986, 0.94309, 0.98986, 0.94309, 0.98986, 0.7306, 0.89453, 1, 1, 0.89552, 0.90527, 1, 0.90186, 1.12308, 1.12308, 1.12308, 1.12308, 1.2566, 1.2566, 1.2566, 0.89552, 0.89552, 1.42259, 0.68994, 1.03809, 1, 1, 1.0176, 1.0176, 1.11523, 1.4956, 2.01462, 0.97858, 0.82616, 0.91133, 0.83437, 0.91133, 1, 1, 1, 0.70508, 1, 1.23108, 0.79801, 0.84426, 0.84426, 0.774, 0.90572, 1.81055, 0.90749, 1.81055, 1.28809, 1.55469, 0.94434, 1.07806, 1, 0.97094, 0.7589, 0.85284, 0.90747, 1.19658, 0.69825, 0.97622, 1.33512, 0.90747, 0.90747, 0.85284, 0.90747, 0.90747, 1.44947, 0.85284, 0.8941, 0.8941, 0.70572, 0.8, 0.70572, 0.70572, 0.70572, 0.70572, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.99862, 0.99862, 1, 1, 1, 1, 1, 1.0336, 0.91027, 1, 1, 1, 0.99862, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.05859, 1.05859, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst CalibriItalicMetrics = {\n  lineHeight: 1.2207,\n  lineGap: 0.2207\n};\nconst CalibriRegularFactors = [1.3877, 1, 1, 1, 1.17223, 1.1293, 0.89552, 0.91133, 0.80395, 1.02269, 1.15601, 0.91056, 0.91056, 1.2798, 0.85284, 0.89807, 1, 0.90861, 1.39016, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.96309, 0.96309, 0.85284, 0.85284, 0.85284, 0.83319, 0.88071, 0.8675, 0.81552, 0.73834, 0.85193, 0.73206, 0.7522, 0.81105, 0.86275, 0.90685, 0.6377, 0.77892, 0.75593, 1.02638, 0.89385, 0.85122, 0.77452, 0.86503, 0.75186, 0.68887, 0.79776, 0.88844, 0.85066, 0.94258, 0.77818, 0.7306, 0.76659, 1.10369, 1.39016, 1.10369, 1.06139, 0.89552, 0.8739, 0.86128, 0.94469, 0.8457, 0.94469, 0.89464, 1.09842, 0.84636, 0.94469, 1.03297, 1.07692, 0.90918, 1.03297, 0.95897, 0.94469, 0.9482, 0.94469, 0.94469, 1.04692, 0.78223, 1.20562, 0.94469, 0.90332, 0.98986, 0.86621, 0.90527, 0.79004, 0.94152, 1.77256, 0.94152, 0.85284, 0.97801, 0.89552, 0.91133, 0.89552, 0.91133, 1.91729, 0.89552, 1.17889, 1.13254, 1.08707, 0.92098, 0.85284, 0.68787, 0.71353, 0.84737, 0.90747, 1.0088, 1.0044, 0.87683, 1, 1.09091, 1, 0.92229, 0.739, 1.15642, 0.92098, 0.76288, 0.80504, 0.80972, 0.75859, 0.8675, 0.8675, 0.8675, 0.8675, 0.8675, 0.8675, 0.76318, 0.73834, 0.73206, 0.73206, 0.73206, 0.73206, 0.90685, 0.90685, 0.90685, 0.90685, 0.86477, 0.89385, 0.85122, 0.85122, 0.85122, 0.85122, 0.85122, 0.85284, 0.85311, 0.88844, 0.88844, 0.88844, 0.88844, 0.7306, 0.77452, 0.86331, 0.86128, 0.86128, 0.86128, 0.86128, 0.86128, 0.86128, 0.8693, 0.8457, 0.89464, 0.89464, 0.89464, 0.89464, 0.82601, 0.82601, 0.82601, 0.82601, 0.94469, 0.94469, 0.9482, 0.9482, 0.9482, 0.9482, 0.9482, 0.90747, 0.86651, 0.94469, 0.94469, 0.94469, 0.94469, 0.90527, 0.94469, 0.90527, 0.8675, 0.86128, 0.8675, 0.86128, 0.8675, 0.86128, 0.73834, 0.8457, 0.73834, 0.8457, 0.73834, 0.8457, 0.73834, 0.8457, 0.85193, 0.92454, 0.86477, 0.9921, 0.73206, 0.89464, 0.73206, 0.89464, 0.73206, 0.89464, 0.73206, 0.89464, 0.73206, 0.89464, 0.81105, 0.84636, 0.81105, 0.84636, 0.81105, 0.84636, 1, 1, 0.86275, 0.94469, 0.90872, 0.95786, 0.90685, 0.82601, 0.90685, 0.82601, 0.90685, 0.82601, 0.90685, 1.03297, 0.90685, 0.82601, 0.77741, 1.05611, 0.6377, 1.07692, 1, 1, 0.90918, 0.75593, 1.03297, 1, 1, 0.76032, 0.90452, 0.98156, 1.11842, 0.77261, 1.11429, 0.89385, 0.94469, 1, 1, 0.89385, 0.94469, 0.95877, 0.86901, 0.94469, 0.85122, 0.9482, 0.85122, 0.9482, 0.85122, 0.9482, 0.8667, 0.90016, 0.75186, 1.04692, 1, 1, 0.75186, 1.04692, 0.68887, 0.78223, 0.68887, 0.78223, 0.68887, 0.78223, 0.68887, 0.78223, 1, 1, 0.79776, 0.92188, 0.79776, 1.23023, 0.88844, 0.94469, 0.88844, 0.94469, 0.88844, 0.94469, 0.88844, 0.94469, 0.88844, 0.94469, 0.88844, 0.94469, 0.94258, 0.98986, 0.7306, 0.90527, 0.7306, 0.76659, 0.79004, 0.76659, 0.79004, 0.76659, 0.79004, 1.09231, 0.54873, 0.8675, 0.86128, 0.76318, 0.8693, 0.85311, 0.86651, 1, 1, 0.79776, 1.20562, 1.18622, 1.18622, 1, 1.1437, 0.67742, 0.96334, 0.93695, 1.35191, 1.40909, 0.95161, 1.48387, 0.86686, 0.90861, 0.62267, 0.74359, 0.65649, 0.85498, 0.56963, 0.88254, 1.23516, 0.8675, 0.81552, 0.75443, 0.84503, 0.73206, 0.76659, 0.86275, 0.85122, 0.90685, 0.77892, 0.85746, 1.02638, 0.89385, 0.75657, 0.85122, 0.86275, 0.77452, 0.74171, 0.79776, 0.7306, 0.95165, 0.77818, 0.89772, 0.88831, 0.90685, 0.7306, 0.98142, 1.02191, 0.96576, 1.23516, 0.99018, 0.98142, 0.9236, 0.89258, 0.94035, 1.02191, 0.78848, 0.96576, 0.9561, 1.23516, 0.90918, 0.92578, 0.95424, 0.89746, 0.83969, 0.9482, 0.80113, 0.89442, 0.85208, 0.86155, 0.98022, 0.99018, 1.00452, 0.81209, 0.99247, 0.89181, 1.23516, 0.99018, 0.9482, 0.99018, 0.89181, 0.73206, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.88844, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.89464, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.96766, 1, 1, 1, 1, 1, 1, 0.94258, 0.98986, 0.94258, 0.98986, 0.94258, 0.98986, 0.7306, 0.90527, 1, 1, 0.89552, 0.90527, 1, 0.90186, 1.12308, 1.12308, 1.12308, 1.12308, 1.2566, 1.2566, 1.2566, 0.89552, 0.89552, 1.42259, 0.69043, 1.03809, 1, 1, 1.0176, 1.0176, 1.11523, 1.4956, 2.01462, 0.99331, 0.82616, 0.91133, 0.84286, 0.91133, 1, 1, 1, 0.70508, 1, 1.23108, 0.79801, 0.84426, 0.84426, 0.774, 0.90527, 1.81055, 0.90527, 1.81055, 1.28809, 1.55469, 0.94434, 1.07806, 1, 0.97094, 0.7589, 0.85284, 0.90747, 1.19658, 0.69825, 0.97622, 1.33512, 0.90747, 0.90747, 0.85356, 0.90747, 0.90747, 1.44947, 0.85284, 0.8941, 0.8941, 0.70572, 0.8, 0.70572, 0.70572, 0.70572, 0.70572, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.99862, 0.99862, 1, 1, 1, 1, 1, 1.0336, 0.91027, 1, 1, 1, 0.99862, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.05859, 1.05859, 1, 1, 1, 1.07185, 0.99413, 0.96334, 1.08065, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst CalibriRegularMetrics = {\n  lineHeight: 1.2207,\n  lineGap: 0.2207\n};\n\n;// CONCATENATED MODULE: ./src/core/helvetica_factors.js\nconst HelveticaBoldFactors = [0.76116, 1, 1, 1.0006, 0.99998, 0.99974, 0.99973, 0.99973, 0.99982, 0.99977, 1.00087, 0.99998, 0.99998, 0.99959, 1.00003, 1.0006, 0.99998, 1.0006, 1.0006, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99998, 1, 1.00003, 1.00003, 1.00003, 1.00026, 0.9999, 0.99977, 0.99977, 0.99977, 0.99977, 1.00001, 1.00026, 1.00022, 0.99977, 1.0006, 0.99973, 0.99977, 1.00026, 0.99999, 0.99977, 1.00022, 1.00001, 1.00022, 0.99977, 1.00001, 1.00026, 0.99977, 1.00001, 1.00016, 1.00001, 1.00001, 1.00026, 0.99998, 1.0006, 0.99998, 1.00003, 0.99973, 0.99998, 0.99973, 1.00026, 0.99973, 1.00026, 0.99973, 0.99998, 1.00026, 1.00026, 1.0006, 1.0006, 0.99973, 1.0006, 0.99982, 1.00026, 1.00026, 1.00026, 1.00026, 0.99959, 0.99973, 0.99998, 1.00026, 0.99973, 1.00022, 0.99973, 0.99973, 1, 0.99959, 1.00077, 0.99959, 1.00003, 0.99998, 0.99973, 0.99973, 0.99973, 0.99973, 1.00077, 0.99973, 0.99998, 1.00025, 0.99968, 0.99973, 1.00003, 1.00025, 0.60299, 1.00024, 1.06409, 1, 1, 0.99998, 1, 0.99973, 1.0006, 0.99998, 1, 0.99936, 0.99973, 1.00002, 1.00002, 1.00002, 1.00026, 0.99977, 0.99977, 0.99977, 0.99977, 0.99977, 0.99977, 1, 0.99977, 1.00001, 1.00001, 1.00001, 1.00001, 1.0006, 1.0006, 1.0006, 1.0006, 0.99977, 0.99977, 1.00022, 1.00022, 1.00022, 1.00022, 1.00022, 1.00003, 1.00022, 0.99977, 0.99977, 0.99977, 0.99977, 1.00001, 1.00001, 1.00026, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99982, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1.0006, 1.0006, 1.0006, 1.0006, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 1.06409, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 0.99973, 1.00026, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 1.03374, 0.99977, 1.00026, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00022, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.00042, 0.99973, 0.99973, 1.0006, 0.99977, 0.99973, 0.99973, 1.00026, 1.0006, 1.00026, 1.0006, 1.00026, 1.03828, 1.00026, 0.99999, 1.00026, 1.0006, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.9993, 0.9998, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 1, 1.00016, 0.99977, 0.99959, 0.99977, 0.99959, 0.99977, 0.99959, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00026, 0.99998, 1.00026, 0.8121, 1.00026, 0.99998, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 1.00016, 1.00022, 1.00001, 0.99973, 1.00001, 1.00026, 1, 1.00026, 1, 1.00026, 1, 1.0006, 0.99973, 0.99977, 0.99973, 1, 0.99982, 1.00022, 1.00026, 1.00001, 0.99973, 1.00026, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 1.00034, 0.99977, 1, 0.99997, 1.00026, 1.00078, 1.00036, 0.99973, 1.00013, 1.0006, 0.99977, 0.99977, 0.99988, 0.85148, 1.00001, 1.00026, 0.99977, 1.00022, 1.0006, 0.99977, 1.00001, 0.99999, 0.99977, 1.00069, 1.00022, 0.99977, 1.00001, 0.99984, 1.00026, 1.00001, 1.00024, 1.00001, 0.9999, 1, 1.0006, 1.00001, 1.00041, 0.99962, 1.00026, 1.0006, 0.99995, 1.00041, 0.99942, 0.99973, 0.99927, 1.00082, 0.99902, 1.00026, 1.00087, 1.0006, 1.00069, 0.99973, 0.99867, 0.99973, 0.9993, 1.00026, 1.00049, 1.00056, 1, 0.99988, 0.99935, 0.99995, 0.99954, 1.00055, 0.99945, 1.00032, 1.0006, 0.99995, 1.00026, 0.99995, 1.00032, 1.00001, 1.00008, 0.99971, 1.00019, 0.9994, 1.00001, 1.0006, 1.00044, 0.99973, 1.00023, 1.00047, 1, 0.99942, 0.99561, 0.99989, 1.00035, 0.99977, 1.00035, 0.99977, 1.00019, 0.99944, 1.00001, 1.00021, 0.99926, 1.00035, 1.00035, 0.99942, 1.00048, 0.99999, 0.99977, 1.00022, 1.00035, 1.00001, 0.99977, 1.00026, 0.99989, 1.00057, 1.00001, 0.99936, 1.00052, 1.00012, 0.99996, 1.00043, 1, 1.00035, 0.9994, 0.99976, 1.00035, 0.99973, 1.00052, 1.00041, 1.00119, 1.00037, 0.99973, 1.00002, 0.99986, 1.00041, 1.00041, 0.99902, 0.9996, 1.00034, 0.99999, 1.00026, 0.99999, 1.00026, 0.99973, 1.00052, 0.99973, 1, 0.99973, 1.00041, 1.00075, 0.9994, 1.0003, 0.99999, 1, 1.00041, 0.99955, 1, 0.99915, 0.99973, 0.99973, 1.00026, 1.00119, 0.99955, 0.99973, 1.0006, 0.99911, 1.0006, 1.00026, 0.99972, 1.00026, 0.99902, 1.00041, 0.99973, 0.99999, 1, 1, 1.00038, 1.0005, 1.00016, 1.00022, 1.00016, 1.00022, 1.00016, 1.00022, 1.00001, 0.99973, 1, 1, 0.99973, 1, 1, 0.99955, 1.0006, 1.0006, 1.0006, 1.0006, 1, 1, 1, 0.99973, 0.99973, 0.99972, 1, 1, 1.00106, 0.99999, 0.99998, 0.99998, 0.99999, 0.99998, 1.66475, 1, 0.99973, 0.99973, 1.00023, 0.99973, 0.99971, 1.00047, 1.00023, 1, 0.99991, 0.99984, 1.00002, 1.00002, 1.00002, 1.00002, 1, 1, 1, 1, 1, 1, 1, 0.99972, 1, 1.20985, 1.39713, 1.00003, 1.00031, 1.00015, 1, 0.99561, 1.00027, 1.00031, 1.00031, 0.99915, 1.00031, 1.00031, 0.99999, 1.00003, 0.99999, 0.99999, 1.41144, 1.6, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.40579, 1.40579, 1.36625, 0.99999, 1, 0.99861, 0.99861, 1, 1.00026, 1.00026, 1.00026, 1.00026, 0.99972, 0.99999, 0.99999, 0.99999, 0.99999, 1.40483, 1, 0.99977, 1.00054, 1, 1, 0.99953, 0.99962, 1.00042, 0.9995, 1, 1, 1, 1, 1, 1, 1, 1, 0.99998, 0.99998, 0.99998, 0.99998, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst HelveticaBoldMetrics = {\n  lineHeight: 1.2,\n  lineGap: 0.2\n};\nconst HelveticaBoldItalicFactors = [0.76116, 1, 1, 1.0006, 0.99998, 0.99974, 0.99973, 0.99973, 0.99982, 0.99977, 1.00087, 0.99998, 0.99998, 0.99959, 1.00003, 1.0006, 0.99998, 1.0006, 1.0006, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99998, 1, 1.00003, 1.00003, 1.00003, 1.00026, 0.9999, 0.99977, 0.99977, 0.99977, 0.99977, 1.00001, 1.00026, 1.00022, 0.99977, 1.0006, 0.99973, 0.99977, 1.00026, 0.99999, 0.99977, 1.00022, 1.00001, 1.00022, 0.99977, 1.00001, 1.00026, 0.99977, 1.00001, 1.00016, 1.00001, 1.00001, 1.00026, 0.99998, 1.0006, 0.99998, 1.00003, 0.99973, 0.99998, 0.99973, 1.00026, 0.99973, 1.00026, 0.99973, 0.99998, 1.00026, 1.00026, 1.0006, 1.0006, 0.99973, 1.0006, 0.99982, 1.00026, 1.00026, 1.00026, 1.00026, 0.99959, 0.99973, 0.99998, 1.00026, 0.99973, 1.00022, 0.99973, 0.99973, 1, 0.99959, 1.00077, 0.99959, 1.00003, 0.99998, 0.99973, 0.99973, 0.99973, 0.99973, 1.00077, 0.99973, 0.99998, 1.00025, 0.99968, 0.99973, 1.00003, 1.00025, 0.60299, 1.00024, 1.06409, 1, 1, 0.99998, 1, 0.99973, 1.0006, 0.99998, 1, 0.99936, 0.99973, 1.00002, 1.00002, 1.00002, 1.00026, 0.99977, 0.99977, 0.99977, 0.99977, 0.99977, 0.99977, 1, 0.99977, 1.00001, 1.00001, 1.00001, 1.00001, 1.0006, 1.0006, 1.0006, 1.0006, 0.99977, 0.99977, 1.00022, 1.00022, 1.00022, 1.00022, 1.00022, 1.00003, 1.00022, 0.99977, 0.99977, 0.99977, 0.99977, 1.00001, 1.00001, 1.00026, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99982, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1.0006, 1.0006, 1.0006, 1.0006, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 1.06409, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 0.99973, 1.00026, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 1.0044, 0.99977, 1.00026, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00022, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 0.99971, 0.99973, 0.99973, 1.0006, 0.99977, 0.99973, 0.99973, 1.00026, 1.0006, 1.00026, 1.0006, 1.00026, 1.01011, 1.00026, 0.99999, 1.00026, 1.0006, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.9993, 0.9998, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 1, 1.00016, 0.99977, 0.99959, 0.99977, 0.99959, 0.99977, 0.99959, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00026, 0.99998, 1.00026, 0.8121, 1.00026, 0.99998, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 1.00016, 1.00022, 1.00001, 0.99973, 1.00001, 1.00026, 1, 1.00026, 1, 1.00026, 1, 1.0006, 0.99973, 0.99977, 0.99973, 1, 0.99982, 1.00022, 1.00026, 1.00001, 0.99973, 1.00026, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99977, 1, 1, 1.00026, 0.99969, 0.99972, 0.99981, 0.9998, 1.0006, 0.99977, 0.99977, 1.00022, 0.91155, 1.00001, 1.00026, 0.99977, 1.00022, 1.0006, 0.99977, 1.00001, 0.99999, 0.99977, 0.99966, 1.00022, 1.00032, 1.00001, 0.99944, 1.00026, 1.00001, 0.99968, 1.00001, 1.00047, 1, 1.0006, 1.00001, 0.99981, 1.00101, 1.00026, 1.0006, 0.99948, 0.99981, 1.00064, 0.99973, 0.99942, 1.00101, 1.00061, 1.00026, 1.00069, 1.0006, 1.00014, 0.99973, 1.01322, 0.99973, 1.00065, 1.00026, 1.00012, 0.99923, 1, 1.00064, 1.00076, 0.99948, 1.00055, 1.00063, 1.00007, 0.99943, 1.0006, 0.99948, 1.00026, 0.99948, 0.99943, 1.00001, 1.00001, 1.00029, 1.00038, 1.00035, 1.00001, 1.0006, 1.0006, 0.99973, 0.99978, 1.00001, 1.00057, 0.99989, 0.99967, 0.99964, 0.99967, 0.99977, 0.99999, 0.99977, 1.00038, 0.99977, 1.00001, 0.99973, 1.00066, 0.99967, 0.99967, 1.00041, 0.99998, 0.99999, 0.99977, 1.00022, 0.99967, 1.00001, 0.99977, 1.00026, 0.99964, 1.00031, 1.00001, 0.99999, 0.99999, 1, 1.00023, 1, 1, 0.99999, 1.00035, 1.00001, 0.99999, 0.99973, 0.99977, 0.99999, 1.00058, 0.99973, 0.99973, 0.99955, 0.9995, 1.00026, 1.00026, 1.00032, 0.99989, 1.00034, 0.99999, 1.00026, 1.00026, 1.00026, 0.99973, 0.45998, 0.99973, 1.00026, 0.99973, 1.00001, 0.99999, 0.99982, 0.99994, 0.99996, 1, 1.00042, 1.00044, 1.00029, 1.00023, 0.99973, 0.99973, 1.00026, 0.99949, 1.00002, 0.99973, 1.0006, 1.0006, 1.0006, 0.99975, 1.00026, 1.00026, 1.00032, 0.98685, 0.99973, 1.00026, 1, 1, 0.99966, 1.00044, 1.00016, 1.00022, 1.00016, 1.00022, 1.00016, 1.00022, 1.00001, 0.99973, 1, 1, 0.99973, 1, 1, 0.99955, 1.0006, 1.0006, 1.0006, 1.0006, 1, 1, 1, 0.99973, 0.99973, 0.99972, 1, 1, 1.00106, 0.99999, 0.99998, 0.99998, 0.99999, 0.99998, 1.66475, 1, 0.99973, 0.99973, 1, 0.99973, 0.99971, 0.99978, 1, 1, 0.99991, 0.99984, 1.00002, 1.00002, 1.00002, 1.00002, 1.00098, 1, 1, 1, 1.00049, 1, 1, 0.99972, 1, 1.20985, 1.39713, 1.00003, 1.00031, 1.00015, 1, 0.99561, 1.00027, 1.00031, 1.00031, 0.99915, 1.00031, 1.00031, 0.99999, 1.00003, 0.99999, 0.99999, 1.41144, 1.6, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.40579, 1.40579, 1.36625, 0.99999, 1, 0.99861, 0.99861, 1, 1.00026, 1.00026, 1.00026, 1.00026, 0.99972, 0.99999, 0.99999, 0.99999, 0.99999, 1.40483, 1, 0.99977, 1.00054, 1, 1, 0.99953, 0.99962, 1.00042, 0.9995, 1, 1, 1, 1, 1, 1, 1, 1, 0.99998, 0.99998, 0.99998, 0.99998, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst HelveticaBoldItalicMetrics = {\n  lineHeight: 1.35,\n  lineGap: 0.2\n};\nconst HelveticaItalicFactors = [0.76116, 1, 1, 1.0006, 1.0006, 1.00006, 0.99973, 0.99973, 0.99982, 1.00001, 1.00043, 0.99998, 0.99998, 0.99959, 1.00003, 1.0006, 0.99998, 1.0006, 1.0006, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1.0006, 1, 1.00003, 1.00003, 1.00003, 0.99973, 0.99987, 1.00001, 1.00001, 0.99977, 0.99977, 1.00001, 1.00026, 1.00022, 0.99977, 1.0006, 1, 1.00001, 0.99973, 0.99999, 0.99977, 1.00022, 1.00001, 1.00022, 0.99977, 1.00001, 1.00026, 0.99977, 1.00001, 1.00016, 1.00001, 1.00001, 1.00026, 1.0006, 1.0006, 1.0006, 0.99949, 0.99973, 0.99998, 0.99973, 0.99973, 1, 0.99973, 0.99973, 1.0006, 0.99973, 0.99973, 0.99924, 0.99924, 1, 0.99924, 0.99999, 0.99973, 0.99973, 0.99973, 0.99973, 0.99998, 1, 1.0006, 0.99973, 1, 0.99977, 1, 1, 1, 1.00005, 1.0009, 1.00005, 1.00003, 0.99998, 0.99973, 0.99973, 0.99973, 0.99973, 1.0009, 0.99973, 0.99998, 1.00025, 0.99968, 0.99973, 1.00003, 1.00025, 0.60299, 1.00024, 1.06409, 1, 1, 0.99998, 1, 0.9998, 1.0006, 0.99998, 1, 0.99936, 0.99973, 1.00002, 1.00002, 1.00002, 1.00026, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1, 0.99977, 1.00001, 1.00001, 1.00001, 1.00001, 1.0006, 1.0006, 1.0006, 1.0006, 0.99977, 0.99977, 1.00022, 1.00022, 1.00022, 1.00022, 1.00022, 1.00003, 1.00022, 0.99977, 0.99977, 0.99977, 0.99977, 1.00001, 1.00001, 1.00026, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99982, 1, 0.99973, 0.99973, 0.99973, 0.99973, 1.0006, 1.0006, 1.0006, 1.0006, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1.06409, 1.00026, 0.99973, 0.99973, 0.99973, 0.99973, 1, 0.99973, 1, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 0.99977, 1, 0.99977, 1, 0.99977, 1, 0.99977, 1, 0.99977, 1.0288, 0.99977, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 0.99924, 1.0006, 1.0006, 0.99946, 1.00034, 1, 0.99924, 1.00001, 1, 1, 0.99973, 0.99924, 0.99973, 0.99924, 0.99973, 1.06311, 0.99973, 1.00024, 0.99973, 0.99924, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 1.00041, 0.9998, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1, 1.00016, 0.99977, 0.99998, 0.99977, 0.99998, 0.99977, 0.99998, 1.00001, 1, 1.00001, 1, 1.00001, 1, 1.00001, 1, 1.00026, 1.0006, 1.00026, 0.89547, 1.00026, 1.0006, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 1.00016, 0.99977, 1.00001, 1, 1.00001, 1.00026, 1, 1.00026, 1, 1.00026, 1, 0.99924, 0.99973, 1.00001, 0.99973, 1, 0.99982, 1.00022, 1.00026, 1.00001, 1, 1.00026, 1.0006, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 1.00001, 1, 1.00054, 0.99977, 1.00084, 1.00007, 0.99973, 1.00013, 0.99924, 1.00001, 1.00001, 0.99945, 0.91221, 1.00001, 1.00026, 0.99977, 1.00022, 1.0006, 1.00001, 1.00001, 0.99999, 0.99977, 0.99933, 1.00022, 1.00054, 1.00001, 1.00065, 1.00026, 1.00001, 1.0001, 1.00001, 1.00052, 1, 1.0006, 1.00001, 0.99945, 0.99897, 0.99968, 0.99924, 1.00036, 0.99945, 0.99949, 1, 1.0006, 0.99897, 0.99918, 0.99968, 0.99911, 0.99924, 1, 0.99962, 1.01487, 1, 1.0005, 0.99973, 1.00012, 1.00043, 1, 0.99995, 0.99994, 1.00036, 0.99947, 1.00019, 1.00063, 1.00025, 0.99924, 1.00036, 0.99973, 1.00036, 1.00025, 1.00001, 1.00001, 1.00027, 1.0001, 1.00068, 1.00001, 1.0006, 1.0006, 1, 1.00008, 0.99957, 0.99972, 0.9994, 0.99954, 0.99975, 1.00051, 1.00001, 1.00019, 1.00001, 1.0001, 0.99986, 1.00001, 1.00001, 1.00038, 0.99954, 0.99954, 0.9994, 1.00066, 0.99999, 0.99977, 1.00022, 1.00054, 1.00001, 0.99977, 1.00026, 0.99975, 1.0001, 1.00001, 0.99993, 0.9995, 0.99955, 1.00016, 0.99978, 0.99974, 1.00019, 1.00022, 0.99955, 1.00053, 0.99973, 1.00089, 1.00005, 0.99967, 1.00048, 0.99973, 1.00002, 1.00034, 0.99973, 0.99973, 0.99964, 1.00006, 1.00066, 0.99947, 0.99973, 0.98894, 0.99973, 1, 0.44898, 1, 0.99946, 1, 1.00039, 1.00082, 0.99991, 0.99991, 0.99985, 1.00022, 1.00023, 1.00061, 1.00006, 0.99966, 0.99973, 0.99973, 0.99973, 1.00019, 1.0008, 1, 0.99924, 0.99924, 0.99924, 0.99983, 1.00044, 0.99973, 0.99964, 0.98332, 1, 0.99973, 1, 1, 0.99962, 0.99895, 1.00016, 0.99977, 1.00016, 0.99977, 1.00016, 0.99977, 1.00001, 1, 1, 1, 0.99973, 1, 1, 0.99955, 0.99924, 0.99924, 0.99924, 0.99924, 0.99998, 0.99998, 0.99998, 0.99973, 0.99973, 0.99972, 1, 1, 1.00267, 0.99999, 0.99998, 0.99998, 1, 0.99998, 1.66475, 1, 0.99973, 0.99973, 1.00023, 0.99973, 1.00423, 0.99925, 0.99999, 1, 0.99991, 0.99984, 1.00002, 1.00002, 1.00002, 1.00002, 1.00049, 1, 1.00245, 1, 1, 1, 1, 0.96329, 1, 1.20985, 1.39713, 1.00003, 0.8254, 1.00015, 1, 1.00035, 1.00027, 1.00031, 1.00031, 1.00003, 1.00031, 1.00031, 0.99999, 1.00003, 0.99999, 0.99999, 1.41144, 1.6, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.40579, 1.40579, 1.36625, 0.99999, 1, 0.99861, 0.99861, 1, 1.00026, 1.00026, 1.00026, 1.00026, 0.95317, 0.99999, 0.99999, 0.99999, 0.99999, 1.40483, 1, 0.99977, 1.00054, 1, 1, 0.99953, 0.99962, 1.00042, 0.9995, 1, 1, 1, 1, 1, 1, 1, 1, 0.99998, 0.99998, 0.99998, 0.99998, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst HelveticaItalicMetrics = {\n  lineHeight: 1.35,\n  lineGap: 0.2\n};\nconst HelveticaRegularFactors = [0.76116, 1, 1, 1.0006, 1.0006, 1.00006, 0.99973, 0.99973, 0.99982, 1.00001, 1.00043, 0.99998, 0.99998, 0.99959, 1.00003, 1.0006, 0.99998, 1.0006, 1.0006, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1.0006, 1, 1.00003, 1.00003, 1.00003, 0.99973, 0.99987, 1.00001, 1.00001, 0.99977, 0.99977, 1.00001, 1.00026, 1.00022, 0.99977, 1.0006, 1, 1.00001, 0.99973, 0.99999, 0.99977, 1.00022, 1.00001, 1.00022, 0.99977, 1.00001, 1.00026, 0.99977, 1.00001, 1.00016, 1.00001, 1.00001, 1.00026, 1.0006, 1.0006, 1.0006, 0.99949, 0.99973, 0.99998, 0.99973, 0.99973, 1, 0.99973, 0.99973, 1.0006, 0.99973, 0.99973, 0.99924, 0.99924, 1, 0.99924, 0.99999, 0.99973, 0.99973, 0.99973, 0.99973, 0.99998, 1, 1.0006, 0.99973, 1, 0.99977, 1, 1, 1, 1.00005, 1.0009, 1.00005, 1.00003, 0.99998, 0.99973, 0.99973, 0.99973, 0.99973, 1.0009, 0.99973, 0.99998, 1.00025, 0.99968, 0.99973, 1.00003, 1.00025, 0.60299, 1.00024, 1.06409, 1, 1, 0.99998, 1, 0.9998, 1.0006, 0.99998, 1, 0.99936, 0.99973, 1.00002, 1.00002, 1.00002, 1.00026, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1, 0.99977, 1.00001, 1.00001, 1.00001, 1.00001, 1.0006, 1.0006, 1.0006, 1.0006, 0.99977, 0.99977, 1.00022, 1.00022, 1.00022, 1.00022, 1.00022, 1.00003, 1.00022, 0.99977, 0.99977, 0.99977, 0.99977, 1.00001, 1.00001, 1.00026, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99982, 1, 0.99973, 0.99973, 0.99973, 0.99973, 1.0006, 1.0006, 1.0006, 1.0006, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1.06409, 1.00026, 0.99973, 0.99973, 0.99973, 0.99973, 1, 0.99973, 1, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 0.99977, 1, 0.99977, 1, 0.99977, 1, 0.99977, 1, 0.99977, 1.04596, 0.99977, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 0.99924, 1.0006, 1.0006, 1.00019, 1.00034, 1, 0.99924, 1.00001, 1, 1, 0.99973, 0.99924, 0.99973, 0.99924, 0.99973, 1.02572, 0.99973, 1.00005, 0.99973, 0.99924, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99999, 0.9998, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1, 1.00016, 0.99977, 0.99998, 0.99977, 0.99998, 0.99977, 0.99998, 1.00001, 1, 1.00001, 1, 1.00001, 1, 1.00001, 1, 1.00026, 1.0006, 1.00026, 0.84533, 1.00026, 1.0006, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 1.00016, 0.99977, 1.00001, 1, 1.00001, 1.00026, 1, 1.00026, 1, 1.00026, 1, 0.99924, 0.99973, 1.00001, 0.99973, 1, 0.99982, 1.00022, 1.00026, 1.00001, 1, 1.00026, 1.0006, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99928, 1, 0.99977, 1.00013, 1.00055, 0.99947, 0.99945, 0.99941, 0.99924, 1.00001, 1.00001, 1.0004, 0.91621, 1.00001, 1.00026, 0.99977, 1.00022, 1.0006, 1.00001, 1.00005, 0.99999, 0.99977, 1.00015, 1.00022, 0.99977, 1.00001, 0.99973, 1.00026, 1.00001, 1.00019, 1.00001, 0.99946, 1, 1.0006, 1.00001, 0.99978, 1.00045, 0.99973, 0.99924, 1.00023, 0.99978, 0.99966, 1, 1.00065, 1.00045, 1.00019, 0.99973, 0.99973, 0.99924, 1, 1, 0.96499, 1, 1.00055, 0.99973, 1.00008, 1.00027, 1, 0.9997, 0.99995, 1.00023, 0.99933, 1.00019, 1.00015, 1.00031, 0.99924, 1.00023, 0.99973, 1.00023, 1.00031, 1.00001, 0.99928, 1.00029, 1.00092, 1.00035, 1.00001, 1.0006, 1.0006, 1, 0.99988, 0.99975, 1, 1.00082, 0.99561, 0.9996, 1.00035, 1.00001, 0.99962, 1.00001, 1.00092, 0.99964, 1.00001, 0.99963, 0.99999, 1.00035, 1.00035, 1.00082, 0.99962, 0.99999, 0.99977, 1.00022, 1.00035, 1.00001, 0.99977, 1.00026, 0.9996, 0.99967, 1.00001, 1.00034, 1.00074, 1.00054, 1.00053, 1.00063, 0.99971, 0.99962, 1.00035, 0.99975, 0.99977, 0.99973, 1.00043, 0.99953, 1.0007, 0.99915, 0.99973, 1.00008, 0.99892, 1.00073, 1.00073, 1.00114, 0.99915, 1.00073, 0.99955, 0.99973, 1.00092, 0.99973, 1, 0.99998, 1, 1.0003, 1, 1.00043, 1.00001, 0.99969, 1.0003, 1, 1.00035, 1.00001, 0.9995, 1, 1.00092, 0.99973, 0.99973, 0.99973, 1.0007, 0.9995, 1, 0.99924, 1.0006, 0.99924, 0.99972, 1.00062, 0.99973, 1.00114, 1.00073, 1, 0.99955, 1, 1, 1.00047, 0.99968, 1.00016, 0.99977, 1.00016, 0.99977, 1.00016, 0.99977, 1.00001, 1, 1, 1, 0.99973, 1, 1, 0.99955, 0.99924, 0.99924, 0.99924, 0.99924, 0.99998, 0.99998, 0.99998, 0.99973, 0.99973, 0.99972, 1, 1, 1.00267, 0.99999, 0.99998, 0.99998, 1, 0.99998, 1.66475, 1, 0.99973, 0.99973, 1.00023, 0.99973, 0.99971, 0.99925, 1.00023, 1, 0.99991, 0.99984, 1.00002, 1.00002, 1.00002, 1.00002, 1, 1, 1, 1, 1, 1, 1, 0.96329, 1, 1.20985, 1.39713, 1.00003, 0.8254, 1.00015, 1, 1.00035, 1.00027, 1.00031, 1.00031, 0.99915, 1.00031, 1.00031, 0.99999, 1.00003, 0.99999, 0.99999, 1.41144, 1.6, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.40579, 1.40579, 1.36625, 0.99999, 1, 0.99861, 0.99861, 1, 1.00026, 1.00026, 1.00026, 1.00026, 0.95317, 0.99999, 0.99999, 0.99999, 0.99999, 1.40483, 1, 0.99977, 1.00054, 1, 1, 0.99953, 0.99962, 1.00042, 0.9995, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst HelveticaRegularMetrics = {\n  lineHeight: 1.2,\n  lineGap: 0.2\n};\n\n;// CONCATENATED MODULE: ./src/core/liberationsans_widths.js\nconst LiberationSansBoldWidths = [365, 0, 333, 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 737, 552, 400, 549, 333, 333, 333, 576, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 549, 611, 611, 611, 611, 611, 556, 611, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 719, 722, 611, 667, 556, 667, 556, 667, 556, 667, 556, 667, 556, 778, 611, 778, 611, 778, 611, 778, 611, 722, 611, 722, 611, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 785, 556, 556, 278, 722, 556, 556, 611, 278, 611, 278, 611, 385, 611, 479, 611, 278, 722, 611, 722, 611, 722, 611, 708, 723, 611, 778, 611, 778, 611, 778, 611, 1000, 944, 722, 389, 722, 389, 722, 389, 667, 556, 667, 556, 667, 556, 667, 556, 611, 333, 611, 479, 611, 333, 722, 611, 722, 611, 722, 611, 722, 611, 722, 611, 722, 611, 944, 778, 667, 556, 667, 611, 500, 611, 500, 611, 500, 278, 556, 722, 556, 1000, 889, 778, 611, 667, 556, 611, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 465, 722, 333, 853, 906, 474, 825, 927, 838, 278, 722, 722, 601, 719, 667, 611, 722, 778, 278, 722, 667, 833, 722, 644, 778, 722, 667, 600, 611, 667, 821, 667, 809, 802, 278, 667, 615, 451, 611, 278, 582, 615, 610, 556, 606, 475, 460, 611, 541, 278, 558, 556, 612, 556, 445, 611, 766, 619, 520, 684, 446, 582, 715, 576, 753, 845, 278, 582, 611, 582, 845, 667, 669, 885, 567, 711, 667, 278, 276, 556, 1094, 1062, 875, 610, 722, 622, 719, 722, 719, 722, 567, 712, 667, 904, 626, 719, 719, 610, 702, 833, 722, 778, 719, 667, 722, 611, 622, 854, 667, 730, 703, 1005, 1019, 870, 979, 719, 711, 1031, 719, 556, 618, 615, 417, 635, 556, 709, 497, 615, 615, 500, 635, 740, 604, 611, 604, 611, 556, 490, 556, 875, 556, 615, 581, 833, 844, 729, 854, 615, 552, 854, 583, 556, 556, 611, 417, 552, 556, 278, 281, 278, 969, 906, 611, 500, 615, 556, 604, 778, 611, 487, 447, 944, 778, 944, 778, 944, 778, 667, 556, 333, 333, 556, 1000, 1000, 552, 278, 278, 278, 278, 500, 500, 500, 556, 556, 350, 1000, 1000, 240, 479, 333, 333, 604, 333, 167, 396, 556, 556, 1094, 556, 885, 489, 1115, 1000, 768, 600, 834, 834, 834, 834, 1000, 500, 1000, 500, 1000, 500, 500, 494, 612, 823, 713, 584, 549, 713, 979, 722, 274, 549, 549, 583, 549, 549, 604, 584, 604, 604, 708, 625, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 729, 604, 604, 354, 354, 1000, 990, 990, 990, 990, 494, 604, 604, 604, 604, 354, 1021, 1052, 917, 750, 750, 531, 656, 594, 510, 500, 750, 750, 611, 611, 333, 333, 333, 333, 333, 333, 333, 333, 222, 222, 333, 333, 333, 333, 333, 333, 333, 333];\nconst LiberationSansBoldMapping = [-1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 402, 506, 507, 508, 509, 510, 511, 536, 537, 538, 539, 710, 711, 713, 728, 729, 730, 731, 732, 733, 900, 901, 902, 903, 904, 905, 906, 908, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1138, 1139, 1168, 1169, 7808, 7809, 7810, 7811, 7812, 7813, 7922, 7923, 8208, 8209, 8211, 8212, 8213, 8215, 8216, 8217, 8218, 8219, 8220, 8221, 8222, 8224, 8225, 8226, 8230, 8240, 8242, 8243, 8249, 8250, 8252, 8254, 8260, 8319, 8355, 8356, 8359, 8364, 8453, 8467, 8470, 8482, 8486, 8494, 8539, 8540, 8541, 8542, 8592, 8593, 8594, 8595, 8596, 8597, 8616, 8706, 8710, 8719, 8721, 8722, 8730, 8734, 8735, 8745, 8747, 8776, 8800, 8801, 8804, 8805, 8962, 8976, 8992, 8993, 9472, 9474, 9484, 9488, 9492, 9496, 9500, 9508, 9516, 9524, 9532, 9552, 9553, 9554, 9555, 9556, 9557, 9558, 9559, 9560, 9561, 9562, 9563, 9564, 9565, 9566, 9567, 9568, 9569, 9570, 9571, 9572, 9573, 9574, 9575, 9576, 9577, 9578, 9579, 9580, 9600, 9604, 9608, 9612, 9616, 9617, 9618, 9619, 9632, 9633, 9642, 9643, 9644, 9650, 9658, 9660, 9668, 9674, 9675, 9679, 9688, 9689, 9702, 9786, 9787, 9788, 9792, 9794, 9824, 9827, 9829, 9830, 9834, 9835, 9836, 61441, 61442, 61445, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1];\nconst LiberationSansBoldItalicWidths = [365, 0, 333, 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 737, 552, 400, 549, 333, 333, 333, 576, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 549, 611, 611, 611, 611, 611, 556, 611, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 740, 722, 611, 667, 556, 667, 556, 667, 556, 667, 556, 667, 556, 778, 611, 778, 611, 778, 611, 778, 611, 722, 611, 722, 611, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 782, 556, 556, 278, 722, 556, 556, 611, 278, 611, 278, 611, 396, 611, 479, 611, 278, 722, 611, 722, 611, 722, 611, 708, 723, 611, 778, 611, 778, 611, 778, 611, 1000, 944, 722, 389, 722, 389, 722, 389, 667, 556, 667, 556, 667, 556, 667, 556, 611, 333, 611, 479, 611, 333, 722, 611, 722, 611, 722, 611, 722, 611, 722, 611, 722, 611, 944, 778, 667, 556, 667, 611, 500, 611, 500, 611, 500, 278, 556, 722, 556, 1000, 889, 778, 611, 667, 556, 611, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 722, 333, 854, 906, 473, 844, 930, 847, 278, 722, 722, 610, 671, 667, 611, 722, 778, 278, 722, 667, 833, 722, 657, 778, 718, 667, 590, 611, 667, 822, 667, 829, 781, 278, 667, 620, 479, 611, 278, 591, 620, 621, 556, 610, 479, 492, 611, 558, 278, 566, 556, 603, 556, 450, 611, 712, 605, 532, 664, 409, 591, 704, 578, 773, 834, 278, 591, 611, 591, 834, 667, 667, 886, 614, 719, 667, 278, 278, 556, 1094, 1042, 854, 622, 719, 677, 719, 722, 708, 722, 614, 722, 667, 927, 643, 719, 719, 615, 687, 833, 722, 778, 719, 667, 722, 611, 677, 781, 667, 729, 708, 979, 989, 854, 1000, 708, 719, 1042, 729, 556, 619, 604, 534, 618, 556, 736, 510, 611, 611, 507, 622, 740, 604, 611, 611, 611, 556, 889, 556, 885, 556, 646, 583, 889, 935, 707, 854, 594, 552, 865, 589, 556, 556, 611, 469, 563, 556, 278, 278, 278, 969, 906, 611, 507, 619, 556, 611, 778, 611, 575, 467, 944, 778, 944, 778, 944, 778, 667, 556, 333, 333, 556, 1000, 1000, 552, 278, 278, 278, 278, 500, 500, 500, 556, 556, 350, 1000, 1000, 240, 479, 333, 333, 604, 333, 167, 396, 556, 556, 1104, 556, 885, 516, 1146, 1000, 768, 600, 834, 834, 834, 834, 999, 500, 1000, 500, 1000, 500, 500, 494, 612, 823, 713, 584, 549, 713, 979, 722, 274, 549, 549, 583, 549, 549, 604, 584, 604, 604, 708, 625, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 729, 604, 604, 354, 354, 1000, 990, 990, 990, 990, 494, 604, 604, 604, 604, 354, 1021, 1052, 917, 750, 750, 531, 656, 594, 510, 500, 750, 750, 611, 611, 333, 333, 333, 333, 333, 333, 333, 333, 222, 222, 333, 333, 333, 333, 333, 333, 333, 333];\nconst LiberationSansBoldItalicMapping = [-1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 402, 506, 507, 508, 509, 510, 511, 536, 537, 538, 539, 710, 711, 713, 728, 729, 730, 731, 732, 733, 900, 901, 902, 903, 904, 905, 906, 908, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1138, 1139, 1168, 1169, 7808, 7809, 7810, 7811, 7812, 7813, 7922, 7923, 8208, 8209, 8211, 8212, 8213, 8215, 8216, 8217, 8218, 8219, 8220, 8221, 8222, 8224, 8225, 8226, 8230, 8240, 8242, 8243, 8249, 8250, 8252, 8254, 8260, 8319, 8355, 8356, 8359, 8364, 8453, 8467, 8470, 8482, 8486, 8494, 8539, 8540, 8541, 8542, 8592, 8593, 8594, 8595, 8596, 8597, 8616, 8706, 8710, 8719, 8721, 8722, 8730, 8734, 8735, 8745, 8747, 8776, 8800, 8801, 8804, 8805, 8962, 8976, 8992, 8993, 9472, 9474, 9484, 9488, 9492, 9496, 9500, 9508, 9516, 9524, 9532, 9552, 9553, 9554, 9555, 9556, 9557, 9558, 9559, 9560, 9561, 9562, 9563, 9564, 9565, 9566, 9567, 9568, 9569, 9570, 9571, 9572, 9573, 9574, 9575, 9576, 9577, 9578, 9579, 9580, 9600, 9604, 9608, 9612, 9616, 9617, 9618, 9619, 9632, 9633, 9642, 9643, 9644, 9650, 9658, 9660, 9668, 9674, 9675, 9679, 9688, 9689, 9702, 9786, 9787, 9788, 9792, 9794, 9824, 9827, 9829, 9830, 9834, 9835, 9836, 61441, 61442, 61445, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1];\nconst LiberationSansItalicWidths = [365, 0, 333, 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 737, 552, 400, 549, 333, 333, 333, 576, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278, 556, 556, 556, 556, 556, 556, 556, 549, 611, 556, 556, 556, 556, 500, 556, 500, 667, 556, 667, 556, 667, 556, 722, 500, 722, 500, 722, 500, 722, 500, 722, 625, 722, 556, 667, 556, 667, 556, 667, 556, 667, 556, 667, 556, 778, 556, 778, 556, 778, 556, 778, 556, 722, 556, 722, 556, 278, 278, 278, 278, 278, 278, 278, 222, 278, 278, 733, 444, 500, 222, 667, 500, 500, 556, 222, 556, 222, 556, 281, 556, 400, 556, 222, 722, 556, 722, 556, 722, 556, 615, 723, 556, 778, 556, 778, 556, 778, 556, 1000, 944, 722, 333, 722, 333, 722, 333, 667, 500, 667, 500, 667, 500, 667, 500, 611, 278, 611, 354, 611, 278, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 944, 722, 667, 500, 667, 611, 500, 611, 500, 611, 500, 222, 556, 667, 556, 1000, 889, 778, 611, 667, 500, 611, 278, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 667, 278, 789, 846, 389, 794, 865, 775, 222, 667, 667, 570, 671, 667, 611, 722, 778, 278, 667, 667, 833, 722, 648, 778, 725, 667, 600, 611, 667, 837, 667, 831, 761, 278, 667, 570, 439, 555, 222, 550, 570, 571, 500, 556, 439, 463, 555, 542, 222, 500, 492, 548, 500, 447, 556, 670, 573, 486, 603, 374, 550, 652, 546, 728, 779, 222, 550, 556, 550, 779, 667, 667, 843, 544, 708, 667, 278, 278, 500, 1066, 982, 844, 589, 715, 639, 724, 667, 651, 667, 544, 704, 667, 917, 614, 715, 715, 589, 686, 833, 722, 778, 725, 667, 722, 611, 639, 795, 667, 727, 673, 920, 923, 805, 886, 651, 694, 1022, 682, 556, 562, 522, 493, 553, 556, 688, 465, 556, 556, 472, 564, 686, 550, 556, 556, 556, 500, 833, 500, 835, 500, 572, 518, 830, 851, 621, 736, 526, 492, 752, 534, 556, 556, 556, 378, 496, 500, 222, 222, 222, 910, 828, 556, 472, 565, 500, 556, 778, 556, 492, 339, 944, 722, 944, 722, 944, 722, 667, 500, 333, 333, 556, 1000, 1000, 552, 222, 222, 222, 222, 333, 333, 333, 556, 556, 350, 1000, 1000, 188, 354, 333, 333, 500, 333, 167, 365, 556, 556, 1094, 556, 885, 323, 1083, 1000, 768, 600, 834, 834, 834, 834, 1000, 500, 998, 500, 1000, 500, 500, 494, 612, 823, 713, 584, 549, 713, 979, 719, 274, 549, 549, 584, 549, 549, 604, 584, 604, 604, 708, 625, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 729, 604, 604, 354, 354, 1000, 990, 990, 990, 990, 494, 604, 604, 604, 604, 354, 1021, 1052, 917, 750, 750, 531, 656, 594, 510, 500, 750, 750, 500, 500, 333, 333, 333, 333, 333, 333, 333, 333, 222, 222, 294, 294, 324, 324, 316, 328, 398, 285];\nconst LiberationSansItalicMapping = [-1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 402, 506, 507, 508, 509, 510, 511, 536, 537, 538, 539, 710, 711, 713, 728, 729, 730, 731, 732, 733, 900, 901, 902, 903, 904, 905, 906, 908, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1138, 1139, 1168, 1169, 7808, 7809, 7810, 7811, 7812, 7813, 7922, 7923, 8208, 8209, 8211, 8212, 8213, 8215, 8216, 8217, 8218, 8219, 8220, 8221, 8222, 8224, 8225, 8226, 8230, 8240, 8242, 8243, 8249, 8250, 8252, 8254, 8260, 8319, 8355, 8356, 8359, 8364, 8453, 8467, 8470, 8482, 8486, 8494, 8539, 8540, 8541, 8542, 8592, 8593, 8594, 8595, 8596, 8597, 8616, 8706, 8710, 8719, 8721, 8722, 8730, 8734, 8735, 8745, 8747, 8776, 8800, 8801, 8804, 8805, 8962, 8976, 8992, 8993, 9472, 9474, 9484, 9488, 9492, 9496, 9500, 9508, 9516, 9524, 9532, 9552, 9553, 9554, 9555, 9556, 9557, 9558, 9559, 9560, 9561, 9562, 9563, 9564, 9565, 9566, 9567, 9568, 9569, 9570, 9571, 9572, 9573, 9574, 9575, 9576, 9577, 9578, 9579, 9580, 9600, 9604, 9608, 9612, 9616, 9617, 9618, 9619, 9632, 9633, 9642, 9643, 9644, 9650, 9658, 9660, 9668, 9674, 9675, 9679, 9688, 9689, 9702, 9786, 9787, 9788, 9792, 9794, 9824, 9827, 9829, 9830, 9834, 9835, 9836, 61441, 61442, 61445, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1];\nconst LiberationSansRegularWidths = [365, 0, 333, 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 737, 552, 400, 549, 333, 333, 333, 576, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278, 556, 556, 556, 556, 556, 556, 556, 549, 611, 556, 556, 556, 556, 500, 556, 500, 667, 556, 667, 556, 667, 556, 722, 500, 722, 500, 722, 500, 722, 500, 722, 615, 722, 556, 667, 556, 667, 556, 667, 556, 667, 556, 667, 556, 778, 556, 778, 556, 778, 556, 778, 556, 722, 556, 722, 556, 278, 278, 278, 278, 278, 278, 278, 222, 278, 278, 735, 444, 500, 222, 667, 500, 500, 556, 222, 556, 222, 556, 292, 556, 334, 556, 222, 722, 556, 722, 556, 722, 556, 604, 723, 556, 778, 556, 778, 556, 778, 556, 1000, 944, 722, 333, 722, 333, 722, 333, 667, 500, 667, 500, 667, 500, 667, 500, 611, 278, 611, 375, 611, 278, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 944, 722, 667, 500, 667, 611, 500, 611, 500, 611, 500, 222, 556, 667, 556, 1000, 889, 778, 611, 667, 500, 611, 278, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 667, 278, 784, 838, 384, 774, 855, 752, 222, 667, 667, 551, 668, 667, 611, 722, 778, 278, 667, 668, 833, 722, 650, 778, 722, 667, 618, 611, 667, 798, 667, 835, 748, 278, 667, 578, 446, 556, 222, 547, 578, 575, 500, 557, 446, 441, 556, 556, 222, 500, 500, 576, 500, 448, 556, 690, 569, 482, 617, 395, 547, 648, 525, 713, 781, 222, 547, 556, 547, 781, 667, 667, 865, 542, 719, 667, 278, 278, 500, 1057, 1010, 854, 583, 722, 635, 719, 667, 656, 667, 542, 677, 667, 923, 604, 719, 719, 583, 656, 833, 722, 778, 719, 667, 722, 611, 635, 760, 667, 740, 667, 917, 938, 792, 885, 656, 719, 1010, 722, 556, 573, 531, 365, 583, 556, 669, 458, 559, 559, 438, 583, 688, 552, 556, 542, 556, 500, 458, 500, 823, 500, 573, 521, 802, 823, 625, 719, 521, 510, 750, 542, 556, 556, 556, 365, 510, 500, 222, 278, 222, 906, 812, 556, 438, 559, 500, 552, 778, 556, 489, 411, 944, 722, 944, 722, 944, 722, 667, 500, 333, 333, 556, 1000, 1000, 552, 222, 222, 222, 222, 333, 333, 333, 556, 556, 350, 1000, 1000, 188, 354, 333, 333, 500, 333, 167, 365, 556, 556, 1094, 556, 885, 323, 1073, 1000, 768, 600, 834, 834, 834, 834, 1000, 500, 1000, 500, 1000, 500, 500, 494, 612, 823, 713, 584, 549, 713, 979, 719, 274, 549, 549, 583, 549, 549, 604, 584, 604, 604, 708, 625, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 729, 604, 604, 354, 354, 1000, 990, 990, 990, 990, 494, 604, 604, 604, 604, 354, 1021, 1052, 917, 750, 750, 531, 656, 594, 510, 500, 750, 750, 500, 500, 333, 333, 333, 333, 333, 333, 333, 333, 222, 222, 294, 294, 324, 324, 316, 328, 398, 285];\nconst LiberationSansRegularMapping = [-1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 402, 506, 507, 508, 509, 510, 511, 536, 537, 538, 539, 710, 711, 713, 728, 729, 730, 731, 732, 733, 900, 901, 902, 903, 904, 905, 906, 908, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1138, 1139, 1168, 1169, 7808, 7809, 7810, 7811, 7812, 7813, 7922, 7923, 8208, 8209, 8211, 8212, 8213, 8215, 8216, 8217, 8218, 8219, 8220, 8221, 8222, 8224, 8225, 8226, 8230, 8240, 8242, 8243, 8249, 8250, 8252, 8254, 8260, 8319, 8355, 8356, 8359, 8364, 8453, 8467, 8470, 8482, 8486, 8494, 8539, 8540, 8541, 8542, 8592, 8593, 8594, 8595, 8596, 8597, 8616, 8706, 8710, 8719, 8721, 8722, 8730, 8734, 8735, 8745, 8747, 8776, 8800, 8801, 8804, 8805, 8962, 8976, 8992, 8993, 9472, 9474, 9484, 9488, 9492, 9496, 9500, 9508, 9516, 9524, 9532, 9552, 9553, 9554, 9555, 9556, 9557, 9558, 9559, 9560, 9561, 9562, 9563, 9564, 9565, 9566, 9567, 9568, 9569, 9570, 9571, 9572, 9573, 9574, 9575, 9576, 9577, 9578, 9579, 9580, 9600, 9604, 9608, 9612, 9616, 9617, 9618, 9619, 9632, 9633, 9642, 9643, 9644, 9650, 9658, 9660, 9668, 9674, 9675, 9679, 9688, 9689, 9702, 9786, 9787, 9788, 9792, 9794, 9824, 9827, 9829, 9830, 9834, 9835, 9836, 61441, 61442, 61445, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1];\n\n;// CONCATENATED MODULE: ./src/core/myriadpro_factors.js\nconst MyriadProBoldFactors = [1.36898, 1, 1, 0.72706, 0.80479, 0.83734, 0.98894, 0.99793, 0.9897, 0.93884, 0.86209, 0.94292, 0.94292, 1.16661, 1.02058, 0.93582, 0.96694, 0.93582, 1.19137, 0.99793, 0.99793, 0.99793, 0.99793, 0.99793, 0.99793, 0.99793, 0.99793, 0.99793, 0.99793, 0.78076, 0.78076, 1.02058, 1.02058, 1.02058, 0.72851, 0.78966, 0.90838, 0.83637, 0.82391, 0.96376, 0.80061, 0.86275, 0.8768, 0.95407, 1.0258, 0.73901, 0.85022, 0.83655, 1.0156, 0.95546, 0.92179, 0.87107, 0.92179, 0.82114, 0.8096, 0.89713, 0.94438, 0.95353, 0.94083, 0.91905, 0.90406, 0.9446, 0.94292, 1.18777, 0.94292, 1.02058, 0.89903, 0.90088, 0.94938, 0.97898, 0.81093, 0.97571, 0.94938, 1.024, 0.9577, 0.95933, 0.98621, 1.0474, 0.97455, 0.98981, 0.9672, 0.95933, 0.9446, 0.97898, 0.97407, 0.97646, 0.78036, 1.10208, 0.95442, 0.95298, 0.97579, 0.9332, 0.94039, 0.938, 0.80687, 1.01149, 0.80687, 1.02058, 0.80479, 0.99793, 0.99793, 0.99793, 0.99793, 1.01149, 1.00872, 0.90088, 0.91882, 1.0213, 0.8361, 1.02058, 0.62295, 0.54324, 0.89022, 1.08595, 1, 1, 0.90088, 1, 0.97455, 0.93582, 0.90088, 1, 1.05686, 0.8361, 0.99642, 0.99642, 0.99642, 0.72851, 0.90838, 0.90838, 0.90838, 0.90838, 0.90838, 0.90838, 0.868, 0.82391, 0.80061, 0.80061, 0.80061, 0.80061, 1.0258, 1.0258, 1.0258, 1.0258, 0.97484, 0.95546, 0.92179, 0.92179, 0.92179, 0.92179, 0.92179, 1.02058, 0.92179, 0.94438, 0.94438, 0.94438, 0.94438, 0.90406, 0.86958, 0.98225, 0.94938, 0.94938, 0.94938, 0.94938, 0.94938, 0.94938, 0.9031, 0.81093, 0.94938, 0.94938, 0.94938, 0.94938, 0.98621, 0.98621, 0.98621, 0.98621, 0.93969, 0.95933, 0.9446, 0.9446, 0.9446, 0.9446, 0.9446, 1.08595, 0.9446, 0.95442, 0.95442, 0.95442, 0.95442, 0.94039, 0.97898, 0.94039, 0.90838, 0.94938, 0.90838, 0.94938, 0.90838, 0.94938, 0.82391, 0.81093, 0.82391, 0.81093, 0.82391, 0.81093, 0.82391, 0.81093, 0.96376, 0.84313, 0.97484, 0.97571, 0.80061, 0.94938, 0.80061, 0.94938, 0.80061, 0.94938, 0.80061, 0.94938, 0.80061, 0.94938, 0.8768, 0.9577, 0.8768, 0.9577, 0.8768, 0.9577, 1, 1, 0.95407, 0.95933, 0.97069, 0.95933, 1.0258, 0.98621, 1.0258, 0.98621, 1.0258, 0.98621, 1.0258, 0.98621, 1.0258, 0.98621, 0.887, 1.01591, 0.73901, 1.0474, 1, 1, 0.97455, 0.83655, 0.98981, 1, 1, 0.83655, 0.73977, 0.83655, 0.73903, 0.84638, 1.033, 0.95546, 0.95933, 1, 1, 0.95546, 0.95933, 0.8271, 0.95417, 0.95933, 0.92179, 0.9446, 0.92179, 0.9446, 0.92179, 0.9446, 0.936, 0.91964, 0.82114, 0.97646, 1, 1, 0.82114, 0.97646, 0.8096, 0.78036, 0.8096, 0.78036, 1, 1, 0.8096, 0.78036, 1, 1, 0.89713, 0.77452, 0.89713, 1.10208, 0.94438, 0.95442, 0.94438, 0.95442, 0.94438, 0.95442, 0.94438, 0.95442, 0.94438, 0.95442, 0.94438, 0.95442, 0.94083, 0.97579, 0.90406, 0.94039, 0.90406, 0.9446, 0.938, 0.9446, 0.938, 0.9446, 0.938, 1, 0.99793, 0.90838, 0.94938, 0.868, 0.9031, 0.92179, 0.9446, 1, 1, 0.89713, 1.10208, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90989, 0.9358, 0.91945, 0.83181, 0.75261, 0.87992, 0.82976, 0.96034, 0.83689, 0.97268, 1.0078, 0.90838, 0.83637, 0.8019, 0.90157, 0.80061, 0.9446, 0.95407, 0.92436, 1.0258, 0.85022, 0.97153, 1.0156, 0.95546, 0.89192, 0.92179, 0.92361, 0.87107, 0.96318, 0.89713, 0.93704, 0.95638, 0.91905, 0.91709, 0.92796, 1.0258, 0.93704, 0.94836, 1.0373, 0.95933, 1.0078, 0.95871, 0.94836, 0.96174, 0.92601, 0.9498, 0.98607, 0.95776, 0.95933, 1.05453, 1.0078, 0.98275, 0.9314, 0.95617, 0.91701, 1.05993, 0.9446, 0.78367, 0.9553, 1, 0.86832, 1.0128, 0.95871, 0.99394, 0.87548, 0.96361, 0.86774, 1.0078, 0.95871, 0.9446, 0.95871, 0.86774, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.94083, 0.97579, 0.94083, 0.97579, 0.94083, 0.97579, 0.90406, 0.94039, 0.96694, 1, 0.89903, 1, 1, 1, 0.93582, 0.93582, 0.93582, 1, 0.908, 0.908, 0.918, 0.94219, 0.94219, 0.96544, 1, 1.285, 1, 1, 0.81079, 0.81079, 1, 1, 0.74854, 1, 1, 1, 1, 0.99793, 1, 1, 1, 0.65, 1, 1.36145, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.17173, 1, 0.80535, 0.76169, 1.02058, 1.0732, 1.05486, 1, 1, 1.30692, 1.08595, 1.08595, 1, 1.08595, 1.08595, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.16161, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst MyriadProBoldMetrics = {\n  lineHeight: 1.2,\n  lineGap: 0.2\n};\nconst MyriadProBoldItalicFactors = [1.36898, 1, 1, 0.66227, 0.80779, 0.81625, 0.97276, 0.97276, 0.97733, 0.92222, 0.83266, 0.94292, 0.94292, 1.16148, 1.02058, 0.93582, 0.96694, 0.93582, 1.17337, 0.97276, 0.97276, 0.97276, 0.97276, 0.97276, 0.97276, 0.97276, 0.97276, 0.97276, 0.97276, 0.78076, 0.78076, 1.02058, 1.02058, 1.02058, 0.71541, 0.76813, 0.85576, 0.80591, 0.80729, 0.94299, 0.77512, 0.83655, 0.86523, 0.92222, 0.98621, 0.71743, 0.81698, 0.79726, 0.98558, 0.92222, 0.90637, 0.83809, 0.90637, 0.80729, 0.76463, 0.86275, 0.90699, 0.91605, 0.9154, 0.85308, 0.85458, 0.90531, 0.94292, 1.21296, 0.94292, 1.02058, 0.89903, 1.18616, 0.99613, 0.91677, 0.78216, 0.91677, 0.90083, 0.98796, 0.9135, 0.92168, 0.95381, 0.98981, 0.95298, 0.95381, 0.93459, 0.92168, 0.91513, 0.92004, 0.91677, 0.95077, 0.748, 1.04502, 0.91677, 0.92061, 0.94236, 0.89544, 0.89364, 0.9, 0.80687, 0.8578, 0.80687, 1.02058, 0.80779, 0.97276, 0.97276, 0.97276, 0.97276, 0.8578, 0.99973, 1.18616, 0.91339, 1.08074, 0.82891, 1.02058, 0.55509, 0.71526, 0.89022, 1.08595, 1, 1, 1.18616, 1, 0.96736, 0.93582, 1.18616, 1, 1.04864, 0.82711, 0.99043, 0.99043, 0.99043, 0.71541, 0.85576, 0.85576, 0.85576, 0.85576, 0.85576, 0.85576, 0.845, 0.80729, 0.77512, 0.77512, 0.77512, 0.77512, 0.98621, 0.98621, 0.98621, 0.98621, 0.95961, 0.92222, 0.90637, 0.90637, 0.90637, 0.90637, 0.90637, 1.02058, 0.90251, 0.90699, 0.90699, 0.90699, 0.90699, 0.85458, 0.83659, 0.94951, 0.99613, 0.99613, 0.99613, 0.99613, 0.99613, 0.99613, 0.85811, 0.78216, 0.90083, 0.90083, 0.90083, 0.90083, 0.95381, 0.95381, 0.95381, 0.95381, 0.9135, 0.92168, 0.91513, 0.91513, 0.91513, 0.91513, 0.91513, 1.08595, 0.91677, 0.91677, 0.91677, 0.91677, 0.91677, 0.89364, 0.92332, 0.89364, 0.85576, 0.99613, 0.85576, 0.99613, 0.85576, 0.99613, 0.80729, 0.78216, 0.80729, 0.78216, 0.80729, 0.78216, 0.80729, 0.78216, 0.94299, 0.76783, 0.95961, 0.91677, 0.77512, 0.90083, 0.77512, 0.90083, 0.77512, 0.90083, 0.77512, 0.90083, 0.77512, 0.90083, 0.86523, 0.9135, 0.86523, 0.9135, 0.86523, 0.9135, 1, 1, 0.92222, 0.92168, 0.92222, 0.92168, 0.98621, 0.95381, 0.98621, 0.95381, 0.98621, 0.95381, 0.98621, 0.95381, 0.98621, 0.95381, 0.86036, 0.97096, 0.71743, 0.98981, 1, 1, 0.95298, 0.79726, 0.95381, 1, 1, 0.79726, 0.6894, 0.79726, 0.74321, 0.81691, 1.0006, 0.92222, 0.92168, 1, 1, 0.92222, 0.92168, 0.79464, 0.92098, 0.92168, 0.90637, 0.91513, 0.90637, 0.91513, 0.90637, 0.91513, 0.909, 0.87514, 0.80729, 0.95077, 1, 1, 0.80729, 0.95077, 0.76463, 0.748, 0.76463, 0.748, 1, 1, 0.76463, 0.748, 1, 1, 0.86275, 0.72651, 0.86275, 1.04502, 0.90699, 0.91677, 0.90699, 0.91677, 0.90699, 0.91677, 0.90699, 0.91677, 0.90699, 0.91677, 0.90699, 0.91677, 0.9154, 0.94236, 0.85458, 0.89364, 0.85458, 0.90531, 0.9, 0.90531, 0.9, 0.90531, 0.9, 1, 0.97276, 0.85576, 0.99613, 0.845, 0.85811, 0.90251, 0.91677, 1, 1, 0.86275, 1.04502, 1.18616, 1.18616, 1.18616, 1.18616, 1.18616, 1.18616, 1.18616, 1.18616, 1.18616, 1.00899, 1.30628, 0.85576, 0.80178, 0.66862, 0.7927, 0.69323, 0.88127, 0.72459, 0.89711, 0.95381, 0.85576, 0.80591, 0.7805, 0.94729, 0.77512, 0.90531, 0.92222, 0.90637, 0.98621, 0.81698, 0.92655, 0.98558, 0.92222, 0.85359, 0.90637, 0.90976, 0.83809, 0.94523, 0.86275, 0.83509, 0.93157, 0.85308, 0.83392, 0.92346, 0.98621, 0.83509, 0.92886, 0.91324, 0.92168, 0.95381, 0.90646, 0.92886, 0.90557, 0.86847, 0.90276, 0.91324, 0.86842, 0.92168, 0.99531, 0.95381, 0.9224, 0.85408, 0.92699, 0.86847, 1.0051, 0.91513, 0.80487, 0.93481, 1, 0.88159, 1.05214, 0.90646, 0.97355, 0.81539, 0.89398, 0.85923, 0.95381, 0.90646, 0.91513, 0.90646, 0.85923, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9154, 0.94236, 0.9154, 0.94236, 0.9154, 0.94236, 0.85458, 0.89364, 0.96694, 1, 0.89903, 1, 1, 1, 0.91782, 0.91782, 0.91782, 1, 0.896, 0.896, 0.896, 0.9332, 0.9332, 0.95973, 1, 1.26, 1, 1, 0.80479, 0.80178, 1, 1, 0.85633, 1, 1, 1, 1, 0.97276, 1, 1, 1, 0.698, 1, 1.36145, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.14542, 1, 0.79199, 0.78694, 1.02058, 1.03493, 1.05486, 1, 1, 1.23026, 1.08595, 1.08595, 1, 1.08595, 1.08595, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.20006, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst MyriadProBoldItalicMetrics = {\n  lineHeight: 1.2,\n  lineGap: 0.2\n};\nconst MyriadProItalicFactors = [1.36898, 1, 1, 0.65507, 0.84943, 0.85639, 0.88465, 0.88465, 0.86936, 0.88307, 0.86948, 0.85283, 0.85283, 1.06383, 1.02058, 0.75945, 0.9219, 0.75945, 1.17337, 0.88465, 0.88465, 0.88465, 0.88465, 0.88465, 0.88465, 0.88465, 0.88465, 0.88465, 0.88465, 0.75945, 0.75945, 1.02058, 1.02058, 1.02058, 0.69046, 0.70926, 0.85158, 0.77812, 0.76852, 0.89591, 0.70466, 0.76125, 0.80094, 0.86822, 0.83864, 0.728, 0.77212, 0.79475, 0.93637, 0.87514, 0.8588, 0.76013, 0.8588, 0.72421, 0.69866, 0.77598, 0.85991, 0.80811, 0.87832, 0.78112, 0.77512, 0.8562, 1.0222, 1.18417, 1.0222, 1.27014, 0.89903, 1.15012, 0.93859, 0.94399, 0.846, 0.94399, 0.81453, 1.0186, 0.94219, 0.96017, 1.03075, 1.02175, 0.912, 1.03075, 0.96998, 0.96017, 0.93859, 0.94399, 0.94399, 0.95493, 0.746, 1.12658, 0.94578, 0.91, 0.979, 0.882, 0.882, 0.83, 0.85034, 0.83537, 0.85034, 1.02058, 0.70869, 0.88465, 0.88465, 0.88465, 0.88465, 0.83537, 0.90083, 1.15012, 0.9161, 0.94565, 0.73541, 1.02058, 0.53609, 0.69353, 0.79519, 1.08595, 1, 1, 1.15012, 1, 0.91974, 0.75945, 1.15012, 1, 0.9446, 0.73361, 0.9005, 0.9005, 0.9005, 0.62864, 0.85158, 0.85158, 0.85158, 0.85158, 0.85158, 0.85158, 0.773, 0.76852, 0.70466, 0.70466, 0.70466, 0.70466, 0.83864, 0.83864, 0.83864, 0.83864, 0.90561, 0.87514, 0.8588, 0.8588, 0.8588, 0.8588, 0.8588, 1.02058, 0.85751, 0.85991, 0.85991, 0.85991, 0.85991, 0.77512, 0.76013, 0.88075, 0.93859, 0.93859, 0.93859, 0.93859, 0.93859, 0.93859, 0.8075, 0.846, 0.81453, 0.81453, 0.81453, 0.81453, 0.82424, 0.82424, 0.82424, 0.82424, 0.9278, 0.96017, 0.93859, 0.93859, 0.93859, 0.93859, 0.93859, 1.08595, 0.8562, 0.94578, 0.94578, 0.94578, 0.94578, 0.882, 0.94578, 0.882, 0.85158, 0.93859, 0.85158, 0.93859, 0.85158, 0.93859, 0.76852, 0.846, 0.76852, 0.846, 0.76852, 0.846, 0.76852, 0.846, 0.89591, 0.8544, 0.90561, 0.94399, 0.70466, 0.81453, 0.70466, 0.81453, 0.70466, 0.81453, 0.70466, 0.81453, 0.70466, 0.81453, 0.80094, 0.94219, 0.80094, 0.94219, 0.80094, 0.94219, 1, 1, 0.86822, 0.96017, 0.86822, 0.96017, 0.83864, 0.82424, 0.83864, 0.82424, 0.83864, 0.82424, 0.83864, 1.03075, 0.83864, 0.82424, 0.81402, 1.02738, 0.728, 1.02175, 1, 1, 0.912, 0.79475, 1.03075, 1, 1, 0.79475, 0.83911, 0.79475, 0.66266, 0.80553, 1.06676, 0.87514, 0.96017, 1, 1, 0.87514, 0.96017, 0.86865, 0.87396, 0.96017, 0.8588, 0.93859, 0.8588, 0.93859, 0.8588, 0.93859, 0.867, 0.84759, 0.72421, 0.95493, 1, 1, 0.72421, 0.95493, 0.69866, 0.746, 0.69866, 0.746, 1, 1, 0.69866, 0.746, 1, 1, 0.77598, 0.88417, 0.77598, 1.12658, 0.85991, 0.94578, 0.85991, 0.94578, 0.85991, 0.94578, 0.85991, 0.94578, 0.85991, 0.94578, 0.85991, 0.94578, 0.87832, 0.979, 0.77512, 0.882, 0.77512, 0.8562, 0.83, 0.8562, 0.83, 0.8562, 0.83, 1, 0.88465, 0.85158, 0.93859, 0.773, 0.8075, 0.85751, 0.8562, 1, 1, 0.77598, 1.12658, 1.15012, 1.15012, 1.15012, 1.15012, 1.15012, 1.15313, 1.15012, 1.15012, 1.15012, 1.08106, 1.03901, 0.85158, 0.77025, 0.62264, 0.7646, 0.65351, 0.86026, 0.69461, 0.89947, 1.03075, 0.85158, 0.77812, 0.76449, 0.88836, 0.70466, 0.8562, 0.86822, 0.8588, 0.83864, 0.77212, 0.85308, 0.93637, 0.87514, 0.82352, 0.8588, 0.85701, 0.76013, 0.89058, 0.77598, 0.8156, 0.82565, 0.78112, 0.77899, 0.89386, 0.83864, 0.8156, 0.9486, 0.92388, 0.96186, 1.03075, 0.91123, 0.9486, 0.93298, 0.878, 0.93942, 0.92388, 0.84596, 0.96186, 0.95119, 1.03075, 0.922, 0.88787, 0.95829, 0.88, 0.93559, 0.93859, 0.78815, 0.93758, 1, 0.89217, 1.03737, 0.91123, 0.93969, 0.77487, 0.85769, 0.86799, 1.03075, 0.91123, 0.93859, 0.91123, 0.86799, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.87832, 0.979, 0.87832, 0.979, 0.87832, 0.979, 0.77512, 0.882, 0.9219, 1, 0.89903, 1, 1, 1, 0.87321, 0.87321, 0.87321, 1, 1.027, 1.027, 1.027, 0.86847, 0.86847, 0.79121, 1, 1.124, 1, 1, 0.73572, 0.73572, 1, 1, 0.85034, 1, 1, 1, 1, 0.88465, 1, 1, 1, 0.669, 1, 1.36145, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.04828, 1, 0.74948, 0.75187, 1.02058, 0.98391, 1.02119, 1, 1, 1.06233, 1.08595, 1.08595, 1, 1.08595, 1.08595, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.05233, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst MyriadProItalicMetrics = {\n  lineHeight: 1.2,\n  lineGap: 0.2\n};\nconst MyriadProRegularFactors = [1.36898, 1, 1, 0.76305, 0.82784, 0.94935, 0.89364, 0.92241, 0.89073, 0.90706, 0.98472, 0.85283, 0.85283, 1.0664, 1.02058, 0.74505, 0.9219, 0.74505, 1.23456, 0.92241, 0.92241, 0.92241, 0.92241, 0.92241, 0.92241, 0.92241, 0.92241, 0.92241, 0.92241, 0.74505, 0.74505, 1.02058, 1.02058, 1.02058, 0.73002, 0.72601, 0.91755, 0.8126, 0.80314, 0.92222, 0.73764, 0.79726, 0.83051, 0.90284, 0.86023, 0.74, 0.8126, 0.84869, 0.96518, 0.91115, 0.8858, 0.79761, 0.8858, 0.74498, 0.73914, 0.81363, 0.89591, 0.83659, 0.89633, 0.85608, 0.8111, 0.90531, 1.0222, 1.22736, 1.0222, 1.27014, 0.89903, 0.90088, 0.86667, 1.0231, 0.896, 1.01411, 0.90083, 1.05099, 1.00512, 0.99793, 1.05326, 1.09377, 0.938, 1.06226, 1.00119, 0.99793, 0.98714, 1.0231, 1.01231, 0.98196, 0.792, 1.19137, 0.99074, 0.962, 1.01915, 0.926, 0.942, 0.856, 0.85034, 0.92006, 0.85034, 1.02058, 0.69067, 0.92241, 0.92241, 0.92241, 0.92241, 0.92006, 0.9332, 0.90088, 0.91882, 0.93484, 0.75339, 1.02058, 0.56866, 0.54324, 0.79519, 1.08595, 1, 1, 0.90088, 1, 0.95325, 0.74505, 0.90088, 1, 0.97198, 0.75339, 0.91009, 0.91009, 0.91009, 0.66466, 0.91755, 0.91755, 0.91755, 0.91755, 0.91755, 0.91755, 0.788, 0.80314, 0.73764, 0.73764, 0.73764, 0.73764, 0.86023, 0.86023, 0.86023, 0.86023, 0.92915, 0.91115, 0.8858, 0.8858, 0.8858, 0.8858, 0.8858, 1.02058, 0.8858, 0.89591, 0.89591, 0.89591, 0.89591, 0.8111, 0.79611, 0.89713, 0.86667, 0.86667, 0.86667, 0.86667, 0.86667, 0.86667, 0.86936, 0.896, 0.90083, 0.90083, 0.90083, 0.90083, 0.84224, 0.84224, 0.84224, 0.84224, 0.97276, 0.99793, 0.98714, 0.98714, 0.98714, 0.98714, 0.98714, 1.08595, 0.89876, 0.99074, 0.99074, 0.99074, 0.99074, 0.942, 1.0231, 0.942, 0.91755, 0.86667, 0.91755, 0.86667, 0.91755, 0.86667, 0.80314, 0.896, 0.80314, 0.896, 0.80314, 0.896, 0.80314, 0.896, 0.92222, 0.93372, 0.92915, 1.01411, 0.73764, 0.90083, 0.73764, 0.90083, 0.73764, 0.90083, 0.73764, 0.90083, 0.73764, 0.90083, 0.83051, 1.00512, 0.83051, 1.00512, 0.83051, 1.00512, 1, 1, 0.90284, 0.99793, 0.90976, 0.99793, 0.86023, 0.84224, 0.86023, 0.84224, 0.86023, 0.84224, 0.86023, 1.05326, 0.86023, 0.84224, 0.82873, 1.07469, 0.74, 1.09377, 1, 1, 0.938, 0.84869, 1.06226, 1, 1, 0.84869, 0.83704, 0.84869, 0.81441, 0.85588, 1.08927, 0.91115, 0.99793, 1, 1, 0.91115, 0.99793, 0.91887, 0.90991, 0.99793, 0.8858, 0.98714, 0.8858, 0.98714, 0.8858, 0.98714, 0.894, 0.91434, 0.74498, 0.98196, 1, 1, 0.74498, 0.98196, 0.73914, 0.792, 0.73914, 0.792, 1, 1, 0.73914, 0.792, 1, 1, 0.81363, 0.904, 0.81363, 1.19137, 0.89591, 0.99074, 0.89591, 0.99074, 0.89591, 0.99074, 0.89591, 0.99074, 0.89591, 0.99074, 0.89591, 0.99074, 0.89633, 1.01915, 0.8111, 0.942, 0.8111, 0.90531, 0.856, 0.90531, 0.856, 0.90531, 0.856, 1, 0.92241, 0.91755, 0.86667, 0.788, 0.86936, 0.8858, 0.89876, 1, 1, 0.81363, 1.19137, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90388, 1.03901, 0.92138, 0.78105, 0.7154, 0.86169, 0.80513, 0.94007, 0.82528, 0.98612, 1.06226, 0.91755, 0.8126, 0.81884, 0.92819, 0.73764, 0.90531, 0.90284, 0.8858, 0.86023, 0.8126, 0.91172, 0.96518, 0.91115, 0.83089, 0.8858, 0.87791, 0.79761, 0.89297, 0.81363, 0.88157, 0.89992, 0.85608, 0.81992, 0.94307, 0.86023, 0.88157, 0.95308, 0.98699, 0.99793, 1.06226, 0.95817, 0.95308, 0.97358, 0.928, 0.98088, 0.98699, 0.92761, 0.99793, 0.96017, 1.06226, 0.986, 0.944, 0.95978, 0.938, 0.96705, 0.98714, 0.80442, 0.98972, 1, 0.89762, 1.04552, 0.95817, 0.99007, 0.87064, 0.91879, 0.88888, 1.06226, 0.95817, 0.98714, 0.95817, 0.88888, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.89633, 1.01915, 0.89633, 1.01915, 0.89633, 1.01915, 0.8111, 0.942, 0.9219, 1, 0.89903, 1, 1, 1, 0.93173, 0.93173, 0.93173, 1, 1.06304, 1.06304, 1.06904, 0.89903, 0.89903, 0.80549, 1, 1.156, 1, 1, 0.76575, 0.76575, 1, 1, 0.72458, 1, 1, 1, 1, 0.92241, 1, 1, 1, 0.619, 1, 1.36145, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.07257, 1, 0.74705, 0.71119, 1.02058, 1.024, 1.02119, 1, 1, 1.1536, 1.08595, 1.08595, 1, 1.08595, 1.08595, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.05638, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst MyriadProRegularMetrics = {\n  lineHeight: 1.2,\n  lineGap: 0.2\n};\n\n;// CONCATENATED MODULE: ./src/core/segoeui_factors.js\nconst SegoeuiBoldFactors = [1.76738, 1, 1, 0.99297, 0.9824, 1.04016, 1.06497, 1.03424, 0.97529, 1.17647, 1.23203, 1.1085, 1.1085, 1.16939, 1.2107, 0.9754, 1.21408, 0.9754, 1.59578, 1.03424, 1.03424, 1.03424, 1.03424, 1.03424, 1.03424, 1.03424, 1.03424, 1.03424, 1.03424, 0.81378, 0.81378, 1.2107, 1.2107, 1.2107, 0.71703, 0.97847, 0.97363, 0.88776, 0.8641, 1.02096, 0.79795, 0.85132, 0.914, 1.06085, 1.1406, 0.8007, 0.89858, 0.83693, 1.14889, 1.09398, 0.97489, 0.92094, 0.97489, 0.90399, 0.84041, 0.95923, 1.00135, 1, 1.06467, 0.98243, 0.90996, 0.99361, 1.1085, 1.56942, 1.1085, 1.2107, 0.74627, 0.94282, 0.96752, 1.01519, 0.86304, 1.01359, 0.97278, 1.15103, 1.01359, 0.98561, 1.02285, 1.02285, 1.00527, 1.02285, 1.0302, 0.99041, 1.0008, 1.01519, 1.01359, 1.02258, 0.79104, 1.16862, 0.99041, 0.97454, 1.02511, 0.99298, 0.96752, 0.95801, 0.94856, 1.16579, 0.94856, 1.2107, 0.9824, 1.03424, 1.03424, 1, 1.03424, 1.16579, 0.8727, 1.3871, 1.18622, 1.10818, 1.04478, 1.2107, 1.18622, 0.75155, 0.94994, 1.28826, 1.21408, 1.21408, 0.91056, 1, 0.91572, 0.9754, 0.64663, 1.18328, 1.24866, 1.04478, 1.14169, 1.15749, 1.17389, 0.71703, 0.97363, 0.97363, 0.97363, 0.97363, 0.97363, 0.97363, 0.93506, 0.8641, 0.79795, 0.79795, 0.79795, 0.79795, 1.1406, 1.1406, 1.1406, 1.1406, 1.02096, 1.09398, 0.97426, 0.97426, 0.97426, 0.97426, 0.97426, 1.2107, 0.97489, 1.00135, 1.00135, 1.00135, 1.00135, 0.90996, 0.92094, 1.02798, 0.96752, 0.96752, 0.96752, 0.96752, 0.96752, 0.96752, 0.93136, 0.86304, 0.97278, 0.97278, 0.97278, 0.97278, 1.02285, 1.02285, 1.02285, 1.02285, 0.97122, 0.99041, 1, 1, 1, 1, 1, 1.28826, 1.0008, 0.99041, 0.99041, 0.99041, 0.99041, 0.96752, 1.01519, 0.96752, 0.97363, 0.96752, 0.97363, 0.96752, 0.97363, 0.96752, 0.8641, 0.86304, 0.8641, 0.86304, 0.8641, 0.86304, 0.8641, 0.86304, 1.02096, 1.03057, 1.02096, 1.03517, 0.79795, 0.97278, 0.79795, 0.97278, 0.79795, 0.97278, 0.79795, 0.97278, 0.79795, 0.97278, 0.914, 1.01359, 0.914, 1.01359, 0.914, 1.01359, 1, 1, 1.06085, 0.98561, 1.06085, 1.00879, 1.1406, 1.02285, 1.1406, 1.02285, 1.1406, 1.02285, 1.1406, 1.02285, 1.1406, 1.02285, 0.97138, 1.08692, 0.8007, 1.02285, 1, 1, 1.00527, 0.83693, 1.02285, 1, 1, 0.83693, 0.9455, 0.83693, 0.90418, 0.83693, 1.13005, 1.09398, 0.99041, 1, 1, 1.09398, 0.99041, 0.96692, 1.09251, 0.99041, 0.97489, 1.0008, 0.97489, 1.0008, 0.97489, 1.0008, 0.93994, 0.97931, 0.90399, 1.02258, 1, 1, 0.90399, 1.02258, 0.84041, 0.79104, 0.84041, 0.79104, 0.84041, 0.79104, 0.84041, 0.79104, 1, 1, 0.95923, 1.07034, 0.95923, 1.16862, 1.00135, 0.99041, 1.00135, 0.99041, 1.00135, 0.99041, 1.00135, 0.99041, 1.00135, 0.99041, 1.00135, 0.99041, 1.06467, 1.02511, 0.90996, 0.96752, 0.90996, 0.99361, 0.95801, 0.99361, 0.95801, 0.99361, 0.95801, 1.07733, 1.03424, 0.97363, 0.96752, 0.93506, 0.93136, 0.97489, 1.0008, 1, 1, 0.95923, 1.16862, 1.15103, 1.15103, 1.01173, 1.03959, 0.75953, 0.81378, 0.79912, 1.15103, 1.21994, 0.95161, 0.87815, 1.01149, 0.81525, 0.7676, 0.98167, 1.01134, 1.02546, 0.84097, 1.03089, 1.18102, 0.97363, 0.88776, 0.85134, 0.97826, 0.79795, 0.99361, 1.06085, 0.97489, 1.1406, 0.89858, 1.0388, 1.14889, 1.09398, 0.86039, 0.97489, 1.0595, 0.92094, 0.94793, 0.95923, 0.90996, 0.99346, 0.98243, 1.02112, 0.95493, 1.1406, 0.90996, 1.03574, 1.02597, 1.0008, 1.18102, 1.06628, 1.03574, 1.0192, 1.01932, 1.00886, 0.97531, 1.0106, 1.0008, 1.13189, 1.18102, 1.02277, 0.98683, 1.0016, 0.99561, 1.07237, 1.0008, 0.90434, 0.99921, 0.93803, 0.8965, 1.23085, 1.06628, 1.04983, 0.96268, 1.0499, 0.98439, 1.18102, 1.06628, 1.0008, 1.06628, 0.98439, 0.79795, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.09466, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.97278, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.02065, 1, 1, 1, 1, 1, 1, 1.06467, 1.02511, 1.06467, 1.02511, 1.06467, 1.02511, 0.90996, 0.96752, 1, 1.21408, 0.89903, 1, 1, 0.75155, 1.04394, 1.04394, 1.04394, 1.04394, 0.98633, 0.98633, 0.98633, 0.73047, 0.73047, 1.20642, 0.91211, 1.25635, 1.222, 1.02956, 1.03372, 1.03372, 0.96039, 1.24633, 1, 1.12454, 0.93503, 1.03424, 1.19687, 1.03424, 1, 1, 1, 0.771, 1, 1, 1.15749, 1.15749, 1.15749, 1.10948, 0.86279, 0.94434, 0.86279, 0.94434, 0.86182, 1, 1, 1.16897, 1, 0.96085, 0.90137, 1.2107, 1.18416, 1.13973, 0.69825, 0.9716, 2.10339, 1.29004, 1.29004, 1.21172, 1.29004, 1.29004, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.42603, 1, 0.99862, 0.99862, 1, 0.87025, 0.87025, 0.87025, 0.87025, 1.18874, 1.42603, 1, 1.42603, 1.42603, 0.99862, 1, 1, 1, 1, 1, 1.2886, 1.04315, 1.15296, 1.34163, 1, 1, 1, 1.09193, 1.09193, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst SegoeuiBoldMetrics = {\n  lineHeight: 1.33008,\n  lineGap: 0\n};\nconst SegoeuiBoldItalicFactors = [1.76738, 1, 1, 0.98946, 1.03959, 1.04016, 1.02809, 1.036, 0.97639, 1.10953, 1.23203, 1.11144, 1.11144, 1.16939, 1.21237, 0.9754, 1.21261, 0.9754, 1.59754, 1.036, 1.036, 1.036, 1.036, 1.036, 1.036, 1.036, 1.036, 1.036, 1.036, 0.81378, 0.81378, 1.21237, 1.21237, 1.21237, 0.73541, 0.97847, 0.97363, 0.89723, 0.87897, 1.0426, 0.79429, 0.85292, 0.91149, 1.05815, 1.1406, 0.79631, 0.90128, 0.83853, 1.04396, 1.10615, 0.97552, 0.94436, 0.97552, 0.88641, 0.80527, 0.96083, 1.00135, 1, 1.06777, 0.9817, 0.91142, 0.99361, 1.11144, 1.57293, 1.11144, 1.21237, 0.74627, 1.31818, 1.06585, 0.97042, 0.83055, 0.97042, 0.93503, 1.1261, 0.97042, 0.97922, 1.14236, 0.94552, 1.01054, 1.14236, 1.02471, 0.97922, 0.94165, 0.97042, 0.97042, 1.0276, 0.78929, 1.1261, 0.97922, 0.95874, 1.02197, 0.98507, 0.96752, 0.97168, 0.95107, 1.16579, 0.95107, 1.21237, 1.03959, 1.036, 1.036, 1, 1.036, 1.16579, 0.87357, 1.31818, 1.18754, 1.26781, 1.05356, 1.21237, 1.18622, 0.79487, 0.94994, 1.29004, 1.24047, 1.24047, 1.31818, 1, 0.91484, 0.9754, 1.31818, 1.1349, 1.24866, 1.05356, 1.13934, 1.15574, 1.17389, 0.73541, 0.97363, 0.97363, 0.97363, 0.97363, 0.97363, 0.97363, 0.94385, 0.87897, 0.79429, 0.79429, 0.79429, 0.79429, 1.1406, 1.1406, 1.1406, 1.1406, 1.0426, 1.10615, 0.97552, 0.97552, 0.97552, 0.97552, 0.97552, 1.21237, 0.97552, 1.00135, 1.00135, 1.00135, 1.00135, 0.91142, 0.94436, 0.98721, 1.06585, 1.06585, 1.06585, 1.06585, 1.06585, 1.06585, 0.96705, 0.83055, 0.93503, 0.93503, 0.93503, 0.93503, 1.14236, 1.14236, 1.14236, 1.14236, 0.93125, 0.97922, 0.94165, 0.94165, 0.94165, 0.94165, 0.94165, 1.29004, 0.94165, 0.97922, 0.97922, 0.97922, 0.97922, 0.96752, 0.97042, 0.96752, 0.97363, 1.06585, 0.97363, 1.06585, 0.97363, 1.06585, 0.87897, 0.83055, 0.87897, 0.83055, 0.87897, 0.83055, 0.87897, 0.83055, 1.0426, 1.0033, 1.0426, 0.97042, 0.79429, 0.93503, 0.79429, 0.93503, 0.79429, 0.93503, 0.79429, 0.93503, 0.79429, 0.93503, 0.91149, 0.97042, 0.91149, 0.97042, 0.91149, 0.97042, 1, 1, 1.05815, 0.97922, 1.05815, 0.97922, 1.1406, 1.14236, 1.1406, 1.14236, 1.1406, 1.14236, 1.1406, 1.14236, 1.1406, 1.14236, 0.97441, 1.04302, 0.79631, 1.01582, 1, 1, 1.01054, 0.83853, 1.14236, 1, 1, 0.83853, 1.09125, 0.83853, 0.90418, 0.83853, 1.19508, 1.10615, 0.97922, 1, 1, 1.10615, 0.97922, 1.01034, 1.10466, 0.97922, 0.97552, 0.94165, 0.97552, 0.94165, 0.97552, 0.94165, 0.91602, 0.91981, 0.88641, 1.0276, 1, 1, 0.88641, 1.0276, 0.80527, 0.78929, 0.80527, 0.78929, 0.80527, 0.78929, 0.80527, 0.78929, 1, 1, 0.96083, 1.05403, 0.95923, 1.16862, 1.00135, 0.97922, 1.00135, 0.97922, 1.00135, 0.97922, 1.00135, 0.97922, 1.00135, 0.97922, 1.00135, 0.97922, 1.06777, 1.02197, 0.91142, 0.96752, 0.91142, 0.99361, 0.97168, 0.99361, 0.97168, 0.99361, 0.97168, 1.23199, 1.036, 0.97363, 1.06585, 0.94385, 0.96705, 0.97552, 0.94165, 1, 1, 0.96083, 1.1261, 1.31818, 1.31818, 1.31818, 1.31818, 1.31818, 1.31818, 1.31818, 1.31818, 1.31818, 0.95161, 1.27126, 1.00811, 0.83284, 0.77702, 0.99137, 0.95253, 1.0347, 0.86142, 1.07205, 1.14236, 0.97363, 0.89723, 0.86869, 1.09818, 0.79429, 0.99361, 1.05815, 0.97552, 1.1406, 0.90128, 1.06662, 1.04396, 1.10615, 0.84918, 0.97552, 1.04694, 0.94436, 0.98015, 0.96083, 0.91142, 1.00356, 0.9817, 1.01945, 0.98999, 1.1406, 0.91142, 1.04961, 0.9898, 1.00639, 1.14236, 1.07514, 1.04961, 0.99607, 1.02897, 1.008, 0.9898, 0.95134, 1.00639, 1.11121, 1.14236, 1.00518, 0.97981, 1.02186, 1, 1.08578, 0.94165, 0.99314, 0.98387, 0.93028, 0.93377, 1.35125, 1.07514, 1.10687, 0.93491, 1.04232, 1.00351, 1.14236, 1.07514, 0.94165, 1.07514, 1.00351, 0.79429, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.09097, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.93503, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.96609, 1, 1, 1, 1, 1, 1, 1.06777, 1.02197, 1.06777, 1.02197, 1.06777, 1.02197, 0.91142, 0.96752, 1, 1.21261, 0.89903, 1, 1, 0.75155, 1.04745, 1.04745, 1.04745, 1.04394, 0.98633, 0.98633, 0.98633, 0.72959, 0.72959, 1.20502, 0.91406, 1.26514, 1.222, 1.02956, 1.03372, 1.03372, 0.96039, 1.24633, 1, 1.09125, 0.93327, 1.03336, 1.16541, 1.036, 1, 1, 1, 0.771, 1, 1, 1.15574, 1.15574, 1.15574, 1.15574, 0.86364, 0.94434, 0.86279, 0.94434, 0.86224, 1, 1, 1.16798, 1, 0.96085, 0.90068, 1.21237, 1.18416, 1.13904, 0.69825, 0.9716, 2.10339, 1.29004, 1.29004, 1.21339, 1.29004, 1.29004, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.42603, 1, 0.99862, 0.99862, 1, 0.87025, 0.87025, 0.87025, 0.87025, 1.18775, 1.42603, 1, 1.42603, 1.42603, 0.99862, 1, 1, 1, 1, 1, 1.2886, 1.04315, 1.15296, 1.34163, 1, 1, 1, 1.13269, 1.13269, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst SegoeuiBoldItalicMetrics = {\n  lineHeight: 1.33008,\n  lineGap: 0\n};\nconst SegoeuiItalicFactors = [1.76738, 1, 1, 0.98946, 1.14763, 1.05365, 1.06234, 0.96927, 0.92586, 1.15373, 1.18414, 0.91349, 0.91349, 1.07403, 1.17308, 0.78383, 1.20088, 0.78383, 1.42531, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.78383, 0.78383, 1.17308, 1.17308, 1.17308, 0.77349, 0.94565, 0.94729, 0.85944, 0.88506, 0.9858, 0.74817, 0.80016, 0.88449, 0.98039, 0.95782, 0.69238, 0.89898, 0.83231, 0.98183, 1.03989, 0.96924, 0.86237, 0.96924, 0.80595, 0.74524, 0.86091, 0.95402, 0.94143, 0.98448, 0.8858, 0.83089, 0.93285, 1.0949, 1.39016, 1.0949, 1.45994, 0.74627, 1.04839, 0.97454, 0.97454, 0.87207, 0.97454, 0.87533, 1.06151, 0.97454, 1.00176, 1.16484, 1.08132, 0.98047, 1.16484, 1.02989, 1.01054, 0.96225, 0.97454, 0.97454, 1.06598, 0.79004, 1.16344, 1.00351, 0.94629, 0.9973, 0.91016, 0.96777, 0.9043, 0.91082, 0.92481, 0.91082, 1.17308, 0.95748, 0.96927, 0.96927, 1, 0.96927, 0.92481, 0.80597, 1.04839, 1.23393, 1.1781, 0.9245, 1.17308, 1.20808, 0.63218, 0.94261, 1.24822, 1.09971, 1.09971, 1.04839, 1, 0.85273, 0.78032, 1.04839, 1.09971, 1.22326, 0.9245, 1.09836, 1.13525, 1.15222, 0.70424, 0.94729, 0.94729, 0.94729, 0.94729, 0.94729, 0.94729, 0.85498, 0.88506, 0.74817, 0.74817, 0.74817, 0.74817, 0.95782, 0.95782, 0.95782, 0.95782, 0.9858, 1.03989, 0.96924, 0.96924, 0.96924, 0.96924, 0.96924, 1.17308, 0.96924, 0.95402, 0.95402, 0.95402, 0.95402, 0.83089, 0.86237, 0.88409, 0.97454, 0.97454, 0.97454, 0.97454, 0.97454, 0.97454, 0.92916, 0.87207, 0.87533, 0.87533, 0.87533, 0.87533, 0.93146, 0.93146, 0.93146, 0.93146, 0.93854, 1.01054, 0.96225, 0.96225, 0.96225, 0.96225, 0.96225, 1.24822, 0.8761, 1.00351, 1.00351, 1.00351, 1.00351, 0.96777, 0.97454, 0.96777, 0.94729, 0.97454, 0.94729, 0.97454, 0.94729, 0.97454, 0.88506, 0.87207, 0.88506, 0.87207, 0.88506, 0.87207, 0.88506, 0.87207, 0.9858, 0.95391, 0.9858, 0.97454, 0.74817, 0.87533, 0.74817, 0.87533, 0.74817, 0.87533, 0.74817, 0.87533, 0.74817, 0.87533, 0.88449, 0.97454, 0.88449, 0.97454, 0.88449, 0.97454, 1, 1, 0.98039, 1.00176, 0.98039, 1.00176, 0.95782, 0.93146, 0.95782, 0.93146, 0.95782, 0.93146, 0.95782, 1.16484, 0.95782, 0.93146, 0.84421, 1.12761, 0.69238, 1.08132, 1, 1, 0.98047, 0.83231, 1.16484, 1, 1, 0.84723, 1.04861, 0.84723, 0.78755, 0.83231, 1.23736, 1.03989, 1.01054, 1, 1, 1.03989, 1.01054, 0.9857, 1.03849, 1.01054, 0.96924, 0.96225, 0.96924, 0.96225, 0.96924, 0.96225, 0.92383, 0.90171, 0.80595, 1.06598, 1, 1, 0.80595, 1.06598, 0.74524, 0.79004, 0.74524, 0.79004, 0.74524, 0.79004, 0.74524, 0.79004, 1, 1, 0.86091, 1.02759, 0.85771, 1.16344, 0.95402, 1.00351, 0.95402, 1.00351, 0.95402, 1.00351, 0.95402, 1.00351, 0.95402, 1.00351, 0.95402, 1.00351, 0.98448, 0.9973, 0.83089, 0.96777, 0.83089, 0.93285, 0.9043, 0.93285, 0.9043, 0.93285, 0.9043, 1.31868, 0.96927, 0.94729, 0.97454, 0.85498, 0.92916, 0.96924, 0.8761, 1, 1, 0.86091, 1.16344, 1.04839, 1.04839, 1.04839, 1.04839, 1.04839, 1.04839, 1.04839, 1.04839, 1.04839, 0.81965, 0.81965, 0.94729, 0.78032, 0.71022, 0.90883, 0.84171, 0.99877, 0.77596, 1.05734, 1.2, 0.94729, 0.85944, 0.82791, 0.9607, 0.74817, 0.93285, 0.98039, 0.96924, 0.95782, 0.89898, 0.98316, 0.98183, 1.03989, 0.78614, 0.96924, 0.97642, 0.86237, 0.86075, 0.86091, 0.83089, 0.90082, 0.8858, 0.97296, 1.01284, 0.95782, 0.83089, 1.0976, 1.04, 1.03342, 1.2, 1.0675, 1.0976, 0.98205, 1.03809, 1.05097, 1.04, 0.95364, 1.03342, 1.05401, 1.2, 1.02148, 1.0119, 1.04724, 1.0127, 1.02732, 0.96225, 0.8965, 0.97783, 0.93574, 0.94818, 1.30679, 1.0675, 1.11826, 0.99821, 1.0557, 1.0326, 1.2, 1.0675, 0.96225, 1.0675, 1.0326, 0.74817, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.03754, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.87533, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.98705, 1, 1, 1, 1, 1, 1, 0.98448, 0.9973, 0.98448, 0.9973, 0.98448, 0.9973, 0.83089, 0.96777, 1, 1.20088, 0.89903, 1, 1, 0.75155, 0.94945, 0.94945, 0.94945, 0.94945, 1.12317, 1.12317, 1.12317, 0.67603, 0.67603, 1.15621, 0.73584, 1.21191, 1.22135, 1.06483, 0.94868, 0.94868, 0.95996, 1.24633, 1, 1.07497, 0.87709, 0.96927, 1.01473, 0.96927, 1, 1, 1, 0.77295, 1, 1, 1.09836, 1.09836, 1.09836, 1.01522, 0.86321, 0.94434, 0.8649, 0.94434, 0.86182, 1, 1, 1.083, 1, 0.91578, 0.86438, 1.17308, 1.18416, 1.14589, 0.69825, 0.97622, 1.96791, 1.24822, 1.24822, 1.17308, 1.24822, 1.24822, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.42603, 1, 0.99862, 0.99862, 1, 0.87025, 0.87025, 0.87025, 0.87025, 1.17984, 1.42603, 1, 1.42603, 1.42603, 0.99862, 1, 1, 1, 1, 1, 1.2886, 1.04315, 1.15296, 1.34163, 1, 1, 1, 1.10742, 1.10742, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst SegoeuiItalicMetrics = {\n  lineHeight: 1.33008,\n  lineGap: 0\n};\nconst SegoeuiRegularFactors = [1.76738, 1, 1, 0.98594, 1.02285, 1.10454, 1.06234, 0.96927, 0.92037, 1.19985, 1.2046, 0.90616, 0.90616, 1.07152, 1.1714, 0.78032, 1.20088, 0.78032, 1.40246, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.78032, 0.78032, 1.1714, 1.1714, 1.1714, 0.80597, 0.94084, 0.96706, 0.85944, 0.85734, 0.97093, 0.75842, 0.79936, 0.88198, 0.9831, 0.95782, 0.71387, 0.86969, 0.84636, 1.07796, 1.03584, 0.96924, 0.83968, 0.96924, 0.82826, 0.79649, 0.85771, 0.95132, 0.93119, 0.98965, 0.88433, 0.8287, 0.93365, 1.08612, 1.3638, 1.08612, 1.45786, 0.74627, 0.80499, 0.91484, 1.05707, 0.92383, 1.05882, 0.9403, 1.12654, 1.05882, 1.01756, 1.09011, 1.09011, 0.99414, 1.09011, 1.034, 1.01756, 1.05356, 1.05707, 1.05882, 1.04399, 0.84863, 1.21968, 1.01756, 0.95801, 1.00068, 0.91797, 0.96777, 0.9043, 0.90351, 0.92105, 0.90351, 1.1714, 0.85337, 0.96927, 0.96927, 0.99912, 0.96927, 0.92105, 0.80597, 1.2434, 1.20808, 1.05937, 0.90957, 1.1714, 1.20808, 0.75155, 0.94261, 1.24644, 1.09971, 1.09971, 0.84751, 1, 0.85273, 0.78032, 0.61584, 1.05425, 1.17914, 0.90957, 1.08665, 1.11593, 1.14169, 0.73381, 0.96706, 0.96706, 0.96706, 0.96706, 0.96706, 0.96706, 0.86035, 0.85734, 0.75842, 0.75842, 0.75842, 0.75842, 0.95782, 0.95782, 0.95782, 0.95782, 0.97093, 1.03584, 0.96924, 0.96924, 0.96924, 0.96924, 0.96924, 1.1714, 0.96924, 0.95132, 0.95132, 0.95132, 0.95132, 0.8287, 0.83968, 0.89049, 0.91484, 0.91484, 0.91484, 0.91484, 0.91484, 0.91484, 0.93575, 0.92383, 0.9403, 0.9403, 0.9403, 0.9403, 0.8717, 0.8717, 0.8717, 0.8717, 1.00527, 1.01756, 1.05356, 1.05356, 1.05356, 1.05356, 1.05356, 1.24644, 0.95923, 1.01756, 1.01756, 1.01756, 1.01756, 0.96777, 1.05707, 0.96777, 0.96706, 0.91484, 0.96706, 0.91484, 0.96706, 0.91484, 0.85734, 0.92383, 0.85734, 0.92383, 0.85734, 0.92383, 0.85734, 0.92383, 0.97093, 1.0969, 0.97093, 1.05882, 0.75842, 0.9403, 0.75842, 0.9403, 0.75842, 0.9403, 0.75842, 0.9403, 0.75842, 0.9403, 0.88198, 1.05882, 0.88198, 1.05882, 0.88198, 1.05882, 1, 1, 0.9831, 1.01756, 0.9831, 1.01756, 0.95782, 0.8717, 0.95782, 0.8717, 0.95782, 0.8717, 0.95782, 1.09011, 0.95782, 0.8717, 0.84784, 1.11551, 0.71387, 1.09011, 1, 1, 0.99414, 0.84636, 1.09011, 1, 1, 0.84636, 1.0536, 0.84636, 0.94298, 0.84636, 1.23297, 1.03584, 1.01756, 1, 1, 1.03584, 1.01756, 1.00323, 1.03444, 1.01756, 0.96924, 1.05356, 0.96924, 1.05356, 0.96924, 1.05356, 0.93066, 0.98293, 0.82826, 1.04399, 1, 1, 0.82826, 1.04399, 0.79649, 0.84863, 0.79649, 0.84863, 0.79649, 0.84863, 0.79649, 0.84863, 1, 1, 0.85771, 1.17318, 0.85771, 1.21968, 0.95132, 1.01756, 0.95132, 1.01756, 0.95132, 1.01756, 0.95132, 1.01756, 0.95132, 1.01756, 0.95132, 1.01756, 0.98965, 1.00068, 0.8287, 0.96777, 0.8287, 0.93365, 0.9043, 0.93365, 0.9043, 0.93365, 0.9043, 1.08571, 0.96927, 0.96706, 0.91484, 0.86035, 0.93575, 0.96924, 0.95923, 1, 1, 0.85771, 1.21968, 1.11437, 1.11437, 0.93109, 0.91202, 0.60411, 0.84164, 0.55572, 1.01173, 0.97361, 0.81818, 0.81818, 0.96635, 0.78032, 0.72727, 0.92366, 0.98601, 1.03405, 0.77968, 1.09799, 1.2, 0.96706, 0.85944, 0.85638, 0.96491, 0.75842, 0.93365, 0.9831, 0.96924, 0.95782, 0.86969, 0.94152, 1.07796, 1.03584, 0.78437, 0.96924, 0.98715, 0.83968, 0.83491, 0.85771, 0.8287, 0.94492, 0.88433, 0.9287, 1.0098, 0.95782, 0.8287, 1.0625, 0.98248, 1.03424, 1.2, 1.01071, 1.0625, 0.95246, 1.03809, 1.04912, 0.98248, 1.00221, 1.03424, 1.05443, 1.2, 1.04785, 0.99609, 1.00169, 1.05176, 0.99346, 1.05356, 0.9087, 1.03004, 0.95542, 0.93117, 1.23362, 1.01071, 1.07831, 1.02512, 1.05205, 1.03502, 1.2, 1.01071, 1.05356, 1.01071, 1.03502, 0.75842, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.03719, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9403, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.04021, 1, 1, 1, 1, 1, 1, 0.98965, 1.00068, 0.98965, 1.00068, 0.98965, 1.00068, 0.8287, 0.96777, 1, 1.20088, 0.89903, 1, 1, 0.75155, 1.03077, 1.03077, 1.03077, 1.03077, 1.13196, 1.13196, 1.13196, 0.67428, 0.67428, 1.16039, 0.73291, 1.20996, 1.22135, 1.06483, 0.94868, 0.94868, 0.95996, 1.24633, 1, 1.07497, 0.87796, 0.96927, 1.01518, 0.96927, 1, 1, 1, 0.77295, 1, 1, 1.10539, 1.10539, 1.11358, 1.06967, 0.86279, 0.94434, 0.86279, 0.94434, 0.86182, 1, 1, 1.083, 1, 0.91578, 0.86507, 1.1714, 1.18416, 1.14589, 0.69825, 0.97622, 1.9697, 1.24822, 1.24822, 1.17238, 1.24822, 1.24822, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.42603, 1, 0.99862, 0.99862, 1, 0.87025, 0.87025, 0.87025, 0.87025, 1.18083, 1.42603, 1, 1.42603, 1.42603, 0.99862, 1, 1, 1, 1, 1, 1.2886, 1.04315, 1.15296, 1.34163, 1, 1, 1, 1.10938, 1.10938, 1, 1, 1, 1.05425, 1.09971, 1.09971, 1.09971, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];\nconst SegoeuiRegularMetrics = {\n  lineHeight: 1.33008,\n  lineGap: 0\n};\n\n;// CONCATENATED MODULE: ./src/core/xfa_fonts.js\n\n\n\n\n\n\n\n\nconst getXFAFontMap = getLookupTableFactory(function (t) {\n  t[\"MyriadPro-Regular\"] = t[\"PdfJS-Fallback-Regular\"] = {\n    name: \"LiberationSans-Regular\",\n    factors: MyriadProRegularFactors,\n    baseWidths: LiberationSansRegularWidths,\n    baseMapping: LiberationSansRegularMapping,\n    metrics: MyriadProRegularMetrics\n  };\n  t[\"MyriadPro-Bold\"] = t[\"PdfJS-Fallback-Bold\"] = {\n    name: \"LiberationSans-Bold\",\n    factors: MyriadProBoldFactors,\n    baseWidths: LiberationSansBoldWidths,\n    baseMapping: LiberationSansBoldMapping,\n    metrics: MyriadProBoldMetrics\n  };\n  t[\"MyriadPro-It\"] = t[\"MyriadPro-Italic\"] = t[\"PdfJS-Fallback-Italic\"] = {\n    name: \"LiberationSans-Italic\",\n    factors: MyriadProItalicFactors,\n    baseWidths: LiberationSansItalicWidths,\n    baseMapping: LiberationSansItalicMapping,\n    metrics: MyriadProItalicMetrics\n  };\n  t[\"MyriadPro-BoldIt\"] = t[\"MyriadPro-BoldItalic\"] = t[\"PdfJS-Fallback-BoldItalic\"] = {\n    name: \"LiberationSans-BoldItalic\",\n    factors: MyriadProBoldItalicFactors,\n    baseWidths: LiberationSansBoldItalicWidths,\n    baseMapping: LiberationSansBoldItalicMapping,\n    metrics: MyriadProBoldItalicMetrics\n  };\n  t.ArialMT = t.Arial = t[\"Arial-Regular\"] = {\n    name: \"LiberationSans-Regular\",\n    baseWidths: LiberationSansRegularWidths,\n    baseMapping: LiberationSansRegularMapping\n  };\n  t[\"Arial-BoldMT\"] = t[\"Arial-Bold\"] = {\n    name: \"LiberationSans-Bold\",\n    baseWidths: LiberationSansBoldWidths,\n    baseMapping: LiberationSansBoldMapping\n  };\n  t[\"Arial-ItalicMT\"] = t[\"Arial-Italic\"] = {\n    name: \"LiberationSans-Italic\",\n    baseWidths: LiberationSansItalicWidths,\n    baseMapping: LiberationSansItalicMapping\n  };\n  t[\"Arial-BoldItalicMT\"] = t[\"Arial-BoldItalic\"] = {\n    name: \"LiberationSans-BoldItalic\",\n    baseWidths: LiberationSansBoldItalicWidths,\n    baseMapping: LiberationSansBoldItalicMapping\n  };\n  t[\"Calibri-Regular\"] = {\n    name: \"LiberationSans-Regular\",\n    factors: CalibriRegularFactors,\n    baseWidths: LiberationSansRegularWidths,\n    baseMapping: LiberationSansRegularMapping,\n    metrics: CalibriRegularMetrics\n  };\n  t[\"Calibri-Bold\"] = {\n    name: \"LiberationSans-Bold\",\n    factors: CalibriBoldFactors,\n    baseWidths: LiberationSansBoldWidths,\n    baseMapping: LiberationSansBoldMapping,\n    metrics: CalibriBoldMetrics\n  };\n  t[\"Calibri-Italic\"] = {\n    name: \"LiberationSans-Italic\",\n    factors: CalibriItalicFactors,\n    baseWidths: LiberationSansItalicWidths,\n    baseMapping: LiberationSansItalicMapping,\n    metrics: CalibriItalicMetrics\n  };\n  t[\"Calibri-BoldItalic\"] = {\n    name: \"LiberationSans-BoldItalic\",\n    factors: CalibriBoldItalicFactors,\n    baseWidths: LiberationSansBoldItalicWidths,\n    baseMapping: LiberationSansBoldItalicMapping,\n    metrics: CalibriBoldItalicMetrics\n  };\n  t[\"Segoeui-Regular\"] = {\n    name: \"LiberationSans-Regular\",\n    factors: SegoeuiRegularFactors,\n    baseWidths: LiberationSansRegularWidths,\n    baseMapping: LiberationSansRegularMapping,\n    metrics: SegoeuiRegularMetrics\n  };\n  t[\"Segoeui-Bold\"] = {\n    name: \"LiberationSans-Bold\",\n    factors: SegoeuiBoldFactors,\n    baseWidths: LiberationSansBoldWidths,\n    baseMapping: LiberationSansBoldMapping,\n    metrics: SegoeuiBoldMetrics\n  };\n  t[\"Segoeui-Italic\"] = {\n    name: \"LiberationSans-Italic\",\n    factors: SegoeuiItalicFactors,\n    baseWidths: LiberationSansItalicWidths,\n    baseMapping: LiberationSansItalicMapping,\n    metrics: SegoeuiItalicMetrics\n  };\n  t[\"Segoeui-BoldItalic\"] = {\n    name: \"LiberationSans-BoldItalic\",\n    factors: SegoeuiBoldItalicFactors,\n    baseWidths: LiberationSansBoldItalicWidths,\n    baseMapping: LiberationSansBoldItalicMapping,\n    metrics: SegoeuiBoldItalicMetrics\n  };\n  t[\"Helvetica-Regular\"] = t.Helvetica = {\n    name: \"LiberationSans-Regular\",\n    factors: HelveticaRegularFactors,\n    baseWidths: LiberationSansRegularWidths,\n    baseMapping: LiberationSansRegularMapping,\n    metrics: HelveticaRegularMetrics\n  };\n  t[\"Helvetica-Bold\"] = {\n    name: \"LiberationSans-Bold\",\n    factors: HelveticaBoldFactors,\n    baseWidths: LiberationSansBoldWidths,\n    baseMapping: LiberationSansBoldMapping,\n    metrics: HelveticaBoldMetrics\n  };\n  t[\"Helvetica-Italic\"] = {\n    name: \"LiberationSans-Italic\",\n    factors: HelveticaItalicFactors,\n    baseWidths: LiberationSansItalicWidths,\n    baseMapping: LiberationSansItalicMapping,\n    metrics: HelveticaItalicMetrics\n  };\n  t[\"Helvetica-BoldItalic\"] = {\n    name: \"LiberationSans-BoldItalic\",\n    factors: HelveticaBoldItalicFactors,\n    baseWidths: LiberationSansBoldItalicWidths,\n    baseMapping: LiberationSansBoldItalicMapping,\n    metrics: HelveticaBoldItalicMetrics\n  };\n});\nfunction getXfaFontName(name) {\n  const fontName = normalizeFontName(name);\n  const fontMap = getXFAFontMap();\n  return fontMap[fontName];\n}\nfunction getXfaFontWidths(name) {\n  const info = getXfaFontName(name);\n  if (!info) {\n    return null;\n  }\n  const {\n    baseWidths,\n    baseMapping,\n    factors\n  } = info;\n  const rescaledBaseWidths = !factors ? baseWidths : baseWidths.map((w, i) => w * factors[i]);\n  let currentCode = -2;\n  let currentArray;\n  const newWidths = [];\n  for (const [unicode, glyphIndex] of baseMapping.map((charUnicode, index) => [charUnicode, index]).sort(([unicode1], [unicode2]) => unicode1 - unicode2)) {\n    if (unicode === -1) {\n      continue;\n    }\n    if (unicode === currentCode + 1) {\n      currentArray.push(rescaledBaseWidths[glyphIndex]);\n      currentCode += 1;\n    } else {\n      currentCode = unicode;\n      currentArray = [rescaledBaseWidths[glyphIndex]];\n      newWidths.push(unicode, currentArray);\n    }\n  }\n  return newWidths;\n}\nfunction getXfaFontDict(name) {\n  const widths = getXfaFontWidths(name);\n  const dict = new Dict(null);\n  dict.set(\"BaseFont\", Name.get(name));\n  dict.set(\"Type\", Name.get(\"Font\"));\n  dict.set(\"Subtype\", Name.get(\"CIDFontType2\"));\n  dict.set(\"Encoding\", Name.get(\"Identity-H\"));\n  dict.set(\"CIDToGIDMap\", Name.get(\"Identity\"));\n  dict.set(\"W\", widths);\n  dict.set(\"FirstChar\", widths[0]);\n  dict.set(\"LastChar\", widths.at(-2) + widths.at(-1).length - 1);\n  const descriptor = new Dict(null);\n  dict.set(\"FontDescriptor\", descriptor);\n  const systemInfo = new Dict(null);\n  systemInfo.set(\"Ordering\", \"Identity\");\n  systemInfo.set(\"Registry\", \"Adobe\");\n  systemInfo.set(\"Supplement\", 0);\n  dict.set(\"CIDSystemInfo\", systemInfo);\n  return dict;\n}\n\n;// CONCATENATED MODULE: ./src/core/ps_parser.js\n\n\n\nclass PostScriptParser {\n  constructor(lexer) {\n    this.lexer = lexer;\n    this.operators = [];\n    this.token = null;\n    this.prev = null;\n  }\n  nextToken() {\n    this.prev = this.token;\n    this.token = this.lexer.getToken();\n  }\n  accept(type) {\n    if (this.token.type === type) {\n      this.nextToken();\n      return true;\n    }\n    return false;\n  }\n  expect(type) {\n    if (this.accept(type)) {\n      return true;\n    }\n    throw new FormatError(`Unexpected symbol: found ${this.token.type} expected ${type}.`);\n  }\n  parse() {\n    this.nextToken();\n    this.expect(PostScriptTokenTypes.LBRACE);\n    this.parseBlock();\n    this.expect(PostScriptTokenTypes.RBRACE);\n    return this.operators;\n  }\n  parseBlock() {\n    while (true) {\n      if (this.accept(PostScriptTokenTypes.NUMBER)) {\n        this.operators.push(this.prev.value);\n      } else if (this.accept(PostScriptTokenTypes.OPERATOR)) {\n        this.operators.push(this.prev.value);\n      } else if (this.accept(PostScriptTokenTypes.LBRACE)) {\n        this.parseCondition();\n      } else {\n        return;\n      }\n    }\n  }\n  parseCondition() {\n    const conditionLocation = this.operators.length;\n    this.operators.push(null, null);\n    this.parseBlock();\n    this.expect(PostScriptTokenTypes.RBRACE);\n    if (this.accept(PostScriptTokenTypes.IF)) {\n      this.operators[conditionLocation] = this.operators.length;\n      this.operators[conditionLocation + 1] = \"jz\";\n    } else if (this.accept(PostScriptTokenTypes.LBRACE)) {\n      const jumpLocation = this.operators.length;\n      this.operators.push(null, null);\n      const endOfTrue = this.operators.length;\n      this.parseBlock();\n      this.expect(PostScriptTokenTypes.RBRACE);\n      this.expect(PostScriptTokenTypes.IFELSE);\n      this.operators[jumpLocation] = this.operators.length;\n      this.operators[jumpLocation + 1] = \"j\";\n      this.operators[conditionLocation] = endOfTrue;\n      this.operators[conditionLocation + 1] = \"jz\";\n    } else {\n      throw new FormatError(\"PS Function: error parsing conditional.\");\n    }\n  }\n}\nconst PostScriptTokenTypes = {\n  LBRACE: 0,\n  RBRACE: 1,\n  NUMBER: 2,\n  OPERATOR: 3,\n  IF: 4,\n  IFELSE: 5\n};\nclass PostScriptToken {\n  static get opCache() {\n    return shadow(this, \"opCache\", Object.create(null));\n  }\n  constructor(type, value) {\n    this.type = type;\n    this.value = value;\n  }\n  static getOperator(op) {\n    return PostScriptToken.opCache[op] ||= new PostScriptToken(PostScriptTokenTypes.OPERATOR, op);\n  }\n  static get LBRACE() {\n    return shadow(this, \"LBRACE\", new PostScriptToken(PostScriptTokenTypes.LBRACE, \"{\"));\n  }\n  static get RBRACE() {\n    return shadow(this, \"RBRACE\", new PostScriptToken(PostScriptTokenTypes.RBRACE, \"}\"));\n  }\n  static get IF() {\n    return shadow(this, \"IF\", new PostScriptToken(PostScriptTokenTypes.IF, \"IF\"));\n  }\n  static get IFELSE() {\n    return shadow(this, \"IFELSE\", new PostScriptToken(PostScriptTokenTypes.IFELSE, \"IFELSE\"));\n  }\n}\nclass PostScriptLexer {\n  constructor(stream) {\n    this.stream = stream;\n    this.nextChar();\n    this.strBuf = [];\n  }\n  nextChar() {\n    return this.currentChar = this.stream.getByte();\n  }\n  getToken() {\n    let comment = false;\n    let ch = this.currentChar;\n    while (true) {\n      if (ch < 0) {\n        return EOF;\n      }\n      if (comment) {\n        if (ch === 0x0a || ch === 0x0d) {\n          comment = false;\n        }\n      } else if (ch === 0x25) {\n        comment = true;\n      } else if (!isWhiteSpace(ch)) {\n        break;\n      }\n      ch = this.nextChar();\n    }\n    switch (ch | 0) {\n      case 0x30:\n      case 0x31:\n      case 0x32:\n      case 0x33:\n      case 0x34:\n      case 0x35:\n      case 0x36:\n      case 0x37:\n      case 0x38:\n      case 0x39:\n      case 0x2b:\n      case 0x2d:\n      case 0x2e:\n        return new PostScriptToken(PostScriptTokenTypes.NUMBER, this.getNumber());\n      case 0x7b:\n        this.nextChar();\n        return PostScriptToken.LBRACE;\n      case 0x7d:\n        this.nextChar();\n        return PostScriptToken.RBRACE;\n    }\n    const strBuf = this.strBuf;\n    strBuf.length = 0;\n    strBuf[0] = String.fromCharCode(ch);\n    while ((ch = this.nextChar()) >= 0 && (ch >= 0x41 && ch <= 0x5a || ch >= 0x61 && ch <= 0x7a)) {\n      strBuf.push(String.fromCharCode(ch));\n    }\n    const str = strBuf.join(\"\");\n    switch (str.toLowerCase()) {\n      case \"if\":\n        return PostScriptToken.IF;\n      case \"ifelse\":\n        return PostScriptToken.IFELSE;\n      default:\n        return PostScriptToken.getOperator(str);\n    }\n  }\n  getNumber() {\n    let ch = this.currentChar;\n    const strBuf = this.strBuf;\n    strBuf.length = 0;\n    strBuf[0] = String.fromCharCode(ch);\n    while ((ch = this.nextChar()) >= 0) {\n      if (ch >= 0x30 && ch <= 0x39 || ch === 0x2d || ch === 0x2e) {\n        strBuf.push(String.fromCharCode(ch));\n      } else {\n        break;\n      }\n    }\n    const value = parseFloat(strBuf.join(\"\"));\n    if (isNaN(value)) {\n      throw new FormatError(`Invalid floating point number: ${value}`);\n    }\n    return value;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/image_utils.js\n\n\nclass BaseLocalCache {\n  constructor(options) {\n    if (this.constructor === BaseLocalCache) {\n      unreachable(\"Cannot initialize BaseLocalCache.\");\n    }\n    this._onlyRefs = options?.onlyRefs === true;\n    if (!this._onlyRefs) {\n      this._nameRefMap = new Map();\n      this._imageMap = new Map();\n    }\n    this._imageCache = new RefSetCache();\n  }\n  getByName(name) {\n    if (this._onlyRefs) {\n      unreachable(\"Should not call `getByName` method.\");\n    }\n    const ref = this._nameRefMap.get(name);\n    if (ref) {\n      return this.getByRef(ref);\n    }\n    return this._imageMap.get(name) || null;\n  }\n  getByRef(ref) {\n    return this._imageCache.get(ref) || null;\n  }\n  set(name, ref, data) {\n    unreachable(\"Abstract method `set` called.\");\n  }\n}\nclass LocalImageCache extends BaseLocalCache {\n  set(name, ref = null, data) {\n    if (typeof name !== \"string\") {\n      throw new Error('LocalImageCache.set - expected \"name\" argument.');\n    }\n    if (ref) {\n      if (this._imageCache.has(ref)) {\n        return;\n      }\n      this._nameRefMap.set(name, ref);\n      this._imageCache.put(ref, data);\n      return;\n    }\n    if (this._imageMap.has(name)) {\n      return;\n    }\n    this._imageMap.set(name, data);\n  }\n}\nclass LocalColorSpaceCache extends BaseLocalCache {\n  set(name = null, ref = null, data) {\n    if (typeof name !== \"string\" && !ref) {\n      throw new Error('LocalColorSpaceCache.set - expected \"name\" and/or \"ref\" argument.');\n    }\n    if (ref) {\n      if (this._imageCache.has(ref)) {\n        return;\n      }\n      if (name !== null) {\n        this._nameRefMap.set(name, ref);\n      }\n      this._imageCache.put(ref, data);\n      return;\n    }\n    if (this._imageMap.has(name)) {\n      return;\n    }\n    this._imageMap.set(name, data);\n  }\n}\nclass LocalFunctionCache extends BaseLocalCache {\n  constructor(options) {\n    super({\n      onlyRefs: true\n    });\n  }\n  set(name = null, ref, data) {\n    if (!ref) {\n      throw new Error('LocalFunctionCache.set - expected \"ref\" argument.');\n    }\n    if (this._imageCache.has(ref)) {\n      return;\n    }\n    this._imageCache.put(ref, data);\n  }\n}\nclass LocalGStateCache extends BaseLocalCache {\n  set(name, ref = null, data) {\n    if (typeof name !== \"string\") {\n      throw new Error('LocalGStateCache.set - expected \"name\" argument.');\n    }\n    if (ref) {\n      if (this._imageCache.has(ref)) {\n        return;\n      }\n      this._nameRefMap.set(name, ref);\n      this._imageCache.put(ref, data);\n      return;\n    }\n    if (this._imageMap.has(name)) {\n      return;\n    }\n    this._imageMap.set(name, data);\n  }\n}\nclass LocalTilingPatternCache extends BaseLocalCache {\n  constructor(options) {\n    super({\n      onlyRefs: true\n    });\n  }\n  set(name = null, ref, data) {\n    if (!ref) {\n      throw new Error('LocalTilingPatternCache.set - expected \"ref\" argument.');\n    }\n    if (this._imageCache.has(ref)) {\n      return;\n    }\n    this._imageCache.put(ref, data);\n  }\n}\nclass RegionalImageCache extends BaseLocalCache {\n  constructor(options) {\n    super({\n      onlyRefs: true\n    });\n  }\n  set(name = null, ref, data) {\n    if (!ref) {\n      throw new Error('RegionalImageCache.set - expected \"ref\" argument.');\n    }\n    if (this._imageCache.has(ref)) {\n      return;\n    }\n    this._imageCache.put(ref, data);\n  }\n}\nclass GlobalImageCache {\n  static NUM_PAGES_THRESHOLD = 2;\n  static MIN_IMAGES_TO_CACHE = 10;\n  static MAX_BYTE_SIZE = 5 * MAX_IMAGE_SIZE_TO_CACHE;\n  constructor() {\n    this._refCache = new RefSetCache();\n    this._imageCache = new RefSetCache();\n  }\n  get _byteSize() {\n    let byteSize = 0;\n    for (const imageData of this._imageCache) {\n      byteSize += imageData.byteSize;\n    }\n    return byteSize;\n  }\n  get _cacheLimitReached() {\n    if (this._imageCache.size < GlobalImageCache.MIN_IMAGES_TO_CACHE) {\n      return false;\n    }\n    if (this._byteSize < GlobalImageCache.MAX_BYTE_SIZE) {\n      return false;\n    }\n    return true;\n  }\n  shouldCache(ref, pageIndex) {\n    let pageIndexSet = this._refCache.get(ref);\n    if (!pageIndexSet) {\n      pageIndexSet = new Set();\n      this._refCache.put(ref, pageIndexSet);\n    }\n    pageIndexSet.add(pageIndex);\n    if (pageIndexSet.size < GlobalImageCache.NUM_PAGES_THRESHOLD) {\n      return false;\n    }\n    if (!this._imageCache.has(ref) && this._cacheLimitReached) {\n      return false;\n    }\n    return true;\n  }\n  addByteSize(ref, byteSize) {\n    const imageData = this._imageCache.get(ref);\n    if (!imageData) {\n      return;\n    }\n    if (imageData.byteSize) {\n      return;\n    }\n    imageData.byteSize = byteSize;\n  }\n  getData(ref, pageIndex) {\n    const pageIndexSet = this._refCache.get(ref);\n    if (!pageIndexSet) {\n      return null;\n    }\n    if (pageIndexSet.size < GlobalImageCache.NUM_PAGES_THRESHOLD) {\n      return null;\n    }\n    const imageData = this._imageCache.get(ref);\n    if (!imageData) {\n      return null;\n    }\n    pageIndexSet.add(pageIndex);\n    return imageData;\n  }\n  setData(ref, data) {\n    if (!this._refCache.has(ref)) {\n      throw new Error('GlobalImageCache.setData - expected \"shouldCache\" to have been called.');\n    }\n    if (this._imageCache.has(ref)) {\n      return;\n    }\n    if (this._cacheLimitReached) {\n      warn(\"GlobalImageCache.setData - cache limit reached.\");\n      return;\n    }\n    this._imageCache.put(ref, data);\n  }\n  clear(onlyData = false) {\n    if (!onlyData) {\n      this._refCache.clear();\n    }\n    this._imageCache.clear();\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/function.js\n\n\n\n\n\nclass PDFFunctionFactory {\n  constructor({\n    xref,\n    isEvalSupported = true\n  }) {\n    this.xref = xref;\n    this.isEvalSupported = isEvalSupported !== false;\n  }\n  create(fn) {\n    const cachedFunction = this.getCached(fn);\n    if (cachedFunction) {\n      return cachedFunction;\n    }\n    const parsedFunction = PDFFunction.parse({\n      xref: this.xref,\n      isEvalSupported: this.isEvalSupported,\n      fn: fn instanceof Ref ? this.xref.fetch(fn) : fn\n    });\n    this._cache(fn, parsedFunction);\n    return parsedFunction;\n  }\n  createFromArray(fnObj) {\n    const cachedFunction = this.getCached(fnObj);\n    if (cachedFunction) {\n      return cachedFunction;\n    }\n    const parsedFunction = PDFFunction.parseArray({\n      xref: this.xref,\n      isEvalSupported: this.isEvalSupported,\n      fnObj: fnObj instanceof Ref ? this.xref.fetch(fnObj) : fnObj\n    });\n    this._cache(fnObj, parsedFunction);\n    return parsedFunction;\n  }\n  getCached(cacheKey) {\n    let fnRef;\n    if (cacheKey instanceof Ref) {\n      fnRef = cacheKey;\n    } else if (cacheKey instanceof Dict) {\n      fnRef = cacheKey.objId;\n    } else if (cacheKey instanceof BaseStream) {\n      fnRef = cacheKey.dict?.objId;\n    }\n    if (fnRef) {\n      const localFunction = this._localFunctionCache.getByRef(fnRef);\n      if (localFunction) {\n        return localFunction;\n      }\n    }\n    return null;\n  }\n  _cache(cacheKey, parsedFunction) {\n    if (!parsedFunction) {\n      throw new Error('PDFFunctionFactory._cache - expected \"parsedFunction\" argument.');\n    }\n    let fnRef;\n    if (cacheKey instanceof Ref) {\n      fnRef = cacheKey;\n    } else if (cacheKey instanceof Dict) {\n      fnRef = cacheKey.objId;\n    } else if (cacheKey instanceof BaseStream) {\n      fnRef = cacheKey.dict?.objId;\n    }\n    if (fnRef) {\n      this._localFunctionCache.set(null, fnRef, parsedFunction);\n    }\n  }\n  get _localFunctionCache() {\n    return shadow(this, \"_localFunctionCache\", new LocalFunctionCache());\n  }\n}\nfunction toNumberArray(arr) {\n  if (!Array.isArray(arr)) {\n    return null;\n  }\n  const length = arr.length;\n  for (let i = 0; i < length; i++) {\n    if (typeof arr[i] !== \"number\") {\n      const result = new Array(length);\n      for (let j = 0; j < length; j++) {\n        result[j] = +arr[j];\n      }\n      return result;\n    }\n  }\n  return arr;\n}\nclass PDFFunction {\n  static getSampleArray(size, outputSize, bps, stream) {\n    let i, ii;\n    let length = 1;\n    for (i = 0, ii = size.length; i < ii; i++) {\n      length *= size[i];\n    }\n    length *= outputSize;\n    const array = new Array(length);\n    let codeSize = 0;\n    let codeBuf = 0;\n    const sampleMul = 1.0 / (2.0 ** bps - 1);\n    const strBytes = stream.getBytes((length * bps + 7) / 8);\n    let strIdx = 0;\n    for (i = 0; i < length; i++) {\n      while (codeSize < bps) {\n        codeBuf <<= 8;\n        codeBuf |= strBytes[strIdx++];\n        codeSize += 8;\n      }\n      codeSize -= bps;\n      array[i] = (codeBuf >> codeSize) * sampleMul;\n      codeBuf &= (1 << codeSize) - 1;\n    }\n    return array;\n  }\n  static parse({\n    xref,\n    isEvalSupported,\n    fn\n  }) {\n    const dict = fn.dict || fn;\n    const typeNum = dict.get(\"FunctionType\");\n    switch (typeNum) {\n      case 0:\n        return this.constructSampled({\n          xref,\n          isEvalSupported,\n          fn,\n          dict\n        });\n      case 1:\n        break;\n      case 2:\n        return this.constructInterpolated({\n          xref,\n          isEvalSupported,\n          dict\n        });\n      case 3:\n        return this.constructStiched({\n          xref,\n          isEvalSupported,\n          dict\n        });\n      case 4:\n        return this.constructPostScript({\n          xref,\n          isEvalSupported,\n          fn,\n          dict\n        });\n    }\n    throw new FormatError(\"Unknown type of function\");\n  }\n  static parseArray({\n    xref,\n    isEvalSupported,\n    fnObj\n  }) {\n    if (!Array.isArray(fnObj)) {\n      return this.parse({\n        xref,\n        isEvalSupported,\n        fn: fnObj\n      });\n    }\n    const fnArray = [];\n    for (const fn of fnObj) {\n      fnArray.push(this.parse({\n        xref,\n        isEvalSupported,\n        fn: xref.fetchIfRef(fn)\n      }));\n    }\n    return function (src, srcOffset, dest, destOffset) {\n      for (let i = 0, ii = fnArray.length; i < ii; i++) {\n        fnArray[i](src, srcOffset, dest, destOffset + i);\n      }\n    };\n  }\n  static constructSampled({\n    xref,\n    isEvalSupported,\n    fn,\n    dict\n  }) {\n    function toMultiArray(arr) {\n      const inputLength = arr.length;\n      const out = [];\n      let index = 0;\n      for (let i = 0; i < inputLength; i += 2) {\n        out[index++] = [arr[i], arr[i + 1]];\n      }\n      return out;\n    }\n    function interpolate(x, xmin, xmax, ymin, ymax) {\n      return ymin + (x - xmin) * ((ymax - ymin) / (xmax - xmin));\n    }\n    let domain = toNumberArray(dict.getArray(\"Domain\"));\n    let range = toNumberArray(dict.getArray(\"Range\"));\n    if (!domain || !range) {\n      throw new FormatError(\"No domain or range\");\n    }\n    const inputSize = domain.length / 2;\n    const outputSize = range.length / 2;\n    domain = toMultiArray(domain);\n    range = toMultiArray(range);\n    const size = toNumberArray(dict.getArray(\"Size\"));\n    const bps = dict.get(\"BitsPerSample\");\n    const order = dict.get(\"Order\") || 1;\n    if (order !== 1) {\n      info(\"No support for cubic spline interpolation: \" + order);\n    }\n    let encode = toNumberArray(dict.getArray(\"Encode\"));\n    if (!encode) {\n      encode = [];\n      for (let i = 0; i < inputSize; ++i) {\n        encode.push([0, size[i] - 1]);\n      }\n    } else {\n      encode = toMultiArray(encode);\n    }\n    let decode = toNumberArray(dict.getArray(\"Decode\"));\n    decode = !decode ? range : toMultiArray(decode);\n    const samples = this.getSampleArray(size, outputSize, bps, fn);\n    return function constructSampledFn(src, srcOffset, dest, destOffset) {\n      const cubeVertices = 1 << inputSize;\n      const cubeN = new Float64Array(cubeVertices);\n      const cubeVertex = new Uint32Array(cubeVertices);\n      let i, j;\n      for (j = 0; j < cubeVertices; j++) {\n        cubeN[j] = 1;\n      }\n      let k = outputSize,\n        pos = 1;\n      for (i = 0; i < inputSize; ++i) {\n        const domain_2i = domain[i][0];\n        const domain_2i_1 = domain[i][1];\n        const xi = Math.min(Math.max(src[srcOffset + i], domain_2i), domain_2i_1);\n        let e = interpolate(xi, domain_2i, domain_2i_1, encode[i][0], encode[i][1]);\n        const size_i = size[i];\n        e = Math.min(Math.max(e, 0), size_i - 1);\n        const e0 = e < size_i - 1 ? Math.floor(e) : e - 1;\n        const n0 = e0 + 1 - e;\n        const n1 = e - e0;\n        const offset0 = e0 * k;\n        const offset1 = offset0 + k;\n        for (j = 0; j < cubeVertices; j++) {\n          if (j & pos) {\n            cubeN[j] *= n1;\n            cubeVertex[j] += offset1;\n          } else {\n            cubeN[j] *= n0;\n            cubeVertex[j] += offset0;\n          }\n        }\n        k *= size_i;\n        pos <<= 1;\n      }\n      for (j = 0; j < outputSize; ++j) {\n        let rj = 0;\n        for (i = 0; i < cubeVertices; i++) {\n          rj += samples[cubeVertex[i] + j] * cubeN[i];\n        }\n        rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);\n        dest[destOffset + j] = Math.min(Math.max(rj, range[j][0]), range[j][1]);\n      }\n    };\n  }\n  static constructInterpolated({\n    xref,\n    isEvalSupported,\n    dict\n  }) {\n    const c0 = toNumberArray(dict.getArray(\"C0\")) || [0];\n    const c1 = toNumberArray(dict.getArray(\"C1\")) || [1];\n    const n = dict.get(\"N\");\n    const diff = [];\n    for (let i = 0, ii = c0.length; i < ii; ++i) {\n      diff.push(c1[i] - c0[i]);\n    }\n    const length = diff.length;\n    return function constructInterpolatedFn(src, srcOffset, dest, destOffset) {\n      const x = n === 1 ? src[srcOffset] : src[srcOffset] ** n;\n      for (let j = 0; j < length; ++j) {\n        dest[destOffset + j] = c0[j] + x * diff[j];\n      }\n    };\n  }\n  static constructStiched({\n    xref,\n    isEvalSupported,\n    dict\n  }) {\n    const domain = toNumberArray(dict.getArray(\"Domain\"));\n    if (!domain) {\n      throw new FormatError(\"No domain\");\n    }\n    const inputSize = domain.length / 2;\n    if (inputSize !== 1) {\n      throw new FormatError(\"Bad domain for stiched function\");\n    }\n    const fns = [];\n    for (const fn of dict.get(\"Functions\")) {\n      fns.push(this.parse({\n        xref,\n        isEvalSupported,\n        fn: xref.fetchIfRef(fn)\n      }));\n    }\n    const bounds = toNumberArray(dict.getArray(\"Bounds\"));\n    const encode = toNumberArray(dict.getArray(\"Encode\"));\n    const tmpBuf = new Float32Array(1);\n    return function constructStichedFn(src, srcOffset, dest, destOffset) {\n      const clip = function constructStichedFromIRClip(v, min, max) {\n        if (v > max) {\n          v = max;\n        } else if (v < min) {\n          v = min;\n        }\n        return v;\n      };\n      const v = clip(src[srcOffset], domain[0], domain[1]);\n      const length = bounds.length;\n      let i;\n      for (i = 0; i < length; ++i) {\n        if (v < bounds[i]) {\n          break;\n        }\n      }\n      let dmin = domain[0];\n      if (i > 0) {\n        dmin = bounds[i - 1];\n      }\n      let dmax = domain[1];\n      if (i < bounds.length) {\n        dmax = bounds[i];\n      }\n      const rmin = encode[2 * i];\n      const rmax = encode[2 * i + 1];\n      tmpBuf[0] = dmin === dmax ? rmin : rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);\n      fns[i](tmpBuf, 0, dest, destOffset);\n    };\n  }\n  static constructPostScript({\n    xref,\n    isEvalSupported,\n    fn,\n    dict\n  }) {\n    const domain = toNumberArray(dict.getArray(\"Domain\"));\n    const range = toNumberArray(dict.getArray(\"Range\"));\n    if (!domain) {\n      throw new FormatError(\"No domain.\");\n    }\n    if (!range) {\n      throw new FormatError(\"No range.\");\n    }\n    const lexer = new PostScriptLexer(fn);\n    const parser = new PostScriptParser(lexer);\n    const code = parser.parse();\n    if (isEvalSupported && FeatureTest.isEvalSupported) {\n      const compiled = new PostScriptCompiler().compile(code, domain, range);\n      if (compiled) {\n        return new Function(\"src\", \"srcOffset\", \"dest\", \"destOffset\", compiled);\n      }\n    }\n    info(\"Unable to compile PS function\");\n    const numOutputs = range.length >> 1;\n    const numInputs = domain.length >> 1;\n    const evaluator = new PostScriptEvaluator(code);\n    const cache = Object.create(null);\n    const MAX_CACHE_SIZE = 2048 * 4;\n    let cache_available = MAX_CACHE_SIZE;\n    const tmpBuf = new Float32Array(numInputs);\n    return function constructPostScriptFn(src, srcOffset, dest, destOffset) {\n      let i, value;\n      let key = \"\";\n      const input = tmpBuf;\n      for (i = 0; i < numInputs; i++) {\n        value = src[srcOffset + i];\n        input[i] = value;\n        key += value + \"_\";\n      }\n      const cachedValue = cache[key];\n      if (cachedValue !== undefined) {\n        dest.set(cachedValue, destOffset);\n        return;\n      }\n      const output = new Float32Array(numOutputs);\n      const stack = evaluator.execute(input);\n      const stackIndex = stack.length - numOutputs;\n      for (i = 0; i < numOutputs; i++) {\n        value = stack[stackIndex + i];\n        let bound = range[i * 2];\n        if (value < bound) {\n          value = bound;\n        } else {\n          bound = range[i * 2 + 1];\n          if (value > bound) {\n            value = bound;\n          }\n        }\n        output[i] = value;\n      }\n      if (cache_available > 0) {\n        cache_available--;\n        cache[key] = output;\n      }\n      dest.set(output, destOffset);\n    };\n  }\n}\nfunction isPDFFunction(v) {\n  let fnDict;\n  if (v instanceof Dict) {\n    fnDict = v;\n  } else if (v instanceof BaseStream) {\n    fnDict = v.dict;\n  } else {\n    return false;\n  }\n  return fnDict.has(\"FunctionType\");\n}\nclass PostScriptStack {\n  static MAX_STACK_SIZE = 100;\n  constructor(initialStack) {\n    this.stack = initialStack ? Array.from(initialStack) : [];\n  }\n  push(value) {\n    if (this.stack.length >= PostScriptStack.MAX_STACK_SIZE) {\n      throw new Error(\"PostScript function stack overflow.\");\n    }\n    this.stack.push(value);\n  }\n  pop() {\n    if (this.stack.length <= 0) {\n      throw new Error(\"PostScript function stack underflow.\");\n    }\n    return this.stack.pop();\n  }\n  copy(n) {\n    if (this.stack.length + n >= PostScriptStack.MAX_STACK_SIZE) {\n      throw new Error(\"PostScript function stack overflow.\");\n    }\n    const stack = this.stack;\n    for (let i = stack.length - n, j = n - 1; j >= 0; j--, i++) {\n      stack.push(stack[i]);\n    }\n  }\n  index(n) {\n    this.push(this.stack[this.stack.length - n - 1]);\n  }\n  roll(n, p) {\n    const stack = this.stack;\n    const l = stack.length - n;\n    const r = stack.length - 1;\n    const c = l + (p - Math.floor(p / n) * n);\n    for (let i = l, j = r; i < j; i++, j--) {\n      const t = stack[i];\n      stack[i] = stack[j];\n      stack[j] = t;\n    }\n    for (let i = l, j = c - 1; i < j; i++, j--) {\n      const t = stack[i];\n      stack[i] = stack[j];\n      stack[j] = t;\n    }\n    for (let i = c, j = r; i < j; i++, j--) {\n      const t = stack[i];\n      stack[i] = stack[j];\n      stack[j] = t;\n    }\n  }\n}\nclass PostScriptEvaluator {\n  constructor(operators) {\n    this.operators = operators;\n  }\n  execute(initialStack) {\n    const stack = new PostScriptStack(initialStack);\n    let counter = 0;\n    const operators = this.operators;\n    const length = operators.length;\n    let operator, a, b;\n    while (counter < length) {\n      operator = operators[counter++];\n      if (typeof operator === \"number\") {\n        stack.push(operator);\n        continue;\n      }\n      switch (operator) {\n        case \"jz\":\n          b = stack.pop();\n          a = stack.pop();\n          if (!a) {\n            counter = b;\n          }\n          break;\n        case \"j\":\n          a = stack.pop();\n          counter = a;\n          break;\n        case \"abs\":\n          a = stack.pop();\n          stack.push(Math.abs(a));\n          break;\n        case \"add\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a + b);\n          break;\n        case \"and\":\n          b = stack.pop();\n          a = stack.pop();\n          if (typeof a === \"boolean\" && typeof b === \"boolean\") {\n            stack.push(a && b);\n          } else {\n            stack.push(a & b);\n          }\n          break;\n        case \"atan\":\n          b = stack.pop();\n          a = stack.pop();\n          a = Math.atan2(a, b) / Math.PI * 180;\n          if (a < 0) {\n            a += 360;\n          }\n          stack.push(a);\n          break;\n        case \"bitshift\":\n          b = stack.pop();\n          a = stack.pop();\n          if (a > 0) {\n            stack.push(a << b);\n          } else {\n            stack.push(a >> b);\n          }\n          break;\n        case \"ceiling\":\n          a = stack.pop();\n          stack.push(Math.ceil(a));\n          break;\n        case \"copy\":\n          a = stack.pop();\n          stack.copy(a);\n          break;\n        case \"cos\":\n          a = stack.pop();\n          stack.push(Math.cos(a % 360 / 180 * Math.PI));\n          break;\n        case \"cvi\":\n          a = stack.pop() | 0;\n          stack.push(a);\n          break;\n        case \"cvr\":\n          break;\n        case \"div\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a / b);\n          break;\n        case \"dup\":\n          stack.copy(1);\n          break;\n        case \"eq\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a === b);\n          break;\n        case \"exch\":\n          stack.roll(2, 1);\n          break;\n        case \"exp\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a ** b);\n          break;\n        case \"false\":\n          stack.push(false);\n          break;\n        case \"floor\":\n          a = stack.pop();\n          stack.push(Math.floor(a));\n          break;\n        case \"ge\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a >= b);\n          break;\n        case \"gt\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a > b);\n          break;\n        case \"idiv\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a / b | 0);\n          break;\n        case \"index\":\n          a = stack.pop();\n          stack.index(a);\n          break;\n        case \"le\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a <= b);\n          break;\n        case \"ln\":\n          a = stack.pop();\n          stack.push(Math.log(a));\n          break;\n        case \"log\":\n          a = stack.pop();\n          stack.push(Math.log10(a));\n          break;\n        case \"lt\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a < b);\n          break;\n        case \"mod\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a % b);\n          break;\n        case \"mul\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a * b);\n          break;\n        case \"ne\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a !== b);\n          break;\n        case \"neg\":\n          a = stack.pop();\n          stack.push(-a);\n          break;\n        case \"not\":\n          a = stack.pop();\n          if (typeof a === \"boolean\") {\n            stack.push(!a);\n          } else {\n            stack.push(~a);\n          }\n          break;\n        case \"or\":\n          b = stack.pop();\n          a = stack.pop();\n          if (typeof a === \"boolean\" && typeof b === \"boolean\") {\n            stack.push(a || b);\n          } else {\n            stack.push(a | b);\n          }\n          break;\n        case \"pop\":\n          stack.pop();\n          break;\n        case \"roll\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.roll(a, b);\n          break;\n        case \"round\":\n          a = stack.pop();\n          stack.push(Math.round(a));\n          break;\n        case \"sin\":\n          a = stack.pop();\n          stack.push(Math.sin(a % 360 / 180 * Math.PI));\n          break;\n        case \"sqrt\":\n          a = stack.pop();\n          stack.push(Math.sqrt(a));\n          break;\n        case \"sub\":\n          b = stack.pop();\n          a = stack.pop();\n          stack.push(a - b);\n          break;\n        case \"true\":\n          stack.push(true);\n          break;\n        case \"truncate\":\n          a = stack.pop();\n          a = a < 0 ? Math.ceil(a) : Math.floor(a);\n          stack.push(a);\n          break;\n        case \"xor\":\n          b = stack.pop();\n          a = stack.pop();\n          if (typeof a === \"boolean\" && typeof b === \"boolean\") {\n            stack.push(a !== b);\n          } else {\n            stack.push(a ^ b);\n          }\n          break;\n        default:\n          throw new FormatError(`Unknown operator ${operator}`);\n      }\n    }\n    return stack.stack;\n  }\n}\nclass AstNode {\n  constructor(type) {\n    this.type = type;\n  }\n  visit(visitor) {\n    unreachable(\"abstract method\");\n  }\n}\nclass AstArgument extends AstNode {\n  constructor(index, min, max) {\n    super(\"args\");\n    this.index = index;\n    this.min = min;\n    this.max = max;\n  }\n  visit(visitor) {\n    visitor.visitArgument(this);\n  }\n}\nclass AstLiteral extends AstNode {\n  constructor(number) {\n    super(\"literal\");\n    this.number = number;\n    this.min = number;\n    this.max = number;\n  }\n  visit(visitor) {\n    visitor.visitLiteral(this);\n  }\n}\nclass AstBinaryOperation extends AstNode {\n  constructor(op, arg1, arg2, min, max) {\n    super(\"binary\");\n    this.op = op;\n    this.arg1 = arg1;\n    this.arg2 = arg2;\n    this.min = min;\n    this.max = max;\n  }\n  visit(visitor) {\n    visitor.visitBinaryOperation(this);\n  }\n}\nclass AstMin extends AstNode {\n  constructor(arg, max) {\n    super(\"max\");\n    this.arg = arg;\n    this.min = arg.min;\n    this.max = max;\n  }\n  visit(visitor) {\n    visitor.visitMin(this);\n  }\n}\nclass AstVariable extends AstNode {\n  constructor(index, min, max) {\n    super(\"var\");\n    this.index = index;\n    this.min = min;\n    this.max = max;\n  }\n  visit(visitor) {\n    visitor.visitVariable(this);\n  }\n}\nclass AstVariableDefinition extends AstNode {\n  constructor(variable, arg) {\n    super(\"definition\");\n    this.variable = variable;\n    this.arg = arg;\n  }\n  visit(visitor) {\n    visitor.visitVariableDefinition(this);\n  }\n}\nclass ExpressionBuilderVisitor {\n  constructor() {\n    this.parts = [];\n  }\n  visitArgument(arg) {\n    this.parts.push(\"Math.max(\", arg.min, \", Math.min(\", arg.max, \", src[srcOffset + \", arg.index, \"]))\");\n  }\n  visitVariable(variable) {\n    this.parts.push(\"v\", variable.index);\n  }\n  visitLiteral(literal) {\n    this.parts.push(literal.number);\n  }\n  visitBinaryOperation(operation) {\n    this.parts.push(\"(\");\n    operation.arg1.visit(this);\n    this.parts.push(\" \", operation.op, \" \");\n    operation.arg2.visit(this);\n    this.parts.push(\")\");\n  }\n  visitVariableDefinition(definition) {\n    this.parts.push(\"var \");\n    definition.variable.visit(this);\n    this.parts.push(\" = \");\n    definition.arg.visit(this);\n    this.parts.push(\";\");\n  }\n  visitMin(max) {\n    this.parts.push(\"Math.min(\");\n    max.arg.visit(this);\n    this.parts.push(\", \", max.max, \")\");\n  }\n  toString() {\n    return this.parts.join(\"\");\n  }\n}\nfunction buildAddOperation(num1, num2) {\n  if (num2.type === \"literal\" && num2.number === 0) {\n    return num1;\n  }\n  if (num1.type === \"literal\" && num1.number === 0) {\n    return num2;\n  }\n  if (num2.type === \"literal\" && num1.type === \"literal\") {\n    return new AstLiteral(num1.number + num2.number);\n  }\n  return new AstBinaryOperation(\"+\", num1, num2, num1.min + num2.min, num1.max + num2.max);\n}\nfunction buildMulOperation(num1, num2) {\n  if (num2.type === \"literal\") {\n    if (num2.number === 0) {\n      return new AstLiteral(0);\n    } else if (num2.number === 1) {\n      return num1;\n    } else if (num1.type === \"literal\") {\n      return new AstLiteral(num1.number * num2.number);\n    }\n  }\n  if (num1.type === \"literal\") {\n    if (num1.number === 0) {\n      return new AstLiteral(0);\n    } else if (num1.number === 1) {\n      return num2;\n    }\n  }\n  const min = Math.min(num1.min * num2.min, num1.min * num2.max, num1.max * num2.min, num1.max * num2.max);\n  const max = Math.max(num1.min * num2.min, num1.min * num2.max, num1.max * num2.min, num1.max * num2.max);\n  return new AstBinaryOperation(\"*\", num1, num2, min, max);\n}\nfunction buildSubOperation(num1, num2) {\n  if (num2.type === \"literal\") {\n    if (num2.number === 0) {\n      return num1;\n    } else if (num1.type === \"literal\") {\n      return new AstLiteral(num1.number - num2.number);\n    }\n  }\n  if (num2.type === \"binary\" && num2.op === \"-\" && num1.type === \"literal\" && num1.number === 1 && num2.arg1.type === \"literal\" && num2.arg1.number === 1) {\n    return num2.arg2;\n  }\n  return new AstBinaryOperation(\"-\", num1, num2, num1.min - num2.max, num1.max - num2.min);\n}\nfunction buildMinOperation(num1, max) {\n  if (num1.min >= max) {\n    return new AstLiteral(max);\n  } else if (num1.max <= max) {\n    return num1;\n  }\n  return new AstMin(num1, max);\n}\nclass PostScriptCompiler {\n  compile(code, domain, range) {\n    const stack = [];\n    const instructions = [];\n    const inputSize = domain.length >> 1,\n      outputSize = range.length >> 1;\n    let lastRegister = 0;\n    let n, j;\n    let num1, num2, ast1, ast2, tmpVar, item;\n    for (let i = 0; i < inputSize; i++) {\n      stack.push(new AstArgument(i, domain[i * 2], domain[i * 2 + 1]));\n    }\n    for (let i = 0, ii = code.length; i < ii; i++) {\n      item = code[i];\n      if (typeof item === \"number\") {\n        stack.push(new AstLiteral(item));\n        continue;\n      }\n      switch (item) {\n        case \"add\":\n          if (stack.length < 2) {\n            return null;\n          }\n          num2 = stack.pop();\n          num1 = stack.pop();\n          stack.push(buildAddOperation(num1, num2));\n          break;\n        case \"cvr\":\n          if (stack.length < 1) {\n            return null;\n          }\n          break;\n        case \"mul\":\n          if (stack.length < 2) {\n            return null;\n          }\n          num2 = stack.pop();\n          num1 = stack.pop();\n          stack.push(buildMulOperation(num1, num2));\n          break;\n        case \"sub\":\n          if (stack.length < 2) {\n            return null;\n          }\n          num2 = stack.pop();\n          num1 = stack.pop();\n          stack.push(buildSubOperation(num1, num2));\n          break;\n        case \"exch\":\n          if (stack.length < 2) {\n            return null;\n          }\n          ast1 = stack.pop();\n          ast2 = stack.pop();\n          stack.push(ast1, ast2);\n          break;\n        case \"pop\":\n          if (stack.length < 1) {\n            return null;\n          }\n          stack.pop();\n          break;\n        case \"index\":\n          if (stack.length < 1) {\n            return null;\n          }\n          num1 = stack.pop();\n          if (num1.type !== \"literal\") {\n            return null;\n          }\n          n = num1.number;\n          if (n < 0 || !Number.isInteger(n) || stack.length < n) {\n            return null;\n          }\n          ast1 = stack[stack.length - n - 1];\n          if (ast1.type === \"literal\" || ast1.type === \"var\") {\n            stack.push(ast1);\n            break;\n          }\n          tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);\n          stack[stack.length - n - 1] = tmpVar;\n          stack.push(tmpVar);\n          instructions.push(new AstVariableDefinition(tmpVar, ast1));\n          break;\n        case \"dup\":\n          if (stack.length < 1) {\n            return null;\n          }\n          if (typeof code[i + 1] === \"number\" && code[i + 2] === \"gt\" && code[i + 3] === i + 7 && code[i + 4] === \"jz\" && code[i + 5] === \"pop\" && code[i + 6] === code[i + 1]) {\n            num1 = stack.pop();\n            stack.push(buildMinOperation(num1, code[i + 1]));\n            i += 6;\n            break;\n          }\n          ast1 = stack.at(-1);\n          if (ast1.type === \"literal\" || ast1.type === \"var\") {\n            stack.push(ast1);\n            break;\n          }\n          tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);\n          stack[stack.length - 1] = tmpVar;\n          stack.push(tmpVar);\n          instructions.push(new AstVariableDefinition(tmpVar, ast1));\n          break;\n        case \"roll\":\n          if (stack.length < 2) {\n            return null;\n          }\n          num2 = stack.pop();\n          num1 = stack.pop();\n          if (num2.type !== \"literal\" || num1.type !== \"literal\") {\n            return null;\n          }\n          j = num2.number;\n          n = num1.number;\n          if (n <= 0 || !Number.isInteger(n) || !Number.isInteger(j) || stack.length < n) {\n            return null;\n          }\n          j = (j % n + n) % n;\n          if (j === 0) {\n            break;\n          }\n          stack.push(...stack.splice(stack.length - n, n - j));\n          break;\n        default:\n          return null;\n      }\n    }\n    if (stack.length !== outputSize) {\n      return null;\n    }\n    const result = [];\n    for (const instruction of instructions) {\n      const statementBuilder = new ExpressionBuilderVisitor();\n      instruction.visit(statementBuilder);\n      result.push(statementBuilder.toString());\n    }\n    for (let i = 0, ii = stack.length; i < ii; i++) {\n      const expr = stack[i],\n        statementBuilder = new ExpressionBuilderVisitor();\n      expr.visit(statementBuilder);\n      const min = range[i * 2],\n        max = range[i * 2 + 1];\n      const out = [statementBuilder.toString()];\n      if (min > expr.min) {\n        out.unshift(\"Math.max(\", min, \", \");\n        out.push(\")\");\n      }\n      if (max < expr.max) {\n        out.unshift(\"Math.min(\", max, \", \");\n        out.push(\")\");\n      }\n      out.unshift(\"dest[destOffset + \", i, \"] = \");\n      out.push(\";\");\n      result.push(out.join(\"\"));\n    }\n    return result.join(\"\\n\");\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/bidi.js\n\nconst baseTypes = [\"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"S\", \"B\", \"S\", \"WS\", \"B\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"B\", \"B\", \"B\", \"S\", \"WS\", \"ON\", \"ON\", \"ET\", \"ET\", \"ET\", \"ON\", \"ON\", \"ON\", \"ON\", \"ON\", \"ES\", \"CS\", \"ES\", \"CS\", \"CS\", \"EN\", \"EN\", \"EN\", \"EN\", \"EN\", \"EN\", \"EN\", \"EN\", \"EN\", \"EN\", \"CS\", \"ON\", \"ON\", \"ON\", \"ON\", \"ON\", \"ON\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"ON\", \"ON\", \"ON\", \"ON\", \"ON\", \"ON\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"ON\", \"ON\", \"ON\", \"ON\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"B\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"BN\", \"CS\", \"ON\", \"ET\", \"ET\", \"ET\", \"ET\", \"ON\", \"ON\", \"ON\", \"ON\", \"L\", \"ON\", \"ON\", \"BN\", \"ON\", \"ON\", \"ET\", \"ET\", \"EN\", \"EN\", \"ON\", \"L\", \"ON\", \"ON\", \"ON\", \"EN\", \"L\", \"ON\", \"ON\", \"ON\", \"ON\", \"ON\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"ON\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"ON\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\", \"L\"];\nconst arabicTypes = [\"AN\", \"AN\", \"AN\", \"AN\", \"AN\", \"AN\", \"ON\", \"ON\", \"AL\", \"ET\", \"ET\", \"AL\", \"CS\", \"AL\", \"ON\", \"ON\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"AL\", \"AL\", \"\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"AN\", \"AN\", \"AN\", \"AN\", \"AN\", \"AN\", \"AN\", \"AN\", \"AN\", \"AN\", \"ET\", \"AN\", \"AN\", \"AL\", \"AL\", \"AL\", \"NSM\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"AN\", \"ON\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"AL\", \"AL\", \"NSM\", \"NSM\", \"ON\", \"NSM\", \"NSM\", \"NSM\", \"NSM\", \"AL\", \"AL\", \"EN\", \"EN\", \"EN\", \"EN\", \"EN\", \"EN\", \"EN\", \"EN\", \"EN\", \"EN\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\", \"AL\"];\nfunction isOdd(i) {\n  return (i & 1) !== 0;\n}\nfunction isEven(i) {\n  return (i & 1) === 0;\n}\nfunction findUnequal(arr, start, value) {\n  let j, jj;\n  for (j = start, jj = arr.length; j < jj; ++j) {\n    if (arr[j] !== value) {\n      return j;\n    }\n  }\n  return j;\n}\nfunction setValues(arr, start, end, value) {\n  for (let j = start; j < end; ++j) {\n    arr[j] = value;\n  }\n}\nfunction reverseValues(arr, start, end) {\n  for (let i = start, j = end - 1; i < j; ++i, --j) {\n    const temp = arr[i];\n    arr[i] = arr[j];\n    arr[j] = temp;\n  }\n}\nfunction createBidiText(str, isLTR, vertical = false) {\n  let dir = \"ltr\";\n  if (vertical) {\n    dir = \"ttb\";\n  } else if (!isLTR) {\n    dir = \"rtl\";\n  }\n  return {\n    str,\n    dir\n  };\n}\nconst chars = [];\nconst types = [];\nfunction bidi(str, startLevel = -1, vertical = false) {\n  let isLTR = true;\n  const strLength = str.length;\n  if (strLength === 0 || vertical) {\n    return createBidiText(str, isLTR, vertical);\n  }\n  chars.length = strLength;\n  types.length = strLength;\n  let numBidi = 0;\n  let i, ii;\n  for (i = 0; i < strLength; ++i) {\n    chars[i] = str.charAt(i);\n    const charCode = str.charCodeAt(i);\n    let charType = \"L\";\n    if (charCode <= 0x00ff) {\n      charType = baseTypes[charCode];\n    } else if (0x0590 <= charCode && charCode <= 0x05f4) {\n      charType = \"R\";\n    } else if (0x0600 <= charCode && charCode <= 0x06ff) {\n      charType = arabicTypes[charCode & 0xff];\n      if (!charType) {\n        warn(\"Bidi: invalid Unicode character \" + charCode.toString(16));\n      }\n    } else if (0x0700 <= charCode && charCode <= 0x08ac || 0xfb50 <= charCode && charCode <= 0xfdff || 0xfe70 <= charCode && charCode <= 0xfeff) {\n      charType = \"AL\";\n    }\n    if (charType === \"R\" || charType === \"AL\" || charType === \"AN\") {\n      numBidi++;\n    }\n    types[i] = charType;\n  }\n  if (numBidi === 0) {\n    isLTR = true;\n    return createBidiText(str, isLTR);\n  }\n  if (startLevel === -1) {\n    if (numBidi / strLength < 0.3 && strLength > 4) {\n      isLTR = true;\n      startLevel = 0;\n    } else {\n      isLTR = false;\n      startLevel = 1;\n    }\n  }\n  const levels = [];\n  for (i = 0; i < strLength; ++i) {\n    levels[i] = startLevel;\n  }\n  const e = isOdd(startLevel) ? \"R\" : \"L\";\n  const sor = e;\n  const eor = sor;\n  let lastType = sor;\n  for (i = 0; i < strLength; ++i) {\n    if (types[i] === \"NSM\") {\n      types[i] = lastType;\n    } else {\n      lastType = types[i];\n    }\n  }\n  lastType = sor;\n  let t;\n  for (i = 0; i < strLength; ++i) {\n    t = types[i];\n    if (t === \"EN\") {\n      types[i] = lastType === \"AL\" ? \"AN\" : \"EN\";\n    } else if (t === \"R\" || t === \"L\" || t === \"AL\") {\n      lastType = t;\n    }\n  }\n  for (i = 0; i < strLength; ++i) {\n    t = types[i];\n    if (t === \"AL\") {\n      types[i] = \"R\";\n    }\n  }\n  for (i = 1; i < strLength - 1; ++i) {\n    if (types[i] === \"ES\" && types[i - 1] === \"EN\" && types[i + 1] === \"EN\") {\n      types[i] = \"EN\";\n    }\n    if (types[i] === \"CS\" && (types[i - 1] === \"EN\" || types[i - 1] === \"AN\") && types[i + 1] === types[i - 1]) {\n      types[i] = types[i - 1];\n    }\n  }\n  for (i = 0; i < strLength; ++i) {\n    if (types[i] === \"EN\") {\n      for (let j = i - 1; j >= 0; --j) {\n        if (types[j] !== \"ET\") {\n          break;\n        }\n        types[j] = \"EN\";\n      }\n      for (let j = i + 1; j < strLength; ++j) {\n        if (types[j] !== \"ET\") {\n          break;\n        }\n        types[j] = \"EN\";\n      }\n    }\n  }\n  for (i = 0; i < strLength; ++i) {\n    t = types[i];\n    if (t === \"WS\" || t === \"ES\" || t === \"ET\" || t === \"CS\") {\n      types[i] = \"ON\";\n    }\n  }\n  lastType = sor;\n  for (i = 0; i < strLength; ++i) {\n    t = types[i];\n    if (t === \"EN\") {\n      types[i] = lastType === \"L\" ? \"L\" : \"EN\";\n    } else if (t === \"R\" || t === \"L\") {\n      lastType = t;\n    }\n  }\n  for (i = 0; i < strLength; ++i) {\n    if (types[i] === \"ON\") {\n      const end = findUnequal(types, i + 1, \"ON\");\n      let before = sor;\n      if (i > 0) {\n        before = types[i - 1];\n      }\n      let after = eor;\n      if (end + 1 < strLength) {\n        after = types[end + 1];\n      }\n      if (before !== \"L\") {\n        before = \"R\";\n      }\n      if (after !== \"L\") {\n        after = \"R\";\n      }\n      if (before === after) {\n        setValues(types, i, end, before);\n      }\n      i = end - 1;\n    }\n  }\n  for (i = 0; i < strLength; ++i) {\n    if (types[i] === \"ON\") {\n      types[i] = e;\n    }\n  }\n  for (i = 0; i < strLength; ++i) {\n    t = types[i];\n    if (isEven(levels[i])) {\n      if (t === \"R\") {\n        levels[i] += 1;\n      } else if (t === \"AN\" || t === \"EN\") {\n        levels[i] += 2;\n      }\n    } else if (t === \"L\" || t === \"AN\" || t === \"EN\") {\n      levels[i] += 1;\n    }\n  }\n  let highestLevel = -1;\n  let lowestOddLevel = 99;\n  let level;\n  for (i = 0, ii = levels.length; i < ii; ++i) {\n    level = levels[i];\n    if (highestLevel < level) {\n      highestLevel = level;\n    }\n    if (lowestOddLevel > level && isOdd(level)) {\n      lowestOddLevel = level;\n    }\n  }\n  for (level = highestLevel; level >= lowestOddLevel; --level) {\n    let start = -1;\n    for (i = 0, ii = levels.length; i < ii; ++i) {\n      if (levels[i] < level) {\n        if (start >= 0) {\n          reverseValues(chars, start, i);\n          start = -1;\n        }\n      } else if (start < 0) {\n        start = i;\n      }\n    }\n    if (start >= 0) {\n      reverseValues(chars, start, levels.length);\n    }\n  }\n  for (i = 0, ii = chars.length; i < ii; ++i) {\n    const ch = chars[i];\n    if (ch === \"<\" || ch === \">\") {\n      chars[i] = \"\";\n    }\n  }\n  return createBidiText(chars.join(\"\"), isLTR);\n}\n\n;// CONCATENATED MODULE: ./src/core/font_substitutions.js\n\n\nconst NORMAL = {\n  style: \"normal\",\n  weight: \"normal\"\n};\nconst BOLD = {\n  style: \"normal\",\n  weight: \"bold\"\n};\nconst ITALIC = {\n  style: \"italic\",\n  weight: \"normal\"\n};\nconst BOLDITALIC = {\n  style: \"italic\",\n  weight: \"bold\"\n};\nconst substitutionMap = new Map([[\"Times-Roman\", {\n  local: [\"Times New Roman\", \"Times-Roman\", \"Times\", \"Liberation Serif\", \"Nimbus Roman\", \"Nimbus Roman L\", \"Tinos\", \"Thorndale\", \"TeX Gyre Termes\", \"FreeSerif\", \"DejaVu Serif\", \"Bitstream Vera Serif\", \"Ubuntu\"],\n  style: NORMAL,\n  ultimate: \"serif\"\n}], [\"Times-Bold\", {\n  alias: \"Times-Roman\",\n  style: BOLD,\n  ultimate: \"serif\"\n}], [\"Times-Italic\", {\n  alias: \"Times-Roman\",\n  style: ITALIC,\n  ultimate: \"serif\"\n}], [\"Times-BoldItalic\", {\n  alias: \"Times-Roman\",\n  style: BOLDITALIC,\n  ultimate: \"serif\"\n}], [\"Helvetica\", {\n  local: [\"Helvetica\", \"Helvetica Neue\", \"Arial\", \"Arial Nova\", \"Liberation Sans\", \"Arimo\", \"Nimbus Sans\", \"Nimbus Sans L\", \"A030\", \"TeX Gyre Heros\", \"FreeSans\", \"DejaVu Sans\", \"Albany\", \"Bitstream Vera Sans\", \"Arial Unicode MS\", \"Microsoft Sans Serif\", \"Apple Symbols\", \"Cantarell\"],\n  path: \"LiberationSans-Regular.ttf\",\n  style: NORMAL,\n  ultimate: \"sans-serif\"\n}], [\"Helvetica-Bold\", {\n  alias: \"Helvetica\",\n  path: \"LiberationSans-Bold.ttf\",\n  style: BOLD,\n  ultimate: \"sans-serif\"\n}], [\"Helvetica-Oblique\", {\n  alias: \"Helvetica\",\n  path: \"LiberationSans-Italic.ttf\",\n  style: ITALIC,\n  ultimate: \"sans-serif\"\n}], [\"Helvetica-BoldOblique\", {\n  alias: \"Helvetica\",\n  path: \"LiberationSans-BoldItalic.ttf\",\n  style: BOLDITALIC,\n  ultimate: \"sans-serif\"\n}], [\"Courier\", {\n  local: [\"Courier\", \"Courier New\", \"Liberation Mono\", \"Nimbus Mono\", \"Nimbus Mono L\", \"Cousine\", \"Cumberland\", \"TeX Gyre Cursor\", \"FreeMono\"],\n  style: NORMAL,\n  ultimate: \"monospace\"\n}], [\"Courier-Bold\", {\n  alias: \"Courier\",\n  style: BOLD,\n  ultimate: \"monospace\"\n}], [\"Courier-Oblique\", {\n  alias: \"Courier\",\n  style: ITALIC,\n  ultimate: \"monospace\"\n}], [\"Courier-BoldOblique\", {\n  alias: \"Courier\",\n  style: BOLDITALIC,\n  ultimate: \"monospace\"\n}], [\"ArialBlack\", {\n  local: [\"Arial Black\"],\n  style: {\n    style: \"normal\",\n    weight: \"900\"\n  },\n  fallback: \"Helvetica-Bold\"\n}], [\"ArialBlack-Bold\", {\n  alias: \"ArialBlack\"\n}], [\"ArialBlack-Italic\", {\n  alias: \"ArialBlack\",\n  style: {\n    style: \"italic\",\n    weight: \"900\"\n  },\n  fallback: \"Helvetica-BoldOblique\"\n}], [\"ArialBlack-BoldItalic\", {\n  alias: \"ArialBlack-Italic\"\n}], [\"ArialNarrow\", {\n  local: [\"Arial Narrow\", \"Liberation Sans Narrow\", \"Helvetica Condensed\", \"Nimbus Sans Narrow\", \"TeX Gyre Heros Cn\"],\n  style: NORMAL,\n  fallback: \"Helvetica\"\n}], [\"ArialNarrow-Bold\", {\n  alias: \"ArialNarrow\",\n  style: BOLD,\n  fallback: \"Helvetica-Bold\"\n}], [\"ArialNarrow-Italic\", {\n  alias: \"ArialNarrow\",\n  style: ITALIC,\n  fallback: \"Helvetica-Oblique\"\n}], [\"ArialNarrow-BoldItalic\", {\n  alias: \"ArialNarrow\",\n  style: BOLDITALIC,\n  fallback: \"Helvetica-BoldOblique\"\n}], [\"Calibri\", {\n  local: [\"Calibri\", \"Carlito\"],\n  style: NORMAL,\n  fallback: \"Helvetica\"\n}], [\"Calibri-Bold\", {\n  alias: \"Calibri\",\n  style: BOLD,\n  fallback: \"Helvetica-Bold\"\n}], [\"Calibri-Italic\", {\n  alias: \"Calibri\",\n  style: ITALIC,\n  fallback: \"Helvetica-Oblique\"\n}], [\"Calibri-BoldItalic\", {\n  alias: \"Calibri\",\n  style: BOLDITALIC,\n  fallback: \"Helvetica-BoldOblique\"\n}], [\"Wingdings\", {\n  local: [\"Wingdings\", \"URW Dingbats\"],\n  style: NORMAL\n}], [\"Wingdings-Regular\", {\n  alias: \"Wingdings\"\n}], [\"Wingdings-Bold\", {\n  alias: \"Wingdings\"\n}]]);\nconst fontAliases = new Map([[\"Arial-Black\", \"ArialBlack\"]]);\nfunction getStyleToAppend(style) {\n  switch (style) {\n    case BOLD:\n      return \"Bold\";\n    case ITALIC:\n      return \"Italic\";\n    case BOLDITALIC:\n      return \"Bold Italic\";\n    default:\n      if (style?.weight === \"bold\") {\n        return \"Bold\";\n      }\n      if (style?.style === \"italic\") {\n        return \"Italic\";\n      }\n  }\n  return \"\";\n}\nfunction generateFont({\n  alias,\n  local,\n  path,\n  fallback,\n  style,\n  ultimate\n}, src, localFontPath, useFallback = true, usePath = true, append = \"\") {\n  const result = {\n    style: null,\n    ultimate: null\n  };\n  if (local) {\n    const extra = append ? ` ${append}` : \"\";\n    for (const name of local) {\n      src.push(`local(${name}${extra})`);\n    }\n  }\n  if (alias) {\n    const substitution = substitutionMap.get(alias);\n    const aliasAppend = append || getStyleToAppend(style);\n    Object.assign(result, generateFont(substitution, src, localFontPath, useFallback && !fallback, usePath && !path, aliasAppend));\n  }\n  if (style) {\n    result.style = style;\n  }\n  if (ultimate) {\n    result.ultimate = ultimate;\n  }\n  if (useFallback && fallback) {\n    const fallbackInfo = substitutionMap.get(fallback);\n    const {\n      ultimate: fallbackUltimate\n    } = generateFont(fallbackInfo, src, localFontPath, useFallback, usePath && !path, append);\n    result.ultimate ||= fallbackUltimate;\n  }\n  if (usePath && path && localFontPath) {\n    src.push(`url(${localFontPath}${path})`);\n  }\n  return result;\n}\nfunction getFontSubstitution(systemFontCache, idFactory, localFontPath, baseFontName, standardFontName) {\n  baseFontName = normalizeFontName(baseFontName);\n  const key = baseFontName;\n  let substitutionInfo = systemFontCache.get(key);\n  if (substitutionInfo) {\n    return substitutionInfo;\n  }\n  let substitution = substitutionMap.get(baseFontName);\n  if (!substitution) {\n    for (const [alias, subst] of fontAliases) {\n      if (baseFontName.startsWith(alias)) {\n        baseFontName = `${subst}${baseFontName.substring(alias.length)}`;\n        substitution = substitutionMap.get(baseFontName);\n        break;\n      }\n    }\n  }\n  let mustAddBaseFont = false;\n  if (!substitution) {\n    substitution = substitutionMap.get(standardFontName);\n    mustAddBaseFont = true;\n  }\n  const loadedName = `${idFactory.getDocId()}_s${idFactory.createFontId()}`;\n  if (!substitution) {\n    if (!validateFontName(baseFontName)) {\n      systemFontCache.set(key, null);\n      return null;\n    }\n    const bold = /bold/gi.test(baseFontName);\n    const italic = /oblique|italic/gi.test(baseFontName);\n    const style = bold && italic && BOLDITALIC || bold && BOLD || italic && ITALIC || NORMAL;\n    substitutionInfo = {\n      css: loadedName,\n      guessFallback: true,\n      loadedName,\n      baseFontName,\n      src: `local(${baseFontName})`,\n      style\n    };\n    systemFontCache.set(key, substitutionInfo);\n    return substitutionInfo;\n  }\n  const src = [];\n  if (mustAddBaseFont && validateFontName(baseFontName)) {\n    src.push(`local(${baseFontName})`);\n  }\n  const {\n    style,\n    ultimate\n  } = generateFont(substitution, src, localFontPath);\n  const guessFallback = ultimate === null;\n  const fallback = guessFallback ? \"\" : `,${ultimate}`;\n  substitutionInfo = {\n    css: `${loadedName}${fallback}`,\n    guessFallback,\n    loadedName,\n    baseFontName,\n    src: src.join(\",\"),\n    style\n  };\n  systemFontCache.set(key, substitutionInfo);\n  return substitutionInfo;\n}\n\n;// CONCATENATED MODULE: ./src/core/image_resizer.js\n\nconst MIN_IMAGE_DIM = 2048;\nconst MAX_IMAGE_DIM = 65537;\nconst MAX_ERROR = 128;\nclass ImageResizer {\n  constructor(imgData, isMask) {\n    this._imgData = imgData;\n    this._isMask = isMask;\n  }\n  static needsToBeResized(width, height) {\n    if (width <= this._goodSquareLength && height <= this._goodSquareLength) {\n      return false;\n    }\n    const {\n      MAX_DIM\n    } = this;\n    if (width > MAX_DIM || height > MAX_DIM) {\n      return true;\n    }\n    const area = width * height;\n    if (this._hasMaxArea) {\n      return area > this.MAX_AREA;\n    }\n    if (area < this._goodSquareLength ** 2) {\n      return false;\n    }\n    if (this._areGoodDims(width, height)) {\n      this._goodSquareLength = Math.max(this._goodSquareLength, Math.floor(Math.sqrt(width * height)));\n      return false;\n    }\n    this._goodSquareLength = this._guessMax(this._goodSquareLength, MAX_DIM, MAX_ERROR, 0);\n    const maxArea = this.MAX_AREA = this._goodSquareLength ** 2;\n    return area > maxArea;\n  }\n  static get MAX_DIM() {\n    return shadow(this, \"MAX_DIM\", this._guessMax(MIN_IMAGE_DIM, MAX_IMAGE_DIM, 0, 1));\n  }\n  static get MAX_AREA() {\n    this._hasMaxArea = true;\n    return shadow(this, \"MAX_AREA\", this._guessMax(ImageResizer._goodSquareLength, this.MAX_DIM, MAX_ERROR, 0) ** 2);\n  }\n  static set MAX_AREA(area) {\n    if (area >= 0) {\n      this._hasMaxArea = true;\n      shadow(this, \"MAX_AREA\", area);\n    }\n  }\n  static setMaxArea(area) {\n    if (!this._hasMaxArea) {\n      this.MAX_AREA = area >> 2;\n    }\n  }\n  static _areGoodDims(width, height) {\n    try {\n      const canvas = new OffscreenCanvas(width, height);\n      const ctx = canvas.getContext(\"2d\");\n      ctx.fillRect(0, 0, 1, 1);\n      const opacity = ctx.getImageData(0, 0, 1, 1).data[3];\n      canvas.width = canvas.height = 1;\n      return opacity !== 0;\n    } catch {\n      return false;\n    }\n  }\n  static _guessMax(start, end, tolerance, defaultHeight) {\n    while (start + tolerance + 1 < end) {\n      const middle = Math.floor((start + end) / 2);\n      const height = defaultHeight || middle;\n      if (this._areGoodDims(middle, height)) {\n        start = middle;\n      } else {\n        end = middle;\n      }\n    }\n    return start;\n  }\n  static async createImage(imgData, isMask = false) {\n    return new ImageResizer(imgData, isMask)._createImage();\n  }\n  async _createImage() {\n    const data = this._encodeBMP();\n    const blob = new Blob([data.buffer], {\n      type: \"image/bmp\"\n    });\n    const bitmapPromise = createImageBitmap(blob);\n    const {\n      MAX_AREA,\n      MAX_DIM\n    } = ImageResizer;\n    const {\n      _imgData: imgData\n    } = this;\n    const {\n      width,\n      height\n    } = imgData;\n    const minFactor = Math.max(width / MAX_DIM, height / MAX_DIM, Math.sqrt(width * height / MAX_AREA));\n    const firstFactor = Math.max(minFactor, 2);\n    const factor = Math.round(10 * (minFactor + 1.25)) / 10 / firstFactor;\n    const N = Math.floor(Math.log2(factor));\n    const steps = new Array(N + 2).fill(2);\n    steps[0] = firstFactor;\n    steps.splice(-1, 1, factor / (1 << N));\n    let newWidth = width;\n    let newHeight = height;\n    let bitmap = await bitmapPromise;\n    for (const step of steps) {\n      const prevWidth = newWidth;\n      const prevHeight = newHeight;\n      newWidth = Math.floor(newWidth / step) - 1;\n      newHeight = Math.floor(newHeight / step) - 1;\n      const canvas = new OffscreenCanvas(newWidth, newHeight);\n      const ctx = canvas.getContext(\"2d\");\n      ctx.drawImage(bitmap, 0, 0, prevWidth, prevHeight, 0, 0, newWidth, newHeight);\n      bitmap = canvas.transferToImageBitmap();\n    }\n    imgData.data = null;\n    imgData.bitmap = bitmap;\n    imgData.width = newWidth;\n    imgData.height = newHeight;\n    return imgData;\n  }\n  _encodeBMP() {\n    const {\n      width,\n      height,\n      kind\n    } = this._imgData;\n    let data = this._imgData.data;\n    let bitPerPixel;\n    let colorTable = new Uint8Array(0);\n    let maskTable = colorTable;\n    let compression = 0;\n    switch (kind) {\n      case ImageKind.GRAYSCALE_1BPP:\n        {\n          bitPerPixel = 1;\n          colorTable = new Uint8Array(this._isMask ? [255, 255, 255, 255, 0, 0, 0, 0] : [0, 0, 0, 0, 255, 255, 255, 255]);\n          const rowLen = width + 7 >> 3;\n          const rowSize = rowLen + 3 & -4;\n          if (rowLen !== rowSize) {\n            const newData = new Uint8Array(rowSize * height);\n            let k = 0;\n            for (let i = 0, ii = height * rowLen; i < ii; i += rowLen, k += rowSize) {\n              newData.set(data.subarray(i, i + rowLen), k);\n            }\n            data = newData;\n          }\n          break;\n        }\n      case ImageKind.RGB_24BPP:\n        {\n          bitPerPixel = 24;\n          if (width & 3) {\n            const rowLen = 3 * width;\n            const rowSize = rowLen + 3 & -4;\n            const extraLen = rowSize - rowLen;\n            const newData = new Uint8Array(rowSize * height);\n            let k = 0;\n            for (let i = 0, ii = height * rowLen; i < ii; i += rowLen) {\n              const row = data.subarray(i, i + rowLen);\n              for (let j = 0; j < rowLen; j += 3) {\n                newData[k++] = row[j + 2];\n                newData[k++] = row[j + 1];\n                newData[k++] = row[j];\n              }\n              k += extraLen;\n            }\n            data = newData;\n          } else {\n            for (let i = 0, ii = data.length; i < ii; i += 3) {\n              const tmp = data[i];\n              data[i] = data[i + 2];\n              data[i + 2] = tmp;\n            }\n          }\n          break;\n        }\n      case ImageKind.RGBA_32BPP:\n        bitPerPixel = 32;\n        compression = 3;\n        maskTable = new Uint8Array(4 + 4 + 4 + 4 + 52);\n        const view = new DataView(maskTable.buffer);\n        if (FeatureTest.isLittleEndian) {\n          view.setUint32(0, 0x000000ff, true);\n          view.setUint32(4, 0x0000ff00, true);\n          view.setUint32(8, 0x00ff0000, true);\n          view.setUint32(12, 0xff000000, true);\n        } else {\n          view.setUint32(0, 0xff000000, true);\n          view.setUint32(4, 0x00ff0000, true);\n          view.setUint32(8, 0x0000ff00, true);\n          view.setUint32(12, 0x000000ff, true);\n        }\n        break;\n      default:\n        throw new Error(\"invalid format\");\n    }\n    let i = 0;\n    const headerLength = 40 + maskTable.length;\n    const fileLength = 14 + headerLength + colorTable.length + data.length;\n    const bmpData = new Uint8Array(fileLength);\n    const view = new DataView(bmpData.buffer);\n    view.setUint16(i, 0x4d42, true);\n    i += 2;\n    view.setUint32(i, fileLength, true);\n    i += 4;\n    view.setUint32(i, 0, true);\n    i += 4;\n    view.setUint32(i, 14 + headerLength + colorTable.length, true);\n    i += 4;\n    view.setUint32(i, headerLength, true);\n    i += 4;\n    view.setInt32(i, width, true);\n    i += 4;\n    view.setInt32(i, -height, true);\n    i += 4;\n    view.setUint16(i, 1, true);\n    i += 2;\n    view.setUint16(i, bitPerPixel, true);\n    i += 2;\n    view.setUint32(i, compression, true);\n    i += 4;\n    view.setUint32(i, 0, true);\n    i += 4;\n    view.setInt32(i, 0, true);\n    i += 4;\n    view.setInt32(i, 0, true);\n    i += 4;\n    view.setUint32(i, colorTable.length / 4, true);\n    i += 4;\n    view.setUint32(i, 0, true);\n    i += 4;\n    bmpData.set(maskTable, i);\n    i += maskTable.length;\n    bmpData.set(colorTable, i);\n    i += colorTable.length;\n    bmpData.set(data, i);\n    return bmpData;\n  }\n}\nImageResizer._goodSquareLength = MIN_IMAGE_DIM;\n\n;// CONCATENATED MODULE: ./src/shared/murmurhash3.js\n\nconst SEED = 0xc3d2e1f0;\nconst MASK_HIGH = 0xffff0000;\nconst MASK_LOW = 0xffff;\nclass MurmurHash3_64 {\n  constructor(seed) {\n    this.h1 = seed ? seed & 0xffffffff : SEED;\n    this.h2 = seed ? seed & 0xffffffff : SEED;\n  }\n  update(input) {\n    let data, length;\n    if (typeof input === \"string\") {\n      data = new Uint8Array(input.length * 2);\n      length = 0;\n      for (let i = 0, ii = input.length; i < ii; i++) {\n        const code = input.charCodeAt(i);\n        if (code <= 0xff) {\n          data[length++] = code;\n        } else {\n          data[length++] = code >>> 8;\n          data[length++] = code & 0xff;\n        }\n      }\n    } else if (isArrayBuffer(input)) {\n      data = input.slice();\n      length = data.byteLength;\n    } else {\n      throw new Error(\"Wrong data format in MurmurHash3_64_update. \" + \"Input must be a string or array.\");\n    }\n    const blockCounts = length >> 2;\n    const tailLength = length - blockCounts * 4;\n    const dataUint32 = new Uint32Array(data.buffer, 0, blockCounts);\n    let k1 = 0,\n      k2 = 0;\n    let h1 = this.h1,\n      h2 = this.h2;\n    const C1 = 0xcc9e2d51,\n      C2 = 0x1b873593;\n    const C1_LOW = C1 & MASK_LOW,\n      C2_LOW = C2 & MASK_LOW;\n    for (let i = 0; i < blockCounts; i++) {\n      if (i & 1) {\n        k1 = dataUint32[i];\n        k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW;\n        k1 = k1 << 15 | k1 >>> 17;\n        k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW;\n        h1 ^= k1;\n        h1 = h1 << 13 | h1 >>> 19;\n        h1 = h1 * 5 + 0xe6546b64;\n      } else {\n        k2 = dataUint32[i];\n        k2 = k2 * C1 & MASK_HIGH | k2 * C1_LOW & MASK_LOW;\n        k2 = k2 << 15 | k2 >>> 17;\n        k2 = k2 * C2 & MASK_HIGH | k2 * C2_LOW & MASK_LOW;\n        h2 ^= k2;\n        h2 = h2 << 13 | h2 >>> 19;\n        h2 = h2 * 5 + 0xe6546b64;\n      }\n    }\n    k1 = 0;\n    switch (tailLength) {\n      case 3:\n        k1 ^= data[blockCounts * 4 + 2] << 16;\n      case 2:\n        k1 ^= data[blockCounts * 4 + 1] << 8;\n      case 1:\n        k1 ^= data[blockCounts * 4];\n        k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW;\n        k1 = k1 << 15 | k1 >>> 17;\n        k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW;\n        if (blockCounts & 1) {\n          h1 ^= k1;\n        } else {\n          h2 ^= k1;\n        }\n    }\n    this.h1 = h1;\n    this.h2 = h2;\n  }\n  hexdigest() {\n    let h1 = this.h1,\n      h2 = this.h2;\n    h1 ^= h2 >>> 1;\n    h1 = h1 * 0xed558ccd & MASK_HIGH | h1 * 0x8ccd & MASK_LOW;\n    h2 = h2 * 0xff51afd7 & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16;\n    h1 ^= h2 >>> 1;\n    h1 = h1 * 0x1a85ec53 & MASK_HIGH | h1 * 0xec53 & MASK_LOW;\n    h2 = h2 * 0xc4ceb9fe & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16;\n    h1 ^= h2 >>> 1;\n    return (h1 >>> 0).toString(16).padStart(8, \"0\") + (h2 >>> 0).toString(16).padStart(8, \"0\");\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/operator_list.js\n\nfunction addState(parentState, pattern, checkFn, iterateFn, processFn) {\n  let state = parentState;\n  for (let i = 0, ii = pattern.length - 1; i < ii; i++) {\n    const item = pattern[i];\n    state = state[item] ||= [];\n  }\n  state[pattern.at(-1)] = {\n    checkFn,\n    iterateFn,\n    processFn\n  };\n}\nconst InitialState = [];\naddState(InitialState, [OPS.save, OPS.transform, OPS.paintInlineImageXObject, OPS.restore], null, function iterateInlineImageGroup(context, i) {\n  const fnArray = context.fnArray;\n  const iFirstSave = context.iCurr - 3;\n  const pos = (i - iFirstSave) % 4;\n  switch (pos) {\n    case 0:\n      return fnArray[i] === OPS.save;\n    case 1:\n      return fnArray[i] === OPS.transform;\n    case 2:\n      return fnArray[i] === OPS.paintInlineImageXObject;\n    case 3:\n      return fnArray[i] === OPS.restore;\n  }\n  throw new Error(`iterateInlineImageGroup - invalid pos: ${pos}`);\n}, function foundInlineImageGroup(context, i) {\n  const MIN_IMAGES_IN_INLINE_IMAGES_BLOCK = 10;\n  const MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200;\n  const MAX_WIDTH = 1000;\n  const IMAGE_PADDING = 1;\n  const fnArray = context.fnArray,\n    argsArray = context.argsArray;\n  const curr = context.iCurr;\n  const iFirstSave = curr - 3;\n  const iFirstTransform = curr - 2;\n  const iFirstPIIXO = curr - 1;\n  const count = Math.min(Math.floor((i - iFirstSave) / 4), MAX_IMAGES_IN_INLINE_IMAGES_BLOCK);\n  if (count < MIN_IMAGES_IN_INLINE_IMAGES_BLOCK) {\n    return i - (i - iFirstSave) % 4;\n  }\n  let maxX = 0;\n  const map = [];\n  let maxLineHeight = 0;\n  let currentX = IMAGE_PADDING,\n    currentY = IMAGE_PADDING;\n  for (let q = 0; q < count; q++) {\n    const transform = argsArray[iFirstTransform + (q << 2)];\n    const img = argsArray[iFirstPIIXO + (q << 2)][0];\n    if (currentX + img.width > MAX_WIDTH) {\n      maxX = Math.max(maxX, currentX);\n      currentY += maxLineHeight + 2 * IMAGE_PADDING;\n      currentX = 0;\n      maxLineHeight = 0;\n    }\n    map.push({\n      transform,\n      x: currentX,\n      y: currentY,\n      w: img.width,\n      h: img.height\n    });\n    currentX += img.width + 2 * IMAGE_PADDING;\n    maxLineHeight = Math.max(maxLineHeight, img.height);\n  }\n  const imgWidth = Math.max(maxX, currentX) + IMAGE_PADDING;\n  const imgHeight = currentY + maxLineHeight + IMAGE_PADDING;\n  const imgData = new Uint8Array(imgWidth * imgHeight * 4);\n  const imgRowSize = imgWidth << 2;\n  for (let q = 0; q < count; q++) {\n    const data = argsArray[iFirstPIIXO + (q << 2)][0].data;\n    const rowSize = map[q].w << 2;\n    let dataOffset = 0;\n    let offset = map[q].x + map[q].y * imgWidth << 2;\n    imgData.set(data.subarray(0, rowSize), offset - imgRowSize);\n    for (let k = 0, kk = map[q].h; k < kk; k++) {\n      imgData.set(data.subarray(dataOffset, dataOffset + rowSize), offset);\n      dataOffset += rowSize;\n      offset += imgRowSize;\n    }\n    imgData.set(data.subarray(dataOffset - rowSize, dataOffset), offset);\n    while (offset >= 0) {\n      data[offset - 4] = data[offset];\n      data[offset - 3] = data[offset + 1];\n      data[offset - 2] = data[offset + 2];\n      data[offset - 1] = data[offset + 3];\n      data[offset + rowSize] = data[offset + rowSize - 4];\n      data[offset + rowSize + 1] = data[offset + rowSize - 3];\n      data[offset + rowSize + 2] = data[offset + rowSize - 2];\n      data[offset + rowSize + 3] = data[offset + rowSize - 1];\n      offset -= imgRowSize;\n    }\n  }\n  const img = {\n    width: imgWidth,\n    height: imgHeight\n  };\n  if (context.isOffscreenCanvasSupported) {\n    const canvas = new OffscreenCanvas(imgWidth, imgHeight);\n    const ctx = canvas.getContext(\"2d\");\n    ctx.putImageData(new ImageData(new Uint8ClampedArray(imgData.buffer), imgWidth, imgHeight), 0, 0);\n    img.bitmap = canvas.transferToImageBitmap();\n    img.data = null;\n  } else {\n    img.kind = ImageKind.RGBA_32BPP;\n    img.data = imgData;\n  }\n  fnArray.splice(iFirstSave, count * 4, OPS.paintInlineImageXObjectGroup);\n  argsArray.splice(iFirstSave, count * 4, [img, map]);\n  return iFirstSave + 1;\n});\naddState(InitialState, [OPS.save, OPS.transform, OPS.paintImageMaskXObject, OPS.restore], null, function iterateImageMaskGroup(context, i) {\n  const fnArray = context.fnArray;\n  const iFirstSave = context.iCurr - 3;\n  const pos = (i - iFirstSave) % 4;\n  switch (pos) {\n    case 0:\n      return fnArray[i] === OPS.save;\n    case 1:\n      return fnArray[i] === OPS.transform;\n    case 2:\n      return fnArray[i] === OPS.paintImageMaskXObject;\n    case 3:\n      return fnArray[i] === OPS.restore;\n  }\n  throw new Error(`iterateImageMaskGroup - invalid pos: ${pos}`);\n}, function foundImageMaskGroup(context, i) {\n  const MIN_IMAGES_IN_MASKS_BLOCK = 10;\n  const MAX_IMAGES_IN_MASKS_BLOCK = 100;\n  const MAX_SAME_IMAGES_IN_MASKS_BLOCK = 1000;\n  const fnArray = context.fnArray,\n    argsArray = context.argsArray;\n  const curr = context.iCurr;\n  const iFirstSave = curr - 3;\n  const iFirstTransform = curr - 2;\n  const iFirstPIMXO = curr - 1;\n  let count = Math.floor((i - iFirstSave) / 4);\n  if (count < MIN_IMAGES_IN_MASKS_BLOCK) {\n    return i - (i - iFirstSave) % 4;\n  }\n  let isSameImage = false;\n  let iTransform, transformArgs;\n  const firstPIMXOArg0 = argsArray[iFirstPIMXO][0];\n  const firstTransformArg0 = argsArray[iFirstTransform][0],\n    firstTransformArg1 = argsArray[iFirstTransform][1],\n    firstTransformArg2 = argsArray[iFirstTransform][2],\n    firstTransformArg3 = argsArray[iFirstTransform][3];\n  if (firstTransformArg1 === firstTransformArg2) {\n    isSameImage = true;\n    iTransform = iFirstTransform + 4;\n    let iPIMXO = iFirstPIMXO + 4;\n    for (let q = 1; q < count; q++, iTransform += 4, iPIMXO += 4) {\n      transformArgs = argsArray[iTransform];\n      if (argsArray[iPIMXO][0] !== firstPIMXOArg0 || transformArgs[0] !== firstTransformArg0 || transformArgs[1] !== firstTransformArg1 || transformArgs[2] !== firstTransformArg2 || transformArgs[3] !== firstTransformArg3) {\n        if (q < MIN_IMAGES_IN_MASKS_BLOCK) {\n          isSameImage = false;\n        } else {\n          count = q;\n        }\n        break;\n      }\n    }\n  }\n  if (isSameImage) {\n    count = Math.min(count, MAX_SAME_IMAGES_IN_MASKS_BLOCK);\n    const positions = new Float32Array(count * 2);\n    iTransform = iFirstTransform;\n    for (let q = 0; q < count; q++, iTransform += 4) {\n      transformArgs = argsArray[iTransform];\n      positions[q << 1] = transformArgs[4];\n      positions[(q << 1) + 1] = transformArgs[5];\n    }\n    fnArray.splice(iFirstSave, count * 4, OPS.paintImageMaskXObjectRepeat);\n    argsArray.splice(iFirstSave, count * 4, [firstPIMXOArg0, firstTransformArg0, firstTransformArg1, firstTransformArg2, firstTransformArg3, positions]);\n  } else {\n    count = Math.min(count, MAX_IMAGES_IN_MASKS_BLOCK);\n    const images = [];\n    for (let q = 0; q < count; q++) {\n      transformArgs = argsArray[iFirstTransform + (q << 2)];\n      const maskParams = argsArray[iFirstPIMXO + (q << 2)][0];\n      images.push({\n        data: maskParams.data,\n        width: maskParams.width,\n        height: maskParams.height,\n        interpolate: maskParams.interpolate,\n        count: maskParams.count,\n        transform: transformArgs\n      });\n    }\n    fnArray.splice(iFirstSave, count * 4, OPS.paintImageMaskXObjectGroup);\n    argsArray.splice(iFirstSave, count * 4, [images]);\n  }\n  return iFirstSave + 1;\n});\naddState(InitialState, [OPS.save, OPS.transform, OPS.paintImageXObject, OPS.restore], function (context) {\n  const argsArray = context.argsArray;\n  const iFirstTransform = context.iCurr - 2;\n  return argsArray[iFirstTransform][1] === 0 && argsArray[iFirstTransform][2] === 0;\n}, function iterateImageGroup(context, i) {\n  const fnArray = context.fnArray,\n    argsArray = context.argsArray;\n  const iFirstSave = context.iCurr - 3;\n  const pos = (i - iFirstSave) % 4;\n  switch (pos) {\n    case 0:\n      return fnArray[i] === OPS.save;\n    case 1:\n      if (fnArray[i] !== OPS.transform) {\n        return false;\n      }\n      const iFirstTransform = context.iCurr - 2;\n      const firstTransformArg0 = argsArray[iFirstTransform][0];\n      const firstTransformArg3 = argsArray[iFirstTransform][3];\n      if (argsArray[i][0] !== firstTransformArg0 || argsArray[i][1] !== 0 || argsArray[i][2] !== 0 || argsArray[i][3] !== firstTransformArg3) {\n        return false;\n      }\n      return true;\n    case 2:\n      if (fnArray[i] !== OPS.paintImageXObject) {\n        return false;\n      }\n      const iFirstPIXO = context.iCurr - 1;\n      const firstPIXOArg0 = argsArray[iFirstPIXO][0];\n      if (argsArray[i][0] !== firstPIXOArg0) {\n        return false;\n      }\n      return true;\n    case 3:\n      return fnArray[i] === OPS.restore;\n  }\n  throw new Error(`iterateImageGroup - invalid pos: ${pos}`);\n}, function (context, i) {\n  const MIN_IMAGES_IN_BLOCK = 3;\n  const MAX_IMAGES_IN_BLOCK = 1000;\n  const fnArray = context.fnArray,\n    argsArray = context.argsArray;\n  const curr = context.iCurr;\n  const iFirstSave = curr - 3;\n  const iFirstTransform = curr - 2;\n  const iFirstPIXO = curr - 1;\n  const firstPIXOArg0 = argsArray[iFirstPIXO][0];\n  const firstTransformArg0 = argsArray[iFirstTransform][0];\n  const firstTransformArg3 = argsArray[iFirstTransform][3];\n  const count = Math.min(Math.floor((i - iFirstSave) / 4), MAX_IMAGES_IN_BLOCK);\n  if (count < MIN_IMAGES_IN_BLOCK) {\n    return i - (i - iFirstSave) % 4;\n  }\n  const positions = new Float32Array(count * 2);\n  let iTransform = iFirstTransform;\n  for (let q = 0; q < count; q++, iTransform += 4) {\n    const transformArgs = argsArray[iTransform];\n    positions[q << 1] = transformArgs[4];\n    positions[(q << 1) + 1] = transformArgs[5];\n  }\n  const args = [firstPIXOArg0, firstTransformArg0, firstTransformArg3, positions];\n  fnArray.splice(iFirstSave, count * 4, OPS.paintImageXObjectRepeat);\n  argsArray.splice(iFirstSave, count * 4, args);\n  return iFirstSave + 1;\n});\naddState(InitialState, [OPS.beginText, OPS.setFont, OPS.setTextMatrix, OPS.showText, OPS.endText], null, function iterateShowTextGroup(context, i) {\n  const fnArray = context.fnArray,\n    argsArray = context.argsArray;\n  const iFirstSave = context.iCurr - 4;\n  const pos = (i - iFirstSave) % 5;\n  switch (pos) {\n    case 0:\n      return fnArray[i] === OPS.beginText;\n    case 1:\n      return fnArray[i] === OPS.setFont;\n    case 2:\n      return fnArray[i] === OPS.setTextMatrix;\n    case 3:\n      if (fnArray[i] !== OPS.showText) {\n        return false;\n      }\n      const iFirstSetFont = context.iCurr - 3;\n      const firstSetFontArg0 = argsArray[iFirstSetFont][0];\n      const firstSetFontArg1 = argsArray[iFirstSetFont][1];\n      if (argsArray[i][0] !== firstSetFontArg0 || argsArray[i][1] !== firstSetFontArg1) {\n        return false;\n      }\n      return true;\n    case 4:\n      return fnArray[i] === OPS.endText;\n  }\n  throw new Error(`iterateShowTextGroup - invalid pos: ${pos}`);\n}, function (context, i) {\n  const MIN_CHARS_IN_BLOCK = 3;\n  const MAX_CHARS_IN_BLOCK = 1000;\n  const fnArray = context.fnArray,\n    argsArray = context.argsArray;\n  const curr = context.iCurr;\n  const iFirstBeginText = curr - 4;\n  const iFirstSetFont = curr - 3;\n  const iFirstSetTextMatrix = curr - 2;\n  const iFirstShowText = curr - 1;\n  const iFirstEndText = curr;\n  const firstSetFontArg0 = argsArray[iFirstSetFont][0];\n  const firstSetFontArg1 = argsArray[iFirstSetFont][1];\n  let count = Math.min(Math.floor((i - iFirstBeginText) / 5), MAX_CHARS_IN_BLOCK);\n  if (count < MIN_CHARS_IN_BLOCK) {\n    return i - (i - iFirstBeginText) % 5;\n  }\n  let iFirst = iFirstBeginText;\n  if (iFirstBeginText >= 4 && fnArray[iFirstBeginText - 4] === fnArray[iFirstSetFont] && fnArray[iFirstBeginText - 3] === fnArray[iFirstSetTextMatrix] && fnArray[iFirstBeginText - 2] === fnArray[iFirstShowText] && fnArray[iFirstBeginText - 1] === fnArray[iFirstEndText] && argsArray[iFirstBeginText - 4][0] === firstSetFontArg0 && argsArray[iFirstBeginText - 4][1] === firstSetFontArg1) {\n    count++;\n    iFirst -= 5;\n  }\n  let iEndText = iFirst + 4;\n  for (let q = 1; q < count; q++) {\n    fnArray.splice(iEndText, 3);\n    argsArray.splice(iEndText, 3);\n    iEndText += 2;\n  }\n  return iEndText + 1;\n});\nclass NullOptimizer {\n  constructor(queue) {\n    this.queue = queue;\n  }\n  _optimize() {}\n  push(fn, args) {\n    this.queue.fnArray.push(fn);\n    this.queue.argsArray.push(args);\n    this._optimize();\n  }\n  flush() {}\n  reset() {}\n}\nclass QueueOptimizer extends NullOptimizer {\n  constructor(queue) {\n    super(queue);\n    this.state = null;\n    this.context = {\n      iCurr: 0,\n      fnArray: queue.fnArray,\n      argsArray: queue.argsArray,\n      isOffscreenCanvasSupported: false\n    };\n    this.match = null;\n    this.lastProcessed = 0;\n  }\n  set isOffscreenCanvasSupported(value) {\n    this.context.isOffscreenCanvasSupported = value;\n  }\n  _optimize() {\n    const fnArray = this.queue.fnArray;\n    let i = this.lastProcessed,\n      ii = fnArray.length;\n    let state = this.state;\n    let match = this.match;\n    if (!state && !match && i + 1 === ii && !InitialState[fnArray[i]]) {\n      this.lastProcessed = ii;\n      return;\n    }\n    const context = this.context;\n    while (i < ii) {\n      if (match) {\n        const iterate = (0, match.iterateFn)(context, i);\n        if (iterate) {\n          i++;\n          continue;\n        }\n        i = (0, match.processFn)(context, i + 1);\n        ii = fnArray.length;\n        match = null;\n        state = null;\n        if (i >= ii) {\n          break;\n        }\n      }\n      state = (state || InitialState)[fnArray[i]];\n      if (!state || Array.isArray(state)) {\n        i++;\n        continue;\n      }\n      context.iCurr = i;\n      i++;\n      if (state.checkFn && !(0, state.checkFn)(context)) {\n        state = null;\n        continue;\n      }\n      match = state;\n      state = null;\n    }\n    this.state = state;\n    this.match = match;\n    this.lastProcessed = i;\n  }\n  flush() {\n    while (this.match) {\n      const length = this.queue.fnArray.length;\n      this.lastProcessed = (0, this.match.processFn)(this.context, length);\n      this.match = null;\n      this.state = null;\n      this._optimize();\n    }\n  }\n  reset() {\n    this.state = null;\n    this.match = null;\n    this.lastProcessed = 0;\n  }\n}\nclass OperatorList {\n  static CHUNK_SIZE = 1000;\n  static CHUNK_SIZE_ABOUT = this.CHUNK_SIZE - 5;\n  constructor(intent = 0, streamSink) {\n    this._streamSink = streamSink;\n    this.fnArray = [];\n    this.argsArray = [];\n    this.optimizer = streamSink && !(intent & RenderingIntentFlag.OPLIST) ? new QueueOptimizer(this) : new NullOptimizer(this);\n    this.dependencies = new Set();\n    this._totalLength = 0;\n    this.weight = 0;\n    this._resolved = streamSink ? null : Promise.resolve();\n  }\n  set isOffscreenCanvasSupported(value) {\n    this.optimizer.isOffscreenCanvasSupported = value;\n  }\n  get length() {\n    return this.argsArray.length;\n  }\n  get ready() {\n    return this._resolved || this._streamSink.ready;\n  }\n  get totalLength() {\n    return this._totalLength + this.length;\n  }\n  addOp(fn, args) {\n    this.optimizer.push(fn, args);\n    this.weight++;\n    if (this._streamSink) {\n      if (this.weight >= OperatorList.CHUNK_SIZE) {\n        this.flush();\n      } else if (this.weight >= OperatorList.CHUNK_SIZE_ABOUT && (fn === OPS.restore || fn === OPS.endText)) {\n        this.flush();\n      }\n    }\n  }\n  addImageOps(fn, args, optionalContent) {\n    if (optionalContent !== undefined) {\n      this.addOp(OPS.beginMarkedContentProps, [\"OC\", optionalContent]);\n    }\n    this.addOp(fn, args);\n    if (optionalContent !== undefined) {\n      this.addOp(OPS.endMarkedContent, []);\n    }\n  }\n  addDependency(dependency) {\n    if (this.dependencies.has(dependency)) {\n      return;\n    }\n    this.dependencies.add(dependency);\n    this.addOp(OPS.dependency, [dependency]);\n  }\n  addDependencies(dependencies) {\n    for (const dependency of dependencies) {\n      this.addDependency(dependency);\n    }\n  }\n  addOpList(opList) {\n    if (!(opList instanceof OperatorList)) {\n      warn('addOpList - ignoring invalid \"opList\" parameter.');\n      return;\n    }\n    for (const dependency of opList.dependencies) {\n      this.dependencies.add(dependency);\n    }\n    for (let i = 0, ii = opList.length; i < ii; i++) {\n      this.addOp(opList.fnArray[i], opList.argsArray[i]);\n    }\n  }\n  getIR() {\n    return {\n      fnArray: this.fnArray,\n      argsArray: this.argsArray,\n      length: this.length\n    };\n  }\n  get _transfers() {\n    const transfers = [];\n    const {\n      fnArray,\n      argsArray,\n      length\n    } = this;\n    for (let i = 0; i < length; i++) {\n      switch (fnArray[i]) {\n        case OPS.paintInlineImageXObject:\n        case OPS.paintInlineImageXObjectGroup:\n        case OPS.paintImageMaskXObject:\n          const arg = argsArray[i][0];\n          if (!arg.cached && arg.data?.buffer instanceof ArrayBuffer) {\n            transfers.push(arg.data.buffer);\n          }\n          break;\n      }\n    }\n    return transfers;\n  }\n  flush(lastChunk = false, separateAnnots = null) {\n    this.optimizer.flush();\n    const length = this.length;\n    this._totalLength += length;\n    this._streamSink.enqueue({\n      fnArray: this.fnArray,\n      argsArray: this.argsArray,\n      lastChunk,\n      separateAnnots,\n      length\n    }, 1, this._transfers);\n    this.dependencies.clear();\n    this.fnArray.length = 0;\n    this.argsArray.length = 0;\n    this.weight = 0;\n    this.optimizer.reset();\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/image.js\n\n\n\n\n\n\n\n\n\nfunction decodeAndClamp(value, addend, coefficient, max) {\n  value = addend + value * coefficient;\n  if (value < 0) {\n    value = 0;\n  } else if (value > max) {\n    value = max;\n  }\n  return value;\n}\nfunction resizeImageMask(src, bpc, w1, h1, w2, h2) {\n  const length = w2 * h2;\n  let dest;\n  if (bpc <= 8) {\n    dest = new Uint8Array(length);\n  } else if (bpc <= 16) {\n    dest = new Uint16Array(length);\n  } else {\n    dest = new Uint32Array(length);\n  }\n  const xRatio = w1 / w2;\n  const yRatio = h1 / h2;\n  let i,\n    j,\n    py,\n    newIndex = 0,\n    oldIndex;\n  const xScaled = new Uint16Array(w2);\n  const w1Scanline = w1;\n  for (i = 0; i < w2; i++) {\n    xScaled[i] = Math.floor(i * xRatio);\n  }\n  for (i = 0; i < h2; i++) {\n    py = Math.floor(i * yRatio) * w1Scanline;\n    for (j = 0; j < w2; j++) {\n      oldIndex = py + xScaled[j];\n      dest[newIndex++] = src[oldIndex];\n    }\n  }\n  return dest;\n}\nclass PDFImage {\n  constructor({\n    xref,\n    res,\n    image,\n    isInline = false,\n    smask = null,\n    mask = null,\n    isMask = false,\n    pdfFunctionFactory,\n    localColorSpaceCache\n  }) {\n    this.image = image;\n    const dict = image.dict;\n    const filter = dict.get(\"F\", \"Filter\");\n    let filterName;\n    if (filter instanceof Name) {\n      filterName = filter.name;\n    } else if (Array.isArray(filter)) {\n      const filterZero = xref.fetchIfRef(filter[0]);\n      if (filterZero instanceof Name) {\n        filterName = filterZero.name;\n      }\n    }\n    switch (filterName) {\n      case \"JPXDecode\":\n        const jpxImage = new JpxImage();\n        jpxImage.parseImageProperties(image.stream);\n        image.stream.reset();\n        image.width = jpxImage.width;\n        image.height = jpxImage.height;\n        image.bitsPerComponent = jpxImage.bitsPerComponent;\n        image.numComps = jpxImage.componentsCount;\n        break;\n      case \"JBIG2Decode\":\n        image.bitsPerComponent = 1;\n        image.numComps = 1;\n        break;\n    }\n    let width = dict.get(\"W\", \"Width\");\n    let height = dict.get(\"H\", \"Height\");\n    if (Number.isInteger(image.width) && image.width > 0 && Number.isInteger(image.height) && image.height > 0 && (image.width !== width || image.height !== height)) {\n      warn(\"PDFImage - using the Width/Height of the image data, \" + \"rather than the image dictionary.\");\n      width = image.width;\n      height = image.height;\n    }\n    if (width < 1 || height < 1) {\n      throw new FormatError(`Invalid image width: ${width} or height: ${height}`);\n    }\n    this.width = width;\n    this.height = height;\n    this.interpolate = dict.get(\"I\", \"Interpolate\");\n    this.imageMask = dict.get(\"IM\", \"ImageMask\") || false;\n    this.matte = dict.get(\"Matte\") || false;\n    let bitsPerComponent = image.bitsPerComponent;\n    if (!bitsPerComponent) {\n      bitsPerComponent = dict.get(\"BPC\", \"BitsPerComponent\");\n      if (!bitsPerComponent) {\n        if (this.imageMask) {\n          bitsPerComponent = 1;\n        } else {\n          throw new FormatError(`Bits per component missing in image: ${this.imageMask}`);\n        }\n      }\n    }\n    this.bpc = bitsPerComponent;\n    if (!this.imageMask) {\n      let colorSpace = dict.getRaw(\"CS\") || dict.getRaw(\"ColorSpace\");\n      if (!colorSpace) {\n        info(\"JPX images (which do not require color spaces)\");\n        switch (image.numComps) {\n          case 1:\n            colorSpace = Name.get(\"DeviceGray\");\n            break;\n          case 3:\n            colorSpace = Name.get(\"DeviceRGB\");\n            break;\n          case 4:\n            colorSpace = Name.get(\"DeviceCMYK\");\n            break;\n          default:\n            throw new Error(`JPX images with ${image.numComps} color components not supported.`);\n        }\n      }\n      this.colorSpace = ColorSpace.parse({\n        cs: colorSpace,\n        xref,\n        resources: isInline ? res : null,\n        pdfFunctionFactory,\n        localColorSpaceCache\n      });\n      this.numComps = this.colorSpace.numComps;\n    }\n    this.decode = dict.getArray(\"D\", \"Decode\");\n    this.needsDecode = false;\n    if (this.decode && (this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode, bitsPerComponent) || isMask && !ColorSpace.isDefaultDecode(this.decode, 1))) {\n      this.needsDecode = true;\n      const max = (1 << bitsPerComponent) - 1;\n      this.decodeCoefficients = [];\n      this.decodeAddends = [];\n      const isIndexed = this.colorSpace?.name === \"Indexed\";\n      for (let i = 0, j = 0; i < this.decode.length; i += 2, ++j) {\n        const dmin = this.decode[i];\n        const dmax = this.decode[i + 1];\n        this.decodeCoefficients[j] = isIndexed ? (dmax - dmin) / max : dmax - dmin;\n        this.decodeAddends[j] = isIndexed ? dmin : max * dmin;\n      }\n    }\n    if (smask) {\n      this.smask = new PDFImage({\n        xref,\n        res,\n        image: smask,\n        isInline,\n        pdfFunctionFactory,\n        localColorSpaceCache\n      });\n    } else if (mask) {\n      if (mask instanceof BaseStream) {\n        const maskDict = mask.dict,\n          imageMask = maskDict.get(\"IM\", \"ImageMask\");\n        if (!imageMask) {\n          warn(\"Ignoring /Mask in image without /ImageMask.\");\n        } else {\n          this.mask = new PDFImage({\n            xref,\n            res,\n            image: mask,\n            isInline,\n            isMask: true,\n            pdfFunctionFactory,\n            localColorSpaceCache\n          });\n        }\n      } else {\n        this.mask = mask;\n      }\n    }\n  }\n  static async buildImage({\n    xref,\n    res,\n    image,\n    isInline = false,\n    pdfFunctionFactory,\n    localColorSpaceCache\n  }) {\n    const imageData = image;\n    let smaskData = null;\n    let maskData = null;\n    const smask = image.dict.get(\"SMask\");\n    const mask = image.dict.get(\"Mask\");\n    if (smask) {\n      if (smask instanceof BaseStream) {\n        smaskData = smask;\n      } else {\n        warn(\"Unsupported /SMask format.\");\n      }\n    } else if (mask) {\n      if (mask instanceof BaseStream || Array.isArray(mask)) {\n        maskData = mask;\n      } else {\n        warn(\"Unsupported /Mask format.\");\n      }\n    }\n    return new PDFImage({\n      xref,\n      res,\n      image: imageData,\n      isInline,\n      smask: smaskData,\n      mask: maskData,\n      pdfFunctionFactory,\n      localColorSpaceCache\n    });\n  }\n  static createRawMask({\n    imgArray,\n    width,\n    height,\n    imageIsFromDecodeStream,\n    inverseDecode,\n    interpolate\n  }) {\n    const computedLength = (width + 7 >> 3) * height;\n    const actualLength = imgArray.byteLength;\n    const haveFullData = computedLength === actualLength;\n    let data, i;\n    if (imageIsFromDecodeStream && (!inverseDecode || haveFullData)) {\n      data = imgArray;\n    } else if (!inverseDecode) {\n      data = new Uint8Array(imgArray);\n    } else {\n      data = new Uint8Array(computedLength);\n      data.set(imgArray);\n      data.fill(0xff, actualLength);\n    }\n    if (inverseDecode) {\n      for (i = 0; i < actualLength; i++) {\n        data[i] ^= 0xff;\n      }\n    }\n    return {\n      data,\n      width,\n      height,\n      interpolate\n    };\n  }\n  static async createMask({\n    imgArray,\n    width,\n    height,\n    imageIsFromDecodeStream,\n    inverseDecode,\n    interpolate,\n    isOffscreenCanvasSupported = false\n  }) {\n    const isSingleOpaquePixel = width === 1 && height === 1 && inverseDecode === (imgArray.length === 0 || !!(imgArray[0] & 128));\n    if (isSingleOpaquePixel) {\n      return {\n        isSingleOpaquePixel\n      };\n    }\n    if (isOffscreenCanvasSupported) {\n      if (ImageResizer.needsToBeResized(width, height)) {\n        const data = new Uint8ClampedArray(width * height * 4);\n        convertBlackAndWhiteToRGBA({\n          src: imgArray,\n          dest: data,\n          width,\n          height,\n          nonBlackColor: 0,\n          inverseDecode\n        });\n        return ImageResizer.createImage({\n          kind: ImageKind.RGBA_32BPP,\n          data,\n          width,\n          height,\n          interpolate\n        });\n      }\n      const canvas = new OffscreenCanvas(width, height);\n      const ctx = canvas.getContext(\"2d\");\n      const imgData = ctx.createImageData(width, height);\n      convertBlackAndWhiteToRGBA({\n        src: imgArray,\n        dest: imgData.data,\n        width,\n        height,\n        nonBlackColor: 0,\n        inverseDecode\n      });\n      ctx.putImageData(imgData, 0, 0);\n      const bitmap = canvas.transferToImageBitmap();\n      return {\n        data: null,\n        width,\n        height,\n        interpolate,\n        bitmap\n      };\n    }\n    return this.createRawMask({\n      imgArray,\n      width,\n      height,\n      inverseDecode,\n      imageIsFromDecodeStream,\n      interpolate\n    });\n  }\n  get drawWidth() {\n    return Math.max(this.width, this.smask?.width || 0, this.mask?.width || 0);\n  }\n  get drawHeight() {\n    return Math.max(this.height, this.smask?.height || 0, this.mask?.height || 0);\n  }\n  decodeBuffer(buffer) {\n    const bpc = this.bpc;\n    const numComps = this.numComps;\n    const decodeAddends = this.decodeAddends;\n    const decodeCoefficients = this.decodeCoefficients;\n    const max = (1 << bpc) - 1;\n    let i, ii;\n    if (bpc === 1) {\n      for (i = 0, ii = buffer.length; i < ii; i++) {\n        buffer[i] = +!buffer[i];\n      }\n      return;\n    }\n    let index = 0;\n    for (i = 0, ii = this.width * this.height; i < ii; i++) {\n      for (let j = 0; j < numComps; j++) {\n        buffer[index] = decodeAndClamp(buffer[index], decodeAddends[j], decodeCoefficients[j], max);\n        index++;\n      }\n    }\n  }\n  getComponents(buffer) {\n    const bpc = this.bpc;\n    if (bpc === 8) {\n      return buffer;\n    }\n    const width = this.width;\n    const height = this.height;\n    const numComps = this.numComps;\n    const length = width * height * numComps;\n    let bufferPos = 0;\n    let output;\n    if (bpc <= 8) {\n      output = new Uint8Array(length);\n    } else if (bpc <= 16) {\n      output = new Uint16Array(length);\n    } else {\n      output = new Uint32Array(length);\n    }\n    const rowComps = width * numComps;\n    const max = (1 << bpc) - 1;\n    let i = 0,\n      ii,\n      buf;\n    if (bpc === 1) {\n      let mask, loop1End, loop2End;\n      for (let j = 0; j < height; j++) {\n        loop1End = i + (rowComps & ~7);\n        loop2End = i + rowComps;\n        while (i < loop1End) {\n          buf = buffer[bufferPos++];\n          output[i] = buf >> 7 & 1;\n          output[i + 1] = buf >> 6 & 1;\n          output[i + 2] = buf >> 5 & 1;\n          output[i + 3] = buf >> 4 & 1;\n          output[i + 4] = buf >> 3 & 1;\n          output[i + 5] = buf >> 2 & 1;\n          output[i + 6] = buf >> 1 & 1;\n          output[i + 7] = buf & 1;\n          i += 8;\n        }\n        if (i < loop2End) {\n          buf = buffer[bufferPos++];\n          mask = 128;\n          while (i < loop2End) {\n            output[i++] = +!!(buf & mask);\n            mask >>= 1;\n          }\n        }\n      }\n    } else {\n      let bits = 0;\n      buf = 0;\n      for (i = 0, ii = length; i < ii; ++i) {\n        if (i % rowComps === 0) {\n          buf = 0;\n          bits = 0;\n        }\n        while (bits < bpc) {\n          buf = buf << 8 | buffer[bufferPos++];\n          bits += 8;\n        }\n        const remainingBits = bits - bpc;\n        let value = buf >> remainingBits;\n        if (value < 0) {\n          value = 0;\n        } else if (value > max) {\n          value = max;\n        }\n        output[i] = value;\n        buf &= (1 << remainingBits) - 1;\n        bits = remainingBits;\n      }\n    }\n    return output;\n  }\n  fillOpacity(rgbaBuf, width, height, actualHeight, image) {\n    const smask = this.smask;\n    const mask = this.mask;\n    let alphaBuf, sw, sh, i, ii, j;\n    if (smask) {\n      sw = smask.width;\n      sh = smask.height;\n      alphaBuf = new Uint8ClampedArray(sw * sh);\n      smask.fillGrayBuffer(alphaBuf);\n      if (sw !== width || sh !== height) {\n        alphaBuf = resizeImageMask(alphaBuf, smask.bpc, sw, sh, width, height);\n      }\n    } else if (mask) {\n      if (mask instanceof PDFImage) {\n        sw = mask.width;\n        sh = mask.height;\n        alphaBuf = new Uint8ClampedArray(sw * sh);\n        mask.numComps = 1;\n        mask.fillGrayBuffer(alphaBuf);\n        for (i = 0, ii = sw * sh; i < ii; ++i) {\n          alphaBuf[i] = 255 - alphaBuf[i];\n        }\n        if (sw !== width || sh !== height) {\n          alphaBuf = resizeImageMask(alphaBuf, mask.bpc, sw, sh, width, height);\n        }\n      } else if (Array.isArray(mask)) {\n        alphaBuf = new Uint8ClampedArray(width * height);\n        const numComps = this.numComps;\n        for (i = 0, ii = width * height; i < ii; ++i) {\n          let opacity = 0;\n          const imageOffset = i * numComps;\n          for (j = 0; j < numComps; ++j) {\n            const color = image[imageOffset + j];\n            const maskOffset = j * 2;\n            if (color < mask[maskOffset] || color > mask[maskOffset + 1]) {\n              opacity = 255;\n              break;\n            }\n          }\n          alphaBuf[i] = opacity;\n        }\n      } else {\n        throw new FormatError(\"Unknown mask format.\");\n      }\n    }\n    if (alphaBuf) {\n      for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {\n        rgbaBuf[j] = alphaBuf[i];\n      }\n    } else {\n      for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {\n        rgbaBuf[j] = 255;\n      }\n    }\n  }\n  undoPreblend(buffer, width, height) {\n    const matte = this.smask?.matte;\n    if (!matte) {\n      return;\n    }\n    const matteRgb = this.colorSpace.getRgb(matte, 0);\n    const matteR = matteRgb[0];\n    const matteG = matteRgb[1];\n    const matteB = matteRgb[2];\n    const length = width * height * 4;\n    for (let i = 0; i < length; i += 4) {\n      const alpha = buffer[i + 3];\n      if (alpha === 0) {\n        buffer[i] = 255;\n        buffer[i + 1] = 255;\n        buffer[i + 2] = 255;\n        continue;\n      }\n      const k = 255 / alpha;\n      buffer[i] = (buffer[i] - matteR) * k + matteR;\n      buffer[i + 1] = (buffer[i + 1] - matteG) * k + matteG;\n      buffer[i + 2] = (buffer[i + 2] - matteB) * k + matteB;\n    }\n  }\n  async createImageData(forceRGBA = false, isOffscreenCanvasSupported = false) {\n    const drawWidth = this.drawWidth;\n    const drawHeight = this.drawHeight;\n    const imgData = {\n      width: drawWidth,\n      height: drawHeight,\n      interpolate: this.interpolate,\n      kind: 0,\n      data: null\n    };\n    const numComps = this.numComps;\n    const originalWidth = this.width;\n    const originalHeight = this.height;\n    const bpc = this.bpc;\n    const rowBytes = originalWidth * numComps * bpc + 7 >> 3;\n    const mustBeResized = isOffscreenCanvasSupported && ImageResizer.needsToBeResized(drawWidth, drawHeight);\n    if (!forceRGBA) {\n      let kind;\n      if (this.colorSpace.name === \"DeviceGray\" && bpc === 1) {\n        kind = ImageKind.GRAYSCALE_1BPP;\n      } else if (this.colorSpace.name === \"DeviceRGB\" && bpc === 8 && !this.needsDecode) {\n        kind = ImageKind.RGB_24BPP;\n      }\n      if (kind && !this.smask && !this.mask && drawWidth === originalWidth && drawHeight === originalHeight) {\n        const data = this.getImageBytes(originalHeight * rowBytes, {});\n        if (isOffscreenCanvasSupported) {\n          if (mustBeResized) {\n            return ImageResizer.createImage({\n              data,\n              kind,\n              width: drawWidth,\n              height: drawHeight,\n              interpolate: this.interpolate\n            }, this.needsDecode);\n          }\n          return this.createBitmap(kind, originalWidth, originalHeight, data);\n        }\n        imgData.kind = kind;\n        imgData.data = data;\n        if (this.needsDecode) {\n          assert(kind === ImageKind.GRAYSCALE_1BPP, \"PDFImage.createImageData: The image must be grayscale.\");\n          const buffer = imgData.data;\n          for (let i = 0, ii = buffer.length; i < ii; i++) {\n            buffer[i] ^= 0xff;\n          }\n        }\n        return imgData;\n      }\n      if (this.image instanceof JpegStream && !this.smask && !this.mask && !this.needsDecode) {\n        let imageLength = originalHeight * rowBytes;\n        if (isOffscreenCanvasSupported && !mustBeResized) {\n          let isHandled = false;\n          switch (this.colorSpace.name) {\n            case \"DeviceGray\":\n              imageLength *= 4;\n              isHandled = true;\n              break;\n            case \"DeviceRGB\":\n              imageLength = imageLength / 3 * 4;\n              isHandled = true;\n              break;\n            case \"DeviceCMYK\":\n              isHandled = true;\n              break;\n          }\n          if (isHandled) {\n            const rgba = this.getImageBytes(imageLength, {\n              drawWidth,\n              drawHeight,\n              forceRGBA: true\n            });\n            return this.createBitmap(ImageKind.RGBA_32BPP, drawWidth, drawHeight, rgba);\n          }\n        } else {\n          switch (this.colorSpace.name) {\n            case \"DeviceGray\":\n              imageLength *= 3;\n            case \"DeviceRGB\":\n            case \"DeviceCMYK\":\n              imgData.kind = ImageKind.RGB_24BPP;\n              imgData.data = this.getImageBytes(imageLength, {\n                drawWidth,\n                drawHeight,\n                forceRGB: true\n              });\n              if (mustBeResized) {\n                return ImageResizer.createImage(imgData);\n              }\n              return imgData;\n          }\n        }\n      }\n    }\n    const imgArray = this.getImageBytes(originalHeight * rowBytes, {\n      internal: true\n    });\n    const actualHeight = 0 | imgArray.length / rowBytes * drawHeight / originalHeight;\n    const comps = this.getComponents(imgArray);\n    let alpha01, maybeUndoPreblend;\n    let canvas, ctx, canvasImgData, data;\n    if (isOffscreenCanvasSupported && !mustBeResized) {\n      canvas = new OffscreenCanvas(drawWidth, drawHeight);\n      ctx = canvas.getContext(\"2d\");\n      canvasImgData = ctx.createImageData(drawWidth, drawHeight);\n      data = canvasImgData.data;\n    }\n    imgData.kind = ImageKind.RGBA_32BPP;\n    if (!forceRGBA && !this.smask && !this.mask) {\n      if (!isOffscreenCanvasSupported || mustBeResized) {\n        imgData.kind = ImageKind.RGB_24BPP;\n        data = new Uint8ClampedArray(drawWidth * drawHeight * 3);\n        alpha01 = 0;\n      } else {\n        const arr = new Uint32Array(data.buffer);\n        arr.fill(FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff);\n        alpha01 = 1;\n      }\n      maybeUndoPreblend = false;\n    } else {\n      if (!isOffscreenCanvasSupported || mustBeResized) {\n        data = new Uint8ClampedArray(drawWidth * drawHeight * 4);\n      }\n      alpha01 = 1;\n      maybeUndoPreblend = true;\n      this.fillOpacity(data, drawWidth, drawHeight, actualHeight, comps);\n    }\n    if (this.needsDecode) {\n      this.decodeBuffer(comps);\n    }\n    this.colorSpace.fillRgb(data, originalWidth, originalHeight, drawWidth, drawHeight, actualHeight, bpc, comps, alpha01);\n    if (maybeUndoPreblend) {\n      this.undoPreblend(data, drawWidth, actualHeight);\n    }\n    if (isOffscreenCanvasSupported && !mustBeResized) {\n      ctx.putImageData(canvasImgData, 0, 0);\n      const bitmap = canvas.transferToImageBitmap();\n      return {\n        data: null,\n        width: drawWidth,\n        height: drawHeight,\n        bitmap,\n        interpolate: this.interpolate\n      };\n    }\n    imgData.data = data;\n    if (mustBeResized) {\n      return ImageResizer.createImage(imgData);\n    }\n    return imgData;\n  }\n  fillGrayBuffer(buffer) {\n    const numComps = this.numComps;\n    if (numComps !== 1) {\n      throw new FormatError(`Reading gray scale from a color image: ${numComps}`);\n    }\n    const width = this.width;\n    const height = this.height;\n    const bpc = this.bpc;\n    const rowBytes = width * numComps * bpc + 7 >> 3;\n    const imgArray = this.getImageBytes(height * rowBytes, {\n      internal: true\n    });\n    const comps = this.getComponents(imgArray);\n    let i, length;\n    if (bpc === 1) {\n      length = width * height;\n      if (this.needsDecode) {\n        for (i = 0; i < length; ++i) {\n          buffer[i] = comps[i] - 1 & 255;\n        }\n      } else {\n        for (i = 0; i < length; ++i) {\n          buffer[i] = -comps[i] & 255;\n        }\n      }\n      return;\n    }\n    if (this.needsDecode) {\n      this.decodeBuffer(comps);\n    }\n    length = width * height;\n    const scale = 255 / ((1 << bpc) - 1);\n    for (i = 0; i < length; ++i) {\n      buffer[i] = scale * comps[i];\n    }\n  }\n  createBitmap(kind, width, height, src) {\n    const canvas = new OffscreenCanvas(width, height);\n    const ctx = canvas.getContext(\"2d\");\n    let imgData;\n    if (kind === ImageKind.RGBA_32BPP) {\n      imgData = new ImageData(src, width, height);\n    } else {\n      imgData = ctx.createImageData(width, height);\n      convertToRGBA({\n        kind,\n        src,\n        dest: new Uint32Array(imgData.data.buffer),\n        width,\n        height,\n        inverseDecode: this.needsDecode\n      });\n    }\n    ctx.putImageData(imgData, 0, 0);\n    const bitmap = canvas.transferToImageBitmap();\n    return {\n      data: null,\n      width,\n      height,\n      bitmap,\n      interpolate: this.interpolate\n    };\n  }\n  getImageBytes(length, {\n    drawWidth,\n    drawHeight,\n    forceRGBA = false,\n    forceRGB = false,\n    internal = false\n  }) {\n    this.image.reset();\n    this.image.drawWidth = drawWidth || this.width;\n    this.image.drawHeight = drawHeight || this.height;\n    this.image.forceRGBA = !!forceRGBA;\n    this.image.forceRGB = !!forceRGB;\n    const imageBytes = this.image.getBytes(length);\n    if (internal || this.image instanceof DecodeStream) {\n      return imageBytes;\n    }\n    assert(imageBytes instanceof Uint8Array, 'PDFImage.getImageBytes: Unsupported \"imageBytes\" type.');\n    return new Uint8Array(imageBytes);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/evaluator.js\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nconst DefaultPartialEvaluatorOptions = Object.freeze({\n  maxImageSize: -1,\n  disableFontFace: false,\n  ignoreErrors: false,\n  isEvalSupported: true,\n  isOffscreenCanvasSupported: false,\n  canvasMaxAreaInBytes: -1,\n  fontExtraProperties: false,\n  useSystemFonts: true,\n  cMapUrl: null,\n  standardFontDataUrl: null\n});\nconst PatternType = {\n  TILING: 1,\n  SHADING: 2\n};\nconst TEXT_CHUNK_BATCH_SIZE = 10;\nconst deferred = Promise.resolve();\nfunction normalizeBlendMode(value, parsingArray = false) {\n  if (Array.isArray(value)) {\n    for (const val of value) {\n      const maybeBM = normalizeBlendMode(val, true);\n      if (maybeBM) {\n        return maybeBM;\n      }\n    }\n    warn(`Unsupported blend mode Array: ${value}`);\n    return \"source-over\";\n  }\n  if (!(value instanceof Name)) {\n    if (parsingArray) {\n      return null;\n    }\n    return \"source-over\";\n  }\n  switch (value.name) {\n    case \"Normal\":\n    case \"Compatible\":\n      return \"source-over\";\n    case \"Multiply\":\n      return \"multiply\";\n    case \"Screen\":\n      return \"screen\";\n    case \"Overlay\":\n      return \"overlay\";\n    case \"Darken\":\n      return \"darken\";\n    case \"Lighten\":\n      return \"lighten\";\n    case \"ColorDodge\":\n      return \"color-dodge\";\n    case \"ColorBurn\":\n      return \"color-burn\";\n    case \"HardLight\":\n      return \"hard-light\";\n    case \"SoftLight\":\n      return \"soft-light\";\n    case \"Difference\":\n      return \"difference\";\n    case \"Exclusion\":\n      return \"exclusion\";\n    case \"Hue\":\n      return \"hue\";\n    case \"Saturation\":\n      return \"saturation\";\n    case \"Color\":\n      return \"color\";\n    case \"Luminosity\":\n      return \"luminosity\";\n  }\n  if (parsingArray) {\n    return null;\n  }\n  warn(`Unsupported blend mode: ${value.name}`);\n  return \"source-over\";\n}\nfunction incrementCachedImageMaskCount(data) {\n  if (data.fn === OPS.paintImageMaskXObject && data.args[0]?.count > 0) {\n    data.args[0].count++;\n  }\n}\nclass TimeSlotManager {\n  static TIME_SLOT_DURATION_MS = 20;\n  static CHECK_TIME_EVERY = 100;\n  constructor() {\n    this.reset();\n  }\n  check() {\n    if (++this.checked < TimeSlotManager.CHECK_TIME_EVERY) {\n      return false;\n    }\n    this.checked = 0;\n    return this.endTime <= Date.now();\n  }\n  reset() {\n    this.endTime = Date.now() + TimeSlotManager.TIME_SLOT_DURATION_MS;\n    this.checked = 0;\n  }\n}\nclass PartialEvaluator {\n  constructor({\n    xref,\n    handler,\n    pageIndex,\n    idFactory,\n    fontCache,\n    builtInCMapCache,\n    standardFontDataCache,\n    globalImageCache,\n    systemFontCache,\n    options = null\n  }) {\n    this.xref = xref;\n    this.handler = handler;\n    this.pageIndex = pageIndex;\n    this.idFactory = idFactory;\n    this.fontCache = fontCache;\n    this.builtInCMapCache = builtInCMapCache;\n    this.standardFontDataCache = standardFontDataCache;\n    this.globalImageCache = globalImageCache;\n    this.systemFontCache = systemFontCache;\n    this.options = options || DefaultPartialEvaluatorOptions;\n    this.parsingType3Font = false;\n    this._regionalImageCache = new RegionalImageCache();\n    this._fetchBuiltInCMapBound = this.fetchBuiltInCMap.bind(this);\n    ImageResizer.setMaxArea(this.options.canvasMaxAreaInBytes);\n  }\n  get _pdfFunctionFactory() {\n    const pdfFunctionFactory = new PDFFunctionFactory({\n      xref: this.xref,\n      isEvalSupported: this.options.isEvalSupported\n    });\n    return shadow(this, \"_pdfFunctionFactory\", pdfFunctionFactory);\n  }\n  clone(newOptions = null) {\n    const newEvaluator = Object.create(this);\n    newEvaluator.options = Object.assign(Object.create(null), this.options, newOptions);\n    return newEvaluator;\n  }\n  hasBlendModes(resources, nonBlendModesSet) {\n    if (!(resources instanceof Dict)) {\n      return false;\n    }\n    if (resources.objId && nonBlendModesSet.has(resources.objId)) {\n      return false;\n    }\n    const processed = new RefSet(nonBlendModesSet);\n    if (resources.objId) {\n      processed.put(resources.objId);\n    }\n    const nodes = [resources],\n      xref = this.xref;\n    while (nodes.length) {\n      const node = nodes.shift();\n      const graphicStates = node.get(\"ExtGState\");\n      if (graphicStates instanceof Dict) {\n        for (let graphicState of graphicStates.getRawValues()) {\n          if (graphicState instanceof Ref) {\n            if (processed.has(graphicState)) {\n              continue;\n            }\n            try {\n              graphicState = xref.fetch(graphicState);\n            } catch (ex) {\n              processed.put(graphicState);\n              info(`hasBlendModes - ignoring ExtGState: \"${ex}\".`);\n              continue;\n            }\n          }\n          if (!(graphicState instanceof Dict)) {\n            continue;\n          }\n          if (graphicState.objId) {\n            processed.put(graphicState.objId);\n          }\n          const bm = graphicState.get(\"BM\");\n          if (bm instanceof Name) {\n            if (bm.name !== \"Normal\") {\n              return true;\n            }\n            continue;\n          }\n          if (bm !== undefined && Array.isArray(bm)) {\n            for (const element of bm) {\n              if (element instanceof Name && element.name !== \"Normal\") {\n                return true;\n              }\n            }\n          }\n        }\n      }\n      const xObjects = node.get(\"XObject\");\n      if (!(xObjects instanceof Dict)) {\n        continue;\n      }\n      for (let xObject of xObjects.getRawValues()) {\n        if (xObject instanceof Ref) {\n          if (processed.has(xObject)) {\n            continue;\n          }\n          try {\n            xObject = xref.fetch(xObject);\n          } catch (ex) {\n            processed.put(xObject);\n            info(`hasBlendModes - ignoring XObject: \"${ex}\".`);\n            continue;\n          }\n        }\n        if (!(xObject instanceof BaseStream)) {\n          continue;\n        }\n        if (xObject.dict.objId) {\n          processed.put(xObject.dict.objId);\n        }\n        const xResources = xObject.dict.get(\"Resources\");\n        if (!(xResources instanceof Dict)) {\n          continue;\n        }\n        if (xResources.objId && processed.has(xResources.objId)) {\n          continue;\n        }\n        nodes.push(xResources);\n        if (xResources.objId) {\n          processed.put(xResources.objId);\n        }\n      }\n    }\n    for (const ref of processed) {\n      nonBlendModesSet.put(ref);\n    }\n    return false;\n  }\n  async fetchBuiltInCMap(name) {\n    const cachedData = this.builtInCMapCache.get(name);\n    if (cachedData) {\n      return cachedData;\n    }\n    let data;\n    if (this.options.cMapUrl !== null) {\n      const url = `${this.options.cMapUrl}${name}.bcmap`;\n      const response = await fetch(url);\n      if (!response.ok) {\n        throw new Error(`fetchBuiltInCMap: failed to fetch file \"${url}\" with \"${response.statusText}\".`);\n      }\n      data = {\n        cMapData: new Uint8Array(await response.arrayBuffer()),\n        compressionType: CMapCompressionType.BINARY\n      };\n    } else {\n      data = await this.handler.sendWithPromise(\"FetchBuiltInCMap\", {\n        name\n      });\n    }\n    if (data.compressionType !== CMapCompressionType.NONE) {\n      this.builtInCMapCache.set(name, data);\n    }\n    return data;\n  }\n  async fetchStandardFontData(name) {\n    const cachedData = this.standardFontDataCache.get(name);\n    if (cachedData) {\n      return new Stream(cachedData);\n    }\n    if (this.options.useSystemFonts && name !== \"Symbol\" && name !== \"ZapfDingbats\") {\n      return null;\n    }\n    const standardFontNameToFileName = getFontNameToFileMap(),\n      filename = standardFontNameToFileName[name];\n    let data;\n    if (this.options.standardFontDataUrl !== null) {\n      const url = `${this.options.standardFontDataUrl}${filename}`;\n      const response = await fetch(url);\n      if (!response.ok) {\n        warn(`fetchStandardFontData: failed to fetch file \"${url}\" with \"${response.statusText}\".`);\n      } else {\n        data = new Uint8Array(await response.arrayBuffer());\n      }\n    } else {\n      try {\n        data = await this.handler.sendWithPromise(\"FetchStandardFontData\", {\n          filename\n        });\n      } catch (e) {\n        warn(`fetchStandardFontData: failed to fetch file \"${filename}\" with \"${e}\".`);\n      }\n    }\n    if (!data) {\n      return null;\n    }\n    this.standardFontDataCache.set(name, data);\n    return new Stream(data);\n  }\n  async buildFormXObject(resources, xobj, smask, operatorList, task, initialState, localColorSpaceCache) {\n    const dict = xobj.dict;\n    const matrix = dict.getArray(\"Matrix\");\n    let bbox = dict.getArray(\"BBox\");\n    bbox = Array.isArray(bbox) && bbox.length === 4 ? Util.normalizeRect(bbox) : null;\n    let optionalContent, groupOptions;\n    if (dict.has(\"OC\")) {\n      optionalContent = await this.parseMarkedContentProps(dict.get(\"OC\"), resources);\n    }\n    if (optionalContent !== undefined) {\n      operatorList.addOp(OPS.beginMarkedContentProps, [\"OC\", optionalContent]);\n    }\n    const group = dict.get(\"Group\");\n    if (group) {\n      groupOptions = {\n        matrix,\n        bbox,\n        smask,\n        isolated: false,\n        knockout: false\n      };\n      const groupSubtype = group.get(\"S\");\n      let colorSpace = null;\n      if (isName(groupSubtype, \"Transparency\")) {\n        groupOptions.isolated = group.get(\"I\") || false;\n        groupOptions.knockout = group.get(\"K\") || false;\n        if (group.has(\"CS\")) {\n          const cs = group.getRaw(\"CS\");\n          const cachedColorSpace = ColorSpace.getCached(cs, this.xref, localColorSpaceCache);\n          if (cachedColorSpace) {\n            colorSpace = cachedColorSpace;\n          } else {\n            colorSpace = await this.parseColorSpace({\n              cs,\n              resources,\n              localColorSpaceCache\n            });\n          }\n        }\n      }\n      if (smask?.backdrop) {\n        colorSpace ||= ColorSpace.singletons.rgb;\n        smask.backdrop = colorSpace.getRgb(smask.backdrop, 0);\n      }\n      operatorList.addOp(OPS.beginGroup, [groupOptions]);\n    }\n    const args = group ? [matrix, null] : [matrix, bbox];\n    operatorList.addOp(OPS.paintFormXObjectBegin, args);\n    return this.getOperatorList({\n      stream: xobj,\n      task,\n      resources: dict.get(\"Resources\") || resources,\n      operatorList,\n      initialState\n    }).then(function () {\n      operatorList.addOp(OPS.paintFormXObjectEnd, []);\n      if (group) {\n        operatorList.addOp(OPS.endGroup, [groupOptions]);\n      }\n      if (optionalContent !== undefined) {\n        operatorList.addOp(OPS.endMarkedContent, []);\n      }\n    });\n  }\n  _sendImgData(objId, imgData, cacheGlobally = false) {\n    const transfers = imgData ? [imgData.bitmap || imgData.data.buffer] : null;\n    if (this.parsingType3Font || cacheGlobally) {\n      return this.handler.send(\"commonobj\", [objId, \"Image\", imgData], transfers);\n    }\n    return this.handler.send(\"obj\", [objId, this.pageIndex, \"Image\", imgData], transfers);\n  }\n  async buildPaintImageXObject({\n    resources,\n    image,\n    isInline = false,\n    operatorList,\n    cacheKey,\n    localImageCache,\n    localColorSpaceCache\n  }) {\n    const dict = image.dict;\n    const imageRef = dict.objId;\n    const w = dict.get(\"W\", \"Width\");\n    const h = dict.get(\"H\", \"Height\");\n    if (!(w && typeof w === \"number\") || !(h && typeof h === \"number\")) {\n      warn(\"Image dimensions are missing, or not numbers.\");\n      return;\n    }\n    const maxImageSize = this.options.maxImageSize;\n    if (maxImageSize !== -1 && w * h > maxImageSize) {\n      const msg = \"Image exceeded maximum allowed size and was removed.\";\n      if (this.options.ignoreErrors) {\n        warn(msg);\n        return;\n      }\n      throw new Error(msg);\n    }\n    let optionalContent;\n    if (dict.has(\"OC\")) {\n      optionalContent = await this.parseMarkedContentProps(dict.get(\"OC\"), resources);\n    }\n    const imageMask = dict.get(\"IM\", \"ImageMask\") || false;\n    let imgData, args;\n    if (imageMask) {\n      const interpolate = dict.get(\"I\", \"Interpolate\");\n      const bitStrideLength = w + 7 >> 3;\n      const imgArray = image.getBytes(bitStrideLength * h);\n      const decode = dict.getArray(\"D\", \"Decode\");\n      if (this.parsingType3Font) {\n        imgData = PDFImage.createRawMask({\n          imgArray,\n          width: w,\n          height: h,\n          imageIsFromDecodeStream: image instanceof DecodeStream,\n          inverseDecode: decode?.[0] > 0,\n          interpolate\n        });\n        imgData.cached = !!cacheKey;\n        args = [imgData];\n        operatorList.addImageOps(OPS.paintImageMaskXObject, args, optionalContent);\n        if (cacheKey) {\n          const cacheData = {\n            fn: OPS.paintImageMaskXObject,\n            args,\n            optionalContent\n          };\n          localImageCache.set(cacheKey, imageRef, cacheData);\n          if (imageRef) {\n            this._regionalImageCache.set(null, imageRef, cacheData);\n          }\n        }\n        return;\n      }\n      imgData = await PDFImage.createMask({\n        imgArray,\n        width: w,\n        height: h,\n        imageIsFromDecodeStream: image instanceof DecodeStream,\n        inverseDecode: decode?.[0] > 0,\n        interpolate,\n        isOffscreenCanvasSupported: this.options.isOffscreenCanvasSupported\n      });\n      if (imgData.isSingleOpaquePixel) {\n        operatorList.addImageOps(OPS.paintSolidColorImageMask, [], optionalContent);\n        if (cacheKey) {\n          const cacheData = {\n            fn: OPS.paintSolidColorImageMask,\n            args: [],\n            optionalContent\n          };\n          localImageCache.set(cacheKey, imageRef, cacheData);\n          if (imageRef) {\n            this._regionalImageCache.set(null, imageRef, cacheData);\n          }\n        }\n        return;\n      }\n      const objId = `mask_${this.idFactory.createObjId()}`;\n      operatorList.addDependency(objId);\n      this._sendImgData(objId, imgData);\n      args = [{\n        data: objId,\n        width: imgData.width,\n        height: imgData.height,\n        interpolate: imgData.interpolate,\n        count: 1\n      }];\n      operatorList.addImageOps(OPS.paintImageMaskXObject, args, optionalContent);\n      if (cacheKey) {\n        const cacheData = {\n          fn: OPS.paintImageMaskXObject,\n          args,\n          optionalContent\n        };\n        localImageCache.set(cacheKey, imageRef, cacheData);\n        if (imageRef) {\n          this._regionalImageCache.set(null, imageRef, cacheData);\n        }\n      }\n      return;\n    }\n    const SMALL_IMAGE_DIMENSIONS = 200;\n    if (isInline && !dict.has(\"SMask\") && !dict.has(\"Mask\") && w + h < SMALL_IMAGE_DIMENSIONS) {\n      const imageObj = new PDFImage({\n        xref: this.xref,\n        res: resources,\n        image,\n        isInline,\n        pdfFunctionFactory: this._pdfFunctionFactory,\n        localColorSpaceCache\n      });\n      imgData = await imageObj.createImageData(true, false);\n      operatorList.isOffscreenCanvasSupported = this.options.isOffscreenCanvasSupported;\n      operatorList.addImageOps(OPS.paintInlineImageXObject, [imgData], optionalContent);\n      return;\n    }\n    let objId = `img_${this.idFactory.createObjId()}`,\n      cacheGlobally = false;\n    if (this.parsingType3Font) {\n      objId = `${this.idFactory.getDocId()}_type3_${objId}`;\n    } else if (imageRef) {\n      cacheGlobally = this.globalImageCache.shouldCache(imageRef, this.pageIndex);\n      if (cacheGlobally) {\n        objId = `${this.idFactory.getDocId()}_${objId}`;\n      }\n    }\n    operatorList.addDependency(objId);\n    args = [objId, w, h];\n    PDFImage.buildImage({\n      xref: this.xref,\n      res: resources,\n      image,\n      isInline,\n      pdfFunctionFactory: this._pdfFunctionFactory,\n      localColorSpaceCache\n    }).then(async imageObj => {\n      imgData = await imageObj.createImageData(false, this.options.isOffscreenCanvasSupported);\n      if (cacheKey && imageRef && cacheGlobally) {\n        const length = imgData.bitmap ? imgData.width * imgData.height * 4 : imgData.data.length;\n        this.globalImageCache.addByteSize(imageRef, length);\n      }\n      return this._sendImgData(objId, imgData, cacheGlobally);\n    }).catch(reason => {\n      warn(`Unable to decode image \"${objId}\": \"${reason}\".`);\n      return this._sendImgData(objId, null, cacheGlobally);\n    });\n    operatorList.addImageOps(OPS.paintImageXObject, args, optionalContent);\n    if (cacheKey) {\n      const cacheData = {\n        fn: OPS.paintImageXObject,\n        args,\n        optionalContent\n      };\n      localImageCache.set(cacheKey, imageRef, cacheData);\n      if (imageRef) {\n        this._regionalImageCache.set(null, imageRef, cacheData);\n        if (cacheGlobally) {\n          assert(!isInline, \"Cannot cache an inline image globally.\");\n          this.globalImageCache.setData(imageRef, {\n            objId,\n            fn: OPS.paintImageXObject,\n            args,\n            optionalContent,\n            byteSize: 0\n          });\n        }\n      }\n    }\n  }\n  handleSMask(smask, resources, operatorList, task, stateManager, localColorSpaceCache) {\n    const smaskContent = smask.get(\"G\");\n    const smaskOptions = {\n      subtype: smask.get(\"S\").name,\n      backdrop: smask.get(\"BC\")\n    };\n    const transferObj = smask.get(\"TR\");\n    if (isPDFFunction(transferObj)) {\n      const transferFn = this._pdfFunctionFactory.create(transferObj);\n      const transferMap = new Uint8Array(256);\n      const tmp = new Float32Array(1);\n      for (let i = 0; i < 256; i++) {\n        tmp[0] = i / 255;\n        transferFn(tmp, 0, tmp, 0);\n        transferMap[i] = tmp[0] * 255 | 0;\n      }\n      smaskOptions.transferMap = transferMap;\n    }\n    return this.buildFormXObject(resources, smaskContent, smaskOptions, operatorList, task, stateManager.state.clone(), localColorSpaceCache);\n  }\n  handleTransferFunction(tr) {\n    let transferArray;\n    if (Array.isArray(tr)) {\n      transferArray = tr;\n    } else if (isPDFFunction(tr)) {\n      transferArray = [tr];\n    } else {\n      return null;\n    }\n    const transferMaps = [];\n    let numFns = 0,\n      numEffectfulFns = 0;\n    for (const entry of transferArray) {\n      const transferObj = this.xref.fetchIfRef(entry);\n      numFns++;\n      if (isName(transferObj, \"Identity\")) {\n        transferMaps.push(null);\n        continue;\n      } else if (!isPDFFunction(transferObj)) {\n        return null;\n      }\n      const transferFn = this._pdfFunctionFactory.create(transferObj);\n      const transferMap = new Uint8Array(256),\n        tmp = new Float32Array(1);\n      for (let j = 0; j < 256; j++) {\n        tmp[0] = j / 255;\n        transferFn(tmp, 0, tmp, 0);\n        transferMap[j] = tmp[0] * 255 | 0;\n      }\n      transferMaps.push(transferMap);\n      numEffectfulFns++;\n    }\n    if (!(numFns === 1 || numFns === 4)) {\n      return null;\n    }\n    if (numEffectfulFns === 0) {\n      return null;\n    }\n    return transferMaps;\n  }\n  handleTilingType(fn, color, resources, pattern, patternDict, operatorList, task, localTilingPatternCache) {\n    const tilingOpList = new OperatorList();\n    const patternResources = Dict.merge({\n      xref: this.xref,\n      dictArray: [patternDict.get(\"Resources\"), resources]\n    });\n    return this.getOperatorList({\n      stream: pattern,\n      task,\n      resources: patternResources,\n      operatorList: tilingOpList\n    }).then(function () {\n      const operatorListIR = tilingOpList.getIR();\n      const tilingPatternIR = getTilingPatternIR(operatorListIR, patternDict, color);\n      operatorList.addDependencies(tilingOpList.dependencies);\n      operatorList.addOp(fn, tilingPatternIR);\n      if (patternDict.objId) {\n        localTilingPatternCache.set(null, patternDict.objId, {\n          operatorListIR,\n          dict: patternDict\n        });\n      }\n    }).catch(reason => {\n      if (reason instanceof AbortException) {\n        return;\n      }\n      if (this.options.ignoreErrors) {\n        warn(`handleTilingType - ignoring pattern: \"${reason}\".`);\n        return;\n      }\n      throw reason;\n    });\n  }\n  handleSetFont(resources, fontArgs, fontRef, operatorList, task, state, fallbackFontDict = null, cssFontInfo = null) {\n    const fontName = fontArgs?.[0] instanceof Name ? fontArgs[0].name : null;\n    return this.loadFont(fontName, fontRef, resources, fallbackFontDict, cssFontInfo).then(translated => {\n      if (!translated.font.isType3Font) {\n        return translated;\n      }\n      return translated.loadType3Data(this, resources, task).then(function () {\n        operatorList.addDependencies(translated.type3Dependencies);\n        return translated;\n      }).catch(reason => {\n        return new TranslatedFont({\n          loadedName: \"g_font_error\",\n          font: new ErrorFont(`Type3 font load error: ${reason}`),\n          dict: translated.font,\n          evaluatorOptions: this.options\n        });\n      });\n    }).then(translated => {\n      state.font = translated.font;\n      translated.send(this.handler);\n      return translated.loadedName;\n    });\n  }\n  handleText(chars, state) {\n    const font = state.font;\n    const glyphs = font.charsToGlyphs(chars);\n    if (font.data) {\n      const isAddToPathSet = !!(state.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG);\n      if (isAddToPathSet || state.fillColorSpace.name === \"Pattern\" || font.disableFontFace || this.options.disableFontFace) {\n        PartialEvaluator.buildFontPaths(font, glyphs, this.handler, this.options);\n      }\n    }\n    return glyphs;\n  }\n  ensureStateFont(state) {\n    if (state.font) {\n      return;\n    }\n    const reason = new FormatError(\"Missing setFont (Tf) operator before text rendering operator.\");\n    if (this.options.ignoreErrors) {\n      warn(`ensureStateFont: \"${reason}\".`);\n      return;\n    }\n    throw reason;\n  }\n  async setGState({\n    resources,\n    gState,\n    operatorList,\n    cacheKey,\n    task,\n    stateManager,\n    localGStateCache,\n    localColorSpaceCache\n  }) {\n    const gStateRef = gState.objId;\n    let isSimpleGState = true;\n    const gStateObj = [];\n    let promise = Promise.resolve();\n    for (const key of gState.getKeys()) {\n      const value = gState.get(key);\n      switch (key) {\n        case \"Type\":\n          break;\n        case \"LW\":\n        case \"LC\":\n        case \"LJ\":\n        case \"ML\":\n        case \"D\":\n        case \"RI\":\n        case \"FL\":\n        case \"CA\":\n        case \"ca\":\n          gStateObj.push([key, value]);\n          break;\n        case \"Font\":\n          isSimpleGState = false;\n          promise = promise.then(() => {\n            return this.handleSetFont(resources, null, value[0], operatorList, task, stateManager.state).then(function (loadedName) {\n              operatorList.addDependency(loadedName);\n              gStateObj.push([key, [loadedName, value[1]]]);\n            });\n          });\n          break;\n        case \"BM\":\n          gStateObj.push([key, normalizeBlendMode(value)]);\n          break;\n        case \"SMask\":\n          if (isName(value, \"None\")) {\n            gStateObj.push([key, false]);\n            break;\n          }\n          if (value instanceof Dict) {\n            isSimpleGState = false;\n            promise = promise.then(() => {\n              return this.handleSMask(value, resources, operatorList, task, stateManager, localColorSpaceCache);\n            });\n            gStateObj.push([key, true]);\n          } else {\n            warn(\"Unsupported SMask type\");\n          }\n          break;\n        case \"TR\":\n          const transferMaps = this.handleTransferFunction(value);\n          gStateObj.push([key, transferMaps]);\n          break;\n        case \"OP\":\n        case \"op\":\n        case \"OPM\":\n        case \"BG\":\n        case \"BG2\":\n        case \"UCR\":\n        case \"UCR2\":\n        case \"TR2\":\n        case \"HT\":\n        case \"SM\":\n        case \"SA\":\n        case \"AIS\":\n        case \"TK\":\n          info(\"graphic state operator \" + key);\n          break;\n        default:\n          info(\"Unknown graphic state operator \" + key);\n          break;\n      }\n    }\n    return promise.then(function () {\n      if (gStateObj.length > 0) {\n        operatorList.addOp(OPS.setGState, [gStateObj]);\n      }\n      if (isSimpleGState) {\n        localGStateCache.set(cacheKey, gStateRef, gStateObj);\n      }\n    });\n  }\n  loadFont(fontName, font, resources, fallbackFontDict = null, cssFontInfo = null) {\n    const errorFont = async () => {\n      return new TranslatedFont({\n        loadedName: \"g_font_error\",\n        font: new ErrorFont(`Font \"${fontName}\" is not available.`),\n        dict: font,\n        evaluatorOptions: this.options\n      });\n    };\n    let fontRef;\n    if (font) {\n      if (font instanceof Ref) {\n        fontRef = font;\n      }\n    } else {\n      const fontRes = resources.get(\"Font\");\n      if (fontRes) {\n        fontRef = fontRes.getRaw(fontName);\n      }\n    }\n    if (fontRef) {\n      if (this.parsingType3Font && this.type3FontRefs.has(fontRef)) {\n        return errorFont();\n      }\n      if (this.fontCache.has(fontRef)) {\n        return this.fontCache.get(fontRef);\n      }\n      font = this.xref.fetchIfRef(fontRef);\n    }\n    if (!(font instanceof Dict)) {\n      if (!this.options.ignoreErrors && !this.parsingType3Font) {\n        warn(`Font \"${fontName}\" is not available.`);\n        return errorFont();\n      }\n      warn(`Font \"${fontName}\" is not available -- attempting to fallback to a default font.`);\n      font = fallbackFontDict || PartialEvaluator.fallbackFontDict;\n    }\n    if (font.cacheKey && this.fontCache.has(font.cacheKey)) {\n      return this.fontCache.get(font.cacheKey);\n    }\n    const fontCapability = new PromiseCapability();\n    let preEvaluatedFont;\n    try {\n      preEvaluatedFont = this.preEvaluateFont(font);\n      preEvaluatedFont.cssFontInfo = cssFontInfo;\n    } catch (reason) {\n      warn(`loadFont - preEvaluateFont failed: \"${reason}\".`);\n      return errorFont();\n    }\n    const {\n      descriptor,\n      hash\n    } = preEvaluatedFont;\n    const fontRefIsRef = fontRef instanceof Ref;\n    let fontID;\n    if (hash && descriptor instanceof Dict) {\n      const fontAliases = descriptor.fontAliases ||= Object.create(null);\n      if (fontAliases[hash]) {\n        const aliasFontRef = fontAliases[hash].aliasRef;\n        if (fontRefIsRef && aliasFontRef && this.fontCache.has(aliasFontRef)) {\n          this.fontCache.putAlias(fontRef, aliasFontRef);\n          return this.fontCache.get(fontRef);\n        }\n      } else {\n        fontAliases[hash] = {\n          fontID: this.idFactory.createFontId()\n        };\n      }\n      if (fontRefIsRef) {\n        fontAliases[hash].aliasRef = fontRef;\n      }\n      fontID = fontAliases[hash].fontID;\n    } else {\n      fontID = this.idFactory.createFontId();\n    }\n    assert(fontID?.startsWith(\"f\"), 'The \"fontID\" must be (correctly) defined.');\n    if (fontRefIsRef) {\n      this.fontCache.put(fontRef, fontCapability.promise);\n    } else {\n      font.cacheKey = `cacheKey_${fontID}`;\n      this.fontCache.put(font.cacheKey, fontCapability.promise);\n    }\n    font.loadedName = `${this.idFactory.getDocId()}_${fontID}`;\n    this.translateFont(preEvaluatedFont).then(translatedFont => {\n      fontCapability.resolve(new TranslatedFont({\n        loadedName: font.loadedName,\n        font: translatedFont,\n        dict: font,\n        evaluatorOptions: this.options\n      }));\n    }).catch(reason => {\n      warn(`loadFont - translateFont failed: \"${reason}\".`);\n      fontCapability.resolve(new TranslatedFont({\n        loadedName: font.loadedName,\n        font: new ErrorFont(reason instanceof Error ? reason.message : reason),\n        dict: font,\n        evaluatorOptions: this.options\n      }));\n    });\n    return fontCapability.promise;\n  }\n  buildPath(operatorList, fn, args, parsingText = false) {\n    const lastIndex = operatorList.length - 1;\n    if (!args) {\n      args = [];\n    }\n    if (lastIndex < 0 || operatorList.fnArray[lastIndex] !== OPS.constructPath) {\n      if (parsingText) {\n        warn(`Encountered path operator \"${fn}\" inside of a text object.`);\n        operatorList.addOp(OPS.save, null);\n      }\n      let minMax;\n      switch (fn) {\n        case OPS.rectangle:\n          const x = args[0] + args[2];\n          const y = args[1] + args[3];\n          minMax = [Math.min(args[0], x), Math.max(args[0], x), Math.min(args[1], y), Math.max(args[1], y)];\n          break;\n        case OPS.moveTo:\n        case OPS.lineTo:\n          minMax = [args[0], args[0], args[1], args[1]];\n          break;\n        default:\n          minMax = [Infinity, -Infinity, Infinity, -Infinity];\n          break;\n      }\n      operatorList.addOp(OPS.constructPath, [[fn], args, minMax]);\n      if (parsingText) {\n        operatorList.addOp(OPS.restore, null);\n      }\n    } else {\n      const opArgs = operatorList.argsArray[lastIndex];\n      opArgs[0].push(fn);\n      opArgs[1].push(...args);\n      const minMax = opArgs[2];\n      switch (fn) {\n        case OPS.rectangle:\n          const x = args[0] + args[2];\n          const y = args[1] + args[3];\n          minMax[0] = Math.min(minMax[0], args[0], x);\n          minMax[1] = Math.max(minMax[1], args[0], x);\n          minMax[2] = Math.min(minMax[2], args[1], y);\n          minMax[3] = Math.max(minMax[3], args[1], y);\n          break;\n        case OPS.moveTo:\n        case OPS.lineTo:\n          minMax[0] = Math.min(minMax[0], args[0]);\n          minMax[1] = Math.max(minMax[1], args[0]);\n          minMax[2] = Math.min(minMax[2], args[1]);\n          minMax[3] = Math.max(minMax[3], args[1]);\n          break;\n      }\n    }\n  }\n  parseColorSpace({\n    cs,\n    resources,\n    localColorSpaceCache\n  }) {\n    return ColorSpace.parseAsync({\n      cs,\n      xref: this.xref,\n      resources,\n      pdfFunctionFactory: this._pdfFunctionFactory,\n      localColorSpaceCache\n    }).catch(reason => {\n      if (reason instanceof AbortException) {\n        return null;\n      }\n      if (this.options.ignoreErrors) {\n        warn(`parseColorSpace - ignoring ColorSpace: \"${reason}\".`);\n        return null;\n      }\n      throw reason;\n    });\n  }\n  parseShading({\n    shading,\n    resources,\n    localColorSpaceCache,\n    localShadingPatternCache\n  }) {\n    let id = localShadingPatternCache.get(shading);\n    if (!id) {\n      var shadingFill = Pattern.parseShading(shading, this.xref, resources, this._pdfFunctionFactory, localColorSpaceCache);\n      const patternIR = shadingFill.getIR();\n      id = `pattern_${this.idFactory.createObjId()}`;\n      if (this.parsingType3Font) {\n        id = `${this.idFactory.getDocId()}_type3_${id}`;\n      }\n      localShadingPatternCache.set(shading, id);\n      if (this.parsingType3Font) {\n        this.handler.send(\"commonobj\", [id, \"Pattern\", patternIR]);\n      } else {\n        this.handler.send(\"obj\", [id, this.pageIndex, \"Pattern\", patternIR]);\n      }\n    }\n    return id;\n  }\n  handleColorN(operatorList, fn, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache, localShadingPatternCache) {\n    const patternName = args.pop();\n    if (patternName instanceof Name) {\n      const rawPattern = patterns.getRaw(patternName.name);\n      const localTilingPattern = rawPattern instanceof Ref && localTilingPatternCache.getByRef(rawPattern);\n      if (localTilingPattern) {\n        try {\n          const color = cs.base ? cs.base.getRgb(args, 0) : null;\n          const tilingPatternIR = getTilingPatternIR(localTilingPattern.operatorListIR, localTilingPattern.dict, color);\n          operatorList.addOp(fn, tilingPatternIR);\n          return undefined;\n        } catch {}\n      }\n      const pattern = this.xref.fetchIfRef(rawPattern);\n      if (pattern) {\n        const dict = pattern instanceof BaseStream ? pattern.dict : pattern;\n        const typeNum = dict.get(\"PatternType\");\n        if (typeNum === PatternType.TILING) {\n          const color = cs.base ? cs.base.getRgb(args, 0) : null;\n          return this.handleTilingType(fn, color, resources, pattern, dict, operatorList, task, localTilingPatternCache);\n        } else if (typeNum === PatternType.SHADING) {\n          const shading = dict.get(\"Shading\");\n          const matrix = dict.getArray(\"Matrix\");\n          const objId = this.parseShading({\n            shading,\n            resources,\n            localColorSpaceCache,\n            localShadingPatternCache\n          });\n          operatorList.addOp(fn, [\"Shading\", objId, matrix]);\n          return undefined;\n        }\n        throw new FormatError(`Unknown PatternType: ${typeNum}`);\n      }\n    }\n    throw new FormatError(`Unknown PatternName: ${patternName}`);\n  }\n  _parseVisibilityExpression(array, nestingCounter, currentResult) {\n    const MAX_NESTING = 10;\n    if (++nestingCounter > MAX_NESTING) {\n      warn(\"Visibility expression is too deeply nested\");\n      return;\n    }\n    const length = array.length;\n    const operator = this.xref.fetchIfRef(array[0]);\n    if (length < 2 || !(operator instanceof Name)) {\n      warn(\"Invalid visibility expression\");\n      return;\n    }\n    switch (operator.name) {\n      case \"And\":\n      case \"Or\":\n      case \"Not\":\n        currentResult.push(operator.name);\n        break;\n      default:\n        warn(`Invalid operator ${operator.name} in visibility expression`);\n        return;\n    }\n    for (let i = 1; i < length; i++) {\n      const raw = array[i];\n      const object = this.xref.fetchIfRef(raw);\n      if (Array.isArray(object)) {\n        const nestedResult = [];\n        currentResult.push(nestedResult);\n        this._parseVisibilityExpression(object, nestingCounter, nestedResult);\n      } else if (raw instanceof Ref) {\n        currentResult.push(raw.toString());\n      }\n    }\n  }\n  async parseMarkedContentProps(contentProperties, resources) {\n    let optionalContent;\n    if (contentProperties instanceof Name) {\n      const properties = resources.get(\"Properties\");\n      optionalContent = properties.get(contentProperties.name);\n    } else if (contentProperties instanceof Dict) {\n      optionalContent = contentProperties;\n    } else {\n      throw new FormatError(\"Optional content properties malformed.\");\n    }\n    const optionalContentType = optionalContent.get(\"Type\")?.name;\n    if (optionalContentType === \"OCG\") {\n      return {\n        type: optionalContentType,\n        id: optionalContent.objId\n      };\n    } else if (optionalContentType === \"OCMD\") {\n      const expression = optionalContent.get(\"VE\");\n      if (Array.isArray(expression)) {\n        const result = [];\n        this._parseVisibilityExpression(expression, 0, result);\n        if (result.length > 0) {\n          return {\n            type: \"OCMD\",\n            expression: result\n          };\n        }\n      }\n      const optionalContentGroups = optionalContent.get(\"OCGs\");\n      if (Array.isArray(optionalContentGroups) || optionalContentGroups instanceof Dict) {\n        const groupIds = [];\n        if (Array.isArray(optionalContentGroups)) {\n          for (const ocg of optionalContentGroups) {\n            groupIds.push(ocg.toString());\n          }\n        } else {\n          groupIds.push(optionalContentGroups.objId);\n        }\n        return {\n          type: optionalContentType,\n          ids: groupIds,\n          policy: optionalContent.get(\"P\") instanceof Name ? optionalContent.get(\"P\").name : null,\n          expression: null\n        };\n      } else if (optionalContentGroups instanceof Ref) {\n        return {\n          type: optionalContentType,\n          id: optionalContentGroups.toString()\n        };\n      }\n    }\n    return null;\n  }\n  getOperatorList({\n    stream,\n    task,\n    resources,\n    operatorList,\n    initialState = null,\n    fallbackFontDict = null\n  }) {\n    resources ||= Dict.empty;\n    initialState ||= new EvalState();\n    if (!operatorList) {\n      throw new Error('getOperatorList: missing \"operatorList\" parameter');\n    }\n    const self = this;\n    const xref = this.xref;\n    let parsingText = false;\n    const localImageCache = new LocalImageCache();\n    const localColorSpaceCache = new LocalColorSpaceCache();\n    const localGStateCache = new LocalGStateCache();\n    const localTilingPatternCache = new LocalTilingPatternCache();\n    const localShadingPatternCache = new Map();\n    const xobjs = resources.get(\"XObject\") || Dict.empty;\n    const patterns = resources.get(\"Pattern\") || Dict.empty;\n    const stateManager = new StateManager(initialState);\n    const preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);\n    const timeSlotManager = new TimeSlotManager();\n    function closePendingRestoreOPS(argument) {\n      for (let i = 0, ii = preprocessor.savedStatesDepth; i < ii; i++) {\n        operatorList.addOp(OPS.restore, []);\n      }\n    }\n    return new Promise(function promiseBody(resolve, reject) {\n      const next = function (promise) {\n        Promise.all([promise, operatorList.ready]).then(function () {\n          try {\n            promiseBody(resolve, reject);\n          } catch (ex) {\n            reject(ex);\n          }\n        }, reject);\n      };\n      task.ensureNotTerminated();\n      timeSlotManager.reset();\n      const operation = {};\n      let stop, i, ii, cs, name, isValidName;\n      while (!(stop = timeSlotManager.check())) {\n        operation.args = null;\n        if (!preprocessor.read(operation)) {\n          break;\n        }\n        let args = operation.args;\n        let fn = operation.fn;\n        switch (fn | 0) {\n          case OPS.paintXObject:\n            isValidName = args[0] instanceof Name;\n            name = args[0].name;\n            if (isValidName) {\n              const localImage = localImageCache.getByName(name);\n              if (localImage) {\n                operatorList.addImageOps(localImage.fn, localImage.args, localImage.optionalContent);\n                incrementCachedImageMaskCount(localImage);\n                args = null;\n                continue;\n              }\n            }\n            next(new Promise(function (resolveXObject, rejectXObject) {\n              if (!isValidName) {\n                throw new FormatError(\"XObject must be referred to by name.\");\n              }\n              let xobj = xobjs.getRaw(name);\n              if (xobj instanceof Ref) {\n                const localImage = localImageCache.getByRef(xobj) || self._regionalImageCache.getByRef(xobj);\n                if (localImage) {\n                  operatorList.addImageOps(localImage.fn, localImage.args, localImage.optionalContent);\n                  incrementCachedImageMaskCount(localImage);\n                  resolveXObject();\n                  return;\n                }\n                const globalImage = self.globalImageCache.getData(xobj, self.pageIndex);\n                if (globalImage) {\n                  operatorList.addDependency(globalImage.objId);\n                  operatorList.addImageOps(globalImage.fn, globalImage.args, globalImage.optionalContent);\n                  resolveXObject();\n                  return;\n                }\n                xobj = xref.fetch(xobj);\n              }\n              if (!(xobj instanceof BaseStream)) {\n                throw new FormatError(\"XObject should be a stream\");\n              }\n              const type = xobj.dict.get(\"Subtype\");\n              if (!(type instanceof Name)) {\n                throw new FormatError(\"XObject should have a Name subtype\");\n              }\n              if (type.name === \"Form\") {\n                stateManager.save();\n                self.buildFormXObject(resources, xobj, null, operatorList, task, stateManager.state.clone(), localColorSpaceCache).then(function () {\n                  stateManager.restore();\n                  resolveXObject();\n                }, rejectXObject);\n                return;\n              } else if (type.name === \"Image\") {\n                self.buildPaintImageXObject({\n                  resources,\n                  image: xobj,\n                  operatorList,\n                  cacheKey: name,\n                  localImageCache,\n                  localColorSpaceCache\n                }).then(resolveXObject, rejectXObject);\n                return;\n              } else if (type.name === \"PS\") {\n                info(\"Ignored XObject subtype PS\");\n              } else {\n                throw new FormatError(`Unhandled XObject subtype ${type.name}`);\n              }\n              resolveXObject();\n            }).catch(function (reason) {\n              if (reason instanceof AbortException) {\n                return;\n              }\n              if (self.options.ignoreErrors) {\n                warn(`getOperatorList - ignoring XObject: \"${reason}\".`);\n                return;\n              }\n              throw reason;\n            }));\n            return;\n          case OPS.setFont:\n            var fontSize = args[1];\n            next(self.handleSetFont(resources, args, null, operatorList, task, stateManager.state, fallbackFontDict).then(function (loadedName) {\n              operatorList.addDependency(loadedName);\n              operatorList.addOp(OPS.setFont, [loadedName, fontSize]);\n            }));\n            return;\n          case OPS.beginText:\n            parsingText = true;\n            break;\n          case OPS.endText:\n            parsingText = false;\n            break;\n          case OPS.endInlineImage:\n            var cacheKey = args[0].cacheKey;\n            if (cacheKey) {\n              const localImage = localImageCache.getByName(cacheKey);\n              if (localImage) {\n                operatorList.addImageOps(localImage.fn, localImage.args, localImage.optionalContent);\n                incrementCachedImageMaskCount(localImage);\n                args = null;\n                continue;\n              }\n            }\n            next(self.buildPaintImageXObject({\n              resources,\n              image: args[0],\n              isInline: true,\n              operatorList,\n              cacheKey,\n              localImageCache,\n              localColorSpaceCache\n            }));\n            return;\n          case OPS.showText:\n            if (!stateManager.state.font) {\n              self.ensureStateFont(stateManager.state);\n              continue;\n            }\n            args[0] = self.handleText(args[0], stateManager.state);\n            break;\n          case OPS.showSpacedText:\n            if (!stateManager.state.font) {\n              self.ensureStateFont(stateManager.state);\n              continue;\n            }\n            var combinedGlyphs = [];\n            var state = stateManager.state;\n            for (const arrItem of args[0]) {\n              if (typeof arrItem === \"string\") {\n                combinedGlyphs.push(...self.handleText(arrItem, state));\n              } else if (typeof arrItem === \"number\") {\n                combinedGlyphs.push(arrItem);\n              }\n            }\n            args[0] = combinedGlyphs;\n            fn = OPS.showText;\n            break;\n          case OPS.nextLineShowText:\n            if (!stateManager.state.font) {\n              self.ensureStateFont(stateManager.state);\n              continue;\n            }\n            operatorList.addOp(OPS.nextLine);\n            args[0] = self.handleText(args[0], stateManager.state);\n            fn = OPS.showText;\n            break;\n          case OPS.nextLineSetSpacingShowText:\n            if (!stateManager.state.font) {\n              self.ensureStateFont(stateManager.state);\n              continue;\n            }\n            operatorList.addOp(OPS.nextLine);\n            operatorList.addOp(OPS.setWordSpacing, [args.shift()]);\n            operatorList.addOp(OPS.setCharSpacing, [args.shift()]);\n            args[0] = self.handleText(args[0], stateManager.state);\n            fn = OPS.showText;\n            break;\n          case OPS.setTextRenderingMode:\n            stateManager.state.textRenderingMode = args[0];\n            break;\n          case OPS.setFillColorSpace:\n            {\n              const cachedColorSpace = ColorSpace.getCached(args[0], xref, localColorSpaceCache);\n              if (cachedColorSpace) {\n                stateManager.state.fillColorSpace = cachedColorSpace;\n                continue;\n              }\n              next(self.parseColorSpace({\n                cs: args[0],\n                resources,\n                localColorSpaceCache\n              }).then(function (colorSpace) {\n                if (colorSpace) {\n                  stateManager.state.fillColorSpace = colorSpace;\n                }\n              }));\n              return;\n            }\n          case OPS.setStrokeColorSpace:\n            {\n              const cachedColorSpace = ColorSpace.getCached(args[0], xref, localColorSpaceCache);\n              if (cachedColorSpace) {\n                stateManager.state.strokeColorSpace = cachedColorSpace;\n                continue;\n              }\n              next(self.parseColorSpace({\n                cs: args[0],\n                resources,\n                localColorSpaceCache\n              }).then(function (colorSpace) {\n                if (colorSpace) {\n                  stateManager.state.strokeColorSpace = colorSpace;\n                }\n              }));\n              return;\n            }\n          case OPS.setFillColor:\n            cs = stateManager.state.fillColorSpace;\n            args = cs.getRgb(args, 0);\n            fn = OPS.setFillRGBColor;\n            break;\n          case OPS.setStrokeColor:\n            cs = stateManager.state.strokeColorSpace;\n            args = cs.getRgb(args, 0);\n            fn = OPS.setStrokeRGBColor;\n            break;\n          case OPS.setFillGray:\n            stateManager.state.fillColorSpace = ColorSpace.singletons.gray;\n            args = ColorSpace.singletons.gray.getRgb(args, 0);\n            fn = OPS.setFillRGBColor;\n            break;\n          case OPS.setStrokeGray:\n            stateManager.state.strokeColorSpace = ColorSpace.singletons.gray;\n            args = ColorSpace.singletons.gray.getRgb(args, 0);\n            fn = OPS.setStrokeRGBColor;\n            break;\n          case OPS.setFillCMYKColor:\n            stateManager.state.fillColorSpace = ColorSpace.singletons.cmyk;\n            args = ColorSpace.singletons.cmyk.getRgb(args, 0);\n            fn = OPS.setFillRGBColor;\n            break;\n          case OPS.setStrokeCMYKColor:\n            stateManager.state.strokeColorSpace = ColorSpace.singletons.cmyk;\n            args = ColorSpace.singletons.cmyk.getRgb(args, 0);\n            fn = OPS.setStrokeRGBColor;\n            break;\n          case OPS.setFillRGBColor:\n            stateManager.state.fillColorSpace = ColorSpace.singletons.rgb;\n            args = ColorSpace.singletons.rgb.getRgb(args, 0);\n            break;\n          case OPS.setStrokeRGBColor:\n            stateManager.state.strokeColorSpace = ColorSpace.singletons.rgb;\n            args = ColorSpace.singletons.rgb.getRgb(args, 0);\n            break;\n          case OPS.setFillColorN:\n            cs = stateManager.state.fillColorSpace;\n            if (cs.name === \"Pattern\") {\n              next(self.handleColorN(operatorList, OPS.setFillColorN, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache, localShadingPatternCache));\n              return;\n            }\n            args = cs.getRgb(args, 0);\n            fn = OPS.setFillRGBColor;\n            break;\n          case OPS.setStrokeColorN:\n            cs = stateManager.state.strokeColorSpace;\n            if (cs.name === \"Pattern\") {\n              next(self.handleColorN(operatorList, OPS.setStrokeColorN, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache, localShadingPatternCache));\n              return;\n            }\n            args = cs.getRgb(args, 0);\n            fn = OPS.setStrokeRGBColor;\n            break;\n          case OPS.shadingFill:\n            var shadingRes = resources.get(\"Shading\");\n            if (!shadingRes) {\n              throw new FormatError(\"No shading resource found\");\n            }\n            var shading = shadingRes.get(args[0].name);\n            if (!shading) {\n              throw new FormatError(\"No shading object found\");\n            }\n            const patternId = self.parseShading({\n              shading,\n              resources,\n              localColorSpaceCache,\n              localShadingPatternCache\n            });\n            args = [patternId];\n            fn = OPS.shadingFill;\n            break;\n          case OPS.setGState:\n            isValidName = args[0] instanceof Name;\n            name = args[0].name;\n            if (isValidName) {\n              const localGStateObj = localGStateCache.getByName(name);\n              if (localGStateObj) {\n                if (localGStateObj.length > 0) {\n                  operatorList.addOp(OPS.setGState, [localGStateObj]);\n                }\n                args = null;\n                continue;\n              }\n            }\n            next(new Promise(function (resolveGState, rejectGState) {\n              if (!isValidName) {\n                throw new FormatError(\"GState must be referred to by name.\");\n              }\n              const extGState = resources.get(\"ExtGState\");\n              if (!(extGState instanceof Dict)) {\n                throw new FormatError(\"ExtGState should be a dictionary.\");\n              }\n              const gState = extGState.get(name);\n              if (!(gState instanceof Dict)) {\n                throw new FormatError(\"GState should be a dictionary.\");\n              }\n              self.setGState({\n                resources,\n                gState,\n                operatorList,\n                cacheKey: name,\n                task,\n                stateManager,\n                localGStateCache,\n                localColorSpaceCache\n              }).then(resolveGState, rejectGState);\n            }).catch(function (reason) {\n              if (reason instanceof AbortException) {\n                return;\n              }\n              if (self.options.ignoreErrors) {\n                warn(`getOperatorList - ignoring ExtGState: \"${reason}\".`);\n                return;\n              }\n              throw reason;\n            }));\n            return;\n          case OPS.moveTo:\n          case OPS.lineTo:\n          case OPS.curveTo:\n          case OPS.curveTo2:\n          case OPS.curveTo3:\n          case OPS.closePath:\n          case OPS.rectangle:\n            self.buildPath(operatorList, fn, args, parsingText);\n            continue;\n          case OPS.markPoint:\n          case OPS.markPointProps:\n          case OPS.beginCompat:\n          case OPS.endCompat:\n            continue;\n          case OPS.beginMarkedContentProps:\n            if (!(args[0] instanceof Name)) {\n              warn(`Expected name for beginMarkedContentProps arg0=${args[0]}`);\n              continue;\n            }\n            if (args[0].name === \"OC\") {\n              next(self.parseMarkedContentProps(args[1], resources).then(data => {\n                operatorList.addOp(OPS.beginMarkedContentProps, [\"OC\", data]);\n              }).catch(reason => {\n                if (reason instanceof AbortException) {\n                  return;\n                }\n                if (self.options.ignoreErrors) {\n                  warn(`getOperatorList - ignoring beginMarkedContentProps: \"${reason}\".`);\n                  return;\n                }\n                throw reason;\n              }));\n              return;\n            }\n            args = [args[0].name, args[1] instanceof Dict ? args[1].get(\"MCID\") : null];\n            break;\n          case OPS.beginMarkedContent:\n          case OPS.endMarkedContent:\n          default:\n            if (args !== null) {\n              for (i = 0, ii = args.length; i < ii; i++) {\n                if (args[i] instanceof Dict) {\n                  break;\n                }\n              }\n              if (i < ii) {\n                warn(\"getOperatorList - ignoring operator: \" + fn);\n                continue;\n              }\n            }\n        }\n        operatorList.addOp(fn, args);\n      }\n      if (stop) {\n        next(deferred);\n        return;\n      }\n      closePendingRestoreOPS();\n      resolve();\n    }).catch(reason => {\n      if (reason instanceof AbortException) {\n        return;\n      }\n      if (this.options.ignoreErrors) {\n        warn(`getOperatorList - ignoring errors during \"${task.name}\" ` + `task: \"${reason}\".`);\n        closePendingRestoreOPS();\n        return;\n      }\n      throw reason;\n    });\n  }\n  getTextContent({\n    stream,\n    task,\n    resources,\n    stateManager = null,\n    includeMarkedContent = false,\n    sink,\n    seenStyles = new Set(),\n    viewBox,\n    markedContentData = null,\n    disableNormalization = false\n  }) {\n    resources ||= Dict.empty;\n    stateManager ||= new StateManager(new TextState());\n    if (includeMarkedContent) {\n      markedContentData ||= {\n        level: 0\n      };\n    }\n    const textContent = {\n      items: [],\n      styles: Object.create(null)\n    };\n    const textContentItem = {\n      initialized: false,\n      str: [],\n      totalWidth: 0,\n      totalHeight: 0,\n      width: 0,\n      height: 0,\n      vertical: false,\n      prevTransform: null,\n      textAdvanceScale: 0,\n      spaceInFlowMin: 0,\n      spaceInFlowMax: 0,\n      trackingSpaceMin: Infinity,\n      negativeSpaceMax: -Infinity,\n      notASpace: -Infinity,\n      transform: null,\n      fontName: null,\n      hasEOL: false\n    };\n    const twoLastChars = [\" \", \" \"];\n    let twoLastCharsPos = 0;\n    function saveLastChar(char) {\n      const nextPos = (twoLastCharsPos + 1) % 2;\n      const ret = twoLastChars[twoLastCharsPos] !== \" \" && twoLastChars[nextPos] === \" \";\n      twoLastChars[twoLastCharsPos] = char;\n      twoLastCharsPos = nextPos;\n      return ret;\n    }\n    function shouldAddWhitepsace() {\n      return twoLastChars[twoLastCharsPos] !== \" \" && twoLastChars[(twoLastCharsPos + 1) % 2] === \" \";\n    }\n    function resetLastChars() {\n      twoLastChars[0] = twoLastChars[1] = \" \";\n      twoLastCharsPos = 0;\n    }\n    const TRACKING_SPACE_FACTOR = 0.102;\n    const NOT_A_SPACE_FACTOR = 0.03;\n    const NEGATIVE_SPACE_FACTOR = -0.2;\n    const SPACE_IN_FLOW_MIN_FACTOR = 0.102;\n    const SPACE_IN_FLOW_MAX_FACTOR = 0.6;\n    const VERTICAL_SHIFT_RATIO = 0.25;\n    const self = this;\n    const xref = this.xref;\n    const showSpacedTextBuffer = [];\n    let xobjs = null;\n    const emptyXObjectCache = new LocalImageCache();\n    const emptyGStateCache = new LocalGStateCache();\n    const preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);\n    let textState;\n    function pushWhitespace({\n      width = 0,\n      height = 0,\n      transform = textContentItem.prevTransform,\n      fontName = textContentItem.fontName\n    }) {\n      textContent.items.push({\n        str: \" \",\n        dir: \"ltr\",\n        width,\n        height,\n        transform,\n        fontName,\n        hasEOL: false\n      });\n    }\n    function getCurrentTextTransform() {\n      const font = textState.font;\n      const tsm = [textState.fontSize * textState.textHScale, 0, 0, textState.fontSize, 0, textState.textRise];\n      if (font.isType3Font && (textState.fontSize <= 1 || font.isCharBBox) && !isArrayEqual(textState.fontMatrix, FONT_IDENTITY_MATRIX)) {\n        const glyphHeight = font.bbox[3] - font.bbox[1];\n        if (glyphHeight > 0) {\n          tsm[3] *= glyphHeight * textState.fontMatrix[3];\n        }\n      }\n      return Util.transform(textState.ctm, Util.transform(textState.textMatrix, tsm));\n    }\n    function ensureTextContentItem() {\n      if (textContentItem.initialized) {\n        return textContentItem;\n      }\n      const {\n        font,\n        loadedName\n      } = textState;\n      if (!seenStyles.has(loadedName)) {\n        seenStyles.add(loadedName);\n        textContent.styles[loadedName] = {\n          fontFamily: font.fallbackName,\n          ascent: font.ascent,\n          descent: font.descent,\n          vertical: font.vertical\n        };\n        if (self.options.fontExtraProperties && font.systemFontInfo) {\n          const style = textContent.styles[loadedName];\n          style.fontSubstitution = font.systemFontInfo.css;\n          style.fontSubstitutionLoadedName = font.systemFontInfo.loadedName;\n        }\n      }\n      textContentItem.fontName = loadedName;\n      const trm = textContentItem.transform = getCurrentTextTransform();\n      if (!font.vertical) {\n        textContentItem.width = textContentItem.totalWidth = 0;\n        textContentItem.height = textContentItem.totalHeight = Math.hypot(trm[2], trm[3]);\n        textContentItem.vertical = false;\n      } else {\n        textContentItem.width = textContentItem.totalWidth = Math.hypot(trm[0], trm[1]);\n        textContentItem.height = textContentItem.totalHeight = 0;\n        textContentItem.vertical = true;\n      }\n      const scaleLineX = Math.hypot(textState.textLineMatrix[0], textState.textLineMatrix[1]);\n      const scaleCtmX = Math.hypot(textState.ctm[0], textState.ctm[1]);\n      textContentItem.textAdvanceScale = scaleCtmX * scaleLineX;\n      const {\n        fontSize\n      } = textState;\n      textContentItem.trackingSpaceMin = fontSize * TRACKING_SPACE_FACTOR;\n      textContentItem.notASpace = fontSize * NOT_A_SPACE_FACTOR;\n      textContentItem.negativeSpaceMax = fontSize * NEGATIVE_SPACE_FACTOR;\n      textContentItem.spaceInFlowMin = fontSize * SPACE_IN_FLOW_MIN_FACTOR;\n      textContentItem.spaceInFlowMax = fontSize * SPACE_IN_FLOW_MAX_FACTOR;\n      textContentItem.hasEOL = false;\n      textContentItem.initialized = true;\n      return textContentItem;\n    }\n    function updateAdvanceScale() {\n      if (!textContentItem.initialized) {\n        return;\n      }\n      const scaleLineX = Math.hypot(textState.textLineMatrix[0], textState.textLineMatrix[1]);\n      const scaleCtmX = Math.hypot(textState.ctm[0], textState.ctm[1]);\n      const scaleFactor = scaleCtmX * scaleLineX;\n      if (scaleFactor === textContentItem.textAdvanceScale) {\n        return;\n      }\n      if (!textContentItem.vertical) {\n        textContentItem.totalWidth += textContentItem.width * textContentItem.textAdvanceScale;\n        textContentItem.width = 0;\n      } else {\n        textContentItem.totalHeight += textContentItem.height * textContentItem.textAdvanceScale;\n        textContentItem.height = 0;\n      }\n      textContentItem.textAdvanceScale = scaleFactor;\n    }\n    function runBidiTransform(textChunk) {\n      let text = textChunk.str.join(\"\");\n      if (!disableNormalization) {\n        text = normalizeUnicode(text);\n      }\n      const bidiResult = bidi(text, -1, textChunk.vertical);\n      return {\n        str: bidiResult.str,\n        dir: bidiResult.dir,\n        width: Math.abs(textChunk.totalWidth),\n        height: Math.abs(textChunk.totalHeight),\n        transform: textChunk.transform,\n        fontName: textChunk.fontName,\n        hasEOL: textChunk.hasEOL\n      };\n    }\n    function handleSetFont(fontName, fontRef) {\n      return self.loadFont(fontName, fontRef, resources).then(function (translated) {\n        if (!translated.font.isType3Font) {\n          return translated;\n        }\n        return translated.loadType3Data(self, resources, task).catch(function () {}).then(function () {\n          return translated;\n        });\n      }).then(function (translated) {\n        textState.loadedName = translated.loadedName;\n        textState.font = translated.font;\n        textState.fontMatrix = translated.font.fontMatrix || FONT_IDENTITY_MATRIX;\n      });\n    }\n    function applyInverseRotation(x, y, matrix) {\n      const scale = Math.hypot(matrix[0], matrix[1]);\n      return [(matrix[0] * x + matrix[1] * y) / scale, (matrix[2] * x + matrix[3] * y) / scale];\n    }\n    function compareWithLastPosition(glyphWidth) {\n      const currentTransform = getCurrentTextTransform();\n      let posX = currentTransform[4];\n      let posY = currentTransform[5];\n      if (textState.font?.vertical) {\n        if (posX < viewBox[0] || posX > viewBox[2] || posY + glyphWidth < viewBox[1] || posY > viewBox[3]) {\n          return false;\n        }\n      } else if (posX + glyphWidth < viewBox[0] || posX > viewBox[2] || posY < viewBox[1] || posY > viewBox[3]) {\n        return false;\n      }\n      if (!textState.font || !textContentItem.prevTransform) {\n        return true;\n      }\n      let lastPosX = textContentItem.prevTransform[4];\n      let lastPosY = textContentItem.prevTransform[5];\n      if (lastPosX === posX && lastPosY === posY) {\n        return true;\n      }\n      let rotate = -1;\n      if (currentTransform[0] && currentTransform[1] === 0 && currentTransform[2] === 0) {\n        rotate = currentTransform[0] > 0 ? 0 : 180;\n      } else if (currentTransform[1] && currentTransform[0] === 0 && currentTransform[3] === 0) {\n        rotate = currentTransform[1] > 0 ? 90 : 270;\n      }\n      switch (rotate) {\n        case 0:\n          break;\n        case 90:\n          [posX, posY] = [posY, posX];\n          [lastPosX, lastPosY] = [lastPosY, lastPosX];\n          break;\n        case 180:\n          [posX, posY, lastPosX, lastPosY] = [-posX, -posY, -lastPosX, -lastPosY];\n          break;\n        case 270:\n          [posX, posY] = [-posY, -posX];\n          [lastPosX, lastPosY] = [-lastPosY, -lastPosX];\n          break;\n        default:\n          [posX, posY] = applyInverseRotation(posX, posY, currentTransform);\n          [lastPosX, lastPosY] = applyInverseRotation(lastPosX, lastPosY, textContentItem.prevTransform);\n      }\n      if (textState.font.vertical) {\n        const advanceY = (lastPosY - posY) / textContentItem.textAdvanceScale;\n        const advanceX = posX - lastPosX;\n        const textOrientation = Math.sign(textContentItem.height);\n        if (advanceY < textOrientation * textContentItem.negativeSpaceMax) {\n          if (Math.abs(advanceX) > 0.5 * textContentItem.width) {\n            appendEOL();\n            return true;\n          }\n          resetLastChars();\n          flushTextContentItem();\n          return true;\n        }\n        if (Math.abs(advanceX) > textContentItem.width) {\n          appendEOL();\n          return true;\n        }\n        if (advanceY <= textOrientation * textContentItem.notASpace) {\n          resetLastChars();\n        }\n        if (advanceY <= textOrientation * textContentItem.trackingSpaceMin) {\n          if (shouldAddWhitepsace()) {\n            resetLastChars();\n            flushTextContentItem();\n            pushWhitespace({\n              height: Math.abs(advanceY)\n            });\n          } else {\n            textContentItem.height += advanceY;\n          }\n        } else if (!addFakeSpaces(advanceY, textContentItem.prevTransform, textOrientation)) {\n          if (textContentItem.str.length === 0) {\n            resetLastChars();\n            pushWhitespace({\n              height: Math.abs(advanceY)\n            });\n          } else {\n            textContentItem.height += advanceY;\n          }\n        }\n        if (Math.abs(advanceX) > textContentItem.width * VERTICAL_SHIFT_RATIO) {\n          flushTextContentItem();\n        }\n        return true;\n      }\n      const advanceX = (posX - lastPosX) / textContentItem.textAdvanceScale;\n      const advanceY = posY - lastPosY;\n      const textOrientation = Math.sign(textContentItem.width);\n      if (advanceX < textOrientation * textContentItem.negativeSpaceMax) {\n        if (Math.abs(advanceY) > 0.5 * textContentItem.height) {\n          appendEOL();\n          return true;\n        }\n        resetLastChars();\n        flushTextContentItem();\n        return true;\n      }\n      if (Math.abs(advanceY) > textContentItem.height) {\n        appendEOL();\n        return true;\n      }\n      if (advanceX <= textOrientation * textContentItem.notASpace) {\n        resetLastChars();\n      }\n      if (advanceX <= textOrientation * textContentItem.trackingSpaceMin) {\n        if (shouldAddWhitepsace()) {\n          resetLastChars();\n          flushTextContentItem();\n          pushWhitespace({\n            width: Math.abs(advanceX)\n          });\n        } else {\n          textContentItem.width += advanceX;\n        }\n      } else if (!addFakeSpaces(advanceX, textContentItem.prevTransform, textOrientation)) {\n        if (textContentItem.str.length === 0) {\n          resetLastChars();\n          pushWhitespace({\n            width: Math.abs(advanceX)\n          });\n        } else {\n          textContentItem.width += advanceX;\n        }\n      }\n      if (Math.abs(advanceY) > textContentItem.height * VERTICAL_SHIFT_RATIO) {\n        flushTextContentItem();\n      }\n      return true;\n    }\n    function buildTextContentItem({\n      chars,\n      extraSpacing\n    }) {\n      const font = textState.font;\n      if (!chars) {\n        const charSpacing = textState.charSpacing + extraSpacing;\n        if (charSpacing) {\n          if (!font.vertical) {\n            textState.translateTextMatrix(charSpacing * textState.textHScale, 0);\n          } else {\n            textState.translateTextMatrix(0, -charSpacing);\n          }\n        }\n        return;\n      }\n      const glyphs = font.charsToGlyphs(chars);\n      const scale = textState.fontMatrix[0] * textState.fontSize;\n      for (let i = 0, ii = glyphs.length; i < ii; i++) {\n        const glyph = glyphs[i];\n        const {\n          category\n        } = glyph;\n        if (category.isInvisibleFormatMark) {\n          continue;\n        }\n        let charSpacing = textState.charSpacing + (i + 1 === ii ? extraSpacing : 0);\n        let glyphWidth = glyph.width;\n        if (font.vertical) {\n          glyphWidth = glyph.vmetric ? glyph.vmetric[0] : -glyphWidth;\n        }\n        let scaledDim = glyphWidth * scale;\n        if (category.isWhitespace) {\n          if (!font.vertical) {\n            charSpacing += scaledDim + textState.wordSpacing;\n            textState.translateTextMatrix(charSpacing * textState.textHScale, 0);\n          } else {\n            charSpacing += -scaledDim + textState.wordSpacing;\n            textState.translateTextMatrix(0, -charSpacing);\n          }\n          saveLastChar(\" \");\n          continue;\n        }\n        if (!category.isZeroWidthDiacritic && !compareWithLastPosition(scaledDim)) {\n          if (!font.vertical) {\n            textState.translateTextMatrix(scaledDim * textState.textHScale, 0);\n          } else {\n            textState.translateTextMatrix(0, scaledDim);\n          }\n          continue;\n        }\n        const textChunk = ensureTextContentItem();\n        if (category.isZeroWidthDiacritic) {\n          scaledDim = 0;\n        }\n        if (!font.vertical) {\n          scaledDim *= textState.textHScale;\n          textState.translateTextMatrix(scaledDim, 0);\n          textChunk.width += scaledDim;\n        } else {\n          textState.translateTextMatrix(0, scaledDim);\n          scaledDim = Math.abs(scaledDim);\n          textChunk.height += scaledDim;\n        }\n        if (scaledDim) {\n          textChunk.prevTransform = getCurrentTextTransform();\n        }\n        const glyphUnicode = glyph.unicode;\n        if (saveLastChar(glyphUnicode)) {\n          textChunk.str.push(\" \");\n        }\n        textChunk.str.push(glyphUnicode);\n        if (charSpacing) {\n          if (!font.vertical) {\n            textState.translateTextMatrix(charSpacing * textState.textHScale, 0);\n          } else {\n            textState.translateTextMatrix(0, -charSpacing);\n          }\n        }\n      }\n    }\n    function appendEOL() {\n      resetLastChars();\n      if (textContentItem.initialized) {\n        textContentItem.hasEOL = true;\n        flushTextContentItem();\n      } else {\n        textContent.items.push({\n          str: \"\",\n          dir: \"ltr\",\n          width: 0,\n          height: 0,\n          transform: getCurrentTextTransform(),\n          fontName: textState.loadedName,\n          hasEOL: true\n        });\n      }\n    }\n    function addFakeSpaces(width, transf, textOrientation) {\n      if (textOrientation * textContentItem.spaceInFlowMin <= width && width <= textOrientation * textContentItem.spaceInFlowMax) {\n        if (textContentItem.initialized) {\n          resetLastChars();\n          textContentItem.str.push(\" \");\n        }\n        return false;\n      }\n      const fontName = textContentItem.fontName;\n      let height = 0;\n      if (textContentItem.vertical) {\n        height = width;\n        width = 0;\n      }\n      flushTextContentItem();\n      resetLastChars();\n      pushWhitespace({\n        width: Math.abs(width),\n        height: Math.abs(height),\n        transform: transf || getCurrentTextTransform(),\n        fontName\n      });\n      return true;\n    }\n    function flushTextContentItem() {\n      if (!textContentItem.initialized || !textContentItem.str) {\n        return;\n      }\n      if (!textContentItem.vertical) {\n        textContentItem.totalWidth += textContentItem.width * textContentItem.textAdvanceScale;\n      } else {\n        textContentItem.totalHeight += textContentItem.height * textContentItem.textAdvanceScale;\n      }\n      textContent.items.push(runBidiTransform(textContentItem));\n      textContentItem.initialized = false;\n      textContentItem.str.length = 0;\n    }\n    function enqueueChunk(batch = false) {\n      const length = textContent.items.length;\n      if (length === 0) {\n        return;\n      }\n      if (batch && length < TEXT_CHUNK_BATCH_SIZE) {\n        return;\n      }\n      sink.enqueue(textContent, length);\n      textContent.items = [];\n      textContent.styles = Object.create(null);\n    }\n    const timeSlotManager = new TimeSlotManager();\n    return new Promise(function promiseBody(resolve, reject) {\n      const next = function (promise) {\n        enqueueChunk(true);\n        Promise.all([promise, sink.ready]).then(function () {\n          try {\n            promiseBody(resolve, reject);\n          } catch (ex) {\n            reject(ex);\n          }\n        }, reject);\n      };\n      task.ensureNotTerminated();\n      timeSlotManager.reset();\n      const operation = {};\n      let stop,\n        args = [];\n      while (!(stop = timeSlotManager.check())) {\n        args.length = 0;\n        operation.args = args;\n        if (!preprocessor.read(operation)) {\n          break;\n        }\n        const previousState = textState;\n        textState = stateManager.state;\n        const fn = operation.fn;\n        args = operation.args;\n        switch (fn | 0) {\n          case OPS.setFont:\n            var fontNameArg = args[0].name,\n              fontSizeArg = args[1];\n            if (textState.font && fontNameArg === textState.fontName && fontSizeArg === textState.fontSize) {\n              break;\n            }\n            flushTextContentItem();\n            textState.fontName = fontNameArg;\n            textState.fontSize = fontSizeArg;\n            next(handleSetFont(fontNameArg, null));\n            return;\n          case OPS.setTextRise:\n            textState.textRise = args[0];\n            break;\n          case OPS.setHScale:\n            textState.textHScale = args[0] / 100;\n            break;\n          case OPS.setLeading:\n            textState.leading = args[0];\n            break;\n          case OPS.moveText:\n            textState.translateTextLineMatrix(args[0], args[1]);\n            textState.textMatrix = textState.textLineMatrix.slice();\n            break;\n          case OPS.setLeadingMoveText:\n            textState.leading = -args[1];\n            textState.translateTextLineMatrix(args[0], args[1]);\n            textState.textMatrix = textState.textLineMatrix.slice();\n            break;\n          case OPS.nextLine:\n            textState.carriageReturn();\n            break;\n          case OPS.setTextMatrix:\n            textState.setTextMatrix(args[0], args[1], args[2], args[3], args[4], args[5]);\n            textState.setTextLineMatrix(args[0], args[1], args[2], args[3], args[4], args[5]);\n            updateAdvanceScale();\n            break;\n          case OPS.setCharSpacing:\n            textState.charSpacing = args[0];\n            break;\n          case OPS.setWordSpacing:\n            textState.wordSpacing = args[0];\n            break;\n          case OPS.beginText:\n            textState.textMatrix = IDENTITY_MATRIX.slice();\n            textState.textLineMatrix = IDENTITY_MATRIX.slice();\n            break;\n          case OPS.showSpacedText:\n            if (!stateManager.state.font) {\n              self.ensureStateFont(stateManager.state);\n              continue;\n            }\n            const spaceFactor = (textState.font.vertical ? 1 : -1) * textState.fontSize / 1000;\n            const elements = args[0];\n            for (let i = 0, ii = elements.length; i < ii; i++) {\n              const item = elements[i];\n              if (typeof item === \"string\") {\n                showSpacedTextBuffer.push(item);\n              } else if (typeof item === \"number\" && item !== 0) {\n                const str = showSpacedTextBuffer.join(\"\");\n                showSpacedTextBuffer.length = 0;\n                buildTextContentItem({\n                  chars: str,\n                  extraSpacing: item * spaceFactor\n                });\n              }\n            }\n            if (showSpacedTextBuffer.length > 0) {\n              const str = showSpacedTextBuffer.join(\"\");\n              showSpacedTextBuffer.length = 0;\n              buildTextContentItem({\n                chars: str,\n                extraSpacing: 0\n              });\n            }\n            break;\n          case OPS.showText:\n            if (!stateManager.state.font) {\n              self.ensureStateFont(stateManager.state);\n              continue;\n            }\n            buildTextContentItem({\n              chars: args[0],\n              extraSpacing: 0\n            });\n            break;\n          case OPS.nextLineShowText:\n            if (!stateManager.state.font) {\n              self.ensureStateFont(stateManager.state);\n              continue;\n            }\n            textState.carriageReturn();\n            buildTextContentItem({\n              chars: args[0],\n              extraSpacing: 0\n            });\n            break;\n          case OPS.nextLineSetSpacingShowText:\n            if (!stateManager.state.font) {\n              self.ensureStateFont(stateManager.state);\n              continue;\n            }\n            textState.wordSpacing = args[0];\n            textState.charSpacing = args[1];\n            textState.carriageReturn();\n            buildTextContentItem({\n              chars: args[2],\n              extraSpacing: 0\n            });\n            break;\n          case OPS.paintXObject:\n            flushTextContentItem();\n            if (!xobjs) {\n              xobjs = resources.get(\"XObject\") || Dict.empty;\n            }\n            var isValidName = args[0] instanceof Name;\n            var name = args[0].name;\n            if (isValidName && emptyXObjectCache.getByName(name)) {\n              break;\n            }\n            next(new Promise(function (resolveXObject, rejectXObject) {\n              if (!isValidName) {\n                throw new FormatError(\"XObject must be referred to by name.\");\n              }\n              let xobj = xobjs.getRaw(name);\n              if (xobj instanceof Ref) {\n                if (emptyXObjectCache.getByRef(xobj)) {\n                  resolveXObject();\n                  return;\n                }\n                const globalImage = self.globalImageCache.getData(xobj, self.pageIndex);\n                if (globalImage) {\n                  resolveXObject();\n                  return;\n                }\n                xobj = xref.fetch(xobj);\n              }\n              if (!(xobj instanceof BaseStream)) {\n                throw new FormatError(\"XObject should be a stream\");\n              }\n              const type = xobj.dict.get(\"Subtype\");\n              if (!(type instanceof Name)) {\n                throw new FormatError(\"XObject should have a Name subtype\");\n              }\n              if (type.name !== \"Form\") {\n                emptyXObjectCache.set(name, xobj.dict.objId, true);\n                resolveXObject();\n                return;\n              }\n              const currentState = stateManager.state.clone();\n              const xObjStateManager = new StateManager(currentState);\n              const matrix = xobj.dict.getArray(\"Matrix\");\n              if (Array.isArray(matrix) && matrix.length === 6) {\n                xObjStateManager.transform(matrix);\n              }\n              enqueueChunk();\n              const sinkWrapper = {\n                enqueueInvoked: false,\n                enqueue(chunk, size) {\n                  this.enqueueInvoked = true;\n                  sink.enqueue(chunk, size);\n                },\n                get desiredSize() {\n                  return sink.desiredSize;\n                },\n                get ready() {\n                  return sink.ready;\n                }\n              };\n              self.getTextContent({\n                stream: xobj,\n                task,\n                resources: xobj.dict.get(\"Resources\") || resources,\n                stateManager: xObjStateManager,\n                includeMarkedContent,\n                sink: sinkWrapper,\n                seenStyles,\n                viewBox,\n                markedContentData,\n                disableNormalization\n              }).then(function () {\n                if (!sinkWrapper.enqueueInvoked) {\n                  emptyXObjectCache.set(name, xobj.dict.objId, true);\n                }\n                resolveXObject();\n              }, rejectXObject);\n            }).catch(function (reason) {\n              if (reason instanceof AbortException) {\n                return;\n              }\n              if (self.options.ignoreErrors) {\n                warn(`getTextContent - ignoring XObject: \"${reason}\".`);\n                return;\n              }\n              throw reason;\n            }));\n            return;\n          case OPS.setGState:\n            isValidName = args[0] instanceof Name;\n            name = args[0].name;\n            if (isValidName && emptyGStateCache.getByName(name)) {\n              break;\n            }\n            next(new Promise(function (resolveGState, rejectGState) {\n              if (!isValidName) {\n                throw new FormatError(\"GState must be referred to by name.\");\n              }\n              const extGState = resources.get(\"ExtGState\");\n              if (!(extGState instanceof Dict)) {\n                throw new FormatError(\"ExtGState should be a dictionary.\");\n              }\n              const gState = extGState.get(name);\n              if (!(gState instanceof Dict)) {\n                throw new FormatError(\"GState should be a dictionary.\");\n              }\n              const gStateFont = gState.get(\"Font\");\n              if (!gStateFont) {\n                emptyGStateCache.set(name, gState.objId, true);\n                resolveGState();\n                return;\n              }\n              flushTextContentItem();\n              textState.fontName = null;\n              textState.fontSize = gStateFont[1];\n              handleSetFont(null, gStateFont[0]).then(resolveGState, rejectGState);\n            }).catch(function (reason) {\n              if (reason instanceof AbortException) {\n                return;\n              }\n              if (self.options.ignoreErrors) {\n                warn(`getTextContent - ignoring ExtGState: \"${reason}\".`);\n                return;\n              }\n              throw reason;\n            }));\n            return;\n          case OPS.beginMarkedContent:\n            flushTextContentItem();\n            if (includeMarkedContent) {\n              markedContentData.level++;\n              textContent.items.push({\n                type: \"beginMarkedContent\",\n                tag: args[0] instanceof Name ? args[0].name : null\n              });\n            }\n            break;\n          case OPS.beginMarkedContentProps:\n            flushTextContentItem();\n            if (includeMarkedContent) {\n              markedContentData.level++;\n              let mcid = null;\n              if (args[1] instanceof Dict) {\n                mcid = args[1].get(\"MCID\");\n              }\n              textContent.items.push({\n                type: \"beginMarkedContentProps\",\n                id: Number.isInteger(mcid) ? `${self.idFactory.getPageObjId()}_mc${mcid}` : null,\n                tag: args[0] instanceof Name ? args[0].name : null\n              });\n            }\n            break;\n          case OPS.endMarkedContent:\n            flushTextContentItem();\n            if (includeMarkedContent) {\n              if (markedContentData.level === 0) {\n                break;\n              }\n              markedContentData.level--;\n              textContent.items.push({\n                type: \"endMarkedContent\"\n              });\n            }\n            break;\n          case OPS.restore:\n            if (previousState && (previousState.font !== textState.font || previousState.fontSize !== textState.fontSize || previousState.fontName !== textState.fontName)) {\n              flushTextContentItem();\n            }\n            break;\n        }\n        if (textContent.items.length >= sink.desiredSize) {\n          stop = true;\n          break;\n        }\n      }\n      if (stop) {\n        next(deferred);\n        return;\n      }\n      flushTextContentItem();\n      enqueueChunk();\n      resolve();\n    }).catch(reason => {\n      if (reason instanceof AbortException) {\n        return;\n      }\n      if (this.options.ignoreErrors) {\n        warn(`getTextContent - ignoring errors during \"${task.name}\" ` + `task: \"${reason}\".`);\n        flushTextContentItem();\n        enqueueChunk();\n        return;\n      }\n      throw reason;\n    });\n  }\n  extractDataStructures(dict, baseDict, properties) {\n    const xref = this.xref;\n    let cidToGidBytes;\n    const toUnicodePromise = this.readToUnicode(properties.toUnicode || dict.get(\"ToUnicode\") || baseDict.get(\"ToUnicode\"));\n    if (properties.composite) {\n      const cidSystemInfo = dict.get(\"CIDSystemInfo\");\n      if (cidSystemInfo instanceof Dict) {\n        properties.cidSystemInfo = {\n          registry: stringToPDFString(cidSystemInfo.get(\"Registry\")),\n          ordering: stringToPDFString(cidSystemInfo.get(\"Ordering\")),\n          supplement: cidSystemInfo.get(\"Supplement\")\n        };\n      }\n      try {\n        const cidToGidMap = dict.get(\"CIDToGIDMap\");\n        if (cidToGidMap instanceof BaseStream) {\n          cidToGidBytes = cidToGidMap.getBytes();\n        }\n      } catch (ex) {\n        if (!this.options.ignoreErrors) {\n          throw ex;\n        }\n        warn(`extractDataStructures - ignoring CIDToGIDMap data: \"${ex}\".`);\n      }\n    }\n    const differences = [];\n    let baseEncodingName = null;\n    let encoding;\n    if (dict.has(\"Encoding\")) {\n      encoding = dict.get(\"Encoding\");\n      if (encoding instanceof Dict) {\n        baseEncodingName = encoding.get(\"BaseEncoding\");\n        baseEncodingName = baseEncodingName instanceof Name ? baseEncodingName.name : null;\n        if (encoding.has(\"Differences\")) {\n          const diffEncoding = encoding.get(\"Differences\");\n          let index = 0;\n          for (const entry of diffEncoding) {\n            const data = xref.fetchIfRef(entry);\n            if (typeof data === \"number\") {\n              index = data;\n            } else if (data instanceof Name) {\n              differences[index++] = data.name;\n            } else {\n              throw new FormatError(`Invalid entry in 'Differences' array: ${data}`);\n            }\n          }\n        }\n      } else if (encoding instanceof Name) {\n        baseEncodingName = encoding.name;\n      } else {\n        const msg = \"Encoding is not a Name nor a Dict\";\n        if (!this.options.ignoreErrors) {\n          throw new FormatError(msg);\n        }\n        warn(msg);\n      }\n      if (baseEncodingName !== \"MacRomanEncoding\" && baseEncodingName !== \"MacExpertEncoding\" && baseEncodingName !== \"WinAnsiEncoding\") {\n        baseEncodingName = null;\n      }\n    }\n    const nonEmbeddedFont = !properties.file || properties.isInternalFont,\n      isSymbolsFontName = getSymbolsFonts()[properties.name];\n    if (baseEncodingName && nonEmbeddedFont && isSymbolsFontName) {\n      baseEncodingName = null;\n    }\n    if (baseEncodingName) {\n      properties.defaultEncoding = getEncoding(baseEncodingName);\n    } else {\n      const isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);\n      const isNonsymbolicFont = !!(properties.flags & FontFlags.Nonsymbolic);\n      encoding = StandardEncoding;\n      if (properties.type === \"TrueType\" && !isNonsymbolicFont) {\n        encoding = WinAnsiEncoding;\n      }\n      if (isSymbolicFont || isSymbolsFontName) {\n        encoding = MacRomanEncoding;\n        if (nonEmbeddedFont) {\n          if (/Symbol/i.test(properties.name)) {\n            encoding = SymbolSetEncoding;\n          } else if (/Dingbats/i.test(properties.name)) {\n            encoding = ZapfDingbatsEncoding;\n          } else if (/Wingdings/i.test(properties.name)) {\n            encoding = WinAnsiEncoding;\n          }\n        }\n      }\n      properties.defaultEncoding = encoding;\n    }\n    properties.differences = differences;\n    properties.baseEncodingName = baseEncodingName;\n    properties.hasEncoding = !!baseEncodingName || differences.length > 0;\n    properties.dict = dict;\n    return toUnicodePromise.then(readToUnicode => {\n      properties.toUnicode = readToUnicode;\n      return this.buildToUnicode(properties);\n    }).then(builtToUnicode => {\n      properties.toUnicode = builtToUnicode;\n      if (cidToGidBytes) {\n        properties.cidToGidMap = this.readCidToGidMap(cidToGidBytes, builtToUnicode);\n      }\n      return properties;\n    });\n  }\n  _simpleFontToUnicode(properties, forceGlyphs = false) {\n    assert(!properties.composite, \"Must be a simple font.\");\n    const toUnicode = [];\n    const encoding = properties.defaultEncoding.slice();\n    const baseEncodingName = properties.baseEncodingName;\n    const differences = properties.differences;\n    for (const charcode in differences) {\n      const glyphName = differences[charcode];\n      if (glyphName === \".notdef\") {\n        continue;\n      }\n      encoding[charcode] = glyphName;\n    }\n    const glyphsUnicodeMap = getGlyphsUnicode();\n    for (const charcode in encoding) {\n      let glyphName = encoding[charcode];\n      if (glyphName === \"\") {\n        continue;\n      }\n      let unicode = glyphsUnicodeMap[glyphName];\n      if (unicode !== undefined) {\n        toUnicode[charcode] = String.fromCharCode(unicode);\n        continue;\n      }\n      let code = 0;\n      switch (glyphName[0]) {\n        case \"G\":\n          if (glyphName.length === 3) {\n            code = parseInt(glyphName.substring(1), 16);\n          }\n          break;\n        case \"g\":\n          if (glyphName.length === 5) {\n            code = parseInt(glyphName.substring(1), 16);\n          }\n          break;\n        case \"C\":\n        case \"c\":\n          if (glyphName.length >= 3 && glyphName.length <= 4) {\n            const codeStr = glyphName.substring(1);\n            if (forceGlyphs) {\n              code = parseInt(codeStr, 16);\n              break;\n            }\n            code = +codeStr;\n            if (Number.isNaN(code) && Number.isInteger(parseInt(codeStr, 16))) {\n              return this._simpleFontToUnicode(properties, true);\n            }\n          }\n          break;\n        case \"u\":\n          unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap);\n          if (unicode !== -1) {\n            code = unicode;\n          }\n          break;\n        default:\n          switch (glyphName) {\n            case \"f_h\":\n            case \"f_t\":\n            case \"T_h\":\n              toUnicode[charcode] = glyphName.replaceAll(\"_\", \"\");\n              continue;\n          }\n          break;\n      }\n      if (code > 0 && code <= 0x10ffff && Number.isInteger(code)) {\n        if (baseEncodingName && code === +charcode) {\n          const baseEncoding = getEncoding(baseEncodingName);\n          if (baseEncoding && (glyphName = baseEncoding[charcode])) {\n            toUnicode[charcode] = String.fromCharCode(glyphsUnicodeMap[glyphName]);\n            continue;\n          }\n        }\n        toUnicode[charcode] = String.fromCodePoint(code);\n      }\n    }\n    return toUnicode;\n  }\n  async buildToUnicode(properties) {\n    properties.hasIncludedToUnicodeMap = properties.toUnicode?.length > 0;\n    if (properties.hasIncludedToUnicodeMap) {\n      if (!properties.composite && properties.hasEncoding) {\n        properties.fallbackToUnicode = this._simpleFontToUnicode(properties);\n      }\n      return properties.toUnicode;\n    }\n    if (!properties.composite) {\n      return new ToUnicodeMap(this._simpleFontToUnicode(properties));\n    }\n    if (properties.composite && (properties.cMap.builtInCMap && !(properties.cMap instanceof IdentityCMap) || properties.cidSystemInfo.registry === \"Adobe\" && (properties.cidSystemInfo.ordering === \"GB1\" || properties.cidSystemInfo.ordering === \"CNS1\" || properties.cidSystemInfo.ordering === \"Japan1\" || properties.cidSystemInfo.ordering === \"Korea1\"))) {\n      const {\n        registry,\n        ordering\n      } = properties.cidSystemInfo;\n      const ucs2CMapName = Name.get(`${registry}-${ordering}-UCS2`);\n      const ucs2CMap = await CMapFactory.create({\n        encoding: ucs2CMapName,\n        fetchBuiltInCMap: this._fetchBuiltInCMapBound,\n        useCMap: null\n      });\n      const toUnicode = [],\n        buf = [];\n      properties.cMap.forEach(function (charcode, cid) {\n        if (cid > 0xffff) {\n          throw new FormatError(\"Max size of CID is 65,535\");\n        }\n        const ucs2 = ucs2CMap.lookup(cid);\n        if (ucs2) {\n          buf.length = 0;\n          for (let i = 0, ii = ucs2.length; i < ii; i += 2) {\n            buf.push((ucs2.charCodeAt(i) << 8) + ucs2.charCodeAt(i + 1));\n          }\n          toUnicode[charcode] = String.fromCharCode(...buf);\n        }\n      });\n      return new ToUnicodeMap(toUnicode);\n    }\n    return new IdentityToUnicodeMap(properties.firstChar, properties.lastChar);\n  }\n  readToUnicode(cmapObj) {\n    if (!cmapObj) {\n      return Promise.resolve(null);\n    }\n    if (cmapObj instanceof Name) {\n      return CMapFactory.create({\n        encoding: cmapObj,\n        fetchBuiltInCMap: this._fetchBuiltInCMapBound,\n        useCMap: null\n      }).then(function (cmap) {\n        if (cmap instanceof IdentityCMap) {\n          return new IdentityToUnicodeMap(0, 0xffff);\n        }\n        return new ToUnicodeMap(cmap.getMap());\n      });\n    } else if (cmapObj instanceof BaseStream) {\n      return CMapFactory.create({\n        encoding: cmapObj,\n        fetchBuiltInCMap: this._fetchBuiltInCMapBound,\n        useCMap: null\n      }).then(function (cmap) {\n        if (cmap instanceof IdentityCMap) {\n          return new IdentityToUnicodeMap(0, 0xffff);\n        }\n        const map = new Array(cmap.length);\n        cmap.forEach(function (charCode, token) {\n          if (typeof token === \"number\") {\n            map[charCode] = String.fromCodePoint(token);\n            return;\n          }\n          const str = [];\n          for (let k = 0; k < token.length; k += 2) {\n            const w1 = token.charCodeAt(k) << 8 | token.charCodeAt(k + 1);\n            if ((w1 & 0xf800) !== 0xd800) {\n              str.push(w1);\n              continue;\n            }\n            k += 2;\n            const w2 = token.charCodeAt(k) << 8 | token.charCodeAt(k + 1);\n            str.push(((w1 & 0x3ff) << 10) + (w2 & 0x3ff) + 0x10000);\n          }\n          map[charCode] = String.fromCodePoint(...str);\n        });\n        return new ToUnicodeMap(map);\n      }, reason => {\n        if (reason instanceof AbortException) {\n          return null;\n        }\n        if (this.options.ignoreErrors) {\n          warn(`readToUnicode - ignoring ToUnicode data: \"${reason}\".`);\n          return null;\n        }\n        throw reason;\n      });\n    }\n    return Promise.resolve(null);\n  }\n  readCidToGidMap(glyphsData, toUnicode) {\n    const result = [];\n    for (let j = 0, jj = glyphsData.length; j < jj; j++) {\n      const glyphID = glyphsData[j++] << 8 | glyphsData[j];\n      const code = j >> 1;\n      if (glyphID === 0 && !toUnicode.has(code)) {\n        continue;\n      }\n      result[code] = glyphID;\n    }\n    return result;\n  }\n  extractWidths(dict, descriptor, properties) {\n    const xref = this.xref;\n    let glyphsWidths = [];\n    let defaultWidth = 0;\n    const glyphsVMetrics = [];\n    let defaultVMetrics;\n    let i, ii, j, jj, start, code, widths;\n    if (properties.composite) {\n      defaultWidth = dict.has(\"DW\") ? dict.get(\"DW\") : 1000;\n      widths = dict.get(\"W\");\n      if (widths) {\n        for (i = 0, ii = widths.length; i < ii; i++) {\n          start = xref.fetchIfRef(widths[i++]);\n          code = xref.fetchIfRef(widths[i]);\n          if (Array.isArray(code)) {\n            for (j = 0, jj = code.length; j < jj; j++) {\n              glyphsWidths[start++] = xref.fetchIfRef(code[j]);\n            }\n          } else {\n            const width = xref.fetchIfRef(widths[++i]);\n            for (j = start; j <= code; j++) {\n              glyphsWidths[j] = width;\n            }\n          }\n        }\n      }\n      if (properties.vertical) {\n        let vmetrics = dict.getArray(\"DW2\") || [880, -1000];\n        defaultVMetrics = [vmetrics[1], defaultWidth * 0.5, vmetrics[0]];\n        vmetrics = dict.get(\"W2\");\n        if (vmetrics) {\n          for (i = 0, ii = vmetrics.length; i < ii; i++) {\n            start = xref.fetchIfRef(vmetrics[i++]);\n            code = xref.fetchIfRef(vmetrics[i]);\n            if (Array.isArray(code)) {\n              for (j = 0, jj = code.length; j < jj; j++) {\n                glyphsVMetrics[start++] = [xref.fetchIfRef(code[j++]), xref.fetchIfRef(code[j++]), xref.fetchIfRef(code[j])];\n              }\n            } else {\n              const vmetric = [xref.fetchIfRef(vmetrics[++i]), xref.fetchIfRef(vmetrics[++i]), xref.fetchIfRef(vmetrics[++i])];\n              for (j = start; j <= code; j++) {\n                glyphsVMetrics[j] = vmetric;\n              }\n            }\n          }\n        }\n      }\n    } else {\n      const firstChar = properties.firstChar;\n      widths = dict.get(\"Widths\");\n      if (widths) {\n        j = firstChar;\n        for (i = 0, ii = widths.length; i < ii; i++) {\n          glyphsWidths[j++] = xref.fetchIfRef(widths[i]);\n        }\n        defaultWidth = parseFloat(descriptor.get(\"MissingWidth\")) || 0;\n      } else {\n        const baseFontName = dict.get(\"BaseFont\");\n        if (baseFontName instanceof Name) {\n          const metrics = this.getBaseFontMetrics(baseFontName.name);\n          glyphsWidths = this.buildCharCodeToWidth(metrics.widths, properties);\n          defaultWidth = metrics.defaultWidth;\n        }\n      }\n    }\n    let isMonospace = true;\n    let firstWidth = defaultWidth;\n    for (const glyph in glyphsWidths) {\n      const glyphWidth = glyphsWidths[glyph];\n      if (!glyphWidth) {\n        continue;\n      }\n      if (!firstWidth) {\n        firstWidth = glyphWidth;\n        continue;\n      }\n      if (firstWidth !== glyphWidth) {\n        isMonospace = false;\n        break;\n      }\n    }\n    if (isMonospace) {\n      properties.flags |= FontFlags.FixedPitch;\n    } else {\n      properties.flags &= ~FontFlags.FixedPitch;\n    }\n    properties.defaultWidth = defaultWidth;\n    properties.widths = glyphsWidths;\n    properties.defaultVMetrics = defaultVMetrics;\n    properties.vmetrics = glyphsVMetrics;\n  }\n  isSerifFont(baseFontName) {\n    const fontNameWoStyle = baseFontName.split(\"-\")[0];\n    return fontNameWoStyle in getSerifFonts() || /serif/gi.test(fontNameWoStyle);\n  }\n  getBaseFontMetrics(name) {\n    let defaultWidth = 0;\n    let widths = Object.create(null);\n    let monospace = false;\n    const stdFontMap = getStdFontMap();\n    let lookupName = stdFontMap[name] || name;\n    const Metrics = getMetrics();\n    if (!(lookupName in Metrics)) {\n      lookupName = this.isSerifFont(name) ? \"Times-Roman\" : \"Helvetica\";\n    }\n    const glyphWidths = Metrics[lookupName];\n    if (typeof glyphWidths === \"number\") {\n      defaultWidth = glyphWidths;\n      monospace = true;\n    } else {\n      widths = glyphWidths();\n    }\n    return {\n      defaultWidth,\n      monospace,\n      widths\n    };\n  }\n  buildCharCodeToWidth(widthsByGlyphName, properties) {\n    const widths = Object.create(null);\n    const differences = properties.differences;\n    const encoding = properties.defaultEncoding;\n    for (let charCode = 0; charCode < 256; charCode++) {\n      if (charCode in differences && widthsByGlyphName[differences[charCode]]) {\n        widths[charCode] = widthsByGlyphName[differences[charCode]];\n        continue;\n      }\n      if (charCode in encoding && widthsByGlyphName[encoding[charCode]]) {\n        widths[charCode] = widthsByGlyphName[encoding[charCode]];\n        continue;\n      }\n    }\n    return widths;\n  }\n  preEvaluateFont(dict) {\n    const baseDict = dict;\n    let type = dict.get(\"Subtype\");\n    if (!(type instanceof Name)) {\n      throw new FormatError(\"invalid font Subtype\");\n    }\n    let composite = false;\n    let hash, toUnicode;\n    if (type.name === \"Type0\") {\n      const df = dict.get(\"DescendantFonts\");\n      if (!df) {\n        throw new FormatError(\"Descendant fonts are not specified\");\n      }\n      dict = Array.isArray(df) ? this.xref.fetchIfRef(df[0]) : df;\n      if (!(dict instanceof Dict)) {\n        throw new FormatError(\"Descendant font is not a dictionary.\");\n      }\n      type = dict.get(\"Subtype\");\n      if (!(type instanceof Name)) {\n        throw new FormatError(\"invalid font Subtype\");\n      }\n      composite = true;\n    }\n    const firstChar = dict.get(\"FirstChar\") || 0,\n      lastChar = dict.get(\"LastChar\") || (composite ? 0xffff : 0xff);\n    const descriptor = dict.get(\"FontDescriptor\");\n    if (descriptor) {\n      hash = new MurmurHash3_64();\n      const encoding = baseDict.getRaw(\"Encoding\");\n      if (encoding instanceof Name) {\n        hash.update(encoding.name);\n      } else if (encoding instanceof Ref) {\n        hash.update(encoding.toString());\n      } else if (encoding instanceof Dict) {\n        for (const entry of encoding.getRawValues()) {\n          if (entry instanceof Name) {\n            hash.update(entry.name);\n          } else if (entry instanceof Ref) {\n            hash.update(entry.toString());\n          } else if (Array.isArray(entry)) {\n            const diffLength = entry.length,\n              diffBuf = new Array(diffLength);\n            for (let j = 0; j < diffLength; j++) {\n              const diffEntry = entry[j];\n              if (diffEntry instanceof Name) {\n                diffBuf[j] = diffEntry.name;\n              } else if (typeof diffEntry === \"number\" || diffEntry instanceof Ref) {\n                diffBuf[j] = diffEntry.toString();\n              }\n            }\n            hash.update(diffBuf.join());\n          }\n        }\n      }\n      hash.update(`${firstChar}-${lastChar}`);\n      toUnicode = dict.get(\"ToUnicode\") || baseDict.get(\"ToUnicode\");\n      if (toUnicode instanceof BaseStream) {\n        const stream = toUnicode.str || toUnicode;\n        const uint8array = stream.buffer ? new Uint8Array(stream.buffer.buffer, 0, stream.bufferLength) : new Uint8Array(stream.bytes.buffer, stream.start, stream.end - stream.start);\n        hash.update(uint8array);\n      } else if (toUnicode instanceof Name) {\n        hash.update(toUnicode.name);\n      }\n      const widths = dict.get(\"Widths\") || baseDict.get(\"Widths\");\n      if (Array.isArray(widths)) {\n        const widthsBuf = [];\n        for (const entry of widths) {\n          if (typeof entry === \"number\" || entry instanceof Ref) {\n            widthsBuf.push(entry.toString());\n          }\n        }\n        hash.update(widthsBuf.join());\n      }\n      if (composite) {\n        hash.update(\"compositeFont\");\n        const compositeWidths = dict.get(\"W\") || baseDict.get(\"W\");\n        if (Array.isArray(compositeWidths)) {\n          const widthsBuf = [];\n          for (const entry of compositeWidths) {\n            if (typeof entry === \"number\" || entry instanceof Ref) {\n              widthsBuf.push(entry.toString());\n            } else if (Array.isArray(entry)) {\n              const subWidthsBuf = [];\n              for (const element of entry) {\n                if (typeof element === \"number\" || element instanceof Ref) {\n                  subWidthsBuf.push(element.toString());\n                }\n              }\n              widthsBuf.push(`[${subWidthsBuf.join()}]`);\n            }\n          }\n          hash.update(widthsBuf.join());\n        }\n        const cidToGidMap = dict.getRaw(\"CIDToGIDMap\") || baseDict.getRaw(\"CIDToGIDMap\");\n        if (cidToGidMap instanceof Name) {\n          hash.update(cidToGidMap.name);\n        } else if (cidToGidMap instanceof Ref) {\n          hash.update(cidToGidMap.toString());\n        } else if (cidToGidMap instanceof BaseStream) {\n          hash.update(cidToGidMap.peekBytes());\n        }\n      }\n    }\n    return {\n      descriptor,\n      dict,\n      baseDict,\n      composite,\n      type: type.name,\n      firstChar,\n      lastChar,\n      toUnicode,\n      hash: hash ? hash.hexdigest() : \"\"\n    };\n  }\n  async translateFont({\n    descriptor,\n    dict,\n    baseDict,\n    composite,\n    type,\n    firstChar,\n    lastChar,\n    toUnicode,\n    cssFontInfo\n  }) {\n    const isType3Font = type === \"Type3\";\n    let properties;\n    if (!descriptor) {\n      if (isType3Font) {\n        descriptor = new Dict(null);\n        descriptor.set(\"FontName\", Name.get(type));\n        descriptor.set(\"FontBBox\", dict.getArray(\"FontBBox\") || [0, 0, 0, 0]);\n      } else {\n        let baseFontName = dict.get(\"BaseFont\");\n        if (!(baseFontName instanceof Name)) {\n          throw new FormatError(\"Base font is not specified\");\n        }\n        baseFontName = baseFontName.name.replaceAll(/[,_]/g, \"-\");\n        const metrics = this.getBaseFontMetrics(baseFontName);\n        const fontNameWoStyle = baseFontName.split(\"-\")[0];\n        const flags = (this.isSerifFont(fontNameWoStyle) ? FontFlags.Serif : 0) | (metrics.monospace ? FontFlags.FixedPitch : 0) | (getSymbolsFonts()[fontNameWoStyle] ? FontFlags.Symbolic : FontFlags.Nonsymbolic);\n        properties = {\n          type,\n          name: baseFontName,\n          loadedName: baseDict.loadedName,\n          systemFontInfo: null,\n          widths: metrics.widths,\n          defaultWidth: metrics.defaultWidth,\n          isSimulatedFlags: true,\n          flags,\n          firstChar,\n          lastChar,\n          toUnicode,\n          xHeight: 0,\n          capHeight: 0,\n          italicAngle: 0,\n          isType3Font\n        };\n        const widths = dict.get(\"Widths\");\n        const standardFontName = getStandardFontName(baseFontName);\n        let file = null;\n        if (standardFontName) {\n          file = await this.fetchStandardFontData(standardFontName);\n          properties.isInternalFont = !!file;\n        }\n        if (!properties.isInternalFont && this.options.useSystemFonts) {\n          properties.systemFontInfo = getFontSubstitution(this.systemFontCache, this.idFactory, this.options.standardFontDataUrl, baseFontName, standardFontName);\n        }\n        return this.extractDataStructures(dict, dict, properties).then(newProperties => {\n          if (widths) {\n            const glyphWidths = [];\n            let j = firstChar;\n            for (const width of widths) {\n              glyphWidths[j++] = this.xref.fetchIfRef(width);\n            }\n            newProperties.widths = glyphWidths;\n          } else {\n            newProperties.widths = this.buildCharCodeToWidth(metrics.widths, newProperties);\n          }\n          return new Font(baseFontName, file, newProperties);\n        });\n      }\n    }\n    let fontName = descriptor.get(\"FontName\");\n    let baseFont = dict.get(\"BaseFont\");\n    if (typeof fontName === \"string\") {\n      fontName = Name.get(fontName);\n    }\n    if (typeof baseFont === \"string\") {\n      baseFont = Name.get(baseFont);\n    }\n    const fontNameStr = fontName?.name;\n    const baseFontStr = baseFont?.name;\n    if (!isType3Font && fontNameStr !== baseFontStr) {\n      info(`The FontDescriptor's FontName is \"${fontNameStr}\" but ` + `should be the same as the Font's BaseFont \"${baseFontStr}\".`);\n      if (fontNameStr && baseFontStr && (baseFontStr.startsWith(fontNameStr) || !isKnownFontName(fontNameStr) && isKnownFontName(baseFontStr))) {\n        fontName = null;\n      }\n    }\n    fontName ||= baseFont;\n    if (!(fontName instanceof Name)) {\n      throw new FormatError(\"invalid font name\");\n    }\n    let fontFile, subtype, length1, length2, length3;\n    try {\n      fontFile = descriptor.get(\"FontFile\", \"FontFile2\", \"FontFile3\");\n    } catch (ex) {\n      if (!this.options.ignoreErrors) {\n        throw ex;\n      }\n      warn(`translateFont - fetching \"${fontName.name}\" font file: \"${ex}\".`);\n      fontFile = new NullStream();\n    }\n    let isInternalFont = false;\n    let glyphScaleFactors = null;\n    let systemFontInfo = null;\n    if (fontFile) {\n      if (fontFile.dict) {\n        const subtypeEntry = fontFile.dict.get(\"Subtype\");\n        if (subtypeEntry instanceof Name) {\n          subtype = subtypeEntry.name;\n        }\n        length1 = fontFile.dict.get(\"Length1\");\n        length2 = fontFile.dict.get(\"Length2\");\n        length3 = fontFile.dict.get(\"Length3\");\n      }\n    } else if (cssFontInfo) {\n      const standardFontName = getXfaFontName(fontName.name);\n      if (standardFontName) {\n        cssFontInfo.fontFamily = `${cssFontInfo.fontFamily}-PdfJS-XFA`;\n        cssFontInfo.metrics = standardFontName.metrics || null;\n        glyphScaleFactors = standardFontName.factors || null;\n        fontFile = await this.fetchStandardFontData(standardFontName.name);\n        isInternalFont = !!fontFile;\n        baseDict = dict = getXfaFontDict(fontName.name);\n        composite = true;\n      }\n    } else if (!isType3Font) {\n      const standardFontName = getStandardFontName(fontName.name);\n      if (standardFontName) {\n        fontFile = await this.fetchStandardFontData(standardFontName);\n        isInternalFont = !!fontFile;\n      }\n      if (!isInternalFont && this.options.useSystemFonts) {\n        systemFontInfo = getFontSubstitution(this.systemFontCache, this.idFactory, this.options.standardFontDataUrl, fontName.name, standardFontName);\n      }\n    }\n    properties = {\n      type,\n      name: fontName.name,\n      subtype,\n      file: fontFile,\n      length1,\n      length2,\n      length3,\n      isInternalFont,\n      loadedName: baseDict.loadedName,\n      composite,\n      fixedPitch: false,\n      fontMatrix: dict.getArray(\"FontMatrix\") || FONT_IDENTITY_MATRIX,\n      firstChar,\n      lastChar,\n      toUnicode,\n      bbox: descriptor.getArray(\"FontBBox\") || dict.getArray(\"FontBBox\"),\n      ascent: descriptor.get(\"Ascent\"),\n      descent: descriptor.get(\"Descent\"),\n      xHeight: descriptor.get(\"XHeight\") || 0,\n      capHeight: descriptor.get(\"CapHeight\") || 0,\n      flags: descriptor.get(\"Flags\"),\n      italicAngle: descriptor.get(\"ItalicAngle\") || 0,\n      isType3Font,\n      cssFontInfo,\n      scaleFactors: glyphScaleFactors,\n      systemFontInfo\n    };\n    if (composite) {\n      const cidEncoding = baseDict.get(\"Encoding\");\n      if (cidEncoding instanceof Name) {\n        properties.cidEncoding = cidEncoding.name;\n      }\n      const cMap = await CMapFactory.create({\n        encoding: cidEncoding,\n        fetchBuiltInCMap: this._fetchBuiltInCMapBound,\n        useCMap: null\n      });\n      properties.cMap = cMap;\n      properties.vertical = properties.cMap.vertical;\n    }\n    return this.extractDataStructures(dict, baseDict, properties).then(newProperties => {\n      this.extractWidths(dict, descriptor, newProperties);\n      return new Font(fontName.name, fontFile, newProperties);\n    });\n  }\n  static buildFontPaths(font, glyphs, handler, evaluatorOptions) {\n    function buildPath(fontChar) {\n      const glyphName = `${font.loadedName}_path_${fontChar}`;\n      try {\n        if (font.renderer.hasBuiltPath(fontChar)) {\n          return;\n        }\n        handler.send(\"commonobj\", [glyphName, \"FontPath\", font.renderer.getPathJs(fontChar)]);\n      } catch (reason) {\n        if (evaluatorOptions.ignoreErrors) {\n          warn(`buildFontPaths - ignoring ${glyphName} glyph: \"${reason}\".`);\n          return;\n        }\n        throw reason;\n      }\n    }\n    for (const glyph of glyphs) {\n      buildPath(glyph.fontChar);\n      const accent = glyph.accent;\n      if (accent?.fontChar) {\n        buildPath(accent.fontChar);\n      }\n    }\n  }\n  static get fallbackFontDict() {\n    const dict = new Dict();\n    dict.set(\"BaseFont\", Name.get(\"Helvetica\"));\n    dict.set(\"Type\", Name.get(\"FallbackType\"));\n    dict.set(\"Subtype\", Name.get(\"FallbackType\"));\n    dict.set(\"Encoding\", Name.get(\"WinAnsiEncoding\"));\n    return shadow(this, \"fallbackFontDict\", dict);\n  }\n}\nclass TranslatedFont {\n  constructor({\n    loadedName,\n    font,\n    dict,\n    evaluatorOptions\n  }) {\n    this.loadedName = loadedName;\n    this.font = font;\n    this.dict = dict;\n    this._evaluatorOptions = evaluatorOptions || DefaultPartialEvaluatorOptions;\n    this.type3Loaded = null;\n    this.type3Dependencies = font.isType3Font ? new Set() : null;\n    this.sent = false;\n  }\n  send(handler) {\n    if (this.sent) {\n      return;\n    }\n    this.sent = true;\n    handler.send(\"commonobj\", [this.loadedName, \"Font\", this.font.exportData(this._evaluatorOptions.fontExtraProperties)]);\n  }\n  fallback(handler) {\n    if (!this.font.data) {\n      return;\n    }\n    this.font.disableFontFace = true;\n    PartialEvaluator.buildFontPaths(this.font, this.font.glyphCacheValues, handler, this._evaluatorOptions);\n  }\n  loadType3Data(evaluator, resources, task) {\n    if (this.type3Loaded) {\n      return this.type3Loaded;\n    }\n    if (!this.font.isType3Font) {\n      throw new Error(\"Must be a Type3 font.\");\n    }\n    const type3Evaluator = evaluator.clone({\n      ignoreErrors: false\n    });\n    type3Evaluator.parsingType3Font = true;\n    const type3FontRefs = new RefSet(evaluator.type3FontRefs);\n    if (this.dict.objId && !type3FontRefs.has(this.dict.objId)) {\n      type3FontRefs.put(this.dict.objId);\n    }\n    type3Evaluator.type3FontRefs = type3FontRefs;\n    const translatedFont = this.font,\n      type3Dependencies = this.type3Dependencies;\n    let loadCharProcsPromise = Promise.resolve();\n    const charProcs = this.dict.get(\"CharProcs\");\n    const fontResources = this.dict.get(\"Resources\") || resources;\n    const charProcOperatorList = Object.create(null);\n    const fontBBox = Util.normalizeRect(translatedFont.bbox || [0, 0, 0, 0]),\n      width = fontBBox[2] - fontBBox[0],\n      height = fontBBox[3] - fontBBox[1];\n    const fontBBoxSize = Math.hypot(width, height);\n    for (const key of charProcs.getKeys()) {\n      loadCharProcsPromise = loadCharProcsPromise.then(() => {\n        const glyphStream = charProcs.get(key);\n        const operatorList = new OperatorList();\n        return type3Evaluator.getOperatorList({\n          stream: glyphStream,\n          task,\n          resources: fontResources,\n          operatorList\n        }).then(() => {\n          if (operatorList.fnArray[0] === OPS.setCharWidthAndBounds) {\n            this._removeType3ColorOperators(operatorList, fontBBoxSize);\n          }\n          charProcOperatorList[key] = operatorList.getIR();\n          for (const dependency of operatorList.dependencies) {\n            type3Dependencies.add(dependency);\n          }\n        }).catch(function (reason) {\n          warn(`Type3 font resource \"${key}\" is not available.`);\n          const dummyOperatorList = new OperatorList();\n          charProcOperatorList[key] = dummyOperatorList.getIR();\n        });\n      });\n    }\n    this.type3Loaded = loadCharProcsPromise.then(() => {\n      translatedFont.charProcOperatorList = charProcOperatorList;\n      if (this._bbox) {\n        translatedFont.isCharBBox = true;\n        translatedFont.bbox = this._bbox;\n      }\n    });\n    return this.type3Loaded;\n  }\n  _removeType3ColorOperators(operatorList, fontBBoxSize = NaN) {\n    const charBBox = Util.normalizeRect(operatorList.argsArray[0].slice(2)),\n      width = charBBox[2] - charBBox[0],\n      height = charBBox[3] - charBBox[1];\n    const charBBoxSize = Math.hypot(width, height);\n    if (width === 0 || height === 0) {\n      operatorList.fnArray.splice(0, 1);\n      operatorList.argsArray.splice(0, 1);\n    } else if (fontBBoxSize === 0 || Math.round(charBBoxSize / fontBBoxSize) >= 10) {\n      if (!this._bbox) {\n        this._bbox = [Infinity, Infinity, -Infinity, -Infinity];\n      }\n      this._bbox[0] = Math.min(this._bbox[0], charBBox[0]);\n      this._bbox[1] = Math.min(this._bbox[1], charBBox[1]);\n      this._bbox[2] = Math.max(this._bbox[2], charBBox[2]);\n      this._bbox[3] = Math.max(this._bbox[3], charBBox[3]);\n    }\n    let i = 0,\n      ii = operatorList.length;\n    while (i < ii) {\n      switch (operatorList.fnArray[i]) {\n        case OPS.setCharWidthAndBounds:\n          break;\n        case OPS.setStrokeColorSpace:\n        case OPS.setFillColorSpace:\n        case OPS.setStrokeColor:\n        case OPS.setStrokeColorN:\n        case OPS.setFillColor:\n        case OPS.setFillColorN:\n        case OPS.setStrokeGray:\n        case OPS.setFillGray:\n        case OPS.setStrokeRGBColor:\n        case OPS.setFillRGBColor:\n        case OPS.setStrokeCMYKColor:\n        case OPS.setFillCMYKColor:\n        case OPS.shadingFill:\n        case OPS.setRenderingIntent:\n          operatorList.fnArray.splice(i, 1);\n          operatorList.argsArray.splice(i, 1);\n          ii--;\n          continue;\n        case OPS.setGState:\n          const [gStateObj] = operatorList.argsArray[i];\n          let j = 0,\n            jj = gStateObj.length;\n          while (j < jj) {\n            const [gStateKey] = gStateObj[j];\n            switch (gStateKey) {\n              case \"TR\":\n              case \"TR2\":\n              case \"HT\":\n              case \"BG\":\n              case \"BG2\":\n              case \"UCR\":\n              case \"UCR2\":\n                gStateObj.splice(j, 1);\n                jj--;\n                continue;\n            }\n            j++;\n          }\n          break;\n      }\n      i++;\n    }\n  }\n}\nclass StateManager {\n  constructor(initialState = new EvalState()) {\n    this.state = initialState;\n    this.stateStack = [];\n  }\n  save() {\n    const old = this.state;\n    this.stateStack.push(this.state);\n    this.state = old.clone();\n  }\n  restore() {\n    const prev = this.stateStack.pop();\n    if (prev) {\n      this.state = prev;\n    }\n  }\n  transform(args) {\n    this.state.ctm = Util.transform(this.state.ctm, args);\n  }\n}\nclass TextState {\n  constructor() {\n    this.ctm = new Float32Array(IDENTITY_MATRIX);\n    this.fontName = null;\n    this.fontSize = 0;\n    this.loadedName = null;\n    this.font = null;\n    this.fontMatrix = FONT_IDENTITY_MATRIX;\n    this.textMatrix = IDENTITY_MATRIX.slice();\n    this.textLineMatrix = IDENTITY_MATRIX.slice();\n    this.charSpacing = 0;\n    this.wordSpacing = 0;\n    this.leading = 0;\n    this.textHScale = 1;\n    this.textRise = 0;\n  }\n  setTextMatrix(a, b, c, d, e, f) {\n    const m = this.textMatrix;\n    m[0] = a;\n    m[1] = b;\n    m[2] = c;\n    m[3] = d;\n    m[4] = e;\n    m[5] = f;\n  }\n  setTextLineMatrix(a, b, c, d, e, f) {\n    const m = this.textLineMatrix;\n    m[0] = a;\n    m[1] = b;\n    m[2] = c;\n    m[3] = d;\n    m[4] = e;\n    m[5] = f;\n  }\n  translateTextMatrix(x, y) {\n    const m = this.textMatrix;\n    m[4] = m[0] * x + m[2] * y + m[4];\n    m[5] = m[1] * x + m[3] * y + m[5];\n  }\n  translateTextLineMatrix(x, y) {\n    const m = this.textLineMatrix;\n    m[4] = m[0] * x + m[2] * y + m[4];\n    m[5] = m[1] * x + m[3] * y + m[5];\n  }\n  carriageReturn() {\n    this.translateTextLineMatrix(0, -this.leading);\n    this.textMatrix = this.textLineMatrix.slice();\n  }\n  clone() {\n    const clone = Object.create(this);\n    clone.textMatrix = this.textMatrix.slice();\n    clone.textLineMatrix = this.textLineMatrix.slice();\n    clone.fontMatrix = this.fontMatrix.slice();\n    return clone;\n  }\n}\nclass EvalState {\n  constructor() {\n    this.ctm = new Float32Array(IDENTITY_MATRIX);\n    this.font = null;\n    this.textRenderingMode = TextRenderingMode.FILL;\n    this.fillColorSpace = ColorSpace.singletons.gray;\n    this.strokeColorSpace = ColorSpace.singletons.gray;\n  }\n  clone() {\n    return Object.create(this);\n  }\n}\nclass EvaluatorPreprocessor {\n  static get opMap() {\n    return shadow(this, \"opMap\", {\n      w: {\n        id: OPS.setLineWidth,\n        numArgs: 1,\n        variableArgs: false\n      },\n      J: {\n        id: OPS.setLineCap,\n        numArgs: 1,\n        variableArgs: false\n      },\n      j: {\n        id: OPS.setLineJoin,\n        numArgs: 1,\n        variableArgs: false\n      },\n      M: {\n        id: OPS.setMiterLimit,\n        numArgs: 1,\n        variableArgs: false\n      },\n      d: {\n        id: OPS.setDash,\n        numArgs: 2,\n        variableArgs: false\n      },\n      ri: {\n        id: OPS.setRenderingIntent,\n        numArgs: 1,\n        variableArgs: false\n      },\n      i: {\n        id: OPS.setFlatness,\n        numArgs: 1,\n        variableArgs: false\n      },\n      gs: {\n        id: OPS.setGState,\n        numArgs: 1,\n        variableArgs: false\n      },\n      q: {\n        id: OPS.save,\n        numArgs: 0,\n        variableArgs: false\n      },\n      Q: {\n        id: OPS.restore,\n        numArgs: 0,\n        variableArgs: false\n      },\n      cm: {\n        id: OPS.transform,\n        numArgs: 6,\n        variableArgs: false\n      },\n      m: {\n        id: OPS.moveTo,\n        numArgs: 2,\n        variableArgs: false\n      },\n      l: {\n        id: OPS.lineTo,\n        numArgs: 2,\n        variableArgs: false\n      },\n      c: {\n        id: OPS.curveTo,\n        numArgs: 6,\n        variableArgs: false\n      },\n      v: {\n        id: OPS.curveTo2,\n        numArgs: 4,\n        variableArgs: false\n      },\n      y: {\n        id: OPS.curveTo3,\n        numArgs: 4,\n        variableArgs: false\n      },\n      h: {\n        id: OPS.closePath,\n        numArgs: 0,\n        variableArgs: false\n      },\n      re: {\n        id: OPS.rectangle,\n        numArgs: 4,\n        variableArgs: false\n      },\n      S: {\n        id: OPS.stroke,\n        numArgs: 0,\n        variableArgs: false\n      },\n      s: {\n        id: OPS.closeStroke,\n        numArgs: 0,\n        variableArgs: false\n      },\n      f: {\n        id: OPS.fill,\n        numArgs: 0,\n        variableArgs: false\n      },\n      F: {\n        id: OPS.fill,\n        numArgs: 0,\n        variableArgs: false\n      },\n      \"f*\": {\n        id: OPS.eoFill,\n        numArgs: 0,\n        variableArgs: false\n      },\n      B: {\n        id: OPS.fillStroke,\n        numArgs: 0,\n        variableArgs: false\n      },\n      \"B*\": {\n        id: OPS.eoFillStroke,\n        numArgs: 0,\n        variableArgs: false\n      },\n      b: {\n        id: OPS.closeFillStroke,\n        numArgs: 0,\n        variableArgs: false\n      },\n      \"b*\": {\n        id: OPS.closeEOFillStroke,\n        numArgs: 0,\n        variableArgs: false\n      },\n      n: {\n        id: OPS.endPath,\n        numArgs: 0,\n        variableArgs: false\n      },\n      W: {\n        id: OPS.clip,\n        numArgs: 0,\n        variableArgs: false\n      },\n      \"W*\": {\n        id: OPS.eoClip,\n        numArgs: 0,\n        variableArgs: false\n      },\n      BT: {\n        id: OPS.beginText,\n        numArgs: 0,\n        variableArgs: false\n      },\n      ET: {\n        id: OPS.endText,\n        numArgs: 0,\n        variableArgs: false\n      },\n      Tc: {\n        id: OPS.setCharSpacing,\n        numArgs: 1,\n        variableArgs: false\n      },\n      Tw: {\n        id: OPS.setWordSpacing,\n        numArgs: 1,\n        variableArgs: false\n      },\n      Tz: {\n        id: OPS.setHScale,\n        numArgs: 1,\n        variableArgs: false\n      },\n      TL: {\n        id: OPS.setLeading,\n        numArgs: 1,\n        variableArgs: false\n      },\n      Tf: {\n        id: OPS.setFont,\n        numArgs: 2,\n        variableArgs: false\n      },\n      Tr: {\n        id: OPS.setTextRenderingMode,\n        numArgs: 1,\n        variableArgs: false\n      },\n      Ts: {\n        id: OPS.setTextRise,\n        numArgs: 1,\n        variableArgs: false\n      },\n      Td: {\n        id: OPS.moveText,\n        numArgs: 2,\n        variableArgs: false\n      },\n      TD: {\n        id: OPS.setLeadingMoveText,\n        numArgs: 2,\n        variableArgs: false\n      },\n      Tm: {\n        id: OPS.setTextMatrix,\n        numArgs: 6,\n        variableArgs: false\n      },\n      \"T*\": {\n        id: OPS.nextLine,\n        numArgs: 0,\n        variableArgs: false\n      },\n      Tj: {\n        id: OPS.showText,\n        numArgs: 1,\n        variableArgs: false\n      },\n      TJ: {\n        id: OPS.showSpacedText,\n        numArgs: 1,\n        variableArgs: false\n      },\n      \"'\": {\n        id: OPS.nextLineShowText,\n        numArgs: 1,\n        variableArgs: false\n      },\n      '\"': {\n        id: OPS.nextLineSetSpacingShowText,\n        numArgs: 3,\n        variableArgs: false\n      },\n      d0: {\n        id: OPS.setCharWidth,\n        numArgs: 2,\n        variableArgs: false\n      },\n      d1: {\n        id: OPS.setCharWidthAndBounds,\n        numArgs: 6,\n        variableArgs: false\n      },\n      CS: {\n        id: OPS.setStrokeColorSpace,\n        numArgs: 1,\n        variableArgs: false\n      },\n      cs: {\n        id: OPS.setFillColorSpace,\n        numArgs: 1,\n        variableArgs: false\n      },\n      SC: {\n        id: OPS.setStrokeColor,\n        numArgs: 4,\n        variableArgs: true\n      },\n      SCN: {\n        id: OPS.setStrokeColorN,\n        numArgs: 33,\n        variableArgs: true\n      },\n      sc: {\n        id: OPS.setFillColor,\n        numArgs: 4,\n        variableArgs: true\n      },\n      scn: {\n        id: OPS.setFillColorN,\n        numArgs: 33,\n        variableArgs: true\n      },\n      G: {\n        id: OPS.setStrokeGray,\n        numArgs: 1,\n        variableArgs: false\n      },\n      g: {\n        id: OPS.setFillGray,\n        numArgs: 1,\n        variableArgs: false\n      },\n      RG: {\n        id: OPS.setStrokeRGBColor,\n        numArgs: 3,\n        variableArgs: false\n      },\n      rg: {\n        id: OPS.setFillRGBColor,\n        numArgs: 3,\n        variableArgs: false\n      },\n      K: {\n        id: OPS.setStrokeCMYKColor,\n        numArgs: 4,\n        variableArgs: false\n      },\n      k: {\n        id: OPS.setFillCMYKColor,\n        numArgs: 4,\n        variableArgs: false\n      },\n      sh: {\n        id: OPS.shadingFill,\n        numArgs: 1,\n        variableArgs: false\n      },\n      BI: {\n        id: OPS.beginInlineImage,\n        numArgs: 0,\n        variableArgs: false\n      },\n      ID: {\n        id: OPS.beginImageData,\n        numArgs: 0,\n        variableArgs: false\n      },\n      EI: {\n        id: OPS.endInlineImage,\n        numArgs: 1,\n        variableArgs: false\n      },\n      Do: {\n        id: OPS.paintXObject,\n        numArgs: 1,\n        variableArgs: false\n      },\n      MP: {\n        id: OPS.markPoint,\n        numArgs: 1,\n        variableArgs: false\n      },\n      DP: {\n        id: OPS.markPointProps,\n        numArgs: 2,\n        variableArgs: false\n      },\n      BMC: {\n        id: OPS.beginMarkedContent,\n        numArgs: 1,\n        variableArgs: false\n      },\n      BDC: {\n        id: OPS.beginMarkedContentProps,\n        numArgs: 2,\n        variableArgs: false\n      },\n      EMC: {\n        id: OPS.endMarkedContent,\n        numArgs: 0,\n        variableArgs: false\n      },\n      BX: {\n        id: OPS.beginCompat,\n        numArgs: 0,\n        variableArgs: false\n      },\n      EX: {\n        id: OPS.endCompat,\n        numArgs: 0,\n        variableArgs: false\n      },\n      BM: null,\n      BD: null,\n      true: null,\n      fa: null,\n      fal: null,\n      fals: null,\n      false: null,\n      nu: null,\n      nul: null,\n      null: null\n    });\n  }\n  static MAX_INVALID_PATH_OPS = 10;\n  constructor(stream, xref, stateManager = new StateManager()) {\n    this.parser = new Parser({\n      lexer: new Lexer(stream, EvaluatorPreprocessor.opMap),\n      xref\n    });\n    this.stateManager = stateManager;\n    this.nonProcessedArgs = [];\n    this._isPathOp = false;\n    this._numInvalidPathOPS = 0;\n  }\n  get savedStatesDepth() {\n    return this.stateManager.stateStack.length;\n  }\n  read(operation) {\n    let args = operation.args;\n    while (true) {\n      const obj = this.parser.getObj();\n      if (obj instanceof Cmd) {\n        const cmd = obj.cmd;\n        const opSpec = EvaluatorPreprocessor.opMap[cmd];\n        if (!opSpec) {\n          warn(`Unknown command \"${cmd}\".`);\n          continue;\n        }\n        const fn = opSpec.id;\n        const numArgs = opSpec.numArgs;\n        let argsLength = args !== null ? args.length : 0;\n        if (!this._isPathOp) {\n          this._numInvalidPathOPS = 0;\n        }\n        this._isPathOp = fn >= OPS.moveTo && fn <= OPS.endPath;\n        if (!opSpec.variableArgs) {\n          if (argsLength !== numArgs) {\n            const nonProcessedArgs = this.nonProcessedArgs;\n            while (argsLength > numArgs) {\n              nonProcessedArgs.push(args.shift());\n              argsLength--;\n            }\n            while (argsLength < numArgs && nonProcessedArgs.length !== 0) {\n              if (args === null) {\n                args = [];\n              }\n              args.unshift(nonProcessedArgs.pop());\n              argsLength++;\n            }\n          }\n          if (argsLength < numArgs) {\n            const partialMsg = `command ${cmd}: expected ${numArgs} args, ` + `but received ${argsLength} args.`;\n            if (this._isPathOp && ++this._numInvalidPathOPS > EvaluatorPreprocessor.MAX_INVALID_PATH_OPS) {\n              throw new FormatError(`Invalid ${partialMsg}`);\n            }\n            warn(`Skipping ${partialMsg}`);\n            if (args !== null) {\n              args.length = 0;\n            }\n            continue;\n          }\n        } else if (argsLength > numArgs) {\n          info(`Command ${cmd}: expected [0, ${numArgs}] args, ` + `but received ${argsLength} args.`);\n        }\n        this.preprocessCommand(fn, args);\n        operation.fn = fn;\n        operation.args = args;\n        return true;\n      }\n      if (obj === EOF) {\n        return false;\n      }\n      if (obj !== null) {\n        if (args === null) {\n          args = [];\n        }\n        args.push(obj);\n        if (args.length > 33) {\n          throw new FormatError(\"Too many arguments\");\n        }\n      }\n    }\n  }\n  preprocessCommand(fn, args) {\n    switch (fn | 0) {\n      case OPS.save:\n        this.stateManager.save();\n        break;\n      case OPS.restore:\n        this.stateManager.restore();\n        break;\n      case OPS.transform:\n        this.stateManager.transform(args);\n        break;\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/default_appearance.js\n\n\n\n\n\n\n\n\nclass DefaultAppearanceEvaluator extends EvaluatorPreprocessor {\n  constructor(str) {\n    super(new StringStream(str));\n  }\n  parse() {\n    const operation = {\n      fn: 0,\n      args: []\n    };\n    const result = {\n      fontSize: 0,\n      fontName: \"\",\n      fontColor: new Uint8ClampedArray(3)\n    };\n    try {\n      while (true) {\n        operation.args.length = 0;\n        if (!this.read(operation)) {\n          break;\n        }\n        if (this.savedStatesDepth !== 0) {\n          continue;\n        }\n        const {\n          fn,\n          args\n        } = operation;\n        switch (fn | 0) {\n          case OPS.setFont:\n            const [fontName, fontSize] = args;\n            if (fontName instanceof Name) {\n              result.fontName = fontName.name;\n            }\n            if (typeof fontSize === \"number\" && fontSize > 0) {\n              result.fontSize = fontSize;\n            }\n            break;\n          case OPS.setFillRGBColor:\n            ColorSpace.singletons.rgb.getRgbItem(args, 0, result.fontColor, 0);\n            break;\n          case OPS.setFillGray:\n            ColorSpace.singletons.gray.getRgbItem(args, 0, result.fontColor, 0);\n            break;\n          case OPS.setFillCMYKColor:\n            ColorSpace.singletons.cmyk.getRgbItem(args, 0, result.fontColor, 0);\n            break;\n        }\n      }\n    } catch (reason) {\n      warn(`parseDefaultAppearance - ignoring errors: \"${reason}\".`);\n    }\n    return result;\n  }\n}\nfunction parseDefaultAppearance(str) {\n  return new DefaultAppearanceEvaluator(str).parse();\n}\nclass AppearanceStreamEvaluator extends EvaluatorPreprocessor {\n  constructor(stream, evaluatorOptions, xref) {\n    super(stream);\n    this.stream = stream;\n    this.evaluatorOptions = evaluatorOptions;\n    this.xref = xref;\n    this.resources = stream.dict?.get(\"Resources\");\n  }\n  parse() {\n    const operation = {\n      fn: 0,\n      args: []\n    };\n    let result = {\n      scaleFactor: 1,\n      fontSize: 0,\n      fontName: \"\",\n      fontColor: new Uint8ClampedArray(3),\n      fillColorSpace: ColorSpace.singletons.gray\n    };\n    let breakLoop = false;\n    const stack = [];\n    try {\n      while (true) {\n        operation.args.length = 0;\n        if (breakLoop || !this.read(operation)) {\n          break;\n        }\n        const {\n          fn,\n          args\n        } = operation;\n        switch (fn | 0) {\n          case OPS.save:\n            stack.push({\n              scaleFactor: result.scaleFactor,\n              fontSize: result.fontSize,\n              fontName: result.fontName,\n              fontColor: result.fontColor.slice(),\n              fillColorSpace: result.fillColorSpace\n            });\n            break;\n          case OPS.restore:\n            result = stack.pop() || result;\n            break;\n          case OPS.setTextMatrix:\n            result.scaleFactor *= Math.hypot(args[0], args[1]);\n            break;\n          case OPS.setFont:\n            const [fontName, fontSize] = args;\n            if (fontName instanceof Name) {\n              result.fontName = fontName.name;\n            }\n            if (typeof fontSize === \"number\" && fontSize > 0) {\n              result.fontSize = fontSize * result.scaleFactor;\n            }\n            break;\n          case OPS.setFillColorSpace:\n            result.fillColorSpace = ColorSpace.parse({\n              cs: args[0],\n              xref: this.xref,\n              resources: this.resources,\n              pdfFunctionFactory: this._pdfFunctionFactory,\n              localColorSpaceCache: this._localColorSpaceCache\n            });\n            break;\n          case OPS.setFillColor:\n            const cs = result.fillColorSpace;\n            cs.getRgbItem(args, 0, result.fontColor, 0);\n            break;\n          case OPS.setFillRGBColor:\n            ColorSpace.singletons.rgb.getRgbItem(args, 0, result.fontColor, 0);\n            break;\n          case OPS.setFillGray:\n            ColorSpace.singletons.gray.getRgbItem(args, 0, result.fontColor, 0);\n            break;\n          case OPS.setFillCMYKColor:\n            ColorSpace.singletons.cmyk.getRgbItem(args, 0, result.fontColor, 0);\n            break;\n          case OPS.showText:\n          case OPS.showSpacedText:\n          case OPS.nextLineShowText:\n          case OPS.nextLineSetSpacingShowText:\n            breakLoop = true;\n            break;\n        }\n      }\n    } catch (reason) {\n      warn(`parseAppearanceStream - ignoring errors: \"${reason}\".`);\n    }\n    this.stream.reset();\n    delete result.scaleFactor;\n    delete result.fillColorSpace;\n    return result;\n  }\n  get _localColorSpaceCache() {\n    return shadow(this, \"_localColorSpaceCache\", new LocalColorSpaceCache());\n  }\n  get _pdfFunctionFactory() {\n    const pdfFunctionFactory = new PDFFunctionFactory({\n      xref: this.xref,\n      isEvalSupported: this.evaluatorOptions.isEvalSupported\n    });\n    return shadow(this, \"_pdfFunctionFactory\", pdfFunctionFactory);\n  }\n}\nfunction parseAppearanceStream(stream, evaluatorOptions, xref) {\n  return new AppearanceStreamEvaluator(stream, evaluatorOptions, xref).parse();\n}\nfunction getPdfColor(color, isFill) {\n  if (color[0] === color[1] && color[1] === color[2]) {\n    const gray = color[0] / 255;\n    return `${numberToString(gray)} ${isFill ? \"g\" : \"G\"}`;\n  }\n  return Array.from(color, c => numberToString(c / 255)).join(\" \") + ` ${isFill ? \"rg\" : \"RG\"}`;\n}\nfunction createDefaultAppearance({\n  fontSize,\n  fontName,\n  fontColor\n}) {\n  return `/${escapePDFName(fontName)} ${fontSize} Tf ${getPdfColor(fontColor, true)}`;\n}\nclass FakeUnicodeFont {\n  constructor(xref, fontFamily) {\n    this.xref = xref;\n    this.widths = null;\n    this.firstChar = Infinity;\n    this.lastChar = -Infinity;\n    this.fontFamily = fontFamily;\n    const canvas = new OffscreenCanvas(1, 1);\n    this.ctxMeasure = canvas.getContext(\"2d\");\n    if (!FakeUnicodeFont._fontNameId) {\n      FakeUnicodeFont._fontNameId = 1;\n    }\n    this.fontName = Name.get(`InvalidPDFjsFont_${fontFamily}_${FakeUnicodeFont._fontNameId++}`);\n  }\n  get toUnicodeRef() {\n    if (!FakeUnicodeFont._toUnicodeRef) {\n      const toUnicode = `/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo\n<< /Registry (Adobe)\n/Ordering (UCS) /Supplement 0 >> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<0000> <FFFF>\nendcodespacerange\n1 beginbfrange\n<0000> <FFFF> <0000>\nendbfrange\nendcmap CMapName currentdict /CMap defineresource pop end end`;\n      const toUnicodeStream = FakeUnicodeFont.toUnicodeStream = new StringStream(toUnicode);\n      const toUnicodeDict = new Dict(this.xref);\n      toUnicodeStream.dict = toUnicodeDict;\n      toUnicodeDict.set(\"Length\", toUnicode.length);\n      FakeUnicodeFont._toUnicodeRef = this.xref.getNewPersistentRef(toUnicodeStream);\n    }\n    return FakeUnicodeFont._toUnicodeRef;\n  }\n  get fontDescriptorRef() {\n    if (!FakeUnicodeFont._fontDescriptorRef) {\n      const fontDescriptor = new Dict(this.xref);\n      fontDescriptor.set(\"Type\", Name.get(\"FontDescriptor\"));\n      fontDescriptor.set(\"FontName\", this.fontName);\n      fontDescriptor.set(\"FontFamily\", \"MyriadPro Regular\");\n      fontDescriptor.set(\"FontBBox\", [0, 0, 0, 0]);\n      fontDescriptor.set(\"FontStretch\", Name.get(\"Normal\"));\n      fontDescriptor.set(\"FontWeight\", 400);\n      fontDescriptor.set(\"ItalicAngle\", 0);\n      FakeUnicodeFont._fontDescriptorRef = this.xref.getNewPersistentRef(fontDescriptor);\n    }\n    return FakeUnicodeFont._fontDescriptorRef;\n  }\n  get descendantFontRef() {\n    const descendantFont = new Dict(this.xref);\n    descendantFont.set(\"BaseFont\", this.fontName);\n    descendantFont.set(\"Type\", Name.get(\"Font\"));\n    descendantFont.set(\"Subtype\", Name.get(\"CIDFontType0\"));\n    descendantFont.set(\"CIDToGIDMap\", Name.get(\"Identity\"));\n    descendantFont.set(\"FirstChar\", this.firstChar);\n    descendantFont.set(\"LastChar\", this.lastChar);\n    descendantFont.set(\"FontDescriptor\", this.fontDescriptorRef);\n    descendantFont.set(\"DW\", 1000);\n    const widths = [];\n    const chars = [...this.widths.entries()].sort();\n    let currentChar = null;\n    let currentWidths = null;\n    for (const [char, width] of chars) {\n      if (!currentChar) {\n        currentChar = char;\n        currentWidths = [width];\n        continue;\n      }\n      if (char === currentChar + currentWidths.length) {\n        currentWidths.push(width);\n      } else {\n        widths.push(currentChar, currentWidths);\n        currentChar = char;\n        currentWidths = [width];\n      }\n    }\n    if (currentChar) {\n      widths.push(currentChar, currentWidths);\n    }\n    descendantFont.set(\"W\", widths);\n    const cidSystemInfo = new Dict(this.xref);\n    cidSystemInfo.set(\"Ordering\", \"Identity\");\n    cidSystemInfo.set(\"Registry\", \"Adobe\");\n    cidSystemInfo.set(\"Supplement\", 0);\n    descendantFont.set(\"CIDSystemInfo\", cidSystemInfo);\n    return this.xref.getNewPersistentRef(descendantFont);\n  }\n  get baseFontRef() {\n    const baseFont = new Dict(this.xref);\n    baseFont.set(\"BaseFont\", this.fontName);\n    baseFont.set(\"Type\", Name.get(\"Font\"));\n    baseFont.set(\"Subtype\", Name.get(\"Type0\"));\n    baseFont.set(\"Encoding\", Name.get(\"Identity-H\"));\n    baseFont.set(\"DescendantFonts\", [this.descendantFontRef]);\n    baseFont.set(\"ToUnicode\", this.toUnicodeRef);\n    return this.xref.getNewPersistentRef(baseFont);\n  }\n  get resources() {\n    const resources = new Dict(this.xref);\n    const font = new Dict(this.xref);\n    font.set(this.fontName.name, this.baseFontRef);\n    resources.set(\"Font\", font);\n    return resources;\n  }\n  _createContext() {\n    this.widths = new Map();\n    this.ctxMeasure.font = `1000px ${this.fontFamily}`;\n    return this.ctxMeasure;\n  }\n  createFontResources(text) {\n    const ctx = this._createContext();\n    for (const line of text.split(/\\r\\n?|\\n/)) {\n      for (const char of line.split(\"\")) {\n        const code = char.charCodeAt(0);\n        if (this.widths.has(code)) {\n          continue;\n        }\n        const metrics = ctx.measureText(char);\n        const width = Math.ceil(metrics.width);\n        this.widths.set(code, width);\n        this.firstChar = Math.min(code, this.firstChar);\n        this.lastChar = Math.max(code, this.lastChar);\n      }\n    }\n    return this.resources;\n  }\n  createAppearance(text, rect, rotation, fontSize, bgColor, strokeAlpha) {\n    const ctx = this._createContext();\n    const lines = [];\n    let maxWidth = -Infinity;\n    for (const line of text.split(/\\r\\n?|\\n/)) {\n      lines.push(line);\n      const lineWidth = ctx.measureText(line).width;\n      maxWidth = Math.max(maxWidth, lineWidth);\n      for (const char of line.split(\"\")) {\n        const code = char.charCodeAt(0);\n        let width = this.widths.get(code);\n        if (width === undefined) {\n          const metrics = ctx.measureText(char);\n          width = Math.ceil(metrics.width);\n          this.widths.set(code, width);\n          this.firstChar = Math.min(code, this.firstChar);\n          this.lastChar = Math.max(code, this.lastChar);\n        }\n      }\n    }\n    maxWidth *= fontSize / 1000;\n    const [x1, y1, x2, y2] = rect;\n    let w = x2 - x1;\n    let h = y2 - y1;\n    if (rotation % 180 !== 0) {\n      [w, h] = [h, w];\n    }\n    let hscale = 1;\n    if (maxWidth > w) {\n      hscale = w / maxWidth;\n    }\n    let vscale = 1;\n    const lineHeight = LINE_FACTOR * fontSize;\n    const lineDescent = LINE_DESCENT_FACTOR * fontSize;\n    const maxHeight = lineHeight * lines.length;\n    if (maxHeight > h) {\n      vscale = h / maxHeight;\n    }\n    const fscale = Math.min(hscale, vscale);\n    const newFontSize = fontSize * fscale;\n    const buffer = [\"q\", `0 0 ${numberToString(w)} ${numberToString(h)} re W n`, `BT`, `1 0 0 1 0 ${numberToString(h + lineDescent)} Tm 0 Tc ${getPdfColor(bgColor, true)}`, `/${this.fontName.name} ${numberToString(newFontSize)} Tf`];\n    const {\n      resources\n    } = this;\n    strokeAlpha = typeof strokeAlpha === \"number\" && strokeAlpha >= 0 && strokeAlpha <= 1 ? strokeAlpha : 1;\n    if (strokeAlpha !== 1) {\n      buffer.push(\"/R0 gs\");\n      const extGState = new Dict(this.xref);\n      const r0 = new Dict(this.xref);\n      r0.set(\"ca\", strokeAlpha);\n      r0.set(\"CA\", strokeAlpha);\n      r0.set(\"Type\", Name.get(\"ExtGState\"));\n      extGState.set(\"R0\", r0);\n      resources.set(\"ExtGState\", extGState);\n    }\n    const vShift = numberToString(lineHeight);\n    for (const line of lines) {\n      buffer.push(`0 -${vShift} Td <${stringToUTF16HexString(line)}> Tj`);\n    }\n    buffer.push(\"ET\", \"Q\");\n    const appearance = buffer.join(\"\\n\");\n    const appearanceStreamDict = new Dict(this.xref);\n    appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n    appearanceStreamDict.set(\"Type\", Name.get(\"XObject\"));\n    appearanceStreamDict.set(\"BBox\", [0, 0, w, h]);\n    appearanceStreamDict.set(\"Length\", appearance.length);\n    appearanceStreamDict.set(\"Resources\", resources);\n    if (rotation) {\n      const matrix = getRotationMatrix(rotation, w, h);\n      appearanceStreamDict.set(\"Matrix\", matrix);\n    }\n    const ap = new StringStream(appearance);\n    ap.dict = appearanceStreamDict;\n    return ap;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/name_number_tree.js\n\n\nclass NameOrNumberTree {\n  constructor(root, xref, type) {\n    if (this.constructor === NameOrNumberTree) {\n      unreachable(\"Cannot initialize NameOrNumberTree.\");\n    }\n    this.root = root;\n    this.xref = xref;\n    this._type = type;\n  }\n  getAll() {\n    const map = new Map();\n    if (!this.root) {\n      return map;\n    }\n    const xref = this.xref;\n    const processed = new RefSet();\n    processed.put(this.root);\n    const queue = [this.root];\n    while (queue.length > 0) {\n      const obj = xref.fetchIfRef(queue.shift());\n      if (!(obj instanceof Dict)) {\n        continue;\n      }\n      if (obj.has(\"Kids\")) {\n        const kids = obj.get(\"Kids\");\n        if (!Array.isArray(kids)) {\n          continue;\n        }\n        for (const kid of kids) {\n          if (processed.has(kid)) {\n            throw new FormatError(`Duplicate entry in \"${this._type}\" tree.`);\n          }\n          queue.push(kid);\n          processed.put(kid);\n        }\n        continue;\n      }\n      const entries = obj.get(this._type);\n      if (!Array.isArray(entries)) {\n        continue;\n      }\n      for (let i = 0, ii = entries.length; i < ii; i += 2) {\n        map.set(xref.fetchIfRef(entries[i]), xref.fetchIfRef(entries[i + 1]));\n      }\n    }\n    return map;\n  }\n  get(key) {\n    if (!this.root) {\n      return null;\n    }\n    const xref = this.xref;\n    let kidsOrEntries = xref.fetchIfRef(this.root);\n    let loopCount = 0;\n    const MAX_LEVELS = 10;\n    while (kidsOrEntries.has(\"Kids\")) {\n      if (++loopCount > MAX_LEVELS) {\n        warn(`Search depth limit reached for \"${this._type}\" tree.`);\n        return null;\n      }\n      const kids = kidsOrEntries.get(\"Kids\");\n      if (!Array.isArray(kids)) {\n        return null;\n      }\n      let l = 0,\n        r = kids.length - 1;\n      while (l <= r) {\n        const m = l + r >> 1;\n        const kid = xref.fetchIfRef(kids[m]);\n        const limits = kid.get(\"Limits\");\n        if (key < xref.fetchIfRef(limits[0])) {\n          r = m - 1;\n        } else if (key > xref.fetchIfRef(limits[1])) {\n          l = m + 1;\n        } else {\n          kidsOrEntries = kid;\n          break;\n        }\n      }\n      if (l > r) {\n        return null;\n      }\n    }\n    const entries = kidsOrEntries.get(this._type);\n    if (Array.isArray(entries)) {\n      let l = 0,\n        r = entries.length - 2;\n      while (l <= r) {\n        const tmp = l + r >> 1,\n          m = tmp + (tmp & 1);\n        const currentKey = xref.fetchIfRef(entries[m]);\n        if (key < currentKey) {\n          r = m - 2;\n        } else if (key > currentKey) {\n          l = m + 2;\n        } else {\n          return xref.fetchIfRef(entries[m + 1]);\n        }\n      }\n    }\n    return null;\n  }\n}\nclass NameTree extends NameOrNumberTree {\n  constructor(root, xref) {\n    super(root, xref, \"Names\");\n  }\n}\nclass NumberTree extends NameOrNumberTree {\n  constructor(root, xref) {\n    super(root, xref, \"Nums\");\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/cleanup_helper.js\n\n\n\nfunction clearGlobalCaches() {\n  clearPatternCaches();\n  clearPrimitiveCaches();\n  clearUnicodeCaches();\n}\n\n;// CONCATENATED MODULE: ./src/core/file_spec.js\n\n\n\nfunction pickPlatformItem(dict) {\n  if (dict.has(\"UF\")) {\n    return dict.get(\"UF\");\n  } else if (dict.has(\"F\")) {\n    return dict.get(\"F\");\n  } else if (dict.has(\"Unix\")) {\n    return dict.get(\"Unix\");\n  } else if (dict.has(\"Mac\")) {\n    return dict.get(\"Mac\");\n  } else if (dict.has(\"DOS\")) {\n    return dict.get(\"DOS\");\n  }\n  return null;\n}\nclass FileSpec {\n  constructor(root, xref) {\n    if (!(root instanceof Dict)) {\n      return;\n    }\n    this.xref = xref;\n    this.root = root;\n    if (root.has(\"FS\")) {\n      this.fs = root.get(\"FS\");\n    }\n    this.description = root.has(\"Desc\") ? stringToPDFString(root.get(\"Desc\")) : \"\";\n    if (root.has(\"RF\")) {\n      warn(\"Related file specifications are not supported\");\n    }\n    this.contentAvailable = true;\n    if (!root.has(\"EF\")) {\n      this.contentAvailable = false;\n      warn(\"Non-embedded file specifications are not supported\");\n    }\n  }\n  get filename() {\n    if (!this._filename && this.root) {\n      const filename = pickPlatformItem(this.root) || \"unnamed\";\n      this._filename = stringToPDFString(filename).replaceAll(\"\\\\\\\\\", \"\\\\\").replaceAll(\"\\\\/\", \"/\").replaceAll(\"\\\\\", \"/\");\n    }\n    return this._filename;\n  }\n  get content() {\n    if (!this.contentAvailable) {\n      return null;\n    }\n    if (!this.contentRef && this.root) {\n      this.contentRef = pickPlatformItem(this.root.get(\"EF\"));\n    }\n    let content = null;\n    if (this.contentRef) {\n      const fileObj = this.xref.fetchIfRef(this.contentRef);\n      if (fileObj instanceof BaseStream) {\n        content = fileObj.getBytes();\n      } else {\n        warn(\"Embedded file specification points to non-existing/invalid content\");\n      }\n    } else {\n      warn(\"Embedded file specification does not have a content\");\n    }\n    return content;\n  }\n  get serializable() {\n    return {\n      filename: this.filename,\n      content: this.content\n    };\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xml_parser.js\n\nconst XMLParserErrorCode = {\n  NoError: 0,\n  EndOfDocument: -1,\n  UnterminatedCdat: -2,\n  UnterminatedXmlDeclaration: -3,\n  UnterminatedDoctypeDeclaration: -4,\n  UnterminatedComment: -5,\n  MalformedElement: -6,\n  OutOfMemory: -7,\n  UnterminatedAttributeValue: -8,\n  UnterminatedElement: -9,\n  ElementNeverBegun: -10\n};\nfunction isWhitespace(s, index) {\n  const ch = s[index];\n  return ch === \" \" || ch === \"\\n\" || ch === \"\\r\" || ch === \"\\t\";\n}\nfunction isWhitespaceString(s) {\n  for (let i = 0, ii = s.length; i < ii; i++) {\n    if (!isWhitespace(s, i)) {\n      return false;\n    }\n  }\n  return true;\n}\nclass XMLParserBase {\n  _resolveEntities(s) {\n    return s.replaceAll(/&([^;]+);/g, (all, entity) => {\n      if (entity.substring(0, 2) === \"#x\") {\n        return String.fromCodePoint(parseInt(entity.substring(2), 16));\n      } else if (entity.substring(0, 1) === \"#\") {\n        return String.fromCodePoint(parseInt(entity.substring(1), 10));\n      }\n      switch (entity) {\n        case \"lt\":\n          return \"<\";\n        case \"gt\":\n          return \">\";\n        case \"amp\":\n          return \"&\";\n        case \"quot\":\n          return '\"';\n        case \"apos\":\n          return \"'\";\n      }\n      return this.onResolveEntity(entity);\n    });\n  }\n  _parseContent(s, start) {\n    const attributes = [];\n    let pos = start;\n    function skipWs() {\n      while (pos < s.length && isWhitespace(s, pos)) {\n        ++pos;\n      }\n    }\n    while (pos < s.length && !isWhitespace(s, pos) && s[pos] !== \">\" && s[pos] !== \"/\") {\n      ++pos;\n    }\n    const name = s.substring(start, pos);\n    skipWs();\n    while (pos < s.length && s[pos] !== \">\" && s[pos] !== \"/\" && s[pos] !== \"?\") {\n      skipWs();\n      let attrName = \"\",\n        attrValue = \"\";\n      while (pos < s.length && !isWhitespace(s, pos) && s[pos] !== \"=\") {\n        attrName += s[pos];\n        ++pos;\n      }\n      skipWs();\n      if (s[pos] !== \"=\") {\n        return null;\n      }\n      ++pos;\n      skipWs();\n      const attrEndChar = s[pos];\n      if (attrEndChar !== '\"' && attrEndChar !== \"'\") {\n        return null;\n      }\n      const attrEndIndex = s.indexOf(attrEndChar, ++pos);\n      if (attrEndIndex < 0) {\n        return null;\n      }\n      attrValue = s.substring(pos, attrEndIndex);\n      attributes.push({\n        name: attrName,\n        value: this._resolveEntities(attrValue)\n      });\n      pos = attrEndIndex + 1;\n      skipWs();\n    }\n    return {\n      name,\n      attributes,\n      parsed: pos - start\n    };\n  }\n  _parseProcessingInstruction(s, start) {\n    let pos = start;\n    function skipWs() {\n      while (pos < s.length && isWhitespace(s, pos)) {\n        ++pos;\n      }\n    }\n    while (pos < s.length && !isWhitespace(s, pos) && s[pos] !== \">\" && s[pos] !== \"?\" && s[pos] !== \"/\") {\n      ++pos;\n    }\n    const name = s.substring(start, pos);\n    skipWs();\n    const attrStart = pos;\n    while (pos < s.length && (s[pos] !== \"?\" || s[pos + 1] !== \">\")) {\n      ++pos;\n    }\n    const value = s.substring(attrStart, pos);\n    return {\n      name,\n      value,\n      parsed: pos - start\n    };\n  }\n  parseXml(s) {\n    let i = 0;\n    while (i < s.length) {\n      const ch = s[i];\n      let j = i;\n      if (ch === \"<\") {\n        ++j;\n        const ch2 = s[j];\n        let q;\n        switch (ch2) {\n          case \"/\":\n            ++j;\n            q = s.indexOf(\">\", j);\n            if (q < 0) {\n              this.onError(XMLParserErrorCode.UnterminatedElement);\n              return;\n            }\n            this.onEndElement(s.substring(j, q));\n            j = q + 1;\n            break;\n          case \"?\":\n            ++j;\n            const pi = this._parseProcessingInstruction(s, j);\n            if (s.substring(j + pi.parsed, j + pi.parsed + 2) !== \"?>\") {\n              this.onError(XMLParserErrorCode.UnterminatedXmlDeclaration);\n              return;\n            }\n            this.onPi(pi.name, pi.value);\n            j += pi.parsed + 2;\n            break;\n          case \"!\":\n            if (s.substring(j + 1, j + 3) === \"--\") {\n              q = s.indexOf(\"-->\", j + 3);\n              if (q < 0) {\n                this.onError(XMLParserErrorCode.UnterminatedComment);\n                return;\n              }\n              this.onComment(s.substring(j + 3, q));\n              j = q + 3;\n            } else if (s.substring(j + 1, j + 8) === \"[CDATA[\") {\n              q = s.indexOf(\"]]>\", j + 8);\n              if (q < 0) {\n                this.onError(XMLParserErrorCode.UnterminatedCdat);\n                return;\n              }\n              this.onCdata(s.substring(j + 8, q));\n              j = q + 3;\n            } else if (s.substring(j + 1, j + 8) === \"DOCTYPE\") {\n              const q2 = s.indexOf(\"[\", j + 8);\n              let complexDoctype = false;\n              q = s.indexOf(\">\", j + 8);\n              if (q < 0) {\n                this.onError(XMLParserErrorCode.UnterminatedDoctypeDeclaration);\n                return;\n              }\n              if (q2 > 0 && q > q2) {\n                q = s.indexOf(\"]>\", j + 8);\n                if (q < 0) {\n                  this.onError(XMLParserErrorCode.UnterminatedDoctypeDeclaration);\n                  return;\n                }\n                complexDoctype = true;\n              }\n              const doctypeContent = s.substring(j + 8, q + (complexDoctype ? 1 : 0));\n              this.onDoctype(doctypeContent);\n              j = q + (complexDoctype ? 2 : 1);\n            } else {\n              this.onError(XMLParserErrorCode.MalformedElement);\n              return;\n            }\n            break;\n          default:\n            const content = this._parseContent(s, j);\n            if (content === null) {\n              this.onError(XMLParserErrorCode.MalformedElement);\n              return;\n            }\n            let isClosed = false;\n            if (s.substring(j + content.parsed, j + content.parsed + 2) === \"/>\") {\n              isClosed = true;\n            } else if (s.substring(j + content.parsed, j + content.parsed + 1) !== \">\") {\n              this.onError(XMLParserErrorCode.UnterminatedElement);\n              return;\n            }\n            this.onBeginElement(content.name, content.attributes, isClosed);\n            j += content.parsed + (isClosed ? 2 : 1);\n            break;\n        }\n      } else {\n        while (j < s.length && s[j] !== \"<\") {\n          j++;\n        }\n        const text = s.substring(i, j);\n        this.onText(this._resolveEntities(text));\n      }\n      i = j;\n    }\n  }\n  onResolveEntity(name) {\n    return `&${name};`;\n  }\n  onPi(name, value) {}\n  onComment(text) {}\n  onCdata(text) {}\n  onDoctype(doctypeContent) {}\n  onText(text) {}\n  onBeginElement(name, attributes, isEmpty) {}\n  onEndElement(name) {}\n  onError(code) {}\n}\nclass SimpleDOMNode {\n  constructor(nodeName, nodeValue) {\n    this.nodeName = nodeName;\n    this.nodeValue = nodeValue;\n    Object.defineProperty(this, \"parentNode\", {\n      value: null,\n      writable: true\n    });\n  }\n  get firstChild() {\n    return this.childNodes?.[0];\n  }\n  get nextSibling() {\n    const childNodes = this.parentNode.childNodes;\n    if (!childNodes) {\n      return undefined;\n    }\n    const index = childNodes.indexOf(this);\n    if (index === -1) {\n      return undefined;\n    }\n    return childNodes[index + 1];\n  }\n  get textContent() {\n    if (!this.childNodes) {\n      return this.nodeValue || \"\";\n    }\n    return this.childNodes.map(function (child) {\n      return child.textContent;\n    }).join(\"\");\n  }\n  get children() {\n    return this.childNodes || [];\n  }\n  hasChildNodes() {\n    return this.childNodes?.length > 0;\n  }\n  searchNode(paths, pos) {\n    if (pos >= paths.length) {\n      return this;\n    }\n    const component = paths[pos];\n    if (component.name.startsWith(\"#\") && pos < paths.length - 1) {\n      return this.searchNode(paths, pos + 1);\n    }\n    const stack = [];\n    let node = this;\n    while (true) {\n      if (component.name === node.nodeName) {\n        if (component.pos === 0) {\n          const res = node.searchNode(paths, pos + 1);\n          if (res !== null) {\n            return res;\n          }\n        } else if (stack.length === 0) {\n          return null;\n        } else {\n          const [parent] = stack.pop();\n          let siblingPos = 0;\n          for (const child of parent.childNodes) {\n            if (component.name === child.nodeName) {\n              if (siblingPos === component.pos) {\n                return child.searchNode(paths, pos + 1);\n              }\n              siblingPos++;\n            }\n          }\n          return node.searchNode(paths, pos + 1);\n        }\n      }\n      if (node.childNodes?.length > 0) {\n        stack.push([node, 0]);\n        node = node.childNodes[0];\n      } else if (stack.length === 0) {\n        return null;\n      } else {\n        while (stack.length !== 0) {\n          const [parent, currentPos] = stack.pop();\n          const newPos = currentPos + 1;\n          if (newPos < parent.childNodes.length) {\n            stack.push([parent, newPos]);\n            node = parent.childNodes[newPos];\n            break;\n          }\n        }\n        if (stack.length === 0) {\n          return null;\n        }\n      }\n    }\n  }\n  dump(buffer) {\n    if (this.nodeName === \"#text\") {\n      buffer.push(encodeToXmlString(this.nodeValue));\n      return;\n    }\n    buffer.push(`<${this.nodeName}`);\n    if (this.attributes) {\n      for (const attribute of this.attributes) {\n        buffer.push(` ${attribute.name}=\"${encodeToXmlString(attribute.value)}\"`);\n      }\n    }\n    if (this.hasChildNodes()) {\n      buffer.push(\">\");\n      for (const child of this.childNodes) {\n        child.dump(buffer);\n      }\n      buffer.push(`</${this.nodeName}>`);\n    } else if (this.nodeValue) {\n      buffer.push(`>${encodeToXmlString(this.nodeValue)}</${this.nodeName}>`);\n    } else {\n      buffer.push(\"/>\");\n    }\n  }\n}\nclass SimpleXMLParser extends XMLParserBase {\n  constructor({\n    hasAttributes = false,\n    lowerCaseName = false\n  }) {\n    super();\n    this._currentFragment = null;\n    this._stack = null;\n    this._errorCode = XMLParserErrorCode.NoError;\n    this._hasAttributes = hasAttributes;\n    this._lowerCaseName = lowerCaseName;\n  }\n  parseFromString(data) {\n    this._currentFragment = [];\n    this._stack = [];\n    this._errorCode = XMLParserErrorCode.NoError;\n    this.parseXml(data);\n    if (this._errorCode !== XMLParserErrorCode.NoError) {\n      return undefined;\n    }\n    const [documentElement] = this._currentFragment;\n    if (!documentElement) {\n      return undefined;\n    }\n    return {\n      documentElement\n    };\n  }\n  onText(text) {\n    if (isWhitespaceString(text)) {\n      return;\n    }\n    const node = new SimpleDOMNode(\"#text\", text);\n    this._currentFragment.push(node);\n  }\n  onCdata(text) {\n    const node = new SimpleDOMNode(\"#text\", text);\n    this._currentFragment.push(node);\n  }\n  onBeginElement(name, attributes, isEmpty) {\n    if (this._lowerCaseName) {\n      name = name.toLowerCase();\n    }\n    const node = new SimpleDOMNode(name);\n    node.childNodes = [];\n    if (this._hasAttributes) {\n      node.attributes = attributes;\n    }\n    this._currentFragment.push(node);\n    if (isEmpty) {\n      return;\n    }\n    this._stack.push(this._currentFragment);\n    this._currentFragment = node.childNodes;\n  }\n  onEndElement(name) {\n    this._currentFragment = this._stack.pop() || [];\n    const lastElement = this._currentFragment.at(-1);\n    if (!lastElement) {\n      return null;\n    }\n    for (const childNode of lastElement.childNodes) {\n      childNode.parentNode = lastElement;\n    }\n    return lastElement;\n  }\n  onError(code) {\n    this._errorCode = code;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/metadata_parser.js\n\nclass MetadataParser {\n  constructor(data) {\n    data = this._repair(data);\n    const parser = new SimpleXMLParser({\n      lowerCaseName: true\n    });\n    const xmlDocument = parser.parseFromString(data);\n    this._metadataMap = new Map();\n    this._data = data;\n    if (xmlDocument) {\n      this._parse(xmlDocument);\n    }\n  }\n  _repair(data) {\n    return data.replace(/^[^<]+/, \"\").replaceAll(/>\\\\376\\\\377([^<]+)/g, function (all, codes) {\n      const bytes = codes.replaceAll(/\\\\([0-3])([0-7])([0-7])/g, function (code, d1, d2, d3) {\n        return String.fromCharCode(d1 * 64 + d2 * 8 + d3 * 1);\n      }).replaceAll(/&(amp|apos|gt|lt|quot);/g, function (str, name) {\n        switch (name) {\n          case \"amp\":\n            return \"&\";\n          case \"apos\":\n            return \"'\";\n          case \"gt\":\n            return \">\";\n          case \"lt\":\n            return \"<\";\n          case \"quot\":\n            return '\"';\n        }\n        throw new Error(`_repair: ${name} isn't defined.`);\n      });\n      const charBuf = [\">\"];\n      for (let i = 0, ii = bytes.length; i < ii; i += 2) {\n        const code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1);\n        if (code >= 32 && code < 127 && code !== 60 && code !== 62 && code !== 38) {\n          charBuf.push(String.fromCharCode(code));\n        } else {\n          charBuf.push(\"&#x\" + (0x10000 + code).toString(16).substring(1) + \";\");\n        }\n      }\n      return charBuf.join(\"\");\n    });\n  }\n  _getSequence(entry) {\n    const name = entry.nodeName;\n    if (name !== \"rdf:bag\" && name !== \"rdf:seq\" && name !== \"rdf:alt\") {\n      return null;\n    }\n    return entry.childNodes.filter(node => node.nodeName === \"rdf:li\");\n  }\n  _parseArray(entry) {\n    if (!entry.hasChildNodes()) {\n      return;\n    }\n    const [seqNode] = entry.childNodes;\n    const sequence = this._getSequence(seqNode) || [];\n    this._metadataMap.set(entry.nodeName, sequence.map(node => node.textContent.trim()));\n  }\n  _parse(xmlDocument) {\n    let rdf = xmlDocument.documentElement;\n    if (rdf.nodeName !== \"rdf:rdf\") {\n      rdf = rdf.firstChild;\n      while (rdf && rdf.nodeName !== \"rdf:rdf\") {\n        rdf = rdf.nextSibling;\n      }\n    }\n    if (!rdf || rdf.nodeName !== \"rdf:rdf\" || !rdf.hasChildNodes()) {\n      return;\n    }\n    for (const desc of rdf.childNodes) {\n      if (desc.nodeName !== \"rdf:description\") {\n        continue;\n      }\n      for (const entry of desc.childNodes) {\n        const name = entry.nodeName;\n        switch (name) {\n          case \"#text\":\n            continue;\n          case \"dc:creator\":\n          case \"dc:subject\":\n            this._parseArray(entry);\n            continue;\n        }\n        this._metadataMap.set(name, entry.textContent.trim());\n      }\n    }\n  }\n  get serializable() {\n    return {\n      parsedData: this._metadataMap,\n      rawData: this._data\n    };\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/decrypt_stream.js\n\nconst chunkSize = 512;\nclass DecryptStream extends DecodeStream {\n  constructor(str, maybeLength, decrypt) {\n    super(maybeLength);\n    this.str = str;\n    this.dict = str.dict;\n    this.decrypt = decrypt;\n    this.nextChunk = null;\n    this.initialized = false;\n  }\n  readBlock() {\n    let chunk;\n    if (this.initialized) {\n      chunk = this.nextChunk;\n    } else {\n      chunk = this.str.getBytes(chunkSize);\n      this.initialized = true;\n    }\n    if (!chunk || chunk.length === 0) {\n      this.eof = true;\n      return;\n    }\n    this.nextChunk = this.str.getBytes(chunkSize);\n    const hasMoreData = this.nextChunk?.length > 0;\n    const decrypt = this.decrypt;\n    chunk = decrypt(chunk, !hasMoreData);\n    const bufferLength = this.bufferLength,\n      newLength = bufferLength + chunk.length,\n      buffer = this.ensureBuffer(newLength);\n    buffer.set(chunk, bufferLength);\n    this.bufferLength = newLength;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/crypto.js\n\n\n\nclass ARCFourCipher {\n  constructor(key) {\n    this.a = 0;\n    this.b = 0;\n    const s = new Uint8Array(256);\n    const keyLength = key.length;\n    for (let i = 0; i < 256; ++i) {\n      s[i] = i;\n    }\n    for (let i = 0, j = 0; i < 256; ++i) {\n      const tmp = s[i];\n      j = j + tmp + key[i % keyLength] & 0xff;\n      s[i] = s[j];\n      s[j] = tmp;\n    }\n    this.s = s;\n  }\n  encryptBlock(data) {\n    let a = this.a,\n      b = this.b;\n    const s = this.s;\n    const n = data.length;\n    const output = new Uint8Array(n);\n    for (let i = 0; i < n; ++i) {\n      a = a + 1 & 0xff;\n      const tmp = s[a];\n      b = b + tmp & 0xff;\n      const tmp2 = s[b];\n      s[a] = tmp2;\n      s[b] = tmp;\n      output[i] = data[i] ^ s[tmp + tmp2 & 0xff];\n    }\n    this.a = a;\n    this.b = b;\n    return output;\n  }\n  decryptBlock(data) {\n    return this.encryptBlock(data);\n  }\n  encrypt(data) {\n    return this.encryptBlock(data);\n  }\n}\nconst calculateMD5 = function calculateMD5Closure() {\n  const r = new Uint8Array([7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]);\n  const k = new Int32Array([-680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426, -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162, 1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632, 643717713, -373897302, -701558691, 38016083, -660478335, -405537848, 568446438, -1019803690, -187363961, 1163531501, -1444681467, -51403784, 1735328473, -1926607734, -378558, -2022574463, 1839030562, -35309556, -1530992060, 1272893353, -155497632, -1094730640, 681279174, -358537222, -722521979, 76029189, -640364487, -421815835, 530742520, -995338651, -198630844, 1126891415, -1416354905, -57434055, 1700485571, -1894986606, -1051523, -2054922799, 1873313359, -30611744, -1560198380, 1309151649, -145523070, -1120210379, 718787259, -343485551]);\n  function hash(data, offset, length) {\n    let h0 = 1732584193,\n      h1 = -271733879,\n      h2 = -1732584194,\n      h3 = 271733878;\n    const paddedLength = length + 72 & ~63;\n    const padded = new Uint8Array(paddedLength);\n    let i, j;\n    for (i = 0; i < length; ++i) {\n      padded[i] = data[offset++];\n    }\n    padded[i++] = 0x80;\n    const n = paddedLength - 8;\n    while (i < n) {\n      padded[i++] = 0;\n    }\n    padded[i++] = length << 3 & 0xff;\n    padded[i++] = length >> 5 & 0xff;\n    padded[i++] = length >> 13 & 0xff;\n    padded[i++] = length >> 21 & 0xff;\n    padded[i++] = length >>> 29 & 0xff;\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = 0;\n    const w = new Int32Array(16);\n    for (i = 0; i < paddedLength;) {\n      for (j = 0; j < 16; ++j, i += 4) {\n        w[j] = padded[i] | padded[i + 1] << 8 | padded[i + 2] << 16 | padded[i + 3] << 24;\n      }\n      let a = h0,\n        b = h1,\n        c = h2,\n        d = h3,\n        f,\n        g;\n      for (j = 0; j < 64; ++j) {\n        if (j < 16) {\n          f = b & c | ~b & d;\n          g = j;\n        } else if (j < 32) {\n          f = d & b | ~d & c;\n          g = 5 * j + 1 & 15;\n        } else if (j < 48) {\n          f = b ^ c ^ d;\n          g = 3 * j + 5 & 15;\n        } else {\n          f = c ^ (b | ~d);\n          g = 7 * j & 15;\n        }\n        const tmp = d,\n          rotateArg = a + f + k[j] + w[g] | 0,\n          rotate = r[j];\n        d = c;\n        c = b;\n        b = b + (rotateArg << rotate | rotateArg >>> 32 - rotate) | 0;\n        a = tmp;\n      }\n      h0 = h0 + a | 0;\n      h1 = h1 + b | 0;\n      h2 = h2 + c | 0;\n      h3 = h3 + d | 0;\n    }\n    return new Uint8Array([h0 & 0xFF, h0 >> 8 & 0xFF, h0 >> 16 & 0xFF, h0 >>> 24 & 0xFF, h1 & 0xFF, h1 >> 8 & 0xFF, h1 >> 16 & 0xFF, h1 >>> 24 & 0xFF, h2 & 0xFF, h2 >> 8 & 0xFF, h2 >> 16 & 0xFF, h2 >>> 24 & 0xFF, h3 & 0xFF, h3 >> 8 & 0xFF, h3 >> 16 & 0xFF, h3 >>> 24 & 0xFF]);\n  }\n  return hash;\n}();\nclass Word64 {\n  constructor(highInteger, lowInteger) {\n    this.high = highInteger | 0;\n    this.low = lowInteger | 0;\n  }\n  and(word) {\n    this.high &= word.high;\n    this.low &= word.low;\n  }\n  xor(word) {\n    this.high ^= word.high;\n    this.low ^= word.low;\n  }\n  or(word) {\n    this.high |= word.high;\n    this.low |= word.low;\n  }\n  shiftRight(places) {\n    if (places >= 32) {\n      this.low = this.high >>> places - 32 | 0;\n      this.high = 0;\n    } else {\n      this.low = this.low >>> places | this.high << 32 - places;\n      this.high = this.high >>> places | 0;\n    }\n  }\n  shiftLeft(places) {\n    if (places >= 32) {\n      this.high = this.low << places - 32;\n      this.low = 0;\n    } else {\n      this.high = this.high << places | this.low >>> 32 - places;\n      this.low <<= places;\n    }\n  }\n  rotateRight(places) {\n    let low, high;\n    if (places & 32) {\n      high = this.low;\n      low = this.high;\n    } else {\n      low = this.low;\n      high = this.high;\n    }\n    places &= 31;\n    this.low = low >>> places | high << 32 - places;\n    this.high = high >>> places | low << 32 - places;\n  }\n  not() {\n    this.high = ~this.high;\n    this.low = ~this.low;\n  }\n  add(word) {\n    const lowAdd = (this.low >>> 0) + (word.low >>> 0);\n    let highAdd = (this.high >>> 0) + (word.high >>> 0);\n    if (lowAdd > 0xffffffff) {\n      highAdd += 1;\n    }\n    this.low = lowAdd | 0;\n    this.high = highAdd | 0;\n  }\n  copyTo(bytes, offset) {\n    bytes[offset] = this.high >>> 24 & 0xff;\n    bytes[offset + 1] = this.high >> 16 & 0xff;\n    bytes[offset + 2] = this.high >> 8 & 0xff;\n    bytes[offset + 3] = this.high & 0xff;\n    bytes[offset + 4] = this.low >>> 24 & 0xff;\n    bytes[offset + 5] = this.low >> 16 & 0xff;\n    bytes[offset + 6] = this.low >> 8 & 0xff;\n    bytes[offset + 7] = this.low & 0xff;\n  }\n  assign(word) {\n    this.high = word.high;\n    this.low = word.low;\n  }\n}\nconst calculateSHA256 = function calculateSHA256Closure() {\n  function rotr(x, n) {\n    return x >>> n | x << 32 - n;\n  }\n  function ch(x, y, z) {\n    return x & y ^ ~x & z;\n  }\n  function maj(x, y, z) {\n    return x & y ^ x & z ^ y & z;\n  }\n  function sigma(x) {\n    return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);\n  }\n  function sigmaPrime(x) {\n    return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);\n  }\n  function littleSigma(x) {\n    return rotr(x, 7) ^ rotr(x, 18) ^ x >>> 3;\n  }\n  function littleSigmaPrime(x) {\n    return rotr(x, 17) ^ rotr(x, 19) ^ x >>> 10;\n  }\n  const k = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];\n  function hash(data, offset, length) {\n    let h0 = 0x6a09e667,\n      h1 = 0xbb67ae85,\n      h2 = 0x3c6ef372,\n      h3 = 0xa54ff53a,\n      h4 = 0x510e527f,\n      h5 = 0x9b05688c,\n      h6 = 0x1f83d9ab,\n      h7 = 0x5be0cd19;\n    const paddedLength = Math.ceil((length + 9) / 64) * 64;\n    const padded = new Uint8Array(paddedLength);\n    let i, j;\n    for (i = 0; i < length; ++i) {\n      padded[i] = data[offset++];\n    }\n    padded[i++] = 0x80;\n    const n = paddedLength - 8;\n    while (i < n) {\n      padded[i++] = 0;\n    }\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = length >>> 29 & 0xff;\n    padded[i++] = length >> 21 & 0xff;\n    padded[i++] = length >> 13 & 0xff;\n    padded[i++] = length >> 5 & 0xff;\n    padded[i++] = length << 3 & 0xff;\n    const w = new Uint32Array(64);\n    for (i = 0; i < paddedLength;) {\n      for (j = 0; j < 16; ++j) {\n        w[j] = padded[i] << 24 | padded[i + 1] << 16 | padded[i + 2] << 8 | padded[i + 3];\n        i += 4;\n      }\n      for (j = 16; j < 64; ++j) {\n        w[j] = littleSigmaPrime(w[j - 2]) + w[j - 7] + littleSigma(w[j - 15]) + w[j - 16] | 0;\n      }\n      let a = h0,\n        b = h1,\n        c = h2,\n        d = h3,\n        e = h4,\n        f = h5,\n        g = h6,\n        h = h7,\n        t1,\n        t2;\n      for (j = 0; j < 64; ++j) {\n        t1 = h + sigmaPrime(e) + ch(e, f, g) + k[j] + w[j];\n        t2 = sigma(a) + maj(a, b, c);\n        h = g;\n        g = f;\n        f = e;\n        e = d + t1 | 0;\n        d = c;\n        c = b;\n        b = a;\n        a = t1 + t2 | 0;\n      }\n      h0 = h0 + a | 0;\n      h1 = h1 + b | 0;\n      h2 = h2 + c | 0;\n      h3 = h3 + d | 0;\n      h4 = h4 + e | 0;\n      h5 = h5 + f | 0;\n      h6 = h6 + g | 0;\n      h7 = h7 + h | 0;\n    }\n    return new Uint8Array([h0 >> 24 & 0xFF, h0 >> 16 & 0xFF, h0 >> 8 & 0xFF, h0 & 0xFF, h1 >> 24 & 0xFF, h1 >> 16 & 0xFF, h1 >> 8 & 0xFF, h1 & 0xFF, h2 >> 24 & 0xFF, h2 >> 16 & 0xFF, h2 >> 8 & 0xFF, h2 & 0xFF, h3 >> 24 & 0xFF, h3 >> 16 & 0xFF, h3 >> 8 & 0xFF, h3 & 0xFF, h4 >> 24 & 0xFF, h4 >> 16 & 0xFF, h4 >> 8 & 0xFF, h4 & 0xFF, h5 >> 24 & 0xFF, h5 >> 16 & 0xFF, h5 >> 8 & 0xFF, h5 & 0xFF, h6 >> 24 & 0xFF, h6 >> 16 & 0xFF, h6 >> 8 & 0xFF, h6 & 0xFF, h7 >> 24 & 0xFF, h7 >> 16 & 0xFF, h7 >> 8 & 0xFF, h7 & 0xFF]);\n  }\n  return hash;\n}();\nconst calculateSHA512 = function calculateSHA512Closure() {\n  function ch(result, x, y, z, tmp) {\n    result.assign(x);\n    result.and(y);\n    tmp.assign(x);\n    tmp.not();\n    tmp.and(z);\n    result.xor(tmp);\n  }\n  function maj(result, x, y, z, tmp) {\n    result.assign(x);\n    result.and(y);\n    tmp.assign(x);\n    tmp.and(z);\n    result.xor(tmp);\n    tmp.assign(y);\n    tmp.and(z);\n    result.xor(tmp);\n  }\n  function sigma(result, x, tmp) {\n    result.assign(x);\n    result.rotateRight(28);\n    tmp.assign(x);\n    tmp.rotateRight(34);\n    result.xor(tmp);\n    tmp.assign(x);\n    tmp.rotateRight(39);\n    result.xor(tmp);\n  }\n  function sigmaPrime(result, x, tmp) {\n    result.assign(x);\n    result.rotateRight(14);\n    tmp.assign(x);\n    tmp.rotateRight(18);\n    result.xor(tmp);\n    tmp.assign(x);\n    tmp.rotateRight(41);\n    result.xor(tmp);\n  }\n  function littleSigma(result, x, tmp) {\n    result.assign(x);\n    result.rotateRight(1);\n    tmp.assign(x);\n    tmp.rotateRight(8);\n    result.xor(tmp);\n    tmp.assign(x);\n    tmp.shiftRight(7);\n    result.xor(tmp);\n  }\n  function littleSigmaPrime(result, x, tmp) {\n    result.assign(x);\n    result.rotateRight(19);\n    tmp.assign(x);\n    tmp.rotateRight(61);\n    result.xor(tmp);\n    tmp.assign(x);\n    tmp.shiftRight(6);\n    result.xor(tmp);\n  }\n  const k = [new Word64(0x428a2f98, 0xd728ae22), new Word64(0x71374491, 0x23ef65cd), new Word64(0xb5c0fbcf, 0xec4d3b2f), new Word64(0xe9b5dba5, 0x8189dbbc), new Word64(0x3956c25b, 0xf348b538), new Word64(0x59f111f1, 0xb605d019), new Word64(0x923f82a4, 0xaf194f9b), new Word64(0xab1c5ed5, 0xda6d8118), new Word64(0xd807aa98, 0xa3030242), new Word64(0x12835b01, 0x45706fbe), new Word64(0x243185be, 0x4ee4b28c), new Word64(0x550c7dc3, 0xd5ffb4e2), new Word64(0x72be5d74, 0xf27b896f), new Word64(0x80deb1fe, 0x3b1696b1), new Word64(0x9bdc06a7, 0x25c71235), new Word64(0xc19bf174, 0xcf692694), new Word64(0xe49b69c1, 0x9ef14ad2), new Word64(0xefbe4786, 0x384f25e3), new Word64(0x0fc19dc6, 0x8b8cd5b5), new Word64(0x240ca1cc, 0x77ac9c65), new Word64(0x2de92c6f, 0x592b0275), new Word64(0x4a7484aa, 0x6ea6e483), new Word64(0x5cb0a9dc, 0xbd41fbd4), new Word64(0x76f988da, 0x831153b5), new Word64(0x983e5152, 0xee66dfab), new Word64(0xa831c66d, 0x2db43210), new Word64(0xb00327c8, 0x98fb213f), new Word64(0xbf597fc7, 0xbeef0ee4), new Word64(0xc6e00bf3, 0x3da88fc2), new Word64(0xd5a79147, 0x930aa725), new Word64(0x06ca6351, 0xe003826f), new Word64(0x14292967, 0x0a0e6e70), new Word64(0x27b70a85, 0x46d22ffc), new Word64(0x2e1b2138, 0x5c26c926), new Word64(0x4d2c6dfc, 0x5ac42aed), new Word64(0x53380d13, 0x9d95b3df), new Word64(0x650a7354, 0x8baf63de), new Word64(0x766a0abb, 0x3c77b2a8), new Word64(0x81c2c92e, 0x47edaee6), new Word64(0x92722c85, 0x1482353b), new Word64(0xa2bfe8a1, 0x4cf10364), new Word64(0xa81a664b, 0xbc423001), new Word64(0xc24b8b70, 0xd0f89791), new Word64(0xc76c51a3, 0x0654be30), new Word64(0xd192e819, 0xd6ef5218), new Word64(0xd6990624, 0x5565a910), new Word64(0xf40e3585, 0x5771202a), new Word64(0x106aa070, 0x32bbd1b8), new Word64(0x19a4c116, 0xb8d2d0c8), new Word64(0x1e376c08, 0x5141ab53), new Word64(0x2748774c, 0xdf8eeb99), new Word64(0x34b0bcb5, 0xe19b48a8), new Word64(0x391c0cb3, 0xc5c95a63), new Word64(0x4ed8aa4a, 0xe3418acb), new Word64(0x5b9cca4f, 0x7763e373), new Word64(0x682e6ff3, 0xd6b2b8a3), new Word64(0x748f82ee, 0x5defb2fc), new Word64(0x78a5636f, 0x43172f60), new Word64(0x84c87814, 0xa1f0ab72), new Word64(0x8cc70208, 0x1a6439ec), new Word64(0x90befffa, 0x23631e28), new Word64(0xa4506ceb, 0xde82bde9), new Word64(0xbef9a3f7, 0xb2c67915), new Word64(0xc67178f2, 0xe372532b), new Word64(0xca273ece, 0xea26619c), new Word64(0xd186b8c7, 0x21c0c207), new Word64(0xeada7dd6, 0xcde0eb1e), new Word64(0xf57d4f7f, 0xee6ed178), new Word64(0x06f067aa, 0x72176fba), new Word64(0x0a637dc5, 0xa2c898a6), new Word64(0x113f9804, 0xbef90dae), new Word64(0x1b710b35, 0x131c471b), new Word64(0x28db77f5, 0x23047d84), new Word64(0x32caab7b, 0x40c72493), new Word64(0x3c9ebe0a, 0x15c9bebc), new Word64(0x431d67c4, 0x9c100d4c), new Word64(0x4cc5d4be, 0xcb3e42b6), new Word64(0x597f299c, 0xfc657e2a), new Word64(0x5fcb6fab, 0x3ad6faec), new Word64(0x6c44198c, 0x4a475817)];\n  function hash(data, offset, length, mode384 = false) {\n    let h0, h1, h2, h3, h4, h5, h6, h7;\n    if (!mode384) {\n      h0 = new Word64(0x6a09e667, 0xf3bcc908);\n      h1 = new Word64(0xbb67ae85, 0x84caa73b);\n      h2 = new Word64(0x3c6ef372, 0xfe94f82b);\n      h3 = new Word64(0xa54ff53a, 0x5f1d36f1);\n      h4 = new Word64(0x510e527f, 0xade682d1);\n      h5 = new Word64(0x9b05688c, 0x2b3e6c1f);\n      h6 = new Word64(0x1f83d9ab, 0xfb41bd6b);\n      h7 = new Word64(0x5be0cd19, 0x137e2179);\n    } else {\n      h0 = new Word64(0xcbbb9d5d, 0xc1059ed8);\n      h1 = new Word64(0x629a292a, 0x367cd507);\n      h2 = new Word64(0x9159015a, 0x3070dd17);\n      h3 = new Word64(0x152fecd8, 0xf70e5939);\n      h4 = new Word64(0x67332667, 0xffc00b31);\n      h5 = new Word64(0x8eb44a87, 0x68581511);\n      h6 = new Word64(0xdb0c2e0d, 0x64f98fa7);\n      h7 = new Word64(0x47b5481d, 0xbefa4fa4);\n    }\n    const paddedLength = Math.ceil((length + 17) / 128) * 128;\n    const padded = new Uint8Array(paddedLength);\n    let i, j;\n    for (i = 0; i < length; ++i) {\n      padded[i] = data[offset++];\n    }\n    padded[i++] = 0x80;\n    const n = paddedLength - 16;\n    while (i < n) {\n      padded[i++] = 0;\n    }\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = 0;\n    padded[i++] = length >>> 29 & 0xff;\n    padded[i++] = length >> 21 & 0xff;\n    padded[i++] = length >> 13 & 0xff;\n    padded[i++] = length >> 5 & 0xff;\n    padded[i++] = length << 3 & 0xff;\n    const w = new Array(80);\n    for (i = 0; i < 80; i++) {\n      w[i] = new Word64(0, 0);\n    }\n    let a = new Word64(0, 0),\n      b = new Word64(0, 0),\n      c = new Word64(0, 0);\n    let d = new Word64(0, 0),\n      e = new Word64(0, 0),\n      f = new Word64(0, 0);\n    let g = new Word64(0, 0),\n      h = new Word64(0, 0);\n    const t1 = new Word64(0, 0),\n      t2 = new Word64(0, 0);\n    const tmp1 = new Word64(0, 0),\n      tmp2 = new Word64(0, 0);\n    let tmp3;\n    for (i = 0; i < paddedLength;) {\n      for (j = 0; j < 16; ++j) {\n        w[j].high = padded[i] << 24 | padded[i + 1] << 16 | padded[i + 2] << 8 | padded[i + 3];\n        w[j].low = padded[i + 4] << 24 | padded[i + 5] << 16 | padded[i + 6] << 8 | padded[i + 7];\n        i += 8;\n      }\n      for (j = 16; j < 80; ++j) {\n        tmp3 = w[j];\n        littleSigmaPrime(tmp3, w[j - 2], tmp2);\n        tmp3.add(w[j - 7]);\n        littleSigma(tmp1, w[j - 15], tmp2);\n        tmp3.add(tmp1);\n        tmp3.add(w[j - 16]);\n      }\n      a.assign(h0);\n      b.assign(h1);\n      c.assign(h2);\n      d.assign(h3);\n      e.assign(h4);\n      f.assign(h5);\n      g.assign(h6);\n      h.assign(h7);\n      for (j = 0; j < 80; ++j) {\n        t1.assign(h);\n        sigmaPrime(tmp1, e, tmp2);\n        t1.add(tmp1);\n        ch(tmp1, e, f, g, tmp2);\n        t1.add(tmp1);\n        t1.add(k[j]);\n        t1.add(w[j]);\n        sigma(t2, a, tmp2);\n        maj(tmp1, a, b, c, tmp2);\n        t2.add(tmp1);\n        tmp3 = h;\n        h = g;\n        g = f;\n        f = e;\n        d.add(t1);\n        e = d;\n        d = c;\n        c = b;\n        b = a;\n        tmp3.assign(t1);\n        tmp3.add(t2);\n        a = tmp3;\n      }\n      h0.add(a);\n      h1.add(b);\n      h2.add(c);\n      h3.add(d);\n      h4.add(e);\n      h5.add(f);\n      h6.add(g);\n      h7.add(h);\n    }\n    let result;\n    if (!mode384) {\n      result = new Uint8Array(64);\n      h0.copyTo(result, 0);\n      h1.copyTo(result, 8);\n      h2.copyTo(result, 16);\n      h3.copyTo(result, 24);\n      h4.copyTo(result, 32);\n      h5.copyTo(result, 40);\n      h6.copyTo(result, 48);\n      h7.copyTo(result, 56);\n    } else {\n      result = new Uint8Array(48);\n      h0.copyTo(result, 0);\n      h1.copyTo(result, 8);\n      h2.copyTo(result, 16);\n      h3.copyTo(result, 24);\n      h4.copyTo(result, 32);\n      h5.copyTo(result, 40);\n    }\n    return result;\n  }\n  return hash;\n}();\nfunction calculateSHA384(data, offset, length) {\n  return calculateSHA512(data, offset, length, true);\n}\nclass NullCipher {\n  decryptBlock(data) {\n    return data;\n  }\n  encrypt(data) {\n    return data;\n  }\n}\nclass AESBaseCipher {\n  constructor() {\n    if (this.constructor === AESBaseCipher) {\n      unreachable(\"Cannot initialize AESBaseCipher.\");\n    }\n    this._s = new Uint8Array([0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16]);\n    this._inv_s = new Uint8Array([0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d]);\n    this._mix = new Uint32Array([0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]);\n    this._mixCol = new Uint8Array(256);\n    for (let i = 0; i < 256; i++) {\n      this._mixCol[i] = i < 128 ? i << 1 : i << 1 ^ 0x1b;\n    }\n    this.buffer = new Uint8Array(16);\n    this.bufferPosition = 0;\n  }\n  _expandKey(cipherKey) {\n    unreachable(\"Cannot call `_expandKey` on the base class\");\n  }\n  _decrypt(input, key) {\n    let t, u, v;\n    const state = new Uint8Array(16);\n    state.set(input);\n    for (let j = 0, k = this._keySize; j < 16; ++j, ++k) {\n      state[j] ^= key[k];\n    }\n    for (let i = this._cyclesOfRepetition - 1; i >= 1; --i) {\n      t = state[13];\n      state[13] = state[9];\n      state[9] = state[5];\n      state[5] = state[1];\n      state[1] = t;\n      t = state[14];\n      u = state[10];\n      state[14] = state[6];\n      state[10] = state[2];\n      state[6] = t;\n      state[2] = u;\n      t = state[15];\n      u = state[11];\n      v = state[7];\n      state[15] = state[3];\n      state[11] = t;\n      state[7] = u;\n      state[3] = v;\n      for (let j = 0; j < 16; ++j) {\n        state[j] = this._inv_s[state[j]];\n      }\n      for (let j = 0, k = i * 16; j < 16; ++j, ++k) {\n        state[j] ^= key[k];\n      }\n      for (let j = 0; j < 16; j += 4) {\n        const s0 = this._mix[state[j]];\n        const s1 = this._mix[state[j + 1]];\n        const s2 = this._mix[state[j + 2]];\n        const s3 = this._mix[state[j + 3]];\n        t = s0 ^ s1 >>> 8 ^ s1 << 24 ^ s2 >>> 16 ^ s2 << 16 ^ s3 >>> 24 ^ s3 << 8;\n        state[j] = t >>> 24 & 0xff;\n        state[j + 1] = t >> 16 & 0xff;\n        state[j + 2] = t >> 8 & 0xff;\n        state[j + 3] = t & 0xff;\n      }\n    }\n    t = state[13];\n    state[13] = state[9];\n    state[9] = state[5];\n    state[5] = state[1];\n    state[1] = t;\n    t = state[14];\n    u = state[10];\n    state[14] = state[6];\n    state[10] = state[2];\n    state[6] = t;\n    state[2] = u;\n    t = state[15];\n    u = state[11];\n    v = state[7];\n    state[15] = state[3];\n    state[11] = t;\n    state[7] = u;\n    state[3] = v;\n    for (let j = 0; j < 16; ++j) {\n      state[j] = this._inv_s[state[j]];\n      state[j] ^= key[j];\n    }\n    return state;\n  }\n  _encrypt(input, key) {\n    const s = this._s;\n    let t, u, v;\n    const state = new Uint8Array(16);\n    state.set(input);\n    for (let j = 0; j < 16; ++j) {\n      state[j] ^= key[j];\n    }\n    for (let i = 1; i < this._cyclesOfRepetition; i++) {\n      for (let j = 0; j < 16; ++j) {\n        state[j] = s[state[j]];\n      }\n      v = state[1];\n      state[1] = state[5];\n      state[5] = state[9];\n      state[9] = state[13];\n      state[13] = v;\n      v = state[2];\n      u = state[6];\n      state[2] = state[10];\n      state[6] = state[14];\n      state[10] = v;\n      state[14] = u;\n      v = state[3];\n      u = state[7];\n      t = state[11];\n      state[3] = state[15];\n      state[7] = v;\n      state[11] = u;\n      state[15] = t;\n      for (let j = 0; j < 16; j += 4) {\n        const s0 = state[j + 0];\n        const s1 = state[j + 1];\n        const s2 = state[j + 2];\n        const s3 = state[j + 3];\n        t = s0 ^ s1 ^ s2 ^ s3;\n        state[j + 0] ^= t ^ this._mixCol[s0 ^ s1];\n        state[j + 1] ^= t ^ this._mixCol[s1 ^ s2];\n        state[j + 2] ^= t ^ this._mixCol[s2 ^ s3];\n        state[j + 3] ^= t ^ this._mixCol[s3 ^ s0];\n      }\n      for (let j = 0, k = i * 16; j < 16; ++j, ++k) {\n        state[j] ^= key[k];\n      }\n    }\n    for (let j = 0; j < 16; ++j) {\n      state[j] = s[state[j]];\n    }\n    v = state[1];\n    state[1] = state[5];\n    state[5] = state[9];\n    state[9] = state[13];\n    state[13] = v;\n    v = state[2];\n    u = state[6];\n    state[2] = state[10];\n    state[6] = state[14];\n    state[10] = v;\n    state[14] = u;\n    v = state[3];\n    u = state[7];\n    t = state[11];\n    state[3] = state[15];\n    state[7] = v;\n    state[11] = u;\n    state[15] = t;\n    for (let j = 0, k = this._keySize; j < 16; ++j, ++k) {\n      state[j] ^= key[k];\n    }\n    return state;\n  }\n  _decryptBlock2(data, finalize) {\n    const sourceLength = data.length;\n    let buffer = this.buffer,\n      bufferLength = this.bufferPosition;\n    const result = [];\n    let iv = this.iv;\n    for (let i = 0; i < sourceLength; ++i) {\n      buffer[bufferLength] = data[i];\n      ++bufferLength;\n      if (bufferLength < 16) {\n        continue;\n      }\n      const plain = this._decrypt(buffer, this._key);\n      for (let j = 0; j < 16; ++j) {\n        plain[j] ^= iv[j];\n      }\n      iv = buffer;\n      result.push(plain);\n      buffer = new Uint8Array(16);\n      bufferLength = 0;\n    }\n    this.buffer = buffer;\n    this.bufferLength = bufferLength;\n    this.iv = iv;\n    if (result.length === 0) {\n      return new Uint8Array(0);\n    }\n    let outputLength = 16 * result.length;\n    if (finalize) {\n      const lastBlock = result.at(-1);\n      let psLen = lastBlock[15];\n      if (psLen <= 16) {\n        for (let i = 15, ii = 16 - psLen; i >= ii; --i) {\n          if (lastBlock[i] !== psLen) {\n            psLen = 0;\n            break;\n          }\n        }\n        outputLength -= psLen;\n        result[result.length - 1] = lastBlock.subarray(0, 16 - psLen);\n      }\n    }\n    const output = new Uint8Array(outputLength);\n    for (let i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) {\n      output.set(result[i], j);\n    }\n    return output;\n  }\n  decryptBlock(data, finalize, iv = null) {\n    const sourceLength = data.length;\n    const buffer = this.buffer;\n    let bufferLength = this.bufferPosition;\n    if (iv) {\n      this.iv = iv;\n    } else {\n      for (let i = 0; bufferLength < 16 && i < sourceLength; ++i, ++bufferLength) {\n        buffer[bufferLength] = data[i];\n      }\n      if (bufferLength < 16) {\n        this.bufferLength = bufferLength;\n        return new Uint8Array(0);\n      }\n      this.iv = buffer;\n      data = data.subarray(16);\n    }\n    this.buffer = new Uint8Array(16);\n    this.bufferLength = 0;\n    this.decryptBlock = this._decryptBlock2;\n    return this.decryptBlock(data, finalize);\n  }\n  encrypt(data, iv) {\n    const sourceLength = data.length;\n    let buffer = this.buffer,\n      bufferLength = this.bufferPosition;\n    const result = [];\n    if (!iv) {\n      iv = new Uint8Array(16);\n    }\n    for (let i = 0; i < sourceLength; ++i) {\n      buffer[bufferLength] = data[i];\n      ++bufferLength;\n      if (bufferLength < 16) {\n        continue;\n      }\n      for (let j = 0; j < 16; ++j) {\n        buffer[j] ^= iv[j];\n      }\n      const cipher = this._encrypt(buffer, this._key);\n      iv = cipher;\n      result.push(cipher);\n      buffer = new Uint8Array(16);\n      bufferLength = 0;\n    }\n    this.buffer = buffer;\n    this.bufferLength = bufferLength;\n    this.iv = iv;\n    if (result.length === 0) {\n      return new Uint8Array(0);\n    }\n    const outputLength = 16 * result.length;\n    const output = new Uint8Array(outputLength);\n    for (let i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) {\n      output.set(result[i], j);\n    }\n    return output;\n  }\n}\nclass AES128Cipher extends AESBaseCipher {\n  constructor(key) {\n    super();\n    this._cyclesOfRepetition = 10;\n    this._keySize = 160;\n    this._rcon = new Uint8Array([0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d]);\n    this._key = this._expandKey(key);\n  }\n  _expandKey(cipherKey) {\n    const b = 176;\n    const s = this._s;\n    const rcon = this._rcon;\n    const result = new Uint8Array(b);\n    result.set(cipherKey);\n    for (let j = 16, i = 1; j < b; ++i) {\n      let t1 = result[j - 3];\n      let t2 = result[j - 2];\n      let t3 = result[j - 1];\n      let t4 = result[j - 4];\n      t1 = s[t1];\n      t2 = s[t2];\n      t3 = s[t3];\n      t4 = s[t4];\n      t1 ^= rcon[i];\n      for (let n = 0; n < 4; ++n) {\n        result[j] = t1 ^= result[j - 16];\n        j++;\n        result[j] = t2 ^= result[j - 16];\n        j++;\n        result[j] = t3 ^= result[j - 16];\n        j++;\n        result[j] = t4 ^= result[j - 16];\n        j++;\n      }\n    }\n    return result;\n  }\n}\nclass AES256Cipher extends AESBaseCipher {\n  constructor(key) {\n    super();\n    this._cyclesOfRepetition = 14;\n    this._keySize = 224;\n    this._key = this._expandKey(key);\n  }\n  _expandKey(cipherKey) {\n    const b = 240;\n    const s = this._s;\n    const result = new Uint8Array(b);\n    result.set(cipherKey);\n    let r = 1;\n    let t1, t2, t3, t4;\n    for (let j = 32, i = 1; j < b; ++i) {\n      if (j % 32 === 16) {\n        t1 = s[t1];\n        t2 = s[t2];\n        t3 = s[t3];\n        t4 = s[t4];\n      } else if (j % 32 === 0) {\n        t1 = result[j - 3];\n        t2 = result[j - 2];\n        t3 = result[j - 1];\n        t4 = result[j - 4];\n        t1 = s[t1];\n        t2 = s[t2];\n        t3 = s[t3];\n        t4 = s[t4];\n        t1 ^= r;\n        if ((r <<= 1) >= 256) {\n          r = (r ^ 0x1b) & 0xff;\n        }\n      }\n      for (let n = 0; n < 4; ++n) {\n        result[j] = t1 ^= result[j - 32];\n        j++;\n        result[j] = t2 ^= result[j - 32];\n        j++;\n        result[j] = t3 ^= result[j - 32];\n        j++;\n        result[j] = t4 ^= result[j - 32];\n        j++;\n      }\n    }\n    return result;\n  }\n}\nclass PDF17 {\n  checkOwnerPassword(password, ownerValidationSalt, userBytes, ownerPassword) {\n    const hashData = new Uint8Array(password.length + 56);\n    hashData.set(password, 0);\n    hashData.set(ownerValidationSalt, password.length);\n    hashData.set(userBytes, password.length + ownerValidationSalt.length);\n    const result = calculateSHA256(hashData, 0, hashData.length);\n    return isArrayEqual(result, ownerPassword);\n  }\n  checkUserPassword(password, userValidationSalt, userPassword) {\n    const hashData = new Uint8Array(password.length + 8);\n    hashData.set(password, 0);\n    hashData.set(userValidationSalt, password.length);\n    const result = calculateSHA256(hashData, 0, hashData.length);\n    return isArrayEqual(result, userPassword);\n  }\n  getOwnerKey(password, ownerKeySalt, userBytes, ownerEncryption) {\n    const hashData = new Uint8Array(password.length + 56);\n    hashData.set(password, 0);\n    hashData.set(ownerKeySalt, password.length);\n    hashData.set(userBytes, password.length + ownerKeySalt.length);\n    const key = calculateSHA256(hashData, 0, hashData.length);\n    const cipher = new AES256Cipher(key);\n    return cipher.decryptBlock(ownerEncryption, false, new Uint8Array(16));\n  }\n  getUserKey(password, userKeySalt, userEncryption) {\n    const hashData = new Uint8Array(password.length + 8);\n    hashData.set(password, 0);\n    hashData.set(userKeySalt, password.length);\n    const key = calculateSHA256(hashData, 0, hashData.length);\n    const cipher = new AES256Cipher(key);\n    return cipher.decryptBlock(userEncryption, false, new Uint8Array(16));\n  }\n}\nclass PDF20 {\n  _hash(password, input, userBytes) {\n    let k = calculateSHA256(input, 0, input.length).subarray(0, 32);\n    let e = [0];\n    let i = 0;\n    while (i < 64 || e.at(-1) > i - 32) {\n      const combinedLength = password.length + k.length + userBytes.length,\n        combinedArray = new Uint8Array(combinedLength);\n      let writeOffset = 0;\n      combinedArray.set(password, writeOffset);\n      writeOffset += password.length;\n      combinedArray.set(k, writeOffset);\n      writeOffset += k.length;\n      combinedArray.set(userBytes, writeOffset);\n      const k1 = new Uint8Array(combinedLength * 64);\n      for (let j = 0, pos = 0; j < 64; j++, pos += combinedLength) {\n        k1.set(combinedArray, pos);\n      }\n      const cipher = new AES128Cipher(k.subarray(0, 16));\n      e = cipher.encrypt(k1, k.subarray(16, 32));\n      const remainder = e.slice(0, 16).reduce((a, b) => a + b, 0) % 3;\n      if (remainder === 0) {\n        k = calculateSHA256(e, 0, e.length);\n      } else if (remainder === 1) {\n        k = calculateSHA384(e, 0, e.length);\n      } else if (remainder === 2) {\n        k = calculateSHA512(e, 0, e.length);\n      }\n      i++;\n    }\n    return k.subarray(0, 32);\n  }\n  checkOwnerPassword(password, ownerValidationSalt, userBytes, ownerPassword) {\n    const hashData = new Uint8Array(password.length + 56);\n    hashData.set(password, 0);\n    hashData.set(ownerValidationSalt, password.length);\n    hashData.set(userBytes, password.length + ownerValidationSalt.length);\n    const result = this._hash(password, hashData, userBytes);\n    return isArrayEqual(result, ownerPassword);\n  }\n  checkUserPassword(password, userValidationSalt, userPassword) {\n    const hashData = new Uint8Array(password.length + 8);\n    hashData.set(password, 0);\n    hashData.set(userValidationSalt, password.length);\n    const result = this._hash(password, hashData, []);\n    return isArrayEqual(result, userPassword);\n  }\n  getOwnerKey(password, ownerKeySalt, userBytes, ownerEncryption) {\n    const hashData = new Uint8Array(password.length + 56);\n    hashData.set(password, 0);\n    hashData.set(ownerKeySalt, password.length);\n    hashData.set(userBytes, password.length + ownerKeySalt.length);\n    const key = this._hash(password, hashData, userBytes);\n    const cipher = new AES256Cipher(key);\n    return cipher.decryptBlock(ownerEncryption, false, new Uint8Array(16));\n  }\n  getUserKey(password, userKeySalt, userEncryption) {\n    const hashData = new Uint8Array(password.length + 8);\n    hashData.set(password, 0);\n    hashData.set(userKeySalt, password.length);\n    const key = this._hash(password, hashData, []);\n    const cipher = new AES256Cipher(key);\n    return cipher.decryptBlock(userEncryption, false, new Uint8Array(16));\n  }\n}\nclass CipherTransform {\n  constructor(stringCipherConstructor, streamCipherConstructor) {\n    this.StringCipherConstructor = stringCipherConstructor;\n    this.StreamCipherConstructor = streamCipherConstructor;\n  }\n  createStream(stream, length) {\n    const cipher = new this.StreamCipherConstructor();\n    return new DecryptStream(stream, length, function cipherTransformDecryptStream(data, finalize) {\n      return cipher.decryptBlock(data, finalize);\n    });\n  }\n  decryptString(s) {\n    const cipher = new this.StringCipherConstructor();\n    let data = stringToBytes(s);\n    data = cipher.decryptBlock(data, true);\n    return bytesToString(data);\n  }\n  encryptString(s) {\n    const cipher = new this.StringCipherConstructor();\n    if (cipher instanceof AESBaseCipher) {\n      const strLen = s.length;\n      const pad = 16 - strLen % 16;\n      s += String.fromCharCode(pad).repeat(pad);\n      const iv = new Uint8Array(16);\n      if (typeof crypto !== \"undefined\") {\n        crypto.getRandomValues(iv);\n      } else {\n        for (let i = 0; i < 16; i++) {\n          iv[i] = Math.floor(256 * Math.random());\n        }\n      }\n      let data = stringToBytes(s);\n      data = cipher.encrypt(data, iv);\n      const buf = new Uint8Array(16 + data.length);\n      buf.set(iv);\n      buf.set(data, 16);\n      return bytesToString(buf);\n    }\n    let data = stringToBytes(s);\n    data = cipher.encrypt(data);\n    return bytesToString(data);\n  }\n}\nclass CipherTransformFactory {\n  static #defaultPasswordBytes = new Uint8Array([0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a]);\n  #createEncryptionKey20(revision, password, ownerPassword, ownerValidationSalt, ownerKeySalt, uBytes, userPassword, userValidationSalt, userKeySalt, ownerEncryption, userEncryption, perms) {\n    if (password) {\n      const passwordLength = Math.min(127, password.length);\n      password = password.subarray(0, passwordLength);\n    } else {\n      password = [];\n    }\n    const pdfAlgorithm = revision === 6 ? new PDF20() : new PDF17();\n    if (pdfAlgorithm.checkUserPassword(password, userValidationSalt, userPassword)) {\n      return pdfAlgorithm.getUserKey(password, userKeySalt, userEncryption);\n    } else if (password.length && pdfAlgorithm.checkOwnerPassword(password, ownerValidationSalt, uBytes, ownerPassword)) {\n      return pdfAlgorithm.getOwnerKey(password, ownerKeySalt, uBytes, ownerEncryption);\n    }\n    return null;\n  }\n  #prepareKeyData(fileId, password, ownerPassword, userPassword, flags, revision, keyLength, encryptMetadata) {\n    const hashDataSize = 40 + ownerPassword.length + fileId.length;\n    const hashData = new Uint8Array(hashDataSize);\n    let i = 0,\n      j,\n      n;\n    if (password) {\n      n = Math.min(32, password.length);\n      for (; i < n; ++i) {\n        hashData[i] = password[i];\n      }\n    }\n    j = 0;\n    while (i < 32) {\n      hashData[i++] = CipherTransformFactory.#defaultPasswordBytes[j++];\n    }\n    for (j = 0, n = ownerPassword.length; j < n; ++j) {\n      hashData[i++] = ownerPassword[j];\n    }\n    hashData[i++] = flags & 0xff;\n    hashData[i++] = flags >> 8 & 0xff;\n    hashData[i++] = flags >> 16 & 0xff;\n    hashData[i++] = flags >>> 24 & 0xff;\n    for (j = 0, n = fileId.length; j < n; ++j) {\n      hashData[i++] = fileId[j];\n    }\n    if (revision >= 4 && !encryptMetadata) {\n      hashData[i++] = 0xff;\n      hashData[i++] = 0xff;\n      hashData[i++] = 0xff;\n      hashData[i++] = 0xff;\n    }\n    let hash = calculateMD5(hashData, 0, i);\n    const keyLengthInBytes = keyLength >> 3;\n    if (revision >= 3) {\n      for (j = 0; j < 50; ++j) {\n        hash = calculateMD5(hash, 0, keyLengthInBytes);\n      }\n    }\n    const encryptionKey = hash.subarray(0, keyLengthInBytes);\n    let cipher, checkData;\n    if (revision >= 3) {\n      for (i = 0; i < 32; ++i) {\n        hashData[i] = CipherTransformFactory.#defaultPasswordBytes[i];\n      }\n      for (j = 0, n = fileId.length; j < n; ++j) {\n        hashData[i++] = fileId[j];\n      }\n      cipher = new ARCFourCipher(encryptionKey);\n      checkData = cipher.encryptBlock(calculateMD5(hashData, 0, i));\n      n = encryptionKey.length;\n      const derivedKey = new Uint8Array(n);\n      for (j = 1; j <= 19; ++j) {\n        for (let k = 0; k < n; ++k) {\n          derivedKey[k] = encryptionKey[k] ^ j;\n        }\n        cipher = new ARCFourCipher(derivedKey);\n        checkData = cipher.encryptBlock(checkData);\n      }\n      for (j = 0, n = checkData.length; j < n; ++j) {\n        if (userPassword[j] !== checkData[j]) {\n          return null;\n        }\n      }\n    } else {\n      cipher = new ARCFourCipher(encryptionKey);\n      checkData = cipher.encryptBlock(CipherTransformFactory.#defaultPasswordBytes);\n      for (j = 0, n = checkData.length; j < n; ++j) {\n        if (userPassword[j] !== checkData[j]) {\n          return null;\n        }\n      }\n    }\n    return encryptionKey;\n  }\n  #decodeUserPassword(password, ownerPassword, revision, keyLength) {\n    const hashData = new Uint8Array(32);\n    let i = 0;\n    const n = Math.min(32, password.length);\n    for (; i < n; ++i) {\n      hashData[i] = password[i];\n    }\n    let j = 0;\n    while (i < 32) {\n      hashData[i++] = CipherTransformFactory.#defaultPasswordBytes[j++];\n    }\n    let hash = calculateMD5(hashData, 0, i);\n    const keyLengthInBytes = keyLength >> 3;\n    if (revision >= 3) {\n      for (j = 0; j < 50; ++j) {\n        hash = calculateMD5(hash, 0, hash.length);\n      }\n    }\n    let cipher, userPassword;\n    if (revision >= 3) {\n      userPassword = ownerPassword;\n      const derivedKey = new Uint8Array(keyLengthInBytes);\n      for (j = 19; j >= 0; j--) {\n        for (let k = 0; k < keyLengthInBytes; ++k) {\n          derivedKey[k] = hash[k] ^ j;\n        }\n        cipher = new ARCFourCipher(derivedKey);\n        userPassword = cipher.encryptBlock(userPassword);\n      }\n    } else {\n      cipher = new ARCFourCipher(hash.subarray(0, keyLengthInBytes));\n      userPassword = cipher.encryptBlock(ownerPassword);\n    }\n    return userPassword;\n  }\n  #buildObjectKey(num, gen, encryptionKey, isAes = false) {\n    const key = new Uint8Array(encryptionKey.length + 9);\n    const n = encryptionKey.length;\n    let i;\n    for (i = 0; i < n; ++i) {\n      key[i] = encryptionKey[i];\n    }\n    key[i++] = num & 0xff;\n    key[i++] = num >> 8 & 0xff;\n    key[i++] = num >> 16 & 0xff;\n    key[i++] = gen & 0xff;\n    key[i++] = gen >> 8 & 0xff;\n    if (isAes) {\n      key[i++] = 0x73;\n      key[i++] = 0x41;\n      key[i++] = 0x6c;\n      key[i++] = 0x54;\n    }\n    const hash = calculateMD5(key, 0, i);\n    return hash.subarray(0, Math.min(encryptionKey.length + 5, 16));\n  }\n  #buildCipherConstructor(cf, name, num, gen, key) {\n    if (!(name instanceof Name)) {\n      throw new FormatError(\"Invalid crypt filter name.\");\n    }\n    const self = this;\n    const cryptFilter = cf.get(name.name);\n    const cfm = cryptFilter?.get(\"CFM\");\n    if (!cfm || cfm.name === \"None\") {\n      return function () {\n        return new NullCipher();\n      };\n    }\n    if (cfm.name === \"V2\") {\n      return function () {\n        return new ARCFourCipher(self.#buildObjectKey(num, gen, key, false));\n      };\n    }\n    if (cfm.name === \"AESV2\") {\n      return function () {\n        return new AES128Cipher(self.#buildObjectKey(num, gen, key, true));\n      };\n    }\n    if (cfm.name === \"AESV3\") {\n      return function () {\n        return new AES256Cipher(key);\n      };\n    }\n    throw new FormatError(\"Unknown crypto method\");\n  }\n  constructor(dict, fileId, password) {\n    const filter = dict.get(\"Filter\");\n    if (!isName(filter, \"Standard\")) {\n      throw new FormatError(\"unknown encryption method\");\n    }\n    this.filterName = filter.name;\n    this.dict = dict;\n    const algorithm = dict.get(\"V\");\n    if (!Number.isInteger(algorithm) || algorithm !== 1 && algorithm !== 2 && algorithm !== 4 && algorithm !== 5) {\n      throw new FormatError(\"unsupported encryption algorithm\");\n    }\n    this.algorithm = algorithm;\n    let keyLength = dict.get(\"Length\");\n    if (!keyLength) {\n      if (algorithm <= 3) {\n        keyLength = 40;\n      } else {\n        const cfDict = dict.get(\"CF\");\n        const streamCryptoName = dict.get(\"StmF\");\n        if (cfDict instanceof Dict && streamCryptoName instanceof Name) {\n          cfDict.suppressEncryption = true;\n          const handlerDict = cfDict.get(streamCryptoName.name);\n          keyLength = handlerDict?.get(\"Length\") || 128;\n          if (keyLength < 40) {\n            keyLength <<= 3;\n          }\n        }\n      }\n    }\n    if (!Number.isInteger(keyLength) || keyLength < 40 || keyLength % 8 !== 0) {\n      throw new FormatError(\"invalid key length\");\n    }\n    const ownerBytes = stringToBytes(dict.get(\"O\")),\n      userBytes = stringToBytes(dict.get(\"U\"));\n    const ownerPassword = ownerBytes.subarray(0, 32);\n    const userPassword = userBytes.subarray(0, 32);\n    const flags = dict.get(\"P\");\n    const revision = dict.get(\"R\");\n    const encryptMetadata = (algorithm === 4 || algorithm === 5) && dict.get(\"EncryptMetadata\") !== false;\n    this.encryptMetadata = encryptMetadata;\n    const fileIdBytes = stringToBytes(fileId);\n    let passwordBytes;\n    if (password) {\n      if (revision === 6) {\n        try {\n          password = utf8StringToString(password);\n        } catch {\n          warn(\"CipherTransformFactory: Unable to convert UTF8 encoded password.\");\n        }\n      }\n      passwordBytes = stringToBytes(password);\n    }\n    let encryptionKey;\n    if (algorithm !== 5) {\n      encryptionKey = this.#prepareKeyData(fileIdBytes, passwordBytes, ownerPassword, userPassword, flags, revision, keyLength, encryptMetadata);\n    } else {\n      const ownerValidationSalt = ownerBytes.subarray(32, 40);\n      const ownerKeySalt = ownerBytes.subarray(40, 48);\n      const uBytes = userBytes.subarray(0, 48);\n      const userValidationSalt = userBytes.subarray(32, 40);\n      const userKeySalt = userBytes.subarray(40, 48);\n      const ownerEncryption = stringToBytes(dict.get(\"OE\"));\n      const userEncryption = stringToBytes(dict.get(\"UE\"));\n      const perms = stringToBytes(dict.get(\"Perms\"));\n      encryptionKey = this.#createEncryptionKey20(revision, passwordBytes, ownerPassword, ownerValidationSalt, ownerKeySalt, uBytes, userPassword, userValidationSalt, userKeySalt, ownerEncryption, userEncryption, perms);\n    }\n    if (!encryptionKey && !password) {\n      throw new PasswordException(\"No password given\", PasswordResponses.NEED_PASSWORD);\n    } else if (!encryptionKey && password) {\n      const decodedPassword = this.#decodeUserPassword(passwordBytes, ownerPassword, revision, keyLength);\n      encryptionKey = this.#prepareKeyData(fileIdBytes, decodedPassword, ownerPassword, userPassword, flags, revision, keyLength, encryptMetadata);\n    }\n    if (!encryptionKey) {\n      throw new PasswordException(\"Incorrect Password\", PasswordResponses.INCORRECT_PASSWORD);\n    }\n    this.encryptionKey = encryptionKey;\n    if (algorithm >= 4) {\n      const cf = dict.get(\"CF\");\n      if (cf instanceof Dict) {\n        cf.suppressEncryption = true;\n      }\n      this.cf = cf;\n      this.stmf = dict.get(\"StmF\") || Name.get(\"Identity\");\n      this.strf = dict.get(\"StrF\") || Name.get(\"Identity\");\n      this.eff = dict.get(\"EFF\") || this.stmf;\n    }\n  }\n  createCipherTransform(num, gen) {\n    if (this.algorithm === 4 || this.algorithm === 5) {\n      return new CipherTransform(this.#buildCipherConstructor(this.cf, this.strf, num, gen, this.encryptionKey), this.#buildCipherConstructor(this.cf, this.stmf, num, gen, this.encryptionKey));\n    }\n    const key = this.#buildObjectKey(num, gen, this.encryptionKey, false);\n    const cipherConstructor = function () {\n      return new ARCFourCipher(key);\n    };\n    return new CipherTransform(cipherConstructor, cipherConstructor);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/writer.js\n\n\n\n\n\n\nasync function writeObject(ref, obj, buffer, {\n  encrypt = null\n}) {\n  const transform = encrypt?.createCipherTransform(ref.num, ref.gen);\n  buffer.push(`${ref.num} ${ref.gen} obj\\n`);\n  if (obj instanceof Dict) {\n    await writeDict(obj, buffer, transform);\n  } else if (obj instanceof BaseStream) {\n    await writeStream(obj, buffer, transform);\n  } else if (Array.isArray(obj)) {\n    await writeArray(obj, buffer, transform);\n  }\n  buffer.push(\"\\nendobj\\n\");\n}\nasync function writeDict(dict, buffer, transform) {\n  buffer.push(\"<<\");\n  for (const key of dict.getKeys()) {\n    buffer.push(` /${escapePDFName(key)} `);\n    await writeValue(dict.getRaw(key), buffer, transform);\n  }\n  buffer.push(\">>\");\n}\nasync function writeStream(stream, buffer, transform) {\n  let bytes = stream.getBytes();\n  const {\n    dict\n  } = stream;\n  const [filter, params] = await Promise.all([dict.getAsync(\"Filter\"), dict.getAsync(\"DecodeParms\")]);\n  const filterZero = Array.isArray(filter) ? await dict.xref.fetchIfRefAsync(filter[0]) : filter;\n  const isFilterZeroFlateDecode = isName(filterZero, \"FlateDecode\");\n  const MIN_LENGTH_FOR_COMPRESSING = 256;\n  if (typeof CompressionStream !== \"undefined\" && (bytes.length >= MIN_LENGTH_FOR_COMPRESSING || isFilterZeroFlateDecode)) {\n    try {\n      const cs = new CompressionStream(\"deflate\");\n      const writer = cs.writable.getWriter();\n      writer.write(bytes);\n      writer.close();\n      const buf = await new Response(cs.readable).arrayBuffer();\n      bytes = new Uint8Array(buf);\n      let newFilter, newParams;\n      if (!filter) {\n        newFilter = Name.get(\"FlateDecode\");\n      } else if (!isFilterZeroFlateDecode) {\n        newFilter = Array.isArray(filter) ? [Name.get(\"FlateDecode\"), ...filter] : [Name.get(\"FlateDecode\"), filter];\n        if (params) {\n          newParams = Array.isArray(params) ? [null, ...params] : [null, params];\n        }\n      }\n      if (newFilter) {\n        dict.set(\"Filter\", newFilter);\n      }\n      if (newParams) {\n        dict.set(\"DecodeParms\", newParams);\n      }\n    } catch (ex) {\n      info(`writeStream - cannot compress data: \"${ex}\".`);\n    }\n  }\n  let string = bytesToString(bytes);\n  if (transform) {\n    string = transform.encryptString(string);\n  }\n  dict.set(\"Length\", string.length);\n  await writeDict(dict, buffer, transform);\n  buffer.push(\" stream\\n\", string, \"\\nendstream\");\n}\nasync function writeArray(array, buffer, transform) {\n  buffer.push(\"[\");\n  let first = true;\n  for (const val of array) {\n    if (!first) {\n      buffer.push(\" \");\n    } else {\n      first = false;\n    }\n    await writeValue(val, buffer, transform);\n  }\n  buffer.push(\"]\");\n}\nasync function writeValue(value, buffer, transform) {\n  if (value instanceof Name) {\n    buffer.push(`/${escapePDFName(value.name)}`);\n  } else if (value instanceof Ref) {\n    buffer.push(`${value.num} ${value.gen} R`);\n  } else if (Array.isArray(value)) {\n    await writeArray(value, buffer, transform);\n  } else if (typeof value === \"string\") {\n    if (transform) {\n      value = transform.encryptString(value);\n    }\n    buffer.push(`(${escapeString(value)})`);\n  } else if (typeof value === \"number\") {\n    buffer.push(numberToString(value));\n  } else if (typeof value === \"boolean\") {\n    buffer.push(value.toString());\n  } else if (value instanceof Dict) {\n    await writeDict(value, buffer, transform);\n  } else if (value instanceof BaseStream) {\n    await writeStream(value, buffer, transform);\n  } else if (value === null) {\n    buffer.push(\"null\");\n  } else {\n    warn(`Unhandled value in writer: ${typeof value}, please file a bug.`);\n  }\n}\nfunction writeInt(number, size, offset, buffer) {\n  for (let i = size + offset - 1; i > offset - 1; i--) {\n    buffer[i] = number & 0xff;\n    number >>= 8;\n  }\n  return offset + size;\n}\nfunction writeString(string, offset, buffer) {\n  for (let i = 0, len = string.length; i < len; i++) {\n    buffer[offset + i] = string.charCodeAt(i) & 0xff;\n  }\n}\nfunction computeMD5(filesize, xrefInfo) {\n  const time = Math.floor(Date.now() / 1000);\n  const filename = xrefInfo.filename || \"\";\n  const md5Buffer = [time.toString(), filename, filesize.toString()];\n  let md5BufferLen = md5Buffer.reduce((a, str) => a + str.length, 0);\n  for (const value of Object.values(xrefInfo.info)) {\n    md5Buffer.push(value);\n    md5BufferLen += value.length;\n  }\n  const array = new Uint8Array(md5BufferLen);\n  let offset = 0;\n  for (const str of md5Buffer) {\n    writeString(str, offset, array);\n    offset += str.length;\n  }\n  return bytesToString(calculateMD5(array));\n}\nfunction writeXFADataForAcroform(str, newRefs) {\n  const xml = new SimpleXMLParser({\n    hasAttributes: true\n  }).parseFromString(str);\n  for (const {\n    xfa\n  } of newRefs) {\n    if (!xfa) {\n      continue;\n    }\n    const {\n      path,\n      value\n    } = xfa;\n    if (!path) {\n      continue;\n    }\n    const nodePath = parseXFAPath(path);\n    let node = xml.documentElement.searchNode(nodePath, 0);\n    if (!node && nodePath.length > 1) {\n      node = xml.documentElement.searchNode([nodePath.at(-1)], 0);\n    }\n    if (node) {\n      node.childNodes = Array.isArray(value) ? value.map(val => new SimpleDOMNode(\"value\", val)) : [new SimpleDOMNode(\"#text\", value)];\n    } else {\n      warn(`Node not found for path: ${path}`);\n    }\n  }\n  const buffer = [];\n  xml.documentElement.dump(buffer);\n  return buffer.join(\"\");\n}\nasync function updateAcroform({\n  xref,\n  acroForm,\n  acroFormRef,\n  hasXfa,\n  hasXfaDatasetsEntry,\n  xfaDatasetsRef,\n  needAppearances,\n  newRefs\n}) {\n  if (hasXfa && !hasXfaDatasetsEntry && !xfaDatasetsRef) {\n    warn(\"XFA - Cannot save it\");\n  }\n  if (!needAppearances && (!hasXfa || !xfaDatasetsRef || hasXfaDatasetsEntry)) {\n    return;\n  }\n  const dict = acroForm.clone();\n  if (hasXfa && !hasXfaDatasetsEntry) {\n    const newXfa = acroForm.get(\"XFA\").slice();\n    newXfa.splice(2, 0, \"datasets\");\n    newXfa.splice(3, 0, xfaDatasetsRef);\n    dict.set(\"XFA\", newXfa);\n  }\n  if (needAppearances) {\n    dict.set(\"NeedAppearances\", true);\n  }\n  const buffer = [];\n  await writeObject(acroFormRef, dict, buffer, xref);\n  newRefs.push({\n    ref: acroFormRef,\n    data: buffer.join(\"\")\n  });\n}\nfunction updateXFA({\n  xfaData,\n  xfaDatasetsRef,\n  newRefs,\n  xref\n}) {\n  if (xfaData === null) {\n    const datasets = xref.fetchIfRef(xfaDatasetsRef);\n    xfaData = writeXFADataForAcroform(datasets.getString(), newRefs);\n  }\n  const encrypt = xref.encrypt;\n  if (encrypt) {\n    const transform = encrypt.createCipherTransform(xfaDatasetsRef.num, xfaDatasetsRef.gen);\n    xfaData = transform.encryptString(xfaData);\n  }\n  const data = `${xfaDatasetsRef.num} ${xfaDatasetsRef.gen} obj\\n` + `<< /Type /EmbeddedFile /Length ${xfaData.length}>>\\nstream\\n` + xfaData + \"\\nendstream\\nendobj\\n\";\n  newRefs.push({\n    ref: xfaDatasetsRef,\n    data\n  });\n}\nasync function incrementalUpdate({\n  originalData,\n  xrefInfo,\n  newRefs,\n  xref = null,\n  hasXfa = false,\n  xfaDatasetsRef = null,\n  hasXfaDatasetsEntry = false,\n  needAppearances,\n  acroFormRef = null,\n  acroForm = null,\n  xfaData = null\n}) {\n  await updateAcroform({\n    xref,\n    acroForm,\n    acroFormRef,\n    hasXfa,\n    hasXfaDatasetsEntry,\n    xfaDatasetsRef,\n    needAppearances,\n    newRefs\n  });\n  if (hasXfa) {\n    updateXFA({\n      xfaData,\n      xfaDatasetsRef,\n      newRefs,\n      xref\n    });\n  }\n  const newXref = new Dict(null);\n  const refForXrefTable = xrefInfo.newRef;\n  let buffer, baseOffset;\n  const lastByte = originalData.at(-1);\n  if (lastByte === 0x0a || lastByte === 0x0d) {\n    buffer = [];\n    baseOffset = originalData.length;\n  } else {\n    buffer = [\"\\n\"];\n    baseOffset = originalData.length + 1;\n  }\n  newXref.set(\"Size\", refForXrefTable.num + 1);\n  newXref.set(\"Prev\", xrefInfo.startXRef);\n  newXref.set(\"Type\", Name.get(\"XRef\"));\n  if (xrefInfo.rootRef !== null) {\n    newXref.set(\"Root\", xrefInfo.rootRef);\n  }\n  if (xrefInfo.infoRef !== null) {\n    newXref.set(\"Info\", xrefInfo.infoRef);\n  }\n  if (xrefInfo.encryptRef !== null) {\n    newXref.set(\"Encrypt\", xrefInfo.encryptRef);\n  }\n  newRefs.push({\n    ref: refForXrefTable,\n    data: \"\"\n  });\n  newRefs = newRefs.sort((a, b) => {\n    return a.ref.num - b.ref.num;\n  });\n  const xrefTableData = [[0, 1, 0xffff]];\n  const indexes = [0, 1];\n  let maxOffset = 0;\n  for (const {\n    ref,\n    data\n  } of newRefs) {\n    maxOffset = Math.max(maxOffset, baseOffset);\n    xrefTableData.push([1, baseOffset, Math.min(ref.gen, 0xffff)]);\n    baseOffset += data.length;\n    indexes.push(ref.num, 1);\n    buffer.push(data);\n  }\n  newXref.set(\"Index\", indexes);\n  if (Array.isArray(xrefInfo.fileIds) && xrefInfo.fileIds.length > 0) {\n    const md5 = computeMD5(baseOffset, xrefInfo);\n    newXref.set(\"ID\", [xrefInfo.fileIds[0], md5]);\n  }\n  const offsetSize = Math.ceil(Math.log2(maxOffset) / 8);\n  const sizes = [1, offsetSize, 2];\n  const structSize = sizes[0] + sizes[1] + sizes[2];\n  const tableLength = structSize * xrefTableData.length;\n  newXref.set(\"W\", sizes);\n  newXref.set(\"Length\", tableLength);\n  buffer.push(`${refForXrefTable.num} ${refForXrefTable.gen} obj\\n`);\n  await writeDict(newXref, buffer, null);\n  buffer.push(\" stream\\n\");\n  const bufferLen = buffer.reduce((a, str) => a + str.length, 0);\n  const footer = `\\nendstream\\nendobj\\nstartxref\\n${baseOffset}\\n%%EOF\\n`;\n  const array = new Uint8Array(originalData.length + bufferLen + tableLength + footer.length);\n  array.set(originalData);\n  let offset = originalData.length;\n  for (const str of buffer) {\n    writeString(str, offset, array);\n    offset += str.length;\n  }\n  for (const [type, objOffset, gen] of xrefTableData) {\n    offset = writeInt(type, sizes[0], offset, array);\n    offset = writeInt(objOffset, sizes[1], offset, array);\n    offset = writeInt(gen, sizes[2], offset, array);\n  }\n  writeString(footer, offset, array);\n  return array;\n}\n\n;// CONCATENATED MODULE: ./src/core/struct_tree.js\n\n\n\n\nconst MAX_DEPTH = 40;\nconst StructElementType = {\n  PAGE_CONTENT: 1,\n  STREAM_CONTENT: 2,\n  OBJECT: 3,\n  ANNOTATION: 4,\n  ELEMENT: 5\n};\nclass StructTreeRoot {\n  constructor(rootDict, rootRef) {\n    this.dict = rootDict;\n    this.ref = rootRef instanceof Ref ? rootRef : null;\n    this.roleMap = new Map();\n    this.structParentIds = null;\n  }\n  init() {\n    this.readRoleMap();\n  }\n  #addIdToPage(pageRef, id, type) {\n    if (!(pageRef instanceof Ref) || id < 0) {\n      return;\n    }\n    this.structParentIds ||= new RefSetCache();\n    let ids = this.structParentIds.get(pageRef);\n    if (!ids) {\n      ids = [];\n      this.structParentIds.put(pageRef, ids);\n    }\n    ids.push([id, type]);\n  }\n  addAnnotationIdToPage(pageRef, id) {\n    this.#addIdToPage(pageRef, id, StructElementType.ANNOTATION);\n  }\n  readRoleMap() {\n    const roleMapDict = this.dict.get(\"RoleMap\");\n    if (!(roleMapDict instanceof Dict)) {\n      return;\n    }\n    roleMapDict.forEach((key, value) => {\n      if (!(value instanceof Name)) {\n        return;\n      }\n      this.roleMap.set(key, value.name);\n    });\n  }\n  static async canCreateStructureTree({\n    catalogRef,\n    pdfManager,\n    newAnnotationsByPage\n  }) {\n    if (!(catalogRef instanceof Ref)) {\n      warn(\"Cannot save the struct tree: no catalog reference.\");\n      return false;\n    }\n    let nextKey = 0;\n    let hasNothingToUpdate = true;\n    for (const [pageIndex, elements] of newAnnotationsByPage) {\n      const {\n        ref: pageRef\n      } = await pdfManager.getPage(pageIndex);\n      if (!(pageRef instanceof Ref)) {\n        warn(`Cannot save the struct tree: page ${pageIndex} has no ref.`);\n        hasNothingToUpdate = true;\n        break;\n      }\n      for (const element of elements) {\n        if (element.accessibilityData?.type) {\n          element.parentTreeId = nextKey++;\n          hasNothingToUpdate = false;\n        }\n      }\n    }\n    if (hasNothingToUpdate) {\n      for (const elements of newAnnotationsByPage.values()) {\n        for (const element of elements) {\n          delete element.parentTreeId;\n        }\n      }\n      return false;\n    }\n    return true;\n  }\n  static async createStructureTree({\n    newAnnotationsByPage,\n    xref,\n    catalogRef,\n    pdfManager,\n    newRefs\n  }) {\n    const root = pdfManager.catalog.cloneDict();\n    const structTreeRootRef = xref.getNewTemporaryRef();\n    root.set(\"StructTreeRoot\", structTreeRootRef);\n    const buffer = [];\n    await writeObject(catalogRef, root, buffer, xref);\n    newRefs.push({\n      ref: catalogRef,\n      data: buffer.join(\"\")\n    });\n    const structTreeRoot = new Dict(xref);\n    structTreeRoot.set(\"Type\", Name.get(\"StructTreeRoot\"));\n    const parentTreeRef = xref.getNewTemporaryRef();\n    structTreeRoot.set(\"ParentTree\", parentTreeRef);\n    const kids = [];\n    structTreeRoot.set(\"K\", kids);\n    const parentTree = new Dict(xref);\n    const nums = [];\n    parentTree.set(\"Nums\", nums);\n    const nextKey = await this.#writeKids({\n      newAnnotationsByPage,\n      structTreeRootRef,\n      kids,\n      nums,\n      xref,\n      pdfManager,\n      newRefs,\n      buffer\n    });\n    structTreeRoot.set(\"ParentTreeNextKey\", nextKey);\n    buffer.length = 0;\n    await writeObject(parentTreeRef, parentTree, buffer, xref);\n    newRefs.push({\n      ref: parentTreeRef,\n      data: buffer.join(\"\")\n    });\n    buffer.length = 0;\n    await writeObject(structTreeRootRef, structTreeRoot, buffer, xref);\n    newRefs.push({\n      ref: structTreeRootRef,\n      data: buffer.join(\"\")\n    });\n  }\n  async canUpdateStructTree({\n    pdfManager,\n    xref,\n    newAnnotationsByPage\n  }) {\n    if (!this.ref) {\n      warn(\"Cannot update the struct tree: no root reference.\");\n      return false;\n    }\n    let nextKey = this.dict.get(\"ParentTreeNextKey\");\n    if (!Number.isInteger(nextKey) || nextKey < 0) {\n      warn(\"Cannot update the struct tree: invalid next key.\");\n      return false;\n    }\n    const parentTree = this.dict.get(\"ParentTree\");\n    if (!(parentTree instanceof Dict)) {\n      warn(\"Cannot update the struct tree: ParentTree isn't a dict.\");\n      return false;\n    }\n    const nums = parentTree.get(\"Nums\");\n    if (!Array.isArray(nums)) {\n      warn(\"Cannot update the struct tree: nums isn't an array.\");\n      return false;\n    }\n    const numberTree = new NumberTree(parentTree, xref);\n    for (const pageIndex of newAnnotationsByPage.keys()) {\n      const {\n        pageDict\n      } = await pdfManager.getPage(pageIndex);\n      if (!pageDict.has(\"StructParents\")) {\n        continue;\n      }\n      const id = pageDict.get(\"StructParents\");\n      if (!Number.isInteger(id) || !Array.isArray(numberTree.get(id))) {\n        warn(`Cannot save the struct tree: page ${pageIndex} has a wrong id.`);\n        return false;\n      }\n    }\n    let hasNothingToUpdate = true;\n    for (const [pageIndex, elements] of newAnnotationsByPage) {\n      const {\n        pageDict\n      } = await pdfManager.getPage(pageIndex);\n      StructTreeRoot.#collectParents({\n        elements,\n        xref: this.dict.xref,\n        pageDict,\n        numberTree\n      });\n      for (const element of elements) {\n        if (element.accessibilityData?.type) {\n          element.parentTreeId = nextKey++;\n          hasNothingToUpdate = false;\n        }\n      }\n    }\n    if (hasNothingToUpdate) {\n      for (const elements of newAnnotationsByPage.values()) {\n        for (const element of elements) {\n          delete element.parentTreeId;\n          delete element.structTreeParent;\n        }\n      }\n      return false;\n    }\n    return true;\n  }\n  async updateStructureTree({\n    newAnnotationsByPage,\n    pdfManager,\n    newRefs\n  }) {\n    const xref = this.dict.xref;\n    const structTreeRoot = this.dict.clone();\n    const structTreeRootRef = this.ref;\n    let parentTreeRef = structTreeRoot.getRaw(\"ParentTree\");\n    let parentTree;\n    if (parentTreeRef instanceof Ref) {\n      parentTree = xref.fetch(parentTreeRef);\n    } else {\n      parentTree = parentTreeRef;\n      parentTreeRef = xref.getNewTemporaryRef();\n      structTreeRoot.set(\"ParentTree\", parentTreeRef);\n    }\n    parentTree = parentTree.clone();\n    let nums = parentTree.getRaw(\"Nums\");\n    let numsRef = null;\n    if (nums instanceof Ref) {\n      numsRef = nums;\n      nums = xref.fetch(numsRef);\n    }\n    nums = nums.slice();\n    if (!numsRef) {\n      parentTree.set(\"Nums\", nums);\n    }\n    let kids = structTreeRoot.getRaw(\"K\");\n    let kidsRef = null;\n    if (kids instanceof Ref) {\n      kidsRef = kids;\n      kids = xref.fetch(kidsRef);\n    } else {\n      kidsRef = xref.getNewTemporaryRef();\n      structTreeRoot.set(\"K\", kidsRef);\n    }\n    kids = Array.isArray(kids) ? kids.slice() : [kids];\n    const buffer = [];\n    const newNextkey = await StructTreeRoot.#writeKids({\n      newAnnotationsByPage,\n      structTreeRootRef,\n      kids,\n      nums,\n      xref,\n      pdfManager,\n      newRefs,\n      buffer\n    });\n    structTreeRoot.set(\"ParentTreeNextKey\", newNextkey);\n    buffer.length = 0;\n    await writeObject(kidsRef, kids, buffer, xref);\n    newRefs.push({\n      ref: kidsRef,\n      data: buffer.join(\"\")\n    });\n    if (numsRef) {\n      buffer.length = 0;\n      await writeObject(numsRef, nums, buffer, xref);\n      newRefs.push({\n        ref: numsRef,\n        data: buffer.join(\"\")\n      });\n    }\n    buffer.length = 0;\n    await writeObject(parentTreeRef, parentTree, buffer, xref);\n    newRefs.push({\n      ref: parentTreeRef,\n      data: buffer.join(\"\")\n    });\n    buffer.length = 0;\n    await writeObject(structTreeRootRef, structTreeRoot, buffer, xref);\n    newRefs.push({\n      ref: structTreeRootRef,\n      data: buffer.join(\"\")\n    });\n  }\n  static async #writeKids({\n    newAnnotationsByPage,\n    structTreeRootRef,\n    kids,\n    nums,\n    xref,\n    pdfManager,\n    newRefs,\n    buffer\n  }) {\n    const objr = Name.get(\"OBJR\");\n    let nextKey = -Infinity;\n    for (const [pageIndex, elements] of newAnnotationsByPage) {\n      const {\n        ref: pageRef\n      } = await pdfManager.getPage(pageIndex);\n      const isPageRef = pageRef instanceof Ref;\n      for (const {\n        accessibilityData,\n        ref,\n        parentTreeId,\n        structTreeParent\n      } of elements) {\n        if (!accessibilityData?.type) {\n          continue;\n        }\n        const {\n          type,\n          title,\n          lang,\n          alt,\n          expanded,\n          actualText\n        } = accessibilityData;\n        nextKey = Math.max(nextKey, parentTreeId);\n        const tagRef = xref.getNewTemporaryRef();\n        const tagDict = new Dict(xref);\n        tagDict.set(\"S\", Name.get(type));\n        if (title) {\n          tagDict.set(\"T\", title);\n        }\n        if (lang) {\n          tagDict.set(\"Lang\", lang);\n        }\n        if (alt) {\n          tagDict.set(\"Alt\", alt);\n        }\n        if (expanded) {\n          tagDict.set(\"E\", expanded);\n        }\n        if (actualText) {\n          tagDict.set(\"ActualText\", actualText);\n        }\n        if (structTreeParent) {\n          await this.#updateParentTag({\n            structTreeParent,\n            tagDict,\n            newTagRef: tagRef,\n            fallbackRef: structTreeRootRef,\n            xref,\n            newRefs,\n            buffer\n          });\n        } else {\n          tagDict.set(\"P\", structTreeRootRef);\n        }\n        const objDict = new Dict(xref);\n        tagDict.set(\"K\", objDict);\n        objDict.set(\"Type\", objr);\n        if (isPageRef) {\n          objDict.set(\"Pg\", pageRef);\n        }\n        objDict.set(\"Obj\", ref);\n        buffer.length = 0;\n        await writeObject(tagRef, tagDict, buffer, xref);\n        newRefs.push({\n          ref: tagRef,\n          data: buffer.join(\"\")\n        });\n        nums.push(parentTreeId, tagRef);\n        kids.push(tagRef);\n      }\n    }\n    return nextKey + 1;\n  }\n  static #collectParents({\n    elements,\n    xref,\n    pageDict,\n    numberTree\n  }) {\n    const idToElement = new Map();\n    for (const element of elements) {\n      if (element.structTreeParentId) {\n        const id = parseInt(element.structTreeParentId.split(\"_mc\")[1], 10);\n        idToElement.set(id, element);\n      }\n    }\n    const id = pageDict.get(\"StructParents\");\n    if (!Number.isInteger(id)) {\n      return;\n    }\n    const parentArray = numberTree.get(id);\n    const updateElement = (kid, pageKid, kidRef) => {\n      const element = idToElement.get(kid);\n      if (element) {\n        const parentRef = pageKid.getRaw(\"P\");\n        const parentDict = xref.fetchIfRef(parentRef);\n        if (parentRef instanceof Ref && parentDict instanceof Dict) {\n          element.structTreeParent = {\n            ref: kidRef,\n            dict: pageKid\n          };\n        }\n        return true;\n      }\n      return false;\n    };\n    for (const kidRef of parentArray) {\n      if (!(kidRef instanceof Ref)) {\n        continue;\n      }\n      const pageKid = xref.fetch(kidRef);\n      const k = pageKid.get(\"K\");\n      if (Number.isInteger(k)) {\n        updateElement(k, pageKid, kidRef);\n        continue;\n      }\n      if (!Array.isArray(k)) {\n        continue;\n      }\n      for (let kid of k) {\n        kid = xref.fetchIfRef(kid);\n        if (Number.isInteger(kid) && updateElement(kid, pageKid, kidRef)) {\n          break;\n        }\n      }\n    }\n  }\n  static async #updateParentTag({\n    structTreeParent: {\n      ref,\n      dict\n    },\n    tagDict,\n    newTagRef,\n    fallbackRef,\n    xref,\n    newRefs,\n    buffer\n  }) {\n    const parentRef = dict.getRaw(\"P\");\n    let parentDict = xref.fetchIfRef(parentRef);\n    tagDict.set(\"P\", parentRef);\n    let saveParentDict = false;\n    let parentKids;\n    let parentKidsRef = parentDict.getRaw(\"K\");\n    if (!(parentKidsRef instanceof Ref)) {\n      parentKids = parentKidsRef;\n      parentKidsRef = xref.getNewTemporaryRef();\n      parentDict = parentDict.clone();\n      parentDict.set(\"K\", parentKidsRef);\n      saveParentDict = true;\n    } else {\n      parentKids = xref.fetch(parentKidsRef);\n    }\n    if (Array.isArray(parentKids)) {\n      const index = parentKids.indexOf(ref);\n      if (index >= 0) {\n        parentKids = parentKids.slice();\n        parentKids.splice(index + 1, 0, newTagRef);\n      } else {\n        warn(\"Cannot update the struct tree: parent kid not found.\");\n        tagDict.set(\"P\", fallbackRef);\n        return;\n      }\n    } else if (parentKids instanceof Dict) {\n      parentKids = [parentKidsRef, newTagRef];\n      parentKidsRef = xref.getNewTemporaryRef();\n      parentDict.set(\"K\", parentKidsRef);\n      saveParentDict = true;\n    }\n    buffer.length = 0;\n    await writeObject(parentKidsRef, parentKids, buffer, xref);\n    newRefs.push({\n      ref: parentKidsRef,\n      data: buffer.join(\"\")\n    });\n    if (!saveParentDict) {\n      return;\n    }\n    buffer.length = 0;\n    await writeObject(parentRef, parentDict, buffer, xref);\n    newRefs.push({\n      ref: parentRef,\n      data: buffer.join(\"\")\n    });\n  }\n}\nclass StructElementNode {\n  constructor(tree, dict) {\n    this.tree = tree;\n    this.dict = dict;\n    this.kids = [];\n    this.parseKids();\n  }\n  get role() {\n    const nameObj = this.dict.get(\"S\");\n    const name = nameObj instanceof Name ? nameObj.name : \"\";\n    const {\n      root\n    } = this.tree;\n    if (root.roleMap.has(name)) {\n      return root.roleMap.get(name);\n    }\n    return name;\n  }\n  parseKids() {\n    let pageObjId = null;\n    const objRef = this.dict.getRaw(\"Pg\");\n    if (objRef instanceof Ref) {\n      pageObjId = objRef.toString();\n    }\n    const kids = this.dict.get(\"K\");\n    if (Array.isArray(kids)) {\n      for (const kid of kids) {\n        const element = this.parseKid(pageObjId, kid);\n        if (element) {\n          this.kids.push(element);\n        }\n      }\n    } else {\n      const element = this.parseKid(pageObjId, kids);\n      if (element) {\n        this.kids.push(element);\n      }\n    }\n  }\n  parseKid(pageObjId, kid) {\n    if (Number.isInteger(kid)) {\n      if (this.tree.pageDict.objId !== pageObjId) {\n        return null;\n      }\n      return new StructElement({\n        type: StructElementType.PAGE_CONTENT,\n        mcid: kid,\n        pageObjId\n      });\n    }\n    let kidDict = null;\n    if (kid instanceof Ref) {\n      kidDict = this.dict.xref.fetch(kid);\n    } else if (kid instanceof Dict) {\n      kidDict = kid;\n    }\n    if (!kidDict) {\n      return null;\n    }\n    const pageRef = kidDict.getRaw(\"Pg\");\n    if (pageRef instanceof Ref) {\n      pageObjId = pageRef.toString();\n    }\n    const type = kidDict.get(\"Type\") instanceof Name ? kidDict.get(\"Type\").name : null;\n    if (type === \"MCR\") {\n      if (this.tree.pageDict.objId !== pageObjId) {\n        return null;\n      }\n      const kidRef = kidDict.getRaw(\"Stm\");\n      return new StructElement({\n        type: StructElementType.STREAM_CONTENT,\n        refObjId: kidRef instanceof Ref ? kidRef.toString() : null,\n        pageObjId,\n        mcid: kidDict.get(\"MCID\")\n      });\n    }\n    if (type === \"OBJR\") {\n      if (this.tree.pageDict.objId !== pageObjId) {\n        return null;\n      }\n      const kidRef = kidDict.getRaw(\"Obj\");\n      return new StructElement({\n        type: StructElementType.OBJECT,\n        refObjId: kidRef instanceof Ref ? kidRef.toString() : null,\n        pageObjId\n      });\n    }\n    return new StructElement({\n      type: StructElementType.ELEMENT,\n      dict: kidDict\n    });\n  }\n}\nclass StructElement {\n  constructor({\n    type,\n    dict = null,\n    mcid = null,\n    pageObjId = null,\n    refObjId = null\n  }) {\n    this.type = type;\n    this.dict = dict;\n    this.mcid = mcid;\n    this.pageObjId = pageObjId;\n    this.refObjId = refObjId;\n    this.parentNode = null;\n  }\n}\nclass StructTreePage {\n  constructor(structTreeRoot, pageDict) {\n    this.root = structTreeRoot;\n    this.rootDict = structTreeRoot ? structTreeRoot.dict : null;\n    this.pageDict = pageDict;\n    this.nodes = [];\n  }\n  parse(pageRef) {\n    if (!this.root || !this.rootDict) {\n      return;\n    }\n    const parentTree = this.rootDict.get(\"ParentTree\");\n    if (!parentTree) {\n      return;\n    }\n    const id = this.pageDict.get(\"StructParents\");\n    const ids = pageRef instanceof Ref && this.root.structParentIds?.get(pageRef);\n    if (!Number.isInteger(id) && !ids) {\n      return;\n    }\n    const map = new Map();\n    const numberTree = new NumberTree(parentTree, this.rootDict.xref);\n    if (Number.isInteger(id)) {\n      const parentArray = numberTree.get(id);\n      if (Array.isArray(parentArray)) {\n        for (const ref of parentArray) {\n          if (ref instanceof Ref) {\n            this.addNode(this.rootDict.xref.fetch(ref), map);\n          }\n        }\n      }\n    }\n    if (!ids) {\n      return;\n    }\n    for (const [elemId, type] of ids) {\n      const obj = numberTree.get(elemId);\n      if (obj) {\n        const elem = this.addNode(this.rootDict.xref.fetchIfRef(obj), map);\n        if (elem?.kids?.length === 1 && elem.kids[0].type === StructElementType.OBJECT) {\n          elem.kids[0].type = type;\n        }\n      }\n    }\n  }\n  addNode(dict, map, level = 0) {\n    if (level > MAX_DEPTH) {\n      warn(\"StructTree MAX_DEPTH reached.\");\n      return null;\n    }\n    if (map.has(dict)) {\n      return map.get(dict);\n    }\n    const element = new StructElementNode(this, dict);\n    map.set(dict, element);\n    const parent = dict.get(\"P\");\n    if (!parent || isName(parent.get(\"Type\"), \"StructTreeRoot\")) {\n      if (!this.addTopLevelNode(dict, element)) {\n        map.delete(dict);\n      }\n      return element;\n    }\n    const parentNode = this.addNode(parent, map, level + 1);\n    if (!parentNode) {\n      return element;\n    }\n    let save = false;\n    for (const kid of parentNode.kids) {\n      if (kid.type === StructElementType.ELEMENT && kid.dict === dict) {\n        kid.parentNode = element;\n        save = true;\n      }\n    }\n    if (!save) {\n      map.delete(dict);\n    }\n    return element;\n  }\n  addTopLevelNode(dict, element) {\n    const obj = this.rootDict.get(\"K\");\n    if (!obj) {\n      return false;\n    }\n    if (obj instanceof Dict) {\n      if (obj.objId !== dict.objId) {\n        return false;\n      }\n      this.nodes[0] = element;\n      return true;\n    }\n    if (!Array.isArray(obj)) {\n      return true;\n    }\n    let save = false;\n    for (let i = 0; i < obj.length; i++) {\n      const kidRef = obj[i];\n      if (kidRef?.toString() === dict.objId) {\n        this.nodes[i] = element;\n        save = true;\n      }\n    }\n    return save;\n  }\n  get serializable() {\n    function nodeToSerializable(node, parent, level = 0) {\n      if (level > MAX_DEPTH) {\n        warn(\"StructTree too deep to be fully serialized.\");\n        return;\n      }\n      const obj = Object.create(null);\n      obj.role = node.role;\n      obj.children = [];\n      parent.children.push(obj);\n      const alt = node.dict.get(\"Alt\");\n      if (typeof alt === \"string\") {\n        obj.alt = stringToPDFString(alt);\n      }\n      const lang = node.dict.get(\"Lang\");\n      if (typeof lang === \"string\") {\n        obj.lang = stringToPDFString(lang);\n      }\n      for (const kid of node.kids) {\n        const kidElement = kid.type === StructElementType.ELEMENT ? kid.parentNode : null;\n        if (kidElement) {\n          nodeToSerializable(kidElement, obj, level + 1);\n          continue;\n        } else if (kid.type === StructElementType.PAGE_CONTENT || kid.type === StructElementType.STREAM_CONTENT) {\n          obj.children.push({\n            type: \"content\",\n            id: `p${kid.pageObjId}_mc${kid.mcid}`\n          });\n        } else if (kid.type === StructElementType.OBJECT) {\n          obj.children.push({\n            type: \"object\",\n            id: kid.refObjId\n          });\n        } else if (kid.type === StructElementType.ANNOTATION) {\n          obj.children.push({\n            type: \"annotation\",\n            id: `${AnnotationPrefix}${kid.refObjId}`\n          });\n        }\n      }\n    }\n    const root = Object.create(null);\n    root.children = [];\n    root.role = \"Root\";\n    for (const child of this.nodes) {\n      if (!child) {\n        continue;\n      }\n      nodeToSerializable(child, root);\n    }\n    return root;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/catalog.js\n\n\n\n\n\n\n\n\n\n\n\nfunction fetchDestination(dest) {\n  if (dest instanceof Dict) {\n    dest = dest.get(\"D\");\n  }\n  return Array.isArray(dest) ? dest : null;\n}\nfunction fetchRemoteDest(action) {\n  let dest = action.get(\"D\");\n  if (dest) {\n    if (dest instanceof Name) {\n      dest = dest.name;\n    }\n    if (typeof dest === \"string\") {\n      return stringToPDFString(dest);\n    } else if (Array.isArray(dest)) {\n      return JSON.stringify(dest);\n    }\n  }\n  return null;\n}\nclass Catalog {\n  constructor(pdfManager, xref) {\n    this.pdfManager = pdfManager;\n    this.xref = xref;\n    this._catDict = xref.getCatalogObj();\n    if (!(this._catDict instanceof Dict)) {\n      throw new FormatError(\"Catalog object is not a dictionary.\");\n    }\n    this.toplevelPagesDict;\n    this._actualNumPages = null;\n    this.fontCache = new RefSetCache();\n    this.builtInCMapCache = new Map();\n    this.standardFontDataCache = new Map();\n    this.globalImageCache = new GlobalImageCache();\n    this.pageKidsCountCache = new RefSetCache();\n    this.pageIndexCache = new RefSetCache();\n    this.nonBlendModesSet = new RefSet();\n    this.systemFontCache = new Map();\n  }\n  cloneDict() {\n    return this._catDict.clone();\n  }\n  get version() {\n    const version = this._catDict.get(\"Version\");\n    if (version instanceof Name) {\n      if (PDF_VERSION_REGEXP.test(version.name)) {\n        return shadow(this, \"version\", version.name);\n      }\n      warn(`Invalid PDF catalog version: ${version.name}`);\n    }\n    return shadow(this, \"version\", null);\n  }\n  get lang() {\n    const lang = this._catDict.get(\"Lang\");\n    return shadow(this, \"lang\", typeof lang === \"string\" ? stringToPDFString(lang) : null);\n  }\n  get needsRendering() {\n    const needsRendering = this._catDict.get(\"NeedsRendering\");\n    return shadow(this, \"needsRendering\", typeof needsRendering === \"boolean\" ? needsRendering : false);\n  }\n  get collection() {\n    let collection = null;\n    try {\n      const obj = this._catDict.get(\"Collection\");\n      if (obj instanceof Dict && obj.size > 0) {\n        collection = obj;\n      }\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      info(\"Cannot fetch Collection entry; assuming no collection is present.\");\n    }\n    return shadow(this, \"collection\", collection);\n  }\n  get acroForm() {\n    let acroForm = null;\n    try {\n      const obj = this._catDict.get(\"AcroForm\");\n      if (obj instanceof Dict && obj.size > 0) {\n        acroForm = obj;\n      }\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      info(\"Cannot fetch AcroForm entry; assuming no forms are present.\");\n    }\n    return shadow(this, \"acroForm\", acroForm);\n  }\n  get acroFormRef() {\n    const value = this._catDict.getRaw(\"AcroForm\");\n    return shadow(this, \"acroFormRef\", value instanceof Ref ? value : null);\n  }\n  get metadata() {\n    const streamRef = this._catDict.getRaw(\"Metadata\");\n    if (!(streamRef instanceof Ref)) {\n      return shadow(this, \"metadata\", null);\n    }\n    let metadata = null;\n    try {\n      const stream = this.xref.fetch(streamRef, !this.xref.encrypt?.encryptMetadata);\n      if (stream instanceof BaseStream && stream.dict instanceof Dict) {\n        const type = stream.dict.get(\"Type\");\n        const subtype = stream.dict.get(\"Subtype\");\n        if (isName(type, \"Metadata\") && isName(subtype, \"XML\")) {\n          const data = stringToUTF8String(stream.getString());\n          if (data) {\n            metadata = new MetadataParser(data).serializable;\n          }\n        }\n      }\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      info(`Skipping invalid Metadata: \"${ex}\".`);\n    }\n    return shadow(this, \"metadata\", metadata);\n  }\n  get markInfo() {\n    let markInfo = null;\n    try {\n      markInfo = this._readMarkInfo();\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(\"Unable to read mark info.\");\n    }\n    return shadow(this, \"markInfo\", markInfo);\n  }\n  _readMarkInfo() {\n    const obj = this._catDict.get(\"MarkInfo\");\n    if (!(obj instanceof Dict)) {\n      return null;\n    }\n    const markInfo = {\n      Marked: false,\n      UserProperties: false,\n      Suspects: false\n    };\n    for (const key in markInfo) {\n      const value = obj.get(key);\n      if (typeof value === \"boolean\") {\n        markInfo[key] = value;\n      }\n    }\n    return markInfo;\n  }\n  get structTreeRoot() {\n    let structTree = null;\n    try {\n      structTree = this._readStructTreeRoot();\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(\"Unable read to structTreeRoot info.\");\n    }\n    return shadow(this, \"structTreeRoot\", structTree);\n  }\n  _readStructTreeRoot() {\n    const rawObj = this._catDict.getRaw(\"StructTreeRoot\");\n    const obj = this.xref.fetchIfRef(rawObj);\n    if (!(obj instanceof Dict)) {\n      return null;\n    }\n    const root = new StructTreeRoot(obj, rawObj);\n    root.init();\n    return root;\n  }\n  get toplevelPagesDict() {\n    const pagesObj = this._catDict.get(\"Pages\");\n    if (!(pagesObj instanceof Dict)) {\n      throw new FormatError(\"Invalid top-level pages dictionary.\");\n    }\n    return shadow(this, \"toplevelPagesDict\", pagesObj);\n  }\n  get documentOutline() {\n    let obj = null;\n    try {\n      obj = this._readDocumentOutline();\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(\"Unable to read document outline.\");\n    }\n    return shadow(this, \"documentOutline\", obj);\n  }\n  _readDocumentOutline() {\n    let obj = this._catDict.get(\"Outlines\");\n    if (!(obj instanceof Dict)) {\n      return null;\n    }\n    obj = obj.getRaw(\"First\");\n    if (!(obj instanceof Ref)) {\n      return null;\n    }\n    const root = {\n      items: []\n    };\n    const queue = [{\n      obj,\n      parent: root\n    }];\n    const processed = new RefSet();\n    processed.put(obj);\n    const xref = this.xref,\n      blackColor = new Uint8ClampedArray(3);\n    while (queue.length > 0) {\n      const i = queue.shift();\n      const outlineDict = xref.fetchIfRef(i.obj);\n      if (outlineDict === null) {\n        continue;\n      }\n      if (!outlineDict.has(\"Title\")) {\n        throw new FormatError(\"Invalid outline item encountered.\");\n      }\n      const data = {\n        url: null,\n        dest: null,\n        action: null\n      };\n      Catalog.parseDestDictionary({\n        destDict: outlineDict,\n        resultObj: data,\n        docBaseUrl: this.baseUrl,\n        docAttachments: this.attachments\n      });\n      const title = outlineDict.get(\"Title\");\n      const flags = outlineDict.get(\"F\") || 0;\n      const color = outlineDict.getArray(\"C\");\n      const count = outlineDict.get(\"Count\");\n      let rgbColor = blackColor;\n      if (Array.isArray(color) && color.length === 3 && (color[0] !== 0 || color[1] !== 0 || color[2] !== 0)) {\n        rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0);\n      }\n      const outlineItem = {\n        action: data.action,\n        attachment: data.attachment,\n        dest: data.dest,\n        url: data.url,\n        unsafeUrl: data.unsafeUrl,\n        newWindow: data.newWindow,\n        setOCGState: data.setOCGState,\n        title: stringToPDFString(title),\n        color: rgbColor,\n        count: Number.isInteger(count) ? count : undefined,\n        bold: !!(flags & 2),\n        italic: !!(flags & 1),\n        items: []\n      };\n      i.parent.items.push(outlineItem);\n      obj = outlineDict.getRaw(\"First\");\n      if (obj instanceof Ref && !processed.has(obj)) {\n        queue.push({\n          obj,\n          parent: outlineItem\n        });\n        processed.put(obj);\n      }\n      obj = outlineDict.getRaw(\"Next\");\n      if (obj instanceof Ref && !processed.has(obj)) {\n        queue.push({\n          obj,\n          parent: i.parent\n        });\n        processed.put(obj);\n      }\n    }\n    return root.items.length > 0 ? root.items : null;\n  }\n  get permissions() {\n    let permissions = null;\n    try {\n      permissions = this._readPermissions();\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(\"Unable to read permissions.\");\n    }\n    return shadow(this, \"permissions\", permissions);\n  }\n  _readPermissions() {\n    const encrypt = this.xref.trailer.get(\"Encrypt\");\n    if (!(encrypt instanceof Dict)) {\n      return null;\n    }\n    let flags = encrypt.get(\"P\");\n    if (typeof flags !== \"number\") {\n      return null;\n    }\n    flags += 2 ** 32;\n    const permissions = [];\n    for (const key in PermissionFlag) {\n      const value = PermissionFlag[key];\n      if (flags & value) {\n        permissions.push(value);\n      }\n    }\n    return permissions;\n  }\n  get optionalContentConfig() {\n    let config = null;\n    try {\n      const properties = this._catDict.get(\"OCProperties\");\n      if (!properties) {\n        return shadow(this, \"optionalContentConfig\", null);\n      }\n      const defaultConfig = properties.get(\"D\");\n      if (!defaultConfig) {\n        return shadow(this, \"optionalContentConfig\", null);\n      }\n      const groupsData = properties.get(\"OCGs\");\n      if (!Array.isArray(groupsData)) {\n        return shadow(this, \"optionalContentConfig\", null);\n      }\n      const groups = [];\n      const groupRefs = new RefSet();\n      for (const groupRef of groupsData) {\n        if (!(groupRef instanceof Ref) || groupRefs.has(groupRef)) {\n          continue;\n        }\n        groupRefs.put(groupRef);\n        const group = this.xref.fetch(groupRef);\n        groups.push({\n          id: groupRef.toString(),\n          name: typeof group.get(\"Name\") === \"string\" ? stringToPDFString(group.get(\"Name\")) : null,\n          intent: typeof group.get(\"Intent\") === \"string\" ? stringToPDFString(group.get(\"Intent\")) : null\n        });\n      }\n      config = this._readOptionalContentConfig(defaultConfig, groupRefs);\n      config.groups = groups;\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(`Unable to read optional content config: ${ex}`);\n    }\n    return shadow(this, \"optionalContentConfig\", config);\n  }\n  _readOptionalContentConfig(config, contentGroupRefs) {\n    function parseOnOff(refs) {\n      const onParsed = [];\n      if (Array.isArray(refs)) {\n        for (const value of refs) {\n          if (!(value instanceof Ref)) {\n            continue;\n          }\n          if (contentGroupRefs.has(value)) {\n            onParsed.push(value.toString());\n          }\n        }\n      }\n      return onParsed;\n    }\n    function parseOrder(refs, nestedLevels = 0) {\n      if (!Array.isArray(refs)) {\n        return null;\n      }\n      const order = [];\n      for (const value of refs) {\n        if (value instanceof Ref && contentGroupRefs.has(value)) {\n          parsedOrderRefs.put(value);\n          order.push(value.toString());\n          continue;\n        }\n        const nestedOrder = parseNestedOrder(value, nestedLevels);\n        if (nestedOrder) {\n          order.push(nestedOrder);\n        }\n      }\n      if (nestedLevels > 0) {\n        return order;\n      }\n      const hiddenGroups = [];\n      for (const groupRef of contentGroupRefs) {\n        if (parsedOrderRefs.has(groupRef)) {\n          continue;\n        }\n        hiddenGroups.push(groupRef.toString());\n      }\n      if (hiddenGroups.length) {\n        order.push({\n          name: null,\n          order: hiddenGroups\n        });\n      }\n      return order;\n    }\n    function parseNestedOrder(ref, nestedLevels) {\n      if (++nestedLevels > MAX_NESTED_LEVELS) {\n        warn(\"parseNestedOrder - reached MAX_NESTED_LEVELS.\");\n        return null;\n      }\n      const value = xref.fetchIfRef(ref);\n      if (!Array.isArray(value)) {\n        return null;\n      }\n      const nestedName = xref.fetchIfRef(value[0]);\n      if (typeof nestedName !== \"string\") {\n        return null;\n      }\n      const nestedOrder = parseOrder(value.slice(1), nestedLevels);\n      if (!nestedOrder || !nestedOrder.length) {\n        return null;\n      }\n      return {\n        name: stringToPDFString(nestedName),\n        order: nestedOrder\n      };\n    }\n    const xref = this.xref,\n      parsedOrderRefs = new RefSet(),\n      MAX_NESTED_LEVELS = 10;\n    return {\n      name: typeof config.get(\"Name\") === \"string\" ? stringToPDFString(config.get(\"Name\")) : null,\n      creator: typeof config.get(\"Creator\") === \"string\" ? stringToPDFString(config.get(\"Creator\")) : null,\n      baseState: config.get(\"BaseState\") instanceof Name ? config.get(\"BaseState\").name : null,\n      on: parseOnOff(config.get(\"ON\")),\n      off: parseOnOff(config.get(\"OFF\")),\n      order: parseOrder(config.get(\"Order\")),\n      groups: null\n    };\n  }\n  setActualNumPages(num = null) {\n    this._actualNumPages = num;\n  }\n  get hasActualNumPages() {\n    return this._actualNumPages !== null;\n  }\n  get _pagesCount() {\n    const obj = this.toplevelPagesDict.get(\"Count\");\n    if (!Number.isInteger(obj)) {\n      throw new FormatError(\"Page count in top-level pages dictionary is not an integer.\");\n    }\n    return shadow(this, \"_pagesCount\", obj);\n  }\n  get numPages() {\n    return this.hasActualNumPages ? this._actualNumPages : this._pagesCount;\n  }\n  get destinations() {\n    const obj = this._readDests(),\n      dests = Object.create(null);\n    if (obj instanceof NameTree) {\n      for (const [key, value] of obj.getAll()) {\n        const dest = fetchDestination(value);\n        if (dest) {\n          dests[stringToPDFString(key)] = dest;\n        }\n      }\n    } else if (obj instanceof Dict) {\n      obj.forEach(function (key, value) {\n        const dest = fetchDestination(value);\n        if (dest) {\n          dests[key] = dest;\n        }\n      });\n    }\n    return shadow(this, \"destinations\", dests);\n  }\n  getDestination(id) {\n    const obj = this._readDests();\n    if (obj instanceof NameTree) {\n      const dest = fetchDestination(obj.get(id));\n      if (dest) {\n        return dest;\n      }\n      const allDest = this.destinations[id];\n      if (allDest) {\n        warn(`Found \"${id}\" at an incorrect position in the NameTree.`);\n        return allDest;\n      }\n    } else if (obj instanceof Dict) {\n      const dest = fetchDestination(obj.get(id));\n      if (dest) {\n        return dest;\n      }\n    }\n    return null;\n  }\n  _readDests() {\n    const obj = this._catDict.get(\"Names\");\n    if (obj?.has(\"Dests\")) {\n      return new NameTree(obj.getRaw(\"Dests\"), this.xref);\n    } else if (this._catDict.has(\"Dests\")) {\n      return this._catDict.get(\"Dests\");\n    }\n    return undefined;\n  }\n  get pageLabels() {\n    let obj = null;\n    try {\n      obj = this._readPageLabels();\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(\"Unable to read page labels.\");\n    }\n    return shadow(this, \"pageLabels\", obj);\n  }\n  _readPageLabels() {\n    const obj = this._catDict.getRaw(\"PageLabels\");\n    if (!obj) {\n      return null;\n    }\n    const pageLabels = new Array(this.numPages);\n    let style = null,\n      prefix = \"\";\n    const numberTree = new NumberTree(obj, this.xref);\n    const nums = numberTree.getAll();\n    let currentLabel = \"\",\n      currentIndex = 1;\n    for (let i = 0, ii = this.numPages; i < ii; i++) {\n      const labelDict = nums.get(i);\n      if (labelDict !== undefined) {\n        if (!(labelDict instanceof Dict)) {\n          throw new FormatError(\"PageLabel is not a dictionary.\");\n        }\n        if (labelDict.has(\"Type\") && !isName(labelDict.get(\"Type\"), \"PageLabel\")) {\n          throw new FormatError(\"Invalid type in PageLabel dictionary.\");\n        }\n        if (labelDict.has(\"S\")) {\n          const s = labelDict.get(\"S\");\n          if (!(s instanceof Name)) {\n            throw new FormatError(\"Invalid style in PageLabel dictionary.\");\n          }\n          style = s.name;\n        } else {\n          style = null;\n        }\n        if (labelDict.has(\"P\")) {\n          const p = labelDict.get(\"P\");\n          if (typeof p !== \"string\") {\n            throw new FormatError(\"Invalid prefix in PageLabel dictionary.\");\n          }\n          prefix = stringToPDFString(p);\n        } else {\n          prefix = \"\";\n        }\n        if (labelDict.has(\"St\")) {\n          const st = labelDict.get(\"St\");\n          if (!(Number.isInteger(st) && st >= 1)) {\n            throw new FormatError(\"Invalid start in PageLabel dictionary.\");\n          }\n          currentIndex = st;\n        } else {\n          currentIndex = 1;\n        }\n      }\n      switch (style) {\n        case \"D\":\n          currentLabel = currentIndex;\n          break;\n        case \"R\":\n        case \"r\":\n          currentLabel = toRomanNumerals(currentIndex, style === \"r\");\n          break;\n        case \"A\":\n        case \"a\":\n          const LIMIT = 26;\n          const A_UPPER_CASE = 0x41,\n            A_LOWER_CASE = 0x61;\n          const baseCharCode = style === \"a\" ? A_LOWER_CASE : A_UPPER_CASE;\n          const letterIndex = currentIndex - 1;\n          const character = String.fromCharCode(baseCharCode + letterIndex % LIMIT);\n          currentLabel = character.repeat(Math.floor(letterIndex / LIMIT) + 1);\n          break;\n        default:\n          if (style) {\n            throw new FormatError(`Invalid style \"${style}\" in PageLabel dictionary.`);\n          }\n          currentLabel = \"\";\n      }\n      pageLabels[i] = prefix + currentLabel;\n      currentIndex++;\n    }\n    return pageLabels;\n  }\n  get pageLayout() {\n    const obj = this._catDict.get(\"PageLayout\");\n    let pageLayout = \"\";\n    if (obj instanceof Name) {\n      switch (obj.name) {\n        case \"SinglePage\":\n        case \"OneColumn\":\n        case \"TwoColumnLeft\":\n        case \"TwoColumnRight\":\n        case \"TwoPageLeft\":\n        case \"TwoPageRight\":\n          pageLayout = obj.name;\n      }\n    }\n    return shadow(this, \"pageLayout\", pageLayout);\n  }\n  get pageMode() {\n    const obj = this._catDict.get(\"PageMode\");\n    let pageMode = \"UseNone\";\n    if (obj instanceof Name) {\n      switch (obj.name) {\n        case \"UseNone\":\n        case \"UseOutlines\":\n        case \"UseThumbs\":\n        case \"FullScreen\":\n        case \"UseOC\":\n        case \"UseAttachments\":\n          pageMode = obj.name;\n      }\n    }\n    return shadow(this, \"pageMode\", pageMode);\n  }\n  get viewerPreferences() {\n    const obj = this._catDict.get(\"ViewerPreferences\");\n    if (!(obj instanceof Dict)) {\n      return shadow(this, \"viewerPreferences\", null);\n    }\n    let prefs = null;\n    for (const key of obj.getKeys()) {\n      const value = obj.get(key);\n      let prefValue;\n      switch (key) {\n        case \"HideToolbar\":\n        case \"HideMenubar\":\n        case \"HideWindowUI\":\n        case \"FitWindow\":\n        case \"CenterWindow\":\n        case \"DisplayDocTitle\":\n        case \"PickTrayByPDFSize\":\n          if (typeof value === \"boolean\") {\n            prefValue = value;\n          }\n          break;\n        case \"NonFullScreenPageMode\":\n          if (value instanceof Name) {\n            switch (value.name) {\n              case \"UseNone\":\n              case \"UseOutlines\":\n              case \"UseThumbs\":\n              case \"UseOC\":\n                prefValue = value.name;\n                break;\n              default:\n                prefValue = \"UseNone\";\n            }\n          }\n          break;\n        case \"Direction\":\n          if (value instanceof Name) {\n            switch (value.name) {\n              case \"L2R\":\n              case \"R2L\":\n                prefValue = value.name;\n                break;\n              default:\n                prefValue = \"L2R\";\n            }\n          }\n          break;\n        case \"ViewArea\":\n        case \"ViewClip\":\n        case \"PrintArea\":\n        case \"PrintClip\":\n          if (value instanceof Name) {\n            switch (value.name) {\n              case \"MediaBox\":\n              case \"CropBox\":\n              case \"BleedBox\":\n              case \"TrimBox\":\n              case \"ArtBox\":\n                prefValue = value.name;\n                break;\n              default:\n                prefValue = \"CropBox\";\n            }\n          }\n          break;\n        case \"PrintScaling\":\n          if (value instanceof Name) {\n            switch (value.name) {\n              case \"None\":\n              case \"AppDefault\":\n                prefValue = value.name;\n                break;\n              default:\n                prefValue = \"AppDefault\";\n            }\n          }\n          break;\n        case \"Duplex\":\n          if (value instanceof Name) {\n            switch (value.name) {\n              case \"Simplex\":\n              case \"DuplexFlipShortEdge\":\n              case \"DuplexFlipLongEdge\":\n                prefValue = value.name;\n                break;\n              default:\n                prefValue = \"None\";\n            }\n          }\n          break;\n        case \"PrintPageRange\":\n          if (Array.isArray(value) && value.length % 2 === 0) {\n            const isValid = value.every((page, i, arr) => {\n              return Number.isInteger(page) && page > 0 && (i === 0 || page >= arr[i - 1]) && page <= this.numPages;\n            });\n            if (isValid) {\n              prefValue = value;\n            }\n          }\n          break;\n        case \"NumCopies\":\n          if (Number.isInteger(value) && value > 0) {\n            prefValue = value;\n          }\n          break;\n        default:\n          warn(`Ignoring non-standard key in ViewerPreferences: ${key}.`);\n          continue;\n      }\n      if (prefValue === undefined) {\n        warn(`Bad value, for key \"${key}\", in ViewerPreferences: ${value}.`);\n        continue;\n      }\n      if (!prefs) {\n        prefs = Object.create(null);\n      }\n      prefs[key] = prefValue;\n    }\n    return shadow(this, \"viewerPreferences\", prefs);\n  }\n  get openAction() {\n    const obj = this._catDict.get(\"OpenAction\");\n    const openAction = Object.create(null);\n    if (obj instanceof Dict) {\n      const destDict = new Dict(this.xref);\n      destDict.set(\"A\", obj);\n      const resultObj = {\n        url: null,\n        dest: null,\n        action: null\n      };\n      Catalog.parseDestDictionary({\n        destDict,\n        resultObj\n      });\n      if (Array.isArray(resultObj.dest)) {\n        openAction.dest = resultObj.dest;\n      } else if (resultObj.action) {\n        openAction.action = resultObj.action;\n      }\n    } else if (Array.isArray(obj)) {\n      openAction.dest = obj;\n    }\n    return shadow(this, \"openAction\", objectSize(openAction) > 0 ? openAction : null);\n  }\n  get attachments() {\n    const obj = this._catDict.get(\"Names\");\n    let attachments = null;\n    if (obj instanceof Dict && obj.has(\"EmbeddedFiles\")) {\n      const nameTree = new NameTree(obj.getRaw(\"EmbeddedFiles\"), this.xref);\n      for (const [key, value] of nameTree.getAll()) {\n        const fs = new FileSpec(value, this.xref);\n        if (!attachments) {\n          attachments = Object.create(null);\n        }\n        attachments[stringToPDFString(key)] = fs.serializable;\n      }\n    }\n    return shadow(this, \"attachments\", attachments);\n  }\n  get xfaImages() {\n    const obj = this._catDict.get(\"Names\");\n    let xfaImages = null;\n    if (obj instanceof Dict && obj.has(\"XFAImages\")) {\n      const nameTree = new NameTree(obj.getRaw(\"XFAImages\"), this.xref);\n      for (const [key, value] of nameTree.getAll()) {\n        if (!xfaImages) {\n          xfaImages = new Dict(this.xref);\n        }\n        xfaImages.set(stringToPDFString(key), value);\n      }\n    }\n    return shadow(this, \"xfaImages\", xfaImages);\n  }\n  _collectJavaScript() {\n    const obj = this._catDict.get(\"Names\");\n    let javaScript = null;\n    function appendIfJavaScriptDict(name, jsDict) {\n      if (!(jsDict instanceof Dict)) {\n        return;\n      }\n      if (!isName(jsDict.get(\"S\"), \"JavaScript\")) {\n        return;\n      }\n      let js = jsDict.get(\"JS\");\n      if (js instanceof BaseStream) {\n        js = js.getString();\n      } else if (typeof js !== \"string\") {\n        return;\n      }\n      js = stringToPDFString(js).replaceAll(\"\\x00\", \"\");\n      if (js) {\n        (javaScript ||= new Map()).set(name, js);\n      }\n    }\n    if (obj instanceof Dict && obj.has(\"JavaScript\")) {\n      const nameTree = new NameTree(obj.getRaw(\"JavaScript\"), this.xref);\n      for (const [key, value] of nameTree.getAll()) {\n        appendIfJavaScriptDict(stringToPDFString(key), value);\n      }\n    }\n    const openAction = this._catDict.get(\"OpenAction\");\n    if (openAction) {\n      appendIfJavaScriptDict(\"OpenAction\", openAction);\n    }\n    return javaScript;\n  }\n  get jsActions() {\n    const javaScript = this._collectJavaScript();\n    let actions = collectActions(this.xref, this._catDict, DocumentActionEventType);\n    if (javaScript) {\n      actions ||= Object.create(null);\n      for (const [key, val] of javaScript) {\n        if (key in actions) {\n          actions[key].push(val);\n        } else {\n          actions[key] = [val];\n        }\n      }\n    }\n    return shadow(this, \"jsActions\", actions);\n  }\n  async fontFallback(id, handler) {\n    const translatedFonts = await Promise.all(this.fontCache);\n    for (const translatedFont of translatedFonts) {\n      if (translatedFont.loadedName === id) {\n        translatedFont.fallback(handler);\n        return;\n      }\n    }\n  }\n  async cleanup(manuallyTriggered = false) {\n    clearGlobalCaches();\n    this.globalImageCache.clear(manuallyTriggered);\n    this.pageKidsCountCache.clear();\n    this.pageIndexCache.clear();\n    this.nonBlendModesSet.clear();\n    const translatedFonts = await Promise.all(this.fontCache);\n    for (const {\n      dict\n    } of translatedFonts) {\n      delete dict.cacheKey;\n    }\n    this.fontCache.clear();\n    this.builtInCMapCache.clear();\n    this.standardFontDataCache.clear();\n    this.systemFontCache.clear();\n  }\n  async getPageDict(pageIndex) {\n    const nodesToVisit = [this.toplevelPagesDict];\n    const visitedNodes = new RefSet();\n    const pagesRef = this._catDict.getRaw(\"Pages\");\n    if (pagesRef instanceof Ref) {\n      visitedNodes.put(pagesRef);\n    }\n    const xref = this.xref,\n      pageKidsCountCache = this.pageKidsCountCache,\n      pageIndexCache = this.pageIndexCache;\n    let currentPageIndex = 0;\n    while (nodesToVisit.length) {\n      const currentNode = nodesToVisit.pop();\n      if (currentNode instanceof Ref) {\n        const count = pageKidsCountCache.get(currentNode);\n        if (count >= 0 && currentPageIndex + count <= pageIndex) {\n          currentPageIndex += count;\n          continue;\n        }\n        if (visitedNodes.has(currentNode)) {\n          throw new FormatError(\"Pages tree contains circular reference.\");\n        }\n        visitedNodes.put(currentNode);\n        const obj = await xref.fetchAsync(currentNode);\n        if (obj instanceof Dict) {\n          let type = obj.getRaw(\"Type\");\n          if (type instanceof Ref) {\n            type = await xref.fetchAsync(type);\n          }\n          if (isName(type, \"Page\") || !obj.has(\"Kids\")) {\n            if (!pageKidsCountCache.has(currentNode)) {\n              pageKidsCountCache.put(currentNode, 1);\n            }\n            if (!pageIndexCache.has(currentNode)) {\n              pageIndexCache.put(currentNode, currentPageIndex);\n            }\n            if (currentPageIndex === pageIndex) {\n              return [obj, currentNode];\n            }\n            currentPageIndex++;\n            continue;\n          }\n        }\n        nodesToVisit.push(obj);\n        continue;\n      }\n      if (!(currentNode instanceof Dict)) {\n        throw new FormatError(\"Page dictionary kid reference points to wrong type of object.\");\n      }\n      const {\n        objId\n      } = currentNode;\n      let count = currentNode.getRaw(\"Count\");\n      if (count instanceof Ref) {\n        count = await xref.fetchAsync(count);\n      }\n      if (Number.isInteger(count) && count >= 0) {\n        if (objId && !pageKidsCountCache.has(objId)) {\n          pageKidsCountCache.put(objId, count);\n        }\n        if (currentPageIndex + count <= pageIndex) {\n          currentPageIndex += count;\n          continue;\n        }\n      }\n      let kids = currentNode.getRaw(\"Kids\");\n      if (kids instanceof Ref) {\n        kids = await xref.fetchAsync(kids);\n      }\n      if (!Array.isArray(kids)) {\n        let type = currentNode.getRaw(\"Type\");\n        if (type instanceof Ref) {\n          type = await xref.fetchAsync(type);\n        }\n        if (isName(type, \"Page\") || !currentNode.has(\"Kids\")) {\n          if (currentPageIndex === pageIndex) {\n            return [currentNode, null];\n          }\n          currentPageIndex++;\n          continue;\n        }\n        throw new FormatError(\"Page dictionary kids object is not an array.\");\n      }\n      for (let last = kids.length - 1; last >= 0; last--) {\n        nodesToVisit.push(kids[last]);\n      }\n    }\n    throw new Error(`Page index ${pageIndex} not found.`);\n  }\n  async getAllPageDicts(recoveryMode = false) {\n    const {\n      ignoreErrors\n    } = this.pdfManager.evaluatorOptions;\n    const queue = [{\n      currentNode: this.toplevelPagesDict,\n      posInKids: 0\n    }];\n    const visitedNodes = new RefSet();\n    const pagesRef = this._catDict.getRaw(\"Pages\");\n    if (pagesRef instanceof Ref) {\n      visitedNodes.put(pagesRef);\n    }\n    const map = new Map(),\n      xref = this.xref,\n      pageIndexCache = this.pageIndexCache;\n    let pageIndex = 0;\n    function addPageDict(pageDict, pageRef) {\n      if (pageRef && !pageIndexCache.has(pageRef)) {\n        pageIndexCache.put(pageRef, pageIndex);\n      }\n      map.set(pageIndex++, [pageDict, pageRef]);\n    }\n    function addPageError(error) {\n      if (error instanceof XRefEntryException && !recoveryMode) {\n        throw error;\n      }\n      if (recoveryMode && ignoreErrors && pageIndex === 0) {\n        warn(`getAllPageDicts - Skipping invalid first page: \"${error}\".`);\n        error = Dict.empty;\n      }\n      map.set(pageIndex++, [error, null]);\n    }\n    while (queue.length > 0) {\n      const queueItem = queue.at(-1);\n      const {\n        currentNode,\n        posInKids\n      } = queueItem;\n      let kids = currentNode.getRaw(\"Kids\");\n      if (kids instanceof Ref) {\n        try {\n          kids = await xref.fetchAsync(kids);\n        } catch (ex) {\n          addPageError(ex);\n          break;\n        }\n      }\n      if (!Array.isArray(kids)) {\n        addPageError(new FormatError(\"Page dictionary kids object is not an array.\"));\n        break;\n      }\n      if (posInKids >= kids.length) {\n        queue.pop();\n        continue;\n      }\n      const kidObj = kids[posInKids];\n      let obj;\n      if (kidObj instanceof Ref) {\n        if (visitedNodes.has(kidObj)) {\n          addPageError(new FormatError(\"Pages tree contains circular reference.\"));\n          break;\n        }\n        visitedNodes.put(kidObj);\n        try {\n          obj = await xref.fetchAsync(kidObj);\n        } catch (ex) {\n          addPageError(ex);\n          break;\n        }\n      } else {\n        obj = kidObj;\n      }\n      if (!(obj instanceof Dict)) {\n        addPageError(new FormatError(\"Page dictionary kid reference points to wrong type of object.\"));\n        break;\n      }\n      let type = obj.getRaw(\"Type\");\n      if (type instanceof Ref) {\n        try {\n          type = await xref.fetchAsync(type);\n        } catch (ex) {\n          addPageError(ex);\n          break;\n        }\n      }\n      if (isName(type, \"Page\") || !obj.has(\"Kids\")) {\n        addPageDict(obj, kidObj instanceof Ref ? kidObj : null);\n      } else {\n        queue.push({\n          currentNode: obj,\n          posInKids: 0\n        });\n      }\n      queueItem.posInKids++;\n    }\n    return map;\n  }\n  getPageIndex(pageRef) {\n    const cachedPageIndex = this.pageIndexCache.get(pageRef);\n    if (cachedPageIndex !== undefined) {\n      return Promise.resolve(cachedPageIndex);\n    }\n    const xref = this.xref;\n    function pagesBeforeRef(kidRef) {\n      let total = 0,\n        parentRef;\n      return xref.fetchAsync(kidRef).then(function (node) {\n        if (isRefsEqual(kidRef, pageRef) && !isDict(node, \"Page\") && !(node instanceof Dict && !node.has(\"Type\") && node.has(\"Contents\"))) {\n          throw new FormatError(\"The reference does not point to a /Page dictionary.\");\n        }\n        if (!node) {\n          return null;\n        }\n        if (!(node instanceof Dict)) {\n          throw new FormatError(\"Node must be a dictionary.\");\n        }\n        parentRef = node.getRaw(\"Parent\");\n        return node.getAsync(\"Parent\");\n      }).then(function (parent) {\n        if (!parent) {\n          return null;\n        }\n        if (!(parent instanceof Dict)) {\n          throw new FormatError(\"Parent must be a dictionary.\");\n        }\n        return parent.getAsync(\"Kids\");\n      }).then(function (kids) {\n        if (!kids) {\n          return null;\n        }\n        const kidPromises = [];\n        let found = false;\n        for (const kid of kids) {\n          if (!(kid instanceof Ref)) {\n            throw new FormatError(\"Kid must be a reference.\");\n          }\n          if (isRefsEqual(kid, kidRef)) {\n            found = true;\n            break;\n          }\n          kidPromises.push(xref.fetchAsync(kid).then(function (obj) {\n            if (!(obj instanceof Dict)) {\n              throw new FormatError(\"Kid node must be a dictionary.\");\n            }\n            if (obj.has(\"Count\")) {\n              total += obj.get(\"Count\");\n            } else {\n              total++;\n            }\n          }));\n        }\n        if (!found) {\n          throw new FormatError(\"Kid reference not found in parent's kids.\");\n        }\n        return Promise.all(kidPromises).then(function () {\n          return [total, parentRef];\n        });\n      });\n    }\n    let total = 0;\n    const next = ref => pagesBeforeRef(ref).then(args => {\n      if (!args) {\n        this.pageIndexCache.put(pageRef, total);\n        return total;\n      }\n      const [count, parentRef] = args;\n      total += count;\n      return next(parentRef);\n    });\n    return next(pageRef);\n  }\n  get baseUrl() {\n    const uri = this._catDict.get(\"URI\");\n    if (uri instanceof Dict) {\n      const base = uri.get(\"Base\");\n      if (typeof base === \"string\") {\n        const absoluteUrl = createValidAbsoluteUrl(base, null, {\n          tryConvertEncoding: true\n        });\n        if (absoluteUrl) {\n          return shadow(this, \"baseUrl\", absoluteUrl.href);\n        }\n      }\n    }\n    return shadow(this, \"baseUrl\", this.pdfManager.docBaseUrl);\n  }\n  static parseDestDictionary({\n    destDict,\n    resultObj,\n    docBaseUrl = null,\n    docAttachments = null\n  }) {\n    if (!(destDict instanceof Dict)) {\n      warn(\"parseDestDictionary: `destDict` must be a dictionary.\");\n      return;\n    }\n    let action = destDict.get(\"A\"),\n      url,\n      dest;\n    if (!(action instanceof Dict)) {\n      if (destDict.has(\"Dest\")) {\n        action = destDict.get(\"Dest\");\n      } else {\n        action = destDict.get(\"AA\");\n        if (action instanceof Dict) {\n          if (action.has(\"D\")) {\n            action = action.get(\"D\");\n          } else if (action.has(\"U\")) {\n            action = action.get(\"U\");\n          }\n        }\n      }\n    }\n    if (action instanceof Dict) {\n      const actionType = action.get(\"S\");\n      if (!(actionType instanceof Name)) {\n        warn(\"parseDestDictionary: Invalid type in Action dictionary.\");\n        return;\n      }\n      const actionName = actionType.name;\n      switch (actionName) {\n        case \"ResetForm\":\n          const flags = action.get(\"Flags\");\n          const include = ((typeof flags === \"number\" ? flags : 0) & 1) === 0;\n          const fields = [];\n          const refs = [];\n          for (const obj of action.get(\"Fields\") || []) {\n            if (obj instanceof Ref) {\n              refs.push(obj.toString());\n            } else if (typeof obj === \"string\") {\n              fields.push(stringToPDFString(obj));\n            }\n          }\n          resultObj.resetForm = {\n            fields,\n            refs,\n            include\n          };\n          break;\n        case \"URI\":\n          url = action.get(\"URI\");\n          if (url instanceof Name) {\n            url = \"/\" + url.name;\n          }\n          break;\n        case \"GoTo\":\n          dest = action.get(\"D\");\n          break;\n        case \"Launch\":\n        case \"GoToR\":\n          const urlDict = action.get(\"F\");\n          if (urlDict instanceof Dict) {\n            url = urlDict.get(\"F\") || null;\n          } else if (typeof urlDict === \"string\") {\n            url = urlDict;\n          }\n          const remoteDest = fetchRemoteDest(action);\n          if (remoteDest && typeof url === \"string\") {\n            url = url.split(\"#\", 1)[0] + \"#\" + remoteDest;\n          }\n          const newWindow = action.get(\"NewWindow\");\n          if (typeof newWindow === \"boolean\") {\n            resultObj.newWindow = newWindow;\n          }\n          break;\n        case \"GoToE\":\n          const target = action.get(\"T\");\n          let attachment;\n          if (docAttachments && target instanceof Dict) {\n            const relationship = target.get(\"R\");\n            const name = target.get(\"N\");\n            if (isName(relationship, \"C\") && typeof name === \"string\") {\n              attachment = docAttachments[stringToPDFString(name)];\n            }\n          }\n          if (attachment) {\n            resultObj.attachment = attachment;\n            const attachmentDest = fetchRemoteDest(action);\n            if (attachmentDest) {\n              resultObj.attachmentDest = attachmentDest;\n            }\n          } else {\n            warn(`parseDestDictionary - unimplemented \"GoToE\" action.`);\n          }\n          break;\n        case \"Named\":\n          const namedAction = action.get(\"N\");\n          if (namedAction instanceof Name) {\n            resultObj.action = namedAction.name;\n          }\n          break;\n        case \"SetOCGState\":\n          const state = action.get(\"State\");\n          const preserveRB = action.get(\"PreserveRB\");\n          if (!Array.isArray(state) || state.length === 0) {\n            break;\n          }\n          const stateArr = [];\n          for (const elem of state) {\n            if (elem instanceof Name) {\n              switch (elem.name) {\n                case \"ON\":\n                case \"OFF\":\n                case \"Toggle\":\n                  stateArr.push(elem.name);\n                  break;\n              }\n            } else if (elem instanceof Ref) {\n              stateArr.push(elem.toString());\n            }\n          }\n          if (stateArr.length !== state.length) {\n            break;\n          }\n          resultObj.setOCGState = {\n            state: stateArr,\n            preserveRB: typeof preserveRB === \"boolean\" ? preserveRB : true\n          };\n          break;\n        case \"JavaScript\":\n          const jsAction = action.get(\"JS\");\n          let js;\n          if (jsAction instanceof BaseStream) {\n            js = jsAction.getString();\n          } else if (typeof jsAction === \"string\") {\n            js = jsAction;\n          }\n          const jsURL = js && recoverJsURL(stringToPDFString(js));\n          if (jsURL) {\n            url = jsURL.url;\n            resultObj.newWindow = jsURL.newWindow;\n            break;\n          }\n        default:\n          if (actionName === \"JavaScript\" || actionName === \"SubmitForm\") {\n            break;\n          }\n          warn(`parseDestDictionary - unsupported action: \"${actionName}\".`);\n          break;\n      }\n    } else if (destDict.has(\"Dest\")) {\n      dest = destDict.get(\"Dest\");\n    }\n    if (typeof url === \"string\") {\n      const absoluteUrl = createValidAbsoluteUrl(url, docBaseUrl, {\n        addDefaultProtocol: true,\n        tryConvertEncoding: true\n      });\n      if (absoluteUrl) {\n        resultObj.url = absoluteUrl.href;\n      }\n      resultObj.unsafeUrl = url;\n    }\n    if (dest) {\n      if (dest instanceof Name) {\n        dest = dest.name;\n      }\n      if (typeof dest === \"string\") {\n        resultObj.dest = stringToPDFString(dest);\n      } else if (Array.isArray(dest)) {\n        resultObj.dest = dest;\n      }\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/object_loader.js\n\n\n\n\nfunction mayHaveChildren(value) {\n  return value instanceof Ref || value instanceof Dict || value instanceof BaseStream || Array.isArray(value);\n}\nfunction addChildren(node, nodesToVisit) {\n  if (node instanceof Dict) {\n    node = node.getRawValues();\n  } else if (node instanceof BaseStream) {\n    node = node.dict.getRawValues();\n  } else if (!Array.isArray(node)) {\n    return;\n  }\n  for (const rawValue of node) {\n    if (mayHaveChildren(rawValue)) {\n      nodesToVisit.push(rawValue);\n    }\n  }\n}\nclass ObjectLoader {\n  constructor(dict, keys, xref) {\n    this.dict = dict;\n    this.keys = keys;\n    this.xref = xref;\n    this.refSet = null;\n  }\n  async load() {\n    if (this.xref.stream.isDataLoaded) {\n      return undefined;\n    }\n    const {\n      keys,\n      dict\n    } = this;\n    this.refSet = new RefSet();\n    const nodesToVisit = [];\n    for (const key of keys) {\n      const rawValue = dict.getRaw(key);\n      if (rawValue !== undefined) {\n        nodesToVisit.push(rawValue);\n      }\n    }\n    return this._walk(nodesToVisit);\n  }\n  async _walk(nodesToVisit) {\n    const nodesToRevisit = [];\n    const pendingRequests = [];\n    while (nodesToVisit.length) {\n      let currentNode = nodesToVisit.pop();\n      if (currentNode instanceof Ref) {\n        if (this.refSet.has(currentNode)) {\n          continue;\n        }\n        try {\n          this.refSet.put(currentNode);\n          currentNode = this.xref.fetch(currentNode);\n        } catch (ex) {\n          if (!(ex instanceof MissingDataException)) {\n            warn(`ObjectLoader._walk - requesting all data: \"${ex}\".`);\n            this.refSet = null;\n            const {\n              manager\n            } = this.xref.stream;\n            return manager.requestAllChunks();\n          }\n          nodesToRevisit.push(currentNode);\n          pendingRequests.push({\n            begin: ex.begin,\n            end: ex.end\n          });\n        }\n      }\n      if (currentNode instanceof BaseStream) {\n        const baseStreams = currentNode.getBaseStreams();\n        if (baseStreams) {\n          let foundMissingData = false;\n          for (const stream of baseStreams) {\n            if (stream.isDataLoaded) {\n              continue;\n            }\n            foundMissingData = true;\n            pendingRequests.push({\n              begin: stream.start,\n              end: stream.end\n            });\n          }\n          if (foundMissingData) {\n            nodesToRevisit.push(currentNode);\n          }\n        }\n      }\n      addChildren(currentNode, nodesToVisit);\n    }\n    if (pendingRequests.length) {\n      await this.xref.stream.manager.requestRanges(pendingRequests);\n      for (const node of nodesToRevisit) {\n        if (node instanceof Ref) {\n          this.refSet.remove(node);\n        }\n      }\n      return this._walk(nodesToRevisit);\n    }\n    this.refSet = null;\n    return undefined;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/symbol_utils.js\nconst $acceptWhitespace = Symbol();\nconst $addHTML = Symbol();\nconst $appendChild = Symbol();\nconst $childrenToHTML = Symbol();\nconst $clean = Symbol();\nconst $cleanPage = Symbol();\nconst $cleanup = Symbol();\nconst $clone = Symbol();\nconst $consumed = Symbol();\nconst $content = Symbol(\"content\");\nconst $data = Symbol(\"data\");\nconst $dump = Symbol();\nconst $extra = Symbol(\"extra\");\nconst $finalize = Symbol();\nconst $flushHTML = Symbol();\nconst $getAttributeIt = Symbol();\nconst $getAttributes = Symbol();\nconst $getAvailableSpace = Symbol();\nconst $getChildrenByClass = Symbol();\nconst $getChildrenByName = Symbol();\nconst $getChildrenByNameIt = Symbol();\nconst $getDataValue = Symbol();\nconst $getExtra = Symbol();\nconst $getRealChildrenByNameIt = Symbol();\nconst $getChildren = Symbol();\nconst $getContainedChildren = Symbol();\nconst $getNextPage = Symbol();\nconst $getSubformParent = Symbol();\nconst $getParent = Symbol();\nconst $getTemplateRoot = Symbol();\nconst $globalData = Symbol();\nconst $hasSettableValue = Symbol();\nconst $ids = Symbol();\nconst $indexOf = Symbol();\nconst $insertAt = Symbol();\nconst $isCDATAXml = Symbol();\nconst $isBindable = Symbol();\nconst $isDataValue = Symbol();\nconst $isDescendent = Symbol();\nconst $isNsAgnostic = Symbol();\nconst $isSplittable = Symbol();\nconst $isThereMoreWidth = Symbol();\nconst $isTransparent = Symbol();\nconst $isUsable = Symbol();\nconst $lastAttribute = Symbol();\nconst $namespaceId = Symbol(\"namespaceId\");\nconst $nodeName = Symbol(\"nodeName\");\nconst $nsAttributes = Symbol();\nconst $onChild = Symbol();\nconst $onChildCheck = Symbol();\nconst $onText = Symbol();\nconst $pushGlyphs = Symbol();\nconst $popPara = Symbol();\nconst $pushPara = Symbol();\nconst $removeChild = Symbol();\nconst $root = Symbol(\"root\");\nconst $resolvePrototypes = Symbol();\nconst $searchNode = Symbol();\nconst $setId = Symbol();\nconst $setSetAttributes = Symbol();\nconst $setValue = Symbol();\nconst $tabIndex = Symbol();\nconst $text = Symbol();\nconst $toPages = Symbol();\nconst $toHTML = Symbol();\nconst $toString = Symbol();\nconst $toStyle = Symbol();\nconst $uid = Symbol(\"uid\");\n\n;// CONCATENATED MODULE: ./src/core/xfa/namespaces.js\nconst $buildXFAObject = Symbol();\nconst NamespaceIds = {\n  config: {\n    id: 0,\n    check: ns => ns.startsWith(\"http://www.xfa.org/schema/xci/\")\n  },\n  connectionSet: {\n    id: 1,\n    check: ns => ns.startsWith(\"http://www.xfa.org/schema/xfa-connection-set/\")\n  },\n  datasets: {\n    id: 2,\n    check: ns => ns.startsWith(\"http://www.xfa.org/schema/xfa-data/\")\n  },\n  form: {\n    id: 3,\n    check: ns => ns.startsWith(\"http://www.xfa.org/schema/xfa-form/\")\n  },\n  localeSet: {\n    id: 4,\n    check: ns => ns.startsWith(\"http://www.xfa.org/schema/xfa-locale-set/\")\n  },\n  pdf: {\n    id: 5,\n    check: ns => ns === \"http://ns.adobe.com/xdp/pdf/\"\n  },\n  signature: {\n    id: 6,\n    check: ns => ns === \"http://www.w3.org/2000/09/xmldsig#\"\n  },\n  sourceSet: {\n    id: 7,\n    check: ns => ns.startsWith(\"http://www.xfa.org/schema/xfa-source-set/\")\n  },\n  stylesheet: {\n    id: 8,\n    check: ns => ns === \"http://www.w3.org/1999/XSL/Transform\"\n  },\n  template: {\n    id: 9,\n    check: ns => ns.startsWith(\"http://www.xfa.org/schema/xfa-template/\")\n  },\n  xdc: {\n    id: 10,\n    check: ns => ns.startsWith(\"http://www.xfa.org/schema/xdc/\")\n  },\n  xdp: {\n    id: 11,\n    check: ns => ns === \"http://ns.adobe.com/xdp/\"\n  },\n  xfdf: {\n    id: 12,\n    check: ns => ns === \"http://ns.adobe.com/xfdf/\"\n  },\n  xhtml: {\n    id: 13,\n    check: ns => ns === \"http://www.w3.org/1999/xhtml\"\n  },\n  xmpmeta: {\n    id: 14,\n    check: ns => ns === \"http://ns.adobe.com/xmpmeta/\"\n  }\n};\n\n;// CONCATENATED MODULE: ./src/core/xfa/utils.js\n\nconst dimConverters = {\n  pt: x => x,\n  cm: x => x / 2.54 * 72,\n  mm: x => x / (10 * 2.54) * 72,\n  in: x => x * 72,\n  px: x => x\n};\nconst measurementPattern = /([+-]?\\d+\\.?\\d*)(.*)/;\nfunction stripQuotes(str) {\n  if (str.startsWith(\"'\") || str.startsWith('\"')) {\n    return str.slice(1, -1);\n  }\n  return str;\n}\nfunction getInteger({\n  data,\n  defaultValue,\n  validate\n}) {\n  if (!data) {\n    return defaultValue;\n  }\n  data = data.trim();\n  const n = parseInt(data, 10);\n  if (!isNaN(n) && validate(n)) {\n    return n;\n  }\n  return defaultValue;\n}\nfunction getFloat({\n  data,\n  defaultValue,\n  validate\n}) {\n  if (!data) {\n    return defaultValue;\n  }\n  data = data.trim();\n  const n = parseFloat(data);\n  if (!isNaN(n) && validate(n)) {\n    return n;\n  }\n  return defaultValue;\n}\nfunction getKeyword({\n  data,\n  defaultValue,\n  validate\n}) {\n  if (!data) {\n    return defaultValue;\n  }\n  data = data.trim();\n  if (validate(data)) {\n    return data;\n  }\n  return defaultValue;\n}\nfunction getStringOption(data, options) {\n  return getKeyword({\n    data,\n    defaultValue: options[0],\n    validate: k => options.includes(k)\n  });\n}\nfunction getMeasurement(str, def = \"0\") {\n  def ||= \"0\";\n  if (!str) {\n    return getMeasurement(def);\n  }\n  const match = str.trim().match(measurementPattern);\n  if (!match) {\n    return getMeasurement(def);\n  }\n  const [, valueStr, unit] = match;\n  const value = parseFloat(valueStr);\n  if (isNaN(value)) {\n    return getMeasurement(def);\n  }\n  if (value === 0) {\n    return 0;\n  }\n  const conv = dimConverters[unit];\n  if (conv) {\n    return conv(value);\n  }\n  return value;\n}\nfunction getRatio(data) {\n  if (!data) {\n    return {\n      num: 1,\n      den: 1\n    };\n  }\n  const ratio = data.trim().split(/\\s*:\\s*/).map(x => parseFloat(x)).filter(x => !isNaN(x));\n  if (ratio.length === 1) {\n    ratio.push(1);\n  }\n  if (ratio.length === 0) {\n    return {\n      num: 1,\n      den: 1\n    };\n  }\n  const [num, den] = ratio;\n  return {\n    num,\n    den\n  };\n}\nfunction getRelevant(data) {\n  if (!data) {\n    return [];\n  }\n  return data.trim().split(/\\s+/).map(e => {\n    return {\n      excluded: e[0] === \"-\",\n      viewname: e.substring(1)\n    };\n  });\n}\nfunction getColor(data, def = [0, 0, 0]) {\n  let [r, g, b] = def;\n  if (!data) {\n    return {\n      r,\n      g,\n      b\n    };\n  }\n  const color = data.trim().split(/\\s*,\\s*/).map(c => Math.min(Math.max(0, parseInt(c.trim(), 10)), 255)).map(c => isNaN(c) ? 0 : c);\n  if (color.length < 3) {\n    return {\n      r,\n      g,\n      b\n    };\n  }\n  [r, g, b] = color;\n  return {\n    r,\n    g,\n    b\n  };\n}\nfunction getBBox(data) {\n  const def = -1;\n  if (!data) {\n    return {\n      x: def,\n      y: def,\n      width: def,\n      height: def\n    };\n  }\n  const bbox = data.trim().split(/\\s*,\\s*/).map(m => getMeasurement(m, \"-1\"));\n  if (bbox.length < 4 || bbox[2] < 0 || bbox[3] < 0) {\n    return {\n      x: def,\n      y: def,\n      width: def,\n      height: def\n    };\n  }\n  const [x, y, width, height] = bbox;\n  return {\n    x,\n    y,\n    width,\n    height\n  };\n}\nclass HTMLResult {\n  static get FAILURE() {\n    return shadow(this, \"FAILURE\", new HTMLResult(false, null, null, null));\n  }\n  static get EMPTY() {\n    return shadow(this, \"EMPTY\", new HTMLResult(true, null, null, null));\n  }\n  constructor(success, html, bbox, breakNode) {\n    this.success = success;\n    this.html = html;\n    this.bbox = bbox;\n    this.breakNode = breakNode;\n  }\n  isBreak() {\n    return !!this.breakNode;\n  }\n  static breakNode(node) {\n    return new HTMLResult(false, null, null, node);\n  }\n  static success(html, bbox = null) {\n    return new HTMLResult(true, html, bbox, null);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/fonts.js\n\n\n\nclass FontFinder {\n  constructor(pdfFonts) {\n    this.fonts = new Map();\n    this.cache = new Map();\n    this.warned = new Set();\n    this.defaultFont = null;\n    this.add(pdfFonts);\n  }\n  add(pdfFonts, reallyMissingFonts = null) {\n    for (const pdfFont of pdfFonts) {\n      this.addPdfFont(pdfFont);\n    }\n    for (const pdfFont of this.fonts.values()) {\n      if (!pdfFont.regular) {\n        pdfFont.regular = pdfFont.italic || pdfFont.bold || pdfFont.bolditalic;\n      }\n    }\n    if (!reallyMissingFonts || reallyMissingFonts.size === 0) {\n      return;\n    }\n    const myriad = this.fonts.get(\"PdfJS-Fallback-PdfJS-XFA\");\n    for (const missing of reallyMissingFonts) {\n      this.fonts.set(missing, myriad);\n    }\n  }\n  addPdfFont(pdfFont) {\n    const cssFontInfo = pdfFont.cssFontInfo;\n    const name = cssFontInfo.fontFamily;\n    let font = this.fonts.get(name);\n    if (!font) {\n      font = Object.create(null);\n      this.fonts.set(name, font);\n      if (!this.defaultFont) {\n        this.defaultFont = font;\n      }\n    }\n    let property = \"\";\n    const fontWeight = parseFloat(cssFontInfo.fontWeight);\n    if (parseFloat(cssFontInfo.italicAngle) !== 0) {\n      property = fontWeight >= 700 ? \"bolditalic\" : \"italic\";\n    } else if (fontWeight >= 700) {\n      property = \"bold\";\n    }\n    if (!property) {\n      if (pdfFont.name.includes(\"Bold\") || pdfFont.psName?.includes(\"Bold\")) {\n        property = \"bold\";\n      }\n      if (pdfFont.name.includes(\"Italic\") || pdfFont.name.endsWith(\"It\") || pdfFont.psName?.includes(\"Italic\") || pdfFont.psName?.endsWith(\"It\")) {\n        property += \"italic\";\n      }\n    }\n    if (!property) {\n      property = \"regular\";\n    }\n    font[property] = pdfFont;\n  }\n  getDefault() {\n    return this.defaultFont;\n  }\n  find(fontName, mustWarn = true) {\n    let font = this.fonts.get(fontName) || this.cache.get(fontName);\n    if (font) {\n      return font;\n    }\n    const pattern = /,|-|_| |bolditalic|bold|italic|regular|it/gi;\n    let name = fontName.replaceAll(pattern, \"\");\n    font = this.fonts.get(name);\n    if (font) {\n      this.cache.set(fontName, font);\n      return font;\n    }\n    name = name.toLowerCase();\n    const maybe = [];\n    for (const [family, pdfFont] of this.fonts.entries()) {\n      if (family.replaceAll(pattern, \"\").toLowerCase().startsWith(name)) {\n        maybe.push(pdfFont);\n      }\n    }\n    if (maybe.length === 0) {\n      for (const [, pdfFont] of this.fonts.entries()) {\n        if (pdfFont.regular.name?.replaceAll(pattern, \"\").toLowerCase().startsWith(name)) {\n          maybe.push(pdfFont);\n        }\n      }\n    }\n    if (maybe.length === 0) {\n      name = name.replaceAll(/psmt|mt/gi, \"\");\n      for (const [family, pdfFont] of this.fonts.entries()) {\n        if (family.replaceAll(pattern, \"\").toLowerCase().startsWith(name)) {\n          maybe.push(pdfFont);\n        }\n      }\n    }\n    if (maybe.length === 0) {\n      for (const pdfFont of this.fonts.values()) {\n        if (pdfFont.regular.name?.replaceAll(pattern, \"\").toLowerCase().startsWith(name)) {\n          maybe.push(pdfFont);\n        }\n      }\n    }\n    if (maybe.length >= 1) {\n      if (maybe.length !== 1 && mustWarn) {\n        warn(`XFA - Too many choices to guess the correct font: ${fontName}`);\n      }\n      this.cache.set(fontName, maybe[0]);\n      return maybe[0];\n    }\n    if (mustWarn && !this.warned.has(fontName)) {\n      this.warned.add(fontName);\n      warn(`XFA - Cannot find the font: ${fontName}`);\n    }\n    return null;\n  }\n}\nfunction selectFont(xfaFont, typeface) {\n  if (xfaFont.posture === \"italic\") {\n    if (xfaFont.weight === \"bold\") {\n      return typeface.bolditalic;\n    }\n    return typeface.italic;\n  } else if (xfaFont.weight === \"bold\") {\n    return typeface.bold;\n  }\n  return typeface.regular;\n}\nfunction fonts_getMetrics(xfaFont, real = false) {\n  let pdfFont = null;\n  if (xfaFont) {\n    const name = stripQuotes(xfaFont.typeface);\n    const typeface = xfaFont[$globalData].fontFinder.find(name);\n    pdfFont = selectFont(xfaFont, typeface);\n  }\n  if (!pdfFont) {\n    return {\n      lineHeight: 12,\n      lineGap: 2,\n      lineNoGap: 10\n    };\n  }\n  const size = xfaFont.size || 10;\n  const lineHeight = pdfFont.lineHeight ? Math.max(real ? 0 : 1.2, pdfFont.lineHeight) : 1.2;\n  const lineGap = pdfFont.lineGap === undefined ? 0.2 : pdfFont.lineGap;\n  return {\n    lineHeight: lineHeight * size,\n    lineGap: lineGap * size,\n    lineNoGap: Math.max(1, lineHeight - lineGap) * size\n  };\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/text.js\n\nconst WIDTH_FACTOR = 1.02;\nclass FontInfo {\n  constructor(xfaFont, margin, lineHeight, fontFinder) {\n    this.lineHeight = lineHeight;\n    this.paraMargin = margin || {\n      top: 0,\n      bottom: 0,\n      left: 0,\n      right: 0\n    };\n    if (!xfaFont) {\n      [this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder);\n      return;\n    }\n    this.xfaFont = {\n      typeface: xfaFont.typeface,\n      posture: xfaFont.posture,\n      weight: xfaFont.weight,\n      size: xfaFont.size,\n      letterSpacing: xfaFont.letterSpacing\n    };\n    const typeface = fontFinder.find(xfaFont.typeface);\n    if (!typeface) {\n      [this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder);\n      return;\n    }\n    this.pdfFont = selectFont(xfaFont, typeface);\n    if (!this.pdfFont) {\n      [this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder);\n    }\n  }\n  defaultFont(fontFinder) {\n    const font = fontFinder.find(\"Helvetica\", false) || fontFinder.find(\"Myriad Pro\", false) || fontFinder.find(\"Arial\", false) || fontFinder.getDefault();\n    if (font?.regular) {\n      const pdfFont = font.regular;\n      const info = pdfFont.cssFontInfo;\n      const xfaFont = {\n        typeface: info.fontFamily,\n        posture: \"normal\",\n        weight: \"normal\",\n        size: 10,\n        letterSpacing: 0\n      };\n      return [pdfFont, xfaFont];\n    }\n    const xfaFont = {\n      typeface: \"Courier\",\n      posture: \"normal\",\n      weight: \"normal\",\n      size: 10,\n      letterSpacing: 0\n    };\n    return [null, xfaFont];\n  }\n}\nclass FontSelector {\n  constructor(defaultXfaFont, defaultParaMargin, defaultLineHeight, fontFinder) {\n    this.fontFinder = fontFinder;\n    this.stack = [new FontInfo(defaultXfaFont, defaultParaMargin, defaultLineHeight, fontFinder)];\n  }\n  pushData(xfaFont, margin, lineHeight) {\n    const lastFont = this.stack.at(-1);\n    for (const name of [\"typeface\", \"posture\", \"weight\", \"size\", \"letterSpacing\"]) {\n      if (!xfaFont[name]) {\n        xfaFont[name] = lastFont.xfaFont[name];\n      }\n    }\n    for (const name of [\"top\", \"bottom\", \"left\", \"right\"]) {\n      if (isNaN(margin[name])) {\n        margin[name] = lastFont.paraMargin[name];\n      }\n    }\n    const fontInfo = new FontInfo(xfaFont, margin, lineHeight || lastFont.lineHeight, this.fontFinder);\n    if (!fontInfo.pdfFont) {\n      fontInfo.pdfFont = lastFont.pdfFont;\n    }\n    this.stack.push(fontInfo);\n  }\n  popFont() {\n    this.stack.pop();\n  }\n  topFont() {\n    return this.stack.at(-1);\n  }\n}\nclass TextMeasure {\n  constructor(defaultXfaFont, defaultParaMargin, defaultLineHeight, fonts) {\n    this.glyphs = [];\n    this.fontSelector = new FontSelector(defaultXfaFont, defaultParaMargin, defaultLineHeight, fonts);\n    this.extraHeight = 0;\n  }\n  pushData(xfaFont, margin, lineHeight) {\n    this.fontSelector.pushData(xfaFont, margin, lineHeight);\n  }\n  popFont(xfaFont) {\n    return this.fontSelector.popFont();\n  }\n  addPara() {\n    const lastFont = this.fontSelector.topFont();\n    this.extraHeight += lastFont.paraMargin.top + lastFont.paraMargin.bottom;\n  }\n  addString(str) {\n    if (!str) {\n      return;\n    }\n    const lastFont = this.fontSelector.topFont();\n    const fontSize = lastFont.xfaFont.size;\n    if (lastFont.pdfFont) {\n      const letterSpacing = lastFont.xfaFont.letterSpacing;\n      const pdfFont = lastFont.pdfFont;\n      const fontLineHeight = pdfFont.lineHeight || 1.2;\n      const lineHeight = lastFont.lineHeight || Math.max(1.2, fontLineHeight) * fontSize;\n      const lineGap = pdfFont.lineGap === undefined ? 0.2 : pdfFont.lineGap;\n      const noGap = fontLineHeight - lineGap;\n      const firstLineHeight = Math.max(1, noGap) * fontSize;\n      const scale = fontSize / 1000;\n      const fallbackWidth = pdfFont.defaultWidth || pdfFont.charsToGlyphs(\" \")[0].width;\n      for (const line of str.split(/[\\u2029\\n]/)) {\n        const encodedLine = pdfFont.encodeString(line).join(\"\");\n        const glyphs = pdfFont.charsToGlyphs(encodedLine);\n        for (const glyph of glyphs) {\n          const width = glyph.width || fallbackWidth;\n          this.glyphs.push([width * scale + letterSpacing, lineHeight, firstLineHeight, glyph.unicode, false]);\n        }\n        this.glyphs.push([0, 0, 0, \"\\n\", true]);\n      }\n      this.glyphs.pop();\n      return;\n    }\n    for (const line of str.split(/[\\u2029\\n]/)) {\n      for (const char of line.split(\"\")) {\n        this.glyphs.push([fontSize, 1.2 * fontSize, fontSize, char, false]);\n      }\n      this.glyphs.push([0, 0, 0, \"\\n\", true]);\n    }\n    this.glyphs.pop();\n  }\n  compute(maxWidth) {\n    let lastSpacePos = -1,\n      lastSpaceWidth = 0,\n      width = 0,\n      height = 0,\n      currentLineWidth = 0,\n      currentLineHeight = 0;\n    let isBroken = false;\n    let isFirstLine = true;\n    for (let i = 0, ii = this.glyphs.length; i < ii; i++) {\n      const [glyphWidth, lineHeight, firstLineHeight, char, isEOL] = this.glyphs[i];\n      const isSpace = char === \" \";\n      const glyphHeight = isFirstLine ? firstLineHeight : lineHeight;\n      if (isEOL) {\n        width = Math.max(width, currentLineWidth);\n        currentLineWidth = 0;\n        height += currentLineHeight;\n        currentLineHeight = glyphHeight;\n        lastSpacePos = -1;\n        lastSpaceWidth = 0;\n        isFirstLine = false;\n        continue;\n      }\n      if (isSpace) {\n        if (currentLineWidth + glyphWidth > maxWidth) {\n          width = Math.max(width, currentLineWidth);\n          currentLineWidth = 0;\n          height += currentLineHeight;\n          currentLineHeight = glyphHeight;\n          lastSpacePos = -1;\n          lastSpaceWidth = 0;\n          isBroken = true;\n          isFirstLine = false;\n        } else {\n          currentLineHeight = Math.max(glyphHeight, currentLineHeight);\n          lastSpaceWidth = currentLineWidth;\n          currentLineWidth += glyphWidth;\n          lastSpacePos = i;\n        }\n        continue;\n      }\n      if (currentLineWidth + glyphWidth > maxWidth) {\n        height += currentLineHeight;\n        currentLineHeight = glyphHeight;\n        if (lastSpacePos !== -1) {\n          i = lastSpacePos;\n          width = Math.max(width, lastSpaceWidth);\n          currentLineWidth = 0;\n          lastSpacePos = -1;\n          lastSpaceWidth = 0;\n        } else {\n          width = Math.max(width, currentLineWidth);\n          currentLineWidth = glyphWidth;\n        }\n        isBroken = true;\n        isFirstLine = false;\n        continue;\n      }\n      currentLineWidth += glyphWidth;\n      currentLineHeight = Math.max(glyphHeight, currentLineHeight);\n    }\n    width = Math.max(width, currentLineWidth);\n    height += currentLineHeight + this.extraHeight;\n    return {\n      width: WIDTH_FACTOR * width,\n      height,\n      isBroken\n    };\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/som.js\n\n\nconst namePattern = /^[^.[]+/;\nconst indexPattern = /^[^\\]]+/;\nconst operators = {\n  dot: 0,\n  dotDot: 1,\n  dotHash: 2,\n  dotBracket: 3,\n  dotParen: 4\n};\nconst shortcuts = new Map([[\"$data\", (root, current) => root.datasets ? root.datasets.data : root], [\"$record\", (root, current) => (root.datasets ? root.datasets.data : root)[$getChildren]()[0]], [\"$template\", (root, current) => root.template], [\"$connectionSet\", (root, current) => root.connectionSet], [\"$form\", (root, current) => root.form], [\"$layout\", (root, current) => root.layout], [\"$host\", (root, current) => root.host], [\"$dataWindow\", (root, current) => root.dataWindow], [\"$event\", (root, current) => root.event], [\"!\", (root, current) => root.datasets], [\"$xfa\", (root, current) => root], [\"xfa\", (root, current) => root], [\"$\", (root, current) => current]]);\nconst somCache = new WeakMap();\nfunction parseIndex(index) {\n  index = index.trim();\n  if (index === \"*\") {\n    return Infinity;\n  }\n  return parseInt(index, 10) || 0;\n}\nfunction parseExpression(expr, dotDotAllowed, noExpr = true) {\n  let match = expr.match(namePattern);\n  if (!match) {\n    return null;\n  }\n  let [name] = match;\n  const parsed = [{\n    name,\n    cacheName: \".\" + name,\n    index: 0,\n    js: null,\n    formCalc: null,\n    operator: operators.dot\n  }];\n  let pos = name.length;\n  while (pos < expr.length) {\n    const spos = pos;\n    const char = expr.charAt(pos++);\n    if (char === \"[\") {\n      match = expr.slice(pos).match(indexPattern);\n      if (!match) {\n        warn(\"XFA - Invalid index in SOM expression\");\n        return null;\n      }\n      parsed.at(-1).index = parseIndex(match[0]);\n      pos += match[0].length + 1;\n      continue;\n    }\n    let operator;\n    switch (expr.charAt(pos)) {\n      case \".\":\n        if (!dotDotAllowed) {\n          return null;\n        }\n        pos++;\n        operator = operators.dotDot;\n        break;\n      case \"#\":\n        pos++;\n        operator = operators.dotHash;\n        break;\n      case \"[\":\n        if (noExpr) {\n          warn(\"XFA - SOM expression contains a FormCalc subexpression which is not supported for now.\");\n          return null;\n        }\n        operator = operators.dotBracket;\n        break;\n      case \"(\":\n        if (noExpr) {\n          warn(\"XFA - SOM expression contains a JavaScript subexpression which is not supported for now.\");\n          return null;\n        }\n        operator = operators.dotParen;\n        break;\n      default:\n        operator = operators.dot;\n        break;\n    }\n    match = expr.slice(pos).match(namePattern);\n    if (!match) {\n      break;\n    }\n    [name] = match;\n    pos += name.length;\n    parsed.push({\n      name,\n      cacheName: expr.slice(spos, pos),\n      operator,\n      index: 0,\n      js: null,\n      formCalc: null\n    });\n  }\n  return parsed;\n}\nfunction searchNode(root, container, expr, dotDotAllowed = true, useCache = true) {\n  const parsed = parseExpression(expr, dotDotAllowed);\n  if (!parsed) {\n    return null;\n  }\n  const fn = shortcuts.get(parsed[0].name);\n  let i = 0;\n  let isQualified;\n  if (fn) {\n    isQualified = true;\n    root = [fn(root, container)];\n    i = 1;\n  } else {\n    isQualified = container === null;\n    root = [container || root];\n  }\n  for (let ii = parsed.length; i < ii; i++) {\n    const {\n      name,\n      cacheName,\n      operator,\n      index\n    } = parsed[i];\n    const nodes = [];\n    for (const node of root) {\n      if (!node.isXFAObject) {\n        continue;\n      }\n      let children, cached;\n      if (useCache) {\n        cached = somCache.get(node);\n        if (!cached) {\n          cached = new Map();\n          somCache.set(node, cached);\n        }\n        children = cached.get(cacheName);\n      }\n      if (!children) {\n        switch (operator) {\n          case operators.dot:\n            children = node[$getChildrenByName](name, false);\n            break;\n          case operators.dotDot:\n            children = node[$getChildrenByName](name, true);\n            break;\n          case operators.dotHash:\n            children = node[$getChildrenByClass](name);\n            children = children.isXFAObjectArray ? children.children : [children];\n            break;\n          default:\n            break;\n        }\n        if (useCache) {\n          cached.set(cacheName, children);\n        }\n      }\n      if (children.length > 0) {\n        nodes.push(children);\n      }\n    }\n    if (nodes.length === 0 && !isQualified && i === 0) {\n      const parent = container[$getParent]();\n      container = parent;\n      if (!container) {\n        return null;\n      }\n      i = -1;\n      root = [container];\n      continue;\n    }\n    root = isFinite(index) ? nodes.filter(node => index < node.length).map(node => node[index]) : nodes.flat();\n  }\n  if (root.length === 0) {\n    return null;\n  }\n  return root;\n}\nfunction createDataNode(root, container, expr) {\n  const parsed = parseExpression(expr);\n  if (!parsed) {\n    return null;\n  }\n  if (parsed.some(x => x.operator === operators.dotDot)) {\n    return null;\n  }\n  const fn = shortcuts.get(parsed[0].name);\n  let i = 0;\n  if (fn) {\n    root = fn(root, container);\n    i = 1;\n  } else {\n    root = container || root;\n  }\n  for (let ii = parsed.length; i < ii; i++) {\n    const {\n      name,\n      operator,\n      index\n    } = parsed[i];\n    if (!isFinite(index)) {\n      parsed[i].index = 0;\n      return root.createNodes(parsed.slice(i));\n    }\n    let children;\n    switch (operator) {\n      case operators.dot:\n        children = root[$getChildrenByName](name, false);\n        break;\n      case operators.dotDot:\n        children = root[$getChildrenByName](name, true);\n        break;\n      case operators.dotHash:\n        children = root[$getChildrenByClass](name);\n        children = children.isXFAObjectArray ? children.children : [children];\n        break;\n      default:\n        break;\n    }\n    if (children.length === 0) {\n      return root.createNodes(parsed.slice(i));\n    }\n    if (index < children.length) {\n      const child = children[index];\n      if (!child.isXFAObject) {\n        warn(`XFA - Cannot create a node.`);\n        return null;\n      }\n      root = child;\n    } else {\n      parsed[i].index = index - children.length;\n      return root.createNodes(parsed.slice(i));\n    }\n  }\n  return null;\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/xfa_object.js\n\n\n\n\n\n\nconst _applyPrototype = Symbol();\nconst _attributes = Symbol();\nconst _attributeNames = Symbol();\nconst _children = Symbol(\"_children\");\nconst _cloneAttribute = Symbol();\nconst _dataValue = Symbol();\nconst _defaultValue = Symbol();\nconst _filteredChildrenGenerator = Symbol();\nconst _getPrototype = Symbol();\nconst _getUnsetAttributes = Symbol();\nconst _hasChildren = Symbol();\nconst _max = Symbol();\nconst _options = Symbol();\nconst _parent = Symbol(\"parent\");\nconst _resolvePrototypesHelper = Symbol();\nconst _setAttributes = Symbol();\nconst _validator = Symbol();\nlet uid = 0;\nconst NS_DATASETS = NamespaceIds.datasets.id;\nclass XFAObject {\n  constructor(nsId, name, hasChildren = false) {\n    this[$namespaceId] = nsId;\n    this[$nodeName] = name;\n    this[_hasChildren] = hasChildren;\n    this[_parent] = null;\n    this[_children] = [];\n    this[$uid] = `${name}${uid++}`;\n    this[$globalData] = null;\n  }\n  get isXFAObject() {\n    return true;\n  }\n  get isXFAObjectArray() {\n    return false;\n  }\n  createNodes(path) {\n    let root = this,\n      node = null;\n    for (const {\n      name,\n      index\n    } of path) {\n      for (let i = 0, ii = isFinite(index) ? index : 0; i <= ii; i++) {\n        const nsId = root[$namespaceId] === NS_DATASETS ? -1 : root[$namespaceId];\n        node = new XmlObject(nsId, name);\n        root[$appendChild](node);\n      }\n      root = node;\n    }\n    return node;\n  }\n  [$onChild](child) {\n    if (!this[_hasChildren] || !this[$onChildCheck](child)) {\n      return false;\n    }\n    const name = child[$nodeName];\n    const node = this[name];\n    if (node instanceof XFAObjectArray) {\n      if (node.push(child)) {\n        this[$appendChild](child);\n        return true;\n      }\n    } else {\n      if (node !== null) {\n        this[$removeChild](node);\n      }\n      this[name] = child;\n      this[$appendChild](child);\n      return true;\n    }\n    let id = \"\";\n    if (this.id) {\n      id = ` (id: ${this.id})`;\n    } else if (this.name) {\n      id = ` (name: ${this.name} ${this.h.value})`;\n    }\n    warn(`XFA - node \"${this[$nodeName]}\"${id} has already enough \"${name}\"!`);\n    return false;\n  }\n  [$onChildCheck](child) {\n    return this.hasOwnProperty(child[$nodeName]) && child[$namespaceId] === this[$namespaceId];\n  }\n  [$isNsAgnostic]() {\n    return false;\n  }\n  [$acceptWhitespace]() {\n    return false;\n  }\n  [$isCDATAXml]() {\n    return false;\n  }\n  [$isBindable]() {\n    return false;\n  }\n  [$popPara]() {\n    if (this.para) {\n      this[$getTemplateRoot]()[$extra].paraStack.pop();\n    }\n  }\n  [$pushPara]() {\n    this[$getTemplateRoot]()[$extra].paraStack.push(this.para);\n  }\n  [$setId](ids) {\n    if (this.id && this[$namespaceId] === NamespaceIds.template.id) {\n      ids.set(this.id, this);\n    }\n  }\n  [$getTemplateRoot]() {\n    return this[$globalData].template;\n  }\n  [$isSplittable]() {\n    return false;\n  }\n  [$isThereMoreWidth]() {\n    return false;\n  }\n  [$appendChild](child) {\n    child[_parent] = this;\n    this[_children].push(child);\n    if (!child[$globalData] && this[$globalData]) {\n      child[$globalData] = this[$globalData];\n    }\n  }\n  [$removeChild](child) {\n    const i = this[_children].indexOf(child);\n    this[_children].splice(i, 1);\n  }\n  [$hasSettableValue]() {\n    return this.hasOwnProperty(\"value\");\n  }\n  [$setValue](_) {}\n  [$onText](_) {}\n  [$finalize]() {}\n  [$clean](builder) {\n    delete this[_hasChildren];\n    if (this[$cleanup]) {\n      builder.clean(this[$cleanup]);\n      delete this[$cleanup];\n    }\n  }\n  [$indexOf](child) {\n    return this[_children].indexOf(child);\n  }\n  [$insertAt](i, child) {\n    child[_parent] = this;\n    this[_children].splice(i, 0, child);\n    if (!child[$globalData] && this[$globalData]) {\n      child[$globalData] = this[$globalData];\n    }\n  }\n  [$isTransparent]() {\n    return !this.name;\n  }\n  [$lastAttribute]() {\n    return \"\";\n  }\n  [$text]() {\n    if (this[_children].length === 0) {\n      return this[$content];\n    }\n    return this[_children].map(c => c[$text]()).join(\"\");\n  }\n  get [_attributeNames]() {\n    const proto = Object.getPrototypeOf(this);\n    if (!proto._attributes) {\n      const attributes = proto._attributes = new Set();\n      for (const name of Object.getOwnPropertyNames(this)) {\n        if (this[name] === null || this[name] instanceof XFAObject || this[name] instanceof XFAObjectArray) {\n          break;\n        }\n        attributes.add(name);\n      }\n    }\n    return shadow(this, _attributeNames, proto._attributes);\n  }\n  [$isDescendent](parent) {\n    let node = this;\n    while (node) {\n      if (node === parent) {\n        return true;\n      }\n      node = node[$getParent]();\n    }\n    return false;\n  }\n  [$getParent]() {\n    return this[_parent];\n  }\n  [$getSubformParent]() {\n    return this[$getParent]();\n  }\n  [$getChildren](name = null) {\n    if (!name) {\n      return this[_children];\n    }\n    return this[name];\n  }\n  [$dump]() {\n    const dumped = Object.create(null);\n    if (this[$content]) {\n      dumped.$content = this[$content];\n    }\n    for (const name of Object.getOwnPropertyNames(this)) {\n      const value = this[name];\n      if (value === null) {\n        continue;\n      }\n      if (value instanceof XFAObject) {\n        dumped[name] = value[$dump]();\n      } else if (value instanceof XFAObjectArray) {\n        if (!value.isEmpty()) {\n          dumped[name] = value.dump();\n        }\n      } else {\n        dumped[name] = value;\n      }\n    }\n    return dumped;\n  }\n  [$toStyle]() {\n    return null;\n  }\n  [$toHTML]() {\n    return HTMLResult.EMPTY;\n  }\n  *[$getContainedChildren]() {\n    for (const node of this[$getChildren]()) {\n      yield node;\n    }\n  }\n  *[_filteredChildrenGenerator](filter, include) {\n    for (const node of this[$getContainedChildren]()) {\n      if (!filter || include === filter.has(node[$nodeName])) {\n        const availableSpace = this[$getAvailableSpace]();\n        const res = node[$toHTML](availableSpace);\n        if (!res.success) {\n          this[$extra].failingNode = node;\n        }\n        yield res;\n      }\n    }\n  }\n  [$flushHTML]() {\n    return null;\n  }\n  [$addHTML](html, bbox) {\n    this[$extra].children.push(html);\n  }\n  [$getAvailableSpace]() {}\n  [$childrenToHTML]({\n    filter = null,\n    include = true\n  }) {\n    if (!this[$extra].generator) {\n      this[$extra].generator = this[_filteredChildrenGenerator](filter, include);\n    } else {\n      const availableSpace = this[$getAvailableSpace]();\n      const res = this[$extra].failingNode[$toHTML](availableSpace);\n      if (!res.success) {\n        return res;\n      }\n      if (res.html) {\n        this[$addHTML](res.html, res.bbox);\n      }\n      delete this[$extra].failingNode;\n    }\n    while (true) {\n      const gen = this[$extra].generator.next();\n      if (gen.done) {\n        break;\n      }\n      const res = gen.value;\n      if (!res.success) {\n        return res;\n      }\n      if (res.html) {\n        this[$addHTML](res.html, res.bbox);\n      }\n    }\n    this[$extra].generator = null;\n    return HTMLResult.EMPTY;\n  }\n  [$setSetAttributes](attributes) {\n    this[_setAttributes] = new Set(Object.keys(attributes));\n  }\n  [_getUnsetAttributes](protoAttributes) {\n    const allAttr = this[_attributeNames];\n    const setAttr = this[_setAttributes];\n    return [...protoAttributes].filter(x => allAttr.has(x) && !setAttr.has(x));\n  }\n  [$resolvePrototypes](ids, ancestors = new Set()) {\n    for (const child of this[_children]) {\n      child[_resolvePrototypesHelper](ids, ancestors);\n    }\n  }\n  [_resolvePrototypesHelper](ids, ancestors) {\n    const proto = this[_getPrototype](ids, ancestors);\n    if (proto) {\n      this[_applyPrototype](proto, ids, ancestors);\n    } else {\n      this[$resolvePrototypes](ids, ancestors);\n    }\n  }\n  [_getPrototype](ids, ancestors) {\n    const {\n      use,\n      usehref\n    } = this;\n    if (!use && !usehref) {\n      return null;\n    }\n    let proto = null;\n    let somExpression = null;\n    let id = null;\n    let ref = use;\n    if (usehref) {\n      ref = usehref;\n      if (usehref.startsWith(\"#som(\") && usehref.endsWith(\")\")) {\n        somExpression = usehref.slice(\"#som(\".length, -1);\n      } else if (usehref.startsWith(\".#som(\") && usehref.endsWith(\")\")) {\n        somExpression = usehref.slice(\".#som(\".length, -1);\n      } else if (usehref.startsWith(\"#\")) {\n        id = usehref.slice(1);\n      } else if (usehref.startsWith(\".#\")) {\n        id = usehref.slice(2);\n      }\n    } else if (use.startsWith(\"#\")) {\n      id = use.slice(1);\n    } else {\n      somExpression = use;\n    }\n    this.use = this.usehref = \"\";\n    if (id) {\n      proto = ids.get(id);\n    } else {\n      proto = searchNode(ids.get($root), this, somExpression, true, false);\n      if (proto) {\n        proto = proto[0];\n      }\n    }\n    if (!proto) {\n      warn(`XFA - Invalid prototype reference: ${ref}.`);\n      return null;\n    }\n    if (proto[$nodeName] !== this[$nodeName]) {\n      warn(`XFA - Incompatible prototype: ${proto[$nodeName]} !== ${this[$nodeName]}.`);\n      return null;\n    }\n    if (ancestors.has(proto)) {\n      warn(`XFA - Cycle detected in prototypes use.`);\n      return null;\n    }\n    ancestors.add(proto);\n    const protoProto = proto[_getPrototype](ids, ancestors);\n    if (protoProto) {\n      proto[_applyPrototype](protoProto, ids, ancestors);\n    }\n    proto[$resolvePrototypes](ids, ancestors);\n    ancestors.delete(proto);\n    return proto;\n  }\n  [_applyPrototype](proto, ids, ancestors) {\n    if (ancestors.has(proto)) {\n      warn(`XFA - Cycle detected in prototypes use.`);\n      return;\n    }\n    if (!this[$content] && proto[$content]) {\n      this[$content] = proto[$content];\n    }\n    const newAncestors = new Set(ancestors);\n    newAncestors.add(proto);\n    for (const unsetAttrName of this[_getUnsetAttributes](proto[_setAttributes])) {\n      this[unsetAttrName] = proto[unsetAttrName];\n      if (this[_setAttributes]) {\n        this[_setAttributes].add(unsetAttrName);\n      }\n    }\n    for (const name of Object.getOwnPropertyNames(this)) {\n      if (this[_attributeNames].has(name)) {\n        continue;\n      }\n      const value = this[name];\n      const protoValue = proto[name];\n      if (value instanceof XFAObjectArray) {\n        for (const child of value[_children]) {\n          child[_resolvePrototypesHelper](ids, ancestors);\n        }\n        for (let i = value[_children].length, ii = protoValue[_children].length; i < ii; i++) {\n          const child = proto[_children][i][$clone]();\n          if (value.push(child)) {\n            child[_parent] = this;\n            this[_children].push(child);\n            child[_resolvePrototypesHelper](ids, ancestors);\n          } else {\n            break;\n          }\n        }\n        continue;\n      }\n      if (value !== null) {\n        value[$resolvePrototypes](ids, ancestors);\n        if (protoValue) {\n          value[_applyPrototype](protoValue, ids, ancestors);\n        }\n        continue;\n      }\n      if (protoValue !== null) {\n        const child = protoValue[$clone]();\n        child[_parent] = this;\n        this[name] = child;\n        this[_children].push(child);\n        child[_resolvePrototypesHelper](ids, ancestors);\n      }\n    }\n  }\n  static [_cloneAttribute](obj) {\n    if (Array.isArray(obj)) {\n      return obj.map(x => XFAObject[_cloneAttribute](x));\n    }\n    if (typeof obj === \"object\" && obj !== null) {\n      return Object.assign({}, obj);\n    }\n    return obj;\n  }\n  [$clone]() {\n    const clone = Object.create(Object.getPrototypeOf(this));\n    for (const $symbol of Object.getOwnPropertySymbols(this)) {\n      try {\n        clone[$symbol] = this[$symbol];\n      } catch {\n        shadow(clone, $symbol, this[$symbol]);\n      }\n    }\n    clone[$uid] = `${clone[$nodeName]}${uid++}`;\n    clone[_children] = [];\n    for (const name of Object.getOwnPropertyNames(this)) {\n      if (this[_attributeNames].has(name)) {\n        clone[name] = XFAObject[_cloneAttribute](this[name]);\n        continue;\n      }\n      const value = this[name];\n      clone[name] = value instanceof XFAObjectArray ? new XFAObjectArray(value[_max]) : null;\n    }\n    for (const child of this[_children]) {\n      const name = child[$nodeName];\n      const clonedChild = child[$clone]();\n      clone[_children].push(clonedChild);\n      clonedChild[_parent] = clone;\n      if (clone[name] === null) {\n        clone[name] = clonedChild;\n      } else {\n        clone[name][_children].push(clonedChild);\n      }\n    }\n    return clone;\n  }\n  [$getChildren](name = null) {\n    if (!name) {\n      return this[_children];\n    }\n    return this[_children].filter(c => c[$nodeName] === name);\n  }\n  [$getChildrenByClass](name) {\n    return this[name];\n  }\n  [$getChildrenByName](name, allTransparent, first = true) {\n    return Array.from(this[$getChildrenByNameIt](name, allTransparent, first));\n  }\n  *[$getChildrenByNameIt](name, allTransparent, first = true) {\n    if (name === \"parent\") {\n      yield this[_parent];\n      return;\n    }\n    for (const child of this[_children]) {\n      if (child[$nodeName] === name) {\n        yield child;\n      }\n      if (child.name === name) {\n        yield child;\n      }\n      if (allTransparent || child[$isTransparent]()) {\n        yield* child[$getChildrenByNameIt](name, allTransparent, false);\n      }\n    }\n    if (first && this[_attributeNames].has(name)) {\n      yield new XFAAttribute(this, name, this[name]);\n    }\n  }\n}\nclass XFAObjectArray {\n  constructor(max = Infinity) {\n    this[_max] = max;\n    this[_children] = [];\n  }\n  get isXFAObject() {\n    return false;\n  }\n  get isXFAObjectArray() {\n    return true;\n  }\n  push(child) {\n    const len = this[_children].length;\n    if (len <= this[_max]) {\n      this[_children].push(child);\n      return true;\n    }\n    warn(`XFA - node \"${child[$nodeName]}\" accepts no more than ${this[_max]} children`);\n    return false;\n  }\n  isEmpty() {\n    return this[_children].length === 0;\n  }\n  dump() {\n    return this[_children].length === 1 ? this[_children][0][$dump]() : this[_children].map(x => x[$dump]());\n  }\n  [$clone]() {\n    const clone = new XFAObjectArray(this[_max]);\n    clone[_children] = this[_children].map(c => c[$clone]());\n    return clone;\n  }\n  get children() {\n    return this[_children];\n  }\n  clear() {\n    this[_children].length = 0;\n  }\n}\nclass XFAAttribute {\n  constructor(node, name, value) {\n    this[_parent] = node;\n    this[$nodeName] = name;\n    this[$content] = value;\n    this[$consumed] = false;\n    this[$uid] = `attribute${uid++}`;\n  }\n  [$getParent]() {\n    return this[_parent];\n  }\n  [$isDataValue]() {\n    return true;\n  }\n  [$getDataValue]() {\n    return this[$content].trim();\n  }\n  [$setValue](value) {\n    value = value.value || \"\";\n    this[$content] = value.toString();\n  }\n  [$text]() {\n    return this[$content];\n  }\n  [$isDescendent](parent) {\n    return this[_parent] === parent || this[_parent][$isDescendent](parent);\n  }\n}\nclass XmlObject extends XFAObject {\n  constructor(nsId, name, attributes = {}) {\n    super(nsId, name);\n    this[$content] = \"\";\n    this[_dataValue] = null;\n    if (name !== \"#text\") {\n      const map = new Map();\n      this[_attributes] = map;\n      for (const [attrName, value] of Object.entries(attributes)) {\n        map.set(attrName, new XFAAttribute(this, attrName, value));\n      }\n      if (attributes.hasOwnProperty($nsAttributes)) {\n        const dataNode = attributes[$nsAttributes].xfa.dataNode;\n        if (dataNode !== undefined) {\n          if (dataNode === \"dataGroup\") {\n            this[_dataValue] = false;\n          } else if (dataNode === \"dataValue\") {\n            this[_dataValue] = true;\n          }\n        }\n      }\n    }\n    this[$consumed] = false;\n  }\n  [$toString](buf) {\n    const tagName = this[$nodeName];\n    if (tagName === \"#text\") {\n      buf.push(encodeToXmlString(this[$content]));\n      return;\n    }\n    const utf8TagName = utf8StringToString(tagName);\n    const prefix = this[$namespaceId] === NS_DATASETS ? \"xfa:\" : \"\";\n    buf.push(`<${prefix}${utf8TagName}`);\n    for (const [name, value] of this[_attributes].entries()) {\n      const utf8Name = utf8StringToString(name);\n      buf.push(` ${utf8Name}=\"${encodeToXmlString(value[$content])}\"`);\n    }\n    if (this[_dataValue] !== null) {\n      if (this[_dataValue]) {\n        buf.push(` xfa:dataNode=\"dataValue\"`);\n      } else {\n        buf.push(` xfa:dataNode=\"dataGroup\"`);\n      }\n    }\n    if (!this[$content] && this[_children].length === 0) {\n      buf.push(\"/>\");\n      return;\n    }\n    buf.push(\">\");\n    if (this[$content]) {\n      if (typeof this[$content] === \"string\") {\n        buf.push(encodeToXmlString(this[$content]));\n      } else {\n        this[$content][$toString](buf);\n      }\n    } else {\n      for (const child of this[_children]) {\n        child[$toString](buf);\n      }\n    }\n    buf.push(`</${prefix}${utf8TagName}>`);\n  }\n  [$onChild](child) {\n    if (this[$content]) {\n      const node = new XmlObject(this[$namespaceId], \"#text\");\n      this[$appendChild](node);\n      node[$content] = this[$content];\n      this[$content] = \"\";\n    }\n    this[$appendChild](child);\n    return true;\n  }\n  [$onText](str) {\n    this[$content] += str;\n  }\n  [$finalize]() {\n    if (this[$content] && this[_children].length > 0) {\n      const node = new XmlObject(this[$namespaceId], \"#text\");\n      this[$appendChild](node);\n      node[$content] = this[$content];\n      delete this[$content];\n    }\n  }\n  [$toHTML]() {\n    if (this[$nodeName] === \"#text\") {\n      return HTMLResult.success({\n        name: \"#text\",\n        value: this[$content]\n      });\n    }\n    return HTMLResult.EMPTY;\n  }\n  [$getChildren](name = null) {\n    if (!name) {\n      return this[_children];\n    }\n    return this[_children].filter(c => c[$nodeName] === name);\n  }\n  [$getAttributes]() {\n    return this[_attributes];\n  }\n  [$getChildrenByClass](name) {\n    const value = this[_attributes].get(name);\n    if (value !== undefined) {\n      return value;\n    }\n    return this[$getChildren](name);\n  }\n  *[$getChildrenByNameIt](name, allTransparent) {\n    const value = this[_attributes].get(name);\n    if (value) {\n      yield value;\n    }\n    for (const child of this[_children]) {\n      if (child[$nodeName] === name) {\n        yield child;\n      }\n      if (allTransparent) {\n        yield* child[$getChildrenByNameIt](name, allTransparent);\n      }\n    }\n  }\n  *[$getAttributeIt](name, skipConsumed) {\n    const value = this[_attributes].get(name);\n    if (value && (!skipConsumed || !value[$consumed])) {\n      yield value;\n    }\n    for (const child of this[_children]) {\n      yield* child[$getAttributeIt](name, skipConsumed);\n    }\n  }\n  *[$getRealChildrenByNameIt](name, allTransparent, skipConsumed) {\n    for (const child of this[_children]) {\n      if (child[$nodeName] === name && (!skipConsumed || !child[$consumed])) {\n        yield child;\n      }\n      if (allTransparent) {\n        yield* child[$getRealChildrenByNameIt](name, allTransparent, skipConsumed);\n      }\n    }\n  }\n  [$isDataValue]() {\n    if (this[_dataValue] === null) {\n      return this[_children].length === 0 || this[_children][0][$namespaceId] === NamespaceIds.xhtml.id;\n    }\n    return this[_dataValue];\n  }\n  [$getDataValue]() {\n    if (this[_dataValue] === null) {\n      if (this[_children].length === 0) {\n        return this[$content].trim();\n      }\n      if (this[_children][0][$namespaceId] === NamespaceIds.xhtml.id) {\n        return this[_children][0][$text]().trim();\n      }\n      return null;\n    }\n    return this[$content].trim();\n  }\n  [$setValue](value) {\n    value = value.value || \"\";\n    this[$content] = value.toString();\n  }\n  [$dump](hasNS = false) {\n    const dumped = Object.create(null);\n    if (hasNS) {\n      dumped.$ns = this[$namespaceId];\n    }\n    if (this[$content]) {\n      dumped.$content = this[$content];\n    }\n    dumped.$name = this[$nodeName];\n    dumped.children = [];\n    for (const child of this[_children]) {\n      dumped.children.push(child[$dump](hasNS));\n    }\n    dumped.attributes = Object.create(null);\n    for (const [name, value] of this[_attributes]) {\n      dumped.attributes[name] = value[$content];\n    }\n    return dumped;\n  }\n}\nclass ContentObject extends XFAObject {\n  constructor(nsId, name) {\n    super(nsId, name);\n    this[$content] = \"\";\n  }\n  [$onText](text) {\n    this[$content] += text;\n  }\n  [$finalize]() {}\n}\nclass OptionObject extends ContentObject {\n  constructor(nsId, name, options) {\n    super(nsId, name);\n    this[_options] = options;\n  }\n  [$finalize]() {\n    this[$content] = getKeyword({\n      data: this[$content],\n      defaultValue: this[_options][0],\n      validate: k => this[_options].includes(k)\n    });\n  }\n  [$clean](builder) {\n    super[$clean](builder);\n    delete this[_options];\n  }\n}\nclass StringObject extends ContentObject {\n  [$finalize]() {\n    this[$content] = this[$content].trim();\n  }\n}\nclass IntegerObject extends ContentObject {\n  constructor(nsId, name, defaultValue, validator) {\n    super(nsId, name);\n    this[_defaultValue] = defaultValue;\n    this[_validator] = validator;\n  }\n  [$finalize]() {\n    this[$content] = getInteger({\n      data: this[$content],\n      defaultValue: this[_defaultValue],\n      validate: this[_validator]\n    });\n  }\n  [$clean](builder) {\n    super[$clean](builder);\n    delete this[_defaultValue];\n    delete this[_validator];\n  }\n}\nclass Option01 extends IntegerObject {\n  constructor(nsId, name) {\n    super(nsId, name, 0, n => n === 1);\n  }\n}\nclass Option10 extends IntegerObject {\n  constructor(nsId, name) {\n    super(nsId, name, 1, n => n === 0);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/html_utils.js\n\n\n\n\n\n\nfunction measureToString(m) {\n  if (typeof m === \"string\") {\n    return \"0px\";\n  }\n  return Number.isInteger(m) ? `${m}px` : `${m.toFixed(2)}px`;\n}\nconst converters = {\n  anchorType(node, style) {\n    const parent = node[$getSubformParent]();\n    if (!parent || parent.layout && parent.layout !== \"position\") {\n      return;\n    }\n    if (!(\"transform\" in style)) {\n      style.transform = \"\";\n    }\n    switch (node.anchorType) {\n      case \"bottomCenter\":\n        style.transform += \"translate(-50%, -100%)\";\n        break;\n      case \"bottomLeft\":\n        style.transform += \"translate(0,-100%)\";\n        break;\n      case \"bottomRight\":\n        style.transform += \"translate(-100%,-100%)\";\n        break;\n      case \"middleCenter\":\n        style.transform += \"translate(-50%,-50%)\";\n        break;\n      case \"middleLeft\":\n        style.transform += \"translate(0,-50%)\";\n        break;\n      case \"middleRight\":\n        style.transform += \"translate(-100%,-50%)\";\n        break;\n      case \"topCenter\":\n        style.transform += \"translate(-50%,0)\";\n        break;\n      case \"topRight\":\n        style.transform += \"translate(-100%,0)\";\n        break;\n    }\n  },\n  dimensions(node, style) {\n    const parent = node[$getSubformParent]();\n    let width = node.w;\n    const height = node.h;\n    if (parent.layout?.includes(\"row\")) {\n      const extra = parent[$extra];\n      const colSpan = node.colSpan;\n      let w;\n      if (colSpan === -1) {\n        w = extra.columnWidths.slice(extra.currentColumn).reduce((a, x) => a + x, 0);\n        extra.currentColumn = 0;\n      } else {\n        w = extra.columnWidths.slice(extra.currentColumn, extra.currentColumn + colSpan).reduce((a, x) => a + x, 0);\n        extra.currentColumn = (extra.currentColumn + node.colSpan) % extra.columnWidths.length;\n      }\n      if (!isNaN(w)) {\n        width = node.w = w;\n      }\n    }\n    style.width = width !== \"\" ? measureToString(width) : \"auto\";\n    style.height = height !== \"\" ? measureToString(height) : \"auto\";\n  },\n  position(node, style) {\n    const parent = node[$getSubformParent]();\n    if (parent?.layout && parent.layout !== \"position\") {\n      return;\n    }\n    style.position = \"absolute\";\n    style.left = measureToString(node.x);\n    style.top = measureToString(node.y);\n  },\n  rotate(node, style) {\n    if (node.rotate) {\n      if (!(\"transform\" in style)) {\n        style.transform = \"\";\n      }\n      style.transform += `rotate(-${node.rotate}deg)`;\n      style.transformOrigin = \"top left\";\n    }\n  },\n  presence(node, style) {\n    switch (node.presence) {\n      case \"invisible\":\n        style.visibility = \"hidden\";\n        break;\n      case \"hidden\":\n      case \"inactive\":\n        style.display = \"none\";\n        break;\n    }\n  },\n  hAlign(node, style) {\n    if (node[$nodeName] === \"para\") {\n      switch (node.hAlign) {\n        case \"justifyAll\":\n          style.textAlign = \"justify-all\";\n          break;\n        case \"radix\":\n          style.textAlign = \"left\";\n          break;\n        default:\n          style.textAlign = node.hAlign;\n      }\n    } else {\n      switch (node.hAlign) {\n        case \"left\":\n          style.alignSelf = \"start\";\n          break;\n        case \"center\":\n          style.alignSelf = \"center\";\n          break;\n        case \"right\":\n          style.alignSelf = \"end\";\n          break;\n      }\n    }\n  },\n  margin(node, style) {\n    if (node.margin) {\n      style.margin = node.margin[$toStyle]().margin;\n    }\n  }\n};\nfunction setMinMaxDimensions(node, style) {\n  const parent = node[$getSubformParent]();\n  if (parent.layout === \"position\") {\n    if (node.minW > 0) {\n      style.minWidth = measureToString(node.minW);\n    }\n    if (node.maxW > 0) {\n      style.maxWidth = measureToString(node.maxW);\n    }\n    if (node.minH > 0) {\n      style.minHeight = measureToString(node.minH);\n    }\n    if (node.maxH > 0) {\n      style.maxHeight = measureToString(node.maxH);\n    }\n  }\n}\nfunction layoutText(text, xfaFont, margin, lineHeight, fontFinder, width) {\n  const measure = new TextMeasure(xfaFont, margin, lineHeight, fontFinder);\n  if (typeof text === \"string\") {\n    measure.addString(text);\n  } else {\n    text[$pushGlyphs](measure);\n  }\n  return measure.compute(width);\n}\nfunction layoutNode(node, availableSpace) {\n  let height = null;\n  let width = null;\n  let isBroken = false;\n  if ((!node.w || !node.h) && node.value) {\n    let marginH = 0;\n    let marginV = 0;\n    if (node.margin) {\n      marginH = node.margin.leftInset + node.margin.rightInset;\n      marginV = node.margin.topInset + node.margin.bottomInset;\n    }\n    let lineHeight = null;\n    let margin = null;\n    if (node.para) {\n      margin = Object.create(null);\n      lineHeight = node.para.lineHeight === \"\" ? null : node.para.lineHeight;\n      margin.top = node.para.spaceAbove === \"\" ? 0 : node.para.spaceAbove;\n      margin.bottom = node.para.spaceBelow === \"\" ? 0 : node.para.spaceBelow;\n      margin.left = node.para.marginLeft === \"\" ? 0 : node.para.marginLeft;\n      margin.right = node.para.marginRight === \"\" ? 0 : node.para.marginRight;\n    }\n    let font = node.font;\n    if (!font) {\n      const root = node[$getTemplateRoot]();\n      let parent = node[$getParent]();\n      while (parent && parent !== root) {\n        if (parent.font) {\n          font = parent.font;\n          break;\n        }\n        parent = parent[$getParent]();\n      }\n    }\n    const maxWidth = (node.w || availableSpace.width) - marginH;\n    const fontFinder = node[$globalData].fontFinder;\n    if (node.value.exData && node.value.exData[$content] && node.value.exData.contentType === \"text/html\") {\n      const res = layoutText(node.value.exData[$content], font, margin, lineHeight, fontFinder, maxWidth);\n      width = res.width;\n      height = res.height;\n      isBroken = res.isBroken;\n    } else {\n      const text = node.value[$text]();\n      if (text) {\n        const res = layoutText(text, font, margin, lineHeight, fontFinder, maxWidth);\n        width = res.width;\n        height = res.height;\n        isBroken = res.isBroken;\n      }\n    }\n    if (width !== null && !node.w) {\n      width += marginH;\n    }\n    if (height !== null && !node.h) {\n      height += marginV;\n    }\n  }\n  return {\n    w: width,\n    h: height,\n    isBroken\n  };\n}\nfunction computeBbox(node, html, availableSpace) {\n  let bbox;\n  if (node.w !== \"\" && node.h !== \"\") {\n    bbox = [node.x, node.y, node.w, node.h];\n  } else {\n    if (!availableSpace) {\n      return null;\n    }\n    let width = node.w;\n    if (width === \"\") {\n      if (node.maxW === 0) {\n        const parent = node[$getSubformParent]();\n        width = parent.layout === \"position\" && parent.w !== \"\" ? 0 : node.minW;\n      } else {\n        width = Math.min(node.maxW, availableSpace.width);\n      }\n      html.attributes.style.width = measureToString(width);\n    }\n    let height = node.h;\n    if (height === \"\") {\n      if (node.maxH === 0) {\n        const parent = node[$getSubformParent]();\n        height = parent.layout === \"position\" && parent.h !== \"\" ? 0 : node.minH;\n      } else {\n        height = Math.min(node.maxH, availableSpace.height);\n      }\n      html.attributes.style.height = measureToString(height);\n    }\n    bbox = [node.x, node.y, width, height];\n  }\n  return bbox;\n}\nfunction fixDimensions(node) {\n  const parent = node[$getSubformParent]();\n  if (parent.layout?.includes(\"row\")) {\n    const extra = parent[$extra];\n    const colSpan = node.colSpan;\n    let width;\n    if (colSpan === -1) {\n      width = extra.columnWidths.slice(extra.currentColumn).reduce((a, w) => a + w, 0);\n    } else {\n      width = extra.columnWidths.slice(extra.currentColumn, extra.currentColumn + colSpan).reduce((a, w) => a + w, 0);\n    }\n    if (!isNaN(width)) {\n      node.w = width;\n    }\n  }\n  if (parent.layout && parent.layout !== \"position\") {\n    node.x = node.y = 0;\n  }\n  if (node.layout === \"table\") {\n    if (node.w === \"\" && Array.isArray(node.columnWidths)) {\n      node.w = node.columnWidths.reduce((a, x) => a + x, 0);\n    }\n  }\n}\nfunction layoutClass(node) {\n  switch (node.layout) {\n    case \"position\":\n      return \"xfaPosition\";\n    case \"lr-tb\":\n      return \"xfaLrTb\";\n    case \"rl-row\":\n      return \"xfaRlRow\";\n    case \"rl-tb\":\n      return \"xfaRlTb\";\n    case \"row\":\n      return \"xfaRow\";\n    case \"table\":\n      return \"xfaTable\";\n    case \"tb\":\n      return \"xfaTb\";\n    default:\n      return \"xfaPosition\";\n  }\n}\nfunction toStyle(node, ...names) {\n  const style = Object.create(null);\n  for (const name of names) {\n    const value = node[name];\n    if (value === null) {\n      continue;\n    }\n    if (converters.hasOwnProperty(name)) {\n      converters[name](node, style);\n      continue;\n    }\n    if (value instanceof XFAObject) {\n      const newStyle = value[$toStyle]();\n      if (newStyle) {\n        Object.assign(style, newStyle);\n      } else {\n        warn(`(DEBUG) - XFA - style for ${name} not implemented yet`);\n      }\n    }\n  }\n  return style;\n}\nfunction createWrapper(node, html) {\n  const {\n    attributes\n  } = html;\n  const {\n    style\n  } = attributes;\n  const wrapper = {\n    name: \"div\",\n    attributes: {\n      class: [\"xfaWrapper\"],\n      style: Object.create(null)\n    },\n    children: []\n  };\n  attributes.class.push(\"xfaWrapped\");\n  if (node.border) {\n    const {\n      widths,\n      insets\n    } = node.border[$extra];\n    let width, height;\n    let top = insets[0];\n    let left = insets[3];\n    const insetsH = insets[0] + insets[2];\n    const insetsW = insets[1] + insets[3];\n    switch (node.border.hand) {\n      case \"even\":\n        top -= widths[0] / 2;\n        left -= widths[3] / 2;\n        width = `calc(100% + ${(widths[1] + widths[3]) / 2 - insetsW}px)`;\n        height = `calc(100% + ${(widths[0] + widths[2]) / 2 - insetsH}px)`;\n        break;\n      case \"left\":\n        top -= widths[0];\n        left -= widths[3];\n        width = `calc(100% + ${widths[1] + widths[3] - insetsW}px)`;\n        height = `calc(100% + ${widths[0] + widths[2] - insetsH}px)`;\n        break;\n      case \"right\":\n        width = insetsW ? `calc(100% - ${insetsW}px)` : \"100%\";\n        height = insetsH ? `calc(100% - ${insetsH}px)` : \"100%\";\n        break;\n    }\n    const classNames = [\"xfaBorder\"];\n    if (isPrintOnly(node.border)) {\n      classNames.push(\"xfaPrintOnly\");\n    }\n    const border = {\n      name: \"div\",\n      attributes: {\n        class: classNames,\n        style: {\n          top: `${top}px`,\n          left: `${left}px`,\n          width,\n          height\n        }\n      },\n      children: []\n    };\n    for (const key of [\"border\", \"borderWidth\", \"borderColor\", \"borderRadius\", \"borderStyle\"]) {\n      if (style[key] !== undefined) {\n        border.attributes.style[key] = style[key];\n        delete style[key];\n      }\n    }\n    wrapper.children.push(border, html);\n  } else {\n    wrapper.children.push(html);\n  }\n  for (const key of [\"background\", \"backgroundClip\", \"top\", \"left\", \"width\", \"height\", \"minWidth\", \"minHeight\", \"maxWidth\", \"maxHeight\", \"transform\", \"transformOrigin\", \"visibility\"]) {\n    if (style[key] !== undefined) {\n      wrapper.attributes.style[key] = style[key];\n      delete style[key];\n    }\n  }\n  wrapper.attributes.style.position = style.position === \"absolute\" ? \"absolute\" : \"relative\";\n  delete style.position;\n  if (style.alignSelf) {\n    wrapper.attributes.style.alignSelf = style.alignSelf;\n    delete style.alignSelf;\n  }\n  return wrapper;\n}\nfunction fixTextIndent(styles) {\n  const indent = getMeasurement(styles.textIndent, \"0px\");\n  if (indent >= 0) {\n    return;\n  }\n  const align = styles.textAlign === \"right\" ? \"right\" : \"left\";\n  const name = \"padding\" + (align === \"left\" ? \"Left\" : \"Right\");\n  const padding = getMeasurement(styles[name], \"0px\");\n  styles[name] = `${padding - indent}px`;\n}\nfunction setAccess(node, classNames) {\n  switch (node.access) {\n    case \"nonInteractive\":\n      classNames.push(\"xfaNonInteractive\");\n      break;\n    case \"readOnly\":\n      classNames.push(\"xfaReadOnly\");\n      break;\n    case \"protected\":\n      classNames.push(\"xfaDisabled\");\n      break;\n  }\n}\nfunction isPrintOnly(node) {\n  return node.relevant.length > 0 && !node.relevant[0].excluded && node.relevant[0].viewname === \"print\";\n}\nfunction getCurrentPara(node) {\n  const stack = node[$getTemplateRoot]()[$extra].paraStack;\n  return stack.length ? stack.at(-1) : null;\n}\nfunction setPara(node, nodeStyle, value) {\n  if (value.attributes.class?.includes(\"xfaRich\")) {\n    if (nodeStyle) {\n      if (node.h === \"\") {\n        nodeStyle.height = \"auto\";\n      }\n      if (node.w === \"\") {\n        nodeStyle.width = \"auto\";\n      }\n    }\n    const para = getCurrentPara(node);\n    if (para) {\n      const valueStyle = value.attributes.style;\n      valueStyle.display = \"flex\";\n      valueStyle.flexDirection = \"column\";\n      switch (para.vAlign) {\n        case \"top\":\n          valueStyle.justifyContent = \"start\";\n          break;\n        case \"bottom\":\n          valueStyle.justifyContent = \"end\";\n          break;\n        case \"middle\":\n          valueStyle.justifyContent = \"center\";\n          break;\n      }\n      const paraStyle = para[$toStyle]();\n      for (const [key, val] of Object.entries(paraStyle)) {\n        if (!(key in valueStyle)) {\n          valueStyle[key] = val;\n        }\n      }\n    }\n  }\n}\nfunction setFontFamily(xfaFont, node, fontFinder, style) {\n  if (!fontFinder) {\n    delete style.fontFamily;\n    return;\n  }\n  const name = stripQuotes(xfaFont.typeface);\n  style.fontFamily = `\"${name}\"`;\n  const typeface = fontFinder.find(name);\n  if (typeface) {\n    const {\n      fontFamily\n    } = typeface.regular.cssFontInfo;\n    if (fontFamily !== name) {\n      style.fontFamily = `\"${fontFamily}\"`;\n    }\n    const para = getCurrentPara(node);\n    if (para && para.lineHeight !== \"\") {\n      return;\n    }\n    if (style.lineHeight) {\n      return;\n    }\n    const pdfFont = selectFont(xfaFont, typeface);\n    if (pdfFont) {\n      style.lineHeight = Math.max(1.2, pdfFont.lineHeight);\n    }\n  }\n}\nfunction fixURL(str) {\n  const absoluteUrl = createValidAbsoluteUrl(str, null, {\n    addDefaultProtocol: true,\n    tryConvertEncoding: true\n  });\n  return absoluteUrl ? absoluteUrl.href : null;\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/layout.js\n\n\nfunction createLine(node, children) {\n  return {\n    name: \"div\",\n    attributes: {\n      class: [node.layout === \"lr-tb\" ? \"xfaLr\" : \"xfaRl\"]\n    },\n    children\n  };\n}\nfunction flushHTML(node) {\n  if (!node[$extra]) {\n    return null;\n  }\n  const attributes = node[$extra].attributes;\n  const html = {\n    name: \"div\",\n    attributes,\n    children: node[$extra].children\n  };\n  if (node[$extra].failingNode) {\n    const htmlFromFailing = node[$extra].failingNode[$flushHTML]();\n    if (htmlFromFailing) {\n      if (node.layout.endsWith(\"-tb\")) {\n        html.children.push(createLine(node, [htmlFromFailing]));\n      } else {\n        html.children.push(htmlFromFailing);\n      }\n    }\n  }\n  if (html.children.length === 0) {\n    return null;\n  }\n  return html;\n}\nfunction addHTML(node, html, bbox) {\n  const extra = node[$extra];\n  const availableSpace = extra.availableSpace;\n  const [x, y, w, h] = bbox;\n  switch (node.layout) {\n    case \"position\":\n      {\n        extra.width = Math.max(extra.width, x + w);\n        extra.height = Math.max(extra.height, y + h);\n        extra.children.push(html);\n        break;\n      }\n    case \"lr-tb\":\n    case \"rl-tb\":\n      if (!extra.line || extra.attempt === 1) {\n        extra.line = createLine(node, []);\n        extra.children.push(extra.line);\n        extra.numberInLine = 0;\n      }\n      extra.numberInLine += 1;\n      extra.line.children.push(html);\n      if (extra.attempt === 0) {\n        extra.currentWidth += w;\n        extra.height = Math.max(extra.height, extra.prevHeight + h);\n      } else {\n        extra.currentWidth = w;\n        extra.prevHeight = extra.height;\n        extra.height += h;\n        extra.attempt = 0;\n      }\n      extra.width = Math.max(extra.width, extra.currentWidth);\n      break;\n    case \"rl-row\":\n    case \"row\":\n      {\n        extra.children.push(html);\n        extra.width += w;\n        extra.height = Math.max(extra.height, h);\n        const height = measureToString(extra.height);\n        for (const child of extra.children) {\n          child.attributes.style.height = height;\n        }\n        break;\n      }\n    case \"table\":\n      {\n        extra.width = Math.min(availableSpace.width, Math.max(extra.width, w));\n        extra.height += h;\n        extra.children.push(html);\n        break;\n      }\n    case \"tb\":\n      {\n        extra.width = Math.min(availableSpace.width, Math.max(extra.width, w));\n        extra.height += h;\n        extra.children.push(html);\n        break;\n      }\n  }\n}\nfunction getAvailableSpace(node) {\n  const availableSpace = node[$extra].availableSpace;\n  const marginV = node.margin ? node.margin.topInset + node.margin.bottomInset : 0;\n  const marginH = node.margin ? node.margin.leftInset + node.margin.rightInset : 0;\n  switch (node.layout) {\n    case \"lr-tb\":\n    case \"rl-tb\":\n      if (node[$extra].attempt === 0) {\n        return {\n          width: availableSpace.width - marginH - node[$extra].currentWidth,\n          height: availableSpace.height - marginV - node[$extra].prevHeight\n        };\n      }\n      return {\n        width: availableSpace.width - marginH,\n        height: availableSpace.height - marginV - node[$extra].height\n      };\n    case \"rl-row\":\n    case \"row\":\n      const width = node[$extra].columnWidths.slice(node[$extra].currentColumn).reduce((a, x) => a + x);\n      return {\n        width,\n        height: availableSpace.height - marginH\n      };\n    case \"table\":\n    case \"tb\":\n      return {\n        width: availableSpace.width - marginH,\n        height: availableSpace.height - marginV - node[$extra].height\n      };\n    case \"position\":\n    default:\n      return availableSpace;\n  }\n}\nfunction getTransformedBBox(node) {\n  let w = node.w === \"\" ? NaN : node.w;\n  let h = node.h === \"\" ? NaN : node.h;\n  let [centerX, centerY] = [0, 0];\n  switch (node.anchorType || \"\") {\n    case \"bottomCenter\":\n      [centerX, centerY] = [w / 2, h];\n      break;\n    case \"bottomLeft\":\n      [centerX, centerY] = [0, h];\n      break;\n    case \"bottomRight\":\n      [centerX, centerY] = [w, h];\n      break;\n    case \"middleCenter\":\n      [centerX, centerY] = [w / 2, h / 2];\n      break;\n    case \"middleLeft\":\n      [centerX, centerY] = [0, h / 2];\n      break;\n    case \"middleRight\":\n      [centerX, centerY] = [w, h / 2];\n      break;\n    case \"topCenter\":\n      [centerX, centerY] = [w / 2, 0];\n      break;\n    case \"topRight\":\n      [centerX, centerY] = [w, 0];\n      break;\n  }\n  let x, y;\n  switch (node.rotate || 0) {\n    case 0:\n      [x, y] = [-centerX, -centerY];\n      break;\n    case 90:\n      [x, y] = [-centerY, centerX];\n      [w, h] = [h, -w];\n      break;\n    case 180:\n      [x, y] = [centerX, centerY];\n      [w, h] = [-w, -h];\n      break;\n    case 270:\n      [x, y] = [centerY, -centerX];\n      [w, h] = [-h, w];\n      break;\n  }\n  return [node.x + x + Math.min(0, w), node.y + y + Math.min(0, h), Math.abs(w), Math.abs(h)];\n}\nfunction checkDimensions(node, space) {\n  if (node[$getTemplateRoot]()[$extra].firstUnsplittable === null) {\n    return true;\n  }\n  if (node.w === 0 || node.h === 0) {\n    return true;\n  }\n  const ERROR = 2;\n  const parent = node[$getSubformParent]();\n  const attempt = parent[$extra]?.attempt || 0;\n  const [, y, w, h] = getTransformedBBox(node);\n  switch (parent.layout) {\n    case \"lr-tb\":\n    case \"rl-tb\":\n      if (attempt === 0) {\n        if (!node[$getTemplateRoot]()[$extra].noLayoutFailure) {\n          if (node.h !== \"\" && Math.round(h - space.height) > ERROR) {\n            return false;\n          }\n          if (node.w !== \"\") {\n            if (Math.round(w - space.width) <= ERROR) {\n              return true;\n            }\n            if (parent[$extra].numberInLine === 0) {\n              return space.height > ERROR;\n            }\n            return false;\n          }\n          return space.width > ERROR;\n        }\n        if (node.w !== \"\") {\n          return Math.round(w - space.width) <= ERROR;\n        }\n        return space.width > ERROR;\n      }\n      if (node[$getTemplateRoot]()[$extra].noLayoutFailure) {\n        return true;\n      }\n      if (node.h !== \"\" && Math.round(h - space.height) > ERROR) {\n        return false;\n      }\n      if (node.w === \"\" || Math.round(w - space.width) <= ERROR) {\n        return space.height > ERROR;\n      }\n      if (parent[$isThereMoreWidth]()) {\n        return false;\n      }\n      return space.height > ERROR;\n    case \"table\":\n    case \"tb\":\n      if (node[$getTemplateRoot]()[$extra].noLayoutFailure) {\n        return true;\n      }\n      if (node.h !== \"\" && !node[$isSplittable]()) {\n        return Math.round(h - space.height) <= ERROR;\n      }\n      if (node.w === \"\" || Math.round(w - space.width) <= ERROR) {\n        return space.height > ERROR;\n      }\n      if (parent[$isThereMoreWidth]()) {\n        return false;\n      }\n      return space.height > ERROR;\n    case \"position\":\n      if (node[$getTemplateRoot]()[$extra].noLayoutFailure) {\n        return true;\n      }\n      if (node.h === \"\" || Math.round(h + y - space.height) <= ERROR) {\n        return true;\n      }\n      const area = node[$getTemplateRoot]()[$extra].currentContentArea;\n      return h + y > area.h;\n    case \"rl-row\":\n    case \"row\":\n      if (node[$getTemplateRoot]()[$extra].noLayoutFailure) {\n        return true;\n      }\n      if (node.h !== \"\") {\n        return Math.round(h - space.height) <= ERROR;\n      }\n      return true;\n    default:\n      return true;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/template.js\n\n\n\n\n\n\n\n\n\n\nconst TEMPLATE_NS_ID = NamespaceIds.template.id;\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\nconst MAX_ATTEMPTS_FOR_LRTB_LAYOUT = 2;\nconst MAX_EMPTY_PAGES = 3;\nconst DEFAULT_TAB_INDEX = 5000;\nconst HEADING_PATTERN = /^H(\\d+)$/;\nconst MIMES = new Set([\"image/gif\", \"image/jpeg\", \"image/jpg\", \"image/pjpeg\", \"image/png\", \"image/apng\", \"image/x-png\", \"image/bmp\", \"image/x-ms-bmp\", \"image/tiff\", \"image/tif\", \"application/octet-stream\"]);\nconst IMAGES_HEADERS = [[[0x42, 0x4d], \"image/bmp\"], [[0xff, 0xd8, 0xff], \"image/jpeg\"], [[0x49, 0x49, 0x2a, 0x00], \"image/tiff\"], [[0x4d, 0x4d, 0x00, 0x2a], \"image/tiff\"], [[0x47, 0x49, 0x46, 0x38, 0x39, 0x61], \"image/gif\"], [[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], \"image/png\"]];\nfunction getBorderDims(node) {\n  if (!node || !node.border) {\n    return {\n      w: 0,\n      h: 0\n    };\n  }\n  const borderExtra = node.border[$getExtra]();\n  if (!borderExtra) {\n    return {\n      w: 0,\n      h: 0\n    };\n  }\n  return {\n    w: borderExtra.widths[0] + borderExtra.widths[2] + borderExtra.insets[0] + borderExtra.insets[2],\n    h: borderExtra.widths[1] + borderExtra.widths[3] + borderExtra.insets[1] + borderExtra.insets[3]\n  };\n}\nfunction hasMargin(node) {\n  return node.margin && (node.margin.topInset || node.margin.rightInset || node.margin.bottomInset || node.margin.leftInset);\n}\nfunction _setValue(templateNode, value) {\n  if (!templateNode.value) {\n    const nodeValue = new Value({});\n    templateNode[$appendChild](nodeValue);\n    templateNode.value = nodeValue;\n  }\n  templateNode.value[$setValue](value);\n}\nfunction* getContainedChildren(node) {\n  for (const child of node[$getChildren]()) {\n    if (child instanceof SubformSet) {\n      yield* child[$getContainedChildren]();\n      continue;\n    }\n    yield child;\n  }\n}\nfunction isRequired(node) {\n  return node.validate?.nullTest === \"error\";\n}\nfunction setTabIndex(node) {\n  while (node) {\n    if (!node.traversal) {\n      node[$tabIndex] = node[$getParent]()[$tabIndex];\n      return;\n    }\n    if (node[$tabIndex]) {\n      return;\n    }\n    let next = null;\n    for (const child of node.traversal[$getChildren]()) {\n      if (child.operation === \"next\") {\n        next = child;\n        break;\n      }\n    }\n    if (!next || !next.ref) {\n      node[$tabIndex] = node[$getParent]()[$tabIndex];\n      return;\n    }\n    const root = node[$getTemplateRoot]();\n    node[$tabIndex] = ++root[$tabIndex];\n    const ref = root[$searchNode](next.ref, node);\n    if (!ref) {\n      return;\n    }\n    node = ref[0];\n  }\n}\nfunction applyAssist(obj, attributes) {\n  const assist = obj.assist;\n  if (assist) {\n    const assistTitle = assist[$toHTML]();\n    if (assistTitle) {\n      attributes.title = assistTitle;\n    }\n    const role = assist.role;\n    const match = role.match(HEADING_PATTERN);\n    if (match) {\n      const ariaRole = \"heading\";\n      const ariaLevel = match[1];\n      attributes.role = ariaRole;\n      attributes[\"aria-level\"] = ariaLevel;\n    }\n  }\n  if (obj.layout === \"table\") {\n    attributes.role = \"table\";\n  } else if (obj.layout === \"row\") {\n    attributes.role = \"row\";\n  } else {\n    const parent = obj[$getParent]();\n    if (parent.layout === \"row\") {\n      attributes.role = parent.assist?.role === \"TH\" ? \"columnheader\" : \"cell\";\n    }\n  }\n}\nfunction ariaLabel(obj) {\n  if (!obj.assist) {\n    return null;\n  }\n  const assist = obj.assist;\n  if (assist.speak && assist.speak[$content] !== \"\") {\n    return assist.speak[$content];\n  }\n  if (assist.toolTip) {\n    return assist.toolTip[$content];\n  }\n  return null;\n}\nfunction valueToHtml(value) {\n  return HTMLResult.success({\n    name: \"div\",\n    attributes: {\n      class: [\"xfaRich\"],\n      style: Object.create(null)\n    },\n    children: [{\n      name: \"span\",\n      attributes: {\n        style: Object.create(null)\n      },\n      value\n    }]\n  });\n}\nfunction setFirstUnsplittable(node) {\n  const root = node[$getTemplateRoot]();\n  if (root[$extra].firstUnsplittable === null) {\n    root[$extra].firstUnsplittable = node;\n    root[$extra].noLayoutFailure = true;\n  }\n}\nfunction unsetFirstUnsplittable(node) {\n  const root = node[$getTemplateRoot]();\n  if (root[$extra].firstUnsplittable === node) {\n    root[$extra].noLayoutFailure = false;\n  }\n}\nfunction handleBreak(node) {\n  if (node[$extra]) {\n    return false;\n  }\n  node[$extra] = Object.create(null);\n  if (node.targetType === \"auto\") {\n    return false;\n  }\n  const root = node[$getTemplateRoot]();\n  let target = null;\n  if (node.target) {\n    target = root[$searchNode](node.target, node[$getParent]());\n    if (!target) {\n      return false;\n    }\n    target = target[0];\n  }\n  const {\n    currentPageArea,\n    currentContentArea\n  } = root[$extra];\n  if (node.targetType === \"pageArea\") {\n    if (!(target instanceof PageArea)) {\n      target = null;\n    }\n    if (node.startNew) {\n      node[$extra].target = target || currentPageArea;\n      return true;\n    } else if (target && target !== currentPageArea) {\n      node[$extra].target = target;\n      return true;\n    }\n    return false;\n  }\n  if (!(target instanceof ContentArea)) {\n    target = null;\n  }\n  const pageArea = target && target[$getParent]();\n  let index;\n  let nextPageArea = pageArea;\n  if (node.startNew) {\n    if (target) {\n      const contentAreas = pageArea.contentArea.children;\n      const indexForCurrent = contentAreas.indexOf(currentContentArea);\n      const indexForTarget = contentAreas.indexOf(target);\n      if (indexForCurrent !== -1 && indexForCurrent < indexForTarget) {\n        nextPageArea = null;\n      }\n      index = indexForTarget - 1;\n    } else {\n      index = currentPageArea.contentArea.children.indexOf(currentContentArea);\n    }\n  } else if (target && target !== currentContentArea) {\n    const contentAreas = pageArea.contentArea.children;\n    index = contentAreas.indexOf(target) - 1;\n    nextPageArea = pageArea === currentPageArea ? null : pageArea;\n  } else {\n    return false;\n  }\n  node[$extra].target = nextPageArea;\n  node[$extra].index = index;\n  return true;\n}\nfunction handleOverflow(node, extraNode, space) {\n  const root = node[$getTemplateRoot]();\n  const saved = root[$extra].noLayoutFailure;\n  const savedMethod = extraNode[$getSubformParent];\n  extraNode[$getSubformParent] = () => node;\n  root[$extra].noLayoutFailure = true;\n  const res = extraNode[$toHTML](space);\n  node[$addHTML](res.html, res.bbox);\n  root[$extra].noLayoutFailure = saved;\n  extraNode[$getSubformParent] = savedMethod;\n}\nclass AppearanceFilter extends StringObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"appearanceFilter\");\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Arc extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"arc\", true);\n    this.circular = getInteger({\n      data: attributes.circular,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.hand = getStringOption(attributes.hand, [\"even\", \"left\", \"right\"]);\n    this.id = attributes.id || \"\";\n    this.startAngle = getFloat({\n      data: attributes.startAngle,\n      defaultValue: 0,\n      validate: x => true\n    });\n    this.sweepAngle = getFloat({\n      data: attributes.sweepAngle,\n      defaultValue: 360,\n      validate: x => true\n    });\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.edge = null;\n    this.fill = null;\n  }\n  [$toHTML]() {\n    const edge = this.edge || new Edge({});\n    const edgeStyle = edge[$toStyle]();\n    const style = Object.create(null);\n    if (this.fill?.presence === \"visible\") {\n      Object.assign(style, this.fill[$toStyle]());\n    } else {\n      style.fill = \"transparent\";\n    }\n    style.strokeWidth = measureToString(edge.presence === \"visible\" ? edge.thickness : 0);\n    style.stroke = edgeStyle.color;\n    let arc;\n    const attributes = {\n      xmlns: SVG_NS,\n      style: {\n        width: \"100%\",\n        height: \"100%\",\n        overflow: \"visible\"\n      }\n    };\n    if (this.sweepAngle === 360) {\n      arc = {\n        name: \"ellipse\",\n        attributes: {\n          xmlns: SVG_NS,\n          cx: \"50%\",\n          cy: \"50%\",\n          rx: \"50%\",\n          ry: \"50%\",\n          style\n        }\n      };\n    } else {\n      const startAngle = this.startAngle * Math.PI / 180;\n      const sweepAngle = this.sweepAngle * Math.PI / 180;\n      const largeArc = this.sweepAngle > 180 ? 1 : 0;\n      const [x1, y1, x2, y2] = [50 * (1 + Math.cos(startAngle)), 50 * (1 - Math.sin(startAngle)), 50 * (1 + Math.cos(startAngle + sweepAngle)), 50 * (1 - Math.sin(startAngle + sweepAngle))];\n      arc = {\n        name: \"path\",\n        attributes: {\n          xmlns: SVG_NS,\n          d: `M ${x1} ${y1} A 50 50 0 ${largeArc} 0 ${x2} ${y2}`,\n          vectorEffect: \"non-scaling-stroke\",\n          style\n        }\n      };\n      Object.assign(attributes, {\n        viewBox: \"0 0 100 100\",\n        preserveAspectRatio: \"none\"\n      });\n    }\n    const svg = {\n      name: \"svg\",\n      children: [arc],\n      attributes\n    };\n    const parent = this[$getParent]()[$getParent]();\n    if (hasMargin(parent)) {\n      return HTMLResult.success({\n        name: \"div\",\n        attributes: {\n          style: {\n            display: \"inline\",\n            width: \"100%\",\n            height: \"100%\"\n          }\n        },\n        children: [svg]\n      });\n    }\n    svg.attributes.style.position = \"absolute\";\n    return HTMLResult.success(svg);\n  }\n}\nclass Area extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"area\", true);\n    this.colSpan = getInteger({\n      data: attributes.colSpan,\n      defaultValue: 1,\n      validate: n => n >= 1 || n === -1\n    });\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.relevant = getRelevant(attributes.relevant);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.x = getMeasurement(attributes.x, \"0pt\");\n    this.y = getMeasurement(attributes.y, \"0pt\");\n    this.desc = null;\n    this.extras = null;\n    this.area = new XFAObjectArray();\n    this.draw = new XFAObjectArray();\n    this.exObject = new XFAObjectArray();\n    this.exclGroup = new XFAObjectArray();\n    this.field = new XFAObjectArray();\n    this.subform = new XFAObjectArray();\n    this.subformSet = new XFAObjectArray();\n  }\n  *[$getContainedChildren]() {\n    yield* getContainedChildren(this);\n  }\n  [$isTransparent]() {\n    return true;\n  }\n  [$isBindable]() {\n    return true;\n  }\n  [$addHTML](html, bbox) {\n    const [x, y, w, h] = bbox;\n    this[$extra].width = Math.max(this[$extra].width, x + w);\n    this[$extra].height = Math.max(this[$extra].height, y + h);\n    this[$extra].children.push(html);\n  }\n  [$getAvailableSpace]() {\n    return this[$extra].availableSpace;\n  }\n  [$toHTML](availableSpace) {\n    const style = toStyle(this, \"position\");\n    const attributes = {\n      style,\n      id: this[$uid],\n      class: [\"xfaArea\"]\n    };\n    if (isPrintOnly(this)) {\n      attributes.class.push(\"xfaPrintOnly\");\n    }\n    if (this.name) {\n      attributes.xfaName = this.name;\n    }\n    const children = [];\n    this[$extra] = {\n      children,\n      width: 0,\n      height: 0,\n      availableSpace\n    };\n    const result = this[$childrenToHTML]({\n      filter: new Set([\"area\", \"draw\", \"field\", \"exclGroup\", \"subform\", \"subformSet\"]),\n      include: true\n    });\n    if (!result.success) {\n      if (result.isBreak()) {\n        return result;\n      }\n      delete this[$extra];\n      return HTMLResult.FAILURE;\n    }\n    style.width = measureToString(this[$extra].width);\n    style.height = measureToString(this[$extra].height);\n    const html = {\n      name: \"div\",\n      attributes,\n      children\n    };\n    const bbox = [this.x, this.y, this[$extra].width, this[$extra].height];\n    delete this[$extra];\n    return HTMLResult.success(html, bbox);\n  }\n}\nclass Assist extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"assist\", true);\n    this.id = attributes.id || \"\";\n    this.role = attributes.role || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.speak = null;\n    this.toolTip = null;\n  }\n  [$toHTML]() {\n    return this.toolTip?.[$content] || null;\n  }\n}\nclass Barcode extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"barcode\", true);\n    this.charEncoding = getKeyword({\n      data: attributes.charEncoding ? attributes.charEncoding.toLowerCase() : \"\",\n      defaultValue: \"\",\n      validate: k => [\"utf-8\", \"big-five\", \"fontspecific\", \"gbk\", \"gb-18030\", \"gb-2312\", \"ksc-5601\", \"none\", \"shift-jis\", \"ucs-2\", \"utf-16\"].includes(k) || k.match(/iso-8859-\\d{2}/)\n    });\n    this.checksum = getStringOption(attributes.checksum, [\"none\", \"1mod10\", \"1mod10_1mod11\", \"2mod10\", \"auto\"]);\n    this.dataColumnCount = getInteger({\n      data: attributes.dataColumnCount,\n      defaultValue: -1,\n      validate: x => x >= 0\n    });\n    this.dataLength = getInteger({\n      data: attributes.dataLength,\n      defaultValue: -1,\n      validate: x => x >= 0\n    });\n    this.dataPrep = getStringOption(attributes.dataPrep, [\"none\", \"flateCompress\"]);\n    this.dataRowCount = getInteger({\n      data: attributes.dataRowCount,\n      defaultValue: -1,\n      validate: x => x >= 0\n    });\n    this.endChar = attributes.endChar || \"\";\n    this.errorCorrectionLevel = getInteger({\n      data: attributes.errorCorrectionLevel,\n      defaultValue: -1,\n      validate: x => x >= 0 && x <= 8\n    });\n    this.id = attributes.id || \"\";\n    this.moduleHeight = getMeasurement(attributes.moduleHeight, \"5mm\");\n    this.moduleWidth = getMeasurement(attributes.moduleWidth, \"0.25mm\");\n    this.printCheckDigit = getInteger({\n      data: attributes.printCheckDigit,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.rowColumnRatio = getRatio(attributes.rowColumnRatio);\n    this.startChar = attributes.startChar || \"\";\n    this.textLocation = getStringOption(attributes.textLocation, [\"below\", \"above\", \"aboveEmbedded\", \"belowEmbedded\", \"none\"]);\n    this.truncate = getInteger({\n      data: attributes.truncate,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.type = getStringOption(attributes.type ? attributes.type.toLowerCase() : \"\", [\"aztec\", \"codabar\", \"code2of5industrial\", \"code2of5interleaved\", \"code2of5matrix\", \"code2of5standard\", \"code3of9\", \"code3of9extended\", \"code11\", \"code49\", \"code93\", \"code128\", \"code128a\", \"code128b\", \"code128c\", \"code128sscc\", \"datamatrix\", \"ean8\", \"ean8add2\", \"ean8add5\", \"ean13\", \"ean13add2\", \"ean13add5\", \"ean13pwcd\", \"fim\", \"logmars\", \"maxicode\", \"msi\", \"pdf417\", \"pdf417macro\", \"plessey\", \"postauscust2\", \"postauscust3\", \"postausreplypaid\", \"postausstandard\", \"postukrm4scc\", \"postusdpbc\", \"postusimb\", \"postusstandard\", \"postus5zip\", \"qrcode\", \"rfid\", \"rss14\", \"rss14expanded\", \"rss14limited\", \"rss14stacked\", \"rss14stackedomni\", \"rss14truncated\", \"telepen\", \"ucc128\", \"ucc128random\", \"ucc128sscc\", \"upca\", \"upcaadd2\", \"upcaadd5\", \"upcapwcd\", \"upce\", \"upceadd2\", \"upceadd5\", \"upcean2\", \"upcean5\", \"upsmaxicode\"]);\n    this.upsMode = getStringOption(attributes.upsMode, [\"usCarrier\", \"internationalCarrier\", \"secureSymbol\", \"standardSymbol\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.wideNarrowRatio = getRatio(attributes.wideNarrowRatio);\n    this.encrypt = null;\n    this.extras = null;\n  }\n}\nclass Bind extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"bind\", true);\n    this.match = getStringOption(attributes.match, [\"once\", \"dataRef\", \"global\", \"none\"]);\n    this.ref = attributes.ref || \"\";\n    this.picture = null;\n  }\n}\nclass BindItems extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"bindItems\");\n    this.connection = attributes.connection || \"\";\n    this.labelRef = attributes.labelRef || \"\";\n    this.ref = attributes.ref || \"\";\n    this.valueRef = attributes.valueRef || \"\";\n  }\n}\nclass Bookend extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"bookend\");\n    this.id = attributes.id || \"\";\n    this.leader = attributes.leader || \"\";\n    this.trailer = attributes.trailer || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass BooleanElement extends Option01 {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"boolean\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$toHTML](availableSpace) {\n    return valueToHtml(this[$content] === 1 ? \"1\" : \"0\");\n  }\n}\nclass Border extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"border\", true);\n    this.break = getStringOption(attributes.break, [\"close\", \"open\"]);\n    this.hand = getStringOption(attributes.hand, [\"even\", \"left\", \"right\"]);\n    this.id = attributes.id || \"\";\n    this.presence = getStringOption(attributes.presence, [\"visible\", \"hidden\", \"inactive\", \"invisible\"]);\n    this.relevant = getRelevant(attributes.relevant);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.corner = new XFAObjectArray(4);\n    this.edge = new XFAObjectArray(4);\n    this.extras = null;\n    this.fill = null;\n    this.margin = null;\n  }\n  [$getExtra]() {\n    if (!this[$extra]) {\n      const edges = this.edge.children.slice();\n      if (edges.length < 4) {\n        const defaultEdge = edges.at(-1) || new Edge({});\n        for (let i = edges.length; i < 4; i++) {\n          edges.push(defaultEdge);\n        }\n      }\n      const widths = edges.map(edge => edge.thickness);\n      const insets = [0, 0, 0, 0];\n      if (this.margin) {\n        insets[0] = this.margin.topInset;\n        insets[1] = this.margin.rightInset;\n        insets[2] = this.margin.bottomInset;\n        insets[3] = this.margin.leftInset;\n      }\n      this[$extra] = {\n        widths,\n        insets,\n        edges\n      };\n    }\n    return this[$extra];\n  }\n  [$toStyle]() {\n    const {\n      edges\n    } = this[$getExtra]();\n    const edgeStyles = edges.map(node => {\n      const style = node[$toStyle]();\n      style.color ||= \"#000000\";\n      return style;\n    });\n    const style = Object.create(null);\n    if (this.margin) {\n      Object.assign(style, this.margin[$toStyle]());\n    }\n    if (this.fill?.presence === \"visible\") {\n      Object.assign(style, this.fill[$toStyle]());\n    }\n    if (this.corner.children.some(node => node.radius !== 0)) {\n      const cornerStyles = this.corner.children.map(node => node[$toStyle]());\n      if (cornerStyles.length === 2 || cornerStyles.length === 3) {\n        const last = cornerStyles.at(-1);\n        for (let i = cornerStyles.length; i < 4; i++) {\n          cornerStyles.push(last);\n        }\n      }\n      style.borderRadius = cornerStyles.map(s => s.radius).join(\" \");\n    }\n    switch (this.presence) {\n      case \"invisible\":\n      case \"hidden\":\n        style.borderStyle = \"\";\n        break;\n      case \"inactive\":\n        style.borderStyle = \"none\";\n        break;\n      default:\n        style.borderStyle = edgeStyles.map(s => s.style).join(\" \");\n        break;\n    }\n    style.borderWidth = edgeStyles.map(s => s.width).join(\" \");\n    style.borderColor = edgeStyles.map(s => s.color).join(\" \");\n    return style;\n  }\n}\nclass Break extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"break\", true);\n    this.after = getStringOption(attributes.after, [\"auto\", \"contentArea\", \"pageArea\", \"pageEven\", \"pageOdd\"]);\n    this.afterTarget = attributes.afterTarget || \"\";\n    this.before = getStringOption(attributes.before, [\"auto\", \"contentArea\", \"pageArea\", \"pageEven\", \"pageOdd\"]);\n    this.beforeTarget = attributes.beforeTarget || \"\";\n    this.bookendLeader = attributes.bookendLeader || \"\";\n    this.bookendTrailer = attributes.bookendTrailer || \"\";\n    this.id = attributes.id || \"\";\n    this.overflowLeader = attributes.overflowLeader || \"\";\n    this.overflowTarget = attributes.overflowTarget || \"\";\n    this.overflowTrailer = attributes.overflowTrailer || \"\";\n    this.startNew = getInteger({\n      data: attributes.startNew,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n  }\n}\nclass BreakAfter extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"breakAfter\", true);\n    this.id = attributes.id || \"\";\n    this.leader = attributes.leader || \"\";\n    this.startNew = getInteger({\n      data: attributes.startNew,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.target = attributes.target || \"\";\n    this.targetType = getStringOption(attributes.targetType, [\"auto\", \"contentArea\", \"pageArea\"]);\n    this.trailer = attributes.trailer || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.script = null;\n  }\n}\nclass BreakBefore extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"breakBefore\", true);\n    this.id = attributes.id || \"\";\n    this.leader = attributes.leader || \"\";\n    this.startNew = getInteger({\n      data: attributes.startNew,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.target = attributes.target || \"\";\n    this.targetType = getStringOption(attributes.targetType, [\"auto\", \"contentArea\", \"pageArea\"]);\n    this.trailer = attributes.trailer || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.script = null;\n  }\n  [$toHTML](availableSpace) {\n    this[$extra] = {};\n    return HTMLResult.FAILURE;\n  }\n}\nclass Button extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"button\", true);\n    this.highlight = getStringOption(attributes.highlight, [\"inverted\", \"none\", \"outline\", \"push\"]);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n  }\n  [$toHTML](availableSpace) {\n    const parent = this[$getParent]();\n    const grandpa = parent[$getParent]();\n    const htmlButton = {\n      name: \"button\",\n      attributes: {\n        id: this[$uid],\n        class: [\"xfaButton\"],\n        style: {}\n      },\n      children: []\n    };\n    for (const event of grandpa.event.children) {\n      if (event.activity !== \"click\" || !event.script) {\n        continue;\n      }\n      const jsURL = recoverJsURL(event.script[$content]);\n      if (!jsURL) {\n        continue;\n      }\n      const href = fixURL(jsURL.url);\n      if (!href) {\n        continue;\n      }\n      htmlButton.children.push({\n        name: \"a\",\n        attributes: {\n          id: \"link\" + this[$uid],\n          href,\n          newWindow: jsURL.newWindow,\n          class: [\"xfaLink\"],\n          style: {}\n        },\n        children: []\n      });\n    }\n    return HTMLResult.success(htmlButton);\n  }\n}\nclass Calculate extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"calculate\", true);\n    this.id = attributes.id || \"\";\n    this.override = getStringOption(attributes.override, [\"disabled\", \"error\", \"ignore\", \"warning\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n    this.message = null;\n    this.script = null;\n  }\n}\nclass Caption extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"caption\", true);\n    this.id = attributes.id || \"\";\n    this.placement = getStringOption(attributes.placement, [\"left\", \"bottom\", \"inline\", \"right\", \"top\"]);\n    this.presence = getStringOption(attributes.presence, [\"visible\", \"hidden\", \"inactive\", \"invisible\"]);\n    this.reserve = Math.ceil(getMeasurement(attributes.reserve));\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n    this.font = null;\n    this.margin = null;\n    this.para = null;\n    this.value = null;\n  }\n  [$setValue](value) {\n    _setValue(this, value);\n  }\n  [$getExtra](availableSpace) {\n    if (!this[$extra]) {\n      let {\n        width,\n        height\n      } = availableSpace;\n      switch (this.placement) {\n        case \"left\":\n        case \"right\":\n        case \"inline\":\n          width = this.reserve <= 0 ? width : this.reserve;\n          break;\n        case \"top\":\n        case \"bottom\":\n          height = this.reserve <= 0 ? height : this.reserve;\n          break;\n      }\n      this[$extra] = layoutNode(this, {\n        width,\n        height\n      });\n    }\n    return this[$extra];\n  }\n  [$toHTML](availableSpace) {\n    if (!this.value) {\n      return HTMLResult.EMPTY;\n    }\n    this[$pushPara]();\n    const value = this.value[$toHTML](availableSpace).html;\n    if (!value) {\n      this[$popPara]();\n      return HTMLResult.EMPTY;\n    }\n    const savedReserve = this.reserve;\n    if (this.reserve <= 0) {\n      const {\n        w,\n        h\n      } = this[$getExtra](availableSpace);\n      switch (this.placement) {\n        case \"left\":\n        case \"right\":\n        case \"inline\":\n          this.reserve = w;\n          break;\n        case \"top\":\n        case \"bottom\":\n          this.reserve = h;\n          break;\n      }\n    }\n    const children = [];\n    if (typeof value === \"string\") {\n      children.push({\n        name: \"#text\",\n        value\n      });\n    } else {\n      children.push(value);\n    }\n    const style = toStyle(this, \"font\", \"margin\", \"visibility\");\n    switch (this.placement) {\n      case \"left\":\n      case \"right\":\n        if (this.reserve > 0) {\n          style.width = measureToString(this.reserve);\n        }\n        break;\n      case \"top\":\n      case \"bottom\":\n        if (this.reserve > 0) {\n          style.height = measureToString(this.reserve);\n        }\n        break;\n    }\n    setPara(this, null, value);\n    this[$popPara]();\n    this.reserve = savedReserve;\n    return HTMLResult.success({\n      name: \"div\",\n      attributes: {\n        style,\n        class: [\"xfaCaption\"]\n      },\n      children\n    });\n  }\n}\nclass Certificate extends StringObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"certificate\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Certificates extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"certificates\", true);\n    this.credentialServerPolicy = getStringOption(attributes.credentialServerPolicy, [\"optional\", \"required\"]);\n    this.id = attributes.id || \"\";\n    this.url = attributes.url || \"\";\n    this.urlPolicy = attributes.urlPolicy || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.encryption = null;\n    this.issuers = null;\n    this.keyUsage = null;\n    this.oids = null;\n    this.signing = null;\n    this.subjectDNs = null;\n  }\n}\nclass CheckButton extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"checkButton\", true);\n    this.id = attributes.id || \"\";\n    this.mark = getStringOption(attributes.mark, [\"default\", \"check\", \"circle\", \"cross\", \"diamond\", \"square\", \"star\"]);\n    this.shape = getStringOption(attributes.shape, [\"square\", \"round\"]);\n    this.size = getMeasurement(attributes.size, \"10pt\");\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.border = null;\n    this.extras = null;\n    this.margin = null;\n  }\n  [$toHTML](availableSpace) {\n    const style = toStyle(\"margin\");\n    const size = measureToString(this.size);\n    style.width = style.height = size;\n    let type;\n    let className;\n    let groupId;\n    const field = this[$getParent]()[$getParent]();\n    const items = field.items.children.length && field.items.children[0][$toHTML]().html || [];\n    const exportedValue = {\n      on: (items[0] !== undefined ? items[0] : \"on\").toString(),\n      off: (items[1] !== undefined ? items[1] : \"off\").toString()\n    };\n    const value = field.value?.[$text]() || \"off\";\n    const checked = value === exportedValue.on || undefined;\n    const container = field[$getSubformParent]();\n    const fieldId = field[$uid];\n    let dataId;\n    if (container instanceof ExclGroup) {\n      groupId = container[$uid];\n      type = \"radio\";\n      className = \"xfaRadio\";\n      dataId = container[$data]?.[$uid] || container[$uid];\n    } else {\n      type = \"checkbox\";\n      className = \"xfaCheckbox\";\n      dataId = field[$data]?.[$uid] || field[$uid];\n    }\n    const input = {\n      name: \"input\",\n      attributes: {\n        class: [className],\n        style,\n        fieldId,\n        dataId,\n        type,\n        checked,\n        xfaOn: exportedValue.on,\n        xfaOff: exportedValue.off,\n        \"aria-label\": ariaLabel(field),\n        \"aria-required\": false\n      }\n    };\n    if (groupId) {\n      input.attributes.name = groupId;\n    }\n    if (isRequired(field)) {\n      input.attributes[\"aria-required\"] = true;\n      input.attributes.required = true;\n    }\n    return HTMLResult.success({\n      name: \"label\",\n      attributes: {\n        class: [\"xfaLabel\"]\n      },\n      children: [input]\n    });\n  }\n}\nclass ChoiceList extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"choiceList\", true);\n    this.commitOn = getStringOption(attributes.commitOn, [\"select\", \"exit\"]);\n    this.id = attributes.id || \"\";\n    this.open = getStringOption(attributes.open, [\"userControl\", \"always\", \"multiSelect\", \"onEntry\"]);\n    this.textEntry = getInteger({\n      data: attributes.textEntry,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.border = null;\n    this.extras = null;\n    this.margin = null;\n  }\n  [$toHTML](availableSpace) {\n    const style = toStyle(this, \"border\", \"margin\");\n    const ui = this[$getParent]();\n    const field = ui[$getParent]();\n    const fontSize = field.font?.size || 10;\n    const optionStyle = {\n      fontSize: `calc(${fontSize}px * var(--scale-factor))`\n    };\n    const children = [];\n    if (field.items.children.length > 0) {\n      const items = field.items;\n      let displayedIndex = 0;\n      let saveIndex = 0;\n      if (items.children.length === 2) {\n        displayedIndex = items.children[0].save;\n        saveIndex = 1 - displayedIndex;\n      }\n      const displayed = items.children[displayedIndex][$toHTML]().html;\n      const values = items.children[saveIndex][$toHTML]().html;\n      let selected = false;\n      const value = field.value?.[$text]() || \"\";\n      for (let i = 0, ii = displayed.length; i < ii; i++) {\n        const option = {\n          name: \"option\",\n          attributes: {\n            value: values[i] || displayed[i],\n            style: optionStyle\n          },\n          value: displayed[i]\n        };\n        if (values[i] === value) {\n          option.attributes.selected = selected = true;\n        }\n        children.push(option);\n      }\n      if (!selected) {\n        children.splice(0, 0, {\n          name: \"option\",\n          attributes: {\n            hidden: true,\n            selected: true\n          },\n          value: \" \"\n        });\n      }\n    }\n    const selectAttributes = {\n      class: [\"xfaSelect\"],\n      fieldId: field[$uid],\n      dataId: field[$data]?.[$uid] || field[$uid],\n      style,\n      \"aria-label\": ariaLabel(field),\n      \"aria-required\": false\n    };\n    if (isRequired(field)) {\n      selectAttributes[\"aria-required\"] = true;\n      selectAttributes.required = true;\n    }\n    if (this.open === \"multiSelect\") {\n      selectAttributes.multiple = true;\n    }\n    return HTMLResult.success({\n      name: \"label\",\n      attributes: {\n        class: [\"xfaLabel\"]\n      },\n      children: [{\n        name: \"select\",\n        children,\n        attributes: selectAttributes\n      }]\n    });\n  }\n}\nclass Color extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"color\", true);\n    this.cSpace = getStringOption(attributes.cSpace, [\"SRGB\"]);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.value = attributes.value ? getColor(attributes.value) : \"\";\n    this.extras = null;\n  }\n  [$hasSettableValue]() {\n    return false;\n  }\n  [$toStyle]() {\n    return this.value ? Util.makeHexColor(this.value.r, this.value.g, this.value.b) : null;\n  }\n}\nclass Comb extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"comb\");\n    this.id = attributes.id || \"\";\n    this.numberOfCells = getInteger({\n      data: attributes.numberOfCells,\n      defaultValue: 0,\n      validate: x => x >= 0\n    });\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Connect extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"connect\", true);\n    this.connection = attributes.connection || \"\";\n    this.id = attributes.id || \"\";\n    this.ref = attributes.ref || \"\";\n    this.usage = getStringOption(attributes.usage, [\"exportAndImport\", \"exportOnly\", \"importOnly\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.picture = null;\n  }\n}\nclass ContentArea extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"contentArea\", true);\n    this.h = getMeasurement(attributes.h);\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.relevant = getRelevant(attributes.relevant);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.w = getMeasurement(attributes.w);\n    this.x = getMeasurement(attributes.x, \"0pt\");\n    this.y = getMeasurement(attributes.y, \"0pt\");\n    this.desc = null;\n    this.extras = null;\n  }\n  [$toHTML](availableSpace) {\n    const left = measureToString(this.x);\n    const top = measureToString(this.y);\n    const style = {\n      left,\n      top,\n      width: measureToString(this.w),\n      height: measureToString(this.h)\n    };\n    const classNames = [\"xfaContentarea\"];\n    if (isPrintOnly(this)) {\n      classNames.push(\"xfaPrintOnly\");\n    }\n    return HTMLResult.success({\n      name: \"div\",\n      children: [],\n      attributes: {\n        style,\n        class: classNames,\n        id: this[$uid]\n      }\n    });\n  }\n}\nclass Corner extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"corner\", true);\n    this.id = attributes.id || \"\";\n    this.inverted = getInteger({\n      data: attributes.inverted,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.join = getStringOption(attributes.join, [\"square\", \"round\"]);\n    this.presence = getStringOption(attributes.presence, [\"visible\", \"hidden\", \"inactive\", \"invisible\"]);\n    this.radius = getMeasurement(attributes.radius);\n    this.stroke = getStringOption(attributes.stroke, [\"solid\", \"dashDot\", \"dashDotDot\", \"dashed\", \"dotted\", \"embossed\", \"etched\", \"lowered\", \"raised\"]);\n    this.thickness = getMeasurement(attributes.thickness, \"0.5pt\");\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.color = null;\n    this.extras = null;\n  }\n  [$toStyle]() {\n    const style = toStyle(this, \"visibility\");\n    style.radius = measureToString(this.join === \"square\" ? 0 : this.radius);\n    return style;\n  }\n}\nclass DateElement extends ContentObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"date\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$finalize]() {\n    const date = this[$content].trim();\n    this[$content] = date ? new Date(date) : null;\n  }\n  [$toHTML](availableSpace) {\n    return valueToHtml(this[$content] ? this[$content].toString() : \"\");\n  }\n}\nclass DateTime extends ContentObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"dateTime\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$finalize]() {\n    const date = this[$content].trim();\n    this[$content] = date ? new Date(date) : null;\n  }\n  [$toHTML](availableSpace) {\n    return valueToHtml(this[$content] ? this[$content].toString() : \"\");\n  }\n}\nclass DateTimeEdit extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"dateTimeEdit\", true);\n    this.hScrollPolicy = getStringOption(attributes.hScrollPolicy, [\"auto\", \"off\", \"on\"]);\n    this.id = attributes.id || \"\";\n    this.picker = getStringOption(attributes.picker, [\"host\", \"none\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.border = null;\n    this.comb = null;\n    this.extras = null;\n    this.margin = null;\n  }\n  [$toHTML](availableSpace) {\n    const style = toStyle(this, \"border\", \"font\", \"margin\");\n    const field = this[$getParent]()[$getParent]();\n    const html = {\n      name: \"input\",\n      attributes: {\n        type: \"text\",\n        fieldId: field[$uid],\n        dataId: field[$data]?.[$uid] || field[$uid],\n        class: [\"xfaTextfield\"],\n        style,\n        \"aria-label\": ariaLabel(field),\n        \"aria-required\": false\n      }\n    };\n    if (isRequired(field)) {\n      html.attributes[\"aria-required\"] = true;\n      html.attributes.required = true;\n    }\n    return HTMLResult.success({\n      name: \"label\",\n      attributes: {\n        class: [\"xfaLabel\"]\n      },\n      children: [html]\n    });\n  }\n}\nclass Decimal extends ContentObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"decimal\");\n    this.fracDigits = getInteger({\n      data: attributes.fracDigits,\n      defaultValue: 2,\n      validate: x => true\n    });\n    this.id = attributes.id || \"\";\n    this.leadDigits = getInteger({\n      data: attributes.leadDigits,\n      defaultValue: -1,\n      validate: x => true\n    });\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$finalize]() {\n    const number = parseFloat(this[$content].trim());\n    this[$content] = isNaN(number) ? null : number;\n  }\n  [$toHTML](availableSpace) {\n    return valueToHtml(this[$content] !== null ? this[$content].toString() : \"\");\n  }\n}\nclass DefaultUi extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"defaultUi\", true);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n  }\n}\nclass Desc extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"desc\", true);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.boolean = new XFAObjectArray();\n    this.date = new XFAObjectArray();\n    this.dateTime = new XFAObjectArray();\n    this.decimal = new XFAObjectArray();\n    this.exData = new XFAObjectArray();\n    this.float = new XFAObjectArray();\n    this.image = new XFAObjectArray();\n    this.integer = new XFAObjectArray();\n    this.text = new XFAObjectArray();\n    this.time = new XFAObjectArray();\n  }\n}\nclass DigestMethod extends OptionObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"digestMethod\", [\"\", \"SHA1\", \"SHA256\", \"SHA512\", \"RIPEMD160\"]);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass DigestMethods extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"digestMethods\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.digestMethod = new XFAObjectArray();\n  }\n}\nclass Draw extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"draw\", true);\n    this.anchorType = getStringOption(attributes.anchorType, [\"topLeft\", \"bottomCenter\", \"bottomLeft\", \"bottomRight\", \"middleCenter\", \"middleLeft\", \"middleRight\", \"topCenter\", \"topRight\"]);\n    this.colSpan = getInteger({\n      data: attributes.colSpan,\n      defaultValue: 1,\n      validate: n => n >= 1 || n === -1\n    });\n    this.h = attributes.h ? getMeasurement(attributes.h) : \"\";\n    this.hAlign = getStringOption(attributes.hAlign, [\"left\", \"center\", \"justify\", \"justifyAll\", \"radix\", \"right\"]);\n    this.id = attributes.id || \"\";\n    this.locale = attributes.locale || \"\";\n    this.maxH = getMeasurement(attributes.maxH, \"0pt\");\n    this.maxW = getMeasurement(attributes.maxW, \"0pt\");\n    this.minH = getMeasurement(attributes.minH, \"0pt\");\n    this.minW = getMeasurement(attributes.minW, \"0pt\");\n    this.name = attributes.name || \"\";\n    this.presence = getStringOption(attributes.presence, [\"visible\", \"hidden\", \"inactive\", \"invisible\"]);\n    this.relevant = getRelevant(attributes.relevant);\n    this.rotate = getInteger({\n      data: attributes.rotate,\n      defaultValue: 0,\n      validate: x => x % 90 === 0\n    });\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.w = attributes.w ? getMeasurement(attributes.w) : \"\";\n    this.x = getMeasurement(attributes.x, \"0pt\");\n    this.y = getMeasurement(attributes.y, \"0pt\");\n    this.assist = null;\n    this.border = null;\n    this.caption = null;\n    this.desc = null;\n    this.extras = null;\n    this.font = null;\n    this.keep = null;\n    this.margin = null;\n    this.para = null;\n    this.traversal = null;\n    this.ui = null;\n    this.value = null;\n    this.setProperty = new XFAObjectArray();\n  }\n  [$setValue](value) {\n    _setValue(this, value);\n  }\n  [$toHTML](availableSpace) {\n    setTabIndex(this);\n    if (this.presence === \"hidden\" || this.presence === \"inactive\") {\n      return HTMLResult.EMPTY;\n    }\n    fixDimensions(this);\n    this[$pushPara]();\n    const savedW = this.w;\n    const savedH = this.h;\n    const {\n      w,\n      h,\n      isBroken\n    } = layoutNode(this, availableSpace);\n    if (w && this.w === \"\") {\n      if (isBroken && this[$getSubformParent]()[$isThereMoreWidth]()) {\n        this[$popPara]();\n        return HTMLResult.FAILURE;\n      }\n      this.w = w;\n    }\n    if (h && this.h === \"\") {\n      this.h = h;\n    }\n    setFirstUnsplittable(this);\n    if (!checkDimensions(this, availableSpace)) {\n      this.w = savedW;\n      this.h = savedH;\n      this[$popPara]();\n      return HTMLResult.FAILURE;\n    }\n    unsetFirstUnsplittable(this);\n    const style = toStyle(this, \"font\", \"hAlign\", \"dimensions\", \"position\", \"presence\", \"rotate\", \"anchorType\", \"border\", \"margin\");\n    setMinMaxDimensions(this, style);\n    if (style.margin) {\n      style.padding = style.margin;\n      delete style.margin;\n    }\n    const classNames = [\"xfaDraw\"];\n    if (this.font) {\n      classNames.push(\"xfaFont\");\n    }\n    if (isPrintOnly(this)) {\n      classNames.push(\"xfaPrintOnly\");\n    }\n    const attributes = {\n      style,\n      id: this[$uid],\n      class: classNames\n    };\n    if (this.name) {\n      attributes.xfaName = this.name;\n    }\n    const html = {\n      name: \"div\",\n      attributes,\n      children: []\n    };\n    applyAssist(this, attributes);\n    const bbox = computeBbox(this, html, availableSpace);\n    const value = this.value ? this.value[$toHTML](availableSpace).html : null;\n    if (value === null) {\n      this.w = savedW;\n      this.h = savedH;\n      this[$popPara]();\n      return HTMLResult.success(createWrapper(this, html), bbox);\n    }\n    html.children.push(value);\n    setPara(this, style, value);\n    this.w = savedW;\n    this.h = savedH;\n    this[$popPara]();\n    return HTMLResult.success(createWrapper(this, html), bbox);\n  }\n}\nclass Edge extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"edge\", true);\n    this.cap = getStringOption(attributes.cap, [\"square\", \"butt\", \"round\"]);\n    this.id = attributes.id || \"\";\n    this.presence = getStringOption(attributes.presence, [\"visible\", \"hidden\", \"inactive\", \"invisible\"]);\n    this.stroke = getStringOption(attributes.stroke, [\"solid\", \"dashDot\", \"dashDotDot\", \"dashed\", \"dotted\", \"embossed\", \"etched\", \"lowered\", \"raised\"]);\n    this.thickness = getMeasurement(attributes.thickness, \"0.5pt\");\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.color = null;\n    this.extras = null;\n  }\n  [$toStyle]() {\n    const style = toStyle(this, \"visibility\");\n    Object.assign(style, {\n      linecap: this.cap,\n      width: measureToString(this.thickness),\n      color: this.color ? this.color[$toStyle]() : \"#000000\",\n      style: \"\"\n    });\n    if (this.presence !== \"visible\") {\n      style.style = \"none\";\n    } else {\n      switch (this.stroke) {\n        case \"solid\":\n          style.style = \"solid\";\n          break;\n        case \"dashDot\":\n          style.style = \"dashed\";\n          break;\n        case \"dashDotDot\":\n          style.style = \"dashed\";\n          break;\n        case \"dashed\":\n          style.style = \"dashed\";\n          break;\n        case \"dotted\":\n          style.style = \"dotted\";\n          break;\n        case \"embossed\":\n          style.style = \"ridge\";\n          break;\n        case \"etched\":\n          style.style = \"groove\";\n          break;\n        case \"lowered\":\n          style.style = \"inset\";\n          break;\n        case \"raised\":\n          style.style = \"outset\";\n          break;\n      }\n    }\n    return style;\n  }\n}\nclass Encoding extends OptionObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"encoding\", [\"adbe.x509.rsa_sha1\", \"adbe.pkcs7.detached\", \"adbe.pkcs7.sha1\"]);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Encodings extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"encodings\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.encoding = new XFAObjectArray();\n  }\n}\nclass Encrypt extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"encrypt\", true);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.certificate = null;\n  }\n}\nclass EncryptData extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"encryptData\", true);\n    this.id = attributes.id || \"\";\n    this.operation = getStringOption(attributes.operation, [\"encrypt\", \"decrypt\"]);\n    this.target = attributes.target || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.filter = null;\n    this.manifest = null;\n  }\n}\nclass Encryption extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"encryption\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.certificate = new XFAObjectArray();\n  }\n}\nclass EncryptionMethod extends OptionObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"encryptionMethod\", [\"\", \"AES256-CBC\", \"TRIPLEDES-CBC\", \"AES128-CBC\", \"AES192-CBC\"]);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass EncryptionMethods extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"encryptionMethods\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.encryptionMethod = new XFAObjectArray();\n  }\n}\nclass Event extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"event\", true);\n    this.activity = getStringOption(attributes.activity, [\"click\", \"change\", \"docClose\", \"docReady\", \"enter\", \"exit\", \"full\", \"indexChange\", \"initialize\", \"mouseDown\", \"mouseEnter\", \"mouseExit\", \"mouseUp\", \"postExecute\", \"postOpen\", \"postPrint\", \"postSave\", \"postSign\", \"postSubmit\", \"preExecute\", \"preOpen\", \"prePrint\", \"preSave\", \"preSign\", \"preSubmit\", \"ready\", \"validationState\"]);\n    this.id = attributes.id || \"\";\n    this.listen = getStringOption(attributes.listen, [\"refOnly\", \"refAndDescendents\"]);\n    this.name = attributes.name || \"\";\n    this.ref = attributes.ref || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n    this.encryptData = null;\n    this.execute = null;\n    this.script = null;\n    this.signData = null;\n    this.submit = null;\n  }\n}\nclass ExData extends ContentObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"exData\");\n    this.contentType = attributes.contentType || \"\";\n    this.href = attributes.href || \"\";\n    this.id = attributes.id || \"\";\n    this.maxLength = getInteger({\n      data: attributes.maxLength,\n      defaultValue: -1,\n      validate: x => x >= -1\n    });\n    this.name = attributes.name || \"\";\n    this.rid = attributes.rid || \"\";\n    this.transferEncoding = getStringOption(attributes.transferEncoding, [\"none\", \"base64\", \"package\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$isCDATAXml]() {\n    return this.contentType === \"text/html\";\n  }\n  [$onChild](child) {\n    if (this.contentType === \"text/html\" && child[$namespaceId] === NamespaceIds.xhtml.id) {\n      this[$content] = child;\n      return true;\n    }\n    if (this.contentType === \"text/xml\") {\n      this[$content] = child;\n      return true;\n    }\n    return false;\n  }\n  [$toHTML](availableSpace) {\n    if (this.contentType !== \"text/html\" || !this[$content]) {\n      return HTMLResult.EMPTY;\n    }\n    return this[$content][$toHTML](availableSpace);\n  }\n}\nclass ExObject extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"exObject\", true);\n    this.archive = attributes.archive || \"\";\n    this.classId = attributes.classId || \"\";\n    this.codeBase = attributes.codeBase || \"\";\n    this.codeType = attributes.codeType || \"\";\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n    this.boolean = new XFAObjectArray();\n    this.date = new XFAObjectArray();\n    this.dateTime = new XFAObjectArray();\n    this.decimal = new XFAObjectArray();\n    this.exData = new XFAObjectArray();\n    this.exObject = new XFAObjectArray();\n    this.float = new XFAObjectArray();\n    this.image = new XFAObjectArray();\n    this.integer = new XFAObjectArray();\n    this.text = new XFAObjectArray();\n    this.time = new XFAObjectArray();\n  }\n}\nclass ExclGroup extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"exclGroup\", true);\n    this.access = getStringOption(attributes.access, [\"open\", \"nonInteractive\", \"protected\", \"readOnly\"]);\n    this.accessKey = attributes.accessKey || \"\";\n    this.anchorType = getStringOption(attributes.anchorType, [\"topLeft\", \"bottomCenter\", \"bottomLeft\", \"bottomRight\", \"middleCenter\", \"middleLeft\", \"middleRight\", \"topCenter\", \"topRight\"]);\n    this.colSpan = getInteger({\n      data: attributes.colSpan,\n      defaultValue: 1,\n      validate: n => n >= 1 || n === -1\n    });\n    this.h = attributes.h ? getMeasurement(attributes.h) : \"\";\n    this.hAlign = getStringOption(attributes.hAlign, [\"left\", \"center\", \"justify\", \"justifyAll\", \"radix\", \"right\"]);\n    this.id = attributes.id || \"\";\n    this.layout = getStringOption(attributes.layout, [\"position\", \"lr-tb\", \"rl-row\", \"rl-tb\", \"row\", \"table\", \"tb\"]);\n    this.maxH = getMeasurement(attributes.maxH, \"0pt\");\n    this.maxW = getMeasurement(attributes.maxW, \"0pt\");\n    this.minH = getMeasurement(attributes.minH, \"0pt\");\n    this.minW = getMeasurement(attributes.minW, \"0pt\");\n    this.name = attributes.name || \"\";\n    this.presence = getStringOption(attributes.presence, [\"visible\", \"hidden\", \"inactive\", \"invisible\"]);\n    this.relevant = getRelevant(attributes.relevant);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.w = attributes.w ? getMeasurement(attributes.w) : \"\";\n    this.x = getMeasurement(attributes.x, \"0pt\");\n    this.y = getMeasurement(attributes.y, \"0pt\");\n    this.assist = null;\n    this.bind = null;\n    this.border = null;\n    this.calculate = null;\n    this.caption = null;\n    this.desc = null;\n    this.extras = null;\n    this.margin = null;\n    this.para = null;\n    this.traversal = null;\n    this.validate = null;\n    this.connect = new XFAObjectArray();\n    this.event = new XFAObjectArray();\n    this.field = new XFAObjectArray();\n    this.setProperty = new XFAObjectArray();\n  }\n  [$isBindable]() {\n    return true;\n  }\n  [$hasSettableValue]() {\n    return true;\n  }\n  [$setValue](value) {\n    for (const field of this.field.children) {\n      if (!field.value) {\n        const nodeValue = new Value({});\n        field[$appendChild](nodeValue);\n        field.value = nodeValue;\n      }\n      field.value[$setValue](value);\n    }\n  }\n  [$isThereMoreWidth]() {\n    return this.layout.endsWith(\"-tb\") && this[$extra].attempt === 0 && this[$extra].numberInLine > 0 || this[$getParent]()[$isThereMoreWidth]();\n  }\n  [$isSplittable]() {\n    const parent = this[$getSubformParent]();\n    if (!parent[$isSplittable]()) {\n      return false;\n    }\n    if (this[$extra]._isSplittable !== undefined) {\n      return this[$extra]._isSplittable;\n    }\n    if (this.layout === \"position\" || this.layout.includes(\"row\")) {\n      this[$extra]._isSplittable = false;\n      return false;\n    }\n    if (parent.layout?.endsWith(\"-tb\") && parent[$extra].numberInLine !== 0) {\n      return false;\n    }\n    this[$extra]._isSplittable = true;\n    return true;\n  }\n  [$flushHTML]() {\n    return flushHTML(this);\n  }\n  [$addHTML](html, bbox) {\n    addHTML(this, html, bbox);\n  }\n  [$getAvailableSpace]() {\n    return getAvailableSpace(this);\n  }\n  [$toHTML](availableSpace) {\n    setTabIndex(this);\n    if (this.presence === \"hidden\" || this.presence === \"inactive\" || this.h === 0 || this.w === 0) {\n      return HTMLResult.EMPTY;\n    }\n    fixDimensions(this);\n    const children = [];\n    const attributes = {\n      id: this[$uid],\n      class: []\n    };\n    setAccess(this, attributes.class);\n    if (!this[$extra]) {\n      this[$extra] = Object.create(null);\n    }\n    Object.assign(this[$extra], {\n      children,\n      attributes,\n      attempt: 0,\n      line: null,\n      numberInLine: 0,\n      availableSpace: {\n        width: Math.min(this.w || Infinity, availableSpace.width),\n        height: Math.min(this.h || Infinity, availableSpace.height)\n      },\n      width: 0,\n      height: 0,\n      prevHeight: 0,\n      currentWidth: 0\n    });\n    const isSplittable = this[$isSplittable]();\n    if (!isSplittable) {\n      setFirstUnsplittable(this);\n    }\n    if (!checkDimensions(this, availableSpace)) {\n      return HTMLResult.FAILURE;\n    }\n    const filter = new Set([\"field\"]);\n    if (this.layout.includes(\"row\")) {\n      const columnWidths = this[$getSubformParent]().columnWidths;\n      if (Array.isArray(columnWidths) && columnWidths.length > 0) {\n        this[$extra].columnWidths = columnWidths;\n        this[$extra].currentColumn = 0;\n      }\n    }\n    const style = toStyle(this, \"anchorType\", \"dimensions\", \"position\", \"presence\", \"border\", \"margin\", \"hAlign\");\n    const classNames = [\"xfaExclgroup\"];\n    const cl = layoutClass(this);\n    if (cl) {\n      classNames.push(cl);\n    }\n    if (isPrintOnly(this)) {\n      classNames.push(\"xfaPrintOnly\");\n    }\n    attributes.style = style;\n    attributes.class = classNames;\n    if (this.name) {\n      attributes.xfaName = this.name;\n    }\n    this[$pushPara]();\n    const isLrTb = this.layout === \"lr-tb\" || this.layout === \"rl-tb\";\n    const maxRun = isLrTb ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT : 1;\n    for (; this[$extra].attempt < maxRun; this[$extra].attempt++) {\n      if (isLrTb && this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT - 1) {\n        this[$extra].numberInLine = 0;\n      }\n      const result = this[$childrenToHTML]({\n        filter,\n        include: true\n      });\n      if (result.success) {\n        break;\n      }\n      if (result.isBreak()) {\n        this[$popPara]();\n        return result;\n      }\n      if (isLrTb && this[$extra].attempt === 0 && this[$extra].numberInLine === 0 && !this[$getTemplateRoot]()[$extra].noLayoutFailure) {\n        this[$extra].attempt = maxRun;\n        break;\n      }\n    }\n    this[$popPara]();\n    if (!isSplittable) {\n      unsetFirstUnsplittable(this);\n    }\n    if (this[$extra].attempt === maxRun) {\n      if (!isSplittable) {\n        delete this[$extra];\n      }\n      return HTMLResult.FAILURE;\n    }\n    let marginH = 0;\n    let marginV = 0;\n    if (this.margin) {\n      marginH = this.margin.leftInset + this.margin.rightInset;\n      marginV = this.margin.topInset + this.margin.bottomInset;\n    }\n    const width = Math.max(this[$extra].width + marginH, this.w || 0);\n    const height = Math.max(this[$extra].height + marginV, this.h || 0);\n    const bbox = [this.x, this.y, width, height];\n    if (this.w === \"\") {\n      style.width = measureToString(width);\n    }\n    if (this.h === \"\") {\n      style.height = measureToString(height);\n    }\n    const html = {\n      name: \"div\",\n      attributes,\n      children\n    };\n    applyAssist(this, attributes);\n    delete this[$extra];\n    return HTMLResult.success(createWrapper(this, html), bbox);\n  }\n}\nclass Execute extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"execute\");\n    this.connection = attributes.connection || \"\";\n    this.executeType = getStringOption(attributes.executeType, [\"import\", \"remerge\"]);\n    this.id = attributes.id || \"\";\n    this.runAt = getStringOption(attributes.runAt, [\"client\", \"both\", \"server\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Extras extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"extras\", true);\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.boolean = new XFAObjectArray();\n    this.date = new XFAObjectArray();\n    this.dateTime = new XFAObjectArray();\n    this.decimal = new XFAObjectArray();\n    this.exData = new XFAObjectArray();\n    this.extras = new XFAObjectArray();\n    this.float = new XFAObjectArray();\n    this.image = new XFAObjectArray();\n    this.integer = new XFAObjectArray();\n    this.text = new XFAObjectArray();\n    this.time = new XFAObjectArray();\n  }\n}\nclass Field extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"field\", true);\n    this.access = getStringOption(attributes.access, [\"open\", \"nonInteractive\", \"protected\", \"readOnly\"]);\n    this.accessKey = attributes.accessKey || \"\";\n    this.anchorType = getStringOption(attributes.anchorType, [\"topLeft\", \"bottomCenter\", \"bottomLeft\", \"bottomRight\", \"middleCenter\", \"middleLeft\", \"middleRight\", \"topCenter\", \"topRight\"]);\n    this.colSpan = getInteger({\n      data: attributes.colSpan,\n      defaultValue: 1,\n      validate: n => n >= 1 || n === -1\n    });\n    this.h = attributes.h ? getMeasurement(attributes.h) : \"\";\n    this.hAlign = getStringOption(attributes.hAlign, [\"left\", \"center\", \"justify\", \"justifyAll\", \"radix\", \"right\"]);\n    this.id = attributes.id || \"\";\n    this.locale = attributes.locale || \"\";\n    this.maxH = getMeasurement(attributes.maxH, \"0pt\");\n    this.maxW = getMeasurement(attributes.maxW, \"0pt\");\n    this.minH = getMeasurement(attributes.minH, \"0pt\");\n    this.minW = getMeasurement(attributes.minW, \"0pt\");\n    this.name = attributes.name || \"\";\n    this.presence = getStringOption(attributes.presence, [\"visible\", \"hidden\", \"inactive\", \"invisible\"]);\n    this.relevant = getRelevant(attributes.relevant);\n    this.rotate = getInteger({\n      data: attributes.rotate,\n      defaultValue: 0,\n      validate: x => x % 90 === 0\n    });\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.w = attributes.w ? getMeasurement(attributes.w) : \"\";\n    this.x = getMeasurement(attributes.x, \"0pt\");\n    this.y = getMeasurement(attributes.y, \"0pt\");\n    this.assist = null;\n    this.bind = null;\n    this.border = null;\n    this.calculate = null;\n    this.caption = null;\n    this.desc = null;\n    this.extras = null;\n    this.font = null;\n    this.format = null;\n    this.items = new XFAObjectArray(2);\n    this.keep = null;\n    this.margin = null;\n    this.para = null;\n    this.traversal = null;\n    this.ui = null;\n    this.validate = null;\n    this.value = null;\n    this.bindItems = new XFAObjectArray();\n    this.connect = new XFAObjectArray();\n    this.event = new XFAObjectArray();\n    this.setProperty = new XFAObjectArray();\n  }\n  [$isBindable]() {\n    return true;\n  }\n  [$setValue](value) {\n    _setValue(this, value);\n  }\n  [$toHTML](availableSpace) {\n    setTabIndex(this);\n    if (!this.ui) {\n      this.ui = new Ui({});\n      this.ui[$globalData] = this[$globalData];\n      this[$appendChild](this.ui);\n      let node;\n      switch (this.items.children.length) {\n        case 0:\n          node = new TextEdit({});\n          this.ui.textEdit = node;\n          break;\n        case 1:\n          node = new CheckButton({});\n          this.ui.checkButton = node;\n          break;\n        case 2:\n          node = new ChoiceList({});\n          this.ui.choiceList = node;\n          break;\n      }\n      this.ui[$appendChild](node);\n    }\n    if (!this.ui || this.presence === \"hidden\" || this.presence === \"inactive\" || this.h === 0 || this.w === 0) {\n      return HTMLResult.EMPTY;\n    }\n    if (this.caption) {\n      delete this.caption[$extra];\n    }\n    this[$pushPara]();\n    const caption = this.caption ? this.caption[$toHTML](availableSpace).html : null;\n    const savedW = this.w;\n    const savedH = this.h;\n    let marginH = 0;\n    let marginV = 0;\n    if (this.margin) {\n      marginH = this.margin.leftInset + this.margin.rightInset;\n      marginV = this.margin.topInset + this.margin.bottomInset;\n    }\n    let borderDims = null;\n    if (this.w === \"\" || this.h === \"\") {\n      let width = null;\n      let height = null;\n      let uiW = 0;\n      let uiH = 0;\n      if (this.ui.checkButton) {\n        uiW = uiH = this.ui.checkButton.size;\n      } else {\n        const {\n          w,\n          h\n        } = layoutNode(this, availableSpace);\n        if (w !== null) {\n          uiW = w;\n          uiH = h;\n        } else {\n          uiH = fonts_getMetrics(this.font, true).lineNoGap;\n        }\n      }\n      borderDims = getBorderDims(this.ui[$getExtra]());\n      uiW += borderDims.w;\n      uiH += borderDims.h;\n      if (this.caption) {\n        const {\n          w,\n          h,\n          isBroken\n        } = this.caption[$getExtra](availableSpace);\n        if (isBroken && this[$getSubformParent]()[$isThereMoreWidth]()) {\n          this[$popPara]();\n          return HTMLResult.FAILURE;\n        }\n        width = w;\n        height = h;\n        switch (this.caption.placement) {\n          case \"left\":\n          case \"right\":\n          case \"inline\":\n            width += uiW;\n            break;\n          case \"top\":\n          case \"bottom\":\n            height += uiH;\n            break;\n        }\n      } else {\n        width = uiW;\n        height = uiH;\n      }\n      if (width && this.w === \"\") {\n        width += marginH;\n        this.w = Math.min(this.maxW <= 0 ? Infinity : this.maxW, this.minW + 1 < width ? width : this.minW);\n      }\n      if (height && this.h === \"\") {\n        height += marginV;\n        this.h = Math.min(this.maxH <= 0 ? Infinity : this.maxH, this.minH + 1 < height ? height : this.minH);\n      }\n    }\n    this[$popPara]();\n    fixDimensions(this);\n    setFirstUnsplittable(this);\n    if (!checkDimensions(this, availableSpace)) {\n      this.w = savedW;\n      this.h = savedH;\n      this[$popPara]();\n      return HTMLResult.FAILURE;\n    }\n    unsetFirstUnsplittable(this);\n    const style = toStyle(this, \"font\", \"dimensions\", \"position\", \"rotate\", \"anchorType\", \"presence\", \"margin\", \"hAlign\");\n    setMinMaxDimensions(this, style);\n    const classNames = [\"xfaField\"];\n    if (this.font) {\n      classNames.push(\"xfaFont\");\n    }\n    if (isPrintOnly(this)) {\n      classNames.push(\"xfaPrintOnly\");\n    }\n    const attributes = {\n      style,\n      id: this[$uid],\n      class: classNames\n    };\n    if (style.margin) {\n      style.padding = style.margin;\n      delete style.margin;\n    }\n    setAccess(this, classNames);\n    if (this.name) {\n      attributes.xfaName = this.name;\n    }\n    const children = [];\n    const html = {\n      name: \"div\",\n      attributes,\n      children\n    };\n    applyAssist(this, attributes);\n    const borderStyle = this.border ? this.border[$toStyle]() : null;\n    const bbox = computeBbox(this, html, availableSpace);\n    const ui = this.ui[$toHTML]().html;\n    if (!ui) {\n      Object.assign(style, borderStyle);\n      return HTMLResult.success(createWrapper(this, html), bbox);\n    }\n    if (this[$tabIndex]) {\n      if (ui.children?.[0]) {\n        ui.children[0].attributes.tabindex = this[$tabIndex];\n      } else {\n        ui.attributes.tabindex = this[$tabIndex];\n      }\n    }\n    if (!ui.attributes.style) {\n      ui.attributes.style = Object.create(null);\n    }\n    let aElement = null;\n    if (this.ui.button) {\n      if (ui.children.length === 1) {\n        [aElement] = ui.children.splice(0, 1);\n      }\n      Object.assign(ui.attributes.style, borderStyle);\n    } else {\n      Object.assign(style, borderStyle);\n    }\n    children.push(ui);\n    if (this.value) {\n      if (this.ui.imageEdit) {\n        ui.children.push(this.value[$toHTML]().html);\n      } else if (!this.ui.button) {\n        let value = \"\";\n        if (this.value.exData) {\n          value = this.value.exData[$text]();\n        } else if (this.value.text) {\n          value = this.value.text[$getExtra]();\n        } else {\n          const htmlValue = this.value[$toHTML]().html;\n          if (htmlValue !== null) {\n            value = htmlValue.children[0].value;\n          }\n        }\n        if (this.ui.textEdit && this.value.text?.maxChars) {\n          ui.children[0].attributes.maxLength = this.value.text.maxChars;\n        }\n        if (value) {\n          if (this.ui.numericEdit) {\n            value = parseFloat(value);\n            value = isNaN(value) ? \"\" : value.toString();\n          }\n          if (ui.children[0].name === \"textarea\") {\n            ui.children[0].attributes.textContent = value;\n          } else {\n            ui.children[0].attributes.value = value;\n          }\n        }\n      }\n    }\n    if (!this.ui.imageEdit && ui.children?.[0] && this.h) {\n      borderDims = borderDims || getBorderDims(this.ui[$getExtra]());\n      let captionHeight = 0;\n      if (this.caption && [\"top\", \"bottom\"].includes(this.caption.placement)) {\n        captionHeight = this.caption.reserve;\n        if (captionHeight <= 0) {\n          captionHeight = this.caption[$getExtra](availableSpace).h;\n        }\n        const inputHeight = this.h - captionHeight - marginV - borderDims.h;\n        ui.children[0].attributes.style.height = measureToString(inputHeight);\n      } else {\n        ui.children[0].attributes.style.height = \"100%\";\n      }\n    }\n    if (aElement) {\n      ui.children.push(aElement);\n    }\n    if (!caption) {\n      if (ui.attributes.class) {\n        ui.attributes.class.push(\"xfaLeft\");\n      }\n      this.w = savedW;\n      this.h = savedH;\n      return HTMLResult.success(createWrapper(this, html), bbox);\n    }\n    if (this.ui.button) {\n      if (style.padding) {\n        delete style.padding;\n      }\n      if (caption.name === \"div\") {\n        caption.name = \"span\";\n      }\n      ui.children.push(caption);\n      return HTMLResult.success(html, bbox);\n    } else if (this.ui.checkButton) {\n      caption.attributes.class[0] = \"xfaCaptionForCheckButton\";\n    }\n    if (!ui.attributes.class) {\n      ui.attributes.class = [];\n    }\n    ui.children.splice(0, 0, caption);\n    switch (this.caption.placement) {\n      case \"left\":\n        ui.attributes.class.push(\"xfaLeft\");\n        break;\n      case \"right\":\n        ui.attributes.class.push(\"xfaRight\");\n        break;\n      case \"top\":\n        ui.attributes.class.push(\"xfaTop\");\n        break;\n      case \"bottom\":\n        ui.attributes.class.push(\"xfaBottom\");\n        break;\n      case \"inline\":\n        ui.attributes.class.push(\"xfaLeft\");\n        break;\n    }\n    this.w = savedW;\n    this.h = savedH;\n    return HTMLResult.success(createWrapper(this, html), bbox);\n  }\n}\nclass Fill extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"fill\", true);\n    this.id = attributes.id || \"\";\n    this.presence = getStringOption(attributes.presence, [\"visible\", \"hidden\", \"inactive\", \"invisible\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.color = null;\n    this.extras = null;\n    this.linear = null;\n    this.pattern = null;\n    this.radial = null;\n    this.solid = null;\n    this.stipple = null;\n  }\n  [$toStyle]() {\n    const parent = this[$getParent]();\n    const grandpa = parent[$getParent]();\n    const ggrandpa = grandpa[$getParent]();\n    const style = Object.create(null);\n    let propName = \"color\";\n    let altPropName = propName;\n    if (parent instanceof Border) {\n      propName = \"background-color\";\n      altPropName = \"background\";\n      if (ggrandpa instanceof Ui) {\n        style.backgroundColor = \"white\";\n      }\n    }\n    if (parent instanceof Rectangle || parent instanceof Arc) {\n      propName = altPropName = \"fill\";\n      style.fill = \"white\";\n    }\n    for (const name of Object.getOwnPropertyNames(this)) {\n      if (name === \"extras\" || name === \"color\") {\n        continue;\n      }\n      const obj = this[name];\n      if (!(obj instanceof XFAObject)) {\n        continue;\n      }\n      const color = obj[$toStyle](this.color);\n      if (color) {\n        style[color.startsWith(\"#\") ? propName : altPropName] = color;\n      }\n      return style;\n    }\n    if (this.color?.value) {\n      const color = this.color[$toStyle]();\n      style[color.startsWith(\"#\") ? propName : altPropName] = color;\n    }\n    return style;\n  }\n}\nclass Filter extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"filter\", true);\n    this.addRevocationInfo = getStringOption(attributes.addRevocationInfo, [\"\", \"required\", \"optional\", \"none\"]);\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.version = getInteger({\n      data: this.version,\n      defaultValue: 5,\n      validate: x => x >= 1 && x <= 5\n    });\n    this.appearanceFilter = null;\n    this.certificates = null;\n    this.digestMethods = null;\n    this.encodings = null;\n    this.encryptionMethods = null;\n    this.handler = null;\n    this.lockDocument = null;\n    this.mdp = null;\n    this.reasons = null;\n    this.timeStamp = null;\n  }\n}\nclass Float extends ContentObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"float\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$finalize]() {\n    const number = parseFloat(this[$content].trim());\n    this[$content] = isNaN(number) ? null : number;\n  }\n  [$toHTML](availableSpace) {\n    return valueToHtml(this[$content] !== null ? this[$content].toString() : \"\");\n  }\n}\nclass template_Font extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"font\", true);\n    this.baselineShift = getMeasurement(attributes.baselineShift);\n    this.fontHorizontalScale = getFloat({\n      data: attributes.fontHorizontalScale,\n      defaultValue: 100,\n      validate: x => x >= 0\n    });\n    this.fontVerticalScale = getFloat({\n      data: attributes.fontVerticalScale,\n      defaultValue: 100,\n      validate: x => x >= 0\n    });\n    this.id = attributes.id || \"\";\n    this.kerningMode = getStringOption(attributes.kerningMode, [\"none\", \"pair\"]);\n    this.letterSpacing = getMeasurement(attributes.letterSpacing, \"0\");\n    this.lineThrough = getInteger({\n      data: attributes.lineThrough,\n      defaultValue: 0,\n      validate: x => x === 1 || x === 2\n    });\n    this.lineThroughPeriod = getStringOption(attributes.lineThroughPeriod, [\"all\", \"word\"]);\n    this.overline = getInteger({\n      data: attributes.overline,\n      defaultValue: 0,\n      validate: x => x === 1 || x === 2\n    });\n    this.overlinePeriod = getStringOption(attributes.overlinePeriod, [\"all\", \"word\"]);\n    this.posture = getStringOption(attributes.posture, [\"normal\", \"italic\"]);\n    this.size = getMeasurement(attributes.size, \"10pt\");\n    this.typeface = attributes.typeface || \"Courier\";\n    this.underline = getInteger({\n      data: attributes.underline,\n      defaultValue: 0,\n      validate: x => x === 1 || x === 2\n    });\n    this.underlinePeriod = getStringOption(attributes.underlinePeriod, [\"all\", \"word\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.weight = getStringOption(attributes.weight, [\"normal\", \"bold\"]);\n    this.extras = null;\n    this.fill = null;\n  }\n  [$clean](builder) {\n    super[$clean](builder);\n    this[$globalData].usedTypefaces.add(this.typeface);\n  }\n  [$toStyle]() {\n    const style = toStyle(this, \"fill\");\n    const color = style.color;\n    if (color) {\n      if (color === \"#000000\") {\n        delete style.color;\n      } else if (!color.startsWith(\"#\")) {\n        style.background = color;\n        style.backgroundClip = \"text\";\n        style.color = \"transparent\";\n      }\n    }\n    if (this.baselineShift) {\n      style.verticalAlign = measureToString(this.baselineShift);\n    }\n    style.fontKerning = this.kerningMode === \"none\" ? \"none\" : \"normal\";\n    style.letterSpacing = measureToString(this.letterSpacing);\n    if (this.lineThrough !== 0) {\n      style.textDecoration = \"line-through\";\n      if (this.lineThrough === 2) {\n        style.textDecorationStyle = \"double\";\n      }\n    }\n    if (this.overline !== 0) {\n      style.textDecoration = \"overline\";\n      if (this.overline === 2) {\n        style.textDecorationStyle = \"double\";\n      }\n    }\n    style.fontStyle = this.posture;\n    style.fontSize = measureToString(0.99 * this.size);\n    setFontFamily(this, this, this[$globalData].fontFinder, style);\n    if (this.underline !== 0) {\n      style.textDecoration = \"underline\";\n      if (this.underline === 2) {\n        style.textDecorationStyle = \"double\";\n      }\n    }\n    style.fontWeight = this.weight;\n    return style;\n  }\n}\nclass Format extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"format\", true);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n    this.picture = null;\n  }\n}\nclass Handler extends StringObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"handler\");\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Hyphenation extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"hyphenation\");\n    this.excludeAllCaps = getInteger({\n      data: attributes.excludeAllCaps,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.excludeInitialCap = getInteger({\n      data: attributes.excludeInitialCap,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.hyphenate = getInteger({\n      data: attributes.hyphenate,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.id = attributes.id || \"\";\n    this.pushCharacterCount = getInteger({\n      data: attributes.pushCharacterCount,\n      defaultValue: 3,\n      validate: x => x >= 0\n    });\n    this.remainCharacterCount = getInteger({\n      data: attributes.remainCharacterCount,\n      defaultValue: 3,\n      validate: x => x >= 0\n    });\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.wordCharacterCount = getInteger({\n      data: attributes.wordCharacterCount,\n      defaultValue: 7,\n      validate: x => x >= 0\n    });\n  }\n}\nclass Image extends StringObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"image\");\n    this.aspect = getStringOption(attributes.aspect, [\"fit\", \"actual\", \"height\", \"none\", \"width\"]);\n    this.contentType = attributes.contentType || \"\";\n    this.href = attributes.href || \"\";\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.transferEncoding = getStringOption(attributes.transferEncoding, [\"base64\", \"none\", \"package\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$toHTML]() {\n    if (this.contentType && !MIMES.has(this.contentType.toLowerCase())) {\n      return HTMLResult.EMPTY;\n    }\n    let buffer = this[$globalData].images && this[$globalData].images.get(this.href);\n    if (!buffer && (this.href || !this[$content])) {\n      return HTMLResult.EMPTY;\n    }\n    if (!buffer && this.transferEncoding === \"base64\") {\n      buffer = stringToBytes(atob(this[$content]));\n    }\n    if (!buffer) {\n      return HTMLResult.EMPTY;\n    }\n    if (!this.contentType) {\n      for (const [header, type] of IMAGES_HEADERS) {\n        if (buffer.length > header.length && header.every((x, i) => x === buffer[i])) {\n          this.contentType = type;\n          break;\n        }\n      }\n      if (!this.contentType) {\n        return HTMLResult.EMPTY;\n      }\n    }\n    const blob = new Blob([buffer], {\n      type: this.contentType\n    });\n    let style;\n    switch (this.aspect) {\n      case \"fit\":\n      case \"actual\":\n        break;\n      case \"height\":\n        style = {\n          height: \"100%\",\n          objectFit: \"fill\"\n        };\n        break;\n      case \"none\":\n        style = {\n          width: \"100%\",\n          height: \"100%\",\n          objectFit: \"fill\"\n        };\n        break;\n      case \"width\":\n        style = {\n          width: \"100%\",\n          objectFit: \"fill\"\n        };\n        break;\n    }\n    const parent = this[$getParent]();\n    return HTMLResult.success({\n      name: \"img\",\n      attributes: {\n        class: [\"xfaImage\"],\n        style,\n        src: URL.createObjectURL(blob),\n        alt: parent ? ariaLabel(parent[$getParent]()) : null\n      }\n    });\n  }\n}\nclass ImageEdit extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"imageEdit\", true);\n    this.data = getStringOption(attributes.data, [\"link\", \"embed\"]);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.border = null;\n    this.extras = null;\n    this.margin = null;\n  }\n  [$toHTML](availableSpace) {\n    if (this.data === \"embed\") {\n      return HTMLResult.success({\n        name: \"div\",\n        children: [],\n        attributes: {}\n      });\n    }\n    return HTMLResult.EMPTY;\n  }\n}\nclass Integer extends ContentObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"integer\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$finalize]() {\n    const number = parseInt(this[$content].trim(), 10);\n    this[$content] = isNaN(number) ? null : number;\n  }\n  [$toHTML](availableSpace) {\n    return valueToHtml(this[$content] !== null ? this[$content].toString() : \"\");\n  }\n}\nclass Issuers extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"issuers\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.certificate = new XFAObjectArray();\n  }\n}\nclass Items extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"items\", true);\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.presence = getStringOption(attributes.presence, [\"visible\", \"hidden\", \"inactive\", \"invisible\"]);\n    this.ref = attributes.ref || \"\";\n    this.save = getInteger({\n      data: attributes.save,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.boolean = new XFAObjectArray();\n    this.date = new XFAObjectArray();\n    this.dateTime = new XFAObjectArray();\n    this.decimal = new XFAObjectArray();\n    this.exData = new XFAObjectArray();\n    this.float = new XFAObjectArray();\n    this.image = new XFAObjectArray();\n    this.integer = new XFAObjectArray();\n    this.text = new XFAObjectArray();\n    this.time = new XFAObjectArray();\n  }\n  [$toHTML]() {\n    const output = [];\n    for (const child of this[$getChildren]()) {\n      output.push(child[$text]());\n    }\n    return HTMLResult.success(output);\n  }\n}\nclass Keep extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"keep\", true);\n    this.id = attributes.id || \"\";\n    const options = [\"none\", \"contentArea\", \"pageArea\"];\n    this.intact = getStringOption(attributes.intact, options);\n    this.next = getStringOption(attributes.next, options);\n    this.previous = getStringOption(attributes.previous, options);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n  }\n}\nclass KeyUsage extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"keyUsage\");\n    const options = [\"\", \"yes\", \"no\"];\n    this.crlSign = getStringOption(attributes.crlSign, options);\n    this.dataEncipherment = getStringOption(attributes.dataEncipherment, options);\n    this.decipherOnly = getStringOption(attributes.decipherOnly, options);\n    this.digitalSignature = getStringOption(attributes.digitalSignature, options);\n    this.encipherOnly = getStringOption(attributes.encipherOnly, options);\n    this.id = attributes.id || \"\";\n    this.keyAgreement = getStringOption(attributes.keyAgreement, options);\n    this.keyCertSign = getStringOption(attributes.keyCertSign, options);\n    this.keyEncipherment = getStringOption(attributes.keyEncipherment, options);\n    this.nonRepudiation = getStringOption(attributes.nonRepudiation, options);\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Line extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"line\", true);\n    this.hand = getStringOption(attributes.hand, [\"even\", \"left\", \"right\"]);\n    this.id = attributes.id || \"\";\n    this.slope = getStringOption(attributes.slope, [\"\\\\\", \"/\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.edge = null;\n  }\n  [$toHTML]() {\n    const parent = this[$getParent]()[$getParent]();\n    const edge = this.edge || new Edge({});\n    const edgeStyle = edge[$toStyle]();\n    const style = Object.create(null);\n    const thickness = edge.presence === \"visible\" ? edge.thickness : 0;\n    style.strokeWidth = measureToString(thickness);\n    style.stroke = edgeStyle.color;\n    let x1, y1, x2, y2;\n    let width = \"100%\";\n    let height = \"100%\";\n    if (parent.w <= thickness) {\n      [x1, y1, x2, y2] = [\"50%\", 0, \"50%\", \"100%\"];\n      width = style.strokeWidth;\n    } else if (parent.h <= thickness) {\n      [x1, y1, x2, y2] = [0, \"50%\", \"100%\", \"50%\"];\n      height = style.strokeWidth;\n    } else if (this.slope === \"\\\\\") {\n      [x1, y1, x2, y2] = [0, 0, \"100%\", \"100%\"];\n    } else {\n      [x1, y1, x2, y2] = [0, \"100%\", \"100%\", 0];\n    }\n    const line = {\n      name: \"line\",\n      attributes: {\n        xmlns: SVG_NS,\n        x1,\n        y1,\n        x2,\n        y2,\n        style\n      }\n    };\n    const svg = {\n      name: \"svg\",\n      children: [line],\n      attributes: {\n        xmlns: SVG_NS,\n        width,\n        height,\n        style: {\n          overflow: \"visible\"\n        }\n      }\n    };\n    if (hasMargin(parent)) {\n      return HTMLResult.success({\n        name: \"div\",\n        attributes: {\n          style: {\n            display: \"inline\",\n            width: \"100%\",\n            height: \"100%\"\n          }\n        },\n        children: [svg]\n      });\n    }\n    svg.attributes.style.position = \"absolute\";\n    return HTMLResult.success(svg);\n  }\n}\nclass Linear extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"linear\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"toRight\", \"toBottom\", \"toLeft\", \"toTop\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.color = null;\n    this.extras = null;\n  }\n  [$toStyle](startColor) {\n    startColor = startColor ? startColor[$toStyle]() : \"#FFFFFF\";\n    const transf = this.type.replace(/([RBLT])/, \" $1\").toLowerCase();\n    const endColor = this.color ? this.color[$toStyle]() : \"#000000\";\n    return `linear-gradient(${transf}, ${startColor}, ${endColor})`;\n  }\n}\nclass LockDocument extends ContentObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"lockDocument\");\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$finalize]() {\n    this[$content] = getStringOption(this[$content], [\"auto\", \"0\", \"1\"]);\n  }\n}\nclass Manifest extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"manifest\", true);\n    this.action = getStringOption(attributes.action, [\"include\", \"all\", \"exclude\"]);\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n    this.ref = new XFAObjectArray();\n  }\n}\nclass Margin extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"margin\", true);\n    this.bottomInset = getMeasurement(attributes.bottomInset, \"0\");\n    this.id = attributes.id || \"\";\n    this.leftInset = getMeasurement(attributes.leftInset, \"0\");\n    this.rightInset = getMeasurement(attributes.rightInset, \"0\");\n    this.topInset = getMeasurement(attributes.topInset, \"0\");\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n  }\n  [$toStyle]() {\n    return {\n      margin: measureToString(this.topInset) + \" \" + measureToString(this.rightInset) + \" \" + measureToString(this.bottomInset) + \" \" + measureToString(this.leftInset)\n    };\n  }\n}\nclass Mdp extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"mdp\");\n    this.id = attributes.id || \"\";\n    this.permissions = getInteger({\n      data: attributes.permissions,\n      defaultValue: 2,\n      validate: x => x === 1 || x === 3\n    });\n    this.signatureType = getStringOption(attributes.signatureType, [\"filler\", \"author\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Medium extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"medium\");\n    this.id = attributes.id || \"\";\n    this.imagingBBox = getBBox(attributes.imagingBBox);\n    this.long = getMeasurement(attributes.long);\n    this.orientation = getStringOption(attributes.orientation, [\"portrait\", \"landscape\"]);\n    this.short = getMeasurement(attributes.short);\n    this.stock = attributes.stock || \"\";\n    this.trayIn = getStringOption(attributes.trayIn, [\"auto\", \"delegate\", \"pageFront\"]);\n    this.trayOut = getStringOption(attributes.trayOut, [\"auto\", \"delegate\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Message extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"message\", true);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.text = new XFAObjectArray();\n  }\n}\nclass NumericEdit extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"numericEdit\", true);\n    this.hScrollPolicy = getStringOption(attributes.hScrollPolicy, [\"auto\", \"off\", \"on\"]);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.border = null;\n    this.comb = null;\n    this.extras = null;\n    this.margin = null;\n  }\n  [$toHTML](availableSpace) {\n    const style = toStyle(this, \"border\", \"font\", \"margin\");\n    const field = this[$getParent]()[$getParent]();\n    const html = {\n      name: \"input\",\n      attributes: {\n        type: \"text\",\n        fieldId: field[$uid],\n        dataId: field[$data]?.[$uid] || field[$uid],\n        class: [\"xfaTextfield\"],\n        style,\n        \"aria-label\": ariaLabel(field),\n        \"aria-required\": false\n      }\n    };\n    if (isRequired(field)) {\n      html.attributes[\"aria-required\"] = true;\n      html.attributes.required = true;\n    }\n    return HTMLResult.success({\n      name: \"label\",\n      attributes: {\n        class: [\"xfaLabel\"]\n      },\n      children: [html]\n    });\n  }\n}\nclass Occur extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"occur\", true);\n    this.id = attributes.id || \"\";\n    this.initial = attributes.initial !== \"\" ? getInteger({\n      data: attributes.initial,\n      defaultValue: \"\",\n      validate: x => true\n    }) : \"\";\n    this.max = attributes.max !== \"\" ? getInteger({\n      data: attributes.max,\n      defaultValue: 1,\n      validate: x => true\n    }) : \"\";\n    this.min = attributes.min !== \"\" ? getInteger({\n      data: attributes.min,\n      defaultValue: 1,\n      validate: x => true\n    }) : \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n  }\n  [$clean]() {\n    const parent = this[$getParent]();\n    const originalMin = this.min;\n    if (this.min === \"\") {\n      this.min = parent instanceof PageArea || parent instanceof PageSet ? 0 : 1;\n    }\n    if (this.max === \"\") {\n      if (originalMin === \"\") {\n        this.max = parent instanceof PageArea || parent instanceof PageSet ? -1 : 1;\n      } else {\n        this.max = this.min;\n      }\n    }\n    if (this.max !== -1 && this.max < this.min) {\n      this.max = this.min;\n    }\n    if (this.initial === \"\") {\n      this.initial = parent instanceof Template ? 1 : this.min;\n    }\n  }\n}\nclass Oid extends StringObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"oid\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Oids extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"oids\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.oid = new XFAObjectArray();\n  }\n}\nclass Overflow extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"overflow\");\n    this.id = attributes.id || \"\";\n    this.leader = attributes.leader || \"\";\n    this.target = attributes.target || \"\";\n    this.trailer = attributes.trailer || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$getExtra]() {\n    if (!this[$extra]) {\n      const parent = this[$getParent]();\n      const root = this[$getTemplateRoot]();\n      const target = root[$searchNode](this.target, parent);\n      const leader = root[$searchNode](this.leader, parent);\n      const trailer = root[$searchNode](this.trailer, parent);\n      this[$extra] = {\n        target: target?.[0] || null,\n        leader: leader?.[0] || null,\n        trailer: trailer?.[0] || null,\n        addLeader: false,\n        addTrailer: false\n      };\n    }\n    return this[$extra];\n  }\n}\nclass PageArea extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"pageArea\", true);\n    this.blankOrNotBlank = getStringOption(attributes.blankOrNotBlank, [\"any\", \"blank\", \"notBlank\"]);\n    this.id = attributes.id || \"\";\n    this.initialNumber = getInteger({\n      data: attributes.initialNumber,\n      defaultValue: 1,\n      validate: x => true\n    });\n    this.name = attributes.name || \"\";\n    this.numbered = getInteger({\n      data: attributes.numbered,\n      defaultValue: 1,\n      validate: x => true\n    });\n    this.oddOrEven = getStringOption(attributes.oddOrEven, [\"any\", \"even\", \"odd\"]);\n    this.pagePosition = getStringOption(attributes.pagePosition, [\"any\", \"first\", \"last\", \"only\", \"rest\"]);\n    this.relevant = getRelevant(attributes.relevant);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.desc = null;\n    this.extras = null;\n    this.medium = null;\n    this.occur = null;\n    this.area = new XFAObjectArray();\n    this.contentArea = new XFAObjectArray();\n    this.draw = new XFAObjectArray();\n    this.exclGroup = new XFAObjectArray();\n    this.field = new XFAObjectArray();\n    this.subform = new XFAObjectArray();\n  }\n  [$isUsable]() {\n    if (!this[$extra]) {\n      this[$extra] = {\n        numberOfUse: 0\n      };\n      return true;\n    }\n    return !this.occur || this.occur.max === -1 || this[$extra].numberOfUse < this.occur.max;\n  }\n  [$cleanPage]() {\n    delete this[$extra];\n  }\n  [$getNextPage]() {\n    if (!this[$extra]) {\n      this[$extra] = {\n        numberOfUse: 0\n      };\n    }\n    const parent = this[$getParent]();\n    if (parent.relation === \"orderedOccurrence\") {\n      if (this[$isUsable]()) {\n        this[$extra].numberOfUse += 1;\n        return this;\n      }\n    }\n    return parent[$getNextPage]();\n  }\n  [$getAvailableSpace]() {\n    return this[$extra].space || {\n      width: 0,\n      height: 0\n    };\n  }\n  [$toHTML]() {\n    if (!this[$extra]) {\n      this[$extra] = {\n        numberOfUse: 1\n      };\n    }\n    const children = [];\n    this[$extra].children = children;\n    const style = Object.create(null);\n    if (this.medium && this.medium.short && this.medium.long) {\n      style.width = measureToString(this.medium.short);\n      style.height = measureToString(this.medium.long);\n      this[$extra].space = {\n        width: this.medium.short,\n        height: this.medium.long\n      };\n      if (this.medium.orientation === \"landscape\") {\n        const x = style.width;\n        style.width = style.height;\n        style.height = x;\n        this[$extra].space = {\n          width: this.medium.long,\n          height: this.medium.short\n        };\n      }\n    } else {\n      warn(\"XFA - No medium specified in pageArea: please file a bug.\");\n    }\n    this[$childrenToHTML]({\n      filter: new Set([\"area\", \"draw\", \"field\", \"subform\"]),\n      include: true\n    });\n    this[$childrenToHTML]({\n      filter: new Set([\"contentArea\"]),\n      include: true\n    });\n    return HTMLResult.success({\n      name: \"div\",\n      children,\n      attributes: {\n        class: [\"xfaPage\"],\n        id: this[$uid],\n        style,\n        xfaName: this.name\n      }\n    });\n  }\n}\nclass PageSet extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"pageSet\", true);\n    this.duplexImposition = getStringOption(attributes.duplexImposition, [\"longEdge\", \"shortEdge\"]);\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.relation = getStringOption(attributes.relation, [\"orderedOccurrence\", \"duplexPaginated\", \"simplexPaginated\"]);\n    this.relevant = getRelevant(attributes.relevant);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n    this.occur = null;\n    this.pageArea = new XFAObjectArray();\n    this.pageSet = new XFAObjectArray();\n  }\n  [$cleanPage]() {\n    for (const page of this.pageArea.children) {\n      page[$cleanPage]();\n    }\n    for (const page of this.pageSet.children) {\n      page[$cleanPage]();\n    }\n  }\n  [$isUsable]() {\n    return !this.occur || this.occur.max === -1 || this[$extra].numberOfUse < this.occur.max;\n  }\n  [$getNextPage]() {\n    if (!this[$extra]) {\n      this[$extra] = {\n        numberOfUse: 1,\n        pageIndex: -1,\n        pageSetIndex: -1\n      };\n    }\n    if (this.relation === \"orderedOccurrence\") {\n      if (this[$extra].pageIndex + 1 < this.pageArea.children.length) {\n        this[$extra].pageIndex += 1;\n        const pageArea = this.pageArea.children[this[$extra].pageIndex];\n        return pageArea[$getNextPage]();\n      }\n      if (this[$extra].pageSetIndex + 1 < this.pageSet.children.length) {\n        this[$extra].pageSetIndex += 1;\n        return this.pageSet.children[this[$extra].pageSetIndex][$getNextPage]();\n      }\n      if (this[$isUsable]()) {\n        this[$extra].numberOfUse += 1;\n        this[$extra].pageIndex = -1;\n        this[$extra].pageSetIndex = -1;\n        return this[$getNextPage]();\n      }\n      const parent = this[$getParent]();\n      if (parent instanceof PageSet) {\n        return parent[$getNextPage]();\n      }\n      this[$cleanPage]();\n      return this[$getNextPage]();\n    }\n    const pageNumber = this[$getTemplateRoot]()[$extra].pageNumber;\n    const parity = pageNumber % 2 === 0 ? \"even\" : \"odd\";\n    const position = pageNumber === 0 ? \"first\" : \"rest\";\n    let page = this.pageArea.children.find(p => p.oddOrEven === parity && p.pagePosition === position);\n    if (page) {\n      return page;\n    }\n    page = this.pageArea.children.find(p => p.oddOrEven === \"any\" && p.pagePosition === position);\n    if (page) {\n      return page;\n    }\n    page = this.pageArea.children.find(p => p.oddOrEven === \"any\" && p.pagePosition === \"any\");\n    if (page) {\n      return page;\n    }\n    return this.pageArea.children[0];\n  }\n}\nclass Para extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"para\", true);\n    this.hAlign = getStringOption(attributes.hAlign, [\"left\", \"center\", \"justify\", \"justifyAll\", \"radix\", \"right\"]);\n    this.id = attributes.id || \"\";\n    this.lineHeight = attributes.lineHeight ? getMeasurement(attributes.lineHeight, \"0pt\") : \"\";\n    this.marginLeft = attributes.marginLeft ? getMeasurement(attributes.marginLeft, \"0pt\") : \"\";\n    this.marginRight = attributes.marginRight ? getMeasurement(attributes.marginRight, \"0pt\") : \"\";\n    this.orphans = getInteger({\n      data: attributes.orphans,\n      defaultValue: 0,\n      validate: x => x >= 0\n    });\n    this.preserve = attributes.preserve || \"\";\n    this.radixOffset = attributes.radixOffset ? getMeasurement(attributes.radixOffset, \"0pt\") : \"\";\n    this.spaceAbove = attributes.spaceAbove ? getMeasurement(attributes.spaceAbove, \"0pt\") : \"\";\n    this.spaceBelow = attributes.spaceBelow ? getMeasurement(attributes.spaceBelow, \"0pt\") : \"\";\n    this.tabDefault = attributes.tabDefault ? getMeasurement(this.tabDefault) : \"\";\n    this.tabStops = (attributes.tabStops || \"\").trim().split(/\\s+/).map((x, i) => i % 2 === 1 ? getMeasurement(x) : x);\n    this.textIndent = attributes.textIndent ? getMeasurement(attributes.textIndent, \"0pt\") : \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.vAlign = getStringOption(attributes.vAlign, [\"top\", \"bottom\", \"middle\"]);\n    this.widows = getInteger({\n      data: attributes.widows,\n      defaultValue: 0,\n      validate: x => x >= 0\n    });\n    this.hyphenation = null;\n  }\n  [$toStyle]() {\n    const style = toStyle(this, \"hAlign\");\n    if (this.marginLeft !== \"\") {\n      style.paddingLeft = measureToString(this.marginLeft);\n    }\n    if (this.marginRight !== \"\") {\n      style.paddingight = measureToString(this.marginRight);\n    }\n    if (this.spaceAbove !== \"\") {\n      style.paddingTop = measureToString(this.spaceAbove);\n    }\n    if (this.spaceBelow !== \"\") {\n      style.paddingBottom = measureToString(this.spaceBelow);\n    }\n    if (this.textIndent !== \"\") {\n      style.textIndent = measureToString(this.textIndent);\n      fixTextIndent(style);\n    }\n    if (this.lineHeight > 0) {\n      style.lineHeight = measureToString(this.lineHeight);\n    }\n    if (this.tabDefault !== \"\") {\n      style.tabSize = measureToString(this.tabDefault);\n    }\n    if (this.tabStops.length > 0) {}\n    if (this.hyphenatation) {\n      Object.assign(style, this.hyphenatation[$toStyle]());\n    }\n    return style;\n  }\n}\nclass PasswordEdit extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"passwordEdit\", true);\n    this.hScrollPolicy = getStringOption(attributes.hScrollPolicy, [\"auto\", \"off\", \"on\"]);\n    this.id = attributes.id || \"\";\n    this.passwordChar = attributes.passwordChar || \"*\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.border = null;\n    this.extras = null;\n    this.margin = null;\n  }\n}\nclass template_Pattern extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"pattern\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"crossHatch\", \"crossDiagonal\", \"diagonalLeft\", \"diagonalRight\", \"horizontal\", \"vertical\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.color = null;\n    this.extras = null;\n  }\n  [$toStyle](startColor) {\n    startColor = startColor ? startColor[$toStyle]() : \"#FFFFFF\";\n    const endColor = this.color ? this.color[$toStyle]() : \"#000000\";\n    const width = 5;\n    const cmd = \"repeating-linear-gradient\";\n    const colors = `${startColor},${startColor} ${width}px,${endColor} ${width}px,${endColor} ${2 * width}px`;\n    switch (this.type) {\n      case \"crossHatch\":\n        return `${cmd}(to top,${colors}) ${cmd}(to right,${colors})`;\n      case \"crossDiagonal\":\n        return `${cmd}(45deg,${colors}) ${cmd}(-45deg,${colors})`;\n      case \"diagonalLeft\":\n        return `${cmd}(45deg,${colors})`;\n      case \"diagonalRight\":\n        return `${cmd}(-45deg,${colors})`;\n      case \"horizontal\":\n        return `${cmd}(to top,${colors})`;\n      case \"vertical\":\n        return `${cmd}(to right,${colors})`;\n    }\n    return \"\";\n  }\n}\nclass Picture extends StringObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"picture\");\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Proto extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"proto\", true);\n    this.appearanceFilter = new XFAObjectArray();\n    this.arc = new XFAObjectArray();\n    this.area = new XFAObjectArray();\n    this.assist = new XFAObjectArray();\n    this.barcode = new XFAObjectArray();\n    this.bindItems = new XFAObjectArray();\n    this.bookend = new XFAObjectArray();\n    this.boolean = new XFAObjectArray();\n    this.border = new XFAObjectArray();\n    this.break = new XFAObjectArray();\n    this.breakAfter = new XFAObjectArray();\n    this.breakBefore = new XFAObjectArray();\n    this.button = new XFAObjectArray();\n    this.calculate = new XFAObjectArray();\n    this.caption = new XFAObjectArray();\n    this.certificate = new XFAObjectArray();\n    this.certificates = new XFAObjectArray();\n    this.checkButton = new XFAObjectArray();\n    this.choiceList = new XFAObjectArray();\n    this.color = new XFAObjectArray();\n    this.comb = new XFAObjectArray();\n    this.connect = new XFAObjectArray();\n    this.contentArea = new XFAObjectArray();\n    this.corner = new XFAObjectArray();\n    this.date = new XFAObjectArray();\n    this.dateTime = new XFAObjectArray();\n    this.dateTimeEdit = new XFAObjectArray();\n    this.decimal = new XFAObjectArray();\n    this.defaultUi = new XFAObjectArray();\n    this.desc = new XFAObjectArray();\n    this.digestMethod = new XFAObjectArray();\n    this.digestMethods = new XFAObjectArray();\n    this.draw = new XFAObjectArray();\n    this.edge = new XFAObjectArray();\n    this.encoding = new XFAObjectArray();\n    this.encodings = new XFAObjectArray();\n    this.encrypt = new XFAObjectArray();\n    this.encryptData = new XFAObjectArray();\n    this.encryption = new XFAObjectArray();\n    this.encryptionMethod = new XFAObjectArray();\n    this.encryptionMethods = new XFAObjectArray();\n    this.event = new XFAObjectArray();\n    this.exData = new XFAObjectArray();\n    this.exObject = new XFAObjectArray();\n    this.exclGroup = new XFAObjectArray();\n    this.execute = new XFAObjectArray();\n    this.extras = new XFAObjectArray();\n    this.field = new XFAObjectArray();\n    this.fill = new XFAObjectArray();\n    this.filter = new XFAObjectArray();\n    this.float = new XFAObjectArray();\n    this.font = new XFAObjectArray();\n    this.format = new XFAObjectArray();\n    this.handler = new XFAObjectArray();\n    this.hyphenation = new XFAObjectArray();\n    this.image = new XFAObjectArray();\n    this.imageEdit = new XFAObjectArray();\n    this.integer = new XFAObjectArray();\n    this.issuers = new XFAObjectArray();\n    this.items = new XFAObjectArray();\n    this.keep = new XFAObjectArray();\n    this.keyUsage = new XFAObjectArray();\n    this.line = new XFAObjectArray();\n    this.linear = new XFAObjectArray();\n    this.lockDocument = new XFAObjectArray();\n    this.manifest = new XFAObjectArray();\n    this.margin = new XFAObjectArray();\n    this.mdp = new XFAObjectArray();\n    this.medium = new XFAObjectArray();\n    this.message = new XFAObjectArray();\n    this.numericEdit = new XFAObjectArray();\n    this.occur = new XFAObjectArray();\n    this.oid = new XFAObjectArray();\n    this.oids = new XFAObjectArray();\n    this.overflow = new XFAObjectArray();\n    this.pageArea = new XFAObjectArray();\n    this.pageSet = new XFAObjectArray();\n    this.para = new XFAObjectArray();\n    this.passwordEdit = new XFAObjectArray();\n    this.pattern = new XFAObjectArray();\n    this.picture = new XFAObjectArray();\n    this.radial = new XFAObjectArray();\n    this.reason = new XFAObjectArray();\n    this.reasons = new XFAObjectArray();\n    this.rectangle = new XFAObjectArray();\n    this.ref = new XFAObjectArray();\n    this.script = new XFAObjectArray();\n    this.setProperty = new XFAObjectArray();\n    this.signData = new XFAObjectArray();\n    this.signature = new XFAObjectArray();\n    this.signing = new XFAObjectArray();\n    this.solid = new XFAObjectArray();\n    this.speak = new XFAObjectArray();\n    this.stipple = new XFAObjectArray();\n    this.subform = new XFAObjectArray();\n    this.subformSet = new XFAObjectArray();\n    this.subjectDN = new XFAObjectArray();\n    this.subjectDNs = new XFAObjectArray();\n    this.submit = new XFAObjectArray();\n    this.text = new XFAObjectArray();\n    this.textEdit = new XFAObjectArray();\n    this.time = new XFAObjectArray();\n    this.timeStamp = new XFAObjectArray();\n    this.toolTip = new XFAObjectArray();\n    this.traversal = new XFAObjectArray();\n    this.traverse = new XFAObjectArray();\n    this.ui = new XFAObjectArray();\n    this.validate = new XFAObjectArray();\n    this.value = new XFAObjectArray();\n    this.variables = new XFAObjectArray();\n  }\n}\nclass Radial extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"radial\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"toEdge\", \"toCenter\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.color = null;\n    this.extras = null;\n  }\n  [$toStyle](startColor) {\n    startColor = startColor ? startColor[$toStyle]() : \"#FFFFFF\";\n    const endColor = this.color ? this.color[$toStyle]() : \"#000000\";\n    const colors = this.type === \"toEdge\" ? `${startColor},${endColor}` : `${endColor},${startColor}`;\n    return `radial-gradient(circle at center, ${colors})`;\n  }\n}\nclass Reason extends StringObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"reason\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Reasons extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"reasons\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.reason = new XFAObjectArray();\n  }\n}\nclass Rectangle extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"rectangle\", true);\n    this.hand = getStringOption(attributes.hand, [\"even\", \"left\", \"right\"]);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.corner = new XFAObjectArray(4);\n    this.edge = new XFAObjectArray(4);\n    this.fill = null;\n  }\n  [$toHTML]() {\n    const edge = this.edge.children.length ? this.edge.children[0] : new Edge({});\n    const edgeStyle = edge[$toStyle]();\n    const style = Object.create(null);\n    if (this.fill?.presence === \"visible\") {\n      Object.assign(style, this.fill[$toStyle]());\n    } else {\n      style.fill = \"transparent\";\n    }\n    style.strokeWidth = measureToString(edge.presence === \"visible\" ? edge.thickness : 0);\n    style.stroke = edgeStyle.color;\n    const corner = this.corner.children.length ? this.corner.children[0] : new Corner({});\n    const cornerStyle = corner[$toStyle]();\n    const rect = {\n      name: \"rect\",\n      attributes: {\n        xmlns: SVG_NS,\n        width: \"100%\",\n        height: \"100%\",\n        x: 0,\n        y: 0,\n        rx: cornerStyle.radius,\n        ry: cornerStyle.radius,\n        style\n      }\n    };\n    const svg = {\n      name: \"svg\",\n      children: [rect],\n      attributes: {\n        xmlns: SVG_NS,\n        style: {\n          overflow: \"visible\"\n        },\n        width: \"100%\",\n        height: \"100%\"\n      }\n    };\n    const parent = this[$getParent]()[$getParent]();\n    if (hasMargin(parent)) {\n      return HTMLResult.success({\n        name: \"div\",\n        attributes: {\n          style: {\n            display: \"inline\",\n            width: \"100%\",\n            height: \"100%\"\n          }\n        },\n        children: [svg]\n      });\n    }\n    svg.attributes.style.position = \"absolute\";\n    return HTMLResult.success(svg);\n  }\n}\nclass RefElement extends StringObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"ref\");\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Script extends StringObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"script\");\n    this.binding = attributes.binding || \"\";\n    this.contentType = attributes.contentType || \"\";\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.runAt = getStringOption(attributes.runAt, [\"client\", \"both\", \"server\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass SetProperty extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"setProperty\");\n    this.connection = attributes.connection || \"\";\n    this.ref = attributes.ref || \"\";\n    this.target = attributes.target || \"\";\n  }\n}\nclass SignData extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"signData\", true);\n    this.id = attributes.id || \"\";\n    this.operation = getStringOption(attributes.operation, [\"sign\", \"clear\", \"verify\"]);\n    this.ref = attributes.ref || \"\";\n    this.target = attributes.target || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.filter = null;\n    this.manifest = null;\n  }\n}\nclass Signature extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"signature\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"PDF1.3\", \"PDF1.6\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.border = null;\n    this.extras = null;\n    this.filter = null;\n    this.manifest = null;\n    this.margin = null;\n  }\n}\nclass Signing extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"signing\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.certificate = new XFAObjectArray();\n  }\n}\nclass Solid extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"solid\", true);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n  }\n  [$toStyle](startColor) {\n    return startColor ? startColor[$toStyle]() : \"#FFFFFF\";\n  }\n}\nclass Speak extends StringObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"speak\");\n    this.disable = getInteger({\n      data: attributes.disable,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.id = attributes.id || \"\";\n    this.priority = getStringOption(attributes.priority, [\"custom\", \"caption\", \"name\", \"toolTip\"]);\n    this.rid = attributes.rid || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Stipple extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"stipple\", true);\n    this.id = attributes.id || \"\";\n    this.rate = getInteger({\n      data: attributes.rate,\n      defaultValue: 50,\n      validate: x => x >= 0 && x <= 100\n    });\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.color = null;\n    this.extras = null;\n  }\n  [$toStyle](bgColor) {\n    const alpha = this.rate / 100;\n    return Util.makeHexColor(Math.round(bgColor.value.r * (1 - alpha) + this.value.r * alpha), Math.round(bgColor.value.g * (1 - alpha) + this.value.g * alpha), Math.round(bgColor.value.b * (1 - alpha) + this.value.b * alpha));\n  }\n}\nclass Subform extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"subform\", true);\n    this.access = getStringOption(attributes.access, [\"open\", \"nonInteractive\", \"protected\", \"readOnly\"]);\n    this.allowMacro = getInteger({\n      data: attributes.allowMacro,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.anchorType = getStringOption(attributes.anchorType, [\"topLeft\", \"bottomCenter\", \"bottomLeft\", \"bottomRight\", \"middleCenter\", \"middleLeft\", \"middleRight\", \"topCenter\", \"topRight\"]);\n    this.colSpan = getInteger({\n      data: attributes.colSpan,\n      defaultValue: 1,\n      validate: n => n >= 1 || n === -1\n    });\n    this.columnWidths = (attributes.columnWidths || \"\").trim().split(/\\s+/).map(x => x === \"-1\" ? -1 : getMeasurement(x));\n    this.h = attributes.h ? getMeasurement(attributes.h) : \"\";\n    this.hAlign = getStringOption(attributes.hAlign, [\"left\", \"center\", \"justify\", \"justifyAll\", \"radix\", \"right\"]);\n    this.id = attributes.id || \"\";\n    this.layout = getStringOption(attributes.layout, [\"position\", \"lr-tb\", \"rl-row\", \"rl-tb\", \"row\", \"table\", \"tb\"]);\n    this.locale = attributes.locale || \"\";\n    this.maxH = getMeasurement(attributes.maxH, \"0pt\");\n    this.maxW = getMeasurement(attributes.maxW, \"0pt\");\n    this.mergeMode = getStringOption(attributes.mergeMode, [\"consumeData\", \"matchTemplate\"]);\n    this.minH = getMeasurement(attributes.minH, \"0pt\");\n    this.minW = getMeasurement(attributes.minW, \"0pt\");\n    this.name = attributes.name || \"\";\n    this.presence = getStringOption(attributes.presence, [\"visible\", \"hidden\", \"inactive\", \"invisible\"]);\n    this.relevant = getRelevant(attributes.relevant);\n    this.restoreState = getStringOption(attributes.restoreState, [\"manual\", \"auto\"]);\n    this.scope = getStringOption(attributes.scope, [\"name\", \"none\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.w = attributes.w ? getMeasurement(attributes.w) : \"\";\n    this.x = getMeasurement(attributes.x, \"0pt\");\n    this.y = getMeasurement(attributes.y, \"0pt\");\n    this.assist = null;\n    this.bind = null;\n    this.bookend = null;\n    this.border = null;\n    this.break = null;\n    this.calculate = null;\n    this.desc = null;\n    this.extras = null;\n    this.keep = null;\n    this.margin = null;\n    this.occur = null;\n    this.overflow = null;\n    this.pageSet = null;\n    this.para = null;\n    this.traversal = null;\n    this.validate = null;\n    this.variables = null;\n    this.area = new XFAObjectArray();\n    this.breakAfter = new XFAObjectArray();\n    this.breakBefore = new XFAObjectArray();\n    this.connect = new XFAObjectArray();\n    this.draw = new XFAObjectArray();\n    this.event = new XFAObjectArray();\n    this.exObject = new XFAObjectArray();\n    this.exclGroup = new XFAObjectArray();\n    this.field = new XFAObjectArray();\n    this.proto = new XFAObjectArray();\n    this.setProperty = new XFAObjectArray();\n    this.subform = new XFAObjectArray();\n    this.subformSet = new XFAObjectArray();\n  }\n  [$getSubformParent]() {\n    const parent = this[$getParent]();\n    if (parent instanceof SubformSet) {\n      return parent[$getSubformParent]();\n    }\n    return parent;\n  }\n  [$isBindable]() {\n    return true;\n  }\n  [$isThereMoreWidth]() {\n    return this.layout.endsWith(\"-tb\") && this[$extra].attempt === 0 && this[$extra].numberInLine > 0 || this[$getParent]()[$isThereMoreWidth]();\n  }\n  *[$getContainedChildren]() {\n    yield* getContainedChildren(this);\n  }\n  [$flushHTML]() {\n    return flushHTML(this);\n  }\n  [$addHTML](html, bbox) {\n    addHTML(this, html, bbox);\n  }\n  [$getAvailableSpace]() {\n    return getAvailableSpace(this);\n  }\n  [$isSplittable]() {\n    const parent = this[$getSubformParent]();\n    if (!parent[$isSplittable]()) {\n      return false;\n    }\n    if (this[$extra]._isSplittable !== undefined) {\n      return this[$extra]._isSplittable;\n    }\n    if (this.layout === \"position\" || this.layout.includes(\"row\")) {\n      this[$extra]._isSplittable = false;\n      return false;\n    }\n    if (this.keep && this.keep.intact !== \"none\") {\n      this[$extra]._isSplittable = false;\n      return false;\n    }\n    if (parent.layout?.endsWith(\"-tb\") && parent[$extra].numberInLine !== 0) {\n      return false;\n    }\n    this[$extra]._isSplittable = true;\n    return true;\n  }\n  [$toHTML](availableSpace) {\n    setTabIndex(this);\n    if (this.break) {\n      if (this.break.after !== \"auto\" || this.break.afterTarget !== \"\") {\n        const node = new BreakAfter({\n          targetType: this.break.after,\n          target: this.break.afterTarget,\n          startNew: this.break.startNew.toString()\n        });\n        node[$globalData] = this[$globalData];\n        this[$appendChild](node);\n        this.breakAfter.push(node);\n      }\n      if (this.break.before !== \"auto\" || this.break.beforeTarget !== \"\") {\n        const node = new BreakBefore({\n          targetType: this.break.before,\n          target: this.break.beforeTarget,\n          startNew: this.break.startNew.toString()\n        });\n        node[$globalData] = this[$globalData];\n        this[$appendChild](node);\n        this.breakBefore.push(node);\n      }\n      if (this.break.overflowTarget !== \"\") {\n        const node = new Overflow({\n          target: this.break.overflowTarget,\n          leader: this.break.overflowLeader,\n          trailer: this.break.overflowTrailer\n        });\n        node[$globalData] = this[$globalData];\n        this[$appendChild](node);\n        this.overflow.push(node);\n      }\n      this[$removeChild](this.break);\n      this.break = null;\n    }\n    if (this.presence === \"hidden\" || this.presence === \"inactive\") {\n      return HTMLResult.EMPTY;\n    }\n    if (this.breakBefore.children.length > 1 || this.breakAfter.children.length > 1) {\n      warn(\"XFA - Several breakBefore or breakAfter in subforms: please file a bug.\");\n    }\n    if (this.breakBefore.children.length >= 1) {\n      const breakBefore = this.breakBefore.children[0];\n      if (handleBreak(breakBefore)) {\n        return HTMLResult.breakNode(breakBefore);\n      }\n    }\n    if (this[$extra]?.afterBreakAfter) {\n      return HTMLResult.EMPTY;\n    }\n    fixDimensions(this);\n    const children = [];\n    const attributes = {\n      id: this[$uid],\n      class: []\n    };\n    setAccess(this, attributes.class);\n    if (!this[$extra]) {\n      this[$extra] = Object.create(null);\n    }\n    Object.assign(this[$extra], {\n      children,\n      line: null,\n      attributes,\n      attempt: 0,\n      numberInLine: 0,\n      availableSpace: {\n        width: Math.min(this.w || Infinity, availableSpace.width),\n        height: Math.min(this.h || Infinity, availableSpace.height)\n      },\n      width: 0,\n      height: 0,\n      prevHeight: 0,\n      currentWidth: 0\n    });\n    const root = this[$getTemplateRoot]();\n    const savedNoLayoutFailure = root[$extra].noLayoutFailure;\n    const isSplittable = this[$isSplittable]();\n    if (!isSplittable) {\n      setFirstUnsplittable(this);\n    }\n    if (!checkDimensions(this, availableSpace)) {\n      return HTMLResult.FAILURE;\n    }\n    const filter = new Set([\"area\", \"draw\", \"exclGroup\", \"field\", \"subform\", \"subformSet\"]);\n    if (this.layout.includes(\"row\")) {\n      const columnWidths = this[$getSubformParent]().columnWidths;\n      if (Array.isArray(columnWidths) && columnWidths.length > 0) {\n        this[$extra].columnWidths = columnWidths;\n        this[$extra].currentColumn = 0;\n      }\n    }\n    const style = toStyle(this, \"anchorType\", \"dimensions\", \"position\", \"presence\", \"border\", \"margin\", \"hAlign\");\n    const classNames = [\"xfaSubform\"];\n    const cl = layoutClass(this);\n    if (cl) {\n      classNames.push(cl);\n    }\n    attributes.style = style;\n    attributes.class = classNames;\n    if (this.name) {\n      attributes.xfaName = this.name;\n    }\n    if (this.overflow) {\n      const overflowExtra = this.overflow[$getExtra]();\n      if (overflowExtra.addLeader) {\n        overflowExtra.addLeader = false;\n        handleOverflow(this, overflowExtra.leader, availableSpace);\n      }\n    }\n    this[$pushPara]();\n    const isLrTb = this.layout === \"lr-tb\" || this.layout === \"rl-tb\";\n    const maxRun = isLrTb ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT : 1;\n    for (; this[$extra].attempt < maxRun; this[$extra].attempt++) {\n      if (isLrTb && this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT - 1) {\n        this[$extra].numberInLine = 0;\n      }\n      const result = this[$childrenToHTML]({\n        filter,\n        include: true\n      });\n      if (result.success) {\n        break;\n      }\n      if (result.isBreak()) {\n        this[$popPara]();\n        return result;\n      }\n      if (isLrTb && this[$extra].attempt === 0 && this[$extra].numberInLine === 0 && !root[$extra].noLayoutFailure) {\n        this[$extra].attempt = maxRun;\n        break;\n      }\n    }\n    this[$popPara]();\n    if (!isSplittable) {\n      unsetFirstUnsplittable(this);\n    }\n    root[$extra].noLayoutFailure = savedNoLayoutFailure;\n    if (this[$extra].attempt === maxRun) {\n      if (this.overflow) {\n        this[$getTemplateRoot]()[$extra].overflowNode = this.overflow;\n      }\n      if (!isSplittable) {\n        delete this[$extra];\n      }\n      return HTMLResult.FAILURE;\n    }\n    if (this.overflow) {\n      const overflowExtra = this.overflow[$getExtra]();\n      if (overflowExtra.addTrailer) {\n        overflowExtra.addTrailer = false;\n        handleOverflow(this, overflowExtra.trailer, availableSpace);\n      }\n    }\n    let marginH = 0;\n    let marginV = 0;\n    if (this.margin) {\n      marginH = this.margin.leftInset + this.margin.rightInset;\n      marginV = this.margin.topInset + this.margin.bottomInset;\n    }\n    const width = Math.max(this[$extra].width + marginH, this.w || 0);\n    const height = Math.max(this[$extra].height + marginV, this.h || 0);\n    const bbox = [this.x, this.y, width, height];\n    if (this.w === \"\") {\n      style.width = measureToString(width);\n    }\n    if (this.h === \"\") {\n      style.height = measureToString(height);\n    }\n    if ((style.width === \"0px\" || style.height === \"0px\") && children.length === 0) {\n      return HTMLResult.EMPTY;\n    }\n    const html = {\n      name: \"div\",\n      attributes,\n      children\n    };\n    applyAssist(this, attributes);\n    const result = HTMLResult.success(createWrapper(this, html), bbox);\n    if (this.breakAfter.children.length >= 1) {\n      const breakAfter = this.breakAfter.children[0];\n      if (handleBreak(breakAfter)) {\n        this[$extra].afterBreakAfter = result;\n        return HTMLResult.breakNode(breakAfter);\n      }\n    }\n    delete this[$extra];\n    return result;\n  }\n}\nclass SubformSet extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"subformSet\", true);\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.relation = getStringOption(attributes.relation, [\"ordered\", \"choice\", \"unordered\"]);\n    this.relevant = getRelevant(attributes.relevant);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.bookend = null;\n    this.break = null;\n    this.desc = null;\n    this.extras = null;\n    this.occur = null;\n    this.overflow = null;\n    this.breakAfter = new XFAObjectArray();\n    this.breakBefore = new XFAObjectArray();\n    this.subform = new XFAObjectArray();\n    this.subformSet = new XFAObjectArray();\n  }\n  *[$getContainedChildren]() {\n    yield* getContainedChildren(this);\n  }\n  [$getSubformParent]() {\n    let parent = this[$getParent]();\n    while (!(parent instanceof Subform)) {\n      parent = parent[$getParent]();\n    }\n    return parent;\n  }\n  [$isBindable]() {\n    return true;\n  }\n}\nclass SubjectDN extends ContentObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"subjectDN\");\n    this.delimiter = attributes.delimiter || \",\";\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$finalize]() {\n    this[$content] = new Map(this[$content].split(this.delimiter).map(kv => {\n      kv = kv.split(\"=\", 2);\n      kv[0] = kv[0].trim();\n      return kv;\n    }));\n  }\n}\nclass SubjectDNs extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"subjectDNs\", true);\n    this.id = attributes.id || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.subjectDN = new XFAObjectArray();\n  }\n}\nclass Submit extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"submit\", true);\n    this.embedPDF = getInteger({\n      data: attributes.embedPDF,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.format = getStringOption(attributes.format, [\"xdp\", \"formdata\", \"pdf\", \"urlencoded\", \"xfd\", \"xml\"]);\n    this.id = attributes.id || \"\";\n    this.target = attributes.target || \"\";\n    this.textEncoding = getKeyword({\n      data: attributes.textEncoding ? attributes.textEncoding.toLowerCase() : \"\",\n      defaultValue: \"\",\n      validate: k => [\"utf-8\", \"big-five\", \"fontspecific\", \"gbk\", \"gb-18030\", \"gb-2312\", \"ksc-5601\", \"none\", \"shift-jis\", \"ucs-2\", \"utf-16\"].includes(k) || k.match(/iso-8859-\\d{2}/)\n    });\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.xdpContent = attributes.xdpContent || \"\";\n    this.encrypt = null;\n    this.encryptData = new XFAObjectArray();\n    this.signData = new XFAObjectArray();\n  }\n}\nclass Template extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"template\", true);\n    this.baseProfile = getStringOption(attributes.baseProfile, [\"full\", \"interactiveForms\"]);\n    this.extras = null;\n    this.subform = new XFAObjectArray();\n  }\n  [$finalize]() {\n    if (this.subform.children.length === 0) {\n      warn(\"XFA - No subforms in template node.\");\n    }\n    if (this.subform.children.length >= 2) {\n      warn(\"XFA - Several subforms in template node: please file a bug.\");\n    }\n    this[$tabIndex] = DEFAULT_TAB_INDEX;\n  }\n  [$isSplittable]() {\n    return true;\n  }\n  [$searchNode](expr, container) {\n    if (expr.startsWith(\"#\")) {\n      return [this[$ids].get(expr.slice(1))];\n    }\n    return searchNode(this, container, expr, true, true);\n  }\n  *[$toPages]() {\n    if (!this.subform.children.length) {\n      return HTMLResult.success({\n        name: \"div\",\n        children: []\n      });\n    }\n    this[$extra] = {\n      overflowNode: null,\n      firstUnsplittable: null,\n      currentContentArea: null,\n      currentPageArea: null,\n      noLayoutFailure: false,\n      pageNumber: 1,\n      pagePosition: \"first\",\n      oddOrEven: \"odd\",\n      blankOrNotBlank: \"nonBlank\",\n      paraStack: []\n    };\n    const root = this.subform.children[0];\n    root.pageSet[$cleanPage]();\n    const pageAreas = root.pageSet.pageArea.children;\n    const mainHtml = {\n      name: \"div\",\n      children: []\n    };\n    let pageArea = null;\n    let breakBefore = null;\n    let breakBeforeTarget = null;\n    if (root.breakBefore.children.length >= 1) {\n      breakBefore = root.breakBefore.children[0];\n      breakBeforeTarget = breakBefore.target;\n    } else if (root.subform.children.length >= 1 && root.subform.children[0].breakBefore.children.length >= 1) {\n      breakBefore = root.subform.children[0].breakBefore.children[0];\n      breakBeforeTarget = breakBefore.target;\n    } else if (root.break?.beforeTarget) {\n      breakBefore = root.break;\n      breakBeforeTarget = breakBefore.beforeTarget;\n    } else if (root.subform.children.length >= 1 && root.subform.children[0].break?.beforeTarget) {\n      breakBefore = root.subform.children[0].break;\n      breakBeforeTarget = breakBefore.beforeTarget;\n    }\n    if (breakBefore) {\n      const target = this[$searchNode](breakBeforeTarget, breakBefore[$getParent]());\n      if (target instanceof PageArea) {\n        pageArea = target;\n        breakBefore[$extra] = {};\n      }\n    }\n    if (!pageArea) {\n      pageArea = pageAreas[0];\n    }\n    pageArea[$extra] = {\n      numberOfUse: 1\n    };\n    const pageAreaParent = pageArea[$getParent]();\n    pageAreaParent[$extra] = {\n      numberOfUse: 1,\n      pageIndex: pageAreaParent.pageArea.children.indexOf(pageArea),\n      pageSetIndex: 0\n    };\n    let targetPageArea;\n    let leader = null;\n    let trailer = null;\n    let hasSomething = true;\n    let hasSomethingCounter = 0;\n    let startIndex = 0;\n    while (true) {\n      if (!hasSomething) {\n        mainHtml.children.pop();\n        if (++hasSomethingCounter === MAX_EMPTY_PAGES) {\n          warn(\"XFA - Something goes wrong: please file a bug.\");\n          return mainHtml;\n        }\n      } else {\n        hasSomethingCounter = 0;\n      }\n      targetPageArea = null;\n      this[$extra].currentPageArea = pageArea;\n      const page = pageArea[$toHTML]().html;\n      mainHtml.children.push(page);\n      if (leader) {\n        this[$extra].noLayoutFailure = true;\n        page.children.push(leader[$toHTML](pageArea[$extra].space).html);\n        leader = null;\n      }\n      if (trailer) {\n        this[$extra].noLayoutFailure = true;\n        page.children.push(trailer[$toHTML](pageArea[$extra].space).html);\n        trailer = null;\n      }\n      const contentAreas = pageArea.contentArea.children;\n      const htmlContentAreas = page.children.filter(node => node.attributes.class.includes(\"xfaContentarea\"));\n      hasSomething = false;\n      this[$extra].firstUnsplittable = null;\n      this[$extra].noLayoutFailure = false;\n      const flush = index => {\n        const html = root[$flushHTML]();\n        if (html) {\n          hasSomething ||= html.children?.length > 0;\n          htmlContentAreas[index].children.push(html);\n        }\n      };\n      for (let i = startIndex, ii = contentAreas.length; i < ii; i++) {\n        const contentArea = this[$extra].currentContentArea = contentAreas[i];\n        const space = {\n          width: contentArea.w,\n          height: contentArea.h\n        };\n        startIndex = 0;\n        if (leader) {\n          htmlContentAreas[i].children.push(leader[$toHTML](space).html);\n          leader = null;\n        }\n        if (trailer) {\n          htmlContentAreas[i].children.push(trailer[$toHTML](space).html);\n          trailer = null;\n        }\n        const html = root[$toHTML](space);\n        if (html.success) {\n          if (html.html) {\n            hasSomething ||= html.html.children?.length > 0;\n            htmlContentAreas[i].children.push(html.html);\n          } else if (!hasSomething && mainHtml.children.length > 1) {\n            mainHtml.children.pop();\n          }\n          return mainHtml;\n        }\n        if (html.isBreak()) {\n          const node = html.breakNode;\n          flush(i);\n          if (node.targetType === \"auto\") {\n            continue;\n          }\n          if (node.leader) {\n            leader = this[$searchNode](node.leader, node[$getParent]());\n            leader = leader ? leader[0] : null;\n          }\n          if (node.trailer) {\n            trailer = this[$searchNode](node.trailer, node[$getParent]());\n            trailer = trailer ? trailer[0] : null;\n          }\n          if (node.targetType === \"pageArea\") {\n            targetPageArea = node[$extra].target;\n            i = Infinity;\n          } else if (!node[$extra].target) {\n            i = node[$extra].index;\n          } else {\n            targetPageArea = node[$extra].target;\n            startIndex = node[$extra].index + 1;\n            i = Infinity;\n          }\n          continue;\n        }\n        if (this[$extra].overflowNode) {\n          const node = this[$extra].overflowNode;\n          this[$extra].overflowNode = null;\n          const overflowExtra = node[$getExtra]();\n          const target = overflowExtra.target;\n          overflowExtra.addLeader = overflowExtra.leader !== null;\n          overflowExtra.addTrailer = overflowExtra.trailer !== null;\n          flush(i);\n          const currentIndex = i;\n          i = Infinity;\n          if (target instanceof PageArea) {\n            targetPageArea = target;\n          } else if (target instanceof ContentArea) {\n            const index = contentAreas.indexOf(target);\n            if (index !== -1) {\n              if (index > currentIndex) {\n                i = index - 1;\n              } else {\n                startIndex = index;\n              }\n            } else {\n              targetPageArea = target[$getParent]();\n              startIndex = targetPageArea.contentArea.children.indexOf(target);\n            }\n          }\n          continue;\n        }\n        flush(i);\n      }\n      this[$extra].pageNumber += 1;\n      if (targetPageArea) {\n        if (targetPageArea[$isUsable]()) {\n          targetPageArea[$extra].numberOfUse += 1;\n        } else {\n          targetPageArea = null;\n        }\n      }\n      pageArea = targetPageArea || pageArea[$getNextPage]();\n      yield null;\n    }\n  }\n}\nclass Text extends ContentObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"text\");\n    this.id = attributes.id || \"\";\n    this.maxChars = getInteger({\n      data: attributes.maxChars,\n      defaultValue: 0,\n      validate: x => x >= 0\n    });\n    this.name = attributes.name || \"\";\n    this.rid = attributes.rid || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$acceptWhitespace]() {\n    return true;\n  }\n  [$onChild](child) {\n    if (child[$namespaceId] === NamespaceIds.xhtml.id) {\n      this[$content] = child;\n      return true;\n    }\n    warn(`XFA - Invalid content in Text: ${child[$nodeName]}.`);\n    return false;\n  }\n  [$onText](str) {\n    if (this[$content] instanceof XFAObject) {\n      return;\n    }\n    super[$onText](str);\n  }\n  [$finalize]() {\n    if (typeof this[$content] === \"string\") {\n      this[$content] = this[$content].replaceAll(\"\\r\\n\", \"\\n\");\n    }\n  }\n  [$getExtra]() {\n    if (typeof this[$content] === \"string\") {\n      return this[$content].split(/[\\u2029\\u2028\\n]/).reduce((acc, line) => {\n        if (line) {\n          acc.push(line);\n        }\n        return acc;\n      }, []).join(\"\\n\");\n    }\n    return this[$content][$text]();\n  }\n  [$toHTML](availableSpace) {\n    if (typeof this[$content] === \"string\") {\n      const html = valueToHtml(this[$content]).html;\n      if (this[$content].includes(\"\\u2029\")) {\n        html.name = \"div\";\n        html.children = [];\n        this[$content].split(\"\\u2029\").map(para => para.split(/[\\u2028\\n]/).reduce((acc, line) => {\n          acc.push({\n            name: \"span\",\n            value: line\n          }, {\n            name: \"br\"\n          });\n          return acc;\n        }, [])).forEach(lines => {\n          html.children.push({\n            name: \"p\",\n            children: lines\n          });\n        });\n      } else if (/[\\u2028\\n]/.test(this[$content])) {\n        html.name = \"div\";\n        html.children = [];\n        this[$content].split(/[\\u2028\\n]/).forEach(line => {\n          html.children.push({\n            name: \"span\",\n            value: line\n          }, {\n            name: \"br\"\n          });\n        });\n      }\n      return HTMLResult.success(html);\n    }\n    return this[$content][$toHTML](availableSpace);\n  }\n}\nclass TextEdit extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"textEdit\", true);\n    this.allowRichText = getInteger({\n      data: attributes.allowRichText,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.hScrollPolicy = getStringOption(attributes.hScrollPolicy, [\"auto\", \"off\", \"on\"]);\n    this.id = attributes.id || \"\";\n    this.multiLine = getInteger({\n      data: attributes.multiLine,\n      defaultValue: \"\",\n      validate: x => x === 0 || x === 1\n    });\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.vScrollPolicy = getStringOption(attributes.vScrollPolicy, [\"auto\", \"off\", \"on\"]);\n    this.border = null;\n    this.comb = null;\n    this.extras = null;\n    this.margin = null;\n  }\n  [$toHTML](availableSpace) {\n    const style = toStyle(this, \"border\", \"font\", \"margin\");\n    let html;\n    const field = this[$getParent]()[$getParent]();\n    if (this.multiLine === \"\") {\n      this.multiLine = field instanceof Draw ? 1 : 0;\n    }\n    if (this.multiLine === 1) {\n      html = {\n        name: \"textarea\",\n        attributes: {\n          dataId: field[$data]?.[$uid] || field[$uid],\n          fieldId: field[$uid],\n          class: [\"xfaTextfield\"],\n          style,\n          \"aria-label\": ariaLabel(field),\n          \"aria-required\": false\n        }\n      };\n    } else {\n      html = {\n        name: \"input\",\n        attributes: {\n          type: \"text\",\n          dataId: field[$data]?.[$uid] || field[$uid],\n          fieldId: field[$uid],\n          class: [\"xfaTextfield\"],\n          style,\n          \"aria-label\": ariaLabel(field),\n          \"aria-required\": false\n        }\n      };\n    }\n    if (isRequired(field)) {\n      html.attributes[\"aria-required\"] = true;\n      html.attributes.required = true;\n    }\n    return HTMLResult.success({\n      name: \"label\",\n      attributes: {\n        class: [\"xfaLabel\"]\n      },\n      children: [html]\n    });\n  }\n}\nclass Time extends StringObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"time\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n  [$finalize]() {\n    const date = this[$content].trim();\n    this[$content] = date ? new Date(date) : null;\n  }\n  [$toHTML](availableSpace) {\n    return valueToHtml(this[$content] ? this[$content].toString() : \"\");\n  }\n}\nclass TimeStamp extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"timeStamp\");\n    this.id = attributes.id || \"\";\n    this.server = attributes.server || \"\";\n    this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass ToolTip extends StringObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"toolTip\");\n    this.id = attributes.id || \"\";\n    this.rid = attributes.rid || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Traversal extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"traversal\", true);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n    this.traverse = new XFAObjectArray();\n  }\n}\nclass Traverse extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"traverse\", true);\n    this.id = attributes.id || \"\";\n    this.operation = getStringOption(attributes.operation, [\"next\", \"back\", \"down\", \"first\", \"left\", \"right\", \"up\"]);\n    this.ref = attributes.ref || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n    this.script = null;\n  }\n  get name() {\n    return this.operation;\n  }\n  [$isTransparent]() {\n    return false;\n  }\n}\nclass Ui extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"ui\", true);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n    this.picture = null;\n    this.barcode = null;\n    this.button = null;\n    this.checkButton = null;\n    this.choiceList = null;\n    this.dateTimeEdit = null;\n    this.defaultUi = null;\n    this.imageEdit = null;\n    this.numericEdit = null;\n    this.passwordEdit = null;\n    this.signature = null;\n    this.textEdit = null;\n  }\n  [$getExtra]() {\n    if (this[$extra] === undefined) {\n      for (const name of Object.getOwnPropertyNames(this)) {\n        if (name === \"extras\" || name === \"picture\") {\n          continue;\n        }\n        const obj = this[name];\n        if (!(obj instanceof XFAObject)) {\n          continue;\n        }\n        this[$extra] = obj;\n        return obj;\n      }\n      this[$extra] = null;\n    }\n    return this[$extra];\n  }\n  [$toHTML](availableSpace) {\n    const obj = this[$getExtra]();\n    if (obj) {\n      return obj[$toHTML](availableSpace);\n    }\n    return HTMLResult.EMPTY;\n  }\n}\nclass Validate extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"validate\", true);\n    this.formatTest = getStringOption(attributes.formatTest, [\"warning\", \"disabled\", \"error\"]);\n    this.id = attributes.id || \"\";\n    this.nullTest = getStringOption(attributes.nullTest, [\"disabled\", \"error\", \"warning\"]);\n    this.scriptTest = getStringOption(attributes.scriptTest, [\"error\", \"disabled\", \"warning\"]);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.extras = null;\n    this.message = null;\n    this.picture = null;\n    this.script = null;\n  }\n}\nclass Value extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"value\", true);\n    this.id = attributes.id || \"\";\n    this.override = getInteger({\n      data: attributes.override,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.relevant = getRelevant(attributes.relevant);\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.arc = null;\n    this.boolean = null;\n    this.date = null;\n    this.dateTime = null;\n    this.decimal = null;\n    this.exData = null;\n    this.float = null;\n    this.image = null;\n    this.integer = null;\n    this.line = null;\n    this.rectangle = null;\n    this.text = null;\n    this.time = null;\n  }\n  [$setValue](value) {\n    const parent = this[$getParent]();\n    if (parent instanceof Field) {\n      if (parent.ui?.imageEdit) {\n        if (!this.image) {\n          this.image = new Image({});\n          this[$appendChild](this.image);\n        }\n        this.image[$content] = value[$content];\n        return;\n      }\n    }\n    const valueName = value[$nodeName];\n    if (this[valueName] !== null) {\n      this[valueName][$content] = value[$content];\n      return;\n    }\n    for (const name of Object.getOwnPropertyNames(this)) {\n      const obj = this[name];\n      if (obj instanceof XFAObject) {\n        this[name] = null;\n        this[$removeChild](obj);\n      }\n    }\n    this[value[$nodeName]] = value;\n    this[$appendChild](value);\n  }\n  [$text]() {\n    if (this.exData) {\n      if (typeof this.exData[$content] === \"string\") {\n        return this.exData[$content].trim();\n      }\n      return this.exData[$content][$text]().trim();\n    }\n    for (const name of Object.getOwnPropertyNames(this)) {\n      if (name === \"image\") {\n        continue;\n      }\n      const obj = this[name];\n      if (obj instanceof XFAObject) {\n        return (obj[$content] || \"\").toString().trim();\n      }\n    }\n    return null;\n  }\n  [$toHTML](availableSpace) {\n    for (const name of Object.getOwnPropertyNames(this)) {\n      const obj = this[name];\n      if (!(obj instanceof XFAObject)) {\n        continue;\n      }\n      return obj[$toHTML](availableSpace);\n    }\n    return HTMLResult.EMPTY;\n  }\n}\nclass Variables extends XFAObject {\n  constructor(attributes) {\n    super(TEMPLATE_NS_ID, \"variables\", true);\n    this.id = attributes.id || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n    this.boolean = new XFAObjectArray();\n    this.date = new XFAObjectArray();\n    this.dateTime = new XFAObjectArray();\n    this.decimal = new XFAObjectArray();\n    this.exData = new XFAObjectArray();\n    this.float = new XFAObjectArray();\n    this.image = new XFAObjectArray();\n    this.integer = new XFAObjectArray();\n    this.manifest = new XFAObjectArray();\n    this.script = new XFAObjectArray();\n    this.text = new XFAObjectArray();\n    this.time = new XFAObjectArray();\n  }\n  [$isTransparent]() {\n    return true;\n  }\n}\nclass TemplateNamespace {\n  static [$buildXFAObject](name, attributes) {\n    if (TemplateNamespace.hasOwnProperty(name)) {\n      const node = TemplateNamespace[name](attributes);\n      node[$setSetAttributes](attributes);\n      return node;\n    }\n    return undefined;\n  }\n  static appearanceFilter(attrs) {\n    return new AppearanceFilter(attrs);\n  }\n  static arc(attrs) {\n    return new Arc(attrs);\n  }\n  static area(attrs) {\n    return new Area(attrs);\n  }\n  static assist(attrs) {\n    return new Assist(attrs);\n  }\n  static barcode(attrs) {\n    return new Barcode(attrs);\n  }\n  static bind(attrs) {\n    return new Bind(attrs);\n  }\n  static bindItems(attrs) {\n    return new BindItems(attrs);\n  }\n  static bookend(attrs) {\n    return new Bookend(attrs);\n  }\n  static boolean(attrs) {\n    return new BooleanElement(attrs);\n  }\n  static border(attrs) {\n    return new Border(attrs);\n  }\n  static break(attrs) {\n    return new Break(attrs);\n  }\n  static breakAfter(attrs) {\n    return new BreakAfter(attrs);\n  }\n  static breakBefore(attrs) {\n    return new BreakBefore(attrs);\n  }\n  static button(attrs) {\n    return new Button(attrs);\n  }\n  static calculate(attrs) {\n    return new Calculate(attrs);\n  }\n  static caption(attrs) {\n    return new Caption(attrs);\n  }\n  static certificate(attrs) {\n    return new Certificate(attrs);\n  }\n  static certificates(attrs) {\n    return new Certificates(attrs);\n  }\n  static checkButton(attrs) {\n    return new CheckButton(attrs);\n  }\n  static choiceList(attrs) {\n    return new ChoiceList(attrs);\n  }\n  static color(attrs) {\n    return new Color(attrs);\n  }\n  static comb(attrs) {\n    return new Comb(attrs);\n  }\n  static connect(attrs) {\n    return new Connect(attrs);\n  }\n  static contentArea(attrs) {\n    return new ContentArea(attrs);\n  }\n  static corner(attrs) {\n    return new Corner(attrs);\n  }\n  static date(attrs) {\n    return new DateElement(attrs);\n  }\n  static dateTime(attrs) {\n    return new DateTime(attrs);\n  }\n  static dateTimeEdit(attrs) {\n    return new DateTimeEdit(attrs);\n  }\n  static decimal(attrs) {\n    return new Decimal(attrs);\n  }\n  static defaultUi(attrs) {\n    return new DefaultUi(attrs);\n  }\n  static desc(attrs) {\n    return new Desc(attrs);\n  }\n  static digestMethod(attrs) {\n    return new DigestMethod(attrs);\n  }\n  static digestMethods(attrs) {\n    return new DigestMethods(attrs);\n  }\n  static draw(attrs) {\n    return new Draw(attrs);\n  }\n  static edge(attrs) {\n    return new Edge(attrs);\n  }\n  static encoding(attrs) {\n    return new Encoding(attrs);\n  }\n  static encodings(attrs) {\n    return new Encodings(attrs);\n  }\n  static encrypt(attrs) {\n    return new Encrypt(attrs);\n  }\n  static encryptData(attrs) {\n    return new EncryptData(attrs);\n  }\n  static encryption(attrs) {\n    return new Encryption(attrs);\n  }\n  static encryptionMethod(attrs) {\n    return new EncryptionMethod(attrs);\n  }\n  static encryptionMethods(attrs) {\n    return new EncryptionMethods(attrs);\n  }\n  static event(attrs) {\n    return new Event(attrs);\n  }\n  static exData(attrs) {\n    return new ExData(attrs);\n  }\n  static exObject(attrs) {\n    return new ExObject(attrs);\n  }\n  static exclGroup(attrs) {\n    return new ExclGroup(attrs);\n  }\n  static execute(attrs) {\n    return new Execute(attrs);\n  }\n  static extras(attrs) {\n    return new Extras(attrs);\n  }\n  static field(attrs) {\n    return new Field(attrs);\n  }\n  static fill(attrs) {\n    return new Fill(attrs);\n  }\n  static filter(attrs) {\n    return new Filter(attrs);\n  }\n  static float(attrs) {\n    return new Float(attrs);\n  }\n  static font(attrs) {\n    return new template_Font(attrs);\n  }\n  static format(attrs) {\n    return new Format(attrs);\n  }\n  static handler(attrs) {\n    return new Handler(attrs);\n  }\n  static hyphenation(attrs) {\n    return new Hyphenation(attrs);\n  }\n  static image(attrs) {\n    return new Image(attrs);\n  }\n  static imageEdit(attrs) {\n    return new ImageEdit(attrs);\n  }\n  static integer(attrs) {\n    return new Integer(attrs);\n  }\n  static issuers(attrs) {\n    return new Issuers(attrs);\n  }\n  static items(attrs) {\n    return new Items(attrs);\n  }\n  static keep(attrs) {\n    return new Keep(attrs);\n  }\n  static keyUsage(attrs) {\n    return new KeyUsage(attrs);\n  }\n  static line(attrs) {\n    return new Line(attrs);\n  }\n  static linear(attrs) {\n    return new Linear(attrs);\n  }\n  static lockDocument(attrs) {\n    return new LockDocument(attrs);\n  }\n  static manifest(attrs) {\n    return new Manifest(attrs);\n  }\n  static margin(attrs) {\n    return new Margin(attrs);\n  }\n  static mdp(attrs) {\n    return new Mdp(attrs);\n  }\n  static medium(attrs) {\n    return new Medium(attrs);\n  }\n  static message(attrs) {\n    return new Message(attrs);\n  }\n  static numericEdit(attrs) {\n    return new NumericEdit(attrs);\n  }\n  static occur(attrs) {\n    return new Occur(attrs);\n  }\n  static oid(attrs) {\n    return new Oid(attrs);\n  }\n  static oids(attrs) {\n    return new Oids(attrs);\n  }\n  static overflow(attrs) {\n    return new Overflow(attrs);\n  }\n  static pageArea(attrs) {\n    return new PageArea(attrs);\n  }\n  static pageSet(attrs) {\n    return new PageSet(attrs);\n  }\n  static para(attrs) {\n    return new Para(attrs);\n  }\n  static passwordEdit(attrs) {\n    return new PasswordEdit(attrs);\n  }\n  static pattern(attrs) {\n    return new template_Pattern(attrs);\n  }\n  static picture(attrs) {\n    return new Picture(attrs);\n  }\n  static proto(attrs) {\n    return new Proto(attrs);\n  }\n  static radial(attrs) {\n    return new Radial(attrs);\n  }\n  static reason(attrs) {\n    return new Reason(attrs);\n  }\n  static reasons(attrs) {\n    return new Reasons(attrs);\n  }\n  static rectangle(attrs) {\n    return new Rectangle(attrs);\n  }\n  static ref(attrs) {\n    return new RefElement(attrs);\n  }\n  static script(attrs) {\n    return new Script(attrs);\n  }\n  static setProperty(attrs) {\n    return new SetProperty(attrs);\n  }\n  static signData(attrs) {\n    return new SignData(attrs);\n  }\n  static signature(attrs) {\n    return new Signature(attrs);\n  }\n  static signing(attrs) {\n    return new Signing(attrs);\n  }\n  static solid(attrs) {\n    return new Solid(attrs);\n  }\n  static speak(attrs) {\n    return new Speak(attrs);\n  }\n  static stipple(attrs) {\n    return new Stipple(attrs);\n  }\n  static subform(attrs) {\n    return new Subform(attrs);\n  }\n  static subformSet(attrs) {\n    return new SubformSet(attrs);\n  }\n  static subjectDN(attrs) {\n    return new SubjectDN(attrs);\n  }\n  static subjectDNs(attrs) {\n    return new SubjectDNs(attrs);\n  }\n  static submit(attrs) {\n    return new Submit(attrs);\n  }\n  static template(attrs) {\n    return new Template(attrs);\n  }\n  static text(attrs) {\n    return new Text(attrs);\n  }\n  static textEdit(attrs) {\n    return new TextEdit(attrs);\n  }\n  static time(attrs) {\n    return new Time(attrs);\n  }\n  static timeStamp(attrs) {\n    return new TimeStamp(attrs);\n  }\n  static toolTip(attrs) {\n    return new ToolTip(attrs);\n  }\n  static traversal(attrs) {\n    return new Traversal(attrs);\n  }\n  static traverse(attrs) {\n    return new Traverse(attrs);\n  }\n  static ui(attrs) {\n    return new Ui(attrs);\n  }\n  static validate(attrs) {\n    return new Validate(attrs);\n  }\n  static value(attrs) {\n    return new Value(attrs);\n  }\n  static variables(attrs) {\n    return new Variables(attrs);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/bind.js\n\n\n\n\n\n\nconst bind_NS_DATASETS = NamespaceIds.datasets.id;\nfunction createText(content) {\n  const node = new Text({});\n  node[$content] = content;\n  return node;\n}\nclass Binder {\n  constructor(root) {\n    this.root = root;\n    this.datasets = root.datasets;\n    this.data = root.datasets?.data || new XmlObject(NamespaceIds.datasets.id, \"data\");\n    this.emptyMerge = this.data[$getChildren]().length === 0;\n    this.root.form = this.form = root.template[$clone]();\n  }\n  _isConsumeData() {\n    return !this.emptyMerge && this._mergeMode;\n  }\n  _isMatchTemplate() {\n    return !this._isConsumeData();\n  }\n  bind() {\n    this._bindElement(this.form, this.data);\n    return this.form;\n  }\n  getData() {\n    return this.data;\n  }\n  _bindValue(formNode, data, picture) {\n    formNode[$data] = data;\n    if (formNode[$hasSettableValue]()) {\n      if (data[$isDataValue]()) {\n        const value = data[$getDataValue]();\n        formNode[$setValue](createText(value));\n      } else if (formNode instanceof Field && formNode.ui?.choiceList?.open === \"multiSelect\") {\n        const value = data[$getChildren]().map(child => child[$content].trim()).join(\"\\n\");\n        formNode[$setValue](createText(value));\n      } else if (this._isConsumeData()) {\n        warn(`XFA - Nodes haven't the same type.`);\n      }\n    } else if (!data[$isDataValue]() || this._isMatchTemplate()) {\n      this._bindElement(formNode, data);\n    } else {\n      warn(`XFA - Nodes haven't the same type.`);\n    }\n  }\n  _findDataByNameToConsume(name, isValue, dataNode, global) {\n    if (!name) {\n      return null;\n    }\n    let generator, match;\n    for (let i = 0; i < 3; i++) {\n      generator = dataNode[$getRealChildrenByNameIt](name, false, true);\n      while (true) {\n        match = generator.next().value;\n        if (!match) {\n          break;\n        }\n        if (isValue === match[$isDataValue]()) {\n          return match;\n        }\n      }\n      if (dataNode[$namespaceId] === NamespaceIds.datasets.id && dataNode[$nodeName] === \"data\") {\n        break;\n      }\n      dataNode = dataNode[$getParent]();\n    }\n    if (!global) {\n      return null;\n    }\n    generator = this.data[$getRealChildrenByNameIt](name, true, false);\n    match = generator.next().value;\n    if (match) {\n      return match;\n    }\n    generator = this.data[$getAttributeIt](name, true);\n    match = generator.next().value;\n    if (match?.[$isDataValue]()) {\n      return match;\n    }\n    return null;\n  }\n  _setProperties(formNode, dataNode) {\n    if (!formNode.hasOwnProperty(\"setProperty\")) {\n      return;\n    }\n    for (const {\n      ref,\n      target,\n      connection\n    } of formNode.setProperty.children) {\n      if (connection) {\n        continue;\n      }\n      if (!ref) {\n        continue;\n      }\n      const nodes = searchNode(this.root, dataNode, ref, false, false);\n      if (!nodes) {\n        warn(`XFA - Invalid reference: ${ref}.`);\n        continue;\n      }\n      const [node] = nodes;\n      if (!node[$isDescendent](this.data)) {\n        warn(`XFA - Invalid node: must be a data node.`);\n        continue;\n      }\n      const targetNodes = searchNode(this.root, formNode, target, false, false);\n      if (!targetNodes) {\n        warn(`XFA - Invalid target: ${target}.`);\n        continue;\n      }\n      const [targetNode] = targetNodes;\n      if (!targetNode[$isDescendent](formNode)) {\n        warn(`XFA - Invalid target: must be a property or subproperty.`);\n        continue;\n      }\n      const targetParent = targetNode[$getParent]();\n      if (targetNode instanceof SetProperty || targetParent instanceof SetProperty) {\n        warn(`XFA - Invalid target: cannot be a setProperty or one of its properties.`);\n        continue;\n      }\n      if (targetNode instanceof BindItems || targetParent instanceof BindItems) {\n        warn(`XFA - Invalid target: cannot be a bindItems or one of its properties.`);\n        continue;\n      }\n      const content = node[$text]();\n      const name = targetNode[$nodeName];\n      if (targetNode instanceof XFAAttribute) {\n        const attrs = Object.create(null);\n        attrs[name] = content;\n        const obj = Reflect.construct(Object.getPrototypeOf(targetParent).constructor, [attrs]);\n        targetParent[name] = obj[name];\n        continue;\n      }\n      if (!targetNode.hasOwnProperty($content)) {\n        warn(`XFA - Invalid node to use in setProperty`);\n        continue;\n      }\n      targetNode[$data] = node;\n      targetNode[$content] = content;\n      targetNode[$finalize]();\n    }\n  }\n  _bindItems(formNode, dataNode) {\n    if (!formNode.hasOwnProperty(\"items\") || !formNode.hasOwnProperty(\"bindItems\") || formNode.bindItems.isEmpty()) {\n      return;\n    }\n    for (const item of formNode.items.children) {\n      formNode[$removeChild](item);\n    }\n    formNode.items.clear();\n    const labels = new Items({});\n    const values = new Items({});\n    formNode[$appendChild](labels);\n    formNode.items.push(labels);\n    formNode[$appendChild](values);\n    formNode.items.push(values);\n    for (const {\n      ref,\n      labelRef,\n      valueRef,\n      connection\n    } of formNode.bindItems.children) {\n      if (connection) {\n        continue;\n      }\n      if (!ref) {\n        continue;\n      }\n      const nodes = searchNode(this.root, dataNode, ref, false, false);\n      if (!nodes) {\n        warn(`XFA - Invalid reference: ${ref}.`);\n        continue;\n      }\n      for (const node of nodes) {\n        if (!node[$isDescendent](this.datasets)) {\n          warn(`XFA - Invalid ref (${ref}): must be a datasets child.`);\n          continue;\n        }\n        const labelNodes = searchNode(this.root, node, labelRef, true, false);\n        if (!labelNodes) {\n          warn(`XFA - Invalid label: ${labelRef}.`);\n          continue;\n        }\n        const [labelNode] = labelNodes;\n        if (!labelNode[$isDescendent](this.datasets)) {\n          warn(`XFA - Invalid label: must be a datasets child.`);\n          continue;\n        }\n        const valueNodes = searchNode(this.root, node, valueRef, true, false);\n        if (!valueNodes) {\n          warn(`XFA - Invalid value: ${valueRef}.`);\n          continue;\n        }\n        const [valueNode] = valueNodes;\n        if (!valueNode[$isDescendent](this.datasets)) {\n          warn(`XFA - Invalid value: must be a datasets child.`);\n          continue;\n        }\n        const label = createText(labelNode[$text]());\n        const value = createText(valueNode[$text]());\n        labels[$appendChild](label);\n        labels.text.push(label);\n        values[$appendChild](value);\n        values.text.push(value);\n      }\n    }\n  }\n  _bindOccurrences(formNode, matches, picture) {\n    let baseClone;\n    if (matches.length > 1) {\n      baseClone = formNode[$clone]();\n      baseClone[$removeChild](baseClone.occur);\n      baseClone.occur = null;\n    }\n    this._bindValue(formNode, matches[0], picture);\n    this._setProperties(formNode, matches[0]);\n    this._bindItems(formNode, matches[0]);\n    if (matches.length === 1) {\n      return;\n    }\n    const parent = formNode[$getParent]();\n    const name = formNode[$nodeName];\n    const pos = parent[$indexOf](formNode);\n    for (let i = 1, ii = matches.length; i < ii; i++) {\n      const match = matches[i];\n      const clone = baseClone[$clone]();\n      parent[name].push(clone);\n      parent[$insertAt](pos + i, clone);\n      this._bindValue(clone, match, picture);\n      this._setProperties(clone, match);\n      this._bindItems(clone, match);\n    }\n  }\n  _createOccurrences(formNode) {\n    if (!this.emptyMerge) {\n      return;\n    }\n    const {\n      occur\n    } = formNode;\n    if (!occur || occur.initial <= 1) {\n      return;\n    }\n    const parent = formNode[$getParent]();\n    const name = formNode[$nodeName];\n    if (!(parent[name] instanceof XFAObjectArray)) {\n      return;\n    }\n    let currentNumber;\n    if (formNode.name) {\n      currentNumber = parent[name].children.filter(e => e.name === formNode.name).length;\n    } else {\n      currentNumber = parent[name].children.length;\n    }\n    const pos = parent[$indexOf](formNode) + 1;\n    const ii = occur.initial - currentNumber;\n    if (ii) {\n      const nodeClone = formNode[$clone]();\n      nodeClone[$removeChild](nodeClone.occur);\n      nodeClone.occur = null;\n      parent[name].push(nodeClone);\n      parent[$insertAt](pos, nodeClone);\n      for (let i = 1; i < ii; i++) {\n        const clone = nodeClone[$clone]();\n        parent[name].push(clone);\n        parent[$insertAt](pos + i, clone);\n      }\n    }\n  }\n  _getOccurInfo(formNode) {\n    const {\n      name,\n      occur\n    } = formNode;\n    if (!occur || !name) {\n      return [1, 1];\n    }\n    const max = occur.max === -1 ? Infinity : occur.max;\n    return [occur.min, max];\n  }\n  _setAndBind(formNode, dataNode) {\n    this._setProperties(formNode, dataNode);\n    this._bindItems(formNode, dataNode);\n    this._bindElement(formNode, dataNode);\n  }\n  _bindElement(formNode, dataNode) {\n    const uselessNodes = [];\n    this._createOccurrences(formNode);\n    for (const child of formNode[$getChildren]()) {\n      if (child[$data]) {\n        continue;\n      }\n      if (this._mergeMode === undefined && child[$nodeName] === \"subform\") {\n        this._mergeMode = child.mergeMode === \"consumeData\";\n        const dataChildren = dataNode[$getChildren]();\n        if (dataChildren.length > 0) {\n          this._bindOccurrences(child, [dataChildren[0]], null);\n        } else if (this.emptyMerge) {\n          const nsId = dataNode[$namespaceId] === bind_NS_DATASETS ? -1 : dataNode[$namespaceId];\n          const dataChild = child[$data] = new XmlObject(nsId, child.name || \"root\");\n          dataNode[$appendChild](dataChild);\n          this._bindElement(child, dataChild);\n        }\n        continue;\n      }\n      if (!child[$isBindable]()) {\n        continue;\n      }\n      let global = false;\n      let picture = null;\n      let ref = null;\n      let match = null;\n      if (child.bind) {\n        switch (child.bind.match) {\n          case \"none\":\n            this._setAndBind(child, dataNode);\n            continue;\n          case \"global\":\n            global = true;\n            break;\n          case \"dataRef\":\n            if (!child.bind.ref) {\n              warn(`XFA - ref is empty in node ${child[$nodeName]}.`);\n              this._setAndBind(child, dataNode);\n              continue;\n            }\n            ref = child.bind.ref;\n            break;\n          default:\n            break;\n        }\n        if (child.bind.picture) {\n          picture = child.bind.picture[$content];\n        }\n      }\n      const [min, max] = this._getOccurInfo(child);\n      if (ref) {\n        match = searchNode(this.root, dataNode, ref, true, false);\n        if (match === null) {\n          match = createDataNode(this.data, dataNode, ref);\n          if (!match) {\n            continue;\n          }\n          if (this._isConsumeData()) {\n            match[$consumed] = true;\n          }\n          this._setAndBind(child, match);\n          continue;\n        } else {\n          if (this._isConsumeData()) {\n            match = match.filter(node => !node[$consumed]);\n          }\n          if (match.length > max) {\n            match = match.slice(0, max);\n          } else if (match.length === 0) {\n            match = null;\n          }\n          if (match && this._isConsumeData()) {\n            match.forEach(node => {\n              node[$consumed] = true;\n            });\n          }\n        }\n      } else {\n        if (!child.name) {\n          this._setAndBind(child, dataNode);\n          continue;\n        }\n        if (this._isConsumeData()) {\n          const matches = [];\n          while (matches.length < max) {\n            const found = this._findDataByNameToConsume(child.name, child[$hasSettableValue](), dataNode, global);\n            if (!found) {\n              break;\n            }\n            found[$consumed] = true;\n            matches.push(found);\n          }\n          match = matches.length > 0 ? matches : null;\n        } else {\n          match = dataNode[$getRealChildrenByNameIt](child.name, false, this.emptyMerge).next().value;\n          if (!match) {\n            if (min === 0) {\n              uselessNodes.push(child);\n              continue;\n            }\n            const nsId = dataNode[$namespaceId] === bind_NS_DATASETS ? -1 : dataNode[$namespaceId];\n            match = child[$data] = new XmlObject(nsId, child.name);\n            if (this.emptyMerge) {\n              match[$consumed] = true;\n            }\n            dataNode[$appendChild](match);\n            this._setAndBind(child, match);\n            continue;\n          }\n          if (this.emptyMerge) {\n            match[$consumed] = true;\n          }\n          match = [match];\n        }\n      }\n      if (match) {\n        this._bindOccurrences(child, match, picture);\n      } else if (min > 0) {\n        this._setAndBind(child, dataNode);\n      } else {\n        uselessNodes.push(child);\n      }\n    }\n    uselessNodes.forEach(node => node[$getParent]()[$removeChild](node));\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/data.js\n\nclass DataHandler {\n  constructor(root, data) {\n    this.data = data;\n    this.dataset = root.datasets || null;\n  }\n  serialize(storage) {\n    const stack = [[-1, this.data[$getChildren]()]];\n    while (stack.length > 0) {\n      const last = stack.at(-1);\n      const [i, children] = last;\n      if (i + 1 === children.length) {\n        stack.pop();\n        continue;\n      }\n      const child = children[++last[0]];\n      const storageEntry = storage.get(child[$uid]);\n      if (storageEntry) {\n        child[$setValue](storageEntry);\n      } else {\n        const attributes = child[$getAttributes]();\n        for (const value of attributes.values()) {\n          const entry = storage.get(value[$uid]);\n          if (entry) {\n            value[$setValue](entry);\n            break;\n          }\n        }\n      }\n      const nodes = child[$getChildren]();\n      if (nodes.length > 0) {\n        stack.push([-1, nodes]);\n      }\n    }\n    const buf = [`<xfa:datasets xmlns:xfa=\"http://www.xfa.org/schema/xfa-data/1.0/\">`];\n    if (this.dataset) {\n      for (const child of this.dataset[$getChildren]()) {\n        if (child[$nodeName] !== \"data\") {\n          child[$toString](buf);\n        }\n      }\n    }\n    this.data[$toString](buf);\n    buf.push(\"</xfa:datasets>\");\n    return buf.join(\"\");\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/config.js\n\n\n\n\n\nconst CONFIG_NS_ID = NamespaceIds.config.id;\nclass Acrobat extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"acrobat\", true);\n    this.acrobat7 = null;\n    this.autoSave = null;\n    this.common = null;\n    this.validate = null;\n    this.validateApprovalSignatures = null;\n    this.submitUrl = new XFAObjectArray();\n  }\n}\nclass Acrobat7 extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"acrobat7\", true);\n    this.dynamicRender = null;\n  }\n}\nclass ADBE_JSConsole extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"ADBE_JSConsole\", [\"delegate\", \"Enable\", \"Disable\"]);\n  }\n}\nclass ADBE_JSDebugger extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"ADBE_JSDebugger\", [\"delegate\", \"Enable\", \"Disable\"]);\n  }\n}\nclass AddSilentPrint extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"addSilentPrint\");\n  }\n}\nclass AddViewerPreferences extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"addViewerPreferences\");\n  }\n}\nclass AdjustData extends Option10 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"adjustData\");\n  }\n}\nclass AdobeExtensionLevel extends IntegerObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"adobeExtensionLevel\", 0, n => n >= 1 && n <= 8);\n  }\n}\nclass Agent extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"agent\", true);\n    this.name = attributes.name ? attributes.name.trim() : \"\";\n    this.common = new XFAObjectArray();\n  }\n}\nclass AlwaysEmbed extends ContentObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"alwaysEmbed\");\n  }\n}\nclass Amd extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"amd\");\n  }\n}\nclass config_Area extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"area\");\n    this.level = getInteger({\n      data: attributes.level,\n      defaultValue: 0,\n      validate: n => n >= 1 && n <= 3\n    });\n    this.name = getStringOption(attributes.name, [\"\", \"barcode\", \"coreinit\", \"deviceDriver\", \"font\", \"general\", \"layout\", \"merge\", \"script\", \"signature\", \"sourceSet\", \"templateCache\"]);\n  }\n}\nclass Attributes extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"attributes\", [\"preserve\", \"delegate\", \"ignore\"]);\n  }\n}\nclass AutoSave extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"autoSave\", [\"disabled\", \"enabled\"]);\n  }\n}\nclass Base extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"base\");\n  }\n}\nclass BatchOutput extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"batchOutput\");\n    this.format = getStringOption(attributes.format, [\"none\", \"concat\", \"zip\", \"zipCompress\"]);\n  }\n}\nclass BehaviorOverride extends ContentObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"behaviorOverride\");\n  }\n  [$finalize]() {\n    this[$content] = new Map(this[$content].trim().split(/\\s+/).filter(x => x.includes(\":\")).map(x => x.split(\":\", 2)));\n  }\n}\nclass Cache extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"cache\", true);\n    this.templateCache = null;\n  }\n}\nclass Change extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"change\");\n  }\n}\nclass Common extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"common\", true);\n    this.data = null;\n    this.locale = null;\n    this.localeSet = null;\n    this.messaging = null;\n    this.suppressBanner = null;\n    this.template = null;\n    this.validationMessaging = null;\n    this.versionControl = null;\n    this.log = new XFAObjectArray();\n  }\n}\nclass Compress extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"compress\");\n    this.scope = getStringOption(attributes.scope, [\"imageOnly\", \"document\"]);\n  }\n}\nclass CompressLogicalStructure extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"compressLogicalStructure\");\n  }\n}\nclass CompressObjectStream extends Option10 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"compressObjectStream\");\n  }\n}\nclass Compression extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"compression\", true);\n    this.compressLogicalStructure = null;\n    this.compressObjectStream = null;\n    this.level = null;\n    this.type = null;\n  }\n}\nclass Config extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"config\", true);\n    this.acrobat = null;\n    this.present = null;\n    this.trace = null;\n    this.agent = new XFAObjectArray();\n  }\n}\nclass Conformance extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"conformance\", [\"A\", \"B\"]);\n  }\n}\nclass ContentCopy extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"contentCopy\");\n  }\n}\nclass Copies extends IntegerObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"copies\", 1, n => n >= 1);\n  }\n}\nclass Creator extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"creator\");\n  }\n}\nclass CurrentPage extends IntegerObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"currentPage\", 0, n => n >= 0);\n  }\n}\nclass Data extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"data\", true);\n    this.adjustData = null;\n    this.attributes = null;\n    this.incrementalLoad = null;\n    this.outputXSL = null;\n    this.range = null;\n    this.record = null;\n    this.startNode = null;\n    this.uri = null;\n    this.window = null;\n    this.xsl = null;\n    this.excludeNS = new XFAObjectArray();\n    this.transform = new XFAObjectArray();\n  }\n}\nclass Debug extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"debug\", true);\n    this.uri = null;\n  }\n}\nclass DefaultTypeface extends ContentObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"defaultTypeface\");\n    this.writingScript = getStringOption(attributes.writingScript, [\"*\", \"Arabic\", \"Cyrillic\", \"EastEuropeanRoman\", \"Greek\", \"Hebrew\", \"Japanese\", \"Korean\", \"Roman\", \"SimplifiedChinese\", \"Thai\", \"TraditionalChinese\", \"Vietnamese\"]);\n  }\n}\nclass Destination extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"destination\", [\"pdf\", \"pcl\", \"ps\", \"webClient\", \"zpl\"]);\n  }\n}\nclass DocumentAssembly extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"documentAssembly\");\n  }\n}\nclass Driver extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"driver\", true);\n    this.name = attributes.name ? attributes.name.trim() : \"\";\n    this.fontInfo = null;\n    this.xdc = null;\n  }\n}\nclass DuplexOption extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"duplexOption\", [\"simplex\", \"duplexFlipLongEdge\", \"duplexFlipShortEdge\"]);\n  }\n}\nclass DynamicRender extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"dynamicRender\", [\"forbidden\", \"required\"]);\n  }\n}\nclass Embed extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"embed\");\n  }\n}\nclass config_Encrypt extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"encrypt\");\n  }\n}\nclass config_Encryption extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"encryption\", true);\n    this.encrypt = null;\n    this.encryptionLevel = null;\n    this.permissions = null;\n  }\n}\nclass EncryptionLevel extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"encryptionLevel\", [\"40bit\", \"128bit\"]);\n  }\n}\nclass Enforce extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"enforce\");\n  }\n}\nclass Equate extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"equate\");\n    this.force = getInteger({\n      data: attributes.force,\n      defaultValue: 1,\n      validate: n => n === 0\n    });\n    this.from = attributes.from || \"\";\n    this.to = attributes.to || \"\";\n  }\n}\nclass EquateRange extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"equateRange\");\n    this.from = attributes.from || \"\";\n    this.to = attributes.to || \"\";\n    this._unicodeRange = attributes.unicodeRange || \"\";\n  }\n  get unicodeRange() {\n    const ranges = [];\n    const unicodeRegex = /U\\+([0-9a-fA-F]+)/;\n    const unicodeRange = this._unicodeRange;\n    for (let range of unicodeRange.split(\",\").map(x => x.trim()).filter(x => !!x)) {\n      range = range.split(\"-\", 2).map(x => {\n        const found = x.match(unicodeRegex);\n        if (!found) {\n          return 0;\n        }\n        return parseInt(found[1], 16);\n      });\n      if (range.length === 1) {\n        range.push(range[0]);\n      }\n      ranges.push(range);\n    }\n    return shadow(this, \"unicodeRange\", ranges);\n  }\n}\nclass Exclude extends ContentObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"exclude\");\n  }\n  [$finalize]() {\n    this[$content] = this[$content].trim().split(/\\s+/).filter(x => x && [\"calculate\", \"close\", \"enter\", \"exit\", \"initialize\", \"ready\", \"validate\"].includes(x));\n  }\n}\nclass ExcludeNS extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"excludeNS\");\n  }\n}\nclass FlipLabel extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"flipLabel\", [\"usePrinterSetting\", \"on\", \"off\"]);\n  }\n}\nclass config_FontInfo extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"fontInfo\", true);\n    this.embed = null;\n    this.map = null;\n    this.subsetBelow = null;\n    this.alwaysEmbed = new XFAObjectArray();\n    this.defaultTypeface = new XFAObjectArray();\n    this.neverEmbed = new XFAObjectArray();\n  }\n}\nclass FormFieldFilling extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"formFieldFilling\");\n  }\n}\nclass GroupParent extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"groupParent\");\n  }\n}\nclass IfEmpty extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"ifEmpty\", [\"dataValue\", \"dataGroup\", \"ignore\", \"remove\"]);\n  }\n}\nclass IncludeXDPContent extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"includeXDPContent\");\n  }\n}\nclass IncrementalLoad extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"incrementalLoad\", [\"none\", \"forwardOnly\"]);\n  }\n}\nclass IncrementalMerge extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"incrementalMerge\");\n  }\n}\nclass Interactive extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"interactive\");\n  }\n}\nclass Jog extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"jog\", [\"usePrinterSetting\", \"none\", \"pageSet\"]);\n  }\n}\nclass LabelPrinter extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"labelPrinter\", true);\n    this.name = getStringOption(attributes.name, [\"zpl\", \"dpl\", \"ipl\", \"tcpl\"]);\n    this.batchOutput = null;\n    this.flipLabel = null;\n    this.fontInfo = null;\n    this.xdc = null;\n  }\n}\nclass Layout extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"layout\", [\"paginate\", \"panel\"]);\n  }\n}\nclass Level extends IntegerObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"level\", 0, n => n > 0);\n  }\n}\nclass Linearized extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"linearized\");\n  }\n}\nclass Locale extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"locale\");\n  }\n}\nclass LocaleSet extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"localeSet\");\n  }\n}\nclass Log extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"log\", true);\n    this.mode = null;\n    this.threshold = null;\n    this.to = null;\n    this.uri = null;\n  }\n}\nclass MapElement extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"map\", true);\n    this.equate = new XFAObjectArray();\n    this.equateRange = new XFAObjectArray();\n  }\n}\nclass MediumInfo extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"mediumInfo\", true);\n    this.map = null;\n  }\n}\nclass config_Message extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"message\", true);\n    this.msgId = null;\n    this.severity = null;\n  }\n}\nclass Messaging extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"messaging\", true);\n    this.message = new XFAObjectArray();\n  }\n}\nclass Mode extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"mode\", [\"append\", \"overwrite\"]);\n  }\n}\nclass ModifyAnnots extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"modifyAnnots\");\n  }\n}\nclass MsgId extends IntegerObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"msgId\", 1, n => n >= 1);\n  }\n}\nclass NameAttr extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"nameAttr\");\n  }\n}\nclass NeverEmbed extends ContentObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"neverEmbed\");\n  }\n}\nclass NumberOfCopies extends IntegerObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"numberOfCopies\", null, n => n >= 2 && n <= 5);\n  }\n}\nclass OpenAction extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"openAction\", true);\n    this.destination = null;\n  }\n}\nclass Output extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"output\", true);\n    this.to = null;\n    this.type = null;\n    this.uri = null;\n  }\n}\nclass OutputBin extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"outputBin\");\n  }\n}\nclass OutputXSL extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"outputXSL\", true);\n    this.uri = null;\n  }\n}\nclass Overprint extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"overprint\", [\"none\", \"both\", \"draw\", \"field\"]);\n  }\n}\nclass Packets extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"packets\");\n  }\n  [$finalize]() {\n    if (this[$content] === \"*\") {\n      return;\n    }\n    this[$content] = this[$content].trim().split(/\\s+/).filter(x => [\"config\", \"datasets\", \"template\", \"xfdf\", \"xslt\"].includes(x));\n  }\n}\nclass PageOffset extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"pageOffset\");\n    this.x = getInteger({\n      data: attributes.x,\n      defaultValue: \"useXDCSetting\",\n      validate: n => true\n    });\n    this.y = getInteger({\n      data: attributes.y,\n      defaultValue: \"useXDCSetting\",\n      validate: n => true\n    });\n  }\n}\nclass PageRange extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"pageRange\");\n  }\n  [$finalize]() {\n    const numbers = this[$content].trim().split(/\\s+/).map(x => parseInt(x, 10));\n    const ranges = [];\n    for (let i = 0, ii = numbers.length; i < ii; i += 2) {\n      ranges.push(numbers.slice(i, i + 2));\n    }\n    this[$content] = ranges;\n  }\n}\nclass Pagination extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"pagination\", [\"simplex\", \"duplexShortEdge\", \"duplexLongEdge\"]);\n  }\n}\nclass PaginationOverride extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"paginationOverride\", [\"none\", \"forceDuplex\", \"forceDuplexLongEdge\", \"forceDuplexShortEdge\", \"forceSimplex\"]);\n  }\n}\nclass Part extends IntegerObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"part\", 1, n => false);\n  }\n}\nclass Pcl extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"pcl\", true);\n    this.name = attributes.name || \"\";\n    this.batchOutput = null;\n    this.fontInfo = null;\n    this.jog = null;\n    this.mediumInfo = null;\n    this.outputBin = null;\n    this.pageOffset = null;\n    this.staple = null;\n    this.xdc = null;\n  }\n}\nclass Pdf extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"pdf\", true);\n    this.name = attributes.name || \"\";\n    this.adobeExtensionLevel = null;\n    this.batchOutput = null;\n    this.compression = null;\n    this.creator = null;\n    this.encryption = null;\n    this.fontInfo = null;\n    this.interactive = null;\n    this.linearized = null;\n    this.openAction = null;\n    this.pdfa = null;\n    this.producer = null;\n    this.renderPolicy = null;\n    this.scriptModel = null;\n    this.silentPrint = null;\n    this.submitFormat = null;\n    this.tagged = null;\n    this.version = null;\n    this.viewerPreferences = null;\n    this.xdc = null;\n  }\n}\nclass Pdfa extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"pdfa\", true);\n    this.amd = null;\n    this.conformance = null;\n    this.includeXDPContent = null;\n    this.part = null;\n  }\n}\nclass Permissions extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"permissions\", true);\n    this.accessibleContent = null;\n    this.change = null;\n    this.contentCopy = null;\n    this.documentAssembly = null;\n    this.formFieldFilling = null;\n    this.modifyAnnots = null;\n    this.plaintextMetadata = null;\n    this.print = null;\n    this.printHighQuality = null;\n  }\n}\nclass PickTrayByPDFSize extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"pickTrayByPDFSize\");\n  }\n}\nclass config_Picture extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"picture\");\n  }\n}\nclass PlaintextMetadata extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"plaintextMetadata\");\n  }\n}\nclass Presence extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"presence\", [\"preserve\", \"dissolve\", \"dissolveStructure\", \"ignore\", \"remove\"]);\n  }\n}\nclass Present extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"present\", true);\n    this.behaviorOverride = null;\n    this.cache = null;\n    this.common = null;\n    this.copies = null;\n    this.destination = null;\n    this.incrementalMerge = null;\n    this.layout = null;\n    this.output = null;\n    this.overprint = null;\n    this.pagination = null;\n    this.paginationOverride = null;\n    this.script = null;\n    this.validate = null;\n    this.xdp = null;\n    this.driver = new XFAObjectArray();\n    this.labelPrinter = new XFAObjectArray();\n    this.pcl = new XFAObjectArray();\n    this.pdf = new XFAObjectArray();\n    this.ps = new XFAObjectArray();\n    this.submitUrl = new XFAObjectArray();\n    this.webClient = new XFAObjectArray();\n    this.zpl = new XFAObjectArray();\n  }\n}\nclass Print extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"print\");\n  }\n}\nclass PrintHighQuality extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"printHighQuality\");\n  }\n}\nclass PrintScaling extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"printScaling\", [\"appdefault\", \"noScaling\"]);\n  }\n}\nclass PrinterName extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"printerName\");\n  }\n}\nclass Producer extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"producer\");\n  }\n}\nclass Ps extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"ps\", true);\n    this.name = attributes.name || \"\";\n    this.batchOutput = null;\n    this.fontInfo = null;\n    this.jog = null;\n    this.mediumInfo = null;\n    this.outputBin = null;\n    this.staple = null;\n    this.xdc = null;\n  }\n}\nclass Range extends ContentObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"range\");\n  }\n  [$finalize]() {\n    this[$content] = this[$content].trim().split(/\\s*,\\s*/, 2).map(range => range.split(\"-\").map(x => parseInt(x.trim(), 10))).filter(range => range.every(x => !isNaN(x))).map(range => {\n      if (range.length === 1) {\n        range.push(range[0]);\n      }\n      return range;\n    });\n  }\n}\nclass Record extends ContentObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"record\");\n  }\n  [$finalize]() {\n    this[$content] = this[$content].trim();\n    const n = parseInt(this[$content], 10);\n    if (!isNaN(n) && n >= 0) {\n      this[$content] = n;\n    }\n  }\n}\nclass Relevant extends ContentObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"relevant\");\n  }\n  [$finalize]() {\n    this[$content] = this[$content].trim().split(/\\s+/);\n  }\n}\nclass Rename extends ContentObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"rename\");\n  }\n  [$finalize]() {\n    this[$content] = this[$content].trim();\n    if (this[$content].toLowerCase().startsWith(\"xml\") || new RegExp(\"[\\\\p{L}_][\\\\p{L}\\\\d._\\\\p{M}-]*\", \"u\").test(this[$content])) {\n      warn(\"XFA - Rename: invalid XFA name\");\n    }\n  }\n}\nclass RenderPolicy extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"renderPolicy\", [\"server\", \"client\"]);\n  }\n}\nclass RunScripts extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"runScripts\", [\"both\", \"client\", \"none\", \"server\"]);\n  }\n}\nclass config_Script extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"script\", true);\n    this.currentPage = null;\n    this.exclude = null;\n    this.runScripts = null;\n  }\n}\nclass ScriptModel extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"scriptModel\", [\"XFA\", \"none\"]);\n  }\n}\nclass Severity extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"severity\", [\"ignore\", \"error\", \"information\", \"trace\", \"warning\"]);\n  }\n}\nclass SilentPrint extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"silentPrint\", true);\n    this.addSilentPrint = null;\n    this.printerName = null;\n  }\n}\nclass Staple extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"staple\");\n    this.mode = getStringOption(attributes.mode, [\"usePrinterSetting\", \"on\", \"off\"]);\n  }\n}\nclass StartNode extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"startNode\");\n  }\n}\nclass StartPage extends IntegerObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"startPage\", 0, n => true);\n  }\n}\nclass SubmitFormat extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"submitFormat\", [\"html\", \"delegate\", \"fdf\", \"xml\", \"pdf\"]);\n  }\n}\nclass SubmitUrl extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"submitUrl\");\n  }\n}\nclass SubsetBelow extends IntegerObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"subsetBelow\", 100, n => n >= 0 && n <= 100);\n  }\n}\nclass SuppressBanner extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"suppressBanner\");\n  }\n}\nclass Tagged extends Option01 {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"tagged\");\n  }\n}\nclass config_Template extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"template\", true);\n    this.base = null;\n    this.relevant = null;\n    this.startPage = null;\n    this.uri = null;\n    this.xsl = null;\n  }\n}\nclass Threshold extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"threshold\", [\"trace\", \"error\", \"information\", \"warning\"]);\n  }\n}\nclass To extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"to\", [\"null\", \"memory\", \"stderr\", \"stdout\", \"system\", \"uri\"]);\n  }\n}\nclass TemplateCache extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"templateCache\");\n    this.maxEntries = getInteger({\n      data: attributes.maxEntries,\n      defaultValue: 5,\n      validate: n => n >= 0\n    });\n  }\n}\nclass Trace extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"trace\", true);\n    this.area = new XFAObjectArray();\n  }\n}\nclass config_Transform extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"transform\", true);\n    this.groupParent = null;\n    this.ifEmpty = null;\n    this.nameAttr = null;\n    this.picture = null;\n    this.presence = null;\n    this.rename = null;\n    this.whitespace = null;\n  }\n}\nclass Type extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"type\", [\"none\", \"ascii85\", \"asciiHex\", \"ccittfax\", \"flate\", \"lzw\", \"runLength\", \"native\", \"xdp\", \"mergedXDP\"]);\n  }\n}\nclass Uri extends StringObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"uri\");\n  }\n}\nclass config_Validate extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"validate\", [\"preSubmit\", \"prePrint\", \"preExecute\", \"preSave\"]);\n  }\n}\nclass ValidateApprovalSignatures extends ContentObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"validateApprovalSignatures\");\n  }\n  [$finalize]() {\n    this[$content] = this[$content].trim().split(/\\s+/).filter(x => [\"docReady\", \"postSign\"].includes(x));\n  }\n}\nclass ValidationMessaging extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"validationMessaging\", [\"allMessagesIndividually\", \"allMessagesTogether\", \"firstMessageOnly\", \"noMessages\"]);\n  }\n}\nclass Version extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"version\", [\"1.7\", \"1.6\", \"1.5\", \"1.4\", \"1.3\", \"1.2\"]);\n  }\n}\nclass VersionControl extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"VersionControl\");\n    this.outputBelow = getStringOption(attributes.outputBelow, [\"warn\", \"error\", \"update\"]);\n    this.sourceAbove = getStringOption(attributes.sourceAbove, [\"warn\", \"error\"]);\n    this.sourceBelow = getStringOption(attributes.sourceBelow, [\"update\", \"maintain\"]);\n  }\n}\nclass ViewerPreferences extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"viewerPreferences\", true);\n    this.ADBE_JSConsole = null;\n    this.ADBE_JSDebugger = null;\n    this.addViewerPreferences = null;\n    this.duplexOption = null;\n    this.enforce = null;\n    this.numberOfCopies = null;\n    this.pageRange = null;\n    this.pickTrayByPDFSize = null;\n    this.printScaling = null;\n  }\n}\nclass WebClient extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"webClient\", true);\n    this.name = attributes.name ? attributes.name.trim() : \"\";\n    this.fontInfo = null;\n    this.xdc = null;\n  }\n}\nclass Whitespace extends OptionObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"whitespace\", [\"preserve\", \"ltrim\", \"normalize\", \"rtrim\", \"trim\"]);\n  }\n}\nclass Window extends ContentObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"window\");\n  }\n  [$finalize]() {\n    const pair = this[$content].trim().split(/\\s*,\\s*/, 2).map(x => parseInt(x, 10));\n    if (pair.some(x => isNaN(x))) {\n      this[$content] = [0, 0];\n      return;\n    }\n    if (pair.length === 1) {\n      pair.push(pair[0]);\n    }\n    this[$content] = pair;\n  }\n}\nclass Xdc extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"xdc\", true);\n    this.uri = new XFAObjectArray();\n    this.xsl = new XFAObjectArray();\n  }\n}\nclass Xdp extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"xdp\", true);\n    this.packets = null;\n  }\n}\nclass Xsl extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"xsl\", true);\n    this.debug = null;\n    this.uri = null;\n  }\n}\nclass Zpl extends XFAObject {\n  constructor(attributes) {\n    super(CONFIG_NS_ID, \"zpl\", true);\n    this.name = attributes.name ? attributes.name.trim() : \"\";\n    this.batchOutput = null;\n    this.flipLabel = null;\n    this.fontInfo = null;\n    this.xdc = null;\n  }\n}\nclass ConfigNamespace {\n  static [$buildXFAObject](name, attributes) {\n    if (ConfigNamespace.hasOwnProperty(name)) {\n      return ConfigNamespace[name](attributes);\n    }\n    return undefined;\n  }\n  static acrobat(attrs) {\n    return new Acrobat(attrs);\n  }\n  static acrobat7(attrs) {\n    return new Acrobat7(attrs);\n  }\n  static ADBE_JSConsole(attrs) {\n    return new ADBE_JSConsole(attrs);\n  }\n  static ADBE_JSDebugger(attrs) {\n    return new ADBE_JSDebugger(attrs);\n  }\n  static addSilentPrint(attrs) {\n    return new AddSilentPrint(attrs);\n  }\n  static addViewerPreferences(attrs) {\n    return new AddViewerPreferences(attrs);\n  }\n  static adjustData(attrs) {\n    return new AdjustData(attrs);\n  }\n  static adobeExtensionLevel(attrs) {\n    return new AdobeExtensionLevel(attrs);\n  }\n  static agent(attrs) {\n    return new Agent(attrs);\n  }\n  static alwaysEmbed(attrs) {\n    return new AlwaysEmbed(attrs);\n  }\n  static amd(attrs) {\n    return new Amd(attrs);\n  }\n  static area(attrs) {\n    return new config_Area(attrs);\n  }\n  static attributes(attrs) {\n    return new Attributes(attrs);\n  }\n  static autoSave(attrs) {\n    return new AutoSave(attrs);\n  }\n  static base(attrs) {\n    return new Base(attrs);\n  }\n  static batchOutput(attrs) {\n    return new BatchOutput(attrs);\n  }\n  static behaviorOverride(attrs) {\n    return new BehaviorOverride(attrs);\n  }\n  static cache(attrs) {\n    return new Cache(attrs);\n  }\n  static change(attrs) {\n    return new Change(attrs);\n  }\n  static common(attrs) {\n    return new Common(attrs);\n  }\n  static compress(attrs) {\n    return new Compress(attrs);\n  }\n  static compressLogicalStructure(attrs) {\n    return new CompressLogicalStructure(attrs);\n  }\n  static compressObjectStream(attrs) {\n    return new CompressObjectStream(attrs);\n  }\n  static compression(attrs) {\n    return new Compression(attrs);\n  }\n  static config(attrs) {\n    return new Config(attrs);\n  }\n  static conformance(attrs) {\n    return new Conformance(attrs);\n  }\n  static contentCopy(attrs) {\n    return new ContentCopy(attrs);\n  }\n  static copies(attrs) {\n    return new Copies(attrs);\n  }\n  static creator(attrs) {\n    return new Creator(attrs);\n  }\n  static currentPage(attrs) {\n    return new CurrentPage(attrs);\n  }\n  static data(attrs) {\n    return new Data(attrs);\n  }\n  static debug(attrs) {\n    return new Debug(attrs);\n  }\n  static defaultTypeface(attrs) {\n    return new DefaultTypeface(attrs);\n  }\n  static destination(attrs) {\n    return new Destination(attrs);\n  }\n  static documentAssembly(attrs) {\n    return new DocumentAssembly(attrs);\n  }\n  static driver(attrs) {\n    return new Driver(attrs);\n  }\n  static duplexOption(attrs) {\n    return new DuplexOption(attrs);\n  }\n  static dynamicRender(attrs) {\n    return new DynamicRender(attrs);\n  }\n  static embed(attrs) {\n    return new Embed(attrs);\n  }\n  static encrypt(attrs) {\n    return new config_Encrypt(attrs);\n  }\n  static encryption(attrs) {\n    return new config_Encryption(attrs);\n  }\n  static encryptionLevel(attrs) {\n    return new EncryptionLevel(attrs);\n  }\n  static enforce(attrs) {\n    return new Enforce(attrs);\n  }\n  static equate(attrs) {\n    return new Equate(attrs);\n  }\n  static equateRange(attrs) {\n    return new EquateRange(attrs);\n  }\n  static exclude(attrs) {\n    return new Exclude(attrs);\n  }\n  static excludeNS(attrs) {\n    return new ExcludeNS(attrs);\n  }\n  static flipLabel(attrs) {\n    return new FlipLabel(attrs);\n  }\n  static fontInfo(attrs) {\n    return new config_FontInfo(attrs);\n  }\n  static formFieldFilling(attrs) {\n    return new FormFieldFilling(attrs);\n  }\n  static groupParent(attrs) {\n    return new GroupParent(attrs);\n  }\n  static ifEmpty(attrs) {\n    return new IfEmpty(attrs);\n  }\n  static includeXDPContent(attrs) {\n    return new IncludeXDPContent(attrs);\n  }\n  static incrementalLoad(attrs) {\n    return new IncrementalLoad(attrs);\n  }\n  static incrementalMerge(attrs) {\n    return new IncrementalMerge(attrs);\n  }\n  static interactive(attrs) {\n    return new Interactive(attrs);\n  }\n  static jog(attrs) {\n    return new Jog(attrs);\n  }\n  static labelPrinter(attrs) {\n    return new LabelPrinter(attrs);\n  }\n  static layout(attrs) {\n    return new Layout(attrs);\n  }\n  static level(attrs) {\n    return new Level(attrs);\n  }\n  static linearized(attrs) {\n    return new Linearized(attrs);\n  }\n  static locale(attrs) {\n    return new Locale(attrs);\n  }\n  static localeSet(attrs) {\n    return new LocaleSet(attrs);\n  }\n  static log(attrs) {\n    return new Log(attrs);\n  }\n  static map(attrs) {\n    return new MapElement(attrs);\n  }\n  static mediumInfo(attrs) {\n    return new MediumInfo(attrs);\n  }\n  static message(attrs) {\n    return new config_Message(attrs);\n  }\n  static messaging(attrs) {\n    return new Messaging(attrs);\n  }\n  static mode(attrs) {\n    return new Mode(attrs);\n  }\n  static modifyAnnots(attrs) {\n    return new ModifyAnnots(attrs);\n  }\n  static msgId(attrs) {\n    return new MsgId(attrs);\n  }\n  static nameAttr(attrs) {\n    return new NameAttr(attrs);\n  }\n  static neverEmbed(attrs) {\n    return new NeverEmbed(attrs);\n  }\n  static numberOfCopies(attrs) {\n    return new NumberOfCopies(attrs);\n  }\n  static openAction(attrs) {\n    return new OpenAction(attrs);\n  }\n  static output(attrs) {\n    return new Output(attrs);\n  }\n  static outputBin(attrs) {\n    return new OutputBin(attrs);\n  }\n  static outputXSL(attrs) {\n    return new OutputXSL(attrs);\n  }\n  static overprint(attrs) {\n    return new Overprint(attrs);\n  }\n  static packets(attrs) {\n    return new Packets(attrs);\n  }\n  static pageOffset(attrs) {\n    return new PageOffset(attrs);\n  }\n  static pageRange(attrs) {\n    return new PageRange(attrs);\n  }\n  static pagination(attrs) {\n    return new Pagination(attrs);\n  }\n  static paginationOverride(attrs) {\n    return new PaginationOverride(attrs);\n  }\n  static part(attrs) {\n    return new Part(attrs);\n  }\n  static pcl(attrs) {\n    return new Pcl(attrs);\n  }\n  static pdf(attrs) {\n    return new Pdf(attrs);\n  }\n  static pdfa(attrs) {\n    return new Pdfa(attrs);\n  }\n  static permissions(attrs) {\n    return new Permissions(attrs);\n  }\n  static pickTrayByPDFSize(attrs) {\n    return new PickTrayByPDFSize(attrs);\n  }\n  static picture(attrs) {\n    return new config_Picture(attrs);\n  }\n  static plaintextMetadata(attrs) {\n    return new PlaintextMetadata(attrs);\n  }\n  static presence(attrs) {\n    return new Presence(attrs);\n  }\n  static present(attrs) {\n    return new Present(attrs);\n  }\n  static print(attrs) {\n    return new Print(attrs);\n  }\n  static printHighQuality(attrs) {\n    return new PrintHighQuality(attrs);\n  }\n  static printScaling(attrs) {\n    return new PrintScaling(attrs);\n  }\n  static printerName(attrs) {\n    return new PrinterName(attrs);\n  }\n  static producer(attrs) {\n    return new Producer(attrs);\n  }\n  static ps(attrs) {\n    return new Ps(attrs);\n  }\n  static range(attrs) {\n    return new Range(attrs);\n  }\n  static record(attrs) {\n    return new Record(attrs);\n  }\n  static relevant(attrs) {\n    return new Relevant(attrs);\n  }\n  static rename(attrs) {\n    return new Rename(attrs);\n  }\n  static renderPolicy(attrs) {\n    return new RenderPolicy(attrs);\n  }\n  static runScripts(attrs) {\n    return new RunScripts(attrs);\n  }\n  static script(attrs) {\n    return new config_Script(attrs);\n  }\n  static scriptModel(attrs) {\n    return new ScriptModel(attrs);\n  }\n  static severity(attrs) {\n    return new Severity(attrs);\n  }\n  static silentPrint(attrs) {\n    return new SilentPrint(attrs);\n  }\n  static staple(attrs) {\n    return new Staple(attrs);\n  }\n  static startNode(attrs) {\n    return new StartNode(attrs);\n  }\n  static startPage(attrs) {\n    return new StartPage(attrs);\n  }\n  static submitFormat(attrs) {\n    return new SubmitFormat(attrs);\n  }\n  static submitUrl(attrs) {\n    return new SubmitUrl(attrs);\n  }\n  static subsetBelow(attrs) {\n    return new SubsetBelow(attrs);\n  }\n  static suppressBanner(attrs) {\n    return new SuppressBanner(attrs);\n  }\n  static tagged(attrs) {\n    return new Tagged(attrs);\n  }\n  static template(attrs) {\n    return new config_Template(attrs);\n  }\n  static templateCache(attrs) {\n    return new TemplateCache(attrs);\n  }\n  static threshold(attrs) {\n    return new Threshold(attrs);\n  }\n  static to(attrs) {\n    return new To(attrs);\n  }\n  static trace(attrs) {\n    return new Trace(attrs);\n  }\n  static transform(attrs) {\n    return new config_Transform(attrs);\n  }\n  static type(attrs) {\n    return new Type(attrs);\n  }\n  static uri(attrs) {\n    return new Uri(attrs);\n  }\n  static validate(attrs) {\n    return new config_Validate(attrs);\n  }\n  static validateApprovalSignatures(attrs) {\n    return new ValidateApprovalSignatures(attrs);\n  }\n  static validationMessaging(attrs) {\n    return new ValidationMessaging(attrs);\n  }\n  static version(attrs) {\n    return new Version(attrs);\n  }\n  static versionControl(attrs) {\n    return new VersionControl(attrs);\n  }\n  static viewerPreferences(attrs) {\n    return new ViewerPreferences(attrs);\n  }\n  static webClient(attrs) {\n    return new WebClient(attrs);\n  }\n  static whitespace(attrs) {\n    return new Whitespace(attrs);\n  }\n  static window(attrs) {\n    return new Window(attrs);\n  }\n  static xdc(attrs) {\n    return new Xdc(attrs);\n  }\n  static xdp(attrs) {\n    return new Xdp(attrs);\n  }\n  static xsl(attrs) {\n    return new Xsl(attrs);\n  }\n  static zpl(attrs) {\n    return new Zpl(attrs);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/connection_set.js\n\n\nconst CONNECTION_SET_NS_ID = NamespaceIds.connectionSet.id;\nclass ConnectionSet extends XFAObject {\n  constructor(attributes) {\n    super(CONNECTION_SET_NS_ID, \"connectionSet\", true);\n    this.wsdlConnection = new XFAObjectArray();\n    this.xmlConnection = new XFAObjectArray();\n    this.xsdConnection = new XFAObjectArray();\n  }\n}\nclass EffectiveInputPolicy extends XFAObject {\n  constructor(attributes) {\n    super(CONNECTION_SET_NS_ID, \"effectiveInputPolicy\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass EffectiveOutputPolicy extends XFAObject {\n  constructor(attributes) {\n    super(CONNECTION_SET_NS_ID, \"effectiveOutputPolicy\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass Operation extends StringObject {\n  constructor(attributes) {\n    super(CONNECTION_SET_NS_ID, \"operation\");\n    this.id = attributes.id || \"\";\n    this.input = attributes.input || \"\";\n    this.name = attributes.name || \"\";\n    this.output = attributes.output || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass RootElement extends StringObject {\n  constructor(attributes) {\n    super(CONNECTION_SET_NS_ID, \"rootElement\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass SoapAction extends StringObject {\n  constructor(attributes) {\n    super(CONNECTION_SET_NS_ID, \"soapAction\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass SoapAddress extends StringObject {\n  constructor(attributes) {\n    super(CONNECTION_SET_NS_ID, \"soapAddress\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass connection_set_Uri extends StringObject {\n  constructor(attributes) {\n    super(CONNECTION_SET_NS_ID, \"uri\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass WsdlAddress extends StringObject {\n  constructor(attributes) {\n    super(CONNECTION_SET_NS_ID, \"wsdlAddress\");\n    this.id = attributes.id || \"\";\n    this.name = attributes.name || \"\";\n    this.use = attributes.use || \"\";\n    this.usehref = attributes.usehref || \"\";\n  }\n}\nclass WsdlConnection extends XFAObject {\n  constructor(attributes) {\n    super(CONNECTION_SET_NS_ID, \"wsdlConnection\", true);\n    this.dataDescription = attributes.dataDescription || \"\";\n    this.name = attributes.name || \"\";\n    this.effectiveInputPolicy = null;\n    this.effectiveOutputPolicy = null;\n    this.operation = null;\n    this.soapAction = null;\n    this.soapAddress = null;\n    this.wsdlAddress = null;\n  }\n}\nclass XmlConnection extends XFAObject {\n  constructor(attributes) {\n    super(CONNECTION_SET_NS_ID, \"xmlConnection\", true);\n    this.dataDescription = attributes.dataDescription || \"\";\n    this.name = attributes.name || \"\";\n    this.uri = null;\n  }\n}\nclass XsdConnection extends XFAObject {\n  constructor(attributes) {\n    super(CONNECTION_SET_NS_ID, \"xsdConnection\", true);\n    this.dataDescription = attributes.dataDescription || \"\";\n    this.name = attributes.name || \"\";\n    this.rootElement = null;\n    this.uri = null;\n  }\n}\nclass ConnectionSetNamespace {\n  static [$buildXFAObject](name, attributes) {\n    if (ConnectionSetNamespace.hasOwnProperty(name)) {\n      return ConnectionSetNamespace[name](attributes);\n    }\n    return undefined;\n  }\n  static connectionSet(attrs) {\n    return new ConnectionSet(attrs);\n  }\n  static effectiveInputPolicy(attrs) {\n    return new EffectiveInputPolicy(attrs);\n  }\n  static effectiveOutputPolicy(attrs) {\n    return new EffectiveOutputPolicy(attrs);\n  }\n  static operation(attrs) {\n    return new Operation(attrs);\n  }\n  static rootElement(attrs) {\n    return new RootElement(attrs);\n  }\n  static soapAction(attrs) {\n    return new SoapAction(attrs);\n  }\n  static soapAddress(attrs) {\n    return new SoapAddress(attrs);\n  }\n  static uri(attrs) {\n    return new connection_set_Uri(attrs);\n  }\n  static wsdlAddress(attrs) {\n    return new WsdlAddress(attrs);\n  }\n  static wsdlConnection(attrs) {\n    return new WsdlConnection(attrs);\n  }\n  static xmlConnection(attrs) {\n    return new XmlConnection(attrs);\n  }\n  static xsdConnection(attrs) {\n    return new XsdConnection(attrs);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/datasets.js\n\n\n\nconst DATASETS_NS_ID = NamespaceIds.datasets.id;\nclass datasets_Data extends XmlObject {\n  constructor(attributes) {\n    super(DATASETS_NS_ID, \"data\", attributes);\n  }\n  [$isNsAgnostic]() {\n    return true;\n  }\n}\nclass Datasets extends XFAObject {\n  constructor(attributes) {\n    super(DATASETS_NS_ID, \"datasets\", true);\n    this.data = null;\n    this.Signature = null;\n  }\n  [$onChild](child) {\n    const name = child[$nodeName];\n    if (name === \"data\" && child[$namespaceId] === DATASETS_NS_ID || name === \"Signature\" && child[$namespaceId] === NamespaceIds.signature.id) {\n      this[name] = child;\n    }\n    this[$appendChild](child);\n  }\n}\nclass DatasetsNamespace {\n  static [$buildXFAObject](name, attributes) {\n    if (DatasetsNamespace.hasOwnProperty(name)) {\n      return DatasetsNamespace[name](attributes);\n    }\n    return undefined;\n  }\n  static datasets(attributes) {\n    return new Datasets(attributes);\n  }\n  static data(attributes) {\n    return new datasets_Data(attributes);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/locale_set.js\n\n\n\nconst LOCALE_SET_NS_ID = NamespaceIds.localeSet.id;\nclass CalendarSymbols extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"calendarSymbols\", true);\n    this.name = \"gregorian\";\n    this.dayNames = new XFAObjectArray(2);\n    this.eraNames = null;\n    this.meridiemNames = null;\n    this.monthNames = new XFAObjectArray(2);\n  }\n}\nclass CurrencySymbol extends StringObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"currencySymbol\");\n    this.name = getStringOption(attributes.name, [\"symbol\", \"isoname\", \"decimal\"]);\n  }\n}\nclass CurrencySymbols extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"currencySymbols\", true);\n    this.currencySymbol = new XFAObjectArray(3);\n  }\n}\nclass DatePattern extends StringObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"datePattern\");\n    this.name = getStringOption(attributes.name, [\"full\", \"long\", \"med\", \"short\"]);\n  }\n}\nclass DatePatterns extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"datePatterns\", true);\n    this.datePattern = new XFAObjectArray(4);\n  }\n}\nclass DateTimeSymbols extends ContentObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"dateTimeSymbols\");\n  }\n}\nclass Day extends StringObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"day\");\n  }\n}\nclass DayNames extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"dayNames\", true);\n    this.abbr = getInteger({\n      data: attributes.abbr,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.day = new XFAObjectArray(7);\n  }\n}\nclass Era extends StringObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"era\");\n  }\n}\nclass EraNames extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"eraNames\", true);\n    this.era = new XFAObjectArray(2);\n  }\n}\nclass locale_set_Locale extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"locale\", true);\n    this.desc = attributes.desc || \"\";\n    this.name = \"isoname\";\n    this.calendarSymbols = null;\n    this.currencySymbols = null;\n    this.datePatterns = null;\n    this.dateTimeSymbols = null;\n    this.numberPatterns = null;\n    this.numberSymbols = null;\n    this.timePatterns = null;\n    this.typeFaces = null;\n  }\n}\nclass locale_set_LocaleSet extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"localeSet\", true);\n    this.locale = new XFAObjectArray();\n  }\n}\nclass Meridiem extends StringObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"meridiem\");\n  }\n}\nclass MeridiemNames extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"meridiemNames\", true);\n    this.meridiem = new XFAObjectArray(2);\n  }\n}\nclass Month extends StringObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"month\");\n  }\n}\nclass MonthNames extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"monthNames\", true);\n    this.abbr = getInteger({\n      data: attributes.abbr,\n      defaultValue: 0,\n      validate: x => x === 1\n    });\n    this.month = new XFAObjectArray(12);\n  }\n}\nclass NumberPattern extends StringObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"numberPattern\");\n    this.name = getStringOption(attributes.name, [\"full\", \"long\", \"med\", \"short\"]);\n  }\n}\nclass NumberPatterns extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"numberPatterns\", true);\n    this.numberPattern = new XFAObjectArray(4);\n  }\n}\nclass NumberSymbol extends StringObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"numberSymbol\");\n    this.name = getStringOption(attributes.name, [\"decimal\", \"grouping\", \"percent\", \"minus\", \"zero\"]);\n  }\n}\nclass NumberSymbols extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"numberSymbols\", true);\n    this.numberSymbol = new XFAObjectArray(5);\n  }\n}\nclass TimePattern extends StringObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"timePattern\");\n    this.name = getStringOption(attributes.name, [\"full\", \"long\", \"med\", \"short\"]);\n  }\n}\nclass TimePatterns extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"timePatterns\", true);\n    this.timePattern = new XFAObjectArray(4);\n  }\n}\nclass TypeFace extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"typeFace\", true);\n    this.name = attributes.name | \"\";\n  }\n}\nclass TypeFaces extends XFAObject {\n  constructor(attributes) {\n    super(LOCALE_SET_NS_ID, \"typeFaces\", true);\n    this.typeFace = new XFAObjectArray();\n  }\n}\nclass LocaleSetNamespace {\n  static [$buildXFAObject](name, attributes) {\n    if (LocaleSetNamespace.hasOwnProperty(name)) {\n      return LocaleSetNamespace[name](attributes);\n    }\n    return undefined;\n  }\n  static calendarSymbols(attrs) {\n    return new CalendarSymbols(attrs);\n  }\n  static currencySymbol(attrs) {\n    return new CurrencySymbol(attrs);\n  }\n  static currencySymbols(attrs) {\n    return new CurrencySymbols(attrs);\n  }\n  static datePattern(attrs) {\n    return new DatePattern(attrs);\n  }\n  static datePatterns(attrs) {\n    return new DatePatterns(attrs);\n  }\n  static dateTimeSymbols(attrs) {\n    return new DateTimeSymbols(attrs);\n  }\n  static day(attrs) {\n    return new Day(attrs);\n  }\n  static dayNames(attrs) {\n    return new DayNames(attrs);\n  }\n  static era(attrs) {\n    return new Era(attrs);\n  }\n  static eraNames(attrs) {\n    return new EraNames(attrs);\n  }\n  static locale(attrs) {\n    return new locale_set_Locale(attrs);\n  }\n  static localeSet(attrs) {\n    return new locale_set_LocaleSet(attrs);\n  }\n  static meridiem(attrs) {\n    return new Meridiem(attrs);\n  }\n  static meridiemNames(attrs) {\n    return new MeridiemNames(attrs);\n  }\n  static month(attrs) {\n    return new Month(attrs);\n  }\n  static monthNames(attrs) {\n    return new MonthNames(attrs);\n  }\n  static numberPattern(attrs) {\n    return new NumberPattern(attrs);\n  }\n  static numberPatterns(attrs) {\n    return new NumberPatterns(attrs);\n  }\n  static numberSymbol(attrs) {\n    return new NumberSymbol(attrs);\n  }\n  static numberSymbols(attrs) {\n    return new NumberSymbols(attrs);\n  }\n  static timePattern(attrs) {\n    return new TimePattern(attrs);\n  }\n  static timePatterns(attrs) {\n    return new TimePatterns(attrs);\n  }\n  static typeFace(attrs) {\n    return new TypeFace(attrs);\n  }\n  static typeFaces(attrs) {\n    return new TypeFaces(attrs);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/signature.js\n\n\nconst SIGNATURE_NS_ID = NamespaceIds.signature.id;\nclass signature_Signature extends XFAObject {\n  constructor(attributes) {\n    super(SIGNATURE_NS_ID, \"signature\", true);\n  }\n}\nclass SignatureNamespace {\n  static [$buildXFAObject](name, attributes) {\n    if (SignatureNamespace.hasOwnProperty(name)) {\n      return SignatureNamespace[name](attributes);\n    }\n    return undefined;\n  }\n  static signature(attributes) {\n    return new signature_Signature(attributes);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/stylesheet.js\n\n\nconst STYLESHEET_NS_ID = NamespaceIds.stylesheet.id;\nclass Stylesheet extends XFAObject {\n  constructor(attributes) {\n    super(STYLESHEET_NS_ID, \"stylesheet\", true);\n  }\n}\nclass StylesheetNamespace {\n  static [$buildXFAObject](name, attributes) {\n    if (StylesheetNamespace.hasOwnProperty(name)) {\n      return StylesheetNamespace[name](attributes);\n    }\n    return undefined;\n  }\n  static stylesheet(attributes) {\n    return new Stylesheet(attributes);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/xdp.js\n\n\n\nconst XDP_NS_ID = NamespaceIds.xdp.id;\nclass xdp_Xdp extends XFAObject {\n  constructor(attributes) {\n    super(XDP_NS_ID, \"xdp\", true);\n    this.uuid = attributes.uuid || \"\";\n    this.timeStamp = attributes.timeStamp || \"\";\n    this.config = null;\n    this.connectionSet = null;\n    this.datasets = null;\n    this.localeSet = null;\n    this.stylesheet = new XFAObjectArray();\n    this.template = null;\n  }\n  [$onChildCheck](child) {\n    const ns = NamespaceIds[child[$nodeName]];\n    return ns && child[$namespaceId] === ns.id;\n  }\n}\nclass XdpNamespace {\n  static [$buildXFAObject](name, attributes) {\n    if (XdpNamespace.hasOwnProperty(name)) {\n      return XdpNamespace[name](attributes);\n    }\n    return undefined;\n  }\n  static xdp(attributes) {\n    return new xdp_Xdp(attributes);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/xhtml.js\n\n\n\n\n\nconst XHTML_NS_ID = NamespaceIds.xhtml.id;\nconst $richText = Symbol();\nconst VALID_STYLES = new Set([\"color\", \"font\", \"font-family\", \"font-size\", \"font-stretch\", \"font-style\", \"font-weight\", \"margin\", \"margin-bottom\", \"margin-left\", \"margin-right\", \"margin-top\", \"letter-spacing\", \"line-height\", \"orphans\", \"page-break-after\", \"page-break-before\", \"page-break-inside\", \"tab-interval\", \"tab-stop\", \"text-align\", \"text-decoration\", \"text-indent\", \"vertical-align\", \"widows\", \"kerning-mode\", \"xfa-font-horizontal-scale\", \"xfa-font-vertical-scale\", \"xfa-spacerun\", \"xfa-tab-stops\"]);\nconst StyleMapping = new Map([[\"page-break-after\", \"breakAfter\"], [\"page-break-before\", \"breakBefore\"], [\"page-break-inside\", \"breakInside\"], [\"kerning-mode\", value => value === \"none\" ? \"none\" : \"normal\"], [\"xfa-font-horizontal-scale\", value => `scaleX(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`], [\"xfa-font-vertical-scale\", value => `scaleY(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`], [\"xfa-spacerun\", \"\"], [\"xfa-tab-stops\", \"\"], [\"font-size\", (value, original) => {\n  value = original.fontSize = getMeasurement(value);\n  return measureToString(0.99 * value);\n}], [\"letter-spacing\", value => measureToString(getMeasurement(value))], [\"line-height\", value => measureToString(getMeasurement(value))], [\"margin\", value => measureToString(getMeasurement(value))], [\"margin-bottom\", value => measureToString(getMeasurement(value))], [\"margin-left\", value => measureToString(getMeasurement(value))], [\"margin-right\", value => measureToString(getMeasurement(value))], [\"margin-top\", value => measureToString(getMeasurement(value))], [\"text-indent\", value => measureToString(getMeasurement(value))], [\"font-family\", value => value], [\"vertical-align\", value => measureToString(getMeasurement(value))]]);\nconst spacesRegExp = /\\s+/g;\nconst crlfRegExp = /[\\r\\n]+/g;\nconst crlfForRichTextRegExp = /\\r\\n?/g;\nfunction mapStyle(styleStr, node, richText) {\n  const style = Object.create(null);\n  if (!styleStr) {\n    return style;\n  }\n  const original = Object.create(null);\n  for (const [key, value] of styleStr.split(\";\").map(s => s.split(\":\", 2))) {\n    const mapping = StyleMapping.get(key);\n    if (mapping === \"\") {\n      continue;\n    }\n    let newValue = value;\n    if (mapping) {\n      newValue = typeof mapping === \"string\" ? mapping : mapping(value, original);\n    }\n    if (key.endsWith(\"scale\")) {\n      style.transform = style.transform ? `${style[key]} ${newValue}` : newValue;\n    } else {\n      style[key.replaceAll(/-([a-zA-Z])/g, (_, x) => x.toUpperCase())] = newValue;\n    }\n  }\n  if (style.fontFamily) {\n    setFontFamily({\n      typeface: style.fontFamily,\n      weight: style.fontWeight || \"normal\",\n      posture: style.fontStyle || \"normal\",\n      size: original.fontSize || 0\n    }, node, node[$globalData].fontFinder, style);\n  }\n  if (richText && style.verticalAlign && style.verticalAlign !== \"0px\" && style.fontSize) {\n    const SUB_SUPER_SCRIPT_FACTOR = 0.583;\n    const VERTICAL_FACTOR = 0.333;\n    const fontSize = getMeasurement(style.fontSize);\n    style.fontSize = measureToString(fontSize * SUB_SUPER_SCRIPT_FACTOR);\n    style.verticalAlign = measureToString(Math.sign(getMeasurement(style.verticalAlign)) * fontSize * VERTICAL_FACTOR);\n  }\n  if (richText && style.fontSize) {\n    style.fontSize = `calc(${style.fontSize} * var(--scale-factor))`;\n  }\n  fixTextIndent(style);\n  return style;\n}\nfunction checkStyle(node) {\n  if (!node.style) {\n    return \"\";\n  }\n  return node.style.trim().split(/\\s*;\\s*/).filter(s => !!s).map(s => s.split(/\\s*:\\s*/, 2)).filter(([key, value]) => {\n    if (key === \"font-family\") {\n      node[$globalData].usedTypefaces.add(value);\n    }\n    return VALID_STYLES.has(key);\n  }).map(kv => kv.join(\":\")).join(\";\");\n}\nconst NoWhites = new Set([\"body\", \"html\"]);\nclass XhtmlObject extends XmlObject {\n  constructor(attributes, name) {\n    super(XHTML_NS_ID, name);\n    this[$richText] = false;\n    this.style = attributes.style || \"\";\n  }\n  [$clean](builder) {\n    super[$clean](builder);\n    this.style = checkStyle(this);\n  }\n  [$acceptWhitespace]() {\n    return !NoWhites.has(this[$nodeName]);\n  }\n  [$onText](str, richText = false) {\n    if (!richText) {\n      str = str.replaceAll(crlfRegExp, \"\");\n      if (!this.style.includes(\"xfa-spacerun:yes\")) {\n        str = str.replaceAll(spacesRegExp, \" \");\n      }\n    } else {\n      this[$richText] = true;\n    }\n    if (str) {\n      this[$content] += str;\n    }\n  }\n  [$pushGlyphs](measure, mustPop = true) {\n    const xfaFont = Object.create(null);\n    const margin = {\n      top: NaN,\n      bottom: NaN,\n      left: NaN,\n      right: NaN\n    };\n    let lineHeight = null;\n    for (const [key, value] of this.style.split(\";\").map(s => s.split(\":\", 2))) {\n      switch (key) {\n        case \"font-family\":\n          xfaFont.typeface = stripQuotes(value);\n          break;\n        case \"font-size\":\n          xfaFont.size = getMeasurement(value);\n          break;\n        case \"font-weight\":\n          xfaFont.weight = value;\n          break;\n        case \"font-style\":\n          xfaFont.posture = value;\n          break;\n        case \"letter-spacing\":\n          xfaFont.letterSpacing = getMeasurement(value);\n          break;\n        case \"margin\":\n          const values = value.split(/ \\t/).map(x => getMeasurement(x));\n          switch (values.length) {\n            case 1:\n              margin.top = margin.bottom = margin.left = margin.right = values[0];\n              break;\n            case 2:\n              margin.top = margin.bottom = values[0];\n              margin.left = margin.right = values[1];\n              break;\n            case 3:\n              margin.top = values[0];\n              margin.bottom = values[2];\n              margin.left = margin.right = values[1];\n              break;\n            case 4:\n              margin.top = values[0];\n              margin.left = values[1];\n              margin.bottom = values[2];\n              margin.right = values[3];\n              break;\n          }\n          break;\n        case \"margin-top\":\n          margin.top = getMeasurement(value);\n          break;\n        case \"margin-bottom\":\n          margin.bottom = getMeasurement(value);\n          break;\n        case \"margin-left\":\n          margin.left = getMeasurement(value);\n          break;\n        case \"margin-right\":\n          margin.right = getMeasurement(value);\n          break;\n        case \"line-height\":\n          lineHeight = getMeasurement(value);\n          break;\n      }\n    }\n    measure.pushData(xfaFont, margin, lineHeight);\n    if (this[$content]) {\n      measure.addString(this[$content]);\n    } else {\n      for (const child of this[$getChildren]()) {\n        if (child[$nodeName] === \"#text\") {\n          measure.addString(child[$content]);\n          continue;\n        }\n        child[$pushGlyphs](measure);\n      }\n    }\n    if (mustPop) {\n      measure.popFont();\n    }\n  }\n  [$toHTML](availableSpace) {\n    const children = [];\n    this[$extra] = {\n      children\n    };\n    this[$childrenToHTML]({});\n    if (children.length === 0 && !this[$content]) {\n      return HTMLResult.EMPTY;\n    }\n    let value;\n    if (this[$richText]) {\n      value = this[$content] ? this[$content].replaceAll(crlfForRichTextRegExp, \"\\n\") : undefined;\n    } else {\n      value = this[$content] || undefined;\n    }\n    return HTMLResult.success({\n      name: this[$nodeName],\n      attributes: {\n        href: this.href,\n        style: mapStyle(this.style, this, this[$richText])\n      },\n      children,\n      value\n    });\n  }\n}\nclass A extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"a\");\n    this.href = fixURL(attributes.href) || \"\";\n  }\n}\nclass B extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"b\");\n  }\n  [$pushGlyphs](measure) {\n    measure.pushFont({\n      weight: \"bold\"\n    });\n    super[$pushGlyphs](measure);\n    measure.popFont();\n  }\n}\nclass Body extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"body\");\n  }\n  [$toHTML](availableSpace) {\n    const res = super[$toHTML](availableSpace);\n    const {\n      html\n    } = res;\n    if (!html) {\n      return HTMLResult.EMPTY;\n    }\n    html.name = \"div\";\n    html.attributes.class = [\"xfaRich\"];\n    return res;\n  }\n}\nclass Br extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"br\");\n  }\n  [$text]() {\n    return \"\\n\";\n  }\n  [$pushGlyphs](measure) {\n    measure.addString(\"\\n\");\n  }\n  [$toHTML](availableSpace) {\n    return HTMLResult.success({\n      name: \"br\"\n    });\n  }\n}\nclass Html extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"html\");\n  }\n  [$toHTML](availableSpace) {\n    const children = [];\n    this[$extra] = {\n      children\n    };\n    this[$childrenToHTML]({});\n    if (children.length === 0) {\n      return HTMLResult.success({\n        name: \"div\",\n        attributes: {\n          class: [\"xfaRich\"],\n          style: {}\n        },\n        value: this[$content] || \"\"\n      });\n    }\n    if (children.length === 1) {\n      const child = children[0];\n      if (child.attributes?.class.includes(\"xfaRich\")) {\n        return HTMLResult.success(child);\n      }\n    }\n    return HTMLResult.success({\n      name: \"div\",\n      attributes: {\n        class: [\"xfaRich\"],\n        style: {}\n      },\n      children\n    });\n  }\n}\nclass I extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"i\");\n  }\n  [$pushGlyphs](measure) {\n    measure.pushFont({\n      posture: \"italic\"\n    });\n    super[$pushGlyphs](measure);\n    measure.popFont();\n  }\n}\nclass Li extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"li\");\n  }\n}\nclass Ol extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"ol\");\n  }\n}\nclass P extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"p\");\n  }\n  [$pushGlyphs](measure) {\n    super[$pushGlyphs](measure, false);\n    measure.addString(\"\\n\");\n    measure.addPara();\n    measure.popFont();\n  }\n  [$text]() {\n    const siblings = this[$getParent]()[$getChildren]();\n    if (siblings.at(-1) === this) {\n      return super[$text]();\n    }\n    return super[$text]() + \"\\n\";\n  }\n}\nclass Span extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"span\");\n  }\n}\nclass Sub extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"sub\");\n  }\n}\nclass Sup extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"sup\");\n  }\n}\nclass Ul extends XhtmlObject {\n  constructor(attributes) {\n    super(attributes, \"ul\");\n  }\n}\nclass XhtmlNamespace {\n  static [$buildXFAObject](name, attributes) {\n    if (XhtmlNamespace.hasOwnProperty(name)) {\n      return XhtmlNamespace[name](attributes);\n    }\n    return undefined;\n  }\n  static a(attributes) {\n    return new A(attributes);\n  }\n  static b(attributes) {\n    return new B(attributes);\n  }\n  static body(attributes) {\n    return new Body(attributes);\n  }\n  static br(attributes) {\n    return new Br(attributes);\n  }\n  static html(attributes) {\n    return new Html(attributes);\n  }\n  static i(attributes) {\n    return new I(attributes);\n  }\n  static li(attributes) {\n    return new Li(attributes);\n  }\n  static ol(attributes) {\n    return new Ol(attributes);\n  }\n  static p(attributes) {\n    return new P(attributes);\n  }\n  static span(attributes) {\n    return new Span(attributes);\n  }\n  static sub(attributes) {\n    return new Sub(attributes);\n  }\n  static sup(attributes) {\n    return new Sup(attributes);\n  }\n  static ul(attributes) {\n    return new Ul(attributes);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/setup.js\n\n\n\n\n\n\n\n\n\nconst NamespaceSetUp = {\n  config: ConfigNamespace,\n  connection: ConnectionSetNamespace,\n  datasets: DatasetsNamespace,\n  localeSet: LocaleSetNamespace,\n  signature: SignatureNamespace,\n  stylesheet: StylesheetNamespace,\n  template: TemplateNamespace,\n  xdp: XdpNamespace,\n  xhtml: XhtmlNamespace\n};\n\n;// CONCATENATED MODULE: ./src/core/xfa/unknown.js\n\n\nclass UnknownNamespace {\n  constructor(nsId) {\n    this.namespaceId = nsId;\n  }\n  [$buildXFAObject](name, attributes) {\n    return new XmlObject(this.namespaceId, name, attributes);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/builder.js\n\n\n\n\n\n\n\nclass Root extends XFAObject {\n  constructor(ids) {\n    super(-1, \"root\", Object.create(null));\n    this.element = null;\n    this[$ids] = ids;\n  }\n  [$onChild](child) {\n    this.element = child;\n    return true;\n  }\n  [$finalize]() {\n    super[$finalize]();\n    if (this.element.template instanceof Template) {\n      this[$ids].set($root, this.element);\n      this.element.template[$resolvePrototypes](this[$ids]);\n      this.element.template[$ids] = this[$ids];\n    }\n  }\n}\nclass Empty extends XFAObject {\n  constructor() {\n    super(-1, \"\", Object.create(null));\n  }\n  [$onChild](_) {\n    return false;\n  }\n}\nclass Builder {\n  constructor(rootNameSpace = null) {\n    this._namespaceStack = [];\n    this._nsAgnosticLevel = 0;\n    this._namespacePrefixes = new Map();\n    this._namespaces = new Map();\n    this._nextNsId = Math.max(...Object.values(NamespaceIds).map(({\n      id\n    }) => id));\n    this._currentNamespace = rootNameSpace || new UnknownNamespace(++this._nextNsId);\n  }\n  buildRoot(ids) {\n    return new Root(ids);\n  }\n  build({\n    nsPrefix,\n    name,\n    attributes,\n    namespace,\n    prefixes\n  }) {\n    const hasNamespaceDef = namespace !== null;\n    if (hasNamespaceDef) {\n      this._namespaceStack.push(this._currentNamespace);\n      this._currentNamespace = this._searchNamespace(namespace);\n    }\n    if (prefixes) {\n      this._addNamespacePrefix(prefixes);\n    }\n    if (attributes.hasOwnProperty($nsAttributes)) {\n      const dataTemplate = NamespaceSetUp.datasets;\n      const nsAttrs = attributes[$nsAttributes];\n      let xfaAttrs = null;\n      for (const [ns, attrs] of Object.entries(nsAttrs)) {\n        const nsToUse = this._getNamespaceToUse(ns);\n        if (nsToUse === dataTemplate) {\n          xfaAttrs = {\n            xfa: attrs\n          };\n          break;\n        }\n      }\n      if (xfaAttrs) {\n        attributes[$nsAttributes] = xfaAttrs;\n      } else {\n        delete attributes[$nsAttributes];\n      }\n    }\n    const namespaceToUse = this._getNamespaceToUse(nsPrefix);\n    const node = namespaceToUse?.[$buildXFAObject](name, attributes) || new Empty();\n    if (node[$isNsAgnostic]()) {\n      this._nsAgnosticLevel++;\n    }\n    if (hasNamespaceDef || prefixes || node[$isNsAgnostic]()) {\n      node[$cleanup] = {\n        hasNamespace: hasNamespaceDef,\n        prefixes,\n        nsAgnostic: node[$isNsAgnostic]()\n      };\n    }\n    return node;\n  }\n  isNsAgnostic() {\n    return this._nsAgnosticLevel > 0;\n  }\n  _searchNamespace(nsName) {\n    let ns = this._namespaces.get(nsName);\n    if (ns) {\n      return ns;\n    }\n    for (const [name, {\n      check\n    }] of Object.entries(NamespaceIds)) {\n      if (check(nsName)) {\n        ns = NamespaceSetUp[name];\n        if (ns) {\n          this._namespaces.set(nsName, ns);\n          return ns;\n        }\n        break;\n      }\n    }\n    ns = new UnknownNamespace(++this._nextNsId);\n    this._namespaces.set(nsName, ns);\n    return ns;\n  }\n  _addNamespacePrefix(prefixes) {\n    for (const {\n      prefix,\n      value\n    } of prefixes) {\n      const namespace = this._searchNamespace(value);\n      let prefixStack = this._namespacePrefixes.get(prefix);\n      if (!prefixStack) {\n        prefixStack = [];\n        this._namespacePrefixes.set(prefix, prefixStack);\n      }\n      prefixStack.push(namespace);\n    }\n  }\n  _getNamespaceToUse(prefix) {\n    if (!prefix) {\n      return this._currentNamespace;\n    }\n    const prefixStack = this._namespacePrefixes.get(prefix);\n    if (prefixStack?.length > 0) {\n      return prefixStack.at(-1);\n    }\n    warn(`Unknown namespace prefix: ${prefix}.`);\n    return null;\n  }\n  clean(data) {\n    const {\n      hasNamespace,\n      prefixes,\n      nsAgnostic\n    } = data;\n    if (hasNamespace) {\n      this._currentNamespace = this._namespaceStack.pop();\n    }\n    if (prefixes) {\n      prefixes.forEach(({\n        prefix\n      }) => {\n        this._namespacePrefixes.get(prefix).pop();\n      });\n    }\n    if (nsAgnostic) {\n      this._nsAgnosticLevel--;\n    }\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/parser.js\n\n\n\n\nclass XFAParser extends XMLParserBase {\n  constructor(rootNameSpace = null, richText = false) {\n    super();\n    this._builder = new Builder(rootNameSpace);\n    this._stack = [];\n    this._globalData = {\n      usedTypefaces: new Set()\n    };\n    this._ids = new Map();\n    this._current = this._builder.buildRoot(this._ids);\n    this._errorCode = XMLParserErrorCode.NoError;\n    this._whiteRegex = /^\\s+$/;\n    this._nbsps = /\\xa0+/g;\n    this._richText = richText;\n  }\n  parse(data) {\n    this.parseXml(data);\n    if (this._errorCode !== XMLParserErrorCode.NoError) {\n      return undefined;\n    }\n    this._current[$finalize]();\n    return this._current.element;\n  }\n  onText(text) {\n    text = text.replace(this._nbsps, match => match.slice(1) + \" \");\n    if (this._richText || this._current[$acceptWhitespace]()) {\n      this._current[$onText](text, this._richText);\n      return;\n    }\n    if (this._whiteRegex.test(text)) {\n      return;\n    }\n    this._current[$onText](text.trim());\n  }\n  onCdata(text) {\n    this._current[$onText](text);\n  }\n  _mkAttributes(attributes, tagName) {\n    let namespace = null;\n    let prefixes = null;\n    const attributeObj = Object.create({});\n    for (const {\n      name,\n      value\n    } of attributes) {\n      if (name === \"xmlns\") {\n        if (!namespace) {\n          namespace = value;\n        } else {\n          warn(`XFA - multiple namespace definition in <${tagName}>`);\n        }\n      } else if (name.startsWith(\"xmlns:\")) {\n        const prefix = name.substring(\"xmlns:\".length);\n        if (!prefixes) {\n          prefixes = [];\n        }\n        prefixes.push({\n          prefix,\n          value\n        });\n      } else {\n        const i = name.indexOf(\":\");\n        if (i === -1) {\n          attributeObj[name] = value;\n        } else {\n          let nsAttrs = attributeObj[$nsAttributes];\n          if (!nsAttrs) {\n            nsAttrs = attributeObj[$nsAttributes] = Object.create(null);\n          }\n          const [ns, attrName] = [name.slice(0, i), name.slice(i + 1)];\n          const attrs = nsAttrs[ns] ||= Object.create(null);\n          attrs[attrName] = value;\n        }\n      }\n    }\n    return [namespace, prefixes, attributeObj];\n  }\n  _getNameAndPrefix(name, nsAgnostic) {\n    const i = name.indexOf(\":\");\n    if (i === -1) {\n      return [name, null];\n    }\n    return [name.substring(i + 1), nsAgnostic ? \"\" : name.substring(0, i)];\n  }\n  onBeginElement(tagName, attributes, isEmpty) {\n    const [namespace, prefixes, attributesObj] = this._mkAttributes(attributes, tagName);\n    const [name, nsPrefix] = this._getNameAndPrefix(tagName, this._builder.isNsAgnostic());\n    const node = this._builder.build({\n      nsPrefix,\n      name,\n      attributes: attributesObj,\n      namespace,\n      prefixes\n    });\n    node[$globalData] = this._globalData;\n    if (isEmpty) {\n      node[$finalize]();\n      if (this._current[$onChild](node)) {\n        node[$setId](this._ids);\n      }\n      node[$clean](this._builder);\n      return;\n    }\n    this._stack.push(this._current);\n    this._current = node;\n  }\n  onEndElement(name) {\n    const node = this._current;\n    if (node[$isCDATAXml]() && typeof node[$content] === \"string\") {\n      const parser = new XFAParser();\n      parser._globalData = this._globalData;\n      const root = parser.parse(node[$content]);\n      node[$content] = null;\n      node[$onChild](root);\n    }\n    node[$finalize]();\n    this._current = this._stack.pop();\n    if (this._current[$onChild](node)) {\n      node[$setId](this._ids);\n    }\n    node[$clean](this._builder);\n  }\n  onError(code) {\n    this._errorCode = code;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xfa/factory.js\n\n\n\n\n\n\n\n\nclass XFAFactory {\n  constructor(data) {\n    try {\n      this.root = new XFAParser().parse(XFAFactory._createDocument(data));\n      const binder = new Binder(this.root);\n      this.form = binder.bind();\n      this.dataHandler = new DataHandler(this.root, binder.getData());\n      this.form[$globalData].template = this.form;\n    } catch (e) {\n      warn(`XFA - an error occurred during parsing and binding: ${e}`);\n    }\n  }\n  isValid() {\n    return this.root && this.form;\n  }\n  _createPagesHelper() {\n    const iterator = this.form[$toPages]();\n    return new Promise((resolve, reject) => {\n      const nextIteration = () => {\n        try {\n          const value = iterator.next();\n          if (value.done) {\n            resolve(value.value);\n          } else {\n            setTimeout(nextIteration, 0);\n          }\n        } catch (e) {\n          reject(e);\n        }\n      };\n      setTimeout(nextIteration, 0);\n    });\n  }\n  async _createPages() {\n    try {\n      this.pages = await this._createPagesHelper();\n      this.dims = this.pages.children.map(c => {\n        const {\n          width,\n          height\n        } = c.attributes.style;\n        return [0, 0, parseInt(width), parseInt(height)];\n      });\n    } catch (e) {\n      warn(`XFA - an error occurred during layout: ${e}`);\n    }\n  }\n  getBoundingBox(pageIndex) {\n    return this.dims[pageIndex];\n  }\n  async getNumPages() {\n    if (!this.pages) {\n      await this._createPages();\n    }\n    return this.dims.length;\n  }\n  setImages(images) {\n    this.form[$globalData].images = images;\n  }\n  setFonts(fonts) {\n    this.form[$globalData].fontFinder = new FontFinder(fonts);\n    const missingFonts = [];\n    for (let typeface of this.form[$globalData].usedTypefaces) {\n      typeface = stripQuotes(typeface);\n      const font = this.form[$globalData].fontFinder.find(typeface);\n      if (!font) {\n        missingFonts.push(typeface);\n      }\n    }\n    if (missingFonts.length > 0) {\n      return missingFonts;\n    }\n    return null;\n  }\n  appendFonts(fonts, reallyMissingFonts) {\n    this.form[$globalData].fontFinder.add(fonts, reallyMissingFonts);\n  }\n  async getPages() {\n    if (!this.pages) {\n      await this._createPages();\n    }\n    const pages = this.pages;\n    this.pages = null;\n    return pages;\n  }\n  serializeData(storage) {\n    return this.dataHandler.serialize(storage);\n  }\n  static _createDocument(data) {\n    if (!data[\"/xdp:xdp\"]) {\n      return data[\"xdp:xdp\"];\n    }\n    return Object.values(data).join(\"\");\n  }\n  static getRichTextAsHtml(rc) {\n    if (!rc || typeof rc !== \"string\") {\n      return null;\n    }\n    try {\n      let root = new XFAParser(XhtmlNamespace, true).parse(rc);\n      if (![\"body\", \"xhtml\"].includes(root[$nodeName])) {\n        const newRoot = XhtmlNamespace.body({});\n        newRoot[$appendChild](root);\n        root = newRoot;\n      }\n      const result = root[$toHTML]();\n      if (!result.success) {\n        return null;\n      }\n      const {\n        html\n      } = result;\n      const {\n        attributes\n      } = html;\n      if (attributes) {\n        if (attributes.class) {\n          attributes.class = attributes.class.filter(attr => !attr.startsWith(\"xfa\"));\n        }\n        attributes.dir = \"auto\";\n      }\n      return {\n        html,\n        str: root[$text]()\n      };\n    } catch (e) {\n      warn(`XFA - an error occurred during parsing of rich text: ${e}`);\n    }\n    return null;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/annotation.js\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nclass AnnotationFactory {\n  static createGlobals(pdfManager) {\n    return Promise.all([pdfManager.ensureCatalog(\"acroForm\"), pdfManager.ensureDoc(\"xfaDatasets\"), pdfManager.ensureCatalog(\"structTreeRoot\"), pdfManager.ensureCatalog(\"baseUrl\"), pdfManager.ensureCatalog(\"attachments\")]).then(([acroForm, xfaDatasets, structTreeRoot, baseUrl, attachments]) => {\n      return {\n        pdfManager,\n        acroForm: acroForm instanceof Dict ? acroForm : Dict.empty,\n        xfaDatasets,\n        structTreeRoot,\n        baseUrl,\n        attachments\n      };\n    }, reason => {\n      warn(`createGlobals: \"${reason}\".`);\n      return null;\n    });\n  }\n  static async create(xref, ref, annotationGlobals, idFactory, collectFields, pageRef) {\n    const pageIndex = collectFields ? await this._getPageIndex(xref, ref, annotationGlobals.pdfManager) : null;\n    return annotationGlobals.pdfManager.ensure(this, \"_create\", [xref, ref, annotationGlobals, idFactory, collectFields, pageIndex, pageRef]);\n  }\n  static _create(xref, ref, annotationGlobals, idFactory, collectFields = false, pageIndex = null, pageRef = null) {\n    const dict = xref.fetchIfRef(ref);\n    if (!(dict instanceof Dict)) {\n      return undefined;\n    }\n    const {\n      acroForm,\n      pdfManager\n    } = annotationGlobals;\n    const id = ref instanceof Ref ? ref.toString() : `annot_${idFactory.createObjId()}`;\n    let subtype = dict.get(\"Subtype\");\n    subtype = subtype instanceof Name ? subtype.name : null;\n    const parameters = {\n      xref,\n      ref,\n      dict,\n      subtype,\n      id,\n      annotationGlobals,\n      collectFields,\n      needAppearances: !collectFields && acroForm.get(\"NeedAppearances\") === true,\n      pageIndex,\n      evaluatorOptions: pdfManager.evaluatorOptions,\n      pageRef\n    };\n    switch (subtype) {\n      case \"Link\":\n        return new LinkAnnotation(parameters);\n      case \"Text\":\n        return new TextAnnotation(parameters);\n      case \"Widget\":\n        let fieldType = getInheritableProperty({\n          dict,\n          key: \"FT\"\n        });\n        fieldType = fieldType instanceof Name ? fieldType.name : null;\n        switch (fieldType) {\n          case \"Tx\":\n            return new TextWidgetAnnotation(parameters);\n          case \"Btn\":\n            return new ButtonWidgetAnnotation(parameters);\n          case \"Ch\":\n            return new ChoiceWidgetAnnotation(parameters);\n          case \"Sig\":\n            return new SignatureWidgetAnnotation(parameters);\n        }\n        warn(`Unimplemented widget field type \"${fieldType}\", ` + \"falling back to base field type.\");\n        return new WidgetAnnotation(parameters);\n      case \"Popup\":\n        return new PopupAnnotation(parameters);\n      case \"FreeText\":\n        return new FreeTextAnnotation(parameters);\n      case \"Line\":\n        return new LineAnnotation(parameters);\n      case \"Square\":\n        return new SquareAnnotation(parameters);\n      case \"Circle\":\n        return new CircleAnnotation(parameters);\n      case \"PolyLine\":\n        return new PolylineAnnotation(parameters);\n      case \"Polygon\":\n        return new PolygonAnnotation(parameters);\n      case \"Caret\":\n        return new CaretAnnotation(parameters);\n      case \"Ink\":\n        return new InkAnnotation(parameters);\n      case \"Highlight\":\n        return new HighlightAnnotation(parameters);\n      case \"Underline\":\n        return new UnderlineAnnotation(parameters);\n      case \"Squiggly\":\n        return new SquigglyAnnotation(parameters);\n      case \"StrikeOut\":\n        return new StrikeOutAnnotation(parameters);\n      case \"Stamp\":\n        return new StampAnnotation(parameters);\n      case \"FileAttachment\":\n        return new FileAttachmentAnnotation(parameters);\n      default:\n        if (!collectFields) {\n          if (!subtype) {\n            warn(\"Annotation is missing the required /Subtype.\");\n          } else {\n            warn(`Unimplemented annotation type \"${subtype}\", ` + \"falling back to base annotation.\");\n          }\n        }\n        return new Annotation(parameters);\n    }\n  }\n  static async _getPageIndex(xref, ref, pdfManager) {\n    try {\n      const annotDict = await xref.fetchIfRefAsync(ref);\n      if (!(annotDict instanceof Dict)) {\n        return -1;\n      }\n      const pageRef = annotDict.getRaw(\"P\");\n      if (pageRef instanceof Ref) {\n        try {\n          const pageIndex = await pdfManager.ensureCatalog(\"getPageIndex\", [pageRef]);\n          return pageIndex;\n        } catch (ex) {\n          info(`_getPageIndex -- not a valid page reference: \"${ex}\".`);\n        }\n      }\n      if (annotDict.has(\"Kids\")) {\n        return -1;\n      }\n      const numPages = await pdfManager.ensureDoc(\"numPages\");\n      for (let pageIndex = 0; pageIndex < numPages; pageIndex++) {\n        const page = await pdfManager.getPage(pageIndex);\n        const annotations = await pdfManager.ensure(page, \"annotations\");\n        for (const annotRef of annotations) {\n          if (annotRef instanceof Ref && isRefsEqual(annotRef, ref)) {\n            return pageIndex;\n          }\n        }\n      }\n    } catch (ex) {\n      warn(`_getPageIndex: \"${ex}\".`);\n    }\n    return -1;\n  }\n  static generateImages(annotations, xref, isOffscreenCanvasSupported) {\n    if (!isOffscreenCanvasSupported) {\n      warn(\"generateImages: OffscreenCanvas is not supported, cannot save or print some annotations with images.\");\n      return null;\n    }\n    let imagePromises;\n    for (const {\n      bitmapId,\n      bitmap\n    } of annotations) {\n      if (!bitmap) {\n        continue;\n      }\n      imagePromises ||= new Map();\n      imagePromises.set(bitmapId, StampAnnotation.createImage(bitmap, xref));\n    }\n    return imagePromises;\n  }\n  static async saveNewAnnotations(evaluator, task, annotations, imagePromises) {\n    const xref = evaluator.xref;\n    let baseFontRef;\n    const dependencies = [];\n    const promises = [];\n    const {\n      isOffscreenCanvasSupported\n    } = evaluator.options;\n    for (const annotation of annotations) {\n      if (annotation.deleted) {\n        continue;\n      }\n      switch (annotation.annotationType) {\n        case AnnotationEditorType.FREETEXT:\n          if (!baseFontRef) {\n            const baseFont = new Dict(xref);\n            baseFont.set(\"BaseFont\", Name.get(\"Helvetica\"));\n            baseFont.set(\"Type\", Name.get(\"Font\"));\n            baseFont.set(\"Subtype\", Name.get(\"Type1\"));\n            baseFont.set(\"Encoding\", Name.get(\"WinAnsiEncoding\"));\n            const buffer = [];\n            baseFontRef = xref.getNewTemporaryRef();\n            await writeObject(baseFontRef, baseFont, buffer, xref);\n            dependencies.push({\n              ref: baseFontRef,\n              data: buffer.join(\"\")\n            });\n          }\n          promises.push(FreeTextAnnotation.createNewAnnotation(xref, annotation, dependencies, {\n            evaluator,\n            task,\n            baseFontRef\n          }));\n          break;\n        case AnnotationEditorType.HIGHLIGHT:\n          promises.push(HighlightAnnotation.createNewAnnotation(xref, annotation, dependencies));\n          break;\n        case AnnotationEditorType.INK:\n          promises.push(InkAnnotation.createNewAnnotation(xref, annotation, dependencies));\n          break;\n        case AnnotationEditorType.STAMP:\n          if (!isOffscreenCanvasSupported) {\n            break;\n          }\n          const image = await imagePromises.get(annotation.bitmapId);\n          if (image.imageStream) {\n            const {\n              imageStream,\n              smaskStream\n            } = image;\n            const buffer = [];\n            if (smaskStream) {\n              const smaskRef = xref.getNewTemporaryRef();\n              await writeObject(smaskRef, smaskStream, buffer, xref);\n              dependencies.push({\n                ref: smaskRef,\n                data: buffer.join(\"\")\n              });\n              imageStream.dict.set(\"SMask\", smaskRef);\n              buffer.length = 0;\n            }\n            const imageRef = image.imageRef = xref.getNewTemporaryRef();\n            await writeObject(imageRef, imageStream, buffer, xref);\n            dependencies.push({\n              ref: imageRef,\n              data: buffer.join(\"\")\n            });\n            image.imageStream = image.smaskStream = null;\n          }\n          promises.push(StampAnnotation.createNewAnnotation(xref, annotation, dependencies, {\n            image\n          }));\n          break;\n      }\n    }\n    return {\n      annotations: await Promise.all(promises),\n      dependencies\n    };\n  }\n  static async printNewAnnotations(annotationGlobals, evaluator, task, annotations, imagePromises) {\n    if (!annotations) {\n      return null;\n    }\n    const {\n      options,\n      xref\n    } = evaluator;\n    const promises = [];\n    for (const annotation of annotations) {\n      if (annotation.deleted) {\n        continue;\n      }\n      switch (annotation.annotationType) {\n        case AnnotationEditorType.FREETEXT:\n          promises.push(FreeTextAnnotation.createNewPrintAnnotation(annotationGlobals, xref, annotation, {\n            evaluator,\n            task,\n            evaluatorOptions: options\n          }));\n          break;\n        case AnnotationEditorType.HIGHLIGHT:\n          promises.push(HighlightAnnotation.createNewPrintAnnotation(annotationGlobals, xref, annotation, {\n            evaluatorOptions: options\n          }));\n          break;\n        case AnnotationEditorType.INK:\n          promises.push(InkAnnotation.createNewPrintAnnotation(annotationGlobals, xref, annotation, {\n            evaluatorOptions: options\n          }));\n          break;\n        case AnnotationEditorType.STAMP:\n          if (!options.isOffscreenCanvasSupported) {\n            break;\n          }\n          const image = await imagePromises.get(annotation.bitmapId);\n          if (image.imageStream) {\n            const {\n              imageStream,\n              smaskStream\n            } = image;\n            if (smaskStream) {\n              imageStream.dict.set(\"SMask\", smaskStream);\n            }\n            image.imageRef = new JpegStream(imageStream, imageStream.length);\n            image.imageStream = image.smaskStream = null;\n          }\n          promises.push(StampAnnotation.createNewPrintAnnotation(annotationGlobals, xref, annotation, {\n            image,\n            evaluatorOptions: options\n          }));\n          break;\n      }\n    }\n    return Promise.all(promises);\n  }\n}\nfunction getRgbColor(color, defaultColor = new Uint8ClampedArray(3)) {\n  if (!Array.isArray(color)) {\n    return defaultColor;\n  }\n  const rgbColor = defaultColor || new Uint8ClampedArray(3);\n  switch (color.length) {\n    case 0:\n      return null;\n    case 1:\n      ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0);\n      return rgbColor;\n    case 3:\n      ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0);\n      return rgbColor;\n    case 4:\n      ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0);\n      return rgbColor;\n    default:\n      return defaultColor;\n  }\n}\nfunction getPdfColorArray(color) {\n  return Array.from(color, c => c / 255);\n}\nfunction getQuadPoints(dict, rect) {\n  const quadPoints = dict.getArray(\"QuadPoints\");\n  if (!Array.isArray(quadPoints) || quadPoints.length === 0 || quadPoints.length % 8 > 0) {\n    return null;\n  }\n  const quadPointsLists = [];\n  for (let i = 0, ii = quadPoints.length / 8; i < ii; i++) {\n    let minX = Infinity,\n      maxX = -Infinity,\n      minY = Infinity,\n      maxY = -Infinity;\n    for (let j = i * 8, jj = i * 8 + 8; j < jj; j += 2) {\n      const x = quadPoints[j];\n      const y = quadPoints[j + 1];\n      minX = Math.min(x, minX);\n      maxX = Math.max(x, maxX);\n      minY = Math.min(y, minY);\n      maxY = Math.max(y, maxY);\n    }\n    if (rect !== null && (minX < rect[0] || maxX > rect[2] || minY < rect[1] || maxY > rect[3])) {\n      return null;\n    }\n    quadPointsLists.push([{\n      x: minX,\n      y: maxY\n    }, {\n      x: maxX,\n      y: maxY\n    }, {\n      x: minX,\n      y: minY\n    }, {\n      x: maxX,\n      y: minY\n    }]);\n  }\n  return quadPointsLists;\n}\nfunction getTransformMatrix(rect, bbox, matrix) {\n  const [minX, minY, maxX, maxY] = Util.getAxialAlignedBoundingBox(bbox, matrix);\n  if (minX === maxX || minY === maxY) {\n    return [1, 0, 0, 1, rect[0], rect[1]];\n  }\n  const xRatio = (rect[2] - rect[0]) / (maxX - minX);\n  const yRatio = (rect[3] - rect[1]) / (maxY - minY);\n  return [xRatio, 0, 0, yRatio, rect[0] - minX * xRatio, rect[1] - minY * yRatio];\n}\nclass Annotation {\n  constructor(params) {\n    const {\n      dict,\n      xref,\n      annotationGlobals\n    } = params;\n    this.setTitle(dict.get(\"T\"));\n    this.setContents(dict.get(\"Contents\"));\n    this.setModificationDate(dict.get(\"M\"));\n    this.setFlags(dict.get(\"F\"));\n    this.setRectangle(dict.getArray(\"Rect\"));\n    this.setColor(dict.getArray(\"C\"));\n    this.setBorderStyle(dict);\n    this.setAppearance(dict);\n    this.setOptionalContent(dict);\n    const MK = dict.get(\"MK\");\n    this.setBorderAndBackgroundColors(MK);\n    this.setRotation(MK, dict);\n    this.ref = params.ref instanceof Ref ? params.ref : null;\n    this._streams = [];\n    if (this.appearance) {\n      this._streams.push(this.appearance);\n    }\n    const isLocked = !!(this.flags & AnnotationFlag.LOCKED);\n    const isContentLocked = !!(this.flags & AnnotationFlag.LOCKEDCONTENTS);\n    if (annotationGlobals.structTreeRoot) {\n      let structParent = dict.get(\"StructParent\");\n      structParent = Number.isInteger(structParent) && structParent >= 0 ? structParent : -1;\n      annotationGlobals.structTreeRoot.addAnnotationIdToPage(params.pageRef, structParent);\n    }\n    this.data = {\n      annotationFlags: this.flags,\n      borderStyle: this.borderStyle,\n      color: this.color,\n      backgroundColor: this.backgroundColor,\n      borderColor: this.borderColor,\n      rotation: this.rotation,\n      contentsObj: this._contents,\n      hasAppearance: !!this.appearance,\n      id: params.id,\n      modificationDate: this.modificationDate,\n      rect: this.rectangle,\n      subtype: params.subtype,\n      hasOwnCanvas: false,\n      noRotate: !!(this.flags & AnnotationFlag.NOROTATE),\n      noHTML: isLocked && isContentLocked\n    };\n    if (params.collectFields) {\n      const kids = dict.get(\"Kids\");\n      if (Array.isArray(kids)) {\n        const kidIds = [];\n        for (const kid of kids) {\n          if (kid instanceof Ref) {\n            kidIds.push(kid.toString());\n          }\n        }\n        if (kidIds.length !== 0) {\n          this.data.kidIds = kidIds;\n        }\n      }\n      this.data.actions = collectActions(xref, dict, AnnotationActionEventType);\n      this.data.fieldName = this._constructFieldName(dict);\n      this.data.pageIndex = params.pageIndex;\n    }\n    this._isOffscreenCanvasSupported = params.evaluatorOptions.isOffscreenCanvasSupported;\n    this._fallbackFontDict = null;\n    this._needAppearances = false;\n  }\n  _hasFlag(flags, flag) {\n    return !!(flags & flag);\n  }\n  _isViewable(flags) {\n    return !this._hasFlag(flags, AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, AnnotationFlag.NOVIEW);\n  }\n  _isPrintable(flags) {\n    return this._hasFlag(flags, AnnotationFlag.PRINT) && !this._hasFlag(flags, AnnotationFlag.HIDDEN) && !this._hasFlag(flags, AnnotationFlag.INVISIBLE);\n  }\n  mustBeViewed(annotationStorage, _renderForms) {\n    const noView = annotationStorage?.get(this.data.id)?.noView;\n    if (noView !== undefined) {\n      return !noView;\n    }\n    return this.viewable && !this._hasFlag(this.flags, AnnotationFlag.HIDDEN);\n  }\n  mustBePrinted(annotationStorage) {\n    const noPrint = annotationStorage?.get(this.data.id)?.noPrint;\n    if (noPrint !== undefined) {\n      return !noPrint;\n    }\n    return this.printable;\n  }\n  get viewable() {\n    if (this.data.quadPoints === null) {\n      return false;\n    }\n    if (this.flags === 0) {\n      return true;\n    }\n    return this._isViewable(this.flags);\n  }\n  get printable() {\n    if (this.data.quadPoints === null) {\n      return false;\n    }\n    if (this.flags === 0) {\n      return false;\n    }\n    return this._isPrintable(this.flags);\n  }\n  _parseStringHelper(data) {\n    const str = typeof data === \"string\" ? stringToPDFString(data) : \"\";\n    const dir = str && bidi(str).dir === \"rtl\" ? \"rtl\" : \"ltr\";\n    return {\n      str,\n      dir\n    };\n  }\n  setDefaultAppearance(params) {\n    const {\n      dict,\n      annotationGlobals\n    } = params;\n    const defaultAppearance = getInheritableProperty({\n      dict,\n      key: \"DA\"\n    }) || annotationGlobals.acroForm.get(\"DA\");\n    this._defaultAppearance = typeof defaultAppearance === \"string\" ? defaultAppearance : \"\";\n    this.data.defaultAppearanceData = parseDefaultAppearance(this._defaultAppearance);\n  }\n  setTitle(title) {\n    this._title = this._parseStringHelper(title);\n  }\n  setContents(contents) {\n    this._contents = this._parseStringHelper(contents);\n  }\n  setModificationDate(modificationDate) {\n    this.modificationDate = typeof modificationDate === \"string\" ? modificationDate : null;\n  }\n  setFlags(flags) {\n    this.flags = Number.isInteger(flags) && flags > 0 ? flags : 0;\n    if (this.flags & AnnotationFlag.INVISIBLE && this.constructor.name !== \"Annotation\") {\n      this.flags ^= AnnotationFlag.INVISIBLE;\n    }\n  }\n  hasFlag(flag) {\n    return this._hasFlag(this.flags, flag);\n  }\n  setRectangle(rectangle) {\n    this.rectangle = Array.isArray(rectangle) && rectangle.length === 4 ? Util.normalizeRect(rectangle) : [0, 0, 0, 0];\n  }\n  setColor(color) {\n    this.color = getRgbColor(color);\n  }\n  setLineEndings(lineEndings) {\n    this.lineEndings = [\"None\", \"None\"];\n    if (Array.isArray(lineEndings) && lineEndings.length === 2) {\n      for (let i = 0; i < 2; i++) {\n        const obj = lineEndings[i];\n        if (obj instanceof Name) {\n          switch (obj.name) {\n            case \"None\":\n              continue;\n            case \"Square\":\n            case \"Circle\":\n            case \"Diamond\":\n            case \"OpenArrow\":\n            case \"ClosedArrow\":\n            case \"Butt\":\n            case \"ROpenArrow\":\n            case \"RClosedArrow\":\n            case \"Slash\":\n              this.lineEndings[i] = obj.name;\n              continue;\n          }\n        }\n        warn(`Ignoring invalid lineEnding: ${obj}`);\n      }\n    }\n  }\n  setRotation(mk, dict) {\n    this.rotation = 0;\n    let angle = mk instanceof Dict ? mk.get(\"R\") || 0 : dict.get(\"Rotate\") || 0;\n    if (Number.isInteger(angle) && angle !== 0) {\n      angle %= 360;\n      if (angle < 0) {\n        angle += 360;\n      }\n      if (angle % 90 === 0) {\n        this.rotation = angle;\n      }\n    }\n  }\n  setBorderAndBackgroundColors(mk) {\n    if (mk instanceof Dict) {\n      this.borderColor = getRgbColor(mk.getArray(\"BC\"), null);\n      this.backgroundColor = getRgbColor(mk.getArray(\"BG\"), null);\n    } else {\n      this.borderColor = this.backgroundColor = null;\n    }\n  }\n  setBorderStyle(borderStyle) {\n    this.borderStyle = new AnnotationBorderStyle();\n    if (!(borderStyle instanceof Dict)) {\n      return;\n    }\n    if (borderStyle.has(\"BS\")) {\n      const dict = borderStyle.get(\"BS\");\n      const dictType = dict.get(\"Type\");\n      if (!dictType || isName(dictType, \"Border\")) {\n        this.borderStyle.setWidth(dict.get(\"W\"), this.rectangle);\n        this.borderStyle.setStyle(dict.get(\"S\"));\n        this.borderStyle.setDashArray(dict.getArray(\"D\"));\n      }\n    } else if (borderStyle.has(\"Border\")) {\n      const array = borderStyle.getArray(\"Border\");\n      if (Array.isArray(array) && array.length >= 3) {\n        this.borderStyle.setHorizontalCornerRadius(array[0]);\n        this.borderStyle.setVerticalCornerRadius(array[1]);\n        this.borderStyle.setWidth(array[2], this.rectangle);\n        if (array.length === 4) {\n          this.borderStyle.setDashArray(array[3], true);\n        }\n      }\n    } else {\n      this.borderStyle.setWidth(0);\n    }\n  }\n  setAppearance(dict) {\n    this.appearance = null;\n    const appearanceStates = dict.get(\"AP\");\n    if (!(appearanceStates instanceof Dict)) {\n      return;\n    }\n    const normalAppearanceState = appearanceStates.get(\"N\");\n    if (normalAppearanceState instanceof BaseStream) {\n      this.appearance = normalAppearanceState;\n      return;\n    }\n    if (!(normalAppearanceState instanceof Dict)) {\n      return;\n    }\n    const as = dict.get(\"AS\");\n    if (!(as instanceof Name) || !normalAppearanceState.has(as.name)) {\n      return;\n    }\n    const appearance = normalAppearanceState.get(as.name);\n    if (appearance instanceof BaseStream) {\n      this.appearance = appearance;\n    }\n  }\n  setOptionalContent(dict) {\n    this.oc = null;\n    const oc = dict.get(\"OC\");\n    if (oc instanceof Name) {\n      warn(\"setOptionalContent: Support for /Name-entry is not implemented.\");\n    } else if (oc instanceof Dict) {\n      this.oc = oc;\n    }\n  }\n  loadResources(keys, appearance) {\n    return appearance.dict.getAsync(\"Resources\").then(resources => {\n      if (!resources) {\n        return undefined;\n      }\n      const objectLoader = new ObjectLoader(resources, keys, resources.xref);\n      return objectLoader.load().then(function () {\n        return resources;\n      });\n    });\n  }\n  async getOperatorList(evaluator, task, intent, renderForms, annotationStorage) {\n    const data = this.data;\n    let appearance = this.appearance;\n    const isUsingOwnCanvas = !!(this.data.hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY);\n    if (!appearance) {\n      if (!isUsingOwnCanvas) {\n        return {\n          opList: new OperatorList(),\n          separateForm: false,\n          separateCanvas: false\n        };\n      }\n      appearance = new StringStream(\"\");\n      appearance.dict = new Dict();\n    }\n    const appearanceDict = appearance.dict;\n    const resources = await this.loadResources([\"ExtGState\", \"ColorSpace\", \"Pattern\", \"Shading\", \"XObject\", \"Font\"], appearance);\n    const bbox = appearanceDict.getArray(\"BBox\") || [0, 0, 1, 1];\n    const matrix = appearanceDict.getArray(\"Matrix\") || [1, 0, 0, 1, 0, 0];\n    const transform = getTransformMatrix(data.rect, bbox, matrix);\n    const opList = new OperatorList();\n    let optionalContent;\n    if (this.oc) {\n      optionalContent = await evaluator.parseMarkedContentProps(this.oc, null);\n    }\n    if (optionalContent !== undefined) {\n      opList.addOp(OPS.beginMarkedContentProps, [\"OC\", optionalContent]);\n    }\n    opList.addOp(OPS.beginAnnotation, [data.id, data.rect, transform, matrix, isUsingOwnCanvas]);\n    await evaluator.getOperatorList({\n      stream: appearance,\n      task,\n      resources,\n      operatorList: opList,\n      fallbackFontDict: this._fallbackFontDict\n    });\n    opList.addOp(OPS.endAnnotation, []);\n    if (optionalContent !== undefined) {\n      opList.addOp(OPS.endMarkedContent, []);\n    }\n    this.reset();\n    return {\n      opList,\n      separateForm: false,\n      separateCanvas: isUsingOwnCanvas\n    };\n  }\n  async save(evaluator, task, annotationStorage) {\n    return null;\n  }\n  get hasTextContent() {\n    return false;\n  }\n  async extractTextContent(evaluator, task, viewBox) {\n    if (!this.appearance) {\n      return;\n    }\n    const resources = await this.loadResources([\"ExtGState\", \"Font\", \"Properties\", \"XObject\"], this.appearance);\n    const text = [];\n    const buffer = [];\n    let firstPosition = null;\n    const sink = {\n      desiredSize: Math.Infinity,\n      ready: true,\n      enqueue(chunk, size) {\n        for (const item of chunk.items) {\n          if (item.str === undefined) {\n            continue;\n          }\n          firstPosition ||= item.transform.slice(-2);\n          buffer.push(item.str);\n          if (item.hasEOL) {\n            text.push(buffer.join(\"\"));\n            buffer.length = 0;\n          }\n        }\n      }\n    };\n    await evaluator.getTextContent({\n      stream: this.appearance,\n      task,\n      resources,\n      includeMarkedContent: true,\n      sink,\n      viewBox\n    });\n    this.reset();\n    if (buffer.length) {\n      text.push(buffer.join(\"\"));\n    }\n    if (text.length > 1 || text[0]) {\n      const appearanceDict = this.appearance.dict;\n      const bbox = appearanceDict.getArray(\"BBox\") || [0, 0, 1, 1];\n      const matrix = appearanceDict.getArray(\"Matrix\") || [1, 0, 0, 1, 0, 0];\n      const rect = this.data.rect;\n      const transform = getTransformMatrix(rect, bbox, matrix);\n      transform[4] -= rect[0];\n      transform[5] -= rect[1];\n      firstPosition = Util.applyTransform(firstPosition, transform);\n      firstPosition = Util.applyTransform(firstPosition, matrix);\n      this.data.textPosition = firstPosition;\n      this.data.textContent = text;\n    }\n  }\n  getFieldObject() {\n    if (this.data.kidIds) {\n      return {\n        id: this.data.id,\n        actions: this.data.actions,\n        name: this.data.fieldName,\n        strokeColor: this.data.borderColor,\n        fillColor: this.data.backgroundColor,\n        type: \"\",\n        kidIds: this.data.kidIds,\n        page: this.data.pageIndex,\n        rotation: this.rotation\n      };\n    }\n    return null;\n  }\n  reset() {\n    for (const stream of this._streams) {\n      stream.reset();\n    }\n  }\n  _constructFieldName(dict) {\n    if (!dict.has(\"T\") && !dict.has(\"Parent\")) {\n      warn(\"Unknown field name, falling back to empty field name.\");\n      return \"\";\n    }\n    if (!dict.has(\"Parent\")) {\n      return stringToPDFString(dict.get(\"T\"));\n    }\n    const fieldName = [];\n    if (dict.has(\"T\")) {\n      fieldName.unshift(stringToPDFString(dict.get(\"T\")));\n    }\n    let loopDict = dict;\n    const visited = new RefSet();\n    if (dict.objId) {\n      visited.put(dict.objId);\n    }\n    while (loopDict.has(\"Parent\")) {\n      loopDict = loopDict.get(\"Parent\");\n      if (!(loopDict instanceof Dict) || loopDict.objId && visited.has(loopDict.objId)) {\n        break;\n      }\n      if (loopDict.objId) {\n        visited.put(loopDict.objId);\n      }\n      if (loopDict.has(\"T\")) {\n        fieldName.unshift(stringToPDFString(loopDict.get(\"T\")));\n      }\n    }\n    return fieldName.join(\".\");\n  }\n}\nclass AnnotationBorderStyle {\n  constructor() {\n    this.width = 1;\n    this.style = AnnotationBorderStyleType.SOLID;\n    this.dashArray = [3];\n    this.horizontalCornerRadius = 0;\n    this.verticalCornerRadius = 0;\n  }\n  setWidth(width, rect = [0, 0, 0, 0]) {\n    if (width instanceof Name) {\n      this.width = 0;\n      return;\n    }\n    if (typeof width === \"number\") {\n      if (width > 0) {\n        const maxWidth = (rect[2] - rect[0]) / 2;\n        const maxHeight = (rect[3] - rect[1]) / 2;\n        if (maxWidth > 0 && maxHeight > 0 && (width > maxWidth || width > maxHeight)) {\n          warn(`AnnotationBorderStyle.setWidth - ignoring width: ${width}`);\n          width = 1;\n        }\n      }\n      this.width = width;\n    }\n  }\n  setStyle(style) {\n    if (!(style instanceof Name)) {\n      return;\n    }\n    switch (style.name) {\n      case \"S\":\n        this.style = AnnotationBorderStyleType.SOLID;\n        break;\n      case \"D\":\n        this.style = AnnotationBorderStyleType.DASHED;\n        break;\n      case \"B\":\n        this.style = AnnotationBorderStyleType.BEVELED;\n        break;\n      case \"I\":\n        this.style = AnnotationBorderStyleType.INSET;\n        break;\n      case \"U\":\n        this.style = AnnotationBorderStyleType.UNDERLINE;\n        break;\n      default:\n        break;\n    }\n  }\n  setDashArray(dashArray, forceStyle = false) {\n    if (Array.isArray(dashArray) && dashArray.length > 0) {\n      let isValid = true;\n      let allZeros = true;\n      for (const element of dashArray) {\n        const validNumber = +element >= 0;\n        if (!validNumber) {\n          isValid = false;\n          break;\n        } else if (element > 0) {\n          allZeros = false;\n        }\n      }\n      if (isValid && !allZeros) {\n        this.dashArray = dashArray;\n        if (forceStyle) {\n          this.setStyle(Name.get(\"D\"));\n        }\n      } else {\n        this.width = 0;\n      }\n    } else if (dashArray) {\n      this.width = 0;\n    }\n  }\n  setHorizontalCornerRadius(radius) {\n    if (Number.isInteger(radius)) {\n      this.horizontalCornerRadius = radius;\n    }\n  }\n  setVerticalCornerRadius(radius) {\n    if (Number.isInteger(radius)) {\n      this.verticalCornerRadius = radius;\n    }\n  }\n}\nclass MarkupAnnotation extends Annotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict\n    } = params;\n    if (dict.has(\"IRT\")) {\n      const rawIRT = dict.getRaw(\"IRT\");\n      this.data.inReplyTo = rawIRT instanceof Ref ? rawIRT.toString() : null;\n      const rt = dict.get(\"RT\");\n      this.data.replyType = rt instanceof Name ? rt.name : AnnotationReplyType.REPLY;\n    }\n    let popupRef = null;\n    if (this.data.replyType === AnnotationReplyType.GROUP) {\n      const parent = dict.get(\"IRT\");\n      this.setTitle(parent.get(\"T\"));\n      this.data.titleObj = this._title;\n      this.setContents(parent.get(\"Contents\"));\n      this.data.contentsObj = this._contents;\n      if (!parent.has(\"CreationDate\")) {\n        this.data.creationDate = null;\n      } else {\n        this.setCreationDate(parent.get(\"CreationDate\"));\n        this.data.creationDate = this.creationDate;\n      }\n      if (!parent.has(\"M\")) {\n        this.data.modificationDate = null;\n      } else {\n        this.setModificationDate(parent.get(\"M\"));\n        this.data.modificationDate = this.modificationDate;\n      }\n      popupRef = parent.getRaw(\"Popup\");\n      if (!parent.has(\"C\")) {\n        this.data.color = null;\n      } else {\n        this.setColor(parent.getArray(\"C\"));\n        this.data.color = this.color;\n      }\n    } else {\n      this.data.titleObj = this._title;\n      this.setCreationDate(dict.get(\"CreationDate\"));\n      this.data.creationDate = this.creationDate;\n      popupRef = dict.getRaw(\"Popup\");\n      if (!dict.has(\"C\")) {\n        this.data.color = null;\n      }\n    }\n    this.data.popupRef = popupRef instanceof Ref ? popupRef.toString() : null;\n    if (dict.has(\"RC\")) {\n      this.data.richText = XFAFactory.getRichTextAsHtml(dict.get(\"RC\"));\n    }\n  }\n  setCreationDate(creationDate) {\n    this.creationDate = typeof creationDate === \"string\" ? creationDate : null;\n  }\n  _setDefaultAppearance({\n    xref,\n    extra,\n    strokeColor,\n    fillColor,\n    blendMode,\n    strokeAlpha,\n    fillAlpha,\n    pointsCallback\n  }) {\n    let minX = Number.MAX_VALUE;\n    let minY = Number.MAX_VALUE;\n    let maxX = Number.MIN_VALUE;\n    let maxY = Number.MIN_VALUE;\n    const buffer = [\"q\"];\n    if (extra) {\n      buffer.push(extra);\n    }\n    if (strokeColor) {\n      buffer.push(`${strokeColor[0]} ${strokeColor[1]} ${strokeColor[2]} RG`);\n    }\n    if (fillColor) {\n      buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`);\n    }\n    let pointsArray = this.data.quadPoints;\n    if (!pointsArray) {\n      pointsArray = [[{\n        x: this.rectangle[0],\n        y: this.rectangle[3]\n      }, {\n        x: this.rectangle[2],\n        y: this.rectangle[3]\n      }, {\n        x: this.rectangle[0],\n        y: this.rectangle[1]\n      }, {\n        x: this.rectangle[2],\n        y: this.rectangle[1]\n      }]];\n    }\n    for (const points of pointsArray) {\n      const [mX, MX, mY, MY] = pointsCallback(buffer, points);\n      minX = Math.min(minX, mX);\n      maxX = Math.max(maxX, MX);\n      minY = Math.min(minY, mY);\n      maxY = Math.max(maxY, MY);\n    }\n    buffer.push(\"Q\");\n    const formDict = new Dict(xref);\n    const appearanceStreamDict = new Dict(xref);\n    appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n    const appearanceStream = new StringStream(buffer.join(\" \"));\n    appearanceStream.dict = appearanceStreamDict;\n    formDict.set(\"Fm0\", appearanceStream);\n    const gsDict = new Dict(xref);\n    if (blendMode) {\n      gsDict.set(\"BM\", Name.get(blendMode));\n    }\n    if (typeof strokeAlpha === \"number\") {\n      gsDict.set(\"CA\", strokeAlpha);\n    }\n    if (typeof fillAlpha === \"number\") {\n      gsDict.set(\"ca\", fillAlpha);\n    }\n    const stateDict = new Dict(xref);\n    stateDict.set(\"GS0\", gsDict);\n    const resources = new Dict(xref);\n    resources.set(\"ExtGState\", stateDict);\n    resources.set(\"XObject\", formDict);\n    const appearanceDict = new Dict(xref);\n    appearanceDict.set(\"Resources\", resources);\n    const bbox = this.data.rect = [minX, minY, maxX, maxY];\n    appearanceDict.set(\"BBox\", bbox);\n    this.appearance = new StringStream(\"/GS0 gs /Fm0 Do\");\n    this.appearance.dict = appearanceDict;\n    this._streams.push(this.appearance, appearanceStream);\n  }\n  static async createNewAnnotation(xref, annotation, dependencies, params) {\n    const annotationRef = annotation.ref ||= xref.getNewTemporaryRef();\n    const ap = await this.createNewAppearanceStream(annotation, xref, params);\n    const buffer = [];\n    let annotationDict;\n    if (ap) {\n      const apRef = xref.getNewTemporaryRef();\n      annotationDict = this.createNewDict(annotation, xref, {\n        apRef\n      });\n      await writeObject(apRef, ap, buffer, xref);\n      dependencies.push({\n        ref: apRef,\n        data: buffer.join(\"\")\n      });\n    } else {\n      annotationDict = this.createNewDict(annotation, xref, {});\n    }\n    if (Number.isInteger(annotation.parentTreeId)) {\n      annotationDict.set(\"StructParent\", annotation.parentTreeId);\n    }\n    buffer.length = 0;\n    await writeObject(annotationRef, annotationDict, buffer, xref);\n    return {\n      ref: annotationRef,\n      data: buffer.join(\"\")\n    };\n  }\n  static async createNewPrintAnnotation(annotationGlobals, xref, annotation, params) {\n    const ap = await this.createNewAppearanceStream(annotation, xref, params);\n    const annotationDict = this.createNewDict(annotation, xref, {\n      ap\n    });\n    const newAnnotation = new this.prototype.constructor({\n      dict: annotationDict,\n      xref,\n      annotationGlobals,\n      evaluatorOptions: params.evaluatorOptions\n    });\n    if (annotation.ref) {\n      newAnnotation.ref = newAnnotation.refToReplace = annotation.ref;\n    }\n    return newAnnotation;\n  }\n}\nclass WidgetAnnotation extends Annotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict,\n      xref,\n      annotationGlobals\n    } = params;\n    const data = this.data;\n    this._needAppearances = params.needAppearances;\n    data.annotationType = AnnotationType.WIDGET;\n    if (data.fieldName === undefined) {\n      data.fieldName = this._constructFieldName(dict);\n    }\n    if (data.actions === undefined) {\n      data.actions = collectActions(xref, dict, AnnotationActionEventType);\n    }\n    let fieldValue = getInheritableProperty({\n      dict,\n      key: \"V\",\n      getArray: true\n    });\n    data.fieldValue = this._decodeFormValue(fieldValue);\n    const defaultFieldValue = getInheritableProperty({\n      dict,\n      key: \"DV\",\n      getArray: true\n    });\n    data.defaultFieldValue = this._decodeFormValue(defaultFieldValue);\n    if (fieldValue === undefined && annotationGlobals.xfaDatasets) {\n      const path = this._title.str;\n      if (path) {\n        this._hasValueFromXFA = true;\n        data.fieldValue = fieldValue = annotationGlobals.xfaDatasets.getValue(path);\n      }\n    }\n    if (fieldValue === undefined && data.defaultFieldValue !== null) {\n      data.fieldValue = data.defaultFieldValue;\n    }\n    data.alternativeText = stringToPDFString(dict.get(\"TU\") || \"\");\n    this.setDefaultAppearance(params);\n    data.hasAppearance ||= this._needAppearances && data.fieldValue !== undefined && data.fieldValue !== null;\n    const fieldType = getInheritableProperty({\n      dict,\n      key: \"FT\"\n    });\n    data.fieldType = fieldType instanceof Name ? fieldType.name : null;\n    const localResources = getInheritableProperty({\n      dict,\n      key: \"DR\"\n    });\n    const acroFormResources = annotationGlobals.acroForm.get(\"DR\");\n    const appearanceResources = this.appearance?.dict.get(\"Resources\");\n    this._fieldResources = {\n      localResources,\n      acroFormResources,\n      appearanceResources,\n      mergedResources: Dict.merge({\n        xref,\n        dictArray: [localResources, appearanceResources, acroFormResources],\n        mergeSubDicts: true\n      })\n    };\n    data.fieldFlags = getInheritableProperty({\n      dict,\n      key: \"Ff\"\n    });\n    if (!Number.isInteger(data.fieldFlags) || data.fieldFlags < 0) {\n      data.fieldFlags = 0;\n    }\n    data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);\n    data.required = this.hasFieldFlag(AnnotationFieldFlag.REQUIRED);\n    data.hidden = this._hasFlag(data.annotationFlags, AnnotationFlag.HIDDEN) || this._hasFlag(data.annotationFlags, AnnotationFlag.NOVIEW);\n  }\n  _decodeFormValue(formValue) {\n    if (Array.isArray(formValue)) {\n      return formValue.filter(item => typeof item === \"string\").map(item => stringToPDFString(item));\n    } else if (formValue instanceof Name) {\n      return stringToPDFString(formValue.name);\n    } else if (typeof formValue === \"string\") {\n      return stringToPDFString(formValue);\n    }\n    return null;\n  }\n  hasFieldFlag(flag) {\n    return !!(this.data.fieldFlags & flag);\n  }\n  _isViewable(flags) {\n    return true;\n  }\n  mustBeViewed(annotationStorage, renderForms) {\n    if (renderForms) {\n      return this.viewable;\n    }\n    return super.mustBeViewed(annotationStorage, renderForms) && !this._hasFlag(this.flags, AnnotationFlag.NOVIEW);\n  }\n  getRotationMatrix(annotationStorage) {\n    let rotation = annotationStorage?.get(this.data.id)?.rotation;\n    if (rotation === undefined) {\n      rotation = this.rotation;\n    }\n    if (rotation === 0) {\n      return IDENTITY_MATRIX;\n    }\n    const width = this.data.rect[2] - this.data.rect[0];\n    const height = this.data.rect[3] - this.data.rect[1];\n    return getRotationMatrix(rotation, width, height);\n  }\n  getBorderAndBackgroundAppearances(annotationStorage) {\n    let rotation = annotationStorage?.get(this.data.id)?.rotation;\n    if (rotation === undefined) {\n      rotation = this.rotation;\n    }\n    if (!this.backgroundColor && !this.borderColor) {\n      return \"\";\n    }\n    const width = this.data.rect[2] - this.data.rect[0];\n    const height = this.data.rect[3] - this.data.rect[1];\n    const rect = rotation === 0 || rotation === 180 ? `0 0 ${width} ${height} re` : `0 0 ${height} ${width} re`;\n    let str = \"\";\n    if (this.backgroundColor) {\n      str = `${getPdfColor(this.backgroundColor, true)} ${rect} f `;\n    }\n    if (this.borderColor) {\n      const borderWidth = this.borderStyle.width || 1;\n      str += `${borderWidth} w ${getPdfColor(this.borderColor, false)} ${rect} S `;\n    }\n    return str;\n  }\n  async getOperatorList(evaluator, task, intent, renderForms, annotationStorage) {\n    if (renderForms && !(this instanceof SignatureWidgetAnnotation) && !this.data.noHTML && !this.data.hasOwnCanvas) {\n      return {\n        opList: new OperatorList(),\n        separateForm: true,\n        separateCanvas: false\n      };\n    }\n    if (!this._hasText) {\n      return super.getOperatorList(evaluator, task, intent, renderForms, annotationStorage);\n    }\n    const content = await this._getAppearance(evaluator, task, intent, annotationStorage);\n    if (this.appearance && content === null) {\n      return super.getOperatorList(evaluator, task, intent, renderForms, annotationStorage);\n    }\n    const opList = new OperatorList();\n    if (!this._defaultAppearance || content === null) {\n      return {\n        opList,\n        separateForm: false,\n        separateCanvas: false\n      };\n    }\n    const isUsingOwnCanvas = !!(this.data.hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY);\n    const matrix = [1, 0, 0, 1, 0, 0];\n    const bbox = [0, 0, this.data.rect[2] - this.data.rect[0], this.data.rect[3] - this.data.rect[1]];\n    const transform = getTransformMatrix(this.data.rect, bbox, matrix);\n    let optionalContent;\n    if (this.oc) {\n      optionalContent = await evaluator.parseMarkedContentProps(this.oc, null);\n    }\n    if (optionalContent !== undefined) {\n      opList.addOp(OPS.beginMarkedContentProps, [\"OC\", optionalContent]);\n    }\n    opList.addOp(OPS.beginAnnotation, [this.data.id, this.data.rect, transform, this.getRotationMatrix(annotationStorage), isUsingOwnCanvas]);\n    const stream = new StringStream(content);\n    await evaluator.getOperatorList({\n      stream,\n      task,\n      resources: this._fieldResources.mergedResources,\n      operatorList: opList\n    });\n    opList.addOp(OPS.endAnnotation, []);\n    if (optionalContent !== undefined) {\n      opList.addOp(OPS.endMarkedContent, []);\n    }\n    return {\n      opList,\n      separateForm: false,\n      separateCanvas: isUsingOwnCanvas\n    };\n  }\n  _getMKDict(rotation) {\n    const mk = new Dict(null);\n    if (rotation) {\n      mk.set(\"R\", rotation);\n    }\n    if (this.borderColor) {\n      mk.set(\"BC\", getPdfColorArray(this.borderColor));\n    }\n    if (this.backgroundColor) {\n      mk.set(\"BG\", getPdfColorArray(this.backgroundColor));\n    }\n    return mk.size > 0 ? mk : null;\n  }\n  amendSavedDict(annotationStorage, dict) {}\n  async save(evaluator, task, annotationStorage) {\n    const storageEntry = annotationStorage?.get(this.data.id);\n    let value = storageEntry?.value,\n      rotation = storageEntry?.rotation;\n    if (value === this.data.fieldValue || value === undefined) {\n      if (!this._hasValueFromXFA && rotation === undefined) {\n        return null;\n      }\n      value ||= this.data.fieldValue;\n    }\n    if (rotation === undefined && !this._hasValueFromXFA && Array.isArray(value) && Array.isArray(this.data.fieldValue) && value.length === this.data.fieldValue.length && value.every((x, i) => x === this.data.fieldValue[i])) {\n      return null;\n    }\n    if (rotation === undefined) {\n      rotation = this.rotation;\n    }\n    let appearance = null;\n    if (!this._needAppearances) {\n      appearance = await this._getAppearance(evaluator, task, RenderingIntentFlag.SAVE, annotationStorage);\n      if (appearance === null) {\n        return null;\n      }\n    } else {}\n    let needAppearances = false;\n    if (appearance?.needAppearances) {\n      needAppearances = true;\n      appearance = null;\n    }\n    const {\n      xref\n    } = evaluator;\n    const originalDict = xref.fetchIfRef(this.ref);\n    if (!(originalDict instanceof Dict)) {\n      return null;\n    }\n    const dict = new Dict(xref);\n    for (const key of originalDict.getKeys()) {\n      if (key !== \"AP\") {\n        dict.set(key, originalDict.getRaw(key));\n      }\n    }\n    const xfa = {\n      path: this.data.fieldName,\n      value\n    };\n    const encoder = val => {\n      return isAscii(val) ? val : stringToUTF16String(val, true);\n    };\n    dict.set(\"V\", Array.isArray(value) ? value.map(encoder) : encoder(value));\n    this.amendSavedDict(annotationStorage, dict);\n    const maybeMK = this._getMKDict(rotation);\n    if (maybeMK) {\n      dict.set(\"MK\", maybeMK);\n    }\n    const buffer = [];\n    const changes = [{\n      ref: this.ref,\n      data: \"\",\n      xfa,\n      needAppearances\n    }];\n    if (appearance !== null) {\n      const newRef = xref.getNewTemporaryRef();\n      const AP = new Dict(xref);\n      dict.set(\"AP\", AP);\n      AP.set(\"N\", newRef);\n      const resources = this._getSaveFieldResources(xref);\n      const appearanceStream = new StringStream(appearance);\n      const appearanceDict = appearanceStream.dict = new Dict(xref);\n      appearanceDict.set(\"Subtype\", Name.get(\"Form\"));\n      appearanceDict.set(\"Resources\", resources);\n      appearanceDict.set(\"BBox\", [0, 0, this.data.rect[2] - this.data.rect[0], this.data.rect[3] - this.data.rect[1]]);\n      const rotationMatrix = this.getRotationMatrix(annotationStorage);\n      if (rotationMatrix !== IDENTITY_MATRIX) {\n        appearanceDict.set(\"Matrix\", rotationMatrix);\n      }\n      await writeObject(newRef, appearanceStream, buffer, xref);\n      changes.push({\n        ref: newRef,\n        data: buffer.join(\"\"),\n        xfa: null,\n        needAppearances: false\n      });\n      buffer.length = 0;\n    }\n    dict.set(\"M\", `D:${getModificationDate()}`);\n    await writeObject(this.ref, dict, buffer, xref);\n    changes[0].data = buffer.join(\"\");\n    return changes;\n  }\n  async _getAppearance(evaluator, task, intent, annotationStorage) {\n    const isPassword = this.hasFieldFlag(AnnotationFieldFlag.PASSWORD);\n    if (isPassword) {\n      return null;\n    }\n    const storageEntry = annotationStorage?.get(this.data.id);\n    let value, rotation;\n    if (storageEntry) {\n      value = storageEntry.formattedValue || storageEntry.value;\n      rotation = storageEntry.rotation;\n    }\n    if (rotation === undefined && value === undefined && !this._needAppearances) {\n      if (!this._hasValueFromXFA || this.appearance) {\n        return null;\n      }\n    }\n    const colors = this.getBorderAndBackgroundAppearances(annotationStorage);\n    if (value === undefined) {\n      value = this.data.fieldValue;\n      if (!value) {\n        return `/Tx BMC q ${colors}Q EMC`;\n      }\n    }\n    if (Array.isArray(value) && value.length === 1) {\n      value = value[0];\n    }\n    assert(typeof value === \"string\", \"Expected `value` to be a string.\");\n    value = value.trim();\n    if (this.data.combo) {\n      const option = this.data.options.find(({\n        exportValue\n      }) => value === exportValue);\n      value = option?.displayValue || value;\n    }\n    if (value === \"\") {\n      return `/Tx BMC q ${colors}Q EMC`;\n    }\n    if (rotation === undefined) {\n      rotation = this.rotation;\n    }\n    let lineCount = -1;\n    let lines;\n    if (this.data.multiLine) {\n      lines = value.split(/\\r\\n?|\\n/).map(line => line.normalize(\"NFC\"));\n      lineCount = lines.length;\n    } else {\n      lines = [value.replace(/\\r\\n?|\\n/, \"\").normalize(\"NFC\")];\n    }\n    const defaultPadding = 1;\n    const defaultHPadding = 2;\n    let totalHeight = this.data.rect[3] - this.data.rect[1];\n    let totalWidth = this.data.rect[2] - this.data.rect[0];\n    if (rotation === 90 || rotation === 270) {\n      [totalWidth, totalHeight] = [totalHeight, totalWidth];\n    }\n    if (!this._defaultAppearance) {\n      this.data.defaultAppearanceData = parseDefaultAppearance(this._defaultAppearance = \"/Helvetica 0 Tf 0 g\");\n    }\n    let font = await WidgetAnnotation._getFontData(evaluator, task, this.data.defaultAppearanceData, this._fieldResources.mergedResources);\n    let defaultAppearance, fontSize, lineHeight;\n    const encodedLines = [];\n    let encodingError = false;\n    for (const line of lines) {\n      const encodedString = font.encodeString(line);\n      if (encodedString.length > 1) {\n        encodingError = true;\n      }\n      encodedLines.push(encodedString.join(\"\"));\n    }\n    if (encodingError && intent & RenderingIntentFlag.SAVE) {\n      return {\n        needAppearances: true\n      };\n    }\n    if (encodingError && this._isOffscreenCanvasSupported) {\n      const fontFamily = this.data.comb ? \"monospace\" : \"sans-serif\";\n      const fakeUnicodeFont = new FakeUnicodeFont(evaluator.xref, fontFamily);\n      const resources = fakeUnicodeFont.createFontResources(lines.join(\"\"));\n      const newFont = resources.getRaw(\"Font\");\n      if (this._fieldResources.mergedResources.has(\"Font\")) {\n        const oldFont = this._fieldResources.mergedResources.get(\"Font\");\n        for (const key of newFont.getKeys()) {\n          oldFont.set(key, newFont.getRaw(key));\n        }\n      } else {\n        this._fieldResources.mergedResources.set(\"Font\", newFont);\n      }\n      const fontName = fakeUnicodeFont.fontName.name;\n      font = await WidgetAnnotation._getFontData(evaluator, task, {\n        fontName,\n        fontSize: 0\n      }, resources);\n      for (let i = 0, ii = encodedLines.length; i < ii; i++) {\n        encodedLines[i] = stringToUTF16String(lines[i]);\n      }\n      const savedDefaultAppearance = Object.assign(Object.create(null), this.data.defaultAppearanceData);\n      this.data.defaultAppearanceData.fontSize = 0;\n      this.data.defaultAppearanceData.fontName = fontName;\n      [defaultAppearance, fontSize, lineHeight] = this._computeFontSize(totalHeight - 2 * defaultPadding, totalWidth - 2 * defaultHPadding, value, font, lineCount);\n      this.data.defaultAppearanceData = savedDefaultAppearance;\n    } else {\n      if (!this._isOffscreenCanvasSupported) {\n        warn(\"_getAppearance: OffscreenCanvas is not supported, annotation may not render correctly.\");\n      }\n      [defaultAppearance, fontSize, lineHeight] = this._computeFontSize(totalHeight - 2 * defaultPadding, totalWidth - 2 * defaultHPadding, value, font, lineCount);\n    }\n    let descent = font.descent;\n    if (isNaN(descent)) {\n      descent = BASELINE_FACTOR * lineHeight;\n    } else {\n      descent = Math.max(BASELINE_FACTOR * lineHeight, Math.abs(descent) * fontSize);\n    }\n    const defaultVPadding = Math.min(Math.floor((totalHeight - fontSize) / 2), defaultPadding);\n    const alignment = this.data.textAlignment;\n    if (this.data.multiLine) {\n      return this._getMultilineAppearance(defaultAppearance, encodedLines, font, fontSize, totalWidth, totalHeight, alignment, defaultHPadding, defaultVPadding, descent, lineHeight, annotationStorage);\n    }\n    if (this.data.comb) {\n      return this._getCombAppearance(defaultAppearance, font, encodedLines[0], fontSize, totalWidth, totalHeight, defaultHPadding, defaultVPadding, descent, lineHeight, annotationStorage);\n    }\n    const bottomPadding = defaultVPadding + descent;\n    if (alignment === 0 || alignment > 2) {\n      return `/Tx BMC q ${colors}BT ` + defaultAppearance + ` 1 0 0 1 ${numberToString(defaultHPadding)} ${numberToString(bottomPadding)} Tm (${escapeString(encodedLines[0])}) Tj` + \" ET Q EMC\";\n    }\n    const prevInfo = {\n      shift: 0\n    };\n    const renderedText = this._renderText(encodedLines[0], font, fontSize, totalWidth, alignment, prevInfo, defaultHPadding, bottomPadding);\n    return `/Tx BMC q ${colors}BT ` + defaultAppearance + ` 1 0 0 1 0 0 Tm ${renderedText}` + \" ET Q EMC\";\n  }\n  static async _getFontData(evaluator, task, appearanceData, resources) {\n    const operatorList = new OperatorList();\n    const initialState = {\n      font: null,\n      clone() {\n        return this;\n      }\n    };\n    const {\n      fontName,\n      fontSize\n    } = appearanceData;\n    await evaluator.handleSetFont(resources, [fontName && Name.get(fontName), fontSize], null, operatorList, task, initialState, null);\n    return initialState.font;\n  }\n  _getTextWidth(text, font) {\n    return font.charsToGlyphs(text).reduce((width, glyph) => width + glyph.width, 0) / 1000;\n  }\n  _computeFontSize(height, width, text, font, lineCount) {\n    let {\n      fontSize\n    } = this.data.defaultAppearanceData;\n    let lineHeight = (fontSize || 12) * LINE_FACTOR,\n      numberOfLines = Math.round(height / lineHeight);\n    if (!fontSize) {\n      const roundWithTwoDigits = x => Math.floor(x * 100) / 100;\n      if (lineCount === -1) {\n        const textWidth = this._getTextWidth(text, font);\n        fontSize = roundWithTwoDigits(Math.min(height / LINE_FACTOR, textWidth > width ? width / textWidth : Infinity));\n        numberOfLines = 1;\n      } else {\n        const lines = text.split(/\\r\\n?|\\n/);\n        const cachedLines = [];\n        for (const line of lines) {\n          const encoded = font.encodeString(line).join(\"\");\n          const glyphs = font.charsToGlyphs(encoded);\n          const positions = font.getCharPositions(encoded);\n          cachedLines.push({\n            line: encoded,\n            glyphs,\n            positions\n          });\n        }\n        const isTooBig = fsize => {\n          let totalHeight = 0;\n          for (const cache of cachedLines) {\n            const chunks = this._splitLine(null, font, fsize, width, cache);\n            totalHeight += chunks.length * fsize;\n            if (totalHeight > height) {\n              return true;\n            }\n          }\n          return false;\n        };\n        numberOfLines = Math.max(numberOfLines, lineCount);\n        while (true) {\n          lineHeight = height / numberOfLines;\n          fontSize = roundWithTwoDigits(lineHeight / LINE_FACTOR);\n          if (isTooBig(fontSize)) {\n            numberOfLines++;\n            continue;\n          }\n          break;\n        }\n      }\n      const {\n        fontName,\n        fontColor\n      } = this.data.defaultAppearanceData;\n      this._defaultAppearance = createDefaultAppearance({\n        fontSize,\n        fontName,\n        fontColor\n      });\n    }\n    return [this._defaultAppearance, fontSize, height / numberOfLines];\n  }\n  _renderText(text, font, fontSize, totalWidth, alignment, prevInfo, hPadding, vPadding) {\n    let shift;\n    if (alignment === 1) {\n      const width = this._getTextWidth(text, font) * fontSize;\n      shift = (totalWidth - width) / 2;\n    } else if (alignment === 2) {\n      const width = this._getTextWidth(text, font) * fontSize;\n      shift = totalWidth - width - hPadding;\n    } else {\n      shift = hPadding;\n    }\n    const shiftStr = numberToString(shift - prevInfo.shift);\n    prevInfo.shift = shift;\n    vPadding = numberToString(vPadding);\n    return `${shiftStr} ${vPadding} Td (${escapeString(text)}) Tj`;\n  }\n  _getSaveFieldResources(xref) {\n    const {\n      localResources,\n      appearanceResources,\n      acroFormResources\n    } = this._fieldResources;\n    const fontName = this.data.defaultAppearanceData?.fontName;\n    if (!fontName) {\n      return localResources || Dict.empty;\n    }\n    for (const resources of [localResources, appearanceResources]) {\n      if (resources instanceof Dict) {\n        const localFont = resources.get(\"Font\");\n        if (localFont instanceof Dict && localFont.has(fontName)) {\n          return resources;\n        }\n      }\n    }\n    if (acroFormResources instanceof Dict) {\n      const acroFormFont = acroFormResources.get(\"Font\");\n      if (acroFormFont instanceof Dict && acroFormFont.has(fontName)) {\n        const subFontDict = new Dict(xref);\n        subFontDict.set(fontName, acroFormFont.getRaw(fontName));\n        const subResourcesDict = new Dict(xref);\n        subResourcesDict.set(\"Font\", subFontDict);\n        return Dict.merge({\n          xref,\n          dictArray: [subResourcesDict, localResources],\n          mergeSubDicts: true\n        });\n      }\n    }\n    return localResources || Dict.empty;\n  }\n  getFieldObject() {\n    return null;\n  }\n}\nclass TextWidgetAnnotation extends WidgetAnnotation {\n  constructor(params) {\n    super(params);\n    this.data.hasOwnCanvas = this.data.readOnly && !this.data.noHTML;\n    this._hasText = true;\n    const dict = params.dict;\n    if (typeof this.data.fieldValue !== \"string\") {\n      this.data.fieldValue = \"\";\n    }\n    let alignment = getInheritableProperty({\n      dict,\n      key: \"Q\"\n    });\n    if (!Number.isInteger(alignment) || alignment < 0 || alignment > 2) {\n      alignment = null;\n    }\n    this.data.textAlignment = alignment;\n    let maximumLength = getInheritableProperty({\n      dict,\n      key: \"MaxLen\"\n    });\n    if (!Number.isInteger(maximumLength) || maximumLength < 0) {\n      maximumLength = 0;\n    }\n    this.data.maxLen = maximumLength;\n    this.data.multiLine = this.hasFieldFlag(AnnotationFieldFlag.MULTILINE);\n    this.data.comb = this.hasFieldFlag(AnnotationFieldFlag.COMB) && !this.hasFieldFlag(AnnotationFieldFlag.MULTILINE) && !this.hasFieldFlag(AnnotationFieldFlag.PASSWORD) && !this.hasFieldFlag(AnnotationFieldFlag.FILESELECT) && this.data.maxLen !== 0;\n    this.data.doNotScroll = this.hasFieldFlag(AnnotationFieldFlag.DONOTSCROLL);\n  }\n  get hasTextContent() {\n    return !!this.appearance && !this._needAppearances;\n  }\n  _getCombAppearance(defaultAppearance, font, text, fontSize, width, height, hPadding, vPadding, descent, lineHeight, annotationStorage) {\n    const combWidth = width / this.data.maxLen;\n    const colors = this.getBorderAndBackgroundAppearances(annotationStorage);\n    const buf = [];\n    const positions = font.getCharPositions(text);\n    for (const [start, end] of positions) {\n      buf.push(`(${escapeString(text.substring(start, end))}) Tj`);\n    }\n    const renderedComb = buf.join(` ${numberToString(combWidth)} 0 Td `);\n    return `/Tx BMC q ${colors}BT ` + defaultAppearance + ` 1 0 0 1 ${numberToString(hPadding)} ${numberToString(vPadding + descent)} Tm ${renderedComb}` + \" ET Q EMC\";\n  }\n  _getMultilineAppearance(defaultAppearance, lines, font, fontSize, width, height, alignment, hPadding, vPadding, descent, lineHeight, annotationStorage) {\n    const buf = [];\n    const totalWidth = width - 2 * hPadding;\n    const prevInfo = {\n      shift: 0\n    };\n    for (let i = 0, ii = lines.length; i < ii; i++) {\n      const line = lines[i];\n      const chunks = this._splitLine(line, font, fontSize, totalWidth);\n      for (let j = 0, jj = chunks.length; j < jj; j++) {\n        const chunk = chunks[j];\n        const vShift = i === 0 && j === 0 ? -vPadding - (lineHeight - descent) : -lineHeight;\n        buf.push(this._renderText(chunk, font, fontSize, width, alignment, prevInfo, hPadding, vShift));\n      }\n    }\n    const colors = this.getBorderAndBackgroundAppearances(annotationStorage);\n    const renderedText = buf.join(\"\\n\");\n    return `/Tx BMC q ${colors}BT ` + defaultAppearance + ` 1 0 0 1 0 ${numberToString(height)} Tm ${renderedText}` + \" ET Q EMC\";\n  }\n  _splitLine(line, font, fontSize, width, cache = {}) {\n    line = cache.line || line;\n    const glyphs = cache.glyphs || font.charsToGlyphs(line);\n    if (glyphs.length <= 1) {\n      return [line];\n    }\n    const positions = cache.positions || font.getCharPositions(line);\n    const scale = fontSize / 1000;\n    const chunks = [];\n    let lastSpacePosInStringStart = -1,\n      lastSpacePosInStringEnd = -1,\n      lastSpacePos = -1,\n      startChunk = 0,\n      currentWidth = 0;\n    for (let i = 0, ii = glyphs.length; i < ii; i++) {\n      const [start, end] = positions[i];\n      const glyph = glyphs[i];\n      const glyphWidth = glyph.width * scale;\n      if (glyph.unicode === \" \") {\n        if (currentWidth + glyphWidth > width) {\n          chunks.push(line.substring(startChunk, start));\n          startChunk = start;\n          currentWidth = glyphWidth;\n          lastSpacePosInStringStart = -1;\n          lastSpacePos = -1;\n        } else {\n          currentWidth += glyphWidth;\n          lastSpacePosInStringStart = start;\n          lastSpacePosInStringEnd = end;\n          lastSpacePos = i;\n        }\n      } else if (currentWidth + glyphWidth > width) {\n        if (lastSpacePosInStringStart !== -1) {\n          chunks.push(line.substring(startChunk, lastSpacePosInStringEnd));\n          startChunk = lastSpacePosInStringEnd;\n          i = lastSpacePos + 1;\n          lastSpacePosInStringStart = -1;\n          currentWidth = 0;\n        } else {\n          chunks.push(line.substring(startChunk, start));\n          startChunk = start;\n          currentWidth = glyphWidth;\n        }\n      } else {\n        currentWidth += glyphWidth;\n      }\n    }\n    if (startChunk < line.length) {\n      chunks.push(line.substring(startChunk, line.length));\n    }\n    return chunks;\n  }\n  getFieldObject() {\n    return {\n      id: this.data.id,\n      value: this.data.fieldValue,\n      defaultValue: this.data.defaultFieldValue || \"\",\n      multiline: this.data.multiLine,\n      password: this.hasFieldFlag(AnnotationFieldFlag.PASSWORD),\n      charLimit: this.data.maxLen,\n      comb: this.data.comb,\n      editable: !this.data.readOnly,\n      hidden: this.data.hidden,\n      name: this.data.fieldName,\n      rect: this.data.rect,\n      actions: this.data.actions,\n      page: this.data.pageIndex,\n      strokeColor: this.data.borderColor,\n      fillColor: this.data.backgroundColor,\n      rotation: this.rotation,\n      type: \"text\"\n    };\n  }\n}\nclass ButtonWidgetAnnotation extends WidgetAnnotation {\n  constructor(params) {\n    super(params);\n    this.checkedAppearance = null;\n    this.uncheckedAppearance = null;\n    this.data.checkBox = !this.hasFieldFlag(AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);\n    this.data.radioButton = this.hasFieldFlag(AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);\n    this.data.pushButton = this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);\n    this.data.isTooltipOnly = false;\n    if (this.data.checkBox) {\n      this._processCheckBox(params);\n    } else if (this.data.radioButton) {\n      this._processRadioButton(params);\n    } else if (this.data.pushButton) {\n      this.data.hasOwnCanvas = true;\n      this.data.noHTML = false;\n      this._processPushButton(params);\n    } else {\n      warn(\"Invalid field flags for button widget annotation\");\n    }\n  }\n  async getOperatorList(evaluator, task, intent, renderForms, annotationStorage) {\n    if (this.data.pushButton) {\n      return super.getOperatorList(evaluator, task, intent, false, annotationStorage);\n    }\n    let value = null;\n    let rotation = null;\n    if (annotationStorage) {\n      const storageEntry = annotationStorage.get(this.data.id);\n      value = storageEntry ? storageEntry.value : null;\n      rotation = storageEntry ? storageEntry.rotation : null;\n    }\n    if (value === null && this.appearance) {\n      return super.getOperatorList(evaluator, task, intent, renderForms, annotationStorage);\n    }\n    if (value === null || value === undefined) {\n      value = this.data.checkBox ? this.data.fieldValue === this.data.exportValue : this.data.fieldValue === this.data.buttonValue;\n    }\n    const appearance = value ? this.checkedAppearance : this.uncheckedAppearance;\n    if (appearance) {\n      const savedAppearance = this.appearance;\n      const savedMatrix = appearance.dict.getArray(\"Matrix\") || IDENTITY_MATRIX;\n      if (rotation) {\n        appearance.dict.set(\"Matrix\", this.getRotationMatrix(annotationStorage));\n      }\n      this.appearance = appearance;\n      const operatorList = super.getOperatorList(evaluator, task, intent, renderForms, annotationStorage);\n      this.appearance = savedAppearance;\n      appearance.dict.set(\"Matrix\", savedMatrix);\n      return operatorList;\n    }\n    return {\n      opList: new OperatorList(),\n      separateForm: false,\n      separateCanvas: false\n    };\n  }\n  async save(evaluator, task, annotationStorage) {\n    if (this.data.checkBox) {\n      return this._saveCheckbox(evaluator, task, annotationStorage);\n    }\n    if (this.data.radioButton) {\n      return this._saveRadioButton(evaluator, task, annotationStorage);\n    }\n    return null;\n  }\n  async _saveCheckbox(evaluator, task, annotationStorage) {\n    if (!annotationStorage) {\n      return null;\n    }\n    const storageEntry = annotationStorage.get(this.data.id);\n    let rotation = storageEntry?.rotation,\n      value = storageEntry?.value;\n    if (rotation === undefined) {\n      if (value === undefined) {\n        return null;\n      }\n      const defaultValue = this.data.fieldValue === this.data.exportValue;\n      if (defaultValue === value) {\n        return null;\n      }\n    }\n    const dict = evaluator.xref.fetchIfRef(this.ref);\n    if (!(dict instanceof Dict)) {\n      return null;\n    }\n    if (rotation === undefined) {\n      rotation = this.rotation;\n    }\n    if (value === undefined) {\n      value = this.data.fieldValue === this.data.exportValue;\n    }\n    const xfa = {\n      path: this.data.fieldName,\n      value: value ? this.data.exportValue : \"\"\n    };\n    const name = Name.get(value ? this.data.exportValue : \"Off\");\n    dict.set(\"V\", name);\n    dict.set(\"AS\", name);\n    dict.set(\"M\", `D:${getModificationDate()}`);\n    const maybeMK = this._getMKDict(rotation);\n    if (maybeMK) {\n      dict.set(\"MK\", maybeMK);\n    }\n    const buffer = [];\n    await writeObject(this.ref, dict, buffer, evaluator.xref);\n    return [{\n      ref: this.ref,\n      data: buffer.join(\"\"),\n      xfa\n    }];\n  }\n  async _saveRadioButton(evaluator, task, annotationStorage) {\n    if (!annotationStorage) {\n      return null;\n    }\n    const storageEntry = annotationStorage.get(this.data.id);\n    let rotation = storageEntry?.rotation,\n      value = storageEntry?.value;\n    if (rotation === undefined) {\n      if (value === undefined) {\n        return null;\n      }\n      const defaultValue = this.data.fieldValue === this.data.buttonValue;\n      if (defaultValue === value) {\n        return null;\n      }\n    }\n    const dict = evaluator.xref.fetchIfRef(this.ref);\n    if (!(dict instanceof Dict)) {\n      return null;\n    }\n    if (value === undefined) {\n      value = this.data.fieldValue === this.data.buttonValue;\n    }\n    if (rotation === undefined) {\n      rotation = this.rotation;\n    }\n    const xfa = {\n      path: this.data.fieldName,\n      value: value ? this.data.buttonValue : \"\"\n    };\n    const name = Name.get(value ? this.data.buttonValue : \"Off\");\n    const buffer = [];\n    let parentData = null;\n    if (value) {\n      if (this.parent instanceof Ref) {\n        const parent = evaluator.xref.fetch(this.parent);\n        parent.set(\"V\", name);\n        await writeObject(this.parent, parent, buffer, evaluator.xref);\n        parentData = buffer.join(\"\");\n        buffer.length = 0;\n      } else if (this.parent instanceof Dict) {\n        this.parent.set(\"V\", name);\n      }\n    }\n    dict.set(\"AS\", name);\n    dict.set(\"M\", `D:${getModificationDate()}`);\n    const maybeMK = this._getMKDict(rotation);\n    if (maybeMK) {\n      dict.set(\"MK\", maybeMK);\n    }\n    await writeObject(this.ref, dict, buffer, evaluator.xref);\n    const newRefs = [{\n      ref: this.ref,\n      data: buffer.join(\"\"),\n      xfa\n    }];\n    if (parentData) {\n      newRefs.push({\n        ref: this.parent,\n        data: parentData,\n        xfa: null\n      });\n    }\n    return newRefs;\n  }\n  _getDefaultCheckedAppearance(params, type) {\n    const width = this.data.rect[2] - this.data.rect[0];\n    const height = this.data.rect[3] - this.data.rect[1];\n    const bbox = [0, 0, width, height];\n    const FONT_RATIO = 0.8;\n    const fontSize = Math.min(width, height) * FONT_RATIO;\n    let metrics, char;\n    if (type === \"check\") {\n      metrics = {\n        width: 0.755 * fontSize,\n        height: 0.705 * fontSize\n      };\n      char = \"\\x33\";\n    } else if (type === \"disc\") {\n      metrics = {\n        width: 0.791 * fontSize,\n        height: 0.705 * fontSize\n      };\n      char = \"\\x6C\";\n    } else {\n      unreachable(`_getDefaultCheckedAppearance - unsupported type: ${type}`);\n    }\n    const xShift = numberToString((width - metrics.width) / 2);\n    const yShift = numberToString((height - metrics.height) / 2);\n    const appearance = `q BT /PdfJsZaDb ${fontSize} Tf 0 g ${xShift} ${yShift} Td (${char}) Tj ET Q`;\n    const appearanceStreamDict = new Dict(params.xref);\n    appearanceStreamDict.set(\"FormType\", 1);\n    appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n    appearanceStreamDict.set(\"Type\", Name.get(\"XObject\"));\n    appearanceStreamDict.set(\"BBox\", bbox);\n    appearanceStreamDict.set(\"Matrix\", [1, 0, 0, 1, 0, 0]);\n    appearanceStreamDict.set(\"Length\", appearance.length);\n    const resources = new Dict(params.xref);\n    const font = new Dict(params.xref);\n    font.set(\"PdfJsZaDb\", this.fallbackFontDict);\n    resources.set(\"Font\", font);\n    appearanceStreamDict.set(\"Resources\", resources);\n    this.checkedAppearance = new StringStream(appearance);\n    this.checkedAppearance.dict = appearanceStreamDict;\n    this._streams.push(this.checkedAppearance);\n  }\n  _processCheckBox(params) {\n    const customAppearance = params.dict.get(\"AP\");\n    if (!(customAppearance instanceof Dict)) {\n      return;\n    }\n    const normalAppearance = customAppearance.get(\"N\");\n    if (!(normalAppearance instanceof Dict)) {\n      return;\n    }\n    const asValue = this._decodeFormValue(params.dict.get(\"AS\"));\n    if (typeof asValue === \"string\") {\n      this.data.fieldValue = asValue;\n    }\n    const yes = this.data.fieldValue !== null && this.data.fieldValue !== \"Off\" ? this.data.fieldValue : \"Yes\";\n    const exportValues = normalAppearance.getKeys();\n    if (exportValues.length === 0) {\n      exportValues.push(\"Off\", yes);\n    } else if (exportValues.length === 1) {\n      if (exportValues[0] === \"Off\") {\n        exportValues.push(yes);\n      } else {\n        exportValues.unshift(\"Off\");\n      }\n    } else if (exportValues.includes(yes)) {\n      exportValues.length = 0;\n      exportValues.push(\"Off\", yes);\n    } else {\n      const otherYes = exportValues.find(v => v !== \"Off\");\n      exportValues.length = 0;\n      exportValues.push(\"Off\", otherYes);\n    }\n    if (!exportValues.includes(this.data.fieldValue)) {\n      this.data.fieldValue = \"Off\";\n    }\n    this.data.exportValue = exportValues[1];\n    const checkedAppearance = normalAppearance.get(this.data.exportValue);\n    this.checkedAppearance = checkedAppearance instanceof BaseStream ? checkedAppearance : null;\n    const uncheckedAppearance = normalAppearance.get(\"Off\");\n    this.uncheckedAppearance = uncheckedAppearance instanceof BaseStream ? uncheckedAppearance : null;\n    if (this.checkedAppearance) {\n      this._streams.push(this.checkedAppearance);\n    } else {\n      this._getDefaultCheckedAppearance(params, \"check\");\n    }\n    if (this.uncheckedAppearance) {\n      this._streams.push(this.uncheckedAppearance);\n    }\n    this._fallbackFontDict = this.fallbackFontDict;\n    if (this.data.defaultFieldValue === null) {\n      this.data.defaultFieldValue = \"Off\";\n    }\n  }\n  _processRadioButton(params) {\n    this.data.buttonValue = null;\n    const fieldParent = params.dict.get(\"Parent\");\n    if (fieldParent instanceof Dict) {\n      this.parent = params.dict.getRaw(\"Parent\");\n      const fieldParentValue = fieldParent.get(\"V\");\n      if (fieldParentValue instanceof Name) {\n        this.data.fieldValue = this._decodeFormValue(fieldParentValue);\n      }\n    }\n    const appearanceStates = params.dict.get(\"AP\");\n    if (!(appearanceStates instanceof Dict)) {\n      return;\n    }\n    const normalAppearance = appearanceStates.get(\"N\");\n    if (!(normalAppearance instanceof Dict)) {\n      return;\n    }\n    for (const key of normalAppearance.getKeys()) {\n      if (key !== \"Off\") {\n        this.data.buttonValue = this._decodeFormValue(key);\n        break;\n      }\n    }\n    const checkedAppearance = normalAppearance.get(this.data.buttonValue);\n    this.checkedAppearance = checkedAppearance instanceof BaseStream ? checkedAppearance : null;\n    const uncheckedAppearance = normalAppearance.get(\"Off\");\n    this.uncheckedAppearance = uncheckedAppearance instanceof BaseStream ? uncheckedAppearance : null;\n    if (this.checkedAppearance) {\n      this._streams.push(this.checkedAppearance);\n    } else {\n      this._getDefaultCheckedAppearance(params, \"disc\");\n    }\n    if (this.uncheckedAppearance) {\n      this._streams.push(this.uncheckedAppearance);\n    }\n    this._fallbackFontDict = this.fallbackFontDict;\n    if (this.data.defaultFieldValue === null) {\n      this.data.defaultFieldValue = \"Off\";\n    }\n  }\n  _processPushButton(params) {\n    const {\n      dict,\n      annotationGlobals\n    } = params;\n    if (!dict.has(\"A\") && !dict.has(\"AA\") && !this.data.alternativeText) {\n      warn(\"Push buttons without action dictionaries are not supported\");\n      return;\n    }\n    this.data.isTooltipOnly = !dict.has(\"A\") && !dict.has(\"AA\");\n    Catalog.parseDestDictionary({\n      destDict: dict,\n      resultObj: this.data,\n      docBaseUrl: annotationGlobals.baseUrl,\n      docAttachments: annotationGlobals.attachments\n    });\n  }\n  getFieldObject() {\n    let type = \"button\";\n    let exportValues;\n    if (this.data.checkBox) {\n      type = \"checkbox\";\n      exportValues = this.data.exportValue;\n    } else if (this.data.radioButton) {\n      type = \"radiobutton\";\n      exportValues = this.data.buttonValue;\n    }\n    return {\n      id: this.data.id,\n      value: this.data.fieldValue || \"Off\",\n      defaultValue: this.data.defaultFieldValue,\n      exportValues,\n      editable: !this.data.readOnly,\n      name: this.data.fieldName,\n      rect: this.data.rect,\n      hidden: this.data.hidden,\n      actions: this.data.actions,\n      page: this.data.pageIndex,\n      strokeColor: this.data.borderColor,\n      fillColor: this.data.backgroundColor,\n      rotation: this.rotation,\n      type\n    };\n  }\n  get fallbackFontDict() {\n    const dict = new Dict();\n    dict.set(\"BaseFont\", Name.get(\"ZapfDingbats\"));\n    dict.set(\"Type\", Name.get(\"FallbackType\"));\n    dict.set(\"Subtype\", Name.get(\"FallbackType\"));\n    dict.set(\"Encoding\", Name.get(\"ZapfDingbatsEncoding\"));\n    return shadow(this, \"fallbackFontDict\", dict);\n  }\n}\nclass ChoiceWidgetAnnotation extends WidgetAnnotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict,\n      xref\n    } = params;\n    this.indices = dict.getArray(\"I\");\n    this.hasIndices = Array.isArray(this.indices) && this.indices.length > 0;\n    this.data.options = [];\n    const options = getInheritableProperty({\n      dict,\n      key: \"Opt\"\n    });\n    if (Array.isArray(options)) {\n      for (let i = 0, ii = options.length; i < ii; i++) {\n        const option = xref.fetchIfRef(options[i]);\n        const isOptionArray = Array.isArray(option);\n        this.data.options[i] = {\n          exportValue: this._decodeFormValue(isOptionArray ? xref.fetchIfRef(option[0]) : option),\n          displayValue: this._decodeFormValue(isOptionArray ? xref.fetchIfRef(option[1]) : option)\n        };\n      }\n    }\n    if (!this.hasIndices) {\n      if (typeof this.data.fieldValue === \"string\") {\n        this.data.fieldValue = [this.data.fieldValue];\n      } else if (!this.data.fieldValue) {\n        this.data.fieldValue = [];\n      }\n    } else {\n      this.data.fieldValue = [];\n      const ii = this.data.options.length;\n      for (const i of this.indices) {\n        if (Number.isInteger(i) && i >= 0 && i < ii) {\n          this.data.fieldValue.push(this.data.options[i].exportValue);\n        }\n      }\n    }\n    this.data.combo = this.hasFieldFlag(AnnotationFieldFlag.COMBO);\n    this.data.multiSelect = this.hasFieldFlag(AnnotationFieldFlag.MULTISELECT);\n    this._hasText = true;\n  }\n  getFieldObject() {\n    const type = this.data.combo ? \"combobox\" : \"listbox\";\n    const value = this.data.fieldValue.length > 0 ? this.data.fieldValue[0] : null;\n    return {\n      id: this.data.id,\n      value,\n      defaultValue: this.data.defaultFieldValue,\n      editable: !this.data.readOnly,\n      name: this.data.fieldName,\n      rect: this.data.rect,\n      numItems: this.data.fieldValue.length,\n      multipleSelection: this.data.multiSelect,\n      hidden: this.data.hidden,\n      actions: this.data.actions,\n      items: this.data.options,\n      page: this.data.pageIndex,\n      strokeColor: this.data.borderColor,\n      fillColor: this.data.backgroundColor,\n      rotation: this.rotation,\n      type\n    };\n  }\n  amendSavedDict(annotationStorage, dict) {\n    if (!this.hasIndices) {\n      return;\n    }\n    let values = annotationStorage?.get(this.data.id)?.value;\n    if (!Array.isArray(values)) {\n      values = [values];\n    }\n    const indices = [];\n    const {\n      options\n    } = this.data;\n    for (let i = 0, j = 0, ii = options.length; i < ii; i++) {\n      if (options[i].exportValue === values[j]) {\n        indices.push(i);\n        j += 1;\n      }\n    }\n    dict.set(\"I\", indices);\n  }\n  async _getAppearance(evaluator, task, intent, annotationStorage) {\n    if (this.data.combo) {\n      return super._getAppearance(evaluator, task, intent, annotationStorage);\n    }\n    let exportedValue, rotation;\n    const storageEntry = annotationStorage?.get(this.data.id);\n    if (storageEntry) {\n      rotation = storageEntry.rotation;\n      exportedValue = storageEntry.value;\n    }\n    if (rotation === undefined && exportedValue === undefined && !this._needAppearances) {\n      return null;\n    }\n    if (exportedValue === undefined) {\n      exportedValue = this.data.fieldValue;\n    } else if (!Array.isArray(exportedValue)) {\n      exportedValue = [exportedValue];\n    }\n    const defaultPadding = 1;\n    const defaultHPadding = 2;\n    let totalHeight = this.data.rect[3] - this.data.rect[1];\n    let totalWidth = this.data.rect[2] - this.data.rect[0];\n    if (rotation === 90 || rotation === 270) {\n      [totalWidth, totalHeight] = [totalHeight, totalWidth];\n    }\n    const lineCount = this.data.options.length;\n    const valueIndices = [];\n    for (let i = 0; i < lineCount; i++) {\n      const {\n        exportValue\n      } = this.data.options[i];\n      if (exportedValue.includes(exportValue)) {\n        valueIndices.push(i);\n      }\n    }\n    if (!this._defaultAppearance) {\n      this.data.defaultAppearanceData = parseDefaultAppearance(this._defaultAppearance = \"/Helvetica 0 Tf 0 g\");\n    }\n    const font = await WidgetAnnotation._getFontData(evaluator, task, this.data.defaultAppearanceData, this._fieldResources.mergedResources);\n    let defaultAppearance;\n    let {\n      fontSize\n    } = this.data.defaultAppearanceData;\n    if (!fontSize) {\n      const lineHeight = (totalHeight - defaultPadding) / lineCount;\n      let lineWidth = -1;\n      let value;\n      for (const {\n        displayValue\n      } of this.data.options) {\n        const width = this._getTextWidth(displayValue, font);\n        if (width > lineWidth) {\n          lineWidth = width;\n          value = displayValue;\n        }\n      }\n      [defaultAppearance, fontSize] = this._computeFontSize(lineHeight, totalWidth - 2 * defaultHPadding, value, font, -1);\n    } else {\n      defaultAppearance = this._defaultAppearance;\n    }\n    const lineHeight = fontSize * LINE_FACTOR;\n    const vPadding = (lineHeight - fontSize) / 2;\n    const numberOfVisibleLines = Math.floor(totalHeight / lineHeight);\n    let firstIndex = 0;\n    if (valueIndices.length > 0) {\n      const minIndex = Math.min(...valueIndices);\n      const maxIndex = Math.max(...valueIndices);\n      firstIndex = Math.max(0, maxIndex - numberOfVisibleLines + 1);\n      if (firstIndex > minIndex) {\n        firstIndex = minIndex;\n      }\n    }\n    const end = Math.min(firstIndex + numberOfVisibleLines + 1, lineCount);\n    const buf = [\"/Tx BMC q\", `1 1 ${totalWidth} ${totalHeight} re W n`];\n    if (valueIndices.length) {\n      buf.push(\"0.600006 0.756866 0.854904 rg\");\n      for (const index of valueIndices) {\n        if (firstIndex <= index && index < end) {\n          buf.push(`1 ${totalHeight - (index - firstIndex + 1) * lineHeight} ${totalWidth} ${lineHeight} re f`);\n        }\n      }\n    }\n    buf.push(\"BT\", defaultAppearance, `1 0 0 1 0 ${totalHeight} Tm`);\n    const prevInfo = {\n      shift: 0\n    };\n    for (let i = firstIndex; i < end; i++) {\n      const {\n        displayValue\n      } = this.data.options[i];\n      const vpadding = i === firstIndex ? vPadding : 0;\n      buf.push(this._renderText(displayValue, font, fontSize, totalWidth, 0, prevInfo, defaultHPadding, -lineHeight + vpadding));\n    }\n    buf.push(\"ET Q EMC\");\n    return buf.join(\"\\n\");\n  }\n}\nclass SignatureWidgetAnnotation extends WidgetAnnotation {\n  constructor(params) {\n    super(params);\n    this.data.fieldValue = null;\n    this.data.hasOwnCanvas = this.data.noRotate;\n    this.data.noHTML = !this.data.hasOwnCanvas;\n  }\n  getFieldObject() {\n    return {\n      id: this.data.id,\n      value: null,\n      page: this.data.pageIndex,\n      type: \"signature\"\n    };\n  }\n}\nclass TextAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    const DEFAULT_ICON_SIZE = 22;\n    super(params);\n    this.data.noRotate = true;\n    this.data.hasOwnCanvas = this.data.noRotate;\n    this.data.noHTML = false;\n    const {\n      dict\n    } = params;\n    this.data.annotationType = AnnotationType.TEXT;\n    if (this.data.hasAppearance) {\n      this.data.name = \"NoIcon\";\n    } else {\n      this.data.rect[1] = this.data.rect[3] - DEFAULT_ICON_SIZE;\n      this.data.rect[2] = this.data.rect[0] + DEFAULT_ICON_SIZE;\n      this.data.name = dict.has(\"Name\") ? dict.get(\"Name\").name : \"Note\";\n    }\n    if (dict.has(\"State\")) {\n      this.data.state = dict.get(\"State\") || null;\n      this.data.stateModel = dict.get(\"StateModel\") || null;\n    } else {\n      this.data.state = null;\n      this.data.stateModel = null;\n    }\n  }\n}\nclass LinkAnnotation extends Annotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict,\n      annotationGlobals\n    } = params;\n    this.data.annotationType = AnnotationType.LINK;\n    const quadPoints = getQuadPoints(dict, this.rectangle);\n    if (quadPoints) {\n      this.data.quadPoints = quadPoints;\n    }\n    this.data.borderColor ||= this.data.color;\n    Catalog.parseDestDictionary({\n      destDict: dict,\n      resultObj: this.data,\n      docBaseUrl: annotationGlobals.baseUrl,\n      docAttachments: annotationGlobals.attachments\n    });\n  }\n}\nclass PopupAnnotation extends Annotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict\n    } = params;\n    this.data.annotationType = AnnotationType.POPUP;\n    this.data.noHTML = false;\n    if (this.data.rect[0] === this.data.rect[2] || this.data.rect[1] === this.data.rect[3]) {\n      this.data.rect = null;\n    }\n    let parentItem = dict.get(\"Parent\");\n    if (!parentItem) {\n      warn(\"Popup annotation has a missing or invalid parent annotation.\");\n      return;\n    }\n    const parentRect = parentItem.getArray(\"Rect\");\n    this.data.parentRect = Array.isArray(parentRect) && parentRect.length === 4 ? Util.normalizeRect(parentRect) : null;\n    const rt = parentItem.get(\"RT\");\n    if (isName(rt, AnnotationReplyType.GROUP)) {\n      parentItem = parentItem.get(\"IRT\");\n    }\n    if (!parentItem.has(\"M\")) {\n      this.data.modificationDate = null;\n    } else {\n      this.setModificationDate(parentItem.get(\"M\"));\n      this.data.modificationDate = this.modificationDate;\n    }\n    if (!parentItem.has(\"C\")) {\n      this.data.color = null;\n    } else {\n      this.setColor(parentItem.getArray(\"C\"));\n      this.data.color = this.color;\n    }\n    if (!this.viewable) {\n      const parentFlags = parentItem.get(\"F\");\n      if (this._isViewable(parentFlags)) {\n        this.setFlags(parentFlags);\n      }\n    }\n    this.setTitle(parentItem.get(\"T\"));\n    this.data.titleObj = this._title;\n    this.setContents(parentItem.get(\"Contents\"));\n    this.data.contentsObj = this._contents;\n    if (parentItem.has(\"RC\")) {\n      this.data.richText = XFAFactory.getRichTextAsHtml(parentItem.get(\"RC\"));\n    }\n    this.data.open = !!dict.get(\"Open\");\n  }\n}\nclass FreeTextAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    this.data.hasOwnCanvas = !this.data.noHTML;\n    this.data.noHTML = false;\n    const {\n      evaluatorOptions,\n      xref\n    } = params;\n    this.data.annotationType = AnnotationType.FREETEXT;\n    this.setDefaultAppearance(params);\n    if (this.appearance) {\n      const {\n        fontColor,\n        fontSize\n      } = parseAppearanceStream(this.appearance, evaluatorOptions, xref);\n      this.data.defaultAppearanceData.fontColor = fontColor;\n      this.data.defaultAppearanceData.fontSize = fontSize || 10;\n    } else if (this._isOffscreenCanvasSupported) {\n      const strokeAlpha = params.dict.get(\"CA\");\n      const fakeUnicodeFont = new FakeUnicodeFont(xref, \"sans-serif\");\n      this.data.defaultAppearanceData.fontSize ||= 10;\n      const {\n        fontColor,\n        fontSize\n      } = this.data.defaultAppearanceData;\n      this.appearance = fakeUnicodeFont.createAppearance(this._contents.str, this.rectangle, this.rotation, fontSize, fontColor, strokeAlpha);\n      this._streams.push(this.appearance, FakeUnicodeFont.toUnicodeStream);\n    } else {\n      warn(\"FreeTextAnnotation: OffscreenCanvas is not supported, annotation may not render correctly.\");\n    }\n  }\n  get hasTextContent() {\n    return !!this.appearance;\n  }\n  static createNewDict(annotation, xref, {\n    apRef,\n    ap\n  }) {\n    const {\n      color,\n      fontSize,\n      rect,\n      rotation,\n      user,\n      value\n    } = annotation;\n    const freetext = new Dict(xref);\n    freetext.set(\"Type\", Name.get(\"Annot\"));\n    freetext.set(\"Subtype\", Name.get(\"FreeText\"));\n    freetext.set(\"CreationDate\", `D:${getModificationDate()}`);\n    freetext.set(\"Rect\", rect);\n    const da = `/Helv ${fontSize} Tf ${getPdfColor(color, true)}`;\n    freetext.set(\"DA\", da);\n    freetext.set(\"Contents\", isAscii(value) ? value : stringToUTF16String(value, true));\n    freetext.set(\"F\", 4);\n    freetext.set(\"Border\", [0, 0, 0]);\n    freetext.set(\"Rotate\", rotation);\n    if (user) {\n      freetext.set(\"T\", isAscii(user) ? user : stringToUTF16String(user, true));\n    }\n    if (apRef || ap) {\n      const n = new Dict(xref);\n      freetext.set(\"AP\", n);\n      if (apRef) {\n        n.set(\"N\", apRef);\n      } else {\n        n.set(\"N\", ap);\n      }\n    }\n    return freetext;\n  }\n  static async createNewAppearanceStream(annotation, xref, params) {\n    const {\n      baseFontRef,\n      evaluator,\n      task\n    } = params;\n    const {\n      color,\n      fontSize,\n      rect,\n      rotation,\n      value\n    } = annotation;\n    const resources = new Dict(xref);\n    const font = new Dict(xref);\n    if (baseFontRef) {\n      font.set(\"Helv\", baseFontRef);\n    } else {\n      const baseFont = new Dict(xref);\n      baseFont.set(\"BaseFont\", Name.get(\"Helvetica\"));\n      baseFont.set(\"Type\", Name.get(\"Font\"));\n      baseFont.set(\"Subtype\", Name.get(\"Type1\"));\n      baseFont.set(\"Encoding\", Name.get(\"WinAnsiEncoding\"));\n      font.set(\"Helv\", baseFont);\n    }\n    resources.set(\"Font\", font);\n    const helv = await WidgetAnnotation._getFontData(evaluator, task, {\n      fontName: \"Helv\",\n      fontSize\n    }, resources);\n    const [x1, y1, x2, y2] = rect;\n    let w = x2 - x1;\n    let h = y2 - y1;\n    if (rotation % 180 !== 0) {\n      [w, h] = [h, w];\n    }\n    const lines = value.split(\"\\n\");\n    const scale = fontSize / 1000;\n    let totalWidth = -Infinity;\n    const encodedLines = [];\n    for (let line of lines) {\n      const encoded = helv.encodeString(line);\n      if (encoded.length > 1) {\n        return null;\n      }\n      line = encoded.join(\"\");\n      encodedLines.push(line);\n      let lineWidth = 0;\n      const glyphs = helv.charsToGlyphs(line);\n      for (const glyph of glyphs) {\n        lineWidth += glyph.width * scale;\n      }\n      totalWidth = Math.max(totalWidth, lineWidth);\n    }\n    let hscale = 1;\n    if (totalWidth > w) {\n      hscale = w / totalWidth;\n    }\n    let vscale = 1;\n    const lineHeight = LINE_FACTOR * fontSize;\n    const lineAscent = (LINE_FACTOR - LINE_DESCENT_FACTOR) * fontSize;\n    const totalHeight = lineHeight * lines.length;\n    if (totalHeight > h) {\n      vscale = h / totalHeight;\n    }\n    const fscale = Math.min(hscale, vscale);\n    const newFontSize = fontSize * fscale;\n    let firstPoint, clipBox, matrix;\n    switch (rotation) {\n      case 0:\n        matrix = [1, 0, 0, 1];\n        clipBox = [rect[0], rect[1], w, h];\n        firstPoint = [rect[0], rect[3] - lineAscent];\n        break;\n      case 90:\n        matrix = [0, 1, -1, 0];\n        clipBox = [rect[1], -rect[2], w, h];\n        firstPoint = [rect[1], -rect[0] - lineAscent];\n        break;\n      case 180:\n        matrix = [-1, 0, 0, -1];\n        clipBox = [-rect[2], -rect[3], w, h];\n        firstPoint = [-rect[2], -rect[1] - lineAscent];\n        break;\n      case 270:\n        matrix = [0, -1, 1, 0];\n        clipBox = [-rect[3], rect[0], w, h];\n        firstPoint = [-rect[3], rect[2] - lineAscent];\n        break;\n    }\n    const buffer = [\"q\", `${matrix.join(\" \")} 0 0 cm`, `${clipBox.join(\" \")} re W n`, `BT`, `${getPdfColor(color, true)}`, `0 Tc /Helv ${numberToString(newFontSize)} Tf`];\n    buffer.push(`${firstPoint.join(\" \")} Td (${escapeString(encodedLines[0])}) Tj`);\n    const vShift = numberToString(lineHeight);\n    for (let i = 1, ii = encodedLines.length; i < ii; i++) {\n      const line = encodedLines[i];\n      buffer.push(`0 -${vShift} Td (${escapeString(line)}) Tj`);\n    }\n    buffer.push(\"ET\", \"Q\");\n    const appearance = buffer.join(\"\\n\");\n    const appearanceStreamDict = new Dict(xref);\n    appearanceStreamDict.set(\"FormType\", 1);\n    appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n    appearanceStreamDict.set(\"Type\", Name.get(\"XObject\"));\n    appearanceStreamDict.set(\"BBox\", rect);\n    appearanceStreamDict.set(\"Resources\", resources);\n    appearanceStreamDict.set(\"Matrix\", [1, 0, 0, 1, -rect[0], -rect[1]]);\n    const ap = new StringStream(appearance);\n    ap.dict = appearanceStreamDict;\n    return ap;\n  }\n}\nclass LineAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict,\n      xref\n    } = params;\n    this.data.annotationType = AnnotationType.LINE;\n    this.data.hasOwnCanvas = this.data.noRotate;\n    this.data.noHTML = false;\n    const lineCoordinates = dict.getArray(\"L\");\n    this.data.lineCoordinates = Util.normalizeRect(lineCoordinates);\n    this.setLineEndings(dict.getArray(\"LE\"));\n    this.data.lineEndings = this.lineEndings;\n    if (!this.appearance) {\n      const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n      const strokeAlpha = dict.get(\"CA\");\n      const interiorColor = getRgbColor(dict.getArray(\"IC\"), null);\n      const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null;\n      const fillAlpha = fillColor ? strokeAlpha : null;\n      const borderWidth = this.borderStyle.width || 1,\n        borderAdjust = 2 * borderWidth;\n      const bbox = [this.data.lineCoordinates[0] - borderAdjust, this.data.lineCoordinates[1] - borderAdjust, this.data.lineCoordinates[2] + borderAdjust, this.data.lineCoordinates[3] + borderAdjust];\n      if (!Util.intersect(this.rectangle, bbox)) {\n        this.rectangle = bbox;\n      }\n      this._setDefaultAppearance({\n        xref,\n        extra: `${borderWidth} w`,\n        strokeColor,\n        fillColor,\n        strokeAlpha,\n        fillAlpha,\n        pointsCallback: (buffer, points) => {\n          buffer.push(`${lineCoordinates[0]} ${lineCoordinates[1]} m`, `${lineCoordinates[2]} ${lineCoordinates[3]} l`, \"S\");\n          return [points[0].x - borderWidth, points[1].x + borderWidth, points[3].y - borderWidth, points[1].y + borderWidth];\n        }\n      });\n    }\n  }\n}\nclass SquareAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict,\n      xref\n    } = params;\n    this.data.annotationType = AnnotationType.SQUARE;\n    this.data.hasOwnCanvas = this.data.noRotate;\n    this.data.noHTML = false;\n    if (!this.appearance) {\n      const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n      const strokeAlpha = dict.get(\"CA\");\n      const interiorColor = getRgbColor(dict.getArray(\"IC\"), null);\n      const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null;\n      const fillAlpha = fillColor ? strokeAlpha : null;\n      if (this.borderStyle.width === 0 && !fillColor) {\n        return;\n      }\n      this._setDefaultAppearance({\n        xref,\n        extra: `${this.borderStyle.width} w`,\n        strokeColor,\n        fillColor,\n        strokeAlpha,\n        fillAlpha,\n        pointsCallback: (buffer, points) => {\n          const x = points[2].x + this.borderStyle.width / 2;\n          const y = points[2].y + this.borderStyle.width / 2;\n          const width = points[3].x - points[2].x - this.borderStyle.width;\n          const height = points[1].y - points[3].y - this.borderStyle.width;\n          buffer.push(`${x} ${y} ${width} ${height} re`);\n          if (fillColor) {\n            buffer.push(\"B\");\n          } else {\n            buffer.push(\"S\");\n          }\n          return [points[0].x, points[1].x, points[3].y, points[1].y];\n        }\n      });\n    }\n  }\n}\nclass CircleAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict,\n      xref\n    } = params;\n    this.data.annotationType = AnnotationType.CIRCLE;\n    if (!this.appearance) {\n      const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n      const strokeAlpha = dict.get(\"CA\");\n      const interiorColor = getRgbColor(dict.getArray(\"IC\"), null);\n      const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null;\n      const fillAlpha = fillColor ? strokeAlpha : null;\n      if (this.borderStyle.width === 0 && !fillColor) {\n        return;\n      }\n      const controlPointsDistance = 4 / 3 * Math.tan(Math.PI / (2 * 4));\n      this._setDefaultAppearance({\n        xref,\n        extra: `${this.borderStyle.width} w`,\n        strokeColor,\n        fillColor,\n        strokeAlpha,\n        fillAlpha,\n        pointsCallback: (buffer, points) => {\n          const x0 = points[0].x + this.borderStyle.width / 2;\n          const y0 = points[0].y - this.borderStyle.width / 2;\n          const x1 = points[3].x - this.borderStyle.width / 2;\n          const y1 = points[3].y + this.borderStyle.width / 2;\n          const xMid = x0 + (x1 - x0) / 2;\n          const yMid = y0 + (y1 - y0) / 2;\n          const xOffset = (x1 - x0) / 2 * controlPointsDistance;\n          const yOffset = (y1 - y0) / 2 * controlPointsDistance;\n          buffer.push(`${xMid} ${y1} m`, `${xMid + xOffset} ${y1} ${x1} ${yMid + yOffset} ${x1} ${yMid} c`, `${x1} ${yMid - yOffset} ${xMid + xOffset} ${y0} ${xMid} ${y0} c`, `${xMid - xOffset} ${y0} ${x0} ${yMid - yOffset} ${x0} ${yMid} c`, `${x0} ${yMid + yOffset} ${xMid - xOffset} ${y1} ${xMid} ${y1} c`, \"h\");\n          if (fillColor) {\n            buffer.push(\"B\");\n          } else {\n            buffer.push(\"S\");\n          }\n          return [points[0].x, points[1].x, points[3].y, points[1].y];\n        }\n      });\n    }\n  }\n}\nclass PolylineAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict,\n      xref\n    } = params;\n    this.data.annotationType = AnnotationType.POLYLINE;\n    this.data.hasOwnCanvas = this.data.noRotate;\n    this.data.noHTML = false;\n    this.data.vertices = [];\n    if (!(this instanceof PolygonAnnotation)) {\n      this.setLineEndings(dict.getArray(\"LE\"));\n      this.data.lineEndings = this.lineEndings;\n    }\n    const rawVertices = dict.getArray(\"Vertices\");\n    if (!Array.isArray(rawVertices)) {\n      return;\n    }\n    for (let i = 0, ii = rawVertices.length; i < ii; i += 2) {\n      this.data.vertices.push({\n        x: rawVertices[i],\n        y: rawVertices[i + 1]\n      });\n    }\n    if (!this.appearance) {\n      const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n      const strokeAlpha = dict.get(\"CA\");\n      const borderWidth = this.borderStyle.width || 1,\n        borderAdjust = 2 * borderWidth;\n      const bbox = [Infinity, Infinity, -Infinity, -Infinity];\n      for (const vertex of this.data.vertices) {\n        bbox[0] = Math.min(bbox[0], vertex.x - borderAdjust);\n        bbox[1] = Math.min(bbox[1], vertex.y - borderAdjust);\n        bbox[2] = Math.max(bbox[2], vertex.x + borderAdjust);\n        bbox[3] = Math.max(bbox[3], vertex.y + borderAdjust);\n      }\n      if (!Util.intersect(this.rectangle, bbox)) {\n        this.rectangle = bbox;\n      }\n      this._setDefaultAppearance({\n        xref,\n        extra: `${borderWidth} w`,\n        strokeColor,\n        strokeAlpha,\n        pointsCallback: (buffer, points) => {\n          const vertices = this.data.vertices;\n          for (let i = 0, ii = vertices.length; i < ii; i++) {\n            buffer.push(`${vertices[i].x} ${vertices[i].y} ${i === 0 ? \"m\" : \"l\"}`);\n          }\n          buffer.push(\"S\");\n          return [points[0].x, points[1].x, points[3].y, points[1].y];\n        }\n      });\n    }\n  }\n}\nclass PolygonAnnotation extends PolylineAnnotation {\n  constructor(params) {\n    super(params);\n    this.data.annotationType = AnnotationType.POLYGON;\n  }\n}\nclass CaretAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    this.data.annotationType = AnnotationType.CARET;\n  }\n}\nclass InkAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    this.data.hasOwnCanvas = this.data.noRotate;\n    this.data.noHTML = false;\n    const {\n      dict,\n      xref\n    } = params;\n    this.data.annotationType = AnnotationType.INK;\n    this.data.inkLists = [];\n    const rawInkLists = dict.getArray(\"InkList\");\n    if (!Array.isArray(rawInkLists)) {\n      return;\n    }\n    for (let i = 0, ii = rawInkLists.length; i < ii; ++i) {\n      this.data.inkLists.push([]);\n      for (let j = 0, jj = rawInkLists[i].length; j < jj; j += 2) {\n        this.data.inkLists[i].push({\n          x: xref.fetchIfRef(rawInkLists[i][j]),\n          y: xref.fetchIfRef(rawInkLists[i][j + 1])\n        });\n      }\n    }\n    if (!this.appearance) {\n      const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n      const strokeAlpha = dict.get(\"CA\");\n      const borderWidth = this.borderStyle.width || 1,\n        borderAdjust = 2 * borderWidth;\n      const bbox = [Infinity, Infinity, -Infinity, -Infinity];\n      for (const inkLists of this.data.inkLists) {\n        for (const vertex of inkLists) {\n          bbox[0] = Math.min(bbox[0], vertex.x - borderAdjust);\n          bbox[1] = Math.min(bbox[1], vertex.y - borderAdjust);\n          bbox[2] = Math.max(bbox[2], vertex.x + borderAdjust);\n          bbox[3] = Math.max(bbox[3], vertex.y + borderAdjust);\n        }\n      }\n      if (!Util.intersect(this.rectangle, bbox)) {\n        this.rectangle = bbox;\n      }\n      this._setDefaultAppearance({\n        xref,\n        extra: `${borderWidth} w`,\n        strokeColor,\n        strokeAlpha,\n        pointsCallback: (buffer, points) => {\n          for (const inkList of this.data.inkLists) {\n            for (let i = 0, ii = inkList.length; i < ii; i++) {\n              buffer.push(`${inkList[i].x} ${inkList[i].y} ${i === 0 ? \"m\" : \"l\"}`);\n            }\n            buffer.push(\"S\");\n          }\n          return [points[0].x, points[1].x, points[3].y, points[1].y];\n        }\n      });\n    }\n  }\n  static createNewDict(annotation, xref, {\n    apRef,\n    ap\n  }) {\n    const {\n      color,\n      opacity,\n      paths,\n      rect,\n      rotation,\n      thickness\n    } = annotation;\n    const ink = new Dict(xref);\n    ink.set(\"Type\", Name.get(\"Annot\"));\n    ink.set(\"Subtype\", Name.get(\"Ink\"));\n    ink.set(\"CreationDate\", `D:${getModificationDate()}`);\n    ink.set(\"Rect\", rect);\n    ink.set(\"InkList\", paths.map(p => p.points));\n    ink.set(\"F\", 4);\n    ink.set(\"Rotate\", rotation);\n    const bs = new Dict(xref);\n    ink.set(\"BS\", bs);\n    bs.set(\"W\", thickness);\n    ink.set(\"C\", Array.from(color, c => c / 255));\n    ink.set(\"CA\", opacity);\n    const n = new Dict(xref);\n    ink.set(\"AP\", n);\n    if (apRef) {\n      n.set(\"N\", apRef);\n    } else {\n      n.set(\"N\", ap);\n    }\n    return ink;\n  }\n  static async createNewAppearanceStream(annotation, xref, params) {\n    const {\n      color,\n      rect,\n      paths,\n      thickness,\n      opacity\n    } = annotation;\n    const appearanceBuffer = [`${thickness} w 1 J 1 j`, `${getPdfColor(color, false)}`];\n    if (opacity !== 1) {\n      appearanceBuffer.push(\"/R0 gs\");\n    }\n    const buffer = [];\n    for (const {\n      bezier\n    } of paths) {\n      buffer.length = 0;\n      buffer.push(`${numberToString(bezier[0])} ${numberToString(bezier[1])} m`);\n      for (let i = 2, ii = bezier.length; i < ii; i += 6) {\n        const curve = bezier.slice(i, i + 6).map(numberToString).join(\" \");\n        buffer.push(`${curve} c`);\n      }\n      buffer.push(\"S\");\n      appearanceBuffer.push(buffer.join(\"\\n\"));\n    }\n    const appearance = appearanceBuffer.join(\"\\n\");\n    const appearanceStreamDict = new Dict(xref);\n    appearanceStreamDict.set(\"FormType\", 1);\n    appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n    appearanceStreamDict.set(\"Type\", Name.get(\"XObject\"));\n    appearanceStreamDict.set(\"BBox\", rect);\n    appearanceStreamDict.set(\"Length\", appearance.length);\n    if (opacity !== 1) {\n      const resources = new Dict(xref);\n      const extGState = new Dict(xref);\n      const r0 = new Dict(xref);\n      r0.set(\"CA\", opacity);\n      r0.set(\"Type\", Name.get(\"ExtGState\"));\n      extGState.set(\"R0\", r0);\n      resources.set(\"ExtGState\", extGState);\n      appearanceStreamDict.set(\"Resources\", resources);\n    }\n    const ap = new StringStream(appearance);\n    ap.dict = appearanceStreamDict;\n    return ap;\n  }\n}\nclass HighlightAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict,\n      xref\n    } = params;\n    this.data.annotationType = AnnotationType.HIGHLIGHT;\n    const quadPoints = this.data.quadPoints = getQuadPoints(dict, null);\n    if (quadPoints) {\n      const resources = this.appearance?.dict.get(\"Resources\");\n      if (!this.appearance || !resources?.has(\"ExtGState\")) {\n        if (this.appearance) {\n          warn(\"HighlightAnnotation - ignoring built-in appearance stream.\");\n        }\n        const fillColor = this.color ? getPdfColorArray(this.color) : [1, 1, 0];\n        const fillAlpha = dict.get(\"CA\");\n        this._setDefaultAppearance({\n          xref,\n          fillColor,\n          blendMode: \"Multiply\",\n          fillAlpha,\n          pointsCallback: (buffer, points) => {\n            buffer.push(`${points[0].x} ${points[0].y} m`, `${points[1].x} ${points[1].y} l`, `${points[3].x} ${points[3].y} l`, `${points[2].x} ${points[2].y} l`, \"f\");\n            return [points[0].x, points[1].x, points[3].y, points[1].y];\n          }\n        });\n      }\n    } else {\n      this.data.popupRef = null;\n    }\n  }\n  static createNewDict(annotation, xref, {\n    apRef,\n    ap\n  }) {\n    const {\n      color,\n      opacity,\n      rect,\n      rotation,\n      user,\n      quadPoints\n    } = annotation;\n    const highlight = new Dict(xref);\n    highlight.set(\"Type\", Name.get(\"Annot\"));\n    highlight.set(\"Subtype\", Name.get(\"Highlight\"));\n    highlight.set(\"CreationDate\", `D:${getModificationDate()}`);\n    highlight.set(\"Rect\", rect);\n    highlight.set(\"F\", 4);\n    highlight.set(\"Border\", [0, 0, 0]);\n    highlight.set(\"Rotate\", rotation);\n    highlight.set(\"QuadPoints\", quadPoints);\n    highlight.set(\"C\", Array.from(color, c => c / 255));\n    highlight.set(\"CA\", opacity);\n    if (user) {\n      highlight.set(\"T\", isAscii(user) ? user : stringToUTF16String(user, true));\n    }\n    if (apRef || ap) {\n      const n = new Dict(xref);\n      highlight.set(\"AP\", n);\n      n.set(\"N\", apRef || ap);\n    }\n    return highlight;\n  }\n  static async createNewAppearanceStream(annotation, xref, params) {\n    const {\n      color,\n      rect,\n      outlines,\n      opacity\n    } = annotation;\n    const appearanceBuffer = [`${getPdfColor(color, true)}`, \"/R0 gs\"];\n    const buffer = [];\n    for (const outline of outlines) {\n      buffer.length = 0;\n      buffer.push(`${numberToString(outline[0])} ${numberToString(outline[1])} m`);\n      for (let i = 2, ii = outline.length; i < ii; i += 2) {\n        buffer.push(`${numberToString(outline[i])} ${numberToString(outline[i + 1])} l`);\n      }\n      buffer.push(\"h\");\n      appearanceBuffer.push(buffer.join(\"\\n\"));\n    }\n    appearanceBuffer.push(\"f*\");\n    const appearance = appearanceBuffer.join(\"\\n\");\n    const appearanceStreamDict = new Dict(xref);\n    appearanceStreamDict.set(\"FormType\", 1);\n    appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n    appearanceStreamDict.set(\"Type\", Name.get(\"XObject\"));\n    appearanceStreamDict.set(\"BBox\", rect);\n    appearanceStreamDict.set(\"Length\", appearance.length);\n    const resources = new Dict(xref);\n    const extGState = new Dict(xref);\n    resources.set(\"ExtGState\", extGState);\n    appearanceStreamDict.set(\"Resources\", resources);\n    const r0 = new Dict(xref);\n    extGState.set(\"R0\", r0);\n    r0.set(\"BM\", Name.get(\"Multiply\"));\n    if (opacity !== 1) {\n      r0.set(\"ca\", opacity);\n      r0.set(\"Type\", Name.get(\"ExtGState\"));\n    }\n    const ap = new StringStream(appearance);\n    ap.dict = appearanceStreamDict;\n    return ap;\n  }\n}\nclass UnderlineAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict,\n      xref\n    } = params;\n    this.data.annotationType = AnnotationType.UNDERLINE;\n    const quadPoints = this.data.quadPoints = getQuadPoints(dict, null);\n    if (quadPoints) {\n      if (!this.appearance) {\n        const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n        const strokeAlpha = dict.get(\"CA\");\n        this._setDefaultAppearance({\n          xref,\n          extra: \"[] 0 d 0.571 w\",\n          strokeColor,\n          strokeAlpha,\n          pointsCallback: (buffer, points) => {\n            buffer.push(`${points[2].x} ${points[2].y + 1.3} m`, `${points[3].x} ${points[3].y + 1.3} l`, \"S\");\n            return [points[0].x, points[1].x, points[3].y, points[1].y];\n          }\n        });\n      }\n    } else {\n      this.data.popupRef = null;\n    }\n  }\n}\nclass SquigglyAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict,\n      xref\n    } = params;\n    this.data.annotationType = AnnotationType.SQUIGGLY;\n    const quadPoints = this.data.quadPoints = getQuadPoints(dict, null);\n    if (quadPoints) {\n      if (!this.appearance) {\n        const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n        const strokeAlpha = dict.get(\"CA\");\n        this._setDefaultAppearance({\n          xref,\n          extra: \"[] 0 d 1 w\",\n          strokeColor,\n          strokeAlpha,\n          pointsCallback: (buffer, points) => {\n            const dy = (points[0].y - points[2].y) / 6;\n            let shift = dy;\n            let x = points[2].x;\n            const y = points[2].y;\n            const xEnd = points[3].x;\n            buffer.push(`${x} ${y + shift} m`);\n            do {\n              x += 2;\n              shift = shift === 0 ? dy : 0;\n              buffer.push(`${x} ${y + shift} l`);\n            } while (x < xEnd);\n            buffer.push(\"S\");\n            return [points[2].x, xEnd, y - 2 * dy, y + 2 * dy];\n          }\n        });\n      }\n    } else {\n      this.data.popupRef = null;\n    }\n  }\n}\nclass StrikeOutAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict,\n      xref\n    } = params;\n    this.data.annotationType = AnnotationType.STRIKEOUT;\n    const quadPoints = this.data.quadPoints = getQuadPoints(dict, null);\n    if (quadPoints) {\n      if (!this.appearance) {\n        const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n        const strokeAlpha = dict.get(\"CA\");\n        this._setDefaultAppearance({\n          xref,\n          extra: \"[] 0 d 1 w\",\n          strokeColor,\n          strokeAlpha,\n          pointsCallback: (buffer, points) => {\n            buffer.push(`${(points[0].x + points[2].x) / 2} ` + `${(points[0].y + points[2].y) / 2} m`, `${(points[1].x + points[3].x) / 2} ` + `${(points[1].y + points[3].y) / 2} l`, \"S\");\n            return [points[0].x, points[1].x, points[3].y, points[1].y];\n          }\n        });\n      }\n    } else {\n      this.data.popupRef = null;\n    }\n  }\n}\nclass StampAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    this.data.annotationType = AnnotationType.STAMP;\n    this.data.hasOwnCanvas = this.data.noRotate;\n    this.data.noHTML = false;\n  }\n  static async createImage(bitmap, xref) {\n    const {\n      width,\n      height\n    } = bitmap;\n    const canvas = new OffscreenCanvas(width, height);\n    const ctx = canvas.getContext(\"2d\", {\n      alpha: true\n    });\n    ctx.drawImage(bitmap, 0, 0);\n    const data = ctx.getImageData(0, 0, width, height).data;\n    const buf32 = new Uint32Array(data.buffer);\n    const hasAlpha = buf32.some(FeatureTest.isLittleEndian ? x => x >>> 24 !== 0xff : x => (x & 0xff) !== 0xff);\n    if (hasAlpha) {\n      ctx.fillStyle = \"white\";\n      ctx.fillRect(0, 0, width, height);\n      ctx.drawImage(bitmap, 0, 0);\n    }\n    const jpegBufferPromise = canvas.convertToBlob({\n      type: \"image/jpeg\",\n      quality: 1\n    }).then(blob => {\n      return blob.arrayBuffer();\n    });\n    const xobjectName = Name.get(\"XObject\");\n    const imageName = Name.get(\"Image\");\n    const image = new Dict(xref);\n    image.set(\"Type\", xobjectName);\n    image.set(\"Subtype\", imageName);\n    image.set(\"BitsPerComponent\", 8);\n    image.set(\"ColorSpace\", Name.get(\"DeviceRGB\"));\n    image.set(\"Filter\", Name.get(\"DCTDecode\"));\n    image.set(\"BBox\", [0, 0, width, height]);\n    image.set(\"Width\", width);\n    image.set(\"Height\", height);\n    let smaskStream = null;\n    if (hasAlpha) {\n      const alphaBuffer = new Uint8Array(buf32.length);\n      if (FeatureTest.isLittleEndian) {\n        for (let i = 0, ii = buf32.length; i < ii; i++) {\n          alphaBuffer[i] = buf32[i] >>> 24;\n        }\n      } else {\n        for (let i = 0, ii = buf32.length; i < ii; i++) {\n          alphaBuffer[i] = buf32[i] & 0xff;\n        }\n      }\n      const smask = new Dict(xref);\n      smask.set(\"Type\", xobjectName);\n      smask.set(\"Subtype\", imageName);\n      smask.set(\"BitsPerComponent\", 8);\n      smask.set(\"ColorSpace\", Name.get(\"DeviceGray\"));\n      smask.set(\"Width\", width);\n      smask.set(\"Height\", height);\n      smaskStream = new Stream(alphaBuffer, 0, 0, smask);\n    }\n    const imageStream = new Stream(await jpegBufferPromise, 0, 0, image);\n    return {\n      imageStream,\n      smaskStream,\n      width,\n      height\n    };\n  }\n  static createNewDict(annotation, xref, {\n    apRef,\n    ap\n  }) {\n    const {\n      rect,\n      rotation,\n      user\n    } = annotation;\n    const stamp = new Dict(xref);\n    stamp.set(\"Type\", Name.get(\"Annot\"));\n    stamp.set(\"Subtype\", Name.get(\"Stamp\"));\n    stamp.set(\"CreationDate\", `D:${getModificationDate()}`);\n    stamp.set(\"Rect\", rect);\n    stamp.set(\"F\", 4);\n    stamp.set(\"Border\", [0, 0, 0]);\n    stamp.set(\"Rotate\", rotation);\n    if (user) {\n      stamp.set(\"T\", isAscii(user) ? user : stringToUTF16String(user, true));\n    }\n    if (apRef || ap) {\n      const n = new Dict(xref);\n      stamp.set(\"AP\", n);\n      if (apRef) {\n        n.set(\"N\", apRef);\n      } else {\n        n.set(\"N\", ap);\n      }\n    }\n    return stamp;\n  }\n  static async createNewAppearanceStream(annotation, xref, params) {\n    const {\n      rotation\n    } = annotation;\n    const {\n      imageRef,\n      width,\n      height\n    } = params.image;\n    const resources = new Dict(xref);\n    const xobject = new Dict(xref);\n    resources.set(\"XObject\", xobject);\n    xobject.set(\"Im0\", imageRef);\n    const appearance = `q ${width} 0 0 ${height} 0 0 cm /Im0 Do Q`;\n    const appearanceStreamDict = new Dict(xref);\n    appearanceStreamDict.set(\"FormType\", 1);\n    appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n    appearanceStreamDict.set(\"Type\", Name.get(\"XObject\"));\n    appearanceStreamDict.set(\"BBox\", [0, 0, width, height]);\n    appearanceStreamDict.set(\"Resources\", resources);\n    if (rotation) {\n      const matrix = getRotationMatrix(rotation, width, height);\n      appearanceStreamDict.set(\"Matrix\", matrix);\n    }\n    const ap = new StringStream(appearance);\n    ap.dict = appearanceStreamDict;\n    return ap;\n  }\n}\nclass FileAttachmentAnnotation extends MarkupAnnotation {\n  constructor(params) {\n    super(params);\n    const {\n      dict,\n      xref\n    } = params;\n    const file = new FileSpec(dict.get(\"FS\"), xref);\n    this.data.annotationType = AnnotationType.FILEATTACHMENT;\n    this.data.hasOwnCanvas = this.data.noRotate;\n    this.data.noHTML = false;\n    this.data.file = file.serializable;\n    const name = dict.get(\"Name\");\n    this.data.name = name instanceof Name ? stringToPDFString(name.name) : \"PushPin\";\n    const fillAlpha = dict.get(\"ca\");\n    this.data.fillAlpha = typeof fillAlpha === \"number\" && fillAlpha >= 0 && fillAlpha <= 1 ? fillAlpha : null;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/dataset_reader.js\n\n\n\nfunction decodeString(str) {\n  try {\n    return stringToUTF8String(str);\n  } catch (ex) {\n    warn(`UTF-8 decoding failed: \"${ex}\".`);\n    return str;\n  }\n}\nclass DatasetXMLParser extends SimpleXMLParser {\n  constructor(options) {\n    super(options);\n    this.node = null;\n  }\n  onEndElement(name) {\n    const node = super.onEndElement(name);\n    if (node && name === \"xfa:datasets\") {\n      this.node = node;\n      throw new Error(\"Aborting DatasetXMLParser.\");\n    }\n  }\n}\nclass DatasetReader {\n  constructor(data) {\n    if (data.datasets) {\n      this.node = new SimpleXMLParser({\n        hasAttributes: true\n      }).parseFromString(data.datasets).documentElement;\n    } else {\n      const parser = new DatasetXMLParser({\n        hasAttributes: true\n      });\n      try {\n        parser.parseFromString(data[\"xdp:xdp\"]);\n      } catch {}\n      this.node = parser.node;\n    }\n  }\n  getValue(path) {\n    if (!this.node || !path) {\n      return \"\";\n    }\n    const node = this.node.searchNode(parseXFAPath(path), 0);\n    if (!node) {\n      return \"\";\n    }\n    const first = node.firstChild;\n    if (first?.nodeName === \"value\") {\n      return node.children.map(child => decodeString(child.textContent));\n    }\n    return decodeString(node.textContent);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/xref.js\n\n\n\n\n\n\nclass XRef {\n  #firstXRefStmPos = null;\n  constructor(stream, pdfManager) {\n    this.stream = stream;\n    this.pdfManager = pdfManager;\n    this.entries = [];\n    this._xrefStms = new Set();\n    this._cacheMap = new Map();\n    this._pendingRefs = new RefSet();\n    this._newPersistentRefNum = null;\n    this._newTemporaryRefNum = null;\n  }\n  getNewPersistentRef(obj) {\n    if (this._newPersistentRefNum === null) {\n      this._newPersistentRefNum = this.entries.length || 1;\n    }\n    const num = this._newPersistentRefNum++;\n    this._cacheMap.set(num, obj);\n    return Ref.get(num, 0);\n  }\n  getNewTemporaryRef() {\n    if (this._newTemporaryRefNum === null) {\n      this._newTemporaryRefNum = this.entries.length || 1;\n    }\n    return Ref.get(this._newTemporaryRefNum++, 0);\n  }\n  resetNewTemporaryRef() {\n    this._newTemporaryRefNum = null;\n  }\n  setStartXRef(startXRef) {\n    this.startXRefQueue = [startXRef];\n  }\n  parse(recoveryMode = false) {\n    let trailerDict;\n    if (!recoveryMode) {\n      trailerDict = this.readXRef();\n    } else {\n      warn(\"Indexing all PDF objects\");\n      trailerDict = this.indexObjects();\n    }\n    trailerDict.assignXref(this);\n    this.trailer = trailerDict;\n    let encrypt;\n    try {\n      encrypt = trailerDict.get(\"Encrypt\");\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(`XRef.parse - Invalid \"Encrypt\" reference: \"${ex}\".`);\n    }\n    if (encrypt instanceof Dict) {\n      const ids = trailerDict.get(\"ID\");\n      const fileId = ids?.length ? ids[0] : \"\";\n      encrypt.suppressEncryption = true;\n      this.encrypt = new CipherTransformFactory(encrypt, fileId, this.pdfManager.password);\n    }\n    let root;\n    try {\n      root = trailerDict.get(\"Root\");\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(`XRef.parse - Invalid \"Root\" reference: \"${ex}\".`);\n    }\n    if (root instanceof Dict) {\n      try {\n        const pages = root.get(\"Pages\");\n        if (pages instanceof Dict) {\n          this.root = root;\n          return;\n        }\n      } catch (ex) {\n        if (ex instanceof MissingDataException) {\n          throw ex;\n        }\n        warn(`XRef.parse - Invalid \"Pages\" reference: \"${ex}\".`);\n      }\n    }\n    if (!recoveryMode) {\n      throw new XRefParseException();\n    }\n    throw new InvalidPDFException(\"Invalid Root reference.\");\n  }\n  processXRefTable(parser) {\n    if (!(\"tableState\" in this)) {\n      this.tableState = {\n        entryNum: 0,\n        streamPos: parser.lexer.stream.pos,\n        parserBuf1: parser.buf1,\n        parserBuf2: parser.buf2\n      };\n    }\n    const obj = this.readXRefTable(parser);\n    if (!isCmd(obj, \"trailer\")) {\n      throw new FormatError(\"Invalid XRef table: could not find trailer dictionary\");\n    }\n    let dict = parser.getObj();\n    if (!(dict instanceof Dict) && dict.dict) {\n      dict = dict.dict;\n    }\n    if (!(dict instanceof Dict)) {\n      throw new FormatError(\"Invalid XRef table: could not parse trailer dictionary\");\n    }\n    delete this.tableState;\n    return dict;\n  }\n  readXRefTable(parser) {\n    const stream = parser.lexer.stream;\n    const tableState = this.tableState;\n    stream.pos = tableState.streamPos;\n    parser.buf1 = tableState.parserBuf1;\n    parser.buf2 = tableState.parserBuf2;\n    let obj;\n    while (true) {\n      if (!(\"firstEntryNum\" in tableState) || !(\"entryCount\" in tableState)) {\n        if (isCmd(obj = parser.getObj(), \"trailer\")) {\n          break;\n        }\n        tableState.firstEntryNum = obj;\n        tableState.entryCount = parser.getObj();\n      }\n      let first = tableState.firstEntryNum;\n      const count = tableState.entryCount;\n      if (!Number.isInteger(first) || !Number.isInteger(count)) {\n        throw new FormatError(\"Invalid XRef table: wrong types in subsection header\");\n      }\n      for (let i = tableState.entryNum; i < count; i++) {\n        tableState.streamPos = stream.pos;\n        tableState.entryNum = i;\n        tableState.parserBuf1 = parser.buf1;\n        tableState.parserBuf2 = parser.buf2;\n        const entry = {};\n        entry.offset = parser.getObj();\n        entry.gen = parser.getObj();\n        const type = parser.getObj();\n        if (type instanceof Cmd) {\n          switch (type.cmd) {\n            case \"f\":\n              entry.free = true;\n              break;\n            case \"n\":\n              entry.uncompressed = true;\n              break;\n          }\n        }\n        if (!Number.isInteger(entry.offset) || !Number.isInteger(entry.gen) || !(entry.free || entry.uncompressed)) {\n          throw new FormatError(`Invalid entry in XRef subsection: ${first}, ${count}`);\n        }\n        if (i === 0 && entry.free && first === 1) {\n          first = 0;\n        }\n        if (!this.entries[i + first]) {\n          this.entries[i + first] = entry;\n        }\n      }\n      tableState.entryNum = 0;\n      tableState.streamPos = stream.pos;\n      tableState.parserBuf1 = parser.buf1;\n      tableState.parserBuf2 = parser.buf2;\n      delete tableState.firstEntryNum;\n      delete tableState.entryCount;\n    }\n    if (this.entries[0] && !this.entries[0].free) {\n      throw new FormatError(\"Invalid XRef table: unexpected first object\");\n    }\n    return obj;\n  }\n  processXRefStream(stream) {\n    if (!(\"streamState\" in this)) {\n      const streamParameters = stream.dict;\n      const byteWidths = streamParameters.get(\"W\");\n      let range = streamParameters.get(\"Index\");\n      if (!range) {\n        range = [0, streamParameters.get(\"Size\")];\n      }\n      this.streamState = {\n        entryRanges: range,\n        byteWidths,\n        entryNum: 0,\n        streamPos: stream.pos\n      };\n    }\n    this.readXRefStream(stream);\n    delete this.streamState;\n    return stream.dict;\n  }\n  readXRefStream(stream) {\n    const streamState = this.streamState;\n    stream.pos = streamState.streamPos;\n    const [typeFieldWidth, offsetFieldWidth, generationFieldWidth] = streamState.byteWidths;\n    const entryRanges = streamState.entryRanges;\n    while (entryRanges.length > 0) {\n      const [first, n] = entryRanges;\n      if (!Number.isInteger(first) || !Number.isInteger(n)) {\n        throw new FormatError(`Invalid XRef range fields: ${first}, ${n}`);\n      }\n      if (!Number.isInteger(typeFieldWidth) || !Number.isInteger(offsetFieldWidth) || !Number.isInteger(generationFieldWidth)) {\n        throw new FormatError(`Invalid XRef entry fields length: ${first}, ${n}`);\n      }\n      for (let i = streamState.entryNum; i < n; ++i) {\n        streamState.entryNum = i;\n        streamState.streamPos = stream.pos;\n        let type = 0,\n          offset = 0,\n          generation = 0;\n        for (let j = 0; j < typeFieldWidth; ++j) {\n          const typeByte = stream.getByte();\n          if (typeByte === -1) {\n            throw new FormatError(\"Invalid XRef byteWidths 'type'.\");\n          }\n          type = type << 8 | typeByte;\n        }\n        if (typeFieldWidth === 0) {\n          type = 1;\n        }\n        for (let j = 0; j < offsetFieldWidth; ++j) {\n          const offsetByte = stream.getByte();\n          if (offsetByte === -1) {\n            throw new FormatError(\"Invalid XRef byteWidths 'offset'.\");\n          }\n          offset = offset << 8 | offsetByte;\n        }\n        for (let j = 0; j < generationFieldWidth; ++j) {\n          const generationByte = stream.getByte();\n          if (generationByte === -1) {\n            throw new FormatError(\"Invalid XRef byteWidths 'generation'.\");\n          }\n          generation = generation << 8 | generationByte;\n        }\n        const entry = {};\n        entry.offset = offset;\n        entry.gen = generation;\n        switch (type) {\n          case 0:\n            entry.free = true;\n            break;\n          case 1:\n            entry.uncompressed = true;\n            break;\n          case 2:\n            break;\n          default:\n            throw new FormatError(`Invalid XRef entry type: ${type}`);\n        }\n        if (!this.entries[first + i]) {\n          this.entries[first + i] = entry;\n        }\n      }\n      streamState.entryNum = 0;\n      streamState.streamPos = stream.pos;\n      entryRanges.splice(0, 2);\n    }\n  }\n  indexObjects() {\n    const TAB = 0x9,\n      LF = 0xa,\n      CR = 0xd,\n      SPACE = 0x20;\n    const PERCENT = 0x25,\n      LT = 0x3c;\n    function readToken(data, offset) {\n      let token = \"\",\n        ch = data[offset];\n      while (ch !== LF && ch !== CR && ch !== LT) {\n        if (++offset >= data.length) {\n          break;\n        }\n        token += String.fromCharCode(ch);\n        ch = data[offset];\n      }\n      return token;\n    }\n    function skipUntil(data, offset, what) {\n      const length = what.length,\n        dataLength = data.length;\n      let skipped = 0;\n      while (offset < dataLength) {\n        let i = 0;\n        while (i < length && data[offset + i] === what[i]) {\n          ++i;\n        }\n        if (i >= length) {\n          break;\n        }\n        offset++;\n        skipped++;\n      }\n      return skipped;\n    }\n    const gEndobjRegExp = /\\b(endobj|\\d+\\s+\\d+\\s+obj|xref|trailer\\s*<<)\\b/g;\n    const gStartxrefRegExp = /\\b(startxref|\\d+\\s+\\d+\\s+obj)\\b/g;\n    const objRegExp = /^(\\d+)\\s+(\\d+)\\s+obj\\b/;\n    const trailerBytes = new Uint8Array([116, 114, 97, 105, 108, 101, 114]);\n    const startxrefBytes = new Uint8Array([115, 116, 97, 114, 116, 120, 114, 101, 102]);\n    const xrefBytes = new Uint8Array([47, 88, 82, 101, 102]);\n    this.entries.length = 0;\n    this._cacheMap.clear();\n    const stream = this.stream;\n    stream.pos = 0;\n    const buffer = stream.getBytes(),\n      bufferStr = bytesToString(buffer),\n      length = buffer.length;\n    let position = stream.start;\n    const trailers = [],\n      xrefStms = [];\n    while (position < length) {\n      let ch = buffer[position];\n      if (ch === TAB || ch === LF || ch === CR || ch === SPACE) {\n        ++position;\n        continue;\n      }\n      if (ch === PERCENT) {\n        do {\n          ++position;\n          if (position >= length) {\n            break;\n          }\n          ch = buffer[position];\n        } while (ch !== LF && ch !== CR);\n        continue;\n      }\n      const token = readToken(buffer, position);\n      let m;\n      if (token.startsWith(\"xref\") && (token.length === 4 || /\\s/.test(token[4]))) {\n        position += skipUntil(buffer, position, trailerBytes);\n        trailers.push(position);\n        position += skipUntil(buffer, position, startxrefBytes);\n      } else if (m = objRegExp.exec(token)) {\n        const num = m[1] | 0,\n          gen = m[2] | 0;\n        const startPos = position + token.length;\n        let contentLength,\n          updateEntries = false;\n        if (!this.entries[num]) {\n          updateEntries = true;\n        } else if (this.entries[num].gen === gen) {\n          try {\n            const parser = new Parser({\n              lexer: new Lexer(stream.makeSubStream(startPos))\n            });\n            parser.getObj();\n            updateEntries = true;\n          } catch (ex) {\n            if (ex instanceof ParserEOFException) {\n              warn(`indexObjects -- checking object (${token}): \"${ex}\".`);\n            } else {\n              updateEntries = true;\n            }\n          }\n        }\n        if (updateEntries) {\n          this.entries[num] = {\n            offset: position - stream.start,\n            gen,\n            uncompressed: true\n          };\n        }\n        gEndobjRegExp.lastIndex = startPos;\n        const match = gEndobjRegExp.exec(bufferStr);\n        if (match) {\n          const endPos = gEndobjRegExp.lastIndex + 1;\n          contentLength = endPos - position;\n          if (match[1] !== \"endobj\") {\n            warn(`indexObjects: Found \"${match[1]}\" inside of another \"obj\", ` + 'caused by missing \"endobj\" -- trying to recover.');\n            contentLength -= match[1].length + 1;\n          }\n        } else {\n          contentLength = length - position;\n        }\n        const content = buffer.subarray(position, position + contentLength);\n        const xrefTagOffset = skipUntil(content, 0, xrefBytes);\n        if (xrefTagOffset < contentLength && content[xrefTagOffset + 5] < 64) {\n          xrefStms.push(position - stream.start);\n          this._xrefStms.add(position - stream.start);\n        }\n        position += contentLength;\n      } else if (token.startsWith(\"trailer\") && (token.length === 7 || /\\s/.test(token[7]))) {\n        trailers.push(position);\n        const startPos = position + token.length;\n        let contentLength;\n        gStartxrefRegExp.lastIndex = startPos;\n        const match = gStartxrefRegExp.exec(bufferStr);\n        if (match) {\n          const endPos = gStartxrefRegExp.lastIndex + 1;\n          contentLength = endPos - position;\n          if (match[1] !== \"startxref\") {\n            warn(`indexObjects: Found \"${match[1]}\" after \"trailer\", ` + 'caused by missing \"startxref\" -- trying to recover.');\n            contentLength -= match[1].length + 1;\n          }\n        } else {\n          contentLength = length - position;\n        }\n        position += contentLength;\n      } else {\n        position += token.length + 1;\n      }\n    }\n    for (const xrefStm of xrefStms) {\n      this.startXRefQueue.push(xrefStm);\n      this.readXRef(true);\n    }\n    const trailerDicts = [];\n    let isEncrypted = false;\n    for (const trailer of trailers) {\n      stream.pos = trailer;\n      const parser = new Parser({\n        lexer: new Lexer(stream),\n        xref: this,\n        allowStreams: true,\n        recoveryMode: true\n      });\n      const obj = parser.getObj();\n      if (!isCmd(obj, \"trailer\")) {\n        continue;\n      }\n      const dict = parser.getObj();\n      if (!(dict instanceof Dict)) {\n        continue;\n      }\n      trailerDicts.push(dict);\n      if (dict.has(\"Encrypt\")) {\n        isEncrypted = true;\n      }\n    }\n    let trailerDict, trailerError;\n    for (const dict of [...trailerDicts, \"genFallback\", ...trailerDicts]) {\n      if (dict === \"genFallback\") {\n        if (!trailerError) {\n          break;\n        }\n        this._generationFallback = true;\n        continue;\n      }\n      let validPagesDict = false;\n      try {\n        const rootDict = dict.get(\"Root\");\n        if (!(rootDict instanceof Dict)) {\n          continue;\n        }\n        const pagesDict = rootDict.get(\"Pages\");\n        if (!(pagesDict instanceof Dict)) {\n          continue;\n        }\n        const pagesCount = pagesDict.get(\"Count\");\n        if (Number.isInteger(pagesCount)) {\n          validPagesDict = true;\n        }\n      } catch (ex) {\n        trailerError = ex;\n        continue;\n      }\n      if (validPagesDict && (!isEncrypted || dict.has(\"Encrypt\")) && dict.has(\"ID\")) {\n        return dict;\n      }\n      trailerDict = dict;\n    }\n    if (trailerDict) {\n      return trailerDict;\n    }\n    if (this.topDict) {\n      return this.topDict;\n    }\n    throw new InvalidPDFException(\"Invalid PDF structure.\");\n  }\n  readXRef(recoveryMode = false) {\n    const stream = this.stream;\n    const startXRefParsedCache = new Set();\n    while (this.startXRefQueue.length) {\n      try {\n        const startXRef = this.startXRefQueue[0];\n        if (startXRefParsedCache.has(startXRef)) {\n          warn(\"readXRef - skipping XRef table since it was already parsed.\");\n          this.startXRefQueue.shift();\n          continue;\n        }\n        startXRefParsedCache.add(startXRef);\n        stream.pos = startXRef + stream.start;\n        const parser = new Parser({\n          lexer: new Lexer(stream),\n          xref: this,\n          allowStreams: true\n        });\n        let obj = parser.getObj();\n        let dict;\n        if (isCmd(obj, \"xref\")) {\n          dict = this.processXRefTable(parser);\n          if (!this.topDict) {\n            this.topDict = dict;\n          }\n          obj = dict.get(\"XRefStm\");\n          if (Number.isInteger(obj) && !this._xrefStms.has(obj)) {\n            this._xrefStms.add(obj);\n            this.startXRefQueue.push(obj);\n            this.#firstXRefStmPos ??= obj;\n          }\n        } else if (Number.isInteger(obj)) {\n          if (!Number.isInteger(parser.getObj()) || !isCmd(parser.getObj(), \"obj\") || !((obj = parser.getObj()) instanceof BaseStream)) {\n            throw new FormatError(\"Invalid XRef stream\");\n          }\n          dict = this.processXRefStream(obj);\n          if (!this.topDict) {\n            this.topDict = dict;\n          }\n          if (!dict) {\n            throw new FormatError(\"Failed to read XRef stream\");\n          }\n        } else {\n          throw new FormatError(\"Invalid XRef stream header\");\n        }\n        obj = dict.get(\"Prev\");\n        if (Number.isInteger(obj)) {\n          this.startXRefQueue.push(obj);\n        } else if (obj instanceof Ref) {\n          this.startXRefQueue.push(obj.num);\n        }\n      } catch (e) {\n        if (e instanceof MissingDataException) {\n          throw e;\n        }\n        info(\"(while reading XRef): \" + e);\n      }\n      this.startXRefQueue.shift();\n    }\n    if (this.topDict) {\n      return this.topDict;\n    }\n    if (recoveryMode) {\n      return undefined;\n    }\n    throw new XRefParseException();\n  }\n  get lastXRefStreamPos() {\n    return this.#firstXRefStmPos ?? (this._xrefStms.size > 0 ? Math.max(...this._xrefStms) : null);\n  }\n  getEntry(i) {\n    const xrefEntry = this.entries[i];\n    if (xrefEntry && !xrefEntry.free && xrefEntry.offset) {\n      return xrefEntry;\n    }\n    return null;\n  }\n  fetchIfRef(obj, suppressEncryption = false) {\n    if (obj instanceof Ref) {\n      return this.fetch(obj, suppressEncryption);\n    }\n    return obj;\n  }\n  fetch(ref, suppressEncryption = false) {\n    if (!(ref instanceof Ref)) {\n      throw new Error(\"ref object is not a reference\");\n    }\n    const num = ref.num;\n    const cacheEntry = this._cacheMap.get(num);\n    if (cacheEntry !== undefined) {\n      if (cacheEntry instanceof Dict && !cacheEntry.objId) {\n        cacheEntry.objId = ref.toString();\n      }\n      return cacheEntry;\n    }\n    let xrefEntry = this.getEntry(num);\n    if (xrefEntry === null) {\n      this._cacheMap.set(num, xrefEntry);\n      return xrefEntry;\n    }\n    if (this._pendingRefs.has(ref)) {\n      this._pendingRefs.remove(ref);\n      warn(`Ignoring circular reference: ${ref}.`);\n      return CIRCULAR_REF;\n    }\n    this._pendingRefs.put(ref);\n    try {\n      xrefEntry = xrefEntry.uncompressed ? this.fetchUncompressed(ref, xrefEntry, suppressEncryption) : this.fetchCompressed(ref, xrefEntry, suppressEncryption);\n      this._pendingRefs.remove(ref);\n    } catch (ex) {\n      this._pendingRefs.remove(ref);\n      throw ex;\n    }\n    if (xrefEntry instanceof Dict) {\n      xrefEntry.objId = ref.toString();\n    } else if (xrefEntry instanceof BaseStream) {\n      xrefEntry.dict.objId = ref.toString();\n    }\n    return xrefEntry;\n  }\n  fetchUncompressed(ref, xrefEntry, suppressEncryption = false) {\n    const gen = ref.gen;\n    let num = ref.num;\n    if (xrefEntry.gen !== gen) {\n      const msg = `Inconsistent generation in XRef: ${ref}`;\n      if (this._generationFallback && xrefEntry.gen < gen) {\n        warn(msg);\n        return this.fetchUncompressed(Ref.get(num, xrefEntry.gen), xrefEntry, suppressEncryption);\n      }\n      throw new XRefEntryException(msg);\n    }\n    const stream = this.stream.makeSubStream(xrefEntry.offset + this.stream.start);\n    const parser = new Parser({\n      lexer: new Lexer(stream),\n      xref: this,\n      allowStreams: true\n    });\n    const obj1 = parser.getObj();\n    const obj2 = parser.getObj();\n    const obj3 = parser.getObj();\n    if (obj1 !== num || obj2 !== gen || !(obj3 instanceof Cmd)) {\n      throw new XRefEntryException(`Bad (uncompressed) XRef entry: ${ref}`);\n    }\n    if (obj3.cmd !== \"obj\") {\n      if (obj3.cmd.startsWith(\"obj\")) {\n        num = parseInt(obj3.cmd.substring(3), 10);\n        if (!Number.isNaN(num)) {\n          return num;\n        }\n      }\n      throw new XRefEntryException(`Bad (uncompressed) XRef entry: ${ref}`);\n    }\n    xrefEntry = this.encrypt && !suppressEncryption ? parser.getObj(this.encrypt.createCipherTransform(num, gen)) : parser.getObj();\n    if (!(xrefEntry instanceof BaseStream)) {\n      this._cacheMap.set(num, xrefEntry);\n    }\n    return xrefEntry;\n  }\n  fetchCompressed(ref, xrefEntry, suppressEncryption = false) {\n    const tableOffset = xrefEntry.offset;\n    const stream = this.fetch(Ref.get(tableOffset, 0));\n    if (!(stream instanceof BaseStream)) {\n      throw new FormatError(\"bad ObjStm stream\");\n    }\n    const first = stream.dict.get(\"First\");\n    const n = stream.dict.get(\"N\");\n    if (!Number.isInteger(first) || !Number.isInteger(n)) {\n      throw new FormatError(\"invalid first and n parameters for ObjStm stream\");\n    }\n    let parser = new Parser({\n      lexer: new Lexer(stream),\n      xref: this,\n      allowStreams: true\n    });\n    const nums = new Array(n);\n    const offsets = new Array(n);\n    for (let i = 0; i < n; ++i) {\n      const num = parser.getObj();\n      if (!Number.isInteger(num)) {\n        throw new FormatError(`invalid object number in the ObjStm stream: ${num}`);\n      }\n      const offset = parser.getObj();\n      if (!Number.isInteger(offset)) {\n        throw new FormatError(`invalid object offset in the ObjStm stream: ${offset}`);\n      }\n      nums[i] = num;\n      offsets[i] = offset;\n    }\n    const start = (stream.start || 0) + first;\n    const entries = new Array(n);\n    for (let i = 0; i < n; ++i) {\n      const length = i < n - 1 ? offsets[i + 1] - offsets[i] : undefined;\n      if (length < 0) {\n        throw new FormatError(\"Invalid offset in the ObjStm stream.\");\n      }\n      parser = new Parser({\n        lexer: new Lexer(stream.makeSubStream(start + offsets[i], length, stream.dict)),\n        xref: this,\n        allowStreams: true\n      });\n      const obj = parser.getObj();\n      entries[i] = obj;\n      if (obj instanceof BaseStream) {\n        continue;\n      }\n      const num = nums[i],\n        entry = this.entries[num];\n      if (entry && entry.offset === tableOffset && entry.gen === i) {\n        this._cacheMap.set(num, obj);\n      }\n    }\n    xrefEntry = entries[xrefEntry.gen];\n    if (xrefEntry === undefined) {\n      throw new XRefEntryException(`Bad (compressed) XRef entry: ${ref}`);\n    }\n    return xrefEntry;\n  }\n  async fetchIfRefAsync(obj, suppressEncryption) {\n    if (obj instanceof Ref) {\n      return this.fetchAsync(obj, suppressEncryption);\n    }\n    return obj;\n  }\n  async fetchAsync(ref, suppressEncryption) {\n    try {\n      return this.fetch(ref, suppressEncryption);\n    } catch (ex) {\n      if (!(ex instanceof MissingDataException)) {\n        throw ex;\n      }\n      await this.pdfManager.requestRange(ex.begin, ex.end);\n      return this.fetchAsync(ref, suppressEncryption);\n    }\n  }\n  getCatalogObj() {\n    return this.root;\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/document.js\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nconst DEFAULT_USER_UNIT = 1.0;\nconst LETTER_SIZE_MEDIABOX = [0, 0, 612, 792];\nclass Page {\n  constructor({\n    pdfManager,\n    xref,\n    pageIndex,\n    pageDict,\n    ref,\n    globalIdFactory,\n    fontCache,\n    builtInCMapCache,\n    standardFontDataCache,\n    globalImageCache,\n    systemFontCache,\n    nonBlendModesSet,\n    xfaFactory\n  }) {\n    this.pdfManager = pdfManager;\n    this.pageIndex = pageIndex;\n    this.pageDict = pageDict;\n    this.xref = xref;\n    this.ref = ref;\n    this.fontCache = fontCache;\n    this.builtInCMapCache = builtInCMapCache;\n    this.standardFontDataCache = standardFontDataCache;\n    this.globalImageCache = globalImageCache;\n    this.systemFontCache = systemFontCache;\n    this.nonBlendModesSet = nonBlendModesSet;\n    this.evaluatorOptions = pdfManager.evaluatorOptions;\n    this.resourcesPromise = null;\n    this.xfaFactory = xfaFactory;\n    const idCounters = {\n      obj: 0\n    };\n    this._localIdFactory = class extends globalIdFactory {\n      static createObjId() {\n        return `p${pageIndex}_${++idCounters.obj}`;\n      }\n      static getPageObjId() {\n        return `p${ref.toString()}`;\n      }\n    };\n  }\n  _getInheritableProperty(key, getArray = false) {\n    const value = getInheritableProperty({\n      dict: this.pageDict,\n      key,\n      getArray,\n      stopWhenFound: false\n    });\n    if (!Array.isArray(value)) {\n      return value;\n    }\n    if (value.length === 1 || !(value[0] instanceof Dict)) {\n      return value[0];\n    }\n    return Dict.merge({\n      xref: this.xref,\n      dictArray: value\n    });\n  }\n  get content() {\n    return this.pageDict.getArray(\"Contents\");\n  }\n  get resources() {\n    const resources = this._getInheritableProperty(\"Resources\");\n    return shadow(this, \"resources\", resources instanceof Dict ? resources : Dict.empty);\n  }\n  _getBoundingBox(name) {\n    if (this.xfaData) {\n      return this.xfaData.bbox;\n    }\n    let box = this._getInheritableProperty(name, true);\n    if (Array.isArray(box) && box.length === 4) {\n      box = Util.normalizeRect(box);\n      if (box[2] - box[0] > 0 && box[3] - box[1] > 0) {\n        return box;\n      }\n      warn(`Empty, or invalid, /${name} entry.`);\n    }\n    return null;\n  }\n  get mediaBox() {\n    return shadow(this, \"mediaBox\", this._getBoundingBox(\"MediaBox\") || LETTER_SIZE_MEDIABOX);\n  }\n  get cropBox() {\n    return shadow(this, \"cropBox\", this._getBoundingBox(\"CropBox\") || this.mediaBox);\n  }\n  get userUnit() {\n    let obj = this.pageDict.get(\"UserUnit\");\n    if (typeof obj !== \"number\" || obj <= 0) {\n      obj = DEFAULT_USER_UNIT;\n    }\n    return shadow(this, \"userUnit\", obj);\n  }\n  get view() {\n    const {\n      cropBox,\n      mediaBox\n    } = this;\n    if (cropBox !== mediaBox && !isArrayEqual(cropBox, mediaBox)) {\n      const box = Util.intersect(cropBox, mediaBox);\n      if (box && box[2] - box[0] > 0 && box[3] - box[1] > 0) {\n        return shadow(this, \"view\", box);\n      }\n      warn(\"Empty /CropBox and /MediaBox intersection.\");\n    }\n    return shadow(this, \"view\", mediaBox);\n  }\n  get rotate() {\n    let rotate = this._getInheritableProperty(\"Rotate\") || 0;\n    if (rotate % 90 !== 0) {\n      rotate = 0;\n    } else if (rotate >= 360) {\n      rotate %= 360;\n    } else if (rotate < 0) {\n      rotate = (rotate % 360 + 360) % 360;\n    }\n    return shadow(this, \"rotate\", rotate);\n  }\n  _onSubStreamError(reason, objId) {\n    if (this.evaluatorOptions.ignoreErrors) {\n      warn(`getContentStream - ignoring sub-stream (${objId}): \"${reason}\".`);\n      return;\n    }\n    throw reason;\n  }\n  getContentStream() {\n    return this.pdfManager.ensure(this, \"content\").then(content => {\n      if (content instanceof BaseStream) {\n        return content;\n      }\n      if (Array.isArray(content)) {\n        return new StreamsSequenceStream(content, this._onSubStreamError.bind(this));\n      }\n      return new NullStream();\n    });\n  }\n  get xfaData() {\n    return shadow(this, \"xfaData\", this.xfaFactory ? {\n      bbox: this.xfaFactory.getBoundingBox(this.pageIndex)\n    } : null);\n  }\n  #replaceIdByRef(annotations, deletedAnnotations, existingAnnotations) {\n    for (const annotation of annotations) {\n      if (annotation.id) {\n        const ref = Ref.fromString(annotation.id);\n        if (!ref) {\n          warn(`A non-linked annotation cannot be modified: ${annotation.id}`);\n          continue;\n        }\n        if (annotation.deleted) {\n          deletedAnnotations.put(ref);\n          continue;\n        }\n        existingAnnotations?.put(ref);\n        annotation.ref = ref;\n        delete annotation.id;\n      }\n    }\n  }\n  async saveNewAnnotations(handler, task, annotations, imagePromises) {\n    if (this.xfaFactory) {\n      throw new Error(\"XFA: Cannot save new annotations.\");\n    }\n    const partialEvaluator = new PartialEvaluator({\n      xref: this.xref,\n      handler,\n      pageIndex: this.pageIndex,\n      idFactory: this._localIdFactory,\n      fontCache: this.fontCache,\n      builtInCMapCache: this.builtInCMapCache,\n      standardFontDataCache: this.standardFontDataCache,\n      globalImageCache: this.globalImageCache,\n      systemFontCache: this.systemFontCache,\n      options: this.evaluatorOptions\n    });\n    const deletedAnnotations = new RefSet();\n    const existingAnnotations = new RefSet();\n    this.#replaceIdByRef(annotations, deletedAnnotations, existingAnnotations);\n    const pageDict = this.pageDict;\n    const annotationsArray = this.annotations.filter(a => !(a instanceof Ref && deletedAnnotations.has(a)));\n    const newData = await AnnotationFactory.saveNewAnnotations(partialEvaluator, task, annotations, imagePromises);\n    for (const {\n      ref\n    } of newData.annotations) {\n      if (ref instanceof Ref && !existingAnnotations.has(ref)) {\n        annotationsArray.push(ref);\n      }\n    }\n    const savedDict = pageDict.get(\"Annots\");\n    pageDict.set(\"Annots\", annotationsArray);\n    const buffer = [];\n    await writeObject(this.ref, pageDict, buffer, this.xref);\n    if (savedDict) {\n      pageDict.set(\"Annots\", savedDict);\n    }\n    const objects = newData.dependencies;\n    objects.push({\n      ref: this.ref,\n      data: buffer.join(\"\")\n    }, ...newData.annotations);\n    return objects;\n  }\n  save(handler, task, annotationStorage) {\n    const partialEvaluator = new PartialEvaluator({\n      xref: this.xref,\n      handler,\n      pageIndex: this.pageIndex,\n      idFactory: this._localIdFactory,\n      fontCache: this.fontCache,\n      builtInCMapCache: this.builtInCMapCache,\n      standardFontDataCache: this.standardFontDataCache,\n      globalImageCache: this.globalImageCache,\n      systemFontCache: this.systemFontCache,\n      options: this.evaluatorOptions\n    });\n    return this._parsedAnnotations.then(function (annotations) {\n      const newRefsPromises = [];\n      for (const annotation of annotations) {\n        if (!annotation.mustBePrinted(annotationStorage)) {\n          continue;\n        }\n        newRefsPromises.push(annotation.save(partialEvaluator, task, annotationStorage).catch(function (reason) {\n          warn(\"save - ignoring annotation data during \" + `\"${task.name}\" task: \"${reason}\".`);\n          return null;\n        }));\n      }\n      return Promise.all(newRefsPromises).then(function (newRefs) {\n        return newRefs.filter(newRef => !!newRef);\n      });\n    });\n  }\n  loadResources(keys) {\n    if (!this.resourcesPromise) {\n      this.resourcesPromise = this.pdfManager.ensure(this, \"resources\");\n    }\n    return this.resourcesPromise.then(() => {\n      const objectLoader = new ObjectLoader(this.resources, keys, this.xref);\n      return objectLoader.load();\n    });\n  }\n  getOperatorList({\n    handler,\n    sink,\n    task,\n    intent,\n    cacheKey,\n    annotationStorage = null\n  }) {\n    const contentStreamPromise = this.getContentStream();\n    const resourcesPromise = this.loadResources([\"ColorSpace\", \"ExtGState\", \"Font\", \"Pattern\", \"Properties\", \"Shading\", \"XObject\"]);\n    const partialEvaluator = new PartialEvaluator({\n      xref: this.xref,\n      handler,\n      pageIndex: this.pageIndex,\n      idFactory: this._localIdFactory,\n      fontCache: this.fontCache,\n      builtInCMapCache: this.builtInCMapCache,\n      standardFontDataCache: this.standardFontDataCache,\n      globalImageCache: this.globalImageCache,\n      systemFontCache: this.systemFontCache,\n      options: this.evaluatorOptions\n    });\n    const newAnnotationsByPage = !this.xfaFactory ? getNewAnnotationsMap(annotationStorage) : null;\n    let deletedAnnotations = null;\n    let newAnnotationsPromise = Promise.resolve(null);\n    if (newAnnotationsByPage) {\n      const newAnnotations = newAnnotationsByPage.get(this.pageIndex);\n      if (newAnnotations) {\n        const annotationGlobalsPromise = this.pdfManager.ensureDoc(\"annotationGlobals\");\n        let imagePromises;\n        const missingBitmaps = new Set();\n        for (const {\n          bitmapId,\n          bitmap\n        } of newAnnotations) {\n          if (bitmapId && !bitmap && !missingBitmaps.has(bitmapId)) {\n            missingBitmaps.add(bitmapId);\n          }\n        }\n        const {\n          isOffscreenCanvasSupported\n        } = this.evaluatorOptions;\n        if (missingBitmaps.size > 0) {\n          const annotationWithBitmaps = newAnnotations.slice();\n          for (const [key, annotation] of annotationStorage) {\n            if (!key.startsWith(AnnotationEditorPrefix)) {\n              continue;\n            }\n            if (annotation.bitmap && missingBitmaps.has(annotation.bitmapId)) {\n              annotationWithBitmaps.push(annotation);\n            }\n          }\n          imagePromises = AnnotationFactory.generateImages(annotationWithBitmaps, this.xref, isOffscreenCanvasSupported);\n        } else {\n          imagePromises = AnnotationFactory.generateImages(newAnnotations, this.xref, isOffscreenCanvasSupported);\n        }\n        deletedAnnotations = new RefSet();\n        this.#replaceIdByRef(newAnnotations, deletedAnnotations, null);\n        newAnnotationsPromise = annotationGlobalsPromise.then(annotationGlobals => {\n          if (!annotationGlobals) {\n            return null;\n          }\n          return AnnotationFactory.printNewAnnotations(annotationGlobals, partialEvaluator, task, newAnnotations, imagePromises);\n        });\n      }\n    }\n    const dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);\n    const pageListPromise = dataPromises.then(([contentStream]) => {\n      const opList = new OperatorList(intent, sink);\n      handler.send(\"StartRenderPage\", {\n        transparency: partialEvaluator.hasBlendModes(this.resources, this.nonBlendModesSet),\n        pageIndex: this.pageIndex,\n        cacheKey\n      });\n      return partialEvaluator.getOperatorList({\n        stream: contentStream,\n        task,\n        resources: this.resources,\n        operatorList: opList\n      }).then(function () {\n        return opList;\n      });\n    });\n    return Promise.all([pageListPromise, this._parsedAnnotations, newAnnotationsPromise]).then(function ([pageOpList, annotations, newAnnotations]) {\n      if (newAnnotations) {\n        annotations = annotations.filter(a => !(a.ref && deletedAnnotations.has(a.ref)));\n        for (let i = 0, ii = newAnnotations.length; i < ii; i++) {\n          const newAnnotation = newAnnotations[i];\n          if (newAnnotation.refToReplace) {\n            const j = annotations.findIndex(a => a.ref && isRefsEqual(a.ref, newAnnotation.refToReplace));\n            if (j >= 0) {\n              annotations.splice(j, 1, newAnnotation);\n              newAnnotations.splice(i--, 1);\n              ii--;\n            }\n          }\n        }\n        annotations = annotations.concat(newAnnotations);\n      }\n      if (annotations.length === 0 || intent & RenderingIntentFlag.ANNOTATIONS_DISABLE) {\n        pageOpList.flush(true);\n        return {\n          length: pageOpList.totalLength\n        };\n      }\n      const renderForms = !!(intent & RenderingIntentFlag.ANNOTATIONS_FORMS),\n        intentAny = !!(intent & RenderingIntentFlag.ANY),\n        intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY),\n        intentPrint = !!(intent & RenderingIntentFlag.PRINT);\n      const opListPromises = [];\n      for (const annotation of annotations) {\n        if (intentAny || intentDisplay && annotation.mustBeViewed(annotationStorage, renderForms) || intentPrint && annotation.mustBePrinted(annotationStorage)) {\n          opListPromises.push(annotation.getOperatorList(partialEvaluator, task, intent, renderForms, annotationStorage).catch(function (reason) {\n            warn(\"getOperatorList - ignoring annotation data during \" + `\"${task.name}\" task: \"${reason}\".`);\n            return {\n              opList: null,\n              separateForm: false,\n              separateCanvas: false\n            };\n          }));\n        }\n      }\n      return Promise.all(opListPromises).then(function (opLists) {\n        let form = false,\n          canvas = false;\n        for (const {\n          opList,\n          separateForm,\n          separateCanvas\n        } of opLists) {\n          pageOpList.addOpList(opList);\n          form ||= separateForm;\n          canvas ||= separateCanvas;\n        }\n        pageOpList.flush(true, {\n          form,\n          canvas\n        });\n        return {\n          length: pageOpList.totalLength\n        };\n      });\n    });\n  }\n  extractTextContent({\n    handler,\n    task,\n    includeMarkedContent,\n    disableNormalization,\n    sink\n  }) {\n    const contentStreamPromise = this.getContentStream();\n    const resourcesPromise = this.loadResources([\"ExtGState\", \"Font\", \"Properties\", \"XObject\"]);\n    const dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);\n    return dataPromises.then(([contentStream]) => {\n      const partialEvaluator = new PartialEvaluator({\n        xref: this.xref,\n        handler,\n        pageIndex: this.pageIndex,\n        idFactory: this._localIdFactory,\n        fontCache: this.fontCache,\n        builtInCMapCache: this.builtInCMapCache,\n        standardFontDataCache: this.standardFontDataCache,\n        globalImageCache: this.globalImageCache,\n        systemFontCache: this.systemFontCache,\n        options: this.evaluatorOptions\n      });\n      return partialEvaluator.getTextContent({\n        stream: contentStream,\n        task,\n        resources: this.resources,\n        includeMarkedContent,\n        disableNormalization,\n        sink,\n        viewBox: this.view\n      });\n    });\n  }\n  async getStructTree() {\n    const structTreeRoot = await this.pdfManager.ensureCatalog(\"structTreeRoot\");\n    if (!structTreeRoot) {\n      return null;\n    }\n    await this._parsedAnnotations;\n    const structTree = await this.pdfManager.ensure(this, \"_parseStructTree\", [structTreeRoot]);\n    return structTree.serializable;\n  }\n  _parseStructTree(structTreeRoot) {\n    const tree = new StructTreePage(structTreeRoot, this.pageDict);\n    tree.parse(this.ref);\n    return tree;\n  }\n  async getAnnotationsData(handler, task, intent) {\n    const annotations = await this._parsedAnnotations;\n    if (annotations.length === 0) {\n      return annotations;\n    }\n    const annotationsData = [],\n      textContentPromises = [];\n    let partialEvaluator;\n    const intentAny = !!(intent & RenderingIntentFlag.ANY),\n      intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY),\n      intentPrint = !!(intent & RenderingIntentFlag.PRINT);\n    for (const annotation of annotations) {\n      const isVisible = intentAny || intentDisplay && annotation.viewable;\n      if (isVisible || intentPrint && annotation.printable) {\n        annotationsData.push(annotation.data);\n      }\n      if (annotation.hasTextContent && isVisible) {\n        partialEvaluator ||= new PartialEvaluator({\n          xref: this.xref,\n          handler,\n          pageIndex: this.pageIndex,\n          idFactory: this._localIdFactory,\n          fontCache: this.fontCache,\n          builtInCMapCache: this.builtInCMapCache,\n          standardFontDataCache: this.standardFontDataCache,\n          globalImageCache: this.globalImageCache,\n          systemFontCache: this.systemFontCache,\n          options: this.evaluatorOptions\n        });\n        textContentPromises.push(annotation.extractTextContent(partialEvaluator, task, [-Infinity, -Infinity, Infinity, Infinity]).catch(function (reason) {\n          warn(`getAnnotationsData - ignoring textContent during \"${task.name}\" task: \"${reason}\".`);\n        }));\n      }\n    }\n    await Promise.all(textContentPromises);\n    return annotationsData;\n  }\n  get annotations() {\n    const annots = this._getInheritableProperty(\"Annots\");\n    return shadow(this, \"annotations\", Array.isArray(annots) ? annots : []);\n  }\n  get _parsedAnnotations() {\n    const promise = this.pdfManager.ensure(this, \"annotations\").then(async annots => {\n      if (annots.length === 0) {\n        return annots;\n      }\n      const annotationGlobals = await this.pdfManager.ensureDoc(\"annotationGlobals\");\n      if (!annotationGlobals) {\n        return [];\n      }\n      const annotationPromises = [];\n      for (const annotationRef of annots) {\n        annotationPromises.push(AnnotationFactory.create(this.xref, annotationRef, annotationGlobals, this._localIdFactory, false, this.ref).catch(function (reason) {\n          warn(`_parsedAnnotations: \"${reason}\".`);\n          return null;\n        }));\n      }\n      const sortedAnnotations = [];\n      let popupAnnotations;\n      for (const annotation of await Promise.all(annotationPromises)) {\n        if (!annotation) {\n          continue;\n        }\n        if (annotation instanceof PopupAnnotation) {\n          (popupAnnotations ||= []).push(annotation);\n          continue;\n        }\n        sortedAnnotations.push(annotation);\n      }\n      if (popupAnnotations) {\n        sortedAnnotations.push(...popupAnnotations);\n      }\n      return sortedAnnotations;\n    });\n    return shadow(this, \"_parsedAnnotations\", promise);\n  }\n  get jsActions() {\n    const actions = collectActions(this.xref, this.pageDict, PageActionEventType);\n    return shadow(this, \"jsActions\", actions);\n  }\n}\nconst PDF_HEADER_SIGNATURE = new Uint8Array([0x25, 0x50, 0x44, 0x46, 0x2d]);\nconst STARTXREF_SIGNATURE = new Uint8Array([0x73, 0x74, 0x61, 0x72, 0x74, 0x78, 0x72, 0x65, 0x66]);\nconst ENDOBJ_SIGNATURE = new Uint8Array([0x65, 0x6e, 0x64, 0x6f, 0x62, 0x6a]);\nconst FINGERPRINT_FIRST_BYTES = 1024;\nconst EMPTY_FINGERPRINT = \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\";\nfunction find(stream, signature, limit = 1024, backwards = false) {\n  const signatureLength = signature.length;\n  const scanBytes = stream.peekBytes(limit);\n  const scanLength = scanBytes.length - signatureLength;\n  if (scanLength <= 0) {\n    return false;\n  }\n  if (backwards) {\n    const signatureEnd = signatureLength - 1;\n    let pos = scanBytes.length - 1;\n    while (pos >= signatureEnd) {\n      let j = 0;\n      while (j < signatureLength && scanBytes[pos - j] === signature[signatureEnd - j]) {\n        j++;\n      }\n      if (j >= signatureLength) {\n        stream.pos += pos - signatureEnd;\n        return true;\n      }\n      pos--;\n    }\n  } else {\n    let pos = 0;\n    while (pos <= scanLength) {\n      let j = 0;\n      while (j < signatureLength && scanBytes[pos + j] === signature[j]) {\n        j++;\n      }\n      if (j >= signatureLength) {\n        stream.pos += pos;\n        return true;\n      }\n      pos++;\n    }\n  }\n  return false;\n}\nclass PDFDocument {\n  constructor(pdfManager, stream) {\n    if (stream.length <= 0) {\n      throw new InvalidPDFException(\"The PDF file is empty, i.e. its size is zero bytes.\");\n    }\n    this.pdfManager = pdfManager;\n    this.stream = stream;\n    this.xref = new XRef(stream, pdfManager);\n    this._pagePromises = new Map();\n    this._version = null;\n    const idCounters = {\n      font: 0\n    };\n    this._globalIdFactory = class {\n      static getDocId() {\n        return `g_${pdfManager.docId}`;\n      }\n      static createFontId() {\n        return `f${++idCounters.font}`;\n      }\n      static createObjId() {\n        unreachable(\"Abstract method `createObjId` called.\");\n      }\n      static getPageObjId() {\n        unreachable(\"Abstract method `getPageObjId` called.\");\n      }\n    };\n  }\n  parse(recoveryMode) {\n    this.xref.parse(recoveryMode);\n    this.catalog = new Catalog(this.pdfManager, this.xref);\n  }\n  get linearization() {\n    let linearization = null;\n    try {\n      linearization = Linearization.create(this.stream);\n    } catch (err) {\n      if (err instanceof MissingDataException) {\n        throw err;\n      }\n      info(err);\n    }\n    return shadow(this, \"linearization\", linearization);\n  }\n  get startXRef() {\n    const stream = this.stream;\n    let startXRef = 0;\n    if (this.linearization) {\n      stream.reset();\n      if (find(stream, ENDOBJ_SIGNATURE)) {\n        startXRef = stream.pos + 6 - stream.start;\n      }\n    } else {\n      const step = 1024;\n      const startXRefLength = STARTXREF_SIGNATURE.length;\n      let found = false,\n        pos = stream.end;\n      while (!found && pos > 0) {\n        pos -= step - startXRefLength;\n        if (pos < 0) {\n          pos = 0;\n        }\n        stream.pos = pos;\n        found = find(stream, STARTXREF_SIGNATURE, step, true);\n      }\n      if (found) {\n        stream.skip(9);\n        let ch;\n        do {\n          ch = stream.getByte();\n        } while (isWhiteSpace(ch));\n        let str = \"\";\n        while (ch >= 0x20 && ch <= 0x39) {\n          str += String.fromCharCode(ch);\n          ch = stream.getByte();\n        }\n        startXRef = parseInt(str, 10);\n        if (isNaN(startXRef)) {\n          startXRef = 0;\n        }\n      }\n    }\n    return shadow(this, \"startXRef\", startXRef);\n  }\n  checkHeader() {\n    const stream = this.stream;\n    stream.reset();\n    if (!find(stream, PDF_HEADER_SIGNATURE)) {\n      return;\n    }\n    stream.moveStart();\n    stream.skip(PDF_HEADER_SIGNATURE.length);\n    let version = \"\",\n      ch;\n    while ((ch = stream.getByte()) > 0x20 && version.length < 7) {\n      version += String.fromCharCode(ch);\n    }\n    if (PDF_VERSION_REGEXP.test(version)) {\n      this._version = version;\n    } else {\n      warn(`Invalid PDF header version: ${version}`);\n    }\n  }\n  parseStartXRef() {\n    this.xref.setStartXRef(this.startXRef);\n  }\n  get numPages() {\n    let num = 0;\n    if (this.catalog.hasActualNumPages) {\n      num = this.catalog.numPages;\n    } else if (this.xfaFactory) {\n      num = this.xfaFactory.getNumPages();\n    } else if (this.linearization) {\n      num = this.linearization.numPages;\n    } else {\n      num = this.catalog.numPages;\n    }\n    return shadow(this, \"numPages\", num);\n  }\n  _hasOnlyDocumentSignatures(fields, recursionDepth = 0) {\n    const RECURSION_LIMIT = 10;\n    if (!Array.isArray(fields)) {\n      return false;\n    }\n    return fields.every(field => {\n      field = this.xref.fetchIfRef(field);\n      if (!(field instanceof Dict)) {\n        return false;\n      }\n      if (field.has(\"Kids\")) {\n        if (++recursionDepth > RECURSION_LIMIT) {\n          warn(\"_hasOnlyDocumentSignatures: maximum recursion depth reached\");\n          return false;\n        }\n        return this._hasOnlyDocumentSignatures(field.get(\"Kids\"), recursionDepth);\n      }\n      const isSignature = isName(field.get(\"FT\"), \"Sig\");\n      const rectangle = field.get(\"Rect\");\n      const isInvisible = Array.isArray(rectangle) && rectangle.every(value => value === 0);\n      return isSignature && isInvisible;\n    });\n  }\n  get _xfaStreams() {\n    const acroForm = this.catalog.acroForm;\n    if (!acroForm) {\n      return null;\n    }\n    const xfa = acroForm.get(\"XFA\");\n    const entries = {\n      \"xdp:xdp\": \"\",\n      template: \"\",\n      datasets: \"\",\n      config: \"\",\n      connectionSet: \"\",\n      localeSet: \"\",\n      stylesheet: \"\",\n      \"/xdp:xdp\": \"\"\n    };\n    if (xfa instanceof BaseStream && !xfa.isEmpty) {\n      entries[\"xdp:xdp\"] = xfa;\n      return entries;\n    }\n    if (!Array.isArray(xfa) || xfa.length === 0) {\n      return null;\n    }\n    for (let i = 0, ii = xfa.length; i < ii; i += 2) {\n      let name;\n      if (i === 0) {\n        name = \"xdp:xdp\";\n      } else if (i === ii - 2) {\n        name = \"/xdp:xdp\";\n      } else {\n        name = xfa[i];\n      }\n      if (!entries.hasOwnProperty(name)) {\n        continue;\n      }\n      const data = this.xref.fetchIfRef(xfa[i + 1]);\n      if (!(data instanceof BaseStream) || data.isEmpty) {\n        continue;\n      }\n      entries[name] = data;\n    }\n    return entries;\n  }\n  get xfaDatasets() {\n    const streams = this._xfaStreams;\n    if (!streams) {\n      return shadow(this, \"xfaDatasets\", null);\n    }\n    for (const key of [\"datasets\", \"xdp:xdp\"]) {\n      const stream = streams[key];\n      if (!stream) {\n        continue;\n      }\n      try {\n        const str = stringToUTF8String(stream.getString());\n        const data = {\n          [key]: str\n        };\n        return shadow(this, \"xfaDatasets\", new DatasetReader(data));\n      } catch {\n        warn(\"XFA - Invalid utf-8 string.\");\n        break;\n      }\n    }\n    return shadow(this, \"xfaDatasets\", null);\n  }\n  get xfaData() {\n    const streams = this._xfaStreams;\n    if (!streams) {\n      return null;\n    }\n    const data = Object.create(null);\n    for (const [key, stream] of Object.entries(streams)) {\n      if (!stream) {\n        continue;\n      }\n      try {\n        data[key] = stringToUTF8String(stream.getString());\n      } catch {\n        warn(\"XFA - Invalid utf-8 string.\");\n        return null;\n      }\n    }\n    return data;\n  }\n  get xfaFactory() {\n    let data;\n    if (this.pdfManager.enableXfa && this.catalog.needsRendering && this.formInfo.hasXfa && !this.formInfo.hasAcroForm) {\n      data = this.xfaData;\n    }\n    return shadow(this, \"xfaFactory\", data ? new XFAFactory(data) : null);\n  }\n  get isPureXfa() {\n    return this.xfaFactory ? this.xfaFactory.isValid() : false;\n  }\n  get htmlForXfa() {\n    return this.xfaFactory ? this.xfaFactory.getPages() : null;\n  }\n  async loadXfaImages() {\n    const xfaImagesDict = await this.pdfManager.ensureCatalog(\"xfaImages\");\n    if (!xfaImagesDict) {\n      return;\n    }\n    const keys = xfaImagesDict.getKeys();\n    const objectLoader = new ObjectLoader(xfaImagesDict, keys, this.xref);\n    await objectLoader.load();\n    const xfaImages = new Map();\n    for (const key of keys) {\n      const stream = xfaImagesDict.get(key);\n      if (stream instanceof BaseStream) {\n        xfaImages.set(key, stream.getBytes());\n      }\n    }\n    this.xfaFactory.setImages(xfaImages);\n  }\n  async loadXfaFonts(handler, task) {\n    const acroForm = await this.pdfManager.ensureCatalog(\"acroForm\");\n    if (!acroForm) {\n      return;\n    }\n    const resources = await acroForm.getAsync(\"DR\");\n    if (!(resources instanceof Dict)) {\n      return;\n    }\n    const objectLoader = new ObjectLoader(resources, [\"Font\"], this.xref);\n    await objectLoader.load();\n    const fontRes = resources.get(\"Font\");\n    if (!(fontRes instanceof Dict)) {\n      return;\n    }\n    const options = Object.assign(Object.create(null), this.pdfManager.evaluatorOptions);\n    options.useSystemFonts = false;\n    const partialEvaluator = new PartialEvaluator({\n      xref: this.xref,\n      handler,\n      pageIndex: -1,\n      idFactory: this._globalIdFactory,\n      fontCache: this.catalog.fontCache,\n      builtInCMapCache: this.catalog.builtInCMapCache,\n      standardFontDataCache: this.catalog.standardFontDataCache,\n      options\n    });\n    const operatorList = new OperatorList();\n    const pdfFonts = [];\n    const initialState = {\n      get font() {\n        return pdfFonts.at(-1);\n      },\n      set font(font) {\n        pdfFonts.push(font);\n      },\n      clone() {\n        return this;\n      }\n    };\n    const fonts = new Map();\n    fontRes.forEach((fontName, font) => {\n      fonts.set(fontName, font);\n    });\n    const promises = [];\n    for (const [fontName, font] of fonts) {\n      const descriptor = font.get(\"FontDescriptor\");\n      if (!(descriptor instanceof Dict)) {\n        continue;\n      }\n      let fontFamily = descriptor.get(\"FontFamily\");\n      fontFamily = fontFamily.replaceAll(/[ ]+(\\d)/g, \"$1\");\n      const fontWeight = descriptor.get(\"FontWeight\");\n      const italicAngle = -descriptor.get(\"ItalicAngle\");\n      const cssFontInfo = {\n        fontFamily,\n        fontWeight,\n        italicAngle\n      };\n      if (!validateCSSFont(cssFontInfo)) {\n        continue;\n      }\n      promises.push(partialEvaluator.handleSetFont(resources, [Name.get(fontName), 1], null, operatorList, task, initialState, null, cssFontInfo).catch(function (reason) {\n        warn(`loadXfaFonts: \"${reason}\".`);\n        return null;\n      }));\n    }\n    await Promise.all(promises);\n    const missingFonts = this.xfaFactory.setFonts(pdfFonts);\n    if (!missingFonts) {\n      return;\n    }\n    options.ignoreErrors = true;\n    promises.length = 0;\n    pdfFonts.length = 0;\n    const reallyMissingFonts = new Set();\n    for (const missing of missingFonts) {\n      if (!getXfaFontName(`${missing}-Regular`)) {\n        reallyMissingFonts.add(missing);\n      }\n    }\n    if (reallyMissingFonts.size) {\n      missingFonts.push(\"PdfJS-Fallback\");\n    }\n    for (const missing of missingFonts) {\n      if (reallyMissingFonts.has(missing)) {\n        continue;\n      }\n      for (const fontInfo of [{\n        name: \"Regular\",\n        fontWeight: 400,\n        italicAngle: 0\n      }, {\n        name: \"Bold\",\n        fontWeight: 700,\n        italicAngle: 0\n      }, {\n        name: \"Italic\",\n        fontWeight: 400,\n        italicAngle: 12\n      }, {\n        name: \"BoldItalic\",\n        fontWeight: 700,\n        italicAngle: 12\n      }]) {\n        const name = `${missing}-${fontInfo.name}`;\n        const dict = getXfaFontDict(name);\n        promises.push(partialEvaluator.handleSetFont(resources, [Name.get(name), 1], null, operatorList, task, initialState, dict, {\n          fontFamily: missing,\n          fontWeight: fontInfo.fontWeight,\n          italicAngle: fontInfo.italicAngle\n        }).catch(function (reason) {\n          warn(`loadXfaFonts: \"${reason}\".`);\n          return null;\n        }));\n      }\n    }\n    await Promise.all(promises);\n    this.xfaFactory.appendFonts(pdfFonts, reallyMissingFonts);\n  }\n  async serializeXfaData(annotationStorage) {\n    return this.xfaFactory ? this.xfaFactory.serializeData(annotationStorage) : null;\n  }\n  get version() {\n    return this.catalog.version || this._version;\n  }\n  get formInfo() {\n    const formInfo = {\n      hasFields: false,\n      hasAcroForm: false,\n      hasXfa: false,\n      hasSignatures: false\n    };\n    const acroForm = this.catalog.acroForm;\n    if (!acroForm) {\n      return shadow(this, \"formInfo\", formInfo);\n    }\n    try {\n      const fields = acroForm.get(\"Fields\");\n      const hasFields = Array.isArray(fields) && fields.length > 0;\n      formInfo.hasFields = hasFields;\n      const xfa = acroForm.get(\"XFA\");\n      formInfo.hasXfa = Array.isArray(xfa) && xfa.length > 0 || xfa instanceof BaseStream && !xfa.isEmpty;\n      const sigFlags = acroForm.get(\"SigFlags\");\n      const hasSignatures = !!(sigFlags & 0x1);\n      const hasOnlyDocumentSignatures = hasSignatures && this._hasOnlyDocumentSignatures(fields);\n      formInfo.hasAcroForm = hasFields && !hasOnlyDocumentSignatures;\n      formInfo.hasSignatures = hasSignatures;\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(`Cannot fetch form information: \"${ex}\".`);\n    }\n    return shadow(this, \"formInfo\", formInfo);\n  }\n  get documentInfo() {\n    const docInfo = {\n      PDFFormatVersion: this.version,\n      Language: this.catalog.lang,\n      EncryptFilterName: this.xref.encrypt ? this.xref.encrypt.filterName : null,\n      IsLinearized: !!this.linearization,\n      IsAcroFormPresent: this.formInfo.hasAcroForm,\n      IsXFAPresent: this.formInfo.hasXfa,\n      IsCollectionPresent: !!this.catalog.collection,\n      IsSignaturesPresent: this.formInfo.hasSignatures\n    };\n    let infoDict;\n    try {\n      infoDict = this.xref.trailer.get(\"Info\");\n    } catch (err) {\n      if (err instanceof MissingDataException) {\n        throw err;\n      }\n      info(\"The document information dictionary is invalid.\");\n    }\n    if (!(infoDict instanceof Dict)) {\n      return shadow(this, \"documentInfo\", docInfo);\n    }\n    for (const key of infoDict.getKeys()) {\n      const value = infoDict.get(key);\n      switch (key) {\n        case \"Title\":\n        case \"Author\":\n        case \"Subject\":\n        case \"Keywords\":\n        case \"Creator\":\n        case \"Producer\":\n        case \"CreationDate\":\n        case \"ModDate\":\n          if (typeof value === \"string\") {\n            docInfo[key] = stringToPDFString(value);\n            continue;\n          }\n          break;\n        case \"Trapped\":\n          if (value instanceof Name) {\n            docInfo[key] = value;\n            continue;\n          }\n          break;\n        default:\n          let customValue;\n          switch (typeof value) {\n            case \"string\":\n              customValue = stringToPDFString(value);\n              break;\n            case \"number\":\n            case \"boolean\":\n              customValue = value;\n              break;\n            default:\n              if (value instanceof Name) {\n                customValue = value;\n              }\n              break;\n          }\n          if (customValue === undefined) {\n            warn(`Bad value, for custom key \"${key}\", in Info: ${value}.`);\n            continue;\n          }\n          if (!docInfo.Custom) {\n            docInfo.Custom = Object.create(null);\n          }\n          docInfo.Custom[key] = customValue;\n          continue;\n      }\n      warn(`Bad value, for key \"${key}\", in Info: ${value}.`);\n    }\n    return shadow(this, \"documentInfo\", docInfo);\n  }\n  get fingerprints() {\n    function validate(data) {\n      return typeof data === \"string\" && data.length > 0 && data !== EMPTY_FINGERPRINT;\n    }\n    function hexString(hash) {\n      const buf = [];\n      for (const num of hash) {\n        const hex = num.toString(16);\n        buf.push(hex.padStart(2, \"0\"));\n      }\n      return buf.join(\"\");\n    }\n    const idArray = this.xref.trailer.get(\"ID\");\n    let hashOriginal, hashModified;\n    if (Array.isArray(idArray) && validate(idArray[0])) {\n      hashOriginal = stringToBytes(idArray[0]);\n      if (idArray[1] !== idArray[0] && validate(idArray[1])) {\n        hashModified = stringToBytes(idArray[1]);\n      }\n    } else {\n      hashOriginal = calculateMD5(this.stream.getByteRange(0, FINGERPRINT_FIRST_BYTES), 0, FINGERPRINT_FIRST_BYTES);\n    }\n    return shadow(this, \"fingerprints\", [hexString(hashOriginal), hashModified ? hexString(hashModified) : null]);\n  }\n  async _getLinearizationPage(pageIndex) {\n    const {\n      catalog,\n      linearization,\n      xref\n    } = this;\n    const ref = Ref.get(linearization.objectNumberFirst, 0);\n    try {\n      const obj = await xref.fetchAsync(ref);\n      if (obj instanceof Dict) {\n        let type = obj.getRaw(\"Type\");\n        if (type instanceof Ref) {\n          type = await xref.fetchAsync(type);\n        }\n        if (isName(type, \"Page\") || !obj.has(\"Type\") && !obj.has(\"Kids\")) {\n          if (!catalog.pageKidsCountCache.has(ref)) {\n            catalog.pageKidsCountCache.put(ref, 1);\n          }\n          if (!catalog.pageIndexCache.has(ref)) {\n            catalog.pageIndexCache.put(ref, 0);\n          }\n          return [obj, ref];\n        }\n      }\n      throw new FormatError(\"The Linearization dictionary doesn't point to a valid Page dictionary.\");\n    } catch (reason) {\n      warn(`_getLinearizationPage: \"${reason.message}\".`);\n      return catalog.getPageDict(pageIndex);\n    }\n  }\n  getPage(pageIndex) {\n    const cachedPromise = this._pagePromises.get(pageIndex);\n    if (cachedPromise) {\n      return cachedPromise;\n    }\n    const {\n      catalog,\n      linearization,\n      xfaFactory\n    } = this;\n    let promise;\n    if (xfaFactory) {\n      promise = Promise.resolve([Dict.empty, null]);\n    } else if (linearization?.pageFirst === pageIndex) {\n      promise = this._getLinearizationPage(pageIndex);\n    } else {\n      promise = catalog.getPageDict(pageIndex);\n    }\n    promise = promise.then(([pageDict, ref]) => {\n      return new Page({\n        pdfManager: this.pdfManager,\n        xref: this.xref,\n        pageIndex,\n        pageDict,\n        ref,\n        globalIdFactory: this._globalIdFactory,\n        fontCache: catalog.fontCache,\n        builtInCMapCache: catalog.builtInCMapCache,\n        standardFontDataCache: catalog.standardFontDataCache,\n        globalImageCache: catalog.globalImageCache,\n        systemFontCache: catalog.systemFontCache,\n        nonBlendModesSet: catalog.nonBlendModesSet,\n        xfaFactory\n      });\n    });\n    this._pagePromises.set(pageIndex, promise);\n    return promise;\n  }\n  async checkFirstPage(recoveryMode = false) {\n    if (recoveryMode) {\n      return;\n    }\n    try {\n      await this.getPage(0);\n    } catch (reason) {\n      if (reason instanceof XRefEntryException) {\n        this._pagePromises.delete(0);\n        await this.cleanup();\n        throw new XRefParseException();\n      }\n    }\n  }\n  async checkLastPage(recoveryMode = false) {\n    const {\n      catalog,\n      pdfManager\n    } = this;\n    catalog.setActualNumPages();\n    let numPages;\n    try {\n      await Promise.all([pdfManager.ensureDoc(\"xfaFactory\"), pdfManager.ensureDoc(\"linearization\"), pdfManager.ensureCatalog(\"numPages\")]);\n      if (this.xfaFactory) {\n        return;\n      } else if (this.linearization) {\n        numPages = this.linearization.numPages;\n      } else {\n        numPages = catalog.numPages;\n      }\n      if (!Number.isInteger(numPages)) {\n        throw new FormatError(\"Page count is not an integer.\");\n      } else if (numPages <= 1) {\n        return;\n      }\n      await this.getPage(numPages - 1);\n    } catch (reason) {\n      this._pagePromises.delete(numPages - 1);\n      await this.cleanup();\n      if (reason instanceof XRefEntryException && !recoveryMode) {\n        throw new XRefParseException();\n      }\n      warn(`checkLastPage - invalid /Pages tree /Count: ${numPages}.`);\n      let pagesTree;\n      try {\n        pagesTree = await catalog.getAllPageDicts(recoveryMode);\n      } catch (reasonAll) {\n        if (reasonAll instanceof XRefEntryException && !recoveryMode) {\n          throw new XRefParseException();\n        }\n        catalog.setActualNumPages(1);\n        return;\n      }\n      for (const [pageIndex, [pageDict, ref]] of pagesTree) {\n        let promise;\n        if (pageDict instanceof Error) {\n          promise = Promise.reject(pageDict);\n          promise.catch(() => {});\n        } else {\n          promise = Promise.resolve(new Page({\n            pdfManager,\n            xref: this.xref,\n            pageIndex,\n            pageDict,\n            ref,\n            globalIdFactory: this._globalIdFactory,\n            fontCache: catalog.fontCache,\n            builtInCMapCache: catalog.builtInCMapCache,\n            standardFontDataCache: catalog.standardFontDataCache,\n            globalImageCache: catalog.globalImageCache,\n            systemFontCache: catalog.systemFontCache,\n            nonBlendModesSet: catalog.nonBlendModesSet,\n            xfaFactory: null\n          }));\n        }\n        this._pagePromises.set(pageIndex, promise);\n      }\n      catalog.setActualNumPages(pagesTree.size);\n    }\n  }\n  fontFallback(id, handler) {\n    return this.catalog.fontFallback(id, handler);\n  }\n  async cleanup(manuallyTriggered = false) {\n    return this.catalog ? this.catalog.cleanup(manuallyTriggered) : clearGlobalCaches();\n  }\n  async #collectFieldObjects(name, fieldRef, promises, annotationGlobals, visitedRefs) {\n    const {\n      xref\n    } = this;\n    if (!(fieldRef instanceof Ref) || visitedRefs.has(fieldRef)) {\n      return;\n    }\n    visitedRefs.put(fieldRef);\n    const field = await xref.fetchAsync(fieldRef);\n    if (!(field instanceof Dict)) {\n      return;\n    }\n    if (field.has(\"T\")) {\n      const partName = stringToPDFString(await field.getAsync(\"T\"));\n      name = name === \"\" ? partName : `${name}.${partName}`;\n    } else {\n      let obj = field;\n      while (true) {\n        obj = obj.getRaw(\"Parent\");\n        if (obj instanceof Ref) {\n          if (visitedRefs.has(obj)) {\n            break;\n          }\n          obj = await xref.fetchAsync(obj);\n        }\n        if (!(obj instanceof Dict)) {\n          break;\n        }\n        if (obj.has(\"T\")) {\n          const partName = stringToPDFString(await obj.getAsync(\"T\"));\n          name = name === \"\" ? partName : `${name}.${partName}`;\n          break;\n        }\n      }\n    }\n    if (!promises.has(name)) {\n      promises.set(name, []);\n    }\n    promises.get(name).push(AnnotationFactory.create(xref, fieldRef, annotationGlobals, null, true, null).then(annotation => annotation?.getFieldObject()).catch(function (reason) {\n      warn(`#collectFieldObjects: \"${reason}\".`);\n      return null;\n    }));\n    if (!field.has(\"Kids\")) {\n      return;\n    }\n    const kids = await field.getAsync(\"Kids\");\n    if (Array.isArray(kids)) {\n      for (const kid of kids) {\n        await this.#collectFieldObjects(name, kid, promises, annotationGlobals, visitedRefs);\n      }\n    }\n  }\n  get fieldObjects() {\n    if (!this.formInfo.hasFields) {\n      return shadow(this, \"fieldObjects\", Promise.resolve(null));\n    }\n    const promise = Promise.all([this.pdfManager.ensureDoc(\"annotationGlobals\"), this.pdfManager.ensureCatalog(\"acroForm\")]).then(async ([annotationGlobals, acroForm]) => {\n      if (!annotationGlobals) {\n        return null;\n      }\n      const visitedRefs = new RefSet();\n      const allFields = Object.create(null);\n      const fieldPromises = new Map();\n      for (const fieldRef of await acroForm.getAsync(\"Fields\")) {\n        await this.#collectFieldObjects(\"\", fieldRef, fieldPromises, annotationGlobals, visitedRefs);\n      }\n      const allPromises = [];\n      for (const [name, promises] of fieldPromises) {\n        allPromises.push(Promise.all(promises).then(fields => {\n          fields = fields.filter(field => !!field);\n          if (fields.length > 0) {\n            allFields[name] = fields;\n          }\n        }));\n      }\n      await Promise.all(allPromises);\n      return allFields;\n    });\n    return shadow(this, \"fieldObjects\", promise);\n  }\n  get hasJSActions() {\n    const promise = this.pdfManager.ensureDoc(\"_parseHasJSActions\");\n    return shadow(this, \"hasJSActions\", promise);\n  }\n  async _parseHasJSActions() {\n    const [catalogJsActions, fieldObjects] = await Promise.all([this.pdfManager.ensureCatalog(\"jsActions\"), this.pdfManager.ensureDoc(\"fieldObjects\")]);\n    if (catalogJsActions) {\n      return true;\n    }\n    if (fieldObjects) {\n      return Object.values(fieldObjects).some(fieldObject => fieldObject.some(object => object.actions !== null));\n    }\n    return false;\n  }\n  get calculationOrderIds() {\n    const acroForm = this.catalog.acroForm;\n    if (!acroForm?.has(\"CO\")) {\n      return shadow(this, \"calculationOrderIds\", null);\n    }\n    const calculationOrder = acroForm.get(\"CO\");\n    if (!Array.isArray(calculationOrder) || calculationOrder.length === 0) {\n      return shadow(this, \"calculationOrderIds\", null);\n    }\n    const ids = [];\n    for (const id of calculationOrder) {\n      if (id instanceof Ref) {\n        ids.push(id.toString());\n      }\n    }\n    if (ids.length === 0) {\n      return shadow(this, \"calculationOrderIds\", null);\n    }\n    return shadow(this, \"calculationOrderIds\", ids);\n  }\n  get annotationGlobals() {\n    return shadow(this, \"annotationGlobals\", AnnotationFactory.createGlobals(this.pdfManager));\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/pdf_manager.js\n\n\n\n\n\nfunction parseDocBaseUrl(url) {\n  if (url) {\n    const absoluteUrl = createValidAbsoluteUrl(url);\n    if (absoluteUrl) {\n      return absoluteUrl.href;\n    }\n    warn(`Invalid absolute docBaseUrl: \"${url}\".`);\n  }\n  return null;\n}\nclass BasePdfManager {\n  constructor(args) {\n    if (this.constructor === BasePdfManager) {\n      unreachable(\"Cannot initialize BasePdfManager.\");\n    }\n    this._docBaseUrl = parseDocBaseUrl(args.docBaseUrl);\n    this._docId = args.docId;\n    this._password = args.password;\n    this.enableXfa = args.enableXfa;\n    args.evaluatorOptions.isOffscreenCanvasSupported &&= FeatureTest.isOffscreenCanvasSupported;\n    this.evaluatorOptions = args.evaluatorOptions;\n  }\n  get docId() {\n    return this._docId;\n  }\n  get password() {\n    return this._password;\n  }\n  get docBaseUrl() {\n    return this._docBaseUrl;\n  }\n  get catalog() {\n    return this.pdfDocument.catalog;\n  }\n  ensureDoc(prop, args) {\n    return this.ensure(this.pdfDocument, prop, args);\n  }\n  ensureXRef(prop, args) {\n    return this.ensure(this.pdfDocument.xref, prop, args);\n  }\n  ensureCatalog(prop, args) {\n    return this.ensure(this.pdfDocument.catalog, prop, args);\n  }\n  getPage(pageIndex) {\n    return this.pdfDocument.getPage(pageIndex);\n  }\n  fontFallback(id, handler) {\n    return this.pdfDocument.fontFallback(id, handler);\n  }\n  loadXfaFonts(handler, task) {\n    return this.pdfDocument.loadXfaFonts(handler, task);\n  }\n  loadXfaImages() {\n    return this.pdfDocument.loadXfaImages();\n  }\n  serializeXfaData(annotationStorage) {\n    return this.pdfDocument.serializeXfaData(annotationStorage);\n  }\n  cleanup(manuallyTriggered = false) {\n    return this.pdfDocument.cleanup(manuallyTriggered);\n  }\n  async ensure(obj, prop, args) {\n    unreachable(\"Abstract method `ensure` called\");\n  }\n  requestRange(begin, end) {\n    unreachable(\"Abstract method `requestRange` called\");\n  }\n  requestLoadedStream(noFetch = false) {\n    unreachable(\"Abstract method `requestLoadedStream` called\");\n  }\n  sendProgressiveData(chunk) {\n    unreachable(\"Abstract method `sendProgressiveData` called\");\n  }\n  updatePassword(password) {\n    this._password = password;\n  }\n  terminate(reason) {\n    unreachable(\"Abstract method `terminate` called\");\n  }\n}\nclass LocalPdfManager extends BasePdfManager {\n  constructor(args) {\n    super(args);\n    const stream = new Stream(args.source);\n    this.pdfDocument = new PDFDocument(this, stream);\n    this._loadedStreamPromise = Promise.resolve(stream);\n  }\n  async ensure(obj, prop, args) {\n    const value = obj[prop];\n    if (typeof value === \"function\") {\n      return value.apply(obj, args);\n    }\n    return value;\n  }\n  requestRange(begin, end) {\n    return Promise.resolve();\n  }\n  requestLoadedStream(noFetch = false) {\n    return this._loadedStreamPromise;\n  }\n  terminate(reason) {}\n}\nclass NetworkPdfManager extends BasePdfManager {\n  constructor(args) {\n    super(args);\n    this.streamManager = new ChunkedStreamManager(args.source, {\n      msgHandler: args.handler,\n      length: args.length,\n      disableAutoFetch: args.disableAutoFetch,\n      rangeChunkSize: args.rangeChunkSize\n    });\n    this.pdfDocument = new PDFDocument(this, this.streamManager.getStream());\n  }\n  async ensure(obj, prop, args) {\n    try {\n      const value = obj[prop];\n      if (typeof value === \"function\") {\n        return value.apply(obj, args);\n      }\n      return value;\n    } catch (ex) {\n      if (!(ex instanceof MissingDataException)) {\n        throw ex;\n      }\n      await this.requestRange(ex.begin, ex.end);\n      return this.ensure(obj, prop, args);\n    }\n  }\n  requestRange(begin, end) {\n    return this.streamManager.requestRange(begin, end);\n  }\n  requestLoadedStream(noFetch = false) {\n    return this.streamManager.requestAllChunks(noFetch);\n  }\n  sendProgressiveData(chunk) {\n    this.streamManager.onReceiveData({\n      chunk\n    });\n  }\n  terminate(reason) {\n    this.streamManager.abort(reason);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/shared/message_handler.js\n\nconst CallbackKind = {\n  UNKNOWN: 0,\n  DATA: 1,\n  ERROR: 2\n};\nconst StreamKind = {\n  UNKNOWN: 0,\n  CANCEL: 1,\n  CANCEL_COMPLETE: 2,\n  CLOSE: 3,\n  ENQUEUE: 4,\n  ERROR: 5,\n  PULL: 6,\n  PULL_COMPLETE: 7,\n  START_COMPLETE: 8\n};\nfunction wrapReason(reason) {\n  if (!(reason instanceof Error || typeof reason === \"object\" && reason !== null)) {\n    unreachable('wrapReason: Expected \"reason\" to be a (possibly cloned) Error.');\n  }\n  switch (reason.name) {\n    case \"AbortException\":\n      return new AbortException(reason.message);\n    case \"MissingPDFException\":\n      return new MissingPDFException(reason.message);\n    case \"PasswordException\":\n      return new PasswordException(reason.message, reason.code);\n    case \"UnexpectedResponseException\":\n      return new UnexpectedResponseException(reason.message, reason.status);\n    case \"UnknownErrorException\":\n      return new UnknownErrorException(reason.message, reason.details);\n    default:\n      return new UnknownErrorException(reason.message, reason.toString());\n  }\n}\nclass MessageHandler {\n  constructor(sourceName, targetName, comObj) {\n    this.sourceName = sourceName;\n    this.targetName = targetName;\n    this.comObj = comObj;\n    this.callbackId = 1;\n    this.streamId = 1;\n    this.streamSinks = Object.create(null);\n    this.streamControllers = Object.create(null);\n    this.callbackCapabilities = Object.create(null);\n    this.actionHandler = Object.create(null);\n    this._onComObjOnMessage = event => {\n      const data = event.data;\n      if (data.targetName !== this.sourceName) {\n        return;\n      }\n      if (data.stream) {\n        this.#processStreamMessage(data);\n        return;\n      }\n      if (data.callback) {\n        const callbackId = data.callbackId;\n        const capability = this.callbackCapabilities[callbackId];\n        if (!capability) {\n          throw new Error(`Cannot resolve callback ${callbackId}`);\n        }\n        delete this.callbackCapabilities[callbackId];\n        if (data.callback === CallbackKind.DATA) {\n          capability.resolve(data.data);\n        } else if (data.callback === CallbackKind.ERROR) {\n          capability.reject(wrapReason(data.reason));\n        } else {\n          throw new Error(\"Unexpected callback case\");\n        }\n        return;\n      }\n      const action = this.actionHandler[data.action];\n      if (!action) {\n        throw new Error(`Unknown action from worker: ${data.action}`);\n      }\n      if (data.callbackId) {\n        const cbSourceName = this.sourceName;\n        const cbTargetName = data.sourceName;\n        new Promise(function (resolve) {\n          resolve(action(data.data));\n        }).then(function (result) {\n          comObj.postMessage({\n            sourceName: cbSourceName,\n            targetName: cbTargetName,\n            callback: CallbackKind.DATA,\n            callbackId: data.callbackId,\n            data: result\n          });\n        }, function (reason) {\n          comObj.postMessage({\n            sourceName: cbSourceName,\n            targetName: cbTargetName,\n            callback: CallbackKind.ERROR,\n            callbackId: data.callbackId,\n            reason: wrapReason(reason)\n          });\n        });\n        return;\n      }\n      if (data.streamId) {\n        this.#createStreamSink(data);\n        return;\n      }\n      action(data.data);\n    };\n    comObj.addEventListener(\"message\", this._onComObjOnMessage);\n  }\n  on(actionName, handler) {\n    const ah = this.actionHandler;\n    if (ah[actionName]) {\n      throw new Error(`There is already an actionName called \"${actionName}\"`);\n    }\n    ah[actionName] = handler;\n  }\n  send(actionName, data, transfers) {\n    this.comObj.postMessage({\n      sourceName: this.sourceName,\n      targetName: this.targetName,\n      action: actionName,\n      data\n    }, transfers);\n  }\n  sendWithPromise(actionName, data, transfers) {\n    const callbackId = this.callbackId++;\n    const capability = new PromiseCapability();\n    this.callbackCapabilities[callbackId] = capability;\n    try {\n      this.comObj.postMessage({\n        sourceName: this.sourceName,\n        targetName: this.targetName,\n        action: actionName,\n        callbackId,\n        data\n      }, transfers);\n    } catch (ex) {\n      capability.reject(ex);\n    }\n    return capability.promise;\n  }\n  sendWithStream(actionName, data, queueingStrategy, transfers) {\n    const streamId = this.streamId++,\n      sourceName = this.sourceName,\n      targetName = this.targetName,\n      comObj = this.comObj;\n    return new ReadableStream({\n      start: controller => {\n        const startCapability = new PromiseCapability();\n        this.streamControllers[streamId] = {\n          controller,\n          startCall: startCapability,\n          pullCall: null,\n          cancelCall: null,\n          isClosed: false\n        };\n        comObj.postMessage({\n          sourceName,\n          targetName,\n          action: actionName,\n          streamId,\n          data,\n          desiredSize: controller.desiredSize\n        }, transfers);\n        return startCapability.promise;\n      },\n      pull: controller => {\n        const pullCapability = new PromiseCapability();\n        this.streamControllers[streamId].pullCall = pullCapability;\n        comObj.postMessage({\n          sourceName,\n          targetName,\n          stream: StreamKind.PULL,\n          streamId,\n          desiredSize: controller.desiredSize\n        });\n        return pullCapability.promise;\n      },\n      cancel: reason => {\n        assert(reason instanceof Error, \"cancel must have a valid reason\");\n        const cancelCapability = new PromiseCapability();\n        this.streamControllers[streamId].cancelCall = cancelCapability;\n        this.streamControllers[streamId].isClosed = true;\n        comObj.postMessage({\n          sourceName,\n          targetName,\n          stream: StreamKind.CANCEL,\n          streamId,\n          reason: wrapReason(reason)\n        });\n        return cancelCapability.promise;\n      }\n    }, queueingStrategy);\n  }\n  #createStreamSink(data) {\n    const streamId = data.streamId,\n      sourceName = this.sourceName,\n      targetName = data.sourceName,\n      comObj = this.comObj;\n    const self = this,\n      action = this.actionHandler[data.action];\n    const streamSink = {\n      enqueue(chunk, size = 1, transfers) {\n        if (this.isCancelled) {\n          return;\n        }\n        const lastDesiredSize = this.desiredSize;\n        this.desiredSize -= size;\n        if (lastDesiredSize > 0 && this.desiredSize <= 0) {\n          this.sinkCapability = new PromiseCapability();\n          this.ready = this.sinkCapability.promise;\n        }\n        comObj.postMessage({\n          sourceName,\n          targetName,\n          stream: StreamKind.ENQUEUE,\n          streamId,\n          chunk\n        }, transfers);\n      },\n      close() {\n        if (this.isCancelled) {\n          return;\n        }\n        this.isCancelled = true;\n        comObj.postMessage({\n          sourceName,\n          targetName,\n          stream: StreamKind.CLOSE,\n          streamId\n        });\n        delete self.streamSinks[streamId];\n      },\n      error(reason) {\n        assert(reason instanceof Error, \"error must have a valid reason\");\n        if (this.isCancelled) {\n          return;\n        }\n        this.isCancelled = true;\n        comObj.postMessage({\n          sourceName,\n          targetName,\n          stream: StreamKind.ERROR,\n          streamId,\n          reason: wrapReason(reason)\n        });\n      },\n      sinkCapability: new PromiseCapability(),\n      onPull: null,\n      onCancel: null,\n      isCancelled: false,\n      desiredSize: data.desiredSize,\n      ready: null\n    };\n    streamSink.sinkCapability.resolve();\n    streamSink.ready = streamSink.sinkCapability.promise;\n    this.streamSinks[streamId] = streamSink;\n    new Promise(function (resolve) {\n      resolve(action(data.data, streamSink));\n    }).then(function () {\n      comObj.postMessage({\n        sourceName,\n        targetName,\n        stream: StreamKind.START_COMPLETE,\n        streamId,\n        success: true\n      });\n    }, function (reason) {\n      comObj.postMessage({\n        sourceName,\n        targetName,\n        stream: StreamKind.START_COMPLETE,\n        streamId,\n        reason: wrapReason(reason)\n      });\n    });\n  }\n  #processStreamMessage(data) {\n    const streamId = data.streamId,\n      sourceName = this.sourceName,\n      targetName = data.sourceName,\n      comObj = this.comObj;\n    const streamController = this.streamControllers[streamId],\n      streamSink = this.streamSinks[streamId];\n    switch (data.stream) {\n      case StreamKind.START_COMPLETE:\n        if (data.success) {\n          streamController.startCall.resolve();\n        } else {\n          streamController.startCall.reject(wrapReason(data.reason));\n        }\n        break;\n      case StreamKind.PULL_COMPLETE:\n        if (data.success) {\n          streamController.pullCall.resolve();\n        } else {\n          streamController.pullCall.reject(wrapReason(data.reason));\n        }\n        break;\n      case StreamKind.PULL:\n        if (!streamSink) {\n          comObj.postMessage({\n            sourceName,\n            targetName,\n            stream: StreamKind.PULL_COMPLETE,\n            streamId,\n            success: true\n          });\n          break;\n        }\n        if (streamSink.desiredSize <= 0 && data.desiredSize > 0) {\n          streamSink.sinkCapability.resolve();\n        }\n        streamSink.desiredSize = data.desiredSize;\n        new Promise(function (resolve) {\n          resolve(streamSink.onPull?.());\n        }).then(function () {\n          comObj.postMessage({\n            sourceName,\n            targetName,\n            stream: StreamKind.PULL_COMPLETE,\n            streamId,\n            success: true\n          });\n        }, function (reason) {\n          comObj.postMessage({\n            sourceName,\n            targetName,\n            stream: StreamKind.PULL_COMPLETE,\n            streamId,\n            reason: wrapReason(reason)\n          });\n        });\n        break;\n      case StreamKind.ENQUEUE:\n        assert(streamController, \"enqueue should have stream controller\");\n        if (streamController.isClosed) {\n          break;\n        }\n        streamController.controller.enqueue(data.chunk);\n        break;\n      case StreamKind.CLOSE:\n        assert(streamController, \"close should have stream controller\");\n        if (streamController.isClosed) {\n          break;\n        }\n        streamController.isClosed = true;\n        streamController.controller.close();\n        this.#deleteStreamController(streamController, streamId);\n        break;\n      case StreamKind.ERROR:\n        assert(streamController, \"error should have stream controller\");\n        streamController.controller.error(wrapReason(data.reason));\n        this.#deleteStreamController(streamController, streamId);\n        break;\n      case StreamKind.CANCEL_COMPLETE:\n        if (data.success) {\n          streamController.cancelCall.resolve();\n        } else {\n          streamController.cancelCall.reject(wrapReason(data.reason));\n        }\n        this.#deleteStreamController(streamController, streamId);\n        break;\n      case StreamKind.CANCEL:\n        if (!streamSink) {\n          break;\n        }\n        new Promise(function (resolve) {\n          resolve(streamSink.onCancel?.(wrapReason(data.reason)));\n        }).then(function () {\n          comObj.postMessage({\n            sourceName,\n            targetName,\n            stream: StreamKind.CANCEL_COMPLETE,\n            streamId,\n            success: true\n          });\n        }, function (reason) {\n          comObj.postMessage({\n            sourceName,\n            targetName,\n            stream: StreamKind.CANCEL_COMPLETE,\n            streamId,\n            reason: wrapReason(reason)\n          });\n        });\n        streamSink.sinkCapability.reject(wrapReason(data.reason));\n        streamSink.isCancelled = true;\n        delete this.streamSinks[streamId];\n        break;\n      default:\n        throw new Error(\"Unexpected stream case\");\n    }\n  }\n  async #deleteStreamController(streamController, streamId) {\n    await Promise.allSettled([streamController.startCall?.promise, streamController.pullCall?.promise, streamController.cancelCall?.promise]);\n    delete this.streamControllers[streamId];\n  }\n  destroy() {\n    this.comObj.removeEventListener(\"message\", this._onComObjOnMessage);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/worker_stream.js\n\nclass PDFWorkerStream {\n  constructor(msgHandler) {\n    this._msgHandler = msgHandler;\n    this._contentLength = null;\n    this._fullRequestReader = null;\n    this._rangeRequestReaders = [];\n  }\n  getFullReader() {\n    assert(!this._fullRequestReader, \"PDFWorkerStream.getFullReader can only be called once.\");\n    this._fullRequestReader = new PDFWorkerStreamReader(this._msgHandler);\n    return this._fullRequestReader;\n  }\n  getRangeReader(begin, end) {\n    const reader = new PDFWorkerStreamRangeReader(begin, end, this._msgHandler);\n    this._rangeRequestReaders.push(reader);\n    return reader;\n  }\n  cancelAllRequests(reason) {\n    this._fullRequestReader?.cancel(reason);\n    for (const reader of this._rangeRequestReaders.slice(0)) {\n      reader.cancel(reason);\n    }\n  }\n}\nclass PDFWorkerStreamReader {\n  constructor(msgHandler) {\n    this._msgHandler = msgHandler;\n    this.onProgress = null;\n    this._contentLength = null;\n    this._isRangeSupported = false;\n    this._isStreamingSupported = false;\n    const readableStream = this._msgHandler.sendWithStream(\"GetReader\");\n    this._reader = readableStream.getReader();\n    this._headersReady = this._msgHandler.sendWithPromise(\"ReaderHeadersReady\").then(data => {\n      this._isStreamingSupported = data.isStreamingSupported;\n      this._isRangeSupported = data.isRangeSupported;\n      this._contentLength = data.contentLength;\n    });\n  }\n  get headersReady() {\n    return this._headersReady;\n  }\n  get contentLength() {\n    return this._contentLength;\n  }\n  get isStreamingSupported() {\n    return this._isStreamingSupported;\n  }\n  get isRangeSupported() {\n    return this._isRangeSupported;\n  }\n  async read() {\n    const {\n      value,\n      done\n    } = await this._reader.read();\n    if (done) {\n      return {\n        value: undefined,\n        done: true\n      };\n    }\n    return {\n      value: value.buffer,\n      done: false\n    };\n  }\n  cancel(reason) {\n    this._reader.cancel(reason);\n  }\n}\nclass PDFWorkerStreamRangeReader {\n  constructor(begin, end, msgHandler) {\n    this._msgHandler = msgHandler;\n    this.onProgress = null;\n    const readableStream = this._msgHandler.sendWithStream(\"GetRangeReader\", {\n      begin,\n      end\n    });\n    this._reader = readableStream.getReader();\n  }\n  get isStreamingSupported() {\n    return false;\n  }\n  async read() {\n    const {\n      value,\n      done\n    } = await this._reader.read();\n    if (done) {\n      return {\n        value: undefined,\n        done: true\n      };\n    }\n    return {\n      value: value.buffer,\n      done: false\n    };\n  }\n  cancel(reason) {\n    this._reader.cancel(reason);\n  }\n}\n\n;// CONCATENATED MODULE: ./src/core/worker.js\n\n\n\n\n\n\n\n\n\n\nclass WorkerTask {\n  constructor(name) {\n    this.name = name;\n    this.terminated = false;\n    this._capability = new PromiseCapability();\n  }\n  get finished() {\n    return this._capability.promise;\n  }\n  finish() {\n    this._capability.resolve();\n  }\n  terminate() {\n    this.terminated = true;\n  }\n  ensureNotTerminated() {\n    if (this.terminated) {\n      throw new Error(\"Worker task was terminated\");\n    }\n  }\n}\nclass WorkerMessageHandler {\n  static setup(handler, port) {\n    let testMessageProcessed = false;\n    handler.on(\"test\", function (data) {\n      if (testMessageProcessed) {\n        return;\n      }\n      testMessageProcessed = true;\n      handler.send(\"test\", data instanceof Uint8Array);\n    });\n    handler.on(\"configure\", function (data) {\n      setVerbosityLevel(data.verbosity);\n    });\n    handler.on(\"GetDocRequest\", function (data) {\n      return WorkerMessageHandler.createDocumentHandler(data, port);\n    });\n  }\n  static createDocumentHandler(docParams, port) {\n    let pdfManager;\n    let terminated = false;\n    let cancelXHRs = null;\n    const WorkerTasks = new Set();\n    const verbosity = getVerbosityLevel();\n    const {\n      docId,\n      apiVersion\n    } = docParams;\n    const workerVersion = '4.0.269';\n    if (apiVersion !== workerVersion) {\n      throw new Error(`The API version \"${apiVersion}\" does not match ` + `the Worker version \"${workerVersion}\".`);\n    }\n    const enumerableProperties = [];\n    for (const property in []) {\n      enumerableProperties.push(property);\n    }\n    if (enumerableProperties.length) {\n      throw new Error(\"The `Array.prototype` contains unexpected enumerable properties: \" + enumerableProperties.join(\", \") + \"; thus breaking e.g. `for...in` iteration of `Array`s.\");\n    }\n    const workerHandlerName = docId + \"_worker\";\n    let handler = new MessageHandler(workerHandlerName, docId, port);\n    function ensureNotTerminated() {\n      if (terminated) {\n        throw new Error(\"Worker was terminated\");\n      }\n    }\n    function startWorkerTask(task) {\n      WorkerTasks.add(task);\n    }\n    function finishWorkerTask(task) {\n      task.finish();\n      WorkerTasks.delete(task);\n    }\n    async function loadDocument(recoveryMode) {\n      await pdfManager.ensureDoc(\"checkHeader\");\n      await pdfManager.ensureDoc(\"parseStartXRef\");\n      await pdfManager.ensureDoc(\"parse\", [recoveryMode]);\n      await pdfManager.ensureDoc(\"checkFirstPage\", [recoveryMode]);\n      await pdfManager.ensureDoc(\"checkLastPage\", [recoveryMode]);\n      const isPureXfa = await pdfManager.ensureDoc(\"isPureXfa\");\n      if (isPureXfa) {\n        const task = new WorkerTask(\"loadXfaFonts\");\n        startWorkerTask(task);\n        await Promise.all([pdfManager.loadXfaFonts(handler, task).catch(reason => {}).then(() => finishWorkerTask(task)), pdfManager.loadXfaImages()]);\n      }\n      const [numPages, fingerprints] = await Promise.all([pdfManager.ensureDoc(\"numPages\"), pdfManager.ensureDoc(\"fingerprints\")]);\n      const htmlForXfa = isPureXfa ? await pdfManager.ensureDoc(\"htmlForXfa\") : null;\n      return {\n        numPages,\n        fingerprints,\n        htmlForXfa\n      };\n    }\n    function getPdfManager({\n      data,\n      password,\n      disableAutoFetch,\n      rangeChunkSize,\n      length,\n      docBaseUrl,\n      enableXfa,\n      evaluatorOptions\n    }) {\n      const pdfManagerArgs = {\n        source: null,\n        disableAutoFetch,\n        docBaseUrl,\n        docId,\n        enableXfa,\n        evaluatorOptions,\n        handler,\n        length,\n        password,\n        rangeChunkSize\n      };\n      const pdfManagerCapability = new PromiseCapability();\n      let newPdfManager;\n      if (data) {\n        try {\n          pdfManagerArgs.source = data;\n          newPdfManager = new LocalPdfManager(pdfManagerArgs);\n          pdfManagerCapability.resolve(newPdfManager);\n        } catch (ex) {\n          pdfManagerCapability.reject(ex);\n        }\n        return pdfManagerCapability.promise;\n      }\n      let pdfStream,\n        cachedChunks = [];\n      try {\n        pdfStream = new PDFWorkerStream(handler);\n      } catch (ex) {\n        pdfManagerCapability.reject(ex);\n        return pdfManagerCapability.promise;\n      }\n      const fullRequest = pdfStream.getFullReader();\n      fullRequest.headersReady.then(function () {\n        if (!fullRequest.isRangeSupported) {\n          return;\n        }\n        pdfManagerArgs.source = pdfStream;\n        pdfManagerArgs.length = fullRequest.contentLength;\n        pdfManagerArgs.disableAutoFetch ||= fullRequest.isStreamingSupported;\n        newPdfManager = new NetworkPdfManager(pdfManagerArgs);\n        for (const chunk of cachedChunks) {\n          newPdfManager.sendProgressiveData(chunk);\n        }\n        cachedChunks = [];\n        pdfManagerCapability.resolve(newPdfManager);\n        cancelXHRs = null;\n      }).catch(function (reason) {\n        pdfManagerCapability.reject(reason);\n        cancelXHRs = null;\n      });\n      let loaded = 0;\n      const flushChunks = function () {\n        const pdfFile = arrayBuffersToBytes(cachedChunks);\n        if (length && pdfFile.length !== length) {\n          warn(\"reported HTTP length is different from actual\");\n        }\n        try {\n          pdfManagerArgs.source = pdfFile;\n          newPdfManager = new LocalPdfManager(pdfManagerArgs);\n          pdfManagerCapability.resolve(newPdfManager);\n        } catch (ex) {\n          pdfManagerCapability.reject(ex);\n        }\n        cachedChunks = [];\n      };\n      new Promise(function (resolve, reject) {\n        const readChunk = function ({\n          value,\n          done\n        }) {\n          try {\n            ensureNotTerminated();\n            if (done) {\n              if (!newPdfManager) {\n                flushChunks();\n              }\n              cancelXHRs = null;\n              return;\n            }\n            loaded += value.byteLength;\n            if (!fullRequest.isStreamingSupported) {\n              handler.send(\"DocProgress\", {\n                loaded,\n                total: Math.max(loaded, fullRequest.contentLength || 0)\n              });\n            }\n            if (newPdfManager) {\n              newPdfManager.sendProgressiveData(value);\n            } else {\n              cachedChunks.push(value);\n            }\n            fullRequest.read().then(readChunk, reject);\n          } catch (e) {\n            reject(e);\n          }\n        };\n        fullRequest.read().then(readChunk, reject);\n      }).catch(function (e) {\n        pdfManagerCapability.reject(e);\n        cancelXHRs = null;\n      });\n      cancelXHRs = function (reason) {\n        pdfStream.cancelAllRequests(reason);\n      };\n      return pdfManagerCapability.promise;\n    }\n    function setupDoc(data) {\n      function onSuccess(doc) {\n        ensureNotTerminated();\n        handler.send(\"GetDoc\", {\n          pdfInfo: doc\n        });\n      }\n      function onFailure(ex) {\n        ensureNotTerminated();\n        if (ex instanceof PasswordException) {\n          const task = new WorkerTask(`PasswordException: response ${ex.code}`);\n          startWorkerTask(task);\n          handler.sendWithPromise(\"PasswordRequest\", ex).then(function ({\n            password\n          }) {\n            finishWorkerTask(task);\n            pdfManager.updatePassword(password);\n            pdfManagerReady();\n          }).catch(function () {\n            finishWorkerTask(task);\n            handler.send(\"DocException\", ex);\n          });\n        } else if (ex instanceof InvalidPDFException || ex instanceof MissingPDFException || ex instanceof UnexpectedResponseException || ex instanceof UnknownErrorException) {\n          handler.send(\"DocException\", ex);\n        } else {\n          handler.send(\"DocException\", new UnknownErrorException(ex.message, ex.toString()));\n        }\n      }\n      function pdfManagerReady() {\n        ensureNotTerminated();\n        loadDocument(false).then(onSuccess, function (reason) {\n          ensureNotTerminated();\n          if (!(reason instanceof XRefParseException)) {\n            onFailure(reason);\n            return;\n          }\n          pdfManager.requestLoadedStream().then(function () {\n            ensureNotTerminated();\n            loadDocument(true).then(onSuccess, onFailure);\n          });\n        });\n      }\n      ensureNotTerminated();\n      getPdfManager(data).then(function (newPdfManager) {\n        if (terminated) {\n          newPdfManager.terminate(new AbortException(\"Worker was terminated.\"));\n          throw new Error(\"Worker was terminated\");\n        }\n        pdfManager = newPdfManager;\n        pdfManager.requestLoadedStream(true).then(stream => {\n          handler.send(\"DataLoaded\", {\n            length: stream.bytes.byteLength\n          });\n        });\n      }).then(pdfManagerReady, onFailure);\n    }\n    handler.on(\"GetPage\", function (data) {\n      return pdfManager.getPage(data.pageIndex).then(function (page) {\n        return Promise.all([pdfManager.ensure(page, \"rotate\"), pdfManager.ensure(page, \"ref\"), pdfManager.ensure(page, \"userUnit\"), pdfManager.ensure(page, \"view\")]).then(function ([rotate, ref, userUnit, view]) {\n          return {\n            rotate,\n            ref,\n            userUnit,\n            view\n          };\n        });\n      });\n    });\n    handler.on(\"GetPageIndex\", function (data) {\n      const pageRef = Ref.get(data.num, data.gen);\n      return pdfManager.ensureCatalog(\"getPageIndex\", [pageRef]);\n    });\n    handler.on(\"GetDestinations\", function (data) {\n      return pdfManager.ensureCatalog(\"destinations\");\n    });\n    handler.on(\"GetDestination\", function (data) {\n      return pdfManager.ensureCatalog(\"getDestination\", [data.id]);\n    });\n    handler.on(\"GetPageLabels\", function (data) {\n      return pdfManager.ensureCatalog(\"pageLabels\");\n    });\n    handler.on(\"GetPageLayout\", function (data) {\n      return pdfManager.ensureCatalog(\"pageLayout\");\n    });\n    handler.on(\"GetPageMode\", function (data) {\n      return pdfManager.ensureCatalog(\"pageMode\");\n    });\n    handler.on(\"GetViewerPreferences\", function (data) {\n      return pdfManager.ensureCatalog(\"viewerPreferences\");\n    });\n    handler.on(\"GetOpenAction\", function (data) {\n      return pdfManager.ensureCatalog(\"openAction\");\n    });\n    handler.on(\"GetAttachments\", function (data) {\n      return pdfManager.ensureCatalog(\"attachments\");\n    });\n    handler.on(\"GetDocJSActions\", function (data) {\n      return pdfManager.ensureCatalog(\"jsActions\");\n    });\n    handler.on(\"GetPageJSActions\", function ({\n      pageIndex\n    }) {\n      return pdfManager.getPage(pageIndex).then(function (page) {\n        return pdfManager.ensure(page, \"jsActions\");\n      });\n    });\n    handler.on(\"GetOutline\", function (data) {\n      return pdfManager.ensureCatalog(\"documentOutline\");\n    });\n    handler.on(\"GetOptionalContentConfig\", function (data) {\n      return pdfManager.ensureCatalog(\"optionalContentConfig\");\n    });\n    handler.on(\"GetPermissions\", function (data) {\n      return pdfManager.ensureCatalog(\"permissions\");\n    });\n    handler.on(\"GetMetadata\", function (data) {\n      return Promise.all([pdfManager.ensureDoc(\"documentInfo\"), pdfManager.ensureCatalog(\"metadata\")]);\n    });\n    handler.on(\"GetMarkInfo\", function (data) {\n      return pdfManager.ensureCatalog(\"markInfo\");\n    });\n    handler.on(\"GetData\", function (data) {\n      return pdfManager.requestLoadedStream().then(function (stream) {\n        return stream.bytes;\n      });\n    });\n    handler.on(\"GetAnnotations\", function ({\n      pageIndex,\n      intent\n    }) {\n      return pdfManager.getPage(pageIndex).then(function (page) {\n        const task = new WorkerTask(`GetAnnotations: page ${pageIndex}`);\n        startWorkerTask(task);\n        return page.getAnnotationsData(handler, task, intent).then(data => {\n          finishWorkerTask(task);\n          return data;\n        }, reason => {\n          finishWorkerTask(task);\n          throw reason;\n        });\n      });\n    });\n    handler.on(\"GetFieldObjects\", function (data) {\n      return pdfManager.ensureDoc(\"fieldObjects\");\n    });\n    handler.on(\"HasJSActions\", function (data) {\n      return pdfManager.ensureDoc(\"hasJSActions\");\n    });\n    handler.on(\"GetCalculationOrderIds\", function (data) {\n      return pdfManager.ensureDoc(\"calculationOrderIds\");\n    });\n    handler.on(\"SaveDocument\", async function ({\n      isPureXfa,\n      numPages,\n      annotationStorage,\n      filename\n    }) {\n      const globalPromises = [pdfManager.requestLoadedStream(), pdfManager.ensureCatalog(\"acroForm\"), pdfManager.ensureCatalog(\"acroFormRef\"), pdfManager.ensureDoc(\"startXRef\"), pdfManager.ensureDoc(\"xref\"), pdfManager.ensureDoc(\"linearization\"), pdfManager.ensureCatalog(\"structTreeRoot\")];\n      const promises = [];\n      const newAnnotationsByPage = !isPureXfa ? getNewAnnotationsMap(annotationStorage) : null;\n      const [stream, acroForm, acroFormRef, startXRef, xref, linearization, _structTreeRoot] = await Promise.all(globalPromises);\n      const catalogRef = xref.trailer.getRaw(\"Root\") || null;\n      let structTreeRoot;\n      if (newAnnotationsByPage) {\n        if (!_structTreeRoot) {\n          if (await StructTreeRoot.canCreateStructureTree({\n            catalogRef,\n            pdfManager,\n            newAnnotationsByPage\n          })) {\n            structTreeRoot = null;\n          }\n        } else if (await _structTreeRoot.canUpdateStructTree({\n          pdfManager,\n          xref,\n          newAnnotationsByPage\n        })) {\n          structTreeRoot = _structTreeRoot;\n        }\n        const imagePromises = AnnotationFactory.generateImages(annotationStorage.values(), xref, pdfManager.evaluatorOptions.isOffscreenCanvasSupported);\n        const newAnnotationPromises = structTreeRoot === undefined ? promises : [];\n        for (const [pageIndex, annotations] of newAnnotationsByPage) {\n          newAnnotationPromises.push(pdfManager.getPage(pageIndex).then(page => {\n            const task = new WorkerTask(`Save (editor): page ${pageIndex}`);\n            return page.saveNewAnnotations(handler, task, annotations, imagePromises).finally(function () {\n              finishWorkerTask(task);\n            });\n          }));\n        }\n        if (structTreeRoot === null) {\n          promises.push(Promise.all(newAnnotationPromises).then(async newRefs => {\n            await StructTreeRoot.createStructureTree({\n              newAnnotationsByPage,\n              xref,\n              catalogRef,\n              pdfManager,\n              newRefs\n            });\n            return newRefs;\n          }));\n        } else if (structTreeRoot) {\n          promises.push(Promise.all(newAnnotationPromises).then(async newRefs => {\n            await structTreeRoot.updateStructureTree({\n              newAnnotationsByPage,\n              pdfManager,\n              newRefs\n            });\n            return newRefs;\n          }));\n        }\n      }\n      if (isPureXfa) {\n        promises.push(pdfManager.serializeXfaData(annotationStorage));\n      } else {\n        for (let pageIndex = 0; pageIndex < numPages; pageIndex++) {\n          promises.push(pdfManager.getPage(pageIndex).then(function (page) {\n            const task = new WorkerTask(`Save: page ${pageIndex}`);\n            return page.save(handler, task, annotationStorage).finally(function () {\n              finishWorkerTask(task);\n            });\n          }));\n        }\n      }\n      const refs = await Promise.all(promises);\n      let newRefs = [];\n      let xfaData = null;\n      if (isPureXfa) {\n        xfaData = refs[0];\n        if (!xfaData) {\n          return stream.bytes;\n        }\n      } else {\n        newRefs = refs.flat(2);\n        if (newRefs.length === 0) {\n          return stream.bytes;\n        }\n      }\n      const needAppearances = acroFormRef && acroForm instanceof Dict && newRefs.some(ref => ref.needAppearances);\n      const xfa = acroForm instanceof Dict && acroForm.get(\"XFA\") || null;\n      let xfaDatasetsRef = null;\n      let hasXfaDatasetsEntry = false;\n      if (Array.isArray(xfa)) {\n        for (let i = 0, ii = xfa.length; i < ii; i += 2) {\n          if (xfa[i] === \"datasets\") {\n            xfaDatasetsRef = xfa[i + 1];\n            hasXfaDatasetsEntry = true;\n          }\n        }\n        if (xfaDatasetsRef === null) {\n          xfaDatasetsRef = xref.getNewTemporaryRef();\n        }\n      } else if (xfa) {\n        warn(\"Unsupported XFA type.\");\n      }\n      let newXrefInfo = Object.create(null);\n      if (xref.trailer) {\n        const infoObj = Object.create(null);\n        const xrefInfo = xref.trailer.get(\"Info\") || null;\n        if (xrefInfo instanceof Dict) {\n          xrefInfo.forEach((key, value) => {\n            if (typeof value === \"string\") {\n              infoObj[key] = stringToPDFString(value);\n            }\n          });\n        }\n        newXrefInfo = {\n          rootRef: catalogRef,\n          encryptRef: xref.trailer.getRaw(\"Encrypt\") || null,\n          newRef: xref.getNewTemporaryRef(),\n          infoRef: xref.trailer.getRaw(\"Info\") || null,\n          info: infoObj,\n          fileIds: xref.trailer.get(\"ID\") || null,\n          startXRef: linearization ? startXRef : xref.lastXRefStreamPos ?? startXRef,\n          filename\n        };\n      }\n      return incrementalUpdate({\n        originalData: stream.bytes,\n        xrefInfo: newXrefInfo,\n        newRefs,\n        xref,\n        hasXfa: !!xfa,\n        xfaDatasetsRef,\n        hasXfaDatasetsEntry,\n        needAppearances,\n        acroFormRef,\n        acroForm,\n        xfaData\n      }).finally(() => {\n        xref.resetNewTemporaryRef();\n      });\n    });\n    handler.on(\"GetOperatorList\", function (data, sink) {\n      const pageIndex = data.pageIndex;\n      pdfManager.getPage(pageIndex).then(function (page) {\n        const task = new WorkerTask(`GetOperatorList: page ${pageIndex}`);\n        startWorkerTask(task);\n        const start = verbosity >= VerbosityLevel.INFOS ? Date.now() : 0;\n        page.getOperatorList({\n          handler,\n          sink,\n          task,\n          intent: data.intent,\n          cacheKey: data.cacheKey,\n          annotationStorage: data.annotationStorage\n        }).then(function (operatorListInfo) {\n          finishWorkerTask(task);\n          if (start) {\n            info(`page=${pageIndex + 1} - getOperatorList: time=` + `${Date.now() - start}ms, len=${operatorListInfo.length}`);\n          }\n          sink.close();\n        }, function (reason) {\n          finishWorkerTask(task);\n          if (task.terminated) {\n            return;\n          }\n          sink.error(reason);\n        });\n      });\n    });\n    handler.on(\"GetTextContent\", function (data, sink) {\n      const {\n        pageIndex,\n        includeMarkedContent,\n        disableNormalization\n      } = data;\n      pdfManager.getPage(pageIndex).then(function (page) {\n        const task = new WorkerTask(\"GetTextContent: page \" + pageIndex);\n        startWorkerTask(task);\n        const start = verbosity >= VerbosityLevel.INFOS ? Date.now() : 0;\n        page.extractTextContent({\n          handler,\n          task,\n          sink,\n          includeMarkedContent,\n          disableNormalization\n        }).then(function () {\n          finishWorkerTask(task);\n          if (start) {\n            info(`page=${pageIndex + 1} - getTextContent: time=` + `${Date.now() - start}ms`);\n          }\n          sink.close();\n        }, function (reason) {\n          finishWorkerTask(task);\n          if (task.terminated) {\n            return;\n          }\n          sink.error(reason);\n        });\n      });\n    });\n    handler.on(\"GetStructTree\", function (data) {\n      return pdfManager.getPage(data.pageIndex).then(function (page) {\n        return pdfManager.ensure(page, \"getStructTree\");\n      });\n    });\n    handler.on(\"FontFallback\", function (data) {\n      return pdfManager.fontFallback(data.id, handler);\n    });\n    handler.on(\"Cleanup\", function (data) {\n      return pdfManager.cleanup(true);\n    });\n    handler.on(\"Terminate\", function (data) {\n      terminated = true;\n      const waitOn = [];\n      if (pdfManager) {\n        pdfManager.terminate(new AbortException(\"Worker was terminated.\"));\n        const cleanupPromise = pdfManager.cleanup();\n        waitOn.push(cleanupPromise);\n        pdfManager = null;\n      } else {\n        clearGlobalCaches();\n      }\n      if (cancelXHRs) {\n        cancelXHRs(new AbortException(\"Worker was terminated.\"));\n      }\n      for (const task of WorkerTasks) {\n        waitOn.push(task.finished);\n        task.terminate();\n      }\n      return Promise.all(waitOn).then(function () {\n        handler.destroy();\n        handler = null;\n      });\n    });\n    handler.on(\"Ready\", function (data) {\n      setupDoc(docParams);\n      docParams = null;\n    });\n    return workerHandlerName;\n  }\n  static initializeFromPort(port) {\n    const handler = new MessageHandler(\"worker\", \"main\", port);\n    WorkerMessageHandler.setup(handler, port);\n    handler.send(\"ready\", null);\n  }\n}\nfunction isMessagePort(maybePort) {\n  return typeof maybePort.postMessage === \"function\" && \"onmessage\" in maybePort;\n}\nif (typeof window === \"undefined\" && !isNodeJS && typeof self !== \"undefined\" && isMessagePort(self)) {\n  WorkerMessageHandler.initializeFromPort(self);\n}\n\n;// CONCATENATED MODULE: ./src/pdf.worker.js\n\nconst pdfjsVersion = '4.0.269';\nconst pdfjsBuild = 'f4b396f6c';\n\nvar __webpack_exports__WorkerMessageHandler = __webpack_exports__.WorkerMessageHandler;\nexport { __webpack_exports__WorkerMessageHandler as WorkerMessageHandler };\n\n//# sourceMappingURL=pdf.worker.mjs.map"
  },
  {
    "path": "public/assets/lib/vendor/three/FontLoader.js",
    "content": "// @ts-nocheck\nimport {\n\tFileLoader,\n\tLoader,\n\tShapePath\n} from './three.module.js';\n\nclass FontLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\n\t}\n\n\tload( url, onLoad, onProgress, onError ) {\n\n\t\tconst scope = this;\n\n\t\tconst loader = new FileLoader( this.manager );\n\t\tloader.setPath( this.path );\n\t\tloader.setRequestHeader( this.requestHeader );\n\t\tloader.setWithCredentials( this.withCredentials );\n\t\tloader.load( url, function ( text ) {\n\n\t\t\tconst font = scope.parse( JSON.parse( text ) );\n\n\t\t\tif ( onLoad ) onLoad( font );\n\n\t\t}, onProgress, onError );\n\n\t}\n\n\tparse( json ) {\n\n\t\treturn new Font( json );\n\n\t}\n\n}\n\n//\n\nclass Font {\n\n\tconstructor( data ) {\n\n\t\tthis.isFont = true;\n\n\t\tthis.type = 'Font';\n\n\t\tthis.data = data;\n\n\t}\n\n\tgenerateShapes( text, size = 100 ) {\n\n\t\tconst shapes = [];\n\t\tconst paths = createPaths( text, size, this.data );\n\n\t\tfor ( let p = 0, pl = paths.length; p < pl; p ++ ) {\n\n\t\t\tshapes.push( ...paths[ p ].toShapes() );\n\n\t\t}\n\n\t\treturn shapes;\n\n\t}\n\n}\n\nfunction createPaths( text, size, data ) {\n\n\tconst chars = Array.from( text );\n\tconst scale = size / data.resolution;\n\tconst line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;\n\n\tconst paths = [];\n\n\tlet offsetX = 0, offsetY = 0;\n\n\tfor ( let i = 0; i < chars.length; i ++ ) {\n\n\t\tconst char = chars[ i ];\n\n\t\tif ( char === '\\n' ) {\n\n\t\t\toffsetX = 0;\n\t\t\toffsetY -= line_height;\n\n\t\t} else {\n\n\t\t\tconst ret = createPath( char, scale, offsetX, offsetY, data );\n\t\t\toffsetX += ret.offsetX;\n\t\t\tpaths.push( ret.path );\n\n\t\t}\n\n\t}\n\n\treturn paths;\n\n}\n\nfunction createPath( char, scale, offsetX, offsetY, data ) {\n\n\tconst glyph = data.glyphs[ char ] || data.glyphs[ '?' ];\n\n\tif ( ! glyph ) {\n\n\t\tconsole.error( 'THREE.Font: character \"' + char + '\" does not exists in font family ' + data.familyName + '.' );\n\n\t\treturn;\n\n\t}\n\n\tconst path = new ShapePath();\n\n\tlet x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2;\n\n\tif ( glyph.o ) {\n\n\t\tconst outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );\n\n\t\tfor ( let i = 0, l = outline.length; i < l; ) {\n\n\t\t\tconst action = outline[ i ++ ];\n\n\t\t\tswitch ( action ) {\n\n\t\t\t\tcase 'm': // moveTo\n\n\t\t\t\t\tx = outline[ i ++ ] * scale + offsetX;\n\t\t\t\t\ty = outline[ i ++ ] * scale + offsetY;\n\n\t\t\t\t\tpath.moveTo( x, y );\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'l': // lineTo\n\n\t\t\t\t\tx = outline[ i ++ ] * scale + offsetX;\n\t\t\t\t\ty = outline[ i ++ ] * scale + offsetY;\n\n\t\t\t\t\tpath.lineTo( x, y );\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'q': // quadraticCurveTo\n\n\t\t\t\t\tcpx = outline[ i ++ ] * scale + offsetX;\n\t\t\t\t\tcpy = outline[ i ++ ] * scale + offsetY;\n\t\t\t\t\tcpx1 = outline[ i ++ ] * scale + offsetX;\n\t\t\t\t\tcpy1 = outline[ i ++ ] * scale + offsetY;\n\n\t\t\t\t\tpath.quadraticCurveTo( cpx1, cpy1, cpx, cpy );\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'b': // bezierCurveTo\n\n\t\t\t\t\tcpx = outline[ i ++ ] * scale + offsetX;\n\t\t\t\t\tcpy = outline[ i ++ ] * scale + offsetY;\n\t\t\t\t\tcpx1 = outline[ i ++ ] * scale + offsetX;\n\t\t\t\t\tcpy1 = outline[ i ++ ] * scale + offsetY;\n\t\t\t\t\tcpx2 = outline[ i ++ ] * scale + offsetX;\n\t\t\t\t\tcpy2 = outline[ i ++ ] * scale + offsetY;\n\n\t\t\t\t\tpath.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );\n\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\treturn { offsetX: glyph.ha * scale, path: path };\n\n}\n\nexport { FontLoader, Font };\n"
  },
  {
    "path": "public/assets/lib/vendor/three/OrbitControls.js",
    "content": "// @ts-nocheck\nimport {\n\tEventDispatcher,\n\tMOUSE,\n\tQuaternion,\n\tSpherical,\n\tTOUCH,\n\tVector2,\n\tVector3,\n\tPlane,\n\tRay,\n\tMathUtils\n} from './three.module.js';\n\n// OrbitControls performs orbiting, dollying (zooming), and panning.\n// Unlike TrackballControls, it maintains the \"up\" direction object.up (+Y by default).\n//\n//    Orbit - left mouse / touch: one-finger move\n//    Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish\n//    Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move\n\nconst _changeEvent = { type: 'change' };\nconst _startEvent = { type: 'start' };\nconst _endEvent = { type: 'end' };\nconst _ray = new Ray();\nconst _plane = new Plane();\nconst TILT_LIMIT = Math.cos( 70 * MathUtils.DEG2RAD );\n\nclass OrbitControls extends EventDispatcher {\n\n\tconstructor( object, domElement ) {\n\n\t\tsuper();\n\n\t\tthis.object = object;\n\t\tthis.domElement = domElement;\n\t\tthis.domElement.style.touchAction = 'none'; // disable touch scroll\n\n\t\t// Set to false to disable this control\n\t\tthis.enabled = true;\n\n\t\t// \"target\" sets the location of focus, where the object orbits around\n\t\tthis.target = new Vector3();\n\n\t\t// Sets the 3D cursor (similar to Blender), from which the maxTargetRadius takes effect\n\t\tthis.cursor = new Vector3();\n\n\t\t// How far you can dolly in and out ( PerspectiveCamera only )\n\t\tthis.minDistance = 0;\n\t\tthis.maxDistance = Infinity;\n\n\t\t// How far you can zoom in and out ( OrthographicCamera only )\n\t\tthis.minZoom = 0;\n\t\tthis.maxZoom = Infinity;\n\n\t\t// Limit camera target within a spherical area around the cursor\n\t\tthis.minTargetRadius = 0;\n\t\tthis.maxTargetRadius = Infinity;\n\n\t\t// How far you can orbit vertically, upper and lower limits.\n\t\t// Range is 0 to Math.PI radians.\n\t\tthis.minPolarAngle = 0; // radians\n\t\tthis.maxPolarAngle = Math.PI; // radians\n\n\t\t// How far you can orbit horizontally, upper and lower limits.\n\t\t// If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI )\n\t\tthis.minAzimuthAngle = - Infinity; // radians\n\t\tthis.maxAzimuthAngle = Infinity; // radians\n\n\t\t// Set to true to enable damping (inertia)\n\t\t// If damping is enabled, you must call controls.update() in your animation loop\n\t\tthis.enableDamping = false;\n\t\tthis.dampingFactor = 0.05;\n\n\t\t// This option actually enables dollying in and out; left as \"zoom\" for backwards compatibility.\n\t\t// Set to false to disable zooming\n\t\tthis.enableZoom = true;\n\t\tthis.zoomSpeed = 1.0;\n\n\t\t// Set to false to disable rotating\n\t\tthis.enableRotate = true;\n\t\tthis.rotateSpeed = 1.0;\n\n\t\t// Set to false to disable panning\n\t\tthis.enablePan = true;\n\t\tthis.panSpeed = 1.0;\n\t\tthis.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up\n\t\tthis.keyPanSpeed = 7.0;\t// pixels moved per arrow key push\n\t\tthis.zoomToCursor = false;\n\n\t\t// Set to true to automatically rotate around the target\n\t\t// If auto-rotate is enabled, you must call controls.update() in your animation loop\n\t\tthis.autoRotate = false;\n\t\tthis.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60\n\n\t\t// The four arrow keys\n\t\tthis.keys = { LEFT: 'ArrowLeft', UP: 'ArrowUp', RIGHT: 'ArrowRight', BOTTOM: 'ArrowDown' };\n\n\t\t// Mouse buttons\n\t\tthis.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };\n\n\t\t// Touch fingers\n\t\tthis.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN };\n\n\t\t// for reset\n\t\tthis.target0 = this.target.clone();\n\t\tthis.position0 = this.object.position.clone();\n\t\tthis.zoom0 = this.object.zoom;\n\n\t\t// the target DOM element for key events\n\t\tthis._domElementKeyEvents = null;\n\n\t\t//\n\t\t// public methods\n\t\t//\n\n\t\tthis.getPolarAngle = function () {\n\n\t\t\treturn spherical.phi;\n\n\t\t};\n\n\t\tthis.getAzimuthalAngle = function () {\n\n\t\t\treturn spherical.theta;\n\n\t\t};\n\n\t\tthis.getDistance = function () {\n\n\t\t\treturn this.object.position.distanceTo( this.target );\n\n\t\t};\n\n\t\tthis.listenToKeyEvents = function ( domElement ) {\n\n\t\t\tdomElement.addEventListener( 'keydown', onKeyDown );\n\t\t\tthis._domElementKeyEvents = domElement;\n\n\t\t};\n\n\t\tthis.stopListenToKeyEvents = function () {\n\n\t\t\tthis._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown );\n\t\t\tthis._domElementKeyEvents = null;\n\n\t\t};\n\n\t\tthis.saveState = function () {\n\n\t\t\tscope.target0.copy( scope.target );\n\t\t\tscope.position0.copy( scope.object.position );\n\t\t\tscope.zoom0 = scope.object.zoom;\n\n\t\t};\n\n\t\tthis.reset = function () {\n\n\t\t\tscope.target.copy( scope.target0 );\n\t\t\tscope.object.position.copy( scope.position0 );\n\t\t\tscope.object.zoom = scope.zoom0;\n\n\t\t\tscope.object.updateProjectionMatrix();\n\t\t\tscope.dispatchEvent( _changeEvent );\n\n\t\t\tscope.update();\n\n\t\t\tstate = STATE.NONE;\n\n\t\t};\n\n\t\t// this method is exposed, but perhaps it would be better if we can make it private...\n\t\tthis.update = function () {\n\n\t\t\tconst offset = new Vector3();\n\n\t\t\t// so camera.up is the orbit axis\n\t\t\tconst quat = new Quaternion().setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) );\n\t\t\tconst quatInverse = quat.clone().invert();\n\n\t\t\tconst lastPosition = new Vector3();\n\t\t\tconst lastQuaternion = new Quaternion();\n\t\t\tconst lastTargetPosition = new Vector3();\n\n\t\t\tconst twoPI = 2 * Math.PI;\n\n\t\t\treturn function update( deltaTime = null ) {\n\n\t\t\t\tconst position = scope.object.position;\n\n\t\t\t\toffset.copy( position ).sub( scope.target );\n\n\t\t\t\t// rotate offset to \"y-axis-is-up\" space\n\t\t\t\toffset.applyQuaternion( quat );\n\n\t\t\t\t// angle from z-axis around y-axis\n\t\t\t\tspherical.setFromVector3( offset );\n\n\t\t\t\tif ( scope.autoRotate && state === STATE.NONE ) {\n\n\t\t\t\t\trotateLeft( getAutoRotationAngle( deltaTime ) );\n\n\t\t\t\t}\n\n\t\t\t\tif ( scope.enableDamping ) {\n\n\t\t\t\t\tspherical.theta += sphericalDelta.theta * scope.dampingFactor;\n\t\t\t\t\tspherical.phi += sphericalDelta.phi * scope.dampingFactor;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tspherical.theta += sphericalDelta.theta;\n\t\t\t\t\tspherical.phi += sphericalDelta.phi;\n\n\t\t\t\t}\n\n\t\t\t\t// restrict theta to be between desired limits\n\n\t\t\t\tlet min = scope.minAzimuthAngle;\n\t\t\t\tlet max = scope.maxAzimuthAngle;\n\n\t\t\t\tif ( isFinite( min ) && isFinite( max ) ) {\n\n\t\t\t\t\tif ( min < - Math.PI ) min += twoPI; else if ( min > Math.PI ) min -= twoPI;\n\n\t\t\t\t\tif ( max < - Math.PI ) max += twoPI; else if ( max > Math.PI ) max -= twoPI;\n\n\t\t\t\t\tif ( min <= max ) {\n\n\t\t\t\t\t\tspherical.theta = Math.max( min, Math.min( max, spherical.theta ) );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tspherical.theta = ( spherical.theta > ( min + max ) / 2 ) ?\n\t\t\t\t\t\t\tMath.max( min, spherical.theta ) :\n\t\t\t\t\t\t\tMath.min( max, spherical.theta );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// restrict phi to be between desired limits\n\t\t\t\tspherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );\n\n\t\t\t\tspherical.makeSafe();\n\n\n\t\t\t\t// move target to panned location\n\n\t\t\t\tif ( scope.enableDamping === true ) {\n\n\t\t\t\t\tscope.target.addScaledVector( panOffset, scope.dampingFactor );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tscope.target.add( panOffset );\n\n\t\t\t\t}\n\n\t\t\t\t// Limit the target distance from the cursor to create a sphere around the center of interest\n\t\t\t\tscope.target.sub( scope.cursor );\n\t\t\t\tscope.target.clampLength( scope.minTargetRadius, scope.maxTargetRadius );\n\t\t\t\tscope.target.add( scope.cursor );\n\n\t\t\t\t// adjust the camera position based on zoom only if we're not zooming to the cursor or if it's an ortho camera\n\t\t\t\t// we adjust zoom later in these cases\n\t\t\t\tif ( scope.zoomToCursor && performCursorZoom || scope.object.isOrthographicCamera ) {\n\n\t\t\t\t\tspherical.radius = clampDistance( spherical.radius );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tspherical.radius = clampDistance( spherical.radius * scale );\n\n\t\t\t\t}\n\n\t\t\t\toffset.setFromSpherical( spherical );\n\n\t\t\t\t// rotate offset back to \"camera-up-vector-is-up\" space\n\t\t\t\toffset.applyQuaternion( quatInverse );\n\n\t\t\t\tposition.copy( scope.target ).add( offset );\n\n\t\t\t\tscope.object.lookAt( scope.target );\n\n\t\t\t\tif ( scope.enableDamping === true ) {\n\n\t\t\t\t\tsphericalDelta.theta *= ( 1 - scope.dampingFactor );\n\t\t\t\t\tsphericalDelta.phi *= ( 1 - scope.dampingFactor );\n\n\t\t\t\t\tpanOffset.multiplyScalar( 1 - scope.dampingFactor );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tsphericalDelta.set( 0, 0, 0 );\n\n\t\t\t\t\tpanOffset.set( 0, 0, 0 );\n\n\t\t\t\t}\n\n\t\t\t\t// adjust camera position\n\t\t\t\tlet zoomChanged = false;\n\t\t\t\tif ( scope.zoomToCursor && performCursorZoom ) {\n\n\t\t\t\t\tlet newRadius = null;\n\t\t\t\t\tif ( scope.object.isPerspectiveCamera ) {\n\n\t\t\t\t\t\t// move the camera down the pointer ray\n\t\t\t\t\t\t// this method avoids floating point error\n\t\t\t\t\t\tconst prevRadius = offset.length();\n\t\t\t\t\t\tnewRadius = clampDistance( prevRadius * scale );\n\n\t\t\t\t\t\tconst radiusDelta = prevRadius - newRadius;\n\t\t\t\t\t\tscope.object.position.addScaledVector( dollyDirection, radiusDelta );\n\t\t\t\t\t\tscope.object.updateMatrixWorld();\n\n\t\t\t\t\t} else if ( scope.object.isOrthographicCamera ) {\n\n\t\t\t\t\t\t// adjust the ortho camera position based on zoom changes\n\t\t\t\t\t\tconst mouseBefore = new Vector3( mouse.x, mouse.y, 0 );\n\t\t\t\t\t\tmouseBefore.unproject( scope.object );\n\n\t\t\t\t\t\tscope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / scale ) );\n\t\t\t\t\t\tscope.object.updateProjectionMatrix();\n\t\t\t\t\t\tzoomChanged = true;\n\n\t\t\t\t\t\tconst mouseAfter = new Vector3( mouse.x, mouse.y, 0 );\n\t\t\t\t\t\tmouseAfter.unproject( scope.object );\n\n\t\t\t\t\t\tscope.object.position.sub( mouseAfter ).add( mouseBefore );\n\t\t\t\t\t\tscope.object.updateMatrixWorld();\n\n\t\t\t\t\t\tnewRadius = offset.length();\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconsole.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled.' );\n\t\t\t\t\t\tscope.zoomToCursor = false;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// handle the placement of the target\n\t\t\t\t\tif ( newRadius !== null ) {\n\n\t\t\t\t\t\tif ( this.screenSpacePanning ) {\n\n\t\t\t\t\t\t\t// position the orbit target in front of the new camera position\n\t\t\t\t\t\t\tscope.target.set( 0, 0, - 1 )\n\t\t\t\t\t\t\t\t.transformDirection( scope.object.matrix )\n\t\t\t\t\t\t\t\t.multiplyScalar( newRadius )\n\t\t\t\t\t\t\t\t.add( scope.object.position );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// get the ray and translation plane to compute target\n\t\t\t\t\t\t\t_ray.origin.copy( scope.object.position );\n\t\t\t\t\t\t\t_ray.direction.set( 0, 0, - 1 ).transformDirection( scope.object.matrix );\n\n\t\t\t\t\t\t\t// if the camera is 20 degrees above the horizon then don't adjust the focus target to avoid\n\t\t\t\t\t\t\t// extremely large values\n\t\t\t\t\t\t\tif ( Math.abs( scope.object.up.dot( _ray.direction ) ) < TILT_LIMIT ) {\n\n\t\t\t\t\t\t\t\tobject.lookAt( scope.target );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t_plane.setFromNormalAndCoplanarPoint( scope.object.up, scope.target );\n\t\t\t\t\t\t\t\t_ray.intersectPlane( _plane, scope.target );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( scope.object.isOrthographicCamera ) {\n\n\t\t\t\t\tscope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / scale ) );\n\t\t\t\t\tscope.object.updateProjectionMatrix();\n\t\t\t\t\tzoomChanged = true;\n\n\t\t\t\t}\n\n\t\t\t\tscale = 1;\n\t\t\t\tperformCursorZoom = false;\n\n\t\t\t\t// update condition is:\n\t\t\t\t// min(camera displacement, camera rotation in radians)^2 > EPS\n\t\t\t\t// using small-angle approximation cos(x/2) = 1 - x^2 / 8\n\n\t\t\t\tif ( zoomChanged ||\n\t\t\t\t\tlastPosition.distanceToSquared( scope.object.position ) > EPS ||\n\t\t\t\t\t8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ||\n\t\t\t\t\tlastTargetPosition.distanceToSquared( scope.target ) > 0 ) {\n\n\t\t\t\t\tscope.dispatchEvent( _changeEvent );\n\n\t\t\t\t\tlastPosition.copy( scope.object.position );\n\t\t\t\t\tlastQuaternion.copy( scope.object.quaternion );\n\t\t\t\t\tlastTargetPosition.copy( scope.target );\n\n\t\t\t\t\treturn true;\n\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\n\t\t\t};\n\n\t\t}();\n\n\t\tthis.dispose = function () {\n\n\t\t\tscope.domElement.removeEventListener( 'contextmenu', onContextMenu );\n\n\t\t\tscope.domElement.removeEventListener( 'pointerdown', onPointerDown );\n\t\t\tscope.domElement.removeEventListener( 'pointercancel', onPointerUp );\n\t\t\tscope.domElement.removeEventListener( 'wheel', onMouseWheel );\n\n\t\t\tscope.domElement.removeEventListener( 'pointermove', onPointerMove );\n\t\t\tscope.domElement.removeEventListener( 'pointerup', onPointerUp );\n\n\n\t\t\tif ( scope._domElementKeyEvents !== null ) {\n\n\t\t\t\tscope._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown );\n\t\t\t\tscope._domElementKeyEvents = null;\n\n\t\t\t}\n\n\t\t\t//scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?\n\n\t\t};\n\n\t\t//\n\t\t// internals\n\t\t//\n\n\t\tconst scope = this;\n\n\t\tconst STATE = {\n\t\t\tNONE: - 1,\n\t\t\tROTATE: 0,\n\t\t\tDOLLY: 1,\n\t\t\tPAN: 2,\n\t\t\tTOUCH_ROTATE: 3,\n\t\t\tTOUCH_PAN: 4,\n\t\t\tTOUCH_DOLLY_PAN: 5,\n\t\t\tTOUCH_DOLLY_ROTATE: 6\n\t\t};\n\n\t\tlet state = STATE.NONE;\n\n\t\tconst EPS = 0.000001;\n\n\t\t// current position in spherical coordinates\n\t\tconst spherical = new Spherical();\n\t\tconst sphericalDelta = new Spherical();\n\n\t\tlet scale = 1;\n\t\tconst panOffset = new Vector3();\n\n\t\tconst rotateStart = new Vector2();\n\t\tconst rotateEnd = new Vector2();\n\t\tconst rotateDelta = new Vector2();\n\n\t\tconst panStart = new Vector2();\n\t\tconst panEnd = new Vector2();\n\t\tconst panDelta = new Vector2();\n\n\t\tconst dollyStart = new Vector2();\n\t\tconst dollyEnd = new Vector2();\n\t\tconst dollyDelta = new Vector2();\n\n\t\tconst dollyDirection = new Vector3();\n\t\tconst mouse = new Vector2();\n\t\tlet performCursorZoom = false;\n\n\t\tconst pointers = [];\n\t\tconst pointerPositions = {};\n\n\t\tfunction getAutoRotationAngle( deltaTime ) {\n\n\t\t\tif ( deltaTime !== null ) {\n\n\t\t\t\treturn ( 2 * Math.PI / 60 * scope.autoRotateSpeed ) * deltaTime;\n\n\t\t\t} else {\n\n\t\t\t\treturn 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction getZoomScale( delta ) {\n\n\t\t\tconst normalized_delta = Math.abs( delta ) / ( 100 * ( window.devicePixelRatio | 0 ) );\n\t\t\treturn Math.pow( 0.95, scope.zoomSpeed * normalized_delta );\n\n\t\t}\n\n\t\tfunction rotateLeft( angle ) {\n\n\t\t\tsphericalDelta.theta -= angle;\n\n\t\t}\n\n\t\tfunction rotateUp( angle ) {\n\n\t\t\tsphericalDelta.phi -= angle;\n\n\t\t}\n\n\t\tconst panLeft = function () {\n\n\t\t\tconst v = new Vector3();\n\n\t\t\treturn function panLeft( distance, objectMatrix ) {\n\n\t\t\t\tv.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix\n\t\t\t\tv.multiplyScalar( - distance );\n\n\t\t\t\tpanOffset.add( v );\n\n\t\t\t};\n\n\t\t}();\n\n\t\tconst panUp = function () {\n\n\t\t\tconst v = new Vector3();\n\n\t\t\treturn function panUp( distance, objectMatrix ) {\n\n\t\t\t\tif ( scope.screenSpacePanning === true ) {\n\n\t\t\t\t\tv.setFromMatrixColumn( objectMatrix, 1 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tv.setFromMatrixColumn( objectMatrix, 0 );\n\t\t\t\t\tv.crossVectors( scope.object.up, v );\n\n\t\t\t\t}\n\n\t\t\t\tv.multiplyScalar( distance );\n\n\t\t\t\tpanOffset.add( v );\n\n\t\t\t};\n\n\t\t}();\n\n\t\t// deltaX and deltaY are in pixels; right and down are positive\n\t\tconst pan = function () {\n\n\t\t\tconst offset = new Vector3();\n\n\t\t\treturn function pan( deltaX, deltaY ) {\n\n\t\t\t\tconst element = scope.domElement;\n\n\t\t\t\tif ( scope.object.isPerspectiveCamera ) {\n\n\t\t\t\t\t// perspective\n\t\t\t\t\tconst position = scope.object.position;\n\t\t\t\t\toffset.copy( position ).sub( scope.target );\n\t\t\t\t\tlet targetDistance = offset.length();\n\n\t\t\t\t\t// half of the fov is center to top of screen\n\t\t\t\t\ttargetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );\n\n\t\t\t\t\t// we use only clientHeight here so aspect ratio does not distort speed\n\t\t\t\t\tpanLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );\n\t\t\t\t\tpanUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );\n\n\t\t\t\t} else if ( scope.object.isOrthographicCamera ) {\n\n\t\t\t\t\t// orthographic\n\t\t\t\t\tpanLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );\n\t\t\t\t\tpanUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// camera neither orthographic nor perspective\n\t\t\t\t\tconsole.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );\n\t\t\t\t\tscope.enablePan = false;\n\n\t\t\t\t}\n\n\t\t\t};\n\n\t\t}();\n\n\t\tfunction dollyOut( dollyScale ) {\n\n\t\t\tif ( scope.object.isPerspectiveCamera || scope.object.isOrthographicCamera ) {\n\n\t\t\t\tscale /= dollyScale;\n\n\t\t\t} else {\n\n\t\t\t\tconsole.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );\n\t\t\t\tscope.enableZoom = false;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction dollyIn( dollyScale ) {\n\n\t\t\tif ( scope.object.isPerspectiveCamera || scope.object.isOrthographicCamera ) {\n\n\t\t\t\tscale *= dollyScale;\n\n\t\t\t} else {\n\n\t\t\t\tconsole.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );\n\t\t\t\tscope.enableZoom = false;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction updateZoomParameters( x, y ) {\n\n\t\t\tif ( ! scope.zoomToCursor ) {\n\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tperformCursorZoom = true;\n\n\t\t\tconst rect = scope.domElement.getBoundingClientRect();\n\t\t\tconst dx = x - rect.left;\n\t\t\tconst dy = y - rect.top;\n\t\t\tconst w = rect.width;\n\t\t\tconst h = rect.height;\n\n\t\t\tmouse.x = ( dx / w ) * 2 - 1;\n\t\t\tmouse.y = - ( dy / h ) * 2 + 1;\n\n\t\t\tdollyDirection.set( mouse.x, mouse.y, 1 ).unproject( scope.object ).sub( scope.object.position ).normalize();\n\n\t\t}\n\n\t\tfunction clampDistance( dist ) {\n\n\t\t\treturn Math.max( scope.minDistance, Math.min( scope.maxDistance, dist ) );\n\n\t\t}\n\n\t\t//\n\t\t// event callbacks - update the object state\n\t\t//\n\n\t\tfunction handleMouseDownRotate( event ) {\n\n\t\t\trotateStart.set( event.clientX, event.clientY );\n\n\t\t}\n\n\t\tfunction handleMouseDownDolly( event ) {\n\n\t\t\tupdateZoomParameters( event.clientX, event.clientX );\n\t\t\tdollyStart.set( event.clientX, event.clientY );\n\n\t\t}\n\n\t\tfunction handleMouseDownPan( event ) {\n\n\t\t\tpanStart.set( event.clientX, event.clientY );\n\n\t\t}\n\n\t\tfunction handleMouseMoveRotate( event ) {\n\n\t\t\trotateEnd.set( event.clientX, event.clientY );\n\n\t\t\trotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );\n\n\t\t\tconst element = scope.domElement;\n\n\t\t\trotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height\n\n\t\t\trotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );\n\n\t\t\trotateStart.copy( rotateEnd );\n\n\t\t\tscope.update();\n\n\t\t}\n\n\t\tfunction handleMouseMoveDolly( event ) {\n\n\t\t\tdollyEnd.set( event.clientX, event.clientY );\n\n\t\t\tdollyDelta.subVectors( dollyEnd, dollyStart );\n\n\t\t\tif ( dollyDelta.y > 0 ) {\n\n\t\t\t\tdollyOut( getZoomScale( dollyDelta.y ) );\n\n\t\t\t} else if ( dollyDelta.y < 0 ) {\n\n\t\t\t\tdollyIn( getZoomScale( dollyDelta.y ) );\n\n\t\t\t}\n\n\t\t\tdollyStart.copy( dollyEnd );\n\n\t\t\tscope.update();\n\n\t\t}\n\n\t\tfunction handleMouseMovePan( event ) {\n\n\t\t\tpanEnd.set( event.clientX, event.clientY );\n\n\t\t\tpanDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );\n\n\t\t\tpan( panDelta.x, panDelta.y );\n\n\t\t\tpanStart.copy( panEnd );\n\n\t\t\tscope.update();\n\n\t\t}\n\n\t\tfunction handleMouseWheel( event ) {\n\n\t\t\tupdateZoomParameters( event.clientX, event.clientY );\n\n\t\t\tif ( event.deltaY < 0 ) {\n\n\t\t\t\tdollyIn( getZoomScale( event.deltaY ) );\n\n\t\t\t} else if ( event.deltaY > 0 ) {\n\n\t\t\t\tdollyOut( getZoomScale( event.deltaY ) );\n\n\t\t\t}\n\n\t\t\tscope.update();\n\n\t\t}\n\n\t\tfunction handleKeyDown( event ) {\n\n\t\t\tlet needsUpdate = false;\n\n\t\t\tswitch ( event.code ) {\n\n\t\t\t\tcase scope.keys.UP:\n\n\t\t\t\t\tif ( event.ctrlKey || event.metaKey || event.shiftKey ) {\n\n\t\t\t\t\t\trotateUp( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tpan( 0, scope.keyPanSpeed );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tneedsUpdate = true;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase scope.keys.BOTTOM:\n\n\t\t\t\t\tif ( event.ctrlKey || event.metaKey || event.shiftKey ) {\n\n\t\t\t\t\t\trotateUp( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tpan( 0, - scope.keyPanSpeed );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tneedsUpdate = true;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase scope.keys.LEFT:\n\n\t\t\t\t\tif ( event.ctrlKey || event.metaKey || event.shiftKey ) {\n\n\t\t\t\t\t\trotateLeft( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tpan( scope.keyPanSpeed, 0 );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tneedsUpdate = true;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase scope.keys.RIGHT:\n\n\t\t\t\t\tif ( event.ctrlKey || event.metaKey || event.shiftKey ) {\n\n\t\t\t\t\t\trotateLeft( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tpan( - scope.keyPanSpeed, 0 );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tneedsUpdate = true;\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t\tif ( needsUpdate ) {\n\n\t\t\t\t// prevent the browser from scrolling on cursor keys\n\t\t\t\tevent.preventDefault();\n\n\t\t\t\tscope.update();\n\n\t\t\t}\n\n\n\t\t}\n\n\t\tfunction handleTouchStartRotate( event ) {\n\n\t\t\tif ( pointers.length === 1 ) {\n\n\t\t\t\trotateStart.set( event.pageX, event.pageY );\n\n\t\t\t} else {\n\n\t\t\t\tconst position = getSecondPointerPosition( event );\n\n\t\t\t\tconst x = 0.5 * ( event.pageX + position.x );\n\t\t\t\tconst y = 0.5 * ( event.pageY + position.y );\n\n\t\t\t\trotateStart.set( x, y );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction handleTouchStartPan( event ) {\n\n\t\t\tif ( pointers.length === 1 ) {\n\n\t\t\t\tpanStart.set( event.pageX, event.pageY );\n\n\t\t\t} else {\n\n\t\t\t\tconst position = getSecondPointerPosition( event );\n\n\t\t\t\tconst x = 0.5 * ( event.pageX + position.x );\n\t\t\t\tconst y = 0.5 * ( event.pageY + position.y );\n\n\t\t\t\tpanStart.set( x, y );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction handleTouchStartDolly( event ) {\n\n\t\t\tconst position = getSecondPointerPosition( event );\n\n\t\t\tconst dx = event.pageX - position.x;\n\t\t\tconst dy = event.pageY - position.y;\n\n\t\t\tconst distance = Math.sqrt( dx * dx + dy * dy );\n\n\t\t\tdollyStart.set( 0, distance );\n\n\t\t}\n\n\t\tfunction handleTouchStartDollyPan( event ) {\n\n\t\t\tif ( scope.enableZoom ) handleTouchStartDolly( event );\n\n\t\t\tif ( scope.enablePan ) handleTouchStartPan( event );\n\n\t\t}\n\n\t\tfunction handleTouchStartDollyRotate( event ) {\n\n\t\t\tif ( scope.enableZoom ) handleTouchStartDolly( event );\n\n\t\t\tif ( scope.enableRotate ) handleTouchStartRotate( event );\n\n\t\t}\n\n\t\tfunction handleTouchMoveRotate( event ) {\n\n\t\t\tif ( pointers.length == 1 ) {\n\n\t\t\t\trotateEnd.set( event.pageX, event.pageY );\n\n\t\t\t} else {\n\n\t\t\t\tconst position = getSecondPointerPosition( event );\n\n\t\t\t\tconst x = 0.5 * ( event.pageX + position.x );\n\t\t\t\tconst y = 0.5 * ( event.pageY + position.y );\n\n\t\t\t\trotateEnd.set( x, y );\n\n\t\t\t}\n\n\t\t\trotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );\n\n\t\t\tconst element = scope.domElement;\n\n\t\t\trotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height\n\n\t\t\trotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );\n\n\t\t\trotateStart.copy( rotateEnd );\n\n\t\t}\n\n\t\tfunction handleTouchMovePan( event ) {\n\n\t\t\tif ( pointers.length === 1 ) {\n\n\t\t\t\tpanEnd.set( event.pageX, event.pageY );\n\n\t\t\t} else {\n\n\t\t\t\tconst position = getSecondPointerPosition( event );\n\n\t\t\t\tconst x = 0.5 * ( event.pageX + position.x );\n\t\t\t\tconst y = 0.5 * ( event.pageY + position.y );\n\n\t\t\t\tpanEnd.set( x, y );\n\n\t\t\t}\n\n\t\t\tpanDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );\n\n\t\t\tpan( panDelta.x, panDelta.y );\n\n\t\t\tpanStart.copy( panEnd );\n\n\t\t}\n\n\t\tfunction handleTouchMoveDolly( event ) {\n\n\t\t\tconst position = getSecondPointerPosition( event );\n\n\t\t\tconst dx = event.pageX - position.x;\n\t\t\tconst dy = event.pageY - position.y;\n\n\t\t\tconst distance = Math.sqrt( dx * dx + dy * dy );\n\n\t\t\tdollyEnd.set( 0, distance );\n\n\t\t\tdollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );\n\n\t\t\tdollyOut( dollyDelta.y );\n\n\t\t\tdollyStart.copy( dollyEnd );\n\n\t\t\tconst centerX = ( event.pageX + position.x ) * 0.5;\n\t\t\tconst centerY = ( event.pageY + position.y ) * 0.5;\n\n\t\t\tupdateZoomParameters( centerX, centerY );\n\n\t\t}\n\n\t\tfunction handleTouchMoveDollyPan( event ) {\n\n\t\t\tif ( scope.enableZoom ) handleTouchMoveDolly( event );\n\n\t\t\tif ( scope.enablePan ) handleTouchMovePan( event );\n\n\t\t}\n\n\t\tfunction handleTouchMoveDollyRotate( event ) {\n\n\t\t\tif ( scope.enableZoom ) handleTouchMoveDolly( event );\n\n\t\t\tif ( scope.enableRotate ) handleTouchMoveRotate( event );\n\n\t\t}\n\n\t\t//\n\t\t// event handlers - FSM: listen for events and reset state\n\t\t//\n\n\t\tfunction onPointerDown( event ) {\n\n\t\t\tif ( scope.enabled === false ) return;\n\n\t\t\tif ( pointers.length === 0 ) {\n\n\t\t\t\tscope.domElement.setPointerCapture( event.pointerId );\n\n\t\t\t\tscope.domElement.addEventListener( 'pointermove', onPointerMove );\n\t\t\t\tscope.domElement.addEventListener( 'pointerup', onPointerUp );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\taddPointer( event );\n\n\t\t\tif ( event.pointerType === 'touch' ) {\n\n\t\t\t\tonTouchStart( event );\n\n\t\t\t} else {\n\n\t\t\t\tonMouseDown( event );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction onPointerMove( event ) {\n\n\t\t\tif ( scope.enabled === false ) return;\n\n\t\t\tif ( event.pointerType === 'touch' ) {\n\n\t\t\t\tonTouchMove( event );\n\n\t\t\t} else {\n\n\t\t\t\tonMouseMove( event );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction onPointerUp( event ) {\n\n\t\t\tremovePointer( event );\n\n\t\t\tif ( pointers.length === 0 ) {\n\n\t\t\t\tscope.domElement.releasePointerCapture( event.pointerId );\n\n\t\t\t\tscope.domElement.removeEventListener( 'pointermove', onPointerMove );\n\t\t\t\tscope.domElement.removeEventListener( 'pointerup', onPointerUp );\n\n\t\t\t}\n\n\t\t\tscope.dispatchEvent( _endEvent );\n\n\t\t\tstate = STATE.NONE;\n\n\t\t}\n\n\t\tfunction onMouseDown( event ) {\n\n\t\t\tlet mouseAction;\n\n\t\t\tswitch ( event.button ) {\n\n\t\t\t\tcase 0:\n\n\t\t\t\t\tmouseAction = scope.mouseButtons.LEFT;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 1:\n\n\t\t\t\t\tmouseAction = scope.mouseButtons.MIDDLE;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 2:\n\n\t\t\t\t\tmouseAction = scope.mouseButtons.RIGHT;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\n\t\t\t\t\tmouseAction = - 1;\n\n\t\t\t}\n\n\t\t\tswitch ( mouseAction ) {\n\n\t\t\t\tcase MOUSE.DOLLY:\n\n\t\t\t\t\tif ( scope.enableZoom === false ) return;\n\n\t\t\t\t\thandleMouseDownDolly( event );\n\n\t\t\t\t\tstate = STATE.DOLLY;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase MOUSE.ROTATE:\n\n\t\t\t\t\tif ( event.ctrlKey || event.metaKey || event.shiftKey ) {\n\n\t\t\t\t\t\tif ( scope.enablePan === false ) return;\n\n\t\t\t\t\t\thandleMouseDownPan( event );\n\n\t\t\t\t\t\tstate = STATE.PAN;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( scope.enableRotate === false ) return;\n\n\t\t\t\t\t\thandleMouseDownRotate( event );\n\n\t\t\t\t\t\tstate = STATE.ROTATE;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase MOUSE.PAN:\n\n\t\t\t\t\tif ( event.ctrlKey || event.metaKey || event.shiftKey ) {\n\n\t\t\t\t\t\tif ( scope.enableRotate === false ) return;\n\n\t\t\t\t\t\thandleMouseDownRotate( event );\n\n\t\t\t\t\t\tstate = STATE.ROTATE;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( scope.enablePan === false ) return;\n\n\t\t\t\t\t\thandleMouseDownPan( event );\n\n\t\t\t\t\t\tstate = STATE.PAN;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\n\t\t\t\t\tstate = STATE.NONE;\n\n\t\t\t}\n\n\t\t\tif ( state !== STATE.NONE ) {\n\n\t\t\t\tscope.dispatchEvent( _startEvent );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction onMouseMove( event ) {\n\n\t\t\tswitch ( state ) {\n\n\t\t\t\tcase STATE.ROTATE:\n\n\t\t\t\t\tif ( scope.enableRotate === false ) return;\n\n\t\t\t\t\thandleMouseMoveRotate( event );\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase STATE.DOLLY:\n\n\t\t\t\t\tif ( scope.enableZoom === false ) return;\n\n\t\t\t\t\thandleMouseMoveDolly( event );\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase STATE.PAN:\n\n\t\t\t\t\tif ( scope.enablePan === false ) return;\n\n\t\t\t\t\thandleMouseMovePan( event );\n\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction onMouseWheel( event ) {\n\n\t\t\tif ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;\n\n\t\t\tevent.preventDefault();\n\n\t\t\tscope.dispatchEvent( _startEvent );\n\n\t\t\thandleMouseWheel( event );\n\n\t\t\tscope.dispatchEvent( _endEvent );\n\n\t\t}\n\n\t\tfunction onKeyDown( event ) {\n\n\t\t\tif ( scope.enabled === false || scope.enablePan === false ) return;\n\n\t\t\thandleKeyDown( event );\n\n\t\t}\n\n\t\tfunction onTouchStart( event ) {\n\n\t\t\ttrackPointer( event );\n\n\t\t\tswitch ( pointers.length ) {\n\n\t\t\t\tcase 1:\n\n\t\t\t\t\tswitch ( scope.touches.ONE ) {\n\n\t\t\t\t\t\tcase TOUCH.ROTATE:\n\n\t\t\t\t\t\t\tif ( scope.enableRotate === false ) return;\n\n\t\t\t\t\t\t\thandleTouchStartRotate( event );\n\n\t\t\t\t\t\t\tstate = STATE.TOUCH_ROTATE;\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase TOUCH.PAN:\n\n\t\t\t\t\t\t\tif ( scope.enablePan === false ) return;\n\n\t\t\t\t\t\t\thandleTouchStartPan( event );\n\n\t\t\t\t\t\t\tstate = STATE.TOUCH_PAN;\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\n\t\t\t\t\t\t\tstate = STATE.NONE;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 2:\n\n\t\t\t\t\tswitch ( scope.touches.TWO ) {\n\n\t\t\t\t\t\tcase TOUCH.DOLLY_PAN:\n\n\t\t\t\t\t\t\tif ( scope.enableZoom === false && scope.enablePan === false ) return;\n\n\t\t\t\t\t\t\thandleTouchStartDollyPan( event );\n\n\t\t\t\t\t\t\tstate = STATE.TOUCH_DOLLY_PAN;\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase TOUCH.DOLLY_ROTATE:\n\n\t\t\t\t\t\t\tif ( scope.enableZoom === false && scope.enableRotate === false ) return;\n\n\t\t\t\t\t\t\thandleTouchStartDollyRotate( event );\n\n\t\t\t\t\t\t\tstate = STATE.TOUCH_DOLLY_ROTATE;\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\n\t\t\t\t\t\t\tstate = STATE.NONE;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\n\t\t\t\t\tstate = STATE.NONE;\n\n\t\t\t}\n\n\t\t\tif ( state !== STATE.NONE ) {\n\n\t\t\t\tscope.dispatchEvent( _startEvent );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction onTouchMove( event ) {\n\n\t\t\ttrackPointer( event );\n\n\t\t\tswitch ( state ) {\n\n\t\t\t\tcase STATE.TOUCH_ROTATE:\n\n\t\t\t\t\tif ( scope.enableRotate === false ) return;\n\n\t\t\t\t\thandleTouchMoveRotate( event );\n\n\t\t\t\t\tscope.update();\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase STATE.TOUCH_PAN:\n\n\t\t\t\t\tif ( scope.enablePan === false ) return;\n\n\t\t\t\t\thandleTouchMovePan( event );\n\n\t\t\t\t\tscope.update();\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase STATE.TOUCH_DOLLY_PAN:\n\n\t\t\t\t\tif ( scope.enableZoom === false && scope.enablePan === false ) return;\n\n\t\t\t\t\thandleTouchMoveDollyPan( event );\n\n\t\t\t\t\tscope.update();\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase STATE.TOUCH_DOLLY_ROTATE:\n\n\t\t\t\t\tif ( scope.enableZoom === false && scope.enableRotate === false ) return;\n\n\t\t\t\t\thandleTouchMoveDollyRotate( event );\n\n\t\t\t\t\tscope.update();\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\n\t\t\t\t\tstate = STATE.NONE;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction onContextMenu( event ) {\n\n\t\t\tif ( scope.enabled === false ) return;\n\n\t\t\tevent.preventDefault();\n\n\t\t}\n\n\t\tfunction addPointer( event ) {\n\n\t\t\tpointers.push( event.pointerId );\n\n\t\t}\n\n\t\tfunction removePointer( event ) {\n\n\t\t\tdelete pointerPositions[ event.pointerId ];\n\n\t\t\tfor ( let i = 0; i < pointers.length; i ++ ) {\n\n\t\t\t\tif ( pointers[ i ] == event.pointerId ) {\n\n\t\t\t\t\tpointers.splice( i, 1 );\n\t\t\t\t\treturn;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction trackPointer( event ) {\n\n\t\t\tlet position = pointerPositions[ event.pointerId ];\n\n\t\t\tif ( position === undefined ) {\n\n\t\t\t\tposition = new Vector2();\n\t\t\t\tpointerPositions[ event.pointerId ] = position;\n\n\t\t\t}\n\n\t\t\tposition.set( event.pageX, event.pageY );\n\n\t\t}\n\n\t\tfunction getSecondPointerPosition( event ) {\n\n\t\t\tconst pointerId = ( event.pointerId === pointers[ 0 ] ) ? pointers[ 1 ] : pointers[ 0 ];\n\n\t\t\treturn pointerPositions[ pointerId ];\n\n\t\t}\n\n\t\t//\n\n\t\tscope.domElement.addEventListener( 'contextmenu', onContextMenu );\n\n\t\tscope.domElement.addEventListener( 'pointerdown', onPointerDown );\n\t\tscope.domElement.addEventListener( 'pointercancel', onPointerUp );\n\t\tscope.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } );\n\n\t\t// force an update at start\n\n\t\tthis.update();\n\n\t}\n\n}\n\nexport { OrbitControls };\n"
  },
  {
    "path": "public/assets/lib/vendor/three/Projector.js",
    "content": "// @ts-nocheck\nimport {\n\tBox3,\n\tColor,\n\tDoubleSide,\n\tFrustum,\n\tMatrix3,\n\tMatrix4,\n\tVector2,\n\tVector3,\n\tVector4\n} from './three.module.js';\n\nclass RenderableObject {\n\n\tconstructor() {\n\n\t\tthis.id = 0;\n\n\t\tthis.object = null;\n\t\tthis.z = 0;\n\t\tthis.renderOrder = 0;\n\n\t}\n\n}\n\n//\n\nclass RenderableFace {\n\n\tconstructor() {\n\n\t\tthis.id = 0;\n\n\t\tthis.v1 = new RenderableVertex();\n\t\tthis.v2 = new RenderableVertex();\n\t\tthis.v3 = new RenderableVertex();\n\n\t\tthis.normalModel = new Vector3();\n\n\t\tthis.vertexNormalsModel = [ new Vector3(), new Vector3(), new Vector3() ];\n\t\tthis.vertexNormalsLength = 0;\n\n\t\tthis.color = new Color();\n\t\tthis.material = null;\n\t\tthis.uvs = [ new Vector2(), new Vector2(), new Vector2() ];\n\n\t\tthis.z = 0;\n\t\tthis.renderOrder = 0;\n\n\t}\n\n}\n\n//\n\nclass RenderableVertex {\n\n\tconstructor() {\n\n\t\tthis.position = new Vector3();\n\t\tthis.positionWorld = new Vector3();\n\t\tthis.positionScreen = new Vector4();\n\n\t\tthis.visible = true;\n\n\t}\n\n\tcopy( vertex ) {\n\n\t\tthis.positionWorld.copy( vertex.positionWorld );\n\t\tthis.positionScreen.copy( vertex.positionScreen );\n\n\t}\n\n}\n\n//\n\nclass RenderableLine {\n\n\tconstructor() {\n\n\t\tthis.id = 0;\n\n\t\tthis.v1 = new RenderableVertex();\n\t\tthis.v2 = new RenderableVertex();\n\n\t\tthis.vertexColors = [ new Color(), new Color() ];\n\t\tthis.material = null;\n\n\t\tthis.z = 0;\n\t\tthis.renderOrder = 0;\n\n\t}\n\n}\n\n//\n\nclass RenderableSprite {\n\n\tconstructor() {\n\n\t\tthis.id = 0;\n\n\t\tthis.object = null;\n\n\t\tthis.x = 0;\n\t\tthis.y = 0;\n\t\tthis.z = 0;\n\n\t\tthis.rotation = 0;\n\t\tthis.scale = new Vector2();\n\n\t\tthis.material = null;\n\t\tthis.renderOrder = 0;\n\n\t}\n\n}\n\n//\n\nclass Projector {\n\n\tconstructor() {\n\n\t\tlet _object, _objectCount, _objectPoolLength = 0,\n\t\t\t_vertex, _vertexCount, _vertexPoolLength = 0,\n\t\t\t_face, _faceCount, _facePoolLength = 0,\n\t\t\t_line, _lineCount, _linePoolLength = 0,\n\t\t\t_sprite, _spriteCount, _spritePoolLength = 0,\n\t\t\t_modelMatrix;\n\n\t\tconst\n\n\t\t\t_renderData = { objects: [], lights: [], elements: [] },\n\n\t\t\t_vector3 = new Vector3(),\n\t\t\t_vector4 = new Vector4(),\n\n\t\t\t_clipBox = new Box3( new Vector3( - 1, - 1, - 1 ), new Vector3( 1, 1, 1 ) ),\n\t\t\t_boundingBox = new Box3(),\n\t\t\t_points3 = new Array( 3 ),\n\n\t\t\t_viewMatrix = new Matrix4(),\n\t\t\t_viewProjectionMatrix = new Matrix4(),\n\n\t\t\t_modelViewProjectionMatrix = new Matrix4(),\n\n\t\t\t_frustum = new Frustum(),\n\n\t\t\t_objectPool = [], _vertexPool = [], _facePool = [], _linePool = [], _spritePool = [];\n\n\t\t//\n\n\t\tfunction RenderList() {\n\n\t\t\tconst normals = [];\n\t\t\tconst colors = [];\n\t\t\tconst uvs = [];\n\n\t\t\tlet object = null;\n\n\t\t\tconst normalMatrix = new Matrix3();\n\n\t\t\tfunction setObject( value ) {\n\n\t\t\t\tobject = value;\n\n\t\t\t\tnormalMatrix.getNormalMatrix( object.matrixWorld );\n\n\t\t\t\tnormals.length = 0;\n\t\t\t\tcolors.length = 0;\n\t\t\t\tuvs.length = 0;\n\n\t\t\t}\n\n\t\t\tfunction projectVertex( vertex ) {\n\n\t\t\t\tconst position = vertex.position;\n\t\t\t\tconst positionWorld = vertex.positionWorld;\n\t\t\t\tconst positionScreen = vertex.positionScreen;\n\n\t\t\t\tpositionWorld.copy( position ).applyMatrix4( _modelMatrix );\n\t\t\t\tpositionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix );\n\n\t\t\t\tconst invW = 1 / positionScreen.w;\n\n\t\t\t\tpositionScreen.x *= invW;\n\t\t\t\tpositionScreen.y *= invW;\n\t\t\t\tpositionScreen.z *= invW;\n\n\t\t\t\tvertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 &&\n\t\t\t\t\t\t positionScreen.y >= - 1 && positionScreen.y <= 1 &&\n\t\t\t\t\t\t positionScreen.z >= - 1 && positionScreen.z <= 1;\n\n\t\t\t}\n\n\t\t\tfunction pushVertex( x, y, z ) {\n\n\t\t\t\t_vertex = getNextVertexInPool();\n\t\t\t\t_vertex.position.set( x, y, z );\n\n\t\t\t\tprojectVertex( _vertex );\n\n\t\t\t}\n\n\t\t\tfunction pushNormal( x, y, z ) {\n\n\t\t\t\tnormals.push( x, y, z );\n\n\t\t\t}\n\n\t\t\tfunction pushColor( r, g, b ) {\n\n\t\t\t\tcolors.push( r, g, b );\n\n\t\t\t}\n\n\t\t\tfunction pushUv( x, y ) {\n\n\t\t\t\tuvs.push( x, y );\n\n\t\t\t}\n\n\t\t\tfunction checkTriangleVisibility( v1, v2, v3 ) {\n\n\t\t\t\tif ( v1.visible === true || v2.visible === true || v3.visible === true ) return true;\n\n\t\t\t\t_points3[ 0 ] = v1.positionScreen;\n\t\t\t\t_points3[ 1 ] = v2.positionScreen;\n\t\t\t\t_points3[ 2 ] = v3.positionScreen;\n\n\t\t\t\treturn _clipBox.intersectsBox( _boundingBox.setFromPoints( _points3 ) );\n\n\t\t\t}\n\n\t\t\tfunction checkBackfaceCulling( v1, v2, v3 ) {\n\n\t\t\t\treturn ( ( v3.positionScreen.x - v1.positionScreen.x ) *\n\t\t\t\t\t    ( v2.positionScreen.y - v1.positionScreen.y ) -\n\t\t\t\t\t    ( v3.positionScreen.y - v1.positionScreen.y ) *\n\t\t\t\t\t    ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0;\n\n\t\t\t}\n\n\t\t\tfunction pushLine( a, b ) {\n\n\t\t\t\tconst v1 = _vertexPool[ a ];\n\t\t\t\tconst v2 = _vertexPool[ b ];\n\n\t\t\t\t// Clip\n\n\t\t\t\tv1.positionScreen.copy( v1.position ).applyMatrix4( _modelViewProjectionMatrix );\n\t\t\t\tv2.positionScreen.copy( v2.position ).applyMatrix4( _modelViewProjectionMatrix );\n\n\t\t\t\tif ( clipLine( v1.positionScreen, v2.positionScreen ) === true ) {\n\n\t\t\t\t\t// Perform the perspective divide\n\t\t\t\t\tv1.positionScreen.multiplyScalar( 1 / v1.positionScreen.w );\n\t\t\t\t\tv2.positionScreen.multiplyScalar( 1 / v2.positionScreen.w );\n\n\t\t\t\t\t_line = getNextLineInPool();\n\t\t\t\t\t_line.id = object.id;\n\t\t\t\t\t_line.v1.copy( v1 );\n\t\t\t\t\t_line.v2.copy( v2 );\n\t\t\t\t\t_line.z = Math.max( v1.positionScreen.z, v2.positionScreen.z );\n\t\t\t\t\t_line.renderOrder = object.renderOrder;\n\n\t\t\t\t\t_line.material = object.material;\n\n\t\t\t\t\tif ( object.material.vertexColors ) {\n\n\t\t\t\t\t\t_line.vertexColors[ 0 ].fromArray( colors, a * 3 );\n\t\t\t\t\t\t_line.vertexColors[ 1 ].fromArray( colors, b * 3 );\n\n\t\t\t\t\t}\n\n\t\t\t\t\t_renderData.elements.push( _line );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfunction pushTriangle( a, b, c, material ) {\n\n\t\t\t\tconst v1 = _vertexPool[ a ];\n\t\t\t\tconst v2 = _vertexPool[ b ];\n\t\t\t\tconst v3 = _vertexPool[ c ];\n\n\t\t\t\tif ( checkTriangleVisibility( v1, v2, v3 ) === false ) return;\n\n\t\t\t\tif ( material.side === DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) {\n\n\t\t\t\t\t_face = getNextFaceInPool();\n\n\t\t\t\t\t_face.id = object.id;\n\t\t\t\t\t_face.v1.copy( v1 );\n\t\t\t\t\t_face.v2.copy( v2 );\n\t\t\t\t\t_face.v3.copy( v3 );\n\t\t\t\t\t_face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3;\n\t\t\t\t\t_face.renderOrder = object.renderOrder;\n\n\t\t\t\t\t// face normal\n\t\t\t\t\t_vector3.subVectors( v3.position, v2.position );\n\t\t\t\t\t_vector4.subVectors( v1.position, v2.position );\n\t\t\t\t\t_vector3.cross( _vector4 );\n\t\t\t\t\t_face.normalModel.copy( _vector3 );\n\t\t\t\t\t_face.normalModel.applyMatrix3( normalMatrix ).normalize();\n\n\t\t\t\t\tfor ( let i = 0; i < 3; i ++ ) {\n\n\t\t\t\t\t\tconst normal = _face.vertexNormalsModel[ i ];\n\t\t\t\t\t\tnormal.fromArray( normals, arguments[ i ] * 3 );\n\t\t\t\t\t\tnormal.applyMatrix3( normalMatrix ).normalize();\n\n\t\t\t\t\t\tconst uv = _face.uvs[ i ];\n\t\t\t\t\t\tuv.fromArray( uvs, arguments[ i ] * 2 );\n\n\t\t\t\t\t}\n\n\t\t\t\t\t_face.vertexNormalsLength = 3;\n\n\t\t\t\t\t_face.material = material;\n\n\t\t\t\t\tif ( material.vertexColors ) {\n\n\t\t\t\t\t\t_face.color.fromArray( colors, a * 3 );\n\n\t\t\t\t\t}\n\n\t\t\t\t\t_renderData.elements.push( _face );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsetObject: setObject,\n\t\t\t\tprojectVertex: projectVertex,\n\t\t\t\tcheckTriangleVisibility: checkTriangleVisibility,\n\t\t\t\tcheckBackfaceCulling: checkBackfaceCulling,\n\t\t\t\tpushVertex: pushVertex,\n\t\t\t\tpushNormal: pushNormal,\n\t\t\t\tpushColor: pushColor,\n\t\t\t\tpushUv: pushUv,\n\t\t\t\tpushLine: pushLine,\n\t\t\t\tpushTriangle: pushTriangle\n\t\t\t};\n\n\t\t}\n\n\t\tconst renderList = new RenderList();\n\n\t\tfunction projectObject( object ) {\n\n\t\t\tif ( object.visible === false ) return;\n\n\t\t\tif ( object.isLight ) {\n\n\t\t\t\t_renderData.lights.push( object );\n\n\t\t\t} else if ( object.isMesh || object.isLine || object.isPoints ) {\n\n\t\t\t\tif ( object.material.visible === false ) return;\n\t\t\t\tif ( object.frustumCulled === true && _frustum.intersectsObject( object ) === false ) return;\n\n\t\t\t\taddObject( object );\n\n\t\t\t} else if ( object.isSprite ) {\n\n\t\t\t\tif ( object.material.visible === false ) return;\n\t\t\t\tif ( object.frustumCulled === true && _frustum.intersectsSprite( object ) === false ) return;\n\n\t\t\t\taddObject( object );\n\n\t\t\t}\n\n\t\t\tconst children = object.children;\n\n\t\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\t\tprojectObject( children[ i ] );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction addObject( object ) {\n\n\t\t\t_object = getNextObjectInPool();\n\t\t\t_object.id = object.id;\n\t\t\t_object.object = object;\n\n\t\t\t_vector3.setFromMatrixPosition( object.matrixWorld );\n\t\t\t_vector3.applyMatrix4( _viewProjectionMatrix );\n\t\t\t_object.z = _vector3.z;\n\t\t\t_object.renderOrder = object.renderOrder;\n\n\t\t\t_renderData.objects.push( _object );\n\n\t\t}\n\n\t\tthis.projectScene = function ( scene, camera, sortObjects, sortElements ) {\n\n\t\t\t_faceCount = 0;\n\t\t\t_lineCount = 0;\n\t\t\t_spriteCount = 0;\n\n\t\t\t_renderData.elements.length = 0;\n\n\t\t\tif ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();\n\t\t\tif ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();\n\n\t\t\t_viewMatrix.copy( camera.matrixWorldInverse );\n\t\t\t_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );\n\n\t\t\t_frustum.setFromProjectionMatrix( _viewProjectionMatrix );\n\n\t\t\t//\n\n\t\t\t_objectCount = 0;\n\n\t\t\t_renderData.objects.length = 0;\n\t\t\t_renderData.lights.length = 0;\n\n\t\t\tprojectObject( scene );\n\n\t\t\tif ( sortObjects === true ) {\n\n\t\t\t\t_renderData.objects.sort( painterSort );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tconst objects = _renderData.objects;\n\n\t\t\tfor ( let o = 0, ol = objects.length; o < ol; o ++ ) {\n\n\t\t\t\tconst object = objects[ o ].object;\n\t\t\t\tconst geometry = object.geometry;\n\n\t\t\t\trenderList.setObject( object );\n\n\t\t\t\t_modelMatrix = object.matrixWorld;\n\n\t\t\t\t_vertexCount = 0;\n\n\t\t\t\tif ( object.isMesh ) {\n\n\t\t\t\t\tlet material = object.material;\n\n\t\t\t\t\tconst isMultiMaterial = Array.isArray( material );\n\n\t\t\t\t\tconst attributes = geometry.attributes;\n\t\t\t\t\tconst groups = geometry.groups;\n\n\t\t\t\t\tif ( attributes.position === undefined ) continue;\n\n\t\t\t\t\tconst positions = attributes.position.array;\n\n\t\t\t\t\tfor ( let i = 0, l = positions.length; i < l; i += 3 ) {\n\n\t\t\t\t\t\tlet x = positions[ i ];\n\t\t\t\t\t\tlet y = positions[ i + 1 ];\n\t\t\t\t\t\tlet z = positions[ i + 2 ];\n\n\t\t\t\t\t\tconst morphTargets = geometry.morphAttributes.position;\n\n\t\t\t\t\t\tif ( morphTargets !== undefined ) {\n\n\t\t\t\t\t\t\tconst morphTargetsRelative = geometry.morphTargetsRelative;\n\t\t\t\t\t\t\tconst morphInfluences = object.morphTargetInfluences;\n\n\t\t\t\t\t\t\tfor ( let t = 0, tl = morphTargets.length; t < tl; t ++ ) {\n\n\t\t\t\t\t\t\t\tconst influence = morphInfluences[ t ];\n\n\t\t\t\t\t\t\t\tif ( influence === 0 ) continue;\n\n\t\t\t\t\t\t\t\tconst target = morphTargets[ t ];\n\n\t\t\t\t\t\t\t\tif ( morphTargetsRelative ) {\n\n\t\t\t\t\t\t\t\t\tx += target.getX( i / 3 ) * influence;\n\t\t\t\t\t\t\t\t\ty += target.getY( i / 3 ) * influence;\n\t\t\t\t\t\t\t\t\tz += target.getZ( i / 3 ) * influence;\n\n\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\tx += ( target.getX( i / 3 ) - positions[ i ] ) * influence;\n\t\t\t\t\t\t\t\t\ty += ( target.getY( i / 3 ) - positions[ i + 1 ] ) * influence;\n\t\t\t\t\t\t\t\t\tz += ( target.getZ( i / 3 ) - positions[ i + 2 ] ) * influence;\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\trenderList.pushVertex( x, y, z );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( attributes.normal !== undefined ) {\n\n\t\t\t\t\t\tconst normals = attributes.normal.array;\n\n\t\t\t\t\t\tfor ( let i = 0, l = normals.length; i < l; i += 3 ) {\n\n\t\t\t\t\t\t\trenderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( attributes.color !== undefined ) {\n\n\t\t\t\t\t\tconst colors = attributes.color.array;\n\n\t\t\t\t\t\tfor ( let i = 0, l = colors.length; i < l; i += 3 ) {\n\n\t\t\t\t\t\t\trenderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( attributes.uv !== undefined ) {\n\n\t\t\t\t\t\tconst uvs = attributes.uv.array;\n\n\t\t\t\t\t\tfor ( let i = 0, l = uvs.length; i < l; i += 2 ) {\n\n\t\t\t\t\t\t\trenderList.pushUv( uvs[ i ], uvs[ i + 1 ] );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( geometry.index !== null ) {\n\n\t\t\t\t\t\tconst indices = geometry.index.array;\n\n\t\t\t\t\t\tif ( groups.length > 0 ) {\n\n\t\t\t\t\t\t\tfor ( let g = 0; g < groups.length; g ++ ) {\n\n\t\t\t\t\t\t\t\tconst group = groups[ g ];\n\n\t\t\t\t\t\t\t\tmaterial = isMultiMaterial === true\n\t\t\t\t\t\t\t\t\t ? object.material[ group.materialIndex ]\n\t\t\t\t\t\t\t\t\t : object.material;\n\n\t\t\t\t\t\t\t\tif ( material === undefined ) continue;\n\n\t\t\t\t\t\t\t\tfor ( let i = group.start, l = group.start + group.count; i < l; i += 3 ) {\n\n\t\t\t\t\t\t\t\t\trenderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tfor ( let i = 0, l = indices.length; i < l; i += 3 ) {\n\n\t\t\t\t\t\t\t\trenderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( groups.length > 0 ) {\n\n\t\t\t\t\t\t\tfor ( let g = 0; g < groups.length; g ++ ) {\n\n\t\t\t\t\t\t\t\tconst group = groups[ g ];\n\n\t\t\t\t\t\t\t\tmaterial = isMultiMaterial === true\n\t\t\t\t\t\t\t\t\t ? object.material[ group.materialIndex ]\n\t\t\t\t\t\t\t\t\t : object.material;\n\n\t\t\t\t\t\t\t\tif ( material === undefined ) continue;\n\n\t\t\t\t\t\t\t\tfor ( let i = group.start, l = group.start + group.count; i < l; i += 3 ) {\n\n\t\t\t\t\t\t\t\t\trenderList.pushTriangle( i, i + 1, i + 2, material );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tfor ( let i = 0, l = positions.length / 3; i < l; i += 3 ) {\n\n\t\t\t\t\t\t\t\trenderList.pushTriangle( i, i + 1, i + 2, material );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( object.isLine ) {\n\n\t\t\t\t\t_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );\n\n\t\t\t\t\tconst attributes = geometry.attributes;\n\n\t\t\t\t\tif ( attributes.position !== undefined ) {\n\n\t\t\t\t\t\tconst positions = attributes.position.array;\n\n\t\t\t\t\t\tfor ( let i = 0, l = positions.length; i < l; i += 3 ) {\n\n\t\t\t\t\t\t\trenderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( attributes.color !== undefined ) {\n\n\t\t\t\t\t\t\tconst colors = attributes.color.array;\n\n\t\t\t\t\t\t\tfor ( let i = 0, l = colors.length; i < l; i += 3 ) {\n\n\t\t\t\t\t\t\t\trenderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( geometry.index !== null ) {\n\n\t\t\t\t\t\t\tconst indices = geometry.index.array;\n\n\t\t\t\t\t\t\tfor ( let i = 0, l = indices.length; i < l; i += 2 ) {\n\n\t\t\t\t\t\t\t\trenderList.pushLine( indices[ i ], indices[ i + 1 ] );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tconst step = object.isLineSegments ? 2 : 1;\n\n\t\t\t\t\t\t\tfor ( let i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) {\n\n\t\t\t\t\t\t\t\trenderList.pushLine( i, i + 1 );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( object.isPoints ) {\n\n\t\t\t\t\t_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );\n\n\t\t\t\t\tconst attributes = geometry.attributes;\n\n\t\t\t\t\tif ( attributes.position !== undefined ) {\n\n\t\t\t\t\t\tconst positions = attributes.position.array;\n\n\t\t\t\t\t\tfor ( let i = 0, l = positions.length; i < l; i += 3 ) {\n\n\t\t\t\t\t\t\t_vector4.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ], 1 );\n\t\t\t\t\t\t\t_vector4.applyMatrix4( _modelViewProjectionMatrix );\n\n\t\t\t\t\t\t\tpushPoint( _vector4, object, camera );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( object.isSprite ) {\n\n\t\t\t\t\tobject.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );\n\t\t\t\t\t_vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 );\n\t\t\t\t\t_vector4.applyMatrix4( _viewProjectionMatrix );\n\n\t\t\t\t\tpushPoint( _vector4, object, camera );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( sortElements === true ) {\n\n\t\t\t\t_renderData.elements.sort( painterSort );\n\n\t\t\t}\n\n\t\t\treturn _renderData;\n\n\t\t};\n\n\t\tfunction pushPoint( _vector4, object, camera ) {\n\n\t\t\tconst invW = 1 / _vector4.w;\n\n\t\t\t_vector4.z *= invW;\n\n\t\t\tif ( _vector4.z >= - 1 && _vector4.z <= 1 ) {\n\n\t\t\t\t_sprite = getNextSpriteInPool();\n\t\t\t\t_sprite.id = object.id;\n\t\t\t\t_sprite.x = _vector4.x * invW;\n\t\t\t\t_sprite.y = _vector4.y * invW;\n\t\t\t\t_sprite.z = _vector4.z;\n\t\t\t\t_sprite.renderOrder = object.renderOrder;\n\t\t\t\t_sprite.object = object;\n\n\t\t\t\t_sprite.rotation = object.rotation;\n\n\t\t\t\t_sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) );\n\t\t\t\t_sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) );\n\n\t\t\t\t_sprite.material = object.material;\n\n\t\t\t\t_renderData.elements.push( _sprite );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Pools\n\n\t\tfunction getNextObjectInPool() {\n\n\t\t\tif ( _objectCount === _objectPoolLength ) {\n\n\t\t\t\tconst object = new RenderableObject();\n\t\t\t\t_objectPool.push( object );\n\t\t\t\t_objectPoolLength ++;\n\t\t\t\t_objectCount ++;\n\t\t\t\treturn object;\n\n\t\t\t}\n\n\t\t\treturn _objectPool[ _objectCount ++ ];\n\n\t\t}\n\n\t\tfunction getNextVertexInPool() {\n\n\t\t\tif ( _vertexCount === _vertexPoolLength ) {\n\n\t\t\t\tconst vertex = new RenderableVertex();\n\t\t\t\t_vertexPool.push( vertex );\n\t\t\t\t_vertexPoolLength ++;\n\t\t\t\t_vertexCount ++;\n\t\t\t\treturn vertex;\n\n\t\t\t}\n\n\t\t\treturn _vertexPool[ _vertexCount ++ ];\n\n\t\t}\n\n\t\tfunction getNextFaceInPool() {\n\n\t\t\tif ( _faceCount === _facePoolLength ) {\n\n\t\t\t\tconst face = new RenderableFace();\n\t\t\t\t_facePool.push( face );\n\t\t\t\t_facePoolLength ++;\n\t\t\t\t_faceCount ++;\n\t\t\t\treturn face;\n\n\t\t\t}\n\n\t\t\treturn _facePool[ _faceCount ++ ];\n\n\n\t\t}\n\n\t\tfunction getNextLineInPool() {\n\n\t\t\tif ( _lineCount === _linePoolLength ) {\n\n\t\t\t\tconst line = new RenderableLine();\n\t\t\t\t_linePool.push( line );\n\t\t\t\t_linePoolLength ++;\n\t\t\t\t_lineCount ++;\n\t\t\t\treturn line;\n\n\t\t\t}\n\n\t\t\treturn _linePool[ _lineCount ++ ];\n\n\t\t}\n\n\t\tfunction getNextSpriteInPool() {\n\n\t\t\tif ( _spriteCount === _spritePoolLength ) {\n\n\t\t\t\tconst sprite = new RenderableSprite();\n\t\t\t\t_spritePool.push( sprite );\n\t\t\t\t_spritePoolLength ++;\n\t\t\t\t_spriteCount ++;\n\t\t\t\treturn sprite;\n\n\t\t\t}\n\n\t\t\treturn _spritePool[ _spriteCount ++ ];\n\n\t\t}\n\n\t\t//\n\n\t\tfunction painterSort( a, b ) {\n\n\t\t\tif ( a.renderOrder !== b.renderOrder ) {\n\n\t\t\t\treturn a.renderOrder - b.renderOrder;\n\n\t\t\t} else if ( a.z !== b.z ) {\n\n\t\t\t\treturn b.z - a.z;\n\n\t\t\t} else if ( a.id !== b.id ) {\n\n\t\t\t\treturn a.id - b.id;\n\n\t\t\t} else {\n\n\t\t\t\treturn 0;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction clipLine( s1, s2 ) {\n\n\t\t\tlet alpha1 = 0, alpha2 = 1;\n\n\t\t\t// Calculate the boundary coordinate of each vertex for the near and far clip planes,\n\t\t\t// Z = -1 and Z = +1, respectively.\n\n\t\t\tconst bc1near = s1.z + s1.w,\n\t\t\t\tbc2near = s2.z + s2.w,\n\t\t\t\tbc1far = - s1.z + s1.w,\n\t\t\t\tbc2far = - s2.z + s2.w;\n\n\t\t\tif ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) {\n\n\t\t\t\t// Both vertices lie entirely within all clip planes.\n\t\t\t\treturn true;\n\n\t\t\t} else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) {\n\n\t\t\t\t// Both vertices lie entirely outside one of the clip planes.\n\t\t\t\treturn false;\n\n\t\t\t} else {\n\n\t\t\t\t// The line segment spans at least one clip plane.\n\n\t\t\t\tif ( bc1near < 0 ) {\n\n\t\t\t\t\t// v1 lies outside the near plane, v2 inside\n\t\t\t\t\talpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) );\n\n\t\t\t\t} else if ( bc2near < 0 ) {\n\n\t\t\t\t\t// v2 lies outside the near plane, v1 inside\n\t\t\t\t\talpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) );\n\n\t\t\t\t}\n\n\t\t\t\tif ( bc1far < 0 ) {\n\n\t\t\t\t\t// v1 lies outside the far plane, v2 inside\n\t\t\t\t\talpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) );\n\n\t\t\t\t} else if ( bc2far < 0 ) {\n\n\t\t\t\t\t// v2 lies outside the far plane, v2 inside\n\t\t\t\t\talpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) );\n\n\t\t\t\t}\n\n\t\t\t\tif ( alpha2 < alpha1 ) {\n\n\t\t\t\t\t// The line segment spans two boundaries, but is outside both of them.\n\t\t\t\t\t// (This can't happen when we're only clipping against just near/far but good\n\t\t\t\t\t//  to leave the check here for future usage if other clip planes are added.)\n\t\t\t\t\treturn false;\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// Update the s1 and s2 vertices to match the clipped line segment.\n\t\t\t\t\ts1.lerp( s2, alpha1 );\n\t\t\t\t\ts2.lerp( s1, 1 - alpha2 );\n\n\t\t\t\t\treturn true;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n\nexport { RenderableObject, RenderableFace, RenderableVertex, RenderableLine, RenderableSprite, Projector };\n"
  },
  {
    "path": "public/assets/lib/vendor/three/TextGeometry.js",
    "content": "/**\n * Text = 3D Text\n *\n * parameters = {\n *  font: <THREE.Font>, // font\n *\n *  size: <float>, // size of the text\n *  depth: <float>, // thickness to extrude text\n *  curveSegments: <int>, // number of points on the curves\n *\n *  bevelEnabled: <bool>, // turn on bevel\n *  bevelThickness: <float>, // how deep into text bevel goes\n *  bevelSize: <float>, // how far from text outline (including bevelOffset) is bevel\n *  bevelOffset: <float> // how far from text outline does bevel start\n * }\n */\n\nimport {\n\tExtrudeGeometry\n} from './three.module.js';\n\nclass TextGeometry extends ExtrudeGeometry {\n\n\tconstructor( text, parameters = {} ) {\n\n\t\tconst font = parameters.font;\n\n\t\tif ( font === undefined ) {\n\n\t\t\tsuper(); // generate default extrude geometry\n\n\t\t} else {\n\n\t\t\tconst shapes = font.generateShapes( text, parameters.size );\n\n\t\t\t// translate parameters to ExtrudeGeometry API\n\n\t\t\tif ( parameters.depth === undefined && parameters.height !== undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.TextGeometry: .height is now depreciated. Please use .depth instead' ); // @deprecated, r163\n\n\t\t\t}\n\n\t\t\tparameters.depth = parameters.depth !== undefined ?\n\t\t\t\tparameters.depth : parameters.height !== undefined ?\n\t\t\t\t\tparameters.height : 50;\n\n\t\t\t// defaults\n\n\t\t\tif ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;\n\t\t\tif ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;\n\t\t\tif ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;\n\n\t\t\tsuper( shapes, parameters );\n\n\t\t}\n\n\t\tthis.type = 'TextGeometry';\n\n\t}\n\n}\n\n\nexport { TextGeometry };\n"
  },
  {
    "path": "public/assets/lib/vendor/three/three.module.js",
    "content": "// @ts-nocheck\n/**\n * @license\n * Copyright 2010-2023 Three.js Authors\n * SPDX-License-Identifier: MIT\n */\nconst REVISION = '160';\n\nconst MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 };\nconst TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 };\nconst CullFaceNone = 0;\nconst CullFaceBack = 1;\nconst CullFaceFront = 2;\nconst CullFaceFrontBack = 3;\nconst BasicShadowMap = 0;\nconst PCFShadowMap = 1;\nconst PCFSoftShadowMap = 2;\nconst VSMShadowMap = 3;\nconst FrontSide = 0;\nconst BackSide = 1;\nconst DoubleSide = 2;\nconst TwoPassDoubleSide = 2; // r149\nconst NoBlending = 0;\nconst NormalBlending = 1;\nconst AdditiveBlending = 2;\nconst SubtractiveBlending = 3;\nconst MultiplyBlending = 4;\nconst CustomBlending = 5;\nconst AddEquation = 100;\nconst SubtractEquation = 101;\nconst ReverseSubtractEquation = 102;\nconst MinEquation = 103;\nconst MaxEquation = 104;\nconst ZeroFactor = 200;\nconst OneFactor = 201;\nconst SrcColorFactor = 202;\nconst OneMinusSrcColorFactor = 203;\nconst SrcAlphaFactor = 204;\nconst OneMinusSrcAlphaFactor = 205;\nconst DstAlphaFactor = 206;\nconst OneMinusDstAlphaFactor = 207;\nconst DstColorFactor = 208;\nconst OneMinusDstColorFactor = 209;\nconst SrcAlphaSaturateFactor = 210;\nconst ConstantColorFactor = 211;\nconst OneMinusConstantColorFactor = 212;\nconst ConstantAlphaFactor = 213;\nconst OneMinusConstantAlphaFactor = 214;\nconst NeverDepth = 0;\nconst AlwaysDepth = 1;\nconst LessDepth = 2;\nconst LessEqualDepth = 3;\nconst EqualDepth = 4;\nconst GreaterEqualDepth = 5;\nconst GreaterDepth = 6;\nconst NotEqualDepth = 7;\nconst MultiplyOperation = 0;\nconst MixOperation = 1;\nconst AddOperation = 2;\nconst NoToneMapping = 0;\nconst LinearToneMapping = 1;\nconst ReinhardToneMapping = 2;\nconst CineonToneMapping = 3;\nconst ACESFilmicToneMapping = 4;\nconst CustomToneMapping = 5;\nconst AgXToneMapping = 6;\nconst AttachedBindMode = 'attached';\nconst DetachedBindMode = 'detached';\n\nconst UVMapping = 300;\nconst CubeReflectionMapping = 301;\nconst CubeRefractionMapping = 302;\nconst EquirectangularReflectionMapping = 303;\nconst EquirectangularRefractionMapping = 304;\nconst CubeUVReflectionMapping = 306;\nconst RepeatWrapping = 1000;\nconst ClampToEdgeWrapping = 1001;\nconst MirroredRepeatWrapping = 1002;\nconst NearestFilter = 1003;\nconst NearestMipmapNearestFilter = 1004;\nconst NearestMipMapNearestFilter = 1004;\nconst NearestMipmapLinearFilter = 1005;\nconst NearestMipMapLinearFilter = 1005;\nconst LinearFilter = 1006;\nconst LinearMipmapNearestFilter = 1007;\nconst LinearMipMapNearestFilter = 1007;\nconst LinearMipmapLinearFilter = 1008;\nconst LinearMipMapLinearFilter = 1008;\nconst UnsignedByteType = 1009;\nconst ByteType = 1010;\nconst ShortType = 1011;\nconst UnsignedShortType = 1012;\nconst IntType = 1013;\nconst UnsignedIntType = 1014;\nconst FloatType = 1015;\nconst HalfFloatType = 1016;\nconst UnsignedShort4444Type = 1017;\nconst UnsignedShort5551Type = 1018;\nconst UnsignedInt248Type = 1020;\nconst AlphaFormat = 1021;\nconst RGBAFormat = 1023;\nconst LuminanceFormat = 1024;\nconst LuminanceAlphaFormat = 1025;\nconst DepthFormat = 1026;\nconst DepthStencilFormat = 1027;\nconst RedFormat = 1028;\nconst RedIntegerFormat = 1029;\nconst RGFormat = 1030;\nconst RGIntegerFormat = 1031;\nconst RGBAIntegerFormat = 1033;\n\nconst RGB_S3TC_DXT1_Format = 33776;\nconst RGBA_S3TC_DXT1_Format = 33777;\nconst RGBA_S3TC_DXT3_Format = 33778;\nconst RGBA_S3TC_DXT5_Format = 33779;\nconst RGB_PVRTC_4BPPV1_Format = 35840;\nconst RGB_PVRTC_2BPPV1_Format = 35841;\nconst RGBA_PVRTC_4BPPV1_Format = 35842;\nconst RGBA_PVRTC_2BPPV1_Format = 35843;\nconst RGB_ETC1_Format = 36196;\nconst RGB_ETC2_Format = 37492;\nconst RGBA_ETC2_EAC_Format = 37496;\nconst RGBA_ASTC_4x4_Format = 37808;\nconst RGBA_ASTC_5x4_Format = 37809;\nconst RGBA_ASTC_5x5_Format = 37810;\nconst RGBA_ASTC_6x5_Format = 37811;\nconst RGBA_ASTC_6x6_Format = 37812;\nconst RGBA_ASTC_8x5_Format = 37813;\nconst RGBA_ASTC_8x6_Format = 37814;\nconst RGBA_ASTC_8x8_Format = 37815;\nconst RGBA_ASTC_10x5_Format = 37816;\nconst RGBA_ASTC_10x6_Format = 37817;\nconst RGBA_ASTC_10x8_Format = 37818;\nconst RGBA_ASTC_10x10_Format = 37819;\nconst RGBA_ASTC_12x10_Format = 37820;\nconst RGBA_ASTC_12x12_Format = 37821;\nconst RGBA_BPTC_Format = 36492;\nconst RGB_BPTC_SIGNED_Format = 36494;\nconst RGB_BPTC_UNSIGNED_Format = 36495;\nconst RED_RGTC1_Format = 36283;\nconst SIGNED_RED_RGTC1_Format = 36284;\nconst RED_GREEN_RGTC2_Format = 36285;\nconst SIGNED_RED_GREEN_RGTC2_Format = 36286;\nconst LoopOnce = 2200;\nconst LoopRepeat = 2201;\nconst LoopPingPong = 2202;\nconst InterpolateDiscrete = 2300;\nconst InterpolateLinear = 2301;\nconst InterpolateSmooth = 2302;\nconst ZeroCurvatureEnding = 2400;\nconst ZeroSlopeEnding = 2401;\nconst WrapAroundEnding = 2402;\nconst NormalAnimationBlendMode = 2500;\nconst AdditiveAnimationBlendMode = 2501;\nconst TrianglesDrawMode = 0;\nconst TriangleStripDrawMode = 1;\nconst TriangleFanDrawMode = 2;\n/** @deprecated Use LinearSRGBColorSpace or NoColorSpace in three.js r152+. */\nconst LinearEncoding = 3000;\n/** @deprecated Use SRGBColorSpace in three.js r152+. */\nconst sRGBEncoding = 3001;\nconst BasicDepthPacking = 3200;\nconst RGBADepthPacking = 3201;\nconst TangentSpaceNormalMap = 0;\nconst ObjectSpaceNormalMap = 1;\n\n// Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available.\nconst NoColorSpace = '';\nconst SRGBColorSpace = 'srgb';\nconst LinearSRGBColorSpace = 'srgb-linear';\nconst DisplayP3ColorSpace = 'display-p3';\nconst LinearDisplayP3ColorSpace = 'display-p3-linear';\n\nconst LinearTransfer = 'linear';\nconst SRGBTransfer = 'srgb';\n\nconst Rec709Primaries = 'rec709';\nconst P3Primaries = 'p3';\n\nconst ZeroStencilOp = 0;\nconst KeepStencilOp = 7680;\nconst ReplaceStencilOp = 7681;\nconst IncrementStencilOp = 7682;\nconst DecrementStencilOp = 7683;\nconst IncrementWrapStencilOp = 34055;\nconst DecrementWrapStencilOp = 34056;\nconst InvertStencilOp = 5386;\n\nconst NeverStencilFunc = 512;\nconst LessStencilFunc = 513;\nconst EqualStencilFunc = 514;\nconst LessEqualStencilFunc = 515;\nconst GreaterStencilFunc = 516;\nconst NotEqualStencilFunc = 517;\nconst GreaterEqualStencilFunc = 518;\nconst AlwaysStencilFunc = 519;\n\nconst NeverCompare = 512;\nconst LessCompare = 513;\nconst EqualCompare = 514;\nconst LessEqualCompare = 515;\nconst GreaterCompare = 516;\nconst NotEqualCompare = 517;\nconst GreaterEqualCompare = 518;\nconst AlwaysCompare = 519;\n\nconst StaticDrawUsage = 35044;\nconst DynamicDrawUsage = 35048;\nconst StreamDrawUsage = 35040;\nconst StaticReadUsage = 35045;\nconst DynamicReadUsage = 35049;\nconst StreamReadUsage = 35041;\nconst StaticCopyUsage = 35046;\nconst DynamicCopyUsage = 35050;\nconst StreamCopyUsage = 35042;\n\nconst GLSL1 = '100';\nconst GLSL3 = '300 es';\n\nconst _SRGBAFormat = 1035; // fallback for WebGL 1\n\nconst WebGLCoordinateSystem = 2000;\nconst WebGPUCoordinateSystem = 2001;\n\n/**\n * https://github.com/mrdoob/eventdispatcher.js/\n */\n\nclass EventDispatcher {\n\n\taddEventListener( type, listener ) {\n\n\t\tif ( this._listeners === undefined ) this._listeners = {};\n\n\t\tconst listeners = this._listeners;\n\n\t\tif ( listeners[ type ] === undefined ) {\n\n\t\t\tlisteners[ type ] = [];\n\n\t\t}\n\n\t\tif ( listeners[ type ].indexOf( listener ) === - 1 ) {\n\n\t\t\tlisteners[ type ].push( listener );\n\n\t\t}\n\n\t}\n\n\thasEventListener( type, listener ) {\n\n\t\tif ( this._listeners === undefined ) return false;\n\n\t\tconst listeners = this._listeners;\n\n\t\treturn listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;\n\n\t}\n\n\tremoveEventListener( type, listener ) {\n\n\t\tif ( this._listeners === undefined ) return;\n\n\t\tconst listeners = this._listeners;\n\t\tconst listenerArray = listeners[ type ];\n\n\t\tif ( listenerArray !== undefined ) {\n\n\t\t\tconst index = listenerArray.indexOf( listener );\n\n\t\t\tif ( index !== - 1 ) {\n\n\t\t\t\tlistenerArray.splice( index, 1 );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tdispatchEvent( event ) {\n\n\t\tif ( this._listeners === undefined ) return;\n\n\t\tconst listeners = this._listeners;\n\t\tconst listenerArray = listeners[ event.type ];\n\n\t\tif ( listenerArray !== undefined ) {\n\n\t\t\tevent.target = this;\n\n\t\t\t// Make a copy, in case listeners are removed while iterating.\n\t\t\tconst array = listenerArray.slice( 0 );\n\n\t\t\tfor ( let i = 0, l = array.length; i < l; i ++ ) {\n\n\t\t\t\tarray[ i ].call( this, event );\n\n\t\t\t}\n\n\t\t\tevent.target = null;\n\n\t\t}\n\n\t}\n\n}\n\nconst _lut = [ '00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c', '0d', '0e', '0f', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e', '8f', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b', '9c', '9d', '9e', '9f', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc', 'dd', 'de', 'df', 'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff' ];\n\nlet _seed = 1234567;\n\n\nconst DEG2RAD = Math.PI / 180;\nconst RAD2DEG = 180 / Math.PI;\n\n// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136\nfunction generateUUID() {\n\n\tconst d0 = Math.random() * 0xffffffff | 0;\n\tconst d1 = Math.random() * 0xffffffff | 0;\n\tconst d2 = Math.random() * 0xffffffff | 0;\n\tconst d3 = Math.random() * 0xffffffff | 0;\n\tconst uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' +\n\t\t\t_lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' +\n\t\t\t_lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] +\n\t\t\t_lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ];\n\n\t// .toLowerCase() here flattens concatenated strings to save heap memory space.\n\treturn uuid.toLowerCase();\n\n}\n\nfunction clamp( value, min, max ) {\n\n\treturn Math.max( min, Math.min( max, value ) );\n\n}\n\n// compute euclidean modulo of m % n\n// https://en.wikipedia.org/wiki/Modulo_operation\nfunction euclideanModulo( n, m ) {\n\n\treturn ( ( n % m ) + m ) % m;\n\n}\n\n// Linear mapping from range <a1, a2> to range <b1, b2>\nfunction mapLinear( x, a1, a2, b1, b2 ) {\n\n\treturn b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );\n\n}\n\n// https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/\nfunction inverseLerp( x, y, value ) {\n\n\tif ( x !== y ) {\n\n\t\treturn ( value - x ) / ( y - x );\n\n\t} else {\n\n\t\treturn 0;\n\n\t}\n\n}\n\n// https://en.wikipedia.org/wiki/Linear_interpolation\nfunction lerp( x, y, t ) {\n\n\treturn ( 1 - t ) * x + t * y;\n\n}\n\n// http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/\nfunction damp( x, y, lambda, dt ) {\n\n\treturn lerp( x, y, 1 - Math.exp( - lambda * dt ) );\n\n}\n\n// https://www.desmos.com/calculator/vcsjnyz7x4\nfunction pingpong( x, length = 1 ) {\n\n\treturn length - Math.abs( euclideanModulo( x, length * 2 ) - length );\n\n}\n\n// http://en.wikipedia.org/wiki/Smoothstep\nfunction smoothstep( x, min, max ) {\n\n\tif ( x <= min ) return 0;\n\tif ( x >= max ) return 1;\n\n\tx = ( x - min ) / ( max - min );\n\n\treturn x * x * ( 3 - 2 * x );\n\n}\n\nfunction smootherstep( x, min, max ) {\n\n\tif ( x <= min ) return 0;\n\tif ( x >= max ) return 1;\n\n\tx = ( x - min ) / ( max - min );\n\n\treturn x * x * x * ( x * ( x * 6 - 15 ) + 10 );\n\n}\n\n// Random integer from <low, high> interval\nfunction randInt( low, high ) {\n\n\treturn low + Math.floor( Math.random() * ( high - low + 1 ) );\n\n}\n\n// Random float from <low, high> interval\nfunction randFloat( low, high ) {\n\n\treturn low + Math.random() * ( high - low );\n\n}\n\n// Random float from <-range/2, range/2> interval\nfunction randFloatSpread( range ) {\n\n\treturn range * ( 0.5 - Math.random() );\n\n}\n\n// Deterministic pseudo-random float in the interval [ 0, 1 ]\nfunction seededRandom( s ) {\n\n\tif ( s !== undefined ) _seed = s;\n\n\t// Mulberry32 generator\n\n\tlet t = _seed += 0x6D2B79F5;\n\n\tt = Math.imul( t ^ t >>> 15, t | 1 );\n\n\tt ^= t + Math.imul( t ^ t >>> 7, t | 61 );\n\n\treturn ( ( t ^ t >>> 14 ) >>> 0 ) / 4294967296;\n\n}\n\nfunction degToRad( degrees ) {\n\n\treturn degrees * DEG2RAD;\n\n}\n\nfunction radToDeg( radians ) {\n\n\treturn radians * RAD2DEG;\n\n}\n\nfunction isPowerOfTwo( value ) {\n\n\treturn ( value & ( value - 1 ) ) === 0 && value !== 0;\n\n}\n\nfunction ceilPowerOfTwo( value ) {\n\n\treturn Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );\n\n}\n\nfunction floorPowerOfTwo( value ) {\n\n\treturn Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );\n\n}\n\nfunction setQuaternionFromProperEuler( q, a, b, c, order ) {\n\n\t// Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles\n\n\t// rotations are applied to the axes in the order specified by 'order'\n\t// rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c'\n\t// angles are in radians\n\n\tconst cos = Math.cos;\n\tconst sin = Math.sin;\n\n\tconst c2 = cos( b / 2 );\n\tconst s2 = sin( b / 2 );\n\n\tconst c13 = cos( ( a + c ) / 2 );\n\tconst s13 = sin( ( a + c ) / 2 );\n\n\tconst c1_3 = cos( ( a - c ) / 2 );\n\tconst s1_3 = sin( ( a - c ) / 2 );\n\n\tconst c3_1 = cos( ( c - a ) / 2 );\n\tconst s3_1 = sin( ( c - a ) / 2 );\n\n\tswitch ( order ) {\n\n\t\tcase 'XYX':\n\t\t\tq.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 );\n\t\t\tbreak;\n\n\t\tcase 'YZY':\n\t\t\tq.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 );\n\t\t\tbreak;\n\n\t\tcase 'ZXZ':\n\t\t\tq.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 );\n\t\t\tbreak;\n\n\t\tcase 'XZX':\n\t\t\tq.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 );\n\t\t\tbreak;\n\n\t\tcase 'YXY':\n\t\t\tq.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 );\n\t\t\tbreak;\n\n\t\tcase 'ZYZ':\n\t\t\tq.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 );\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tconsole.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order );\n\n\t}\n\n}\n\nfunction denormalize( value, array ) {\n\n\tswitch ( array.constructor ) {\n\n\t\tcase Float32Array:\n\n\t\t\treturn value;\n\n\t\tcase Uint32Array:\n\n\t\t\treturn value / 4294967295.0;\n\n\t\tcase Uint16Array:\n\n\t\t\treturn value / 65535.0;\n\n\t\tcase Uint8Array:\n\n\t\t\treturn value / 255.0;\n\n\t\tcase Int32Array:\n\n\t\t\treturn Math.max( value / 2147483647.0, - 1.0 );\n\n\t\tcase Int16Array:\n\n\t\t\treturn Math.max( value / 32767.0, - 1.0 );\n\n\t\tcase Int8Array:\n\n\t\t\treturn Math.max( value / 127.0, - 1.0 );\n\n\t\tdefault:\n\n\t\t\tthrow new Error( 'Invalid component type.' );\n\n\t}\n\n}\n\nfunction normalize( value, array ) {\n\n\tswitch ( array.constructor ) {\n\n\t\tcase Float32Array:\n\n\t\t\treturn value;\n\n\t\tcase Uint32Array:\n\n\t\t\treturn Math.round( value * 4294967295.0 );\n\n\t\tcase Uint16Array:\n\n\t\t\treturn Math.round( value * 65535.0 );\n\n\t\tcase Uint8Array:\n\n\t\t\treturn Math.round( value * 255.0 );\n\n\t\tcase Int32Array:\n\n\t\t\treturn Math.round( value * 2147483647.0 );\n\n\t\tcase Int16Array:\n\n\t\t\treturn Math.round( value * 32767.0 );\n\n\t\tcase Int8Array:\n\n\t\t\treturn Math.round( value * 127.0 );\n\n\t\tdefault:\n\n\t\t\tthrow new Error( 'Invalid component type.' );\n\n\t}\n\n}\n\nconst MathUtils = {\n\tDEG2RAD: DEG2RAD,\n\tRAD2DEG: RAD2DEG,\n\tgenerateUUID: generateUUID,\n\tclamp: clamp,\n\teuclideanModulo: euclideanModulo,\n\tmapLinear: mapLinear,\n\tinverseLerp: inverseLerp,\n\tlerp: lerp,\n\tdamp: damp,\n\tpingpong: pingpong,\n\tsmoothstep: smoothstep,\n\tsmootherstep: smootherstep,\n\trandInt: randInt,\n\trandFloat: randFloat,\n\trandFloatSpread: randFloatSpread,\n\tseededRandom: seededRandom,\n\tdegToRad: degToRad,\n\tradToDeg: radToDeg,\n\tisPowerOfTwo: isPowerOfTwo,\n\tceilPowerOfTwo: ceilPowerOfTwo,\n\tfloorPowerOfTwo: floorPowerOfTwo,\n\tsetQuaternionFromProperEuler: setQuaternionFromProperEuler,\n\tnormalize: normalize,\n\tdenormalize: denormalize\n};\n\nclass Vector2 {\n\n\tconstructor( x = 0, y = 0 ) {\n\n\t\tVector2.prototype.isVector2 = true;\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\n\t}\n\n\tget width() {\n\n\t\treturn this.x;\n\n\t}\n\n\tset width( value ) {\n\n\t\tthis.x = value;\n\n\t}\n\n\tget height() {\n\n\t\treturn this.y;\n\n\t}\n\n\tset height( value ) {\n\n\t\tthis.y = value;\n\n\t}\n\n\tset( x, y ) {\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\n\t\treturn this;\n\n\t}\n\n\tsetScalar( scalar ) {\n\n\t\tthis.x = scalar;\n\t\tthis.y = scalar;\n\n\t\treturn this;\n\n\t}\n\n\tsetX( x ) {\n\n\t\tthis.x = x;\n\n\t\treturn this;\n\n\t}\n\n\tsetY( y ) {\n\n\t\tthis.y = y;\n\n\t\treturn this;\n\n\t}\n\n\tsetComponent( index, value ) {\n\n\t\tswitch ( index ) {\n\n\t\t\tcase 0: this.x = value; break;\n\t\t\tcase 1: this.y = value; break;\n\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tgetComponent( index ) {\n\n\t\tswitch ( index ) {\n\n\t\t\tcase 0: return this.x;\n\t\t\tcase 1: return this.y;\n\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t}\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this.x, this.y );\n\n\t}\n\n\tcopy( v ) {\n\n\t\tthis.x = v.x;\n\t\tthis.y = v.y;\n\n\t\treturn this;\n\n\t}\n\n\tadd( v ) {\n\n\t\tthis.x += v.x;\n\t\tthis.y += v.y;\n\n\t\treturn this;\n\n\t}\n\n\taddScalar( s ) {\n\n\t\tthis.x += s;\n\t\tthis.y += s;\n\n\t\treturn this;\n\n\t}\n\n\taddVectors( a, b ) {\n\n\t\tthis.x = a.x + b.x;\n\t\tthis.y = a.y + b.y;\n\n\t\treturn this;\n\n\t}\n\n\taddScaledVector( v, s ) {\n\n\t\tthis.x += v.x * s;\n\t\tthis.y += v.y * s;\n\n\t\treturn this;\n\n\t}\n\n\tsub( v ) {\n\n\t\tthis.x -= v.x;\n\t\tthis.y -= v.y;\n\n\t\treturn this;\n\n\t}\n\n\tsubScalar( s ) {\n\n\t\tthis.x -= s;\n\t\tthis.y -= s;\n\n\t\treturn this;\n\n\t}\n\n\tsubVectors( a, b ) {\n\n\t\tthis.x = a.x - b.x;\n\t\tthis.y = a.y - b.y;\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( v ) {\n\n\t\tthis.x *= v.x;\n\t\tthis.y *= v.y;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyScalar( scalar ) {\n\n\t\tthis.x *= scalar;\n\t\tthis.y *= scalar;\n\n\t\treturn this;\n\n\t}\n\n\tdivide( v ) {\n\n\t\tthis.x /= v.x;\n\t\tthis.y /= v.y;\n\n\t\treturn this;\n\n\t}\n\n\tdivideScalar( scalar ) {\n\n\t\treturn this.multiplyScalar( 1 / scalar );\n\n\t}\n\n\tapplyMatrix3( m ) {\n\n\t\tconst x = this.x, y = this.y;\n\t\tconst e = m.elements;\n\n\t\tthis.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ];\n\t\tthis.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ];\n\n\t\treturn this;\n\n\t}\n\n\tmin( v ) {\n\n\t\tthis.x = Math.min( this.x, v.x );\n\t\tthis.y = Math.min( this.y, v.y );\n\n\t\treturn this;\n\n\t}\n\n\tmax( v ) {\n\n\t\tthis.x = Math.max( this.x, v.x );\n\t\tthis.y = Math.max( this.y, v.y );\n\n\t\treturn this;\n\n\t}\n\n\tclamp( min, max ) {\n\n\t\t// assumes min < max, componentwise\n\n\t\tthis.x = Math.max( min.x, Math.min( max.x, this.x ) );\n\t\tthis.y = Math.max( min.y, Math.min( max.y, this.y ) );\n\n\t\treturn this;\n\n\t}\n\n\tclampScalar( minVal, maxVal ) {\n\n\t\tthis.x = Math.max( minVal, Math.min( maxVal, this.x ) );\n\t\tthis.y = Math.max( minVal, Math.min( maxVal, this.y ) );\n\n\t\treturn this;\n\n\t}\n\n\tclampLength( min, max ) {\n\n\t\tconst length = this.length();\n\n\t\treturn this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );\n\n\t}\n\n\tfloor() {\n\n\t\tthis.x = Math.floor( this.x );\n\t\tthis.y = Math.floor( this.y );\n\n\t\treturn this;\n\n\t}\n\n\tceil() {\n\n\t\tthis.x = Math.ceil( this.x );\n\t\tthis.y = Math.ceil( this.y );\n\n\t\treturn this;\n\n\t}\n\n\tround() {\n\n\t\tthis.x = Math.round( this.x );\n\t\tthis.y = Math.round( this.y );\n\n\t\treturn this;\n\n\t}\n\n\troundToZero() {\n\n\t\tthis.x = Math.trunc( this.x );\n\t\tthis.y = Math.trunc( this.y );\n\n\t\treturn this;\n\n\t}\n\n\tnegate() {\n\n\t\tthis.x = - this.x;\n\t\tthis.y = - this.y;\n\n\t\treturn this;\n\n\t}\n\n\tdot( v ) {\n\n\t\treturn this.x * v.x + this.y * v.y;\n\n\t}\n\n\tcross( v ) {\n\n\t\treturn this.x * v.y - this.y * v.x;\n\n\t}\n\n\tlengthSq() {\n\n\t\treturn this.x * this.x + this.y * this.y;\n\n\t}\n\n\tlength() {\n\n\t\treturn Math.sqrt( this.x * this.x + this.y * this.y );\n\n\t}\n\n\tmanhattanLength() {\n\n\t\treturn Math.abs( this.x ) + Math.abs( this.y );\n\n\t}\n\n\tnormalize() {\n\n\t\treturn this.divideScalar( this.length() || 1 );\n\n\t}\n\n\tangle() {\n\n\t\t// computes the angle in radians with respect to the positive x-axis\n\n\t\tconst angle = Math.atan2( - this.y, - this.x ) + Math.PI;\n\n\t\treturn angle;\n\n\t}\n\n\tangleTo( v ) {\n\n\t\tconst denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );\n\n\t\tif ( denominator === 0 ) return Math.PI / 2;\n\n\t\tconst theta = this.dot( v ) / denominator;\n\n\t\t// clamp, to handle numerical problems\n\n\t\treturn Math.acos( clamp( theta, - 1, 1 ) );\n\n\t}\n\n\tdistanceTo( v ) {\n\n\t\treturn Math.sqrt( this.distanceToSquared( v ) );\n\n\t}\n\n\tdistanceToSquared( v ) {\n\n\t\tconst dx = this.x - v.x, dy = this.y - v.y;\n\t\treturn dx * dx + dy * dy;\n\n\t}\n\n\tmanhattanDistanceTo( v ) {\n\n\t\treturn Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );\n\n\t}\n\n\tsetLength( length ) {\n\n\t\treturn this.normalize().multiplyScalar( length );\n\n\t}\n\n\tlerp( v, alpha ) {\n\n\t\tthis.x += ( v.x - this.x ) * alpha;\n\t\tthis.y += ( v.y - this.y ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tlerpVectors( v1, v2, alpha ) {\n\n\t\tthis.x = v1.x + ( v2.x - v1.x ) * alpha;\n\t\tthis.y = v1.y + ( v2.y - v1.y ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tequals( v ) {\n\n\t\treturn ( ( v.x === this.x ) && ( v.y === this.y ) );\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tthis.x = array[ offset ];\n\t\tthis.y = array[ offset + 1 ];\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tarray[ offset ] = this.x;\n\t\tarray[ offset + 1 ] = this.y;\n\n\t\treturn array;\n\n\t}\n\n\tfromBufferAttribute( attribute, index ) {\n\n\t\tthis.x = attribute.getX( index );\n\t\tthis.y = attribute.getY( index );\n\n\t\treturn this;\n\n\t}\n\n\trotateAround( center, angle ) {\n\n\t\tconst c = Math.cos( angle ), s = Math.sin( angle );\n\n\t\tconst x = this.x - center.x;\n\t\tconst y = this.y - center.y;\n\n\t\tthis.x = x * c - y * s + center.x;\n\t\tthis.y = x * s + y * c + center.y;\n\n\t\treturn this;\n\n\t}\n\n\trandom() {\n\n\t\tthis.x = Math.random();\n\t\tthis.y = Math.random();\n\n\t\treturn this;\n\n\t}\n\n\t*[ Symbol.iterator ]() {\n\n\t\tyield this.x;\n\t\tyield this.y;\n\n\t}\n\n}\n\nclass Matrix3 {\n\n\tconstructor( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {\n\n\t\tMatrix3.prototype.isMatrix3 = true;\n\n\t\tthis.elements = [\n\n\t\t\t1, 0, 0,\n\t\t\t0, 1, 0,\n\t\t\t0, 0, 1\n\n\t\t];\n\n\t\tif ( n11 !== undefined ) {\n\n\t\t\tthis.set( n11, n12, n13, n21, n22, n23, n31, n32, n33 );\n\n\t\t}\n\n\t}\n\n\tset( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {\n\n\t\tconst te = this.elements;\n\n\t\tte[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;\n\t\tte[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;\n\t\tte[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;\n\n\t\treturn this;\n\n\t}\n\n\tidentity() {\n\n\t\tthis.set(\n\n\t\t\t1, 0, 0,\n\t\t\t0, 1, 0,\n\t\t\t0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tcopy( m ) {\n\n\t\tconst te = this.elements;\n\t\tconst me = m.elements;\n\n\t\tte[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];\n\t\tte[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];\n\t\tte[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];\n\n\t\treturn this;\n\n\t}\n\n\textractBasis( xAxis, yAxis, zAxis ) {\n\n\t\txAxis.setFromMatrix3Column( this, 0 );\n\t\tyAxis.setFromMatrix3Column( this, 1 );\n\t\tzAxis.setFromMatrix3Column( this, 2 );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromMatrix4( m ) {\n\n\t\tconst me = m.elements;\n\n\t\tthis.set(\n\n\t\t\tme[ 0 ], me[ 4 ], me[ 8 ],\n\t\t\tme[ 1 ], me[ 5 ], me[ 9 ],\n\t\t\tme[ 2 ], me[ 6 ], me[ 10 ]\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( m ) {\n\n\t\treturn this.multiplyMatrices( this, m );\n\n\t}\n\n\tpremultiply( m ) {\n\n\t\treturn this.multiplyMatrices( m, this );\n\n\t}\n\n\tmultiplyMatrices( a, b ) {\n\n\t\tconst ae = a.elements;\n\t\tconst be = b.elements;\n\t\tconst te = this.elements;\n\n\t\tconst a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];\n\t\tconst a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];\n\t\tconst a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];\n\n\t\tconst b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];\n\t\tconst b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];\n\t\tconst b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];\n\n\t\tte[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;\n\t\tte[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;\n\t\tte[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;\n\n\t\tte[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;\n\t\tte[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;\n\t\tte[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;\n\n\t\tte[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;\n\t\tte[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;\n\t\tte[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyScalar( s ) {\n\n\t\tconst te = this.elements;\n\n\t\tte[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;\n\t\tte[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;\n\t\tte[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;\n\n\t\treturn this;\n\n\t}\n\n\tdeterminant() {\n\n\t\tconst te = this.elements;\n\n\t\tconst a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],\n\t\t\td = te[ 3 ], e = te[ 4 ], f = te[ 5 ],\n\t\t\tg = te[ 6 ], h = te[ 7 ], i = te[ 8 ];\n\n\t\treturn a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;\n\n\t}\n\n\tinvert() {\n\n\t\tconst te = this.elements,\n\n\t\t\tn11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ],\n\t\t\tn12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ],\n\t\t\tn13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ],\n\n\t\t\tt11 = n33 * n22 - n32 * n23,\n\t\t\tt12 = n32 * n13 - n33 * n12,\n\t\t\tt13 = n23 * n12 - n22 * n13,\n\n\t\t\tdet = n11 * t11 + n21 * t12 + n31 * t13;\n\n\t\tif ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 );\n\n\t\tconst detInv = 1 / det;\n\n\t\tte[ 0 ] = t11 * detInv;\n\t\tte[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;\n\t\tte[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;\n\n\t\tte[ 3 ] = t12 * detInv;\n\t\tte[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;\n\t\tte[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;\n\n\t\tte[ 6 ] = t13 * detInv;\n\t\tte[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;\n\t\tte[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;\n\n\t\treturn this;\n\n\t}\n\n\ttranspose() {\n\n\t\tlet tmp;\n\t\tconst m = this.elements;\n\n\t\ttmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;\n\t\ttmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;\n\t\ttmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;\n\n\t\treturn this;\n\n\t}\n\n\tgetNormalMatrix( matrix4 ) {\n\n\t\treturn this.setFromMatrix4( matrix4 ).invert().transpose();\n\n\t}\n\n\ttransposeIntoArray( r ) {\n\n\t\tconst m = this.elements;\n\n\t\tr[ 0 ] = m[ 0 ];\n\t\tr[ 1 ] = m[ 3 ];\n\t\tr[ 2 ] = m[ 6 ];\n\t\tr[ 3 ] = m[ 1 ];\n\t\tr[ 4 ] = m[ 4 ];\n\t\tr[ 5 ] = m[ 7 ];\n\t\tr[ 6 ] = m[ 2 ];\n\t\tr[ 7 ] = m[ 5 ];\n\t\tr[ 8 ] = m[ 8 ];\n\n\t\treturn this;\n\n\t}\n\n\tsetUvTransform( tx, ty, sx, sy, rotation, cx, cy ) {\n\n\t\tconst c = Math.cos( rotation );\n\t\tconst s = Math.sin( rotation );\n\n\t\tthis.set(\n\t\t\tsx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,\n\t\t\t- sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,\n\t\t\t0, 0, 1\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\t//\n\n\tscale( sx, sy ) {\n\n\t\tthis.premultiply( _m3.makeScale( sx, sy ) );\n\n\t\treturn this;\n\n\t}\n\n\trotate( theta ) {\n\n\t\tthis.premultiply( _m3.makeRotation( - theta ) );\n\n\t\treturn this;\n\n\t}\n\n\ttranslate( tx, ty ) {\n\n\t\tthis.premultiply( _m3.makeTranslation( tx, ty ) );\n\n\t\treturn this;\n\n\t}\n\n\t// for 2D Transforms\n\n\tmakeTranslation( x, y ) {\n\n\t\tif ( x.isVector2 ) {\n\n\t\t\tthis.set(\n\n\t\t\t\t1, 0, x.x,\n\t\t\t\t0, 1, x.y,\n\t\t\t\t0, 0, 1\n\n\t\t\t);\n\n\t\t} else {\n\n\t\t\tthis.set(\n\n\t\t\t\t1, 0, x,\n\t\t\t\t0, 1, y,\n\t\t\t\t0, 0, 1\n\n\t\t\t);\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotation( theta ) {\n\n\t\t// counterclockwise\n\n\t\tconst c = Math.cos( theta );\n\t\tconst s = Math.sin( theta );\n\n\t\tthis.set(\n\n\t\t\tc, - s, 0,\n\t\t\ts, c, 0,\n\t\t\t0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmakeScale( x, y ) {\n\n\t\tthis.set(\n\n\t\t\tx, 0, 0,\n\t\t\t0, y, 0,\n\t\t\t0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\t//\n\n\tequals( matrix ) {\n\n\t\tconst te = this.elements;\n\t\tconst me = matrix.elements;\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tif ( te[ i ] !== me[ i ] ) return false;\n\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tthis.elements[ i ] = array[ i + offset ];\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tconst te = this.elements;\n\n\t\tarray[ offset ] = te[ 0 ];\n\t\tarray[ offset + 1 ] = te[ 1 ];\n\t\tarray[ offset + 2 ] = te[ 2 ];\n\n\t\tarray[ offset + 3 ] = te[ 3 ];\n\t\tarray[ offset + 4 ] = te[ 4 ];\n\t\tarray[ offset + 5 ] = te[ 5 ];\n\n\t\tarray[ offset + 6 ] = te[ 6 ];\n\t\tarray[ offset + 7 ] = te[ 7 ];\n\t\tarray[ offset + 8 ] = te[ 8 ];\n\n\t\treturn array;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().fromArray( this.elements );\n\n\t}\n\n}\n\nconst _m3 = /*@__PURE__*/ new Matrix3();\n\nfunction arrayNeedsUint32( array ) {\n\n\t// assumes larger values usually on last\n\n\tfor ( let i = array.length - 1; i >= 0; -- i ) {\n\n\t\tif ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565\n\n\t}\n\n\treturn false;\n\n}\n\nconst TYPED_ARRAYS = {\n\tInt8Array: Int8Array,\n\tUint8Array: Uint8Array,\n\tUint8ClampedArray: Uint8ClampedArray,\n\tInt16Array: Int16Array,\n\tUint16Array: Uint16Array,\n\tInt32Array: Int32Array,\n\tUint32Array: Uint32Array,\n\tFloat32Array: Float32Array,\n\tFloat64Array: Float64Array\n};\n\nfunction getTypedArray( type, buffer ) {\n\n\treturn new TYPED_ARRAYS[ type ]( buffer );\n\n}\n\nfunction createElementNS( name ) {\n\n\treturn document.createElementNS( 'http://www.w3.org/1999/xhtml', name );\n\n}\n\nfunction createCanvasElement() {\n\n\tconst canvas = createElementNS( 'canvas' );\n\tcanvas.style.display = 'block';\n\treturn canvas;\n\n}\n\nconst _cache = {};\n\nfunction warnOnce( message ) {\n\n\tif ( message in _cache ) return;\n\n\t_cache[ message ] = true;\n\n\tconsole.warn( message );\n\n}\n\n/**\n * Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping\n * or clipping. Based on W3C specifications for sRGB and Display P3,\n * and ICC specifications for the D50 connection space. Values in/out\n * are _linear_ sRGB and _linear_ Display P3.\n *\n * Note that both sRGB and Display P3 use the sRGB transfer functions.\n *\n * Reference:\n * - http://www.russellcottrell.com/photo/matrixCalculator.htm\n */\n\nconst LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/ new Matrix3().set(\n\t0.8224621, 0.177538, 0.0,\n\t0.0331941, 0.9668058, 0.0,\n\t0.0170827, 0.0723974, 0.9105199,\n);\n\nconst LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = /*@__PURE__*/ new Matrix3().set(\n\t1.2249401, - 0.2249404, 0.0,\n\t- 0.0420569, 1.0420571, 0.0,\n\t- 0.0196376, - 0.0786361, 1.0982735\n);\n\n/**\n * Defines supported color spaces by transfer function and primaries,\n * and provides conversions to/from the Linear-sRGB reference space.\n */\nconst COLOR_SPACES = {\n\t[ LinearSRGBColorSpace ]: {\n\t\ttransfer: LinearTransfer,\n\t\tprimaries: Rec709Primaries,\n\t\ttoReference: ( color ) => color,\n\t\tfromReference: ( color ) => color,\n\t},\n\t[ SRGBColorSpace ]: {\n\t\ttransfer: SRGBTransfer,\n\t\tprimaries: Rec709Primaries,\n\t\ttoReference: ( color ) => color.convertSRGBToLinear(),\n\t\tfromReference: ( color ) => color.convertLinearToSRGB(),\n\t},\n\t[ LinearDisplayP3ColorSpace ]: {\n\t\ttransfer: LinearTransfer,\n\t\tprimaries: P3Primaries,\n\t\ttoReference: ( color ) => color.applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ),\n\t\tfromReference: ( color ) => color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ),\n\t},\n\t[ DisplayP3ColorSpace ]: {\n\t\ttransfer: SRGBTransfer,\n\t\tprimaries: P3Primaries,\n\t\ttoReference: ( color ) => color.convertSRGBToLinear().applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ),\n\t\tfromReference: ( color ) => color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ).convertLinearToSRGB(),\n\t},\n};\n\nconst SUPPORTED_WORKING_COLOR_SPACES = new Set( [ LinearSRGBColorSpace, LinearDisplayP3ColorSpace ] );\n\nconst ColorManagement = {\n\n\tenabled: true,\n\n\t_workingColorSpace: LinearSRGBColorSpace,\n\n\tget workingColorSpace() {\n\n\t\treturn this._workingColorSpace;\n\n\t},\n\n\tset workingColorSpace( colorSpace ) {\n\n\t\tif ( ! SUPPORTED_WORKING_COLOR_SPACES.has( colorSpace ) ) {\n\n\t\t\tthrow new Error( `Unsupported working color space, \"${ colorSpace }\".` );\n\n\t\t}\n\n\t\tthis._workingColorSpace = colorSpace;\n\n\t},\n\n\tconvert: function ( color, sourceColorSpace, targetColorSpace ) {\n\n\t\tif ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) {\n\n\t\t\treturn color;\n\n\t\t}\n\n\t\tconst sourceToReference = COLOR_SPACES[ sourceColorSpace ].toReference;\n\t\tconst targetFromReference = COLOR_SPACES[ targetColorSpace ].fromReference;\n\n\t\treturn targetFromReference( sourceToReference( color ) );\n\n\t},\n\n\tfromWorkingColorSpace: function ( color, targetColorSpace ) {\n\n\t\treturn this.convert( color, this._workingColorSpace, targetColorSpace );\n\n\t},\n\n\ttoWorkingColorSpace: function ( color, sourceColorSpace ) {\n\n\t\treturn this.convert( color, sourceColorSpace, this._workingColorSpace );\n\n\t},\n\n\tgetPrimaries: function ( colorSpace ) {\n\n\t\treturn COLOR_SPACES[ colorSpace ].primaries;\n\n\t},\n\n\tgetTransfer: function ( colorSpace ) {\n\n\t\tif ( colorSpace === NoColorSpace ) return LinearTransfer;\n\n\t\treturn COLOR_SPACES[ colorSpace ].transfer;\n\n\t},\n\n};\n\n\nfunction SRGBToLinear( c ) {\n\n\treturn ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 );\n\n}\n\nfunction LinearToSRGB( c ) {\n\n\treturn ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055;\n\n}\n\nlet _canvas;\n\nclass ImageUtils {\n\n\tstatic getDataURL( image ) {\n\n\t\tif ( /^data:/i.test( image.src ) ) {\n\n\t\t\treturn image.src;\n\n\t\t}\n\n\t\tif ( typeof HTMLCanvasElement === 'undefined' ) {\n\n\t\t\treturn image.src;\n\n\t\t}\n\n\t\tlet canvas;\n\n\t\tif ( image instanceof HTMLCanvasElement ) {\n\n\t\t\tcanvas = image;\n\n\t\t} else {\n\n\t\t\tif ( _canvas === undefined ) _canvas = createElementNS( 'canvas' );\n\n\t\t\t_canvas.width = image.width;\n\t\t\t_canvas.height = image.height;\n\n\t\t\tconst context = _canvas.getContext( '2d' );\n\n\t\t\tif ( image instanceof ImageData ) {\n\n\t\t\t\tcontext.putImageData( image, 0, 0 );\n\n\t\t\t} else {\n\n\t\t\t\tcontext.drawImage( image, 0, 0, image.width, image.height );\n\n\t\t\t}\n\n\t\t\tcanvas = _canvas;\n\n\t\t}\n\n\t\tif ( canvas.width > 2048 || canvas.height > 2048 ) {\n\n\t\t\tconsole.warn( 'THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons', image );\n\n\t\t\treturn canvas.toDataURL( 'image/jpeg', 0.6 );\n\n\t\t} else {\n\n\t\t\treturn canvas.toDataURL( 'image/png' );\n\n\t\t}\n\n\t}\n\n\tstatic sRGBToLinear( image ) {\n\n\t\tif ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||\n\t\t\t( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||\n\t\t\t( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {\n\n\t\t\tconst canvas = createElementNS( 'canvas' );\n\n\t\t\tcanvas.width = image.width;\n\t\t\tcanvas.height = image.height;\n\n\t\t\tconst context = canvas.getContext( '2d' );\n\t\t\tcontext.drawImage( image, 0, 0, image.width, image.height );\n\n\t\t\tconst imageData = context.getImageData( 0, 0, image.width, image.height );\n\t\t\tconst data = imageData.data;\n\n\t\t\tfor ( let i = 0; i < data.length; i ++ ) {\n\n\t\t\t\tdata[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255;\n\n\t\t\t}\n\n\t\t\tcontext.putImageData( imageData, 0, 0 );\n\n\t\t\treturn canvas;\n\n\t\t} else if ( image.data ) {\n\n\t\t\tconst data = image.data.slice( 0 );\n\n\t\t\tfor ( let i = 0; i < data.length; i ++ ) {\n\n\t\t\t\tif ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) {\n\n\t\t\t\t\tdata[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// assuming float\n\n\t\t\t\t\tdata[ i ] = SRGBToLinear( data[ i ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tdata: data,\n\t\t\t\twidth: image.width,\n\t\t\t\theight: image.height\n\t\t\t};\n\n\t\t} else {\n\n\t\t\tconsole.warn( 'THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' );\n\t\t\treturn image;\n\n\t\t}\n\n\t}\n\n}\n\nlet _sourceId = 0;\n\nclass Source {\n\n\tconstructor( data = null ) {\n\n\t\tthis.isSource = true;\n\n\t\tObject.defineProperty( this, 'id', { value: _sourceId ++ } );\n\n\t\tthis.uuid = generateUUID();\n\n\t\tthis.data = data;\n\n\t\tthis.version = 0;\n\n\t}\n\n\tset needsUpdate( value ) {\n\n\t\tif ( value === true ) this.version ++;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst isRootObject = ( meta === undefined || typeof meta === 'string' );\n\n\t\tif ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) {\n\n\t\t\treturn meta.images[ this.uuid ];\n\n\t\t}\n\n\t\tconst output = {\n\t\t\tuuid: this.uuid,\n\t\t\turl: ''\n\t\t};\n\n\t\tconst data = this.data;\n\n\t\tif ( data !== null ) {\n\n\t\t\tlet url;\n\n\t\t\tif ( Array.isArray( data ) ) {\n\n\t\t\t\t// cube texture\n\n\t\t\t\turl = [];\n\n\t\t\t\tfor ( let i = 0, l = data.length; i < l; i ++ ) {\n\n\t\t\t\t\tif ( data[ i ].isDataTexture ) {\n\n\t\t\t\t\t\turl.push( serializeImage( data[ i ].image ) );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\turl.push( serializeImage( data[ i ] ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// texture\n\n\t\t\t\turl = serializeImage( data );\n\n\t\t\t}\n\n\t\t\toutput.url = url;\n\n\t\t}\n\n\t\tif ( ! isRootObject ) {\n\n\t\t\tmeta.images[ this.uuid ] = output;\n\n\t\t}\n\n\t\treturn output;\n\n\t}\n\n}\n\nfunction serializeImage( image ) {\n\n\tif ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||\n\t\t( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||\n\t\t( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {\n\n\t\t// default images\n\n\t\treturn ImageUtils.getDataURL( image );\n\n\t} else {\n\n\t\tif ( image.data ) {\n\n\t\t\t// images of DataTexture\n\n\t\t\treturn {\n\t\t\t\tdata: Array.from( image.data ),\n\t\t\t\twidth: image.width,\n\t\t\t\theight: image.height,\n\t\t\t\ttype: image.data.constructor.name\n\t\t\t};\n\n\t\t} else {\n\n\t\t\tconsole.warn( 'THREE.Texture: Unable to serialize Texture.' );\n\t\t\treturn {};\n\n\t\t}\n\n\t}\n\n}\n\nlet _textureId = 0;\n\nclass Texture extends EventDispatcher {\n\n\tconstructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace ) {\n\n\t\tsuper();\n\n\t\tthis.isTexture = true;\n\n\t\tObject.defineProperty( this, 'id', { value: _textureId ++ } );\n\n\t\tthis.uuid = generateUUID();\n\n\t\tthis.name = '';\n\n\t\tthis.source = new Source( image );\n\t\tthis.mipmaps = [];\n\n\t\tthis.mapping = mapping;\n\t\tthis.channel = 0;\n\n\t\tthis.wrapS = wrapS;\n\t\tthis.wrapT = wrapT;\n\n\t\tthis.magFilter = magFilter;\n\t\tthis.minFilter = minFilter;\n\n\t\tthis.anisotropy = anisotropy;\n\n\t\tthis.format = format;\n\t\tthis.internalFormat = null;\n\t\tthis.type = type;\n\n\t\tthis.offset = new Vector2( 0, 0 );\n\t\tthis.repeat = new Vector2( 1, 1 );\n\t\tthis.center = new Vector2( 0, 0 );\n\t\tthis.rotation = 0;\n\n\t\tthis.matrixAutoUpdate = true;\n\t\tthis.matrix = new Matrix3();\n\n\t\tthis.generateMipmaps = true;\n\t\tthis.premultiplyAlpha = false;\n\t\tthis.flipY = true;\n\t\tthis.unpackAlignment = 4;\t// valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)\n\n\t\tif ( typeof colorSpace === 'string' ) {\n\n\t\t\tthis.colorSpace = colorSpace;\n\n\t\t} else { // @deprecated, r152\n\n\t\t\twarnOnce( 'THREE.Texture: Property .encoding has been replaced by .colorSpace.' );\n\t\t\tthis.colorSpace = colorSpace === sRGBEncoding ? SRGBColorSpace : NoColorSpace;\n\n\t\t}\n\n\n\t\tthis.userData = {};\n\n\t\tthis.version = 0;\n\t\tthis.onUpdate = null;\n\n\t\tthis.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not\n\t\tthis.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures)\n\n\t}\n\n\tget image() {\n\n\t\treturn this.source.data;\n\n\t}\n\n\tset image( value = null ) {\n\n\t\tthis.source.data = value;\n\n\t}\n\n\tupdateMatrix() {\n\n\t\tthis.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.name = source.name;\n\n\t\tthis.source = source.source;\n\t\tthis.mipmaps = source.mipmaps.slice( 0 );\n\n\t\tthis.mapping = source.mapping;\n\t\tthis.channel = source.channel;\n\n\t\tthis.wrapS = source.wrapS;\n\t\tthis.wrapT = source.wrapT;\n\n\t\tthis.magFilter = source.magFilter;\n\t\tthis.minFilter = source.minFilter;\n\n\t\tthis.anisotropy = source.anisotropy;\n\n\t\tthis.format = source.format;\n\t\tthis.internalFormat = source.internalFormat;\n\t\tthis.type = source.type;\n\n\t\tthis.offset.copy( source.offset );\n\t\tthis.repeat.copy( source.repeat );\n\t\tthis.center.copy( source.center );\n\t\tthis.rotation = source.rotation;\n\n\t\tthis.matrixAutoUpdate = source.matrixAutoUpdate;\n\t\tthis.matrix.copy( source.matrix );\n\n\t\tthis.generateMipmaps = source.generateMipmaps;\n\t\tthis.premultiplyAlpha = source.premultiplyAlpha;\n\t\tthis.flipY = source.flipY;\n\t\tthis.unpackAlignment = source.unpackAlignment;\n\t\tthis.colorSpace = source.colorSpace;\n\n\t\tthis.userData = JSON.parse( JSON.stringify( source.userData ) );\n\n\t\tthis.needsUpdate = true;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst isRootObject = ( meta === undefined || typeof meta === 'string' );\n\n\t\tif ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) {\n\n\t\t\treturn meta.textures[ this.uuid ];\n\n\t\t}\n\n\t\tconst output = {\n\n\t\t\tmetadata: {\n\t\t\t\tversion: 4.6,\n\t\t\t\ttype: 'Texture',\n\t\t\t\tgenerator: 'Texture.toJSON'\n\t\t\t},\n\n\t\t\tuuid: this.uuid,\n\t\t\tname: this.name,\n\n\t\t\timage: this.source.toJSON( meta ).uuid,\n\n\t\t\tmapping: this.mapping,\n\t\t\tchannel: this.channel,\n\n\t\t\trepeat: [ this.repeat.x, this.repeat.y ],\n\t\t\toffset: [ this.offset.x, this.offset.y ],\n\t\t\tcenter: [ this.center.x, this.center.y ],\n\t\t\trotation: this.rotation,\n\n\t\t\twrap: [ this.wrapS, this.wrapT ],\n\n\t\t\tformat: this.format,\n\t\t\tinternalFormat: this.internalFormat,\n\t\t\ttype: this.type,\n\t\t\tcolorSpace: this.colorSpace,\n\n\t\t\tminFilter: this.minFilter,\n\t\t\tmagFilter: this.magFilter,\n\t\t\tanisotropy: this.anisotropy,\n\n\t\t\tflipY: this.flipY,\n\n\t\t\tgenerateMipmaps: this.generateMipmaps,\n\t\t\tpremultiplyAlpha: this.premultiplyAlpha,\n\t\t\tunpackAlignment: this.unpackAlignment\n\n\t\t};\n\n\t\tif ( Object.keys( this.userData ).length > 0 ) output.userData = this.userData;\n\n\t\tif ( ! isRootObject ) {\n\n\t\t\tmeta.textures[ this.uuid ] = output;\n\n\t\t}\n\n\t\treturn output;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t}\n\n\ttransformUv( uv ) {\n\n\t\tif ( this.mapping !== UVMapping ) return uv;\n\n\t\tuv.applyMatrix3( this.matrix );\n\n\t\tif ( uv.x < 0 || uv.x > 1 ) {\n\n\t\t\tswitch ( this.wrapS ) {\n\n\t\t\t\tcase RepeatWrapping:\n\n\t\t\t\t\tuv.x = uv.x - Math.floor( uv.x );\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase ClampToEdgeWrapping:\n\n\t\t\t\t\tuv.x = uv.x < 0 ? 0 : 1;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase MirroredRepeatWrapping:\n\n\t\t\t\t\tif ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {\n\n\t\t\t\t\t\tuv.x = Math.ceil( uv.x ) - uv.x;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tuv.x = uv.x - Math.floor( uv.x );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( uv.y < 0 || uv.y > 1 ) {\n\n\t\t\tswitch ( this.wrapT ) {\n\n\t\t\t\tcase RepeatWrapping:\n\n\t\t\t\t\tuv.y = uv.y - Math.floor( uv.y );\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase ClampToEdgeWrapping:\n\n\t\t\t\t\tuv.y = uv.y < 0 ? 0 : 1;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase MirroredRepeatWrapping:\n\n\t\t\t\t\tif ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {\n\n\t\t\t\t\t\tuv.y = Math.ceil( uv.y ) - uv.y;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tuv.y = uv.y - Math.floor( uv.y );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( this.flipY ) {\n\n\t\t\tuv.y = 1 - uv.y;\n\n\t\t}\n\n\t\treturn uv;\n\n\t}\n\n\tset needsUpdate( value ) {\n\n\t\tif ( value === true ) {\n\n\t\t\tthis.version ++;\n\t\t\tthis.source.needsUpdate = true;\n\n\t\t}\n\n\t}\n\n\tget encoding() { // @deprecated, r152\n\n\t\twarnOnce( 'THREE.Texture: Property .encoding has been replaced by .colorSpace.' );\n\t\treturn this.colorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding;\n\n\t}\n\n\tset encoding( encoding ) { // @deprecated, r152\n\n\t\twarnOnce( 'THREE.Texture: Property .encoding has been replaced by .colorSpace.' );\n\t\tthis.colorSpace = encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace;\n\n\t}\n\n}\n\nTexture.DEFAULT_IMAGE = null;\nTexture.DEFAULT_MAPPING = UVMapping;\nTexture.DEFAULT_ANISOTROPY = 1;\n\nclass Vector4 {\n\n\tconstructor( x = 0, y = 0, z = 0, w = 1 ) {\n\n\t\tVector4.prototype.isVector4 = true;\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\tthis.z = z;\n\t\tthis.w = w;\n\n\t}\n\n\tget width() {\n\n\t\treturn this.z;\n\n\t}\n\n\tset width( value ) {\n\n\t\tthis.z = value;\n\n\t}\n\n\tget height() {\n\n\t\treturn this.w;\n\n\t}\n\n\tset height( value ) {\n\n\t\tthis.w = value;\n\n\t}\n\n\tset( x, y, z, w ) {\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\tthis.z = z;\n\t\tthis.w = w;\n\n\t\treturn this;\n\n\t}\n\n\tsetScalar( scalar ) {\n\n\t\tthis.x = scalar;\n\t\tthis.y = scalar;\n\t\tthis.z = scalar;\n\t\tthis.w = scalar;\n\n\t\treturn this;\n\n\t}\n\n\tsetX( x ) {\n\n\t\tthis.x = x;\n\n\t\treturn this;\n\n\t}\n\n\tsetY( y ) {\n\n\t\tthis.y = y;\n\n\t\treturn this;\n\n\t}\n\n\tsetZ( z ) {\n\n\t\tthis.z = z;\n\n\t\treturn this;\n\n\t}\n\n\tsetW( w ) {\n\n\t\tthis.w = w;\n\n\t\treturn this;\n\n\t}\n\n\tsetComponent( index, value ) {\n\n\t\tswitch ( index ) {\n\n\t\t\tcase 0: this.x = value; break;\n\t\t\tcase 1: this.y = value; break;\n\t\t\tcase 2: this.z = value; break;\n\t\t\tcase 3: this.w = value; break;\n\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tgetComponent( index ) {\n\n\t\tswitch ( index ) {\n\n\t\t\tcase 0: return this.x;\n\t\t\tcase 1: return this.y;\n\t\t\tcase 2: return this.z;\n\t\t\tcase 3: return this.w;\n\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t}\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this.x, this.y, this.z, this.w );\n\n\t}\n\n\tcopy( v ) {\n\n\t\tthis.x = v.x;\n\t\tthis.y = v.y;\n\t\tthis.z = v.z;\n\t\tthis.w = ( v.w !== undefined ) ? v.w : 1;\n\n\t\treturn this;\n\n\t}\n\n\tadd( v ) {\n\n\t\tthis.x += v.x;\n\t\tthis.y += v.y;\n\t\tthis.z += v.z;\n\t\tthis.w += v.w;\n\n\t\treturn this;\n\n\t}\n\n\taddScalar( s ) {\n\n\t\tthis.x += s;\n\t\tthis.y += s;\n\t\tthis.z += s;\n\t\tthis.w += s;\n\n\t\treturn this;\n\n\t}\n\n\taddVectors( a, b ) {\n\n\t\tthis.x = a.x + b.x;\n\t\tthis.y = a.y + b.y;\n\t\tthis.z = a.z + b.z;\n\t\tthis.w = a.w + b.w;\n\n\t\treturn this;\n\n\t}\n\n\taddScaledVector( v, s ) {\n\n\t\tthis.x += v.x * s;\n\t\tthis.y += v.y * s;\n\t\tthis.z += v.z * s;\n\t\tthis.w += v.w * s;\n\n\t\treturn this;\n\n\t}\n\n\tsub( v ) {\n\n\t\tthis.x -= v.x;\n\t\tthis.y -= v.y;\n\t\tthis.z -= v.z;\n\t\tthis.w -= v.w;\n\n\t\treturn this;\n\n\t}\n\n\tsubScalar( s ) {\n\n\t\tthis.x -= s;\n\t\tthis.y -= s;\n\t\tthis.z -= s;\n\t\tthis.w -= s;\n\n\t\treturn this;\n\n\t}\n\n\tsubVectors( a, b ) {\n\n\t\tthis.x = a.x - b.x;\n\t\tthis.y = a.y - b.y;\n\t\tthis.z = a.z - b.z;\n\t\tthis.w = a.w - b.w;\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( v ) {\n\n\t\tthis.x *= v.x;\n\t\tthis.y *= v.y;\n\t\tthis.z *= v.z;\n\t\tthis.w *= v.w;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyScalar( scalar ) {\n\n\t\tthis.x *= scalar;\n\t\tthis.y *= scalar;\n\t\tthis.z *= scalar;\n\t\tthis.w *= scalar;\n\n\t\treturn this;\n\n\t}\n\n\tapplyMatrix4( m ) {\n\n\t\tconst x = this.x, y = this.y, z = this.z, w = this.w;\n\t\tconst e = m.elements;\n\n\t\tthis.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;\n\t\tthis.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;\n\t\tthis.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;\n\t\tthis.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;\n\n\t\treturn this;\n\n\t}\n\n\tdivideScalar( scalar ) {\n\n\t\treturn this.multiplyScalar( 1 / scalar );\n\n\t}\n\n\tsetAxisAngleFromQuaternion( q ) {\n\n\t\t// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm\n\n\t\t// q is assumed to be normalized\n\n\t\tthis.w = 2 * Math.acos( q.w );\n\n\t\tconst s = Math.sqrt( 1 - q.w * q.w );\n\n\t\tif ( s < 0.0001 ) {\n\n\t\t\tthis.x = 1;\n\t\t\tthis.y = 0;\n\t\t\tthis.z = 0;\n\n\t\t} else {\n\n\t\t\tthis.x = q.x / s;\n\t\t\tthis.y = q.y / s;\n\t\t\tthis.z = q.z / s;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetAxisAngleFromRotationMatrix( m ) {\n\n\t\t// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm\n\n\t\t// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n\t\tlet angle, x, y, z; // variables for result\n\t\tconst epsilon = 0.01,\t\t// margin to allow for rounding errors\n\t\t\tepsilon2 = 0.1,\t\t// margin to distinguish between 0 and 180 degrees\n\n\t\t\tte = m.elements,\n\n\t\t\tm11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],\n\t\t\tm21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],\n\t\t\tm31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];\n\n\t\tif ( ( Math.abs( m12 - m21 ) < epsilon ) &&\n\t\t     ( Math.abs( m13 - m31 ) < epsilon ) &&\n\t\t     ( Math.abs( m23 - m32 ) < epsilon ) ) {\n\n\t\t\t// singularity found\n\t\t\t// first check for identity matrix which must have +1 for all terms\n\t\t\t// in leading diagonal and zero in other terms\n\n\t\t\tif ( ( Math.abs( m12 + m21 ) < epsilon2 ) &&\n\t\t\t     ( Math.abs( m13 + m31 ) < epsilon2 ) &&\n\t\t\t     ( Math.abs( m23 + m32 ) < epsilon2 ) &&\n\t\t\t     ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {\n\n\t\t\t\t// this singularity is identity matrix so angle = 0\n\n\t\t\t\tthis.set( 1, 0, 0, 0 );\n\n\t\t\t\treturn this; // zero angle, arbitrary axis\n\n\t\t\t}\n\n\t\t\t// otherwise this singularity is angle = 180\n\n\t\t\tangle = Math.PI;\n\n\t\t\tconst xx = ( m11 + 1 ) / 2;\n\t\t\tconst yy = ( m22 + 1 ) / 2;\n\t\t\tconst zz = ( m33 + 1 ) / 2;\n\t\t\tconst xy = ( m12 + m21 ) / 4;\n\t\t\tconst xz = ( m13 + m31 ) / 4;\n\t\t\tconst yz = ( m23 + m32 ) / 4;\n\n\t\t\tif ( ( xx > yy ) && ( xx > zz ) ) {\n\n\t\t\t\t// m11 is the largest diagonal term\n\n\t\t\t\tif ( xx < epsilon ) {\n\n\t\t\t\t\tx = 0;\n\t\t\t\t\ty = 0.707106781;\n\t\t\t\t\tz = 0.707106781;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tx = Math.sqrt( xx );\n\t\t\t\t\ty = xy / x;\n\t\t\t\t\tz = xz / x;\n\n\t\t\t\t}\n\n\t\t\t} else if ( yy > zz ) {\n\n\t\t\t\t// m22 is the largest diagonal term\n\n\t\t\t\tif ( yy < epsilon ) {\n\n\t\t\t\t\tx = 0.707106781;\n\t\t\t\t\ty = 0;\n\t\t\t\t\tz = 0.707106781;\n\n\t\t\t\t} else {\n\n\t\t\t\t\ty = Math.sqrt( yy );\n\t\t\t\t\tx = xy / y;\n\t\t\t\t\tz = yz / y;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// m33 is the largest diagonal term so base result on this\n\n\t\t\t\tif ( zz < epsilon ) {\n\n\t\t\t\t\tx = 0.707106781;\n\t\t\t\t\ty = 0.707106781;\n\t\t\t\t\tz = 0;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tz = Math.sqrt( zz );\n\t\t\t\t\tx = xz / z;\n\t\t\t\t\ty = yz / z;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis.set( x, y, z, angle );\n\n\t\t\treturn this; // return 180 deg rotation\n\n\t\t}\n\n\t\t// as we have reached here there are no singularities so we can handle normally\n\n\t\tlet s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) +\n\t\t\t( m13 - m31 ) * ( m13 - m31 ) +\n\t\t\t( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize\n\n\t\tif ( Math.abs( s ) < 0.001 ) s = 1;\n\n\t\t// prevent divide by zero, should not happen if matrix is orthogonal and should be\n\t\t// caught by singularity test above, but I've left it in just in case\n\n\t\tthis.x = ( m32 - m23 ) / s;\n\t\tthis.y = ( m13 - m31 ) / s;\n\t\tthis.z = ( m21 - m12 ) / s;\n\t\tthis.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );\n\n\t\treturn this;\n\n\t}\n\n\tmin( v ) {\n\n\t\tthis.x = Math.min( this.x, v.x );\n\t\tthis.y = Math.min( this.y, v.y );\n\t\tthis.z = Math.min( this.z, v.z );\n\t\tthis.w = Math.min( this.w, v.w );\n\n\t\treturn this;\n\n\t}\n\n\tmax( v ) {\n\n\t\tthis.x = Math.max( this.x, v.x );\n\t\tthis.y = Math.max( this.y, v.y );\n\t\tthis.z = Math.max( this.z, v.z );\n\t\tthis.w = Math.max( this.w, v.w );\n\n\t\treturn this;\n\n\t}\n\n\tclamp( min, max ) {\n\n\t\t// assumes min < max, componentwise\n\n\t\tthis.x = Math.max( min.x, Math.min( max.x, this.x ) );\n\t\tthis.y = Math.max( min.y, Math.min( max.y, this.y ) );\n\t\tthis.z = Math.max( min.z, Math.min( max.z, this.z ) );\n\t\tthis.w = Math.max( min.w, Math.min( max.w, this.w ) );\n\n\t\treturn this;\n\n\t}\n\n\tclampScalar( minVal, maxVal ) {\n\n\t\tthis.x = Math.max( minVal, Math.min( maxVal, this.x ) );\n\t\tthis.y = Math.max( minVal, Math.min( maxVal, this.y ) );\n\t\tthis.z = Math.max( minVal, Math.min( maxVal, this.z ) );\n\t\tthis.w = Math.max( minVal, Math.min( maxVal, this.w ) );\n\n\t\treturn this;\n\n\t}\n\n\tclampLength( min, max ) {\n\n\t\tconst length = this.length();\n\n\t\treturn this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );\n\n\t}\n\n\tfloor() {\n\n\t\tthis.x = Math.floor( this.x );\n\t\tthis.y = Math.floor( this.y );\n\t\tthis.z = Math.floor( this.z );\n\t\tthis.w = Math.floor( this.w );\n\n\t\treturn this;\n\n\t}\n\n\tceil() {\n\n\t\tthis.x = Math.ceil( this.x );\n\t\tthis.y = Math.ceil( this.y );\n\t\tthis.z = Math.ceil( this.z );\n\t\tthis.w = Math.ceil( this.w );\n\n\t\treturn this;\n\n\t}\n\n\tround() {\n\n\t\tthis.x = Math.round( this.x );\n\t\tthis.y = Math.round( this.y );\n\t\tthis.z = Math.round( this.z );\n\t\tthis.w = Math.round( this.w );\n\n\t\treturn this;\n\n\t}\n\n\troundToZero() {\n\n\t\tthis.x = Math.trunc( this.x );\n\t\tthis.y = Math.trunc( this.y );\n\t\tthis.z = Math.trunc( this.z );\n\t\tthis.w = Math.trunc( this.w );\n\n\t\treturn this;\n\n\t}\n\n\tnegate() {\n\n\t\tthis.x = - this.x;\n\t\tthis.y = - this.y;\n\t\tthis.z = - this.z;\n\t\tthis.w = - this.w;\n\n\t\treturn this;\n\n\t}\n\n\tdot( v ) {\n\n\t\treturn this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;\n\n\t}\n\n\tlengthSq() {\n\n\t\treturn this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;\n\n\t}\n\n\tlength() {\n\n\t\treturn Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );\n\n\t}\n\n\tmanhattanLength() {\n\n\t\treturn Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );\n\n\t}\n\n\tnormalize() {\n\n\t\treturn this.divideScalar( this.length() || 1 );\n\n\t}\n\n\tsetLength( length ) {\n\n\t\treturn this.normalize().multiplyScalar( length );\n\n\t}\n\n\tlerp( v, alpha ) {\n\n\t\tthis.x += ( v.x - this.x ) * alpha;\n\t\tthis.y += ( v.y - this.y ) * alpha;\n\t\tthis.z += ( v.z - this.z ) * alpha;\n\t\tthis.w += ( v.w - this.w ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tlerpVectors( v1, v2, alpha ) {\n\n\t\tthis.x = v1.x + ( v2.x - v1.x ) * alpha;\n\t\tthis.y = v1.y + ( v2.y - v1.y ) * alpha;\n\t\tthis.z = v1.z + ( v2.z - v1.z ) * alpha;\n\t\tthis.w = v1.w + ( v2.w - v1.w ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tequals( v ) {\n\n\t\treturn ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tthis.x = array[ offset ];\n\t\tthis.y = array[ offset + 1 ];\n\t\tthis.z = array[ offset + 2 ];\n\t\tthis.w = array[ offset + 3 ];\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tarray[ offset ] = this.x;\n\t\tarray[ offset + 1 ] = this.y;\n\t\tarray[ offset + 2 ] = this.z;\n\t\tarray[ offset + 3 ] = this.w;\n\n\t\treturn array;\n\n\t}\n\n\tfromBufferAttribute( attribute, index ) {\n\n\t\tthis.x = attribute.getX( index );\n\t\tthis.y = attribute.getY( index );\n\t\tthis.z = attribute.getZ( index );\n\t\tthis.w = attribute.getW( index );\n\n\t\treturn this;\n\n\t}\n\n\trandom() {\n\n\t\tthis.x = Math.random();\n\t\tthis.y = Math.random();\n\t\tthis.z = Math.random();\n\t\tthis.w = Math.random();\n\n\t\treturn this;\n\n\t}\n\n\t*[ Symbol.iterator ]() {\n\n\t\tyield this.x;\n\t\tyield this.y;\n\t\tyield this.z;\n\t\tyield this.w;\n\n\t}\n\n}\n\n/*\n In options, we can specify:\n * Texture parameters for an auto-generated target texture\n * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers\n*/\nclass RenderTarget extends EventDispatcher {\n\n\tconstructor( width = 1, height = 1, options = {} ) {\n\n\t\tsuper();\n\n\t\tthis.isRenderTarget = true;\n\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.depth = 1;\n\n\t\tthis.scissor = new Vector4( 0, 0, width, height );\n\t\tthis.scissorTest = false;\n\n\t\tthis.viewport = new Vector4( 0, 0, width, height );\n\n\t\tconst image = { width: width, height: height, depth: 1 };\n\n\t\tif ( options.encoding !== undefined ) {\n\n\t\t\t// @deprecated, r152\n\t\t\twarnOnce( 'THREE.WebGLRenderTarget: option.encoding has been replaced by option.colorSpace.' );\n\t\t\toptions.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace;\n\n\t\t}\n\n\t\toptions = Object.assign( {\n\t\t\tgenerateMipmaps: false,\n\t\t\tinternalFormat: null,\n\t\t\tminFilter: LinearFilter,\n\t\t\tdepthBuffer: true,\n\t\t\tstencilBuffer: false,\n\t\t\tdepthTexture: null,\n\t\t\tsamples: 0\n\t\t}, options );\n\n\t\tthis.texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace );\n\t\tthis.texture.isRenderTargetTexture = true;\n\n\t\tthis.texture.flipY = false;\n\t\tthis.texture.generateMipmaps = options.generateMipmaps;\n\t\tthis.texture.internalFormat = options.internalFormat;\n\n\t\tthis.depthBuffer = options.depthBuffer;\n\t\tthis.stencilBuffer = options.stencilBuffer;\n\n\t\tthis.depthTexture = options.depthTexture;\n\n\t\tthis.samples = options.samples;\n\n\t}\n\n\tsetSize( width, height, depth = 1 ) {\n\n\t\tif ( this.width !== width || this.height !== height || this.depth !== depth ) {\n\n\t\t\tthis.width = width;\n\t\t\tthis.height = height;\n\t\t\tthis.depth = depth;\n\n\t\t\tthis.texture.image.width = width;\n\t\t\tthis.texture.image.height = height;\n\t\t\tthis.texture.image.depth = depth;\n\n\t\t\tthis.dispose();\n\n\t\t}\n\n\t\tthis.viewport.set( 0, 0, width, height );\n\t\tthis.scissor.set( 0, 0, width, height );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.width = source.width;\n\t\tthis.height = source.height;\n\t\tthis.depth = source.depth;\n\n\t\tthis.scissor.copy( source.scissor );\n\t\tthis.scissorTest = source.scissorTest;\n\n\t\tthis.viewport.copy( source.viewport );\n\n\t\tthis.texture = source.texture.clone();\n\t\tthis.texture.isRenderTargetTexture = true;\n\n\t\t// ensure image object is not shared, see #20328\n\n\t\tconst image = Object.assign( {}, source.texture.image );\n\t\tthis.texture.source = new Source( image );\n\n\t\tthis.depthBuffer = source.depthBuffer;\n\t\tthis.stencilBuffer = source.stencilBuffer;\n\n\t\tif ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone();\n\n\t\tthis.samples = source.samples;\n\n\t\treturn this;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t}\n\n}\n\nclass WebGLRenderTarget extends RenderTarget {\n\n\tconstructor( width = 1, height = 1, options = {} ) {\n\n\t\tsuper( width, height, options );\n\n\t\tthis.isWebGLRenderTarget = true;\n\n\t}\n\n}\n\nclass DataArrayTexture extends Texture {\n\n\tconstructor( data = null, width = 1, height = 1, depth = 1 ) {\n\n\t\tsuper( null );\n\n\t\tthis.isDataArrayTexture = true;\n\n\t\tthis.image = { data, width, height, depth };\n\n\t\tthis.magFilter = NearestFilter;\n\t\tthis.minFilter = NearestFilter;\n\n\t\tthis.wrapR = ClampToEdgeWrapping;\n\n\t\tthis.generateMipmaps = false;\n\t\tthis.flipY = false;\n\t\tthis.unpackAlignment = 1;\n\n\t}\n\n}\n\nclass WebGLArrayRenderTarget extends WebGLRenderTarget {\n\n\tconstructor( width = 1, height = 1, depth = 1, options = {} ) {\n\n\t\tsuper( width, height, options );\n\n\t\tthis.isWebGLArrayRenderTarget = true;\n\n\t\tthis.depth = depth;\n\n\t\tthis.texture = new DataArrayTexture( null, width, height, depth );\n\n\t\tthis.texture.isRenderTargetTexture = true;\n\n\t}\n\n}\n\nclass Data3DTexture extends Texture {\n\n\tconstructor( data = null, width = 1, height = 1, depth = 1 ) {\n\n\t\t// We're going to add .setXXX() methods for setting properties later.\n\t\t// Users can still set in DataTexture3D directly.\n\t\t//\n\t\t//\tconst texture = new THREE.DataTexture3D( data, width, height, depth );\n\t\t// \ttexture.anisotropy = 16;\n\t\t//\n\t\t// See #14839\n\n\t\tsuper( null );\n\n\t\tthis.isData3DTexture = true;\n\n\t\tthis.image = { data, width, height, depth };\n\n\t\tthis.magFilter = NearestFilter;\n\t\tthis.minFilter = NearestFilter;\n\n\t\tthis.wrapR = ClampToEdgeWrapping;\n\n\t\tthis.generateMipmaps = false;\n\t\tthis.flipY = false;\n\t\tthis.unpackAlignment = 1;\n\n\t}\n\n}\n\nclass WebGL3DRenderTarget extends WebGLRenderTarget {\n\n\tconstructor( width = 1, height = 1, depth = 1, options = {} ) {\n\n\t\tsuper( width, height, options );\n\n\t\tthis.isWebGL3DRenderTarget = true;\n\n\t\tthis.depth = depth;\n\n\t\tthis.texture = new Data3DTexture( null, width, height, depth );\n\n\t\tthis.texture.isRenderTargetTexture = true;\n\n\t}\n\n}\n\nclass WebGLMultipleRenderTargets extends WebGLRenderTarget {\n\n\tconstructor( width = 1, height = 1, count = 1, options = {} ) {\n\n\t\tsuper( width, height, options );\n\n\t\tthis.isWebGLMultipleRenderTargets = true;\n\n\t\tconst texture = this.texture;\n\n\t\tthis.texture = [];\n\n\t\tfor ( let i = 0; i < count; i ++ ) {\n\n\t\t\tthis.texture[ i ] = texture.clone();\n\t\t\tthis.texture[ i ].isRenderTargetTexture = true;\n\n\t\t}\n\n\t}\n\n\tsetSize( width, height, depth = 1 ) {\n\n\t\tif ( this.width !== width || this.height !== height || this.depth !== depth ) {\n\n\t\t\tthis.width = width;\n\t\t\tthis.height = height;\n\t\t\tthis.depth = depth;\n\n\t\t\tfor ( let i = 0, il = this.texture.length; i < il; i ++ ) {\n\n\t\t\t\tthis.texture[ i ].image.width = width;\n\t\t\t\tthis.texture[ i ].image.height = height;\n\t\t\t\tthis.texture[ i ].image.depth = depth;\n\n\t\t\t}\n\n\t\t\tthis.dispose();\n\n\t\t}\n\n\t\tthis.viewport.set( 0, 0, width, height );\n\t\tthis.scissor.set( 0, 0, width, height );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.dispose();\n\n\t\tthis.width = source.width;\n\t\tthis.height = source.height;\n\t\tthis.depth = source.depth;\n\n\t\tthis.scissor.copy( source.scissor );\n\t\tthis.scissorTest = source.scissorTest;\n\n\t\tthis.viewport.copy( source.viewport );\n\n\t\tthis.depthBuffer = source.depthBuffer;\n\t\tthis.stencilBuffer = source.stencilBuffer;\n\n\t\tif ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone();\n\n\t\tthis.texture.length = 0;\n\n\t\tfor ( let i = 0, il = source.texture.length; i < il; i ++ ) {\n\n\t\t\tthis.texture[ i ] = source.texture[ i ].clone();\n\t\t\tthis.texture[ i ].isRenderTargetTexture = true;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass Quaternion {\n\n\tconstructor( x = 0, y = 0, z = 0, w = 1 ) {\n\n\t\tthis.isQuaternion = true;\n\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tthis._z = z;\n\t\tthis._w = w;\n\n\t}\n\n\tstatic slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {\n\n\t\t// fuzz-free, array-based Quaternion SLERP operation\n\n\t\tlet x0 = src0[ srcOffset0 + 0 ],\n\t\t\ty0 = src0[ srcOffset0 + 1 ],\n\t\t\tz0 = src0[ srcOffset0 + 2 ],\n\t\t\tw0 = src0[ srcOffset0 + 3 ];\n\n\t\tconst x1 = src1[ srcOffset1 + 0 ],\n\t\t\ty1 = src1[ srcOffset1 + 1 ],\n\t\t\tz1 = src1[ srcOffset1 + 2 ],\n\t\t\tw1 = src1[ srcOffset1 + 3 ];\n\n\t\tif ( t === 0 ) {\n\n\t\t\tdst[ dstOffset + 0 ] = x0;\n\t\t\tdst[ dstOffset + 1 ] = y0;\n\t\t\tdst[ dstOffset + 2 ] = z0;\n\t\t\tdst[ dstOffset + 3 ] = w0;\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( t === 1 ) {\n\n\t\t\tdst[ dstOffset + 0 ] = x1;\n\t\t\tdst[ dstOffset + 1 ] = y1;\n\t\t\tdst[ dstOffset + 2 ] = z1;\n\t\t\tdst[ dstOffset + 3 ] = w1;\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {\n\n\t\t\tlet s = 1 - t;\n\t\t\tconst cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,\n\t\t\t\tdir = ( cos >= 0 ? 1 : - 1 ),\n\t\t\t\tsqrSin = 1 - cos * cos;\n\n\t\t\t// Skip the Slerp for tiny steps to avoid numeric problems:\n\t\t\tif ( sqrSin > Number.EPSILON ) {\n\n\t\t\t\tconst sin = Math.sqrt( sqrSin ),\n\t\t\t\t\tlen = Math.atan2( sin, cos * dir );\n\n\t\t\t\ts = Math.sin( s * len ) / sin;\n\t\t\t\tt = Math.sin( t * len ) / sin;\n\n\t\t\t}\n\n\t\t\tconst tDir = t * dir;\n\n\t\t\tx0 = x0 * s + x1 * tDir;\n\t\t\ty0 = y0 * s + y1 * tDir;\n\t\t\tz0 = z0 * s + z1 * tDir;\n\t\t\tw0 = w0 * s + w1 * tDir;\n\n\t\t\t// Normalize in case we just did a lerp:\n\t\t\tif ( s === 1 - t ) {\n\n\t\t\t\tconst f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );\n\n\t\t\t\tx0 *= f;\n\t\t\t\ty0 *= f;\n\t\t\t\tz0 *= f;\n\t\t\t\tw0 *= f;\n\n\t\t\t}\n\n\t\t}\n\n\t\tdst[ dstOffset ] = x0;\n\t\tdst[ dstOffset + 1 ] = y0;\n\t\tdst[ dstOffset + 2 ] = z0;\n\t\tdst[ dstOffset + 3 ] = w0;\n\n\t}\n\n\tstatic multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) {\n\n\t\tconst x0 = src0[ srcOffset0 ];\n\t\tconst y0 = src0[ srcOffset0 + 1 ];\n\t\tconst z0 = src0[ srcOffset0 + 2 ];\n\t\tconst w0 = src0[ srcOffset0 + 3 ];\n\n\t\tconst x1 = src1[ srcOffset1 ];\n\t\tconst y1 = src1[ srcOffset1 + 1 ];\n\t\tconst z1 = src1[ srcOffset1 + 2 ];\n\t\tconst w1 = src1[ srcOffset1 + 3 ];\n\n\t\tdst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1;\n\t\tdst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1;\n\t\tdst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1;\n\t\tdst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;\n\n\t\treturn dst;\n\n\t}\n\n\tget x() {\n\n\t\treturn this._x;\n\n\t}\n\n\tset x( value ) {\n\n\t\tthis._x = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tget y() {\n\n\t\treturn this._y;\n\n\t}\n\n\tset y( value ) {\n\n\t\tthis._y = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tget z() {\n\n\t\treturn this._z;\n\n\t}\n\n\tset z( value ) {\n\n\t\tthis._z = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tget w() {\n\n\t\treturn this._w;\n\n\t}\n\n\tset w( value ) {\n\n\t\tthis._w = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tset( x, y, z, w ) {\n\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tthis._z = z;\n\t\tthis._w = w;\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this._x, this._y, this._z, this._w );\n\n\t}\n\n\tcopy( quaternion ) {\n\n\t\tthis._x = quaternion.x;\n\t\tthis._y = quaternion.y;\n\t\tthis._z = quaternion.z;\n\t\tthis._w = quaternion.w;\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tsetFromEuler( euler, update = true ) {\n\n\t\tconst x = euler._x, y = euler._y, z = euler._z, order = euler._order;\n\n\t\t// http://www.mathworks.com/matlabcentral/fileexchange/\n\t\t// \t20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/\n\t\t//\tcontent/SpinCalc.m\n\n\t\tconst cos = Math.cos;\n\t\tconst sin = Math.sin;\n\n\t\tconst c1 = cos( x / 2 );\n\t\tconst c2 = cos( y / 2 );\n\t\tconst c3 = cos( z / 2 );\n\n\t\tconst s1 = sin( x / 2 );\n\t\tconst s2 = sin( y / 2 );\n\t\tconst s3 = sin( z / 2 );\n\n\t\tswitch ( order ) {\n\n\t\t\tcase 'XYZ':\n\t\t\t\tthis._x = s1 * c2 * c3 + c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 - s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 + s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 - s1 * s2 * s3;\n\t\t\t\tbreak;\n\n\t\t\tcase 'YXZ':\n\t\t\t\tthis._x = s1 * c2 * c3 + c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 - s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 - s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 + s1 * s2 * s3;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ZXY':\n\t\t\t\tthis._x = s1 * c2 * c3 - c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 + s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 + s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 - s1 * s2 * s3;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ZYX':\n\t\t\t\tthis._x = s1 * c2 * c3 - c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 + s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 - s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 + s1 * s2 * s3;\n\t\t\t\tbreak;\n\n\t\t\tcase 'YZX':\n\t\t\t\tthis._x = s1 * c2 * c3 + c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 + s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 - s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 - s1 * s2 * s3;\n\t\t\t\tbreak;\n\n\t\t\tcase 'XZY':\n\t\t\t\tthis._x = s1 * c2 * c3 - c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 - s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 + s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 + s1 * s2 * s3;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tconsole.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order );\n\n\t\t}\n\n\t\tif ( update === true ) this._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tsetFromAxisAngle( axis, angle ) {\n\n\t\t// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm\n\n\t\t// assumes axis is normalized\n\n\t\tconst halfAngle = angle / 2, s = Math.sin( halfAngle );\n\n\t\tthis._x = axis.x * s;\n\t\tthis._y = axis.y * s;\n\t\tthis._z = axis.z * s;\n\t\tthis._w = Math.cos( halfAngle );\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tsetFromRotationMatrix( m ) {\n\n\t\t// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm\n\n\t\t// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n\t\tconst te = m.elements,\n\n\t\t\tm11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],\n\t\t\tm21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],\n\t\t\tm31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],\n\n\t\t\ttrace = m11 + m22 + m33;\n\n\t\tif ( trace > 0 ) {\n\n\t\t\tconst s = 0.5 / Math.sqrt( trace + 1.0 );\n\n\t\t\tthis._w = 0.25 / s;\n\t\t\tthis._x = ( m32 - m23 ) * s;\n\t\t\tthis._y = ( m13 - m31 ) * s;\n\t\t\tthis._z = ( m21 - m12 ) * s;\n\n\t\t} else if ( m11 > m22 && m11 > m33 ) {\n\n\t\t\tconst s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );\n\n\t\t\tthis._w = ( m32 - m23 ) / s;\n\t\t\tthis._x = 0.25 * s;\n\t\t\tthis._y = ( m12 + m21 ) / s;\n\t\t\tthis._z = ( m13 + m31 ) / s;\n\n\t\t} else if ( m22 > m33 ) {\n\n\t\t\tconst s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );\n\n\t\t\tthis._w = ( m13 - m31 ) / s;\n\t\t\tthis._x = ( m12 + m21 ) / s;\n\t\t\tthis._y = 0.25 * s;\n\t\t\tthis._z = ( m23 + m32 ) / s;\n\n\t\t} else {\n\n\t\t\tconst s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );\n\n\t\t\tthis._w = ( m21 - m12 ) / s;\n\t\t\tthis._x = ( m13 + m31 ) / s;\n\t\t\tthis._y = ( m23 + m32 ) / s;\n\t\t\tthis._z = 0.25 * s;\n\n\t\t}\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tsetFromUnitVectors( vFrom, vTo ) {\n\n\t\t// assumes direction vectors vFrom and vTo are normalized\n\n\t\tlet r = vFrom.dot( vTo ) + 1;\n\n\t\tif ( r < Number.EPSILON ) {\n\n\t\t\t// vFrom and vTo point in opposite directions\n\n\t\t\tr = 0;\n\n\t\t\tif ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {\n\n\t\t\t\tthis._x = - vFrom.y;\n\t\t\t\tthis._y = vFrom.x;\n\t\t\t\tthis._z = 0;\n\t\t\t\tthis._w = r;\n\n\t\t\t} else {\n\n\t\t\t\tthis._x = 0;\n\t\t\t\tthis._y = - vFrom.z;\n\t\t\t\tthis._z = vFrom.y;\n\t\t\t\tthis._w = r;\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3\n\n\t\t\tthis._x = vFrom.y * vTo.z - vFrom.z * vTo.y;\n\t\t\tthis._y = vFrom.z * vTo.x - vFrom.x * vTo.z;\n\t\t\tthis._z = vFrom.x * vTo.y - vFrom.y * vTo.x;\n\t\t\tthis._w = r;\n\n\t\t}\n\n\t\treturn this.normalize();\n\n\t}\n\n\tangleTo( q ) {\n\n\t\treturn 2 * Math.acos( Math.abs( clamp( this.dot( q ), - 1, 1 ) ) );\n\n\t}\n\n\trotateTowards( q, step ) {\n\n\t\tconst angle = this.angleTo( q );\n\n\t\tif ( angle === 0 ) return this;\n\n\t\tconst t = Math.min( 1, step / angle );\n\n\t\tthis.slerp( q, t );\n\n\t\treturn this;\n\n\t}\n\n\tidentity() {\n\n\t\treturn this.set( 0, 0, 0, 1 );\n\n\t}\n\n\tinvert() {\n\n\t\t// quaternion is assumed to have unit length\n\n\t\treturn this.conjugate();\n\n\t}\n\n\tconjugate() {\n\n\t\tthis._x *= - 1;\n\t\tthis._y *= - 1;\n\t\tthis._z *= - 1;\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tdot( v ) {\n\n\t\treturn this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;\n\n\t}\n\n\tlengthSq() {\n\n\t\treturn this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;\n\n\t}\n\n\tlength() {\n\n\t\treturn Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );\n\n\t}\n\n\tnormalize() {\n\n\t\tlet l = this.length();\n\n\t\tif ( l === 0 ) {\n\n\t\t\tthis._x = 0;\n\t\t\tthis._y = 0;\n\t\t\tthis._z = 0;\n\t\t\tthis._w = 1;\n\n\t\t} else {\n\n\t\t\tl = 1 / l;\n\n\t\t\tthis._x = this._x * l;\n\t\t\tthis._y = this._y * l;\n\t\t\tthis._z = this._z * l;\n\t\t\tthis._w = this._w * l;\n\n\t\t}\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( q ) {\n\n\t\treturn this.multiplyQuaternions( this, q );\n\n\t}\n\n\tpremultiply( q ) {\n\n\t\treturn this.multiplyQuaternions( q, this );\n\n\t}\n\n\tmultiplyQuaternions( a, b ) {\n\n\t\t// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm\n\n\t\tconst qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;\n\t\tconst qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;\n\n\t\tthis._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;\n\t\tthis._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;\n\t\tthis._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;\n\t\tthis._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tslerp( qb, t ) {\n\n\t\tif ( t === 0 ) return this;\n\t\tif ( t === 1 ) return this.copy( qb );\n\n\t\tconst x = this._x, y = this._y, z = this._z, w = this._w;\n\n\t\t// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/\n\n\t\tlet cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;\n\n\t\tif ( cosHalfTheta < 0 ) {\n\n\t\t\tthis._w = - qb._w;\n\t\t\tthis._x = - qb._x;\n\t\t\tthis._y = - qb._y;\n\t\t\tthis._z = - qb._z;\n\n\t\t\tcosHalfTheta = - cosHalfTheta;\n\n\t\t} else {\n\n\t\t\tthis.copy( qb );\n\n\t\t}\n\n\t\tif ( cosHalfTheta >= 1.0 ) {\n\n\t\t\tthis._w = w;\n\t\t\tthis._x = x;\n\t\t\tthis._y = y;\n\t\t\tthis._z = z;\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tconst sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta;\n\n\t\tif ( sqrSinHalfTheta <= Number.EPSILON ) {\n\n\t\t\tconst s = 1 - t;\n\t\t\tthis._w = s * w + t * this._w;\n\t\t\tthis._x = s * x + t * this._x;\n\t\t\tthis._y = s * y + t * this._y;\n\t\t\tthis._z = s * z + t * this._z;\n\n\t\t\tthis.normalize(); // normalize calls _onChangeCallback()\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tconst sinHalfTheta = Math.sqrt( sqrSinHalfTheta );\n\t\tconst halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );\n\t\tconst ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,\n\t\t\tratioB = Math.sin( t * halfTheta ) / sinHalfTheta;\n\n\t\tthis._w = ( w * ratioA + this._w * ratioB );\n\t\tthis._x = ( x * ratioA + this._x * ratioB );\n\t\tthis._y = ( y * ratioA + this._y * ratioB );\n\t\tthis._z = ( z * ratioA + this._z * ratioB );\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tslerpQuaternions( qa, qb, t ) {\n\n\t\treturn this.copy( qa ).slerp( qb, t );\n\n\t}\n\n\trandom() {\n\n\t\t// Derived from http://planning.cs.uiuc.edu/node198.html\n\t\t// Note, this source uses w, x, y, z ordering,\n\t\t// so we swap the order below.\n\n\t\tconst u1 = Math.random();\n\t\tconst sqrt1u1 = Math.sqrt( 1 - u1 );\n\t\tconst sqrtu1 = Math.sqrt( u1 );\n\n\t\tconst u2 = 2 * Math.PI * Math.random();\n\n\t\tconst u3 = 2 * Math.PI * Math.random();\n\n\t\treturn this.set(\n\t\t\tsqrt1u1 * Math.cos( u2 ),\n\t\t\tsqrtu1 * Math.sin( u3 ),\n\t\t\tsqrtu1 * Math.cos( u3 ),\n\t\t\tsqrt1u1 * Math.sin( u2 ),\n\t\t);\n\n\t}\n\n\tequals( quaternion ) {\n\n\t\treturn ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tthis._x = array[ offset ];\n\t\tthis._y = array[ offset + 1 ];\n\t\tthis._z = array[ offset + 2 ];\n\t\tthis._w = array[ offset + 3 ];\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tarray[ offset ] = this._x;\n\t\tarray[ offset + 1 ] = this._y;\n\t\tarray[ offset + 2 ] = this._z;\n\t\tarray[ offset + 3 ] = this._w;\n\n\t\treturn array;\n\n\t}\n\n\tfromBufferAttribute( attribute, index ) {\n\n\t\tthis._x = attribute.getX( index );\n\t\tthis._y = attribute.getY( index );\n\t\tthis._z = attribute.getZ( index );\n\t\tthis._w = attribute.getW( index );\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\treturn this.toArray();\n\n\t}\n\n\t_onChange( callback ) {\n\n\t\tthis._onChangeCallback = callback;\n\n\t\treturn this;\n\n\t}\n\n\t_onChangeCallback() {}\n\n\t*[ Symbol.iterator ]() {\n\n\t\tyield this._x;\n\t\tyield this._y;\n\t\tyield this._z;\n\t\tyield this._w;\n\n\t}\n\n}\n\nclass Vector3 {\n\n\tconstructor( x = 0, y = 0, z = 0 ) {\n\n\t\tVector3.prototype.isVector3 = true;\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\tthis.z = z;\n\n\t}\n\n\tset( x, y, z ) {\n\n\t\tif ( z === undefined ) z = this.z; // sprite.scale.set(x,y)\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\tthis.z = z;\n\n\t\treturn this;\n\n\t}\n\n\tsetScalar( scalar ) {\n\n\t\tthis.x = scalar;\n\t\tthis.y = scalar;\n\t\tthis.z = scalar;\n\n\t\treturn this;\n\n\t}\n\n\tsetX( x ) {\n\n\t\tthis.x = x;\n\n\t\treturn this;\n\n\t}\n\n\tsetY( y ) {\n\n\t\tthis.y = y;\n\n\t\treturn this;\n\n\t}\n\n\tsetZ( z ) {\n\n\t\tthis.z = z;\n\n\t\treturn this;\n\n\t}\n\n\tsetComponent( index, value ) {\n\n\t\tswitch ( index ) {\n\n\t\t\tcase 0: this.x = value; break;\n\t\t\tcase 1: this.y = value; break;\n\t\t\tcase 2: this.z = value; break;\n\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tgetComponent( index ) {\n\n\t\tswitch ( index ) {\n\n\t\t\tcase 0: return this.x;\n\t\t\tcase 1: return this.y;\n\t\t\tcase 2: return this.z;\n\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t}\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this.x, this.y, this.z );\n\n\t}\n\n\tcopy( v ) {\n\n\t\tthis.x = v.x;\n\t\tthis.y = v.y;\n\t\tthis.z = v.z;\n\n\t\treturn this;\n\n\t}\n\n\tadd( v ) {\n\n\t\tthis.x += v.x;\n\t\tthis.y += v.y;\n\t\tthis.z += v.z;\n\n\t\treturn this;\n\n\t}\n\n\taddScalar( s ) {\n\n\t\tthis.x += s;\n\t\tthis.y += s;\n\t\tthis.z += s;\n\n\t\treturn this;\n\n\t}\n\n\taddVectors( a, b ) {\n\n\t\tthis.x = a.x + b.x;\n\t\tthis.y = a.y + b.y;\n\t\tthis.z = a.z + b.z;\n\n\t\treturn this;\n\n\t}\n\n\taddScaledVector( v, s ) {\n\n\t\tthis.x += v.x * s;\n\t\tthis.y += v.y * s;\n\t\tthis.z += v.z * s;\n\n\t\treturn this;\n\n\t}\n\n\tsub( v ) {\n\n\t\tthis.x -= v.x;\n\t\tthis.y -= v.y;\n\t\tthis.z -= v.z;\n\n\t\treturn this;\n\n\t}\n\n\tsubScalar( s ) {\n\n\t\tthis.x -= s;\n\t\tthis.y -= s;\n\t\tthis.z -= s;\n\n\t\treturn this;\n\n\t}\n\n\tsubVectors( a, b ) {\n\n\t\tthis.x = a.x - b.x;\n\t\tthis.y = a.y - b.y;\n\t\tthis.z = a.z - b.z;\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( v ) {\n\n\t\tthis.x *= v.x;\n\t\tthis.y *= v.y;\n\t\tthis.z *= v.z;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyScalar( scalar ) {\n\n\t\tthis.x *= scalar;\n\t\tthis.y *= scalar;\n\t\tthis.z *= scalar;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyVectors( a, b ) {\n\n\t\tthis.x = a.x * b.x;\n\t\tthis.y = a.y * b.y;\n\t\tthis.z = a.z * b.z;\n\n\t\treturn this;\n\n\t}\n\n\tapplyEuler( euler ) {\n\n\t\treturn this.applyQuaternion( _quaternion$4.setFromEuler( euler ) );\n\n\t}\n\n\tapplyAxisAngle( axis, angle ) {\n\n\t\treturn this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) );\n\n\t}\n\n\tapplyMatrix3( m ) {\n\n\t\tconst x = this.x, y = this.y, z = this.z;\n\t\tconst e = m.elements;\n\n\t\tthis.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;\n\t\tthis.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;\n\t\tthis.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;\n\n\t\treturn this;\n\n\t}\n\n\tapplyNormalMatrix( m ) {\n\n\t\treturn this.applyMatrix3( m ).normalize();\n\n\t}\n\n\tapplyMatrix4( m ) {\n\n\t\tconst x = this.x, y = this.y, z = this.z;\n\t\tconst e = m.elements;\n\n\t\tconst w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );\n\n\t\tthis.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;\n\t\tthis.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;\n\t\tthis.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;\n\n\t\treturn this;\n\n\t}\n\n\tapplyQuaternion( q ) {\n\n\t\t// quaternion q is assumed to have unit length\n\n\t\tconst vx = this.x, vy = this.y, vz = this.z;\n\t\tconst qx = q.x, qy = q.y, qz = q.z, qw = q.w;\n\n\t\t// t = 2 * cross( q.xyz, v );\n\t\tconst tx = 2 * ( qy * vz - qz * vy );\n\t\tconst ty = 2 * ( qz * vx - qx * vz );\n\t\tconst tz = 2 * ( qx * vy - qy * vx );\n\n\t\t// v + q.w * t + cross( q.xyz, t );\n\t\tthis.x = vx + qw * tx + qy * tz - qz * ty;\n\t\tthis.y = vy + qw * ty + qz * tx - qx * tz;\n\t\tthis.z = vz + qw * tz + qx * ty - qy * tx;\n\n\t\treturn this;\n\n\t}\n\n\tproject( camera ) {\n\n\t\treturn this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix );\n\n\t}\n\n\tunproject( camera ) {\n\n\t\treturn this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld );\n\n\t}\n\n\ttransformDirection( m ) {\n\n\t\t// input: THREE.Matrix4 affine matrix\n\t\t// vector interpreted as a direction\n\n\t\tconst x = this.x, y = this.y, z = this.z;\n\t\tconst e = m.elements;\n\n\t\tthis.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;\n\t\tthis.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;\n\t\tthis.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;\n\n\t\treturn this.normalize();\n\n\t}\n\n\tdivide( v ) {\n\n\t\tthis.x /= v.x;\n\t\tthis.y /= v.y;\n\t\tthis.z /= v.z;\n\n\t\treturn this;\n\n\t}\n\n\tdivideScalar( scalar ) {\n\n\t\treturn this.multiplyScalar( 1 / scalar );\n\n\t}\n\n\tmin( v ) {\n\n\t\tthis.x = Math.min( this.x, v.x );\n\t\tthis.y = Math.min( this.y, v.y );\n\t\tthis.z = Math.min( this.z, v.z );\n\n\t\treturn this;\n\n\t}\n\n\tmax( v ) {\n\n\t\tthis.x = Math.max( this.x, v.x );\n\t\tthis.y = Math.max( this.y, v.y );\n\t\tthis.z = Math.max( this.z, v.z );\n\n\t\treturn this;\n\n\t}\n\n\tclamp( min, max ) {\n\n\t\t// assumes min < max, componentwise\n\n\t\tthis.x = Math.max( min.x, Math.min( max.x, this.x ) );\n\t\tthis.y = Math.max( min.y, Math.min( max.y, this.y ) );\n\t\tthis.z = Math.max( min.z, Math.min( max.z, this.z ) );\n\n\t\treturn this;\n\n\t}\n\n\tclampScalar( minVal, maxVal ) {\n\n\t\tthis.x = Math.max( minVal, Math.min( maxVal, this.x ) );\n\t\tthis.y = Math.max( minVal, Math.min( maxVal, this.y ) );\n\t\tthis.z = Math.max( minVal, Math.min( maxVal, this.z ) );\n\n\t\treturn this;\n\n\t}\n\n\tclampLength( min, max ) {\n\n\t\tconst length = this.length();\n\n\t\treturn this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );\n\n\t}\n\n\tfloor() {\n\n\t\tthis.x = Math.floor( this.x );\n\t\tthis.y = Math.floor( this.y );\n\t\tthis.z = Math.floor( this.z );\n\n\t\treturn this;\n\n\t}\n\n\tceil() {\n\n\t\tthis.x = Math.ceil( this.x );\n\t\tthis.y = Math.ceil( this.y );\n\t\tthis.z = Math.ceil( this.z );\n\n\t\treturn this;\n\n\t}\n\n\tround() {\n\n\t\tthis.x = Math.round( this.x );\n\t\tthis.y = Math.round( this.y );\n\t\tthis.z = Math.round( this.z );\n\n\t\treturn this;\n\n\t}\n\n\troundToZero() {\n\n\t\tthis.x = Math.trunc( this.x );\n\t\tthis.y = Math.trunc( this.y );\n\t\tthis.z = Math.trunc( this.z );\n\n\t\treturn this;\n\n\t}\n\n\tnegate() {\n\n\t\tthis.x = - this.x;\n\t\tthis.y = - this.y;\n\t\tthis.z = - this.z;\n\n\t\treturn this;\n\n\t}\n\n\tdot( v ) {\n\n\t\treturn this.x * v.x + this.y * v.y + this.z * v.z;\n\n\t}\n\n\t// TODO lengthSquared?\n\n\tlengthSq() {\n\n\t\treturn this.x * this.x + this.y * this.y + this.z * this.z;\n\n\t}\n\n\tlength() {\n\n\t\treturn Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );\n\n\t}\n\n\tmanhattanLength() {\n\n\t\treturn Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );\n\n\t}\n\n\tnormalize() {\n\n\t\treturn this.divideScalar( this.length() || 1 );\n\n\t}\n\n\tsetLength( length ) {\n\n\t\treturn this.normalize().multiplyScalar( length );\n\n\t}\n\n\tlerp( v, alpha ) {\n\n\t\tthis.x += ( v.x - this.x ) * alpha;\n\t\tthis.y += ( v.y - this.y ) * alpha;\n\t\tthis.z += ( v.z - this.z ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tlerpVectors( v1, v2, alpha ) {\n\n\t\tthis.x = v1.x + ( v2.x - v1.x ) * alpha;\n\t\tthis.y = v1.y + ( v2.y - v1.y ) * alpha;\n\t\tthis.z = v1.z + ( v2.z - v1.z ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tcross( v ) {\n\n\t\treturn this.crossVectors( this, v );\n\n\t}\n\n\tcrossVectors( a, b ) {\n\n\t\tconst ax = a.x, ay = a.y, az = a.z;\n\t\tconst bx = b.x, by = b.y, bz = b.z;\n\n\t\tthis.x = ay * bz - az * by;\n\t\tthis.y = az * bx - ax * bz;\n\t\tthis.z = ax * by - ay * bx;\n\n\t\treturn this;\n\n\t}\n\n\tprojectOnVector( v ) {\n\n\t\tconst denominator = v.lengthSq();\n\n\t\tif ( denominator === 0 ) return this.set( 0, 0, 0 );\n\n\t\tconst scalar = v.dot( this ) / denominator;\n\n\t\treturn this.copy( v ).multiplyScalar( scalar );\n\n\t}\n\n\tprojectOnPlane( planeNormal ) {\n\n\t\t_vector$c.copy( this ).projectOnVector( planeNormal );\n\n\t\treturn this.sub( _vector$c );\n\n\t}\n\n\treflect( normal ) {\n\n\t\t// reflect incident vector off plane orthogonal to normal\n\t\t// normal is assumed to have unit length\n\n\t\treturn this.sub( _vector$c.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );\n\n\t}\n\n\tangleTo( v ) {\n\n\t\tconst denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );\n\n\t\tif ( denominator === 0 ) return Math.PI / 2;\n\n\t\tconst theta = this.dot( v ) / denominator;\n\n\t\t// clamp, to handle numerical problems\n\n\t\treturn Math.acos( clamp( theta, - 1, 1 ) );\n\n\t}\n\n\tdistanceTo( v ) {\n\n\t\treturn Math.sqrt( this.distanceToSquared( v ) );\n\n\t}\n\n\tdistanceToSquared( v ) {\n\n\t\tconst dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;\n\n\t\treturn dx * dx + dy * dy + dz * dz;\n\n\t}\n\n\tmanhattanDistanceTo( v ) {\n\n\t\treturn Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );\n\n\t}\n\n\tsetFromSpherical( s ) {\n\n\t\treturn this.setFromSphericalCoords( s.radius, s.phi, s.theta );\n\n\t}\n\n\tsetFromSphericalCoords( radius, phi, theta ) {\n\n\t\tconst sinPhiRadius = Math.sin( phi ) * radius;\n\n\t\tthis.x = sinPhiRadius * Math.sin( theta );\n\t\tthis.y = Math.cos( phi ) * radius;\n\t\tthis.z = sinPhiRadius * Math.cos( theta );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromCylindrical( c ) {\n\n\t\treturn this.setFromCylindricalCoords( c.radius, c.theta, c.y );\n\n\t}\n\n\tsetFromCylindricalCoords( radius, theta, y ) {\n\n\t\tthis.x = radius * Math.sin( theta );\n\t\tthis.y = y;\n\t\tthis.z = radius * Math.cos( theta );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromMatrixPosition( m ) {\n\n\t\tconst e = m.elements;\n\n\t\tthis.x = e[ 12 ];\n\t\tthis.y = e[ 13 ];\n\t\tthis.z = e[ 14 ];\n\n\t\treturn this;\n\n\t}\n\n\tsetFromMatrixScale( m ) {\n\n\t\tconst sx = this.setFromMatrixColumn( m, 0 ).length();\n\t\tconst sy = this.setFromMatrixColumn( m, 1 ).length();\n\t\tconst sz = this.setFromMatrixColumn( m, 2 ).length();\n\n\t\tthis.x = sx;\n\t\tthis.y = sy;\n\t\tthis.z = sz;\n\n\t\treturn this;\n\n\t}\n\n\tsetFromMatrixColumn( m, index ) {\n\n\t\treturn this.fromArray( m.elements, index * 4 );\n\n\t}\n\n\tsetFromMatrix3Column( m, index ) {\n\n\t\treturn this.fromArray( m.elements, index * 3 );\n\n\t}\n\n\tsetFromEuler( e ) {\n\n\t\tthis.x = e._x;\n\t\tthis.y = e._y;\n\t\tthis.z = e._z;\n\n\t\treturn this;\n\n\t}\n\n\tsetFromColor( c ) {\n\n\t\tthis.x = c.r;\n\t\tthis.y = c.g;\n\t\tthis.z = c.b;\n\n\t\treturn this;\n\n\t}\n\n\tequals( v ) {\n\n\t\treturn ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tthis.x = array[ offset ];\n\t\tthis.y = array[ offset + 1 ];\n\t\tthis.z = array[ offset + 2 ];\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tarray[ offset ] = this.x;\n\t\tarray[ offset + 1 ] = this.y;\n\t\tarray[ offset + 2 ] = this.z;\n\n\t\treturn array;\n\n\t}\n\n\tfromBufferAttribute( attribute, index ) {\n\n\t\tthis.x = attribute.getX( index );\n\t\tthis.y = attribute.getY( index );\n\t\tthis.z = attribute.getZ( index );\n\n\t\treturn this;\n\n\t}\n\n\trandom() {\n\n\t\tthis.x = Math.random();\n\t\tthis.y = Math.random();\n\t\tthis.z = Math.random();\n\n\t\treturn this;\n\n\t}\n\n\trandomDirection() {\n\n\t\t// Derived from https://mathworld.wolfram.com/SpherePointPicking.html\n\n\t\tconst u = ( Math.random() - 0.5 ) * 2;\n\t\tconst t = Math.random() * Math.PI * 2;\n\t\tconst f = Math.sqrt( 1 - u ** 2 );\n\n\t\tthis.x = f * Math.cos( t );\n\t\tthis.y = f * Math.sin( t );\n\t\tthis.z = u;\n\n\t\treturn this;\n\n\t}\n\n\t*[ Symbol.iterator ]() {\n\n\t\tyield this.x;\n\t\tyield this.y;\n\t\tyield this.z;\n\n\t}\n\n}\n\nconst _vector$c = /*@__PURE__*/ new Vector3();\nconst _quaternion$4 = /*@__PURE__*/ new Quaternion();\n\nclass Box3 {\n\n\tconstructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) {\n\n\t\tthis.isBox3 = true;\n\n\t\tthis.min = min;\n\t\tthis.max = max;\n\n\t}\n\n\tset( min, max ) {\n\n\t\tthis.min.copy( min );\n\t\tthis.max.copy( max );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromArray( array ) {\n\n\t\tthis.makeEmpty();\n\n\t\tfor ( let i = 0, il = array.length; i < il; i += 3 ) {\n\n\t\t\tthis.expandByPoint( _vector$b.fromArray( array, i ) );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetFromBufferAttribute( attribute ) {\n\n\t\tthis.makeEmpty();\n\n\t\tfor ( let i = 0, il = attribute.count; i < il; i ++ ) {\n\n\t\t\tthis.expandByPoint( _vector$b.fromBufferAttribute( attribute, i ) );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetFromPoints( points ) {\n\n\t\tthis.makeEmpty();\n\n\t\tfor ( let i = 0, il = points.length; i < il; i ++ ) {\n\n\t\t\tthis.expandByPoint( points[ i ] );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetFromCenterAndSize( center, size ) {\n\n\t\tconst halfSize = _vector$b.copy( size ).multiplyScalar( 0.5 );\n\n\t\tthis.min.copy( center ).sub( halfSize );\n\t\tthis.max.copy( center ).add( halfSize );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromObject( object, precise = false ) {\n\n\t\tthis.makeEmpty();\n\n\t\treturn this.expandByObject( object, precise );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( box ) {\n\n\t\tthis.min.copy( box.min );\n\t\tthis.max.copy( box.max );\n\n\t\treturn this;\n\n\t}\n\n\tmakeEmpty() {\n\n\t\tthis.min.x = this.min.y = this.min.z = + Infinity;\n\t\tthis.max.x = this.max.y = this.max.z = - Infinity;\n\n\t\treturn this;\n\n\t}\n\n\tisEmpty() {\n\n\t\t// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes\n\n\t\treturn ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );\n\n\t}\n\n\tgetCenter( target ) {\n\n\t\treturn this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );\n\n\t}\n\n\tgetSize( target ) {\n\n\t\treturn this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min );\n\n\t}\n\n\texpandByPoint( point ) {\n\n\t\tthis.min.min( point );\n\t\tthis.max.max( point );\n\n\t\treturn this;\n\n\t}\n\n\texpandByVector( vector ) {\n\n\t\tthis.min.sub( vector );\n\t\tthis.max.add( vector );\n\n\t\treturn this;\n\n\t}\n\n\texpandByScalar( scalar ) {\n\n\t\tthis.min.addScalar( - scalar );\n\t\tthis.max.addScalar( scalar );\n\n\t\treturn this;\n\n\t}\n\n\texpandByObject( object, precise = false ) {\n\n\t\t// Computes the world-axis-aligned bounding box of an object (including its children),\n\t\t// accounting for both the object's, and children's, world transforms\n\n\t\tobject.updateWorldMatrix( false, false );\n\n\t\tconst geometry = object.geometry;\n\n\t\tif ( geometry !== undefined ) {\n\n\t\t\tconst positionAttribute = geometry.getAttribute( 'position' );\n\n\t\t\t// precise AABB computation based on vertex data requires at least a position attribute.\n\t\t\t// instancing isn't supported so far and uses the normal (conservative) code path.\n\n\t\t\tif ( precise === true && positionAttribute !== undefined && object.isInstancedMesh !== true ) {\n\n\t\t\t\tfor ( let i = 0, l = positionAttribute.count; i < l; i ++ ) {\n\n\t\t\t\t\tif ( object.isMesh === true ) {\n\n\t\t\t\t\t\tobject.getVertexPosition( i, _vector$b );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t_vector$b.fromBufferAttribute( positionAttribute, i );\n\n\t\t\t\t\t}\n\n\t\t\t\t\t_vector$b.applyMatrix4( object.matrixWorld );\n\t\t\t\t\tthis.expandByPoint( _vector$b );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tif ( object.boundingBox !== undefined ) {\n\n\t\t\t\t\t// object-level bounding box\n\n\t\t\t\t\tif ( object.boundingBox === null ) {\n\n\t\t\t\t\t\tobject.computeBoundingBox();\n\n\t\t\t\t\t}\n\n\t\t\t\t\t_box$4.copy( object.boundingBox );\n\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// geometry-level bounding box\n\n\t\t\t\t\tif ( geometry.boundingBox === null ) {\n\n\t\t\t\t\t\tgeometry.computeBoundingBox();\n\n\t\t\t\t\t}\n\n\t\t\t\t\t_box$4.copy( geometry.boundingBox );\n\n\t\t\t\t}\n\n\t\t\t\t_box$4.applyMatrix4( object.matrixWorld );\n\n\t\t\t\tthis.union( _box$4 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst children = object.children;\n\n\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\tthis.expandByObject( children[ i ], precise );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tcontainsPoint( point ) {\n\n\t\treturn point.x < this.min.x || point.x > this.max.x ||\n\t\t\tpoint.y < this.min.y || point.y > this.max.y ||\n\t\t\tpoint.z < this.min.z || point.z > this.max.z ? false : true;\n\n\t}\n\n\tcontainsBox( box ) {\n\n\t\treturn this.min.x <= box.min.x && box.max.x <= this.max.x &&\n\t\t\tthis.min.y <= box.min.y && box.max.y <= this.max.y &&\n\t\t\tthis.min.z <= box.min.z && box.max.z <= this.max.z;\n\n\t}\n\n\tgetParameter( point, target ) {\n\n\t\t// This can potentially have a divide by zero if the box\n\t\t// has a size dimension of 0.\n\n\t\treturn target.set(\n\t\t\t( point.x - this.min.x ) / ( this.max.x - this.min.x ),\n\t\t\t( point.y - this.min.y ) / ( this.max.y - this.min.y ),\n\t\t\t( point.z - this.min.z ) / ( this.max.z - this.min.z )\n\t\t);\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\t// using 6 splitting planes to rule out intersections.\n\t\treturn box.max.x < this.min.x || box.min.x > this.max.x ||\n\t\t\tbox.max.y < this.min.y || box.min.y > this.max.y ||\n\t\t\tbox.max.z < this.min.z || box.min.z > this.max.z ? false : true;\n\n\t}\n\n\tintersectsSphere( sphere ) {\n\n\t\t// Find the point on the AABB closest to the sphere center.\n\t\tthis.clampPoint( sphere.center, _vector$b );\n\n\t\t// If that point is inside the sphere, the AABB and sphere intersect.\n\t\treturn _vector$b.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );\n\n\t}\n\n\tintersectsPlane( plane ) {\n\n\t\t// We compute the minimum and maximum dot product values. If those values\n\t\t// are on the same side (back or front) of the plane, then there is no intersection.\n\n\t\tlet min, max;\n\n\t\tif ( plane.normal.x > 0 ) {\n\n\t\t\tmin = plane.normal.x * this.min.x;\n\t\t\tmax = plane.normal.x * this.max.x;\n\n\t\t} else {\n\n\t\t\tmin = plane.normal.x * this.max.x;\n\t\t\tmax = plane.normal.x * this.min.x;\n\n\t\t}\n\n\t\tif ( plane.normal.y > 0 ) {\n\n\t\t\tmin += plane.normal.y * this.min.y;\n\t\t\tmax += plane.normal.y * this.max.y;\n\n\t\t} else {\n\n\t\t\tmin += plane.normal.y * this.max.y;\n\t\t\tmax += plane.normal.y * this.min.y;\n\n\t\t}\n\n\t\tif ( plane.normal.z > 0 ) {\n\n\t\t\tmin += plane.normal.z * this.min.z;\n\t\t\tmax += plane.normal.z * this.max.z;\n\n\t\t} else {\n\n\t\t\tmin += plane.normal.z * this.max.z;\n\t\t\tmax += plane.normal.z * this.min.z;\n\n\t\t}\n\n\t\treturn ( min <= - plane.constant && max >= - plane.constant );\n\n\t}\n\n\tintersectsTriangle( triangle ) {\n\n\t\tif ( this.isEmpty() ) {\n\n\t\t\treturn false;\n\n\t\t}\n\n\t\t// compute box center and extents\n\t\tthis.getCenter( _center );\n\t\t_extents.subVectors( this.max, _center );\n\n\t\t// translate triangle to aabb origin\n\t\t_v0$2.subVectors( triangle.a, _center );\n\t\t_v1$7.subVectors( triangle.b, _center );\n\t\t_v2$4.subVectors( triangle.c, _center );\n\n\t\t// compute edge vectors for triangle\n\t\t_f0.subVectors( _v1$7, _v0$2 );\n\t\t_f1.subVectors( _v2$4, _v1$7 );\n\t\t_f2.subVectors( _v0$2, _v2$4 );\n\n\t\t// test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb\n\t\t// make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation\n\t\t// axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned)\n\t\tlet axes = [\n\t\t\t0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y,\n\t\t\t_f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x,\n\t\t\t- _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0\n\t\t];\n\t\tif ( ! satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ) ) {\n\n\t\t\treturn false;\n\n\t\t}\n\n\t\t// test 3 face normals from the aabb\n\t\taxes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ];\n\t\tif ( ! satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ) ) {\n\n\t\t\treturn false;\n\n\t\t}\n\n\t\t// finally testing the face normal of the triangle\n\t\t// use already existing triangle edge vectors here\n\t\t_triangleNormal.crossVectors( _f0, _f1 );\n\t\taxes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ];\n\n\t\treturn satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents );\n\n\t}\n\n\tclampPoint( point, target ) {\n\n\t\treturn target.copy( point ).clamp( this.min, this.max );\n\n\t}\n\n\tdistanceToPoint( point ) {\n\n\t\treturn this.clampPoint( point, _vector$b ).distanceTo( point );\n\n\t}\n\n\tgetBoundingSphere( target ) {\n\n\t\tif ( this.isEmpty() ) {\n\n\t\t\ttarget.makeEmpty();\n\n\t\t} else {\n\n\t\t\tthis.getCenter( target.center );\n\n\t\t\ttarget.radius = this.getSize( _vector$b ).length() * 0.5;\n\n\t\t}\n\n\t\treturn target;\n\n\t}\n\n\tintersect( box ) {\n\n\t\tthis.min.max( box.min );\n\t\tthis.max.min( box.max );\n\n\t\t// ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values.\n\t\tif ( this.isEmpty() ) this.makeEmpty();\n\n\t\treturn this;\n\n\t}\n\n\tunion( box ) {\n\n\t\tthis.min.min( box.min );\n\t\tthis.max.max( box.max );\n\n\t\treturn this;\n\n\t}\n\n\tapplyMatrix4( matrix ) {\n\n\t\t// transform of empty box is an empty box.\n\t\tif ( this.isEmpty() ) return this;\n\n\t\t// NOTE: I am using a binary pattern to specify all 2^3 combinations below\n\t\t_points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000\n\t\t_points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001\n\t\t_points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010\n\t\t_points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011\n\t\t_points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100\n\t\t_points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101\n\t\t_points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110\n\t\t_points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111\n\n\t\tthis.setFromPoints( _points );\n\n\t\treturn this;\n\n\t}\n\n\ttranslate( offset ) {\n\n\t\tthis.min.add( offset );\n\t\tthis.max.add( offset );\n\n\t\treturn this;\n\n\t}\n\n\tequals( box ) {\n\n\t\treturn box.min.equals( this.min ) && box.max.equals( this.max );\n\n\t}\n\n}\n\nconst _points = [\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3()\n];\n\nconst _vector$b = /*@__PURE__*/ new Vector3();\n\nconst _box$4 = /*@__PURE__*/ new Box3();\n\n// triangle centered vertices\n\nconst _v0$2 = /*@__PURE__*/ new Vector3();\nconst _v1$7 = /*@__PURE__*/ new Vector3();\nconst _v2$4 = /*@__PURE__*/ new Vector3();\n\n// triangle edge vectors\n\nconst _f0 = /*@__PURE__*/ new Vector3();\nconst _f1 = /*@__PURE__*/ new Vector3();\nconst _f2 = /*@__PURE__*/ new Vector3();\n\nconst _center = /*@__PURE__*/ new Vector3();\nconst _extents = /*@__PURE__*/ new Vector3();\nconst _triangleNormal = /*@__PURE__*/ new Vector3();\nconst _testAxis = /*@__PURE__*/ new Vector3();\n\nfunction satForAxes( axes, v0, v1, v2, extents ) {\n\n\tfor ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) {\n\n\t\t_testAxis.fromArray( axes, i );\n\t\t// project the aabb onto the separating axis\n\t\tconst r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z );\n\t\t// project all 3 vertices of the triangle onto the separating axis\n\t\tconst p0 = v0.dot( _testAxis );\n\t\tconst p1 = v1.dot( _testAxis );\n\t\tconst p2 = v2.dot( _testAxis );\n\t\t// actual test, basically see if either of the most extreme of the triangle points intersects r\n\t\tif ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {\n\n\t\t\t// points of the projected triangle are outside the projected half-length of the aabb\n\t\t\t// the axis is separating and we can exit\n\t\t\treturn false;\n\n\t\t}\n\n\t}\n\n\treturn true;\n\n}\n\nconst _box$3 = /*@__PURE__*/ new Box3();\nconst _v1$6 = /*@__PURE__*/ new Vector3();\nconst _v2$3 = /*@__PURE__*/ new Vector3();\n\nclass Sphere {\n\n\tconstructor( center = new Vector3(), radius = - 1 ) {\n\n\t\tthis.isSphere = true;\n\n\t\tthis.center = center;\n\t\tthis.radius = radius;\n\n\t}\n\n\tset( center, radius ) {\n\n\t\tthis.center.copy( center );\n\t\tthis.radius = radius;\n\n\t\treturn this;\n\n\t}\n\n\tsetFromPoints( points, optionalCenter ) {\n\n\t\tconst center = this.center;\n\n\t\tif ( optionalCenter !== undefined ) {\n\n\t\t\tcenter.copy( optionalCenter );\n\n\t\t} else {\n\n\t\t\t_box$3.setFromPoints( points ).getCenter( center );\n\n\t\t}\n\n\t\tlet maxRadiusSq = 0;\n\n\t\tfor ( let i = 0, il = points.length; i < il; i ++ ) {\n\n\t\t\tmaxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) );\n\n\t\t}\n\n\t\tthis.radius = Math.sqrt( maxRadiusSq );\n\n\t\treturn this;\n\n\t}\n\n\tcopy( sphere ) {\n\n\t\tthis.center.copy( sphere.center );\n\t\tthis.radius = sphere.radius;\n\n\t\treturn this;\n\n\t}\n\n\tisEmpty() {\n\n\t\treturn ( this.radius < 0 );\n\n\t}\n\n\tmakeEmpty() {\n\n\t\tthis.center.set( 0, 0, 0 );\n\t\tthis.radius = - 1;\n\n\t\treturn this;\n\n\t}\n\n\tcontainsPoint( point ) {\n\n\t\treturn ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );\n\n\t}\n\n\tdistanceToPoint( point ) {\n\n\t\treturn ( point.distanceTo( this.center ) - this.radius );\n\n\t}\n\n\tintersectsSphere( sphere ) {\n\n\t\tconst radiusSum = this.radius + sphere.radius;\n\n\t\treturn sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\treturn box.intersectsSphere( this );\n\n\t}\n\n\tintersectsPlane( plane ) {\n\n\t\treturn Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius;\n\n\t}\n\n\tclampPoint( point, target ) {\n\n\t\tconst deltaLengthSq = this.center.distanceToSquared( point );\n\n\t\ttarget.copy( point );\n\n\t\tif ( deltaLengthSq > ( this.radius * this.radius ) ) {\n\n\t\t\ttarget.sub( this.center ).normalize();\n\t\t\ttarget.multiplyScalar( this.radius ).add( this.center );\n\n\t\t}\n\n\t\treturn target;\n\n\t}\n\n\tgetBoundingBox( target ) {\n\n\t\tif ( this.isEmpty() ) {\n\n\t\t\t// Empty sphere produces empty bounding box\n\t\t\ttarget.makeEmpty();\n\t\t\treturn target;\n\n\t\t}\n\n\t\ttarget.set( this.center, this.center );\n\t\ttarget.expandByScalar( this.radius );\n\n\t\treturn target;\n\n\t}\n\n\tapplyMatrix4( matrix ) {\n\n\t\tthis.center.applyMatrix4( matrix );\n\t\tthis.radius = this.radius * matrix.getMaxScaleOnAxis();\n\n\t\treturn this;\n\n\t}\n\n\ttranslate( offset ) {\n\n\t\tthis.center.add( offset );\n\n\t\treturn this;\n\n\t}\n\n\texpandByPoint( point ) {\n\n\t\tif ( this.isEmpty() ) {\n\n\t\t\tthis.center.copy( point );\n\n\t\t\tthis.radius = 0;\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\t_v1$6.subVectors( point, this.center );\n\n\t\tconst lengthSq = _v1$6.lengthSq();\n\n\t\tif ( lengthSq > ( this.radius * this.radius ) ) {\n\n\t\t\t// calculate the minimal sphere\n\n\t\t\tconst length = Math.sqrt( lengthSq );\n\n\t\t\tconst delta = ( length - this.radius ) * 0.5;\n\n\t\t\tthis.center.addScaledVector( _v1$6, delta / length );\n\n\t\t\tthis.radius += delta;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tunion( sphere ) {\n\n\t\tif ( sphere.isEmpty() ) {\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tif ( this.isEmpty() ) {\n\n\t\t\tthis.copy( sphere );\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tif ( this.center.equals( sphere.center ) === true ) {\n\n\t\t\t this.radius = Math.max( this.radius, sphere.radius );\n\n\t\t} else {\n\n\t\t\t_v2$3.subVectors( sphere.center, this.center ).setLength( sphere.radius );\n\n\t\t\tthis.expandByPoint( _v1$6.copy( sphere.center ).add( _v2$3 ) );\n\n\t\t\tthis.expandByPoint( _v1$6.copy( sphere.center ).sub( _v2$3 ) );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tequals( sphere ) {\n\n\t\treturn sphere.center.equals( this.center ) && ( sphere.radius === this.radius );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\nconst _vector$a = /*@__PURE__*/ new Vector3();\nconst _segCenter = /*@__PURE__*/ new Vector3();\nconst _segDir = /*@__PURE__*/ new Vector3();\nconst _diff = /*@__PURE__*/ new Vector3();\n\nconst _edge1 = /*@__PURE__*/ new Vector3();\nconst _edge2 = /*@__PURE__*/ new Vector3();\nconst _normal$1 = /*@__PURE__*/ new Vector3();\n\nclass Ray {\n\n\tconstructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) {\n\n\t\tthis.origin = origin;\n\t\tthis.direction = direction;\n\n\t}\n\n\tset( origin, direction ) {\n\n\t\tthis.origin.copy( origin );\n\t\tthis.direction.copy( direction );\n\n\t\treturn this;\n\n\t}\n\n\tcopy( ray ) {\n\n\t\tthis.origin.copy( ray.origin );\n\t\tthis.direction.copy( ray.direction );\n\n\t\treturn this;\n\n\t}\n\n\tat( t, target ) {\n\n\t\treturn target.copy( this.origin ).addScaledVector( this.direction, t );\n\n\t}\n\n\tlookAt( v ) {\n\n\t\tthis.direction.copy( v ).sub( this.origin ).normalize();\n\n\t\treturn this;\n\n\t}\n\n\trecast( t ) {\n\n\t\tthis.origin.copy( this.at( t, _vector$a ) );\n\n\t\treturn this;\n\n\t}\n\n\tclosestPointToPoint( point, target ) {\n\n\t\ttarget.subVectors( point, this.origin );\n\n\t\tconst directionDistance = target.dot( this.direction );\n\n\t\tif ( directionDistance < 0 ) {\n\n\t\t\treturn target.copy( this.origin );\n\n\t\t}\n\n\t\treturn target.copy( this.origin ).addScaledVector( this.direction, directionDistance );\n\n\t}\n\n\tdistanceToPoint( point ) {\n\n\t\treturn Math.sqrt( this.distanceSqToPoint( point ) );\n\n\t}\n\n\tdistanceSqToPoint( point ) {\n\n\t\tconst directionDistance = _vector$a.subVectors( point, this.origin ).dot( this.direction );\n\n\t\t// point behind the ray\n\n\t\tif ( directionDistance < 0 ) {\n\n\t\t\treturn this.origin.distanceToSquared( point );\n\n\t\t}\n\n\t\t_vector$a.copy( this.origin ).addScaledVector( this.direction, directionDistance );\n\n\t\treturn _vector$a.distanceToSquared( point );\n\n\t}\n\n\tdistanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {\n\n\t\t// from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h\n\t\t// It returns the min distance between the ray and the segment\n\t\t// defined by v0 and v1\n\t\t// It can also set two optional targets :\n\t\t// - The closest point on the ray\n\t\t// - The closest point on the segment\n\n\t\t_segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );\n\t\t_segDir.copy( v1 ).sub( v0 ).normalize();\n\t\t_diff.copy( this.origin ).sub( _segCenter );\n\n\t\tconst segExtent = v0.distanceTo( v1 ) * 0.5;\n\t\tconst a01 = - this.direction.dot( _segDir );\n\t\tconst b0 = _diff.dot( this.direction );\n\t\tconst b1 = - _diff.dot( _segDir );\n\t\tconst c = _diff.lengthSq();\n\t\tconst det = Math.abs( 1 - a01 * a01 );\n\t\tlet s0, s1, sqrDist, extDet;\n\n\t\tif ( det > 0 ) {\n\n\t\t\t// The ray and segment are not parallel.\n\n\t\t\ts0 = a01 * b1 - b0;\n\t\t\ts1 = a01 * b0 - b1;\n\t\t\textDet = segExtent * det;\n\n\t\t\tif ( s0 >= 0 ) {\n\n\t\t\t\tif ( s1 >= - extDet ) {\n\n\t\t\t\t\tif ( s1 <= extDet ) {\n\n\t\t\t\t\t\t// region 0\n\t\t\t\t\t\t// Minimum at interior points of ray and segment.\n\n\t\t\t\t\t\tconst invDet = 1 / det;\n\t\t\t\t\t\ts0 *= invDet;\n\t\t\t\t\t\ts1 *= invDet;\n\t\t\t\t\t\tsqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// region 1\n\n\t\t\t\t\t\ts1 = segExtent;\n\t\t\t\t\t\ts0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n\t\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// region 5\n\n\t\t\t\t\ts1 = - segExtent;\n\t\t\t\t\ts0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tif ( s1 <= - extDet ) {\n\n\t\t\t\t\t// region 4\n\n\t\t\t\t\ts0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );\n\t\t\t\t\ts1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );\n\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t} else if ( s1 <= extDet ) {\n\n\t\t\t\t\t// region 3\n\n\t\t\t\t\ts0 = 0;\n\t\t\t\t\ts1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );\n\t\t\t\t\tsqrDist = s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// region 2\n\n\t\t\t\t\ts0 = Math.max( 0, - ( a01 * segExtent + b0 ) );\n\t\t\t\t\ts1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );\n\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// Ray and segment are parallel.\n\n\t\t\ts1 = ( a01 > 0 ) ? - segExtent : segExtent;\n\t\t\ts0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t}\n\n\t\tif ( optionalPointOnRay ) {\n\n\t\t\toptionalPointOnRay.copy( this.origin ).addScaledVector( this.direction, s0 );\n\n\t\t}\n\n\t\tif ( optionalPointOnSegment ) {\n\n\t\t\toptionalPointOnSegment.copy( _segCenter ).addScaledVector( _segDir, s1 );\n\n\t\t}\n\n\t\treturn sqrDist;\n\n\t}\n\n\tintersectSphere( sphere, target ) {\n\n\t\t_vector$a.subVectors( sphere.center, this.origin );\n\t\tconst tca = _vector$a.dot( this.direction );\n\t\tconst d2 = _vector$a.dot( _vector$a ) - tca * tca;\n\t\tconst radius2 = sphere.radius * sphere.radius;\n\n\t\tif ( d2 > radius2 ) return null;\n\n\t\tconst thc = Math.sqrt( radius2 - d2 );\n\n\t\t// t0 = first intersect point - entrance on front of sphere\n\t\tconst t0 = tca - thc;\n\n\t\t// t1 = second intersect point - exit point on back of sphere\n\t\tconst t1 = tca + thc;\n\n\t\t// test to see if t1 is behind the ray - if so, return null\n\t\tif ( t1 < 0 ) return null;\n\n\t\t// test to see if t0 is behind the ray:\n\t\t// if it is, the ray is inside the sphere, so return the second exit point scaled by t1,\n\t\t// in order to always return an intersect point that is in front of the ray.\n\t\tif ( t0 < 0 ) return this.at( t1, target );\n\n\t\t// else t0 is in front of the ray, so return the first collision point scaled by t0\n\t\treturn this.at( t0, target );\n\n\t}\n\n\tintersectsSphere( sphere ) {\n\n\t\treturn this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius );\n\n\t}\n\n\tdistanceToPlane( plane ) {\n\n\t\tconst denominator = plane.normal.dot( this.direction );\n\n\t\tif ( denominator === 0 ) {\n\n\t\t\t// line is coplanar, return origin\n\t\t\tif ( plane.distanceToPoint( this.origin ) === 0 ) {\n\n\t\t\t\treturn 0;\n\n\t\t\t}\n\n\t\t\t// Null is preferable to undefined since undefined means.... it is undefined\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\tconst t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;\n\n\t\t// Return if the ray never intersects the plane\n\n\t\treturn t >= 0 ? t : null;\n\n\t}\n\n\tintersectPlane( plane, target ) {\n\n\t\tconst t = this.distanceToPlane( plane );\n\n\t\tif ( t === null ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\treturn this.at( t, target );\n\n\t}\n\n\tintersectsPlane( plane ) {\n\n\t\t// check if the ray lies on the plane first\n\n\t\tconst distToPoint = plane.distanceToPoint( this.origin );\n\n\t\tif ( distToPoint === 0 ) {\n\n\t\t\treturn true;\n\n\t\t}\n\n\t\tconst denominator = plane.normal.dot( this.direction );\n\n\t\tif ( denominator * distToPoint < 0 ) {\n\n\t\t\treturn true;\n\n\t\t}\n\n\t\t// ray origin is behind the plane (and is pointing behind it)\n\n\t\treturn false;\n\n\t}\n\n\tintersectBox( box, target ) {\n\n\t\tlet tmin, tmax, tymin, tymax, tzmin, tzmax;\n\n\t\tconst invdirx = 1 / this.direction.x,\n\t\t\tinvdiry = 1 / this.direction.y,\n\t\t\tinvdirz = 1 / this.direction.z;\n\n\t\tconst origin = this.origin;\n\n\t\tif ( invdirx >= 0 ) {\n\n\t\t\ttmin = ( box.min.x - origin.x ) * invdirx;\n\t\t\ttmax = ( box.max.x - origin.x ) * invdirx;\n\n\t\t} else {\n\n\t\t\ttmin = ( box.max.x - origin.x ) * invdirx;\n\t\t\ttmax = ( box.min.x - origin.x ) * invdirx;\n\n\t\t}\n\n\t\tif ( invdiry >= 0 ) {\n\n\t\t\ttymin = ( box.min.y - origin.y ) * invdiry;\n\t\t\ttymax = ( box.max.y - origin.y ) * invdiry;\n\n\t\t} else {\n\n\t\t\ttymin = ( box.max.y - origin.y ) * invdiry;\n\t\t\ttymax = ( box.min.y - origin.y ) * invdiry;\n\n\t\t}\n\n\t\tif ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;\n\n\t\tif ( tymin > tmin || isNaN( tmin ) ) tmin = tymin;\n\n\t\tif ( tymax < tmax || isNaN( tmax ) ) tmax = tymax;\n\n\t\tif ( invdirz >= 0 ) {\n\n\t\t\ttzmin = ( box.min.z - origin.z ) * invdirz;\n\t\t\ttzmax = ( box.max.z - origin.z ) * invdirz;\n\n\t\t} else {\n\n\t\t\ttzmin = ( box.max.z - origin.z ) * invdirz;\n\t\t\ttzmax = ( box.min.z - origin.z ) * invdirz;\n\n\t\t}\n\n\t\tif ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;\n\n\t\tif ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;\n\n\t\tif ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;\n\n\t\t//return point closest to the ray (positive side)\n\n\t\tif ( tmax < 0 ) return null;\n\n\t\treturn this.at( tmin >= 0 ? tmin : tmax, target );\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\treturn this.intersectBox( box, _vector$a ) !== null;\n\n\t}\n\n\tintersectTriangle( a, b, c, backfaceCulling, target ) {\n\n\t\t// Compute the offset origin, edges, and normal.\n\n\t\t// from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h\n\n\t\t_edge1.subVectors( b, a );\n\t\t_edge2.subVectors( c, a );\n\t\t_normal$1.crossVectors( _edge1, _edge2 );\n\n\t\t// Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,\n\t\t// E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by\n\t\t//   |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))\n\t\t//   |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))\n\t\t//   |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)\n\t\tlet DdN = this.direction.dot( _normal$1 );\n\t\tlet sign;\n\n\t\tif ( DdN > 0 ) {\n\n\t\t\tif ( backfaceCulling ) return null;\n\t\t\tsign = 1;\n\n\t\t} else if ( DdN < 0 ) {\n\n\t\t\tsign = - 1;\n\t\t\tDdN = - DdN;\n\n\t\t} else {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\t_diff.subVectors( this.origin, a );\n\t\tconst DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) );\n\n\t\t// b1 < 0, no intersection\n\t\tif ( DdQxE2 < 0 ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\tconst DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) );\n\n\t\t// b2 < 0, no intersection\n\t\tif ( DdE1xQ < 0 ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\t// b1+b2 > 1, no intersection\n\t\tif ( DdQxE2 + DdE1xQ > DdN ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\t// Line intersects triangle, check if ray does.\n\t\tconst QdN = - sign * _diff.dot( _normal$1 );\n\n\t\t// t < 0, no intersection\n\t\tif ( QdN < 0 ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\t// Ray intersects triangle.\n\t\treturn this.at( QdN / DdN, target );\n\n\t}\n\n\tapplyMatrix4( matrix4 ) {\n\n\t\tthis.origin.applyMatrix4( matrix4 );\n\t\tthis.direction.transformDirection( matrix4 );\n\n\t\treturn this;\n\n\t}\n\n\tequals( ray ) {\n\n\t\treturn ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\nclass Matrix4 {\n\n\tconstructor( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {\n\n\t\tMatrix4.prototype.isMatrix4 = true;\n\n\t\tthis.elements = [\n\n\t\t\t1, 0, 0, 0,\n\t\t\t0, 1, 0, 0,\n\t\t\t0, 0, 1, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t];\n\n\t\tif ( n11 !== undefined ) {\n\n\t\t\tthis.set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 );\n\n\t\t}\n\n\t}\n\n\tset( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {\n\n\t\tconst te = this.elements;\n\n\t\tte[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;\n\t\tte[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;\n\t\tte[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;\n\t\tte[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;\n\n\t\treturn this;\n\n\t}\n\n\tidentity() {\n\n\t\tthis.set(\n\n\t\t\t1, 0, 0, 0,\n\t\t\t0, 1, 0, 0,\n\t\t\t0, 0, 1, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new Matrix4().fromArray( this.elements );\n\n\t}\n\n\tcopy( m ) {\n\n\t\tconst te = this.elements;\n\t\tconst me = m.elements;\n\n\t\tte[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];\n\t\tte[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];\n\t\tte[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];\n\t\tte[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];\n\n\t\treturn this;\n\n\t}\n\n\tcopyPosition( m ) {\n\n\t\tconst te = this.elements, me = m.elements;\n\n\t\tte[ 12 ] = me[ 12 ];\n\t\tte[ 13 ] = me[ 13 ];\n\t\tte[ 14 ] = me[ 14 ];\n\n\t\treturn this;\n\n\t}\n\n\tsetFromMatrix3( m ) {\n\n\t\tconst me = m.elements;\n\n\t\tthis.set(\n\n\t\t\tme[ 0 ], me[ 3 ], me[ 6 ], 0,\n\t\t\tme[ 1 ], me[ 4 ], me[ 7 ], 0,\n\t\t\tme[ 2 ], me[ 5 ], me[ 8 ], 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\textractBasis( xAxis, yAxis, zAxis ) {\n\n\t\txAxis.setFromMatrixColumn( this, 0 );\n\t\tyAxis.setFromMatrixColumn( this, 1 );\n\t\tzAxis.setFromMatrixColumn( this, 2 );\n\n\t\treturn this;\n\n\t}\n\n\tmakeBasis( xAxis, yAxis, zAxis ) {\n\n\t\tthis.set(\n\t\t\txAxis.x, yAxis.x, zAxis.x, 0,\n\t\t\txAxis.y, yAxis.y, zAxis.y, 0,\n\t\t\txAxis.z, yAxis.z, zAxis.z, 0,\n\t\t\t0, 0, 0, 1\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\textractRotation( m ) {\n\n\t\t// this method does not support reflection matrices\n\n\t\tconst te = this.elements;\n\t\tconst me = m.elements;\n\n\t\tconst scaleX = 1 / _v1$5.setFromMatrixColumn( m, 0 ).length();\n\t\tconst scaleY = 1 / _v1$5.setFromMatrixColumn( m, 1 ).length();\n\t\tconst scaleZ = 1 / _v1$5.setFromMatrixColumn( m, 2 ).length();\n\n\t\tte[ 0 ] = me[ 0 ] * scaleX;\n\t\tte[ 1 ] = me[ 1 ] * scaleX;\n\t\tte[ 2 ] = me[ 2 ] * scaleX;\n\t\tte[ 3 ] = 0;\n\n\t\tte[ 4 ] = me[ 4 ] * scaleY;\n\t\tte[ 5 ] = me[ 5 ] * scaleY;\n\t\tte[ 6 ] = me[ 6 ] * scaleY;\n\t\tte[ 7 ] = 0;\n\n\t\tte[ 8 ] = me[ 8 ] * scaleZ;\n\t\tte[ 9 ] = me[ 9 ] * scaleZ;\n\t\tte[ 10 ] = me[ 10 ] * scaleZ;\n\t\tte[ 11 ] = 0;\n\n\t\tte[ 12 ] = 0;\n\t\tte[ 13 ] = 0;\n\t\tte[ 14 ] = 0;\n\t\tte[ 15 ] = 1;\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotationFromEuler( euler ) {\n\n\t\tconst te = this.elements;\n\n\t\tconst x = euler.x, y = euler.y, z = euler.z;\n\t\tconst a = Math.cos( x ), b = Math.sin( x );\n\t\tconst c = Math.cos( y ), d = Math.sin( y );\n\t\tconst e = Math.cos( z ), f = Math.sin( z );\n\n\t\tif ( euler.order === 'XYZ' ) {\n\n\t\t\tconst ae = a * e, af = a * f, be = b * e, bf = b * f;\n\n\t\t\tte[ 0 ] = c * e;\n\t\t\tte[ 4 ] = - c * f;\n\t\t\tte[ 8 ] = d;\n\n\t\t\tte[ 1 ] = af + be * d;\n\t\t\tte[ 5 ] = ae - bf * d;\n\t\t\tte[ 9 ] = - b * c;\n\n\t\t\tte[ 2 ] = bf - ae * d;\n\t\t\tte[ 6 ] = be + af * d;\n\t\t\tte[ 10 ] = a * c;\n\n\t\t} else if ( euler.order === 'YXZ' ) {\n\n\t\t\tconst ce = c * e, cf = c * f, de = d * e, df = d * f;\n\n\t\t\tte[ 0 ] = ce + df * b;\n\t\t\tte[ 4 ] = de * b - cf;\n\t\t\tte[ 8 ] = a * d;\n\n\t\t\tte[ 1 ] = a * f;\n\t\t\tte[ 5 ] = a * e;\n\t\t\tte[ 9 ] = - b;\n\n\t\t\tte[ 2 ] = cf * b - de;\n\t\t\tte[ 6 ] = df + ce * b;\n\t\t\tte[ 10 ] = a * c;\n\n\t\t} else if ( euler.order === 'ZXY' ) {\n\n\t\t\tconst ce = c * e, cf = c * f, de = d * e, df = d * f;\n\n\t\t\tte[ 0 ] = ce - df * b;\n\t\t\tte[ 4 ] = - a * f;\n\t\t\tte[ 8 ] = de + cf * b;\n\n\t\t\tte[ 1 ] = cf + de * b;\n\t\t\tte[ 5 ] = a * e;\n\t\t\tte[ 9 ] = df - ce * b;\n\n\t\t\tte[ 2 ] = - a * d;\n\t\t\tte[ 6 ] = b;\n\t\t\tte[ 10 ] = a * c;\n\n\t\t} else if ( euler.order === 'ZYX' ) {\n\n\t\t\tconst ae = a * e, af = a * f, be = b * e, bf = b * f;\n\n\t\t\tte[ 0 ] = c * e;\n\t\t\tte[ 4 ] = be * d - af;\n\t\t\tte[ 8 ] = ae * d + bf;\n\n\t\t\tte[ 1 ] = c * f;\n\t\t\tte[ 5 ] = bf * d + ae;\n\t\t\tte[ 9 ] = af * d - be;\n\n\t\t\tte[ 2 ] = - d;\n\t\t\tte[ 6 ] = b * c;\n\t\t\tte[ 10 ] = a * c;\n\n\t\t} else if ( euler.order === 'YZX' ) {\n\n\t\t\tconst ac = a * c, ad = a * d, bc = b * c, bd = b * d;\n\n\t\t\tte[ 0 ] = c * e;\n\t\t\tte[ 4 ] = bd - ac * f;\n\t\t\tte[ 8 ] = bc * f + ad;\n\n\t\t\tte[ 1 ] = f;\n\t\t\tte[ 5 ] = a * e;\n\t\t\tte[ 9 ] = - b * e;\n\n\t\t\tte[ 2 ] = - d * e;\n\t\t\tte[ 6 ] = ad * f + bc;\n\t\t\tte[ 10 ] = ac - bd * f;\n\n\t\t} else if ( euler.order === 'XZY' ) {\n\n\t\t\tconst ac = a * c, ad = a * d, bc = b * c, bd = b * d;\n\n\t\t\tte[ 0 ] = c * e;\n\t\t\tte[ 4 ] = - f;\n\t\t\tte[ 8 ] = d * e;\n\n\t\t\tte[ 1 ] = ac * f + bd;\n\t\t\tte[ 5 ] = a * e;\n\t\t\tte[ 9 ] = ad * f - bc;\n\n\t\t\tte[ 2 ] = bc * f - ad;\n\t\t\tte[ 6 ] = b * e;\n\t\t\tte[ 10 ] = bd * f + ac;\n\n\t\t}\n\n\t\t// bottom row\n\t\tte[ 3 ] = 0;\n\t\tte[ 7 ] = 0;\n\t\tte[ 11 ] = 0;\n\n\t\t// last column\n\t\tte[ 12 ] = 0;\n\t\tte[ 13 ] = 0;\n\t\tte[ 14 ] = 0;\n\t\tte[ 15 ] = 1;\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotationFromQuaternion( q ) {\n\n\t\treturn this.compose( _zero, q, _one );\n\n\t}\n\n\tlookAt( eye, target, up ) {\n\n\t\tconst te = this.elements;\n\n\t\t_z.subVectors( eye, target );\n\n\t\tif ( _z.lengthSq() === 0 ) {\n\n\t\t\t// eye and target are in the same position\n\n\t\t\t_z.z = 1;\n\n\t\t}\n\n\t\t_z.normalize();\n\t\t_x.crossVectors( up, _z );\n\n\t\tif ( _x.lengthSq() === 0 ) {\n\n\t\t\t// up and z are parallel\n\n\t\t\tif ( Math.abs( up.z ) === 1 ) {\n\n\t\t\t\t_z.x += 0.0001;\n\n\t\t\t} else {\n\n\t\t\t\t_z.z += 0.0001;\n\n\t\t\t}\n\n\t\t\t_z.normalize();\n\t\t\t_x.crossVectors( up, _z );\n\n\t\t}\n\n\t\t_x.normalize();\n\t\t_y.crossVectors( _z, _x );\n\n\t\tte[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x;\n\t\tte[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y;\n\t\tte[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z;\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( m ) {\n\n\t\treturn this.multiplyMatrices( this, m );\n\n\t}\n\n\tpremultiply( m ) {\n\n\t\treturn this.multiplyMatrices( m, this );\n\n\t}\n\n\tmultiplyMatrices( a, b ) {\n\n\t\tconst ae = a.elements;\n\t\tconst be = b.elements;\n\t\tconst te = this.elements;\n\n\t\tconst a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];\n\t\tconst a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];\n\t\tconst a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];\n\t\tconst a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];\n\n\t\tconst b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];\n\t\tconst b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];\n\t\tconst b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];\n\t\tconst b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];\n\n\t\tte[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;\n\t\tte[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;\n\t\tte[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;\n\t\tte[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;\n\n\t\tte[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;\n\t\tte[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;\n\t\tte[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;\n\t\tte[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;\n\n\t\tte[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;\n\t\tte[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;\n\t\tte[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;\n\t\tte[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;\n\n\t\tte[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;\n\t\tte[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;\n\t\tte[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;\n\t\tte[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyScalar( s ) {\n\n\t\tconst te = this.elements;\n\n\t\tte[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;\n\t\tte[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;\n\t\tte[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;\n\t\tte[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;\n\n\t\treturn this;\n\n\t}\n\n\tdeterminant() {\n\n\t\tconst te = this.elements;\n\n\t\tconst n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];\n\t\tconst n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];\n\t\tconst n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];\n\t\tconst n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];\n\n\t\t//TODO: make this more efficient\n\t\t//( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )\n\n\t\treturn (\n\t\t\tn41 * (\n\t\t\t\t+ n14 * n23 * n32\n\t\t\t\t - n13 * n24 * n32\n\t\t\t\t - n14 * n22 * n33\n\t\t\t\t + n12 * n24 * n33\n\t\t\t\t + n13 * n22 * n34\n\t\t\t\t - n12 * n23 * n34\n\t\t\t) +\n\t\t\tn42 * (\n\t\t\t\t+ n11 * n23 * n34\n\t\t\t\t - n11 * n24 * n33\n\t\t\t\t + n14 * n21 * n33\n\t\t\t\t - n13 * n21 * n34\n\t\t\t\t + n13 * n24 * n31\n\t\t\t\t - n14 * n23 * n31\n\t\t\t) +\n\t\t\tn43 * (\n\t\t\t\t+ n11 * n24 * n32\n\t\t\t\t - n11 * n22 * n34\n\t\t\t\t - n14 * n21 * n32\n\t\t\t\t + n12 * n21 * n34\n\t\t\t\t + n14 * n22 * n31\n\t\t\t\t - n12 * n24 * n31\n\t\t\t) +\n\t\t\tn44 * (\n\t\t\t\t- n13 * n22 * n31\n\t\t\t\t - n11 * n23 * n32\n\t\t\t\t + n11 * n22 * n33\n\t\t\t\t + n13 * n21 * n32\n\t\t\t\t - n12 * n21 * n33\n\t\t\t\t + n12 * n23 * n31\n\t\t\t)\n\n\t\t);\n\n\t}\n\n\ttranspose() {\n\n\t\tconst te = this.elements;\n\t\tlet tmp;\n\n\t\ttmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;\n\t\ttmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;\n\t\ttmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;\n\n\t\ttmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;\n\t\ttmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;\n\t\ttmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;\n\n\t\treturn this;\n\n\t}\n\n\tsetPosition( x, y, z ) {\n\n\t\tconst te = this.elements;\n\n\t\tif ( x.isVector3 ) {\n\n\t\t\tte[ 12 ] = x.x;\n\t\t\tte[ 13 ] = x.y;\n\t\t\tte[ 14 ] = x.z;\n\n\t\t} else {\n\n\t\t\tte[ 12 ] = x;\n\t\t\tte[ 13 ] = y;\n\t\t\tte[ 14 ] = z;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tinvert() {\n\n\t\t// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm\n\t\tconst te = this.elements,\n\n\t\t\tn11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ],\n\t\t\tn12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ],\n\t\t\tn13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ],\n\t\t\tn14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ],\n\n\t\t\tt11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,\n\t\t\tt12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,\n\t\t\tt13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,\n\t\t\tt14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;\n\n\t\tconst det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;\n\n\t\tif ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );\n\n\t\tconst detInv = 1 / det;\n\n\t\tte[ 0 ] = t11 * detInv;\n\t\tte[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;\n\t\tte[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;\n\t\tte[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;\n\n\t\tte[ 4 ] = t12 * detInv;\n\t\tte[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;\n\t\tte[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;\n\t\tte[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;\n\n\t\tte[ 8 ] = t13 * detInv;\n\t\tte[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;\n\t\tte[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;\n\t\tte[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;\n\n\t\tte[ 12 ] = t14 * detInv;\n\t\tte[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;\n\t\tte[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;\n\t\tte[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;\n\n\t\treturn this;\n\n\t}\n\n\tscale( v ) {\n\n\t\tconst te = this.elements;\n\t\tconst x = v.x, y = v.y, z = v.z;\n\n\t\tte[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;\n\t\tte[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;\n\t\tte[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;\n\t\tte[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;\n\n\t\treturn this;\n\n\t}\n\n\tgetMaxScaleOnAxis() {\n\n\t\tconst te = this.elements;\n\n\t\tconst scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];\n\t\tconst scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];\n\t\tconst scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];\n\n\t\treturn Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );\n\n\t}\n\n\tmakeTranslation( x, y, z ) {\n\n\t\tif ( x.isVector3 ) {\n\n\t\t\tthis.set(\n\n\t\t\t\t1, 0, 0, x.x,\n\t\t\t\t0, 1, 0, x.y,\n\t\t\t\t0, 0, 1, x.z,\n\t\t\t\t0, 0, 0, 1\n\n\t\t\t);\n\n\t\t} else {\n\n\t\t\tthis.set(\n\n\t\t\t\t1, 0, 0, x,\n\t\t\t\t0, 1, 0, y,\n\t\t\t\t0, 0, 1, z,\n\t\t\t\t0, 0, 0, 1\n\n\t\t\t);\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotationX( theta ) {\n\n\t\tconst c = Math.cos( theta ), s = Math.sin( theta );\n\n\t\tthis.set(\n\n\t\t\t1, 0, 0, 0,\n\t\t\t0, c, - s, 0,\n\t\t\t0, s, c, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotationY( theta ) {\n\n\t\tconst c = Math.cos( theta ), s = Math.sin( theta );\n\n\t\tthis.set(\n\n\t\t\t c, 0, s, 0,\n\t\t\t 0, 1, 0, 0,\n\t\t\t- s, 0, c, 0,\n\t\t\t 0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotationZ( theta ) {\n\n\t\tconst c = Math.cos( theta ), s = Math.sin( theta );\n\n\t\tthis.set(\n\n\t\t\tc, - s, 0, 0,\n\t\t\ts, c, 0, 0,\n\t\t\t0, 0, 1, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotationAxis( axis, angle ) {\n\n\t\t// Based on http://www.gamedev.net/reference/articles/article1199.asp\n\n\t\tconst c = Math.cos( angle );\n\t\tconst s = Math.sin( angle );\n\t\tconst t = 1 - c;\n\t\tconst x = axis.x, y = axis.y, z = axis.z;\n\t\tconst tx = t * x, ty = t * y;\n\n\t\tthis.set(\n\n\t\t\ttx * x + c, tx * y - s * z, tx * z + s * y, 0,\n\t\t\ttx * y + s * z, ty * y + c, ty * z - s * x, 0,\n\t\t\ttx * z - s * y, ty * z + s * x, t * z * z + c, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmakeScale( x, y, z ) {\n\n\t\tthis.set(\n\n\t\t\tx, 0, 0, 0,\n\t\t\t0, y, 0, 0,\n\t\t\t0, 0, z, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmakeShear( xy, xz, yx, yz, zx, zy ) {\n\n\t\tthis.set(\n\n\t\t\t1, yx, zx, 0,\n\t\t\txy, 1, zy, 0,\n\t\t\txz, yz, 1, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tcompose( position, quaternion, scale ) {\n\n\t\tconst te = this.elements;\n\n\t\tconst x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w;\n\t\tconst x2 = x + x,\ty2 = y + y, z2 = z + z;\n\t\tconst xx = x * x2, xy = x * y2, xz = x * z2;\n\t\tconst yy = y * y2, yz = y * z2, zz = z * z2;\n\t\tconst wx = w * x2, wy = w * y2, wz = w * z2;\n\n\t\tconst sx = scale.x, sy = scale.y, sz = scale.z;\n\n\t\tte[ 0 ] = ( 1 - ( yy + zz ) ) * sx;\n\t\tte[ 1 ] = ( xy + wz ) * sx;\n\t\tte[ 2 ] = ( xz - wy ) * sx;\n\t\tte[ 3 ] = 0;\n\n\t\tte[ 4 ] = ( xy - wz ) * sy;\n\t\tte[ 5 ] = ( 1 - ( xx + zz ) ) * sy;\n\t\tte[ 6 ] = ( yz + wx ) * sy;\n\t\tte[ 7 ] = 0;\n\n\t\tte[ 8 ] = ( xz + wy ) * sz;\n\t\tte[ 9 ] = ( yz - wx ) * sz;\n\t\tte[ 10 ] = ( 1 - ( xx + yy ) ) * sz;\n\t\tte[ 11 ] = 0;\n\n\t\tte[ 12 ] = position.x;\n\t\tte[ 13 ] = position.y;\n\t\tte[ 14 ] = position.z;\n\t\tte[ 15 ] = 1;\n\n\t\treturn this;\n\n\t}\n\n\tdecompose( position, quaternion, scale ) {\n\n\t\tconst te = this.elements;\n\n\t\tlet sx = _v1$5.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();\n\t\tconst sy = _v1$5.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();\n\t\tconst sz = _v1$5.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();\n\n\t\t// if determine is negative, we need to invert one scale\n\t\tconst det = this.determinant();\n\t\tif ( det < 0 ) sx = - sx;\n\n\t\tposition.x = te[ 12 ];\n\t\tposition.y = te[ 13 ];\n\t\tposition.z = te[ 14 ];\n\n\t\t// scale the rotation part\n\t\t_m1$2.copy( this );\n\n\t\tconst invSX = 1 / sx;\n\t\tconst invSY = 1 / sy;\n\t\tconst invSZ = 1 / sz;\n\n\t\t_m1$2.elements[ 0 ] *= invSX;\n\t\t_m1$2.elements[ 1 ] *= invSX;\n\t\t_m1$2.elements[ 2 ] *= invSX;\n\n\t\t_m1$2.elements[ 4 ] *= invSY;\n\t\t_m1$2.elements[ 5 ] *= invSY;\n\t\t_m1$2.elements[ 6 ] *= invSY;\n\n\t\t_m1$2.elements[ 8 ] *= invSZ;\n\t\t_m1$2.elements[ 9 ] *= invSZ;\n\t\t_m1$2.elements[ 10 ] *= invSZ;\n\n\t\tquaternion.setFromRotationMatrix( _m1$2 );\n\n\t\tscale.x = sx;\n\t\tscale.y = sy;\n\t\tscale.z = sz;\n\n\t\treturn this;\n\n\t}\n\n\tmakePerspective( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem ) {\n\n\t\tconst te = this.elements;\n\t\tconst x = 2 * near / ( right - left );\n\t\tconst y = 2 * near / ( top - bottom );\n\n\t\tconst a = ( right + left ) / ( right - left );\n\t\tconst b = ( top + bottom ) / ( top - bottom );\n\n\t\tlet c, d;\n\n\t\tif ( coordinateSystem === WebGLCoordinateSystem ) {\n\n\t\t\tc = - ( far + near ) / ( far - near );\n\t\t\td = ( - 2 * far * near ) / ( far - near );\n\n\t\t} else if ( coordinateSystem === WebGPUCoordinateSystem ) {\n\n\t\t\tc = - far / ( far - near );\n\t\t\td = ( - far * near ) / ( far - near );\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'THREE.Matrix4.makePerspective(): Invalid coordinate system: ' + coordinateSystem );\n\n\t\t}\n\n\t\tte[ 0 ] = x;\tte[ 4 ] = 0;\tte[ 8 ] = a; \tte[ 12 ] = 0;\n\t\tte[ 1 ] = 0;\tte[ 5 ] = y;\tte[ 9 ] = b; \tte[ 13 ] = 0;\n\t\tte[ 2 ] = 0;\tte[ 6 ] = 0;\tte[ 10 ] = c; \tte[ 14 ] = d;\n\t\tte[ 3 ] = 0;\tte[ 7 ] = 0;\tte[ 11 ] = - 1;\tte[ 15 ] = 0;\n\n\t\treturn this;\n\n\t}\n\n\tmakeOrthographic( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem ) {\n\n\t\tconst te = this.elements;\n\t\tconst w = 1.0 / ( right - left );\n\t\tconst h = 1.0 / ( top - bottom );\n\t\tconst p = 1.0 / ( far - near );\n\n\t\tconst x = ( right + left ) * w;\n\t\tconst y = ( top + bottom ) * h;\n\n\t\tlet z, zInv;\n\n\t\tif ( coordinateSystem === WebGLCoordinateSystem ) {\n\n\t\t\tz = ( far + near ) * p;\n\t\t\tzInv = - 2 * p;\n\n\t\t} else if ( coordinateSystem === WebGPUCoordinateSystem ) {\n\n\t\t\tz = near * p;\n\t\t\tzInv = - 1 * p;\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'THREE.Matrix4.makeOrthographic(): Invalid coordinate system: ' + coordinateSystem );\n\n\t\t}\n\n\t\tte[ 0 ] = 2 * w;\tte[ 4 ] = 0;\t\tte[ 8 ] = 0; \t\tte[ 12 ] = - x;\n\t\tte[ 1 ] = 0; \t\tte[ 5 ] = 2 * h;\tte[ 9 ] = 0; \t\tte[ 13 ] = - y;\n\t\tte[ 2 ] = 0; \t\tte[ 6 ] = 0;\t\tte[ 10 ] = zInv;\tte[ 14 ] = - z;\n\t\tte[ 3 ] = 0; \t\tte[ 7 ] = 0;\t\tte[ 11 ] = 0;\t\tte[ 15 ] = 1;\n\n\t\treturn this;\n\n\t}\n\n\tequals( matrix ) {\n\n\t\tconst te = this.elements;\n\t\tconst me = matrix.elements;\n\n\t\tfor ( let i = 0; i < 16; i ++ ) {\n\n\t\t\tif ( te[ i ] !== me[ i ] ) return false;\n\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tfor ( let i = 0; i < 16; i ++ ) {\n\n\t\t\tthis.elements[ i ] = array[ i + offset ];\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tconst te = this.elements;\n\n\t\tarray[ offset ] = te[ 0 ];\n\t\tarray[ offset + 1 ] = te[ 1 ];\n\t\tarray[ offset + 2 ] = te[ 2 ];\n\t\tarray[ offset + 3 ] = te[ 3 ];\n\n\t\tarray[ offset + 4 ] = te[ 4 ];\n\t\tarray[ offset + 5 ] = te[ 5 ];\n\t\tarray[ offset + 6 ] = te[ 6 ];\n\t\tarray[ offset + 7 ] = te[ 7 ];\n\n\t\tarray[ offset + 8 ] = te[ 8 ];\n\t\tarray[ offset + 9 ] = te[ 9 ];\n\t\tarray[ offset + 10 ] = te[ 10 ];\n\t\tarray[ offset + 11 ] = te[ 11 ];\n\n\t\tarray[ offset + 12 ] = te[ 12 ];\n\t\tarray[ offset + 13 ] = te[ 13 ];\n\t\tarray[ offset + 14 ] = te[ 14 ];\n\t\tarray[ offset + 15 ] = te[ 15 ];\n\n\t\treturn array;\n\n\t}\n\n}\n\nconst _v1$5 = /*@__PURE__*/ new Vector3();\nconst _m1$2 = /*@__PURE__*/ new Matrix4();\nconst _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 );\nconst _one = /*@__PURE__*/ new Vector3( 1, 1, 1 );\nconst _x = /*@__PURE__*/ new Vector3();\nconst _y = /*@__PURE__*/ new Vector3();\nconst _z = /*@__PURE__*/ new Vector3();\n\nconst _matrix$1 = /*@__PURE__*/ new Matrix4();\nconst _quaternion$3 = /*@__PURE__*/ new Quaternion();\n\nclass Euler {\n\n\tconstructor( x = 0, y = 0, z = 0, order = Euler.DEFAULT_ORDER ) {\n\n\t\tthis.isEuler = true;\n\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tthis._z = z;\n\t\tthis._order = order;\n\n\t}\n\n\tget x() {\n\n\t\treturn this._x;\n\n\t}\n\n\tset x( value ) {\n\n\t\tthis._x = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tget y() {\n\n\t\treturn this._y;\n\n\t}\n\n\tset y( value ) {\n\n\t\tthis._y = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tget z() {\n\n\t\treturn this._z;\n\n\t}\n\n\tset z( value ) {\n\n\t\tthis._z = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tget order() {\n\n\t\treturn this._order;\n\n\t}\n\n\tset order( value ) {\n\n\t\tthis._order = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tset( x, y, z, order = this._order ) {\n\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tthis._z = z;\n\t\tthis._order = order;\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this._x, this._y, this._z, this._order );\n\n\t}\n\n\tcopy( euler ) {\n\n\t\tthis._x = euler._x;\n\t\tthis._y = euler._y;\n\t\tthis._z = euler._z;\n\t\tthis._order = euler._order;\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tsetFromRotationMatrix( m, order = this._order, update = true ) {\n\n\t\t// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n\t\tconst te = m.elements;\n\t\tconst m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];\n\t\tconst m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];\n\t\tconst m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];\n\n\t\tswitch ( order ) {\n\n\t\t\tcase 'XYZ':\n\n\t\t\t\tthis._y = Math.asin( clamp( m13, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m13 ) < 0.9999999 ) {\n\n\t\t\t\t\tthis._x = Math.atan2( - m23, m33 );\n\t\t\t\t\tthis._z = Math.atan2( - m12, m11 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._x = Math.atan2( m32, m22 );\n\t\t\t\t\tthis._z = 0;\n\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'YXZ':\n\n\t\t\t\tthis._x = Math.asin( - clamp( m23, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m23 ) < 0.9999999 ) {\n\n\t\t\t\t\tthis._y = Math.atan2( m13, m33 );\n\t\t\t\t\tthis._z = Math.atan2( m21, m22 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._y = Math.atan2( - m31, m11 );\n\t\t\t\t\tthis._z = 0;\n\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'ZXY':\n\n\t\t\t\tthis._x = Math.asin( clamp( m32, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m32 ) < 0.9999999 ) {\n\n\t\t\t\t\tthis._y = Math.atan2( - m31, m33 );\n\t\t\t\t\tthis._z = Math.atan2( - m12, m22 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._y = 0;\n\t\t\t\t\tthis._z = Math.atan2( m21, m11 );\n\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'ZYX':\n\n\t\t\t\tthis._y = Math.asin( - clamp( m31, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m31 ) < 0.9999999 ) {\n\n\t\t\t\t\tthis._x = Math.atan2( m32, m33 );\n\t\t\t\t\tthis._z = Math.atan2( m21, m11 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._x = 0;\n\t\t\t\t\tthis._z = Math.atan2( - m12, m22 );\n\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'YZX':\n\n\t\t\t\tthis._z = Math.asin( clamp( m21, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m21 ) < 0.9999999 ) {\n\n\t\t\t\t\tthis._x = Math.atan2( - m23, m22 );\n\t\t\t\t\tthis._y = Math.atan2( - m31, m11 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._x = 0;\n\t\t\t\t\tthis._y = Math.atan2( m13, m33 );\n\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'XZY':\n\n\t\t\t\tthis._z = Math.asin( - clamp( m12, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m12 ) < 0.9999999 ) {\n\n\t\t\t\t\tthis._x = Math.atan2( m32, m22 );\n\t\t\t\t\tthis._y = Math.atan2( m13, m11 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._x = Math.atan2( - m23, m33 );\n\t\t\t\t\tthis._y = 0;\n\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\n\t\t\t\tconsole.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order );\n\n\t\t}\n\n\t\tthis._order = order;\n\n\t\tif ( update === true ) this._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tsetFromQuaternion( q, order, update ) {\n\n\t\t_matrix$1.makeRotationFromQuaternion( q );\n\n\t\treturn this.setFromRotationMatrix( _matrix$1, order, update );\n\n\t}\n\n\tsetFromVector3( v, order = this._order ) {\n\n\t\treturn this.set( v.x, v.y, v.z, order );\n\n\t}\n\n\treorder( newOrder ) {\n\n\t\t// WARNING: this discards revolution information -bhouston\n\n\t\t_quaternion$3.setFromEuler( this );\n\n\t\treturn this.setFromQuaternion( _quaternion$3, newOrder );\n\n\t}\n\n\tequals( euler ) {\n\n\t\treturn ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );\n\n\t}\n\n\tfromArray( array ) {\n\n\t\tthis._x = array[ 0 ];\n\t\tthis._y = array[ 1 ];\n\t\tthis._z = array[ 2 ];\n\t\tif ( array[ 3 ] !== undefined ) this._order = array[ 3 ];\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tarray[ offset ] = this._x;\n\t\tarray[ offset + 1 ] = this._y;\n\t\tarray[ offset + 2 ] = this._z;\n\t\tarray[ offset + 3 ] = this._order;\n\n\t\treturn array;\n\n\t}\n\n\t_onChange( callback ) {\n\n\t\tthis._onChangeCallback = callback;\n\n\t\treturn this;\n\n\t}\n\n\t_onChangeCallback() {}\n\n\t*[ Symbol.iterator ]() {\n\n\t\tyield this._x;\n\t\tyield this._y;\n\t\tyield this._z;\n\t\tyield this._order;\n\n\t}\n\n}\n\nEuler.DEFAULT_ORDER = 'XYZ';\n\nclass Layers {\n\n\tconstructor() {\n\n\t\tthis.mask = 1 | 0;\n\n\t}\n\n\tset( channel ) {\n\n\t\tthis.mask = ( 1 << channel | 0 ) >>> 0;\n\n\t}\n\n\tenable( channel ) {\n\n\t\tthis.mask |= 1 << channel | 0;\n\n\t}\n\n\tenableAll() {\n\n\t\tthis.mask = 0xffffffff | 0;\n\n\t}\n\n\ttoggle( channel ) {\n\n\t\tthis.mask ^= 1 << channel | 0;\n\n\t}\n\n\tdisable( channel ) {\n\n\t\tthis.mask &= ~ ( 1 << channel | 0 );\n\n\t}\n\n\tdisableAll() {\n\n\t\tthis.mask = 0;\n\n\t}\n\n\ttest( layers ) {\n\n\t\treturn ( this.mask & layers.mask ) !== 0;\n\n\t}\n\n\tisEnabled( channel ) {\n\n\t\treturn ( this.mask & ( 1 << channel | 0 ) ) !== 0;\n\n\t}\n\n}\n\nlet _object3DId = 0;\n\nconst _v1$4 = /*@__PURE__*/ new Vector3();\nconst _q1 = /*@__PURE__*/ new Quaternion();\nconst _m1$1 = /*@__PURE__*/ new Matrix4();\nconst _target = /*@__PURE__*/ new Vector3();\n\nconst _position$3 = /*@__PURE__*/ new Vector3();\nconst _scale$2 = /*@__PURE__*/ new Vector3();\nconst _quaternion$2 = /*@__PURE__*/ new Quaternion();\n\nconst _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 );\nconst _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 );\nconst _zAxis = /*@__PURE__*/ new Vector3( 0, 0, 1 );\n\nconst _addedEvent = { type: 'added' };\nconst _removedEvent = { type: 'removed' };\n\nclass Object3D extends EventDispatcher {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isObject3D = true;\n\n\t\tObject.defineProperty( this, 'id', { value: _object3DId ++ } );\n\n\t\tthis.uuid = generateUUID();\n\n\t\tthis.name = '';\n\t\tthis.type = 'Object3D';\n\n\t\tthis.parent = null;\n\t\tthis.children = [];\n\n\t\tthis.up = Object3D.DEFAULT_UP.clone();\n\n\t\tconst position = new Vector3();\n\t\tconst rotation = new Euler();\n\t\tconst quaternion = new Quaternion();\n\t\tconst scale = new Vector3( 1, 1, 1 );\n\n\t\tfunction onRotationChange() {\n\n\t\t\tquaternion.setFromEuler( rotation, false );\n\n\t\t}\n\n\t\tfunction onQuaternionChange() {\n\n\t\t\trotation.setFromQuaternion( quaternion, undefined, false );\n\n\t\t}\n\n\t\trotation._onChange( onRotationChange );\n\t\tquaternion._onChange( onQuaternionChange );\n\n\t\tObject.defineProperties( this, {\n\t\t\tposition: {\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: position\n\t\t\t},\n\t\t\trotation: {\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: rotation\n\t\t\t},\n\t\t\tquaternion: {\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: quaternion\n\t\t\t},\n\t\t\tscale: {\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: scale\n\t\t\t},\n\t\t\tmodelViewMatrix: {\n\t\t\t\tvalue: new Matrix4()\n\t\t\t},\n\t\t\tnormalMatrix: {\n\t\t\t\tvalue: new Matrix3()\n\t\t\t}\n\t\t} );\n\n\t\tthis.matrix = new Matrix4();\n\t\tthis.matrixWorld = new Matrix4();\n\n\t\tthis.matrixAutoUpdate = Object3D.DEFAULT_MATRIX_AUTO_UPDATE;\n\n\t\tthis.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE; // checked by the renderer\n\t\tthis.matrixWorldNeedsUpdate = false;\n\n\t\tthis.layers = new Layers();\n\t\tthis.visible = true;\n\n\t\tthis.castShadow = false;\n\t\tthis.receiveShadow = false;\n\n\t\tthis.frustumCulled = true;\n\t\tthis.renderOrder = 0;\n\n\t\tthis.animations = [];\n\n\t\tthis.userData = {};\n\n\t}\n\n\tonBeforeShadow( /* renderer, object, camera, shadowCamera, geometry, depthMaterial, group */ ) {}\n\n\tonAfterShadow( /* renderer, object, camera, shadowCamera, geometry, depthMaterial, group */ ) {}\n\n\tonBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {}\n\n\tonAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {}\n\n\tapplyMatrix4( matrix ) {\n\n\t\tif ( this.matrixAutoUpdate ) this.updateMatrix();\n\n\t\tthis.matrix.premultiply( matrix );\n\n\t\tthis.matrix.decompose( this.position, this.quaternion, this.scale );\n\n\t}\n\n\tapplyQuaternion( q ) {\n\n\t\tthis.quaternion.premultiply( q );\n\n\t\treturn this;\n\n\t}\n\n\tsetRotationFromAxisAngle( axis, angle ) {\n\n\t\t// assumes axis is normalized\n\n\t\tthis.quaternion.setFromAxisAngle( axis, angle );\n\n\t}\n\n\tsetRotationFromEuler( euler ) {\n\n\t\tthis.quaternion.setFromEuler( euler, true );\n\n\t}\n\n\tsetRotationFromMatrix( m ) {\n\n\t\t// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n\t\tthis.quaternion.setFromRotationMatrix( m );\n\n\t}\n\n\tsetRotationFromQuaternion( q ) {\n\n\t\t// assumes q is normalized\n\n\t\tthis.quaternion.copy( q );\n\n\t}\n\n\trotateOnAxis( axis, angle ) {\n\n\t\t// rotate object on axis in object space\n\t\t// axis is assumed to be normalized\n\n\t\t_q1.setFromAxisAngle( axis, angle );\n\n\t\tthis.quaternion.multiply( _q1 );\n\n\t\treturn this;\n\n\t}\n\n\trotateOnWorldAxis( axis, angle ) {\n\n\t\t// rotate object on axis in world space\n\t\t// axis is assumed to be normalized\n\t\t// method assumes no rotated parent\n\n\t\t_q1.setFromAxisAngle( axis, angle );\n\n\t\tthis.quaternion.premultiply( _q1 );\n\n\t\treturn this;\n\n\t}\n\n\trotateX( angle ) {\n\n\t\treturn this.rotateOnAxis( _xAxis, angle );\n\n\t}\n\n\trotateY( angle ) {\n\n\t\treturn this.rotateOnAxis( _yAxis, angle );\n\n\t}\n\n\trotateZ( angle ) {\n\n\t\treturn this.rotateOnAxis( _zAxis, angle );\n\n\t}\n\n\ttranslateOnAxis( axis, distance ) {\n\n\t\t// translate object by distance along axis in object space\n\t\t// axis is assumed to be normalized\n\n\t\t_v1$4.copy( axis ).applyQuaternion( this.quaternion );\n\n\t\tthis.position.add( _v1$4.multiplyScalar( distance ) );\n\n\t\treturn this;\n\n\t}\n\n\ttranslateX( distance ) {\n\n\t\treturn this.translateOnAxis( _xAxis, distance );\n\n\t}\n\n\ttranslateY( distance ) {\n\n\t\treturn this.translateOnAxis( _yAxis, distance );\n\n\t}\n\n\ttranslateZ( distance ) {\n\n\t\treturn this.translateOnAxis( _zAxis, distance );\n\n\t}\n\n\tlocalToWorld( vector ) {\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\treturn vector.applyMatrix4( this.matrixWorld );\n\n\t}\n\n\tworldToLocal( vector ) {\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\treturn vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() );\n\n\t}\n\n\tlookAt( x, y, z ) {\n\n\t\t// This method does not support objects having non-uniformly-scaled parent(s)\n\n\t\tif ( x.isVector3 ) {\n\n\t\t\t_target.copy( x );\n\n\t\t} else {\n\n\t\t\t_target.set( x, y, z );\n\n\t\t}\n\n\t\tconst parent = this.parent;\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\t_position$3.setFromMatrixPosition( this.matrixWorld );\n\n\t\tif ( this.isCamera || this.isLight ) {\n\n\t\t\t_m1$1.lookAt( _position$3, _target, this.up );\n\n\t\t} else {\n\n\t\t\t_m1$1.lookAt( _target, _position$3, this.up );\n\n\t\t}\n\n\t\tthis.quaternion.setFromRotationMatrix( _m1$1 );\n\n\t\tif ( parent ) {\n\n\t\t\t_m1$1.extractRotation( parent.matrixWorld );\n\t\t\t_q1.setFromRotationMatrix( _m1$1 );\n\t\t\tthis.quaternion.premultiply( _q1.invert() );\n\n\t\t}\n\n\t}\n\n\tadd( object ) {\n\n\t\tif ( arguments.length > 1 ) {\n\n\t\t\tfor ( let i = 0; i < arguments.length; i ++ ) {\n\n\t\t\t\tthis.add( arguments[ i ] );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tif ( object === this ) {\n\n\t\t\tconsole.error( 'THREE.Object3D.add: object can\\'t be added as a child of itself.', object );\n\t\t\treturn this;\n\n\t\t}\n\n\t\tif ( object && object.isObject3D ) {\n\n\t\t\tif ( object.parent !== null ) {\n\n\t\t\t\tobject.parent.remove( object );\n\n\t\t\t}\n\n\t\t\tobject.parent = this;\n\t\t\tthis.children.push( object );\n\n\t\t\tobject.dispatchEvent( _addedEvent );\n\n\t\t} else {\n\n\t\t\tconsole.error( 'THREE.Object3D.add: object not an instance of THREE.Object3D.', object );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tremove( object ) {\n\n\t\tif ( arguments.length > 1 ) {\n\n\t\t\tfor ( let i = 0; i < arguments.length; i ++ ) {\n\n\t\t\t\tthis.remove( arguments[ i ] );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tconst index = this.children.indexOf( object );\n\n\t\tif ( index !== - 1 ) {\n\n\t\t\tobject.parent = null;\n\t\t\tthis.children.splice( index, 1 );\n\n\t\t\tobject.dispatchEvent( _removedEvent );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tremoveFromParent() {\n\n\t\tconst parent = this.parent;\n\n\t\tif ( parent !== null ) {\n\n\t\t\tparent.remove( this );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tclear() {\n\n\t\treturn this.remove( ... this.children );\n\n\t}\n\n\tattach( object ) {\n\n\t\t// adds object as a child of this, while maintaining the object's world transform\n\n\t\t// Note: This method does not support scene graphs having non-uniformly-scaled nodes(s)\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\t_m1$1.copy( this.matrixWorld ).invert();\n\n\t\tif ( object.parent !== null ) {\n\n\t\t\tobject.parent.updateWorldMatrix( true, false );\n\n\t\t\t_m1$1.multiply( object.parent.matrixWorld );\n\n\t\t}\n\n\t\tobject.applyMatrix4( _m1$1 );\n\n\t\tthis.add( object );\n\n\t\tobject.updateWorldMatrix( false, true );\n\n\t\treturn this;\n\n\t}\n\n\tgetObjectById( id ) {\n\n\t\treturn this.getObjectByProperty( 'id', id );\n\n\t}\n\n\tgetObjectByName( name ) {\n\n\t\treturn this.getObjectByProperty( 'name', name );\n\n\t}\n\n\tgetObjectByProperty( name, value ) {\n\n\t\tif ( this[ name ] === value ) return this;\n\n\t\tfor ( let i = 0, l = this.children.length; i < l; i ++ ) {\n\n\t\t\tconst child = this.children[ i ];\n\t\t\tconst object = child.getObjectByProperty( name, value );\n\n\t\t\tif ( object !== undefined ) {\n\n\t\t\t\treturn object;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn undefined;\n\n\t}\n\n\tgetObjectsByProperty( name, value, result = [] ) {\n\n\t\tif ( this[ name ] === value ) result.push( this );\n\n\t\tconst children = this.children;\n\n\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\tchildren[ i ].getObjectsByProperty( name, value, result );\n\n\t\t}\n\n\t\treturn result;\n\n\t}\n\n\tgetWorldPosition( target ) {\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\treturn target.setFromMatrixPosition( this.matrixWorld );\n\n\t}\n\n\tgetWorldQuaternion( target ) {\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\tthis.matrixWorld.decompose( _position$3, target, _scale$2 );\n\n\t\treturn target;\n\n\t}\n\n\tgetWorldScale( target ) {\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\tthis.matrixWorld.decompose( _position$3, _quaternion$2, target );\n\n\t\treturn target;\n\n\t}\n\n\tgetWorldDirection( target ) {\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\tconst e = this.matrixWorld.elements;\n\n\t\treturn target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize();\n\n\t}\n\n\traycast( /* raycaster, intersects */ ) {}\n\n\ttraverse( callback ) {\n\n\t\tcallback( this );\n\n\t\tconst children = this.children;\n\n\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\tchildren[ i ].traverse( callback );\n\n\t\t}\n\n\t}\n\n\ttraverseVisible( callback ) {\n\n\t\tif ( this.visible === false ) return;\n\n\t\tcallback( this );\n\n\t\tconst children = this.children;\n\n\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\tchildren[ i ].traverseVisible( callback );\n\n\t\t}\n\n\t}\n\n\ttraverseAncestors( callback ) {\n\n\t\tconst parent = this.parent;\n\n\t\tif ( parent !== null ) {\n\n\t\t\tcallback( parent );\n\n\t\t\tparent.traverseAncestors( callback );\n\n\t\t}\n\n\t}\n\n\tupdateMatrix() {\n\n\t\tthis.matrix.compose( this.position, this.quaternion, this.scale );\n\n\t\tthis.matrixWorldNeedsUpdate = true;\n\n\t}\n\n\tupdateMatrixWorld( force ) {\n\n\t\tif ( this.matrixAutoUpdate ) this.updateMatrix();\n\n\t\tif ( this.matrixWorldNeedsUpdate || force ) {\n\n\t\t\tif ( this.parent === null ) {\n\n\t\t\t\tthis.matrixWorld.copy( this.matrix );\n\n\t\t\t} else {\n\n\t\t\t\tthis.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );\n\n\t\t\t}\n\n\t\t\tthis.matrixWorldNeedsUpdate = false;\n\n\t\t\tforce = true;\n\n\t\t}\n\n\t\t// update children\n\n\t\tconst children = this.children;\n\n\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\tconst child = children[ i ];\n\n\t\t\tif ( child.matrixWorldAutoUpdate === true || force === true ) {\n\n\t\t\t\tchild.updateMatrixWorld( force );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tupdateWorldMatrix( updateParents, updateChildren ) {\n\n\t\tconst parent = this.parent;\n\n\t\tif ( updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true ) {\n\n\t\t\tparent.updateWorldMatrix( true, false );\n\n\t\t}\n\n\t\tif ( this.matrixAutoUpdate ) this.updateMatrix();\n\n\t\tif ( this.parent === null ) {\n\n\t\t\tthis.matrixWorld.copy( this.matrix );\n\n\t\t} else {\n\n\t\t\tthis.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );\n\n\t\t}\n\n\t\t// update children\n\n\t\tif ( updateChildren === true ) {\n\n\t\t\tconst children = this.children;\n\n\t\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\t\tconst child = children[ i ];\n\n\t\t\t\tif ( child.matrixWorldAutoUpdate === true ) {\n\n\t\t\t\t\tchild.updateWorldMatrix( false, true );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\t// meta is a string when called from JSON.stringify\n\t\tconst isRootObject = ( meta === undefined || typeof meta === 'string' );\n\n\t\tconst output = {};\n\n\t\t// meta is a hash used to collect geometries, materials.\n\t\t// not providing it implies that this is the root object\n\t\t// being serialized.\n\t\tif ( isRootObject ) {\n\n\t\t\t// initialize meta obj\n\t\t\tmeta = {\n\t\t\t\tgeometries: {},\n\t\t\t\tmaterials: {},\n\t\t\t\ttextures: {},\n\t\t\t\timages: {},\n\t\t\t\tshapes: {},\n\t\t\t\tskeletons: {},\n\t\t\t\tanimations: {},\n\t\t\t\tnodes: {}\n\t\t\t};\n\n\t\t\toutput.metadata = {\n\t\t\t\tversion: 4.6,\n\t\t\t\ttype: 'Object',\n\t\t\t\tgenerator: 'Object3D.toJSON'\n\t\t\t};\n\n\t\t}\n\n\t\t// standard Object3D serialization\n\n\t\tconst object = {};\n\n\t\tobject.uuid = this.uuid;\n\t\tobject.type = this.type;\n\n\t\tif ( this.name !== '' ) object.name = this.name;\n\t\tif ( this.castShadow === true ) object.castShadow = true;\n\t\tif ( this.receiveShadow === true ) object.receiveShadow = true;\n\t\tif ( this.visible === false ) object.visible = false;\n\t\tif ( this.frustumCulled === false ) object.frustumCulled = false;\n\t\tif ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder;\n\t\tif ( Object.keys( this.userData ).length > 0 ) object.userData = this.userData;\n\n\t\tobject.layers = this.layers.mask;\n\t\tobject.matrix = this.matrix.toArray();\n\t\tobject.up = this.up.toArray();\n\n\t\tif ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false;\n\n\t\t// object specific properties\n\n\t\tif ( this.isInstancedMesh ) {\n\n\t\t\tobject.type = 'InstancedMesh';\n\t\t\tobject.count = this.count;\n\t\t\tobject.instanceMatrix = this.instanceMatrix.toJSON();\n\t\t\tif ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON();\n\n\t\t}\n\n\t\tif ( this.isBatchedMesh ) {\n\n\t\t\tobject.type = 'BatchedMesh';\n\t\t\tobject.perObjectFrustumCulled = this.perObjectFrustumCulled;\n\t\t\tobject.sortObjects = this.sortObjects;\n\n\t\t\tobject.drawRanges = this._drawRanges;\n\t\t\tobject.reservedRanges = this._reservedRanges;\n\n\t\t\tobject.visibility = this._visibility;\n\t\t\tobject.active = this._active;\n\t\t\tobject.bounds = this._bounds.map( bound => ( {\n\t\t\t\tboxInitialized: bound.boxInitialized,\n\t\t\t\tboxMin: bound.box.min.toArray(),\n\t\t\t\tboxMax: bound.box.max.toArray(),\n\n\t\t\t\tsphereInitialized: bound.sphereInitialized,\n\t\t\t\tsphereRadius: bound.sphere.radius,\n\t\t\t\tsphereCenter: bound.sphere.center.toArray()\n\t\t\t} ) );\n\n\t\t\tobject.maxGeometryCount = this._maxGeometryCount;\n\t\t\tobject.maxVertexCount = this._maxVertexCount;\n\t\t\tobject.maxIndexCount = this._maxIndexCount;\n\n\t\t\tobject.geometryInitialized = this._geometryInitialized;\n\t\t\tobject.geometryCount = this._geometryCount;\n\n\t\t\tobject.matricesTexture = this._matricesTexture.toJSON( meta );\n\n\t\t\tif ( this.boundingSphere !== null ) {\n\n\t\t\t\tobject.boundingSphere = {\n\t\t\t\t\tcenter: object.boundingSphere.center.toArray(),\n\t\t\t\t\tradius: object.boundingSphere.radius\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t\tif ( this.boundingBox !== null ) {\n\n\t\t\t\tobject.boundingBox = {\n\t\t\t\t\tmin: object.boundingBox.min.toArray(),\n\t\t\t\t\tmax: object.boundingBox.max.toArray()\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t}\n\n\t\t//\n\n\t\tfunction serialize( library, element ) {\n\n\t\t\tif ( library[ element.uuid ] === undefined ) {\n\n\t\t\t\tlibrary[ element.uuid ] = element.toJSON( meta );\n\n\t\t\t}\n\n\t\t\treturn element.uuid;\n\n\t\t}\n\n\t\tif ( this.isScene ) {\n\n\t\t\tif ( this.background ) {\n\n\t\t\t\tif ( this.background.isColor ) {\n\n\t\t\t\t\tobject.background = this.background.toJSON();\n\n\t\t\t\t} else if ( this.background.isTexture ) {\n\n\t\t\t\t\tobject.background = this.background.toJSON( meta ).uuid;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( this.environment && this.environment.isTexture && this.environment.isRenderTargetTexture !== true ) {\n\n\t\t\t\tobject.environment = this.environment.toJSON( meta ).uuid;\n\n\t\t\t}\n\n\t\t} else if ( this.isMesh || this.isLine || this.isPoints ) {\n\n\t\t\tobject.geometry = serialize( meta.geometries, this.geometry );\n\n\t\t\tconst parameters = this.geometry.parameters;\n\n\t\t\tif ( parameters !== undefined && parameters.shapes !== undefined ) {\n\n\t\t\t\tconst shapes = parameters.shapes;\n\n\t\t\t\tif ( Array.isArray( shapes ) ) {\n\n\t\t\t\t\tfor ( let i = 0, l = shapes.length; i < l; i ++ ) {\n\n\t\t\t\t\t\tconst shape = shapes[ i ];\n\n\t\t\t\t\t\tserialize( meta.shapes, shape );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tserialize( meta.shapes, shapes );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( this.isSkinnedMesh ) {\n\n\t\t\tobject.bindMode = this.bindMode;\n\t\t\tobject.bindMatrix = this.bindMatrix.toArray();\n\n\t\t\tif ( this.skeleton !== undefined ) {\n\n\t\t\t\tserialize( meta.skeletons, this.skeleton );\n\n\t\t\t\tobject.skeleton = this.skeleton.uuid;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( this.material !== undefined ) {\n\n\t\t\tif ( Array.isArray( this.material ) ) {\n\n\t\t\t\tconst uuids = [];\n\n\t\t\t\tfor ( let i = 0, l = this.material.length; i < l; i ++ ) {\n\n\t\t\t\t\tuuids.push( serialize( meta.materials, this.material[ i ] ) );\n\n\t\t\t\t}\n\n\t\t\t\tobject.material = uuids;\n\n\t\t\t} else {\n\n\t\t\t\tobject.material = serialize( meta.materials, this.material );\n\n\t\t\t}\n\n\t\t}\n\n\t\t//\n\n\t\tif ( this.children.length > 0 ) {\n\n\t\t\tobject.children = [];\n\n\t\t\tfor ( let i = 0; i < this.children.length; i ++ ) {\n\n\t\t\t\tobject.children.push( this.children[ i ].toJSON( meta ).object );\n\n\t\t\t}\n\n\t\t}\n\n\t\t//\n\n\t\tif ( this.animations.length > 0 ) {\n\n\t\t\tobject.animations = [];\n\n\t\t\tfor ( let i = 0; i < this.animations.length; i ++ ) {\n\n\t\t\t\tconst animation = this.animations[ i ];\n\n\t\t\t\tobject.animations.push( serialize( meta.animations, animation ) );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( isRootObject ) {\n\n\t\t\tconst geometries = extractFromCache( meta.geometries );\n\t\t\tconst materials = extractFromCache( meta.materials );\n\t\t\tconst textures = extractFromCache( meta.textures );\n\t\t\tconst images = extractFromCache( meta.images );\n\t\t\tconst shapes = extractFromCache( meta.shapes );\n\t\t\tconst skeletons = extractFromCache( meta.skeletons );\n\t\t\tconst animations = extractFromCache( meta.animations );\n\t\t\tconst nodes = extractFromCache( meta.nodes );\n\n\t\t\tif ( geometries.length > 0 ) output.geometries = geometries;\n\t\t\tif ( materials.length > 0 ) output.materials = materials;\n\t\t\tif ( textures.length > 0 ) output.textures = textures;\n\t\t\tif ( images.length > 0 ) output.images = images;\n\t\t\tif ( shapes.length > 0 ) output.shapes = shapes;\n\t\t\tif ( skeletons.length > 0 ) output.skeletons = skeletons;\n\t\t\tif ( animations.length > 0 ) output.animations = animations;\n\t\t\tif ( nodes.length > 0 ) output.nodes = nodes;\n\n\t\t}\n\n\t\toutput.object = object;\n\n\t\treturn output;\n\n\t\t// extract data from the cache hash\n\t\t// remove metadata on each item\n\t\t// and return as array\n\t\tfunction extractFromCache( cache ) {\n\n\t\t\tconst values = [];\n\t\t\tfor ( const key in cache ) {\n\n\t\t\t\tconst data = cache[ key ];\n\t\t\t\tdelete data.metadata;\n\t\t\t\tvalues.push( data );\n\n\t\t\t}\n\n\t\t\treturn values;\n\n\t\t}\n\n\t}\n\n\tclone( recursive ) {\n\n\t\treturn new this.constructor().copy( this, recursive );\n\n\t}\n\n\tcopy( source, recursive = true ) {\n\n\t\tthis.name = source.name;\n\n\t\tthis.up.copy( source.up );\n\n\t\tthis.position.copy( source.position );\n\t\tthis.rotation.order = source.rotation.order;\n\t\tthis.quaternion.copy( source.quaternion );\n\t\tthis.scale.copy( source.scale );\n\n\t\tthis.matrix.copy( source.matrix );\n\t\tthis.matrixWorld.copy( source.matrixWorld );\n\n\t\tthis.matrixAutoUpdate = source.matrixAutoUpdate;\n\n\t\tthis.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate;\n\t\tthis.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;\n\n\t\tthis.layers.mask = source.layers.mask;\n\t\tthis.visible = source.visible;\n\n\t\tthis.castShadow = source.castShadow;\n\t\tthis.receiveShadow = source.receiveShadow;\n\n\t\tthis.frustumCulled = source.frustumCulled;\n\t\tthis.renderOrder = source.renderOrder;\n\n\t\tthis.animations = source.animations.slice();\n\n\t\tthis.userData = JSON.parse( JSON.stringify( source.userData ) );\n\n\t\tif ( recursive === true ) {\n\n\t\t\tfor ( let i = 0; i < source.children.length; i ++ ) {\n\n\t\t\t\tconst child = source.children[ i ];\n\t\t\t\tthis.add( child.clone() );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n}\n\nObject3D.DEFAULT_UP = /*@__PURE__*/ new Vector3( 0, 1, 0 );\nObject3D.DEFAULT_MATRIX_AUTO_UPDATE = true;\nObject3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true;\n\nconst _v0$1 = /*@__PURE__*/ new Vector3();\nconst _v1$3 = /*@__PURE__*/ new Vector3();\nconst _v2$2 = /*@__PURE__*/ new Vector3();\nconst _v3$1 = /*@__PURE__*/ new Vector3();\n\nconst _vab = /*@__PURE__*/ new Vector3();\nconst _vac = /*@__PURE__*/ new Vector3();\nconst _vbc = /*@__PURE__*/ new Vector3();\nconst _vap = /*@__PURE__*/ new Vector3();\nconst _vbp = /*@__PURE__*/ new Vector3();\nconst _vcp = /*@__PURE__*/ new Vector3();\n\nlet warnedGetUV = false;\n\nclass Triangle {\n\n\tconstructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) {\n\n\t\tthis.a = a;\n\t\tthis.b = b;\n\t\tthis.c = c;\n\n\t}\n\n\tstatic getNormal( a, b, c, target ) {\n\n\t\ttarget.subVectors( c, b );\n\t\t_v0$1.subVectors( a, b );\n\t\ttarget.cross( _v0$1 );\n\n\t\tconst targetLengthSq = target.lengthSq();\n\t\tif ( targetLengthSq > 0 ) {\n\n\t\t\treturn target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) );\n\n\t\t}\n\n\t\treturn target.set( 0, 0, 0 );\n\n\t}\n\n\t// static/instance method to calculate barycentric coordinates\n\t// based on: http://www.blackpawn.com/texts/pointinpoly/default.html\n\tstatic getBarycoord( point, a, b, c, target ) {\n\n\t\t_v0$1.subVectors( c, a );\n\t\t_v1$3.subVectors( b, a );\n\t\t_v2$2.subVectors( point, a );\n\n\t\tconst dot00 = _v0$1.dot( _v0$1 );\n\t\tconst dot01 = _v0$1.dot( _v1$3 );\n\t\tconst dot02 = _v0$1.dot( _v2$2 );\n\t\tconst dot11 = _v1$3.dot( _v1$3 );\n\t\tconst dot12 = _v1$3.dot( _v2$2 );\n\n\t\tconst denom = ( dot00 * dot11 - dot01 * dot01 );\n\n\t\t// collinear or singular triangle\n\t\tif ( denom === 0 ) {\n\n\t\t\ttarget.set( 0, 0, 0 );\n\t\t\treturn null;\n\n\t\t}\n\n\t\tconst invDenom = 1 / denom;\n\t\tconst u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;\n\t\tconst v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;\n\n\t\t// barycentric coordinates must always sum to 1\n\t\treturn target.set( 1 - u - v, v, u );\n\n\t}\n\n\tstatic containsPoint( point, a, b, c ) {\n\n\t\t// if the triangle is degenerate then we can't contain a point\n\t\tif ( this.getBarycoord( point, a, b, c, _v3$1 ) === null ) {\n\n\t\t\treturn false;\n\n\t\t}\n\n\t\treturn ( _v3$1.x >= 0 ) && ( _v3$1.y >= 0 ) && ( ( _v3$1.x + _v3$1.y ) <= 1 );\n\n\t}\n\n\tstatic getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { // @deprecated, r151\n\n\t\tif ( warnedGetUV === false ) {\n\n\t\t\tconsole.warn( 'THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation().' );\n\n\t\t\twarnedGetUV = true;\n\n\t\t}\n\n\t\treturn this.getInterpolation( point, p1, p2, p3, uv1, uv2, uv3, target );\n\n\t}\n\n\tstatic getInterpolation( point, p1, p2, p3, v1, v2, v3, target ) {\n\n\t\tif ( this.getBarycoord( point, p1, p2, p3, _v3$1 ) === null ) {\n\n\t\t\ttarget.x = 0;\n\t\t\ttarget.y = 0;\n\t\t\tif ( 'z' in target ) target.z = 0;\n\t\t\tif ( 'w' in target ) target.w = 0;\n\t\t\treturn null;\n\n\t\t}\n\n\t\ttarget.setScalar( 0 );\n\t\ttarget.addScaledVector( v1, _v3$1.x );\n\t\ttarget.addScaledVector( v2, _v3$1.y );\n\t\ttarget.addScaledVector( v3, _v3$1.z );\n\n\t\treturn target;\n\n\t}\n\n\tstatic isFrontFacing( a, b, c, direction ) {\n\n\t\t_v0$1.subVectors( c, b );\n\t\t_v1$3.subVectors( a, b );\n\n\t\t// strictly front facing\n\t\treturn ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false;\n\n\t}\n\n\tset( a, b, c ) {\n\n\t\tthis.a.copy( a );\n\t\tthis.b.copy( b );\n\t\tthis.c.copy( c );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromPointsAndIndices( points, i0, i1, i2 ) {\n\n\t\tthis.a.copy( points[ i0 ] );\n\t\tthis.b.copy( points[ i1 ] );\n\t\tthis.c.copy( points[ i2 ] );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromAttributeAndIndices( attribute, i0, i1, i2 ) {\n\n\t\tthis.a.fromBufferAttribute( attribute, i0 );\n\t\tthis.b.fromBufferAttribute( attribute, i1 );\n\t\tthis.c.fromBufferAttribute( attribute, i2 );\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( triangle ) {\n\n\t\tthis.a.copy( triangle.a );\n\t\tthis.b.copy( triangle.b );\n\t\tthis.c.copy( triangle.c );\n\n\t\treturn this;\n\n\t}\n\n\tgetArea() {\n\n\t\t_v0$1.subVectors( this.c, this.b );\n\t\t_v1$3.subVectors( this.a, this.b );\n\n\t\treturn _v0$1.cross( _v1$3 ).length() * 0.5;\n\n\t}\n\n\tgetMidpoint( target ) {\n\n\t\treturn target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );\n\n\t}\n\n\tgetNormal( target ) {\n\n\t\treturn Triangle.getNormal( this.a, this.b, this.c, target );\n\n\t}\n\n\tgetPlane( target ) {\n\n\t\treturn target.setFromCoplanarPoints( this.a, this.b, this.c );\n\n\t}\n\n\tgetBarycoord( point, target ) {\n\n\t\treturn Triangle.getBarycoord( point, this.a, this.b, this.c, target );\n\n\t}\n\n\tgetUV( point, uv1, uv2, uv3, target ) { // @deprecated, r151\n\n\t\tif ( warnedGetUV === false ) {\n\n\t\t\tconsole.warn( 'THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation().' );\n\n\t\t\twarnedGetUV = true;\n\n\t\t}\n\n\t\treturn Triangle.getInterpolation( point, this.a, this.b, this.c, uv1, uv2, uv3, target );\n\n\t}\n\n\tgetInterpolation( point, v1, v2, v3, target ) {\n\n\t\treturn Triangle.getInterpolation( point, this.a, this.b, this.c, v1, v2, v3, target );\n\n\t}\n\n\tcontainsPoint( point ) {\n\n\t\treturn Triangle.containsPoint( point, this.a, this.b, this.c );\n\n\t}\n\n\tisFrontFacing( direction ) {\n\n\t\treturn Triangle.isFrontFacing( this.a, this.b, this.c, direction );\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\treturn box.intersectsTriangle( this );\n\n\t}\n\n\tclosestPointToPoint( p, target ) {\n\n\t\tconst a = this.a, b = this.b, c = this.c;\n\t\tlet v, w;\n\n\t\t// algorithm thanks to Real-Time Collision Detection by Christer Ericson,\n\t\t// published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc.,\n\t\t// under the accompanying license; see chapter 5.1.5 for detailed explanation.\n\t\t// basically, we're distinguishing which of the voronoi regions of the triangle\n\t\t// the point lies in with the minimum amount of redundant computation.\n\n\t\t_vab.subVectors( b, a );\n\t\t_vac.subVectors( c, a );\n\t\t_vap.subVectors( p, a );\n\t\tconst d1 = _vab.dot( _vap );\n\t\tconst d2 = _vac.dot( _vap );\n\t\tif ( d1 <= 0 && d2 <= 0 ) {\n\n\t\t\t// vertex region of A; barycentric coords (1, 0, 0)\n\t\t\treturn target.copy( a );\n\n\t\t}\n\n\t\t_vbp.subVectors( p, b );\n\t\tconst d3 = _vab.dot( _vbp );\n\t\tconst d4 = _vac.dot( _vbp );\n\t\tif ( d3 >= 0 && d4 <= d3 ) {\n\n\t\t\t// vertex region of B; barycentric coords (0, 1, 0)\n\t\t\treturn target.copy( b );\n\n\t\t}\n\n\t\tconst vc = d1 * d4 - d3 * d2;\n\t\tif ( vc <= 0 && d1 >= 0 && d3 <= 0 ) {\n\n\t\t\tv = d1 / ( d1 - d3 );\n\t\t\t// edge region of AB; barycentric coords (1-v, v, 0)\n\t\t\treturn target.copy( a ).addScaledVector( _vab, v );\n\n\t\t}\n\n\t\t_vcp.subVectors( p, c );\n\t\tconst d5 = _vab.dot( _vcp );\n\t\tconst d6 = _vac.dot( _vcp );\n\t\tif ( d6 >= 0 && d5 <= d6 ) {\n\n\t\t\t// vertex region of C; barycentric coords (0, 0, 1)\n\t\t\treturn target.copy( c );\n\n\t\t}\n\n\t\tconst vb = d5 * d2 - d1 * d6;\n\t\tif ( vb <= 0 && d2 >= 0 && d6 <= 0 ) {\n\n\t\t\tw = d2 / ( d2 - d6 );\n\t\t\t// edge region of AC; barycentric coords (1-w, 0, w)\n\t\t\treturn target.copy( a ).addScaledVector( _vac, w );\n\n\t\t}\n\n\t\tconst va = d3 * d6 - d5 * d4;\n\t\tif ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) {\n\n\t\t\t_vbc.subVectors( c, b );\n\t\t\tw = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) );\n\t\t\t// edge region of BC; barycentric coords (0, 1-w, w)\n\t\t\treturn target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC\n\n\t\t}\n\n\t\t// face region\n\t\tconst denom = 1 / ( va + vb + vc );\n\t\t// u = va * denom\n\t\tv = vb * denom;\n\t\tw = vc * denom;\n\n\t\treturn target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w );\n\n\t}\n\n\tequals( triangle ) {\n\n\t\treturn triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );\n\n\t}\n\n}\n\nconst _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,\n\t'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,\n\t'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,\n\t'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,\n\t'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,\n\t'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,\n\t'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,\n\t'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,\n\t'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,\n\t'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,\n\t'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,\n\t'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,\n\t'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,\n\t'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,\n\t'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,\n\t'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,\n\t'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,\n\t'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,\n\t'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,\n\t'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,\n\t'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,\n\t'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,\n\t'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,\n\t'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };\n\nconst _hslA = { h: 0, s: 0, l: 0 };\nconst _hslB = { h: 0, s: 0, l: 0 };\n\nfunction hue2rgb( p, q, t ) {\n\n\tif ( t < 0 ) t += 1;\n\tif ( t > 1 ) t -= 1;\n\tif ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;\n\tif ( t < 1 / 2 ) return q;\n\tif ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );\n\treturn p;\n\n}\n\nclass Color {\n\n\tconstructor( r, g, b ) {\n\n\t\tthis.isColor = true;\n\n\t\tthis.r = 1;\n\t\tthis.g = 1;\n\t\tthis.b = 1;\n\n\t\treturn this.set( r, g, b );\n\n\t}\n\n\tset( r, g, b ) {\n\n\t\tif ( g === undefined && b === undefined ) {\n\n\t\t\t// r is THREE.Color, hex or string\n\n\t\t\tconst value = r;\n\n\t\t\tif ( value && value.isColor ) {\n\n\t\t\t\tthis.copy( value );\n\n\t\t\t} else if ( typeof value === 'number' ) {\n\n\t\t\t\tthis.setHex( value );\n\n\t\t\t} else if ( typeof value === 'string' ) {\n\n\t\t\t\tthis.setStyle( value );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tthis.setRGB( r, g, b );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetScalar( scalar ) {\n\n\t\tthis.r = scalar;\n\t\tthis.g = scalar;\n\t\tthis.b = scalar;\n\n\t\treturn this;\n\n\t}\n\n\tsetHex( hex, colorSpace = SRGBColorSpace ) {\n\n\t\thex = Math.floor( hex );\n\n\t\tthis.r = ( hex >> 16 & 255 ) / 255;\n\t\tthis.g = ( hex >> 8 & 255 ) / 255;\n\t\tthis.b = ( hex & 255 ) / 255;\n\n\t\tColorManagement.toWorkingColorSpace( this, colorSpace );\n\n\t\treturn this;\n\n\t}\n\n\tsetRGB( r, g, b, colorSpace = ColorManagement.workingColorSpace ) {\n\n\t\tthis.r = r;\n\t\tthis.g = g;\n\t\tthis.b = b;\n\n\t\tColorManagement.toWorkingColorSpace( this, colorSpace );\n\n\t\treturn this;\n\n\t}\n\n\tsetHSL( h, s, l, colorSpace = ColorManagement.workingColorSpace ) {\n\n\t\t// h,s,l ranges are in 0.0 - 1.0\n\t\th = euclideanModulo( h, 1 );\n\t\ts = clamp( s, 0, 1 );\n\t\tl = clamp( l, 0, 1 );\n\n\t\tif ( s === 0 ) {\n\n\t\t\tthis.r = this.g = this.b = l;\n\n\t\t} else {\n\n\t\t\tconst p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );\n\t\t\tconst q = ( 2 * l ) - p;\n\n\t\t\tthis.r = hue2rgb( q, p, h + 1 / 3 );\n\t\t\tthis.g = hue2rgb( q, p, h );\n\t\t\tthis.b = hue2rgb( q, p, h - 1 / 3 );\n\n\t\t}\n\n\t\tColorManagement.toWorkingColorSpace( this, colorSpace );\n\n\t\treturn this;\n\n\t}\n\n\tsetStyle( style, colorSpace = SRGBColorSpace ) {\n\n\t\tfunction handleAlpha( string ) {\n\n\t\t\tif ( string === undefined ) return;\n\n\t\t\tif ( parseFloat( string ) < 1 ) {\n\n\t\t\t\tconsole.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );\n\n\t\t\t}\n\n\t\t}\n\n\n\t\tlet m;\n\n\t\tif ( m = /^(\\w+)\\(([^\\)]*)\\)/.exec( style ) ) {\n\n\t\t\t// rgb / hsl\n\n\t\t\tlet color;\n\t\t\tconst name = m[ 1 ];\n\t\t\tconst components = m[ 2 ];\n\n\t\t\tswitch ( name ) {\n\n\t\t\t\tcase 'rgb':\n\t\t\t\tcase 'rgba':\n\n\t\t\t\t\tif ( color = /^\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*(?:,\\s*(\\d*\\.?\\d+)\\s*)?$/.exec( components ) ) {\n\n\t\t\t\t\t\t// rgb(255,0,0) rgba(255,0,0,0.5)\n\n\t\t\t\t\t\thandleAlpha( color[ 4 ] );\n\n\t\t\t\t\t\treturn this.setRGB(\n\t\t\t\t\t\t\tMath.min( 255, parseInt( color[ 1 ], 10 ) ) / 255,\n\t\t\t\t\t\t\tMath.min( 255, parseInt( color[ 2 ], 10 ) ) / 255,\n\t\t\t\t\t\t\tMath.min( 255, parseInt( color[ 3 ], 10 ) ) / 255,\n\t\t\t\t\t\t\tcolorSpace\n\t\t\t\t\t\t);\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( color = /^\\s*(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*(?:,\\s*(\\d*\\.?\\d+)\\s*)?$/.exec( components ) ) {\n\n\t\t\t\t\t\t// rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)\n\n\t\t\t\t\t\thandleAlpha( color[ 4 ] );\n\n\t\t\t\t\t\treturn this.setRGB(\n\t\t\t\t\t\t\tMath.min( 100, parseInt( color[ 1 ], 10 ) ) / 100,\n\t\t\t\t\t\t\tMath.min( 100, parseInt( color[ 2 ], 10 ) ) / 100,\n\t\t\t\t\t\t\tMath.min( 100, parseInt( color[ 3 ], 10 ) ) / 100,\n\t\t\t\t\t\t\tcolorSpace\n\t\t\t\t\t\t);\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'hsl':\n\t\t\t\tcase 'hsla':\n\n\t\t\t\t\tif ( color = /^\\s*(\\d*\\.?\\d+)\\s*,\\s*(\\d*\\.?\\d+)\\%\\s*,\\s*(\\d*\\.?\\d+)\\%\\s*(?:,\\s*(\\d*\\.?\\d+)\\s*)?$/.exec( components ) ) {\n\n\t\t\t\t\t\t// hsl(120,50%,50%) hsla(120,50%,50%,0.5)\n\n\t\t\t\t\t\thandleAlpha( color[ 4 ] );\n\n\t\t\t\t\t\treturn this.setHSL(\n\t\t\t\t\t\t\tparseFloat( color[ 1 ] ) / 360,\n\t\t\t\t\t\t\tparseFloat( color[ 2 ] ) / 100,\n\t\t\t\t\t\t\tparseFloat( color[ 3 ] ) / 100,\n\t\t\t\t\t\t\tcolorSpace\n\t\t\t\t\t\t);\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\n\t\t\t\t\tconsole.warn( 'THREE.Color: Unknown color model ' + style );\n\n\t\t\t}\n\n\t\t} else if ( m = /^\\#([A-Fa-f\\d]+)$/.exec( style ) ) {\n\n\t\t\t// hex color\n\n\t\t\tconst hex = m[ 1 ];\n\t\t\tconst size = hex.length;\n\n\t\t\tif ( size === 3 ) {\n\n\t\t\t\t// #ff0\n\t\t\t\treturn this.setRGB(\n\t\t\t\t\tparseInt( hex.charAt( 0 ), 16 ) / 15,\n\t\t\t\t\tparseInt( hex.charAt( 1 ), 16 ) / 15,\n\t\t\t\t\tparseInt( hex.charAt( 2 ), 16 ) / 15,\n\t\t\t\t\tcolorSpace\n\t\t\t\t);\n\n\t\t\t} else if ( size === 6 ) {\n\n\t\t\t\t// #ff0000\n\t\t\t\treturn this.setHex( parseInt( hex, 16 ), colorSpace );\n\n\t\t\t} else {\n\n\t\t\t\tconsole.warn( 'THREE.Color: Invalid hex color ' + style );\n\n\t\t\t}\n\n\t\t} else if ( style && style.length > 0 ) {\n\n\t\t\treturn this.setColorName( style, colorSpace );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetColorName( style, colorSpace = SRGBColorSpace ) {\n\n\t\t// color keywords\n\t\tconst hex = _colorKeywords[ style.toLowerCase() ];\n\n\t\tif ( hex !== undefined ) {\n\n\t\t\t// red\n\t\t\tthis.setHex( hex, colorSpace );\n\n\t\t} else {\n\n\t\t\t// unknown color\n\t\t\tconsole.warn( 'THREE.Color: Unknown color ' + style );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this.r, this.g, this.b );\n\n\t}\n\n\tcopy( color ) {\n\n\t\tthis.r = color.r;\n\t\tthis.g = color.g;\n\t\tthis.b = color.b;\n\n\t\treturn this;\n\n\t}\n\n\tcopySRGBToLinear( color ) {\n\n\t\tthis.r = SRGBToLinear( color.r );\n\t\tthis.g = SRGBToLinear( color.g );\n\t\tthis.b = SRGBToLinear( color.b );\n\n\t\treturn this;\n\n\t}\n\n\tcopyLinearToSRGB( color ) {\n\n\t\tthis.r = LinearToSRGB( color.r );\n\t\tthis.g = LinearToSRGB( color.g );\n\t\tthis.b = LinearToSRGB( color.b );\n\n\t\treturn this;\n\n\t}\n\n\tconvertSRGBToLinear() {\n\n\t\tthis.copySRGBToLinear( this );\n\n\t\treturn this;\n\n\t}\n\n\tconvertLinearToSRGB() {\n\n\t\tthis.copyLinearToSRGB( this );\n\n\t\treturn this;\n\n\t}\n\n\tgetHex( colorSpace = SRGBColorSpace ) {\n\n\t\tColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace );\n\n\t\treturn Math.round( clamp( _color.r * 255, 0, 255 ) ) * 65536 + Math.round( clamp( _color.g * 255, 0, 255 ) ) * 256 + Math.round( clamp( _color.b * 255, 0, 255 ) );\n\n\t}\n\n\tgetHexString( colorSpace = SRGBColorSpace ) {\n\n\t\treturn ( '000000' + this.getHex( colorSpace ).toString( 16 ) ).slice( - 6 );\n\n\t}\n\n\tgetHSL( target, colorSpace = ColorManagement.workingColorSpace ) {\n\n\t\t// h,s,l ranges are in 0.0 - 1.0\n\n\t\tColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace );\n\n\t\tconst r = _color.r, g = _color.g, b = _color.b;\n\n\t\tconst max = Math.max( r, g, b );\n\t\tconst min = Math.min( r, g, b );\n\n\t\tlet hue, saturation;\n\t\tconst lightness = ( min + max ) / 2.0;\n\n\t\tif ( min === max ) {\n\n\t\t\thue = 0;\n\t\t\tsaturation = 0;\n\n\t\t} else {\n\n\t\t\tconst delta = max - min;\n\n\t\t\tsaturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );\n\n\t\t\tswitch ( max ) {\n\n\t\t\t\tcase r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;\n\t\t\t\tcase g: hue = ( b - r ) / delta + 2; break;\n\t\t\t\tcase b: hue = ( r - g ) / delta + 4; break;\n\n\t\t\t}\n\n\t\t\thue /= 6;\n\n\t\t}\n\n\t\ttarget.h = hue;\n\t\ttarget.s = saturation;\n\t\ttarget.l = lightness;\n\n\t\treturn target;\n\n\t}\n\n\tgetRGB( target, colorSpace = ColorManagement.workingColorSpace ) {\n\n\t\tColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace );\n\n\t\ttarget.r = _color.r;\n\t\ttarget.g = _color.g;\n\t\ttarget.b = _color.b;\n\n\t\treturn target;\n\n\t}\n\n\tgetStyle( colorSpace = SRGBColorSpace ) {\n\n\t\tColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace );\n\n\t\tconst r = _color.r, g = _color.g, b = _color.b;\n\n\t\tif ( colorSpace !== SRGBColorSpace ) {\n\n\t\t\t// Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/).\n\t\t\treturn `color(${ colorSpace } ${ r.toFixed( 3 ) } ${ g.toFixed( 3 ) } ${ b.toFixed( 3 ) })`;\n\n\t\t}\n\n\t\treturn `rgb(${ Math.round( r * 255 ) },${ Math.round( g * 255 ) },${ Math.round( b * 255 ) })`;\n\n\t}\n\n\toffsetHSL( h, s, l ) {\n\n\t\tthis.getHSL( _hslA );\n\n\t\treturn this.setHSL( _hslA.h + h, _hslA.s + s, _hslA.l + l );\n\n\t}\n\n\tadd( color ) {\n\n\t\tthis.r += color.r;\n\t\tthis.g += color.g;\n\t\tthis.b += color.b;\n\n\t\treturn this;\n\n\t}\n\n\taddColors( color1, color2 ) {\n\n\t\tthis.r = color1.r + color2.r;\n\t\tthis.g = color1.g + color2.g;\n\t\tthis.b = color1.b + color2.b;\n\n\t\treturn this;\n\n\t}\n\n\taddScalar( s ) {\n\n\t\tthis.r += s;\n\t\tthis.g += s;\n\t\tthis.b += s;\n\n\t\treturn this;\n\n\t}\n\n\tsub( color ) {\n\n\t\tthis.r = Math.max( 0, this.r - color.r );\n\t\tthis.g = Math.max( 0, this.g - color.g );\n\t\tthis.b = Math.max( 0, this.b - color.b );\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( color ) {\n\n\t\tthis.r *= color.r;\n\t\tthis.g *= color.g;\n\t\tthis.b *= color.b;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyScalar( s ) {\n\n\t\tthis.r *= s;\n\t\tthis.g *= s;\n\t\tthis.b *= s;\n\n\t\treturn this;\n\n\t}\n\n\tlerp( color, alpha ) {\n\n\t\tthis.r += ( color.r - this.r ) * alpha;\n\t\tthis.g += ( color.g - this.g ) * alpha;\n\t\tthis.b += ( color.b - this.b ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tlerpColors( color1, color2, alpha ) {\n\n\t\tthis.r = color1.r + ( color2.r - color1.r ) * alpha;\n\t\tthis.g = color1.g + ( color2.g - color1.g ) * alpha;\n\t\tthis.b = color1.b + ( color2.b - color1.b ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tlerpHSL( color, alpha ) {\n\n\t\tthis.getHSL( _hslA );\n\t\tcolor.getHSL( _hslB );\n\n\t\tconst h = lerp( _hslA.h, _hslB.h, alpha );\n\t\tconst s = lerp( _hslA.s, _hslB.s, alpha );\n\t\tconst l = lerp( _hslA.l, _hslB.l, alpha );\n\n\t\tthis.setHSL( h, s, l );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromVector3( v ) {\n\n\t\tthis.r = v.x;\n\t\tthis.g = v.y;\n\t\tthis.b = v.z;\n\n\t\treturn this;\n\n\t}\n\n\tapplyMatrix3( m ) {\n\n\t\tconst r = this.r, g = this.g, b = this.b;\n\t\tconst e = m.elements;\n\n\t\tthis.r = e[ 0 ] * r + e[ 3 ] * g + e[ 6 ] * b;\n\t\tthis.g = e[ 1 ] * r + e[ 4 ] * g + e[ 7 ] * b;\n\t\tthis.b = e[ 2 ] * r + e[ 5 ] * g + e[ 8 ] * b;\n\n\t\treturn this;\n\n\t}\n\n\tequals( c ) {\n\n\t\treturn ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tthis.r = array[ offset ];\n\t\tthis.g = array[ offset + 1 ];\n\t\tthis.b = array[ offset + 2 ];\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tarray[ offset ] = this.r;\n\t\tarray[ offset + 1 ] = this.g;\n\t\tarray[ offset + 2 ] = this.b;\n\n\t\treturn array;\n\n\t}\n\n\tfromBufferAttribute( attribute, index ) {\n\n\t\tthis.r = attribute.getX( index );\n\t\tthis.g = attribute.getY( index );\n\t\tthis.b = attribute.getZ( index );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\treturn this.getHex();\n\n\t}\n\n\t*[ Symbol.iterator ]() {\n\n\t\tyield this.r;\n\t\tyield this.g;\n\t\tyield this.b;\n\n\t}\n\n}\n\nconst _color = /*@__PURE__*/ new Color();\n\nColor.NAMES = _colorKeywords;\n\nlet _materialId = 0;\n\nclass Material extends EventDispatcher {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isMaterial = true;\n\n\t\tObject.defineProperty( this, 'id', { value: _materialId ++ } );\n\n\t\tthis.uuid = generateUUID();\n\n\t\tthis.name = '';\n\t\tthis.type = 'Material';\n\n\t\tthis.blending = NormalBlending;\n\t\tthis.side = FrontSide;\n\t\tthis.vertexColors = false;\n\n\t\tthis.opacity = 1;\n\t\tthis.transparent = false;\n\t\tthis.alphaHash = false;\n\n\t\tthis.blendSrc = SrcAlphaFactor;\n\t\tthis.blendDst = OneMinusSrcAlphaFactor;\n\t\tthis.blendEquation = AddEquation;\n\t\tthis.blendSrcAlpha = null;\n\t\tthis.blendDstAlpha = null;\n\t\tthis.blendEquationAlpha = null;\n\t\tthis.blendColor = new Color( 0, 0, 0 );\n\t\tthis.blendAlpha = 0;\n\n\t\tthis.depthFunc = LessEqualDepth;\n\t\tthis.depthTest = true;\n\t\tthis.depthWrite = true;\n\n\t\tthis.stencilWriteMask = 0xff;\n\t\tthis.stencilFunc = AlwaysStencilFunc;\n\t\tthis.stencilRef = 0;\n\t\tthis.stencilFuncMask = 0xff;\n\t\tthis.stencilFail = KeepStencilOp;\n\t\tthis.stencilZFail = KeepStencilOp;\n\t\tthis.stencilZPass = KeepStencilOp;\n\t\tthis.stencilWrite = false;\n\n\t\tthis.clippingPlanes = null;\n\t\tthis.clipIntersection = false;\n\t\tthis.clipShadows = false;\n\n\t\tthis.shadowSide = null;\n\n\t\tthis.colorWrite = true;\n\n\t\tthis.precision = null; // override the renderer's default precision for this material\n\n\t\tthis.polygonOffset = false;\n\t\tthis.polygonOffsetFactor = 0;\n\t\tthis.polygonOffsetUnits = 0;\n\n\t\tthis.dithering = false;\n\n\t\tthis.alphaToCoverage = false;\n\t\tthis.premultipliedAlpha = false;\n\t\tthis.forceSinglePass = false;\n\n\t\tthis.visible = true;\n\n\t\tthis.toneMapped = true;\n\n\t\tthis.userData = {};\n\n\t\tthis.version = 0;\n\n\t\tthis._alphaTest = 0;\n\n\t}\n\n\tget alphaTest() {\n\n\t\treturn this._alphaTest;\n\n\t}\n\n\tset alphaTest( value ) {\n\n\t\tif ( this._alphaTest > 0 !== value > 0 ) {\n\n\t\t\tthis.version ++;\n\n\t\t}\n\n\t\tthis._alphaTest = value;\n\n\t}\n\n\tonBuild( /* shaderobject, renderer */ ) {}\n\n\tonBeforeRender( /* renderer, scene, camera, geometry, object, group */ ) {}\n\n\tonBeforeCompile( /* shaderobject, renderer */ ) {}\n\n\tcustomProgramCacheKey() {\n\n\t\treturn this.onBeforeCompile.toString();\n\n\t}\n\n\tsetValues( values ) {\n\n\t\tif ( values === undefined ) return;\n\n\t\tfor ( const key in values ) {\n\n\t\t\tconst newValue = values[ key ];\n\n\t\t\tif ( newValue === undefined ) {\n\n\t\t\t\tconsole.warn( `THREE.Material: parameter '${ key }' has value of undefined.` );\n\t\t\t\tcontinue;\n\n\t\t\t}\n\n\t\t\tconst currentValue = this[ key ];\n\n\t\t\tif ( currentValue === undefined ) {\n\n\t\t\t\tconsole.warn( `THREE.Material: '${ key }' is not a property of THREE.${ this.type }.` );\n\t\t\t\tcontinue;\n\n\t\t\t}\n\n\t\t\tif ( currentValue && currentValue.isColor ) {\n\n\t\t\t\tcurrentValue.set( newValue );\n\n\t\t\t} else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) {\n\n\t\t\t\tcurrentValue.copy( newValue );\n\n\t\t\t} else {\n\n\t\t\t\tthis[ key ] = newValue;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst isRootObject = ( meta === undefined || typeof meta === 'string' );\n\n\t\tif ( isRootObject ) {\n\n\t\t\tmeta = {\n\t\t\t\ttextures: {},\n\t\t\t\timages: {}\n\t\t\t};\n\n\t\t}\n\n\t\tconst data = {\n\t\t\tmetadata: {\n\t\t\t\tversion: 4.6,\n\t\t\t\ttype: 'Material',\n\t\t\t\tgenerator: 'Material.toJSON'\n\t\t\t}\n\t\t};\n\n\t\t// standard Material serialization\n\t\tdata.uuid = this.uuid;\n\t\tdata.type = this.type;\n\n\t\tif ( this.name !== '' ) data.name = this.name;\n\n\t\tif ( this.color && this.color.isColor ) data.color = this.color.getHex();\n\n\t\tif ( this.roughness !== undefined ) data.roughness = this.roughness;\n\t\tif ( this.metalness !== undefined ) data.metalness = this.metalness;\n\n\t\tif ( this.sheen !== undefined ) data.sheen = this.sheen;\n\t\tif ( this.sheenColor && this.sheenColor.isColor ) data.sheenColor = this.sheenColor.getHex();\n\t\tif ( this.sheenRoughness !== undefined ) data.sheenRoughness = this.sheenRoughness;\n\t\tif ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex();\n\t\tif ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity;\n\n\t\tif ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex();\n\t\tif ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity;\n\t\tif ( this.specularColor && this.specularColor.isColor ) data.specularColor = this.specularColor.getHex();\n\t\tif ( this.shininess !== undefined ) data.shininess = this.shininess;\n\t\tif ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat;\n\t\tif ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness;\n\n\t\tif ( this.clearcoatMap && this.clearcoatMap.isTexture ) {\n\n\t\t\tdata.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid;\n\n\t\t}\n\n\t\tif ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) {\n\n\t\t\tdata.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid;\n\n\t\t}\n\n\t\tif ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) {\n\n\t\t\tdata.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid;\n\t\t\tdata.clearcoatNormalScale = this.clearcoatNormalScale.toArray();\n\n\t\t}\n\n\t\tif ( this.iridescence !== undefined ) data.iridescence = this.iridescence;\n\t\tif ( this.iridescenceIOR !== undefined ) data.iridescenceIOR = this.iridescenceIOR;\n\t\tif ( this.iridescenceThicknessRange !== undefined ) data.iridescenceThicknessRange = this.iridescenceThicknessRange;\n\n\t\tif ( this.iridescenceMap && this.iridescenceMap.isTexture ) {\n\n\t\t\tdata.iridescenceMap = this.iridescenceMap.toJSON( meta ).uuid;\n\n\t\t}\n\n\t\tif ( this.iridescenceThicknessMap && this.iridescenceThicknessMap.isTexture ) {\n\n\t\t\tdata.iridescenceThicknessMap = this.iridescenceThicknessMap.toJSON( meta ).uuid;\n\n\t\t}\n\n\t\tif ( this.anisotropy !== undefined ) data.anisotropy = this.anisotropy;\n\t\tif ( this.anisotropyRotation !== undefined ) data.anisotropyRotation = this.anisotropyRotation;\n\n\t\tif ( this.anisotropyMap && this.anisotropyMap.isTexture ) {\n\n\t\t\tdata.anisotropyMap = this.anisotropyMap.toJSON( meta ).uuid;\n\n\t\t}\n\n\t\tif ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid;\n\t\tif ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid;\n\t\tif ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;\n\n\t\tif ( this.lightMap && this.lightMap.isTexture ) {\n\n\t\t\tdata.lightMap = this.lightMap.toJSON( meta ).uuid;\n\t\t\tdata.lightMapIntensity = this.lightMapIntensity;\n\n\t\t}\n\n\t\tif ( this.aoMap && this.aoMap.isTexture ) {\n\n\t\t\tdata.aoMap = this.aoMap.toJSON( meta ).uuid;\n\t\t\tdata.aoMapIntensity = this.aoMapIntensity;\n\n\t\t}\n\n\t\tif ( this.bumpMap && this.bumpMap.isTexture ) {\n\n\t\t\tdata.bumpMap = this.bumpMap.toJSON( meta ).uuid;\n\t\t\tdata.bumpScale = this.bumpScale;\n\n\t\t}\n\n\t\tif ( this.normalMap && this.normalMap.isTexture ) {\n\n\t\t\tdata.normalMap = this.normalMap.toJSON( meta ).uuid;\n\t\t\tdata.normalMapType = this.normalMapType;\n\t\t\tdata.normalScale = this.normalScale.toArray();\n\n\t\t}\n\n\t\tif ( this.displacementMap && this.displacementMap.isTexture ) {\n\n\t\t\tdata.displacementMap = this.displacementMap.toJSON( meta ).uuid;\n\t\t\tdata.displacementScale = this.displacementScale;\n\t\t\tdata.displacementBias = this.displacementBias;\n\n\t\t}\n\n\t\tif ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid;\n\t\tif ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid;\n\n\t\tif ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid;\n\t\tif ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;\n\t\tif ( this.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid;\n\t\tif ( this.specularColorMap && this.specularColorMap.isTexture ) data.specularColorMap = this.specularColorMap.toJSON( meta ).uuid;\n\n\t\tif ( this.envMap && this.envMap.isTexture ) {\n\n\t\t\tdata.envMap = this.envMap.toJSON( meta ).uuid;\n\n\t\t\tif ( this.combine !== undefined ) data.combine = this.combine;\n\n\t\t}\n\n\t\tif ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity;\n\t\tif ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity;\n\t\tif ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio;\n\n\t\tif ( this.gradientMap && this.gradientMap.isTexture ) {\n\n\t\t\tdata.gradientMap = this.gradientMap.toJSON( meta ).uuid;\n\n\t\t}\n\n\t\tif ( this.transmission !== undefined ) data.transmission = this.transmission;\n\t\tif ( this.transmissionMap && this.transmissionMap.isTexture ) data.transmissionMap = this.transmissionMap.toJSON( meta ).uuid;\n\t\tif ( this.thickness !== undefined ) data.thickness = this.thickness;\n\t\tif ( this.thicknessMap && this.thicknessMap.isTexture ) data.thicknessMap = this.thicknessMap.toJSON( meta ).uuid;\n\t\tif ( this.attenuationDistance !== undefined && this.attenuationDistance !== Infinity ) data.attenuationDistance = this.attenuationDistance;\n\t\tif ( this.attenuationColor !== undefined ) data.attenuationColor = this.attenuationColor.getHex();\n\n\t\tif ( this.size !== undefined ) data.size = this.size;\n\t\tif ( this.shadowSide !== null ) data.shadowSide = this.shadowSide;\n\t\tif ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;\n\n\t\tif ( this.blending !== NormalBlending ) data.blending = this.blending;\n\t\tif ( this.side !== FrontSide ) data.side = this.side;\n\t\tif ( this.vertexColors === true ) data.vertexColors = true;\n\n\t\tif ( this.opacity < 1 ) data.opacity = this.opacity;\n\t\tif ( this.transparent === true ) data.transparent = true;\n\n\t\tif ( this.blendSrc !== SrcAlphaFactor ) data.blendSrc = this.blendSrc;\n\t\tif ( this.blendDst !== OneMinusSrcAlphaFactor ) data.blendDst = this.blendDst;\n\t\tif ( this.blendEquation !== AddEquation ) data.blendEquation = this.blendEquation;\n\t\tif ( this.blendSrcAlpha !== null ) data.blendSrcAlpha = this.blendSrcAlpha;\n\t\tif ( this.blendDstAlpha !== null ) data.blendDstAlpha = this.blendDstAlpha;\n\t\tif ( this.blendEquationAlpha !== null ) data.blendEquationAlpha = this.blendEquationAlpha;\n\t\tif ( this.blendColor && this.blendColor.isColor ) data.blendColor = this.blendColor.getHex();\n\t\tif ( this.blendAlpha !== 0 ) data.blendAlpha = this.blendAlpha;\n\n\t\tif ( this.depthFunc !== LessEqualDepth ) data.depthFunc = this.depthFunc;\n\t\tif ( this.depthTest === false ) data.depthTest = this.depthTest;\n\t\tif ( this.depthWrite === false ) data.depthWrite = this.depthWrite;\n\t\tif ( this.colorWrite === false ) data.colorWrite = this.colorWrite;\n\n\t\tif ( this.stencilWriteMask !== 0xff ) data.stencilWriteMask = this.stencilWriteMask;\n\t\tif ( this.stencilFunc !== AlwaysStencilFunc ) data.stencilFunc = this.stencilFunc;\n\t\tif ( this.stencilRef !== 0 ) data.stencilRef = this.stencilRef;\n\t\tif ( this.stencilFuncMask !== 0xff ) data.stencilFuncMask = this.stencilFuncMask;\n\t\tif ( this.stencilFail !== KeepStencilOp ) data.stencilFail = this.stencilFail;\n\t\tif ( this.stencilZFail !== KeepStencilOp ) data.stencilZFail = this.stencilZFail;\n\t\tif ( this.stencilZPass !== KeepStencilOp ) data.stencilZPass = this.stencilZPass;\n\t\tif ( this.stencilWrite === true ) data.stencilWrite = this.stencilWrite;\n\n\t\t// rotation (SpriteMaterial)\n\t\tif ( this.rotation !== undefined && this.rotation !== 0 ) data.rotation = this.rotation;\n\n\t\tif ( this.polygonOffset === true ) data.polygonOffset = true;\n\t\tif ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor;\n\t\tif ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits;\n\n\t\tif ( this.linewidth !== undefined && this.linewidth !== 1 ) data.linewidth = this.linewidth;\n\t\tif ( this.dashSize !== undefined ) data.dashSize = this.dashSize;\n\t\tif ( this.gapSize !== undefined ) data.gapSize = this.gapSize;\n\t\tif ( this.scale !== undefined ) data.scale = this.scale;\n\n\t\tif ( this.dithering === true ) data.dithering = true;\n\n\t\tif ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;\n\t\tif ( this.alphaHash === true ) data.alphaHash = true;\n\t\tif ( this.alphaToCoverage === true ) data.alphaToCoverage = true;\n\t\tif ( this.premultipliedAlpha === true ) data.premultipliedAlpha = true;\n\t\tif ( this.forceSinglePass === true ) data.forceSinglePass = true;\n\n\t\tif ( this.wireframe === true ) data.wireframe = true;\n\t\tif ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;\n\t\tif ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;\n\t\tif ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;\n\n\t\tif ( this.flatShading === true ) data.flatShading = true;\n\n\t\tif ( this.visible === false ) data.visible = false;\n\n\t\tif ( this.toneMapped === false ) data.toneMapped = false;\n\n\t\tif ( this.fog === false ) data.fog = false;\n\n\t\tif ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData;\n\n\t\t// TODO: Copied from Object3D.toJSON\n\n\t\tfunction extractFromCache( cache ) {\n\n\t\t\tconst values = [];\n\n\t\t\tfor ( const key in cache ) {\n\n\t\t\t\tconst data = cache[ key ];\n\t\t\t\tdelete data.metadata;\n\t\t\t\tvalues.push( data );\n\n\t\t\t}\n\n\t\t\treturn values;\n\n\t\t}\n\n\t\tif ( isRootObject ) {\n\n\t\t\tconst textures = extractFromCache( meta.textures );\n\t\t\tconst images = extractFromCache( meta.images );\n\n\t\t\tif ( textures.length > 0 ) data.textures = textures;\n\t\t\tif ( images.length > 0 ) data.images = images;\n\n\t\t}\n\n\t\treturn data;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.name = source.name;\n\n\t\tthis.blending = source.blending;\n\t\tthis.side = source.side;\n\t\tthis.vertexColors = source.vertexColors;\n\n\t\tthis.opacity = source.opacity;\n\t\tthis.transparent = source.transparent;\n\n\t\tthis.blendSrc = source.blendSrc;\n\t\tthis.blendDst = source.blendDst;\n\t\tthis.blendEquation = source.blendEquation;\n\t\tthis.blendSrcAlpha = source.blendSrcAlpha;\n\t\tthis.blendDstAlpha = source.blendDstAlpha;\n\t\tthis.blendEquationAlpha = source.blendEquationAlpha;\n\t\tthis.blendColor.copy( source.blendColor );\n\t\tthis.blendAlpha = source.blendAlpha;\n\n\t\tthis.depthFunc = source.depthFunc;\n\t\tthis.depthTest = source.depthTest;\n\t\tthis.depthWrite = source.depthWrite;\n\n\t\tthis.stencilWriteMask = source.stencilWriteMask;\n\t\tthis.stencilFunc = source.stencilFunc;\n\t\tthis.stencilRef = source.stencilRef;\n\t\tthis.stencilFuncMask = source.stencilFuncMask;\n\t\tthis.stencilFail = source.stencilFail;\n\t\tthis.stencilZFail = source.stencilZFail;\n\t\tthis.stencilZPass = source.stencilZPass;\n\t\tthis.stencilWrite = source.stencilWrite;\n\n\t\tconst srcPlanes = source.clippingPlanes;\n\t\tlet dstPlanes = null;\n\n\t\tif ( srcPlanes !== null ) {\n\n\t\t\tconst n = srcPlanes.length;\n\t\t\tdstPlanes = new Array( n );\n\n\t\t\tfor ( let i = 0; i !== n; ++ i ) {\n\n\t\t\t\tdstPlanes[ i ] = srcPlanes[ i ].clone();\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.clippingPlanes = dstPlanes;\n\t\tthis.clipIntersection = source.clipIntersection;\n\t\tthis.clipShadows = source.clipShadows;\n\n\t\tthis.shadowSide = source.shadowSide;\n\n\t\tthis.colorWrite = source.colorWrite;\n\n\t\tthis.precision = source.precision;\n\n\t\tthis.polygonOffset = source.polygonOffset;\n\t\tthis.polygonOffsetFactor = source.polygonOffsetFactor;\n\t\tthis.polygonOffsetUnits = source.polygonOffsetUnits;\n\n\t\tthis.dithering = source.dithering;\n\n\t\tthis.alphaTest = source.alphaTest;\n\t\tthis.alphaHash = source.alphaHash;\n\t\tthis.alphaToCoverage = source.alphaToCoverage;\n\t\tthis.premultipliedAlpha = source.premultipliedAlpha;\n\t\tthis.forceSinglePass = source.forceSinglePass;\n\n\t\tthis.visible = source.visible;\n\n\t\tthis.toneMapped = source.toneMapped;\n\n\t\tthis.userData = JSON.parse( JSON.stringify( source.userData ) );\n\n\t\treturn this;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t}\n\n\tset needsUpdate( value ) {\n\n\t\tif ( value === true ) this.version ++;\n\n\t}\n\n}\n\nclass MeshBasicMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshBasicMaterial = true;\n\n\t\tthis.type = 'MeshBasicMaterial';\n\n\t\tthis.color = new Color( 0xffffff ); // emissive\n\n\t\tthis.map = null;\n\n\t\tthis.lightMap = null;\n\t\tthis.lightMapIntensity = 1.0;\n\n\t\tthis.aoMap = null;\n\t\tthis.aoMapIntensity = 1.0;\n\n\t\tthis.specularMap = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.envMap = null;\n\t\tthis.combine = MultiplyOperation;\n\t\tthis.reflectivity = 1;\n\t\tthis.refractionRatio = 0.98;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\t\tthis.wireframeLinecap = 'round';\n\t\tthis.wireframeLinejoin = 'round';\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.map = source.map;\n\n\t\tthis.lightMap = source.lightMap;\n\t\tthis.lightMapIntensity = source.lightMapIntensity;\n\n\t\tthis.aoMap = source.aoMap;\n\t\tthis.aoMapIntensity = source.aoMapIntensity;\n\n\t\tthis.specularMap = source.specularMap;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.envMap = source.envMap;\n\t\tthis.combine = source.combine;\n\t\tthis.reflectivity = source.reflectivity;\n\t\tthis.refractionRatio = source.refractionRatio;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\t\tthis.wireframeLinecap = source.wireframeLinecap;\n\t\tthis.wireframeLinejoin = source.wireframeLinejoin;\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\n// Fast Half Float Conversions, http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf\n\nconst _tables = /*@__PURE__*/ _generateTables();\n\nfunction _generateTables() {\n\n\t// float32 to float16 helpers\n\n\tconst buffer = new ArrayBuffer( 4 );\n\tconst floatView = new Float32Array( buffer );\n\tconst uint32View = new Uint32Array( buffer );\n\n\tconst baseTable = new Uint32Array( 512 );\n\tconst shiftTable = new Uint32Array( 512 );\n\n\tfor ( let i = 0; i < 256; ++ i ) {\n\n\t\tconst e = i - 127;\n\n\t\t// very small number (0, -0)\n\n\t\tif ( e < - 27 ) {\n\n\t\t\tbaseTable[ i ] = 0x0000;\n\t\t\tbaseTable[ i | 0x100 ] = 0x8000;\n\t\t\tshiftTable[ i ] = 24;\n\t\t\tshiftTable[ i | 0x100 ] = 24;\n\n\t\t\t// small number (denorm)\n\n\t\t} else if ( e < - 14 ) {\n\n\t\t\tbaseTable[ i ] = 0x0400 >> ( - e - 14 );\n\t\t\tbaseTable[ i | 0x100 ] = ( 0x0400 >> ( - e - 14 ) ) | 0x8000;\n\t\t\tshiftTable[ i ] = - e - 1;\n\t\t\tshiftTable[ i | 0x100 ] = - e - 1;\n\n\t\t\t// normal number\n\n\t\t} else if ( e <= 15 ) {\n\n\t\t\tbaseTable[ i ] = ( e + 15 ) << 10;\n\t\t\tbaseTable[ i | 0x100 ] = ( ( e + 15 ) << 10 ) | 0x8000;\n\t\t\tshiftTable[ i ] = 13;\n\t\t\tshiftTable[ i | 0x100 ] = 13;\n\n\t\t\t// large number (Infinity, -Infinity)\n\n\t\t} else if ( e < 128 ) {\n\n\t\t\tbaseTable[ i ] = 0x7c00;\n\t\t\tbaseTable[ i | 0x100 ] = 0xfc00;\n\t\t\tshiftTable[ i ] = 24;\n\t\t\tshiftTable[ i | 0x100 ] = 24;\n\n\t\t\t// stay (NaN, Infinity, -Infinity)\n\n\t\t} else {\n\n\t\t\tbaseTable[ i ] = 0x7c00;\n\t\t\tbaseTable[ i | 0x100 ] = 0xfc00;\n\t\t\tshiftTable[ i ] = 13;\n\t\t\tshiftTable[ i | 0x100 ] = 13;\n\n\t\t}\n\n\t}\n\n\t// float16 to float32 helpers\n\n\tconst mantissaTable = new Uint32Array( 2048 );\n\tconst exponentTable = new Uint32Array( 64 );\n\tconst offsetTable = new Uint32Array( 64 );\n\n\tfor ( let i = 1; i < 1024; ++ i ) {\n\n\t\tlet m = i << 13; // zero pad mantissa bits\n\t\tlet e = 0; // zero exponent\n\n\t\t// normalized\n\t\twhile ( ( m & 0x00800000 ) === 0 ) {\n\n\t\t\tm <<= 1;\n\t\t\te -= 0x00800000; // decrement exponent\n\n\t\t}\n\n\t\tm &= ~ 0x00800000; // clear leading 1 bit\n\t\te += 0x38800000; // adjust bias\n\n\t\tmantissaTable[ i ] = m | e;\n\n\t}\n\n\tfor ( let i = 1024; i < 2048; ++ i ) {\n\n\t\tmantissaTable[ i ] = 0x38000000 + ( ( i - 1024 ) << 13 );\n\n\t}\n\n\tfor ( let i = 1; i < 31; ++ i ) {\n\n\t\texponentTable[ i ] = i << 23;\n\n\t}\n\n\texponentTable[ 31 ] = 0x47800000;\n\texponentTable[ 32 ] = 0x80000000;\n\n\tfor ( let i = 33; i < 63; ++ i ) {\n\n\t\texponentTable[ i ] = 0x80000000 + ( ( i - 32 ) << 23 );\n\n\t}\n\n\texponentTable[ 63 ] = 0xc7800000;\n\n\tfor ( let i = 1; i < 64; ++ i ) {\n\n\t\tif ( i !== 32 ) {\n\n\t\t\toffsetTable[ i ] = 1024;\n\n\t\t}\n\n\t}\n\n\treturn {\n\t\tfloatView: floatView,\n\t\tuint32View: uint32View,\n\t\tbaseTable: baseTable,\n\t\tshiftTable: shiftTable,\n\t\tmantissaTable: mantissaTable,\n\t\texponentTable: exponentTable,\n\t\toffsetTable: offsetTable\n\t};\n\n}\n\n// float32 to float16\n\nfunction toHalfFloat( val ) {\n\n\tif ( Math.abs( val ) > 65504 ) console.warn( 'THREE.DataUtils.toHalfFloat(): Value out of range.' );\n\n\tval = clamp( val, - 65504, 65504 );\n\n\t_tables.floatView[ 0 ] = val;\n\tconst f = _tables.uint32View[ 0 ];\n\tconst e = ( f >> 23 ) & 0x1ff;\n\treturn _tables.baseTable[ e ] + ( ( f & 0x007fffff ) >> _tables.shiftTable[ e ] );\n\n}\n\n// float16 to float32\n\nfunction fromHalfFloat( val ) {\n\n\tconst m = val >> 10;\n\t_tables.uint32View[ 0 ] = _tables.mantissaTable[ _tables.offsetTable[ m ] + ( val & 0x3ff ) ] + _tables.exponentTable[ m ];\n\treturn _tables.floatView[ 0 ];\n\n}\n\nconst DataUtils = {\n\ttoHalfFloat: toHalfFloat,\n\tfromHalfFloat: fromHalfFloat,\n};\n\nconst _vector$9 = /*@__PURE__*/ new Vector3();\nconst _vector2$1 = /*@__PURE__*/ new Vector2();\n\nclass BufferAttribute {\n\n\tconstructor( array, itemSize, normalized = false ) {\n\n\t\tif ( Array.isArray( array ) ) {\n\n\t\t\tthrow new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );\n\n\t\t}\n\n\t\tthis.isBufferAttribute = true;\n\n\t\tthis.name = '';\n\n\t\tthis.array = array;\n\t\tthis.itemSize = itemSize;\n\t\tthis.count = array !== undefined ? array.length / itemSize : 0;\n\t\tthis.normalized = normalized;\n\n\t\tthis.usage = StaticDrawUsage;\n\t\tthis._updateRange = { offset: 0, count: - 1 };\n\t\tthis.updateRanges = [];\n\t\tthis.gpuType = FloatType;\n\n\t\tthis.version = 0;\n\n\t}\n\n\tonUploadCallback() {}\n\n\tset needsUpdate( value ) {\n\n\t\tif ( value === true ) this.version ++;\n\n\t}\n\n\tget updateRange() {\n\n\t\tconsole.warn( 'THREE.BufferAttribute: updateRange() is deprecated and will be removed in r169. Use addUpdateRange() instead.' ); // @deprecated, r159\n\t\treturn this._updateRange;\n\n\t}\n\n\tsetUsage( value ) {\n\n\t\tthis.usage = value;\n\n\t\treturn this;\n\n\t}\n\n\taddUpdateRange( start, count ) {\n\n\t\tthis.updateRanges.push( { start, count } );\n\n\t}\n\n\tclearUpdateRanges() {\n\n\t\tthis.updateRanges.length = 0;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.name = source.name;\n\t\tthis.array = new source.array.constructor( source.array );\n\t\tthis.itemSize = source.itemSize;\n\t\tthis.count = source.count;\n\t\tthis.normalized = source.normalized;\n\n\t\tthis.usage = source.usage;\n\t\tthis.gpuType = source.gpuType;\n\n\t\treturn this;\n\n\t}\n\n\tcopyAt( index1, attribute, index2 ) {\n\n\t\tindex1 *= this.itemSize;\n\t\tindex2 *= attribute.itemSize;\n\n\t\tfor ( let i = 0, l = this.itemSize; i < l; i ++ ) {\n\n\t\t\tthis.array[ index1 + i ] = attribute.array[ index2 + i ];\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tcopyArray( array ) {\n\n\t\tthis.array.set( array );\n\n\t\treturn this;\n\n\t}\n\n\tapplyMatrix3( m ) {\n\n\t\tif ( this.itemSize === 2 ) {\n\n\t\t\tfor ( let i = 0, l = this.count; i < l; i ++ ) {\n\n\t\t\t\t_vector2$1.fromBufferAttribute( this, i );\n\t\t\t\t_vector2$1.applyMatrix3( m );\n\n\t\t\t\tthis.setXY( i, _vector2$1.x, _vector2$1.y );\n\n\t\t\t}\n\n\t\t} else if ( this.itemSize === 3 ) {\n\n\t\t\tfor ( let i = 0, l = this.count; i < l; i ++ ) {\n\n\t\t\t\t_vector$9.fromBufferAttribute( this, i );\n\t\t\t\t_vector$9.applyMatrix3( m );\n\n\t\t\t\tthis.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tapplyMatrix4( m ) {\n\n\t\tfor ( let i = 0, l = this.count; i < l; i ++ ) {\n\n\t\t\t_vector$9.fromBufferAttribute( this, i );\n\n\t\t\t_vector$9.applyMatrix4( m );\n\n\t\t\tthis.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tapplyNormalMatrix( m ) {\n\n\t\tfor ( let i = 0, l = this.count; i < l; i ++ ) {\n\n\t\t\t_vector$9.fromBufferAttribute( this, i );\n\n\t\t\t_vector$9.applyNormalMatrix( m );\n\n\t\t\tthis.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\ttransformDirection( m ) {\n\n\t\tfor ( let i = 0, l = this.count; i < l; i ++ ) {\n\n\t\t\t_vector$9.fromBufferAttribute( this, i );\n\n\t\t\t_vector$9.transformDirection( m );\n\n\t\t\tthis.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tset( value, offset = 0 ) {\n\n\t\t// Matching BufferAttribute constructor, do not normalize the array.\n\t\tthis.array.set( value, offset );\n\n\t\treturn this;\n\n\t}\n\n\tgetComponent( index, component ) {\n\n\t\tlet value = this.array[ index * this.itemSize + component ];\n\n\t\tif ( this.normalized ) value = denormalize( value, this.array );\n\n\t\treturn value;\n\n\t}\n\n\tsetComponent( index, component, value ) {\n\n\t\tif ( this.normalized ) value = normalize( value, this.array );\n\n\t\tthis.array[ index * this.itemSize + component ] = value;\n\n\t\treturn this;\n\n\t}\n\n\tgetX( index ) {\n\n\t\tlet x = this.array[ index * this.itemSize ];\n\n\t\tif ( this.normalized ) x = denormalize( x, this.array );\n\n\t\treturn x;\n\n\t}\n\n\tsetX( index, x ) {\n\n\t\tif ( this.normalized ) x = normalize( x, this.array );\n\n\t\tthis.array[ index * this.itemSize ] = x;\n\n\t\treturn this;\n\n\t}\n\n\tgetY( index ) {\n\n\t\tlet y = this.array[ index * this.itemSize + 1 ];\n\n\t\tif ( this.normalized ) y = denormalize( y, this.array );\n\n\t\treturn y;\n\n\t}\n\n\tsetY( index, y ) {\n\n\t\tif ( this.normalized ) y = normalize( y, this.array );\n\n\t\tthis.array[ index * this.itemSize + 1 ] = y;\n\n\t\treturn this;\n\n\t}\n\n\tgetZ( index ) {\n\n\t\tlet z = this.array[ index * this.itemSize + 2 ];\n\n\t\tif ( this.normalized ) z = denormalize( z, this.array );\n\n\t\treturn z;\n\n\t}\n\n\tsetZ( index, z ) {\n\n\t\tif ( this.normalized ) z = normalize( z, this.array );\n\n\t\tthis.array[ index * this.itemSize + 2 ] = z;\n\n\t\treturn this;\n\n\t}\n\n\tgetW( index ) {\n\n\t\tlet w = this.array[ index * this.itemSize + 3 ];\n\n\t\tif ( this.normalized ) w = denormalize( w, this.array );\n\n\t\treturn w;\n\n\t}\n\n\tsetW( index, w ) {\n\n\t\tif ( this.normalized ) w = normalize( w, this.array );\n\n\t\tthis.array[ index * this.itemSize + 3 ] = w;\n\n\t\treturn this;\n\n\t}\n\n\tsetXY( index, x, y ) {\n\n\t\tindex *= this.itemSize;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\n\t\t}\n\n\t\tthis.array[ index + 0 ] = x;\n\t\tthis.array[ index + 1 ] = y;\n\n\t\treturn this;\n\n\t}\n\n\tsetXYZ( index, x, y, z ) {\n\n\t\tindex *= this.itemSize;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\t\t\tz = normalize( z, this.array );\n\n\t\t}\n\n\t\tthis.array[ index + 0 ] = x;\n\t\tthis.array[ index + 1 ] = y;\n\t\tthis.array[ index + 2 ] = z;\n\n\t\treturn this;\n\n\t}\n\n\tsetXYZW( index, x, y, z, w ) {\n\n\t\tindex *= this.itemSize;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\t\t\tz = normalize( z, this.array );\n\t\t\tw = normalize( w, this.array );\n\n\t\t}\n\n\t\tthis.array[ index + 0 ] = x;\n\t\tthis.array[ index + 1 ] = y;\n\t\tthis.array[ index + 2 ] = z;\n\t\tthis.array[ index + 3 ] = w;\n\n\t\treturn this;\n\n\t}\n\n\tonUpload( callback ) {\n\n\t\tthis.onUploadCallback = callback;\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this.array, this.itemSize ).copy( this );\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = {\n\t\t\titemSize: this.itemSize,\n\t\t\ttype: this.array.constructor.name,\n\t\t\tarray: Array.from( this.array ),\n\t\t\tnormalized: this.normalized\n\t\t};\n\n\t\tif ( this.name !== '' ) data.name = this.name;\n\t\tif ( this.usage !== StaticDrawUsage ) data.usage = this.usage;\n\n\t\treturn data;\n\n\t}\n\n}\n\n//\n\nclass Int8BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Int8Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Uint8BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Uint8Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Uint8ClampedBufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Uint8ClampedArray( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Int16BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Int16Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Uint16BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Uint16Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Int32BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Int32Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Uint32BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Uint32Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Float16BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Uint16Array( array ), itemSize, normalized );\n\n\t\tthis.isFloat16BufferAttribute = true;\n\n\t}\n\n\tgetX( index ) {\n\n\t\tlet x = fromHalfFloat( this.array[ index * this.itemSize ] );\n\n\t\tif ( this.normalized ) x = denormalize( x, this.array );\n\n\t\treturn x;\n\n\t}\n\n\tsetX( index, x ) {\n\n\t\tif ( this.normalized ) x = normalize( x, this.array );\n\n\t\tthis.array[ index * this.itemSize ] = toHalfFloat( x );\n\n\t\treturn this;\n\n\t}\n\n\tgetY( index ) {\n\n\t\tlet y = fromHalfFloat( this.array[ index * this.itemSize + 1 ] );\n\n\t\tif ( this.normalized ) y = denormalize( y, this.array );\n\n\t\treturn y;\n\n\t}\n\n\tsetY( index, y ) {\n\n\t\tif ( this.normalized ) y = normalize( y, this.array );\n\n\t\tthis.array[ index * this.itemSize + 1 ] = toHalfFloat( y );\n\n\t\treturn this;\n\n\t}\n\n\tgetZ( index ) {\n\n\t\tlet z = fromHalfFloat( this.array[ index * this.itemSize + 2 ] );\n\n\t\tif ( this.normalized ) z = denormalize( z, this.array );\n\n\t\treturn z;\n\n\t}\n\n\tsetZ( index, z ) {\n\n\t\tif ( this.normalized ) z = normalize( z, this.array );\n\n\t\tthis.array[ index * this.itemSize + 2 ] = toHalfFloat( z );\n\n\t\treturn this;\n\n\t}\n\n\tgetW( index ) {\n\n\t\tlet w = fromHalfFloat( this.array[ index * this.itemSize + 3 ] );\n\n\t\tif ( this.normalized ) w = denormalize( w, this.array );\n\n\t\treturn w;\n\n\t}\n\n\tsetW( index, w ) {\n\n\t\tif ( this.normalized ) w = normalize( w, this.array );\n\n\t\tthis.array[ index * this.itemSize + 3 ] = toHalfFloat( w );\n\n\t\treturn this;\n\n\t}\n\n\tsetXY( index, x, y ) {\n\n\t\tindex *= this.itemSize;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\n\t\t}\n\n\t\tthis.array[ index + 0 ] = toHalfFloat( x );\n\t\tthis.array[ index + 1 ] = toHalfFloat( y );\n\n\t\treturn this;\n\n\t}\n\n\tsetXYZ( index, x, y, z ) {\n\n\t\tindex *= this.itemSize;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\t\t\tz = normalize( z, this.array );\n\n\t\t}\n\n\t\tthis.array[ index + 0 ] = toHalfFloat( x );\n\t\tthis.array[ index + 1 ] = toHalfFloat( y );\n\t\tthis.array[ index + 2 ] = toHalfFloat( z );\n\n\t\treturn this;\n\n\t}\n\n\tsetXYZW( index, x, y, z, w ) {\n\n\t\tindex *= this.itemSize;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\t\t\tz = normalize( z, this.array );\n\t\t\tw = normalize( w, this.array );\n\n\t\t}\n\n\t\tthis.array[ index + 0 ] = toHalfFloat( x );\n\t\tthis.array[ index + 1 ] = toHalfFloat( y );\n\t\tthis.array[ index + 2 ] = toHalfFloat( z );\n\t\tthis.array[ index + 3 ] = toHalfFloat( w );\n\n\t\treturn this;\n\n\t}\n\n}\n\n\nclass Float32BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Float32Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Float64BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Float64Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nlet _id$2 = 0;\n\nconst _m1 = /*@__PURE__*/ new Matrix4();\nconst _obj = /*@__PURE__*/ new Object3D();\nconst _offset = /*@__PURE__*/ new Vector3();\nconst _box$2 = /*@__PURE__*/ new Box3();\nconst _boxMorphTargets = /*@__PURE__*/ new Box3();\nconst _vector$8 = /*@__PURE__*/ new Vector3();\n\nclass BufferGeometry extends EventDispatcher {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isBufferGeometry = true;\n\n\t\tObject.defineProperty( this, 'id', { value: _id$2 ++ } );\n\n\t\tthis.uuid = generateUUID();\n\n\t\tthis.name = '';\n\t\tthis.type = 'BufferGeometry';\n\n\t\tthis.index = null;\n\t\tthis.attributes = {};\n\n\t\tthis.morphAttributes = {};\n\t\tthis.morphTargetsRelative = false;\n\n\t\tthis.groups = [];\n\n\t\tthis.boundingBox = null;\n\t\tthis.boundingSphere = null;\n\n\t\tthis.drawRange = { start: 0, count: Infinity };\n\n\t\tthis.userData = {};\n\n\t}\n\n\tgetIndex() {\n\n\t\treturn this.index;\n\n\t}\n\n\tsetIndex( index ) {\n\n\t\tif ( Array.isArray( index ) ) {\n\n\t\t\tthis.index = new ( arrayNeedsUint32( index ) ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 );\n\n\t\t} else {\n\n\t\t\tthis.index = index;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tgetAttribute( name ) {\n\n\t\treturn this.attributes[ name ];\n\n\t}\n\n\tsetAttribute( name, attribute ) {\n\n\t\tthis.attributes[ name ] = attribute;\n\n\t\treturn this;\n\n\t}\n\n\tdeleteAttribute( name ) {\n\n\t\tdelete this.attributes[ name ];\n\n\t\treturn this;\n\n\t}\n\n\thasAttribute( name ) {\n\n\t\treturn this.attributes[ name ] !== undefined;\n\n\t}\n\n\taddGroup( start, count, materialIndex = 0 ) {\n\n\t\tthis.groups.push( {\n\n\t\t\tstart: start,\n\t\t\tcount: count,\n\t\t\tmaterialIndex: materialIndex\n\n\t\t} );\n\n\t}\n\n\tclearGroups() {\n\n\t\tthis.groups = [];\n\n\t}\n\n\tsetDrawRange( start, count ) {\n\n\t\tthis.drawRange.start = start;\n\t\tthis.drawRange.count = count;\n\n\t}\n\n\tapplyMatrix4( matrix ) {\n\n\t\tconst position = this.attributes.position;\n\n\t\tif ( position !== undefined ) {\n\n\t\t\tposition.applyMatrix4( matrix );\n\n\t\t\tposition.needsUpdate = true;\n\n\t\t}\n\n\t\tconst normal = this.attributes.normal;\n\n\t\tif ( normal !== undefined ) {\n\n\t\t\tconst normalMatrix = new Matrix3().getNormalMatrix( matrix );\n\n\t\t\tnormal.applyNormalMatrix( normalMatrix );\n\n\t\t\tnormal.needsUpdate = true;\n\n\t\t}\n\n\t\tconst tangent = this.attributes.tangent;\n\n\t\tif ( tangent !== undefined ) {\n\n\t\t\ttangent.transformDirection( matrix );\n\n\t\t\ttangent.needsUpdate = true;\n\n\t\t}\n\n\t\tif ( this.boundingBox !== null ) {\n\n\t\t\tthis.computeBoundingBox();\n\n\t\t}\n\n\t\tif ( this.boundingSphere !== null ) {\n\n\t\t\tthis.computeBoundingSphere();\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tapplyQuaternion( q ) {\n\n\t\t_m1.makeRotationFromQuaternion( q );\n\n\t\tthis.applyMatrix4( _m1 );\n\n\t\treturn this;\n\n\t}\n\n\trotateX( angle ) {\n\n\t\t// rotate geometry around world x-axis\n\n\t\t_m1.makeRotationX( angle );\n\n\t\tthis.applyMatrix4( _m1 );\n\n\t\treturn this;\n\n\t}\n\n\trotateY( angle ) {\n\n\t\t// rotate geometry around world y-axis\n\n\t\t_m1.makeRotationY( angle );\n\n\t\tthis.applyMatrix4( _m1 );\n\n\t\treturn this;\n\n\t}\n\n\trotateZ( angle ) {\n\n\t\t// rotate geometry around world z-axis\n\n\t\t_m1.makeRotationZ( angle );\n\n\t\tthis.applyMatrix4( _m1 );\n\n\t\treturn this;\n\n\t}\n\n\ttranslate( x, y, z ) {\n\n\t\t// translate geometry\n\n\t\t_m1.makeTranslation( x, y, z );\n\n\t\tthis.applyMatrix4( _m1 );\n\n\t\treturn this;\n\n\t}\n\n\tscale( x, y, z ) {\n\n\t\t// scale geometry\n\n\t\t_m1.makeScale( x, y, z );\n\n\t\tthis.applyMatrix4( _m1 );\n\n\t\treturn this;\n\n\t}\n\n\tlookAt( vector ) {\n\n\t\t_obj.lookAt( vector );\n\n\t\t_obj.updateMatrix();\n\n\t\tthis.applyMatrix4( _obj.matrix );\n\n\t\treturn this;\n\n\t}\n\n\tcenter() {\n\n\t\tthis.computeBoundingBox();\n\n\t\tthis.boundingBox.getCenter( _offset ).negate();\n\n\t\tthis.translate( _offset.x, _offset.y, _offset.z );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromPoints( points ) {\n\n\t\tconst position = [];\n\n\t\tfor ( let i = 0, l = points.length; i < l; i ++ ) {\n\n\t\t\tconst point = points[ i ];\n\t\t\tposition.push( point.x, point.y, point.z || 0 );\n\n\t\t}\n\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) );\n\n\t\treturn this;\n\n\t}\n\n\tcomputeBoundingBox() {\n\n\t\tif ( this.boundingBox === null ) {\n\n\t\t\tthis.boundingBox = new Box3();\n\n\t\t}\n\n\t\tconst position = this.attributes.position;\n\t\tconst morphAttributesPosition = this.morphAttributes.position;\n\n\t\tif ( position && position.isGLBufferAttribute ) {\n\n\t\t\tconsole.error( 'THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set \"mesh.frustumCulled\" to \"false\".', this );\n\n\t\t\tthis.boundingBox.set(\n\t\t\t\tnew Vector3( - Infinity, - Infinity, - Infinity ),\n\t\t\t\tnew Vector3( + Infinity, + Infinity, + Infinity )\n\t\t\t);\n\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( position !== undefined ) {\n\n\t\t\tthis.boundingBox.setFromBufferAttribute( position );\n\n\t\t\t// process morph attributes if present\n\n\t\t\tif ( morphAttributesPosition ) {\n\n\t\t\t\tfor ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst morphAttribute = morphAttributesPosition[ i ];\n\t\t\t\t\t_box$2.setFromBufferAttribute( morphAttribute );\n\n\t\t\t\t\tif ( this.morphTargetsRelative ) {\n\n\t\t\t\t\t\t_vector$8.addVectors( this.boundingBox.min, _box$2.min );\n\t\t\t\t\t\tthis.boundingBox.expandByPoint( _vector$8 );\n\n\t\t\t\t\t\t_vector$8.addVectors( this.boundingBox.max, _box$2.max );\n\t\t\t\t\t\tthis.boundingBox.expandByPoint( _vector$8 );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tthis.boundingBox.expandByPoint( _box$2.min );\n\t\t\t\t\t\tthis.boundingBox.expandByPoint( _box$2.max );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tthis.boundingBox.makeEmpty();\n\n\t\t}\n\n\t\tif ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {\n\n\t\t\tconsole.error( 'THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The \"position\" attribute is likely to have NaN values.', this );\n\n\t\t}\n\n\t}\n\n\tcomputeBoundingSphere() {\n\n\t\tif ( this.boundingSphere === null ) {\n\n\t\t\tthis.boundingSphere = new Sphere();\n\n\t\t}\n\n\t\tconst position = this.attributes.position;\n\t\tconst morphAttributesPosition = this.morphAttributes.position;\n\n\t\tif ( position && position.isGLBufferAttribute ) {\n\n\t\t\tconsole.error( 'THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set \"mesh.frustumCulled\" to \"false\".', this );\n\n\t\t\tthis.boundingSphere.set( new Vector3(), Infinity );\n\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( position ) {\n\n\t\t\t// first, find the center of the bounding sphere\n\n\t\t\tconst center = this.boundingSphere.center;\n\n\t\t\t_box$2.setFromBufferAttribute( position );\n\n\t\t\t// process morph attributes if present\n\n\t\t\tif ( morphAttributesPosition ) {\n\n\t\t\t\tfor ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst morphAttribute = morphAttributesPosition[ i ];\n\t\t\t\t\t_boxMorphTargets.setFromBufferAttribute( morphAttribute );\n\n\t\t\t\t\tif ( this.morphTargetsRelative ) {\n\n\t\t\t\t\t\t_vector$8.addVectors( _box$2.min, _boxMorphTargets.min );\n\t\t\t\t\t\t_box$2.expandByPoint( _vector$8 );\n\n\t\t\t\t\t\t_vector$8.addVectors( _box$2.max, _boxMorphTargets.max );\n\t\t\t\t\t\t_box$2.expandByPoint( _vector$8 );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t_box$2.expandByPoint( _boxMorphTargets.min );\n\t\t\t\t\t\t_box$2.expandByPoint( _boxMorphTargets.max );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t_box$2.getCenter( center );\n\n\t\t\t// second, try to find a boundingSphere with a radius smaller than the\n\t\t\t// boundingSphere of the boundingBox: sqrt(3) smaller in the best case\n\n\t\t\tlet maxRadiusSq = 0;\n\n\t\t\tfor ( let i = 0, il = position.count; i < il; i ++ ) {\n\n\t\t\t\t_vector$8.fromBufferAttribute( position, i );\n\n\t\t\t\tmaxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$8 ) );\n\n\t\t\t}\n\n\t\t\t// process morph attributes if present\n\n\t\t\tif ( morphAttributesPosition ) {\n\n\t\t\t\tfor ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst morphAttribute = morphAttributesPosition[ i ];\n\t\t\t\t\tconst morphTargetsRelative = this.morphTargetsRelative;\n\n\t\t\t\t\tfor ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) {\n\n\t\t\t\t\t\t_vector$8.fromBufferAttribute( morphAttribute, j );\n\n\t\t\t\t\t\tif ( morphTargetsRelative ) {\n\n\t\t\t\t\t\t\t_offset.fromBufferAttribute( position, j );\n\t\t\t\t\t\t\t_vector$8.add( _offset );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmaxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$8 ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis.boundingSphere.radius = Math.sqrt( maxRadiusSq );\n\n\t\t\tif ( isNaN( this.boundingSphere.radius ) ) {\n\n\t\t\t\tconsole.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The \"position\" attribute is likely to have NaN values.', this );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tcomputeTangents() {\n\n\t\tconst index = this.index;\n\t\tconst attributes = this.attributes;\n\n\t\t// based on http://www.terathon.com/code/tangent.html\n\t\t// (per vertex tangents)\n\n\t\tif ( index === null ||\n\t\t\t attributes.position === undefined ||\n\t\t\t attributes.normal === undefined ||\n\t\t\t attributes.uv === undefined ) {\n\n\t\t\tconsole.error( 'THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)' );\n\t\t\treturn;\n\n\t\t}\n\n\t\tconst indices = index.array;\n\t\tconst positions = attributes.position.array;\n\t\tconst normals = attributes.normal.array;\n\t\tconst uvs = attributes.uv.array;\n\n\t\tconst nVertices = positions.length / 3;\n\n\t\tif ( this.hasAttribute( 'tangent' ) === false ) {\n\n\t\t\tthis.setAttribute( 'tangent', new BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) );\n\n\t\t}\n\n\t\tconst tangents = this.getAttribute( 'tangent' ).array;\n\n\t\tconst tan1 = [], tan2 = [];\n\n\t\tfor ( let i = 0; i < nVertices; i ++ ) {\n\n\t\t\ttan1[ i ] = new Vector3();\n\t\t\ttan2[ i ] = new Vector3();\n\n\t\t}\n\n\t\tconst vA = new Vector3(),\n\t\t\tvB = new Vector3(),\n\t\t\tvC = new Vector3(),\n\n\t\t\tuvA = new Vector2(),\n\t\t\tuvB = new Vector2(),\n\t\t\tuvC = new Vector2(),\n\n\t\t\tsdir = new Vector3(),\n\t\t\ttdir = new Vector3();\n\n\t\tfunction handleTriangle( a, b, c ) {\n\n\t\t\tvA.fromArray( positions, a * 3 );\n\t\t\tvB.fromArray( positions, b * 3 );\n\t\t\tvC.fromArray( positions, c * 3 );\n\n\t\t\tuvA.fromArray( uvs, a * 2 );\n\t\t\tuvB.fromArray( uvs, b * 2 );\n\t\t\tuvC.fromArray( uvs, c * 2 );\n\n\t\t\tvB.sub( vA );\n\t\t\tvC.sub( vA );\n\n\t\t\tuvB.sub( uvA );\n\t\t\tuvC.sub( uvA );\n\n\t\t\tconst r = 1.0 / ( uvB.x * uvC.y - uvC.x * uvB.y );\n\n\t\t\t// silently ignore degenerate uv triangles having coincident or colinear vertices\n\n\t\t\tif ( ! isFinite( r ) ) return;\n\n\t\t\tsdir.copy( vB ).multiplyScalar( uvC.y ).addScaledVector( vC, - uvB.y ).multiplyScalar( r );\n\t\t\ttdir.copy( vC ).multiplyScalar( uvB.x ).addScaledVector( vB, - uvC.x ).multiplyScalar( r );\n\n\t\t\ttan1[ a ].add( sdir );\n\t\t\ttan1[ b ].add( sdir );\n\t\t\ttan1[ c ].add( sdir );\n\n\t\t\ttan2[ a ].add( tdir );\n\t\t\ttan2[ b ].add( tdir );\n\t\t\ttan2[ c ].add( tdir );\n\n\t\t}\n\n\t\tlet groups = this.groups;\n\n\t\tif ( groups.length === 0 ) {\n\n\t\t\tgroups = [ {\n\t\t\t\tstart: 0,\n\t\t\t\tcount: indices.length\n\t\t\t} ];\n\n\t\t}\n\n\t\tfor ( let i = 0, il = groups.length; i < il; ++ i ) {\n\n\t\t\tconst group = groups[ i ];\n\n\t\t\tconst start = group.start;\n\t\t\tconst count = group.count;\n\n\t\t\tfor ( let j = start, jl = start + count; j < jl; j += 3 ) {\n\n\t\t\t\thandleTriangle(\n\t\t\t\t\tindices[ j + 0 ],\n\t\t\t\t\tindices[ j + 1 ],\n\t\t\t\t\tindices[ j + 2 ]\n\t\t\t\t);\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst tmp = new Vector3(), tmp2 = new Vector3();\n\t\tconst n = new Vector3(), n2 = new Vector3();\n\n\t\tfunction handleVertex( v ) {\n\n\t\t\tn.fromArray( normals, v * 3 );\n\t\t\tn2.copy( n );\n\n\t\t\tconst t = tan1[ v ];\n\n\t\t\t// Gram-Schmidt orthogonalize\n\n\t\t\ttmp.copy( t );\n\t\t\ttmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize();\n\n\t\t\t// Calculate handedness\n\n\t\t\ttmp2.crossVectors( n2, t );\n\t\t\tconst test = tmp2.dot( tan2[ v ] );\n\t\t\tconst w = ( test < 0.0 ) ? - 1.0 : 1.0;\n\n\t\t\ttangents[ v * 4 ] = tmp.x;\n\t\t\ttangents[ v * 4 + 1 ] = tmp.y;\n\t\t\ttangents[ v * 4 + 2 ] = tmp.z;\n\t\t\ttangents[ v * 4 + 3 ] = w;\n\n\t\t}\n\n\t\tfor ( let i = 0, il = groups.length; i < il; ++ i ) {\n\n\t\t\tconst group = groups[ i ];\n\n\t\t\tconst start = group.start;\n\t\t\tconst count = group.count;\n\n\t\t\tfor ( let j = start, jl = start + count; j < jl; j += 3 ) {\n\n\t\t\t\thandleVertex( indices[ j + 0 ] );\n\t\t\t\thandleVertex( indices[ j + 1 ] );\n\t\t\t\thandleVertex( indices[ j + 2 ] );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tcomputeVertexNormals() {\n\n\t\tconst index = this.index;\n\t\tconst positionAttribute = this.getAttribute( 'position' );\n\n\t\tif ( positionAttribute !== undefined ) {\n\n\t\t\tlet normalAttribute = this.getAttribute( 'normal' );\n\n\t\t\tif ( normalAttribute === undefined ) {\n\n\t\t\t\tnormalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 );\n\t\t\t\tthis.setAttribute( 'normal', normalAttribute );\n\n\t\t\t} else {\n\n\t\t\t\t// reset existing normals to zero\n\n\t\t\t\tfor ( let i = 0, il = normalAttribute.count; i < il; i ++ ) {\n\n\t\t\t\t\tnormalAttribute.setXYZ( i, 0, 0, 0 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tconst pA = new Vector3(), pB = new Vector3(), pC = new Vector3();\n\t\t\tconst nA = new Vector3(), nB = new Vector3(), nC = new Vector3();\n\t\t\tconst cb = new Vector3(), ab = new Vector3();\n\n\t\t\t// indexed elements\n\n\t\t\tif ( index ) {\n\n\t\t\t\tfor ( let i = 0, il = index.count; i < il; i += 3 ) {\n\n\t\t\t\t\tconst vA = index.getX( i + 0 );\n\t\t\t\t\tconst vB = index.getX( i + 1 );\n\t\t\t\t\tconst vC = index.getX( i + 2 );\n\n\t\t\t\t\tpA.fromBufferAttribute( positionAttribute, vA );\n\t\t\t\t\tpB.fromBufferAttribute( positionAttribute, vB );\n\t\t\t\t\tpC.fromBufferAttribute( positionAttribute, vC );\n\n\t\t\t\t\tcb.subVectors( pC, pB );\n\t\t\t\t\tab.subVectors( pA, pB );\n\t\t\t\t\tcb.cross( ab );\n\n\t\t\t\t\tnA.fromBufferAttribute( normalAttribute, vA );\n\t\t\t\t\tnB.fromBufferAttribute( normalAttribute, vB );\n\t\t\t\t\tnC.fromBufferAttribute( normalAttribute, vC );\n\n\t\t\t\t\tnA.add( cb );\n\t\t\t\t\tnB.add( cb );\n\t\t\t\t\tnC.add( cb );\n\n\t\t\t\t\tnormalAttribute.setXYZ( vA, nA.x, nA.y, nA.z );\n\t\t\t\t\tnormalAttribute.setXYZ( vB, nB.x, nB.y, nB.z );\n\t\t\t\t\tnormalAttribute.setXYZ( vC, nC.x, nC.y, nC.z );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// non-indexed elements (unconnected triangle soup)\n\n\t\t\t\tfor ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) {\n\n\t\t\t\t\tpA.fromBufferAttribute( positionAttribute, i + 0 );\n\t\t\t\t\tpB.fromBufferAttribute( positionAttribute, i + 1 );\n\t\t\t\t\tpC.fromBufferAttribute( positionAttribute, i + 2 );\n\n\t\t\t\t\tcb.subVectors( pC, pB );\n\t\t\t\t\tab.subVectors( pA, pB );\n\t\t\t\t\tcb.cross( ab );\n\n\t\t\t\t\tnormalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z );\n\t\t\t\t\tnormalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z );\n\t\t\t\t\tnormalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis.normalizeNormals();\n\n\t\t\tnormalAttribute.needsUpdate = true;\n\n\t\t}\n\n\t}\n\n\tnormalizeNormals() {\n\n\t\tconst normals = this.attributes.normal;\n\n\t\tfor ( let i = 0, il = normals.count; i < il; i ++ ) {\n\n\t\t\t_vector$8.fromBufferAttribute( normals, i );\n\n\t\t\t_vector$8.normalize();\n\n\t\t\tnormals.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z );\n\n\t\t}\n\n\t}\n\n\ttoNonIndexed() {\n\n\t\tfunction convertBufferAttribute( attribute, indices ) {\n\n\t\t\tconst array = attribute.array;\n\t\t\tconst itemSize = attribute.itemSize;\n\t\t\tconst normalized = attribute.normalized;\n\n\t\t\tconst array2 = new array.constructor( indices.length * itemSize );\n\n\t\t\tlet index = 0, index2 = 0;\n\n\t\t\tfor ( let i = 0, l = indices.length; i < l; i ++ ) {\n\n\t\t\t\tif ( attribute.isInterleavedBufferAttribute ) {\n\n\t\t\t\t\tindex = indices[ i ] * attribute.data.stride + attribute.offset;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tindex = indices[ i ] * itemSize;\n\n\t\t\t\t}\n\n\t\t\t\tfor ( let j = 0; j < itemSize; j ++ ) {\n\n\t\t\t\t\tarray2[ index2 ++ ] = array[ index ++ ];\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn new BufferAttribute( array2, itemSize, normalized );\n\n\t\t}\n\n\t\t//\n\n\t\tif ( this.index === null ) {\n\n\t\t\tconsole.warn( 'THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed.' );\n\t\t\treturn this;\n\n\t\t}\n\n\t\tconst geometry2 = new BufferGeometry();\n\n\t\tconst indices = this.index.array;\n\t\tconst attributes = this.attributes;\n\n\t\t// attributes\n\n\t\tfor ( const name in attributes ) {\n\n\t\t\tconst attribute = attributes[ name ];\n\n\t\t\tconst newAttribute = convertBufferAttribute( attribute, indices );\n\n\t\t\tgeometry2.setAttribute( name, newAttribute );\n\n\t\t}\n\n\t\t// morph attributes\n\n\t\tconst morphAttributes = this.morphAttributes;\n\n\t\tfor ( const name in morphAttributes ) {\n\n\t\t\tconst morphArray = [];\n\t\t\tconst morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes\n\n\t\t\tfor ( let i = 0, il = morphAttribute.length; i < il; i ++ ) {\n\n\t\t\t\tconst attribute = morphAttribute[ i ];\n\n\t\t\t\tconst newAttribute = convertBufferAttribute( attribute, indices );\n\n\t\t\t\tmorphArray.push( newAttribute );\n\n\t\t\t}\n\n\t\t\tgeometry2.morphAttributes[ name ] = morphArray;\n\n\t\t}\n\n\t\tgeometry2.morphTargetsRelative = this.morphTargetsRelative;\n\n\t\t// groups\n\n\t\tconst groups = this.groups;\n\n\t\tfor ( let i = 0, l = groups.length; i < l; i ++ ) {\n\n\t\t\tconst group = groups[ i ];\n\t\t\tgeometry2.addGroup( group.start, group.count, group.materialIndex );\n\n\t\t}\n\n\t\treturn geometry2;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = {\n\t\t\tmetadata: {\n\t\t\t\tversion: 4.6,\n\t\t\t\ttype: 'BufferGeometry',\n\t\t\t\tgenerator: 'BufferGeometry.toJSON'\n\t\t\t}\n\t\t};\n\n\t\t// standard BufferGeometry serialization\n\n\t\tdata.uuid = this.uuid;\n\t\tdata.type = this.type;\n\t\tif ( this.name !== '' ) data.name = this.name;\n\t\tif ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData;\n\n\t\tif ( this.parameters !== undefined ) {\n\n\t\t\tconst parameters = this.parameters;\n\n\t\t\tfor ( const key in parameters ) {\n\n\t\t\t\tif ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];\n\n\t\t\t}\n\n\t\t\treturn data;\n\n\t\t}\n\n\t\t// for simplicity the code assumes attributes are not shared across geometries, see #15811\n\n\t\tdata.data = { attributes: {} };\n\n\t\tconst index = this.index;\n\n\t\tif ( index !== null ) {\n\n\t\t\tdata.data.index = {\n\t\t\t\ttype: index.array.constructor.name,\n\t\t\t\tarray: Array.prototype.slice.call( index.array )\n\t\t\t};\n\n\t\t}\n\n\t\tconst attributes = this.attributes;\n\n\t\tfor ( const key in attributes ) {\n\n\t\t\tconst attribute = attributes[ key ];\n\n\t\t\tdata.data.attributes[ key ] = attribute.toJSON( data.data );\n\n\t\t}\n\n\t\tconst morphAttributes = {};\n\t\tlet hasMorphAttributes = false;\n\n\t\tfor ( const key in this.morphAttributes ) {\n\n\t\t\tconst attributeArray = this.morphAttributes[ key ];\n\n\t\t\tconst array = [];\n\n\t\t\tfor ( let i = 0, il = attributeArray.length; i < il; i ++ ) {\n\n\t\t\t\tconst attribute = attributeArray[ i ];\n\n\t\t\t\tarray.push( attribute.toJSON( data.data ) );\n\n\t\t\t}\n\n\t\t\tif ( array.length > 0 ) {\n\n\t\t\t\tmorphAttributes[ key ] = array;\n\n\t\t\t\thasMorphAttributes = true;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( hasMorphAttributes ) {\n\n\t\t\tdata.data.morphAttributes = morphAttributes;\n\t\t\tdata.data.morphTargetsRelative = this.morphTargetsRelative;\n\n\t\t}\n\n\t\tconst groups = this.groups;\n\n\t\tif ( groups.length > 0 ) {\n\n\t\t\tdata.data.groups = JSON.parse( JSON.stringify( groups ) );\n\n\t\t}\n\n\t\tconst boundingSphere = this.boundingSphere;\n\n\t\tif ( boundingSphere !== null ) {\n\n\t\t\tdata.data.boundingSphere = {\n\t\t\t\tcenter: boundingSphere.center.toArray(),\n\t\t\t\tradius: boundingSphere.radius\n\t\t\t};\n\n\t\t}\n\n\t\treturn data;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( source ) {\n\n\t\t// reset\n\n\t\tthis.index = null;\n\t\tthis.attributes = {};\n\t\tthis.morphAttributes = {};\n\t\tthis.groups = [];\n\t\tthis.boundingBox = null;\n\t\tthis.boundingSphere = null;\n\n\t\t// used for storing cloned, shared data\n\n\t\tconst data = {};\n\n\t\t// name\n\n\t\tthis.name = source.name;\n\n\t\t// index\n\n\t\tconst index = source.index;\n\n\t\tif ( index !== null ) {\n\n\t\t\tthis.setIndex( index.clone( data ) );\n\n\t\t}\n\n\t\t// attributes\n\n\t\tconst attributes = source.attributes;\n\n\t\tfor ( const name in attributes ) {\n\n\t\t\tconst attribute = attributes[ name ];\n\t\t\tthis.setAttribute( name, attribute.clone( data ) );\n\n\t\t}\n\n\t\t// morph attributes\n\n\t\tconst morphAttributes = source.morphAttributes;\n\n\t\tfor ( const name in morphAttributes ) {\n\n\t\t\tconst array = [];\n\t\t\tconst morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes\n\n\t\t\tfor ( let i = 0, l = morphAttribute.length; i < l; i ++ ) {\n\n\t\t\t\tarray.push( morphAttribute[ i ].clone( data ) );\n\n\t\t\t}\n\n\t\t\tthis.morphAttributes[ name ] = array;\n\n\t\t}\n\n\t\tthis.morphTargetsRelative = source.morphTargetsRelative;\n\n\t\t// groups\n\n\t\tconst groups = source.groups;\n\n\t\tfor ( let i = 0, l = groups.length; i < l; i ++ ) {\n\n\t\t\tconst group = groups[ i ];\n\t\t\tthis.addGroup( group.start, group.count, group.materialIndex );\n\n\t\t}\n\n\t\t// bounding box\n\n\t\tconst boundingBox = source.boundingBox;\n\n\t\tif ( boundingBox !== null ) {\n\n\t\t\tthis.boundingBox = boundingBox.clone();\n\n\t\t}\n\n\t\t// bounding sphere\n\n\t\tconst boundingSphere = source.boundingSphere;\n\n\t\tif ( boundingSphere !== null ) {\n\n\t\t\tthis.boundingSphere = boundingSphere.clone();\n\n\t\t}\n\n\t\t// draw range\n\n\t\tthis.drawRange.start = source.drawRange.start;\n\t\tthis.drawRange.count = source.drawRange.count;\n\n\t\t// user data\n\n\t\tthis.userData = source.userData;\n\n\t\treturn this;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t}\n\n}\n\nconst _inverseMatrix$3 = /*@__PURE__*/ new Matrix4();\nconst _ray$3 = /*@__PURE__*/ new Ray();\nconst _sphere$6 = /*@__PURE__*/ new Sphere();\nconst _sphereHitAt = /*@__PURE__*/ new Vector3();\n\nconst _vA$1 = /*@__PURE__*/ new Vector3();\nconst _vB$1 = /*@__PURE__*/ new Vector3();\nconst _vC$1 = /*@__PURE__*/ new Vector3();\n\nconst _tempA = /*@__PURE__*/ new Vector3();\nconst _morphA = /*@__PURE__*/ new Vector3();\n\nconst _uvA$1 = /*@__PURE__*/ new Vector2();\nconst _uvB$1 = /*@__PURE__*/ new Vector2();\nconst _uvC$1 = /*@__PURE__*/ new Vector2();\n\nconst _normalA = /*@__PURE__*/ new Vector3();\nconst _normalB = /*@__PURE__*/ new Vector3();\nconst _normalC = /*@__PURE__*/ new Vector3();\n\nconst _intersectionPoint = /*@__PURE__*/ new Vector3();\nconst _intersectionPointWorld = /*@__PURE__*/ new Vector3();\n\nclass Mesh extends Object3D {\n\n\tconstructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) {\n\n\t\tsuper();\n\n\t\tthis.isMesh = true;\n\n\t\tthis.type = 'Mesh';\n\n\t\tthis.geometry = geometry;\n\t\tthis.material = material;\n\n\t\tthis.updateMorphTargets();\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tif ( source.morphTargetInfluences !== undefined ) {\n\n\t\t\tthis.morphTargetInfluences = source.morphTargetInfluences.slice();\n\n\t\t}\n\n\t\tif ( source.morphTargetDictionary !== undefined ) {\n\n\t\t\tthis.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary );\n\n\t\t}\n\n\t\tthis.material = Array.isArray( source.material ) ? source.material.slice() : source.material;\n\t\tthis.geometry = source.geometry;\n\n\t\treturn this;\n\n\t}\n\n\tupdateMorphTargets() {\n\n\t\tconst geometry = this.geometry;\n\n\t\tconst morphAttributes = geometry.morphAttributes;\n\t\tconst keys = Object.keys( morphAttributes );\n\n\t\tif ( keys.length > 0 ) {\n\n\t\t\tconst morphAttribute = morphAttributes[ keys[ 0 ] ];\n\n\t\t\tif ( morphAttribute !== undefined ) {\n\n\t\t\t\tthis.morphTargetInfluences = [];\n\t\t\t\tthis.morphTargetDictionary = {};\n\n\t\t\t\tfor ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {\n\n\t\t\t\t\tconst name = morphAttribute[ m ].name || String( m );\n\n\t\t\t\t\tthis.morphTargetInfluences.push( 0 );\n\t\t\t\t\tthis.morphTargetDictionary[ name ] = m;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tgetVertexPosition( index, target ) {\n\n\t\tconst geometry = this.geometry;\n\t\tconst position = geometry.attributes.position;\n\t\tconst morphPosition = geometry.morphAttributes.position;\n\t\tconst morphTargetsRelative = geometry.morphTargetsRelative;\n\n\t\ttarget.fromBufferAttribute( position, index );\n\n\t\tconst morphInfluences = this.morphTargetInfluences;\n\n\t\tif ( morphPosition && morphInfluences ) {\n\n\t\t\t_morphA.set( 0, 0, 0 );\n\n\t\t\tfor ( let i = 0, il = morphPosition.length; i < il; i ++ ) {\n\n\t\t\t\tconst influence = morphInfluences[ i ];\n\t\t\t\tconst morphAttribute = morphPosition[ i ];\n\n\t\t\t\tif ( influence === 0 ) continue;\n\n\t\t\t\t_tempA.fromBufferAttribute( morphAttribute, index );\n\n\t\t\t\tif ( morphTargetsRelative ) {\n\n\t\t\t\t\t_morphA.addScaledVector( _tempA, influence );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_morphA.addScaledVector( _tempA.sub( target ), influence );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\ttarget.add( _morphA );\n\n\t\t}\n\n\t\treturn target;\n\n\t}\n\n\traycast( raycaster, intersects ) {\n\n\t\tconst geometry = this.geometry;\n\t\tconst material = this.material;\n\t\tconst matrixWorld = this.matrixWorld;\n\n\t\tif ( material === undefined ) return;\n\n\t\t// test with bounding sphere in world space\n\n\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n\t\t_sphere$6.copy( geometry.boundingSphere );\n\t\t_sphere$6.applyMatrix4( matrixWorld );\n\n\t\t// check distance from ray origin to bounding sphere\n\n\t\t_ray$3.copy( raycaster.ray ).recast( raycaster.near );\n\n\t\tif ( _sphere$6.containsPoint( _ray$3.origin ) === false ) {\n\n\t\t\tif ( _ray$3.intersectSphere( _sphere$6, _sphereHitAt ) === null ) return;\n\n\t\t\tif ( _ray$3.origin.distanceToSquared( _sphereHitAt ) > ( raycaster.far - raycaster.near ) ** 2 ) return;\n\n\t\t}\n\n\t\t// convert ray to local space of mesh\n\n\t\t_inverseMatrix$3.copy( matrixWorld ).invert();\n\t\t_ray$3.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$3 );\n\n\t\t// test with bounding box in local space\n\n\t\tif ( geometry.boundingBox !== null ) {\n\n\t\t\tif ( _ray$3.intersectsBox( geometry.boundingBox ) === false ) return;\n\n\t\t}\n\n\t\t// test for intersections with geometry\n\n\t\tthis._computeIntersections( raycaster, intersects, _ray$3 );\n\n\t}\n\n\t_computeIntersections( raycaster, intersects, rayLocalSpace ) {\n\n\t\tlet intersection;\n\n\t\tconst geometry = this.geometry;\n\t\tconst material = this.material;\n\n\t\tconst index = geometry.index;\n\t\tconst position = geometry.attributes.position;\n\t\tconst uv = geometry.attributes.uv;\n\t\tconst uv1 = geometry.attributes.uv1;\n\t\tconst normal = geometry.attributes.normal;\n\t\tconst groups = geometry.groups;\n\t\tconst drawRange = geometry.drawRange;\n\n\t\tif ( index !== null ) {\n\n\t\t\t// indexed buffer geometry\n\n\t\t\tif ( Array.isArray( material ) ) {\n\n\t\t\t\tfor ( let i = 0, il = groups.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst group = groups[ i ];\n\t\t\t\t\tconst groupMaterial = material[ group.materialIndex ];\n\n\t\t\t\t\tconst start = Math.max( group.start, drawRange.start );\n\t\t\t\t\tconst end = Math.min( index.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) );\n\n\t\t\t\t\tfor ( let j = start, jl = end; j < jl; j += 3 ) {\n\n\t\t\t\t\t\tconst a = index.getX( j );\n\t\t\t\t\t\tconst b = index.getX( j + 1 );\n\t\t\t\t\t\tconst c = index.getX( j + 2 );\n\n\t\t\t\t\t\tintersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c );\n\n\t\t\t\t\t\tif ( intersection ) {\n\n\t\t\t\t\t\t\tintersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics\n\t\t\t\t\t\t\tintersection.face.materialIndex = group.materialIndex;\n\t\t\t\t\t\t\tintersects.push( intersection );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tconst start = Math.max( 0, drawRange.start );\n\t\t\t\tconst end = Math.min( index.count, ( drawRange.start + drawRange.count ) );\n\n\t\t\t\tfor ( let i = start, il = end; i < il; i += 3 ) {\n\n\t\t\t\t\tconst a = index.getX( i );\n\t\t\t\t\tconst b = index.getX( i + 1 );\n\t\t\t\t\tconst c = index.getX( i + 2 );\n\n\t\t\t\t\tintersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c );\n\n\t\t\t\t\tif ( intersection ) {\n\n\t\t\t\t\t\tintersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics\n\t\t\t\t\t\tintersects.push( intersection );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else if ( position !== undefined ) {\n\n\t\t\t// non-indexed buffer geometry\n\n\t\t\tif ( Array.isArray( material ) ) {\n\n\t\t\t\tfor ( let i = 0, il = groups.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst group = groups[ i ];\n\t\t\t\t\tconst groupMaterial = material[ group.materialIndex ];\n\n\t\t\t\t\tconst start = Math.max( group.start, drawRange.start );\n\t\t\t\t\tconst end = Math.min( position.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) );\n\n\t\t\t\t\tfor ( let j = start, jl = end; j < jl; j += 3 ) {\n\n\t\t\t\t\t\tconst a = j;\n\t\t\t\t\t\tconst b = j + 1;\n\t\t\t\t\t\tconst c = j + 2;\n\n\t\t\t\t\t\tintersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c );\n\n\t\t\t\t\t\tif ( intersection ) {\n\n\t\t\t\t\t\t\tintersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics\n\t\t\t\t\t\t\tintersection.face.materialIndex = group.materialIndex;\n\t\t\t\t\t\t\tintersects.push( intersection );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tconst start = Math.max( 0, drawRange.start );\n\t\t\t\tconst end = Math.min( position.count, ( drawRange.start + drawRange.count ) );\n\n\t\t\t\tfor ( let i = start, il = end; i < il; i += 3 ) {\n\n\t\t\t\t\tconst a = i;\n\t\t\t\t\tconst b = i + 1;\n\t\t\t\t\tconst c = i + 2;\n\n\t\t\t\t\tintersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c );\n\n\t\t\t\t\tif ( intersection ) {\n\n\t\t\t\t\t\tintersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics\n\t\t\t\t\t\tintersects.push( intersection );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n\nfunction checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) {\n\n\tlet intersect;\n\n\tif ( material.side === BackSide ) {\n\n\t\tintersect = ray.intersectTriangle( pC, pB, pA, true, point );\n\n\t} else {\n\n\t\tintersect = ray.intersectTriangle( pA, pB, pC, ( material.side === FrontSide ), point );\n\n\t}\n\n\tif ( intersect === null ) return null;\n\n\t_intersectionPointWorld.copy( point );\n\t_intersectionPointWorld.applyMatrix4( object.matrixWorld );\n\n\tconst distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld );\n\n\tif ( distance < raycaster.near || distance > raycaster.far ) return null;\n\n\treturn {\n\t\tdistance: distance,\n\t\tpoint: _intersectionPointWorld.clone(),\n\t\tobject: object\n\t};\n\n}\n\nfunction checkGeometryIntersection( object, material, raycaster, ray, uv, uv1, normal, a, b, c ) {\n\n\tobject.getVertexPosition( a, _vA$1 );\n\tobject.getVertexPosition( b, _vB$1 );\n\tobject.getVertexPosition( c, _vC$1 );\n\n\tconst intersection = checkIntersection( object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint );\n\n\tif ( intersection ) {\n\n\t\tif ( uv ) {\n\n\t\t\t_uvA$1.fromBufferAttribute( uv, a );\n\t\t\t_uvB$1.fromBufferAttribute( uv, b );\n\t\t\t_uvC$1.fromBufferAttribute( uv, c );\n\n\t\t\tintersection.uv = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() );\n\n\t\t}\n\n\t\tif ( uv1 ) {\n\n\t\t\t_uvA$1.fromBufferAttribute( uv1, a );\n\t\t\t_uvB$1.fromBufferAttribute( uv1, b );\n\t\t\t_uvC$1.fromBufferAttribute( uv1, c );\n\n\t\t\tintersection.uv1 = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() );\n\t\t\tintersection.uv2 = intersection.uv1; // @deprecated, r152\n\n\t\t}\n\n\t\tif ( normal ) {\n\n\t\t\t_normalA.fromBufferAttribute( normal, a );\n\t\t\t_normalB.fromBufferAttribute( normal, b );\n\t\t\t_normalC.fromBufferAttribute( normal, c );\n\n\t\t\tintersection.normal = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _normalA, _normalB, _normalC, new Vector3() );\n\n\t\t\tif ( intersection.normal.dot( ray.direction ) > 0 ) {\n\n\t\t\t\tintersection.normal.multiplyScalar( - 1 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst face = {\n\t\t\ta: a,\n\t\t\tb: b,\n\t\t\tc: c,\n\t\t\tnormal: new Vector3(),\n\t\t\tmaterialIndex: 0\n\t\t};\n\n\t\tTriangle.getNormal( _vA$1, _vB$1, _vC$1, face.normal );\n\n\t\tintersection.face = face;\n\n\t}\n\n\treturn intersection;\n\n}\n\nclass BoxGeometry extends BufferGeometry {\n\n\tconstructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'BoxGeometry';\n\n\t\tthis.parameters = {\n\t\t\twidth: width,\n\t\t\theight: height,\n\t\t\tdepth: depth,\n\t\t\twidthSegments: widthSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\tdepthSegments: depthSegments\n\t\t};\n\n\t\tconst scope = this;\n\n\t\t// segments\n\n\t\twidthSegments = Math.floor( widthSegments );\n\t\theightSegments = Math.floor( heightSegments );\n\t\tdepthSegments = Math.floor( depthSegments );\n\n\t\t// buffers\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\t// helper variables\n\n\t\tlet numberOfVertices = 0;\n\t\tlet groupStart = 0;\n\n\t\t// build each side of the box geometry\n\n\t\tbuildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px\n\t\tbuildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx\n\t\tbuildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py\n\t\tbuildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny\n\t\tbuildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz\n\t\tbuildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t\tfunction buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) {\n\n\t\t\tconst segmentWidth = width / gridX;\n\t\t\tconst segmentHeight = height / gridY;\n\n\t\t\tconst widthHalf = width / 2;\n\t\t\tconst heightHalf = height / 2;\n\t\t\tconst depthHalf = depth / 2;\n\n\t\t\tconst gridX1 = gridX + 1;\n\t\t\tconst gridY1 = gridY + 1;\n\n\t\t\tlet vertexCounter = 0;\n\t\t\tlet groupCount = 0;\n\n\t\t\tconst vector = new Vector3();\n\n\t\t\t// generate vertices, normals and uvs\n\n\t\t\tfor ( let iy = 0; iy < gridY1; iy ++ ) {\n\n\t\t\t\tconst y = iy * segmentHeight - heightHalf;\n\n\t\t\t\tfor ( let ix = 0; ix < gridX1; ix ++ ) {\n\n\t\t\t\t\tconst x = ix * segmentWidth - widthHalf;\n\n\t\t\t\t\t// set values to correct vector component\n\n\t\t\t\t\tvector[ u ] = x * udir;\n\t\t\t\t\tvector[ v ] = y * vdir;\n\t\t\t\t\tvector[ w ] = depthHalf;\n\n\t\t\t\t\t// now apply vector to vertex buffer\n\n\t\t\t\t\tvertices.push( vector.x, vector.y, vector.z );\n\n\t\t\t\t\t// set values to correct vector component\n\n\t\t\t\t\tvector[ u ] = 0;\n\t\t\t\t\tvector[ v ] = 0;\n\t\t\t\t\tvector[ w ] = depth > 0 ? 1 : - 1;\n\n\t\t\t\t\t// now apply vector to normal buffer\n\n\t\t\t\t\tnormals.push( vector.x, vector.y, vector.z );\n\n\t\t\t\t\t// uvs\n\n\t\t\t\t\tuvs.push( ix / gridX );\n\t\t\t\t\tuvs.push( 1 - ( iy / gridY ) );\n\n\t\t\t\t\t// counters\n\n\t\t\t\t\tvertexCounter += 1;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// indices\n\n\t\t\t// 1. you need three indices to draw a single face\n\t\t\t// 2. a single segment consists of two faces\n\t\t\t// 3. so we need to generate six (2*3) indices per segment\n\n\t\t\tfor ( let iy = 0; iy < gridY; iy ++ ) {\n\n\t\t\t\tfor ( let ix = 0; ix < gridX; ix ++ ) {\n\n\t\t\t\t\tconst a = numberOfVertices + ix + gridX1 * iy;\n\t\t\t\t\tconst b = numberOfVertices + ix + gridX1 * ( iy + 1 );\n\t\t\t\t\tconst c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 );\n\t\t\t\t\tconst d = numberOfVertices + ( ix + 1 ) + gridX1 * iy;\n\n\t\t\t\t\t// faces\n\n\t\t\t\t\tindices.push( a, b, d );\n\t\t\t\t\tindices.push( b, c, d );\n\n\t\t\t\t\t// increase counter\n\n\t\t\t\t\tgroupCount += 6;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// add a group to the geometry. this will ensure multi material support\n\n\t\t\tscope.addGroup( groupStart, groupCount, materialIndex );\n\n\t\t\t// calculate new start value for groups\n\n\t\t\tgroupStart += groupCount;\n\n\t\t\t// update total number of vertices\n\n\t\t\tnumberOfVertices += vertexCounter;\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments );\n\n\t}\n\n}\n\n/**\n * Uniform Utilities\n */\n\nfunction cloneUniforms( src ) {\n\n\tconst dst = {};\n\n\tfor ( const u in src ) {\n\n\t\tdst[ u ] = {};\n\n\t\tfor ( const p in src[ u ] ) {\n\n\t\t\tconst property = src[ u ][ p ];\n\n\t\t\tif ( property && ( property.isColor ||\n\t\t\t\tproperty.isMatrix3 || property.isMatrix4 ||\n\t\t\t\tproperty.isVector2 || property.isVector3 || property.isVector4 ||\n\t\t\t\tproperty.isTexture || property.isQuaternion ) ) {\n\n\t\t\t\tif ( property.isRenderTargetTexture ) {\n\n\t\t\t\t\tconsole.warn( 'UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms().' );\n\t\t\t\t\tdst[ u ][ p ] = null;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tdst[ u ][ p ] = property.clone();\n\n\t\t\t\t}\n\n\t\t\t} else if ( Array.isArray( property ) ) {\n\n\t\t\t\tdst[ u ][ p ] = property.slice();\n\n\t\t\t} else {\n\n\t\t\t\tdst[ u ][ p ] = property;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\treturn dst;\n\n}\n\nfunction mergeUniforms( uniforms ) {\n\n\tconst merged = {};\n\n\tfor ( let u = 0; u < uniforms.length; u ++ ) {\n\n\t\tconst tmp = cloneUniforms( uniforms[ u ] );\n\n\t\tfor ( const p in tmp ) {\n\n\t\t\tmerged[ p ] = tmp[ p ];\n\n\t\t}\n\n\t}\n\n\treturn merged;\n\n}\n\nfunction cloneUniformsGroups( src ) {\n\n\tconst dst = [];\n\n\tfor ( let u = 0; u < src.length; u ++ ) {\n\n\t\tdst.push( src[ u ].clone() );\n\n\t}\n\n\treturn dst;\n\n}\n\nfunction getUnlitUniformColorSpace( renderer ) {\n\n\tif ( renderer.getRenderTarget() === null ) {\n\n\t\t// https://github.com/mrdoob/three.js/pull/23937#issuecomment-1111067398\n\t\treturn renderer.outputColorSpace;\n\n\t}\n\n\treturn ColorManagement.workingColorSpace;\n\n}\n\n// Legacy\n\nconst UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms };\n\nvar default_vertex = \"void main() {\\n\\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\\n}\";\n\nvar default_fragment = \"void main() {\\n\\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\\n}\";\n\nclass ShaderMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isShaderMaterial = true;\n\n\t\tthis.type = 'ShaderMaterial';\n\n\t\tthis.defines = {};\n\t\tthis.uniforms = {};\n\t\tthis.uniformsGroups = [];\n\n\t\tthis.vertexShader = default_vertex;\n\t\tthis.fragmentShader = default_fragment;\n\n\t\tthis.linewidth = 1;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\n\t\tthis.fog = false; // set to use scene fog\n\t\tthis.lights = false; // set to use scene lights\n\t\tthis.clipping = false; // set to use user-defined clipping planes\n\n\t\tthis.forceSinglePass = true;\n\n\t\tthis.extensions = {\n\t\t\tderivatives: false, // set to use derivatives\n\t\t\tfragDepth: false, // set to use fragment depth values\n\t\t\tdrawBuffers: false, // set to use draw buffers\n\t\t\tshaderTextureLOD: false, // set to use shader texture LOD\n\t\t\tclipCullDistance: false // set to use vertex shader clipping\n\t\t};\n\n\t\t// When rendered geometry doesn't include these attributes but the material does,\n\t\t// use these default values in WebGL. This avoids errors when buffer data is missing.\n\t\tthis.defaultAttributeValues = {\n\t\t\t'color': [ 1, 1, 1 ],\n\t\t\t'uv': [ 0, 0 ],\n\t\t\t'uv1': [ 0, 0 ]\n\t\t};\n\n\t\tthis.index0AttributeName = undefined;\n\t\tthis.uniformsNeedUpdate = false;\n\n\t\tthis.glslVersion = null;\n\n\t\tif ( parameters !== undefined ) {\n\n\t\t\tthis.setValues( parameters );\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.fragmentShader = source.fragmentShader;\n\t\tthis.vertexShader = source.vertexShader;\n\n\t\tthis.uniforms = cloneUniforms( source.uniforms );\n\t\tthis.uniformsGroups = cloneUniformsGroups( source.uniformsGroups );\n\n\t\tthis.defines = Object.assign( {}, source.defines );\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\n\t\tthis.fog = source.fog;\n\t\tthis.lights = source.lights;\n\t\tthis.clipping = source.clipping;\n\n\t\tthis.extensions = Object.assign( {}, source.extensions );\n\n\t\tthis.glslVersion = source.glslVersion;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tdata.glslVersion = this.glslVersion;\n\t\tdata.uniforms = {};\n\n\t\tfor ( const name in this.uniforms ) {\n\n\t\t\tconst uniform = this.uniforms[ name ];\n\t\t\tconst value = uniform.value;\n\n\t\t\tif ( value && value.isTexture ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 't',\n\t\t\t\t\tvalue: value.toJSON( meta ).uuid\n\t\t\t\t};\n\n\t\t\t} else if ( value && value.isColor ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 'c',\n\t\t\t\t\tvalue: value.getHex()\n\t\t\t\t};\n\n\t\t\t} else if ( value && value.isVector2 ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 'v2',\n\t\t\t\t\tvalue: value.toArray()\n\t\t\t\t};\n\n\t\t\t} else if ( value && value.isVector3 ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 'v3',\n\t\t\t\t\tvalue: value.toArray()\n\t\t\t\t};\n\n\t\t\t} else if ( value && value.isVector4 ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 'v4',\n\t\t\t\t\tvalue: value.toArray()\n\t\t\t\t};\n\n\t\t\t} else if ( value && value.isMatrix3 ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 'm3',\n\t\t\t\t\tvalue: value.toArray()\n\t\t\t\t};\n\n\t\t\t} else if ( value && value.isMatrix4 ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 'm4',\n\t\t\t\t\tvalue: value.toArray()\n\t\t\t\t};\n\n\t\t\t} else {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\tvalue: value\n\t\t\t\t};\n\n\t\t\t\t// note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines;\n\n\t\tdata.vertexShader = this.vertexShader;\n\t\tdata.fragmentShader = this.fragmentShader;\n\n\t\tdata.lights = this.lights;\n\t\tdata.clipping = this.clipping;\n\n\t\tconst extensions = {};\n\n\t\tfor ( const key in this.extensions ) {\n\n\t\t\tif ( this.extensions[ key ] === true ) extensions[ key ] = true;\n\n\t\t}\n\n\t\tif ( Object.keys( extensions ).length > 0 ) data.extensions = extensions;\n\n\t\treturn data;\n\n\t}\n\n}\n\nclass Camera extends Object3D {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isCamera = true;\n\n\t\tthis.type = 'Camera';\n\n\t\tthis.matrixWorldInverse = new Matrix4();\n\n\t\tthis.projectionMatrix = new Matrix4();\n\t\tthis.projectionMatrixInverse = new Matrix4();\n\n\t\tthis.coordinateSystem = WebGLCoordinateSystem;\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.matrixWorldInverse.copy( source.matrixWorldInverse );\n\n\t\tthis.projectionMatrix.copy( source.projectionMatrix );\n\t\tthis.projectionMatrixInverse.copy( source.projectionMatrixInverse );\n\n\t\tthis.coordinateSystem = source.coordinateSystem;\n\n\t\treturn this;\n\n\t}\n\n\tgetWorldDirection( target ) {\n\n\t\treturn super.getWorldDirection( target ).negate();\n\n\t}\n\n\tupdateMatrixWorld( force ) {\n\n\t\tsuper.updateMatrixWorld( force );\n\n\t\tthis.matrixWorldInverse.copy( this.matrixWorld ).invert();\n\n\t}\n\n\tupdateWorldMatrix( updateParents, updateChildren ) {\n\n\t\tsuper.updateWorldMatrix( updateParents, updateChildren );\n\n\t\tthis.matrixWorldInverse.copy( this.matrixWorld ).invert();\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\nclass PerspectiveCamera extends Camera {\n\n\tconstructor( fov = 50, aspect = 1, near = 0.1, far = 2000 ) {\n\n\t\tsuper();\n\n\t\tthis.isPerspectiveCamera = true;\n\n\t\tthis.type = 'PerspectiveCamera';\n\n\t\tthis.fov = fov;\n\t\tthis.zoom = 1;\n\n\t\tthis.near = near;\n\t\tthis.far = far;\n\t\tthis.focus = 10;\n\n\t\tthis.aspect = aspect;\n\t\tthis.view = null;\n\n\t\tthis.filmGauge = 35;\t// width of the film (default in millimeters)\n\t\tthis.filmOffset = 0;\t// horizontal film offset (same unit as gauge)\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.fov = source.fov;\n\t\tthis.zoom = source.zoom;\n\n\t\tthis.near = source.near;\n\t\tthis.far = source.far;\n\t\tthis.focus = source.focus;\n\n\t\tthis.aspect = source.aspect;\n\t\tthis.view = source.view === null ? null : Object.assign( {}, source.view );\n\n\t\tthis.filmGauge = source.filmGauge;\n\t\tthis.filmOffset = source.filmOffset;\n\n\t\treturn this;\n\n\t}\n\n\t/**\n\t * Sets the FOV by focal length in respect to the current .filmGauge.\n\t *\n\t * The default film gauge is 35, so that the focal length can be specified for\n\t * a 35mm (full frame) camera.\n\t *\n\t * Values for focal length and film gauge must have the same unit.\n\t */\n\tsetFocalLength( focalLength ) {\n\n\t\t/** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */\n\t\tconst vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;\n\n\t\tthis.fov = RAD2DEG * 2 * Math.atan( vExtentSlope );\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\t/**\n\t * Calculates the focal length from the current .fov and .filmGauge.\n\t */\n\tgetFocalLength() {\n\n\t\tconst vExtentSlope = Math.tan( DEG2RAD * 0.5 * this.fov );\n\n\t\treturn 0.5 * this.getFilmHeight() / vExtentSlope;\n\n\t}\n\n\tgetEffectiveFOV() {\n\n\t\treturn RAD2DEG * 2 * Math.atan(\n\t\t\tMath.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom );\n\n\t}\n\n\tgetFilmWidth() {\n\n\t\t// film not completely covered in portrait format (aspect < 1)\n\t\treturn this.filmGauge * Math.min( this.aspect, 1 );\n\n\t}\n\n\tgetFilmHeight() {\n\n\t\t// film not completely covered in landscape format (aspect > 1)\n\t\treturn this.filmGauge / Math.max( this.aspect, 1 );\n\n\t}\n\n\t/**\n\t * Sets an offset in a larger frustum. This is useful for multi-window or\n\t * multi-monitor/multi-machine setups.\n\t *\n\t * For example, if you have 3x2 monitors and each monitor is 1920x1080 and\n\t * the monitors are in grid like this\n\t *\n\t *   +---+---+---+\n\t *   | A | B | C |\n\t *   +---+---+---+\n\t *   | D | E | F |\n\t *   +---+---+---+\n\t *\n\t * then for each monitor you would call it like this\n\t *\n\t *   const w = 1920;\n\t *   const h = 1080;\n\t *   const fullWidth = w * 3;\n\t *   const fullHeight = h * 2;\n\t *\n\t *   --A--\n\t *   camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );\n\t *   --B--\n\t *   camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );\n\t *   --C--\n\t *   camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );\n\t *   --D--\n\t *   camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );\n\t *   --E--\n\t *   camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );\n\t *   --F--\n\t *   camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );\n\t *\n\t *   Note there is no reason monitors have to be the same size or in a grid.\n\t */\n\tsetViewOffset( fullWidth, fullHeight, x, y, width, height ) {\n\n\t\tthis.aspect = fullWidth / fullHeight;\n\n\t\tif ( this.view === null ) {\n\n\t\t\tthis.view = {\n\t\t\t\tenabled: true,\n\t\t\t\tfullWidth: 1,\n\t\t\t\tfullHeight: 1,\n\t\t\t\toffsetX: 0,\n\t\t\t\toffsetY: 0,\n\t\t\t\twidth: 1,\n\t\t\t\theight: 1\n\t\t\t};\n\n\t\t}\n\n\t\tthis.view.enabled = true;\n\t\tthis.view.fullWidth = fullWidth;\n\t\tthis.view.fullHeight = fullHeight;\n\t\tthis.view.offsetX = x;\n\t\tthis.view.offsetY = y;\n\t\tthis.view.width = width;\n\t\tthis.view.height = height;\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tclearViewOffset() {\n\n\t\tif ( this.view !== null ) {\n\n\t\t\tthis.view.enabled = false;\n\n\t\t}\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tupdateProjectionMatrix() {\n\n\t\tconst near = this.near;\n\t\tlet top = near * Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom;\n\t\tlet height = 2 * top;\n\t\tlet width = this.aspect * height;\n\t\tlet left = - 0.5 * width;\n\t\tconst view = this.view;\n\n\t\tif ( this.view !== null && this.view.enabled ) {\n\n\t\t\tconst fullWidth = view.fullWidth,\n\t\t\t\tfullHeight = view.fullHeight;\n\n\t\t\tleft += view.offsetX * width / fullWidth;\n\t\t\ttop -= view.offsetY * height / fullHeight;\n\t\t\twidth *= view.width / fullWidth;\n\t\t\theight *= view.height / fullHeight;\n\n\t\t}\n\n\t\tconst skew = this.filmOffset;\n\t\tif ( skew !== 0 ) left += near * skew / this.getFilmWidth();\n\n\t\tthis.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far, this.coordinateSystem );\n\n\t\tthis.projectionMatrixInverse.copy( this.projectionMatrix ).invert();\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tdata.object.fov = this.fov;\n\t\tdata.object.zoom = this.zoom;\n\n\t\tdata.object.near = this.near;\n\t\tdata.object.far = this.far;\n\t\tdata.object.focus = this.focus;\n\n\t\tdata.object.aspect = this.aspect;\n\n\t\tif ( this.view !== null ) data.object.view = Object.assign( {}, this.view );\n\n\t\tdata.object.filmGauge = this.filmGauge;\n\t\tdata.object.filmOffset = this.filmOffset;\n\n\t\treturn data;\n\n\t}\n\n}\n\nconst fov = - 90; // negative fov is not an error\nconst aspect = 1;\n\nclass CubeCamera extends Object3D {\n\n\tconstructor( near, far, renderTarget ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'CubeCamera';\n\n\t\tthis.renderTarget = renderTarget;\n\t\tthis.coordinateSystem = null;\n\t\tthis.activeMipmapLevel = 0;\n\n\t\tconst cameraPX = new PerspectiveCamera( fov, aspect, near, far );\n\t\tcameraPX.layers = this.layers;\n\t\tthis.add( cameraPX );\n\n\t\tconst cameraNX = new PerspectiveCamera( fov, aspect, near, far );\n\t\tcameraNX.layers = this.layers;\n\t\tthis.add( cameraNX );\n\n\t\tconst cameraPY = new PerspectiveCamera( fov, aspect, near, far );\n\t\tcameraPY.layers = this.layers;\n\t\tthis.add( cameraPY );\n\n\t\tconst cameraNY = new PerspectiveCamera( fov, aspect, near, far );\n\t\tcameraNY.layers = this.layers;\n\t\tthis.add( cameraNY );\n\n\t\tconst cameraPZ = new PerspectiveCamera( fov, aspect, near, far );\n\t\tcameraPZ.layers = this.layers;\n\t\tthis.add( cameraPZ );\n\n\t\tconst cameraNZ = new PerspectiveCamera( fov, aspect, near, far );\n\t\tcameraNZ.layers = this.layers;\n\t\tthis.add( cameraNZ );\n\n\t}\n\n\tupdateCoordinateSystem() {\n\n\t\tconst coordinateSystem = this.coordinateSystem;\n\n\t\tconst cameras = this.children.concat();\n\n\t\tconst [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = cameras;\n\n\t\tfor ( const camera of cameras ) this.remove( camera );\n\n\t\tif ( coordinateSystem === WebGLCoordinateSystem ) {\n\n\t\t\tcameraPX.up.set( 0, 1, 0 );\n\t\t\tcameraPX.lookAt( 1, 0, 0 );\n\n\t\t\tcameraNX.up.set( 0, 1, 0 );\n\t\t\tcameraNX.lookAt( - 1, 0, 0 );\n\n\t\t\tcameraPY.up.set( 0, 0, - 1 );\n\t\t\tcameraPY.lookAt( 0, 1, 0 );\n\n\t\t\tcameraNY.up.set( 0, 0, 1 );\n\t\t\tcameraNY.lookAt( 0, - 1, 0 );\n\n\t\t\tcameraPZ.up.set( 0, 1, 0 );\n\t\t\tcameraPZ.lookAt( 0, 0, 1 );\n\n\t\t\tcameraNZ.up.set( 0, 1, 0 );\n\t\t\tcameraNZ.lookAt( 0, 0, - 1 );\n\n\t\t} else if ( coordinateSystem === WebGPUCoordinateSystem ) {\n\n\t\t\tcameraPX.up.set( 0, - 1, 0 );\n\t\t\tcameraPX.lookAt( - 1, 0, 0 );\n\n\t\t\tcameraNX.up.set( 0, - 1, 0 );\n\t\t\tcameraNX.lookAt( 1, 0, 0 );\n\n\t\t\tcameraPY.up.set( 0, 0, 1 );\n\t\t\tcameraPY.lookAt( 0, 1, 0 );\n\n\t\t\tcameraNY.up.set( 0, 0, - 1 );\n\t\t\tcameraNY.lookAt( 0, - 1, 0 );\n\n\t\t\tcameraPZ.up.set( 0, - 1, 0 );\n\t\t\tcameraPZ.lookAt( 0, 0, 1 );\n\n\t\t\tcameraNZ.up.set( 0, - 1, 0 );\n\t\t\tcameraNZ.lookAt( 0, 0, - 1 );\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: ' + coordinateSystem );\n\n\t\t}\n\n\t\tfor ( const camera of cameras ) {\n\n\t\t\tthis.add( camera );\n\n\t\t\tcamera.updateMatrixWorld();\n\n\t\t}\n\n\t}\n\n\tupdate( renderer, scene ) {\n\n\t\tif ( this.parent === null ) this.updateMatrixWorld();\n\n\t\tconst { renderTarget, activeMipmapLevel } = this;\n\n\t\tif ( this.coordinateSystem !== renderer.coordinateSystem ) {\n\n\t\t\tthis.coordinateSystem = renderer.coordinateSystem;\n\n\t\t\tthis.updateCoordinateSystem();\n\n\t\t}\n\n\t\tconst [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children;\n\n\t\tconst currentRenderTarget = renderer.getRenderTarget();\n\t\tconst currentActiveCubeFace = renderer.getActiveCubeFace();\n\t\tconst currentActiveMipmapLevel = renderer.getActiveMipmapLevel();\n\n\t\tconst currentXrEnabled = renderer.xr.enabled;\n\n\t\trenderer.xr.enabled = false;\n\n\t\tconst generateMipmaps = renderTarget.texture.generateMipmaps;\n\n\t\trenderTarget.texture.generateMipmaps = false;\n\n\t\trenderer.setRenderTarget( renderTarget, 0, activeMipmapLevel );\n\t\trenderer.render( scene, cameraPX );\n\n\t\trenderer.setRenderTarget( renderTarget, 1, activeMipmapLevel );\n\t\trenderer.render( scene, cameraNX );\n\n\t\trenderer.setRenderTarget( renderTarget, 2, activeMipmapLevel );\n\t\trenderer.render( scene, cameraPY );\n\n\t\trenderer.setRenderTarget( renderTarget, 3, activeMipmapLevel );\n\t\trenderer.render( scene, cameraNY );\n\n\t\trenderer.setRenderTarget( renderTarget, 4, activeMipmapLevel );\n\t\trenderer.render( scene, cameraPZ );\n\n\t\t// mipmaps are generated during the last call of render()\n\t\t// at this point, all sides of the cube render target are defined\n\n\t\trenderTarget.texture.generateMipmaps = generateMipmaps;\n\n\t\trenderer.setRenderTarget( renderTarget, 5, activeMipmapLevel );\n\t\trenderer.render( scene, cameraNZ );\n\n\t\trenderer.setRenderTarget( currentRenderTarget, currentActiveCubeFace, currentActiveMipmapLevel );\n\n\t\trenderer.xr.enabled = currentXrEnabled;\n\n\t\trenderTarget.texture.needsPMREMUpdate = true;\n\n\t}\n\n}\n\nclass CubeTexture extends Texture {\n\n\tconstructor( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ) {\n\n\t\timages = images !== undefined ? images : [];\n\t\tmapping = mapping !== undefined ? mapping : CubeReflectionMapping;\n\n\t\tsuper( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace );\n\n\t\tthis.isCubeTexture = true;\n\n\t\tthis.flipY = false;\n\n\t}\n\n\tget images() {\n\n\t\treturn this.image;\n\n\t}\n\n\tset images( value ) {\n\n\t\tthis.image = value;\n\n\t}\n\n}\n\nclass WebGLCubeRenderTarget extends WebGLRenderTarget {\n\n\tconstructor( size = 1, options = {} ) {\n\n\t\tsuper( size, size, options );\n\n\t\tthis.isWebGLCubeRenderTarget = true;\n\n\t\tconst image = { width: size, height: size, depth: 1 };\n\t\tconst images = [ image, image, image, image, image, image ];\n\n\t\tif ( options.encoding !== undefined ) {\n\n\t\t\t// @deprecated, r152\n\t\t\twarnOnce( 'THREE.WebGLCubeRenderTarget: option.encoding has been replaced by option.colorSpace.' );\n\t\t\toptions.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace;\n\n\t\t}\n\n\t\tthis.texture = new CubeTexture( images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace );\n\n\t\t// By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js)\n\t\t// in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words,\n\t\t// in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly.\n\n\t\t// three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped\n\t\t// and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture\n\t\t// as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures).\n\n\t\tthis.texture.isRenderTargetTexture = true;\n\n\t\tthis.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false;\n\t\tthis.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter;\n\n\t}\n\n\tfromEquirectangularTexture( renderer, texture ) {\n\n\t\tthis.texture.type = texture.type;\n\t\tthis.texture.colorSpace = texture.colorSpace;\n\n\t\tthis.texture.generateMipmaps = texture.generateMipmaps;\n\t\tthis.texture.minFilter = texture.minFilter;\n\t\tthis.texture.magFilter = texture.magFilter;\n\n\t\tconst shader = {\n\n\t\t\tuniforms: {\n\t\t\t\ttEquirect: { value: null },\n\t\t\t},\n\n\t\t\tvertexShader: /* glsl */`\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\tvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\n\t\t\t\t\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n\n\t\t\t\t}\n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvWorldDirection = transformDirection( position, modelMatrix );\n\n\t\t\t\t\t#include <begin_vertex>\n\t\t\t\t\t#include <project_vertex>\n\n\t\t\t\t}\n\t\t\t`,\n\n\t\t\tfragmentShader: /* glsl */`\n\n\t\t\t\tuniform sampler2D tEquirect;\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\t#include <common>\n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvec3 direction = normalize( vWorldDirection );\n\n\t\t\t\t\tvec2 sampleUV = equirectUv( direction );\n\n\t\t\t\t\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\n\t\t\t\t}\n\t\t\t`\n\t\t};\n\n\t\tconst geometry = new BoxGeometry( 5, 5, 5 );\n\n\t\tconst material = new ShaderMaterial( {\n\n\t\t\tname: 'CubemapFromEquirect',\n\n\t\t\tuniforms: cloneUniforms( shader.uniforms ),\n\t\t\tvertexShader: shader.vertexShader,\n\t\t\tfragmentShader: shader.fragmentShader,\n\t\t\tside: BackSide,\n\t\t\tblending: NoBlending\n\n\t\t} );\n\n\t\tmaterial.uniforms.tEquirect.value = texture;\n\n\t\tconst mesh = new Mesh( geometry, material );\n\n\t\tconst currentMinFilter = texture.minFilter;\n\n\t\t// Avoid blurred poles\n\t\tif ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter;\n\n\t\tconst camera = new CubeCamera( 1, 10, this );\n\t\tcamera.update( renderer, mesh );\n\n\t\ttexture.minFilter = currentMinFilter;\n\n\t\tmesh.geometry.dispose();\n\t\tmesh.material.dispose();\n\n\t\treturn this;\n\n\t}\n\n\tclear( renderer, color, depth, stencil ) {\n\n\t\tconst currentRenderTarget = renderer.getRenderTarget();\n\n\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\trenderer.setRenderTarget( this, i );\n\n\t\t\trenderer.clear( color, depth, stencil );\n\n\t\t}\n\n\t\trenderer.setRenderTarget( currentRenderTarget );\n\n\t}\n\n}\n\nconst _vector1 = /*@__PURE__*/ new Vector3();\nconst _vector2 = /*@__PURE__*/ new Vector3();\nconst _normalMatrix = /*@__PURE__*/ new Matrix3();\n\nclass Plane {\n\n\tconstructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) {\n\n\t\tthis.isPlane = true;\n\n\t\t// normal is assumed to be normalized\n\n\t\tthis.normal = normal;\n\t\tthis.constant = constant;\n\n\t}\n\n\tset( normal, constant ) {\n\n\t\tthis.normal.copy( normal );\n\t\tthis.constant = constant;\n\n\t\treturn this;\n\n\t}\n\n\tsetComponents( x, y, z, w ) {\n\n\t\tthis.normal.set( x, y, z );\n\t\tthis.constant = w;\n\n\t\treturn this;\n\n\t}\n\n\tsetFromNormalAndCoplanarPoint( normal, point ) {\n\n\t\tthis.normal.copy( normal );\n\t\tthis.constant = - point.dot( this.normal );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromCoplanarPoints( a, b, c ) {\n\n\t\tconst normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize();\n\n\t\t// Q: should an error be thrown if normal is zero (e.g. degenerate plane)?\n\n\t\tthis.setFromNormalAndCoplanarPoint( normal, a );\n\n\t\treturn this;\n\n\t}\n\n\tcopy( plane ) {\n\n\t\tthis.normal.copy( plane.normal );\n\t\tthis.constant = plane.constant;\n\n\t\treturn this;\n\n\t}\n\n\tnormalize() {\n\n\t\t// Note: will lead to a divide by zero if the plane is invalid.\n\n\t\tconst inverseNormalLength = 1.0 / this.normal.length();\n\t\tthis.normal.multiplyScalar( inverseNormalLength );\n\t\tthis.constant *= inverseNormalLength;\n\n\t\treturn this;\n\n\t}\n\n\tnegate() {\n\n\t\tthis.constant *= - 1;\n\t\tthis.normal.negate();\n\n\t\treturn this;\n\n\t}\n\n\tdistanceToPoint( point ) {\n\n\t\treturn this.normal.dot( point ) + this.constant;\n\n\t}\n\n\tdistanceToSphere( sphere ) {\n\n\t\treturn this.distanceToPoint( sphere.center ) - sphere.radius;\n\n\t}\n\n\tprojectPoint( point, target ) {\n\n\t\treturn target.copy( point ).addScaledVector( this.normal, - this.distanceToPoint( point ) );\n\n\t}\n\n\tintersectLine( line, target ) {\n\n\t\tconst direction = line.delta( _vector1 );\n\n\t\tconst denominator = this.normal.dot( direction );\n\n\t\tif ( denominator === 0 ) {\n\n\t\t\t// line is coplanar, return origin\n\t\t\tif ( this.distanceToPoint( line.start ) === 0 ) {\n\n\t\t\t\treturn target.copy( line.start );\n\n\t\t\t}\n\n\t\t\t// Unsure if this is the correct method to handle this case.\n\t\t\treturn null;\n\n\t\t}\n\n\t\tconst t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;\n\n\t\tif ( t < 0 || t > 1 ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\treturn target.copy( line.start ).addScaledVector( direction, t );\n\n\t}\n\n\tintersectsLine( line ) {\n\n\t\t// Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.\n\n\t\tconst startSign = this.distanceToPoint( line.start );\n\t\tconst endSign = this.distanceToPoint( line.end );\n\n\t\treturn ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\treturn box.intersectsPlane( this );\n\n\t}\n\n\tintersectsSphere( sphere ) {\n\n\t\treturn sphere.intersectsPlane( this );\n\n\t}\n\n\tcoplanarPoint( target ) {\n\n\t\treturn target.copy( this.normal ).multiplyScalar( - this.constant );\n\n\t}\n\n\tapplyMatrix4( matrix, optionalNormalMatrix ) {\n\n\t\tconst normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix );\n\n\t\tconst referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix );\n\n\t\tconst normal = this.normal.applyMatrix3( normalMatrix ).normalize();\n\n\t\tthis.constant = - referencePoint.dot( normal );\n\n\t\treturn this;\n\n\t}\n\n\ttranslate( offset ) {\n\n\t\tthis.constant -= offset.dot( this.normal );\n\n\t\treturn this;\n\n\t}\n\n\tequals( plane ) {\n\n\t\treturn plane.normal.equals( this.normal ) && ( plane.constant === this.constant );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\nconst _sphere$5 = /*@__PURE__*/ new Sphere();\nconst _vector$7 = /*@__PURE__*/ new Vector3();\n\nclass Frustum {\n\n\tconstructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane() ) {\n\n\t\tthis.planes = [ p0, p1, p2, p3, p4, p5 ];\n\n\t}\n\n\tset( p0, p1, p2, p3, p4, p5 ) {\n\n\t\tconst planes = this.planes;\n\n\t\tplanes[ 0 ].copy( p0 );\n\t\tplanes[ 1 ].copy( p1 );\n\t\tplanes[ 2 ].copy( p2 );\n\t\tplanes[ 3 ].copy( p3 );\n\t\tplanes[ 4 ].copy( p4 );\n\t\tplanes[ 5 ].copy( p5 );\n\n\t\treturn this;\n\n\t}\n\n\tcopy( frustum ) {\n\n\t\tconst planes = this.planes;\n\n\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\tplanes[ i ].copy( frustum.planes[ i ] );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetFromProjectionMatrix( m, coordinateSystem = WebGLCoordinateSystem ) {\n\n\t\tconst planes = this.planes;\n\t\tconst me = m.elements;\n\t\tconst me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];\n\t\tconst me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];\n\t\tconst me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];\n\t\tconst me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];\n\n\t\tplanes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();\n\t\tplanes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();\n\t\tplanes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();\n\t\tplanes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();\n\t\tplanes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();\n\n\t\tif ( coordinateSystem === WebGLCoordinateSystem ) {\n\n\t\t\tplanes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();\n\n\t\t} else if ( coordinateSystem === WebGPUCoordinateSystem ) {\n\n\t\t\tplanes[ 5 ].setComponents( me2, me6, me10, me14 ).normalize();\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: ' + coordinateSystem );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tintersectsObject( object ) {\n\n\t\tif ( object.boundingSphere !== undefined ) {\n\n\t\t\tif ( object.boundingSphere === null ) object.computeBoundingSphere();\n\n\t\t\t_sphere$5.copy( object.boundingSphere ).applyMatrix4( object.matrixWorld );\n\n\t\t} else {\n\n\t\t\tconst geometry = object.geometry;\n\n\t\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n\t\t\t_sphere$5.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld );\n\n\t\t}\n\n\t\treturn this.intersectsSphere( _sphere$5 );\n\n\t}\n\n\tintersectsSprite( sprite ) {\n\n\t\t_sphere$5.center.set( 0, 0, 0 );\n\t\t_sphere$5.radius = 0.7071067811865476;\n\t\t_sphere$5.applyMatrix4( sprite.matrixWorld );\n\n\t\treturn this.intersectsSphere( _sphere$5 );\n\n\t}\n\n\tintersectsSphere( sphere ) {\n\n\t\tconst planes = this.planes;\n\t\tconst center = sphere.center;\n\t\tconst negRadius = - sphere.radius;\n\n\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\tconst distance = planes[ i ].distanceToPoint( center );\n\n\t\t\tif ( distance < negRadius ) {\n\n\t\t\t\treturn false;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\tconst planes = this.planes;\n\n\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\tconst plane = planes[ i ];\n\n\t\t\t// corner at max distance\n\n\t\t\t_vector$7.x = plane.normal.x > 0 ? box.max.x : box.min.x;\n\t\t\t_vector$7.y = plane.normal.y > 0 ? box.max.y : box.min.y;\n\t\t\t_vector$7.z = plane.normal.z > 0 ? box.max.z : box.min.z;\n\n\t\t\tif ( plane.distanceToPoint( _vector$7 ) < 0 ) {\n\n\t\t\t\treturn false;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tcontainsPoint( point ) {\n\n\t\tconst planes = this.planes;\n\n\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\tif ( planes[ i ].distanceToPoint( point ) < 0 ) {\n\n\t\t\t\treturn false;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\nfunction WebGLAnimation() {\n\n\tlet context = null;\n\tlet isAnimating = false;\n\tlet animationLoop = null;\n\tlet requestId = null;\n\n\tfunction onAnimationFrame( time, frame ) {\n\n\t\tanimationLoop( time, frame );\n\n\t\trequestId = context.requestAnimationFrame( onAnimationFrame );\n\n\t}\n\n\treturn {\n\n\t\tstart: function () {\n\n\t\t\tif ( isAnimating === true ) return;\n\t\t\tif ( animationLoop === null ) return;\n\n\t\t\trequestId = context.requestAnimationFrame( onAnimationFrame );\n\n\t\t\tisAnimating = true;\n\n\t\t},\n\n\t\tstop: function () {\n\n\t\t\tcontext.cancelAnimationFrame( requestId );\n\n\t\t\tisAnimating = false;\n\n\t\t},\n\n\t\tsetAnimationLoop: function ( callback ) {\n\n\t\t\tanimationLoop = callback;\n\n\t\t},\n\n\t\tsetContext: function ( value ) {\n\n\t\t\tcontext = value;\n\n\t\t}\n\n\t};\n\n}\n\nfunction WebGLAttributes( gl, capabilities ) {\n\n\tconst isWebGL2 = capabilities.isWebGL2;\n\n\tconst buffers = new WeakMap();\n\n\tfunction createBuffer( attribute, bufferType ) {\n\n\t\tconst array = attribute.array;\n\t\tconst usage = attribute.usage;\n\t\tconst size = array.byteLength;\n\n\t\tconst buffer = gl.createBuffer();\n\n\t\tgl.bindBuffer( bufferType, buffer );\n\t\tgl.bufferData( bufferType, array, usage );\n\n\t\tattribute.onUploadCallback();\n\n\t\tlet type;\n\n\t\tif ( array instanceof Float32Array ) {\n\n\t\t\ttype = gl.FLOAT;\n\n\t\t} else if ( array instanceof Uint16Array ) {\n\n\t\t\tif ( attribute.isFloat16BufferAttribute ) {\n\n\t\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\t\ttype = gl.HALF_FLOAT;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthrow new Error( 'THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.' );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\ttype = gl.UNSIGNED_SHORT;\n\n\t\t\t}\n\n\t\t} else if ( array instanceof Int16Array ) {\n\n\t\t\ttype = gl.SHORT;\n\n\t\t} else if ( array instanceof Uint32Array ) {\n\n\t\t\ttype = gl.UNSIGNED_INT;\n\n\t\t} else if ( array instanceof Int32Array ) {\n\n\t\t\ttype = gl.INT;\n\n\t\t} else if ( array instanceof Int8Array ) {\n\n\t\t\ttype = gl.BYTE;\n\n\t\t} else if ( array instanceof Uint8Array ) {\n\n\t\t\ttype = gl.UNSIGNED_BYTE;\n\n\t\t} else if ( array instanceof Uint8ClampedArray ) {\n\n\t\t\ttype = gl.UNSIGNED_BYTE;\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'THREE.WebGLAttributes: Unsupported buffer data format: ' + array );\n\n\t\t}\n\n\t\treturn {\n\t\t\tbuffer: buffer,\n\t\t\ttype: type,\n\t\t\tbytesPerElement: array.BYTES_PER_ELEMENT,\n\t\t\tversion: attribute.version,\n\t\t\tsize: size\n\t\t};\n\n\t}\n\n\tfunction updateBuffer( buffer, attribute, bufferType ) {\n\n\t\tconst array = attribute.array;\n\t\tconst updateRange = attribute._updateRange; // deprecated\n\t\tconst updateRanges = attribute.updateRanges;\n\n\t\tgl.bindBuffer( bufferType, buffer );\n\n\t\tif ( updateRange.count === - 1 && updateRanges.length === 0 ) {\n\n\t\t\t// Not using update ranges\n\t\t\tgl.bufferSubData( bufferType, 0, array );\n\n\t\t}\n\n\t\tif ( updateRanges.length !== 0 ) {\n\n\t\t\tfor ( let i = 0, l = updateRanges.length; i < l; i ++ ) {\n\n\t\t\t\tconst range = updateRanges[ i ];\n\t\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\t\tgl.bufferSubData( bufferType, range.start * array.BYTES_PER_ELEMENT,\n\t\t\t\t\t\tarray, range.start, range.count );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tgl.bufferSubData( bufferType, range.start * array.BYTES_PER_ELEMENT,\n\t\t\t\t\t\tarray.subarray( range.start, range.start + range.count ) );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tattribute.clearUpdateRanges();\n\n\t\t}\n\n\t\t// deprecated\n\t\tif ( updateRange.count !== - 1 ) {\n\n\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\tgl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,\n\t\t\t\t\tarray, updateRange.offset, updateRange.count );\n\n\t\t\t} else {\n\n\t\t\t\tgl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,\n\t\t\t\t\tarray.subarray( updateRange.offset, updateRange.offset + updateRange.count ) );\n\n\t\t\t}\n\n\t\t\tupdateRange.count = - 1; // reset range\n\n\t\t}\n\n\t\tattribute.onUploadCallback();\n\n\t}\n\n\t//\n\n\tfunction get( attribute ) {\n\n\t\tif ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;\n\n\t\treturn buffers.get( attribute );\n\n\t}\n\n\tfunction remove( attribute ) {\n\n\t\tif ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;\n\n\t\tconst data = buffers.get( attribute );\n\n\t\tif ( data ) {\n\n\t\t\tgl.deleteBuffer( data.buffer );\n\n\t\t\tbuffers.delete( attribute );\n\n\t\t}\n\n\t}\n\n\tfunction update( attribute, bufferType ) {\n\n\t\tif ( attribute.isGLBufferAttribute ) {\n\n\t\t\tconst cached = buffers.get( attribute );\n\n\t\t\tif ( ! cached || cached.version < attribute.version ) {\n\n\t\t\t\tbuffers.set( attribute, {\n\t\t\t\t\tbuffer: attribute.buffer,\n\t\t\t\t\ttype: attribute.type,\n\t\t\t\t\tbytesPerElement: attribute.elementSize,\n\t\t\t\t\tversion: attribute.version\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;\n\n\t\tconst data = buffers.get( attribute );\n\n\t\tif ( data === undefined ) {\n\n\t\t\tbuffers.set( attribute, createBuffer( attribute, bufferType ) );\n\n\t\t} else if ( data.version < attribute.version ) {\n\n\t\t\tif ( data.size !== attribute.array.byteLength ) {\n\n\t\t\t\tthrow new Error( 'THREE.WebGLAttributes: The size of the buffer attribute\\'s array buffer does not match the original size. Resizing buffer attributes is not supported.' );\n\n\t\t\t}\n\n\t\t\tupdateBuffer( data.buffer, attribute, bufferType );\n\n\t\t\tdata.version = attribute.version;\n\n\t\t}\n\n\t}\n\n\treturn {\n\n\t\tget: get,\n\t\tremove: remove,\n\t\tupdate: update\n\n\t};\n\n}\n\nclass PlaneGeometry extends BufferGeometry {\n\n\tconstructor( width = 1, height = 1, widthSegments = 1, heightSegments = 1 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'PlaneGeometry';\n\n\t\tthis.parameters = {\n\t\t\twidth: width,\n\t\t\theight: height,\n\t\t\twidthSegments: widthSegments,\n\t\t\theightSegments: heightSegments\n\t\t};\n\n\t\tconst width_half = width / 2;\n\t\tconst height_half = height / 2;\n\n\t\tconst gridX = Math.floor( widthSegments );\n\t\tconst gridY = Math.floor( heightSegments );\n\n\t\tconst gridX1 = gridX + 1;\n\t\tconst gridY1 = gridY + 1;\n\n\t\tconst segment_width = width / gridX;\n\t\tconst segment_height = height / gridY;\n\n\t\t//\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\tfor ( let iy = 0; iy < gridY1; iy ++ ) {\n\n\t\t\tconst y = iy * segment_height - height_half;\n\n\t\t\tfor ( let ix = 0; ix < gridX1; ix ++ ) {\n\n\t\t\t\tconst x = ix * segment_width - width_half;\n\n\t\t\t\tvertices.push( x, - y, 0 );\n\n\t\t\t\tnormals.push( 0, 0, 1 );\n\n\t\t\t\tuvs.push( ix / gridX );\n\t\t\t\tuvs.push( 1 - ( iy / gridY ) );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfor ( let iy = 0; iy < gridY; iy ++ ) {\n\n\t\t\tfor ( let ix = 0; ix < gridX; ix ++ ) {\n\n\t\t\t\tconst a = ix + gridX1 * iy;\n\t\t\t\tconst b = ix + gridX1 * ( iy + 1 );\n\t\t\t\tconst c = ( ix + 1 ) + gridX1 * ( iy + 1 );\n\t\t\t\tconst d = ( ix + 1 ) + gridX1 * iy;\n\n\t\t\t\tindices.push( a, b, d );\n\t\t\t\tindices.push( b, c, d );\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments );\n\n\t}\n\n}\n\nvar alphahash_fragment = \"#ifdef USE_ALPHAHASH\\n\\tif ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard;\\n#endif\";\n\nvar alphahash_pars_fragment = \"#ifdef USE_ALPHAHASH\\n\\tconst float ALPHA_HASH_SCALE = 0.05;\\n\\tfloat hash2D( vec2 value ) {\\n\\t\\treturn fract( 1.0e4 * sin( 17.0 * value.x + 0.1 * value.y ) * ( 0.1 + abs( sin( 13.0 * value.y + value.x ) ) ) );\\n\\t}\\n\\tfloat hash3D( vec3 value ) {\\n\\t\\treturn hash2D( vec2( hash2D( value.xy ), value.z ) );\\n\\t}\\n\\tfloat getAlphaHashThreshold( vec3 position ) {\\n\\t\\tfloat maxDeriv = max(\\n\\t\\t\\tlength( dFdx( position.xyz ) ),\\n\\t\\t\\tlength( dFdy( position.xyz ) )\\n\\t\\t);\\n\\t\\tfloat pixScale = 1.0 / ( ALPHA_HASH_SCALE * maxDeriv );\\n\\t\\tvec2 pixScales = vec2(\\n\\t\\t\\texp2( floor( log2( pixScale ) ) ),\\n\\t\\t\\texp2( ceil( log2( pixScale ) ) )\\n\\t\\t);\\n\\t\\tvec2 alpha = vec2(\\n\\t\\t\\thash3D( floor( pixScales.x * position.xyz ) ),\\n\\t\\t\\thash3D( floor( pixScales.y * position.xyz ) )\\n\\t\\t);\\n\\t\\tfloat lerpFactor = fract( log2( pixScale ) );\\n\\t\\tfloat x = ( 1.0 - lerpFactor ) * alpha.x + lerpFactor * alpha.y;\\n\\t\\tfloat a = min( lerpFactor, 1.0 - lerpFactor );\\n\\t\\tvec3 cases = vec3(\\n\\t\\t\\tx * x / ( 2.0 * a * ( 1.0 - a ) ),\\n\\t\\t\\t( x - 0.5 * a ) / ( 1.0 - a ),\\n\\t\\t\\t1.0 - ( ( 1.0 - x ) * ( 1.0 - x ) / ( 2.0 * a * ( 1.0 - a ) ) )\\n\\t\\t);\\n\\t\\tfloat threshold = ( x < ( 1.0 - a ) )\\n\\t\\t\\t? ( ( x < a ) ? cases.x : cases.y )\\n\\t\\t\\t: cases.z;\\n\\t\\treturn clamp( threshold , 1.0e-6, 1.0 );\\n\\t}\\n#endif\";\n\nvar alphamap_fragment = \"#ifdef USE_ALPHAMAP\\n\\tdiffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g;\\n#endif\";\n\nvar alphamap_pars_fragment = \"#ifdef USE_ALPHAMAP\\n\\tuniform sampler2D alphaMap;\\n#endif\";\n\nvar alphatest_fragment = \"#ifdef USE_ALPHATEST\\n\\tif ( diffuseColor.a < alphaTest ) discard;\\n#endif\";\n\nvar alphatest_pars_fragment = \"#ifdef USE_ALPHATEST\\n\\tuniform float alphaTest;\\n#endif\";\n\nvar aomap_fragment = \"#ifdef USE_AOMAP\\n\\tfloat ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0;\\n\\treflectedLight.indirectDiffuse *= ambientOcclusion;\\n\\t#if defined( USE_CLEARCOAT ) \\n\\t\\tclearcoatSpecularIndirect *= ambientOcclusion;\\n\\t#endif\\n\\t#if defined( USE_SHEEN ) \\n\\t\\tsheenSpecularIndirect *= ambientOcclusion;\\n\\t#endif\\n\\t#if defined( USE_ENVMAP ) && defined( STANDARD )\\n\\t\\tfloat dotNV = saturate( dot( geometryNormal, geometryViewDir ) );\\n\\t\\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );\\n\\t#endif\\n#endif\";\n\nvar aomap_pars_fragment = \"#ifdef USE_AOMAP\\n\\tuniform sampler2D aoMap;\\n\\tuniform float aoMapIntensity;\\n#endif\";\n\nvar batching_pars_vertex = \"#ifdef USE_BATCHING\\n\\tattribute float batchId;\\n\\tuniform highp sampler2D batchingTexture;\\n\\tmat4 getBatchingMatrix( const in float i ) {\\n\\t\\tint size = textureSize( batchingTexture, 0 ).x;\\n\\t\\tint j = int( i ) * 4;\\n\\t\\tint x = j % size;\\n\\t\\tint y = j / size;\\n\\t\\tvec4 v1 = texelFetch( batchingTexture, ivec2( x, y ), 0 );\\n\\t\\tvec4 v2 = texelFetch( batchingTexture, ivec2( x + 1, y ), 0 );\\n\\t\\tvec4 v3 = texelFetch( batchingTexture, ivec2( x + 2, y ), 0 );\\n\\t\\tvec4 v4 = texelFetch( batchingTexture, ivec2( x + 3, y ), 0 );\\n\\t\\treturn mat4( v1, v2, v3, v4 );\\n\\t}\\n#endif\";\n\nvar batching_vertex = \"#ifdef USE_BATCHING\\n\\tmat4 batchingMatrix = getBatchingMatrix( batchId );\\n#endif\";\n\nvar begin_vertex = \"vec3 transformed = vec3( position );\\n#ifdef USE_ALPHAHASH\\n\\tvPosition = vec3( position );\\n#endif\";\n\nvar beginnormal_vertex = \"vec3 objectNormal = vec3( normal );\\n#ifdef USE_TANGENT\\n\\tvec3 objectTangent = vec3( tangent.xyz );\\n#endif\";\n\nvar bsdfs = \"float G_BlinnPhong_Implicit( ) {\\n\\treturn 0.25;\\n}\\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\\n\\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\\n}\\nvec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) {\\n\\tvec3 halfDir = normalize( lightDir + viewDir );\\n\\tfloat dotNH = saturate( dot( normal, halfDir ) );\\n\\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\\n\\tvec3 F = F_Schlick( specularColor, 1.0, dotVH );\\n\\tfloat G = G_BlinnPhong_Implicit( );\\n\\tfloat D = D_BlinnPhong( shininess, dotNH );\\n\\treturn F * ( G * D );\\n} // validated\";\n\nvar iridescence_fragment = \"#ifdef USE_IRIDESCENCE\\n\\tconst mat3 XYZ_TO_REC709 = mat3(\\n\\t\\t 3.2404542, -0.9692660,  0.0556434,\\n\\t\\t-1.5371385,  1.8760108, -0.2040259,\\n\\t\\t-0.4985314,  0.0415560,  1.0572252\\n\\t);\\n\\tvec3 Fresnel0ToIor( vec3 fresnel0 ) {\\n\\t\\tvec3 sqrtF0 = sqrt( fresnel0 );\\n\\t\\treturn ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 );\\n\\t}\\n\\tvec3 IorToFresnel0( vec3 transmittedIor, float incidentIor ) {\\n\\t\\treturn pow2( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) );\\n\\t}\\n\\tfloat IorToFresnel0( float transmittedIor, float incidentIor ) {\\n\\t\\treturn pow2( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor ));\\n\\t}\\n\\tvec3 evalSensitivity( float OPD, vec3 shift ) {\\n\\t\\tfloat phase = 2.0 * PI * OPD * 1.0e-9;\\n\\t\\tvec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 );\\n\\t\\tvec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 );\\n\\t\\tvec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 );\\n\\t\\tvec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - pow2( phase ) * var );\\n\\t\\txyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * pow2( phase ) );\\n\\t\\txyz /= 1.0685e-7;\\n\\t\\tvec3 rgb = XYZ_TO_REC709 * xyz;\\n\\t\\treturn rgb;\\n\\t}\\n\\tvec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) {\\n\\t\\tvec3 I;\\n\\t\\tfloat iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) );\\n\\t\\tfloat sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) );\\n\\t\\tfloat cosTheta2Sq = 1.0 - sinTheta2Sq;\\n\\t\\tif ( cosTheta2Sq < 0.0 ) {\\n\\t\\t\\treturn vec3( 1.0 );\\n\\t\\t}\\n\\t\\tfloat cosTheta2 = sqrt( cosTheta2Sq );\\n\\t\\tfloat R0 = IorToFresnel0( iridescenceIOR, outsideIOR );\\n\\t\\tfloat R12 = F_Schlick( R0, 1.0, cosTheta1 );\\n\\t\\tfloat T121 = 1.0 - R12;\\n\\t\\tfloat phi12 = 0.0;\\n\\t\\tif ( iridescenceIOR < outsideIOR ) phi12 = PI;\\n\\t\\tfloat phi21 = PI - phi12;\\n\\t\\tvec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) );\\t\\tvec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR );\\n\\t\\tvec3 R23 = F_Schlick( R1, 1.0, cosTheta2 );\\n\\t\\tvec3 phi23 = vec3( 0.0 );\\n\\t\\tif ( baseIOR[ 0 ] < iridescenceIOR ) phi23[ 0 ] = PI;\\n\\t\\tif ( baseIOR[ 1 ] < iridescenceIOR ) phi23[ 1 ] = PI;\\n\\t\\tif ( baseIOR[ 2 ] < iridescenceIOR ) phi23[ 2 ] = PI;\\n\\t\\tfloat OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2;\\n\\t\\tvec3 phi = vec3( phi21 ) + phi23;\\n\\t\\tvec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 );\\n\\t\\tvec3 r123 = sqrt( R123 );\\n\\t\\tvec3 Rs = pow2( T121 ) * R23 / ( vec3( 1.0 ) - R123 );\\n\\t\\tvec3 C0 = R12 + Rs;\\n\\t\\tI = C0;\\n\\t\\tvec3 Cm = Rs - T121;\\n\\t\\tfor ( int m = 1; m <= 2; ++ m ) {\\n\\t\\t\\tCm *= r123;\\n\\t\\t\\tvec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi );\\n\\t\\t\\tI += Cm * Sm;\\n\\t\\t}\\n\\t\\treturn max( I, vec3( 0.0 ) );\\n\\t}\\n#endif\";\n\nvar bumpmap_pars_fragment = \"#ifdef USE_BUMPMAP\\n\\tuniform sampler2D bumpMap;\\n\\tuniform float bumpScale;\\n\\tvec2 dHdxy_fwd() {\\n\\t\\tvec2 dSTdx = dFdx( vBumpMapUv );\\n\\t\\tvec2 dSTdy = dFdy( vBumpMapUv );\\n\\t\\tfloat Hll = bumpScale * texture2D( bumpMap, vBumpMapUv ).x;\\n\\t\\tfloat dBx = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdx ).x - Hll;\\n\\t\\tfloat dBy = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdy ).x - Hll;\\n\\t\\treturn vec2( dBx, dBy );\\n\\t}\\n\\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {\\n\\t\\tvec3 vSigmaX = normalize( dFdx( surf_pos.xyz ) );\\n\\t\\tvec3 vSigmaY = normalize( dFdy( surf_pos.xyz ) );\\n\\t\\tvec3 vN = surf_norm;\\n\\t\\tvec3 R1 = cross( vSigmaY, vN );\\n\\t\\tvec3 R2 = cross( vN, vSigmaX );\\n\\t\\tfloat fDet = dot( vSigmaX, R1 ) * faceDirection;\\n\\t\\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\\n\\t\\treturn normalize( abs( fDet ) * surf_norm - vGrad );\\n\\t}\\n#endif\";\n\nvar clipping_planes_fragment = \"#if NUM_CLIPPING_PLANES > 0\\n\\tvec4 plane;\\n\\t#pragma unroll_loop_start\\n\\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\\n\\t\\tplane = clippingPlanes[ i ];\\n\\t\\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\\n\\t}\\n\\t#pragma unroll_loop_end\\n\\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\\n\\t\\tbool clipped = true;\\n\\t\\t#pragma unroll_loop_start\\n\\t\\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\\n\\t\\t\\tplane = clippingPlanes[ i ];\\n\\t\\t\\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\\n\\t\\t}\\n\\t\\t#pragma unroll_loop_end\\n\\t\\tif ( clipped ) discard;\\n\\t#endif\\n#endif\";\n\nvar clipping_planes_pars_fragment = \"#if NUM_CLIPPING_PLANES > 0\\n\\tvarying vec3 vClipPosition;\\n\\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\\n#endif\";\n\nvar clipping_planes_pars_vertex = \"#if NUM_CLIPPING_PLANES > 0\\n\\tvarying vec3 vClipPosition;\\n#endif\";\n\nvar clipping_planes_vertex = \"#if NUM_CLIPPING_PLANES > 0\\n\\tvClipPosition = - mvPosition.xyz;\\n#endif\";\n\nvar color_fragment = \"#if defined( USE_COLOR_ALPHA )\\n\\tdiffuseColor *= vColor;\\n#elif defined( USE_COLOR )\\n\\tdiffuseColor.rgb *= vColor;\\n#endif\";\n\nvar color_pars_fragment = \"#if defined( USE_COLOR_ALPHA )\\n\\tvarying vec4 vColor;\\n#elif defined( USE_COLOR )\\n\\tvarying vec3 vColor;\\n#endif\";\n\nvar color_pars_vertex = \"#if defined( USE_COLOR_ALPHA )\\n\\tvarying vec4 vColor;\\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\\n\\tvarying vec3 vColor;\\n#endif\";\n\nvar color_vertex = \"#if defined( USE_COLOR_ALPHA )\\n\\tvColor = vec4( 1.0 );\\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\\n\\tvColor = vec3( 1.0 );\\n#endif\\n#ifdef USE_COLOR\\n\\tvColor *= color;\\n#endif\\n#ifdef USE_INSTANCING_COLOR\\n\\tvColor.xyz *= instanceColor.xyz;\\n#endif\";\n\nvar common = \"#define PI 3.141592653589793\\n#define PI2 6.283185307179586\\n#define PI_HALF 1.5707963267948966\\n#define RECIPROCAL_PI 0.3183098861837907\\n#define RECIPROCAL_PI2 0.15915494309189535\\n#define EPSILON 1e-6\\n#ifndef saturate\\n#define saturate( a ) clamp( a, 0.0, 1.0 )\\n#endif\\n#define whiteComplement( a ) ( 1.0 - saturate( a ) )\\nfloat pow2( const in float x ) { return x*x; }\\nvec3 pow2( const in vec3 x ) { return x*x; }\\nfloat pow3( const in float x ) { return x*x*x; }\\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\\nfloat max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); }\\nfloat average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); }\\nhighp float rand( const in vec2 uv ) {\\n\\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\\n\\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\\n\\treturn fract( sin( sn ) * c );\\n}\\n#ifdef HIGH_PRECISION\\n\\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\\n#else\\n\\tfloat precisionSafeLength( vec3 v ) {\\n\\t\\tfloat maxComponent = max3( abs( v ) );\\n\\t\\treturn length( v / maxComponent ) * maxComponent;\\n\\t}\\n#endif\\nstruct IncidentLight {\\n\\tvec3 color;\\n\\tvec3 direction;\\n\\tbool visible;\\n};\\nstruct ReflectedLight {\\n\\tvec3 directDiffuse;\\n\\tvec3 directSpecular;\\n\\tvec3 indirectDiffuse;\\n\\tvec3 indirectSpecular;\\n};\\n#ifdef USE_ALPHAHASH\\n\\tvarying vec3 vPosition;\\n#endif\\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\\n\\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\\n}\\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\\n\\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\\n}\\nmat3 transposeMat3( const in mat3 m ) {\\n\\tmat3 tmp;\\n\\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\\n\\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\\n\\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\\n\\treturn tmp;\\n}\\nfloat luminance( const in vec3 rgb ) {\\n\\tconst vec3 weights = vec3( 0.2126729, 0.7151522, 0.0721750 );\\n\\treturn dot( weights, rgb );\\n}\\nbool isPerspectiveMatrix( mat4 m ) {\\n\\treturn m[ 2 ][ 3 ] == - 1.0;\\n}\\nvec2 equirectUv( in vec3 dir ) {\\n\\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\\n\\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\\n\\treturn vec2( u, v );\\n}\\nvec3 BRDF_Lambert( const in vec3 diffuseColor ) {\\n\\treturn RECIPROCAL_PI * diffuseColor;\\n}\\nvec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {\\n\\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\\n\\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\\n}\\nfloat F_Schlick( const in float f0, const in float f90, const in float dotVH ) {\\n\\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\\n\\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\\n} // validated\";\n\nvar cube_uv_reflection_fragment = \"#ifdef ENVMAP_TYPE_CUBE_UV\\n\\t#define cubeUV_minMipLevel 4.0\\n\\t#define cubeUV_minTileSize 16.0\\n\\tfloat getFace( vec3 direction ) {\\n\\t\\tvec3 absDirection = abs( direction );\\n\\t\\tfloat face = - 1.0;\\n\\t\\tif ( absDirection.x > absDirection.z ) {\\n\\t\\t\\tif ( absDirection.x > absDirection.y )\\n\\t\\t\\t\\tface = direction.x > 0.0 ? 0.0 : 3.0;\\n\\t\\t\\telse\\n\\t\\t\\t\\tface = direction.y > 0.0 ? 1.0 : 4.0;\\n\\t\\t} else {\\n\\t\\t\\tif ( absDirection.z > absDirection.y )\\n\\t\\t\\t\\tface = direction.z > 0.0 ? 2.0 : 5.0;\\n\\t\\t\\telse\\n\\t\\t\\t\\tface = direction.y > 0.0 ? 1.0 : 4.0;\\n\\t\\t}\\n\\t\\treturn face;\\n\\t}\\n\\tvec2 getUV( vec3 direction, float face ) {\\n\\t\\tvec2 uv;\\n\\t\\tif ( face == 0.0 ) {\\n\\t\\t\\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\\n\\t\\t} else if ( face == 1.0 ) {\\n\\t\\t\\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\\n\\t\\t} else if ( face == 2.0 ) {\\n\\t\\t\\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\\n\\t\\t} else if ( face == 3.0 ) {\\n\\t\\t\\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\\n\\t\\t} else if ( face == 4.0 ) {\\n\\t\\t\\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\\n\\t\\t} else {\\n\\t\\t\\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\\n\\t\\t}\\n\\t\\treturn 0.5 * ( uv + 1.0 );\\n\\t}\\n\\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\\n\\t\\tfloat face = getFace( direction );\\n\\t\\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\\n\\t\\tmipInt = max( mipInt, cubeUV_minMipLevel );\\n\\t\\tfloat faceSize = exp2( mipInt );\\n\\t\\thighp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0;\\n\\t\\tif ( face > 2.0 ) {\\n\\t\\t\\tuv.y += faceSize;\\n\\t\\t\\tface -= 3.0;\\n\\t\\t}\\n\\t\\tuv.x += face * faceSize;\\n\\t\\tuv.x += filterInt * 3.0 * cubeUV_minTileSize;\\n\\t\\tuv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize );\\n\\t\\tuv.x *= CUBEUV_TEXEL_WIDTH;\\n\\t\\tuv.y *= CUBEUV_TEXEL_HEIGHT;\\n\\t\\t#ifdef texture2DGradEXT\\n\\t\\t\\treturn texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb;\\n\\t\\t#else\\n\\t\\t\\treturn texture2D( envMap, uv ).rgb;\\n\\t\\t#endif\\n\\t}\\n\\t#define cubeUV_r0 1.0\\n\\t#define cubeUV_m0 - 2.0\\n\\t#define cubeUV_r1 0.8\\n\\t#define cubeUV_m1 - 1.0\\n\\t#define cubeUV_r4 0.4\\n\\t#define cubeUV_m4 2.0\\n\\t#define cubeUV_r5 0.305\\n\\t#define cubeUV_m5 3.0\\n\\t#define cubeUV_r6 0.21\\n\\t#define cubeUV_m6 4.0\\n\\tfloat roughnessToMip( float roughness ) {\\n\\t\\tfloat mip = 0.0;\\n\\t\\tif ( roughness >= cubeUV_r1 ) {\\n\\t\\t\\tmip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0;\\n\\t\\t} else if ( roughness >= cubeUV_r4 ) {\\n\\t\\t\\tmip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1;\\n\\t\\t} else if ( roughness >= cubeUV_r5 ) {\\n\\t\\t\\tmip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4;\\n\\t\\t} else if ( roughness >= cubeUV_r6 ) {\\n\\t\\t\\tmip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5;\\n\\t\\t} else {\\n\\t\\t\\tmip = - 2.0 * log2( 1.16 * roughness );\\t\\t}\\n\\t\\treturn mip;\\n\\t}\\n\\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\\n\\t\\tfloat mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP );\\n\\t\\tfloat mipF = fract( mip );\\n\\t\\tfloat mipInt = floor( mip );\\n\\t\\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\\n\\t\\tif ( mipF == 0.0 ) {\\n\\t\\t\\treturn vec4( color0, 1.0 );\\n\\t\\t} else {\\n\\t\\t\\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\\n\\t\\t\\treturn vec4( mix( color0, color1, mipF ), 1.0 );\\n\\t\\t}\\n\\t}\\n#endif\";\n\nvar defaultnormal_vertex = \"vec3 transformedNormal = objectNormal;\\n#ifdef USE_TANGENT\\n\\tvec3 transformedTangent = objectTangent;\\n#endif\\n#ifdef USE_BATCHING\\n\\tmat3 bm = mat3( batchingMatrix );\\n\\ttransformedNormal /= vec3( dot( bm[ 0 ], bm[ 0 ] ), dot( bm[ 1 ], bm[ 1 ] ), dot( bm[ 2 ], bm[ 2 ] ) );\\n\\ttransformedNormal = bm * transformedNormal;\\n\\t#ifdef USE_TANGENT\\n\\t\\ttransformedTangent = bm * transformedTangent;\\n\\t#endif\\n#endif\\n#ifdef USE_INSTANCING\\n\\tmat3 im = mat3( instanceMatrix );\\n\\ttransformedNormal /= vec3( dot( im[ 0 ], im[ 0 ] ), dot( im[ 1 ], im[ 1 ] ), dot( im[ 2 ], im[ 2 ] ) );\\n\\ttransformedNormal = im * transformedNormal;\\n\\t#ifdef USE_TANGENT\\n\\t\\ttransformedTangent = im * transformedTangent;\\n\\t#endif\\n#endif\\ntransformedNormal = normalMatrix * transformedNormal;\\n#ifdef FLIP_SIDED\\n\\ttransformedNormal = - transformedNormal;\\n#endif\\n#ifdef USE_TANGENT\\n\\ttransformedTangent = ( modelViewMatrix * vec4( transformedTangent, 0.0 ) ).xyz;\\n\\t#ifdef FLIP_SIDED\\n\\t\\ttransformedTangent = - transformedTangent;\\n\\t#endif\\n#endif\";\n\nvar displacementmap_pars_vertex = \"#ifdef USE_DISPLACEMENTMAP\\n\\tuniform sampler2D displacementMap;\\n\\tuniform float displacementScale;\\n\\tuniform float displacementBias;\\n#endif\";\n\nvar displacementmap_vertex = \"#ifdef USE_DISPLACEMENTMAP\\n\\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias );\\n#endif\";\n\nvar emissivemap_fragment = \"#ifdef USE_EMISSIVEMAP\\n\\tvec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv );\\n\\ttotalEmissiveRadiance *= emissiveColor.rgb;\\n#endif\";\n\nvar emissivemap_pars_fragment = \"#ifdef USE_EMISSIVEMAP\\n\\tuniform sampler2D emissiveMap;\\n#endif\";\n\nvar colorspace_fragment = \"gl_FragColor = linearToOutputTexel( gl_FragColor );\";\n\nvar colorspace_pars_fragment = \"\\nconst mat3 LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = mat3(\\n\\tvec3( 0.8224621, 0.177538, 0.0 ),\\n\\tvec3( 0.0331941, 0.9668058, 0.0 ),\\n\\tvec3( 0.0170827, 0.0723974, 0.9105199 )\\n);\\nconst mat3 LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = mat3(\\n\\tvec3( 1.2249401, - 0.2249404, 0.0 ),\\n\\tvec3( - 0.0420569, 1.0420571, 0.0 ),\\n\\tvec3( - 0.0196376, - 0.0786361, 1.0982735 )\\n);\\nvec4 LinearSRGBToLinearDisplayP3( in vec4 value ) {\\n\\treturn vec4( value.rgb * LINEAR_SRGB_TO_LINEAR_DISPLAY_P3, value.a );\\n}\\nvec4 LinearDisplayP3ToLinearSRGB( in vec4 value ) {\\n\\treturn vec4( value.rgb * LINEAR_DISPLAY_P3_TO_LINEAR_SRGB, value.a );\\n}\\nvec4 LinearTransferOETF( in vec4 value ) {\\n\\treturn value;\\n}\\nvec4 sRGBTransferOETF( in vec4 value ) {\\n\\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\\n}\\nvec4 LinearToLinear( in vec4 value ) {\\n\\treturn value;\\n}\\nvec4 LinearTosRGB( in vec4 value ) {\\n\\treturn sRGBTransferOETF( value );\\n}\";\n\nvar envmap_fragment = \"#ifdef USE_ENVMAP\\n\\t#ifdef ENV_WORLDPOS\\n\\t\\tvec3 cameraToFrag;\\n\\t\\tif ( isOrthographic ) {\\n\\t\\t\\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\\n\\t\\t} else {\\n\\t\\t\\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\\n\\t\\t}\\n\\t\\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\\n\\t\\t#ifdef ENVMAP_MODE_REFLECTION\\n\\t\\t\\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\\n\\t\\t#else\\n\\t\\t\\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\\n\\t\\t#endif\\n\\t#else\\n\\t\\tvec3 reflectVec = vReflect;\\n\\t#endif\\n\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\\n\\t#else\\n\\t\\tvec4 envColor = vec4( 0.0 );\\n\\t#endif\\n\\t#ifdef ENVMAP_BLENDING_MULTIPLY\\n\\t\\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\\n\\t#elif defined( ENVMAP_BLENDING_MIX )\\n\\t\\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\\n\\t#elif defined( ENVMAP_BLENDING_ADD )\\n\\t\\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\\n\\t#endif\\n#endif\";\n\nvar envmap_common_pars_fragment = \"#ifdef USE_ENVMAP\\n\\tuniform float envMapIntensity;\\n\\tuniform float flipEnvMap;\\n\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\tuniform samplerCube envMap;\\n\\t#else\\n\\t\\tuniform sampler2D envMap;\\n\\t#endif\\n\\t\\n#endif\";\n\nvar envmap_pars_fragment = \"#ifdef USE_ENVMAP\\n\\tuniform float reflectivity;\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT )\\n\\t\\t#define ENV_WORLDPOS\\n\\t#endif\\n\\t#ifdef ENV_WORLDPOS\\n\\t\\tvarying vec3 vWorldPosition;\\n\\t\\tuniform float refractionRatio;\\n\\t#else\\n\\t\\tvarying vec3 vReflect;\\n\\t#endif\\n#endif\";\n\nvar envmap_pars_vertex = \"#ifdef USE_ENVMAP\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT )\\n\\t\\t#define ENV_WORLDPOS\\n\\t#endif\\n\\t#ifdef ENV_WORLDPOS\\n\\t\\t\\n\\t\\tvarying vec3 vWorldPosition;\\n\\t#else\\n\\t\\tvarying vec3 vReflect;\\n\\t\\tuniform float refractionRatio;\\n\\t#endif\\n#endif\";\n\nvar envmap_vertex = \"#ifdef USE_ENVMAP\\n\\t#ifdef ENV_WORLDPOS\\n\\t\\tvWorldPosition = worldPosition.xyz;\\n\\t#else\\n\\t\\tvec3 cameraToVertex;\\n\\t\\tif ( isOrthographic ) {\\n\\t\\t\\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\\n\\t\\t} else {\\n\\t\\t\\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\\n\\t\\t}\\n\\t\\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\\n\\t\\t#ifdef ENVMAP_MODE_REFLECTION\\n\\t\\t\\tvReflect = reflect( cameraToVertex, worldNormal );\\n\\t\\t#else\\n\\t\\t\\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\\n\\t\\t#endif\\n\\t#endif\\n#endif\";\n\nvar fog_vertex = \"#ifdef USE_FOG\\n\\tvFogDepth = - mvPosition.z;\\n#endif\";\n\nvar fog_pars_vertex = \"#ifdef USE_FOG\\n\\tvarying float vFogDepth;\\n#endif\";\n\nvar fog_fragment = \"#ifdef USE_FOG\\n\\t#ifdef FOG_EXP2\\n\\t\\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth );\\n\\t#else\\n\\t\\tfloat fogFactor = smoothstep( fogNear, fogFar, vFogDepth );\\n\\t#endif\\n\\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\\n#endif\";\n\nvar fog_pars_fragment = \"#ifdef USE_FOG\\n\\tuniform vec3 fogColor;\\n\\tvarying float vFogDepth;\\n\\t#ifdef FOG_EXP2\\n\\t\\tuniform float fogDensity;\\n\\t#else\\n\\t\\tuniform float fogNear;\\n\\t\\tuniform float fogFar;\\n\\t#endif\\n#endif\";\n\nvar gradientmap_pars_fragment = \"#ifdef USE_GRADIENTMAP\\n\\tuniform sampler2D gradientMap;\\n#endif\\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\\n\\tfloat dotNL = dot( normal, lightDirection );\\n\\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\\n\\t#ifdef USE_GRADIENTMAP\\n\\t\\treturn vec3( texture2D( gradientMap, coord ).r );\\n\\t#else\\n\\t\\tvec2 fw = fwidth( coord ) * 0.5;\\n\\t\\treturn mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) );\\n\\t#endif\\n}\";\n\nvar lightmap_fragment = \"#ifdef USE_LIGHTMAP\\n\\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\\n\\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\\n\\treflectedLight.indirectDiffuse += lightMapIrradiance;\\n#endif\";\n\nvar lightmap_pars_fragment = \"#ifdef USE_LIGHTMAP\\n\\tuniform sampler2D lightMap;\\n\\tuniform float lightMapIntensity;\\n#endif\";\n\nvar lights_lambert_fragment = \"LambertMaterial material;\\nmaterial.diffuseColor = diffuseColor.rgb;\\nmaterial.specularStrength = specularStrength;\";\n\nvar lights_lambert_pars_fragment = \"varying vec3 vViewPosition;\\nstruct LambertMaterial {\\n\\tvec3 diffuseColor;\\n\\tfloat specularStrength;\\n};\\nvoid RE_Direct_Lambert( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) {\\n\\tfloat dotNL = saturate( dot( geometryNormal, directLight.direction ) );\\n\\tvec3 irradiance = dotNL * directLight.color;\\n\\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\\n}\\nvoid RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) {\\n\\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\\n}\\n#define RE_Direct\\t\\t\\t\\tRE_Direct_Lambert\\n#define RE_IndirectDiffuse\\t\\tRE_IndirectDiffuse_Lambert\";\n\nvar lights_pars_begin = \"uniform bool receiveShadow;\\nuniform vec3 ambientLightColor;\\n#if defined( USE_LIGHT_PROBES )\\n\\tuniform vec3 lightProbe[ 9 ];\\n#endif\\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\\n\\tfloat x = normal.x, y = normal.y, z = normal.z;\\n\\tvec3 result = shCoefficients[ 0 ] * 0.886227;\\n\\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\\n\\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\\n\\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\\n\\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\\n\\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\\n\\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\\n\\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\\n\\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\\n\\treturn result;\\n}\\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) {\\n\\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\\n\\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\\n\\treturn irradiance;\\n}\\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\\n\\tvec3 irradiance = ambientLightColor;\\n\\treturn irradiance;\\n}\\nfloat getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\\n\\t#if defined ( LEGACY_LIGHTS )\\n\\t\\tif ( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\\n\\t\\t\\treturn pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent );\\n\\t\\t}\\n\\t\\treturn 1.0;\\n\\t#else\\n\\t\\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\\n\\t\\tif ( cutoffDistance > 0.0 ) {\\n\\t\\t\\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\\n\\t\\t}\\n\\t\\treturn distanceFalloff;\\n\\t#endif\\n}\\nfloat getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {\\n\\treturn smoothstep( coneCosine, penumbraCosine, angleCosine );\\n}\\n#if NUM_DIR_LIGHTS > 0\\n\\tstruct DirectionalLight {\\n\\t\\tvec3 direction;\\n\\t\\tvec3 color;\\n\\t};\\n\\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\\n\\tvoid getDirectionalLightInfo( const in DirectionalLight directionalLight, out IncidentLight light ) {\\n\\t\\tlight.color = directionalLight.color;\\n\\t\\tlight.direction = directionalLight.direction;\\n\\t\\tlight.visible = true;\\n\\t}\\n#endif\\n#if NUM_POINT_LIGHTS > 0\\n\\tstruct PointLight {\\n\\t\\tvec3 position;\\n\\t\\tvec3 color;\\n\\t\\tfloat distance;\\n\\t\\tfloat decay;\\n\\t};\\n\\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\\n\\tvoid getPointLightInfo( const in PointLight pointLight, const in vec3 geometryPosition, out IncidentLight light ) {\\n\\t\\tvec3 lVector = pointLight.position - geometryPosition;\\n\\t\\tlight.direction = normalize( lVector );\\n\\t\\tfloat lightDistance = length( lVector );\\n\\t\\tlight.color = pointLight.color;\\n\\t\\tlight.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay );\\n\\t\\tlight.visible = ( light.color != vec3( 0.0 ) );\\n\\t}\\n#endif\\n#if NUM_SPOT_LIGHTS > 0\\n\\tstruct SpotLight {\\n\\t\\tvec3 position;\\n\\t\\tvec3 direction;\\n\\t\\tvec3 color;\\n\\t\\tfloat distance;\\n\\t\\tfloat decay;\\n\\t\\tfloat coneCos;\\n\\t\\tfloat penumbraCos;\\n\\t};\\n\\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\\n\\tvoid getSpotLightInfo( const in SpotLight spotLight, const in vec3 geometryPosition, out IncidentLight light ) {\\n\\t\\tvec3 lVector = spotLight.position - geometryPosition;\\n\\t\\tlight.direction = normalize( lVector );\\n\\t\\tfloat angleCos = dot( light.direction, spotLight.direction );\\n\\t\\tfloat spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos );\\n\\t\\tif ( spotAttenuation > 0.0 ) {\\n\\t\\t\\tfloat lightDistance = length( lVector );\\n\\t\\t\\tlight.color = spotLight.color * spotAttenuation;\\n\\t\\t\\tlight.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay );\\n\\t\\t\\tlight.visible = ( light.color != vec3( 0.0 ) );\\n\\t\\t} else {\\n\\t\\t\\tlight.color = vec3( 0.0 );\\n\\t\\t\\tlight.visible = false;\\n\\t\\t}\\n\\t}\\n#endif\\n#if NUM_RECT_AREA_LIGHTS > 0\\n\\tstruct RectAreaLight {\\n\\t\\tvec3 color;\\n\\t\\tvec3 position;\\n\\t\\tvec3 halfWidth;\\n\\t\\tvec3 halfHeight;\\n\\t};\\n\\tuniform sampler2D ltc_1;\\tuniform sampler2D ltc_2;\\n\\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\\n#endif\\n#if NUM_HEMI_LIGHTS > 0\\n\\tstruct HemisphereLight {\\n\\t\\tvec3 direction;\\n\\t\\tvec3 skyColor;\\n\\t\\tvec3 groundColor;\\n\\t};\\n\\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\\n\\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) {\\n\\t\\tfloat dotNL = dot( normal, hemiLight.direction );\\n\\t\\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\\n\\t\\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\\n\\t\\treturn irradiance;\\n\\t}\\n#endif\";\n\nvar envmap_physical_pars_fragment = \"#ifdef USE_ENVMAP\\n\\tvec3 getIBLIrradiance( const in vec3 normal ) {\\n\\t\\t#ifdef ENVMAP_TYPE_CUBE_UV\\n\\t\\t\\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\\n\\t\\t\\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\\n\\t\\t\\treturn PI * envMapColor.rgb * envMapIntensity;\\n\\t\\t#else\\n\\t\\t\\treturn vec3( 0.0 );\\n\\t\\t#endif\\n\\t}\\n\\tvec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) {\\n\\t\\t#ifdef ENVMAP_TYPE_CUBE_UV\\n\\t\\t\\tvec3 reflectVec = reflect( - viewDir, normal );\\n\\t\\t\\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\\n\\t\\t\\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\\n\\t\\t\\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\\n\\t\\t\\treturn envMapColor.rgb * envMapIntensity;\\n\\t\\t#else\\n\\t\\t\\treturn vec3( 0.0 );\\n\\t\\t#endif\\n\\t}\\n\\t#ifdef USE_ANISOTROPY\\n\\t\\tvec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) {\\n\\t\\t\\t#ifdef ENVMAP_TYPE_CUBE_UV\\n\\t\\t\\t\\tvec3 bentNormal = cross( bitangent, viewDir );\\n\\t\\t\\t\\tbentNormal = normalize( cross( bentNormal, bitangent ) );\\n\\t\\t\\t\\tbentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) );\\n\\t\\t\\t\\treturn getIBLRadiance( viewDir, bentNormal, roughness );\\n\\t\\t\\t#else\\n\\t\\t\\t\\treturn vec3( 0.0 );\\n\\t\\t\\t#endif\\n\\t\\t}\\n\\t#endif\\n#endif\";\n\nvar lights_toon_fragment = \"ToonMaterial material;\\nmaterial.diffuseColor = diffuseColor.rgb;\";\n\nvar lights_toon_pars_fragment = \"varying vec3 vViewPosition;\\nstruct ToonMaterial {\\n\\tvec3 diffuseColor;\\n};\\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\\n\\tvec3 irradiance = getGradientIrradiance( geometryNormal, directLight.direction ) * directLight.color;\\n\\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\\n}\\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\\n\\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\\n}\\n#define RE_Direct\\t\\t\\t\\tRE_Direct_Toon\\n#define RE_IndirectDiffuse\\t\\tRE_IndirectDiffuse_Toon\";\n\nvar lights_phong_fragment = \"BlinnPhongMaterial material;\\nmaterial.diffuseColor = diffuseColor.rgb;\\nmaterial.specularColor = specular;\\nmaterial.specularShininess = shininess;\\nmaterial.specularStrength = specularStrength;\";\n\nvar lights_phong_pars_fragment = \"varying vec3 vViewPosition;\\nstruct BlinnPhongMaterial {\\n\\tvec3 diffuseColor;\\n\\tvec3 specularColor;\\n\\tfloat specularShininess;\\n\\tfloat specularStrength;\\n};\\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\\n\\tfloat dotNL = saturate( dot( geometryNormal, directLight.direction ) );\\n\\tvec3 irradiance = dotNL * directLight.color;\\n\\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\\n\\treflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometryViewDir, geometryNormal, material.specularColor, material.specularShininess ) * material.specularStrength;\\n}\\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\\n\\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\\n}\\n#define RE_Direct\\t\\t\\t\\tRE_Direct_BlinnPhong\\n#define RE_IndirectDiffuse\\t\\tRE_IndirectDiffuse_BlinnPhong\";\n\nvar lights_physical_fragment = \"PhysicalMaterial material;\\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\\nvec3 dxy = max( abs( dFdx( nonPerturbedNormal ) ), abs( dFdy( nonPerturbedNormal ) ) );\\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\\nmaterial.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness;\\nmaterial.roughness = min( material.roughness, 1.0 );\\n#ifdef IOR\\n\\tmaterial.ior = ior;\\n\\t#ifdef USE_SPECULAR\\n\\t\\tfloat specularIntensityFactor = specularIntensity;\\n\\t\\tvec3 specularColorFactor = specularColor;\\n\\t\\t#ifdef USE_SPECULAR_COLORMAP\\n\\t\\t\\tspecularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb;\\n\\t\\t#endif\\n\\t\\t#ifdef USE_SPECULAR_INTENSITYMAP\\n\\t\\t\\tspecularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a;\\n\\t\\t#endif\\n\\t\\tmaterial.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor );\\n\\t#else\\n\\t\\tfloat specularIntensityFactor = 1.0;\\n\\t\\tvec3 specularColorFactor = vec3( 1.0 );\\n\\t\\tmaterial.specularF90 = 1.0;\\n\\t#endif\\n\\tmaterial.specularColor = mix( min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor );\\n#else\\n\\tmaterial.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );\\n\\tmaterial.specularF90 = 1.0;\\n#endif\\n#ifdef USE_CLEARCOAT\\n\\tmaterial.clearcoat = clearcoat;\\n\\tmaterial.clearcoatRoughness = clearcoatRoughness;\\n\\tmaterial.clearcoatF0 = vec3( 0.04 );\\n\\tmaterial.clearcoatF90 = 1.0;\\n\\t#ifdef USE_CLEARCOATMAP\\n\\t\\tmaterial.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x;\\n\\t#endif\\n\\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\\n\\t\\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y;\\n\\t#endif\\n\\tmaterial.clearcoat = saturate( material.clearcoat );\\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\\n\\tmaterial.clearcoatRoughness += geometryRoughness;\\n\\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\\n#endif\\n#ifdef USE_IRIDESCENCE\\n\\tmaterial.iridescence = iridescence;\\n\\tmaterial.iridescenceIOR = iridescenceIOR;\\n\\t#ifdef USE_IRIDESCENCEMAP\\n\\t\\tmaterial.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r;\\n\\t#endif\\n\\t#ifdef USE_IRIDESCENCE_THICKNESSMAP\\n\\t\\tmaterial.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum;\\n\\t#else\\n\\t\\tmaterial.iridescenceThickness = iridescenceThicknessMaximum;\\n\\t#endif\\n#endif\\n#ifdef USE_SHEEN\\n\\tmaterial.sheenColor = sheenColor;\\n\\t#ifdef USE_SHEEN_COLORMAP\\n\\t\\tmaterial.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb;\\n\\t#endif\\n\\tmaterial.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 );\\n\\t#ifdef USE_SHEEN_ROUGHNESSMAP\\n\\t\\tmaterial.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a;\\n\\t#endif\\n#endif\\n#ifdef USE_ANISOTROPY\\n\\t#ifdef USE_ANISOTROPYMAP\\n\\t\\tmat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x );\\n\\t\\tvec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb;\\n\\t\\tvec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b;\\n\\t#else\\n\\t\\tvec2 anisotropyV = anisotropyVector;\\n\\t#endif\\n\\tmaterial.anisotropy = length( anisotropyV );\\n\\tif( material.anisotropy == 0.0 ) {\\n\\t\\tanisotropyV = vec2( 1.0, 0.0 );\\n\\t} else {\\n\\t\\tanisotropyV /= material.anisotropy;\\n\\t\\tmaterial.anisotropy = saturate( material.anisotropy );\\n\\t}\\n\\tmaterial.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) );\\n\\tmaterial.anisotropyT = tbn[ 0 ] * anisotropyV.x + tbn[ 1 ] * anisotropyV.y;\\n\\tmaterial.anisotropyB = tbn[ 1 ] * anisotropyV.x - tbn[ 0 ] * anisotropyV.y;\\n#endif\";\n\nvar lights_physical_pars_fragment = \"struct PhysicalMaterial {\\n\\tvec3 diffuseColor;\\n\\tfloat roughness;\\n\\tvec3 specularColor;\\n\\tfloat specularF90;\\n\\t#ifdef USE_CLEARCOAT\\n\\t\\tfloat clearcoat;\\n\\t\\tfloat clearcoatRoughness;\\n\\t\\tvec3 clearcoatF0;\\n\\t\\tfloat clearcoatF90;\\n\\t#endif\\n\\t#ifdef USE_IRIDESCENCE\\n\\t\\tfloat iridescence;\\n\\t\\tfloat iridescenceIOR;\\n\\t\\tfloat iridescenceThickness;\\n\\t\\tvec3 iridescenceFresnel;\\n\\t\\tvec3 iridescenceF0;\\n\\t#endif\\n\\t#ifdef USE_SHEEN\\n\\t\\tvec3 sheenColor;\\n\\t\\tfloat sheenRoughness;\\n\\t#endif\\n\\t#ifdef IOR\\n\\t\\tfloat ior;\\n\\t#endif\\n\\t#ifdef USE_TRANSMISSION\\n\\t\\tfloat transmission;\\n\\t\\tfloat transmissionAlpha;\\n\\t\\tfloat thickness;\\n\\t\\tfloat attenuationDistance;\\n\\t\\tvec3 attenuationColor;\\n\\t#endif\\n\\t#ifdef USE_ANISOTROPY\\n\\t\\tfloat anisotropy;\\n\\t\\tfloat alphaT;\\n\\t\\tvec3 anisotropyT;\\n\\t\\tvec3 anisotropyB;\\n\\t#endif\\n};\\nvec3 clearcoatSpecularDirect = vec3( 0.0 );\\nvec3 clearcoatSpecularIndirect = vec3( 0.0 );\\nvec3 sheenSpecularDirect = vec3( 0.0 );\\nvec3 sheenSpecularIndirect = vec3(0.0 );\\nvec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) {\\n    float x = clamp( 1.0 - dotVH, 0.0, 1.0 );\\n    float x2 = x * x;\\n    float x5 = clamp( x * x2 * x2, 0.0, 0.9999 );\\n    return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 );\\n}\\nfloat V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\\n\\tfloat a2 = pow2( alpha );\\n\\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\\n\\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\\n\\treturn 0.5 / max( gv + gl, EPSILON );\\n}\\nfloat D_GGX( const in float alpha, const in float dotNH ) {\\n\\tfloat a2 = pow2( alpha );\\n\\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\\n\\treturn RECIPROCAL_PI * a2 / pow2( denom );\\n}\\n#ifdef USE_ANISOTROPY\\n\\tfloat V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) {\\n\\t\\tfloat gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) );\\n\\t\\tfloat gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) );\\n\\t\\tfloat v = 0.5 / ( gv + gl );\\n\\t\\treturn saturate(v);\\n\\t}\\n\\tfloat D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) {\\n\\t\\tfloat a2 = alphaT * alphaB;\\n\\t\\thighp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH );\\n\\t\\thighp float v2 = dot( v, v );\\n\\t\\tfloat w2 = a2 / v2;\\n\\t\\treturn RECIPROCAL_PI * a2 * pow2 ( w2 );\\n\\t}\\n#endif\\n#ifdef USE_CLEARCOAT\\n\\tvec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) {\\n\\t\\tvec3 f0 = material.clearcoatF0;\\n\\t\\tfloat f90 = material.clearcoatF90;\\n\\t\\tfloat roughness = material.clearcoatRoughness;\\n\\t\\tfloat alpha = pow2( roughness );\\n\\t\\tvec3 halfDir = normalize( lightDir + viewDir );\\n\\t\\tfloat dotNL = saturate( dot( normal, lightDir ) );\\n\\t\\tfloat dotNV = saturate( dot( normal, viewDir ) );\\n\\t\\tfloat dotNH = saturate( dot( normal, halfDir ) );\\n\\t\\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\\n\\t\\tvec3 F = F_Schlick( f0, f90, dotVH );\\n\\t\\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\\n\\t\\tfloat D = D_GGX( alpha, dotNH );\\n\\t\\treturn F * ( V * D );\\n\\t}\\n#endif\\nvec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) {\\n\\tvec3 f0 = material.specularColor;\\n\\tfloat f90 = material.specularF90;\\n\\tfloat roughness = material.roughness;\\n\\tfloat alpha = pow2( roughness );\\n\\tvec3 halfDir = normalize( lightDir + viewDir );\\n\\tfloat dotNL = saturate( dot( normal, lightDir ) );\\n\\tfloat dotNV = saturate( dot( normal, viewDir ) );\\n\\tfloat dotNH = saturate( dot( normal, halfDir ) );\\n\\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\\n\\tvec3 F = F_Schlick( f0, f90, dotVH );\\n\\t#ifdef USE_IRIDESCENCE\\n\\t\\tF = mix( F, material.iridescenceFresnel, material.iridescence );\\n\\t#endif\\n\\t#ifdef USE_ANISOTROPY\\n\\t\\tfloat dotTL = dot( material.anisotropyT, lightDir );\\n\\t\\tfloat dotTV = dot( material.anisotropyT, viewDir );\\n\\t\\tfloat dotTH = dot( material.anisotropyT, halfDir );\\n\\t\\tfloat dotBL = dot( material.anisotropyB, lightDir );\\n\\t\\tfloat dotBV = dot( material.anisotropyB, viewDir );\\n\\t\\tfloat dotBH = dot( material.anisotropyB, halfDir );\\n\\t\\tfloat V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL );\\n\\t\\tfloat D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH );\\n\\t#else\\n\\t\\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\\n\\t\\tfloat D = D_GGX( alpha, dotNH );\\n\\t#endif\\n\\treturn F * ( V * D );\\n}\\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\\n\\tconst float LUT_SIZE = 64.0;\\n\\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\\n\\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\\n\\tfloat dotNV = saturate( dot( N, V ) );\\n\\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\\n\\tuv = uv * LUT_SCALE + LUT_BIAS;\\n\\treturn uv;\\n}\\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\\n\\tfloat l = length( f );\\n\\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\\n}\\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\\n\\tfloat x = dot( v1, v2 );\\n\\tfloat y = abs( x );\\n\\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\\n\\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\\n\\tfloat v = a / b;\\n\\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\\n\\treturn cross( v1, v2 ) * theta_sintheta;\\n}\\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\\n\\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\\n\\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\\n\\tvec3 lightNormal = cross( v1, v2 );\\n\\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\\n\\tvec3 T1, T2;\\n\\tT1 = normalize( V - N * dot( V, N ) );\\n\\tT2 = - cross( N, T1 );\\n\\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\\n\\tvec3 coords[ 4 ];\\n\\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\\n\\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\\n\\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\\n\\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\\n\\tcoords[ 0 ] = normalize( coords[ 0 ] );\\n\\tcoords[ 1 ] = normalize( coords[ 1 ] );\\n\\tcoords[ 2 ] = normalize( coords[ 2 ] );\\n\\tcoords[ 3 ] = normalize( coords[ 3 ] );\\n\\tvec3 vectorFormFactor = vec3( 0.0 );\\n\\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\\n\\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\\n\\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\\n\\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\\n\\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\\n\\treturn vec3( result );\\n}\\n#if defined( USE_SHEEN )\\nfloat D_Charlie( float roughness, float dotNH ) {\\n\\tfloat alpha = pow2( roughness );\\n\\tfloat invAlpha = 1.0 / alpha;\\n\\tfloat cos2h = dotNH * dotNH;\\n\\tfloat sin2h = max( 1.0 - cos2h, 0.0078125 );\\n\\treturn ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI );\\n}\\nfloat V_Neubelt( float dotNV, float dotNL ) {\\n\\treturn saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) );\\n}\\nvec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) {\\n\\tvec3 halfDir = normalize( lightDir + viewDir );\\n\\tfloat dotNL = saturate( dot( normal, lightDir ) );\\n\\tfloat dotNV = saturate( dot( normal, viewDir ) );\\n\\tfloat dotNH = saturate( dot( normal, halfDir ) );\\n\\tfloat D = D_Charlie( sheenRoughness, dotNH );\\n\\tfloat V = V_Neubelt( dotNV, dotNL );\\n\\treturn sheenColor * ( D * V );\\n}\\n#endif\\nfloat IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\\n\\tfloat dotNV = saturate( dot( normal, viewDir ) );\\n\\tfloat r2 = roughness * roughness;\\n\\tfloat a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95;\\n\\tfloat b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72;\\n\\tfloat DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) );\\n\\treturn saturate( DG * RECIPROCAL_PI );\\n}\\nvec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\\n\\tfloat dotNV = saturate( dot( normal, viewDir ) );\\n\\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\\n\\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\\n\\tvec4 r = roughness * c0 + c1;\\n\\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\\n\\tvec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw;\\n\\treturn fab;\\n}\\nvec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) {\\n\\tvec2 fab = DFGApprox( normal, viewDir, roughness );\\n\\treturn specularColor * fab.x + specularF90 * fab.y;\\n}\\n#ifdef USE_IRIDESCENCE\\nvoid computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\\n#else\\nvoid computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\\n#endif\\n\\tvec2 fab = DFGApprox( normal, viewDir, roughness );\\n\\t#ifdef USE_IRIDESCENCE\\n\\t\\tvec3 Fr = mix( specularColor, iridescenceF0, iridescence );\\n\\t#else\\n\\t\\tvec3 Fr = specularColor;\\n\\t#endif\\n\\tvec3 FssEss = Fr * fab.x + specularF90 * fab.y;\\n\\tfloat Ess = fab.x + fab.y;\\n\\tfloat Ems = 1.0 - Ess;\\n\\tvec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619;\\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\\n\\tsingleScatter += FssEss;\\n\\tmultiScatter += Fms * Ems;\\n}\\n#if NUM_RECT_AREA_LIGHTS > 0\\n\\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\t\\tvec3 normal = geometryNormal;\\n\\t\\tvec3 viewDir = geometryViewDir;\\n\\t\\tvec3 position = geometryPosition;\\n\\t\\tvec3 lightPos = rectAreaLight.position;\\n\\t\\tvec3 halfWidth = rectAreaLight.halfWidth;\\n\\t\\tvec3 halfHeight = rectAreaLight.halfHeight;\\n\\t\\tvec3 lightColor = rectAreaLight.color;\\n\\t\\tfloat roughness = material.roughness;\\n\\t\\tvec3 rectCoords[ 4 ];\\n\\t\\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\\t\\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\\n\\t\\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\\n\\t\\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\\n\\t\\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\\n\\t\\tvec4 t1 = texture2D( ltc_1, uv );\\n\\t\\tvec4 t2 = texture2D( ltc_2, uv );\\n\\t\\tmat3 mInv = mat3(\\n\\t\\t\\tvec3( t1.x, 0, t1.y ),\\n\\t\\t\\tvec3(    0, 1,    0 ),\\n\\t\\t\\tvec3( t1.z, 0, t1.w )\\n\\t\\t);\\n\\t\\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\\n\\t\\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\\n\\t\\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\\n\\t}\\n#endif\\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\tfloat dotNL = saturate( dot( geometryNormal, directLight.direction ) );\\n\\tvec3 irradiance = dotNL * directLight.color;\\n\\t#ifdef USE_CLEARCOAT\\n\\t\\tfloat dotNLcc = saturate( dot( geometryClearcoatNormal, directLight.direction ) );\\n\\t\\tvec3 ccIrradiance = dotNLcc * directLight.color;\\n\\t\\tclearcoatSpecularDirect += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometryViewDir, geometryClearcoatNormal, material );\\n\\t#endif\\n\\t#ifdef USE_SHEEN\\n\\t\\tsheenSpecularDirect += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness );\\n\\t#endif\\n\\treflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometryViewDir, geometryNormal, material );\\n\\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\\n}\\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\\n}\\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\\n\\t#ifdef USE_CLEARCOAT\\n\\t\\tclearcoatSpecularIndirect += clearcoatRadiance * EnvironmentBRDF( geometryClearcoatNormal, geometryViewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\\n\\t#endif\\n\\t#ifdef USE_SHEEN\\n\\t\\tsheenSpecularIndirect += irradiance * material.sheenColor * IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness );\\n\\t#endif\\n\\tvec3 singleScattering = vec3( 0.0 );\\n\\tvec3 multiScattering = vec3( 0.0 );\\n\\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\\n\\t#ifdef USE_IRIDESCENCE\\n\\t\\tcomputeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering );\\n\\t#else\\n\\t\\tcomputeMultiscattering( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering );\\n\\t#endif\\n\\tvec3 totalScattering = singleScattering + multiScattering;\\n\\tvec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) );\\n\\treflectedLight.indirectSpecular += radiance * singleScattering;\\n\\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\\n\\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\\n}\\n#define RE_Direct\\t\\t\\t\\tRE_Direct_Physical\\n#define RE_Direct_RectArea\\t\\tRE_Direct_RectArea_Physical\\n#define RE_IndirectDiffuse\\t\\tRE_IndirectDiffuse_Physical\\n#define RE_IndirectSpecular\\t\\tRE_IndirectSpecular_Physical\\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\\n\\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\\n}\";\n\nvar lights_fragment_begin = \"\\nvec3 geometryPosition = - vViewPosition;\\nvec3 geometryNormal = normal;\\nvec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\\nvec3 geometryClearcoatNormal = vec3( 0.0 );\\n#ifdef USE_CLEARCOAT\\n\\tgeometryClearcoatNormal = clearcoatNormal;\\n#endif\\n#ifdef USE_IRIDESCENCE\\n\\tfloat dotNVi = saturate( dot( normal, geometryViewDir ) );\\n\\tif ( material.iridescenceThickness == 0.0 ) {\\n\\t\\tmaterial.iridescence = 0.0;\\n\\t} else {\\n\\t\\tmaterial.iridescence = saturate( material.iridescence );\\n\\t}\\n\\tif ( material.iridescence > 0.0 ) {\\n\\t\\tmaterial.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor );\\n\\t\\tmaterial.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi );\\n\\t}\\n#endif\\nIncidentLight directLight;\\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\\n\\tPointLight pointLight;\\n\\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\\n\\tPointLightShadow pointLightShadow;\\n\\t#endif\\n\\t#pragma unroll_loop_start\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tpointLight = pointLights[ i ];\\n\\t\\tgetPointLightInfo( pointLight, geometryPosition, directLight );\\n\\t\\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\\n\\t\\tpointLightShadow = pointLightShadows[ i ];\\n\\t\\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\\n\\t\\t#endif\\n\\t\\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\\n\\t}\\n\\t#pragma unroll_loop_end\\n#endif\\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\\n\\tSpotLight spotLight;\\n\\tvec4 spotColor;\\n\\tvec3 spotLightCoord;\\n\\tbool inSpotLightMap;\\n\\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\\n\\tSpotLightShadow spotLightShadow;\\n\\t#endif\\n\\t#pragma unroll_loop_start\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tspotLight = spotLights[ i ];\\n\\t\\tgetSpotLightInfo( spotLight, geometryPosition, directLight );\\n\\t\\t#if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )\\n\\t\\t#define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX\\n\\t\\t#elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\\n\\t\\t#define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS\\n\\t\\t#else\\n\\t\\t#define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )\\n\\t\\t#endif\\n\\t\\t#if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS )\\n\\t\\t\\tspotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w;\\n\\t\\t\\tinSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) );\\n\\t\\t\\tspotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy );\\n\\t\\t\\tdirectLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color;\\n\\t\\t#endif\\n\\t\\t#undef SPOT_LIGHT_MAP_INDEX\\n\\t\\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\\n\\t\\tspotLightShadow = spotLightShadows[ i ];\\n\\t\\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;\\n\\t\\t#endif\\n\\t\\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\\n\\t}\\n\\t#pragma unroll_loop_end\\n#endif\\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\\n\\tDirectionalLight directionalLight;\\n\\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\\n\\tDirectionalLightShadow directionalLightShadow;\\n\\t#endif\\n\\t#pragma unroll_loop_start\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tdirectionalLight = directionalLights[ i ];\\n\\t\\tgetDirectionalLightInfo( directionalLight, directLight );\\n\\t\\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\\n\\t\\tdirectionalLightShadow = directionalLightShadows[ i ];\\n\\t\\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\\n\\t\\t#endif\\n\\t\\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\\n\\t}\\n\\t#pragma unroll_loop_end\\n#endif\\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\\n\\tRectAreaLight rectAreaLight;\\n\\t#pragma unroll_loop_start\\n\\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\\n\\t\\trectAreaLight = rectAreaLights[ i ];\\n\\t\\tRE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\\n\\t}\\n\\t#pragma unroll_loop_end\\n#endif\\n#if defined( RE_IndirectDiffuse )\\n\\tvec3 iblIrradiance = vec3( 0.0 );\\n\\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\\n\\t#if defined( USE_LIGHT_PROBES )\\n\\t\\tirradiance += getLightProbeIrradiance( lightProbe, geometryNormal );\\n\\t#endif\\n\\t#if ( NUM_HEMI_LIGHTS > 0 )\\n\\t\\t#pragma unroll_loop_start\\n\\t\\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\\n\\t\\t\\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal );\\n\\t\\t}\\n\\t\\t#pragma unroll_loop_end\\n\\t#endif\\n#endif\\n#if defined( RE_IndirectSpecular )\\n\\tvec3 radiance = vec3( 0.0 );\\n\\tvec3 clearcoatRadiance = vec3( 0.0 );\\n#endif\";\n\nvar lights_fragment_maps = \"#if defined( RE_IndirectDiffuse )\\n\\t#ifdef USE_LIGHTMAP\\n\\t\\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\\n\\t\\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\\n\\t\\tirradiance += lightMapIrradiance;\\n\\t#endif\\n\\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\\n\\t\\tiblIrradiance += getIBLIrradiance( geometryNormal );\\n\\t#endif\\n#endif\\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\\n\\t#ifdef USE_ANISOTROPY\\n\\t\\tradiance += getIBLAnisotropyRadiance( geometryViewDir, geometryNormal, material.roughness, material.anisotropyB, material.anisotropy );\\n\\t#else\\n\\t\\tradiance += getIBLRadiance( geometryViewDir, geometryNormal, material.roughness );\\n\\t#endif\\n\\t#ifdef USE_CLEARCOAT\\n\\t\\tclearcoatRadiance += getIBLRadiance( geometryViewDir, geometryClearcoatNormal, material.clearcoatRoughness );\\n\\t#endif\\n#endif\";\n\nvar lights_fragment_end = \"#if defined( RE_IndirectDiffuse )\\n\\tRE_IndirectDiffuse( irradiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\\n#endif\\n#if defined( RE_IndirectSpecular )\\n\\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\\n#endif\";\n\nvar logdepthbuf_fragment = \"#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\\n\\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\\n#endif\";\n\nvar logdepthbuf_pars_fragment = \"#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\\n\\tuniform float logDepthBufFC;\\n\\tvarying float vFragDepth;\\n\\tvarying float vIsPerspective;\\n#endif\";\n\nvar logdepthbuf_pars_vertex = \"#ifdef USE_LOGDEPTHBUF\\n\\t#ifdef USE_LOGDEPTHBUF_EXT\\n\\t\\tvarying float vFragDepth;\\n\\t\\tvarying float vIsPerspective;\\n\\t#else\\n\\t\\tuniform float logDepthBufFC;\\n\\t#endif\\n#endif\";\n\nvar logdepthbuf_vertex = \"#ifdef USE_LOGDEPTHBUF\\n\\t#ifdef USE_LOGDEPTHBUF_EXT\\n\\t\\tvFragDepth = 1.0 + gl_Position.w;\\n\\t\\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\\n\\t#else\\n\\t\\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\\n\\t\\t\\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\\n\\t\\t\\tgl_Position.z *= gl_Position.w;\\n\\t\\t}\\n\\t#endif\\n#endif\";\n\nvar map_fragment = \"#ifdef USE_MAP\\n\\tvec4 sampledDiffuseColor = texture2D( map, vMapUv );\\n\\t#ifdef DECODE_VIDEO_TEXTURE\\n\\t\\tsampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w );\\n\\t\\n\\t#endif\\n\\tdiffuseColor *= sampledDiffuseColor;\\n#endif\";\n\nvar map_pars_fragment = \"#ifdef USE_MAP\\n\\tuniform sampler2D map;\\n#endif\";\n\nvar map_particle_fragment = \"#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\\n\\t#if defined( USE_POINTS_UV )\\n\\t\\tvec2 uv = vUv;\\n\\t#else\\n\\t\\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\\n\\t#endif\\n#endif\\n#ifdef USE_MAP\\n\\tdiffuseColor *= texture2D( map, uv );\\n#endif\\n#ifdef USE_ALPHAMAP\\n\\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\\n#endif\";\n\nvar map_particle_pars_fragment = \"#if defined( USE_POINTS_UV )\\n\\tvarying vec2 vUv;\\n#else\\n\\t#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\\n\\t\\tuniform mat3 uvTransform;\\n\\t#endif\\n#endif\\n#ifdef USE_MAP\\n\\tuniform sampler2D map;\\n#endif\\n#ifdef USE_ALPHAMAP\\n\\tuniform sampler2D alphaMap;\\n#endif\";\n\nvar metalnessmap_fragment = \"float metalnessFactor = metalness;\\n#ifdef USE_METALNESSMAP\\n\\tvec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv );\\n\\tmetalnessFactor *= texelMetalness.b;\\n#endif\";\n\nvar metalnessmap_pars_fragment = \"#ifdef USE_METALNESSMAP\\n\\tuniform sampler2D metalnessMap;\\n#endif\";\n\nvar morphcolor_vertex = \"#if defined( USE_MORPHCOLORS ) && defined( MORPHTARGETS_TEXTURE )\\n\\tvColor *= morphTargetBaseInfluence;\\n\\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\\n\\t\\t#if defined( USE_COLOR_ALPHA )\\n\\t\\t\\tif ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ];\\n\\t\\t#elif defined( USE_COLOR )\\n\\t\\t\\tif ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ];\\n\\t\\t#endif\\n\\t}\\n#endif\";\n\nvar morphnormal_vertex = \"#ifdef USE_MORPHNORMALS\\n\\tobjectNormal *= morphTargetBaseInfluence;\\n\\t#ifdef MORPHTARGETS_TEXTURE\\n\\t\\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\\n\\t\\t\\tif ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ];\\n\\t\\t}\\n\\t#else\\n\\t\\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\\n\\t\\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\\n\\t\\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\\n\\t\\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\\n\\t#endif\\n#endif\";\n\nvar morphtarget_pars_vertex = \"#ifdef USE_MORPHTARGETS\\n\\tuniform float morphTargetBaseInfluence;\\n\\t#ifdef MORPHTARGETS_TEXTURE\\n\\t\\tuniform float morphTargetInfluences[ MORPHTARGETS_COUNT ];\\n\\t\\tuniform sampler2DArray morphTargetsTexture;\\n\\t\\tuniform ivec2 morphTargetsTextureSize;\\n\\t\\tvec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) {\\n\\t\\t\\tint texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset;\\n\\t\\t\\tint y = texelIndex / morphTargetsTextureSize.x;\\n\\t\\t\\tint x = texelIndex - y * morphTargetsTextureSize.x;\\n\\t\\t\\tivec3 morphUV = ivec3( x, y, morphTargetIndex );\\n\\t\\t\\treturn texelFetch( morphTargetsTexture, morphUV, 0 );\\n\\t\\t}\\n\\t#else\\n\\t\\t#ifndef USE_MORPHNORMALS\\n\\t\\t\\tuniform float morphTargetInfluences[ 8 ];\\n\\t\\t#else\\n\\t\\t\\tuniform float morphTargetInfluences[ 4 ];\\n\\t\\t#endif\\n\\t#endif\\n#endif\";\n\nvar morphtarget_vertex = \"#ifdef USE_MORPHTARGETS\\n\\ttransformed *= morphTargetBaseInfluence;\\n\\t#ifdef MORPHTARGETS_TEXTURE\\n\\t\\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\\n\\t\\t\\tif ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ];\\n\\t\\t}\\n\\t#else\\n\\t\\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\\n\\t\\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\\n\\t\\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\\n\\t\\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\\n\\t\\t#ifndef USE_MORPHNORMALS\\n\\t\\t\\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\\n\\t\\t\\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\\n\\t\\t\\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\\n\\t\\t\\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\\n\\t\\t#endif\\n\\t#endif\\n#endif\";\n\nvar normal_fragment_begin = \"float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;\\n#ifdef FLAT_SHADED\\n\\tvec3 fdx = dFdx( vViewPosition );\\n\\tvec3 fdy = dFdy( vViewPosition );\\n\\tvec3 normal = normalize( cross( fdx, fdy ) );\\n#else\\n\\tvec3 normal = normalize( vNormal );\\n\\t#ifdef DOUBLE_SIDED\\n\\t\\tnormal *= faceDirection;\\n\\t#endif\\n#endif\\n#if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY )\\n\\t#ifdef USE_TANGENT\\n\\t\\tmat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal );\\n\\t#else\\n\\t\\tmat3 tbn = getTangentFrame( - vViewPosition, normal,\\n\\t\\t#if defined( USE_NORMALMAP )\\n\\t\\t\\tvNormalMapUv\\n\\t\\t#elif defined( USE_CLEARCOAT_NORMALMAP )\\n\\t\\t\\tvClearcoatNormalMapUv\\n\\t\\t#else\\n\\t\\t\\tvUv\\n\\t\\t#endif\\n\\t\\t);\\n\\t#endif\\n\\t#if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED )\\n\\t\\ttbn[0] *= faceDirection;\\n\\t\\ttbn[1] *= faceDirection;\\n\\t#endif\\n#endif\\n#ifdef USE_CLEARCOAT_NORMALMAP\\n\\t#ifdef USE_TANGENT\\n\\t\\tmat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal );\\n\\t#else\\n\\t\\tmat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv );\\n\\t#endif\\n\\t#if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED )\\n\\t\\ttbn2[0] *= faceDirection;\\n\\t\\ttbn2[1] *= faceDirection;\\n\\t#endif\\n#endif\\nvec3 nonPerturbedNormal = normal;\";\n\nvar normal_fragment_maps = \"#ifdef USE_NORMALMAP_OBJECTSPACE\\n\\tnormal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0;\\n\\t#ifdef FLIP_SIDED\\n\\t\\tnormal = - normal;\\n\\t#endif\\n\\t#ifdef DOUBLE_SIDED\\n\\t\\tnormal = normal * faceDirection;\\n\\t#endif\\n\\tnormal = normalize( normalMatrix * normal );\\n#elif defined( USE_NORMALMAP_TANGENTSPACE )\\n\\tvec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0;\\n\\tmapN.xy *= normalScale;\\n\\tnormal = normalize( tbn * mapN );\\n#elif defined( USE_BUMPMAP )\\n\\tnormal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection );\\n#endif\";\n\nvar normal_pars_fragment = \"#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n\\t#ifdef USE_TANGENT\\n\\t\\tvarying vec3 vTangent;\\n\\t\\tvarying vec3 vBitangent;\\n\\t#endif\\n#endif\";\n\nvar normal_pars_vertex = \"#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n\\t#ifdef USE_TANGENT\\n\\t\\tvarying vec3 vTangent;\\n\\t\\tvarying vec3 vBitangent;\\n\\t#endif\\n#endif\";\n\nvar normal_vertex = \"#ifndef FLAT_SHADED\\n\\tvNormal = normalize( transformedNormal );\\n\\t#ifdef USE_TANGENT\\n\\t\\tvTangent = normalize( transformedTangent );\\n\\t\\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\\n\\t#endif\\n#endif\";\n\nvar normalmap_pars_fragment = \"#ifdef USE_NORMALMAP\\n\\tuniform sampler2D normalMap;\\n\\tuniform vec2 normalScale;\\n#endif\\n#ifdef USE_NORMALMAP_OBJECTSPACE\\n\\tuniform mat3 normalMatrix;\\n#endif\\n#if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) )\\n\\tmat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) {\\n\\t\\tvec3 q0 = dFdx( eye_pos.xyz );\\n\\t\\tvec3 q1 = dFdy( eye_pos.xyz );\\n\\t\\tvec2 st0 = dFdx( uv.st );\\n\\t\\tvec2 st1 = dFdy( uv.st );\\n\\t\\tvec3 N = surf_norm;\\n\\t\\tvec3 q1perp = cross( q1, N );\\n\\t\\tvec3 q0perp = cross( N, q0 );\\n\\t\\tvec3 T = q1perp * st0.x + q0perp * st1.x;\\n\\t\\tvec3 B = q1perp * st0.y + q0perp * st1.y;\\n\\t\\tfloat det = max( dot( T, T ), dot( B, B ) );\\n\\t\\tfloat scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det );\\n\\t\\treturn mat3( T * scale, B * scale, N );\\n\\t}\\n#endif\";\n\nvar clearcoat_normal_fragment_begin = \"#ifdef USE_CLEARCOAT\\n\\tvec3 clearcoatNormal = nonPerturbedNormal;\\n#endif\";\n\nvar clearcoat_normal_fragment_maps = \"#ifdef USE_CLEARCOAT_NORMALMAP\\n\\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0;\\n\\tclearcoatMapN.xy *= clearcoatNormalScale;\\n\\tclearcoatNormal = normalize( tbn2 * clearcoatMapN );\\n#endif\";\n\nvar clearcoat_pars_fragment = \"#ifdef USE_CLEARCOATMAP\\n\\tuniform sampler2D clearcoatMap;\\n#endif\\n#ifdef USE_CLEARCOAT_NORMALMAP\\n\\tuniform sampler2D clearcoatNormalMap;\\n\\tuniform vec2 clearcoatNormalScale;\\n#endif\\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\\n\\tuniform sampler2D clearcoatRoughnessMap;\\n#endif\";\n\nvar iridescence_pars_fragment = \"#ifdef USE_IRIDESCENCEMAP\\n\\tuniform sampler2D iridescenceMap;\\n#endif\\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\\n\\tuniform sampler2D iridescenceThicknessMap;\\n#endif\";\n\nvar opaque_fragment = \"#ifdef OPAQUE\\ndiffuseColor.a = 1.0;\\n#endif\\n#ifdef USE_TRANSMISSION\\ndiffuseColor.a *= material.transmissionAlpha;\\n#endif\\ngl_FragColor = vec4( outgoingLight, diffuseColor.a );\";\n\nvar packing = \"vec3 packNormalToRGB( const in vec3 normal ) {\\n\\treturn normalize( normal ) * 0.5 + 0.5;\\n}\\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\\n\\treturn 2.0 * rgb.xyz - 1.0;\\n}\\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\\nconst float ShiftRight8 = 1. / 256.;\\nvec4 packDepthToRGBA( const in float v ) {\\n\\tvec4 r = vec4( fract( v * PackFactors ), v );\\n\\tr.yzw -= r.xyz * ShiftRight8;\\treturn r * PackUpscale;\\n}\\nfloat unpackRGBAToDepth( const in vec4 v ) {\\n\\treturn dot( v, UnpackFactors );\\n}\\nvec2 packDepthToRG( in highp float v ) {\\n\\treturn packDepthToRGBA( v ).yx;\\n}\\nfloat unpackRGToDepth( const in highp vec2 v ) {\\n\\treturn unpackRGBAToDepth( vec4( v.xy, 0.0, 0.0 ) );\\n}\\nvec4 pack2HalfToRGBA( vec2 v ) {\\n\\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) );\\n\\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w );\\n}\\nvec2 unpackRGBATo2Half( vec4 v ) {\\n\\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\\n}\\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\\n\\treturn ( viewZ + near ) / ( near - far );\\n}\\nfloat orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) {\\n\\treturn depth * ( near - far ) - near;\\n}\\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\\n\\treturn ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ );\\n}\\nfloat perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) {\\n\\treturn ( near * far ) / ( ( far - near ) * depth - far );\\n}\";\n\nvar premultiplied_alpha_fragment = \"#ifdef PREMULTIPLIED_ALPHA\\n\\tgl_FragColor.rgb *= gl_FragColor.a;\\n#endif\";\n\nvar project_vertex = \"vec4 mvPosition = vec4( transformed, 1.0 );\\n#ifdef USE_BATCHING\\n\\tmvPosition = batchingMatrix * mvPosition;\\n#endif\\n#ifdef USE_INSTANCING\\n\\tmvPosition = instanceMatrix * mvPosition;\\n#endif\\nmvPosition = modelViewMatrix * mvPosition;\\ngl_Position = projectionMatrix * mvPosition;\";\n\nvar dithering_fragment = \"#ifdef DITHERING\\n\\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\\n#endif\";\n\nvar dithering_pars_fragment = \"#ifdef DITHERING\\n\\tvec3 dithering( vec3 color ) {\\n\\t\\tfloat grid_position = rand( gl_FragCoord.xy );\\n\\t\\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\\n\\t\\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\\n\\t\\treturn color + dither_shift_RGB;\\n\\t}\\n#endif\";\n\nvar roughnessmap_fragment = \"float roughnessFactor = roughness;\\n#ifdef USE_ROUGHNESSMAP\\n\\tvec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv );\\n\\troughnessFactor *= texelRoughness.g;\\n#endif\";\n\nvar roughnessmap_pars_fragment = \"#ifdef USE_ROUGHNESSMAP\\n\\tuniform sampler2D roughnessMap;\\n#endif\";\n\nvar shadowmap_pars_fragment = \"#if NUM_SPOT_LIGHT_COORDS > 0\\n\\tvarying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ];\\n#endif\\n#if NUM_SPOT_LIGHT_MAPS > 0\\n\\tuniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ];\\n#endif\\n#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHT_SHADOWS > 0\\n\\t\\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\\n\\t\\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\\n\\t\\tstruct DirectionalLightShadow {\\n\\t\\t\\tfloat shadowBias;\\n\\t\\t\\tfloat shadowNormalBias;\\n\\t\\t\\tfloat shadowRadius;\\n\\t\\t\\tvec2 shadowMapSize;\\n\\t\\t};\\n\\t\\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHT_SHADOWS > 0\\n\\t\\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\\n\\t\\tstruct SpotLightShadow {\\n\\t\\t\\tfloat shadowBias;\\n\\t\\t\\tfloat shadowNormalBias;\\n\\t\\t\\tfloat shadowRadius;\\n\\t\\t\\tvec2 shadowMapSize;\\n\\t\\t};\\n\\t\\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\\n\\t#endif\\n\\t#if NUM_POINT_LIGHT_SHADOWS > 0\\n\\t\\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\\n\\t\\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\\n\\t\\tstruct PointLightShadow {\\n\\t\\t\\tfloat shadowBias;\\n\\t\\t\\tfloat shadowNormalBias;\\n\\t\\t\\tfloat shadowRadius;\\n\\t\\t\\tvec2 shadowMapSize;\\n\\t\\t\\tfloat shadowCameraNear;\\n\\t\\t\\tfloat shadowCameraFar;\\n\\t\\t};\\n\\t\\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\\n\\t#endif\\n\\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\\n\\t\\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\\n\\t}\\n\\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\\n\\t\\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\\n\\t}\\n\\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\\n\\t\\tfloat occlusion = 1.0;\\n\\t\\tvec2 distribution = texture2DDistribution( shadow, uv );\\n\\t\\tfloat hard_shadow = step( compare , distribution.x );\\n\\t\\tif (hard_shadow != 1.0 ) {\\n\\t\\t\\tfloat distance = compare - distribution.x ;\\n\\t\\t\\tfloat variance = max( 0.00000, distribution.y * distribution.y );\\n\\t\\t\\tfloat softness_probability = variance / (variance + distance * distance );\\t\\t\\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\\t\\t\\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\\n\\t\\t}\\n\\t\\treturn occlusion;\\n\\t}\\n\\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\\n\\t\\tfloat shadow = 1.0;\\n\\t\\tshadowCoord.xyz /= shadowCoord.w;\\n\\t\\tshadowCoord.z += shadowBias;\\n\\t\\tbool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;\\n\\t\\tbool frustumTest = inFrustum && shadowCoord.z <= 1.0;\\n\\t\\tif ( frustumTest ) {\\n\\t\\t#if defined( SHADOWMAP_TYPE_PCF )\\n\\t\\t\\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\\n\\t\\t\\tfloat dx0 = - texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy0 = - texelSize.y * shadowRadius;\\n\\t\\t\\tfloat dx1 = + texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy1 = + texelSize.y * shadowRadius;\\n\\t\\t\\tfloat dx2 = dx0 / 2.0;\\n\\t\\t\\tfloat dy2 = dy0 / 2.0;\\n\\t\\t\\tfloat dx3 = dx1 / 2.0;\\n\\t\\t\\tfloat dy3 = dy1 / 2.0;\\n\\t\\t\\tshadow = (\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\\n\\t\\t\\t) * ( 1.0 / 17.0 );\\n\\t\\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\\n\\t\\t\\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\\n\\t\\t\\tfloat dx = texelSize.x;\\n\\t\\t\\tfloat dy = texelSize.y;\\n\\t\\t\\tvec2 uv = shadowCoord.xy;\\n\\t\\t\\tvec2 f = fract( uv * shadowMapSize + 0.5 );\\n\\t\\t\\tuv -= f * texelSize;\\n\\t\\t\\tshadow = (\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\\n\\t\\t\\t\\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ),\\n\\t\\t\\t\\t\\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\\n\\t\\t\\t\\t\\t f.x ) +\\n\\t\\t\\t\\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ),\\n\\t\\t\\t\\t\\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\\n\\t\\t\\t\\t\\t f.x ) +\\n\\t\\t\\t\\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ),\\n\\t\\t\\t\\t\\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\\n\\t\\t\\t\\t\\t f.y ) +\\n\\t\\t\\t\\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ),\\n\\t\\t\\t\\t\\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\\n\\t\\t\\t\\t\\t f.y ) +\\n\\t\\t\\t\\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ),\\n\\t\\t\\t\\t\\t\\t  texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\\n\\t\\t\\t\\t\\t\\t  f.x ),\\n\\t\\t\\t\\t\\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ),\\n\\t\\t\\t\\t\\t\\t  texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\\n\\t\\t\\t\\t\\t\\t  f.x ),\\n\\t\\t\\t\\t\\t f.y )\\n\\t\\t\\t) * ( 1.0 / 9.0 );\\n\\t\\t#elif defined( SHADOWMAP_TYPE_VSM )\\n\\t\\t\\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\\n\\t\\t#else\\n\\t\\t\\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\\n\\t\\t#endif\\n\\t\\t}\\n\\t\\treturn shadow;\\n\\t}\\n\\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\\n\\t\\tvec3 absV = abs( v );\\n\\t\\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\\n\\t\\tabsV *= scaleToCube;\\n\\t\\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\\n\\t\\tvec2 planar = v.xy;\\n\\t\\tfloat almostATexel = 1.5 * texelSizeY;\\n\\t\\tfloat almostOne = 1.0 - almostATexel;\\n\\t\\tif ( absV.z >= almostOne ) {\\n\\t\\t\\tif ( v.z > 0.0 )\\n\\t\\t\\t\\tplanar.x = 4.0 - v.x;\\n\\t\\t} else if ( absV.x >= almostOne ) {\\n\\t\\t\\tfloat signX = sign( v.x );\\n\\t\\t\\tplanar.x = v.z * signX + 2.0 * signX;\\n\\t\\t} else if ( absV.y >= almostOne ) {\\n\\t\\t\\tfloat signY = sign( v.y );\\n\\t\\t\\tplanar.x = v.x + 2.0 * signY + 2.0;\\n\\t\\t\\tplanar.y = v.z * signY - 2.0;\\n\\t\\t}\\n\\t\\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\\n\\t}\\n\\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\\n\\t\\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\\n\\t\\tvec3 lightToPosition = shadowCoord.xyz;\\n\\t\\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\\t\\tdp += shadowBias;\\n\\t\\tvec3 bd3D = normalize( lightToPosition );\\n\\t\\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\\n\\t\\t\\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\\n\\t\\t\\treturn (\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\\n\\t\\t\\t) * ( 1.0 / 9.0 );\\n\\t\\t#else\\n\\t\\t\\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\\n\\t\\t#endif\\n\\t}\\n#endif\";\n\nvar shadowmap_pars_vertex = \"#if NUM_SPOT_LIGHT_COORDS > 0\\n\\tuniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ];\\n\\tvarying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ];\\n#endif\\n#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHT_SHADOWS > 0\\n\\t\\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\\n\\t\\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\\n\\t\\tstruct DirectionalLightShadow {\\n\\t\\t\\tfloat shadowBias;\\n\\t\\t\\tfloat shadowNormalBias;\\n\\t\\t\\tfloat shadowRadius;\\n\\t\\t\\tvec2 shadowMapSize;\\n\\t\\t};\\n\\t\\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHT_SHADOWS > 0\\n\\t\\tstruct SpotLightShadow {\\n\\t\\t\\tfloat shadowBias;\\n\\t\\t\\tfloat shadowNormalBias;\\n\\t\\t\\tfloat shadowRadius;\\n\\t\\t\\tvec2 shadowMapSize;\\n\\t\\t};\\n\\t\\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\\n\\t#endif\\n\\t#if NUM_POINT_LIGHT_SHADOWS > 0\\n\\t\\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\\n\\t\\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\\n\\t\\tstruct PointLightShadow {\\n\\t\\t\\tfloat shadowBias;\\n\\t\\t\\tfloat shadowNormalBias;\\n\\t\\t\\tfloat shadowRadius;\\n\\t\\t\\tvec2 shadowMapSize;\\n\\t\\t\\tfloat shadowCameraNear;\\n\\t\\t\\tfloat shadowCameraFar;\\n\\t\\t};\\n\\t\\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\\n\\t#endif\\n#endif\";\n\nvar shadowmap_vertex = \"#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 )\\n\\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\\n\\tvec4 shadowWorldPosition;\\n#endif\\n#if defined( USE_SHADOWMAP )\\n\\t#if NUM_DIR_LIGHT_SHADOWS > 0\\n\\t\\t#pragma unroll_loop_start\\n\\t\\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\\n\\t\\t\\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\\n\\t\\t\\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\\n\\t\\t}\\n\\t\\t#pragma unroll_loop_end\\n\\t#endif\\n\\t#if NUM_POINT_LIGHT_SHADOWS > 0\\n\\t\\t#pragma unroll_loop_start\\n\\t\\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\\n\\t\\t\\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\\n\\t\\t\\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\\n\\t\\t}\\n\\t\\t#pragma unroll_loop_end\\n\\t#endif\\n#endif\\n#if NUM_SPOT_LIGHT_COORDS > 0\\n\\t#pragma unroll_loop_start\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) {\\n\\t\\tshadowWorldPosition = worldPosition;\\n\\t\\t#if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\\n\\t\\t\\tshadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias;\\n\\t\\t#endif\\n\\t\\tvSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition;\\n\\t}\\n\\t#pragma unroll_loop_end\\n#endif\";\n\nvar shadowmask_pars_fragment = \"float getShadowMask() {\\n\\tfloat shadow = 1.0;\\n\\t#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHT_SHADOWS > 0\\n\\tDirectionalLightShadow directionalLight;\\n\\t#pragma unroll_loop_start\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\\n\\t\\tdirectionalLight = directionalLightShadows[ i ];\\n\\t\\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\\n\\t}\\n\\t#pragma unroll_loop_end\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHT_SHADOWS > 0\\n\\tSpotLightShadow spotLight;\\n\\t#pragma unroll_loop_start\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\\n\\t\\tspotLight = spotLightShadows[ i ];\\n\\t\\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;\\n\\t}\\n\\t#pragma unroll_loop_end\\n\\t#endif\\n\\t#if NUM_POINT_LIGHT_SHADOWS > 0\\n\\tPointLightShadow pointLight;\\n\\t#pragma unroll_loop_start\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\\n\\t\\tpointLight = pointLightShadows[ i ];\\n\\t\\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\\n\\t}\\n\\t#pragma unroll_loop_end\\n\\t#endif\\n\\t#endif\\n\\treturn shadow;\\n}\";\n\nvar skinbase_vertex = \"#ifdef USE_SKINNING\\n\\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\\n\\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\\n\\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\\n\\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\\n#endif\";\n\nvar skinning_pars_vertex = \"#ifdef USE_SKINNING\\n\\tuniform mat4 bindMatrix;\\n\\tuniform mat4 bindMatrixInverse;\\n\\tuniform highp sampler2D boneTexture;\\n\\tmat4 getBoneMatrix( const in float i ) {\\n\\t\\tint size = textureSize( boneTexture, 0 ).x;\\n\\t\\tint j = int( i ) * 4;\\n\\t\\tint x = j % size;\\n\\t\\tint y = j / size;\\n\\t\\tvec4 v1 = texelFetch( boneTexture, ivec2( x, y ), 0 );\\n\\t\\tvec4 v2 = texelFetch( boneTexture, ivec2( x + 1, y ), 0 );\\n\\t\\tvec4 v3 = texelFetch( boneTexture, ivec2( x + 2, y ), 0 );\\n\\t\\tvec4 v4 = texelFetch( boneTexture, ivec2( x + 3, y ), 0 );\\n\\t\\treturn mat4( v1, v2, v3, v4 );\\n\\t}\\n#endif\";\n\nvar skinning_vertex = \"#ifdef USE_SKINNING\\n\\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\\n\\tvec4 skinned = vec4( 0.0 );\\n\\tskinned += boneMatX * skinVertex * skinWeight.x;\\n\\tskinned += boneMatY * skinVertex * skinWeight.y;\\n\\tskinned += boneMatZ * skinVertex * skinWeight.z;\\n\\tskinned += boneMatW * skinVertex * skinWeight.w;\\n\\ttransformed = ( bindMatrixInverse * skinned ).xyz;\\n#endif\";\n\nvar skinnormal_vertex = \"#ifdef USE_SKINNING\\n\\tmat4 skinMatrix = mat4( 0.0 );\\n\\tskinMatrix += skinWeight.x * boneMatX;\\n\\tskinMatrix += skinWeight.y * boneMatY;\\n\\tskinMatrix += skinWeight.z * boneMatZ;\\n\\tskinMatrix += skinWeight.w * boneMatW;\\n\\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\\n\\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\\n\\t#ifdef USE_TANGENT\\n\\t\\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\\n\\t#endif\\n#endif\";\n\nvar specularmap_fragment = \"float specularStrength;\\n#ifdef USE_SPECULARMAP\\n\\tvec4 texelSpecular = texture2D( specularMap, vSpecularMapUv );\\n\\tspecularStrength = texelSpecular.r;\\n#else\\n\\tspecularStrength = 1.0;\\n#endif\";\n\nvar specularmap_pars_fragment = \"#ifdef USE_SPECULARMAP\\n\\tuniform sampler2D specularMap;\\n#endif\";\n\nvar tonemapping_fragment = \"#if defined( TONE_MAPPING )\\n\\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\\n#endif\";\n\nvar tonemapping_pars_fragment = \"#ifndef saturate\\n#define saturate( a ) clamp( a, 0.0, 1.0 )\\n#endif\\nuniform float toneMappingExposure;\\nvec3 LinearToneMapping( vec3 color ) {\\n\\treturn saturate( toneMappingExposure * color );\\n}\\nvec3 ReinhardToneMapping( vec3 color ) {\\n\\tcolor *= toneMappingExposure;\\n\\treturn saturate( color / ( vec3( 1.0 ) + color ) );\\n}\\nvec3 OptimizedCineonToneMapping( vec3 color ) {\\n\\tcolor *= toneMappingExposure;\\n\\tcolor = max( vec3( 0.0 ), color - 0.004 );\\n\\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\\n}\\nvec3 RRTAndODTFit( vec3 v ) {\\n\\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\\n\\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\\n\\treturn a / b;\\n}\\nvec3 ACESFilmicToneMapping( vec3 color ) {\\n\\tconst mat3 ACESInputMat = mat3(\\n\\t\\tvec3( 0.59719, 0.07600, 0.02840 ),\\t\\tvec3( 0.35458, 0.90834, 0.13383 ),\\n\\t\\tvec3( 0.04823, 0.01566, 0.83777 )\\n\\t);\\n\\tconst mat3 ACESOutputMat = mat3(\\n\\t\\tvec3(  1.60475, -0.10208, -0.00327 ),\\t\\tvec3( -0.53108,  1.10813, -0.07276 ),\\n\\t\\tvec3( -0.07367, -0.00605,  1.07602 )\\n\\t);\\n\\tcolor *= toneMappingExposure / 0.6;\\n\\tcolor = ACESInputMat * color;\\n\\tcolor = RRTAndODTFit( color );\\n\\tcolor = ACESOutputMat * color;\\n\\treturn saturate( color );\\n}\\nconst mat3 LINEAR_REC2020_TO_LINEAR_SRGB = mat3(\\n\\tvec3( 1.6605, - 0.1246, - 0.0182 ),\\n\\tvec3( - 0.5876, 1.1329, - 0.1006 ),\\n\\tvec3( - 0.0728, - 0.0083, 1.1187 )\\n);\\nconst mat3 LINEAR_SRGB_TO_LINEAR_REC2020 = mat3(\\n\\tvec3( 0.6274, 0.0691, 0.0164 ),\\n\\tvec3( 0.3293, 0.9195, 0.0880 ),\\n\\tvec3( 0.0433, 0.0113, 0.8956 )\\n);\\nvec3 agxDefaultContrastApprox( vec3 x ) {\\n\\tvec3 x2 = x * x;\\n\\tvec3 x4 = x2 * x2;\\n\\treturn + 15.5 * x4 * x2\\n\\t\\t- 40.14 * x4 * x\\n\\t\\t+ 31.96 * x4\\n\\t\\t- 6.868 * x2 * x\\n\\t\\t+ 0.4298 * x2\\n\\t\\t+ 0.1191 * x\\n\\t\\t- 0.00232;\\n}\\nvec3 AgXToneMapping( vec3 color ) {\\n\\tconst mat3 AgXInsetMatrix = mat3(\\n\\t\\tvec3( 0.856627153315983, 0.137318972929847, 0.11189821299995 ),\\n\\t\\tvec3( 0.0951212405381588, 0.761241990602591, 0.0767994186031903 ),\\n\\t\\tvec3( 0.0482516061458583, 0.101439036467562, 0.811302368396859 )\\n\\t);\\n\\tconst mat3 AgXOutsetMatrix = mat3(\\n\\t\\tvec3( 1.1271005818144368, - 0.1413297634984383, - 0.14132976349843826 ),\\n\\t\\tvec3( - 0.11060664309660323, 1.157823702216272, - 0.11060664309660294 ),\\n\\t\\tvec3( - 0.016493938717834573, - 0.016493938717834257, 1.2519364065950405 )\\n\\t);\\n\\tconst float AgxMinEv = - 12.47393;\\tconst float AgxMaxEv = 4.026069;\\n\\tcolor = LINEAR_SRGB_TO_LINEAR_REC2020 * color;\\n\\tcolor *= toneMappingExposure;\\n\\tcolor = AgXInsetMatrix * color;\\n\\tcolor = max( color, 1e-10 );\\tcolor = log2( color );\\n\\tcolor = ( color - AgxMinEv ) / ( AgxMaxEv - AgxMinEv );\\n\\tcolor = clamp( color, 0.0, 1.0 );\\n\\tcolor = agxDefaultContrastApprox( color );\\n\\tcolor = AgXOutsetMatrix * color;\\n\\tcolor = pow( max( vec3( 0.0 ), color ), vec3( 2.2 ) );\\n\\tcolor = LINEAR_REC2020_TO_LINEAR_SRGB * color;\\n\\treturn color;\\n}\\nvec3 CustomToneMapping( vec3 color ) { return color; }\";\n\nvar transmission_fragment = \"#ifdef USE_TRANSMISSION\\n\\tmaterial.transmission = transmission;\\n\\tmaterial.transmissionAlpha = 1.0;\\n\\tmaterial.thickness = thickness;\\n\\tmaterial.attenuationDistance = attenuationDistance;\\n\\tmaterial.attenuationColor = attenuationColor;\\n\\t#ifdef USE_TRANSMISSIONMAP\\n\\t\\tmaterial.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r;\\n\\t#endif\\n\\t#ifdef USE_THICKNESSMAP\\n\\t\\tmaterial.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g;\\n\\t#endif\\n\\tvec3 pos = vWorldPosition;\\n\\tvec3 v = normalize( cameraPosition - pos );\\n\\tvec3 n = inverseTransformDirection( normal, viewMatrix );\\n\\tvec4 transmitted = getIBLVolumeRefraction(\\n\\t\\tn, v, material.roughness, material.diffuseColor, material.specularColor, material.specularF90,\\n\\t\\tpos, modelMatrix, viewMatrix, projectionMatrix, material.ior, material.thickness,\\n\\t\\tmaterial.attenuationColor, material.attenuationDistance );\\n\\tmaterial.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission );\\n\\ttotalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission );\\n#endif\";\n\nvar transmission_pars_fragment = \"#ifdef USE_TRANSMISSION\\n\\tuniform float transmission;\\n\\tuniform float thickness;\\n\\tuniform float attenuationDistance;\\n\\tuniform vec3 attenuationColor;\\n\\t#ifdef USE_TRANSMISSIONMAP\\n\\t\\tuniform sampler2D transmissionMap;\\n\\t#endif\\n\\t#ifdef USE_THICKNESSMAP\\n\\t\\tuniform sampler2D thicknessMap;\\n\\t#endif\\n\\tuniform vec2 transmissionSamplerSize;\\n\\tuniform sampler2D transmissionSamplerMap;\\n\\tuniform mat4 modelMatrix;\\n\\tuniform mat4 projectionMatrix;\\n\\tvarying vec3 vWorldPosition;\\n\\tfloat w0( float a ) {\\n\\t\\treturn ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 );\\n\\t}\\n\\tfloat w1( float a ) {\\n\\t\\treturn ( 1.0 / 6.0 ) * ( a *  a * ( 3.0 * a - 6.0 ) + 4.0 );\\n\\t}\\n\\tfloat w2( float a ){\\n\\t\\treturn ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 );\\n\\t}\\n\\tfloat w3( float a ) {\\n\\t\\treturn ( 1.0 / 6.0 ) * ( a * a * a );\\n\\t}\\n\\tfloat g0( float a ) {\\n\\t\\treturn w0( a ) + w1( a );\\n\\t}\\n\\tfloat g1( float a ) {\\n\\t\\treturn w2( a ) + w3( a );\\n\\t}\\n\\tfloat h0( float a ) {\\n\\t\\treturn - 1.0 + w1( a ) / ( w0( a ) + w1( a ) );\\n\\t}\\n\\tfloat h1( float a ) {\\n\\t\\treturn 1.0 + w3( a ) / ( w2( a ) + w3( a ) );\\n\\t}\\n\\tvec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) {\\n\\t\\tuv = uv * texelSize.zw + 0.5;\\n\\t\\tvec2 iuv = floor( uv );\\n\\t\\tvec2 fuv = fract( uv );\\n\\t\\tfloat g0x = g0( fuv.x );\\n\\t\\tfloat g1x = g1( fuv.x );\\n\\t\\tfloat h0x = h0( fuv.x );\\n\\t\\tfloat h1x = h1( fuv.x );\\n\\t\\tfloat h0y = h0( fuv.y );\\n\\t\\tfloat h1y = h1( fuv.y );\\n\\t\\tvec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy;\\n\\t\\tvec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy;\\n\\t\\tvec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy;\\n\\t\\tvec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy;\\n\\t\\treturn g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) +\\n\\t\\t\\tg1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) );\\n\\t}\\n\\tvec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) {\\n\\t\\tvec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) );\\n\\t\\tvec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) );\\n\\t\\tvec2 fLodSizeInv = 1.0 / fLodSize;\\n\\t\\tvec2 cLodSizeInv = 1.0 / cLodSize;\\n\\t\\tvec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) );\\n\\t\\tvec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) );\\n\\t\\treturn mix( fSample, cSample, fract( lod ) );\\n\\t}\\n\\tvec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) {\\n\\t\\tvec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior );\\n\\t\\tvec3 modelScale;\\n\\t\\tmodelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) );\\n\\t\\tmodelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) );\\n\\t\\tmodelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) );\\n\\t\\treturn normalize( refractionVector ) * thickness * modelScale;\\n\\t}\\n\\tfloat applyIorToRoughness( const in float roughness, const in float ior ) {\\n\\t\\treturn roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 );\\n\\t}\\n\\tvec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) {\\n\\t\\tfloat lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior );\\n\\t\\treturn textureBicubic( transmissionSamplerMap, fragCoord.xy, lod );\\n\\t}\\n\\tvec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) {\\n\\t\\tif ( isinf( attenuationDistance ) ) {\\n\\t\\t\\treturn vec3( 1.0 );\\n\\t\\t} else {\\n\\t\\t\\tvec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance;\\n\\t\\t\\tvec3 transmittance = exp( - attenuationCoefficient * transmissionDistance );\\t\\t\\treturn transmittance;\\n\\t\\t}\\n\\t}\\n\\tvec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor,\\n\\t\\tconst in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix,\\n\\t\\tconst in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness,\\n\\t\\tconst in vec3 attenuationColor, const in float attenuationDistance ) {\\n\\t\\tvec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix );\\n\\t\\tvec3 refractedRayExit = position + transmissionRay;\\n\\t\\tvec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );\\n\\t\\tvec2 refractionCoords = ndcPos.xy / ndcPos.w;\\n\\t\\trefractionCoords += 1.0;\\n\\t\\trefractionCoords /= 2.0;\\n\\t\\tvec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior );\\n\\t\\tvec3 transmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance );\\n\\t\\tvec3 attenuatedColor = transmittance * transmittedLight.rgb;\\n\\t\\tvec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness );\\n\\t\\tfloat transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0;\\n\\t\\treturn vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor );\\n\\t}\\n#endif\";\n\nvar uv_pars_fragment = \"#if defined( USE_UV ) || defined( USE_ANISOTROPY )\\n\\tvarying vec2 vUv;\\n#endif\\n#ifdef USE_MAP\\n\\tvarying vec2 vMapUv;\\n#endif\\n#ifdef USE_ALPHAMAP\\n\\tvarying vec2 vAlphaMapUv;\\n#endif\\n#ifdef USE_LIGHTMAP\\n\\tvarying vec2 vLightMapUv;\\n#endif\\n#ifdef USE_AOMAP\\n\\tvarying vec2 vAoMapUv;\\n#endif\\n#ifdef USE_BUMPMAP\\n\\tvarying vec2 vBumpMapUv;\\n#endif\\n#ifdef USE_NORMALMAP\\n\\tvarying vec2 vNormalMapUv;\\n#endif\\n#ifdef USE_EMISSIVEMAP\\n\\tvarying vec2 vEmissiveMapUv;\\n#endif\\n#ifdef USE_METALNESSMAP\\n\\tvarying vec2 vMetalnessMapUv;\\n#endif\\n#ifdef USE_ROUGHNESSMAP\\n\\tvarying vec2 vRoughnessMapUv;\\n#endif\\n#ifdef USE_ANISOTROPYMAP\\n\\tvarying vec2 vAnisotropyMapUv;\\n#endif\\n#ifdef USE_CLEARCOATMAP\\n\\tvarying vec2 vClearcoatMapUv;\\n#endif\\n#ifdef USE_CLEARCOAT_NORMALMAP\\n\\tvarying vec2 vClearcoatNormalMapUv;\\n#endif\\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\\n\\tvarying vec2 vClearcoatRoughnessMapUv;\\n#endif\\n#ifdef USE_IRIDESCENCEMAP\\n\\tvarying vec2 vIridescenceMapUv;\\n#endif\\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\\n\\tvarying vec2 vIridescenceThicknessMapUv;\\n#endif\\n#ifdef USE_SHEEN_COLORMAP\\n\\tvarying vec2 vSheenColorMapUv;\\n#endif\\n#ifdef USE_SHEEN_ROUGHNESSMAP\\n\\tvarying vec2 vSheenRoughnessMapUv;\\n#endif\\n#ifdef USE_SPECULARMAP\\n\\tvarying vec2 vSpecularMapUv;\\n#endif\\n#ifdef USE_SPECULAR_COLORMAP\\n\\tvarying vec2 vSpecularColorMapUv;\\n#endif\\n#ifdef USE_SPECULAR_INTENSITYMAP\\n\\tvarying vec2 vSpecularIntensityMapUv;\\n#endif\\n#ifdef USE_TRANSMISSIONMAP\\n\\tuniform mat3 transmissionMapTransform;\\n\\tvarying vec2 vTransmissionMapUv;\\n#endif\\n#ifdef USE_THICKNESSMAP\\n\\tuniform mat3 thicknessMapTransform;\\n\\tvarying vec2 vThicknessMapUv;\\n#endif\";\n\nvar uv_pars_vertex = \"#if defined( USE_UV ) || defined( USE_ANISOTROPY )\\n\\tvarying vec2 vUv;\\n#endif\\n#ifdef USE_MAP\\n\\tuniform mat3 mapTransform;\\n\\tvarying vec2 vMapUv;\\n#endif\\n#ifdef USE_ALPHAMAP\\n\\tuniform mat3 alphaMapTransform;\\n\\tvarying vec2 vAlphaMapUv;\\n#endif\\n#ifdef USE_LIGHTMAP\\n\\tuniform mat3 lightMapTransform;\\n\\tvarying vec2 vLightMapUv;\\n#endif\\n#ifdef USE_AOMAP\\n\\tuniform mat3 aoMapTransform;\\n\\tvarying vec2 vAoMapUv;\\n#endif\\n#ifdef USE_BUMPMAP\\n\\tuniform mat3 bumpMapTransform;\\n\\tvarying vec2 vBumpMapUv;\\n#endif\\n#ifdef USE_NORMALMAP\\n\\tuniform mat3 normalMapTransform;\\n\\tvarying vec2 vNormalMapUv;\\n#endif\\n#ifdef USE_DISPLACEMENTMAP\\n\\tuniform mat3 displacementMapTransform;\\n\\tvarying vec2 vDisplacementMapUv;\\n#endif\\n#ifdef USE_EMISSIVEMAP\\n\\tuniform mat3 emissiveMapTransform;\\n\\tvarying vec2 vEmissiveMapUv;\\n#endif\\n#ifdef USE_METALNESSMAP\\n\\tuniform mat3 metalnessMapTransform;\\n\\tvarying vec2 vMetalnessMapUv;\\n#endif\\n#ifdef USE_ROUGHNESSMAP\\n\\tuniform mat3 roughnessMapTransform;\\n\\tvarying vec2 vRoughnessMapUv;\\n#endif\\n#ifdef USE_ANISOTROPYMAP\\n\\tuniform mat3 anisotropyMapTransform;\\n\\tvarying vec2 vAnisotropyMapUv;\\n#endif\\n#ifdef USE_CLEARCOATMAP\\n\\tuniform mat3 clearcoatMapTransform;\\n\\tvarying vec2 vClearcoatMapUv;\\n#endif\\n#ifdef USE_CLEARCOAT_NORMALMAP\\n\\tuniform mat3 clearcoatNormalMapTransform;\\n\\tvarying vec2 vClearcoatNormalMapUv;\\n#endif\\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\\n\\tuniform mat3 clearcoatRoughnessMapTransform;\\n\\tvarying vec2 vClearcoatRoughnessMapUv;\\n#endif\\n#ifdef USE_SHEEN_COLORMAP\\n\\tuniform mat3 sheenColorMapTransform;\\n\\tvarying vec2 vSheenColorMapUv;\\n#endif\\n#ifdef USE_SHEEN_ROUGHNESSMAP\\n\\tuniform mat3 sheenRoughnessMapTransform;\\n\\tvarying vec2 vSheenRoughnessMapUv;\\n#endif\\n#ifdef USE_IRIDESCENCEMAP\\n\\tuniform mat3 iridescenceMapTransform;\\n\\tvarying vec2 vIridescenceMapUv;\\n#endif\\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\\n\\tuniform mat3 iridescenceThicknessMapTransform;\\n\\tvarying vec2 vIridescenceThicknessMapUv;\\n#endif\\n#ifdef USE_SPECULARMAP\\n\\tuniform mat3 specularMapTransform;\\n\\tvarying vec2 vSpecularMapUv;\\n#endif\\n#ifdef USE_SPECULAR_COLORMAP\\n\\tuniform mat3 specularColorMapTransform;\\n\\tvarying vec2 vSpecularColorMapUv;\\n#endif\\n#ifdef USE_SPECULAR_INTENSITYMAP\\n\\tuniform mat3 specularIntensityMapTransform;\\n\\tvarying vec2 vSpecularIntensityMapUv;\\n#endif\\n#ifdef USE_TRANSMISSIONMAP\\n\\tuniform mat3 transmissionMapTransform;\\n\\tvarying vec2 vTransmissionMapUv;\\n#endif\\n#ifdef USE_THICKNESSMAP\\n\\tuniform mat3 thicknessMapTransform;\\n\\tvarying vec2 vThicknessMapUv;\\n#endif\";\n\nvar uv_vertex = \"#if defined( USE_UV ) || defined( USE_ANISOTROPY )\\n\\tvUv = vec3( uv, 1 ).xy;\\n#endif\\n#ifdef USE_MAP\\n\\tvMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_ALPHAMAP\\n\\tvAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_LIGHTMAP\\n\\tvLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_AOMAP\\n\\tvAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_BUMPMAP\\n\\tvBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_NORMALMAP\\n\\tvNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_DISPLACEMENTMAP\\n\\tvDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_EMISSIVEMAP\\n\\tvEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_METALNESSMAP\\n\\tvMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_ROUGHNESSMAP\\n\\tvRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_ANISOTROPYMAP\\n\\tvAnisotropyMapUv = ( anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_CLEARCOATMAP\\n\\tvClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_CLEARCOAT_NORMALMAP\\n\\tvClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\\n\\tvClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_IRIDESCENCEMAP\\n\\tvIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\\n\\tvIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_SHEEN_COLORMAP\\n\\tvSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_SHEEN_ROUGHNESSMAP\\n\\tvSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_SPECULARMAP\\n\\tvSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_SPECULAR_COLORMAP\\n\\tvSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_SPECULAR_INTENSITYMAP\\n\\tvSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_TRANSMISSIONMAP\\n\\tvTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy;\\n#endif\\n#ifdef USE_THICKNESSMAP\\n\\tvThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy;\\n#endif\";\n\nvar worldpos_vertex = \"#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0\\n\\tvec4 worldPosition = vec4( transformed, 1.0 );\\n\\t#ifdef USE_BATCHING\\n\\t\\tworldPosition = batchingMatrix * worldPosition;\\n\\t#endif\\n\\t#ifdef USE_INSTANCING\\n\\t\\tworldPosition = instanceMatrix * worldPosition;\\n\\t#endif\\n\\tworldPosition = modelMatrix * worldPosition;\\n#endif\";\n\nconst vertex$h = \"varying vec2 vUv;\\nuniform mat3 uvTransform;\\nvoid main() {\\n\\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\\n\\tgl_Position = vec4( position.xy, 1.0, 1.0 );\\n}\";\n\nconst fragment$h = \"uniform sampler2D t2D;\\nuniform float backgroundIntensity;\\nvarying vec2 vUv;\\nvoid main() {\\n\\tvec4 texColor = texture2D( t2D, vUv );\\n\\t#ifdef DECODE_VIDEO_TEXTURE\\n\\t\\ttexColor = vec4( mix( pow( texColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), texColor.rgb * 0.0773993808, vec3( lessThanEqual( texColor.rgb, vec3( 0.04045 ) ) ) ), texColor.w );\\n\\t#endif\\n\\ttexColor.rgb *= backgroundIntensity;\\n\\tgl_FragColor = texColor;\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n}\";\n\nconst vertex$g = \"varying vec3 vWorldDirection;\\n#include <common>\\nvoid main() {\\n\\tvWorldDirection = transformDirection( position, modelMatrix );\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n\\tgl_Position.z = gl_Position.w;\\n}\";\n\nconst fragment$g = \"#ifdef ENVMAP_TYPE_CUBE\\n\\tuniform samplerCube envMap;\\n#elif defined( ENVMAP_TYPE_CUBE_UV )\\n\\tuniform sampler2D envMap;\\n#endif\\nuniform float flipEnvMap;\\nuniform float backgroundBlurriness;\\nuniform float backgroundIntensity;\\nvarying vec3 vWorldDirection;\\n#include <cube_uv_reflection_fragment>\\nvoid main() {\\n\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\tvec4 texColor = textureCube( envMap, vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) );\\n\\t#elif defined( ENVMAP_TYPE_CUBE_UV )\\n\\t\\tvec4 texColor = textureCubeUV( envMap, vWorldDirection, backgroundBlurriness );\\n\\t#else\\n\\t\\tvec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 );\\n\\t#endif\\n\\ttexColor.rgb *= backgroundIntensity;\\n\\tgl_FragColor = texColor;\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n}\";\n\nconst vertex$f = \"varying vec3 vWorldDirection;\\n#include <common>\\nvoid main() {\\n\\tvWorldDirection = transformDirection( position, modelMatrix );\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n\\tgl_Position.z = gl_Position.w;\\n}\";\n\nconst fragment$f = \"uniform samplerCube tCube;\\nuniform float tFlip;\\nuniform float opacity;\\nvarying vec3 vWorldDirection;\\nvoid main() {\\n\\tvec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) );\\n\\tgl_FragColor = texColor;\\n\\tgl_FragColor.a *= opacity;\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n}\";\n\nconst vertex$e = \"#include <common>\\n#include <batching_pars_vertex>\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvarying vec2 vHighPrecisionZW;\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <batching_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#ifdef USE_DISPLACEMENTMAP\\n\\t\\t#include <beginnormal_vertex>\\n\\t\\t#include <morphnormal_vertex>\\n\\t\\t#include <skinnormal_vertex>\\n\\t#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvHighPrecisionZW = gl_Position.zw;\\n}\";\n\nconst fragment$e = \"#if DEPTH_PACKING == 3200\\n\\tuniform float opacity;\\n#endif\\n#include <common>\\n#include <packing>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <alphatest_pars_fragment>\\n#include <alphahash_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvarying vec2 vHighPrecisionZW;\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( 1.0 );\\n\\t#if DEPTH_PACKING == 3200\\n\\t\\tdiffuseColor.a = opacity;\\n\\t#endif\\n\\t#include <map_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <alphahash_fragment>\\n\\t#include <logdepthbuf_fragment>\\n\\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\\n\\t#if DEPTH_PACKING == 3200\\n\\t\\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\\n\\t#elif DEPTH_PACKING == 3201\\n\\t\\tgl_FragColor = packDepthToRGBA( fragCoordZ );\\n\\t#endif\\n}\";\n\nconst vertex$d = \"#define DISTANCE\\nvarying vec3 vWorldPosition;\\n#include <common>\\n#include <batching_pars_vertex>\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <batching_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#ifdef USE_DISPLACEMENTMAP\\n\\t\\t#include <beginnormal_vertex>\\n\\t\\t#include <morphnormal_vertex>\\n\\t\\t#include <skinnormal_vertex>\\n\\t#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvWorldPosition = worldPosition.xyz;\\n}\";\n\nconst fragment$d = \"#define DISTANCE\\nuniform vec3 referencePosition;\\nuniform float nearDistance;\\nuniform float farDistance;\\nvarying vec3 vWorldPosition;\\n#include <common>\\n#include <packing>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <alphatest_pars_fragment>\\n#include <alphahash_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main () {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( 1.0 );\\n\\t#include <map_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <alphahash_fragment>\\n\\tfloat dist = length( vWorldPosition - referencePosition );\\n\\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\\n\\tdist = saturate( dist );\\n\\tgl_FragColor = packDepthToRGBA( dist );\\n}\";\n\nconst vertex$c = \"varying vec3 vWorldDirection;\\n#include <common>\\nvoid main() {\\n\\tvWorldDirection = transformDirection( position, modelMatrix );\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n}\";\n\nconst fragment$c = \"uniform sampler2D tEquirect;\\nvarying vec3 vWorldDirection;\\n#include <common>\\nvoid main() {\\n\\tvec3 direction = normalize( vWorldDirection );\\n\\tvec2 sampleUV = equirectUv( direction );\\n\\tgl_FragColor = texture2D( tEquirect, sampleUV );\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n}\";\n\nconst vertex$b = \"uniform float scale;\\nattribute float lineDistance;\\nvarying float vLineDistance;\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\tvLineDistance = scale * lineDistance;\\n\\t#include <uv_vertex>\\n\\t#include <color_vertex>\\n\\t#include <morphcolor_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <fog_vertex>\\n}\";\n\nconst fragment$b = \"uniform vec3 diffuse;\\nuniform float opacity;\\nuniform float dashSize;\\nuniform float totalSize;\\nvarying float vLineDistance;\\n#include <common>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\\n\\t\\tdiscard;\\n\\t}\\n\\tvec3 outgoingLight = vec3( 0.0 );\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\toutgoingLight = diffuseColor.rgb;\\n\\t#include <opaque_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n}\";\n\nconst vertex$a = \"#include <common>\\n#include <batching_pars_vertex>\\n#include <uv_pars_vertex>\\n#include <envmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <color_vertex>\\n\\t#include <morphcolor_vertex>\\n\\t#include <batching_vertex>\\n\\t#if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )\\n\\t\\t#include <beginnormal_vertex>\\n\\t\\t#include <morphnormal_vertex>\\n\\t\\t#include <skinbase_vertex>\\n\\t\\t#include <skinnormal_vertex>\\n\\t\\t#include <defaultnormal_vertex>\\n\\t#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <envmap_vertex>\\n\\t#include <fog_vertex>\\n}\";\n\nconst fragment$a = \"uniform vec3 diffuse;\\nuniform float opacity;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <dithering_pars_fragment>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <alphatest_pars_fragment>\\n#include <alphahash_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <envmap_common_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <alphahash_fragment>\\n\\t#include <specularmap_fragment>\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\t#ifdef USE_LIGHTMAP\\n\\t\\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\\n\\t\\treflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI;\\n\\t#else\\n\\t\\treflectedLight.indirectDiffuse += vec3( 1.0 );\\n\\t#endif\\n\\t#include <aomap_fragment>\\n\\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\\n\\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\\n\\t#include <envmap_fragment>\\n\\t#include <opaque_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <dithering_fragment>\\n}\";\n\nconst vertex$9 = \"#define LAMBERT\\nvarying vec3 vViewPosition;\\n#include <common>\\n#include <batching_pars_vertex>\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <envmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <normal_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <color_vertex>\\n\\t#include <morphcolor_vertex>\\n\\t#include <batching_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#include <normal_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvViewPosition = - mvPosition.xyz;\\n\\t#include <worldpos_vertex>\\n\\t#include <envmap_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\";\n\nconst fragment$9 = \"#define LAMBERT\\nuniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <dithering_pars_fragment>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <alphatest_pars_fragment>\\n#include <alphahash_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <envmap_common_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <bsdfs>\\n#include <lights_pars_begin>\\n#include <normal_pars_fragment>\\n#include <lights_lambert_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <alphahash_fragment>\\n\\t#include <specularmap_fragment>\\n\\t#include <normal_fragment_begin>\\n\\t#include <normal_fragment_maps>\\n\\t#include <emissivemap_fragment>\\n\\t#include <lights_lambert_fragment>\\n\\t#include <lights_fragment_begin>\\n\\t#include <lights_fragment_maps>\\n\\t#include <lights_fragment_end>\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\\n\\t#include <envmap_fragment>\\n\\t#include <opaque_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <dithering_fragment>\\n}\";\n\nconst vertex$8 = \"#define MATCAP\\nvarying vec3 vViewPosition;\\n#include <common>\\n#include <batching_pars_vertex>\\n#include <uv_pars_vertex>\\n#include <color_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <normal_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <color_vertex>\\n\\t#include <morphcolor_vertex>\\n\\t#include <batching_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#include <normal_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <fog_vertex>\\n\\tvViewPosition = - mvPosition.xyz;\\n}\";\n\nconst fragment$8 = \"#define MATCAP\\nuniform vec3 diffuse;\\nuniform float opacity;\\nuniform sampler2D matcap;\\nvarying vec3 vViewPosition;\\n#include <common>\\n#include <dithering_pars_fragment>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <alphatest_pars_fragment>\\n#include <alphahash_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <normal_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <alphahash_fragment>\\n\\t#include <normal_fragment_begin>\\n\\t#include <normal_fragment_maps>\\n\\tvec3 viewDir = normalize( vViewPosition );\\n\\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\\n\\tvec3 y = cross( viewDir, x );\\n\\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\\n\\t#ifdef USE_MATCAP\\n\\t\\tvec4 matcapColor = texture2D( matcap, uv );\\n\\t#else\\n\\t\\tvec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 );\\n\\t#endif\\n\\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\\n\\t#include <opaque_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <dithering_fragment>\\n}\";\n\nconst vertex$7 = \"#define NORMAL\\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\\n\\tvarying vec3 vViewPosition;\\n#endif\\n#include <common>\\n#include <batching_pars_vertex>\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <normal_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <batching_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#include <normal_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\\n\\tvViewPosition = - mvPosition.xyz;\\n#endif\\n}\";\n\nconst fragment$7 = \"#define NORMAL\\nuniform float opacity;\\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\\n\\tvarying vec3 vViewPosition;\\n#endif\\n#include <packing>\\n#include <uv_pars_fragment>\\n#include <normal_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <normal_fragment_begin>\\n\\t#include <normal_fragment_maps>\\n\\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\\n\\t#ifdef OPAQUE\\n\\t\\tgl_FragColor.a = 1.0;\\n\\t#endif\\n}\";\n\nconst vertex$6 = \"#define PHONG\\nvarying vec3 vViewPosition;\\n#include <common>\\n#include <batching_pars_vertex>\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <envmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <normal_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <color_vertex>\\n\\t#include <morphcolor_vertex>\\n\\t#include <batching_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#include <normal_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvViewPosition = - mvPosition.xyz;\\n\\t#include <worldpos_vertex>\\n\\t#include <envmap_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\";\n\nconst fragment$6 = \"#define PHONG\\nuniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform vec3 specular;\\nuniform float shininess;\\nuniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <dithering_pars_fragment>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <alphatest_pars_fragment>\\n#include <alphahash_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <envmap_common_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <bsdfs>\\n#include <lights_pars_begin>\\n#include <normal_pars_fragment>\\n#include <lights_phong_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <alphahash_fragment>\\n\\t#include <specularmap_fragment>\\n\\t#include <normal_fragment_begin>\\n\\t#include <normal_fragment_maps>\\n\\t#include <emissivemap_fragment>\\n\\t#include <lights_phong_fragment>\\n\\t#include <lights_fragment_begin>\\n\\t#include <lights_fragment_maps>\\n\\t#include <lights_fragment_end>\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\\n\\t#include <envmap_fragment>\\n\\t#include <opaque_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <dithering_fragment>\\n}\";\n\nconst vertex$5 = \"#define STANDARD\\nvarying vec3 vViewPosition;\\n#ifdef USE_TRANSMISSION\\n\\tvarying vec3 vWorldPosition;\\n#endif\\n#include <common>\\n#include <batching_pars_vertex>\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <normal_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <color_vertex>\\n\\t#include <morphcolor_vertex>\\n\\t#include <batching_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#include <normal_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvViewPosition = - mvPosition.xyz;\\n\\t#include <worldpos_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n#ifdef USE_TRANSMISSION\\n\\tvWorldPosition = worldPosition.xyz;\\n#endif\\n}\";\n\nconst fragment$5 = \"#define STANDARD\\n#ifdef PHYSICAL\\n\\t#define IOR\\n\\t#define USE_SPECULAR\\n#endif\\nuniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform float roughness;\\nuniform float metalness;\\nuniform float opacity;\\n#ifdef IOR\\n\\tuniform float ior;\\n#endif\\n#ifdef USE_SPECULAR\\n\\tuniform float specularIntensity;\\n\\tuniform vec3 specularColor;\\n\\t#ifdef USE_SPECULAR_COLORMAP\\n\\t\\tuniform sampler2D specularColorMap;\\n\\t#endif\\n\\t#ifdef USE_SPECULAR_INTENSITYMAP\\n\\t\\tuniform sampler2D specularIntensityMap;\\n\\t#endif\\n#endif\\n#ifdef USE_CLEARCOAT\\n\\tuniform float clearcoat;\\n\\tuniform float clearcoatRoughness;\\n#endif\\n#ifdef USE_IRIDESCENCE\\n\\tuniform float iridescence;\\n\\tuniform float iridescenceIOR;\\n\\tuniform float iridescenceThicknessMinimum;\\n\\tuniform float iridescenceThicknessMaximum;\\n#endif\\n#ifdef USE_SHEEN\\n\\tuniform vec3 sheenColor;\\n\\tuniform float sheenRoughness;\\n\\t#ifdef USE_SHEEN_COLORMAP\\n\\t\\tuniform sampler2D sheenColorMap;\\n\\t#endif\\n\\t#ifdef USE_SHEEN_ROUGHNESSMAP\\n\\t\\tuniform sampler2D sheenRoughnessMap;\\n\\t#endif\\n#endif\\n#ifdef USE_ANISOTROPY\\n\\tuniform vec2 anisotropyVector;\\n\\t#ifdef USE_ANISOTROPYMAP\\n\\t\\tuniform sampler2D anisotropyMap;\\n\\t#endif\\n#endif\\nvarying vec3 vViewPosition;\\n#include <common>\\n#include <packing>\\n#include <dithering_pars_fragment>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <alphatest_pars_fragment>\\n#include <alphahash_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <iridescence_fragment>\\n#include <cube_uv_reflection_fragment>\\n#include <envmap_common_pars_fragment>\\n#include <envmap_physical_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <lights_pars_begin>\\n#include <normal_pars_fragment>\\n#include <lights_physical_pars_fragment>\\n#include <transmission_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <clearcoat_pars_fragment>\\n#include <iridescence_pars_fragment>\\n#include <roughnessmap_pars_fragment>\\n#include <metalnessmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <alphahash_fragment>\\n\\t#include <roughnessmap_fragment>\\n\\t#include <metalnessmap_fragment>\\n\\t#include <normal_fragment_begin>\\n\\t#include <normal_fragment_maps>\\n\\t#include <clearcoat_normal_fragment_begin>\\n\\t#include <clearcoat_normal_fragment_maps>\\n\\t#include <emissivemap_fragment>\\n\\t#include <lights_physical_fragment>\\n\\t#include <lights_fragment_begin>\\n\\t#include <lights_fragment_maps>\\n\\t#include <lights_fragment_end>\\n\\t#include <aomap_fragment>\\n\\tvec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;\\n\\tvec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;\\n\\t#include <transmission_fragment>\\n\\tvec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;\\n\\t#ifdef USE_SHEEN\\n\\t\\tfloat sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );\\n\\t\\toutgoingLight = outgoingLight * sheenEnergyComp + sheenSpecularDirect + sheenSpecularIndirect;\\n\\t#endif\\n\\t#ifdef USE_CLEARCOAT\\n\\t\\tfloat dotNVcc = saturate( dot( geometryClearcoatNormal, geometryViewDir ) );\\n\\t\\tvec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );\\n\\t\\toutgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + ( clearcoatSpecularDirect + clearcoatSpecularIndirect ) * material.clearcoat;\\n\\t#endif\\n\\t#include <opaque_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <dithering_fragment>\\n}\";\n\nconst vertex$4 = \"#define TOON\\nvarying vec3 vViewPosition;\\n#include <common>\\n#include <batching_pars_vertex>\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <normal_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <color_vertex>\\n\\t#include <morphcolor_vertex>\\n\\t#include <batching_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#include <normal_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvViewPosition = - mvPosition.xyz;\\n\\t#include <worldpos_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\";\n\nconst fragment$4 = \"#define TOON\\nuniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <dithering_pars_fragment>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <alphatest_pars_fragment>\\n#include <alphahash_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <gradientmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <bsdfs>\\n#include <lights_pars_begin>\\n#include <normal_pars_fragment>\\n#include <lights_toon_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <alphahash_fragment>\\n\\t#include <normal_fragment_begin>\\n\\t#include <normal_fragment_maps>\\n\\t#include <emissivemap_fragment>\\n\\t#include <lights_toon_fragment>\\n\\t#include <lights_fragment_begin>\\n\\t#include <lights_fragment_maps>\\n\\t#include <lights_fragment_end>\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\\n\\t#include <opaque_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <dithering_fragment>\\n}\";\n\nconst vertex$3 = \"uniform float size;\\nuniform float scale;\\n#include <common>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\n#ifdef USE_POINTS_UV\\n\\tvarying vec2 vUv;\\n\\tuniform mat3 uvTransform;\\n#endif\\nvoid main() {\\n\\t#ifdef USE_POINTS_UV\\n\\t\\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\\n\\t#endif\\n\\t#include <color_vertex>\\n\\t#include <morphcolor_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <project_vertex>\\n\\tgl_PointSize = size;\\n\\t#ifdef USE_SIZEATTENUATION\\n\\t\\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\\n\\t\\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\\n\\t#endif\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <fog_vertex>\\n}\";\n\nconst fragment$3 = \"uniform vec3 diffuse;\\nuniform float opacity;\\n#include <common>\\n#include <color_pars_fragment>\\n#include <map_particle_pars_fragment>\\n#include <alphatest_pars_fragment>\\n#include <alphahash_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec3 outgoingLight = vec3( 0.0 );\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_particle_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <alphahash_fragment>\\n\\toutgoingLight = diffuseColor.rgb;\\n\\t#include <opaque_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n}\";\n\nconst vertex$2 = \"#include <common>\\n#include <batching_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <shadowmap_pars_vertex>\\nvoid main() {\\n\\t#include <batching_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\";\n\nconst fragment$2 = \"uniform vec3 color;\\nuniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <fog_pars_fragment>\\n#include <bsdfs>\\n#include <lights_pars_begin>\\n#include <logdepthbuf_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <shadowmask_pars_fragment>\\nvoid main() {\\n\\t#include <logdepthbuf_fragment>\\n\\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n\\t#include <fog_fragment>\\n}\";\n\nconst vertex$1 = \"uniform float rotation;\\nuniform vec2 center;\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\\n\\tvec2 scale;\\n\\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\\n\\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\\n\\t#ifndef USE_SIZEATTENUATION\\n\\t\\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\\n\\t\\tif ( isPerspective ) scale *= - mvPosition.z;\\n\\t#endif\\n\\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\\n\\tvec2 rotatedPosition;\\n\\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\\n\\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\\n\\tmvPosition.xy += rotatedPosition;\\n\\tgl_Position = projectionMatrix * mvPosition;\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <fog_vertex>\\n}\";\n\nconst fragment$1 = \"uniform vec3 diffuse;\\nuniform float opacity;\\n#include <common>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <alphatest_pars_fragment>\\n#include <alphahash_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec3 outgoingLight = vec3( 0.0 );\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <alphahash_fragment>\\n\\toutgoingLight = diffuseColor.rgb;\\n\\t#include <opaque_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <colorspace_fragment>\\n\\t#include <fog_fragment>\\n}\";\n\nconst ShaderChunk = {\n\talphahash_fragment: alphahash_fragment,\n\talphahash_pars_fragment: alphahash_pars_fragment,\n\talphamap_fragment: alphamap_fragment,\n\talphamap_pars_fragment: alphamap_pars_fragment,\n\talphatest_fragment: alphatest_fragment,\n\talphatest_pars_fragment: alphatest_pars_fragment,\n\taomap_fragment: aomap_fragment,\n\taomap_pars_fragment: aomap_pars_fragment,\n\tbatching_pars_vertex: batching_pars_vertex,\n\tbatching_vertex: batching_vertex,\n\tbegin_vertex: begin_vertex,\n\tbeginnormal_vertex: beginnormal_vertex,\n\tbsdfs: bsdfs,\n\tiridescence_fragment: iridescence_fragment,\n\tbumpmap_pars_fragment: bumpmap_pars_fragment,\n\tclipping_planes_fragment: clipping_planes_fragment,\n\tclipping_planes_pars_fragment: clipping_planes_pars_fragment,\n\tclipping_planes_pars_vertex: clipping_planes_pars_vertex,\n\tclipping_planes_vertex: clipping_planes_vertex,\n\tcolor_fragment: color_fragment,\n\tcolor_pars_fragment: color_pars_fragment,\n\tcolor_pars_vertex: color_pars_vertex,\n\tcolor_vertex: color_vertex,\n\tcommon: common,\n\tcube_uv_reflection_fragment: cube_uv_reflection_fragment,\n\tdefaultnormal_vertex: defaultnormal_vertex,\n\tdisplacementmap_pars_vertex: displacementmap_pars_vertex,\n\tdisplacementmap_vertex: displacementmap_vertex,\n\temissivemap_fragment: emissivemap_fragment,\n\temissivemap_pars_fragment: emissivemap_pars_fragment,\n\tcolorspace_fragment: colorspace_fragment,\n\tcolorspace_pars_fragment: colorspace_pars_fragment,\n\tenvmap_fragment: envmap_fragment,\n\tenvmap_common_pars_fragment: envmap_common_pars_fragment,\n\tenvmap_pars_fragment: envmap_pars_fragment,\n\tenvmap_pars_vertex: envmap_pars_vertex,\n\tenvmap_physical_pars_fragment: envmap_physical_pars_fragment,\n\tenvmap_vertex: envmap_vertex,\n\tfog_vertex: fog_vertex,\n\tfog_pars_vertex: fog_pars_vertex,\n\tfog_fragment: fog_fragment,\n\tfog_pars_fragment: fog_pars_fragment,\n\tgradientmap_pars_fragment: gradientmap_pars_fragment,\n\tlightmap_fragment: lightmap_fragment,\n\tlightmap_pars_fragment: lightmap_pars_fragment,\n\tlights_lambert_fragment: lights_lambert_fragment,\n\tlights_lambert_pars_fragment: lights_lambert_pars_fragment,\n\tlights_pars_begin: lights_pars_begin,\n\tlights_toon_fragment: lights_toon_fragment,\n\tlights_toon_pars_fragment: lights_toon_pars_fragment,\n\tlights_phong_fragment: lights_phong_fragment,\n\tlights_phong_pars_fragment: lights_phong_pars_fragment,\n\tlights_physical_fragment: lights_physical_fragment,\n\tlights_physical_pars_fragment: lights_physical_pars_fragment,\n\tlights_fragment_begin: lights_fragment_begin,\n\tlights_fragment_maps: lights_fragment_maps,\n\tlights_fragment_end: lights_fragment_end,\n\tlogdepthbuf_fragment: logdepthbuf_fragment,\n\tlogdepthbuf_pars_fragment: logdepthbuf_pars_fragment,\n\tlogdepthbuf_pars_vertex: logdepthbuf_pars_vertex,\n\tlogdepthbuf_vertex: logdepthbuf_vertex,\n\tmap_fragment: map_fragment,\n\tmap_pars_fragment: map_pars_fragment,\n\tmap_particle_fragment: map_particle_fragment,\n\tmap_particle_pars_fragment: map_particle_pars_fragment,\n\tmetalnessmap_fragment: metalnessmap_fragment,\n\tmetalnessmap_pars_fragment: metalnessmap_pars_fragment,\n\tmorphcolor_vertex: morphcolor_vertex,\n\tmorphnormal_vertex: morphnormal_vertex,\n\tmorphtarget_pars_vertex: morphtarget_pars_vertex,\n\tmorphtarget_vertex: morphtarget_vertex,\n\tnormal_fragment_begin: normal_fragment_begin,\n\tnormal_fragment_maps: normal_fragment_maps,\n\tnormal_pars_fragment: normal_pars_fragment,\n\tnormal_pars_vertex: normal_pars_vertex,\n\tnormal_vertex: normal_vertex,\n\tnormalmap_pars_fragment: normalmap_pars_fragment,\n\tclearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin,\n\tclearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps,\n\tclearcoat_pars_fragment: clearcoat_pars_fragment,\n\tiridescence_pars_fragment: iridescence_pars_fragment,\n\topaque_fragment: opaque_fragment,\n\tpacking: packing,\n\tpremultiplied_alpha_fragment: premultiplied_alpha_fragment,\n\tproject_vertex: project_vertex,\n\tdithering_fragment: dithering_fragment,\n\tdithering_pars_fragment: dithering_pars_fragment,\n\troughnessmap_fragment: roughnessmap_fragment,\n\troughnessmap_pars_fragment: roughnessmap_pars_fragment,\n\tshadowmap_pars_fragment: shadowmap_pars_fragment,\n\tshadowmap_pars_vertex: shadowmap_pars_vertex,\n\tshadowmap_vertex: shadowmap_vertex,\n\tshadowmask_pars_fragment: shadowmask_pars_fragment,\n\tskinbase_vertex: skinbase_vertex,\n\tskinning_pars_vertex: skinning_pars_vertex,\n\tskinning_vertex: skinning_vertex,\n\tskinnormal_vertex: skinnormal_vertex,\n\tspecularmap_fragment: specularmap_fragment,\n\tspecularmap_pars_fragment: specularmap_pars_fragment,\n\ttonemapping_fragment: tonemapping_fragment,\n\ttonemapping_pars_fragment: tonemapping_pars_fragment,\n\ttransmission_fragment: transmission_fragment,\n\ttransmission_pars_fragment: transmission_pars_fragment,\n\tuv_pars_fragment: uv_pars_fragment,\n\tuv_pars_vertex: uv_pars_vertex,\n\tuv_vertex: uv_vertex,\n\tworldpos_vertex: worldpos_vertex,\n\n\tbackground_vert: vertex$h,\n\tbackground_frag: fragment$h,\n\tbackgroundCube_vert: vertex$g,\n\tbackgroundCube_frag: fragment$g,\n\tcube_vert: vertex$f,\n\tcube_frag: fragment$f,\n\tdepth_vert: vertex$e,\n\tdepth_frag: fragment$e,\n\tdistanceRGBA_vert: vertex$d,\n\tdistanceRGBA_frag: fragment$d,\n\tequirect_vert: vertex$c,\n\tequirect_frag: fragment$c,\n\tlinedashed_vert: vertex$b,\n\tlinedashed_frag: fragment$b,\n\tmeshbasic_vert: vertex$a,\n\tmeshbasic_frag: fragment$a,\n\tmeshlambert_vert: vertex$9,\n\tmeshlambert_frag: fragment$9,\n\tmeshmatcap_vert: vertex$8,\n\tmeshmatcap_frag: fragment$8,\n\tmeshnormal_vert: vertex$7,\n\tmeshnormal_frag: fragment$7,\n\tmeshphong_vert: vertex$6,\n\tmeshphong_frag: fragment$6,\n\tmeshphysical_vert: vertex$5,\n\tmeshphysical_frag: fragment$5,\n\tmeshtoon_vert: vertex$4,\n\tmeshtoon_frag: fragment$4,\n\tpoints_vert: vertex$3,\n\tpoints_frag: fragment$3,\n\tshadow_vert: vertex$2,\n\tshadow_frag: fragment$2,\n\tsprite_vert: vertex$1,\n\tsprite_frag: fragment$1\n};\n\n/**\n * Uniforms library for shared webgl shaders\n */\n\nconst UniformsLib = {\n\n\tcommon: {\n\n\t\tdiffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) },\n\t\topacity: { value: 1.0 },\n\n\t\tmap: { value: null },\n\t\tmapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\n\t\talphaMap: { value: null },\n\t\talphaMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\n\t\talphaTest: { value: 0 }\n\n\t},\n\n\tspecularmap: {\n\n\t\tspecularMap: { value: null },\n\t\tspecularMapTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\tenvmap: {\n\n\t\tenvMap: { value: null },\n\t\tflipEnvMap: { value: - 1 },\n\t\treflectivity: { value: 1.0 }, // basic, lambert, phong\n\t\tior: { value: 1.5 }, // physical\n\t\trefractionRatio: { value: 0.98 }, // basic, lambert, phong\n\n\t},\n\n\taomap: {\n\n\t\taoMap: { value: null },\n\t\taoMapIntensity: { value: 1 },\n\t\taoMapTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\tlightmap: {\n\n\t\tlightMap: { value: null },\n\t\tlightMapIntensity: { value: 1 },\n\t\tlightMapTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\tbumpmap: {\n\n\t\tbumpMap: { value: null },\n\t\tbumpMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\tbumpScale: { value: 1 }\n\n\t},\n\n\tnormalmap: {\n\n\t\tnormalMap: { value: null },\n\t\tnormalMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\tnormalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) }\n\n\t},\n\n\tdisplacementmap: {\n\n\t\tdisplacementMap: { value: null },\n\t\tdisplacementMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\tdisplacementScale: { value: 1 },\n\t\tdisplacementBias: { value: 0 }\n\n\t},\n\n\temissivemap: {\n\n\t\temissiveMap: { value: null },\n\t\temissiveMapTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\tmetalnessmap: {\n\n\t\tmetalnessMap: { value: null },\n\t\tmetalnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\troughnessmap: {\n\n\t\troughnessMap: { value: null },\n\t\troughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\tgradientmap: {\n\n\t\tgradientMap: { value: null }\n\n\t},\n\n\tfog: {\n\n\t\tfogDensity: { value: 0.00025 },\n\t\tfogNear: { value: 1 },\n\t\tfogFar: { value: 2000 },\n\t\tfogColor: { value: /*@__PURE__*/ new Color( 0xffffff ) }\n\n\t},\n\n\tlights: {\n\n\t\tambientLightColor: { value: [] },\n\n\t\tlightProbe: { value: [] },\n\n\t\tdirectionalLights: { value: [], properties: {\n\t\t\tdirection: {},\n\t\t\tcolor: {}\n\t\t} },\n\n\t\tdirectionalLightShadows: { value: [], properties: {\n\t\t\tshadowBias: {},\n\t\t\tshadowNormalBias: {},\n\t\t\tshadowRadius: {},\n\t\t\tshadowMapSize: {}\n\t\t} },\n\n\t\tdirectionalShadowMap: { value: [] },\n\t\tdirectionalShadowMatrix: { value: [] },\n\n\t\tspotLights: { value: [], properties: {\n\t\t\tcolor: {},\n\t\t\tposition: {},\n\t\t\tdirection: {},\n\t\t\tdistance: {},\n\t\t\tconeCos: {},\n\t\t\tpenumbraCos: {},\n\t\t\tdecay: {}\n\t\t} },\n\n\t\tspotLightShadows: { value: [], properties: {\n\t\t\tshadowBias: {},\n\t\t\tshadowNormalBias: {},\n\t\t\tshadowRadius: {},\n\t\t\tshadowMapSize: {}\n\t\t} },\n\n\t\tspotLightMap: { value: [] },\n\t\tspotShadowMap: { value: [] },\n\t\tspotLightMatrix: { value: [] },\n\n\t\tpointLights: { value: [], properties: {\n\t\t\tcolor: {},\n\t\t\tposition: {},\n\t\t\tdecay: {},\n\t\t\tdistance: {}\n\t\t} },\n\n\t\tpointLightShadows: { value: [], properties: {\n\t\t\tshadowBias: {},\n\t\t\tshadowNormalBias: {},\n\t\t\tshadowRadius: {},\n\t\t\tshadowMapSize: {},\n\t\t\tshadowCameraNear: {},\n\t\t\tshadowCameraFar: {}\n\t\t} },\n\n\t\tpointShadowMap: { value: [] },\n\t\tpointShadowMatrix: { value: [] },\n\n\t\themisphereLights: { value: [], properties: {\n\t\t\tdirection: {},\n\t\t\tskyColor: {},\n\t\t\tgroundColor: {}\n\t\t} },\n\n\t\t// TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src\n\t\trectAreaLights: { value: [], properties: {\n\t\t\tcolor: {},\n\t\t\tposition: {},\n\t\t\twidth: {},\n\t\t\theight: {}\n\t\t} },\n\n\t\tltc_1: { value: null },\n\t\tltc_2: { value: null }\n\n\t},\n\n\tpoints: {\n\n\t\tdiffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) },\n\t\topacity: { value: 1.0 },\n\t\tsize: { value: 1.0 },\n\t\tscale: { value: 1.0 },\n\t\tmap: { value: null },\n\t\talphaMap: { value: null },\n\t\talphaMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\talphaTest: { value: 0 },\n\t\tuvTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\tsprite: {\n\n\t\tdiffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) },\n\t\topacity: { value: 1.0 },\n\t\tcenter: { value: /*@__PURE__*/ new Vector2( 0.5, 0.5 ) },\n\t\trotation: { value: 0.0 },\n\t\tmap: { value: null },\n\t\tmapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\talphaMap: { value: null },\n\t\talphaMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\talphaTest: { value: 0 }\n\n\t}\n\n};\n\nconst ShaderLib = {\n\n\tbasic: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.specularmap,\n\t\t\tUniformsLib.envmap,\n\t\t\tUniformsLib.aomap,\n\t\t\tUniformsLib.lightmap,\n\t\t\tUniformsLib.fog\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshbasic_vert,\n\t\tfragmentShader: ShaderChunk.meshbasic_frag\n\n\t},\n\n\tlambert: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.specularmap,\n\t\t\tUniformsLib.envmap,\n\t\t\tUniformsLib.aomap,\n\t\t\tUniformsLib.lightmap,\n\t\t\tUniformsLib.emissivemap,\n\t\t\tUniformsLib.bumpmap,\n\t\t\tUniformsLib.normalmap,\n\t\t\tUniformsLib.displacementmap,\n\t\t\tUniformsLib.fog,\n\t\t\tUniformsLib.lights,\n\t\t\t{\n\t\t\t\temissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshlambert_vert,\n\t\tfragmentShader: ShaderChunk.meshlambert_frag\n\n\t},\n\n\tphong: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.specularmap,\n\t\t\tUniformsLib.envmap,\n\t\t\tUniformsLib.aomap,\n\t\t\tUniformsLib.lightmap,\n\t\t\tUniformsLib.emissivemap,\n\t\t\tUniformsLib.bumpmap,\n\t\t\tUniformsLib.normalmap,\n\t\t\tUniformsLib.displacementmap,\n\t\t\tUniformsLib.fog,\n\t\t\tUniformsLib.lights,\n\t\t\t{\n\t\t\t\temissive: { value: /*@__PURE__*/ new Color( 0x000000 ) },\n\t\t\t\tspecular: { value: /*@__PURE__*/ new Color( 0x111111 ) },\n\t\t\t\tshininess: { value: 30 }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshphong_vert,\n\t\tfragmentShader: ShaderChunk.meshphong_frag\n\n\t},\n\n\tstandard: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.envmap,\n\t\t\tUniformsLib.aomap,\n\t\t\tUniformsLib.lightmap,\n\t\t\tUniformsLib.emissivemap,\n\t\t\tUniformsLib.bumpmap,\n\t\t\tUniformsLib.normalmap,\n\t\t\tUniformsLib.displacementmap,\n\t\t\tUniformsLib.roughnessmap,\n\t\t\tUniformsLib.metalnessmap,\n\t\t\tUniformsLib.fog,\n\t\t\tUniformsLib.lights,\n\t\t\t{\n\t\t\t\temissive: { value: /*@__PURE__*/ new Color( 0x000000 ) },\n\t\t\t\troughness: { value: 1.0 },\n\t\t\t\tmetalness: { value: 0.0 },\n\t\t\t\tenvMapIntensity: { value: 1 } // temporary\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshphysical_vert,\n\t\tfragmentShader: ShaderChunk.meshphysical_frag\n\n\t},\n\n\ttoon: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.aomap,\n\t\t\tUniformsLib.lightmap,\n\t\t\tUniformsLib.emissivemap,\n\t\t\tUniformsLib.bumpmap,\n\t\t\tUniformsLib.normalmap,\n\t\t\tUniformsLib.displacementmap,\n\t\t\tUniformsLib.gradientmap,\n\t\t\tUniformsLib.fog,\n\t\t\tUniformsLib.lights,\n\t\t\t{\n\t\t\t\temissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshtoon_vert,\n\t\tfragmentShader: ShaderChunk.meshtoon_frag\n\n\t},\n\n\tmatcap: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.bumpmap,\n\t\t\tUniformsLib.normalmap,\n\t\t\tUniformsLib.displacementmap,\n\t\t\tUniformsLib.fog,\n\t\t\t{\n\t\t\t\tmatcap: { value: null }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshmatcap_vert,\n\t\tfragmentShader: ShaderChunk.meshmatcap_frag\n\n\t},\n\n\tpoints: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.points,\n\t\t\tUniformsLib.fog\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.points_vert,\n\t\tfragmentShader: ShaderChunk.points_frag\n\n\t},\n\n\tdashed: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.fog,\n\t\t\t{\n\t\t\t\tscale: { value: 1 },\n\t\t\t\tdashSize: { value: 1 },\n\t\t\t\ttotalSize: { value: 2 }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.linedashed_vert,\n\t\tfragmentShader: ShaderChunk.linedashed_frag\n\n\t},\n\n\tdepth: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.displacementmap\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.depth_vert,\n\t\tfragmentShader: ShaderChunk.depth_frag\n\n\t},\n\n\tnormal: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.bumpmap,\n\t\t\tUniformsLib.normalmap,\n\t\t\tUniformsLib.displacementmap,\n\t\t\t{\n\t\t\t\topacity: { value: 1.0 }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshnormal_vert,\n\t\tfragmentShader: ShaderChunk.meshnormal_frag\n\n\t},\n\n\tsprite: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.sprite,\n\t\t\tUniformsLib.fog\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.sprite_vert,\n\t\tfragmentShader: ShaderChunk.sprite_frag\n\n\t},\n\n\tbackground: {\n\n\t\tuniforms: {\n\t\t\tuvTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tt2D: { value: null },\n\t\t\tbackgroundIntensity: { value: 1 }\n\t\t},\n\n\t\tvertexShader: ShaderChunk.background_vert,\n\t\tfragmentShader: ShaderChunk.background_frag\n\n\t},\n\n\tbackgroundCube: {\n\n\t\tuniforms: {\n\t\t\tenvMap: { value: null },\n\t\t\tflipEnvMap: { value: - 1 },\n\t\t\tbackgroundBlurriness: { value: 0 },\n\t\t\tbackgroundIntensity: { value: 1 }\n\t\t},\n\n\t\tvertexShader: ShaderChunk.backgroundCube_vert,\n\t\tfragmentShader: ShaderChunk.backgroundCube_frag\n\n\t},\n\n\tcube: {\n\n\t\tuniforms: {\n\t\t\ttCube: { value: null },\n\t\t\ttFlip: { value: - 1 },\n\t\t\topacity: { value: 1.0 }\n\t\t},\n\n\t\tvertexShader: ShaderChunk.cube_vert,\n\t\tfragmentShader: ShaderChunk.cube_frag\n\n\t},\n\n\tequirect: {\n\n\t\tuniforms: {\n\t\t\ttEquirect: { value: null },\n\t\t},\n\n\t\tvertexShader: ShaderChunk.equirect_vert,\n\t\tfragmentShader: ShaderChunk.equirect_frag\n\n\t},\n\n\tdistanceRGBA: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.displacementmap,\n\t\t\t{\n\t\t\t\treferencePosition: { value: /*@__PURE__*/ new Vector3() },\n\t\t\t\tnearDistance: { value: 1 },\n\t\t\t\tfarDistance: { value: 1000 }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.distanceRGBA_vert,\n\t\tfragmentShader: ShaderChunk.distanceRGBA_frag\n\n\t},\n\n\tshadow: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.lights,\n\t\t\tUniformsLib.fog,\n\t\t\t{\n\t\t\t\tcolor: { value: /*@__PURE__*/ new Color( 0x00000 ) },\n\t\t\t\topacity: { value: 1.0 }\n\t\t\t},\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.shadow_vert,\n\t\tfragmentShader: ShaderChunk.shadow_frag\n\n\t}\n\n};\n\nShaderLib.physical = {\n\n\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\tShaderLib.standard.uniforms,\n\t\t{\n\t\t\tclearcoat: { value: 0 },\n\t\t\tclearcoatMap: { value: null },\n\t\t\tclearcoatMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tclearcoatNormalMap: { value: null },\n\t\t\tclearcoatNormalMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tclearcoatNormalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) },\n\t\t\tclearcoatRoughness: { value: 0 },\n\t\t\tclearcoatRoughnessMap: { value: null },\n\t\t\tclearcoatRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tiridescence: { value: 0 },\n\t\t\tiridescenceMap: { value: null },\n\t\t\tiridescenceMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tiridescenceIOR: { value: 1.3 },\n\t\t\tiridescenceThicknessMinimum: { value: 100 },\n\t\t\tiridescenceThicknessMaximum: { value: 400 },\n\t\t\tiridescenceThicknessMap: { value: null },\n\t\t\tiridescenceThicknessMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tsheen: { value: 0 },\n\t\t\tsheenColor: { value: /*@__PURE__*/ new Color( 0x000000 ) },\n\t\t\tsheenColorMap: { value: null },\n\t\t\tsheenColorMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tsheenRoughness: { value: 1 },\n\t\t\tsheenRoughnessMap: { value: null },\n\t\t\tsheenRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\ttransmission: { value: 0 },\n\t\t\ttransmissionMap: { value: null },\n\t\t\ttransmissionMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\ttransmissionSamplerSize: { value: /*@__PURE__*/ new Vector2() },\n\t\t\ttransmissionSamplerMap: { value: null },\n\t\t\tthickness: { value: 0 },\n\t\t\tthicknessMap: { value: null },\n\t\t\tthicknessMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tattenuationDistance: { value: 0 },\n\t\t\tattenuationColor: { value: /*@__PURE__*/ new Color( 0x000000 ) },\n\t\t\tspecularColor: { value: /*@__PURE__*/ new Color( 1, 1, 1 ) },\n\t\t\tspecularColorMap: { value: null },\n\t\t\tspecularColorMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tspecularIntensity: { value: 1 },\n\t\t\tspecularIntensityMap: { value: null },\n\t\t\tspecularIntensityMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tanisotropyVector: { value: /*@__PURE__*/ new Vector2() },\n\t\t\tanisotropyMap: { value: null },\n\t\t\tanisotropyMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t}\n\t] ),\n\n\tvertexShader: ShaderChunk.meshphysical_vert,\n\tfragmentShader: ShaderChunk.meshphysical_frag\n\n};\n\nconst _rgb = { r: 0, b: 0, g: 0 };\n\nfunction WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha, premultipliedAlpha ) {\n\n\tconst clearColor = new Color( 0x000000 );\n\tlet clearAlpha = alpha === true ? 0 : 1;\n\n\tlet planeMesh;\n\tlet boxMesh;\n\n\tlet currentBackground = null;\n\tlet currentBackgroundVersion = 0;\n\tlet currentTonemapping = null;\n\n\tfunction render( renderList, scene ) {\n\n\t\tlet forceClear = false;\n\t\tlet background = scene.isScene === true ? scene.background : null;\n\n\t\tif ( background && background.isTexture ) {\n\n\t\t\tconst usePMREM = scene.backgroundBlurriness > 0; // use PMREM if the user wants to blur the background\n\t\t\tbackground = ( usePMREM ? cubeuvmaps : cubemaps ).get( background );\n\n\t\t}\n\n\t\tif ( background === null ) {\n\n\t\t\tsetClear( clearColor, clearAlpha );\n\n\t\t} else if ( background && background.isColor ) {\n\n\t\t\tsetClear( background, 1 );\n\t\t\tforceClear = true;\n\n\t\t}\n\n\t\tconst environmentBlendMode = renderer.xr.getEnvironmentBlendMode();\n\n\t\tif ( environmentBlendMode === 'additive' ) {\n\n\t\t\tstate.buffers.color.setClear( 0, 0, 0, 1, premultipliedAlpha );\n\n\t\t} else if ( environmentBlendMode === 'alpha-blend' ) {\n\n\t\t\tstate.buffers.color.setClear( 0, 0, 0, 0, premultipliedAlpha );\n\n\t\t}\n\n\t\tif ( renderer.autoClear || forceClear ) {\n\n\t\t\trenderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );\n\n\t\t}\n\n\t\tif ( background && ( background.isCubeTexture || background.mapping === CubeUVReflectionMapping ) ) {\n\n\t\t\tif ( boxMesh === undefined ) {\n\n\t\t\t\tboxMesh = new Mesh(\n\t\t\t\t\tnew BoxGeometry( 1, 1, 1 ),\n\t\t\t\t\tnew ShaderMaterial( {\n\t\t\t\t\t\tname: 'BackgroundCubeMaterial',\n\t\t\t\t\t\tuniforms: cloneUniforms( ShaderLib.backgroundCube.uniforms ),\n\t\t\t\t\t\tvertexShader: ShaderLib.backgroundCube.vertexShader,\n\t\t\t\t\t\tfragmentShader: ShaderLib.backgroundCube.fragmentShader,\n\t\t\t\t\t\tside: BackSide,\n\t\t\t\t\t\tdepthTest: false,\n\t\t\t\t\t\tdepthWrite: false,\n\t\t\t\t\t\tfog: false\n\t\t\t\t\t} )\n\t\t\t\t);\n\n\t\t\t\tboxMesh.geometry.deleteAttribute( 'normal' );\n\t\t\t\tboxMesh.geometry.deleteAttribute( 'uv' );\n\n\t\t\t\tboxMesh.onBeforeRender = function ( renderer, scene, camera ) {\n\n\t\t\t\t\tthis.matrixWorld.copyPosition( camera.matrixWorld );\n\n\t\t\t\t};\n\n\t\t\t\t// add \"envMap\" material property so the renderer can evaluate it like for built-in materials\n\t\t\t\tObject.defineProperty( boxMesh.material, 'envMap', {\n\n\t\t\t\t\tget: function () {\n\n\t\t\t\t\t\treturn this.uniforms.envMap.value;\n\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\n\t\t\t\tobjects.update( boxMesh );\n\n\t\t\t}\n\n\t\t\tboxMesh.material.uniforms.envMap.value = background;\n\t\t\tboxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1;\n\t\t\tboxMesh.material.uniforms.backgroundBlurriness.value = scene.backgroundBlurriness;\n\t\t\tboxMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity;\n\t\t\tboxMesh.material.toneMapped = ColorManagement.getTransfer( background.colorSpace ) !== SRGBTransfer;\n\n\t\t\tif ( currentBackground !== background ||\n\t\t\t\tcurrentBackgroundVersion !== background.version ||\n\t\t\t\tcurrentTonemapping !== renderer.toneMapping ) {\n\n\t\t\t\tboxMesh.material.needsUpdate = true;\n\n\t\t\t\tcurrentBackground = background;\n\t\t\t\tcurrentBackgroundVersion = background.version;\n\t\t\t\tcurrentTonemapping = renderer.toneMapping;\n\n\t\t\t}\n\n\t\t\tboxMesh.layers.enableAll();\n\n\t\t\t// push to the pre-sorted opaque render list\n\t\t\trenderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null );\n\n\t\t} else if ( background && background.isTexture ) {\n\n\t\t\tif ( planeMesh === undefined ) {\n\n\t\t\t\tplaneMesh = new Mesh(\n\t\t\t\t\tnew PlaneGeometry( 2, 2 ),\n\t\t\t\t\tnew ShaderMaterial( {\n\t\t\t\t\t\tname: 'BackgroundMaterial',\n\t\t\t\t\t\tuniforms: cloneUniforms( ShaderLib.background.uniforms ),\n\t\t\t\t\t\tvertexShader: ShaderLib.background.vertexShader,\n\t\t\t\t\t\tfragmentShader: ShaderLib.background.fragmentShader,\n\t\t\t\t\t\tside: FrontSide,\n\t\t\t\t\t\tdepthTest: false,\n\t\t\t\t\t\tdepthWrite: false,\n\t\t\t\t\t\tfog: false\n\t\t\t\t\t} )\n\t\t\t\t);\n\n\t\t\t\tplaneMesh.geometry.deleteAttribute( 'normal' );\n\n\t\t\t\t// add \"map\" material property so the renderer can evaluate it like for built-in materials\n\t\t\t\tObject.defineProperty( planeMesh.material, 'map', {\n\n\t\t\t\t\tget: function () {\n\n\t\t\t\t\t\treturn this.uniforms.t2D.value;\n\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\n\t\t\t\tobjects.update( planeMesh );\n\n\t\t\t}\n\n\t\t\tplaneMesh.material.uniforms.t2D.value = background;\n\t\t\tplaneMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity;\n\t\t\tplaneMesh.material.toneMapped = ColorManagement.getTransfer( background.colorSpace ) !== SRGBTransfer;\n\n\t\t\tif ( background.matrixAutoUpdate === true ) {\n\n\t\t\t\tbackground.updateMatrix();\n\n\t\t\t}\n\n\t\t\tplaneMesh.material.uniforms.uvTransform.value.copy( background.matrix );\n\n\t\t\tif ( currentBackground !== background ||\n\t\t\t\tcurrentBackgroundVersion !== background.version ||\n\t\t\t\tcurrentTonemapping !== renderer.toneMapping ) {\n\n\t\t\t\tplaneMesh.material.needsUpdate = true;\n\n\t\t\t\tcurrentBackground = background;\n\t\t\t\tcurrentBackgroundVersion = background.version;\n\t\t\t\tcurrentTonemapping = renderer.toneMapping;\n\n\t\t\t}\n\n\t\t\tplaneMesh.layers.enableAll();\n\n\t\t\t// push to the pre-sorted opaque render list\n\t\t\trenderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null );\n\n\t\t}\n\n\t}\n\n\tfunction setClear( color, alpha ) {\n\n\t\tcolor.getRGB( _rgb, getUnlitUniformColorSpace( renderer ) );\n\n\t\tstate.buffers.color.setClear( _rgb.r, _rgb.g, _rgb.b, alpha, premultipliedAlpha );\n\n\t}\n\n\treturn {\n\n\t\tgetClearColor: function () {\n\n\t\t\treturn clearColor;\n\n\t\t},\n\t\tsetClearColor: function ( color, alpha = 1 ) {\n\n\t\t\tclearColor.set( color );\n\t\t\tclearAlpha = alpha;\n\t\t\tsetClear( clearColor, clearAlpha );\n\n\t\t},\n\t\tgetClearAlpha: function () {\n\n\t\t\treturn clearAlpha;\n\n\t\t},\n\t\tsetClearAlpha: function ( alpha ) {\n\n\t\t\tclearAlpha = alpha;\n\t\t\tsetClear( clearColor, clearAlpha );\n\n\t\t},\n\t\trender: render\n\n\t};\n\n}\n\nfunction WebGLBindingStates( gl, extensions, attributes, capabilities ) {\n\n\tconst maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );\n\n\tconst extension = capabilities.isWebGL2 ? null : extensions.get( 'OES_vertex_array_object' );\n\tconst vaoAvailable = capabilities.isWebGL2 || extension !== null;\n\n\tconst bindingStates = {};\n\n\tconst defaultState = createBindingState( null );\n\tlet currentState = defaultState;\n\tlet forceUpdate = false;\n\n\tfunction setup( object, material, program, geometry, index ) {\n\n\t\tlet updateBuffers = false;\n\n\t\tif ( vaoAvailable ) {\n\n\t\t\tconst state = getBindingState( geometry, program, material );\n\n\t\t\tif ( currentState !== state ) {\n\n\t\t\t\tcurrentState = state;\n\t\t\t\tbindVertexArrayObject( currentState.object );\n\n\t\t\t}\n\n\t\t\tupdateBuffers = needsUpdate( object, geometry, program, index );\n\n\t\t\tif ( updateBuffers ) saveCache( object, geometry, program, index );\n\n\t\t} else {\n\n\t\t\tconst wireframe = ( material.wireframe === true );\n\n\t\t\tif ( currentState.geometry !== geometry.id ||\n\t\t\t\tcurrentState.program !== program.id ||\n\t\t\t\tcurrentState.wireframe !== wireframe ) {\n\n\t\t\t\tcurrentState.geometry = geometry.id;\n\t\t\t\tcurrentState.program = program.id;\n\t\t\t\tcurrentState.wireframe = wireframe;\n\n\t\t\t\tupdateBuffers = true;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( index !== null ) {\n\n\t\t\tattributes.update( index, gl.ELEMENT_ARRAY_BUFFER );\n\n\t\t}\n\n\t\tif ( updateBuffers || forceUpdate ) {\n\n\t\t\tforceUpdate = false;\n\n\t\t\tsetupVertexAttributes( object, material, program, geometry );\n\n\t\t\tif ( index !== null ) {\n\n\t\t\t\tgl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, attributes.get( index ).buffer );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tfunction createVertexArrayObject() {\n\n\t\tif ( capabilities.isWebGL2 ) return gl.createVertexArray();\n\n\t\treturn extension.createVertexArrayOES();\n\n\t}\n\n\tfunction bindVertexArrayObject( vao ) {\n\n\t\tif ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao );\n\n\t\treturn extension.bindVertexArrayOES( vao );\n\n\t}\n\n\tfunction deleteVertexArrayObject( vao ) {\n\n\t\tif ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao );\n\n\t\treturn extension.deleteVertexArrayOES( vao );\n\n\t}\n\n\tfunction getBindingState( geometry, program, material ) {\n\n\t\tconst wireframe = ( material.wireframe === true );\n\n\t\tlet programMap = bindingStates[ geometry.id ];\n\n\t\tif ( programMap === undefined ) {\n\n\t\t\tprogramMap = {};\n\t\t\tbindingStates[ geometry.id ] = programMap;\n\n\t\t}\n\n\t\tlet stateMap = programMap[ program.id ];\n\n\t\tif ( stateMap === undefined ) {\n\n\t\t\tstateMap = {};\n\t\t\tprogramMap[ program.id ] = stateMap;\n\n\t\t}\n\n\t\tlet state = stateMap[ wireframe ];\n\n\t\tif ( state === undefined ) {\n\n\t\t\tstate = createBindingState( createVertexArrayObject() );\n\t\t\tstateMap[ wireframe ] = state;\n\n\t\t}\n\n\t\treturn state;\n\n\t}\n\n\tfunction createBindingState( vao ) {\n\n\t\tconst newAttributes = [];\n\t\tconst enabledAttributes = [];\n\t\tconst attributeDivisors = [];\n\n\t\tfor ( let i = 0; i < maxVertexAttributes; i ++ ) {\n\n\t\t\tnewAttributes[ i ] = 0;\n\t\t\tenabledAttributes[ i ] = 0;\n\t\t\tattributeDivisors[ i ] = 0;\n\n\t\t}\n\n\t\treturn {\n\n\t\t\t// for backward compatibility on non-VAO support browser\n\t\t\tgeometry: null,\n\t\t\tprogram: null,\n\t\t\twireframe: false,\n\n\t\t\tnewAttributes: newAttributes,\n\t\t\tenabledAttributes: enabledAttributes,\n\t\t\tattributeDivisors: attributeDivisors,\n\t\t\tobject: vao,\n\t\t\tattributes: {},\n\t\t\tindex: null\n\n\t\t};\n\n\t}\n\n\tfunction needsUpdate( object, geometry, program, index ) {\n\n\t\tconst cachedAttributes = currentState.attributes;\n\t\tconst geometryAttributes = geometry.attributes;\n\n\t\tlet attributesNum = 0;\n\n\t\tconst programAttributes = program.getAttributes();\n\n\t\tfor ( const name in programAttributes ) {\n\n\t\t\tconst programAttribute = programAttributes[ name ];\n\n\t\t\tif ( programAttribute.location >= 0 ) {\n\n\t\t\t\tconst cachedAttribute = cachedAttributes[ name ];\n\t\t\t\tlet geometryAttribute = geometryAttributes[ name ];\n\n\t\t\t\tif ( geometryAttribute === undefined ) {\n\n\t\t\t\t\tif ( name === 'instanceMatrix' && object.instanceMatrix ) geometryAttribute = object.instanceMatrix;\n\t\t\t\t\tif ( name === 'instanceColor' && object.instanceColor ) geometryAttribute = object.instanceColor;\n\n\t\t\t\t}\n\n\t\t\t\tif ( cachedAttribute === undefined ) return true;\n\n\t\t\t\tif ( cachedAttribute.attribute !== geometryAttribute ) return true;\n\n\t\t\t\tif ( geometryAttribute && cachedAttribute.data !== geometryAttribute.data ) return true;\n\n\t\t\t\tattributesNum ++;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( currentState.attributesNum !== attributesNum ) return true;\n\n\t\tif ( currentState.index !== index ) return true;\n\n\t\treturn false;\n\n\t}\n\n\tfunction saveCache( object, geometry, program, index ) {\n\n\t\tconst cache = {};\n\t\tconst attributes = geometry.attributes;\n\t\tlet attributesNum = 0;\n\n\t\tconst programAttributes = program.getAttributes();\n\n\t\tfor ( const name in programAttributes ) {\n\n\t\t\tconst programAttribute = programAttributes[ name ];\n\n\t\t\tif ( programAttribute.location >= 0 ) {\n\n\t\t\t\tlet attribute = attributes[ name ];\n\n\t\t\t\tif ( attribute === undefined ) {\n\n\t\t\t\t\tif ( name === 'instanceMatrix' && object.instanceMatrix ) attribute = object.instanceMatrix;\n\t\t\t\t\tif ( name === 'instanceColor' && object.instanceColor ) attribute = object.instanceColor;\n\n\t\t\t\t}\n\n\t\t\t\tconst data = {};\n\t\t\t\tdata.attribute = attribute;\n\n\t\t\t\tif ( attribute && attribute.data ) {\n\n\t\t\t\t\tdata.data = attribute.data;\n\n\t\t\t\t}\n\n\t\t\t\tcache[ name ] = data;\n\n\t\t\t\tattributesNum ++;\n\n\t\t\t}\n\n\t\t}\n\n\t\tcurrentState.attributes = cache;\n\t\tcurrentState.attributesNum = attributesNum;\n\n\t\tcurrentState.index = index;\n\n\t}\n\n\tfunction initAttributes() {\n\n\t\tconst newAttributes = currentState.newAttributes;\n\n\t\tfor ( let i = 0, il = newAttributes.length; i < il; i ++ ) {\n\n\t\t\tnewAttributes[ i ] = 0;\n\n\t\t}\n\n\t}\n\n\tfunction enableAttribute( attribute ) {\n\n\t\tenableAttributeAndDivisor( attribute, 0 );\n\n\t}\n\n\tfunction enableAttributeAndDivisor( attribute, meshPerAttribute ) {\n\n\t\tconst newAttributes = currentState.newAttributes;\n\t\tconst enabledAttributes = currentState.enabledAttributes;\n\t\tconst attributeDivisors = currentState.attributeDivisors;\n\n\t\tnewAttributes[ attribute ] = 1;\n\n\t\tif ( enabledAttributes[ attribute ] === 0 ) {\n\n\t\t\tgl.enableVertexAttribArray( attribute );\n\t\t\tenabledAttributes[ attribute ] = 1;\n\n\t\t}\n\n\t\tif ( attributeDivisors[ attribute ] !== meshPerAttribute ) {\n\n\t\t\tconst extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' );\n\n\t\t\textension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute );\n\t\t\tattributeDivisors[ attribute ] = meshPerAttribute;\n\n\t\t}\n\n\t}\n\n\tfunction disableUnusedAttributes() {\n\n\t\tconst newAttributes = currentState.newAttributes;\n\t\tconst enabledAttributes = currentState.enabledAttributes;\n\n\t\tfor ( let i = 0, il = enabledAttributes.length; i < il; i ++ ) {\n\n\t\t\tif ( enabledAttributes[ i ] !== newAttributes[ i ] ) {\n\n\t\t\t\tgl.disableVertexAttribArray( i );\n\t\t\t\tenabledAttributes[ i ] = 0;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tfunction vertexAttribPointer( index, size, type, normalized, stride, offset, integer ) {\n\n\t\tif ( integer === true ) {\n\n\t\t\tgl.vertexAttribIPointer( index, size, type, stride, offset );\n\n\t\t} else {\n\n\t\t\tgl.vertexAttribPointer( index, size, type, normalized, stride, offset );\n\n\t\t}\n\n\t}\n\n\tfunction setupVertexAttributes( object, material, program, geometry ) {\n\n\t\tif ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) {\n\n\t\t\tif ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) return;\n\n\t\t}\n\n\t\tinitAttributes();\n\n\t\tconst geometryAttributes = geometry.attributes;\n\n\t\tconst programAttributes = program.getAttributes();\n\n\t\tconst materialDefaultAttributeValues = material.defaultAttributeValues;\n\n\t\tfor ( const name in programAttributes ) {\n\n\t\t\tconst programAttribute = programAttributes[ name ];\n\n\t\t\tif ( programAttribute.location >= 0 ) {\n\n\t\t\t\tlet geometryAttribute = geometryAttributes[ name ];\n\n\t\t\t\tif ( geometryAttribute === undefined ) {\n\n\t\t\t\t\tif ( name === 'instanceMatrix' && object.instanceMatrix ) geometryAttribute = object.instanceMatrix;\n\t\t\t\t\tif ( name === 'instanceColor' && object.instanceColor ) geometryAttribute = object.instanceColor;\n\n\t\t\t\t}\n\n\t\t\t\tif ( geometryAttribute !== undefined ) {\n\n\t\t\t\t\tconst normalized = geometryAttribute.normalized;\n\t\t\t\t\tconst size = geometryAttribute.itemSize;\n\n\t\t\t\t\tconst attribute = attributes.get( geometryAttribute );\n\n\t\t\t\t\t// TODO Attribute may not be available on context restore\n\n\t\t\t\t\tif ( attribute === undefined ) continue;\n\n\t\t\t\t\tconst buffer = attribute.buffer;\n\t\t\t\t\tconst type = attribute.type;\n\t\t\t\t\tconst bytesPerElement = attribute.bytesPerElement;\n\n\t\t\t\t\t// check for integer attributes (WebGL 2 only)\n\n\t\t\t\t\tconst integer = ( capabilities.isWebGL2 === true && ( type === gl.INT || type === gl.UNSIGNED_INT || geometryAttribute.gpuType === IntType ) );\n\n\t\t\t\t\tif ( geometryAttribute.isInterleavedBufferAttribute ) {\n\n\t\t\t\t\t\tconst data = geometryAttribute.data;\n\t\t\t\t\t\tconst stride = data.stride;\n\t\t\t\t\t\tconst offset = geometryAttribute.offset;\n\n\t\t\t\t\t\tif ( data.isInstancedInterleavedBuffer ) {\n\n\t\t\t\t\t\t\tfor ( let i = 0; i < programAttribute.locationSize; i ++ ) {\n\n\t\t\t\t\t\t\t\tenableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) {\n\n\t\t\t\t\t\t\t\tgeometry._maxInstanceCount = data.meshPerAttribute * data.count;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tfor ( let i = 0; i < programAttribute.locationSize; i ++ ) {\n\n\t\t\t\t\t\t\t\tenableAttribute( programAttribute.location + i );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tgl.bindBuffer( gl.ARRAY_BUFFER, buffer );\n\n\t\t\t\t\t\tfor ( let i = 0; i < programAttribute.locationSize; i ++ ) {\n\n\t\t\t\t\t\t\tvertexAttribPointer(\n\t\t\t\t\t\t\t\tprogramAttribute.location + i,\n\t\t\t\t\t\t\t\tsize / programAttribute.locationSize,\n\t\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\t\tnormalized,\n\t\t\t\t\t\t\t\tstride * bytesPerElement,\n\t\t\t\t\t\t\t\t( offset + ( size / programAttribute.locationSize ) * i ) * bytesPerElement,\n\t\t\t\t\t\t\t\tinteger\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( geometryAttribute.isInstancedBufferAttribute ) {\n\n\t\t\t\t\t\t\tfor ( let i = 0; i < programAttribute.locationSize; i ++ ) {\n\n\t\t\t\t\t\t\t\tenableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) {\n\n\t\t\t\t\t\t\t\tgeometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tfor ( let i = 0; i < programAttribute.locationSize; i ++ ) {\n\n\t\t\t\t\t\t\t\tenableAttribute( programAttribute.location + i );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tgl.bindBuffer( gl.ARRAY_BUFFER, buffer );\n\n\t\t\t\t\t\tfor ( let i = 0; i < programAttribute.locationSize; i ++ ) {\n\n\t\t\t\t\t\t\tvertexAttribPointer(\n\t\t\t\t\t\t\t\tprogramAttribute.location + i,\n\t\t\t\t\t\t\t\tsize / programAttribute.locationSize,\n\t\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\t\tnormalized,\n\t\t\t\t\t\t\t\tsize * bytesPerElement,\n\t\t\t\t\t\t\t\t( size / programAttribute.locationSize ) * i * bytesPerElement,\n\t\t\t\t\t\t\t\tinteger\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( materialDefaultAttributeValues !== undefined ) {\n\n\t\t\t\t\tconst value = materialDefaultAttributeValues[ name ];\n\n\t\t\t\t\tif ( value !== undefined ) {\n\n\t\t\t\t\t\tswitch ( value.length ) {\n\n\t\t\t\t\t\t\tcase 2:\n\t\t\t\t\t\t\t\tgl.vertexAttrib2fv( programAttribute.location, value );\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase 3:\n\t\t\t\t\t\t\t\tgl.vertexAttrib3fv( programAttribute.location, value );\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase 4:\n\t\t\t\t\t\t\t\tgl.vertexAttrib4fv( programAttribute.location, value );\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tgl.vertexAttrib1fv( programAttribute.location, value );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tdisableUnusedAttributes();\n\n\t}\n\n\tfunction dispose() {\n\n\t\treset();\n\n\t\tfor ( const geometryId in bindingStates ) {\n\n\t\t\tconst programMap = bindingStates[ geometryId ];\n\n\t\t\tfor ( const programId in programMap ) {\n\n\t\t\t\tconst stateMap = programMap[ programId ];\n\n\t\t\t\tfor ( const wireframe in stateMap ) {\n\n\t\t\t\t\tdeleteVertexArrayObject( stateMap[ wireframe ].object );\n\n\t\t\t\t\tdelete stateMap[ wireframe ];\n\n\t\t\t\t}\n\n\t\t\t\tdelete programMap[ programId ];\n\n\t\t\t}\n\n\t\t\tdelete bindingStates[ geometryId ];\n\n\t\t}\n\n\t}\n\n\tfunction releaseStatesOfGeometry( geometry ) {\n\n\t\tif ( bindingStates[ geometry.id ] === undefined ) return;\n\n\t\tconst programMap = bindingStates[ geometry.id ];\n\n\t\tfor ( const programId in programMap ) {\n\n\t\t\tconst stateMap = programMap[ programId ];\n\n\t\t\tfor ( const wireframe in stateMap ) {\n\n\t\t\t\tdeleteVertexArrayObject( stateMap[ wireframe ].object );\n\n\t\t\t\tdelete stateMap[ wireframe ];\n\n\t\t\t}\n\n\t\t\tdelete programMap[ programId ];\n\n\t\t}\n\n\t\tdelete bindingStates[ geometry.id ];\n\n\t}\n\n\tfunction releaseStatesOfProgram( program ) {\n\n\t\tfor ( const geometryId in bindingStates ) {\n\n\t\t\tconst programMap = bindingStates[ geometryId ];\n\n\t\t\tif ( programMap[ program.id ] === undefined ) continue;\n\n\t\t\tconst stateMap = programMap[ program.id ];\n\n\t\t\tfor ( const wireframe in stateMap ) {\n\n\t\t\t\tdeleteVertexArrayObject( stateMap[ wireframe ].object );\n\n\t\t\t\tdelete stateMap[ wireframe ];\n\n\t\t\t}\n\n\t\t\tdelete programMap[ program.id ];\n\n\t\t}\n\n\t}\n\n\tfunction reset() {\n\n\t\tresetDefaultState();\n\t\tforceUpdate = true;\n\n\t\tif ( currentState === defaultState ) return;\n\n\t\tcurrentState = defaultState;\n\t\tbindVertexArrayObject( currentState.object );\n\n\t}\n\n\t// for backward-compatibility\n\n\tfunction resetDefaultState() {\n\n\t\tdefaultState.geometry = null;\n\t\tdefaultState.program = null;\n\t\tdefaultState.wireframe = false;\n\n\t}\n\n\treturn {\n\n\t\tsetup: setup,\n\t\treset: reset,\n\t\tresetDefaultState: resetDefaultState,\n\t\tdispose: dispose,\n\t\treleaseStatesOfGeometry: releaseStatesOfGeometry,\n\t\treleaseStatesOfProgram: releaseStatesOfProgram,\n\n\t\tinitAttributes: initAttributes,\n\t\tenableAttribute: enableAttribute,\n\t\tdisableUnusedAttributes: disableUnusedAttributes\n\n\t};\n\n}\n\nfunction WebGLBufferRenderer( gl, extensions, info, capabilities ) {\n\n\tconst isWebGL2 = capabilities.isWebGL2;\n\n\tlet mode;\n\n\tfunction setMode( value ) {\n\n\t\tmode = value;\n\n\t}\n\n\tfunction render( start, count ) {\n\n\t\tgl.drawArrays( mode, start, count );\n\n\t\tinfo.update( count, mode, 1 );\n\n\t}\n\n\tfunction renderInstances( start, count, primcount ) {\n\n\t\tif ( primcount === 0 ) return;\n\n\t\tlet extension, methodName;\n\n\t\tif ( isWebGL2 ) {\n\n\t\t\textension = gl;\n\t\t\tmethodName = 'drawArraysInstanced';\n\n\t\t} else {\n\n\t\t\textension = extensions.get( 'ANGLE_instanced_arrays' );\n\t\t\tmethodName = 'drawArraysInstancedANGLE';\n\n\t\t\tif ( extension === null ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t}\n\n\t\textension[ methodName ]( mode, start, count, primcount );\n\n\t\tinfo.update( count, mode, primcount );\n\n\t}\n\n\tfunction renderMultiDraw( starts, counts, drawCount ) {\n\n\t\tif ( drawCount === 0 ) return;\n\n\t\tconst extension = extensions.get( 'WEBGL_multi_draw' );\n\t\tif ( extension === null ) {\n\n\t\t\tfor ( let i = 0; i < drawCount; i ++ ) {\n\n\t\t\t\tthis.render( starts[ i ], counts[ i ] );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\textension.multiDrawArraysWEBGL( mode, starts, 0, counts, 0, drawCount );\n\n\t\t\tlet elementCount = 0;\n\t\t\tfor ( let i = 0; i < drawCount; i ++ ) {\n\n\t\t\t\telementCount += counts[ i ];\n\n\t\t\t}\n\n\t\t\tinfo.update( elementCount, mode, 1 );\n\n\t\t}\n\n\t}\n\n\t//\n\n\tthis.setMode = setMode;\n\tthis.render = render;\n\tthis.renderInstances = renderInstances;\n\tthis.renderMultiDraw = renderMultiDraw;\n\n}\n\nfunction WebGLCapabilities( gl, extensions, parameters ) {\n\n\tlet maxAnisotropy;\n\n\tfunction getMaxAnisotropy() {\n\n\t\tif ( maxAnisotropy !== undefined ) return maxAnisotropy;\n\n\t\tif ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) {\n\n\t\t\tconst extension = extensions.get( 'EXT_texture_filter_anisotropic' );\n\n\t\t\tmaxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT );\n\n\t\t} else {\n\n\t\t\tmaxAnisotropy = 0;\n\n\t\t}\n\n\t\treturn maxAnisotropy;\n\n\t}\n\n\tfunction getMaxPrecision( precision ) {\n\n\t\tif ( precision === 'highp' ) {\n\n\t\t\tif ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 &&\n\t\t\t\tgl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) {\n\n\t\t\t\treturn 'highp';\n\n\t\t\t}\n\n\t\t\tprecision = 'mediump';\n\n\t\t}\n\n\t\tif ( precision === 'mediump' ) {\n\n\t\t\tif ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 &&\n\t\t\t\tgl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) {\n\n\t\t\t\treturn 'mediump';\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn 'lowp';\n\n\t}\n\n\tconst isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl.constructor.name === 'WebGL2RenderingContext';\n\n\tlet precision = parameters.precision !== undefined ? parameters.precision : 'highp';\n\tconst maxPrecision = getMaxPrecision( precision );\n\n\tif ( maxPrecision !== precision ) {\n\n\t\tconsole.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' );\n\t\tprecision = maxPrecision;\n\n\t}\n\n\tconst drawBuffers = isWebGL2 || extensions.has( 'WEBGL_draw_buffers' );\n\n\tconst logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true;\n\n\tconst maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );\n\tconst maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );\n\tconst maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE );\n\tconst maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE );\n\n\tconst maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );\n\tconst maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS );\n\tconst maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS );\n\tconst maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS );\n\n\tconst vertexTextures = maxVertexTextures > 0;\n\tconst floatFragmentTextures = isWebGL2 || extensions.has( 'OES_texture_float' );\n\tconst floatVertexTextures = vertexTextures && floatFragmentTextures;\n\n\tconst maxSamples = isWebGL2 ? gl.getParameter( gl.MAX_SAMPLES ) : 0;\n\n\treturn {\n\n\t\tisWebGL2: isWebGL2,\n\n\t\tdrawBuffers: drawBuffers,\n\n\t\tgetMaxAnisotropy: getMaxAnisotropy,\n\t\tgetMaxPrecision: getMaxPrecision,\n\n\t\tprecision: precision,\n\t\tlogarithmicDepthBuffer: logarithmicDepthBuffer,\n\n\t\tmaxTextures: maxTextures,\n\t\tmaxVertexTextures: maxVertexTextures,\n\t\tmaxTextureSize: maxTextureSize,\n\t\tmaxCubemapSize: maxCubemapSize,\n\n\t\tmaxAttributes: maxAttributes,\n\t\tmaxVertexUniforms: maxVertexUniforms,\n\t\tmaxVaryings: maxVaryings,\n\t\tmaxFragmentUniforms: maxFragmentUniforms,\n\n\t\tvertexTextures: vertexTextures,\n\t\tfloatFragmentTextures: floatFragmentTextures,\n\t\tfloatVertexTextures: floatVertexTextures,\n\n\t\tmaxSamples: maxSamples\n\n\t};\n\n}\n\nfunction WebGLClipping( properties ) {\n\n\tconst scope = this;\n\n\tlet globalState = null,\n\t\tnumGlobalPlanes = 0,\n\t\tlocalClippingEnabled = false,\n\t\trenderingShadows = false;\n\n\tconst plane = new Plane(),\n\t\tviewNormalMatrix = new Matrix3(),\n\n\t\tuniform = { value: null, needsUpdate: false };\n\n\tthis.uniform = uniform;\n\tthis.numPlanes = 0;\n\tthis.numIntersection = 0;\n\n\tthis.init = function ( planes, enableLocalClipping ) {\n\n\t\tconst enabled =\n\t\t\tplanes.length !== 0 ||\n\t\t\tenableLocalClipping ||\n\t\t\t// enable state of previous frame - the clipping code has to\n\t\t\t// run another frame in order to reset the state:\n\t\t\tnumGlobalPlanes !== 0 ||\n\t\t\tlocalClippingEnabled;\n\n\t\tlocalClippingEnabled = enableLocalClipping;\n\n\t\tnumGlobalPlanes = planes.length;\n\n\t\treturn enabled;\n\n\t};\n\n\tthis.beginShadows = function () {\n\n\t\trenderingShadows = true;\n\t\tprojectPlanes( null );\n\n\t};\n\n\tthis.endShadows = function () {\n\n\t\trenderingShadows = false;\n\n\t};\n\n\tthis.setGlobalState = function ( planes, camera ) {\n\n\t\tglobalState = projectPlanes( planes, camera, 0 );\n\n\t};\n\n\tthis.setState = function ( material, camera, useCache ) {\n\n\t\tconst planes = material.clippingPlanes,\n\t\t\tclipIntersection = material.clipIntersection,\n\t\t\tclipShadows = material.clipShadows;\n\n\t\tconst materialProperties = properties.get( material );\n\n\t\tif ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) {\n\n\t\t\t// there's no local clipping\n\n\t\t\tif ( renderingShadows ) {\n\n\t\t\t\t// there's no global clipping\n\n\t\t\t\tprojectPlanes( null );\n\n\t\t\t} else {\n\n\t\t\t\tresetGlobalState();\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tconst nGlobal = renderingShadows ? 0 : numGlobalPlanes,\n\t\t\t\tlGlobal = nGlobal * 4;\n\n\t\t\tlet dstArray = materialProperties.clippingState || null;\n\n\t\t\tuniform.value = dstArray; // ensure unique state\n\n\t\t\tdstArray = projectPlanes( planes, camera, lGlobal, useCache );\n\n\t\t\tfor ( let i = 0; i !== lGlobal; ++ i ) {\n\n\t\t\t\tdstArray[ i ] = globalState[ i ];\n\n\t\t\t}\n\n\t\t\tmaterialProperties.clippingState = dstArray;\n\t\t\tthis.numIntersection = clipIntersection ? this.numPlanes : 0;\n\t\t\tthis.numPlanes += nGlobal;\n\n\t\t}\n\n\n\t};\n\n\tfunction resetGlobalState() {\n\n\t\tif ( uniform.value !== globalState ) {\n\n\t\t\tuniform.value = globalState;\n\t\t\tuniform.needsUpdate = numGlobalPlanes > 0;\n\n\t\t}\n\n\t\tscope.numPlanes = numGlobalPlanes;\n\t\tscope.numIntersection = 0;\n\n\t}\n\n\tfunction projectPlanes( planes, camera, dstOffset, skipTransform ) {\n\n\t\tconst nPlanes = planes !== null ? planes.length : 0;\n\t\tlet dstArray = null;\n\n\t\tif ( nPlanes !== 0 ) {\n\n\t\t\tdstArray = uniform.value;\n\n\t\t\tif ( skipTransform !== true || dstArray === null ) {\n\n\t\t\t\tconst flatSize = dstOffset + nPlanes * 4,\n\t\t\t\t\tviewMatrix = camera.matrixWorldInverse;\n\n\t\t\t\tviewNormalMatrix.getNormalMatrix( viewMatrix );\n\n\t\t\t\tif ( dstArray === null || dstArray.length < flatSize ) {\n\n\t\t\t\t\tdstArray = new Float32Array( flatSize );\n\n\t\t\t\t}\n\n\t\t\t\tfor ( let i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) {\n\n\t\t\t\t\tplane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix );\n\n\t\t\t\t\tplane.normal.toArray( dstArray, i4 );\n\t\t\t\t\tdstArray[ i4 + 3 ] = plane.constant;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tuniform.value = dstArray;\n\t\t\tuniform.needsUpdate = true;\n\n\t\t}\n\n\t\tscope.numPlanes = nPlanes;\n\t\tscope.numIntersection = 0;\n\n\t\treturn dstArray;\n\n\t}\n\n}\n\nfunction WebGLCubeMaps( renderer ) {\n\n\tlet cubemaps = new WeakMap();\n\n\tfunction mapTextureMapping( texture, mapping ) {\n\n\t\tif ( mapping === EquirectangularReflectionMapping ) {\n\n\t\t\ttexture.mapping = CubeReflectionMapping;\n\n\t\t} else if ( mapping === EquirectangularRefractionMapping ) {\n\n\t\t\ttexture.mapping = CubeRefractionMapping;\n\n\t\t}\n\n\t\treturn texture;\n\n\t}\n\n\tfunction get( texture ) {\n\n\t\tif ( texture && texture.isTexture ) {\n\n\t\t\tconst mapping = texture.mapping;\n\n\t\t\tif ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) {\n\n\t\t\t\tif ( cubemaps.has( texture ) ) {\n\n\t\t\t\t\tconst cubemap = cubemaps.get( texture ).texture;\n\t\t\t\t\treturn mapTextureMapping( cubemap, texture.mapping );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconst image = texture.image;\n\n\t\t\t\t\tif ( image && image.height > 0 ) {\n\n\t\t\t\t\t\tconst renderTarget = new WebGLCubeRenderTarget( image.height / 2 );\n\t\t\t\t\t\trenderTarget.fromEquirectangularTexture( renderer, texture );\n\t\t\t\t\t\tcubemaps.set( texture, renderTarget );\n\n\t\t\t\t\t\ttexture.addEventListener( 'dispose', onTextureDispose );\n\n\t\t\t\t\t\treturn mapTextureMapping( renderTarget.texture, texture.mapping );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// image not yet ready. try the conversion next frame\n\n\t\t\t\t\t\treturn null;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn texture;\n\n\t}\n\n\tfunction onTextureDispose( event ) {\n\n\t\tconst texture = event.target;\n\n\t\ttexture.removeEventListener( 'dispose', onTextureDispose );\n\n\t\tconst cubemap = cubemaps.get( texture );\n\n\t\tif ( cubemap !== undefined ) {\n\n\t\t\tcubemaps.delete( texture );\n\t\t\tcubemap.dispose();\n\n\t\t}\n\n\t}\n\n\tfunction dispose() {\n\n\t\tcubemaps = new WeakMap();\n\n\t}\n\n\treturn {\n\t\tget: get,\n\t\tdispose: dispose\n\t};\n\n}\n\nclass OrthographicCamera extends Camera {\n\n\tconstructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) {\n\n\t\tsuper();\n\n\t\tthis.isOrthographicCamera = true;\n\n\t\tthis.type = 'OrthographicCamera';\n\n\t\tthis.zoom = 1;\n\t\tthis.view = null;\n\n\t\tthis.left = left;\n\t\tthis.right = right;\n\t\tthis.top = top;\n\t\tthis.bottom = bottom;\n\n\t\tthis.near = near;\n\t\tthis.far = far;\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.left = source.left;\n\t\tthis.right = source.right;\n\t\tthis.top = source.top;\n\t\tthis.bottom = source.bottom;\n\t\tthis.near = source.near;\n\t\tthis.far = source.far;\n\n\t\tthis.zoom = source.zoom;\n\t\tthis.view = source.view === null ? null : Object.assign( {}, source.view );\n\n\t\treturn this;\n\n\t}\n\n\tsetViewOffset( fullWidth, fullHeight, x, y, width, height ) {\n\n\t\tif ( this.view === null ) {\n\n\t\t\tthis.view = {\n\t\t\t\tenabled: true,\n\t\t\t\tfullWidth: 1,\n\t\t\t\tfullHeight: 1,\n\t\t\t\toffsetX: 0,\n\t\t\t\toffsetY: 0,\n\t\t\t\twidth: 1,\n\t\t\t\theight: 1\n\t\t\t};\n\n\t\t}\n\n\t\tthis.view.enabled = true;\n\t\tthis.view.fullWidth = fullWidth;\n\t\tthis.view.fullHeight = fullHeight;\n\t\tthis.view.offsetX = x;\n\t\tthis.view.offsetY = y;\n\t\tthis.view.width = width;\n\t\tthis.view.height = height;\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tclearViewOffset() {\n\n\t\tif ( this.view !== null ) {\n\n\t\t\tthis.view.enabled = false;\n\n\t\t}\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tupdateProjectionMatrix() {\n\n\t\tconst dx = ( this.right - this.left ) / ( 2 * this.zoom );\n\t\tconst dy = ( this.top - this.bottom ) / ( 2 * this.zoom );\n\t\tconst cx = ( this.right + this.left ) / 2;\n\t\tconst cy = ( this.top + this.bottom ) / 2;\n\n\t\tlet left = cx - dx;\n\t\tlet right = cx + dx;\n\t\tlet top = cy + dy;\n\t\tlet bottom = cy - dy;\n\n\t\tif ( this.view !== null && this.view.enabled ) {\n\n\t\t\tconst scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom;\n\t\t\tconst scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom;\n\n\t\t\tleft += scaleW * this.view.offsetX;\n\t\t\tright = left + scaleW * this.view.width;\n\t\t\ttop -= scaleH * this.view.offsetY;\n\t\t\tbottom = top - scaleH * this.view.height;\n\n\t\t}\n\n\t\tthis.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far, this.coordinateSystem );\n\n\t\tthis.projectionMatrixInverse.copy( this.projectionMatrix ).invert();\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tdata.object.zoom = this.zoom;\n\t\tdata.object.left = this.left;\n\t\tdata.object.right = this.right;\n\t\tdata.object.top = this.top;\n\t\tdata.object.bottom = this.bottom;\n\t\tdata.object.near = this.near;\n\t\tdata.object.far = this.far;\n\n\t\tif ( this.view !== null ) data.object.view = Object.assign( {}, this.view );\n\n\t\treturn data;\n\n\t}\n\n}\n\nconst LOD_MIN = 4;\n\n// The standard deviations (radians) associated with the extra mips. These are\n// chosen to approximate a Trowbridge-Reitz distribution function times the\n// geometric shadowing function. These sigma values squared must match the\n// variance #defines in cube_uv_reflection_fragment.glsl.js.\nconst EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ];\n\n// The maximum length of the blur for loop. Smaller sigmas will use fewer\n// samples and exit early, but not recompile the shader.\nconst MAX_SAMPLES = 20;\n\nconst _flatCamera = /*@__PURE__*/ new OrthographicCamera();\nconst _clearColor = /*@__PURE__*/ new Color();\nlet _oldTarget = null;\nlet _oldActiveCubeFace = 0;\nlet _oldActiveMipmapLevel = 0;\n\n// Golden Ratio\nconst PHI = ( 1 + Math.sqrt( 5 ) ) / 2;\nconst INV_PHI = 1 / PHI;\n\n// Vertices of a dodecahedron (except the opposites, which represent the\n// same axis), used as axis directions evenly spread on a sphere.\nconst _axisDirections = [\n\t/*@__PURE__*/ new Vector3( 1, 1, 1 ),\n\t/*@__PURE__*/ new Vector3( - 1, 1, 1 ),\n\t/*@__PURE__*/ new Vector3( 1, 1, - 1 ),\n\t/*@__PURE__*/ new Vector3( - 1, 1, - 1 ),\n\t/*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ),\n\t/*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ),\n\t/*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ),\n\t/*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ),\n\t/*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ),\n\t/*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ) ];\n\n/**\n * This class generates a Prefiltered, Mipmapped Radiance Environment Map\n * (PMREM) from a cubeMap environment texture. This allows different levels of\n * blur to be quickly accessed based on material roughness. It is packed into a\n * special CubeUV format that allows us to perform custom interpolation so that\n * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap\n * chain, it only goes down to the LOD_MIN level (above), and then creates extra\n * even more filtered 'mips' at the same LOD_MIN resolution, associated with\n * higher roughness levels. In this way we maintain resolution to smoothly\n * interpolate diffuse lighting while limiting sampling computation.\n *\n * Paper: Fast, Accurate Image-Based Lighting\n * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view\n*/\n\nclass PMREMGenerator {\n\n\tconstructor( renderer ) {\n\n\t\tthis._renderer = renderer;\n\t\tthis._pingPongRenderTarget = null;\n\n\t\tthis._lodMax = 0;\n\t\tthis._cubeSize = 0;\n\t\tthis._lodPlanes = [];\n\t\tthis._sizeLods = [];\n\t\tthis._sigmas = [];\n\n\t\tthis._blurMaterial = null;\n\t\tthis._cubemapMaterial = null;\n\t\tthis._equirectMaterial = null;\n\n\t\tthis._compileMaterial( this._blurMaterial );\n\n\t}\n\n\t/**\n\t * Generates a PMREM from a supplied Scene, which can be faster than using an\n\t * image if networking bandwidth is low. Optional sigma specifies a blur radius\n\t * in radians to be applied to the scene before PMREM generation. Optional near\n\t * and far planes ensure the scene is rendered in its entirety (the cubeCamera\n\t * is placed at the origin).\n\t */\n\tfromScene( scene, sigma = 0, near = 0.1, far = 100 ) {\n\n\t\t_oldTarget = this._renderer.getRenderTarget();\n\t\t_oldActiveCubeFace = this._renderer.getActiveCubeFace();\n\t\t_oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();\n\n\t\tthis._setSize( 256 );\n\n\t\tconst cubeUVRenderTarget = this._allocateTargets();\n\t\tcubeUVRenderTarget.depthBuffer = true;\n\n\t\tthis._sceneToCubeUV( scene, near, far, cubeUVRenderTarget );\n\n\t\tif ( sigma > 0 ) {\n\n\t\t\tthis._blur( cubeUVRenderTarget, 0, 0, sigma );\n\n\t\t}\n\n\t\tthis._applyPMREM( cubeUVRenderTarget );\n\t\tthis._cleanup( cubeUVRenderTarget );\n\n\t\treturn cubeUVRenderTarget;\n\n\t}\n\n\t/**\n\t * Generates a PMREM from an equirectangular texture, which can be either LDR\n\t * or HDR. The ideal input image size is 1k (1024 x 512),\n\t * as this matches best with the 256 x 256 cubemap output.\n\t */\n\tfromEquirectangular( equirectangular, renderTarget = null ) {\n\n\t\treturn this._fromTexture( equirectangular, renderTarget );\n\n\t}\n\n\t/**\n\t * Generates a PMREM from an cubemap texture, which can be either LDR\n\t * or HDR. The ideal input cube size is 256 x 256,\n\t * as this matches best with the 256 x 256 cubemap output.\n\t */\n\tfromCubemap( cubemap, renderTarget = null ) {\n\n\t\treturn this._fromTexture( cubemap, renderTarget );\n\n\t}\n\n\t/**\n\t * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during\n\t * your texture's network fetch for increased concurrency.\n\t */\n\tcompileCubemapShader() {\n\n\t\tif ( this._cubemapMaterial === null ) {\n\n\t\t\tthis._cubemapMaterial = _getCubemapMaterial();\n\t\t\tthis._compileMaterial( this._cubemapMaterial );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during\n\t * your texture's network fetch for increased concurrency.\n\t */\n\tcompileEquirectangularShader() {\n\n\t\tif ( this._equirectMaterial === null ) {\n\n\t\t\tthis._equirectMaterial = _getEquirectMaterial();\n\t\t\tthis._compileMaterial( this._equirectMaterial );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class,\n\t * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on\n\t * one of them will cause any others to also become unusable.\n\t */\n\tdispose() {\n\n\t\tthis._dispose();\n\n\t\tif ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose();\n\t\tif ( this._equirectMaterial !== null ) this._equirectMaterial.dispose();\n\n\t}\n\n\t// private interface\n\n\t_setSize( cubeSize ) {\n\n\t\tthis._lodMax = Math.floor( Math.log2( cubeSize ) );\n\t\tthis._cubeSize = Math.pow( 2, this._lodMax );\n\n\t}\n\n\t_dispose() {\n\n\t\tif ( this._blurMaterial !== null ) this._blurMaterial.dispose();\n\n\t\tif ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose();\n\n\t\tfor ( let i = 0; i < this._lodPlanes.length; i ++ ) {\n\n\t\t\tthis._lodPlanes[ i ].dispose();\n\n\t\t}\n\n\t}\n\n\t_cleanup( outputTarget ) {\n\n\t\tthis._renderer.setRenderTarget( _oldTarget, _oldActiveCubeFace, _oldActiveMipmapLevel );\n\t\toutputTarget.scissorTest = false;\n\t\t_setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height );\n\n\t}\n\n\t_fromTexture( texture, renderTarget ) {\n\n\t\tif ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) {\n\n\t\t\tthis._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) );\n\n\t\t} else { // Equirectangular\n\n\t\t\tthis._setSize( texture.image.width / 4 );\n\n\t\t}\n\n\t\t_oldTarget = this._renderer.getRenderTarget();\n\t\t_oldActiveCubeFace = this._renderer.getActiveCubeFace();\n\t\t_oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();\n\n\t\tconst cubeUVRenderTarget = renderTarget || this._allocateTargets();\n\t\tthis._textureToCubeUV( texture, cubeUVRenderTarget );\n\t\tthis._applyPMREM( cubeUVRenderTarget );\n\t\tthis._cleanup( cubeUVRenderTarget );\n\n\t\treturn cubeUVRenderTarget;\n\n\t}\n\n\t_allocateTargets() {\n\n\t\tconst width = 3 * Math.max( this._cubeSize, 16 * 7 );\n\t\tconst height = 4 * this._cubeSize;\n\n\t\tconst params = {\n\t\t\tmagFilter: LinearFilter,\n\t\t\tminFilter: LinearFilter,\n\t\t\tgenerateMipmaps: false,\n\t\t\ttype: HalfFloatType,\n\t\t\tformat: RGBAFormat,\n\t\t\tcolorSpace: LinearSRGBColorSpace,\n\t\t\tdepthBuffer: false\n\t\t};\n\n\t\tconst cubeUVRenderTarget = _createRenderTarget( width, height, params );\n\n\t\tif ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) {\n\n\t\t\tif ( this._pingPongRenderTarget !== null ) {\n\n\t\t\t\tthis._dispose();\n\n\t\t\t}\n\n\t\t\tthis._pingPongRenderTarget = _createRenderTarget( width, height, params );\n\n\t\t\tconst { _lodMax } = this;\n\t\t\t( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) );\n\n\t\t\tthis._blurMaterial = _getBlurShader( _lodMax, width, height );\n\n\t\t}\n\n\t\treturn cubeUVRenderTarget;\n\n\t}\n\n\t_compileMaterial( material ) {\n\n\t\tconst tmpMesh = new Mesh( this._lodPlanes[ 0 ], material );\n\t\tthis._renderer.compile( tmpMesh, _flatCamera );\n\n\t}\n\n\t_sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) {\n\n\t\tconst fov = 90;\n\t\tconst aspect = 1;\n\t\tconst cubeCamera = new PerspectiveCamera( fov, aspect, near, far );\n\t\tconst upSign = [ 1, - 1, 1, 1, 1, 1 ];\n\t\tconst forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ];\n\t\tconst renderer = this._renderer;\n\n\t\tconst originalAutoClear = renderer.autoClear;\n\t\tconst toneMapping = renderer.toneMapping;\n\t\trenderer.getClearColor( _clearColor );\n\n\t\trenderer.toneMapping = NoToneMapping;\n\t\trenderer.autoClear = false;\n\n\t\tconst backgroundMaterial = new MeshBasicMaterial( {\n\t\t\tname: 'PMREM.Background',\n\t\t\tside: BackSide,\n\t\t\tdepthWrite: false,\n\t\t\tdepthTest: false,\n\t\t} );\n\n\t\tconst backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial );\n\n\t\tlet useSolidColor = false;\n\t\tconst background = scene.background;\n\n\t\tif ( background ) {\n\n\t\t\tif ( background.isColor ) {\n\n\t\t\t\tbackgroundMaterial.color.copy( background );\n\t\t\t\tscene.background = null;\n\t\t\t\tuseSolidColor = true;\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tbackgroundMaterial.color.copy( _clearColor );\n\t\t\tuseSolidColor = true;\n\n\t\t}\n\n\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\tconst col = i % 3;\n\n\t\t\tif ( col === 0 ) {\n\n\t\t\t\tcubeCamera.up.set( 0, upSign[ i ], 0 );\n\t\t\t\tcubeCamera.lookAt( forwardSign[ i ], 0, 0 );\n\n\t\t\t} else if ( col === 1 ) {\n\n\t\t\t\tcubeCamera.up.set( 0, 0, upSign[ i ] );\n\t\t\t\tcubeCamera.lookAt( 0, forwardSign[ i ], 0 );\n\n\t\t\t} else {\n\n\t\t\t\tcubeCamera.up.set( 0, upSign[ i ], 0 );\n\t\t\t\tcubeCamera.lookAt( 0, 0, forwardSign[ i ] );\n\n\t\t\t}\n\n\t\t\tconst size = this._cubeSize;\n\n\t\t\t_setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size );\n\n\t\t\trenderer.setRenderTarget( cubeUVRenderTarget );\n\n\t\t\tif ( useSolidColor ) {\n\n\t\t\t\trenderer.render( backgroundBox, cubeCamera );\n\n\t\t\t}\n\n\t\t\trenderer.render( scene, cubeCamera );\n\n\t\t}\n\n\t\tbackgroundBox.geometry.dispose();\n\t\tbackgroundBox.material.dispose();\n\n\t\trenderer.toneMapping = toneMapping;\n\t\trenderer.autoClear = originalAutoClear;\n\t\tscene.background = background;\n\n\t}\n\n\t_textureToCubeUV( texture, cubeUVRenderTarget ) {\n\n\t\tconst renderer = this._renderer;\n\n\t\tconst isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping );\n\n\t\tif ( isCubeTexture ) {\n\n\t\t\tif ( this._cubemapMaterial === null ) {\n\n\t\t\t\tthis._cubemapMaterial = _getCubemapMaterial();\n\n\t\t\t}\n\n\t\t\tthis._cubemapMaterial.uniforms.flipEnvMap.value = ( texture.isRenderTargetTexture === false ) ? - 1 : 1;\n\n\t\t} else {\n\n\t\t\tif ( this._equirectMaterial === null ) {\n\n\t\t\t\tthis._equirectMaterial = _getEquirectMaterial();\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial;\n\t\tconst mesh = new Mesh( this._lodPlanes[ 0 ], material );\n\n\t\tconst uniforms = material.uniforms;\n\n\t\tuniforms[ 'envMap' ].value = texture;\n\n\t\tconst size = this._cubeSize;\n\n\t\t_setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size );\n\n\t\trenderer.setRenderTarget( cubeUVRenderTarget );\n\t\trenderer.render( mesh, _flatCamera );\n\n\t}\n\n\t_applyPMREM( cubeUVRenderTarget ) {\n\n\t\tconst renderer = this._renderer;\n\t\tconst autoClear = renderer.autoClear;\n\t\trenderer.autoClear = false;\n\n\t\tfor ( let i = 1; i < this._lodPlanes.length; i ++ ) {\n\n\t\t\tconst sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] );\n\n\t\t\tconst poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ];\n\n\t\t\tthis._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis );\n\n\t\t}\n\n\t\trenderer.autoClear = autoClear;\n\n\t}\n\n\t/**\n\t * This is a two-pass Gaussian blur for a cubemap. Normally this is done\n\t * vertically and horizontally, but this breaks down on a cube. Here we apply\n\t * the blur latitudinally (around the poles), and then longitudinally (towards\n\t * the poles) to approximate the orthogonally-separable blur. It is least\n\t * accurate at the poles, but still does a decent job.\n\t */\n\t_blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) {\n\n\t\tconst pingPongRenderTarget = this._pingPongRenderTarget;\n\n\t\tthis._halfBlur(\n\t\t\tcubeUVRenderTarget,\n\t\t\tpingPongRenderTarget,\n\t\t\tlodIn,\n\t\t\tlodOut,\n\t\t\tsigma,\n\t\t\t'latitudinal',\n\t\t\tpoleAxis );\n\n\t\tthis._halfBlur(\n\t\t\tpingPongRenderTarget,\n\t\t\tcubeUVRenderTarget,\n\t\t\tlodOut,\n\t\t\tlodOut,\n\t\t\tsigma,\n\t\t\t'longitudinal',\n\t\t\tpoleAxis );\n\n\t}\n\n\t_halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) {\n\n\t\tconst renderer = this._renderer;\n\t\tconst blurMaterial = this._blurMaterial;\n\n\t\tif ( direction !== 'latitudinal' && direction !== 'longitudinal' ) {\n\n\t\t\tconsole.error(\n\t\t\t\t'blur direction must be either latitudinal or longitudinal!' );\n\n\t\t}\n\n\t\t// Number of standard deviations at which to cut off the discrete approximation.\n\t\tconst STANDARD_DEVIATIONS = 3;\n\n\t\tconst blurMesh = new Mesh( this._lodPlanes[ lodOut ], blurMaterial );\n\t\tconst blurUniforms = blurMaterial.uniforms;\n\n\t\tconst pixels = this._sizeLods[ lodIn ] - 1;\n\t\tconst radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 );\n\t\tconst sigmaPixels = sigmaRadians / radiansPerPixel;\n\t\tconst samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES;\n\n\t\tif ( samples > MAX_SAMPLES ) {\n\n\t\t\tconsole.warn( `sigmaRadians, ${\n\t\t\t\tsigmaRadians}, is too large and will clip, as it requested ${\n\t\t\t\tsamples} samples when the maximum is set to ${MAX_SAMPLES}` );\n\n\t\t}\n\n\t\tconst weights = [];\n\t\tlet sum = 0;\n\n\t\tfor ( let i = 0; i < MAX_SAMPLES; ++ i ) {\n\n\t\t\tconst x = i / sigmaPixels;\n\t\t\tconst weight = Math.exp( - x * x / 2 );\n\t\t\tweights.push( weight );\n\n\t\t\tif ( i === 0 ) {\n\n\t\t\t\tsum += weight;\n\n\t\t\t} else if ( i < samples ) {\n\n\t\t\t\tsum += 2 * weight;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfor ( let i = 0; i < weights.length; i ++ ) {\n\n\t\t\tweights[ i ] = weights[ i ] / sum;\n\n\t\t}\n\n\t\tblurUniforms[ 'envMap' ].value = targetIn.texture;\n\t\tblurUniforms[ 'samples' ].value = samples;\n\t\tblurUniforms[ 'weights' ].value = weights;\n\t\tblurUniforms[ 'latitudinal' ].value = direction === 'latitudinal';\n\n\t\tif ( poleAxis ) {\n\n\t\t\tblurUniforms[ 'poleAxis' ].value = poleAxis;\n\n\t\t}\n\n\t\tconst { _lodMax } = this;\n\t\tblurUniforms[ 'dTheta' ].value = radiansPerPixel;\n\t\tblurUniforms[ 'mipInt' ].value = _lodMax - lodIn;\n\n\t\tconst outputSize = this._sizeLods[ lodOut ];\n\t\tconst x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 );\n\t\tconst y = 4 * ( this._cubeSize - outputSize );\n\n\t\t_setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize );\n\t\trenderer.setRenderTarget( targetOut );\n\t\trenderer.render( blurMesh, _flatCamera );\n\n\t}\n\n}\n\n\n\nfunction _createPlanes( lodMax ) {\n\n\tconst lodPlanes = [];\n\tconst sizeLods = [];\n\tconst sigmas = [];\n\n\tlet lod = lodMax;\n\n\tconst totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length;\n\n\tfor ( let i = 0; i < totalLods; i ++ ) {\n\n\t\tconst sizeLod = Math.pow( 2, lod );\n\t\tsizeLods.push( sizeLod );\n\t\tlet sigma = 1.0 / sizeLod;\n\n\t\tif ( i > lodMax - LOD_MIN ) {\n\n\t\t\tsigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ];\n\n\t\t} else if ( i === 0 ) {\n\n\t\t\tsigma = 0;\n\n\t\t}\n\n\t\tsigmas.push( sigma );\n\n\t\tconst texelSize = 1.0 / ( sizeLod - 2 );\n\t\tconst min = - texelSize;\n\t\tconst max = 1 + texelSize;\n\t\tconst uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ];\n\n\t\tconst cubeFaces = 6;\n\t\tconst vertices = 6;\n\t\tconst positionSize = 3;\n\t\tconst uvSize = 2;\n\t\tconst faceIndexSize = 1;\n\n\t\tconst position = new Float32Array( positionSize * vertices * cubeFaces );\n\t\tconst uv = new Float32Array( uvSize * vertices * cubeFaces );\n\t\tconst faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces );\n\n\t\tfor ( let face = 0; face < cubeFaces; face ++ ) {\n\n\t\t\tconst x = ( face % 3 ) * 2 / 3 - 1;\n\t\t\tconst y = face > 2 ? 0 : - 1;\n\t\t\tconst coordinates = [\n\t\t\t\tx, y, 0,\n\t\t\t\tx + 2 / 3, y, 0,\n\t\t\t\tx + 2 / 3, y + 1, 0,\n\t\t\t\tx, y, 0,\n\t\t\t\tx + 2 / 3, y + 1, 0,\n\t\t\t\tx, y + 1, 0\n\t\t\t];\n\t\t\tposition.set( coordinates, positionSize * vertices * face );\n\t\t\tuv.set( uv1, uvSize * vertices * face );\n\t\t\tconst fill = [ face, face, face, face, face, face ];\n\t\t\tfaceIndex.set( fill, faceIndexSize * vertices * face );\n\n\t\t}\n\n\t\tconst planes = new BufferGeometry();\n\t\tplanes.setAttribute( 'position', new BufferAttribute( position, positionSize ) );\n\t\tplanes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) );\n\t\tplanes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) );\n\t\tlodPlanes.push( planes );\n\n\t\tif ( lod > LOD_MIN ) {\n\n\t\t\tlod --;\n\n\t\t}\n\n\t}\n\n\treturn { lodPlanes, sizeLods, sigmas };\n\n}\n\nfunction _createRenderTarget( width, height, params ) {\n\n\tconst cubeUVRenderTarget = new WebGLRenderTarget( width, height, params );\n\tcubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;\n\tcubeUVRenderTarget.texture.name = 'PMREM.cubeUv';\n\tcubeUVRenderTarget.scissorTest = true;\n\treturn cubeUVRenderTarget;\n\n}\n\nfunction _setViewport( target, x, y, width, height ) {\n\n\ttarget.viewport.set( x, y, width, height );\n\ttarget.scissor.set( x, y, width, height );\n\n}\n\nfunction _getBlurShader( lodMax, width, height ) {\n\n\tconst weights = new Float32Array( MAX_SAMPLES );\n\tconst poleAxis = new Vector3( 0, 1, 0 );\n\tconst shaderMaterial = new ShaderMaterial( {\n\n\t\tname: 'SphericalGaussianBlur',\n\n\t\tdefines: {\n\t\t\t'n': MAX_SAMPLES,\n\t\t\t'CUBEUV_TEXEL_WIDTH': 1.0 / width,\n\t\t\t'CUBEUV_TEXEL_HEIGHT': 1.0 / height,\n\t\t\t'CUBEUV_MAX_MIP': `${lodMax}.0`,\n\t\t},\n\n\t\tuniforms: {\n\t\t\t'envMap': { value: null },\n\t\t\t'samples': { value: 1 },\n\t\t\t'weights': { value: weights },\n\t\t\t'latitudinal': { value: false },\n\t\t\t'dTheta': { value: 0 },\n\t\t\t'mipInt': { value: 0 },\n\t\t\t'poleAxis': { value: poleAxis }\n\t\t},\n\n\t\tvertexShader: _getCommonVertexShader(),\n\n\t\tfragmentShader: /* glsl */`\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\t\t\tuniform int samples;\n\t\t\tuniform float weights[ n ];\n\t\t\tuniform bool latitudinal;\n\t\t\tuniform float dTheta;\n\t\t\tuniform float mipInt;\n\t\t\tuniform vec3 poleAxis;\n\n\t\t\t#define ENVMAP_TYPE_CUBE_UV\n\t\t\t#include <cube_uv_reflection_fragment>\n\n\t\t\tvec3 getSample( float theta, vec3 axis ) {\n\n\t\t\t\tfloat cosTheta = cos( theta );\n\t\t\t\t// Rodrigues' axis-angle rotation\n\t\t\t\tvec3 sampleDirection = vOutputDirection * cosTheta\n\t\t\t\t\t+ cross( axis, vOutputDirection ) * sin( theta )\n\t\t\t\t\t+ axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta );\n\n\t\t\t\treturn bilinearCubeUV( envMap, sampleDirection, mipInt );\n\n\t\t\t}\n\n\t\t\tvoid main() {\n\n\t\t\t\tvec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection );\n\n\t\t\t\tif ( all( equal( axis, vec3( 0.0 ) ) ) ) {\n\n\t\t\t\t\taxis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x );\n\n\t\t\t\t}\n\n\t\t\t\taxis = normalize( axis );\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t\t\t\tgl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis );\n\n\t\t\t\tfor ( int i = 1; i < n; i++ ) {\n\n\t\t\t\t\tif ( i >= samples ) {\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat theta = dTheta * float( i );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( theta, axis );\n\n\t\t\t\t}\n\n\t\t\t}\n\t\t`,\n\n\t\tblending: NoBlending,\n\t\tdepthTest: false,\n\t\tdepthWrite: false\n\n\t} );\n\n\treturn shaderMaterial;\n\n}\n\nfunction _getEquirectMaterial() {\n\n\treturn new ShaderMaterial( {\n\n\t\tname: 'EquirectangularToCubeUV',\n\n\t\tuniforms: {\n\t\t\t'envMap': { value: null }\n\t\t},\n\n\t\tvertexShader: _getCommonVertexShader(),\n\n\t\tfragmentShader: /* glsl */`\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\n\t\t\t#include <common>\n\n\t\t\tvoid main() {\n\n\t\t\t\tvec3 outputDirection = normalize( vOutputDirection );\n\t\t\t\tvec2 uv = equirectUv( outputDirection );\n\n\t\t\t\tgl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 );\n\n\t\t\t}\n\t\t`,\n\n\t\tblending: NoBlending,\n\t\tdepthTest: false,\n\t\tdepthWrite: false\n\n\t} );\n\n}\n\nfunction _getCubemapMaterial() {\n\n\treturn new ShaderMaterial( {\n\n\t\tname: 'CubemapToCubeUV',\n\n\t\tuniforms: {\n\t\t\t'envMap': { value: null },\n\t\t\t'flipEnvMap': { value: - 1 }\n\t\t},\n\n\t\tvertexShader: _getCommonVertexShader(),\n\n\t\tfragmentShader: /* glsl */`\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tuniform float flipEnvMap;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform samplerCube envMap;\n\n\t\t\tvoid main() {\n\n\t\t\t\tgl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) );\n\n\t\t\t}\n\t\t`,\n\n\t\tblending: NoBlending,\n\t\tdepthTest: false,\n\t\tdepthWrite: false\n\n\t} );\n\n}\n\nfunction _getCommonVertexShader() {\n\n\treturn /* glsl */`\n\n\t\tprecision mediump float;\n\t\tprecision mediump int;\n\n\t\tattribute float faceIndex;\n\n\t\tvarying vec3 vOutputDirection;\n\n\t\t// RH coordinate system; PMREM face-indexing convention\n\t\tvec3 getDirection( vec2 uv, float face ) {\n\n\t\t\tuv = 2.0 * uv - 1.0;\n\n\t\t\tvec3 direction = vec3( uv, 1.0 );\n\n\t\t\tif ( face == 0.0 ) {\n\n\t\t\t\tdirection = direction.zyx; // ( 1, v, u ) pos x\n\n\t\t\t} else if ( face == 1.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xz *= -1.0; // ( -u, 1, -v ) pos y\n\n\t\t\t} else if ( face == 2.0 ) {\n\n\t\t\t\tdirection.x *= -1.0; // ( -u, v, 1 ) pos z\n\n\t\t\t} else if ( face == 3.0 ) {\n\n\t\t\t\tdirection = direction.zyx;\n\t\t\t\tdirection.xz *= -1.0; // ( -1, v, -u ) neg x\n\n\t\t\t} else if ( face == 4.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xy *= -1.0; // ( -u, -1, v ) neg y\n\n\t\t\t} else if ( face == 5.0 ) {\n\n\t\t\t\tdirection.z *= -1.0; // ( u, v, -1 ) neg z\n\n\t\t\t}\n\n\t\t\treturn direction;\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\tvOutputDirection = getDirection( uv, faceIndex );\n\t\t\tgl_Position = vec4( position, 1.0 );\n\n\t\t}\n\t`;\n\n}\n\nfunction WebGLCubeUVMaps( renderer ) {\n\n\tlet cubeUVmaps = new WeakMap();\n\n\tlet pmremGenerator = null;\n\n\tfunction get( texture ) {\n\n\t\tif ( texture && texture.isTexture ) {\n\n\t\t\tconst mapping = texture.mapping;\n\n\t\t\tconst isEquirectMap = ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping );\n\t\t\tconst isCubeMap = ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping );\n\n\t\t\t// equirect/cube map to cubeUV conversion\n\n\t\t\tif ( isEquirectMap || isCubeMap ) {\n\n\t\t\t\tif ( texture.isRenderTargetTexture && texture.needsPMREMUpdate === true ) {\n\n\t\t\t\t\ttexture.needsPMREMUpdate = false;\n\n\t\t\t\t\tlet renderTarget = cubeUVmaps.get( texture );\n\n\t\t\t\t\tif ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer );\n\n\t\t\t\t\trenderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture, renderTarget ) : pmremGenerator.fromCubemap( texture, renderTarget );\n\t\t\t\t\tcubeUVmaps.set( texture, renderTarget );\n\n\t\t\t\t\treturn renderTarget.texture;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( cubeUVmaps.has( texture ) ) {\n\n\t\t\t\t\t\treturn cubeUVmaps.get( texture ).texture;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconst image = texture.image;\n\n\t\t\t\t\t\tif ( ( isEquirectMap && image && image.height > 0 ) || ( isCubeMap && image && isCubeTextureComplete( image ) ) ) {\n\n\t\t\t\t\t\t\tif ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer );\n\n\t\t\t\t\t\t\tconst renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture ) : pmremGenerator.fromCubemap( texture );\n\t\t\t\t\t\t\tcubeUVmaps.set( texture, renderTarget );\n\n\t\t\t\t\t\t\ttexture.addEventListener( 'dispose', onTextureDispose );\n\n\t\t\t\t\t\t\treturn renderTarget.texture;\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// image not yet ready. try the conversion next frame\n\n\t\t\t\t\t\t\treturn null;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn texture;\n\n\t}\n\n\tfunction isCubeTextureComplete( image ) {\n\n\t\tlet count = 0;\n\t\tconst length = 6;\n\n\t\tfor ( let i = 0; i < length; i ++ ) {\n\n\t\t\tif ( image[ i ] !== undefined ) count ++;\n\n\t\t}\n\n\t\treturn count === length;\n\n\n\t}\n\n\tfunction onTextureDispose( event ) {\n\n\t\tconst texture = event.target;\n\n\t\ttexture.removeEventListener( 'dispose', onTextureDispose );\n\n\t\tconst cubemapUV = cubeUVmaps.get( texture );\n\n\t\tif ( cubemapUV !== undefined ) {\n\n\t\t\tcubeUVmaps.delete( texture );\n\t\t\tcubemapUV.dispose();\n\n\t\t}\n\n\t}\n\n\tfunction dispose() {\n\n\t\tcubeUVmaps = new WeakMap();\n\n\t\tif ( pmremGenerator !== null ) {\n\n\t\t\tpmremGenerator.dispose();\n\t\t\tpmremGenerator = null;\n\n\t\t}\n\n\t}\n\n\treturn {\n\t\tget: get,\n\t\tdispose: dispose\n\t};\n\n}\n\nfunction WebGLExtensions( gl ) {\n\n\tconst extensions = {};\n\n\tfunction getExtension( name ) {\n\n\t\tif ( extensions[ name ] !== undefined ) {\n\n\t\t\treturn extensions[ name ];\n\n\t\t}\n\n\t\tlet extension;\n\n\t\tswitch ( name ) {\n\n\t\t\tcase 'WEBGL_depth_texture':\n\t\t\t\textension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' );\n\t\t\t\tbreak;\n\n\t\t\tcase 'EXT_texture_filter_anisotropic':\n\t\t\t\textension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );\n\t\t\t\tbreak;\n\n\t\t\tcase 'WEBGL_compressed_texture_s3tc':\n\t\t\t\textension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );\n\t\t\t\tbreak;\n\n\t\t\tcase 'WEBGL_compressed_texture_pvrtc':\n\t\t\t\textension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\textension = gl.getExtension( name );\n\n\t\t}\n\n\t\textensions[ name ] = extension;\n\n\t\treturn extension;\n\n\t}\n\n\treturn {\n\n\t\thas: function ( name ) {\n\n\t\t\treturn getExtension( name ) !== null;\n\n\t\t},\n\n\t\tinit: function ( capabilities ) {\n\n\t\t\tif ( capabilities.isWebGL2 ) {\n\n\t\t\t\tgetExtension( 'EXT_color_buffer_float' );\n\t\t\t\tgetExtension( 'WEBGL_clip_cull_distance' );\n\n\t\t\t} else {\n\n\t\t\t\tgetExtension( 'WEBGL_depth_texture' );\n\t\t\t\tgetExtension( 'OES_texture_float' );\n\t\t\t\tgetExtension( 'OES_texture_half_float' );\n\t\t\t\tgetExtension( 'OES_texture_half_float_linear' );\n\t\t\t\tgetExtension( 'OES_standard_derivatives' );\n\t\t\t\tgetExtension( 'OES_element_index_uint' );\n\t\t\t\tgetExtension( 'OES_vertex_array_object' );\n\t\t\t\tgetExtension( 'ANGLE_instanced_arrays' );\n\n\t\t\t}\n\n\t\t\tgetExtension( 'OES_texture_float_linear' );\n\t\t\tgetExtension( 'EXT_color_buffer_half_float' );\n\t\t\tgetExtension( 'WEBGL_multisampled_render_to_texture' );\n\n\t\t},\n\n\t\tget: function ( name ) {\n\n\t\t\tconst extension = getExtension( name );\n\n\t\t\tif ( extension === null ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' );\n\n\t\t\t}\n\n\t\t\treturn extension;\n\n\t\t}\n\n\t};\n\n}\n\nfunction WebGLGeometries( gl, attributes, info, bindingStates ) {\n\n\tconst geometries = {};\n\tconst wireframeAttributes = new WeakMap();\n\n\tfunction onGeometryDispose( event ) {\n\n\t\tconst geometry = event.target;\n\n\t\tif ( geometry.index !== null ) {\n\n\t\t\tattributes.remove( geometry.index );\n\n\t\t}\n\n\t\tfor ( const name in geometry.attributes ) {\n\n\t\t\tattributes.remove( geometry.attributes[ name ] );\n\n\t\t}\n\n\t\tfor ( const name in geometry.morphAttributes ) {\n\n\t\t\tconst array = geometry.morphAttributes[ name ];\n\n\t\t\tfor ( let i = 0, l = array.length; i < l; i ++ ) {\n\n\t\t\t\tattributes.remove( array[ i ] );\n\n\t\t\t}\n\n\t\t}\n\n\t\tgeometry.removeEventListener( 'dispose', onGeometryDispose );\n\n\t\tdelete geometries[ geometry.id ];\n\n\t\tconst attribute = wireframeAttributes.get( geometry );\n\n\t\tif ( attribute ) {\n\n\t\t\tattributes.remove( attribute );\n\t\t\twireframeAttributes.delete( geometry );\n\n\t\t}\n\n\t\tbindingStates.releaseStatesOfGeometry( geometry );\n\n\t\tif ( geometry.isInstancedBufferGeometry === true ) {\n\n\t\t\tdelete geometry._maxInstanceCount;\n\n\t\t}\n\n\t\t//\n\n\t\tinfo.memory.geometries --;\n\n\t}\n\n\tfunction get( object, geometry ) {\n\n\t\tif ( geometries[ geometry.id ] === true ) return geometry;\n\n\t\tgeometry.addEventListener( 'dispose', onGeometryDispose );\n\n\t\tgeometries[ geometry.id ] = true;\n\n\t\tinfo.memory.geometries ++;\n\n\t\treturn geometry;\n\n\t}\n\n\tfunction update( geometry ) {\n\n\t\tconst geometryAttributes = geometry.attributes;\n\n\t\t// Updating index buffer in VAO now. See WebGLBindingStates.\n\n\t\tfor ( const name in geometryAttributes ) {\n\n\t\t\tattributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER );\n\n\t\t}\n\n\t\t// morph targets\n\n\t\tconst morphAttributes = geometry.morphAttributes;\n\n\t\tfor ( const name in morphAttributes ) {\n\n\t\t\tconst array = morphAttributes[ name ];\n\n\t\t\tfor ( let i = 0, l = array.length; i < l; i ++ ) {\n\n\t\t\t\tattributes.update( array[ i ], gl.ARRAY_BUFFER );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tfunction updateWireframeAttribute( geometry ) {\n\n\t\tconst indices = [];\n\n\t\tconst geometryIndex = geometry.index;\n\t\tconst geometryPosition = geometry.attributes.position;\n\t\tlet version = 0;\n\n\t\tif ( geometryIndex !== null ) {\n\n\t\t\tconst array = geometryIndex.array;\n\t\t\tversion = geometryIndex.version;\n\n\t\t\tfor ( let i = 0, l = array.length; i < l; i += 3 ) {\n\n\t\t\t\tconst a = array[ i + 0 ];\n\t\t\t\tconst b = array[ i + 1 ];\n\t\t\t\tconst c = array[ i + 2 ];\n\n\t\t\t\tindices.push( a, b, b, c, c, a );\n\n\t\t\t}\n\n\t\t} else if ( geometryPosition !== undefined ) {\n\n\t\t\tconst array = geometryPosition.array;\n\t\t\tversion = geometryPosition.version;\n\n\t\t\tfor ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) {\n\n\t\t\t\tconst a = i + 0;\n\t\t\t\tconst b = i + 1;\n\t\t\t\tconst c = i + 2;\n\n\t\t\t\tindices.push( a, b, b, c, c, a );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\treturn;\n\n\t\t}\n\n\t\tconst attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 );\n\t\tattribute.version = version;\n\n\t\t// Updating index buffer in VAO now. See WebGLBindingStates\n\n\t\t//\n\n\t\tconst previousAttribute = wireframeAttributes.get( geometry );\n\n\t\tif ( previousAttribute ) attributes.remove( previousAttribute );\n\n\t\t//\n\n\t\twireframeAttributes.set( geometry, attribute );\n\n\t}\n\n\tfunction getWireframeAttribute( geometry ) {\n\n\t\tconst currentAttribute = wireframeAttributes.get( geometry );\n\n\t\tif ( currentAttribute ) {\n\n\t\t\tconst geometryIndex = geometry.index;\n\n\t\t\tif ( geometryIndex !== null ) {\n\n\t\t\t\t// if the attribute is obsolete, create a new one\n\n\t\t\t\tif ( currentAttribute.version < geometryIndex.version ) {\n\n\t\t\t\t\tupdateWireframeAttribute( geometry );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tupdateWireframeAttribute( geometry );\n\n\t\t}\n\n\t\treturn wireframeAttributes.get( geometry );\n\n\t}\n\n\treturn {\n\n\t\tget: get,\n\t\tupdate: update,\n\n\t\tgetWireframeAttribute: getWireframeAttribute\n\n\t};\n\n}\n\nfunction WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) {\n\n\tconst isWebGL2 = capabilities.isWebGL2;\n\n\tlet mode;\n\n\tfunction setMode( value ) {\n\n\t\tmode = value;\n\n\t}\n\n\tlet type, bytesPerElement;\n\n\tfunction setIndex( value ) {\n\n\t\ttype = value.type;\n\t\tbytesPerElement = value.bytesPerElement;\n\n\t}\n\n\tfunction render( start, count ) {\n\n\t\tgl.drawElements( mode, count, type, start * bytesPerElement );\n\n\t\tinfo.update( count, mode, 1 );\n\n\t}\n\n\tfunction renderInstances( start, count, primcount ) {\n\n\t\tif ( primcount === 0 ) return;\n\n\t\tlet extension, methodName;\n\n\t\tif ( isWebGL2 ) {\n\n\t\t\textension = gl;\n\t\t\tmethodName = 'drawElementsInstanced';\n\n\t\t} else {\n\n\t\t\textension = extensions.get( 'ANGLE_instanced_arrays' );\n\t\t\tmethodName = 'drawElementsInstancedANGLE';\n\n\t\t\tif ( extension === null ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t}\n\n\t\textension[ methodName ]( mode, count, type, start * bytesPerElement, primcount );\n\n\t\tinfo.update( count, mode, primcount );\n\n\t}\n\n\tfunction renderMultiDraw( starts, counts, drawCount ) {\n\n\t\tif ( drawCount === 0 ) return;\n\n\t\tconst extension = extensions.get( 'WEBGL_multi_draw' );\n\t\tif ( extension === null ) {\n\n\t\t\tfor ( let i = 0; i < drawCount; i ++ ) {\n\n\t\t\t\tthis.render( starts[ i ] / bytesPerElement, counts[ i ] );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\textension.multiDrawElementsWEBGL( mode, counts, 0, type, starts, 0, drawCount );\n\n\t\t\tlet elementCount = 0;\n\t\t\tfor ( let i = 0; i < drawCount; i ++ ) {\n\n\t\t\t\telementCount += counts[ i ];\n\n\t\t\t}\n\n\t\t\tinfo.update( elementCount, mode, 1 );\n\n\t\t}\n\n\t}\n\n\t//\n\n\tthis.setMode = setMode;\n\tthis.setIndex = setIndex;\n\tthis.render = render;\n\tthis.renderInstances = renderInstances;\n\tthis.renderMultiDraw = renderMultiDraw;\n\n}\n\nfunction WebGLInfo( gl ) {\n\n\tconst memory = {\n\t\tgeometries: 0,\n\t\ttextures: 0\n\t};\n\n\tconst render = {\n\t\tframe: 0,\n\t\tcalls: 0,\n\t\ttriangles: 0,\n\t\tpoints: 0,\n\t\tlines: 0\n\t};\n\n\tfunction update( count, mode, instanceCount ) {\n\n\t\trender.calls ++;\n\n\t\tswitch ( mode ) {\n\n\t\t\tcase gl.TRIANGLES:\n\t\t\t\trender.triangles += instanceCount * ( count / 3 );\n\t\t\t\tbreak;\n\n\t\t\tcase gl.LINES:\n\t\t\t\trender.lines += instanceCount * ( count / 2 );\n\t\t\t\tbreak;\n\n\t\t\tcase gl.LINE_STRIP:\n\t\t\t\trender.lines += instanceCount * ( count - 1 );\n\t\t\t\tbreak;\n\n\t\t\tcase gl.LINE_LOOP:\n\t\t\t\trender.lines += instanceCount * count;\n\t\t\t\tbreak;\n\n\t\t\tcase gl.POINTS:\n\t\t\t\trender.points += instanceCount * count;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tconsole.error( 'THREE.WebGLInfo: Unknown draw mode:', mode );\n\t\t\t\tbreak;\n\n\t\t}\n\n\t}\n\n\tfunction reset() {\n\n\t\trender.calls = 0;\n\t\trender.triangles = 0;\n\t\trender.points = 0;\n\t\trender.lines = 0;\n\n\t}\n\n\treturn {\n\t\tmemory: memory,\n\t\trender: render,\n\t\tprograms: null,\n\t\tautoReset: true,\n\t\treset: reset,\n\t\tupdate: update\n\t};\n\n}\n\nfunction numericalSort( a, b ) {\n\n\treturn a[ 0 ] - b[ 0 ];\n\n}\n\nfunction absNumericalSort( a, b ) {\n\n\treturn Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] );\n\n}\n\nfunction WebGLMorphtargets( gl, capabilities, textures ) {\n\n\tconst influencesList = {};\n\tconst morphInfluences = new Float32Array( 8 );\n\tconst morphTextures = new WeakMap();\n\tconst morph = new Vector4();\n\n\tconst workInfluences = [];\n\n\tfor ( let i = 0; i < 8; i ++ ) {\n\n\t\tworkInfluences[ i ] = [ i, 0 ];\n\n\t}\n\n\tfunction update( object, geometry, program ) {\n\n\t\tconst objectInfluences = object.morphTargetInfluences;\n\n\t\tif ( capabilities.isWebGL2 === true ) {\n\n\t\t\t// instead of using attributes, the WebGL 2 code path encodes morph targets\n\t\t\t// into an array of data textures. Each layer represents a single morph target.\n\n\t\t\tconst morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;\n\t\t\tconst morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;\n\n\t\t\tlet entry = morphTextures.get( geometry );\n\n\t\t\tif ( entry === undefined || entry.count !== morphTargetsCount ) {\n\n\t\t\t\tif ( entry !== undefined ) entry.texture.dispose();\n\n\t\t\t\tconst hasMorphPosition = geometry.morphAttributes.position !== undefined;\n\t\t\t\tconst hasMorphNormals = geometry.morphAttributes.normal !== undefined;\n\t\t\t\tconst hasMorphColors = geometry.morphAttributes.color !== undefined;\n\n\t\t\t\tconst morphTargets = geometry.morphAttributes.position || [];\n\t\t\t\tconst morphNormals = geometry.morphAttributes.normal || [];\n\t\t\t\tconst morphColors = geometry.morphAttributes.color || [];\n\n\t\t\t\tlet vertexDataCount = 0;\n\n\t\t\t\tif ( hasMorphPosition === true ) vertexDataCount = 1;\n\t\t\t\tif ( hasMorphNormals === true ) vertexDataCount = 2;\n\t\t\t\tif ( hasMorphColors === true ) vertexDataCount = 3;\n\n\t\t\t\tlet width = geometry.attributes.position.count * vertexDataCount;\n\t\t\t\tlet height = 1;\n\n\t\t\t\tif ( width > capabilities.maxTextureSize ) {\n\n\t\t\t\t\theight = Math.ceil( width / capabilities.maxTextureSize );\n\t\t\t\t\twidth = capabilities.maxTextureSize;\n\n\t\t\t\t}\n\n\t\t\t\tconst buffer = new Float32Array( width * height * 4 * morphTargetsCount );\n\n\t\t\t\tconst texture = new DataArrayTexture( buffer, width, height, morphTargetsCount );\n\t\t\t\ttexture.type = FloatType;\n\t\t\t\ttexture.needsUpdate = true;\n\n\t\t\t\t// fill buffer\n\n\t\t\t\tconst vertexDataStride = vertexDataCount * 4;\n\n\t\t\t\tfor ( let i = 0; i < morphTargetsCount; i ++ ) {\n\n\t\t\t\t\tconst morphTarget = morphTargets[ i ];\n\t\t\t\t\tconst morphNormal = morphNormals[ i ];\n\t\t\t\t\tconst morphColor = morphColors[ i ];\n\n\t\t\t\t\tconst offset = width * height * 4 * i;\n\n\t\t\t\t\tfor ( let j = 0; j < morphTarget.count; j ++ ) {\n\n\t\t\t\t\t\tconst stride = j * vertexDataStride;\n\n\t\t\t\t\t\tif ( hasMorphPosition === true ) {\n\n\t\t\t\t\t\t\tmorph.fromBufferAttribute( morphTarget, j );\n\n\t\t\t\t\t\t\tbuffer[ offset + stride + 0 ] = morph.x;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 1 ] = morph.y;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 2 ] = morph.z;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 3 ] = 0;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( hasMorphNormals === true ) {\n\n\t\t\t\t\t\t\tmorph.fromBufferAttribute( morphNormal, j );\n\n\t\t\t\t\t\t\tbuffer[ offset + stride + 4 ] = morph.x;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 5 ] = morph.y;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 6 ] = morph.z;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 7 ] = 0;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( hasMorphColors === true ) {\n\n\t\t\t\t\t\t\tmorph.fromBufferAttribute( morphColor, j );\n\n\t\t\t\t\t\t\tbuffer[ offset + stride + 8 ] = morph.x;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 9 ] = morph.y;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 10 ] = morph.z;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? morph.w : 1;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tentry = {\n\t\t\t\t\tcount: morphTargetsCount,\n\t\t\t\t\ttexture: texture,\n\t\t\t\t\tsize: new Vector2( width, height )\n\t\t\t\t};\n\n\t\t\t\tmorphTextures.set( geometry, entry );\n\n\t\t\t\tfunction disposeTexture() {\n\n\t\t\t\t\ttexture.dispose();\n\n\t\t\t\t\tmorphTextures.delete( geometry );\n\n\t\t\t\t\tgeometry.removeEventListener( 'dispose', disposeTexture );\n\n\t\t\t\t}\n\n\t\t\t\tgeometry.addEventListener( 'dispose', disposeTexture );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tlet morphInfluencesSum = 0;\n\n\t\t\tfor ( let i = 0; i < objectInfluences.length; i ++ ) {\n\n\t\t\t\tmorphInfluencesSum += objectInfluences[ i ];\n\n\t\t\t}\n\n\t\t\tconst morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum;\n\n\t\t\tprogram.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence );\n\t\t\tprogram.getUniforms().setValue( gl, 'morphTargetInfluences', objectInfluences );\n\n\t\t\tprogram.getUniforms().setValue( gl, 'morphTargetsTexture', entry.texture, textures );\n\t\t\tprogram.getUniforms().setValue( gl, 'morphTargetsTextureSize', entry.size );\n\n\n\t\t} else {\n\n\t\t\t// When object doesn't have morph target influences defined, we treat it as a 0-length array\n\t\t\t// This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences\n\n\t\t\tconst length = objectInfluences === undefined ? 0 : objectInfluences.length;\n\n\t\t\tlet influences = influencesList[ geometry.id ];\n\n\t\t\tif ( influences === undefined || influences.length !== length ) {\n\n\t\t\t\t// initialise list\n\n\t\t\t\tinfluences = [];\n\n\t\t\t\tfor ( let i = 0; i < length; i ++ ) {\n\n\t\t\t\t\tinfluences[ i ] = [ i, 0 ];\n\n\t\t\t\t}\n\n\t\t\t\tinfluencesList[ geometry.id ] = influences;\n\n\t\t\t}\n\n\t\t\t// Collect influences\n\n\t\t\tfor ( let i = 0; i < length; i ++ ) {\n\n\t\t\t\tconst influence = influences[ i ];\n\n\t\t\t\tinfluence[ 0 ] = i;\n\t\t\t\tinfluence[ 1 ] = objectInfluences[ i ];\n\n\t\t\t}\n\n\t\t\tinfluences.sort( absNumericalSort );\n\n\t\t\tfor ( let i = 0; i < 8; i ++ ) {\n\n\t\t\t\tif ( i < length && influences[ i ][ 1 ] ) {\n\n\t\t\t\t\tworkInfluences[ i ][ 0 ] = influences[ i ][ 0 ];\n\t\t\t\t\tworkInfluences[ i ][ 1 ] = influences[ i ][ 1 ];\n\n\t\t\t\t} else {\n\n\t\t\t\t\tworkInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER;\n\t\t\t\t\tworkInfluences[ i ][ 1 ] = 0;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tworkInfluences.sort( numericalSort );\n\n\t\t\tconst morphTargets = geometry.morphAttributes.position;\n\t\t\tconst morphNormals = geometry.morphAttributes.normal;\n\n\t\t\tlet morphInfluencesSum = 0;\n\n\t\t\tfor ( let i = 0; i < 8; i ++ ) {\n\n\t\t\t\tconst influence = workInfluences[ i ];\n\t\t\t\tconst index = influence[ 0 ];\n\t\t\t\tconst value = influence[ 1 ];\n\n\t\t\t\tif ( index !== Number.MAX_SAFE_INTEGER && value ) {\n\n\t\t\t\t\tif ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) {\n\n\t\t\t\t\t\tgeometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) {\n\n\t\t\t\t\t\tgeometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tmorphInfluences[ i ] = value;\n\t\t\t\t\tmorphInfluencesSum += value;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( morphTargets && geometry.hasAttribute( 'morphTarget' + i ) === true ) {\n\n\t\t\t\t\t\tgeometry.deleteAttribute( 'morphTarget' + i );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( morphNormals && geometry.hasAttribute( 'morphNormal' + i ) === true ) {\n\n\t\t\t\t\t\tgeometry.deleteAttribute( 'morphNormal' + i );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tmorphInfluences[ i ] = 0;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// GLSL shader uses formula baseinfluence * base + sum(target * influence)\n\t\t\t// This allows us to switch between absolute morphs and relative morphs without changing shader code\n\t\t\t// When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence)\n\t\t\tconst morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum;\n\n\t\t\tprogram.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence );\n\t\t\tprogram.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences );\n\n\t\t}\n\n\t}\n\n\treturn {\n\n\t\tupdate: update\n\n\t};\n\n}\n\nfunction WebGLObjects( gl, geometries, attributes, info ) {\n\n\tlet updateMap = new WeakMap();\n\n\tfunction update( object ) {\n\n\t\tconst frame = info.render.frame;\n\n\t\tconst geometry = object.geometry;\n\t\tconst buffergeometry = geometries.get( object, geometry );\n\n\t\t// Update once per frame\n\n\t\tif ( updateMap.get( buffergeometry ) !== frame ) {\n\n\t\t\tgeometries.update( buffergeometry );\n\n\t\t\tupdateMap.set( buffergeometry, frame );\n\n\t\t}\n\n\t\tif ( object.isInstancedMesh ) {\n\n\t\t\tif ( object.hasEventListener( 'dispose', onInstancedMeshDispose ) === false ) {\n\n\t\t\t\tobject.addEventListener( 'dispose', onInstancedMeshDispose );\n\n\t\t\t}\n\n\t\t\tif ( updateMap.get( object ) !== frame ) {\n\n\t\t\t\tattributes.update( object.instanceMatrix, gl.ARRAY_BUFFER );\n\n\t\t\t\tif ( object.instanceColor !== null ) {\n\n\t\t\t\t\tattributes.update( object.instanceColor, gl.ARRAY_BUFFER );\n\n\t\t\t\t}\n\n\t\t\t\tupdateMap.set( object, frame );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( object.isSkinnedMesh ) {\n\n\t\t\tconst skeleton = object.skeleton;\n\n\t\t\tif ( updateMap.get( skeleton ) !== frame ) {\n\n\t\t\t\tskeleton.update();\n\n\t\t\t\tupdateMap.set( skeleton, frame );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn buffergeometry;\n\n\t}\n\n\tfunction dispose() {\n\n\t\tupdateMap = new WeakMap();\n\n\t}\n\n\tfunction onInstancedMeshDispose( event ) {\n\n\t\tconst instancedMesh = event.target;\n\n\t\tinstancedMesh.removeEventListener( 'dispose', onInstancedMeshDispose );\n\n\t\tattributes.remove( instancedMesh.instanceMatrix );\n\n\t\tif ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor );\n\n\t}\n\n\treturn {\n\n\t\tupdate: update,\n\t\tdispose: dispose\n\n\t};\n\n}\n\nclass DepthTexture extends Texture {\n\n\tconstructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) {\n\n\t\tformat = format !== undefined ? format : DepthFormat;\n\n\t\tif ( format !== DepthFormat && format !== DepthStencilFormat ) {\n\n\t\t\tthrow new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' );\n\n\t\t}\n\n\t\tif ( type === undefined && format === DepthFormat ) type = UnsignedIntType;\n\t\tif ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type;\n\n\t\tsuper( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n\t\tthis.isDepthTexture = true;\n\n\t\tthis.image = { width: width, height: height };\n\n\t\tthis.magFilter = magFilter !== undefined ? magFilter : NearestFilter;\n\t\tthis.minFilter = minFilter !== undefined ? minFilter : NearestFilter;\n\n\t\tthis.flipY = false;\n\t\tthis.generateMipmaps = false;\n\n\t\tthis.compareFunction = null;\n\n\t}\n\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.compareFunction = source.compareFunction;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tif ( this.compareFunction !== null ) data.compareFunction = this.compareFunction;\n\n\t\treturn data;\n\n\t}\n\n}\n\n/**\n * Uniforms of a program.\n * Those form a tree structure with a special top-level container for the root,\n * which you get by calling 'new WebGLUniforms( gl, program )'.\n *\n *\n * Properties of inner nodes including the top-level container:\n *\n * .seq - array of nested uniforms\n * .map - nested uniforms by name\n *\n *\n * Methods of all nodes except the top-level container:\n *\n * .setValue( gl, value, [textures] )\n *\n * \t\tuploads a uniform value(s)\n *  \tthe 'textures' parameter is needed for sampler uniforms\n *\n *\n * Static methods of the top-level container (textures factorizations):\n *\n * .upload( gl, seq, values, textures )\n *\n * \t\tsets uniforms in 'seq' to 'values[id].value'\n *\n * .seqWithValue( seq, values ) : filteredSeq\n *\n * \t\tfilters 'seq' entries with corresponding entry in values\n *\n *\n * Methods of the top-level container (textures factorizations):\n *\n * .setValue( gl, name, value, textures )\n *\n * \t\tsets uniform with  name 'name' to 'value'\n *\n * .setOptional( gl, obj, prop )\n *\n * \t\tlike .set for an optional property of the object\n *\n */\n\n\nconst emptyTexture = /*@__PURE__*/ new Texture();\n\nconst emptyShadowTexture = /*@__PURE__*/ new DepthTexture( 1, 1 );\nemptyShadowTexture.compareFunction = LessEqualCompare;\n\nconst emptyArrayTexture = /*@__PURE__*/ new DataArrayTexture();\nconst empty3dTexture = /*@__PURE__*/ new Data3DTexture();\nconst emptyCubeTexture = /*@__PURE__*/ new CubeTexture();\n\n// --- Utilities ---\n\n// Array Caches (provide typed arrays for temporary by size)\n\nconst arrayCacheF32 = [];\nconst arrayCacheI32 = [];\n\n// Float32Array caches used for uploading Matrix uniforms\n\nconst mat4array = new Float32Array( 16 );\nconst mat3array = new Float32Array( 9 );\nconst mat2array = new Float32Array( 4 );\n\n// Flattening for arrays of vectors and matrices\n\nfunction flatten( array, nBlocks, blockSize ) {\n\n\tconst firstElem = array[ 0 ];\n\n\tif ( firstElem <= 0 || firstElem > 0 ) return array;\n\t// unoptimized: ! isNaN( firstElem )\n\t// see http://jacksondunstan.com/articles/983\n\n\tconst n = nBlocks * blockSize;\n\tlet r = arrayCacheF32[ n ];\n\n\tif ( r === undefined ) {\n\n\t\tr = new Float32Array( n );\n\t\tarrayCacheF32[ n ] = r;\n\n\t}\n\n\tif ( nBlocks !== 0 ) {\n\n\t\tfirstElem.toArray( r, 0 );\n\n\t\tfor ( let i = 1, offset = 0; i !== nBlocks; ++ i ) {\n\n\t\t\toffset += blockSize;\n\t\t\tarray[ i ].toArray( r, offset );\n\n\t\t}\n\n\t}\n\n\treturn r;\n\n}\n\nfunction arraysEqual( a, b ) {\n\n\tif ( a.length !== b.length ) return false;\n\n\tfor ( let i = 0, l = a.length; i < l; i ++ ) {\n\n\t\tif ( a[ i ] !== b[ i ] ) return false;\n\n\t}\n\n\treturn true;\n\n}\n\nfunction copyArray( a, b ) {\n\n\tfor ( let i = 0, l = b.length; i < l; i ++ ) {\n\n\t\ta[ i ] = b[ i ];\n\n\t}\n\n}\n\n// Texture unit allocation\n\nfunction allocTexUnits( textures, n ) {\n\n\tlet r = arrayCacheI32[ n ];\n\n\tif ( r === undefined ) {\n\n\t\tr = new Int32Array( n );\n\t\tarrayCacheI32[ n ] = r;\n\n\t}\n\n\tfor ( let i = 0; i !== n; ++ i ) {\n\n\t\tr[ i ] = textures.allocateTextureUnit();\n\n\t}\n\n\treturn r;\n\n}\n\n// --- Setters ---\n\n// Note: Defining these methods externally, because they come in a bunch\n// and this way their names minify.\n\n// Single scalar\n\nfunction setValueV1f( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( cache[ 0 ] === v ) return;\n\n\tgl.uniform1f( this.addr, v );\n\n\tcache[ 0 ] = v;\n\n}\n\n// Single float vector (from flat array or THREE.VectorN)\n\nfunction setValueV2f( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {\n\n\t\t\tgl.uniform2f( this.addr, v.x, v.y );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform2fv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\nfunction setValueV3f( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {\n\n\t\t\tgl.uniform3f( this.addr, v.x, v.y, v.z );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\t\t\tcache[ 2 ] = v.z;\n\n\t\t}\n\n\t} else if ( v.r !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) {\n\n\t\t\tgl.uniform3f( this.addr, v.r, v.g, v.b );\n\n\t\t\tcache[ 0 ] = v.r;\n\t\t\tcache[ 1 ] = v.g;\n\t\t\tcache[ 2 ] = v.b;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform3fv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\nfunction setValueV4f( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {\n\n\t\t\tgl.uniform4f( this.addr, v.x, v.y, v.z, v.w );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\t\t\tcache[ 2 ] = v.z;\n\t\t\tcache[ 3 ] = v.w;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform4fv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\n// Single matrix (from flat array or THREE.MatrixN)\n\nfunction setValueM2( gl, v ) {\n\n\tconst cache = this.cache;\n\tconst elements = v.elements;\n\n\tif ( elements === undefined ) {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniformMatrix2fv( this.addr, false, v );\n\n\t\tcopyArray( cache, v );\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, elements ) ) return;\n\n\t\tmat2array.set( elements );\n\n\t\tgl.uniformMatrix2fv( this.addr, false, mat2array );\n\n\t\tcopyArray( cache, elements );\n\n\t}\n\n}\n\nfunction setValueM3( gl, v ) {\n\n\tconst cache = this.cache;\n\tconst elements = v.elements;\n\n\tif ( elements === undefined ) {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniformMatrix3fv( this.addr, false, v );\n\n\t\tcopyArray( cache, v );\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, elements ) ) return;\n\n\t\tmat3array.set( elements );\n\n\t\tgl.uniformMatrix3fv( this.addr, false, mat3array );\n\n\t\tcopyArray( cache, elements );\n\n\t}\n\n}\n\nfunction setValueM4( gl, v ) {\n\n\tconst cache = this.cache;\n\tconst elements = v.elements;\n\n\tif ( elements === undefined ) {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniformMatrix4fv( this.addr, false, v );\n\n\t\tcopyArray( cache, v );\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, elements ) ) return;\n\n\t\tmat4array.set( elements );\n\n\t\tgl.uniformMatrix4fv( this.addr, false, mat4array );\n\n\t\tcopyArray( cache, elements );\n\n\t}\n\n}\n\n// Single integer / boolean\n\nfunction setValueV1i( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( cache[ 0 ] === v ) return;\n\n\tgl.uniform1i( this.addr, v );\n\n\tcache[ 0 ] = v;\n\n}\n\n// Single integer / boolean vector (from flat array or THREE.VectorN)\n\nfunction setValueV2i( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {\n\n\t\t\tgl.uniform2i( this.addr, v.x, v.y );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform2iv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\nfunction setValueV3i( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {\n\n\t\t\tgl.uniform3i( this.addr, v.x, v.y, v.z );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\t\t\tcache[ 2 ] = v.z;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform3iv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\nfunction setValueV4i( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {\n\n\t\t\tgl.uniform4i( this.addr, v.x, v.y, v.z, v.w );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\t\t\tcache[ 2 ] = v.z;\n\t\t\tcache[ 3 ] = v.w;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform4iv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\n// Single unsigned integer\n\nfunction setValueV1ui( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( cache[ 0 ] === v ) return;\n\n\tgl.uniform1ui( this.addr, v );\n\n\tcache[ 0 ] = v;\n\n}\n\n// Single unsigned integer vector (from flat array or THREE.VectorN)\n\nfunction setValueV2ui( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {\n\n\t\t\tgl.uniform2ui( this.addr, v.x, v.y );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform2uiv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\nfunction setValueV3ui( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {\n\n\t\t\tgl.uniform3ui( this.addr, v.x, v.y, v.z );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\t\t\tcache[ 2 ] = v.z;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform3uiv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\nfunction setValueV4ui( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {\n\n\t\t\tgl.uniform4ui( this.addr, v.x, v.y, v.z, v.w );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\t\t\tcache[ 2 ] = v.z;\n\t\t\tcache[ 3 ] = v.w;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform4uiv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\n\n// Single texture (2D / Cube)\n\nfunction setValueT1( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\tconst unit = textures.allocateTextureUnit();\n\n\tif ( cache[ 0 ] !== unit ) {\n\n\t\tgl.uniform1i( this.addr, unit );\n\t\tcache[ 0 ] = unit;\n\n\t}\n\n\tconst emptyTexture2D = ( this.type === gl.SAMPLER_2D_SHADOW ) ? emptyShadowTexture : emptyTexture;\n\n\ttextures.setTexture2D( v || emptyTexture2D, unit );\n\n}\n\nfunction setValueT3D1( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\tconst unit = textures.allocateTextureUnit();\n\n\tif ( cache[ 0 ] !== unit ) {\n\n\t\tgl.uniform1i( this.addr, unit );\n\t\tcache[ 0 ] = unit;\n\n\t}\n\n\ttextures.setTexture3D( v || empty3dTexture, unit );\n\n}\n\nfunction setValueT6( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\tconst unit = textures.allocateTextureUnit();\n\n\tif ( cache[ 0 ] !== unit ) {\n\n\t\tgl.uniform1i( this.addr, unit );\n\t\tcache[ 0 ] = unit;\n\n\t}\n\n\ttextures.setTextureCube( v || emptyCubeTexture, unit );\n\n}\n\nfunction setValueT2DArray1( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\tconst unit = textures.allocateTextureUnit();\n\n\tif ( cache[ 0 ] !== unit ) {\n\n\t\tgl.uniform1i( this.addr, unit );\n\t\tcache[ 0 ] = unit;\n\n\t}\n\n\ttextures.setTexture2DArray( v || emptyArrayTexture, unit );\n\n}\n\n// Helper to pick the right setter for the singular case\n\nfunction getSingularSetter( type ) {\n\n\tswitch ( type ) {\n\n\t\tcase 0x1406: return setValueV1f; // FLOAT\n\t\tcase 0x8b50: return setValueV2f; // _VEC2\n\t\tcase 0x8b51: return setValueV3f; // _VEC3\n\t\tcase 0x8b52: return setValueV4f; // _VEC4\n\n\t\tcase 0x8b5a: return setValueM2; // _MAT2\n\t\tcase 0x8b5b: return setValueM3; // _MAT3\n\t\tcase 0x8b5c: return setValueM4; // _MAT4\n\n\t\tcase 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL\n\t\tcase 0x8b53: case 0x8b57: return setValueV2i; // _VEC2\n\t\tcase 0x8b54: case 0x8b58: return setValueV3i; // _VEC3\n\t\tcase 0x8b55: case 0x8b59: return setValueV4i; // _VEC4\n\n\t\tcase 0x1405: return setValueV1ui; // UINT\n\t\tcase 0x8dc6: return setValueV2ui; // _VEC2\n\t\tcase 0x8dc7: return setValueV3ui; // _VEC3\n\t\tcase 0x8dc8: return setValueV4ui; // _VEC4\n\n\t\tcase 0x8b5e: // SAMPLER_2D\n\t\tcase 0x8d66: // SAMPLER_EXTERNAL_OES\n\t\tcase 0x8dca: // INT_SAMPLER_2D\n\t\tcase 0x8dd2: // UNSIGNED_INT_SAMPLER_2D\n\t\tcase 0x8b62: // SAMPLER_2D_SHADOW\n\t\t\treturn setValueT1;\n\n\t\tcase 0x8b5f: // SAMPLER_3D\n\t\tcase 0x8dcb: // INT_SAMPLER_3D\n\t\tcase 0x8dd3: // UNSIGNED_INT_SAMPLER_3D\n\t\t\treturn setValueT3D1;\n\n\t\tcase 0x8b60: // SAMPLER_CUBE\n\t\tcase 0x8dcc: // INT_SAMPLER_CUBE\n\t\tcase 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE\n\t\tcase 0x8dc5: // SAMPLER_CUBE_SHADOW\n\t\t\treturn setValueT6;\n\n\t\tcase 0x8dc1: // SAMPLER_2D_ARRAY\n\t\tcase 0x8dcf: // INT_SAMPLER_2D_ARRAY\n\t\tcase 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY\n\t\tcase 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW\n\t\t\treturn setValueT2DArray1;\n\n\t}\n\n}\n\n\n// Array of scalars\n\nfunction setValueV1fArray( gl, v ) {\n\n\tgl.uniform1fv( this.addr, v );\n\n}\n\n// Array of vectors (from flat array or array of THREE.VectorN)\n\nfunction setValueV2fArray( gl, v ) {\n\n\tconst data = flatten( v, this.size, 2 );\n\n\tgl.uniform2fv( this.addr, data );\n\n}\n\nfunction setValueV3fArray( gl, v ) {\n\n\tconst data = flatten( v, this.size, 3 );\n\n\tgl.uniform3fv( this.addr, data );\n\n}\n\nfunction setValueV4fArray( gl, v ) {\n\n\tconst data = flatten( v, this.size, 4 );\n\n\tgl.uniform4fv( this.addr, data );\n\n}\n\n// Array of matrices (from flat array or array of THREE.MatrixN)\n\nfunction setValueM2Array( gl, v ) {\n\n\tconst data = flatten( v, this.size, 4 );\n\n\tgl.uniformMatrix2fv( this.addr, false, data );\n\n}\n\nfunction setValueM3Array( gl, v ) {\n\n\tconst data = flatten( v, this.size, 9 );\n\n\tgl.uniformMatrix3fv( this.addr, false, data );\n\n}\n\nfunction setValueM4Array( gl, v ) {\n\n\tconst data = flatten( v, this.size, 16 );\n\n\tgl.uniformMatrix4fv( this.addr, false, data );\n\n}\n\n// Array of integer / boolean\n\nfunction setValueV1iArray( gl, v ) {\n\n\tgl.uniform1iv( this.addr, v );\n\n}\n\n// Array of integer / boolean vectors (from flat array)\n\nfunction setValueV2iArray( gl, v ) {\n\n\tgl.uniform2iv( this.addr, v );\n\n}\n\nfunction setValueV3iArray( gl, v ) {\n\n\tgl.uniform3iv( this.addr, v );\n\n}\n\nfunction setValueV4iArray( gl, v ) {\n\n\tgl.uniform4iv( this.addr, v );\n\n}\n\n// Array of unsigned integer\n\nfunction setValueV1uiArray( gl, v ) {\n\n\tgl.uniform1uiv( this.addr, v );\n\n}\n\n// Array of unsigned integer vectors (from flat array)\n\nfunction setValueV2uiArray( gl, v ) {\n\n\tgl.uniform2uiv( this.addr, v );\n\n}\n\nfunction setValueV3uiArray( gl, v ) {\n\n\tgl.uniform3uiv( this.addr, v );\n\n}\n\nfunction setValueV4uiArray( gl, v ) {\n\n\tgl.uniform4uiv( this.addr, v );\n\n}\n\n\n// Array of textures (2D / 3D / Cube / 2DArray)\n\nfunction setValueT1Array( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\n\tconst n = v.length;\n\n\tconst units = allocTexUnits( textures, n );\n\n\tif ( ! arraysEqual( cache, units ) ) {\n\n\t\tgl.uniform1iv( this.addr, units );\n\n\t\tcopyArray( cache, units );\n\n\t}\n\n\tfor ( let i = 0; i !== n; ++ i ) {\n\n\t\ttextures.setTexture2D( v[ i ] || emptyTexture, units[ i ] );\n\n\t}\n\n}\n\nfunction setValueT3DArray( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\n\tconst n = v.length;\n\n\tconst units = allocTexUnits( textures, n );\n\n\tif ( ! arraysEqual( cache, units ) ) {\n\n\t\tgl.uniform1iv( this.addr, units );\n\n\t\tcopyArray( cache, units );\n\n\t}\n\n\tfor ( let i = 0; i !== n; ++ i ) {\n\n\t\ttextures.setTexture3D( v[ i ] || empty3dTexture, units[ i ] );\n\n\t}\n\n}\n\nfunction setValueT6Array( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\n\tconst n = v.length;\n\n\tconst units = allocTexUnits( textures, n );\n\n\tif ( ! arraysEqual( cache, units ) ) {\n\n\t\tgl.uniform1iv( this.addr, units );\n\n\t\tcopyArray( cache, units );\n\n\t}\n\n\tfor ( let i = 0; i !== n; ++ i ) {\n\n\t\ttextures.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );\n\n\t}\n\n}\n\nfunction setValueT2DArrayArray( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\n\tconst n = v.length;\n\n\tconst units = allocTexUnits( textures, n );\n\n\tif ( ! arraysEqual( cache, units ) ) {\n\n\t\tgl.uniform1iv( this.addr, units );\n\n\t\tcopyArray( cache, units );\n\n\t}\n\n\tfor ( let i = 0; i !== n; ++ i ) {\n\n\t\ttextures.setTexture2DArray( v[ i ] || emptyArrayTexture, units[ i ] );\n\n\t}\n\n}\n\n\n// Helper to pick the right setter for a pure (bottom-level) array\n\nfunction getPureArraySetter( type ) {\n\n\tswitch ( type ) {\n\n\t\tcase 0x1406: return setValueV1fArray; // FLOAT\n\t\tcase 0x8b50: return setValueV2fArray; // _VEC2\n\t\tcase 0x8b51: return setValueV3fArray; // _VEC3\n\t\tcase 0x8b52: return setValueV4fArray; // _VEC4\n\n\t\tcase 0x8b5a: return setValueM2Array; // _MAT2\n\t\tcase 0x8b5b: return setValueM3Array; // _MAT3\n\t\tcase 0x8b5c: return setValueM4Array; // _MAT4\n\n\t\tcase 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL\n\t\tcase 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2\n\t\tcase 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3\n\t\tcase 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4\n\n\t\tcase 0x1405: return setValueV1uiArray; // UINT\n\t\tcase 0x8dc6: return setValueV2uiArray; // _VEC2\n\t\tcase 0x8dc7: return setValueV3uiArray; // _VEC3\n\t\tcase 0x8dc8: return setValueV4uiArray; // _VEC4\n\n\t\tcase 0x8b5e: // SAMPLER_2D\n\t\tcase 0x8d66: // SAMPLER_EXTERNAL_OES\n\t\tcase 0x8dca: // INT_SAMPLER_2D\n\t\tcase 0x8dd2: // UNSIGNED_INT_SAMPLER_2D\n\t\tcase 0x8b62: // SAMPLER_2D_SHADOW\n\t\t\treturn setValueT1Array;\n\n\t\tcase 0x8b5f: // SAMPLER_3D\n\t\tcase 0x8dcb: // INT_SAMPLER_3D\n\t\tcase 0x8dd3: // UNSIGNED_INT_SAMPLER_3D\n\t\t\treturn setValueT3DArray;\n\n\t\tcase 0x8b60: // SAMPLER_CUBE\n\t\tcase 0x8dcc: // INT_SAMPLER_CUBE\n\t\tcase 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE\n\t\tcase 0x8dc5: // SAMPLER_CUBE_SHADOW\n\t\t\treturn setValueT6Array;\n\n\t\tcase 0x8dc1: // SAMPLER_2D_ARRAY\n\t\tcase 0x8dcf: // INT_SAMPLER_2D_ARRAY\n\t\tcase 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY\n\t\tcase 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW\n\t\t\treturn setValueT2DArrayArray;\n\n\t}\n\n}\n\n// --- Uniform Classes ---\n\nclass SingleUniform {\n\n\tconstructor( id, activeInfo, addr ) {\n\n\t\tthis.id = id;\n\t\tthis.addr = addr;\n\t\tthis.cache = [];\n\t\tthis.type = activeInfo.type;\n\t\tthis.setValue = getSingularSetter( activeInfo.type );\n\n\t\t// this.path = activeInfo.name; // DEBUG\n\n\t}\n\n}\n\nclass PureArrayUniform {\n\n\tconstructor( id, activeInfo, addr ) {\n\n\t\tthis.id = id;\n\t\tthis.addr = addr;\n\t\tthis.cache = [];\n\t\tthis.type = activeInfo.type;\n\t\tthis.size = activeInfo.size;\n\t\tthis.setValue = getPureArraySetter( activeInfo.type );\n\n\t\t// this.path = activeInfo.name; // DEBUG\n\n\t}\n\n}\n\nclass StructuredUniform {\n\n\tconstructor( id ) {\n\n\t\tthis.id = id;\n\n\t\tthis.seq = [];\n\t\tthis.map = {};\n\n\t}\n\n\tsetValue( gl, value, textures ) {\n\n\t\tconst seq = this.seq;\n\n\t\tfor ( let i = 0, n = seq.length; i !== n; ++ i ) {\n\n\t\t\tconst u = seq[ i ];\n\t\t\tu.setValue( gl, value[ u.id ], textures );\n\n\t\t}\n\n\t}\n\n}\n\n// --- Top-level ---\n\n// Parser - builds up the property tree from the path strings\n\nconst RePathPart = /(\\w+)(\\])?(\\[|\\.)?/g;\n\n// extracts\n// \t- the identifier (member name or array index)\n//  - followed by an optional right bracket (found when array index)\n//  - followed by an optional left bracket or dot (type of subscript)\n//\n// Note: These portions can be read in a non-overlapping fashion and\n// allow straightforward parsing of the hierarchy that WebGL encodes\n// in the uniform names.\n\nfunction addUniform( container, uniformObject ) {\n\n\tcontainer.seq.push( uniformObject );\n\tcontainer.map[ uniformObject.id ] = uniformObject;\n\n}\n\nfunction parseUniform( activeInfo, addr, container ) {\n\n\tconst path = activeInfo.name,\n\t\tpathLength = path.length;\n\n\t// reset RegExp object, because of the early exit of a previous run\n\tRePathPart.lastIndex = 0;\n\n\twhile ( true ) {\n\n\t\tconst match = RePathPart.exec( path ),\n\t\t\tmatchEnd = RePathPart.lastIndex;\n\n\t\tlet id = match[ 1 ];\n\t\tconst idIsIndex = match[ 2 ] === ']',\n\t\t\tsubscript = match[ 3 ];\n\n\t\tif ( idIsIndex ) id = id | 0; // convert to integer\n\n\t\tif ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) {\n\n\t\t\t// bare name or \"pure\" bottom-level array \"[0]\" suffix\n\n\t\t\taddUniform( container, subscript === undefined ?\n\t\t\t\tnew SingleUniform( id, activeInfo, addr ) :\n\t\t\t\tnew PureArrayUniform( id, activeInfo, addr ) );\n\n\t\t\tbreak;\n\n\t\t} else {\n\n\t\t\t// step into inner node / create it in case it doesn't exist\n\n\t\t\tconst map = container.map;\n\t\t\tlet next = map[ id ];\n\n\t\t\tif ( next === undefined ) {\n\n\t\t\t\tnext = new StructuredUniform( id );\n\t\t\t\taddUniform( container, next );\n\n\t\t\t}\n\n\t\t\tcontainer = next;\n\n\t\t}\n\n\t}\n\n}\n\n// Root Container\n\nclass WebGLUniforms {\n\n\tconstructor( gl, program ) {\n\n\t\tthis.seq = [];\n\t\tthis.map = {};\n\n\t\tconst n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );\n\n\t\tfor ( let i = 0; i < n; ++ i ) {\n\n\t\t\tconst info = gl.getActiveUniform( program, i ),\n\t\t\t\taddr = gl.getUniformLocation( program, info.name );\n\n\t\t\tparseUniform( info, addr, this );\n\n\t\t}\n\n\t}\n\n\tsetValue( gl, name, value, textures ) {\n\n\t\tconst u = this.map[ name ];\n\n\t\tif ( u !== undefined ) u.setValue( gl, value, textures );\n\n\t}\n\n\tsetOptional( gl, object, name ) {\n\n\t\tconst v = object[ name ];\n\n\t\tif ( v !== undefined ) this.setValue( gl, name, v );\n\n\t}\n\n\tstatic upload( gl, seq, values, textures ) {\n\n\t\tfor ( let i = 0, n = seq.length; i !== n; ++ i ) {\n\n\t\t\tconst u = seq[ i ],\n\t\t\t\tv = values[ u.id ];\n\n\t\t\tif ( v.needsUpdate !== false ) {\n\n\t\t\t\t// note: always updating when .needsUpdate is undefined\n\t\t\t\tu.setValue( gl, v.value, textures );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tstatic seqWithValue( seq, values ) {\n\n\t\tconst r = [];\n\n\t\tfor ( let i = 0, n = seq.length; i !== n; ++ i ) {\n\n\t\t\tconst u = seq[ i ];\n\t\t\tif ( u.id in values ) r.push( u );\n\n\t\t}\n\n\t\treturn r;\n\n\t}\n\n}\n\nfunction WebGLShader( gl, type, string ) {\n\n\tconst shader = gl.createShader( type );\n\n\tgl.shaderSource( shader, string );\n\tgl.compileShader( shader );\n\n\treturn shader;\n\n}\n\n// From https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/\nconst COMPLETION_STATUS_KHR = 0x91B1;\n\nlet programIdCount = 0;\n\nfunction handleSource( string, errorLine ) {\n\n\tconst lines = string.split( '\\n' );\n\tconst lines2 = [];\n\n\tconst from = Math.max( errorLine - 6, 0 );\n\tconst to = Math.min( errorLine + 6, lines.length );\n\n\tfor ( let i = from; i < to; i ++ ) {\n\n\t\tconst line = i + 1;\n\t\tlines2.push( `${line === errorLine ? '>' : ' '} ${line}: ${lines[ i ]}` );\n\n\t}\n\n\treturn lines2.join( '\\n' );\n\n}\n\nfunction getEncodingComponents( colorSpace ) {\n\n\tconst workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace );\n\tconst encodingPrimaries = ColorManagement.getPrimaries( colorSpace );\n\n\tlet gamutMapping;\n\n\tif ( workingPrimaries === encodingPrimaries ) {\n\n\t\tgamutMapping = '';\n\n\t} else if ( workingPrimaries === P3Primaries && encodingPrimaries === Rec709Primaries ) {\n\n\t\tgamutMapping = 'LinearDisplayP3ToLinearSRGB';\n\n\t} else if ( workingPrimaries === Rec709Primaries && encodingPrimaries === P3Primaries ) {\n\n\t\tgamutMapping = 'LinearSRGBToLinearDisplayP3';\n\n\t}\n\n\tswitch ( colorSpace ) {\n\n\t\tcase LinearSRGBColorSpace:\n\t\tcase LinearDisplayP3ColorSpace:\n\t\t\treturn [ gamutMapping, 'LinearTransferOETF' ];\n\n\t\tcase SRGBColorSpace:\n\t\tcase DisplayP3ColorSpace:\n\t\t\treturn [ gamutMapping, 'sRGBTransferOETF' ];\n\n\t\tdefault:\n\t\t\tconsole.warn( 'THREE.WebGLProgram: Unsupported color space:', colorSpace );\n\t\t\treturn [ gamutMapping, 'LinearTransferOETF' ];\n\n\t}\n\n}\n\nfunction getShaderErrors( gl, shader, type ) {\n\n\tconst status = gl.getShaderParameter( shader, gl.COMPILE_STATUS );\n\tconst errors = gl.getShaderInfoLog( shader ).trim();\n\n\tif ( status && errors === '' ) return '';\n\n\tconst errorMatches = /ERROR: 0:(\\d+)/.exec( errors );\n\tif ( errorMatches ) {\n\n\t\t// --enable-privileged-webgl-extension\n\t\t// console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) );\n\n\t\tconst errorLine = parseInt( errorMatches[ 1 ] );\n\t\treturn type.toUpperCase() + '\\n\\n' + errors + '\\n\\n' + handleSource( gl.getShaderSource( shader ), errorLine );\n\n\t} else {\n\n\t\treturn errors;\n\n\t}\n\n}\n\nfunction getTexelEncodingFunction( functionName, colorSpace ) {\n\n\tconst components = getEncodingComponents( colorSpace );\n\treturn `vec4 ${functionName}( vec4 value ) { return ${components[ 0 ]}( ${components[ 1 ]}( value ) ); }`;\n\n}\n\nfunction getToneMappingFunction( functionName, toneMapping ) {\n\n\tlet toneMappingName;\n\n\tswitch ( toneMapping ) {\n\n\t\tcase LinearToneMapping:\n\t\t\ttoneMappingName = 'Linear';\n\t\t\tbreak;\n\n\t\tcase ReinhardToneMapping:\n\t\t\ttoneMappingName = 'Reinhard';\n\t\t\tbreak;\n\n\t\tcase CineonToneMapping:\n\t\t\ttoneMappingName = 'OptimizedCineon';\n\t\t\tbreak;\n\n\t\tcase ACESFilmicToneMapping:\n\t\t\ttoneMappingName = 'ACESFilmic';\n\t\t\tbreak;\n\n\t\tcase AgXToneMapping:\n\t\t\ttoneMappingName = 'AgX';\n\t\t\tbreak;\n\n\t\tcase CustomToneMapping:\n\t\t\ttoneMappingName = 'Custom';\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tconsole.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping );\n\t\t\ttoneMappingName = 'Linear';\n\n\t}\n\n\treturn 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }';\n\n}\n\nfunction generateExtensions( parameters ) {\n\n\tconst chunks = [\n\t\t( parameters.extensionDerivatives || !! parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.normalMapTangentSpace || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '',\n\t\t( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '',\n\t\t( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '',\n\t\t( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : ''\n\t];\n\n\treturn chunks.filter( filterEmptyLine ).join( '\\n' );\n\n}\n\nfunction generateVertexExtensions( parameters ) {\n\n\tconst chunks = [\n\t\tparameters.extensionClipCullDistance ? '#extension GL_ANGLE_clip_cull_distance : require' : ''\n\t];\n\n\treturn chunks.filter( filterEmptyLine ).join( '\\n' );\n\n}\n\nfunction generateDefines( defines ) {\n\n\tconst chunks = [];\n\n\tfor ( const name in defines ) {\n\n\t\tconst value = defines[ name ];\n\n\t\tif ( value === false ) continue;\n\n\t\tchunks.push( '#define ' + name + ' ' + value );\n\n\t}\n\n\treturn chunks.join( '\\n' );\n\n}\n\nfunction fetchAttributeLocations( gl, program ) {\n\n\tconst attributes = {};\n\n\tconst n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );\n\n\tfor ( let i = 0; i < n; i ++ ) {\n\n\t\tconst info = gl.getActiveAttrib( program, i );\n\t\tconst name = info.name;\n\n\t\tlet locationSize = 1;\n\t\tif ( info.type === gl.FLOAT_MAT2 ) locationSize = 2;\n\t\tif ( info.type === gl.FLOAT_MAT3 ) locationSize = 3;\n\t\tif ( info.type === gl.FLOAT_MAT4 ) locationSize = 4;\n\n\t\t// console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i );\n\n\t\tattributes[ name ] = {\n\t\t\ttype: info.type,\n\t\t\tlocation: gl.getAttribLocation( program, name ),\n\t\t\tlocationSize: locationSize\n\t\t};\n\n\t}\n\n\treturn attributes;\n\n}\n\nfunction filterEmptyLine( string ) {\n\n\treturn string !== '';\n\n}\n\nfunction replaceLightNums( string, parameters ) {\n\n\tconst numSpotLightCoords = parameters.numSpotLightShadows + parameters.numSpotLightMaps - parameters.numSpotLightShadowsWithMaps;\n\n\treturn string\n\t\t.replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights )\n\t\t.replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights )\n\t\t.replace( /NUM_SPOT_LIGHT_MAPS/g, parameters.numSpotLightMaps )\n\t\t.replace( /NUM_SPOT_LIGHT_COORDS/g, numSpotLightCoords )\n\t\t.replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights )\n\t\t.replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights )\n\t\t.replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights )\n\t\t.replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows )\n\t\t.replace( /NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS/g, parameters.numSpotLightShadowsWithMaps )\n\t\t.replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows )\n\t\t.replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows );\n\n}\n\nfunction replaceClippingPlaneNums( string, parameters ) {\n\n\treturn string\n\t\t.replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes )\n\t\t.replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) );\n\n}\n\n// Resolve Includes\n\nconst includePattern = /^[ \\t]*#include +<([\\w\\d./]+)>/gm;\n\nfunction resolveIncludes( string ) {\n\n\treturn string.replace( includePattern, includeReplacer );\n\n}\n\nconst shaderChunkMap = new Map( [\n\t[ 'encodings_fragment', 'colorspace_fragment' ], // @deprecated, r154\n\t[ 'encodings_pars_fragment', 'colorspace_pars_fragment' ], // @deprecated, r154\n\t[ 'output_fragment', 'opaque_fragment' ], // @deprecated, r154\n] );\n\nfunction includeReplacer( match, include ) {\n\n\tlet string = ShaderChunk[ include ];\n\n\tif ( string === undefined ) {\n\n\t\tconst newInclude = shaderChunkMap.get( include );\n\n\t\tif ( newInclude !== undefined ) {\n\n\t\t\tstring = ShaderChunk[ newInclude ];\n\t\t\tconsole.warn( 'THREE.WebGLRenderer: Shader chunk \"%s\" has been deprecated. Use \"%s\" instead.', include, newInclude );\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'Can not resolve #include <' + include + '>' );\n\n\t\t}\n\n\t}\n\n\treturn resolveIncludes( string );\n\n}\n\n// Unroll Loops\n\nconst unrollLoopPattern = /#pragma unroll_loop_start\\s+for\\s*\\(\\s*int\\s+i\\s*=\\s*(\\d+)\\s*;\\s*i\\s*<\\s*(\\d+)\\s*;\\s*i\\s*\\+\\+\\s*\\)\\s*{([\\s\\S]+?)}\\s+#pragma unroll_loop_end/g;\n\nfunction unrollLoops( string ) {\n\n\treturn string.replace( unrollLoopPattern, loopReplacer );\n\n}\n\nfunction loopReplacer( match, start, end, snippet ) {\n\n\tlet string = '';\n\n\tfor ( let i = parseInt( start ); i < parseInt( end ); i ++ ) {\n\n\t\tstring += snippet\n\t\t\t.replace( /\\[\\s*i\\s*\\]/g, '[ ' + i + ' ]' )\n\t\t\t.replace( /UNROLLED_LOOP_INDEX/g, i );\n\n\t}\n\n\treturn string;\n\n}\n\n//\n\nfunction generatePrecision( parameters ) {\n\n\tlet precisionstring = 'precision ' + parameters.precision + ' float;\\nprecision ' + parameters.precision + ' int;';\n\n\tif ( parameters.precision === 'highp' ) {\n\n\t\tprecisionstring += '\\n#define HIGH_PRECISION';\n\n\t} else if ( parameters.precision === 'mediump' ) {\n\n\t\tprecisionstring += '\\n#define MEDIUM_PRECISION';\n\n\t} else if ( parameters.precision === 'lowp' ) {\n\n\t\tprecisionstring += '\\n#define LOW_PRECISION';\n\n\t}\n\n\treturn precisionstring;\n\n}\n\nfunction generateShadowMapTypeDefine( parameters ) {\n\n\tlet shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';\n\n\tif ( parameters.shadowMapType === PCFShadowMap ) {\n\n\t\tshadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';\n\n\t} else if ( parameters.shadowMapType === PCFSoftShadowMap ) {\n\n\t\tshadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';\n\n\t} else if ( parameters.shadowMapType === VSMShadowMap ) {\n\n\t\tshadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM';\n\n\t}\n\n\treturn shadowMapTypeDefine;\n\n}\n\nfunction generateEnvMapTypeDefine( parameters ) {\n\n\tlet envMapTypeDefine = 'ENVMAP_TYPE_CUBE';\n\n\tif ( parameters.envMap ) {\n\n\t\tswitch ( parameters.envMapMode ) {\n\n\t\t\tcase CubeReflectionMapping:\n\t\t\tcase CubeRefractionMapping:\n\t\t\t\tenvMapTypeDefine = 'ENVMAP_TYPE_CUBE';\n\t\t\t\tbreak;\n\n\t\t\tcase CubeUVReflectionMapping:\n\t\t\t\tenvMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV';\n\t\t\t\tbreak;\n\n\t\t}\n\n\t}\n\n\treturn envMapTypeDefine;\n\n}\n\nfunction generateEnvMapModeDefine( parameters ) {\n\n\tlet envMapModeDefine = 'ENVMAP_MODE_REFLECTION';\n\n\tif ( parameters.envMap ) {\n\n\t\tswitch ( parameters.envMapMode ) {\n\n\t\t\tcase CubeRefractionMapping:\n\n\t\t\t\tenvMapModeDefine = 'ENVMAP_MODE_REFRACTION';\n\t\t\t\tbreak;\n\n\t\t}\n\n\t}\n\n\treturn envMapModeDefine;\n\n}\n\nfunction generateEnvMapBlendingDefine( parameters ) {\n\n\tlet envMapBlendingDefine = 'ENVMAP_BLENDING_NONE';\n\n\tif ( parameters.envMap ) {\n\n\t\tswitch ( parameters.combine ) {\n\n\t\t\tcase MultiplyOperation:\n\t\t\t\tenvMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';\n\t\t\t\tbreak;\n\n\t\t\tcase MixOperation:\n\t\t\t\tenvMapBlendingDefine = 'ENVMAP_BLENDING_MIX';\n\t\t\t\tbreak;\n\n\t\t\tcase AddOperation:\n\t\t\t\tenvMapBlendingDefine = 'ENVMAP_BLENDING_ADD';\n\t\t\t\tbreak;\n\n\t\t}\n\n\t}\n\n\treturn envMapBlendingDefine;\n\n}\n\nfunction generateCubeUVSize( parameters ) {\n\n\tconst imageHeight = parameters.envMapCubeUVHeight;\n\n\tif ( imageHeight === null ) return null;\n\n\tconst maxMip = Math.log2( imageHeight ) - 2;\n\n\tconst texelHeight = 1.0 / imageHeight;\n\n\tconst texelWidth = 1.0 / ( 3 * Math.max( Math.pow( 2, maxMip ), 7 * 16 ) );\n\n\treturn { texelWidth, texelHeight, maxMip };\n\n}\n\nfunction WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {\n\n\t// TODO Send this event to Three.js DevTools\n\t// console.log( 'WebGLProgram', cacheKey );\n\n\tconst gl = renderer.getContext();\n\n\tconst defines = parameters.defines;\n\n\tlet vertexShader = parameters.vertexShader;\n\tlet fragmentShader = parameters.fragmentShader;\n\n\tconst shadowMapTypeDefine = generateShadowMapTypeDefine( parameters );\n\tconst envMapTypeDefine = generateEnvMapTypeDefine( parameters );\n\tconst envMapModeDefine = generateEnvMapModeDefine( parameters );\n\tconst envMapBlendingDefine = generateEnvMapBlendingDefine( parameters );\n\tconst envMapCubeUVSize = generateCubeUVSize( parameters );\n\n\tconst customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters );\n\n\tconst customVertexExtensions = generateVertexExtensions( parameters );\n\n\tconst customDefines = generateDefines( defines );\n\n\tconst program = gl.createProgram();\n\n\tlet prefixVertex, prefixFragment;\n\tlet versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\\n' : '';\n\n\tif ( parameters.isRawShaderMaterial ) {\n\n\t\tprefixVertex = [\n\n\t\t\t'#define SHADER_TYPE ' + parameters.shaderType,\n\t\t\t'#define SHADER_NAME ' + parameters.shaderName,\n\n\t\t\tcustomDefines\n\n\t\t].filter( filterEmptyLine ).join( '\\n' );\n\n\t\tif ( prefixVertex.length > 0 ) {\n\n\t\t\tprefixVertex += '\\n';\n\n\t\t}\n\n\t\tprefixFragment = [\n\n\t\t\tcustomExtensions,\n\n\t\t\t'#define SHADER_TYPE ' + parameters.shaderType,\n\t\t\t'#define SHADER_NAME ' + parameters.shaderName,\n\n\t\t\tcustomDefines\n\n\t\t].filter( filterEmptyLine ).join( '\\n' );\n\n\t\tif ( prefixFragment.length > 0 ) {\n\n\t\t\tprefixFragment += '\\n';\n\n\t\t}\n\n\t} else {\n\n\t\tprefixVertex = [\n\n\t\t\tgeneratePrecision( parameters ),\n\n\t\t\t'#define SHADER_TYPE ' + parameters.shaderType,\n\t\t\t'#define SHADER_NAME ' + parameters.shaderName,\n\n\t\t\tcustomDefines,\n\n\t\t\tparameters.extensionClipCullDistance ? '#define USE_CLIP_DISTANCE' : '',\n\t\t\tparameters.batching ? '#define USE_BATCHING' : '',\n\t\t\tparameters.instancing ? '#define USE_INSTANCING' : '',\n\t\t\tparameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '',\n\n\t\t\tparameters.useFog && parameters.fog ? '#define USE_FOG' : '',\n\t\t\tparameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '',\n\n\t\t\tparameters.map ? '#define USE_MAP' : '',\n\t\t\tparameters.envMap ? '#define USE_ENVMAP' : '',\n\t\t\tparameters.envMap ? '#define ' + envMapModeDefine : '',\n\t\t\tparameters.lightMap ? '#define USE_LIGHTMAP' : '',\n\t\t\tparameters.aoMap ? '#define USE_AOMAP' : '',\n\t\t\tparameters.bumpMap ? '#define USE_BUMPMAP' : '',\n\t\t\tparameters.normalMap ? '#define USE_NORMALMAP' : '',\n\t\t\tparameters.normalMapObjectSpace ? '#define USE_NORMALMAP_OBJECTSPACE' : '',\n\t\t\tparameters.normalMapTangentSpace ? '#define USE_NORMALMAP_TANGENTSPACE' : '',\n\t\t\tparameters.displacementMap ? '#define USE_DISPLACEMENTMAP' : '',\n\t\t\tparameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',\n\n\t\t\tparameters.anisotropy ? '#define USE_ANISOTROPY' : '',\n\t\t\tparameters.anisotropyMap ? '#define USE_ANISOTROPYMAP' : '',\n\n\t\t\tparameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '',\n\t\t\tparameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '',\n\t\t\tparameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',\n\n\t\t\tparameters.iridescenceMap ? '#define USE_IRIDESCENCEMAP' : '',\n\t\t\tparameters.iridescenceThicknessMap ? '#define USE_IRIDESCENCE_THICKNESSMAP' : '',\n\n\t\t\tparameters.specularMap ? '#define USE_SPECULARMAP' : '',\n\t\t\tparameters.specularColorMap ? '#define USE_SPECULAR_COLORMAP' : '',\n\t\t\tparameters.specularIntensityMap ? '#define USE_SPECULAR_INTENSITYMAP' : '',\n\n\t\t\tparameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',\n\t\t\tparameters.metalnessMap ? '#define USE_METALNESSMAP' : '',\n\t\t\tparameters.alphaMap ? '#define USE_ALPHAMAP' : '',\n\t\t\tparameters.alphaHash ? '#define USE_ALPHAHASH' : '',\n\n\t\t\tparameters.transmission ? '#define USE_TRANSMISSION' : '',\n\t\t\tparameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '',\n\t\t\tparameters.thicknessMap ? '#define USE_THICKNESSMAP' : '',\n\n\t\t\tparameters.sheenColorMap ? '#define USE_SHEEN_COLORMAP' : '',\n\t\t\tparameters.sheenRoughnessMap ? '#define USE_SHEEN_ROUGHNESSMAP' : '',\n\n\t\t\t//\n\n\t\t\tparameters.mapUv ? '#define MAP_UV ' + parameters.mapUv : '',\n\t\t\tparameters.alphaMapUv ? '#define ALPHAMAP_UV ' + parameters.alphaMapUv : '',\n\t\t\tparameters.lightMapUv ? '#define LIGHTMAP_UV ' + parameters.lightMapUv : '',\n\t\t\tparameters.aoMapUv ? '#define AOMAP_UV ' + parameters.aoMapUv : '',\n\t\t\tparameters.emissiveMapUv ? '#define EMISSIVEMAP_UV ' + parameters.emissiveMapUv : '',\n\t\t\tparameters.bumpMapUv ? '#define BUMPMAP_UV ' + parameters.bumpMapUv : '',\n\t\t\tparameters.normalMapUv ? '#define NORMALMAP_UV ' + parameters.normalMapUv : '',\n\t\t\tparameters.displacementMapUv ? '#define DISPLACEMENTMAP_UV ' + parameters.displacementMapUv : '',\n\n\t\t\tparameters.metalnessMapUv ? '#define METALNESSMAP_UV ' + parameters.metalnessMapUv : '',\n\t\t\tparameters.roughnessMapUv ? '#define ROUGHNESSMAP_UV ' + parameters.roughnessMapUv : '',\n\n\t\t\tparameters.anisotropyMapUv ? '#define ANISOTROPYMAP_UV ' + parameters.anisotropyMapUv : '',\n\n\t\t\tparameters.clearcoatMapUv ? '#define CLEARCOATMAP_UV ' + parameters.clearcoatMapUv : '',\n\t\t\tparameters.clearcoatNormalMapUv ? '#define CLEARCOAT_NORMALMAP_UV ' + parameters.clearcoatNormalMapUv : '',\n\t\t\tparameters.clearcoatRoughnessMapUv ? '#define CLEARCOAT_ROUGHNESSMAP_UV ' + parameters.clearcoatRoughnessMapUv : '',\n\n\t\t\tparameters.iridescenceMapUv ? '#define IRIDESCENCEMAP_UV ' + parameters.iridescenceMapUv : '',\n\t\t\tparameters.iridescenceThicknessMapUv ? '#define IRIDESCENCE_THICKNESSMAP_UV ' + parameters.iridescenceThicknessMapUv : '',\n\n\t\t\tparameters.sheenColorMapUv ? '#define SHEEN_COLORMAP_UV ' + parameters.sheenColorMapUv : '',\n\t\t\tparameters.sheenRoughnessMapUv ? '#define SHEEN_ROUGHNESSMAP_UV ' + parameters.sheenRoughnessMapUv : '',\n\n\t\t\tparameters.specularMapUv ? '#define SPECULARMAP_UV ' + parameters.specularMapUv : '',\n\t\t\tparameters.specularColorMapUv ? '#define SPECULAR_COLORMAP_UV ' + parameters.specularColorMapUv : '',\n\t\t\tparameters.specularIntensityMapUv ? '#define SPECULAR_INTENSITYMAP_UV ' + parameters.specularIntensityMapUv : '',\n\n\t\t\tparameters.transmissionMapUv ? '#define TRANSMISSIONMAP_UV ' + parameters.transmissionMapUv : '',\n\t\t\tparameters.thicknessMapUv ? '#define THICKNESSMAP_UV ' + parameters.thicknessMapUv : '',\n\n\t\t\t//\n\n\t\t\tparameters.vertexTangents && parameters.flatShading === false ? '#define USE_TANGENT' : '',\n\t\t\tparameters.vertexColors ? '#define USE_COLOR' : '',\n\t\t\tparameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '',\n\t\t\tparameters.vertexUv1s ? '#define USE_UV1' : '',\n\t\t\tparameters.vertexUv2s ? '#define USE_UV2' : '',\n\t\t\tparameters.vertexUv3s ? '#define USE_UV3' : '',\n\n\t\t\tparameters.pointsUvs ? '#define USE_POINTS_UV' : '',\n\n\t\t\tparameters.flatShading ? '#define FLAT_SHADED' : '',\n\n\t\t\tparameters.skinning ? '#define USE_SKINNING' : '',\n\n\t\t\tparameters.morphTargets ? '#define USE_MORPHTARGETS' : '',\n\t\t\tparameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '',\n\t\t\t( parameters.morphColors && parameters.isWebGL2 ) ? '#define USE_MORPHCOLORS' : '',\n\t\t\t( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_TEXTURE' : '',\n\t\t\t( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_TEXTURE_STRIDE ' + parameters.morphTextureStride : '',\n\t\t\t( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_COUNT ' + parameters.morphTargetsCount : '',\n\t\t\tparameters.doubleSided ? '#define DOUBLE_SIDED' : '',\n\t\t\tparameters.flipSided ? '#define FLIP_SIDED' : '',\n\n\t\t\tparameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',\n\t\t\tparameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',\n\n\t\t\tparameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',\n\n\t\t\tparameters.numLightProbes > 0 ? '#define USE_LIGHT_PROBES' : '',\n\n\t\t\tparameters.useLegacyLights ? '#define LEGACY_LIGHTS' : '',\n\n\t\t\tparameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',\n\t\t\t( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '',\n\n\t\t\t'uniform mat4 modelMatrix;',\n\t\t\t'uniform mat4 modelViewMatrix;',\n\t\t\t'uniform mat4 projectionMatrix;',\n\t\t\t'uniform mat4 viewMatrix;',\n\t\t\t'uniform mat3 normalMatrix;',\n\t\t\t'uniform vec3 cameraPosition;',\n\t\t\t'uniform bool isOrthographic;',\n\n\t\t\t'#ifdef USE_INSTANCING',\n\n\t\t\t'\tattribute mat4 instanceMatrix;',\n\n\t\t\t'#endif',\n\n\t\t\t'#ifdef USE_INSTANCING_COLOR',\n\n\t\t\t'\tattribute vec3 instanceColor;',\n\n\t\t\t'#endif',\n\n\t\t\t'attribute vec3 position;',\n\t\t\t'attribute vec3 normal;',\n\t\t\t'attribute vec2 uv;',\n\n\t\t\t'#ifdef USE_UV1',\n\n\t\t\t'\tattribute vec2 uv1;',\n\n\t\t\t'#endif',\n\n\t\t\t'#ifdef USE_UV2',\n\n\t\t\t'\tattribute vec2 uv2;',\n\n\t\t\t'#endif',\n\n\t\t\t'#ifdef USE_UV3',\n\n\t\t\t'\tattribute vec2 uv3;',\n\n\t\t\t'#endif',\n\n\t\t\t'#ifdef USE_TANGENT',\n\n\t\t\t'\tattribute vec4 tangent;',\n\n\t\t\t'#endif',\n\n\t\t\t'#if defined( USE_COLOR_ALPHA )',\n\n\t\t\t'\tattribute vec4 color;',\n\n\t\t\t'#elif defined( USE_COLOR )',\n\n\t\t\t'\tattribute vec3 color;',\n\n\t\t\t'#endif',\n\n\t\t\t'#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )',\n\n\t\t\t'\tattribute vec3 morphTarget0;',\n\t\t\t'\tattribute vec3 morphTarget1;',\n\t\t\t'\tattribute vec3 morphTarget2;',\n\t\t\t'\tattribute vec3 morphTarget3;',\n\n\t\t\t'\t#ifdef USE_MORPHNORMALS',\n\n\t\t\t'\t\tattribute vec3 morphNormal0;',\n\t\t\t'\t\tattribute vec3 morphNormal1;',\n\t\t\t'\t\tattribute vec3 morphNormal2;',\n\t\t\t'\t\tattribute vec3 morphNormal3;',\n\n\t\t\t'\t#else',\n\n\t\t\t'\t\tattribute vec3 morphTarget4;',\n\t\t\t'\t\tattribute vec3 morphTarget5;',\n\t\t\t'\t\tattribute vec3 morphTarget6;',\n\t\t\t'\t\tattribute vec3 morphTarget7;',\n\n\t\t\t'\t#endif',\n\n\t\t\t'#endif',\n\n\t\t\t'#ifdef USE_SKINNING',\n\n\t\t\t'\tattribute vec4 skinIndex;',\n\t\t\t'\tattribute vec4 skinWeight;',\n\n\t\t\t'#endif',\n\n\t\t\t'\\n'\n\n\t\t].filter( filterEmptyLine ).join( '\\n' );\n\n\t\tprefixFragment = [\n\n\t\t\tcustomExtensions,\n\n\t\t\tgeneratePrecision( parameters ),\n\n\t\t\t'#define SHADER_TYPE ' + parameters.shaderType,\n\t\t\t'#define SHADER_NAME ' + parameters.shaderName,\n\n\t\t\tcustomDefines,\n\n\t\t\tparameters.useFog && parameters.fog ? '#define USE_FOG' : '',\n\t\t\tparameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '',\n\n\t\t\tparameters.map ? '#define USE_MAP' : '',\n\t\t\tparameters.matcap ? '#define USE_MATCAP' : '',\n\t\t\tparameters.envMap ? '#define USE_ENVMAP' : '',\n\t\t\tparameters.envMap ? '#define ' + envMapTypeDefine : '',\n\t\t\tparameters.envMap ? '#define ' + envMapModeDefine : '',\n\t\t\tparameters.envMap ? '#define ' + envMapBlendingDefine : '',\n\t\t\tenvMapCubeUVSize ? '#define CUBEUV_TEXEL_WIDTH ' + envMapCubeUVSize.texelWidth : '',\n\t\t\tenvMapCubeUVSize ? '#define CUBEUV_TEXEL_HEIGHT ' + envMapCubeUVSize.texelHeight : '',\n\t\t\tenvMapCubeUVSize ? '#define CUBEUV_MAX_MIP ' + envMapCubeUVSize.maxMip + '.0' : '',\n\t\t\tparameters.lightMap ? '#define USE_LIGHTMAP' : '',\n\t\t\tparameters.aoMap ? '#define USE_AOMAP' : '',\n\t\t\tparameters.bumpMap ? '#define USE_BUMPMAP' : '',\n\t\t\tparameters.normalMap ? '#define USE_NORMALMAP' : '',\n\t\t\tparameters.normalMapObjectSpace ? '#define USE_NORMALMAP_OBJECTSPACE' : '',\n\t\t\tparameters.normalMapTangentSpace ? '#define USE_NORMALMAP_TANGENTSPACE' : '',\n\t\t\tparameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',\n\n\t\t\tparameters.anisotropy ? '#define USE_ANISOTROPY' : '',\n\t\t\tparameters.anisotropyMap ? '#define USE_ANISOTROPYMAP' : '',\n\n\t\t\tparameters.clearcoat ? '#define USE_CLEARCOAT' : '',\n\t\t\tparameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '',\n\t\t\tparameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '',\n\t\t\tparameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',\n\n\t\t\tparameters.iridescence ? '#define USE_IRIDESCENCE' : '',\n\t\t\tparameters.iridescenceMap ? '#define USE_IRIDESCENCEMAP' : '',\n\t\t\tparameters.iridescenceThicknessMap ? '#define USE_IRIDESCENCE_THICKNESSMAP' : '',\n\n\t\t\tparameters.specularMap ? '#define USE_SPECULARMAP' : '',\n\t\t\tparameters.specularColorMap ? '#define USE_SPECULAR_COLORMAP' : '',\n\t\t\tparameters.specularIntensityMap ? '#define USE_SPECULAR_INTENSITYMAP' : '',\n\n\t\t\tparameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',\n\t\t\tparameters.metalnessMap ? '#define USE_METALNESSMAP' : '',\n\n\t\t\tparameters.alphaMap ? '#define USE_ALPHAMAP' : '',\n\t\t\tparameters.alphaTest ? '#define USE_ALPHATEST' : '',\n\t\t\tparameters.alphaHash ? '#define USE_ALPHAHASH' : '',\n\n\t\t\tparameters.sheen ? '#define USE_SHEEN' : '',\n\t\t\tparameters.sheenColorMap ? '#define USE_SHEEN_COLORMAP' : '',\n\t\t\tparameters.sheenRoughnessMap ? '#define USE_SHEEN_ROUGHNESSMAP' : '',\n\n\t\t\tparameters.transmission ? '#define USE_TRANSMISSION' : '',\n\t\t\tparameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '',\n\t\t\tparameters.thicknessMap ? '#define USE_THICKNESSMAP' : '',\n\n\t\t\tparameters.vertexTangents && parameters.flatShading === false ? '#define USE_TANGENT' : '',\n\t\t\tparameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '',\n\t\t\tparameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '',\n\t\t\tparameters.vertexUv1s ? '#define USE_UV1' : '',\n\t\t\tparameters.vertexUv2s ? '#define USE_UV2' : '',\n\t\t\tparameters.vertexUv3s ? '#define USE_UV3' : '',\n\n\t\t\tparameters.pointsUvs ? '#define USE_POINTS_UV' : '',\n\n\t\t\tparameters.gradientMap ? '#define USE_GRADIENTMAP' : '',\n\n\t\t\tparameters.flatShading ? '#define FLAT_SHADED' : '',\n\n\t\t\tparameters.doubleSided ? '#define DOUBLE_SIDED' : '',\n\t\t\tparameters.flipSided ? '#define FLIP_SIDED' : '',\n\n\t\t\tparameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',\n\t\t\tparameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',\n\n\t\t\tparameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '',\n\n\t\t\tparameters.numLightProbes > 0 ? '#define USE_LIGHT_PROBES' : '',\n\n\t\t\tparameters.useLegacyLights ? '#define LEGACY_LIGHTS' : '',\n\n\t\t\tparameters.decodeVideoTexture ? '#define DECODE_VIDEO_TEXTURE' : '',\n\n\t\t\tparameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',\n\t\t\t( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '',\n\n\t\t\t'uniform mat4 viewMatrix;',\n\t\t\t'uniform vec3 cameraPosition;',\n\t\t\t'uniform bool isOrthographic;',\n\n\t\t\t( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '',\n\t\t\t( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below\n\t\t\t( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '',\n\n\t\t\tparameters.dithering ? '#define DITHERING' : '',\n\t\t\tparameters.opaque ? '#define OPAQUE' : '',\n\n\t\t\tShaderChunk[ 'colorspace_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below\n\t\t\tgetTexelEncodingFunction( 'linearToOutputTexel', parameters.outputColorSpace ),\n\n\t\t\tparameters.useDepthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '',\n\n\t\t\t'\\n'\n\n\t\t].filter( filterEmptyLine ).join( '\\n' );\n\n\t}\n\n\tvertexShader = resolveIncludes( vertexShader );\n\tvertexShader = replaceLightNums( vertexShader, parameters );\n\tvertexShader = replaceClippingPlaneNums( vertexShader, parameters );\n\n\tfragmentShader = resolveIncludes( fragmentShader );\n\tfragmentShader = replaceLightNums( fragmentShader, parameters );\n\tfragmentShader = replaceClippingPlaneNums( fragmentShader, parameters );\n\n\tvertexShader = unrollLoops( vertexShader );\n\tfragmentShader = unrollLoops( fragmentShader );\n\n\tif ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) {\n\n\t\t// GLSL 3.0 conversion for built-in materials and ShaderMaterial\n\n\t\tversionString = '#version 300 es\\n';\n\n\t\tprefixVertex = [\n\t\t\tcustomVertexExtensions,\n\t\t\t'precision mediump sampler2DArray;',\n\t\t\t'#define attribute in',\n\t\t\t'#define varying out',\n\t\t\t'#define texture2D texture'\n\t\t].join( '\\n' ) + '\\n' + prefixVertex;\n\n\t\tprefixFragment = [\n\t\t\t'precision mediump sampler2DArray;',\n\t\t\t'#define varying in',\n\t\t\t( parameters.glslVersion === GLSL3 ) ? '' : 'layout(location = 0) out highp vec4 pc_fragColor;',\n\t\t\t( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor',\n\t\t\t'#define gl_FragDepthEXT gl_FragDepth',\n\t\t\t'#define texture2D texture',\n\t\t\t'#define textureCube texture',\n\t\t\t'#define texture2DProj textureProj',\n\t\t\t'#define texture2DLodEXT textureLod',\n\t\t\t'#define texture2DProjLodEXT textureProjLod',\n\t\t\t'#define textureCubeLodEXT textureLod',\n\t\t\t'#define texture2DGradEXT textureGrad',\n\t\t\t'#define texture2DProjGradEXT textureProjGrad',\n\t\t\t'#define textureCubeGradEXT textureGrad'\n\t\t].join( '\\n' ) + '\\n' + prefixFragment;\n\n\t}\n\n\tconst vertexGlsl = versionString + prefixVertex + vertexShader;\n\tconst fragmentGlsl = versionString + prefixFragment + fragmentShader;\n\n\t// console.log( '*VERTEX*', vertexGlsl );\n\t// console.log( '*FRAGMENT*', fragmentGlsl );\n\n\tconst glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );\n\tconst glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );\n\n\tgl.attachShader( program, glVertexShader );\n\tgl.attachShader( program, glFragmentShader );\n\n\t// Force a particular attribute to index 0.\n\n\tif ( parameters.index0AttributeName !== undefined ) {\n\n\t\tgl.bindAttribLocation( program, 0, parameters.index0AttributeName );\n\n\t} else if ( parameters.morphTargets === true ) {\n\n\t\t// programs with morphTargets displace position out of attribute 0\n\t\tgl.bindAttribLocation( program, 0, 'position' );\n\n\t}\n\n\tgl.linkProgram( program );\n\n\tfunction onFirstUse( self ) {\n\n\t\t// check for link errors\n\t\tif ( renderer.debug.checkShaderErrors ) {\n\n\t\t\tconst programLog = gl.getProgramInfoLog( program ).trim();\n\t\t\tconst vertexLog = gl.getShaderInfoLog( glVertexShader ).trim();\n\t\t\tconst fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim();\n\n\t\t\tlet runnable = true;\n\t\t\tlet haveDiagnostics = true;\n\n\t\t\tif ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {\n\n\t\t\t\trunnable = false;\n\n\t\t\t\tif ( typeof renderer.debug.onShaderError === 'function' ) {\n\n\t\t\t\t\trenderer.debug.onShaderError( gl, program, glVertexShader, glFragmentShader );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// default error reporting\n\n\t\t\t\t\tconst vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' );\n\t\t\t\t\tconst fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' );\n\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t'THREE.WebGLProgram: Shader Error ' + gl.getError() + ' - ' +\n\t\t\t\t\t\t'VALIDATE_STATUS ' + gl.getProgramParameter( program, gl.VALIDATE_STATUS ) + '\\n\\n' +\n\t\t\t\t\t\t'Program Info Log: ' + programLog + '\\n' +\n\t\t\t\t\t\tvertexErrors + '\\n' +\n\t\t\t\t\t\tfragmentErrors\n\t\t\t\t\t);\n\n\t\t\t\t}\n\n\t\t\t} else if ( programLog !== '' ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLProgram: Program Info Log:', programLog );\n\n\t\t\t} else if ( vertexLog === '' || fragmentLog === '' ) {\n\n\t\t\t\thaveDiagnostics = false;\n\n\t\t\t}\n\n\t\t\tif ( haveDiagnostics ) {\n\n\t\t\t\tself.diagnostics = {\n\n\t\t\t\t\trunnable: runnable,\n\n\t\t\t\t\tprogramLog: programLog,\n\n\t\t\t\t\tvertexShader: {\n\n\t\t\t\t\t\tlog: vertexLog,\n\t\t\t\t\t\tprefix: prefixVertex\n\n\t\t\t\t\t},\n\n\t\t\t\t\tfragmentShader: {\n\n\t\t\t\t\t\tlog: fragmentLog,\n\t\t\t\t\t\tprefix: prefixFragment\n\n\t\t\t\t\t}\n\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Clean up\n\n\t\t// Crashes in iOS9 and iOS10. #18402\n\t\t// gl.detachShader( program, glVertexShader );\n\t\t// gl.detachShader( program, glFragmentShader );\n\n\t\tgl.deleteShader( glVertexShader );\n\t\tgl.deleteShader( glFragmentShader );\n\n\t\tcachedUniforms = new WebGLUniforms( gl, program );\n\t\tcachedAttributes = fetchAttributeLocations( gl, program );\n\n\t}\n\n\t// set up caching for uniform locations\n\n\tlet cachedUniforms;\n\n\tthis.getUniforms = function () {\n\n\t\tif ( cachedUniforms === undefined ) {\n\n\t\t\t// Populates cachedUniforms and cachedAttributes\n\t\t\tonFirstUse( this );\n\n\t\t}\n\n\t\treturn cachedUniforms;\n\n\t};\n\n\t// set up caching for attribute locations\n\n\tlet cachedAttributes;\n\n\tthis.getAttributes = function () {\n\n\t\tif ( cachedAttributes === undefined ) {\n\n\t\t\t// Populates cachedAttributes and cachedUniforms\n\t\t\tonFirstUse( this );\n\n\t\t}\n\n\t\treturn cachedAttributes;\n\n\t};\n\n\t// indicate when the program is ready to be used. if the KHR_parallel_shader_compile extension isn't supported,\n\t// flag the program as ready immediately. It may cause a stall when it's first used.\n\n\tlet programReady = ( parameters.rendererExtensionParallelShaderCompile === false );\n\n\tthis.isReady = function () {\n\n\t\tif ( programReady === false ) {\n\n\t\t\tprogramReady = gl.getProgramParameter( program, COMPLETION_STATUS_KHR );\n\n\t\t}\n\n\t\treturn programReady;\n\n\t};\n\n\t// free resource\n\n\tthis.destroy = function () {\n\n\t\tbindingStates.releaseStatesOfProgram( this );\n\n\t\tgl.deleteProgram( program );\n\t\tthis.program = undefined;\n\n\t};\n\n\t//\n\n\tthis.type = parameters.shaderType;\n\tthis.name = parameters.shaderName;\n\tthis.id = programIdCount ++;\n\tthis.cacheKey = cacheKey;\n\tthis.usedTimes = 1;\n\tthis.program = program;\n\tthis.vertexShader = glVertexShader;\n\tthis.fragmentShader = glFragmentShader;\n\n\treturn this;\n\n}\n\nlet _id$1 = 0;\n\nclass WebGLShaderCache {\n\n\tconstructor() {\n\n\t\tthis.shaderCache = new Map();\n\t\tthis.materialCache = new Map();\n\n\t}\n\n\tupdate( material ) {\n\n\t\tconst vertexShader = material.vertexShader;\n\t\tconst fragmentShader = material.fragmentShader;\n\n\t\tconst vertexShaderStage = this._getShaderStage( vertexShader );\n\t\tconst fragmentShaderStage = this._getShaderStage( fragmentShader );\n\n\t\tconst materialShaders = this._getShaderCacheForMaterial( material );\n\n\t\tif ( materialShaders.has( vertexShaderStage ) === false ) {\n\n\t\t\tmaterialShaders.add( vertexShaderStage );\n\t\t\tvertexShaderStage.usedTimes ++;\n\n\t\t}\n\n\t\tif ( materialShaders.has( fragmentShaderStage ) === false ) {\n\n\t\t\tmaterialShaders.add( fragmentShaderStage );\n\t\t\tfragmentShaderStage.usedTimes ++;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tremove( material ) {\n\n\t\tconst materialShaders = this.materialCache.get( material );\n\n\t\tfor ( const shaderStage of materialShaders ) {\n\n\t\t\tshaderStage.usedTimes --;\n\n\t\t\tif ( shaderStage.usedTimes === 0 ) this.shaderCache.delete( shaderStage.code );\n\n\t\t}\n\n\t\tthis.materialCache.delete( material );\n\n\t\treturn this;\n\n\t}\n\n\tgetVertexShaderID( material ) {\n\n\t\treturn this._getShaderStage( material.vertexShader ).id;\n\n\t}\n\n\tgetFragmentShaderID( material ) {\n\n\t\treturn this._getShaderStage( material.fragmentShader ).id;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.shaderCache.clear();\n\t\tthis.materialCache.clear();\n\n\t}\n\n\t_getShaderCacheForMaterial( material ) {\n\n\t\tconst cache = this.materialCache;\n\t\tlet set = cache.get( material );\n\n\t\tif ( set === undefined ) {\n\n\t\t\tset = new Set();\n\t\t\tcache.set( material, set );\n\n\t\t}\n\n\t\treturn set;\n\n\t}\n\n\t_getShaderStage( code ) {\n\n\t\tconst cache = this.shaderCache;\n\t\tlet stage = cache.get( code );\n\n\t\tif ( stage === undefined ) {\n\n\t\t\tstage = new WebGLShaderStage( code );\n\t\t\tcache.set( code, stage );\n\n\t\t}\n\n\t\treturn stage;\n\n\t}\n\n}\n\nclass WebGLShaderStage {\n\n\tconstructor( code ) {\n\n\t\tthis.id = _id$1 ++;\n\n\t\tthis.code = code;\n\t\tthis.usedTimes = 0;\n\n\t}\n\n}\n\nfunction WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ) {\n\n\tconst _programLayers = new Layers();\n\tconst _customShaders = new WebGLShaderCache();\n\tconst programs = [];\n\n\tconst IS_WEBGL2 = capabilities.isWebGL2;\n\tconst logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer;\n\tconst SUPPORTS_VERTEX_TEXTURES = capabilities.vertexTextures;\n\n\tlet precision = capabilities.precision;\n\n\tconst shaderIDs = {\n\t\tMeshDepthMaterial: 'depth',\n\t\tMeshDistanceMaterial: 'distanceRGBA',\n\t\tMeshNormalMaterial: 'normal',\n\t\tMeshBasicMaterial: 'basic',\n\t\tMeshLambertMaterial: 'lambert',\n\t\tMeshPhongMaterial: 'phong',\n\t\tMeshToonMaterial: 'toon',\n\t\tMeshStandardMaterial: 'physical',\n\t\tMeshPhysicalMaterial: 'physical',\n\t\tMeshMatcapMaterial: 'matcap',\n\t\tLineBasicMaterial: 'basic',\n\t\tLineDashedMaterial: 'dashed',\n\t\tPointsMaterial: 'points',\n\t\tShadowMaterial: 'shadow',\n\t\tSpriteMaterial: 'sprite'\n\t};\n\n\tfunction getChannel( value ) {\n\n\t\tif ( value === 0 ) return 'uv';\n\n\t\treturn `uv${ value }`;\n\n\t}\n\n\tfunction getParameters( material, lights, shadows, scene, object ) {\n\n\t\tconst fog = scene.fog;\n\t\tconst geometry = object.geometry;\n\t\tconst environment = material.isMeshStandardMaterial ? scene.environment : null;\n\n\t\tconst envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment );\n\t\tconst envMapCubeUVHeight = ( !! envMap ) && ( envMap.mapping === CubeUVReflectionMapping ) ? envMap.image.height : null;\n\n\t\tconst shaderID = shaderIDs[ material.type ];\n\n\t\t// heuristics to create shader parameters according to lights in the scene\n\t\t// (not to blow over maxLights budget)\n\n\t\tif ( material.precision !== null ) {\n\n\t\t\tprecision = capabilities.getMaxPrecision( material.precision );\n\n\t\t\tif ( precision !== material.precision ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' );\n\n\t\t\t}\n\n\t\t}\n\n\t\t//\n\n\t\tconst morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;\n\t\tconst morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;\n\n\t\tlet morphTextureStride = 0;\n\n\t\tif ( geometry.morphAttributes.position !== undefined ) morphTextureStride = 1;\n\t\tif ( geometry.morphAttributes.normal !== undefined ) morphTextureStride = 2;\n\t\tif ( geometry.morphAttributes.color !== undefined ) morphTextureStride = 3;\n\n\t\t//\n\n\t\tlet vertexShader, fragmentShader;\n\t\tlet customVertexShaderID, customFragmentShaderID;\n\n\t\tif ( shaderID ) {\n\n\t\t\tconst shader = ShaderLib[ shaderID ];\n\n\t\t\tvertexShader = shader.vertexShader;\n\t\t\tfragmentShader = shader.fragmentShader;\n\n\t\t} else {\n\n\t\t\tvertexShader = material.vertexShader;\n\t\t\tfragmentShader = material.fragmentShader;\n\n\t\t\t_customShaders.update( material );\n\n\t\t\tcustomVertexShaderID = _customShaders.getVertexShaderID( material );\n\t\t\tcustomFragmentShaderID = _customShaders.getFragmentShaderID( material );\n\n\t\t}\n\n\t\tconst currentRenderTarget = renderer.getRenderTarget();\n\n\t\tconst IS_INSTANCEDMESH = object.isInstancedMesh === true;\n\t\tconst IS_BATCHEDMESH = object.isBatchedMesh === true;\n\n\t\tconst HAS_MAP = !! material.map;\n\t\tconst HAS_MATCAP = !! material.matcap;\n\t\tconst HAS_ENVMAP = !! envMap;\n\t\tconst HAS_AOMAP = !! material.aoMap;\n\t\tconst HAS_LIGHTMAP = !! material.lightMap;\n\t\tconst HAS_BUMPMAP = !! material.bumpMap;\n\t\tconst HAS_NORMALMAP = !! material.normalMap;\n\t\tconst HAS_DISPLACEMENTMAP = !! material.displacementMap;\n\t\tconst HAS_EMISSIVEMAP = !! material.emissiveMap;\n\n\t\tconst HAS_METALNESSMAP = !! material.metalnessMap;\n\t\tconst HAS_ROUGHNESSMAP = !! material.roughnessMap;\n\n\t\tconst HAS_ANISOTROPY = material.anisotropy > 0;\n\t\tconst HAS_CLEARCOAT = material.clearcoat > 0;\n\t\tconst HAS_IRIDESCENCE = material.iridescence > 0;\n\t\tconst HAS_SHEEN = material.sheen > 0;\n\t\tconst HAS_TRANSMISSION = material.transmission > 0;\n\n\t\tconst HAS_ANISOTROPYMAP = HAS_ANISOTROPY && !! material.anisotropyMap;\n\n\t\tconst HAS_CLEARCOATMAP = HAS_CLEARCOAT && !! material.clearcoatMap;\n\t\tconst HAS_CLEARCOAT_NORMALMAP = HAS_CLEARCOAT && !! material.clearcoatNormalMap;\n\t\tconst HAS_CLEARCOAT_ROUGHNESSMAP = HAS_CLEARCOAT && !! material.clearcoatRoughnessMap;\n\n\t\tconst HAS_IRIDESCENCEMAP = HAS_IRIDESCENCE && !! material.iridescenceMap;\n\t\tconst HAS_IRIDESCENCE_THICKNESSMAP = HAS_IRIDESCENCE && !! material.iridescenceThicknessMap;\n\n\t\tconst HAS_SHEEN_COLORMAP = HAS_SHEEN && !! material.sheenColorMap;\n\t\tconst HAS_SHEEN_ROUGHNESSMAP = HAS_SHEEN && !! material.sheenRoughnessMap;\n\n\t\tconst HAS_SPECULARMAP = !! material.specularMap;\n\t\tconst HAS_SPECULAR_COLORMAP = !! material.specularColorMap;\n\t\tconst HAS_SPECULAR_INTENSITYMAP = !! material.specularIntensityMap;\n\n\t\tconst HAS_TRANSMISSIONMAP = HAS_TRANSMISSION && !! material.transmissionMap;\n\t\tconst HAS_THICKNESSMAP = HAS_TRANSMISSION && !! material.thicknessMap;\n\n\t\tconst HAS_GRADIENTMAP = !! material.gradientMap;\n\n\t\tconst HAS_ALPHAMAP = !! material.alphaMap;\n\n\t\tconst HAS_ALPHATEST = material.alphaTest > 0;\n\n\t\tconst HAS_ALPHAHASH = !! material.alphaHash;\n\n\t\tconst HAS_EXTENSIONS = !! material.extensions;\n\n\t\tconst HAS_ATTRIBUTE_UV1 = !! geometry.attributes.uv1;\n\t\tconst HAS_ATTRIBUTE_UV2 = !! geometry.attributes.uv2;\n\t\tconst HAS_ATTRIBUTE_UV3 = !! geometry.attributes.uv3;\n\n\t\tlet toneMapping = NoToneMapping;\n\n\t\tif ( material.toneMapped ) {\n\n\t\t\tif ( currentRenderTarget === null || currentRenderTarget.isXRRenderTarget === true ) {\n\n\t\t\t\ttoneMapping = renderer.toneMapping;\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst parameters = {\n\n\t\t\tisWebGL2: IS_WEBGL2,\n\n\t\t\tshaderID: shaderID,\n\t\t\tshaderType: material.type,\n\t\t\tshaderName: material.name,\n\n\t\t\tvertexShader: vertexShader,\n\t\t\tfragmentShader: fragmentShader,\n\t\t\tdefines: material.defines,\n\n\t\t\tcustomVertexShaderID: customVertexShaderID,\n\t\t\tcustomFragmentShaderID: customFragmentShaderID,\n\n\t\t\tisRawShaderMaterial: material.isRawShaderMaterial === true,\n\t\t\tglslVersion: material.glslVersion,\n\n\t\t\tprecision: precision,\n\n\t\t\tbatching: IS_BATCHEDMESH,\n\t\t\tinstancing: IS_INSTANCEDMESH,\n\t\t\tinstancingColor: IS_INSTANCEDMESH && object.instanceColor !== null,\n\n\t\t\tsupportsVertexTextures: SUPPORTS_VERTEX_TEXTURES,\n\t\t\toutputColorSpace: ( currentRenderTarget === null ) ? renderer.outputColorSpace : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ),\n\n\t\t\tmap: HAS_MAP,\n\t\t\tmatcap: HAS_MATCAP,\n\t\t\tenvMap: HAS_ENVMAP,\n\t\t\tenvMapMode: HAS_ENVMAP && envMap.mapping,\n\t\t\tenvMapCubeUVHeight: envMapCubeUVHeight,\n\t\t\taoMap: HAS_AOMAP,\n\t\t\tlightMap: HAS_LIGHTMAP,\n\t\t\tbumpMap: HAS_BUMPMAP,\n\t\t\tnormalMap: HAS_NORMALMAP,\n\t\t\tdisplacementMap: SUPPORTS_VERTEX_TEXTURES && HAS_DISPLACEMENTMAP,\n\t\t\temissiveMap: HAS_EMISSIVEMAP,\n\n\t\t\tnormalMapObjectSpace: HAS_NORMALMAP && material.normalMapType === ObjectSpaceNormalMap,\n\t\t\tnormalMapTangentSpace: HAS_NORMALMAP && material.normalMapType === TangentSpaceNormalMap,\n\n\t\t\tmetalnessMap: HAS_METALNESSMAP,\n\t\t\troughnessMap: HAS_ROUGHNESSMAP,\n\n\t\t\tanisotropy: HAS_ANISOTROPY,\n\t\t\tanisotropyMap: HAS_ANISOTROPYMAP,\n\n\t\t\tclearcoat: HAS_CLEARCOAT,\n\t\t\tclearcoatMap: HAS_CLEARCOATMAP,\n\t\t\tclearcoatNormalMap: HAS_CLEARCOAT_NORMALMAP,\n\t\t\tclearcoatRoughnessMap: HAS_CLEARCOAT_ROUGHNESSMAP,\n\n\t\t\tiridescence: HAS_IRIDESCENCE,\n\t\t\tiridescenceMap: HAS_IRIDESCENCEMAP,\n\t\t\tiridescenceThicknessMap: HAS_IRIDESCENCE_THICKNESSMAP,\n\n\t\t\tsheen: HAS_SHEEN,\n\t\t\tsheenColorMap: HAS_SHEEN_COLORMAP,\n\t\t\tsheenRoughnessMap: HAS_SHEEN_ROUGHNESSMAP,\n\n\t\t\tspecularMap: HAS_SPECULARMAP,\n\t\t\tspecularColorMap: HAS_SPECULAR_COLORMAP,\n\t\t\tspecularIntensityMap: HAS_SPECULAR_INTENSITYMAP,\n\n\t\t\ttransmission: HAS_TRANSMISSION,\n\t\t\ttransmissionMap: HAS_TRANSMISSIONMAP,\n\t\t\tthicknessMap: HAS_THICKNESSMAP,\n\n\t\t\tgradientMap: HAS_GRADIENTMAP,\n\n\t\t\topaque: material.transparent === false && material.blending === NormalBlending,\n\n\t\t\talphaMap: HAS_ALPHAMAP,\n\t\t\talphaTest: HAS_ALPHATEST,\n\t\t\talphaHash: HAS_ALPHAHASH,\n\n\t\t\tcombine: material.combine,\n\n\t\t\t//\n\n\t\t\tmapUv: HAS_MAP && getChannel( material.map.channel ),\n\t\t\taoMapUv: HAS_AOMAP && getChannel( material.aoMap.channel ),\n\t\t\tlightMapUv: HAS_LIGHTMAP && getChannel( material.lightMap.channel ),\n\t\t\tbumpMapUv: HAS_BUMPMAP && getChannel( material.bumpMap.channel ),\n\t\t\tnormalMapUv: HAS_NORMALMAP && getChannel( material.normalMap.channel ),\n\t\t\tdisplacementMapUv: HAS_DISPLACEMENTMAP && getChannel( material.displacementMap.channel ),\n\t\t\temissiveMapUv: HAS_EMISSIVEMAP && getChannel( material.emissiveMap.channel ),\n\n\t\t\tmetalnessMapUv: HAS_METALNESSMAP && getChannel( material.metalnessMap.channel ),\n\t\t\troughnessMapUv: HAS_ROUGHNESSMAP && getChannel( material.roughnessMap.channel ),\n\n\t\t\tanisotropyMapUv: HAS_ANISOTROPYMAP && getChannel( material.anisotropyMap.channel ),\n\n\t\t\tclearcoatMapUv: HAS_CLEARCOATMAP && getChannel( material.clearcoatMap.channel ),\n\t\t\tclearcoatNormalMapUv: HAS_CLEARCOAT_NORMALMAP && getChannel( material.clearcoatNormalMap.channel ),\n\t\t\tclearcoatRoughnessMapUv: HAS_CLEARCOAT_ROUGHNESSMAP && getChannel( material.clearcoatRoughnessMap.channel ),\n\n\t\t\tiridescenceMapUv: HAS_IRIDESCENCEMAP && getChannel( material.iridescenceMap.channel ),\n\t\t\tiridescenceThicknessMapUv: HAS_IRIDESCENCE_THICKNESSMAP && getChannel( material.iridescenceThicknessMap.channel ),\n\n\t\t\tsheenColorMapUv: HAS_SHEEN_COLORMAP && getChannel( material.sheenColorMap.channel ),\n\t\t\tsheenRoughnessMapUv: HAS_SHEEN_ROUGHNESSMAP && getChannel( material.sheenRoughnessMap.channel ),\n\n\t\t\tspecularMapUv: HAS_SPECULARMAP && getChannel( material.specularMap.channel ),\n\t\t\tspecularColorMapUv: HAS_SPECULAR_COLORMAP && getChannel( material.specularColorMap.channel ),\n\t\t\tspecularIntensityMapUv: HAS_SPECULAR_INTENSITYMAP && getChannel( material.specularIntensityMap.channel ),\n\n\t\t\ttransmissionMapUv: HAS_TRANSMISSIONMAP && getChannel( material.transmissionMap.channel ),\n\t\t\tthicknessMapUv: HAS_THICKNESSMAP && getChannel( material.thicknessMap.channel ),\n\n\t\t\talphaMapUv: HAS_ALPHAMAP && getChannel( material.alphaMap.channel ),\n\n\t\t\t//\n\n\t\t\tvertexTangents: !! geometry.attributes.tangent && ( HAS_NORMALMAP || HAS_ANISOTROPY ),\n\t\t\tvertexColors: material.vertexColors,\n\t\t\tvertexAlphas: material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4,\n\t\t\tvertexUv1s: HAS_ATTRIBUTE_UV1,\n\t\t\tvertexUv2s: HAS_ATTRIBUTE_UV2,\n\t\t\tvertexUv3s: HAS_ATTRIBUTE_UV3,\n\n\t\t\tpointsUvs: object.isPoints === true && !! geometry.attributes.uv && ( HAS_MAP || HAS_ALPHAMAP ),\n\n\t\t\tfog: !! fog,\n\t\t\tuseFog: material.fog === true,\n\t\t\tfogExp2: ( fog && fog.isFogExp2 ),\n\n\t\t\tflatShading: material.flatShading === true,\n\n\t\t\tsizeAttenuation: material.sizeAttenuation === true,\n\t\t\tlogarithmicDepthBuffer: logarithmicDepthBuffer,\n\n\t\t\tskinning: object.isSkinnedMesh === true,\n\n\t\t\tmorphTargets: geometry.morphAttributes.position !== undefined,\n\t\t\tmorphNormals: geometry.morphAttributes.normal !== undefined,\n\t\t\tmorphColors: geometry.morphAttributes.color !== undefined,\n\t\t\tmorphTargetsCount: morphTargetsCount,\n\t\t\tmorphTextureStride: morphTextureStride,\n\n\t\t\tnumDirLights: lights.directional.length,\n\t\t\tnumPointLights: lights.point.length,\n\t\t\tnumSpotLights: lights.spot.length,\n\t\t\tnumSpotLightMaps: lights.spotLightMap.length,\n\t\t\tnumRectAreaLights: lights.rectArea.length,\n\t\t\tnumHemiLights: lights.hemi.length,\n\n\t\t\tnumDirLightShadows: lights.directionalShadowMap.length,\n\t\t\tnumPointLightShadows: lights.pointShadowMap.length,\n\t\t\tnumSpotLightShadows: lights.spotShadowMap.length,\n\t\t\tnumSpotLightShadowsWithMaps: lights.numSpotLightShadowsWithMaps,\n\n\t\t\tnumLightProbes: lights.numLightProbes,\n\n\t\t\tnumClippingPlanes: clipping.numPlanes,\n\t\t\tnumClipIntersection: clipping.numIntersection,\n\n\t\t\tdithering: material.dithering,\n\n\t\t\tshadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0,\n\t\t\tshadowMapType: renderer.shadowMap.type,\n\n\t\t\ttoneMapping: toneMapping,\n\t\t\tuseLegacyLights: renderer._useLegacyLights,\n\n\t\t\tdecodeVideoTexture: HAS_MAP && ( material.map.isVideoTexture === true ) && ( ColorManagement.getTransfer( material.map.colorSpace ) === SRGBTransfer ),\n\n\t\t\tpremultipliedAlpha: material.premultipliedAlpha,\n\n\t\t\tdoubleSided: material.side === DoubleSide,\n\t\t\tflipSided: material.side === BackSide,\n\n\t\t\tuseDepthPacking: material.depthPacking >= 0,\n\t\t\tdepthPacking: material.depthPacking || 0,\n\n\t\t\tindex0AttributeName: material.index0AttributeName,\n\n\t\t\textensionDerivatives: HAS_EXTENSIONS && material.extensions.derivatives === true,\n\t\t\textensionFragDepth: HAS_EXTENSIONS && material.extensions.fragDepth === true,\n\t\t\textensionDrawBuffers: HAS_EXTENSIONS && material.extensions.drawBuffers === true,\n\t\t\textensionShaderTextureLOD: HAS_EXTENSIONS && material.extensions.shaderTextureLOD === true,\n\t\t\textensionClipCullDistance: HAS_EXTENSIONS && material.extensions.clipCullDistance && extensions.has( 'WEBGL_clip_cull_distance' ),\n\n\t\t\trendererExtensionFragDepth: IS_WEBGL2 || extensions.has( 'EXT_frag_depth' ),\n\t\t\trendererExtensionDrawBuffers: IS_WEBGL2 || extensions.has( 'WEBGL_draw_buffers' ),\n\t\t\trendererExtensionShaderTextureLod: IS_WEBGL2 || extensions.has( 'EXT_shader_texture_lod' ),\n\t\t\trendererExtensionParallelShaderCompile: extensions.has( 'KHR_parallel_shader_compile' ),\n\n\t\t\tcustomProgramCacheKey: material.customProgramCacheKey()\n\n\t\t};\n\n\t\treturn parameters;\n\n\t}\n\n\tfunction getProgramCacheKey( parameters ) {\n\n\t\tconst array = [];\n\n\t\tif ( parameters.shaderID ) {\n\n\t\t\tarray.push( parameters.shaderID );\n\n\t\t} else {\n\n\t\t\tarray.push( parameters.customVertexShaderID );\n\t\t\tarray.push( parameters.customFragmentShaderID );\n\n\t\t}\n\n\t\tif ( parameters.defines !== undefined ) {\n\n\t\t\tfor ( const name in parameters.defines ) {\n\n\t\t\t\tarray.push( name );\n\t\t\t\tarray.push( parameters.defines[ name ] );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( parameters.isRawShaderMaterial === false ) {\n\n\t\t\tgetProgramCacheKeyParameters( array, parameters );\n\t\t\tgetProgramCacheKeyBooleans( array, parameters );\n\t\t\tarray.push( renderer.outputColorSpace );\n\n\t\t}\n\n\t\tarray.push( parameters.customProgramCacheKey );\n\n\t\treturn array.join();\n\n\t}\n\n\tfunction getProgramCacheKeyParameters( array, parameters ) {\n\n\t\tarray.push( parameters.precision );\n\t\tarray.push( parameters.outputColorSpace );\n\t\tarray.push( parameters.envMapMode );\n\t\tarray.push( parameters.envMapCubeUVHeight );\n\t\tarray.push( parameters.mapUv );\n\t\tarray.push( parameters.alphaMapUv );\n\t\tarray.push( parameters.lightMapUv );\n\t\tarray.push( parameters.aoMapUv );\n\t\tarray.push( parameters.bumpMapUv );\n\t\tarray.push( parameters.normalMapUv );\n\t\tarray.push( parameters.displacementMapUv );\n\t\tarray.push( parameters.emissiveMapUv );\n\t\tarray.push( parameters.metalnessMapUv );\n\t\tarray.push( parameters.roughnessMapUv );\n\t\tarray.push( parameters.anisotropyMapUv );\n\t\tarray.push( parameters.clearcoatMapUv );\n\t\tarray.push( parameters.clearcoatNormalMapUv );\n\t\tarray.push( parameters.clearcoatRoughnessMapUv );\n\t\tarray.push( parameters.iridescenceMapUv );\n\t\tarray.push( parameters.iridescenceThicknessMapUv );\n\t\tarray.push( parameters.sheenColorMapUv );\n\t\tarray.push( parameters.sheenRoughnessMapUv );\n\t\tarray.push( parameters.specularMapUv );\n\t\tarray.push( parameters.specularColorMapUv );\n\t\tarray.push( parameters.specularIntensityMapUv );\n\t\tarray.push( parameters.transmissionMapUv );\n\t\tarray.push( parameters.thicknessMapUv );\n\t\tarray.push( parameters.combine );\n\t\tarray.push( parameters.fogExp2 );\n\t\tarray.push( parameters.sizeAttenuation );\n\t\tarray.push( parameters.morphTargetsCount );\n\t\tarray.push( parameters.morphAttributeCount );\n\t\tarray.push( parameters.numDirLights );\n\t\tarray.push( parameters.numPointLights );\n\t\tarray.push( parameters.numSpotLights );\n\t\tarray.push( parameters.numSpotLightMaps );\n\t\tarray.push( parameters.numHemiLights );\n\t\tarray.push( parameters.numRectAreaLights );\n\t\tarray.push( parameters.numDirLightShadows );\n\t\tarray.push( parameters.numPointLightShadows );\n\t\tarray.push( parameters.numSpotLightShadows );\n\t\tarray.push( parameters.numSpotLightShadowsWithMaps );\n\t\tarray.push( parameters.numLightProbes );\n\t\tarray.push( parameters.shadowMapType );\n\t\tarray.push( parameters.toneMapping );\n\t\tarray.push( parameters.numClippingPlanes );\n\t\tarray.push( parameters.numClipIntersection );\n\t\tarray.push( parameters.depthPacking );\n\n\t}\n\n\tfunction getProgramCacheKeyBooleans( array, parameters ) {\n\n\t\t_programLayers.disableAll();\n\n\t\tif ( parameters.isWebGL2 )\n\t\t\t_programLayers.enable( 0 );\n\t\tif ( parameters.supportsVertexTextures )\n\t\t\t_programLayers.enable( 1 );\n\t\tif ( parameters.instancing )\n\t\t\t_programLayers.enable( 2 );\n\t\tif ( parameters.instancingColor )\n\t\t\t_programLayers.enable( 3 );\n\t\tif ( parameters.matcap )\n\t\t\t_programLayers.enable( 4 );\n\t\tif ( parameters.envMap )\n\t\t\t_programLayers.enable( 5 );\n\t\tif ( parameters.normalMapObjectSpace )\n\t\t\t_programLayers.enable( 6 );\n\t\tif ( parameters.normalMapTangentSpace )\n\t\t\t_programLayers.enable( 7 );\n\t\tif ( parameters.clearcoat )\n\t\t\t_programLayers.enable( 8 );\n\t\tif ( parameters.iridescence )\n\t\t\t_programLayers.enable( 9 );\n\t\tif ( parameters.alphaTest )\n\t\t\t_programLayers.enable( 10 );\n\t\tif ( parameters.vertexColors )\n\t\t\t_programLayers.enable( 11 );\n\t\tif ( parameters.vertexAlphas )\n\t\t\t_programLayers.enable( 12 );\n\t\tif ( parameters.vertexUv1s )\n\t\t\t_programLayers.enable( 13 );\n\t\tif ( parameters.vertexUv2s )\n\t\t\t_programLayers.enable( 14 );\n\t\tif ( parameters.vertexUv3s )\n\t\t\t_programLayers.enable( 15 );\n\t\tif ( parameters.vertexTangents )\n\t\t\t_programLayers.enable( 16 );\n\t\tif ( parameters.anisotropy )\n\t\t\t_programLayers.enable( 17 );\n\t\tif ( parameters.alphaHash )\n\t\t\t_programLayers.enable( 18 );\n\t\tif ( parameters.batching )\n\t\t\t_programLayers.enable( 19 );\n\n\t\tarray.push( _programLayers.mask );\n\t\t_programLayers.disableAll();\n\n\t\tif ( parameters.fog )\n\t\t\t_programLayers.enable( 0 );\n\t\tif ( parameters.useFog )\n\t\t\t_programLayers.enable( 1 );\n\t\tif ( parameters.flatShading )\n\t\t\t_programLayers.enable( 2 );\n\t\tif ( parameters.logarithmicDepthBuffer )\n\t\t\t_programLayers.enable( 3 );\n\t\tif ( parameters.skinning )\n\t\t\t_programLayers.enable( 4 );\n\t\tif ( parameters.morphTargets )\n\t\t\t_programLayers.enable( 5 );\n\t\tif ( parameters.morphNormals )\n\t\t\t_programLayers.enable( 6 );\n\t\tif ( parameters.morphColors )\n\t\t\t_programLayers.enable( 7 );\n\t\tif ( parameters.premultipliedAlpha )\n\t\t\t_programLayers.enable( 8 );\n\t\tif ( parameters.shadowMapEnabled )\n\t\t\t_programLayers.enable( 9 );\n\t\tif ( parameters.useLegacyLights )\n\t\t\t_programLayers.enable( 10 );\n\t\tif ( parameters.doubleSided )\n\t\t\t_programLayers.enable( 11 );\n\t\tif ( parameters.flipSided )\n\t\t\t_programLayers.enable( 12 );\n\t\tif ( parameters.useDepthPacking )\n\t\t\t_programLayers.enable( 13 );\n\t\tif ( parameters.dithering )\n\t\t\t_programLayers.enable( 14 );\n\t\tif ( parameters.transmission )\n\t\t\t_programLayers.enable( 15 );\n\t\tif ( parameters.sheen )\n\t\t\t_programLayers.enable( 16 );\n\t\tif ( parameters.opaque )\n\t\t\t_programLayers.enable( 17 );\n\t\tif ( parameters.pointsUvs )\n\t\t\t_programLayers.enable( 18 );\n\t\tif ( parameters.decodeVideoTexture )\n\t\t\t_programLayers.enable( 19 );\n\n\t\tarray.push( _programLayers.mask );\n\n\t}\n\n\tfunction getUniforms( material ) {\n\n\t\tconst shaderID = shaderIDs[ material.type ];\n\t\tlet uniforms;\n\n\t\tif ( shaderID ) {\n\n\t\t\tconst shader = ShaderLib[ shaderID ];\n\t\t\tuniforms = UniformsUtils.clone( shader.uniforms );\n\n\t\t} else {\n\n\t\t\tuniforms = material.uniforms;\n\n\t\t}\n\n\t\treturn uniforms;\n\n\t}\n\n\tfunction acquireProgram( parameters, cacheKey ) {\n\n\t\tlet program;\n\n\t\t// Check if code has been already compiled\n\t\tfor ( let p = 0, pl = programs.length; p < pl; p ++ ) {\n\n\t\t\tconst preexistingProgram = programs[ p ];\n\n\t\t\tif ( preexistingProgram.cacheKey === cacheKey ) {\n\n\t\t\t\tprogram = preexistingProgram;\n\t\t\t\t++ program.usedTimes;\n\n\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( program === undefined ) {\n\n\t\t\tprogram = new WebGLProgram( renderer, cacheKey, parameters, bindingStates );\n\t\t\tprograms.push( program );\n\n\t\t}\n\n\t\treturn program;\n\n\t}\n\n\tfunction releaseProgram( program ) {\n\n\t\tif ( -- program.usedTimes === 0 ) {\n\n\t\t\t// Remove from unordered set\n\t\t\tconst i = programs.indexOf( program );\n\t\t\tprograms[ i ] = programs[ programs.length - 1 ];\n\t\t\tprograms.pop();\n\n\t\t\t// Free WebGL resources\n\t\t\tprogram.destroy();\n\n\t\t}\n\n\t}\n\n\tfunction releaseShaderCache( material ) {\n\n\t\t_customShaders.remove( material );\n\n\t}\n\n\tfunction dispose() {\n\n\t\t_customShaders.dispose();\n\n\t}\n\n\treturn {\n\t\tgetParameters: getParameters,\n\t\tgetProgramCacheKey: getProgramCacheKey,\n\t\tgetUniforms: getUniforms,\n\t\tacquireProgram: acquireProgram,\n\t\treleaseProgram: releaseProgram,\n\t\treleaseShaderCache: releaseShaderCache,\n\t\t// Exposed for resource monitoring & error feedback via renderer.info:\n\t\tprograms: programs,\n\t\tdispose: dispose\n\t};\n\n}\n\nfunction WebGLProperties() {\n\n\tlet properties = new WeakMap();\n\n\tfunction get( object ) {\n\n\t\tlet map = properties.get( object );\n\n\t\tif ( map === undefined ) {\n\n\t\t\tmap = {};\n\t\t\tproperties.set( object, map );\n\n\t\t}\n\n\t\treturn map;\n\n\t}\n\n\tfunction remove( object ) {\n\n\t\tproperties.delete( object );\n\n\t}\n\n\tfunction update( object, key, value ) {\n\n\t\tproperties.get( object )[ key ] = value;\n\n\t}\n\n\tfunction dispose() {\n\n\t\tproperties = new WeakMap();\n\n\t}\n\n\treturn {\n\t\tget: get,\n\t\tremove: remove,\n\t\tupdate: update,\n\t\tdispose: dispose\n\t};\n\n}\n\nfunction painterSortStable( a, b ) {\n\n\tif ( a.groupOrder !== b.groupOrder ) {\n\n\t\treturn a.groupOrder - b.groupOrder;\n\n\t} else if ( a.renderOrder !== b.renderOrder ) {\n\n\t\treturn a.renderOrder - b.renderOrder;\n\n\t} else if ( a.material.id !== b.material.id ) {\n\n\t\treturn a.material.id - b.material.id;\n\n\t} else if ( a.z !== b.z ) {\n\n\t\treturn a.z - b.z;\n\n\t} else {\n\n\t\treturn a.id - b.id;\n\n\t}\n\n}\n\nfunction reversePainterSortStable( a, b ) {\n\n\tif ( a.groupOrder !== b.groupOrder ) {\n\n\t\treturn a.groupOrder - b.groupOrder;\n\n\t} else if ( a.renderOrder !== b.renderOrder ) {\n\n\t\treturn a.renderOrder - b.renderOrder;\n\n\t} else if ( a.z !== b.z ) {\n\n\t\treturn b.z - a.z;\n\n\t} else {\n\n\t\treturn a.id - b.id;\n\n\t}\n\n}\n\n\nfunction WebGLRenderList() {\n\n\tconst renderItems = [];\n\tlet renderItemsIndex = 0;\n\n\tconst opaque = [];\n\tconst transmissive = [];\n\tconst transparent = [];\n\n\tfunction init() {\n\n\t\trenderItemsIndex = 0;\n\n\t\topaque.length = 0;\n\t\ttransmissive.length = 0;\n\t\ttransparent.length = 0;\n\n\t}\n\n\tfunction getNextRenderItem( object, geometry, material, groupOrder, z, group ) {\n\n\t\tlet renderItem = renderItems[ renderItemsIndex ];\n\n\t\tif ( renderItem === undefined ) {\n\n\t\t\trenderItem = {\n\t\t\t\tid: object.id,\n\t\t\t\tobject: object,\n\t\t\t\tgeometry: geometry,\n\t\t\t\tmaterial: material,\n\t\t\t\tgroupOrder: groupOrder,\n\t\t\t\trenderOrder: object.renderOrder,\n\t\t\t\tz: z,\n\t\t\t\tgroup: group\n\t\t\t};\n\n\t\t\trenderItems[ renderItemsIndex ] = renderItem;\n\n\t\t} else {\n\n\t\t\trenderItem.id = object.id;\n\t\t\trenderItem.object = object;\n\t\t\trenderItem.geometry = geometry;\n\t\t\trenderItem.material = material;\n\t\t\trenderItem.groupOrder = groupOrder;\n\t\t\trenderItem.renderOrder = object.renderOrder;\n\t\t\trenderItem.z = z;\n\t\t\trenderItem.group = group;\n\n\t\t}\n\n\t\trenderItemsIndex ++;\n\n\t\treturn renderItem;\n\n\t}\n\n\tfunction push( object, geometry, material, groupOrder, z, group ) {\n\n\t\tconst renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group );\n\n\t\tif ( material.transmission > 0.0 ) {\n\n\t\t\ttransmissive.push( renderItem );\n\n\t\t} else if ( material.transparent === true ) {\n\n\t\t\ttransparent.push( renderItem );\n\n\t\t} else {\n\n\t\t\topaque.push( renderItem );\n\n\t\t}\n\n\t}\n\n\tfunction unshift( object, geometry, material, groupOrder, z, group ) {\n\n\t\tconst renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group );\n\n\t\tif ( material.transmission > 0.0 ) {\n\n\t\t\ttransmissive.unshift( renderItem );\n\n\t\t} else if ( material.transparent === true ) {\n\n\t\t\ttransparent.unshift( renderItem );\n\n\t\t} else {\n\n\t\t\topaque.unshift( renderItem );\n\n\t\t}\n\n\t}\n\n\tfunction sort( customOpaqueSort, customTransparentSort ) {\n\n\t\tif ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable );\n\t\tif ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable );\n\t\tif ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable );\n\n\t}\n\n\tfunction finish() {\n\n\t\t// Clear references from inactive renderItems in the list\n\n\t\tfor ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) {\n\n\t\t\tconst renderItem = renderItems[ i ];\n\n\t\t\tif ( renderItem.id === null ) break;\n\n\t\t\trenderItem.id = null;\n\t\t\trenderItem.object = null;\n\t\t\trenderItem.geometry = null;\n\t\t\trenderItem.material = null;\n\t\t\trenderItem.group = null;\n\n\t\t}\n\n\t}\n\n\treturn {\n\n\t\topaque: opaque,\n\t\ttransmissive: transmissive,\n\t\ttransparent: transparent,\n\n\t\tinit: init,\n\t\tpush: push,\n\t\tunshift: unshift,\n\t\tfinish: finish,\n\n\t\tsort: sort\n\t};\n\n}\n\nfunction WebGLRenderLists() {\n\n\tlet lists = new WeakMap();\n\n\tfunction get( scene, renderCallDepth ) {\n\n\t\tconst listArray = lists.get( scene );\n\t\tlet list;\n\n\t\tif ( listArray === undefined ) {\n\n\t\t\tlist = new WebGLRenderList();\n\t\t\tlists.set( scene, [ list ] );\n\n\t\t} else {\n\n\t\t\tif ( renderCallDepth >= listArray.length ) {\n\n\t\t\t\tlist = new WebGLRenderList();\n\t\t\t\tlistArray.push( list );\n\n\t\t\t} else {\n\n\t\t\t\tlist = listArray[ renderCallDepth ];\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn list;\n\n\t}\n\n\tfunction dispose() {\n\n\t\tlists = new WeakMap();\n\n\t}\n\n\treturn {\n\t\tget: get,\n\t\tdispose: dispose\n\t};\n\n}\n\nfunction UniformsCache() {\n\n\tconst lights = {};\n\n\treturn {\n\n\t\tget: function ( light ) {\n\n\t\t\tif ( lights[ light.id ] !== undefined ) {\n\n\t\t\t\treturn lights[ light.id ];\n\n\t\t\t}\n\n\t\t\tlet uniforms;\n\n\t\t\tswitch ( light.type ) {\n\n\t\t\t\tcase 'DirectionalLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tdirection: new Vector3(),\n\t\t\t\t\t\tcolor: new Color()\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'SpotLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tposition: new Vector3(),\n\t\t\t\t\t\tdirection: new Vector3(),\n\t\t\t\t\t\tcolor: new Color(),\n\t\t\t\t\t\tdistance: 0,\n\t\t\t\t\t\tconeCos: 0,\n\t\t\t\t\t\tpenumbraCos: 0,\n\t\t\t\t\t\tdecay: 0\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'PointLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tposition: new Vector3(),\n\t\t\t\t\t\tcolor: new Color(),\n\t\t\t\t\t\tdistance: 0,\n\t\t\t\t\t\tdecay: 0\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'HemisphereLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tdirection: new Vector3(),\n\t\t\t\t\t\tskyColor: new Color(),\n\t\t\t\t\t\tgroundColor: new Color()\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'RectAreaLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tcolor: new Color(),\n\t\t\t\t\t\tposition: new Vector3(),\n\t\t\t\t\t\thalfWidth: new Vector3(),\n\t\t\t\t\t\thalfHeight: new Vector3()\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t\tlights[ light.id ] = uniforms;\n\n\t\t\treturn uniforms;\n\n\t\t}\n\n\t};\n\n}\n\nfunction ShadowUniformsCache() {\n\n\tconst lights = {};\n\n\treturn {\n\n\t\tget: function ( light ) {\n\n\t\t\tif ( lights[ light.id ] !== undefined ) {\n\n\t\t\t\treturn lights[ light.id ];\n\n\t\t\t}\n\n\t\t\tlet uniforms;\n\n\t\t\tswitch ( light.type ) {\n\n\t\t\t\tcase 'DirectionalLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tshadowBias: 0,\n\t\t\t\t\t\tshadowNormalBias: 0,\n\t\t\t\t\t\tshadowRadius: 1,\n\t\t\t\t\t\tshadowMapSize: new Vector2()\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'SpotLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tshadowBias: 0,\n\t\t\t\t\t\tshadowNormalBias: 0,\n\t\t\t\t\t\tshadowRadius: 1,\n\t\t\t\t\t\tshadowMapSize: new Vector2()\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'PointLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tshadowBias: 0,\n\t\t\t\t\t\tshadowNormalBias: 0,\n\t\t\t\t\t\tshadowRadius: 1,\n\t\t\t\t\t\tshadowMapSize: new Vector2(),\n\t\t\t\t\t\tshadowCameraNear: 1,\n\t\t\t\t\t\tshadowCameraFar: 1000\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\t// TODO (abelnation): set RectAreaLight shadow uniforms\n\n\t\t\t}\n\n\t\t\tlights[ light.id ] = uniforms;\n\n\t\t\treturn uniforms;\n\n\t\t}\n\n\t};\n\n}\n\n\n\nlet nextVersion = 0;\n\nfunction shadowCastingAndTexturingLightsFirst( lightA, lightB ) {\n\n\treturn ( lightB.castShadow ? 2 : 0 ) - ( lightA.castShadow ? 2 : 0 ) + ( lightB.map ? 1 : 0 ) - ( lightA.map ? 1 : 0 );\n\n}\n\nfunction WebGLLights( extensions, capabilities ) {\n\n\tconst cache = new UniformsCache();\n\n\tconst shadowCache = ShadowUniformsCache();\n\n\tconst state = {\n\n\t\tversion: 0,\n\n\t\thash: {\n\t\t\tdirectionalLength: - 1,\n\t\t\tpointLength: - 1,\n\t\t\tspotLength: - 1,\n\t\t\trectAreaLength: - 1,\n\t\t\themiLength: - 1,\n\n\t\t\tnumDirectionalShadows: - 1,\n\t\t\tnumPointShadows: - 1,\n\t\t\tnumSpotShadows: - 1,\n\t\t\tnumSpotMaps: - 1,\n\n\t\t\tnumLightProbes: - 1\n\t\t},\n\n\t\tambient: [ 0, 0, 0 ],\n\t\tprobe: [],\n\t\tdirectional: [],\n\t\tdirectionalShadow: [],\n\t\tdirectionalShadowMap: [],\n\t\tdirectionalShadowMatrix: [],\n\t\tspot: [],\n\t\tspotLightMap: [],\n\t\tspotShadow: [],\n\t\tspotShadowMap: [],\n\t\tspotLightMatrix: [],\n\t\trectArea: [],\n\t\trectAreaLTC1: null,\n\t\trectAreaLTC2: null,\n\t\tpoint: [],\n\t\tpointShadow: [],\n\t\tpointShadowMap: [],\n\t\tpointShadowMatrix: [],\n\t\themi: [],\n\t\tnumSpotLightShadowsWithMaps: 0,\n\t\tnumLightProbes: 0\n\n\t};\n\n\tfor ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() );\n\n\tconst vector3 = new Vector3();\n\tconst matrix4 = new Matrix4();\n\tconst matrix42 = new Matrix4();\n\n\tfunction setup( lights, useLegacyLights ) {\n\n\t\tlet r = 0, g = 0, b = 0;\n\n\t\tfor ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 );\n\n\t\tlet directionalLength = 0;\n\t\tlet pointLength = 0;\n\t\tlet spotLength = 0;\n\t\tlet rectAreaLength = 0;\n\t\tlet hemiLength = 0;\n\n\t\tlet numDirectionalShadows = 0;\n\t\tlet numPointShadows = 0;\n\t\tlet numSpotShadows = 0;\n\t\tlet numSpotMaps = 0;\n\t\tlet numSpotShadowsWithMaps = 0;\n\n\t\tlet numLightProbes = 0;\n\n\t\t// ordering : [shadow casting + map texturing, map texturing, shadow casting, none ]\n\t\tlights.sort( shadowCastingAndTexturingLightsFirst );\n\n\t\t// artist-friendly light intensity scaling factor\n\t\tconst scaleFactor = ( useLegacyLights === true ) ? Math.PI : 1;\n\n\t\tfor ( let i = 0, l = lights.length; i < l; i ++ ) {\n\n\t\t\tconst light = lights[ i ];\n\n\t\t\tconst color = light.color;\n\t\t\tconst intensity = light.intensity;\n\t\t\tconst distance = light.distance;\n\n\t\t\tconst shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null;\n\n\t\t\tif ( light.isAmbientLight ) {\n\n\t\t\t\tr += color.r * intensity * scaleFactor;\n\t\t\t\tg += color.g * intensity * scaleFactor;\n\t\t\t\tb += color.b * intensity * scaleFactor;\n\n\t\t\t} else if ( light.isLightProbe ) {\n\n\t\t\t\tfor ( let j = 0; j < 9; j ++ ) {\n\n\t\t\t\t\tstate.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity );\n\n\t\t\t\t}\n\n\t\t\t\tnumLightProbes ++;\n\n\t\t\t} else if ( light.isDirectionalLight ) {\n\n\t\t\t\tconst uniforms = cache.get( light );\n\n\t\t\t\tuniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor );\n\n\t\t\t\tif ( light.castShadow ) {\n\n\t\t\t\t\tconst shadow = light.shadow;\n\n\t\t\t\t\tconst shadowUniforms = shadowCache.get( light );\n\n\t\t\t\t\tshadowUniforms.shadowBias = shadow.bias;\n\t\t\t\t\tshadowUniforms.shadowNormalBias = shadow.normalBias;\n\t\t\t\t\tshadowUniforms.shadowRadius = shadow.radius;\n\t\t\t\t\tshadowUniforms.shadowMapSize = shadow.mapSize;\n\n\t\t\t\t\tstate.directionalShadow[ directionalLength ] = shadowUniforms;\n\t\t\t\t\tstate.directionalShadowMap[ directionalLength ] = shadowMap;\n\t\t\t\t\tstate.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix;\n\n\t\t\t\t\tnumDirectionalShadows ++;\n\n\t\t\t\t}\n\n\t\t\t\tstate.directional[ directionalLength ] = uniforms;\n\n\t\t\t\tdirectionalLength ++;\n\n\t\t\t} else if ( light.isSpotLight ) {\n\n\t\t\t\tconst uniforms = cache.get( light );\n\n\t\t\t\tuniforms.position.setFromMatrixPosition( light.matrixWorld );\n\n\t\t\t\tuniforms.color.copy( color ).multiplyScalar( intensity * scaleFactor );\n\t\t\t\tuniforms.distance = distance;\n\n\t\t\t\tuniforms.coneCos = Math.cos( light.angle );\n\t\t\t\tuniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) );\n\t\t\t\tuniforms.decay = light.decay;\n\n\t\t\t\tstate.spot[ spotLength ] = uniforms;\n\n\t\t\t\tconst shadow = light.shadow;\n\n\t\t\t\tif ( light.map ) {\n\n\t\t\t\t\tstate.spotLightMap[ numSpotMaps ] = light.map;\n\t\t\t\t\tnumSpotMaps ++;\n\n\t\t\t\t\t// make sure the lightMatrix is up to date\n\t\t\t\t\t// TODO : do it if required only\n\t\t\t\t\tshadow.updateMatrices( light );\n\n\t\t\t\t\tif ( light.castShadow ) numSpotShadowsWithMaps ++;\n\n\t\t\t\t}\n\n\t\t\t\tstate.spotLightMatrix[ spotLength ] = shadow.matrix;\n\n\t\t\t\tif ( light.castShadow ) {\n\n\t\t\t\t\tconst shadowUniforms = shadowCache.get( light );\n\n\t\t\t\t\tshadowUniforms.shadowBias = shadow.bias;\n\t\t\t\t\tshadowUniforms.shadowNormalBias = shadow.normalBias;\n\t\t\t\t\tshadowUniforms.shadowRadius = shadow.radius;\n\t\t\t\t\tshadowUniforms.shadowMapSize = shadow.mapSize;\n\n\t\t\t\t\tstate.spotShadow[ spotLength ] = shadowUniforms;\n\t\t\t\t\tstate.spotShadowMap[ spotLength ] = shadowMap;\n\n\t\t\t\t\tnumSpotShadows ++;\n\n\t\t\t\t}\n\n\t\t\t\tspotLength ++;\n\n\t\t\t} else if ( light.isRectAreaLight ) {\n\n\t\t\t\tconst uniforms = cache.get( light );\n\n\t\t\t\tuniforms.color.copy( color ).multiplyScalar( intensity );\n\n\t\t\t\tuniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );\n\t\t\t\tuniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );\n\n\t\t\t\tstate.rectArea[ rectAreaLength ] = uniforms;\n\n\t\t\t\trectAreaLength ++;\n\n\t\t\t} else if ( light.isPointLight ) {\n\n\t\t\t\tconst uniforms = cache.get( light );\n\n\t\t\t\tuniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor );\n\t\t\t\tuniforms.distance = light.distance;\n\t\t\t\tuniforms.decay = light.decay;\n\n\t\t\t\tif ( light.castShadow ) {\n\n\t\t\t\t\tconst shadow = light.shadow;\n\n\t\t\t\t\tconst shadowUniforms = shadowCache.get( light );\n\n\t\t\t\t\tshadowUniforms.shadowBias = shadow.bias;\n\t\t\t\t\tshadowUniforms.shadowNormalBias = shadow.normalBias;\n\t\t\t\t\tshadowUniforms.shadowRadius = shadow.radius;\n\t\t\t\t\tshadowUniforms.shadowMapSize = shadow.mapSize;\n\t\t\t\t\tshadowUniforms.shadowCameraNear = shadow.camera.near;\n\t\t\t\t\tshadowUniforms.shadowCameraFar = shadow.camera.far;\n\n\t\t\t\t\tstate.pointShadow[ pointLength ] = shadowUniforms;\n\t\t\t\t\tstate.pointShadowMap[ pointLength ] = shadowMap;\n\t\t\t\t\tstate.pointShadowMatrix[ pointLength ] = light.shadow.matrix;\n\n\t\t\t\t\tnumPointShadows ++;\n\n\t\t\t\t}\n\n\t\t\t\tstate.point[ pointLength ] = uniforms;\n\n\t\t\t\tpointLength ++;\n\n\t\t\t} else if ( light.isHemisphereLight ) {\n\n\t\t\t\tconst uniforms = cache.get( light );\n\n\t\t\t\tuniforms.skyColor.copy( light.color ).multiplyScalar( intensity * scaleFactor );\n\t\t\t\tuniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity * scaleFactor );\n\n\t\t\t\tstate.hemi[ hemiLength ] = uniforms;\n\n\t\t\t\themiLength ++;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( rectAreaLength > 0 ) {\n\n\t\t\tif ( capabilities.isWebGL2 ) {\n\n\t\t\t\t// WebGL 2\n\n\t\t\t\tif ( extensions.has( 'OES_texture_float_linear' ) === true ) {\n\n\t\t\t\t\tstate.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1;\n\t\t\t\t\tstate.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tstate.rectAreaLTC1 = UniformsLib.LTC_HALF_1;\n\t\t\t\t\tstate.rectAreaLTC2 = UniformsLib.LTC_HALF_2;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// WebGL 1\n\n\t\t\t\tif ( extensions.has( 'OES_texture_float_linear' ) === true ) {\n\n\t\t\t\t\tstate.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1;\n\t\t\t\t\tstate.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2;\n\n\t\t\t\t} else if ( extensions.has( 'OES_texture_half_float_linear' ) === true ) {\n\n\t\t\t\t\tstate.rectAreaLTC1 = UniformsLib.LTC_HALF_1;\n\t\t\t\t\tstate.rectAreaLTC2 = UniformsLib.LTC_HALF_2;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconsole.error( 'THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.' );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tstate.ambient[ 0 ] = r;\n\t\tstate.ambient[ 1 ] = g;\n\t\tstate.ambient[ 2 ] = b;\n\n\t\tconst hash = state.hash;\n\n\t\tif ( hash.directionalLength !== directionalLength ||\n\t\t\thash.pointLength !== pointLength ||\n\t\t\thash.spotLength !== spotLength ||\n\t\t\thash.rectAreaLength !== rectAreaLength ||\n\t\t\thash.hemiLength !== hemiLength ||\n\t\t\thash.numDirectionalShadows !== numDirectionalShadows ||\n\t\t\thash.numPointShadows !== numPointShadows ||\n\t\t\thash.numSpotShadows !== numSpotShadows ||\n\t\t\thash.numSpotMaps !== numSpotMaps ||\n\t\t\thash.numLightProbes !== numLightProbes ) {\n\n\t\t\tstate.directional.length = directionalLength;\n\t\t\tstate.spot.length = spotLength;\n\t\t\tstate.rectArea.length = rectAreaLength;\n\t\t\tstate.point.length = pointLength;\n\t\t\tstate.hemi.length = hemiLength;\n\n\t\t\tstate.directionalShadow.length = numDirectionalShadows;\n\t\t\tstate.directionalShadowMap.length = numDirectionalShadows;\n\t\t\tstate.pointShadow.length = numPointShadows;\n\t\t\tstate.pointShadowMap.length = numPointShadows;\n\t\t\tstate.spotShadow.length = numSpotShadows;\n\t\t\tstate.spotShadowMap.length = numSpotShadows;\n\t\t\tstate.directionalShadowMatrix.length = numDirectionalShadows;\n\t\t\tstate.pointShadowMatrix.length = numPointShadows;\n\t\t\tstate.spotLightMatrix.length = numSpotShadows + numSpotMaps - numSpotShadowsWithMaps;\n\t\t\tstate.spotLightMap.length = numSpotMaps;\n\t\t\tstate.numSpotLightShadowsWithMaps = numSpotShadowsWithMaps;\n\t\t\tstate.numLightProbes = numLightProbes;\n\n\t\t\thash.directionalLength = directionalLength;\n\t\t\thash.pointLength = pointLength;\n\t\t\thash.spotLength = spotLength;\n\t\t\thash.rectAreaLength = rectAreaLength;\n\t\t\thash.hemiLength = hemiLength;\n\n\t\t\thash.numDirectionalShadows = numDirectionalShadows;\n\t\t\thash.numPointShadows = numPointShadows;\n\t\t\thash.numSpotShadows = numSpotShadows;\n\t\t\thash.numSpotMaps = numSpotMaps;\n\n\t\t\thash.numLightProbes = numLightProbes;\n\n\t\t\tstate.version = nextVersion ++;\n\n\t\t}\n\n\t}\n\n\tfunction setupView( lights, camera ) {\n\n\t\tlet directionalLength = 0;\n\t\tlet pointLength = 0;\n\t\tlet spotLength = 0;\n\t\tlet rectAreaLength = 0;\n\t\tlet hemiLength = 0;\n\n\t\tconst viewMatrix = camera.matrixWorldInverse;\n\n\t\tfor ( let i = 0, l = lights.length; i < l; i ++ ) {\n\n\t\t\tconst light = lights[ i ];\n\n\t\t\tif ( light.isDirectionalLight ) {\n\n\t\t\t\tconst uniforms = state.directional[ directionalLength ];\n\n\t\t\t\tuniforms.direction.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tvector3.setFromMatrixPosition( light.target.matrixWorld );\n\t\t\t\tuniforms.direction.sub( vector3 );\n\t\t\t\tuniforms.direction.transformDirection( viewMatrix );\n\n\t\t\t\tdirectionalLength ++;\n\n\t\t\t} else if ( light.isSpotLight ) {\n\n\t\t\t\tconst uniforms = state.spot[ spotLength ];\n\n\t\t\t\tuniforms.position.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tuniforms.position.applyMatrix4( viewMatrix );\n\n\t\t\t\tuniforms.direction.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tvector3.setFromMatrixPosition( light.target.matrixWorld );\n\t\t\t\tuniforms.direction.sub( vector3 );\n\t\t\t\tuniforms.direction.transformDirection( viewMatrix );\n\n\t\t\t\tspotLength ++;\n\n\t\t\t} else if ( light.isRectAreaLight ) {\n\n\t\t\t\tconst uniforms = state.rectArea[ rectAreaLength ];\n\n\t\t\t\tuniforms.position.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tuniforms.position.applyMatrix4( viewMatrix );\n\n\t\t\t\t// extract local rotation of light to derive width/height half vectors\n\t\t\t\tmatrix42.identity();\n\t\t\t\tmatrix4.copy( light.matrixWorld );\n\t\t\t\tmatrix4.premultiply( viewMatrix );\n\t\t\t\tmatrix42.extractRotation( matrix4 );\n\n\t\t\t\tuniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );\n\t\t\t\tuniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );\n\n\t\t\t\tuniforms.halfWidth.applyMatrix4( matrix42 );\n\t\t\t\tuniforms.halfHeight.applyMatrix4( matrix42 );\n\n\t\t\t\trectAreaLength ++;\n\n\t\t\t} else if ( light.isPointLight ) {\n\n\t\t\t\tconst uniforms = state.point[ pointLength ];\n\n\t\t\t\tuniforms.position.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tuniforms.position.applyMatrix4( viewMatrix );\n\n\t\t\t\tpointLength ++;\n\n\t\t\t} else if ( light.isHemisphereLight ) {\n\n\t\t\t\tconst uniforms = state.hemi[ hemiLength ];\n\n\t\t\t\tuniforms.direction.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tuniforms.direction.transformDirection( viewMatrix );\n\n\t\t\t\themiLength ++;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\treturn {\n\t\tsetup: setup,\n\t\tsetupView: setupView,\n\t\tstate: state\n\t};\n\n}\n\nfunction WebGLRenderState( extensions, capabilities ) {\n\n\tconst lights = new WebGLLights( extensions, capabilities );\n\n\tconst lightsArray = [];\n\tconst shadowsArray = [];\n\n\tfunction init() {\n\n\t\tlightsArray.length = 0;\n\t\tshadowsArray.length = 0;\n\n\t}\n\n\tfunction pushLight( light ) {\n\n\t\tlightsArray.push( light );\n\n\t}\n\n\tfunction pushShadow( shadowLight ) {\n\n\t\tshadowsArray.push( shadowLight );\n\n\t}\n\n\tfunction setupLights( useLegacyLights ) {\n\n\t\tlights.setup( lightsArray, useLegacyLights );\n\n\t}\n\n\tfunction setupLightsView( camera ) {\n\n\t\tlights.setupView( lightsArray, camera );\n\n\t}\n\n\tconst state = {\n\t\tlightsArray: lightsArray,\n\t\tshadowsArray: shadowsArray,\n\n\t\tlights: lights\n\t};\n\n\treturn {\n\t\tinit: init,\n\t\tstate: state,\n\t\tsetupLights: setupLights,\n\t\tsetupLightsView: setupLightsView,\n\n\t\tpushLight: pushLight,\n\t\tpushShadow: pushShadow\n\t};\n\n}\n\nfunction WebGLRenderStates( extensions, capabilities ) {\n\n\tlet renderStates = new WeakMap();\n\n\tfunction get( scene, renderCallDepth = 0 ) {\n\n\t\tconst renderStateArray = renderStates.get( scene );\n\t\tlet renderState;\n\n\t\tif ( renderStateArray === undefined ) {\n\n\t\t\trenderState = new WebGLRenderState( extensions, capabilities );\n\t\t\trenderStates.set( scene, [ renderState ] );\n\n\t\t} else {\n\n\t\t\tif ( renderCallDepth >= renderStateArray.length ) {\n\n\t\t\t\trenderState = new WebGLRenderState( extensions, capabilities );\n\t\t\t\trenderStateArray.push( renderState );\n\n\t\t\t} else {\n\n\t\t\t\trenderState = renderStateArray[ renderCallDepth ];\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn renderState;\n\n\t}\n\n\tfunction dispose() {\n\n\t\trenderStates = new WeakMap();\n\n\t}\n\n\treturn {\n\t\tget: get,\n\t\tdispose: dispose\n\t};\n\n}\n\nclass MeshDepthMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshDepthMaterial = true;\n\n\t\tthis.type = 'MeshDepthMaterial';\n\n\t\tthis.depthPacking = BasicDepthPacking;\n\n\t\tthis.map = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.depthPacking = source.depthPacking;\n\n\t\tthis.map = source.map;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass MeshDistanceMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshDistanceMaterial = true;\n\n\t\tthis.type = 'MeshDistanceMaterial';\n\n\t\tthis.map = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.map = source.map;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\treturn this;\n\n\t}\n\n}\n\nconst vertex = \"void main() {\\n\\tgl_Position = vec4( position, 1.0 );\\n}\";\n\nconst fragment = \"uniform sampler2D shadow_pass;\\nuniform vec2 resolution;\\nuniform float radius;\\n#include <packing>\\nvoid main() {\\n\\tconst float samples = float( VSM_SAMPLES );\\n\\tfloat mean = 0.0;\\n\\tfloat squared_mean = 0.0;\\n\\tfloat uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 );\\n\\tfloat uvStart = samples <= 1.0 ? 0.0 : - 1.0;\\n\\tfor ( float i = 0.0; i < samples; i ++ ) {\\n\\t\\tfloat uvOffset = uvStart + i * uvStride;\\n\\t\\t#ifdef HORIZONTAL_PASS\\n\\t\\t\\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) );\\n\\t\\t\\tmean += distribution.x;\\n\\t\\t\\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\\n\\t\\t#else\\n\\t\\t\\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) );\\n\\t\\t\\tmean += depth;\\n\\t\\t\\tsquared_mean += depth * depth;\\n\\t\\t#endif\\n\\t}\\n\\tmean = mean / samples;\\n\\tsquared_mean = squared_mean / samples;\\n\\tfloat std_dev = sqrt( squared_mean - mean * mean );\\n\\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\\n}\";\n\nfunction WebGLShadowMap( _renderer, _objects, _capabilities ) {\n\n\tlet _frustum = new Frustum();\n\n\tconst _shadowMapSize = new Vector2(),\n\t\t_viewportSize = new Vector2(),\n\n\t\t_viewport = new Vector4(),\n\n\t\t_depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ),\n\t\t_distanceMaterial = new MeshDistanceMaterial(),\n\n\t\t_materialCache = {},\n\n\t\t_maxTextureSize = _capabilities.maxTextureSize;\n\n\tconst shadowSide = { [ FrontSide ]: BackSide, [ BackSide ]: FrontSide, [ DoubleSide ]: DoubleSide };\n\n\tconst shadowMaterialVertical = new ShaderMaterial( {\n\t\tdefines: {\n\t\t\tVSM_SAMPLES: 8\n\t\t},\n\t\tuniforms: {\n\t\t\tshadow_pass: { value: null },\n\t\t\tresolution: { value: new Vector2() },\n\t\t\tradius: { value: 4.0 }\n\t\t},\n\n\t\tvertexShader: vertex,\n\t\tfragmentShader: fragment\n\n\t} );\n\n\tconst shadowMaterialHorizontal = shadowMaterialVertical.clone();\n\tshadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1;\n\n\tconst fullScreenTri = new BufferGeometry();\n\tfullScreenTri.setAttribute(\n\t\t'position',\n\t\tnew BufferAttribute(\n\t\t\tnew Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ),\n\t\t\t3\n\t\t)\n\t);\n\n\tconst fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical );\n\n\tconst scope = this;\n\n\tthis.enabled = false;\n\n\tthis.autoUpdate = true;\n\tthis.needsUpdate = false;\n\n\tthis.type = PCFShadowMap;\n\tlet _previousType = this.type;\n\n\tthis.render = function ( lights, scene, camera ) {\n\n\t\tif ( scope.enabled === false ) return;\n\t\tif ( scope.autoUpdate === false && scope.needsUpdate === false ) return;\n\n\t\tif ( lights.length === 0 ) return;\n\n\t\tconst currentRenderTarget = _renderer.getRenderTarget();\n\t\tconst activeCubeFace = _renderer.getActiveCubeFace();\n\t\tconst activeMipmapLevel = _renderer.getActiveMipmapLevel();\n\n\t\tconst _state = _renderer.state;\n\n\t\t// Set GL state for depth map.\n\t\t_state.setBlending( NoBlending );\n\t\t_state.buffers.color.setClear( 1, 1, 1, 1 );\n\t\t_state.buffers.depth.setTest( true );\n\t\t_state.setScissorTest( false );\n\n\t\t// check for shadow map type changes\n\n\t\tconst toVSM = ( _previousType !== VSMShadowMap && this.type === VSMShadowMap );\n\t\tconst fromVSM = ( _previousType === VSMShadowMap && this.type !== VSMShadowMap );\n\n\t\t// render depth map\n\n\t\tfor ( let i = 0, il = lights.length; i < il; i ++ ) {\n\n\t\t\tconst light = lights[ i ];\n\t\t\tconst shadow = light.shadow;\n\n\t\t\tif ( shadow === undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' );\n\t\t\t\tcontinue;\n\n\t\t\t}\n\n\t\t\tif ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue;\n\n\t\t\t_shadowMapSize.copy( shadow.mapSize );\n\n\t\t\tconst shadowFrameExtents = shadow.getFrameExtents();\n\n\t\t\t_shadowMapSize.multiply( shadowFrameExtents );\n\n\t\t\t_viewportSize.copy( shadow.mapSize );\n\n\t\t\tif ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) {\n\n\t\t\t\tif ( _shadowMapSize.x > _maxTextureSize ) {\n\n\t\t\t\t\t_viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x );\n\t\t\t\t\t_shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x;\n\t\t\t\t\tshadow.mapSize.x = _viewportSize.x;\n\n\t\t\t\t}\n\n\t\t\t\tif ( _shadowMapSize.y > _maxTextureSize ) {\n\n\t\t\t\t\t_viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y );\n\t\t\t\t\t_shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y;\n\t\t\t\t\tshadow.mapSize.y = _viewportSize.y;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( shadow.map === null || toVSM === true || fromVSM === true ) {\n\n\t\t\t\tconst pars = ( this.type !== VSMShadowMap ) ? { minFilter: NearestFilter, magFilter: NearestFilter } : {};\n\n\t\t\t\tif ( shadow.map !== null ) {\n\n\t\t\t\t\tshadow.map.dispose();\n\n\t\t\t\t}\n\n\t\t\t\tshadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );\n\t\t\t\tshadow.map.texture.name = light.name + '.shadowMap';\n\n\t\t\t\tshadow.camera.updateProjectionMatrix();\n\n\t\t\t}\n\n\t\t\t_renderer.setRenderTarget( shadow.map );\n\t\t\t_renderer.clear();\n\n\t\t\tconst viewportCount = shadow.getViewportCount();\n\n\t\t\tfor ( let vp = 0; vp < viewportCount; vp ++ ) {\n\n\t\t\t\tconst viewport = shadow.getViewport( vp );\n\n\t\t\t\t_viewport.set(\n\t\t\t\t\t_viewportSize.x * viewport.x,\n\t\t\t\t\t_viewportSize.y * viewport.y,\n\t\t\t\t\t_viewportSize.x * viewport.z,\n\t\t\t\t\t_viewportSize.y * viewport.w\n\t\t\t\t);\n\n\t\t\t\t_state.viewport( _viewport );\n\n\t\t\t\tshadow.updateMatrices( light, vp );\n\n\t\t\t\t_frustum = shadow.getFrustum();\n\n\t\t\t\trenderObject( scene, camera, shadow.camera, light, this.type );\n\n\t\t\t}\n\n\t\t\t// do blur pass for VSM\n\n\t\t\tif ( shadow.isPointLightShadow !== true && this.type === VSMShadowMap ) {\n\n\t\t\t\tVSMPass( shadow, camera );\n\n\t\t\t}\n\n\t\t\tshadow.needsUpdate = false;\n\n\t\t}\n\n\t\t_previousType = this.type;\n\n\t\tscope.needsUpdate = false;\n\n\t\t_renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel );\n\n\t};\n\n\tfunction VSMPass( shadow, camera ) {\n\n\t\tconst geometry = _objects.update( fullScreenMesh );\n\n\t\tif ( shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples ) {\n\n\t\t\tshadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples;\n\t\t\tshadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples;\n\n\t\t\tshadowMaterialVertical.needsUpdate = true;\n\t\t\tshadowMaterialHorizontal.needsUpdate = true;\n\n\t\t}\n\n\t\tif ( shadow.mapPass === null ) {\n\n\t\t\tshadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y );\n\n\t\t}\n\n\t\t// vertical pass\n\n\t\tshadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture;\n\t\tshadowMaterialVertical.uniforms.resolution.value = shadow.mapSize;\n\t\tshadowMaterialVertical.uniforms.radius.value = shadow.radius;\n\t\t_renderer.setRenderTarget( shadow.mapPass );\n\t\t_renderer.clear();\n\t\t_renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null );\n\n\t\t// horizontal pass\n\n\t\tshadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture;\n\t\tshadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize;\n\t\tshadowMaterialHorizontal.uniforms.radius.value = shadow.radius;\n\t\t_renderer.setRenderTarget( shadow.map );\n\t\t_renderer.clear();\n\t\t_renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null );\n\n\t}\n\n\tfunction getDepthMaterial( object, material, light, type ) {\n\n\t\tlet result = null;\n\n\t\tconst customMaterial = ( light.isPointLight === true ) ? object.customDistanceMaterial : object.customDepthMaterial;\n\n\t\tif ( customMaterial !== undefined ) {\n\n\t\t\tresult = customMaterial;\n\n\t\t} else {\n\n\t\t\tresult = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial;\n\n\t\t\tif ( ( _renderer.localClippingEnabled && material.clipShadows === true && Array.isArray( material.clippingPlanes ) && material.clippingPlanes.length !== 0 ) ||\n\t\t\t\t( material.displacementMap && material.displacementScale !== 0 ) ||\n\t\t\t\t( material.alphaMap && material.alphaTest > 0 ) ||\n\t\t\t\t( material.map && material.alphaTest > 0 ) ) {\n\n\t\t\t\t// in this case we need a unique material instance reflecting the\n\t\t\t\t// appropriate state\n\n\t\t\t\tconst keyA = result.uuid, keyB = material.uuid;\n\n\t\t\t\tlet materialsForVariant = _materialCache[ keyA ];\n\n\t\t\t\tif ( materialsForVariant === undefined ) {\n\n\t\t\t\t\tmaterialsForVariant = {};\n\t\t\t\t\t_materialCache[ keyA ] = materialsForVariant;\n\n\t\t\t\t}\n\n\t\t\t\tlet cachedMaterial = materialsForVariant[ keyB ];\n\n\t\t\t\tif ( cachedMaterial === undefined ) {\n\n\t\t\t\t\tcachedMaterial = result.clone();\n\t\t\t\t\tmaterialsForVariant[ keyB ] = cachedMaterial;\n\t\t\t\t\tmaterial.addEventListener( 'dispose', onMaterialDispose );\n\n\t\t\t\t}\n\n\t\t\t\tresult = cachedMaterial;\n\n\t\t\t}\n\n\t\t}\n\n\t\tresult.visible = material.visible;\n\t\tresult.wireframe = material.wireframe;\n\n\t\tif ( type === VSMShadowMap ) {\n\n\t\t\tresult.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side;\n\n\t\t} else {\n\n\t\t\tresult.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ];\n\n\t\t}\n\n\t\tresult.alphaMap = material.alphaMap;\n\t\tresult.alphaTest = material.alphaTest;\n\t\tresult.map = material.map;\n\n\t\tresult.clipShadows = material.clipShadows;\n\t\tresult.clippingPlanes = material.clippingPlanes;\n\t\tresult.clipIntersection = material.clipIntersection;\n\n\t\tresult.displacementMap = material.displacementMap;\n\t\tresult.displacementScale = material.displacementScale;\n\t\tresult.displacementBias = material.displacementBias;\n\n\t\tresult.wireframeLinewidth = material.wireframeLinewidth;\n\t\tresult.linewidth = material.linewidth;\n\n\t\tif ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) {\n\n\t\t\tconst materialProperties = _renderer.properties.get( result );\n\t\t\tmaterialProperties.light = light;\n\n\t\t}\n\n\t\treturn result;\n\n\t}\n\n\tfunction renderObject( object, camera, shadowCamera, light, type ) {\n\n\t\tif ( object.visible === false ) return;\n\n\t\tconst visible = object.layers.test( camera.layers );\n\n\t\tif ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) {\n\n\t\t\tif ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) {\n\n\t\t\t\tobject.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );\n\n\t\t\t\tconst geometry = _objects.update( object );\n\t\t\t\tconst material = object.material;\n\n\t\t\t\tif ( Array.isArray( material ) ) {\n\n\t\t\t\t\tconst groups = geometry.groups;\n\n\t\t\t\t\tfor ( let k = 0, kl = groups.length; k < kl; k ++ ) {\n\n\t\t\t\t\t\tconst group = groups[ k ];\n\t\t\t\t\t\tconst groupMaterial = material[ group.materialIndex ];\n\n\t\t\t\t\t\tif ( groupMaterial && groupMaterial.visible ) {\n\n\t\t\t\t\t\t\tconst depthMaterial = getDepthMaterial( object, groupMaterial, light, type );\n\n\t\t\t\t\t\t\tobject.onBeforeShadow( _renderer, object, camera, shadowCamera, geometry, depthMaterial, group );\n\n\t\t\t\t\t\t\t_renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group );\n\n\t\t\t\t\t\t\tobject.onAfterShadow( _renderer, object, camera, shadowCamera, geometry, depthMaterial, group );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( material.visible ) {\n\n\t\t\t\t\tconst depthMaterial = getDepthMaterial( object, material, light, type );\n\n\t\t\t\t\tobject.onBeforeShadow( _renderer, object, camera, shadowCamera, geometry, depthMaterial, null );\n\n\t\t\t\t\t_renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null );\n\n\t\t\t\t\tobject.onAfterShadow( _renderer, object, camera, shadowCamera, geometry, depthMaterial, null );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst children = object.children;\n\n\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\trenderObject( children[ i ], camera, shadowCamera, light, type );\n\n\t\t}\n\n\t}\n\n\tfunction onMaterialDispose( event ) {\n\n\t\tconst material = event.target;\n\n\t\tmaterial.removeEventListener( 'dispose', onMaterialDispose );\n\n\t\t// make sure to remove the unique distance/depth materials used for shadow map rendering\n\n\t\tfor ( const id in _materialCache ) {\n\n\t\t\tconst cache = _materialCache[ id ];\n\n\t\t\tconst uuid = event.target.uuid;\n\n\t\t\tif ( uuid in cache ) {\n\n\t\t\t\tconst shadowMaterial = cache[ uuid ];\n\t\t\t\tshadowMaterial.dispose();\n\t\t\t\tdelete cache[ uuid ];\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n\nfunction WebGLState( gl, extensions, capabilities ) {\n\n\tconst isWebGL2 = capabilities.isWebGL2;\n\n\tfunction ColorBuffer() {\n\n\t\tlet locked = false;\n\n\t\tconst color = new Vector4();\n\t\tlet currentColorMask = null;\n\t\tconst currentColorClear = new Vector4( 0, 0, 0, 0 );\n\n\t\treturn {\n\n\t\t\tsetMask: function ( colorMask ) {\n\n\t\t\t\tif ( currentColorMask !== colorMask && ! locked ) {\n\n\t\t\t\t\tgl.colorMask( colorMask, colorMask, colorMask, colorMask );\n\t\t\t\t\tcurrentColorMask = colorMask;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetLocked: function ( lock ) {\n\n\t\t\t\tlocked = lock;\n\n\t\t\t},\n\n\t\t\tsetClear: function ( r, g, b, a, premultipliedAlpha ) {\n\n\t\t\t\tif ( premultipliedAlpha === true ) {\n\n\t\t\t\t\tr *= a; g *= a; b *= a;\n\n\t\t\t\t}\n\n\t\t\t\tcolor.set( r, g, b, a );\n\n\t\t\t\tif ( currentColorClear.equals( color ) === false ) {\n\n\t\t\t\t\tgl.clearColor( r, g, b, a );\n\t\t\t\t\tcurrentColorClear.copy( color );\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\treset: function () {\n\n\t\t\t\tlocked = false;\n\n\t\t\t\tcurrentColorMask = null;\n\t\t\t\tcurrentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state\n\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\tfunction DepthBuffer() {\n\n\t\tlet locked = false;\n\n\t\tlet currentDepthMask = null;\n\t\tlet currentDepthFunc = null;\n\t\tlet currentDepthClear = null;\n\n\t\treturn {\n\n\t\t\tsetTest: function ( depthTest ) {\n\n\t\t\t\tif ( depthTest ) {\n\n\t\t\t\t\tenable( gl.DEPTH_TEST );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tdisable( gl.DEPTH_TEST );\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetMask: function ( depthMask ) {\n\n\t\t\t\tif ( currentDepthMask !== depthMask && ! locked ) {\n\n\t\t\t\t\tgl.depthMask( depthMask );\n\t\t\t\t\tcurrentDepthMask = depthMask;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetFunc: function ( depthFunc ) {\n\n\t\t\t\tif ( currentDepthFunc !== depthFunc ) {\n\n\t\t\t\t\tswitch ( depthFunc ) {\n\n\t\t\t\t\t\tcase NeverDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.NEVER );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase AlwaysDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.ALWAYS );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase LessDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.LESS );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase LessEqualDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.LEQUAL );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase EqualDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.EQUAL );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase GreaterEqualDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.GEQUAL );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase GreaterDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.GREATER );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase NotEqualDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.NOTEQUAL );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.LEQUAL );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrentDepthFunc = depthFunc;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetLocked: function ( lock ) {\n\n\t\t\t\tlocked = lock;\n\n\t\t\t},\n\n\t\t\tsetClear: function ( depth ) {\n\n\t\t\t\tif ( currentDepthClear !== depth ) {\n\n\t\t\t\t\tgl.clearDepth( depth );\n\t\t\t\t\tcurrentDepthClear = depth;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\treset: function () {\n\n\t\t\t\tlocked = false;\n\n\t\t\t\tcurrentDepthMask = null;\n\t\t\t\tcurrentDepthFunc = null;\n\t\t\t\tcurrentDepthClear = null;\n\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\tfunction StencilBuffer() {\n\n\t\tlet locked = false;\n\n\t\tlet currentStencilMask = null;\n\t\tlet currentStencilFunc = null;\n\t\tlet currentStencilRef = null;\n\t\tlet currentStencilFuncMask = null;\n\t\tlet currentStencilFail = null;\n\t\tlet currentStencilZFail = null;\n\t\tlet currentStencilZPass = null;\n\t\tlet currentStencilClear = null;\n\n\t\treturn {\n\n\t\t\tsetTest: function ( stencilTest ) {\n\n\t\t\t\tif ( ! locked ) {\n\n\t\t\t\t\tif ( stencilTest ) {\n\n\t\t\t\t\t\tenable( gl.STENCIL_TEST );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tdisable( gl.STENCIL_TEST );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetMask: function ( stencilMask ) {\n\n\t\t\t\tif ( currentStencilMask !== stencilMask && ! locked ) {\n\n\t\t\t\t\tgl.stencilMask( stencilMask );\n\t\t\t\t\tcurrentStencilMask = stencilMask;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetFunc: function ( stencilFunc, stencilRef, stencilMask ) {\n\n\t\t\t\tif ( currentStencilFunc !== stencilFunc ||\n\t\t\t\t     currentStencilRef !== stencilRef ||\n\t\t\t\t     currentStencilFuncMask !== stencilMask ) {\n\n\t\t\t\t\tgl.stencilFunc( stencilFunc, stencilRef, stencilMask );\n\n\t\t\t\t\tcurrentStencilFunc = stencilFunc;\n\t\t\t\t\tcurrentStencilRef = stencilRef;\n\t\t\t\t\tcurrentStencilFuncMask = stencilMask;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetOp: function ( stencilFail, stencilZFail, stencilZPass ) {\n\n\t\t\t\tif ( currentStencilFail !== stencilFail ||\n\t\t\t\t     currentStencilZFail !== stencilZFail ||\n\t\t\t\t     currentStencilZPass !== stencilZPass ) {\n\n\t\t\t\t\tgl.stencilOp( stencilFail, stencilZFail, stencilZPass );\n\n\t\t\t\t\tcurrentStencilFail = stencilFail;\n\t\t\t\t\tcurrentStencilZFail = stencilZFail;\n\t\t\t\t\tcurrentStencilZPass = stencilZPass;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetLocked: function ( lock ) {\n\n\t\t\t\tlocked = lock;\n\n\t\t\t},\n\n\t\t\tsetClear: function ( stencil ) {\n\n\t\t\t\tif ( currentStencilClear !== stencil ) {\n\n\t\t\t\t\tgl.clearStencil( stencil );\n\t\t\t\t\tcurrentStencilClear = stencil;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\treset: function () {\n\n\t\t\t\tlocked = false;\n\n\t\t\t\tcurrentStencilMask = null;\n\t\t\t\tcurrentStencilFunc = null;\n\t\t\t\tcurrentStencilRef = null;\n\t\t\t\tcurrentStencilFuncMask = null;\n\t\t\t\tcurrentStencilFail = null;\n\t\t\t\tcurrentStencilZFail = null;\n\t\t\t\tcurrentStencilZPass = null;\n\t\t\t\tcurrentStencilClear = null;\n\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\t//\n\n\tconst colorBuffer = new ColorBuffer();\n\tconst depthBuffer = new DepthBuffer();\n\tconst stencilBuffer = new StencilBuffer();\n\n\tconst uboBindings = new WeakMap();\n\tconst uboProgramMap = new WeakMap();\n\n\tlet enabledCapabilities = {};\n\n\tlet currentBoundFramebuffers = {};\n\tlet currentDrawbuffers = new WeakMap();\n\tlet defaultDrawbuffers = [];\n\n\tlet currentProgram = null;\n\n\tlet currentBlendingEnabled = false;\n\tlet currentBlending = null;\n\tlet currentBlendEquation = null;\n\tlet currentBlendSrc = null;\n\tlet currentBlendDst = null;\n\tlet currentBlendEquationAlpha = null;\n\tlet currentBlendSrcAlpha = null;\n\tlet currentBlendDstAlpha = null;\n\tlet currentBlendColor = new Color( 0, 0, 0 );\n\tlet currentBlendAlpha = 0;\n\tlet currentPremultipledAlpha = false;\n\n\tlet currentFlipSided = null;\n\tlet currentCullFace = null;\n\n\tlet currentLineWidth = null;\n\n\tlet currentPolygonOffsetFactor = null;\n\tlet currentPolygonOffsetUnits = null;\n\n\tconst maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS );\n\n\tlet lineWidthAvailable = false;\n\tlet version = 0;\n\tconst glVersion = gl.getParameter( gl.VERSION );\n\n\tif ( glVersion.indexOf( 'WebGL' ) !== - 1 ) {\n\n\t\tversion = parseFloat( /^WebGL (\\d)/.exec( glVersion )[ 1 ] );\n\t\tlineWidthAvailable = ( version >= 1.0 );\n\n\t} else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) {\n\n\t\tversion = parseFloat( /^OpenGL ES (\\d)/.exec( glVersion )[ 1 ] );\n\t\tlineWidthAvailable = ( version >= 2.0 );\n\n\t}\n\n\tlet currentTextureSlot = null;\n\tlet currentBoundTextures = {};\n\n\tconst scissorParam = gl.getParameter( gl.SCISSOR_BOX );\n\tconst viewportParam = gl.getParameter( gl.VIEWPORT );\n\n\tconst currentScissor = new Vector4().fromArray( scissorParam );\n\tconst currentViewport = new Vector4().fromArray( viewportParam );\n\n\tfunction createTexture( type, target, count, dimensions ) {\n\n\t\tconst data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4.\n\t\tconst texture = gl.createTexture();\n\n\t\tgl.bindTexture( type, texture );\n\t\tgl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST );\n\t\tgl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST );\n\n\t\tfor ( let i = 0; i < count; i ++ ) {\n\n\t\t\tif ( isWebGL2 && ( type === gl.TEXTURE_3D || type === gl.TEXTURE_2D_ARRAY ) ) {\n\n\t\t\t\tgl.texImage3D( target, 0, gl.RGBA, 1, 1, dimensions, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );\n\n\t\t\t} else {\n\n\t\t\t\tgl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn texture;\n\n\t}\n\n\tconst emptyTextures = {};\n\temptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 );\n\temptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 );\n\n\tif ( isWebGL2 ) {\n\n\t\temptyTextures[ gl.TEXTURE_2D_ARRAY ] = createTexture( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_2D_ARRAY, 1, 1 );\n\t\temptyTextures[ gl.TEXTURE_3D ] = createTexture( gl.TEXTURE_3D, gl.TEXTURE_3D, 1, 1 );\n\n\t}\n\n\t// init\n\n\tcolorBuffer.setClear( 0, 0, 0, 1 );\n\tdepthBuffer.setClear( 1 );\n\tstencilBuffer.setClear( 0 );\n\n\tenable( gl.DEPTH_TEST );\n\tdepthBuffer.setFunc( LessEqualDepth );\n\n\tsetFlipSided( false );\n\tsetCullFace( CullFaceBack );\n\tenable( gl.CULL_FACE );\n\n\tsetBlending( NoBlending );\n\n\t//\n\n\tfunction enable( id ) {\n\n\t\tif ( enabledCapabilities[ id ] !== true ) {\n\n\t\t\tgl.enable( id );\n\t\t\tenabledCapabilities[ id ] = true;\n\n\t\t}\n\n\t}\n\n\tfunction disable( id ) {\n\n\t\tif ( enabledCapabilities[ id ] !== false ) {\n\n\t\t\tgl.disable( id );\n\t\t\tenabledCapabilities[ id ] = false;\n\n\t\t}\n\n\t}\n\n\tfunction bindFramebuffer( target, framebuffer ) {\n\n\t\tif ( currentBoundFramebuffers[ target ] !== framebuffer ) {\n\n\t\t\tgl.bindFramebuffer( target, framebuffer );\n\n\t\t\tcurrentBoundFramebuffers[ target ] = framebuffer;\n\n\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\t// gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER\n\n\t\t\t\tif ( target === gl.DRAW_FRAMEBUFFER ) {\n\n\t\t\t\t\tcurrentBoundFramebuffers[ gl.FRAMEBUFFER ] = framebuffer;\n\n\t\t\t\t}\n\n\t\t\t\tif ( target === gl.FRAMEBUFFER ) {\n\n\t\t\t\t\tcurrentBoundFramebuffers[ gl.DRAW_FRAMEBUFFER ] = framebuffer;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\tfunction drawBuffers( renderTarget, framebuffer ) {\n\n\t\tlet drawBuffers = defaultDrawbuffers;\n\n\t\tlet needsUpdate = false;\n\n\t\tif ( renderTarget ) {\n\n\t\t\tdrawBuffers = currentDrawbuffers.get( framebuffer );\n\n\t\t\tif ( drawBuffers === undefined ) {\n\n\t\t\t\tdrawBuffers = [];\n\t\t\t\tcurrentDrawbuffers.set( framebuffer, drawBuffers );\n\n\t\t\t}\n\n\t\t\tif ( renderTarget.isWebGLMultipleRenderTargets ) {\n\n\t\t\t\tconst textures = renderTarget.texture;\n\n\t\t\t\tif ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) {\n\n\t\t\t\t\tfor ( let i = 0, il = textures.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tdrawBuffers[ i ] = gl.COLOR_ATTACHMENT0 + i;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tdrawBuffers.length = textures.length;\n\n\t\t\t\t\tneedsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tif ( drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) {\n\n\t\t\t\t\tdrawBuffers[ 0 ] = gl.COLOR_ATTACHMENT0;\n\n\t\t\t\t\tneedsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tif ( drawBuffers[ 0 ] !== gl.BACK ) {\n\n\t\t\t\tdrawBuffers[ 0 ] = gl.BACK;\n\n\t\t\t\tneedsUpdate = true;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( needsUpdate ) {\n\n\t\t\tif ( capabilities.isWebGL2 ) {\n\n\t\t\t\tgl.drawBuffers( drawBuffers );\n\n\t\t\t} else {\n\n\t\t\t\textensions.get( 'WEBGL_draw_buffers' ).drawBuffersWEBGL( drawBuffers );\n\n\t\t\t}\n\n\t\t}\n\n\n\t}\n\n\tfunction useProgram( program ) {\n\n\t\tif ( currentProgram !== program ) {\n\n\t\t\tgl.useProgram( program );\n\n\t\t\tcurrentProgram = program;\n\n\t\t\treturn true;\n\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\tconst equationToGL = {\n\t\t[ AddEquation ]: gl.FUNC_ADD,\n\t\t[ SubtractEquation ]: gl.FUNC_SUBTRACT,\n\t\t[ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT\n\t};\n\n\tif ( isWebGL2 ) {\n\n\t\tequationToGL[ MinEquation ] = gl.MIN;\n\t\tequationToGL[ MaxEquation ] = gl.MAX;\n\n\t} else {\n\n\t\tconst extension = extensions.get( 'EXT_blend_minmax' );\n\n\t\tif ( extension !== null ) {\n\n\t\t\tequationToGL[ MinEquation ] = extension.MIN_EXT;\n\t\t\tequationToGL[ MaxEquation ] = extension.MAX_EXT;\n\n\t\t}\n\n\t}\n\n\tconst factorToGL = {\n\t\t[ ZeroFactor ]: gl.ZERO,\n\t\t[ OneFactor ]: gl.ONE,\n\t\t[ SrcColorFactor ]: gl.SRC_COLOR,\n\t\t[ SrcAlphaFactor ]: gl.SRC_ALPHA,\n\t\t[ SrcAlphaSaturateFactor ]: gl.SRC_ALPHA_SATURATE,\n\t\t[ DstColorFactor ]: gl.DST_COLOR,\n\t\t[ DstAlphaFactor ]: gl.DST_ALPHA,\n\t\t[ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR,\n\t\t[ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA,\n\t\t[ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR,\n\t\t[ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA,\n\t\t[ ConstantColorFactor ]: gl.CONSTANT_COLOR,\n\t\t[ OneMinusConstantColorFactor ]: gl.ONE_MINUS_CONSTANT_COLOR,\n\t\t[ ConstantAlphaFactor ]: gl.CONSTANT_ALPHA,\n\t\t[ OneMinusConstantAlphaFactor ]: gl.ONE_MINUS_CONSTANT_ALPHA\n\t};\n\n\tfunction setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, blendColor, blendAlpha, premultipliedAlpha ) {\n\n\t\tif ( blending === NoBlending ) {\n\n\t\t\tif ( currentBlendingEnabled === true ) {\n\n\t\t\t\tdisable( gl.BLEND );\n\t\t\t\tcurrentBlendingEnabled = false;\n\n\t\t\t}\n\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( currentBlendingEnabled === false ) {\n\n\t\t\tenable( gl.BLEND );\n\t\t\tcurrentBlendingEnabled = true;\n\n\t\t}\n\n\t\tif ( blending !== CustomBlending ) {\n\n\t\t\tif ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) {\n\n\t\t\t\tif ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) {\n\n\t\t\t\t\tgl.blendEquation( gl.FUNC_ADD );\n\n\t\t\t\t\tcurrentBlendEquation = AddEquation;\n\t\t\t\t\tcurrentBlendEquationAlpha = AddEquation;\n\n\t\t\t\t}\n\n\t\t\t\tif ( premultipliedAlpha ) {\n\n\t\t\t\t\tswitch ( blending ) {\n\n\t\t\t\t\t\tcase NormalBlending:\n\t\t\t\t\t\t\tgl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase AdditiveBlending:\n\t\t\t\t\t\t\tgl.blendFunc( gl.ONE, gl.ONE );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase SubtractiveBlending:\n\t\t\t\t\t\t\tgl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase MultiplyBlending:\n\t\t\t\t\t\t\tgl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tconsole.error( 'THREE.WebGLState: Invalid blending: ', blending );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tswitch ( blending ) {\n\n\t\t\t\t\t\tcase NormalBlending:\n\t\t\t\t\t\t\tgl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase AdditiveBlending:\n\t\t\t\t\t\t\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase SubtractiveBlending:\n\t\t\t\t\t\t\tgl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase MultiplyBlending:\n\t\t\t\t\t\t\tgl.blendFunc( gl.ZERO, gl.SRC_COLOR );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tconsole.error( 'THREE.WebGLState: Invalid blending: ', blending );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tcurrentBlendSrc = null;\n\t\t\t\tcurrentBlendDst = null;\n\t\t\t\tcurrentBlendSrcAlpha = null;\n\t\t\t\tcurrentBlendDstAlpha = null;\n\t\t\t\tcurrentBlendColor.set( 0, 0, 0 );\n\t\t\t\tcurrentBlendAlpha = 0;\n\n\t\t\t\tcurrentBlending = blending;\n\t\t\t\tcurrentPremultipledAlpha = premultipliedAlpha;\n\n\t\t\t}\n\n\t\t\treturn;\n\n\t\t}\n\n\t\t// custom blending\n\n\t\tblendEquationAlpha = blendEquationAlpha || blendEquation;\n\t\tblendSrcAlpha = blendSrcAlpha || blendSrc;\n\t\tblendDstAlpha = blendDstAlpha || blendDst;\n\n\t\tif ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {\n\n\t\t\tgl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] );\n\n\t\t\tcurrentBlendEquation = blendEquation;\n\t\t\tcurrentBlendEquationAlpha = blendEquationAlpha;\n\n\t\t}\n\n\t\tif ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {\n\n\t\t\tgl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] );\n\n\t\t\tcurrentBlendSrc = blendSrc;\n\t\t\tcurrentBlendDst = blendDst;\n\t\t\tcurrentBlendSrcAlpha = blendSrcAlpha;\n\t\t\tcurrentBlendDstAlpha = blendDstAlpha;\n\n\t\t}\n\n\t\tif ( blendColor.equals( currentBlendColor ) === false || blendAlpha !== currentBlendAlpha ) {\n\n\t\t\tgl.blendColor( blendColor.r, blendColor.g, blendColor.b, blendAlpha );\n\n\t\t\tcurrentBlendColor.copy( blendColor );\n\t\t\tcurrentBlendAlpha = blendAlpha;\n\n\t\t}\n\n\t\tcurrentBlending = blending;\n\t\tcurrentPremultipledAlpha = false;\n\n\t}\n\n\tfunction setMaterial( material, frontFaceCW ) {\n\n\t\tmaterial.side === DoubleSide\n\t\t\t? disable( gl.CULL_FACE )\n\t\t\t: enable( gl.CULL_FACE );\n\n\t\tlet flipSided = ( material.side === BackSide );\n\t\tif ( frontFaceCW ) flipSided = ! flipSided;\n\n\t\tsetFlipSided( flipSided );\n\n\t\t( material.blending === NormalBlending && material.transparent === false )\n\t\t\t? setBlending( NoBlending )\n\t\t\t: setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha );\n\n\t\tdepthBuffer.setFunc( material.depthFunc );\n\t\tdepthBuffer.setTest( material.depthTest );\n\t\tdepthBuffer.setMask( material.depthWrite );\n\t\tcolorBuffer.setMask( material.colorWrite );\n\n\t\tconst stencilWrite = material.stencilWrite;\n\t\tstencilBuffer.setTest( stencilWrite );\n\t\tif ( stencilWrite ) {\n\n\t\t\tstencilBuffer.setMask( material.stencilWriteMask );\n\t\t\tstencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask );\n\t\t\tstencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass );\n\n\t\t}\n\n\t\tsetPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );\n\n\t\tmaterial.alphaToCoverage === true\n\t\t\t? enable( gl.SAMPLE_ALPHA_TO_COVERAGE )\n\t\t\t: disable( gl.SAMPLE_ALPHA_TO_COVERAGE );\n\n\t}\n\n\t//\n\n\tfunction setFlipSided( flipSided ) {\n\n\t\tif ( currentFlipSided !== flipSided ) {\n\n\t\t\tif ( flipSided ) {\n\n\t\t\t\tgl.frontFace( gl.CW );\n\n\t\t\t} else {\n\n\t\t\t\tgl.frontFace( gl.CCW );\n\n\t\t\t}\n\n\t\t\tcurrentFlipSided = flipSided;\n\n\t\t}\n\n\t}\n\n\tfunction setCullFace( cullFace ) {\n\n\t\tif ( cullFace !== CullFaceNone ) {\n\n\t\t\tenable( gl.CULL_FACE );\n\n\t\t\tif ( cullFace !== currentCullFace ) {\n\n\t\t\t\tif ( cullFace === CullFaceBack ) {\n\n\t\t\t\t\tgl.cullFace( gl.BACK );\n\n\t\t\t\t} else if ( cullFace === CullFaceFront ) {\n\n\t\t\t\t\tgl.cullFace( gl.FRONT );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tgl.cullFace( gl.FRONT_AND_BACK );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tdisable( gl.CULL_FACE );\n\n\t\t}\n\n\t\tcurrentCullFace = cullFace;\n\n\t}\n\n\tfunction setLineWidth( width ) {\n\n\t\tif ( width !== currentLineWidth ) {\n\n\t\t\tif ( lineWidthAvailable ) gl.lineWidth( width );\n\n\t\t\tcurrentLineWidth = width;\n\n\t\t}\n\n\t}\n\n\tfunction setPolygonOffset( polygonOffset, factor, units ) {\n\n\t\tif ( polygonOffset ) {\n\n\t\t\tenable( gl.POLYGON_OFFSET_FILL );\n\n\t\t\tif ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) {\n\n\t\t\t\tgl.polygonOffset( factor, units );\n\n\t\t\t\tcurrentPolygonOffsetFactor = factor;\n\t\t\t\tcurrentPolygonOffsetUnits = units;\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tdisable( gl.POLYGON_OFFSET_FILL );\n\n\t\t}\n\n\t}\n\n\tfunction setScissorTest( scissorTest ) {\n\n\t\tif ( scissorTest ) {\n\n\t\t\tenable( gl.SCISSOR_TEST );\n\n\t\t} else {\n\n\t\t\tdisable( gl.SCISSOR_TEST );\n\n\t\t}\n\n\t}\n\n\t// texture\n\n\tfunction activeTexture( webglSlot ) {\n\n\t\tif ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;\n\n\t\tif ( currentTextureSlot !== webglSlot ) {\n\n\t\t\tgl.activeTexture( webglSlot );\n\t\t\tcurrentTextureSlot = webglSlot;\n\n\t\t}\n\n\t}\n\n\tfunction bindTexture( webglType, webglTexture, webglSlot ) {\n\n\t\tif ( webglSlot === undefined ) {\n\n\t\t\tif ( currentTextureSlot === null ) {\n\n\t\t\t\twebglSlot = gl.TEXTURE0 + maxTextures - 1;\n\n\t\t\t} else {\n\n\t\t\t\twebglSlot = currentTextureSlot;\n\n\t\t\t}\n\n\t\t}\n\n\t\tlet boundTexture = currentBoundTextures[ webglSlot ];\n\n\t\tif ( boundTexture === undefined ) {\n\n\t\t\tboundTexture = { type: undefined, texture: undefined };\n\t\t\tcurrentBoundTextures[ webglSlot ] = boundTexture;\n\n\t\t}\n\n\t\tif ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {\n\n\t\t\tif ( currentTextureSlot !== webglSlot ) {\n\n\t\t\t\tgl.activeTexture( webglSlot );\n\t\t\t\tcurrentTextureSlot = webglSlot;\n\n\t\t\t}\n\n\t\t\tgl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] );\n\n\t\t\tboundTexture.type = webglType;\n\t\t\tboundTexture.texture = webglTexture;\n\n\t\t}\n\n\t}\n\n\tfunction unbindTexture() {\n\n\t\tconst boundTexture = currentBoundTextures[ currentTextureSlot ];\n\n\t\tif ( boundTexture !== undefined && boundTexture.type !== undefined ) {\n\n\t\t\tgl.bindTexture( boundTexture.type, null );\n\n\t\t\tboundTexture.type = undefined;\n\t\t\tboundTexture.texture = undefined;\n\n\t\t}\n\n\t}\n\n\tfunction compressedTexImage2D() {\n\n\t\ttry {\n\n\t\t\tgl.compressedTexImage2D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction compressedTexImage3D() {\n\n\t\ttry {\n\n\t\t\tgl.compressedTexImage3D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction texSubImage2D() {\n\n\t\ttry {\n\n\t\t\tgl.texSubImage2D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction texSubImage3D() {\n\n\t\ttry {\n\n\t\t\tgl.texSubImage3D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction compressedTexSubImage2D() {\n\n\t\ttry {\n\n\t\t\tgl.compressedTexSubImage2D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction compressedTexSubImage3D() {\n\n\t\ttry {\n\n\t\t\tgl.compressedTexSubImage3D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction texStorage2D() {\n\n\t\ttry {\n\n\t\t\tgl.texStorage2D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction texStorage3D() {\n\n\t\ttry {\n\n\t\t\tgl.texStorage3D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction texImage2D() {\n\n\t\ttry {\n\n\t\t\tgl.texImage2D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction texImage3D() {\n\n\t\ttry {\n\n\t\t\tgl.texImage3D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\t//\n\n\tfunction scissor( scissor ) {\n\n\t\tif ( currentScissor.equals( scissor ) === false ) {\n\n\t\t\tgl.scissor( scissor.x, scissor.y, scissor.z, scissor.w );\n\t\t\tcurrentScissor.copy( scissor );\n\n\t\t}\n\n\t}\n\n\tfunction viewport( viewport ) {\n\n\t\tif ( currentViewport.equals( viewport ) === false ) {\n\n\t\t\tgl.viewport( viewport.x, viewport.y, viewport.z, viewport.w );\n\t\t\tcurrentViewport.copy( viewport );\n\n\t\t}\n\n\t}\n\n\tfunction updateUBOMapping( uniformsGroup, program ) {\n\n\t\tlet mapping = uboProgramMap.get( program );\n\n\t\tif ( mapping === undefined ) {\n\n\t\t\tmapping = new WeakMap();\n\n\t\t\tuboProgramMap.set( program, mapping );\n\n\t\t}\n\n\t\tlet blockIndex = mapping.get( uniformsGroup );\n\n\t\tif ( blockIndex === undefined ) {\n\n\t\t\tblockIndex = gl.getUniformBlockIndex( program, uniformsGroup.name );\n\n\t\t\tmapping.set( uniformsGroup, blockIndex );\n\n\t\t}\n\n\t}\n\n\tfunction uniformBlockBinding( uniformsGroup, program ) {\n\n\t\tconst mapping = uboProgramMap.get( program );\n\t\tconst blockIndex = mapping.get( uniformsGroup );\n\n\t\tif ( uboBindings.get( program ) !== blockIndex ) {\n\n\t\t\t// bind shader specific block index to global block point\n\t\t\tgl.uniformBlockBinding( program, blockIndex, uniformsGroup.__bindingPointIndex );\n\n\t\t\tuboBindings.set( program, blockIndex );\n\n\t\t}\n\n\t}\n\n\t//\n\n\tfunction reset() {\n\n\t\t// reset state\n\n\t\tgl.disable( gl.BLEND );\n\t\tgl.disable( gl.CULL_FACE );\n\t\tgl.disable( gl.DEPTH_TEST );\n\t\tgl.disable( gl.POLYGON_OFFSET_FILL );\n\t\tgl.disable( gl.SCISSOR_TEST );\n\t\tgl.disable( gl.STENCIL_TEST );\n\t\tgl.disable( gl.SAMPLE_ALPHA_TO_COVERAGE );\n\n\t\tgl.blendEquation( gl.FUNC_ADD );\n\t\tgl.blendFunc( gl.ONE, gl.ZERO );\n\t\tgl.blendFuncSeparate( gl.ONE, gl.ZERO, gl.ONE, gl.ZERO );\n\t\tgl.blendColor( 0, 0, 0, 0 );\n\n\t\tgl.colorMask( true, true, true, true );\n\t\tgl.clearColor( 0, 0, 0, 0 );\n\n\t\tgl.depthMask( true );\n\t\tgl.depthFunc( gl.LESS );\n\t\tgl.clearDepth( 1 );\n\n\t\tgl.stencilMask( 0xffffffff );\n\t\tgl.stencilFunc( gl.ALWAYS, 0, 0xffffffff );\n\t\tgl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP );\n\t\tgl.clearStencil( 0 );\n\n\t\tgl.cullFace( gl.BACK );\n\t\tgl.frontFace( gl.CCW );\n\n\t\tgl.polygonOffset( 0, 0 );\n\n\t\tgl.activeTexture( gl.TEXTURE0 );\n\n\t\tgl.bindFramebuffer( gl.FRAMEBUFFER, null );\n\n\t\tif ( isWebGL2 === true ) {\n\n\t\t\tgl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null );\n\t\t\tgl.bindFramebuffer( gl.READ_FRAMEBUFFER, null );\n\n\t\t}\n\n\t\tgl.useProgram( null );\n\n\t\tgl.lineWidth( 1 );\n\n\t\tgl.scissor( 0, 0, gl.canvas.width, gl.canvas.height );\n\t\tgl.viewport( 0, 0, gl.canvas.width, gl.canvas.height );\n\n\t\t// reset internals\n\n\t\tenabledCapabilities = {};\n\n\t\tcurrentTextureSlot = null;\n\t\tcurrentBoundTextures = {};\n\n\t\tcurrentBoundFramebuffers = {};\n\t\tcurrentDrawbuffers = new WeakMap();\n\t\tdefaultDrawbuffers = [];\n\n\t\tcurrentProgram = null;\n\n\t\tcurrentBlendingEnabled = false;\n\t\tcurrentBlending = null;\n\t\tcurrentBlendEquation = null;\n\t\tcurrentBlendSrc = null;\n\t\tcurrentBlendDst = null;\n\t\tcurrentBlendEquationAlpha = null;\n\t\tcurrentBlendSrcAlpha = null;\n\t\tcurrentBlendDstAlpha = null;\n\t\tcurrentBlendColor = new Color( 0, 0, 0 );\n\t\tcurrentBlendAlpha = 0;\n\t\tcurrentPremultipledAlpha = false;\n\n\t\tcurrentFlipSided = null;\n\t\tcurrentCullFace = null;\n\n\t\tcurrentLineWidth = null;\n\n\t\tcurrentPolygonOffsetFactor = null;\n\t\tcurrentPolygonOffsetUnits = null;\n\n\t\tcurrentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height );\n\t\tcurrentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height );\n\n\t\tcolorBuffer.reset();\n\t\tdepthBuffer.reset();\n\t\tstencilBuffer.reset();\n\n\t}\n\n\treturn {\n\n\t\tbuffers: {\n\t\t\tcolor: colorBuffer,\n\t\t\tdepth: depthBuffer,\n\t\t\tstencil: stencilBuffer\n\t\t},\n\n\t\tenable: enable,\n\t\tdisable: disable,\n\n\t\tbindFramebuffer: bindFramebuffer,\n\t\tdrawBuffers: drawBuffers,\n\n\t\tuseProgram: useProgram,\n\n\t\tsetBlending: setBlending,\n\t\tsetMaterial: setMaterial,\n\n\t\tsetFlipSided: setFlipSided,\n\t\tsetCullFace: setCullFace,\n\n\t\tsetLineWidth: setLineWidth,\n\t\tsetPolygonOffset: setPolygonOffset,\n\n\t\tsetScissorTest: setScissorTest,\n\n\t\tactiveTexture: activeTexture,\n\t\tbindTexture: bindTexture,\n\t\tunbindTexture: unbindTexture,\n\t\tcompressedTexImage2D: compressedTexImage2D,\n\t\tcompressedTexImage3D: compressedTexImage3D,\n\t\ttexImage2D: texImage2D,\n\t\ttexImage3D: texImage3D,\n\n\t\tupdateUBOMapping: updateUBOMapping,\n\t\tuniformBlockBinding: uniformBlockBinding,\n\n\t\ttexStorage2D: texStorage2D,\n\t\ttexStorage3D: texStorage3D,\n\t\ttexSubImage2D: texSubImage2D,\n\t\ttexSubImage3D: texSubImage3D,\n\t\tcompressedTexSubImage2D: compressedTexSubImage2D,\n\t\tcompressedTexSubImage3D: compressedTexSubImage3D,\n\n\t\tscissor: scissor,\n\t\tviewport: viewport,\n\n\t\treset: reset\n\n\t};\n\n}\n\nfunction WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) {\n\n\tconst isWebGL2 = capabilities.isWebGL2;\n\tconst multisampledRTTExt = extensions.has( 'WEBGL_multisampled_render_to_texture' ) ? extensions.get( 'WEBGL_multisampled_render_to_texture' ) : null;\n\tconst supportsInvalidateFramebuffer = typeof navigator === 'undefined' ? false : /OculusBrowser/g.test( navigator.userAgent );\n\n\tconst _videoTextures = new WeakMap();\n\tlet _canvas;\n\n\tconst _sources = new WeakMap(); // maps WebglTexture objects to instances of Source\n\n\t// cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas,\n\t// also OffscreenCanvas.getContext(\"webgl\"), but not OffscreenCanvas.getContext(\"2d\")!\n\t// Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d).\n\n\tlet useOffscreenCanvas = false;\n\n\ttry {\n\n\t\tuseOffscreenCanvas = typeof OffscreenCanvas !== 'undefined'\n\t\t\t// eslint-disable-next-line compat/compat\n\t\t\t&& ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null;\n\n\t} catch ( err ) {\n\n\t\t// Ignore any errors\n\n\t}\n\n\tfunction createCanvas( width, height ) {\n\n\t\t// Use OffscreenCanvas when available. Specially needed in web workers\n\n\t\treturn useOffscreenCanvas ?\n\t\t\t// eslint-disable-next-line compat/compat\n\t\t\tnew OffscreenCanvas( width, height ) : createElementNS( 'canvas' );\n\n\t}\n\n\tfunction resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) {\n\n\t\tlet scale = 1;\n\n\t\t// handle case if texture exceeds max size\n\n\t\tif ( image.width > maxSize || image.height > maxSize ) {\n\n\t\t\tscale = maxSize / Math.max( image.width, image.height );\n\n\t\t}\n\n\t\t// only perform resize if necessary\n\n\t\tif ( scale < 1 || needsPowerOfTwo === true ) {\n\n\t\t\t// only perform resize for certain image types\n\n\t\t\tif ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||\n\t\t\t\t( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||\n\t\t\t\t( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {\n\n\t\t\t\tconst floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor;\n\n\t\t\t\tconst width = floor( scale * image.width );\n\t\t\t\tconst height = floor( scale * image.height );\n\n\t\t\t\tif ( _canvas === undefined ) _canvas = createCanvas( width, height );\n\n\t\t\t\t// cube textures can't reuse the same canvas\n\n\t\t\t\tconst canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas;\n\n\t\t\t\tcanvas.width = width;\n\t\t\t\tcanvas.height = height;\n\n\t\t\t\tconst context = canvas.getContext( '2d' );\n\t\t\t\tcontext.drawImage( image, 0, 0, width, height );\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' );\n\n\t\t\t\treturn canvas;\n\n\t\t\t} else {\n\n\t\t\t\tif ( 'data' in image ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' );\n\n\t\t\t\t}\n\n\t\t\t\treturn image;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn image;\n\n\t}\n\n\tfunction isPowerOfTwo$1( image ) {\n\n\t\treturn isPowerOfTwo( image.width ) && isPowerOfTwo( image.height );\n\n\t}\n\n\tfunction textureNeedsPowerOfTwo( texture ) {\n\n\t\tif ( isWebGL2 ) return false;\n\n\t\treturn ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) ||\n\t\t\t( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter );\n\n\t}\n\n\tfunction textureNeedsGenerateMipmaps( texture, supportsMips ) {\n\n\t\treturn texture.generateMipmaps && supportsMips &&\n\t\t\ttexture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter;\n\n\t}\n\n\tfunction generateMipmap( target ) {\n\n\t\t_gl.generateMipmap( target );\n\n\t}\n\n\tfunction getInternalFormat( internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false ) {\n\n\t\tif ( isWebGL2 === false ) return glFormat;\n\n\t\tif ( internalFormatName !== null ) {\n\n\t\t\tif ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ];\n\n\t\t\tconsole.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \\'' + internalFormatName + '\\'' );\n\n\t\t}\n\n\t\tlet internalFormat = glFormat;\n\n\t\tif ( glFormat === _gl.RED ) {\n\n\t\t\tif ( glType === _gl.FLOAT ) internalFormat = _gl.R32F;\n\t\t\tif ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.R16F;\n\t\t\tif ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.R8;\n\n\t\t}\n\n\t\tif ( glFormat === _gl.RED_INTEGER ) {\n\n\t\t\tif ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.R8UI;\n\t\t\tif ( glType === _gl.UNSIGNED_SHORT ) internalFormat = _gl.R16UI;\n\t\t\tif ( glType === _gl.UNSIGNED_INT ) internalFormat = _gl.R32UI;\n\t\t\tif ( glType === _gl.BYTE ) internalFormat = _gl.R8I;\n\t\t\tif ( glType === _gl.SHORT ) internalFormat = _gl.R16I;\n\t\t\tif ( glType === _gl.INT ) internalFormat = _gl.R32I;\n\n\t\t}\n\n\t\tif ( glFormat === _gl.RG ) {\n\n\t\t\tif ( glType === _gl.FLOAT ) internalFormat = _gl.RG32F;\n\t\t\tif ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RG16F;\n\t\t\tif ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.RG8;\n\n\t\t}\n\n\t\tif ( glFormat === _gl.RGBA ) {\n\n\t\t\tconst transfer = forceLinearTransfer ? LinearTransfer : ColorManagement.getTransfer( colorSpace );\n\n\t\t\tif ( glType === _gl.FLOAT ) internalFormat = _gl.RGBA32F;\n\t\t\tif ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RGBA16F;\n\t\t\tif ( glType === _gl.UNSIGNED_BYTE ) internalFormat = ( transfer === SRGBTransfer ) ? _gl.SRGB8_ALPHA8 : _gl.RGBA8;\n\t\t\tif ( glType === _gl.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = _gl.RGBA4;\n\t\t\tif ( glType === _gl.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = _gl.RGB5_A1;\n\n\t\t}\n\n\t\tif ( internalFormat === _gl.R16F || internalFormat === _gl.R32F ||\n\t\t\tinternalFormat === _gl.RG16F || internalFormat === _gl.RG32F ||\n\t\t\tinternalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F ) {\n\n\t\t\textensions.get( 'EXT_color_buffer_float' );\n\n\t\t}\n\n\t\treturn internalFormat;\n\n\t}\n\n\tfunction getMipLevels( texture, image, supportsMips ) {\n\n\t\tif ( textureNeedsGenerateMipmaps( texture, supportsMips ) === true || ( texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) ) {\n\n\t\t\treturn Math.log2( Math.max( image.width, image.height ) ) + 1;\n\n\t\t} else if ( texture.mipmaps !== undefined && texture.mipmaps.length > 0 ) {\n\n\t\t\t// user-defined mipmaps\n\n\t\t\treturn texture.mipmaps.length;\n\n\t\t} else if ( texture.isCompressedTexture && Array.isArray( texture.image ) ) {\n\n\t\t\treturn image.mipmaps.length;\n\n\t\t} else {\n\n\t\t\t// texture without mipmaps (only base level)\n\n\t\t\treturn 1;\n\n\t\t}\n\n\t}\n\n\t// Fallback filters for non-power-of-2 textures\n\n\tfunction filterFallback( f ) {\n\n\t\tif ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) {\n\n\t\t\treturn _gl.NEAREST;\n\n\t\t}\n\n\t\treturn _gl.LINEAR;\n\n\t}\n\n\t//\n\n\tfunction onTextureDispose( event ) {\n\n\t\tconst texture = event.target;\n\n\t\ttexture.removeEventListener( 'dispose', onTextureDispose );\n\n\t\tdeallocateTexture( texture );\n\n\t\tif ( texture.isVideoTexture ) {\n\n\t\t\t_videoTextures.delete( texture );\n\n\t\t}\n\n\t}\n\n\tfunction onRenderTargetDispose( event ) {\n\n\t\tconst renderTarget = event.target;\n\n\t\trenderTarget.removeEventListener( 'dispose', onRenderTargetDispose );\n\n\t\tdeallocateRenderTarget( renderTarget );\n\n\t}\n\n\t//\n\n\tfunction deallocateTexture( texture ) {\n\n\t\tconst textureProperties = properties.get( texture );\n\n\t\tif ( textureProperties.__webglInit === undefined ) return;\n\n\t\t// check if it's necessary to remove the WebGLTexture object\n\n\t\tconst source = texture.source;\n\t\tconst webglTextures = _sources.get( source );\n\n\t\tif ( webglTextures ) {\n\n\t\t\tconst webglTexture = webglTextures[ textureProperties.__cacheKey ];\n\t\t\twebglTexture.usedTimes --;\n\n\t\t\t// the WebGLTexture object is not used anymore, remove it\n\n\t\t\tif ( webglTexture.usedTimes === 0 ) {\n\n\t\t\t\tdeleteTexture( texture );\n\n\t\t\t}\n\n\t\t\t// remove the weak map entry if no WebGLTexture uses the source anymore\n\n\t\t\tif ( Object.keys( webglTextures ).length === 0 ) {\n\n\t\t\t\t_sources.delete( source );\n\n\t\t\t}\n\n\t\t}\n\n\t\tproperties.remove( texture );\n\n\t}\n\n\tfunction deleteTexture( texture ) {\n\n\t\tconst textureProperties = properties.get( texture );\n\t\t_gl.deleteTexture( textureProperties.__webglTexture );\n\n\t\tconst source = texture.source;\n\t\tconst webglTextures = _sources.get( source );\n\t\tdelete webglTextures[ textureProperties.__cacheKey ];\n\n\t\tinfo.memory.textures --;\n\n\t}\n\n\tfunction deallocateRenderTarget( renderTarget ) {\n\n\t\tconst texture = renderTarget.texture;\n\n\t\tconst renderTargetProperties = properties.get( renderTarget );\n\t\tconst textureProperties = properties.get( texture );\n\n\t\tif ( textureProperties.__webglTexture !== undefined ) {\n\n\t\t\t_gl.deleteTexture( textureProperties.__webglTexture );\n\n\t\t\tinfo.memory.textures --;\n\n\t\t}\n\n\t\tif ( renderTarget.depthTexture ) {\n\n\t\t\trenderTarget.depthTexture.dispose();\n\n\t\t}\n\n\t\tif ( renderTarget.isWebGLCubeRenderTarget ) {\n\n\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\tif ( Array.isArray( renderTargetProperties.__webglFramebuffer[ i ] ) ) {\n\n\t\t\t\t\tfor ( let level = 0; level < renderTargetProperties.__webglFramebuffer[ i ].length; level ++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ][ level ] );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] );\n\n\t\t\t\t}\n\n\t\t\t\tif ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tif ( Array.isArray( renderTargetProperties.__webglFramebuffer ) ) {\n\n\t\t\t\tfor ( let level = 0; level < renderTargetProperties.__webglFramebuffer.length; level ++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ level ] );\n\n\t\t\t} else {\n\n\t\t\t\t_gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer );\n\n\t\t\t}\n\n\t\t\tif ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer );\n\t\t\tif ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer );\n\n\t\t\tif ( renderTargetProperties.__webglColorRenderbuffer ) {\n\n\t\t\t\tfor ( let i = 0; i < renderTargetProperties.__webglColorRenderbuffer.length; i ++ ) {\n\n\t\t\t\t\tif ( renderTargetProperties.__webglColorRenderbuffer[ i ] ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer[ i ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer );\n\n\t\t}\n\n\t\tif ( renderTarget.isWebGLMultipleRenderTargets ) {\n\n\t\t\tfor ( let i = 0, il = texture.length; i < il; i ++ ) {\n\n\t\t\t\tconst attachmentProperties = properties.get( texture[ i ] );\n\n\t\t\t\tif ( attachmentProperties.__webglTexture ) {\n\n\t\t\t\t\t_gl.deleteTexture( attachmentProperties.__webglTexture );\n\n\t\t\t\t\tinfo.memory.textures --;\n\n\t\t\t\t}\n\n\t\t\t\tproperties.remove( texture[ i ] );\n\n\t\t\t}\n\n\t\t}\n\n\t\tproperties.remove( texture );\n\t\tproperties.remove( renderTarget );\n\n\t}\n\n\t//\n\n\tlet textureUnits = 0;\n\n\tfunction resetTextureUnits() {\n\n\t\ttextureUnits = 0;\n\n\t}\n\n\tfunction allocateTextureUnit() {\n\n\t\tconst textureUnit = textureUnits;\n\n\t\tif ( textureUnit >= capabilities.maxTextures ) {\n\n\t\t\tconsole.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures );\n\n\t\t}\n\n\t\ttextureUnits += 1;\n\n\t\treturn textureUnit;\n\n\t}\n\n\tfunction getTextureCacheKey( texture ) {\n\n\t\tconst array = [];\n\n\t\tarray.push( texture.wrapS );\n\t\tarray.push( texture.wrapT );\n\t\tarray.push( texture.wrapR || 0 );\n\t\tarray.push( texture.magFilter );\n\t\tarray.push( texture.minFilter );\n\t\tarray.push( texture.anisotropy );\n\t\tarray.push( texture.internalFormat );\n\t\tarray.push( texture.format );\n\t\tarray.push( texture.type );\n\t\tarray.push( texture.generateMipmaps );\n\t\tarray.push( texture.premultiplyAlpha );\n\t\tarray.push( texture.flipY );\n\t\tarray.push( texture.unpackAlignment );\n\t\tarray.push( texture.colorSpace );\n\n\t\treturn array.join();\n\n\t}\n\n\t//\n\n\tfunction setTexture2D( texture, slot ) {\n\n\t\tconst textureProperties = properties.get( texture );\n\n\t\tif ( texture.isVideoTexture ) updateVideoTexture( texture );\n\n\t\tif ( texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n\t\t\tconst image = texture.image;\n\n\t\t\tif ( image === null ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture marked for update but no image data found.' );\n\n\t\t\t} else if ( image.complete === false ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' );\n\n\t\t\t} else {\n\n\t\t\t\tuploadTexture( textureProperties, texture, slot );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t}\n\n\t\tstate.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot );\n\n\t}\n\n\tfunction setTexture2DArray( texture, slot ) {\n\n\t\tconst textureProperties = properties.get( texture );\n\n\t\tif ( texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n\t\t\tuploadTexture( textureProperties, texture, slot );\n\t\t\treturn;\n\n\t\t}\n\n\t\tstate.bindTexture( _gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture, _gl.TEXTURE0 + slot );\n\n\t}\n\n\tfunction setTexture3D( texture, slot ) {\n\n\t\tconst textureProperties = properties.get( texture );\n\n\t\tif ( texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n\t\t\tuploadTexture( textureProperties, texture, slot );\n\t\t\treturn;\n\n\t\t}\n\n\t\tstate.bindTexture( _gl.TEXTURE_3D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot );\n\n\t}\n\n\tfunction setTextureCube( texture, slot ) {\n\n\t\tconst textureProperties = properties.get( texture );\n\n\t\tif ( texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n\t\t\tuploadCubeTexture( textureProperties, texture, slot );\n\t\t\treturn;\n\n\t\t}\n\n\t\tstate.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot );\n\n\t}\n\n\tconst wrappingToGL = {\n\t\t[ RepeatWrapping ]: _gl.REPEAT,\n\t\t[ ClampToEdgeWrapping ]: _gl.CLAMP_TO_EDGE,\n\t\t[ MirroredRepeatWrapping ]: _gl.MIRRORED_REPEAT\n\t};\n\n\tconst filterToGL = {\n\t\t[ NearestFilter ]: _gl.NEAREST,\n\t\t[ NearestMipmapNearestFilter ]: _gl.NEAREST_MIPMAP_NEAREST,\n\t\t[ NearestMipmapLinearFilter ]: _gl.NEAREST_MIPMAP_LINEAR,\n\n\t\t[ LinearFilter ]: _gl.LINEAR,\n\t\t[ LinearMipmapNearestFilter ]: _gl.LINEAR_MIPMAP_NEAREST,\n\t\t[ LinearMipmapLinearFilter ]: _gl.LINEAR_MIPMAP_LINEAR\n\t};\n\n\tconst compareToGL = {\n\t\t[ NeverCompare ]: _gl.NEVER,\n\t\t[ AlwaysCompare ]: _gl.ALWAYS,\n\t\t[ LessCompare ]: _gl.LESS,\n\t\t[ LessEqualCompare ]: _gl.LEQUAL,\n\t\t[ EqualCompare ]: _gl.EQUAL,\n\t\t[ GreaterEqualCompare ]: _gl.GEQUAL,\n\t\t[ GreaterCompare ]: _gl.GREATER,\n\t\t[ NotEqualCompare ]: _gl.NOTEQUAL\n\t};\n\n\tfunction setTextureParameters( textureType, texture, supportsMips ) {\n\n\t\tif ( supportsMips ) {\n\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ] );\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ] );\n\n\t\t\tif ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) {\n\n\t\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ] );\n\n\t\t\t}\n\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[ texture.magFilter ] );\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[ texture.minFilter ] );\n\n\t\t} else {\n\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );\n\n\t\t\tif ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) {\n\n\t\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE );\n\n\t\t\t}\n\n\t\t\tif ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' );\n\n\t\t\t}\n\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );\n\n\t\t\tif ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( texture.compareFunction ) {\n\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_COMPARE_MODE, _gl.COMPARE_REF_TO_TEXTURE );\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_COMPARE_FUNC, compareToGL[ texture.compareFunction ] );\n\n\t\t}\n\n\t\tif ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) {\n\n\t\t\tconst extension = extensions.get( 'EXT_texture_filter_anisotropic' );\n\n\t\t\tif ( texture.magFilter === NearestFilter ) return;\n\t\t\tif ( texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter ) return;\n\t\t\tif ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2\n\t\t\tif ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( 'OES_texture_half_float_linear' ) === false ) ) return; // verify extension for WebGL 1 only\n\n\t\t\tif ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) {\n\n\t\t\t\t_gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) );\n\t\t\t\tproperties.get( texture ).__currentAnisotropy = texture.anisotropy;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tfunction initTexture( textureProperties, texture ) {\n\n\t\tlet forceUpload = false;\n\n\t\tif ( textureProperties.__webglInit === undefined ) {\n\n\t\t\ttextureProperties.__webglInit = true;\n\n\t\t\ttexture.addEventListener( 'dispose', onTextureDispose );\n\n\t\t}\n\n\t\t// create Source <-> WebGLTextures mapping if necessary\n\n\t\tconst source = texture.source;\n\t\tlet webglTextures = _sources.get( source );\n\n\t\tif ( webglTextures === undefined ) {\n\n\t\t\twebglTextures = {};\n\t\t\t_sources.set( source, webglTextures );\n\n\t\t}\n\n\t\t// check if there is already a WebGLTexture object for the given texture parameters\n\n\t\tconst textureCacheKey = getTextureCacheKey( texture );\n\n\t\tif ( textureCacheKey !== textureProperties.__cacheKey ) {\n\n\t\t\t// if not, create a new instance of WebGLTexture\n\n\t\t\tif ( webglTextures[ textureCacheKey ] === undefined ) {\n\n\t\t\t\t// create new entry\n\n\t\t\t\twebglTextures[ textureCacheKey ] = {\n\t\t\t\t\ttexture: _gl.createTexture(),\n\t\t\t\t\tusedTimes: 0\n\t\t\t\t};\n\n\t\t\t\tinfo.memory.textures ++;\n\n\t\t\t\t// when a new instance of WebGLTexture was created, a texture upload is required\n\t\t\t\t// even if the image contents are identical\n\n\t\t\t\tforceUpload = true;\n\n\t\t\t}\n\n\t\t\twebglTextures[ textureCacheKey ].usedTimes ++;\n\n\t\t\t// every time the texture cache key changes, it's necessary to check if an instance of\n\t\t\t// WebGLTexture can be deleted in order to avoid a memory leak.\n\n\t\t\tconst webglTexture = webglTextures[ textureProperties.__cacheKey ];\n\n\t\t\tif ( webglTexture !== undefined ) {\n\n\t\t\t\twebglTextures[ textureProperties.__cacheKey ].usedTimes --;\n\n\t\t\t\tif ( webglTexture.usedTimes === 0 ) {\n\n\t\t\t\t\tdeleteTexture( texture );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// store references to cache key and WebGLTexture object\n\n\t\t\ttextureProperties.__cacheKey = textureCacheKey;\n\t\t\ttextureProperties.__webglTexture = webglTextures[ textureCacheKey ].texture;\n\n\t\t}\n\n\t\treturn forceUpload;\n\n\t}\n\n\tfunction uploadTexture( textureProperties, texture, slot ) {\n\n\t\tlet textureType = _gl.TEXTURE_2D;\n\n\t\tif ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) textureType = _gl.TEXTURE_2D_ARRAY;\n\t\tif ( texture.isData3DTexture ) textureType = _gl.TEXTURE_3D;\n\n\t\tconst forceUpload = initTexture( textureProperties, texture );\n\t\tconst source = texture.source;\n\n\t\tstate.bindTexture( textureType, textureProperties.__webglTexture, _gl.TEXTURE0 + slot );\n\n\t\tconst sourceProperties = properties.get( source );\n\n\t\tif ( source.version !== sourceProperties.__version || forceUpload === true ) {\n\n\t\t\tstate.activeTexture( _gl.TEXTURE0 + slot );\n\n\t\t\tconst workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace );\n\t\t\tconst texturePrimaries = texture.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries( texture.colorSpace );\n\t\t\tconst unpackConversion = texture.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? _gl.NONE : _gl.BROWSER_DEFAULT_WEBGL;\n\n\t\t\t_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, unpackConversion );\n\n\t\t\tconst needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo$1( texture.image ) === false;\n\t\t\tlet image = resizeImage( texture.image, needsPowerOfTwo, false, capabilities.maxTextureSize );\n\t\t\timage = verifyColorSpace( texture, image );\n\n\t\t\tconst supportsMips = isPowerOfTwo$1( image ) || isWebGL2,\n\t\t\t\tglFormat = utils.convert( texture.format, texture.colorSpace );\n\n\t\t\tlet glType = utils.convert( texture.type ),\n\t\t\t\tglInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture );\n\n\t\t\tsetTextureParameters( textureType, texture, supportsMips );\n\n\t\t\tlet mipmap;\n\t\t\tconst mipmaps = texture.mipmaps;\n\n\t\t\tconst useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true && glInternalFormat !== RGB_ETC1_Format );\n\t\t\tconst allocateMemory = ( sourceProperties.__version === undefined ) || ( forceUpload === true );\n\t\t\tconst levels = getMipLevels( texture, image, supportsMips );\n\n\t\t\tif ( texture.isDepthTexture ) {\n\n\t\t\t\t// populate depth texture with dummy data\n\n\t\t\t\tglInternalFormat = _gl.DEPTH_COMPONENT;\n\n\t\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\t\tif ( texture.type === FloatType ) {\n\n\t\t\t\t\t\tglInternalFormat = _gl.DEPTH_COMPONENT32F;\n\n\t\t\t\t\t} else if ( texture.type === UnsignedIntType ) {\n\n\t\t\t\t\t\tglInternalFormat = _gl.DEPTH_COMPONENT24;\n\n\t\t\t\t\t} else if ( texture.type === UnsignedInt248Type ) {\n\n\t\t\t\t\t\tglInternalFormat = _gl.DEPTH24_STENCIL8;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tglInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( texture.type === FloatType ) {\n\n\t\t\t\t\t\tconsole.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// validation checks for WebGL 1\n\n\t\t\t\tif ( texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT ) {\n\n\t\t\t\t\t// The error INVALID_OPERATION is generated by texImage2D if format and internalformat are\n\t\t\t\t\t// DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT\n\t\t\t\t\t// (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n\t\t\t\t\tif ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' );\n\n\t\t\t\t\t\ttexture.type = UnsignedIntType;\n\t\t\t\t\t\tglType = utils.convert( texture.type );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT ) {\n\n\t\t\t\t\t// Depth stencil textures need the DEPTH_STENCIL internal format\n\t\t\t\t\t// (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n\t\t\t\t\tglInternalFormat = _gl.DEPTH_STENCIL;\n\n\t\t\t\t\t// The error INVALID_OPERATION is generated by texImage2D if format and internalformat are\n\t\t\t\t\t// DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL.\n\t\t\t\t\t// (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n\t\t\t\t\tif ( texture.type !== UnsignedInt248Type ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' );\n\n\t\t\t\t\t\ttexture.type = UnsignedInt248Type;\n\t\t\t\t\t\tglType = utils.convert( texture.type );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t//\n\n\t\t\t\tif ( allocateMemory ) {\n\n\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else if ( texture.isDataTexture ) {\n\n\t\t\t\t// use manually created mipmaps if available\n\t\t\t\t// if there are no manual mipmaps\n\t\t\t\t// set 0 level mipmap and then use GL to generate other mipmap levels\n\n\t\t\t\tif ( mipmaps.length > 0 && supportsMips ) {\n\n\t\t\t\t\tif ( useTexStorage && allocateMemory ) {\n\n\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfor ( let i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tmipmap = mipmaps[ i ];\n\n\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttexture.generateMipmaps = false;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\tif ( allocateMemory ) {\n\n\t\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else if ( texture.isCompressedTexture ) {\n\n\t\t\t\tif ( texture.isCompressedArrayTexture ) {\n\n\t\t\t\t\tif ( useTexStorage && allocateMemory ) {\n\n\t\t\t\t\t\tstate.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height, image.depth );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfor ( let i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tmipmap = mipmaps[ i ];\n\n\t\t\t\t\t\tif ( texture.format !== RGBAFormat ) {\n\n\t\t\t\t\t\t\tif ( glFormat !== null ) {\n\n\t\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\t\tstate.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0 );\n\n\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\tstate.compressedTexImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, mipmap.data, 0, 0 );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\tstate.texSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.texImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( useTexStorage && allocateMemory ) {\n\n\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfor ( let i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tmipmap = mipmaps[ i ];\n\n\t\t\t\t\t\tif ( texture.format !== RGBAFormat ) {\n\n\t\t\t\t\t\t\tif ( glFormat !== null ) {\n\n\t\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\t\tstate.compressedTexSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data );\n\n\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\tstate.compressedTexImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else if ( texture.isDataArrayTexture ) {\n\n\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\tif ( allocateMemory ) {\n\n\t\t\t\t\t\tstate.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tstate.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tstate.texImage3D( _gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data );\n\n\t\t\t\t}\n\n\t\t\t} else if ( texture.isData3DTexture ) {\n\n\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\tif ( allocateMemory ) {\n\n\t\t\t\t\t\tstate.texStorage3D( _gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tstate.texSubImage3D( _gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tstate.texImage3D( _gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data );\n\n\t\t\t\t}\n\n\t\t\t} else if ( texture.isFramebufferTexture ) {\n\n\t\t\t\tif ( allocateMemory ) {\n\n\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tlet width = image.width, height = image.height;\n\n\t\t\t\t\t\tfor ( let i = 0; i < levels; i ++ ) {\n\n\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, width, height, 0, glFormat, glType, null );\n\n\t\t\t\t\t\t\twidth >>= 1;\n\t\t\t\t\t\t\theight >>= 1;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// regular Texture (image, video, canvas)\n\n\t\t\t\t// use manually created mipmaps if available\n\t\t\t\t// if there are no manual mipmaps\n\t\t\t\t// set 0 level mipmap and then use GL to generate other mipmap levels\n\n\t\t\t\tif ( mipmaps.length > 0 && supportsMips ) {\n\n\t\t\t\t\tif ( useTexStorage && allocateMemory ) {\n\n\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfor ( let i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tmipmap = mipmaps[ i ];\n\n\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttexture.generateMipmaps = false;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\tif ( allocateMemory ) {\n\n\t\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {\n\n\t\t\t\tgenerateMipmap( textureType );\n\n\t\t\t}\n\n\t\t\tsourceProperties.__version = source.version;\n\n\t\t\tif ( texture.onUpdate ) texture.onUpdate( texture );\n\n\t\t}\n\n\t\ttextureProperties.__version = texture.version;\n\n\t}\n\n\tfunction uploadCubeTexture( textureProperties, texture, slot ) {\n\n\t\tif ( texture.image.length !== 6 ) return;\n\n\t\tconst forceUpload = initTexture( textureProperties, texture );\n\t\tconst source = texture.source;\n\n\t\tstate.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot );\n\n\t\tconst sourceProperties = properties.get( source );\n\n\t\tif ( source.version !== sourceProperties.__version || forceUpload === true ) {\n\n\t\t\tstate.activeTexture( _gl.TEXTURE0 + slot );\n\n\t\t\tconst workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace );\n\t\t\tconst texturePrimaries = texture.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries( texture.colorSpace );\n\t\t\tconst unpackConversion = texture.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? _gl.NONE : _gl.BROWSER_DEFAULT_WEBGL;\n\n\t\t\t_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, unpackConversion );\n\n\t\t\tconst isCompressed = ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture );\n\t\t\tconst isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture );\n\n\t\t\tconst cubeImage = [];\n\n\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\tif ( ! isCompressed && ! isDataTexture ) {\n\n\t\t\t\t\tcubeImage[ i ] = resizeImage( texture.image[ i ], false, true, capabilities.maxCubemapSize );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tcubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];\n\n\t\t\t\t}\n\n\t\t\t\tcubeImage[ i ] = verifyColorSpace( texture, cubeImage[ i ] );\n\n\t\t\t}\n\n\t\t\tconst image = cubeImage[ 0 ],\n\t\t\t\tsupportsMips = isPowerOfTwo$1( image ) || isWebGL2,\n\t\t\t\tglFormat = utils.convert( texture.format, texture.colorSpace ),\n\t\t\t\tglType = utils.convert( texture.type ),\n\t\t\t\tglInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace );\n\n\t\t\tconst useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true );\n\t\t\tconst allocateMemory = ( sourceProperties.__version === undefined ) || ( forceUpload === true );\n\t\t\tlet levels = getMipLevels( texture, image, supportsMips );\n\n\t\t\tsetTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips );\n\n\t\t\tlet mipmaps;\n\n\t\t\tif ( isCompressed ) {\n\n\t\t\t\tif ( useTexStorage && allocateMemory ) {\n\n\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height );\n\n\t\t\t\t}\n\n\t\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\t\tmipmaps = cubeImage[ i ].mipmaps;\n\n\t\t\t\t\tfor ( let j = 0; j < mipmaps.length; j ++ ) {\n\n\t\t\t\t\t\tconst mipmap = mipmaps[ j ];\n\n\t\t\t\t\t\tif ( texture.format !== RGBAFormat ) {\n\n\t\t\t\t\t\t\tif ( glFormat !== null ) {\n\n\t\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\t\tstate.compressedTexSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data );\n\n\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\tstate.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tmipmaps = texture.mipmaps;\n\n\t\t\t\tif ( useTexStorage && allocateMemory ) {\n\n\t\t\t\t\t// TODO: Uniformly handle mipmap definitions\n\t\t\t\t\t// Normal textures and compressed cube textures define base level + mips with their mipmap array\n\t\t\t\t\t// Uncompressed cube textures use their mipmap array only for mips (no base level)\n\n\t\t\t\t\tif ( mipmaps.length > 0 ) levels ++;\n\n\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[ 0 ].width, cubeImage[ 0 ].height );\n\n\t\t\t\t}\n\n\t\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\t\tif ( isDataTexture ) {\n\n\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[ i ].width, cubeImage[ i ].height, glFormat, glType, cubeImage[ i ].data );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor ( let j = 0; j < mipmaps.length; j ++ ) {\n\n\t\t\t\t\t\t\tconst mipmap = mipmaps[ j ];\n\t\t\t\t\t\t\tconst mipmapImage = mipmap.image[ i ].image;\n\n\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[ i ] );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor ( let j = 0; j < mipmaps.length; j ++ ) {\n\n\t\t\t\t\t\t\tconst mipmap = mipmaps[ j ];\n\n\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[ i ] );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {\n\n\t\t\t\t// We assume images for cube map have the same size.\n\t\t\t\tgenerateMipmap( _gl.TEXTURE_CUBE_MAP );\n\n\t\t\t}\n\n\t\t\tsourceProperties.__version = source.version;\n\n\t\t\tif ( texture.onUpdate ) texture.onUpdate( texture );\n\n\t\t}\n\n\t\ttextureProperties.__version = texture.version;\n\n\t}\n\n\t// Render targets\n\n\t// Setup storage for target texture and bind it to correct framebuffer\n\tfunction setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget, level ) {\n\n\t\tconst glFormat = utils.convert( texture.format, texture.colorSpace );\n\t\tconst glType = utils.convert( texture.type );\n\t\tconst glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace );\n\t\tconst renderTargetProperties = properties.get( renderTarget );\n\n\t\tif ( ! renderTargetProperties.__hasExternalTextures ) {\n\n\t\t\tconst width = Math.max( 1, renderTarget.width >> level );\n\t\t\tconst height = Math.max( 1, renderTarget.height >> level );\n\n\t\t\tif ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) {\n\n\t\t\t\tstate.texImage3D( textureTarget, level, glInternalFormat, width, height, renderTarget.depth, 0, glFormat, glType, null );\n\n\t\t\t} else {\n\n\t\t\t\tstate.texImage2D( textureTarget, level, glInternalFormat, width, height, 0, glFormat, glType, null );\n\n\t\t\t}\n\n\t\t}\n\n\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n\t\tif ( useMultisampledRTT( renderTarget ) ) {\n\n\t\t\tmultisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) );\n\n\t\t} else if ( textureTarget === _gl.TEXTURE_2D || ( textureTarget >= _gl.TEXTURE_CUBE_MAP_POSITIVE_X && textureTarget <= _gl.TEXTURE_CUBE_MAP_NEGATIVE_Z ) ) { // see #24753\n\n\t\t\t_gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, level );\n\n\t\t}\n\n\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\n\t}\n\n\n\t// Setup storage for internal depth/stencil buffers and bind to correct framebuffer\n\tfunction setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) {\n\n\t\t_gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );\n\n\t\tif ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {\n\n\t\t\tlet glInternalFormat = ( isWebGL2 === true ) ? _gl.DEPTH_COMPONENT24 : _gl.DEPTH_COMPONENT16;\n\n\t\t\tif ( isMultisample || useMultisampledRTT( renderTarget ) ) {\n\n\t\t\t\tconst depthTexture = renderTarget.depthTexture;\n\n\t\t\t\tif ( depthTexture && depthTexture.isDepthTexture ) {\n\n\t\t\t\t\tif ( depthTexture.type === FloatType ) {\n\n\t\t\t\t\t\tglInternalFormat = _gl.DEPTH_COMPONENT32F;\n\n\t\t\t\t\t} else if ( depthTexture.type === UnsignedIntType ) {\n\n\t\t\t\t\t\tglInternalFormat = _gl.DEPTH_COMPONENT24;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tconst samples = getRenderTargetSamples( renderTarget );\n\n\t\t\t\tif ( useMultisampledRTT( renderTarget ) ) {\n\n\t\t\t\t\tmultisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t_gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t}\n\n\t\t\t_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );\n\n\t\t} else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {\n\n\t\t\tconst samples = getRenderTargetSamples( renderTarget );\n\n\t\t\tif ( isMultisample && useMultisampledRTT( renderTarget ) === false ) {\n\n\t\t\t\t_gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height );\n\n\t\t\t} else if ( useMultisampledRTT( renderTarget ) ) {\n\n\t\t\t\tmultisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height );\n\n\t\t\t} else {\n\n\t\t\t\t_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );\n\n\t\t\t}\n\n\n\t\t\t_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );\n\n\t\t} else {\n\n\t\t\tconst textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ];\n\n\t\t\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\t\t\tconst texture = textures[ i ];\n\n\t\t\t\tconst glFormat = utils.convert( texture.format, texture.colorSpace );\n\t\t\t\tconst glType = utils.convert( texture.type );\n\t\t\t\tconst glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace );\n\t\t\t\tconst samples = getRenderTargetSamples( renderTarget );\n\n\t\t\t\tif ( isMultisample && useMultisampledRTT( renderTarget ) === false ) {\n\n\t\t\t\t\t_gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t\t} else if ( useMultisampledRTT( renderTarget ) ) {\n\n\t\t\t\t\tmultisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t_gl.bindRenderbuffer( _gl.RENDERBUFFER, null );\n\n\t}\n\n\t// Setup resources for a Depth Texture for a FBO (needs an extension)\n\tfunction setupDepthTexture( framebuffer, renderTarget ) {\n\n\t\tconst isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget );\n\t\tif ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' );\n\n\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n\t\tif ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) {\n\n\t\t\tthrow new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' );\n\n\t\t}\n\n\t\t// upload an empty depth texture with framebuffer size\n\t\tif ( ! properties.get( renderTarget.depthTexture ).__webglTexture ||\n\t\t\t\trenderTarget.depthTexture.image.width !== renderTarget.width ||\n\t\t\t\trenderTarget.depthTexture.image.height !== renderTarget.height ) {\n\n\t\t\trenderTarget.depthTexture.image.width = renderTarget.width;\n\t\t\trenderTarget.depthTexture.image.height = renderTarget.height;\n\t\t\trenderTarget.depthTexture.needsUpdate = true;\n\n\t\t}\n\n\t\tsetTexture2D( renderTarget.depthTexture, 0 );\n\n\t\tconst webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture;\n\t\tconst samples = getRenderTargetSamples( renderTarget );\n\n\t\tif ( renderTarget.depthTexture.format === DepthFormat ) {\n\n\t\t\tif ( useMultisampledRTT( renderTarget ) ) {\n\n\t\t\t\tmultisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples );\n\n\t\t\t} else {\n\n\t\t\t\t_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );\n\n\t\t\t}\n\n\t\t} else if ( renderTarget.depthTexture.format === DepthStencilFormat ) {\n\n\t\t\tif ( useMultisampledRTT( renderTarget ) ) {\n\n\t\t\t\tmultisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples );\n\n\t\t\t} else {\n\n\t\t\t\t_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'Unknown depthTexture format' );\n\n\t\t}\n\n\t}\n\n\t// Setup GL resources for a non-texture depth buffer\n\tfunction setupDepthRenderbuffer( renderTarget ) {\n\n\t\tconst renderTargetProperties = properties.get( renderTarget );\n\t\tconst isCube = ( renderTarget.isWebGLCubeRenderTarget === true );\n\n\t\tif ( renderTarget.depthTexture && ! renderTargetProperties.__autoAllocateDepthBuffer ) {\n\n\t\t\tif ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' );\n\n\t\t\tsetupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget );\n\n\t\t} else {\n\n\t\t\tif ( isCube ) {\n\n\t\t\t\trenderTargetProperties.__webglDepthbuffer = [];\n\n\t\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] );\n\t\t\t\t\trenderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer();\n\t\t\t\t\tsetupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );\n\t\t\t\trenderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer();\n\t\t\t\tsetupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false );\n\n\t\t\t}\n\n\t\t}\n\n\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\n\t}\n\n\t// rebind framebuffer with external textures\n\tfunction rebindTextures( renderTarget, colorTexture, depthTexture ) {\n\n\t\tconst renderTargetProperties = properties.get( renderTarget );\n\n\t\tif ( colorTexture !== undefined ) {\n\n\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, 0 );\n\n\t\t}\n\n\t\tif ( depthTexture !== undefined ) {\n\n\t\t\tsetupDepthRenderbuffer( renderTarget );\n\n\t\t}\n\n\t}\n\n\t// Set up GL resources for the render target\n\tfunction setupRenderTarget( renderTarget ) {\n\n\t\tconst texture = renderTarget.texture;\n\n\t\tconst renderTargetProperties = properties.get( renderTarget );\n\t\tconst textureProperties = properties.get( texture );\n\n\t\trenderTarget.addEventListener( 'dispose', onRenderTargetDispose );\n\n\t\tif ( renderTarget.isWebGLMultipleRenderTargets !== true ) {\n\n\t\t\tif ( textureProperties.__webglTexture === undefined ) {\n\n\t\t\t\ttextureProperties.__webglTexture = _gl.createTexture();\n\n\t\t\t}\n\n\t\t\ttextureProperties.__version = texture.version;\n\t\t\tinfo.memory.textures ++;\n\n\t\t}\n\n\t\tconst isCube = ( renderTarget.isWebGLCubeRenderTarget === true );\n\t\tconst isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true );\n\t\tconst supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2;\n\n\t\t// Setup framebuffer\n\n\t\tif ( isCube ) {\n\n\t\t\trenderTargetProperties.__webglFramebuffer = [];\n\n\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\tif ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) {\n\n\t\t\t\t\trenderTargetProperties.__webglFramebuffer[ i ] = [];\n\n\t\t\t\t\tfor ( let level = 0; level < texture.mipmaps.length; level ++ ) {\n\n\t\t\t\t\t\trenderTargetProperties.__webglFramebuffer[ i ][ level ] = _gl.createFramebuffer();\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\trenderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tif ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) {\n\n\t\t\t\trenderTargetProperties.__webglFramebuffer = [];\n\n\t\t\t\tfor ( let level = 0; level < texture.mipmaps.length; level ++ ) {\n\n\t\t\t\t\trenderTargetProperties.__webglFramebuffer[ level ] = _gl.createFramebuffer();\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\trenderTargetProperties.__webglFramebuffer = _gl.createFramebuffer();\n\n\t\t\t}\n\n\t\t\tif ( isMultipleRenderTargets ) {\n\n\t\t\t\tif ( capabilities.drawBuffers ) {\n\n\t\t\t\t\tconst textures = renderTarget.texture;\n\n\t\t\t\t\tfor ( let i = 0, il = textures.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tconst attachmentProperties = properties.get( textures[ i ] );\n\n\t\t\t\t\t\tif ( attachmentProperties.__webglTexture === undefined ) {\n\n\t\t\t\t\t\t\tattachmentProperties.__webglTexture = _gl.createTexture();\n\n\t\t\t\t\t\t\tinfo.memory.textures ++;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension.' );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) {\n\n\t\t\t\tconst textures = isMultipleRenderTargets ? texture : [ texture ];\n\n\t\t\t\trenderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer();\n\t\t\t\trenderTargetProperties.__webglColorRenderbuffer = [];\n\n\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer );\n\n\t\t\t\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\t\t\t\tconst texture = textures[ i ];\n\t\t\t\t\trenderTargetProperties.__webglColorRenderbuffer[ i ] = _gl.createRenderbuffer();\n\n\t\t\t\t\t_gl.bindRenderbuffer( _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] );\n\n\t\t\t\t\tconst glFormat = utils.convert( texture.format, texture.colorSpace );\n\t\t\t\t\tconst glType = utils.convert( texture.type );\n\t\t\t\t\tconst glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, renderTarget.isXRRenderTarget === true );\n\t\t\t\t\tconst samples = getRenderTargetSamples( renderTarget );\n\t\t\t\t\t_gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t\t\t_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] );\n\n\t\t\t\t}\n\n\t\t\t\t_gl.bindRenderbuffer( _gl.RENDERBUFFER, null );\n\n\t\t\t\tif ( renderTarget.depthBuffer ) {\n\n\t\t\t\t\trenderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer();\n\t\t\t\t\tsetupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true );\n\n\t\t\t\t}\n\n\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Setup color buffer\n\n\t\tif ( isCube ) {\n\n\t\t\tstate.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );\n\t\t\tsetTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips );\n\n\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\tif ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) {\n\n\t\t\t\t\tfor ( let level = 0; level < texture.mipmaps.length; level ++ ) {\n\n\t\t\t\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ][ level ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {\n\n\t\t\t\tgenerateMipmap( _gl.TEXTURE_CUBE_MAP );\n\n\t\t\t}\n\n\t\t\tstate.unbindTexture();\n\n\t\t} else if ( isMultipleRenderTargets ) {\n\n\t\t\tconst textures = renderTarget.texture;\n\n\t\t\tfor ( let i = 0, il = textures.length; i < il; i ++ ) {\n\n\t\t\t\tconst attachment = textures[ i ];\n\t\t\t\tconst attachmentProperties = properties.get( attachment );\n\n\t\t\t\tstate.bindTexture( _gl.TEXTURE_2D, attachmentProperties.__webglTexture );\n\t\t\t\tsetTextureParameters( _gl.TEXTURE_2D, attachment, supportsMips );\n\t\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, 0 );\n\n\t\t\t\tif ( textureNeedsGenerateMipmaps( attachment, supportsMips ) ) {\n\n\t\t\t\t\tgenerateMipmap( _gl.TEXTURE_2D );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tstate.unbindTexture();\n\n\t\t} else {\n\n\t\t\tlet glTextureType = _gl.TEXTURE_2D;\n\n\t\t\tif ( renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) {\n\n\t\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\t\tglTextureType = renderTarget.isWebGL3DRenderTarget ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconsole.error( 'THREE.WebGLTextures: THREE.Data3DTexture and THREE.DataArrayTexture only supported with WebGL2.' );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tstate.bindTexture( glTextureType, textureProperties.__webglTexture );\n\t\t\tsetTextureParameters( glTextureType, texture, supportsMips );\n\n\t\t\tif ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) {\n\n\t\t\t\tfor ( let level = 0; level < texture.mipmaps.length; level ++ ) {\n\n\t\t\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ level ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, level );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, 0 );\n\n\t\t\t}\n\n\t\t\tif ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {\n\n\t\t\t\tgenerateMipmap( glTextureType );\n\n\t\t\t}\n\n\t\t\tstate.unbindTexture();\n\n\t\t}\n\n\t\t// Setup depth and stencil buffers\n\n\t\tif ( renderTarget.depthBuffer ) {\n\n\t\t\tsetupDepthRenderbuffer( renderTarget );\n\n\t\t}\n\n\t}\n\n\tfunction updateRenderTargetMipmap( renderTarget ) {\n\n\t\tconst supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2;\n\n\t\tconst textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ];\n\n\t\tfor ( let i = 0, il = textures.length; i < il; i ++ ) {\n\n\t\t\tconst texture = textures[ i ];\n\n\t\t\tif ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {\n\n\t\t\t\tconst target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D;\n\t\t\t\tconst webglTexture = properties.get( texture ).__webglTexture;\n\n\t\t\t\tstate.bindTexture( target, webglTexture );\n\t\t\t\tgenerateMipmap( target );\n\t\t\t\tstate.unbindTexture();\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tfunction updateMultisampleRenderTarget( renderTarget ) {\n\n\t\tif ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) {\n\n\t\t\tconst textures = renderTarget.isWebGLMultipleRenderTargets ? renderTarget.texture : [ renderTarget.texture ];\n\t\t\tconst width = renderTarget.width;\n\t\t\tconst height = renderTarget.height;\n\t\t\tlet mask = _gl.COLOR_BUFFER_BIT;\n\t\t\tconst invalidationArray = [];\n\t\t\tconst depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT;\n\t\t\tconst renderTargetProperties = properties.get( renderTarget );\n\t\t\tconst isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true );\n\n\t\t\t// If MRT we need to remove FBO attachments\n\t\t\tif ( isMultipleRenderTargets ) {\n\n\t\t\t\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer );\n\t\t\t\t\t_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, null );\n\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );\n\t\t\t\t\t_gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, null, 0 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tstate.bindFramebuffer( _gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer );\n\t\t\tstate.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );\n\n\t\t\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\t\t\tinvalidationArray.push( _gl.COLOR_ATTACHMENT0 + i );\n\n\t\t\t\tif ( renderTarget.depthBuffer ) {\n\n\t\t\t\t\tinvalidationArray.push( depthStyle );\n\n\t\t\t\t}\n\n\t\t\t\tconst ignoreDepthValues = ( renderTargetProperties.__ignoreDepthValues !== undefined ) ? renderTargetProperties.__ignoreDepthValues : false;\n\n\t\t\t\tif ( ignoreDepthValues === false ) {\n\n\t\t\t\t\tif ( renderTarget.depthBuffer ) mask |= _gl.DEPTH_BUFFER_BIT;\n\t\t\t\t\tif ( renderTarget.stencilBuffer ) mask |= _gl.STENCIL_BUFFER_BIT;\n\n\t\t\t\t}\n\n\t\t\t\tif ( isMultipleRenderTargets ) {\n\n\t\t\t\t\t_gl.framebufferRenderbuffer( _gl.READ_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] );\n\n\t\t\t\t}\n\n\t\t\t\tif ( ignoreDepthValues === true ) {\n\n\t\t\t\t\t_gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, [ depthStyle ] );\n\t\t\t\t\t_gl.invalidateFramebuffer( _gl.DRAW_FRAMEBUFFER, [ depthStyle ] );\n\n\t\t\t\t}\n\n\t\t\t\tif ( isMultipleRenderTargets ) {\n\n\t\t\t\t\tconst webglTexture = properties.get( textures[ i ] ).__webglTexture;\n\t\t\t\t\t_gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, webglTexture, 0 );\n\n\t\t\t\t}\n\n\t\t\t\t_gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST );\n\n\t\t\t\tif ( supportsInvalidateFramebuffer ) {\n\n\t\t\t\t\t_gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, invalidationArray );\n\n\t\t\t\t}\n\n\n\t\t\t}\n\n\t\t\tstate.bindFramebuffer( _gl.READ_FRAMEBUFFER, null );\n\t\t\tstate.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, null );\n\n\t\t\t// If MRT since pre-blit we removed the FBO we need to reconstruct the attachments\n\t\t\tif ( isMultipleRenderTargets ) {\n\n\t\t\t\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer );\n\t\t\t\t\t_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] );\n\n\t\t\t\t\tconst webglTexture = properties.get( textures[ i ] ).__webglTexture;\n\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );\n\t\t\t\t\t_gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, webglTexture, 0 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tstate.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer );\n\n\t\t}\n\n\t}\n\n\tfunction getRenderTargetSamples( renderTarget ) {\n\n\t\treturn Math.min( capabilities.maxSamples, renderTarget.samples );\n\n\t}\n\n\tfunction useMultisampledRTT( renderTarget ) {\n\n\t\tconst renderTargetProperties = properties.get( renderTarget );\n\n\t\treturn isWebGL2 && renderTarget.samples > 0 && extensions.has( 'WEBGL_multisampled_render_to_texture' ) === true && renderTargetProperties.__useRenderToTexture !== false;\n\n\t}\n\n\tfunction updateVideoTexture( texture ) {\n\n\t\tconst frame = info.render.frame;\n\n\t\t// Check the last frame we updated the VideoTexture\n\n\t\tif ( _videoTextures.get( texture ) !== frame ) {\n\n\t\t\t_videoTextures.set( texture, frame );\n\t\t\ttexture.update();\n\n\t\t}\n\n\t}\n\n\tfunction verifyColorSpace( texture, image ) {\n\n\t\tconst colorSpace = texture.colorSpace;\n\t\tconst format = texture.format;\n\t\tconst type = texture.type;\n\n\t\tif ( texture.isCompressedTexture === true || texture.isVideoTexture === true || texture.format === _SRGBAFormat ) return image;\n\n\t\tif ( colorSpace !== LinearSRGBColorSpace && colorSpace !== NoColorSpace ) {\n\n\t\t\t// sRGB\n\n\t\t\tif ( ColorManagement.getTransfer( colorSpace ) === SRGBTransfer ) {\n\n\t\t\t\tif ( isWebGL2 === false ) {\n\n\t\t\t\t\t// in WebGL 1, try to use EXT_sRGB extension and unsized formats\n\n\t\t\t\t\tif ( extensions.has( 'EXT_sRGB' ) === true && format === RGBAFormat ) {\n\n\t\t\t\t\t\ttexture.format = _SRGBAFormat;\n\n\t\t\t\t\t\t// it's not possible to generate mips in WebGL 1 with this extension\n\n\t\t\t\t\t\ttexture.minFilter = LinearFilter;\n\t\t\t\t\t\ttexture.generateMipmaps = false;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// slow fallback (CPU decode)\n\n\t\t\t\t\t\timage = ImageUtils.sRGBToLinear( image );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format\n\n\t\t\t\t\tif ( format !== RGBAFormat || type !== UnsignedByteType ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.' );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tconsole.error( 'THREE.WebGLTextures: Unsupported texture color space:', colorSpace );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn image;\n\n\t}\n\n\t//\n\n\tthis.allocateTextureUnit = allocateTextureUnit;\n\tthis.resetTextureUnits = resetTextureUnits;\n\n\tthis.setTexture2D = setTexture2D;\n\tthis.setTexture2DArray = setTexture2DArray;\n\tthis.setTexture3D = setTexture3D;\n\tthis.setTextureCube = setTextureCube;\n\tthis.rebindTextures = rebindTextures;\n\tthis.setupRenderTarget = setupRenderTarget;\n\tthis.updateRenderTargetMipmap = updateRenderTargetMipmap;\n\tthis.updateMultisampleRenderTarget = updateMultisampleRenderTarget;\n\tthis.setupDepthRenderbuffer = setupDepthRenderbuffer;\n\tthis.setupFrameBufferTexture = setupFrameBufferTexture;\n\tthis.useMultisampledRTT = useMultisampledRTT;\n\n}\n\nfunction WebGLUtils( gl, extensions, capabilities ) {\n\n\tconst isWebGL2 = capabilities.isWebGL2;\n\n\tfunction convert( p, colorSpace = NoColorSpace ) {\n\n\t\tlet extension;\n\n\t\tconst transfer = ColorManagement.getTransfer( colorSpace );\n\n\t\tif ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE;\n\t\tif ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4;\n\t\tif ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1;\n\n\t\tif ( p === ByteType ) return gl.BYTE;\n\t\tif ( p === ShortType ) return gl.SHORT;\n\t\tif ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT;\n\t\tif ( p === IntType ) return gl.INT;\n\t\tif ( p === UnsignedIntType ) return gl.UNSIGNED_INT;\n\t\tif ( p === FloatType ) return gl.FLOAT;\n\n\t\tif ( p === HalfFloatType ) {\n\n\t\t\tif ( isWebGL2 ) return gl.HALF_FLOAT;\n\n\t\t\textension = extensions.get( 'OES_texture_half_float' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\treturn extension.HALF_FLOAT_OES;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( p === AlphaFormat ) return gl.ALPHA;\n\t\tif ( p === RGBAFormat ) return gl.RGBA;\n\t\tif ( p === LuminanceFormat ) return gl.LUMINANCE;\n\t\tif ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA;\n\t\tif ( p === DepthFormat ) return gl.DEPTH_COMPONENT;\n\t\tif ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL;\n\n\t\t// WebGL 1 sRGB fallback\n\n\t\tif ( p === _SRGBAFormat ) {\n\n\t\t\textension = extensions.get( 'EXT_sRGB' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\treturn extension.SRGB_ALPHA_EXT;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// WebGL2 formats.\n\n\t\tif ( p === RedFormat ) return gl.RED;\n\t\tif ( p === RedIntegerFormat ) return gl.RED_INTEGER;\n\t\tif ( p === RGFormat ) return gl.RG;\n\t\tif ( p === RGIntegerFormat ) return gl.RG_INTEGER;\n\t\tif ( p === RGBAIntegerFormat ) return gl.RGBA_INTEGER;\n\n\t\t// S3TC\n\n\t\tif ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) {\n\n\t\t\tif ( transfer === SRGBTransfer ) {\n\n\t\t\t\textension = extensions.get( 'WEBGL_compressed_texture_s3tc_srgb' );\n\n\t\t\t\tif ( extension !== null ) {\n\n\t\t\t\t\tif ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;\n\n\t\t\t\t} else {\n\n\t\t\t\t\treturn null;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\textension = extensions.get( 'WEBGL_compressed_texture_s3tc' );\n\n\t\t\t\tif ( extension !== null ) {\n\n\t\t\t\t\tif ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;\n\n\t\t\t\t} else {\n\n\t\t\t\t\treturn null;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// PVRTC\n\n\t\tif ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) {\n\n\t\t\textension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\tif ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;\n\t\t\t\tif ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;\n\t\t\t\tif ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;\n\t\t\t\tif ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// ETC1\n\n\t\tif ( p === RGB_ETC1_Format ) {\n\n\t\t\textension = extensions.get( 'WEBGL_compressed_texture_etc1' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\treturn extension.COMPRESSED_RGB_ETC1_WEBGL;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// ETC2\n\n\t\tif ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) {\n\n\t\t\textension = extensions.get( 'WEBGL_compressed_texture_etc' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\tif ( p === RGB_ETC2_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2;\n\t\t\t\tif ( p === RGBA_ETC2_EAC_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// ASTC\n\n\t\tif ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format ||\n\t\t\tp === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format ||\n\t\t\tp === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format ||\n\t\t\tp === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format ||\n\t\t\tp === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) {\n\n\t\t\textension = extensions.get( 'WEBGL_compressed_texture_astc' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\tif ( p === RGBA_ASTC_4x4_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_5x4_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_5x5_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_6x5_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_6x6_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_8x5_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_8x6_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_8x8_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_10x5_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_10x6_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_10x8_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_10x10_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_12x10_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_12x12_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// BPTC\n\n\t\tif ( p === RGBA_BPTC_Format || p === RGB_BPTC_SIGNED_Format || p === RGB_BPTC_UNSIGNED_Format ) {\n\n\t\t\textension = extensions.get( 'EXT_texture_compression_bptc' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\tif ( p === RGBA_BPTC_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT;\n\t\t\t\tif ( p === RGB_BPTC_SIGNED_Format ) return extension.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;\n\t\t\t\tif ( p === RGB_BPTC_UNSIGNED_Format ) return extension.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// RGTC\n\n\t\tif ( p === RED_RGTC1_Format || p === SIGNED_RED_RGTC1_Format || p === RED_GREEN_RGTC2_Format || p === SIGNED_RED_GREEN_RGTC2_Format ) {\n\n\t\t\textension = extensions.get( 'EXT_texture_compression_rgtc' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\tif ( p === RGBA_BPTC_Format ) return extension.COMPRESSED_RED_RGTC1_EXT;\n\t\t\t\tif ( p === SIGNED_RED_RGTC1_Format ) return extension.COMPRESSED_SIGNED_RED_RGTC1_EXT;\n\t\t\t\tif ( p === RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_RED_GREEN_RGTC2_EXT;\n\t\t\t\tif ( p === SIGNED_RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t//\n\n\t\tif ( p === UnsignedInt248Type ) {\n\n\t\t\tif ( isWebGL2 ) return gl.UNSIGNED_INT_24_8;\n\n\t\t\textension = extensions.get( 'WEBGL_depth_texture' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\treturn extension.UNSIGNED_INT_24_8_WEBGL;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// if \"p\" can't be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats)\n\n\t\treturn ( gl[ p ] !== undefined ) ? gl[ p ] : null;\n\n\t}\n\n\treturn { convert: convert };\n\n}\n\nclass ArrayCamera extends PerspectiveCamera {\n\n\tconstructor( array = [] ) {\n\n\t\tsuper();\n\n\t\tthis.isArrayCamera = true;\n\n\t\tthis.cameras = array;\n\n\t}\n\n}\n\nclass Group extends Object3D {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isGroup = true;\n\n\t\tthis.type = 'Group';\n\n\t}\n\n}\n\nconst _moveEvent = { type: 'move' };\n\nclass WebXRController {\n\n\tconstructor() {\n\n\t\tthis._targetRay = null;\n\t\tthis._grip = null;\n\t\tthis._hand = null;\n\n\t}\n\n\tgetHandSpace() {\n\n\t\tif ( this._hand === null ) {\n\n\t\t\tthis._hand = new Group();\n\t\t\tthis._hand.matrixAutoUpdate = false;\n\t\t\tthis._hand.visible = false;\n\n\t\t\tthis._hand.joints = {};\n\t\t\tthis._hand.inputState = { pinching: false };\n\n\t\t}\n\n\t\treturn this._hand;\n\n\t}\n\n\tgetTargetRaySpace() {\n\n\t\tif ( this._targetRay === null ) {\n\n\t\t\tthis._targetRay = new Group();\n\t\t\tthis._targetRay.matrixAutoUpdate = false;\n\t\t\tthis._targetRay.visible = false;\n\t\t\tthis._targetRay.hasLinearVelocity = false;\n\t\t\tthis._targetRay.linearVelocity = new Vector3();\n\t\t\tthis._targetRay.hasAngularVelocity = false;\n\t\t\tthis._targetRay.angularVelocity = new Vector3();\n\n\t\t}\n\n\t\treturn this._targetRay;\n\n\t}\n\n\tgetGripSpace() {\n\n\t\tif ( this._grip === null ) {\n\n\t\t\tthis._grip = new Group();\n\t\t\tthis._grip.matrixAutoUpdate = false;\n\t\t\tthis._grip.visible = false;\n\t\t\tthis._grip.hasLinearVelocity = false;\n\t\t\tthis._grip.linearVelocity = new Vector3();\n\t\t\tthis._grip.hasAngularVelocity = false;\n\t\t\tthis._grip.angularVelocity = new Vector3();\n\n\t\t}\n\n\t\treturn this._grip;\n\n\t}\n\n\tdispatchEvent( event ) {\n\n\t\tif ( this._targetRay !== null ) {\n\n\t\t\tthis._targetRay.dispatchEvent( event );\n\n\t\t}\n\n\t\tif ( this._grip !== null ) {\n\n\t\t\tthis._grip.dispatchEvent( event );\n\n\t\t}\n\n\t\tif ( this._hand !== null ) {\n\n\t\t\tthis._hand.dispatchEvent( event );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tconnect( inputSource ) {\n\n\t\tif ( inputSource && inputSource.hand ) {\n\n\t\t\tconst hand = this._hand;\n\n\t\t\tif ( hand ) {\n\n\t\t\t\tfor ( const inputjoint of inputSource.hand.values() ) {\n\n\t\t\t\t\t// Initialize hand with joints when connected\n\t\t\t\t\tthis._getHandJoint( hand, inputjoint );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.dispatchEvent( { type: 'connected', data: inputSource } );\n\n\t\treturn this;\n\n\t}\n\n\tdisconnect( inputSource ) {\n\n\t\tthis.dispatchEvent( { type: 'disconnected', data: inputSource } );\n\n\t\tif ( this._targetRay !== null ) {\n\n\t\t\tthis._targetRay.visible = false;\n\n\t\t}\n\n\t\tif ( this._grip !== null ) {\n\n\t\t\tthis._grip.visible = false;\n\n\t\t}\n\n\t\tif ( this._hand !== null ) {\n\n\t\t\tthis._hand.visible = false;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tupdate( inputSource, frame, referenceSpace ) {\n\n\t\tlet inputPose = null;\n\t\tlet gripPose = null;\n\t\tlet handPose = null;\n\n\t\tconst targetRay = this._targetRay;\n\t\tconst grip = this._grip;\n\t\tconst hand = this._hand;\n\n\t\tif ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) {\n\n\t\t\tif ( hand && inputSource.hand ) {\n\n\t\t\t\thandPose = true;\n\n\t\t\t\tfor ( const inputjoint of inputSource.hand.values() ) {\n\n\t\t\t\t\t// Update the joints groups with the XRJoint poses\n\t\t\t\t\tconst jointPose = frame.getJointPose( inputjoint, referenceSpace );\n\n\t\t\t\t\t// The transform of this joint will be updated with the joint pose on each frame\n\t\t\t\t\tconst joint = this._getHandJoint( hand, inputjoint );\n\n\t\t\t\t\tif ( jointPose !== null ) {\n\n\t\t\t\t\t\tjoint.matrix.fromArray( jointPose.transform.matrix );\n\t\t\t\t\t\tjoint.matrix.decompose( joint.position, joint.rotation, joint.scale );\n\t\t\t\t\t\tjoint.matrixWorldNeedsUpdate = true;\n\t\t\t\t\t\tjoint.jointRadius = jointPose.radius;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tjoint.visible = jointPose !== null;\n\n\t\t\t\t}\n\n\t\t\t\t// Custom events\n\n\t\t\t\t// Check pinchz\n\t\t\t\tconst indexTip = hand.joints[ 'index-finger-tip' ];\n\t\t\t\tconst thumbTip = hand.joints[ 'thumb-tip' ];\n\t\t\t\tconst distance = indexTip.position.distanceTo( thumbTip.position );\n\n\t\t\t\tconst distanceToPinch = 0.02;\n\t\t\t\tconst threshold = 0.005;\n\n\t\t\t\tif ( hand.inputState.pinching && distance > distanceToPinch + threshold ) {\n\n\t\t\t\t\thand.inputState.pinching = false;\n\t\t\t\t\tthis.dispatchEvent( {\n\t\t\t\t\t\ttype: 'pinchend',\n\t\t\t\t\t\thandedness: inputSource.handedness,\n\t\t\t\t\t\ttarget: this\n\t\t\t\t\t} );\n\n\t\t\t\t} else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) {\n\n\t\t\t\t\thand.inputState.pinching = true;\n\t\t\t\t\tthis.dispatchEvent( {\n\t\t\t\t\t\ttype: 'pinchstart',\n\t\t\t\t\t\thandedness: inputSource.handedness,\n\t\t\t\t\t\ttarget: this\n\t\t\t\t\t} );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tif ( grip !== null && inputSource.gripSpace ) {\n\n\t\t\t\t\tgripPose = frame.getPose( inputSource.gripSpace, referenceSpace );\n\n\t\t\t\t\tif ( gripPose !== null ) {\n\n\t\t\t\t\t\tgrip.matrix.fromArray( gripPose.transform.matrix );\n\t\t\t\t\t\tgrip.matrix.decompose( grip.position, grip.rotation, grip.scale );\n\t\t\t\t\t\tgrip.matrixWorldNeedsUpdate = true;\n\n\t\t\t\t\t\tif ( gripPose.linearVelocity ) {\n\n\t\t\t\t\t\t\tgrip.hasLinearVelocity = true;\n\t\t\t\t\t\t\tgrip.linearVelocity.copy( gripPose.linearVelocity );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tgrip.hasLinearVelocity = false;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( gripPose.angularVelocity ) {\n\n\t\t\t\t\t\t\tgrip.hasAngularVelocity = true;\n\t\t\t\t\t\t\tgrip.angularVelocity.copy( gripPose.angularVelocity );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tgrip.hasAngularVelocity = false;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( targetRay !== null ) {\n\n\t\t\t\tinputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace );\n\n\t\t\t\t// Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it\n\t\t\t\tif ( inputPose === null && gripPose !== null ) {\n\n\t\t\t\t\tinputPose = gripPose;\n\n\t\t\t\t}\n\n\t\t\t\tif ( inputPose !== null ) {\n\n\t\t\t\t\ttargetRay.matrix.fromArray( inputPose.transform.matrix );\n\t\t\t\t\ttargetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale );\n\t\t\t\t\ttargetRay.matrixWorldNeedsUpdate = true;\n\n\t\t\t\t\tif ( inputPose.linearVelocity ) {\n\n\t\t\t\t\t\ttargetRay.hasLinearVelocity = true;\n\t\t\t\t\t\ttargetRay.linearVelocity.copy( inputPose.linearVelocity );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\ttargetRay.hasLinearVelocity = false;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( inputPose.angularVelocity ) {\n\n\t\t\t\t\t\ttargetRay.hasAngularVelocity = true;\n\t\t\t\t\t\ttargetRay.angularVelocity.copy( inputPose.angularVelocity );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\ttargetRay.hasAngularVelocity = false;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.dispatchEvent( _moveEvent );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\n\t\t}\n\n\t\tif ( targetRay !== null ) {\n\n\t\t\ttargetRay.visible = ( inputPose !== null );\n\n\t\t}\n\n\t\tif ( grip !== null ) {\n\n\t\t\tgrip.visible = ( gripPose !== null );\n\n\t\t}\n\n\t\tif ( hand !== null ) {\n\n\t\t\thand.visible = ( handPose !== null );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\t// private method\n\n\t_getHandJoint( hand, inputjoint ) {\n\n\t\tif ( hand.joints[ inputjoint.jointName ] === undefined ) {\n\n\t\t\tconst joint = new Group();\n\t\t\tjoint.matrixAutoUpdate = false;\n\t\t\tjoint.visible = false;\n\t\t\thand.joints[ inputjoint.jointName ] = joint;\n\n\t\t\thand.add( joint );\n\n\t\t}\n\n\t\treturn hand.joints[ inputjoint.jointName ];\n\n\t}\n\n}\n\nclass WebXRManager extends EventDispatcher {\n\n\tconstructor( renderer, gl ) {\n\n\t\tsuper();\n\n\t\tconst scope = this;\n\n\t\tlet session = null;\n\n\t\tlet framebufferScaleFactor = 1.0;\n\n\t\tlet referenceSpace = null;\n\t\tlet referenceSpaceType = 'local-floor';\n\t\t// Set default foveation to maximum.\n\t\tlet foveation = 1.0;\n\t\tlet customReferenceSpace = null;\n\n\t\tlet pose = null;\n\t\tlet glBinding = null;\n\t\tlet glProjLayer = null;\n\t\tlet glBaseLayer = null;\n\t\tlet xrFrame = null;\n\t\tconst attributes = gl.getContextAttributes();\n\t\tlet initialRenderTarget = null;\n\t\tlet newRenderTarget = null;\n\n\t\tconst controllers = [];\n\t\tconst controllerInputSources = [];\n\n\t\tconst currentSize = new Vector2();\n\t\tlet currentPixelRatio = null;\n\n\t\t//\n\n\t\tconst cameraL = new PerspectiveCamera();\n\t\tcameraL.layers.enable( 1 );\n\t\tcameraL.viewport = new Vector4();\n\n\t\tconst cameraR = new PerspectiveCamera();\n\t\tcameraR.layers.enable( 2 );\n\t\tcameraR.viewport = new Vector4();\n\n\t\tconst cameras = [ cameraL, cameraR ];\n\n\t\tconst cameraXR = new ArrayCamera();\n\t\tcameraXR.layers.enable( 1 );\n\t\tcameraXR.layers.enable( 2 );\n\n\t\tlet _currentDepthNear = null;\n\t\tlet _currentDepthFar = null;\n\n\t\t//\n\n\t\tthis.cameraAutoUpdate = true;\n\t\tthis.enabled = false;\n\n\t\tthis.isPresenting = false;\n\n\t\tthis.getController = function ( index ) {\n\n\t\t\tlet controller = controllers[ index ];\n\n\t\t\tif ( controller === undefined ) {\n\n\t\t\t\tcontroller = new WebXRController();\n\t\t\t\tcontrollers[ index ] = controller;\n\n\t\t\t}\n\n\t\t\treturn controller.getTargetRaySpace();\n\n\t\t};\n\n\t\tthis.getControllerGrip = function ( index ) {\n\n\t\t\tlet controller = controllers[ index ];\n\n\t\t\tif ( controller === undefined ) {\n\n\t\t\t\tcontroller = new WebXRController();\n\t\t\t\tcontrollers[ index ] = controller;\n\n\t\t\t}\n\n\t\t\treturn controller.getGripSpace();\n\n\t\t};\n\n\t\tthis.getHand = function ( index ) {\n\n\t\t\tlet controller = controllers[ index ];\n\n\t\t\tif ( controller === undefined ) {\n\n\t\t\t\tcontroller = new WebXRController();\n\t\t\t\tcontrollers[ index ] = controller;\n\n\t\t\t}\n\n\t\t\treturn controller.getHandSpace();\n\n\t\t};\n\n\t\t//\n\n\t\tfunction onSessionEvent( event ) {\n\n\t\t\tconst controllerIndex = controllerInputSources.indexOf( event.inputSource );\n\n\t\t\tif ( controllerIndex === - 1 ) {\n\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tconst controller = controllers[ controllerIndex ];\n\n\t\t\tif ( controller !== undefined ) {\n\n\t\t\t\tcontroller.update( event.inputSource, event.frame, customReferenceSpace || referenceSpace );\n\t\t\t\tcontroller.dispatchEvent( { type: event.type, data: event.inputSource } );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction onSessionEnd() {\n\n\t\t\tsession.removeEventListener( 'select', onSessionEvent );\n\t\t\tsession.removeEventListener( 'selectstart', onSessionEvent );\n\t\t\tsession.removeEventListener( 'selectend', onSessionEvent );\n\t\t\tsession.removeEventListener( 'squeeze', onSessionEvent );\n\t\t\tsession.removeEventListener( 'squeezestart', onSessionEvent );\n\t\t\tsession.removeEventListener( 'squeezeend', onSessionEvent );\n\t\t\tsession.removeEventListener( 'end', onSessionEnd );\n\t\t\tsession.removeEventListener( 'inputsourceschange', onInputSourcesChange );\n\n\t\t\tfor ( let i = 0; i < controllers.length; i ++ ) {\n\n\t\t\t\tconst inputSource = controllerInputSources[ i ];\n\n\t\t\t\tif ( inputSource === null ) continue;\n\n\t\t\t\tcontrollerInputSources[ i ] = null;\n\n\t\t\t\tcontrollers[ i ].disconnect( inputSource );\n\n\t\t\t}\n\n\t\t\t_currentDepthNear = null;\n\t\t\t_currentDepthFar = null;\n\n\t\t\t// restore framebuffer/rendering state\n\n\t\t\trenderer.setRenderTarget( initialRenderTarget );\n\n\t\t\tglBaseLayer = null;\n\t\t\tglProjLayer = null;\n\t\t\tglBinding = null;\n\t\t\tsession = null;\n\t\t\tnewRenderTarget = null;\n\n\t\t\t//\n\n\t\t\tanimation.stop();\n\n\t\t\tscope.isPresenting = false;\n\n\t\t\trenderer.setPixelRatio( currentPixelRatio );\n\t\t\trenderer.setSize( currentSize.width, currentSize.height, false );\n\n\t\t\tscope.dispatchEvent( { type: 'sessionend' } );\n\n\t\t}\n\n\t\tthis.setFramebufferScaleFactor = function ( value ) {\n\n\t\t\tframebufferScaleFactor = value;\n\n\t\t\tif ( scope.isPresenting === true ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' );\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.setReferenceSpaceType = function ( value ) {\n\n\t\t\treferenceSpaceType = value;\n\n\t\t\tif ( scope.isPresenting === true ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' );\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.getReferenceSpace = function () {\n\n\t\t\treturn customReferenceSpace || referenceSpace;\n\n\t\t};\n\n\t\tthis.setReferenceSpace = function ( space ) {\n\n\t\t\tcustomReferenceSpace = space;\n\n\t\t};\n\n\t\tthis.getBaseLayer = function () {\n\n\t\t\treturn glProjLayer !== null ? glProjLayer : glBaseLayer;\n\n\t\t};\n\n\t\tthis.getBinding = function () {\n\n\t\t\treturn glBinding;\n\n\t\t};\n\n\t\tthis.getFrame = function () {\n\n\t\t\treturn xrFrame;\n\n\t\t};\n\n\t\tthis.getSession = function () {\n\n\t\t\treturn session;\n\n\t\t};\n\n\t\tthis.setSession = async function ( value ) {\n\n\t\t\tsession = value;\n\n\t\t\tif ( session !== null ) {\n\n\t\t\t\tinitialRenderTarget = renderer.getRenderTarget();\n\n\t\t\t\tsession.addEventListener( 'select', onSessionEvent );\n\t\t\t\tsession.addEventListener( 'selectstart', onSessionEvent );\n\t\t\t\tsession.addEventListener( 'selectend', onSessionEvent );\n\t\t\t\tsession.addEventListener( 'squeeze', onSessionEvent );\n\t\t\t\tsession.addEventListener( 'squeezestart', onSessionEvent );\n\t\t\t\tsession.addEventListener( 'squeezeend', onSessionEvent );\n\t\t\t\tsession.addEventListener( 'end', onSessionEnd );\n\t\t\t\tsession.addEventListener( 'inputsourceschange', onInputSourcesChange );\n\n\t\t\t\tif ( attributes.xrCompatible !== true ) {\n\n\t\t\t\t\tawait gl.makeXRCompatible();\n\n\t\t\t\t}\n\n\t\t\t\tcurrentPixelRatio = renderer.getPixelRatio();\n\t\t\t\trenderer.getSize( currentSize );\n\n\t\t\t\tif ( ( session.renderState.layers === undefined ) || ( renderer.capabilities.isWebGL2 === false ) ) {\n\n\t\t\t\t\tconst layerInit = {\n\t\t\t\t\t\tantialias: ( session.renderState.layers === undefined ) ? attributes.antialias : true,\n\t\t\t\t\t\talpha: true,\n\t\t\t\t\t\tdepth: attributes.depth,\n\t\t\t\t\t\tstencil: attributes.stencil,\n\t\t\t\t\t\tframebufferScaleFactor: framebufferScaleFactor\n\t\t\t\t\t};\n\n\t\t\t\t\tglBaseLayer = new XRWebGLLayer( session, gl, layerInit );\n\n\t\t\t\t\tsession.updateRenderState( { baseLayer: glBaseLayer } );\n\n\t\t\t\t\trenderer.setPixelRatio( 1 );\n\t\t\t\t\trenderer.setSize( glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, false );\n\n\t\t\t\t\tnewRenderTarget = new WebGLRenderTarget(\n\t\t\t\t\t\tglBaseLayer.framebufferWidth,\n\t\t\t\t\t\tglBaseLayer.framebufferHeight,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tformat: RGBAFormat,\n\t\t\t\t\t\t\ttype: UnsignedByteType,\n\t\t\t\t\t\t\tcolorSpace: renderer.outputColorSpace,\n\t\t\t\t\t\t\tstencilBuffer: attributes.stencil\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\n\t\t\t\t} else {\n\n\t\t\t\t\tlet depthFormat = null;\n\t\t\t\t\tlet depthType = null;\n\t\t\t\t\tlet glDepthFormat = null;\n\n\t\t\t\t\tif ( attributes.depth ) {\n\n\t\t\t\t\t\tglDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24;\n\t\t\t\t\t\tdepthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat;\n\t\t\t\t\t\tdepthType = attributes.stencil ? UnsignedInt248Type : UnsignedIntType;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tconst projectionlayerInit = {\n\t\t\t\t\t\tcolorFormat: gl.RGBA8,\n\t\t\t\t\t\tdepthFormat: glDepthFormat,\n\t\t\t\t\t\tscaleFactor: framebufferScaleFactor\n\t\t\t\t\t};\n\n\t\t\t\t\tglBinding = new XRWebGLBinding( session, gl );\n\n\t\t\t\t\tglProjLayer = glBinding.createProjectionLayer( projectionlayerInit );\n\n\t\t\t\t\tsession.updateRenderState( { layers: [ glProjLayer ] } );\n\n\t\t\t\t\trenderer.setPixelRatio( 1 );\n\t\t\t\t\trenderer.setSize( glProjLayer.textureWidth, glProjLayer.textureHeight, false );\n\n\t\t\t\t\tnewRenderTarget = new WebGLRenderTarget(\n\t\t\t\t\t\tglProjLayer.textureWidth,\n\t\t\t\t\t\tglProjLayer.textureHeight,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tformat: RGBAFormat,\n\t\t\t\t\t\t\ttype: UnsignedByteType,\n\t\t\t\t\t\t\tdepthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ),\n\t\t\t\t\t\t\tstencilBuffer: attributes.stencil,\n\t\t\t\t\t\t\tcolorSpace: renderer.outputColorSpace,\n\t\t\t\t\t\t\tsamples: attributes.antialias ? 4 : 0\n\t\t\t\t\t\t} );\n\n\t\t\t\t\tconst renderTargetProperties = renderer.properties.get( newRenderTarget );\n\t\t\t\t\trenderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues;\n\n\t\t\t\t}\n\n\t\t\t\tnewRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278\n\n\t\t\t\tthis.setFoveation( foveation );\n\n\t\t\t\tcustomReferenceSpace = null;\n\t\t\t\treferenceSpace = await session.requestReferenceSpace( referenceSpaceType );\n\n\t\t\t\tanimation.setContext( session );\n\t\t\t\tanimation.start();\n\n\t\t\t\tscope.isPresenting = true;\n\n\t\t\t\tscope.dispatchEvent( { type: 'sessionstart' } );\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.getEnvironmentBlendMode = function () {\n\n\t\t\tif ( session !== null ) {\n\n\t\t\t\treturn session.environmentBlendMode;\n\n\t\t\t}\n\n\t\t};\n\n\t\tfunction onInputSourcesChange( event ) {\n\n\t\t\t// Notify disconnected\n\n\t\t\tfor ( let i = 0; i < event.removed.length; i ++ ) {\n\n\t\t\t\tconst inputSource = event.removed[ i ];\n\t\t\t\tconst index = controllerInputSources.indexOf( inputSource );\n\n\t\t\t\tif ( index >= 0 ) {\n\n\t\t\t\t\tcontrollerInputSources[ index ] = null;\n\t\t\t\t\tcontrollers[ index ].disconnect( inputSource );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Notify connected\n\n\t\t\tfor ( let i = 0; i < event.added.length; i ++ ) {\n\n\t\t\t\tconst inputSource = event.added[ i ];\n\n\t\t\t\tlet controllerIndex = controllerInputSources.indexOf( inputSource );\n\n\t\t\t\tif ( controllerIndex === - 1 ) {\n\n\t\t\t\t\t// Assign input source a controller that currently has no input source\n\n\t\t\t\t\tfor ( let i = 0; i < controllers.length; i ++ ) {\n\n\t\t\t\t\t\tif ( i >= controllerInputSources.length ) {\n\n\t\t\t\t\t\t\tcontrollerInputSources.push( inputSource );\n\t\t\t\t\t\t\tcontrollerIndex = i;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t} else if ( controllerInputSources[ i ] === null ) {\n\n\t\t\t\t\t\t\tcontrollerInputSources[ i ] = inputSource;\n\t\t\t\t\t\t\tcontrollerIndex = i;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// If all controllers do currently receive input we ignore new ones\n\n\t\t\t\t\tif ( controllerIndex === - 1 ) break;\n\n\t\t\t\t}\n\n\t\t\t\tconst controller = controllers[ controllerIndex ];\n\n\t\t\t\tif ( controller ) {\n\n\t\t\t\t\tcontroller.connect( inputSource );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t//\n\n\t\tconst cameraLPos = new Vector3();\n\t\tconst cameraRPos = new Vector3();\n\n\t\t/**\n\t\t * Assumes 2 cameras that are parallel and share an X-axis, and that\n\t\t * the cameras' projection and world matrices have already been set.\n\t\t * And that near and far planes are identical for both cameras.\n\t\t * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765\n\t\t */\n\t\tfunction setProjectionFromUnion( camera, cameraL, cameraR ) {\n\n\t\t\tcameraLPos.setFromMatrixPosition( cameraL.matrixWorld );\n\t\t\tcameraRPos.setFromMatrixPosition( cameraR.matrixWorld );\n\n\t\t\tconst ipd = cameraLPos.distanceTo( cameraRPos );\n\n\t\t\tconst projL = cameraL.projectionMatrix.elements;\n\t\t\tconst projR = cameraR.projectionMatrix.elements;\n\n\t\t\t// VR systems will have identical far and near planes, and\n\t\t\t// most likely identical top and bottom frustum extents.\n\t\t\t// Use the left camera for these values.\n\t\t\tconst near = projL[ 14 ] / ( projL[ 10 ] - 1 );\n\t\t\tconst far = projL[ 14 ] / ( projL[ 10 ] + 1 );\n\t\t\tconst topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ];\n\t\t\tconst bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ];\n\n\t\t\tconst leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ];\n\t\t\tconst rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ];\n\t\t\tconst left = near * leftFov;\n\t\t\tconst right = near * rightFov;\n\n\t\t\t// Calculate the new camera's position offset from the\n\t\t\t// left camera. xOffset should be roughly half `ipd`.\n\t\t\tconst zOffset = ipd / ( - leftFov + rightFov );\n\t\t\tconst xOffset = zOffset * - leftFov;\n\n\t\t\t// TODO: Better way to apply this offset?\n\t\t\tcameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale );\n\t\t\tcamera.translateX( xOffset );\n\t\t\tcamera.translateZ( zOffset );\n\t\t\tcamera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale );\n\t\t\tcamera.matrixWorldInverse.copy( camera.matrixWorld ).invert();\n\n\t\t\t// Find the union of the frustum values of the cameras and scale\n\t\t\t// the values so that the near plane's position does not change in world space,\n\t\t\t// although must now be relative to the new union camera.\n\t\t\tconst near2 = near + zOffset;\n\t\t\tconst far2 = far + zOffset;\n\t\t\tconst left2 = left - xOffset;\n\t\t\tconst right2 = right + ( ipd - xOffset );\n\t\t\tconst top2 = topFov * far / far2 * near2;\n\t\t\tconst bottom2 = bottomFov * far / far2 * near2;\n\n\t\t\tcamera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 );\n\t\t\tcamera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert();\n\n\t\t}\n\n\t\tfunction updateCamera( camera, parent ) {\n\n\t\t\tif ( parent === null ) {\n\n\t\t\t\tcamera.matrixWorld.copy( camera.matrix );\n\n\t\t\t} else {\n\n\t\t\t\tcamera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix );\n\n\t\t\t}\n\n\t\t\tcamera.matrixWorldInverse.copy( camera.matrixWorld ).invert();\n\n\t\t}\n\n\t\tthis.updateCamera = function ( camera ) {\n\n\t\t\tif ( session === null ) return;\n\n\t\t\tcameraXR.near = cameraR.near = cameraL.near = camera.near;\n\t\t\tcameraXR.far = cameraR.far = cameraL.far = camera.far;\n\n\t\t\tif ( _currentDepthNear !== cameraXR.near || _currentDepthFar !== cameraXR.far ) {\n\n\t\t\t\t// Note that the new renderState won't apply until the next frame. See #18320\n\n\t\t\t\tsession.updateRenderState( {\n\t\t\t\t\tdepthNear: cameraXR.near,\n\t\t\t\t\tdepthFar: cameraXR.far\n\t\t\t\t} );\n\n\t\t\t\t_currentDepthNear = cameraXR.near;\n\t\t\t\t_currentDepthFar = cameraXR.far;\n\n\t\t\t}\n\n\t\t\tconst parent = camera.parent;\n\t\t\tconst cameras = cameraXR.cameras;\n\n\t\t\tupdateCamera( cameraXR, parent );\n\n\t\t\tfor ( let i = 0; i < cameras.length; i ++ ) {\n\n\t\t\t\tupdateCamera( cameras[ i ], parent );\n\n\t\t\t}\n\n\t\t\t// update projection matrix for proper view frustum culling\n\n\t\t\tif ( cameras.length === 2 ) {\n\n\t\t\t\tsetProjectionFromUnion( cameraXR, cameraL, cameraR );\n\n\t\t\t} else {\n\n\t\t\t\t// assume single camera setup (AR)\n\n\t\t\t\tcameraXR.projectionMatrix.copy( cameraL.projectionMatrix );\n\n\t\t\t}\n\n\t\t\t// update user camera and its children\n\n\t\t\tupdateUserCamera( camera, cameraXR, parent );\n\n\t\t};\n\n\t\tfunction updateUserCamera( camera, cameraXR, parent ) {\n\n\t\t\tif ( parent === null ) {\n\n\t\t\t\tcamera.matrix.copy( cameraXR.matrixWorld );\n\n\t\t\t} else {\n\n\t\t\t\tcamera.matrix.copy( parent.matrixWorld );\n\t\t\t\tcamera.matrix.invert();\n\t\t\t\tcamera.matrix.multiply( cameraXR.matrixWorld );\n\n\t\t\t}\n\n\t\t\tcamera.matrix.decompose( camera.position, camera.quaternion, camera.scale );\n\t\t\tcamera.updateMatrixWorld( true );\n\n\t\t\tcamera.projectionMatrix.copy( cameraXR.projectionMatrix );\n\t\t\tcamera.projectionMatrixInverse.copy( cameraXR.projectionMatrixInverse );\n\n\t\t\tif ( camera.isPerspectiveCamera ) {\n\n\t\t\t\tcamera.fov = RAD2DEG * 2 * Math.atan( 1 / camera.projectionMatrix.elements[ 5 ] );\n\t\t\t\tcamera.zoom = 1;\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.getCamera = function () {\n\n\t\t\treturn cameraXR;\n\n\t\t};\n\n\t\tthis.getFoveation = function () {\n\n\t\t\tif ( glProjLayer === null && glBaseLayer === null ) {\n\n\t\t\t\treturn undefined;\n\n\t\t\t}\n\n\t\t\treturn foveation;\n\n\t\t};\n\n\t\tthis.setFoveation = function ( value ) {\n\n\t\t\t// 0 = no foveation = full resolution\n\t\t\t// 1 = maximum foveation = the edges render at lower resolution\n\n\t\t\tfoveation = value;\n\n\t\t\tif ( glProjLayer !== null ) {\n\n\t\t\t\tglProjLayer.fixedFoveation = value;\n\n\t\t\t}\n\n\t\t\tif ( glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined ) {\n\n\t\t\t\tglBaseLayer.fixedFoveation = value;\n\n\t\t\t}\n\n\t\t};\n\n\t\t// Animation Loop\n\n\t\tlet onAnimationFrameCallback = null;\n\n\t\tfunction onAnimationFrame( time, frame ) {\n\n\t\t\tpose = frame.getViewerPose( customReferenceSpace || referenceSpace );\n\t\t\txrFrame = frame;\n\n\t\t\tif ( pose !== null ) {\n\n\t\t\t\tconst views = pose.views;\n\n\t\t\t\tif ( glBaseLayer !== null ) {\n\n\t\t\t\t\trenderer.setRenderTargetFramebuffer( newRenderTarget, glBaseLayer.framebuffer );\n\t\t\t\t\trenderer.setRenderTarget( newRenderTarget );\n\n\t\t\t\t}\n\n\t\t\t\tlet cameraXRNeedsUpdate = false;\n\n\t\t\t\t// check if it's necessary to rebuild cameraXR's camera list\n\n\t\t\t\tif ( views.length !== cameraXR.cameras.length ) {\n\n\t\t\t\t\tcameraXR.cameras.length = 0;\n\t\t\t\t\tcameraXRNeedsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t\tfor ( let i = 0; i < views.length; i ++ ) {\n\n\t\t\t\t\tconst view = views[ i ];\n\n\t\t\t\t\tlet viewport = null;\n\n\t\t\t\t\tif ( glBaseLayer !== null ) {\n\n\t\t\t\t\t\tviewport = glBaseLayer.getViewport( view );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconst glSubImage = glBinding.getViewSubImage( glProjLayer, view );\n\t\t\t\t\t\tviewport = glSubImage.viewport;\n\n\t\t\t\t\t\t// For side-by-side projection, we only produce a single texture for both eyes.\n\t\t\t\t\t\tif ( i === 0 ) {\n\n\t\t\t\t\t\t\trenderer.setRenderTargetTextures(\n\t\t\t\t\t\t\t\tnewRenderTarget,\n\t\t\t\t\t\t\t\tglSubImage.colorTexture,\n\t\t\t\t\t\t\t\tglProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture );\n\n\t\t\t\t\t\t\trenderer.setRenderTarget( newRenderTarget );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tlet camera = cameras[ i ];\n\n\t\t\t\t\tif ( camera === undefined ) {\n\n\t\t\t\t\t\tcamera = new PerspectiveCamera();\n\t\t\t\t\t\tcamera.layers.enable( i );\n\t\t\t\t\t\tcamera.viewport = new Vector4();\n\t\t\t\t\t\tcameras[ i ] = camera;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tcamera.matrix.fromArray( view.transform.matrix );\n\t\t\t\t\tcamera.matrix.decompose( camera.position, camera.quaternion, camera.scale );\n\t\t\t\t\tcamera.projectionMatrix.fromArray( view.projectionMatrix );\n\t\t\t\t\tcamera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert();\n\t\t\t\t\tcamera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height );\n\n\t\t\t\t\tif ( i === 0 ) {\n\n\t\t\t\t\t\tcameraXR.matrix.copy( camera.matrix );\n\t\t\t\t\t\tcameraXR.matrix.decompose( cameraXR.position, cameraXR.quaternion, cameraXR.scale );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( cameraXRNeedsUpdate === true ) {\n\n\t\t\t\t\t\tcameraXR.cameras.push( camera );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tfor ( let i = 0; i < controllers.length; i ++ ) {\n\n\t\t\t\tconst inputSource = controllerInputSources[ i ];\n\t\t\t\tconst controller = controllers[ i ];\n\n\t\t\t\tif ( inputSource !== null && controller !== undefined ) {\n\n\t\t\t\t\tcontroller.update( inputSource, frame, customReferenceSpace || referenceSpace );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame );\n\n\t\t\tif ( frame.detectedPlanes ) {\n\n\t\t\t\tscope.dispatchEvent( { type: 'planesdetected', data: frame } );\n\n\t\t\t}\n\n\t\t\txrFrame = null;\n\n\t\t}\n\n\t\tconst animation = new WebGLAnimation();\n\n\t\tanimation.setAnimationLoop( onAnimationFrame );\n\n\t\tthis.setAnimationLoop = function ( callback ) {\n\n\t\t\tonAnimationFrameCallback = callback;\n\n\t\t};\n\n\t\tthis.dispose = function () {};\n\n\t}\n\n}\n\nfunction WebGLMaterials( renderer, properties ) {\n\n\tfunction refreshTransformUniform( map, uniform ) {\n\n\t\tif ( map.matrixAutoUpdate === true ) {\n\n\t\t\tmap.updateMatrix();\n\n\t\t}\n\n\t\tuniform.value.copy( map.matrix );\n\n\t}\n\n\tfunction refreshFogUniforms( uniforms, fog ) {\n\n\t\tfog.color.getRGB( uniforms.fogColor.value, getUnlitUniformColorSpace( renderer ) );\n\n\t\tif ( fog.isFog ) {\n\n\t\t\tuniforms.fogNear.value = fog.near;\n\t\t\tuniforms.fogFar.value = fog.far;\n\n\t\t} else if ( fog.isFogExp2 ) {\n\n\t\t\tuniforms.fogDensity.value = fog.density;\n\n\t\t}\n\n\t}\n\n\tfunction refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget ) {\n\n\t\tif ( material.isMeshBasicMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\n\t\t} else if ( material.isMeshLambertMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\n\t\t} else if ( material.isMeshToonMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\t\t\trefreshUniformsToon( uniforms, material );\n\n\t\t} else if ( material.isMeshPhongMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\t\t\trefreshUniformsPhong( uniforms, material );\n\n\t\t} else if ( material.isMeshStandardMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\t\t\trefreshUniformsStandard( uniforms, material );\n\n\t\t\tif ( material.isMeshPhysicalMaterial ) {\n\n\t\t\t\trefreshUniformsPhysical( uniforms, material, transmissionRenderTarget );\n\n\t\t\t}\n\n\t\t} else if ( material.isMeshMatcapMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\t\t\trefreshUniformsMatcap( uniforms, material );\n\n\t\t} else if ( material.isMeshDepthMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\n\t\t} else if ( material.isMeshDistanceMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\t\t\trefreshUniformsDistance( uniforms, material );\n\n\t\t} else if ( material.isMeshNormalMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\n\t\t} else if ( material.isLineBasicMaterial ) {\n\n\t\t\trefreshUniformsLine( uniforms, material );\n\n\t\t\tif ( material.isLineDashedMaterial ) {\n\n\t\t\t\trefreshUniformsDash( uniforms, material );\n\n\t\t\t}\n\n\t\t} else if ( material.isPointsMaterial ) {\n\n\t\t\trefreshUniformsPoints( uniforms, material, pixelRatio, height );\n\n\t\t} else if ( material.isSpriteMaterial ) {\n\n\t\t\trefreshUniformsSprites( uniforms, material );\n\n\t\t} else if ( material.isShadowMaterial ) {\n\n\t\t\tuniforms.color.value.copy( material.color );\n\t\t\tuniforms.opacity.value = material.opacity;\n\n\t\t} else if ( material.isShaderMaterial ) {\n\n\t\t\tmaterial.uniformsNeedUpdate = false; // #15581\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsCommon( uniforms, material ) {\n\n\t\tuniforms.opacity.value = material.opacity;\n\n\t\tif ( material.color ) {\n\n\t\t\tuniforms.diffuse.value.copy( material.color );\n\n\t\t}\n\n\t\tif ( material.emissive ) {\n\n\t\t\tuniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );\n\n\t\t}\n\n\t\tif ( material.map ) {\n\n\t\t\tuniforms.map.value = material.map;\n\n\t\t\trefreshTransformUniform( material.map, uniforms.mapTransform );\n\n\t\t}\n\n\t\tif ( material.alphaMap ) {\n\n\t\t\tuniforms.alphaMap.value = material.alphaMap;\n\n\t\t\trefreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform );\n\n\t\t}\n\n\t\tif ( material.bumpMap ) {\n\n\t\t\tuniforms.bumpMap.value = material.bumpMap;\n\n\t\t\trefreshTransformUniform( material.bumpMap, uniforms.bumpMapTransform );\n\n\t\t\tuniforms.bumpScale.value = material.bumpScale;\n\n\t\t\tif ( material.side === BackSide ) {\n\n\t\t\t\tuniforms.bumpScale.value *= - 1;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( material.normalMap ) {\n\n\t\t\tuniforms.normalMap.value = material.normalMap;\n\n\t\t\trefreshTransformUniform( material.normalMap, uniforms.normalMapTransform );\n\n\t\t\tuniforms.normalScale.value.copy( material.normalScale );\n\n\t\t\tif ( material.side === BackSide ) {\n\n\t\t\t\tuniforms.normalScale.value.negate();\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( material.displacementMap ) {\n\n\t\t\tuniforms.displacementMap.value = material.displacementMap;\n\n\t\t\trefreshTransformUniform( material.displacementMap, uniforms.displacementMapTransform );\n\n\t\t\tuniforms.displacementScale.value = material.displacementScale;\n\t\t\tuniforms.displacementBias.value = material.displacementBias;\n\n\t\t}\n\n\t\tif ( material.emissiveMap ) {\n\n\t\t\tuniforms.emissiveMap.value = material.emissiveMap;\n\n\t\t\trefreshTransformUniform( material.emissiveMap, uniforms.emissiveMapTransform );\n\n\t\t}\n\n\t\tif ( material.specularMap ) {\n\n\t\t\tuniforms.specularMap.value = material.specularMap;\n\n\t\t\trefreshTransformUniform( material.specularMap, uniforms.specularMapTransform );\n\n\t\t}\n\n\t\tif ( material.alphaTest > 0 ) {\n\n\t\t\tuniforms.alphaTest.value = material.alphaTest;\n\n\t\t}\n\n\t\tconst envMap = properties.get( material ).envMap;\n\n\t\tif ( envMap ) {\n\n\t\t\tuniforms.envMap.value = envMap;\n\n\t\t\tuniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1;\n\n\t\t\tuniforms.reflectivity.value = material.reflectivity;\n\t\t\tuniforms.ior.value = material.ior;\n\t\t\tuniforms.refractionRatio.value = material.refractionRatio;\n\n\t\t}\n\n\t\tif ( material.lightMap ) {\n\n\t\t\tuniforms.lightMap.value = material.lightMap;\n\n\t\t\t// artist-friendly light intensity scaling factor\n\t\t\tconst scaleFactor = ( renderer._useLegacyLights === true ) ? Math.PI : 1;\n\n\t\t\tuniforms.lightMapIntensity.value = material.lightMapIntensity * scaleFactor;\n\n\t\t\trefreshTransformUniform( material.lightMap, uniforms.lightMapTransform );\n\n\t\t}\n\n\t\tif ( material.aoMap ) {\n\n\t\t\tuniforms.aoMap.value = material.aoMap;\n\t\t\tuniforms.aoMapIntensity.value = material.aoMapIntensity;\n\n\t\t\trefreshTransformUniform( material.aoMap, uniforms.aoMapTransform );\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsLine( uniforms, material ) {\n\n\t\tuniforms.diffuse.value.copy( material.color );\n\t\tuniforms.opacity.value = material.opacity;\n\n\t\tif ( material.map ) {\n\n\t\t\tuniforms.map.value = material.map;\n\n\t\t\trefreshTransformUniform( material.map, uniforms.mapTransform );\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsDash( uniforms, material ) {\n\n\t\tuniforms.dashSize.value = material.dashSize;\n\t\tuniforms.totalSize.value = material.dashSize + material.gapSize;\n\t\tuniforms.scale.value = material.scale;\n\n\t}\n\n\tfunction refreshUniformsPoints( uniforms, material, pixelRatio, height ) {\n\n\t\tuniforms.diffuse.value.copy( material.color );\n\t\tuniforms.opacity.value = material.opacity;\n\t\tuniforms.size.value = material.size * pixelRatio;\n\t\tuniforms.scale.value = height * 0.5;\n\n\t\tif ( material.map ) {\n\n\t\t\tuniforms.map.value = material.map;\n\n\t\t\trefreshTransformUniform( material.map, uniforms.uvTransform );\n\n\t\t}\n\n\t\tif ( material.alphaMap ) {\n\n\t\t\tuniforms.alphaMap.value = material.alphaMap;\n\n\t\t\trefreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform );\n\n\t\t}\n\n\t\tif ( material.alphaTest > 0 ) {\n\n\t\t\tuniforms.alphaTest.value = material.alphaTest;\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsSprites( uniforms, material ) {\n\n\t\tuniforms.diffuse.value.copy( material.color );\n\t\tuniforms.opacity.value = material.opacity;\n\t\tuniforms.rotation.value = material.rotation;\n\n\t\tif ( material.map ) {\n\n\t\t\tuniforms.map.value = material.map;\n\n\t\t\trefreshTransformUniform( material.map, uniforms.mapTransform );\n\n\t\t}\n\n\t\tif ( material.alphaMap ) {\n\n\t\t\tuniforms.alphaMap.value = material.alphaMap;\n\n\t\t\trefreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform );\n\n\t\t}\n\n\t\tif ( material.alphaTest > 0 ) {\n\n\t\t\tuniforms.alphaTest.value = material.alphaTest;\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsPhong( uniforms, material ) {\n\n\t\tuniforms.specular.value.copy( material.specular );\n\t\tuniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )\n\n\t}\n\n\tfunction refreshUniformsToon( uniforms, material ) {\n\n\t\tif ( material.gradientMap ) {\n\n\t\t\tuniforms.gradientMap.value = material.gradientMap;\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsStandard( uniforms, material ) {\n\n\t\tuniforms.metalness.value = material.metalness;\n\n\t\tif ( material.metalnessMap ) {\n\n\t\t\tuniforms.metalnessMap.value = material.metalnessMap;\n\n\t\t\trefreshTransformUniform( material.metalnessMap, uniforms.metalnessMapTransform );\n\n\t\t}\n\n\t\tuniforms.roughness.value = material.roughness;\n\n\t\tif ( material.roughnessMap ) {\n\n\t\t\tuniforms.roughnessMap.value = material.roughnessMap;\n\n\t\t\trefreshTransformUniform( material.roughnessMap, uniforms.roughnessMapTransform );\n\n\t\t}\n\n\t\tconst envMap = properties.get( material ).envMap;\n\n\t\tif ( envMap ) {\n\n\t\t\t//uniforms.envMap.value = material.envMap; // part of uniforms common\n\t\t\tuniforms.envMapIntensity.value = material.envMapIntensity;\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ) {\n\n\t\tuniforms.ior.value = material.ior; // also part of uniforms common\n\n\t\tif ( material.sheen > 0 ) {\n\n\t\t\tuniforms.sheenColor.value.copy( material.sheenColor ).multiplyScalar( material.sheen );\n\n\t\t\tuniforms.sheenRoughness.value = material.sheenRoughness;\n\n\t\t\tif ( material.sheenColorMap ) {\n\n\t\t\t\tuniforms.sheenColorMap.value = material.sheenColorMap;\n\n\t\t\t\trefreshTransformUniform( material.sheenColorMap, uniforms.sheenColorMapTransform );\n\n\t\t\t}\n\n\t\t\tif ( material.sheenRoughnessMap ) {\n\n\t\t\t\tuniforms.sheenRoughnessMap.value = material.sheenRoughnessMap;\n\n\t\t\t\trefreshTransformUniform( material.sheenRoughnessMap, uniforms.sheenRoughnessMapTransform );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( material.clearcoat > 0 ) {\n\n\t\t\tuniforms.clearcoat.value = material.clearcoat;\n\t\t\tuniforms.clearcoatRoughness.value = material.clearcoatRoughness;\n\n\t\t\tif ( material.clearcoatMap ) {\n\n\t\t\t\tuniforms.clearcoatMap.value = material.clearcoatMap;\n\n\t\t\t\trefreshTransformUniform( material.clearcoatMap, uniforms.clearcoatMapTransform );\n\n\t\t\t}\n\n\t\t\tif ( material.clearcoatRoughnessMap ) {\n\n\t\t\t\tuniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap;\n\n\t\t\t\trefreshTransformUniform( material.clearcoatRoughnessMap, uniforms.clearcoatRoughnessMapTransform );\n\n\t\t\t}\n\n\t\t\tif ( material.clearcoatNormalMap ) {\n\n\t\t\t\tuniforms.clearcoatNormalMap.value = material.clearcoatNormalMap;\n\n\t\t\t\trefreshTransformUniform( material.clearcoatNormalMap, uniforms.clearcoatNormalMapTransform );\n\n\t\t\t\tuniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale );\n\n\t\t\t\tif ( material.side === BackSide ) {\n\n\t\t\t\t\tuniforms.clearcoatNormalScale.value.negate();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( material.iridescence > 0 ) {\n\n\t\t\tuniforms.iridescence.value = material.iridescence;\n\t\t\tuniforms.iridescenceIOR.value = material.iridescenceIOR;\n\t\t\tuniforms.iridescenceThicknessMinimum.value = material.iridescenceThicknessRange[ 0 ];\n\t\t\tuniforms.iridescenceThicknessMaximum.value = material.iridescenceThicknessRange[ 1 ];\n\n\t\t\tif ( material.iridescenceMap ) {\n\n\t\t\t\tuniforms.iridescenceMap.value = material.iridescenceMap;\n\n\t\t\t\trefreshTransformUniform( material.iridescenceMap, uniforms.iridescenceMapTransform );\n\n\t\t\t}\n\n\t\t\tif ( material.iridescenceThicknessMap ) {\n\n\t\t\t\tuniforms.iridescenceThicknessMap.value = material.iridescenceThicknessMap;\n\n\t\t\t\trefreshTransformUniform( material.iridescenceThicknessMap, uniforms.iridescenceThicknessMapTransform );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( material.transmission > 0 ) {\n\n\t\t\tuniforms.transmission.value = material.transmission;\n\t\t\tuniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture;\n\t\t\tuniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height );\n\n\t\t\tif ( material.transmissionMap ) {\n\n\t\t\t\tuniforms.transmissionMap.value = material.transmissionMap;\n\n\t\t\t\trefreshTransformUniform( material.transmissionMap, uniforms.transmissionMapTransform );\n\n\t\t\t}\n\n\t\t\tuniforms.thickness.value = material.thickness;\n\n\t\t\tif ( material.thicknessMap ) {\n\n\t\t\t\tuniforms.thicknessMap.value = material.thicknessMap;\n\n\t\t\t\trefreshTransformUniform( material.thicknessMap, uniforms.thicknessMapTransform );\n\n\t\t\t}\n\n\t\t\tuniforms.attenuationDistance.value = material.attenuationDistance;\n\t\t\tuniforms.attenuationColor.value.copy( material.attenuationColor );\n\n\t\t}\n\n\t\tif ( material.anisotropy > 0 ) {\n\n\t\t\tuniforms.anisotropyVector.value.set( material.anisotropy * Math.cos( material.anisotropyRotation ), material.anisotropy * Math.sin( material.anisotropyRotation ) );\n\n\t\t\tif ( material.anisotropyMap ) {\n\n\t\t\t\tuniforms.anisotropyMap.value = material.anisotropyMap;\n\n\t\t\t\trefreshTransformUniform( material.anisotropyMap, uniforms.anisotropyMapTransform );\n\n\t\t\t}\n\n\t\t}\n\n\t\tuniforms.specularIntensity.value = material.specularIntensity;\n\t\tuniforms.specularColor.value.copy( material.specularColor );\n\n\t\tif ( material.specularColorMap ) {\n\n\t\t\tuniforms.specularColorMap.value = material.specularColorMap;\n\n\t\t\trefreshTransformUniform( material.specularColorMap, uniforms.specularColorMapTransform );\n\n\t\t}\n\n\t\tif ( material.specularIntensityMap ) {\n\n\t\t\tuniforms.specularIntensityMap.value = material.specularIntensityMap;\n\n\t\t\trefreshTransformUniform( material.specularIntensityMap, uniforms.specularIntensityMapTransform );\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsMatcap( uniforms, material ) {\n\n\t\tif ( material.matcap ) {\n\n\t\t\tuniforms.matcap.value = material.matcap;\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsDistance( uniforms, material ) {\n\n\t\tconst light = properties.get( material ).light;\n\n\t\tuniforms.referencePosition.value.setFromMatrixPosition( light.matrixWorld );\n\t\tuniforms.nearDistance.value = light.shadow.camera.near;\n\t\tuniforms.farDistance.value = light.shadow.camera.far;\n\n\t}\n\n\treturn {\n\t\trefreshFogUniforms: refreshFogUniforms,\n\t\trefreshMaterialUniforms: refreshMaterialUniforms\n\t};\n\n}\n\nfunction WebGLUniformsGroups( gl, info, capabilities, state ) {\n\n\tlet buffers = {};\n\tlet updateList = {};\n\tlet allocatedBindingPoints = [];\n\n\tconst maxBindingPoints = ( capabilities.isWebGL2 ) ? gl.getParameter( gl.MAX_UNIFORM_BUFFER_BINDINGS ) : 0; // binding points are global whereas block indices are per shader program\n\n\tfunction bind( uniformsGroup, program ) {\n\n\t\tconst webglProgram = program.program;\n\t\tstate.uniformBlockBinding( uniformsGroup, webglProgram );\n\n\t}\n\n\tfunction update( uniformsGroup, program ) {\n\n\t\tlet buffer = buffers[ uniformsGroup.id ];\n\n\t\tif ( buffer === undefined ) {\n\n\t\t\tprepareUniformsGroup( uniformsGroup );\n\n\t\t\tbuffer = createBuffer( uniformsGroup );\n\t\t\tbuffers[ uniformsGroup.id ] = buffer;\n\n\t\t\tuniformsGroup.addEventListener( 'dispose', onUniformsGroupsDispose );\n\n\t\t}\n\n\t\t// ensure to update the binding points/block indices mapping for this program\n\n\t\tconst webglProgram = program.program;\n\t\tstate.updateUBOMapping( uniformsGroup, webglProgram );\n\n\t\t// update UBO once per frame\n\n\t\tconst frame = info.render.frame;\n\n\t\tif ( updateList[ uniformsGroup.id ] !== frame ) {\n\n\t\t\tupdateBufferData( uniformsGroup );\n\n\t\t\tupdateList[ uniformsGroup.id ] = frame;\n\n\t\t}\n\n\t}\n\n\tfunction createBuffer( uniformsGroup ) {\n\n\t\t// the setup of an UBO is independent of a particular shader program but global\n\n\t\tconst bindingPointIndex = allocateBindingPointIndex();\n\t\tuniformsGroup.__bindingPointIndex = bindingPointIndex;\n\n\t\tconst buffer = gl.createBuffer();\n\t\tconst size = uniformsGroup.__size;\n\t\tconst usage = uniformsGroup.usage;\n\n\t\tgl.bindBuffer( gl.UNIFORM_BUFFER, buffer );\n\t\tgl.bufferData( gl.UNIFORM_BUFFER, size, usage );\n\t\tgl.bindBuffer( gl.UNIFORM_BUFFER, null );\n\t\tgl.bindBufferBase( gl.UNIFORM_BUFFER, bindingPointIndex, buffer );\n\n\t\treturn buffer;\n\n\t}\n\n\tfunction allocateBindingPointIndex() {\n\n\t\tfor ( let i = 0; i < maxBindingPoints; i ++ ) {\n\n\t\t\tif ( allocatedBindingPoints.indexOf( i ) === - 1 ) {\n\n\t\t\t\tallocatedBindingPoints.push( i );\n\t\t\t\treturn i;\n\n\t\t\t}\n\n\t\t}\n\n\t\tconsole.error( 'THREE.WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached.' );\n\n\t\treturn 0;\n\n\t}\n\n\tfunction updateBufferData( uniformsGroup ) {\n\n\t\tconst buffer = buffers[ uniformsGroup.id ];\n\t\tconst uniforms = uniformsGroup.uniforms;\n\t\tconst cache = uniformsGroup.__cache;\n\n\t\tgl.bindBuffer( gl.UNIFORM_BUFFER, buffer );\n\n\t\tfor ( let i = 0, il = uniforms.length; i < il; i ++ ) {\n\n\t\t\tconst uniformArray = Array.isArray( uniforms[ i ] ) ? uniforms[ i ] : [ uniforms[ i ] ];\n\n\t\t\tfor ( let j = 0, jl = uniformArray.length; j < jl; j ++ ) {\n\n\t\t\t\tconst uniform = uniformArray[ j ];\n\n\t\t\t\tif ( hasUniformChanged( uniform, i, j, cache ) === true ) {\n\n\t\t\t\t\tconst offset = uniform.__offset;\n\n\t\t\t\t\tconst values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ];\n\n\t\t\t\t\tlet arrayOffset = 0;\n\n\t\t\t\t\tfor ( let k = 0; k < values.length; k ++ ) {\n\n\t\t\t\t\t\tconst value = values[ k ];\n\n\t\t\t\t\t\tconst info = getUniformSize( value );\n\n\t\t\t\t\t\t// TODO add integer and struct support\n\t\t\t\t\t\tif ( typeof value === 'number' || typeof value === 'boolean' ) {\n\n\t\t\t\t\t\t\tuniform.__data[ 0 ] = value;\n\t\t\t\t\t\t\tgl.bufferSubData( gl.UNIFORM_BUFFER, offset + arrayOffset, uniform.__data );\n\n\t\t\t\t\t\t} else if ( value.isMatrix3 ) {\n\n\t\t\t\t\t\t\t// manually converting 3x3 to 3x4\n\n\t\t\t\t\t\t\tuniform.__data[ 0 ] = value.elements[ 0 ];\n\t\t\t\t\t\t\tuniform.__data[ 1 ] = value.elements[ 1 ];\n\t\t\t\t\t\t\tuniform.__data[ 2 ] = value.elements[ 2 ];\n\t\t\t\t\t\t\tuniform.__data[ 3 ] = 0;\n\t\t\t\t\t\t\tuniform.__data[ 4 ] = value.elements[ 3 ];\n\t\t\t\t\t\t\tuniform.__data[ 5 ] = value.elements[ 4 ];\n\t\t\t\t\t\t\tuniform.__data[ 6 ] = value.elements[ 5 ];\n\t\t\t\t\t\t\tuniform.__data[ 7 ] = 0;\n\t\t\t\t\t\t\tuniform.__data[ 8 ] = value.elements[ 6 ];\n\t\t\t\t\t\t\tuniform.__data[ 9 ] = value.elements[ 7 ];\n\t\t\t\t\t\t\tuniform.__data[ 10 ] = value.elements[ 8 ];\n\t\t\t\t\t\t\tuniform.__data[ 11 ] = 0;\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tvalue.toArray( uniform.__data, arrayOffset );\n\n\t\t\t\t\t\t\tarrayOffset += info.storage / Float32Array.BYTES_PER_ELEMENT;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tgl.bufferSubData( gl.UNIFORM_BUFFER, offset, uniform.__data );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tgl.bindBuffer( gl.UNIFORM_BUFFER, null );\n\n\t}\n\n\tfunction hasUniformChanged( uniform, index, indexArray, cache ) {\n\n\t\tconst value = uniform.value;\n\t\tconst indexString = index + '_' + indexArray;\n\n\t\tif ( cache[ indexString ] === undefined ) {\n\n\t\t\t// cache entry does not exist so far\n\n\t\t\tif ( typeof value === 'number' || typeof value === 'boolean' ) {\n\n\t\t\t\tcache[ indexString ] = value;\n\n\t\t\t} else {\n\n\t\t\t\tcache[ indexString ] = value.clone();\n\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t} else {\n\n\t\t\tconst cachedObject = cache[ indexString ];\n\n\t\t\t// compare current value with cached entry\n\n\t\t\tif ( typeof value === 'number' || typeof value === 'boolean' ) {\n\n\t\t\t\tif ( cachedObject !== value ) {\n\n\t\t\t\t\tcache[ indexString ] = value;\n\t\t\t\t\treturn true;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tif ( cachedObject.equals( value ) === false ) {\n\n\t\t\t\t\tcachedObject.copy( value );\n\t\t\t\t\treturn true;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\tfunction prepareUniformsGroup( uniformsGroup ) {\n\n\t\t// determine total buffer size according to the STD140 layout\n\t\t// Hint: STD140 is the only supported layout in WebGL 2\n\n\t\tconst uniforms = uniformsGroup.uniforms;\n\n\t\tlet offset = 0; // global buffer offset in bytes\n\t\tconst chunkSize = 16; // size of a chunk in bytes\n\n\t\tfor ( let i = 0, l = uniforms.length; i < l; i ++ ) {\n\n\t\t\tconst uniformArray = Array.isArray( uniforms[ i ] ) ? uniforms[ i ] : [ uniforms[ i ] ];\n\n\t\t\tfor ( let j = 0, jl = uniformArray.length; j < jl; j ++ ) {\n\n\t\t\t\tconst uniform = uniformArray[ j ];\n\n\t\t\t\tconst values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ];\n\n\t\t\t\tfor ( let k = 0, kl = values.length; k < kl; k ++ ) {\n\n\t\t\t\t\tconst value = values[ k ];\n\n\t\t\t\t\tconst info = getUniformSize( value );\n\n\t\t\t\t\t// Calculate the chunk offset\n\t\t\t\t\tconst chunkOffsetUniform = offset % chunkSize;\n\n\t\t\t\t\t// Check for chunk overflow\n\t\t\t\t\tif ( chunkOffsetUniform !== 0 && ( chunkSize - chunkOffsetUniform ) < info.boundary ) {\n\n\t\t\t\t\t\t// Add padding and adjust offset\n\t\t\t\t\t\toffset += ( chunkSize - chunkOffsetUniform );\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// the following two properties will be used for partial buffer updates\n\n\t\t\t\t\tuniform.__data = new Float32Array( info.storage / Float32Array.BYTES_PER_ELEMENT );\n\t\t\t\t\tuniform.__offset = offset;\n\n\n\t\t\t\t\t// Update the global offset\n\t\t\t\t\toffset += info.storage;\n\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// ensure correct final padding\n\n\t\tconst chunkOffset = offset % chunkSize;\n\n\t\tif ( chunkOffset > 0 ) offset += ( chunkSize - chunkOffset );\n\n\t\t//\n\n\t\tuniformsGroup.__size = offset;\n\t\tuniformsGroup.__cache = {};\n\n\t\treturn this;\n\n\t}\n\n\tfunction getUniformSize( value ) {\n\n\t\tconst info = {\n\t\t\tboundary: 0, // bytes\n\t\t\tstorage: 0 // bytes\n\t\t};\n\n\t\t// determine sizes according to STD140\n\n\t\tif ( typeof value === 'number' || typeof value === 'boolean' ) {\n\n\t\t\t// float/int/bool\n\n\t\t\tinfo.boundary = 4;\n\t\t\tinfo.storage = 4;\n\n\t\t} else if ( value.isVector2 ) {\n\n\t\t\t// vec2\n\n\t\t\tinfo.boundary = 8;\n\t\t\tinfo.storage = 8;\n\n\t\t} else if ( value.isVector3 || value.isColor ) {\n\n\t\t\t// vec3\n\n\t\t\tinfo.boundary = 16;\n\t\t\tinfo.storage = 12; // evil: vec3 must start on a 16-byte boundary but it only consumes 12 bytes\n\n\t\t} else if ( value.isVector4 ) {\n\n\t\t\t// vec4\n\n\t\t\tinfo.boundary = 16;\n\t\t\tinfo.storage = 16;\n\n\t\t} else if ( value.isMatrix3 ) {\n\n\t\t\t// mat3 (in STD140 a 3x3 matrix is represented as 3x4)\n\n\t\t\tinfo.boundary = 48;\n\t\t\tinfo.storage = 48;\n\n\t\t} else if ( value.isMatrix4 ) {\n\n\t\t\t// mat4\n\n\t\t\tinfo.boundary = 64;\n\t\t\tinfo.storage = 64;\n\n\t\t} else if ( value.isTexture ) {\n\n\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group.' );\n\n\t\t} else {\n\n\t\t\tconsole.warn( 'THREE.WebGLRenderer: Unsupported uniform value type.', value );\n\n\t\t}\n\n\t\treturn info;\n\n\t}\n\n\tfunction onUniformsGroupsDispose( event ) {\n\n\t\tconst uniformsGroup = event.target;\n\n\t\tuniformsGroup.removeEventListener( 'dispose', onUniformsGroupsDispose );\n\n\t\tconst index = allocatedBindingPoints.indexOf( uniformsGroup.__bindingPointIndex );\n\t\tallocatedBindingPoints.splice( index, 1 );\n\n\t\tgl.deleteBuffer( buffers[ uniformsGroup.id ] );\n\n\t\tdelete buffers[ uniformsGroup.id ];\n\t\tdelete updateList[ uniformsGroup.id ];\n\n\t}\n\n\tfunction dispose() {\n\n\t\tfor ( const id in buffers ) {\n\n\t\t\tgl.deleteBuffer( buffers[ id ] );\n\n\t\t}\n\n\t\tallocatedBindingPoints = [];\n\t\tbuffers = {};\n\t\tupdateList = {};\n\n\t}\n\n\treturn {\n\n\t\tbind: bind,\n\t\tupdate: update,\n\n\t\tdispose: dispose\n\n\t};\n\n}\n\nclass WebGLRenderer {\n\n\tconstructor( parameters = {} ) {\n\n\t\tconst {\n\t\t\tcanvas = createCanvasElement(),\n\t\t\tcontext = null,\n\t\t\tdepth = true,\n\t\t\tstencil = true,\n\t\t\talpha = false,\n\t\t\tantialias = false,\n\t\t\tpremultipliedAlpha = true,\n\t\t\tpreserveDrawingBuffer = false,\n\t\t\tpowerPreference = 'default',\n\t\t\tfailIfMajorPerformanceCaveat = false,\n\t\t} = parameters;\n\n\t\tthis.isWebGLRenderer = true;\n\n\t\tlet _alpha;\n\n\t\tif ( context !== null ) {\n\n\t\t\t_alpha = context.getContextAttributes().alpha;\n\n\t\t} else {\n\n\t\t\t_alpha = alpha;\n\n\t\t}\n\n\t\tconst uintClearColor = new Uint32Array( 4 );\n\t\tconst intClearColor = new Int32Array( 4 );\n\n\t\tlet currentRenderList = null;\n\t\tlet currentRenderState = null;\n\n\t\t// render() can be called from within a callback triggered by another render.\n\t\t// We track this so that the nested render call gets its list and state isolated from the parent render call.\n\n\t\tconst renderListStack = [];\n\t\tconst renderStateStack = [];\n\n\t\t// public properties\n\n\t\tthis.domElement = canvas;\n\n\t\t// Debug configuration container\n\t\tthis.debug = {\n\n\t\t\t/**\n\t\t\t * Enables error checking and reporting when shader programs are being compiled\n\t\t\t * @type {boolean}\n\t\t\t */\n\t\t\tcheckShaderErrors: true,\n\t\t\t/**\n\t\t\t * Callback for custom error reporting.\n\t\t\t * @type {?Function}\n\t\t\t */\n\t\t\tonShaderError: null\n\t\t};\n\n\t\t// clearing\n\n\t\tthis.autoClear = true;\n\t\tthis.autoClearColor = true;\n\t\tthis.autoClearDepth = true;\n\t\tthis.autoClearStencil = true;\n\n\t\t// scene graph\n\n\t\tthis.sortObjects = true;\n\n\t\t// user-defined clipping\n\n\t\tthis.clippingPlanes = [];\n\t\tthis.localClippingEnabled = false;\n\n\t\t// physically based shading\n\n\t\tthis._outputColorSpace = SRGBColorSpace;\n\n\t\t// physical lights\n\n\t\tthis._useLegacyLights = false;\n\n\t\t// tone mapping\n\n\t\tthis.toneMapping = NoToneMapping;\n\t\tthis.toneMappingExposure = 1.0;\n\n\t\t// internal properties\n\n\t\tconst _this = this;\n\n\t\tlet _isContextLost = false;\n\n\t\t// internal state cache\n\n\t\tlet _currentActiveCubeFace = 0;\n\t\tlet _currentActiveMipmapLevel = 0;\n\t\tlet _currentRenderTarget = null;\n\t\tlet _currentMaterialId = - 1;\n\n\t\tlet _currentCamera = null;\n\n\t\tconst _currentViewport = new Vector4();\n\t\tconst _currentScissor = new Vector4();\n\t\tlet _currentScissorTest = null;\n\n\t\tconst _currentClearColor = new Color( 0x000000 );\n\t\tlet _currentClearAlpha = 0;\n\n\t\t//\n\n\t\tlet _width = canvas.width;\n\t\tlet _height = canvas.height;\n\n\t\tlet _pixelRatio = 1;\n\t\tlet _opaqueSort = null;\n\t\tlet _transparentSort = null;\n\n\t\tconst _viewport = new Vector4( 0, 0, _width, _height );\n\t\tconst _scissor = new Vector4( 0, 0, _width, _height );\n\t\tlet _scissorTest = false;\n\n\t\t// frustum\n\n\t\tconst _frustum = new Frustum();\n\n\t\t// clipping\n\n\t\tlet _clippingEnabled = false;\n\t\tlet _localClippingEnabled = false;\n\n\t\t// transmission\n\n\t\tlet _transmissionRenderTarget = null;\n\n\t\t// camera matrices cache\n\n\t\tconst _projScreenMatrix = new Matrix4();\n\n\t\tconst _vector2 = new Vector2();\n\t\tconst _vector3 = new Vector3();\n\n\t\tconst _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true };\n\n\t\tfunction getTargetPixelRatio() {\n\n\t\t\treturn _currentRenderTarget === null ? _pixelRatio : 1;\n\n\t\t}\n\n\t\t// initialize\n\n\t\tlet _gl = context;\n\n\t\tfunction getContext( contextNames, contextAttributes ) {\n\n\t\t\tfor ( let i = 0; i < contextNames.length; i ++ ) {\n\n\t\t\t\tconst contextName = contextNames[ i ];\n\t\t\t\tconst context = canvas.getContext( contextName, contextAttributes );\n\t\t\t\tif ( context !== null ) return context;\n\n\t\t\t}\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\ttry {\n\n\t\t\tconst contextAttributes = {\n\t\t\t\talpha: true,\n\t\t\t\tdepth,\n\t\t\t\tstencil,\n\t\t\t\tantialias,\n\t\t\t\tpremultipliedAlpha,\n\t\t\t\tpreserveDrawingBuffer,\n\t\t\t\tpowerPreference,\n\t\t\t\tfailIfMajorPerformanceCaveat,\n\t\t\t};\n\n\t\t\t// OffscreenCanvas does not have setAttribute, see #22811\n\t\t\tif ( 'setAttribute' in canvas ) canvas.setAttribute( 'data-engine', `three.js r${REVISION}` );\n\n\t\t\t// event listeners must be registered before WebGL context is created, see #12753\n\t\t\tcanvas.addEventListener( 'webglcontextlost', onContextLost, false );\n\t\t\tcanvas.addEventListener( 'webglcontextrestored', onContextRestore, false );\n\t\t\tcanvas.addEventListener( 'webglcontextcreationerror', onContextCreationError, false );\n\n\t\t\tif ( _gl === null ) {\n\n\t\t\t\tconst contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ];\n\n\t\t\t\tif ( _this.isWebGL1Renderer === true ) {\n\n\t\t\t\t\tcontextNames.shift();\n\n\t\t\t\t}\n\n\t\t\t\t_gl = getContext( contextNames, contextAttributes );\n\n\t\t\t\tif ( _gl === null ) {\n\n\t\t\t\t\tif ( getContext( contextNames ) ) {\n\n\t\t\t\t\t\tthrow new Error( 'Error creating WebGL context with your selected attributes.' );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tthrow new Error( 'Error creating WebGL context.' );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( typeof WebGLRenderingContext !== 'undefined' && _gl instanceof WebGLRenderingContext ) { // @deprecated, r153\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: WebGL 1 support was deprecated in r153 and will be removed in r163.' );\n\n\t\t\t}\n\n\t\t\t// Some experimental-webgl implementations do not have getShaderPrecisionFormat\n\n\t\t\tif ( _gl.getShaderPrecisionFormat === undefined ) {\n\n\t\t\t\t_gl.getShaderPrecisionFormat = function () {\n\n\t\t\t\t\treturn { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 };\n\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLRenderer: ' + error.message );\n\t\t\tthrow error;\n\n\t\t}\n\n\t\tlet extensions, capabilities, state, info;\n\t\tlet properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects;\n\t\tlet programCache, materials, renderLists, renderStates, clipping, shadowMap;\n\n\t\tlet background, morphtargets, bufferRenderer, indexedBufferRenderer;\n\n\t\tlet utils, bindingStates, uniformsGroups;\n\n\t\tfunction initGLContext() {\n\n\t\t\textensions = new WebGLExtensions( _gl );\n\n\t\t\tcapabilities = new WebGLCapabilities( _gl, extensions, parameters );\n\n\t\t\textensions.init( capabilities );\n\n\t\t\tutils = new WebGLUtils( _gl, extensions, capabilities );\n\n\t\t\tstate = new WebGLState( _gl, extensions, capabilities );\n\n\t\t\tinfo = new WebGLInfo( _gl );\n\t\t\tproperties = new WebGLProperties();\n\t\t\ttextures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );\n\t\t\tcubemaps = new WebGLCubeMaps( _this );\n\t\t\tcubeuvmaps = new WebGLCubeUVMaps( _this );\n\t\t\tattributes = new WebGLAttributes( _gl, capabilities );\n\t\t\tbindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities );\n\t\t\tgeometries = new WebGLGeometries( _gl, attributes, info, bindingStates );\n\t\t\tobjects = new WebGLObjects( _gl, geometries, attributes, info );\n\t\t\tmorphtargets = new WebGLMorphtargets( _gl, capabilities, textures );\n\t\t\tclipping = new WebGLClipping( properties );\n\t\t\tprogramCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping );\n\t\t\tmaterials = new WebGLMaterials( _this, properties );\n\t\t\trenderLists = new WebGLRenderLists();\n\t\t\trenderStates = new WebGLRenderStates( extensions, capabilities );\n\t\t\tbackground = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha );\n\t\t\tshadowMap = new WebGLShadowMap( _this, objects, capabilities );\n\t\t\tuniformsGroups = new WebGLUniformsGroups( _gl, info, capabilities, state );\n\n\t\t\tbufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities );\n\t\t\tindexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities );\n\n\t\t\tinfo.programs = programCache.programs;\n\n\t\t\t_this.capabilities = capabilities;\n\t\t\t_this.extensions = extensions;\n\t\t\t_this.properties = properties;\n\t\t\t_this.renderLists = renderLists;\n\t\t\t_this.shadowMap = shadowMap;\n\t\t\t_this.state = state;\n\t\t\t_this.info = info;\n\n\t\t}\n\n\t\tinitGLContext();\n\n\t\t// xr\n\n\t\tconst xr = new WebXRManager( _this, _gl );\n\n\t\tthis.xr = xr;\n\n\t\t// API\n\n\t\tthis.getContext = function () {\n\n\t\t\treturn _gl;\n\n\t\t};\n\n\t\tthis.getContextAttributes = function () {\n\n\t\t\treturn _gl.getContextAttributes();\n\n\t\t};\n\n\t\tthis.forceContextLoss = function () {\n\n\t\t\tconst extension = extensions.get( 'WEBGL_lose_context' );\n\t\t\tif ( extension ) extension.loseContext();\n\n\t\t};\n\n\t\tthis.forceContextRestore = function () {\n\n\t\t\tconst extension = extensions.get( 'WEBGL_lose_context' );\n\t\t\tif ( extension ) extension.restoreContext();\n\n\t\t};\n\n\t\tthis.getPixelRatio = function () {\n\n\t\t\treturn _pixelRatio;\n\n\t\t};\n\n\t\tthis.setPixelRatio = function ( value ) {\n\n\t\t\tif ( value === undefined ) return;\n\n\t\t\t_pixelRatio = value;\n\n\t\t\tthis.setSize( _width, _height, false );\n\n\t\t};\n\n\t\tthis.getSize = function ( target ) {\n\n\t\t\treturn target.set( _width, _height );\n\n\t\t};\n\n\t\tthis.setSize = function ( width, height, updateStyle = true ) {\n\n\t\t\tif ( xr.isPresenting ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Can\\'t change size while VR device is presenting.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\t_width = width;\n\t\t\t_height = height;\n\n\t\t\tcanvas.width = Math.floor( width * _pixelRatio );\n\t\t\tcanvas.height = Math.floor( height * _pixelRatio );\n\n\t\t\tif ( updateStyle === true ) {\n\n\t\t\t\tcanvas.style.width = width + 'px';\n\t\t\t\tcanvas.style.height = height + 'px';\n\n\t\t\t}\n\n\t\t\tthis.setViewport( 0, 0, width, height );\n\n\t\t};\n\n\t\tthis.getDrawingBufferSize = function ( target ) {\n\n\t\t\treturn target.set( _width * _pixelRatio, _height * _pixelRatio ).floor();\n\n\t\t};\n\n\t\tthis.setDrawingBufferSize = function ( width, height, pixelRatio ) {\n\n\t\t\t_width = width;\n\t\t\t_height = height;\n\n\t\t\t_pixelRatio = pixelRatio;\n\n\t\t\tcanvas.width = Math.floor( width * pixelRatio );\n\t\t\tcanvas.height = Math.floor( height * pixelRatio );\n\n\t\t\tthis.setViewport( 0, 0, width, height );\n\n\t\t};\n\n\t\tthis.getCurrentViewport = function ( target ) {\n\n\t\t\treturn target.copy( _currentViewport );\n\n\t\t};\n\n\t\tthis.getViewport = function ( target ) {\n\n\t\t\treturn target.copy( _viewport );\n\n\t\t};\n\n\t\tthis.setViewport = function ( x, y, width, height ) {\n\n\t\t\tif ( x.isVector4 ) {\n\n\t\t\t\t_viewport.set( x.x, x.y, x.z, x.w );\n\n\t\t\t} else {\n\n\t\t\t\t_viewport.set( x, y, width, height );\n\n\t\t\t}\n\n\t\t\tstate.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );\n\n\t\t};\n\n\t\tthis.getScissor = function ( target ) {\n\n\t\t\treturn target.copy( _scissor );\n\n\t\t};\n\n\t\tthis.setScissor = function ( x, y, width, height ) {\n\n\t\t\tif ( x.isVector4 ) {\n\n\t\t\t\t_scissor.set( x.x, x.y, x.z, x.w );\n\n\t\t\t} else {\n\n\t\t\t\t_scissor.set( x, y, width, height );\n\n\t\t\t}\n\n\t\t\tstate.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );\n\n\t\t};\n\n\t\tthis.getScissorTest = function () {\n\n\t\t\treturn _scissorTest;\n\n\t\t};\n\n\t\tthis.setScissorTest = function ( boolean ) {\n\n\t\t\tstate.setScissorTest( _scissorTest = boolean );\n\n\t\t};\n\n\t\tthis.setOpaqueSort = function ( method ) {\n\n\t\t\t_opaqueSort = method;\n\n\t\t};\n\n\t\tthis.setTransparentSort = function ( method ) {\n\n\t\t\t_transparentSort = method;\n\n\t\t};\n\n\t\t// Clearing\n\n\t\tthis.getClearColor = function ( target ) {\n\n\t\t\treturn target.copy( background.getClearColor() );\n\n\t\t};\n\n\t\tthis.setClearColor = function () {\n\n\t\t\tbackground.setClearColor.apply( background, arguments );\n\n\t\t};\n\n\t\tthis.getClearAlpha = function () {\n\n\t\t\treturn background.getClearAlpha();\n\n\t\t};\n\n\t\tthis.setClearAlpha = function () {\n\n\t\t\tbackground.setClearAlpha.apply( background, arguments );\n\n\t\t};\n\n\t\tthis.clear = function ( color = true, depth = true, stencil = true ) {\n\n\t\t\tlet bits = 0;\n\n\t\t\tif ( color ) {\n\n\t\t\t\t// check if we're trying to clear an integer target\n\t\t\t\tlet isIntegerFormat = false;\n\t\t\t\tif ( _currentRenderTarget !== null ) {\n\n\t\t\t\t\tconst targetFormat = _currentRenderTarget.texture.format;\n\t\t\t\t\tisIntegerFormat = targetFormat === RGBAIntegerFormat ||\n\t\t\t\t\t\ttargetFormat === RGIntegerFormat ||\n\t\t\t\t\t\ttargetFormat === RedIntegerFormat;\n\n\t\t\t\t}\n\n\t\t\t\t// use the appropriate clear functions to clear the target if it's a signed\n\t\t\t\t// or unsigned integer target\n\t\t\t\tif ( isIntegerFormat ) {\n\n\t\t\t\t\tconst targetType = _currentRenderTarget.texture.type;\n\t\t\t\t\tconst isUnsignedType = targetType === UnsignedByteType ||\n\t\t\t\t\t\ttargetType === UnsignedIntType ||\n\t\t\t\t\t\ttargetType === UnsignedShortType ||\n\t\t\t\t\t\ttargetType === UnsignedInt248Type ||\n\t\t\t\t\t\ttargetType === UnsignedShort4444Type ||\n\t\t\t\t\t\ttargetType === UnsignedShort5551Type;\n\n\t\t\t\t\tconst clearColor = background.getClearColor();\n\t\t\t\t\tconst a = background.getClearAlpha();\n\t\t\t\t\tconst r = clearColor.r;\n\t\t\t\t\tconst g = clearColor.g;\n\t\t\t\t\tconst b = clearColor.b;\n\n\t\t\t\t\tif ( isUnsignedType ) {\n\n\t\t\t\t\t\tuintClearColor[ 0 ] = r;\n\t\t\t\t\t\tuintClearColor[ 1 ] = g;\n\t\t\t\t\t\tuintClearColor[ 2 ] = b;\n\t\t\t\t\t\tuintClearColor[ 3 ] = a;\n\t\t\t\t\t\t_gl.clearBufferuiv( _gl.COLOR, 0, uintClearColor );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tintClearColor[ 0 ] = r;\n\t\t\t\t\t\tintClearColor[ 1 ] = g;\n\t\t\t\t\t\tintClearColor[ 2 ] = b;\n\t\t\t\t\t\tintClearColor[ 3 ] = a;\n\t\t\t\t\t\t_gl.clearBufferiv( _gl.COLOR, 0, intClearColor );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tbits |= _gl.COLOR_BUFFER_BIT;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( depth ) bits |= _gl.DEPTH_BUFFER_BIT;\n\t\t\tif ( stencil ) {\n\n\t\t\t\tbits |= _gl.STENCIL_BUFFER_BIT;\n\t\t\t\tthis.state.buffers.stencil.setMask( 0xffffffff );\n\n\t\t\t}\n\n\t\t\t_gl.clear( bits );\n\n\t\t};\n\n\t\tthis.clearColor = function () {\n\n\t\t\tthis.clear( true, false, false );\n\n\t\t};\n\n\t\tthis.clearDepth = function () {\n\n\t\t\tthis.clear( false, true, false );\n\n\t\t};\n\n\t\tthis.clearStencil = function () {\n\n\t\t\tthis.clear( false, false, true );\n\n\t\t};\n\n\t\t//\n\n\t\tthis.dispose = function () {\n\n\t\t\tcanvas.removeEventListener( 'webglcontextlost', onContextLost, false );\n\t\t\tcanvas.removeEventListener( 'webglcontextrestored', onContextRestore, false );\n\t\t\tcanvas.removeEventListener( 'webglcontextcreationerror', onContextCreationError, false );\n\n\t\t\trenderLists.dispose();\n\t\t\trenderStates.dispose();\n\t\t\tproperties.dispose();\n\t\t\tcubemaps.dispose();\n\t\t\tcubeuvmaps.dispose();\n\t\t\tobjects.dispose();\n\t\t\tbindingStates.dispose();\n\t\t\tuniformsGroups.dispose();\n\t\t\tprogramCache.dispose();\n\n\t\t\txr.dispose();\n\n\t\t\txr.removeEventListener( 'sessionstart', onXRSessionStart );\n\t\t\txr.removeEventListener( 'sessionend', onXRSessionEnd );\n\n\t\t\tif ( _transmissionRenderTarget ) {\n\n\t\t\t\t_transmissionRenderTarget.dispose();\n\t\t\t\t_transmissionRenderTarget = null;\n\n\t\t\t}\n\n\t\t\tanimation.stop();\n\n\t\t};\n\n\t\t// Events\n\n\t\tfunction onContextLost( event ) {\n\n\t\t\tevent.preventDefault();\n\n\t\t\tconsole.log( 'THREE.WebGLRenderer: Context Lost.' );\n\n\t\t\t_isContextLost = true;\n\n\t\t}\n\n\t\tfunction onContextRestore( /* event */ ) {\n\n\t\t\tconsole.log( 'THREE.WebGLRenderer: Context Restored.' );\n\n\t\t\t_isContextLost = false;\n\n\t\t\tconst infoAutoReset = info.autoReset;\n\t\t\tconst shadowMapEnabled = shadowMap.enabled;\n\t\t\tconst shadowMapAutoUpdate = shadowMap.autoUpdate;\n\t\t\tconst shadowMapNeedsUpdate = shadowMap.needsUpdate;\n\t\t\tconst shadowMapType = shadowMap.type;\n\n\t\t\tinitGLContext();\n\n\t\t\tinfo.autoReset = infoAutoReset;\n\t\t\tshadowMap.enabled = shadowMapEnabled;\n\t\t\tshadowMap.autoUpdate = shadowMapAutoUpdate;\n\t\t\tshadowMap.needsUpdate = shadowMapNeedsUpdate;\n\t\t\tshadowMap.type = shadowMapType;\n\n\t\t}\n\n\t\tfunction onContextCreationError( event ) {\n\n\t\t\tconsole.error( 'THREE.WebGLRenderer: A WebGL context could not be created. Reason: ', event.statusMessage );\n\n\t\t}\n\n\t\tfunction onMaterialDispose( event ) {\n\n\t\t\tconst material = event.target;\n\n\t\t\tmaterial.removeEventListener( 'dispose', onMaterialDispose );\n\n\t\t\tdeallocateMaterial( material );\n\n\t\t}\n\n\t\t// Buffer deallocation\n\n\t\tfunction deallocateMaterial( material ) {\n\n\t\t\treleaseMaterialProgramReferences( material );\n\n\t\t\tproperties.remove( material );\n\n\t\t}\n\n\n\t\tfunction releaseMaterialProgramReferences( material ) {\n\n\t\t\tconst programs = properties.get( material ).programs;\n\n\t\t\tif ( programs !== undefined ) {\n\n\t\t\t\tprograms.forEach( function ( program ) {\n\n\t\t\t\t\tprogramCache.releaseProgram( program );\n\n\t\t\t\t} );\n\n\t\t\t\tif ( material.isShaderMaterial ) {\n\n\t\t\t\t\tprogramCache.releaseShaderCache( material );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Buffer rendering\n\n\t\tthis.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {\n\n\t\t\tif ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null)\n\n\t\t\tconst frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );\n\n\t\t\tconst program = setProgram( camera, scene, geometry, material, object );\n\n\t\t\tstate.setMaterial( material, frontFaceCW );\n\n\t\t\t//\n\n\t\t\tlet index = geometry.index;\n\t\t\tlet rangeFactor = 1;\n\n\t\t\tif ( material.wireframe === true ) {\n\n\t\t\t\tindex = geometries.getWireframeAttribute( geometry );\n\n\t\t\t\tif ( index === undefined ) return;\n\n\t\t\t\trangeFactor = 2;\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tconst drawRange = geometry.drawRange;\n\t\t\tconst position = geometry.attributes.position;\n\n\t\t\tlet drawStart = drawRange.start * rangeFactor;\n\t\t\tlet drawEnd = ( drawRange.start + drawRange.count ) * rangeFactor;\n\n\t\t\tif ( group !== null ) {\n\n\t\t\t\tdrawStart = Math.max( drawStart, group.start * rangeFactor );\n\t\t\t\tdrawEnd = Math.min( drawEnd, ( group.start + group.count ) * rangeFactor );\n\n\t\t\t}\n\n\t\t\tif ( index !== null ) {\n\n\t\t\t\tdrawStart = Math.max( drawStart, 0 );\n\t\t\t\tdrawEnd = Math.min( drawEnd, index.count );\n\n\t\t\t} else if ( position !== undefined && position !== null ) {\n\n\t\t\t\tdrawStart = Math.max( drawStart, 0 );\n\t\t\t\tdrawEnd = Math.min( drawEnd, position.count );\n\n\t\t\t}\n\n\t\t\tconst drawCount = drawEnd - drawStart;\n\n\t\t\tif ( drawCount < 0 || drawCount === Infinity ) return;\n\n\t\t\t//\n\n\t\t\tbindingStates.setup( object, material, program, geometry, index );\n\n\t\t\tlet attribute;\n\t\t\tlet renderer = bufferRenderer;\n\n\t\t\tif ( index !== null ) {\n\n\t\t\t\tattribute = attributes.get( index );\n\n\t\t\t\trenderer = indexedBufferRenderer;\n\t\t\t\trenderer.setIndex( attribute );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tif ( object.isMesh ) {\n\n\t\t\t\tif ( material.wireframe === true ) {\n\n\t\t\t\t\tstate.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() );\n\t\t\t\t\trenderer.setMode( _gl.LINES );\n\n\t\t\t\t} else {\n\n\t\t\t\t\trenderer.setMode( _gl.TRIANGLES );\n\n\t\t\t\t}\n\n\t\t\t} else if ( object.isLine ) {\n\n\t\t\t\tlet lineWidth = material.linewidth;\n\n\t\t\t\tif ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material\n\n\t\t\t\tstate.setLineWidth( lineWidth * getTargetPixelRatio() );\n\n\t\t\t\tif ( object.isLineSegments ) {\n\n\t\t\t\t\trenderer.setMode( _gl.LINES );\n\n\t\t\t\t} else if ( object.isLineLoop ) {\n\n\t\t\t\t\trenderer.setMode( _gl.LINE_LOOP );\n\n\t\t\t\t} else {\n\n\t\t\t\t\trenderer.setMode( _gl.LINE_STRIP );\n\n\t\t\t\t}\n\n\t\t\t} else if ( object.isPoints ) {\n\n\t\t\t\trenderer.setMode( _gl.POINTS );\n\n\t\t\t} else if ( object.isSprite ) {\n\n\t\t\t\trenderer.setMode( _gl.TRIANGLES );\n\n\t\t\t}\n\n\t\t\tif ( object.isBatchedMesh ) {\n\n\t\t\t\trenderer.renderMultiDraw( object._multiDrawStarts, object._multiDrawCounts, object._multiDrawCount );\n\n\t\t\t} else if ( object.isInstancedMesh ) {\n\n\t\t\t\trenderer.renderInstances( drawStart, drawCount, object.count );\n\n\t\t\t} else if ( geometry.isInstancedBufferGeometry ) {\n\n\t\t\t\tconst maxInstanceCount = geometry._maxInstanceCount !== undefined ? geometry._maxInstanceCount : Infinity;\n\t\t\t\tconst instanceCount = Math.min( geometry.instanceCount, maxInstanceCount );\n\n\t\t\t\trenderer.renderInstances( drawStart, drawCount, instanceCount );\n\n\t\t\t} else {\n\n\t\t\t\trenderer.render( drawStart, drawCount );\n\n\t\t\t}\n\n\t\t};\n\n\t\t// Compile\n\n\t\tfunction prepareMaterial( material, scene, object ) {\n\n\t\t\tif ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) {\n\n\t\t\t\tmaterial.side = BackSide;\n\t\t\t\tmaterial.needsUpdate = true;\n\t\t\t\tgetProgram( material, scene, object );\n\n\t\t\t\tmaterial.side = FrontSide;\n\t\t\t\tmaterial.needsUpdate = true;\n\t\t\t\tgetProgram( material, scene, object );\n\n\t\t\t\tmaterial.side = DoubleSide;\n\n\t\t\t} else {\n\n\t\t\t\tgetProgram( material, scene, object );\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.compile = function ( scene, camera, targetScene = null ) {\n\n\t\t\tif ( targetScene === null ) targetScene = scene;\n\n\t\t\tcurrentRenderState = renderStates.get( targetScene );\n\t\t\tcurrentRenderState.init();\n\n\t\t\trenderStateStack.push( currentRenderState );\n\n\t\t\t// gather lights from both the target scene and the new object that will be added to the scene.\n\n\t\t\ttargetScene.traverseVisible( function ( object ) {\n\n\t\t\t\tif ( object.isLight && object.layers.test( camera.layers ) ) {\n\n\t\t\t\t\tcurrentRenderState.pushLight( object );\n\n\t\t\t\t\tif ( object.castShadow ) {\n\n\t\t\t\t\t\tcurrentRenderState.pushShadow( object );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} );\n\n\t\t\tif ( scene !== targetScene ) {\n\n\t\t\t\tscene.traverseVisible( function ( object ) {\n\n\t\t\t\t\tif ( object.isLight && object.layers.test( camera.layers ) ) {\n\n\t\t\t\t\t\tcurrentRenderState.pushLight( object );\n\n\t\t\t\t\t\tif ( object.castShadow ) {\n\n\t\t\t\t\t\t\tcurrentRenderState.pushShadow( object );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t\tcurrentRenderState.setupLights( _this._useLegacyLights );\n\n\t\t\t// Only initialize materials in the new scene, not the targetScene.\n\n\t\t\tconst materials = new Set();\n\n\t\t\tscene.traverse( function ( object ) {\n\n\t\t\t\tconst material = object.material;\n\n\t\t\t\tif ( material ) {\n\n\t\t\t\t\tif ( Array.isArray( material ) ) {\n\n\t\t\t\t\t\tfor ( let i = 0; i < material.length; i ++ ) {\n\n\t\t\t\t\t\t\tconst material2 = material[ i ];\n\n\t\t\t\t\t\t\tprepareMaterial( material2, targetScene, object );\n\t\t\t\t\t\t\tmaterials.add( material2 );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tprepareMaterial( material, targetScene, object );\n\t\t\t\t\t\tmaterials.add( material );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} );\n\n\t\t\trenderStateStack.pop();\n\t\t\tcurrentRenderState = null;\n\n\t\t\treturn materials;\n\n\t\t};\n\n\t\t// compileAsync\n\n\t\tthis.compileAsync = function ( scene, camera, targetScene = null ) {\n\n\t\t\tconst materials = this.compile( scene, camera, targetScene );\n\n\t\t\t// Wait for all the materials in the new object to indicate that they're\n\t\t\t// ready to be used before resolving the promise.\n\n\t\t\treturn new Promise( ( resolve ) => {\n\n\t\t\t\tfunction checkMaterialsReady() {\n\n\t\t\t\t\tmaterials.forEach( function ( material ) {\n\n\t\t\t\t\t\tconst materialProperties = properties.get( material );\n\t\t\t\t\t\tconst program = materialProperties.currentProgram;\n\n\t\t\t\t\t\tif ( program.isReady() ) {\n\n\t\t\t\t\t\t\t// remove any programs that report they're ready to use from the list\n\t\t\t\t\t\t\tmaterials.delete( material );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} );\n\n\t\t\t\t\t// once the list of compiling materials is empty, call the callback\n\n\t\t\t\t\tif ( materials.size === 0 ) {\n\n\t\t\t\t\t\tresolve( scene );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// if some materials are still not ready, wait a bit and check again\n\n\t\t\t\t\tsetTimeout( checkMaterialsReady, 10 );\n\n\t\t\t\t}\n\n\t\t\t\tif ( extensions.get( 'KHR_parallel_shader_compile' ) !== null ) {\n\n\t\t\t\t\t// If we can check the compilation status of the materials without\n\t\t\t\t\t// blocking then do so right away.\n\n\t\t\t\t\tcheckMaterialsReady();\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// Otherwise start by waiting a bit to give the materials we just\n\t\t\t\t\t// initialized a chance to finish.\n\n\t\t\t\t\tsetTimeout( checkMaterialsReady, 10 );\n\n\t\t\t\t}\n\n\t\t\t} );\n\n\t\t};\n\n\t\t// Animation Loop\n\n\t\tlet onAnimationFrameCallback = null;\n\n\t\tfunction onAnimationFrame( time ) {\n\n\t\t\tif ( onAnimationFrameCallback ) onAnimationFrameCallback( time );\n\n\t\t}\n\n\t\tfunction onXRSessionStart() {\n\n\t\t\tanimation.stop();\n\n\t\t}\n\n\t\tfunction onXRSessionEnd() {\n\n\t\t\tanimation.start();\n\n\t\t}\n\n\t\tconst animation = new WebGLAnimation();\n\t\tanimation.setAnimationLoop( onAnimationFrame );\n\n\t\tif ( typeof self !== 'undefined' ) animation.setContext( self );\n\n\t\tthis.setAnimationLoop = function ( callback ) {\n\n\t\t\tonAnimationFrameCallback = callback;\n\t\t\txr.setAnimationLoop( callback );\n\n\t\t\t( callback === null ) ? animation.stop() : animation.start();\n\n\t\t};\n\n\t\txr.addEventListener( 'sessionstart', onXRSessionStart );\n\t\txr.addEventListener( 'sessionend', onXRSessionEnd );\n\n\t\t// Rendering\n\n\t\tthis.render = function ( scene, camera ) {\n\n\t\t\tif ( camera !== undefined && camera.isCamera !== true ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tif ( _isContextLost === true ) return;\n\n\t\t\t// update scene graph\n\n\t\t\tif ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();\n\n\t\t\t// update camera matrices and frustum\n\n\t\t\tif ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();\n\n\t\t\tif ( xr.enabled === true && xr.isPresenting === true ) {\n\n\t\t\t\tif ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera );\n\n\t\t\t\tcamera = xr.getCamera(); // use XR camera for rendering\n\n\t\t\t}\n\n\t\t\t//\n\t\t\tif ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget );\n\n\t\t\tcurrentRenderState = renderStates.get( scene, renderStateStack.length );\n\t\t\tcurrentRenderState.init();\n\n\t\t\trenderStateStack.push( currentRenderState );\n\n\t\t\t_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );\n\t\t\t_frustum.setFromProjectionMatrix( _projScreenMatrix );\n\n\t\t\t_localClippingEnabled = this.localClippingEnabled;\n\t\t\t_clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled );\n\n\t\t\tcurrentRenderList = renderLists.get( scene, renderListStack.length );\n\t\t\tcurrentRenderList.init();\n\n\t\t\trenderListStack.push( currentRenderList );\n\n\t\t\tprojectObject( scene, camera, 0, _this.sortObjects );\n\n\t\t\tcurrentRenderList.finish();\n\n\t\t\tif ( _this.sortObjects === true ) {\n\n\t\t\t\tcurrentRenderList.sort( _opaqueSort, _transparentSort );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tthis.info.render.frame ++;\n\n\t\t\tif ( _clippingEnabled === true ) clipping.beginShadows();\n\n\t\t\tconst shadowsArray = currentRenderState.state.shadowsArray;\n\n\t\t\tshadowMap.render( shadowsArray, scene, camera );\n\n\t\t\tif ( _clippingEnabled === true ) clipping.endShadows();\n\n\t\t\t//\n\n\t\t\tif ( this.info.autoReset === true ) this.info.reset();\n\n\n\t\t\t//\n\n\t\t\tbackground.render( currentRenderList, scene );\n\n\t\t\t// render scene\n\n\t\t\tcurrentRenderState.setupLights( _this._useLegacyLights );\n\n\t\t\tif ( camera.isArrayCamera ) {\n\n\t\t\t\tconst cameras = camera.cameras;\n\n\t\t\t\tfor ( let i = 0, l = cameras.length; i < l; i ++ ) {\n\n\t\t\t\t\tconst camera2 = cameras[ i ];\n\n\t\t\t\t\trenderScene( currentRenderList, scene, camera2, camera2.viewport );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\trenderScene( currentRenderList, scene, camera );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tif ( _currentRenderTarget !== null ) {\n\n\t\t\t\t// resolve multisample renderbuffers to a single-sample texture if necessary\n\n\t\t\t\ttextures.updateMultisampleRenderTarget( _currentRenderTarget );\n\n\t\t\t\t// Generate mipmap if we're using any kind of mipmap filtering\n\n\t\t\t\ttextures.updateRenderTargetMipmap( _currentRenderTarget );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tif ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera );\n\n\t\t\t// _gl.finish();\n\n\t\t\tbindingStates.resetDefaultState();\n\t\t\t_currentMaterialId = - 1;\n\t\t\t_currentCamera = null;\n\n\t\t\trenderStateStack.pop();\n\n\t\t\tif ( renderStateStack.length > 0 ) {\n\n\t\t\t\tcurrentRenderState = renderStateStack[ renderStateStack.length - 1 ];\n\n\t\t\t} else {\n\n\t\t\t\tcurrentRenderState = null;\n\n\t\t\t}\n\n\t\t\trenderListStack.pop();\n\n\t\t\tif ( renderListStack.length > 0 ) {\n\n\t\t\t\tcurrentRenderList = renderListStack[ renderListStack.length - 1 ];\n\n\t\t\t} else {\n\n\t\t\t\tcurrentRenderList = null;\n\n\t\t\t}\n\n\t\t};\n\n\t\tfunction projectObject( object, camera, groupOrder, sortObjects ) {\n\n\t\t\tif ( object.visible === false ) return;\n\n\t\t\tconst visible = object.layers.test( camera.layers );\n\n\t\t\tif ( visible ) {\n\n\t\t\t\tif ( object.isGroup ) {\n\n\t\t\t\t\tgroupOrder = object.renderOrder;\n\n\t\t\t\t} else if ( object.isLOD ) {\n\n\t\t\t\t\tif ( object.autoUpdate === true ) object.update( camera );\n\n\t\t\t\t} else if ( object.isLight ) {\n\n\t\t\t\t\tcurrentRenderState.pushLight( object );\n\n\t\t\t\t\tif ( object.castShadow ) {\n\n\t\t\t\t\t\tcurrentRenderState.pushShadow( object );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( object.isSprite ) {\n\n\t\t\t\t\tif ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {\n\n\t\t\t\t\t\tif ( sortObjects ) {\n\n\t\t\t\t\t\t\t_vector3.setFromMatrixPosition( object.matrixWorld )\n\t\t\t\t\t\t\t\t.applyMatrix4( _projScreenMatrix );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst geometry = objects.update( object );\n\t\t\t\t\t\tconst material = object.material;\n\n\t\t\t\t\t\tif ( material.visible ) {\n\n\t\t\t\t\t\t\tcurrentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( object.isMesh || object.isLine || object.isPoints ) {\n\n\t\t\t\t\tif ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {\n\n\t\t\t\t\t\tconst geometry = objects.update( object );\n\t\t\t\t\t\tconst material = object.material;\n\n\t\t\t\t\t\tif ( sortObjects ) {\n\n\t\t\t\t\t\t\tif ( object.boundingSphere !== undefined ) {\n\n\t\t\t\t\t\t\t\tif ( object.boundingSphere === null ) object.computeBoundingSphere();\n\t\t\t\t\t\t\t\t_vector3.copy( object.boundingSphere.center );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\t\t\t\t\t\t\t\t_vector3.copy( geometry.boundingSphere.center );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t_vector3\n\t\t\t\t\t\t\t\t.applyMatrix4( object.matrixWorld )\n\t\t\t\t\t\t\t\t.applyMatrix4( _projScreenMatrix );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( Array.isArray( material ) ) {\n\n\t\t\t\t\t\t\tconst groups = geometry.groups;\n\n\t\t\t\t\t\t\tfor ( let i = 0, l = groups.length; i < l; i ++ ) {\n\n\t\t\t\t\t\t\t\tconst group = groups[ i ];\n\t\t\t\t\t\t\t\tconst groupMaterial = material[ group.materialIndex ];\n\n\t\t\t\t\t\t\t\tif ( groupMaterial && groupMaterial.visible ) {\n\n\t\t\t\t\t\t\t\t\tcurrentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else if ( material.visible ) {\n\n\t\t\t\t\t\t\tcurrentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tconst children = object.children;\n\n\t\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\t\tprojectObject( children[ i ], camera, groupOrder, sortObjects );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction renderScene( currentRenderList, scene, camera, viewport ) {\n\n\t\t\tconst opaqueObjects = currentRenderList.opaque;\n\t\t\tconst transmissiveObjects = currentRenderList.transmissive;\n\t\t\tconst transparentObjects = currentRenderList.transparent;\n\n\t\t\tcurrentRenderState.setupLightsView( camera );\n\n\t\t\tif ( _clippingEnabled === true ) clipping.setGlobalState( _this.clippingPlanes, camera );\n\n\t\t\tif ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera );\n\n\t\t\tif ( viewport ) state.viewport( _currentViewport.copy( viewport ) );\n\n\t\t\tif ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera );\n\t\t\tif ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera );\n\t\t\tif ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera );\n\n\t\t\t// Ensure depth buffer writing is enabled so it can be cleared on next render\n\n\t\t\tstate.buffers.depth.setTest( true );\n\t\t\tstate.buffers.depth.setMask( true );\n\t\t\tstate.buffers.color.setMask( true );\n\n\t\t\tstate.setPolygonOffset( false );\n\n\t\t}\n\n\t\tfunction renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ) {\n\n\t\t\tconst overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null;\n\n\t\t\tif ( overrideMaterial !== null ) {\n\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tconst isWebGL2 = capabilities.isWebGL2;\n\n\t\t\tif ( _transmissionRenderTarget === null ) {\n\n\t\t\t\t_transmissionRenderTarget = new WebGLRenderTarget( 1, 1, {\n\t\t\t\t\tgenerateMipmaps: true,\n\t\t\t\t\ttype: extensions.has( 'EXT_color_buffer_half_float' ) ? HalfFloatType : UnsignedByteType,\n\t\t\t\t\tminFilter: LinearMipmapLinearFilter,\n\t\t\t\t\tsamples: ( isWebGL2 ) ? 4 : 0\n\t\t\t\t} );\n\n\t\t\t\t// debug\n\n\t\t\t\t/*\n\t\t\t\tconst geometry = new PlaneGeometry();\n\t\t\t\tconst material = new MeshBasicMaterial( { map: _transmissionRenderTarget.texture } );\n\n\t\t\t\tconst mesh = new Mesh( geometry, material );\n\t\t\t\tscene.add( mesh );\n\t\t\t\t*/\n\n\t\t\t}\n\n\t\t\t_this.getDrawingBufferSize( _vector2 );\n\n\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\t_transmissionRenderTarget.setSize( _vector2.x, _vector2.y );\n\n\t\t\t} else {\n\n\t\t\t\t_transmissionRenderTarget.setSize( floorPowerOfTwo( _vector2.x ), floorPowerOfTwo( _vector2.y ) );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tconst currentRenderTarget = _this.getRenderTarget();\n\t\t\t_this.setRenderTarget( _transmissionRenderTarget );\n\n\t\t\t_this.getClearColor( _currentClearColor );\n\t\t\t_currentClearAlpha = _this.getClearAlpha();\n\t\t\tif ( _currentClearAlpha < 1 ) _this.setClearColor( 0xffffff, 0.5 );\n\n\t\t\t_this.clear();\n\n\t\t\t// Turn off the features which can affect the frag color for opaque objects pass.\n\t\t\t// Otherwise they are applied twice in opaque objects pass and transmission objects pass.\n\t\t\tconst currentToneMapping = _this.toneMapping;\n\t\t\t_this.toneMapping = NoToneMapping;\n\n\t\t\trenderObjects( opaqueObjects, scene, camera );\n\n\t\t\ttextures.updateMultisampleRenderTarget( _transmissionRenderTarget );\n\t\t\ttextures.updateRenderTargetMipmap( _transmissionRenderTarget );\n\n\t\t\tlet renderTargetNeedsUpdate = false;\n\n\t\t\tfor ( let i = 0, l = transmissiveObjects.length; i < l; i ++ ) {\n\n\t\t\t\tconst renderItem = transmissiveObjects[ i ];\n\n\t\t\t\tconst object = renderItem.object;\n\t\t\t\tconst geometry = renderItem.geometry;\n\t\t\t\tconst material = renderItem.material;\n\t\t\t\tconst group = renderItem.group;\n\n\t\t\t\tif ( material.side === DoubleSide && object.layers.test( camera.layers ) ) {\n\n\t\t\t\t\tconst currentSide = material.side;\n\n\t\t\t\t\tmaterial.side = BackSide;\n\t\t\t\t\tmaterial.needsUpdate = true;\n\n\t\t\t\t\trenderObject( object, scene, camera, geometry, material, group );\n\n\t\t\t\t\tmaterial.side = currentSide;\n\t\t\t\t\tmaterial.needsUpdate = true;\n\n\t\t\t\t\trenderTargetNeedsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( renderTargetNeedsUpdate === true ) {\n\n\t\t\t\ttextures.updateMultisampleRenderTarget( _transmissionRenderTarget );\n\t\t\t\ttextures.updateRenderTargetMipmap( _transmissionRenderTarget );\n\n\t\t\t}\n\n\t\t\t_this.setRenderTarget( currentRenderTarget );\n\n\t\t\t_this.setClearColor( _currentClearColor, _currentClearAlpha );\n\n\t\t\t_this.toneMapping = currentToneMapping;\n\n\t\t}\n\n\t\tfunction renderObjects( renderList, scene, camera ) {\n\n\t\t\tconst overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null;\n\n\t\t\tfor ( let i = 0, l = renderList.length; i < l; i ++ ) {\n\n\t\t\t\tconst renderItem = renderList[ i ];\n\n\t\t\t\tconst object = renderItem.object;\n\t\t\t\tconst geometry = renderItem.geometry;\n\t\t\t\tconst material = overrideMaterial === null ? renderItem.material : overrideMaterial;\n\t\t\t\tconst group = renderItem.group;\n\n\t\t\t\tif ( object.layers.test( camera.layers ) ) {\n\n\t\t\t\t\trenderObject( object, scene, camera, geometry, material, group );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction renderObject( object, scene, camera, geometry, material, group ) {\n\n\t\t\tobject.onBeforeRender( _this, scene, camera, geometry, material, group );\n\n\t\t\tobject.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );\n\t\t\tobject.normalMatrix.getNormalMatrix( object.modelViewMatrix );\n\n\t\t\tmaterial.onBeforeRender( _this, scene, camera, geometry, object, group );\n\n\t\t\tif ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) {\n\n\t\t\t\tmaterial.side = BackSide;\n\t\t\t\tmaterial.needsUpdate = true;\n\t\t\t\t_this.renderBufferDirect( camera, scene, geometry, material, object, group );\n\n\t\t\t\tmaterial.side = FrontSide;\n\t\t\t\tmaterial.needsUpdate = true;\n\t\t\t\t_this.renderBufferDirect( camera, scene, geometry, material, object, group );\n\n\t\t\t\tmaterial.side = DoubleSide;\n\n\t\t\t} else {\n\n\t\t\t\t_this.renderBufferDirect( camera, scene, geometry, material, object, group );\n\n\t\t\t}\n\n\t\t\tobject.onAfterRender( _this, scene, camera, geometry, material, group );\n\n\t\t}\n\n\t\tfunction getProgram( material, scene, object ) {\n\n\t\t\tif ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...\n\n\t\t\tconst materialProperties = properties.get( material );\n\n\t\t\tconst lights = currentRenderState.state.lights;\n\t\t\tconst shadowsArray = currentRenderState.state.shadowsArray;\n\n\t\t\tconst lightsStateVersion = lights.state.version;\n\n\t\t\tconst parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object );\n\t\t\tconst programCacheKey = programCache.getProgramCacheKey( parameters );\n\n\t\t\tlet programs = materialProperties.programs;\n\n\t\t\t// always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change\n\n\t\t\tmaterialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null;\n\t\t\tmaterialProperties.fog = scene.fog;\n\t\t\tmaterialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment );\n\n\t\t\tif ( programs === undefined ) {\n\n\t\t\t\t// new material\n\n\t\t\t\tmaterial.addEventListener( 'dispose', onMaterialDispose );\n\n\t\t\t\tprograms = new Map();\n\t\t\t\tmaterialProperties.programs = programs;\n\n\t\t\t}\n\n\t\t\tlet program = programs.get( programCacheKey );\n\n\t\t\tif ( program !== undefined ) {\n\n\t\t\t\t// early out if program and light state is identical\n\n\t\t\t\tif ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) {\n\n\t\t\t\t\tupdateCommonMaterialProperties( material, parameters );\n\n\t\t\t\t\treturn program;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tparameters.uniforms = programCache.getUniforms( material );\n\n\t\t\t\tmaterial.onBuild( object, parameters, _this );\n\n\t\t\t\tmaterial.onBeforeCompile( parameters, _this );\n\n\t\t\t\tprogram = programCache.acquireProgram( parameters, programCacheKey );\n\t\t\t\tprograms.set( programCacheKey, program );\n\n\t\t\t\tmaterialProperties.uniforms = parameters.uniforms;\n\n\t\t\t}\n\n\t\t\tconst uniforms = materialProperties.uniforms;\n\n\t\t\tif ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) {\n\n\t\t\t\tuniforms.clippingPlanes = clipping.uniform;\n\n\t\t\t}\n\n\t\t\tupdateCommonMaterialProperties( material, parameters );\n\n\t\t\t// store the light setup it was created for\n\n\t\t\tmaterialProperties.needsLights = materialNeedsLights( material );\n\t\t\tmaterialProperties.lightsStateVersion = lightsStateVersion;\n\n\t\t\tif ( materialProperties.needsLights ) {\n\n\t\t\t\t// wire up the material to this renderer's lighting state\n\n\t\t\t\tuniforms.ambientLightColor.value = lights.state.ambient;\n\t\t\t\tuniforms.lightProbe.value = lights.state.probe;\n\t\t\t\tuniforms.directionalLights.value = lights.state.directional;\n\t\t\t\tuniforms.directionalLightShadows.value = lights.state.directionalShadow;\n\t\t\t\tuniforms.spotLights.value = lights.state.spot;\n\t\t\t\tuniforms.spotLightShadows.value = lights.state.spotShadow;\n\t\t\t\tuniforms.rectAreaLights.value = lights.state.rectArea;\n\t\t\t\tuniforms.ltc_1.value = lights.state.rectAreaLTC1;\n\t\t\t\tuniforms.ltc_2.value = lights.state.rectAreaLTC2;\n\t\t\t\tuniforms.pointLights.value = lights.state.point;\n\t\t\t\tuniforms.pointLightShadows.value = lights.state.pointShadow;\n\t\t\t\tuniforms.hemisphereLights.value = lights.state.hemi;\n\n\t\t\t\tuniforms.directionalShadowMap.value = lights.state.directionalShadowMap;\n\t\t\t\tuniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix;\n\t\t\t\tuniforms.spotShadowMap.value = lights.state.spotShadowMap;\n\t\t\t\tuniforms.spotLightMatrix.value = lights.state.spotLightMatrix;\n\t\t\t\tuniforms.spotLightMap.value = lights.state.spotLightMap;\n\t\t\t\tuniforms.pointShadowMap.value = lights.state.pointShadowMap;\n\t\t\t\tuniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix;\n\t\t\t\t// TODO (abelnation): add area lights shadow info to uniforms\n\n\t\t\t}\n\n\t\t\tmaterialProperties.currentProgram = program;\n\t\t\tmaterialProperties.uniformsList = null;\n\n\t\t\treturn program;\n\n\t\t}\n\n\t\tfunction getUniformList( materialProperties ) {\n\n\t\t\tif ( materialProperties.uniformsList === null ) {\n\n\t\t\t\tconst progUniforms = materialProperties.currentProgram.getUniforms();\n\t\t\t\tmaterialProperties.uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, materialProperties.uniforms );\n\n\t\t\t}\n\n\t\t\treturn materialProperties.uniformsList;\n\n\t\t}\n\n\t\tfunction updateCommonMaterialProperties( material, parameters ) {\n\n\t\t\tconst materialProperties = properties.get( material );\n\n\t\t\tmaterialProperties.outputColorSpace = parameters.outputColorSpace;\n\t\t\tmaterialProperties.batching = parameters.batching;\n\t\t\tmaterialProperties.instancing = parameters.instancing;\n\t\t\tmaterialProperties.instancingColor = parameters.instancingColor;\n\t\t\tmaterialProperties.skinning = parameters.skinning;\n\t\t\tmaterialProperties.morphTargets = parameters.morphTargets;\n\t\t\tmaterialProperties.morphNormals = parameters.morphNormals;\n\t\t\tmaterialProperties.morphColors = parameters.morphColors;\n\t\t\tmaterialProperties.morphTargetsCount = parameters.morphTargetsCount;\n\t\t\tmaterialProperties.numClippingPlanes = parameters.numClippingPlanes;\n\t\t\tmaterialProperties.numIntersection = parameters.numClipIntersection;\n\t\t\tmaterialProperties.vertexAlphas = parameters.vertexAlphas;\n\t\t\tmaterialProperties.vertexTangents = parameters.vertexTangents;\n\t\t\tmaterialProperties.toneMapping = parameters.toneMapping;\n\n\t\t}\n\n\t\tfunction setProgram( camera, scene, geometry, material, object ) {\n\n\t\t\tif ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...\n\n\t\t\ttextures.resetTextureUnits();\n\n\t\t\tconst fog = scene.fog;\n\t\t\tconst environment = material.isMeshStandardMaterial ? scene.environment : null;\n\t\t\tconst colorSpace = ( _currentRenderTarget === null ) ? _this.outputColorSpace : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace );\n\t\t\tconst envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment );\n\t\t\tconst vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4;\n\t\t\tconst vertexTangents = !! geometry.attributes.tangent && ( !! material.normalMap || material.anisotropy > 0 );\n\t\t\tconst morphTargets = !! geometry.morphAttributes.position;\n\t\t\tconst morphNormals = !! geometry.morphAttributes.normal;\n\t\t\tconst morphColors = !! geometry.morphAttributes.color;\n\n\t\t\tlet toneMapping = NoToneMapping;\n\n\t\t\tif ( material.toneMapped ) {\n\n\t\t\t\tif ( _currentRenderTarget === null || _currentRenderTarget.isXRRenderTarget === true ) {\n\n\t\t\t\t\ttoneMapping = _this.toneMapping;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tconst morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;\n\t\t\tconst morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;\n\n\t\t\tconst materialProperties = properties.get( material );\n\t\t\tconst lights = currentRenderState.state.lights;\n\n\t\t\tif ( _clippingEnabled === true ) {\n\n\t\t\t\tif ( _localClippingEnabled === true || camera !== _currentCamera ) {\n\n\t\t\t\t\tconst useCache =\n\t\t\t\t\t\tcamera === _currentCamera &&\n\t\t\t\t\t\tmaterial.id === _currentMaterialId;\n\n\t\t\t\t\t// we might want to call this function with some ClippingGroup\n\t\t\t\t\t// object instead of the material, once it becomes feasible\n\t\t\t\t\t// (#8465, #8379)\n\t\t\t\t\tclipping.setState( material, camera, useCache );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tlet needsProgramChange = false;\n\n\t\t\tif ( material.version === materialProperties.__version ) {\n\n\t\t\t\tif ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.outputColorSpace !== colorSpace ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( object.isBatchedMesh && materialProperties.batching === false ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( ! object.isBatchedMesh && materialProperties.batching === true ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( object.isInstancedMesh && materialProperties.instancing === false ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( object.isSkinnedMesh && materialProperties.skinning === false ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( object.isInstancedMesh && materialProperties.instancingColor === true && object.instanceColor === null ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( object.isInstancedMesh && materialProperties.instancingColor === false && object.instanceColor !== null ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.envMap !== envMap ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( material.fog === true && materialProperties.fog !== fog ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.numClippingPlanes !== undefined &&\n\t\t\t\t\t( materialProperties.numClippingPlanes !== clipping.numPlanes ||\n\t\t\t\t\tmaterialProperties.numIntersection !== clipping.numIntersection ) ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.vertexAlphas !== vertexAlphas ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.vertexTangents !== vertexTangents ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.morphTargets !== morphTargets ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.morphNormals !== morphNormals ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.morphColors !== morphColors ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.toneMapping !== toneMapping ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tneedsProgramChange = true;\n\t\t\t\tmaterialProperties.__version = material.version;\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tlet program = materialProperties.currentProgram;\n\n\t\t\tif ( needsProgramChange === true ) {\n\n\t\t\t\tprogram = getProgram( material, scene, object );\n\n\t\t\t}\n\n\t\t\tlet refreshProgram = false;\n\t\t\tlet refreshMaterial = false;\n\t\t\tlet refreshLights = false;\n\n\t\t\tconst p_uniforms = program.getUniforms(),\n\t\t\t\tm_uniforms = materialProperties.uniforms;\n\n\t\t\tif ( state.useProgram( program.program ) ) {\n\n\t\t\t\trefreshProgram = true;\n\t\t\t\trefreshMaterial = true;\n\t\t\t\trefreshLights = true;\n\n\t\t\t}\n\n\t\t\tif ( material.id !== _currentMaterialId ) {\n\n\t\t\t\t_currentMaterialId = material.id;\n\n\t\t\t\trefreshMaterial = true;\n\n\t\t\t}\n\n\t\t\tif ( refreshProgram || _currentCamera !== camera ) {\n\n\t\t\t\t// common camera uniforms\n\n\t\t\t\tp_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );\n\t\t\t\tp_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );\n\n\t\t\t\tconst uCamPos = p_uniforms.map.cameraPosition;\n\n\t\t\t\tif ( uCamPos !== undefined ) {\n\n\t\t\t\t\tuCamPos.setValue( _gl, _vector3.setFromMatrixPosition( camera.matrixWorld ) );\n\n\t\t\t\t}\n\n\t\t\t\tif ( capabilities.logarithmicDepthBuffer ) {\n\n\t\t\t\t\tp_uniforms.setValue( _gl, 'logDepthBufFC',\n\t\t\t\t\t\t2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );\n\n\t\t\t\t}\n\n\t\t\t\t// consider moving isOrthographic to UniformLib and WebGLMaterials, see https://github.com/mrdoob/three.js/pull/26467#issuecomment-1645185067\n\n\t\t\t\tif ( material.isMeshPhongMaterial ||\n\t\t\t\t\tmaterial.isMeshToonMaterial ||\n\t\t\t\t\tmaterial.isMeshLambertMaterial ||\n\t\t\t\t\tmaterial.isMeshBasicMaterial ||\n\t\t\t\t\tmaterial.isMeshStandardMaterial ||\n\t\t\t\t\tmaterial.isShaderMaterial ) {\n\n\t\t\t\t\tp_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true );\n\n\t\t\t\t}\n\n\t\t\t\tif ( _currentCamera !== camera ) {\n\n\t\t\t\t\t_currentCamera = camera;\n\n\t\t\t\t\t// lighting uniforms depend on the camera so enforce an update\n\t\t\t\t\t// now, in case this material supports lights - or later, when\n\t\t\t\t\t// the next material that does gets activated:\n\n\t\t\t\t\trefreshMaterial = true;\t\t// set to true on material change\n\t\t\t\t\trefreshLights = true;\t\t// remains set until update done\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// skinning and morph target uniforms must be set even if material didn't change\n\t\t\t// auto-setting of texture unit for bone and morph texture must go before other textures\n\t\t\t// otherwise textures used for skinning and morphing can take over texture units reserved for other material textures\n\n\t\t\tif ( object.isSkinnedMesh ) {\n\n\t\t\t\tp_uniforms.setOptional( _gl, object, 'bindMatrix' );\n\t\t\t\tp_uniforms.setOptional( _gl, object, 'bindMatrixInverse' );\n\n\t\t\t\tconst skeleton = object.skeleton;\n\n\t\t\t\tif ( skeleton ) {\n\n\t\t\t\t\tif ( capabilities.floatVertexTextures ) {\n\n\t\t\t\t\t\tif ( skeleton.boneTexture === null ) skeleton.computeBoneTexture();\n\n\t\t\t\t\t\tp_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: SkinnedMesh can only be used with WebGL 2. With WebGL 1 OES_texture_float and vertex textures support is required.' );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( object.isBatchedMesh ) {\n\n\t\t\t\tp_uniforms.setOptional( _gl, object, 'batchingTexture' );\n\t\t\t\tp_uniforms.setValue( _gl, 'batchingTexture', object._matricesTexture, textures );\n\n\t\t\t}\n\n\t\t\tconst morphAttributes = geometry.morphAttributes;\n\n\t\t\tif ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || ( morphAttributes.color !== undefined && capabilities.isWebGL2 === true ) ) {\n\n\t\t\t\tmorphtargets.update( object, geometry, program );\n\n\t\t\t}\n\n\t\t\tif ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) {\n\n\t\t\t\tmaterialProperties.receiveShadow = object.receiveShadow;\n\t\t\t\tp_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow );\n\n\t\t\t}\n\n\t\t\t// https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512\n\n\t\t\tif ( material.isMeshGouraudMaterial && material.envMap !== null ) {\n\n\t\t\t\tm_uniforms.envMap.value = envMap;\n\n\t\t\t\tm_uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1;\n\n\t\t\t}\n\n\t\t\tif ( refreshMaterial ) {\n\n\t\t\t\tp_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure );\n\n\t\t\t\tif ( materialProperties.needsLights ) {\n\n\t\t\t\t\t// the current material requires lighting info\n\n\t\t\t\t\t// note: all lighting uniforms are always set correctly\n\t\t\t\t\t// they simply reference the renderer's state for their\n\t\t\t\t\t// values\n\t\t\t\t\t//\n\t\t\t\t\t// use the current material's .needsUpdate flags to set\n\t\t\t\t\t// the GL state when required\n\n\t\t\t\t\tmarkUniformsLightsNeedsUpdate( m_uniforms, refreshLights );\n\n\t\t\t\t}\n\n\t\t\t\t// refresh uniforms common to several materials\n\n\t\t\t\tif ( fog && material.fog === true ) {\n\n\t\t\t\t\tmaterials.refreshFogUniforms( m_uniforms, fog );\n\n\t\t\t\t}\n\n\t\t\t\tmaterials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget );\n\n\t\t\t\tWebGLUniforms.upload( _gl, getUniformList( materialProperties ), m_uniforms, textures );\n\n\t\t\t}\n\n\t\t\tif ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) {\n\n\t\t\t\tWebGLUniforms.upload( _gl, getUniformList( materialProperties ), m_uniforms, textures );\n\t\t\t\tmaterial.uniformsNeedUpdate = false;\n\n\t\t\t}\n\n\t\t\tif ( material.isSpriteMaterial ) {\n\n\t\t\t\tp_uniforms.setValue( _gl, 'center', object.center );\n\n\t\t\t}\n\n\t\t\t// common matrices\n\n\t\t\tp_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );\n\t\t\tp_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );\n\t\t\tp_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );\n\n\t\t\t// UBOs\n\n\t\t\tif ( material.isShaderMaterial || material.isRawShaderMaterial ) {\n\n\t\t\t\tconst groups = material.uniformsGroups;\n\n\t\t\t\tfor ( let i = 0, l = groups.length; i < l; i ++ ) {\n\n\t\t\t\t\tif ( capabilities.isWebGL2 ) {\n\n\t\t\t\t\t\tconst group = groups[ i ];\n\n\t\t\t\t\t\tuniformsGroups.update( group, program );\n\t\t\t\t\t\tuniformsGroups.bind( group, program );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Uniform Buffer Objects can only be used with WebGL 2.' );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn program;\n\n\t\t}\n\n\t\t// If uniforms are marked as clean, they don't need to be loaded to the GPU.\n\n\t\tfunction markUniformsLightsNeedsUpdate( uniforms, value ) {\n\n\t\t\tuniforms.ambientLightColor.needsUpdate = value;\n\t\t\tuniforms.lightProbe.needsUpdate = value;\n\n\t\t\tuniforms.directionalLights.needsUpdate = value;\n\t\t\tuniforms.directionalLightShadows.needsUpdate = value;\n\t\t\tuniforms.pointLights.needsUpdate = value;\n\t\t\tuniforms.pointLightShadows.needsUpdate = value;\n\t\t\tuniforms.spotLights.needsUpdate = value;\n\t\t\tuniforms.spotLightShadows.needsUpdate = value;\n\t\t\tuniforms.rectAreaLights.needsUpdate = value;\n\t\t\tuniforms.hemisphereLights.needsUpdate = value;\n\n\t\t}\n\n\t\tfunction materialNeedsLights( material ) {\n\n\t\t\treturn material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial ||\n\t\t\t\tmaterial.isMeshStandardMaterial || material.isShadowMaterial ||\n\t\t\t\t( material.isShaderMaterial && material.lights === true );\n\n\t\t}\n\n\t\tthis.getActiveCubeFace = function () {\n\n\t\t\treturn _currentActiveCubeFace;\n\n\t\t};\n\n\t\tthis.getActiveMipmapLevel = function () {\n\n\t\t\treturn _currentActiveMipmapLevel;\n\n\t\t};\n\n\t\tthis.getRenderTarget = function () {\n\n\t\t\treturn _currentRenderTarget;\n\n\t\t};\n\n\t\tthis.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture ) {\n\n\t\t\tproperties.get( renderTarget.texture ).__webglTexture = colorTexture;\n\t\t\tproperties.get( renderTarget.depthTexture ).__webglTexture = depthTexture;\n\n\t\t\tconst renderTargetProperties = properties.get( renderTarget );\n\t\t\trenderTargetProperties.__hasExternalTextures = true;\n\n\t\t\tif ( renderTargetProperties.__hasExternalTextures ) {\n\n\t\t\t\trenderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined;\n\n\t\t\t\tif ( ! renderTargetProperties.__autoAllocateDepthBuffer ) {\n\n\t\t\t\t\t// The multisample_render_to_texture extension doesn't work properly if there\n\t\t\t\t\t// are midframe flushes and an external depth buffer. Disable use of the extension.\n\t\t\t\t\tif ( extensions.has( 'WEBGL_multisampled_render_to_texture' ) === true ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided' );\n\t\t\t\t\t\trenderTargetProperties.__useRenderToTexture = false;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer ) {\n\n\t\t\tconst renderTargetProperties = properties.get( renderTarget );\n\t\t\trenderTargetProperties.__webglFramebuffer = defaultFramebuffer;\n\t\t\trenderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined;\n\n\t\t};\n\n\t\tthis.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) {\n\n\t\t\t_currentRenderTarget = renderTarget;\n\t\t\t_currentActiveCubeFace = activeCubeFace;\n\t\t\t_currentActiveMipmapLevel = activeMipmapLevel;\n\n\t\t\tlet useDefaultFramebuffer = true;\n\t\t\tlet framebuffer = null;\n\t\t\tlet isCube = false;\n\t\t\tlet isRenderTarget3D = false;\n\n\t\t\tif ( renderTarget ) {\n\n\t\t\t\tconst renderTargetProperties = properties.get( renderTarget );\n\n\t\t\t\tif ( renderTargetProperties.__useDefaultFramebuffer !== undefined ) {\n\n\t\t\t\t\t// We need to make sure to rebind the framebuffer.\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\t\t\t\t\tuseDefaultFramebuffer = false;\n\n\t\t\t\t} else if ( renderTargetProperties.__webglFramebuffer === undefined ) {\n\n\t\t\t\t\ttextures.setupRenderTarget( renderTarget );\n\n\t\t\t\t} else if ( renderTargetProperties.__hasExternalTextures ) {\n\n\t\t\t\t\t// Color and depth texture must be rebound in order for the swapchain to update.\n\t\t\t\t\ttextures.rebindTextures( renderTarget, properties.get( renderTarget.texture ).__webglTexture, properties.get( renderTarget.depthTexture ).__webglTexture );\n\n\t\t\t\t}\n\n\t\t\t\tconst texture = renderTarget.texture;\n\n\t\t\t\tif ( texture.isData3DTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {\n\n\t\t\t\t\tisRenderTarget3D = true;\n\n\t\t\t\t}\n\n\t\t\t\tconst __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer;\n\n\t\t\t\tif ( renderTarget.isWebGLCubeRenderTarget ) {\n\n\t\t\t\t\tif ( Array.isArray( __webglFramebuffer[ activeCubeFace ] ) ) {\n\n\t\t\t\t\t\tframebuffer = __webglFramebuffer[ activeCubeFace ][ activeMipmapLevel ];\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tframebuffer = __webglFramebuffer[ activeCubeFace ];\n\n\t\t\t\t\t}\n\n\t\t\t\t\tisCube = true;\n\n\t\t\t\t} else if ( ( capabilities.isWebGL2 && renderTarget.samples > 0 ) && textures.useMultisampledRTT( renderTarget ) === false ) {\n\n\t\t\t\t\tframebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( Array.isArray( __webglFramebuffer ) ) {\n\n\t\t\t\t\t\tframebuffer = __webglFramebuffer[ activeMipmapLevel ];\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tframebuffer = __webglFramebuffer;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t_currentViewport.copy( renderTarget.viewport );\n\t\t\t\t_currentScissor.copy( renderTarget.scissor );\n\t\t\t\t_currentScissorTest = renderTarget.scissorTest;\n\n\t\t\t} else {\n\n\t\t\t\t_currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor();\n\t\t\t\t_currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor();\n\t\t\t\t_currentScissorTest = _scissorTest;\n\n\t\t\t}\n\n\t\t\tconst framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n\t\t\tif ( framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer ) {\n\n\t\t\t\tstate.drawBuffers( renderTarget, framebuffer );\n\n\t\t\t}\n\n\t\t\tstate.viewport( _currentViewport );\n\t\t\tstate.scissor( _currentScissor );\n\t\t\tstate.setScissorTest( _currentScissorTest );\n\n\t\t\tif ( isCube ) {\n\n\t\t\t\tconst textureProperties = properties.get( renderTarget.texture );\n\t\t\t\t_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel );\n\n\t\t\t} else if ( isRenderTarget3D ) {\n\n\t\t\t\tconst textureProperties = properties.get( renderTarget.texture );\n\t\t\t\tconst layer = activeCubeFace || 0;\n\t\t\t\t_gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer );\n\n\t\t\t}\n\n\t\t\t_currentMaterialId = - 1; // reset current material to ensure correct uniform bindings\n\n\t\t};\n\n\t\tthis.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) {\n\n\t\t\tif ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tlet framebuffer = properties.get( renderTarget ).__webglFramebuffer;\n\n\t\t\tif ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) {\n\n\t\t\t\tframebuffer = framebuffer[ activeCubeFaceIndex ];\n\n\t\t\t}\n\n\t\t\tif ( framebuffer ) {\n\n\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n\t\t\t\ttry {\n\n\t\t\t\t\tconst texture = renderTarget.texture;\n\t\t\t\t\tconst textureFormat = texture.format;\n\t\t\t\t\tconst textureType = texture.type;\n\n\t\t\t\t\tif ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tconst halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) );\n\n\t\t\t\t\tif ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513)\n\t\t\t\t\t\t! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox\n\t\t\t\t\t\t! halfFloatSupportedByExt ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604)\n\n\t\t\t\t\tif ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) {\n\n\t\t\t\t\t\t_gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer );\n\n\t\t\t\t\t}\n\n\t\t\t\t} finally {\n\n\t\t\t\t\t// restore framebuffer of current render target if necessary\n\n\t\t\t\t\tconst framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null;\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.copyFramebufferToTexture = function ( position, texture, level = 0 ) {\n\n\t\t\tconst levelScale = Math.pow( 2, - level );\n\t\t\tconst width = Math.floor( texture.image.width * levelScale );\n\t\t\tconst height = Math.floor( texture.image.height * levelScale );\n\n\t\t\ttextures.setTexture2D( texture, 0 );\n\n\t\t\t_gl.copyTexSubImage2D( _gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height );\n\n\t\t\tstate.unbindTexture();\n\n\t\t};\n\n\t\tthis.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) {\n\n\t\t\tconst width = srcTexture.image.width;\n\t\t\tconst height = srcTexture.image.height;\n\t\t\tconst glFormat = utils.convert( dstTexture.format );\n\t\t\tconst glType = utils.convert( dstTexture.type );\n\n\t\t\ttextures.setTexture2D( dstTexture, 0 );\n\n\t\t\t// As another texture upload may have changed pixelStorei\n\t\t\t// parameters, make sure they are correct for the dstTexture\n\t\t\t_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );\n\n\t\t\tif ( srcTexture.isDataTexture ) {\n\n\t\t\t\t_gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data );\n\n\t\t\t} else {\n\n\t\t\t\tif ( srcTexture.isCompressedTexture ) {\n\n\t\t\t\t\t_gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Generate mipmaps only when copying level 0\n\t\t\tif ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( _gl.TEXTURE_2D );\n\n\t\t\tstate.unbindTexture();\n\n\t\t};\n\n\t\tthis.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) {\n\n\t\t\tif ( _this.isWebGL1Renderer ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tconst width = sourceBox.max.x - sourceBox.min.x + 1;\n\t\t\tconst height = sourceBox.max.y - sourceBox.min.y + 1;\n\t\t\tconst depth = sourceBox.max.z - sourceBox.min.z + 1;\n\t\t\tconst glFormat = utils.convert( dstTexture.format );\n\t\t\tconst glType = utils.convert( dstTexture.type );\n\t\t\tlet glTarget;\n\n\t\t\tif ( dstTexture.isData3DTexture ) {\n\n\t\t\t\ttextures.setTexture3D( dstTexture, 0 );\n\t\t\t\tglTarget = _gl.TEXTURE_3D;\n\n\t\t\t} else if ( dstTexture.isDataArrayTexture || dstTexture.isCompressedArrayTexture ) {\n\n\t\t\t\ttextures.setTexture2DArray( dstTexture, 0 );\n\t\t\t\tglTarget = _gl.TEXTURE_2D_ARRAY;\n\n\t\t\t} else {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\t_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );\n\n\t\t\tconst unpackRowLen = _gl.getParameter( _gl.UNPACK_ROW_LENGTH );\n\t\t\tconst unpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT );\n\t\t\tconst unpackSkipPixels = _gl.getParameter( _gl.UNPACK_SKIP_PIXELS );\n\t\t\tconst unpackSkipRows = _gl.getParameter( _gl.UNPACK_SKIP_ROWS );\n\t\t\tconst unpackSkipImages = _gl.getParameter( _gl.UNPACK_SKIP_IMAGES );\n\n\t\t\tconst image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ level ] : srcTexture.image;\n\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, image.width );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, image.height );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, sourceBox.min.x );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, sourceBox.min.y );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, sourceBox.min.z );\n\n\t\t\tif ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) {\n\n\t\t\t\t_gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data );\n\n\t\t\t} else {\n\n\t\t\t\tif ( srcTexture.isCompressedArrayTexture ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture.' );\n\t\t\t\t\t_gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, unpackRowLen );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, unpackSkipPixels );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, unpackSkipRows );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, unpackSkipImages );\n\n\t\t\t// Generate mipmaps only when copying level 0\n\t\t\tif ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget );\n\n\t\t\tstate.unbindTexture();\n\n\t\t};\n\n\t\tthis.initTexture = function ( texture ) {\n\n\t\t\tif ( texture.isCubeTexture ) {\n\n\t\t\t\ttextures.setTextureCube( texture, 0 );\n\n\t\t\t} else if ( texture.isData3DTexture ) {\n\n\t\t\t\ttextures.setTexture3D( texture, 0 );\n\n\t\t\t} else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {\n\n\t\t\t\ttextures.setTexture2DArray( texture, 0 );\n\n\t\t\t} else {\n\n\t\t\t\ttextures.setTexture2D( texture, 0 );\n\n\t\t\t}\n\n\t\t\tstate.unbindTexture();\n\n\t\t};\n\n\t\tthis.resetState = function () {\n\n\t\t\t_currentActiveCubeFace = 0;\n\t\t\t_currentActiveMipmapLevel = 0;\n\t\t\t_currentRenderTarget = null;\n\n\t\t\tstate.reset();\n\t\t\tbindingStates.reset();\n\n\t\t};\n\n\t\tif ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) {\n\n\t\t\t__THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) );\n\n\t\t}\n\n\t}\n\n\tget coordinateSystem() {\n\n\t\treturn WebGLCoordinateSystem;\n\n\t}\n\n\tget outputColorSpace() {\n\n\t\treturn this._outputColorSpace;\n\n\t}\n\n\tset outputColorSpace( colorSpace ) {\n\n\t\tthis._outputColorSpace = colorSpace;\n\n\t\tconst gl = this.getContext();\n\t\tgl.drawingBufferColorSpace = colorSpace === DisplayP3ColorSpace ? 'display-p3' : 'srgb';\n\t\tgl.unpackColorSpace = ColorManagement.workingColorSpace === LinearDisplayP3ColorSpace ? 'display-p3' : 'srgb';\n\n\t}\n\n\tget outputEncoding() { // @deprecated, r152\n\n\t\tconsole.warn( 'THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead.' );\n\t\treturn this.outputColorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding;\n\n\t}\n\n\tset outputEncoding( encoding ) { // @deprecated, r152\n\n\t\tconsole.warn( 'THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead.' );\n\t\tthis.outputColorSpace = encoding === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;\n\n\t}\n\n\tget useLegacyLights() { // @deprecated, r155\n\n\t\tconsole.warn( 'THREE.WebGLRenderer: The property .useLegacyLights has been deprecated. Migrate your lighting according to the following guide: https://discourse.threejs.org/t/updates-to-lighting-in-three-js-r155/53733.' );\n\t\treturn this._useLegacyLights;\n\n\t}\n\n\tset useLegacyLights( value ) { // @deprecated, r155\n\n\t\tconsole.warn( 'THREE.WebGLRenderer: The property .useLegacyLights has been deprecated. Migrate your lighting according to the following guide: https://discourse.threejs.org/t/updates-to-lighting-in-three-js-r155/53733.' );\n\t\tthis._useLegacyLights = value;\n\n\t}\n\n}\n\nclass WebGL1Renderer extends WebGLRenderer {}\n\nWebGL1Renderer.prototype.isWebGL1Renderer = true;\n\nclass FogExp2 {\n\n\tconstructor( color, density = 0.00025 ) {\n\n\t\tthis.isFogExp2 = true;\n\n\t\tthis.name = '';\n\n\t\tthis.color = new Color( color );\n\t\tthis.density = density;\n\n\t}\n\n\tclone() {\n\n\t\treturn new FogExp2( this.color, this.density );\n\n\t}\n\n\ttoJSON( /* meta */ ) {\n\n\t\treturn {\n\t\t\ttype: 'FogExp2',\n\t\t\tname: this.name,\n\t\t\tcolor: this.color.getHex(),\n\t\t\tdensity: this.density\n\t\t};\n\n\t}\n\n}\n\nclass Fog {\n\n\tconstructor( color, near = 1, far = 1000 ) {\n\n\t\tthis.isFog = true;\n\n\t\tthis.name = '';\n\n\t\tthis.color = new Color( color );\n\n\t\tthis.near = near;\n\t\tthis.far = far;\n\n\t}\n\n\tclone() {\n\n\t\treturn new Fog( this.color, this.near, this.far );\n\n\t}\n\n\ttoJSON( /* meta */ ) {\n\n\t\treturn {\n\t\t\ttype: 'Fog',\n\t\t\tname: this.name,\n\t\t\tcolor: this.color.getHex(),\n\t\t\tnear: this.near,\n\t\t\tfar: this.far\n\t\t};\n\n\t}\n\n}\n\nclass Scene extends Object3D {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isScene = true;\n\n\t\tthis.type = 'Scene';\n\n\t\tthis.background = null;\n\t\tthis.environment = null;\n\t\tthis.fog = null;\n\n\t\tthis.backgroundBlurriness = 0;\n\t\tthis.backgroundIntensity = 1;\n\n\t\tthis.overrideMaterial = null;\n\n\t\tif ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) {\n\n\t\t\t__THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) );\n\n\t\t}\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tif ( source.background !== null ) this.background = source.background.clone();\n\t\tif ( source.environment !== null ) this.environment = source.environment.clone();\n\t\tif ( source.fog !== null ) this.fog = source.fog.clone();\n\n\t\tthis.backgroundBlurriness = source.backgroundBlurriness;\n\t\tthis.backgroundIntensity = source.backgroundIntensity;\n\n\t\tif ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();\n\n\t\tthis.matrixAutoUpdate = source.matrixAutoUpdate;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tif ( this.fog !== null ) data.object.fog = this.fog.toJSON();\n\t\tif ( this.backgroundBlurriness > 0 ) data.object.backgroundBlurriness = this.backgroundBlurriness;\n\t\tif ( this.backgroundIntensity !== 1 ) data.object.backgroundIntensity = this.backgroundIntensity;\n\n\t\treturn data;\n\n\t}\n\n}\n\nclass InterleavedBuffer {\n\n\tconstructor( array, stride ) {\n\n\t\tthis.isInterleavedBuffer = true;\n\n\t\tthis.array = array;\n\t\tthis.stride = stride;\n\t\tthis.count = array !== undefined ? array.length / stride : 0;\n\n\t\tthis.usage = StaticDrawUsage;\n\t\tthis._updateRange = { offset: 0, count: - 1 };\n\t\tthis.updateRanges = [];\n\n\t\tthis.version = 0;\n\n\t\tthis.uuid = generateUUID();\n\n\t}\n\n\tonUploadCallback() {}\n\n\tset needsUpdate( value ) {\n\n\t\tif ( value === true ) this.version ++;\n\n\t}\n\n\tget updateRange() {\n\n\t\tconsole.warn( 'THREE.InterleavedBuffer: updateRange() is deprecated and will be removed in r169. Use addUpdateRange() instead.' ); // @deprecated, r159\n\t\treturn this._updateRange;\n\n\t}\n\n\tsetUsage( value ) {\n\n\t\tthis.usage = value;\n\n\t\treturn this;\n\n\t}\n\n\taddUpdateRange( start, count ) {\n\n\t\tthis.updateRanges.push( { start, count } );\n\n\t}\n\n\tclearUpdateRanges() {\n\n\t\tthis.updateRanges.length = 0;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.array = new source.array.constructor( source.array );\n\t\tthis.count = source.count;\n\t\tthis.stride = source.stride;\n\t\tthis.usage = source.usage;\n\n\t\treturn this;\n\n\t}\n\n\tcopyAt( index1, attribute, index2 ) {\n\n\t\tindex1 *= this.stride;\n\t\tindex2 *= attribute.stride;\n\n\t\tfor ( let i = 0, l = this.stride; i < l; i ++ ) {\n\n\t\t\tthis.array[ index1 + i ] = attribute.array[ index2 + i ];\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tset( value, offset = 0 ) {\n\n\t\tthis.array.set( value, offset );\n\n\t\treturn this;\n\n\t}\n\n\tclone( data ) {\n\n\t\tif ( data.arrayBuffers === undefined ) {\n\n\t\t\tdata.arrayBuffers = {};\n\n\t\t}\n\n\t\tif ( this.array.buffer._uuid === undefined ) {\n\n\t\t\tthis.array.buffer._uuid = generateUUID();\n\n\t\t}\n\n\t\tif ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) {\n\n\t\t\tdata.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer;\n\n\t\t}\n\n\t\tconst array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] );\n\n\t\tconst ib = new this.constructor( array, this.stride );\n\t\tib.setUsage( this.usage );\n\n\t\treturn ib;\n\n\t}\n\n\tonUpload( callback ) {\n\n\t\tthis.onUploadCallback = callback;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( data ) {\n\n\t\tif ( data.arrayBuffers === undefined ) {\n\n\t\t\tdata.arrayBuffers = {};\n\n\t\t}\n\n\t\t// generate UUID for array buffer if necessary\n\n\t\tif ( this.array.buffer._uuid === undefined ) {\n\n\t\t\tthis.array.buffer._uuid = generateUUID();\n\n\t\t}\n\n\t\tif ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) {\n\n\t\t\tdata.arrayBuffers[ this.array.buffer._uuid ] = Array.from( new Uint32Array( this.array.buffer ) );\n\n\t\t}\n\n\t\t//\n\n\t\treturn {\n\t\t\tuuid: this.uuid,\n\t\t\tbuffer: this.array.buffer._uuid,\n\t\t\ttype: this.array.constructor.name,\n\t\t\tstride: this.stride\n\t\t};\n\n\t}\n\n}\n\nconst _vector$6 = /*@__PURE__*/ new Vector3();\n\nclass InterleavedBufferAttribute {\n\n\tconstructor( interleavedBuffer, itemSize, offset, normalized = false ) {\n\n\t\tthis.isInterleavedBufferAttribute = true;\n\n\t\tthis.name = '';\n\n\t\tthis.data = interleavedBuffer;\n\t\tthis.itemSize = itemSize;\n\t\tthis.offset = offset;\n\n\t\tthis.normalized = normalized;\n\n\t}\n\n\tget count() {\n\n\t\treturn this.data.count;\n\n\t}\n\n\tget array() {\n\n\t\treturn this.data.array;\n\n\t}\n\n\tset needsUpdate( value ) {\n\n\t\tthis.data.needsUpdate = value;\n\n\t}\n\n\tapplyMatrix4( m ) {\n\n\t\tfor ( let i = 0, l = this.data.count; i < l; i ++ ) {\n\n\t\t\t_vector$6.fromBufferAttribute( this, i );\n\n\t\t\t_vector$6.applyMatrix4( m );\n\n\t\t\tthis.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tapplyNormalMatrix( m ) {\n\n\t\tfor ( let i = 0, l = this.count; i < l; i ++ ) {\n\n\t\t\t_vector$6.fromBufferAttribute( this, i );\n\n\t\t\t_vector$6.applyNormalMatrix( m );\n\n\t\t\tthis.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\ttransformDirection( m ) {\n\n\t\tfor ( let i = 0, l = this.count; i < l; i ++ ) {\n\n\t\t\t_vector$6.fromBufferAttribute( this, i );\n\n\t\t\t_vector$6.transformDirection( m );\n\n\t\t\tthis.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetX( index, x ) {\n\n\t\tif ( this.normalized ) x = normalize( x, this.array );\n\n\t\tthis.data.array[ index * this.data.stride + this.offset ] = x;\n\n\t\treturn this;\n\n\t}\n\n\tsetY( index, y ) {\n\n\t\tif ( this.normalized ) y = normalize( y, this.array );\n\n\t\tthis.data.array[ index * this.data.stride + this.offset + 1 ] = y;\n\n\t\treturn this;\n\n\t}\n\n\tsetZ( index, z ) {\n\n\t\tif ( this.normalized ) z = normalize( z, this.array );\n\n\t\tthis.data.array[ index * this.data.stride + this.offset + 2 ] = z;\n\n\t\treturn this;\n\n\t}\n\n\tsetW( index, w ) {\n\n\t\tif ( this.normalized ) w = normalize( w, this.array );\n\n\t\tthis.data.array[ index * this.data.stride + this.offset + 3 ] = w;\n\n\t\treturn this;\n\n\t}\n\n\tgetX( index ) {\n\n\t\tlet x = this.data.array[ index * this.data.stride + this.offset ];\n\n\t\tif ( this.normalized ) x = denormalize( x, this.array );\n\n\t\treturn x;\n\n\t}\n\n\tgetY( index ) {\n\n\t\tlet y = this.data.array[ index * this.data.stride + this.offset + 1 ];\n\n\t\tif ( this.normalized ) y = denormalize( y, this.array );\n\n\t\treturn y;\n\n\t}\n\n\tgetZ( index ) {\n\n\t\tlet z = this.data.array[ index * this.data.stride + this.offset + 2 ];\n\n\t\tif ( this.normalized ) z = denormalize( z, this.array );\n\n\t\treturn z;\n\n\t}\n\n\tgetW( index ) {\n\n\t\tlet w = this.data.array[ index * this.data.stride + this.offset + 3 ];\n\n\t\tif ( this.normalized ) w = denormalize( w, this.array );\n\n\t\treturn w;\n\n\t}\n\n\tsetXY( index, x, y ) {\n\n\t\tindex = index * this.data.stride + this.offset;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\n\t\t}\n\n\t\tthis.data.array[ index + 0 ] = x;\n\t\tthis.data.array[ index + 1 ] = y;\n\n\t\treturn this;\n\n\t}\n\n\tsetXYZ( index, x, y, z ) {\n\n\t\tindex = index * this.data.stride + this.offset;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\t\t\tz = normalize( z, this.array );\n\n\t\t}\n\n\t\tthis.data.array[ index + 0 ] = x;\n\t\tthis.data.array[ index + 1 ] = y;\n\t\tthis.data.array[ index + 2 ] = z;\n\n\t\treturn this;\n\n\t}\n\n\tsetXYZW( index, x, y, z, w ) {\n\n\t\tindex = index * this.data.stride + this.offset;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\t\t\tz = normalize( z, this.array );\n\t\t\tw = normalize( w, this.array );\n\n\t\t}\n\n\t\tthis.data.array[ index + 0 ] = x;\n\t\tthis.data.array[ index + 1 ] = y;\n\t\tthis.data.array[ index + 2 ] = z;\n\t\tthis.data.array[ index + 3 ] = w;\n\n\t\treturn this;\n\n\t}\n\n\tclone( data ) {\n\n\t\tif ( data === undefined ) {\n\n\t\t\tconsole.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interleaved buffer attribute will de-interleave buffer data.' );\n\n\t\t\tconst array = [];\n\n\t\t\tfor ( let i = 0; i < this.count; i ++ ) {\n\n\t\t\t\tconst index = i * this.data.stride + this.offset;\n\n\t\t\t\tfor ( let j = 0; j < this.itemSize; j ++ ) {\n\n\t\t\t\t\tarray.push( this.data.array[ index + j ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized );\n\n\t\t} else {\n\n\t\t\tif ( data.interleavedBuffers === undefined ) {\n\n\t\t\t\tdata.interleavedBuffers = {};\n\n\t\t\t}\n\n\t\t\tif ( data.interleavedBuffers[ this.data.uuid ] === undefined ) {\n\n\t\t\t\tdata.interleavedBuffers[ this.data.uuid ] = this.data.clone( data );\n\n\t\t\t}\n\n\t\t\treturn new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized );\n\n\t\t}\n\n\t}\n\n\ttoJSON( data ) {\n\n\t\tif ( data === undefined ) {\n\n\t\t\tconsole.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interleaved buffer attribute will de-interleave buffer data.' );\n\n\t\t\tconst array = [];\n\n\t\t\tfor ( let i = 0; i < this.count; i ++ ) {\n\n\t\t\t\tconst index = i * this.data.stride + this.offset;\n\n\t\t\t\tfor ( let j = 0; j < this.itemSize; j ++ ) {\n\n\t\t\t\t\tarray.push( this.data.array[ index + j ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// de-interleave data and save it as an ordinary buffer attribute for now\n\n\t\t\treturn {\n\t\t\t\titemSize: this.itemSize,\n\t\t\t\ttype: this.array.constructor.name,\n\t\t\t\tarray: array,\n\t\t\t\tnormalized: this.normalized\n\t\t\t};\n\n\t\t} else {\n\n\t\t\t// save as true interleaved attribute\n\n\t\t\tif ( data.interleavedBuffers === undefined ) {\n\n\t\t\t\tdata.interleavedBuffers = {};\n\n\t\t\t}\n\n\t\t\tif ( data.interleavedBuffers[ this.data.uuid ] === undefined ) {\n\n\t\t\t\tdata.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data );\n\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tisInterleavedBufferAttribute: true,\n\t\t\t\titemSize: this.itemSize,\n\t\t\t\tdata: this.data.uuid,\n\t\t\t\toffset: this.offset,\n\t\t\t\tnormalized: this.normalized\n\t\t\t};\n\n\t\t}\n\n\t}\n\n}\n\nclass SpriteMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isSpriteMaterial = true;\n\n\t\tthis.type = 'SpriteMaterial';\n\n\t\tthis.color = new Color( 0xffffff );\n\n\t\tthis.map = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.rotation = 0;\n\n\t\tthis.sizeAttenuation = true;\n\n\t\tthis.transparent = true;\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.map = source.map;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.rotation = source.rotation;\n\n\t\tthis.sizeAttenuation = source.sizeAttenuation;\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\nlet _geometry;\n\nconst _intersectPoint = /*@__PURE__*/ new Vector3();\nconst _worldScale = /*@__PURE__*/ new Vector3();\nconst _mvPosition = /*@__PURE__*/ new Vector3();\n\nconst _alignedPosition = /*@__PURE__*/ new Vector2();\nconst _rotatedPosition = /*@__PURE__*/ new Vector2();\nconst _viewWorldMatrix = /*@__PURE__*/ new Matrix4();\n\nconst _vA = /*@__PURE__*/ new Vector3();\nconst _vB = /*@__PURE__*/ new Vector3();\nconst _vC = /*@__PURE__*/ new Vector3();\n\nconst _uvA = /*@__PURE__*/ new Vector2();\nconst _uvB = /*@__PURE__*/ new Vector2();\nconst _uvC = /*@__PURE__*/ new Vector2();\n\nclass Sprite extends Object3D {\n\n\tconstructor( material = new SpriteMaterial() ) {\n\n\t\tsuper();\n\n\t\tthis.isSprite = true;\n\n\t\tthis.type = 'Sprite';\n\n\t\tif ( _geometry === undefined ) {\n\n\t\t\t_geometry = new BufferGeometry();\n\n\t\t\tconst float32Array = new Float32Array( [\n\t\t\t\t- 0.5, - 0.5, 0, 0, 0,\n\t\t\t\t0.5, - 0.5, 0, 1, 0,\n\t\t\t\t0.5, 0.5, 0, 1, 1,\n\t\t\t\t- 0.5, 0.5, 0, 0, 1\n\t\t\t] );\n\n\t\t\tconst interleavedBuffer = new InterleavedBuffer( float32Array, 5 );\n\n\t\t\t_geometry.setIndex( [ 0, 1, 2,\t0, 2, 3 ] );\n\t\t\t_geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) );\n\t\t\t_geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) );\n\n\t\t}\n\n\t\tthis.geometry = _geometry;\n\t\tthis.material = material;\n\n\t\tthis.center = new Vector2( 0.5, 0.5 );\n\n\t}\n\n\traycast( raycaster, intersects ) {\n\n\t\tif ( raycaster.camera === null ) {\n\n\t\t\tconsole.error( 'THREE.Sprite: \"Raycaster.camera\" needs to be set in order to raycast against sprites.' );\n\n\t\t}\n\n\t\t_worldScale.setFromMatrixScale( this.matrixWorld );\n\n\t\t_viewWorldMatrix.copy( raycaster.camera.matrixWorld );\n\t\tthis.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld );\n\n\t\t_mvPosition.setFromMatrixPosition( this.modelViewMatrix );\n\n\t\tif ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) {\n\n\t\t\t_worldScale.multiplyScalar( - _mvPosition.z );\n\n\t\t}\n\n\t\tconst rotation = this.material.rotation;\n\t\tlet sin, cos;\n\n\t\tif ( rotation !== 0 ) {\n\n\t\t\tcos = Math.cos( rotation );\n\t\t\tsin = Math.sin( rotation );\n\n\t\t}\n\n\t\tconst center = this.center;\n\n\t\ttransformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos );\n\t\ttransformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos );\n\t\ttransformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos );\n\n\t\t_uvA.set( 0, 0 );\n\t\t_uvB.set( 1, 0 );\n\t\t_uvC.set( 1, 1 );\n\n\t\t// check first triangle\n\t\tlet intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint );\n\n\t\tif ( intersect === null ) {\n\n\t\t\t// check second triangle\n\t\t\ttransformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos );\n\t\t\t_uvB.set( 0, 1 );\n\n\t\t\tintersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint );\n\t\t\tif ( intersect === null ) {\n\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst distance = raycaster.ray.origin.distanceTo( _intersectPoint );\n\n\t\tif ( distance < raycaster.near || distance > raycaster.far ) return;\n\n\t\tintersects.push( {\n\n\t\t\tdistance: distance,\n\t\t\tpoint: _intersectPoint.clone(),\n\t\t\tuv: Triangle.getInterpolation( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ),\n\t\t\tface: null,\n\t\t\tobject: this\n\n\t\t} );\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tif ( source.center !== undefined ) this.center.copy( source.center );\n\n\t\tthis.material = source.material;\n\n\t\treturn this;\n\n\t}\n\n}\n\nfunction transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) {\n\n\t// compute position in camera space\n\t_alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale );\n\n\t// to check if rotation is not zero\n\tif ( sin !== undefined ) {\n\n\t\t_rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y );\n\t\t_rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y );\n\n\t} else {\n\n\t\t_rotatedPosition.copy( _alignedPosition );\n\n\t}\n\n\n\tvertexPosition.copy( mvPosition );\n\tvertexPosition.x += _rotatedPosition.x;\n\tvertexPosition.y += _rotatedPosition.y;\n\n\t// transform to world space\n\tvertexPosition.applyMatrix4( _viewWorldMatrix );\n\n}\n\nconst _v1$2 = /*@__PURE__*/ new Vector3();\nconst _v2$1 = /*@__PURE__*/ new Vector3();\n\nclass LOD extends Object3D {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis._currentLevel = 0;\n\n\t\tthis.type = 'LOD';\n\n\t\tObject.defineProperties( this, {\n\t\t\tlevels: {\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: []\n\t\t\t},\n\t\t\tisLOD: {\n\t\t\t\tvalue: true,\n\t\t\t}\n\t\t} );\n\n\t\tthis.autoUpdate = true;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source, false );\n\n\t\tconst levels = source.levels;\n\n\t\tfor ( let i = 0, l = levels.length; i < l; i ++ ) {\n\n\t\t\tconst level = levels[ i ];\n\n\t\t\tthis.addLevel( level.object.clone(), level.distance, level.hysteresis );\n\n\t\t}\n\n\t\tthis.autoUpdate = source.autoUpdate;\n\n\t\treturn this;\n\n\t}\n\n\taddLevel( object, distance = 0, hysteresis = 0 ) {\n\n\t\tdistance = Math.abs( distance );\n\n\t\tconst levels = this.levels;\n\n\t\tlet l;\n\n\t\tfor ( l = 0; l < levels.length; l ++ ) {\n\n\t\t\tif ( distance < levels[ l ].distance ) {\n\n\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t}\n\n\t\tlevels.splice( l, 0, { distance: distance, hysteresis: hysteresis, object: object } );\n\n\t\tthis.add( object );\n\n\t\treturn this;\n\n\t}\n\n\tgetCurrentLevel() {\n\n\t\treturn this._currentLevel;\n\n\t}\n\n\n\n\tgetObjectForDistance( distance ) {\n\n\t\tconst levels = this.levels;\n\n\t\tif ( levels.length > 0 ) {\n\n\t\t\tlet i, l;\n\n\t\t\tfor ( i = 1, l = levels.length; i < l; i ++ ) {\n\n\t\t\t\tlet levelDistance = levels[ i ].distance;\n\n\t\t\t\tif ( levels[ i ].object.visible ) {\n\n\t\t\t\t\tlevelDistance -= levelDistance * levels[ i ].hysteresis;\n\n\t\t\t\t}\n\n\t\t\t\tif ( distance < levelDistance ) {\n\n\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn levels[ i - 1 ].object;\n\n\t\t}\n\n\t\treturn null;\n\n\t}\n\n\traycast( raycaster, intersects ) {\n\n\t\tconst levels = this.levels;\n\n\t\tif ( levels.length > 0 ) {\n\n\t\t\t_v1$2.setFromMatrixPosition( this.matrixWorld );\n\n\t\t\tconst distance = raycaster.ray.origin.distanceTo( _v1$2 );\n\n\t\t\tthis.getObjectForDistance( distance ).raycast( raycaster, intersects );\n\n\t\t}\n\n\t}\n\n\tupdate( camera ) {\n\n\t\tconst levels = this.levels;\n\n\t\tif ( levels.length > 1 ) {\n\n\t\t\t_v1$2.setFromMatrixPosition( camera.matrixWorld );\n\t\t\t_v2$1.setFromMatrixPosition( this.matrixWorld );\n\n\t\t\tconst distance = _v1$2.distanceTo( _v2$1 ) / camera.zoom;\n\n\t\t\tlevels[ 0 ].object.visible = true;\n\n\t\t\tlet i, l;\n\n\t\t\tfor ( i = 1, l = levels.length; i < l; i ++ ) {\n\n\t\t\t\tlet levelDistance = levels[ i ].distance;\n\n\t\t\t\tif ( levels[ i ].object.visible ) {\n\n\t\t\t\t\tlevelDistance -= levelDistance * levels[ i ].hysteresis;\n\n\t\t\t\t}\n\n\t\t\t\tif ( distance >= levelDistance ) {\n\n\t\t\t\t\tlevels[ i - 1 ].object.visible = false;\n\t\t\t\t\tlevels[ i ].object.visible = true;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis._currentLevel = i - 1;\n\n\t\t\tfor ( ; i < l; i ++ ) {\n\n\t\t\t\tlevels[ i ].object.visible = false;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tif ( this.autoUpdate === false ) data.object.autoUpdate = false;\n\n\t\tdata.object.levels = [];\n\n\t\tconst levels = this.levels;\n\n\t\tfor ( let i = 0, l = levels.length; i < l; i ++ ) {\n\n\t\t\tconst level = levels[ i ];\n\n\t\t\tdata.object.levels.push( {\n\t\t\t\tobject: level.object.uuid,\n\t\t\t\tdistance: level.distance,\n\t\t\t\thysteresis: level.hysteresis\n\t\t\t} );\n\n\t\t}\n\n\t\treturn data;\n\n\t}\n\n}\n\nconst _basePosition = /*@__PURE__*/ new Vector3();\n\nconst _skinIndex = /*@__PURE__*/ new Vector4();\nconst _skinWeight = /*@__PURE__*/ new Vector4();\n\nconst _vector3 = /*@__PURE__*/ new Vector3();\nconst _matrix4 = /*@__PURE__*/ new Matrix4();\nconst _vertex = /*@__PURE__*/ new Vector3();\n\nconst _sphere$4 = /*@__PURE__*/ new Sphere();\nconst _inverseMatrix$2 = /*@__PURE__*/ new Matrix4();\nconst _ray$2 = /*@__PURE__*/ new Ray();\n\nclass SkinnedMesh extends Mesh {\n\n\tconstructor( geometry, material ) {\n\n\t\tsuper( geometry, material );\n\n\t\tthis.isSkinnedMesh = true;\n\n\t\tthis.type = 'SkinnedMesh';\n\n\t\tthis.bindMode = AttachedBindMode;\n\t\tthis.bindMatrix = new Matrix4();\n\t\tthis.bindMatrixInverse = new Matrix4();\n\n\t\tthis.boundingBox = null;\n\t\tthis.boundingSphere = null;\n\n\t}\n\n\tcomputeBoundingBox() {\n\n\t\tconst geometry = this.geometry;\n\n\t\tif ( this.boundingBox === null ) {\n\n\t\t\tthis.boundingBox = new Box3();\n\n\t\t}\n\n\t\tthis.boundingBox.makeEmpty();\n\n\t\tconst positionAttribute = geometry.getAttribute( 'position' );\n\n\t\tfor ( let i = 0; i < positionAttribute.count; i ++ ) {\n\n\t\t\tthis.getVertexPosition( i, _vertex );\n\t\t\tthis.boundingBox.expandByPoint( _vertex );\n\n\t\t}\n\n\t}\n\n\tcomputeBoundingSphere() {\n\n\t\tconst geometry = this.geometry;\n\n\t\tif ( this.boundingSphere === null ) {\n\n\t\t\tthis.boundingSphere = new Sphere();\n\n\t\t}\n\n\t\tthis.boundingSphere.makeEmpty();\n\n\t\tconst positionAttribute = geometry.getAttribute( 'position' );\n\n\t\tfor ( let i = 0; i < positionAttribute.count; i ++ ) {\n\n\t\t\tthis.getVertexPosition( i, _vertex );\n\t\t\tthis.boundingSphere.expandByPoint( _vertex );\n\n\t\t}\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.bindMode = source.bindMode;\n\t\tthis.bindMatrix.copy( source.bindMatrix );\n\t\tthis.bindMatrixInverse.copy( source.bindMatrixInverse );\n\n\t\tthis.skeleton = source.skeleton;\n\n\t\tif ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone();\n\t\tif ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone();\n\n\t\treturn this;\n\n\t}\n\n\traycast( raycaster, intersects ) {\n\n\t\tconst material = this.material;\n\t\tconst matrixWorld = this.matrixWorld;\n\n\t\tif ( material === undefined ) return;\n\n\t\t// test with bounding sphere in world space\n\n\t\tif ( this.boundingSphere === null ) this.computeBoundingSphere();\n\n\t\t_sphere$4.copy( this.boundingSphere );\n\t\t_sphere$4.applyMatrix4( matrixWorld );\n\n\t\tif ( raycaster.ray.intersectsSphere( _sphere$4 ) === false ) return;\n\n\t\t// convert ray to local space of skinned mesh\n\n\t\t_inverseMatrix$2.copy( matrixWorld ).invert();\n\t\t_ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 );\n\n\t\t// test with bounding box in local space\n\n\t\tif ( this.boundingBox !== null ) {\n\n\t\t\tif ( _ray$2.intersectsBox( this.boundingBox ) === false ) return;\n\n\t\t}\n\n\t\t// test for intersections with geometry\n\n\t\tthis._computeIntersections( raycaster, intersects, _ray$2 );\n\n\t}\n\n\tgetVertexPosition( index, target ) {\n\n\t\tsuper.getVertexPosition( index, target );\n\n\t\tthis.applyBoneTransform( index, target );\n\n\t\treturn target;\n\n\t}\n\n\tbind( skeleton, bindMatrix ) {\n\n\t\tthis.skeleton = skeleton;\n\n\t\tif ( bindMatrix === undefined ) {\n\n\t\t\tthis.updateMatrixWorld( true );\n\n\t\t\tthis.skeleton.calculateInverses();\n\n\t\t\tbindMatrix = this.matrixWorld;\n\n\t\t}\n\n\t\tthis.bindMatrix.copy( bindMatrix );\n\t\tthis.bindMatrixInverse.copy( bindMatrix ).invert();\n\n\t}\n\n\tpose() {\n\n\t\tthis.skeleton.pose();\n\n\t}\n\n\tnormalizeSkinWeights() {\n\n\t\tconst vector = new Vector4();\n\n\t\tconst skinWeight = this.geometry.attributes.skinWeight;\n\n\t\tfor ( let i = 0, l = skinWeight.count; i < l; i ++ ) {\n\n\t\t\tvector.fromBufferAttribute( skinWeight, i );\n\n\t\t\tconst scale = 1.0 / vector.manhattanLength();\n\n\t\t\tif ( scale !== Infinity ) {\n\n\t\t\t\tvector.multiplyScalar( scale );\n\n\t\t\t} else {\n\n\t\t\t\tvector.set( 1, 0, 0, 0 ); // do something reasonable\n\n\t\t\t}\n\n\t\t\tskinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w );\n\n\t\t}\n\n\t}\n\n\tupdateMatrixWorld( force ) {\n\n\t\tsuper.updateMatrixWorld( force );\n\n\t\tif ( this.bindMode === AttachedBindMode ) {\n\n\t\t\tthis.bindMatrixInverse.copy( this.matrixWorld ).invert();\n\n\t\t} else if ( this.bindMode === DetachedBindMode ) {\n\n\t\t\tthis.bindMatrixInverse.copy( this.bindMatrix ).invert();\n\n\t\t} else {\n\n\t\t\tconsole.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode );\n\n\t\t}\n\n\t}\n\n\tapplyBoneTransform( index, vector ) {\n\n\t\tconst skeleton = this.skeleton;\n\t\tconst geometry = this.geometry;\n\n\t\t_skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index );\n\t\t_skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index );\n\n\t\t_basePosition.copy( vector ).applyMatrix4( this.bindMatrix );\n\n\t\tvector.set( 0, 0, 0 );\n\n\t\tfor ( let i = 0; i < 4; i ++ ) {\n\n\t\t\tconst weight = _skinWeight.getComponent( i );\n\n\t\t\tif ( weight !== 0 ) {\n\n\t\t\t\tconst boneIndex = _skinIndex.getComponent( i );\n\n\t\t\t\t_matrix4.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] );\n\n\t\t\t\tvector.addScaledVector( _vector3.copy( _basePosition ).applyMatrix4( _matrix4 ), weight );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn vector.applyMatrix4( this.bindMatrixInverse );\n\n\t}\n\n\tboneTransform( index, vector ) { // @deprecated, r151\n\n\t\tconsole.warn( 'THREE.SkinnedMesh: .boneTransform() was renamed to .applyBoneTransform() in r151.' );\n\t\treturn this.applyBoneTransform( index, vector );\n\n\t}\n\n\n}\n\nclass Bone extends Object3D {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isBone = true;\n\n\t\tthis.type = 'Bone';\n\n\t}\n\n}\n\nclass DataTexture extends Texture {\n\n\tconstructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, colorSpace ) {\n\n\t\tsuper( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace );\n\n\t\tthis.isDataTexture = true;\n\n\t\tthis.image = { data: data, width: width, height: height };\n\n\t\tthis.generateMipmaps = false;\n\t\tthis.flipY = false;\n\t\tthis.unpackAlignment = 1;\n\n\t}\n\n}\n\nconst _offsetMatrix = /*@__PURE__*/ new Matrix4();\nconst _identityMatrix$1 = /*@__PURE__*/ new Matrix4();\n\nclass Skeleton {\n\n\tconstructor( bones = [], boneInverses = [] ) {\n\n\t\tthis.uuid = generateUUID();\n\n\t\tthis.bones = bones.slice( 0 );\n\t\tthis.boneInverses = boneInverses;\n\t\tthis.boneMatrices = null;\n\n\t\tthis.boneTexture = null;\n\n\t\tthis.init();\n\n\t}\n\n\tinit() {\n\n\t\tconst bones = this.bones;\n\t\tconst boneInverses = this.boneInverses;\n\n\t\tthis.boneMatrices = new Float32Array( bones.length * 16 );\n\n\t\t// calculate inverse bone matrices if necessary\n\n\t\tif ( boneInverses.length === 0 ) {\n\n\t\t\tthis.calculateInverses();\n\n\t\t} else {\n\n\t\t\t// handle special case\n\n\t\t\tif ( bones.length !== boneInverses.length ) {\n\n\t\t\t\tconsole.warn( 'THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.' );\n\n\t\t\t\tthis.boneInverses = [];\n\n\t\t\t\tfor ( let i = 0, il = this.bones.length; i < il; i ++ ) {\n\n\t\t\t\t\tthis.boneInverses.push( new Matrix4() );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tcalculateInverses() {\n\n\t\tthis.boneInverses.length = 0;\n\n\t\tfor ( let i = 0, il = this.bones.length; i < il; i ++ ) {\n\n\t\t\tconst inverse = new Matrix4();\n\n\t\t\tif ( this.bones[ i ] ) {\n\n\t\t\t\tinverse.copy( this.bones[ i ].matrixWorld ).invert();\n\n\t\t\t}\n\n\t\t\tthis.boneInverses.push( inverse );\n\n\t\t}\n\n\t}\n\n\tpose() {\n\n\t\t// recover the bind-time world matrices\n\n\t\tfor ( let i = 0, il = this.bones.length; i < il; i ++ ) {\n\n\t\t\tconst bone = this.bones[ i ];\n\n\t\t\tif ( bone ) {\n\n\t\t\t\tbone.matrixWorld.copy( this.boneInverses[ i ] ).invert();\n\n\t\t\t}\n\n\t\t}\n\n\t\t// compute the local matrices, positions, rotations and scales\n\n\t\tfor ( let i = 0, il = this.bones.length; i < il; i ++ ) {\n\n\t\t\tconst bone = this.bones[ i ];\n\n\t\t\tif ( bone ) {\n\n\t\t\t\tif ( bone.parent && bone.parent.isBone ) {\n\n\t\t\t\t\tbone.matrix.copy( bone.parent.matrixWorld ).invert();\n\t\t\t\t\tbone.matrix.multiply( bone.matrixWorld );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tbone.matrix.copy( bone.matrixWorld );\n\n\t\t\t\t}\n\n\t\t\t\tbone.matrix.decompose( bone.position, bone.quaternion, bone.scale );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tupdate() {\n\n\t\tconst bones = this.bones;\n\t\tconst boneInverses = this.boneInverses;\n\t\tconst boneMatrices = this.boneMatrices;\n\t\tconst boneTexture = this.boneTexture;\n\n\t\t// flatten bone matrices to array\n\n\t\tfor ( let i = 0, il = bones.length; i < il; i ++ ) {\n\n\t\t\t// compute the offset between the current and the original transform\n\n\t\t\tconst matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix$1;\n\n\t\t\t_offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] );\n\t\t\t_offsetMatrix.toArray( boneMatrices, i * 16 );\n\n\t\t}\n\n\t\tif ( boneTexture !== null ) {\n\n\t\t\tboneTexture.needsUpdate = true;\n\n\t\t}\n\n\t}\n\n\tclone() {\n\n\t\treturn new Skeleton( this.bones, this.boneInverses );\n\n\t}\n\n\tcomputeBoneTexture() {\n\n\t\t// layout (1 matrix = 4 pixels)\n\t\t//      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)\n\t\t//  with  8x8  pixel texture max   16 bones * 4 pixels =  (8 * 8)\n\t\t//       16x16 pixel texture max   64 bones * 4 pixels = (16 * 16)\n\t\t//       32x32 pixel texture max  256 bones * 4 pixels = (32 * 32)\n\t\t//       64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)\n\n\t\tlet size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix\n\t\tsize = Math.ceil( size / 4 ) * 4;\n\t\tsize = Math.max( size, 4 );\n\n\t\tconst boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel\n\t\tboneMatrices.set( this.boneMatrices ); // copy current values\n\n\t\tconst boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType );\n\t\tboneTexture.needsUpdate = true;\n\n\t\tthis.boneMatrices = boneMatrices;\n\t\tthis.boneTexture = boneTexture;\n\n\t\treturn this;\n\n\t}\n\n\tgetBoneByName( name ) {\n\n\t\tfor ( let i = 0, il = this.bones.length; i < il; i ++ ) {\n\n\t\t\tconst bone = this.bones[ i ];\n\n\t\t\tif ( bone.name === name ) {\n\n\t\t\t\treturn bone;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn undefined;\n\n\t}\n\n\tdispose( ) {\n\n\t\tif ( this.boneTexture !== null ) {\n\n\t\t\tthis.boneTexture.dispose();\n\n\t\t\tthis.boneTexture = null;\n\n\t\t}\n\n\t}\n\n\tfromJSON( json, bones ) {\n\n\t\tthis.uuid = json.uuid;\n\n\t\tfor ( let i = 0, l = json.bones.length; i < l; i ++ ) {\n\n\t\t\tconst uuid = json.bones[ i ];\n\t\t\tlet bone = bones[ uuid ];\n\n\t\t\tif ( bone === undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.Skeleton: No bone found with UUID:', uuid );\n\t\t\t\tbone = new Bone();\n\n\t\t\t}\n\n\t\t\tthis.bones.push( bone );\n\t\t\tthis.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) );\n\n\t\t}\n\n\t\tthis.init();\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = {\n\t\t\tmetadata: {\n\t\t\t\tversion: 4.6,\n\t\t\t\ttype: 'Skeleton',\n\t\t\t\tgenerator: 'Skeleton.toJSON'\n\t\t\t},\n\t\t\tbones: [],\n\t\t\tboneInverses: []\n\t\t};\n\n\t\tdata.uuid = this.uuid;\n\n\t\tconst bones = this.bones;\n\t\tconst boneInverses = this.boneInverses;\n\n\t\tfor ( let i = 0, l = bones.length; i < l; i ++ ) {\n\n\t\t\tconst bone = bones[ i ];\n\t\t\tdata.bones.push( bone.uuid );\n\n\t\t\tconst boneInverse = boneInverses[ i ];\n\t\t\tdata.boneInverses.push( boneInverse.toArray() );\n\n\t\t}\n\n\t\treturn data;\n\n\t}\n\n}\n\nclass InstancedBufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized, meshPerAttribute = 1 ) {\n\n\t\tsuper( array, itemSize, normalized );\n\n\t\tthis.isInstancedBufferAttribute = true;\n\n\t\tthis.meshPerAttribute = meshPerAttribute;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.meshPerAttribute = source.meshPerAttribute;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.meshPerAttribute = this.meshPerAttribute;\n\n\t\tdata.isInstancedBufferAttribute = true;\n\n\t\treturn data;\n\n\t}\n\n}\n\nconst _instanceLocalMatrix = /*@__PURE__*/ new Matrix4();\nconst _instanceWorldMatrix = /*@__PURE__*/ new Matrix4();\n\nconst _instanceIntersects = [];\n\nconst _box3 = /*@__PURE__*/ new Box3();\nconst _identity = /*@__PURE__*/ new Matrix4();\nconst _mesh$1 = /*@__PURE__*/ new Mesh();\nconst _sphere$3 = /*@__PURE__*/ new Sphere();\n\nclass InstancedMesh extends Mesh {\n\n\tconstructor( geometry, material, count ) {\n\n\t\tsuper( geometry, material );\n\n\t\tthis.isInstancedMesh = true;\n\n\t\tthis.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 );\n\t\tthis.instanceColor = null;\n\n\t\tthis.count = count;\n\n\t\tthis.boundingBox = null;\n\t\tthis.boundingSphere = null;\n\n\t\tfor ( let i = 0; i < count; i ++ ) {\n\n\t\t\tthis.setMatrixAt( i, _identity );\n\n\t\t}\n\n\t}\n\n\tcomputeBoundingBox() {\n\n\t\tconst geometry = this.geometry;\n\t\tconst count = this.count;\n\n\t\tif ( this.boundingBox === null ) {\n\n\t\t\tthis.boundingBox = new Box3();\n\n\t\t}\n\n\t\tif ( geometry.boundingBox === null ) {\n\n\t\t\tgeometry.computeBoundingBox();\n\n\t\t}\n\n\t\tthis.boundingBox.makeEmpty();\n\n\t\tfor ( let i = 0; i < count; i ++ ) {\n\n\t\t\tthis.getMatrixAt( i, _instanceLocalMatrix );\n\n\t\t\t_box3.copy( geometry.boundingBox ).applyMatrix4( _instanceLocalMatrix );\n\n\t\t\tthis.boundingBox.union( _box3 );\n\n\t\t}\n\n\t}\n\n\tcomputeBoundingSphere() {\n\n\t\tconst geometry = this.geometry;\n\t\tconst count = this.count;\n\n\t\tif ( this.boundingSphere === null ) {\n\n\t\t\tthis.boundingSphere = new Sphere();\n\n\t\t}\n\n\t\tif ( geometry.boundingSphere === null ) {\n\n\t\t\tgeometry.computeBoundingSphere();\n\n\t\t}\n\n\t\tthis.boundingSphere.makeEmpty();\n\n\t\tfor ( let i = 0; i < count; i ++ ) {\n\n\t\t\tthis.getMatrixAt( i, _instanceLocalMatrix );\n\n\t\t\t_sphere$3.copy( geometry.boundingSphere ).applyMatrix4( _instanceLocalMatrix );\n\n\t\t\tthis.boundingSphere.union( _sphere$3 );\n\n\t\t}\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.instanceMatrix.copy( source.instanceMatrix );\n\n\t\tif ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone();\n\n\t\tthis.count = source.count;\n\n\t\tif ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone();\n\t\tif ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone();\n\n\t\treturn this;\n\n\t}\n\n\tgetColorAt( index, color ) {\n\n\t\tcolor.fromArray( this.instanceColor.array, index * 3 );\n\n\t}\n\n\tgetMatrixAt( index, matrix ) {\n\n\t\tmatrix.fromArray( this.instanceMatrix.array, index * 16 );\n\n\t}\n\n\traycast( raycaster, intersects ) {\n\n\t\tconst matrixWorld = this.matrixWorld;\n\t\tconst raycastTimes = this.count;\n\n\t\t_mesh$1.geometry = this.geometry;\n\t\t_mesh$1.material = this.material;\n\n\t\tif ( _mesh$1.material === undefined ) return;\n\n\t\t// test with bounding sphere first\n\n\t\tif ( this.boundingSphere === null ) this.computeBoundingSphere();\n\n\t\t_sphere$3.copy( this.boundingSphere );\n\t\t_sphere$3.applyMatrix4( matrixWorld );\n\n\t\tif ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) return;\n\n\t\t// now test each instance\n\n\t\tfor ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) {\n\n\t\t\t// calculate the world matrix for each instance\n\n\t\t\tthis.getMatrixAt( instanceId, _instanceLocalMatrix );\n\n\t\t\t_instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix );\n\n\t\t\t// the mesh represents this single instance\n\n\t\t\t_mesh$1.matrixWorld = _instanceWorldMatrix;\n\n\t\t\t_mesh$1.raycast( raycaster, _instanceIntersects );\n\n\t\t\t// process the result of raycast\n\n\t\t\tfor ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) {\n\n\t\t\t\tconst intersect = _instanceIntersects[ i ];\n\t\t\t\tintersect.instanceId = instanceId;\n\t\t\t\tintersect.object = this;\n\t\t\t\tintersects.push( intersect );\n\n\t\t\t}\n\n\t\t\t_instanceIntersects.length = 0;\n\n\t\t}\n\n\t}\n\n\tsetColorAt( index, color ) {\n\n\t\tif ( this.instanceColor === null ) {\n\n\t\t\tthis.instanceColor = new InstancedBufferAttribute( new Float32Array( this.instanceMatrix.count * 3 ), 3 );\n\n\t\t}\n\n\t\tcolor.toArray( this.instanceColor.array, index * 3 );\n\n\t}\n\n\tsetMatrixAt( index, matrix ) {\n\n\t\tmatrix.toArray( this.instanceMatrix.array, index * 16 );\n\n\t}\n\n\tupdateMorphTargets() {\n\n\t}\n\n\tdispose() {\n\n\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t}\n\n}\n\nfunction sortOpaque( a, b ) {\n\n\treturn a.z - b.z;\n\n}\n\nfunction sortTransparent( a, b ) {\n\n\treturn b.z - a.z;\n\n}\n\nclass MultiDrawRenderList {\n\n\tconstructor() {\n\n\t\tthis.index = 0;\n\t\tthis.pool = [];\n\t\tthis.list = [];\n\n\t}\n\n\tpush( drawRange, z ) {\n\n\t\tconst pool = this.pool;\n\t\tconst list = this.list;\n\t\tif ( this.index >= pool.length ) {\n\n\t\t\tpool.push( {\n\n\t\t\t\tstart: - 1,\n\t\t\t\tcount: - 1,\n\t\t\t\tz: - 1,\n\n\t\t\t} );\n\n\t\t}\n\n\t\tconst item = pool[ this.index ];\n\t\tlist.push( item );\n\t\tthis.index ++;\n\n\t\titem.start = drawRange.start;\n\t\titem.count = drawRange.count;\n\t\titem.z = z;\n\n\t}\n\n\treset() {\n\n\t\tthis.list.length = 0;\n\t\tthis.index = 0;\n\n\t}\n\n}\n\nconst ID_ATTR_NAME = 'batchId';\nconst _matrix = /*@__PURE__*/ new Matrix4();\nconst _invMatrixWorld = /*@__PURE__*/ new Matrix4();\nconst _identityMatrix = /*@__PURE__*/ new Matrix4();\nconst _projScreenMatrix$2 = /*@__PURE__*/ new Matrix4();\nconst _frustum = /*@__PURE__*/ new Frustum();\nconst _box$1 = /*@__PURE__*/ new Box3();\nconst _sphere$2 = /*@__PURE__*/ new Sphere();\nconst _vector$5 = /*@__PURE__*/ new Vector3();\nconst _renderList = /*@__PURE__*/ new MultiDrawRenderList();\nconst _mesh = /*@__PURE__*/ new Mesh();\nconst _batchIntersects = [];\n\n// @TODO: SkinnedMesh support?\n// @TODO: geometry.groups support?\n// @TODO: geometry.drawRange support?\n// @TODO: geometry.morphAttributes support?\n// @TODO: Support uniform parameter per geometry\n// @TODO: Add an \"optimize\" function to pack geometry and remove data gaps\n\n// copies data from attribute \"src\" into \"target\" starting at \"targetOffset\"\nfunction copyAttributeData( src, target, targetOffset = 0 ) {\n\n\tconst itemSize = target.itemSize;\n\tif ( src.isInterleavedBufferAttribute || src.array.constructor !== target.array.constructor ) {\n\n\t\t// use the component getters and setters if the array data cannot\n\t\t// be copied directly\n\t\tconst vertexCount = src.count;\n\t\tfor ( let i = 0; i < vertexCount; i ++ ) {\n\n\t\t\tfor ( let c = 0; c < itemSize; c ++ ) {\n\n\t\t\t\ttarget.setComponent( i + targetOffset, c, src.getComponent( i, c ) );\n\n\t\t\t}\n\n\t\t}\n\n\t} else {\n\n\t\t// faster copy approach using typed array set function\n\t\ttarget.array.set( src.array, targetOffset * itemSize );\n\n\t}\n\n\ttarget.needsUpdate = true;\n\n}\n\nclass BatchedMesh extends Mesh {\n\n\tget maxGeometryCount() {\n\n\t\treturn this._maxGeometryCount;\n\n\t}\n\n\tconstructor( maxGeometryCount, maxVertexCount, maxIndexCount = maxVertexCount * 2, material ) {\n\n\t\tsuper( new BufferGeometry(), material );\n\n\t\tthis.isBatchedMesh = true;\n\t\tthis.perObjectFrustumCulled = true;\n\t\tthis.sortObjects = true;\n\t\tthis.boundingBox = null;\n\t\tthis.boundingSphere = null;\n\t\tthis.customSort = null;\n\n\t\tthis._drawRanges = [];\n\t\tthis._reservedRanges = [];\n\n\t\tthis._visibility = [];\n\t\tthis._active = [];\n\t\tthis._bounds = [];\n\n\t\tthis._maxGeometryCount = maxGeometryCount;\n\t\tthis._maxVertexCount = maxVertexCount;\n\t\tthis._maxIndexCount = maxIndexCount;\n\n\t\tthis._geometryInitialized = false;\n\t\tthis._geometryCount = 0;\n\t\tthis._multiDrawCounts = new Int32Array( maxGeometryCount );\n\t\tthis._multiDrawStarts = new Int32Array( maxGeometryCount );\n\t\tthis._multiDrawCount = 0;\n\t\tthis._visibilityChanged = true;\n\n\t\t// Local matrix per geometry by using data texture\n\t\tthis._matricesTexture = null;\n\n\t\tthis._initMatricesTexture();\n\n\t}\n\n\t_initMatricesTexture() {\n\n\t\t// layout (1 matrix = 4 pixels)\n\t\t//      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)\n\t\t//  with  8x8  pixel texture max   16 matrices * 4 pixels =  (8 * 8)\n\t\t//       16x16 pixel texture max   64 matrices * 4 pixels = (16 * 16)\n\t\t//       32x32 pixel texture max  256 matrices * 4 pixels = (32 * 32)\n\t\t//       64x64 pixel texture max 1024 matrices * 4 pixels = (64 * 64)\n\n\t\tlet size = Math.sqrt( this._maxGeometryCount * 4 ); // 4 pixels needed for 1 matrix\n\t\tsize = Math.ceil( size / 4 ) * 4;\n\t\tsize = Math.max( size, 4 );\n\n\t\tconst matricesArray = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel\n\t\tconst matricesTexture = new DataTexture( matricesArray, size, size, RGBAFormat, FloatType );\n\n\t\tthis._matricesTexture = matricesTexture;\n\n\t}\n\n\t_initializeGeometry( reference ) {\n\n\t\tconst geometry = this.geometry;\n\t\tconst maxVertexCount = this._maxVertexCount;\n\t\tconst maxGeometryCount = this._maxGeometryCount;\n\t\tconst maxIndexCount = this._maxIndexCount;\n\t\tif ( this._geometryInitialized === false ) {\n\n\t\t\tfor ( const attributeName in reference.attributes ) {\n\n\t\t\t\tconst srcAttribute = reference.getAttribute( attributeName );\n\t\t\t\tconst { array, itemSize, normalized } = srcAttribute;\n\n\t\t\t\tconst dstArray = new array.constructor( maxVertexCount * itemSize );\n\t\t\t\tconst dstAttribute = new srcAttribute.constructor( dstArray, itemSize, normalized );\n\t\t\t\tdstAttribute.setUsage( srcAttribute.usage );\n\n\t\t\t\tgeometry.setAttribute( attributeName, dstAttribute );\n\n\t\t\t}\n\n\t\t\tif ( reference.getIndex() !== null ) {\n\n\t\t\t\tconst indexArray = maxVertexCount > 65536\n\t\t\t\t\t? new Uint32Array( maxIndexCount )\n\t\t\t\t\t: new Uint16Array( maxIndexCount );\n\n\t\t\t\tgeometry.setIndex( new BufferAttribute( indexArray, 1 ) );\n\n\t\t\t}\n\n\t\t\tconst idArray = maxGeometryCount > 65536\n\t\t\t\t? new Uint32Array( maxVertexCount )\n\t\t\t\t: new Uint16Array( maxVertexCount );\n\t\t\tgeometry.setAttribute( ID_ATTR_NAME, new BufferAttribute( idArray, 1 ) );\n\n\t\t\tthis._geometryInitialized = true;\n\n\t\t}\n\n\t}\n\n\t// Make sure the geometry is compatible with the existing combined geometry atributes\n\t_validateGeometry( geometry ) {\n\n\t\t// check that the geometry doesn't have a version of our reserved id attribute\n\t\tif ( geometry.getAttribute( ID_ATTR_NAME ) ) {\n\n\t\t\tthrow new Error( `BatchedMesh: Geometry cannot use attribute \"${ ID_ATTR_NAME }\"` );\n\n\t\t}\n\n\t\t// check to ensure the geometries are using consistent attributes and indices\n\t\tconst batchGeometry = this.geometry;\n\t\tif ( Boolean( geometry.getIndex() ) !== Boolean( batchGeometry.getIndex() ) ) {\n\n\t\t\tthrow new Error( 'BatchedMesh: All geometries must consistently have \"index\".' );\n\n\t\t}\n\n\t\tfor ( const attributeName in batchGeometry.attributes ) {\n\n\t\t\tif ( attributeName === ID_ATTR_NAME ) {\n\n\t\t\t\tcontinue;\n\n\t\t\t}\n\n\t\t\tif ( ! geometry.hasAttribute( attributeName ) ) {\n\n\t\t\t\tthrow new Error( `BatchedMesh: Added geometry missing \"${ attributeName }\". All geometries must have consistent attributes.` );\n\n\t\t\t}\n\n\t\t\tconst srcAttribute = geometry.getAttribute( attributeName );\n\t\t\tconst dstAttribute = batchGeometry.getAttribute( attributeName );\n\t\t\tif ( srcAttribute.itemSize !== dstAttribute.itemSize || srcAttribute.normalized !== dstAttribute.normalized ) {\n\n\t\t\t\tthrow new Error( 'BatchedMesh: All attributes must have a consistent itemSize and normalized value.' );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tsetCustomSort( func ) {\n\n\t\tthis.customSort = func;\n\t\treturn this;\n\n\t}\n\n\tcomputeBoundingBox() {\n\n\t\tif ( this.boundingBox === null ) {\n\n\t\t\tthis.boundingBox = new Box3();\n\n\t\t}\n\n\t\tconst geometryCount = this._geometryCount;\n\t\tconst boundingBox = this.boundingBox;\n\t\tconst active = this._active;\n\n\t\tboundingBox.makeEmpty();\n\t\tfor ( let i = 0; i < geometryCount; i ++ ) {\n\n\t\t\tif ( active[ i ] === false ) continue;\n\n\t\t\tthis.getMatrixAt( i, _matrix );\n\t\t\tthis.getBoundingBoxAt( i, _box$1 ).applyMatrix4( _matrix );\n\t\t\tboundingBox.union( _box$1 );\n\n\t\t}\n\n\t}\n\n\tcomputeBoundingSphere() {\n\n\t\tif ( this.boundingSphere === null ) {\n\n\t\t\tthis.boundingSphere = new Sphere();\n\n\t\t}\n\n\t\tconst geometryCount = this._geometryCount;\n\t\tconst boundingSphere = this.boundingSphere;\n\t\tconst active = this._active;\n\n\t\tboundingSphere.makeEmpty();\n\t\tfor ( let i = 0; i < geometryCount; i ++ ) {\n\n\t\t\tif ( active[ i ] === false ) continue;\n\n\t\t\tthis.getMatrixAt( i, _matrix );\n\t\t\tthis.getBoundingSphereAt( i, _sphere$2 ).applyMatrix4( _matrix );\n\t\t\tboundingSphere.union( _sphere$2 );\n\n\t\t}\n\n\t}\n\n\taddGeometry( geometry, vertexCount = - 1, indexCount = - 1 ) {\n\n\t\tthis._initializeGeometry( geometry );\n\n\t\tthis._validateGeometry( geometry );\n\n\t\t// ensure we're not over geometry\n\t\tif ( this._geometryCount >= this._maxGeometryCount ) {\n\n\t\t\tthrow new Error( 'BatchedMesh: Maximum geometry count reached.' );\n\n\t\t}\n\n\t\t// get the necessary range fo the geometry\n\t\tconst reservedRange = {\n\t\t\tvertexStart: - 1,\n\t\t\tvertexCount: - 1,\n\t\t\tindexStart: - 1,\n\t\t\tindexCount: - 1,\n\t\t};\n\n\t\tlet lastRange = null;\n\t\tconst reservedRanges = this._reservedRanges;\n\t\tconst drawRanges = this._drawRanges;\n\t\tconst bounds = this._bounds;\n\t\tif ( this._geometryCount !== 0 ) {\n\n\t\t\tlastRange = reservedRanges[ reservedRanges.length - 1 ];\n\n\t\t}\n\n\t\tif ( vertexCount === - 1 ) {\n\n\t\t\treservedRange.vertexCount = geometry.getAttribute( 'position' ).count;\n\n\t\t} else {\n\n\t\t\treservedRange.vertexCount = vertexCount;\n\n\t\t}\n\n\t\tif ( lastRange === null ) {\n\n\t\t\treservedRange.vertexStart = 0;\n\n\t\t} else {\n\n\t\t\treservedRange.vertexStart = lastRange.vertexStart + lastRange.vertexCount;\n\n\t\t}\n\n\t\tconst index = geometry.getIndex();\n\t\tconst hasIndex = index !== null;\n\t\tif ( hasIndex ) {\n\n\t\t\tif ( indexCount\t=== - 1 ) {\n\n\t\t\t\treservedRange.indexCount = index.count;\n\n\t\t\t} else {\n\n\t\t\t\treservedRange.indexCount = indexCount;\n\n\t\t\t}\n\n\t\t\tif ( lastRange === null ) {\n\n\t\t\t\treservedRange.indexStart = 0;\n\n\t\t\t} else {\n\n\t\t\t\treservedRange.indexStart = lastRange.indexStart + lastRange.indexCount;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif (\n\t\t\treservedRange.indexStart !== - 1 &&\n\t\t\treservedRange.indexStart + reservedRange.indexCount > this._maxIndexCount ||\n\t\t\treservedRange.vertexStart + reservedRange.vertexCount > this._maxVertexCount\n\t\t) {\n\n\t\t\tthrow new Error( 'BatchedMesh: Reserved space request exceeds the maximum buffer size.' );\n\n\t\t}\n\n\t\tconst visibility = this._visibility;\n\t\tconst active = this._active;\n\t\tconst matricesTexture = this._matricesTexture;\n\t\tconst matricesArray = this._matricesTexture.image.data;\n\n\t\t// push new visibility states\n\t\tvisibility.push( true );\n\t\tactive.push( true );\n\n\t\t// update id\n\t\tconst geometryId = this._geometryCount;\n\t\tthis._geometryCount ++;\n\n\t\t// initialize matrix information\n\t\t_identityMatrix.toArray( matricesArray, geometryId * 16 );\n\t\tmatricesTexture.needsUpdate = true;\n\n\t\t// add the reserved range and draw range objects\n\t\treservedRanges.push( reservedRange );\n\t\tdrawRanges.push( {\n\t\t\tstart: hasIndex ? reservedRange.indexStart : reservedRange.vertexStart,\n\t\t\tcount: - 1\n\t\t} );\n\t\tbounds.push( {\n\t\t\tboxInitialized: false,\n\t\t\tbox: new Box3(),\n\n\t\t\tsphereInitialized: false,\n\t\t\tsphere: new Sphere()\n\t\t} );\n\n\t\t// set the id for the geometry\n\t\tconst idAttribute = this.geometry.getAttribute( ID_ATTR_NAME );\n\t\tfor ( let i = 0; i < reservedRange.vertexCount; i ++ ) {\n\n\t\t\tidAttribute.setX( reservedRange.vertexStart + i, geometryId );\n\n\t\t}\n\n\t\tidAttribute.needsUpdate = true;\n\n\t\t// update the geometry\n\t\tthis.setGeometryAt( geometryId, geometry );\n\n\t\treturn geometryId;\n\n\t}\n\n\tsetGeometryAt( id, geometry ) {\n\n\t\tif ( id >= this._geometryCount ) {\n\n\t\t\tthrow new Error( 'BatchedMesh: Maximum geometry count reached.' );\n\n\t\t}\n\n\t\tthis._validateGeometry( geometry );\n\n\t\tconst batchGeometry = this.geometry;\n\t\tconst hasIndex = batchGeometry.getIndex() !== null;\n\t\tconst dstIndex = batchGeometry.getIndex();\n\t\tconst srcIndex = geometry.getIndex();\n\t\tconst reservedRange = this._reservedRanges[ id ];\n\t\tif (\n\t\t\thasIndex &&\n\t\t\tsrcIndex.count > reservedRange.indexCount ||\n\t\t\tgeometry.attributes.position.count > reservedRange.vertexCount\n\t\t) {\n\n\t\t\tthrow new Error( 'BatchedMesh: Reserved space not large enough for provided geometry.' );\n\n\t\t}\n\n\t\t// copy geometry over\n\t\tconst vertexStart = reservedRange.vertexStart;\n\t\tconst vertexCount = reservedRange.vertexCount;\n\t\tfor ( const attributeName in batchGeometry.attributes ) {\n\n\t\t\tif ( attributeName === ID_ATTR_NAME ) {\n\n\t\t\t\tcontinue;\n\n\t\t\t}\n\n\t\t\t// copy attribute data\n\t\t\tconst srcAttribute = geometry.getAttribute( attributeName );\n\t\t\tconst dstAttribute = batchGeometry.getAttribute( attributeName );\n\t\t\tcopyAttributeData( srcAttribute, dstAttribute, vertexStart );\n\n\t\t\t// fill the rest in with zeroes\n\t\t\tconst itemSize = srcAttribute.itemSize;\n\t\t\tfor ( let i = srcAttribute.count, l = vertexCount; i < l; i ++ ) {\n\n\t\t\t\tconst index = vertexStart + i;\n\t\t\t\tfor ( let c = 0; c < itemSize; c ++ ) {\n\n\t\t\t\t\tdstAttribute.setComponent( index, c, 0 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tdstAttribute.needsUpdate = true;\n\n\t\t}\n\n\t\t// copy index\n\t\tif ( hasIndex ) {\n\n\t\t\tconst indexStart = reservedRange.indexStart;\n\n\t\t\t// copy index data over\n\t\t\tfor ( let i = 0; i < srcIndex.count; i ++ ) {\n\n\t\t\t\tdstIndex.setX( indexStart + i, vertexStart + srcIndex.getX( i ) );\n\n\t\t\t}\n\n\t\t\t// fill the rest in with zeroes\n\t\t\tfor ( let i = srcIndex.count, l = reservedRange.indexCount; i < l; i ++ ) {\n\n\t\t\t\tdstIndex.setX( indexStart + i, vertexStart );\n\n\t\t\t}\n\n\t\t\tdstIndex.needsUpdate = true;\n\n\t\t}\n\n\t\t// store the bounding boxes\n\t\tconst bound = this._bounds[ id ];\n\t\tif ( geometry.boundingBox !== null ) {\n\n\t\t\tbound.box.copy( geometry.boundingBox );\n\t\t\tbound.boxInitialized = true;\n\n\t\t} else {\n\n\t\t\tbound.boxInitialized = false;\n\n\t\t}\n\n\t\tif ( geometry.boundingSphere !== null ) {\n\n\t\t\tbound.sphere.copy( geometry.boundingSphere );\n\t\t\tbound.sphereInitialized = true;\n\n\t\t} else {\n\n\t\t\tbound.sphereInitialized = false;\n\n\t\t}\n\n\t\t// set drawRange count\n\t\tconst drawRange = this._drawRanges[ id ];\n\t\tconst posAttr = geometry.getAttribute( 'position' );\n\t\tdrawRange.count = hasIndex ? srcIndex.count : posAttr.count;\n\t\tthis._visibilityChanged = true;\n\n\t\treturn id;\n\n\t}\n\n\tdeleteGeometry( geometryId ) {\n\n\t\t// Note: User needs to call optimize() afterward to pack the data.\n\n\t\tconst active = this._active;\n\t\tif ( geometryId >= active.length || active[ geometryId ] === false ) {\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tactive[ geometryId ] = false;\n\t\tthis._visibilityChanged = true;\n\n\t\treturn this;\n\n\t}\n\n\t// get bounding box and compute it if it doesn't exist\n\tgetBoundingBoxAt( id, target ) {\n\n\t\tconst active = this._active;\n\t\tif ( active[ id ] === false ) {\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\t// compute bounding box\n\t\tconst bound = this._bounds[ id ];\n\t\tconst box = bound.box;\n\t\tconst geometry = this.geometry;\n\t\tif ( bound.boxInitialized === false ) {\n\n\t\t\tbox.makeEmpty();\n\n\t\t\tconst index = geometry.index;\n\t\t\tconst position = geometry.attributes.position;\n\t\t\tconst drawRange = this._drawRanges[ id ];\n\t\t\tfor ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i ++ ) {\n\n\t\t\t\tlet iv = i;\n\t\t\t\tif ( index ) {\n\n\t\t\t\t\tiv = index.getX( iv );\n\n\t\t\t\t}\n\n\t\t\t\tbox.expandByPoint( _vector$5.fromBufferAttribute( position, iv ) );\n\n\t\t\t}\n\n\t\t\tbound.boxInitialized = true;\n\n\t\t}\n\n\t\ttarget.copy( box );\n\t\treturn target;\n\n\t}\n\n\t// get bounding sphere and compute it if it doesn't exist\n\tgetBoundingSphereAt( id, target ) {\n\n\t\tconst active = this._active;\n\t\tif ( active[ id ] === false ) {\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\t// compute bounding sphere\n\t\tconst bound = this._bounds[ id ];\n\t\tconst sphere = bound.sphere;\n\t\tconst geometry = this.geometry;\n\t\tif ( bound.sphereInitialized === false ) {\n\n\t\t\tsphere.makeEmpty();\n\n\t\t\tthis.getBoundingBoxAt( id, _box$1 );\n\t\t\t_box$1.getCenter( sphere.center );\n\n\t\t\tconst index = geometry.index;\n\t\t\tconst position = geometry.attributes.position;\n\t\t\tconst drawRange = this._drawRanges[ id ];\n\n\t\t\tlet maxRadiusSq = 0;\n\t\t\tfor ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i ++ ) {\n\n\t\t\t\tlet iv = i;\n\t\t\t\tif ( index ) {\n\n\t\t\t\t\tiv = index.getX( iv );\n\n\t\t\t\t}\n\n\t\t\t\t_vector$5.fromBufferAttribute( position, iv );\n\t\t\t\tmaxRadiusSq = Math.max( maxRadiusSq, sphere.center.distanceToSquared( _vector$5 ) );\n\n\t\t\t}\n\n\t\t\tsphere.radius = Math.sqrt( maxRadiusSq );\n\t\t\tbound.sphereInitialized = true;\n\n\t\t}\n\n\t\ttarget.copy( sphere );\n\t\treturn target;\n\n\t}\n\n\tsetMatrixAt( geometryId, matrix ) {\n\n\t\t// @TODO: Map geometryId to index of the arrays because\n\t\t//        optimize() can make geometryId mismatch the index\n\n\t\tconst active = this._active;\n\t\tconst matricesTexture = this._matricesTexture;\n\t\tconst matricesArray = this._matricesTexture.image.data;\n\t\tconst geometryCount = this._geometryCount;\n\t\tif ( geometryId >= geometryCount || active[ geometryId ] === false ) {\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tmatrix.toArray( matricesArray, geometryId * 16 );\n\t\tmatricesTexture.needsUpdate = true;\n\n\t\treturn this;\n\n\t}\n\n\tgetMatrixAt( geometryId, matrix ) {\n\n\t\tconst active = this._active;\n\t\tconst matricesArray = this._matricesTexture.image.data;\n\t\tconst geometryCount = this._geometryCount;\n\t\tif ( geometryId >= geometryCount || active[ geometryId ] === false ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\treturn matrix.fromArray( matricesArray, geometryId * 16 );\n\n\t}\n\n\tsetVisibleAt( geometryId, value ) {\n\n\t\tconst visibility = this._visibility;\n\t\tconst active = this._active;\n\t\tconst geometryCount = this._geometryCount;\n\n\t\t// if the geometry is out of range, not active, or visibility state\n\t\t// does not change then return early\n\t\tif (\n\t\t\tgeometryId >= geometryCount ||\n\t\t\tactive[ geometryId ] === false ||\n\t\t\tvisibility[ geometryId ] === value\n\t\t) {\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tvisibility[ geometryId ] = value;\n\t\tthis._visibilityChanged = true;\n\n\t\treturn this;\n\n\t}\n\n\tgetVisibleAt( geometryId ) {\n\n\t\tconst visibility = this._visibility;\n\t\tconst active = this._active;\n\t\tconst geometryCount = this._geometryCount;\n\n\t\t// return early if the geometry is out of range or not active\n\t\tif ( geometryId >= geometryCount || active[ geometryId ] === false ) {\n\n\t\t\treturn false;\n\n\t\t}\n\n\t\treturn visibility[ geometryId ];\n\n\t}\n\n\traycast( raycaster, intersects ) {\n\n\t\tconst visibility = this._visibility;\n\t\tconst active = this._active;\n\t\tconst drawRanges = this._drawRanges;\n\t\tconst geometryCount = this._geometryCount;\n\t\tconst matrixWorld = this.matrixWorld;\n\t\tconst batchGeometry = this.geometry;\n\n\t\t// iterate over each geometry\n\t\t_mesh.material = this.material;\n\t\t_mesh.geometry.index = batchGeometry.index;\n\t\t_mesh.geometry.attributes = batchGeometry.attributes;\n\t\tif ( _mesh.geometry.boundingBox === null ) {\n\n\t\t\t_mesh.geometry.boundingBox = new Box3();\n\n\t\t}\n\n\t\tif ( _mesh.geometry.boundingSphere === null ) {\n\n\t\t\t_mesh.geometry.boundingSphere = new Sphere();\n\n\t\t}\n\n\t\tfor ( let i = 0; i < geometryCount; i ++ ) {\n\n\t\t\tif ( ! visibility[ i ] || ! active[ i ] ) {\n\n\t\t\t\tcontinue;\n\n\t\t\t}\n\n\t\t\tconst drawRange = drawRanges[ i ];\n\t\t\t_mesh.geometry.setDrawRange( drawRange.start, drawRange.count );\n\n\t\t\t// ge the intersects\n\t\t\tthis.getMatrixAt( i, _mesh.matrixWorld ).premultiply( matrixWorld );\n\t\t\tthis.getBoundingBoxAt( i, _mesh.geometry.boundingBox );\n\t\t\tthis.getBoundingSphereAt( i, _mesh.geometry.boundingSphere );\n\t\t\t_mesh.raycast( raycaster, _batchIntersects );\n\n\t\t\t// add batch id to the intersects\n\t\t\tfor ( let j = 0, l = _batchIntersects.length; j < l; j ++ ) {\n\n\t\t\t\tconst intersect = _batchIntersects[ j ];\n\t\t\t\tintersect.object = this;\n\t\t\t\tintersect.batchId = i;\n\t\t\t\tintersects.push( intersect );\n\n\t\t\t}\n\n\t\t\t_batchIntersects.length = 0;\n\n\t\t}\n\n\t\t_mesh.material = null;\n\t\t_mesh.geometry.index = null;\n\t\t_mesh.geometry.attributes = {};\n\t\t_mesh.geometry.setDrawRange( 0, Infinity );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.geometry = source.geometry.clone();\n\t\tthis.perObjectFrustumCulled = source.perObjectFrustumCulled;\n\t\tthis.sortObjects = source.sortObjects;\n\t\tthis.boundingBox = source.boundingBox !== null ? source.boundingBox.clone() : null;\n\t\tthis.boundingSphere = source.boundingSphere !== null ? source.boundingSphere.clone() : null;\n\n\t\tthis._drawRanges = source._drawRanges.map( range => ( { ...range } ) );\n\t\tthis._reservedRanges = source._reservedRanges.map( range => ( { ...range } ) );\n\n\t\tthis._visibility = source._visibility.slice();\n\t\tthis._active = source._active.slice();\n\t\tthis._bounds = source._bounds.map( bound => ( {\n\t\t\tboxInitialized: bound.boxInitialized,\n\t\t\tbox: bound.box.clone(),\n\n\t\t\tsphereInitialized: bound.sphereInitialized,\n\t\t\tsphere: bound.sphere.clone()\n\t\t} ) );\n\n\t\tthis._maxGeometryCount = source._maxGeometryCount;\n\t\tthis._maxVertexCount = source._maxVertexCount;\n\t\tthis._maxIndexCount = source._maxIndexCount;\n\n\t\tthis._geometryInitialized = source._geometryInitialized;\n\t\tthis._geometryCount = source._geometryCount;\n\t\tthis._multiDrawCounts = source._multiDrawCounts.slice();\n\t\tthis._multiDrawStarts = source._multiDrawStarts.slice();\n\n\t\tthis._matricesTexture = source._matricesTexture.clone();\n\t\tthis._matricesTexture.image.data = this._matricesTexture.image.slice();\n\n\t\treturn this;\n\n\t}\n\n\tdispose() {\n\n\t\t// Assuming the geometry is not shared with other meshes\n\t\tthis.geometry.dispose();\n\n\t\tthis._matricesTexture.dispose();\n\t\tthis._matricesTexture = null;\n\t\treturn this;\n\n\t}\n\n\tonBeforeRender( renderer, scene, camera, geometry, material/*, _group*/ ) {\n\n\t\t// if visibility has not changed and frustum culling and object sorting is not required\n\t\t// then skip iterating over all items\n\t\tif ( ! this._visibilityChanged && ! this.perObjectFrustumCulled && ! this.sortObjects ) {\n\n\t\t\treturn;\n\n\t\t}\n\n\t\t// the indexed version of the multi draw function requires specifying the start\n\t\t// offset in bytes.\n\t\tconst index = geometry.getIndex();\n\t\tconst bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT;\n\n\t\tconst visibility = this._visibility;\n\t\tconst multiDrawStarts = this._multiDrawStarts;\n\t\tconst multiDrawCounts = this._multiDrawCounts;\n\t\tconst drawRanges = this._drawRanges;\n\t\tconst perObjectFrustumCulled = this.perObjectFrustumCulled;\n\n\t\t// prepare the frustum in the local frame\n\t\tif ( perObjectFrustumCulled ) {\n\n\t\t\t_projScreenMatrix$2\n\t\t\t\t.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse )\n\t\t\t\t.multiply( this.matrixWorld );\n\t\t\t_frustum.setFromProjectionMatrix(\n\t\t\t\t_projScreenMatrix$2,\n\t\t\t\trenderer.isWebGPURenderer ? WebGPUCoordinateSystem : WebGLCoordinateSystem\n\t\t\t);\n\n\t\t}\n\n\t\tlet count = 0;\n\t\tif ( this.sortObjects ) {\n\n\t\t\t// get the camera position in the local frame\n\t\t\t_invMatrixWorld.copy( this.matrixWorld ).invert();\n\t\t\t_vector$5.setFromMatrixPosition( camera.matrixWorld ).applyMatrix4( _invMatrixWorld );\n\n\t\t\tfor ( let i = 0, l = visibility.length; i < l; i ++ ) {\n\n\t\t\t\tif ( visibility[ i ] ) {\n\n\t\t\t\t\t// get the bounds in world space\n\t\t\t\t\tthis.getMatrixAt( i, _matrix );\n\t\t\t\t\tthis.getBoundingSphereAt( i, _sphere$2 ).applyMatrix4( _matrix );\n\n\t\t\t\t\t// determine whether the batched geometry is within the frustum\n\t\t\t\t\tlet culled = false;\n\t\t\t\t\tif ( perObjectFrustumCulled ) {\n\n\t\t\t\t\t\tculled = ! _frustum.intersectsSphere( _sphere$2 );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( ! culled ) {\n\n\t\t\t\t\t\t// get the distance from camera used for sorting\n\t\t\t\t\t\tconst z = _vector$5.distanceTo( _sphere$2.center );\n\t\t\t\t\t\t_renderList.push( drawRanges[ i ], z );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Sort the draw ranges and prep for rendering\n\t\t\tconst list = _renderList.list;\n\t\t\tconst customSort = this.customSort;\n\t\t\tif ( customSort === null ) {\n\n\t\t\t\tlist.sort( material.transparent ? sortTransparent : sortOpaque );\n\n\t\t\t} else {\n\n\t\t\t\tcustomSort.call( this, list, camera );\n\n\t\t\t}\n\n\t\t\tfor ( let i = 0, l = list.length; i < l; i ++ ) {\n\n\t\t\t\tconst item = list[ i ];\n\t\t\t\tmultiDrawStarts[ count ] = item.start * bytesPerElement;\n\t\t\t\tmultiDrawCounts[ count ] = item.count;\n\t\t\t\tcount ++;\n\n\t\t\t}\n\n\t\t\t_renderList.reset();\n\n\t\t} else {\n\n\t\t\tfor ( let i = 0, l = visibility.length; i < l; i ++ ) {\n\n\t\t\t\tif ( visibility[ i ] ) {\n\n\t\t\t\t\t// determine whether the batched geometry is within the frustum\n\t\t\t\t\tlet culled = false;\n\t\t\t\t\tif ( perObjectFrustumCulled ) {\n\n\t\t\t\t\t\t// get the bounds in world space\n\t\t\t\t\t\tthis.getMatrixAt( i, _matrix );\n\t\t\t\t\t\tthis.getBoundingSphereAt( i, _sphere$2 ).applyMatrix4( _matrix );\n\t\t\t\t\t\tculled = ! _frustum.intersectsSphere( _sphere$2 );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( ! culled ) {\n\n\t\t\t\t\t\tconst range = drawRanges[ i ];\n\t\t\t\t\t\tmultiDrawStarts[ count ] = range.start * bytesPerElement;\n\t\t\t\t\t\tmultiDrawCounts[ count ] = range.count;\n\t\t\t\t\t\tcount ++;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis._multiDrawCount = count;\n\t\tthis._visibilityChanged = false;\n\n\t}\n\n\tonBeforeShadow( renderer, object, camera, shadowCamera, geometry, depthMaterial/* , group */ ) {\n\n\t\tthis.onBeforeRender( renderer, null, shadowCamera, geometry, depthMaterial );\n\n\t}\n\n}\n\nclass LineBasicMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isLineBasicMaterial = true;\n\n\t\tthis.type = 'LineBasicMaterial';\n\n\t\tthis.color = new Color( 0xffffff );\n\n\t\tthis.map = null;\n\n\t\tthis.linewidth = 1;\n\t\tthis.linecap = 'round';\n\t\tthis.linejoin = 'round';\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.map = source.map;\n\n\t\tthis.linewidth = source.linewidth;\n\t\tthis.linecap = source.linecap;\n\t\tthis.linejoin = source.linejoin;\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\nconst _start$1 = /*@__PURE__*/ new Vector3();\nconst _end$1 = /*@__PURE__*/ new Vector3();\nconst _inverseMatrix$1 = /*@__PURE__*/ new Matrix4();\nconst _ray$1 = /*@__PURE__*/ new Ray();\nconst _sphere$1 = /*@__PURE__*/ new Sphere();\n\nclass Line extends Object3D {\n\n\tconstructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) {\n\n\t\tsuper();\n\n\t\tthis.isLine = true;\n\n\t\tthis.type = 'Line';\n\n\t\tthis.geometry = geometry;\n\t\tthis.material = material;\n\n\t\tthis.updateMorphTargets();\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.material = Array.isArray( source.material ) ? source.material.slice() : source.material;\n\t\tthis.geometry = source.geometry;\n\n\t\treturn this;\n\n\t}\n\n\tcomputeLineDistances() {\n\n\t\tconst geometry = this.geometry;\n\n\t\t// we assume non-indexed geometry\n\n\t\tif ( geometry.index === null ) {\n\n\t\t\tconst positionAttribute = geometry.attributes.position;\n\t\t\tconst lineDistances = [ 0 ];\n\n\t\t\tfor ( let i = 1, l = positionAttribute.count; i < l; i ++ ) {\n\n\t\t\t\t_start$1.fromBufferAttribute( positionAttribute, i - 1 );\n\t\t\t\t_end$1.fromBufferAttribute( positionAttribute, i );\n\n\t\t\t\tlineDistances[ i ] = lineDistances[ i - 1 ];\n\t\t\t\tlineDistances[ i ] += _start$1.distanceTo( _end$1 );\n\n\t\t\t}\n\n\t\t\tgeometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) );\n\n\t\t} else {\n\n\t\t\tconsole.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\traycast( raycaster, intersects ) {\n\n\t\tconst geometry = this.geometry;\n\t\tconst matrixWorld = this.matrixWorld;\n\t\tconst threshold = raycaster.params.Line.threshold;\n\t\tconst drawRange = geometry.drawRange;\n\n\t\t// Checking boundingSphere distance to ray\n\n\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n\t\t_sphere$1.copy( geometry.boundingSphere );\n\t\t_sphere$1.applyMatrix4( matrixWorld );\n\t\t_sphere$1.radius += threshold;\n\n\t\tif ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return;\n\n\t\t//\n\n\t\t_inverseMatrix$1.copy( matrixWorld ).invert();\n\t\t_ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 );\n\n\t\tconst localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );\n\t\tconst localThresholdSq = localThreshold * localThreshold;\n\n\t\tconst vStart = new Vector3();\n\t\tconst vEnd = new Vector3();\n\t\tconst interSegment = new Vector3();\n\t\tconst interRay = new Vector3();\n\t\tconst step = this.isLineSegments ? 2 : 1;\n\n\t\tconst index = geometry.index;\n\t\tconst attributes = geometry.attributes;\n\t\tconst positionAttribute = attributes.position;\n\n\t\tif ( index !== null ) {\n\n\t\t\tconst start = Math.max( 0, drawRange.start );\n\t\t\tconst end = Math.min( index.count, ( drawRange.start + drawRange.count ) );\n\n\t\t\tfor ( let i = start, l = end - 1; i < l; i += step ) {\n\n\t\t\t\tconst a = index.getX( i );\n\t\t\t\tconst b = index.getX( i + 1 );\n\n\t\t\t\tvStart.fromBufferAttribute( positionAttribute, a );\n\t\t\t\tvEnd.fromBufferAttribute( positionAttribute, b );\n\n\t\t\t\tconst distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment );\n\n\t\t\t\tif ( distSq > localThresholdSq ) continue;\n\n\t\t\t\tinterRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n\t\t\t\tconst distance = raycaster.ray.origin.distanceTo( interRay );\n\n\t\t\t\tif ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n\t\t\t\tintersects.push( {\n\n\t\t\t\t\tdistance: distance,\n\t\t\t\t\t// What do we want? intersection point on the ray or on the segment??\n\t\t\t\t\t// point: raycaster.ray.at( distance ),\n\t\t\t\t\tpoint: interSegment.clone().applyMatrix4( this.matrixWorld ),\n\t\t\t\t\tindex: i,\n\t\t\t\t\tface: null,\n\t\t\t\t\tfaceIndex: null,\n\t\t\t\t\tobject: this\n\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tconst start = Math.max( 0, drawRange.start );\n\t\t\tconst end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) );\n\n\t\t\tfor ( let i = start, l = end - 1; i < l; i += step ) {\n\n\t\t\t\tvStart.fromBufferAttribute( positionAttribute, i );\n\t\t\t\tvEnd.fromBufferAttribute( positionAttribute, i + 1 );\n\n\t\t\t\tconst distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment );\n\n\t\t\t\tif ( distSq > localThresholdSq ) continue;\n\n\t\t\t\tinterRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n\t\t\t\tconst distance = raycaster.ray.origin.distanceTo( interRay );\n\n\t\t\t\tif ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n\t\t\t\tintersects.push( {\n\n\t\t\t\t\tdistance: distance,\n\t\t\t\t\t// What do we want? intersection point on the ray or on the segment??\n\t\t\t\t\t// point: raycaster.ray.at( distance ),\n\t\t\t\t\tpoint: interSegment.clone().applyMatrix4( this.matrixWorld ),\n\t\t\t\t\tindex: i,\n\t\t\t\t\tface: null,\n\t\t\t\t\tfaceIndex: null,\n\t\t\t\t\tobject: this\n\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tupdateMorphTargets() {\n\n\t\tconst geometry = this.geometry;\n\n\t\tconst morphAttributes = geometry.morphAttributes;\n\t\tconst keys = Object.keys( morphAttributes );\n\n\t\tif ( keys.length > 0 ) {\n\n\t\t\tconst morphAttribute = morphAttributes[ keys[ 0 ] ];\n\n\t\t\tif ( morphAttribute !== undefined ) {\n\n\t\t\t\tthis.morphTargetInfluences = [];\n\t\t\t\tthis.morphTargetDictionary = {};\n\n\t\t\t\tfor ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {\n\n\t\t\t\t\tconst name = morphAttribute[ m ].name || String( m );\n\n\t\t\t\t\tthis.morphTargetInfluences.push( 0 );\n\t\t\t\t\tthis.morphTargetDictionary[ name ] = m;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n\nconst _start = /*@__PURE__*/ new Vector3();\nconst _end = /*@__PURE__*/ new Vector3();\n\nclass LineSegments extends Line {\n\n\tconstructor( geometry, material ) {\n\n\t\tsuper( geometry, material );\n\n\t\tthis.isLineSegments = true;\n\n\t\tthis.type = 'LineSegments';\n\n\t}\n\n\tcomputeLineDistances() {\n\n\t\tconst geometry = this.geometry;\n\n\t\t// we assume non-indexed geometry\n\n\t\tif ( geometry.index === null ) {\n\n\t\t\tconst positionAttribute = geometry.attributes.position;\n\t\t\tconst lineDistances = [];\n\n\t\t\tfor ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) {\n\n\t\t\t\t_start.fromBufferAttribute( positionAttribute, i );\n\t\t\t\t_end.fromBufferAttribute( positionAttribute, i + 1 );\n\n\t\t\t\tlineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ];\n\t\t\t\tlineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end );\n\n\t\t\t}\n\n\t\t\tgeometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) );\n\n\t\t} else {\n\n\t\t\tconsole.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass LineLoop extends Line {\n\n\tconstructor( geometry, material ) {\n\n\t\tsuper( geometry, material );\n\n\t\tthis.isLineLoop = true;\n\n\t\tthis.type = 'LineLoop';\n\n\t}\n\n}\n\nclass PointsMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isPointsMaterial = true;\n\n\t\tthis.type = 'PointsMaterial';\n\n\t\tthis.color = new Color( 0xffffff );\n\n\t\tthis.map = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.size = 1;\n\t\tthis.sizeAttenuation = true;\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.map = source.map;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.size = source.size;\n\t\tthis.sizeAttenuation = source.sizeAttenuation;\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\nconst _inverseMatrix = /*@__PURE__*/ new Matrix4();\nconst _ray = /*@__PURE__*/ new Ray();\nconst _sphere = /*@__PURE__*/ new Sphere();\nconst _position$2 = /*@__PURE__*/ new Vector3();\n\nclass Points extends Object3D {\n\n\tconstructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) {\n\n\t\tsuper();\n\n\t\tthis.isPoints = true;\n\n\t\tthis.type = 'Points';\n\n\t\tthis.geometry = geometry;\n\t\tthis.material = material;\n\n\t\tthis.updateMorphTargets();\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.material = Array.isArray( source.material ) ? source.material.slice() : source.material;\n\t\tthis.geometry = source.geometry;\n\n\t\treturn this;\n\n\t}\n\n\traycast( raycaster, intersects ) {\n\n\t\tconst geometry = this.geometry;\n\t\tconst matrixWorld = this.matrixWorld;\n\t\tconst threshold = raycaster.params.Points.threshold;\n\t\tconst drawRange = geometry.drawRange;\n\n\t\t// Checking boundingSphere distance to ray\n\n\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n\t\t_sphere.copy( geometry.boundingSphere );\n\t\t_sphere.applyMatrix4( matrixWorld );\n\t\t_sphere.radius += threshold;\n\n\t\tif ( raycaster.ray.intersectsSphere( _sphere ) === false ) return;\n\n\t\t//\n\n\t\t_inverseMatrix.copy( matrixWorld ).invert();\n\t\t_ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix );\n\n\t\tconst localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );\n\t\tconst localThresholdSq = localThreshold * localThreshold;\n\n\t\tconst index = geometry.index;\n\t\tconst attributes = geometry.attributes;\n\t\tconst positionAttribute = attributes.position;\n\n\t\tif ( index !== null ) {\n\n\t\t\tconst start = Math.max( 0, drawRange.start );\n\t\t\tconst end = Math.min( index.count, ( drawRange.start + drawRange.count ) );\n\n\t\t\tfor ( let i = start, il = end; i < il; i ++ ) {\n\n\t\t\t\tconst a = index.getX( i );\n\n\t\t\t\t_position$2.fromBufferAttribute( positionAttribute, a );\n\n\t\t\t\ttestPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tconst start = Math.max( 0, drawRange.start );\n\t\t\tconst end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) );\n\n\t\t\tfor ( let i = start, l = end; i < l; i ++ ) {\n\n\t\t\t\t_position$2.fromBufferAttribute( positionAttribute, i );\n\n\t\t\t\ttestPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tupdateMorphTargets() {\n\n\t\tconst geometry = this.geometry;\n\n\t\tconst morphAttributes = geometry.morphAttributes;\n\t\tconst keys = Object.keys( morphAttributes );\n\n\t\tif ( keys.length > 0 ) {\n\n\t\t\tconst morphAttribute = morphAttributes[ keys[ 0 ] ];\n\n\t\t\tif ( morphAttribute !== undefined ) {\n\n\t\t\t\tthis.morphTargetInfluences = [];\n\t\t\t\tthis.morphTargetDictionary = {};\n\n\t\t\t\tfor ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {\n\n\t\t\t\t\tconst name = morphAttribute[ m ].name || String( m );\n\n\t\t\t\t\tthis.morphTargetInfluences.push( 0 );\n\t\t\t\t\tthis.morphTargetDictionary[ name ] = m;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n\nfunction testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) {\n\n\tconst rayPointDistanceSq = _ray.distanceSqToPoint( point );\n\n\tif ( rayPointDistanceSq < localThresholdSq ) {\n\n\t\tconst intersectPoint = new Vector3();\n\n\t\t_ray.closestPointToPoint( point, intersectPoint );\n\t\tintersectPoint.applyMatrix4( matrixWorld );\n\n\t\tconst distance = raycaster.ray.origin.distanceTo( intersectPoint );\n\n\t\tif ( distance < raycaster.near || distance > raycaster.far ) return;\n\n\t\tintersects.push( {\n\n\t\t\tdistance: distance,\n\t\t\tdistanceToRay: Math.sqrt( rayPointDistanceSq ),\n\t\t\tpoint: intersectPoint,\n\t\t\tindex: index,\n\t\t\tface: null,\n\t\t\tobject: object\n\n\t\t} );\n\n\t}\n\n}\n\nclass VideoTexture extends Texture {\n\n\tconstructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {\n\n\t\tsuper( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n\t\tthis.isVideoTexture = true;\n\n\t\tthis.minFilter = minFilter !== undefined ? minFilter : LinearFilter;\n\t\tthis.magFilter = magFilter !== undefined ? magFilter : LinearFilter;\n\n\t\tthis.generateMipmaps = false;\n\n\t\tconst scope = this;\n\n\t\tfunction updateVideo() {\n\n\t\t\tscope.needsUpdate = true;\n\t\t\tvideo.requestVideoFrameCallback( updateVideo );\n\n\t\t}\n\n\t\tif ( 'requestVideoFrameCallback' in video ) {\n\n\t\t\tvideo.requestVideoFrameCallback( updateVideo );\n\n\t\t}\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this.image ).copy( this );\n\n\t}\n\n\tupdate() {\n\n\t\tconst video = this.image;\n\t\tconst hasVideoFrameCallback = 'requestVideoFrameCallback' in video;\n\n\t\tif ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) {\n\n\t\t\tthis.needsUpdate = true;\n\n\t\t}\n\n\t}\n\n}\n\nclass FramebufferTexture extends Texture {\n\n\tconstructor( width, height ) {\n\n\t\tsuper( { width, height } );\n\n\t\tthis.isFramebufferTexture = true;\n\n\t\tthis.magFilter = NearestFilter;\n\t\tthis.minFilter = NearestFilter;\n\n\t\tthis.generateMipmaps = false;\n\n\t\tthis.needsUpdate = true;\n\n\t}\n\n}\n\nclass CompressedTexture extends Texture {\n\n\tconstructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, colorSpace ) {\n\n\t\tsuper( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace );\n\n\t\tthis.isCompressedTexture = true;\n\n\t\tthis.image = { width: width, height: height };\n\t\tthis.mipmaps = mipmaps;\n\n\t\t// no flipping for cube textures\n\t\t// (also flipping doesn't work for compressed textures )\n\n\t\tthis.flipY = false;\n\n\t\t// can't generate mipmaps for compressed textures\n\t\t// mips must be embedded in DDS files\n\n\t\tthis.generateMipmaps = false;\n\n\t}\n\n}\n\nclass CompressedArrayTexture extends CompressedTexture {\n\n\tconstructor( mipmaps, width, height, depth, format, type ) {\n\n\t\tsuper( mipmaps, width, height, format, type );\n\n\t\tthis.isCompressedArrayTexture = true;\n\t\tthis.image.depth = depth;\n\t\tthis.wrapR = ClampToEdgeWrapping;\n\n\t}\n\n}\n\nclass CompressedCubeTexture extends CompressedTexture {\n\n\tconstructor( images, format, type ) {\n\n\t\tsuper( undefined, images[ 0 ].width, images[ 0 ].height, format, type, CubeReflectionMapping );\n\n\t\tthis.isCompressedCubeTexture = true;\n\t\tthis.isCubeTexture = true;\n\n\t\tthis.image = images;\n\n\t}\n\n}\n\nclass CanvasTexture extends Texture {\n\n\tconstructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {\n\n\t\tsuper( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n\t\tthis.isCanvasTexture = true;\n\n\t\tthis.needsUpdate = true;\n\n\t}\n\n}\n\n/**\n * Extensible curve object.\n *\n * Some common of curve methods:\n * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget )\n * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget )\n * .getPoints(), .getSpacedPoints()\n * .getLength()\n * .updateArcLengths()\n *\n * This following curves inherit from THREE.Curve:\n *\n * -- 2D curves --\n * THREE.ArcCurve\n * THREE.CubicBezierCurve\n * THREE.EllipseCurve\n * THREE.LineCurve\n * THREE.QuadraticBezierCurve\n * THREE.SplineCurve\n *\n * -- 3D curves --\n * THREE.CatmullRomCurve3\n * THREE.CubicBezierCurve3\n * THREE.LineCurve3\n * THREE.QuadraticBezierCurve3\n *\n * A series of curves can be represented as a THREE.CurvePath.\n *\n **/\n\nclass Curve {\n\n\tconstructor() {\n\n\t\tthis.type = 'Curve';\n\n\t\tthis.arcLengthDivisions = 200;\n\n\t}\n\n\t// Virtual base class method to overwrite and implement in subclasses\n\t//\t- t [0 .. 1]\n\n\tgetPoint( /* t, optionalTarget */ ) {\n\n\t\tconsole.warn( 'THREE.Curve: .getPoint() not implemented.' );\n\t\treturn null;\n\n\t}\n\n\t// Get point at relative position in curve according to arc length\n\t// - u [0 .. 1]\n\n\tgetPointAt( u, optionalTarget ) {\n\n\t\tconst t = this.getUtoTmapping( u );\n\t\treturn this.getPoint( t, optionalTarget );\n\n\t}\n\n\t// Get sequence of points using getPoint( t )\n\n\tgetPoints( divisions = 5 ) {\n\n\t\tconst points = [];\n\n\t\tfor ( let d = 0; d <= divisions; d ++ ) {\n\n\t\t\tpoints.push( this.getPoint( d / divisions ) );\n\n\t\t}\n\n\t\treturn points;\n\n\t}\n\n\t// Get sequence of points using getPointAt( u )\n\n\tgetSpacedPoints( divisions = 5 ) {\n\n\t\tconst points = [];\n\n\t\tfor ( let d = 0; d <= divisions; d ++ ) {\n\n\t\t\tpoints.push( this.getPointAt( d / divisions ) );\n\n\t\t}\n\n\t\treturn points;\n\n\t}\n\n\t// Get total curve arc length\n\n\tgetLength() {\n\n\t\tconst lengths = this.getLengths();\n\t\treturn lengths[ lengths.length - 1 ];\n\n\t}\n\n\t// Get list of cumulative segment lengths\n\n\tgetLengths( divisions = this.arcLengthDivisions ) {\n\n\t\tif ( this.cacheArcLengths &&\n\t\t\t( this.cacheArcLengths.length === divisions + 1 ) &&\n\t\t\t! this.needsUpdate ) {\n\n\t\t\treturn this.cacheArcLengths;\n\n\t\t}\n\n\t\tthis.needsUpdate = false;\n\n\t\tconst cache = [];\n\t\tlet current, last = this.getPoint( 0 );\n\t\tlet sum = 0;\n\n\t\tcache.push( 0 );\n\n\t\tfor ( let p = 1; p <= divisions; p ++ ) {\n\n\t\t\tcurrent = this.getPoint( p / divisions );\n\t\t\tsum += current.distanceTo( last );\n\t\t\tcache.push( sum );\n\t\t\tlast = current;\n\n\t\t}\n\n\t\tthis.cacheArcLengths = cache;\n\n\t\treturn cache; // { sums: cache, sum: sum }; Sum is in the last element.\n\n\t}\n\n\tupdateArcLengths() {\n\n\t\tthis.needsUpdate = true;\n\t\tthis.getLengths();\n\n\t}\n\n\t// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant\n\n\tgetUtoTmapping( u, distance ) {\n\n\t\tconst arcLengths = this.getLengths();\n\n\t\tlet i = 0;\n\t\tconst il = arcLengths.length;\n\n\t\tlet targetArcLength; // The targeted u distance value to get\n\n\t\tif ( distance ) {\n\n\t\t\ttargetArcLength = distance;\n\n\t\t} else {\n\n\t\t\ttargetArcLength = u * arcLengths[ il - 1 ];\n\n\t\t}\n\n\t\t// binary search for the index with largest value smaller than target u distance\n\n\t\tlet low = 0, high = il - 1, comparison;\n\n\t\twhile ( low <= high ) {\n\n\t\t\ti = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats\n\n\t\t\tcomparison = arcLengths[ i ] - targetArcLength;\n\n\t\t\tif ( comparison < 0 ) {\n\n\t\t\t\tlow = i + 1;\n\n\t\t\t} else if ( comparison > 0 ) {\n\n\t\t\t\thigh = i - 1;\n\n\t\t\t} else {\n\n\t\t\t\thigh = i;\n\t\t\t\tbreak;\n\n\t\t\t\t// DONE\n\n\t\t\t}\n\n\t\t}\n\n\t\ti = high;\n\n\t\tif ( arcLengths[ i ] === targetArcLength ) {\n\n\t\t\treturn i / ( il - 1 );\n\n\t\t}\n\n\t\t// we could get finer grain at lengths, or use simple interpolation between two points\n\n\t\tconst lengthBefore = arcLengths[ i ];\n\t\tconst lengthAfter = arcLengths[ i + 1 ];\n\n\t\tconst segmentLength = lengthAfter - lengthBefore;\n\n\t\t// determine where we are between the 'before' and 'after' points\n\n\t\tconst segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;\n\n\t\t// add that fractional amount to t\n\n\t\tconst t = ( i + segmentFraction ) / ( il - 1 );\n\n\t\treturn t;\n\n\t}\n\n\t// Returns a unit vector tangent at t\n\t// In case any sub curve does not implement its tangent derivation,\n\t// 2 points a small delta apart will be used to find its gradient\n\t// which seems to give a reasonable approximation\n\n\tgetTangent( t, optionalTarget ) {\n\n\t\tconst delta = 0.0001;\n\t\tlet t1 = t - delta;\n\t\tlet t2 = t + delta;\n\n\t\t// Capping in case of danger\n\n\t\tif ( t1 < 0 ) t1 = 0;\n\t\tif ( t2 > 1 ) t2 = 1;\n\n\t\tconst pt1 = this.getPoint( t1 );\n\t\tconst pt2 = this.getPoint( t2 );\n\n\t\tconst tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() );\n\n\t\ttangent.copy( pt2 ).sub( pt1 ).normalize();\n\n\t\treturn tangent;\n\n\t}\n\n\tgetTangentAt( u, optionalTarget ) {\n\n\t\tconst t = this.getUtoTmapping( u );\n\t\treturn this.getTangent( t, optionalTarget );\n\n\t}\n\n\tcomputeFrenetFrames( segments, closed ) {\n\n\t\t// see http://www.cs.indiana.edu/pub/techreports/TR425.pdf\n\n\t\tconst normal = new Vector3();\n\n\t\tconst tangents = [];\n\t\tconst normals = [];\n\t\tconst binormals = [];\n\n\t\tconst vec = new Vector3();\n\t\tconst mat = new Matrix4();\n\n\t\t// compute the tangent vectors for each segment on the curve\n\n\t\tfor ( let i = 0; i <= segments; i ++ ) {\n\n\t\t\tconst u = i / segments;\n\n\t\t\ttangents[ i ] = this.getTangentAt( u, new Vector3() );\n\n\t\t}\n\n\t\t// select an initial normal vector perpendicular to the first tangent vector,\n\t\t// and in the direction of the minimum tangent xyz component\n\n\t\tnormals[ 0 ] = new Vector3();\n\t\tbinormals[ 0 ] = new Vector3();\n\t\tlet min = Number.MAX_VALUE;\n\t\tconst tx = Math.abs( tangents[ 0 ].x );\n\t\tconst ty = Math.abs( tangents[ 0 ].y );\n\t\tconst tz = Math.abs( tangents[ 0 ].z );\n\n\t\tif ( tx <= min ) {\n\n\t\t\tmin = tx;\n\t\t\tnormal.set( 1, 0, 0 );\n\n\t\t}\n\n\t\tif ( ty <= min ) {\n\n\t\t\tmin = ty;\n\t\t\tnormal.set( 0, 1, 0 );\n\n\t\t}\n\n\t\tif ( tz <= min ) {\n\n\t\t\tnormal.set( 0, 0, 1 );\n\n\t\t}\n\n\t\tvec.crossVectors( tangents[ 0 ], normal ).normalize();\n\n\t\tnormals[ 0 ].crossVectors( tangents[ 0 ], vec );\n\t\tbinormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );\n\n\n\t\t// compute the slowly-varying normal and binormal vectors for each segment on the curve\n\n\t\tfor ( let i = 1; i <= segments; i ++ ) {\n\n\t\t\tnormals[ i ] = normals[ i - 1 ].clone();\n\n\t\t\tbinormals[ i ] = binormals[ i - 1 ].clone();\n\n\t\t\tvec.crossVectors( tangents[ i - 1 ], tangents[ i ] );\n\n\t\t\tif ( vec.length() > Number.EPSILON ) {\n\n\t\t\t\tvec.normalize();\n\n\t\t\t\tconst theta = Math.acos( clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors\n\n\t\t\t\tnormals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );\n\n\t\t\t}\n\n\t\t\tbinormals[ i ].crossVectors( tangents[ i ], normals[ i ] );\n\n\t\t}\n\n\t\t// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same\n\n\t\tif ( closed === true ) {\n\n\t\t\tlet theta = Math.acos( clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) );\n\t\t\ttheta /= segments;\n\n\t\t\tif ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) {\n\n\t\t\t\ttheta = - theta;\n\n\t\t\t}\n\n\t\t\tfor ( let i = 1; i <= segments; i ++ ) {\n\n\t\t\t\t// twist a little...\n\t\t\t\tnormals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );\n\t\t\t\tbinormals[ i ].crossVectors( tangents[ i ], normals[ i ] );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn {\n\t\t\ttangents: tangents,\n\t\t\tnormals: normals,\n\t\t\tbinormals: binormals\n\t\t};\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.arcLengthDivisions = source.arcLengthDivisions;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = {\n\t\t\tmetadata: {\n\t\t\t\tversion: 4.6,\n\t\t\t\ttype: 'Curve',\n\t\t\t\tgenerator: 'Curve.toJSON'\n\t\t\t}\n\t\t};\n\n\t\tdata.arcLengthDivisions = this.arcLengthDivisions;\n\t\tdata.type = this.type;\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tthis.arcLengthDivisions = json.arcLengthDivisions;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass EllipseCurve extends Curve {\n\n\tconstructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0 ) {\n\n\t\tsuper();\n\n\t\tthis.isEllipseCurve = true;\n\n\t\tthis.type = 'EllipseCurve';\n\n\t\tthis.aX = aX;\n\t\tthis.aY = aY;\n\n\t\tthis.xRadius = xRadius;\n\t\tthis.yRadius = yRadius;\n\n\t\tthis.aStartAngle = aStartAngle;\n\t\tthis.aEndAngle = aEndAngle;\n\n\t\tthis.aClockwise = aClockwise;\n\n\t\tthis.aRotation = aRotation;\n\n\t}\n\n\tgetPoint( t, optionalTarget ) {\n\n\t\tconst point = optionalTarget || new Vector2();\n\n\t\tconst twoPi = Math.PI * 2;\n\t\tlet deltaAngle = this.aEndAngle - this.aStartAngle;\n\t\tconst samePoints = Math.abs( deltaAngle ) < Number.EPSILON;\n\n\t\t// ensures that deltaAngle is 0 .. 2 PI\n\t\twhile ( deltaAngle < 0 ) deltaAngle += twoPi;\n\t\twhile ( deltaAngle > twoPi ) deltaAngle -= twoPi;\n\n\t\tif ( deltaAngle < Number.EPSILON ) {\n\n\t\t\tif ( samePoints ) {\n\n\t\t\t\tdeltaAngle = 0;\n\n\t\t\t} else {\n\n\t\t\t\tdeltaAngle = twoPi;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( this.aClockwise === true && ! samePoints ) {\n\n\t\t\tif ( deltaAngle === twoPi ) {\n\n\t\t\t\tdeltaAngle = - twoPi;\n\n\t\t\t} else {\n\n\t\t\t\tdeltaAngle = deltaAngle - twoPi;\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst angle = this.aStartAngle + t * deltaAngle;\n\t\tlet x = this.aX + this.xRadius * Math.cos( angle );\n\t\tlet y = this.aY + this.yRadius * Math.sin( angle );\n\n\t\tif ( this.aRotation !== 0 ) {\n\n\t\t\tconst cos = Math.cos( this.aRotation );\n\t\t\tconst sin = Math.sin( this.aRotation );\n\n\t\t\tconst tx = x - this.aX;\n\t\t\tconst ty = y - this.aY;\n\n\t\t\t// Rotate the point about the center of the ellipse.\n\t\t\tx = tx * cos - ty * sin + this.aX;\n\t\t\ty = tx * sin + ty * cos + this.aY;\n\n\t\t}\n\n\t\treturn point.set( x, y );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.aX = source.aX;\n\t\tthis.aY = source.aY;\n\n\t\tthis.xRadius = source.xRadius;\n\t\tthis.yRadius = source.yRadius;\n\n\t\tthis.aStartAngle = source.aStartAngle;\n\t\tthis.aEndAngle = source.aEndAngle;\n\n\t\tthis.aClockwise = source.aClockwise;\n\n\t\tthis.aRotation = source.aRotation;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.aX = this.aX;\n\t\tdata.aY = this.aY;\n\n\t\tdata.xRadius = this.xRadius;\n\t\tdata.yRadius = this.yRadius;\n\n\t\tdata.aStartAngle = this.aStartAngle;\n\t\tdata.aEndAngle = this.aEndAngle;\n\n\t\tdata.aClockwise = this.aClockwise;\n\n\t\tdata.aRotation = this.aRotation;\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tsuper.fromJSON( json );\n\n\t\tthis.aX = json.aX;\n\t\tthis.aY = json.aY;\n\n\t\tthis.xRadius = json.xRadius;\n\t\tthis.yRadius = json.yRadius;\n\n\t\tthis.aStartAngle = json.aStartAngle;\n\t\tthis.aEndAngle = json.aEndAngle;\n\n\t\tthis.aClockwise = json.aClockwise;\n\n\t\tthis.aRotation = json.aRotation;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass ArcCurve extends EllipseCurve {\n\n\tconstructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {\n\n\t\tsuper( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );\n\n\t\tthis.isArcCurve = true;\n\n\t\tthis.type = 'ArcCurve';\n\n\t}\n\n}\n\n/**\n * Centripetal CatmullRom Curve - which is useful for avoiding\n * cusps and self-intersections in non-uniform catmull rom curves.\n * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf\n *\n * curve.type accepts centripetal(default), chordal and catmullrom\n * curve.tension is used for catmullrom which defaults to 0.5\n */\n\n\n/*\nBased on an optimized c++ solution in\n - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/\n - http://ideone.com/NoEbVM\n\nThis CubicPoly class could be used for reusing some variables and calculations,\nbut for three.js curve use, it could be possible inlined and flatten into a single function call\nwhich can be placed in CurveUtils.\n*/\n\nfunction CubicPoly() {\n\n\tlet c0 = 0, c1 = 0, c2 = 0, c3 = 0;\n\n\t/*\n\t * Compute coefficients for a cubic polynomial\n\t *   p(s) = c0 + c1*s + c2*s^2 + c3*s^3\n\t * such that\n\t *   p(0) = x0, p(1) = x1\n\t *  and\n\t *   p'(0) = t0, p'(1) = t1.\n\t */\n\tfunction init( x0, x1, t0, t1 ) {\n\n\t\tc0 = x0;\n\t\tc1 = t0;\n\t\tc2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1;\n\t\tc3 = 2 * x0 - 2 * x1 + t0 + t1;\n\n\t}\n\n\treturn {\n\n\t\tinitCatmullRom: function ( x0, x1, x2, x3, tension ) {\n\n\t\t\tinit( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) );\n\n\t\t},\n\n\t\tinitNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) {\n\n\t\t\t// compute tangents when parameterized in [t1,t2]\n\t\t\tlet t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1;\n\t\t\tlet t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2;\n\n\t\t\t// rescale tangents for parametrization in [0,1]\n\t\t\tt1 *= dt1;\n\t\t\tt2 *= dt1;\n\n\t\t\tinit( x1, x2, t1, t2 );\n\n\t\t},\n\n\t\tcalc: function ( t ) {\n\n\t\t\tconst t2 = t * t;\n\t\t\tconst t3 = t2 * t;\n\t\t\treturn c0 + c1 * t + c2 * t2 + c3 * t3;\n\n\t\t}\n\n\t};\n\n}\n\n//\n\nconst tmp = /*@__PURE__*/ new Vector3();\nconst px = /*@__PURE__*/ new CubicPoly();\nconst py = /*@__PURE__*/ new CubicPoly();\nconst pz = /*@__PURE__*/ new CubicPoly();\n\nclass CatmullRomCurve3 extends Curve {\n\n\tconstructor( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) {\n\n\t\tsuper();\n\n\t\tthis.isCatmullRomCurve3 = true;\n\n\t\tthis.type = 'CatmullRomCurve3';\n\n\t\tthis.points = points;\n\t\tthis.closed = closed;\n\t\tthis.curveType = curveType;\n\t\tthis.tension = tension;\n\n\t}\n\n\tgetPoint( t, optionalTarget = new Vector3() ) {\n\n\t\tconst point = optionalTarget;\n\n\t\tconst points = this.points;\n\t\tconst l = points.length;\n\n\t\tconst p = ( l - ( this.closed ? 0 : 1 ) ) * t;\n\t\tlet intPoint = Math.floor( p );\n\t\tlet weight = p - intPoint;\n\n\t\tif ( this.closed ) {\n\n\t\t\tintPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l;\n\n\t\t} else if ( weight === 0 && intPoint === l - 1 ) {\n\n\t\t\tintPoint = l - 2;\n\t\t\tweight = 1;\n\n\t\t}\n\n\t\tlet p0, p3; // 4 points (p1 & p2 defined below)\n\n\t\tif ( this.closed || intPoint > 0 ) {\n\n\t\t\tp0 = points[ ( intPoint - 1 ) % l ];\n\n\t\t} else {\n\n\t\t\t// extrapolate first point\n\t\t\ttmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] );\n\t\t\tp0 = tmp;\n\n\t\t}\n\n\t\tconst p1 = points[ intPoint % l ];\n\t\tconst p2 = points[ ( intPoint + 1 ) % l ];\n\n\t\tif ( this.closed || intPoint + 2 < l ) {\n\n\t\t\tp3 = points[ ( intPoint + 2 ) % l ];\n\n\t\t} else {\n\n\t\t\t// extrapolate last point\n\t\t\ttmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] );\n\t\t\tp3 = tmp;\n\n\t\t}\n\n\t\tif ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) {\n\n\t\t\t// init Centripetal / Chordal Catmull-Rom\n\t\t\tconst pow = this.curveType === 'chordal' ? 0.5 : 0.25;\n\t\t\tlet dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );\n\t\t\tlet dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );\n\t\t\tlet dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );\n\n\t\t\t// safety check for repeated points\n\t\t\tif ( dt1 < 1e-4 ) dt1 = 1.0;\n\t\t\tif ( dt0 < 1e-4 ) dt0 = dt1;\n\t\t\tif ( dt2 < 1e-4 ) dt2 = dt1;\n\n\t\t\tpx.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 );\n\t\t\tpy.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );\n\t\t\tpz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );\n\n\t\t} else if ( this.curveType === 'catmullrom' ) {\n\n\t\t\tpx.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension );\n\t\t\tpy.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension );\n\t\t\tpz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension );\n\n\t\t}\n\n\t\tpoint.set(\n\t\t\tpx.calc( weight ),\n\t\t\tpy.calc( weight ),\n\t\t\tpz.calc( weight )\n\t\t);\n\n\t\treturn point;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.points = [];\n\n\t\tfor ( let i = 0, l = source.points.length; i < l; i ++ ) {\n\n\t\t\tconst point = source.points[ i ];\n\n\t\t\tthis.points.push( point.clone() );\n\n\t\t}\n\n\t\tthis.closed = source.closed;\n\t\tthis.curveType = source.curveType;\n\t\tthis.tension = source.tension;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.points = [];\n\n\t\tfor ( let i = 0, l = this.points.length; i < l; i ++ ) {\n\n\t\t\tconst point = this.points[ i ];\n\t\t\tdata.points.push( point.toArray() );\n\n\t\t}\n\n\t\tdata.closed = this.closed;\n\t\tdata.curveType = this.curveType;\n\t\tdata.tension = this.tension;\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tsuper.fromJSON( json );\n\n\t\tthis.points = [];\n\n\t\tfor ( let i = 0, l = json.points.length; i < l; i ++ ) {\n\n\t\t\tconst point = json.points[ i ];\n\t\t\tthis.points.push( new Vector3().fromArray( point ) );\n\n\t\t}\n\n\t\tthis.closed = json.closed;\n\t\tthis.curveType = json.curveType;\n\t\tthis.tension = json.tension;\n\n\t\treturn this;\n\n\t}\n\n}\n\n/**\n * Bezier Curves formulas obtained from\n * https://en.wikipedia.org/wiki/B%C3%A9zier_curve\n */\n\nfunction CatmullRom( t, p0, p1, p2, p3 ) {\n\n\tconst v0 = ( p2 - p0 ) * 0.5;\n\tconst v1 = ( p3 - p1 ) * 0.5;\n\tconst t2 = t * t;\n\tconst t3 = t * t2;\n\treturn ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;\n\n}\n\n//\n\nfunction QuadraticBezierP0( t, p ) {\n\n\tconst k = 1 - t;\n\treturn k * k * p;\n\n}\n\nfunction QuadraticBezierP1( t, p ) {\n\n\treturn 2 * ( 1 - t ) * t * p;\n\n}\n\nfunction QuadraticBezierP2( t, p ) {\n\n\treturn t * t * p;\n\n}\n\nfunction QuadraticBezier( t, p0, p1, p2 ) {\n\n\treturn QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) +\n\t\tQuadraticBezierP2( t, p2 );\n\n}\n\n//\n\nfunction CubicBezierP0( t, p ) {\n\n\tconst k = 1 - t;\n\treturn k * k * k * p;\n\n}\n\nfunction CubicBezierP1( t, p ) {\n\n\tconst k = 1 - t;\n\treturn 3 * k * k * t * p;\n\n}\n\nfunction CubicBezierP2( t, p ) {\n\n\treturn 3 * ( 1 - t ) * t * t * p;\n\n}\n\nfunction CubicBezierP3( t, p ) {\n\n\treturn t * t * t * p;\n\n}\n\nfunction CubicBezier( t, p0, p1, p2, p3 ) {\n\n\treturn CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) +\n\t\tCubicBezierP3( t, p3 );\n\n}\n\nclass CubicBezierCurve extends Curve {\n\n\tconstructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) {\n\n\t\tsuper();\n\n\t\tthis.isCubicBezierCurve = true;\n\n\t\tthis.type = 'CubicBezierCurve';\n\n\t\tthis.v0 = v0;\n\t\tthis.v1 = v1;\n\t\tthis.v2 = v2;\n\t\tthis.v3 = v3;\n\n\t}\n\n\tgetPoint( t, optionalTarget = new Vector2() ) {\n\n\t\tconst point = optionalTarget;\n\n\t\tconst v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3;\n\n\t\tpoint.set(\n\t\t\tCubicBezier( t, v0.x, v1.x, v2.x, v3.x ),\n\t\t\tCubicBezier( t, v0.y, v1.y, v2.y, v3.y )\n\t\t);\n\n\t\treturn point;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.v0.copy( source.v0 );\n\t\tthis.v1.copy( source.v1 );\n\t\tthis.v2.copy( source.v2 );\n\t\tthis.v3.copy( source.v3 );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.v0 = this.v0.toArray();\n\t\tdata.v1 = this.v1.toArray();\n\t\tdata.v2 = this.v2.toArray();\n\t\tdata.v3 = this.v3.toArray();\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tsuper.fromJSON( json );\n\n\t\tthis.v0.fromArray( json.v0 );\n\t\tthis.v1.fromArray( json.v1 );\n\t\tthis.v2.fromArray( json.v2 );\n\t\tthis.v3.fromArray( json.v3 );\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass CubicBezierCurve3 extends Curve {\n\n\tconstructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) {\n\n\t\tsuper();\n\n\t\tthis.isCubicBezierCurve3 = true;\n\n\t\tthis.type = 'CubicBezierCurve3';\n\n\t\tthis.v0 = v0;\n\t\tthis.v1 = v1;\n\t\tthis.v2 = v2;\n\t\tthis.v3 = v3;\n\n\t}\n\n\tgetPoint( t, optionalTarget = new Vector3() ) {\n\n\t\tconst point = optionalTarget;\n\n\t\tconst v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3;\n\n\t\tpoint.set(\n\t\t\tCubicBezier( t, v0.x, v1.x, v2.x, v3.x ),\n\t\t\tCubicBezier( t, v0.y, v1.y, v2.y, v3.y ),\n\t\t\tCubicBezier( t, v0.z, v1.z, v2.z, v3.z )\n\t\t);\n\n\t\treturn point;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.v0.copy( source.v0 );\n\t\tthis.v1.copy( source.v1 );\n\t\tthis.v2.copy( source.v2 );\n\t\tthis.v3.copy( source.v3 );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.v0 = this.v0.toArray();\n\t\tdata.v1 = this.v1.toArray();\n\t\tdata.v2 = this.v2.toArray();\n\t\tdata.v3 = this.v3.toArray();\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tsuper.fromJSON( json );\n\n\t\tthis.v0.fromArray( json.v0 );\n\t\tthis.v1.fromArray( json.v1 );\n\t\tthis.v2.fromArray( json.v2 );\n\t\tthis.v3.fromArray( json.v3 );\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass LineCurve extends Curve {\n\n\tconstructor( v1 = new Vector2(), v2 = new Vector2() ) {\n\n\t\tsuper();\n\n\t\tthis.isLineCurve = true;\n\n\t\tthis.type = 'LineCurve';\n\n\t\tthis.v1 = v1;\n\t\tthis.v2 = v2;\n\n\t}\n\n\tgetPoint( t, optionalTarget = new Vector2() ) {\n\n\t\tconst point = optionalTarget;\n\n\t\tif ( t === 1 ) {\n\n\t\t\tpoint.copy( this.v2 );\n\n\t\t} else {\n\n\t\t\tpoint.copy( this.v2 ).sub( this.v1 );\n\t\t\tpoint.multiplyScalar( t ).add( this.v1 );\n\n\t\t}\n\n\t\treturn point;\n\n\t}\n\n\t// Line curve is linear, so we can overwrite default getPointAt\n\tgetPointAt( u, optionalTarget ) {\n\n\t\treturn this.getPoint( u, optionalTarget );\n\n\t}\n\n\tgetTangent( t, optionalTarget = new Vector2() ) {\n\n\t\treturn optionalTarget.subVectors( this.v2, this.v1 ).normalize();\n\n\t}\n\n\tgetTangentAt( u, optionalTarget ) {\n\n\t\treturn this.getTangent( u, optionalTarget );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.v1.copy( source.v1 );\n\t\tthis.v2.copy( source.v2 );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.v1 = this.v1.toArray();\n\t\tdata.v2 = this.v2.toArray();\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tsuper.fromJSON( json );\n\n\t\tthis.v1.fromArray( json.v1 );\n\t\tthis.v2.fromArray( json.v2 );\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass LineCurve3 extends Curve {\n\n\tconstructor( v1 = new Vector3(), v2 = new Vector3() ) {\n\n\t\tsuper();\n\n\t\tthis.isLineCurve3 = true;\n\n\t\tthis.type = 'LineCurve3';\n\n\t\tthis.v1 = v1;\n\t\tthis.v2 = v2;\n\n\t}\n\n\tgetPoint( t, optionalTarget = new Vector3() ) {\n\n\t\tconst point = optionalTarget;\n\n\t\tif ( t === 1 ) {\n\n\t\t\tpoint.copy( this.v2 );\n\n\t\t} else {\n\n\t\t\tpoint.copy( this.v2 ).sub( this.v1 );\n\t\t\tpoint.multiplyScalar( t ).add( this.v1 );\n\n\t\t}\n\n\t\treturn point;\n\n\t}\n\n\t// Line curve is linear, so we can overwrite default getPointAt\n\tgetPointAt( u, optionalTarget ) {\n\n\t\treturn this.getPoint( u, optionalTarget );\n\n\t}\n\n\tgetTangent( t, optionalTarget = new Vector3() ) {\n\n\t\treturn optionalTarget.subVectors( this.v2, this.v1 ).normalize();\n\n\t}\n\n\tgetTangentAt( u, optionalTarget ) {\n\n\t\treturn this.getTangent( u, optionalTarget );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.v1.copy( source.v1 );\n\t\tthis.v2.copy( source.v2 );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.v1 = this.v1.toArray();\n\t\tdata.v2 = this.v2.toArray();\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tsuper.fromJSON( json );\n\n\t\tthis.v1.fromArray( json.v1 );\n\t\tthis.v2.fromArray( json.v2 );\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass QuadraticBezierCurve extends Curve {\n\n\tconstructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) {\n\n\t\tsuper();\n\n\t\tthis.isQuadraticBezierCurve = true;\n\n\t\tthis.type = 'QuadraticBezierCurve';\n\n\t\tthis.v0 = v0;\n\t\tthis.v1 = v1;\n\t\tthis.v2 = v2;\n\n\t}\n\n\tgetPoint( t, optionalTarget = new Vector2() ) {\n\n\t\tconst point = optionalTarget;\n\n\t\tconst v0 = this.v0, v1 = this.v1, v2 = this.v2;\n\n\t\tpoint.set(\n\t\t\tQuadraticBezier( t, v0.x, v1.x, v2.x ),\n\t\t\tQuadraticBezier( t, v0.y, v1.y, v2.y )\n\t\t);\n\n\t\treturn point;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.v0.copy( source.v0 );\n\t\tthis.v1.copy( source.v1 );\n\t\tthis.v2.copy( source.v2 );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.v0 = this.v0.toArray();\n\t\tdata.v1 = this.v1.toArray();\n\t\tdata.v2 = this.v2.toArray();\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tsuper.fromJSON( json );\n\n\t\tthis.v0.fromArray( json.v0 );\n\t\tthis.v1.fromArray( json.v1 );\n\t\tthis.v2.fromArray( json.v2 );\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass QuadraticBezierCurve3 extends Curve {\n\n\tconstructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) {\n\n\t\tsuper();\n\n\t\tthis.isQuadraticBezierCurve3 = true;\n\n\t\tthis.type = 'QuadraticBezierCurve3';\n\n\t\tthis.v0 = v0;\n\t\tthis.v1 = v1;\n\t\tthis.v2 = v2;\n\n\t}\n\n\tgetPoint( t, optionalTarget = new Vector3() ) {\n\n\t\tconst point = optionalTarget;\n\n\t\tconst v0 = this.v0, v1 = this.v1, v2 = this.v2;\n\n\t\tpoint.set(\n\t\t\tQuadraticBezier( t, v0.x, v1.x, v2.x ),\n\t\t\tQuadraticBezier( t, v0.y, v1.y, v2.y ),\n\t\t\tQuadraticBezier( t, v0.z, v1.z, v2.z )\n\t\t);\n\n\t\treturn point;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.v0.copy( source.v0 );\n\t\tthis.v1.copy( source.v1 );\n\t\tthis.v2.copy( source.v2 );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.v0 = this.v0.toArray();\n\t\tdata.v1 = this.v1.toArray();\n\t\tdata.v2 = this.v2.toArray();\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tsuper.fromJSON( json );\n\n\t\tthis.v0.fromArray( json.v0 );\n\t\tthis.v1.fromArray( json.v1 );\n\t\tthis.v2.fromArray( json.v2 );\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass SplineCurve extends Curve {\n\n\tconstructor( points = [] ) {\n\n\t\tsuper();\n\n\t\tthis.isSplineCurve = true;\n\n\t\tthis.type = 'SplineCurve';\n\n\t\tthis.points = points;\n\n\t}\n\n\tgetPoint( t, optionalTarget = new Vector2() ) {\n\n\t\tconst point = optionalTarget;\n\n\t\tconst points = this.points;\n\t\tconst p = ( points.length - 1 ) * t;\n\n\t\tconst intPoint = Math.floor( p );\n\t\tconst weight = p - intPoint;\n\n\t\tconst p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ];\n\t\tconst p1 = points[ intPoint ];\n\t\tconst p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];\n\t\tconst p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];\n\n\t\tpoint.set(\n\t\t\tCatmullRom( weight, p0.x, p1.x, p2.x, p3.x ),\n\t\t\tCatmullRom( weight, p0.y, p1.y, p2.y, p3.y )\n\t\t);\n\n\t\treturn point;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.points = [];\n\n\t\tfor ( let i = 0, l = source.points.length; i < l; i ++ ) {\n\n\t\t\tconst point = source.points[ i ];\n\n\t\t\tthis.points.push( point.clone() );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.points = [];\n\n\t\tfor ( let i = 0, l = this.points.length; i < l; i ++ ) {\n\n\t\t\tconst point = this.points[ i ];\n\t\t\tdata.points.push( point.toArray() );\n\n\t\t}\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tsuper.fromJSON( json );\n\n\t\tthis.points = [];\n\n\t\tfor ( let i = 0, l = json.points.length; i < l; i ++ ) {\n\n\t\t\tconst point = json.points[ i ];\n\t\t\tthis.points.push( new Vector2().fromArray( point ) );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n}\n\nvar Curves = /*#__PURE__*/Object.freeze({\n\t__proto__: null,\n\tArcCurve: ArcCurve,\n\tCatmullRomCurve3: CatmullRomCurve3,\n\tCubicBezierCurve: CubicBezierCurve,\n\tCubicBezierCurve3: CubicBezierCurve3,\n\tEllipseCurve: EllipseCurve,\n\tLineCurve: LineCurve,\n\tLineCurve3: LineCurve3,\n\tQuadraticBezierCurve: QuadraticBezierCurve,\n\tQuadraticBezierCurve3: QuadraticBezierCurve3,\n\tSplineCurve: SplineCurve\n});\n\n/**************************************************************\n *\tCurved Path - a curve path is simply a array of connected\n *  curves, but retains the api of a curve\n **************************************************************/\n\nclass CurvePath extends Curve {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.type = 'CurvePath';\n\n\t\tthis.curves = [];\n\t\tthis.autoClose = false; // Automatically closes the path\n\n\t}\n\n\tadd( curve ) {\n\n\t\tthis.curves.push( curve );\n\n\t}\n\n\tclosePath() {\n\n\t\t// Add a line curve if start and end of lines are not connected\n\t\tconst startPoint = this.curves[ 0 ].getPoint( 0 );\n\t\tconst endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 );\n\n\t\tif ( ! startPoint.equals( endPoint ) ) {\n\n\t\t\tconst lineType = ( startPoint.isVector2 === true ) ? 'LineCurve' : 'LineCurve3';\n\t\t\tthis.curves.push( new Curves[ lineType ]( endPoint, startPoint ) );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\t// To get accurate point with reference to\n\t// entire path distance at time t,\n\t// following has to be done:\n\n\t// 1. Length of each sub path have to be known\n\t// 2. Locate and identify type of curve\n\t// 3. Get t for the curve\n\t// 4. Return curve.getPointAt(t')\n\n\tgetPoint( t, optionalTarget ) {\n\n\t\tconst d = t * this.getLength();\n\t\tconst curveLengths = this.getCurveLengths();\n\t\tlet i = 0;\n\n\t\t// To think about boundaries points.\n\n\t\twhile ( i < curveLengths.length ) {\n\n\t\t\tif ( curveLengths[ i ] >= d ) {\n\n\t\t\t\tconst diff = curveLengths[ i ] - d;\n\t\t\t\tconst curve = this.curves[ i ];\n\n\t\t\t\tconst segmentLength = curve.getLength();\n\t\t\t\tconst u = segmentLength === 0 ? 0 : 1 - diff / segmentLength;\n\n\t\t\t\treturn curve.getPointAt( u, optionalTarget );\n\n\t\t\t}\n\n\t\t\ti ++;\n\n\t\t}\n\n\t\treturn null;\n\n\t\t// loop where sum != 0, sum > d , sum+1 <d\n\n\t}\n\n\t// We cannot use the default THREE.Curve getPoint() with getLength() because in\n\t// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath\n\t// getPoint() depends on getLength\n\n\tgetLength() {\n\n\t\tconst lens = this.getCurveLengths();\n\t\treturn lens[ lens.length - 1 ];\n\n\t}\n\n\t// cacheLengths must be recalculated.\n\tupdateArcLengths() {\n\n\t\tthis.needsUpdate = true;\n\t\tthis.cacheLengths = null;\n\t\tthis.getCurveLengths();\n\n\t}\n\n\t// Compute lengths and cache them\n\t// We cannot overwrite getLengths() because UtoT mapping uses it.\n\n\tgetCurveLengths() {\n\n\t\t// We use cache values if curves and cache array are same length\n\n\t\tif ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) {\n\n\t\t\treturn this.cacheLengths;\n\n\t\t}\n\n\t\t// Get length of sub-curve\n\t\t// Push sums into cached array\n\n\t\tconst lengths = [];\n\t\tlet sums = 0;\n\n\t\tfor ( let i = 0, l = this.curves.length; i < l; i ++ ) {\n\n\t\t\tsums += this.curves[ i ].getLength();\n\t\t\tlengths.push( sums );\n\n\t\t}\n\n\t\tthis.cacheLengths = lengths;\n\n\t\treturn lengths;\n\n\t}\n\n\tgetSpacedPoints( divisions = 40 ) {\n\n\t\tconst points = [];\n\n\t\tfor ( let i = 0; i <= divisions; i ++ ) {\n\n\t\t\tpoints.push( this.getPoint( i / divisions ) );\n\n\t\t}\n\n\t\tif ( this.autoClose ) {\n\n\t\t\tpoints.push( points[ 0 ] );\n\n\t\t}\n\n\t\treturn points;\n\n\t}\n\n\tgetPoints( divisions = 12 ) {\n\n\t\tconst points = [];\n\t\tlet last;\n\n\t\tfor ( let i = 0, curves = this.curves; i < curves.length; i ++ ) {\n\n\t\t\tconst curve = curves[ i ];\n\t\t\tconst resolution = curve.isEllipseCurve ? divisions * 2\n\t\t\t\t: ( curve.isLineCurve || curve.isLineCurve3 ) ? 1\n\t\t\t\t\t: curve.isSplineCurve ? divisions * curve.points.length\n\t\t\t\t\t\t: divisions;\n\n\t\t\tconst pts = curve.getPoints( resolution );\n\n\t\t\tfor ( let j = 0; j < pts.length; j ++ ) {\n\n\t\t\t\tconst point = pts[ j ];\n\n\t\t\t\tif ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates\n\n\t\t\t\tpoints.push( point );\n\t\t\t\tlast = point;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( this.autoClose && points.length > 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) {\n\n\t\t\tpoints.push( points[ 0 ] );\n\n\t\t}\n\n\t\treturn points;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.curves = [];\n\n\t\tfor ( let i = 0, l = source.curves.length; i < l; i ++ ) {\n\n\t\t\tconst curve = source.curves[ i ];\n\n\t\t\tthis.curves.push( curve.clone() );\n\n\t\t}\n\n\t\tthis.autoClose = source.autoClose;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.autoClose = this.autoClose;\n\t\tdata.curves = [];\n\n\t\tfor ( let i = 0, l = this.curves.length; i < l; i ++ ) {\n\n\t\t\tconst curve = this.curves[ i ];\n\t\t\tdata.curves.push( curve.toJSON() );\n\n\t\t}\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tsuper.fromJSON( json );\n\n\t\tthis.autoClose = json.autoClose;\n\t\tthis.curves = [];\n\n\t\tfor ( let i = 0, l = json.curves.length; i < l; i ++ ) {\n\n\t\t\tconst curve = json.curves[ i ];\n\t\t\tthis.curves.push( new Curves[ curve.type ]().fromJSON( curve ) );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass Path extends CurvePath {\n\n\tconstructor( points ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'Path';\n\n\t\tthis.currentPoint = new Vector2();\n\n\t\tif ( points ) {\n\n\t\t\tthis.setFromPoints( points );\n\n\t\t}\n\n\t}\n\n\tsetFromPoints( points ) {\n\n\t\tthis.moveTo( points[ 0 ].x, points[ 0 ].y );\n\n\t\tfor ( let i = 1, l = points.length; i < l; i ++ ) {\n\n\t\t\tthis.lineTo( points[ i ].x, points[ i ].y );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tmoveTo( x, y ) {\n\n\t\tthis.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying?\n\n\t\treturn this;\n\n\t}\n\n\tlineTo( x, y ) {\n\n\t\tconst curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) );\n\t\tthis.curves.push( curve );\n\n\t\tthis.currentPoint.set( x, y );\n\n\t\treturn this;\n\n\t}\n\n\tquadraticCurveTo( aCPx, aCPy, aX, aY ) {\n\n\t\tconst curve = new QuadraticBezierCurve(\n\t\t\tthis.currentPoint.clone(),\n\t\t\tnew Vector2( aCPx, aCPy ),\n\t\t\tnew Vector2( aX, aY )\n\t\t);\n\n\t\tthis.curves.push( curve );\n\n\t\tthis.currentPoint.set( aX, aY );\n\n\t\treturn this;\n\n\t}\n\n\tbezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {\n\n\t\tconst curve = new CubicBezierCurve(\n\t\t\tthis.currentPoint.clone(),\n\t\t\tnew Vector2( aCP1x, aCP1y ),\n\t\t\tnew Vector2( aCP2x, aCP2y ),\n\t\t\tnew Vector2( aX, aY )\n\t\t);\n\n\t\tthis.curves.push( curve );\n\n\t\tthis.currentPoint.set( aX, aY );\n\n\t\treturn this;\n\n\t}\n\n\tsplineThru( pts /*Array of Vector*/ ) {\n\n\t\tconst npts = [ this.currentPoint.clone() ].concat( pts );\n\n\t\tconst curve = new SplineCurve( npts );\n\t\tthis.curves.push( curve );\n\n\t\tthis.currentPoint.copy( pts[ pts.length - 1 ] );\n\n\t\treturn this;\n\n\t}\n\n\tarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {\n\n\t\tconst x0 = this.currentPoint.x;\n\t\tconst y0 = this.currentPoint.y;\n\n\t\tthis.absarc( aX + x0, aY + y0, aRadius,\n\t\t\taStartAngle, aEndAngle, aClockwise );\n\n\t\treturn this;\n\n\t}\n\n\tabsarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {\n\n\t\tthis.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );\n\n\t\treturn this;\n\n\t}\n\n\tellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {\n\n\t\tconst x0 = this.currentPoint.x;\n\t\tconst y0 = this.currentPoint.y;\n\n\t\tthis.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );\n\n\t\treturn this;\n\n\t}\n\n\tabsellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {\n\n\t\tconst curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );\n\n\t\tif ( this.curves.length > 0 ) {\n\n\t\t\t// if a previous curve is present, attempt to join\n\t\t\tconst firstPoint = curve.getPoint( 0 );\n\n\t\t\tif ( ! firstPoint.equals( this.currentPoint ) ) {\n\n\t\t\t\tthis.lineTo( firstPoint.x, firstPoint.y );\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.curves.push( curve );\n\n\t\tconst lastPoint = curve.getPoint( 1 );\n\t\tthis.currentPoint.copy( lastPoint );\n\n\t\treturn this;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.currentPoint.copy( source.currentPoint );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.currentPoint = this.currentPoint.toArray();\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tsuper.fromJSON( json );\n\n\t\tthis.currentPoint.fromArray( json.currentPoint );\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass LatheGeometry extends BufferGeometry {\n\n\tconstructor( points = [ new Vector2( 0, - 0.5 ), new Vector2( 0.5, 0 ), new Vector2( 0, 0.5 ) ], segments = 12, phiStart = 0, phiLength = Math.PI * 2 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'LatheGeometry';\n\n\t\tthis.parameters = {\n\t\t\tpoints: points,\n\t\t\tsegments: segments,\n\t\t\tphiStart: phiStart,\n\t\t\tphiLength: phiLength\n\t\t};\n\n\t\tsegments = Math.floor( segments );\n\n\t\t// clamp phiLength so it's in range of [ 0, 2PI ]\n\n\t\tphiLength = clamp( phiLength, 0, Math.PI * 2 );\n\n\t\t// buffers\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst uvs = [];\n\t\tconst initNormals = [];\n\t\tconst normals = [];\n\n\t\t// helper variables\n\n\t\tconst inverseSegments = 1.0 / segments;\n\t\tconst vertex = new Vector3();\n\t\tconst uv = new Vector2();\n\t\tconst normal = new Vector3();\n\t\tconst curNormal = new Vector3();\n\t\tconst prevNormal = new Vector3();\n\t\tlet dx = 0;\n\t\tlet dy = 0;\n\n\t\t// pre-compute normals for initial \"meridian\"\n\n\t\tfor ( let j = 0; j <= ( points.length - 1 ); j ++ ) {\n\n\t\t\tswitch ( j ) {\n\n\t\t\t\tcase 0:\t\t\t\t// special handling for 1st vertex on path\n\n\t\t\t\t\tdx = points[ j + 1 ].x - points[ j ].x;\n\t\t\t\t\tdy = points[ j + 1 ].y - points[ j ].y;\n\n\t\t\t\t\tnormal.x = dy * 1.0;\n\t\t\t\t\tnormal.y = - dx;\n\t\t\t\t\tnormal.z = dy * 0.0;\n\n\t\t\t\t\tprevNormal.copy( normal );\n\n\t\t\t\t\tnormal.normalize();\n\n\t\t\t\t\tinitNormals.push( normal.x, normal.y, normal.z );\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase ( points.length - 1 ):\t// special handling for last Vertex on path\n\n\t\t\t\t\tinitNormals.push( prevNormal.x, prevNormal.y, prevNormal.z );\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\t\t\t// default handling for all vertices in between\n\n\t\t\t\t\tdx = points[ j + 1 ].x - points[ j ].x;\n\t\t\t\t\tdy = points[ j + 1 ].y - points[ j ].y;\n\n\t\t\t\t\tnormal.x = dy * 1.0;\n\t\t\t\t\tnormal.y = - dx;\n\t\t\t\t\tnormal.z = dy * 0.0;\n\n\t\t\t\t\tcurNormal.copy( normal );\n\n\t\t\t\t\tnormal.x += prevNormal.x;\n\t\t\t\t\tnormal.y += prevNormal.y;\n\t\t\t\t\tnormal.z += prevNormal.z;\n\n\t\t\t\t\tnormal.normalize();\n\n\t\t\t\t\tinitNormals.push( normal.x, normal.y, normal.z );\n\n\t\t\t\t\tprevNormal.copy( curNormal );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// generate vertices, uvs and normals\n\n\t\tfor ( let i = 0; i <= segments; i ++ ) {\n\n\t\t\tconst phi = phiStart + i * inverseSegments * phiLength;\n\n\t\t\tconst sin = Math.sin( phi );\n\t\t\tconst cos = Math.cos( phi );\n\n\t\t\tfor ( let j = 0; j <= ( points.length - 1 ); j ++ ) {\n\n\t\t\t\t// vertex\n\n\t\t\t\tvertex.x = points[ j ].x * sin;\n\t\t\t\tvertex.y = points[ j ].y;\n\t\t\t\tvertex.z = points[ j ].x * cos;\n\n\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t// uv\n\n\t\t\t\tuv.x = i / segments;\n\t\t\t\tuv.y = j / ( points.length - 1 );\n\n\t\t\t\tuvs.push( uv.x, uv.y );\n\n\t\t\t\t// normal\n\n\t\t\t\tconst x = initNormals[ 3 * j + 0 ] * sin;\n\t\t\t\tconst y = initNormals[ 3 * j + 1 ];\n\t\t\t\tconst z = initNormals[ 3 * j + 0 ] * cos;\n\n\t\t\t\tnormals.push( x, y, z );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// indices\n\n\t\tfor ( let i = 0; i < segments; i ++ ) {\n\n\t\t\tfor ( let j = 0; j < ( points.length - 1 ); j ++ ) {\n\n\t\t\t\tconst base = j + i * points.length;\n\n\t\t\t\tconst a = base;\n\t\t\t\tconst b = base + points.length;\n\t\t\t\tconst c = base + points.length + 1;\n\t\t\t\tconst d = base + 1;\n\n\t\t\t\t// faces\n\n\t\t\t\tindices.push( a, b, d );\n\t\t\t\tindices.push( c, d, b );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new LatheGeometry( data.points, data.segments, data.phiStart, data.phiLength );\n\n\t}\n\n}\n\nclass CapsuleGeometry extends LatheGeometry {\n\n\tconstructor( radius = 1, length = 1, capSegments = 4, radialSegments = 8 ) {\n\n\t\tconst path = new Path();\n\t\tpath.absarc( 0, - length / 2, radius, Math.PI * 1.5, 0 );\n\t\tpath.absarc( 0, length / 2, radius, 0, Math.PI * 0.5 );\n\n\t\tsuper( path.getPoints( capSegments ), radialSegments );\n\n\t\tthis.type = 'CapsuleGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tlength: length,\n\t\t\tcapSegments: capSegments,\n\t\t\tradialSegments: radialSegments,\n\t\t};\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new CapsuleGeometry( data.radius, data.length, data.capSegments, data.radialSegments );\n\n\t}\n\n}\n\nclass CircleGeometry extends BufferGeometry {\n\n\tconstructor( radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'CircleGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tsegments: segments,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\tsegments = Math.max( 3, segments );\n\n\t\t// buffers\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\t// helper variables\n\n\t\tconst vertex = new Vector3();\n\t\tconst uv = new Vector2();\n\n\t\t// center point\n\n\t\tvertices.push( 0, 0, 0 );\n\t\tnormals.push( 0, 0, 1 );\n\t\tuvs.push( 0.5, 0.5 );\n\n\t\tfor ( let s = 0, i = 3; s <= segments; s ++, i += 3 ) {\n\n\t\t\tconst segment = thetaStart + s / segments * thetaLength;\n\n\t\t\t// vertex\n\n\t\t\tvertex.x = radius * Math.cos( segment );\n\t\t\tvertex.y = radius * Math.sin( segment );\n\n\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t// normal\n\n\t\t\tnormals.push( 0, 0, 1 );\n\n\t\t\t// uvs\n\n\t\t\tuv.x = ( vertices[ i ] / radius + 1 ) / 2;\n\t\t\tuv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2;\n\n\t\t\tuvs.push( uv.x, uv.y );\n\n\t\t}\n\n\t\t// indices\n\n\t\tfor ( let i = 1; i <= segments; i ++ ) {\n\n\t\t\tindices.push( i, i + 1, 0 );\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new CircleGeometry( data.radius, data.segments, data.thetaStart, data.thetaLength );\n\n\t}\n\n}\n\nclass CylinderGeometry extends BufferGeometry {\n\n\tconstructor( radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'CylinderGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradiusTop: radiusTop,\n\t\t\tradiusBottom: radiusBottom,\n\t\t\theight: height,\n\t\t\tradialSegments: radialSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\topenEnded: openEnded,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\tconst scope = this;\n\n\t\tradialSegments = Math.floor( radialSegments );\n\t\theightSegments = Math.floor( heightSegments );\n\n\t\t// buffers\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\t// helper variables\n\n\t\tlet index = 0;\n\t\tconst indexArray = [];\n\t\tconst halfHeight = height / 2;\n\t\tlet groupStart = 0;\n\n\t\t// generate geometry\n\n\t\tgenerateTorso();\n\n\t\tif ( openEnded === false ) {\n\n\t\t\tif ( radiusTop > 0 ) generateCap( true );\n\t\t\tif ( radiusBottom > 0 ) generateCap( false );\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t\tfunction generateTorso() {\n\n\t\t\tconst normal = new Vector3();\n\t\t\tconst vertex = new Vector3();\n\n\t\t\tlet groupCount = 0;\n\n\t\t\t// this will be used to calculate the normal\n\t\t\tconst slope = ( radiusBottom - radiusTop ) / height;\n\n\t\t\t// generate vertices, normals and uvs\n\n\t\t\tfor ( let y = 0; y <= heightSegments; y ++ ) {\n\n\t\t\t\tconst indexRow = [];\n\n\t\t\t\tconst v = y / heightSegments;\n\n\t\t\t\t// calculate the radius of the current row\n\n\t\t\t\tconst radius = v * ( radiusBottom - radiusTop ) + radiusTop;\n\n\t\t\t\tfor ( let x = 0; x <= radialSegments; x ++ ) {\n\n\t\t\t\t\tconst u = x / radialSegments;\n\n\t\t\t\t\tconst theta = u * thetaLength + thetaStart;\n\n\t\t\t\t\tconst sinTheta = Math.sin( theta );\n\t\t\t\t\tconst cosTheta = Math.cos( theta );\n\n\t\t\t\t\t// vertex\n\n\t\t\t\t\tvertex.x = radius * sinTheta;\n\t\t\t\t\tvertex.y = - v * height + halfHeight;\n\t\t\t\t\tvertex.z = radius * cosTheta;\n\t\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t\t// normal\n\n\t\t\t\t\tnormal.set( sinTheta, slope, cosTheta ).normalize();\n\t\t\t\t\tnormals.push( normal.x, normal.y, normal.z );\n\n\t\t\t\t\t// uv\n\n\t\t\t\t\tuvs.push( u, 1 - v );\n\n\t\t\t\t\t// save index of vertex in respective row\n\n\t\t\t\t\tindexRow.push( index ++ );\n\n\t\t\t\t}\n\n\t\t\t\t// now save vertices of the row in our index array\n\n\t\t\t\tindexArray.push( indexRow );\n\n\t\t\t}\n\n\t\t\t// generate indices\n\n\t\t\tfor ( let x = 0; x < radialSegments; x ++ ) {\n\n\t\t\t\tfor ( let y = 0; y < heightSegments; y ++ ) {\n\n\t\t\t\t\t// we use the index array to access the correct indices\n\n\t\t\t\t\tconst a = indexArray[ y ][ x ];\n\t\t\t\t\tconst b = indexArray[ y + 1 ][ x ];\n\t\t\t\t\tconst c = indexArray[ y + 1 ][ x + 1 ];\n\t\t\t\t\tconst d = indexArray[ y ][ x + 1 ];\n\n\t\t\t\t\t// faces\n\n\t\t\t\t\tindices.push( a, b, d );\n\t\t\t\t\tindices.push( b, c, d );\n\n\t\t\t\t\t// update group counter\n\n\t\t\t\t\tgroupCount += 6;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// add a group to the geometry. this will ensure multi material support\n\n\t\t\tscope.addGroup( groupStart, groupCount, 0 );\n\n\t\t\t// calculate new start value for groups\n\n\t\t\tgroupStart += groupCount;\n\n\t\t}\n\n\t\tfunction generateCap( top ) {\n\n\t\t\t// save the index of the first center vertex\n\t\t\tconst centerIndexStart = index;\n\n\t\t\tconst uv = new Vector2();\n\t\t\tconst vertex = new Vector3();\n\n\t\t\tlet groupCount = 0;\n\n\t\t\tconst radius = ( top === true ) ? radiusTop : radiusBottom;\n\t\t\tconst sign = ( top === true ) ? 1 : - 1;\n\n\t\t\t// first we generate the center vertex data of the cap.\n\t\t\t// because the geometry needs one set of uvs per face,\n\t\t\t// we must generate a center vertex per face/segment\n\n\t\t\tfor ( let x = 1; x <= radialSegments; x ++ ) {\n\n\t\t\t\t// vertex\n\n\t\t\t\tvertices.push( 0, halfHeight * sign, 0 );\n\n\t\t\t\t// normal\n\n\t\t\t\tnormals.push( 0, sign, 0 );\n\n\t\t\t\t// uv\n\n\t\t\t\tuvs.push( 0.5, 0.5 );\n\n\t\t\t\t// increase index\n\n\t\t\t\tindex ++;\n\n\t\t\t}\n\n\t\t\t// save the index of the last center vertex\n\t\t\tconst centerIndexEnd = index;\n\n\t\t\t// now we generate the surrounding vertices, normals and uvs\n\n\t\t\tfor ( let x = 0; x <= radialSegments; x ++ ) {\n\n\t\t\t\tconst u = x / radialSegments;\n\t\t\t\tconst theta = u * thetaLength + thetaStart;\n\n\t\t\t\tconst cosTheta = Math.cos( theta );\n\t\t\t\tconst sinTheta = Math.sin( theta );\n\n\t\t\t\t// vertex\n\n\t\t\t\tvertex.x = radius * sinTheta;\n\t\t\t\tvertex.y = halfHeight * sign;\n\t\t\t\tvertex.z = radius * cosTheta;\n\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t// normal\n\n\t\t\t\tnormals.push( 0, sign, 0 );\n\n\t\t\t\t// uv\n\n\t\t\t\tuv.x = ( cosTheta * 0.5 ) + 0.5;\n\t\t\t\tuv.y = ( sinTheta * 0.5 * sign ) + 0.5;\n\t\t\t\tuvs.push( uv.x, uv.y );\n\n\t\t\t\t// increase index\n\n\t\t\t\tindex ++;\n\n\t\t\t}\n\n\t\t\t// generate indices\n\n\t\t\tfor ( let x = 0; x < radialSegments; x ++ ) {\n\n\t\t\t\tconst c = centerIndexStart + x;\n\t\t\t\tconst i = centerIndexEnd + x;\n\n\t\t\t\tif ( top === true ) {\n\n\t\t\t\t\t// face top\n\n\t\t\t\t\tindices.push( i, i + 1, c );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// face bottom\n\n\t\t\t\t\tindices.push( i + 1, i, c );\n\n\t\t\t\t}\n\n\t\t\t\tgroupCount += 3;\n\n\t\t\t}\n\n\t\t\t// add a group to the geometry. this will ensure multi material support\n\n\t\t\tscope.addGroup( groupStart, groupCount, top === true ? 1 : 2 );\n\n\t\t\t// calculate new start value for groups\n\n\t\t\tgroupStart += groupCount;\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength );\n\n\t}\n\n}\n\nclass ConeGeometry extends CylinderGeometry {\n\n\tconstructor( radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) {\n\n\t\tsuper( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength );\n\n\t\tthis.type = 'ConeGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\theight: height,\n\t\t\tradialSegments: radialSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\topenEnded: openEnded,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new ConeGeometry( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength );\n\n\t}\n\n}\n\nclass PolyhedronGeometry extends BufferGeometry {\n\n\tconstructor( vertices = [], indices = [], radius = 1, detail = 0 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'PolyhedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tvertices: vertices,\n\t\t\tindices: indices,\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t\t// default buffer data\n\n\t\tconst vertexBuffer = [];\n\t\tconst uvBuffer = [];\n\n\t\t// the subdivision creates the vertex buffer data\n\n\t\tsubdivide( detail );\n\n\t\t// all vertices should lie on a conceptual sphere with a given radius\n\n\t\tapplyRadius( radius );\n\n\t\t// finally, create the uv data\n\n\t\tgenerateUVs();\n\n\t\t// build non-indexed geometry\n\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) );\n\n\t\tif ( detail === 0 ) {\n\n\t\t\tthis.computeVertexNormals(); // flat normals\n\n\t\t} else {\n\n\t\t\tthis.normalizeNormals(); // smooth normals\n\n\t\t}\n\n\t\t// helper functions\n\n\t\tfunction subdivide( detail ) {\n\n\t\t\tconst a = new Vector3();\n\t\t\tconst b = new Vector3();\n\t\t\tconst c = new Vector3();\n\n\t\t\t// iterate over all faces and apply a subdivision with the given detail value\n\n\t\t\tfor ( let i = 0; i < indices.length; i += 3 ) {\n\n\t\t\t\t// get the vertices of the face\n\n\t\t\t\tgetVertexByIndex( indices[ i + 0 ], a );\n\t\t\t\tgetVertexByIndex( indices[ i + 1 ], b );\n\t\t\t\tgetVertexByIndex( indices[ i + 2 ], c );\n\n\t\t\t\t// perform subdivision\n\n\t\t\t\tsubdivideFace( a, b, c, detail );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction subdivideFace( a, b, c, detail ) {\n\n\t\t\tconst cols = detail + 1;\n\n\t\t\t// we use this multidimensional array as a data structure for creating the subdivision\n\n\t\t\tconst v = [];\n\n\t\t\t// construct all of the vertices for this subdivision\n\n\t\t\tfor ( let i = 0; i <= cols; i ++ ) {\n\n\t\t\t\tv[ i ] = [];\n\n\t\t\t\tconst aj = a.clone().lerp( c, i / cols );\n\t\t\t\tconst bj = b.clone().lerp( c, i / cols );\n\n\t\t\t\tconst rows = cols - i;\n\n\t\t\t\tfor ( let j = 0; j <= rows; j ++ ) {\n\n\t\t\t\t\tif ( j === 0 && i === cols ) {\n\n\t\t\t\t\t\tv[ i ][ j ] = aj;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tv[ i ][ j ] = aj.clone().lerp( bj, j / rows );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// construct all of the faces\n\n\t\t\tfor ( let i = 0; i < cols; i ++ ) {\n\n\t\t\t\tfor ( let j = 0; j < 2 * ( cols - i ) - 1; j ++ ) {\n\n\t\t\t\t\tconst k = Math.floor( j / 2 );\n\n\t\t\t\t\tif ( j % 2 === 0 ) {\n\n\t\t\t\t\t\tpushVertex( v[ i ][ k + 1 ] );\n\t\t\t\t\t\tpushVertex( v[ i + 1 ][ k ] );\n\t\t\t\t\t\tpushVertex( v[ i ][ k ] );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tpushVertex( v[ i ][ k + 1 ] );\n\t\t\t\t\t\tpushVertex( v[ i + 1 ][ k + 1 ] );\n\t\t\t\t\t\tpushVertex( v[ i + 1 ][ k ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction applyRadius( radius ) {\n\n\t\t\tconst vertex = new Vector3();\n\n\t\t\t// iterate over the entire buffer and apply the radius to each vertex\n\n\t\t\tfor ( let i = 0; i < vertexBuffer.length; i += 3 ) {\n\n\t\t\t\tvertex.x = vertexBuffer[ i + 0 ];\n\t\t\t\tvertex.y = vertexBuffer[ i + 1 ];\n\t\t\t\tvertex.z = vertexBuffer[ i + 2 ];\n\n\t\t\t\tvertex.normalize().multiplyScalar( radius );\n\n\t\t\t\tvertexBuffer[ i + 0 ] = vertex.x;\n\t\t\t\tvertexBuffer[ i + 1 ] = vertex.y;\n\t\t\t\tvertexBuffer[ i + 2 ] = vertex.z;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction generateUVs() {\n\n\t\t\tconst vertex = new Vector3();\n\n\t\t\tfor ( let i = 0; i < vertexBuffer.length; i += 3 ) {\n\n\t\t\t\tvertex.x = vertexBuffer[ i + 0 ];\n\t\t\t\tvertex.y = vertexBuffer[ i + 1 ];\n\t\t\t\tvertex.z = vertexBuffer[ i + 2 ];\n\n\t\t\t\tconst u = azimuth( vertex ) / 2 / Math.PI + 0.5;\n\t\t\t\tconst v = inclination( vertex ) / Math.PI + 0.5;\n\t\t\t\tuvBuffer.push( u, 1 - v );\n\n\t\t\t}\n\n\t\t\tcorrectUVs();\n\n\t\t\tcorrectSeam();\n\n\t\t}\n\n\t\tfunction correctSeam() {\n\n\t\t\t// handle case when face straddles the seam, see #3269\n\n\t\t\tfor ( let i = 0; i < uvBuffer.length; i += 6 ) {\n\n\t\t\t\t// uv data of a single face\n\n\t\t\t\tconst x0 = uvBuffer[ i + 0 ];\n\t\t\t\tconst x1 = uvBuffer[ i + 2 ];\n\t\t\t\tconst x2 = uvBuffer[ i + 4 ];\n\n\t\t\t\tconst max = Math.max( x0, x1, x2 );\n\t\t\t\tconst min = Math.min( x0, x1, x2 );\n\n\t\t\t\t// 0.9 is somewhat arbitrary\n\n\t\t\t\tif ( max > 0.9 && min < 0.1 ) {\n\n\t\t\t\t\tif ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1;\n\t\t\t\t\tif ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1;\n\t\t\t\t\tif ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction pushVertex( vertex ) {\n\n\t\t\tvertexBuffer.push( vertex.x, vertex.y, vertex.z );\n\n\t\t}\n\n\t\tfunction getVertexByIndex( index, vertex ) {\n\n\t\t\tconst stride = index * 3;\n\n\t\t\tvertex.x = vertices[ stride + 0 ];\n\t\t\tvertex.y = vertices[ stride + 1 ];\n\t\t\tvertex.z = vertices[ stride + 2 ];\n\n\t\t}\n\n\t\tfunction correctUVs() {\n\n\t\t\tconst a = new Vector3();\n\t\t\tconst b = new Vector3();\n\t\t\tconst c = new Vector3();\n\n\t\t\tconst centroid = new Vector3();\n\n\t\t\tconst uvA = new Vector2();\n\t\t\tconst uvB = new Vector2();\n\t\t\tconst uvC = new Vector2();\n\n\t\t\tfor ( let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) {\n\n\t\t\t\ta.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] );\n\t\t\t\tb.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] );\n\t\t\t\tc.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] );\n\n\t\t\t\tuvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] );\n\t\t\t\tuvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] );\n\t\t\t\tuvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] );\n\n\t\t\t\tcentroid.copy( a ).add( b ).add( c ).divideScalar( 3 );\n\n\t\t\t\tconst azi = azimuth( centroid );\n\n\t\t\t\tcorrectUV( uvA, j + 0, a, azi );\n\t\t\t\tcorrectUV( uvB, j + 2, b, azi );\n\t\t\t\tcorrectUV( uvC, j + 4, c, azi );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction correctUV( uv, stride, vector, azimuth ) {\n\n\t\t\tif ( ( azimuth < 0 ) && ( uv.x === 1 ) ) {\n\n\t\t\t\tuvBuffer[ stride ] = uv.x - 1;\n\n\t\t\t}\n\n\t\t\tif ( ( vector.x === 0 ) && ( vector.z === 0 ) ) {\n\n\t\t\t\tuvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Angle around the Y axis, counter-clockwise when looking from above.\n\n\t\tfunction azimuth( vector ) {\n\n\t\t\treturn Math.atan2( vector.z, - vector.x );\n\n\t\t}\n\n\n\t\t// Angle above the XZ plane.\n\n\t\tfunction inclination( vector ) {\n\n\t\t\treturn Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new PolyhedronGeometry( data.vertices, data.indices, data.radius, data.details );\n\n\t}\n\n}\n\nclass DodecahedronGeometry extends PolyhedronGeometry {\n\n\tconstructor( radius = 1, detail = 0 ) {\n\n\t\tconst t = ( 1 + Math.sqrt( 5 ) ) / 2;\n\t\tconst r = 1 / t;\n\n\t\tconst vertices = [\n\n\t\t\t// (±1, ±1, ±1)\n\t\t\t- 1, - 1, - 1,\t- 1, - 1, 1,\n\t\t\t- 1, 1, - 1, - 1, 1, 1,\n\t\t\t1, - 1, - 1, 1, - 1, 1,\n\t\t\t1, 1, - 1, 1, 1, 1,\n\n\t\t\t// (0, ±1/φ, ±φ)\n\t\t\t0, - r, - t, 0, - r, t,\n\t\t\t0, r, - t, 0, r, t,\n\n\t\t\t// (±1/φ, ±φ, 0)\n\t\t\t- r, - t, 0, - r, t, 0,\n\t\t\tr, - t, 0, r, t, 0,\n\n\t\t\t// (±φ, 0, ±1/φ)\n\t\t\t- t, 0, - r, t, 0, - r,\n\t\t\t- t, 0, r, t, 0, r\n\t\t];\n\n\t\tconst indices = [\n\t\t\t3, 11, 7, \t3, 7, 15, \t3, 15, 13,\n\t\t\t7, 19, 17, \t7, 17, 6, \t7, 6, 15,\n\t\t\t17, 4, 8, \t17, 8, 10, \t17, 10, 6,\n\t\t\t8, 0, 16, \t8, 16, 2, \t8, 2, 10,\n\t\t\t0, 12, 1, \t0, 1, 18, \t0, 18, 16,\n\t\t\t6, 10, 2, \t6, 2, 13, \t6, 13, 15,\n\t\t\t2, 16, 18, \t2, 18, 3, \t2, 3, 13,\n\t\t\t18, 1, 9, \t18, 9, 11, \t18, 11, 3,\n\t\t\t4, 14, 12, \t4, 12, 0, \t4, 0, 8,\n\t\t\t11, 9, 5, \t11, 5, 19, \t11, 19, 7,\n\t\t\t19, 5, 14, \t19, 14, 4, \t19, 4, 17,\n\t\t\t1, 12, 14, \t1, 14, 5, \t1, 5, 9\n\t\t];\n\n\t\tsuper( vertices, indices, radius, detail );\n\n\t\tthis.type = 'DodecahedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new DodecahedronGeometry( data.radius, data.detail );\n\n\t}\n\n}\n\nconst _v0 = /*@__PURE__*/ new Vector3();\nconst _v1$1 = /*@__PURE__*/ new Vector3();\nconst _normal = /*@__PURE__*/ new Vector3();\nconst _triangle = /*@__PURE__*/ new Triangle();\n\nclass EdgesGeometry extends BufferGeometry {\n\n\tconstructor( geometry = null, thresholdAngle = 1 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'EdgesGeometry';\n\n\t\tthis.parameters = {\n\t\t\tgeometry: geometry,\n\t\t\tthresholdAngle: thresholdAngle\n\t\t};\n\n\t\tif ( geometry !== null ) {\n\n\t\t\tconst precisionPoints = 4;\n\t\t\tconst precision = Math.pow( 10, precisionPoints );\n\t\t\tconst thresholdDot = Math.cos( DEG2RAD * thresholdAngle );\n\n\t\t\tconst indexAttr = geometry.getIndex();\n\t\t\tconst positionAttr = geometry.getAttribute( 'position' );\n\t\t\tconst indexCount = indexAttr ? indexAttr.count : positionAttr.count;\n\n\t\t\tconst indexArr = [ 0, 0, 0 ];\n\t\t\tconst vertKeys = [ 'a', 'b', 'c' ];\n\t\t\tconst hashes = new Array( 3 );\n\n\t\t\tconst edgeData = {};\n\t\t\tconst vertices = [];\n\t\t\tfor ( let i = 0; i < indexCount; i += 3 ) {\n\n\t\t\t\tif ( indexAttr ) {\n\n\t\t\t\t\tindexArr[ 0 ] = indexAttr.getX( i );\n\t\t\t\t\tindexArr[ 1 ] = indexAttr.getX( i + 1 );\n\t\t\t\t\tindexArr[ 2 ] = indexAttr.getX( i + 2 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tindexArr[ 0 ] = i;\n\t\t\t\t\tindexArr[ 1 ] = i + 1;\n\t\t\t\t\tindexArr[ 2 ] = i + 2;\n\n\t\t\t\t}\n\n\t\t\t\tconst { a, b, c } = _triangle;\n\t\t\t\ta.fromBufferAttribute( positionAttr, indexArr[ 0 ] );\n\t\t\t\tb.fromBufferAttribute( positionAttr, indexArr[ 1 ] );\n\t\t\t\tc.fromBufferAttribute( positionAttr, indexArr[ 2 ] );\n\t\t\t\t_triangle.getNormal( _normal );\n\n\t\t\t\t// create hashes for the edge from the vertices\n\t\t\t\thashes[ 0 ] = `${ Math.round( a.x * precision ) },${ Math.round( a.y * precision ) },${ Math.round( a.z * precision ) }`;\n\t\t\t\thashes[ 1 ] = `${ Math.round( b.x * precision ) },${ Math.round( b.y * precision ) },${ Math.round( b.z * precision ) }`;\n\t\t\t\thashes[ 2 ] = `${ Math.round( c.x * precision ) },${ Math.round( c.y * precision ) },${ Math.round( c.z * precision ) }`;\n\n\t\t\t\t// skip degenerate triangles\n\t\t\t\tif ( hashes[ 0 ] === hashes[ 1 ] || hashes[ 1 ] === hashes[ 2 ] || hashes[ 2 ] === hashes[ 0 ] ) {\n\n\t\t\t\t\tcontinue;\n\n\t\t\t\t}\n\n\t\t\t\t// iterate over every edge\n\t\t\t\tfor ( let j = 0; j < 3; j ++ ) {\n\n\t\t\t\t\t// get the first and next vertex making up the edge\n\t\t\t\t\tconst jNext = ( j + 1 ) % 3;\n\t\t\t\t\tconst vecHash0 = hashes[ j ];\n\t\t\t\t\tconst vecHash1 = hashes[ jNext ];\n\t\t\t\t\tconst v0 = _triangle[ vertKeys[ j ] ];\n\t\t\t\t\tconst v1 = _triangle[ vertKeys[ jNext ] ];\n\n\t\t\t\t\tconst hash = `${ vecHash0 }_${ vecHash1 }`;\n\t\t\t\t\tconst reverseHash = `${ vecHash1 }_${ vecHash0 }`;\n\n\t\t\t\t\tif ( reverseHash in edgeData && edgeData[ reverseHash ] ) {\n\n\t\t\t\t\t\t// if we found a sibling edge add it into the vertex array if\n\t\t\t\t\t\t// it meets the angle threshold and delete the edge from the map.\n\t\t\t\t\t\tif ( _normal.dot( edgeData[ reverseHash ].normal ) <= thresholdDot ) {\n\n\t\t\t\t\t\t\tvertices.push( v0.x, v0.y, v0.z );\n\t\t\t\t\t\t\tvertices.push( v1.x, v1.y, v1.z );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tedgeData[ reverseHash ] = null;\n\n\t\t\t\t\t} else if ( ! ( hash in edgeData ) ) {\n\n\t\t\t\t\t\t// if we've already got an edge here then skip adding a new one\n\t\t\t\t\t\tedgeData[ hash ] = {\n\n\t\t\t\t\t\t\tindex0: indexArr[ j ],\n\t\t\t\t\t\t\tindex1: indexArr[ jNext ],\n\t\t\t\t\t\t\tnormal: _normal.clone(),\n\n\t\t\t\t\t\t};\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// iterate over all remaining, unmatched edges and add them to the vertex array\n\t\t\tfor ( const key in edgeData ) {\n\n\t\t\t\tif ( edgeData[ key ] ) {\n\n\t\t\t\t\tconst { index0, index1 } = edgeData[ key ];\n\t\t\t\t\t_v0.fromBufferAttribute( positionAttr, index0 );\n\t\t\t\t\t_v1$1.fromBufferAttribute( positionAttr, index1 );\n\n\t\t\t\t\tvertices.push( _v0.x, _v0.y, _v0.z );\n\t\t\t\t\tvertices.push( _v1$1.x, _v1$1.y, _v1$1.z );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass Shape extends Path {\n\n\tconstructor( points ) {\n\n\t\tsuper( points );\n\n\t\tthis.uuid = generateUUID();\n\n\t\tthis.type = 'Shape';\n\n\t\tthis.holes = [];\n\n\t}\n\n\tgetPointsHoles( divisions ) {\n\n\t\tconst holesPts = [];\n\n\t\tfor ( let i = 0, l = this.holes.length; i < l; i ++ ) {\n\n\t\t\tholesPts[ i ] = this.holes[ i ].getPoints( divisions );\n\n\t\t}\n\n\t\treturn holesPts;\n\n\t}\n\n\t// get points of shape and holes (keypoints based on segments parameter)\n\n\textractPoints( divisions ) {\n\n\t\treturn {\n\n\t\t\tshape: this.getPoints( divisions ),\n\t\t\tholes: this.getPointsHoles( divisions )\n\n\t\t};\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.holes = [];\n\n\t\tfor ( let i = 0, l = source.holes.length; i < l; i ++ ) {\n\n\t\t\tconst hole = source.holes[ i ];\n\n\t\t\tthis.holes.push( hole.clone() );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.uuid = this.uuid;\n\t\tdata.holes = [];\n\n\t\tfor ( let i = 0, l = this.holes.length; i < l; i ++ ) {\n\n\t\t\tconst hole = this.holes[ i ];\n\t\t\tdata.holes.push( hole.toJSON() );\n\n\t\t}\n\n\t\treturn data;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tsuper.fromJSON( json );\n\n\t\tthis.uuid = json.uuid;\n\t\tthis.holes = [];\n\n\t\tfor ( let i = 0, l = json.holes.length; i < l; i ++ ) {\n\n\t\t\tconst hole = json.holes[ i ];\n\t\t\tthis.holes.push( new Path().fromJSON( hole ) );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n}\n\n/**\n * Port from https://github.com/mapbox/earcut (v2.2.4)\n */\n\nconst Earcut = {\n\n\ttriangulate: function ( data, holeIndices, dim = 2 ) {\n\n\t\tconst hasHoles = holeIndices && holeIndices.length;\n\t\tconst outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length;\n\t\tlet outerNode = linkedList( data, 0, outerLen, dim, true );\n\t\tconst triangles = [];\n\n\t\tif ( ! outerNode || outerNode.next === outerNode.prev ) return triangles;\n\n\t\tlet minX, minY, maxX, maxY, x, y, invSize;\n\n\t\tif ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim );\n\n\t\t// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox\n\t\tif ( data.length > 80 * dim ) {\n\n\t\t\tminX = maxX = data[ 0 ];\n\t\t\tminY = maxY = data[ 1 ];\n\n\t\t\tfor ( let i = dim; i < outerLen; i += dim ) {\n\n\t\t\t\tx = data[ i ];\n\t\t\t\ty = data[ i + 1 ];\n\t\t\t\tif ( x < minX ) minX = x;\n\t\t\t\tif ( y < minY ) minY = y;\n\t\t\t\tif ( x > maxX ) maxX = x;\n\t\t\t\tif ( y > maxY ) maxY = y;\n\n\t\t\t}\n\n\t\t\t// minX, minY and invSize are later used to transform coords into integers for z-order calculation\n\t\t\tinvSize = Math.max( maxX - minX, maxY - minY );\n\t\t\tinvSize = invSize !== 0 ? 32767 / invSize : 0;\n\n\t\t}\n\n\t\tearcutLinked( outerNode, triangles, dim, minX, minY, invSize, 0 );\n\n\t\treturn triangles;\n\n\t}\n\n};\n\n// create a circular doubly linked list from polygon points in the specified winding order\nfunction linkedList( data, start, end, dim, clockwise ) {\n\n\tlet i, last;\n\n\tif ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) {\n\n\t\tfor ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last );\n\n\t} else {\n\n\t\tfor ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last );\n\n\t}\n\n\tif ( last && equals( last, last.next ) ) {\n\n\t\tremoveNode( last );\n\t\tlast = last.next;\n\n\t}\n\n\treturn last;\n\n}\n\n// eliminate colinear or duplicate points\nfunction filterPoints( start, end ) {\n\n\tif ( ! start ) return start;\n\tif ( ! end ) end = start;\n\n\tlet p = start,\n\t\tagain;\n\tdo {\n\n\t\tagain = false;\n\n\t\tif ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) {\n\n\t\t\tremoveNode( p );\n\t\t\tp = end = p.prev;\n\t\t\tif ( p === p.next ) break;\n\t\t\tagain = true;\n\n\t\t} else {\n\n\t\t\tp = p.next;\n\n\t\t}\n\n\t} while ( again || p !== end );\n\n\treturn end;\n\n}\n\n// main ear slicing loop which triangulates a polygon (given as a linked list)\nfunction earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) {\n\n\tif ( ! ear ) return;\n\n\t// interlink polygon nodes in z-order\n\tif ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize );\n\n\tlet stop = ear,\n\t\tprev, next;\n\n\t// iterate through ears, slicing them one by one\n\twhile ( ear.prev !== ear.next ) {\n\n\t\tprev = ear.prev;\n\t\tnext = ear.next;\n\n\t\tif ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) {\n\n\t\t\t// cut off the triangle\n\t\t\ttriangles.push( prev.i / dim | 0 );\n\t\t\ttriangles.push( ear.i / dim | 0 );\n\t\t\ttriangles.push( next.i / dim | 0 );\n\n\t\t\tremoveNode( ear );\n\n\t\t\t// skipping the next vertex leads to less sliver triangles\n\t\t\tear = next.next;\n\t\t\tstop = next.next;\n\n\t\t\tcontinue;\n\n\t\t}\n\n\t\tear = next;\n\n\t\t// if we looped through the whole remaining polygon and can't find any more ears\n\t\tif ( ear === stop ) {\n\n\t\t\t// try filtering points and slicing again\n\t\t\tif ( ! pass ) {\n\n\t\t\t\tearcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 );\n\n\t\t\t\t// if this didn't work, try curing all small self-intersections locally\n\n\t\t\t} else if ( pass === 1 ) {\n\n\t\t\t\tear = cureLocalIntersections( filterPoints( ear ), triangles, dim );\n\t\t\t\tearcutLinked( ear, triangles, dim, minX, minY, invSize, 2 );\n\n\t\t\t\t// as a last resort, try splitting the remaining polygon into two\n\n\t\t\t} else if ( pass === 2 ) {\n\n\t\t\t\tsplitEarcut( ear, triangles, dim, minX, minY, invSize );\n\n\t\t\t}\n\n\t\t\tbreak;\n\n\t\t}\n\n\t}\n\n}\n\n// check whether a polygon node forms a valid ear with adjacent nodes\nfunction isEar( ear ) {\n\n\tconst a = ear.prev,\n\t\tb = ear,\n\t\tc = ear.next;\n\n\tif ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear\n\n\t// now make sure we don't have other points inside the potential ear\n\tconst ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;\n\n\t// triangle bbox; min & max are calculated like this for speed\n\tconst x0 = ax < bx ? ( ax < cx ? ax : cx ) : ( bx < cx ? bx : cx ),\n\t\ty0 = ay < by ? ( ay < cy ? ay : cy ) : ( by < cy ? by : cy ),\n\t\tx1 = ax > bx ? ( ax > cx ? ax : cx ) : ( bx > cx ? bx : cx ),\n\t\ty1 = ay > by ? ( ay > cy ? ay : cy ) : ( by > cy ? by : cy );\n\n\tlet p = c.next;\n\twhile ( p !== a ) {\n\n\t\tif ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 &&\n\t\t\tpointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) &&\n\t\t\tarea( p.prev, p, p.next ) >= 0 ) return false;\n\t\tp = p.next;\n\n\t}\n\n\treturn true;\n\n}\n\nfunction isEarHashed( ear, minX, minY, invSize ) {\n\n\tconst a = ear.prev,\n\t\tb = ear,\n\t\tc = ear.next;\n\n\tif ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear\n\n\tconst ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;\n\n\t// triangle bbox; min & max are calculated like this for speed\n\tconst x0 = ax < bx ? ( ax < cx ? ax : cx ) : ( bx < cx ? bx : cx ),\n\t\ty0 = ay < by ? ( ay < cy ? ay : cy ) : ( by < cy ? by : cy ),\n\t\tx1 = ax > bx ? ( ax > cx ? ax : cx ) : ( bx > cx ? bx : cx ),\n\t\ty1 = ay > by ? ( ay > cy ? ay : cy ) : ( by > cy ? by : cy );\n\n\t// z-order range for the current triangle bbox;\n\tconst minZ = zOrder( x0, y0, minX, minY, invSize ),\n\t\tmaxZ = zOrder( x1, y1, minX, minY, invSize );\n\n\tlet p = ear.prevZ,\n\t\tn = ear.nextZ;\n\n\t// look for points inside the triangle in both directions\n\twhile ( p && p.z >= minZ && n && n.z <= maxZ ) {\n\n\t\tif ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&\n\t\t\tpointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false;\n\t\tp = p.prevZ;\n\n\t\tif ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&\n\t\t\tpointInTriangle( ax, ay, bx, by, cx, cy, n.x, n.y ) && area( n.prev, n, n.next ) >= 0 ) return false;\n\t\tn = n.nextZ;\n\n\t}\n\n\t// look for remaining points in decreasing z-order\n\twhile ( p && p.z >= minZ ) {\n\n\t\tif ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&\n\t\t\tpointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false;\n\t\tp = p.prevZ;\n\n\t}\n\n\t// look for remaining points in increasing z-order\n\twhile ( n && n.z <= maxZ ) {\n\n\t\tif ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&\n\t\t\tpointInTriangle( ax, ay, bx, by, cx, cy, n.x, n.y ) && area( n.prev, n, n.next ) >= 0 ) return false;\n\t\tn = n.nextZ;\n\n\t}\n\n\treturn true;\n\n}\n\n// go through all polygon nodes and cure small local self-intersections\nfunction cureLocalIntersections( start, triangles, dim ) {\n\n\tlet p = start;\n\tdo {\n\n\t\tconst a = p.prev,\n\t\t\tb = p.next.next;\n\n\t\tif ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) {\n\n\t\t\ttriangles.push( a.i / dim | 0 );\n\t\t\ttriangles.push( p.i / dim | 0 );\n\t\t\ttriangles.push( b.i / dim | 0 );\n\n\t\t\t// remove two nodes involved\n\t\t\tremoveNode( p );\n\t\t\tremoveNode( p.next );\n\n\t\t\tp = start = b;\n\n\t\t}\n\n\t\tp = p.next;\n\n\t} while ( p !== start );\n\n\treturn filterPoints( p );\n\n}\n\n// try splitting polygon into two and triangulate them independently\nfunction splitEarcut( start, triangles, dim, minX, minY, invSize ) {\n\n\t// look for a valid diagonal that divides the polygon into two\n\tlet a = start;\n\tdo {\n\n\t\tlet b = a.next.next;\n\t\twhile ( b !== a.prev ) {\n\n\t\t\tif ( a.i !== b.i && isValidDiagonal( a, b ) ) {\n\n\t\t\t\t// split the polygon in two by the diagonal\n\t\t\t\tlet c = splitPolygon( a, b );\n\n\t\t\t\t// filter colinear points around the cuts\n\t\t\t\ta = filterPoints( a, a.next );\n\t\t\t\tc = filterPoints( c, c.next );\n\n\t\t\t\t// run earcut on each half\n\t\t\t\tearcutLinked( a, triangles, dim, minX, minY, invSize, 0 );\n\t\t\t\tearcutLinked( c, triangles, dim, minX, minY, invSize, 0 );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tb = b.next;\n\n\t\t}\n\n\t\ta = a.next;\n\n\t} while ( a !== start );\n\n}\n\n// link every hole into the outer loop, producing a single-ring polygon without holes\nfunction eliminateHoles( data, holeIndices, outerNode, dim ) {\n\n\tconst queue = [];\n\tlet i, len, start, end, list;\n\n\tfor ( i = 0, len = holeIndices.length; i < len; i ++ ) {\n\n\t\tstart = holeIndices[ i ] * dim;\n\t\tend = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length;\n\t\tlist = linkedList( data, start, end, dim, false );\n\t\tif ( list === list.next ) list.steiner = true;\n\t\tqueue.push( getLeftmost( list ) );\n\n\t}\n\n\tqueue.sort( compareX );\n\n\t// process holes from left to right\n\tfor ( i = 0; i < queue.length; i ++ ) {\n\n\t\touterNode = eliminateHole( queue[ i ], outerNode );\n\n\t}\n\n\treturn outerNode;\n\n}\n\nfunction compareX( a, b ) {\n\n\treturn a.x - b.x;\n\n}\n\n// find a bridge between vertices that connects hole with an outer ring and link it\nfunction eliminateHole( hole, outerNode ) {\n\n\tconst bridge = findHoleBridge( hole, outerNode );\n\tif ( ! bridge ) {\n\n\t\treturn outerNode;\n\n\t}\n\n\tconst bridgeReverse = splitPolygon( bridge, hole );\n\n\t// filter collinear points around the cuts\n\tfilterPoints( bridgeReverse, bridgeReverse.next );\n\treturn filterPoints( bridge, bridge.next );\n\n}\n\n// David Eberly's algorithm for finding a bridge between hole and outer polygon\nfunction findHoleBridge( hole, outerNode ) {\n\n\tlet p = outerNode,\n\t\tqx = - Infinity,\n\t\tm;\n\n\tconst hx = hole.x, hy = hole.y;\n\n\t// find a segment intersected by a ray from the hole's leftmost point to the left;\n\t// segment's endpoint with lesser x will be potential connection point\n\tdo {\n\n\t\tif ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) {\n\n\t\t\tconst x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y );\n\t\t\tif ( x <= hx && x > qx ) {\n\n\t\t\t\tqx = x;\n\t\t\t\tm = p.x < p.next.x ? p : p.next;\n\t\t\t\tif ( x === hx ) return m; // hole touches outer segment; pick leftmost endpoint\n\n\t\t\t}\n\n\t\t}\n\n\t\tp = p.next;\n\n\t} while ( p !== outerNode );\n\n\tif ( ! m ) return null;\n\n\t// look for points inside the triangle of hole point, segment intersection and endpoint;\n\t// if there are no points found, we have a valid connection;\n\t// otherwise choose the point of the minimum angle with the ray as connection point\n\n\tconst stop = m,\n\t\tmx = m.x,\n\t\tmy = m.y;\n\tlet tanMin = Infinity, tan;\n\n\tp = m;\n\n\tdo {\n\n\t\tif ( hx >= p.x && p.x >= mx && hx !== p.x &&\n\t\t\t\tpointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) {\n\n\t\t\ttan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential\n\n\t\t\tif ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) {\n\n\t\t\t\tm = p;\n\t\t\t\ttanMin = tan;\n\n\t\t\t}\n\n\t\t}\n\n\t\tp = p.next;\n\n\t} while ( p !== stop );\n\n\treturn m;\n\n}\n\n// whether sector in vertex m contains sector in vertex p in the same coordinates\nfunction sectorContainsSector( m, p ) {\n\n\treturn area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0;\n\n}\n\n// interlink polygon nodes in z-order\nfunction indexCurve( start, minX, minY, invSize ) {\n\n\tlet p = start;\n\tdo {\n\n\t\tif ( p.z === 0 ) p.z = zOrder( p.x, p.y, minX, minY, invSize );\n\t\tp.prevZ = p.prev;\n\t\tp.nextZ = p.next;\n\t\tp = p.next;\n\n\t} while ( p !== start );\n\n\tp.prevZ.nextZ = null;\n\tp.prevZ = null;\n\n\tsortLinked( p );\n\n}\n\n// Simon Tatham's linked list merge sort algorithm\n// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html\nfunction sortLinked( list ) {\n\n\tlet i, p, q, e, tail, numMerges, pSize, qSize,\n\t\tinSize = 1;\n\n\tdo {\n\n\t\tp = list;\n\t\tlist = null;\n\t\ttail = null;\n\t\tnumMerges = 0;\n\n\t\twhile ( p ) {\n\n\t\t\tnumMerges ++;\n\t\t\tq = p;\n\t\t\tpSize = 0;\n\t\t\tfor ( i = 0; i < inSize; i ++ ) {\n\n\t\t\t\tpSize ++;\n\t\t\t\tq = q.nextZ;\n\t\t\t\tif ( ! q ) break;\n\n\t\t\t}\n\n\t\t\tqSize = inSize;\n\n\t\t\twhile ( pSize > 0 || ( qSize > 0 && q ) ) {\n\n\t\t\t\tif ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) {\n\n\t\t\t\t\te = p;\n\t\t\t\t\tp = p.nextZ;\n\t\t\t\t\tpSize --;\n\n\t\t\t\t} else {\n\n\t\t\t\t\te = q;\n\t\t\t\t\tq = q.nextZ;\n\t\t\t\t\tqSize --;\n\n\t\t\t\t}\n\n\t\t\t\tif ( tail ) tail.nextZ = e;\n\t\t\t\telse list = e;\n\n\t\t\t\te.prevZ = tail;\n\t\t\t\ttail = e;\n\n\t\t\t}\n\n\t\t\tp = q;\n\n\t\t}\n\n\t\ttail.nextZ = null;\n\t\tinSize *= 2;\n\n\t} while ( numMerges > 1 );\n\n\treturn list;\n\n}\n\n// z-order of a point given coords and inverse of the longer side of data bbox\nfunction zOrder( x, y, minX, minY, invSize ) {\n\n\t// coords are transformed into non-negative 15-bit integer range\n\tx = ( x - minX ) * invSize | 0;\n\ty = ( y - minY ) * invSize | 0;\n\n\tx = ( x | ( x << 8 ) ) & 0x00FF00FF;\n\tx = ( x | ( x << 4 ) ) & 0x0F0F0F0F;\n\tx = ( x | ( x << 2 ) ) & 0x33333333;\n\tx = ( x | ( x << 1 ) ) & 0x55555555;\n\n\ty = ( y | ( y << 8 ) ) & 0x00FF00FF;\n\ty = ( y | ( y << 4 ) ) & 0x0F0F0F0F;\n\ty = ( y | ( y << 2 ) ) & 0x33333333;\n\ty = ( y | ( y << 1 ) ) & 0x55555555;\n\n\treturn x | ( y << 1 );\n\n}\n\n// find the leftmost node of a polygon ring\nfunction getLeftmost( start ) {\n\n\tlet p = start,\n\t\tleftmost = start;\n\tdo {\n\n\t\tif ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p;\n\t\tp = p.next;\n\n\t} while ( p !== start );\n\n\treturn leftmost;\n\n}\n\n// check if a point lies within a convex triangle\nfunction pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) {\n\n\treturn ( cx - px ) * ( ay - py ) >= ( ax - px ) * ( cy - py ) &&\n           ( ax - px ) * ( by - py ) >= ( bx - px ) * ( ay - py ) &&\n           ( bx - px ) * ( cy - py ) >= ( cx - px ) * ( by - py );\n\n}\n\n// check if a diagonal between two polygon nodes is valid (lies in polygon interior)\nfunction isValidDiagonal( a, b ) {\n\n\treturn a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // dones't intersect other edges\n           ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible\n            ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors\n            equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case\n\n}\n\n// signed area of a triangle\nfunction area( p, q, r ) {\n\n\treturn ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y );\n\n}\n\n// check if two points are equal\nfunction equals( p1, p2 ) {\n\n\treturn p1.x === p2.x && p1.y === p2.y;\n\n}\n\n// check if two segments intersect\nfunction intersects( p1, q1, p2, q2 ) {\n\n\tconst o1 = sign( area( p1, q1, p2 ) );\n\tconst o2 = sign( area( p1, q1, q2 ) );\n\tconst o3 = sign( area( p2, q2, p1 ) );\n\tconst o4 = sign( area( p2, q2, q1 ) );\n\n\tif ( o1 !== o2 && o3 !== o4 ) return true; // general case\n\n\tif ( o1 === 0 && onSegment( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1\n\tif ( o2 === 0 && onSegment( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1\n\tif ( o3 === 0 && onSegment( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2\n\tif ( o4 === 0 && onSegment( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2\n\n\treturn false;\n\n}\n\n// for collinear points p, q, r, check if point q lies on segment pr\nfunction onSegment( p, q, r ) {\n\n\treturn q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y );\n\n}\n\nfunction sign( num ) {\n\n\treturn num > 0 ? 1 : num < 0 ? - 1 : 0;\n\n}\n\n// check if a polygon diagonal intersects any polygon segments\nfunction intersectsPolygon( a, b ) {\n\n\tlet p = a;\n\tdo {\n\n\t\tif ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&\n\t\t\tintersects( p, p.next, a, b ) ) return true;\n\t\tp = p.next;\n\n\t} while ( p !== a );\n\n\treturn false;\n\n}\n\n// check if a polygon diagonal is locally inside the polygon\nfunction locallyInside( a, b ) {\n\n\treturn area( a.prev, a, a.next ) < 0 ?\n\t\tarea( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 :\n\t\tarea( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0;\n\n}\n\n// check if the middle point of a polygon diagonal is inside the polygon\nfunction middleInside( a, b ) {\n\n\tlet p = a,\n\t\tinside = false;\n\tconst px = ( a.x + b.x ) / 2,\n\t\tpy = ( a.y + b.y ) / 2;\n\tdo {\n\n\t\tif ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y &&\n\t\t\t( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) )\n\t\t\tinside = ! inside;\n\t\tp = p.next;\n\n\t} while ( p !== a );\n\n\treturn inside;\n\n}\n\n// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;\n// if one belongs to the outer ring and another to a hole, it merges it into a single ring\nfunction splitPolygon( a, b ) {\n\n\tconst a2 = new Node( a.i, a.x, a.y ),\n\t\tb2 = new Node( b.i, b.x, b.y ),\n\t\tan = a.next,\n\t\tbp = b.prev;\n\n\ta.next = b;\n\tb.prev = a;\n\n\ta2.next = an;\n\tan.prev = a2;\n\n\tb2.next = a2;\n\ta2.prev = b2;\n\n\tbp.next = b2;\n\tb2.prev = bp;\n\n\treturn b2;\n\n}\n\n// create a node and optionally link it with previous one (in a circular doubly linked list)\nfunction insertNode( i, x, y, last ) {\n\n\tconst p = new Node( i, x, y );\n\n\tif ( ! last ) {\n\n\t\tp.prev = p;\n\t\tp.next = p;\n\n\t} else {\n\n\t\tp.next = last.next;\n\t\tp.prev = last;\n\t\tlast.next.prev = p;\n\t\tlast.next = p;\n\n\t}\n\n\treturn p;\n\n}\n\nfunction removeNode( p ) {\n\n\tp.next.prev = p.prev;\n\tp.prev.next = p.next;\n\n\tif ( p.prevZ ) p.prevZ.nextZ = p.nextZ;\n\tif ( p.nextZ ) p.nextZ.prevZ = p.prevZ;\n\n}\n\nfunction Node( i, x, y ) {\n\n\t// vertex index in coordinates array\n\tthis.i = i;\n\n\t// vertex coordinates\n\tthis.x = x;\n\tthis.y = y;\n\n\t// previous and next vertex nodes in a polygon ring\n\tthis.prev = null;\n\tthis.next = null;\n\n\t// z-order curve value\n\tthis.z = 0;\n\n\t// previous and next nodes in z-order\n\tthis.prevZ = null;\n\tthis.nextZ = null;\n\n\t// indicates whether this is a steiner point\n\tthis.steiner = false;\n\n}\n\nfunction signedArea( data, start, end, dim ) {\n\n\tlet sum = 0;\n\tfor ( let i = start, j = end - dim; i < end; i += dim ) {\n\n\t\tsum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] );\n\t\tj = i;\n\n\t}\n\n\treturn sum;\n\n}\n\nclass ShapeUtils {\n\n\t// calculate area of the contour polygon\n\n\tstatic area( contour ) {\n\n\t\tconst n = contour.length;\n\t\tlet a = 0.0;\n\n\t\tfor ( let p = n - 1, q = 0; q < n; p = q ++ ) {\n\n\t\t\ta += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;\n\n\t\t}\n\n\t\treturn a * 0.5;\n\n\t}\n\n\tstatic isClockWise( pts ) {\n\n\t\treturn ShapeUtils.area( pts ) < 0;\n\n\t}\n\n\tstatic triangulateShape( contour, holes ) {\n\n\t\tconst vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ]\n\t\tconst holeIndices = []; // array of hole indices\n\t\tconst faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ]\n\n\t\tremoveDupEndPts( contour );\n\t\taddContour( vertices, contour );\n\n\t\t//\n\n\t\tlet holeIndex = contour.length;\n\n\t\tholes.forEach( removeDupEndPts );\n\n\t\tfor ( let i = 0; i < holes.length; i ++ ) {\n\n\t\t\tholeIndices.push( holeIndex );\n\t\t\tholeIndex += holes[ i ].length;\n\t\t\taddContour( vertices, holes[ i ] );\n\n\t\t}\n\n\t\t//\n\n\t\tconst triangles = Earcut.triangulate( vertices, holeIndices );\n\n\t\t//\n\n\t\tfor ( let i = 0; i < triangles.length; i += 3 ) {\n\n\t\t\tfaces.push( triangles.slice( i, i + 3 ) );\n\n\t\t}\n\n\t\treturn faces;\n\n\t}\n\n}\n\nfunction removeDupEndPts( points ) {\n\n\tconst l = points.length;\n\n\tif ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) {\n\n\t\tpoints.pop();\n\n\t}\n\n}\n\nfunction addContour( vertices, contour ) {\n\n\tfor ( let i = 0; i < contour.length; i ++ ) {\n\n\t\tvertices.push( contour[ i ].x );\n\t\tvertices.push( contour[ i ].y );\n\n\t}\n\n}\n\n/**\n * Creates extruded geometry from a path shape.\n *\n * parameters = {\n *\n *  curveSegments: <int>, // number of points on the curves\n *  steps: <int>, // number of points for z-side extrusions / used for subdividing segments of extrude spline too\n *  depth: <float>, // Depth to extrude the shape\n *\n *  bevelEnabled: <bool>, // turn on bevel\n *  bevelThickness: <float>, // how deep into the original shape bevel goes\n *  bevelSize: <float>, // how far from shape outline (including bevelOffset) is bevel\n *  bevelOffset: <float>, // how far from shape outline does bevel start\n *  bevelSegments: <int>, // number of bevel layers\n *\n *  extrudePath: <THREE.Curve> // curve to extrude shape along\n *\n *  UVGenerator: <Object> // object that provides UV generator functions\n *\n * }\n */\n\n\nclass ExtrudeGeometry extends BufferGeometry {\n\n\tconstructor( shapes = new Shape( [ new Vector2( 0.5, 0.5 ), new Vector2( - 0.5, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), options = {} ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'ExtrudeGeometry';\n\n\t\tthis.parameters = {\n\t\t\tshapes: shapes,\n\t\t\toptions: options\n\t\t};\n\n\t\tshapes = Array.isArray( shapes ) ? shapes : [ shapes ];\n\n\t\tconst scope = this;\n\n\t\tconst verticesArray = [];\n\t\tconst uvArray = [];\n\n\t\tfor ( let i = 0, l = shapes.length; i < l; i ++ ) {\n\n\t\t\tconst shape = shapes[ i ];\n\t\t\taddShape( shape );\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) );\n\n\t\tthis.computeVertexNormals();\n\n\t\t// functions\n\n\t\tfunction addShape( shape ) {\n\n\t\t\tconst placeholder = [];\n\n\t\t\t// options\n\n\t\t\tconst curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;\n\t\t\tconst steps = options.steps !== undefined ? options.steps : 1;\n\t\t\tconst depth = options.depth !== undefined ? options.depth : 1;\n\n\t\t\tlet bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true;\n\t\t\tlet bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2;\n\t\t\tlet bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1;\n\t\t\tlet bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0;\n\t\t\tlet bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;\n\n\t\t\tconst extrudePath = options.extrudePath;\n\n\t\t\tconst uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator;\n\n\t\t\t//\n\n\t\t\tlet extrudePts, extrudeByPath = false;\n\t\t\tlet splineTube, binormal, normal, position2;\n\n\t\t\tif ( extrudePath ) {\n\n\t\t\t\textrudePts = extrudePath.getSpacedPoints( steps );\n\n\t\t\t\textrudeByPath = true;\n\t\t\t\tbevelEnabled = false; // bevels not supported for path extrusion\n\n\t\t\t\t// SETUP TNB variables\n\n\t\t\t\t// TODO1 - have a .isClosed in spline?\n\n\t\t\t\tsplineTube = extrudePath.computeFrenetFrames( steps, false );\n\n\t\t\t\t// console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);\n\n\t\t\t\tbinormal = new Vector3();\n\t\t\t\tnormal = new Vector3();\n\t\t\t\tposition2 = new Vector3();\n\n\t\t\t}\n\n\t\t\t// Safeguards if bevels are not enabled\n\n\t\t\tif ( ! bevelEnabled ) {\n\n\t\t\t\tbevelSegments = 0;\n\t\t\t\tbevelThickness = 0;\n\t\t\t\tbevelSize = 0;\n\t\t\t\tbevelOffset = 0;\n\n\t\t\t}\n\n\t\t\t// Variables initialization\n\n\t\t\tconst shapePoints = shape.extractPoints( curveSegments );\n\n\t\t\tlet vertices = shapePoints.shape;\n\t\t\tconst holes = shapePoints.holes;\n\n\t\t\tconst reverse = ! ShapeUtils.isClockWise( vertices );\n\n\t\t\tif ( reverse ) {\n\n\t\t\t\tvertices = vertices.reverse();\n\n\t\t\t\t// Maybe we should also check if holes are in the opposite direction, just to be safe ...\n\n\t\t\t\tfor ( let h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\t\t\tconst ahole = holes[ h ];\n\n\t\t\t\t\tif ( ShapeUtils.isClockWise( ahole ) ) {\n\n\t\t\t\t\t\tholes[ h ] = ahole.reverse();\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\n\t\t\tconst faces = ShapeUtils.triangulateShape( vertices, holes );\n\n\t\t\t/* Vertices */\n\n\t\t\tconst contour = vertices; // vertices has all points but contour has only points of circumference\n\n\t\t\tfor ( let h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\t\tconst ahole = holes[ h ];\n\n\t\t\t\tvertices = vertices.concat( ahole );\n\n\t\t\t}\n\n\n\t\t\tfunction scalePt2( pt, vec, size ) {\n\n\t\t\t\tif ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' );\n\n\t\t\t\treturn pt.clone().addScaledVector( vec, size );\n\n\t\t\t}\n\n\t\t\tconst vlen = vertices.length, flen = faces.length;\n\n\n\t\t\t// Find directions for point movement\n\n\n\t\t\tfunction getBevelVec( inPt, inPrev, inNext ) {\n\n\t\t\t\t// computes for inPt the corresponding point inPt' on a new contour\n\t\t\t\t//   shifted by 1 unit (length of normalized vector) to the left\n\t\t\t\t// if we walk along contour clockwise, this new contour is outside the old one\n\t\t\t\t//\n\t\t\t\t// inPt' is the intersection of the two lines parallel to the two\n\t\t\t\t//  adjacent edges of inPt at a distance of 1 unit on the left side.\n\n\t\t\t\tlet v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt\n\n\t\t\t\t// good reading for geometry algorithms (here: line-line intersection)\n\t\t\t\t// http://geomalgorithms.com/a05-_intersect-1.html\n\n\t\t\t\tconst v_prev_x = inPt.x - inPrev.x,\n\t\t\t\t\tv_prev_y = inPt.y - inPrev.y;\n\t\t\t\tconst v_next_x = inNext.x - inPt.x,\n\t\t\t\t\tv_next_y = inNext.y - inPt.y;\n\n\t\t\t\tconst v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );\n\n\t\t\t\t// check for collinear edges\n\t\t\t\tconst collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );\n\n\t\t\t\tif ( Math.abs( collinear0 ) > Number.EPSILON ) {\n\n\t\t\t\t\t// not collinear\n\n\t\t\t\t\t// length of vectors for normalizing\n\n\t\t\t\t\tconst v_prev_len = Math.sqrt( v_prev_lensq );\n\t\t\t\t\tconst v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );\n\n\t\t\t\t\t// shift adjacent points by unit vectors to the left\n\n\t\t\t\t\tconst ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );\n\t\t\t\t\tconst ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );\n\n\t\t\t\t\tconst ptNextShift_x = ( inNext.x - v_next_y / v_next_len );\n\t\t\t\t\tconst ptNextShift_y = ( inNext.y + v_next_x / v_next_len );\n\n\t\t\t\t\t// scaling factor for v_prev to intersection point\n\n\t\t\t\t\tconst sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y -\n\t\t\t\t\t\t\t( ptNextShift_y - ptPrevShift_y ) * v_next_x ) /\n\t\t\t\t\t\t( v_prev_x * v_next_y - v_prev_y * v_next_x );\n\n\t\t\t\t\t// vector from inPt to intersection point\n\n\t\t\t\t\tv_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );\n\t\t\t\t\tv_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );\n\n\t\t\t\t\t// Don't normalize!, otherwise sharp corners become ugly\n\t\t\t\t\t//  but prevent crazy spikes\n\t\t\t\t\tconst v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y );\n\t\t\t\t\tif ( v_trans_lensq <= 2 ) {\n\n\t\t\t\t\t\treturn new Vector2( v_trans_x, v_trans_y );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tshrink_by = Math.sqrt( v_trans_lensq / 2 );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// handle special case of collinear edges\n\n\t\t\t\t\tlet direction_eq = false; // assumes: opposite\n\n\t\t\t\t\tif ( v_prev_x > Number.EPSILON ) {\n\n\t\t\t\t\t\tif ( v_next_x > Number.EPSILON ) {\n\n\t\t\t\t\t\t\tdirection_eq = true;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( v_prev_x < - Number.EPSILON ) {\n\n\t\t\t\t\t\t\tif ( v_next_x < - Number.EPSILON ) {\n\n\t\t\t\t\t\t\t\tdirection_eq = true;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tif ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) {\n\n\t\t\t\t\t\t\t\tdirection_eq = true;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( direction_eq ) {\n\n\t\t\t\t\t\t// console.log(\"Warning: lines are a straight sequence\");\n\t\t\t\t\t\tv_trans_x = - v_prev_y;\n\t\t\t\t\t\tv_trans_y = v_prev_x;\n\t\t\t\t\t\tshrink_by = Math.sqrt( v_prev_lensq );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// console.log(\"Warning: lines are a straight spike\");\n\t\t\t\t\t\tv_trans_x = v_prev_x;\n\t\t\t\t\t\tv_trans_y = v_prev_y;\n\t\t\t\t\t\tshrink_by = Math.sqrt( v_prev_lensq / 2 );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\treturn new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );\n\n\t\t\t}\n\n\n\t\t\tconst contourMovements = [];\n\n\t\t\tfor ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {\n\n\t\t\t\tif ( j === il ) j = 0;\n\t\t\t\tif ( k === il ) k = 0;\n\n\t\t\t\t//  (j)---(i)---(k)\n\t\t\t\t// console.log('i,j,k', i, j , k)\n\n\t\t\t\tcontourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] );\n\n\t\t\t}\n\n\t\t\tconst holesMovements = [];\n\t\t\tlet oneHoleMovements, verticesMovements = contourMovements.concat();\n\n\t\t\tfor ( let h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\t\tconst ahole = holes[ h ];\n\n\t\t\t\toneHoleMovements = [];\n\n\t\t\t\tfor ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {\n\n\t\t\t\t\tif ( j === il ) j = 0;\n\t\t\t\t\tif ( k === il ) k = 0;\n\n\t\t\t\t\t//  (j)---(i)---(k)\n\t\t\t\t\toneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );\n\n\t\t\t\t}\n\n\t\t\t\tholesMovements.push( oneHoleMovements );\n\t\t\t\tverticesMovements = verticesMovements.concat( oneHoleMovements );\n\n\t\t\t}\n\n\n\t\t\t// Loop bevelSegments, 1 for the front, 1 for the back\n\n\t\t\tfor ( let b = 0; b < bevelSegments; b ++ ) {\n\n\t\t\t\t//for ( b = bevelSegments; b > 0; b -- ) {\n\n\t\t\t\tconst t = b / bevelSegments;\n\t\t\t\tconst z = bevelThickness * Math.cos( t * Math.PI / 2 );\n\t\t\t\tconst bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset;\n\n\t\t\t\t// contract shape\n\n\t\t\t\tfor ( let i = 0, il = contour.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst vert = scalePt2( contour[ i ], contourMovements[ i ], bs );\n\n\t\t\t\t\tv( vert.x, vert.y, - z );\n\n\t\t\t\t}\n\n\t\t\t\t// expand holes\n\n\t\t\t\tfor ( let h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\t\t\tconst ahole = holes[ h ];\n\t\t\t\t\toneHoleMovements = holesMovements[ h ];\n\n\t\t\t\t\tfor ( let i = 0, il = ahole.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tconst vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );\n\n\t\t\t\t\t\tv( vert.x, vert.y, - z );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tconst bs = bevelSize + bevelOffset;\n\n\t\t\t// Back facing vertices\n\n\t\t\tfor ( let i = 0; i < vlen; i ++ ) {\n\n\t\t\t\tconst vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];\n\n\t\t\t\tif ( ! extrudeByPath ) {\n\n\t\t\t\t\tv( vert.x, vert.y, 0 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );\n\n\t\t\t\t\tnormal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x );\n\t\t\t\t\tbinormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y );\n\n\t\t\t\t\tposition2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal );\n\n\t\t\t\t\tv( position2.x, position2.y, position2.z );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Add stepped vertices...\n\t\t\t// Including front facing vertices\n\n\t\t\tfor ( let s = 1; s <= steps; s ++ ) {\n\n\t\t\t\tfor ( let i = 0; i < vlen; i ++ ) {\n\n\t\t\t\t\tconst vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];\n\n\t\t\t\t\tif ( ! extrudeByPath ) {\n\n\t\t\t\t\t\tv( vert.x, vert.y, depth / steps * s );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );\n\n\t\t\t\t\t\tnormal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x );\n\t\t\t\t\t\tbinormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y );\n\n\t\t\t\t\t\tposition2.copy( extrudePts[ s ] ).add( normal ).add( binormal );\n\n\t\t\t\t\t\tv( position2.x, position2.y, position2.z );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\n\t\t\t// Add bevel segments planes\n\n\t\t\t//for ( b = 1; b <= bevelSegments; b ++ ) {\n\t\t\tfor ( let b = bevelSegments - 1; b >= 0; b -- ) {\n\n\t\t\t\tconst t = b / bevelSegments;\n\t\t\t\tconst z = bevelThickness * Math.cos( t * Math.PI / 2 );\n\t\t\t\tconst bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset;\n\n\t\t\t\t// contract shape\n\n\t\t\t\tfor ( let i = 0, il = contour.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst vert = scalePt2( contour[ i ], contourMovements[ i ], bs );\n\t\t\t\t\tv( vert.x, vert.y, depth + z );\n\n\t\t\t\t}\n\n\t\t\t\t// expand holes\n\n\t\t\t\tfor ( let h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\t\t\tconst ahole = holes[ h ];\n\t\t\t\t\toneHoleMovements = holesMovements[ h ];\n\n\t\t\t\t\tfor ( let i = 0, il = ahole.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tconst vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );\n\n\t\t\t\t\t\tif ( ! extrudeByPath ) {\n\n\t\t\t\t\t\t\tv( vert.x, vert.y, depth + z );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tv( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t/* Faces */\n\n\t\t\t// Top and bottom faces\n\n\t\t\tbuildLidFaces();\n\n\t\t\t// Sides faces\n\n\t\t\tbuildSideFaces();\n\n\n\t\t\t/////  Internal functions\n\n\t\t\tfunction buildLidFaces() {\n\n\t\t\t\tconst start = verticesArray.length / 3;\n\n\t\t\t\tif ( bevelEnabled ) {\n\n\t\t\t\t\tlet layer = 0; // steps + 1\n\t\t\t\t\tlet offset = vlen * layer;\n\n\t\t\t\t\t// Bottom faces\n\n\t\t\t\t\tfor ( let i = 0; i < flen; i ++ ) {\n\n\t\t\t\t\t\tconst face = faces[ i ];\n\t\t\t\t\t\tf3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tlayer = steps + bevelSegments * 2;\n\t\t\t\t\toffset = vlen * layer;\n\n\t\t\t\t\t// Top faces\n\n\t\t\t\t\tfor ( let i = 0; i < flen; i ++ ) {\n\n\t\t\t\t\t\tconst face = faces[ i ];\n\t\t\t\t\t\tf3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// Bottom faces\n\n\t\t\t\t\tfor ( let i = 0; i < flen; i ++ ) {\n\n\t\t\t\t\t\tconst face = faces[ i ];\n\t\t\t\t\t\tf3( face[ 2 ], face[ 1 ], face[ 0 ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// Top faces\n\n\t\t\t\t\tfor ( let i = 0; i < flen; i ++ ) {\n\n\t\t\t\t\t\tconst face = faces[ i ];\n\t\t\t\t\t\tf3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tscope.addGroup( start, verticesArray.length / 3 - start, 0 );\n\n\t\t\t}\n\n\t\t\t// Create faces for the z-sides of the shape\n\n\t\t\tfunction buildSideFaces() {\n\n\t\t\t\tconst start = verticesArray.length / 3;\n\t\t\t\tlet layeroffset = 0;\n\t\t\t\tsidewalls( contour, layeroffset );\n\t\t\t\tlayeroffset += contour.length;\n\n\t\t\t\tfor ( let h = 0, hl = holes.length; h < hl; h ++ ) {\n\n\t\t\t\t\tconst ahole = holes[ h ];\n\t\t\t\t\tsidewalls( ahole, layeroffset );\n\n\t\t\t\t\t//, true\n\t\t\t\t\tlayeroffset += ahole.length;\n\n\t\t\t\t}\n\n\n\t\t\t\tscope.addGroup( start, verticesArray.length / 3 - start, 1 );\n\n\n\t\t\t}\n\n\t\t\tfunction sidewalls( contour, layeroffset ) {\n\n\t\t\t\tlet i = contour.length;\n\n\t\t\t\twhile ( -- i >= 0 ) {\n\n\t\t\t\t\tconst j = i;\n\t\t\t\t\tlet k = i - 1;\n\t\t\t\t\tif ( k < 0 ) k = contour.length - 1;\n\n\t\t\t\t\t//console.log('b', i,j, i-1, k,vertices.length);\n\n\t\t\t\t\tfor ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) {\n\n\t\t\t\t\t\tconst slen1 = vlen * s;\n\t\t\t\t\t\tconst slen2 = vlen * ( s + 1 );\n\n\t\t\t\t\t\tconst a = layeroffset + j + slen1,\n\t\t\t\t\t\t\tb = layeroffset + k + slen1,\n\t\t\t\t\t\t\tc = layeroffset + k + slen2,\n\t\t\t\t\t\t\td = layeroffset + j + slen2;\n\n\t\t\t\t\t\tf4( a, b, c, d );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfunction v( x, y, z ) {\n\n\t\t\t\tplaceholder.push( x );\n\t\t\t\tplaceholder.push( y );\n\t\t\t\tplaceholder.push( z );\n\n\t\t\t}\n\n\n\t\t\tfunction f3( a, b, c ) {\n\n\t\t\t\taddVertex( a );\n\t\t\t\taddVertex( b );\n\t\t\t\taddVertex( c );\n\n\t\t\t\tconst nextIndex = verticesArray.length / 3;\n\t\t\t\tconst uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 );\n\n\t\t\t\taddUV( uvs[ 0 ] );\n\t\t\t\taddUV( uvs[ 1 ] );\n\t\t\t\taddUV( uvs[ 2 ] );\n\n\t\t\t}\n\n\t\t\tfunction f4( a, b, c, d ) {\n\n\t\t\t\taddVertex( a );\n\t\t\t\taddVertex( b );\n\t\t\t\taddVertex( d );\n\n\t\t\t\taddVertex( b );\n\t\t\t\taddVertex( c );\n\t\t\t\taddVertex( d );\n\n\n\t\t\t\tconst nextIndex = verticesArray.length / 3;\n\t\t\t\tconst uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 );\n\n\t\t\t\taddUV( uvs[ 0 ] );\n\t\t\t\taddUV( uvs[ 1 ] );\n\t\t\t\taddUV( uvs[ 3 ] );\n\n\t\t\t\taddUV( uvs[ 1 ] );\n\t\t\t\taddUV( uvs[ 2 ] );\n\t\t\t\taddUV( uvs[ 3 ] );\n\n\t\t\t}\n\n\t\t\tfunction addVertex( index ) {\n\n\t\t\t\tverticesArray.push( placeholder[ index * 3 + 0 ] );\n\t\t\t\tverticesArray.push( placeholder[ index * 3 + 1 ] );\n\t\t\t\tverticesArray.push( placeholder[ index * 3 + 2 ] );\n\n\t\t\t}\n\n\n\t\t\tfunction addUV( vector2 ) {\n\n\t\t\t\tuvArray.push( vector2.x );\n\t\t\t\tuvArray.push( vector2.y );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tconst shapes = this.parameters.shapes;\n\t\tconst options = this.parameters.options;\n\n\t\treturn toJSON$1( shapes, options, data );\n\n\t}\n\n\tstatic fromJSON( data, shapes ) {\n\n\t\tconst geometryShapes = [];\n\n\t\tfor ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) {\n\n\t\t\tconst shape = shapes[ data.shapes[ j ] ];\n\n\t\t\tgeometryShapes.push( shape );\n\n\t\t}\n\n\t\tconst extrudePath = data.options.extrudePath;\n\n\t\tif ( extrudePath !== undefined ) {\n\n\t\t\tdata.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath );\n\n\t\t}\n\n\t\treturn new ExtrudeGeometry( geometryShapes, data.options );\n\n\t}\n\n}\n\nconst WorldUVGenerator = {\n\n\tgenerateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) {\n\n\t\tconst a_x = vertices[ indexA * 3 ];\n\t\tconst a_y = vertices[ indexA * 3 + 1 ];\n\t\tconst b_x = vertices[ indexB * 3 ];\n\t\tconst b_y = vertices[ indexB * 3 + 1 ];\n\t\tconst c_x = vertices[ indexC * 3 ];\n\t\tconst c_y = vertices[ indexC * 3 + 1 ];\n\n\t\treturn [\n\t\t\tnew Vector2( a_x, a_y ),\n\t\t\tnew Vector2( b_x, b_y ),\n\t\t\tnew Vector2( c_x, c_y )\n\t\t];\n\n\t},\n\n\tgenerateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) {\n\n\t\tconst a_x = vertices[ indexA * 3 ];\n\t\tconst a_y = vertices[ indexA * 3 + 1 ];\n\t\tconst a_z = vertices[ indexA * 3 + 2 ];\n\t\tconst b_x = vertices[ indexB * 3 ];\n\t\tconst b_y = vertices[ indexB * 3 + 1 ];\n\t\tconst b_z = vertices[ indexB * 3 + 2 ];\n\t\tconst c_x = vertices[ indexC * 3 ];\n\t\tconst c_y = vertices[ indexC * 3 + 1 ];\n\t\tconst c_z = vertices[ indexC * 3 + 2 ];\n\t\tconst d_x = vertices[ indexD * 3 ];\n\t\tconst d_y = vertices[ indexD * 3 + 1 ];\n\t\tconst d_z = vertices[ indexD * 3 + 2 ];\n\n\t\tif ( Math.abs( a_y - b_y ) < Math.abs( a_x - b_x ) ) {\n\n\t\t\treturn [\n\t\t\t\tnew Vector2( a_x, 1 - a_z ),\n\t\t\t\tnew Vector2( b_x, 1 - b_z ),\n\t\t\t\tnew Vector2( c_x, 1 - c_z ),\n\t\t\t\tnew Vector2( d_x, 1 - d_z )\n\t\t\t];\n\n\t\t} else {\n\n\t\t\treturn [\n\t\t\t\tnew Vector2( a_y, 1 - a_z ),\n\t\t\t\tnew Vector2( b_y, 1 - b_z ),\n\t\t\t\tnew Vector2( c_y, 1 - c_z ),\n\t\t\t\tnew Vector2( d_y, 1 - d_z )\n\t\t\t];\n\n\t\t}\n\n\t}\n\n};\n\nfunction toJSON$1( shapes, options, data ) {\n\n\tdata.shapes = [];\n\n\tif ( Array.isArray( shapes ) ) {\n\n\t\tfor ( let i = 0, l = shapes.length; i < l; i ++ ) {\n\n\t\t\tconst shape = shapes[ i ];\n\n\t\t\tdata.shapes.push( shape.uuid );\n\n\t\t}\n\n\t} else {\n\n\t\tdata.shapes.push( shapes.uuid );\n\n\t}\n\n\tdata.options = Object.assign( {}, options );\n\n\tif ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON();\n\n\treturn data;\n\n}\n\nclass IcosahedronGeometry extends PolyhedronGeometry {\n\n\tconstructor( radius = 1, detail = 0 ) {\n\n\t\tconst t = ( 1 + Math.sqrt( 5 ) ) / 2;\n\n\t\tconst vertices = [\n\t\t\t- 1, t, 0, \t1, t, 0, \t- 1, - t, 0, \t1, - t, 0,\n\t\t\t0, - 1, t, \t0, 1, t,\t0, - 1, - t, \t0, 1, - t,\n\t\t\tt, 0, - 1, \tt, 0, 1, \t- t, 0, - 1, \t- t, 0, 1\n\t\t];\n\n\t\tconst indices = [\n\t\t\t0, 11, 5, \t0, 5, 1, \t0, 1, 7, \t0, 7, 10, \t0, 10, 11,\n\t\t\t1, 5, 9, \t5, 11, 4,\t11, 10, 2,\t10, 7, 6,\t7, 1, 8,\n\t\t\t3, 9, 4, \t3, 4, 2,\t3, 2, 6,\t3, 6, 8,\t3, 8, 9,\n\t\t\t4, 9, 5, \t2, 4, 11,\t6, 2, 10,\t8, 6, 7,\t9, 8, 1\n\t\t];\n\n\t\tsuper( vertices, indices, radius, detail );\n\n\t\tthis.type = 'IcosahedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new IcosahedronGeometry( data.radius, data.detail );\n\n\t}\n\n}\n\nclass OctahedronGeometry extends PolyhedronGeometry {\n\n\tconstructor( radius = 1, detail = 0 ) {\n\n\t\tconst vertices = [\n\t\t\t1, 0, 0, \t- 1, 0, 0,\t0, 1, 0,\n\t\t\t0, - 1, 0, \t0, 0, 1,\t0, 0, - 1\n\t\t];\n\n\t\tconst indices = [\n\t\t\t0, 2, 4,\t0, 4, 3,\t0, 3, 5,\n\t\t\t0, 5, 2,\t1, 2, 5,\t1, 5, 3,\n\t\t\t1, 3, 4,\t1, 4, 2\n\t\t];\n\n\t\tsuper( vertices, indices, radius, detail );\n\n\t\tthis.type = 'OctahedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new OctahedronGeometry( data.radius, data.detail );\n\n\t}\n\n}\n\nclass RingGeometry extends BufferGeometry {\n\n\tconstructor( innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'RingGeometry';\n\n\t\tthis.parameters = {\n\t\t\tinnerRadius: innerRadius,\n\t\t\touterRadius: outerRadius,\n\t\t\tthetaSegments: thetaSegments,\n\t\t\tphiSegments: phiSegments,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\tthetaSegments = Math.max( 3, thetaSegments );\n\t\tphiSegments = Math.max( 1, phiSegments );\n\n\t\t// buffers\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\t// some helper variables\n\n\t\tlet radius = innerRadius;\n\t\tconst radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );\n\t\tconst vertex = new Vector3();\n\t\tconst uv = new Vector2();\n\n\t\t// generate vertices, normals and uvs\n\n\t\tfor ( let j = 0; j <= phiSegments; j ++ ) {\n\n\t\t\tfor ( let i = 0; i <= thetaSegments; i ++ ) {\n\n\t\t\t\t// values are generate from the inside of the ring to the outside\n\n\t\t\t\tconst segment = thetaStart + i / thetaSegments * thetaLength;\n\n\t\t\t\t// vertex\n\n\t\t\t\tvertex.x = radius * Math.cos( segment );\n\t\t\t\tvertex.y = radius * Math.sin( segment );\n\n\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t// normal\n\n\t\t\t\tnormals.push( 0, 0, 1 );\n\n\t\t\t\t// uv\n\n\t\t\t\tuv.x = ( vertex.x / outerRadius + 1 ) / 2;\n\t\t\t\tuv.y = ( vertex.y / outerRadius + 1 ) / 2;\n\n\t\t\t\tuvs.push( uv.x, uv.y );\n\n\t\t\t}\n\n\t\t\t// increase the radius for next row of vertices\n\n\t\t\tradius += radiusStep;\n\n\t\t}\n\n\t\t// indices\n\n\t\tfor ( let j = 0; j < phiSegments; j ++ ) {\n\n\t\t\tconst thetaSegmentLevel = j * ( thetaSegments + 1 );\n\n\t\t\tfor ( let i = 0; i < thetaSegments; i ++ ) {\n\n\t\t\t\tconst segment = i + thetaSegmentLevel;\n\n\t\t\t\tconst a = segment;\n\t\t\t\tconst b = segment + thetaSegments + 1;\n\t\t\t\tconst c = segment + thetaSegments + 2;\n\t\t\t\tconst d = segment + 1;\n\n\t\t\t\t// faces\n\n\t\t\t\tindices.push( a, b, d );\n\t\t\t\tindices.push( b, c, d );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new RingGeometry( data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength );\n\n\t}\n\n}\n\nclass ShapeGeometry extends BufferGeometry {\n\n\tconstructor( shapes = new Shape( [ new Vector2( 0, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), curveSegments = 12 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'ShapeGeometry';\n\n\t\tthis.parameters = {\n\t\t\tshapes: shapes,\n\t\t\tcurveSegments: curveSegments\n\t\t};\n\n\t\t// buffers\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\t// helper variables\n\n\t\tlet groupStart = 0;\n\t\tlet groupCount = 0;\n\n\t\t// allow single and array values for \"shapes\" parameter\n\n\t\tif ( Array.isArray( shapes ) === false ) {\n\n\t\t\taddShape( shapes );\n\n\t\t} else {\n\n\t\t\tfor ( let i = 0; i < shapes.length; i ++ ) {\n\n\t\t\t\taddShape( shapes[ i ] );\n\n\t\t\t\tthis.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support\n\n\t\t\t\tgroupStart += groupCount;\n\t\t\t\tgroupCount = 0;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\n\t\t// helper functions\n\n\t\tfunction addShape( shape ) {\n\n\t\t\tconst indexOffset = vertices.length / 3;\n\t\t\tconst points = shape.extractPoints( curveSegments );\n\n\t\t\tlet shapeVertices = points.shape;\n\t\t\tconst shapeHoles = points.holes;\n\n\t\t\t// check direction of vertices\n\n\t\t\tif ( ShapeUtils.isClockWise( shapeVertices ) === false ) {\n\n\t\t\t\tshapeVertices = shapeVertices.reverse();\n\n\t\t\t}\n\n\t\t\tfor ( let i = 0, l = shapeHoles.length; i < l; i ++ ) {\n\n\t\t\t\tconst shapeHole = shapeHoles[ i ];\n\n\t\t\t\tif ( ShapeUtils.isClockWise( shapeHole ) === true ) {\n\n\t\t\t\t\tshapeHoles[ i ] = shapeHole.reverse();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tconst faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles );\n\n\t\t\t// join vertices of inner and outer paths to a single array\n\n\t\t\tfor ( let i = 0, l = shapeHoles.length; i < l; i ++ ) {\n\n\t\t\t\tconst shapeHole = shapeHoles[ i ];\n\t\t\t\tshapeVertices = shapeVertices.concat( shapeHole );\n\n\t\t\t}\n\n\t\t\t// vertices, normals, uvs\n\n\t\t\tfor ( let i = 0, l = shapeVertices.length; i < l; i ++ ) {\n\n\t\t\t\tconst vertex = shapeVertices[ i ];\n\n\t\t\t\tvertices.push( vertex.x, vertex.y, 0 );\n\t\t\t\tnormals.push( 0, 0, 1 );\n\t\t\t\tuvs.push( vertex.x, vertex.y ); // world uvs\n\n\t\t\t}\n\n\t\t\t// indices\n\n\t\t\tfor ( let i = 0, l = faces.length; i < l; i ++ ) {\n\n\t\t\t\tconst face = faces[ i ];\n\n\t\t\t\tconst a = face[ 0 ] + indexOffset;\n\t\t\t\tconst b = face[ 1 ] + indexOffset;\n\t\t\t\tconst c = face[ 2 ] + indexOffset;\n\n\t\t\t\tindices.push( a, b, c );\n\t\t\t\tgroupCount += 3;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tconst shapes = this.parameters.shapes;\n\n\t\treturn toJSON( shapes, data );\n\n\t}\n\n\tstatic fromJSON( data, shapes ) {\n\n\t\tconst geometryShapes = [];\n\n\t\tfor ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) {\n\n\t\t\tconst shape = shapes[ data.shapes[ j ] ];\n\n\t\t\tgeometryShapes.push( shape );\n\n\t\t}\n\n\t\treturn new ShapeGeometry( geometryShapes, data.curveSegments );\n\n\t}\n\n}\n\nfunction toJSON( shapes, data ) {\n\n\tdata.shapes = [];\n\n\tif ( Array.isArray( shapes ) ) {\n\n\t\tfor ( let i = 0, l = shapes.length; i < l; i ++ ) {\n\n\t\t\tconst shape = shapes[ i ];\n\n\t\t\tdata.shapes.push( shape.uuid );\n\n\t\t}\n\n\t} else {\n\n\t\tdata.shapes.push( shapes.uuid );\n\n\t}\n\n\treturn data;\n\n}\n\nclass SphereGeometry extends BufferGeometry {\n\n\tconstructor( radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'SphereGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\twidthSegments: widthSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\tphiStart: phiStart,\n\t\t\tphiLength: phiLength,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\twidthSegments = Math.max( 3, Math.floor( widthSegments ) );\n\t\theightSegments = Math.max( 2, Math.floor( heightSegments ) );\n\n\t\tconst thetaEnd = Math.min( thetaStart + thetaLength, Math.PI );\n\n\t\tlet index = 0;\n\t\tconst grid = [];\n\n\t\tconst vertex = new Vector3();\n\t\tconst normal = new Vector3();\n\n\t\t// buffers\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\t// generate vertices, normals and uvs\n\n\t\tfor ( let iy = 0; iy <= heightSegments; iy ++ ) {\n\n\t\t\tconst verticesRow = [];\n\n\t\t\tconst v = iy / heightSegments;\n\n\t\t\t// special case for the poles\n\n\t\t\tlet uOffset = 0;\n\n\t\t\tif ( iy === 0 && thetaStart === 0 ) {\n\n\t\t\t\tuOffset = 0.5 / widthSegments;\n\n\t\t\t} else if ( iy === heightSegments && thetaEnd === Math.PI ) {\n\n\t\t\t\tuOffset = - 0.5 / widthSegments;\n\n\t\t\t}\n\n\t\t\tfor ( let ix = 0; ix <= widthSegments; ix ++ ) {\n\n\t\t\t\tconst u = ix / widthSegments;\n\n\t\t\t\t// vertex\n\n\t\t\t\tvertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );\n\t\t\t\tvertex.y = radius * Math.cos( thetaStart + v * thetaLength );\n\t\t\t\tvertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );\n\n\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t// normal\n\n\t\t\t\tnormal.copy( vertex ).normalize();\n\t\t\t\tnormals.push( normal.x, normal.y, normal.z );\n\n\t\t\t\t// uv\n\n\t\t\t\tuvs.push( u + uOffset, 1 - v );\n\n\t\t\t\tverticesRow.push( index ++ );\n\n\t\t\t}\n\n\t\t\tgrid.push( verticesRow );\n\n\t\t}\n\n\t\t// indices\n\n\t\tfor ( let iy = 0; iy < heightSegments; iy ++ ) {\n\n\t\t\tfor ( let ix = 0; ix < widthSegments; ix ++ ) {\n\n\t\t\t\tconst a = grid[ iy ][ ix + 1 ];\n\t\t\t\tconst b = grid[ iy ][ ix ];\n\t\t\t\tconst c = grid[ iy + 1 ][ ix ];\n\t\t\t\tconst d = grid[ iy + 1 ][ ix + 1 ];\n\n\t\t\t\tif ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d );\n\t\t\t\tif ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new SphereGeometry( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength );\n\n\t}\n\n}\n\nclass TetrahedronGeometry extends PolyhedronGeometry {\n\n\tconstructor( radius = 1, detail = 0 ) {\n\n\t\tconst vertices = [\n\t\t\t1, 1, 1, \t- 1, - 1, 1, \t- 1, 1, - 1, \t1, - 1, - 1\n\t\t];\n\n\t\tconst indices = [\n\t\t\t2, 1, 0, \t0, 3, 2,\t1, 3, 0,\t2, 3, 1\n\t\t];\n\n\t\tsuper( vertices, indices, radius, detail );\n\n\t\tthis.type = 'TetrahedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new TetrahedronGeometry( data.radius, data.detail );\n\n\t}\n\n}\n\nclass TorusGeometry extends BufferGeometry {\n\n\tconstructor( radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'TorusGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\ttube: tube,\n\t\t\tradialSegments: radialSegments,\n\t\t\ttubularSegments: tubularSegments,\n\t\t\tarc: arc\n\t\t};\n\n\t\tradialSegments = Math.floor( radialSegments );\n\t\ttubularSegments = Math.floor( tubularSegments );\n\n\t\t// buffers\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\t// helper variables\n\n\t\tconst center = new Vector3();\n\t\tconst vertex = new Vector3();\n\t\tconst normal = new Vector3();\n\n\t\t// generate vertices, normals and uvs\n\n\t\tfor ( let j = 0; j <= radialSegments; j ++ ) {\n\n\t\t\tfor ( let i = 0; i <= tubularSegments; i ++ ) {\n\n\t\t\t\tconst u = i / tubularSegments * arc;\n\t\t\t\tconst v = j / radialSegments * Math.PI * 2;\n\n\t\t\t\t// vertex\n\n\t\t\t\tvertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );\n\t\t\t\tvertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );\n\t\t\t\tvertex.z = tube * Math.sin( v );\n\n\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t// normal\n\n\t\t\t\tcenter.x = radius * Math.cos( u );\n\t\t\t\tcenter.y = radius * Math.sin( u );\n\t\t\t\tnormal.subVectors( vertex, center ).normalize();\n\n\t\t\t\tnormals.push( normal.x, normal.y, normal.z );\n\n\t\t\t\t// uv\n\n\t\t\t\tuvs.push( i / tubularSegments );\n\t\t\t\tuvs.push( j / radialSegments );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// generate indices\n\n\t\tfor ( let j = 1; j <= radialSegments; j ++ ) {\n\n\t\t\tfor ( let i = 1; i <= tubularSegments; i ++ ) {\n\n\t\t\t\t// indices\n\n\t\t\t\tconst a = ( tubularSegments + 1 ) * j + i - 1;\n\t\t\t\tconst b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;\n\t\t\t\tconst c = ( tubularSegments + 1 ) * ( j - 1 ) + i;\n\t\t\t\tconst d = ( tubularSegments + 1 ) * j + i;\n\n\t\t\t\t// faces\n\n\t\t\t\tindices.push( a, b, d );\n\t\t\t\tindices.push( b, c, d );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new TorusGeometry( data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc );\n\n\t}\n\n}\n\nclass TorusKnotGeometry extends BufferGeometry {\n\n\tconstructor( radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'TorusKnotGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\ttube: tube,\n\t\t\ttubularSegments: tubularSegments,\n\t\t\tradialSegments: radialSegments,\n\t\t\tp: p,\n\t\t\tq: q\n\t\t};\n\n\t\ttubularSegments = Math.floor( tubularSegments );\n\t\tradialSegments = Math.floor( radialSegments );\n\n\t\t// buffers\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\t// helper variables\n\n\t\tconst vertex = new Vector3();\n\t\tconst normal = new Vector3();\n\n\t\tconst P1 = new Vector3();\n\t\tconst P2 = new Vector3();\n\n\t\tconst B = new Vector3();\n\t\tconst T = new Vector3();\n\t\tconst N = new Vector3();\n\n\t\t// generate vertices, normals and uvs\n\n\t\tfor ( let i = 0; i <= tubularSegments; ++ i ) {\n\n\t\t\t// the radian \"u\" is used to calculate the position on the torus curve of the current tubular segment\n\n\t\t\tconst u = i / tubularSegments * p * Math.PI * 2;\n\n\t\t\t// now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead.\n\t\t\t// these points are used to create a special \"coordinate space\", which is necessary to calculate the correct vertex positions\n\n\t\t\tcalculatePositionOnCurve( u, p, q, radius, P1 );\n\t\t\tcalculatePositionOnCurve( u + 0.01, p, q, radius, P2 );\n\n\t\t\t// calculate orthonormal basis\n\n\t\t\tT.subVectors( P2, P1 );\n\t\t\tN.addVectors( P2, P1 );\n\t\t\tB.crossVectors( T, N );\n\t\t\tN.crossVectors( B, T );\n\n\t\t\t// normalize B, N. T can be ignored, we don't use it\n\n\t\t\tB.normalize();\n\t\t\tN.normalize();\n\n\t\t\tfor ( let j = 0; j <= radialSegments; ++ j ) {\n\n\t\t\t\t// now calculate the vertices. they are nothing more than an extrusion of the torus curve.\n\t\t\t\t// because we extrude a shape in the xy-plane, there is no need to calculate a z-value.\n\n\t\t\t\tconst v = j / radialSegments * Math.PI * 2;\n\t\t\t\tconst cx = - tube * Math.cos( v );\n\t\t\t\tconst cy = tube * Math.sin( v );\n\n\t\t\t\t// now calculate the final vertex position.\n\t\t\t\t// first we orient the extrusion with our basis vectors, then we add it to the current position on the curve\n\n\t\t\t\tvertex.x = P1.x + ( cx * N.x + cy * B.x );\n\t\t\t\tvertex.y = P1.y + ( cx * N.y + cy * B.y );\n\t\t\t\tvertex.z = P1.z + ( cx * N.z + cy * B.z );\n\n\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t// normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal)\n\n\t\t\t\tnormal.subVectors( vertex, P1 ).normalize();\n\n\t\t\t\tnormals.push( normal.x, normal.y, normal.z );\n\n\t\t\t\t// uv\n\n\t\t\t\tuvs.push( i / tubularSegments );\n\t\t\t\tuvs.push( j / radialSegments );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// generate indices\n\n\t\tfor ( let j = 1; j <= tubularSegments; j ++ ) {\n\n\t\t\tfor ( let i = 1; i <= radialSegments; i ++ ) {\n\n\t\t\t\t// indices\n\n\t\t\t\tconst a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );\n\t\t\t\tconst b = ( radialSegments + 1 ) * j + ( i - 1 );\n\t\t\t\tconst c = ( radialSegments + 1 ) * j + i;\n\t\t\t\tconst d = ( radialSegments + 1 ) * ( j - 1 ) + i;\n\n\t\t\t\t// faces\n\n\t\t\t\tindices.push( a, b, d );\n\t\t\t\tindices.push( b, c, d );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t\t// this function calculates the current position on the torus curve\n\n\t\tfunction calculatePositionOnCurve( u, p, q, radius, position ) {\n\n\t\t\tconst cu = Math.cos( u );\n\t\t\tconst su = Math.sin( u );\n\t\t\tconst quOverP = q / p * u;\n\t\t\tconst cs = Math.cos( quOverP );\n\n\t\t\tposition.x = radius * ( 2 + cs ) * 0.5 * cu;\n\t\t\tposition.y = radius * ( 2 + cs ) * su * 0.5;\n\t\t\tposition.z = radius * Math.sin( quOverP ) * 0.5;\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new TorusKnotGeometry( data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q );\n\n\t}\n\n}\n\nclass TubeGeometry extends BufferGeometry {\n\n\tconstructor( path = new QuadraticBezierCurve3( new Vector3( - 1, - 1, 0 ), new Vector3( - 1, 1, 0 ), new Vector3( 1, 1, 0 ) ), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'TubeGeometry';\n\n\t\tthis.parameters = {\n\t\t\tpath: path,\n\t\t\ttubularSegments: tubularSegments,\n\t\t\tradius: radius,\n\t\t\tradialSegments: radialSegments,\n\t\t\tclosed: closed\n\t\t};\n\n\t\tconst frames = path.computeFrenetFrames( tubularSegments, closed );\n\n\t\t// expose internals\n\n\t\tthis.tangents = frames.tangents;\n\t\tthis.normals = frames.normals;\n\t\tthis.binormals = frames.binormals;\n\n\t\t// helper variables\n\n\t\tconst vertex = new Vector3();\n\t\tconst normal = new Vector3();\n\t\tconst uv = new Vector2();\n\t\tlet P = new Vector3();\n\n\t\t// buffer\n\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\t\tconst indices = [];\n\n\t\t// create buffer data\n\n\t\tgenerateBufferData();\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t\t// functions\n\n\t\tfunction generateBufferData() {\n\n\t\t\tfor ( let i = 0; i < tubularSegments; i ++ ) {\n\n\t\t\t\tgenerateSegment( i );\n\n\t\t\t}\n\n\t\t\t// if the geometry is not closed, generate the last row of vertices and normals\n\t\t\t// at the regular position on the given path\n\t\t\t//\n\t\t\t// if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ)\n\n\t\t\tgenerateSegment( ( closed === false ) ? tubularSegments : 0 );\n\n\t\t\t// uvs are generated in a separate function.\n\t\t\t// this makes it easy compute correct values for closed geometries\n\n\t\t\tgenerateUVs();\n\n\t\t\t// finally create faces\n\n\t\t\tgenerateIndices();\n\n\t\t}\n\n\t\tfunction generateSegment( i ) {\n\n\t\t\t// we use getPointAt to sample evenly distributed points from the given path\n\n\t\t\tP = path.getPointAt( i / tubularSegments, P );\n\n\t\t\t// retrieve corresponding normal and binormal\n\n\t\t\tconst N = frames.normals[ i ];\n\t\t\tconst B = frames.binormals[ i ];\n\n\t\t\t// generate normals and vertices for the current segment\n\n\t\t\tfor ( let j = 0; j <= radialSegments; j ++ ) {\n\n\t\t\t\tconst v = j / radialSegments * Math.PI * 2;\n\n\t\t\t\tconst sin = Math.sin( v );\n\t\t\t\tconst cos = - Math.cos( v );\n\n\t\t\t\t// normal\n\n\t\t\t\tnormal.x = ( cos * N.x + sin * B.x );\n\t\t\t\tnormal.y = ( cos * N.y + sin * B.y );\n\t\t\t\tnormal.z = ( cos * N.z + sin * B.z );\n\t\t\t\tnormal.normalize();\n\n\t\t\t\tnormals.push( normal.x, normal.y, normal.z );\n\n\t\t\t\t// vertex\n\n\t\t\t\tvertex.x = P.x + radius * normal.x;\n\t\t\t\tvertex.y = P.y + radius * normal.y;\n\t\t\t\tvertex.z = P.z + radius * normal.z;\n\n\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction generateIndices() {\n\n\t\t\tfor ( let j = 1; j <= tubularSegments; j ++ ) {\n\n\t\t\t\tfor ( let i = 1; i <= radialSegments; i ++ ) {\n\n\t\t\t\t\tconst a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );\n\t\t\t\t\tconst b = ( radialSegments + 1 ) * j + ( i - 1 );\n\t\t\t\t\tconst c = ( radialSegments + 1 ) * j + i;\n\t\t\t\t\tconst d = ( radialSegments + 1 ) * ( j - 1 ) + i;\n\n\t\t\t\t\t// faces\n\n\t\t\t\t\tindices.push( a, b, d );\n\t\t\t\t\tindices.push( b, c, d );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction generateUVs() {\n\n\t\t\tfor ( let i = 0; i <= tubularSegments; i ++ ) {\n\n\t\t\t\tfor ( let j = 0; j <= radialSegments; j ++ ) {\n\n\t\t\t\t\tuv.x = i / tubularSegments;\n\t\t\t\t\tuv.y = j / radialSegments;\n\n\t\t\t\t\tuvs.push( uv.x, uv.y );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.path = this.parameters.path.toJSON();\n\n\t\treturn data;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\t// This only works for built-in curves (e.g. CatmullRomCurve3).\n\t\t// User defined curves or instances of CurvePath will not be deserialized.\n\t\treturn new TubeGeometry(\n\t\t\tnew Curves[ data.path.type ]().fromJSON( data.path ),\n\t\t\tdata.tubularSegments,\n\t\t\tdata.radius,\n\t\t\tdata.radialSegments,\n\t\t\tdata.closed\n\t\t);\n\n\t}\n\n}\n\nclass WireframeGeometry extends BufferGeometry {\n\n\tconstructor( geometry = null ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'WireframeGeometry';\n\n\t\tthis.parameters = {\n\t\t\tgeometry: geometry\n\t\t};\n\n\t\tif ( geometry !== null ) {\n\n\t\t\t// buffer\n\n\t\t\tconst vertices = [];\n\t\t\tconst edges = new Set();\n\n\t\t\t// helper variables\n\n\t\t\tconst start = new Vector3();\n\t\t\tconst end = new Vector3();\n\n\t\t\tif ( geometry.index !== null ) {\n\n\t\t\t\t// indexed BufferGeometry\n\n\t\t\t\tconst position = geometry.attributes.position;\n\t\t\t\tconst indices = geometry.index;\n\t\t\t\tlet groups = geometry.groups;\n\n\t\t\t\tif ( groups.length === 0 ) {\n\n\t\t\t\t\tgroups = [ { start: 0, count: indices.count, materialIndex: 0 } ];\n\n\t\t\t\t}\n\n\t\t\t\t// create a data structure that contains all edges without duplicates\n\n\t\t\t\tfor ( let o = 0, ol = groups.length; o < ol; ++ o ) {\n\n\t\t\t\t\tconst group = groups[ o ];\n\n\t\t\t\t\tconst groupStart = group.start;\n\t\t\t\t\tconst groupCount = group.count;\n\n\t\t\t\t\tfor ( let i = groupStart, l = ( groupStart + groupCount ); i < l; i += 3 ) {\n\n\t\t\t\t\t\tfor ( let j = 0; j < 3; j ++ ) {\n\n\t\t\t\t\t\t\tconst index1 = indices.getX( i + j );\n\t\t\t\t\t\t\tconst index2 = indices.getX( i + ( j + 1 ) % 3 );\n\n\t\t\t\t\t\t\tstart.fromBufferAttribute( position, index1 );\n\t\t\t\t\t\t\tend.fromBufferAttribute( position, index2 );\n\n\t\t\t\t\t\t\tif ( isUniqueEdge( start, end, edges ) === true ) {\n\n\t\t\t\t\t\t\t\tvertices.push( start.x, start.y, start.z );\n\t\t\t\t\t\t\t\tvertices.push( end.x, end.y, end.z );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// non-indexed BufferGeometry\n\n\t\t\t\tconst position = geometry.attributes.position;\n\n\t\t\t\tfor ( let i = 0, l = ( position.count / 3 ); i < l; i ++ ) {\n\n\t\t\t\t\tfor ( let j = 0; j < 3; j ++ ) {\n\n\t\t\t\t\t\t// three edges per triangle, an edge is represented as (index1, index2)\n\t\t\t\t\t\t// e.g. the first triangle has the following edges: (0,1),(1,2),(2,0)\n\n\t\t\t\t\t\tconst index1 = 3 * i + j;\n\t\t\t\t\t\tconst index2 = 3 * i + ( ( j + 1 ) % 3 );\n\n\t\t\t\t\t\tstart.fromBufferAttribute( position, index1 );\n\t\t\t\t\t\tend.fromBufferAttribute( position, index2 );\n\n\t\t\t\t\t\tif ( isUniqueEdge( start, end, edges ) === true ) {\n\n\t\t\t\t\t\t\tvertices.push( start.x, start.y, start.z );\n\t\t\t\t\t\t\tvertices.push( end.x, end.y, end.z );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// build geometry\n\n\t\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n}\n\nfunction isUniqueEdge( start, end, edges ) {\n\n\tconst hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`;\n\tconst hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge\n\n\tif ( edges.has( hash1 ) === true || edges.has( hash2 ) === true ) {\n\n\t\treturn false;\n\n\t} else {\n\n\t\tedges.add( hash1 );\n\t\tedges.add( hash2 );\n\t\treturn true;\n\n\t}\n\n}\n\nvar Geometries = /*#__PURE__*/Object.freeze({\n\t__proto__: null,\n\tBoxGeometry: BoxGeometry,\n\tCapsuleGeometry: CapsuleGeometry,\n\tCircleGeometry: CircleGeometry,\n\tConeGeometry: ConeGeometry,\n\tCylinderGeometry: CylinderGeometry,\n\tDodecahedronGeometry: DodecahedronGeometry,\n\tEdgesGeometry: EdgesGeometry,\n\tExtrudeGeometry: ExtrudeGeometry,\n\tIcosahedronGeometry: IcosahedronGeometry,\n\tLatheGeometry: LatheGeometry,\n\tOctahedronGeometry: OctahedronGeometry,\n\tPlaneGeometry: PlaneGeometry,\n\tPolyhedronGeometry: PolyhedronGeometry,\n\tRingGeometry: RingGeometry,\n\tShapeGeometry: ShapeGeometry,\n\tSphereGeometry: SphereGeometry,\n\tTetrahedronGeometry: TetrahedronGeometry,\n\tTorusGeometry: TorusGeometry,\n\tTorusKnotGeometry: TorusKnotGeometry,\n\tTubeGeometry: TubeGeometry,\n\tWireframeGeometry: WireframeGeometry\n});\n\nclass ShadowMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isShadowMaterial = true;\n\n\t\tthis.type = 'ShadowMaterial';\n\n\t\tthis.color = new Color( 0x000000 );\n\t\tthis.transparent = true;\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass RawShaderMaterial extends ShaderMaterial {\n\n\tconstructor( parameters ) {\n\n\t\tsuper( parameters );\n\n\t\tthis.isRawShaderMaterial = true;\n\n\t\tthis.type = 'RawShaderMaterial';\n\n\t}\n\n}\n\nclass MeshStandardMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshStandardMaterial = true;\n\n\t\tthis.defines = { 'STANDARD': '' };\n\n\t\tthis.type = 'MeshStandardMaterial';\n\n\t\tthis.color = new Color( 0xffffff ); // diffuse\n\t\tthis.roughness = 1.0;\n\t\tthis.metalness = 0.0;\n\n\t\tthis.map = null;\n\n\t\tthis.lightMap = null;\n\t\tthis.lightMapIntensity = 1.0;\n\n\t\tthis.aoMap = null;\n\t\tthis.aoMapIntensity = 1.0;\n\n\t\tthis.emissive = new Color( 0x000000 );\n\t\tthis.emissiveIntensity = 1.0;\n\t\tthis.emissiveMap = null;\n\n\t\tthis.bumpMap = null;\n\t\tthis.bumpScale = 1;\n\n\t\tthis.normalMap = null;\n\t\tthis.normalMapType = TangentSpaceNormalMap;\n\t\tthis.normalScale = new Vector2( 1, 1 );\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.roughnessMap = null;\n\n\t\tthis.metalnessMap = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.envMap = null;\n\t\tthis.envMapIntensity = 1.0;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\t\tthis.wireframeLinecap = 'round';\n\t\tthis.wireframeLinejoin = 'round';\n\n\t\tthis.flatShading = false;\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.defines = { 'STANDARD': '' };\n\n\t\tthis.color.copy( source.color );\n\t\tthis.roughness = source.roughness;\n\t\tthis.metalness = source.metalness;\n\n\t\tthis.map = source.map;\n\n\t\tthis.lightMap = source.lightMap;\n\t\tthis.lightMapIntensity = source.lightMapIntensity;\n\n\t\tthis.aoMap = source.aoMap;\n\t\tthis.aoMapIntensity = source.aoMapIntensity;\n\n\t\tthis.emissive.copy( source.emissive );\n\t\tthis.emissiveMap = source.emissiveMap;\n\t\tthis.emissiveIntensity = source.emissiveIntensity;\n\n\t\tthis.bumpMap = source.bumpMap;\n\t\tthis.bumpScale = source.bumpScale;\n\n\t\tthis.normalMap = source.normalMap;\n\t\tthis.normalMapType = source.normalMapType;\n\t\tthis.normalScale.copy( source.normalScale );\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\tthis.roughnessMap = source.roughnessMap;\n\n\t\tthis.metalnessMap = source.metalnessMap;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.envMap = source.envMap;\n\t\tthis.envMapIntensity = source.envMapIntensity;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\t\tthis.wireframeLinecap = source.wireframeLinecap;\n\t\tthis.wireframeLinejoin = source.wireframeLinejoin;\n\n\t\tthis.flatShading = source.flatShading;\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass MeshPhysicalMaterial extends MeshStandardMaterial {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshPhysicalMaterial = true;\n\n\t\tthis.defines = {\n\n\t\t\t'STANDARD': '',\n\t\t\t'PHYSICAL': ''\n\n\t\t};\n\n\t\tthis.type = 'MeshPhysicalMaterial';\n\n\t\tthis.anisotropyRotation = 0;\n\t\tthis.anisotropyMap = null;\n\n\t\tthis.clearcoatMap = null;\n\t\tthis.clearcoatRoughness = 0.0;\n\t\tthis.clearcoatRoughnessMap = null;\n\t\tthis.clearcoatNormalScale = new Vector2( 1, 1 );\n\t\tthis.clearcoatNormalMap = null;\n\n\t\tthis.ior = 1.5;\n\n\t\tObject.defineProperty( this, 'reflectivity', {\n\t\t\tget: function () {\n\n\t\t\t\treturn ( clamp( 2.5 * ( this.ior - 1 ) / ( this.ior + 1 ), 0, 1 ) );\n\n\t\t\t},\n\t\t\tset: function ( reflectivity ) {\n\n\t\t\t\tthis.ior = ( 1 + 0.4 * reflectivity ) / ( 1 - 0.4 * reflectivity );\n\n\t\t\t}\n\t\t} );\n\n\t\tthis.iridescenceMap = null;\n\t\tthis.iridescenceIOR = 1.3;\n\t\tthis.iridescenceThicknessRange = [ 100, 400 ];\n\t\tthis.iridescenceThicknessMap = null;\n\n\t\tthis.sheenColor = new Color( 0x000000 );\n\t\tthis.sheenColorMap = null;\n\t\tthis.sheenRoughness = 1.0;\n\t\tthis.sheenRoughnessMap = null;\n\n\t\tthis.transmissionMap = null;\n\n\t\tthis.thickness = 0;\n\t\tthis.thicknessMap = null;\n\t\tthis.attenuationDistance = Infinity;\n\t\tthis.attenuationColor = new Color( 1, 1, 1 );\n\n\t\tthis.specularIntensity = 1.0;\n\t\tthis.specularIntensityMap = null;\n\t\tthis.specularColor = new Color( 1, 1, 1 );\n\t\tthis.specularColorMap = null;\n\n\t\tthis._anisotropy = 0;\n\t\tthis._clearcoat = 0;\n\t\tthis._iridescence = 0;\n\t\tthis._sheen = 0.0;\n\t\tthis._transmission = 0;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tget anisotropy() {\n\n\t\treturn this._anisotropy;\n\n\t}\n\n\tset anisotropy( value ) {\n\n\t\tif ( this._anisotropy > 0 !== value > 0 ) {\n\n\t\t\tthis.version ++;\n\n\t\t}\n\n\t\tthis._anisotropy = value;\n\n\t}\n\n\tget clearcoat() {\n\n\t\treturn this._clearcoat;\n\n\t}\n\n\tset clearcoat( value ) {\n\n\t\tif ( this._clearcoat > 0 !== value > 0 ) {\n\n\t\t\tthis.version ++;\n\n\t\t}\n\n\t\tthis._clearcoat = value;\n\n\t}\n\n\tget iridescence() {\n\n\t\treturn this._iridescence;\n\n\t}\n\n\tset iridescence( value ) {\n\n\t\tif ( this._iridescence > 0 !== value > 0 ) {\n\n\t\t\tthis.version ++;\n\n\t\t}\n\n\t\tthis._iridescence = value;\n\n\t}\n\n\tget sheen() {\n\n\t\treturn this._sheen;\n\n\t}\n\n\tset sheen( value ) {\n\n\t\tif ( this._sheen > 0 !== value > 0 ) {\n\n\t\t\tthis.version ++;\n\n\t\t}\n\n\t\tthis._sheen = value;\n\n\t}\n\n\tget transmission() {\n\n\t\treturn this._transmission;\n\n\t}\n\n\tset transmission( value ) {\n\n\t\tif ( this._transmission > 0 !== value > 0 ) {\n\n\t\t\tthis.version ++;\n\n\t\t}\n\n\t\tthis._transmission = value;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.defines = {\n\n\t\t\t'STANDARD': '',\n\t\t\t'PHYSICAL': ''\n\n\t\t};\n\n\t\tthis.anisotropy = source.anisotropy;\n\t\tthis.anisotropyRotation = source.anisotropyRotation;\n\t\tthis.anisotropyMap = source.anisotropyMap;\n\n\t\tthis.clearcoat = source.clearcoat;\n\t\tthis.clearcoatMap = source.clearcoatMap;\n\t\tthis.clearcoatRoughness = source.clearcoatRoughness;\n\t\tthis.clearcoatRoughnessMap = source.clearcoatRoughnessMap;\n\t\tthis.clearcoatNormalMap = source.clearcoatNormalMap;\n\t\tthis.clearcoatNormalScale.copy( source.clearcoatNormalScale );\n\n\t\tthis.ior = source.ior;\n\n\t\tthis.iridescence = source.iridescence;\n\t\tthis.iridescenceMap = source.iridescenceMap;\n\t\tthis.iridescenceIOR = source.iridescenceIOR;\n\t\tthis.iridescenceThicknessRange = [ ...source.iridescenceThicknessRange ];\n\t\tthis.iridescenceThicknessMap = source.iridescenceThicknessMap;\n\n\t\tthis.sheen = source.sheen;\n\t\tthis.sheenColor.copy( source.sheenColor );\n\t\tthis.sheenColorMap = source.sheenColorMap;\n\t\tthis.sheenRoughness = source.sheenRoughness;\n\t\tthis.sheenRoughnessMap = source.sheenRoughnessMap;\n\n\t\tthis.transmission = source.transmission;\n\t\tthis.transmissionMap = source.transmissionMap;\n\n\t\tthis.thickness = source.thickness;\n\t\tthis.thicknessMap = source.thicknessMap;\n\t\tthis.attenuationDistance = source.attenuationDistance;\n\t\tthis.attenuationColor.copy( source.attenuationColor );\n\n\t\tthis.specularIntensity = source.specularIntensity;\n\t\tthis.specularIntensityMap = source.specularIntensityMap;\n\t\tthis.specularColor.copy( source.specularColor );\n\t\tthis.specularColorMap = source.specularColorMap;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass MeshPhongMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshPhongMaterial = true;\n\n\t\tthis.type = 'MeshPhongMaterial';\n\n\t\tthis.color = new Color( 0xffffff ); // diffuse\n\t\tthis.specular = new Color( 0x111111 );\n\t\tthis.shininess = 30;\n\n\t\tthis.map = null;\n\n\t\tthis.lightMap = null;\n\t\tthis.lightMapIntensity = 1.0;\n\n\t\tthis.aoMap = null;\n\t\tthis.aoMapIntensity = 1.0;\n\n\t\tthis.emissive = new Color( 0x000000 );\n\t\tthis.emissiveIntensity = 1.0;\n\t\tthis.emissiveMap = null;\n\n\t\tthis.bumpMap = null;\n\t\tthis.bumpScale = 1;\n\n\t\tthis.normalMap = null;\n\t\tthis.normalMapType = TangentSpaceNormalMap;\n\t\tthis.normalScale = new Vector2( 1, 1 );\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.specularMap = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.envMap = null;\n\t\tthis.combine = MultiplyOperation;\n\t\tthis.reflectivity = 1;\n\t\tthis.refractionRatio = 0.98;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\t\tthis.wireframeLinecap = 'round';\n\t\tthis.wireframeLinejoin = 'round';\n\n\t\tthis.flatShading = false;\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.color.copy( source.color );\n\t\tthis.specular.copy( source.specular );\n\t\tthis.shininess = source.shininess;\n\n\t\tthis.map = source.map;\n\n\t\tthis.lightMap = source.lightMap;\n\t\tthis.lightMapIntensity = source.lightMapIntensity;\n\n\t\tthis.aoMap = source.aoMap;\n\t\tthis.aoMapIntensity = source.aoMapIntensity;\n\n\t\tthis.emissive.copy( source.emissive );\n\t\tthis.emissiveMap = source.emissiveMap;\n\t\tthis.emissiveIntensity = source.emissiveIntensity;\n\n\t\tthis.bumpMap = source.bumpMap;\n\t\tthis.bumpScale = source.bumpScale;\n\n\t\tthis.normalMap = source.normalMap;\n\t\tthis.normalMapType = source.normalMapType;\n\t\tthis.normalScale.copy( source.normalScale );\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\tthis.specularMap = source.specularMap;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.envMap = source.envMap;\n\t\tthis.combine = source.combine;\n\t\tthis.reflectivity = source.reflectivity;\n\t\tthis.refractionRatio = source.refractionRatio;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\t\tthis.wireframeLinecap = source.wireframeLinecap;\n\t\tthis.wireframeLinejoin = source.wireframeLinejoin;\n\n\t\tthis.flatShading = source.flatShading;\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass MeshToonMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshToonMaterial = true;\n\n\t\tthis.defines = { 'TOON': '' };\n\n\t\tthis.type = 'MeshToonMaterial';\n\n\t\tthis.color = new Color( 0xffffff );\n\n\t\tthis.map = null;\n\t\tthis.gradientMap = null;\n\n\t\tthis.lightMap = null;\n\t\tthis.lightMapIntensity = 1.0;\n\n\t\tthis.aoMap = null;\n\t\tthis.aoMapIntensity = 1.0;\n\n\t\tthis.emissive = new Color( 0x000000 );\n\t\tthis.emissiveIntensity = 1.0;\n\t\tthis.emissiveMap = null;\n\n\t\tthis.bumpMap = null;\n\t\tthis.bumpScale = 1;\n\n\t\tthis.normalMap = null;\n\t\tthis.normalMapType = TangentSpaceNormalMap;\n\t\tthis.normalScale = new Vector2( 1, 1 );\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\t\tthis.wireframeLinecap = 'round';\n\t\tthis.wireframeLinejoin = 'round';\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.map = source.map;\n\t\tthis.gradientMap = source.gradientMap;\n\n\t\tthis.lightMap = source.lightMap;\n\t\tthis.lightMapIntensity = source.lightMapIntensity;\n\n\t\tthis.aoMap = source.aoMap;\n\t\tthis.aoMapIntensity = source.aoMapIntensity;\n\n\t\tthis.emissive.copy( source.emissive );\n\t\tthis.emissiveMap = source.emissiveMap;\n\t\tthis.emissiveIntensity = source.emissiveIntensity;\n\n\t\tthis.bumpMap = source.bumpMap;\n\t\tthis.bumpScale = source.bumpScale;\n\n\t\tthis.normalMap = source.normalMap;\n\t\tthis.normalMapType = source.normalMapType;\n\t\tthis.normalScale.copy( source.normalScale );\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\t\tthis.wireframeLinecap = source.wireframeLinecap;\n\t\tthis.wireframeLinejoin = source.wireframeLinejoin;\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass MeshNormalMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshNormalMaterial = true;\n\n\t\tthis.type = 'MeshNormalMaterial';\n\n\t\tthis.bumpMap = null;\n\t\tthis.bumpScale = 1;\n\n\t\tthis.normalMap = null;\n\t\tthis.normalMapType = TangentSpaceNormalMap;\n\t\tthis.normalScale = new Vector2( 1, 1 );\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\n\t\tthis.flatShading = false;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.bumpMap = source.bumpMap;\n\t\tthis.bumpScale = source.bumpScale;\n\n\t\tthis.normalMap = source.normalMap;\n\t\tthis.normalMapType = source.normalMapType;\n\t\tthis.normalScale.copy( source.normalScale );\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\n\t\tthis.flatShading = source.flatShading;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass MeshLambertMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshLambertMaterial = true;\n\n\t\tthis.type = 'MeshLambertMaterial';\n\n\t\tthis.color = new Color( 0xffffff ); // diffuse\n\n\t\tthis.map = null;\n\n\t\tthis.lightMap = null;\n\t\tthis.lightMapIntensity = 1.0;\n\n\t\tthis.aoMap = null;\n\t\tthis.aoMapIntensity = 1.0;\n\n\t\tthis.emissive = new Color( 0x000000 );\n\t\tthis.emissiveIntensity = 1.0;\n\t\tthis.emissiveMap = null;\n\n\t\tthis.bumpMap = null;\n\t\tthis.bumpScale = 1;\n\n\t\tthis.normalMap = null;\n\t\tthis.normalMapType = TangentSpaceNormalMap;\n\t\tthis.normalScale = new Vector2( 1, 1 );\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.specularMap = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.envMap = null;\n\t\tthis.combine = MultiplyOperation;\n\t\tthis.reflectivity = 1;\n\t\tthis.refractionRatio = 0.98;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\t\tthis.wireframeLinecap = 'round';\n\t\tthis.wireframeLinejoin = 'round';\n\n\t\tthis.flatShading = false;\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.map = source.map;\n\n\t\tthis.lightMap = source.lightMap;\n\t\tthis.lightMapIntensity = source.lightMapIntensity;\n\n\t\tthis.aoMap = source.aoMap;\n\t\tthis.aoMapIntensity = source.aoMapIntensity;\n\n\t\tthis.emissive.copy( source.emissive );\n\t\tthis.emissiveMap = source.emissiveMap;\n\t\tthis.emissiveIntensity = source.emissiveIntensity;\n\n\t\tthis.bumpMap = source.bumpMap;\n\t\tthis.bumpScale = source.bumpScale;\n\n\t\tthis.normalMap = source.normalMap;\n\t\tthis.normalMapType = source.normalMapType;\n\t\tthis.normalScale.copy( source.normalScale );\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\tthis.specularMap = source.specularMap;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.envMap = source.envMap;\n\t\tthis.combine = source.combine;\n\t\tthis.reflectivity = source.reflectivity;\n\t\tthis.refractionRatio = source.refractionRatio;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\t\tthis.wireframeLinecap = source.wireframeLinecap;\n\t\tthis.wireframeLinejoin = source.wireframeLinejoin;\n\n\t\tthis.flatShading = source.flatShading;\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass MeshMatcapMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshMatcapMaterial = true;\n\n\t\tthis.defines = { 'MATCAP': '' };\n\n\t\tthis.type = 'MeshMatcapMaterial';\n\n\t\tthis.color = new Color( 0xffffff ); // diffuse\n\n\t\tthis.matcap = null;\n\n\t\tthis.map = null;\n\n\t\tthis.bumpMap = null;\n\t\tthis.bumpScale = 1;\n\n\t\tthis.normalMap = null;\n\t\tthis.normalMapType = TangentSpaceNormalMap;\n\t\tthis.normalScale = new Vector2( 1, 1 );\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.flatShading = false;\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.defines = { 'MATCAP': '' };\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.matcap = source.matcap;\n\n\t\tthis.map = source.map;\n\n\t\tthis.bumpMap = source.bumpMap;\n\t\tthis.bumpScale = source.bumpScale;\n\n\t\tthis.normalMap = source.normalMap;\n\t\tthis.normalMapType = source.normalMapType;\n\t\tthis.normalScale.copy( source.normalScale );\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.flatShading = source.flatShading;\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass LineDashedMaterial extends LineBasicMaterial {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isLineDashedMaterial = true;\n\n\t\tthis.type = 'LineDashedMaterial';\n\n\t\tthis.scale = 1;\n\t\tthis.dashSize = 3;\n\t\tthis.gapSize = 1;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.scale = source.scale;\n\t\tthis.dashSize = source.dashSize;\n\t\tthis.gapSize = source.gapSize;\n\n\t\treturn this;\n\n\t}\n\n}\n\n// converts an array to a specific type\nfunction convertArray( array, type, forceClone ) {\n\n\tif ( ! array || // let 'undefined' and 'null' pass\n\t\t! forceClone && array.constructor === type ) return array;\n\n\tif ( typeof type.BYTES_PER_ELEMENT === 'number' ) {\n\n\t\treturn new type( array ); // create typed array\n\n\t}\n\n\treturn Array.prototype.slice.call( array ); // create Array\n\n}\n\nfunction isTypedArray( object ) {\n\n\treturn ArrayBuffer.isView( object ) &&\n\t\t! ( object instanceof DataView );\n\n}\n\n// returns an array by which times and values can be sorted\nfunction getKeyframeOrder( times ) {\n\n\tfunction compareTime( i, j ) {\n\n\t\treturn times[ i ] - times[ j ];\n\n\t}\n\n\tconst n = times.length;\n\tconst result = new Array( n );\n\tfor ( let i = 0; i !== n; ++ i ) result[ i ] = i;\n\n\tresult.sort( compareTime );\n\n\treturn result;\n\n}\n\n// uses the array previously returned by 'getKeyframeOrder' to sort data\nfunction sortedArray( values, stride, order ) {\n\n\tconst nValues = values.length;\n\tconst result = new values.constructor( nValues );\n\n\tfor ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {\n\n\t\tconst srcOffset = order[ i ] * stride;\n\n\t\tfor ( let j = 0; j !== stride; ++ j ) {\n\n\t\t\tresult[ dstOffset ++ ] = values[ srcOffset + j ];\n\n\t\t}\n\n\t}\n\n\treturn result;\n\n}\n\n// function for parsing AOS keyframe formats\nfunction flattenJSON( jsonKeys, times, values, valuePropertyName ) {\n\n\tlet i = 1, key = jsonKeys[ 0 ];\n\n\twhile ( key !== undefined && key[ valuePropertyName ] === undefined ) {\n\n\t\tkey = jsonKeys[ i ++ ];\n\n\t}\n\n\tif ( key === undefined ) return; // no data\n\n\tlet value = key[ valuePropertyName ];\n\tif ( value === undefined ) return; // no data\n\n\tif ( Array.isArray( value ) ) {\n\n\t\tdo {\n\n\t\t\tvalue = key[ valuePropertyName ];\n\n\t\t\tif ( value !== undefined ) {\n\n\t\t\t\ttimes.push( key.time );\n\t\t\t\tvalues.push.apply( values, value ); // push all elements\n\n\t\t\t}\n\n\t\t\tkey = jsonKeys[ i ++ ];\n\n\t\t} while ( key !== undefined );\n\n\t} else if ( value.toArray !== undefined ) {\n\n\t\t// ...assume THREE.Math-ish\n\n\t\tdo {\n\n\t\t\tvalue = key[ valuePropertyName ];\n\n\t\t\tif ( value !== undefined ) {\n\n\t\t\t\ttimes.push( key.time );\n\t\t\t\tvalue.toArray( values, values.length );\n\n\t\t\t}\n\n\t\t\tkey = jsonKeys[ i ++ ];\n\n\t\t} while ( key !== undefined );\n\n\t} else {\n\n\t\t// otherwise push as-is\n\n\t\tdo {\n\n\t\t\tvalue = key[ valuePropertyName ];\n\n\t\t\tif ( value !== undefined ) {\n\n\t\t\t\ttimes.push( key.time );\n\t\t\t\tvalues.push( value );\n\n\t\t\t}\n\n\t\t\tkey = jsonKeys[ i ++ ];\n\n\t\t} while ( key !== undefined );\n\n\t}\n\n}\n\nfunction subclip( sourceClip, name, startFrame, endFrame, fps = 30 ) {\n\n\tconst clip = sourceClip.clone();\n\n\tclip.name = name;\n\n\tconst tracks = [];\n\n\tfor ( let i = 0; i < clip.tracks.length; ++ i ) {\n\n\t\tconst track = clip.tracks[ i ];\n\t\tconst valueSize = track.getValueSize();\n\n\t\tconst times = [];\n\t\tconst values = [];\n\n\t\tfor ( let j = 0; j < track.times.length; ++ j ) {\n\n\t\t\tconst frame = track.times[ j ] * fps;\n\n\t\t\tif ( frame < startFrame || frame >= endFrame ) continue;\n\n\t\t\ttimes.push( track.times[ j ] );\n\n\t\t\tfor ( let k = 0; k < valueSize; ++ k ) {\n\n\t\t\t\tvalues.push( track.values[ j * valueSize + k ] );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( times.length === 0 ) continue;\n\n\t\ttrack.times = convertArray( times, track.times.constructor );\n\t\ttrack.values = convertArray( values, track.values.constructor );\n\n\t\ttracks.push( track );\n\n\t}\n\n\tclip.tracks = tracks;\n\n\t// find minimum .times value across all tracks in the trimmed clip\n\n\tlet minStartTime = Infinity;\n\n\tfor ( let i = 0; i < clip.tracks.length; ++ i ) {\n\n\t\tif ( minStartTime > clip.tracks[ i ].times[ 0 ] ) {\n\n\t\t\tminStartTime = clip.tracks[ i ].times[ 0 ];\n\n\t\t}\n\n\t}\n\n\t// shift all tracks such that clip begins at t=0\n\n\tfor ( let i = 0; i < clip.tracks.length; ++ i ) {\n\n\t\tclip.tracks[ i ].shift( - 1 * minStartTime );\n\n\t}\n\n\tclip.resetDuration();\n\n\treturn clip;\n\n}\n\nfunction makeClipAdditive( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) {\n\n\tif ( fps <= 0 ) fps = 30;\n\n\tconst numTracks = referenceClip.tracks.length;\n\tconst referenceTime = referenceFrame / fps;\n\n\t// Make each track's values relative to the values at the reference frame\n\tfor ( let i = 0; i < numTracks; ++ i ) {\n\n\t\tconst referenceTrack = referenceClip.tracks[ i ];\n\t\tconst referenceTrackType = referenceTrack.ValueTypeName;\n\n\t\t// Skip this track if it's non-numeric\n\t\tif ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue;\n\n\t\t// Find the track in the target clip whose name and type matches the reference track\n\t\tconst targetTrack = targetClip.tracks.find( function ( track ) {\n\n\t\t\treturn track.name === referenceTrack.name\n\t\t\t\t&& track.ValueTypeName === referenceTrackType;\n\n\t\t} );\n\n\t\tif ( targetTrack === undefined ) continue;\n\n\t\tlet referenceOffset = 0;\n\t\tconst referenceValueSize = referenceTrack.getValueSize();\n\n\t\tif ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) {\n\n\t\t\treferenceOffset = referenceValueSize / 3;\n\n\t\t}\n\n\t\tlet targetOffset = 0;\n\t\tconst targetValueSize = targetTrack.getValueSize();\n\n\t\tif ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) {\n\n\t\t\ttargetOffset = targetValueSize / 3;\n\n\t\t}\n\n\t\tconst lastIndex = referenceTrack.times.length - 1;\n\t\tlet referenceValue;\n\n\t\t// Find the value to subtract out of the track\n\t\tif ( referenceTime <= referenceTrack.times[ 0 ] ) {\n\n\t\t\t// Reference frame is earlier than the first keyframe, so just use the first keyframe\n\t\t\tconst startIndex = referenceOffset;\n\t\t\tconst endIndex = referenceValueSize - referenceOffset;\n\t\t\treferenceValue = referenceTrack.values.slice( startIndex, endIndex );\n\n\t\t} else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) {\n\n\t\t\t// Reference frame is after the last keyframe, so just use the last keyframe\n\t\t\tconst startIndex = lastIndex * referenceValueSize + referenceOffset;\n\t\t\tconst endIndex = startIndex + referenceValueSize - referenceOffset;\n\t\t\treferenceValue = referenceTrack.values.slice( startIndex, endIndex );\n\n\t\t} else {\n\n\t\t\t// Interpolate to the reference value\n\t\t\tconst interpolant = referenceTrack.createInterpolant();\n\t\t\tconst startIndex = referenceOffset;\n\t\t\tconst endIndex = referenceValueSize - referenceOffset;\n\t\t\tinterpolant.evaluate( referenceTime );\n\t\t\treferenceValue = interpolant.resultBuffer.slice( startIndex, endIndex );\n\n\t\t}\n\n\t\t// Conjugate the quaternion\n\t\tif ( referenceTrackType === 'quaternion' ) {\n\n\t\t\tconst referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate();\n\t\t\treferenceQuat.toArray( referenceValue );\n\n\t\t}\n\n\t\t// Subtract the reference value from all of the track values\n\n\t\tconst numTimes = targetTrack.times.length;\n\t\tfor ( let j = 0; j < numTimes; ++ j ) {\n\n\t\t\tconst valueStart = j * targetValueSize + targetOffset;\n\n\t\t\tif ( referenceTrackType === 'quaternion' ) {\n\n\t\t\t\t// Multiply the conjugate for quaternion track types\n\t\t\t\tQuaternion.multiplyQuaternionsFlat(\n\t\t\t\t\ttargetTrack.values,\n\t\t\t\t\tvalueStart,\n\t\t\t\t\treferenceValue,\n\t\t\t\t\t0,\n\t\t\t\t\ttargetTrack.values,\n\t\t\t\t\tvalueStart\n\t\t\t\t);\n\n\t\t\t} else {\n\n\t\t\t\tconst valueEnd = targetValueSize - targetOffset * 2;\n\n\t\t\t\t// Subtract each value for all other numeric track types\n\t\t\t\tfor ( let k = 0; k < valueEnd; ++ k ) {\n\n\t\t\t\t\ttargetTrack.values[ valueStart + k ] -= referenceValue[ k ];\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\ttargetClip.blendMode = AdditiveAnimationBlendMode;\n\n\treturn targetClip;\n\n}\n\nconst AnimationUtils = {\n\tconvertArray: convertArray,\n\tisTypedArray: isTypedArray,\n\tgetKeyframeOrder: getKeyframeOrder,\n\tsortedArray: sortedArray,\n\tflattenJSON: flattenJSON,\n\tsubclip: subclip,\n\tmakeClipAdditive: makeClipAdditive\n};\n\n/**\n * Abstract base class of interpolants over parametric samples.\n *\n * The parameter domain is one dimensional, typically the time or a path\n * along a curve defined by the data.\n *\n * The sample values can have any dimensionality and derived classes may\n * apply special interpretations to the data.\n *\n * This class provides the interval seek in a Template Method, deferring\n * the actual interpolation to derived classes.\n *\n * Time complexity is O(1) for linear access crossing at most two points\n * and O(log N) for random access, where N is the number of positions.\n *\n * References:\n *\n * \t\thttp://www.oodesign.com/template-method-pattern.html\n *\n */\n\nclass Interpolant {\n\n\tconstructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n\t\tthis.parameterPositions = parameterPositions;\n\t\tthis._cachedIndex = 0;\n\n\t\tthis.resultBuffer = resultBuffer !== undefined ?\n\t\t\tresultBuffer : new sampleValues.constructor( sampleSize );\n\t\tthis.sampleValues = sampleValues;\n\t\tthis.valueSize = sampleSize;\n\n\t\tthis.settings = null;\n\t\tthis.DefaultSettings_ = {};\n\n\t}\n\n\tevaluate( t ) {\n\n\t\tconst pp = this.parameterPositions;\n\t\tlet i1 = this._cachedIndex,\n\t\t\tt1 = pp[ i1 ],\n\t\t\tt0 = pp[ i1 - 1 ];\n\n\t\tvalidate_interval: {\n\n\t\t\tseek: {\n\n\t\t\t\tlet right;\n\n\t\t\t\tlinear_scan: {\n\n\t\t\t\t\t//- See http://jsperf.com/comparison-to-undefined/3\n\t\t\t\t\t//- slower code:\n\t\t\t\t\t//-\n\t\t\t\t\t//- \t\t\t\tif ( t >= t1 || t1 === undefined ) {\n\t\t\t\t\tforward_scan: if ( ! ( t < t1 ) ) {\n\n\t\t\t\t\t\tfor ( let giveUpAt = i1 + 2; ; ) {\n\n\t\t\t\t\t\t\tif ( t1 === undefined ) {\n\n\t\t\t\t\t\t\t\tif ( t < t0 ) break forward_scan;\n\n\t\t\t\t\t\t\t\t// after end\n\n\t\t\t\t\t\t\t\ti1 = pp.length;\n\t\t\t\t\t\t\t\tthis._cachedIndex = i1;\n\t\t\t\t\t\t\t\treturn this.copySampleValue_( i1 - 1 );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ( i1 === giveUpAt ) break; // this loop\n\n\t\t\t\t\t\t\tt0 = t1;\n\t\t\t\t\t\t\tt1 = pp[ ++ i1 ];\n\n\t\t\t\t\t\t\tif ( t < t1 ) {\n\n\t\t\t\t\t\t\t\t// we have arrived at the sought interval\n\t\t\t\t\t\t\t\tbreak seek;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// prepare binary search on the right side of the index\n\t\t\t\t\t\tright = pp.length;\n\t\t\t\t\t\tbreak linear_scan;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t//- slower code:\n\t\t\t\t\t//-\t\t\t\t\tif ( t < t0 || t0 === undefined ) {\n\t\t\t\t\tif ( ! ( t >= t0 ) ) {\n\n\t\t\t\t\t\t// looping?\n\n\t\t\t\t\t\tconst t1global = pp[ 1 ];\n\n\t\t\t\t\t\tif ( t < t1global ) {\n\n\t\t\t\t\t\t\ti1 = 2; // + 1, using the scan for the details\n\t\t\t\t\t\t\tt0 = t1global;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// linear reverse scan\n\n\t\t\t\t\t\tfor ( let giveUpAt = i1 - 2; ; ) {\n\n\t\t\t\t\t\t\tif ( t0 === undefined ) {\n\n\t\t\t\t\t\t\t\t// before start\n\n\t\t\t\t\t\t\t\tthis._cachedIndex = 0;\n\t\t\t\t\t\t\t\treturn this.copySampleValue_( 0 );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ( i1 === giveUpAt ) break; // this loop\n\n\t\t\t\t\t\t\tt1 = t0;\n\t\t\t\t\t\t\tt0 = pp[ -- i1 - 1 ];\n\n\t\t\t\t\t\t\tif ( t >= t0 ) {\n\n\t\t\t\t\t\t\t\t// we have arrived at the sought interval\n\t\t\t\t\t\t\t\tbreak seek;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// prepare binary search on the left side of the index\n\t\t\t\t\t\tright = i1;\n\t\t\t\t\t\ti1 = 0;\n\t\t\t\t\t\tbreak linear_scan;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// the interval is valid\n\n\t\t\t\t\tbreak validate_interval;\n\n\t\t\t\t} // linear scan\n\n\t\t\t\t// binary search\n\n\t\t\t\twhile ( i1 < right ) {\n\n\t\t\t\t\tconst mid = ( i1 + right ) >>> 1;\n\n\t\t\t\t\tif ( t < pp[ mid ] ) {\n\n\t\t\t\t\t\tright = mid;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\ti1 = mid + 1;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tt1 = pp[ i1 ];\n\t\t\t\tt0 = pp[ i1 - 1 ];\n\n\t\t\t\t// check boundary cases, again\n\n\t\t\t\tif ( t0 === undefined ) {\n\n\t\t\t\t\tthis._cachedIndex = 0;\n\t\t\t\t\treturn this.copySampleValue_( 0 );\n\n\t\t\t\t}\n\n\t\t\t\tif ( t1 === undefined ) {\n\n\t\t\t\t\ti1 = pp.length;\n\t\t\t\t\tthis._cachedIndex = i1;\n\t\t\t\t\treturn this.copySampleValue_( i1 - 1 );\n\n\t\t\t\t}\n\n\t\t\t} // seek\n\n\t\t\tthis._cachedIndex = i1;\n\n\t\t\tthis.intervalChanged_( i1, t0, t1 );\n\n\t\t} // validate_interval\n\n\t\treturn this.interpolate_( i1, t0, t, t1 );\n\n\t}\n\n\tgetSettings_() {\n\n\t\treturn this.settings || this.DefaultSettings_;\n\n\t}\n\n\tcopySampleValue_( index ) {\n\n\t\t// copies a sample value to the result buffer\n\n\t\tconst result = this.resultBuffer,\n\t\t\tvalues = this.sampleValues,\n\t\t\tstride = this.valueSize,\n\t\t\toffset = index * stride;\n\n\t\tfor ( let i = 0; i !== stride; ++ i ) {\n\n\t\t\tresult[ i ] = values[ offset + i ];\n\n\t\t}\n\n\t\treturn result;\n\n\t}\n\n\t// Template methods for derived classes:\n\n\tinterpolate_( /* i1, t0, t, t1 */ ) {\n\n\t\tthrow new Error( 'call to abstract method' );\n\t\t// implementations shall return this.resultBuffer\n\n\t}\n\n\tintervalChanged_( /* i1, t0, t1 */ ) {\n\n\t\t// empty\n\n\t}\n\n}\n\n/**\n * Fast and simple cubic spline interpolant.\n *\n * It was derived from a Hermitian construction setting the first derivative\n * at each sample position to the linear slope between neighboring positions\n * over their parameter interval.\n */\n\nclass CubicInterpolant extends Interpolant {\n\n\tconstructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n\t\tsuper( parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n\t\tthis._weightPrev = - 0;\n\t\tthis._offsetPrev = - 0;\n\t\tthis._weightNext = - 0;\n\t\tthis._offsetNext = - 0;\n\n\t\tthis.DefaultSettings_ = {\n\n\t\t\tendingStart: ZeroCurvatureEnding,\n\t\t\tendingEnd: ZeroCurvatureEnding\n\n\t\t};\n\n\t}\n\n\tintervalChanged_( i1, t0, t1 ) {\n\n\t\tconst pp = this.parameterPositions;\n\t\tlet iPrev = i1 - 2,\n\t\t\tiNext = i1 + 1,\n\n\t\t\ttPrev = pp[ iPrev ],\n\t\t\ttNext = pp[ iNext ];\n\n\t\tif ( tPrev === undefined ) {\n\n\t\t\tswitch ( this.getSettings_().endingStart ) {\n\n\t\t\t\tcase ZeroSlopeEnding:\n\n\t\t\t\t\t// f'(t0) = 0\n\t\t\t\t\tiPrev = i1;\n\t\t\t\t\ttPrev = 2 * t0 - t1;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase WrapAroundEnding:\n\n\t\t\t\t\t// use the other end of the curve\n\t\t\t\t\tiPrev = pp.length - 2;\n\t\t\t\t\ttPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ];\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault: // ZeroCurvatureEnding\n\n\t\t\t\t\t// f''(t0) = 0 a.k.a. Natural Spline\n\t\t\t\t\tiPrev = i1;\n\t\t\t\t\ttPrev = t1;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( tNext === undefined ) {\n\n\t\t\tswitch ( this.getSettings_().endingEnd ) {\n\n\t\t\t\tcase ZeroSlopeEnding:\n\n\t\t\t\t\t// f'(tN) = 0\n\t\t\t\t\tiNext = i1;\n\t\t\t\t\ttNext = 2 * t1 - t0;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase WrapAroundEnding:\n\n\t\t\t\t\t// use the other end of the curve\n\t\t\t\t\tiNext = 1;\n\t\t\t\t\ttNext = t1 + pp[ 1 ] - pp[ 0 ];\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault: // ZeroCurvatureEnding\n\n\t\t\t\t\t// f''(tN) = 0, a.k.a. Natural Spline\n\t\t\t\t\tiNext = i1 - 1;\n\t\t\t\t\ttNext = t0;\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst halfDt = ( t1 - t0 ) * 0.5,\n\t\t\tstride = this.valueSize;\n\n\t\tthis._weightPrev = halfDt / ( t0 - tPrev );\n\t\tthis._weightNext = halfDt / ( tNext - t1 );\n\t\tthis._offsetPrev = iPrev * stride;\n\t\tthis._offsetNext = iNext * stride;\n\n\t}\n\n\tinterpolate_( i1, t0, t, t1 ) {\n\n\t\tconst result = this.resultBuffer,\n\t\t\tvalues = this.sampleValues,\n\t\t\tstride = this.valueSize,\n\n\t\t\to1 = i1 * stride,\t\to0 = o1 - stride,\n\t\t\toP = this._offsetPrev, \toN = this._offsetNext,\n\t\t\twP = this._weightPrev,\twN = this._weightNext,\n\n\t\t\tp = ( t - t0 ) / ( t1 - t0 ),\n\t\t\tpp = p * p,\n\t\t\tppp = pp * p;\n\n\t\t// evaluate polynomials\n\n\t\tconst sP = - wP * ppp + 2 * wP * pp - wP * p;\n\t\tconst s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1;\n\t\tconst s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p;\n\t\tconst sN = wN * ppp - wN * pp;\n\n\t\t// combine data linearly\n\n\t\tfor ( let i = 0; i !== stride; ++ i ) {\n\n\t\t\tresult[ i ] =\n\t\t\t\t\tsP * values[ oP + i ] +\n\t\t\t\t\ts0 * values[ o0 + i ] +\n\t\t\t\t\ts1 * values[ o1 + i ] +\n\t\t\t\t\tsN * values[ oN + i ];\n\n\t\t}\n\n\t\treturn result;\n\n\t}\n\n}\n\nclass LinearInterpolant extends Interpolant {\n\n\tconstructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n\t\tsuper( parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n\t}\n\n\tinterpolate_( i1, t0, t, t1 ) {\n\n\t\tconst result = this.resultBuffer,\n\t\t\tvalues = this.sampleValues,\n\t\t\tstride = this.valueSize,\n\n\t\t\toffset1 = i1 * stride,\n\t\t\toffset0 = offset1 - stride,\n\n\t\t\tweight1 = ( t - t0 ) / ( t1 - t0 ),\n\t\t\tweight0 = 1 - weight1;\n\n\t\tfor ( let i = 0; i !== stride; ++ i ) {\n\n\t\t\tresult[ i ] =\n\t\t\t\t\tvalues[ offset0 + i ] * weight0 +\n\t\t\t\t\tvalues[ offset1 + i ] * weight1;\n\n\t\t}\n\n\t\treturn result;\n\n\t}\n\n}\n\n/**\n *\n * Interpolant that evaluates to the sample value at the position preceding\n * the parameter.\n */\n\nclass DiscreteInterpolant extends Interpolant {\n\n\tconstructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n\t\tsuper( parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n\t}\n\n\tinterpolate_( i1 /*, t0, t, t1 */ ) {\n\n\t\treturn this.copySampleValue_( i1 - 1 );\n\n\t}\n\n}\n\nclass KeyframeTrack {\n\n\tconstructor( name, times, values, interpolation ) {\n\n\t\tif ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' );\n\t\tif ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name );\n\n\t\tthis.name = name;\n\n\t\tthis.times = convertArray( times, this.TimeBufferType );\n\t\tthis.values = convertArray( values, this.ValueBufferType );\n\n\t\tthis.setInterpolation( interpolation || this.DefaultInterpolation );\n\n\t}\n\n\t// Serialization (in static context, because of constructor invocation\n\t// and automatic invocation of .toJSON):\n\n\tstatic toJSON( track ) {\n\n\t\tconst trackType = track.constructor;\n\n\t\tlet json;\n\n\t\t// derived classes can define a static toJSON method\n\t\tif ( trackType.toJSON !== this.toJSON ) {\n\n\t\t\tjson = trackType.toJSON( track );\n\n\t\t} else {\n\n\t\t\t// by default, we assume the data can be serialized as-is\n\t\t\tjson = {\n\n\t\t\t\t'name': track.name,\n\t\t\t\t'times': convertArray( track.times, Array ),\n\t\t\t\t'values': convertArray( track.values, Array )\n\n\t\t\t};\n\n\t\t\tconst interpolation = track.getInterpolation();\n\n\t\t\tif ( interpolation !== track.DefaultInterpolation ) {\n\n\t\t\t\tjson.interpolation = interpolation;\n\n\t\t\t}\n\n\t\t}\n\n\t\tjson.type = track.ValueTypeName; // mandatory\n\n\t\treturn json;\n\n\t}\n\n\tInterpolantFactoryMethodDiscrete( result ) {\n\n\t\treturn new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );\n\n\t}\n\n\tInterpolantFactoryMethodLinear( result ) {\n\n\t\treturn new LinearInterpolant( this.times, this.values, this.getValueSize(), result );\n\n\t}\n\n\tInterpolantFactoryMethodSmooth( result ) {\n\n\t\treturn new CubicInterpolant( this.times, this.values, this.getValueSize(), result );\n\n\t}\n\n\tsetInterpolation( interpolation ) {\n\n\t\tlet factoryMethod;\n\n\t\tswitch ( interpolation ) {\n\n\t\t\tcase InterpolateDiscrete:\n\n\t\t\t\tfactoryMethod = this.InterpolantFactoryMethodDiscrete;\n\n\t\t\t\tbreak;\n\n\t\t\tcase InterpolateLinear:\n\n\t\t\t\tfactoryMethod = this.InterpolantFactoryMethodLinear;\n\n\t\t\t\tbreak;\n\n\t\t\tcase InterpolateSmooth:\n\n\t\t\t\tfactoryMethod = this.InterpolantFactoryMethodSmooth;\n\n\t\t\t\tbreak;\n\n\t\t}\n\n\t\tif ( factoryMethod === undefined ) {\n\n\t\t\tconst message = 'unsupported interpolation for ' +\n\t\t\t\tthis.ValueTypeName + ' keyframe track named ' + this.name;\n\n\t\t\tif ( this.createInterpolant === undefined ) {\n\n\t\t\t\t// fall back to default, unless the default itself is messed up\n\t\t\t\tif ( interpolation !== this.DefaultInterpolation ) {\n\n\t\t\t\t\tthis.setInterpolation( this.DefaultInterpolation );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthrow new Error( message ); // fatal, in this case\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tconsole.warn( 'THREE.KeyframeTrack:', message );\n\t\t\treturn this;\n\n\t\t}\n\n\t\tthis.createInterpolant = factoryMethod;\n\n\t\treturn this;\n\n\t}\n\n\tgetInterpolation() {\n\n\t\tswitch ( this.createInterpolant ) {\n\n\t\t\tcase this.InterpolantFactoryMethodDiscrete:\n\n\t\t\t\treturn InterpolateDiscrete;\n\n\t\t\tcase this.InterpolantFactoryMethodLinear:\n\n\t\t\t\treturn InterpolateLinear;\n\n\t\t\tcase this.InterpolantFactoryMethodSmooth:\n\n\t\t\t\treturn InterpolateSmooth;\n\n\t\t}\n\n\t}\n\n\tgetValueSize() {\n\n\t\treturn this.values.length / this.times.length;\n\n\t}\n\n\t// move all keyframes either forwards or backwards in time\n\tshift( timeOffset ) {\n\n\t\tif ( timeOffset !== 0.0 ) {\n\n\t\t\tconst times = this.times;\n\n\t\t\tfor ( let i = 0, n = times.length; i !== n; ++ i ) {\n\n\t\t\t\ttimes[ i ] += timeOffset;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\t// scale all keyframe times by a factor (useful for frame <-> seconds conversions)\n\tscale( timeScale ) {\n\n\t\tif ( timeScale !== 1.0 ) {\n\n\t\t\tconst times = this.times;\n\n\t\t\tfor ( let i = 0, n = times.length; i !== n; ++ i ) {\n\n\t\t\t\ttimes[ i ] *= timeScale;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\t// removes keyframes before and after animation without changing any values within the range [startTime, endTime].\n\t// IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values\n\ttrim( startTime, endTime ) {\n\n\t\tconst times = this.times,\n\t\t\tnKeys = times.length;\n\n\t\tlet from = 0,\n\t\t\tto = nKeys - 1;\n\n\t\twhile ( from !== nKeys && times[ from ] < startTime ) {\n\n\t\t\t++ from;\n\n\t\t}\n\n\t\twhile ( to !== - 1 && times[ to ] > endTime ) {\n\n\t\t\t-- to;\n\n\t\t}\n\n\t\t++ to; // inclusive -> exclusive bound\n\n\t\tif ( from !== 0 || to !== nKeys ) {\n\n\t\t\t// empty tracks are forbidden, so keep at least one keyframe\n\t\t\tif ( from >= to ) {\n\n\t\t\t\tto = Math.max( to, 1 );\n\t\t\t\tfrom = to - 1;\n\n\t\t\t}\n\n\t\t\tconst stride = this.getValueSize();\n\t\t\tthis.times = times.slice( from, to );\n\t\t\tthis.values = this.values.slice( from * stride, to * stride );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\t// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable\n\tvalidate() {\n\n\t\tlet valid = true;\n\n\t\tconst valueSize = this.getValueSize();\n\t\tif ( valueSize - Math.floor( valueSize ) !== 0 ) {\n\n\t\t\tconsole.error( 'THREE.KeyframeTrack: Invalid value size in track.', this );\n\t\t\tvalid = false;\n\n\t\t}\n\n\t\tconst times = this.times,\n\t\t\tvalues = this.values,\n\n\t\t\tnKeys = times.length;\n\n\t\tif ( nKeys === 0 ) {\n\n\t\t\tconsole.error( 'THREE.KeyframeTrack: Track is empty.', this );\n\t\t\tvalid = false;\n\n\t\t}\n\n\t\tlet prevTime = null;\n\n\t\tfor ( let i = 0; i !== nKeys; i ++ ) {\n\n\t\t\tconst currTime = times[ i ];\n\n\t\t\tif ( typeof currTime === 'number' && isNaN( currTime ) ) {\n\n\t\t\t\tconsole.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime );\n\t\t\t\tvalid = false;\n\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t\tif ( prevTime !== null && prevTime > currTime ) {\n\n\t\t\t\tconsole.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime );\n\t\t\t\tvalid = false;\n\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t\tprevTime = currTime;\n\n\t\t}\n\n\t\tif ( values !== undefined ) {\n\n\t\t\tif ( isTypedArray( values ) ) {\n\n\t\t\t\tfor ( let i = 0, n = values.length; i !== n; ++ i ) {\n\n\t\t\t\t\tconst value = values[ i ];\n\n\t\t\t\t\tif ( isNaN( value ) ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value );\n\t\t\t\t\t\tvalid = false;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn valid;\n\n\t}\n\n\t// removes equivalent sequential keys as common in morph target sequences\n\t// (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)\n\toptimize() {\n\n\t\t// times or values may be shared with other tracks, so overwriting is unsafe\n\t\tconst times = this.times.slice(),\n\t\t\tvalues = this.values.slice(),\n\t\t\tstride = this.getValueSize(),\n\n\t\t\tsmoothInterpolation = this.getInterpolation() === InterpolateSmooth,\n\n\t\t\tlastIndex = times.length - 1;\n\n\t\tlet writeIndex = 1;\n\n\t\tfor ( let i = 1; i < lastIndex; ++ i ) {\n\n\t\t\tlet keep = false;\n\n\t\t\tconst time = times[ i ];\n\t\t\tconst timeNext = times[ i + 1 ];\n\n\t\t\t// remove adjacent keyframes scheduled at the same time\n\n\t\t\tif ( time !== timeNext && ( i !== 1 || time !== times[ 0 ] ) ) {\n\n\t\t\t\tif ( ! smoothInterpolation ) {\n\n\t\t\t\t\t// remove unnecessary keyframes same as their neighbors\n\n\t\t\t\t\tconst offset = i * stride,\n\t\t\t\t\t\toffsetP = offset - stride,\n\t\t\t\t\t\toffsetN = offset + stride;\n\n\t\t\t\t\tfor ( let j = 0; j !== stride; ++ j ) {\n\n\t\t\t\t\t\tconst value = values[ offset + j ];\n\n\t\t\t\t\t\tif ( value !== values[ offsetP + j ] ||\n\t\t\t\t\t\t\tvalue !== values[ offsetN + j ] ) {\n\n\t\t\t\t\t\t\tkeep = true;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tkeep = true;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// in-place compaction\n\n\t\t\tif ( keep ) {\n\n\t\t\t\tif ( i !== writeIndex ) {\n\n\t\t\t\t\ttimes[ writeIndex ] = times[ i ];\n\n\t\t\t\t\tconst readOffset = i * stride,\n\t\t\t\t\t\twriteOffset = writeIndex * stride;\n\n\t\t\t\t\tfor ( let j = 0; j !== stride; ++ j ) {\n\n\t\t\t\t\t\tvalues[ writeOffset + j ] = values[ readOffset + j ];\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t++ writeIndex;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// flush last keyframe (compaction looks ahead)\n\n\t\tif ( lastIndex > 0 ) {\n\n\t\t\ttimes[ writeIndex ] = times[ lastIndex ];\n\n\t\t\tfor ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) {\n\n\t\t\t\tvalues[ writeOffset + j ] = values[ readOffset + j ];\n\n\t\t\t}\n\n\t\t\t++ writeIndex;\n\n\t\t}\n\n\t\tif ( writeIndex !== times.length ) {\n\n\t\t\tthis.times = times.slice( 0, writeIndex );\n\t\t\tthis.values = values.slice( 0, writeIndex * stride );\n\n\t\t} else {\n\n\t\t\tthis.times = times;\n\t\t\tthis.values = values;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\tconst times = this.times.slice();\n\t\tconst values = this.values.slice();\n\n\t\tconst TypedKeyframeTrack = this.constructor;\n\t\tconst track = new TypedKeyframeTrack( this.name, times, values );\n\n\t\t// Interpolant argument to constructor is not saved, so copy the factory method directly.\n\t\ttrack.createInterpolant = this.createInterpolant;\n\n\t\treturn track;\n\n\t}\n\n}\n\nKeyframeTrack.prototype.TimeBufferType = Float32Array;\nKeyframeTrack.prototype.ValueBufferType = Float32Array;\nKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear;\n\n/**\n * A Track of Boolean keyframe values.\n */\nclass BooleanKeyframeTrack extends KeyframeTrack {}\n\nBooleanKeyframeTrack.prototype.ValueTypeName = 'bool';\nBooleanKeyframeTrack.prototype.ValueBufferType = Array;\nBooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete;\nBooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined;\nBooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined;\n\n/**\n * A Track of keyframe values that represent color.\n */\nclass ColorKeyframeTrack extends KeyframeTrack {}\n\nColorKeyframeTrack.prototype.ValueTypeName = 'color';\n\n/**\n * A Track of numeric keyframe values.\n */\nclass NumberKeyframeTrack extends KeyframeTrack {}\n\nNumberKeyframeTrack.prototype.ValueTypeName = 'number';\n\n/**\n * Spherical linear unit quaternion interpolant.\n */\n\nclass QuaternionLinearInterpolant extends Interpolant {\n\n\tconstructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n\t\tsuper( parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n\t}\n\n\tinterpolate_( i1, t0, t, t1 ) {\n\n\t\tconst result = this.resultBuffer,\n\t\t\tvalues = this.sampleValues,\n\t\t\tstride = this.valueSize,\n\n\t\t\talpha = ( t - t0 ) / ( t1 - t0 );\n\n\t\tlet offset = i1 * stride;\n\n\t\tfor ( let end = offset + stride; offset !== end; offset += 4 ) {\n\n\t\t\tQuaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha );\n\n\t\t}\n\n\t\treturn result;\n\n\t}\n\n}\n\n/**\n * A Track of quaternion keyframe values.\n */\nclass QuaternionKeyframeTrack extends KeyframeTrack {\n\n\tInterpolantFactoryMethodLinear( result ) {\n\n\t\treturn new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result );\n\n\t}\n\n}\n\nQuaternionKeyframeTrack.prototype.ValueTypeName = 'quaternion';\n// ValueBufferType is inherited\nQuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear;\nQuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined;\n\n/**\n * A Track that interpolates Strings\n */\nclass StringKeyframeTrack extends KeyframeTrack {}\n\nStringKeyframeTrack.prototype.ValueTypeName = 'string';\nStringKeyframeTrack.prototype.ValueBufferType = Array;\nStringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete;\nStringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined;\nStringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined;\n\n/**\n * A Track of vectored keyframe values.\n */\nclass VectorKeyframeTrack extends KeyframeTrack {}\n\nVectorKeyframeTrack.prototype.ValueTypeName = 'vector';\n\nclass AnimationClip {\n\n\tconstructor( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) {\n\n\t\tthis.name = name;\n\t\tthis.tracks = tracks;\n\t\tthis.duration = duration;\n\t\tthis.blendMode = blendMode;\n\n\t\tthis.uuid = generateUUID();\n\n\t\t// this means it should figure out its duration by scanning the tracks\n\t\tif ( this.duration < 0 ) {\n\n\t\t\tthis.resetDuration();\n\n\t\t}\n\n\t}\n\n\n\tstatic parse( json ) {\n\n\t\tconst tracks = [],\n\t\t\tjsonTracks = json.tracks,\n\t\t\tframeTime = 1.0 / ( json.fps || 1.0 );\n\n\t\tfor ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) {\n\n\t\t\ttracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) );\n\n\t\t}\n\n\t\tconst clip = new this( json.name, json.duration, tracks, json.blendMode );\n\t\tclip.uuid = json.uuid;\n\n\t\treturn clip;\n\n\t}\n\n\tstatic toJSON( clip ) {\n\n\t\tconst tracks = [],\n\t\t\tclipTracks = clip.tracks;\n\n\t\tconst json = {\n\n\t\t\t'name': clip.name,\n\t\t\t'duration': clip.duration,\n\t\t\t'tracks': tracks,\n\t\t\t'uuid': clip.uuid,\n\t\t\t'blendMode': clip.blendMode\n\n\t\t};\n\n\t\tfor ( let i = 0, n = clipTracks.length; i !== n; ++ i ) {\n\n\t\t\ttracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) );\n\n\t\t}\n\n\t\treturn json;\n\n\t}\n\n\tstatic CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) {\n\n\t\tconst numMorphTargets = morphTargetSequence.length;\n\t\tconst tracks = [];\n\n\t\tfor ( let i = 0; i < numMorphTargets; i ++ ) {\n\n\t\t\tlet times = [];\n\t\t\tlet values = [];\n\n\t\t\ttimes.push(\n\t\t\t\t( i + numMorphTargets - 1 ) % numMorphTargets,\n\t\t\t\ti,\n\t\t\t\t( i + 1 ) % numMorphTargets );\n\n\t\t\tvalues.push( 0, 1, 0 );\n\n\t\t\tconst order = getKeyframeOrder( times );\n\t\t\ttimes = sortedArray( times, 1, order );\n\t\t\tvalues = sortedArray( values, 1, order );\n\n\t\t\t// if there is a key at the first frame, duplicate it as the\n\t\t\t// last frame as well for perfect loop.\n\t\t\tif ( ! noLoop && times[ 0 ] === 0 ) {\n\n\t\t\t\ttimes.push( numMorphTargets );\n\t\t\t\tvalues.push( values[ 0 ] );\n\n\t\t\t}\n\n\t\t\ttracks.push(\n\t\t\t\tnew NumberKeyframeTrack(\n\t\t\t\t\t'.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']',\n\t\t\t\t\ttimes, values\n\t\t\t\t).scale( 1.0 / fps ) );\n\n\t\t}\n\n\t\treturn new this( name, - 1, tracks );\n\n\t}\n\n\tstatic findByName( objectOrClipArray, name ) {\n\n\t\tlet clipArray = objectOrClipArray;\n\n\t\tif ( ! Array.isArray( objectOrClipArray ) ) {\n\n\t\t\tconst o = objectOrClipArray;\n\t\t\tclipArray = o.geometry && o.geometry.animations || o.animations;\n\n\t\t}\n\n\t\tfor ( let i = 0; i < clipArray.length; i ++ ) {\n\n\t\t\tif ( clipArray[ i ].name === name ) {\n\n\t\t\t\treturn clipArray[ i ];\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn null;\n\n\t}\n\n\tstatic CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) {\n\n\t\tconst animationToMorphTargets = {};\n\n\t\t// tested with https://regex101.com/ on trick sequences\n\t\t// such flamingo_flyA_003, flamingo_run1_003, crdeath0059\n\t\tconst pattern = /^([\\w-]*?)([\\d]+)$/;\n\n\t\t// sort morph target names into animation groups based\n\t\t// patterns like Walk_001, Walk_002, Run_001, Run_002\n\t\tfor ( let i = 0, il = morphTargets.length; i < il; i ++ ) {\n\n\t\t\tconst morphTarget = morphTargets[ i ];\n\t\t\tconst parts = morphTarget.name.match( pattern );\n\n\t\t\tif ( parts && parts.length > 1 ) {\n\n\t\t\t\tconst name = parts[ 1 ];\n\n\t\t\t\tlet animationMorphTargets = animationToMorphTargets[ name ];\n\n\t\t\t\tif ( ! animationMorphTargets ) {\n\n\t\t\t\t\tanimationToMorphTargets[ name ] = animationMorphTargets = [];\n\n\t\t\t\t}\n\n\t\t\t\tanimationMorphTargets.push( morphTarget );\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst clips = [];\n\n\t\tfor ( const name in animationToMorphTargets ) {\n\n\t\t\tclips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) );\n\n\t\t}\n\n\t\treturn clips;\n\n\t}\n\n\t// parse the animation.hierarchy format\n\tstatic parseAnimation( animation, bones ) {\n\n\t\tif ( ! animation ) {\n\n\t\t\tconsole.error( 'THREE.AnimationClip: No animation in JSONLoader data.' );\n\t\t\treturn null;\n\n\t\t}\n\n\t\tconst addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) {\n\n\t\t\t// only return track if there are actually keys.\n\t\t\tif ( animationKeys.length !== 0 ) {\n\n\t\t\t\tconst times = [];\n\t\t\t\tconst values = [];\n\n\t\t\t\tflattenJSON( animationKeys, times, values, propertyName );\n\n\t\t\t\t// empty keys are filtered out, so check again\n\t\t\t\tif ( times.length !== 0 ) {\n\n\t\t\t\t\tdestTracks.push( new trackType( trackName, times, values ) );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t};\n\n\t\tconst tracks = [];\n\n\t\tconst clipName = animation.name || 'default';\n\t\tconst fps = animation.fps || 30;\n\t\tconst blendMode = animation.blendMode;\n\n\t\t// automatic length determination in AnimationClip.\n\t\tlet duration = animation.length || - 1;\n\n\t\tconst hierarchyTracks = animation.hierarchy || [];\n\n\t\tfor ( let h = 0; h < hierarchyTracks.length; h ++ ) {\n\n\t\t\tconst animationKeys = hierarchyTracks[ h ].keys;\n\n\t\t\t// skip empty tracks\n\t\t\tif ( ! animationKeys || animationKeys.length === 0 ) continue;\n\n\t\t\t// process morph targets\n\t\t\tif ( animationKeys[ 0 ].morphTargets ) {\n\n\t\t\t\t// figure out all morph targets used in this track\n\t\t\t\tconst morphTargetNames = {};\n\n\t\t\t\tlet k;\n\n\t\t\t\tfor ( k = 0; k < animationKeys.length; k ++ ) {\n\n\t\t\t\t\tif ( animationKeys[ k ].morphTargets ) {\n\n\t\t\t\t\t\tfor ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) {\n\n\t\t\t\t\t\t\tmorphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// create a track for each morph target with all zero\n\t\t\t\t// morphTargetInfluences except for the keys in which\n\t\t\t\t// the morphTarget is named.\n\t\t\t\tfor ( const morphTargetName in morphTargetNames ) {\n\n\t\t\t\t\tconst times = [];\n\t\t\t\t\tconst values = [];\n\n\t\t\t\t\tfor ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) {\n\n\t\t\t\t\t\tconst animationKey = animationKeys[ k ];\n\n\t\t\t\t\t\ttimes.push( animationKey.time );\n\t\t\t\t\t\tvalues.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 );\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) );\n\n\t\t\t\t}\n\n\t\t\t\tduration = morphTargetNames.length * fps;\n\n\t\t\t} else {\n\n\t\t\t\t// ...assume skeletal animation\n\n\t\t\t\tconst boneName = '.bones[' + bones[ h ].name + ']';\n\n\t\t\t\taddNonemptyTrack(\n\t\t\t\t\tVectorKeyframeTrack, boneName + '.position',\n\t\t\t\t\tanimationKeys, 'pos', tracks );\n\n\t\t\t\taddNonemptyTrack(\n\t\t\t\t\tQuaternionKeyframeTrack, boneName + '.quaternion',\n\t\t\t\t\tanimationKeys, 'rot', tracks );\n\n\t\t\t\taddNonemptyTrack(\n\t\t\t\t\tVectorKeyframeTrack, boneName + '.scale',\n\t\t\t\t\tanimationKeys, 'scl', tracks );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( tracks.length === 0 ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\tconst clip = new this( clipName, duration, tracks, blendMode );\n\n\t\treturn clip;\n\n\t}\n\n\tresetDuration() {\n\n\t\tconst tracks = this.tracks;\n\t\tlet duration = 0;\n\n\t\tfor ( let i = 0, n = tracks.length; i !== n; ++ i ) {\n\n\t\t\tconst track = this.tracks[ i ];\n\n\t\t\tduration = Math.max( duration, track.times[ track.times.length - 1 ] );\n\n\t\t}\n\n\t\tthis.duration = duration;\n\n\t\treturn this;\n\n\t}\n\n\ttrim() {\n\n\t\tfor ( let i = 0; i < this.tracks.length; i ++ ) {\n\n\t\t\tthis.tracks[ i ].trim( 0, this.duration );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tvalidate() {\n\n\t\tlet valid = true;\n\n\t\tfor ( let i = 0; i < this.tracks.length; i ++ ) {\n\n\t\t\tvalid = valid && this.tracks[ i ].validate();\n\n\t\t}\n\n\t\treturn valid;\n\n\t}\n\n\toptimize() {\n\n\t\tfor ( let i = 0; i < this.tracks.length; i ++ ) {\n\n\t\t\tthis.tracks[ i ].optimize();\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\tconst tracks = [];\n\n\t\tfor ( let i = 0; i < this.tracks.length; i ++ ) {\n\n\t\t\ttracks.push( this.tracks[ i ].clone() );\n\n\t\t}\n\n\t\treturn new this.constructor( this.name, this.duration, tracks, this.blendMode );\n\n\t}\n\n\ttoJSON() {\n\n\t\treturn this.constructor.toJSON( this );\n\n\t}\n\n}\n\nfunction getTrackTypeForValueTypeName( typeName ) {\n\n\tswitch ( typeName.toLowerCase() ) {\n\n\t\tcase 'scalar':\n\t\tcase 'double':\n\t\tcase 'float':\n\t\tcase 'number':\n\t\tcase 'integer':\n\n\t\t\treturn NumberKeyframeTrack;\n\n\t\tcase 'vector':\n\t\tcase 'vector2':\n\t\tcase 'vector3':\n\t\tcase 'vector4':\n\n\t\t\treturn VectorKeyframeTrack;\n\n\t\tcase 'color':\n\n\t\t\treturn ColorKeyframeTrack;\n\n\t\tcase 'quaternion':\n\n\t\t\treturn QuaternionKeyframeTrack;\n\n\t\tcase 'bool':\n\t\tcase 'boolean':\n\n\t\t\treturn BooleanKeyframeTrack;\n\n\t\tcase 'string':\n\n\t\t\treturn StringKeyframeTrack;\n\n\t}\n\n\tthrow new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName );\n\n}\n\nfunction parseKeyframeTrack( json ) {\n\n\tif ( json.type === undefined ) {\n\n\t\tthrow new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' );\n\n\t}\n\n\tconst trackType = getTrackTypeForValueTypeName( json.type );\n\n\tif ( json.times === undefined ) {\n\n\t\tconst times = [], values = [];\n\n\t\tflattenJSON( json.keys, times, values, 'value' );\n\n\t\tjson.times = times;\n\t\tjson.values = values;\n\n\t}\n\n\t// derived classes can define a static parse method\n\tif ( trackType.parse !== undefined ) {\n\n\t\treturn trackType.parse( json );\n\n\t} else {\n\n\t\t// by default, we assume a constructor compatible with the base\n\t\treturn new trackType( json.name, json.times, json.values, json.interpolation );\n\n\t}\n\n}\n\nconst Cache = {\n\n\tenabled: false,\n\n\tfiles: {},\n\n\tadd: function ( key, file ) {\n\n\t\tif ( this.enabled === false ) return;\n\n\t\t// console.log( 'THREE.Cache', 'Adding key:', key );\n\n\t\tthis.files[ key ] = file;\n\n\t},\n\n\tget: function ( key ) {\n\n\t\tif ( this.enabled === false ) return;\n\n\t\t// console.log( 'THREE.Cache', 'Checking key:', key );\n\n\t\treturn this.files[ key ];\n\n\t},\n\n\tremove: function ( key ) {\n\n\t\tdelete this.files[ key ];\n\n\t},\n\n\tclear: function () {\n\n\t\tthis.files = {};\n\n\t}\n\n};\n\nclass LoadingManager {\n\n\tconstructor( onLoad, onProgress, onError ) {\n\n\t\tconst scope = this;\n\n\t\tlet isLoading = false;\n\t\tlet itemsLoaded = 0;\n\t\tlet itemsTotal = 0;\n\t\tlet urlModifier = undefined;\n\t\tconst handlers = [];\n\n\t\t// Refer to #5689 for the reason why we don't set .onStart\n\t\t// in the constructor\n\n\t\tthis.onStart = undefined;\n\t\tthis.onLoad = onLoad;\n\t\tthis.onProgress = onProgress;\n\t\tthis.onError = onError;\n\n\t\tthis.itemStart = function ( url ) {\n\n\t\t\titemsTotal ++;\n\n\t\t\tif ( isLoading === false ) {\n\n\t\t\t\tif ( scope.onStart !== undefined ) {\n\n\t\t\t\t\tscope.onStart( url, itemsLoaded, itemsTotal );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tisLoading = true;\n\n\t\t};\n\n\t\tthis.itemEnd = function ( url ) {\n\n\t\t\titemsLoaded ++;\n\n\t\t\tif ( scope.onProgress !== undefined ) {\n\n\t\t\t\tscope.onProgress( url, itemsLoaded, itemsTotal );\n\n\t\t\t}\n\n\t\t\tif ( itemsLoaded === itemsTotal ) {\n\n\t\t\t\tisLoading = false;\n\n\t\t\t\tif ( scope.onLoad !== undefined ) {\n\n\t\t\t\t\tscope.onLoad();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.itemError = function ( url ) {\n\n\t\t\tif ( scope.onError !== undefined ) {\n\n\t\t\t\tscope.onError( url );\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.resolveURL = function ( url ) {\n\n\t\t\tif ( urlModifier ) {\n\n\t\t\t\treturn urlModifier( url );\n\n\t\t\t}\n\n\t\t\treturn url;\n\n\t\t};\n\n\t\tthis.setURLModifier = function ( transform ) {\n\n\t\t\turlModifier = transform;\n\n\t\t\treturn this;\n\n\t\t};\n\n\t\tthis.addHandler = function ( regex, loader ) {\n\n\t\t\thandlers.push( regex, loader );\n\n\t\t\treturn this;\n\n\t\t};\n\n\t\tthis.removeHandler = function ( regex ) {\n\n\t\t\tconst index = handlers.indexOf( regex );\n\n\t\t\tif ( index !== - 1 ) {\n\n\t\t\t\thandlers.splice( index, 2 );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t};\n\n\t\tthis.getHandler = function ( file ) {\n\n\t\t\tfor ( let i = 0, l = handlers.length; i < l; i += 2 ) {\n\n\t\t\t\tconst regex = handlers[ i ];\n\t\t\t\tconst loader = handlers[ i + 1 ];\n\n\t\t\t\tif ( regex.global ) regex.lastIndex = 0; // see #17920\n\n\t\t\t\tif ( regex.test( file ) ) {\n\n\t\t\t\t\treturn loader;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn null;\n\n\t\t};\n\n\t}\n\n}\n\nconst DefaultLoadingManager = /*@__PURE__*/ new LoadingManager();\n\nclass Loader {\n\n\tconstructor( manager ) {\n\n\t\tthis.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n\t\tthis.crossOrigin = 'anonymous';\n\t\tthis.withCredentials = false;\n\t\tthis.path = '';\n\t\tthis.resourcePath = '';\n\t\tthis.requestHeader = {};\n\n\t}\n\n\tload( /* url, onLoad, onProgress, onError */ ) {}\n\n\tloadAsync( url, onProgress ) {\n\n\t\tconst scope = this;\n\n\t\treturn new Promise( function ( resolve, reject ) {\n\n\t\t\tscope.load( url, resolve, onProgress, reject );\n\n\t\t} );\n\n\t}\n\n\tparse( /* data */ ) {}\n\n\tsetCrossOrigin( crossOrigin ) {\n\n\t\tthis.crossOrigin = crossOrigin;\n\t\treturn this;\n\n\t}\n\n\tsetWithCredentials( value ) {\n\n\t\tthis.withCredentials = value;\n\t\treturn this;\n\n\t}\n\n\tsetPath( path ) {\n\n\t\tthis.path = path;\n\t\treturn this;\n\n\t}\n\n\tsetResourcePath( resourcePath ) {\n\n\t\tthis.resourcePath = resourcePath;\n\t\treturn this;\n\n\t}\n\n\tsetRequestHeader( requestHeader ) {\n\n\t\tthis.requestHeader = requestHeader;\n\t\treturn this;\n\n\t}\n\n}\n\nLoader.DEFAULT_MATERIAL_NAME = '__DEFAULT';\n\nconst loading = {};\n\nclass HttpError extends Error {\n\n\tconstructor( message, response ) {\n\n\t\tsuper( message );\n\t\tthis.response = response;\n\n\t}\n\n}\n\nclass FileLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\n\t}\n\n\tload( url, onLoad, onProgress, onError ) {\n\n\t\tif ( url === undefined ) url = '';\n\n\t\tif ( this.path !== undefined ) url = this.path + url;\n\n\t\turl = this.manager.resolveURL( url );\n\n\t\tconst cached = Cache.get( url );\n\n\t\tif ( cached !== undefined ) {\n\n\t\t\tthis.manager.itemStart( url );\n\n\t\t\tsetTimeout( () => {\n\n\t\t\t\tif ( onLoad ) onLoad( cached );\n\n\t\t\t\tthis.manager.itemEnd( url );\n\n\t\t\t}, 0 );\n\n\t\t\treturn cached;\n\n\t\t}\n\n\t\t// Check if request is duplicate\n\n\t\tif ( loading[ url ] !== undefined ) {\n\n\t\t\tloading[ url ].push( {\n\n\t\t\t\tonLoad: onLoad,\n\t\t\t\tonProgress: onProgress,\n\t\t\t\tonError: onError\n\n\t\t\t} );\n\n\t\t\treturn;\n\n\t\t}\n\n\t\t// Initialise array for duplicate requests\n\t\tloading[ url ] = [];\n\n\t\tloading[ url ].push( {\n\t\t\tonLoad: onLoad,\n\t\t\tonProgress: onProgress,\n\t\t\tonError: onError,\n\t\t} );\n\n\t\t// create request\n\t\tconst req = new Request( url, {\n\t\t\theaders: new Headers( this.requestHeader ),\n\t\t\tcredentials: this.withCredentials ? 'include' : 'same-origin',\n\t\t\t// An abort controller could be added within a future PR\n\t\t} );\n\n\t\t// record states ( avoid data race )\n\t\tconst mimeType = this.mimeType;\n\t\tconst responseType = this.responseType;\n\n\t\t// start the fetch\n\t\tfetch( req )\n\t\t\t.then( response => {\n\n\t\t\t\tif ( response.status === 200 || response.status === 0 ) {\n\n\t\t\t\t\t// Some browsers return HTTP Status 0 when using non-http protocol\n\t\t\t\t\t// e.g. 'file://' or 'data://'. Handle as success.\n\n\t\t\t\t\tif ( response.status === 0 ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.FileLoader: HTTP Status 0 received.' );\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// Workaround: Checking if response.body === undefined for Alipay browser #23548\n\n\t\t\t\t\tif ( typeof ReadableStream === 'undefined' || response.body === undefined || response.body.getReader === undefined ) {\n\n\t\t\t\t\t\treturn response;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tconst callbacks = loading[ url ];\n\t\t\t\t\tconst reader = response.body.getReader();\n\n\t\t\t\t\t// Nginx needs X-File-Size check\n\t\t\t\t\t// https://serverfault.com/questions/482875/why-does-nginx-remove-content-length-header-for-chunked-content\n\t\t\t\t\tconst contentLength = response.headers.get( 'Content-Length' ) || response.headers.get( 'X-File-Size' );\n\t\t\t\t\tconst total = contentLength ? parseInt( contentLength ) : 0;\n\t\t\t\t\tconst lengthComputable = total !== 0;\n\t\t\t\t\tlet loaded = 0;\n\n\t\t\t\t\t// periodically read data into the new stream tracking while download progress\n\t\t\t\t\tconst stream = new ReadableStream( {\n\t\t\t\t\t\tstart( controller ) {\n\n\t\t\t\t\t\t\treadData();\n\n\t\t\t\t\t\t\tfunction readData() {\n\n\t\t\t\t\t\t\t\treader.read().then( ( { done, value } ) => {\n\n\t\t\t\t\t\t\t\t\tif ( done ) {\n\n\t\t\t\t\t\t\t\t\t\tcontroller.close();\n\n\t\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\t\tloaded += value.byteLength;\n\n\t\t\t\t\t\t\t\t\t\tconst event = new ProgressEvent( 'progress', { lengthComputable, loaded, total } );\n\t\t\t\t\t\t\t\t\t\tfor ( let i = 0, il = callbacks.length; i < il; i ++ ) {\n\n\t\t\t\t\t\t\t\t\t\t\tconst callback = callbacks[ i ];\n\t\t\t\t\t\t\t\t\t\t\tif ( callback.onProgress ) callback.onProgress( event );\n\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tcontroller.enqueue( value );\n\t\t\t\t\t\t\t\t\t\treadData();\n\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t} );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} );\n\n\t\t\t\t\treturn new Response( stream );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthrow new HttpError( `fetch for \"${response.url}\" responded with ${response.status}: ${response.statusText}`, response );\n\n\t\t\t\t}\n\n\t\t\t} )\n\t\t\t.then( response => {\n\n\t\t\t\tswitch ( responseType ) {\n\n\t\t\t\t\tcase 'arraybuffer':\n\n\t\t\t\t\t\treturn response.arrayBuffer();\n\n\t\t\t\t\tcase 'blob':\n\n\t\t\t\t\t\treturn response.blob();\n\n\t\t\t\t\tcase 'document':\n\n\t\t\t\t\t\treturn response.text()\n\t\t\t\t\t\t\t.then( text => {\n\n\t\t\t\t\t\t\t\tconst parser = new DOMParser();\n\t\t\t\t\t\t\t\treturn parser.parseFromString( text, mimeType );\n\n\t\t\t\t\t\t\t} );\n\n\t\t\t\t\tcase 'json':\n\n\t\t\t\t\t\treturn response.json();\n\n\t\t\t\t\tdefault:\n\n\t\t\t\t\t\tif ( mimeType === undefined ) {\n\n\t\t\t\t\t\t\treturn response.text();\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// sniff encoding\n\t\t\t\t\t\t\tconst re = /charset=\"?([^;\"\\s]*)\"?/i;\n\t\t\t\t\t\t\tconst exec = re.exec( mimeType );\n\t\t\t\t\t\t\tconst label = exec && exec[ 1 ] ? exec[ 1 ].toLowerCase() : undefined;\n\t\t\t\t\t\t\tconst decoder = new TextDecoder( label );\n\t\t\t\t\t\t\treturn response.arrayBuffer().then( ab => decoder.decode( ab ) );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} )\n\t\t\t.then( data => {\n\n\t\t\t\t// Add to cache only on HTTP success, so that we do not cache\n\t\t\t\t// error response bodies as proper responses to requests.\n\t\t\t\tCache.add( url, data );\n\n\t\t\t\tconst callbacks = loading[ url ];\n\t\t\t\tdelete loading[ url ];\n\n\t\t\t\tfor ( let i = 0, il = callbacks.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst callback = callbacks[ i ];\n\t\t\t\t\tif ( callback.onLoad ) callback.onLoad( data );\n\n\t\t\t\t}\n\n\t\t\t} )\n\t\t\t.catch( err => {\n\n\t\t\t\t// Abort errors and other errors are handled the same\n\n\t\t\t\tconst callbacks = loading[ url ];\n\n\t\t\t\tif ( callbacks === undefined ) {\n\n\t\t\t\t\t// When onLoad was called and url was deleted in `loading`\n\t\t\t\t\tthis.manager.itemError( url );\n\t\t\t\t\tthrow err;\n\n\t\t\t\t}\n\n\t\t\t\tdelete loading[ url ];\n\n\t\t\t\tfor ( let i = 0, il = callbacks.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst callback = callbacks[ i ];\n\t\t\t\t\tif ( callback.onError ) callback.onError( err );\n\n\t\t\t\t}\n\n\t\t\t\tthis.manager.itemError( url );\n\n\t\t\t} )\n\t\t\t.finally( () => {\n\n\t\t\t\tthis.manager.itemEnd( url );\n\n\t\t\t} );\n\n\t\tthis.manager.itemStart( url );\n\n\t}\n\n\tsetResponseType( value ) {\n\n\t\tthis.responseType = value;\n\t\treturn this;\n\n\t}\n\n\tsetMimeType( value ) {\n\n\t\tthis.mimeType = value;\n\t\treturn this;\n\n\t}\n\n}\n\nclass AnimationLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\n\t}\n\n\tload( url, onLoad, onProgress, onError ) {\n\n\t\tconst scope = this;\n\n\t\tconst loader = new FileLoader( this.manager );\n\t\tloader.setPath( this.path );\n\t\tloader.setRequestHeader( this.requestHeader );\n\t\tloader.setWithCredentials( this.withCredentials );\n\t\tloader.load( url, function ( text ) {\n\n\t\t\ttry {\n\n\t\t\t\tonLoad( scope.parse( JSON.parse( text ) ) );\n\n\t\t\t} catch ( e ) {\n\n\t\t\t\tif ( onError ) {\n\n\t\t\t\t\tonError( e );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconsole.error( e );\n\n\t\t\t\t}\n\n\t\t\t\tscope.manager.itemError( url );\n\n\t\t\t}\n\n\t\t}, onProgress, onError );\n\n\t}\n\n\tparse( json ) {\n\n\t\tconst animations = [];\n\n\t\tfor ( let i = 0; i < json.length; i ++ ) {\n\n\t\t\tconst clip = AnimationClip.parse( json[ i ] );\n\n\t\t\tanimations.push( clip );\n\n\t\t}\n\n\t\treturn animations;\n\n\t}\n\n}\n\n/**\n * Abstract Base class to block based textures loader (dds, pvr, ...)\n *\n * Sub classes have to implement the parse() method which will be used in load().\n */\n\nclass CompressedTextureLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\n\t}\n\n\tload( url, onLoad, onProgress, onError ) {\n\n\t\tconst scope = this;\n\n\t\tconst images = [];\n\n\t\tconst texture = new CompressedTexture();\n\n\t\tconst loader = new FileLoader( this.manager );\n\t\tloader.setPath( this.path );\n\t\tloader.setResponseType( 'arraybuffer' );\n\t\tloader.setRequestHeader( this.requestHeader );\n\t\tloader.setWithCredentials( scope.withCredentials );\n\n\t\tlet loaded = 0;\n\n\t\tfunction loadTexture( i ) {\n\n\t\t\tloader.load( url[ i ], function ( buffer ) {\n\n\t\t\t\tconst texDatas = scope.parse( buffer, true );\n\n\t\t\t\timages[ i ] = {\n\t\t\t\t\twidth: texDatas.width,\n\t\t\t\t\theight: texDatas.height,\n\t\t\t\t\tformat: texDatas.format,\n\t\t\t\t\tmipmaps: texDatas.mipmaps\n\t\t\t\t};\n\n\t\t\t\tloaded += 1;\n\n\t\t\t\tif ( loaded === 6 ) {\n\n\t\t\t\t\tif ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter;\n\n\t\t\t\t\ttexture.image = images;\n\t\t\t\t\ttexture.format = texDatas.format;\n\t\t\t\t\ttexture.needsUpdate = true;\n\n\t\t\t\t\tif ( onLoad ) onLoad( texture );\n\n\t\t\t\t}\n\n\t\t\t}, onProgress, onError );\n\n\t\t}\n\n\t\tif ( Array.isArray( url ) ) {\n\n\t\t\tfor ( let i = 0, il = url.length; i < il; ++ i ) {\n\n\t\t\t\tloadTexture( i );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// compressed cubemap texture stored in a single DDS file\n\n\t\t\tloader.load( url, function ( buffer ) {\n\n\t\t\t\tconst texDatas = scope.parse( buffer, true );\n\n\t\t\t\tif ( texDatas.isCubemap ) {\n\n\t\t\t\t\tconst faces = texDatas.mipmaps.length / texDatas.mipmapCount;\n\n\t\t\t\t\tfor ( let f = 0; f < faces; f ++ ) {\n\n\t\t\t\t\t\timages[ f ] = { mipmaps: [] };\n\n\t\t\t\t\t\tfor ( let i = 0; i < texDatas.mipmapCount; i ++ ) {\n\n\t\t\t\t\t\t\timages[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] );\n\t\t\t\t\t\t\timages[ f ].format = texDatas.format;\n\t\t\t\t\t\t\timages[ f ].width = texDatas.width;\n\t\t\t\t\t\t\timages[ f ].height = texDatas.height;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttexture.image = images;\n\n\t\t\t\t} else {\n\n\t\t\t\t\ttexture.image.width = texDatas.width;\n\t\t\t\t\ttexture.image.height = texDatas.height;\n\t\t\t\t\ttexture.mipmaps = texDatas.mipmaps;\n\n\t\t\t\t}\n\n\t\t\t\tif ( texDatas.mipmapCount === 1 ) {\n\n\t\t\t\t\ttexture.minFilter = LinearFilter;\n\n\t\t\t\t}\n\n\t\t\t\ttexture.format = texDatas.format;\n\t\t\t\ttexture.needsUpdate = true;\n\n\t\t\t\tif ( onLoad ) onLoad( texture );\n\n\t\t\t}, onProgress, onError );\n\n\t\t}\n\n\t\treturn texture;\n\n\t}\n\n}\n\nclass ImageLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\n\t}\n\n\tload( url, onLoad, onProgress, onError ) {\n\n\t\tif ( this.path !== undefined ) url = this.path + url;\n\n\t\turl = this.manager.resolveURL( url );\n\n\t\tconst scope = this;\n\n\t\tconst cached = Cache.get( url );\n\n\t\tif ( cached !== undefined ) {\n\n\t\t\tscope.manager.itemStart( url );\n\n\t\t\tsetTimeout( function () {\n\n\t\t\t\tif ( onLoad ) onLoad( cached );\n\n\t\t\t\tscope.manager.itemEnd( url );\n\n\t\t\t}, 0 );\n\n\t\t\treturn cached;\n\n\t\t}\n\n\t\tconst image = createElementNS( 'img' );\n\n\t\tfunction onImageLoad() {\n\n\t\t\tremoveEventListeners();\n\n\t\t\tCache.add( url, this );\n\n\t\t\tif ( onLoad ) onLoad( this );\n\n\t\t\tscope.manager.itemEnd( url );\n\n\t\t}\n\n\t\tfunction onImageError( event ) {\n\n\t\t\tremoveEventListeners();\n\n\t\t\tif ( onError ) onError( event );\n\n\t\t\tscope.manager.itemError( url );\n\t\t\tscope.manager.itemEnd( url );\n\n\t\t}\n\n\t\tfunction removeEventListeners() {\n\n\t\t\timage.removeEventListener( 'load', onImageLoad, false );\n\t\t\timage.removeEventListener( 'error', onImageError, false );\n\n\t\t}\n\n\t\timage.addEventListener( 'load', onImageLoad, false );\n\t\timage.addEventListener( 'error', onImageError, false );\n\n\t\tif ( url.slice( 0, 5 ) !== 'data:' ) {\n\n\t\t\tif ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin;\n\n\t\t}\n\n\t\tscope.manager.itemStart( url );\n\n\t\timage.src = url;\n\n\t\treturn image;\n\n\t}\n\n}\n\nclass CubeTextureLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\n\t}\n\n\tload( urls, onLoad, onProgress, onError ) {\n\n\t\tconst texture = new CubeTexture();\n\t\ttexture.colorSpace = SRGBColorSpace;\n\n\t\tconst loader = new ImageLoader( this.manager );\n\t\tloader.setCrossOrigin( this.crossOrigin );\n\t\tloader.setPath( this.path );\n\n\t\tlet loaded = 0;\n\n\t\tfunction loadTexture( i ) {\n\n\t\t\tloader.load( urls[ i ], function ( image ) {\n\n\t\t\t\ttexture.images[ i ] = image;\n\n\t\t\t\tloaded ++;\n\n\t\t\t\tif ( loaded === 6 ) {\n\n\t\t\t\t\ttexture.needsUpdate = true;\n\n\t\t\t\t\tif ( onLoad ) onLoad( texture );\n\n\t\t\t\t}\n\n\t\t\t}, undefined, onError );\n\n\t\t}\n\n\t\tfor ( let i = 0; i < urls.length; ++ i ) {\n\n\t\t\tloadTexture( i );\n\n\t\t}\n\n\t\treturn texture;\n\n\t}\n\n}\n\n/**\n * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...)\n *\n * Sub classes have to implement the parse() method which will be used in load().\n */\n\nclass DataTextureLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\n\t}\n\n\tload( url, onLoad, onProgress, onError ) {\n\n\t\tconst scope = this;\n\n\t\tconst texture = new DataTexture();\n\n\t\tconst loader = new FileLoader( this.manager );\n\t\tloader.setResponseType( 'arraybuffer' );\n\t\tloader.setRequestHeader( this.requestHeader );\n\t\tloader.setPath( this.path );\n\t\tloader.setWithCredentials( scope.withCredentials );\n\t\tloader.load( url, function ( buffer ) {\n\n\t\t\tlet texData;\n\n\t\t\ttry {\n\n\t\t\t\ttexData = scope.parse( buffer );\n\n\t\t\t} catch ( error ) {\n\n\t\t\t\tif ( onError !== undefined ) {\n\n\t\t\t\t\tonError( error );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconsole.error( error );\n\t\t\t\t\treturn;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( texData.image !== undefined ) {\n\n\t\t\t\ttexture.image = texData.image;\n\n\t\t\t} else if ( texData.data !== undefined ) {\n\n\t\t\t\ttexture.image.width = texData.width;\n\t\t\t\ttexture.image.height = texData.height;\n\t\t\t\ttexture.image.data = texData.data;\n\n\t\t\t}\n\n\t\t\ttexture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping;\n\t\t\ttexture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping;\n\n\t\t\ttexture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter;\n\t\t\ttexture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter;\n\n\t\t\ttexture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1;\n\n\t\t\tif ( texData.colorSpace !== undefined ) {\n\n\t\t\t\ttexture.colorSpace = texData.colorSpace;\n\n\t\t\t} else if ( texData.encoding !== undefined ) { // @deprecated, r152\n\n\t\t\t\ttexture.encoding = texData.encoding;\n\n\t\t\t}\n\n\t\t\tif ( texData.flipY !== undefined ) {\n\n\t\t\t\ttexture.flipY = texData.flipY;\n\n\t\t\t}\n\n\t\t\tif ( texData.format !== undefined ) {\n\n\t\t\t\ttexture.format = texData.format;\n\n\t\t\t}\n\n\t\t\tif ( texData.type !== undefined ) {\n\n\t\t\t\ttexture.type = texData.type;\n\n\t\t\t}\n\n\t\t\tif ( texData.mipmaps !== undefined ) {\n\n\t\t\t\ttexture.mipmaps = texData.mipmaps;\n\t\t\t\ttexture.minFilter = LinearMipmapLinearFilter; // presumably...\n\n\t\t\t}\n\n\t\t\tif ( texData.mipmapCount === 1 ) {\n\n\t\t\t\ttexture.minFilter = LinearFilter;\n\n\t\t\t}\n\n\t\t\tif ( texData.generateMipmaps !== undefined ) {\n\n\t\t\t\ttexture.generateMipmaps = texData.generateMipmaps;\n\n\t\t\t}\n\n\t\t\ttexture.needsUpdate = true;\n\n\t\t\tif ( onLoad ) onLoad( texture, texData );\n\n\t\t}, onProgress, onError );\n\n\n\t\treturn texture;\n\n\t}\n\n}\n\nclass TextureLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\n\t}\n\n\tload( url, onLoad, onProgress, onError ) {\n\n\t\tconst texture = new Texture();\n\n\t\tconst loader = new ImageLoader( this.manager );\n\t\tloader.setCrossOrigin( this.crossOrigin );\n\t\tloader.setPath( this.path );\n\n\t\tloader.load( url, function ( image ) {\n\n\t\t\ttexture.image = image;\n\t\t\ttexture.needsUpdate = true;\n\n\t\t\tif ( onLoad !== undefined ) {\n\n\t\t\t\tonLoad( texture );\n\n\t\t\t}\n\n\t\t}, onProgress, onError );\n\n\t\treturn texture;\n\n\t}\n\n}\n\nclass Light extends Object3D {\n\n\tconstructor( color, intensity = 1 ) {\n\n\t\tsuper();\n\n\t\tthis.isLight = true;\n\n\t\tthis.type = 'Light';\n\n\t\tthis.color = new Color( color );\n\t\tthis.intensity = intensity;\n\n\t}\n\n\tdispose() {\n\n\t\t// Empty here in base class; some subclasses override.\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.color.copy( source.color );\n\t\tthis.intensity = source.intensity;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tdata.object.color = this.color.getHex();\n\t\tdata.object.intensity = this.intensity;\n\n\t\tif ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex();\n\n\t\tif ( this.distance !== undefined ) data.object.distance = this.distance;\n\t\tif ( this.angle !== undefined ) data.object.angle = this.angle;\n\t\tif ( this.decay !== undefined ) data.object.decay = this.decay;\n\t\tif ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra;\n\n\t\tif ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON();\n\n\t\treturn data;\n\n\t}\n\n}\n\nclass HemisphereLight extends Light {\n\n\tconstructor( skyColor, groundColor, intensity ) {\n\n\t\tsuper( skyColor, intensity );\n\n\t\tthis.isHemisphereLight = true;\n\n\t\tthis.type = 'HemisphereLight';\n\n\t\tthis.position.copy( Object3D.DEFAULT_UP );\n\t\tthis.updateMatrix();\n\n\t\tthis.groundColor = new Color( groundColor );\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.groundColor.copy( source.groundColor );\n\n\t\treturn this;\n\n\t}\n\n}\n\nconst _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4();\nconst _lightPositionWorld$1 = /*@__PURE__*/ new Vector3();\nconst _lookTarget$1 = /*@__PURE__*/ new Vector3();\n\nclass LightShadow {\n\n\tconstructor( camera ) {\n\n\t\tthis.camera = camera;\n\n\t\tthis.bias = 0;\n\t\tthis.normalBias = 0;\n\t\tthis.radius = 1;\n\t\tthis.blurSamples = 8;\n\n\t\tthis.mapSize = new Vector2( 512, 512 );\n\n\t\tthis.map = null;\n\t\tthis.mapPass = null;\n\t\tthis.matrix = new Matrix4();\n\n\t\tthis.autoUpdate = true;\n\t\tthis.needsUpdate = false;\n\n\t\tthis._frustum = new Frustum();\n\t\tthis._frameExtents = new Vector2( 1, 1 );\n\n\t\tthis._viewportCount = 1;\n\n\t\tthis._viewports = [\n\n\t\t\tnew Vector4( 0, 0, 1, 1 )\n\n\t\t];\n\n\t}\n\n\tgetViewportCount() {\n\n\t\treturn this._viewportCount;\n\n\t}\n\n\tgetFrustum() {\n\n\t\treturn this._frustum;\n\n\t}\n\n\tupdateMatrices( light ) {\n\n\t\tconst shadowCamera = this.camera;\n\t\tconst shadowMatrix = this.matrix;\n\n\t\t_lightPositionWorld$1.setFromMatrixPosition( light.matrixWorld );\n\t\tshadowCamera.position.copy( _lightPositionWorld$1 );\n\n\t\t_lookTarget$1.setFromMatrixPosition( light.target.matrixWorld );\n\t\tshadowCamera.lookAt( _lookTarget$1 );\n\t\tshadowCamera.updateMatrixWorld();\n\n\t\t_projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );\n\t\tthis._frustum.setFromProjectionMatrix( _projScreenMatrix$1 );\n\n\t\tshadowMatrix.set(\n\t\t\t0.5, 0.0, 0.0, 0.5,\n\t\t\t0.0, 0.5, 0.0, 0.5,\n\t\t\t0.0, 0.0, 0.5, 0.5,\n\t\t\t0.0, 0.0, 0.0, 1.0\n\t\t);\n\n\t\tshadowMatrix.multiply( _projScreenMatrix$1 );\n\n\t}\n\n\tgetViewport( viewportIndex ) {\n\n\t\treturn this._viewports[ viewportIndex ];\n\n\t}\n\n\tgetFrameExtents() {\n\n\t\treturn this._frameExtents;\n\n\t}\n\n\tdispose() {\n\n\t\tif ( this.map ) {\n\n\t\t\tthis.map.dispose();\n\n\t\t}\n\n\t\tif ( this.mapPass ) {\n\n\t\t\tthis.mapPass.dispose();\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.camera = source.camera.clone();\n\n\t\tthis.bias = source.bias;\n\t\tthis.radius = source.radius;\n\n\t\tthis.mapSize.copy( source.mapSize );\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst object = {};\n\n\t\tif ( this.bias !== 0 ) object.bias = this.bias;\n\t\tif ( this.normalBias !== 0 ) object.normalBias = this.normalBias;\n\t\tif ( this.radius !== 1 ) object.radius = this.radius;\n\t\tif ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray();\n\n\t\tobject.camera = this.camera.toJSON( false ).object;\n\t\tdelete object.camera.matrix;\n\n\t\treturn object;\n\n\t}\n\n}\n\nclass SpotLightShadow extends LightShadow {\n\n\tconstructor() {\n\n\t\tsuper( new PerspectiveCamera( 50, 1, 0.5, 500 ) );\n\n\t\tthis.isSpotLightShadow = true;\n\n\t\tthis.focus = 1;\n\n\t}\n\n\tupdateMatrices( light ) {\n\n\t\tconst camera = this.camera;\n\n\t\tconst fov = RAD2DEG * 2 * light.angle * this.focus;\n\t\tconst aspect = this.mapSize.width / this.mapSize.height;\n\t\tconst far = light.distance || camera.far;\n\n\t\tif ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) {\n\n\t\t\tcamera.fov = fov;\n\t\t\tcamera.aspect = aspect;\n\t\t\tcamera.far = far;\n\t\t\tcamera.updateProjectionMatrix();\n\n\t\t}\n\n\t\tsuper.updateMatrices( light );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.focus = source.focus;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass SpotLight extends Light {\n\n\tconstructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 2 ) {\n\n\t\tsuper( color, intensity );\n\n\t\tthis.isSpotLight = true;\n\n\t\tthis.type = 'SpotLight';\n\n\t\tthis.position.copy( Object3D.DEFAULT_UP );\n\t\tthis.updateMatrix();\n\n\t\tthis.target = new Object3D();\n\n\t\tthis.distance = distance;\n\t\tthis.angle = angle;\n\t\tthis.penumbra = penumbra;\n\t\tthis.decay = decay;\n\n\t\tthis.map = null;\n\n\t\tthis.shadow = new SpotLightShadow();\n\n\t}\n\n\tget power() {\n\n\t\t// compute the light's luminous power (in lumens) from its intensity (in candela)\n\t\t// by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd)\n\t\treturn this.intensity * Math.PI;\n\n\t}\n\n\tset power( power ) {\n\n\t\t// set the light's intensity (in candela) from the desired luminous power (in lumens)\n\t\tthis.intensity = power / Math.PI;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.shadow.dispose();\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.distance = source.distance;\n\t\tthis.angle = source.angle;\n\t\tthis.penumbra = source.penumbra;\n\t\tthis.decay = source.decay;\n\n\t\tthis.target = source.target.clone();\n\n\t\tthis.shadow = source.shadow.clone();\n\n\t\treturn this;\n\n\t}\n\n}\n\nconst _projScreenMatrix = /*@__PURE__*/ new Matrix4();\nconst _lightPositionWorld = /*@__PURE__*/ new Vector3();\nconst _lookTarget = /*@__PURE__*/ new Vector3();\n\nclass PointLightShadow extends LightShadow {\n\n\tconstructor() {\n\n\t\tsuper( new PerspectiveCamera( 90, 1, 0.5, 500 ) );\n\n\t\tthis.isPointLightShadow = true;\n\n\t\tthis._frameExtents = new Vector2( 4, 2 );\n\n\t\tthis._viewportCount = 6;\n\n\t\tthis._viewports = [\n\t\t\t// These viewports map a cube-map onto a 2D texture with the\n\t\t\t// following orientation:\n\t\t\t//\n\t\t\t//  xzXZ\n\t\t\t//   y Y\n\t\t\t//\n\t\t\t// X - Positive x direction\n\t\t\t// x - Negative x direction\n\t\t\t// Y - Positive y direction\n\t\t\t// y - Negative y direction\n\t\t\t// Z - Positive z direction\n\t\t\t// z - Negative z direction\n\n\t\t\t// positive X\n\t\t\tnew Vector4( 2, 1, 1, 1 ),\n\t\t\t// negative X\n\t\t\tnew Vector4( 0, 1, 1, 1 ),\n\t\t\t// positive Z\n\t\t\tnew Vector4( 3, 1, 1, 1 ),\n\t\t\t// negative Z\n\t\t\tnew Vector4( 1, 1, 1, 1 ),\n\t\t\t// positive Y\n\t\t\tnew Vector4( 3, 0, 1, 1 ),\n\t\t\t// negative Y\n\t\t\tnew Vector4( 1, 0, 1, 1 )\n\t\t];\n\n\t\tthis._cubeDirections = [\n\t\t\tnew Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ),\n\t\t\tnew Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 )\n\t\t];\n\n\t\tthis._cubeUps = [\n\t\t\tnew Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ),\n\t\t\tnew Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ),\tnew Vector3( 0, 0, - 1 )\n\t\t];\n\n\t}\n\n\tupdateMatrices( light, viewportIndex = 0 ) {\n\n\t\tconst camera = this.camera;\n\t\tconst shadowMatrix = this.matrix;\n\n\t\tconst far = light.distance || camera.far;\n\n\t\tif ( far !== camera.far ) {\n\n\t\t\tcamera.far = far;\n\t\t\tcamera.updateProjectionMatrix();\n\n\t\t}\n\n\t\t_lightPositionWorld.setFromMatrixPosition( light.matrixWorld );\n\t\tcamera.position.copy( _lightPositionWorld );\n\n\t\t_lookTarget.copy( camera.position );\n\t\t_lookTarget.add( this._cubeDirections[ viewportIndex ] );\n\t\tcamera.up.copy( this._cubeUps[ viewportIndex ] );\n\t\tcamera.lookAt( _lookTarget );\n\t\tcamera.updateMatrixWorld();\n\n\t\tshadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z );\n\n\t\t_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );\n\t\tthis._frustum.setFromProjectionMatrix( _projScreenMatrix );\n\n\t}\n\n}\n\nclass PointLight extends Light {\n\n\tconstructor( color, intensity, distance = 0, decay = 2 ) {\n\n\t\tsuper( color, intensity );\n\n\t\tthis.isPointLight = true;\n\n\t\tthis.type = 'PointLight';\n\n\t\tthis.distance = distance;\n\t\tthis.decay = decay;\n\n\t\tthis.shadow = new PointLightShadow();\n\n\t}\n\n\tget power() {\n\n\t\t// compute the light's luminous power (in lumens) from its intensity (in candela)\n\t\t// for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd)\n\t\treturn this.intensity * 4 * Math.PI;\n\n\t}\n\n\tset power( power ) {\n\n\t\t// set the light's intensity (in candela) from the desired luminous power (in lumens)\n\t\tthis.intensity = power / ( 4 * Math.PI );\n\n\t}\n\n\tdispose() {\n\n\t\tthis.shadow.dispose();\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.distance = source.distance;\n\t\tthis.decay = source.decay;\n\n\t\tthis.shadow = source.shadow.clone();\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass DirectionalLightShadow extends LightShadow {\n\n\tconstructor() {\n\n\t\tsuper( new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) );\n\n\t\tthis.isDirectionalLightShadow = true;\n\n\t}\n\n}\n\nclass DirectionalLight extends Light {\n\n\tconstructor( color, intensity ) {\n\n\t\tsuper( color, intensity );\n\n\t\tthis.isDirectionalLight = true;\n\n\t\tthis.type = 'DirectionalLight';\n\n\t\tthis.position.copy( Object3D.DEFAULT_UP );\n\t\tthis.updateMatrix();\n\n\t\tthis.target = new Object3D();\n\n\t\tthis.shadow = new DirectionalLightShadow();\n\n\t}\n\n\tdispose() {\n\n\t\tthis.shadow.dispose();\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.target = source.target.clone();\n\t\tthis.shadow = source.shadow.clone();\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass AmbientLight extends Light {\n\n\tconstructor( color, intensity ) {\n\n\t\tsuper( color, intensity );\n\n\t\tthis.isAmbientLight = true;\n\n\t\tthis.type = 'AmbientLight';\n\n\t}\n\n}\n\nclass RectAreaLight extends Light {\n\n\tconstructor( color, intensity, width = 10, height = 10 ) {\n\n\t\tsuper( color, intensity );\n\n\t\tthis.isRectAreaLight = true;\n\n\t\tthis.type = 'RectAreaLight';\n\n\t\tthis.width = width;\n\t\tthis.height = height;\n\n\t}\n\n\tget power() {\n\n\t\t// compute the light's luminous power (in lumens) from its intensity (in nits)\n\t\treturn this.intensity * this.width * this.height * Math.PI;\n\n\t}\n\n\tset power( power ) {\n\n\t\t// set the light's intensity (in nits) from the desired luminous power (in lumens)\n\t\tthis.intensity = power / ( this.width * this.height * Math.PI );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.width = source.width;\n\t\tthis.height = source.height;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tdata.object.width = this.width;\n\t\tdata.object.height = this.height;\n\n\t\treturn data;\n\n\t}\n\n}\n\n/**\n * Primary reference:\n *   https://graphics.stanford.edu/papers/envmap/envmap.pdf\n *\n * Secondary reference:\n *   https://www.ppsloan.org/publications/StupidSH36.pdf\n */\n\n// 3-band SH defined by 9 coefficients\n\nclass SphericalHarmonics3 {\n\n\tconstructor() {\n\n\t\tthis.isSphericalHarmonics3 = true;\n\n\t\tthis.coefficients = [];\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tthis.coefficients.push( new Vector3() );\n\n\t\t}\n\n\t}\n\n\tset( coefficients ) {\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tthis.coefficients[ i ].copy( coefficients[ i ] );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tzero() {\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tthis.coefficients[ i ].set( 0, 0, 0 );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\t// get the radiance in the direction of the normal\n\t// target is a Vector3\n\tgetAt( normal, target ) {\n\n\t\t// normal is assumed to be unit length\n\n\t\tconst x = normal.x, y = normal.y, z = normal.z;\n\n\t\tconst coeff = this.coefficients;\n\n\t\t// band 0\n\t\ttarget.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 );\n\n\t\t// band 1\n\t\ttarget.addScaledVector( coeff[ 1 ], 0.488603 * y );\n\t\ttarget.addScaledVector( coeff[ 2 ], 0.488603 * z );\n\t\ttarget.addScaledVector( coeff[ 3 ], 0.488603 * x );\n\n\t\t// band 2\n\t\ttarget.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) );\n\t\ttarget.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) );\n\t\ttarget.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) );\n\t\ttarget.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) );\n\t\ttarget.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) );\n\n\t\treturn target;\n\n\t}\n\n\t// get the irradiance (radiance convolved with cosine lobe) in the direction of the normal\n\t// target is a Vector3\n\t// https://graphics.stanford.edu/papers/envmap/envmap.pdf\n\tgetIrradianceAt( normal, target ) {\n\n\t\t// normal is assumed to be unit length\n\n\t\tconst x = normal.x, y = normal.y, z = normal.z;\n\n\t\tconst coeff = this.coefficients;\n\n\t\t// band 0\n\t\ttarget.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095\n\n\t\t// band 1\n\t\ttarget.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603\n\t\ttarget.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z );\n\t\ttarget.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x );\n\n\t\t// band 2\n\t\ttarget.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548\n\t\ttarget.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z );\n\t\ttarget.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3\n\t\ttarget.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z );\n\t\ttarget.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274\n\n\t\treturn target;\n\n\t}\n\n\tadd( sh ) {\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tthis.coefficients[ i ].add( sh.coefficients[ i ] );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\taddScaledSH( sh, s ) {\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tthis.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tscale( s ) {\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tthis.coefficients[ i ].multiplyScalar( s );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tlerp( sh, alpha ) {\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tthis.coefficients[ i ].lerp( sh.coefficients[ i ], alpha );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tequals( sh ) {\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tif ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) {\n\n\t\t\t\treturn false;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tcopy( sh ) {\n\n\t\treturn this.set( sh.coefficients );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tconst coefficients = this.coefficients;\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tcoefficients[ i ].fromArray( array, offset + ( i * 3 ) );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tconst coefficients = this.coefficients;\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tcoefficients[ i ].toArray( array, offset + ( i * 3 ) );\n\n\t\t}\n\n\t\treturn array;\n\n\t}\n\n\t// evaluate the basis functions\n\t// shBasis is an Array[ 9 ]\n\tstatic getBasisAt( normal, shBasis ) {\n\n\t\t// normal is assumed to be unit length\n\n\t\tconst x = normal.x, y = normal.y, z = normal.z;\n\n\t\t// band 0\n\t\tshBasis[ 0 ] = 0.282095;\n\n\t\t// band 1\n\t\tshBasis[ 1 ] = 0.488603 * y;\n\t\tshBasis[ 2 ] = 0.488603 * z;\n\t\tshBasis[ 3 ] = 0.488603 * x;\n\n\t\t// band 2\n\t\tshBasis[ 4 ] = 1.092548 * x * y;\n\t\tshBasis[ 5 ] = 1.092548 * y * z;\n\t\tshBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 );\n\t\tshBasis[ 7 ] = 1.092548 * x * z;\n\t\tshBasis[ 8 ] = 0.546274 * ( x * x - y * y );\n\n\t}\n\n}\n\nclass LightProbe extends Light {\n\n\tconstructor( sh = new SphericalHarmonics3(), intensity = 1 ) {\n\n\t\tsuper( undefined, intensity );\n\n\t\tthis.isLightProbe = true;\n\n\t\tthis.sh = sh;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.sh.copy( source.sh );\n\n\t\treturn this;\n\n\t}\n\n\tfromJSON( json ) {\n\n\t\tthis.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON();\n\t\tthis.sh.fromArray( json.sh );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tdata.object.sh = this.sh.toArray();\n\n\t\treturn data;\n\n\t}\n\n}\n\nclass MaterialLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\t\tthis.textures = {};\n\n\t}\n\n\tload( url, onLoad, onProgress, onError ) {\n\n\t\tconst scope = this;\n\n\t\tconst loader = new FileLoader( scope.manager );\n\t\tloader.setPath( scope.path );\n\t\tloader.setRequestHeader( scope.requestHeader );\n\t\tloader.setWithCredentials( scope.withCredentials );\n\t\tloader.load( url, function ( text ) {\n\n\t\t\ttry {\n\n\t\t\t\tonLoad( scope.parse( JSON.parse( text ) ) );\n\n\t\t\t} catch ( e ) {\n\n\t\t\t\tif ( onError ) {\n\n\t\t\t\t\tonError( e );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconsole.error( e );\n\n\t\t\t\t}\n\n\t\t\t\tscope.manager.itemError( url );\n\n\t\t\t}\n\n\t\t}, onProgress, onError );\n\n\t}\n\n\tparse( json ) {\n\n\t\tconst textures = this.textures;\n\n\t\tfunction getTexture( name ) {\n\n\t\t\tif ( textures[ name ] === undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.MaterialLoader: Undefined texture', name );\n\n\t\t\t}\n\n\t\t\treturn textures[ name ];\n\n\t\t}\n\n\t\tconst material = MaterialLoader.createMaterialFromType( json.type );\n\n\t\tif ( json.uuid !== undefined ) material.uuid = json.uuid;\n\t\tif ( json.name !== undefined ) material.name = json.name;\n\t\tif ( json.color !== undefined && material.color !== undefined ) material.color.setHex( json.color );\n\t\tif ( json.roughness !== undefined ) material.roughness = json.roughness;\n\t\tif ( json.metalness !== undefined ) material.metalness = json.metalness;\n\t\tif ( json.sheen !== undefined ) material.sheen = json.sheen;\n\t\tif ( json.sheenColor !== undefined ) material.sheenColor = new Color().setHex( json.sheenColor );\n\t\tif ( json.sheenRoughness !== undefined ) material.sheenRoughness = json.sheenRoughness;\n\t\tif ( json.emissive !== undefined && material.emissive !== undefined ) material.emissive.setHex( json.emissive );\n\t\tif ( json.specular !== undefined && material.specular !== undefined ) material.specular.setHex( json.specular );\n\t\tif ( json.specularIntensity !== undefined ) material.specularIntensity = json.specularIntensity;\n\t\tif ( json.specularColor !== undefined && material.specularColor !== undefined ) material.specularColor.setHex( json.specularColor );\n\t\tif ( json.shininess !== undefined ) material.shininess = json.shininess;\n\t\tif ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat;\n\t\tif ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness;\n\t\tif ( json.iridescence !== undefined ) material.iridescence = json.iridescence;\n\t\tif ( json.iridescenceIOR !== undefined ) material.iridescenceIOR = json.iridescenceIOR;\n\t\tif ( json.iridescenceThicknessRange !== undefined ) material.iridescenceThicknessRange = json.iridescenceThicknessRange;\n\t\tif ( json.transmission !== undefined ) material.transmission = json.transmission;\n\t\tif ( json.thickness !== undefined ) material.thickness = json.thickness;\n\t\tif ( json.attenuationDistance !== undefined ) material.attenuationDistance = json.attenuationDistance;\n\t\tif ( json.attenuationColor !== undefined && material.attenuationColor !== undefined ) material.attenuationColor.setHex( json.attenuationColor );\n\t\tif ( json.anisotropy !== undefined ) material.anisotropy = json.anisotropy;\n\t\tif ( json.anisotropyRotation !== undefined ) material.anisotropyRotation = json.anisotropyRotation;\n\t\tif ( json.fog !== undefined ) material.fog = json.fog;\n\t\tif ( json.flatShading !== undefined ) material.flatShading = json.flatShading;\n\t\tif ( json.blending !== undefined ) material.blending = json.blending;\n\t\tif ( json.combine !== undefined ) material.combine = json.combine;\n\t\tif ( json.side !== undefined ) material.side = json.side;\n\t\tif ( json.shadowSide !== undefined ) material.shadowSide = json.shadowSide;\n\t\tif ( json.opacity !== undefined ) material.opacity = json.opacity;\n\t\tif ( json.transparent !== undefined ) material.transparent = json.transparent;\n\t\tif ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest;\n\t\tif ( json.alphaHash !== undefined ) material.alphaHash = json.alphaHash;\n\t\tif ( json.depthFunc !== undefined ) material.depthFunc = json.depthFunc;\n\t\tif ( json.depthTest !== undefined ) material.depthTest = json.depthTest;\n\t\tif ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite;\n\t\tif ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite;\n\t\tif ( json.blendSrc !== undefined ) material.blendSrc = json.blendSrc;\n\t\tif ( json.blendDst !== undefined ) material.blendDst = json.blendDst;\n\t\tif ( json.blendEquation !== undefined ) material.blendEquation = json.blendEquation;\n\t\tif ( json.blendSrcAlpha !== undefined ) material.blendSrcAlpha = json.blendSrcAlpha;\n\t\tif ( json.blendDstAlpha !== undefined ) material.blendDstAlpha = json.blendDstAlpha;\n\t\tif ( json.blendEquationAlpha !== undefined ) material.blendEquationAlpha = json.blendEquationAlpha;\n\t\tif ( json.blendColor !== undefined && material.blendColor !== undefined ) material.blendColor.setHex( json.blendColor );\n\t\tif ( json.blendAlpha !== undefined ) material.blendAlpha = json.blendAlpha;\n\t\tif ( json.stencilWriteMask !== undefined ) material.stencilWriteMask = json.stencilWriteMask;\n\t\tif ( json.stencilFunc !== undefined ) material.stencilFunc = json.stencilFunc;\n\t\tif ( json.stencilRef !== undefined ) material.stencilRef = json.stencilRef;\n\t\tif ( json.stencilFuncMask !== undefined ) material.stencilFuncMask = json.stencilFuncMask;\n\t\tif ( json.stencilFail !== undefined ) material.stencilFail = json.stencilFail;\n\t\tif ( json.stencilZFail !== undefined ) material.stencilZFail = json.stencilZFail;\n\t\tif ( json.stencilZPass !== undefined ) material.stencilZPass = json.stencilZPass;\n\t\tif ( json.stencilWrite !== undefined ) material.stencilWrite = json.stencilWrite;\n\n\t\tif ( json.wireframe !== undefined ) material.wireframe = json.wireframe;\n\t\tif ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;\n\t\tif ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap;\n\t\tif ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin;\n\n\t\tif ( json.rotation !== undefined ) material.rotation = json.rotation;\n\n\t\tif ( json.linewidth !== undefined ) material.linewidth = json.linewidth;\n\t\tif ( json.dashSize !== undefined ) material.dashSize = json.dashSize;\n\t\tif ( json.gapSize !== undefined ) material.gapSize = json.gapSize;\n\t\tif ( json.scale !== undefined ) material.scale = json.scale;\n\n\t\tif ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset;\n\t\tif ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor;\n\t\tif ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits;\n\n\t\tif ( json.dithering !== undefined ) material.dithering = json.dithering;\n\n\t\tif ( json.alphaToCoverage !== undefined ) material.alphaToCoverage = json.alphaToCoverage;\n\t\tif ( json.premultipliedAlpha !== undefined ) material.premultipliedAlpha = json.premultipliedAlpha;\n\t\tif ( json.forceSinglePass !== undefined ) material.forceSinglePass = json.forceSinglePass;\n\n\t\tif ( json.visible !== undefined ) material.visible = json.visible;\n\n\t\tif ( json.toneMapped !== undefined ) material.toneMapped = json.toneMapped;\n\n\t\tif ( json.userData !== undefined ) material.userData = json.userData;\n\n\t\tif ( json.vertexColors !== undefined ) {\n\n\t\t\tif ( typeof json.vertexColors === 'number' ) {\n\n\t\t\t\tmaterial.vertexColors = ( json.vertexColors > 0 ) ? true : false;\n\n\t\t\t} else {\n\n\t\t\t\tmaterial.vertexColors = json.vertexColors;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Shader Material\n\n\t\tif ( json.uniforms !== undefined ) {\n\n\t\t\tfor ( const name in json.uniforms ) {\n\n\t\t\t\tconst uniform = json.uniforms[ name ];\n\n\t\t\t\tmaterial.uniforms[ name ] = {};\n\n\t\t\t\tswitch ( uniform.type ) {\n\n\t\t\t\t\tcase 't':\n\t\t\t\t\t\tmaterial.uniforms[ name ].value = getTexture( uniform.value );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'c':\n\t\t\t\t\t\tmaterial.uniforms[ name ].value = new Color().setHex( uniform.value );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'v2':\n\t\t\t\t\t\tmaterial.uniforms[ name ].value = new Vector2().fromArray( uniform.value );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'v3':\n\t\t\t\t\t\tmaterial.uniforms[ name ].value = new Vector3().fromArray( uniform.value );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'v4':\n\t\t\t\t\t\tmaterial.uniforms[ name ].value = new Vector4().fromArray( uniform.value );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'm3':\n\t\t\t\t\t\tmaterial.uniforms[ name ].value = new Matrix3().fromArray( uniform.value );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'm4':\n\t\t\t\t\t\tmaterial.uniforms[ name ].value = new Matrix4().fromArray( uniform.value );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tmaterial.uniforms[ name ].value = uniform.value;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( json.defines !== undefined ) material.defines = json.defines;\n\t\tif ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader;\n\t\tif ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader;\n\t\tif ( json.glslVersion !== undefined ) material.glslVersion = json.glslVersion;\n\n\t\tif ( json.extensions !== undefined ) {\n\n\t\t\tfor ( const key in json.extensions ) {\n\n\t\t\t\tmaterial.extensions[ key ] = json.extensions[ key ];\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( json.lights !== undefined ) material.lights = json.lights;\n\t\tif ( json.clipping !== undefined ) material.clipping = json.clipping;\n\n\t\t// for PointsMaterial\n\n\t\tif ( json.size !== undefined ) material.size = json.size;\n\t\tif ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation;\n\n\t\t// maps\n\n\t\tif ( json.map !== undefined ) material.map = getTexture( json.map );\n\t\tif ( json.matcap !== undefined ) material.matcap = getTexture( json.matcap );\n\n\t\tif ( json.alphaMap !== undefined ) material.alphaMap = getTexture( json.alphaMap );\n\n\t\tif ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap );\n\t\tif ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale;\n\n\t\tif ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap );\n\t\tif ( json.normalMapType !== undefined ) material.normalMapType = json.normalMapType;\n\t\tif ( json.normalScale !== undefined ) {\n\n\t\t\tlet normalScale = json.normalScale;\n\n\t\t\tif ( Array.isArray( normalScale ) === false ) {\n\n\t\t\t\t// Blender exporter used to export a scalar. See #7459\n\n\t\t\t\tnormalScale = [ normalScale, normalScale ];\n\n\t\t\t}\n\n\t\t\tmaterial.normalScale = new Vector2().fromArray( normalScale );\n\n\t\t}\n\n\t\tif ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap );\n\t\tif ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale;\n\t\tif ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias;\n\n\t\tif ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap );\n\t\tif ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap );\n\n\t\tif ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap );\n\t\tif ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity;\n\n\t\tif ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap );\n\t\tif ( json.specularIntensityMap !== undefined ) material.specularIntensityMap = getTexture( json.specularIntensityMap );\n\t\tif ( json.specularColorMap !== undefined ) material.specularColorMap = getTexture( json.specularColorMap );\n\n\t\tif ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap );\n\t\tif ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity;\n\n\t\tif ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity;\n\t\tif ( json.refractionRatio !== undefined ) material.refractionRatio = json.refractionRatio;\n\n\t\tif ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap );\n\t\tif ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity;\n\n\t\tif ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap );\n\t\tif ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity;\n\n\t\tif ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap );\n\n\t\tif ( json.clearcoatMap !== undefined ) material.clearcoatMap = getTexture( json.clearcoatMap );\n\t\tif ( json.clearcoatRoughnessMap !== undefined ) material.clearcoatRoughnessMap = getTexture( json.clearcoatRoughnessMap );\n\t\tif ( json.clearcoatNormalMap !== undefined ) material.clearcoatNormalMap = getTexture( json.clearcoatNormalMap );\n\t\tif ( json.clearcoatNormalScale !== undefined ) material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale );\n\n\t\tif ( json.iridescenceMap !== undefined ) material.iridescenceMap = getTexture( json.iridescenceMap );\n\t\tif ( json.iridescenceThicknessMap !== undefined ) material.iridescenceThicknessMap = getTexture( json.iridescenceThicknessMap );\n\n\t\tif ( json.transmissionMap !== undefined ) material.transmissionMap = getTexture( json.transmissionMap );\n\t\tif ( json.thicknessMap !== undefined ) material.thicknessMap = getTexture( json.thicknessMap );\n\n\t\tif ( json.anisotropyMap !== undefined ) material.anisotropyMap = getTexture( json.anisotropyMap );\n\n\t\tif ( json.sheenColorMap !== undefined ) material.sheenColorMap = getTexture( json.sheenColorMap );\n\t\tif ( json.sheenRoughnessMap !== undefined ) material.sheenRoughnessMap = getTexture( json.sheenRoughnessMap );\n\n\t\treturn material;\n\n\t}\n\n\tsetTextures( value ) {\n\n\t\tthis.textures = value;\n\t\treturn this;\n\n\t}\n\n\tstatic createMaterialFromType( type ) {\n\n\t\tconst materialLib = {\n\t\t\tShadowMaterial,\n\t\t\tSpriteMaterial,\n\t\t\tRawShaderMaterial,\n\t\t\tShaderMaterial,\n\t\t\tPointsMaterial,\n\t\t\tMeshPhysicalMaterial,\n\t\t\tMeshStandardMaterial,\n\t\t\tMeshPhongMaterial,\n\t\t\tMeshToonMaterial,\n\t\t\tMeshNormalMaterial,\n\t\t\tMeshLambertMaterial,\n\t\t\tMeshDepthMaterial,\n\t\t\tMeshDistanceMaterial,\n\t\t\tMeshBasicMaterial,\n\t\t\tMeshMatcapMaterial,\n\t\t\tLineDashedMaterial,\n\t\t\tLineBasicMaterial,\n\t\t\tMaterial\n\t\t};\n\n\t\treturn new materialLib[ type ]();\n\n\t}\n\n}\n\nclass LoaderUtils {\n\n\tstatic decodeText( array ) {\n\n\t\tif ( typeof TextDecoder !== 'undefined' ) {\n\n\t\t\treturn new TextDecoder().decode( array );\n\n\t\t}\n\n\t\t// Avoid the String.fromCharCode.apply(null, array) shortcut, which\n\t\t// throws a \"maximum call stack size exceeded\" error for large arrays.\n\n\t\tlet s = '';\n\n\t\tfor ( let i = 0, il = array.length; i < il; i ++ ) {\n\n\t\t\t// Implicitly assumes little-endian.\n\t\t\ts += String.fromCharCode( array[ i ] );\n\n\t\t}\n\n\t\ttry {\n\n\t\t\t// merges multi-byte utf-8 characters.\n\n\t\t\treturn decodeURIComponent( escape( s ) );\n\n\t\t} catch ( e ) { // see #16358\n\n\t\t\treturn s;\n\n\t\t}\n\n\t}\n\n\tstatic extractUrlBase( url ) {\n\n\t\tconst index = url.lastIndexOf( '/' );\n\n\t\tif ( index === - 1 ) return './';\n\n\t\treturn url.slice( 0, index + 1 );\n\n\t}\n\n\tstatic resolveURL( url, path ) {\n\n\t\t// Invalid URL\n\t\tif ( typeof url !== 'string' || url === '' ) return '';\n\n\t\t// Host Relative URL\n\t\tif ( /^https?:\\/\\//i.test( path ) && /^\\//.test( url ) ) {\n\n\t\t\tpath = path.replace( /(^https?:\\/\\/[^\\/]+).*/i, '$1' );\n\n\t\t}\n\n\t\t// Absolute URL http://,https://,//\n\t\tif ( /^(https?:)?\\/\\//i.test( url ) ) return url;\n\n\t\t// Data URI\n\t\tif ( /^data:.*,.*$/i.test( url ) ) return url;\n\n\t\t// Blob URL\n\t\tif ( /^blob:.*$/i.test( url ) ) return url;\n\n\t\t// Relative URL\n\t\treturn path + url;\n\n\t}\n\n}\n\nclass InstancedBufferGeometry extends BufferGeometry {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isInstancedBufferGeometry = true;\n\n\t\tthis.type = 'InstancedBufferGeometry';\n\t\tthis.instanceCount = Infinity;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.instanceCount = source.instanceCount;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = super.toJSON();\n\n\t\tdata.instanceCount = this.instanceCount;\n\n\t\tdata.isInstancedBufferGeometry = true;\n\n\t\treturn data;\n\n\t}\n\n}\n\nclass BufferGeometryLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\n\t}\n\n\tload( url, onLoad, onProgress, onError ) {\n\n\t\tconst scope = this;\n\n\t\tconst loader = new FileLoader( scope.manager );\n\t\tloader.setPath( scope.path );\n\t\tloader.setRequestHeader( scope.requestHeader );\n\t\tloader.setWithCredentials( scope.withCredentials );\n\t\tloader.load( url, function ( text ) {\n\n\t\t\ttry {\n\n\t\t\t\tonLoad( scope.parse( JSON.parse( text ) ) );\n\n\t\t\t} catch ( e ) {\n\n\t\t\t\tif ( onError ) {\n\n\t\t\t\t\tonError( e );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconsole.error( e );\n\n\t\t\t\t}\n\n\t\t\t\tscope.manager.itemError( url );\n\n\t\t\t}\n\n\t\t}, onProgress, onError );\n\n\t}\n\n\tparse( json ) {\n\n\t\tconst interleavedBufferMap = {};\n\t\tconst arrayBufferMap = {};\n\n\t\tfunction getInterleavedBuffer( json, uuid ) {\n\n\t\t\tif ( interleavedBufferMap[ uuid ] !== undefined ) return interleavedBufferMap[ uuid ];\n\n\t\t\tconst interleavedBuffers = json.interleavedBuffers;\n\t\t\tconst interleavedBuffer = interleavedBuffers[ uuid ];\n\n\t\t\tconst buffer = getArrayBuffer( json, interleavedBuffer.buffer );\n\n\t\t\tconst array = getTypedArray( interleavedBuffer.type, buffer );\n\t\t\tconst ib = new InterleavedBuffer( array, interleavedBuffer.stride );\n\t\t\tib.uuid = interleavedBuffer.uuid;\n\n\t\t\tinterleavedBufferMap[ uuid ] = ib;\n\n\t\t\treturn ib;\n\n\t\t}\n\n\t\tfunction getArrayBuffer( json, uuid ) {\n\n\t\t\tif ( arrayBufferMap[ uuid ] !== undefined ) return arrayBufferMap[ uuid ];\n\n\t\t\tconst arrayBuffers = json.arrayBuffers;\n\t\t\tconst arrayBuffer = arrayBuffers[ uuid ];\n\n\t\t\tconst ab = new Uint32Array( arrayBuffer ).buffer;\n\n\t\t\tarrayBufferMap[ uuid ] = ab;\n\n\t\t\treturn ab;\n\n\t\t}\n\n\t\tconst geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry();\n\n\t\tconst index = json.data.index;\n\n\t\tif ( index !== undefined ) {\n\n\t\t\tconst typedArray = getTypedArray( index.type, index.array );\n\t\t\tgeometry.setIndex( new BufferAttribute( typedArray, 1 ) );\n\n\t\t}\n\n\t\tconst attributes = json.data.attributes;\n\n\t\tfor ( const key in attributes ) {\n\n\t\t\tconst attribute = attributes[ key ];\n\t\t\tlet bufferAttribute;\n\n\t\t\tif ( attribute.isInterleavedBufferAttribute ) {\n\n\t\t\t\tconst interleavedBuffer = getInterleavedBuffer( json.data, attribute.data );\n\t\t\t\tbufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized );\n\n\t\t\t} else {\n\n\t\t\t\tconst typedArray = getTypedArray( attribute.type, attribute.array );\n\t\t\t\tconst bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute;\n\t\t\t\tbufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized );\n\n\t\t\t}\n\n\t\t\tif ( attribute.name !== undefined ) bufferAttribute.name = attribute.name;\n\t\t\tif ( attribute.usage !== undefined ) bufferAttribute.setUsage( attribute.usage );\n\n\t\t\tgeometry.setAttribute( key, bufferAttribute );\n\n\t\t}\n\n\t\tconst morphAttributes = json.data.morphAttributes;\n\n\t\tif ( morphAttributes ) {\n\n\t\t\tfor ( const key in morphAttributes ) {\n\n\t\t\t\tconst attributeArray = morphAttributes[ key ];\n\n\t\t\t\tconst array = [];\n\n\t\t\t\tfor ( let i = 0, il = attributeArray.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst attribute = attributeArray[ i ];\n\t\t\t\t\tlet bufferAttribute;\n\n\t\t\t\t\tif ( attribute.isInterleavedBufferAttribute ) {\n\n\t\t\t\t\t\tconst interleavedBuffer = getInterleavedBuffer( json.data, attribute.data );\n\t\t\t\t\t\tbufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconst typedArray = getTypedArray( attribute.type, attribute.array );\n\t\t\t\t\t\tbufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( attribute.name !== undefined ) bufferAttribute.name = attribute.name;\n\t\t\t\t\tarray.push( bufferAttribute );\n\n\t\t\t\t}\n\n\t\t\t\tgeometry.morphAttributes[ key ] = array;\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst morphTargetsRelative = json.data.morphTargetsRelative;\n\n\t\tif ( morphTargetsRelative ) {\n\n\t\t\tgeometry.morphTargetsRelative = true;\n\n\t\t}\n\n\t\tconst groups = json.data.groups || json.data.drawcalls || json.data.offsets;\n\n\t\tif ( groups !== undefined ) {\n\n\t\t\tfor ( let i = 0, n = groups.length; i !== n; ++ i ) {\n\n\t\t\t\tconst group = groups[ i ];\n\n\t\t\t\tgeometry.addGroup( group.start, group.count, group.materialIndex );\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst boundingSphere = json.data.boundingSphere;\n\n\t\tif ( boundingSphere !== undefined ) {\n\n\t\t\tconst center = new Vector3();\n\n\t\t\tif ( boundingSphere.center !== undefined ) {\n\n\t\t\t\tcenter.fromArray( boundingSphere.center );\n\n\t\t\t}\n\n\t\t\tgeometry.boundingSphere = new Sphere( center, boundingSphere.radius );\n\n\t\t}\n\n\t\tif ( json.name ) geometry.name = json.name;\n\t\tif ( json.userData ) geometry.userData = json.userData;\n\n\t\treturn geometry;\n\n\t}\n\n}\n\nclass ObjectLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\n\t}\n\n\tload( url, onLoad, onProgress, onError ) {\n\n\t\tconst scope = this;\n\n\t\tconst path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path;\n\t\tthis.resourcePath = this.resourcePath || path;\n\n\t\tconst loader = new FileLoader( this.manager );\n\t\tloader.setPath( this.path );\n\t\tloader.setRequestHeader( this.requestHeader );\n\t\tloader.setWithCredentials( this.withCredentials );\n\t\tloader.load( url, function ( text ) {\n\n\t\t\tlet json = null;\n\n\t\t\ttry {\n\n\t\t\t\tjson = JSON.parse( text );\n\n\t\t\t} catch ( error ) {\n\n\t\t\t\tif ( onError !== undefined ) onError( error );\n\n\t\t\t\tconsole.error( 'THREE:ObjectLoader: Can\\'t parse ' + url + '.', error.message );\n\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tconst metadata = json.metadata;\n\n\t\t\tif ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) {\n\n\t\t\t\tif ( onError !== undefined ) onError( new Error( 'THREE.ObjectLoader: Can\\'t load ' + url ) );\n\n\t\t\t\tconsole.error( 'THREE.ObjectLoader: Can\\'t load ' + url );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tscope.parse( json, onLoad );\n\n\t\t}, onProgress, onError );\n\n\t}\n\n\tasync loadAsync( url, onProgress ) {\n\n\t\tconst scope = this;\n\n\t\tconst path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path;\n\t\tthis.resourcePath = this.resourcePath || path;\n\n\t\tconst loader = new FileLoader( this.manager );\n\t\tloader.setPath( this.path );\n\t\tloader.setRequestHeader( this.requestHeader );\n\t\tloader.setWithCredentials( this.withCredentials );\n\n\t\tconst text = await loader.loadAsync( url, onProgress );\n\n\t\tconst json = JSON.parse( text );\n\n\t\tconst metadata = json.metadata;\n\n\t\tif ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) {\n\n\t\t\tthrow new Error( 'THREE.ObjectLoader: Can\\'t load ' + url );\n\n\t\t}\n\n\t\treturn await scope.parseAsync( json );\n\n\t}\n\n\tparse( json, onLoad ) {\n\n\t\tconst animations = this.parseAnimations( json.animations );\n\t\tconst shapes = this.parseShapes( json.shapes );\n\t\tconst geometries = this.parseGeometries( json.geometries, shapes );\n\n\t\tconst images = this.parseImages( json.images, function () {\n\n\t\t\tif ( onLoad !== undefined ) onLoad( object );\n\n\t\t} );\n\n\t\tconst textures = this.parseTextures( json.textures, images );\n\t\tconst materials = this.parseMaterials( json.materials, textures );\n\n\t\tconst object = this.parseObject( json.object, geometries, materials, textures, animations );\n\t\tconst skeletons = this.parseSkeletons( json.skeletons, object );\n\n\t\tthis.bindSkeletons( object, skeletons );\n\n\t\t//\n\n\t\tif ( onLoad !== undefined ) {\n\n\t\t\tlet hasImages = false;\n\n\t\t\tfor ( const uuid in images ) {\n\n\t\t\t\tif ( images[ uuid ].data instanceof HTMLImageElement ) {\n\n\t\t\t\t\thasImages = true;\n\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( hasImages === false ) onLoad( object );\n\n\t\t}\n\n\t\treturn object;\n\n\t}\n\n\tasync parseAsync( json ) {\n\n\t\tconst animations = this.parseAnimations( json.animations );\n\t\tconst shapes = this.parseShapes( json.shapes );\n\t\tconst geometries = this.parseGeometries( json.geometries, shapes );\n\n\t\tconst images = await this.parseImagesAsync( json.images );\n\n\t\tconst textures = this.parseTextures( json.textures, images );\n\t\tconst materials = this.parseMaterials( json.materials, textures );\n\n\t\tconst object = this.parseObject( json.object, geometries, materials, textures, animations );\n\t\tconst skeletons = this.parseSkeletons( json.skeletons, object );\n\n\t\tthis.bindSkeletons( object, skeletons );\n\n\t\treturn object;\n\n\t}\n\n\tparseShapes( json ) {\n\n\t\tconst shapes = {};\n\n\t\tif ( json !== undefined ) {\n\n\t\t\tfor ( let i = 0, l = json.length; i < l; i ++ ) {\n\n\t\t\t\tconst shape = new Shape().fromJSON( json[ i ] );\n\n\t\t\t\tshapes[ shape.uuid ] = shape;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn shapes;\n\n\t}\n\n\tparseSkeletons( json, object ) {\n\n\t\tconst skeletons = {};\n\t\tconst bones = {};\n\n\t\t// generate bone lookup table\n\n\t\tobject.traverse( function ( child ) {\n\n\t\t\tif ( child.isBone ) bones[ child.uuid ] = child;\n\n\t\t} );\n\n\t\t// create skeletons\n\n\t\tif ( json !== undefined ) {\n\n\t\t\tfor ( let i = 0, l = json.length; i < l; i ++ ) {\n\n\t\t\t\tconst skeleton = new Skeleton().fromJSON( json[ i ], bones );\n\n\t\t\t\tskeletons[ skeleton.uuid ] = skeleton;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn skeletons;\n\n\t}\n\n\tparseGeometries( json, shapes ) {\n\n\t\tconst geometries = {};\n\n\t\tif ( json !== undefined ) {\n\n\t\t\tconst bufferGeometryLoader = new BufferGeometryLoader();\n\n\t\t\tfor ( let i = 0, l = json.length; i < l; i ++ ) {\n\n\t\t\t\tlet geometry;\n\t\t\t\tconst data = json[ i ];\n\n\t\t\t\tswitch ( data.type ) {\n\n\t\t\t\t\tcase 'BufferGeometry':\n\t\t\t\t\tcase 'InstancedBufferGeometry':\n\n\t\t\t\t\t\tgeometry = bufferGeometryLoader.parse( data );\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\n\t\t\t\t\t\tif ( data.type in Geometries ) {\n\n\t\t\t\t\t\t\tgeometry = Geometries[ data.type ].fromJSON( data, shapes );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tconsole.warn( `THREE.ObjectLoader: Unsupported geometry type \"${ data.type }\"` );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tgeometry.uuid = data.uuid;\n\n\t\t\t\tif ( data.name !== undefined ) geometry.name = data.name;\n\t\t\t\tif ( data.userData !== undefined ) geometry.userData = data.userData;\n\n\t\t\t\tgeometries[ data.uuid ] = geometry;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn geometries;\n\n\t}\n\n\tparseMaterials( json, textures ) {\n\n\t\tconst cache = {}; // MultiMaterial\n\t\tconst materials = {};\n\n\t\tif ( json !== undefined ) {\n\n\t\t\tconst loader = new MaterialLoader();\n\t\t\tloader.setTextures( textures );\n\n\t\t\tfor ( let i = 0, l = json.length; i < l; i ++ ) {\n\n\t\t\t\tconst data = json[ i ];\n\n\t\t\t\tif ( cache[ data.uuid ] === undefined ) {\n\n\t\t\t\t\tcache[ data.uuid ] = loader.parse( data );\n\n\t\t\t\t}\n\n\t\t\t\tmaterials[ data.uuid ] = cache[ data.uuid ];\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn materials;\n\n\t}\n\n\tparseAnimations( json ) {\n\n\t\tconst animations = {};\n\n\t\tif ( json !== undefined ) {\n\n\t\t\tfor ( let i = 0; i < json.length; i ++ ) {\n\n\t\t\t\tconst data = json[ i ];\n\n\t\t\t\tconst clip = AnimationClip.parse( data );\n\n\t\t\t\tanimations[ clip.uuid ] = clip;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn animations;\n\n\t}\n\n\tparseImages( json, onLoad ) {\n\n\t\tconst scope = this;\n\t\tconst images = {};\n\n\t\tlet loader;\n\n\t\tfunction loadImage( url ) {\n\n\t\t\tscope.manager.itemStart( url );\n\n\t\t\treturn loader.load( url, function () {\n\n\t\t\t\tscope.manager.itemEnd( url );\n\n\t\t\t}, undefined, function () {\n\n\t\t\t\tscope.manager.itemError( url );\n\t\t\t\tscope.manager.itemEnd( url );\n\n\t\t\t} );\n\n\t\t}\n\n\t\tfunction deserializeImage( image ) {\n\n\t\t\tif ( typeof image === 'string' ) {\n\n\t\t\t\tconst url = image;\n\n\t\t\t\tconst path = /^(\\/\\/)|([a-z]+:(\\/\\/)?)/i.test( url ) ? url : scope.resourcePath + url;\n\n\t\t\t\treturn loadImage( path );\n\n\t\t\t} else {\n\n\t\t\t\tif ( image.data ) {\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tdata: getTypedArray( image.type, image.data ),\n\t\t\t\t\t\twidth: image.width,\n\t\t\t\t\t\theight: image.height\n\t\t\t\t\t};\n\n\t\t\t\t} else {\n\n\t\t\t\t\treturn null;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( json !== undefined && json.length > 0 ) {\n\n\t\t\tconst manager = new LoadingManager( onLoad );\n\n\t\t\tloader = new ImageLoader( manager );\n\t\t\tloader.setCrossOrigin( this.crossOrigin );\n\n\t\t\tfor ( let i = 0, il = json.length; i < il; i ++ ) {\n\n\t\t\t\tconst image = json[ i ];\n\t\t\t\tconst url = image.url;\n\n\t\t\t\tif ( Array.isArray( url ) ) {\n\n\t\t\t\t\t// load array of images e.g CubeTexture\n\n\t\t\t\t\tconst imageArray = [];\n\n\t\t\t\t\tfor ( let j = 0, jl = url.length; j < jl; j ++ ) {\n\n\t\t\t\t\t\tconst currentUrl = url[ j ];\n\n\t\t\t\t\t\tconst deserializedImage = deserializeImage( currentUrl );\n\n\t\t\t\t\t\tif ( deserializedImage !== null ) {\n\n\t\t\t\t\t\t\tif ( deserializedImage instanceof HTMLImageElement ) {\n\n\t\t\t\t\t\t\t\timageArray.push( deserializedImage );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t// special case: handle array of data textures for cube textures\n\n\t\t\t\t\t\t\t\timageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height ) );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\timages[ image.uuid ] = new Source( imageArray );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// load single image\n\n\t\t\t\t\tconst deserializedImage = deserializeImage( image.url );\n\t\t\t\t\timages[ image.uuid ] = new Source( deserializedImage );\n\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn images;\n\n\t}\n\n\tasync parseImagesAsync( json ) {\n\n\t\tconst scope = this;\n\t\tconst images = {};\n\n\t\tlet loader;\n\n\t\tasync function deserializeImage( image ) {\n\n\t\t\tif ( typeof image === 'string' ) {\n\n\t\t\t\tconst url = image;\n\n\t\t\t\tconst path = /^(\\/\\/)|([a-z]+:(\\/\\/)?)/i.test( url ) ? url : scope.resourcePath + url;\n\n\t\t\t\treturn await loader.loadAsync( path );\n\n\t\t\t} else {\n\n\t\t\t\tif ( image.data ) {\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tdata: getTypedArray( image.type, image.data ),\n\t\t\t\t\t\twidth: image.width,\n\t\t\t\t\t\theight: image.height\n\t\t\t\t\t};\n\n\t\t\t\t} else {\n\n\t\t\t\t\treturn null;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( json !== undefined && json.length > 0 ) {\n\n\t\t\tloader = new ImageLoader( this.manager );\n\t\t\tloader.setCrossOrigin( this.crossOrigin );\n\n\t\t\tfor ( let i = 0, il = json.length; i < il; i ++ ) {\n\n\t\t\t\tconst image = json[ i ];\n\t\t\t\tconst url = image.url;\n\n\t\t\t\tif ( Array.isArray( url ) ) {\n\n\t\t\t\t\t// load array of images e.g CubeTexture\n\n\t\t\t\t\tconst imageArray = [];\n\n\t\t\t\t\tfor ( let j = 0, jl = url.length; j < jl; j ++ ) {\n\n\t\t\t\t\t\tconst currentUrl = url[ j ];\n\n\t\t\t\t\t\tconst deserializedImage = await deserializeImage( currentUrl );\n\n\t\t\t\t\t\tif ( deserializedImage !== null ) {\n\n\t\t\t\t\t\t\tif ( deserializedImage instanceof HTMLImageElement ) {\n\n\t\t\t\t\t\t\t\timageArray.push( deserializedImage );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t// special case: handle array of data textures for cube textures\n\n\t\t\t\t\t\t\t\timageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height ) );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\timages[ image.uuid ] = new Source( imageArray );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// load single image\n\n\t\t\t\t\tconst deserializedImage = await deserializeImage( image.url );\n\t\t\t\t\timages[ image.uuid ] = new Source( deserializedImage );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn images;\n\n\t}\n\n\tparseTextures( json, images ) {\n\n\t\tfunction parseConstant( value, type ) {\n\n\t\t\tif ( typeof value === 'number' ) return value;\n\n\t\t\tconsole.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value );\n\n\t\t\treturn type[ value ];\n\n\t\t}\n\n\t\tconst textures = {};\n\n\t\tif ( json !== undefined ) {\n\n\t\t\tfor ( let i = 0, l = json.length; i < l; i ++ ) {\n\n\t\t\t\tconst data = json[ i ];\n\n\t\t\t\tif ( data.image === undefined ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.ObjectLoader: No \"image\" specified for', data.uuid );\n\n\t\t\t\t}\n\n\t\t\t\tif ( images[ data.image ] === undefined ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.ObjectLoader: Undefined image', data.image );\n\n\t\t\t\t}\n\n\t\t\t\tconst source = images[ data.image ];\n\t\t\t\tconst image = source.data;\n\n\t\t\t\tlet texture;\n\n\t\t\t\tif ( Array.isArray( image ) ) {\n\n\t\t\t\t\ttexture = new CubeTexture();\n\n\t\t\t\t\tif ( image.length === 6 ) texture.needsUpdate = true;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( image && image.data ) {\n\n\t\t\t\t\t\ttexture = new DataTexture();\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\ttexture = new Texture();\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( image ) texture.needsUpdate = true; // textures can have undefined image data\n\n\t\t\t\t}\n\n\t\t\t\ttexture.source = source;\n\n\t\t\t\ttexture.uuid = data.uuid;\n\n\t\t\t\tif ( data.name !== undefined ) texture.name = data.name;\n\n\t\t\t\tif ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING );\n\t\t\t\tif ( data.channel !== undefined ) texture.channel = data.channel;\n\n\t\t\t\tif ( data.offset !== undefined ) texture.offset.fromArray( data.offset );\n\t\t\t\tif ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat );\n\t\t\t\tif ( data.center !== undefined ) texture.center.fromArray( data.center );\n\t\t\t\tif ( data.rotation !== undefined ) texture.rotation = data.rotation;\n\n\t\t\t\tif ( data.wrap !== undefined ) {\n\n\t\t\t\t\ttexture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING );\n\t\t\t\t\ttexture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING );\n\n\t\t\t\t}\n\n\t\t\t\tif ( data.format !== undefined ) texture.format = data.format;\n\t\t\t\tif ( data.internalFormat !== undefined ) texture.internalFormat = data.internalFormat;\n\t\t\t\tif ( data.type !== undefined ) texture.type = data.type;\n\t\t\t\tif ( data.colorSpace !== undefined ) texture.colorSpace = data.colorSpace;\n\t\t\t\tif ( data.encoding !== undefined ) texture.encoding = data.encoding; // @deprecated, r152\n\n\t\t\t\tif ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER );\n\t\t\t\tif ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER );\n\t\t\t\tif ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy;\n\n\t\t\t\tif ( data.flipY !== undefined ) texture.flipY = data.flipY;\n\n\t\t\t\tif ( data.generateMipmaps !== undefined ) texture.generateMipmaps = data.generateMipmaps;\n\t\t\t\tif ( data.premultiplyAlpha !== undefined ) texture.premultiplyAlpha = data.premultiplyAlpha;\n\t\t\t\tif ( data.unpackAlignment !== undefined ) texture.unpackAlignment = data.unpackAlignment;\n\t\t\t\tif ( data.compareFunction !== undefined ) texture.compareFunction = data.compareFunction;\n\n\t\t\t\tif ( data.userData !== undefined ) texture.userData = data.userData;\n\n\t\t\t\ttextures[ data.uuid ] = texture;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn textures;\n\n\t}\n\n\tparseObject( data, geometries, materials, textures, animations ) {\n\n\t\tlet object;\n\n\t\tfunction getGeometry( name ) {\n\n\t\t\tif ( geometries[ name ] === undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.ObjectLoader: Undefined geometry', name );\n\n\t\t\t}\n\n\t\t\treturn geometries[ name ];\n\n\t\t}\n\n\t\tfunction getMaterial( name ) {\n\n\t\t\tif ( name === undefined ) return undefined;\n\n\t\t\tif ( Array.isArray( name ) ) {\n\n\t\t\t\tconst array = [];\n\n\t\t\t\tfor ( let i = 0, l = name.length; i < l; i ++ ) {\n\n\t\t\t\t\tconst uuid = name[ i ];\n\n\t\t\t\t\tif ( materials[ uuid ] === undefined ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.ObjectLoader: Undefined material', uuid );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tarray.push( materials[ uuid ] );\n\n\t\t\t\t}\n\n\t\t\t\treturn array;\n\n\t\t\t}\n\n\t\t\tif ( materials[ name ] === undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.ObjectLoader: Undefined material', name );\n\n\t\t\t}\n\n\t\t\treturn materials[ name ];\n\n\t\t}\n\n\t\tfunction getTexture( uuid ) {\n\n\t\t\tif ( textures[ uuid ] === undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.ObjectLoader: Undefined texture', uuid );\n\n\t\t\t}\n\n\t\t\treturn textures[ uuid ];\n\n\t\t}\n\n\t\tlet geometry, material;\n\n\t\tswitch ( data.type ) {\n\n\t\t\tcase 'Scene':\n\n\t\t\t\tobject = new Scene();\n\n\t\t\t\tif ( data.background !== undefined ) {\n\n\t\t\t\t\tif ( Number.isInteger( data.background ) ) {\n\n\t\t\t\t\t\tobject.background = new Color( data.background );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tobject.background = getTexture( data.background );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( data.environment !== undefined ) {\n\n\t\t\t\t\tobject.environment = getTexture( data.environment );\n\n\t\t\t\t}\n\n\t\t\t\tif ( data.fog !== undefined ) {\n\n\t\t\t\t\tif ( data.fog.type === 'Fog' ) {\n\n\t\t\t\t\t\tobject.fog = new Fog( data.fog.color, data.fog.near, data.fog.far );\n\n\t\t\t\t\t} else if ( data.fog.type === 'FogExp2' ) {\n\n\t\t\t\t\t\tobject.fog = new FogExp2( data.fog.color, data.fog.density );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( data.fog.name !== '' ) {\n\n\t\t\t\t\t\tobject.fog.name = data.fog.name;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( data.backgroundBlurriness !== undefined ) object.backgroundBlurriness = data.backgroundBlurriness;\n\t\t\t\tif ( data.backgroundIntensity !== undefined ) object.backgroundIntensity = data.backgroundIntensity;\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'PerspectiveCamera':\n\n\t\t\t\tobject = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far );\n\n\t\t\t\tif ( data.focus !== undefined ) object.focus = data.focus;\n\t\t\t\tif ( data.zoom !== undefined ) object.zoom = data.zoom;\n\t\t\t\tif ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge;\n\t\t\t\tif ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset;\n\t\t\t\tif ( data.view !== undefined ) object.view = Object.assign( {}, data.view );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'OrthographicCamera':\n\n\t\t\t\tobject = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far );\n\n\t\t\t\tif ( data.zoom !== undefined ) object.zoom = data.zoom;\n\t\t\t\tif ( data.view !== undefined ) object.view = Object.assign( {}, data.view );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'AmbientLight':\n\n\t\t\t\tobject = new AmbientLight( data.color, data.intensity );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'DirectionalLight':\n\n\t\t\t\tobject = new DirectionalLight( data.color, data.intensity );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'PointLight':\n\n\t\t\t\tobject = new PointLight( data.color, data.intensity, data.distance, data.decay );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'RectAreaLight':\n\n\t\t\t\tobject = new RectAreaLight( data.color, data.intensity, data.width, data.height );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'SpotLight':\n\n\t\t\t\tobject = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'HemisphereLight':\n\n\t\t\t\tobject = new HemisphereLight( data.color, data.groundColor, data.intensity );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'LightProbe':\n\n\t\t\t\tobject = new LightProbe().fromJSON( data );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'SkinnedMesh':\n\n\t\t\t\tgeometry = getGeometry( data.geometry );\n\t\t\t \tmaterial = getMaterial( data.material );\n\n\t\t\t\tobject = new SkinnedMesh( geometry, material );\n\n\t\t\t\tif ( data.bindMode !== undefined ) object.bindMode = data.bindMode;\n\t\t\t\tif ( data.bindMatrix !== undefined ) object.bindMatrix.fromArray( data.bindMatrix );\n\t\t\t\tif ( data.skeleton !== undefined ) object.skeleton = data.skeleton;\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'Mesh':\n\n\t\t\t\tgeometry = getGeometry( data.geometry );\n\t\t\t\tmaterial = getMaterial( data.material );\n\n\t\t\t\tobject = new Mesh( geometry, material );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'InstancedMesh':\n\n\t\t\t\tgeometry = getGeometry( data.geometry );\n\t\t\t\tmaterial = getMaterial( data.material );\n\t\t\t\tconst count = data.count;\n\t\t\t\tconst instanceMatrix = data.instanceMatrix;\n\t\t\t\tconst instanceColor = data.instanceColor;\n\n\t\t\t\tobject = new InstancedMesh( geometry, material, count );\n\t\t\t\tobject.instanceMatrix = new InstancedBufferAttribute( new Float32Array( instanceMatrix.array ), 16 );\n\t\t\t\tif ( instanceColor !== undefined ) object.instanceColor = new InstancedBufferAttribute( new Float32Array( instanceColor.array ), instanceColor.itemSize );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'BatchedMesh':\n\n\t\t\t\tgeometry = getGeometry( data.geometry );\n\t\t\t\tmaterial = getMaterial( data.material );\n\n\t\t\t\tobject = new BatchedMesh( data.maxGeometryCount, data.maxVertexCount, data.maxIndexCount, material );\n\t\t\t\tobject.geometry = geometry;\n\t\t\t\tobject.perObjectFrustumCulled = data.perObjectFrustumCulled;\n\t\t\t\tobject.sortObjects = data.sortObjects;\n\n\t\t\t\tobject._drawRanges = data.drawRanges;\n\t\t\t\tobject._reservedRanges = data.reservedRanges;\n\n\t\t\t\tobject._visibility = data.visibility;\n\t\t\t\tobject._active = data.active;\n\t\t\t\tobject._bounds = data.bounds.map( bound => {\n\n\t\t\t\t\tconst box = new Box3();\n\t\t\t\t\tbox.min.fromArray( bound.boxMin );\n\t\t\t\t\tbox.max.fromArray( bound.boxMax );\n\n\t\t\t\t\tconst sphere = new Sphere();\n\t\t\t\t\tsphere.radius = bound.sphereRadius;\n\t\t\t\t\tsphere.center.fromArray( bound.sphereCenter );\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tboxInitialized: bound.boxInitialized,\n\t\t\t\t\t\tbox: box,\n\n\t\t\t\t\t\tsphereInitialized: bound.sphereInitialized,\n\t\t\t\t\t\tsphere: sphere\n\t\t\t\t\t};\n\n\t\t\t\t} );\n\n\t\t\t\tobject._maxGeometryCount = data.maxGeometryCount;\n\t\t\t\tobject._maxVertexCount = data.maxVertexCount;\n\t\t\t\tobject._maxIndexCount = data.maxIndexCount;\n\n\t\t\t\tobject._geometryInitialized = data.geometryInitialized;\n\t\t\t\tobject._geometryCount = data.geometryCount;\n\n\t\t\t\tobject._matricesTexture = getTexture( data.matricesTexture.uuid );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'LOD':\n\n\t\t\t\tobject = new LOD();\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'Line':\n\n\t\t\t\tobject = new Line( getGeometry( data.geometry ), getMaterial( data.material ) );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'LineLoop':\n\n\t\t\t\tobject = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'LineSegments':\n\n\t\t\t\tobject = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'PointCloud':\n\t\t\tcase 'Points':\n\n\t\t\t\tobject = new Points( getGeometry( data.geometry ), getMaterial( data.material ) );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'Sprite':\n\n\t\t\t\tobject = new Sprite( getMaterial( data.material ) );\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'Group':\n\n\t\t\t\tobject = new Group();\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'Bone':\n\n\t\t\t\tobject = new Bone();\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\n\t\t\t\tobject = new Object3D();\n\n\t\t}\n\n\t\tobject.uuid = data.uuid;\n\n\t\tif ( data.name !== undefined ) object.name = data.name;\n\n\t\tif ( data.matrix !== undefined ) {\n\n\t\t\tobject.matrix.fromArray( data.matrix );\n\n\t\t\tif ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate;\n\t\t\tif ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale );\n\n\t\t} else {\n\n\t\t\tif ( data.position !== undefined ) object.position.fromArray( data.position );\n\t\t\tif ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation );\n\t\t\tif ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion );\n\t\t\tif ( data.scale !== undefined ) object.scale.fromArray( data.scale );\n\n\t\t}\n\n\t\tif ( data.up !== undefined ) object.up.fromArray( data.up );\n\n\t\tif ( data.castShadow !== undefined ) object.castShadow = data.castShadow;\n\t\tif ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow;\n\n\t\tif ( data.shadow ) {\n\n\t\t\tif ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias;\n\t\t\tif ( data.shadow.normalBias !== undefined ) object.shadow.normalBias = data.shadow.normalBias;\n\t\t\tif ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius;\n\t\t\tif ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize );\n\t\t\tif ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera );\n\n\t\t}\n\n\t\tif ( data.visible !== undefined ) object.visible = data.visible;\n\t\tif ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled;\n\t\tif ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder;\n\t\tif ( data.userData !== undefined ) object.userData = data.userData;\n\t\tif ( data.layers !== undefined ) object.layers.mask = data.layers;\n\n\t\tif ( data.children !== undefined ) {\n\n\t\t\tconst children = data.children;\n\n\t\t\tfor ( let i = 0; i < children.length; i ++ ) {\n\n\t\t\t\tobject.add( this.parseObject( children[ i ], geometries, materials, textures, animations ) );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( data.animations !== undefined ) {\n\n\t\t\tconst objectAnimations = data.animations;\n\n\t\t\tfor ( let i = 0; i < objectAnimations.length; i ++ ) {\n\n\t\t\t\tconst uuid = objectAnimations[ i ];\n\n\t\t\t\tobject.animations.push( animations[ uuid ] );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( data.type === 'LOD' ) {\n\n\t\t\tif ( data.autoUpdate !== undefined ) object.autoUpdate = data.autoUpdate;\n\n\t\t\tconst levels = data.levels;\n\n\t\t\tfor ( let l = 0; l < levels.length; l ++ ) {\n\n\t\t\t\tconst level = levels[ l ];\n\t\t\t\tconst child = object.getObjectByProperty( 'uuid', level.object );\n\n\t\t\t\tif ( child !== undefined ) {\n\n\t\t\t\t\tobject.addLevel( child, level.distance, level.hysteresis );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn object;\n\n\t}\n\n\tbindSkeletons( object, skeletons ) {\n\n\t\tif ( Object.keys( skeletons ).length === 0 ) return;\n\n\t\tobject.traverse( function ( child ) {\n\n\t\t\tif ( child.isSkinnedMesh === true && child.skeleton !== undefined ) {\n\n\t\t\t\tconst skeleton = skeletons[ child.skeleton ];\n\n\t\t\t\tif ( skeleton === undefined ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.ObjectLoader: No skeleton found with UUID:', child.skeleton );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tchild.bind( skeleton, child.bindMatrix );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} );\n\n\t}\n\n}\n\nconst TEXTURE_MAPPING = {\n\tUVMapping: UVMapping,\n\tCubeReflectionMapping: CubeReflectionMapping,\n\tCubeRefractionMapping: CubeRefractionMapping,\n\tEquirectangularReflectionMapping: EquirectangularReflectionMapping,\n\tEquirectangularRefractionMapping: EquirectangularRefractionMapping,\n\tCubeUVReflectionMapping: CubeUVReflectionMapping\n};\n\nconst TEXTURE_WRAPPING = {\n\tRepeatWrapping: RepeatWrapping,\n\tClampToEdgeWrapping: ClampToEdgeWrapping,\n\tMirroredRepeatWrapping: MirroredRepeatWrapping\n};\n\nconst TEXTURE_FILTER = {\n\tNearestFilter: NearestFilter,\n\tNearestMipmapNearestFilter: NearestMipmapNearestFilter,\n\tNearestMipmapLinearFilter: NearestMipmapLinearFilter,\n\tLinearFilter: LinearFilter,\n\tLinearMipmapNearestFilter: LinearMipmapNearestFilter,\n\tLinearMipmapLinearFilter: LinearMipmapLinearFilter\n};\n\nclass ImageBitmapLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\n\t\tthis.isImageBitmapLoader = true;\n\n\t\tif ( typeof createImageBitmap === 'undefined' ) {\n\n\t\t\tconsole.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' );\n\n\t\t}\n\n\t\tif ( typeof fetch === 'undefined' ) {\n\n\t\t\tconsole.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' );\n\n\t\t}\n\n\t\tthis.options = { premultiplyAlpha: 'none' };\n\n\t}\n\n\tsetOptions( options ) {\n\n\t\tthis.options = options;\n\n\t\treturn this;\n\n\t}\n\n\tload( url, onLoad, onProgress, onError ) {\n\n\t\tif ( url === undefined ) url = '';\n\n\t\tif ( this.path !== undefined ) url = this.path + url;\n\n\t\turl = this.manager.resolveURL( url );\n\n\t\tconst scope = this;\n\n\t\tconst cached = Cache.get( url );\n\n\t\tif ( cached !== undefined ) {\n\n\t\t\tscope.manager.itemStart( url );\n\n\t\t\t// If cached is a promise, wait for it to resolve\n\t\t\tif ( cached.then ) {\n\n\t\t\t\tcached.then( imageBitmap => {\n\n\t\t\t\t\tif ( onLoad ) onLoad( imageBitmap );\n\n\t\t\t\t\tscope.manager.itemEnd( url );\n\n\t\t\t\t} ).catch( e => {\n\n\t\t\t\t\tif ( onError ) onError( e );\n\n\t\t\t\t} );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\t// If cached is not a promise (i.e., it's already an imageBitmap)\n\t\t\tsetTimeout( function () {\n\n\t\t\t\tif ( onLoad ) onLoad( cached );\n\n\t\t\t\tscope.manager.itemEnd( url );\n\n\t\t\t}, 0 );\n\n\t\t\treturn cached;\n\n\t\t}\n\n\t\tconst fetchOptions = {};\n\t\tfetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include';\n\t\tfetchOptions.headers = this.requestHeader;\n\n\t\tconst promise = fetch( url, fetchOptions ).then( function ( res ) {\n\n\t\t\treturn res.blob();\n\n\t\t} ).then( function ( blob ) {\n\n\t\t\treturn createImageBitmap( blob, Object.assign( scope.options, { colorSpaceConversion: 'none' } ) );\n\n\t\t} ).then( function ( imageBitmap ) {\n\n\t\t\tCache.add( url, imageBitmap );\n\n\t\t\tif ( onLoad ) onLoad( imageBitmap );\n\n\t\t\tscope.manager.itemEnd( url );\n\n\t\t\treturn imageBitmap;\n\n\t\t} ).catch( function ( e ) {\n\n\t\t\tif ( onError ) onError( e );\n\n\t\t\tCache.remove( url );\n\n\t\t\tscope.manager.itemError( url );\n\t\t\tscope.manager.itemEnd( url );\n\n\t\t} );\n\n\t\tCache.add( url, promise );\n\t\tscope.manager.itemStart( url );\n\n\t}\n\n}\n\nlet _context;\n\nclass AudioContext {\n\n\tstatic getContext() {\n\n\t\tif ( _context === undefined ) {\n\n\t\t\t_context = new ( window.AudioContext || window.webkitAudioContext )();\n\n\t\t}\n\n\t\treturn _context;\n\n\t}\n\n\tstatic setContext( value ) {\n\n\t\t_context = value;\n\n\t}\n\n}\n\nclass AudioLoader extends Loader {\n\n\tconstructor( manager ) {\n\n\t\tsuper( manager );\n\n\t}\n\n\tload( url, onLoad, onProgress, onError ) {\n\n\t\tconst scope = this;\n\n\t\tconst loader = new FileLoader( this.manager );\n\t\tloader.setResponseType( 'arraybuffer' );\n\t\tloader.setPath( this.path );\n\t\tloader.setRequestHeader( this.requestHeader );\n\t\tloader.setWithCredentials( this.withCredentials );\n\t\tloader.load( url, function ( buffer ) {\n\n\t\t\ttry {\n\n\t\t\t\t// Create a copy of the buffer. The `decodeAudioData` method\n\t\t\t\t// detaches the buffer when complete, preventing reuse.\n\t\t\t\tconst bufferCopy = buffer.slice( 0 );\n\n\t\t\t\tconst context = AudioContext.getContext();\n\t\t\t\tcontext.decodeAudioData( bufferCopy, function ( audioBuffer ) {\n\n\t\t\t\t\tonLoad( audioBuffer );\n\n\t\t\t\t} ).catch( handleError );\n\n\t\t\t} catch ( e ) {\n\n\t\t\t\thandleError( e );\n\n\t\t\t}\n\n\t\t}, onProgress, onError );\n\n\t\tfunction handleError( e ) {\n\n\t\t\tif ( onError ) {\n\n\t\t\t\tonError( e );\n\n\t\t\t} else {\n\n\t\t\t\tconsole.error( e );\n\n\t\t\t}\n\n\t\t\tscope.manager.itemError( url );\n\n\t\t}\n\n\t}\n\n}\n\nconst _eyeRight = /*@__PURE__*/ new Matrix4();\nconst _eyeLeft = /*@__PURE__*/ new Matrix4();\nconst _projectionMatrix = /*@__PURE__*/ new Matrix4();\n\nclass StereoCamera {\n\n\tconstructor() {\n\n\t\tthis.type = 'StereoCamera';\n\n\t\tthis.aspect = 1;\n\n\t\tthis.eyeSep = 0.064;\n\n\t\tthis.cameraL = new PerspectiveCamera();\n\t\tthis.cameraL.layers.enable( 1 );\n\t\tthis.cameraL.matrixAutoUpdate = false;\n\n\t\tthis.cameraR = new PerspectiveCamera();\n\t\tthis.cameraR.layers.enable( 2 );\n\t\tthis.cameraR.matrixAutoUpdate = false;\n\n\t\tthis._cache = {\n\t\t\tfocus: null,\n\t\t\tfov: null,\n\t\t\taspect: null,\n\t\t\tnear: null,\n\t\t\tfar: null,\n\t\t\tzoom: null,\n\t\t\teyeSep: null\n\t\t};\n\n\t}\n\n\tupdate( camera ) {\n\n\t\tconst cache = this._cache;\n\n\t\tconst needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov ||\n\t\t\tcache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near ||\n\t\t\tcache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep;\n\n\t\tif ( needsUpdate ) {\n\n\t\t\tcache.focus = camera.focus;\n\t\t\tcache.fov = camera.fov;\n\t\t\tcache.aspect = camera.aspect * this.aspect;\n\t\t\tcache.near = camera.near;\n\t\t\tcache.far = camera.far;\n\t\t\tcache.zoom = camera.zoom;\n\t\t\tcache.eyeSep = this.eyeSep;\n\n\t\t\t// Off-axis stereoscopic effect based on\n\t\t\t// http://paulbourke.net/stereographics/stereorender/\n\n\t\t\t_projectionMatrix.copy( camera.projectionMatrix );\n\t\t\tconst eyeSepHalf = cache.eyeSep / 2;\n\t\t\tconst eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus;\n\t\t\tconst ymax = ( cache.near * Math.tan( DEG2RAD * cache.fov * 0.5 ) ) / cache.zoom;\n\t\t\tlet xmin, xmax;\n\n\t\t\t// translate xOffset\n\n\t\t\t_eyeLeft.elements[ 12 ] = - eyeSepHalf;\n\t\t\t_eyeRight.elements[ 12 ] = eyeSepHalf;\n\n\t\t\t// for left eye\n\n\t\t\txmin = - ymax * cache.aspect + eyeSepOnProjection;\n\t\t\txmax = ymax * cache.aspect + eyeSepOnProjection;\n\n\t\t\t_projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin );\n\t\t\t_projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );\n\n\t\t\tthis.cameraL.projectionMatrix.copy( _projectionMatrix );\n\n\t\t\t// for right eye\n\n\t\t\txmin = - ymax * cache.aspect - eyeSepOnProjection;\n\t\t\txmax = ymax * cache.aspect - eyeSepOnProjection;\n\n\t\t\t_projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin );\n\t\t\t_projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );\n\n\t\t\tthis.cameraR.projectionMatrix.copy( _projectionMatrix );\n\n\t\t}\n\n\t\tthis.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeLeft );\n\t\tthis.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeRight );\n\n\t}\n\n}\n\nclass Clock {\n\n\tconstructor( autoStart = true ) {\n\n\t\tthis.autoStart = autoStart;\n\n\t\tthis.startTime = 0;\n\t\tthis.oldTime = 0;\n\t\tthis.elapsedTime = 0;\n\n\t\tthis.running = false;\n\n\t}\n\n\tstart() {\n\n\t\tthis.startTime = now();\n\n\t\tthis.oldTime = this.startTime;\n\t\tthis.elapsedTime = 0;\n\t\tthis.running = true;\n\n\t}\n\n\tstop() {\n\n\t\tthis.getElapsedTime();\n\t\tthis.running = false;\n\t\tthis.autoStart = false;\n\n\t}\n\n\tgetElapsedTime() {\n\n\t\tthis.getDelta();\n\t\treturn this.elapsedTime;\n\n\t}\n\n\tgetDelta() {\n\n\t\tlet diff = 0;\n\n\t\tif ( this.autoStart && ! this.running ) {\n\n\t\t\tthis.start();\n\t\t\treturn 0;\n\n\t\t}\n\n\t\tif ( this.running ) {\n\n\t\t\tconst newTime = now();\n\n\t\t\tdiff = ( newTime - this.oldTime ) / 1000;\n\t\t\tthis.oldTime = newTime;\n\n\t\t\tthis.elapsedTime += diff;\n\n\t\t}\n\n\t\treturn diff;\n\n\t}\n\n}\n\nfunction now() {\n\n\treturn ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732\n\n}\n\nconst _position$1 = /*@__PURE__*/ new Vector3();\nconst _quaternion$1 = /*@__PURE__*/ new Quaternion();\nconst _scale$1 = /*@__PURE__*/ new Vector3();\nconst _orientation$1 = /*@__PURE__*/ new Vector3();\n\nclass AudioListener extends Object3D {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.type = 'AudioListener';\n\n\t\tthis.context = AudioContext.getContext();\n\n\t\tthis.gain = this.context.createGain();\n\t\tthis.gain.connect( this.context.destination );\n\n\t\tthis.filter = null;\n\n\t\tthis.timeDelta = 0;\n\n\t\t// private\n\n\t\tthis._clock = new Clock();\n\n\t}\n\n\tgetInput() {\n\n\t\treturn this.gain;\n\n\t}\n\n\tremoveFilter() {\n\n\t\tif ( this.filter !== null ) {\n\n\t\t\tthis.gain.disconnect( this.filter );\n\t\t\tthis.filter.disconnect( this.context.destination );\n\t\t\tthis.gain.connect( this.context.destination );\n\t\t\tthis.filter = null;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tgetFilter() {\n\n\t\treturn this.filter;\n\n\t}\n\n\tsetFilter( value ) {\n\n\t\tif ( this.filter !== null ) {\n\n\t\t\tthis.gain.disconnect( this.filter );\n\t\t\tthis.filter.disconnect( this.context.destination );\n\n\t\t} else {\n\n\t\t\tthis.gain.disconnect( this.context.destination );\n\n\t\t}\n\n\t\tthis.filter = value;\n\t\tthis.gain.connect( this.filter );\n\t\tthis.filter.connect( this.context.destination );\n\n\t\treturn this;\n\n\t}\n\n\tgetMasterVolume() {\n\n\t\treturn this.gain.gain.value;\n\n\t}\n\n\tsetMasterVolume( value ) {\n\n\t\tthis.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 );\n\n\t\treturn this;\n\n\t}\n\n\tupdateMatrixWorld( force ) {\n\n\t\tsuper.updateMatrixWorld( force );\n\n\t\tconst listener = this.context.listener;\n\t\tconst up = this.up;\n\n\t\tthis.timeDelta = this._clock.getDelta();\n\n\t\tthis.matrixWorld.decompose( _position$1, _quaternion$1, _scale$1 );\n\n\t\t_orientation$1.set( 0, 0, - 1 ).applyQuaternion( _quaternion$1 );\n\n\t\tif ( listener.positionX ) {\n\n\t\t\t// code path for Chrome (see #14393)\n\n\t\t\tconst endTime = this.context.currentTime + this.timeDelta;\n\n\t\t\tlistener.positionX.linearRampToValueAtTime( _position$1.x, endTime );\n\t\t\tlistener.positionY.linearRampToValueAtTime( _position$1.y, endTime );\n\t\t\tlistener.positionZ.linearRampToValueAtTime( _position$1.z, endTime );\n\t\t\tlistener.forwardX.linearRampToValueAtTime( _orientation$1.x, endTime );\n\t\t\tlistener.forwardY.linearRampToValueAtTime( _orientation$1.y, endTime );\n\t\t\tlistener.forwardZ.linearRampToValueAtTime( _orientation$1.z, endTime );\n\t\t\tlistener.upX.linearRampToValueAtTime( up.x, endTime );\n\t\t\tlistener.upY.linearRampToValueAtTime( up.y, endTime );\n\t\t\tlistener.upZ.linearRampToValueAtTime( up.z, endTime );\n\n\t\t} else {\n\n\t\t\tlistener.setPosition( _position$1.x, _position$1.y, _position$1.z );\n\t\t\tlistener.setOrientation( _orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z );\n\n\t\t}\n\n\t}\n\n}\n\nclass Audio extends Object3D {\n\n\tconstructor( listener ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'Audio';\n\n\t\tthis.listener = listener;\n\t\tthis.context = listener.context;\n\n\t\tthis.gain = this.context.createGain();\n\t\tthis.gain.connect( listener.getInput() );\n\n\t\tthis.autoplay = false;\n\n\t\tthis.buffer = null;\n\t\tthis.detune = 0;\n\t\tthis.loop = false;\n\t\tthis.loopStart = 0;\n\t\tthis.loopEnd = 0;\n\t\tthis.offset = 0;\n\t\tthis.duration = undefined;\n\t\tthis.playbackRate = 1;\n\t\tthis.isPlaying = false;\n\t\tthis.hasPlaybackControl = true;\n\t\tthis.source = null;\n\t\tthis.sourceType = 'empty';\n\n\t\tthis._startedAt = 0;\n\t\tthis._progress = 0;\n\t\tthis._connected = false;\n\n\t\tthis.filters = [];\n\n\t}\n\n\tgetOutput() {\n\n\t\treturn this.gain;\n\n\t}\n\n\tsetNodeSource( audioNode ) {\n\n\t\tthis.hasPlaybackControl = false;\n\t\tthis.sourceType = 'audioNode';\n\t\tthis.source = audioNode;\n\t\tthis.connect();\n\n\t\treturn this;\n\n\t}\n\n\tsetMediaElementSource( mediaElement ) {\n\n\t\tthis.hasPlaybackControl = false;\n\t\tthis.sourceType = 'mediaNode';\n\t\tthis.source = this.context.createMediaElementSource( mediaElement );\n\t\tthis.connect();\n\n\t\treturn this;\n\n\t}\n\n\tsetMediaStreamSource( mediaStream ) {\n\n\t\tthis.hasPlaybackControl = false;\n\t\tthis.sourceType = 'mediaStreamNode';\n\t\tthis.source = this.context.createMediaStreamSource( mediaStream );\n\t\tthis.connect();\n\n\t\treturn this;\n\n\t}\n\n\tsetBuffer( audioBuffer ) {\n\n\t\tthis.buffer = audioBuffer;\n\t\tthis.sourceType = 'buffer';\n\n\t\tif ( this.autoplay ) this.play();\n\n\t\treturn this;\n\n\t}\n\n\tplay( delay = 0 ) {\n\n\t\tif ( this.isPlaying === true ) {\n\n\t\t\tconsole.warn( 'THREE.Audio: Audio is already playing.' );\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( this.hasPlaybackControl === false ) {\n\n\t\t\tconsole.warn( 'THREE.Audio: this Audio has no playback control.' );\n\t\t\treturn;\n\n\t\t}\n\n\t\tthis._startedAt = this.context.currentTime + delay;\n\n\t\tconst source = this.context.createBufferSource();\n\t\tsource.buffer = this.buffer;\n\t\tsource.loop = this.loop;\n\t\tsource.loopStart = this.loopStart;\n\t\tsource.loopEnd = this.loopEnd;\n\t\tsource.onended = this.onEnded.bind( this );\n\t\tsource.start( this._startedAt, this._progress + this.offset, this.duration );\n\n\t\tthis.isPlaying = true;\n\n\t\tthis.source = source;\n\n\t\tthis.setDetune( this.detune );\n\t\tthis.setPlaybackRate( this.playbackRate );\n\n\t\treturn this.connect();\n\n\t}\n\n\tpause() {\n\n\t\tif ( this.hasPlaybackControl === false ) {\n\n\t\t\tconsole.warn( 'THREE.Audio: this Audio has no playback control.' );\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( this.isPlaying === true ) {\n\n\t\t\t// update current progress\n\n\t\t\tthis._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate;\n\n\t\t\tif ( this.loop === true ) {\n\n\t\t\t\t// ensure _progress does not exceed duration with looped audios\n\n\t\t\t\tthis._progress = this._progress % ( this.duration || this.buffer.duration );\n\n\t\t\t}\n\n\t\t\tthis.source.stop();\n\t\t\tthis.source.onended = null;\n\n\t\t\tthis.isPlaying = false;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tstop() {\n\n\t\tif ( this.hasPlaybackControl === false ) {\n\n\t\t\tconsole.warn( 'THREE.Audio: this Audio has no playback control.' );\n\t\t\treturn;\n\n\t\t}\n\n\t\tthis._progress = 0;\n\n\t\tif ( this.source !== null ) {\n\n\t\t\tthis.source.stop();\n\t\t\tthis.source.onended = null;\n\n\t\t}\n\n\t\tthis.isPlaying = false;\n\n\t\treturn this;\n\n\t}\n\n\tconnect() {\n\n\t\tif ( this.filters.length > 0 ) {\n\n\t\t\tthis.source.connect( this.filters[ 0 ] );\n\n\t\t\tfor ( let i = 1, l = this.filters.length; i < l; i ++ ) {\n\n\t\t\t\tthis.filters[ i - 1 ].connect( this.filters[ i ] );\n\n\t\t\t}\n\n\t\t\tthis.filters[ this.filters.length - 1 ].connect( this.getOutput() );\n\n\t\t} else {\n\n\t\t\tthis.source.connect( this.getOutput() );\n\n\t\t}\n\n\t\tthis._connected = true;\n\n\t\treturn this;\n\n\t}\n\n\tdisconnect() {\n\n\t\tif ( this._connected === false ) {\n\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( this.filters.length > 0 ) {\n\n\t\t\tthis.source.disconnect( this.filters[ 0 ] );\n\n\t\t\tfor ( let i = 1, l = this.filters.length; i < l; i ++ ) {\n\n\t\t\t\tthis.filters[ i - 1 ].disconnect( this.filters[ i ] );\n\n\t\t\t}\n\n\t\t\tthis.filters[ this.filters.length - 1 ].disconnect( this.getOutput() );\n\n\t\t} else {\n\n\t\t\tthis.source.disconnect( this.getOutput() );\n\n\t\t}\n\n\t\tthis._connected = false;\n\n\t\treturn this;\n\n\t}\n\n\tgetFilters() {\n\n\t\treturn this.filters;\n\n\t}\n\n\tsetFilters( value ) {\n\n\t\tif ( ! value ) value = [];\n\n\t\tif ( this._connected === true ) {\n\n\t\t\tthis.disconnect();\n\t\t\tthis.filters = value.slice();\n\t\t\tthis.connect();\n\n\t\t} else {\n\n\t\t\tthis.filters = value.slice();\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetDetune( value ) {\n\n\t\tthis.detune = value;\n\n\t\tif ( this.source.detune === undefined ) return; // only set detune when available\n\n\t\tif ( this.isPlaying === true ) {\n\n\t\t\tthis.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tgetDetune() {\n\n\t\treturn this.detune;\n\n\t}\n\n\tgetFilter() {\n\n\t\treturn this.getFilters()[ 0 ];\n\n\t}\n\n\tsetFilter( filter ) {\n\n\t\treturn this.setFilters( filter ? [ filter ] : [] );\n\n\t}\n\n\tsetPlaybackRate( value ) {\n\n\t\tif ( this.hasPlaybackControl === false ) {\n\n\t\t\tconsole.warn( 'THREE.Audio: this Audio has no playback control.' );\n\t\t\treturn;\n\n\t\t}\n\n\t\tthis.playbackRate = value;\n\n\t\tif ( this.isPlaying === true ) {\n\n\t\t\tthis.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tgetPlaybackRate() {\n\n\t\treturn this.playbackRate;\n\n\t}\n\n\tonEnded() {\n\n\t\tthis.isPlaying = false;\n\n\t}\n\n\tgetLoop() {\n\n\t\tif ( this.hasPlaybackControl === false ) {\n\n\t\t\tconsole.warn( 'THREE.Audio: this Audio has no playback control.' );\n\t\t\treturn false;\n\n\t\t}\n\n\t\treturn this.loop;\n\n\t}\n\n\tsetLoop( value ) {\n\n\t\tif ( this.hasPlaybackControl === false ) {\n\n\t\t\tconsole.warn( 'THREE.Audio: this Audio has no playback control.' );\n\t\t\treturn;\n\n\t\t}\n\n\t\tthis.loop = value;\n\n\t\tif ( this.isPlaying === true ) {\n\n\t\t\tthis.source.loop = this.loop;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetLoopStart( value ) {\n\n\t\tthis.loopStart = value;\n\n\t\treturn this;\n\n\t}\n\n\tsetLoopEnd( value ) {\n\n\t\tthis.loopEnd = value;\n\n\t\treturn this;\n\n\t}\n\n\tgetVolume() {\n\n\t\treturn this.gain.gain.value;\n\n\t}\n\n\tsetVolume( value ) {\n\n\t\tthis.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 );\n\n\t\treturn this;\n\n\t}\n\n}\n\nconst _position = /*@__PURE__*/ new Vector3();\nconst _quaternion = /*@__PURE__*/ new Quaternion();\nconst _scale = /*@__PURE__*/ new Vector3();\nconst _orientation = /*@__PURE__*/ new Vector3();\n\nclass PositionalAudio extends Audio {\n\n\tconstructor( listener ) {\n\n\t\tsuper( listener );\n\n\t\tthis.panner = this.context.createPanner();\n\t\tthis.panner.panningModel = 'HRTF';\n\t\tthis.panner.connect( this.gain );\n\n\t}\n\n\tconnect() {\n\n\t\tsuper.connect();\n\n\t\tthis.panner.connect( this.gain );\n\n\t}\n\n\tdisconnect() {\n\n\t\tsuper.disconnect();\n\n\t\tthis.panner.disconnect( this.gain );\n\n\t}\n\n\tgetOutput() {\n\n\t\treturn this.panner;\n\n\t}\n\n\tgetRefDistance() {\n\n\t\treturn this.panner.refDistance;\n\n\t}\n\n\tsetRefDistance( value ) {\n\n\t\tthis.panner.refDistance = value;\n\n\t\treturn this;\n\n\t}\n\n\tgetRolloffFactor() {\n\n\t\treturn this.panner.rolloffFactor;\n\n\t}\n\n\tsetRolloffFactor( value ) {\n\n\t\tthis.panner.rolloffFactor = value;\n\n\t\treturn this;\n\n\t}\n\n\tgetDistanceModel() {\n\n\t\treturn this.panner.distanceModel;\n\n\t}\n\n\tsetDistanceModel( value ) {\n\n\t\tthis.panner.distanceModel = value;\n\n\t\treturn this;\n\n\t}\n\n\tgetMaxDistance() {\n\n\t\treturn this.panner.maxDistance;\n\n\t}\n\n\tsetMaxDistance( value ) {\n\n\t\tthis.panner.maxDistance = value;\n\n\t\treturn this;\n\n\t}\n\n\tsetDirectionalCone( coneInnerAngle, coneOuterAngle, coneOuterGain ) {\n\n\t\tthis.panner.coneInnerAngle = coneInnerAngle;\n\t\tthis.panner.coneOuterAngle = coneOuterAngle;\n\t\tthis.panner.coneOuterGain = coneOuterGain;\n\n\t\treturn this;\n\n\t}\n\n\tupdateMatrixWorld( force ) {\n\n\t\tsuper.updateMatrixWorld( force );\n\n\t\tif ( this.hasPlaybackControl === true && this.isPlaying === false ) return;\n\n\t\tthis.matrixWorld.decompose( _position, _quaternion, _scale );\n\n\t\t_orientation.set( 0, 0, 1 ).applyQuaternion( _quaternion );\n\n\t\tconst panner = this.panner;\n\n\t\tif ( panner.positionX ) {\n\n\t\t\t// code path for Chrome and Firefox (see #14393)\n\n\t\t\tconst endTime = this.context.currentTime + this.listener.timeDelta;\n\n\t\t\tpanner.positionX.linearRampToValueAtTime( _position.x, endTime );\n\t\t\tpanner.positionY.linearRampToValueAtTime( _position.y, endTime );\n\t\t\tpanner.positionZ.linearRampToValueAtTime( _position.z, endTime );\n\t\t\tpanner.orientationX.linearRampToValueAtTime( _orientation.x, endTime );\n\t\t\tpanner.orientationY.linearRampToValueAtTime( _orientation.y, endTime );\n\t\t\tpanner.orientationZ.linearRampToValueAtTime( _orientation.z, endTime );\n\n\t\t} else {\n\n\t\t\tpanner.setPosition( _position.x, _position.y, _position.z );\n\t\t\tpanner.setOrientation( _orientation.x, _orientation.y, _orientation.z );\n\n\t\t}\n\n\t}\n\n}\n\nclass AudioAnalyser {\n\n\tconstructor( audio, fftSize = 2048 ) {\n\n\t\tthis.analyser = audio.context.createAnalyser();\n\t\tthis.analyser.fftSize = fftSize;\n\n\t\tthis.data = new Uint8Array( this.analyser.frequencyBinCount );\n\n\t\taudio.getOutput().connect( this.analyser );\n\n\t}\n\n\n\tgetFrequencyData() {\n\n\t\tthis.analyser.getByteFrequencyData( this.data );\n\n\t\treturn this.data;\n\n\t}\n\n\tgetAverageFrequency() {\n\n\t\tlet value = 0;\n\t\tconst data = this.getFrequencyData();\n\n\t\tfor ( let i = 0; i < data.length; i ++ ) {\n\n\t\t\tvalue += data[ i ];\n\n\t\t}\n\n\t\treturn value / data.length;\n\n\t}\n\n}\n\nclass PropertyMixer {\n\n\tconstructor( binding, typeName, valueSize ) {\n\n\t\tthis.binding = binding;\n\t\tthis.valueSize = valueSize;\n\n\t\tlet mixFunction,\n\t\t\tmixFunctionAdditive,\n\t\t\tsetIdentity;\n\n\t\t// buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ]\n\t\t//\n\t\t// interpolators can use .buffer as their .result\n\t\t// the data then goes to 'incoming'\n\t\t//\n\t\t// 'accu0' and 'accu1' are used frame-interleaved for\n\t\t// the cumulative result and are compared to detect\n\t\t// changes\n\t\t//\n\t\t// 'orig' stores the original state of the property\n\t\t//\n\t\t// 'add' is used for additive cumulative results\n\t\t//\n\t\t// 'work' is optional and is only present for quaternion types. It is used\n\t\t// to store intermediate quaternion multiplication results\n\n\t\tswitch ( typeName ) {\n\n\t\t\tcase 'quaternion':\n\t\t\t\tmixFunction = this._slerp;\n\t\t\t\tmixFunctionAdditive = this._slerpAdditive;\n\t\t\t\tsetIdentity = this._setAdditiveIdentityQuaternion;\n\n\t\t\t\tthis.buffer = new Float64Array( valueSize * 6 );\n\t\t\t\tthis._workIndex = 5;\n\t\t\t\tbreak;\n\n\t\t\tcase 'string':\n\t\t\tcase 'bool':\n\t\t\t\tmixFunction = this._select;\n\n\t\t\t\t// Use the regular mix function and for additive on these types,\n\t\t\t\t// additive is not relevant for non-numeric types\n\t\t\t\tmixFunctionAdditive = this._select;\n\n\t\t\t\tsetIdentity = this._setAdditiveIdentityOther;\n\n\t\t\t\tthis.buffer = new Array( valueSize * 5 );\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tmixFunction = this._lerp;\n\t\t\t\tmixFunctionAdditive = this._lerpAdditive;\n\t\t\t\tsetIdentity = this._setAdditiveIdentityNumeric;\n\n\t\t\t\tthis.buffer = new Float64Array( valueSize * 5 );\n\n\t\t}\n\n\t\tthis._mixBufferRegion = mixFunction;\n\t\tthis._mixBufferRegionAdditive = mixFunctionAdditive;\n\t\tthis._setIdentity = setIdentity;\n\t\tthis._origIndex = 3;\n\t\tthis._addIndex = 4;\n\n\t\tthis.cumulativeWeight = 0;\n\t\tthis.cumulativeWeightAdditive = 0;\n\n\t\tthis.useCount = 0;\n\t\tthis.referenceCount = 0;\n\n\t}\n\n\t// accumulate data in the 'incoming' region into 'accu<i>'\n\taccumulate( accuIndex, weight ) {\n\n\t\t// note: happily accumulating nothing when weight = 0, the caller knows\n\t\t// the weight and shouldn't have made the call in the first place\n\n\t\tconst buffer = this.buffer,\n\t\t\tstride = this.valueSize,\n\t\t\toffset = accuIndex * stride + stride;\n\n\t\tlet currentWeight = this.cumulativeWeight;\n\n\t\tif ( currentWeight === 0 ) {\n\n\t\t\t// accuN := incoming * weight\n\n\t\t\tfor ( let i = 0; i !== stride; ++ i ) {\n\n\t\t\t\tbuffer[ offset + i ] = buffer[ i ];\n\n\t\t\t}\n\n\t\t\tcurrentWeight = weight;\n\n\t\t} else {\n\n\t\t\t// accuN := accuN + incoming * weight\n\n\t\t\tcurrentWeight += weight;\n\t\t\tconst mix = weight / currentWeight;\n\t\t\tthis._mixBufferRegion( buffer, offset, 0, mix, stride );\n\n\t\t}\n\n\t\tthis.cumulativeWeight = currentWeight;\n\n\t}\n\n\t// accumulate data in the 'incoming' region into 'add'\n\taccumulateAdditive( weight ) {\n\n\t\tconst buffer = this.buffer,\n\t\t\tstride = this.valueSize,\n\t\t\toffset = stride * this._addIndex;\n\n\t\tif ( this.cumulativeWeightAdditive === 0 ) {\n\n\t\t\t// add = identity\n\n\t\t\tthis._setIdentity();\n\n\t\t}\n\n\t\t// add := add + incoming * weight\n\n\t\tthis._mixBufferRegionAdditive( buffer, offset, 0, weight, stride );\n\t\tthis.cumulativeWeightAdditive += weight;\n\n\t}\n\n\t// apply the state of 'accu<i>' to the binding when accus differ\n\tapply( accuIndex ) {\n\n\t\tconst stride = this.valueSize,\n\t\t\tbuffer = this.buffer,\n\t\t\toffset = accuIndex * stride + stride,\n\n\t\t\tweight = this.cumulativeWeight,\n\t\t\tweightAdditive = this.cumulativeWeightAdditive,\n\n\t\t\tbinding = this.binding;\n\n\t\tthis.cumulativeWeight = 0;\n\t\tthis.cumulativeWeightAdditive = 0;\n\n\t\tif ( weight < 1 ) {\n\n\t\t\t// accuN := accuN + original * ( 1 - cumulativeWeight )\n\n\t\t\tconst originalValueOffset = stride * this._origIndex;\n\n\t\t\tthis._mixBufferRegion(\n\t\t\t\tbuffer, offset, originalValueOffset, 1 - weight, stride );\n\n\t\t}\n\n\t\tif ( weightAdditive > 0 ) {\n\n\t\t\t// accuN := accuN + additive accuN\n\n\t\t\tthis._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride );\n\n\t\t}\n\n\t\tfor ( let i = stride, e = stride + stride; i !== e; ++ i ) {\n\n\t\t\tif ( buffer[ i ] !== buffer[ i + stride ] ) {\n\n\t\t\t\t// value has changed -> update scene graph\n\n\t\t\t\tbinding.setValue( buffer, offset );\n\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t// remember the state of the bound property and copy it to both accus\n\tsaveOriginalState() {\n\n\t\tconst binding = this.binding;\n\n\t\tconst buffer = this.buffer,\n\t\t\tstride = this.valueSize,\n\n\t\t\toriginalValueOffset = stride * this._origIndex;\n\n\t\tbinding.getValue( buffer, originalValueOffset );\n\n\t\t// accu[0..1] := orig -- initially detect changes against the original\n\t\tfor ( let i = stride, e = originalValueOffset; i !== e; ++ i ) {\n\n\t\t\tbuffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ];\n\n\t\t}\n\n\t\t// Add to identity for additive\n\t\tthis._setIdentity();\n\n\t\tthis.cumulativeWeight = 0;\n\t\tthis.cumulativeWeightAdditive = 0;\n\n\t}\n\n\t// apply the state previously taken via 'saveOriginalState' to the binding\n\trestoreOriginalState() {\n\n\t\tconst originalValueOffset = this.valueSize * 3;\n\t\tthis.binding.setValue( this.buffer, originalValueOffset );\n\n\t}\n\n\t_setAdditiveIdentityNumeric() {\n\n\t\tconst startIndex = this._addIndex * this.valueSize;\n\t\tconst endIndex = startIndex + this.valueSize;\n\n\t\tfor ( let i = startIndex; i < endIndex; i ++ ) {\n\n\t\t\tthis.buffer[ i ] = 0;\n\n\t\t}\n\n\t}\n\n\t_setAdditiveIdentityQuaternion() {\n\n\t\tthis._setAdditiveIdentityNumeric();\n\t\tthis.buffer[ this._addIndex * this.valueSize + 3 ] = 1;\n\n\t}\n\n\t_setAdditiveIdentityOther() {\n\n\t\tconst startIndex = this._origIndex * this.valueSize;\n\t\tconst targetIndex = this._addIndex * this.valueSize;\n\n\t\tfor ( let i = 0; i < this.valueSize; i ++ ) {\n\n\t\t\tthis.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ];\n\n\t\t}\n\n\t}\n\n\n\t// mix functions\n\n\t_select( buffer, dstOffset, srcOffset, t, stride ) {\n\n\t\tif ( t >= 0.5 ) {\n\n\t\t\tfor ( let i = 0; i !== stride; ++ i ) {\n\n\t\t\t\tbuffer[ dstOffset + i ] = buffer[ srcOffset + i ];\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t_slerp( buffer, dstOffset, srcOffset, t ) {\n\n\t\tQuaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t );\n\n\t}\n\n\t_slerpAdditive( buffer, dstOffset, srcOffset, t, stride ) {\n\n\t\tconst workOffset = this._workIndex * stride;\n\n\t\t// Store result in intermediate buffer offset\n\t\tQuaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset );\n\n\t\t// Slerp to the intermediate result\n\t\tQuaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t );\n\n\t}\n\n\t_lerp( buffer, dstOffset, srcOffset, t, stride ) {\n\n\t\tconst s = 1 - t;\n\n\t\tfor ( let i = 0; i !== stride; ++ i ) {\n\n\t\t\tconst j = dstOffset + i;\n\n\t\t\tbuffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t;\n\n\t\t}\n\n\t}\n\n\t_lerpAdditive( buffer, dstOffset, srcOffset, t, stride ) {\n\n\t\tfor ( let i = 0; i !== stride; ++ i ) {\n\n\t\t\tconst j = dstOffset + i;\n\n\t\t\tbuffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t;\n\n\t\t}\n\n\t}\n\n}\n\n// Characters [].:/ are reserved for track binding syntax.\nconst _RESERVED_CHARS_RE = '\\\\[\\\\]\\\\.:\\\\/';\nconst _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' );\n\n// Attempts to allow node names from any language. ES5's `\\w` regexp matches\n// only latin characters, and the unicode \\p{L} is not yet supported. So\n// instead, we exclude reserved characters and match everything else.\nconst _wordChar = '[^' + _RESERVED_CHARS_RE + ']';\nconst _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\\\.', '' ) + ']';\n\n// Parent directories, delimited by '/' or ':'. Currently unused, but must\n// be matched to parse the rest of the track name.\nconst _directoryRe = /*@__PURE__*/ /((?:WC+[\\/:])*)/.source.replace( 'WC', _wordChar );\n\n// Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'.\nconst _nodeRe = /*@__PURE__*/ /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot );\n\n// Object on target node, and accessor. May not contain reserved\n// characters. Accessor may contain any character except closing bracket.\nconst _objectRe = /*@__PURE__*/ /(?:\\.(WC+)(?:\\[(.+)\\])?)?/.source.replace( 'WC', _wordChar );\n\n// Property and accessor. May not contain reserved characters. Accessor may\n// contain any non-bracket characters.\nconst _propertyRe = /*@__PURE__*/ /\\.(WC+)(?:\\[(.+)\\])?/.source.replace( 'WC', _wordChar );\n\nconst _trackRe = new RegExp( ''\n\t+ '^'\n\t+ _directoryRe\n\t+ _nodeRe\n\t+ _objectRe\n\t+ _propertyRe\n\t+ '$'\n);\n\nconst _supportedObjectNames = [ 'material', 'materials', 'bones', 'map' ];\n\nclass Composite {\n\n\tconstructor( targetGroup, path, optionalParsedPath ) {\n\n\t\tconst parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );\n\n\t\tthis._targetGroup = targetGroup;\n\t\tthis._bindings = targetGroup.subscribe_( path, parsedPath );\n\n\t}\n\n\tgetValue( array, offset ) {\n\n\t\tthis.bind(); // bind all binding\n\n\t\tconst firstValidIndex = this._targetGroup.nCachedObjects_,\n\t\t\tbinding = this._bindings[ firstValidIndex ];\n\n\t\t// and only call .getValue on the first\n\t\tif ( binding !== undefined ) binding.getValue( array, offset );\n\n\t}\n\n\tsetValue( array, offset ) {\n\n\t\tconst bindings = this._bindings;\n\n\t\tfor ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) {\n\n\t\t\tbindings[ i ].setValue( array, offset );\n\n\t\t}\n\n\t}\n\n\tbind() {\n\n\t\tconst bindings = this._bindings;\n\n\t\tfor ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) {\n\n\t\t\tbindings[ i ].bind();\n\n\t\t}\n\n\t}\n\n\tunbind() {\n\n\t\tconst bindings = this._bindings;\n\n\t\tfor ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) {\n\n\t\t\tbindings[ i ].unbind();\n\n\t\t}\n\n\t}\n\n}\n\n// Note: This class uses a State pattern on a per-method basis:\n// 'bind' sets 'this.getValue' / 'setValue' and shadows the\n// prototype version of these methods with one that represents\n// the bound state. When the property is not found, the methods\n// become no-ops.\nclass PropertyBinding {\n\n\tconstructor( rootNode, path, parsedPath ) {\n\n\t\tthis.path = path;\n\t\tthis.parsedPath = parsedPath || PropertyBinding.parseTrackName( path );\n\n\t\tthis.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName );\n\n\t\tthis.rootNode = rootNode;\n\n\t\t// initial state of these methods that calls 'bind'\n\t\tthis.getValue = this._getValue_unbound;\n\t\tthis.setValue = this._setValue_unbound;\n\n\t}\n\n\n\tstatic create( root, path, parsedPath ) {\n\n\t\tif ( ! ( root && root.isAnimationObjectGroup ) ) {\n\n\t\t\treturn new PropertyBinding( root, path, parsedPath );\n\n\t\t} else {\n\n\t\t\treturn new PropertyBinding.Composite( root, path, parsedPath );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Replaces spaces with underscores and removes unsupported characters from\n\t * node names, to ensure compatibility with parseTrackName().\n\t *\n\t * @param {string} name Node name to be sanitized.\n\t * @return {string}\n\t */\n\tstatic sanitizeNodeName( name ) {\n\n\t\treturn name.replace( /\\s/g, '_' ).replace( _reservedRe, '' );\n\n\t}\n\n\tstatic parseTrackName( trackName ) {\n\n\t\tconst matches = _trackRe.exec( trackName );\n\n\t\tif ( matches === null ) {\n\n\t\t\tthrow new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName );\n\n\t\t}\n\n\t\tconst results = {\n\t\t\t// directoryName: matches[ 1 ], // (tschw) currently unused\n\t\t\tnodeName: matches[ 2 ],\n\t\t\tobjectName: matches[ 3 ],\n\t\t\tobjectIndex: matches[ 4 ],\n\t\t\tpropertyName: matches[ 5 ], // required\n\t\t\tpropertyIndex: matches[ 6 ]\n\t\t};\n\n\t\tconst lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' );\n\n\t\tif ( lastDot !== undefined && lastDot !== - 1 ) {\n\n\t\t\tconst objectName = results.nodeName.substring( lastDot + 1 );\n\n\t\t\t// Object names must be checked against an allowlist. Otherwise, there\n\t\t\t// is no way to parse 'foo.bar.baz': 'baz' must be a property, but\n\t\t\t// 'bar' could be the objectName, or part of a nodeName (which can\n\t\t\t// include '.' characters).\n\t\t\tif ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) {\n\n\t\t\t\tresults.nodeName = results.nodeName.substring( 0, lastDot );\n\t\t\t\tresults.objectName = objectName;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( results.propertyName === null || results.propertyName.length === 0 ) {\n\n\t\t\tthrow new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName );\n\n\t\t}\n\n\t\treturn results;\n\n\t}\n\n\tstatic findNode( root, nodeName ) {\n\n\t\tif ( nodeName === undefined || nodeName === '' || nodeName === '.' || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) {\n\n\t\t\treturn root;\n\n\t\t}\n\n\t\t// search into skeleton bones.\n\t\tif ( root.skeleton ) {\n\n\t\t\tconst bone = root.skeleton.getBoneByName( nodeName );\n\n\t\t\tif ( bone !== undefined ) {\n\n\t\t\t\treturn bone;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// search into node subtree.\n\t\tif ( root.children ) {\n\n\t\t\tconst searchNodeSubtree = function ( children ) {\n\n\t\t\t\tfor ( let i = 0; i < children.length; i ++ ) {\n\n\t\t\t\t\tconst childNode = children[ i ];\n\n\t\t\t\t\tif ( childNode.name === nodeName || childNode.uuid === nodeName ) {\n\n\t\t\t\t\t\treturn childNode;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tconst result = searchNodeSubtree( childNode.children );\n\n\t\t\t\t\tif ( result ) return result;\n\n\t\t\t\t}\n\n\t\t\t\treturn null;\n\n\t\t\t};\n\n\t\t\tconst subTreeNode = searchNodeSubtree( root.children );\n\n\t\t\tif ( subTreeNode ) {\n\n\t\t\t\treturn subTreeNode;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn null;\n\n\t}\n\n\t// these are used to \"bind\" a nonexistent property\n\t_getValue_unavailable() {}\n\t_setValue_unavailable() {}\n\n\t// Getters\n\n\t_getValue_direct( buffer, offset ) {\n\n\t\tbuffer[ offset ] = this.targetObject[ this.propertyName ];\n\n\t}\n\n\t_getValue_array( buffer, offset ) {\n\n\t\tconst source = this.resolvedProperty;\n\n\t\tfor ( let i = 0, n = source.length; i !== n; ++ i ) {\n\n\t\t\tbuffer[ offset ++ ] = source[ i ];\n\n\t\t}\n\n\t}\n\n\t_getValue_arrayElement( buffer, offset ) {\n\n\t\tbuffer[ offset ] = this.resolvedProperty[ this.propertyIndex ];\n\n\t}\n\n\t_getValue_toArray( buffer, offset ) {\n\n\t\tthis.resolvedProperty.toArray( buffer, offset );\n\n\t}\n\n\t// Direct\n\n\t_setValue_direct( buffer, offset ) {\n\n\t\tthis.targetObject[ this.propertyName ] = buffer[ offset ];\n\n\t}\n\n\t_setValue_direct_setNeedsUpdate( buffer, offset ) {\n\n\t\tthis.targetObject[ this.propertyName ] = buffer[ offset ];\n\t\tthis.targetObject.needsUpdate = true;\n\n\t}\n\n\t_setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) {\n\n\t\tthis.targetObject[ this.propertyName ] = buffer[ offset ];\n\t\tthis.targetObject.matrixWorldNeedsUpdate = true;\n\n\t}\n\n\t// EntireArray\n\n\t_setValue_array( buffer, offset ) {\n\n\t\tconst dest = this.resolvedProperty;\n\n\t\tfor ( let i = 0, n = dest.length; i !== n; ++ i ) {\n\n\t\t\tdest[ i ] = buffer[ offset ++ ];\n\n\t\t}\n\n\t}\n\n\t_setValue_array_setNeedsUpdate( buffer, offset ) {\n\n\t\tconst dest = this.resolvedProperty;\n\n\t\tfor ( let i = 0, n = dest.length; i !== n; ++ i ) {\n\n\t\t\tdest[ i ] = buffer[ offset ++ ];\n\n\t\t}\n\n\t\tthis.targetObject.needsUpdate = true;\n\n\t}\n\n\t_setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) {\n\n\t\tconst dest = this.resolvedProperty;\n\n\t\tfor ( let i = 0, n = dest.length; i !== n; ++ i ) {\n\n\t\t\tdest[ i ] = buffer[ offset ++ ];\n\n\t\t}\n\n\t\tthis.targetObject.matrixWorldNeedsUpdate = true;\n\n\t}\n\n\t// ArrayElement\n\n\t_setValue_arrayElement( buffer, offset ) {\n\n\t\tthis.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];\n\n\t}\n\n\t_setValue_arrayElement_setNeedsUpdate( buffer, offset ) {\n\n\t\tthis.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];\n\t\tthis.targetObject.needsUpdate = true;\n\n\t}\n\n\t_setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) {\n\n\t\tthis.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];\n\t\tthis.targetObject.matrixWorldNeedsUpdate = true;\n\n\t}\n\n\t// HasToFromArray\n\n\t_setValue_fromArray( buffer, offset ) {\n\n\t\tthis.resolvedProperty.fromArray( buffer, offset );\n\n\t}\n\n\t_setValue_fromArray_setNeedsUpdate( buffer, offset ) {\n\n\t\tthis.resolvedProperty.fromArray( buffer, offset );\n\t\tthis.targetObject.needsUpdate = true;\n\n\t}\n\n\t_setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) {\n\n\t\tthis.resolvedProperty.fromArray( buffer, offset );\n\t\tthis.targetObject.matrixWorldNeedsUpdate = true;\n\n\t}\n\n\t_getValue_unbound( targetArray, offset ) {\n\n\t\tthis.bind();\n\t\tthis.getValue( targetArray, offset );\n\n\t}\n\n\t_setValue_unbound( sourceArray, offset ) {\n\n\t\tthis.bind();\n\t\tthis.setValue( sourceArray, offset );\n\n\t}\n\n\t// create getter / setter pair for a property in the scene graph\n\tbind() {\n\n\t\tlet targetObject = this.node;\n\t\tconst parsedPath = this.parsedPath;\n\n\t\tconst objectName = parsedPath.objectName;\n\t\tconst propertyName = parsedPath.propertyName;\n\t\tlet propertyIndex = parsedPath.propertyIndex;\n\n\t\tif ( ! targetObject ) {\n\n\t\t\ttargetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName );\n\n\t\t\tthis.node = targetObject;\n\n\t\t}\n\n\t\t// set fail state so we can just 'return' on error\n\t\tthis.getValue = this._getValue_unavailable;\n\t\tthis.setValue = this._setValue_unavailable;\n\n\t\t// ensure there is a value node\n\t\tif ( ! targetObject ) {\n\n\t\t\tconsole.warn( 'THREE.PropertyBinding: No target node found for track: ' + this.path + '.' );\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( objectName ) {\n\n\t\t\tlet objectIndex = parsedPath.objectIndex;\n\n\t\t\t// special cases were we need to reach deeper into the hierarchy to get the face materials....\n\t\t\tswitch ( objectName ) {\n\n\t\t\t\tcase 'materials':\n\n\t\t\t\t\tif ( ! targetObject.material ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( ! targetObject.material.materials ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttargetObject = targetObject.material.materials;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'bones':\n\n\t\t\t\t\tif ( ! targetObject.skeleton ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// potential future optimization: skip this if propertyIndex is already an integer\n\t\t\t\t\t// and convert the integer string to a true integer.\n\n\t\t\t\t\ttargetObject = targetObject.skeleton.bones;\n\n\t\t\t\t\t// support resolving morphTarget names into indices.\n\t\t\t\t\tfor ( let i = 0; i < targetObject.length; i ++ ) {\n\n\t\t\t\t\t\tif ( targetObject[ i ].name === objectIndex ) {\n\n\t\t\t\t\t\t\tobjectIndex = i;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'map':\n\n\t\t\t\t\tif ( 'map' in targetObject ) {\n\n\t\t\t\t\t\ttargetObject = targetObject.map;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( ! targetObject.material ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( ! targetObject.material.map ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.PropertyBinding: Can not bind to material.map as node.material does not have a map.', this );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttargetObject = targetObject.material.map;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\n\t\t\t\t\tif ( targetObject[ objectName ] === undefined ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttargetObject = targetObject[ objectName ];\n\n\t\t\t}\n\n\n\t\t\tif ( objectIndex !== undefined ) {\n\n\t\t\t\tif ( targetObject[ objectIndex ] === undefined ) {\n\n\t\t\t\t\tconsole.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject );\n\t\t\t\t\treturn;\n\n\t\t\t\t}\n\n\t\t\t\ttargetObject = targetObject[ objectIndex ];\n\n\t\t\t}\n\n\t\t}\n\n\t\t// resolve property\n\t\tconst nodeProperty = targetObject[ propertyName ];\n\n\t\tif ( nodeProperty === undefined ) {\n\n\t\t\tconst nodeName = parsedPath.nodeName;\n\n\t\t\tconsole.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName +\n\t\t\t\t'.' + propertyName + ' but it wasn\\'t found.', targetObject );\n\t\t\treturn;\n\n\t\t}\n\n\t\t// determine versioning scheme\n\t\tlet versioning = this.Versioning.None;\n\n\t\tthis.targetObject = targetObject;\n\n\t\tif ( targetObject.needsUpdate !== undefined ) { // material\n\n\t\t\tversioning = this.Versioning.NeedsUpdate;\n\n\t\t} else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform\n\n\t\t\tversioning = this.Versioning.MatrixWorldNeedsUpdate;\n\n\t\t}\n\n\t\t// determine how the property gets bound\n\t\tlet bindingType = this.BindingType.Direct;\n\n\t\tif ( propertyIndex !== undefined ) {\n\n\t\t\t// access a sub element of the property array (only primitives are supported right now)\n\n\t\t\tif ( propertyName === 'morphTargetInfluences' ) {\n\n\t\t\t\t// potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.\n\n\t\t\t\t// support resolving morphTarget names into indices.\n\t\t\t\tif ( ! targetObject.geometry ) {\n\n\t\t\t\t\tconsole.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this );\n\t\t\t\t\treturn;\n\n\t\t\t\t}\n\n\t\t\t\tif ( ! targetObject.geometry.morphAttributes ) {\n\n\t\t\t\t\tconsole.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this );\n\t\t\t\t\treturn;\n\n\t\t\t\t}\n\n\t\t\t\tif ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) {\n\n\t\t\t\t\tpropertyIndex = targetObject.morphTargetDictionary[ propertyIndex ];\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tbindingType = this.BindingType.ArrayElement;\n\n\t\t\tthis.resolvedProperty = nodeProperty;\n\t\t\tthis.propertyIndex = propertyIndex;\n\n\t\t} else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) {\n\n\t\t\t// must use copy for Object3D.Euler/Quaternion\n\n\t\t\tbindingType = this.BindingType.HasFromToArray;\n\n\t\t\tthis.resolvedProperty = nodeProperty;\n\n\t\t} else if ( Array.isArray( nodeProperty ) ) {\n\n\t\t\tbindingType = this.BindingType.EntireArray;\n\n\t\t\tthis.resolvedProperty = nodeProperty;\n\n\t\t} else {\n\n\t\t\tthis.propertyName = propertyName;\n\n\t\t}\n\n\t\t// select getter / setter\n\t\tthis.getValue = this.GetterByBindingType[ bindingType ];\n\t\tthis.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ];\n\n\t}\n\n\tunbind() {\n\n\t\tthis.node = null;\n\n\t\t// back to the prototype version of getValue / setValue\n\t\t// note: avoiding to mutate the shape of 'this' via 'delete'\n\t\tthis.getValue = this._getValue_unbound;\n\t\tthis.setValue = this._setValue_unbound;\n\n\t}\n\n}\n\nPropertyBinding.Composite = Composite;\n\nPropertyBinding.prototype.BindingType = {\n\tDirect: 0,\n\tEntireArray: 1,\n\tArrayElement: 2,\n\tHasFromToArray: 3\n};\n\nPropertyBinding.prototype.Versioning = {\n\tNone: 0,\n\tNeedsUpdate: 1,\n\tMatrixWorldNeedsUpdate: 2\n};\n\nPropertyBinding.prototype.GetterByBindingType = [\n\n\tPropertyBinding.prototype._getValue_direct,\n\tPropertyBinding.prototype._getValue_array,\n\tPropertyBinding.prototype._getValue_arrayElement,\n\tPropertyBinding.prototype._getValue_toArray,\n\n];\n\nPropertyBinding.prototype.SetterByBindingTypeAndVersioning = [\n\n\t[\n\t\t// Direct\n\t\tPropertyBinding.prototype._setValue_direct,\n\t\tPropertyBinding.prototype._setValue_direct_setNeedsUpdate,\n\t\tPropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate,\n\n\t], [\n\n\t\t// EntireArray\n\n\t\tPropertyBinding.prototype._setValue_array,\n\t\tPropertyBinding.prototype._setValue_array_setNeedsUpdate,\n\t\tPropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate,\n\n\t], [\n\n\t\t// ArrayElement\n\t\tPropertyBinding.prototype._setValue_arrayElement,\n\t\tPropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate,\n\t\tPropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate,\n\n\t], [\n\n\t\t// HasToFromArray\n\t\tPropertyBinding.prototype._setValue_fromArray,\n\t\tPropertyBinding.prototype._setValue_fromArray_setNeedsUpdate,\n\t\tPropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate,\n\n\t]\n\n];\n\n/**\n *\n * A group of objects that receives a shared animation state.\n *\n * Usage:\n *\n *  - Add objects you would otherwise pass as 'root' to the\n *    constructor or the .clipAction method of AnimationMixer.\n *\n *  - Instead pass this object as 'root'.\n *\n *  - You can also add and remove objects later when the mixer\n *    is running.\n *\n * Note:\n *\n *    Objects of this class appear as one object to the mixer,\n *    so cache control of the individual objects must be done\n *    on the group.\n *\n * Limitation:\n *\n *  - The animated properties must be compatible among the\n *    all objects in the group.\n *\n *  - A single property can either be controlled through a\n *    target group or directly, but not both.\n */\n\nclass AnimationObjectGroup {\n\n\tconstructor() {\n\n\t\tthis.isAnimationObjectGroup = true;\n\n\t\tthis.uuid = generateUUID();\n\n\t\t// cached objects followed by the active ones\n\t\tthis._objects = Array.prototype.slice.call( arguments );\n\n\t\tthis.nCachedObjects_ = 0; // threshold\n\t\t// note: read by PropertyBinding.Composite\n\n\t\tconst indices = {};\n\t\tthis._indicesByUUID = indices; // for bookkeeping\n\n\t\tfor ( let i = 0, n = arguments.length; i !== n; ++ i ) {\n\n\t\t\tindices[ arguments[ i ].uuid ] = i;\n\n\t\t}\n\n\t\tthis._paths = []; // inside: string\n\t\tthis._parsedPaths = []; // inside: { we don't care, here }\n\t\tthis._bindings = []; // inside: Array< PropertyBinding >\n\t\tthis._bindingsIndicesByPath = {}; // inside: indices in these arrays\n\n\t\tconst scope = this;\n\n\t\tthis.stats = {\n\n\t\t\tobjects: {\n\t\t\t\tget total() {\n\n\t\t\t\t\treturn scope._objects.length;\n\n\t\t\t\t},\n\t\t\t\tget inUse() {\n\n\t\t\t\t\treturn this.total - scope.nCachedObjects_;\n\n\t\t\t\t}\n\t\t\t},\n\t\t\tget bindingsPerObject() {\n\n\t\t\t\treturn scope._bindings.length;\n\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\tadd() {\n\n\t\tconst objects = this._objects,\n\t\t\tindicesByUUID = this._indicesByUUID,\n\t\t\tpaths = this._paths,\n\t\t\tparsedPaths = this._parsedPaths,\n\t\t\tbindings = this._bindings,\n\t\t\tnBindings = bindings.length;\n\n\t\tlet knownObject = undefined,\n\t\t\tnObjects = objects.length,\n\t\t\tnCachedObjects = this.nCachedObjects_;\n\n\t\tfor ( let i = 0, n = arguments.length; i !== n; ++ i ) {\n\n\t\t\tconst object = arguments[ i ],\n\t\t\t\tuuid = object.uuid;\n\t\t\tlet index = indicesByUUID[ uuid ];\n\n\t\t\tif ( index === undefined ) {\n\n\t\t\t\t// unknown object -> add it to the ACTIVE region\n\n\t\t\t\tindex = nObjects ++;\n\t\t\t\tindicesByUUID[ uuid ] = index;\n\t\t\t\tobjects.push( object );\n\n\t\t\t\t// accounting is done, now do the same for all bindings\n\n\t\t\t\tfor ( let j = 0, m = nBindings; j !== m; ++ j ) {\n\n\t\t\t\t\tbindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) );\n\n\t\t\t\t}\n\n\t\t\t} else if ( index < nCachedObjects ) {\n\n\t\t\t\tknownObject = objects[ index ];\n\n\t\t\t\t// move existing object to the ACTIVE region\n\n\t\t\t\tconst firstActiveIndex = -- nCachedObjects,\n\t\t\t\t\tlastCachedObject = objects[ firstActiveIndex ];\n\n\t\t\t\tindicesByUUID[ lastCachedObject.uuid ] = index;\n\t\t\t\tobjects[ index ] = lastCachedObject;\n\n\t\t\t\tindicesByUUID[ uuid ] = firstActiveIndex;\n\t\t\t\tobjects[ firstActiveIndex ] = object;\n\n\t\t\t\t// accounting is done, now do the same for all bindings\n\n\t\t\t\tfor ( let j = 0, m = nBindings; j !== m; ++ j ) {\n\n\t\t\t\t\tconst bindingsForPath = bindings[ j ],\n\t\t\t\t\t\tlastCached = bindingsForPath[ firstActiveIndex ];\n\n\t\t\t\t\tlet binding = bindingsForPath[ index ];\n\n\t\t\t\t\tbindingsForPath[ index ] = lastCached;\n\n\t\t\t\t\tif ( binding === undefined ) {\n\n\t\t\t\t\t\t// since we do not bother to create new bindings\n\t\t\t\t\t\t// for objects that are cached, the binding may\n\t\t\t\t\t\t// or may not exist\n\n\t\t\t\t\t\tbinding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbindingsForPath[ firstActiveIndex ] = binding;\n\n\t\t\t\t}\n\n\t\t\t} else if ( objects[ index ] !== knownObject ) {\n\n\t\t\t\tconsole.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' +\n\t\t\t\t\t'detected. Clean the caches or recreate your infrastructure when reloading scenes.' );\n\n\t\t\t} // else the object is already where we want it to be\n\n\t\t} // for arguments\n\n\t\tthis.nCachedObjects_ = nCachedObjects;\n\n\t}\n\n\tremove() {\n\n\t\tconst objects = this._objects,\n\t\t\tindicesByUUID = this._indicesByUUID,\n\t\t\tbindings = this._bindings,\n\t\t\tnBindings = bindings.length;\n\n\t\tlet nCachedObjects = this.nCachedObjects_;\n\n\t\tfor ( let i = 0, n = arguments.length; i !== n; ++ i ) {\n\n\t\t\tconst object = arguments[ i ],\n\t\t\t\tuuid = object.uuid,\n\t\t\t\tindex = indicesByUUID[ uuid ];\n\n\t\t\tif ( index !== undefined && index >= nCachedObjects ) {\n\n\t\t\t\t// move existing object into the CACHED region\n\n\t\t\t\tconst lastCachedIndex = nCachedObjects ++,\n\t\t\t\t\tfirstActiveObject = objects[ lastCachedIndex ];\n\n\t\t\t\tindicesByUUID[ firstActiveObject.uuid ] = index;\n\t\t\t\tobjects[ index ] = firstActiveObject;\n\n\t\t\t\tindicesByUUID[ uuid ] = lastCachedIndex;\n\t\t\t\tobjects[ lastCachedIndex ] = object;\n\n\t\t\t\t// accounting is done, now do the same for all bindings\n\n\t\t\t\tfor ( let j = 0, m = nBindings; j !== m; ++ j ) {\n\n\t\t\t\t\tconst bindingsForPath = bindings[ j ],\n\t\t\t\t\t\tfirstActive = bindingsForPath[ lastCachedIndex ],\n\t\t\t\t\t\tbinding = bindingsForPath[ index ];\n\n\t\t\t\t\tbindingsForPath[ index ] = firstActive;\n\t\t\t\t\tbindingsForPath[ lastCachedIndex ] = binding;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} // for arguments\n\n\t\tthis.nCachedObjects_ = nCachedObjects;\n\n\t}\n\n\t// remove & forget\n\tuncache() {\n\n\t\tconst objects = this._objects,\n\t\t\tindicesByUUID = this._indicesByUUID,\n\t\t\tbindings = this._bindings,\n\t\t\tnBindings = bindings.length;\n\n\t\tlet nCachedObjects = this.nCachedObjects_,\n\t\t\tnObjects = objects.length;\n\n\t\tfor ( let i = 0, n = arguments.length; i !== n; ++ i ) {\n\n\t\t\tconst object = arguments[ i ],\n\t\t\t\tuuid = object.uuid,\n\t\t\t\tindex = indicesByUUID[ uuid ];\n\n\t\t\tif ( index !== undefined ) {\n\n\t\t\t\tdelete indicesByUUID[ uuid ];\n\n\t\t\t\tif ( index < nCachedObjects ) {\n\n\t\t\t\t\t// object is cached, shrink the CACHED region\n\n\t\t\t\t\tconst firstActiveIndex = -- nCachedObjects,\n\t\t\t\t\t\tlastCachedObject = objects[ firstActiveIndex ],\n\t\t\t\t\t\tlastIndex = -- nObjects,\n\t\t\t\t\t\tlastObject = objects[ lastIndex ];\n\n\t\t\t\t\t// last cached object takes this object's place\n\t\t\t\t\tindicesByUUID[ lastCachedObject.uuid ] = index;\n\t\t\t\t\tobjects[ index ] = lastCachedObject;\n\n\t\t\t\t\t// last object goes to the activated slot and pop\n\t\t\t\t\tindicesByUUID[ lastObject.uuid ] = firstActiveIndex;\n\t\t\t\t\tobjects[ firstActiveIndex ] = lastObject;\n\t\t\t\t\tobjects.pop();\n\n\t\t\t\t\t// accounting is done, now do the same for all bindings\n\n\t\t\t\t\tfor ( let j = 0, m = nBindings; j !== m; ++ j ) {\n\n\t\t\t\t\t\tconst bindingsForPath = bindings[ j ],\n\t\t\t\t\t\t\tlastCached = bindingsForPath[ firstActiveIndex ],\n\t\t\t\t\t\t\tlast = bindingsForPath[ lastIndex ];\n\n\t\t\t\t\t\tbindingsForPath[ index ] = lastCached;\n\t\t\t\t\t\tbindingsForPath[ firstActiveIndex ] = last;\n\t\t\t\t\t\tbindingsForPath.pop();\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// object is active, just swap with the last and pop\n\n\t\t\t\t\tconst lastIndex = -- nObjects,\n\t\t\t\t\t\tlastObject = objects[ lastIndex ];\n\n\t\t\t\t\tif ( lastIndex > 0 ) {\n\n\t\t\t\t\t\tindicesByUUID[ lastObject.uuid ] = index;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tobjects[ index ] = lastObject;\n\t\t\t\t\tobjects.pop();\n\n\t\t\t\t\t// accounting is done, now do the same for all bindings\n\n\t\t\t\t\tfor ( let j = 0, m = nBindings; j !== m; ++ j ) {\n\n\t\t\t\t\t\tconst bindingsForPath = bindings[ j ];\n\n\t\t\t\t\t\tbindingsForPath[ index ] = bindingsForPath[ lastIndex ];\n\t\t\t\t\t\tbindingsForPath.pop();\n\n\t\t\t\t\t}\n\n\t\t\t\t} // cached or active\n\n\t\t\t} // if object is known\n\n\t\t} // for arguments\n\n\t\tthis.nCachedObjects_ = nCachedObjects;\n\n\t}\n\n\t// Internal interface used by befriended PropertyBinding.Composite:\n\n\tsubscribe_( path, parsedPath ) {\n\n\t\t// returns an array of bindings for the given path that is changed\n\t\t// according to the contained objects in the group\n\n\t\tconst indicesByPath = this._bindingsIndicesByPath;\n\t\tlet index = indicesByPath[ path ];\n\t\tconst bindings = this._bindings;\n\n\t\tif ( index !== undefined ) return bindings[ index ];\n\n\t\tconst paths = this._paths,\n\t\t\tparsedPaths = this._parsedPaths,\n\t\t\tobjects = this._objects,\n\t\t\tnObjects = objects.length,\n\t\t\tnCachedObjects = this.nCachedObjects_,\n\t\t\tbindingsForPath = new Array( nObjects );\n\n\t\tindex = bindings.length;\n\n\t\tindicesByPath[ path ] = index;\n\n\t\tpaths.push( path );\n\t\tparsedPaths.push( parsedPath );\n\t\tbindings.push( bindingsForPath );\n\n\t\tfor ( let i = nCachedObjects, n = objects.length; i !== n; ++ i ) {\n\n\t\t\tconst object = objects[ i ];\n\t\t\tbindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath );\n\n\t\t}\n\n\t\treturn bindingsForPath;\n\n\t}\n\n\tunsubscribe_( path ) {\n\n\t\t// tells the group to forget about a property path and no longer\n\t\t// update the array previously obtained with 'subscribe_'\n\n\t\tconst indicesByPath = this._bindingsIndicesByPath,\n\t\t\tindex = indicesByPath[ path ];\n\n\t\tif ( index !== undefined ) {\n\n\t\t\tconst paths = this._paths,\n\t\t\t\tparsedPaths = this._parsedPaths,\n\t\t\t\tbindings = this._bindings,\n\t\t\t\tlastBindingsIndex = bindings.length - 1,\n\t\t\t\tlastBindings = bindings[ lastBindingsIndex ],\n\t\t\t\tlastBindingsPath = path[ lastBindingsIndex ];\n\n\t\t\tindicesByPath[ lastBindingsPath ] = index;\n\n\t\t\tbindings[ index ] = lastBindings;\n\t\t\tbindings.pop();\n\n\t\t\tparsedPaths[ index ] = parsedPaths[ lastBindingsIndex ];\n\t\t\tparsedPaths.pop();\n\n\t\t\tpaths[ index ] = paths[ lastBindingsIndex ];\n\t\t\tpaths.pop();\n\n\t\t}\n\n\t}\n\n}\n\nclass AnimationAction {\n\n\tconstructor( mixer, clip, localRoot = null, blendMode = clip.blendMode ) {\n\n\t\tthis._mixer = mixer;\n\t\tthis._clip = clip;\n\t\tthis._localRoot = localRoot;\n\t\tthis.blendMode = blendMode;\n\n\t\tconst tracks = clip.tracks,\n\t\t\tnTracks = tracks.length,\n\t\t\tinterpolants = new Array( nTracks );\n\n\t\tconst interpolantSettings = {\n\t\t\tendingStart: ZeroCurvatureEnding,\n\t\t\tendingEnd: ZeroCurvatureEnding\n\t\t};\n\n\t\tfor ( let i = 0; i !== nTracks; ++ i ) {\n\n\t\t\tconst interpolant = tracks[ i ].createInterpolant( null );\n\t\t\tinterpolants[ i ] = interpolant;\n\t\t\tinterpolant.settings = interpolantSettings;\n\n\t\t}\n\n\t\tthis._interpolantSettings = interpolantSettings;\n\n\t\tthis._interpolants = interpolants; // bound by the mixer\n\n\t\t// inside: PropertyMixer (managed by the mixer)\n\t\tthis._propertyBindings = new Array( nTracks );\n\n\t\tthis._cacheIndex = null; // for the memory manager\n\t\tthis._byClipCacheIndex = null; // for the memory manager\n\n\t\tthis._timeScaleInterpolant = null;\n\t\tthis._weightInterpolant = null;\n\n\t\tthis.loop = LoopRepeat;\n\t\tthis._loopCount = - 1;\n\n\t\t// global mixer time when the action is to be started\n\t\t// it's set back to 'null' upon start of the action\n\t\tthis._startTime = null;\n\n\t\t// scaled local time of the action\n\t\t// gets clamped or wrapped to 0..clip.duration according to loop\n\t\tthis.time = 0;\n\n\t\tthis.timeScale = 1;\n\t\tthis._effectiveTimeScale = 1;\n\n\t\tthis.weight = 1;\n\t\tthis._effectiveWeight = 1;\n\n\t\tthis.repetitions = Infinity; // no. of repetitions when looping\n\n\t\tthis.paused = false; // true -> zero effective time scale\n\t\tthis.enabled = true; // false -> zero effective weight\n\n\t\tthis.clampWhenFinished = false;// keep feeding the last frame?\n\n\t\tthis.zeroSlopeAtStart = true;// for smooth interpolation w/o separate\n\t\tthis.zeroSlopeAtEnd = true;// clips for start, loop and end\n\n\t}\n\n\t// State & Scheduling\n\n\tplay() {\n\n\t\tthis._mixer._activateAction( this );\n\n\t\treturn this;\n\n\t}\n\n\tstop() {\n\n\t\tthis._mixer._deactivateAction( this );\n\n\t\treturn this.reset();\n\n\t}\n\n\treset() {\n\n\t\tthis.paused = false;\n\t\tthis.enabled = true;\n\n\t\tthis.time = 0; // restart clip\n\t\tthis._loopCount = - 1;// forget previous loops\n\t\tthis._startTime = null;// forget scheduling\n\n\t\treturn this.stopFading().stopWarping();\n\n\t}\n\n\tisRunning() {\n\n\t\treturn this.enabled && ! this.paused && this.timeScale !== 0 &&\n\t\t\tthis._startTime === null && this._mixer._isActiveAction( this );\n\n\t}\n\n\t// return true when play has been called\n\tisScheduled() {\n\n\t\treturn this._mixer._isActiveAction( this );\n\n\t}\n\n\tstartAt( time ) {\n\n\t\tthis._startTime = time;\n\n\t\treturn this;\n\n\t}\n\n\tsetLoop( mode, repetitions ) {\n\n\t\tthis.loop = mode;\n\t\tthis.repetitions = repetitions;\n\n\t\treturn this;\n\n\t}\n\n\t// Weight\n\n\t// set the weight stopping any scheduled fading\n\t// although .enabled = false yields an effective weight of zero, this\n\t// method does *not* change .enabled, because it would be confusing\n\tsetEffectiveWeight( weight ) {\n\n\t\tthis.weight = weight;\n\n\t\t// note: same logic as when updated at runtime\n\t\tthis._effectiveWeight = this.enabled ? weight : 0;\n\n\t\treturn this.stopFading();\n\n\t}\n\n\t// return the weight considering fading and .enabled\n\tgetEffectiveWeight() {\n\n\t\treturn this._effectiveWeight;\n\n\t}\n\n\tfadeIn( duration ) {\n\n\t\treturn this._scheduleFading( duration, 0, 1 );\n\n\t}\n\n\tfadeOut( duration ) {\n\n\t\treturn this._scheduleFading( duration, 1, 0 );\n\n\t}\n\n\tcrossFadeFrom( fadeOutAction, duration, warp ) {\n\n\t\tfadeOutAction.fadeOut( duration );\n\t\tthis.fadeIn( duration );\n\n\t\tif ( warp ) {\n\n\t\t\tconst fadeInDuration = this._clip.duration,\n\t\t\t\tfadeOutDuration = fadeOutAction._clip.duration,\n\n\t\t\t\tstartEndRatio = fadeOutDuration / fadeInDuration,\n\t\t\t\tendStartRatio = fadeInDuration / fadeOutDuration;\n\n\t\t\tfadeOutAction.warp( 1.0, startEndRatio, duration );\n\t\t\tthis.warp( endStartRatio, 1.0, duration );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tcrossFadeTo( fadeInAction, duration, warp ) {\n\n\t\treturn fadeInAction.crossFadeFrom( this, duration, warp );\n\n\t}\n\n\tstopFading() {\n\n\t\tconst weightInterpolant = this._weightInterpolant;\n\n\t\tif ( weightInterpolant !== null ) {\n\n\t\t\tthis._weightInterpolant = null;\n\t\t\tthis._mixer._takeBackControlInterpolant( weightInterpolant );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\t// Time Scale Control\n\n\t// set the time scale stopping any scheduled warping\n\t// although .paused = true yields an effective time scale of zero, this\n\t// method does *not* change .paused, because it would be confusing\n\tsetEffectiveTimeScale( timeScale ) {\n\n\t\tthis.timeScale = timeScale;\n\t\tthis._effectiveTimeScale = this.paused ? 0 : timeScale;\n\n\t\treturn this.stopWarping();\n\n\t}\n\n\t// return the time scale considering warping and .paused\n\tgetEffectiveTimeScale() {\n\n\t\treturn this._effectiveTimeScale;\n\n\t}\n\n\tsetDuration( duration ) {\n\n\t\tthis.timeScale = this._clip.duration / duration;\n\n\t\treturn this.stopWarping();\n\n\t}\n\n\tsyncWith( action ) {\n\n\t\tthis.time = action.time;\n\t\tthis.timeScale = action.timeScale;\n\n\t\treturn this.stopWarping();\n\n\t}\n\n\thalt( duration ) {\n\n\t\treturn this.warp( this._effectiveTimeScale, 0, duration );\n\n\t}\n\n\twarp( startTimeScale, endTimeScale, duration ) {\n\n\t\tconst mixer = this._mixer,\n\t\t\tnow = mixer.time,\n\t\t\ttimeScale = this.timeScale;\n\n\t\tlet interpolant = this._timeScaleInterpolant;\n\n\t\tif ( interpolant === null ) {\n\n\t\t\tinterpolant = mixer._lendControlInterpolant();\n\t\t\tthis._timeScaleInterpolant = interpolant;\n\n\t\t}\n\n\t\tconst times = interpolant.parameterPositions,\n\t\t\tvalues = interpolant.sampleValues;\n\n\t\ttimes[ 0 ] = now;\n\t\ttimes[ 1 ] = now + duration;\n\n\t\tvalues[ 0 ] = startTimeScale / timeScale;\n\t\tvalues[ 1 ] = endTimeScale / timeScale;\n\n\t\treturn this;\n\n\t}\n\n\tstopWarping() {\n\n\t\tconst timeScaleInterpolant = this._timeScaleInterpolant;\n\n\t\tif ( timeScaleInterpolant !== null ) {\n\n\t\t\tthis._timeScaleInterpolant = null;\n\t\t\tthis._mixer._takeBackControlInterpolant( timeScaleInterpolant );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\t// Object Accessors\n\n\tgetMixer() {\n\n\t\treturn this._mixer;\n\n\t}\n\n\tgetClip() {\n\n\t\treturn this._clip;\n\n\t}\n\n\tgetRoot() {\n\n\t\treturn this._localRoot || this._mixer._root;\n\n\t}\n\n\t// Interna\n\n\t_update( time, deltaTime, timeDirection, accuIndex ) {\n\n\t\t// called by the mixer\n\n\t\tif ( ! this.enabled ) {\n\n\t\t\t// call ._updateWeight() to update ._effectiveWeight\n\n\t\t\tthis._updateWeight( time );\n\t\t\treturn;\n\n\t\t}\n\n\t\tconst startTime = this._startTime;\n\n\t\tif ( startTime !== null ) {\n\n\t\t\t// check for scheduled start of action\n\n\t\t\tconst timeRunning = ( time - startTime ) * timeDirection;\n\t\t\tif ( timeRunning < 0 || timeDirection === 0 ) {\n\n\t\t\t\tdeltaTime = 0;\n\n\t\t\t} else {\n\n\n\t\t\t\tthis._startTime = null; // unschedule\n\t\t\t\tdeltaTime = timeDirection * timeRunning;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// apply time scale and advance time\n\n\t\tdeltaTime *= this._updateTimeScale( time );\n\t\tconst clipTime = this._updateTime( deltaTime );\n\n\t\t// note: _updateTime may disable the action resulting in\n\t\t// an effective weight of 0\n\n\t\tconst weight = this._updateWeight( time );\n\n\t\tif ( weight > 0 ) {\n\n\t\t\tconst interpolants = this._interpolants;\n\t\t\tconst propertyMixers = this._propertyBindings;\n\n\t\t\tswitch ( this.blendMode ) {\n\n\t\t\t\tcase AdditiveAnimationBlendMode:\n\n\t\t\t\t\tfor ( let j = 0, m = interpolants.length; j !== m; ++ j ) {\n\n\t\t\t\t\t\tinterpolants[ j ].evaluate( clipTime );\n\t\t\t\t\t\tpropertyMixers[ j ].accumulateAdditive( weight );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase NormalAnimationBlendMode:\n\t\t\t\tdefault:\n\n\t\t\t\t\tfor ( let j = 0, m = interpolants.length; j !== m; ++ j ) {\n\n\t\t\t\t\t\tinterpolants[ j ].evaluate( clipTime );\n\t\t\t\t\t\tpropertyMixers[ j ].accumulate( accuIndex, weight );\n\n\t\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t_updateWeight( time ) {\n\n\t\tlet weight = 0;\n\n\t\tif ( this.enabled ) {\n\n\t\t\tweight = this.weight;\n\t\t\tconst interpolant = this._weightInterpolant;\n\n\t\t\tif ( interpolant !== null ) {\n\n\t\t\t\tconst interpolantValue = interpolant.evaluate( time )[ 0 ];\n\n\t\t\t\tweight *= interpolantValue;\n\n\t\t\t\tif ( time > interpolant.parameterPositions[ 1 ] ) {\n\n\t\t\t\t\tthis.stopFading();\n\n\t\t\t\t\tif ( interpolantValue === 0 ) {\n\n\t\t\t\t\t\t// faded out, disable\n\t\t\t\t\t\tthis.enabled = false;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis._effectiveWeight = weight;\n\t\treturn weight;\n\n\t}\n\n\t_updateTimeScale( time ) {\n\n\t\tlet timeScale = 0;\n\n\t\tif ( ! this.paused ) {\n\n\t\t\ttimeScale = this.timeScale;\n\n\t\t\tconst interpolant = this._timeScaleInterpolant;\n\n\t\t\tif ( interpolant !== null ) {\n\n\t\t\t\tconst interpolantValue = interpolant.evaluate( time )[ 0 ];\n\n\t\t\t\ttimeScale *= interpolantValue;\n\n\t\t\t\tif ( time > interpolant.parameterPositions[ 1 ] ) {\n\n\t\t\t\t\tthis.stopWarping();\n\n\t\t\t\t\tif ( timeScale === 0 ) {\n\n\t\t\t\t\t\t// motion has halted, pause\n\t\t\t\t\t\tthis.paused = true;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// warp done - apply final time scale\n\t\t\t\t\t\tthis.timeScale = timeScale;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis._effectiveTimeScale = timeScale;\n\t\treturn timeScale;\n\n\t}\n\n\t_updateTime( deltaTime ) {\n\n\t\tconst duration = this._clip.duration;\n\t\tconst loop = this.loop;\n\n\t\tlet time = this.time + deltaTime;\n\t\tlet loopCount = this._loopCount;\n\n\t\tconst pingPong = ( loop === LoopPingPong );\n\n\t\tif ( deltaTime === 0 ) {\n\n\t\t\tif ( loopCount === - 1 ) return time;\n\n\t\t\treturn ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time;\n\n\t\t}\n\n\t\tif ( loop === LoopOnce ) {\n\n\t\t\tif ( loopCount === - 1 ) {\n\n\t\t\t\t// just started\n\n\t\t\t\tthis._loopCount = 0;\n\t\t\t\tthis._setEndings( true, true, false );\n\n\t\t\t}\n\n\t\t\thandle_stop: {\n\n\t\t\t\tif ( time >= duration ) {\n\n\t\t\t\t\ttime = duration;\n\n\t\t\t\t} else if ( time < 0 ) {\n\n\t\t\t\t\ttime = 0;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis.time = time;\n\n\t\t\t\t\tbreak handle_stop;\n\n\t\t\t\t}\n\n\t\t\t\tif ( this.clampWhenFinished ) this.paused = true;\n\t\t\t\telse this.enabled = false;\n\n\t\t\t\tthis.time = time;\n\n\t\t\t\tthis._mixer.dispatchEvent( {\n\t\t\t\t\ttype: 'finished', action: this,\n\t\t\t\t\tdirection: deltaTime < 0 ? - 1 : 1\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t} else { // repetitive Repeat or PingPong\n\n\t\t\tif ( loopCount === - 1 ) {\n\n\t\t\t\t// just started\n\n\t\t\t\tif ( deltaTime >= 0 ) {\n\n\t\t\t\t\tloopCount = 0;\n\n\t\t\t\t\tthis._setEndings( true, this.repetitions === 0, pingPong );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// when looping in reverse direction, the initial\n\t\t\t\t\t// transition through zero counts as a repetition,\n\t\t\t\t\t// so leave loopCount at -1\n\n\t\t\t\t\tthis._setEndings( this.repetitions === 0, true, pingPong );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( time >= duration || time < 0 ) {\n\n\t\t\t\t// wrap around\n\n\t\t\t\tconst loopDelta = Math.floor( time / duration ); // signed\n\t\t\t\ttime -= duration * loopDelta;\n\n\t\t\t\tloopCount += Math.abs( loopDelta );\n\n\t\t\t\tconst pending = this.repetitions - loopCount;\n\n\t\t\t\tif ( pending <= 0 ) {\n\n\t\t\t\t\t// have to stop (switch state, clamp time, fire event)\n\n\t\t\t\t\tif ( this.clampWhenFinished ) this.paused = true;\n\t\t\t\t\telse this.enabled = false;\n\n\t\t\t\t\ttime = deltaTime > 0 ? duration : 0;\n\n\t\t\t\t\tthis.time = time;\n\n\t\t\t\t\tthis._mixer.dispatchEvent( {\n\t\t\t\t\t\ttype: 'finished', action: this,\n\t\t\t\t\t\tdirection: deltaTime > 0 ? 1 : - 1\n\t\t\t\t\t} );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// keep running\n\n\t\t\t\t\tif ( pending === 1 ) {\n\n\t\t\t\t\t\t// entering the last round\n\n\t\t\t\t\t\tconst atStart = deltaTime < 0;\n\t\t\t\t\t\tthis._setEndings( atStart, ! atStart, pingPong );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tthis._setEndings( false, false, pingPong );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tthis._loopCount = loopCount;\n\n\t\t\t\t\tthis.time = time;\n\n\t\t\t\t\tthis._mixer.dispatchEvent( {\n\t\t\t\t\t\ttype: 'loop', action: this, loopDelta: loopDelta\n\t\t\t\t\t} );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tthis.time = time;\n\n\t\t\t}\n\n\t\t\tif ( pingPong && ( loopCount & 1 ) === 1 ) {\n\n\t\t\t\t// invert time for the \"pong round\"\n\n\t\t\t\treturn duration - time;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn time;\n\n\t}\n\n\t_setEndings( atStart, atEnd, pingPong ) {\n\n\t\tconst settings = this._interpolantSettings;\n\n\t\tif ( pingPong ) {\n\n\t\t\tsettings.endingStart = ZeroSlopeEnding;\n\t\t\tsettings.endingEnd = ZeroSlopeEnding;\n\n\t\t} else {\n\n\t\t\t// assuming for LoopOnce atStart == atEnd == true\n\n\t\t\tif ( atStart ) {\n\n\t\t\t\tsettings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding;\n\n\t\t\t} else {\n\n\t\t\t\tsettings.endingStart = WrapAroundEnding;\n\n\t\t\t}\n\n\t\t\tif ( atEnd ) {\n\n\t\t\t\tsettings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding;\n\n\t\t\t} else {\n\n\t\t\t\tsettings.endingEnd \t = WrapAroundEnding;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t_scheduleFading( duration, weightNow, weightThen ) {\n\n\t\tconst mixer = this._mixer, now = mixer.time;\n\t\tlet interpolant = this._weightInterpolant;\n\n\t\tif ( interpolant === null ) {\n\n\t\t\tinterpolant = mixer._lendControlInterpolant();\n\t\t\tthis._weightInterpolant = interpolant;\n\n\t\t}\n\n\t\tconst times = interpolant.parameterPositions,\n\t\t\tvalues = interpolant.sampleValues;\n\n\t\ttimes[ 0 ] = now;\n\t\tvalues[ 0 ] = weightNow;\n\t\ttimes[ 1 ] = now + duration;\n\t\tvalues[ 1 ] = weightThen;\n\n\t\treturn this;\n\n\t}\n\n}\n\nconst _controlInterpolantsResultBuffer = new Float32Array( 1 );\n\n\nclass AnimationMixer extends EventDispatcher {\n\n\tconstructor( root ) {\n\n\t\tsuper();\n\n\t\tthis._root = root;\n\t\tthis._initMemoryManager();\n\t\tthis._accuIndex = 0;\n\t\tthis.time = 0;\n\t\tthis.timeScale = 1.0;\n\n\t}\n\n\t_bindAction( action, prototypeAction ) {\n\n\t\tconst root = action._localRoot || this._root,\n\t\t\ttracks = action._clip.tracks,\n\t\t\tnTracks = tracks.length,\n\t\t\tbindings = action._propertyBindings,\n\t\t\tinterpolants = action._interpolants,\n\t\t\trootUuid = root.uuid,\n\t\t\tbindingsByRoot = this._bindingsByRootAndName;\n\n\t\tlet bindingsByName = bindingsByRoot[ rootUuid ];\n\n\t\tif ( bindingsByName === undefined ) {\n\n\t\t\tbindingsByName = {};\n\t\t\tbindingsByRoot[ rootUuid ] = bindingsByName;\n\n\t\t}\n\n\t\tfor ( let i = 0; i !== nTracks; ++ i ) {\n\n\t\t\tconst track = tracks[ i ],\n\t\t\t\ttrackName = track.name;\n\n\t\t\tlet binding = bindingsByName[ trackName ];\n\n\t\t\tif ( binding !== undefined ) {\n\n\t\t\t\t++ binding.referenceCount;\n\t\t\t\tbindings[ i ] = binding;\n\n\t\t\t} else {\n\n\t\t\t\tbinding = bindings[ i ];\n\n\t\t\t\tif ( binding !== undefined ) {\n\n\t\t\t\t\t// existing binding, make sure the cache knows\n\n\t\t\t\t\tif ( binding._cacheIndex === null ) {\n\n\t\t\t\t\t\t++ binding.referenceCount;\n\t\t\t\t\t\tthis._addInactiveBinding( binding, rootUuid, trackName );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tcontinue;\n\n\t\t\t\t}\n\n\t\t\t\tconst path = prototypeAction && prototypeAction.\n\t\t\t\t\t_propertyBindings[ i ].binding.parsedPath;\n\n\t\t\t\tbinding = new PropertyMixer(\n\t\t\t\t\tPropertyBinding.create( root, trackName, path ),\n\t\t\t\t\ttrack.ValueTypeName, track.getValueSize() );\n\n\t\t\t\t++ binding.referenceCount;\n\t\t\t\tthis._addInactiveBinding( binding, rootUuid, trackName );\n\n\t\t\t\tbindings[ i ] = binding;\n\n\t\t\t}\n\n\t\t\tinterpolants[ i ].resultBuffer = binding.buffer;\n\n\t\t}\n\n\t}\n\n\t_activateAction( action ) {\n\n\t\tif ( ! this._isActiveAction( action ) ) {\n\n\t\t\tif ( action._cacheIndex === null ) {\n\n\t\t\t\t// this action has been forgotten by the cache, but the user\n\t\t\t\t// appears to be still using it -> rebind\n\n\t\t\t\tconst rootUuid = ( action._localRoot || this._root ).uuid,\n\t\t\t\t\tclipUuid = action._clip.uuid,\n\t\t\t\t\tactionsForClip = this._actionsByClip[ clipUuid ];\n\n\t\t\t\tthis._bindAction( action,\n\t\t\t\t\tactionsForClip && actionsForClip.knownActions[ 0 ] );\n\n\t\t\t\tthis._addInactiveAction( action, clipUuid, rootUuid );\n\n\t\t\t}\n\n\t\t\tconst bindings = action._propertyBindings;\n\n\t\t\t// increment reference counts / sort out state\n\t\t\tfor ( let i = 0, n = bindings.length; i !== n; ++ i ) {\n\n\t\t\t\tconst binding = bindings[ i ];\n\n\t\t\t\tif ( binding.useCount ++ === 0 ) {\n\n\t\t\t\t\tthis._lendBinding( binding );\n\t\t\t\t\tbinding.saveOriginalState();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis._lendAction( action );\n\n\t\t}\n\n\t}\n\n\t_deactivateAction( action ) {\n\n\t\tif ( this._isActiveAction( action ) ) {\n\n\t\t\tconst bindings = action._propertyBindings;\n\n\t\t\t// decrement reference counts / sort out state\n\t\t\tfor ( let i = 0, n = bindings.length; i !== n; ++ i ) {\n\n\t\t\t\tconst binding = bindings[ i ];\n\n\t\t\t\tif ( -- binding.useCount === 0 ) {\n\n\t\t\t\t\tbinding.restoreOriginalState();\n\t\t\t\t\tthis._takeBackBinding( binding );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis._takeBackAction( action );\n\n\t\t}\n\n\t}\n\n\t// Memory manager\n\n\t_initMemoryManager() {\n\n\t\tthis._actions = []; // 'nActiveActions' followed by inactive ones\n\t\tthis._nActiveActions = 0;\n\n\t\tthis._actionsByClip = {};\n\t\t// inside:\n\t\t// {\n\t\t// \tknownActions: Array< AnimationAction > - used as prototypes\n\t\t// \tactionByRoot: AnimationAction - lookup\n\t\t// }\n\n\n\t\tthis._bindings = []; // 'nActiveBindings' followed by inactive ones\n\t\tthis._nActiveBindings = 0;\n\n\t\tthis._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer >\n\n\n\t\tthis._controlInterpolants = []; // same game as above\n\t\tthis._nActiveControlInterpolants = 0;\n\n\t\tconst scope = this;\n\n\t\tthis.stats = {\n\n\t\t\tactions: {\n\t\t\t\tget total() {\n\n\t\t\t\t\treturn scope._actions.length;\n\n\t\t\t\t},\n\t\t\t\tget inUse() {\n\n\t\t\t\t\treturn scope._nActiveActions;\n\n\t\t\t\t}\n\t\t\t},\n\t\t\tbindings: {\n\t\t\t\tget total() {\n\n\t\t\t\t\treturn scope._bindings.length;\n\n\t\t\t\t},\n\t\t\t\tget inUse() {\n\n\t\t\t\t\treturn scope._nActiveBindings;\n\n\t\t\t\t}\n\t\t\t},\n\t\t\tcontrolInterpolants: {\n\t\t\t\tget total() {\n\n\t\t\t\t\treturn scope._controlInterpolants.length;\n\n\t\t\t\t},\n\t\t\t\tget inUse() {\n\n\t\t\t\t\treturn scope._nActiveControlInterpolants;\n\n\t\t\t\t}\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\t// Memory management for AnimationAction objects\n\n\t_isActiveAction( action ) {\n\n\t\tconst index = action._cacheIndex;\n\t\treturn index !== null && index < this._nActiveActions;\n\n\t}\n\n\t_addInactiveAction( action, clipUuid, rootUuid ) {\n\n\t\tconst actions = this._actions,\n\t\t\tactionsByClip = this._actionsByClip;\n\n\t\tlet actionsForClip = actionsByClip[ clipUuid ];\n\n\t\tif ( actionsForClip === undefined ) {\n\n\t\t\tactionsForClip = {\n\n\t\t\t\tknownActions: [ action ],\n\t\t\t\tactionByRoot: {}\n\n\t\t\t};\n\n\t\t\taction._byClipCacheIndex = 0;\n\n\t\t\tactionsByClip[ clipUuid ] = actionsForClip;\n\n\t\t} else {\n\n\t\t\tconst knownActions = actionsForClip.knownActions;\n\n\t\t\taction._byClipCacheIndex = knownActions.length;\n\t\t\tknownActions.push( action );\n\n\t\t}\n\n\t\taction._cacheIndex = actions.length;\n\t\tactions.push( action );\n\n\t\tactionsForClip.actionByRoot[ rootUuid ] = action;\n\n\t}\n\n\t_removeInactiveAction( action ) {\n\n\t\tconst actions = this._actions,\n\t\t\tlastInactiveAction = actions[ actions.length - 1 ],\n\t\t\tcacheIndex = action._cacheIndex;\n\n\t\tlastInactiveAction._cacheIndex = cacheIndex;\n\t\tactions[ cacheIndex ] = lastInactiveAction;\n\t\tactions.pop();\n\n\t\taction._cacheIndex = null;\n\n\n\t\tconst clipUuid = action._clip.uuid,\n\t\t\tactionsByClip = this._actionsByClip,\n\t\t\tactionsForClip = actionsByClip[ clipUuid ],\n\t\t\tknownActionsForClip = actionsForClip.knownActions,\n\n\t\t\tlastKnownAction =\n\t\t\t\tknownActionsForClip[ knownActionsForClip.length - 1 ],\n\n\t\t\tbyClipCacheIndex = action._byClipCacheIndex;\n\n\t\tlastKnownAction._byClipCacheIndex = byClipCacheIndex;\n\t\tknownActionsForClip[ byClipCacheIndex ] = lastKnownAction;\n\t\tknownActionsForClip.pop();\n\n\t\taction._byClipCacheIndex = null;\n\n\n\t\tconst actionByRoot = actionsForClip.actionByRoot,\n\t\t\trootUuid = ( action._localRoot || this._root ).uuid;\n\n\t\tdelete actionByRoot[ rootUuid ];\n\n\t\tif ( knownActionsForClip.length === 0 ) {\n\n\t\t\tdelete actionsByClip[ clipUuid ];\n\n\t\t}\n\n\t\tthis._removeInactiveBindingsForAction( action );\n\n\t}\n\n\t_removeInactiveBindingsForAction( action ) {\n\n\t\tconst bindings = action._propertyBindings;\n\n\t\tfor ( let i = 0, n = bindings.length; i !== n; ++ i ) {\n\n\t\t\tconst binding = bindings[ i ];\n\n\t\t\tif ( -- binding.referenceCount === 0 ) {\n\n\t\t\t\tthis._removeInactiveBinding( binding );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t_lendAction( action ) {\n\n\t\t// [ active actions |  inactive actions  ]\n\t\t// [  active actions >| inactive actions ]\n\t\t//                 s        a\n\t\t//                  <-swap->\n\t\t//                 a        s\n\n\t\tconst actions = this._actions,\n\t\t\tprevIndex = action._cacheIndex,\n\n\t\t\tlastActiveIndex = this._nActiveActions ++,\n\n\t\t\tfirstInactiveAction = actions[ lastActiveIndex ];\n\n\t\taction._cacheIndex = lastActiveIndex;\n\t\tactions[ lastActiveIndex ] = action;\n\n\t\tfirstInactiveAction._cacheIndex = prevIndex;\n\t\tactions[ prevIndex ] = firstInactiveAction;\n\n\t}\n\n\t_takeBackAction( action ) {\n\n\t\t// [  active actions  | inactive actions ]\n\t\t// [ active actions |< inactive actions  ]\n\t\t//        a        s\n\t\t//         <-swap->\n\t\t//        s        a\n\n\t\tconst actions = this._actions,\n\t\t\tprevIndex = action._cacheIndex,\n\n\t\t\tfirstInactiveIndex = -- this._nActiveActions,\n\n\t\t\tlastActiveAction = actions[ firstInactiveIndex ];\n\n\t\taction._cacheIndex = firstInactiveIndex;\n\t\tactions[ firstInactiveIndex ] = action;\n\n\t\tlastActiveAction._cacheIndex = prevIndex;\n\t\tactions[ prevIndex ] = lastActiveAction;\n\n\t}\n\n\t// Memory management for PropertyMixer objects\n\n\t_addInactiveBinding( binding, rootUuid, trackName ) {\n\n\t\tconst bindingsByRoot = this._bindingsByRootAndName,\n\t\t\tbindings = this._bindings;\n\n\t\tlet bindingByName = bindingsByRoot[ rootUuid ];\n\n\t\tif ( bindingByName === undefined ) {\n\n\t\t\tbindingByName = {};\n\t\t\tbindingsByRoot[ rootUuid ] = bindingByName;\n\n\t\t}\n\n\t\tbindingByName[ trackName ] = binding;\n\n\t\tbinding._cacheIndex = bindings.length;\n\t\tbindings.push( binding );\n\n\t}\n\n\t_removeInactiveBinding( binding ) {\n\n\t\tconst bindings = this._bindings,\n\t\t\tpropBinding = binding.binding,\n\t\t\trootUuid = propBinding.rootNode.uuid,\n\t\t\ttrackName = propBinding.path,\n\t\t\tbindingsByRoot = this._bindingsByRootAndName,\n\t\t\tbindingByName = bindingsByRoot[ rootUuid ],\n\n\t\t\tlastInactiveBinding = bindings[ bindings.length - 1 ],\n\t\t\tcacheIndex = binding._cacheIndex;\n\n\t\tlastInactiveBinding._cacheIndex = cacheIndex;\n\t\tbindings[ cacheIndex ] = lastInactiveBinding;\n\t\tbindings.pop();\n\n\t\tdelete bindingByName[ trackName ];\n\n\t\tif ( Object.keys( bindingByName ).length === 0 ) {\n\n\t\t\tdelete bindingsByRoot[ rootUuid ];\n\n\t\t}\n\n\t}\n\n\t_lendBinding( binding ) {\n\n\t\tconst bindings = this._bindings,\n\t\t\tprevIndex = binding._cacheIndex,\n\n\t\t\tlastActiveIndex = this._nActiveBindings ++,\n\n\t\t\tfirstInactiveBinding = bindings[ lastActiveIndex ];\n\n\t\tbinding._cacheIndex = lastActiveIndex;\n\t\tbindings[ lastActiveIndex ] = binding;\n\n\t\tfirstInactiveBinding._cacheIndex = prevIndex;\n\t\tbindings[ prevIndex ] = firstInactiveBinding;\n\n\t}\n\n\t_takeBackBinding( binding ) {\n\n\t\tconst bindings = this._bindings,\n\t\t\tprevIndex = binding._cacheIndex,\n\n\t\t\tfirstInactiveIndex = -- this._nActiveBindings,\n\n\t\t\tlastActiveBinding = bindings[ firstInactiveIndex ];\n\n\t\tbinding._cacheIndex = firstInactiveIndex;\n\t\tbindings[ firstInactiveIndex ] = binding;\n\n\t\tlastActiveBinding._cacheIndex = prevIndex;\n\t\tbindings[ prevIndex ] = lastActiveBinding;\n\n\t}\n\n\n\t// Memory management of Interpolants for weight and time scale\n\n\t_lendControlInterpolant() {\n\n\t\tconst interpolants = this._controlInterpolants,\n\t\t\tlastActiveIndex = this._nActiveControlInterpolants ++;\n\n\t\tlet interpolant = interpolants[ lastActiveIndex ];\n\n\t\tif ( interpolant === undefined ) {\n\n\t\t\tinterpolant = new LinearInterpolant(\n\t\t\t\tnew Float32Array( 2 ), new Float32Array( 2 ),\n\t\t\t\t1, _controlInterpolantsResultBuffer );\n\n\t\t\tinterpolant.__cacheIndex = lastActiveIndex;\n\t\t\tinterpolants[ lastActiveIndex ] = interpolant;\n\n\t\t}\n\n\t\treturn interpolant;\n\n\t}\n\n\t_takeBackControlInterpolant( interpolant ) {\n\n\t\tconst interpolants = this._controlInterpolants,\n\t\t\tprevIndex = interpolant.__cacheIndex,\n\n\t\t\tfirstInactiveIndex = -- this._nActiveControlInterpolants,\n\n\t\t\tlastActiveInterpolant = interpolants[ firstInactiveIndex ];\n\n\t\tinterpolant.__cacheIndex = firstInactiveIndex;\n\t\tinterpolants[ firstInactiveIndex ] = interpolant;\n\n\t\tlastActiveInterpolant.__cacheIndex = prevIndex;\n\t\tinterpolants[ prevIndex ] = lastActiveInterpolant;\n\n\t}\n\n\t// return an action for a clip optionally using a custom root target\n\t// object (this method allocates a lot of dynamic memory in case a\n\t// previously unknown clip/root combination is specified)\n\tclipAction( clip, optionalRoot, blendMode ) {\n\n\t\tconst root = optionalRoot || this._root,\n\t\t\trootUuid = root.uuid;\n\n\t\tlet clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip;\n\n\t\tconst clipUuid = clipObject !== null ? clipObject.uuid : clip;\n\n\t\tconst actionsForClip = this._actionsByClip[ clipUuid ];\n\t\tlet prototypeAction = null;\n\n\t\tif ( blendMode === undefined ) {\n\n\t\t\tif ( clipObject !== null ) {\n\n\t\t\t\tblendMode = clipObject.blendMode;\n\n\t\t\t} else {\n\n\t\t\t\tblendMode = NormalAnimationBlendMode;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( actionsForClip !== undefined ) {\n\n\t\t\tconst existingAction = actionsForClip.actionByRoot[ rootUuid ];\n\n\t\t\tif ( existingAction !== undefined && existingAction.blendMode === blendMode ) {\n\n\t\t\t\treturn existingAction;\n\n\t\t\t}\n\n\t\t\t// we know the clip, so we don't have to parse all\n\t\t\t// the bindings again but can just copy\n\t\t\tprototypeAction = actionsForClip.knownActions[ 0 ];\n\n\t\t\t// also, take the clip from the prototype action\n\t\t\tif ( clipObject === null )\n\t\t\t\tclipObject = prototypeAction._clip;\n\n\t\t}\n\n\t\t// clip must be known when specified via string\n\t\tif ( clipObject === null ) return null;\n\n\t\t// allocate all resources required to run it\n\t\tconst newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode );\n\n\t\tthis._bindAction( newAction, prototypeAction );\n\n\t\t// and make the action known to the memory manager\n\t\tthis._addInactiveAction( newAction, clipUuid, rootUuid );\n\n\t\treturn newAction;\n\n\t}\n\n\t// get an existing action\n\texistingAction( clip, optionalRoot ) {\n\n\t\tconst root = optionalRoot || this._root,\n\t\t\trootUuid = root.uuid,\n\n\t\t\tclipObject = typeof clip === 'string' ?\n\t\t\t\tAnimationClip.findByName( root, clip ) : clip,\n\n\t\t\tclipUuid = clipObject ? clipObject.uuid : clip,\n\n\t\t\tactionsForClip = this._actionsByClip[ clipUuid ];\n\n\t\tif ( actionsForClip !== undefined ) {\n\n\t\t\treturn actionsForClip.actionByRoot[ rootUuid ] || null;\n\n\t\t}\n\n\t\treturn null;\n\n\t}\n\n\t// deactivates all previously scheduled actions\n\tstopAllAction() {\n\n\t\tconst actions = this._actions,\n\t\t\tnActions = this._nActiveActions;\n\n\t\tfor ( let i = nActions - 1; i >= 0; -- i ) {\n\n\t\t\tactions[ i ].stop();\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\t// advance the time and update apply the animation\n\tupdate( deltaTime ) {\n\n\t\tdeltaTime *= this.timeScale;\n\n\t\tconst actions = this._actions,\n\t\t\tnActions = this._nActiveActions,\n\n\t\t\ttime = this.time += deltaTime,\n\t\t\ttimeDirection = Math.sign( deltaTime ),\n\n\t\t\taccuIndex = this._accuIndex ^= 1;\n\n\t\t// run active actions\n\n\t\tfor ( let i = 0; i !== nActions; ++ i ) {\n\n\t\t\tconst action = actions[ i ];\n\n\t\t\taction._update( time, deltaTime, timeDirection, accuIndex );\n\n\t\t}\n\n\t\t// update scene graph\n\n\t\tconst bindings = this._bindings,\n\t\t\tnBindings = this._nActiveBindings;\n\n\t\tfor ( let i = 0; i !== nBindings; ++ i ) {\n\n\t\t\tbindings[ i ].apply( accuIndex );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\t// Allows you to seek to a specific time in an animation.\n\tsetTime( timeInSeconds ) {\n\n\t\tthis.time = 0; // Zero out time attribute for AnimationMixer object;\n\t\tfor ( let i = 0; i < this._actions.length; i ++ ) {\n\n\t\t\tthis._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects.\n\n\t\t}\n\n\t\treturn this.update( timeInSeconds ); // Update used to set exact time. Returns \"this\" AnimationMixer object.\n\n\t}\n\n\t// return this mixer's root target object\n\tgetRoot() {\n\n\t\treturn this._root;\n\n\t}\n\n\t// free all resources specific to a particular clip\n\tuncacheClip( clip ) {\n\n\t\tconst actions = this._actions,\n\t\t\tclipUuid = clip.uuid,\n\t\t\tactionsByClip = this._actionsByClip,\n\t\t\tactionsForClip = actionsByClip[ clipUuid ];\n\n\t\tif ( actionsForClip !== undefined ) {\n\n\t\t\t// note: just calling _removeInactiveAction would mess up the\n\t\t\t// iteration state and also require updating the state we can\n\t\t\t// just throw away\n\n\t\t\tconst actionsToRemove = actionsForClip.knownActions;\n\n\t\t\tfor ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) {\n\n\t\t\t\tconst action = actionsToRemove[ i ];\n\n\t\t\t\tthis._deactivateAction( action );\n\n\t\t\t\tconst cacheIndex = action._cacheIndex,\n\t\t\t\t\tlastInactiveAction = actions[ actions.length - 1 ];\n\n\t\t\t\taction._cacheIndex = null;\n\t\t\t\taction._byClipCacheIndex = null;\n\n\t\t\t\tlastInactiveAction._cacheIndex = cacheIndex;\n\t\t\t\tactions[ cacheIndex ] = lastInactiveAction;\n\t\t\t\tactions.pop();\n\n\t\t\t\tthis._removeInactiveBindingsForAction( action );\n\n\t\t\t}\n\n\t\t\tdelete actionsByClip[ clipUuid ];\n\n\t\t}\n\n\t}\n\n\t// free all resources specific to a particular root target object\n\tuncacheRoot( root ) {\n\n\t\tconst rootUuid = root.uuid,\n\t\t\tactionsByClip = this._actionsByClip;\n\n\t\tfor ( const clipUuid in actionsByClip ) {\n\n\t\t\tconst actionByRoot = actionsByClip[ clipUuid ].actionByRoot,\n\t\t\t\taction = actionByRoot[ rootUuid ];\n\n\t\t\tif ( action !== undefined ) {\n\n\t\t\t\tthis._deactivateAction( action );\n\t\t\t\tthis._removeInactiveAction( action );\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst bindingsByRoot = this._bindingsByRootAndName,\n\t\t\tbindingByName = bindingsByRoot[ rootUuid ];\n\n\t\tif ( bindingByName !== undefined ) {\n\n\t\t\tfor ( const trackName in bindingByName ) {\n\n\t\t\t\tconst binding = bindingByName[ trackName ];\n\t\t\t\tbinding.restoreOriginalState();\n\t\t\t\tthis._removeInactiveBinding( binding );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t// remove a targeted clip from the cache\n\tuncacheAction( clip, optionalRoot ) {\n\n\t\tconst action = this.existingAction( clip, optionalRoot );\n\n\t\tif ( action !== null ) {\n\n\t\t\tthis._deactivateAction( action );\n\t\t\tthis._removeInactiveAction( action );\n\n\t\t}\n\n\t}\n\n}\n\nclass Uniform {\n\n\tconstructor( value ) {\n\n\t\tthis.value = value;\n\n\t}\n\n\tclone() {\n\n\t\treturn new Uniform( this.value.clone === undefined ? this.value : this.value.clone() );\n\n\t}\n\n}\n\nlet _id = 0;\n\nclass UniformsGroup extends EventDispatcher {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isUniformsGroup = true;\n\n\t\tObject.defineProperty( this, 'id', { value: _id ++ } );\n\n\t\tthis.name = '';\n\n\t\tthis.usage = StaticDrawUsage;\n\t\tthis.uniforms = [];\n\n\t}\n\n\tadd( uniform ) {\n\n\t\tthis.uniforms.push( uniform );\n\n\t\treturn this;\n\n\t}\n\n\tremove( uniform ) {\n\n\t\tconst index = this.uniforms.indexOf( uniform );\n\n\t\tif ( index !== - 1 ) this.uniforms.splice( index, 1 );\n\n\t\treturn this;\n\n\t}\n\n\tsetName( name ) {\n\n\t\tthis.name = name;\n\n\t\treturn this;\n\n\t}\n\n\tsetUsage( value ) {\n\n\t\tthis.usage = value;\n\n\t\treturn this;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t\treturn this;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.name = source.name;\n\t\tthis.usage = source.usage;\n\n\t\tconst uniformsSource = source.uniforms;\n\n\t\tthis.uniforms.length = 0;\n\n\t\tfor ( let i = 0, l = uniformsSource.length; i < l; i ++ ) {\n\n\t\t\tconst uniforms = Array.isArray( uniformsSource[ i ] ) ? uniformsSource[ i ] : [ uniformsSource[ i ] ];\n\n\t\t\tfor ( let j = 0; j < uniforms.length; j ++ ) {\n\n\t\t\t\tthis.uniforms.push( uniforms[ j ].clone() );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\nclass InstancedInterleavedBuffer extends InterleavedBuffer {\n\n\tconstructor( array, stride, meshPerAttribute = 1 ) {\n\n\t\tsuper( array, stride );\n\n\t\tthis.isInstancedInterleavedBuffer = true;\n\n\t\tthis.meshPerAttribute = meshPerAttribute;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.meshPerAttribute = source.meshPerAttribute;\n\n\t\treturn this;\n\n\t}\n\n\tclone( data ) {\n\n\t\tconst ib = super.clone( data );\n\n\t\tib.meshPerAttribute = this.meshPerAttribute;\n\n\t\treturn ib;\n\n\t}\n\n\ttoJSON( data ) {\n\n\t\tconst json = super.toJSON( data );\n\n\t\tjson.isInstancedInterleavedBuffer = true;\n\t\tjson.meshPerAttribute = this.meshPerAttribute;\n\n\t\treturn json;\n\n\t}\n\n}\n\nclass GLBufferAttribute {\n\n\tconstructor( buffer, type, itemSize, elementSize, count ) {\n\n\t\tthis.isGLBufferAttribute = true;\n\n\t\tthis.name = '';\n\n\t\tthis.buffer = buffer;\n\t\tthis.type = type;\n\t\tthis.itemSize = itemSize;\n\t\tthis.elementSize = elementSize;\n\t\tthis.count = count;\n\n\t\tthis.version = 0;\n\n\t}\n\n\tset needsUpdate( value ) {\n\n\t\tif ( value === true ) this.version ++;\n\n\t}\n\n\tsetBuffer( buffer ) {\n\n\t\tthis.buffer = buffer;\n\n\t\treturn this;\n\n\t}\n\n\tsetType( type, elementSize ) {\n\n\t\tthis.type = type;\n\t\tthis.elementSize = elementSize;\n\n\t\treturn this;\n\n\t}\n\n\tsetItemSize( itemSize ) {\n\n\t\tthis.itemSize = itemSize;\n\n\t\treturn this;\n\n\t}\n\n\tsetCount( count ) {\n\n\t\tthis.count = count;\n\n\t\treturn this;\n\n\t}\n\n}\n\nclass Raycaster {\n\n\tconstructor( origin, direction, near = 0, far = Infinity ) {\n\n\t\tthis.ray = new Ray( origin, direction );\n\t\t// direction is assumed to be normalized (for accurate distance calculations)\n\n\t\tthis.near = near;\n\t\tthis.far = far;\n\t\tthis.camera = null;\n\t\tthis.layers = new Layers();\n\n\t\tthis.params = {\n\t\t\tMesh: {},\n\t\t\tLine: { threshold: 1 },\n\t\t\tLOD: {},\n\t\t\tPoints: { threshold: 1 },\n\t\t\tSprite: {}\n\t\t};\n\n\t}\n\n\tset( origin, direction ) {\n\n\t\t// direction is assumed to be normalized (for accurate distance calculations)\n\n\t\tthis.ray.set( origin, direction );\n\n\t}\n\n\tsetFromCamera( coords, camera ) {\n\n\t\tif ( camera.isPerspectiveCamera ) {\n\n\t\t\tthis.ray.origin.setFromMatrixPosition( camera.matrixWorld );\n\t\t\tthis.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize();\n\t\t\tthis.camera = camera;\n\n\t\t} else if ( camera.isOrthographicCamera ) {\n\n\t\t\tthis.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera\n\t\t\tthis.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );\n\t\t\tthis.camera = camera;\n\n\t\t} else {\n\n\t\t\tconsole.error( 'THREE.Raycaster: Unsupported camera type: ' + camera.type );\n\n\t\t}\n\n\t}\n\n\tintersectObject( object, recursive = true, intersects = [] ) {\n\n\t\tintersectObject( object, this, intersects, recursive );\n\n\t\tintersects.sort( ascSort );\n\n\t\treturn intersects;\n\n\t}\n\n\tintersectObjects( objects, recursive = true, intersects = [] ) {\n\n\t\tfor ( let i = 0, l = objects.length; i < l; i ++ ) {\n\n\t\t\tintersectObject( objects[ i ], this, intersects, recursive );\n\n\t\t}\n\n\t\tintersects.sort( ascSort );\n\n\t\treturn intersects;\n\n\t}\n\n}\n\nfunction ascSort( a, b ) {\n\n\treturn a.distance - b.distance;\n\n}\n\nfunction intersectObject( object, raycaster, intersects, recursive ) {\n\n\tif ( object.layers.test( raycaster.layers ) ) {\n\n\t\tobject.raycast( raycaster, intersects );\n\n\t}\n\n\tif ( recursive === true ) {\n\n\t\tconst children = object.children;\n\n\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\tintersectObject( children[ i ], raycaster, intersects, true );\n\n\t\t}\n\n\t}\n\n}\n\n/**\n * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system\n *\n * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up.\n * The azimuthal angle (theta) is measured from the positive z-axis.\n */\n\n\nclass Spherical {\n\n\tconstructor( radius = 1, phi = 0, theta = 0 ) {\n\n\t\tthis.radius = radius;\n\t\tthis.phi = phi; // polar angle\n\t\tthis.theta = theta; // azimuthal angle\n\n\t\treturn this;\n\n\t}\n\n\tset( radius, phi, theta ) {\n\n\t\tthis.radius = radius;\n\t\tthis.phi = phi;\n\t\tthis.theta = theta;\n\n\t\treturn this;\n\n\t}\n\n\tcopy( other ) {\n\n\t\tthis.radius = other.radius;\n\t\tthis.phi = other.phi;\n\t\tthis.theta = other.theta;\n\n\t\treturn this;\n\n\t}\n\n\t// restrict phi to be between EPS and PI-EPS\n\tmakeSafe() {\n\n\t\tconst EPS = 0.000001;\n\t\tthis.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromVector3( v ) {\n\n\t\treturn this.setFromCartesianCoords( v.x, v.y, v.z );\n\n\t}\n\n\tsetFromCartesianCoords( x, y, z ) {\n\n\t\tthis.radius = Math.sqrt( x * x + y * y + z * z );\n\n\t\tif ( this.radius === 0 ) {\n\n\t\t\tthis.theta = 0;\n\t\t\tthis.phi = 0;\n\n\t\t} else {\n\n\t\t\tthis.theta = Math.atan2( x, z );\n\t\t\tthis.phi = Math.acos( clamp( y / this.radius, - 1, 1 ) );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\n/**\n * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system\n */\n\nclass Cylindrical {\n\n\tconstructor( radius = 1, theta = 0, y = 0 ) {\n\n\t\tthis.radius = radius; // distance from the origin to a point in the x-z plane\n\t\tthis.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis\n\t\tthis.y = y; // height above the x-z plane\n\n\t\treturn this;\n\n\t}\n\n\tset( radius, theta, y ) {\n\n\t\tthis.radius = radius;\n\t\tthis.theta = theta;\n\t\tthis.y = y;\n\n\t\treturn this;\n\n\t}\n\n\tcopy( other ) {\n\n\t\tthis.radius = other.radius;\n\t\tthis.theta = other.theta;\n\t\tthis.y = other.y;\n\n\t\treturn this;\n\n\t}\n\n\tsetFromVector3( v ) {\n\n\t\treturn this.setFromCartesianCoords( v.x, v.y, v.z );\n\n\t}\n\n\tsetFromCartesianCoords( x, y, z ) {\n\n\t\tthis.radius = Math.sqrt( x * x + z * z );\n\t\tthis.theta = Math.atan2( x, z );\n\t\tthis.y = y;\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\nconst _vector$4 = /*@__PURE__*/ new Vector2();\n\nclass Box2 {\n\n\tconstructor( min = new Vector2( + Infinity, + Infinity ), max = new Vector2( - Infinity, - Infinity ) ) {\n\n\t\tthis.isBox2 = true;\n\n\t\tthis.min = min;\n\t\tthis.max = max;\n\n\t}\n\n\tset( min, max ) {\n\n\t\tthis.min.copy( min );\n\t\tthis.max.copy( max );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromPoints( points ) {\n\n\t\tthis.makeEmpty();\n\n\t\tfor ( let i = 0, il = points.length; i < il; i ++ ) {\n\n\t\t\tthis.expandByPoint( points[ i ] );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetFromCenterAndSize( center, size ) {\n\n\t\tconst halfSize = _vector$4.copy( size ).multiplyScalar( 0.5 );\n\t\tthis.min.copy( center ).sub( halfSize );\n\t\tthis.max.copy( center ).add( halfSize );\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( box ) {\n\n\t\tthis.min.copy( box.min );\n\t\tthis.max.copy( box.max );\n\n\t\treturn this;\n\n\t}\n\n\tmakeEmpty() {\n\n\t\tthis.min.x = this.min.y = + Infinity;\n\t\tthis.max.x = this.max.y = - Infinity;\n\n\t\treturn this;\n\n\t}\n\n\tisEmpty() {\n\n\t\t// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes\n\n\t\treturn ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );\n\n\t}\n\n\tgetCenter( target ) {\n\n\t\treturn this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );\n\n\t}\n\n\tgetSize( target ) {\n\n\t\treturn this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min );\n\n\t}\n\n\texpandByPoint( point ) {\n\n\t\tthis.min.min( point );\n\t\tthis.max.max( point );\n\n\t\treturn this;\n\n\t}\n\n\texpandByVector( vector ) {\n\n\t\tthis.min.sub( vector );\n\t\tthis.max.add( vector );\n\n\t\treturn this;\n\n\t}\n\n\texpandByScalar( scalar ) {\n\n\t\tthis.min.addScalar( - scalar );\n\t\tthis.max.addScalar( scalar );\n\n\t\treturn this;\n\n\t}\n\n\tcontainsPoint( point ) {\n\n\t\treturn point.x < this.min.x || point.x > this.max.x ||\n\t\t\tpoint.y < this.min.y || point.y > this.max.y ? false : true;\n\n\t}\n\n\tcontainsBox( box ) {\n\n\t\treturn this.min.x <= box.min.x && box.max.x <= this.max.x &&\n\t\t\tthis.min.y <= box.min.y && box.max.y <= this.max.y;\n\n\t}\n\n\tgetParameter( point, target ) {\n\n\t\t// This can potentially have a divide by zero if the box\n\t\t// has a size dimension of 0.\n\n\t\treturn target.set(\n\t\t\t( point.x - this.min.x ) / ( this.max.x - this.min.x ),\n\t\t\t( point.y - this.min.y ) / ( this.max.y - this.min.y )\n\t\t);\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\t// using 4 splitting planes to rule out intersections\n\n\t\treturn box.max.x < this.min.x || box.min.x > this.max.x ||\n\t\t\tbox.max.y < this.min.y || box.min.y > this.max.y ? false : true;\n\n\t}\n\n\tclampPoint( point, target ) {\n\n\t\treturn target.copy( point ).clamp( this.min, this.max );\n\n\t}\n\n\tdistanceToPoint( point ) {\n\n\t\treturn this.clampPoint( point, _vector$4 ).distanceTo( point );\n\n\t}\n\n\tintersect( box ) {\n\n\t\tthis.min.max( box.min );\n\t\tthis.max.min( box.max );\n\n\t\tif ( this.isEmpty() ) this.makeEmpty();\n\n\t\treturn this;\n\n\t}\n\n\tunion( box ) {\n\n\t\tthis.min.min( box.min );\n\t\tthis.max.max( box.max );\n\n\t\treturn this;\n\n\t}\n\n\ttranslate( offset ) {\n\n\t\tthis.min.add( offset );\n\t\tthis.max.add( offset );\n\n\t\treturn this;\n\n\t}\n\n\tequals( box ) {\n\n\t\treturn box.min.equals( this.min ) && box.max.equals( this.max );\n\n\t}\n\n}\n\nconst _startP = /*@__PURE__*/ new Vector3();\nconst _startEnd = /*@__PURE__*/ new Vector3();\n\nclass Line3 {\n\n\tconstructor( start = new Vector3(), end = new Vector3() ) {\n\n\t\tthis.start = start;\n\t\tthis.end = end;\n\n\t}\n\n\tset( start, end ) {\n\n\t\tthis.start.copy( start );\n\t\tthis.end.copy( end );\n\n\t\treturn this;\n\n\t}\n\n\tcopy( line ) {\n\n\t\tthis.start.copy( line.start );\n\t\tthis.end.copy( line.end );\n\n\t\treturn this;\n\n\t}\n\n\tgetCenter( target ) {\n\n\t\treturn target.addVectors( this.start, this.end ).multiplyScalar( 0.5 );\n\n\t}\n\n\tdelta( target ) {\n\n\t\treturn target.subVectors( this.end, this.start );\n\n\t}\n\n\tdistanceSq() {\n\n\t\treturn this.start.distanceToSquared( this.end );\n\n\t}\n\n\tdistance() {\n\n\t\treturn this.start.distanceTo( this.end );\n\n\t}\n\n\tat( t, target ) {\n\n\t\treturn this.delta( target ).multiplyScalar( t ).add( this.start );\n\n\t}\n\n\tclosestPointToPointParameter( point, clampToLine ) {\n\n\t\t_startP.subVectors( point, this.start );\n\t\t_startEnd.subVectors( this.end, this.start );\n\n\t\tconst startEnd2 = _startEnd.dot( _startEnd );\n\t\tconst startEnd_startP = _startEnd.dot( _startP );\n\n\t\tlet t = startEnd_startP / startEnd2;\n\n\t\tif ( clampToLine ) {\n\n\t\t\tt = clamp( t, 0, 1 );\n\n\t\t}\n\n\t\treturn t;\n\n\t}\n\n\tclosestPointToPoint( point, clampToLine, target ) {\n\n\t\tconst t = this.closestPointToPointParameter( point, clampToLine );\n\n\t\treturn this.delta( target ).multiplyScalar( t ).add( this.start );\n\n\t}\n\n\tapplyMatrix4( matrix ) {\n\n\t\tthis.start.applyMatrix4( matrix );\n\t\tthis.end.applyMatrix4( matrix );\n\n\t\treturn this;\n\n\t}\n\n\tequals( line ) {\n\n\t\treturn line.start.equals( this.start ) && line.end.equals( this.end );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\nconst _vector$3 = /*@__PURE__*/ new Vector3();\n\nclass SpotLightHelper extends Object3D {\n\n\tconstructor( light, color ) {\n\n\t\tsuper();\n\n\t\tthis.light = light;\n\n\t\tthis.matrix = light.matrixWorld;\n\t\tthis.matrixAutoUpdate = false;\n\n\t\tthis.color = color;\n\n\t\tthis.type = 'SpotLightHelper';\n\n\t\tconst geometry = new BufferGeometry();\n\n\t\tconst positions = [\n\t\t\t0, 0, 0, \t0, 0, 1,\n\t\t\t0, 0, 0, \t1, 0, 1,\n\t\t\t0, 0, 0,\t- 1, 0, 1,\n\t\t\t0, 0, 0, \t0, 1, 1,\n\t\t\t0, 0, 0, \t0, - 1, 1\n\t\t];\n\n\t\tfor ( let i = 0, j = 1, l = 32; i < l; i ++, j ++ ) {\n\n\t\t\tconst p1 = ( i / l ) * Math.PI * 2;\n\t\t\tconst p2 = ( j / l ) * Math.PI * 2;\n\n\t\t\tpositions.push(\n\t\t\t\tMath.cos( p1 ), Math.sin( p1 ), 1,\n\t\t\t\tMath.cos( p2 ), Math.sin( p2 ), 1\n\t\t\t);\n\n\t\t}\n\n\t\tgeometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );\n\n\t\tconst material = new LineBasicMaterial( { fog: false, toneMapped: false } );\n\n\t\tthis.cone = new LineSegments( geometry, material );\n\t\tthis.add( this.cone );\n\n\t\tthis.update();\n\n\t}\n\n\tdispose() {\n\n\t\tthis.cone.geometry.dispose();\n\t\tthis.cone.material.dispose();\n\n\t}\n\n\tupdate() {\n\n\t\tthis.light.updateWorldMatrix( true, false );\n\t\tthis.light.target.updateWorldMatrix( true, false );\n\n\t\tconst coneLength = this.light.distance ? this.light.distance : 1000;\n\t\tconst coneWidth = coneLength * Math.tan( this.light.angle );\n\n\t\tthis.cone.scale.set( coneWidth, coneWidth, coneLength );\n\n\t\t_vector$3.setFromMatrixPosition( this.light.target.matrixWorld );\n\n\t\tthis.cone.lookAt( _vector$3 );\n\n\t\tif ( this.color !== undefined ) {\n\n\t\t\tthis.cone.material.color.set( this.color );\n\n\t\t} else {\n\n\t\t\tthis.cone.material.color.copy( this.light.color );\n\n\t\t}\n\n\t}\n\n}\n\nconst _vector$2 = /*@__PURE__*/ new Vector3();\nconst _boneMatrix = /*@__PURE__*/ new Matrix4();\nconst _matrixWorldInv = /*@__PURE__*/ new Matrix4();\n\n\nclass SkeletonHelper extends LineSegments {\n\n\tconstructor( object ) {\n\n\t\tconst bones = getBoneList( object );\n\n\t\tconst geometry = new BufferGeometry();\n\n\t\tconst vertices = [];\n\t\tconst colors = [];\n\n\t\tconst color1 = new Color( 0, 0, 1 );\n\t\tconst color2 = new Color( 0, 1, 0 );\n\n\t\tfor ( let i = 0; i < bones.length; i ++ ) {\n\n\t\t\tconst bone = bones[ i ];\n\n\t\t\tif ( bone.parent && bone.parent.isBone ) {\n\n\t\t\t\tvertices.push( 0, 0, 0 );\n\t\t\t\tvertices.push( 0, 0, 0 );\n\t\t\t\tcolors.push( color1.r, color1.g, color1.b );\n\t\t\t\tcolors.push( color2.r, color2.g, color2.b );\n\n\t\t\t}\n\n\t\t}\n\n\t\tgeometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tgeometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n\t\tconst material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } );\n\n\t\tsuper( geometry, material );\n\n\t\tthis.isSkeletonHelper = true;\n\n\t\tthis.type = 'SkeletonHelper';\n\n\t\tthis.root = object;\n\t\tthis.bones = bones;\n\n\t\tthis.matrix = object.matrixWorld;\n\t\tthis.matrixAutoUpdate = false;\n\n\t}\n\n\tupdateMatrixWorld( force ) {\n\n\t\tconst bones = this.bones;\n\n\t\tconst geometry = this.geometry;\n\t\tconst position = geometry.getAttribute( 'position' );\n\n\t\t_matrixWorldInv.copy( this.root.matrixWorld ).invert();\n\n\t\tfor ( let i = 0, j = 0; i < bones.length; i ++ ) {\n\n\t\t\tconst bone = bones[ i ];\n\n\t\t\tif ( bone.parent && bone.parent.isBone ) {\n\n\t\t\t\t_boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld );\n\t\t\t\t_vector$2.setFromMatrixPosition( _boneMatrix );\n\t\t\t\tposition.setXYZ( j, _vector$2.x, _vector$2.y, _vector$2.z );\n\n\t\t\t\t_boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld );\n\t\t\t\t_vector$2.setFromMatrixPosition( _boneMatrix );\n\t\t\t\tposition.setXYZ( j + 1, _vector$2.x, _vector$2.y, _vector$2.z );\n\n\t\t\t\tj += 2;\n\n\t\t\t}\n\n\t\t}\n\n\t\tgeometry.getAttribute( 'position' ).needsUpdate = true;\n\n\t\tsuper.updateMatrixWorld( force );\n\n\t}\n\n\tdispose() {\n\n\t\tthis.geometry.dispose();\n\t\tthis.material.dispose();\n\n\t}\n\n}\n\n\nfunction getBoneList( object ) {\n\n\tconst boneList = [];\n\n\tif ( object.isBone === true ) {\n\n\t\tboneList.push( object );\n\n\t}\n\n\tfor ( let i = 0; i < object.children.length; i ++ ) {\n\n\t\tboneList.push.apply( boneList, getBoneList( object.children[ i ] ) );\n\n\t}\n\n\treturn boneList;\n\n}\n\nclass PointLightHelper extends Mesh {\n\n\tconstructor( light, sphereSize, color ) {\n\n\t\tconst geometry = new SphereGeometry( sphereSize, 4, 2 );\n\t\tconst material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } );\n\n\t\tsuper( geometry, material );\n\n\t\tthis.light = light;\n\n\t\tthis.color = color;\n\n\t\tthis.type = 'PointLightHelper';\n\n\t\tthis.matrix = this.light.matrixWorld;\n\t\tthis.matrixAutoUpdate = false;\n\n\t\tthis.update();\n\n\n\t\t/*\n\t// TODO: delete this comment?\n\tconst distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );\n\tconst distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );\n\n\tthis.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );\n\tthis.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );\n\n\tconst d = light.distance;\n\n\tif ( d === 0.0 ) {\n\n\t\tthis.lightDistance.visible = false;\n\n\t} else {\n\n\t\tthis.lightDistance.scale.set( d, d, d );\n\n\t}\n\n\tthis.add( this.lightDistance );\n\t*/\n\n\t}\n\n\tdispose() {\n\n\t\tthis.geometry.dispose();\n\t\tthis.material.dispose();\n\n\t}\n\n\tupdate() {\n\n\t\tthis.light.updateWorldMatrix( true, false );\n\n\t\tif ( this.color !== undefined ) {\n\n\t\t\tthis.material.color.set( this.color );\n\n\t\t} else {\n\n\t\t\tthis.material.color.copy( this.light.color );\n\n\t\t}\n\n\t\t/*\n\t\tconst d = this.light.distance;\n\n\t\tif ( d === 0.0 ) {\n\n\t\t\tthis.lightDistance.visible = false;\n\n\t\t} else {\n\n\t\t\tthis.lightDistance.visible = true;\n\t\t\tthis.lightDistance.scale.set( d, d, d );\n\n\t\t}\n\t\t*/\n\n\t}\n\n}\n\nconst _vector$1 = /*@__PURE__*/ new Vector3();\nconst _color1 = /*@__PURE__*/ new Color();\nconst _color2 = /*@__PURE__*/ new Color();\n\nclass HemisphereLightHelper extends Object3D {\n\n\tconstructor( light, size, color ) {\n\n\t\tsuper();\n\n\t\tthis.light = light;\n\n\t\tthis.matrix = light.matrixWorld;\n\t\tthis.matrixAutoUpdate = false;\n\n\t\tthis.color = color;\n\n\t\tthis.type = 'HemisphereLightHelper';\n\n\t\tconst geometry = new OctahedronGeometry( size );\n\t\tgeometry.rotateY( Math.PI * 0.5 );\n\n\t\tthis.material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } );\n\t\tif ( this.color === undefined ) this.material.vertexColors = true;\n\n\t\tconst position = geometry.getAttribute( 'position' );\n\t\tconst colors = new Float32Array( position.count * 3 );\n\n\t\tgeometry.setAttribute( 'color', new BufferAttribute( colors, 3 ) );\n\n\t\tthis.add( new Mesh( geometry, this.material ) );\n\n\t\tthis.update();\n\n\t}\n\n\tdispose() {\n\n\t\tthis.children[ 0 ].geometry.dispose();\n\t\tthis.children[ 0 ].material.dispose();\n\n\t}\n\n\tupdate() {\n\n\t\tconst mesh = this.children[ 0 ];\n\n\t\tif ( this.color !== undefined ) {\n\n\t\t\tthis.material.color.set( this.color );\n\n\t\t} else {\n\n\t\t\tconst colors = mesh.geometry.getAttribute( 'color' );\n\n\t\t\t_color1.copy( this.light.color );\n\t\t\t_color2.copy( this.light.groundColor );\n\n\t\t\tfor ( let i = 0, l = colors.count; i < l; i ++ ) {\n\n\t\t\t\tconst color = ( i < ( l / 2 ) ) ? _color1 : _color2;\n\n\t\t\t\tcolors.setXYZ( i, color.r, color.g, color.b );\n\n\t\t\t}\n\n\t\t\tcolors.needsUpdate = true;\n\n\t\t}\n\n\t\tthis.light.updateWorldMatrix( true, false );\n\n\t\tmesh.lookAt( _vector$1.setFromMatrixPosition( this.light.matrixWorld ).negate() );\n\n\t}\n\n}\n\nclass GridHelper extends LineSegments {\n\n\tconstructor( size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888 ) {\n\n\t\tcolor1 = new Color( color1 );\n\t\tcolor2 = new Color( color2 );\n\n\t\tconst center = divisions / 2;\n\t\tconst step = size / divisions;\n\t\tconst halfSize = size / 2;\n\n\t\tconst vertices = [], colors = [];\n\n\t\tfor ( let i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) {\n\n\t\t\tvertices.push( - halfSize, 0, k, halfSize, 0, k );\n\t\t\tvertices.push( k, 0, - halfSize, k, 0, halfSize );\n\n\t\t\tconst color = i === center ? color1 : color2;\n\n\t\t\tcolor.toArray( colors, j ); j += 3;\n\t\t\tcolor.toArray( colors, j ); j += 3;\n\t\t\tcolor.toArray( colors, j ); j += 3;\n\t\t\tcolor.toArray( colors, j ); j += 3;\n\n\t\t}\n\n\t\tconst geometry = new BufferGeometry();\n\t\tgeometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tgeometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n\t\tconst material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } );\n\n\t\tsuper( geometry, material );\n\n\t\tthis.type = 'GridHelper';\n\n\t}\n\n\tdispose() {\n\n\t\tthis.geometry.dispose();\n\t\tthis.material.dispose();\n\n\t}\n\n}\n\nclass PolarGridHelper extends LineSegments {\n\n\tconstructor( radius = 10, sectors = 16, rings = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888 ) {\n\n\t\tcolor1 = new Color( color1 );\n\t\tcolor2 = new Color( color2 );\n\n\t\tconst vertices = [];\n\t\tconst colors = [];\n\n\t\t// create the sectors\n\n\t\tif ( sectors > 1 ) {\n\n\t\t\tfor ( let i = 0; i < sectors; i ++ ) {\n\n\t\t\t\tconst v = ( i / sectors ) * ( Math.PI * 2 );\n\n\t\t\t\tconst x = Math.sin( v ) * radius;\n\t\t\t\tconst z = Math.cos( v ) * radius;\n\n\t\t\t\tvertices.push( 0, 0, 0 );\n\t\t\t\tvertices.push( x, 0, z );\n\n\t\t\t\tconst color = ( i & 1 ) ? color1 : color2;\n\n\t\t\t\tcolors.push( color.r, color.g, color.b );\n\t\t\t\tcolors.push( color.r, color.g, color.b );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// create the rings\n\n\t\tfor ( let i = 0; i < rings; i ++ ) {\n\n\t\t\tconst color = ( i & 1 ) ? color1 : color2;\n\n\t\t\tconst r = radius - ( radius / rings * i );\n\n\t\t\tfor ( let j = 0; j < divisions; j ++ ) {\n\n\t\t\t\t// first vertex\n\n\t\t\t\tlet v = ( j / divisions ) * ( Math.PI * 2 );\n\n\t\t\t\tlet x = Math.sin( v ) * r;\n\t\t\t\tlet z = Math.cos( v ) * r;\n\n\t\t\t\tvertices.push( x, 0, z );\n\t\t\t\tcolors.push( color.r, color.g, color.b );\n\n\t\t\t\t// second vertex\n\n\t\t\t\tv = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 );\n\n\t\t\t\tx = Math.sin( v ) * r;\n\t\t\t\tz = Math.cos( v ) * r;\n\n\t\t\t\tvertices.push( x, 0, z );\n\t\t\t\tcolors.push( color.r, color.g, color.b );\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst geometry = new BufferGeometry();\n\t\tgeometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tgeometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n\t\tconst material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } );\n\n\t\tsuper( geometry, material );\n\n\t\tthis.type = 'PolarGridHelper';\n\n\t}\n\n\tdispose() {\n\n\t\tthis.geometry.dispose();\n\t\tthis.material.dispose();\n\n\t}\n\n}\n\nconst _v1 = /*@__PURE__*/ new Vector3();\nconst _v2 = /*@__PURE__*/ new Vector3();\nconst _v3 = /*@__PURE__*/ new Vector3();\n\nclass DirectionalLightHelper extends Object3D {\n\n\tconstructor( light, size, color ) {\n\n\t\tsuper();\n\n\t\tthis.light = light;\n\n\t\tthis.matrix = light.matrixWorld;\n\t\tthis.matrixAutoUpdate = false;\n\n\t\tthis.color = color;\n\n\t\tthis.type = 'DirectionalLightHelper';\n\n\t\tif ( size === undefined ) size = 1;\n\n\t\tlet geometry = new BufferGeometry();\n\t\tgeometry.setAttribute( 'position', new Float32BufferAttribute( [\n\t\t\t- size, size, 0,\n\t\t\tsize, size, 0,\n\t\t\tsize, - size, 0,\n\t\t\t- size, - size, 0,\n\t\t\t- size, size, 0\n\t\t], 3 ) );\n\n\t\tconst material = new LineBasicMaterial( { fog: false, toneMapped: false } );\n\n\t\tthis.lightPlane = new Line( geometry, material );\n\t\tthis.add( this.lightPlane );\n\n\t\tgeometry = new BufferGeometry();\n\t\tgeometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) );\n\n\t\tthis.targetLine = new Line( geometry, material );\n\t\tthis.add( this.targetLine );\n\n\t\tthis.update();\n\n\t}\n\n\tdispose() {\n\n\t\tthis.lightPlane.geometry.dispose();\n\t\tthis.lightPlane.material.dispose();\n\t\tthis.targetLine.geometry.dispose();\n\t\tthis.targetLine.material.dispose();\n\n\t}\n\n\tupdate() {\n\n\t\tthis.light.updateWorldMatrix( true, false );\n\t\tthis.light.target.updateWorldMatrix( true, false );\n\n\t\t_v1.setFromMatrixPosition( this.light.matrixWorld );\n\t\t_v2.setFromMatrixPosition( this.light.target.matrixWorld );\n\t\t_v3.subVectors( _v2, _v1 );\n\n\t\tthis.lightPlane.lookAt( _v2 );\n\n\t\tif ( this.color !== undefined ) {\n\n\t\t\tthis.lightPlane.material.color.set( this.color );\n\t\t\tthis.targetLine.material.color.set( this.color );\n\n\t\t} else {\n\n\t\t\tthis.lightPlane.material.color.copy( this.light.color );\n\t\t\tthis.targetLine.material.color.copy( this.light.color );\n\n\t\t}\n\n\t\tthis.targetLine.lookAt( _v2 );\n\t\tthis.targetLine.scale.z = _v3.length();\n\n\t}\n\n}\n\nconst _vector = /*@__PURE__*/ new Vector3();\nconst _camera = /*@__PURE__*/ new Camera();\n\n/**\n *\t- shows frustum, line of sight and up of the camera\n *\t- suitable for fast updates\n * \t- based on frustum visualization in lightgl.js shadowmap example\n *\t\thttps://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html\n */\n\nclass CameraHelper extends LineSegments {\n\n\tconstructor( camera ) {\n\n\t\tconst geometry = new BufferGeometry();\n\t\tconst material = new LineBasicMaterial( { color: 0xffffff, vertexColors: true, toneMapped: false } );\n\n\t\tconst vertices = [];\n\t\tconst colors = [];\n\n\t\tconst pointMap = {};\n\n\t\t// near\n\n\t\taddLine( 'n1', 'n2' );\n\t\taddLine( 'n2', 'n4' );\n\t\taddLine( 'n4', 'n3' );\n\t\taddLine( 'n3', 'n1' );\n\n\t\t// far\n\n\t\taddLine( 'f1', 'f2' );\n\t\taddLine( 'f2', 'f4' );\n\t\taddLine( 'f4', 'f3' );\n\t\taddLine( 'f3', 'f1' );\n\n\t\t// sides\n\n\t\taddLine( 'n1', 'f1' );\n\t\taddLine( 'n2', 'f2' );\n\t\taddLine( 'n3', 'f3' );\n\t\taddLine( 'n4', 'f4' );\n\n\t\t// cone\n\n\t\taddLine( 'p', 'n1' );\n\t\taddLine( 'p', 'n2' );\n\t\taddLine( 'p', 'n3' );\n\t\taddLine( 'p', 'n4' );\n\n\t\t// up\n\n\t\taddLine( 'u1', 'u2' );\n\t\taddLine( 'u2', 'u3' );\n\t\taddLine( 'u3', 'u1' );\n\n\t\t// target\n\n\t\taddLine( 'c', 't' );\n\t\taddLine( 'p', 'c' );\n\n\t\t// cross\n\n\t\taddLine( 'cn1', 'cn2' );\n\t\taddLine( 'cn3', 'cn4' );\n\n\t\taddLine( 'cf1', 'cf2' );\n\t\taddLine( 'cf3', 'cf4' );\n\n\t\tfunction addLine( a, b ) {\n\n\t\t\taddPoint( a );\n\t\t\taddPoint( b );\n\n\t\t}\n\n\t\tfunction addPoint( id ) {\n\n\t\t\tvertices.push( 0, 0, 0 );\n\t\t\tcolors.push( 0, 0, 0 );\n\n\t\t\tif ( pointMap[ id ] === undefined ) {\n\n\t\t\t\tpointMap[ id ] = [];\n\n\t\t\t}\n\n\t\t\tpointMap[ id ].push( ( vertices.length / 3 ) - 1 );\n\n\t\t}\n\n\t\tgeometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tgeometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n\t\tsuper( geometry, material );\n\n\t\tthis.type = 'CameraHelper';\n\n\t\tthis.camera = camera;\n\t\tif ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix();\n\n\t\tthis.matrix = camera.matrixWorld;\n\t\tthis.matrixAutoUpdate = false;\n\n\t\tthis.pointMap = pointMap;\n\n\t\tthis.update();\n\n\t\t// colors\n\n\t\tconst colorFrustum = new Color( 0xffaa00 );\n\t\tconst colorCone = new Color( 0xff0000 );\n\t\tconst colorUp = new Color( 0x00aaff );\n\t\tconst colorTarget = new Color( 0xffffff );\n\t\tconst colorCross = new Color( 0x333333 );\n\n\t\tthis.setColors( colorFrustum, colorCone, colorUp, colorTarget, colorCross );\n\n\t}\n\n\tsetColors( frustum, cone, up, target, cross ) {\n\n\t\tconst geometry = this.geometry;\n\n\t\tconst colorAttribute = geometry.getAttribute( 'color' );\n\n\t\t// near\n\n\t\tcolorAttribute.setXYZ( 0, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 1, frustum.r, frustum.g, frustum.b ); // n1, n2\n\t\tcolorAttribute.setXYZ( 2, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 3, frustum.r, frustum.g, frustum.b ); // n2, n4\n\t\tcolorAttribute.setXYZ( 4, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 5, frustum.r, frustum.g, frustum.b ); // n4, n3\n\t\tcolorAttribute.setXYZ( 6, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 7, frustum.r, frustum.g, frustum.b ); // n3, n1\n\n\t\t// far\n\n\t\tcolorAttribute.setXYZ( 8, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 9, frustum.r, frustum.g, frustum.b ); // f1, f2\n\t\tcolorAttribute.setXYZ( 10, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 11, frustum.r, frustum.g, frustum.b ); // f2, f4\n\t\tcolorAttribute.setXYZ( 12, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 13, frustum.r, frustum.g, frustum.b ); // f4, f3\n\t\tcolorAttribute.setXYZ( 14, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 15, frustum.r, frustum.g, frustum.b ); // f3, f1\n\n\t\t// sides\n\n\t\tcolorAttribute.setXYZ( 16, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 17, frustum.r, frustum.g, frustum.b ); // n1, f1\n\t\tcolorAttribute.setXYZ( 18, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 19, frustum.r, frustum.g, frustum.b ); // n2, f2\n\t\tcolorAttribute.setXYZ( 20, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 21, frustum.r, frustum.g, frustum.b ); // n3, f3\n\t\tcolorAttribute.setXYZ( 22, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 23, frustum.r, frustum.g, frustum.b ); // n4, f4\n\n\t\t// cone\n\n\t\tcolorAttribute.setXYZ( 24, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 25, cone.r, cone.g, cone.b ); // p, n1\n\t\tcolorAttribute.setXYZ( 26, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 27, cone.r, cone.g, cone.b ); // p, n2\n\t\tcolorAttribute.setXYZ( 28, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 29, cone.r, cone.g, cone.b ); // p, n3\n\t\tcolorAttribute.setXYZ( 30, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 31, cone.r, cone.g, cone.b ); // p, n4\n\n\t\t// up\n\n\t\tcolorAttribute.setXYZ( 32, up.r, up.g, up.b ); colorAttribute.setXYZ( 33, up.r, up.g, up.b ); // u1, u2\n\t\tcolorAttribute.setXYZ( 34, up.r, up.g, up.b ); colorAttribute.setXYZ( 35, up.r, up.g, up.b ); // u2, u3\n\t\tcolorAttribute.setXYZ( 36, up.r, up.g, up.b ); colorAttribute.setXYZ( 37, up.r, up.g, up.b ); // u3, u1\n\n\t\t// target\n\n\t\tcolorAttribute.setXYZ( 38, target.r, target.g, target.b ); colorAttribute.setXYZ( 39, target.r, target.g, target.b ); // c, t\n\t\tcolorAttribute.setXYZ( 40, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 41, cross.r, cross.g, cross.b ); // p, c\n\n\t\t// cross\n\n\t\tcolorAttribute.setXYZ( 42, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 43, cross.r, cross.g, cross.b ); // cn1, cn2\n\t\tcolorAttribute.setXYZ( 44, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 45, cross.r, cross.g, cross.b ); // cn3, cn4\n\n\t\tcolorAttribute.setXYZ( 46, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 47, cross.r, cross.g, cross.b ); // cf1, cf2\n\t\tcolorAttribute.setXYZ( 48, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 49, cross.r, cross.g, cross.b ); // cf3, cf4\n\n\t\tcolorAttribute.needsUpdate = true;\n\n\t}\n\n\tupdate() {\n\n\t\tconst geometry = this.geometry;\n\t\tconst pointMap = this.pointMap;\n\n\t\tconst w = 1, h = 1;\n\n\t\t// we need just camera projection matrix inverse\n\t\t// world matrix must be identity\n\n\t\t_camera.projectionMatrixInverse.copy( this.camera.projectionMatrixInverse );\n\n\t\t// center / target\n\n\t\tsetPoint( 'c', pointMap, geometry, _camera, 0, 0, - 1 );\n\t\tsetPoint( 't', pointMap, geometry, _camera, 0, 0, 1 );\n\n\t\t// near\n\n\t\tsetPoint( 'n1', pointMap, geometry, _camera, - w, - h, - 1 );\n\t\tsetPoint( 'n2', pointMap, geometry, _camera, w, - h, - 1 );\n\t\tsetPoint( 'n3', pointMap, geometry, _camera, - w, h, - 1 );\n\t\tsetPoint( 'n4', pointMap, geometry, _camera, w, h, - 1 );\n\n\t\t// far\n\n\t\tsetPoint( 'f1', pointMap, geometry, _camera, - w, - h, 1 );\n\t\tsetPoint( 'f2', pointMap, geometry, _camera, w, - h, 1 );\n\t\tsetPoint( 'f3', pointMap, geometry, _camera, - w, h, 1 );\n\t\tsetPoint( 'f4', pointMap, geometry, _camera, w, h, 1 );\n\n\t\t// up\n\n\t\tsetPoint( 'u1', pointMap, geometry, _camera, w * 0.7, h * 1.1, - 1 );\n\t\tsetPoint( 'u2', pointMap, geometry, _camera, - w * 0.7, h * 1.1, - 1 );\n\t\tsetPoint( 'u3', pointMap, geometry, _camera, 0, h * 2, - 1 );\n\n\t\t// cross\n\n\t\tsetPoint( 'cf1', pointMap, geometry, _camera, - w, 0, 1 );\n\t\tsetPoint( 'cf2', pointMap, geometry, _camera, w, 0, 1 );\n\t\tsetPoint( 'cf3', pointMap, geometry, _camera, 0, - h, 1 );\n\t\tsetPoint( 'cf4', pointMap, geometry, _camera, 0, h, 1 );\n\n\t\tsetPoint( 'cn1', pointMap, geometry, _camera, - w, 0, - 1 );\n\t\tsetPoint( 'cn2', pointMap, geometry, _camera, w, 0, - 1 );\n\t\tsetPoint( 'cn3', pointMap, geometry, _camera, 0, - h, - 1 );\n\t\tsetPoint( 'cn4', pointMap, geometry, _camera, 0, h, - 1 );\n\n\t\tgeometry.getAttribute( 'position' ).needsUpdate = true;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.geometry.dispose();\n\t\tthis.material.dispose();\n\n\t}\n\n}\n\n\nfunction setPoint( point, pointMap, geometry, camera, x, y, z ) {\n\n\t_vector.set( x, y, z ).unproject( camera );\n\n\tconst points = pointMap[ point ];\n\n\tif ( points !== undefined ) {\n\n\t\tconst position = geometry.getAttribute( 'position' );\n\n\t\tfor ( let i = 0, l = points.length; i < l; i ++ ) {\n\n\t\t\tposition.setXYZ( points[ i ], _vector.x, _vector.y, _vector.z );\n\n\t\t}\n\n\t}\n\n}\n\nconst _box = /*@__PURE__*/ new Box3();\n\nclass BoxHelper extends LineSegments {\n\n\tconstructor( object, color = 0xffff00 ) {\n\n\t\tconst indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );\n\t\tconst positions = new Float32Array( 8 * 3 );\n\n\t\tconst geometry = new BufferGeometry();\n\t\tgeometry.setIndex( new BufferAttribute( indices, 1 ) );\n\t\tgeometry.setAttribute( 'position', new BufferAttribute( positions, 3 ) );\n\n\t\tsuper( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );\n\n\t\tthis.object = object;\n\t\tthis.type = 'BoxHelper';\n\n\t\tthis.matrixAutoUpdate = false;\n\n\t\tthis.update();\n\n\t}\n\n\tupdate( object ) {\n\n\t\tif ( object !== undefined ) {\n\n\t\t\tconsole.warn( 'THREE.BoxHelper: .update() has no longer arguments.' );\n\n\t\t}\n\n\t\tif ( this.object !== undefined ) {\n\n\t\t\t_box.setFromObject( this.object );\n\n\t\t}\n\n\t\tif ( _box.isEmpty() ) return;\n\n\t\tconst min = _box.min;\n\t\tconst max = _box.max;\n\n\t\t/*\n\t\t\t5____4\n\t\t1/___0/|\n\t\t| 6__|_7\n\t\t2/___3/\n\n\t\t0: max.x, max.y, max.z\n\t\t1: min.x, max.y, max.z\n\t\t2: min.x, min.y, max.z\n\t\t3: max.x, min.y, max.z\n\t\t4: max.x, max.y, min.z\n\t\t5: min.x, max.y, min.z\n\t\t6: min.x, min.y, min.z\n\t\t7: max.x, min.y, min.z\n\t\t*/\n\n\t\tconst position = this.geometry.attributes.position;\n\t\tconst array = position.array;\n\n\t\tarray[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z;\n\t\tarray[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z;\n\t\tarray[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z;\n\t\tarray[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z;\n\t\tarray[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z;\n\t\tarray[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z;\n\t\tarray[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z;\n\t\tarray[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z;\n\n\t\tposition.needsUpdate = true;\n\n\t\tthis.geometry.computeBoundingSphere();\n\n\t}\n\n\tsetFromObject( object ) {\n\n\t\tthis.object = object;\n\t\tthis.update();\n\n\t\treturn this;\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.object = source.object;\n\n\t\treturn this;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.geometry.dispose();\n\t\tthis.material.dispose();\n\n\t}\n\n}\n\nclass Box3Helper extends LineSegments {\n\n\tconstructor( box, color = 0xffff00 ) {\n\n\t\tconst indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );\n\n\t\tconst positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ];\n\n\t\tconst geometry = new BufferGeometry();\n\n\t\tgeometry.setIndex( new BufferAttribute( indices, 1 ) );\n\n\t\tgeometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );\n\n\t\tsuper( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );\n\n\t\tthis.box = box;\n\n\t\tthis.type = 'Box3Helper';\n\n\t\tthis.geometry.computeBoundingSphere();\n\n\t}\n\n\tupdateMatrixWorld( force ) {\n\n\t\tconst box = this.box;\n\n\t\tif ( box.isEmpty() ) return;\n\n\t\tbox.getCenter( this.position );\n\n\t\tbox.getSize( this.scale );\n\n\t\tthis.scale.multiplyScalar( 0.5 );\n\n\t\tsuper.updateMatrixWorld( force );\n\n\t}\n\n\tdispose() {\n\n\t\tthis.geometry.dispose();\n\t\tthis.material.dispose();\n\n\t}\n\n}\n\nclass PlaneHelper extends Line {\n\n\tconstructor( plane, size = 1, hex = 0xffff00 ) {\n\n\t\tconst color = hex;\n\n\t\tconst positions = [ 1, - 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, - 1, 0, 1, 1, 0 ];\n\n\t\tconst geometry = new BufferGeometry();\n\t\tgeometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );\n\t\tgeometry.computeBoundingSphere();\n\n\t\tsuper( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );\n\n\t\tthis.type = 'PlaneHelper';\n\n\t\tthis.plane = plane;\n\n\t\tthis.size = size;\n\n\t\tconst positions2 = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, - 1, 0, 1, - 1, 0 ];\n\n\t\tconst geometry2 = new BufferGeometry();\n\t\tgeometry2.setAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) );\n\t\tgeometry2.computeBoundingSphere();\n\n\t\tthis.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false } ) ) );\n\n\t}\n\n\tupdateMatrixWorld( force ) {\n\n\t\tthis.position.set( 0, 0, 0 );\n\n\t\tthis.scale.set( 0.5 * this.size, 0.5 * this.size, 1 );\n\n\t\tthis.lookAt( this.plane.normal );\n\n\t\tthis.translateZ( - this.plane.constant );\n\n\t\tsuper.updateMatrixWorld( force );\n\n\t}\n\n\tdispose() {\n\n\t\tthis.geometry.dispose();\n\t\tthis.material.dispose();\n\t\tthis.children[ 0 ].geometry.dispose();\n\t\tthis.children[ 0 ].material.dispose();\n\n\t}\n\n}\n\nconst _axis = /*@__PURE__*/ new Vector3();\nlet _lineGeometry, _coneGeometry;\n\nclass ArrowHelper extends Object3D {\n\n\t// dir is assumed to be normalized\n\n\tconstructor( dir = new Vector3( 0, 0, 1 ), origin = new Vector3( 0, 0, 0 ), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'ArrowHelper';\n\n\t\tif ( _lineGeometry === undefined ) {\n\n\t\t\t_lineGeometry = new BufferGeometry();\n\t\t\t_lineGeometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) );\n\n\t\t\t_coneGeometry = new CylinderGeometry( 0, 0.5, 1, 5, 1 );\n\t\t\t_coneGeometry.translate( 0, - 0.5, 0 );\n\n\t\t}\n\n\t\tthis.position.copy( origin );\n\n\t\tthis.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );\n\t\tthis.line.matrixAutoUpdate = false;\n\t\tthis.add( this.line );\n\n\t\tthis.cone = new Mesh( _coneGeometry, new MeshBasicMaterial( { color: color, toneMapped: false } ) );\n\t\tthis.cone.matrixAutoUpdate = false;\n\t\tthis.add( this.cone );\n\n\t\tthis.setDirection( dir );\n\t\tthis.setLength( length, headLength, headWidth );\n\n\t}\n\n\tsetDirection( dir ) {\n\n\t\t// dir is assumed to be normalized\n\n\t\tif ( dir.y > 0.99999 ) {\n\n\t\t\tthis.quaternion.set( 0, 0, 0, 1 );\n\n\t\t} else if ( dir.y < - 0.99999 ) {\n\n\t\t\tthis.quaternion.set( 1, 0, 0, 0 );\n\n\t\t} else {\n\n\t\t\t_axis.set( dir.z, 0, - dir.x ).normalize();\n\n\t\t\tconst radians = Math.acos( dir.y );\n\n\t\t\tthis.quaternion.setFromAxisAngle( _axis, radians );\n\n\t\t}\n\n\t}\n\n\tsetLength( length, headLength = length * 0.2, headWidth = headLength * 0.2 ) {\n\n\t\tthis.line.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458\n\t\tthis.line.updateMatrix();\n\n\t\tthis.cone.scale.set( headWidth, headLength, headWidth );\n\t\tthis.cone.position.y = length;\n\t\tthis.cone.updateMatrix();\n\n\t}\n\n\tsetColor( color ) {\n\n\t\tthis.line.material.color.set( color );\n\t\tthis.cone.material.color.set( color );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source, false );\n\n\t\tthis.line.copy( source.line );\n\t\tthis.cone.copy( source.cone );\n\n\t\treturn this;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.line.geometry.dispose();\n\t\tthis.line.material.dispose();\n\t\tthis.cone.geometry.dispose();\n\t\tthis.cone.material.dispose();\n\n\t}\n\n}\n\nclass AxesHelper extends LineSegments {\n\n\tconstructor( size = 1 ) {\n\n\t\tconst vertices = [\n\t\t\t0, 0, 0,\tsize, 0, 0,\n\t\t\t0, 0, 0,\t0, size, 0,\n\t\t\t0, 0, 0,\t0, 0, size\n\t\t];\n\n\t\tconst colors = [\n\t\t\t1, 0, 0,\t1, 0.6, 0,\n\t\t\t0, 1, 0,\t0.6, 1, 0,\n\t\t\t0, 0, 1,\t0, 0.6, 1\n\t\t];\n\n\t\tconst geometry = new BufferGeometry();\n\t\tgeometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tgeometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n\t\tconst material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } );\n\n\t\tsuper( geometry, material );\n\n\t\tthis.type = 'AxesHelper';\n\n\t}\n\n\tsetColors( xAxisColor, yAxisColor, zAxisColor ) {\n\n\t\tconst color = new Color();\n\t\tconst array = this.geometry.attributes.color.array;\n\n\t\tcolor.set( xAxisColor );\n\t\tcolor.toArray( array, 0 );\n\t\tcolor.toArray( array, 3 );\n\n\t\tcolor.set( yAxisColor );\n\t\tcolor.toArray( array, 6 );\n\t\tcolor.toArray( array, 9 );\n\n\t\tcolor.set( zAxisColor );\n\t\tcolor.toArray( array, 12 );\n\t\tcolor.toArray( array, 15 );\n\n\t\tthis.geometry.attributes.color.needsUpdate = true;\n\n\t\treturn this;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.geometry.dispose();\n\t\tthis.material.dispose();\n\n\t}\n\n}\n\nclass ShapePath {\n\n\tconstructor() {\n\n\t\tthis.type = 'ShapePath';\n\n\t\tthis.color = new Color();\n\n\t\tthis.subPaths = [];\n\t\tthis.currentPath = null;\n\n\t}\n\n\tmoveTo( x, y ) {\n\n\t\tthis.currentPath = new Path();\n\t\tthis.subPaths.push( this.currentPath );\n\t\tthis.currentPath.moveTo( x, y );\n\n\t\treturn this;\n\n\t}\n\n\tlineTo( x, y ) {\n\n\t\tthis.currentPath.lineTo( x, y );\n\n\t\treturn this;\n\n\t}\n\n\tquadraticCurveTo( aCPx, aCPy, aX, aY ) {\n\n\t\tthis.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY );\n\n\t\treturn this;\n\n\t}\n\n\tbezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {\n\n\t\tthis.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY );\n\n\t\treturn this;\n\n\t}\n\n\tsplineThru( pts ) {\n\n\t\tthis.currentPath.splineThru( pts );\n\n\t\treturn this;\n\n\t}\n\n\ttoShapes( isCCW ) {\n\n\t\tfunction toShapesNoHoles( inSubpaths ) {\n\n\t\t\tconst shapes = [];\n\n\t\t\tfor ( let i = 0, l = inSubpaths.length; i < l; i ++ ) {\n\n\t\t\t\tconst tmpPath = inSubpaths[ i ];\n\n\t\t\t\tconst tmpShape = new Shape();\n\t\t\t\ttmpShape.curves = tmpPath.curves;\n\n\t\t\t\tshapes.push( tmpShape );\n\n\t\t\t}\n\n\t\t\treturn shapes;\n\n\t\t}\n\n\t\tfunction isPointInsidePolygon( inPt, inPolygon ) {\n\n\t\t\tconst polyLen = inPolygon.length;\n\n\t\t\t// inPt on polygon contour => immediate success    or\n\t\t\t// toggling of inside/outside at every single! intersection point of an edge\n\t\t\t//  with the horizontal line through inPt, left of inPt\n\t\t\t//  not counting lowerY endpoints of edges and whole edges on that line\n\t\t\tlet inside = false;\n\t\t\tfor ( let p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {\n\n\t\t\t\tlet edgeLowPt = inPolygon[ p ];\n\t\t\t\tlet edgeHighPt = inPolygon[ q ];\n\n\t\t\t\tlet edgeDx = edgeHighPt.x - edgeLowPt.x;\n\t\t\t\tlet edgeDy = edgeHighPt.y - edgeLowPt.y;\n\n\t\t\t\tif ( Math.abs( edgeDy ) > Number.EPSILON ) {\n\n\t\t\t\t\t// not parallel\n\t\t\t\t\tif ( edgeDy < 0 ) {\n\n\t\t\t\t\t\tedgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;\n\t\t\t\t\t\tedgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) \t\tcontinue;\n\n\t\t\t\t\tif ( inPt.y === edgeLowPt.y ) {\n\n\t\t\t\t\t\tif ( inPt.x === edgeLowPt.x )\t\treturn\ttrue;\t\t// inPt is on contour ?\n\t\t\t\t\t\t// continue;\t\t\t\t// no intersection or edgeLowPt => doesn't count !!!\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconst perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y );\n\t\t\t\t\t\tif ( perpEdge === 0 )\t\t\t\treturn\ttrue;\t\t// inPt is on contour ?\n\t\t\t\t\t\tif ( perpEdge < 0 ) \t\t\t\tcontinue;\n\t\t\t\t\t\tinside = ! inside;\t\t// true intersection left of inPt\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// parallel or collinear\n\t\t\t\t\tif ( inPt.y !== edgeLowPt.y ) \t\tcontinue;\t\t\t// parallel\n\t\t\t\t\t// edge lies on the same horizontal line as inPt\n\t\t\t\t\tif ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||\n\t\t\t\t\t\t ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) )\t\treturn\ttrue;\t// inPt: Point on contour !\n\t\t\t\t\t// continue;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn\tinside;\n\n\t\t}\n\n\t\tconst isClockWise = ShapeUtils.isClockWise;\n\n\t\tconst subPaths = this.subPaths;\n\t\tif ( subPaths.length === 0 ) return [];\n\n\t\tlet solid, tmpPath, tmpShape;\n\t\tconst shapes = [];\n\n\t\tif ( subPaths.length === 1 ) {\n\n\t\t\ttmpPath = subPaths[ 0 ];\n\t\t\ttmpShape = new Shape();\n\t\t\ttmpShape.curves = tmpPath.curves;\n\t\t\tshapes.push( tmpShape );\n\t\t\treturn shapes;\n\n\t\t}\n\n\t\tlet holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );\n\t\tholesFirst = isCCW ? ! holesFirst : holesFirst;\n\n\t\t// console.log(\"Holes first\", holesFirst);\n\n\t\tconst betterShapeHoles = [];\n\t\tconst newShapes = [];\n\t\tlet newShapeHoles = [];\n\t\tlet mainIdx = 0;\n\t\tlet tmpPoints;\n\n\t\tnewShapes[ mainIdx ] = undefined;\n\t\tnewShapeHoles[ mainIdx ] = [];\n\n\t\tfor ( let i = 0, l = subPaths.length; i < l; i ++ ) {\n\n\t\t\ttmpPath = subPaths[ i ];\n\t\t\ttmpPoints = tmpPath.getPoints();\n\t\t\tsolid = isClockWise( tmpPoints );\n\t\t\tsolid = isCCW ? ! solid : solid;\n\n\t\t\tif ( solid ) {\n\n\t\t\t\tif ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) )\tmainIdx ++;\n\n\t\t\t\tnewShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints };\n\t\t\t\tnewShapes[ mainIdx ].s.curves = tmpPath.curves;\n\n\t\t\t\tif ( holesFirst )\tmainIdx ++;\n\t\t\t\tnewShapeHoles[ mainIdx ] = [];\n\n\t\t\t\t//console.log('cw', i);\n\n\t\t\t} else {\n\n\t\t\t\tnewShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );\n\n\t\t\t\t//console.log('ccw', i);\n\n\t\t\t}\n\n\t\t}\n\n\t\t// only Holes? -> probably all Shapes with wrong orientation\n\t\tif ( ! newShapes[ 0 ] )\treturn\ttoShapesNoHoles( subPaths );\n\n\n\t\tif ( newShapes.length > 1 ) {\n\n\t\t\tlet ambiguous = false;\n\t\t\tlet toChange = 0;\n\n\t\t\tfor ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {\n\n\t\t\t\tbetterShapeHoles[ sIdx ] = [];\n\n\t\t\t}\n\n\t\t\tfor ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {\n\n\t\t\t\tconst sho = newShapeHoles[ sIdx ];\n\n\t\t\t\tfor ( let hIdx = 0; hIdx < sho.length; hIdx ++ ) {\n\n\t\t\t\t\tconst ho = sho[ hIdx ];\n\t\t\t\t\tlet hole_unassigned = true;\n\n\t\t\t\t\tfor ( let s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {\n\n\t\t\t\t\t\tif ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {\n\n\t\t\t\t\t\t\tif ( sIdx !== s2Idx )\ttoChange ++;\n\n\t\t\t\t\t\t\tif ( hole_unassigned ) {\n\n\t\t\t\t\t\t\t\thole_unassigned = false;\n\t\t\t\t\t\t\t\tbetterShapeHoles[ s2Idx ].push( ho );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tambiguous = true;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( hole_unassigned ) {\n\n\t\t\t\t\t\tbetterShapeHoles[ sIdx ].push( ho );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( toChange > 0 && ambiguous === false ) {\n\n\t\t\t\tnewShapeHoles = betterShapeHoles;\n\n\t\t\t}\n\n\t\t}\n\n\t\tlet tmpHoles;\n\n\t\tfor ( let i = 0, il = newShapes.length; i < il; i ++ ) {\n\n\t\t\ttmpShape = newShapes[ i ].s;\n\t\t\tshapes.push( tmpShape );\n\t\t\ttmpHoles = newShapeHoles[ i ];\n\n\t\t\tfor ( let j = 0, jl = tmpHoles.length; j < jl; j ++ ) {\n\n\t\t\t\ttmpShape.holes.push( tmpHoles[ j ].h );\n\n\t\t\t}\n\n\t\t}\n\n\t\t//console.log(\"shape\", shapes);\n\n\t\treturn shapes;\n\n\t}\n\n}\n\nif ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) {\n\n\t__THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: {\n\t\trevision: REVISION,\n\t} } ) );\n\n}\n\nif ( typeof window !== 'undefined' ) {\n\n\tif ( window.__THREE__ ) {\n\n\t\tconsole.warn( 'WARNING: Multiple instances of Three.js being imported.' );\n\n\t} else {\n\n\t\twindow.__THREE__ = REVISION;\n\n\t}\n\n}\n\nexport { ACESFilmicToneMapping, AddEquation, AddOperation, AdditiveAnimationBlendMode, AdditiveBlending, AgXToneMapping, AlphaFormat, AlwaysCompare, AlwaysDepth, AlwaysStencilFunc, AmbientLight, AnimationAction, AnimationClip, AnimationLoader, AnimationMixer, AnimationObjectGroup, AnimationUtils, ArcCurve, ArrayCamera, ArrowHelper, AttachedBindMode, Audio, AudioAnalyser, AudioContext, AudioListener, AudioLoader, AxesHelper, BackSide, BasicDepthPacking, BasicShadowMap, BatchedMesh, Bone, BooleanKeyframeTrack, Box2, Box3, Box3Helper, BoxGeometry, BoxHelper, BufferAttribute, BufferGeometry, BufferGeometryLoader, ByteType, Cache, Camera, CameraHelper, CanvasTexture, CapsuleGeometry, CatmullRomCurve3, CineonToneMapping, CircleGeometry, ClampToEdgeWrapping, Clock, Color, ColorKeyframeTrack, ColorManagement, CompressedArrayTexture, CompressedCubeTexture, CompressedTexture, CompressedTextureLoader, ConeGeometry, ConstantAlphaFactor, ConstantColorFactor, CubeCamera, CubeReflectionMapping, CubeRefractionMapping, CubeTexture, CubeTextureLoader, CubeUVReflectionMapping, CubicBezierCurve, CubicBezierCurve3, CubicInterpolant, CullFaceBack, CullFaceFront, CullFaceFrontBack, CullFaceNone, Curve, CurvePath, CustomBlending, CustomToneMapping, CylinderGeometry, Cylindrical, Data3DTexture, DataArrayTexture, DataTexture, DataTextureLoader, DataUtils, DecrementStencilOp, DecrementWrapStencilOp, DefaultLoadingManager, DepthFormat, DepthStencilFormat, DepthTexture, DetachedBindMode, DirectionalLight, DirectionalLightHelper, DiscreteInterpolant, DisplayP3ColorSpace, DodecahedronGeometry, DoubleSide, DstAlphaFactor, DstColorFactor, DynamicCopyUsage, DynamicDrawUsage, DynamicReadUsage, EdgesGeometry, EllipseCurve, EqualCompare, EqualDepth, EqualStencilFunc, EquirectangularReflectionMapping, EquirectangularRefractionMapping, Euler, EventDispatcher, ExtrudeGeometry, FileLoader, Float16BufferAttribute, Float32BufferAttribute, Float64BufferAttribute, FloatType, Fog, FogExp2, FramebufferTexture, FrontSide, Frustum, GLBufferAttribute, GLSL1, GLSL3, GreaterCompare, GreaterDepth, GreaterEqualCompare, GreaterEqualDepth, GreaterEqualStencilFunc, GreaterStencilFunc, GridHelper, Group, HalfFloatType, HemisphereLight, HemisphereLightHelper, IcosahedronGeometry, ImageBitmapLoader, ImageLoader, ImageUtils, IncrementStencilOp, IncrementWrapStencilOp, InstancedBufferAttribute, InstancedBufferGeometry, InstancedInterleavedBuffer, InstancedMesh, Int16BufferAttribute, Int32BufferAttribute, Int8BufferAttribute, IntType, InterleavedBuffer, InterleavedBufferAttribute, Interpolant, InterpolateDiscrete, InterpolateLinear, InterpolateSmooth, InvertStencilOp, KeepStencilOp, KeyframeTrack, LOD, LatheGeometry, Layers, LessCompare, LessDepth, LessEqualCompare, LessEqualDepth, LessEqualStencilFunc, LessStencilFunc, Light, LightProbe, Line, Line3, LineBasicMaterial, LineCurve, LineCurve3, LineDashedMaterial, LineLoop, LineSegments, LinearDisplayP3ColorSpace, LinearEncoding, LinearFilter, LinearInterpolant, LinearMipMapLinearFilter, LinearMipMapNearestFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, LinearSRGBColorSpace, LinearToneMapping, LinearTransfer, Loader, LoaderUtils, LoadingManager, LoopOnce, LoopPingPong, LoopRepeat, LuminanceAlphaFormat, LuminanceFormat, MOUSE, Material, MaterialLoader, MathUtils, Matrix3, Matrix4, MaxEquation, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshDistanceMaterial, MeshLambertMaterial, MeshMatcapMaterial, MeshNormalMaterial, MeshPhongMaterial, MeshPhysicalMaterial, MeshStandardMaterial, MeshToonMaterial, MinEquation, MirroredRepeatWrapping, MixOperation, MultiplyBlending, MultiplyOperation, NearestFilter, NearestMipMapLinearFilter, NearestMipMapNearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, NeverCompare, NeverDepth, NeverStencilFunc, NoBlending, NoColorSpace, NoToneMapping, NormalAnimationBlendMode, NormalBlending, NotEqualCompare, NotEqualDepth, NotEqualStencilFunc, NumberKeyframeTrack, Object3D, ObjectLoader, ObjectSpaceNormalMap, OctahedronGeometry, OneFactor, OneMinusConstantAlphaFactor, OneMinusConstantColorFactor, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcAlphaFactor, OneMinusSrcColorFactor, OrthographicCamera, P3Primaries, PCFShadowMap, PCFSoftShadowMap, PMREMGenerator, Path, PerspectiveCamera, Plane, PlaneGeometry, PlaneHelper, PointLight, PointLightHelper, Points, PointsMaterial, PolarGridHelper, PolyhedronGeometry, PositionalAudio, PropertyBinding, PropertyMixer, QuadraticBezierCurve, QuadraticBezierCurve3, Quaternion, QuaternionKeyframeTrack, QuaternionLinearInterpolant, RED_GREEN_RGTC2_Format, RED_RGTC1_Format, REVISION, RGBADepthPacking, RGBAFormat, RGBAIntegerFormat, RGBA_ASTC_10x10_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_BPTC_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGB_S3TC_DXT1_Format, RGFormat, RGIntegerFormat, RawShaderMaterial, Ray, Raycaster, Rec709Primaries, RectAreaLight, RedFormat, RedIntegerFormat, ReinhardToneMapping, RenderTarget, RepeatWrapping, ReplaceStencilOp, ReverseSubtractEquation, RingGeometry, SIGNED_RED_GREEN_RGTC2_Format, SIGNED_RED_RGTC1_Format, SRGBColorSpace, SRGBTransfer, Scene, ShaderChunk, ShaderLib, ShaderMaterial, ShadowMaterial, Shape, ShapeGeometry, ShapePath, ShapeUtils, ShortType, Skeleton, SkeletonHelper, SkinnedMesh, Source, Sphere, SphereGeometry, Spherical, SphericalHarmonics3, SplineCurve, SpotLight, SpotLightHelper, Sprite, SpriteMaterial, SrcAlphaFactor, SrcAlphaSaturateFactor, SrcColorFactor, StaticCopyUsage, StaticDrawUsage, StaticReadUsage, StereoCamera, StreamCopyUsage, StreamDrawUsage, StreamReadUsage, StringKeyframeTrack, SubtractEquation, SubtractiveBlending, TOUCH, TangentSpaceNormalMap, TetrahedronGeometry, Texture, TextureLoader, TorusGeometry, TorusKnotGeometry, Triangle, TriangleFanDrawMode, TriangleStripDrawMode, TrianglesDrawMode, TubeGeometry, TwoPassDoubleSide, UVMapping, Uint16BufferAttribute, Uint32BufferAttribute, Uint8BufferAttribute, Uint8ClampedBufferAttribute, Uniform, UniformsGroup, UniformsLib, UniformsUtils, UnsignedByteType, UnsignedInt248Type, UnsignedIntType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedShortType, VSMShadowMap, Vector2, Vector3, Vector4, VectorKeyframeTrack, VideoTexture, WebGL1Renderer, WebGL3DRenderTarget, WebGLArrayRenderTarget, WebGLCoordinateSystem, WebGLCubeRenderTarget, WebGLMultipleRenderTargets, WebGLRenderTarget, WebGLRenderer, WebGLUtils, WebGPUCoordinateSystem, WireframeGeometry, WrapAroundEnding, ZeroCurvatureEnding, ZeroFactor, ZeroSlopeEnding, ZeroStencilOp, _SRGBAFormat, createCanvasElement, sRGBEncoding };\n"
  },
  {
    "path": "public/assets/lib/vendor/three/viewcube.js",
    "content": "import * as THREE from \"./three.module.js\";\nconst DEFAULT_FACENAMES = {\n  top: \"TOP\",\n  front: \"FRONT\",\n  right: \"RIGHT\",\n  back: \"BACK\",\n  left: \"LEFT\",\n  bottom: \"BOTTOM\"\n};\nvar ObjectPosition = /* @__PURE__ */ ((ObjectPosition2) => {\n  ObjectPosition2[ObjectPosition2[\"LEFT_BOTTOM\"] = 0] = \"LEFT_BOTTOM\";\n  ObjectPosition2[ObjectPosition2[\"LEFT_TOP\"] = 1] = \"LEFT_TOP\";\n  ObjectPosition2[ObjectPosition2[\"RIGHT_TOP\"] = 2] = \"RIGHT_TOP\";\n  ObjectPosition2[ObjectPosition2[\"RIGHT_BOTTOM\"] = 4] = \"RIGHT_BOTTOM\";\n  return ObjectPosition2;\n})(ObjectPosition || {});\nclass FixedPosGizmo extends THREE.Object3D {\n  /**\n   * Construct one instance of this gizmo\n   * @param camera Camera used in your canvas\n   * @param renderer Renderer used in your canvas\n   * @param dimension Size of area ocupied by this gizmo. Because width and height of this area is same,\n   * it is single value. The real size of the objet will be calculated automatically considering rotation.\n   * @param pos Position of the gizmo\n   */\n  constructor(camera, renderer, dimension = 150, pos = 2) {\n    super();\n    this.camera = camera;\n    this.renderer = renderer;\n    this.gizmoCamera = new THREE.OrthographicCamera(-2, 2, 2, -2, 0, 4);\n    this.gizmoCamera.position.set(0, 0, 2);\n    this.gizmoDim = dimension;\n    this.gizmoPos = pos;\n    this.initialize();\n  }\n  /**\n   * Function called by constructor to initialize this gizmo. The children class can override this function\n   * to add its own initialization logic.\n   */\n  initialize() {\n  }\n  /**\n   * Update and rerender this gizmo\n   */\n  update() {\n    this.updateOrientation();\n    const autoClear = this.renderer.autoClear;\n    this.renderer.autoClear = false;\n    this.renderer.clearDepth();\n    const viewport = new THREE.Vector4();\n    this.renderer.getViewport(viewport);\n    const pos = this.calculateViewportPos();\n    this.renderer.setViewport(pos.x, pos.y, this.gizmoDim, this.gizmoDim);\n    this.renderer.render(this, this.gizmoCamera);\n    this.renderer.setViewport(viewport.x, viewport.y, viewport.z, viewport.w);\n    this.renderer.autoClear = autoClear;\n  }\n  /**\n   * Free the GPU-related resources allocated by this instance. Call this method whenever this instance\n   * is no longer used in your app.\n   */\n  dispose() {\n  }\n  updateOrientation() {\n    this.quaternion.copy(this.camera.quaternion).invert();\n    this.updateMatrixWorld();\n  }\n  calculatePosInViewport(offsetX, offsetY, bbox) {\n    const x = (offsetX - bbox.min.x) / this.gizmoDim * 2 - 1;\n    const y = -((offsetY - bbox.min.y) / this.gizmoDim) * 2 + 1;\n    return { x, y };\n  }\n  calculateViewportPos() {\n    const domElement = this.renderer.domElement;\n    const canvasWidth = domElement.offsetWidth;\n    const canvasHeight = domElement.offsetHeight;\n    const pos = this.gizmoPos;\n    const length = this.gizmoDim;\n    let x = canvasWidth - length;\n    let y = canvasHeight - length;\n    switch (pos) {\n      case 0:\n        x = 0;\n        y = 0;\n        break;\n      case 1:\n        x = 0;\n        break;\n      case 4:\n        y = 0;\n        break;\n    }\n    return { x, y };\n  }\n  calculateViewportBbox() {\n    const domElement = this.renderer.domElement;\n    const canvasWidth = domElement.offsetWidth;\n    const canvasHeight = domElement.offsetHeight;\n    const pos = this.gizmoPos;\n    const length = this.gizmoDim;\n    const bbox = new THREE.Box2(\n      new THREE.Vector2(canvasWidth - length, 0),\n      new THREE.Vector2(canvasWidth, length)\n    );\n    switch (pos) {\n      case 0:\n        bbox.set(\n          new THREE.Vector2(0, canvasHeight - length),\n          new THREE.Vector2(length, canvasHeight)\n        );\n        break;\n      case 1:\n        bbox.set(new THREE.Vector2(0, 0), new THREE.Vector2(length, length));\n        break;\n      case 4:\n        bbox.set(\n          new THREE.Vector2(canvasWidth - length, canvasHeight - length),\n          new THREE.Vector2(canvasWidth, canvasHeight)\n        );\n        break;\n    }\n    return bbox;\n  }\n}\nfunction createTextTexture(text, props) {\n  const fontface = props.font || \"Helvetica\";\n  const fontsize = props.fontSize || 30;\n  const width = props.width || 200;\n  const height = props.height || 200;\n  const bgColor = props.bgColor ? props.bgColor.join(\", \") : \"255, 255, 255, 1.0\";\n  const fgColor = props.color ? props.color.join(\", \") : \"0, 0, 0, 1.0\";\n  const canvas = document.createElement(\"canvas\");\n  canvas.width = width;\n  canvas.height = height;\n  const context = canvas.getContext(\"2d\");\n  if (context) {\n    context.font = `bold ${fontsize}px ${fontface}`;\n    context.fillStyle = `rgba(${bgColor})`;\n    context.fillRect(0, 0, width, height);\n    const metrics = context.measureText(text);\n    const textWidth = metrics.width;\n    context.fillStyle = `rgba(${fgColor})`;\n    context.fillText(\n      text,\n      width / 2 - textWidth / 2,\n      height / 2 + fontsize / 2 - 2\n    );\n  }\n  const texture = new THREE.Texture(canvas);\n  texture.minFilter = THREE.LinearFilter;\n  texture.needsUpdate = true;\n  return texture;\n}\nfunction createTextSprite(text) {\n  const texture = createTextTexture(text, {\n    fontSize: 100,\n    font: \"Arial Narrow, sans-serif\",\n    color: [255, 255, 255, 1],\n    bgColor: [0, 0, 0, 0]\n  });\n  const material = new THREE.SpriteMaterial({ map: texture, transparent: true });\n  return new THREE.Sprite(material);\n}\nfunction createFaceMaterials(faceNames = DEFAULT_FACENAMES) {\n  const materials = [\n    {\n      name: FACES.FRONT,\n      map: createTextTexture(faceNames.front, {\n        fontSize: 55,\n        font: \"Arial Narrow, sans-serif\",\n        color: [\"87\", \"89\", \"90\", \"1\"]\n      })\n    },\n    {\n      name: FACES.RIGHT,\n      map: createTextTexture(faceNames.right, {\n        fontSize: 55,\n        font: \"Arial Narrow, sans-serif\",\n        color: [\"87\", \"89\", \"90\", \"1\"]\n      })\n    },\n    {\n      name: FACES.BACK,\n      map: createTextTexture(faceNames.back, {\n        fontSize: 55,\n        font: \"Arial Narrow, sans-serif\",\n        color: [\"87\", \"89\", \"90\", \"1\"]\n      })\n    },\n    {\n      name: FACES.LEFT,\n      map: createTextTexture(faceNames.left, {\n        fontSize: 55,\n        font: \"Arial Narrow, sans-serif\",\n        color: [\"87\", \"89\", \"90\", \"1\"]\n      })\n    },\n    {\n      name: FACES.TOP,\n      map: createTextTexture(faceNames.top, {\n        fontSize: 60,\n        font: \"Arial Narrow, sans-serif\",\n        color: [\"87\", \"89\", \"90\", \"1\"]\n      })\n    },\n    {\n      name: FACES.BOTTOM,\n      map: createTextTexture(faceNames.bottom, {\n        fontSize: 48,\n        font: \"Arial Narrow, sans-serif\",\n        color: [\"87\", \"89\", \"90\", \"1\"]\n      })\n    }\n  ];\n  return materials;\n}\nconst FACES = {\n  TOP: \"1\",\n  FRONT: \"2\",\n  RIGHT: \"3\",\n  BACK: \"4\",\n  LEFT: \"5\",\n  BOTTOM: \"6\",\n  TOP_FRONT_EDGE: \"7\",\n  TOP_RIGHT_EDGE: \"8\",\n  TOP_BACK_EDGE: \"9\",\n  TOP_LEFT_EDGE: \"10\",\n  FRONT_RIGHT_EDGE: \"11\",\n  BACK_RIGHT_EDGE: \"12\",\n  BACK_LEFT_EDGE: \"13\",\n  FRONT_LEFT_EDGE: \"14\",\n  BOTTOM_FRONT_EDGE: \"15\",\n  BOTTOM_RIGHT_EDGE: \"16\",\n  BOTTOM_BACK_EDGE: \"17\",\n  BOTTOM_LEFT_EDGE: \"18\",\n  TOP_FRONT_RIGHT_CORNER: \"19\",\n  TOP_BACK_RIGHT_CORNER: \"20\",\n  TOP_BACK_LEFT_CORNER: \"21\",\n  TOP_FRONT_LEFT_CORNER: \"22\",\n  BOTTOM_FRONT_RIGHT_CORNER: \"23\",\n  BOTTOM_BACK_RIGHT_CORNER: \"24\",\n  BOTTOM_BACK_LEFT_CORNER: \"25\",\n  BOTTOM_FRONT_LEFT_CORNER: \"26\"\n};\nconst CORNER_FACES = [\n  { name: FACES.TOP_FRONT_RIGHT_CORNER },\n  { name: FACES.TOP_BACK_RIGHT_CORNER },\n  { name: FACES.TOP_BACK_LEFT_CORNER },\n  { name: FACES.TOP_FRONT_LEFT_CORNER },\n  { name: FACES.BOTTOM_BACK_RIGHT_CORNER },\n  { name: FACES.BOTTOM_FRONT_RIGHT_CORNER },\n  { name: FACES.BOTTOM_FRONT_LEFT_CORNER },\n  { name: FACES.BOTTOM_BACK_LEFT_CORNER }\n];\nconst EDGE_FACES = [\n  { name: FACES.TOP_FRONT_EDGE },\n  { name: FACES.TOP_RIGHT_EDGE },\n  { name: FACES.TOP_BACK_EDGE },\n  { name: FACES.TOP_LEFT_EDGE },\n  // flip back and front bottom edges\n  { name: FACES.BOTTOM_BACK_EDGE },\n  { name: FACES.BOTTOM_RIGHT_EDGE },\n  { name: FACES.BOTTOM_FRONT_EDGE },\n  { name: FACES.BOTTOM_LEFT_EDGE }\n];\nconst EDGE_FACES_SIDE = [\n  { name: FACES.FRONT_RIGHT_EDGE },\n  { name: FACES.BACK_RIGHT_EDGE },\n  { name: FACES.BACK_LEFT_EDGE },\n  { name: FACES.FRONT_LEFT_EDGE }\n];\nclass ViewCube extends THREE.Object3D {\n  /**\n   * Construct one instance of view cube 3d object\n   * @param cubeSize Size of area ocupied by view cube\n   * @param borderSize Border size of view cube\n   * @param isShowOutline Flag to decide whether to show edge of view cube\n   * @param faceColor Face color of view cube\n   * @param outlineColor Edge color of view cube\n   * @param faceNames Texts in each face of view cube\n   */\n  constructor(cubeSize = 60, borderSize = 5, isShowOutline = true, faceColor = 13421772, outlineColor = 10066329, faceNames = DEFAULT_FACENAMES) {\n    super();\n    this._cubeSize = cubeSize;\n    this._borderSize = borderSize;\n    this._isShowOutline = isShowOutline;\n    this._faceColor = faceColor;\n    this._outlineColor = outlineColor;\n    this.build(faceNames);\n  }\n  /**\n   * Free the GPU-related resources allocated by this instance. Call this method whenever this instance\n   * is no longer used in your app.\n   */\n  dispose() {\n    this.children.forEach((child) => {\n      var _a, _b, _c, _d;\n      const mesh = child;\n      (_a = mesh.material) == null ? void 0 : _a.dispose();\n      (_c = (_b = mesh.material) == null ? void 0 : _b.map) == null ? void 0 : _c.dispose();\n      (_d = mesh.geometry) == null ? void 0 : _d.dispose();\n    });\n  }\n  build(faceNames) {\n    const faceSize = this._cubeSize - this._borderSize * 2;\n    const faceOffset = this._cubeSize / 2;\n    const borderSize = this._borderSize;\n    const cubeFaces = this.createCubeFaces(faceSize, faceOffset);\n    const faceMaterials = createFaceMaterials(faceNames);\n    for (const [i, props] of faceMaterials.entries()) {\n      const face = cubeFaces.children[i];\n      const material = face.material;\n      material.color.setHex(this._faceColor);\n      material.map = props.map;\n      face.name = props.name;\n    }\n    this.add(cubeFaces);\n    const corners = [];\n    for (const [i, props] of CORNER_FACES.entries()) {\n      const corner = this.createCornerFaces(\n        borderSize,\n        faceOffset,\n        props.name,\n        { color: this._faceColor }\n      );\n      corner.rotateOnAxis(\n        new THREE.Vector3(0, 1, 0),\n        THREE.MathUtils.degToRad(i % 4 * 90)\n      );\n      corners.push(corner);\n    }\n    const topCorners = new THREE.Group();\n    const bottomCorners = new THREE.Group();\n    this.add(topCorners.add(...corners.slice(0, 4)));\n    this.add(\n      bottomCorners.add(...corners.slice(4)).rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI)\n    );\n    const edges = [];\n    for (const [i, props] of EDGE_FACES.entries()) {\n      const edge = this.createHorzEdgeFaces(\n        faceSize,\n        borderSize,\n        faceOffset,\n        props.name,\n        { color: this._faceColor }\n      );\n      edge.rotateOnAxis(\n        new THREE.Vector3(0, 1, 0),\n        THREE.MathUtils.degToRad(i % 4 * 90)\n      );\n      edges.push(edge);\n    }\n    const topEdges = new THREE.Group();\n    const bottomEdges = new THREE.Group();\n    this.add(topEdges.add(...edges.slice(0, 4)));\n    this.add(\n      bottomEdges.add(...edges.slice(4)).rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI)\n    );\n    const sideEdges = new THREE.Group();\n    for (const [i, props] of EDGE_FACES_SIDE.entries()) {\n      const edge = this.createVertEdgeFaces(\n        borderSize,\n        faceSize,\n        faceOffset,\n        props.name,\n        { color: this._faceColor }\n      );\n      edge.rotateOnAxis(\n        new THREE.Vector3(0, 1, 0),\n        THREE.MathUtils.degToRad(i * 90)\n      );\n      sideEdges.add(edge);\n    }\n    this.add(sideEdges);\n    if (this._isShowOutline) {\n      this.add(this.createCubeOutline(this._cubeSize));\n    }\n  }\n  createFace(size, position, { axis = [0, 1, 0], angle = 0, name = \"\", matProps = {} } = {}) {\n    if (!Array.isArray(size)) size = [size, size];\n    const material = new THREE.MeshBasicMaterial(matProps);\n    const geometry = new THREE.PlaneGeometry(size[0], size[1]);\n    const face = new THREE.Mesh(geometry, material);\n    face.name = name;\n    face.rotateOnAxis(\n      new THREE.Vector3(...axis),\n      THREE.MathUtils.degToRad(angle)\n    );\n    face.position.set(position[0], position[1], position[2]);\n    return face;\n  }\n  createCubeFaces(faceSize, offset) {\n    const faces = new THREE.Object3D();\n    faces.add(\n      this.createFace(faceSize, [0, 0, offset], { axis: [0, 1, 0], angle: 0 })\n    );\n    faces.add(\n      this.createFace(faceSize, [offset, 0, 0], { axis: [0, 1, 0], angle: 90 })\n    );\n    faces.add(\n      this.createFace(faceSize, [0, 0, -offset], {\n        axis: [0, 1, 0],\n        angle: 180\n      })\n    );\n    faces.add(\n      this.createFace(faceSize, [-offset, 0, 0], {\n        axis: [0, 1, 0],\n        angle: 270\n      })\n    );\n    faces.add(\n      this.createFace(faceSize, [0, offset, 0], {\n        axis: [1, 0, 0],\n        angle: -90\n      })\n    );\n    faces.add(\n      this.createFace(faceSize, [0, -offset, 0], {\n        axis: [1, 0, 0],\n        angle: 90\n      })\n    );\n    return faces;\n  }\n  createCornerFaces(faceSize, offset, name = \"\", matProps = {}) {\n    const corner = new THREE.Object3D();\n    const borderOffset = offset - faceSize / 2;\n    corner.add(\n      this.createFace(faceSize, [borderOffset, borderOffset, offset], {\n        axis: [0, 1, 0],\n        angle: 0,\n        matProps,\n        name\n      })\n    );\n    corner.add(\n      this.createFace(faceSize, [offset, borderOffset, borderOffset], {\n        axis: [0, 1, 0],\n        angle: 90,\n        matProps,\n        name\n      })\n    );\n    corner.add(\n      this.createFace(faceSize, [borderOffset, offset, borderOffset], {\n        axis: [1, 0, 0],\n        angle: -90,\n        matProps,\n        name\n      })\n    );\n    return corner;\n  }\n  createHorzEdgeFaces(w, h, offset, name = \"\", matProps = {}) {\n    const edge = new THREE.Object3D();\n    const borderOffset = offset - h / 2;\n    edge.add(\n      this.createFace([w, h], [0, borderOffset, offset], {\n        axis: [0, 1, 0],\n        angle: 0,\n        name,\n        matProps\n      })\n    );\n    edge.add(\n      this.createFace([w, h], [0, offset, borderOffset], {\n        axis: [1, 0, 0],\n        angle: -90,\n        name,\n        matProps\n      })\n    );\n    return edge;\n  }\n  createVertEdgeFaces(w, h, offset, name = \"\", matProps = {}) {\n    const edge = new THREE.Object3D();\n    const borderOffset = offset - w / 2;\n    edge.add(\n      this.createFace([w, h], [borderOffset, 0, offset], {\n        axis: [0, 1, 0],\n        angle: 0,\n        name,\n        matProps\n      })\n    );\n    edge.add(\n      this.createFace([w, h], [offset, 0, borderOffset], {\n        axis: [0, 1, 0],\n        angle: 90,\n        name,\n        matProps\n      })\n    );\n    return edge;\n  }\n  createCubeOutline(size) {\n    const geometry = new THREE.BoxGeometry(size, size, size);\n    const geo = new THREE.EdgesGeometry(geometry);\n    const mat = new THREE.LineBasicMaterial({\n      color: this._outlineColor,\n      linewidth: 1\n    });\n    const wireframe = new THREE.LineSegments(geo, mat);\n    return wireframe;\n  }\n}\nconst MAIN_COLOR = 0xf9f9fa;\nconst HOVER_COLOR = 0xececec;\nconst OUTLINE_COLOR = 13421772;\nconst DEFAULT_VIEWCUBE_OPTIONS = {\n  pos: ObjectPosition.RIGHT_TOP,\n  dimension: 150,\n  faceColor: MAIN_COLOR,\n  hoverColor: HOVER_COLOR,\n  outlineColor: OUTLINE_COLOR,\n  faceNames: DEFAULT_FACENAMES\n};\nclass ViewCubeGizmo extends FixedPosGizmo {\n  /**\n   * Construct one instance of view cube gizmo\n   * @param camera Camera used in your canvas\n   * @param renderer Renderer used in your canvas\n   * @param options Options to customize view cube gizmo\n   */\n  constructor(camera, renderer, options = DEFAULT_VIEWCUBE_OPTIONS) {\n    const mergedOptions = {\n      ...DEFAULT_VIEWCUBE_OPTIONS,\n      ...options\n    };\n    super(camera, renderer, options.dimension, options.pos);\n    this.cube = new ViewCube(\n      2,\n      0.2,\n      true,\n      mergedOptions.faceColor,\n      mergedOptions.outlineColor,\n      mergedOptions.faceNames\n    );\n    this.add(this.cube);\n    this.handleMouseMove = this.handleMouseMove.bind(this);\n    this.handleMouseClick = this.handleMouseClick.bind(this);\n    this.listen(renderer.domElement);\n  }\n  /**\n   * Free the GPU-related resources allocated by this instance. Call this method whenever this instance\n   * is no longer used in your app.\n   */\n  dispose() {\n    this.cube.dispose();\n  }\n  listen(domElement) {\n    domElement.addEventListener(\"mousemove\", this.handleMouseMove);\n    domElement.addEventListener(\"click\", this.handleMouseClick);\n  }\n  handleMouseClick(event) {\n    const bbox = this.calculateViewportBbox();\n    if (bbox.containsPoint(new THREE.Vector2(event.offsetX, event.offsetY))) {\n      const pos = this.calculatePosInViewport(\n        event.offsetX,\n        event.offsetY,\n        bbox\n      );\n      this.checkSideTouch(pos.x, pos.y);\n    }\n  }\n  handleMouseMove(event) {\n    const bbox = this.calculateViewportBbox();\n    if (bbox.containsPoint(new THREE.Vector2(event.offsetX, event.offsetY))) {\n      const pos = this.calculatePosInViewport(\n        event.offsetX,\n        event.offsetY,\n        bbox\n      );\n      this.checkSideOver(pos.x, pos.y);\n    }\n  }\n  checkSideTouch(x, y) {\n    const raycaster = new THREE.Raycaster();\n    raycaster.setFromCamera(new THREE.Vector2(x, y), this.gizmoCamera);\n    const intersects = raycaster.intersectObjects(this.cube.children, true);\n    if (intersects.length) {\n      for (const { object } of intersects) {\n        if (object.name) {\n          const quaternion = this.getRotation(object.name);\n          this.dispatchEvent({\n            type: \"change\",\n            quaternion\n          });\n          break;\n        }\n      }\n    }\n  }\n  checkSideOver(x, y) {\n    const raycaster = new THREE.Raycaster();\n    raycaster.setFromCamera(new THREE.Vector2(x, y), this.gizmoCamera);\n    const intersects = raycaster.intersectObjects(this.cube.children, true);\n    this.cube.traverse(function(obj) {\n      if (obj.name) {\n        const mesh = obj;\n        mesh.material.color.setHex(MAIN_COLOR);\n      }\n    });\n    if (intersects.length) {\n      for (const { object } of intersects) {\n        if (object.name) {\n          object.parent.children.forEach(function(child) {\n            if (child.name === object.name) {\n              const mesh = child;\n              mesh.material.color.setHex(\n                HOVER_COLOR\n              );\n            }\n          });\n          break;\n        }\n      }\n    }\n  }\n  getRotation(side) {\n    const targetQuaternion = new THREE.Quaternion();\n    switch (side) {\n      case FACES.FRONT:\n        targetQuaternion.setFromEuler(new THREE.Euler());\n        break;\n      case FACES.RIGHT:\n        targetQuaternion.setFromEuler(new THREE.Euler(0, Math.PI * 0.5, 0));\n        break;\n      case FACES.BACK:\n        targetQuaternion.setFromEuler(new THREE.Euler(0, Math.PI, 0));\n        break;\n      case FACES.LEFT:\n        targetQuaternion.setFromEuler(new THREE.Euler(0, -Math.PI * 0.5, 0));\n        break;\n      case FACES.TOP:\n        targetQuaternion.setFromEuler(new THREE.Euler(-Math.PI * 0.5, 0, 0));\n        break;\n      case FACES.BOTTOM:\n        targetQuaternion.setFromEuler(new THREE.Euler(Math.PI * 0.5, 0, 0));\n        break;\n      case FACES.TOP_FRONT_EDGE:\n        targetQuaternion.setFromEuler(new THREE.Euler(-Math.PI * 0.25, 0, 0));\n        break;\n      case FACES.TOP_RIGHT_EDGE:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(-Math.PI * 0.25, Math.PI * 0.5, 0, \"YXZ\")\n        );\n        break;\n      case FACES.TOP_BACK_EDGE:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(-Math.PI * 0.25, Math.PI, 0, \"YXZ\")\n        );\n        break;\n      case FACES.TOP_LEFT_EDGE:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(-Math.PI * 0.25, -Math.PI * 0.5, 0, \"YXZ\")\n        );\n        break;\n      case FACES.BOTTOM_FRONT_EDGE:\n        targetQuaternion.setFromEuler(new THREE.Euler(Math.PI * 0.25, 0, 0));\n        break;\n      case FACES.BOTTOM_RIGHT_EDGE:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(Math.PI * 0.25, Math.PI * 0.5, 0, \"YXZ\")\n        );\n        break;\n      case FACES.BOTTOM_BACK_EDGE:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(Math.PI * 0.25, Math.PI, 0, \"YXZ\")\n        );\n        break;\n      case FACES.BOTTOM_LEFT_EDGE:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(Math.PI * 0.25, -Math.PI * 0.5, 0, \"YXZ\")\n        );\n        break;\n      case FACES.FRONT_RIGHT_EDGE:\n        targetQuaternion.setFromEuler(new THREE.Euler(0, Math.PI * 0.25, 0));\n        break;\n      case FACES.BACK_RIGHT_EDGE:\n        targetQuaternion.setFromEuler(new THREE.Euler(0, Math.PI * 0.75, 0));\n        break;\n      case FACES.BACK_LEFT_EDGE:\n        targetQuaternion.setFromEuler(new THREE.Euler(0, -Math.PI * 0.75, 0));\n        break;\n      case FACES.FRONT_LEFT_EDGE:\n        targetQuaternion.setFromEuler(new THREE.Euler(0, -Math.PI * 0.25, 0));\n        break;\n      case FACES.TOP_FRONT_RIGHT_CORNER:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(-Math.PI * 0.25, -Math.PI * 1.75, 0)\n        );\n        break;\n      case FACES.TOP_BACK_RIGHT_CORNER:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(Math.PI * 0.25, -Math.PI * 1.25, 0)\n        );\n        break;\n      case FACES.TOP_BACK_LEFT_CORNER:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(Math.PI * 0.25, -Math.PI * 0.75, 0)\n        );\n        break;\n      case FACES.TOP_FRONT_LEFT_CORNER:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(-Math.PI * 0.25, -Math.PI * 0.25, 0)\n        );\n        break;\n      case FACES.BOTTOM_FRONT_RIGHT_CORNER:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(Math.PI * 0.25, -Math.PI * 1.75, 0)\n        );\n        break;\n      case FACES.BOTTOM_BACK_RIGHT_CORNER:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(-Math.PI * 0.25, -Math.PI * 1.25, 0)\n        );\n        break;\n      case FACES.BOTTOM_BACK_LEFT_CORNER:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(-Math.PI * 0.25, -Math.PI * 0.75, 0)\n        );\n        break;\n      case FACES.BOTTOM_FRONT_LEFT_CORNER:\n        targetQuaternion.setFromEuler(\n          new THREE.Euler(Math.PI * 0.25, -Math.PI * 0.25, 0)\n        );\n        break;\n      default:\n        console.error(\n          `[ViewCubeGizmo]: Invalid face, edge, or corner name '${side}'!`\n        );\n        break;\n    }\n    return targetQuaternion;\n  }\n}\nconst DEFAULT_AXES_OPTIONS = {\n  pos: ObjectPosition.LEFT_BOTTOM,\n  size: 100,\n  hasZAxis: true\n};\nclass AxesGizmo extends FixedPosGizmo {\n  constructor(camera, renderer, options) {\n    const mergedOptions = {\n      ...DEFAULT_AXES_OPTIONS,\n      ...options\n    };\n    super(camera, renderer, mergedOptions.size, options.pos);\n    this.hasZAxis = mergedOptions.hasZAxis;\n    const vertices = [0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0];\n    const colors = [1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0];\n    if (this.hasZAxis) {\n      vertices.push(0, 0, 0, 0, 0, 2);\n      colors.push(0, 0, 1, 0, 0.6, 1);\n    }\n    const geometry = new THREE.BufferGeometry();\n    geometry.setAttribute(\n      \"position\",\n      new THREE.Float32BufferAttribute(vertices, 3)\n    );\n    geometry.setAttribute(\"color\", new THREE.Float32BufferAttribute(colors, 3));\n    const material = new THREE.LineBasicMaterial({\n      vertexColors: true,\n      toneMapped: false\n    });\n    this.axes = new THREE.LineSegments(geometry, material);\n    this.axes.position.set(-1, -1, -1);\n    this.add(this.axes);\n    this.xText = createTextSprite(\"X\");\n    this.xText.position.set(1.5, -1, -1);\n    this.add(this.xText);\n    this.yText = createTextSprite(\"Y\");\n    this.yText.position.set(-1, 1.5, -1);\n    this.add(this.yText);\n    if (this.hasZAxis) {\n      this.zText = createTextSprite(\"Z\");\n      this.zText.position.set(-1, -1, 1.5);\n      this.add(this.zText);\n    }\n  }\n  /**\n   * Set color of x-axis and y-axis\n   * @param xAxisColor color of x-axis\n   * @param yAxisColor color of y-axis\n   */\n  setLineColors(xAxisColor, yAxisColor) {\n    const color = new THREE.Color();\n    const array = this.axes.geometry.attributes.color.array;\n    color.set(xAxisColor);\n    color.toArray(array, 0);\n    color.toArray(array, 3);\n    color.set(yAxisColor);\n    color.toArray(array, 6);\n    color.toArray(array, 9);\n    this.axes.geometry.attributes.color.needsUpdate = true;\n    return this;\n  }\n  /**\n   * Set text color\n   * @param color text color\n   */\n  setTextColor(color) {\n    this.xText.material.color = color;\n    this.yText.material.color = color;\n  }\n  /**\n   * Free the GPU-related resources allocated by this instance. Call this method whenever this instance\n   * is no longer used in your app.\n   */\n  dispose() {\n    var _a, _b;\n    this.axes.geometry.dispose();\n    const material = this.axes.material;\n    material.dispose();\n    this.xText.geometry.dispose();\n    this.xText.material.dispose();\n    this.yText.geometry.dispose();\n    this.yText.material.dispose();\n    if (this.hasZAxis) {\n      (_a = this.zText) == null ? void 0 : _a.geometry.dispose();\n      (_b = this.zText) == null ? void 0 : _b.material.dispose();\n    }\n  }\n}\nclass SimpleCameraControls {\n  /**\n   * Construct one instance of view cube helper\n   * @param camera Camera used in your canvas\n   * @param renderer Renderer used in your canvas\n   * @param options Options to customize view cube helper\n   */\n  constructor(camera) {\n    this.camera = camera;\n    this.animating = false;\n    this.turnRate = 2 * Math.PI;\n    this.target = new THREE.Vector3();\n    this.q1 = new THREE.Quaternion();\n    this.q2 = new THREE.Quaternion();\n    this.radius = 0;\n    this.clock = new THREE.Clock();\n  }\n  /**\n   * Set associated obit controls\n   * @param controls The associated orbit controls\n   */\n  setControls(controls) {\n    if (!controls) return;\n    this.controls = controls;\n  }\n  /**\n   * Animation loop\n   */\n  update() {\n    var _a;\n    if (this.animating === false) return;\n    const delta = this.clock.getDelta();\n    const step = delta * this.turnRate;\n    this.q1.rotateTowards(this.q2, step);\n    this.camera.position.set(0, 0, 1).applyQuaternion(this.q1).multiplyScalar(this.radius).add(this.target);\n    this.camera.quaternion.rotateTowards(this.q2, step);\n    this.camera.updateProjectionMatrix();\n    (_a = this.controls) == null ? void 0 : _a.update();\n    if (this.q1.angleTo(this.q2) <= 1e-5) {\n      this.animating = false;\n      this.clock.stop();\n    }\n  }\n  /**\n   * Fly with the target quaterion\n   * @param quaternion\n   */\n  flyTo(quaternion) {\n    const focusPoint = new THREE.Vector3();\n    const targetPosition = new THREE.Vector3(0, 0, 1);\n    this.radius = this.camera.position.distanceTo(focusPoint);\n    targetPosition.applyQuaternion(quaternion).multiplyScalar(this.radius).add(focusPoint);\n    const dummy = new THREE.Object3D();\n    dummy.position.copy(focusPoint);\n    dummy.lookAt(this.camera.position);\n    this.q1.copy(dummy.quaternion);\n    dummy.lookAt(targetPosition);\n    this.q2.copy(dummy.quaternion);\n    this.animating = true;\n    this.clock.start();\n  }\n}\nexport {\n  AxesGizmo,\n  DEFAULT_AXES_OPTIONS,\n  DEFAULT_FACENAMES,\n  DEFAULT_VIEWCUBE_OPTIONS,\n  FixedPosGizmo,\n  ObjectPosition,\n  SimpleCameraControls,\n  ViewCube,\n  ViewCubeGizmo\n};\n"
  },
  {
    "path": "public/assets/lib/vendor/wavesurfer.js",
    "content": "/*! wavesurfer.js 1.4.0 (Mon, 10 Apr 2017 08:55:35 GMT)\n* https://github.com/katspaugh/wavesurfer.js\n* @license BSD-3-Clause */\n!function(a,b){\"function\"==typeof define&&define.amd?define(\"wavesurfer\",[],function(){return a.WaveSurfer=b()}):\"object\"==typeof exports?module.exports=b():a.WaveSurfer=b()}(this,function(){\"use strict\";var a={defaultParams:{audioContext:null,audioRate:1,autoCenter:!0,backend:\"WebAudio\",barHeight:1,closeAudioContext:!1,container:null,cursorColor:\"#333\",cursorWidth:1,dragSelection:!0,fillParent:!0,forceDecode:!1,height:128,hideScrollbar:!1,interact:!0,loopSelection:!0,mediaContainer:null,mediaControls:!1,mediaType:\"audio\",minPxPerSec:20,partialRender:!1,pixelRatio:window.devicePixelRatio||screen.deviceXDPI/screen.logicalXDPI,progressColor:\"#555\",normalize:!1,renderer:\"MultiCanvas\",scrollParent:!1,skipLength:2,splitChannels:!1,waveColor:\"#999\"},init:function(b){if(this.params=a.util.extend({},this.defaultParams,b),this.container=\"string\"==typeof b.container?document.querySelector(this.params.container):this.params.container,!this.container)throw new Error(\"Container element not found\");if(null==this.params.mediaContainer?this.mediaContainer=this.container:\"string\"==typeof this.params.mediaContainer?this.mediaContainer=document.querySelector(this.params.mediaContainer):this.mediaContainer=this.params.mediaContainer,!this.mediaContainer)throw new Error(\"Media Container element not found\");this.savedVolume=0,this.isMuted=!1,this.tmpEvents=[],this.currentAjax=null,this.createDrawer(),this.createBackend(),this.createPeakCache(),this.isDestroyed=!1},createDrawer:function(){var b=this;this.drawer=Object.create(a.Drawer[this.params.renderer]),this.drawer.init(this.container,this.params),this.drawer.on(\"redraw\",function(){b.drawBuffer(),b.drawer.progress(b.backend.getPlayedPercents())}),this.drawer.on(\"click\",function(a,c){setTimeout(function(){b.seekTo(c)},0)}),this.drawer.on(\"scroll\",function(a){b.params.partialRender&&b.drawBuffer(),b.fireEvent(\"scroll\",a)})},createBackend:function(){var b=this;this.backend&&this.backend.destroy(),\"AudioElement\"==this.params.backend&&(this.params.backend=\"MediaElement\"),\"WebAudio\"!=this.params.backend||a.WebAudio.supportsWebAudio()||(this.params.backend=\"MediaElement\"),this.backend=Object.create(a[this.params.backend]),this.backend.init(this.params),this.backend.on(\"finish\",function(){b.fireEvent(\"finish\")}),this.backend.on(\"play\",function(){b.fireEvent(\"play\")}),this.backend.on(\"pause\",function(){b.fireEvent(\"pause\")}),this.backend.on(\"audioprocess\",function(a){b.drawer.progress(b.backend.getPlayedPercents()),b.fireEvent(\"audioprocess\",a)})},createPeakCache:function(){this.params.partialRender&&(this.peakCache=Object.create(a.PeakCache),this.peakCache.init())},getDuration:function(){return this.backend.getDuration()},getCurrentTime:function(){return this.backend.getCurrentTime()},play:function(a,b){this.fireEvent(\"interaction\",this.play.bind(this,a,b)),this.backend.play(a,b)},pause:function(){this.backend.isPaused()||this.backend.pause()},playPause:function(){this.backend.isPaused()?this.play():this.pause()},isPlaying:function(){return!this.backend.isPaused()},skipBackward:function(a){this.skip(-a||-this.params.skipLength)},skipForward:function(a){this.skip(a||this.params.skipLength)},skip:function(a){var b=this.getCurrentTime()||0,c=this.getDuration()||1;b=Math.max(0,Math.min(c,b+(a||0))),this.seekAndCenter(b/c)},seekAndCenter:function(a){this.seekTo(a),this.drawer.recenter(a)},seekTo:function(a){this.fireEvent(\"interaction\",this.seekTo.bind(this,a));var b=this.backend.isPaused();b||this.backend.pause();var c=this.params.scrollParent;this.params.scrollParent=!1,this.backend.seekTo(a*this.getDuration()),this.drawer.progress(this.backend.getPlayedPercents()),b||this.backend.play(),this.params.scrollParent=c,this.fireEvent(\"seek\",a)},stop:function(){this.pause(),this.seekTo(0),this.drawer.progress(0)},setVolume:function(a){this.backend.setVolume(a)},getVolume:function(){return this.backend.getVolume()},setPlaybackRate:function(a){this.backend.setPlaybackRate(a)},getPlaybackRate:function(){return this.backend.getPlaybackRate()},toggleMute:function(){this.setMute(!this.isMuted)},setMute:function(a){a!==this.isMuted&&(a?(this.savedVolume=this.backend.getVolume(),this.backend.setVolume(0),this.isMuted=!0):(this.backend.setVolume(this.savedVolume),this.isMuted=!1))},getMute:function(){return this.isMuted},getFilters:function(){return this.backend.filters||[]},toggleScroll:function(){this.params.scrollParent=!this.params.scrollParent,this.drawBuffer()},toggleInteraction:function(){this.params.interact=!this.params.interact},drawBuffer:function(){var a=Math.round(this.getDuration()*this.params.minPxPerSec*this.params.pixelRatio),b=this.drawer.getWidth(),c=a,d=this.drawer.getScrollX(),e=Math.min(d+b,c);if(this.params.fillParent&&(!this.params.scrollParent||a<b)&&(c=b,d=0,e=c),this.params.partialRender)for(var f=this.peakCache.addRangeToPeakCache(c,d,e),g=0;g<f.length;g++){var h=this.backend.getPeaks(c,f[g][0],f[g][1]);this.drawer.drawPeaks(h,c,f[g][0],f[g][1])}else{d=0,e=c;var h=this.backend.getPeaks(c,d,e);this.drawer.drawPeaks(h,c,d,e)}this.fireEvent(\"redraw\",h,c)},zoom:function(a){this.params.minPxPerSec=a,this.params.scrollParent=!0,this.drawBuffer(),this.drawer.progress(this.backend.getPlayedPercents()),this.drawer.recenter(this.getCurrentTime()/this.getDuration()),this.fireEvent(\"zoom\",a)},loadArrayBuffer:function(a){this.decodeArrayBuffer(a,function(a){this.isDestroyed||this.loadDecodedBuffer(a)}.bind(this))},loadDecodedBuffer:function(a){this.backend.load(a),this.drawBuffer(),this.fireEvent(\"ready\")},loadBlob:function(a){var b=this,c=new FileReader;c.addEventListener(\"progress\",function(a){b.onProgress(a)}),c.addEventListener(\"load\",function(a){b.loadArrayBuffer(a.target.result)}),c.addEventListener(\"error\",function(){b.fireEvent(\"error\",\"Error reading file\")}),c.readAsArrayBuffer(a),this.empty()},load:function(a,b,c){switch(this.empty(),this.isMuted=!1,this.params.backend){case\"WebAudio\":return this.loadBuffer(a,b);case\"MediaElement\":return this.loadMediaElement(a,b,c)}},loadBuffer:function(a,b){var c=function(b){return b&&this.tmpEvents.push(this.once(\"ready\",b)),this.getArrayBuffer(a,this.loadArrayBuffer.bind(this))}.bind(this);return b?(this.backend.setPeaks(b),this.drawBuffer(),this.tmpEvents.push(this.once(\"interaction\",c)),void 0):c()},loadMediaElement:function(a,b,c){var d=a;if(\"string\"==typeof a)this.backend.load(d,this.mediaContainer,b,c);else{var e=a;this.backend.loadElt(e,b),d=e.src}this.tmpEvents.push(this.backend.once(\"canplay\",function(){this.drawBuffer(),this.fireEvent(\"ready\")}.bind(this)),this.backend.once(\"error\",function(a){this.fireEvent(\"error\",a)}.bind(this))),b&&this.backend.setPeaks(b),b&&!this.params.forceDecode||!this.backend.supportsWebAudio()||this.getArrayBuffer(d,function(a){this.decodeArrayBuffer(a,function(a){this.backend.buffer=a,this.backend.setPeaks(null),this.drawBuffer(),this.fireEvent(\"waveform-ready\")}.bind(this))}.bind(this))},decodeArrayBuffer:function(a,b){this.arraybuffer=a,this.backend.decodeArrayBuffer(a,function(c){this.isDestroyed||this.arraybuffer!=a||(b(c),this.arraybuffer=null)}.bind(this),this.fireEvent.bind(this,\"error\",\"Error decoding audiobuffer\"))},getArrayBuffer:function(b,c){var d=this,e=a.util.ajax({url:b,responseType:\"arraybuffer\"});return this.currentAjax=e,this.tmpEvents.push(e.on(\"progress\",function(a){d.onProgress(a)}),e.on(\"success\",function(a,b){c(a),d.currentAjax=null}),e.on(\"error\",function(a){d.fireEvent(\"error\",\"XHR error: \"+a.target.statusText),d.currentAjax=null})),e},onProgress:function(a){if(a.lengthComputable)var b=a.loaded/a.total;else b=a.loaded/(a.loaded+1e6);this.fireEvent(\"loading\",Math.round(100*b),a.target)},exportPCM:function(a,b,c){a=a||1024,b=b||1e4,c=c||!1;var d=this.backend.getPeaks(a,b),e=[].map.call(d,function(a){return Math.round(a*b)/b}),f=JSON.stringify(e);return c||window.open(\"data:application/json;charset=utf-8,\"+encodeURIComponent(f)),f},exportImage:function(a,b){return a||(a=\"image/png\"),b||(b=1),this.drawer.getImage(a,b)},cancelAjax:function(){this.currentAjax&&(this.currentAjax.xhr.abort(),this.currentAjax=null)},clearTmpEvents:function(){this.tmpEvents.forEach(function(a){a.un()})},empty:function(){this.backend.isPaused()||(this.stop(),this.backend.disconnectSource()),this.cancelAjax(),this.clearTmpEvents(),this.drawer.progress(0),this.drawer.setWidth(0),this.drawer.drawPeaks({length:this.drawer.getWidth()},0)},destroy:function(){this.fireEvent(\"destroy\"),this.cancelAjax(),this.clearTmpEvents(),this.unAll(),this.backend.destroy(),this.drawer.destroy(),this.isDestroyed=!0}};return a.create=function(b){var c=Object.create(a);return c.init(b),c},a.util={extend:function(a){var b=Array.prototype.slice.call(arguments,1);return b.forEach(function(b){Object.keys(b).forEach(function(c){a[c]=b[c]})}),a},debounce:function(a,b,c){var d,e,f,g=function(){f=null,c||a.apply(e,d)};return function(){e=this,d=arguments;var h=c&&!f;clearTimeout(f),f=setTimeout(g,b),f||(f=setTimeout(g,b)),h&&a.apply(e,d)}},min:function(a){var b=+(1/0);for(var c in a)a[c]<b&&(b=a[c]);return b},max:function(a){var b=-(1/0);for(var c in a)a[c]>b&&(b=a[c]);return b},getId:function(){return\"wavesurfer_\"+Math.random().toString(32).substring(2)},ajax:function(b){var c=Object.create(a.Observer),d=new XMLHttpRequest,e=!1;return d.open(b.method||\"GET\",b.url,!0),d.responseType=b.responseType||\"json\",d.addEventListener(\"progress\",function(a){c.fireEvent(\"progress\",a),a.lengthComputable&&a.loaded==a.total&&(e=!0)}),d.addEventListener(\"load\",function(a){e||c.fireEvent(\"progress\",a),c.fireEvent(\"load\",a),200==d.status||206==d.status?c.fireEvent(\"success\",d.response,a):c.fireEvent(\"error\",a)}),d.addEventListener(\"error\",function(a){c.fireEvent(\"error\",a)}),d.send(),c.xhr=d,c}},a.Observer={on:function(a,b){this.handlers||(this.handlers={});var c=this.handlers[a];return c||(c=this.handlers[a]=[]),c.push(b),{name:a,callback:b,un:this.un.bind(this,a,b)}},un:function(a,b){if(this.handlers){var c=this.handlers[a];if(c)if(b)for(var d=c.length-1;d>=0;d--)c[d]==b&&c.splice(d,1);else c.length=0}},unAll:function(){this.handlers=null},once:function(a,b){var c=this,d=function(){b.apply(this,arguments),setTimeout(function(){c.un(a,d)},0)};return this.on(a,d)},fireEvent:function(a){if(this.handlers){var b=this.handlers[a],c=Array.prototype.slice.call(arguments,1);b&&b.forEach(function(a){a.apply(null,c)})}}},a.util.extend(a,a.Observer),a.WebAudio={scriptBufferSize:256,PLAYING_STATE:0,PAUSED_STATE:1,FINISHED_STATE:2,supportsWebAudio:function(){return!(!window.AudioContext&&!window.webkitAudioContext)},getAudioContext:function(){return a.WebAudio.audioContext||(a.WebAudio.audioContext=new(window.AudioContext||window.webkitAudioContext)),a.WebAudio.audioContext},getOfflineAudioContext:function(b){return a.WebAudio.offlineAudioContext||(a.WebAudio.offlineAudioContext=new(window.OfflineAudioContext||window.webkitOfflineAudioContext)(1,2,b)),a.WebAudio.offlineAudioContext},init:function(b){this.params=b,this.ac=b.audioContext||this.getAudioContext(),this.lastPlay=this.ac.currentTime,this.startPosition=0,this.scheduledPause=null,this.states=[Object.create(a.WebAudio.state.playing),Object.create(a.WebAudio.state.paused),Object.create(a.WebAudio.state.finished)],this.createVolumeNode(),this.createScriptNode(),this.createAnalyserNode(),this.setState(this.PAUSED_STATE),this.setPlaybackRate(this.params.audioRate),this.setLength(0)},disconnectFilters:function(){this.filters&&(this.filters.forEach(function(a){a&&a.disconnect()}),this.filters=null,this.analyser.connect(this.gainNode))},setState:function(a){this.state!==this.states[a]&&(this.state=this.states[a],this.state.init.call(this))},setFilter:function(){this.setFilters([].slice.call(arguments))},setFilters:function(a){this.disconnectFilters(),a&&a.length&&(this.filters=a,this.analyser.disconnect(),a.reduce(function(a,b){return a.connect(b),b},this.analyser).connect(this.gainNode))},createScriptNode:function(){this.ac.createScriptProcessor?this.scriptNode=this.ac.createScriptProcessor(this.scriptBufferSize):this.scriptNode=this.ac.createJavaScriptNode(this.scriptBufferSize),this.scriptNode.connect(this.ac.destination)},addOnAudioProcess:function(){var a=this;this.scriptNode.onaudioprocess=function(){var b=a.getCurrentTime();b>=a.getDuration()?(a.setState(a.FINISHED_STATE),a.fireEvent(\"pause\")):b>=a.scheduledPause?a.pause():a.state===a.states[a.PLAYING_STATE]&&a.fireEvent(\"audioprocess\",b)}},removeOnAudioProcess:function(){this.scriptNode.onaudioprocess=null},createAnalyserNode:function(){this.analyser=this.ac.createAnalyser(),this.analyser.connect(this.gainNode)},createVolumeNode:function(){this.ac.createGain?this.gainNode=this.ac.createGain():this.gainNode=this.ac.createGainNode(),this.gainNode.connect(this.ac.destination)},setVolume:function(a){this.gainNode.gain.value=a},getVolume:function(){return this.gainNode.gain.value},decodeArrayBuffer:function(a,b,c){this.offlineAc||(this.offlineAc=this.getOfflineAudioContext(this.ac?this.ac.sampleRate:44100)),this.offlineAc.decodeAudioData(a,function(a){b(a)}.bind(this),c)},setPeaks:function(a){this.peaks=a},setLength:function(a){if(!this.mergedPeaks||a!=2*this.mergedPeaks.length-1+2){this.splitPeaks=[],this.mergedPeaks=[];for(var b=this.buffer?this.buffer.numberOfChannels:1,c=0;c<b;c++)this.splitPeaks[c]=[],this.splitPeaks[c][2*(a-1)]=0,this.splitPeaks[c][2*(a-1)+1]=0;this.mergedPeaks[2*(a-1)]=0,this.mergedPeaks[2*(a-1)+1]=0}},getPeaks:function(a,b,c){if(this.peaks)return this.peaks;this.setLength(a);for(var d=this.buffer.length/a,e=~~(d/10)||1,f=this.buffer.numberOfChannels,g=0;g<f;g++)for(var h=this.splitPeaks[g],i=this.buffer.getChannelData(g),j=b;j<=c;j++){for(var k=~~(j*d),l=~~(k+d),m=0,n=0,o=k;o<l;o+=e){var p=i[o];p>n&&(n=p),p<m&&(m=p)}h[2*j]=n,h[2*j+1]=m,(0==g||n>this.mergedPeaks[2*j])&&(this.mergedPeaks[2*j]=n),(0==g||m<this.mergedPeaks[2*j+1])&&(this.mergedPeaks[2*j+1]=m)}return this.params.splitChannels?this.splitPeaks:this.mergedPeaks},getPlayedPercents:function(){return this.state.getPlayedPercents.call(this)},disconnectSource:function(){this.source&&this.source.disconnect()},destroy:function(){this.isPaused()||this.pause(),this.unAll(),this.buffer=null,this.disconnectFilters(),this.disconnectSource(),this.gainNode.disconnect(),this.scriptNode.disconnect(),this.analyser.disconnect(),this.params.closeAudioContext&&(\"function\"==typeof this.ac.close&&\"closed\"!=this.ac.state&&this.ac.close(),this.ac=null,this.params.audioContext?this.params.audioContext=null:a.WebAudio.audioContext=null,a.WebAudio.offlineAudioContext=null)},load:function(a){this.startPosition=0,this.lastPlay=this.ac.currentTime,this.buffer=a,this.createSource()},createSource:function(){this.disconnectSource(),this.source=this.ac.createBufferSource(),this.source.start=this.source.start||this.source.noteGrainOn,this.source.stop=this.source.stop||this.source.noteOff,this.source.playbackRate.value=this.playbackRate,this.source.buffer=this.buffer,this.source.connect(this.analyser)},isPaused:function(){return this.state!==this.states[this.PLAYING_STATE]},getDuration:function(){return this.buffer?this.buffer.duration:0},seekTo:function(a,b){if(this.buffer)return this.scheduledPause=null,null==a&&(a=this.getCurrentTime(),a>=this.getDuration()&&(a=0)),null==b&&(b=this.getDuration()),this.startPosition=a,this.lastPlay=this.ac.currentTime,this.state===this.states[this.FINISHED_STATE]&&this.setState(this.PAUSED_STATE),{start:a,end:b}},getPlayedTime:function(){return(this.ac.currentTime-this.lastPlay)*this.playbackRate},play:function(a,b){if(this.buffer){this.createSource();var c=this.seekTo(a,b);a=c.start,b=c.end,this.scheduledPause=b,this.source.start(0,a,b-a),\"suspended\"==this.ac.state&&this.ac.resume&&this.ac.resume(),this.setState(this.PLAYING_STATE),this.fireEvent(\"play\")}},pause:function(){this.scheduledPause=null,this.startPosition+=this.getPlayedTime(),this.source&&this.source.stop(0),this.setState(this.PAUSED_STATE),this.fireEvent(\"pause\")},getCurrentTime:function(){return this.state.getCurrentTime.call(this)},getPlaybackRate:function(){return this.playbackRate},setPlaybackRate:function(a){a=a||1,this.isPaused()?this.playbackRate=a:(this.pause(),this.playbackRate=a,this.play())}},a.WebAudio.state={},a.WebAudio.state.playing={init:function(){this.addOnAudioProcess()},getPlayedPercents:function(){var a=this.getDuration();return this.getCurrentTime()/a||0},getCurrentTime:function(){return this.startPosition+this.getPlayedTime()}},a.WebAudio.state.paused={init:function(){this.removeOnAudioProcess()},getPlayedPercents:function(){var a=this.getDuration();return this.getCurrentTime()/a||0},getCurrentTime:function(){return this.startPosition}},a.WebAudio.state.finished={init:function(){this.removeOnAudioProcess(),this.fireEvent(\"finish\")},getPlayedPercents:function(){return 1},getCurrentTime:function(){return this.getDuration()}},a.util.extend(a.WebAudio,a.Observer),a.MediaElement=Object.create(a.WebAudio),a.util.extend(a.MediaElement,{init:function(a){this.params=a,this.media={currentTime:0,duration:0,paused:!0,playbackRate:1,play:function(){},pause:function(){}},this.mediaType=a.mediaType.toLowerCase(),this.elementPosition=a.elementPosition,this.setPlaybackRate(this.params.audioRate),this.createTimer()},createTimer:function(){var a=this,b=function(){if(!a.isPaused()){a.fireEvent(\"audioprocess\",a.getCurrentTime());var c=window.requestAnimationFrame||window.webkitRequestAnimationFrame;c(b)}};this.on(\"play\",b)},load:function(a,b,c,d){var e=document.createElement(this.mediaType);e.controls=this.params.mediaControls,e.autoplay=this.params.autoplay||!1,e.preload=null==d?\"auto\":d,e.src=a,e.style.width=\"100%\";var f=b.querySelector(this.mediaType);f&&b.removeChild(f),b.appendChild(e),this._load(e,c)},loadElt:function(a,b){var c=a;c.controls=this.params.mediaControls,c.autoplay=this.params.autoplay||!1,this._load(c,b)},_load:function(a,b){var c=this;\"function\"==typeof a.load&&a.load(),a.addEventListener(\"error\",function(){c.fireEvent(\"error\",\"Error loading media element\")}),a.addEventListener(\"canplay\",function(){c.fireEvent(\"canplay\")}),a.addEventListener(\"ended\",function(){c.fireEvent(\"finish\")}),this.media=a,this.peaks=b,this.onPlayEnd=null,this.buffer=null,this.setPlaybackRate(this.playbackRate)},isPaused:function(){return!this.media||this.media.paused},getDuration:function(){var a=(this.buffer||this.media).duration;return a>=1/0&&(a=this.media.seekable.end(0)),a},getCurrentTime:function(){return this.media&&this.media.currentTime},getPlayedPercents:function(){return this.getCurrentTime()/this.getDuration()||0},getPlaybackRate:function(){return this.playbackRate||this.media.playbackRate},setPlaybackRate:function(a){this.playbackRate=a||1,this.media.playbackRate=this.playbackRate},seekTo:function(a){null!=a&&(this.media.currentTime=a),this.clearPlayEnd()},play:function(a,b){this.seekTo(a),this.media.play(),b&&this.setPlayEnd(b),this.fireEvent(\"play\")},pause:function(){this.media&&this.media.pause(),this.clearPlayEnd(),this.fireEvent(\"pause\")},setPlayEnd:function(a){var b=this;this.onPlayEnd=function(c){c>=a&&(b.pause(),b.seekTo(a))},this.on(\"audioprocess\",this.onPlayEnd)},clearPlayEnd:function(){this.onPlayEnd&&(this.un(\"audioprocess\",this.onPlayEnd),this.onPlayEnd=null)},getPeaks:function(b,c,d){return this.buffer?a.WebAudio.getPeaks.call(this,b,c,d):this.peaks||[]},getVolume:function(){return this.media.volume},setVolume:function(a){this.media.volume=a},destroy:function(){this.pause(),this.unAll(),this.media&&this.media.parentNode&&this.media.parentNode.removeChild(this.media),this.media=null}}),a.AudioElement=a.MediaElement,a.Drawer={init:function(a,b){this.container=a,this.params=b,this.width=0,this.height=b.height*this.params.pixelRatio,this.lastPos=0,this.initDrawer(b),this.createWrapper(),this.createElements()},createWrapper:function(){this.wrapper=this.container.appendChild(document.createElement(\"wave\")),this.style(this.wrapper,{display:\"block\",position:\"relative\",userSelect:\"none\",webkitUserSelect:\"none\",height:this.params.height+\"px\"}),(this.params.fillParent||this.params.scrollParent)&&this.style(this.wrapper,{width:\"100%\",overflowX:this.params.hideScrollbar?\"hidden\":\"auto\",overflowY:\"hidden\"}),this.setupWrapperEvents()},handleEvent:function(a,b){!b&&a.preventDefault();var c,d=a.targetTouches?a.targetTouches[0].clientX:a.clientX,e=this.wrapper.getBoundingClientRect(),f=this.width,g=this.getWidth();return!this.params.fillParent&&f<g?(c=(d-e.left)*this.params.pixelRatio/f||0,c>1&&(c=1)):c=(d-e.left+this.wrapper.scrollLeft)/this.wrapper.scrollWidth||0,c},setupWrapperEvents:function(){var a=this;this.wrapper.addEventListener(\"click\",function(b){var c=a.wrapper.offsetHeight-a.wrapper.clientHeight;if(0!=c){var d=a.wrapper.getBoundingClientRect();if(b.clientY>=d.bottom-c)return}a.params.interact&&a.fireEvent(\"click\",b,a.handleEvent(b))}),this.wrapper.addEventListener(\"scroll\",function(b){a.fireEvent(\"scroll\",b)})},drawPeaks:function(a,b,c,d){this.setWidth(b),this.params.barWidth?this.drawBars(a,0,c,d):this.drawWave(a,0,c,d)},style:function(a,b){return Object.keys(b).forEach(function(c){a.style[c]!==b[c]&&(a.style[c]=b[c])}),a},resetScroll:function(){null!==this.wrapper&&(this.wrapper.scrollLeft=0)},recenter:function(a){var b=this.wrapper.scrollWidth*a;this.recenterOnPosition(b,!0)},recenterOnPosition:function(a,b){var c=this.wrapper.scrollLeft,d=~~(this.wrapper.clientWidth/2),e=a-d,f=e-c,g=this.wrapper.scrollWidth-this.wrapper.clientWidth;if(0!=g){if(!b&&-d<=f&&f<d){var h=5;f=Math.max(-h,Math.min(h,f)),e=c+f}e=Math.max(0,Math.min(g,e)),e!=c&&(this.wrapper.scrollLeft=e)}},getScrollX:function(){return Math.round(this.wrapper.scrollLeft*this.params.pixelRatio)},getWidth:function(){return Math.round(this.container.clientWidth*this.params.pixelRatio)},setWidth:function(a){this.width!=a&&(this.width=a,this.params.fillParent||this.params.scrollParent?this.style(this.wrapper,{width:\"\"}):this.style(this.wrapper,{width:~~(this.width/this.params.pixelRatio)+\"px\"}),this.updateSize())},setHeight:function(a){a!=this.height&&(this.height=a,this.style(this.wrapper,{height:~~(this.height/this.params.pixelRatio)+\"px\"}),this.updateSize())},progress:function(a){var b=1/this.params.pixelRatio,c=Math.round(a*this.width)*b;if(c<this.lastPos||c-this.lastPos>=b){if(this.lastPos=c,this.params.scrollParent&&this.params.autoCenter){var d=~~(this.wrapper.scrollWidth*a);this.recenterOnPosition(d)}this.updateProgress(c)}},destroy:function(){this.unAll(),this.wrapper&&(this.container.removeChild(this.wrapper),this.wrapper=null)},initDrawer:function(){},createElements:function(){},updateSize:function(){},drawWave:function(a,b){},clearWave:function(){},updateProgress:function(a){}},a.util.extend(a.Drawer,a.Observer),a.Drawer.Canvas=Object.create(a.Drawer),a.util.extend(a.Drawer.Canvas,{createElements:function(){var a=this.wrapper.appendChild(this.style(document.createElement(\"canvas\"),{position:\"absolute\",zIndex:1,left:0,top:0,bottom:0}));if(this.waveCc=a.getContext(\"2d\"),this.progressWave=this.wrapper.appendChild(this.style(document.createElement(\"wave\"),{position:\"absolute\",zIndex:2,left:0,top:0,bottom:0,overflow:\"hidden\",width:\"0\",display:\"none\",boxSizing:\"border-box\",borderRightStyle:\"solid\",borderRightWidth:this.params.cursorWidth+\"px\",borderRightColor:this.params.cursorColor})),this.params.waveColor!=this.params.progressColor){var b=this.progressWave.appendChild(document.createElement(\"canvas\"));this.progressCc=b.getContext(\"2d\")}},updateSize:function(){var a=Math.round(this.width/this.params.pixelRatio);this.waveCc.canvas.width=this.width,this.waveCc.canvas.height=this.height,this.style(this.waveCc.canvas,{width:a+\"px\"}),this.style(this.progressWave,{display:\"block\"}),this.progressCc&&(this.progressCc.canvas.width=this.width,this.progressCc.canvas.height=this.height,this.style(this.progressCc.canvas,{width:a+\"px\"})),this.clearWave()},clearWave:function(){this.waveCc.clearRect(0,0,this.width,this.height),this.progressCc&&this.progressCc.clearRect(0,0,this.width,this.height)},drawBars:function(b,c,d,e){var f=this;if(b[0]instanceof Array){var g=b;if(this.params.splitChannels)return this.setHeight(g.length*this.params.height*this.params.pixelRatio),void g.forEach(function(a,b){f.drawBars(a,b,d,e)});b=g[0]}var h=[].some.call(b,function(a){return a<0}),i=1;h&&(i=2);var j=.5/this.params.pixelRatio,k=this.width,l=this.params.height*this.params.pixelRatio,m=l*c||0,n=l/2,o=b.length/i,p=this.params.barWidth*this.params.pixelRatio,q=Math.max(this.params.pixelRatio,~~(p/2)),r=p+q,s=1/this.params.barHeight;if(this.params.normalize){var t=a.util.max(b),u=a.util.min(b);s=-u>t?-u:t}var v=o/k;this.waveCc.fillStyle=this.params.waveColor,this.progressCc&&(this.progressCc.fillStyle=this.params.progressColor),[this.waveCc,this.progressCc].forEach(function(a){if(a)for(var c=d/v;c<e/v;c+=r){var f=b[Math.floor(c*v*i)]||0,g=Math.round(f/s*n);a.fillRect(c+j,n-g+m,p+j,2*g)}},this)},drawWave:function(b,c,d,e){var f=this;if(b[0]instanceof Array){var g=b;if(this.params.splitChannels)return this.setHeight(g.length*this.params.height*this.params.pixelRatio),void g.forEach(function(a,b){f.drawWave(a,b,d,e)});b=g[0]}var h=[].some.call(b,function(a){return a<0});if(!h){for(var i=[],j=0,k=b.length;j<k;j++)i[2*j]=b[j],i[2*j+1]=-b[j];b=i}var l=.5/this.params.pixelRatio,m=this.params.height*this.params.pixelRatio,n=m*c||0,o=m/2,p=~~(b.length/2),q=1;this.params.fillParent&&this.width!=p&&(q=this.width/p);var r=1/this.params.barHeight;if(this.params.normalize){var s=a.util.max(b),t=a.util.min(b);r=-t>s?-t:s}this.waveCc.fillStyle=this.params.waveColor,this.progressCc&&(this.progressCc.fillStyle=this.params.progressColor),[this.waveCc,this.progressCc].forEach(function(a){if(a){a.beginPath(),a.moveTo(d*q+l,o+n);for(var c=d;c<e;c++){var f=Math.round(b[2*c]/r*o);a.lineTo(c*q+l,o-f+n)}for(var c=e-1;c>=d;c--){var f=Math.round(b[2*c+1]/r*o);a.lineTo(c*q+l,o-f+n)}a.closePath(),a.fill(),a.fillRect(0,o+n-l,this.width,l)}},this)},updateProgress:function(a){this.style(this.progressWave,{width:a+\"px\"})},getImage:function(a,b){return this.waveCc.canvas.toDataURL(a,b)}}),a.Drawer.MultiCanvas=Object.create(a.Drawer),a.util.extend(a.Drawer.MultiCanvas,{initDrawer:function(a){if(this.maxCanvasWidth=null!=a.maxCanvasWidth?a.maxCanvasWidth:4e3,this.maxCanvasElementWidth=Math.round(this.maxCanvasWidth/this.params.pixelRatio),this.maxCanvasWidth<=1)throw\"maxCanvasWidth must be greater than 1.\";if(this.maxCanvasWidth%2==1)throw\"maxCanvasWidth must be an even number.\";this.hasProgressCanvas=this.params.waveColor!=this.params.progressColor,this.halfPixel=.5/this.params.pixelRatio,this.canvases=[]},createElements:function(){this.progressWave=this.wrapper.appendChild(this.style(document.createElement(\"wave\"),{position:\"absolute\",zIndex:2,left:0,top:0,bottom:0,overflow:\"hidden\",width:\"0\",display:\"none\",boxSizing:\"border-box\",borderRightStyle:\"solid\",borderRightWidth:this.params.cursorWidth+\"px\",borderRightColor:this.params.cursorColor})),this.addCanvas()},updateSize:function(){for(var a=Math.round(this.width/this.params.pixelRatio),b=Math.ceil(a/this.maxCanvasElementWidth);this.canvases.length<b;)this.addCanvas();for(;this.canvases.length>b;)this.removeCanvas();for(var c in this.canvases){var d=this.maxCanvasWidth+2*Math.ceil(this.params.pixelRatio/2);c==this.canvases.length-1&&(d=this.width-this.maxCanvasWidth*(this.canvases.length-1)),this.updateDimensions(this.canvases[c],d,this.height),this.clearWaveForEntry(this.canvases[c])}},addCanvas:function(){var a={},b=this.maxCanvasElementWidth*this.canvases.length;a.wave=this.wrapper.appendChild(this.style(document.createElement(\"canvas\"),{position:\"absolute\",zIndex:1,left:b+\"px\",top:0,bottom:0,height:\"100%\"})),a.waveCtx=a.wave.getContext(\"2d\"),this.hasProgressCanvas&&(a.progress=this.progressWave.appendChild(this.style(document.createElement(\"canvas\"),{position:\"absolute\",left:b+\"px\",top:0,bottom:0,height:\"100%\"})),a.progressCtx=a.progress.getContext(\"2d\")),this.canvases.push(a)},removeCanvas:function(){var a=this.canvases.pop();a.wave.parentElement.removeChild(a.wave),this.hasProgressCanvas&&a.progress.parentElement.removeChild(a.progress)},updateDimensions:function(a,b,c){var d=Math.round(b/this.params.pixelRatio),e=Math.round(this.width/this.params.pixelRatio);a.start=a.waveCtx.canvas.offsetLeft/e||0,a.end=a.start+d/e,a.waveCtx.canvas.width=b,a.waveCtx.canvas.height=c,this.style(a.waveCtx.canvas,{width:d+\"px\"}),this.style(this.progressWave,{display:\"block\"}),this.hasProgressCanvas&&(a.progressCtx.canvas.width=b,a.progressCtx.canvas.height=c,this.style(a.progressCtx.canvas,{width:d+\"px\"}))},clearWave:function(){for(var a in this.canvases)this.clearWaveForEntry(this.canvases[a])},clearWaveForEntry:function(a){a.waveCtx.clearRect(0,0,a.waveCtx.canvas.width,a.waveCtx.canvas.height),this.hasProgressCanvas&&a.progressCtx.clearRect(0,0,a.progressCtx.canvas.width,a.progressCtx.canvas.height)},drawBars:function(b,c,d,e){var f=this;if(b[0]instanceof Array){var g=b;if(this.params.splitChannels)return this.setHeight(g.length*this.params.height*this.params.pixelRatio),void g.forEach(function(a,b){f.drawBars(a,b,d,e)});b=g[0]}var h=[].some.call(b,function(a){return a<0}),i=1;h&&(i=2);var j=this.width,k=this.params.height*this.params.pixelRatio,l=k*c||0,m=k/2,n=b.length/i,o=this.params.barWidth*this.params.pixelRatio,p=Math.max(this.params.pixelRatio,~~(o/2)),q=o+p,r=1/this.params.barHeight;if(this.params.normalize){var s=a.util.max(b),t=a.util.min(b);r=-t>s?-t:s}for(var u=n/j,v=d/u;v<e/u;v+=q){var w=b[Math.floor(v*u*i)]||0,x=Math.round(w/r*m);this.fillRect(v+this.halfPixel,m-x+l,o+this.halfPixel,2*x)}},drawWave:function(b,c,d,e){var f=this;if(b[0]instanceof Array){var g=b;if(this.params.splitChannels)return this.setHeight(g.length*this.params.height*this.params.pixelRatio),void g.forEach(function(a,b){f.drawWave(a,b,d,e)});b=g[0]}var h=[].some.call(b,function(a){return a<0});if(!h){for(var i=[],j=0,k=b.length;j<k;j++)i[2*j]=b[j],i[2*j+1]=-b[j];b=i}var l=this.params.height*this.params.pixelRatio,m=l*c||0,n=l/2,o=1/this.params.barHeight;if(this.params.normalize){var p=a.util.max(b),q=a.util.min(b);o=-q>p?-q:p}this.drawLine(b,o,n,m,d,e),this.fillRect(0,n+m-this.halfPixel,this.width,this.halfPixel)},drawLine:function(a,b,c,d,e,f){for(var g in this.canvases){var h=this.canvases[g];this.setFillStyles(h),this.drawLineToContext(h,h.waveCtx,a,b,c,d,e,f),this.drawLineToContext(h,h.progressCtx,a,b,c,d,e,f)}},drawLineToContext:function(a,b,c,d,e,f,g,h){if(b){var i=c.length/2,j=1;this.params.fillParent&&this.width!=i&&(j=this.width/i);var k=Math.round(i*a.start),l=Math.round(i*a.end);if(!(k>h||l<g)){var m=Math.max(k,g),n=Math.min(l,h);b.beginPath(),b.moveTo((m-k)*j+this.halfPixel,e+f);for(var o=m;o<n;o++){var p=c[2*o]||0,q=Math.round(p/d*e);b.lineTo((o-k)*j+this.halfPixel,e-q+f)}for(var o=n-1;o>=m;o--){var p=c[2*o+1]||0,q=Math.round(p/d*e);b.lineTo((o-k)*j+this.halfPixel,e-q+f)}b.closePath(),b.fill()}}},fillRect:function(a,b,c,d){for(var e=Math.floor(a/this.maxCanvasWidth),f=Math.min(Math.ceil((a+c)/this.maxCanvasWidth)+1,this.canvases.length),g=e;g<f;g++){var h=this.canvases[g],i=g*this.maxCanvasWidth,j={x1:Math.max(a,g*this.maxCanvasWidth),y1:b,x2:Math.min(a+c,g*this.maxCanvasWidth+h.waveCtx.canvas.width),y2:b+d};j.x1<j.x2&&(this.setFillStyles(h),this.fillRectToContext(h.waveCtx,j.x1-i,j.y1,j.x2-j.x1,j.y2-j.y1),this.fillRectToContext(h.progressCtx,j.x1-i,j.y1,j.x2-j.x1,j.y2-j.y1))}},fillRectToContext:function(a,b,c,d,e){a&&a.fillRect(b,c,d,e)},setFillStyles:function(a){a.waveCtx.fillStyle=this.params.waveColor,this.hasProgressCanvas&&(a.progressCtx.fillStyle=this.params.progressColor)},updateProgress:function(a){this.style(this.progressWave,{width:a+\"px\"})},getImage:function(a,b){var c=[];return this.canvases.forEach(function(d){c.push(d.wave.toDataURL(a,b))}),c.length>1?c:c[0]}}),a.Drawer.SplitWavePointPlot=Object.create(a.Drawer.Canvas),a.util.extend(a.Drawer.SplitWavePointPlot,{defaultPlotParams:{plotNormalizeTo:\"whole\",plotTimeStart:0,plotMin:0,\nplotMax:1,plotColor:\"#f63\",plotProgressColor:\"#F00\",plotPointHeight:2,plotPointWidth:2,plotSeparator:!0,plotSeparatorColor:\"black\",plotRangeDisplay:!1,plotRangeUnits:\"\",plotRangePrecision:4,plotRangeIgnoreOutliers:!1,plotRangeFontSize:12,plotRangeFontType:\"Ariel\",waveDrawMedianLine:!0,plotFileDelimiter:\"\\t\"},plotTimeStart:0,plotTimeEnd:-1,plotArrayLoaded:!1,plotArray:[],plotPoints:[],plotMin:0,plotMax:1,initDrawer:function(a){var b=this;for(var c in this.defaultPlotParams)void 0===this.params[c]&&(this.params[c]=this.defaultPlotParams[c]);if(this.plotTimeStart=this.params.plotTimeStart,void 0!==this.params.plotTimeEnd&&(this.plotTimeEnd=this.params.plotTimeEnd),Array.isArray(a.plotArray))this.plotArray=a.plotArray,this.plotArrayLoaded=!0;else{var d=function(a){b.plotArray=a,b.plotArrayLoaded=!0,b.fireEvent(\"plot_array_loaded\")};this.loadPlotArrayFromFile(a.plotFileUrl,d,this.params.plotFileDelimiter)}},drawPeaks:function(a,b,c,d){if(1==this.plotArrayLoaded)this.setWidth(b),this.splitChannels=!0,this.params.height=this.params.height/2,a[0]instanceof Array&&(a=a[0]),this.params.barWidth?this.drawBars(a,1,c,d):this.drawWave(a,1,c,d),this.params.height=2*this.params.height,this.calculatePlots(),this.drawPlots();else{var e=this;e.on(\"plot-array-loaded\",function(){e.drawPeaks(a,b,c,d)})}},drawPlots:function(){var a=this.params.height*this.params.pixelRatio/2,b=.5/this.params.pixelRatio;this.waveCc.fillStyle=this.params.plotColor,this.progressCc&&(this.progressCc.fillStyle=this.params.plotProgressColor);for(var c in this.plotPoints){var d=parseInt(c),e=a-this.params.plotPointHeight-this.plotPoints[c]*(a-this.params.plotPointHeight),f=this.params.plotPointHeight;this.waveCc.fillRect(d,e,this.params.plotPointWidth,f),this.progressCc&&this.progressCc.fillRect(d,e,this.params.plotPointWidth,f)}this.params.plotSeparator&&(this.waveCc.fillStyle=this.params.plotSeparatorColor,this.waveCc.fillRect(0,a,this.width,b)),this.params.plotRangeDisplay&&this.displayPlotRange()},displayPlotRange:function(){var a=this.params.plotRangeFontSize*this.params.pixelRatio,b=this.plotMax.toPrecision(this.params.plotRangePrecision)+\" \"+this.params.plotRangeUnits,c=this.plotMin.toPrecision(this.params.plotRangePrecision)+\" \"+this.params.plotRangeUnits;this.waveCc.font=a.toString()+\"px \"+this.params.plotRangeFontType,this.waveCc.fillText(b,3,a),this.waveCc.fillText(c,3,this.height/2)},calculatePlots:function(){this.plotPoints={},this.calculatePlotTimeEnd();for(var a=[],b=-1,c=0,d=99999999999999,e=0,f=99999999999999,g=this.plotTimeEnd-this.plotTimeStart,h=0;h<this.plotArray.length;h++){var i=this.plotArray[h];if(i.value>c&&(c=i.value),i.value<d&&(d=i.value),i.time>=this.plotTimeStart&&i.time<=this.plotTimeEnd){var j=Math.round(this.width*(i.time-this.plotTimeStart)/g);if(a.push(i.value),j!==b&&a.length>0){var k=this.avg(a);k>e&&(e=k),k<f&&(f=k),this.plotPoints[b]=k,a=[]}b=j}}\"whole\"==this.params.plotNormalizeTo?(this.plotMin=d,this.plotMax=c):\"values\"==this.params.plotNormalizeTo?(this.plotMin=this.params.plotMin,this.plotMax=this.params.plotMax):(this.plotMin=f,this.plotMax=e),this.normalizeValues()},normalizeValues:function(){var a={};if(\"none\"!==this.params.plotNormalizeTo){for(var b in this.plotPoints){var c=(this.plotPoints[b]-this.plotMin)/(this.plotMax-this.plotMin);c>1?this.params.plotRangeIgnoreOutliers||(a[b]=1):c<0?this.params.plotRangeIgnoreOutliers||(a[b]=0):a[b]=c}this.plotPoints=a}},loadPlotArrayFromFile:function(b,c,d){void 0===d&&(d=\"\\t\");var e=[],f={url:b,responseType:\"text\"},g=a.util.ajax(f);g.on(\"load\",function(a){if(200==a.currentTarget.status){for(var b=a.currentTarget.responseText.split(\"\\n\"),f=0;f<b.length;f++){var g=b[f].split(d);2==g.length&&e.push({time:parseFloat(g[0]),value:parseFloat(g[1])})}c(e)}})},calculatePlotTimeEnd:function(){void 0!==this.params.plotTimeEnd?this.plotTimeEnd=this.params.plotTimeEnd:this.plotTimeEnd=this.plotArray[this.plotArray.length-1].time},avg:function(a){var b=a.reduce(function(a,b){return a+b});return b/a.length}}),a.util.extend(a.Drawer.SplitWavePointPlot,a.Observer),a.PeakCache={init:function(){this.clearPeakCache()},clearPeakCache:function(){this.peakCacheRanges=[],this.peakCacheLength=-1},addRangeToPeakCache:function(a,b,c){a!=this.peakCacheLength&&(this.clearPeakCache(),this.peakCacheLength=a);for(var d=[],e=0;e<this.peakCacheRanges.length&&this.peakCacheRanges[e]<b;)e++;for(e%2==0&&d.push(b);e<this.peakCacheRanges.length&&this.peakCacheRanges[e]<=c;)d.push(this.peakCacheRanges[e]),e++;e%2==0&&d.push(c),d=d.filter(function(a,b,c){return 0==b?a!=c[b+1]:b==c.length-1?a!=c[b-1]:a!=c[b-1]&&a!=c[b+1]}),this.peakCacheRanges=this.peakCacheRanges.concat(d),this.peakCacheRanges=this.peakCacheRanges.sort(function(a,b){return a-b}).filter(function(a,b,c){return 0==b?a!=c[b+1]:b==c.length-1?a!=c[b-1]:a!=c[b-1]&&a!=c[b+1]});var f=[];for(e=0;e<d.length;e+=2)f.push([d[e],d[e+1]]);return f},getCacheRanges:function(){for(var a=[],b=0;b<this.peakCacheRanges.length;b+=2)a.push([this.peakCacheRanges[b],this.peakCacheRanges[b+1]]);return a}},function(){var b=function(){var b=document.querySelectorAll(\"wavesurfer\");Array.prototype.forEach.call(b,function(b){var c=a.util.extend({container:b,backend:\"MediaElement\",mediaControls:!0},b.dataset);b.style.display=\"block\";var d=a.create(c);if(b.dataset.peaks)var e=JSON.parse(b.dataset.peaks);d.load(b.dataset.url,e)})};\"complete\"===document.readyState?b():window.addEventListener(\"load\",b)}(),a});\n//# sourceMappingURL=wavesurfer.min.js.map"
  },
  {
    "path": "public/assets/locales/_.json",
    "content": "{\n    \"ABORTED\": \"\",\n    \"ABORT_CURRENT_UPLOADS?\": \"\",\n    \"ACTIVITY\": \"\",\n    \"ADVANCED\": \"\",\n    \"ALL_DONE\": \"\",\n    \"ALREADY_EXIST\": \"\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"\",\n    \"BEAUTIFUL_URL\": \"\",\n    \"BOOKMARK\": \"\",\n    \"CAMERA\": \"\",\n    \"CANCEL\": \"\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"\",\n    \"CANT_LOAD_THIS_PICTURE\": \"\",\n    \"CANT_USE_FILESYSTEM\": \"\",\n    \"CAN_RESHARE\": \"\",\n    \"CODE\": \"\",\n    \"CONFIGURE\": \"\",\n    \"CONFIRM_BY_TYPING\": \"\",\n    \"CONNECT\": \"\",\n    \"CONNECTION_LOST\": \"\",\n    \"COPIED_TO_CLIPBOARD\": \"\",\n    \"CREATE_A_NEW_LINK\": \"\",\n    \"CREATE_A_TAG\": \"\",\n    \"CURRENT\": \"\",\n    \"CURRENT_UPLOAD\": \"\",\n    \"CUSTOM_LINK_URL\": \"\",\n    \"DASHBOARD\": \"\",\n    \"DATE\": \"\",\n    \"DISPLAY_HIDDEN_FILES\": \"\",\n    \"DOESNT_MATCH\": \"\",\n    \"DONE\": \"\",\n    \"DOWNLOAD\": \"\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"\",\n    \"DROP_HERE_TO_UPLOAD\": \"\",\n    \"EDITOR\": \"\",\n    \"EMBED\": \"\",\n    \"EMPTY\": \"\",\n    \"ENCRYPTION_KEY\": \"\",\n    \"ENDPOINT\": \"\",\n    \"ERROR\": \"\",\n    \"EXISTING_LINKS\": \"\",\n    \"EXPIRATION\": \"\",\n    \"EXPORT_AS_{{VALUE}}\": \"\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"\",\n    \"HIDE_HIDDEN_FILES\": \"\",\n    \"HOME\": \"\",\n    \"HOSTNAME*\": \"\",\n    \"HOST_KEY\": \"\",\n    \"INCORRECT_PASSWORD\": \"\",\n    \"INFO\": \"\",\n    \"INTERNAL_ERROR\": \"\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"\",\n    \"INVALID_ACCOUNT\": \"\",\n    \"INVALID_PASSWORD\": \"\",\n    \"LAYOUT\": \"\",\n    \"LOADING\": \"\",\n    \"LOCATION\": \"\",\n    \"MISSING_DEPENDENCY\": \"\",\n    \"MORE_DETAILS\": \"\",\n    \"NAVIGATE\": \"\",\n    \"NEW_FILE\": \"\",\n    \"NEW_FILE::SHORT\": \"\",\n    \"NEW_FOLDER\": \"\",\n    \"NEW_FOLDER::SHORT\": \"\",\n    \"NO\": \"\",\n    \"NOT_ALLOWED\": \"\",\n    \"NOT_AUTHORISED\": \"\",\n    \"NOT_FOUND\": \"\",\n    \"NOT_IMPLEMENTED\": \"\",\n    \"NOT_SUPPORTED\": \"\",\n    \"NOT_VALID\": \"\",\n    \"NUMBER_OF_CONNECTIONS\": \"\",\n    \"OK\": \"\",\n    \"ONLY_FOR_USERS\": \"\",\n    \"OOPS\": \"\",\n    \"PASSPHRASE\": \"\",\n    \"PASSWORD\": \"\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"\",\n    \"PATH\": \"\",\n    \"PERMISSION_DENIED\": \"\",\n    \"PICK_A_MASTER_PASSWORD\": \"\",\n    \"PORT\": \"\",\n    \"POWERED_BY\": \"\",\n    \"PROPERTIES\": \"\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"\",\n    \"QUICK_ACCESS\": \"\",\n    \"REGION\": \"\",\n    \"REMEMBER_ME\": \"\",\n    \"REMOVE\": \"\",\n    \"RENAME\": \"\",\n    \"RENAME_AS\": \"\",\n    \"RESTRICTIONS\": \"\",\n    \"RUNNING\": \"\",\n    \"SAVE_CURRENT_FILE\": \"\",\n    \"SEARCH\": \"\",\n    \"SETTINGS\": \"\",\n    \"SHARE\": \"\",\n    \"SHARED_DRIVE\": \"\",\n    \"SKIP_TO_CONTENT\": \"\",\n    \"SORT\": \"\",\n    \"SORT_BY_DATE\": \"\",\n    \"SORT_BY_NAME\": \"\",\n    \"SORT_BY_SIZE\": \"\",\n    \"SORT_BY_TYPE\": \"\",\n    \"STARRED\": \"\",\n    \"SUPPORT\": \"\",\n    \"TAG\": \"\",\n    \"TAGS\": \"\",\n    \"THERE_IS_NOTHING_HERE\": \"\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"\",\n    \"TIMEOUT\": \"\",\n    \"TODO\": \"\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"\",\n    \"UPLOAD\": \"\",\n    \"UPLOADER\": \"\",\n    \"USERNAME\": \"\",\n    \"VERSION\": \"\",\n    \"VIEWER\": \"\",\n    \"WAITING\": \"\",\n    \"YES\": \"\",\n    \"YOUR_EMAIL_ADDRESS\": \"\",\n    \"YOUR_FILES\": \"\",\n    \"YOUR_MASTER_PASSWORD\": \"\",\n    \"YOU_CANT_DO_THAT\": \"\"\n}\n"
  },
  {
    "path": "public/assets/locales/az.json",
    "content": "{\n    \"ABORTED\": \"ləğv edildi\",\n    \"ABORT_CURRENT_UPLOADS?\": \"cari yükləmə ləğv edilsin?\",\n    \"ACTIVITY\": \"Fəaliyyət\",\n    \"ADVANCED\": \"qabaqcıl\",\n    \"ALL_DONE\": \"hər şey hazırdır\",\n    \"ALREADY_EXIST\": \"artıq mövcuddur\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"{{VALUE}} adlı bir fayl yaradıldı\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"{{VALUE}} adlı bir qovluq yaradıldı\",\n    \"BEAUTIFUL_URL\": \"gözəl_url\",\n    \"BOOKMARK\": \"əlfəcin\",\n    \"CAMERA\": \"kamera\",\n    \"CANCEL\": \"ləğv et\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"əlaqə qura bilmir\",\n    \"CANT_LOAD_THIS_PICTURE\": \"bu şəkli yükləyə bilməz\",\n    \"CANT_USE_FILESYSTEM\": \"fayl sistemindən istifadə edə bilər\",\n    \"CAN_RESHARE\": \"yenidən yaşaya bilər\",\n    \"CODE\": \"kodu\",\n    \"CONFIGURE\": \"konfiqurasiya etmək\",\n    \"CONFIRM_BY_TYPING\": \"yazaraq təsdiqləyin\",\n    \"CONNECT\": \"qoşulmaq\",\n    \"CONNECTION_LOST\": \"bağlantı kəsildi\",\n    \"COPIED_TO_CLIPBOARD\": \"panoya kopyalandı\",\n    \"CREATE_A_NEW_LINK\": \"yeni bir əlaqə yaradın\",\n    \"CREATE_A_TAG\": \"etiket yarat\",\n    \"CURRENT\": \"cari\",\n    \"CURRENT_UPLOAD\": \"cari yükləmə\",\n    \"CUSTOM_LINK_URL\": \"xüsusi link URL\",\n    \"DASHBOARD\": \"idarə paneli\",\n    \"DATE\": \"Tarix\",\n    \"DISPLAY_HIDDEN_FILES\": \"gizli sənədləri göstərin\",\n    \"DOESNT_MATCH\": \"uyğun gəlmir\",\n    \"DONE\": \"hazır\",\n    \"DOWNLOAD\": \"yükləyin\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"dəyişiklikləri saxlamaq istəyirsən\",\n    \"DROP_HERE_TO_UPLOAD\": \"yükləmək üçün buraya salın\",\n    \"EDITOR\": \"Redaktor\",\n    \"EMBED\": \"yerləşdir\",\n    \"EMPTY\": \"boş\",\n    \"ENCRYPTION_KEY\": \"şifrələmə açarı\",\n    \"ENDPOINT\": \"son nöqtə\",\n    \"ERROR\": \"səhv\",\n    \"EXISTING_LINKS\": \"mövcud bağlantılar\",\n    \"EXPIRATION\": \"sona çatması\",\n    \"EXPORT_AS_{{VALUE}}\": \"{{VALUE}} olaraq ixrac\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"tez-tez giriş qovluqları burada göstəriləcəkdir\",\n    \"HIDE_HIDDEN_FILES\": \"gizli sənədləri gizlət\",\n    \"HOME\": \"ana səhifə\",\n    \"HOSTNAME*\": \"host adı\",\n    \"HOST_KEY\": \"host açarı\",\n    \"INCORRECT_PASSWORD\": \"səhv parol\",\n    \"INFO\": \"məlumat\",\n    \"INTERNAL_ERROR\": \"daxili səhv\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"daxili səhv: {{VALUE}} yarada bilməz\",\n    \"INVALID_ACCOUNT\": \"etibarsız hesab\",\n    \"INVALID_PASSWORD\": \"etibarsız Şifrə\",\n    \"LAYOUT\": \"düzülüş\",\n    \"LOADING\": \"yüklənir\",\n    \"LOCATION\": \"yeri\",\n    \"MISSING_DEPENDENCY\": \"itkin asılılıq\",\n    \"MORE_DETAILS\": \"daha çox məlumat\",\n    \"NAVIGATE\": \"gedin\",\n    \"NEW_FILE\": \"yeni sənəd\",\n    \"NEW_FILE::SHORT\": \"yeni sənəd\",\n    \"NEW_FOLDER\": \"yeni qovluq\",\n    \"NEW_FOLDER::SHORT\": \"yeni qovluq\",\n    \"NO\": \"yox\",\n    \"NOT_ALLOWED\": \"icazəli deyildir, izinli deyildir, qadağandır\",\n    \"NOT_AUTHORISED\": \"səlahiyyətli deyil\",\n    \"NOT_FOUND\": \"tapılmadı\",\n    \"NOT_IMPLEMENTED\": \"tətbiq olunmayıb\",\n    \"NOT_SUPPORTED\": \"dəstəklənmir\",\n    \"NOT_VALID\": \"etibarlı deyil\",\n    \"NUMBER_OF_CONNECTIONS\": \"əlaqələrin sayı\",\n    \"OK\": \"tamam\",\n    \"ONLY_FOR_USERS\": \"Yalnız istifadəçilər üçün\",\n    \"OOPS\": \"Vay\",\n    \"PASSPHRASE\": \"parol\",\n    \"PASSWORD\": \"parol\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"parol boş ola bilməz\",\n    \"PATH\": \"yol\",\n    \"PERMISSION_DENIED\": \"icazə rədd edildi\",\n    \"PICK_A_MASTER_PASSWORD\": \"usta parol seçin\",\n    \"PORT\": \"liman\",\n    \"POWERED_BY\": \"ilə təchiz edilmişdir\",\n    \"PROPERTIES\": \"xassələri\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"girişi bir parol ilə qorumaq\",\n    \"QUICK_ACCESS\": \"sürətli giriş\",\n    \"REGION\": \"bölgə\",\n    \"REMEMBER_ME\": \"məni xatırla\",\n    \"REMOVE\": \"çıxarın\",\n    \"RENAME\": \"adını dəyişdir\",\n    \"RENAME_AS\": \"kimi adlandır\",\n    \"RESTRICTIONS\": \"məhdudiyyətlər\",\n    \"RUNNING\": \"işləyir\",\n    \"SAVE_CURRENT_FILE\": \"cari faylı qeyd edin\",\n    \"SEARCH\": \"axtarış\",\n    \"SETTINGS\": \"parametrləri\",\n    \"SHARE\": \"paylaş\",\n    \"SHARED_DRIVE\": \"paylaşılmış disk\",\n    \"SKIP_TO_CONTENT\": \"Məzmuna keç\",\n    \"SORT\": \"sırala\",\n    \"SORT_BY_DATE\": \"tarixə görə sırala\",\n    \"SORT_BY_NAME\": \"adı ilə sırala\",\n    \"SORT_BY_SIZE\": \"Ölçüyə görə sırala\",\n    \"SORT_BY_TYPE\": \"növünə görə sırala\",\n    \"STARRED\": \"əlfəcin\",\n    \"SUPPORT\": \"dəstək\",\n    \"TAG\": \"etiket\",\n    \"TAGS\": \"etiketlər\",\n    \"THERE_IS_NOTHING_HERE\": \"burada heç nə yoxdur\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"fayl {{VALUE}} silindi\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"{{VALUE}} adı dəyişdirildi\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"link panoya kopyalandı\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"link sonra etibarlı olmayacaq\",\n    \"TIMEOUT\": \"vaxt\",\n    \"TODO\": \"etmək\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"Trafik sıxlığı, daha sonra yenidən cəhd edin\",\n    \"UPLOAD\": \"yüklə\",\n    \"UPLOADER\": \"yükləyici\",\n    \"USERNAME\": \"istifadəçi adı\",\n    \"VERSION\": \"versiya\",\n    \"VIEWER\": \"tamaşaçı\",\n    \"WAITING\": \"gözləyir\",\n    \"YES\": \"bəli\",\n    \"YOUR_EMAIL_ADDRESS\": \"e-poçt adresiniz\",\n    \"YOUR_FILES\": \"sənədləriniz\",\n    \"YOUR_MASTER_PASSWORD\": \"usta parolunuz\",\n    \"YOU_CANT_DO_THAT\": \"bunu edə bilməzsən\"\n}\n"
  },
  {
    "path": "public/assets/locales/be.json",
    "content": "{\n    \"ABORTED\": \"перапынілі\",\n    \"ABORT_CURRENT_UPLOADS?\": \"перапыніць бягучую загрузку?\",\n    \"ACTIVITY\": \"Дзейнасць\",\n    \"ADVANCED\": \"прасунуты\",\n    \"ALL_DONE\": \"усё зроблена\",\n    \"ALREADY_EXIST\": \"ужо існуюць\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Быў створаны файл з імем {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Была створана папка пад назвай {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"закладка\",\n    \"CAMERA\": \"фотаапарат\",\n    \"CANCEL\": \"адмяніць\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"не ўдаецца ўсталяваць злучэнне\",\n    \"CANT_LOAD_THIS_PICTURE\": \"не ўдаецца загрузіць гэтую выяву\",\n    \"CANT_USE_FILESYSTEM\": \"можна выкарыстоўваць файлавую сістэму\",\n    \"CAN_RESHARE\": \"можа адкрыць агульны доступ\",\n    \"CODE\": \"код\",\n    \"CONFIGURE\": \"наладзіць\",\n    \"CONFIRM_BY_TYPING\": \"пацвердзіце, увёўшы\",\n    \"CONNECT\": \"злучыць\",\n    \"CONNECTION_LOST\": \"злучэнне страчана\",\n    \"COPIED_TO_CLIPBOARD\": \"скапіраваны ў буфер абмену\",\n    \"CREATE_A_NEW_LINK\": \"стварыць новую спасылку\",\n    \"CREATE_A_TAG\": \"стварыць тэг\",\n    \"CURRENT\": \"бягучы\",\n    \"CURRENT_UPLOAD\": \"бягучая загрузка\",\n    \"CUSTOM_LINK_URL\": \"карыстацкі URL спасылкі\",\n    \"DASHBOARD\": \"панэль\",\n    \"DATE\": \"даты\",\n    \"DISPLAY_HIDDEN_FILES\": \"адлюстроўваць схаваныя файлы\",\n    \"DOESNT_MATCH\": \"не супадае\",\n    \"DONE\": \"зроблена\",\n    \"DOWNLOAD\": \"спампаваць\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"Вы хочаце захаваць змены\",\n    \"DROP_HERE_TO_UPLOAD\": \"падзенне сюды, каб загрузіць\",\n    \"EDITOR\": \"рэдактар\",\n    \"EMBED\": \"убудаваць\",\n    \"EMPTY\": \"пусты\",\n    \"ENCRYPTION_KEY\": \"ключ шыфравання\",\n    \"ENDPOINT\": \"канчатковая кропка\",\n    \"ERROR\": \"памылка\",\n    \"EXISTING_LINKS\": \"існуючыя спасылкі\",\n    \"EXPIRATION\": \"тэрмін прыдатнасці\",\n    \"EXPORT_AS_{{VALUE}}\": \"экспартаваць як {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"Тут будуць паказаны папкі частага доступу\",\n    \"HIDE_HIDDEN_FILES\": \"схаваць скрытыя файлы\",\n    \"HOME\": \"галоўная старонка\",\n    \"HOSTNAME*\": \"імя хаста\",\n    \"HOST_KEY\": \"ключ хаста\",\n    \"INCORRECT_PASSWORD\": \"няправільны пароль\",\n    \"INFO\": \"інфармацыя\",\n    \"INTERNAL_ERROR\": \"Унутраная памылка\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"унутраная памылка: немагчыма стварыць {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"несапраўдны ўліковы запіс\",\n    \"INVALID_PASSWORD\": \"няправільны пароль\",\n    \"LAYOUT\": \"макет\",\n    \"LOADING\": \"загрузка\",\n    \"LOCATION\": \"месцазнаходжанне\",\n    \"MISSING_DEPENDENCY\": \"адсутнічае залежнасць\",\n    \"MORE_DETAILS\": \"падрабязней\",\n    \"NAVIGATE\": \"арыентавацца\",\n    \"NEW_FILE\": \"новы файл\",\n    \"NEW_FILE::SHORT\": \"новы файл\",\n    \"NEW_FOLDER\": \"новы каталог\",\n    \"NEW_FOLDER::SHORT\": \"новы каталог\",\n    \"NO\": \"не\",\n    \"NOT_ALLOWED\": \"не дазволена\",\n    \"NOT_AUTHORISED\": \"не ўпаўнаважаны\",\n    \"NOT_FOUND\": \"не знойдзены\",\n    \"NOT_IMPLEMENTED\": \"не рэалізаваны\",\n    \"NOT_SUPPORTED\": \"не падтрымліваецца\",\n    \"NOT_VALID\": \"не дзейнічае\",\n    \"NUMBER_OF_CONNECTIONS\": \"колькасць злучэнняў\",\n    \"OK\": \"добра\",\n    \"ONLY_FOR_USERS\": \"Толькі для карыстальнікаў\",\n    \"OOPS\": \"На жаль\",\n    \"PASSPHRASE\": \"фразу\",\n    \"PASSWORD\": \"пароль\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"пусты пароль не можа быць\",\n    \"PATH\": \"шлях\",\n    \"PERMISSION_DENIED\": \"у доступе адмоўлена\",\n    \"PICK_A_MASTER_PASSWORD\": \"выбраць галоўны пароль\",\n    \"PORT\": \"порт\",\n    \"POWERED_BY\": \"харчаванне ад\",\n    \"PROPERTIES\": \"ўласцівасці\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"абараніць доступ паролем\",\n    \"QUICK_ACCESS\": \"хуткі доступ\",\n    \"REGION\": \"вобласць\",\n    \"REMEMBER_ME\": \"Запомні мяне\",\n    \"REMOVE\": \"выдаліць\",\n    \"RENAME\": \"пераназваць\",\n    \"RENAME_AS\": \"пераназваць як\",\n    \"RESTRICTIONS\": \"абмежаванні\",\n    \"RUNNING\": \"працуе\",\n    \"SAVE_CURRENT_FILE\": \"захаваць бягучы файл\",\n    \"SEARCH\": \"пошук\",\n    \"SETTINGS\": \"налады\",\n    \"SHARE\": \"падзяліцца\",\n    \"SHARED_DRIVE\": \"агульны дыск\",\n    \"SKIP_TO_CONTENT\": \"Перайсці да змесціва\",\n    \"SORT\": \"сартаваць\",\n    \"SORT_BY_DATE\": \"Сартаваць па даце\",\n    \"SORT_BY_NAME\": \"сартаваць па імені\",\n    \"SORT_BY_SIZE\": \"Сартаваць па памеры\",\n    \"SORT_BY_TYPE\": \"сартаваць па тыпу\",\n    \"STARRED\": \"закладка\",\n    \"SUPPORT\": \"падтрымка\",\n    \"TAG\": \"тэг\",\n    \"TAGS\": \"тэгі\",\n    \"THERE_IS_NOTHING_HERE\": \"тут нічога няма\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"файл {{VALUE}} быў выдалены\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"файл {{VALUE}} быў перайменаваны\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"спасылка была скапіявана ў буфер абмену\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"пасля гэтага спасылка не будзе сапраўднай\",\n    \"TIMEOUT\": \"тайм-аўт\",\n    \"TODO\": \"рабіць\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"заторы, паспрабуйце зноў пазней\",\n    \"UPLOAD\": \"загрузіць\",\n    \"UPLOADER\": \"загрузчык\",\n    \"USERNAME\": \"імя карыстальніка\",\n    \"VERSION\": \"версія\",\n    \"VIEWER\": \"глядач\",\n    \"WAITING\": \"чакаюць\",\n    \"YES\": \"так\",\n    \"YOUR_EMAIL_ADDRESS\": \"Ваш электронны адрас\",\n    \"YOUR_FILES\": \"вашы файлы\",\n    \"YOUR_MASTER_PASSWORD\": \"ваш галоўны пароль\",\n    \"YOU_CANT_DO_THAT\": \"вы не можаце зрабіць гэтага\"\n}\n"
  },
  {
    "path": "public/assets/locales/bg.json",
    "content": "{\n    \"ABORTED\": \"прекратено\",\n    \"ABORT_CURRENT_UPLOADS?\": \"прекратяване на текущото качване?\",\n    \"ACTIVITY\": \"Дейност\",\n    \"ADVANCED\": \"напреднал\",\n    \"ALL_DONE\": \"готово\",\n    \"ALREADY_EXIST\": \"вече съществува\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Беше създаден файл с име {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Създадена е папка с име {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"отметка\",\n    \"CAMERA\": \"камера\",\n    \"CANCEL\": \"отказ\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"не може да установи връзка\",\n    \"CANT_LOAD_THIS_PICTURE\": \"не може да зареди тази снимка\",\n    \"CANT_USE_FILESYSTEM\": \"може да използва файлова система\",\n    \"CAN_RESHARE\": \"може да сподели повторно\",\n    \"CODE\": \"код\",\n    \"CONFIGURE\": \"конфигуриране\",\n    \"CONFIRM_BY_TYPING\": \"потвърдете, като напишете\",\n    \"CONNECT\": \"Connect\",\n    \"CONNECTION_LOST\": \"връзката е загубена\",\n    \"COPIED_TO_CLIPBOARD\": \"копиран в клипборда\",\n    \"CREATE_A_NEW_LINK\": \"създайте нова връзка\",\n    \"CREATE_A_TAG\": \"създайте етикет\",\n    \"CURRENT\": \"текущ\",\n    \"CURRENT_UPLOAD\": \"текущо качване\",\n    \"CUSTOM_LINK_URL\": \"персонализиран URL адрес на връзка\",\n    \"DASHBOARD\": \"табло\",\n    \"DATE\": \"дата\",\n    \"DISPLAY_HIDDEN_FILES\": \"показване на скрити файлове\",\n    \"DOESNT_MATCH\": \"не съвпада\",\n    \"DONE\": \"Готово\",\n    \"DOWNLOAD\": \"Изтегли\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"искате ли да запазите промените\",\n    \"DROP_HERE_TO_UPLOAD\": \"пуснете тук, за да качите\",\n    \"EDITOR\": \"редактор\",\n    \"EMBED\": \"вграждане\",\n    \"EMPTY\": \"изпразни\",\n    \"ENCRYPTION_KEY\": \"ключ за криптиране\",\n    \"ENDPOINT\": \"крайна точка\",\n    \"ERROR\": \"грешка\",\n    \"EXISTING_LINKS\": \"съществуващи връзки\",\n    \"EXPIRATION\": \"изтичане\",\n    \"EXPORT_AS_{{VALUE}}\": \"експортиране като {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"тук ще бъдат показани често достъпни папки\",\n    \"HIDE_HIDDEN_FILES\": \"скрий скрити файлове\",\n    \"HOME\": \"начало\",\n    \"HOSTNAME*\": \"име на хост\",\n    \"HOST_KEY\": \"хост ключ\",\n    \"INCORRECT_PASSWORD\": \"грешна парола\",\n    \"INFO\": \"инфо\",\n    \"INTERNAL_ERROR\": \"Вътрешна грешка\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"вътрешна грешка: не може да създаде {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"невалиден акаунт\",\n    \"INVALID_PASSWORD\": \"невалидна парола\",\n    \"LAYOUT\": \"оформление\",\n    \"LOADING\": \"зареждане\",\n    \"LOCATION\": \"местоположение\",\n    \"MISSING_DEPENDENCY\": \"липсваща зависимост\",\n    \"MORE_DETAILS\": \"повече подробности\",\n    \"NAVIGATE\": \"навигация\",\n    \"NEW_FILE\": \"нов файл\",\n    \"NEW_FILE::SHORT\": \"нов файл\",\n    \"NEW_FOLDER\": \"нова директория\",\n    \"NEW_FOLDER::SHORT\": \"нова директория\",\n    \"NO\": \"не\",\n    \"NOT_ALLOWED\": \"не е позволено\",\n    \"NOT_AUTHORISED\": \"не е оторизиран\",\n    \"NOT_FOUND\": \"не е намерен\",\n    \"NOT_IMPLEMENTED\": \"не се прилага\",\n    \"NOT_SUPPORTED\": \"Не се поддържа\",\n    \"NOT_VALID\": \"не важи\",\n    \"NUMBER_OF_CONNECTIONS\": \"брой връзки\",\n    \"OK\": \"Добре\",\n    \"ONLY_FOR_USERS\": \"Само за потребители\",\n    \"OOPS\": \"Oops\",\n    \"PASSPHRASE\": \"фраза за достъп\",\n    \"PASSWORD\": \"парола\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"паролата не може да бъде празна\",\n    \"PATH\": \"път\",\n    \"PERMISSION_DENIED\": \"отказано разрешение\",\n    \"PICK_A_MASTER_PASSWORD\": \"изберете главна парола\",\n    \"PORT\": \"порт\",\n    \"POWERED_BY\": \"задвижвани от\",\n    \"PROPERTIES\": \"свойства\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"защити достъпа с парола\",\n    \"QUICK_ACCESS\": \"бърз достъп\",\n    \"REGION\": \"област\",\n    \"REMEMBER_ME\": \"помни ме\",\n    \"REMOVE\": \"Премахване\",\n    \"RENAME\": \"преименуване\",\n    \"RENAME_AS\": \"преименуване като\",\n    \"RESTRICTIONS\": \"ограничения\",\n    \"RUNNING\": \"работи\",\n    \"SAVE_CURRENT_FILE\": \"запишете текущия файл\",\n    \"SEARCH\": \"Търсене\",\n    \"SETTINGS\": \"настройки\",\n    \"SHARE\": \"споделяне\",\n    \"SHARED_DRIVE\": \"споделен диск\",\n    \"SKIP_TO_CONTENT\": \"Към съдържанието\",\n    \"SORT\": \"сортиране\",\n    \"SORT_BY_DATE\": \"сортиране по дата\",\n    \"SORT_BY_NAME\": \"подредете по име\",\n    \"SORT_BY_SIZE\": \"сортиране по размер\",\n    \"SORT_BY_TYPE\": \"сортиране по тип\",\n    \"STARRED\": \"отметка\",\n    \"SUPPORT\": \"поддържа\",\n    \"TAG\": \"етикет\",\n    \"TAGS\": \"етикети\",\n    \"THERE_IS_NOTHING_HERE\": \"тук няма нищо\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"файлът {{VALUE}} беше изтрит\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"файлът {{VALUE}} беше преименуван\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"връзката бе копирана в клипборда\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"след това връзката няма да бъде валидна\",\n    \"TIMEOUT\": \"изчакване\",\n    \"TODO\": \"да направя\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"трафик задръствания, опитайте отново по-късно\",\n    \"UPLOAD\": \"качване\",\n    \"UPLOADER\": \"качващ\",\n    \"USERNAME\": \"потребителско име\",\n    \"VERSION\": \"версия\",\n    \"VIEWER\": \"зрител\",\n    \"WAITING\": \"очакване\",\n    \"YES\": \"да\",\n    \"YOUR_EMAIL_ADDRESS\": \"Вашата електронна поща\",\n    \"YOUR_FILES\": \"вашите файлове\",\n    \"YOUR_MASTER_PASSWORD\": \"вашата главна парола\",\n    \"YOU_CANT_DO_THAT\": \"не можеш да го направиш\"\n}\n"
  },
  {
    "path": "public/assets/locales/ca.json",
    "content": "{\n    \"ABORTED\": \"avortat\",\n    \"ABORT_CURRENT_UPLOADS?\": \"Avortar la càrrega actual?\",\n    \"ACTIVITY\": \"Activitat\",\n    \"ADVANCED\": \"avançat\",\n    \"ALL_DONE\": \"tot fet\",\n    \"ALREADY_EXIST\": \"ja existeix\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Es va crear un fitxer anomenat {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Es va crear una carpeta anomenada {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"bella_url\",\n    \"BOOKMARK\": \"marcador\",\n    \"CAMERA\": \"càmera\",\n    \"CANCEL\": \"cancel·lar\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"no pot establir una connexió\",\n    \"CANT_LOAD_THIS_PICTURE\": \"No es pot carregar aquesta imatge\",\n    \"CANT_USE_FILESYSTEM\": \"pot utilitzar el sistema de fitxers\",\n    \"CAN_RESHARE\": \"pot tornar a compartir\",\n    \"CODE\": \"codi\",\n    \"CONFIGURE\": \"configurar\",\n    \"CONFIRM_BY_TYPING\": \"confirmeu escrivint\",\n    \"CONNECT\": \"connectar\",\n    \"CONNECTION_LOST\": \"connexió perduda\",\n    \"COPIED_TO_CLIPBOARD\": \"copiat al porta-retalls\",\n    \"CREATE_A_NEW_LINK\": \"crear un enllaç nou\",\n    \"CREATE_A_TAG\": \"crear una etiqueta\",\n    \"CURRENT\": \"actual\",\n    \"CURRENT_UPLOAD\": \"càrrega actual\",\n    \"CUSTOM_LINK_URL\": \"URL d'enllaç personalitzat\",\n    \"DASHBOARD\": \"panell\",\n    \"DATE\": \"data\",\n    \"DISPLAY_HIDDEN_FILES\": \"mostrar fitxers ocults\",\n    \"DOESNT_MATCH\": \"no coincideix\",\n    \"DONE\": \"fet\",\n    \"DOWNLOAD\": \"descarregar\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"voleu desar els canvis\",\n    \"DROP_HERE_TO_UPLOAD\": \"deixeu-ho aquí per penjar-lo\",\n    \"EDITOR\": \"editor\",\n    \"EMBED\": \"incrustar\",\n    \"EMPTY\": \"buit\",\n    \"ENCRYPTION_KEY\": \"clau de xifrat\",\n    \"ENDPOINT\": \"punt final\",\n    \"ERROR\": \"error\",\n    \"EXISTING_LINKS\": \"enllaços existents\",\n    \"EXPIRATION\": \"caducitat\",\n    \"EXPORT_AS_{{VALUE}}\": \"exporta com a {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"les carpetes d'accés freqüent es mostren aquí\",\n    \"HIDE_HIDDEN_FILES\": \"ocultar fitxers ocults\",\n    \"HOME\": \"inici\",\n    \"HOSTNAME*\": \"nom d'amfitrió\",\n    \"HOST_KEY\": \"clau d'amfitrió\",\n    \"INCORRECT_PASSWORD\": \"contrasenya incorrecta\",\n    \"INFO\": \"info\",\n    \"INTERNAL_ERROR\": \"Error intern\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"error intern: no es pot crear un {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"compte no vàlid\",\n    \"INVALID_PASSWORD\": \"contrasenya invàlida\",\n    \"LAYOUT\": \"disseny\",\n    \"LOADING\": \"carregant\",\n    \"LOCATION\": \"ubicació\",\n    \"MISSING_DEPENDENCY\": \"falta dependència\",\n    \"MORE_DETAILS\": \"més detalls\",\n    \"NAVIGATE\": \"navegar\",\n    \"NEW_FILE\": \"nou fitxer\",\n    \"NEW_FILE::SHORT\": \"nou fitxer\",\n    \"NEW_FOLDER\": \"nou directori\",\n    \"NEW_FOLDER::SHORT\": \"nou directori\",\n    \"NO\": \"no\",\n    \"NOT_ALLOWED\": \"no permès\",\n    \"NOT_AUTHORISED\": \"no autoritzat\",\n    \"NOT_FOUND\": \"no trobat\",\n    \"NOT_IMPLEMENTED\": \"No implementat\",\n    \"NOT_SUPPORTED\": \"no compatible\",\n    \"NOT_VALID\": \"no és vàlid\",\n    \"NUMBER_OF_CONNECTIONS\": \"nombre de connexions\",\n    \"OK\": \"D'acord\",\n    \"ONLY_FOR_USERS\": \"Només per als usuaris\",\n    \"OOPS\": \"Vaja!\",\n    \"PASSPHRASE\": \"frase de contrasenya\",\n    \"PASSWORD\": \"contrasenya\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"la contrasenya no pot estar buida\",\n    \"PATH\": \"Camí\",\n    \"PERMISSION_DENIED\": \"permís denegat\",\n    \"PICK_A_MASTER_PASSWORD\": \"trieu una contrasenya principal\",\n    \"PORT\": \"port\",\n    \"POWERED_BY\": \"impulsat per\",\n    \"PROPERTIES\": \"propietats\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"protegir l’accés amb una contrasenya\",\n    \"QUICK_ACCESS\": \"accés ràpid\",\n    \"REGION\": \"regió\",\n    \"REMEMBER_ME\": \"Recorda'm\",\n    \"REMOVE\": \"eliminar\",\n    \"RENAME\": \"canviar el nom\",\n    \"RENAME_AS\": \"canviar el nom com a\",\n    \"RESTRICTIONS\": \"restriccions\",\n    \"RUNNING\": \"execució\",\n    \"SAVE_CURRENT_FILE\": \"desar el fitxer actual\",\n    \"SEARCH\": \"cerca\",\n    \"SETTINGS\": \"configuració\",\n    \"SHARE\": \"compartir\",\n    \"SHARED_DRIVE\": \"unitat compartida\",\n    \"SKIP_TO_CONTENT\": \"Anar al contingut\",\n    \"SORT\": \"ordenar\",\n    \"SORT_BY_DATE\": \"ordenar per data\",\n    \"SORT_BY_NAME\": \"ordena per nom\",\n    \"SORT_BY_SIZE\": \"ordenar per mida\",\n    \"SORT_BY_TYPE\": \"ordenar per tipus\",\n    \"STARRED\": \"favorit\",\n    \"SUPPORT\": \"suport\",\n    \"TAG\": \"etiqueta\",\n    \"TAGS\": \"etiquetes\",\n    \"THERE_IS_NOTHING_HERE\": \"aquí no hi ha res\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"s'ha suprimit el fitxer {{VALUE}}\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"es va canviar el nom del fitxer {{VALUE}}\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"l’enllaç es va copiar al porta-retalls\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"l’enllaç no serà vàlid després\",\n    \"TIMEOUT\": \"temps d’esperació\",\n    \"TODO\": \"fer\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"congestió de trànsit, torna-ho a provar més tard\",\n    \"UPLOAD\": \"pujar\",\n    \"UPLOADER\": \"carregador\",\n    \"USERNAME\": \"nom d'usuari\",\n    \"VERSION\": \"versió\",\n    \"VIEWER\": \"espectador\",\n    \"WAITING\": \"esperant\",\n    \"YES\": \"sí\",\n    \"YOUR_EMAIL_ADDRESS\": \"la teva adreça de correu electrònic\",\n    \"YOUR_FILES\": \"els teus fitxers\",\n    \"YOUR_MASTER_PASSWORD\": \"la vostra contrasenya principal\",\n    \"YOU_CANT_DO_THAT\": \"No pots fer això\"\n}\n"
  },
  {
    "path": "public/assets/locales/cs.json",
    "content": "{\n    \"ABORTED\": \"přerušeno\",\n    \"ABORT_CURRENT_UPLOADS?\": \"přerušit aktuální nahrávání?\",\n    \"ACTIVITY\": \"Aktivita\",\n    \"ADVANCED\": \"pokročilý\",\n    \"ALL_DONE\": \"vše hotovo\",\n    \"ALREADY_EXIST\": \"už existuje\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Byl vytvořen soubor s názvem {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Byla vytvořena složka s názvem {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"záložka\",\n    \"CAMERA\": \"Fotoaparát\",\n    \"CANCEL\": \"zrušení\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"nelze navázat spojení\",\n    \"CANT_LOAD_THIS_PICTURE\": \"tento obrázek nelze načíst\",\n    \"CANT_USE_FILESYSTEM\": \"může používat souborový systém\",\n    \"CAN_RESHARE\": \"může znovu sdílet\",\n    \"CODE\": \"kód\",\n    \"CONFIGURE\": \"konfigurovat\",\n    \"CONFIRM_BY_TYPING\": \"potvrďte zadáním\",\n    \"CONNECT\": \"připojit\",\n    \"CONNECTION_LOST\": \"připojení ztraceno\",\n    \"COPIED_TO_CLIPBOARD\": \"zkopírováno do schránky\",\n    \"CREATE_A_NEW_LINK\": \"vytvořit nový odkaz\",\n    \"CREATE_A_TAG\": \"vytvořit štítek\",\n    \"CURRENT\": \"proud\",\n    \"CURRENT_UPLOAD\": \"aktuální nahrávání\",\n    \"CUSTOM_LINK_URL\": \"vlastní adresa URL odkazu\",\n    \"DASHBOARD\": \"přístrojová deska\",\n    \"DATE\": \"datum\",\n    \"DISPLAY_HIDDEN_FILES\": \"zobrazit skryté soubory\",\n    \"DOESNT_MATCH\": \"neodpovídá\",\n    \"DONE\": \"Hotovo\",\n    \"DOWNLOAD\": \"stažení\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"chcete uložit změny\",\n    \"DROP_HERE_TO_UPLOAD\": \"přetáhněte sem a nahrajte\",\n    \"EDITOR\": \"editor\",\n    \"EMBED\": \"vložit\",\n    \"EMPTY\": \"prázdný\",\n    \"ENCRYPTION_KEY\": \"šifrovací klíč\",\n    \"ENDPOINT\": \"koncový bod\",\n    \"ERROR\": \"chyba\",\n    \"EXISTING_LINKS\": \"existující odkazy\",\n    \"EXPIRATION\": \"vypršení\",\n    \"EXPORT_AS_{{VALUE}}\": \"exportovat jako {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"zde se zobrazí často přístupné složky\",\n    \"HIDE_HIDDEN_FILES\": \"skrýt skryté soubory\",\n    \"HOME\": \"domů\",\n    \"HOSTNAME*\": \"název hostitele\",\n    \"HOST_KEY\": \"hostitelský klíč\",\n    \"INCORRECT_PASSWORD\": \"nesprávné heslo\",\n    \"INFO\": \"informace\",\n    \"INTERNAL_ERROR\": \"Vnitřní chyba\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"interní chyba: nelze vytvořit {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"neplatný účet\",\n    \"INVALID_PASSWORD\": \"neplatné heslo\",\n    \"LAYOUT\": \"rozvržení\",\n    \"LOADING\": \"načítání\",\n    \"LOCATION\": \"umístění\",\n    \"MISSING_DEPENDENCY\": \"chybějící závislost\",\n    \"MORE_DETAILS\": \"více podrobností\",\n    \"NAVIGATE\": \"navigovat\",\n    \"NEW_FILE\": \"nový soubor\",\n    \"NEW_FILE::SHORT\": \"nový soubor\",\n    \"NEW_FOLDER\": \"nový adresář\",\n    \"NEW_FOLDER::SHORT\": \"nový adresář\",\n    \"NO\": \"Ne\",\n    \"NOT_ALLOWED\": \"nepovoleno\",\n    \"NOT_AUTHORISED\": \"není povoleno\",\n    \"NOT_FOUND\": \"nenalezeno\",\n    \"NOT_IMPLEMENTED\": \"není implementováno\",\n    \"NOT_SUPPORTED\": \"není podporováno\",\n    \"NOT_VALID\": \"neplatný\",\n    \"NUMBER_OF_CONNECTIONS\": \"počet připojení\",\n    \"OK\": \"OK\",\n    \"ONLY_FOR_USERS\": \"Pouze pro uživatele\",\n    \"OOPS\": \"Jejda\",\n    \"PASSPHRASE\": \"přístupová fráze\",\n    \"PASSWORD\": \"Heslo\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"heslo nemůže být prázdné\",\n    \"PATH\": \"cesta\",\n    \"PERMISSION_DENIED\": \"přístup odepřen\",\n    \"PICK_A_MASTER_PASSWORD\": \"vyberte hlavní heslo\",\n    \"PORT\": \"port\",\n    \"POWERED_BY\": \"poháněno\",\n    \"PROPERTIES\": \"vlastnosti\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"chránit přístup pomocí hesla\",\n    \"QUICK_ACCESS\": \"rychlý přístup\",\n    \"REGION\": \"oblast\",\n    \"REMEMBER_ME\": \"zapamatuj si mě\",\n    \"REMOVE\": \"odstranit\",\n    \"RENAME\": \"přejmenovat\",\n    \"RENAME_AS\": \"přejmenovat jako\",\n    \"RESTRICTIONS\": \"omezení\",\n    \"RUNNING\": \"běh\",\n    \"SAVE_CURRENT_FILE\": \"uložit aktuální soubor\",\n    \"SEARCH\": \"Vyhledávání\",\n    \"SETTINGS\": \"nastavení\",\n    \"SHARE\": \"sdílet\",\n    \"SHARED_DRIVE\": \"sdílený disk\",\n    \"SKIP_TO_CONTENT\": \"Přejít na obsah\",\n    \"SORT\": \"seřadit\",\n    \"SORT_BY_DATE\": \"seřadit podle data\",\n    \"SORT_BY_NAME\": \"Seřaď dle jména\",\n    \"SORT_BY_SIZE\": \"\",\n    \"SORT_BY_TYPE\": \"seřadit podle typu\",\n    \"STARRED\": \"oblíbené\",\n    \"SUPPORT\": \"Podpěra, podpora\",\n    \"TAG\": \"štítek\",\n    \"TAGS\": \"štítky\",\n    \"THERE_IS_NOTHING_HERE\": \"není tu nic\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"soubor {{VALUE}} byl smazán\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"soubor {{VALUE}} byl přejmenován\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"odkaz byl zkopírován do schránky\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"odkaz nebude platný po\",\n    \"TIMEOUT\": \"Časový limit\",\n    \"TODO\": \"dělat\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"dopravní zácpy, zkuste to znovu později\",\n    \"UPLOAD\": \"nahrát\",\n    \"UPLOADER\": \"uploader\",\n    \"USERNAME\": \"uživatelské jméno\",\n    \"VERSION\": \"verze\",\n    \"VIEWER\": \"divák\",\n    \"WAITING\": \"čekání\",\n    \"YES\": \"Ano\",\n    \"YOUR_EMAIL_ADDRESS\": \"Vaše emailová adresa\",\n    \"YOUR_FILES\": \"vaše soubory\",\n    \"YOUR_MASTER_PASSWORD\": \"vaše hlavní heslo\",\n    \"YOU_CANT_DO_THAT\": \"to nemůžeš udělat\"\n}\n"
  },
  {
    "path": "public/assets/locales/da.json",
    "content": "{\n    \"ABORTED\": \"aborterede\",\n    \"ABORT_CURRENT_UPLOADS?\": \"afbryde den aktuelle upload?\",\n    \"ACTIVITY\": \"Aktivitet\",\n    \"ADVANCED\": \"fremskreden\",\n    \"ALL_DONE\": \"helt færdig\",\n    \"ALREADY_EXIST\": \"eksisterer allerede\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"En fil med navnet {{VALUE}} blev oprettet\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"En mappe med navnet {{VALUE}} blev oprettet\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"bogmærke\",\n    \"CAMERA\": \"kamera\",\n    \"CANCEL\": \"afbestille\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"kan ikke oprette en forbindelse\",\n    \"CANT_LOAD_THIS_PICTURE\": \"kan ikke indlæse dette billede\",\n    \"CANT_USE_FILESYSTEM\": \"kan bruge filsystem\",\n    \"CAN_RESHARE\": \"kan videredele\",\n    \"CODE\": \"kode\",\n    \"CONFIGURE\": \"configure\",\n    \"CONFIRM_BY_TYPING\": \"bekræft ved at skrive\",\n    \"CONNECT\": \"forbinde\",\n    \"CONNECTION_LOST\": \"forbindelsen mistet\",\n    \"COPIED_TO_CLIPBOARD\": \"kopieret til udklipsholder\",\n    \"CREATE_A_NEW_LINK\": \"oprette et nyt link\",\n    \"CREATE_A_TAG\": \"opret et mærke\",\n    \"CURRENT\": \"nuværende\",\n    \"CURRENT_UPLOAD\": \"nuværende upload\",\n    \"CUSTOM_LINK_URL\": \"brugerdefineret link URL\",\n    \"DASHBOARD\": \"instrumentbræt\",\n    \"DATE\": \"dato\",\n    \"DISPLAY_HIDDEN_FILES\": \"vis skjulte filer\",\n    \"DOESNT_MATCH\": \"stemmer ikke overens\",\n    \"DONE\": \"Færdig\",\n    \"DOWNLOAD\": \"Hent\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"vil du gemme ændringerne\",\n    \"DROP_HERE_TO_UPLOAD\": \"slip her for at uploade\",\n    \"EDITOR\": \"redaktør\",\n    \"EMBED\": \"indlejr\",\n    \"EMPTY\": \"tom\",\n    \"ENCRYPTION_KEY\": \"krypteringsnøgle\",\n    \"ENDPOINT\": \"endepunkt\",\n    \"ERROR\": \"fejl\",\n    \"EXISTING_LINKS\": \"eksisterende links\",\n    \"EXPIRATION\": \"udløb\",\n    \"EXPORT_AS_{{VALUE}}\": \"eksporter som {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"ofte vises mapper der vises her\",\n    \"HIDE_HIDDEN_FILES\": \"skjul skjulte filer\",\n    \"HOME\": \"hjem\",\n    \"HOSTNAME*\": \"værtsnavn\",\n    \"HOST_KEY\": \"værtsnøgle\",\n    \"INCORRECT_PASSWORD\": \"forkert kodeord\",\n    \"INFO\": \"info\",\n    \"INTERNAL_ERROR\": \"Intern fejl\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"intern fejl: kan ikke oprette en {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"ugyldig konto\",\n    \"INVALID_PASSWORD\": \"forkert kodeord\",\n    \"LAYOUT\": \"layout\",\n    \"LOADING\": \"indlæser\",\n    \"LOCATION\": \"Beliggenhed\",\n    \"MISSING_DEPENDENCY\": \"manglende afhængighed\",\n    \"MORE_DETAILS\": \"flere detaljer\",\n    \"NAVIGATE\": \"navigere\",\n    \"NEW_FILE\": \"ny fil\",\n    \"NEW_FILE::SHORT\": \"ny fil\",\n    \"NEW_FOLDER\": \"nyt bibliotek\",\n    \"NEW_FOLDER::SHORT\": \"nyt bibliotek\",\n    \"NO\": \"ingen\",\n    \"NOT_ALLOWED\": \"ikke tilladt\",\n    \"NOT_AUTHORISED\": \"ikke tilladt\",\n    \"NOT_FOUND\": \"ikke fundet\",\n    \"NOT_IMPLEMENTED\": \"ikke implementeret\",\n    \"NOT_SUPPORTED\": \"ikke understøttet\",\n    \"NOT_VALID\": \"ikke gyldig\",\n    \"NUMBER_OF_CONNECTIONS\": \"antal forbindelser\",\n    \"OK\": \"Okay\",\n    \"ONLY_FOR_USERS\": \"Kun til brugere\",\n    \"OOPS\": \"Ups\",\n    \"PASSPHRASE\": \"løsen\",\n    \"PASSWORD\": \"adgangskode\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"adgangskode kan ikke være tomt\",\n    \"PATH\": \"sti\",\n    \"PERMISSION_DENIED\": \"adgang nægtet\",\n    \"PICK_A_MASTER_PASSWORD\": \"vælg en hovedadgangskode\",\n    \"PORT\": \"Havn\",\n    \"POWERED_BY\": \"drevet af\",\n    \"PROPERTIES\": \"ejendomme\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"beskytte adgang med et kodeord\",\n    \"QUICK_ACCESS\": \"hurtig adgang\",\n    \"REGION\": \"område\",\n    \"REMEMBER_ME\": \"Husk mig\",\n    \"REMOVE\": \"fjerne\",\n    \"RENAME\": \"omdøb\",\n    \"RENAME_AS\": \"omdøb som\",\n    \"RESTRICTIONS\": \"restriktioner\",\n    \"RUNNING\": \"kører\",\n    \"SAVE_CURRENT_FILE\": \"gem nuværende fil\",\n    \"SEARCH\": \"Søg\",\n    \"SETTINGS\": \"indstillinger\",\n    \"SHARE\": \"del\",\n    \"SHARED_DRIVE\": \"delt drev\",\n    \"SKIP_TO_CONTENT\": \"Spring til indhold\",\n    \"SORT\": \"sortér\",\n    \"SORT_BY_DATE\": \"sorter efter dato\",\n    \"SORT_BY_NAME\": \"sorter efter navn\",\n    \"SORT_BY_SIZE\": \"sorter efter størrelse\",\n    \"SORT_BY_TYPE\": \"sorter efter type\",\n    \"STARRED\": \"markeret\",\n    \"SUPPORT\": \"support\",\n    \"TAG\": \"mærke\",\n    \"TAGS\": \"mærker\",\n    \"THERE_IS_NOTHING_HERE\": \"der er intet her\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"filen {{VALUE}} blev slettet\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"filen {{VALUE}} blev omdøbt\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"linket blev kopieret på udklipsholderen\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"linket er ikke gyldigt efter\",\n    \"TIMEOUT\": \"tiden er gået\",\n    \"TODO\": \"at gøre\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"trafikpropper, prøv igen senere\",\n    \"UPLOAD\": \"upload\",\n    \"UPLOADER\": \"uploaderen\",\n    \"USERNAME\": \"brugernavn\",\n    \"VERSION\": \"version\",\n    \"VIEWER\": \"seeren\",\n    \"WAITING\": \"venter\",\n    \"YES\": \"Ja\",\n    \"YOUR_EMAIL_ADDRESS\": \"Din email adresse\",\n    \"YOUR_FILES\": \"dine filer\",\n    \"YOUR_MASTER_PASSWORD\": \"din hovedadgangskode\",\n    \"YOU_CANT_DO_THAT\": \"du kan ikke gøre det\"\n}\n"
  },
  {
    "path": "public/assets/locales/de.json",
    "content": "{\n    \"ABORTED\": \"abgebrochen\",\n    \"ABORT_CURRENT_UPLOADS?\": \"Aktuellen Upload abbrechen?\",\n    \"ACTIVITY\": \"Aktivität\",\n    \"ADVANCED\": \"Fortgeschritten\",\n    \"ALL_DONE\": \"Alles erledigt\",\n    \"ALREADY_EXIST\": \"existieren bereits\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Eine Datei mit dem Namen {{VALUE}} wurde erstellt\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Ein Ordner mit dem Namen {{VALUE}} wurde erstellt\",\n    \"BEAUTIFUL_URL\": \"schöne URL\",\n    \"BOOKMARK\": \"Lesezeichen\",\n    \"CAMERA\": \"Kamera\",\n    \"CANCEL\": \"Abbrechen\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"Kann keine Verbindung herstellen\",\n    \"CANT_LOAD_THIS_PICTURE\": \"Kann dieses Bild nicht laden\",\n    \"CANT_USE_FILESYSTEM\": \"Kann Dateisystem nicht verwenden\",\n    \"CAN_RESHARE\": \"kann neu teilen\",\n    \"CODE\": \"Code\",\n    \"CONFIGURE\": \"Konfigurieren\",\n    \"CONFIRM_BY_TYPING\": \"Bestätige durch eingeben von\",\n    \"CONNECT\": \"Verbinden\",\n    \"CONNECTION_LOST\": \"Verbindung verloren\",\n    \"COPIED_TO_CLIPBOARD\": \"in die Zwischenablage kopiert\",\n    \"CREATE_A_NEW_LINK\": \"Erstelle einen neuen Link\",\n    \"CREATE_A_TAG\": \"Tag erstellen\",\n    \"CURRENT\": \"Aktuell\",\n    \"CURRENT_UPLOAD\": \"Aktueller Upload\",\n    \"CUSTOM_LINK_URL\": \"benutzerdefinierte Link-URL\",\n    \"DASHBOARD\": \"Dashboard\",\n    \"DATE\": \"Datum\",\n    \"DISPLAY_HIDDEN_FILES\": \"versteckte Dateien anzeigen\",\n    \"DOESNT_MATCH\": \"sind nicht gleich\",\n    \"DONE\": \"erledigt\",\n    \"DOWNLOAD\": \"herunterladen\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"Willst du die Änderungen speichern\",\n    \"DROP_HERE_TO_UPLOAD\": \"Hierhin ziehen, um hochzuladen\",\n    \"EDITOR\": \"Editor\",\n    \"EMBED\": \"einbetten\",\n    \"EMPTY\": \"leer\",\n    \"ENCRYPTION_KEY\": \"Verschlüsselungsschlüssel\",\n    \"ENDPOINT\": \"Endpunkt\",\n    \"ERROR\": \"Fehler\",\n    \"EXISTING_LINKS\": \"vorhandene Links\",\n    \"EXPIRATION\": \"Ablauf\",\n    \"EXPORT_AS_{{VALUE}}\": \"Export als {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"Hier werden Ordner angezeigt, auf die häufig zugegriffen wird\",\n    \"HIDE_HIDDEN_FILES\": \"versteckte Dateien verstecken\",\n    \"HOME\": \"Startseite\",\n    \"HOSTNAME*\": \"Hostname*\",\n    \"HOST_KEY\": \"Host-Schlüssel\",\n    \"INCORRECT_PASSWORD\": \"falsches Passwort\",\n    \"INFO\": \"Info\",\n    \"INTERNAL_ERROR\": \"Interner Fehler\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"interner Fehler: {{VALUE}} kann nicht erstellt werden\",\n    \"INVALID_ACCOUNT\": \"ungültiger Account\",\n    \"INVALID_PASSWORD\": \"Ungültiges Passwort\",\n    \"LAYOUT\": \"Layout\",\n    \"LOADING\": \"laden\",\n    \"LOCATION\": \"Standort\",\n    \"MISSING_DEPENDENCY\": \"Fehlende Abhängigkeit\",\n    \"MORE_DETAILS\": \"mehr Details\",\n    \"NAVIGATE\": \"navigieren\",\n    \"NEW_FILE\": \"neue Datei\",\n    \"NEW_FILE::SHORT\": \"neue Datei\",\n    \"NEW_FOLDER\": \"neues Verzeichnis\",\n    \"NEW_FOLDER::SHORT\": \"neues Verz.\",\n    \"NO\": \"Nein\",\n    \"NOT_ALLOWED\": \"nicht erlaubt\",\n    \"NOT_AUTHORISED\": \"nicht autorisiert\",\n    \"NOT_FOUND\": \"nicht gefunden\",\n    \"NOT_IMPLEMENTED\": \"nicht implementiert\",\n    \"NOT_SUPPORTED\": \"nicht unterstützt\",\n    \"NOT_VALID\": \"ungültig\",\n    \"NUMBER_OF_CONNECTIONS\": \"Anzahl der Verbindungen\",\n    \"OK\": \"Ok\",\n    \"ONLY_FOR_USERS\": \"Nur für Benutzer\",\n    \"OOPS\": \"Hoppla\",\n    \"PASSPHRASE\": \"Passphrase\",\n    \"PASSWORD\": \"Passwort\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"Passwort darf nicht leer sein\",\n    \"PATH\": \"Pfad\",\n    \"PERMISSION_DENIED\": \"Zugang verweigert\",\n    \"PICK_A_MASTER_PASSWORD\": \"Wähle ein Master-Passwort\",\n    \"PORT\": \"Port\",\n    \"POWERED_BY\": \"unterstützt von\",\n    \"PROPERTIES\": \"Eigenschaften\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"Schütze den Zugriff mit einem Passwort\",\n    \"QUICK_ACCESS\": \"schneller Zugriff\",\n    \"REGION\": \"Region\",\n    \"REMEMBER_ME\": \"angemeldet bleiben\",\n    \"REMOVE\": \"entfernen\",\n    \"RENAME\": \"umbenennen\",\n    \"RENAME_AS\": \"umbenennen als\",\n    \"RESTRICTIONS\": \"Beschränkungen\",\n    \"RUNNING\": \"läuft\",\n    \"SAVE_CURRENT_FILE\": \"Aktuelle Datei speichern\",\n    \"SEARCH\": \"Suche\",\n    \"SETTINGS\": \"Einstellungen\",\n    \"SHARE\": \"teilen\",\n    \"SHARED_DRIVE\": \"freigegebenes Laufwerk\",\n    \"SKIP_TO_CONTENT\": \"Zum Inhalt springen\",\n    \"SORT\": \"sortieren\",\n    \"SORT_BY_DATE\": \"nach Datum sortieren\",\n    \"SORT_BY_NAME\": \"nach Name sortieren\",\n    \"SORT_BY_SIZE\": \"nach Größe sortieren\",\n    \"SORT_BY_TYPE\": \"nach Typ sortieren\",\n    \"STARRED\": \"markiert\",\n    \"SUPPORT\": \"Unterstützung\",\n    \"TAG\": \"Tag\",\n    \"TAGS\": \"Tags\",\n    \"THERE_IS_NOTHING_HERE\": \"Hier ist nichts\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"Die Datei {{VALUE}} wurde gelöscht\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"Die Datei {{VALUE}} wurde umbenannt\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"Der Link wurde in die Zwischenablage kopiert\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"Der Link ist danach nicht mehr gültig\",\n    \"TIMEOUT\": \"Timeout\",\n    \"TODO\": \"To-Do\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"Verkehrsstau, versuche es später erneut\",\n    \"UPLOAD\": \"hochladen\",\n    \"UPLOADER\": \"Hochlader\",\n    \"USERNAME\": \"Nutzername\",\n    \"VERSION\": \"Version\",\n    \"VIEWER\": \"Anzeiger\",\n    \"WAITING\": \"warten\",\n    \"YES\": \"Ja\",\n    \"YOUR_EMAIL_ADDRESS\": \"Deine E-Mail-Adresse\",\n    \"YOUR_FILES\": \"Ihre Dateien\",\n    \"YOUR_MASTER_PASSWORD\": \"Dein Master-Passwort\",\n    \"YOU_CANT_DO_THAT\": \"das kannst du nicht machen\"\n}\n"
  },
  {
    "path": "public/assets/locales/el.json",
    "content": "{\n    \"ABORTED\": \"ματαιώθηκε\",\n    \"ABORT_CURRENT_UPLOADS?\": \"ματαίωση της τρέχουσας μεταφόρτωσης;\",\n    \"ACTIVITY\": \"Δραστηριότητα\",\n    \"ADVANCED\": \"προχωρημένος\",\n    \"ALL_DONE\": \"Όλα τελείωσαν\",\n    \"ALREADY_EXIST\": \"υπάρχει ήδη\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Δημιουργήθηκε ένα αρχείο με το όνομα {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Δημιουργήθηκε ένας φάκελος με το όνομα {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"όμορφη_υρ\",\n    \"BOOKMARK\": \"σελιδοδείκτης\",\n    \"CAMERA\": \"Κάμερα\",\n    \"CANCEL\": \"Ματαίωση\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"δεν μπορεί να δημιουργήσει σύνδεση\",\n    \"CANT_LOAD_THIS_PICTURE\": \"δεν μπορώ να φορτώσω αυτήν την εικόνα\",\n    \"CANT_USE_FILESYSTEM\": \"δεν μπορεί να χρησιμοποιήσει το σύστημα αρχείων\",\n    \"CAN_RESHARE\": \"μπορεί να αναδημοσιεύσει\",\n    \"CODE\": \"κώδικας\",\n    \"CONFIGURE\": \"Διαμορφώστε\",\n    \"CONFIRM_BY_TYPING\": \"επιβεβαιώστε πληκτρολογώντας\",\n    \"CONNECT\": \"συνδέω-συωδεομαι\",\n    \"CONNECTION_LOST\": \"η σύνδεση χάθηκε\",\n    \"COPIED_TO_CLIPBOARD\": \"αντιγράφηκε στο πρόχειρο\",\n    \"CREATE_A_NEW_LINK\": \"δημιουργήστε έναν νέο σύνδεσμο\",\n    \"CREATE_A_TAG\": \"δημιουργία ετικέτας\",\n    \"CURRENT\": \"Τρέχον\",\n    \"CURRENT_UPLOAD\": \"τρέχουσα μεταφόρτωση\",\n    \"CUSTOM_LINK_URL\": \"προσαρμοσμένη διεύθυνση URL συνδέσμου\",\n    \"DASHBOARD\": \"ταμπλό\",\n    \"DATE\": \"ημερομηνία\",\n    \"DISPLAY_HIDDEN_FILES\": \"εμφάνιση κρυφών αρχείων\",\n    \"DOESNT_MATCH\": \"δεν ταιριάζει\",\n    \"DONE\": \"Ολοκληρώθηκε\",\n    \"DOWNLOAD\": \"Κατεβάστε\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"θέλετε να αποθηκεύσετε τις αλλαγές\",\n    \"DROP_HERE_TO_UPLOAD\": \"ρίξτε εδώ για να ανεβάσετε\",\n    \"EDITOR\": \"συντάκτης\",\n    \"EMBED\": \"ενσωμάτωση\",\n    \"EMPTY\": \"κενό\",\n    \"ENCRYPTION_KEY\": \"κλειδί κρυπτογράφησης\",\n    \"ENDPOINT\": \"τελικό σημείο\",\n    \"ERROR\": \"λάθος\",\n    \"EXISTING_LINKS\": \"υπάρχοντες σύνδεσμοι\",\n    \"EXPIRATION\": \"λήξη\",\n    \"EXPORT_AS_{{VALUE}}\": \"εξαγωγή ως {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"συχνά εμφανίζονται φάκελοι πρόσβασης εδώ\",\n    \"HIDE_HIDDEN_FILES\": \"απόκρυψη κρυφών αρχείων\",\n    \"HOME\": \"αρχική σελίδα\",\n    \"HOSTNAME*\": \"όνομα κεντρικού υπολογιστή\",\n    \"HOST_KEY\": \"κλειδί κεντρικού υπολογιστή\",\n    \"INCORRECT_PASSWORD\": \"Λάθος κωδικός\",\n    \"INFO\": \"πληροφορίες\",\n    \"INTERNAL_ERROR\": \"Εσωτερικό σφάλμα\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"εσωτερικό σφάλμα: δεν είναι δυνατή η δημιουργία {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"μη έγκυρος λογαριασμός\",\n    \"INVALID_PASSWORD\": \"Λανθασμένος κωδικός\",\n    \"LAYOUT\": \"διάταξη\",\n    \"LOADING\": \"φόρτωση\",\n    \"LOCATION\": \"τοποθεσία\",\n    \"MISSING_DEPENDENCY\": \"λείπει εξάρτηση\",\n    \"MORE_DETAILS\": \"λεπτομέρειες\",\n    \"NAVIGATE\": \"κυβερνώ\",\n    \"NEW_FILE\": \"νέο αρχείο\",\n    \"NEW_FILE::SHORT\": \"νέο αρχείο\",\n    \"NEW_FOLDER\": \"νέος κατάλογος\",\n    \"NEW_FOLDER::SHORT\": \"νέος κατάλογος\",\n    \"NO\": \"όχι\",\n    \"NOT_ALLOWED\": \"δεν επιτρέπεται\",\n    \"NOT_AUTHORISED\": \"δεν επιτρέπεται\",\n    \"NOT_FOUND\": \"δεν βρέθηκε\",\n    \"NOT_IMPLEMENTED\": \"δεν εφαρμόζεται\",\n    \"NOT_SUPPORTED\": \"Δεν υποστηρίζεται\",\n    \"NOT_VALID\": \"δεν είναι έγκυρη\",\n    \"NUMBER_OF_CONNECTIONS\": \"αριθμός συνδέσεων\",\n    \"OK\": \"Εντάξει\",\n    \"ONLY_FOR_USERS\": \"Μόνο για χρήστες\",\n    \"OOPS\": \"Ωχ\",\n    \"PASSPHRASE\": \"φράση πρόσβασης\",\n    \"PASSWORD\": \"Κωδικός πρόσβασης\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"ο κωδικός πρόσβασης δεν μπορεί να είναι κενός\",\n    \"PATH\": \"μονοπάτι\",\n    \"PERMISSION_DENIED\": \"η άδεια απορρίφθηκε\",\n    \"PICK_A_MASTER_PASSWORD\": \"επιλέξτε έναν κύριο κωδικό πρόσβασης\",\n    \"PORT\": \"θύρα\",\n    \"POWERED_BY\": \"τροφοδοτείται από\",\n    \"PROPERTIES\": \"ιδιότητες\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"προστασία της πρόσβασης με κωδικό πρόσβασης\",\n    \"QUICK_ACCESS\": \"άμεση πρόσβαση\",\n    \"REGION\": \"περιοχή\",\n    \"REMEMBER_ME\": \"Θυμήσου με\",\n    \"REMOVE\": \"αφαιρώ\",\n    \"RENAME\": \"μετονομασία\",\n    \"RENAME_AS\": \"μετονομασία ως\",\n    \"RESTRICTIONS\": \"περιορισμοί\",\n    \"RUNNING\": \"εκτελείται\",\n    \"SAVE_CURRENT_FILE\": \"αποθήκευση τρέχοντος αρχείου\",\n    \"SEARCH\": \"Αναζήτηση\",\n    \"SETTINGS\": \"Ρυθμίσεις\",\n    \"SHARE\": \"κοινοποίηση\",\n    \"SHARED_DRIVE\": \"κοινόχρηστος δίσκος\",\n    \"SKIP_TO_CONTENT\": \"Μετάβαση στο περιεχόμενο\",\n    \"SORT\": \"ταξινόμηση\",\n    \"SORT_BY_DATE\": \"ταξινόμηση κατά ημερομηνία\",\n    \"SORT_BY_NAME\": \"ταξινόμηση κατά όνομα\",\n    \"SORT_BY_SIZE\": \"ταξινόμηση κατά μέγεθος\",\n    \"SORT_BY_TYPE\": \"ταξινόμηση ανά τύπο\",\n    \"STARRED\": \"αστεράκι\",\n    \"SUPPORT\": \"υποστήριξη\",\n    \"TAG\": \"ετικέτα\",\n    \"TAGS\": \"ετικέτες\",\n    \"THERE_IS_NOTHING_HERE\": \"δεν υπάρχει τίποτα εδώ\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"το αρχείο {{VALUE}} διαγράφηκε\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"το αρχείο {{VALUE}} μετονομάστηκε\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"ο σύνδεσμος αντιγράφηκε στο πρόχειρο\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"ο σύνδεσμος δεν θα είναι έγκυρος μετά\",\n    \"TIMEOUT\": \"τέλος χρόνου\",\n    \"TODO\": \"να κάνω\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"κυκλοφοριακή συμφόρηση, δοκιμάστε ξανά αργότερα\",\n    \"UPLOAD\": \"μεταφόρτωση\",\n    \"UPLOADER\": \"μεταφορτωτής\",\n    \"USERNAME\": \"όνομα χρήστη\",\n    \"VERSION\": \"έκδοση\",\n    \"VIEWER\": \"θεατής\",\n    \"WAITING\": \"αναμονή\",\n    \"YES\": \"Ναι.\",\n    \"YOUR_EMAIL_ADDRESS\": \"η ηλεκτρονική σου διεύθυνση\",\n    \"YOUR_FILES\": \"τα αρχεία σας\",\n    \"YOUR_MASTER_PASSWORD\": \"τον κύριο κωδικό πρόσβασής σας\",\n    \"YOU_CANT_DO_THAT\": \"δεν μπορείς να το κάνεις αυτό\"\n}\n"
  },
  {
    "path": "public/assets/locales/es.json",
    "content": "{\n    \"ABORTED\": \"abortado\",\n    \"ABORT_CURRENT_UPLOADS?\": \"¿cancelar la carga actual?\",\n    \"ACTIVITY\": \"Actividad\",\n    \"ADVANCED\": \"avanzado\",\n    \"ALL_DONE\": \"todo listo\",\n    \"ALREADY_EXIST\": \"ya existe\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Se creó un archivo llamado {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Se creó una carpeta llamada {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"URL bonita\",\n    \"BOOKMARK\": \"marcador\",\n    \"CAMERA\": \"cámara\",\n    \"CANCEL\": \"cancelar\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"no puedo establecer una conexión\",\n    \"CANT_LOAD_THIS_PICTURE\": \"no puedo cargar esta foto\",\n    \"CANT_USE_FILESYSTEM\": \"no puede usar el sistema de archivos\",\n    \"CAN_RESHARE\": \"puede compartir\",\n    \"CODE\": \"código\",\n    \"CONFIGURE\": \"configurar\",\n    \"CONFIRM_BY_TYPING\": \"confirmar escribiendo\",\n    \"CONNECT\": \"conectar\",\n    \"CONNECTION_LOST\": \"conexión perdida\",\n    \"COPIED_TO_CLIPBOARD\": \"Copiado al portapapeles\",\n    \"CREATE_A_NEW_LINK\": \"crear un nuevo enlace\",\n    \"CREATE_A_TAG\": \"crear una etiqueta\",\n    \"CURRENT\": \"Actual\",\n    \"CURRENT_UPLOAD\": \"carga actual\",\n    \"CUSTOM_LINK_URL\": \"URL de enlace personalizado\",\n    \"DASHBOARD\": \"tablero\",\n    \"DATE\": \"fecha\",\n    \"DISPLAY_HIDDEN_FILES\": \"mostrar archivos ocultos\",\n    \"DOESNT_MATCH\": \"no coincide\",\n    \"DONE\": \"hecho\",\n    \"DOWNLOAD\": \"descargar\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"quieres guardar los cambios\",\n    \"DROP_HERE_TO_UPLOAD\": \"arrastra aquí para subir\",\n    \"EDITOR\": \"editor\",\n    \"EMBED\": \"incrustar\",\n    \"EMPTY\": \"vacío\",\n    \"ENCRYPTION_KEY\": \"Clave de encriptación\",\n    \"ENDPOINT\": \"punto final\",\n    \"ERROR\": \"error\",\n    \"EXISTING_LINKS\": \"enlaces existentes\",\n    \"EXPIRATION\": \"vencimiento\",\n    \"EXPORT_AS_{{VALUE}}\": \"exportar como {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"las carpetas de acceso frecuente se mostrarán aquí\",\n    \"HIDE_HIDDEN_FILES\": \"ocultar archivos ocultos\",\n    \"HOME\": \"inicio\",\n    \"HOSTNAME*\": \"nombre de servidor*\",\n    \"HOST_KEY\": \"clave de servidor\",\n    \"INCORRECT_PASSWORD\": \"la contraseña no es correcta\",\n    \"INFO\": \"información\",\n    \"INTERNAL_ERROR\": \"Error interno\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"error interno: no se puede crear un {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"cuenta no válida\",\n    \"INVALID_PASSWORD\": \"contraseña inválida\",\n    \"LAYOUT\": \"diseño\",\n    \"LOADING\": \"cargando\",\n    \"LOCATION\": \"ubicación\",\n    \"MISSING_DEPENDENCY\": \"falta dependencia\",\n    \"MORE_DETAILS\": \"más detalles\",\n    \"NAVIGATE\": \"navegar\",\n    \"NEW_FILE\": \"nuevo fichero\",\n    \"NEW_FILE::SHORT\": \"nuevo fichero\",\n    \"NEW_FOLDER\": \"nuevo directorio\",\n    \"NEW_FOLDER::SHORT\": \"nuevo direct.\",\n    \"NO\": \"No\",\n    \"NOT_ALLOWED\": \"No permitido\",\n    \"NOT_AUTHORISED\": \"no autorizado\",\n    \"NOT_FOUND\": \"no encontrado\",\n    \"NOT_IMPLEMENTED\": \"no se ha implementado\",\n    \"NOT_SUPPORTED\": \"No soportado\",\n    \"NOT_VALID\": \"no es válido\",\n    \"NUMBER_OF_CONNECTIONS\": \"cantidad de conexiones\",\n    \"OK\": \"Vale\",\n    \"ONLY_FOR_USERS\": \"Solo para usuarios\",\n    \"OOPS\": \"Ups\",\n    \"PASSPHRASE\": \"frase de contraseña\",\n    \"PASSWORD\": \"contraseña\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"la contraseña no puede estar vacía\",\n    \"PATH\": \"camino\",\n    \"PERMISSION_DENIED\": \"Permiso denegado\",\n    \"PICK_A_MASTER_PASSWORD\": \"elige una contraseña maestra\",\n    \"PORT\": \"Puerto\",\n    \"POWERED_BY\": \"impulsado por\",\n    \"PROPERTIES\": \"propiedades\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"proteger el acceso con una contraseña\",\n    \"QUICK_ACCESS\": \"acceso rápido\",\n    \"REGION\": \"región\",\n    \"REMEMBER_ME\": \"Recuérdame\",\n    \"REMOVE\": \"eliminar\",\n    \"RENAME\": \"renombrar\",\n    \"RENAME_AS\": \"renombrar como\",\n    \"RESTRICTIONS\": \"restricciones\",\n    \"RUNNING\": \"ejecutando\",\n    \"SAVE_CURRENT_FILE\": \"guardar el archivo actual\",\n    \"SEARCH\": \"buscar\",\n    \"SETTINGS\": \"ajustes\",\n    \"SHARE\": \"compartir\",\n    \"SHARED_DRIVE\": \"unidad compartida\",\n    \"SKIP_TO_CONTENT\": \"Saltar al contenido\",\n    \"SORT\": \"ordenar\",\n    \"SORT_BY_DATE\": \"ordenar por fecha\",\n    \"SORT_BY_NAME\": \"ordenar por nombre\",\n    \"SORT_BY_SIZE\": \"ordenar por tamaño\",\n    \"SORT_BY_TYPE\": \"ordenar por tipo\",\n    \"STARRED\": \"destacado\",\n    \"SUPPORT\": \"apoyo\",\n    \"TAG\": \"etiqueta\",\n    \"TAGS\": \"etiquetas\",\n    \"THERE_IS_NOTHING_HERE\": \"No hay nada aquí\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"se eliminó el archivo {{VALUE}}\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"se cambió el nombre del archivo {{VALUE}}\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"el enlace fue copiado al portapapeles\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"el enlace no será válido después\",\n    \"TIMEOUT\": \"se acabó el tiempo\",\n    \"TODO\": \"todo\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"congestión de tráfico, intenta nuevamente más tarde\",\n    \"UPLOAD\": \"subir\",\n    \"UPLOADER\": \"cargador\",\n    \"USERNAME\": \"nombre de usuario\",\n    \"VERSION\": \"versión\",\n    \"VIEWER\": \"espectador\",\n    \"WAITING\": \"esperando\",\n    \"YES\": \"sí\",\n    \"YOUR_EMAIL_ADDRESS\": \"tu correo electrónico\",\n    \"YOUR_FILES\": \"tus archivos\",\n    \"YOUR_MASTER_PASSWORD\": \"tu contraseña maestra\",\n    \"YOU_CANT_DO_THAT\": \"no puedes hacer eso\"\n}\n"
  },
  {
    "path": "public/assets/locales/et.json",
    "content": "{\n    \"ABORTED\": \"katkestatud\",\n    \"ABORT_CURRENT_UPLOADS?\": \"kas katkestada praegune üleslaadimine?\",\n    \"ACTIVITY\": \"Tegevus\",\n    \"ADVANCED\": \"edasijõudnud\",\n    \"ALL_DONE\": \"kõik tehtud\",\n    \"ALREADY_EXIST\": \"juba olemas\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Loodi fail nimega {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Loodi kaust nimega {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"ilus_url\",\n    \"BOOKMARK\": \"järjehoidja\",\n    \"CAMERA\": \"kaamera\",\n    \"CANCEL\": \"tühista\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"ühendust ei saa luua\",\n    \"CANT_LOAD_THIS_PICTURE\": \"seda pilti ei saa laadida\",\n    \"CANT_USE_FILESYSTEM\": \"ei saa kasutada failisüsteemi\",\n    \"CAN_RESHARE\": \"saab edasi jagada\",\n    \"CODE\": \"kood\",\n    \"CONFIGURE\": \"seadistama\",\n    \"CONFIRM_BY_TYPING\": \"kinnitage sisestades\",\n    \"CONNECT\": \"ühendada\",\n    \"CONNECTION_LOST\": \"ühendus katkes\",\n    \"COPIED_TO_CLIPBOARD\": \"kopeeriti lõikelauale\",\n    \"CREATE_A_NEW_LINK\": \"loo uus link\",\n    \"CREATE_A_TAG\": \"loo silt\",\n    \"CURRENT\": \"praegune\",\n    \"CURRENT_UPLOAD\": \"praegune üleslaadimine\",\n    \"CUSTOM_LINK_URL\": \"kohandatud lingi URL\",\n    \"DASHBOARD\": \"armatuurlaud\",\n    \"DATE\": \"kuupäev\",\n    \"DISPLAY_HIDDEN_FILES\": \"kuva peidetud faile\",\n    \"DOESNT_MATCH\": \"ei sobi\",\n    \"DONE\": \"tehtud\",\n    \"DOWNLOAD\": \"lae alla\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"kas soovite muudatused salvestada?\",\n    \"DROP_HERE_TO_UPLOAD\": \"laadimiseks klõpsake siin\",\n    \"EDITOR\": \"toimetaja\",\n    \"EMBED\": \"manusta\",\n    \"EMPTY\": \"tühi\",\n    \"ENCRYPTION_KEY\": \"krüptimisvõti\",\n    \"ENDPOINT\": \"lõpp-punkt\",\n    \"ERROR\": \"viga\",\n    \"EXISTING_LINKS\": \"olemasolevad lingid\",\n    \"EXPIRATION\": \"aegumine\",\n    \"EXPORT_AS_{{VALUE}}\": \"eksportima kui {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"siin kuvatakse sageli juurdepääsuga kaustad\",\n    \"HIDE_HIDDEN_FILES\": \"peita peidetud failid\",\n    \"HOME\": \"avaleht\",\n    \"HOSTNAME*\": \"hostinimi\",\n    \"HOST_KEY\": \"host võti\",\n    \"INCORRECT_PASSWORD\": \"vale salasõna\",\n    \"INFO\": \"info\",\n    \"INTERNAL_ERROR\": \"Sisemine viga\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"sisemine viga: {{VALUE}} ei saa luua\",\n    \"INVALID_ACCOUNT\": \"kehtetu konto\",\n    \"INVALID_PASSWORD\": \"vale parool\",\n    \"LAYOUT\": \"paigutus\",\n    \"LOADING\": \"laadimine\",\n    \"LOCATION\": \"asukoht\",\n    \"MISSING_DEPENDENCY\": \"puudub sõltuvus\",\n    \"MORE_DETAILS\": \"rohkem üksikasju\",\n    \"NAVIGATE\": \"navigeerima\",\n    \"NEW_FILE\": \"uus fail\",\n    \"NEW_FILE::SHORT\": \"uus fail\",\n    \"NEW_FOLDER\": \"uus kataloog\",\n    \"NEW_FOLDER::SHORT\": \"uus kataloog\",\n    \"NO\": \"ei\",\n    \"NOT_ALLOWED\": \"ei ole lubatud\",\n    \"NOT_AUTHORISED\": \"pole volitatud\",\n    \"NOT_FOUND\": \"ei leitud\",\n    \"NOT_IMPLEMENTED\": \"pole rakendatud\",\n    \"NOT_SUPPORTED\": \"ei toetata\",\n    \"NOT_VALID\": \"ei kehti\",\n    \"NUMBER_OF_CONNECTIONS\": \"ühenduste arv\",\n    \"OK\": \"Okei\",\n    \"ONLY_FOR_USERS\": \"Ainult kasutajatele\",\n    \"OOPS\": \"Vabandust\",\n    \"PASSPHRASE\": \"parool\",\n    \"PASSWORD\": \"parool\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"parool ei tohi olla tühi\",\n    \"PATH\": \"tee\",\n    \"PERMISSION_DENIED\": \"luba on keelatud\",\n    \"PICK_A_MASTER_PASSWORD\": \"vali põhiparool\",\n    \"PORT\": \"port\",\n    \"POWERED_BY\": \"toetatud\",\n    \"PROPERTIES\": \"omadused\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"kaitsta juurdepääsu parooliga\",\n    \"QUICK_ACCESS\": \"kiirpääs\",\n    \"REGION\": \"piirkonnas\",\n    \"REMEMBER_ME\": \"mäleta mind\",\n    \"REMOVE\": \"eemalda\",\n    \"RENAME\": \"nimeta ümber\",\n    \"RENAME_AS\": \"nimeta ümber kui\",\n    \"RESTRICTIONS\": \"piirangud\",\n    \"RUNNING\": \"töötab\",\n    \"SAVE_CURRENT_FILE\": \"salvesta praegune fail\",\n    \"SEARCH\": \"otsing\",\n    \"SETTINGS\": \"seaded\",\n    \"SHARE\": \"jaga\",\n    \"SHARED_DRIVE\": \"jagatud ketas\",\n    \"SKIP_TO_CONTENT\": \"Sisusse\",\n    \"SORT\": \"sorteeri\",\n    \"SORT_BY_DATE\": \"järjesta kuupäeva järgi\",\n    \"SORT_BY_NAME\": \"järjesta nime järgi\",\n    \"SORT_BY_SIZE\": \"sorteeri suuruse järgi\",\n    \"SORT_BY_TYPE\": \"sorteeri tüübi järgi\",\n    \"STARRED\": \"tärniga märgitud\",\n    \"SUPPORT\": \"toetus\",\n    \"TAG\": \"silt\",\n    \"TAGS\": \"sildid\",\n    \"THERE_IS_NOTHING_HERE\": \"siin pole midagi\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"fail {{VALUE}} kustutati\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"fail {{VALUE}} nimetati ümber\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"link kopeeriti lõikelauale\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"link ei kehti pärast\",\n    \"TIMEOUT\": \"aeg maha\",\n    \"TODO\": \"tegema\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"liiklusummikud, proovige hiljem uuesti\",\n    \"UPLOAD\": \"laadi üles\",\n    \"UPLOADER\": \"üleslaadija\",\n    \"USERNAME\": \"kasutajanimi\",\n    \"VERSION\": \"versioon\",\n    \"VIEWER\": \"vaataja\",\n    \"WAITING\": \"ootamine\",\n    \"YES\": \"jah\",\n    \"YOUR_EMAIL_ADDRESS\": \"sinu emaili aadress\",\n    \"YOUR_FILES\": \"sinu failid\",\n    \"YOUR_MASTER_PASSWORD\": \"teie põhiparool\",\n    \"YOU_CANT_DO_THAT\": \"sa ei saa seda teha\"\n}\n"
  },
  {
    "path": "public/assets/locales/eu.json",
    "content": "{\n    \"ABORTED\": \"abortatu\",\n    \"ABORT_CURRENT_UPLOADS?\": \"Uneko igoera bertan behera utzi?\",\n    \"ACTIVITY\": \"Jarduera\",\n    \"ADVANCED\": \"aurreratu\",\n    \"ALL_DONE\": \"dena eginda\",\n    \"ALREADY_EXIST\": \"dagoeneko existitzen dira\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"{{VALUE}} izeneko fitxategia sortu zen\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"{{VALUE}} izeneko karpeta sortu da\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"laster-marka\",\n    \"CAMERA\": \"kamera\",\n    \"CANCEL\": \"bertan behera\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"Ezin da konexiorik ezarri\",\n    \"CANT_LOAD_THIS_PICTURE\": \"Ezin da irudi hau kargatu\",\n    \"CANT_USE_FILESYSTEM\": \"ezin da sistema erabili\",\n    \"CAN_RESHARE\": \"birkargatu dezake\",\n    \"CODE\": \"kodea\",\n    \"CONFIGURE\": \"konfiguratu\",\n    \"CONFIRM_BY_TYPING\": \"berretsi idatzita\",\n    \"CONNECT\": \"konektatu\",\n    \"CONNECTION_LOST\": \"konexioa galdu da\",\n    \"COPIED_TO_CLIPBOARD\": \"arbelean kopiatuta\",\n    \"CREATE_A_NEW_LINK\": \"lotura berria sortu\",\n    \"CREATE_A_TAG\": \"etiketa sortu\",\n    \"CURRENT\": \"egungo\",\n    \"CURRENT_UPLOAD\": \"Uneko igoera\",\n    \"CUSTOM_LINK_URL\": \"esteka URL pertsonalizatua\",\n    \"DASHBOARD\": \"Arbel\",\n    \"DATE\": \"data\",\n    \"DISPLAY_HIDDEN_FILES\": \"erakutsi ezkutuko fitxategiak\",\n    \"DOESNT_MATCH\": \"ez dator bat\",\n    \"DONE\": \"eginda\",\n    \"DOWNLOAD\": \"deskargatu\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"nahi duzu aldaketak gordetzea\",\n    \"DROP_HERE_TO_UPLOAD\": \"jaregin hemen kargatzeko\",\n    \"EDITOR\": \"editore\",\n    \"EMBED\": \"txertatu\",\n    \"EMPTY\": \"hutsik\",\n    \"ENCRYPTION_KEY\": \"zifratzeko gakoa\",\n    \"ENDPOINT\": \"amaiera\",\n    \"ERROR\": \"errorea\",\n    \"EXISTING_LINKS\": \"dauden loturak\",\n    \"EXPIRATION\": \"iraungitze\",\n    \"EXPORT_AS_{{VALUE}}\": \"esportatu {{VALUE}} gisa\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"maiz sarbide karpetak hemen erakutsiko dira\",\n    \"HIDE_HIDDEN_FILES\": \"ezkutuko fitxategiak ezkutatu\",\n    \"HOME\": \"hasiera\",\n    \"HOSTNAME*\": \"hostname\",\n    \"HOST_KEY\": \"ostalariaren gakoa\",\n    \"INCORRECT_PASSWORD\": \"Pasahitz okerra\",\n    \"INFO\": \"info\",\n    \"INTERNAL_ERROR\": \"Barne-errorea\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"barne errorea: ezin da {{VALUE}} sortu\",\n    \"INVALID_ACCOUNT\": \"kontua baliogabea\",\n    \"INVALID_PASSWORD\": \"pasahitz okerra\",\n    \"LAYOUT\": \"diseinua\",\n    \"LOADING\": \"kargatzen\",\n    \"LOCATION\": \"kokapena\",\n    \"MISSING_DEPENDENCY\": \"mendekotasun falta\",\n    \"MORE_DETAILS\": \"xehetasun gehiago\",\n    \"NAVIGATE\": \"nabigatu\",\n    \"NEW_FILE\": \"fitxategi berria\",\n    \"NEW_FILE::SHORT\": \"fitxat. berria\",\n    \"NEW_FOLDER\": \"direktorio berria\",\n    \"NEW_FOLDER::SHORT\": \"direkt. berria\",\n    \"NO\": \"no\",\n    \"NOT_ALLOWED\": \"ez da onartzen\",\n    \"NOT_AUTHORISED\": \"baimenduta ez\",\n    \"NOT_FOUND\": \"ez da aurkitu\",\n    \"NOT_IMPLEMENTED\": \"ez da aplikatu\",\n    \"NOT_SUPPORTED\": \"ez da onartzen\",\n    \"NOT_VALID\": \"ez du balio\",\n    \"NUMBER_OF_CONNECTIONS\": \"konexio kopurua\",\n    \"OK\": \"Ados\",\n    \"ONLY_FOR_USERS\": \"Erabiltzaileentzat bakarrik\",\n    \"OOPS\": \"Oops\",\n    \"PASSPHRASE\": \"pasaesaldi\",\n    \"PASSWORD\": \"pasahitza\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"pasahitza ezin da hutsik egon\",\n    \"PATH\": \"bidea\",\n    \"PERMISSION_DENIED\": \"baimena ukatu\",\n    \"PICK_A_MASTER_PASSWORD\": \"aukeratu pasahitz nagusia\",\n    \"PORT\": \"portu\",\n    \"POWERED_BY\": \"bultzatuta\",\n    \"PROPERTIES\": \"propietate\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"babestu sarbidea pasahitz batekin\",\n    \"QUICK_ACCESS\": \"sarrera azkarra\",\n    \"REGION\": \"eskualde\",\n    \"REMEMBER_ME\": \"Gogora nazazu\",\n    \"REMOVE\": \"kendu\",\n    \"RENAME\": \"izena aldatu\",\n    \"RENAME_AS\": \"izena aldatu honela\",\n    \"RESTRICTIONS\": \"murrizketak\",\n    \"RUNNING\": \"exekutatzen\",\n    \"SAVE_CURRENT_FILE\": \"gorde uneko fitxategia\",\n    \"SEARCH\": \"bilatu\",\n    \"SETTINGS\": \"ezarpenak\",\n    \"SHARE\": \"partekatzea\",\n    \"SHARED_DRIVE\": \"partekatutako diskoa\",\n    \"SKIP_TO_CONTENT\": \"Joan edukira\",\n    \"SORT\": \"ordenatu\",\n    \"SORT_BY_DATE\": \"ordenatu dataren arabera\",\n    \"SORT_BY_NAME\": \"ordenatu izenaren arabera\",\n    \"SORT_BY_SIZE\": \"ordenatu tamainaren arabera\",\n    \"SORT_BY_TYPE\": \"ordenatu motaren arabera\",\n    \"STARRED\": \"izarduna\",\n    \"SUPPORT\": \"laguntza\",\n    \"TAG\": \"etiketa\",\n    \"TAGS\": \"etiketak\",\n    \"THERE_IS_NOTHING_HERE\": \"hemen ez dago ezer\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"{{VALUE}} fitxategia ezabatu da\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"{{VALUE}} fitxategiari berrizendatu zaio\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"esteka arbelean kopiatu zen\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"esteka ez da baliozkoa ondoren\",\n    \"TIMEOUT\": \"denboraz kanpo\",\n    \"TODO\": \"egin\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"trafiko pilaketa, saiatu berriro geroago\",\n    \"UPLOAD\": \"igo\",\n    \"UPLOADER\": \"igorlea\",\n    \"USERNAME\": \"Erabiltzaile izena\",\n    \"VERSION\": \"bertsioa\",\n    \"VIEWER\": \"ikusle\",\n    \"WAITING\": \"itxarote\",\n    \"YES\": \"bai\",\n    \"YOUR_EMAIL_ADDRESS\": \"Zure helbide elektronikoa\",\n    \"YOUR_FILES\": \"zure fitxategiak\",\n    \"YOUR_MASTER_PASSWORD\": \"zure pasahitz nagusia\",\n    \"YOU_CANT_DO_THAT\": \"ezin duzu hori egin\"\n}\n"
  },
  {
    "path": "public/assets/locales/fi.json",
    "content": "{\n    \"ABORTED\": \"keskeytetty\",\n    \"ABORT_CURRENT_UPLOADS?\": \"keskeytetäänkö nykyiset lähetykset?\",\n    \"ACTIVITY\": \"Toiminta\",\n    \"ADVANCED\": \"lisäasetukset\",\n    \"ALL_DONE\": \"valmista\",\n    \"ALREADY_EXIST\": \"on jo olemassa\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Tiedosto {{VALUE}} luotiin\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Kansio {{VALUE}} luotiin\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"kirjanmerkki\",\n    \"CAMERA\": \"kamera\",\n    \"CANCEL\": \"peruuta\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"ei voi muodostaa yhteyttä\",\n    \"CANT_LOAD_THIS_PICTURE\": \"tätä kuvaa ei voi ladata\",\n    \"CANT_USE_FILESYSTEM\": \"tiedostojärjestelmää ei voi käyttää\",\n    \"CAN_RESHARE\": \"voi jakaa uudelleen\",\n    \"CODE\": \"koodi\",\n    \"CONFIGURE\": \"muokkaa asetuksia\",\n    \"CONFIRM_BY_TYPING\": \"Vahvista kirjoittamalla\",\n    \"CONNECT\": \"yhdistä\",\n    \"CONNECTION_LOST\": \"yhteys katkennut\",\n    \"COPIED_TO_CLIPBOARD\": \"kopioitu leikepöydälle\",\n    \"CREATE_A_NEW_LINK\": \"luo uusi linkki\",\n    \"CREATE_A_TAG\": \"luo tunniste\",\n    \"CURRENT\": \"nykyinen\",\n    \"CURRENT_UPLOAD\": \"nykyinen lähetys\",\n    \"CUSTOM_LINK_URL\": \"mukautetun linkin URL-osoite\",\n    \"DASHBOARD\": \"kojelauta\",\n    \"DATE\": \"Päivämäärä\",\n    \"DISPLAY_HIDDEN_FILES\": \"näytä piilotetut tiedostot\",\n    \"DOESNT_MATCH\": \"ei täsmää\",\n    \"DONE\": \"valmis\",\n    \"DOWNLOAD\": \"lataa\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"haluatko tallentaa muutokset\",\n    \"DROP_HERE_TO_UPLOAD\": \"pudota tähän lähettääksesi\",\n    \"EDITOR\": \"toimittaja\",\n    \"EMBED\": \"upota\",\n    \"EMPTY\": \"tyhjä\",\n    \"ENCRYPTION_KEY\": \"salausavain\",\n    \"ENDPOINT\": \"päätepiste\",\n    \"ERROR\": \"virhe\",\n    \"EXISTING_LINKS\": \"olemassa olevat linkit\",\n    \"EXPIRATION\": \"päättyminen\",\n    \"EXPORT_AS_{{VALUE}}\": \"vie nimellä {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"usein näkyvät kansiot näytetään täällä\",\n    \"HIDE_HIDDEN_FILES\": \"piilota piilotetut tiedostot\",\n    \"HOME\": \"kotisivu\",\n    \"HOSTNAME*\": \"isäntänimi\",\n    \"HOST_KEY\": \"isäntäavain\",\n    \"INCORRECT_PASSWORD\": \"väärä salasana\",\n    \"INFO\": \"tiedot\",\n    \"INTERNAL_ERROR\": \"sisäinen virhe\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"sisäinen virhe: ei voi luoda {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"virheellinen tili\",\n    \"INVALID_PASSWORD\": \"väärä salasana\",\n    \"LAYOUT\": \"asettelu\",\n    \"LOADING\": \"ladataan\",\n    \"LOCATION\": \"sijainti\",\n    \"MISSING_DEPENDENCY\": \"puuttuva riippuvuus\",\n    \"MORE_DETAILS\": \"lisätietoja\",\n    \"NAVIGATE\": \"navigoida\",\n    \"NEW_FILE\": \"uusi tiedosto\",\n    \"NEW_FILE::SHORT\": \"uusi tiedosto\",\n    \"NEW_FOLDER\": \"uusi hakemisto\",\n    \"NEW_FOLDER::SHORT\": \"uusi hakemi..\",\n    \"NO\": \"ei\",\n    \"NOT_ALLOWED\": \"ei sallittu\",\n    \"NOT_AUTHORISED\": \"ei valtuutettu\",\n    \"NOT_FOUND\": \"ei löydy\",\n    \"NOT_IMPLEMENTED\": \"ei toteutettu\",\n    \"NOT_SUPPORTED\": \"ei tueta\",\n    \"NOT_VALID\": \"ei kelpaa\",\n    \"NUMBER_OF_CONNECTIONS\": \"yhteyksien lukumäärä\",\n    \"OK\": \"kunnossa\",\n    \"ONLY_FOR_USERS\": \"Vain käyttäjille\",\n    \"OOPS\": \"Oho\",\n    \"PASSPHRASE\": \"tunnuslause\",\n    \"PASSWORD\": \"Salasana\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"salasana ei voi olla tyhjä\",\n    \"PATH\": \"polku\",\n    \"PERMISSION_DENIED\": \"pääsy kielletty\",\n    \"PICK_A_MASTER_PASSWORD\": \"valitse pääsalasana\",\n    \"PORT\": \"portti\",\n    \"POWERED_BY\": \"powered by\",\n    \"PROPERTIES\": \"ominaisuudet\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"suojaa pääsy salasanalla\",\n    \"QUICK_ACCESS\": \"pikaosoite\",\n    \"REGION\": \"alue\",\n    \"REMEMBER_ME\": \"muista minut\",\n    \"REMOVE\": \"Poisto\",\n    \"RENAME\": \"nimeä\",\n    \"RENAME_AS\": \"uusi nimi\",\n    \"RESTRICTIONS\": \"rajoituksia\",\n    \"RUNNING\": \"käynnissä\",\n    \"SAVE_CURRENT_FILE\": \"tallenna nykyinen tiedosto\",\n    \"SEARCH\": \"Hae\",\n    \"SETTINGS\": \"asetukset\",\n    \"SHARE\": \"jaa\",\n    \"SHARED_DRIVE\": \"jaettu asema\",\n    \"SKIP_TO_CONTENT\": \"Siirry sisältöön\",\n    \"SORT\": \"lajittele\",\n    \"SORT_BY_DATE\": \"lajittele päivämäärän mukaan\",\n    \"SORT_BY_NAME\": \"lajittele nimen mukaan\",\n    \"SORT_BY_SIZE\": \"lajittele koon mukaan\",\n    \"SORT_BY_TYPE\": \"lajittele tyypin mukaan\",\n    \"STARRED\": \"tähtimerkintä\",\n    \"SUPPORT\": \"tuki\",\n    \"TAG\": \"tunniste\",\n    \"TAGS\": \"tunnisteet\",\n    \"THERE_IS_NOTHING_HERE\": \"täällä ei ole mitään\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"tiedosto {{VALUE}} poistettiin\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"tiedosto {{VALUE}} nimettiin uudelleen\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"linkki kopioitiin leikepöydälle\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"linkki ei kelpaa jälkeen\",\n    \"TIMEOUT\": \"Aikalisä\",\n    \"TODO\": \"tehtävää\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"ruuhkaa, yritä myöhemmin uudelleen\",\n    \"UPLOAD\": \"lataa\",\n    \"UPLOADER\": \"lataaja\",\n    \"USERNAME\": \"käyttäjätunnus\",\n    \"VERSION\": \"versio\",\n    \"VIEWER\": \"katsoja\",\n    \"WAITING\": \"odotetaan\",\n    \"YES\": \"Joo\",\n    \"YOUR_EMAIL_ADDRESS\": \"Sähköpostiosoitteesi\",\n    \"YOUR_FILES\": \"tiedostosi\",\n    \"YOUR_MASTER_PASSWORD\": \"pääsalasanasi\",\n    \"YOU_CANT_DO_THAT\": \"et voi tehdä sitä\"\n}\n"
  },
  {
    "path": "public/assets/locales/fr.json",
    "content": "{\n    \"ABORTED\": \"avorté\",\n    \"ABORT_CURRENT_UPLOADS?\": \"Annuler les téléchargements en cours?\",\n    \"ACTIVITY\": \"activité\",\n    \"ADVANCED\": \"avancé\",\n    \"ALL_DONE\": \"Tout est bon!\",\n    \"ALREADY_EXIST\": \"existe déjà\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Un fichier nommé \\\"{{VALUE}}\\\" a été créé\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Un dossier nommé \\\"{{VALUE}}\\\" a été créé\",\n    \"BEAUTIFUL_URL\": \"id_du_lien\",\n    \"BOOKMARK\": \"favoris\",\n    \"CAMERA\": \"appareil\",\n    \"CANCEL\": \"annuler\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"Impossible d'établir une connexion\",\n    \"CANT_LOAD_THIS_PICTURE\": \"impossible de charger cette image\",\n    \"CANT_USE_FILESYSTEM\": \"Impossible d'utiliser le système de fichiers\",\n    \"CAN_RESHARE\": \"peut repartager\",\n    \"CODE\": \"code\",\n    \"CONFIGURE\": \"configuration\",\n    \"CONFIRM_BY_TYPING\": \"confirmez en écrivant\",\n    \"CONNECT\": \"connexion\",\n    \"CONNECTION_LOST\": \"connexion perdue\",\n    \"COPIED_TO_CLIPBOARD\": \"Copié dans le presse-papier\",\n    \"CREATE_A_NEW_LINK\": \"créer un lien partagé\",\n    \"CREATE_A_TAG\": \"créer un tag\",\n    \"CURRENT\": \"en cours\",\n    \"CURRENT_UPLOAD\": \"en cours\",\n    \"CUSTOM_LINK_URL\": \"URL du lien personnalisé\",\n    \"DASHBOARD\": \"dashboard\",\n    \"DATE\": \"date\",\n    \"DISPLAY_HIDDEN_FILES\": \"afficher les fichiers cachés\",\n    \"DOESNT_MATCH\": \"ne correspond pas\",\n    \"DONE\": \"terminé\",\n    \"DOWNLOAD\": \"Télécharger\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"enregistrer les modifications?\",\n    \"DROP_HERE_TO_UPLOAD\": \"glisser-déposer pour charger vos fichiers et dossiers\",\n    \"EDITOR\": \"éditeur\",\n    \"EMBED\": \"intégrer\",\n    \"EMPTY\": \"vide\",\n    \"ENCRYPTION_KEY\": \"clé de cryptage\",\n    \"ENDPOINT\": \"endpoint\",\n    \"ERROR\": \"erreur\",\n    \"EXISTING_LINKS\": \"liens existants\",\n    \"EXPIRATION\": \"expiration\",\n    \"EXPORT_AS_{{VALUE}}\": \"exporter au format {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"les dossiers d'accès fréquents apparaîtront ici\",\n    \"HIDE_HIDDEN_FILES\": \"masquer les fichiers cachés\",\n    \"HOME\": \"accueil\",\n    \"HOSTNAME*\": \"nom d'hôte*\",\n    \"HOST_KEY\": \"clé d'hôte\",\n    \"INCORRECT_PASSWORD\": \"mot de passe incorrect\",\n    \"INFO\": \"Information\",\n    \"INTERNAL_ERROR\": \"Erreur interne\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"erreur interne: vous ne pouvez pas créer un {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"compte invalide\",\n    \"INVALID_PASSWORD\": \"mot de passe incorrect\",\n    \"LAYOUT\": \"disposition\",\n    \"LOADING\": \"chargement\",\n    \"LOCATION\": \"position\",\n    \"MISSING_DEPENDENCY\": \"dépendance manquante\",\n    \"MORE_DETAILS\": \"plus de détails\",\n    \"NAVIGATE\": \"naviguer\",\n    \"NEW_FILE\": \"Nouveau Fichier\",\n    \"NEW_FILE::SHORT\": \"Nouv. Fichier\",\n    \"NEW_FOLDER\": \"Nouveau Dossier\",\n    \"NEW_FOLDER::SHORT\": \"Nouv. Dossier\",\n    \"NO\": \"non\",\n    \"NOT_ALLOWED\": \"interdit\",\n    \"NOT_AUTHORISED\": \"autorisation manquante\",\n    \"NOT_FOUND\": \"introuvable\",\n    \"NOT_IMPLEMENTED\": \"non implémenté\",\n    \"NOT_SUPPORTED\": \"non supporté\",\n    \"NOT_VALID\": \"pas valide\",\n    \"NUMBER_OF_CONNECTIONS\": \"nombre de connexion\",\n    \"OK\": \"ok\",\n    \"ONLY_FOR_USERS\": \"uniquement pour certains utilisateurs\",\n    \"OOPS\": \"oops!\",\n    \"PASSPHRASE\": \"mot de passe de clef\",\n    \"PASSWORD\": \"mot de passe\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"le mot de passe ne peut pas être vide\",\n    \"PATH\": \"chemin\",\n    \"PERMISSION_DENIED\": \"autorisation refusée\",\n    \"PICK_A_MASTER_PASSWORD\": \"choisissez un mot de passe principal\",\n    \"PORT\": \"port\",\n    \"POWERED_BY\": \"propulsé par\",\n    \"PROPERTIES\": \"propriétés\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"protéger l'accès avec un mot de passe\",\n    \"QUICK_ACCESS\": \"accès rapide\",\n    \"REGION\": \"région\",\n    \"REMEMBER_ME\": \"se souvenir de moi\",\n    \"REMOVE\": \"supprimer\",\n    \"RENAME\": \"renommer\",\n    \"RENAME_AS\": \"renommer en tant que\",\n    \"RESTRICTIONS\": \"restrictions\",\n    \"RUNNING\": \"en cours\",\n    \"SAVE_CURRENT_FILE\": \"enregistrer le fichier actuel\",\n    \"SEARCH\": \"recherche\",\n    \"SETTINGS\": \"réglage\",\n    \"SHARE\": \"partager\",\n    \"SHARED_DRIVE\": \"disque partagé\",\n    \"SKIP_TO_CONTENT\": \"Aller au contenu\",\n    \"SORT\": \"trier\",\n    \"SORT_BY_DATE\": \"trier par date\",\n    \"SORT_BY_NAME\": \"trier par nom\",\n    \"SORT_BY_SIZE\": \"trier par taille\",\n    \"SORT_BY_TYPE\": \"trier par type\",\n    \"STARRED\": \"favoris\",\n    \"SUPPORT\": \"assistance\",\n    \"TAG\": \"tag\",\n    \"TAGS\": \"tags\",\n    \"THERE_IS_NOTHING_HERE\": \"il n'y a rien ici\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"le fichier \\\"{{VALUE}}\\\" a été supprimé\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"le fichier \\\"{{VALUE}}\\\" a été renommé\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"le lien a été copié dans le presse-papiers\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"le lien ne sera plus valide après\",\n    \"TIMEOUT\": \"trop long\",\n    \"TODO\": \"todo\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"Congestion, réessayez plus tard\",\n    \"UPLOAD\": \"importer\",\n    \"UPLOADER\": \"uploader\",\n    \"USERNAME\": \"nom d'utilisateur\",\n    \"VERSION\": \"version\",\n    \"VIEWER\": \"viewer\",\n    \"WAITING\": \"attente\",\n    \"YES\": \"oui\",\n    \"YOUR_EMAIL_ADDRESS\": \"votre adresse email\",\n    \"YOUR_FILES\": \"vos fichiers\",\n    \"YOUR_MASTER_PASSWORD\": \"votre mot de passe principal\",\n    \"YOU_CANT_DO_THAT\": \"vous ne pouvez pas faire :)\"\n}\n"
  },
  {
    "path": "public/assets/locales/gl.json",
    "content": "{\n    \"ABORTED\": \"abortada\",\n    \"ABORT_CURRENT_UPLOADS?\": \"cancelar a carga actual?\",\n    \"ACTIVITY\": \"Actividade\",\n    \"ADVANCED\": \"avanzado\",\n    \"ALL_DONE\": \"todo feito\",\n    \"ALREADY_EXIST\": \"xa existen\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Creouse un ficheiro chamado {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Creouse un cartafol chamado {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"fermoso_url\",\n    \"BOOKMARK\": \"marcador\",\n    \"CAMERA\": \"cámara\",\n    \"CANCEL\": \"cancelar\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"non pode establecer unha conexión\",\n    \"CANT_LOAD_THIS_PICTURE\": \"Non se pode cargar esta foto\",\n    \"CANT_USE_FILESYSTEM\": \"pode usar o sistema de ficheiros\",\n    \"CAN_RESHARE\": \"pode volver compartir\",\n    \"CODE\": \"código\",\n    \"CONFIGURE\": \"configurar\",\n    \"CONFIRM_BY_TYPING\": \"confirma escribindo\",\n    \"CONNECT\": \"conectar\",\n    \"CONNECTION_LOST\": \"conexión perdida\",\n    \"COPIED_TO_CLIPBOARD\": \"copiado ao portapapeis\",\n    \"CREATE_A_NEW_LINK\": \"crear unha nova ligazón\",\n    \"CREATE_A_TAG\": \"crear unha etiqueta\",\n    \"CURRENT\": \"actual\",\n    \"CURRENT_UPLOAD\": \"carga actual\",\n    \"CUSTOM_LINK_URL\": \"URL de ligazón personalizado\",\n    \"DASHBOARD\": \"panel de traballo\",\n    \"DATE\": \"data\",\n    \"DISPLAY_HIDDEN_FILES\": \"amosar ficheiros ocultos\",\n    \"DOESNT_MATCH\": \"non coincide\",\n    \"DONE\": \"feito\",\n    \"DOWNLOAD\": \"descargar\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"quere gardar os cambios\",\n    \"DROP_HERE_TO_UPLOAD\": \"solta aquí para cargar\",\n    \"EDITOR\": \"editor\",\n    \"EMBED\": \"incrustar\",\n    \"EMPTY\": \"baleiro\",\n    \"ENCRYPTION_KEY\": \"clave de cifrado\",\n    \"ENDPOINT\": \"punto final\",\n    \"ERROR\": \"erro\",\n    \"EXISTING_LINKS\": \"ligazóns existentes\",\n    \"EXPIRATION\": \"caducidade\",\n    \"EXPORT_AS_{{VALUE}}\": \"exportar como {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"Aquí móstranse os cartafoles de acceso frecuente\",\n    \"HIDE_HIDDEN_FILES\": \"ocultar ficheiros ocultos\",\n    \"HOME\": \"inicio\",\n    \"HOSTNAME*\": \"nome de host\",\n    \"HOST_KEY\": \"clave de anfitrión\",\n    \"INCORRECT_PASSWORD\": \"Contrasinal incorrecto\",\n    \"INFO\": \"información\",\n    \"INTERNAL_ERROR\": \"Erro interno\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"erro interno: non se pode crear un {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"conta non válida\",\n    \"INVALID_PASSWORD\": \"Contrasinal incorrecta\",\n    \"LAYOUT\": \"disposición\",\n    \"LOADING\": \"cargando\",\n    \"LOCATION\": \"localización\",\n    \"MISSING_DEPENDENCY\": \"falta dependencia\",\n    \"MORE_DETAILS\": \"máis detalles\",\n    \"NAVIGATE\": \"navegar\",\n    \"NEW_FILE\": \"novo ficheiro\",\n    \"NEW_FILE::SHORT\": \"novo ficheiro\",\n    \"NEW_FOLDER\": \"novo directorio\",\n    \"NEW_FOLDER::SHORT\": \"novo direct..\",\n    \"NO\": \"non\",\n    \"NOT_ALLOWED\": \"Prohibido\",\n    \"NOT_AUTHORISED\": \"non autorizado\",\n    \"NOT_FOUND\": \"non atopado\",\n    \"NOT_IMPLEMENTED\": \"non implementado\",\n    \"NOT_SUPPORTED\": \"non é compatible\",\n    \"NOT_VALID\": \"non é válido\",\n    \"NUMBER_OF_CONNECTIONS\": \"número de conexións\",\n    \"OK\": \"Ok\",\n    \"ONLY_FOR_USERS\": \"Só para usuarios\",\n    \"OOPS\": \"Oops\",\n    \"PASSPHRASE\": \"frase de contrasinal\",\n    \"PASSWORD\": \"contrasinal\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"o contrasinal non pode estar baleiro\",\n    \"PATH\": \"camiño\",\n    \"PERMISSION_DENIED\": \"Permiso denegado\",\n    \"PICK_A_MASTER_PASSWORD\": \"escolle un contrasinal principal\",\n    \"PORT\": \"Porto\",\n    \"POWERED_BY\": \"impulsado por\",\n    \"PROPERTIES\": \"propiedades\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"protexer o acceso cun contrasinal\",\n    \"QUICK_ACCESS\": \"acceso rápido\",\n    \"REGION\": \"rexión\",\n    \"REMEMBER_ME\": \"Lémbrame\",\n    \"REMOVE\": \"quitar\",\n    \"RENAME\": \"renomear\",\n    \"RENAME_AS\": \"renomear como\",\n    \"RESTRICTIONS\": \"restricións\",\n    \"RUNNING\": \"correndo\",\n    \"SAVE_CURRENT_FILE\": \"gardar o ficheiro actual\",\n    \"SEARCH\": \"buscar\",\n    \"SETTINGS\": \"configuración\",\n    \"SHARE\": \"compartir\",\n    \"SHARED_DRIVE\": \"unidade compartida\",\n    \"SKIP_TO_CONTENT\": \"Ir ao contido\",\n    \"SORT\": \"ordenar\",\n    \"SORT_BY_DATE\": \"ordenar por data\",\n    \"SORT_BY_NAME\": \"ordenar por nome\",\n    \"SORT_BY_SIZE\": \"ordenar por tamaño\",\n    \"SORT_BY_TYPE\": \"ordenar por tipo\",\n    \"STARRED\": \"destacado\",\n    \"SUPPORT\": \"apoiar\",\n    \"TAG\": \"etiqueta\",\n    \"TAGS\": \"etiqueta\",\n    \"THERE_IS_NOTHING_HERE\": \"aquí non hai nada\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"eliminouse o ficheiro {{VALUE}}\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"renomeouse o ficheiro {{VALUE}}\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"a ligazón foi copiada no portapapeis\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"a ligazón non será válida despois\",\n    \"TIMEOUT\": \"tempo de espera\",\n    \"TODO\": \"facer\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"conxestión de tráfico, ténteo de novo máis tarde\",\n    \"UPLOAD\": \"cargar\",\n    \"UPLOADER\": \"cargador\",\n    \"USERNAME\": \"nome de usuario\",\n    \"VERSION\": \"versión\",\n    \"VIEWER\": \"espectador\",\n    \"WAITING\": \"á espera\",\n    \"YES\": \"si\",\n    \"YOUR_EMAIL_ADDRESS\": \"O teu enderezo de email\",\n    \"YOUR_FILES\": \"os teus ficheiros\",\n    \"YOUR_MASTER_PASSWORD\": \"o seu contrasinal principal\",\n    \"YOU_CANT_DO_THAT\": \"Non podes facelo\"\n}\n"
  },
  {
    "path": "public/assets/locales/hr.json",
    "content": "{\n    \"ABORTED\": \"prekinut\",\n    \"ABORT_CURRENT_UPLOADS?\": \"prekinuti trenutačni prijenos?\",\n    \"ACTIVITY\": \"Aktivnost\",\n    \"ADVANCED\": \"Napredna\",\n    \"ALL_DONE\": \"sve učinjeno\",\n    \"ALREADY_EXIST\": \"već postoji\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"pod nazivom {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Stvorena je mapa pod nazivom {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"oznaka\",\n    \"CAMERA\": \"fotoaparat\",\n    \"CANCEL\": \"otkazati\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"ne može uspostaviti vezu\",\n    \"CANT_LOAD_THIS_PICTURE\": \"ne mogu učitati ovu sliku\",\n    \"CANT_USE_FILESYSTEM\": \"mogu koristiti datotečni sustav\",\n    \"CAN_RESHARE\": \"može ponovno dijeliti\",\n    \"CODE\": \"kodirati\",\n    \"CONFIGURE\": \"konfigurirati\",\n    \"CONFIRM_BY_TYPING\": \"potvrdite tipkanjem\",\n    \"CONNECT\": \"Spojiti\",\n    \"CONNECTION_LOST\": \"veza izgubljena\",\n    \"COPIED_TO_CLIPBOARD\": \"kopirano u međuspremnik\",\n    \"CREATE_A_NEW_LINK\": \"stvori novu vezu\",\n    \"CREATE_A_TAG\": \"stvoriti oznaku\",\n    \"CURRENT\": \"Trenutno\",\n    \"CURRENT_UPLOAD\": \"trenutačni prijenos\",\n    \"CUSTOM_LINK_URL\": \"prilagođeni URL veze\",\n    \"DASHBOARD\": \"kontrolna ploča\",\n    \"DATE\": \"datum\",\n    \"DISPLAY_HIDDEN_FILES\": \"prikaz skrivenih datoteka\",\n    \"DOESNT_MATCH\": \"ne podudara se\",\n    \"DONE\": \"učinio\",\n    \"DOWNLOAD\": \"preuzimanje datoteka\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"želite li spremiti promjene\",\n    \"DROP_HERE_TO_UPLOAD\": \"ispustite ovdje za učitavanje\",\n    \"EDITOR\": \"urednik\",\n    \"EMBED\": \"ugradi\",\n    \"EMPTY\": \"prazan\",\n    \"ENCRYPTION_KEY\": \"ključ za šifriranje\",\n    \"ENDPOINT\": \"krajnja točka\",\n    \"ERROR\": \"greška\",\n    \"EXISTING_LINKS\": \"postojeće poveznice\",\n    \"EXPIRATION\": \"izdisanje\",\n    \"EXPORT_AS_{{VALUE}}\": \"izvesti kao {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"Ovdje će se prikazivati ​​mape s često pristupom\",\n    \"HIDE_HIDDEN_FILES\": \"sakriti skrivene datoteke\",\n    \"HOME\": \"početna\",\n    \"HOSTNAME*\": \"hostname\",\n    \"HOST_KEY\": \"ključ računala\",\n    \"INCORRECT_PASSWORD\": \"Netočna lozinka\",\n    \"INFO\": \"Informacije\",\n    \"INTERNAL_ERROR\": \"Interna greška\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"unutarnja pogreška: ne mogu stvoriti {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"nevažeći račun\",\n    \"INVALID_PASSWORD\": \"Netočna zaporka\",\n    \"LAYOUT\": \"izgled\",\n    \"LOADING\": \"učitavanje\",\n    \"LOCATION\": \"mjesto\",\n    \"MISSING_DEPENDENCY\": \"nedostaje ovisnost\",\n    \"MORE_DETAILS\": \"više detalja\",\n    \"NAVIGATE\": \"navigaciju\",\n    \"NEW_FILE\": \"nova datoteka\",\n    \"NEW_FILE::SHORT\": \"nova datoteka\",\n    \"NEW_FOLDER\": \"novi direktorij\",\n    \"NEW_FOLDER::SHORT\": \"novi direkt..\",\n    \"NO\": \"Ne\",\n    \"NOT_ALLOWED\": \"nije dozvoljeno\",\n    \"NOT_AUTHORISED\": \"nije ovlašten\",\n    \"NOT_FOUND\": \"nije pronađeno\",\n    \"NOT_IMPLEMENTED\": \"nije implementirano\",\n    \"NOT_SUPPORTED\": \"Nije podržano\",\n    \"NOT_VALID\": \"Ne vrijedi\",\n    \"NUMBER_OF_CONNECTIONS\": \"broj priključaka\",\n    \"OK\": \"u redu\",\n    \"ONLY_FOR_USERS\": \"Samo za korisnike\",\n    \"OOPS\": \"Ups\",\n    \"PASSPHRASE\": \"šifra\",\n    \"PASSWORD\": \"lozinka\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"lozinka ne može biti prazna\",\n    \"PATH\": \"staza\",\n    \"PERMISSION_DENIED\": \"odobrenje odbijeno\",\n    \"PICK_A_MASTER_PASSWORD\": \"odaberite glavnu lozinku\",\n    \"PORT\": \"luka\",\n    \"POWERED_BY\": \"powered by\",\n    \"PROPERTIES\": \"Svojstva\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"zaštititi pristup lozinkom\",\n    \"QUICK_ACCESS\": \"brzi pristup\",\n    \"REGION\": \"regija\",\n    \"REMEMBER_ME\": \"Zapamti me\",\n    \"REMOVE\": \"ukloniti\",\n    \"RENAME\": \"preimenuj\",\n    \"RENAME_AS\": \"preimenuj kao\",\n    \"RESTRICTIONS\": \"ograničenja\",\n    \"RUNNING\": \"trčanje\",\n    \"SAVE_CURRENT_FILE\": \"spremite trenutnu datoteku\",\n    \"SEARCH\": \"traži\",\n    \"SETTINGS\": \"postavke\",\n    \"SHARE\": \"podijeli\",\n    \"SHARED_DRIVE\": \"zajednički disk\",\n    \"SKIP_TO_CONTENT\": \"Idi na sadržaj\",\n    \"SORT\": \"sortiraj\",\n    \"SORT_BY_DATE\": \"poredaj po datumu\",\n    \"SORT_BY_NAME\": \"poredaj po imenu\",\n    \"SORT_BY_SIZE\": \"poredaj po veličini\",\n    \"SORT_BY_TYPE\": \"poredati po vrsti\",\n    \"STARRED\": \"favoriti\",\n    \"SUPPORT\": \"podrška\",\n    \"TAG\": \"oznaka\",\n    \"TAGS\": \"oznake\",\n    \"THERE_IS_NOTHING_HERE\": \"tu nema ničega\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"datoteka {{VALUE}} je izbrisana\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"datoteka {{VALUE}} preimenovana je\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"veza je kopirana u međuspremnik\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"veza poslije neće biti valjana\",\n    \"TIMEOUT\": \"pauza\",\n    \"TODO\": \"napraviti\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"prometni zastoji, pokušajte ponovo kasnije\",\n    \"UPLOAD\": \"učitaj\",\n    \"UPLOADER\": \"uploader\",\n    \"USERNAME\": \"Korisničko ime\",\n    \"VERSION\": \"verzija\",\n    \"VIEWER\": \"gledatelj\",\n    \"WAITING\": \"čekanja\",\n    \"YES\": \"Da\",\n    \"YOUR_EMAIL_ADDRESS\": \"Vaša email adresa\",\n    \"YOUR_FILES\": \"vaše datoteke\",\n    \"YOUR_MASTER_PASSWORD\": \"svoju glavnu lozinku\",\n    \"YOU_CANT_DO_THAT\": \"ne možeš to učiniti\"\n}\n"
  },
  {
    "path": "public/assets/locales/hu.json",
    "content": "{\n    \"ABORTED\": \"Megszakítva\",\n    \"ABORT_CURRENT_UPLOADS?\": \"megszakítja az aktuális feltöltést?\",\n    \"ACTIVITY\": \"Tevékenység\",\n    \"ADVANCED\": \"fejlett\",\n    \"ALL_DONE\": \"minden kész\",\n    \"ALREADY_EXIST\": \"már létezik\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"A (z) {{VALUE}} nevű fájlt létrehoztuk\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Létrehozott egy {{VALUE}} nevű mappát\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"könyvjelző\",\n    \"CAMERA\": \"kamera\",\n    \"CANCEL\": \"mégse\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"nem tud kapcsolatot létesíteni\",\n    \"CANT_LOAD_THIS_PICTURE\": \"nem lehet betölteni ezt a képet\",\n    \"CANT_USE_FILESYSTEM\": \"nem használható a fájlrendszer\",\n    \"CAN_RESHARE\": \"újra megoszthatja\",\n    \"CODE\": \"kód\",\n    \"CONFIGURE\": \"Beállítás\",\n    \"CONFIRM_BY_TYPING\": \"gépeléssel erősítse meg\",\n    \"CONNECT\": \"kapcsolódás\",\n    \"CONNECTION_LOST\": \"kapcsolat megszakadt\",\n    \"COPIED_TO_CLIPBOARD\": \"a vágólapra másolták\",\n    \"CREATE_A_NEW_LINK\": \"Új link létrehozása\",\n    \"CREATE_A_TAG\": \"címke létrehozása\",\n    \"CURRENT\": \"jelenlegi\",\n    \"CURRENT_UPLOAD\": \"aktuális feltöltés\",\n    \"CUSTOM_LINK_URL\": \"egyéni link URL\",\n    \"DASHBOARD\": \"Irányítópult\",\n    \"DATE\": \"dátum\",\n    \"DISPLAY_HIDDEN_FILES\": \"rejtett fájlok megjelenítése\",\n    \"DOESNT_MATCH\": \"nem egyezik\",\n    \"DONE\": \"Kész\",\n    \"DOWNLOAD\": \"Letöltés\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"el akarja menteni a változtatásokat\",\n    \"DROP_HERE_TO_UPLOAD\": \"ide kattintva feltöltheti\",\n    \"EDITOR\": \"szerkesztő\",\n    \"EMBED\": \"beágyaz\",\n    \"EMPTY\": \"üres\",\n    \"ENCRYPTION_KEY\": \"titkosítási kulcs\",\n    \"ENDPOINT\": \"végpont\",\n    \"ERROR\": \"hiba\",\n    \"EXISTING_LINKS\": \"meglévő linkek\",\n    \"EXPIRATION\": \"lejárat\",\n    \"EXPORT_AS_{{VALUE}}\": \"exportálás mint {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"itt jelennek meg a gyakran hozzáférhető mappák\",\n    \"HIDE_HIDDEN_FILES\": \"rejtett fájlok elrejtése\",\n    \"HOME\": \"kezdőlap\",\n    \"HOSTNAME*\": \"hostname\",\n    \"HOST_KEY\": \"host kulcs\",\n    \"INCORRECT_PASSWORD\": \"hibás jelszó\",\n    \"INFO\": \"info\",\n    \"INTERNAL_ERROR\": \"Belső hiba\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"belső hiba: nem lehet létrehozni a (z) {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"Érvénytelen fiók\",\n    \"INVALID_PASSWORD\": \"Érvénytelen jelszó\",\n    \"LAYOUT\": \"elrendezés\",\n    \"LOADING\": \"betöltés\",\n    \"LOCATION\": \"elhelyezkedés\",\n    \"MISSING_DEPENDENCY\": \"hiányzik a függőség\",\n    \"MORE_DETAILS\": \"Részletek\",\n    \"NAVIGATE\": \"navigálás\",\n    \"NEW_FILE\": \"új fájl\",\n    \"NEW_FILE::SHORT\": \"új fájl\",\n    \"NEW_FOLDER\": \"új könyvtár\",\n    \"NEW_FOLDER::SHORT\": \"új könyvtár\",\n    \"NO\": \"nem\",\n    \"NOT_ALLOWED\": \"nem megengedett\",\n    \"NOT_AUTHORISED\": \"nem engedélyezett\",\n    \"NOT_FOUND\": \"nem található\",\n    \"NOT_IMPLEMENTED\": \"nincs implementálva\",\n    \"NOT_SUPPORTED\": \"Nem támogatott\",\n    \"NOT_VALID\": \"nem érvényes\",\n    \"NUMBER_OF_CONNECTIONS\": \"csatlakozások száma\",\n    \"OK\": \"rendben\",\n    \"ONLY_FOR_USERS\": \"Csak a felhasználók számára\",\n    \"OOPS\": \"Hoppá\",\n    \"PASSPHRASE\": \"jelmondat\",\n    \"PASSWORD\": \"Jelszó\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"a jelszó nem lehet üres\",\n    \"PATH\": \"pálya\",\n    \"PERMISSION_DENIED\": \"hozzáférés megtagadva\",\n    \"PICK_A_MASTER_PASSWORD\": \"válasszon egy fő jelszót\",\n    \"PORT\": \"kikötő\",\n    \"POWERED_BY\": \"meghajtó\",\n    \"PROPERTIES\": \"tulajdonságok\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"védje a hozzáférést jelszóval\",\n    \"QUICK_ACCESS\": \"gyors hozzáférés\",\n    \"REGION\": \"vidék\",\n    \"REMEMBER_ME\": \"Emlékezz rám\",\n    \"REMOVE\": \"Törlés\",\n    \"RENAME\": \"átnevezés\",\n    \"RENAME_AS\": \"átnevezés erre\",\n    \"RESTRICTIONS\": \"korlátozások\",\n    \"RUNNING\": \"futás\",\n    \"SAVE_CURRENT_FILE\": \"mentse az aktuális fájlt\",\n    \"SEARCH\": \"keresés\",\n    \"SETTINGS\": \"beállítások\",\n    \"SHARE\": \"megosztás\",\n    \"SHARED_DRIVE\": \"meghajtó\",\n    \"SKIP_TO_CONTENT\": \"Ugrás a tartalomhoz\",\n    \"SORT\": \"rendezés\",\n    \"SORT_BY_DATE\": \"rendezés dátum szerint\",\n    \"SORT_BY_NAME\": \"név szerinti rendezés\",\n    \"SORT_BY_SIZE\": \"méret szerinti rendezés\",\n    \"SORT_BY_TYPE\": \"típus szerint rendezni\",\n    \"STARRED\": \"csillagozott\",\n    \"SUPPORT\": \"támogatás\",\n    \"TAG\": \"címke\",\n    \"TAGS\": \"címkék\",\n    \"THERE_IS_NOTHING_HERE\": \"nincs itt semmi\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"a (z) {{VALUE}} fájlt törölték\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"a (z) {{VALUE}} fájlt átnevezték\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"a linket a vágólapra másolták\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"a link nem lesz érvényes utána\",\n    \"TIMEOUT\": \"időtúllépés\",\n    \"TODO\": \"csinálni\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"forgalmi torlódások, próbálkozzon később újra\",\n    \"UPLOAD\": \"feltöltés\",\n    \"UPLOADER\": \"feltöltő\",\n    \"USERNAME\": \"felhasználónév\",\n    \"VERSION\": \"verzió\",\n    \"VIEWER\": \"néző\",\n    \"WAITING\": \"Várakozik\",\n    \"YES\": \"Igen\",\n    \"YOUR_EMAIL_ADDRESS\": \"az email címed\",\n    \"YOUR_FILES\": \"fájljaid\",\n    \"YOUR_MASTER_PASSWORD\": \"a fő jelszavad\",\n    \"YOU_CANT_DO_THAT\": \"Nem hajtható végre\"\n}\n"
  },
  {
    "path": "public/assets/locales/id.json",
    "content": "{\n    \"ABORTED\": \"dibatalkan\",\n    \"ABORT_CURRENT_UPLOADS?\": \"batalkan unggahan saat ini?\",\n    \"ACTIVITY\": \"Aktivitas\",\n    \"ADVANCED\": \"lanjutan\",\n    \"ALL_DONE\": \"semua selesai\",\n    \"ALREADY_EXIST\": \"sudah ada\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"File bernama {{VALUE}} telah dibuat\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Folder bernama {{VALUE}} telah dibuat\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"penanda\",\n    \"CAMERA\": \"kamera\",\n    \"CANCEL\": \"batalkan\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"tidak dapat membuat koneksi\",\n    \"CANT_LOAD_THIS_PICTURE\": \"tidak dapat memuat gambar ini\",\n    \"CANT_USE_FILESYSTEM\": \"idak bisa gunakan sistem file\",\n    \"CAN_RESHARE\": \"dapat membagikan ulang\",\n    \"CODE\": \"kode\",\n    \"CONFIGURE\": \"konfigurasikan\",\n    \"CONFIRM_BY_TYPING\": \"konfirmasi dengan mengetik\",\n    \"CONNECT\": \"hubungkan\",\n    \"CONNECTION_LOST\": \"koneksi terputus\",\n    \"COPIED_TO_CLIPBOARD\": \"disalin ke papan klip\",\n    \"CREATE_A_NEW_LINK\": \"buat tautan baru\",\n    \"CREATE_A_TAG\": \"buat tag\",\n    \"CURRENT\": \"terkini\",\n    \"CURRENT_UPLOAD\": \"unggahan saat ini\",\n    \"CUSTOM_LINK_URL\": \"URL tautan khusus\",\n    \"DASHBOARD\": \"dasbor\",\n    \"DATE\": \"tanggal\",\n    \"DISPLAY_HIDDEN_FILES\": \"tampilkan file tersembunyi\",\n    \"DOESNT_MATCH\": \"tidak cocok\",\n    \"DONE\": \"selesai\",\n    \"DOWNLOAD\": \"unduh\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"Simpan perubahan?\",\n    \"DROP_HERE_TO_UPLOAD\": \"turun di sini untuk mengunggah\",\n    \"EDITOR\": \"editor\",\n    \"EMBED\": \"sematkan\",\n    \"EMPTY\": \"kosong\",\n    \"ENCRYPTION_KEY\": \"kunci enkripsi\",\n    \"ENDPOINT\": \"titik akhir\",\n    \"ERROR\": \"kesalahan\",\n    \"EXISTING_LINKS\": \"tautan yang ada\",\n    \"EXPIRATION\": \"kedaluwarsa\",\n    \"EXPORT_AS_{{VALUE}}\": \"ekspor sebagai {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"folder yang sering diakses akan ditampilkan di sini\",\n    \"HIDE_HIDDEN_FILES\": \"sembunyikan file yang tersembunyi\",\n    \"HOME\": \"beranda\",\n    \"HOSTNAME*\": \"nama host\",\n    \"HOST_KEY\": \"kunci host\",\n    \"INCORRECT_PASSWORD\": \"kata kunci Salah\",\n    \"INFO\": \"info\",\n    \"INTERNAL_ERROR\": \"Internal error\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"kesalahan internal: tidak dapat membuat {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"akun tidak berlaku\",\n    \"INVALID_PASSWORD\": \"kata sandi salah\",\n    \"LAYOUT\": \"tata letak\",\n    \"LOADING\": \"memuat\",\n    \"LOCATION\": \"lokasi\",\n    \"MISSING_DEPENDENCY\": \"hilang ketergantungan\",\n    \"MORE_DETAILS\": \"lebih detail\",\n    \"NAVIGATE\": \"navigasi\",\n    \"NEW_FILE\": \"file baru\",\n    \"NEW_FILE::SHORT\": \"file baru\",\n    \"NEW_FOLDER\": \"direktori baru\",\n    \"NEW_FOLDER::SHORT\": \"direktori baru\",\n    \"NO\": \"tidak\",\n    \"NOT_ALLOWED\": \"tidak diizinkan\",\n    \"NOT_AUTHORISED\": \"tidak resmi\",\n    \"NOT_FOUND\": \"tidak ditemukan\",\n    \"NOT_IMPLEMENTED\": \"tidak diterapkan\",\n    \"NOT_SUPPORTED\": \"tidak didukung\",\n    \"NOT_VALID\": \"tidak valid\",\n    \"NUMBER_OF_CONNECTIONS\": \"jumlah koneksi\",\n    \"OK\": \"baik\",\n    \"ONLY_FOR_USERS\": \"Hanya untuk pengguna\",\n    \"OOPS\": \"Ups\",\n    \"PASSPHRASE\": \"frasa sandi\",\n    \"PASSWORD\": \"kata sandi\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"kata sandi tidak boleh kosong\",\n    \"PATH\": \"jalan\",\n    \"PERMISSION_DENIED\": \"izin ditolak\",\n    \"PICK_A_MASTER_PASSWORD\": \"pilih kata sandi utama\",\n    \"PORT\": \"port\",\n    \"POWERED_BY\": \"dipersembahkan oleh\",\n    \"PROPERTIES\": \"properti\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"lindungi akses dengan kata sandi\",\n    \"QUICK_ACCESS\": \"akses cepat\",\n    \"REGION\": \"wilayah\",\n    \"REMEMBER_ME\": \"ingat saya\",\n    \"REMOVE\": \"hapus\",\n    \"RENAME\": \"ubah nama\",\n    \"RENAME_AS\": \"ubah nama sebagai\",\n    \"RESTRICTIONS\": \"pembatasan\",\n    \"RUNNING\": \"berjalan\",\n    \"SAVE_CURRENT_FILE\": \"simpan file saat ini\",\n    \"SEARCH\": \"Cari\",\n    \"SETTINGS\": \"pengaturan\",\n    \"SHARE\": \"bagikan\",\n    \"SHARED_DRIVE\": \"drive bersama\",\n    \"SKIP_TO_CONTENT\": \"Lewati ke konten\",\n    \"SORT\": \"urutkan\",\n    \"SORT_BY_DATE\": \"urutkan berdasarkan tanggal\",\n    \"SORT_BY_NAME\": \"diurutkan berdasarkan nama\",\n    \"SORT_BY_SIZE\": \"urutkan berdasarkan ukuran\",\n    \"SORT_BY_TYPE\": \"urutkan berdasarkan jenis\",\n    \"STARRED\": \"berbintang\",\n    \"SUPPORT\": \"dukung\",\n    \"TAG\": \"tag\",\n    \"TAGS\": \"tag\",\n    \"THERE_IS_NOTHING_HERE\": \"tidak ada apa-apa di sini\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"file {{VALUE}} telah dihapus\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"file {{VALUE}} diganti namanya\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"tautannya disalin di clipboard\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"tautan tidak akan valid setelah\",\n    \"TIMEOUT\": \"waktu habis\",\n    \"TODO\": \"melakukan\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"kemacetan lalu lintas, coba lagi nanti\",\n    \"UPLOAD\": \"kirim\",\n    \"UPLOADER\": \"pengunggah\",\n    \"USERNAME\": \"nama pengguna\",\n    \"VERSION\": \"versi\",\n    \"VIEWER\": \"penonton\",\n    \"WAITING\": \"menunggu\",\n    \"YES\": \"Iya\",\n    \"YOUR_EMAIL_ADDRESS\": \"Alamat email anda\",\n    \"YOUR_FILES\": \"file Anda\",\n    \"YOUR_MASTER_PASSWORD\": \"kata sandi utama Anda\",\n    \"YOU_CANT_DO_THAT\": \"kamu tidak bisa melakukan itu\"\n}\n"
  },
  {
    "path": "public/assets/locales/index.js",
    "content": "import rxjs from \"../lib/rx.js\";\nimport ajax from \"../lib/ajax.js\";\nimport { join } from \"../lib/path.js\";\n\nlet LNG = {};\n\nexport default function t(str = \"\", replacementString, requestedKey) {\n    const calculatedKey = str.toUpperCase()\n        .replace(/ /g, \"_\")\n        .replace(/[^a-zA-Z0-9\\-\\_\\*\\{\\}\\?]/g, \"\")\n        .replace(/\\_+$/, \"\");\n\n    const value = requestedKey === undefined\n        ? LNG && LNG[calculatedKey]\n        : LNG && LNG[requestedKey];\n\n    return reformat(\n        value || str || \"\",\n        str,\n    ).replace(\"{{VALUE}}\", replacementString);\n}\n\nexport async function init() {\n    let selectedLanguage = \"en\";\n    switch (navigator.language) {\n    case \"zh-TW\":\n        selectedLanguage = \"zh_tw\";\n        break;\n    default:\n        const userLanguage = navigator.language.split(\"-\")[0] || \"\";\n        const idx = [\n            \"az\", \"be\", \"bg\", \"ca\", \"cs\", \"da\", \"de\", \"el\", \"es\", \"et\",\n            \"eu\", \"fi\", \"fr\", \"gl\", \"hr\", \"hu\", \"id\", \"is\", \"it\", \"ja\",\n            \"ka\", \"ko\", \"lt\", \"lv\", \"mn\", \"nb\", \"nl\", \"no\", \"pl\", \"pt\",\n            \"ro\", \"ru\", \"sk\", \"sl\", \"sr\", \"sv\", \"th\", \"tr\", \"uk\", \"vi\",\n            \"zh\",\n        ].indexOf(userLanguage);\n        if (idx !== -1) {\n            selectedLanguage = userLanguage;\n        }\n    }\n    if (selectedLanguage === \"en\") {\n        return Promise.resolve();\n    }\n    return ajax({\n        url: join(import.meta.url, selectedLanguage + \".json\"),\n    }).pipe(rxjs.tap(({ responseHeaders, response }) => {\n        const contentType = responseHeaders[\"content-type\"].trim();\n        if (contentType === \"application/json\") {\n            LNG = JSON.parse(response);\n            return;\n        }\n        throw new Error(`wrong content type '${contentType}'`);\n    })).toPromise();\n}\n\nfunction reformat(translated, initial) {\n    if (initial[0] && initial[0].toLowerCase() === initial[0]) {\n        return translated || \"\";\n    }\n    return (translated[0] && translated[0].toUpperCase() + translated.substring(1)) || \"\";\n}\n"
  },
  {
    "path": "public/assets/locales/is.json",
    "content": "{\n    \"ABORTED\": \"Hætt við\",\n    \"ABORT_CURRENT_UPLOADS?\": \"hætta við núverandi upphleðslu?\",\n    \"ACTIVITY\": \"Virkni\",\n    \"ADVANCED\": \"Ítarlegt\",\n    \"ALL_DONE\": \"allt búið\",\n    \"ALREADY_EXIST\": \"nú þegar til\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Skrá sem heitir {{VALUE}} var búin til\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Mappa sem hét {{VALUE}} var búin til\",\n    \"BEAUTIFUL_URL\": \"fallegur_url\",\n    \"BOOKMARK\": \"bókamerki\",\n    \"CAMERA\": \"myndavél\",\n    \"CANCEL\": \"hætta við\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"getur ekki komið á tengingu\",\n    \"CANT_LOAD_THIS_PICTURE\": \"get ekki hlaðið þessa mynd\",\n    \"CANT_USE_FILESYSTEM\": \"ekki hægt að nota skráarkerfi\",\n    \"CAN_RESHARE\": \"getur deilt á ný\",\n    \"CODE\": \"kóða\",\n    \"CONFIGURE\": \"stilla\",\n    \"CONFIRM_BY_TYPING\": \"staðfesta með því að slá inn\",\n    \"CONNECT\": \"tengja\",\n    \"CONNECTION_LOST\": \"tenging rofin\",\n    \"COPIED_TO_CLIPBOARD\": \"afritað á klemmuspjald\",\n    \"CREATE_A_NEW_LINK\": \"búa til nýjan hlekk\",\n    \"CREATE_A_TAG\": \"búa til merki\",\n    \"CURRENT\": \"núverandi\",\n    \"CURRENT_UPLOAD\": \"núverandi upp\",\n    \"CUSTOM_LINK_URL\": \"sérsniðin tengil slóð\",\n    \"DASHBOARD\": \"mælaborð\",\n    \"DATE\": \"dagsetning\",\n    \"DISPLAY_HIDDEN_FILES\": \"birta faldar skrár\",\n    \"DOESNT_MATCH\": \"passar ekki\",\n    \"DONE\": \"gert\",\n    \"DOWNLOAD\": \"hala niður\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"viltu vista breytingarnar\",\n    \"DROP_HERE_TO_UPLOAD\": \"slepptu hér til að hlaða inn\",\n    \"EDITOR\": \"ritstjóri\",\n    \"EMBED\": \"fella inn\",\n    \"EMPTY\": \"tómt\",\n    \"ENCRYPTION_KEY\": \"dulkóðunarlykill\",\n    \"ENDPOINT\": \"endapunktur\",\n    \"ERROR\": \"villa\",\n    \"EXISTING_LINKS\": \"núverandi tenglar\",\n    \"EXPIRATION\": \"gildistími\",\n    \"EXPORT_AS_{{VALUE}}\": \"flytja út sem {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"oft birtast möppur hér\",\n    \"HIDE_HIDDEN_FILES\": \"fela faldar skrár\",\n    \"HOME\": \"heimasíða\",\n    \"HOSTNAME*\": \"gestgjafanafn\",\n    \"HOST_KEY\": \"hýsillykill\",\n    \"INCORRECT_PASSWORD\": \"Rangt lykilorð\",\n    \"INFO\": \"upplýsingar\",\n    \"INTERNAL_ERROR\": \"Innri villa\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"innri villa: getur ekki búið til {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"ógildur reikningur\",\n    \"INVALID_PASSWORD\": \"ógilt lykilorð\",\n    \"LAYOUT\": \"útlit\",\n    \"LOADING\": \"hleður\",\n    \"LOCATION\": \"staðsetningu\",\n    \"MISSING_DEPENDENCY\": \"vantar ósjálfstæði\",\n    \"MORE_DETAILS\": \"meiri upplýsingar\",\n    \"NAVIGATE\": \"sigla\",\n    \"NEW_FILE\": \"ný skjal\",\n    \"NEW_FILE::SHORT\": \"ný skjal\",\n    \"NEW_FOLDER\": \"ný skrá\",\n    \"NEW_FOLDER::SHORT\": \"ný skrá\",\n    \"NO\": \"nei\",\n    \"NOT_ALLOWED\": \"ekki leyft\",\n    \"NOT_AUTHORISED\": \"ekki heimild\",\n    \"NOT_FOUND\": \"ekki fundið\",\n    \"NOT_IMPLEMENTED\": \"ekki hrint í framkvæmd\",\n    \"NOT_SUPPORTED\": \"ekki stutt\",\n    \"NOT_VALID\": \"ekki gilt\",\n    \"NUMBER_OF_CONNECTIONS\": \"fjöldi tenginga\",\n    \"OK\": \"allt í lagi\",\n    \"ONLY_FOR_USERS\": \"Aðeins fyrir notendur\",\n    \"OOPS\": \"Úps\",\n    \"PASSPHRASE\": \"aðgangsorð\",\n    \"PASSWORD\": \"lykilorð\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"lykilorð getur ekki verið tómt\",\n    \"PATH\": \"leið\",\n    \"PERMISSION_DENIED\": \"leyfi hafnað\",\n    \"PICK_A_MASTER_PASSWORD\": \"veldu aðal lykilorð\",\n    \"PORT\": \"höfn\",\n    \"POWERED_BY\": \"Knúið af\",\n    \"PROPERTIES\": \"eiginleikar\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"vernda aðgang með lykilorði\",\n    \"QUICK_ACCESS\": \"hraður aðgangur\",\n    \"REGION\": \"svæði\",\n    \"REMEMBER_ME\": \"mundu eftir mér\",\n    \"REMOVE\": \"eyða\",\n    \"RENAME\": \"endurnefna\",\n    \"RENAME_AS\": \"endurnefna sem\",\n    \"RESTRICTIONS\": \"takmarkanir\",\n    \"RUNNING\": \"í gangi\",\n    \"SAVE_CURRENT_FILE\": \"vista núverandi skrá\",\n    \"SEARCH\": \"leit\",\n    \"SETTINGS\": \"stillingar\",\n    \"SHARE\": \"deila\",\n    \"SHARED_DRIVE\": \"deildrifið\",\n    \"SKIP_TO_CONTENT\": \"Fara í efni\",\n    \"SORT\": \"raða\",\n    \"SORT_BY_DATE\": \"raða eftir dagsetningu\",\n    \"SORT_BY_NAME\": \"raða eftir nafni\",\n    \"SORT_BY_SIZE\": \"raða eftir stærð\",\n    \"SORT_BY_TYPE\": \"raða eftir tegund\",\n    \"STARRED\": \"merkt\",\n    \"SUPPORT\": \"stuðning\",\n    \"TAG\": \"merki\",\n    \"TAGS\": \"merki\",\n    \"THERE_IS_NOTHING_HERE\": \"það er ekkert hérna\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"skránni {{VALUE}} var eytt\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"var endurnefnt skráin {{VALUE}}\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"hlekkurinn var afritaður á klemmuspjaldið\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"hlekkurinn gildir ekki eftir\",\n    \"TIMEOUT\": \"Hlé\",\n    \"TODO\": \"að gera\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"umferðaröngþveiti, reyndu aftur seinna\",\n    \"UPLOAD\": \"hleð upp\",\n    \"UPLOADER\": \"upphleðslutæki\",\n    \"USERNAME\": \"notandanafn\",\n    \"VERSION\": \"útgáfa\",\n    \"VIEWER\": \"áhorfandi\",\n    \"WAITING\": \"að bíða\",\n    \"YES\": \"Já\",\n    \"YOUR_EMAIL_ADDRESS\": \"Netfangið þitt\",\n    \"YOUR_FILES\": \"skjöl þín\",\n    \"YOUR_MASTER_PASSWORD\": \"aðal lykilorð þitt\",\n    \"YOU_CANT_DO_THAT\": \"þú getur ekki gert það\"\n}\n"
  },
  {
    "path": "public/assets/locales/it.json",
    "content": "{\n    \"ABORTED\": \"abortito\",\n    \"ABORT_CURRENT_UPLOADS?\": \"interrompere il caricamento corrente?\",\n    \"ACTIVITY\": \"Attività\",\n    \"ADVANCED\": \"Avanzato\",\n    \"ALL_DONE\": \"tutto fatto\",\n    \"ALREADY_EXIST\": \"esistono già\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"È stato creato un file chiamato {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"È stata creata una cartella denominata {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"segnalibro\",\n    \"CAMERA\": \"telecamera\",\n    \"CANCEL\": \"Annulla\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"impossibile stabilire una connessione\",\n    \"CANT_LOAD_THIS_PICTURE\": \"non riesco a caricare questa immagine\",\n    \"CANT_USE_FILESYSTEM\": \"impossibile utilizzare il filesystem\",\n    \"CAN_RESHARE\": \"può ricondividere\",\n    \"CODE\": \"codice\",\n    \"CONFIGURE\": \"configura\",\n    \"CONFIRM_BY_TYPING\": \"confermare digitando\",\n    \"CONNECT\": \"Collegare\",\n    \"CONNECTION_LOST\": \"connessione persa\",\n    \"COPIED_TO_CLIPBOARD\": \"copiato negli appunti\",\n    \"CREATE_A_NEW_LINK\": \"crea un nuovo link\",\n    \"CREATE_A_TAG\": \"crea un tag\",\n    \"CURRENT\": \"attuale\",\n    \"CURRENT_UPLOAD\": \"caricamento in corso\",\n    \"CUSTOM_LINK_URL\": \"URL del link personalizzato\",\n    \"DASHBOARD\": \"pannello di controllo\",\n    \"DATE\": \"Data\",\n    \"DISPLAY_HIDDEN_FILES\": \"mostra file nascosti\",\n    \"DOESNT_MATCH\": \"non corrisponde\",\n    \"DONE\": \"fatto\",\n    \"DOWNLOAD\": \"Scarica\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"vuoi salvare le modifiche\",\n    \"DROP_HERE_TO_UPLOAD\": \"rilascia qui per caricare\",\n    \"EDITOR\": \"editore\",\n    \"EMBED\": \"incorpora\",\n    \"EMPTY\": \"vuoto\",\n    \"ENCRYPTION_KEY\": \"chiave di crittografia\",\n    \"ENDPOINT\": \"endpoint\",\n    \"ERROR\": \"errore\",\n    \"EXISTING_LINKS\": \"link esistenti\",\n    \"EXPIRATION\": \"scadenza\",\n    \"EXPORT_AS_{{VALUE}}\": \"esporta come {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"le cartelle di accesso frequente verranno visualizzate qui\",\n    \"HIDE_HIDDEN_FILES\": \"nascondi file nascosti\",\n    \"HOME\": \"home\",\n    \"HOSTNAME*\": \"Nome host*\",\n    \"HOST_KEY\": \"chiave host\",\n    \"INCORRECT_PASSWORD\": \"password errata\",\n    \"INFO\": \"Informazioni\",\n    \"INTERNAL_ERROR\": \"Errore interno\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"errore interno: impossibile creare un {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"account non valido\",\n    \"INVALID_PASSWORD\": \"password non valida\",\n    \"LAYOUT\": \"layout\",\n    \"LOADING\": \"caricamento\",\n    \"LOCATION\": \"Posizione\",\n    \"MISSING_DEPENDENCY\": \"dipendenza mancante\",\n    \"MORE_DETAILS\": \"più dettagli\",\n    \"NAVIGATE\": \"navigare\",\n    \"NEW_FILE\": \"nuovo file\",\n    \"NEW_FILE::SHORT\": \"nuovo file\",\n    \"NEW_FOLDER\": \"nuova directory\",\n    \"NEW_FOLDER::SHORT\": \"nuova directory\",\n    \"NO\": \"no\",\n    \"NOT_ALLOWED\": \"non autorizzato\",\n    \"NOT_AUTHORISED\": \"non autorizzato\",\n    \"NOT_FOUND\": \"non trovato\",\n    \"NOT_IMPLEMENTED\": \"non implementato\",\n    \"NOT_SUPPORTED\": \"non supportato\",\n    \"NOT_VALID\": \"non valido\",\n    \"NUMBER_OF_CONNECTIONS\": \"numero di connessioni\",\n    \"OK\": \"ok\",\n    \"ONLY_FOR_USERS\": \"Solo per utenti\",\n    \"OOPS\": \"Ops\",\n    \"PASSPHRASE\": \"frase d'accesso\",\n    \"PASSWORD\": \"parola d'ordine\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"la password non può essere vuota\",\n    \"PATH\": \"sentiero\",\n    \"PERMISSION_DENIED\": \"permesso negato\",\n    \"PICK_A_MASTER_PASSWORD\": \"scegli una password principale\",\n    \"PORT\": \"porta\",\n    \"POWERED_BY\": \"offerto da\",\n    \"PROPERTIES\": \"proprietà\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"proteggere l'accesso con una password\",\n    \"QUICK_ACCESS\": \"accesso rapido\",\n    \"REGION\": \"regione\",\n    \"REMEMBER_ME\": \"Ricordati di me\",\n    \"REMOVE\": \"rimuovere\",\n    \"RENAME\": \"rinomina\",\n    \"RENAME_AS\": \"rinomina come\",\n    \"RESTRICTIONS\": \"restrizioni\",\n    \"RUNNING\": \"attivo\",\n    \"SAVE_CURRENT_FILE\": \"salva il file corrente\",\n    \"SEARCH\": \"cerca\",\n    \"SETTINGS\": \"impostazioni\",\n    \"SHARE\": \"condividi\",\n    \"SHARED_DRIVE\": \"unità condivisa\",\n    \"SKIP_TO_CONTENT\": \"Vai al contenuto\",\n    \"SORT\": \"ordina\",\n    \"SORT_BY_DATE\": \"ordinare per data\",\n    \"SORT_BY_NAME\": \"ordina per nome\",\n    \"SORT_BY_SIZE\": \"ordina per dimensione\",\n    \"SORT_BY_TYPE\": \"ordina per tipo\",\n    \"STARRED\": \"preferiti\",\n    \"SUPPORT\": \"supporto\",\n    \"TAG\": \"tag\",\n    \"TAGS\": \"tag\",\n    \"THERE_IS_NOTHING_HERE\": \"non c'è niente qui\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"il file {{VALUE}} è stato eliminato\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"il file {{VALUE}} è stato rinominato\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"il collegamento è stato copiato negli appunti\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"il link non sarà valido dopo\",\n    \"TIMEOUT\": \"tempo scaduto\",\n    \"TODO\": \"fare\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"congestione del traffico, riprovare più tardi\",\n    \"UPLOAD\": \"carica\",\n    \"UPLOADER\": \"uploader\",\n    \"USERNAME\": \"nome utente\",\n    \"VERSION\": \"versione\",\n    \"VIEWER\": \"spettatore\",\n    \"WAITING\": \"in attesa\",\n    \"YES\": \"sì\",\n    \"YOUR_EMAIL_ADDRESS\": \"Il tuo indirizzo email\",\n    \"YOUR_FILES\": \"i tuoi file\",\n    \"YOUR_MASTER_PASSWORD\": \"la tua password principale\",\n    \"YOU_CANT_DO_THAT\": \"non puoi farlo\"\n}\n"
  },
  {
    "path": "public/assets/locales/ja.json",
    "content": "{\n    \"ABORTED\": \"中止\",\n    \"ABORT_CURRENT_UPLOADS?\": \"アップロードを中止しますか？\",\n    \"ACTIVITY\": \"アクティビティ\",\n    \"ADVANCED\": \"詳細\",\n    \"ALL_DONE\": \"全て完了\",\n    \"ALREADY_EXIST\": \"すでに存在しています\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"ファイル {{VALUE}} が作成されました\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"フォルダ {{VALUE}} が作成されました\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"ブックマーク\",\n    \"CAMERA\": \"カメラ\",\n    \"CANCEL\": \"キャンセル\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"接続を確立できませんでした\",\n    \"CANT_LOAD_THIS_PICTURE\": \"この画像を読み込むことができません\",\n    \"CANT_USE_FILESYSTEM\": \"ファイルシステムを使用できません\",\n    \"CAN_RESHARE\": \"再共有の許可\",\n    \"CODE\": \"コード\",\n    \"CONFIGURE\": \"設定\",\n    \"CONFIRM_BY_TYPING\": \"入力して確認\",\n    \"CONNECT\": \"接続\",\n    \"CONNECTION_LOST\": \"接続が失われました\",\n    \"COPIED_TO_CLIPBOARD\": \"クリップボードにコピーしました\",\n    \"CREATE_A_NEW_LINK\": \"新しいリンクを作成\",\n    \"CREATE_A_TAG\": \"タグを作成\",\n    \"CURRENT\": \"現在\",\n    \"CURRENT_UPLOAD\": \"アップロード中\",\n    \"CUSTOM_LINK_URL\": \"カスタムリンクURL\",\n    \"DASHBOARD\": \"ダッシュボード\",\n    \"DATE\": \"日付\",\n    \"DISPLAY_HIDDEN_FILES\": \"隠しファイルを表示\",\n    \"DOESNT_MATCH\": \"一致しません\",\n    \"DONE\": \"完了しました\",\n    \"DOWNLOAD\": \"ダウンロード\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"保存しますか？\",\n    \"DROP_HERE_TO_UPLOAD\": \"ここにドロップしてアップロード\",\n    \"EDITOR\": \"編集\",\n    \"EMBED\": \"埋め込む\",\n    \"EMPTY\": \"空\",\n    \"ENCRYPTION_KEY\": \"暗号化キー\",\n    \"ENDPOINT\": \"エンドポイント\",\n    \"ERROR\": \"エラー\",\n    \"EXISTING_LINKS\": \"既存のリンク\",\n    \"EXPIRATION\": \"有効期限\",\n    \"EXPORT_AS_{{VALUE}}\": \"{{VALUE}}としてエクスポート\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"頻繁にアクセスするフォルダがここに表示されます\",\n    \"HIDE_HIDDEN_FILES\": \"隠しファイルを非表示\",\n    \"HOME\": \"ホーム\",\n    \"HOSTNAME*\": \"ホスト名\",\n    \"HOST_KEY\": \"ホストキー\",\n    \"INCORRECT_PASSWORD\": \"パスワードが正しくありません\",\n    \"INFO\": \"情報\",\n    \"INTERNAL_ERROR\": \"内部エラー\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"内部エラー：{{VALUE}}を作成できません\",\n    \"INVALID_ACCOUNT\": \"利用できないアカウント\",\n    \"INVALID_PASSWORD\": \"利用できないパスワード\",\n    \"LAYOUT\": \"レイアウト\",\n    \"LOADING\": \"ローディング\",\n    \"LOCATION\": \"場所\",\n    \"MISSING_DEPENDENCY\": \"依存関係が不足しています\",\n    \"MORE_DETAILS\": \"詳細\",\n    \"NAVIGATE\": \"移動\",\n    \"NEW_FILE\": \"新しいファイル\",\n    \"NEW_FILE::SHORT\": \"新しいファイル\",\n    \"NEW_FOLDER\": \"新しいディレクトリ\",\n    \"NEW_FOLDER::SHORT\": \"新しいディレクトリ\",\n    \"NO\": \"いいえ\",\n    \"NOT_ALLOWED\": \"許可されていません\",\n    \"NOT_AUTHORISED\": \"認証されていません\",\n    \"NOT_FOUND\": \"見つかりません\",\n    \"NOT_IMPLEMENTED\": \"実装されていません\",\n    \"NOT_SUPPORTED\": \"サポートされていません\",\n    \"NOT_VALID\": \"有効ではありません\",\n    \"NUMBER_OF_CONNECTIONS\": \"接続数\",\n    \"OK\": \"OK\",\n    \"ONLY_FOR_USERS\": \"ユーザー限定\",\n    \"OOPS\": \"Oops\",\n    \"PASSPHRASE\": \"パスフレーズ\",\n    \"PASSWORD\": \"パスワード\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"パスワードは空にできません\",\n    \"PATH\": \"パス\",\n    \"PERMISSION_DENIED\": \"アクセスが拒否されました\",\n    \"PICK_A_MASTER_PASSWORD\": \"マスターパスワードを設定\",\n    \"PORT\": \"ポート\",\n    \"POWERED_BY\": \"Powered by\",\n    \"PROPERTIES\": \"プロパティ\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"パスワードでアクセスを保護する\",\n    \"QUICK_ACCESS\": \"クイックアクセス\",\n    \"REGION\": \"領域\",\n    \"REMEMBER_ME\": \"記憶する\",\n    \"REMOVE\": \"削除\",\n    \"RENAME\": \"名前を変更\",\n    \"RENAME_AS\": \"次の名前に変更\",\n    \"RESTRICTIONS\": \"制限\",\n    \"RUNNING\": \"実行中\",\n    \"SAVE_CURRENT_FILE\": \"このファイルを保存\",\n    \"SEARCH\": \"検索\",\n    \"SETTINGS\": \"設定\",\n    \"SHARE\": \"シェア\",\n    \"SHARED_DRIVE\": \"共有ドライブ\",\n    \"SKIP_TO_CONTENT\": \"コンテンツにスキップ\",\n    \"SORT\": \"並べ替え\",\n    \"SORT_BY_DATE\": \"日付順\",\n    \"SORT_BY_NAME\": \"名前順\",\n    \"SORT_BY_SIZE\": \"サイズ順\",\n    \"SORT_BY_TYPE\": \"種類順\",\n    \"STARRED\": \"スター付き\",\n    \"SUPPORT\": \"サポート\",\n    \"TAG\": \"タグ\",\n    \"TAGS\": \"タグ\",\n    \"THERE_IS_NOTHING_HERE\": \"何もありません\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"ファイル {{VALUE}} が削除されました\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"ファイル {{VALUE}} の名前が変更されました\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"リンクがクリップボードにコピーされました\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"リンクはその後無効になります\",\n    \"TIMEOUT\": \"タイムアウト\",\n    \"TODO\": \"Todo\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"しばらくしてからもう一度お試しください\",\n    \"UPLOAD\": \"アップロード\",\n    \"UPLOADER\": \"アップロー\",\n    \"USERNAME\": \"ユーザー名\",\n    \"VERSION\": \"バージョン\",\n    \"VIEWER\": \"閲覧\",\n    \"WAITING\": \"待機中\",\n    \"YES\": \"はい\",\n    \"YOUR_EMAIL_ADDRESS\": \"メールアドレス\",\n    \"YOUR_FILES\": \"あなたのファイル\",\n    \"YOUR_MASTER_PASSWORD\": \"マスターパスワード\",\n    \"YOU_CANT_DO_THAT\": \"実行できません\"\n}\n"
  },
  {
    "path": "public/assets/locales/ka.json",
    "content": "{\n    \"ABORTED\": \"შეწყვეტილი\",\n    \"ABORT_CURRENT_UPLOADS?\": \"მიმდინარე ატვირთვის შეწყვეტა?\",\n    \"ACTIVITY\": \"აქტივობა\",\n    \"ADVANCED\": \"მოწინავე\",\n    \"ALL_DONE\": \"ყველაფერი შესრულებულია\",\n    \"ALREADY_EXIST\": \"უკვე არსებობს\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"შეიქმნა ფაილი სახელწოდებით {{VALUE}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"შეიქმნა საქაღალდე სახელწოდებით {{VALUE}\",\n    \"BEAUTIFUL_URL\": \"ლამაზი_ურლი\",\n    \"BOOKMARK\": \"სანიშნე\",\n    \"CAMERA\": \"კამერა\",\n    \"CANCEL\": \"გაუქმება\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"არ შეუძლია კავშირის დამყარება\",\n    \"CANT_LOAD_THIS_PICTURE\": \"ამ სურათის ჩატვირთვა ვერ მოხერხდა\",\n    \"CANT_USE_FILESYSTEM\": \"შეგიძლიათ გამოიყენოთ ფაილების სისტემა\",\n    \"CAN_RESHARE\": \"შეუძლია ხელახლა გაზიარება\",\n    \"CODE\": \"კოდი\",\n    \"CONFIGURE\": \"კონფიგურაცია\",\n    \"CONFIRM_BY_TYPING\": \"აკრეფით დაადასტურეთ\",\n    \"CONNECT\": \"დაკავშირება\",\n    \"CONNECTION_LOST\": \"კავშირი დაკარგულია\",\n    \"COPIED_TO_CLIPBOARD\": \"გადაწერა ბუფერში\",\n    \"CREATE_A_NEW_LINK\": \"შექმენით ახალი ბმული\",\n    \"CREATE_A_TAG\": \"ტეგის შექმნა\",\n    \"CURRENT\": \"მიმდინარე\",\n    \"CURRENT_UPLOAD\": \"მიმდინარე ატვირთვა\",\n    \"CUSTOM_LINK_URL\": \"პირადი ბმული URL\",\n    \"DASHBOARD\": \"დაფა\",\n    \"DATE\": \"თარიღი\",\n    \"DISPLAY_HIDDEN_FILES\": \"ფარული ფაილების ჩვენება\",\n    \"DOESNT_MATCH\": \"არ ემთხვევა\",\n    \"DONE\": \"შესრულებულია\",\n    \"DOWNLOAD\": \"ჩამოტვირთვა\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"გსურთ შეინახოთ ცვლილებები\",\n    \"DROP_HERE_TO_UPLOAD\": \"ჩამოაგდეთ აქ ატვირთვისთვის\",\n    \"EDITOR\": \"რედაქტორი\",\n    \"EMBED\": \"ჩართვა\",\n    \"EMPTY\": \"ცარიელი\",\n    \"ENCRYPTION_KEY\": \"დაშიფვრის გასაღები\",\n    \"ENDPOINT\": \"დასკვნა\",\n    \"ERROR\": \"შეცდომა\",\n    \"EXISTING_LINKS\": \"არსებული ბმულები\",\n    \"EXPIRATION\": \"ვადის გასვლა\",\n    \"EXPORT_AS_{{VALUE}}\": \"ექსპორტი, როგორც {{VALUE\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"აქ ხშირად ნახვის საქაღალდეები იქნება ნაჩვენები\",\n    \"HIDE_HIDDEN_FILES\": \"დამალული ფაილების დამალვა\",\n    \"HOME\": \"მთავარი\",\n    \"HOSTNAME*\": \"მასპინძლის სახელი\",\n    \"HOST_KEY\": \"მასპინძელი გასაღები\",\n    \"INCORRECT_PASSWORD\": \"არასწორი პაროლი\",\n    \"INFO\": \"ინფორმაცია\",\n    \"INTERNAL_ERROR\": \"შიდა შეცდომა\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"შიდა შეცდომა: ვერ შექმნით {{VALUE}\",\n    \"INVALID_ACCOUNT\": \"არასწორი ანგარიში\",\n    \"INVALID_PASSWORD\": \"არასწორი პაროლი\",\n    \"LAYOUT\": \"განლაგება\",\n    \"LOADING\": \"ჩატვირთვა\",\n    \"LOCATION\": \"ადგილმდებარეობა\",\n    \"MISSING_DEPENDENCY\": \"დაკარგული დამოკიდებულება\",\n    \"MORE_DETAILS\": \"მეტი დეტალი\",\n    \"NAVIGATE\": \"ნავიგაცია\",\n    \"NEW_FILE\": \"ახალი ფაილი\",\n    \"NEW_FILE::SHORT\": \"ახალი ფაილი\",\n    \"NEW_FOLDER\": \"ახალი დირექტორია\",\n    \"NEW_FOLDER::SHORT\": \"ახალი დირექტორია\",\n    \"NO\": \"არა\",\n    \"NOT_ALLOWED\": \"არაა ნებადართული\",\n    \"NOT_AUTHORISED\": \"არ არის უფლებამოსილი\",\n    \"NOT_FOUND\": \"ნაპოვნია\",\n    \"NOT_IMPLEMENTED\": \"არ განხორციელებულა\",\n    \"NOT_SUPPORTED\": \"არ არის მხარდაჭერილი\",\n    \"NOT_VALID\": \"მართებული არ არის\",\n    \"NUMBER_OF_CONNECTIONS\": \"კავშირების რაოდენობა\",\n    \"OK\": \"კარგი\",\n    \"ONLY_FOR_USERS\": \"მხოლოდ მომხმარებლებისთვის\",\n    \"OOPS\": \"უი\",\n    \"PASSPHRASE\": \"ფრაზის ფრაზა\",\n    \"PASSWORD\": \"პაროლი\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"პაროლი ვერ იქნება ცარიელი\",\n    \"PATH\": \"ბილიკი\",\n    \"PERMISSION_DENIED\": \"წვდომა აკრძალულია\",\n    \"PICK_A_MASTER_PASSWORD\": \"აირჩიე სამაგისტრო პაროლი\",\n    \"PORT\": \"პორტი\",\n    \"POWERED_BY\": \"იკვებება\",\n    \"PROPERTIES\": \"თვისებები\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"დაიცავით წვდომა პაროლით\",\n    \"QUICK_ACCESS\": \"სწრაფი წვდომა\",\n    \"REGION\": \"რეგიონი\",\n    \"REMEMBER_ME\": \"დამიმახსოვრე\",\n    \"REMOVE\": \"ამოიღე\",\n    \"RENAME\": \"სახელის შეცვლა\",\n    \"RENAME_AS\": \"სახელის შეცვლა როგორც\",\n    \"RESTRICTIONS\": \"შეზღუდვები\",\n    \"RUNNING\": \"სირბილი\",\n    \"SAVE_CURRENT_FILE\": \"მიმდინარე ფაილის შენახვა\",\n    \"SEARCH\": \"ძებნა\",\n    \"SETTINGS\": \"პარამეტრები\",\n    \"SHARE\": \"გაზიარება\",\n    \"SHARED_DRIVE\": \"გაზიარებული დისკი\",\n    \"SKIP_TO_CONTENT\": \"შინაარსზე გადასვლა\",\n    \"SORT\": \"დალაგება\",\n    \"SORT_BY_DATE\": \"დაალაგეთ თარიღით\",\n    \"SORT_BY_NAME\": \"დაალაგე სახელით\",\n    \"SORT_BY_SIZE\": \"დაალაგე ზომით\",\n    \"SORT_BY_TYPE\": \"დახარისხება ტიპის მიხედვით\",\n    \"STARRED\": \"ჩანიშნული.\",\n    \"SUPPORT\": \"მხარდაჭერა\",\n    \"TAG\": \"ტეგი\",\n    \"TAGS\": \"ტეგები\",\n    \"THERE_IS_NOTHING_HERE\": \"აქ არაფერია\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"ფაილი {{VALUE}} წაიშალა\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"ფაილი {{VALUE} დაარქვეს\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"ბმული გადაწერა იქნა ბუფერში\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"ამის შემდეგ ბმული ძალაში იქნება\",\n    \"TIMEOUT\": \"დროის ამოწურვა\",\n    \"TODO\": \"კეთება\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"მოძრაობის შეშუპება, სცადეთ მოგვიანებით\",\n    \"UPLOAD\": \"ატვირთვა\",\n    \"UPLOADER\": \"ატვირთვის\",\n    \"USERNAME\": \"მომხმარებლის სახელი\",\n    \"VERSION\": \"ვერსია\",\n    \"VIEWER\": \"მნახველი\",\n    \"WAITING\": \"ლოდინი\",\n    \"YES\": \"დიახ\",\n    \"YOUR_EMAIL_ADDRESS\": \"თქვენი ელ - ფოსტის მისამართი\",\n    \"YOUR_FILES\": \"ფაილები.\",\n    \"YOUR_MASTER_PASSWORD\": \"თქვენი სამაგისტრო პაროლი\",\n    \"YOU_CANT_DO_THAT\": \"თქვენ არ შეგიძლიათ ამის გაკეთება\"\n}\n"
  },
  {
    "path": "public/assets/locales/ko.json",
    "content": "{\n    \"ABORTED\": \"중단됨\",\n    \"ABORT_CURRENT_UPLOADS?\": \"현재 업로드를 중단 하시겠습니까?\",\n    \"ACTIVITY\": \"활동\",\n    \"ADVANCED\": \"고급\",\n    \"ALL_DONE\": \"작업 완료\",\n    \"ALREADY_EXIST\": \"이미 존재합니다\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"이름이 {{VALUE}} 인 파일이 생성되었습니다\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"이름이 {{VALUE}} 인 폴더가 생성되었습니다\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"서표\",\n    \"CAMERA\": \"카메라\",\n    \"CANCEL\": \"취소\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"연결을 설정할 수 없음\",\n    \"CANT_LOAD_THIS_PICTURE\": \"이 사진을 로드 할 수 없음\",\n    \"CANT_USE_FILESYSTEM\": \"파일 시스템을 사용할 수 없음\",\n    \"CAN_RESHARE\": \"재공유 가능\",\n    \"CODE\": \"암호\",\n    \"CONFIGURE\": \"구성\",\n    \"CONFIRM_BY_TYPING\": \"입력하여 확인\",\n    \"CONNECT\": \"연결\",\n    \"CONNECTION_LOST\": \"연결 끊김\",\n    \"COPIED_TO_CLIPBOARD\": \"클립 보드에 복사\",\n    \"CREATE_A_NEW_LINK\": \"새 링크 만들기\",\n    \"CREATE_A_TAG\": \"태그 만들기\",\n    \"CURRENT\": \"현재\",\n    \"CURRENT_UPLOAD\": \"현재 업로드\",\n    \"CUSTOM_LINK_URL\": \"맞춤 링크 URL\",\n    \"DASHBOARD\": \"제어판\",\n    \"DATE\": \"날짜\",\n    \"DISPLAY_HIDDEN_FILES\": \"숨겨진 파일 표시\",\n    \"DOESNT_MATCH\": \"일치하지 않음\",\n    \"DONE\": \"완료\",\n    \"DOWNLOAD\": \"다운로드\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"변경 사항을 저장 하시겠습니까?\",\n    \"DROP_HERE_TO_UPLOAD\": \"업로드하려면 여기로 드롭하십시오\",\n    \"EDITOR\": \"편집자\",\n    \"EMBED\": \"비어 있는\",\n    \"EMPTY\": \"빈\",\n    \"ENCRYPTION_KEY\": \"암호화 키\",\n    \"ENDPOINT\": \"종점\",\n    \"ERROR\": \"오류\",\n    \"EXISTING_LINKS\": \"기존 링크\",\n    \"EXPIRATION\": \"링크 만료\",\n    \"EXPORT_AS_{{VALUE}}\": \"{{VALUE}} (으)로 내보내기\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"자주 액세스하는 폴더가 여기에 표시됩니다\",\n    \"HIDE_HIDDEN_FILES\": \"숨겨진 파일 숨기기\",\n    \"HOME\": \"집\",\n    \"HOSTNAME*\": \"호스트 이름\",\n    \"HOST_KEY\": \"호스트 키\",\n    \"INCORRECT_PASSWORD\": \"잘못된 비밀번호\",\n    \"INFO\": \"정보\",\n    \"INTERNAL_ERROR\": \"내부 에러\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"내부 오류 : {{VALUE}}을 (를) 만들 수 없습니다\",\n    \"INVALID_ACCOUNT\": \"잘못된 계정\",\n    \"INVALID_PASSWORD\": \"유효하지 않은 비밀번호\",\n    \"LAYOUT\": \"공들여 나열한 것\",\n    \"LOADING\": \"로딩 중\",\n    \"LOCATION\": \"위치\",\n    \"MISSING_DEPENDENCY\": \"의존성 누락\",\n    \"MORE_DETAILS\": \"자세한 내용\",\n    \"NAVIGATE\": \"탐색\",\n    \"NEW_FILE\": \"새로운 파일\",\n    \"NEW_FILE::SHORT\": \"새 파일\",\n    \"NEW_FOLDER\": \"새로운 디렉토리\",\n    \"NEW_FOLDER::SHORT\": \"새 디렉토리\",\n    \"NO\": \"아니요\",\n    \"NOT_ALLOWED\": \"허용되지 않음\",\n    \"NOT_AUTHORISED\": \"승인되지 않음\",\n    \"NOT_FOUND\": \"찾을 수 없습니다\",\n    \"NOT_IMPLEMENTED\": \"구현되지 않음\",\n    \"NOT_SUPPORTED\": \"지원되지 않습니다\",\n    \"NOT_VALID\": \"유효하지 않습니다\",\n    \"NUMBER_OF_CONNECTIONS\": \"연결 수\",\n    \"OK\": \"확인\",\n    \"ONLY_FOR_USERS\": \"사용자 만\",\n    \"OOPS\": \"죄송합니다\",\n    \"PASSPHRASE\": \"암호\",\n    \"PASSWORD\": \"비밀번호\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"비밀번호는 비워 둘 수 없습니다\",\n    \"PATH\": \"경로\",\n    \"PERMISSION_DENIED\": \"권한 거부됨\",\n    \"PICK_A_MASTER_PASSWORD\": \"마스터 비밀번호를 선택하십시오\",\n    \"PORT\": \"포트\",\n    \"POWERED_BY\": \"에 의해 구동\",\n    \"PROPERTIES\": \"속성\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"비밀번호로 접근 제한\",\n    \"QUICK_ACCESS\": \"빠른 액세스\",\n    \"REGION\": \"지역\",\n    \"REMEMBER_ME\": \"로그인 유지\",\n    \"REMOVE\": \"제거\",\n    \"RENAME\": \"이름 바꾸기\",\n    \"RENAME_AS\": \"다음으로 이름 바꾸기\",\n    \"RESTRICTIONS\": \"제한 사항\",\n    \"RUNNING\": \"실행중\",\n    \"SAVE_CURRENT_FILE\": \"현재 파일 저장\",\n    \"SEARCH\": \"검색\",\n    \"SETTINGS\": \"설정\",\n    \"SHARE\": \"공유하다\",\n    \"SHARED_DRIVE\": \"공유 드라이브\",\n    \"SKIP_TO_CONTENT\": \"내용으로 건너뛰기\",\n    \"SORT\": \"종류\",\n    \"SORT_BY_DATE\": \"날짜별로 정렬\",\n    \"SORT_BY_NAME\": \"이름으로 정렬\",\n    \"SORT_BY_SIZE\": \"크기별로 정렬\",\n    \"SORT_BY_TYPE\": \"유형별로 정렬\",\n    \"STARRED\": \"별표가 붙은\",\n    \"SUPPORT\": \"지원\",\n    \"TAG\": \"꼬리표\",\n    \"TAGS\": \"태그\",\n    \"THERE_IS_NOTHING_HERE\": \"여기에 아무것도 없습니다\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"{{VALUE}} 파일이 삭제되었습니다\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"{{VALUE}} 파일의 이름이 변경되었습니다\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"링크가 클립 보드에 복사되었습니다\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"링크는 유효하지 않습니다\",\n    \"TIMEOUT\": \"타임 아웃\",\n    \"TODO\": \"할일\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"전송량 정체, 나중에 다시 시도해주십시오\",\n    \"UPLOAD\": \"업로드\",\n    \"UPLOADER\": \"업로더\",\n    \"USERNAME\": \"사용자 이름\",\n    \"VERSION\": \"버전\",\n    \"VIEWER\": \"뷰어\",\n    \"WAITING\": \"기다리는 중\",\n    \"YES\": \"예\",\n    \"YOUR_EMAIL_ADDRESS\": \"귀하의 이메일 주소\",\n    \"YOUR_FILES\": \"당신의 파일\",\n    \"YOUR_MASTER_PASSWORD\": \"귀하의 마스터 비밀번호\",\n    \"YOU_CANT_DO_THAT\": \"당신은 이 작업을 할 수 없습니다\"\n}\n"
  },
  {
    "path": "public/assets/locales/lt.json",
    "content": "{\n    \"ABORTED\": \"Atšaukta\",\n    \"ABORT_CURRENT_UPLOADS?\": \"nutraukti įkėlimą?\",\n    \"ACTIVITY\": \"Veikla\",\n    \"ADVANCED\": \"išplėstinis\",\n    \"ALL_DONE\": \"viskas padaryta\",\n    \"ALREADY_EXIST\": \"jau yra\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Buvo sukurtas failas pavadinimu {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Buvo sukurtas aplankas pavadinimu {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"graži_nuoroda\",\n    \"BOOKMARK\": \"žymė\",\n    \"CAMERA\": \"fotoaparatas\",\n    \"CANCEL\": \"atšaukti\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"negali užmegzti ryšio\",\n    \"CANT_LOAD_THIS_PICTURE\": \"Nepavyksta įkelti nuotraukos\",\n    \"CANT_USE_FILESYSTEM\": \"Nepavyksta naudoti failų sistemos\",\n    \"CAN_RESHARE\": \"gali bendrinti dar kartą\",\n    \"CODE\": \"kodas\",\n    \"CONFIGURE\": \"konfigūruoti\",\n    \"CONFIRM_BY_TYPING\": \"patvirtinkite įvesdami\",\n    \"CONNECT\": \"Prisijungti\",\n    \"CONNECTION_LOST\": \"ryšys prarastas\",\n    \"COPIED_TO_CLIPBOARD\": \"Nukopijuota į iškarpinę\",\n    \"CREATE_A_NEW_LINK\": \"sukurti naują nuorodą\",\n    \"CREATE_A_TAG\": \"sukurti žymą\",\n    \"CURRENT\": \"esamas\",\n    \"CURRENT_UPLOAD\": \"Įkeliama dabar\",\n    \"CUSTOM_LINK_URL\": \"tinkintos nuorodos URL\",\n    \"DASHBOARD\": \"prietaisų skydelis\",\n    \"DATE\": \"data\",\n    \"DISPLAY_HIDDEN_FILES\": \"rodyti paslėptus failus\",\n    \"DOESNT_MATCH\": \"Neatitinka\",\n    \"DONE\": \"padaryta\",\n    \"DOWNLOAD\": \"parsisiųsti\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"ar norite išsaugoti pakeitimus\",\n    \"DROP_HERE_TO_UPLOAD\": \"numeskite čia norėdami įkelti\",\n    \"EDITOR\": \"redaktorius\",\n    \"EMBED\": \"įterpti\",\n    \"EMPTY\": \"tuščia\",\n    \"ENCRYPTION_KEY\": \"šifravimo raktas\",\n    \"ENDPOINT\": \"baigtis\",\n    \"ERROR\": \"klaida\",\n    \"EXISTING_LINKS\": \"esamos nuorodos\",\n    \"EXPIRATION\": \"galiojimo laikas\",\n    \"EXPORT_AS_{{VALUE}}\": \"eksportuoti kaip {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"čia bus rodomi dažnai prieinami aplankai\",\n    \"HIDE_HIDDEN_FILES\": \"paslėpti paslėptus failus\",\n    \"HOME\": \"pradžia\",\n    \"HOSTNAME*\": \"pagrindinio kompiuterio vardas\",\n    \"HOST_KEY\": \"pagrindinio kompiuterio raktas\",\n    \"INCORRECT_PASSWORD\": \"Neteisingas slaptažodis\",\n    \"INFO\": \"informacija\",\n    \"INTERNAL_ERROR\": \"Vidinė klaida\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"vidinė klaida: negalima sukurti {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"neteisinga sąskaita\",\n    \"INVALID_PASSWORD\": \"Neteisingas slaptažodis\",\n    \"LAYOUT\": \"išdėstymas\",\n    \"LOADING\": \"įkeliama\",\n    \"LOCATION\": \"vieta\",\n    \"MISSING_DEPENDENCY\": \"trūksta priklausomybės\",\n    \"MORE_DETAILS\": \"daugiau informacijos\",\n    \"NAVIGATE\": \"naviguoti\",\n    \"NEW_FILE\": \"naujas failas\",\n    \"NEW_FILE::SHORT\": \"naujas failas\",\n    \"NEW_FOLDER\": \"naujas katalogas\",\n    \"NEW_FOLDER::SHORT\": \"naujas katal..\",\n    \"NO\": \"ne\",\n    \"NOT_ALLOWED\": \"neleidžiama\",\n    \"NOT_AUTHORISED\": \"neleidžiama\",\n    \"NOT_FOUND\": \"nerastas\",\n    \"NOT_IMPLEMENTED\": \"neįgyvendino\",\n    \"NOT_SUPPORTED\": \"nepalaikomas\",\n    \"NOT_VALID\": \"negaliojantis\",\n    \"NUMBER_OF_CONNECTIONS\": \"jungčių skaičius\",\n    \"OK\": \"Gerai\",\n    \"ONLY_FOR_USERS\": \"Tik vartotojams\",\n    \"OOPS\": \"Oi\",\n    \"PASSPHRASE\": \"slaptafrazė\",\n    \"PASSWORD\": \"Slaptažodis\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"slaptažodis negali būti tuščias\",\n    \"PATH\": \"kelias\",\n    \"PERMISSION_DENIED\": \"leidimas nesuteiktas\",\n    \"PICK_A_MASTER_PASSWORD\": \"pasirinkti pagrindinį slaptažodį\",\n    \"PORT\": \"uostas\",\n    \"POWERED_BY\": \"maitina\",\n    \"PROPERTIES\": \"savybes\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"apsaugoti prieigą slaptažodžiu\",\n    \"QUICK_ACCESS\": \"greita prieiga\",\n    \"REGION\": \"regionas\",\n    \"REMEMBER_ME\": \"Prisimink mane\",\n    \"REMOVE\": \"pašalinti\",\n    \"RENAME\": \"pervadinti\",\n    \"RENAME_AS\": \"pervadinti kaip\",\n    \"RESTRICTIONS\": \"apribojimai\",\n    \"RUNNING\": \"vykdomas\",\n    \"SAVE_CURRENT_FILE\": \"išsaugoti dabartinį failą\",\n    \"SEARCH\": \"Paieška\",\n    \"SETTINGS\": \"nustatymai\",\n    \"SHARE\": \"bendrinti\",\n    \"SHARED_DRIVE\": \"bendrinamas diskas\",\n    \"SKIP_TO_CONTENT\": \"Pereiti prie turinio\",\n    \"SORT\": \"rūšiuoti\",\n    \"SORT_BY_DATE\": \"rūšiuoti pagal datą\",\n    \"SORT_BY_NAME\": \"rūšiuoti pagal pavadinimą\",\n    \"SORT_BY_SIZE\": \"rūšiuoti pagal dydį\",\n    \"SORT_BY_TYPE\": \"rūšiuoti pagal tipą\",\n    \"STARRED\": \"pažymėta\",\n    \"SUPPORT\": \"palaikymas\",\n    \"TAG\": \"žyma\",\n    \"TAGS\": \"žymos\",\n    \"THERE_IS_NOTHING_HERE\": \"čia nieko nėra\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"failas {{VALUE}} buvo ištrintas\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"failas {{VALUE}} buvo pervadintas\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"nuoroda buvo nukopijuota į mainų sritį\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"Nuoroda nebegalios po\",\n    \"TIMEOUT\": \"laikas baigėsi\",\n    \"TODO\": \"daryti\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"transporto spūstys, bandykite dar kartą vėliau\",\n    \"UPLOAD\": \"įkelti\",\n    \"UPLOADER\": \"Įkėlėjas\",\n    \"USERNAME\": \"Vartotojo vardas\",\n    \"VERSION\": \"versija\",\n    \"VIEWER\": \"žiūrovas\",\n    \"WAITING\": \"laukimas\",\n    \"YES\": \"taip\",\n    \"YOUR_EMAIL_ADDRESS\": \"Jūsų elektroninio pašto adresas\",\n    \"YOUR_FILES\": \"Jūsų failai\",\n    \"YOUR_MASTER_PASSWORD\": \"jūsų pagrindinis slaptažodis\",\n    \"YOU_CANT_DO_THAT\": \"tu negali to padaryti\"\n}\n"
  },
  {
    "path": "public/assets/locales/lv.json",
    "content": "{\n    \"ABORTED\": \"pārtraukts\",\n    \"ABORT_CURRENT_UPLOADS?\": \"Vai pārtraukt pašreizējo augšupielādi?\",\n    \"ACTIVITY\": \"Darbība\",\n    \"ADVANCED\": \"pārlabots\",\n    \"ALL_DONE\": \"visi uzdevumi izpildīti\",\n    \"ALREADY_EXIST\": \"jau pastāv\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Tika izveidots fails ar nosaukumu {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Tika izveidota mape ar nosaukumu {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"skaista_url\",\n    \"BOOKMARK\": \"grāmatzīme\",\n    \"CAMERA\": \"kamera\",\n    \"CANCEL\": \"atcelt\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"nevar izveidot savienojumu\",\n    \"CANT_LOAD_THIS_PICTURE\": \"nevar ielādēt šo attēlu\",\n    \"CANT_USE_FILESYSTEM\": \"Nevar izmantot failu sistēmu.\",\n    \"CAN_RESHARE\": \"var atkārtoti koplietot\",\n    \"CODE\": \"kods\",\n    \"CONFIGURE\": \"konfigurēt\",\n    \"CONFIRM_BY_TYPING\": \"apstipriniet, ierakstot\",\n    \"CONNECT\": \"savienot\",\n    \"CONNECTION_LOST\": \"savienojums pazaudēts\",\n    \"COPIED_TO_CLIPBOARD\": \"nokopēts uz starpliktuvi\",\n    \"CREATE_A_NEW_LINK\": \"izveidot jaunu saiti\",\n    \"CREATE_A_TAG\": \"izveidot tagu\",\n    \"CURRENT\": \"pašreizējais\",\n    \"CURRENT_UPLOAD\": \"pašreizējā augšupielāde\",\n    \"CUSTOM_LINK_URL\": \"pielāgots saites URL\",\n    \"DASHBOARD\": \"instruktīva panelis\",\n    \"DATE\": \"datums\",\n    \"DISPLAY_HIDDEN_FILES\": \"rādīt paslēptos failus\",\n    \"DOESNT_MATCH\": \"neatbilst\",\n    \"DONE\": \"izdarīts\",\n    \"DOWNLOAD\": \"lejupielādēt\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"Vai vēlaties saglabāt izmaiņas?\",\n    \"DROP_HERE_TO_UPLOAD\": \"Nometiet šeit, lai augšupielādētu.\",\n    \"EDITOR\": \"redaktors\",\n    \"EMBED\": \"ielikt\",\n    \"EMPTY\": \"tukšs\",\n    \"ENCRYPTION_KEY\": \"šifrēšanas atslēga\",\n    \"ENDPOINT\": \"galapunkts\",\n    \"ERROR\": \"kļūda\",\n    \"EXISTING_LINKS\": \"esošās saites\",\n    \"EXPIRATION\": \"beigu datums\",\n    \"EXPORT_AS_{{VALUE}}\": \"eksportēt kā {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"Šeit tiks parādītas bieži pieejamās mapes\",\n    \"HIDE_HIDDEN_FILES\": \"paslēpt paslēptos failus\",\n    \"HOME\": \"mājas\",\n    \"HOSTNAME*\": \"hosta nosaukums\",\n    \"HOST_KEY\": \"hosta atslēga\",\n    \"INCORRECT_PASSWORD\": \"nepareizs parole\",\n    \"INFO\": \"informācija\",\n    \"INTERNAL_ERROR\": \"iekšēja kļūda\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"iekšēja kļūda: nevar izveidot {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"nederzīgs konts\",\n    \"INVALID_PASSWORD\": \"nepareizs parole\",\n    \"LAYOUT\": \"izkārtojums\",\n    \"LOADING\": \"ielādē\",\n    \"LOCATION\": \"atrašanās vieta\",\n    \"MISSING_DEPENDENCY\": \"trūkst atkarība\",\n    \"MORE_DETAILS\": \"vairāk detaļu\",\n    \"NAVIGATE\": \"orientēties\",\n    \"NEW_FILE\": \"jauns fails\",\n    \"NEW_FILE::SHORT\": \"jauns fails\",\n    \"NEW_FOLDER\": \"jauna mape\",\n    \"NEW_FOLDER::SHORT\": \"jauna mape\",\n    \"NO\": \"nē\",\n    \"NOT_ALLOWED\": \"nav atļauts\",\n    \"NOT_AUTHORISED\": \"nav autorizēts\",\n    \"NOT_FOUND\": \"nav atrasts\",\n    \"NOT_IMPLEMENTED\": \"nav ieviests\",\n    \"NOT_SUPPORTED\": \"nav atbalstīts\",\n    \"NOT_VALID\": \"nav derīgs\",\n    \"NUMBER_OF_CONNECTIONS\": \"savienojumu skaits\",\n    \"OK\": \"labi\",\n    \"ONLY_FOR_USERS\": \"Tikai lietotājiem\",\n    \"OOPS\": \"žēl\",\n    \"PASSPHRASE\": \"paroles frāze\",\n    \"PASSWORD\": \"parole\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"parole nevar būt tukša\",\n    \"PATH\": \"ceļš\",\n    \"PERMISSION_DENIED\": \"pieeja noraidīta\",\n    \"PICK_A_MASTER_PASSWORD\": \"izvēlieties galveno paroli\",\n    \"PORT\": \"ports\",\n    \"POWERED_BY\": \"piedāvāta ar\",\n    \"PROPERTIES\": \"īpašības\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"aizsargāt piekļuvi ar paroli\",\n    \"QUICK_ACCESS\": \"ātra piekļuve\",\n    \"REGION\": \"reģions\",\n    \"REMEMBER_ME\": \"atcerēties mani\",\n    \"REMOVE\": \"noņemt\",\n    \"RENAME\": \"pārdēvēt\",\n    \"RENAME_AS\": \"pārdēvēt kā\",\n    \"RESTRICTIONS\": \"ierobežojumi\",\n    \"RUNNING\": \"darbojas\",\n    \"SAVE_CURRENT_FILE\": \"saglabāt pašreizējo failu\",\n    \"SEARCH\": \"meklēt\",\n    \"SETTINGS\": \"iestatījumi\",\n    \"SHARE\": \"koplietot\",\n    \"SHARED_DRIVE\": \"koplietots disks\",\n    \"SKIP_TO_CONTENT\": \"Iet uz saturu\",\n    \"SORT\": \"kārtot\",\n    \"SORT_BY_DATE\": \"sārtot pēc datuma\",\n    \"SORT_BY_NAME\": \"sārtot pēc nosaukuma\",\n    \"SORT_BY_SIZE\": \"sārtot pēc izmēra\",\n    \"SORT_BY_TYPE\": \"sārtot pēc veida\",\n    \"STARRED\": \"atzīmēts\",\n    \"SUPPORT\": \"atbalsts\",\n    \"TAG\": \"tags\",\n    \"TAGS\": \"tagi\",\n    \"THERE_IS_NOTHING_HERE\": \"šeit nav nekā\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"fails {{VALUE}} tika izdzēsts\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"fails {{VALUE}} tika pārdēvēts\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"saites tika nokopētas starpliktuvē\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"Pēc tam saite vairs nebūs derīga\",\n    \"TIMEOUT\": \"laika iztrūkums\",\n    \"TODO\": \"uzdevums\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"piekļuve aizņemta, mēģiniet vēlāk\",\n    \"UPLOAD\": \"augšupielādēt\",\n    \"UPLOADER\": \"augšupielādētājs\",\n    \"USERNAME\": \"lietotājvārds\",\n    \"VERSION\": \"versija\",\n    \"VIEWER\": \"skatītājs\",\n    \"WAITING\": \"gaida\",\n    \"YES\": \"jā\",\n    \"YOUR_EMAIL_ADDRESS\": \"jūsu e-pasta adrese\",\n    \"YOUR_FILES\": \"jūsu faili\",\n    \"YOUR_MASTER_PASSWORD\": \"jūsu galvenā parole\",\n    \"YOU_CANT_DO_THAT\": \"nevarat to izdarīt\"\n}\n"
  },
  {
    "path": "public/assets/locales/mn.json",
    "content": "{\n    \"ABORTED\": \"зогсоосон\",\n    \"ABORT_CURRENT_UPLOADS?\": \"одоогийн байршуулалтыг зогсоох уу?\",\n    \"ACTIVITY\": \"Үйл ажиллагаа\",\n    \"ADVANCED\": \"дэвшилтэт\",\n    \"ALL_DONE\": \"бүгд бэлэн\",\n    \"ALREADY_EXIST\": \"аль хэдийн байдаг\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"{{VALUE}} нэртэй файл үүсгэгдсэн\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"{{VALUE}} нэртэй хавтас үүсгэсэн\",\n    \"BEAUTIFUL_URL\": \"үзэсгэлэнтэй_урл\",\n    \"BOOKMARK\": \"тэмдэглэл\",\n    \"CAMERA\": \"камер\",\n    \"CANCEL\": \"цуцлах\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"холболт үүсгэж чадахгүй байна\",\n    \"CANT_LOAD_THIS_PICTURE\": \"энэ зургийг ачаалах боломжгүй\",\n    \"CANT_USE_FILESYSTEM\": \"файлын системийг ашиглах боломжгүй\",\n    \"CAN_RESHARE\": \"хуваалцах боломжтой\",\n    \"CODE\": \"код\",\n    \"CONFIGURE\": \"тохируулах\",\n    \"CONFIRM_BY_TYPING\": \"бичиж баталгаажуулна уу\",\n    \"CONNECT\": \"холбох\",\n    \"CONNECTION_LOST\": \"холболт тасарсан\",\n    \"COPIED_TO_CLIPBOARD\": \"санах ой руу хуулсан\",\n    \"CREATE_A_NEW_LINK\": \"шинэ холбоос үүсгэх\",\n    \"CREATE_A_TAG\": \"шошго үүсгэх\",\n    \"CURRENT\": \"Одоогийн\",\n    \"CURRENT_UPLOAD\": \"одоогийн байршуулах\",\n    \"CUSTOM_LINK_URL\": \"захиалгат холбоосын URL\",\n    \"DASHBOARD\": \"хяналтын самбар\",\n    \"DATE\": \"огноо\",\n    \"DISPLAY_HIDDEN_FILES\": \"далд файлуудыг харуулах\",\n    \"DOESNT_MATCH\": \"таарахгүй байна\",\n    \"DONE\": \"хийсэн\",\n    \"DOWNLOAD\": \"татаж авах\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"та өөрчлөлтийг хадгалахыг хүсэж байна уу\",\n    \"DROP_HERE_TO_UPLOAD\": \"энд чирж байршуулна уу\",\n    \"EDITOR\": \"редактор\",\n    \"EMBED\": \"оруулсан\",\n    \"EMPTY\": \"хоосон\",\n    \"ENCRYPTION_KEY\": \"шифрлэх түлхүүр\",\n    \"ENDPOINT\": \"эцсийн цэг\",\n    \"ERROR\": \"алдаа\",\n    \"EXISTING_LINKS\": \"байгаа холбоосууд\",\n    \"EXPIRATION\": \"дуусах хугацаа\",\n    \"EXPORT_AS_{{VALUE}}\": \"{{VALUE}} болгож экспорт хийх\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"эндээс шууд хандалт хийхийг харах болно\",\n    \"HIDE_HIDDEN_FILES\": \"далд файлуудыг нуух\",\n    \"HOME\": \"гэр\",\n    \"HOSTNAME*\": \"хостын нэр\",\n    \"HOST_KEY\": \"хост түлхүүр\",\n    \"INCORRECT_PASSWORD\": \"нууц үг буруу\",\n    \"INFO\": \"мэдээлэл\",\n    \"INTERNAL_ERROR\": \"Дотоод алдаа\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"дотоод алдаа: {{VALUE}} үүсгэх боломжгүй\",\n    \"INVALID_ACCOUNT\": \"хүчингүй данс\",\n    \"INVALID_PASSWORD\": \"Нууц үг буруу байна\",\n    \"LAYOUT\": \"төлөвлөлт\",\n    \"LOADING\": \"ачаалж байна\",\n    \"LOCATION\": \"байршил\",\n    \"MISSING_DEPENDENCY\": \"хамаарал алдагдсан\",\n    \"MORE_DETAILS\": \"дэлгэрэнгүй мэдээлэл\",\n    \"NAVIGATE\": \"залуурдах\",\n    \"NEW_FILE\": \"шинэ файл\",\n    \"NEW_FILE::SHORT\": \"шинэ файл\",\n    \"NEW_FOLDER\": \"шинэ лавлах\",\n    \"NEW_FOLDER::SHORT\": \"шинэ лавлах\",\n    \"NO\": \"үгүй шүү\",\n    \"NOT_ALLOWED\": \"зөвшөөрөгдөөгүй\",\n    \"NOT_AUTHORISED\": \"зөвшөөрөлгүй\",\n    \"NOT_FOUND\": \"олдсонгүй\",\n    \"NOT_IMPLEMENTED\": \"хэрэгжүүлээгүй\",\n    \"NOT_SUPPORTED\": \"дэмжихгүй байна\",\n    \"NOT_VALID\": \"хүчин төгөлдөр бус\",\n    \"NUMBER_OF_CONNECTIONS\": \"холболтын тоо\",\n    \"OK\": \"за\",\n    \"ONLY_FOR_USERS\": \"Зөвхөн хэрэглэгчид\",\n    \"OOPS\": \"Өө,\",\n    \"PASSPHRASE\": \"Нэвтрэх үг\",\n    \"PASSWORD\": \"нууц үг\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"нууц үг хоосон байж болохгүй\",\n    \"PATH\": \"зам\",\n    \"PERMISSION_DENIED\": \"зөвшөөрөл олгогдоогүй\",\n    \"PICK_A_MASTER_PASSWORD\": \"мастер нууц үг сонгох\",\n    \"PORT\": \"боомт\",\n    \"POWERED_BY\": \"дэмжигдсэн\",\n    \"PROPERTIES\": \"шинж чанарууд\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"нууц үгээр нэвтрэх эрхийг хамгаална\",\n    \"QUICK_ACCESS\": \"түргэн нэвтрэх\",\n    \"REGION\": \"бүс нутаг\",\n    \"REMEMBER_ME\": \"санаж байна уу\",\n    \"REMOVE\": \"устгах\",\n    \"RENAME\": \"нэрийг өөрчлөх\",\n    \"RENAME_AS\": \"дараах нэрээр өөрчлөх\",\n    \"RESTRICTIONS\": \"хязгаарлалт\",\n    \"RUNNING\": \"ажиллаж байгаа\",\n    \"SAVE_CURRENT_FILE\": \"одоогийн файлыг хадгалах\",\n    \"SEARCH\": \"хайх\",\n    \"SETTINGS\": \"тохиргоо\",\n    \"SHARE\": \"хуваалцах\",\n    \"SHARED_DRIVE\": \"хуваалцсан хөтөч\",\n    \"SKIP_TO_CONTENT\": \"Агуулга руу шилжих\",\n    \"SORT\": \"эрэмбэлэх\",\n    \"SORT_BY_DATE\": \"огноогоор нь ангилах\",\n    \"SORT_BY_NAME\": \"нэрээр нь ангил\",\n    \"SORT_BY_SIZE\": \"хэмжийн дагуу эрэмбэлэх\",\n    \"SORT_BY_TYPE\": \"төрлөөр нь ангил\",\n    \"STARRED\": \"тэмдэглэгдсэн\",\n    \"SUPPORT\": \"дэмжлэг\",\n    \"TAG\": \"шошго\",\n    \"TAGS\": \"шошгонууда\",\n    \"THERE_IS_NOTHING_HERE\": \"энд юу ч байхгүй\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"файлыг {{VALUE}} устгасан байна\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"файлын нэр {{VALUE}} өөрчлөгдсөн\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"холбоосыг нь санах ойд хуулав\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"холбоос дараа нь хүчингүй болно\",\n    \"TIMEOUT\": \"завсарлага\",\n    \"TODO\": \"хийх\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"түгжрэл, дараа дахин оролдоно уу\",\n    \"UPLOAD\": \"хуулах\",\n    \"UPLOADER\": \"байршуулагч\",\n    \"USERNAME\": \"хэрэглэгчийн нэр\",\n    \"VERSION\": \"хувилбар\",\n    \"VIEWER\": \"үзэгч\",\n    \"WAITING\": \"хүлээж байна\",\n    \"YES\": \"тийм шүү\",\n    \"YOUR_EMAIL_ADDRESS\": \"таны имэйл хаяг\",\n    \"YOUR_FILES\": \"таны файлууд\",\n    \"YOUR_MASTER_PASSWORD\": \"таны мастер нууц үг\",\n    \"YOU_CANT_DO_THAT\": \"та үүнийг хийж чадахгүй\"\n}\n"
  },
  {
    "path": "public/assets/locales/nb.json",
    "content": "{\n    \"ABORTED\": \"abortert\",\n    \"ABORT_CURRENT_UPLOADS?\": \"avbryte gjeldende opplasting?\",\n    \"ACTIVITY\": \"Aktivitet\",\n    \"ADVANCED\": \"avansert\",\n    \"ALL_DONE\": \"ferdig\",\n    \"ALREADY_EXIST\": \"Finnes allerede\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"En fil med navnet {{VALUE}} ble opprettet\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"En mappe med navnet {{VALUE}} ble opprettet\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"bokmerke\",\n    \"CAMERA\": \"kamera\",\n    \"CANCEL\": \"Avbryt\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"kan ikke opprette en forbindelse\",\n    \"CANT_LOAD_THIS_PICTURE\": \"kan ikke laste dette bildet\",\n    \"CANT_USE_FILESYSTEM\": \"kan bruke filsystem\",\n    \"CAN_RESHARE\": \"kan dele på nytt\",\n    \"CODE\": \"kode\",\n    \"CONFIGURE\": \"konfigurere\",\n    \"CONFIRM_BY_TYPING\": \"bekreft ved å skrive\",\n    \"CONNECT\": \"koble\",\n    \"CONNECTION_LOST\": \"forbindelsen mistet\",\n    \"COPIED_TO_CLIPBOARD\": \"kopiert til utklippstavlen\",\n    \"CREATE_A_NEW_LINK\": \"opprette en ny lenke\",\n    \"CREATE_A_TAG\": \"opprett en merkelapp\",\n    \"CURRENT\": \"strøm\",\n    \"CURRENT_UPLOAD\": \"nåværende opplasting\",\n    \"CUSTOM_LINK_URL\": \"tilpasset lenke URL\",\n    \"DASHBOARD\": \"dashbord\",\n    \"DATE\": \"Dato\",\n    \"DISPLAY_HIDDEN_FILES\": \"vise skjulte filer\",\n    \"DOESNT_MATCH\": \"stemmer ikke\",\n    \"DONE\": \"ferdig\",\n    \"DOWNLOAD\": \"nedlasting\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"vil du lagre endringene\",\n    \"DROP_HERE_TO_UPLOAD\": \"slipp her for å laste opp\",\n    \"EDITOR\": \"redaktør\",\n    \"EMBED\": \"innebygg\",\n    \"EMPTY\": \"tømme\",\n    \"ENCRYPTION_KEY\": \"krypteringsnøkkel\",\n    \"ENDPOINT\": \"endepunkt\",\n    \"ERROR\": \"feil\",\n    \"EXISTING_LINKS\": \"eksisterende lenker\",\n    \"EXPIRATION\": \"utløp\",\n    \"EXPORT_AS_{{VALUE}}\": \"eksportere som {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"ofte vises mapper her\",\n    \"HIDE_HIDDEN_FILES\": \"skjul skjulte filer\",\n    \"HOME\": \"hjem\",\n    \"HOSTNAME*\": \"vertsnavn\",\n    \"HOST_KEY\": \"vertsnøkkel\",\n    \"INCORRECT_PASSWORD\": \"feil passord\",\n    \"INFO\": \"info\",\n    \"INTERNAL_ERROR\": \"Intern feil\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"intern feil: kan ikke opprette en {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"ugyldig konto\",\n    \"INVALID_PASSWORD\": \"ugyldig passord\",\n    \"LAYOUT\": \"oppsett\",\n    \"LOADING\": \"laster\",\n    \"LOCATION\": \"plassering\",\n    \"MISSING_DEPENDENCY\": \"mangler avhengighet\",\n    \"MORE_DETAILS\": \"mer informasjon\",\n    \"NAVIGATE\": \"navigere\",\n    \"NEW_FILE\": \"ny fil\",\n    \"NEW_FILE::SHORT\": \"ny fil\",\n    \"NEW_FOLDER\": \"ny katalog\",\n    \"NEW_FOLDER::SHORT\": \"ny katalog\",\n    \"NO\": \"Nei\",\n    \"NOT_ALLOWED\": \"ikke tillatt\",\n    \"NOT_AUTHORISED\": \"ikke autorisert\",\n    \"NOT_FOUND\": \"ikke funnet\",\n    \"NOT_IMPLEMENTED\": \"ikke implementert\",\n    \"NOT_SUPPORTED\": \"ikke støttet\",\n    \"NOT_VALID\": \"ikke gyldig\",\n    \"NUMBER_OF_CONNECTIONS\": \"antall tilkoblinger\",\n    \"OK\": \"ok\",\n    \"ONLY_FOR_USERS\": \"Bare for brukere\",\n    \"OOPS\": \"Oops\",\n    \"PASSPHRASE\": \"passphrase\",\n    \"PASSWORD\": \"passord\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"passord kan ikke være tomt\",\n    \"PATH\": \"sti\",\n    \"PERMISSION_DENIED\": \"tillatelse avslått\",\n    \"PICK_A_MASTER_PASSWORD\": \"velg et hovedpassord\",\n    \"PORT\": \"havn\",\n    \"POWERED_BY\": \"drevet av\",\n    \"PROPERTIES\": \"egenskaper\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"beskytte tilgang med et passord\",\n    \"QUICK_ACCESS\": \"hurtigtilgang\",\n    \"REGION\": \"region\",\n    \"REMEMBER_ME\": \"Husk meg\",\n    \"REMOVE\": \"fjern\",\n    \"RENAME\": \"gi nytt navn\",\n    \"RENAME_AS\": \"gi nytt navn som\",\n    \"RESTRICTIONS\": \"begrensninger\",\n    \"RUNNING\": \"kjører\",\n    \"SAVE_CURRENT_FILE\": \"lagre gjeldende fil\",\n    \"SEARCH\": \"Søk\",\n    \"SETTINGS\": \"innstillinger\",\n    \"SHARE\": \"del\",\n    \"SHARED_DRIVE\": \"delt stasjon\",\n    \"SKIP_TO_CONTENT\": \"Gå til innhold\",\n    \"SORT\": \"sorter\",\n    \"SORT_BY_DATE\": \"sorter etter dato\",\n    \"SORT_BY_NAME\": \"sorter etter navn\",\n    \"SORT_BY_SIZE\": \"sorter etter størrelse\",\n    \"SORT_BY_TYPE\": \"sorter etter type\",\n    \"STARRED\": \"stjernemerket\",\n    \"SUPPORT\": \"Brukerstøtte\",\n    \"TAG\": \"merkelapp\",\n    \"TAGS\": \"merkelapper\",\n    \"THERE_IS_NOTHING_HERE\": \"her er ingenting\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"filen {{VALUE}} ble slettet\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"filen {{VALUE}} ble gitt nytt navn\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"lenken ble kopiert i utklippstavlen\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"koblingen vil ikke være gyldig etter\",\n    \"TIMEOUT\": \"tidsavbrudd\",\n    \"TODO\": \"å gjøre\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"trafikkstopp, prøv igjen senere\",\n    \"UPLOAD\": \"last opp\",\n    \"UPLOADER\": \"uploader\",\n    \"USERNAME\": \"brukernavn\",\n    \"VERSION\": \"versjon\",\n    \"VIEWER\": \"viewer\",\n    \"WAITING\": \"venter\",\n    \"YES\": \"ja\",\n    \"YOUR_EMAIL_ADDRESS\": \"Din epostadresse\",\n    \"YOUR_FILES\": \"dine filer\",\n    \"YOUR_MASTER_PASSWORD\": \"ditt hovedpassord\",\n    \"YOU_CANT_DO_THAT\": \"du kan ikke gjøre det\"\n}\n"
  },
  {
    "path": "public/assets/locales/nl.json",
    "content": "{\n    \"ABORTED\": \"afgebroken\",\n    \"ABORT_CURRENT_UPLOADS?\": \"huidige upload afbreken?\",\n    \"ACTIVITY\": \"Activiteit\",\n    \"ADVANCED\": \"Geavanceerd\",\n    \"ALL_DONE\": \"helemaal klaar\",\n    \"ALREADY_EXIST\": \"bestaat al\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Er is een bestand met de naam {{VALUE}} gemaakt\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Er is een map met de naam {{VALUE}} gemaakt\",\n    \"BEAUTIFUL_URL\": \"mooie_url\",\n    \"BOOKMARK\": \"bladwijzer\",\n    \"CAMERA\": \"camera\",\n    \"CANCEL\": \"annuleren\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"kan geen verbinding tot stand brengen\",\n    \"CANT_LOAD_THIS_PICTURE\": \"kan deze afbeelding niet laden\",\n    \"CANT_USE_FILESYSTEM\": \"kan bestandssysteem niet gebruiken\",\n    \"CAN_RESHARE\": \"kan opnieuw delen\",\n    \"CODE\": \"code\",\n    \"CONFIGURE\": \"configureren\",\n    \"CONFIRM_BY_TYPING\": \"bevestig door te typen\",\n    \"CONNECT\": \"verbinden\",\n    \"CONNECTION_LOST\": \"verbinding verloren\",\n    \"COPIED_TO_CLIPBOARD\": \"gekopieerd naar het klembord\",\n    \"CREATE_A_NEW_LINK\": \"maak een nieuwe link\",\n    \"CREATE_A_TAG\": \"maak een tag\",\n    \"CURRENT\": \"actueel\",\n    \"CURRENT_UPLOAD\": \"huidige upload\",\n    \"CUSTOM_LINK_URL\": \"aangepaste link-URL\",\n    \"DASHBOARD\": \"dashboard\",\n    \"DATE\": \"datum\",\n    \"DISPLAY_HIDDEN_FILES\": \"verborgen bestanden weergeven\",\n    \"DOESNT_MATCH\": \"komt niet overeen\",\n    \"DONE\": \"gedaan\",\n    \"DOWNLOAD\": \"downloaden\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"wilt u de wijzigingen opslaan\",\n    \"DROP_HERE_TO_UPLOAD\": \"hier neerzetten om te uploaden\",\n    \"EDITOR\": \"editor\",\n    \"EMBED\": \"insluiten\",\n    \"EMPTY\": \"leeg\",\n    \"ENCRYPTION_KEY\": \"coderingssleutel\",\n    \"ENDPOINT\": \"eindpunt\",\n    \"ERROR\": \"fout\",\n    \"EXISTING_LINKS\": \"bestaande links\",\n    \"EXPIRATION\": \"vervaldatum\",\n    \"EXPORT_AS_{{VALUE}}\": \"exporteren als {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"vaak worden toegangsmappen hier weergegeven\",\n    \"HIDE_HIDDEN_FILES\": \"verberg verborgen bestanden\",\n    \"HOME\": \"home\",\n    \"HOSTNAME*\": \"hostnaam\",\n    \"HOST_KEY\": \"host sleutel\",\n    \"INCORRECT_PASSWORD\": \"Incorrect wachtwoord\",\n    \"INFO\": \"info\",\n    \"INTERNAL_ERROR\": \"Interne fout\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"interne fout: kan geen {{VALUE}} maken\",\n    \"INVALID_ACCOUNT\": \"ongeldig account\",\n    \"INVALID_PASSWORD\": \"ongeldig wachtwoord\",\n    \"LAYOUT\": \"indeling\",\n    \"LOADING\": \"laden\",\n    \"LOCATION\": \"plaats\",\n    \"MISSING_DEPENDENCY\": \"ontbrekende afhankelijkheid\",\n    \"MORE_DETAILS\": \"meer details\",\n    \"NAVIGATE\": \"navigeren\",\n    \"NEW_FILE\": \"nieuw bestand\",\n    \"NEW_FILE::SHORT\": \"nieuw bestand\",\n    \"NEW_FOLDER\": \"nieuwe map\",\n    \"NEW_FOLDER::SHORT\": \"nieuwe map\",\n    \"NO\": \"Nee\",\n    \"NOT_ALLOWED\": \"niet toegestaan\",\n    \"NOT_AUTHORISED\": \"niet bevoegd\",\n    \"NOT_FOUND\": \"niet gevonden\",\n    \"NOT_IMPLEMENTED\": \"Niet geïmplementeerd\",\n    \"NOT_SUPPORTED\": \"niet ondersteund\",\n    \"NOT_VALID\": \"niet geldig\",\n    \"NUMBER_OF_CONNECTIONS\": \"aantal verbindingen\",\n    \"OK\": \"OK\",\n    \"ONLY_FOR_USERS\": \"Alleen voor gebruikers\",\n    \"OOPS\": \"Oeps\",\n    \"PASSPHRASE\": \"wachtwoordzin\",\n    \"PASSWORD\": \"wachtwoord\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"wachtwoord mag niet leeg zijn\",\n    \"PATH\": \"pad\",\n    \"PERMISSION_DENIED\": \"toestemming geweigerd\",\n    \"PICK_A_MASTER_PASSWORD\": \"jouw hoofdwachtwoord\",\n    \"PORT\": \"poort\",\n    \"POWERED_BY\": \"powered by\",\n    \"PROPERTIES\": \"eigenschappen\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"beveilig toegang met wachtwoord\",\n    \"QUICK_ACCESS\": \"snelle toegang\",\n    \"REGION\": \"regio\",\n    \"REMEMBER_ME\": \"Onthoud me\",\n    \"REMOVE\": \"verwijderen\",\n    \"RENAME\": \"hernoemen\",\n    \"RENAME_AS\": \"hernoemen als\",\n    \"RESTRICTIONS\": \"beperkingen\",\n    \"RUNNING\": \"verwerken\",\n    \"SAVE_CURRENT_FILE\": \"sla het huidige bestand op\",\n    \"SEARCH\": \"zoeken\",\n    \"SETTINGS\": \"instellingen\",\n    \"SHARE\": \"delen\",\n    \"SHARED_DRIVE\": \"gedeelde schijf\",\n    \"SKIP_TO_CONTENT\": \"Ga naar inhoud\",\n    \"SORT\": \"sorteren\",\n    \"SORT_BY_DATE\": \"sorteren op datum\",\n    \"SORT_BY_NAME\": \"sorteren op naam\",\n    \"SORT_BY_SIZE\": \"sorteren op grootte\",\n    \"SORT_BY_TYPE\": \"sorteer op type\",\n    \"STARRED\": \"met ster\",\n    \"SUPPORT\": \"ondersteuning\",\n    \"TAG\": \"tag\",\n    \"TAGS\": \"tags\",\n    \"THERE_IS_NOTHING_HERE\": \"er is niks hier\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"het bestand {{VALUE}} is verwijderd\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"het bestand {{VALUE}} is hernoemd\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"de link is gekopieerd naar het klembord\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"de link is daarna niet meer geldig\",\n    \"TIMEOUT\": \"tijdslimiet\",\n    \"TODO\": \"Te doen\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"verkeersopstoppingen, probeer het later opnieuw\",\n    \"UPLOAD\": \"uploaden\",\n    \"UPLOADER\": \"uploader\",\n    \"USERNAME\": \"gebruikersnaam\",\n    \"VERSION\": \"versie\",\n    \"VIEWER\": \"weergave\",\n    \"WAITING\": \"aan het wachten\",\n    \"YES\": \"Ja\",\n    \"YOUR_EMAIL_ADDRESS\": \"jouw e-mailadres\",\n    \"YOUR_FILES\": \"jouw bestanden\",\n    \"YOUR_MASTER_PASSWORD\": \"uw hoofdwachtwoord\",\n    \"YOU_CANT_DO_THAT\": \"dat kun je niet doen\"\n}\n"
  },
  {
    "path": "public/assets/locales/no.json",
    "content": "{\n    \"ABORTED\": \"Avbrutt\",\n    \"ABORT_CURRENT_UPLOADS?\": \"Avbryt pågående opplastinger?\",\n    \"ACTIVITY\": \"Aktivitet\",\n    \"ADVANCED\": \"Avansert\",\n    \"ALL_DONE\": \"Alt er klart!\",\n    \"ALREADY_EXIST\": \"finnes allerede\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"En fil med navn \\\"{{VALUE}}\\\" ble opprettet\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"En mappe med navn \\\"{{VALUE}}\\\" ble opprettet\",\n    \"BEAUTIFUL_URL\": \"pen_url\",\n    \"BOOKMARK\": \"Bokmerke\",\n    \"CAMERA\": \"Kamera\",\n    \"CANCEL\": \"Avbryt\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"Kan ikke opprette en tilkobling\",\n    \"CANT_LOAD_THIS_PICTURE\": \"Kan ikke laste inn dette bildet\",\n    \"CANT_USE_FILESYSTEM\": \"Kan ikke bruke filsystemet\",\n    \"CAN_RESHARE\": \"Kan dele videre\",\n    \"CODE\": \"Kode\",\n    \"CONFIGURE\": \"Konfigurer\",\n    \"CONFIRM_BY_TYPING\": \"Bekreft ved å skrive\",\n    \"CONNECT\": \"Koble til\",\n    \"CONNECTION_LOST\": \"Tilkobling brutt\",\n    \"COPIED_TO_CLIPBOARD\": \"Kopiert til utklippstavlen\",\n    \"CREATE_A_NEW_LINK\": \"Opprett en ny delingslenke\",\n    \"CREATE_A_TAG\": \"Opprett en merkelapp\",\n    \"CURRENT\": \"Gjeldende\",\n    \"CURRENT_UPLOAD\": \"Pågående opplastning\",\n    \"CUSTOM_LINK_URL\": \"Tilpasset lenke-URL\",\n    \"DASHBOARD\": \"Oversikt\",\n    \"DATE\": \"Dato\",\n    \"DISPLAY_HIDDEN_FILES\": \"Vis skjulte filer\",\n    \"DOESNT_MATCH\": \"Matcher ikke\",\n    \"DONE\": \"Ferdig\",\n    \"DOWNLOAD\": \"Last ned\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"Vil du lagre endringene?\",\n    \"DROP_HERE_TO_UPLOAD\": \"Dra og slipp filer og mapper her for å laste opp\",\n    \"EDITOR\": \"Redigerer\",\n    \"EMBED\": \"Bygg inn\",\n    \"EMPTY\": \"Tom\",\n    \"ENCRYPTION_KEY\": \"Krypteringsnøkkel\",\n    \"ENDPOINT\": \"Endepunkt\",\n    \"ERROR\": \"Feil\",\n    \"EXISTING_LINKS\": \"Eksisterende lenker\",\n    \"EXPIRATION\": \"Utløpstid\",\n    \"EXPORT_AS_{{VALUE}}\": \"Eksporter som {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"Ofte brukte mapper vises her\",\n    \"HIDE_HIDDEN_FILES\": \"Skjul skjulte filer\",\n    \"HOME\": \"Hjem\",\n    \"HOSTNAME*\": \"Vertsnavn*\",\n    \"HOST_KEY\": \"Verts­nøkkel\",\n    \"INCORRECT_PASSWORD\": \"Feil passord\",\n    \"INFO\": \"Informasjon\",\n    \"INTERNAL_ERROR\": \"Intern feil\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"Intern feil: Kan ikke opprette {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"Ugyldig konto\",\n    \"INVALID_PASSWORD\": \"Ugyldig passord\",\n    \"LAYOUT\": \"Oppsett\",\n    \"LOADING\": \"Laster\",\n    \"LOCATION\": \"Plassering\",\n    \"MISSING_DEPENDENCY\": \"Mangler avhengighet\",\n    \"MORE_DETAILS\": \"Mer informasjon\",\n    \"NAVIGATE\": \"Naviger\",\n    \"NEW_FILE\": \"Ny fil\",\n    \"NEW_FILE::SHORT\": \"Ny fil\",\n    \"NEW_FOLDER\": \"Ny mappe\",\n    \"NEW_FOLDER::SHORT\": \"Ny mappe\",\n    \"NO\": \"Nei\",\n    \"NOT_ALLOWED\": \"Ikke tillatt\",\n    \"NOT_AUTHORISED\": \"Ikke autorisert\",\n    \"NOT_FOUND\": \"Ikke funnet\",\n    \"NOT_IMPLEMENTED\": \"Ikke implementert\",\n    \"NOT_SUPPORTED\": \"Ikke støttet\",\n    \"NOT_VALID\": \"Ikke gyldig\",\n    \"NUMBER_OF_CONNECTIONS\": \"Antall tilkoblinger\",\n    \"OK\": \"OK\",\n    \"ONLY_FOR_USERS\": \"Kun for bestemte brukere\",\n    \"OOPS\": \"Oops!\",\n    \"PASSPHRASE\": \"Passordfrase\",\n    \"PASSWORD\": \"Passord\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"Passordet kan ikke være tomt\",\n    \"PATH\": \"Sti\",\n    \"PERMISSION_DENIED\": \"Ingen tilgang\",\n    \"PICK_A_MASTER_PASSWORD\": \"Velg et hovedpassord\",\n    \"PORT\": \"Port\",\n    \"POWERED_BY\": \"Drevet av\",\n    \"PROPERTIES\": \"Egenskaper\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"Beskytt tilgangen med passord\",\n    \"QUICK_ACCESS\": \"Hurtigtilgang\",\n    \"REGION\": \"Region\",\n    \"REMEMBER_ME\": \"Husk meg\",\n    \"REMOVE\": \"Fjern\",\n    \"RENAME\": \"Gi nytt navn\",\n    \"RENAME_AS\": \"Gi nytt navn som\",\n    \"RESTRICTIONS\": \"Begrensninger\",\n    \"RUNNING\": \"Kjører\",\n    \"SAVE_CURRENT_FILE\": \"Lagre gjeldende fil\",\n    \"SEARCH\": \"Søk\",\n    \"SETTINGS\": \"Innstillinger\",\n    \"SHARE\": \"Del\",\n    \"SHARED_DRIVE\": \"Delt stasjon\",\n    \"SKIP_TO_CONTENT\": \"Gå til innhold\",\n    \"SORT\": \"Sorter\",\n    \"SORT_BY_DATE\": \"Sorter etter dato\",\n    \"SORT_BY_NAME\": \"Sorter etter navn\",\n    \"SORT_BY_SIZE\": \"Sorter etter størrelse\",\n    \"SORT_BY_TYPE\": \"Sorter etter type\",\n    \"STARRED\": \"Favoritter\",\n    \"SUPPORT\": \"Støtte\",\n    \"TAG\": \"Tag\",\n    \"TAGS\": \"Tagger\",\n    \"THERE_IS_NOTHING_HERE\": \"Det er ingenting her\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"Filen \\\"{{VALUE}}\\\" ble slettet\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"Filen \\\"{{VALUE}}\\\" ble gitt nytt navn\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"Lenken ble kopiert til utklippstavlen\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"Lenken blir ugyldig etter\",\n    \"TIMEOUT\": \"Tidsavbrudd\",\n    \"TODO\": \"Gjøremål\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"Høy trafikk, prøv igjen senere\",\n    \"UPLOAD\": \"Last opp\",\n    \"UPLOADER\": \"Opplaster\",\n    \"USERNAME\": \"Brukernavn\",\n    \"VERSION\": \"Versjon\",\n    \"VIEWER\": \"Viser\",\n    \"WAITING\": \"Venter\",\n    \"YES\": \"Ja\",\n    \"YOUR_EMAIL_ADDRESS\": \"Din e-postadresse\",\n    \"YOUR_FILES\": \"Dine filer\",\n    \"YOUR_MASTER_PASSWORD\": \"Ditt hovedpassord\",\n    \"YOU_CANT_DO_THAT\": \"Du kan ikke gjøre det\"\n}\n"
  },
  {
    "path": "public/assets/locales/pl.json",
    "content": "{\n    \"ABORTED\": \"anulowano\",\n    \"ABORT_CURRENT_UPLOADS?\": \"Przerwać przesyłanie plików?\",\n    \"ACTIVITY\": \"Aktywność\",\n    \"ADVANCED\": \"Zaawansowane\",\n    \"ALL_DONE\": \"Wszystko zrobione\",\n    \"ALREADY_EXIST\": \"już istnieje\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Utworzono plik o nazwie {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Utworzono katalog o nazwie {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"zakładka\",\n    \"CAMERA\": \"Kamera\",\n    \"CANCEL\": \"Anuluj\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"Nie można nawiązać połączenia\",\n    \"CANT_LOAD_THIS_PICTURE\": \"Nie można załadować tego zdjęcia\",\n    \"CANT_USE_FILESYSTEM\": \"Nie można odczytać systemu plików\",\n    \"CAN_RESHARE\": \"można udostępnić dalej\",\n    \"CODE\": \"Kod\",\n    \"CONFIGURE\": \"Skonfiguruj\",\n    \"CONFIRM_BY_TYPING\": \"Potwierdź operację, pisząc\",\n    \"CONNECT\": \"Połącz\",\n    \"CONNECTION_LOST\": \"utracono połączenie\",\n    \"COPIED_TO_CLIPBOARD\": \"Skopionowano do schowka\",\n    \"CREATE_A_NEW_LINK\": \"Utwórz nowe łącze\",\n    \"CREATE_A_TAG\": \"utwórz tag\",\n    \"CURRENT\": \"Aktualny\",\n    \"CURRENT_UPLOAD\": \"bieżące przesyłanie\",\n    \"CUSTOM_LINK_URL\": \"niestandardowy adres łącza\",\n    \"DASHBOARD\": \"Kokpit\",\n    \"DATE\": \"Data\",\n    \"DISPLAY_HIDDEN_FILES\": \"Pokaż ukryte pliki\",\n    \"DOESNT_MATCH\": \"nie pasuje\",\n    \"DONE\": \"gotowy\",\n    \"DOWNLOAD\": \"Pobierz\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"Czy chcesz zapisać zmiany\",\n    \"DROP_HERE_TO_UPLOAD\": \"upuść tutaj, aby przesłać\",\n    \"EDITOR\": \"Edycja\",\n    \"EMBED\": \"osadzić\",\n    \"EMPTY\": \"pusty\",\n    \"ENCRYPTION_KEY\": \"Klucz szyfrujący\",\n    \"ENDPOINT\": \"Punkt końcowy\",\n    \"ERROR\": \"Błąd\",\n    \"EXISTING_LINKS\": \"istniejące linki\",\n    \"EXPIRATION\": \"Wygaśnięcie\",\n    \"EXPORT_AS_{{VALUE}}\": \"Eksportuj jako {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"Często używane katalogi będą widoczne tutaj\",\n    \"HIDE_HIDDEN_FILES\": \"Ukryj pliki ukryte\",\n    \"HOME\": \"start\",\n    \"HOSTNAME*\": \"Nazwa hosta\",\n    \"HOST_KEY\": \"Klucz hosta\",\n    \"INCORRECT_PASSWORD\": \"niepoprawne hasło\",\n    \"INFO\": \"Informacje\",\n    \"INTERNAL_ERROR\": \"Błąd wewnętrzny\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"Błąd wewnętrzny: nie można utworzyć {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"nieważne konto\",\n    \"INVALID_PASSWORD\": \"Nieprawidłowe hasło\",\n    \"LAYOUT\": \"układ\",\n    \"LOADING\": \"ładowanie\",\n    \"LOCATION\": \"Lokalizacja\",\n    \"MISSING_DEPENDENCY\": \"Brakujące zależności\",\n    \"MORE_DETAILS\": \"szczegóły\",\n    \"NAVIGATE\": \"nawiguj\",\n    \"NEW_FILE\": \"nowy plik\",\n    \"NEW_FILE::SHORT\": \"nowy plik\",\n    \"NEW_FOLDER\": \"nowy folder\",\n    \"NEW_FOLDER::SHORT\": \"nowy katalog\",\n    \"NO\": \"Nie\",\n    \"NOT_ALLOWED\": \"niedozwolony\",\n    \"NOT_AUTHORISED\": \"nieautoryzowany\",\n    \"NOT_FOUND\": \"nie znaleziono\",\n    \"NOT_IMPLEMENTED\": \"nie zaimplementowano\",\n    \"NOT_SUPPORTED\": \"niewspierany\",\n    \"NOT_VALID\": \"niepoprawny\",\n    \"NUMBER_OF_CONNECTIONS\": \"Liczba połączeń\",\n    \"OK\": \"Ok\",\n    \"ONLY_FOR_USERS\": \"Tylko dla użytkowników\",\n    \"OOPS\": \"Ups\",\n    \"PASSPHRASE\": \"Hasło\",\n    \"PASSWORD\": \"Hasło\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"Hasło nie może być puste\",\n    \"PATH\": \"ścieżka\",\n    \"PERMISSION_DENIED\": \"Odmowa dostępu\",\n    \"PICK_A_MASTER_PASSWORD\": \"Wybierz hasło główne\",\n    \"PORT\": \"Port\",\n    \"POWERED_BY\": \"Działa dzięki\",\n    \"PROPERTIES\": \"Właściwości\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"Chroń dostęp hasłem\",\n    \"QUICK_ACCESS\": \"szybki dostęp\",\n    \"REGION\": \"Region\",\n    \"REMEMBER_ME\": \"Zapamiętaj mnie\",\n    \"REMOVE\": \"Usuń\",\n    \"RENAME\": \"zmień nazwę\",\n    \"RENAME_AS\": \"zmień nazwę na\",\n    \"RESTRICTIONS\": \"Ograniczenia\",\n    \"RUNNING\": \"Uruchomione\",\n    \"SAVE_CURRENT_FILE\": \"Zapisz otwarty plik\",\n    \"SEARCH\": \"Szukaj\",\n    \"SETTINGS\": \"Ustawienia\",\n    \"SHARE\": \"udostępnij\",\n    \"SHARED_DRIVE\": \"wspólny dysk\",\n    \"SKIP_TO_CONTENT\": \"Przejdź do treści\",\n    \"SORT\": \"sortuj\",\n    \"SORT_BY_DATE\": \"Sortuj według daty\",\n    \"SORT_BY_NAME\": \"sortuj według nazwy\",\n    \"SORT_BY_SIZE\": \"sortuj według rozmiaru\",\n    \"SORT_BY_TYPE\": \"Sortuj według typu\",\n    \"STARRED\": \"oznaczone gwiazdką\",\n    \"SUPPORT\": \"Wsparcie\",\n    \"TAG\": \"tag\",\n    \"TAGS\": \"tagi\",\n    \"THERE_IS_NOTHING_HERE\": \"Nic tutaj nie ma\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"Obiekt {{VALUE}} został usunięty\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"Nazwa obiektu {{VALUE}} została zmieniona\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"Łącze zostało skopiowane do schowka\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"Łącze wygaśnie po\",\n    \"TIMEOUT\": \"Koniec czasu\",\n    \"TODO\": \"Do zrobienia\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"Spowolnienie sieci, spróbuj ponownie później\",\n    \"UPLOAD\": \"prześlij\",\n    \"UPLOADER\": \"Przesyłający\",\n    \"USERNAME\": \"Nazwa użytkownika\",\n    \"VERSION\": \"wersja\",\n    \"VIEWER\": \"Odczyt\",\n    \"WAITING\": \"Oczekiwanie\",\n    \"YES\": \"Tak\",\n    \"YOUR_EMAIL_ADDRESS\": \"Twój adres e-mail\",\n    \"YOUR_FILES\": \"twoje pliki\",\n    \"YOUR_MASTER_PASSWORD\": \"Twoje hasło główne\",\n    \"YOU_CANT_DO_THAT\": \"Nie możesz tego zrobić\"\n}\n"
  },
  {
    "path": "public/assets/locales/pt.json",
    "content": "{\n    \"ABORTED\": \"abortado\",\n    \"ABORT_CURRENT_UPLOADS?\": \"abortar upload atual?\",\n    \"ACTIVITY\": \"Atividade\",\n    \"ADVANCED\": \"avançado\",\n    \"ALL_DONE\": \"tudo feito\",\n    \"ALREADY_EXIST\": \"já existe\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Um arquivo chamado {{VALUE}} foi criado\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Uma pasta chamada {{VALUE}} foi criada\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"marcador\",\n    \"CAMERA\": \"Câmera\",\n    \"CANCEL\": \"cancelar\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"não pode estabelecer uma conexão\",\n    \"CANT_LOAD_THIS_PICTURE\": \"não consigo carregar esta foto\",\n    \"CANT_USE_FILESYSTEM\": \"pode usar sistema de arquivos\",\n    \"CAN_RESHARE\": \"pode compartilhar de novo\",\n    \"CODE\": \"código\",\n    \"CONFIGURE\": \"configurar\",\n    \"CONFIRM_BY_TYPING\": \"confirme digitando\",\n    \"CONNECT\": \"conectar\",\n    \"CONNECTION_LOST\": \"conexão perdida\",\n    \"COPIED_TO_CLIPBOARD\": \"Copiado para a área de transferência\",\n    \"CREATE_A_NEW_LINK\": \"crie um novo link\",\n    \"CREATE_A_TAG\": \"criar uma etiqueta\",\n    \"CURRENT\": \"atual\",\n    \"CURRENT_UPLOAD\": \"upload atual\",\n    \"CUSTOM_LINK_URL\": \"URL de link personalizado\",\n    \"DASHBOARD\": \"painel de controle\",\n    \"DATE\": \"encontro\",\n    \"DISPLAY_HIDDEN_FILES\": \"exibir arquivos ocultos\",\n    \"DOESNT_MATCH\": \"não corresponde\",\n    \"DONE\": \"feito\",\n    \"DOWNLOAD\": \"baixar\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"você quer salvar as alterações\",\n    \"DROP_HERE_TO_UPLOAD\": \"solte aqui para fazer upload\",\n    \"EDITOR\": \"editor\",\n    \"EMBED\": \"incorporar\",\n    \"EMPTY\": \"esvaziar\",\n    \"ENCRYPTION_KEY\": \"Chave de encriptação\",\n    \"ENDPOINT\": \"ponto final\",\n    \"ERROR\": \"erro\",\n    \"EXISTING_LINKS\": \"links existentes\",\n    \"EXPIRATION\": \"expiração\",\n    \"EXPORT_AS_{{VALUE}}\": \"exportar como {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"As pastas de acesso frequente serão mostradas aqui\",\n    \"HIDE_HIDDEN_FILES\": \"ocultar arquivos ocultos\",\n    \"HOME\": \"início\",\n    \"HOSTNAME*\": \"nome de host*\",\n    \"HOST_KEY\": \"chave do host\",\n    \"INCORRECT_PASSWORD\": \"senha incorreta\",\n    \"INFO\": \"informação\",\n    \"INTERNAL_ERROR\": \"Erro interno\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"erro interno: não é possível criar um {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"conta inválida\",\n    \"INVALID_PASSWORD\": \"senha inválida\",\n    \"LAYOUT\": \"layout\",\n    \"LOADING\": \"carregando\",\n    \"LOCATION\": \"localização\",\n    \"MISSING_DEPENDENCY\": \"falta de dependência\",\n    \"MORE_DETAILS\": \"mais detalhes\",\n    \"NAVIGATE\": \"navegar\",\n    \"NEW_FILE\": \"novo arquivo\",\n    \"NEW_FILE::SHORT\": \"novo arquivo\",\n    \"NEW_FOLDER\": \"novo diretório\",\n    \"NEW_FOLDER::SHORT\": \"novo diret.\",\n    \"NO\": \"não\",\n    \"NOT_ALLOWED\": \"não permitido\",\n    \"NOT_AUTHORISED\": \"não autorizado\",\n    \"NOT_FOUND\": \"não encontrado\",\n    \"NOT_IMPLEMENTED\": \"não implementado\",\n    \"NOT_SUPPORTED\": \"não suportado\",\n    \"NOT_VALID\": \"inválido\",\n    \"NUMBER_OF_CONNECTIONS\": \"número de conexões\",\n    \"OK\": \"Está bem\",\n    \"ONLY_FOR_USERS\": \"Somente para usuários\",\n    \"OOPS\": \"Opa\",\n    \"PASSPHRASE\": \"frase secreta\",\n    \"PASSWORD\": \"senha\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"a senha não pode estar vazia\",\n    \"PATH\": \"caminho\",\n    \"PERMISSION_DENIED\": \"permissão negada\",\n    \"PICK_A_MASTER_PASSWORD\": \"escolha uma senha mestra\",\n    \"PORT\": \"porta\",\n    \"POWERED_BY\": \"distribuído por\",\n    \"PROPERTIES\": \"propriedades\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"proteger o acesso com uma senha\",\n    \"QUICK_ACCESS\": \"acesso rápido\",\n    \"REGION\": \"região\",\n    \"REMEMBER_ME\": \"lembre de mim\",\n    \"REMOVE\": \"retirar\",\n    \"RENAME\": \"renomear\",\n    \"RENAME_AS\": \"renomear como\",\n    \"RESTRICTIONS\": \"restrições\",\n    \"RUNNING\": \"corrida\",\n    \"SAVE_CURRENT_FILE\": \"salvar arquivo atual\",\n    \"SEARCH\": \"procurar\",\n    \"SETTINGS\": \"definições\",\n    \"SHARE\": \"compartilhar\",\n    \"SHARED_DRIVE\": \"unidade compartilhada\",\n    \"SKIP_TO_CONTENT\": \"Pular para o conteúdo\",\n    \"SORT\": \"ordenar\",\n    \"SORT_BY_DATE\": \"classificar por data\",\n    \"SORT_BY_NAME\": \"ordenar por nome\",\n    \"SORT_BY_SIZE\": \"ordenar por tamanho\",\n    \"SORT_BY_TYPE\": \"ordenar por tipo\",\n    \"STARRED\": \"marcado com estrela\",\n    \"SUPPORT\": \"Apoio, suporte\",\n    \"TAG\": \"etiqueta\",\n    \"TAGS\": \"etiquetas\",\n    \"THERE_IS_NOTHING_HERE\": \"Não há nada aqui\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"o arquivo {{VALUE}} foi excluído\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"o arquivo {{VALUE}} foi renomeado\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"o link foi copiado na área de transferência\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"o link não será válido depois\",\n    \"TIMEOUT\": \"tempo esgotado\",\n    \"TODO\": \"façam\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"congestionamento de tráfego, tente novamente mais tarde\",\n    \"UPLOAD\": \"carregar\",\n    \"UPLOADER\": \"remetente\",\n    \"USERNAME\": \"nome do usuário\",\n    \"VERSION\": \"versão\",\n    \"VIEWER\": \"espectador\",\n    \"WAITING\": \"esperando\",\n    \"YES\": \"sim\",\n    \"YOUR_EMAIL_ADDRESS\": \"Seu endereço de email\",\n    \"YOUR_FILES\": \"seus arquivos\",\n    \"YOUR_MASTER_PASSWORD\": \"sua senha mestra\",\n    \"YOU_CANT_DO_THAT\": \"você não pode fazer isso\"\n}\n"
  },
  {
    "path": "public/assets/locales/ro.json",
    "content": "{\n    \"ABORTED\": \"avortat\",\n    \"ABORT_CURRENT_UPLOADS?\": \"anula încărcarea curentă?\",\n    \"ACTIVITY\": \"Activitate\",\n    \"ADVANCED\": \"avansat\",\n    \"ALL_DONE\": \"totul este gata\",\n    \"ALREADY_EXIST\": \"Există deja\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"A fost creat un fișier numit {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"A fost creat un folder numit {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"semn de carte\",\n    \"CAMERA\": \"aparat foto\",\n    \"CANCEL\": \"Anulare\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"nu poate stabili o conexiune\",\n    \"CANT_LOAD_THIS_PICTURE\": \"nu pot încărca această imagine\",\n    \"CANT_USE_FILESYSTEM\": \"poate utiliza sistemul de fișiere\",\n    \"CAN_RESHARE\": \"se poate reîncărca\",\n    \"CODE\": \"cod\",\n    \"CONFIGURE\": \"Configureaza\",\n    \"CONFIRM_BY_TYPING\": \"confirmați tastând\",\n    \"CONNECT\": \"conectați\",\n    \"CONNECTION_LOST\": \"conexiune pierdută\",\n    \"COPIED_TO_CLIPBOARD\": \"copiat în clipboard\",\n    \"CREATE_A_NEW_LINK\": \"creați o nouă legătură\",\n    \"CREATE_A_TAG\": \"creează o etichetă\",\n    \"CURRENT\": \"actual\",\n    \"CURRENT_UPLOAD\": \"încărcare actuală\",\n    \"CUSTOM_LINK_URL\": \"URL-ul linkului personalizat\",\n    \"DASHBOARD\": \"tablou de bord\",\n    \"DATE\": \"Data\",\n    \"DISPLAY_HIDDEN_FILES\": \"afișează fișiere ascunse\",\n    \"DOESNT_MATCH\": \"nu se potriveste\",\n    \"DONE\": \"Terminat\",\n    \"DOWNLOAD\": \"Descarca\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"doriți să salvați modificările\",\n    \"DROP_HERE_TO_UPLOAD\": \"picătură aici pentru a încărca\",\n    \"EDITOR\": \"editor\",\n    \"EMBED\": \"încorporează\",\n    \"EMPTY\": \"gol\",\n    \"ENCRYPTION_KEY\": \"cheie de criptare\",\n    \"ENDPOINT\": \"punct final\",\n    \"ERROR\": \"eroare\",\n    \"EXISTING_LINKS\": \"legături existente\",\n    \"EXPIRATION\": \"expirare\",\n    \"EXPORT_AS_{{VALUE}}\": \"export ca {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"frecvent, folderele de acces vor fi afișate aici\",\n    \"HIDE_HIDDEN_FILES\": \"ascunde fișiere ascunse\",\n    \"HOME\": \"acasă\",\n    \"HOSTNAME*\": \"nume de gazdă\",\n    \"HOST_KEY\": \"cheia de gazdă\",\n    \"INCORRECT_PASSWORD\": \"parola incorecta\",\n    \"INFO\": \"info\",\n    \"INTERNAL_ERROR\": \"Eroare interna\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"eroare internă: nu se poate crea o {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"cont invalid\",\n    \"INVALID_PASSWORD\": \"Parolă Invalidă\",\n    \"LAYOUT\": \"aranjament\",\n    \"LOADING\": \"încărcare\",\n    \"LOCATION\": \"Locație\",\n    \"MISSING_DEPENDENCY\": \"lipsă de dependență\",\n    \"MORE_DETAILS\": \"mai multe detalii\",\n    \"NAVIGATE\": \"Naviga\",\n    \"NEW_FILE\": \"nou fișier\",\n    \"NEW_FILE::SHORT\": \"nou fișier\",\n    \"NEW_FOLDER\": \"director nou\",\n    \"NEW_FOLDER::SHORT\": \"director nou\",\n    \"NO\": \"Nu\",\n    \"NOT_ALLOWED\": \"nepermis\",\n    \"NOT_AUTHORISED\": \"neautorizat\",\n    \"NOT_FOUND\": \"nu a fost gasit\",\n    \"NOT_IMPLEMENTED\": \"neimplementat\",\n    \"NOT_SUPPORTED\": \"nu sunt acceptate\",\n    \"NOT_VALID\": \"invalid\",\n    \"NUMBER_OF_CONNECTIONS\": \"numărul de conexiuni\",\n    \"OK\": \"O.K\",\n    \"ONLY_FOR_USERS\": \"Numai pentru utilizatori\",\n    \"OOPS\": \"Hopa\",\n    \"PASSPHRASE\": \"expresia de acces\",\n    \"PASSWORD\": \"parola\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"parola nu poate fi goală\",\n    \"PATH\": \"cale\",\n    \"PERMISSION_DENIED\": \"acces refuzat\",\n    \"PICK_A_MASTER_PASSWORD\": \"alege o parolă principală\",\n    \"PORT\": \"port\",\n    \"POWERED_BY\": \"cu sprijinul\",\n    \"PROPERTIES\": \"proprietăţi\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"protejați accesul cu o parolă\",\n    \"QUICK_ACCESS\": \"acces rapid\",\n    \"REGION\": \"regiune\",\n    \"REMEMBER_ME\": \"amintește-ți de mine\",\n    \"REMOVE\": \"elimina\",\n    \"RENAME\": \"redenumește\",\n    \"RENAME_AS\": \"redenumește ca\",\n    \"RESTRICTIONS\": \"restricţii\",\n    \"RUNNING\": \"rulând\",\n    \"SAVE_CURRENT_FILE\": \"salvați fișierul curent\",\n    \"SEARCH\": \"căutare\",\n    \"SETTINGS\": \"setări\",\n    \"SHARE\": \"partajează\",\n    \"SHARED_DRIVE\": \"unitate partajată\",\n    \"SKIP_TO_CONTENT\": \"Salt la conținut\",\n    \"SORT\": \"sortează\",\n    \"SORT_BY_DATE\": \"sortati dupa data\",\n    \"SORT_BY_NAME\": \"sorteaza dupa nume\",\n    \"SORT_BY_SIZE\": \"sortează după dimensiune\",\n    \"SORT_BY_TYPE\": \"sortează după tip\",\n    \"STARRED\": \"favorite\",\n    \"SUPPORT\": \"suport\",\n    \"TAG\": \"etichetă\",\n    \"TAGS\": \"etichete\",\n    \"THERE_IS_NOTHING_HERE\": \"nu este nimic aici\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"fișierul {{VALUE}} a ​​fost șters\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"fișierul {{VALUE}} a ​​fost redenumit\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"linkul a fost copiat în clipboard\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"link-ul nu va fi valabil după\",\n    \"TIMEOUT\": \"pauză\",\n    \"TODO\": \"a face\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"congestionarea traficului, încercați din nou mai târziu\",\n    \"UPLOAD\": \"încarcă\",\n    \"UPLOADER\": \"uploader\",\n    \"USERNAME\": \"nume de utilizator\",\n    \"VERSION\": \"versiune\",\n    \"VIEWER\": \"vizualizator\",\n    \"WAITING\": \"aşteptare\",\n    \"YES\": \"da\",\n    \"YOUR_EMAIL_ADDRESS\": \"adresa ta de email\",\n    \"YOUR_FILES\": \"fișierele tale\",\n    \"YOUR_MASTER_PASSWORD\": \"parola de master\",\n    \"YOU_CANT_DO_THAT\": \"nu poți face asta\"\n}\n"
  },
  {
    "path": "public/assets/locales/ru.json",
    "content": "{\n    \"ABORTED\": \"прерванный\",\n    \"ABORT_CURRENT_UPLOADS?\": \"прервать текущую загрузку?\",\n    \"ACTIVITY\": \"Деятельность\",\n    \"ADVANCED\": \"передовой\",\n    \"ALL_DONE\": \"все сделано\",\n    \"ALREADY_EXIST\": \"уже существует\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Файл с именем {{VALUE}} создан\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Папка с именем {{VALUE}} была создана\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"закладка\",\n    \"CAMERA\": \"камера\",\n    \"CANCEL\": \"Отмена\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"не может установить соединение\",\n    \"CANT_LOAD_THIS_PICTURE\": \"не могу загрузить эту картинку\",\n    \"CANT_USE_FILESYSTEM\": \"не удается использовать файловую систему\",\n    \"CAN_RESHARE\": \"можно поделиться\",\n    \"CODE\": \"код\",\n    \"CONFIGURE\": \"настроить\",\n    \"CONFIRM_BY_TYPING\": \"подтвердить, набрав\",\n    \"CONNECT\": \"подключения\",\n    \"CONNECTION_LOST\": \"соединение потеряно\",\n    \"COPIED_TO_CLIPBOARD\": \"скопировано в буфер обмена\",\n    \"CREATE_A_NEW_LINK\": \"создать новую ссылку\",\n    \"CREATE_A_TAG\": \"создать тег\",\n    \"CURRENT\": \"текущий\",\n    \"CURRENT_UPLOAD\": \"текущая загрузка\",\n    \"CUSTOM_LINK_URL\": \"URL пользовательской ссылки\",\n    \"DASHBOARD\": \"приборная панель\",\n    \"DATE\": \"свидание\",\n    \"DISPLAY_HIDDEN_FILES\": \"отображать скрытые файлы\",\n    \"DOESNT_MATCH\": \"не совпадает\",\n    \"DONE\": \"сделанный\",\n    \"DOWNLOAD\": \"скачать\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"Вы хотите сохранить изменения?\",\n    \"DROP_HERE_TO_UPLOAD\": \"сюда, чтобы загрузить\",\n    \"EDITOR\": \"редактор\",\n    \"EMBED\": \"встроить\",\n    \"EMPTY\": \"опорожнить\",\n    \"ENCRYPTION_KEY\": \"ключ шифрования\",\n    \"ENDPOINT\": \"конечная точка\",\n    \"ERROR\": \"ошибка\",\n    \"EXISTING_LINKS\": \"существующие ссылки\",\n    \"EXPIRATION\": \"истечение\",\n    \"EXPORT_AS_{{VALUE}}\": \"экспортировать как {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"папки с часто используемым доступом будут показаны здесь\",\n    \"HIDE_HIDDEN_FILES\": \"скрыть скрытые файлы\",\n    \"HOME\": \"главная\",\n    \"HOSTNAME*\": \"имя хоста\",\n    \"HOST_KEY\": \"ключ хоста\",\n    \"INCORRECT_PASSWORD\": \"неверный пароль\",\n    \"INFO\": \"Информация\",\n    \"INTERNAL_ERROR\": \"Внутренняя ошибка\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"внутренняя ошибка: невозможно создать {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"неверный аккаунт\",\n    \"INVALID_PASSWORD\": \"Неверный пароль\",\n    \"LAYOUT\": \"макет\",\n    \"LOADING\": \"загрузка\",\n    \"LOCATION\": \"расположение\",\n    \"MISSING_DEPENDENCY\": \"отсутствует зависимость\",\n    \"MORE_DETAILS\": \"подробности\",\n    \"NAVIGATE\": \"навигации\",\n    \"NEW_FILE\": \"новый файл\",\n    \"NEW_FILE::SHORT\": \"новый файл\",\n    \"NEW_FOLDER\": \"новый каталог\",\n    \"NEW_FOLDER::SHORT\": \"новый каталог\",\n    \"NO\": \"нет\",\n    \"NOT_ALLOWED\": \"не положено\",\n    \"NOT_AUTHORISED\": \"не авторизован\",\n    \"NOT_FOUND\": \"не найдено\",\n    \"NOT_IMPLEMENTED\": \"не реализована\",\n    \"NOT_SUPPORTED\": \"не поддерживается\",\n    \"NOT_VALID\": \"недействительный\",\n    \"NUMBER_OF_CONNECTIONS\": \"количество соединений\",\n    \"OK\": \"Хорошо\",\n    \"ONLY_FOR_USERS\": \"Только для пользователей\",\n    \"OOPS\": \"ой\",\n    \"PASSPHRASE\": \"ключевая фраза\",\n    \"PASSWORD\": \"пароль\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"пароль не может быть пустым\",\n    \"PATH\": \"дорожка\",\n    \"PERMISSION_DENIED\": \"в правах отказано\",\n    \"PICK_A_MASTER_PASSWORD\": \"выберите мастер-пароль\",\n    \"PORT\": \"порт\",\n    \"POWERED_BY\": \"работает на основе\",\n    \"PROPERTIES\": \"свойства\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"защитить доступ паролем\",\n    \"QUICK_ACCESS\": \"быстрый доступ\",\n    \"REGION\": \"область\",\n    \"REMEMBER_ME\": \"Запомни меня\",\n    \"REMOVE\": \"Удалить\",\n    \"RENAME\": \"переименовать\",\n    \"RENAME_AS\": \"переименовать как\",\n    \"RESTRICTIONS\": \"ограничения\",\n    \"RUNNING\": \"выполняется\",\n    \"SAVE_CURRENT_FILE\": \"сохранить текущий файл\",\n    \"SEARCH\": \"поиск\",\n    \"SETTINGS\": \"настройки\",\n    \"SHARE\": \"поделиться\",\n    \"SHARED_DRIVE\": \"общий диск\",\n    \"SKIP_TO_CONTENT\": \"Перейти к содержимому\",\n    \"SORT\": \"сортировать\",\n    \"SORT_BY_DATE\": \"сортировать по дате\",\n    \"SORT_BY_NAME\": \"сортировать по имени\",\n    \"SORT_BY_SIZE\": \"сортировать по размеру\",\n    \"SORT_BY_TYPE\": \"сортировать по типу\",\n    \"STARRED\": \"избранное\",\n    \"SUPPORT\": \"служба поддержки\",\n    \"TAG\": \"тег\",\n    \"TAGS\": \"теги\",\n    \"THERE_IS_NOTHING_HERE\": \"здесь ничего нет\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"файл {{VALUE}} был удален\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"файл {{VALUE}} был переименован\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"ссылка была скопирована в буфер обмена\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"ссылка не будет действительной после\",\n    \"TIMEOUT\": \"Тайм-аут\",\n    \"TODO\": \"список дел\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"пробка, попробуйте позже\",\n    \"UPLOAD\": \"загрузить\",\n    \"UPLOADER\": \"загрузчик\",\n    \"USERNAME\": \"имя пользователя\",\n    \"VERSION\": \"версия\",\n    \"VIEWER\": \"зритель\",\n    \"WAITING\": \"ожидания\",\n    \"YES\": \"да\",\n    \"YOUR_EMAIL_ADDRESS\": \"Ваш адрес электронной почты\",\n    \"YOUR_FILES\": \"ваши файлы\",\n    \"YOUR_MASTER_PASSWORD\": \"ваш мастер-пароль\",\n    \"YOU_CANT_DO_THAT\": \"ты не можешь сделать это\"\n}\n"
  },
  {
    "path": "public/assets/locales/script.js",
    "content": "import fs from \"fs\";\n\nfunction mainReorderKey(argv) {\n    const filepath = argv[2];\n    if (!filepath) throw new Error(\"missing args\");\n\n    let jsonStr = fs.readFileSync(filepath);\n    const res = {};\n    const obj = JSON.parse(jsonStr);\n    Object.keys(obj).sort()\n        .map((key) => res[key] = obj[key]);\n\n    jsonStr = JSON.stringify(res, null, 4);\n    fs.writeFileSync(filepath, jsonStr + \"\\n\");\n}\n\nfunction mainAddTranslationKey(argv) {\n    const key = argv[3];\n    const filepath = argv[2];\n    if (!filepath) throw new Error(\"missing args\");\n    else if (!key) return;\n\n    const json = JSON.parse(fs.readFileSync(filepath));\n    if (json[key] !== undefined) return;\n    json[key] = \"\";\n    fs.writeFileSync(filepath, JSON.stringify(json, null, 4) + \"\\n\");\n}\n\n// usage: find *.json -type f -exec node script.js {} \\;\n(function() {\n    mainAddTranslationKey(process.argv);\n    mainReorderKey(process.argv);\n})();\n"
  },
  {
    "path": "public/assets/locales/sk.json",
    "content": "{\n    \"ABORTED\": \"chybne\",\n    \"ABORT_CURRENT_UPLOADS?\": \"zrušiť aktuálne nahrávania?\",\n    \"ACTIVITY\": \"aktivita\",\n    \"ADVANCED\": \"pokročilé\",\n    \"ALL_DONE\": \"všetko hotové\",\n    \"ALREADY_EXIST\": \"už existuje\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Bol vytvorený súbor s názvom {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Bol vytvorený priečinok s názvom {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"pekná URL\",\n    \"BOOKMARK\": \"záložka\",\n    \"CAMERA\": \"fotoaparát\",\n    \"CANCEL\": \"zrušiť\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"nie je možné nadviazať spojenie\",\n    \"CANT_LOAD_THIS_PICTURE\": \"tento obrázok sa nedá načítať\",\n    \"CANT_USE_FILESYSTEM\": \"môže používať súborový systém\",\n    \"CAN_RESHARE\": \"môžu zdieľať\",\n    \"CODE\": \"kód\",\n    \"CONFIGURE\": \"konfigurovať\",\n    \"CONFIRM_BY_TYPING\": \"potvrďte zadaním\",\n    \"CONNECT\": \"pripojiť\",\n    \"CONNECTION_LOST\": \"spojenie stratené\",\n    \"COPIED_TO_CLIPBOARD\": \"skopírované do schránky\",\n    \"CREATE_A_NEW_LINK\": \"vytvoriť nový odkaz\",\n    \"CREATE_A_TAG\": \"vytvoriť značku\",\n    \"CURRENT\": \"aktuálne\",\n    \"CURRENT_UPLOAD\": \"aktuálne nahrávanie\",\n    \"CUSTOM_LINK_URL\": \"vlastný odkaz\",\n    \"DASHBOARD\": \"ovládací panel\",\n    \"DATE\": \"dátum\",\n    \"DISPLAY_HIDDEN_FILES\": \"zobraziť skryté súbory\",\n    \"DOESNT_MATCH\": \"nesedí\",\n    \"DONE\": \"hotovo\",\n    \"DOWNLOAD\": \"stiahnuť\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"chcete uložiť zmeny\",\n    \"DROP_HERE_TO_UPLOAD\": \"súbory nahráte presunutím sem\",\n    \"EDITOR\": \"upravovať\",\n    \"EMBED\": \"vložiť\",\n    \"EMPTY\": \"prázdne\",\n    \"ENCRYPTION_KEY\": \"šifrovací kľúč\",\n    \"ENDPOINT\": \"endpoint\",\n    \"ERROR\": \"chyba\",\n    \"EXISTING_LINKS\": \"existujúce odkazy\",\n    \"EXPIRATION\": \"expirácia\",\n    \"EXPORT_AS_{{VALUE}}\": \"exportovať ako {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"tu budú často používané priečinky\",\n    \"HIDE_HIDDEN_FILES\": \"skryť skryté súbory\",\n    \"HOME\": \"domov\",\n    \"HOSTNAME*\": \"hostname\",\n    \"HOST_KEY\": \"hostiteľský kľúč\",\n    \"INCORRECT_PASSWORD\": \"nesprávne heslo\",\n    \"INFO\": \"Info\",\n    \"INTERNAL_ERROR\": \"Interná chyba\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"interná chyba: nedá sa vytvoriť {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"nesprávny účet\",\n    \"INVALID_PASSWORD\": \"nesprávne heslo\",\n    \"LAYOUT\": \"rozloženie\",\n    \"LOADING\": \"načítava sa\",\n    \"LOCATION\": \"umiestnenie\",\n    \"MISSING_DEPENDENCY\": \"chýba závislosť\",\n    \"MORE_DETAILS\": \"viac podrobností\",\n    \"NAVIGATE\": \"navigácia\",\n    \"NEW_FILE\": \"nový súbor\",\n    \"NEW_FILE::SHORT\": \"nový súbor\",\n    \"NEW_FOLDER\": \"nový priečinok\",\n    \"NEW_FOLDER::SHORT\": \"nový priečinok\",\n    \"NO\": \"nie\",\n    \"NOT_ALLOWED\": \"nepovolené\",\n    \"NOT_AUTHORISED\": \"neautorizované\",\n    \"NOT_FOUND\": \"nenájdené\",\n    \"NOT_IMPLEMENTED\": \"neimplementované\",\n    \"NOT_SUPPORTED\": \"nepodporované\",\n    \"NOT_VALID\": \"neplatné\",\n    \"NUMBER_OF_CONNECTIONS\": \"počet pripojení\",\n    \"OK\": \"ok\",\n    \"ONLY_FOR_USERS\": \"Len pre prihlásených\",\n    \"OOPS\": \"ups\",\n    \"PASSPHRASE\": \"passphrase\",\n    \"PASSWORD\": \"heslo\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"heslo nemôže byť prázdne\",\n    \"PATH\": \"cesta\",\n    \"PERMISSION_DENIED\": \"prístup zamietnutý\",\n    \"PICK_A_MASTER_PASSWORD\": \"zvoľte hlavné heslo\",\n    \"PORT\": \"port\",\n    \"POWERED_BY\": \"postavené na\",\n    \"PROPERTIES\": \"vlastnosti\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"chrániť prístup heslom\",\n    \"QUICK_ACCESS\": \"rýchly prístup\",\n    \"REGION\": \"región\",\n    \"REMEMBER_ME\": \"Pamätať si ma\",\n    \"REMOVE\": \"odstrániť\",\n    \"RENAME\": \"premenovať\",\n    \"RENAME_AS\": \"premenovať ako\",\n    \"RESTRICTIONS\": \"obmedzenia\",\n    \"RUNNING\": \"Nahráva\",\n    \"SAVE_CURRENT_FILE\": \"uložiť aktuálny súbor\",\n    \"SEARCH\": \"Hľadať\",\n    \"SETTINGS\": \"Nastavenia\",\n    \"SHARE\": \"zdieľať\",\n    \"SHARED_DRIVE\": \"zdieľaná jednotka\",\n    \"SKIP_TO_CONTENT\": \"Prejsť na obsah\",\n    \"SORT\": \"zoradiť\",\n    \"SORT_BY_DATE\": \"zoradiť podľa dátumu\",\n    \"SORT_BY_NAME\": \"zoradiť podľa názvu\",\n    \"SORT_BY_SIZE\": \"zoradiť podľa veľkosti\",\n    \"SORT_BY_TYPE\": \"zoradiť podľa typu\",\n    \"STARRED\": \"označené hviezdičkou\",\n    \"SUPPORT\": \"podpora\",\n    \"TAG\": \"značka\",\n    \"TAGS\": \"značky\",\n    \"THERE_IS_NOTHING_HERE\": \"nič tu nie je\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"súbor {{VALUE}} bol odstránený\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"súbor {{VALUE}} bol premenovaný\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"odkaz bol skopírovaný do schránky\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"odkaz bude neplatný po\",\n    \"TIMEOUT\": \"čas vypršal\",\n    \"TODO\": \"spraviť\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"zahltená linka, skúste neskôr\",\n    \"UPLOAD\": \"nahrať\",\n    \"UPLOADER\": \"nahrávať\",\n    \"USERNAME\": \"používateľské meno\",\n    \"VERSION\": \"verzia\",\n    \"VIEWER\": \"pozerať\",\n    \"WAITING\": \"čaká\",\n    \"YES\": \"Áno\",\n    \"YOUR_EMAIL_ADDRESS\": \"Vaša emailová adresa\",\n    \"YOUR_FILES\": \"vaše súbory\",\n    \"YOUR_MASTER_PASSWORD\": \"vaše hlavné heslo\",\n    \"YOU_CANT_DO_THAT\": \"to nemôžete urobiť\"\n}\n"
  },
  {
    "path": "public/assets/locales/sl.json",
    "content": "{\n    \"ABORTED\": \"splavili\",\n    \"ABORT_CURRENT_UPLOADS?\": \"prekiniti trenutne nalaganje?\",\n    \"ACTIVITY\": \"Dejavnost\",\n    \"ADVANCED\": \"napredno\",\n    \"ALL_DONE\": \"končano\",\n    \"ALREADY_EXIST\": \"že obstaja\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Ustvarjena je bila datoteka z imenom {{VALUE}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Ustvarjena je bila mapa z imenom {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"lepo_url\",\n    \"BOOKMARK\": \"zaznamek\",\n    \"CAMERA\": \"kamero\",\n    \"CANCEL\": \"Prekliči\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"ni mogoče vzpostaviti povezave\",\n    \"CANT_LOAD_THIS_PICTURE\": \"ni mogoče naložiti te slike\",\n    \"CANT_USE_FILESYSTEM\": \"lahko uporablja datotečni sistem\",\n    \"CAN_RESHARE\": \"lahko dajo v skupno rabo\",\n    \"CODE\": \"Koda\",\n    \"CONFIGURE\": \"konfigurirati\",\n    \"CONFIRM_BY_TYPING\": \"potrdite s tipkanjem\",\n    \"CONNECT\": \"povežite\",\n    \"CONNECTION_LOST\": \"povezava izgubljena\",\n    \"COPIED_TO_CLIPBOARD\": \"kopirano v odložišče\",\n    \"CREATE_A_NEW_LINK\": \"ustvarite novo povezavo\",\n    \"CREATE_A_TAG\": \"ustvari oznako\",\n    \"CURRENT\": \"trenutno\",\n    \"CURRENT_UPLOAD\": \"trenutni prenos\",\n    \"CUSTOM_LINK_URL\": \"URL povezave po meri\",\n    \"DASHBOARD\": \"armaturna plošča\",\n    \"DATE\": \"datum\",\n    \"DISPLAY_HIDDEN_FILES\": \"prikaži skrite datoteke\",\n    \"DOESNT_MATCH\": \"se ne ujema\",\n    \"DONE\": \"Končano\",\n    \"DOWNLOAD\": \"Prenesi\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"ali želite shraniti spremembe\",\n    \"DROP_HERE_TO_UPLOAD\": \"spusti tukaj za nalaganje\",\n    \"EDITOR\": \"urednik\",\n    \"EMBED\": \"vgrajeno\",\n    \"EMPTY\": \"prazno\",\n    \"ENCRYPTION_KEY\": \"šifrirni ključ\",\n    \"ENDPOINT\": \"končna točka\",\n    \"ERROR\": \"napaka\",\n    \"EXISTING_LINKS\": \"obstoječe povezave\",\n    \"EXPIRATION\": \"prenehanje veljavnosti\",\n    \"EXPORT_AS_{{VALUE}}\": \"izvoziti kot {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"Tu so prikazane pogosto dostopne mape\",\n    \"HIDE_HIDDEN_FILES\": \"skrije skrite datoteke\",\n    \"HOME\": \"domov\",\n    \"HOSTNAME*\": \"ime gostitelja\",\n    \"HOST_KEY\": \"gostiteljski ključ\",\n    \"INCORRECT_PASSWORD\": \"napačno geslo\",\n    \"INFO\": \"info\",\n    \"INTERNAL_ERROR\": \"Notranja napaka\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"notranja napaka: ni mogoče ustvariti {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"neveljaven račun\",\n    \"INVALID_PASSWORD\": \"Neveljavno geslo\",\n    \"LAYOUT\": \"postavitev\",\n    \"LOADING\": \"nalaganje\",\n    \"LOCATION\": \"lokacijo\",\n    \"MISSING_DEPENDENCY\": \"manjka odvisnost\",\n    \"MORE_DETAILS\": \"več podrobnosti\",\n    \"NAVIGATE\": \"krmariti\",\n    \"NEW_FILE\": \"nova datoteka\",\n    \"NEW_FILE::SHORT\": \"nova datoteka\",\n    \"NEW_FOLDER\": \"nov imenik\",\n    \"NEW_FOLDER::SHORT\": \"nov imenik\",\n    \"NO\": \"št\",\n    \"NOT_ALLOWED\": \"ni dovoljeno\",\n    \"NOT_AUTHORISED\": \"ni pooblaščen\",\n    \"NOT_FOUND\": \"ni najdeno\",\n    \"NOT_IMPLEMENTED\": \"ni izveden\",\n    \"NOT_SUPPORTED\": \"ne podpira\",\n    \"NOT_VALID\": \"ni veljaven\",\n    \"NUMBER_OF_CONNECTIONS\": \"število povezav\",\n    \"OK\": \"v redu\",\n    \"ONLY_FOR_USERS\": \"Samo za uporabnike\",\n    \"OOPS\": \"Ups\",\n    \"PASSPHRASE\": \"geslo\",\n    \"PASSWORD\": \"geslo\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"geslo ne more biti prazno\",\n    \"PATH\": \"pot\",\n    \"PERMISSION_DENIED\": \"dovoljenje zavrnjeno\",\n    \"PICK_A_MASTER_PASSWORD\": \"izberite glavno geslo\",\n    \"PORT\": \"pristanišče\",\n    \"POWERED_BY\": \"poganja ga\",\n    \"PROPERTIES\": \"lastnosti\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"zaščiti dostop z geslom\",\n    \"QUICK_ACCESS\": \"hiter dostop\",\n    \"REGION\": \"regija\",\n    \"REMEMBER_ME\": \"spomni se me\",\n    \"REMOVE\": \"Izbriši\",\n    \"RENAME\": \"preimenuj\",\n    \"RENAME_AS\": \"preimenuj kot\",\n    \"RESTRICTIONS\": \"omejitve\",\n    \"RUNNING\": \"tek\",\n    \"SAVE_CURRENT_FILE\": \"shrani trenutno datoteko\",\n    \"SEARCH\": \"Iskanje\",\n    \"SETTINGS\": \"nastavitve\",\n    \"SHARE\": \"deli\",\n    \"SHARED_DRIVE\": \"skupni disk\",\n    \"SKIP_TO_CONTENT\": \"Pojdi na vsebino\",\n    \"SORT\": \"razvrsti\",\n    \"SORT_BY_DATE\": \"razvrsti po datumu\",\n    \"SORT_BY_NAME\": \"razvrsti po imenu\",\n    \"SORT_BY_SIZE\": \"razvrsti po velikosti\",\n    \"SORT_BY_TYPE\": \"razvrsti po vrsti\",\n    \"STARRED\": \"označeno\",\n    \"SUPPORT\": \"podpora\",\n    \"TAG\": \"oznaka\",\n    \"TAGS\": \"oznake\",\n    \"THERE_IS_NOTHING_HERE\": \"tukaj ni ničesar\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"datoteka {{VALUE}} je bila izbrisana\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"datoteka {{VALUE}} je bila preimenovana\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"povezava je bila kopirana v odložišče\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"povezava po tem ne bo veljavna\",\n    \"TIMEOUT\": \"odmor\",\n    \"TODO\": \"narediti\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"prometne zastoje, poskusite pozneje\",\n    \"UPLOAD\": \"naloži\",\n    \"UPLOADER\": \"uploader\",\n    \"USERNAME\": \"uporabniško ime\",\n    \"VERSION\": \"različica\",\n    \"VIEWER\": \"gledalca\",\n    \"WAITING\": \"čaka\",\n    \"YES\": \"da\",\n    \"YOUR_EMAIL_ADDRESS\": \"Vaš email naslov\",\n    \"YOUR_FILES\": \"vaše datoteke\",\n    \"YOUR_MASTER_PASSWORD\": \"svoje glavno geslo\",\n    \"YOU_CANT_DO_THAT\": \"tega ne moreš narediti\"\n}\n"
  },
  {
    "path": "public/assets/locales/sr.json",
    "content": "{\n    \"ABORTED\": \"прекинута\",\n    \"ABORT_CURRENT_UPLOADS?\": \"прекинути тренутни уплоад?\",\n    \"ACTIVITY\": \"Активност\",\n    \"ADVANCED\": \"напредни\",\n    \"ALL_DONE\": \"завршено\",\n    \"ALREADY_EXIST\": \"већ постоји\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Направљена је датотека под називом {{ВАЛУЕ}}\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Направљена је фасцикла под називом {{ВАЛУЕ}}\",\n    \"BEAUTIFUL_URL\": \"беаутифул_урл\",\n    \"BOOKMARK\": \"обележивач\",\n    \"CAMERA\": \"Камера\",\n    \"CANCEL\": \"откажи\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"не може успоставити везу\",\n    \"CANT_LOAD_THIS_PICTURE\": \"не могу учитати ову слику\",\n    \"CANT_USE_FILESYSTEM\": \"може да користи датотечни систем\",\n    \"CAN_RESHARE\": \"дели поново\",\n    \"CODE\": \"код\",\n    \"CONFIGURE\": \"подеси\",\n    \"CONFIRM_BY_TYPING\": \"потврдите куцањем\",\n    \"CONNECT\": \"повежите се\",\n    \"CONNECTION_LOST\": \"веза је изгубљена\",\n    \"COPIED_TO_CLIPBOARD\": \"копирано у међуспремник\",\n    \"CREATE_A_NEW_LINK\": \"створите нову везу\",\n    \"CREATE_A_TAG\": \"креирај ознаку\",\n    \"CURRENT\": \"Тренутни\",\n    \"CURRENT_UPLOAD\": \"тренутно отпремање\",\n    \"CUSTOM_LINK_URL\": \"прилагођена УРЛ адреса везе\",\n    \"DASHBOARD\": \"Командна табла\",\n    \"DATE\": \"датум\",\n    \"DISPLAY_HIDDEN_FILES\": \"приказ скривених датотека\",\n    \"DOESNT_MATCH\": \"не одговара\",\n    \"DONE\": \"Готово\",\n    \"DOWNLOAD\": \"преузимање\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"да ли желите да сачувате измене\",\n    \"DROP_HERE_TO_UPLOAD\": \"испустите овде да бисте је послали\",\n    \"EDITOR\": \"уредник\",\n    \"EMBED\": \"угради\",\n    \"EMPTY\": \"празно\",\n    \"ENCRYPTION_KEY\": \"шифрирање\",\n    \"ENDPOINT\": \"крајња тачка\",\n    \"ERROR\": \"грешка\",\n    \"EXISTING_LINKS\": \"постојеће везе\",\n    \"EXPIRATION\": \"истицање\",\n    \"EXPORT_AS_{{VALUE}}\": \"извести као {{ВАЛУЕ}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"Овде ће се приказивати директоријуми са често приступом\",\n    \"HIDE_HIDDEN_FILES\": \"сакривање скривених датотека\",\n    \"HOME\": \"почетна\",\n    \"HOSTNAME*\": \"име домаћина\",\n    \"HOST_KEY\": \"главни кључ\",\n    \"INCORRECT_PASSWORD\": \"Погрешна лозинка\",\n    \"INFO\": \"инфо\",\n    \"INTERNAL_ERROR\": \"Интeрна грeшка\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"интерна грешка: не може да се креира {{ВАЛУЕ}}\",\n    \"INVALID_ACCOUNT\": \"Неважећи рачун\",\n    \"INVALID_PASSWORD\": \"неважећа лозинка\",\n    \"LAYOUT\": \"распоред\",\n    \"LOADING\": \"учитавање\",\n    \"LOCATION\": \"локацију\",\n    \"MISSING_DEPENDENCY\": \"недостаје зависност\",\n    \"MORE_DETAILS\": \"више детаља\",\n    \"NAVIGATE\": \"навигација\",\n    \"NEW_FILE\": \"нова датотека\",\n    \"NEW_FILE::SHORT\": \"нова датотека\",\n    \"NEW_FOLDER\": \"нови директориј\",\n    \"NEW_FOLDER::SHORT\": \"нови директориј\",\n    \"NO\": \"не\",\n    \"NOT_ALLOWED\": \"није дозвољено\",\n    \"NOT_AUTHORISED\": \"није овлашћен\",\n    \"NOT_FOUND\": \"није пронађен\",\n    \"NOT_IMPLEMENTED\": \"не спроводи\",\n    \"NOT_SUPPORTED\": \"није подржан\",\n    \"NOT_VALID\": \"не важи\",\n    \"NUMBER_OF_CONNECTIONS\": \"број веза\",\n    \"OK\": \"ОК\",\n    \"ONLY_FOR_USERS\": \"Само за кориснике\",\n    \"OOPS\": \"Упс\",\n    \"PASSPHRASE\": \"лозинку\",\n    \"PASSWORD\": \"Лозинка\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"лозинка не може бити празна\",\n    \"PATH\": \"стаза\",\n    \"PERMISSION_DENIED\": \"Приступ је одбијен\",\n    \"PICK_A_MASTER_PASSWORD\": \"изаберите главну лозинку\",\n    \"PORT\": \"Порт\",\n    \"POWERED_BY\": \"покреће га\",\n    \"PROPERTIES\": \"својства\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"заштитите приступ лозинком\",\n    \"QUICK_ACCESS\": \"брзи приступ\",\n    \"REGION\": \"регион\",\n    \"REMEMBER_ME\": \"сети ме се\",\n    \"REMOVE\": \"уклони\",\n    \"RENAME\": \"преименуј\",\n    \"RENAME_AS\": \"преименуј као\",\n    \"RESTRICTIONS\": \"Ограничења\",\n    \"RUNNING\": \"покретање\",\n    \"SAVE_CURRENT_FILE\": \"сачувај тренутну датотеку\",\n    \"SEARCH\": \"Претрага\",\n    \"SETTINGS\": \"подешавања\",\n    \"SHARE\": \"подели\",\n    \"SHARED_DRIVE\": \"заједнички диск\",\n    \"SKIP_TO_CONTENT\": \"Иди на садржај\",\n    \"SORT\": \"сортирај\",\n    \"SORT_BY_DATE\": \"сортирај по датуму\",\n    \"SORT_BY_NAME\": \"сортирај по имену\",\n    \"SORT_BY_SIZE\": \"сортирај по величини\",\n    \"SORT_BY_TYPE\": \"сортирај по врсти\",\n    \"STARRED\": \"означено\",\n    \"SUPPORT\": \"подршка\",\n    \"TAG\": \"ознака\",\n    \"TAGS\": \"означи\",\n    \"THERE_IS_NOTHING_HERE\": \"овде нема ничега\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"датотека {{ВАЛУЕ}} је избрисана\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"датотека {{ВАЛУЕ}} је преименована\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"веза је копирана у међуспремник\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"веза после тога неће бити валидна\",\n    \"TIMEOUT\": \"Истек времена\",\n    \"TODO\": \"урадити\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"саобраћајна гужва, покушајте касније\",\n    \"UPLOAD\": \"отпреми\",\n    \"UPLOADER\": \"отпремач\",\n    \"USERNAME\": \"корисничко име\",\n    \"VERSION\": \"верзија\",\n    \"VIEWER\": \"прегледач\",\n    \"WAITING\": \"чека\",\n    \"YES\": \"да\",\n    \"YOUR_EMAIL_ADDRESS\": \"Ваша имејл адреса\",\n    \"YOUR_FILES\": \"ваши фајлови\",\n    \"YOUR_MASTER_PASSWORD\": \"ваша главна лозинка\",\n    \"YOU_CANT_DO_THAT\": \"не можеш то учинити\"\n}\n"
  },
  {
    "path": "public/assets/locales/sv.json",
    "content": "{\n    \"ABORTED\": \"avbruten\",\n    \"ABORT_CURRENT_UPLOADS?\": \"avbryta aktuell uppladdning?\",\n    \"ACTIVITY\": \"Aktivitet\",\n    \"ADVANCED\": \"Avancerad\",\n    \"ALL_DONE\": \"helt klar\",\n    \"ALREADY_EXIST\": \"redan finns\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"En fil med namnet {{VALUE}} skapades\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"En mapp med namnet {{VALUE}} skapades\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"bokmärke\",\n    \"CAMERA\": \"kamera\",\n    \"CANCEL\": \"Avbryt\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"kan inte upprätta en anslutning\",\n    \"CANT_LOAD_THIS_PICTURE\": \"kan inte ladda den här bilden\",\n    \"CANT_USE_FILESYSTEM\": \"kan använda filsystem\",\n    \"CAN_RESHARE\": \"kan dela om\",\n    \"CODE\": \"kod\",\n    \"CONFIGURE\": \"configure\",\n    \"CONFIRM_BY_TYPING\": \"bekräfta genom att skriva\",\n    \"CONNECT\": \"ansluta\",\n    \"CONNECTION_LOST\": \"anslutning förlorad\",\n    \"COPIED_TO_CLIPBOARD\": \"kopierade till urklipp\",\n    \"CREATE_A_NEW_LINK\": \"skapa en ny länk\",\n    \"CREATE_A_TAG\": \"skapa en etikett\",\n    \"CURRENT\": \"nuvarande\",\n    \"CURRENT_UPLOAD\": \"nuvarande uppladdning\",\n    \"CUSTOM_LINK_URL\": \"anpassad länk URL\",\n    \"DASHBOARD\": \"instrumentbräda\",\n    \"DATE\": \"datum\",\n    \"DISPLAY_HIDDEN_FILES\": \"visa dolda filer\",\n    \"DOESNT_MATCH\": \"matchar inte\",\n    \"DONE\": \"Gjort\",\n    \"DOWNLOAD\": \"ladda ner\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"vill du spara ändringarna\",\n    \"DROP_HERE_TO_UPLOAD\": \"släpp här för att ladda upp\",\n    \"EDITOR\": \"redaktör\",\n    \"EMBED\": \"bädda in\",\n    \"EMPTY\": \"tömma\",\n    \"ENCRYPTION_KEY\": \"krypteringsnyckel\",\n    \"ENDPOINT\": \"slutpunkt\",\n    \"ERROR\": \"fel\",\n    \"EXISTING_LINKS\": \"befintliga länkar\",\n    \"EXPIRATION\": \"utgång\",\n    \"EXPORT_AS_{{VALUE}}\": \"exportera som {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"ofta visas mappar här\",\n    \"HIDE_HIDDEN_FILES\": \"dölja dolda filer\",\n    \"HOME\": \"Hemsida\",\n    \"HOSTNAME*\": \"hostname\",\n    \"HOST_KEY\": \"värdnyckel\",\n    \"INCORRECT_PASSWORD\": \"fel lösenord\",\n    \"INFO\": \"info\",\n    \"INTERNAL_ERROR\": \"Internt fel\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"internt fel: kan inte skapa ett {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"ogiltigt konto\",\n    \"INVALID_PASSWORD\": \"felaktigt lösenord\",\n    \"LAYOUT\": \"layout\",\n    \"LOADING\": \"laddar\",\n    \"LOCATION\": \"lokalisering\",\n    \"MISSING_DEPENDENCY\": \"saknas beroende\",\n    \"MORE_DETAILS\": \"mer information\",\n    \"NAVIGATE\": \"navigera\",\n    \"NEW_FILE\": \"ny fil\",\n    \"NEW_FILE::SHORT\": \"ny fil\",\n    \"NEW_FOLDER\": \"ny katalog\",\n    \"NEW_FOLDER::SHORT\": \"ny katalog\",\n    \"NO\": \"Nej\",\n    \"NOT_ALLOWED\": \"inte tillåtet\",\n    \"NOT_AUTHORISED\": \"inte godkänd\",\n    \"NOT_FOUND\": \"hittades inte\",\n    \"NOT_IMPLEMENTED\": \"ej implementerad\",\n    \"NOT_SUPPORTED\": \"stöds inte\",\n    \"NOT_VALID\": \"inte giltig\",\n    \"NUMBER_OF_CONNECTIONS\": \"antal anslutningar\",\n    \"OK\": \"ok\",\n    \"ONLY_FOR_USERS\": \"Endast för användare\",\n    \"OOPS\": \"Hoppsan\",\n    \"PASSPHRASE\": \"lösenfras\",\n    \"PASSWORD\": \"Lösenord\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"lösenordet kan inte vara tomt\",\n    \"PATH\": \"väg\",\n    \"PERMISSION_DENIED\": \"åtkomst nekad\",\n    \"PICK_A_MASTER_PASSWORD\": \"välj ett huvudlösenord\",\n    \"PORT\": \"Port\",\n    \"POWERED_BY\": \"drivs av\",\n    \"PROPERTIES\": \"attribut\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"skydda åtkomst med ett lösenord\",\n    \"QUICK_ACCESS\": \"snabb åtkomst\",\n    \"REGION\": \"område\",\n    \"REMEMBER_ME\": \"kom ihåg mig\",\n    \"REMOVE\": \"ta bort\",\n    \"RENAME\": \"byt namn\",\n    \"RENAME_AS\": \"byt namn till\",\n    \"RESTRICTIONS\": \"restriktioner\",\n    \"RUNNING\": \"pågående\",\n    \"SAVE_CURRENT_FILE\": \"spara aktuell fil\",\n    \"SEARCH\": \"Sök\",\n    \"SETTINGS\": \"inställningar\",\n    \"SHARE\": \"dela\",\n    \"SHARED_DRIVE\": \"delad enhet\",\n    \"SKIP_TO_CONTENT\": \"Gå till innehåll\",\n    \"SORT\": \"sortera\",\n    \"SORT_BY_DATE\": \"sortera efter datum\",\n    \"SORT_BY_NAME\": \"sortera efter namn\",\n    \"SORT_BY_SIZE\": \"sortera efter storlek\",\n    \"SORT_BY_TYPE\": \"sortera efter typ\",\n    \"STARRED\": \"markerad\",\n    \"SUPPORT\": \"Stöd\",\n    \"TAG\": \"etikett\",\n    \"TAGS\": \"etiketter\",\n    \"THERE_IS_NOTHING_HERE\": \"det finns ingenting här\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"filen {{VALUE}} raderades\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"filen {{VALUE}} byttes namn\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"länken kopierades i Urklipp\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"länken kommer inte att vara giltig efter\",\n    \"TIMEOUT\": \"Paus\",\n    \"TODO\": \"att göra\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"trafikstockningar, försök igen senare\",\n    \"UPLOAD\": \"ladda upp\",\n    \"UPLOADER\": \"Uppladdare\",\n    \"USERNAME\": \"Användarnamn\",\n    \"VERSION\": \"version\",\n    \"VIEWER\": \"tittare\",\n    \"WAITING\": \"väntar\",\n    \"YES\": \"ja\",\n    \"YOUR_EMAIL_ADDRESS\": \"din e-postadress\",\n    \"YOUR_FILES\": \"dina filer\",\n    \"YOUR_MASTER_PASSWORD\": \"ditt huvudlösenord\",\n    \"YOU_CANT_DO_THAT\": \"du kan inte göra det\"\n}\n"
  },
  {
    "path": "public/assets/locales/th.json",
    "content": "{\n    \"ABORTED\": \"ยกเลิก\",\n    \"ABORT_CURRENT_UPLOADS?\": \"ยกเลิกการอัปโหลดปัจจุบันหรือไม่\",\n    \"ACTIVITY\": \"กิจกรรม\",\n    \"ADVANCED\": \"สูง\",\n    \"ALL_DONE\": \"ทุกอย่างเสร็จเรียบร้อย\",\n    \"ALREADY_EXIST\": \"มีอยู่แล้ว\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"สร้างไฟล์ชื่อ {{VALUE}} แล้ว\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"สร้างโฟลเดอร์ชื่อ {{VALUE}} แล้ว\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"ที่คั่นหน้า\",\n    \"CAMERA\": \"กล้อง\",\n    \"CANCEL\": \"ยกเลิก\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"ไม่สามารถสร้างการเชื่อมต่อ\",\n    \"CANT_LOAD_THIS_PICTURE\": \"ไม่สามารถโหลดรูปภาพนี้\",\n    \"CANT_USE_FILESYSTEM\": \"สามารถใช้ระบบไฟล์\",\n    \"CAN_RESHARE\": \"สามารถแชร์ต่อได้\",\n    \"CODE\": \"รหัส\",\n    \"CONFIGURE\": \"กำหนดค่า\",\n    \"CONFIRM_BY_TYPING\": \"ยืนยันโดยพิมพ์\",\n    \"CONNECT\": \"การเชื่อมต่อ\",\n    \"CONNECTION_LOST\": \"การเชื่อมต่อขาดหายไป\",\n    \"COPIED_TO_CLIPBOARD\": \"คัดลอกไปยังคลิปบอร์ดแล้ว\",\n    \"CREATE_A_NEW_LINK\": \"สร้างลิงค์ใหม่\",\n    \"CREATE_A_TAG\": \"สร้างแท็ก\",\n    \"CURRENT\": \"ปัจจุบัน\",\n    \"CURRENT_UPLOAD\": \"อัปโหลดปัจจุบัน\",\n    \"CUSTOM_LINK_URL\": \"URL ลิงค์ที่กำหนดเอง\",\n    \"DASHBOARD\": \"แผงควบคุม\",\n    \"DATE\": \"วันที่\",\n    \"DISPLAY_HIDDEN_FILES\": \"แสดงไฟล์ที่ซ่อน\",\n    \"DOESNT_MATCH\": \"ไม่ตรง\",\n    \"DONE\": \"เสร็จแล้ว\",\n    \"DOWNLOAD\": \"ดาวน์โหลด\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"คุณต้องการบันทึกการเปลี่ยนแปลงหรือไม่\",\n    \"DROP_HERE_TO_UPLOAD\": \"วางที่นี่เพื่ออัปโหลด\",\n    \"EDITOR\": \"บรรณาธิการ\",\n    \"EMBED\": \"ฝัง\",\n    \"EMPTY\": \"ว่างเปล่า\",\n    \"ENCRYPTION_KEY\": \"คีย์การเข้ารหัส\",\n    \"ENDPOINT\": \"ปลายทาง\",\n    \"ERROR\": \"ความผิดพลาด\",\n    \"EXISTING_LINKS\": \"ลิงก์ที่มีอยู่\",\n    \"EXPIRATION\": \"การหมดอายุ\",\n    \"EXPORT_AS_{{VALUE}}\": \"ส่งออกเป็น {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"โฟลเดอร์ที่เข้าถึงบ่อย ๆ จะปรากฏที่นี่\",\n    \"HIDE_HIDDEN_FILES\": \"ซ่อนไฟล์ที่ซ่อน\",\n    \"HOME\": \"หน้าหลัก\",\n    \"HOSTNAME*\": \"ชื่อโฮสต์\",\n    \"HOST_KEY\": \"รหัสโฮสต์\",\n    \"INCORRECT_PASSWORD\": \"รหัสผ่านผิดพลาด\",\n    \"INFO\": \"ข้อมูล\",\n    \"INTERNAL_ERROR\": \"ข้อผิดพลาดภายใน\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"ข้อผิดพลาดภายใน: ไม่สามารถสร้าง {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"บัญชีไม่ถูกต้อง\",\n    \"INVALID_PASSWORD\": \"รหัสผ่านไม่ถูกต้อง\",\n    \"LAYOUT\": \"รูปแบบ\",\n    \"LOADING\": \"กำลังโหลด\",\n    \"LOCATION\": \"ที่ตั้ง\",\n    \"MISSING_DEPENDENCY\": \"ขาดการพึ่งพา\",\n    \"MORE_DETAILS\": \"รายละเอียดเพิ่มเติม\",\n    \"NAVIGATE\": \"นำทาง\",\n    \"NEW_FILE\": \"ไฟล์ใหม่\",\n    \"NEW_FILE::SHORT\": \"ไฟล์ใหม่\",\n    \"NEW_FOLDER\": \"ไดเรกทอรีใหม่\",\n    \"NEW_FOLDER::SHORT\": \"ไดเรกทอรีใหม่\",\n    \"NO\": \"ไม่\",\n    \"NOT_ALLOWED\": \"ไม่ได้รับอนุญาต\",\n    \"NOT_AUTHORISED\": \"ไม่ได้รับอนุญาต\",\n    \"NOT_FOUND\": \"ไม่พบ\",\n    \"NOT_IMPLEMENTED\": \"ไม่ได้ดำเนินการ\",\n    \"NOT_SUPPORTED\": \"ไม่รองรับ\",\n    \"NOT_VALID\": \"ไม่ถูกต้อง\",\n    \"NUMBER_OF_CONNECTIONS\": \"จำนวนการเชื่อมต่อ\",\n    \"OK\": \"ตกลง\",\n    \"ONLY_FOR_USERS\": \"สำหรับผู้ใช้เท่านั้น\",\n    \"OOPS\": \"อุ่ย\",\n    \"PASSPHRASE\": \"ข้อความรหัสผ่าน\",\n    \"PASSWORD\": \"รหัสผ่าน\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"รหัสผ่านต้องไม่ว่างเปล่า\",\n    \"PATH\": \"เส้นทาง\",\n    \"PERMISSION_DENIED\": \"ปฏิเสธการอนุญาต\",\n    \"PICK_A_MASTER_PASSWORD\": \"เลือกรหัสผ่านหลัก\",\n    \"PORT\": \"ท่าเรือ\",\n    \"POWERED_BY\": \"ขับเคลื่อนโดย\",\n    \"PROPERTIES\": \"คุณสมบัติ\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"ป้องกันการเข้าถึงด้วยรหัสผ่าน\",\n    \"QUICK_ACCESS\": \"การเข้าถึงอย่างรวดเร็ว\",\n    \"REGION\": \"ภูมิภาค\",\n    \"REMEMBER_ME\": \"จดจำฉัน\",\n    \"REMOVE\": \"ลบ\",\n    \"RENAME\": \"เปลี่ยนชื่อ\",\n    \"RENAME_AS\": \"เปลี่ยนชื่อเป็น\",\n    \"RESTRICTIONS\": \"ข้อ จำกัด\",\n    \"RUNNING\": \"วิ่ง\",\n    \"SAVE_CURRENT_FILE\": \"บันทึกไฟล์ปัจจุบัน\",\n    \"SEARCH\": \"ค้นหา\",\n    \"SETTINGS\": \"การตั้งค่า\",\n    \"SHARE\": \"แชร์\",\n    \"SHARED_DRIVE\": \"ไดรฟ์ที่แชร์\",\n    \"SKIP_TO_CONTENT\": \"ข้ามไปยังเนื้อหา\",\n    \"SORT\": \"เรียงลำดับ\",\n    \"SORT_BY_DATE\": \"จัดเรียงตามวันที่\",\n    \"SORT_BY_NAME\": \"เรียงตามชื่อ\",\n    \"SORT_BY_SIZE\": \"เรียงตามขนาด\",\n    \"SORT_BY_TYPE\": \"จัดเรียงตามประเภท\",\n    \"STARRED\": \"ติดดาว\",\n    \"SUPPORT\": \"สนับสนุน\",\n    \"TAG\": \"แท็ก\",\n    \"TAGS\": \"แท็ก\",\n    \"THERE_IS_NOTHING_HERE\": \"ไม่มีอะไรที่นี่\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"ไฟล์ {{VALUE}} ถูกลบแล้ว\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"ไฟล์ {{VALUE}} ถูกเปลี่ยนชื่อ\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"ลิงก์ถูกคัดลอกในคลิปบอร์ด\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"ลิงก์จะไม่สามารถใช้ได้หลังจากนั้น\",\n    \"TIMEOUT\": \"หมดเวลา\",\n    \"TODO\": \"ทำ\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"การจราจรติดขัดลองอีกครั้งในภายหลัง\",\n    \"UPLOAD\": \"อัปโหลด\",\n    \"UPLOADER\": \"อัปโหลด\",\n    \"USERNAME\": \"ชื่อผู้ใช้\",\n    \"VERSION\": \"เวอร์ชัน\",\n    \"VIEWER\": \"ผู้ชม\",\n    \"WAITING\": \"ที่รอคอย\",\n    \"YES\": \"ใช่\",\n    \"YOUR_EMAIL_ADDRESS\": \"ที่อยู่อีเมลของคุณ\",\n    \"YOUR_FILES\": \"ไฟล์ของคุณ\",\n    \"YOUR_MASTER_PASSWORD\": \"รหัสผ่านหลักของคุณ\",\n    \"YOU_CANT_DO_THAT\": \"คุณทำอย่างนั้นไม่ได้\"\n}\n"
  },
  {
    "path": "public/assets/locales/tr.json",
    "content": "{\n    \"ABORTED\": \"Sonlandırıldı\",\n    \"ABORT_CURRENT_UPLOADS?\": \"Mevcut yükleme sonlandırılsın mı?\",\n    \"ACTIVITY\": \"Hareketlilik\",\n    \"ADVANCED\": \"Gelişmiş\",\n    \"ALL_DONE\": \"Tamamlandı\",\n    \"ALREADY_EXIST\": \"zaten mevcut\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"{{VALUE}} adlı bir dosya oluşturuldu\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"{{VALUE}} adlı bir klasör oluşturuldu\",\n    \"BEAUTIFUL_URL\": \"özel URL\",\n    \"BOOKMARK\": \"yer imi\",\n    \"CAMERA\": \"Kamera\",\n    \"CANCEL\": \"İptal et\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"Bağlantı kurulamıyor\",\n    \"CANT_LOAD_THIS_PICTURE\": \"Bu resim görüntülenemiyor\",\n    \"CANT_USE_FILESYSTEM\": \"Dosya sistemi kullanılamıyor\",\n    \"CAN_RESHARE\": \"yeniden paylaş\",\n    \"CODE\": \"Kod\",\n    \"CONFIGURE\": \"Yapılandır\",\n    \"CONFIRM_BY_TYPING\": \"Yazarak onayla\",\n    \"CONNECT\": \"Bağlan\",\n    \"CONNECTION_LOST\": \"bağlantı kesildi\",\n    \"COPIED_TO_CLIPBOARD\": \"Panoya kopyalandı\",\n    \"CREATE_A_NEW_LINK\": \"Yeni bir bağlantı oluştur\",\n    \"CREATE_A_TAG\": \"etiket oluştur\",\n    \"CURRENT\": \"Mevcut işlem\",\n    \"CURRENT_UPLOAD\": \"Mevcut yükleme\",\n    \"CUSTOM_LINK_URL\": \"özel bağlantı URL'si\",\n    \"DASHBOARD\": \"Panel\",\n    \"DATE\": \"Tarih\",\n    \"DISPLAY_HIDDEN_FILES\": \"gizli dosyaları göster\",\n    \"DOESNT_MATCH\": \"eşleşmiyor\",\n    \"DONE\": \"tamam\",\n    \"DOWNLOAD\": \"indir\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"Değişiklikleri kaydetmek ister misin?\",\n    \"DROP_HERE_TO_UPLOAD\": \"yüklemek için buraya sürükle\",\n    \"EDITOR\": \"Metin Düzenleyici\",\n    \"EMBED\": \"göm\",\n    \"EMPTY\": \"boş\",\n    \"ENCRYPTION_KEY\": \"şifreleme anahtarı\",\n    \"ENDPOINT\": \"uç nokta\",\n    \"ERROR\": \"Hata\",\n    \"EXISTING_LINKS\": \"mevcut bağlantılar\",\n    \"EXPIRATION\": \"Sona erme tarihi\",\n    \"EXPORT_AS_{{VALUE}}\": \"{{VALUE}} olarak dışa aktar\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"Sıkça erişilen klasörler burada gösterilecek\",\n    \"HIDE_HIDDEN_FILES\": \"gizli dosyaları gizle\",\n    \"HOME\": \"ana sayfa\",\n    \"HOSTNAME*\": \"Ana makine adı\",\n    \"HOST_KEY\": \"Ana makine anahtarı\",\n    \"INCORRECT_PASSWORD\": \"yanlış parola\",\n    \"INFO\": \"Bilgi\",\n    \"INTERNAL_ERROR\": \"İç hata\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"Dahili hata: {{VALUE}} oluşturulamıyor\",\n    \"INVALID_ACCOUNT\": \"Geçersiz hesap\",\n    \"INVALID_PASSWORD\": \"Geçersiz şifre\",\n    \"LAYOUT\": \"düzen\",\n    \"LOADING\": \"yükleniyor\",\n    \"LOCATION\": \"Konum\",\n    \"MISSING_DEPENDENCY\": \"Eksik bağımlılıklar\",\n    \"MORE_DETAILS\": \"daha fazla detay\",\n    \"NAVIGATE\": \"Gezinti\",\n    \"NEW_FILE\": \"yeni dosya\",\n    \"NEW_FILE::SHORT\": \"yeni dosya\",\n    \"NEW_FOLDER\": \"yeni dizin\",\n    \"NEW_FOLDER::SHORT\": \"yeni dizin\",\n    \"NO\": \"Hayır\",\n    \"NOT_ALLOWED\": \"izin verilmedi\",\n    \"NOT_AUTHORISED\": \"yetkin değil\",\n    \"NOT_FOUND\": \"bulunamadı\",\n    \"NOT_IMPLEMENTED\": \"uygulanmadı\",\n    \"NOT_SUPPORTED\": \"desteklenmiyor\",\n    \"NOT_VALID\": \"geçerli değil\",\n    \"NUMBER_OF_CONNECTIONS\": \"Bağlantı sayısı\",\n    \"OK\": \"Tamam\",\n    \"ONLY_FOR_USERS\": \"Yalnızca şu kullanıcılar için\",\n    \"OOPS\": \"Amanın!\",\n    \"PASSPHRASE\": \"Anahtar Parolası\",\n    \"PASSWORD\": \"Parola\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"Parola boş olamaz\",\n    \"PATH\": \"Yol\",\n    \"PERMISSION_DENIED\": \"İzin reddedildi\",\n    \"PICK_A_MASTER_PASSWORD\": \"Bir ana parola belirleyin\",\n    \"PORT\": \"Port\",\n    \"POWERED_BY\": \"tarafından desteklenmektedir\",\n    \"PROPERTIES\": \"Özellikleri\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"Parola ile erişimi koru\",\n    \"QUICK_ACCESS\": \"hızlı erişim\",\n    \"REGION\": \"Bölge\",\n    \"REMEMBER_ME\": \"Beni hatırla\",\n    \"REMOVE\": \"Kaldır\",\n    \"RENAME\": \"yeniden adlandır\",\n    \"RENAME_AS\": \"olarak yeniden adlandır\",\n    \"RESTRICTIONS\": \"Kısıtlamalar\",\n    \"RUNNING\": \"çalışıyor\",\n    \"SAVE_CURRENT_FILE\": \"Mevcut dosyayı kaydet\",\n    \"SEARCH\": \"Ara\",\n    \"SETTINGS\": \"Ayarlar\",\n    \"SHARE\": \"paylaş\",\n    \"SHARED_DRIVE\": \"paylaşılan sürücü\",\n    \"SKIP_TO_CONTENT\": \"İçeriğe atla\",\n    \"SORT\": \"sırala\",\n    \"SORT_BY_DATE\": \"tarihe göre sırala\",\n    \"SORT_BY_NAME\": \"isme göre sırala\",\n    \"SORT_BY_SIZE\": \"boyuta göre sırala\",\n    \"SORT_BY_TYPE\": \"türe göre sırala\",\n    \"STARRED\": \"yıldızlı\",\n    \"SUPPORT\": \"Destek\",\n    \"TAG\": \"etiket\",\n    \"TAGS\": \"etiketler\",\n    \"THERE_IS_NOTHING_HERE\": \"Burada hiçbir şey yok\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"{{VALUE}} dosyası silindi\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"{{VALUE}} dosyası yeniden adlandırıldı\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"Bağlantı panoya kopyalandı\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"Bağlantı daha sonra geçerli olmayacaktır\",\n    \"TIMEOUT\": \"Zaman aşımı\",\n    \"TODO\": \"Yapılacaklar\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"Ağ yoğunluğu, daha sonra tekrar deneyin\",\n    \"UPLOAD\": \"yükle\",\n    \"UPLOADER\": \"Yükleyici\",\n    \"USERNAME\": \"Kullanıcı adı\",\n    \"VERSION\": \"sürüm\",\n    \"VIEWER\": \"Görüntüleyici\",\n    \"WAITING\": \"bekliyor\",\n    \"YES\": \"Evet\",\n    \"YOUR_EMAIL_ADDRESS\": \"E-posta adresi\",\n    \"YOUR_FILES\": \"dosyalarınız\",\n    \"YOUR_MASTER_PASSWORD\": \"Ana parolan\",\n    \"YOU_CANT_DO_THAT\": \"bunu yapamazsınız\"\n}\n"
  },
  {
    "path": "public/assets/locales/uk.json",
    "content": "{\n    \"ABORTED\": \"перервано\",\n    \"ABORT_CURRENT_UPLOADS?\": \"перервати поточне завантаження?\",\n    \"ACTIVITY\": \"Діяльність\",\n    \"ADVANCED\": \"розширені\",\n    \"ALL_DONE\": \"все зроблено\",\n    \"ALREADY_EXIST\": \"вже існує\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Файл під назвою {{VALUE}} створено\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Створена папка під назвою {{VALUE}}\",\n    \"BEAUTIFUL_URL\": \"beautiful_url\",\n    \"BOOKMARK\": \"закладка\",\n    \"CAMERA\": \"камера\",\n    \"CANCEL\": \"скасувати\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"не вдається встановити з'єднання\",\n    \"CANT_LOAD_THIS_PICTURE\": \"не вдається завантажити це зображення\",\n    \"CANT_USE_FILESYSTEM\": \"Неможливо використовувати файлову систему\",\n    \"CAN_RESHARE\": \"може повторно ділитися\",\n    \"CODE\": \"код\",\n    \"CONFIGURE\": \"налаштувати\",\n    \"CONFIRM_BY_TYPING\": \"підтвердити, ввівши\",\n    \"CONNECT\": \"підключити\",\n    \"CONNECTION_LOST\": \"зв'язок втрачено\",\n    \"COPIED_TO_CLIPBOARD\": \"скопійовано у буфер обміну\",\n    \"CREATE_A_NEW_LINK\": \"створити нове посилання\",\n    \"CREATE_A_TAG\": \"створити тег\",\n    \"CURRENT\": \"струм\",\n    \"CURRENT_UPLOAD\": \"поточне завантаження\",\n    \"CUSTOM_LINK_URL\": \"спеціальна URL-адреса посилання\",\n    \"DASHBOARD\": \"панель приладів\",\n    \"DATE\": \"дата\",\n    \"DISPLAY_HIDDEN_FILES\": \"відображення прихованих файлів\",\n    \"DOESNT_MATCH\": \"не відповідає\",\n    \"DONE\": \"зроблено\",\n    \"DOWNLOAD\": \"завантажити\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"чи хочете ви зберегти зміни\",\n    \"DROP_HERE_TO_UPLOAD\": \"сюди, щоб завантажити\",\n    \"EDITOR\": \"редактор\",\n    \"EMBED\": \"вбудувати\",\n    \"EMPTY\": \"порожній\",\n    \"ENCRYPTION_KEY\": \"ключ шифрування\",\n    \"ENDPOINT\": \"кінцева точка\",\n    \"ERROR\": \"помилка\",\n    \"EXISTING_LINKS\": \"існуючі посилання\",\n    \"EXPIRATION\": \"термін придатності\",\n    \"EXPORT_AS_{{VALUE}}\": \"експортувати як {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"тут будуть відображатися папки, які часто отримують доступ\",\n    \"HIDE_HIDDEN_FILES\": \"приховати приховані файли\",\n    \"HOME\": \"головна\",\n    \"HOSTNAME*\": \"ім'я хоста\",\n    \"HOST_KEY\": \"ключ хоста\",\n    \"INCORRECT_PASSWORD\": \"Невірний пароль\",\n    \"INFO\": \"інформація\",\n    \"INTERNAL_ERROR\": \"Внутрішня помилка\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"внутрішня помилка: не вдається створити {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"недійсний рахунок\",\n    \"INVALID_PASSWORD\": \"недійсний пароль\",\n    \"LAYOUT\": \"макет\",\n    \"LOADING\": \"завантаження\",\n    \"LOCATION\": \"Розташування\",\n    \"MISSING_DEPENDENCY\": \"відсутність залежності\",\n    \"MORE_DETAILS\": \"більше деталей\",\n    \"NAVIGATE\": \"орієнтуватися\",\n    \"NEW_FILE\": \"новий файл\",\n    \"NEW_FILE::SHORT\": \"новий файл\",\n    \"NEW_FOLDER\": \"новий каталог\",\n    \"NEW_FOLDER::SHORT\": \"новий каталог\",\n    \"NO\": \"ні\",\n    \"NOT_ALLOWED\": \"не дозволено\",\n    \"NOT_AUTHORISED\": \"не уповноважений\",\n    \"NOT_FOUND\": \"не знайдено\",\n    \"NOT_IMPLEMENTED\": \"не впроваджений\",\n    \"NOT_SUPPORTED\": \"Не підтримується\",\n    \"NOT_VALID\": \"не діє\",\n    \"NUMBER_OF_CONNECTIONS\": \"кількість підключень\",\n    \"OK\": \"Гаразд\",\n    \"ONLY_FOR_USERS\": \"Тільки для користувачів\",\n    \"OOPS\": \"На жаль\",\n    \"PASSPHRASE\": \"парольна фраза\",\n    \"PASSWORD\": \"пароль\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"пароль не може бути порожнім\",\n    \"PATH\": \"шлях\",\n    \"PERMISSION_DENIED\": \"у дозволі відмовлено\",\n    \"PICK_A_MASTER_PASSWORD\": \"виберіть головний пароль\",\n    \"PORT\": \"порт\",\n    \"POWERED_BY\": \"працює на основі\",\n    \"PROPERTIES\": \"властивості\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"захистити доступ паролем\",\n    \"QUICK_ACCESS\": \"швидкий доступ\",\n    \"REGION\": \"область\",\n    \"REMEMBER_ME\": \"Пам'ятай мене\",\n    \"REMOVE\": \"видалити\",\n    \"RENAME\": \"перейменувати\",\n    \"RENAME_AS\": \"перейменувати як\",\n    \"RESTRICTIONS\": \"обмеження\",\n    \"RUNNING\": \"виконується\",\n    \"SAVE_CURRENT_FILE\": \"зберегти поточний файл\",\n    \"SEARCH\": \"пошук\",\n    \"SETTINGS\": \"налаштування\",\n    \"SHARE\": \"поділитися\",\n    \"SHARED_DRIVE\": \"спільний диск\",\n    \"SKIP_TO_CONTENT\": \"Перейти до контенту\",\n    \"SORT\": \"сортувати\",\n    \"SORT_BY_DATE\": \"сортувати за датою\",\n    \"SORT_BY_NAME\": \"сортувати за назвою\",\n    \"SORT_BY_SIZE\": \"сортувати за розміром\",\n    \"SORT_BY_TYPE\": \"сортувати за типом\",\n    \"STARRED\": \"позначено зіркою\",\n    \"SUPPORT\": \"підтримка\",\n    \"TAG\": \"тег\",\n    \"TAGS\": \"теги\",\n    \"THERE_IS_NOTHING_HERE\": \"тут нічого немає\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"файл {{VALUE}} видалено\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"файл {{VALUE}} було перейпрацює на основіменовано\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"посилання було скопійовано у буфер обміну\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"після цього посилання не буде дійсним\",\n    \"TIMEOUT\": \"час вийшов\",\n    \"TODO\": \"зробити\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"затори, спробуйте пізніше\",\n    \"UPLOAD\": \"завантажити\",\n    \"UPLOADER\": \"завантажувач\",\n    \"USERNAME\": \"ім'я користувача\",\n    \"VERSION\": \"версія\",\n    \"VIEWER\": \"глядач\",\n    \"WAITING\": \"очікування\",\n    \"YES\": \"так\",\n    \"YOUR_EMAIL_ADDRESS\": \"Ваша електронна адреса\",\n    \"YOUR_FILES\": \"ваші файли\",\n    \"YOUR_MASTER_PASSWORD\": \"ваш головний пароль\",\n    \"YOU_CANT_DO_THAT\": \"ти не можеш цього зробити\"\n}\n"
  },
  {
    "path": "public/assets/locales/vi.json",
    "content": "{\n    \"ABORTED\": \"đã hủy bỏ\",\n    \"ABORT_CURRENT_UPLOADS?\": \"hủy bỏ tải lên hiện tại?\",\n    \"ACTIVITY\": \"Hoạt động\",\n    \"ADVANCED\": \"nâng cao\",\n    \"ALL_DONE\": \"Hoàn tất\",\n    \"ALREADY_EXIST\": \"đã tồn tại\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"Một tệp có tên {{VALUE}} đã được tạo\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"Một thư mục có tên {{VALUE}} đã được tạo\",\n    \"BEAUTIFUL_URL\": \"đẹp_url\",\n    \"BOOKMARK\": \"dấu trang\",\n    \"CAMERA\": \"Máy ảnh\",\n    \"CANCEL\": \"hủy bỏ\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"không thể thiết lập kết nối\",\n    \"CANT_LOAD_THIS_PICTURE\": \"không thể tải hình ảnh này\",\n    \"CANT_USE_FILESYSTEM\": \"Không thể sử dụng hệ thống tập tin\",\n    \"CAN_RESHARE\": \"có thể chia sẻ lại\",\n    \"CODE\": \"mã\",\n    \"CONFIGURE\": \"cấu hình\",\n    \"CONFIRM_BY_TYPING\": \"xác nhận bằng cách gõ\",\n    \"CONNECT\": \"kết nối\",\n    \"CONNECTION_LOST\": \"mất liên lạc\",\n    \"COPIED_TO_CLIPBOARD\": \"sao chép vào clipboard\",\n    \"CREATE_A_NEW_LINK\": \"tạo một liên kết mới\",\n    \"CREATE_A_TAG\": \"tạo thẻ\",\n    \"CURRENT\": \"hiện hành\",\n    \"CURRENT_UPLOAD\": \"tải lên hiện tại\",\n    \"CUSTOM_LINK_URL\": \"URL liên kết tùy chỉnh\",\n    \"DASHBOARD\": \"bảng điều khiển\",\n    \"DATE\": \"ngày\",\n    \"DISPLAY_HIDDEN_FILES\": \"hiển thị các tập tin ẩn\",\n    \"DOESNT_MATCH\": \"không phù hợp\",\n    \"DONE\": \"Xong\",\n    \"DOWNLOAD\": \"Tải xuống\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"bạn có muốn lưu các thay đổi\",\n    \"DROP_HERE_TO_UPLOAD\": \"thả vào đây để tải lên\",\n    \"EDITOR\": \"biên tập viên\",\n    \"EMBED\": \"nhúng\",\n    \"EMPTY\": \"Trống\",\n    \"ENCRYPTION_KEY\": \"khóa mã hóa\",\n    \"ENDPOINT\": \"điểm cuối\",\n    \"ERROR\": \"lỗi\",\n    \"EXISTING_LINKS\": \"liên kết hiện có\",\n    \"EXPIRATION\": \"hết hạn\",\n    \"EXPORT_AS_{{VALUE}}\": \"xuất dưới dạng {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"thư mục truy cập thường xuyên sẽ được hiển thị ở đây\",\n    \"HIDE_HIDDEN_FILES\": \"ẩn các tập tin ẩn\",\n    \"HOME\": \"trang chủ\",\n    \"HOSTNAME*\": \"tên máy chủ\",\n    \"HOST_KEY\": \"khóa máy chủ\",\n    \"INCORRECT_PASSWORD\": \"mật khẩu không đúng\",\n    \"INFO\": \"thông tin\",\n    \"INTERNAL_ERROR\": \"Lỗi cục bộ\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"lỗi nội bộ: không thể tạo {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"tài khoản không hợp lệ\",\n    \"INVALID_PASSWORD\": \"mật khẩu không hợp lệ\",\n    \"LAYOUT\": \"bố cục\",\n    \"LOADING\": \"đang tải\",\n    \"LOCATION\": \"vị trí\",\n    \"MISSING_DEPENDENCY\": \"thiếu phụ thuộc\",\n    \"MORE_DETAILS\": \"thêm chi tiết\",\n    \"NAVIGATE\": \"điều hướng\",\n    \"NEW_FILE\": \"tập tin mới\",\n    \"NEW_FILE::SHORT\": \"tập tin mới\",\n    \"NEW_FOLDER\": \"từ điển mới\",\n    \"NEW_FOLDER::SHORT\": \"từ điển mới\",\n    \"NO\": \"Không\",\n    \"NOT_ALLOWED\": \"không cho phép\",\n    \"NOT_AUTHORISED\": \"không được uỷ quyền\",\n    \"NOT_FOUND\": \"không tìm thấy\",\n    \"NOT_IMPLEMENTED\": \"không được thực hiện\",\n    \"NOT_SUPPORTED\": \"không được hỗ trợ\",\n    \"NOT_VALID\": \"không hợp lệ\",\n    \"NUMBER_OF_CONNECTIONS\": \"số lượng kết nối\",\n    \"OK\": \"đồng ý\",\n    \"ONLY_FOR_USERS\": \"Chỉ dành cho người dùng\",\n    \"OOPS\": \"Rất tiếc!\",\n    \"PASSPHRASE\": \"cụm mật khẩu\",\n    \"PASSWORD\": \"mật khẩu\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"mật khẩu không thể để trống\",\n    \"PATH\": \"Đường dẫn\",\n    \"PERMISSION_DENIED\": \"Quyền truy cập bị từ chối\",\n    \"PICK_A_MASTER_PASSWORD\": \"Chọn mật khẩu chính\",\n    \"PORT\": \"Cổng\",\n    \"POWERED_BY\": \"cung cấp bởi\",\n    \"PROPERTIES\": \"Thuộc tính\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"bảo vệ quyền truy cập bằng mật khẩu\",\n    \"QUICK_ACCESS\": \"truy cập nhanh\",\n    \"REGION\": \"khu vực\",\n    \"REMEMBER_ME\": \"nhớ tôi\",\n    \"REMOVE\": \"Xóa\",\n    \"RENAME\": \"đổi tên\",\n    \"RENAME_AS\": \"đổi tên thành\",\n    \"RESTRICTIONS\": \"những hạn chế\",\n    \"RUNNING\": \"đang chạy\",\n    \"SAVE_CURRENT_FILE\": \"lưu tập tin hiện tại\",\n    \"SEARCH\": \"Tìm kiếm\",\n    \"SETTINGS\": \"cài đặt\",\n    \"SHARE\": \"chia sẻ\",\n    \"SHARED_DRIVE\": \"ổ chung\",\n    \"SKIP_TO_CONTENT\": \"Chuyển đến nội dung\",\n    \"SORT\": \"sắp xếp\",\n    \"SORT_BY_DATE\": \"lọc theo ngày\",\n    \"SORT_BY_NAME\": \"sắp xếp theo tên\",\n    \"SORT_BY_SIZE\": \"sắp xếp theo kích thước\",\n    \"SORT_BY_TYPE\": \"sắp xếp theo loại\",\n    \"STARRED\": \"được gắn sao\",\n    \"SUPPORT\": \"Hỗ trợ\",\n    \"TAG\": \"thẻ\",\n    \"TAGS\": \"các thẻ\",\n    \"THERE_IS_NOTHING_HERE\": \"Không có cái gì ở đây cả\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"tệp {{VALUE}} đã bị xóa\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"tệp {{VALUE}} đã được đổi tên\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"liên kết đã được sao chép trong clipboard\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"liên kết sẽ không có hiệu lực sau\",\n    \"TIMEOUT\": \"hết giờ\",\n    \"TODO\": \"làm\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"tắc nghẽn giao thông, thử lại sau\",\n    \"UPLOAD\": \"tải lên\",\n    \"UPLOADER\": \"người tải lên\",\n    \"USERNAME\": \"tên tài khoản\",\n    \"VERSION\": \"phiên bản\",\n    \"VIEWER\": \"người xem\",\n    \"WAITING\": \"đang chờ đợi\",\n    \"YES\": \"Đúng\",\n    \"YOUR_EMAIL_ADDRESS\": \"địa chỉ email của bạn\",\n    \"YOUR_FILES\": \"các tệp của bạn\",\n    \"YOUR_MASTER_PASSWORD\": \"mật khẩu chủ của bạn\",\n    \"YOU_CANT_DO_THAT\": \"bạn không thể làm điều đó\"\n}\n"
  },
  {
    "path": "public/assets/locales/zh.json",
    "content": "{\n    \"ABORTED\": \"已中止\",\n    \"ABORT_CURRENT_UPLOADS?\": \"中止当前上传？\",\n    \"ACTIVITY\": \"活动\",\n    \"ADVANCED\": \"高级\",\n    \"ALL_DONE\": \"全部完成\",\n    \"ALREADY_EXIST\": \"已经存在\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"已创建名为 {{VALUE}} 的文件\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"已创建名为 {{VALUE}} 的文件夹\",\n    \"BEAUTIFUL_URL\": \"好看的 URL\",\n    \"BOOKMARK\": \"书签\",\n    \"CAMERA\": \"相机\",\n    \"CANCEL\": \"取消\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"无法建立连接\",\n    \"CANT_LOAD_THIS_PICTURE\": \"无法加载这张图片\",\n    \"CANT_USE_FILESYSTEM\": \"无法访问文件系统\",\n    \"CAN_RESHARE\": \"可以转发\",\n    \"CODE\": \"代码\",\n    \"CONFIGURE\": \"配置\",\n    \"CONFIRM_BY_TYPING\": \"输入以确认\",\n    \"CONNECT\": \"连接\",\n    \"CONNECTION_LOST\": \"连接已断开\",\n    \"COPIED_TO_CLIPBOARD\": \"复制到剪贴板\",\n    \"CREATE_A_NEW_LINK\": \"创建一个新链接\",\n    \"CREATE_A_TAG\": \"创建标签\",\n    \"CURRENT\": \"目前\",\n    \"CURRENT_UPLOAD\": \"当前上传\",\n    \"CUSTOM_LINK_URL\": \"自定义链接网址\",\n    \"DASHBOARD\": \"仪表板\",\n    \"DATE\": \"日期\",\n    \"DISPLAY_HIDDEN_FILES\": \"显示隐藏文件\",\n    \"DOESNT_MATCH\": \"不匹配\",\n    \"DONE\": \"完成\",\n    \"DOWNLOAD\": \"下载\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"您要保存更改吗\",\n    \"DROP_HERE_TO_UPLOAD\": \"拖拽到这里上传\",\n    \"EDITOR\": \"编辑\",\n    \"EMBED\": \"嵌入\",\n    \"EMPTY\": \"空的\",\n    \"ENCRYPTION_KEY\": \"加密密钥\",\n    \"ENDPOINT\": \"端点\",\n    \"ERROR\": \"错误\",\n    \"EXISTING_LINKS\": \"现有链接\",\n    \"EXPIRATION\": \"到期\",\n    \"EXPORT_AS_{{VALUE}}\": \"导出为 {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"经常访问的文件夹将显示在这里\",\n    \"HIDE_HIDDEN_FILES\": \"不显示隐藏文件\",\n    \"HOME\": \"首页\",\n    \"HOSTNAME*\": \"主机名\",\n    \"HOST_KEY\": \"主机密钥\",\n    \"INCORRECT_PASSWORD\": \"密码错误\",\n    \"INFO\": \"信息\",\n    \"INTERNAL_ERROR\": \"内部错误\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"内部错误：无法创建 {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"无效账户\",\n    \"INVALID_PASSWORD\": \"无效密码\",\n    \"LAYOUT\": \"布局\",\n    \"LOADING\": \"加载中\",\n    \"LOCATION\": \"位置\",\n    \"MISSING_DEPENDENCY\": \"缺少依赖\",\n    \"MORE_DETAILS\": \"更多详情\",\n    \"NAVIGATE\": \"导航\",\n    \"NEW_FILE\": \"新文件\",\n    \"NEW_FILE::SHORT\": \"新文件\",\n    \"NEW_FOLDER\": \"新目录\",\n    \"NEW_FOLDER::SHORT\": \"新目录\",\n    \"NO\": \"否\",\n    \"NOT_ALLOWED\": \"不允许\",\n    \"NOT_AUTHORISED\": \"未经授权\",\n    \"NOT_FOUND\": \"未找到\",\n    \"NOT_IMPLEMENTED\": \"未实现\",\n    \"NOT_SUPPORTED\": \"不支持\",\n    \"NOT_VALID\": \"无效\",\n    \"NUMBER_OF_CONNECTIONS\": \"连接数\",\n    \"OK\": \"好\",\n    \"ONLY_FOR_USERS\": \"仅供用户\",\n    \"OOPS\": \"哎呀\",\n    \"PASSPHRASE\": \"密码短语\",\n    \"PASSWORD\": \"密码\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"密码不能为空\",\n    \"PATH\": \"路径\",\n    \"PERMISSION_DENIED\": \"拒绝访问\",\n    \"PICK_A_MASTER_PASSWORD\": \"选择一个主密码\",\n    \"PORT\": \"端口\",\n    \"POWERED_BY\": \"技术支持来自\",\n    \"PROPERTIES\": \"属性\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"用密码保护访问\",\n    \"QUICK_ACCESS\": \"快速访问\",\n    \"REGION\": \"区域\",\n    \"REMEMBER_ME\": \"记住账号\",\n    \"REMOVE\": \"移除\",\n    \"RENAME\": \"重命名\",\n    \"RENAME_AS\": \"重命名为\",\n    \"RESTRICTIONS\": \"限制条件\",\n    \"RUNNING\": \"运行中\",\n    \"SAVE_CURRENT_FILE\": \"保存当前文件\",\n    \"SEARCH\": \"搜索\",\n    \"SETTINGS\": \"参数\",\n    \"SHARE\": \"分享\",\n    \"SHARED_DRIVE\": \"共享驱动器\",\n    \"SKIP_TO_CONTENT\": \"跳转到内容\",\n    \"SORT\": \"排序\",\n    \"SORT_BY_DATE\": \"按日期排序\",\n    \"SORT_BY_NAME\": \"按名称分类\",\n    \"SORT_BY_SIZE\": \"按大小排序\",\n    \"SORT_BY_TYPE\": \"按类型排序\",\n    \"STARRED\": \"标星\",\n    \"SUPPORT\": \"支持\",\n    \"TAG\": \"标签\",\n    \"TAGS\": \"标签\",\n    \"THERE_IS_NOTHING_HERE\": \"这里什么都没有\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"文件 {{VALUE}} 已删除\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"文件 {{VALUE}} 已重命名\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"链接已复制到剪贴板\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"链接在之后将无效\",\n    \"TIMEOUT\": \"超时\",\n    \"TODO\": \"待办\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"网络拥堵，请稍后再试\",\n    \"UPLOAD\": \"上传\",\n    \"UPLOADER\": \"上传者\",\n    \"USERNAME\": \"用户名\",\n    \"VERSION\": \"版本\",\n    \"VIEWER\": \"查看者\",\n    \"WAITING\": \"等候\",\n    \"YES\": \"是\",\n    \"YOUR_EMAIL_ADDRESS\": \"您的电子邮件地址\",\n    \"YOUR_FILES\": \"您的文件\",\n    \"YOUR_MASTER_PASSWORD\": \"您的主密码\",\n    \"YOU_CANT_DO_THAT\": \"您无法那样做\"\n}\n"
  },
  {
    "path": "public/assets/locales/zh_tw.json",
    "content": "{\n    \"ABORTED\": \"已中止\",\n    \"ABORT_CURRENT_UPLOADS?\": \"中止目前上傳？\",\n    \"ACTIVITY\": \"活動\",\n    \"ADVANCED\": \"進階\",\n    \"ALL_DONE\": \"全部完成\",\n    \"ALREADY_EXIST\": \"已經存在\",\n    \"A_FILE_NAMED_{{VALUE}}_WAS_CREATED\": \"已建立名為 {{VALUE}} 的檔案\",\n    \"A_FOLDER_NAMED_{{VALUE}}_WAS_CREATED\": \"已建立名為 {{VALUE}} 的資料夾\",\n    \"BEAUTIFUL_URL\": \"美觀的 URL\",\n    \"BOOKMARK\": \"書籤\",\n    \"CAMERA\": \"相機\",\n    \"CANCEL\": \"取消\",\n    \"CANNOT_ESTABLISH_A_CONNECTION\": \"無法建立連線\",\n    \"CANT_LOAD_THIS_PICTURE\": \"無法載入這張圖片\",\n    \"CANT_USE_FILESYSTEM\": \"無法使用檔案系統\",\n    \"CAN_RESHARE\": \"可再次分享\",\n    \"CODE\": \"代碼\",\n    \"CONFIGURE\": \"設定\",\n    \"CONFIRM_BY_TYPING\": \"輸入以確認\",\n    \"CONNECT\": \"連線\",\n    \"CONNECTION_LOST\": \"連線已中斷\",\n    \"COPIED_TO_CLIPBOARD\": \"已複製到剪貼簿\",\n    \"CREATE_A_NEW_LINK\": \"建立一個新連結\",\n    \"CREATE_A_TAG\": \"建立標籤\",\n    \"CURRENT\": \"目前\",\n    \"CURRENT_UPLOAD\": \"目前上傳\",\n    \"CUSTOM_LINK_URL\": \"自訂連結網址\",\n    \"DASHBOARD\": \"儀表板\",\n    \"DATE\": \"日期\",\n    \"DISPLAY_HIDDEN_FILES\": \"顯示隱藏檔案\",\n    \"DOESNT_MATCH\": \"不符合\",\n    \"DONE\": \"完成\",\n    \"DOWNLOAD\": \"下載\",\n    \"DO_YOU_WANT_TO_SAVE_THE_CHANGES_?\": \"您要儲存變更嗎\",\n    \"DROP_HERE_TO_UPLOAD\": \"將檔案拖曳到此處上傳\",\n    \"EDITOR\": \"編輯\",\n    \"EMBED\": \"嵌入\",\n    \"EMPTY\": \"空的\",\n    \"ENCRYPTION_KEY\": \"加密金鑰\",\n    \"ENDPOINT\": \"端點\",\n    \"ERROR\": \"錯誤\",\n    \"EXISTING_LINKS\": \"現有連結\",\n    \"EXPIRATION\": \"到期\",\n    \"EXPORT_AS_{{VALUE}}\": \"匯出為 {{VALUE}}\",\n    \"FREQUENTLY_ACCESS_FOLDERS_WILL_BE_SHOWN_HERE\": \"經常存取的資料夾將顯示在這裡\",\n    \"HIDE_HIDDEN_FILES\": \"不顯示隱藏檔案\",\n    \"HOME\": \"首頁\",\n    \"HOSTNAME*\": \"主機名稱\",\n    \"HOST_KEY\": \"主機金鑰\",\n    \"INCORRECT_PASSWORD\": \"密碼錯誤\",\n    \"INFO\": \"資訊\",\n    \"INTERNAL_ERROR\": \"內部錯誤\",\n    \"INTERNAL_ERROR_CANT_CREATE_A_{{VALUE}}\": \"內部錯誤：無法建立 {{VALUE}}\",\n    \"INVALID_ACCOUNT\": \"無效帳戶\",\n    \"INVALID_PASSWORD\": \"無效的密碼\",\n    \"LAYOUT\": \"配置\",\n    \"LOADING\": \"載入中\",\n    \"LOCATION\": \"位置\",\n    \"MISSING_DEPENDENCY\": \"缺少相依性\",\n    \"MORE_DETAILS\": \"更多細節\",\n    \"NAVIGATE\": \"導覽\",\n    \"NEW_FILE\": \"新增檔案\",\n    \"NEW_FILE::SHORT\": \"新增檔案\",\n    \"NEW_FOLDER\": \"新資料夾\",\n    \"NEW_FOLDER::SHORT\": \"新資料夾\",\n    \"NO\": \"否\",\n    \"NOT_ALLOWED\": \"不允許\",\n    \"NOT_AUTHORISED\": \"未經授權\",\n    \"NOT_FOUND\": \"未找到\",\n    \"NOT_IMPLEMENTED\": \"未實作\",\n    \"NOT_SUPPORTED\": \"不支援\",\n    \"NOT_VALID\": \"無效\",\n    \"NUMBER_OF_CONNECTIONS\": \"連線數\",\n    \"OK\": \"確定\",\n    \"ONLY_FOR_USERS\": \"僅供使用者\",\n    \"OOPS\": \"哎呀\",\n    \"PASSPHRASE\": \"密碼短語\",\n    \"PASSWORD\": \"密碼\",\n    \"PASSWORD_CANT_BE_EMPTY\": \"密碼不能為空\",\n    \"PATH\": \"路徑\",\n    \"PERMISSION_DENIED\": \"權限不足\",\n    \"PICK_A_MASTER_PASSWORD\": \"選擇一個主密碼\",\n    \"PORT\": \"連接埠\",\n    \"POWERED_BY\": \"Powered by\",\n    \"PROPERTIES\": \"屬性\",\n    \"PROTECT_ACCESS_WITH_A_PASSWORD\": \"用密碼保護存取\",\n    \"QUICK_ACCESS\": \"快速存取\",\n    \"REGION\": \"區域\",\n    \"REMEMBER_ME\": \"記住我\",\n    \"REMOVE\": \"移除\",\n    \"RENAME\": \"重新命名\",\n    \"RENAME_AS\": \"重新命名為\",\n    \"RESTRICTIONS\": \"限制\",\n    \"RUNNING\": \"運作中\",\n    \"SAVE_CURRENT_FILE\": \"儲存目前檔案\",\n    \"SEARCH\": \"搜尋\",\n    \"SETTINGS\": \"設定\",\n    \"SHARE\": \"分享\",\n    \"SHARED_DRIVE\": \"共用磁碟\",\n    \"SKIP_TO_CONTENT\": \"跳轉到內容\",\n    \"SORT\": \"排序\",\n    \"SORT_BY_DATE\": \"依日期排序\",\n    \"SORT_BY_NAME\": \"依名稱排序\",\n    \"SORT_BY_SIZE\": \"按大小排序\",\n    \"SORT_BY_TYPE\": \"依類型排序\",\n    \"STARRED\": \"加星標\",\n    \"SUPPORT\": \"支援\",\n    \"TAG\": \"標籤\",\n    \"TAGS\": \"標籤\",\n    \"THERE_IS_NOTHING_HERE\": \"這裡什麼都沒有\",\n    \"THE_FILE_{{VALUE}}_WAS_DELETED\": \"檔案 {{VALUE}} 已刪除\",\n    \"THE_FILE_{{VALUE}}_WAS_RENAMED\": \"檔案 {{VALUE}} 已重新命名\",\n    \"THE_LINK_WAS_COPIED_IN_THE_CLIPBOARD\": \"連結已複製到剪貼簿\",\n    \"THE_LINK_WONT_BE_VALID_AFTER\": \"連結在此後將失效\",\n    \"TIMEOUT\": \"逾時\",\n    \"TODO\": \"待辦事項\",\n    \"TRAFFIC_CONGESTION_TRY_AGAIN_LATER\": \"網路壅塞，請稍後再試\",\n    \"UPLOAD\": \"上傳\",\n    \"UPLOADER\": \"上傳者\",\n    \"USERNAME\": \"使用者名稱\",\n    \"VERSION\": \"版本\",\n    \"VIEWER\": \"檢視者\",\n    \"WAITING\": \"等待中\",\n    \"YES\": \"是\",\n    \"YOUR_EMAIL_ADDRESS\": \"您的電子郵件地址\",\n    \"YOUR_FILES\": \"您的檔案\",\n    \"YOUR_MASTER_PASSWORD\": \"您的主密碼\",\n    \"YOU_CANT_DO_THAT\": \"您不能這麼做\"\n}\n"
  },
  {
    "path": "public/assets/model/backend.js",
    "content": "import rxjs from \"../lib/rx.js\";\nimport ajax from \"../lib/ajax.js\";\n\nconst backend$ = ajax({\n    url: \"api/backend\",\n    method: \"GET\",\n    responseType: \"json\"\n}).pipe(\n    rxjs.map(({ responseJSON }) => responseJSON.result),\n);\n\nexport function getBackends() {\n    return backend$;\n}\n"
  },
  {
    "path": "public/assets/model/chromecast.js",
    "content": "import { get as getConfig } from \"./config.js\";\n\nexport const Chromecast = new class ChromecastManager {\n    init() {\n        if (navigator.onLine === false) return Promise.resolve();\n        if (!getConfig(\"enable_chromecast\", false)) {\n            return Promise.resolve();\n        } else if (!(\"chrome\" in window)) {\n            return Promise.resolve();\n        } else if (location.hostname === \"localhost\" || location.hostname === \"127.0.0.1\") {\n            return Promise.resolve();\n        }\n        return new Promise((resolve) => {\n            if (document.head.querySelector(\"script#chromecast\")) return resolve(null);\n            const script = document.createElement(\"script\");\n            script.id = \"chromecast\";\n            script.src = \"https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1\";\n            script.onerror = () => resolve(null);\n            window[\"__onGCastApiAvailable\"] = function(isAvailable) {\n                if (isAvailable) window.cast.framework.CastContext.getInstance().setOptions({\n                    receiverApplicationId: window.chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,\n                    autoJoinPolicy: window.chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED,\n                });\n                resolve(null);\n            };\n            document.head.appendChild(script);\n        });\n    }\n\n    createLink(apiPath) {\n        const target = new URL(location.origin + apiPath);\n        const shareID = new URLSearchParams(location.search).get(\"search\");\n        if (shareID) target.searchParams.append(\"share\", shareID);\n        return target.toString();\n    }\n\n    createRequest(mediaInfo) {\n        if (!window.BEARER_TOKEN) throw new Error(\"Invalid account\");\n        // TODO: it would be much much nicer to set the authorization from an HTTP header\n        // but this would require to create a custom web receiver app, setup accounts on\n        // google, etc,... Until that happens, we're setting the authorization within the\n        // url. Once we have that app, the authorisation will come from a customData field\n        // of a chrome.cast.media.LoadRequest\n        const target = new URL(mediaInfo.contentId);\n        target.searchParams.append(\"authorization\", window.BEARER_TOKEN);\n        mediaInfo.contentId = target.toString();\n        return new window.chrome.cast.media.LoadRequest(mediaInfo);\n    }\n\n    context() {\n        if (!window.chrome?.cast?.isAvailable) return;\n        return window.cast.framework.CastContext.getInstance();\n    }\n\n    session() {\n        const context = this.context();\n        if (!context) return;\n        return context.getCurrentSession();\n    }\n\n    media() {\n        const session = this.session();\n        if (!session) return;\n        return session.getMediaSession();\n    }\n}();\n"
  },
  {
    "path": "public/assets/model/config.d.ts",
    "content": "interface Config {\n    [key: string]: any;\n    thumbnailer: string[];\n}\n\nexport function init(): Promise<Config>;\n\nexport function get(): Config;\n\nexport function get<T>(key: string, defaultValue?: T): T;\n\nexport function getVersion(): string;\n\nexport function query(): any;"
  },
  {
    "path": "public/assets/model/config.js",
    "content": "import rxjs from \"../lib/rx.js\";\nimport ajax from \"../lib/ajax.js\";\n\nconst config$ = ajax({\n    url: \"api/config\",\n    method: \"GET\",\n    responseType: \"json\",\n}).pipe(\n    rxjs.map(({ responseJSON }) => responseJSON.result),\n);\n\nlet CONFIG = {};\n\nexport async function init() {\n    const config = await config$.toPromise();\n    CONFIG = config;\n    return config;\n}\n\nexport function get(key, defaultValue) {\n    if (key) return CONFIG[key] || defaultValue;\n    return CONFIG;\n}\n\nexport function getVersion() {\n    return get(\"version\", \"na\");\n}\n\nexport function query() {\n    return config$;\n}\n"
  },
  {
    "path": "public/assets/model/plugin.js",
    "content": "import rxjs from \"../lib/rx.js\";\nimport ajax from \"../lib/ajax.js\";\n\nconst plugin$ = ajax({\n    url: \"api/plugin\",\n    method: \"GET\",\n    responseType: \"json\",\n}).pipe(\n    rxjs.map(({ responseJSON }) => responseJSON.result),\n);\n\nlet plugins = {};\n\nexport async function init() {\n    plugins = await plugin$.toPromise();\n}\n\nexport function get(mime) {\n    return plugins[mime];\n}\n\nexport async function load(mime) {\n    const specs = plugins[mime];\n    if (!specs) return null;\n    const [, url] = specs;\n    const module = await import(new URL(url, import.meta.url).href);\n    return module.default;\n}\n"
  },
  {
    "path": "public/assets/model/session.js",
    "content": "import rxjs from \"../lib/rx.js\";\nimport ajax from \"../lib/ajax.js\";\nimport { forwardURLParams } from \"../lib/path.js\";\n\nexport function getSession() {\n    return ajax({\n        url: withShare(\"api/session\"),\n        method: \"GET\",\n        responseType: \"json\"\n    }).pipe(\n        rxjs.map(({ responseJSON }) => responseJSON.result),\n        rxjs.tap(({ authorization }) => {\n            if (authorization) window.BEARER_TOKEN = authorization;\n        }),\n    );\n}\n\nexport function createSession(authenticationRequest) {\n    return ajax({\n        method: \"POST\",\n        url: withShare(\"api/session\"),\n        body: authenticationRequest,\n        responseType: \"json\",\n    }).pipe(\n        rxjs.tap(({ responseHeaders }) => {\n            if (responseHeaders.bearer) window.BEARER_TOKEN = responseHeaders.bearer; // see ctrl_boot_frontoffice.js -> setup_iframe\n        }),\n        rxjs.map(({ responseJSON }) => responseJSON.result),\n    );\n}\n\nexport function deleteSession() {\n    return ajax({\n        url: withShare(\"api/session\"),\n        method: \"DELETE\"\n    }).pipe(rxjs.tap(() => {\n        delete window.BEARER_TOKEN;\n    }));\n}\n\nwindow.addEventListener(\"pagechange\", async() => {\n    if (location.hash === \"\") return; // happy path\n    const token = new URLSearchParams(location.hash.replace(new RegExp(\"^#\"), \"?\")).get(\"bearer\");\n    if (token) window.BEARER_TOKEN = token;\n});\n\nconst withShare = (url) => forwardURLParams(url, [\"share\"]);\n"
  },
  {
    "path": "public/assets/pages/adminpage/animate.js",
    "content": "import { transition, slideYIn } from \"../../lib/animate.js\";\n\nexport default function($node) {\n    return transition($node, {\n        timeEnter: 100,\n        enter: slideYIn(3)\n    });\n}\n\nexport const cssHideMenu = \".component_menu_sidebar{transform: translateX(-300px)}\";\n"
  },
  {
    "path": "public/assets/pages/adminpage/component_box-item.js",
    "content": "import { ApplicationError } from \"../../lib/error.js\";\n\nclass BoxItem extends HTMLElement {\n    constructor() {\n        super();\n        this.attributeChangedCallback();\n    }\n\n    static get observedAttributes() {\n        return [\"data-selected\"];\n    }\n\n    attributeChangedCallback() {\n        this.innerHTML = this.render({\n            label: this.getAttribute(\"data-label\"),\n        });\n        this.classList.add(\"box-item\", \"pointer\", \"no-select\");\n    }\n\n    render({ label }) {\n        return `\n            <div>\n                <strong>${label}</strong>\n                <span class=\"no-select\">\n                    <span class=\"icon\">+</span>\n                </span>\n            </div>\n        `;\n    }\n\n    toggleSelection(opt = {}) {\n        const { tmpl, isSelected = !this.classList.contains(\"active\") } = opt;\n        const $icon = this.querySelector(\".icon\");\n        if (!$icon) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: no icon\");\n        if (isSelected) {\n            this.classList.add(\"active\");\n            if (tmpl) $icon.innerHTML = tmpl;\n        } else {\n            this.classList.remove(\"active\");\n            $icon.innerHTML = \"+\";\n        }\n    }\n}\n\ncustomElements.define(\"box-item\", BoxItem);\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_about.css",
    "content": ".component_page_about {\n  padding-top: 50px;\n  overflow-x: auto;\n}\n.component_page_about tr {\n    display: block;\n    background: white;\n    border: 2px solid #ebebec;\n    border-style: solid;\n    border-radius: 5px;\n    margin: 15px 0 0 0;\n    padding: 10px 15px;\n    color: var(--light);\n    font-size: 0.9rem;\n}\n.component_page_about td {\n    display: block;\n    min-width: 100%;\n}\n.component_page_about td:first-of-type {\n    font-weight: 600;\n    color: var(--color);\n    text-transform: capitalize;\n    font-size: 1.05rem;\n    border-bottom: 2px solid var(--border);\n    margin-bottom: 5px;\n    padding-bottom: 5px;\n}\n.component_page_about .small {\n    display: block;\n    margin-left: 20px;\n    font-size: 0.9rem;\n    padding: 0px 2px;\n}\n.component_page_admin .page_container .component_page_about a {\n    color: var(--ligh);\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_about.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, stateMutation } from \"../../lib/rx.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport { CSS } from \"../../helpers/loader.js\";\n\nimport AdminHOC from \"./decorator.js\";\nimport { get as getRelease } from \"./model_release.js\";\nimport transition from \"./animate.js\";\n\nexport default AdminHOC(async function(render) {\n    const $page = createElement(`\n        <div class=\"component_page_about\">\n            <style>${await CSS(import.meta.url, \"ctrl_about.css\")}</style>\n            <div data-bind=\"about\"><Loader /></div>\n        </div>\n    `);\n    render(transition($page));\n\n    effect(getRelease().pipe(\n        rxjs.map(({ html }) => html),\n        stateMutation(qs($page, \"[data-bind=\\\"about\\\"]\"), \"innerHTML\"),\n    ));\n});\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_activity.js",
    "content": "import { createElement, createRender } from \"../../lib/skeleton/index.js\";\n\nimport { initConfig } from \"./model_config.js\";\nimport componentLogForm from \"./ctrl_activity_form.js\";\nimport componentLogViewer from \"./ctrl_activity_viewer.js\";\nimport componentLogGraph from \"./ctrl_activity_graph.js\";\nimport componentAuditor from \"./ctrl_activity_audit.js\";\nimport transition from \"./animate.js\";\nimport AdminHOC from \"./decorator.js\";\n\nexport default AdminHOC(async function(render) {\n    const $page = createElement(`\n        <div class=\"component_logpage sticky\">\n            <h2>System Logs</h2>\n            <div class=\"component_logviewer\"></div>\n            <div class=\"component_stats\"></div>\n            <div class=\"component_logger\"></div>\n\n            <h2>Audit Report</h2>\n            <div class=\"component_audit\"></div>\n        <div>\n    `);\n    render(transition($page));\n    await initConfig();\n\n    componentLogViewer(createRender($page.querySelector(\".component_logviewer\")));\n    componentLogForm(createRender($page.querySelector(\".component_logger\")));\n    componentLogGraph(createRender($page.querySelector(\".component_stats\")));\n    componentAuditor(createRender($page.querySelector(\".component_audit\")));\n});\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_activity_audit.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, stateMutation, applyMutation } from \"../../lib/rx.js\";\nimport { qs, qsa } from \"../../lib/dom.js\";\nimport { createForm } from \"../../lib/form.js\";\nimport { formTmpl } from \"../../components/form.js\";\nimport { generateSkeleton } from \"../../components/skeleton.js\";\n\nimport { useForm$ } from \"./helper_form.js\";\nimport { get as getAudit, setLoader } from \"./model_audit.js\";\n\nexport default function(render) {\n    const $page = createElement(`\n        <div>\n            <form>\n                ${generateSkeleton(10)}\n            </form>\n            <div data-bind=\"auditor\"></div>\n        </div>\n    `);\n    render($page);\n    const audit$ = getAudit().pipe(rxjs.share());\n\n    // create the form on the dom\n    const setup$ = audit$.pipe(\n        rxjs.first(),\n        rxjs.map(({ form }) => form),\n        rxjs.mergeMap((formSpec) => createForm(formSpec, formTmpl())),\n        rxjs.map(($form) => [$form]),\n        applyMutation(qs($page, \"form\"), \"replaceChildren\"),\n    );\n    effect(setup$);\n\n    // setup the form handler\n    effect(setup$.pipe(\n        rxjs.first(),\n        rxjs.tap(() => updateLoop($page, audit$)),\n    ));\n}\n\nfunction updateLoop($page, audit$) {\n    // feature1: query result\n    effect(audit$.pipe(\n        rxjs.map(({ render }) => render),\n        stateMutation(qs($page, \"[data-bind=\\\"auditor\\\"]\"), \"innerHTML\"),\n        rxjs.tap(() => setLoader(false)),\n    ));\n\n    // feature2: update to the query form\n    effect(rxjs.of(null).pipe(\n        useForm$(() => qsa($page, \"form [name]\")),\n        rxjs.tap(() => setLoader(true)),\n        rxjs.debounceTime(1000),\n        rxjs.first(),\n        rxjs.map(() => qs($page, \"form\")),\n        rxjs.map(($form) => {\n            const formData = new FormData($form);\n            const p = new URLSearchParams();\n            for (const [key, value] of formData.entries()) {\n                if (!value) continue;\n                p.set(key.replace(new RegExp(\"^search\\.\"), \"\"), `${value}`);\n            }\n            return p;\n        }),\n        rxjs.tap((p) => updateLoop($page, getAudit(p).pipe(rxjs.share()))),\n    ));\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_activity_form.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, applyMutation } from \"../../lib/rx.js\";\nimport { qsa } from \"../../lib/dom.js\";\nimport { createForm, mutateForm } from \"../../lib/form.js\";\nimport { generateSkeleton } from \"../../components/skeleton.js\";\nimport notification from \"../../components/notification.js\";\nimport { formTmpl } from \"../../components/form.js\";\nimport { query as getConfig } from \"../../model/config.js\";\n\nimport { get as getAdminConfig, save as saveConfig } from \"./model_config.js\";\nimport { renderLeaf, useForm$, formObjToJSON$ } from \"./helper_form.js\";\n\nexport default function(render) {\n    const $form = createElement(`\n        <form style=\"min-height: 240px; margin-top:20px;\">\n            ${generateSkeleton(4)}\n        </form>\n    `);\n\n    render($form);\n\n    // feature1: render the form\n    const setup$ = getAdminConfig().pipe(\n        rxjs.map(({ log }) => ({ params: log })),\n        rxjs.map((formSpec) => createForm(formSpec, formTmpl({ renderLeaf }))),\n        rxjs.mergeMap((promise) => rxjs.from(promise)),\n        rxjs.map(($form) => [$form]),\n        applyMutation($form, \"replaceChildren\"),\n        rxjs.share(),\n    );\n    effect(setup$);\n\n    // feature2: form change\n    effect(setup$.pipe(\n        useForm$(() => qsa($form, \"[name]\")),\n        rxjs.mergeMap((formState) => getAdminConfig().pipe(\n            rxjs.first(),\n            rxjs.map((formSpec) => {\n                const fstate = Object.fromEntries(Object.entries(formState).map(([key, value]) => ([\n                    key.replace(new RegExp(\"^params\\.\"), \"log.\"),\n                    value,\n                ])));\n                return mutateForm(formSpec, fstate);\n            }),\n            formObjToJSON$(),\n        )),\n        rxjs.mergeMap((adminConfig) => getConfig().pipe(\n            rxjs.first(),\n            rxjs.map((publicConfig) => {\n                adminConfig[\"connections\"] = publicConfig[\"connections\"];\n                return adminConfig;\n            }),\n        )),\n        saveConfig(),\n        rxjs.catchError((err) => {\n            notification.error((err && err.message) || \"Oops\");\n            return rxjs.EMPTY;\n        }),\n    ));\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_activity_graph.css",
    "content": ".component_page_admin .component_stats {\n    clear: both;\n    height: 100px;\n    z-index: 1;\n    margin-bottom: 30px;\n}\n.component_page_admin .component_logviewer {\n    z-index: 0;\n}\n.component_stats .chart {\n    display: flex;\n    align-items: flex-end;\n    justify-content: end;\n    height: 100%;\n}\n.component_stats .chart .bar {\n    max-width: 45px;\n    cursor: pointer;\n    display: flex;\n    flex: 1;\n    border-top-left-radius: 3px;\n    border-top-right-radius: 3px;\n    background: #ebebec;\n    border: 2px solid var(--border);\n}\n.component_stats .chart .bar[title=\"0\"], .component_stats .chart .bar[title=\"1\"] {\n    border-top-left-radius: 0px;\n    border-top-right-radius: 0px;\n}\n.component_stats .legend {\n    display: flex;\n    justify-content: space-between;\n    color: var(--light);\n    font-size: 0.8rem;\n}\n.component_stats .legend .title {\n    font-style: italic;\n}\n.component_stats .legend.invisible {\n    opacity: 0;\n}\n.component_stats .component_skeleton {\n    margin-bottom: 5px;\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_activity_graph.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport { generateSkeleton } from \"../../components/skeleton.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\n\nimport { get as getLogs } from \"./model_log.js\";\n\nconst NUMBER_BUCKETS = 30;\nconst MIN_TIME_WIDTH = 5000;\n\nexport default async function(render) {\n    render(createElement(`<div>${generateSkeleton(3)}</div>`));\n    await loadCSS(import.meta.url, \"./ctrl_activity_graph.css\");\n\n    effect(getLogs(300).pipe(\n        rxjs.first(),\n        rxjs.repeat({ delay: 2500 }),\n        rxjs.scan(({ start, end, width, max, init = true, buckets = Array(NUMBER_BUCKETS).fill(0) }, logfile) => {\n            const times = [];\n            let i = 0; while (i < logfile.length) {\n                const t = new Date(logfile.substring(i, i + 19)).getTime();\n                if (!isNaN(t)) times.push(t);\n                const end = logfile.indexOf(\"\\n\", i);\n                i = end === -1 ? logfile.length : end + 1;\n            }\n            if (init === true) {\n                start = times[0];\n                end = times[times.length - 1];\n                width = Math.max((end - start) / NUMBER_BUCKETS, MIN_TIME_WIDTH);\n                for (let i=times.length-1; i>=0; i--) {\n                    let idx = Math.floor((times[i] - start) / width);\n                    if (idx === NUMBER_BUCKETS) idx -= 1;\n                    buckets[idx] += 1;\n                }\n                for (let i=buckets.length-1; i>=0; i--) {\n                    if (buckets[i] === 0) buckets[i] = -1;\n                    else break;\n                }\n                max = Math.max(1, ...buckets);\n                init = false;\n            } else {\n                for (let i=times.length-1; i>=0; i--) {\n                    const current = times[i];\n                    // start          end       current\n                    //   |             |           |\n                    //   |=============|<--  new -->\n                    if (current <= end) {\n                        break;\n                    }\n                    const idx = Math.floor((times[i] - start) / width);\n                    if (!buckets[idx]) buckets[idx] = 0;\n                    buckets[idx] += 1;\n                }\n                const shift = buckets.length - NUMBER_BUCKETS;\n                for (let i=0; i<shift; i++) {\n                    buckets.shift();\n                    start += width;\n                }\n                end = times[times.length - 1];\n            }\n            return { start, end, width, buckets, max, init };\n        }, {}),\n        rxjs.tap(({ buckets, start, end, max }) => {\n            const $root = document.createDocumentFragment();\n            const $chart = createElement(`<div class=\"chart\"></div>`);\n            let display = true;\n            for (let i = 0; i < buckets.length; i++) {\n                if (buckets[i] < 0) {\n                    display = false;\n                    continue;\n                }\n                const $bar = createElement(`<div class=\"bar\" title=\"${buckets[i]}\"></div>`);\n                const height = Math.sqrt(buckets[i]) / Math.sqrt(max) * 100;\n                $bar.style.height = Math.min(height, 120) + \"%\";\n                $chart.appendChild($bar);\n            }\n            $root.appendChild($chart);\n            $root.appendChild(createElement(`\n                <div class=\"legend\">\n                    <span>${new Date(start).toLocaleTimeString()}</span>\n                    <span class=\"title\">Log Events</span>\n                    <span>${new Date(end).toLocaleTimeString()}</span>\n                </div>\n            `));\n            if (display) render($root);\n        }),\n        rxjs.catchError((err) => rxjs.EMPTY),\n    ));\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_activity_viewer.css",
    "content": ".component_logpage button{\n    width: inherit;\n    float: right;\n    margin-top: 5px;\n    padding-left: 20px;\n    padding-right: 20px;\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_activity_viewer.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, stateMutation } from \"../../lib/rx.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport { CSS } from \"../../helpers/loader.js\";\n\nimport { get as getLogs, url as getLogUrl } from \"./model_log.js\";\n\nexport default async function(render) {\n    const $page = createElement(`\n        <div>\n            <style>${await CSS(import.meta.url, \"ctrl_activity_viewer.css\")}</style>\n            <pre style=\"height:350px; max-height: 350px\">…</pre>\n            <a href=\"${getLogUrl()}\" download=\"${logname()}\">\n                <button class=\"component_button primary\">Download</button>\n            </a>\n            <br><br>\n        </div>\n    `);\n    const $log = qs($page, \"pre\");\n    render($page);\n\n    effect(rxjs.of(null).pipe(\n        rxjs.mergeMap(() => $log.matches(\":hover\") ? rxjs.EMPTY : getLogs()),\n        rxjs.map((logData) => logData + \"\\n\\n\\n\\n\\n\"),\n        stateMutation($log, \"textContent\"),\n        rxjs.tap(() => {\n            if ($log?.scrollTop !== 0) return;\n            $log.scrollTop = $log.scrollHeight;\n        }),\n        rxjs.repeat({ delay: 2500 }),\n        rxjs.catchError(() => rxjs.EMPTY),\n    ));\n}\n\nfunction logname() {\n    const t = new Date().toISOString().substring(0, 10).replace(/-/g, \"\");\n    return `access_${t}.log`;\n};\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_login.css",
    "content": ".component_page_adminlogin {\n    max-width: 300px;\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_login.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, stateMutation, applyMutation, preventDefault } from \"../../lib/rx.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport { transition, zoomIn } from \"../../lib/animate.js\";\nimport { AjaxError } from \"../../lib/error.js\";\nimport ctrlError from \"../ctrl_error.js\";\nimport { CSS } from \"../../helpers/loader.js\";\nimport notification from \"../../components/notification.js\";\nimport \"../../components/icon.js\";\n\nimport { authenticate$ } from \"./model_admin_session.js\";\n\nexport default async function(render) {\n    const $form = createElement(`\n        <div class=\"component_container component_page_adminlogin\">\n            <style>${await CSS(import.meta.url, \"ctrl_login.css\")}</style>\n            <form>\n                <div class=\"input_group\">\n                    <input type=\"password\" name=\"password\" placeholder=\"Password\" class=\"component_input\" autocomplete>\n                    <button class=\"transparent\">\n                        <component-icon name=\"arrow_right\"></component-icon>\n                    </button>\n                </div>\n            </form>\n        </div>\n    `);\n\n    // feature: nice transition\n    render(transition($form, {\n        timeEnter: 250,\n        enter: zoomIn(1.2),\n        timeLeave: 0\n    }));\n\n    // feature: form interactions\n    effect(rxjs.fromEvent(qs($form, \"form\"), \"submit\").pipe(\n        preventDefault(),\n        // STEP1: loading spinner\n        rxjs.mapTo([\"name\", \"loading\"]),\n        applyMutation(qs($form, \"component-icon\"), \"setAttribute\"),\n        // STEP2: attempt to login\n        rxjs.map(() => ({ password: qs($form, \"[name=\\\"password\\\"]\").value })),\n        rxjs.switchMap((creds) => authenticate$(creds).pipe(\n            rxjs.catchError((err) => {\n                if (err instanceof AjaxError) {\n                    switch (err.code()) {\n                    case \"INTERNAL_SERVER_ERROR\":\n                        return rxjs.throwError(err);\n                    case \"FORBIDDEN\":\n                        return rxjs.of(false);\n                    }\n                }\n                notification.error(err && err.message);\n                return rxjs.of(false);\n            }),\n        )),\n        // STEP3: update the UI when authentication fails, happy path is handle at the middleware\n        //        level one layer above as the login ctrl has no idea what to show after login\n        rxjs.filter((ok) => !ok),\n        rxjs.mapTo([\"name\", \"arrow_right\"]), applyMutation(qs($form, \"component-icon\"), \"setAttribute\"),\n        rxjs.mapTo(\"\"), stateMutation(qs($form, \"[name=\\\"password\\\"]\"), \"value\"),\n        rxjs.mapTo([\"error\"]), applyMutation(qs($form, \".input_group\"), \"classList\", \"add\"),\n        rxjs.delay(300), applyMutation(qs($form, \".input_group\"), \"classList\", \"remove\"),\n        rxjs.catchError(ctrlError(render)),\n    ));\n\n    // feature: autofocus\n    effect(rxjs.of(null).pipe(\n        applyMutation(qs($form, \"input\"), \"focus\")\n    ));\n\n    // feature: vertically center the form\n    effect(rxjs.fromEvent(window, \"resize\").pipe(\n        rxjs.startWith(null),\n        rxjs.map(() => [\"margin-top\", `${Math.floor(window.innerHeight / 3)}px`]),\n        applyMutation($form, \"style\", \"setProperty\")\n    ));\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_settings.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, applyMutation } from \"../../lib/rx.js\";\nimport { qs, qsa } from \"../../lib/dom.js\";\nimport { createForm, mutateForm } from \"../../lib/form.js\";\nimport { formTmpl } from \"../../components/form.js\";\nimport { generateSkeleton } from \"../../components/skeleton.js\";\nimport { query as getConfig } from \"../../model/config.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport { get as getAdminConfig, save as saveConfig, initConfig } from \"./model_config.js\";\nimport { renderLeaf, useForm$, formObjToJSON$ } from \"./helper_form.js\";\nimport transition from \"./animate.js\";\nimport AdminHOC from \"./decorator.js\";\n\nexport default AdminHOC(async function(render) {\n    const $container = createElement(`\n        <div class=\"component_settingspage sticky\">\n            <form data-bind=\"form\" class=\"formbuilder\">\n                <h2>…</h2>\n                ${generateSkeleton(10)}\n            </form>\n        </div>\n    `);\n    render(transition($container));\n    await initConfig();\n\n    const config$ = getAdminConfig().pipe(\n        rxjs.first(),\n        reshapeConfigBeforeDisplay,\n    );\n\n    const tmpl = formTmpl({\n        renderNode: ({ level, format, label }) => {\n            if (level !== 0) return null;\n            return createElement(`\n                <div>\n                    <h2>${format(label)}</h2>\n                    <div data-bind=\"children\"></div>\n                </div>\n            `);\n        },\n        renderLeaf,\n        autocomplete: false,\n    });\n\n    // feature: setup the form\n    const init$ = config$.pipe(\n        rxjs.mergeMap((formSpec) => createForm(formSpec, tmpl)),\n        rxjs.map(($form) => [$form]),\n        applyMutation(qs($container, `[data-bind=\"form\"]`), \"replaceChildren\"),\n        rxjs.share(),\n    );\n    effect(init$);\n\n    // feature: handle form change\n    effect(init$.pipe(\n        useForm$(() => qsa($container, \"[data-bind=\\\"form\\\"] [name]\")),\n        rxjs.debounceTime(250),\n        rxjs.mergeMap((formState) => config$.pipe(\n            rxjs.first(),\n            rxjs.map((formSpec) => mutateForm(formSpec, formState)),\n        )),\n        reshapeConfigBeforeSave,\n        saveConfig(),\n        rxjs.catchError(ctrlError()),\n    ));\n});\n\n// the config contains stuff wich we don't want to show in this page such as:\n// - the middleware info which is set in the backend page\n// - the connections info which is set in the backend page\n// - the constant info which is for the setup page\nconst reshapeConfigBeforeDisplay = rxjs.map((cfg) => {\n    const { constant, middleware, connections, ...other } = cfg;\n    return other;\n});\n\n// before saving things back to the server, we want to hydrate the config and insert back:\n// - the middleware info\n// - the connections info\nconst reshapeConfigBeforeSave = rxjs.pipe(\n    rxjs.mergeMap((configWithMissingKeys) => getAdminConfig().pipe(\n        rxjs.first(),\n        rxjs.map((config) => {\n            configWithMissingKeys[\"middleware\"] = config[\"middleware\"];\n            return configWithMissingKeys;\n        }),\n        formObjToJSON$(),\n    )),\n    rxjs.mergeMap((adminConfig) => getConfig().pipe(\n        rxjs.first(),\n        rxjs.map((publicConfig) => {\n            adminConfig[\"connections\"] = publicConfig[\"connections\"];\n            return adminConfig;\n        }),\n    )),\n);\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_setup.css",
    "content": ".component_setup {\n    transform: none !important;\n}\n.component_setup h4 {\n    user-select: none;\n    text-align: center;\n    font-size: 1.4em;\n    font-weight: 500;\n    padding: 20px 0 0px 0;\n}\n.component_setup h4 .component_icon {\n    width: 1.3em;\n    cursor: pointer;\n}\n.component_setup h4 .component_icon[alt=\"loading\"] {\n    opacity: 0;\n}\n.component_setup #step1 p {\n    font-size: 1.05em;\n    margin-bottom: 5px;\n}\n.component_setup #step1 form {\n    max-width: 400px;\n}\n\n.component_setup #step2 .component_dependency_installed {\n    margin: 10px 0;\n    padding: 10px 10px;\n    border-radius: 3px;\n    color: rgba(0, 0, 0, 0.6);\n}\n.component_setup #step2 .component_dependency_installed.yes {\n    background: var(--success);\n}\n.component_setup #step2 .component_dependency_installed.no {\n    background: var(--primary);\n}\n.component_setup #step2 .component_dependency_installed.no.severe {\n    background: var(--error);\n}\n.component_setup #step2 .component_dependency_installed strong {\n    font-weight: bold;\n    font-style: italic;\n    display: block;\n}\n.component_setup .stepper-form-appear, .component_setup .stepper-form-enter {\n    transition-delay: 0.3s;\n    transform: scale(1.02);\n    opacity: 0;\n    transition: all 0.3s ease;\n}\n.component_setup .stepper-form-appear.stepper-form-appear-active,\n.component_setup .stepper-form-appear.stepper-form-enter-active,\n.component_setup .stepper-form-enter.stepper-form-appear-active,\n.component_setup .stepper-form-enter.stepper-form-enter-active {\n    opacity: 1;\n    transform: scale(1);\n}\n.component_setup .component_icon {\n    width: 30px;\n}\n\n.pulse {\n  animation: zoomPulse 1.2s ease-in-out infinite;\n  transform-origin: center left;\n}\n@keyframes zoomPulse {\n  0%, 100% { transform: scale(1.03); }\n  50%      { transform: scale(1.07); }\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_setup.js",
    "content": "import { createElement, createRender, onDestroy } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, applyMutation, preventDefault } from \"../../lib/rx.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport { ApplicationError } from \"../../lib/error.js\";\nimport { transition, animate, zoomIn, slideXOut, slideXIn } from \"../../lib/animate.js\";\nimport bcrypt from \"../../lib/vendor/bcrypt.js\";\nimport { CSS } from \"../../helpers/loader.js\";\nimport { createModal, MODAL_RIGHT_BUTTON } from \"../../components/modal.js\";\nimport { query as getConfig } from \"../../model/config.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport { get as getAdminConfig, save as saveConfig } from \"./model_config.js\";\nimport WithShell from \"./decorator_sidemenu.js\";\nimport { cssHideMenu } from \"./animate.js\";\nimport { formObjToJSON$ } from \"./helper_form.js\";\nimport { getDeps } from \"./model_setup.js\";\nimport { authenticate$, isAdmin$ } from \"./model_admin_session.js\";\n\nimport \"../../components/icon.js\";\n\nconst stepper$ = new rxjs.BehaviorSubject(\n    parseInt(new URLSearchParams(location.search).get(\"step\")) || 1\n);\n\nexport default setupHOC(async function(render) {\n    const $page = createElement(`\n        <div class=\"component_setup\">\n           <div data-bind=\"multistep-form\"></div>\n           <style>${await CSS(import.meta.url, \"ctrl_setup.css\")}</style>\n        </div>\n    `);\n    render($page);\n\n    let pwd = \"\";\n    effect(stepper$.pipe(\n        rxjs.map((step) => {\n            if (step === 1) return WithShell(componentStep1, { setPassword: (p) => pwd = p });\n            else if (step === 2) return WithShell(componentStep2, { getPassword: () => pwd });\n            throw new ApplicationError(\"INTERNAL_ERROR\", \"Assumption failed\");\n        }),\n        rxjs.tap((ctrl) => ctrl(createRender(qs($page, \"[data-bind=\\\"multistep-form\\\"]\")))),\n        rxjs.catchError(ctrlError(render)),\n    ));\n});\n\nfunction setupHOC(ctrlWrapped) {\n    const ctrlGoAdmin = () => location.href = \"/admin/\";\n    return (render) => {\n        effect(isAdmin$().pipe(\n            rxjs.map((isAdmin) => isAdmin ? ctrlWrapped : ctrlGoAdmin),\n            rxjs.tap((ctrl) => ctrl(render)),\n            rxjs.catchError(ctrlError(render)),\n        ));\n    };\n}\n\nfunction componentStep1(render, { setPassword }) {\n    const $page = createElement(`\n        <div id=\"step1\">\n            <h4>Welcome Aboard!</h4>\n            <div>\n                <p>First thing first, setup your password: </p>\n                <form>\n                    <div class=\"input_group\">\n                        <input type=\"password\" name=\"password\" placeholder=\"Password\" class=\"component_input\" autocomplete autofocus>\n                        <button class=\"transparent\">\n                            <component-icon name=\"arrow_right\"></component-icon>\n                        </button>\n                    </div>\n                </form>\n            </div>\n            <style>${cssHideMenu}</style>\n        </div>\n    `);\n    render(transition($page, {\n        timeEnter: 250,\n        enter: zoomIn(1.2),\n        timeLeave: 0\n    }));\n\n    qs($page, \"input\").focus();\n\n    // feature: form handling\n    effect(rxjs.fromEvent(qs($page, \"form\"), \"submit\").pipe(\n        preventDefault(),\n        rxjs.mapTo([\"name\", \"loading\"]), applyMutation(qs($page, \"component-icon\"), \"setAttribute\"),\n        rxjs.map(() => qs($page, \"input\").value),\n        rxjs.mergeMap((pwd) => getAdminConfig().pipe(\n            rxjs.first(),\n            rxjs.map((config) => {\n                config[\"auth\"][\"admin\"][\"value\"] = bcrypt.hashSync(pwd);\n                return config;\n            }),\n            reshapeConfigBeforeSave,\n            saveConfig(),\n            rxjs.mergeMap(() => authenticate$({ password: pwd })),\n            rxjs.tap(() => setPassword(pwd)),\n        )),\n        rxjs.tap(() => animate($page, { time: 200, keyframes: slideXOut(-30) })),\n        rxjs.delay(200),\n        rxjs.tap(() => stepper$.next(2))\n    ));\n}\n\nconst reshapeConfigBeforeSave = rxjs.pipe(\n    formObjToJSON$(),\n    rxjs.mergeMap((config) => getConfig().pipe(\n        rxjs.first(),\n        rxjs.map((publicConfig) => {\n            config[\"connections\"] = publicConfig[\"connections\"];\n            return config;\n        }),\n    )),\n);\n\nfunction componentStep2(render, { getPassword }) {\n    const $page = createElement(`\n        <div id=\"step2\">\n            <h4>\n                <component-icon name=\"arrow_left\" data-bind=\"previous\"></component-icon>\n                You're at the Helm now\n            </h4>\n            <div data-bind=\"dependencies\"></div>\n            <div data-bind=\"onboarding\"></div>\n            <style id=\"cssHideMenu\">${cssHideMenu}</style>\n        </div>\n    `);\n    render($page);\n\n    // feature: show state of dependencies\n    effect(getDeps({ getPassword }).pipe(\n        rxjs.first(),\n        rxjs.mergeMap((deps) => deps),\n        rxjs.map(({ name_success, name_failure, pass, severe, message }) => ({\n            className: (severe ? \"severe\" : \"\") + \" \" + (pass ? \"yes\" : \"no\"),\n            label: pass ? name_success : name_failure,\n            $extraLabel: pass ? null : message,\n        })),\n        rxjs.mergeMap(({ label, className, $extraLabel }) => rxjs.of(createElement(`\n            <div class=\"component_dependency_installed ${className}\">\n                <strong>${label}</strong>\n            </div>\n        `)).pipe(rxjs.tap(($node) => $extraLabel && $node.appendChild($extraLabel)))),\n        applyMutation(qs($page, \"[data-bind=\\\"dependencies\\\"]\"), \"appendChild\"),\n    ));\n\n    // feature: navigate previous step\n    effect(rxjs.fromEvent(qs($page, \"[data-bind=\\\"previous\\\"]\"), \"click\").pipe(\n        rxjs.tap(() => stepper$.next(1))\n    ));\n\n    // feature: reveal animation\n    effect(rxjs.of(null).pipe(\n        rxjs.tap(() => animate(qs($page, \"h4\"), { time: 200, keyframes: slideXIn(30) })),\n        rxjs.delay(200),\n        rxjs.mapTo([]), applyMutation(qs($page, \"style#cssHideMenu\"), \"remove\")\n    ));\n\n    // feature: telemetry popup\n    const componentTelemetryPopup = (render) => {\n        const $modal = createElement(`\n            <div>\n                <p style=\"text-align: justify;\">\n                    Help making this software better by sending crash reports and anonymous usage statistics\n                </p>\n                <form style=\"font-size: 0.9em; margin-top: 10px; line-height: 1rem;\">\n                    <label>\n                        <div class=\"component_checkbox\">\n                            <input type=\"checkbox\">\n                            <span class=\"indicator\"></span>\n                        </div>\n                        The data is never shared with a third party.\n                    </label>\n                </form>\n            </div>\n        `);\n        const ret = new rxjs.Subject();\n        const $checkbox = qs($modal, `[type=\"checkbox\"]`);\n        const close = render($modal, (id) => {\n            if (id !== MODAL_RIGHT_BUTTON) {\n                ret.next(false);\n                ret.complete();\n                return ret.toPromise();\n            }\n            ret.next($checkbox.checked);\n            ret.complete();\n            return ret.toPromise();\n        });\n        $checkbox.oninput = (e) => {\n            if (!e.target.checked) return;\n            close(MODAL_RIGHT_BUTTON);\n        };\n        return ret.toPromise();\n    };\n\n    // feature: telemetry modal\n    onDestroy(() => requestAnimationFrame(() => getAdminConfig().pipe(\n        reshapeConfigBeforeSave,\n        rxjs.first(),\n        rxjs.delay(300),\n        rxjs.filter((config) => config[\"log\"][\"telemetry\"] !== true),\n        rxjs.mergeMap(async(config) => {\n            const enabled = await componentTelemetryPopup(createModal({ withButtonsRight: \"OK\" }));\n            if (enabled === false) return null;\n            config[\"log\"][\"telemetry\"] = enabled;\n            return config;\n        }),\n        rxjs.mergeMap((config) => {\n            if (config) return rxjs.of(config).pipe(saveConfig());\n            return rxjs.of(null);\n        }),\n    ).toPromise()));\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_storage.css",
    "content": ".component_dashboard .box-container {\n    display: flex;\n    flex-wrap: wrap;\n    margin-left: -3px;\n    margin-right: -3px;\n}\n.component_dashboard .box-container .box-item {\n    position: relative;\n    width: 20%;\n}\n.component_dashboard .box-container .box-item strong {\n    font-weight: 400;\n}\n@media (max-width: 1350px) {\n    .component_dashboard .box-container .box-item {\n        width: 25%;\n    }\n}\n@media (max-width: 900px) {\n    .component_dashboard .box-container .box-item {\n        width: 33.33%;\n    }\n}\n@media (max-width: 750px) {\n    .component_dashboard .box-container .box-item {\n        width: 50%;\n    }\n}\n.component_dashboard .box-container .box-item > div {\n    margin: 3px;\n    padding: 30px 0;\n    text-align: center;\n    color: var(--light);\n    text-shadow: 0px 0px 1px rgba(0, 0, 0, 0.5);\n    font-size: 1.1em;\n    text-transform: uppercase;\n    border-radius: 2px;\n\n    background: var(--border);\n    border: 2px solid var(--border);\n}\n@media (max-width: 900px) {\n    .component_dashboard .box-container .box-item > div {\n        padding: 25px 0;\n    }\n}\n@media (max-width: 750px) {\n    .component_dashboard .box-container .box-item > div {\n        padding: 20px 0;\n    }\n}\n.component_dashboard .box-container .box-item > div > span {\n    display: none;\n}\n.component_dashboard .box-container .box-item > div:hover > span {\n    display: block;\n    cursor: pointer;\n    position: absolute;\n    top: 0px;\n    right: 0px;\n    left: 0;\n    bottom: 0;\n    font-size: 2.3rem;\n    font-family: monospace;\n    line-height: 25px;\n    color: var(--color);\n    text-shadow: none;\n    background: var(--emphasis-primary);\n    padding: 18px 0;\n    margin: 6px;\n    opacity: 0.95;\n}\n@media (max-width: 900px) {\n    .component_dashboard .box-container .box-item > div:hover > span {\n        padding: 12px 0;\n    }\n}\n@media (max-width: 750px) {\n    .component_dashboard .box-container .box-item > div:hover > span {\n        padding: 7px 0;\n    }\n}\n.component_dashboard .box-container .box-item > div:hover > span .icon {\n    background: var(--primary);\n    border-radius: 50%;\n    width: 40px;\n    height: 40px;\n    display: inline-block;\n    line-height: 40px;\n    opacity: 0.6;\n    color: white;\n}\n.component_dashboard .box-container .box-item > div:hover > span .icon .component_icon {\n    padding: 7px;\n    width: 25px;\n    height: 25px;\n}\n.component_dashboard .box-container .box-item.active > div {\n    background: var(--primary);\n    transition: background 0.1s;\n}\n.component_dashboard .box-container .box-item.pointer {\n    cursor: pointer;\n}\n.component_dashboard .box-container .box-item.no-select {\n    user-select: none;\n}\n.component_dashboard form fieldset {\n    position: relative;\n}\n.component_dashboard form fieldset .icons {\n    position: absolute;\n    border-radius: 50%;\n    padding: 10px;\n    border: 3px solid var(--bg-color);\n    background: var(--primary);\n    cursor: pointer;\n    right: -20px;\n    top: -30px;\n}\n.component_dashboard form fieldset .icons .component_icon {\n    height: 20px;\n}\n.component_dashboard form fieldset label > img {\n    height: 200px;\n    display: flex;\n    margin: 5px auto 0 auto;\n}\n.component_dashboard .component_storagebackend form {\n    width: calc(100% - 15px);\n}\n\n.component_dashboard [data-bind=\"authentication_middleware\"] .component_checkbox {\n    position: relative;\n    top: 5px;\n}\n\n.component_dashboard [data-bind=\"idp\"] > .formbuilder > fieldset > legend {\n    display: none;\n}\n.component_dashboard [data-bind=\"attribute-mapping\"] > .formbuilder > fieldset > legend {\n    color: var(--color);\n    font-weight: 600;\n    font-size: 1.15rem;\n    display: contents;\n    text-transform: none;\n}\n.component_dashboard [data-bind=\"attribute-mapping\"] > .formbuilder > fieldset > legend:before {\n    content: \" \";\n    display: inline-block;\n    height: 20px;\n    width: 20px;\n    background: url(data:image/svg+xml;base64,PHN2ZyBjbGFzcz0ic3ZnLWljb24iIHN0eWxlPSJmaWxsOiAjNTc1OTVBIiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTc2LjgxODM0NyA5ODEuMzMzMzMzYTE4OC4zMzA2NjcgMTg4LjMzMDY2NyAwIDAgMS01Ny42LTEyLjhBNTcuMzQ0IDU3LjM0NCAwIDAgMSAwLjAxODM0NyA5MjQuMjQ1MzMzYTU0LjYxMzMzMyA1NC42MTMzMzMgMCAwIDEgMTkuMi0zOC4wNTg2NjYgOTEuNTIgOTEuNTIgMCAwIDEgMzguNC0xMi44aDMyYzE5LjIgMCAzOC40IDYuMzU3MzMzIDY0IDYuMzU3MzMzYTY5LjQxODY2NyA2OS40MTg2NjcgMCAwIDAgNTEuMi0xMi44IDI5Ny40NzIgMjk3LjQ3MiAwIDAgMCAzOC40LTk1LjE0NjY2N2w4OS42LTM2MS41MTQ2NjZIMjI0LjAxODM0N2wxOS4yLTY5Ljc2aDExNS4ydi0xMi44YTQyOC45NzA2NjcgNDI4Ljk3MDY2NyAwIDAgMSA1MS4yLTE1Mi4yMzQ2NjcgMjQzLjIgMjQzLjIgMCAwIDEgMTAyLjQtOTUuMTQ2NjY3IDM4OC42MDggMzg4LjYwOCAwIDAgMSAxMjEuNi0zOC4wNTg2NjYgMTIxLjQyOTMzMyAxMjEuNDI5MzMzIDAgMCAxIDUxLjIgMTIuOCA1Ny4zNDQgNTcuMzQ0IDAgMCAxIDE5LjIgNDQuNDE2IDg5LjYgODkuNiAwIDAgMS0xMi44IDM4LjA1ODY2NiA5MS41MiA5MS41MiAwIDAgMS0zOC40IDEyLjggMjI5LjIwNTMzMyAyMjkuMjA1MzMzIDAgMCAxLTY0LTEyLjhjLTEyLjggMC0xOS4yLTYuMzU3MzMzLTMyLTYuMzU3MzMzYTY2LjI2MTMzMyA2Ni4yNjEzMzMgMCAwIDAtNDQuOCAzMS43MDEzMzMgNTU0LjY2NjY2NyA1NTQuNjY2NjY3IDAgMCAwLTMyIDk1LjE0NjY2N2wtMjUuNiA4Mi40MzIgMjc1LjItNi4zNTczMzNoNi40YTU1LjQ2NjY2NyA1NS40NjY2NjcgMCAwIDEgMzguNCAxOS4wMjkzMzMgNDM0LjUxNzMzMyA0MzQuNTE3MzMzIDAgMCAxIDE5LjIgNTAuNzMwNjY3bDYuNCAyNS4zODY2NjYgMTkuMi0yNS4zODY2NjZhMzAyLjAzNzMzMyAzMDIuMDM3MzMzIDAgMCAxIDY0LTYzLjQwMjY2NyAxOTkuMTY4IDE5OS4xNjggMCAwIDEgODMuMi0zOC4wNTg2NjcgOTEuNTIgOTEuNTIgMCAwIDEgMzguNCAxMi44IDQyLjExMiA0Mi4xMTIgMCAwIDEgMCA2My40MDI2NjdsLTYuNCA2LjM1NzMzM2MtNi40IDYuMzU3MzMzLTE5LjIgMTIuOC01Ny42IDEyLjhhNzMuMDQ1MzMzIDczLjA0NTMzMyAwIDAgMC01MS4yIDE5LjAyOTMzNEE0MzcuNzE3MzMzIDQzNy43MTczMzMgMCAwIDAgODE5LjIxODM0NyA0OTkuMnYxMi44bDI1LjYgMTMzLjIwNTMzM2MxMi44IDUwLjczMDY2NyA0NC44IDEwMS40NjEzMzMgNjQgMTAxLjQ2MTMzNHMxOS4yLTEyLjggMjUuNi0xOS4wMjkzMzR2LTYuMzU3MzMzYTY3LjQ5ODY2NyA2Ny40OTg2NjcgMCAwIDEgNDQuOC0zOC4wNTg2NjdjMTIuOCAwIDE5LjIgNi4zNTczMzMgMzIgMTIuOGE0OC4yMTMzMzMgNDguMjEzMzMzIDAgMCAxIDEyLjggMzEuNzAxMzM0IDY2LjQ3NDY2NyA2Ni40NzQ2NjcgMCAwIDEtMzIgNTcuMDg4IDk2Ljc2OCA5Ni43NjggMCAwIDEtNzAuNCAyNS4zODY2NjYgMTU3Ljg2NjY2NyAxNTcuODY2NjY3IDAgMCAxLTEwMi40LTM4LjA1ODY2NiAzNTYuNzM2IDM1Ni43MzYgMCAwIDEtNzAuNC0xMjYuODQ4bC02LjQtMjUuMzg2NjY3LTE5LjIgMTkuMDI5MzMzQTY0MS41Nzg2NjcgNjQxLjU3ODY2NyAwIDAgMSA2NDAuMDE4MzQ3IDc0MC4zMDkzMzNhOTYuNzY4IDk2Ljc2OCAwIDAgMS03MC40IDI1LjM4NjY2NyA1OC4zNjggNTguMzY4IDAgMCAxLTQ0LjgtMTkuMDI5MzMzIDU0LjYxMzMzMyA1NC42MTMzMzMgMCAwIDEtMTkuMi0zOC4wNTg2NjcgNDYuMzM2IDQ2LjMzNiAwIDAgMSAxMi44LTM4LjA1ODY2N2M2LjQtMTIuOCAxOS4yLTEyLjggMzItMTIuOGE0NS43Mzg2NjcgNDUuNzM4NjY3IDAgMCAxIDM4LjQgMTkuMDI5MzM0bDYuNCAxMi44IDEyLjgtNi4zNTczMzQgMTkuMi0xOS4wMjkzMzNhMzE4LjU0OTMzMyAzMTguNTQ5MzMzIDAgMCAwIDQ0LjgtNTAuNzMwNjY3bDU3LjYtODIuNDMyLTI1LjYtMTIwLjQ5MDY2Ni0yNTYtNi4zNTczMzQtNzAuNCAyODUuMzk3MzM0YTUzMC41MTczMzMgNTMwLjUxNzMzMyAwIDAgMS0xMDguOCAyMTUuNjM3MzMzQTMxNy43Mzg2NjcgMzE3LjczODY2NyAwIDAgMSA3Ni44MTgzNDcgOTgxLjMzMzMzM3oiICAvPjwvc3ZnPgo=);\n    background-repeat: no-repeat;\n    margin-right: 10px;\n    position: relative;\n    top: 3px;\n}\n.component_dashboard [data-bind=\"attribute-mapping\"] > .formbuilder > fieldset {\n    padding: 15px;\n}\n.component_dashboard [data-bind=\"attribute-mapping\"] > .formbuilder > fieldset > legend:after {\n    content: \" \";\n    display: block;\n    border-bottom: 2px solid rgba(0, 0, 0, 0.1);\n    margin: 10px 0 15px 0;\n}\n/*\n.component_dashboard [data-bind=\"authentication_middleware\"] form fieldset legend {\n    font-weight: bold;\n}\n.component_dashboard [data-bind=\"authentication_middleware\"] form fieldset fieldset legend {\n    background: var(--border);\n    color: var(--light);\n    font-weight: normal;\n    border-radius: 5px;\n    border: 2px solid var(--border);\n    font-size: 1.05rem;\n}\n*/\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_storage.js",
    "content": "import { createElement, createRender } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport { CSS } from \"../../helpers/loader.js\";\n\nimport transition from \"./animate.js\";\nimport AdminHOC from \"./decorator.js\";\nimport { initConfig } from \"./model_config.js\";\nimport { initStorage, initMiddleware } from \"./ctrl_storage_state.js\";\nimport componentBanner from \"./ctrl_storage_component_banner.js\";\nimport componentBackend from \"./ctrl_storage_component_backend.js\";\nimport componentAuthenticationMiddleware from \"./ctrl_storage_component_authentication.js\";\n\nexport default AdminHOC(async function(render) {\n    const $page = createElement(`\n        <div class=\"component_dashboard sticky\">\n            <style>${await CSS(import.meta.url, \"ctrl_storage.css\")}</style>\n            <div data-bind=\"banner\"></div>\n            <div data-bind=\"backend\"></div>\n            <div data-bind=\"authentication_middleware\"></div>\n        </div>\n    `);\n    await initConfig();\n    await initStorage();\n    await initMiddleware();\n\n    render(transition($page));\n\n    componentBanner(createRender(qs($page, \"[data-bind=\\\"banner\\\"]\")));\n    componentBackend(createRender(qs($page, \"[data-bind=\\\"backend\\\"]\")));\n    componentAuthenticationMiddleware(createRender(qs($page, \"[data-bind=\\\"authentication_middleware\\\"]\")));\n\n    // feature: request to reload page\n    effect(rxjs.fromEvent(new BroadcastChannel(\"admin\"), \"message\").pipe(\n        rxjs.filter(({ data }) => data === \"reload\"),\n        rxjs.mergeMap(() => rxjs.fromEvent(document, \"visibilitychange\")),\n        rxjs.filter(() => document.visibilityState === \"visible\"),\n        rxjs.tap(() => location.reload()),\n    ));\n});\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_storage_component_authentication.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, applyMutation, applyMutations, onClick } from \"../../lib/rx.js\";\nimport { createForm, mutateForm } from \"../../lib/form.js\";\nimport { qs, qsa } from \"../../lib/dom.js\";\nimport { ApplicationError } from \"../../lib/error.js\";\nimport { formTmpl } from \"../../components/form.js\";\nimport { generateSkeleton } from \"../../components/skeleton.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport {\n    getState,\n    getMiddlewareAvailable, getMiddlewareEnabled, toggleMiddleware,\n    getBackendAvailable, getBackendEnabled,\n} from \"./ctrl_storage_state.js\";\nimport { renderLeaf } from \"./helper_form.js\";\nimport { get as getAdminConfig, save as saveConfig } from \"./model_config.js\";\n\nimport \"./component_box-item.js\";\n\nexport default async function(render) {\n    const $page = createElement(`\n        <div>\n            <h2 class=\"hidden\">Authentication Middleware</h2>\n            <div class=\"box-container\">\n                ${generateSkeleton(5)}\n            </div>\n            <div style=\"min-height: 300px\">\n                <form data-bind=\"idp\"></form>\n                <form data-bind=\"attribute-mapping\"></div>\n            </div>\n        </div>\n    `);\n    render($page);\n\n    // feature: setup the buttons\n    const init$ = getMiddlewareAvailable().pipe(\n        rxjs.first(),\n        rxjs.map((specs) => Object.keys(specs).map((label) => createElement(`\n            <box-item data-label=\"${label}\"></box-item>\n        `))),\n        rxjs.tap(() => {\n            qs($page, \"h2\").classList.remove(\"hidden\");\n            qs($page, \".box-container\").innerHTML = \"\";\n        }),\n        applyMutations(qs($page, \".box-container\"), \"appendChild\"),\n        rxjs.share(),\n    );\n    effect(init$);\n\n    // feature: state of buttons\n    effect(init$.pipe(\n        rxjs.concatMap(() => getMiddlewareEnabled()),\n        rxjs.filter((backend) => !!backend),\n        rxjs.tap((backend) => qsa($page, \"box-item\").forEach(($button) => {\n            $button.getAttribute(\"data-label\") === backend\n                ? $button.classList.add(\"active\")\n                : $button.classList.remove(\"active\");\n        })),\n    ));\n\n    // feature: click to select a middleware\n    effect(init$.pipe(\n        rxjs.mergeMap(($nodes) => $nodes),\n        rxjs.mergeMap(($node) => onClick($node)),\n        rxjs.mergeMap(($node) => toggleMiddleware($node.getAttribute(\"data-label\"))),\n        saveMiddleware(),\n    ));\n\n    // feature: setup forms - we insert everything in the DOM so we don't lose\n    // transient state when clicking around\n    const setupIDPForm$ = getMiddlewareAvailable().pipe(\n        rxjs.mergeMap((availableSpecs) => getAdminConfig().pipe(\n            rxjs.first(),\n            rxjs.map((cfg) => ({\n                type: cfg?.middleware?.identity_provider?.type?.value,\n                params: JSON.parse(cfg?.middleware?.identity_provider?.params?.value || \"{}\"),\n            })),\n            rxjs.catchError(() => rxjs.of({})),\n            rxjs.map((idpState) => [availableSpecs, idpState]),\n        )),\n        rxjs.concatMap(async([\n            availableSpecs,\n            idpState = { type: null, params: null },\n        ]) => {\n            const { type, params } = idpState;\n            const idps = [];\n            for (const key in availableSpecs) {\n                let idpSpec = availableSpecs[key];\n                delete idpSpec.type;\n                if (key === type) idpSpec = mutateForm(idpSpec, params || {});\n                const $idp = await createForm({ [key]: idpSpec }, formTmpl({\n                    renderLeaf,\n                    autocomplete: false,\n                }));\n                $idp.classList.add(\"hidden\");\n                $idp.setAttribute(\"id\", key);\n                if (Object.keys(idpSpec).length === 0) $idp.style.display = \"none\";\n                idps.push($idp);\n            }\n            return idps;\n        }),\n        applyMutations(qs($page, `[data-bind=\"idp\"]`), \"appendChild\"),\n        rxjs.share(),\n    );\n    effect(setupIDPForm$);\n\n    // feature: handle visibility of the identity_provider form to match the selected midleware\n    effect(setupIDPForm$.pipe(\n        rxjs.concatMap(() => getMiddlewareEnabled()),\n        rxjs.tap((currentMiddleware) => {\n            qsa($page, `[data-bind=\"idp\"] .formbuilder`).forEach(($node) => {\n                $node.getAttribute(\"id\") === currentMiddleware\n                    ? $node.classList.remove(\"hidden\")\n                    : $node.classList.add(\"hidden\");\n            });\n            const $attrMap = qs($page, `[data-bind=\"attribute-mapping\"]`);\n            currentMiddleware\n                ? $attrMap.classList.remove(\"hidden\")\n                : $attrMap.classList.add(\"hidden\");\n\n            qsa($page, \".box-item\").forEach(($button) => {\n                const $icon = qs($button, \".icon\");\n                $icon.style.transition = \"transform 0.2s ease\";\n                if (qs($button, \"strong\").textContent === currentMiddleware) {\n                    $button.classList.add(\"active\");\n                    $icon.style.transform = \"rotate(45deg)\";\n                } else {\n                    $button.classList.remove(\"active\");\n                    $icon.style.transform = \"\";\n                }\n            });\n        }),\n    ));\n\n    // feature: setup the attribute mapping form\n    const setupAMForm$ = init$.pipe(\n        rxjs.mapTo({\n            attribute_mapping: {\n                related_backend: {\n                    type: \"text\",\n                    datalist: [],\n                    multi: true,\n                    autocomplete: false,\n                    value: \"\",\n                },\n                // dynamic form here is generated reactively from the value of the \"related_backend\" field\n            }\n        }),\n        // related_backend value\n        rxjs.mergeMap((spec) => getAdminConfig().pipe(\n            rxjs.first(),\n            rxjs.map((cfg) => {\n                spec.attribute_mapping.related_backend.value = cfg?.middleware?.attribute_mapping?.related_backend?.value;\n                return spec;\n            }),\n        )),\n        rxjs.concatMap(async(specs) => await createForm(specs, formTmpl({}))),\n        applyMutation(qs($page, `[data-bind=\"attribute-mapping\"]`), \"replaceChildren\"),\n        rxjs.share(),\n    );\n    effect(setupAMForm$);\n\n    // feature: setup autocompletion of related backend field\n    effect(setupAMForm$.pipe(\n        rxjs.switchMap(() => rxjs.merge(\n            getBackendEnabled(),\n            rxjs.fromEvent(qs(document.body, `[data-bind=\"backend-enabled\"]`), \"input\").pipe(\n                rxjs.debounceTime(500),\n                rxjs.mergeMap(() => getState().pipe(rxjs.map(({ connections }) => connections))),\n            ),\n        )),\n        rxjs.map((connections) => connections.map(({ label }) => label)),\n        rxjs.tap((datalist) => {\n            const $input = $page.querySelector(`[name=\"attribute_mapping.related_backend\"]`);\n            if (!$input) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: missing related backend\");\n            $input.setAttribute(\"datalist\", datalist.join(\",\"));\n            // @ts-ignore\n            $input.refresh();\n        }),\n    ));\n\n    // feature: related backend values triggers creation/deletion of related backends\n    effect(setupAMForm$.pipe(\n        rxjs.switchMap(() => rxjs.merge(\n            // case 1: user is typing in the related backend field\n            rxjs.fromEvent(qs($page, `[name=\"attribute_mapping.related_backend\"]`), \"input\").pipe(\n                rxjs.map((e) => e.target.value),\n            ),\n            // case 2: user is adding / removing a storage backend\n            getBackendEnabled().pipe(\n                rxjs.map(() => qs($page, `[name=\\\"attribute_mapping.related_backend\"]`).value)\n            ),\n            // case 3: user is changing the storage backend label\n            rxjs.fromEvent(qs(document.body, `[data-bind=\"backend-enabled\"]`), \"input\").pipe(\n                rxjs.map(() => qs($page, `[name=\"attribute_mapping.related_backend\"]`).value),\n            ),\n        )),\n        rxjs.map((value) => value.split(\",\").map((val) => (val || \"\").trim()).filter((t) => !!t)),\n        rxjs.mergeMap((inputBackends) => getState().pipe(\n            rxjs.map(({ connections }) => connections),\n            rxjs.first(),\n            rxjs.map((enabledBackends) => inputBackends\n                .map((label) => enabledBackends.find((b) => b.label === label))\n                .filter((label) => !!label)),\n        )),\n        rxjs.mergeMap((backends) => getBackendAvailable().pipe(rxjs.first(), rxjs.map((specs) => {\n            // we don't want to show the \"normal\" form but a flat version of it\n            // so we're getting rid of anything that could make some magic happen like toggle and\n            // ids which enable those interactions\n            for (const key in specs) {\n                for (const input in specs[key]) {\n                    if (specs[key][input][\"type\"] === \"enable\") {\n                        delete specs[key][input];\n                    } else if (\"id\" in specs[key][input]) {\n                        delete specs[key][input][\"id\"];\n                    }\n                }\n            }\n            return [backends, specs];\n        }))),\n        rxjs.map(([backends, formSpec]) => {\n            const spec = {};\n            backends.forEach(({ label, type }) => {\n                if (formSpec[type]) spec[label] = JSON.parse(JSON.stringify(formSpec[type]));\n            });\n            return spec;\n        }),\n        rxjs.mergeMap((spec) => getAdminConfig().pipe(\n            rxjs.first(),\n            rxjs.map((cfg) => JSON.parse(cfg?.middleware?.attribute_mapping?.params?.value || \"{}\")),\n            rxjs.catchError(() => rxjs.of({})),\n            rxjs.map((cfg) => {\n                // transform the form state from legacy format (= an object struct which was replicating the spec object)\n                // to the new format which leverage the dom (= or the input name attribute to be precise) to store the entire schema\n                const state = {};\n                for (const key1 in cfg) {\n                    for (const key2 in cfg[key1]) {\n                        state[`${key1}.${key2}`] = cfg[key1][key2];\n                    }\n                }\n                return [spec, state];\n            }),\n        )),\n        rxjs.map(([formSpec, formState]) => mutateForm(formSpec, formState)),\n        rxjs.mergeMap(async(formSpec) => await createForm(formSpec, formTmpl({\n            renderLeaf: () => createElement(\"<label></label>\"),\n        }))),\n        rxjs.tap(($node) => {\n            /** @type { Element | undefined} */\n            let $relatedBackendField;\n            qsa($page, `[data-bind=\"attribute-mapping\"] fieldset`).forEach(($el, i) => {\n                if (i === 0) $relatedBackendField = $el;\n                else $el.remove();\n            });\n            $relatedBackendField?.appendChild($node);\n        }),\n    ));\n\n    // feature: form input change handler\n    effect(setupAMForm$.pipe(\n        rxjs.mapTo(new Date()),\n        rxjs.switchMap((d) => rxjs.fromEvent($page, \"input\").pipe(\n            rxjs.filter(() => new Date() - d > 200), // to prevent password manager from auto triggerring events ...\n        )),\n        rxjs.mergeMap(() => getMiddlewareEnabled().pipe(rxjs.first())),\n        saveMiddleware(),\n    ));\n}\n\nconst saveMiddleware = () => rxjs.pipe(\n    rxjs.mergeMap(() => getState()),\n    saveConfig(),\n    rxjs.catchError(ctrlError()),\n);\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_storage_component_backend.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, applyMutations, applyMutation, onClick } from \"../../lib/rx.js\";\nimport { createForm } from \"../../lib/form.js\";\nimport { qs, qsa } from \"../../lib/dom.js\";\nimport { formTmpl } from \"../../components/form.js\";\nimport { generateSkeleton } from \"../../components/skeleton.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport { getState, getBackendAvailable, getBackendEnabled, addBackendEnabled, removeBackendEnabled } from \"./ctrl_storage_state.js\";\nimport { save as saveConfig } from \"./model_config.js\";\n\nimport \"./component_box-item.js\";\n\nexport default async function(render) {\n    const $page = createElement(`\n        <div class=\"component_storagebackend\">\n            <h2>Storage Backend</h2>\n            <div class=\"box-container\" data-bind=\"backend-available\">\n                ${generateSkeleton(10)}\n            </div>\n            <form data-bind=\"backend-enabled\"></form>\n        </div>\n    `);\n    render($page);\n\n    const $available = qs($page, `[data-bind=\"backend-available\"]`);\n    const $enabled = qs($page, `[data-bind=\"backend-enabled\"]`);\n\n    // feature: setup the buttons\n    const init$ = getBackendAvailable().pipe(\n        rxjs.tap(() => $available.innerHTML = \"\"),\n        rxjs.mergeMap((specs) => Promise.all(Object.keys(specs).map((label) => createElement(`\n            <box-item data-label=\"${label}\"></box-item>\n        `)))),\n        applyMutations($available, \"appendChild\"),\n        rxjs.share(),\n    );\n    effect(init$);\n\n    // feature: state of buttons\n    effect(init$.pipe(\n        rxjs.mergeMap(() => getBackendEnabled()),\n        rxjs.map((enabled) => {\n            const enabledSet = new Set();\n            enabled.forEach(({ type }) => {\n                enabledSet.add(type);\n            });\n            return enabledSet;\n        }),\n        rxjs.tap((backends) => qsa($page, \"box-item\").forEach(($button) => {\n            backends.has($button.getAttribute(\"data-label\"))\n                ? $button.classList.add(\"active\")\n                : $button.classList.remove(\"active\");\n        })),\n    ));\n\n    // feature: click to select a backend\n    effect(init$.pipe(\n        rxjs.mergeMap(($nodes) => $nodes),\n        rxjs.mergeMap(($node) => onClick($node)),\n        rxjs.mergeMap(($node) => addBackendEnabled($node.getAttribute(\"data-label\"))),\n        saveConnections(),\n    ));\n\n    // feature: setup form\n    const setupForm$ = getBackendEnabled().pipe(\n        // initialise the forms\n        rxjs.mergeMap((enabled) => Promise.all(enabled.map(({ type, label }) => createForm({\n            [type]: {\n                \"\": { type: \"text\", placeholder: \"Label\", value: label },\n            }\n        }, formTmpl({\n            renderLeaf: () => createElement(\"<label></label>\"),\n            renderNode: ({ label, format }) => {\n                const $fieldset = createElement(`\n                    <fieldset>\n                        <legend class=\"no-select\">\n                            ${format(label)}\n                        </legend>\n                        <div data-bind=\"children\"></div>\n                    </fieldset>\n                `);\n                const $remove = createElement(`\n                    <div class=\"icons no-select\">\n                        <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MS45NzYgNTEuOTc2Ij4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjUzMzMzMjg1O3N0cm9rZS13aWR0aDoxLjQ1NjgxMTE5IiBkPSJtIDQxLjAwNTMxLDQwLjg0NDA2MiBjIC0xLjEzNzc2OCwxLjEzNzc2NSAtMi45ODIwODgsMS4xMzc3NjUgLTQuMTE5ODYxLDAgTCAyNi4wNjg2MjgsMzAuMDI3MjM0IDE0LjczNzU1MSw0MS4zNTgzMSBjIC0xLjEzNzc3MSwxLjEzNzc3MSAtMi45ODIwOTMsMS4xMzc3NzEgLTQuMTE5ODYxLDAgLTEuMTM3NzcyMiwtMS4xMzc3NjggLTEuMTM3NzcyMiwtMi45ODIwODggMCwtNC4xMTk4NjEgTCAyMS45NDg3NjYsMjUuOTA3MzcyIDExLjEzMTkzOCwxNS4wOTA1NTEgYyAtMS4xMzc3NjQ3LC0xLjEzNzc3MSAtMS4xMzc3NjQ3LC0yLjk4MzU1MyAwLC00LjExOTg2MSAxLjEzNzc3NCwtMS4xMzc3NzIxIDIuOTgyMDk4LC0xLjEzNzc3MjEgNC4xMTk4NjUsMCBMIDI2LjA2ODYyOCwyMS43ODc1MTIgMzYuMzY5NzM5LDExLjQ4NjM5OSBjIDEuMTM3NzY4LC0xLjEzNzc2OCAyLjk4MjA5MywtMS4xMzc3NjggNC4xMTk4NjIsMCAxLjEzNzc2NywxLjEzNzc2OSAxLjEzNzc2NywyLjk4MjA5NCAwLDQuMTE5ODYyIEwgMzAuMTg4NDg5LDI1LjkwNzM3MiA0MS4wMDUzMSwzNi43MjQxOTcgYyAxLjEzNzc3MSwxLjEzNzc2NyAxLjEzNzc3MSwyLjk4MjA5MSAwLDQuMTE5ODY1IHoiIC8+Cjwvc3ZnPgo=\" alt=\"close\">\n                    </div>\n                `);\n                $fieldset.appendChild($remove);\n                return $fieldset;\n            },\n        }))))),\n        rxjs.tap(() => $enabled.innerHTML = \"\"),\n        rxjs.mergeMap((nodeList) => {\n            if (nodeList.length === 0) return rxjs.of(createElement(`\n                <div class=\"alert\">\n                    You need to select at least 1 storage backend\n                </div>\n            `)).pipe(\n                applyMutation($enabled, \"appendChild\"),\n                rxjs.mergeMap(() => rxjs.EMPTY),\n            );\n            return rxjs.of(nodeList).pipe(\n                applyMutations($enabled, \"appendChild\"),\n            );\n        }),\n        rxjs.share(),\n    );\n    effect(setupForm$);\n\n    // feature: remove an existing backend\n    effect(setupForm$.pipe(\n        rxjs.mergeMap(($nodes) => $nodes),\n        rxjs.mergeMap(($node) => onClick($node.querySelector(\".icons\"))),\n        rxjs.map(($node) => qs($node.parentElement, \"input\").value),\n        rxjs.mergeMap((label) => removeBackendEnabled(label)),\n        saveConnections(),\n    ));\n\n    // feature: form input change handler\n    effect(setupForm$.pipe(\n        rxjs.mergeMap((forms) => forms),\n        rxjs.mergeMap(($el) => rxjs.fromEvent($el, \"input\")),\n        saveConnections(),\n    ));\n}\n\nconst saveConnections = () => rxjs.pipe(\n    rxjs.mergeMap((connections) => getState().pipe(rxjs.map((config) => {\n        if (Array.isArray(connections)) config.connections = connections;\n        return config;\n    }))),\n    saveConfig(),\n    rxjs.catchError(ctrlError()),\n);\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_storage_component_banner.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs from \"../../lib/rx.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport { settingsGet, settingsSave } from \"../../lib/store.js\";\nimport { getBackendEnabled } from \"./ctrl_storage_state.js\";\n\nexport default async function(render) {\n    const { storage_banner } = settingsGet({ storage_banner: true }, \"admin\");\n    if (!storage_banner) return;\n    const connections = await getBackendEnabled().pipe(rxjs.first()).toPromise();\n    if (connections.length > 0) return render(withClose(createElement(`\n        <div id=\"storage-banner\">\n            <span class=\"no-select pointer\" id=\"close\">X</span>\n            Want to apply configuration preset? Check out the <a class=\"wavy\" href=\"admin/setup?step=2\">configuration wizard</a>\n            <style>${CSS}</style>\n        </div>\n    `)));\n    render(withClose(createElement(`\n        <div id=\"storage-banner\">\n            <span class=\"no-select pointer\" id=\"close\">X</span>\n            First time here? You have 2 options for the initial setup:<br>\n            1. Pick a storage below and connect directly with your storage credentials<br>\n            2. OR Connect your storage with a separate authentication method. Use the <a class=\"wavy\" href=\"admin/setup?step=2\">configuration wizard</a> presets to get started quickly\n            <style>${CSS}</style>\n        </div>\n    `)));\n}\n\nfunction withClose($el) {\n    qs($el, \"#close\").onclick = () => {\n        settingsSave({ storage_banner: false }, \"admin\");\n        $el.remove();\n    };\n    return $el;\n}\n\nconst CSS = `\n#storage-banner {\n    background: var(--surface);\n    margin: 20px 0 0 0;\n    padding: 15px 20px;\n    border-radius: 3px;\n    color: rgba(255, 255, 255, 0.8);\n    text-align: justify;\n}\n#storage-banner a {\n    color: inherit;\n}\n#storage-banner #close {\n    font-family: monospace;\n    cursor: pointer;\n    text-shadow: 0 0 black;\n    float: right;\n    padding: 5px;\n    margin-right: -5px;\n}`;\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_storage_state.js",
    "content": "import rxjs from \"../../lib/rx.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport { ApplicationError } from \"../../lib/error.js\";\nimport { query as getConfig } from \"../../model/config.js\";\nimport { get as getAdminConfig } from \"./model_config.js\";\nimport { formObjToJSON$ } from \"./helper_form.js\";\n\nconst backendsEnabled$ = new rxjs.BehaviorSubject();\n\nexport async function initStorage() {\n    return await getConfig().pipe(\n        rxjs.map(({ connections }) => connections),\n        rxjs.tap((connections) => {\n            if (backendsEnabled$.value !== undefined) return;\n            backendsEnabled$.next(Array.isArray(connections) ? connections : []);\n        }),\n    ).toPromise();\n}\n\nexport { getBackends as getBackendAvailable } from \"./model_backend.js\";\n\nexport function getBackendEnabled() {\n    return backendsEnabled$.asObservable();\n}\n\nexport function addBackendEnabled(type) {\n    return getState().pipe(rxjs.map(({ connections }) => {\n        let label = type;\n        let i = 0;\n        while (true) {\n            if (!connections.find((obj) => obj.label === label)) {\n                break;\n            }\n            i += 1;\n            label = `${type} ${i}`;\n        }\n        const newConnections = connections.concat({ type, label });\n        backendsEnabled$.next(newConnections);\n        return newConnections;\n    }));\n}\n\nexport function removeBackendEnabled(labelToRemove) {\n    return getState().pipe(rxjs.map(({ connections }) => {\n        const newConnections = connections.filter(({ label }) => label !== labelToRemove);\n        backendsEnabled$.next(newConnections);\n        return newConnections;\n    }));\n}\n\nconst middlewareEnabled$ = new rxjs.ReplaySubject(1);\n\nexport async function initMiddleware() {\n    return await getAdminConfig().pipe(\n        rxjs.map(({ middleware }) => middleware),\n        formObjToJSON$(),\n        rxjs.tap(({ identity_provider }) => middlewareEnabled$.next(identity_provider.type)),\n        rxjs.first(),\n    ).toPromise();\n}\n\nexport { getAuthMiddleware as getMiddlewareAvailable } from \"./model_auth_middleware.js\";\n\nexport function getMiddlewareEnabled() {\n    return middlewareEnabled$.asObservable();\n}\n\nexport function toggleMiddleware(type) {\n    return middlewareEnabled$.pipe(\n        rxjs.first(),\n        rxjs.map((oldValue) => {\n            const newValue = oldValue === type ? null : type;\n            middlewareEnabled$.next(newValue);\n            return newValue;\n        }),\n    );\n}\n\nexport function getState() {\n    return getAdminConfig().pipe(\n        rxjs.first(),\n        formObjToJSON$(),\n        rxjs.map((config) => { // connections\n            const connections = [];\n            const formData = new FormData(qs(document.body, `[data-bind=\"backend-enabled\"]`));\n            for (const [type, label] of formData.entries()) {\n                connections.push({ type, label });\n            }\n            config.connections = connections;\n            return config;\n        }),\n        rxjs.map((config) => { // middleware\n            const authType = document\n                .querySelector(`[data-bind=\"authentication_middleware\"] box-item.active`)\n                ?.getAttribute(\"data-label\");\n\n            config.middleware = {\n                identity_provider: { type: null },\n                attribute_mapping: null,\n            };\n            if (!authType) return config;\n\n            const $formIDP = document.querySelector(`[data-bind=\"idp\"]`);\n            if (!($formIDP instanceof HTMLFormElement)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: idp isn't a form\");\n            let formValues = [...new FormData($formIDP)];\n            config.middleware.identity_provider = {\n                type: authType,\n                params: JSON.stringify(\n                    formValues\n                        .filter(([key]) => key.startsWith(`${authType}.`)) // remove elements that aren't in scope\n                        .map(([key, value]) => [key.replace(new RegExp(`^${authType}\\.`), \"\"), value]) // format the relevant keys\n                        .reduce((acc, [key, value]) => { // transform onto something ready to be saved\n                            if (key === \"type\") return acc;\n                            else if (key === \"banner\") return acc;\n                            else if (typeof key !== \"string\") return acc;\n                            return {\n                                ...acc,\n                                [key]: value,\n                            };\n                        }, {}),\n                ),\n            };\n\n            const $formAM = document.querySelector(`[data-bind=\"attribute-mapping\"]`);\n            if (!($formAM instanceof HTMLFormElement)) throw new ApplicationError(\"INTERNAL_ERROR\", \"assumption failed: attribute mapping isn't a form\");\n            formValues = [...new FormData($formAM)];\n            config.middleware.attribute_mapping = {\n                related_backend: (formValues.shift() || [])[1],\n                params: JSON.stringify(formValues.reduce((acc, [key, value]) => {\n                    const k = key.split(\".\");\n                    if (k.length !== 2) return acc;\n                    if (!acc[`${k[0]}`]) acc[`${k[0]}`] = {};\n                    if (value !== \"\") acc[`${k[0]}`][`${k[1]}`] = value;\n                    return acc;\n                }, {})),\n            };\n\n            return config;\n        }),\n    );\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_workflow.css",
    "content": ".component_page_workflow .pull-right {\n    float: right;\n}\n.component_page_workflow button {\n    font-size: 0.9rem;\n    padding: 3px 5px;\n    margin: 0 2px;\n}\n.component_page_workflow h2 input {\n    padding: 0 0 0 20px;\n    border: none;\n    font-family: inherit;\n    color: inherit;\n    font-size: inherit;\n    width: 100%;\n    position: relative;\n    bottom: 2px;\n}\n.component_page_workflow .box {\n    display: block;\n    background: white;\n    border: 2px solid #ebebec;\n    border-style: solid;\n    border-radius: 5px;\n    margin: 15px 0 0 0;\n    padding: 15px;\n    overflow: visible;\n}\n.component_page_workflow .box.disabled {\n    background: var(--border);\n}\n.component_page_workflow .box h3 {\n    font-weight: 600;\n    margin: 0;\n}\n.component_page_workflow .box h3 span {\n    font-weight: 300;\n    font-size: 0.8rem;\n    font-style: italic;\n    color: var(--light);\n}\n.component_page_workflow .box svg {\n    float: left;\n    width: 25px;\n    fill: var(--light);\n    opacity: 0.8;\n}\n.component_page_workflow .box .workflow-summary button {\n    color: var(--light);\n}\n.component_page_workflow hr {\n    border: none;\n    height: 30px;\n    margin: 0 0 -15px 0;\n    position: relative;\n}\n.component_page_workflow hr:after {\n    content: \" \";\n    position: absolute;\n    left: 26px;\n    top: 0;\n    bottom: 0;\n    border-right: 4px dashed rgba(0, 0, 0, 0.2);\n    border-right-style: dotted;\n}\n.component_page_workflow .box [data-bind=\"form\"] {\n    overflow: hidden;\n}\n.component_page_workflow .box [data-bind=\"form\"] .formbuilder {\n    padding: 10px 10px 0px 10px;\n    border-radius: 5px;\n    margin-top: 10px;\n    border-top: 2px solid #ebebec;\n    background: transparent;\n}\n.component_page_workflow .box.disabled [data-bind=\"form\"] .formbuilder {\n    border-color: var(--border);\n}\n.component_page_workflow .box [data-bind=\"form\"] .formbuilder:empty {\n    display: none;\n}\n\n.component_page_workflow .box h3 button.pull-right {\n    padding: 0;\n    margin: 0;\n}\n\n/* details page buttons */\n.component_page_workflow .box button[alt=\"delete\"] {\n    position: absolute;\n    top: -13px;\n    right: -13px;\n}\n.component_page_workflow .box button[alt=\"delete\"] svg {\n    width: 10px;\n    height: 10px;\n    border: 2px solid var(--border);\n    background: #ebebec;\n    fill: var(--light);\n    padding: 5px;\n    border-radius: 15px;\n}\n\n/* list page */\n.component_page_workflow h3.empty {\n    padding: 30px;\n    background: white;\n    border: 2px dashed var(--color);\n    text-shadow: 0px 0px 1px rgba(0, 0, 0, 0.5);\n    border-radius: 5px;\n    font-weight: 600;\n    color: var(--color);\n    text-transform: uppercase;\n    font-size: 1.05rem;\n    letter-spacing: -0.5px;\n    opacity: 0.4;\n    cursor: pointer;\n}\n\n/* Workflow creation modal */\n.component_workflow_create {\n    font-size: 1rem;\n}\n.component_workflow_create h2 {\n    margin: 0 0 5px 0;\n    font-size: 1.1rem;\n    color: var(--light);\n}\n.component_workflow_create form {\n    margin-bottom: 15px;\n}\n.component_workflow_create form input {\n    font-size: 1rem;\n    border: 2px solid var(--border);\n    border-radius: 5px;\n    padding: 5px 10px;\n    color: rgba(0,0,0,0.75);\n    width: 100%;\n    display: block;\n    box-sizing: border-box;\n    margin-bottom: 5px;\n}\n.component_workflow_create form input::placeholder {\n    color: rgba(0,0,0,0.4);\n}\n.component_workflow_create [data-bind=\"list\"] {\n    overflow: hidden;\n}\n.component_workflow_create [data-bind=\"list\"] .item {\n    color: var(--color);\n    background: #f2f2f2;\n    transition: all 0.1s ease;\n    padding: 5px 5px 5px 12px;\n    border-radius: 5px;\n    cursor: pointer;\n    letter-spacing: -0.5px;\n    margin-top: 2px;\n    align-items: center;\n}\n.component_workflow_create [data-bind=\"list\"] .item svg {\n    height: 20px;\n    margin-right: 5px;\n    fill: var(--light);\n}\n\n/* save button */\n.workflow-fab {\n    position: fixed;\n    bottom: 20px;\n    right: 20px;\n    background: var(--emphasis);\n    border-radius: 10px;\n    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);\n    cursor: pointer;\n}\n.workflow-fab select {\n    background: var(--emphasis);\n    color: var(--bg-color);\n    font-family: monospace;\n    border: none;\n    padding-left: 10px;\n    padding-right: 5px;\n    text-align: right;\n    border-top-left-radius: 2px;\n    border-bottom-left-radius: 10px;\n    font-weight: 600;\n}\n.workflow-fab button > * {\n    height: 30px;\n    fill: var(--bg-color);\n    padding: 7px;\n}\n.workflow-fab button > component-icon img {\n    filter: brightness(0) invert(1);\n}\n\n/* ADD BUTTON */\n.component_page_workflow [data-bind=\"add\"] {\n    display: inline-block;\n}\n.component_page_workflow [data-bind=\"add\"] .box {\n    align-items: center;\n    background: var(--emphasis);\n    display: flex;\n    padding: 4px 0px 2px 0px;\n    position: relative;\n    left: 5px;\n    overflow-x: scroll;\n    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);\n    border-width: 0;\n}\n.component_page_workflow [data-bind=\"add\"] .box button {\n    color: var(--bg-color);\n    font-family: monospace;\n    text-transform: uppercase;\n    background:var(--border);\n    font-size: 0.85rem;\n    letter-spacing: -0.5px;\n    margin-right: 5px;\n}\n.component_page_workflow [data-bind=\"add\"] .box svg {\n    margin-right: 0;\n    fill: var(--bg-color);\n    transition: transform 0.2s ease;\n}\n.component_page_workflow [data-bind=\"add\"] .box > .item {\n    position: sticky;\n    left: 0;\n    z-index: 1;\n    background: transparent;\n    backdrop-filter: blur(2px);\n    padding: 3px 8px;\n}\n.component_page_workflow [data-bind=\"add\"] .box > .flex {\n    padding: 0 5px 0 0;\n    margin-left: -5px;\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_workflow.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\n\nimport AdminHOC from \"./decorator.js\";\nimport { workflowAll } from \"./model_workflow.js\";\nimport ctrlList from \"./ctrl_workflow_list.js\";\nimport ctrlDetails from \"./ctrl_workflow_details.js\";\n\nexport default AdminHOC(async function(render) {\n    await loadCSS(import.meta.url, \"./ctrl_workflow.css\");\n    render(createElement(\"<component-loader inlined></component-loader>\"));\n\n    const { workflows, triggers, actions } = await workflowAll().toPromise();\n\n    const specs = getSpecs();\n    if (specs) ctrlDetails(render, { workflow: specs, triggers, actions });\n    else ctrlList(render, { workflows, triggers, actions });\n});\n\nfunction getSpecs() {\n    const GET = new URLSearchParams(location.search);\n    try {\n        return JSON.parse(atob(GET.get(\"specs\")));\n    } catch (err) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_workflow_details.js",
    "content": "import { createElement, createFragment, navigate } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, onClick } from \"../../lib/rx.js\";\nimport { animate, slideXIn, slideXOut } from \"../../lib/animate.js\";\nimport { qs, qsa, safe } from \"../../lib/dom.js\";\nimport { createForm, mutateForm } from \"../../lib/form.js\";\nimport { formTmpl } from \"../../components/form.js\";\n\nimport { workflowUpsert, workflowDelete, workflowGet } from \"./model_workflow.js\";\nimport transition from \"./animate.js\";\n\nexport default async function(render, { workflow, triggers, actions }) {\n    const { id } = workflow;\n    const $page = createElement(`\n        <div class=\"component_page_workflow\">\n            <h2 class=\"ellipsis flex\">\n                <a href=\"./admin/workflow\" data-link>\n                    <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\" style=\"width:20px;fill:var(--color);\">\n                        <path d=\"M169.4 297.4C156.9 309.9 156.9 330.2 169.4 342.7L361.4 534.7C373.9 547.2 394.2 547.2 406.7 534.7C419.2 522.2 419.2 501.9 406.7 489.4L237.3 320L406.6 150.6C419.1 138.1 419.1 117.8 406.6 105.3C394.1 92.8 373.8 92.8 361.3 105.3L169.3 297.3z\"/>\n                    </svg>\n                </a>\n                <form class=\"full-width\"><input type=\"text\" value=\"${safe(workflow.name)}\" name=\"name\" /></form>\n            </h2>\n\n            <div data-bind=\"trigger\"></div>\n            <div data-bind=\"actions\"></div>\n            <div data-bind=\"add\"></div>\n\n            <h2 class=\"ellipsis\">Activity</h2>\n            <pre data-bind=\"history\" class=\"scroll-y\" style=\"max-height:300px\">...</pre>\n\n            <style>.component_page_admin .page_container h2:after { display: none; }</style>\n        </div>\n    `);\n    render(transition($page));\n\n    // feature1: setup trigger\n    const $trigger = qs($page, `[data-bind=\"trigger\"]`);\n    $trigger.appendChild(await createTrigger({ trigger: workflow.trigger, triggers }));\n\n    // feature2: setup actions\n    const $actions = qs($page, `[data-bind=\"actions\"]`);\n    for (let i=0; i<(workflow.actions || []).length; i++) {\n        $actions.appendChild(await createAction({ action: workflow.actions[i], actions }));\n    }\n\n    // feature3: add a step\n    const $add = qs($page, `[data-bind=\"add\"]`);\n    $add.appendChild(await createAdd({\n        workflow,\n        actions,\n        createAction: async({ action, actions }) => {\n            const $action = await createAction({ action, actions });\n            qs($action, `button[alt=\"delete\"]`).onclick = (e) => removeAction(e.target);\n            $actions.appendChild($action);\n            withToggle(qs($action, `[data-bind=\"form\"]`));\n        },\n    }));\n\n    // feature4: save button\n    effect(rxjs.of(createSave({ withDelete: !!id })).pipe(\n        rxjs.tap(($fab) => $page.parentElement.appendChild($fab)),\n        rxjs.mergeMap(($fab) => onClick(qs($fab, \"button\"))),\n        rxjs.tap(($button) => {\n            $button.setAttribute(\"disabled\", \"true\");\n            $button.firstElementChild.classList.add(\"hidden\");\n            $button.firstElementChild.nextElementSibling.classList.remove(\"hidden\");\n        }),\n        rxjs.mergeMap(($button) => {\n            let action = rxjs.of(null);\n            let redirect = !id;\n            const workflow = formData($page, { id });\n            if (workflow.published === \"delete\") {\n                if (window.confirm(\"delete this workflow?\")) {\n                    action = workflowDelete(id);\n                    redirect = true;\n                }\n            } else {\n                workflow.published = workflow.published === \"publish\";\n                action = workflowUpsert(workflow);\n            }\n            const start = new Date();\n            return action.pipe(\n                rxjs.switchMap((result) => {\n                    const elapsed = new Date() - start;\n                    return rxjs.of(result).pipe(rxjs.delay(Math.max(0, 400 - elapsed)));\n                }),\n                rxjs.finalize(() => {\n                    $button.removeAttribute(\"disabled\");\n                    $button.firstElementChild.classList.remove(\"hidden\");\n                    $button.firstElementChild.nextElementSibling.classList.add(\"hidden\");\n                    if (redirect) navigate(window.location.pathname);\n                    else history.replaceState(null, \"\", window.location.pathname + \"?specs=\" + encodeURIComponent(btoa(JSON.stringify(workflow))));\n                }),\n            );\n        }),\n    ));\n\n    // feature5: toggle form visibility\n    qsa($page, `[data-bind=\"form\"]`).forEach(($form) => {\n        withToggle($form);\n        if (workflow.id) $form.classList.add(\"hidden\");\n    });\n\n    // feature6: remove button\n    qsa($page, `button[alt=\"delete\"]`).forEach(($delete) => $delete.onclick = (e) => {\n        removeAction(e.target);\n    });\n\n    // feature7: history\n    const $history = qs($page, `[data-bind=\"history\"]`);\n    if (!workflow.id) {\n        $history.previousElementSibling.remove();\n        $history.remove();\n    } else effect(rxjs.of(null).pipe(\n        rxjs.mergeMap(() => workflowGet(workflow.id)),\n        rxjs.tap(({ history }) => {\n            if (history.length === 0) $history.innerText = \"Ø\";\n            else $history.innerText = history.map(({ id, created_at, status, steps }) => {\n                const [date, time] = created_at.split(\" \");\n                let msg = `date=${safe(date)} time=${safe(time)} id=${safe(String(id))} status=${safe(status)}`;\n                try { steps = JSON.parse(steps); }\n                catch (err) { steps = []; }\n                if (steps.length > 0) {\n                    const s = new Array(steps.length);\n                    for (let i = 0; i < steps.length; i++) {\n                        const { name, done = false } = steps[i];\n                        const out = name.split(\"/\")[1] || \"na\";\n                        if (status === \"FAILURE\" && !done) s[i] = out + \"[✗]\";\n                        else if (status === \"RUNNING\" && !done && (steps[i-1] ? steps[i-1].done : true)) s[i] = out + \"[○]\";\n                        else if ([\"RUNNING\", \"PENDING\"].indexOf(status) !== -1 && done) s[i] = out + \"[✓]\";\n                        else if ([\"READY\", \"CLAIMED\"].indexOf(status) !== -1) s[i] = out + \"[○]\";\n                        else s[i] = out;\n                    }\n                    msg += \"[\" + s.join(\" ➜ \") + \"]\";\n                }\n                return msg;\n            }).join(\"\\n\");\n        }),\n        rxjs.catchError(() => rxjs.of(null)),\n        rxjs.delay(5000),\n        rxjs.repeat(),\n    ));\n}\n\nasync function createTrigger({ trigger, triggers }) {\n    const currentTrigger = triggers.find(({ name }) => name === trigger.name);\n    if (!currentTrigger) return createElement(`<div class=\"box disabled\">Trigger not found \"${safe(trigger.name)}\"</div>`);\n    const { title, icon } = currentTrigger;\n    const $trigger = createFragment(`\n        <div class=\"box disabled\">\n            ${icon}\n            <h3 class=\"ellipsis no-select\">\n                &nbsp;${safe(title)}\n                <button alt=\"configure\" class=\"pull-right\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><path d=\"M259.1 73.5C262.1 58.7 275.2 48 290.4 48L350.2 48C365.4 48 378.5 58.7 381.5 73.5L396 143.5C410.1 149.5 423.3 157.2 435.3 166.3L503.1 143.8C517.5 139 533.3 145 540.9 158.2L570.8 210C578.4 223.2 575.7 239.8 564.3 249.9L511 297.3C511.9 304.7 512.3 312.3 512.3 320C512.3 327.7 511.8 335.3 511 342.7L564.4 390.2C575.8 400.3 578.4 417 570.9 430.1L541 481.9C533.4 495 517.6 501.1 503.2 496.3L435.4 473.8C423.3 482.9 410.1 490.5 396.1 496.6L381.7 566.5C378.6 581.4 365.5 592 350.4 592L290.6 592C275.4 592 262.3 581.3 259.3 566.5L244.9 496.6C230.8 490.6 217.7 482.9 205.6 473.8L137.5 496.3C123.1 501.1 107.3 495.1 99.7 481.9L69.8 430.1C62.2 416.9 64.9 400.3 76.3 390.2L129.7 342.7C128.8 335.3 128.4 327.7 128.4 320C128.4 312.3 128.9 304.7 129.7 297.3L76.3 249.8C64.9 239.7 62.3 223 69.8 209.9L99.7 158.1C107.3 144.9 123.1 138.9 137.5 143.7L205.3 166.2C217.4 157.1 230.6 149.5 244.6 143.4L259.1 73.5zM320.3 400C364.5 399.8 400.2 363.9 400 319.7C399.8 275.5 363.9 239.8 319.7 240C275.5 240.2 239.8 276.1 240 320.3C240.2 364.5 276.1 400.2 320.3 400z\"/></svg></button>\n            </h3>\n            <form data-bind=\"form\" data-key=\"trigger\" data-step=\"${safe(trigger.name)}\"></form>\n        </div><hr>\n    `);\n    const $form = await createForm(\n        mutateForm(currentTrigger.specs, trigger.params || {}),\n        formTmpl(),\n    );\n    qs($trigger, `[data-bind=\"form\"]`).appendChild($form);\n    return $trigger;\n}\n\nasync function createAction({ action, actions }) {\n    const selected = actions.find((_action) => _action.name === action.name);\n    if (!selected) return createElement(`\n        <div>\n            <div class=\"box disabled\">Action not found \"${safe(action.name)}\"</div>\n            <hr>\n        </div>\n    `);\n    const subtitle = selected.subtitle && action.params && action.params[selected.subtitle] ? `(${safe(action.params[selected.subtitle])})` : \"\";\n    const $action = createElement(`\n        <div>\n            <div class=\"box\">\n                ${selected.icon}\n                <h3 class=\"ellipsis no-select\">\n                    &nbsp;${safe(selected.title)} <span>${safe(subtitle)}</span>\n                    <button alt=\"delete\" class=\"pull-right\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><path d=\"M183.1 137.4C170.6 124.9 150.3 124.9 137.8 137.4C125.3 149.9 125.3 170.2 137.8 182.7L275.2 320L137.9 457.4C125.4 469.9 125.4 490.2 137.9 502.7C150.4 515.2 170.7 515.2 183.2 502.7L320.5 365.3L457.9 502.6C470.4 515.1 490.7 515.1 503.2 502.6C515.7 490.1 515.7 469.8 503.2 457.3L365.8 320L503.1 182.6C515.6 170.1 515.6 149.8 503.1 137.3C490.6 124.8 470.3 124.8 457.8 137.3L320.5 274.7L183.1 137.4z\"/></svg></button>\n                    <button alt=\"configure\" class=\"pull-right ${Object.keys(selected.specs).length === 0 ? \"hidden\": \"\"}\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><path d=\"M259.1 73.5C262.1 58.7 275.2 48 290.4 48L350.2 48C365.4 48 378.5 58.7 381.5 73.5L396 143.5C410.1 149.5 423.3 157.2 435.3 166.3L503.1 143.8C517.5 139 533.3 145 540.9 158.2L570.8 210C578.4 223.2 575.7 239.8 564.3 249.9L511 297.3C511.9 304.7 512.3 312.3 512.3 320C512.3 327.7 511.8 335.3 511 342.7L564.4 390.2C575.8 400.3 578.4 417 570.9 430.1L541 481.9C533.4 495 517.6 501.1 503.2 496.3L435.4 473.8C423.3 482.9 410.1 490.5 396.1 496.6L381.7 566.5C378.6 581.4 365.5 592 350.4 592L290.6 592C275.4 592 262.3 581.3 259.3 566.5L244.9 496.6C230.8 490.6 217.7 482.9 205.6 473.8L137.5 496.3C123.1 501.1 107.3 495.1 99.7 481.9L69.8 430.1C62.2 416.9 64.9 400.3 76.3 390.2L129.7 342.7C128.8 335.3 128.4 327.7 128.4 320C128.4 312.3 128.9 304.7 129.7 297.3L76.3 249.8C64.9 239.7 62.3 223 69.8 209.9L99.7 158.1C107.3 144.9 123.1 138.9 137.5 143.7L205.3 166.2C217.4 157.1 230.6 149.5 244.6 143.4L259.1 73.5zM320.3 400C364.5 399.8 400.2 363.9 400 319.7C399.8 275.5 363.9 239.8 319.7 240C275.5 240.2 239.8 276.1 240 320.3C240.2 364.5 276.1 400.2 320.3 400z\"/></svg></button>\n                </h3>\n                <form data-bind=\"form\" data-key=\"actions\" data-step=\"${safe(action.name)}\" data-array></form>\n            </div>\n            <hr>\n        </div>\n    `);\n    const $form = await createForm(\n        mutateForm(selected.specs, action.params || {}),\n        formTmpl(),\n    );\n    qs($action, `[data-bind=\"form\"]`).appendChild($form);\n    return $action;\n}\n\nasync function removeAction($target) {\n    const $box = $target.closest(\".box\");\n    await animate($box, {\n        time: 150,\n        keyframes: slideXOut(10),\n    });\n    $box.parentElement.remove();\n}\n\nasync function createAdd({ actions, createAction }) {\n    const $el = createElement(`\n        <div class=\"box\">\n            <button class=\"item\" title=\"add\">\n                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\">\n                    <path d=\"M352 128C352 110.3 337.7 96 320 96C302.3 96 288 110.3 288 128L288 288L128 288C110.3 288 96 302.3 96 320C96 337.7 110.3 352 128 352L288 352L288 512C288 529.7 302.3 544 320 544C337.7 544 352 529.7 352 512L352 352L512 352C529.7 352 544 337.7 544 320C544 302.3 529.7 288 512 288L352 288L352 128z\"/>\n                </svg>\n            </button>\n        </div>\n    `);\n    const $categories = createElement(`\n        <div class=\"hidden flex no-select\">\n            `+actions.map(({ name }) => `<button data-name=\"${safe(name)}\">${safe(name)}</button>`).join(\"\")+`\n        </div>\n    `);\n    const $item = qs($el, \".item\");\n    const width = 45;\n    let rotate = 0;\n    $el.appendChild($categories);\n    $item.onclick = async(e) => {\n        rotate += 45;\n        $item.firstElementChild.style.transform = `rotate(${rotate}deg)`;\n        if (rotate % 90 === 0) {\n            $categories.classList.add(\"hidden\");\n            await animate($el, {\n                time: 150,\n                keyframes: [\n                    { width: \"300px\" },\n                    { width: `${width}px` },\n                ],\n            });\n        } else {\n            await animate($el, {\n                time: 150,\n                keyframes: [\n                    { width: `${width}px` },\n                    { width: \"300px\" },\n                ],\n            });\n            $categories.classList.remove(\"hidden\");\n            animate($categories, { time: 80, keyframes: slideXIn(-20) });\n        }\n    };\n\n    qsa($el, \"[data-name]\").forEach(($action) => $action.onclick = () => {\n        const action = actions.find(({ name }) => name === $action.getAttribute(\"data-name\"));\n        if (action) createAction({ action, actions });\n        $item.onclick();\n    });\n    return $el;\n}\n\nfunction createSave({ withDelete = true }) {\n    const $fab = createElement(`\n        <form class=\"workflow-fab flex no-select\">\n            <select name=\"published\">\n                <option>publish</option>\n                <option>unpublish</option>\n                ${withDelete ? \"<option>delete</option>\" : \"\"}\n            </select>\n            <button style=\"display:contents;\" type=\"button\">\n                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\">\n                    <path d=\"M160 96C124.7 96 96 124.7 96 160L96 480C96 515.3 124.7 544 160 544L480 544C515.3 544 544 515.3 544 480L544 237.3C544 220.3 537.3 204 525.3 192L448 114.7C436 102.7 419.7 96 402.7 96L160 96zM192 192C192 174.3 206.3 160 224 160L384 160C401.7 160 416 174.3 416 192L416 256C416 273.7 401.7 288 384 288L224 288C206.3 288 192 273.7 192 256L192 192zM320 352C355.3 352 384 380.7 384 416C384 451.3 355.3 480 320 480C284.7 480 256 451.3 256 416C256 380.7 284.7 352 320 352z\"/>\n                </svg>\n                <component-icon class=\"hidden\" name=\"loading\"></component-icon>\n            </button>\n        </form>\n    `);\n    animate($fab, { time: 100, keyframes: slideXIn(5) });\n    return $fab;\n}\n\nfunction withToggle($form) {\n    const height = $form.clientHeight;\n    const $box = $form.closest(\".box\");\n    qs($box, `h3 button[alt=\"configure\"]`).onclick = () => {\n        const shouldOpen = $form.classList.contains(\"hidden\");\n        if (shouldOpen) {\n            animate($form, {\n                time: Math.max(120, height/2),\n                keyframes: [{ height: \"0px\" }, { height: `${height}px` }],\n            });\n            $form.classList.remove(\"hidden\");\n        } else {\n            animate($form, {\n                time: 80,\n                keyframes: [{ height: `${height}px` }, { height: \"0px\" }],\n                onExit: () => $form.classList.add(\"hidden\"),\n            });\n        }\n    };\n}\n\nfunction formData($page, { id }) {\n    const result = {\n        id: id || Date.now().toString(36) + Math.random().toString(36).slice(2, 6),\n    };\n    qsa($page.parentElement, \"form\").forEach(($form) => {\n        const params = [...new FormData($form)].reduce((acc, [key, value]) => {\n            if (value) acc[key] = value;\n            return acc;\n        }, {});\n        const key = $form.getAttribute(\"data-key\");\n        if (key) {\n            const name = $form.getAttribute(\"data-step\");\n            if ($form.hasAttribute(\"data-array\")) {\n                if (!result[key]) result[key] = [];\n                const step = { name };\n                if (Object.keys(params).length > 0) step.params = params;\n                result[key].push(step);\n            } else {\n                const step = { name };\n                if (Object.keys(params).length > 0) step.params = params;\n                result[key] = step;\n            }\n        } else {\n            Object.keys(params).forEach((key) => {\n                result[key] = params[key];\n            });\n        }\n    });\n    return result;\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/ctrl_workflow_list.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, onClick } from \"../../lib/rx.js\";\nimport { qs, safe } from \"../../lib/dom.js\";\nimport { animate } from \"../../lib/animate.js\";\nimport { createModal } from \"../../components/modal.js\";\nimport { generateSkeleton } from \"../../components/skeleton.js\";\nimport t from \"../../locales/index.js\";\n\nimport transition from \"./animate.js\";\n\nexport default async function(render, { workflows, triggers }) {\n    const $page = createElement(`\n        <div class=\"component_page_workflow\">\n            <h2>\n                Workflows\n                <a class=\"pull-right pointer no-select\">+</a>\n            </h2>\n            <div data-bind=\"workflows\"></div>\n        </div>\n    `);\n    render(transition($page));\n    const $workflows = qs($page, `[data-bind=\"workflows\"]`);\n\n    if (workflows.length === 0) $workflows.appendChild(createEmptyWorkflow({ triggers }));\n    else workflows\n        .sort((wa, wb) => wa.published === wb.published ? 0 : wa.published ? -1 : 1)\n        .forEach((workflow) => $workflows.appendChild(createWorkflow({ workflow })));\n\n    effect(onClick(qs($page, \"h2 > a\")).pipe(\n        rxjs.tap(($a) => animate($a, {\n            time: 300,\n            keyframes: [{ transform: \"rotate(0)\" }, { transform: `rotate(90deg)` }],\n        })),\n        rxjs.tap(() => ctrlModal(createModal(), { triggers })),\n    ));\n}\n\nfunction createWorkflow({ workflow }) {\n    const { name, published, actions, trigger, updated_at } = workflow;\n    const summaryHTML = {\n        trigger: `<button class=\"light\">${safe(trigger.name)}</button>`,\n        actions: (actions || []).map(({ name }) => `<button class=\"light\">${safe(name.split(\"/\")[0])}</button>`).join(\"\"),\n    };\n    const $workflow = createElement(`\n        <a href=\"./admin/workflow?specs=${encodeURIComponent(btoa(JSON.stringify(workflow)))}\" class=\"box ${published ? \"\" : \"disabled\"}\" data-link>\n            <h3 class=\"ellipsis\">\n                ${safe(name)}\n                <span>(${Intl.DateTimeFormat(navigator.language).format(new Date(safe(updated_at)))})</span>\n            </h3>\n            <div class=\"workflow-summary\">\n                ${summaryHTML.trigger} ${summaryHTML.actions ? \"→\" + summaryHTML.actions : \"\"}\n            </div>\n        </a>\n    `);\n    return $workflow;\n}\n\nfunction createEmptyWorkflow({ triggers }) {\n    const $empty = createElement(`\n        <h3 class=\"center empty no-select\">Create your first workflow</h3>\n    `);\n    effect(onClick($empty).pipe(\n        rxjs.tap(() => ctrlModal(createModal(), { triggers })),\n    ));\n    return $empty;\n}\n\nfunction ctrlModal(render, { triggers }) {\n    const $page = createElement(`\n        <div class=\"component_workflow_create\">\n            <form>\n                <h2>Step1: Name the Workflow</h2>\n                <input name=\"tag\" type=\"text\" placeholder=\"${t(\"Workflow Name\")}\" value=\"\">\n                <div data-bind=\"list\">\n                    ${generateSkeleton(1)}\n                </div>\n            </form>\n        </div>\n    `);\n    render($page);\n\n    const $list = qs($page, `[data-bind=\"list\"]`);\n    const $input = qs($page, \"input\");\n    effect(rxjs.of(triggers).pipe(\n        rxjs.map((arr) => arr.map(({ name, title, icon }) => createElement(`\n            <a class=\"item flex no-select ellipsis\" data-name=\"${safe(name)}\">${icon} ${safe(title)}</a>\n        `))),\n        rxjs.map(($els) => {\n            $list.innerHTML = \"\";\n            $input.focus();\n            const $fragment = document.createDocumentFragment();\n            $fragment.appendChild(createElement(`<h2>Step2: Select a Trigger</h2>`));\n            $els.forEach(($el) => $fragment.appendChild($el));\n            $list.appendChild($fragment);\n            const height = $list.clientHeight;\n            $list.style.height = \"0\";\n            return { height, $els };\n        }),\n        rxjs.mergeMap(({ height, $els }) => rxjs.fromEvent($input, \"keydown\").pipe(\n            rxjs.debounceTime(200),\n            rxjs.tap((e) => {\n                const shouldOpen = e.target.value.length > 0;\n                if ($list.clientHeight === 0 && shouldOpen) animate($list, {\n                    time: 300,\n                    keyframes: [{ height: \"0\" }, { height: `${height}px` }],\n                    onExit: () => $list.style.height = \"\",\n                });\n                else if ($list.clientHeight > 0 && !shouldOpen) animate($list, {\n                    time: 100,\n                    keyframes: [{ height: `${height}px` }, { height: \"0\" }],\n                    onExit: () => $list.style.height = \"0\",\n                });\n                $els.forEach(($el) => $el.setAttribute(\"href\", \"./admin/workflow?specs=\"+encodeURIComponent(btoa(JSON.stringify({\n                    name: e.target.value,\n                    published: false,\n                    trigger: { name: $el.getAttribute(\"data-name\") },\n                    actions: [{ name: \"tools/debug\" }]\n                })))));\n            }),\n        )),\n    ));\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/decorator.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport AdminOnly from \"./decorator_admin_only.js\";\nimport WithShell from \"./decorator_sidemenu.js\";\n\nimport \"../../components/loader.js\";\n\nexport default function HOC(ctrlPage) {\n    const ctrlLoading = (render) => {\n        render(createElement(\"<component-loader inlined></component-loader>\"));\n    };\n\n    return (render) => {\n        AdminOnly(WithShell(ctrlPage))(render);\n\n        return (render) => {\n            WithShell(ctrlLoading)(render);\n        };\n    };\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/decorator_admin_only.js",
    "content": "import rxjs, { effect } from \"../../lib/rx.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport ctrlLogin from \"./ctrl_login.js\";\nimport { isAdmin$ } from \"./model_admin_session.js\";\n\nexport default function AdminOnly(ctrlWrapped) {\n    return (render) => {\n        effect(isAdmin$().pipe(\n            rxjs.map((isAdmin) => isAdmin ? ctrlWrapped : ctrlLogin),\n            rxjs.tap((ctrl) => ctrl(render)),\n            rxjs.catchError(ctrlError(render)),\n        ));\n    };\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/decorator_sidemenu.css",
    "content": ".component_menu_sidebar {\n    height: 100vh;\n    background: linear-gradient(#232628, #2c3032);\n    width: 250px;\n    border-right: 2px solid var(--color);\n    padding: 50px 0px 0px 40px;\n    transition: transform 0.3s ease;\n    transition-delay: 0s;\n    flex-shrink: 0;\n}\n.component_menu_sidebar h2 {\n    font-family: \"Source Code Pro\", monospace;\n    color: white;\n    font-weight: normal;\n    font-size: 1.5rem;\n    line-height: 1.5rem;\n    margin: 25px 0 40px 0;\n}\n.component_menu_sidebar ul {\n    color: rgba(255, 255, 255, 0.5);\n    list-style-type: none;\n    padding: 0;\n}\n.component_menu_sidebar ul li {\n    margin: 15px 0;\n}\n.component_menu_sidebar ul li a.active, .component_menu_sidebar ul li a:hover {\n    color: white;\n}\n.component_menu_sidebar ul li.version {\n    position: absolute;\n    bottom: 0;\n    opacity: 0.25;\n}\n.component_menu_sidebar ul li a {\n    display: inline-block;\n}\n.component_menu_sidebar .header {\n    display: block;\n    height: 40px;\n}\n.component_menu_sidebar .header svg {\n    width: 40px;\n    height: 40px;\n}\n.component_menu_sidebar .header svg.arrow_left {\n    position: absolute;\n    margin-left: -35px;\n    opacity: 0.7;\n    padding: 8px;\n    box-sizing: border-box;\n    fill: #6f6f6f;\n}\n.component_menu_sidebar .header svg.logo {\n    transform: scale(1.4);\n    color: #9AD1ED;\n}\n\n@media screen and (max-width: 760px) {\n    .component_menu_sidebar .header svg.arrow_left {\n        display: none;\n    }\n}\n@media screen and (max-width: 1000px) {\n    .component_menu_sidebar {\n        padding-left: 30px;\n        width: 200px;\n    }\n    .component_menu_sidebar h2 {\n        font-size: 1.35em;\n    }\n}\n@media screen and (max-width: 760px) {\n    .component_menu_sidebar {\n        padding: 25px 10px;\n    }\n    .component_menu_sidebar h2 {\n        margin: 15px 0 25px 0;\n        font-size: 1.25em;\n        padding: 0;\n    }\n    .component_menu_sidebar .version {\n        display: none;\n    }\n}\n@media screen and (max-width: 650px) {\n    .component_menu_sidebar { width: 100px; }\n    .component_menu_sidebar h2 { display: none; }\n}\n@media screen and (max-width: 500px) {\n    .component_page_admin { flex-wrap: wrap; }\n\n    .component_menu_sidebar {\n        width: 100%;\n        height: initial;\n        display: flex;\n        padding: 10px;\n        border-bottom: 2px solid var(--color);\n    }\n    .component_menu_sidebar ul {\n        margin: 0;\n        display: flex;\n        align-items: center;\n        padding-left: 10px;\n    }\n    .component_menu_sidebar ul li {\n        margin: 0 10px;\n    }\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/decorator_sidemenu.js",
    "content": "import { createElement, createRender } from \"../../lib/skeleton/index.js\";\nimport { toHref } from \"../../lib/skeleton/router.js\";\nimport rxjs, { effect, stateMutation } from \"../../lib/rx.js\";\nimport { qs } from \"../../lib/dom.js\";\n\nimport { CSS } from \"../../helpers/loader.js\";\n\nimport { get as getRelease } from \"./model_release.js\";\nimport { isSaving } from \"./model_config.js\";\nimport { isLoading } from \"./model_audit.js\";\n\nimport \"../../components/icon.js\";\n\nexport default function(ctrl, opts = {}) {\n    return async function(render) {\n        const $page = createElement(`\n            <div class=\"component_page_admin\">\n                <style>${await CSS(import.meta.url, \"decorator_sidemenu.css\", \"index.css\")}</style>\n                <div class=\"component_menu_sidebar no-select\">\n                    <a class=\"header\" href=\"\">\n                        <svg class=\"arrow_left\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">\n                            <path d=\"m 16,7.16 -4.58,4.59 4.58,4.59 -1.41,1.41 -6,-6 6,-6 z\"/>\n                        </svg>\n                        <span data-bind=\"logo\"></span>\n                    </a>\n                    <h2>Admin console</h2>\n                    <ul>\n                        <li>\n                            <a href=\"${toHref(\"/admin/storage\")}\" data-link>\n                                Storage\n                            </a>\n                        </li>\n                        <li>\n                            <a href=\"${toHref(\"/admin/workflow\")}\" data-link>\n                                Workflow\n                            </a>\n                        </li>\n                        <li>\n                            <a href=\"${toHref(\"/admin/activity\")}\" data-link>\n                                Activity\n                            </a>\n                        </li>\n                        <li>\n                            <a href=\"${toHref(\"/admin/settings\")}\" data-link>\n                                Settings\n                            </a>\n                        </li>\n                        <li class=\"version\">\n                            <a href=\"${toHref(\"/admin/about\")}\" data-link data-bind=\"version\">\n                                &nbsp;\n                            </a>\n                        </li>\n                    </ul>\n                </div>\n                <div class=\"page_container scroll-y\" data-bind=\"admin\"></div>\n            </div>\n        `);\n        render($page);\n\n        // feature: setup the childrens\n        ctrl(createRender(qs($page, \"[data-bind=\\\"admin\\\"]\")), opts);\n\n        // feature: display the release version\n        effect(getRelease().pipe(\n            rxjs.map(({ version }) => version),\n            stateMutation(qs($page, \"[data-bind=\\\"version\\\"]\"), \"textContent\"),\n            rxjs.catchError(() => rxjs.EMPTY),\n        ));\n\n        // feature: logo serving as loading indicator\n        effect(rxjs.combineLatest([\n            isSaving().pipe(rxjs.startWith(false)),\n            isLoading().pipe(rxjs.startWith(false)),\n        ]).pipe(\n            rxjs.map(([a, b]) => a || b),\n            rxjs.map((isLoading) => isLoading\n                ? \"<component-icon name=\\\"loading\\\"></component-icon>\"\n                : `<svg class=\"logo\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\">\n                     <path d=\"M330 202a81 79 0 00-162 0 81 79 0 000 158 81 79 0 000-158m81 79a81 79 0 1181 79H168\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"35px\"/>\n                 </svg>`),\n            stateMutation(qs($page, \"[data-bind=\\\"logo\\\"]\"), \"innerHTML\")\n        ));\n\n        // feature: currently active menu link\n        effect(rxjs.of($page.querySelectorAll(\".component_menu_sidebar li a\")).pipe(\n            rxjs.mergeMap(($els) => $els),\n            rxjs.filter(($el) => location.pathname.endsWith($el.getAttribute(\"href\"))),\n            rxjs.tap(($el) => $el.classList.add(\"active\")),\n            rxjs.tap(($el) => $el.removeAttribute(\"href\"))\n        ));\n    };\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/helper_form.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs from \"../../lib/rx.js\";\n\nexport function renderLeaf({ format, label, description, type }) {\n    if (label === \"banner\") return createElement(`\n        <div class=\"banner\">\n            ${fromMarkdown(description)}\n        </div>\n    `);\n    const $el = createElement(`\n        <label class=\"no-select\">\n            <div class=\"flex\">\n                <span class=\"ellipsis\">\n                    ${format(label)}:\n                </span>\n                <div style=\"width:100%;\" data-bind=\"children\"></div>\n            </div>\n        </label>\n    `);\n    if (type === \"hidden\") $el.classList.add(\"hidden\");\n    if (description) $el.appendChild(createElement(`\n        <div class=\"flex\">\n            <span class=\"nothing\"></span>\n            <div style=\"width:100%;\">\n                <div class=\"description\">${description}</div>\n            </div>\n        </div>\n    `));\n    return $el;\n}\n\nfunction fromMarkdown(str = \"\") {\n    str = str.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, \"<a href=\\\"$2\\\">$1</a>\");\n    str = str.replaceAll(\"\\n\", \"<br>\");\n    return str;\n}\n\nexport function useForm$($inputNodeList) {\n    return rxjs.pipe(\n        rxjs.mergeMap(() => $inputNodeList()),\n        rxjs.mergeMap(($el) => rxjs.fromEvent($el, \"input\")),\n        rxjs.mergeMap(($el) => {\n            if ($el.target.checkValidity() === false) {\n                $el.target.reportValidity();\n                return rxjs.EMPTY;\n            }\n            return rxjs.of($el);\n        }),\n        rxjs.mergeMap(async(e) => ({\n            name: e.target.getAttribute(\"name\"),\n            value: await (async function() {\n                switch (e.target.getAttribute(\"type\")) {\n                case \"checkbox\":\n                    return e.target.checked;\n                case \"file\":\n                    if (e.target.files.length === 0) return null;\n                    return await new Promise((done) => {\n                        const reader = new window.FileReader();\n                        reader.readAsDataURL(e.target.files[0]);\n                        reader.onload = () => done(reader.result);\n                    });\n                default:\n                    return e.target.value;\n                }\n            }()),\n        })),\n        rxjs.scan((store, keyValue) => {\n            store[keyValue.name] = keyValue.value;\n            return store;\n        }, {})\n    );\n}\n\nexport function formObjToJSON$() {\n    const formObjToJSON = (o, level = 0) => {\n        const obj = Object.assign({}, o);\n        Object.keys(obj).forEach((key) => {\n            const t = obj[key];\n            if (\"label\" in t && \"type\" in t && \"default\" in t && \"value\" in t) {\n                let value = obj[key].value;\n                if (t.type === \"number\") value = parseInt(value);\n                obj[key] = value;\n            } else {\n                obj[key] = formObjToJSON(obj[key], level + 1);\n            }\n        });\n        return obj;\n    };\n    return rxjs.map(formObjToJSON);\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/index.css",
    "content": ".component_page_admin {\n    display: flex;\n}\n.component_page_admin .adminpage {\n    max-width: 1400px;\n}\n.component_page_admin .page_container {\n    width: 100%;\n    background: #fdfdfc;\n    padding-bottom: 150px;\n    padding-left: 60px;\n    padding-right: 60px;\n    box-sizing: border-box;\n    max-height: 100vh;\n}\n.component_page_admin .page_container.scroll-y > div {\n    max-width: 1300px;\n}\n@media screen and (max-width: 1000px) {\n    .component_page_admin .page_container {\n        padding-left: 30px;\n        padding-right: 30px;\n    }\n}\n@media screen and (max-width: 500px) {\n    .component_page_admin .page_container {\n        padding-left: 10px;\n        padding-right: 10px;\n    }\n}\n.component_page_admin .page_container h2 {\n    font-family: 'Source Code Pro', monospace;\n    text-shadow: 0 0 2px var(--bg-color);\n    font-size: 2em;\n    padding: 50px 0 0 0;\n    margin-bottom: 30px;\n    margin-top: 0;\n    font-weight: normal;\n    line-height: 1em;\n    margin-left: -60px;\n    margin-right: -60px;\n    padding-left: 60px;\n    padding-right: 60px;\n}\n@media screen and (max-width: 1000px) {\n    .component_page_admin .page_container h2 {\n        padding-top: 25px;\n    }\n}\n@media screen and (max-width: 500px) {\n    .component_page_admin .page_container h2 {\n        padding-top: 10px;\n    }\n}\n.component_page_admin .page_container h2:after {\n    content: \"_\";\n    display: block;\n    font-size: 0;\n    border-bottom: 3px solid var(--color);\n    width: 90px;\n    margin-top: 5px;\n    opacity: 0.9;\n    line-height: 0;\n}\n.component_page_admin .page_container .sticky h2 {\n    position: sticky;\n    background: rgba(253, 253, 252, 0.9);\n    backdrop-filter: saturate(180%) blur(20px);\n    box-shadow: 0 2px 10px rgba(0,0,0,0.02);\n    z-index: 2;\n    top: 0;\n}\n@media screen and (max-width: 550px) {\n    .component_page_admin .page_container label > div {\n        display: block;\n    }\n    .component_page_admin .page_container label > div .nothing {\n        display: none;\n    }\n}\n.component_page_admin .page_container label > div > span {\n    display: inline-block;\n    line-height: 30px;\n    min-width: 150px;\n    padding-right: 20px;\n    margin-top: auto;\n    margin-bottom: auto;\n}\n@media screen and (max-width: 760px) {\n    .component_page_admin .page_container label > div > span {\n        min-width: 115px;\n    }\n}\n\n/* form builder */\n.component_page_admin .page_container label > div .component_checkbox .indicator {\n    top: 6px;\n}\n.component_page_admin .page_container a {\n    color: var(--dark);\n}\n.component_page_admin .page_container a:hover {\n    opacity: 0.8;\n    transition: opacity 0.2s;\n    text-shadow: 0 0 #000000aa;\n}\n.component_page_admin .page_container pre {\n    font-family: 'Source Code Pro', monospace;\n    background: var(--dark);\n    font-size: 0.9em;\n    padding: 10px;\n    margin-bottom: 0;\n    border-radius: 3px;\n    color: white;\n    max-width: 100%;\n    overflow-x: auto;\n    overflow-y: auto;\n}\n.component_page_admin .page_container .component_loader > svg {\n    height: 50px;\n}\n.component_page_admin .page_container fieldset {\n    background: white;\n    border-color: #ebebec;\n    border-style: solid;\n    border-radius: 5px;\n    padding: 10px 15px;\n    margin: 15px 0 0 0;\n}\n\n.component_page_admin .formbuilder h2 ~ div > div > label,\n.component_page_admin .formbuilder h2 ~ div > div > div > label {\n    display: block;\n    border-bottom: 2px dashed var(--border);\n    margin-bottom: 15px;\n    padding-bottom: 5px;\n    margin-top: 15px;\n}\n.component_page_admin .formbuilder .component_input,\n.component_page_admin .formbuilder .component_textarea,\n.component_page_admin .formbuilder .component_select {\n    background: var(--border);\n    border: 2px solid var(--border);\n    border-radius: 3px;\n    padding-left: 5px;\n}\n.component_page_admin .formbuilder .component_input,\n.component_page_admin .formbuilder .component_select{\n    height: 33px;\n}\n.component_page_admin .formbuilder .component_input:not([disabled]):hover,\n.component_page_admin .formbuilder .component_input:not([disabled]):focus,\n.component_page_admin .formbuilder .component_textarea:not([disabled]):hover,\n.component_page_admin .formbuilder .component_textarea:not([disabled]):focus,\n.component_page_admin .formbuilder .component_select:not([disabled]):hover,\n.component_page_admin .formbuilder .component_select:not([disabled]):focus {\n    background: rgba(0, 0, 0, 0.08);\n}\n.component_page_admin form .formbuilder_password:focus-within img.component_icon,\n.component_page_admin form .formbuilder_password:hover img.component_icon{\n    border-color: rgba(0, 0, 0, 0.05);\n    background: rgba(0, 0, 0, 0.08);\n}\n.component_page_admin .formbuilder .formbuilder_password .component_input {\n    border-right: none;\n    border-top-right-radius: 0;\n    border-bottom-right-radius: 0;\n}\n.component_page_admin .formbuilder_password img.component_icon {\n    border: 2px solid rgba(0, 0, 0, 0.05);\n    height: 19px;\n    border-left: none;\n    background: rgba(0, 0, 0, 0.07);\n    border-top-right-radius: 3px;\n    border-bottom-right-radius: 3px;\n    padding-right: 5px;\n}\n.component_page_admin .formbuilder input::placeholder,\n.component_page_admin .formbuilder textarea::placeholder {\n    opacity: 0.6;\n}\n.component_page_admin .formbuilder .banner pre {\n    background: inherit;\n    color: inherit;\n    padding: 0;\n    margin: 0;\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/model_admin_session.js",
    "content": "import rxjs from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\n\nconst sessionSubject$ = new rxjs.Subject();\n\nconst adminSession$ = rxjs.merge(\n    sessionSubject$,\n    rxjs.merge(\n        rxjs.interval(30000),\n        rxjs.fromEvent(document, \"visibilitychange\").pipe(rxjs.filter(() => !document.hidden)),\n    ).pipe(\n        rxjs.startWith(null),\n        rxjs.mergeMap(() => ajax({ url: \"admin/api/session\", responseType: \"json\" })),\n        rxjs.map(({ responseJSON }) => responseJSON.result),\n    )\n).pipe(\n    rxjs.distinctUntilChanged(),\n    rxjs.shareReplay(1)\n);\n\nexport function isAdmin$() {\n    return adminSession$;\n}\n\nexport function authenticate$(body) {\n    return ajax({\n        url: \"admin/api/session\",\n        method: \"POST\",\n        body,\n        responseType: \"json\"\n    }).pipe(\n        rxjs.mapTo(true),\n        rxjs.tap((ok) => ok && sessionSubject$.next(ok)),\n    );\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/model_audit.js",
    "content": "import rxjs from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\n\nconst isLoading$ = new rxjs.BehaviorSubject(false);\n\nexport function get(searchParams = new URLSearchParams()) {\n    return ajax({\n        url: \"admin/api/audit?\" + searchParams.toString(),\n        responseType: \"json\"\n    }).pipe(\n        rxjs.map(({ responseJSON }) => responseJSON.result)\n    );\n}\n\nexport function setLoader(value) {\n    return isLoading$.next(!!value);\n}\n\nexport function isLoading() {\n    return isLoading$.asObservable();\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/model_auth_middleware.js",
    "content": "import rxjs from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\n\nconst model$ = ajax({\n    url: \"admin/api/middlewares/authentication\",\n    method: \"GET\",\n    responseType: \"json\"\n}).pipe(\n    rxjs.map(({ responseJSON }) => responseJSON.result),\n    rxjs.share(),\n);\n\nexport function getAuthMiddleware() {\n    return model$;\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/model_backend.js",
    "content": "import rxjs from \"../../lib/rx.js\";\n\nimport { getBackends as _getBackends } from \"../../model/backend.js\";\nimport { isSaving } from \"./model_config.js\";\n\nconst backend$ = _getBackends().pipe(rxjs.shareReplay(1));\n\nexport function getBackends() {\n    return isSaving().pipe(\n        rxjs.filter((loading) => !loading),\n        rxjs.mergeMap(() => backend$),\n    );\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/model_config.js",
    "content": "import rxjs from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\n\nconst isSaving$ = new rxjs.BehaviorSubject(false);\n\nconst config$ = isSaving$.pipe(\n    rxjs.filter((loading) => !loading),\n    rxjs.switchMapTo(ajax({\n        url: \"admin/api/config\",\n        method: \"GET\",\n        responseType: \"json\"\n    })),\n    rxjs.map((res) => res.responseJSON.result),\n    rxjs.shareReplay(1),\n);\n\nexport async function initConfig() {\n    if (isSaving$.value === true) isSaving$.next(false);\n}\n\nexport function isSaving() {\n    return isSaving$.asObservable();\n}\n\nexport function get() {\n    return config$;\n}\n\nexport function save() {\n    return rxjs.pipe(\n        rxjs.tap(() => isSaving$.next(true)),\n        rxjs.debounceTime(800),\n        rxjs.mergeMap((formData) => ajax({\n            url: \"admin/api/config\",\n            method: \"POST\",\n            responseType: \"json\",\n            body: formData,\n        })),\n        rxjs.tap(() => isSaving$.next(false)),\n        rxjs.catchError((err) => {\n            isSaving$.next(false);\n            return rxjs.throwError(err);\n        }),\n    );\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/model_log.js",
    "content": "import rxjs from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\n\nexport function url(logSize = 0) {\n    return \"admin/api/logs\" + (logSize ? `?maxSize=${logSize}` : \"\");\n}\n\nexport function get(t = 100) {\n    return ajax({\n        url: url(1024 * t), // fetch the last 100KB by default\n        responseType: \"text\",\n    }).pipe(\n        rxjs.map(({ response }) => response),\n    );\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/model_release.js",
    "content": "import rxjs from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\n\nconst release$ = ajax({\n    url: \"about\",\n    responseType: \"text\",\n}).pipe(rxjs.shareReplay(1));\n\nexport function get() {\n    return release$.pipe(\n        rxjs.map(({ response, responseHeaders }) => {\n            const a = document.createElement(\"html\");\n            a.innerHTML = response;\n            return {\n                html: a.querySelector(\"table\")?.outerHTML,\n                version: responseHeaders[\"x-powered-by\"].trim().replace(/^Filestash\\/([v\\.0-9]*).*$/, \"$1\")\n            };\n        }),\n    );\n}\n"
  },
  {
    "path": "public/assets/pages/adminpage/model_setup.js",
    "content": "import { createElement, nop } from \"../../lib/skeleton/index.js\";\nimport rxjs from \"../../lib/rx.js\";\nimport { qs, qsa } from \"../../lib/dom.js\";\nimport { createModal, MODAL_RIGHT_BUTTON } from \"../../components/modal.js\";\nimport notification from \"../../components/notification.js\";\nimport { query as getConfig } from \"../../model/config.js\";\n\nimport { formObjToJSON$ } from \"./helper_form.js\";\nimport { getBackendAvailable, getMiddlewareAvailable } from \"./ctrl_storage_state.js\";\nimport { get as getAdminConfig, save as saveConfig } from \"./model_config.js\";\n\nexport function getDeps({ getPassword = nop }) {\n    return rxjs.combineLatest([\n        getAdminConfig().pipe(formObjToJSON$()),\n        getBackendAvailable(),\n        getMiddlewareAvailable(),\n        getConfig(),\n    ]).pipe(\n        rxjs.map(([{ constant, middleware }, backendSpecs, authSpecs, { connections }]) => ([\n            {\n                name_success: \"You're using HTTPS\",\n                name_failure: \"Your connection isn't encrypted\",\n                pass: window.location.protocol !== \"http:\",\n                severe: true,\n                message: createElement(\"<i>Set up HTTPS to secure your connection and protect your data in transit</i>\"),\n            }, {\n                name_success: \"Running as non root user: '\" + constant.user + \"'\",\n                name_failure: \"Running as root\",\n                pass: constant.user !== \"root\",\n                severe: true,\n                message: createElement(\"<i>You should run as a non-privileged user, running as root is risky.</i>\"),\n            }, {\n                pass: false,\n                severe: connections.length === 0,\n                name_failure: connections.length === 0 ? \"Almost there!\" : \"Want to try something different?\",\n                message: withPreset(createElement(`\n                    <div id=\"configuration-preset\">\n                        <i>Pick a preset that best fits your needs:</i><br>\n                        <div data-bind=\"templates\"></div>\n                        <div class=\"note\">\n                            Notes: Templates get you started quickly, but they're just scratching the surface.\n                            Want more? Head to <a href=\"admin/storage\">/admin/storage</a> to wire up your own storage, authentication via SSO, and RBAC authorization.\n                        </div>\n                        <style>${CSS}</style>\n                    </div>\n                `), {\n                    password: getPassword(),\n                    hasStorage: (name, field = null) => !!backendSpecs[name] && (field === null ? true : !!backendSpecs[name][field]),\n                    hasAuth: (name) => !!authSpecs[name]\n                }),\n            }, {\n                name_success: \"Enterprise Support: active\",\n                name_failure: \"Enterprise Support: no\",\n                pass: constant.license !== \"agpl\",\n                message: createElement(\"<i>Need guaranteed response times? Upgrade to a support plan with 9x5 or 24x7 coverage and SLAs</i>\"),\n            }, {\n                name_success: \"Enterprise License: active\",\n                name_failure: \"You're on the AGPL version\",\n                pass: constant.license !== \"agpl\",\n                message: createElement(\"<i>Upgrade to Filestash enterprise for production usage in a commercial environment</i>\"),\n            }\n        ])),\n    );\n}\n\nfunction withPreset($preset, { password, hasStorage, hasAuth }) {\n    const TEMPLATES = [\n        {\n            name: \"Local files - just for me\",\n            success: createElement(\"<span>Ready! Visit <a class=\\\"wavy\\\" href=\\\"login\\\">/login</a> with your admin password to start browsing files.</span>\"),\n            output_connections: [{ \"type\": \"local\", \"label\": \"local\" }],\n            output_identity_provider: { \"type\": \"passthrough\", \"params\": \"{\\\"strategy\\\":\\\"password_only\\\"}\" },\n            output_attribute_mapping: { \"related_backend\": \"local\", \"params\": \"{\\\"local\\\":{\\\"type\\\":\\\"local\\\",\\\"password\\\":\\\"{{ .password }}\\\"}}\" },\n            isOK: hasStorage(\"local\") && hasAuth(\"passthrough\"),\n        },\n        {\n            name: \"Local Storage - Local Users\",\n            success: createElement(\"<span>Applied! Create your first users from <a class=\\\"wavy\\\" href=\\\"admin/simple-user-management\\\">/admin/simple-user-management</a></span>\"),\n            input_storage: password ? null : [\"admin_password\"],\n            output_connections: [{ \"type\": \"local\", \"label\": \"local\" }],\n            output_identity_provider: { \"type\": \"local\", \"params\": \"{}\" },\n            output_attribute_mapping: {\n                \"related_backend\": \"local\",\n                \"params\": \"{\\\"local\\\":{\\\"type\\\":\\\"local\\\",\\\"password\\\":\\\"\"+(password || \"[[admin_password]]\") +\"\\\"}}\",\n            },\n            isOK: hasStorage(\"local\") && hasAuth(\"local\"),\n        },\n        {\n            name: \"SFTP Storage - Use existing credentials\",\n            success: createElement(\"<span>Done! Visit <a class=\\\"wavy\\\" href=\\\"login\\\">/login</a> and authenticate with your SFTP username and password or private key.</span>\"),\n            input_storage: [\"sftp_hostname\"],\n            output_connections: [{ \"type\": \"sftp\", \"label\": \"sftp\" }],\n            output_identity_provider: { \"type\": \"passthrough\", \"params\": \"{\\\"strategy\\\":\\\"username_and_password\\\"}\" },\n            output_attribute_mapping: { \"related_backend\": \"sftp\", \"params\": \"{\\\"sftp\\\":{\\\"type\\\":\\\"sftp\\\",\\\"hostname\\\":\\\"[[sftp_hostname]]\\\",\\\"username\\\":\\\"{{ .user }}\\\",\\\"password\\\":\\\"{{ .password }}\\\"}}\" },\n            isOK: hasStorage(\"sftp\") && hasAuth(\"passthrough\"),\n        },\n        {\n            name: \"S3 Storage - Use your AWS credentials\",\n            success: createElement(\"<span>All set! Visit <a class=\\\"wavy\\\" href=\\\"login\\\">/login</a> - use your access_key_id as username, secret_access_key as password.</span>\"),\n            output_connections: [{ \"type\": \"s3\", \"label\": \"s3\" }],\n            output_identity_provider: { \"type\": \"passthrough\", \"params\": \"{\\\"strategy\\\":\\\"username_and_password\\\"}\" },\n            output_attribute_mapping: { \"related_backend\": \"s3\", \"params\": \"{\\\"s3\\\":{\\\"type\\\":\\\"s3\\\",\\\"access_key_id\\\":\\\"{{ .user }}\\\",\\\"secret_access_key\\\":\\\"{{ .password }}\\\"}}\" },\n            isOK: hasStorage(\"s3\") && hasAuth(\"passthrough\"),\n        },\n        {\n            name: \"S3 Storage - Multiple users\",\n            success: createElement(\"<span>All set! Create your first users from <a class=\\\"wavy\\\" href=\\\"admin/simple-user-management\\\">/admin/simple-user-management</a></span>\"),\n            input_storage: [\"access_key_id\", \"secret_access_key\"],\n            output_connections: [{ \"type\": \"s3\", \"label\": \"s3\" }],\n            output_identity_provider: { \"type\": \"local\", \"params\": \"{}\" },\n            output_attribute_mapping: { \"related_backend\": \"s3\", \"params\": \"{\\\"s3\\\":{\\\"type\\\":\\\"s3\\\",\\\"access_key_id\\\":\\\"[[access_key_id]]\\\",\\\"secret_access_key\\\":\\\"[[secret_access_key]]\\\"}}\" },\n            isOK: hasStorage(\"s3\") && hasAuth(\"local\"),\n        },\n        {\n            name: \"FTP Storage - WordPress Users\",\n            success: createElement(\"<span>Done! Visit <a class=\\\"wavy\\\" href=\\\"login\\\">/login</a> - use your wordpress credentials to access your FTP.</span>\"),\n            input_storage: [\"ftp_hostname\", \"ftp_username\", \"ftp_password\"],\n            input_auth: [\"wordpress_url\"],\n            output_connections: [{ \"type\": \"ftp\", \"label\": \"ftp\" }],\n            output_identity_provider: { \"type\": \"wordpress\", \"params\": \"{\\\"url\\\":\\\"[[wordpress_url]]\\\"}\" },\n            output_attribute_mapping: { \"related_backend\": \"ftp\", \"params\": \"{\\\"ftp\\\":{\\\"type\\\":\\\"ftp\\\",\\\"hostname\\\":\\\"[[ftp_hostname]]\\\",\\\"username\\\":\\\"[[ftp_username]]\\\",\\\"password\\\":\\\"[[ftp_password]]\\\"}}\" },\n            isOK: hasStorage(\"ftp\") && hasAuth(\"wordpress\"),\n        },\n    ];\n\n    const $templates = qs($preset, `[data-bind=\"templates\"]`);\n    const onClick = (specs) => {\n        return async() => {\n            if (!specs.isOK) return createModal({ withButtonsRight: \"OK\" })(createElement(`\n                <div>\n                    Your build is missing some plugins. Contact Support\n                </div>\n            `));\n            const output = {\n                connections: specs.output_connections,\n                identity_provider: specs.output_identity_provider,\n                attribute_mapping: specs.output_attribute_mapping,\n            };\n            const $form = createElement(\"<form id=\\\"configuration-preset-modal\\\"></form>\");\n            $form.onsubmit = (e) => e.preventDefault();\n            const save = await new Promise((done) => {\n                const fields = (specs.input_storage || []).concat(specs.input_auth || []);\n                if (fields.length === 0) return done(true);\n                fields.forEach((fieldName) => $form.appendChild(createElement(`\n                    <label class=\"no-select\">\n                        ${fieldName}\n                        <input class=\"component_input\" type=\"text\" name=\"${fieldName}\" />\n                    </label>\n                `)));\n                createModal({ withButtonsRight: \"Apply\" })($form, (choice, $modal) => {\n                    if (choice !== MODAL_RIGHT_BUTTON) return done(false);\n                    let ok = false;\n                    qsa($modal, \"input\").forEach(($input) => {\n                        if ($input.value) ok = true;\n                        output.identity_provider.params = output.identity_provider.params.replaceAll(\"[[\"+$input.getAttribute(\"name\")+\"]]\", $input.value.replaceAll(`\"`, `\\\"`));\n                        output.attribute_mapping.params = output.attribute_mapping.params.replaceAll(\"[[\"+$input.getAttribute(\"name\")+\"]]\", $input.value.replaceAll(`\"`, `\\\"`));\n                    });\n                    done(ok);\n                });\n            });\n            if (save === false) return;\n            await getAdminConfig().pipe(\n                rxjs.first(),\n                formObjToJSON$(),\n                rxjs.map((config) => {\n                    config.connections = output.connections;\n                    config.middleware.attribute_mapping = output.attribute_mapping;\n                    config.middleware.identity_provider = output.identity_provider;\n                    return config;\n                }),\n                saveConfig(),\n                rxjs.tap(() => notification.success(specs.success)),\n                rxjs.catchError((err) => {\n                    notification.error(err.message);\n                    return rxjs.throwError(err);\n                }),\n            ).toPromise();\n        };\n    };\n    TEMPLATES.map((specs) => {\n        const $div = createElement(`<div></div>`);\n        const $button = document.createElement(\"button\");\n        $button.className = \"no-select\";\n        $button.innerText = `APPLY PRESET`;\n        $button.onclick = onClick(specs);\n        $div.appendChild($button);\n        $div.appendChild(createElement(`<span> &#x27A1; ${specs.name || \"N/A\"}</span>`));\n        return $div;\n    }).forEach(($button) => $templates.appendChild($button));\n\n    return $preset;\n}\n\nconst CSS = `\n#configuration-preset .note {\n    font-style: italic;\n    text-align: justify;\n    padding: 5px 10px;\n    margin-top: 10px;\n    border: 2px dashed rgba(255,255,255,0.2);\n    font-size: 0.95rem;\n}\n\n#configuration-preset a {\n    text-decoration: underline;\n    text-decoration-style: wavy;\n    text-decoration-color: rgba(0, 0, 0, 0.3);\n}\n\n#configuration-preset button {\n    background: rgba(0, 0, 0, 0.3);\n    margin: 2px 0;\n    padding: 5px 9px;\n    text-transform: uppercase;\n    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);\n    font-size: 0.92rem;\n}\n\n.touch-no #configuration-preset button:hover {\n    background: rgba(0, 0, 0, 0.45);\n}\n\n#configuration-preset-modal label {\n    font-size: 0.9rem;\n}\n`;\n"
  },
  {
    "path": "public/assets/pages/adminpage/model_workflow.js",
    "content": "import rxjs from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\n\nexport function workflowAll() {\n    return ajax({\n        url: \"admin/api/workflow\",\n        responseType: \"json\",\n    }).pipe(\n        rxjs.map(({ responseJSON }) => responseJSON.result),\n        rxjs.map(({ workflows, triggers, actions }) => ({\n            workflows,\n            triggers,\n            actions,\n        })),\n    );\n}\n\nexport function workflowUpsert(body) {\n    return ajax({\n        url: \"admin/api/workflow\",\n        responseType: \"json\",\n        method: \"POST\",\n        body,\n    }).pipe(\n        rxjs.map(({ responseJSON }) => responseJSON.result)\n    );\n}\n\nexport function workflowDelete(id) {\n    return ajax({\n        url: \"admin/api/workflow?id=\" + id,\n        responseType: \"json\",\n        method: \"DELETE\",\n    }).pipe(\n        rxjs.map(({ responseJSON }) => responseJSON.result)\n    );\n}\n\nexport function workflowGet(id) {\n    return ajax({\n        url: \"admin/api/workflow/\" + id,\n        responseType: \"json\",\n    }).pipe(\n        rxjs.map(({ responseJSON }) => responseJSON.result)\n    );\n}\n"
  },
  {
    "path": "public/assets/pages/connectpage/ctrl_forkme.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs from \"../../lib/rx.js\";\n\nimport config$ from \"./model_config.js\";\n\nexport default async function(render) {\n    const hasFork = await config$.pipe(rxjs.filter(({ license }) => license === \"agpl\")).toPromise();\n    if (!hasFork) return;\n\n    render(createElement(`\n        <a href=\"https://github.com/mickael-kerjean/skeleton\" class=\"component_forkme\" aria-label=\"View source on GitHub\">\n            <svg width=\"80\" height=\"80\" viewBox=\"0 0 250 250\" style=\"fill:var(--emphasis); color:var(--primary); position: absolute; top: 0; border: 0; right: 0;\" aria-hidden=\"true\">\n                <path d=\"M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z\"></path>\n                <path d=\"M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2\" fill=\"currentColor\" style=\"transform-origin: 130px 106px;\" class=\"octo-arm\"></path>\n                <path d=\"M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z\" fill=\"currentColor\" class=\"octo-body\"></path>\n            </svg>\n            <style>\n                .component_forkme:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}\n                .dark-mode .component_forkme .octo-arm,\n                .dark-mode .component_forkme .octo-body { fill: var(--bg-color); }\n                @media (max-width:500px){\n                    .component_forkme:hover .octo-arm{animation:none}\n                    .component_forkme .octo-arm{animation:octocat-wave 560ms ease-in-out}\n                }\n                @keyframes octocat-wave{\n                    0%,100%{transform:rotate(0)}\n                    20%,60%{transform:rotate(-25deg)}\n                    40%,80%{transform:rotate(10deg)}\n                }\n            </style>\n        </a>\n    `));\n}\n"
  },
  {
    "path": "public/assets/pages/connectpage/ctrl_form.css",
    "content": ".component_page_connection_form .box {\n    margin-bottom: 7px;\n    border-radius: 3px;\n    box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2);\n    background: white;\n}\n.component_page_connection_form .box button {\n    padding: 7px 5px;\n}\n.component_page_connection_form .box form {\n    margin-top: 5px;\n}\n.component_page_connection_form div.buttons {\n    display: flex;\n    margin: 0px 0px 15px 0px;\n    padding: 5px;\n    overflow-x: auto;\n}\n.component_page_connection_form div.buttons button {\n    width: 100%;\n    min-width: 110px;\n    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.2);\n    border: 1px solid transparent;\n}\n.component_page_connection_form div.buttons button:not(.active):hover {\n    transition: background 0.2s ease;\n    background: rgba(0, 0, 0, 0.03);\n}\n.component_page_connection_form div.buttons button.active {\n    transition: box-shadow 0.2s;\n    box-shadow: 0px 2px 20px rgba(0, 0, 0, 0.2);\n}\n.component_page_connection_form form label {\n    font-style: italic;\n    font-size: 0.9em;\n    display: block;\n    text-transform: capitalize;\n}\n.component_page_connection_form form .advanced {\n    color: rgba(0, 0, 0, 0.4);\n    font-style: italic;\n}\n.component_page_connection_form form .advanced_form {\n    max-height: 156px;\n    margin-top: 5px;\n    margin-bottom: 5px;\n    padding-right: 10px;\n}\n.component_page_connection_form form button.emphasis {\n    color: white;\n    text-transform: uppercase;\n    margin-top: 5px;\n}\n\n.component_page_connection_form form .third-party {\n    text-align: center;\n}\n.component_page_connection_form form .third-party img {\n    height: 115px;\n    margin: 0;\n}\n.component_page_connection_form form .component_checkbox .indicator {\n    top: 2px;\n}\n.component_page_connection_form form input.component_input[name=\"oauth2\"] {\n    display: none;\n}\n.component_page_connection_form .component_loader {\n    margin: 45px 0;\n}\n\n.scroll-x {\n    overflow-x: auto !important;\n    overflow-y: hidden !important;\n    -webkit-overflow-scrolling: touch;\n}\n.scroll-x::-webkit-scrollbar {\n    height: 4px;\n}\n.scroll-x::-webkit-scrollbar-track {\n    background: white;\n}\n.scroll-x::-webkit-scrollbar-thumb {\n    -webkit-border-radius: 2px;\n    -moz-border-radius: 2px;\n    -ms-border-radius: 2px;\n    -o-border-radius: 2px;\n    border-radius: 2px;\n    background: rgba(0, 0, 0, 0.1);\n}\n\n.dark-mode div.buttons button.active {\n    background: var(--bg-color);\n    color: var(--super-light);\n}\n"
  },
  {
    "path": "public/assets/pages/connectpage/ctrl_form.js",
    "content": "import { createElement, navigate } from \"../../lib/skeleton/index.js\";\nimport { toHref } from \"../../lib/skeleton/router.js\";\nimport rxjs, { effect, applyMutation, applyMutations, preventDefault, onClick } from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { qs, qsa, safe } from \"../../lib/dom.js\";\nimport { animate, slideYIn, transition, opacityIn } from \"../../lib/animate.js\";\nimport assert from \"../../lib/assert.js\";\nimport { forwardURLParams } from \"../../lib/path.js\";\nimport { createForm } from \"../../lib/form.js\";\nimport { settings_get, settings_put } from \"../../lib/settings.js\";\nimport t from \"../../locales/index.js\";\nimport { formTmpl } from \"../../components/form.js\";\nimport notification from \"../../components/notification.js\";\nimport { CSS } from \"../../helpers/loader.js\";\nimport { createSession } from \"../../model/session.js\";\n\nimport ctrlError from \"../ctrl_error.js\";\nimport config$ from \"./model_config.js\";\nimport backend$ from \"./model_backend.js\";\nimport { setCurrentBackend, getCurrentBackend, getURLParams } from \"./ctrl_form_state.js\";\nimport { updateBackend } from \"../filespage/cache.js\";\n\nconst connections$ = config$.pipe(\n    rxjs.map(({ connections, auth }) => (connections || []).map((conn) => {\n        conn.middleware = (auth || []).indexOf(conn.label) >= 0;\n        return conn;\n    })),\n    rxjs.shareReplay(1),\n);\n\nexport default async function(render) {\n    const $page = createElement(`\n        <div class=\"no-select component_page_connection_form\">\n            <style>${await CSS(import.meta.url, \"ctrl_form.css\")}</style>\n            <div role=\"navigation\" class=\"buttons scroll-x box hidden\"></div>\n            <div data-bind=\"form\" class=\"box hidden\"><form></form></div>\n        </div>\n    `);\n    render(transition($page, {\n        enter: [\n            { transform: \"scale(0.97)\", opacity: 0 },\n            { transform: \"scale(1)\", opacity: 1 },\n        ],\n        timeEnter: 100,\n    }));\n\n    // feature1: create navigation buttons to select storage\n    const $nav = qs($page, \"[role=\\\"navigation\\\"]\");\n    effect(connections$.pipe(\n        rxjs.map((conns) => conns.map((conn, i) => ({ ...conn, n: i }))),\n        rxjs.map((conns) => conns.map(({ label, n }) => createElement(`<button data-current=\"${n}\">${safe(label)}</button>`))),\n        applyMutations($nav, \"appendChild\"),\n        rxjs.tap((conns = []) => { if (conns.length > 1) $nav.classList.remove(\"hidden\"); }),\n        rxjs.tap(() => animate($nav, { time: 250, keyframes: opacityIn() })),\n    ));\n\n    // feature2: select a default storage among all the available ones\n    effect(connections$.pipe(\n        rxjs.map((conns) => {\n            let n = parseInt(settings_get(\"login_tab\"));\n            if (Number.isNaN(n)) n = (conns.length || 0) / 2 - 1;\n            if (n < 0 || n >= conns.length) n = 0;\n            return n;\n        }),\n        rxjs.tap((current) => setCurrentBackend(Math.round(current))),\n    ));\n\n    // feature3: create the storage forms\n    const formSpecs$ = connections$.pipe(rxjs.mergeMap((conns) => backend$.pipe(\n        rxjs.map((backendSpecs) => conns.map(({ type, middleware, label }) => {\n            if (middleware) return { // admin has set this storage as auth middleware\n                middleware: { type: \"hidden\" },\n                label: { type: \"hidden\", value: label },\n            };\n            return backendSpecs[type] || {};\n        })),\n    )));\n    effect(getCurrentBackend().pipe(\n        rxjs.mergeMap((n) => formSpecs$.pipe(\n            rxjs.map((specs) => specs[n]),\n        )),\n        rxjs.mergeMap((formSpec) => createForm(formSpec, formTmpl({\n            renderNode: () => createElement(\"<div></div>\"),\n            renderLeaf: ({ label, type }) => {\n                if (type === \"enable\") return createElement(`\n                    <label class=\"advanced\">\n                        <span data-bind=\"children\"></span>\n                        ${label}\n                    </label>\n                `);\n                return createElement(\"<label></label>\");\n            }\n        }))),\n        applyMutation(qs($page, \"[data-bind=\\\"form\\\"] form\"), \"replaceChildren\"),\n        rxjs.tap(($innerForm) => $innerForm.parentElement.appendChild(createElement(`<button class=\"emphasis full-width\">${t(\"CONNECT\")}</button>`))),\n        rxjs.tap(($innerForm) => {\n            const $box = $innerForm.parentElement.parentElement;\n            let $animationTarget = $innerForm;\n            if ($box.classList.contains(\"hidden\")) { // first load\n                $box.classList.remove(\"hidden\");\n                $animationTarget = $box;\n            }\n            animate($animationTarget, { time: 200, keyframes: slideYIn(2) });\n        }),\n    ));\n\n    // feature4: interaction with the nav buttons\n    effect(getCurrentBackend().pipe(\n        rxjs.first(),\n        rxjs.mergeMap(() => qsa($page, \"[role=\\\"navigation\\\"] button\")),\n        rxjs.mergeMap(($button) => onClick($button)),\n        rxjs.map(($button) => parseInt($button.getAttribute(\"data-current\"))),\n        rxjs.distinctUntilChanged(),\n        rxjs.tap((current) => {\n            settings_put(\"login_tab\", current);\n            setCurrentBackend(current);\n        }),\n    ));\n\n    // feature5: highlight the currently selected storage\n    effect(getCurrentBackend().pipe(\n        rxjs.map((n) => [qsa($page, \"[role=\\\"navigation\\\"] button\"), n]),\n        rxjs.tap(([$buttons, n]) => $buttons.forEach(($button, i) => {\n            if (i !== n) $button.classList.remove(\"active\", \"primary\");\n            else $button.classList.add(\"active\", \"primary\");\n        })),\n    ));\n\n    // feature6: form submission\n    const $loader = createElement(`<component-loader></component-loader>`);\n    const toggleLoader = (hide) => {\n        if (hide) {\n            $page.classList.add(\"hidden\");\n            assert.truthy($page.parentElement).appendChild($loader);\n        } else {\n            $loader.remove();\n            $page.classList.remove(\"hidden\");\n        }\n    };\n    effect(rxjs.merge(\n        // 6.a form submission event handler\n        rxjs.fromEvent(qs($page, \"form\"), \"submit\").pipe(\n            preventDefault(),\n            rxjs.map((e) => new FormData(e.target)),\n            rxjs.map((formData) => {\n                const json = {};\n                for (const pair of formData.entries()) {\n                    json[pair[0]] = pair[1] === \"\" ? null : pair[1];\n                }\n                return json;\n            }),\n        ),\n        // 6.b formatted URL in the like of type=xxx&etc=etc\n        rxjs.of(getURLParams()).pipe(\n            rxjs.filter(({ type }) => !!type),\n            rxjs.mergeMap((urlParams) => connections$.pipe(\n                rxjs.map((conns) => conns.filter(({ middleware, type }) => middleware !== true && type === urlParams[\"type\"])),\n                rxjs.mergeMap((conns) => {\n                    if (conns.length === 0) return rxjs.EMPTY;\n                    return rxjs.of(urlParams);\n                }),\n            )),\n        ),\n        // 6.c auto submit when it's the only choice available\n        connections$.pipe(\n            rxjs.filter((conns) => conns.length === 1),\n            rxjs.map((conns) => conns[0]),\n            rxjs.filter(({ middleware }) => middleware),\n        ),\n    ).pipe(\n        rxjs.mergeMap((formData) => { // CASE 1: authentication middleware flow\n            if (!(\"middleware\" in formData)) return rxjs.of(formData);\n            let url = \"api/session/auth/?action=redirect\";\n            url += \"&label=\" + formData[\"label\"];\n            const p = getURLParams();\n            if (Object.keys(p).length > 0) {\n                url += \"&state=\" + btoa(JSON.stringify(p));\n            }\n            toggleLoader(true);\n            location.href = url;\n            return rxjs.EMPTY;\n        }),\n        rxjs.mergeMap((formData) => { // CASE 2: oauth2 related backends like dropbox and gdrive\n            if (!(\"oauth2\" in formData)) return rxjs.of(formData);\n            return new rxjs.Observable((subscriber) => {\n                const u = new URL(location.toString());\n                u.pathname = formData[\"oauth2\"];\n                const _next = getURLParams()[\"next\"];\n                if (_next) u.searchParams.set(\"next\", _next);\n                subscriber.next(u.toString());\n            }).pipe(\n                rxjs.tap(() => toggleLoader(true)),\n                rxjs.mergeMap((url) => ajax({ url, responseType: \"json\" })),\n                rxjs.tap(({ responseJSON }) => location.href = responseJSON.result),\n                rxjs.catchError(ctrlError()),\n            );\n        }),\n        rxjs.mergeMap((formData) => { // CASE 3: regular login\n            delete formData[\"label\"];\n            delete formData[\"middleware\"];\n            return rxjs.of(null).pipe(\n                rxjs.tap(() => toggleLoader(true)),\n                rxjs.mergeMap(() => createSession(formData)),\n                rxjs.tap(({ home, backendID }) => {\n                    updateBackend(backendID);\n                    let redirectURL = toHref(\"/files/\");\n                    const GET = getURLParams();\n                    if (GET[\"next\"]) redirectURL = GET[\"next\"];\n                    else if (home) redirectURL = toHref(\"/files\" + home);\n\n                    if (redirectURL.startsWith(\"/api/\")) return location.replace(redirectURL);\n                    navigate(forwardURLParams(redirectURL, [\"nav\"]));\n                }),\n                rxjs.catchError((err) => {\n                    toggleLoader(false);\n                    notification.error(t(err && err.message));\n                    return rxjs.EMPTY;\n                })\n            );\n        }),\n    ));\n\n    // feature7: empty connection handling\n    effect(connections$.pipe(\n        rxjs.filter((conns) => conns.length === 0),\n        rxjs.mergeMap(() => Promise.reject(new Error(\"there is nothing here\"))), // TODO: check translation?\n        rxjs.catchError(ctrlError()),\n    ));\n\n    // feature8: bug on back navigation where loader get stuck\n    effect(rxjs.fromEvent(window, \"pageshow\").pipe(\n        rxjs.tap((event) => event.persisted && toggleLoader(false)),\n    ));\n}\n"
  },
  {
    "path": "public/assets/pages/connectpage/ctrl_form_state.js",
    "content": "import rxjs from \"../../lib/rx.js\";\n\nconst currentBackend$ = new rxjs.ReplaySubject(1);\n\nexport function setCurrentBackend(n) {\n    currentBackend$.next(n);\n}\n\nexport function getCurrentBackend() {\n    return currentBackend$.asObservable();\n}\n\nexport function getURLParams() {\n    return [...new URLSearchParams(location.search)].reduce((acc, [key, value]) => {\n        acc[key] = value;\n        return acc;\n    }, {});\n}\n"
  },
  {
    "path": "public/assets/pages/connectpage/ctrl_poweredby.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs from \"../../lib/rx.js\";\nimport { transition } from \"../../lib/animate.js\";\nimport t from \"../../locales/index.js\";\n\nimport config$ from \"./model_config.js\";\n\nexport default async function(render) {\n    const hasFork = await config$.pipe(rxjs.filter(({ license }) => license === \"agpl\")).toPromise();\n    if (!hasFork) return;\n\n    await new Promise((done) => setTimeout(done, 1000));\n\n    render(transition(createElement(`\n        <div class=\"component_poweredbyfilestash\">\n            ${t(\"Powered by\")} <strong><a href=\"https://www.filestash.app\">Filestash</a></strong>\n            <style>\n                .component_poweredbyfilestash{\n                    display: inline-block;\n                    color: rgba(0, 0, 0, 0.4);\n                    font-size: 0.9em;\n                    line-height: 20px;\n\n                    position: fixed;\n                    bottom: 10px;\n                    right: 20px;\n                }\n                .component_poweredbyfilestash strong{\n                    font-weight: normal;\n                }\n                .component_poweredbyfilestash strong a{\n                    text-decoration: underline;\n                }\n                .dark-mode .component_poweredbyfilestash {\n                    color: var(--light);\n                }\n            </style>\n        </div>\n    `)));\n}\n"
  },
  {
    "path": "public/assets/pages/connectpage/model_backend.js",
    "content": "import rxjs from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\n\nexport default ajax({\n    url: \"api/backend\",\n    responseType: \"json\"\n}).pipe(\n    rxjs.map(({ responseJSON }) => responseJSON.result),\n    rxjs.shareReplay(1),\n);\n"
  },
  {
    "path": "public/assets/pages/connectpage/model_config.js",
    "content": "import rxjs from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\n\nexport default ajax({\n    url: \"api/config\",\n    responseType: \"json\"\n}).pipe(\n    rxjs.map(({ responseJSON }) => responseJSON.result),\n    rxjs.shareReplay(1),\n);\n"
  },
  {
    "path": "public/assets/pages/ctrl_adminpage.js",
    "content": "import { navigate } from \"../lib/skeleton/index.js\";\nimport { toHref } from \"../lib/skeleton/router.js\";\nimport AdminOnly from \"./adminpage/decorator_admin_only.js\";\n\nexport default AdminOnly(function() {\n    navigate(toHref(\"/admin/storage\"));\n});\n"
  },
  {
    "path": "public/assets/pages/ctrl_connectpage.css",
    "content": ".component_page_connect{\n    background: var(--primary);\n    height: 100%;\n    padding: 15px 0 0 0;\n}\n.dark-mode .component_page_connect{\n    background: var(--bg-color);\n}\n"
  },
  {
    "path": "public/assets/pages/ctrl_connectpage.js",
    "content": "import { createElement, createRender } from \"../lib/skeleton/index.js\";\nimport rxjs, { effect, applyMutation } from \"../lib/rx.js\";\nimport { qs } from \"../lib/dom.js\";\nimport { CSS } from \"../helpers/loader.js\";\n\nimport ctrlForm from \"./connectpage/ctrl_form.js\";\nimport ctrlForkme from \"./connectpage/ctrl_forkme.js\";\nimport ctrlPoweredby from \"./connectpage/ctrl_poweredby.js\";\n\nexport default async function(render) {\n    const $page = createElement(`\n        <div class=\"component_page_connect\">\n            <style>${await CSS(import.meta.url, \"ctrl_connectpage.css\")}</style>\n            <div data-bind=\"component_forkme\"></div>\n            <div data-bind=\"centerthis\" class=\"component_page_connection_form component_container\" style=\"max-width:565px;\">\n                <div data-bind=\"component_form\"></div>\n            </div>\n            <div data-bind=\"component_poweredby\"></div>\n        </div>\n    `);\n    render($page);\n\n    // feature1: forkme & poweredby button\n    ctrlForkme(createRender(qs($page, `[data-bind=\"component_forkme\"]`)));\n    ctrlPoweredby(createRender(qs($page, `[data-bind=\"component_poweredby\"]`)));\n    await new Promise((done) => setTimeout(done, 250));\n\n    // feature2: connection form\n    ctrlForm(createRender(qs($page, `[data-bind=\"component_form\"]`)));\n\n    // feature3: center the form\n    effect(rxjs.fromEvent(window, \"resize\").pipe(\n        rxjs.startWith(null),\n        rxjs.map(() => {\n            const h = 400;\n            const size = Math.round((document.body.offsetHeight - h) / 2);\n            if (size < 0) return 0;\n            if (size > 150) return 150;\n            return size;\n        }),\n        rxjs.map((size) => [\"padding-top\", `${size}px`]),\n        applyMutation(qs($page, `[data-bind=\"centerthis\"]`), \"style\", \"setProperty\")\n    ));\n}\n"
  },
  {
    "path": "public/assets/pages/ctrl_error.js",
    "content": "import { createElement, createRender } from \"../lib/skeleton/index.js\";\nimport { toHref, fromHref, navigate } from \"../lib/skeleton/router.js\";\nimport { forwardURLParams } from \"../lib/path.js\";\nimport rxjs, { effect, applyMutation } from \"../lib/rx.js\";\nimport { qs, safe } from \"../lib/dom.js\";\nimport t from \"../locales/index.js\";\n\nimport { AjaxError, ApplicationError } from \"../lib/error.js\";\n\nimport \"../components/icon.js\";\n\nexport default function(render) {\n    let hasBack = window.self === window.top;\n    if (!render) {\n        render = createRender(document.body);\n        try { render = createRender(qs(document.body, \"#app\")); }\n        catch (err) { hasBack = false; }\n    }\n\n    return function(err) {\n        const [msg, trace] = processError(err);\n\n        const shouldRedirectLogin = err instanceof AjaxError && err.err().status === 401;\n        let link = \"\";\n        if (hasBack) {\n            link = forwardURLParams(calculateBacklink(fromHref(window.location.pathname)), [\"share\"]);\n            if (shouldRedirectLogin) {\n                link = fromHref(\"/login?next=\" + encodeURIComponent(forwardURLParams(fromHref(window.location.pathname), [\"share\"])));\n            }\n        }\n        const $page = createElement(`\n            <div>\n                <style>${css}</style>\n                <a href=\"${link}\" class=\"backnav ${!hasBack && \"hidden\"}\">\n                    <component-icon name=\"arrow_left\"></component-icon>\n                    ${t(\"home\")}\n                </a>\n                <div class=\"component_container\">\n                    <div class=\"error-page\">\n                        <h1>${t(\"Oops!\")}</h1>\n                        <h2>${t(safe(msg))}</h2>\n                        <p>\n                            <button class=\"light\" data-bind=\"details\">${t(\"More details\")}</button>\n                            <button class=\"primary\" data-bind=\"refresh\">${t(\"Reload\")}</button>\n                            <pre class=\"hidden\"><code>${formatTrace(trace)}</code></pre>\n                        </p>\n                    </div>\n                </div>\n            </div>\n        `);\n        render($page);\n\n        // feature: show error details\n        effect(rxjs.fromEvent(qs($page, \"button[data-bind=\\\"details\\\"]\"), \"click\").pipe(\n            rxjs.mapTo([\"hidden\"]),\n            applyMutation(qs($page, \"pre\"), \"classList\", \"toggle\")\n        ));\n\n        // feature: refresh button\n        const shouldHideRefreshButton = location.pathname === \"/\";\n        const $refresh = qs($page, \"button[data-bind=\\\"refresh\\\"]\");\n        if (shouldHideRefreshButton) $refresh.remove();\n        else effect(rxjs.fromEvent($refresh, \"click\").pipe(\n            rxjs.tap(() => shouldRedirectLogin ? navigate(link) : location.reload()),\n        ));\n\n        return rxjs.EMPTY;\n    };\n}\n\nfunction processError(err) {\n    let msg, trace;\n    if (err instanceof AjaxError) {\n        msg = t(err.message);\n        trace = `\ntype:    ${err.type()}\ncode:    ${err.code()}\nmessage: ${err.message}\ntrace:   ${err.stack}`;\n    } else if (err instanceof ApplicationError) {\n        msg = t(err.message);\n        trace = `\ntype:  ${err.type()}\ndebug: ${err.debug()}\ntrace: ${err.stack}`;\n    } else {\n        msg = t(\"Internal Error\");\n        trace = `\ntype:    Error\nmessage: ${err.message}\ntrace:   ${err.stack || \"N/A\"}`;\n    }\n    return [msg, trace.trim()];\n}\n\nfunction formatTrace(str) {\n    return str\n        .replaceAll(\"<\", \"&lt;\")\n        .replaceAll(\">\", \"&gt;\")\n        .replaceAll(\" \", \"&nbsp;\")\n        .split(\"\\n\")\n        .map((line) => line.indexOf(\"/lib/vendor/\") === -1 ? line : `<span style=\"opacity:0.25\">${line}</span>`)\n        .join(\"\\n\");\n}\n\nconst css = `\n.error-page {\n  width: 80%;\n  max-width: 600px;\n  margin: 50px auto 0 auto;\n  flex-direction: column;\n}\n.error-page h1 {\n    margin: 5px 0;\n    font-size: 3.1em;\n}\n.error-page h2 {\n    margin: 10px 0;\n    font-weight: normal;\n    font-weight: 100;\n}\n.error-page button {\n    padding-left: 10px;\n    padding-right: 10px;\n    line-height: 1.2rem;\n    text-transform: capitalize;\n}\n.error-page code {\n    margin-top: 5px;\n    display: block;\n    padding: 10px;\n    overflow-x: auto;\n    overflow-y: auto;\n    background: #e2e2e2;\n    color: var(--dark);\n    border-radius: 3px;\n    max-height: 350px;\n}\n.error-page pre {\n    margin: 0;\n}\n.error-page p {\n    font-style: italic;\n    margin-bottom: 5px;\n}\n.error-page a {\n    border-bottom: 1px dashed;\n}\n\n.backnav {\n  display: inline-block;\n  padding: 10px 5px;\n}\n.backnav .component_icon {\n    height: 23px;\n    margin-right: -3px;\n    vertical-align: middle;\n}\n`;\n\nfunction calculateBacklink(pathname = \"\") {\n    let url = \"/\";\n    const listPath = pathname.replace(new RegExp(\"/$\"), \"\").split(\"/\");\n    switch (listPath[1]) {\n    case \"view\": // in view mode, navigate to current folder\n        listPath[1] = \"files\";\n        listPath.pop();\n        url = listPath.join(\"/\") + \"/\";\n        break;\n    case \"files\": // in file browser mode, navigate to parent folder\n        listPath.pop();\n        url = listPath.join(\"/\") + \"/\";\n        break;\n    }\n    return toHref(url === \"/files/\" ? \"/\" : url);\n}\n"
  },
  {
    "path": "public/assets/pages/ctrl_filespage.css",
    "content": ".component_page_filespage {\n    display: flex;\n    flex-direction: column;\n    height: auto;\n    min-height: 100%;\n    width: 100%;\n    background: var(--bg-color);\n    box-sizing: border-box;\n}\nbody:not(.dark-mode) .component_page_filespage {\n    background: linear-gradient(177deg, rgba(250,250,250,0.3) 0%, var(--bg-color) 30%);\n}\n.scroll-y {\n    flex: 1;\n    overflow-y: scroll !important;\n    overflow-x: hidden !important;\n    -webkit-overflow-scrolling: touch;\n}\n"
  },
  {
    "path": "public/assets/pages/ctrl_filespage.js",
    "content": "import { createElement, createRender, onDestroy } from \"../lib/skeleton/index.js\";\nimport { navigate } from \"../lib/skeleton/router.js\";\nimport { qs } from \"../lib/dom.js\";\nimport assert from \"../lib/assert.js\";\nimport { loadCSS } from \"../helpers/loader.js\";\nimport WithShell, { init as initShell } from \"../components/decorator_shell_filemanager.js\";\nimport t from \"../locales/index.js\";\n\nimport componentFilesystem, { init as initFilesystem } from \"./filespage/ctrl_filesystem.js\";\nimport componentSubmenu, { init as initSubmenu } from \"./filespage/ctrl_submenu.js\";\nimport componentNewItem, { init as initNewItem } from \"./filespage/ctrl_newitem.js\";\nimport componentUpload, { init as initUpload } from \"./filespage/ctrl_upload.js\";\nimport { init as initState } from \"./filespage/state_config.js\";\nimport { init as initThing } from \"./filespage/thing.js\";\n\nimport \"../components/breadcrumb.js\";\n\nexport default WithShell(function(render) {\n    if (new RegExp(\"/$\").test(location.pathname) === false) {\n        navigate(location.pathname + \"/\");\n        return;\n    }\n\n    const $page = createElement(`\n        <div class=\"component_page_filespage scroll-y\">\n            <div is=\"component_upload\"></div>\n            <div is=\"component_submenu\"></div>\n            <div is=\"component_newitem\"></div>\n            <div is=\"component_filesystem\"></div>\n        </div>\n    `);\n    render($page);\n\n    // feature1: render the filesystem\n    componentFilesystem(createRender(qs($page, \"[is=\\\"component_filesystem\\\"]\")));\n\n    // feature2: render the menubar\n    componentSubmenu(createRender(qs($page, \"[is=\\\"component_submenu\\\"]\")));\n\n    // feature3: render the creation menu\n    componentNewItem(createRender(qs($page, \"[is=\\\"component_newitem\\\"]\")));\n\n    // feature4: render the upload button\n    componentUpload(createRender(qs($page, \"[is=\\\"component_upload\\\"]\")));\n\n    // feature5: accessibility / skip links\n    const $skip = createElement(`<a aria-role=\"navigation\" href=\"#main\">${t(\"Skip to content\")}</a>`);\n    $skip.onclick = (e) => {\n        e.preventDefault();\n        const $content = document.querySelector(\"main a\");\n        if ($content) assert.type($content, HTMLElement).focus();\n    };\n    document.body.prepend($skip);\n    onDestroy(() => $skip.remove());\n});\n\nexport function init() {\n    return Promise.all([\n        loadCSS(import.meta.url, \"ctrl_filespage.css\"),\n        initShell(), initFilesystem(),\n        initSubmenu(), initNewItem(), initUpload(),\n        initState(), initThing(),\n    ]);\n}\n"
  },
  {
    "path": "public/assets/pages/ctrl_homepage.js",
    "content": "import { createElement, navigate } from \"../lib/skeleton/index.js\";\nimport { toHref } from \"../lib/skeleton/router.js\";\nimport rxjs, { effect } from \"../lib/rx.js\";\nimport { ApplicationError, AjaxError } from \"../lib/error.js\";\nimport { forwardURLParams } from \"../lib/path.js\";\nimport ctrlError from \"./ctrl_error.js\";\n\nimport { getSession } from \"../model/session.js\";\n\nimport \"../components/loader.js\";\n\nexport default function(render) {\n    render(createElement(\"<component-loader></component-loader>\"));\n\n    // feature1: trigger error page via url params\n    const GET = new URLSearchParams(location.search);\n    const err = GET.get(\"error\");\n    if (err) {\n        ctrlError(render)(new ApplicationError(\n            err,\n            GET.get(\"trace\") || \"server error from URL\",\n        ));\n        return;\n    }\n\n    // feature2: redirect user where it makes most sense\n    effect(getSession().pipe(\n        rxjs.catchError((err) => {\n            if (err instanceof AjaxError && err.err().status === 401) {\n                return rxjs.of({ is_authenticated: false });\n            }\n            return rxjs.throwError(err);\n        }),\n        rxjs.tap(({ is_authenticated, home = \"/\" }) => {\n            if (is_authenticated !== true) return navigate(forwardURLParams(toHref(\"/login\"), [\"share\", \"next\"]));\n            return navigate(forwardURLParams(toHref(`/files${home}`), [\"share\"]));\n        }),\n        rxjs.catchError(ctrlError(render)),\n    ));\n};\n"
  },
  {
    "path": "public/assets/pages/ctrl_logout.js",
    "content": "import { navigate } from \"../lib/skeleton/index.js\";\nimport { toHref } from \"../lib/skeleton/router.js\";\nimport rxjs, { effect } from \"../lib/rx.js\";\n\nimport { deleteSession } from \"../model/session.js\";\nimport { init as setup_config, get as getConfig } from \"../model/config.js\";\nimport ctrlError from \"./ctrl_error.js\";\nimport $loader from \"../components/loader.js\";\n\nexport default function(render) {\n    render($loader);\n\n    effect(deleteSession().pipe(\n        rxjs.mergeMap(setup_config),\n        rxjs.tap(() => { while (hooks.length) hooks.pop()(); }),\n        rxjs.tap(() => getConfig(\"logout\") ? location.href = getConfig(\"logout\") : navigate(toHref(\"/\"))),\n        rxjs.catchError(ctrlError(render)),\n    ));\n}\n\nconst hooks = [];\nexport function onLogout(fn) {\n    hooks.push(fn);\n}\n"
  },
  {
    "path": "public/assets/pages/ctrl_notfound.js",
    "content": "import ctrlError from \"./ctrl_error.js\";\nimport { ApplicationError } from \"../lib/error.js\";\n\nexport default function(render) {\n    ctrlError(render)(new ApplicationError(\"Not Found\", \"404 - Not Found\"));\n}\n"
  },
  {
    "path": "public/assets/pages/ctrl_sharepage.css",
    "content": ".component_page_sharelogin {\n    max-width: 300px;\n}\n"
  },
  {
    "path": "public/assets/pages/ctrl_sharepage.js",
    "content": "import { createElement } from \"../lib/skeleton/index.js\";\nimport rxjs, { effect, stateMutation, applyMutation, preventDefault } from \"../lib/rx.js\";\nimport { qs } from \"../lib/dom.js\";\nimport { navigate, toHref } from \"../lib/skeleton/router.js\";\nimport { transition, zoomIn } from \"../lib/animate.js\";\nimport ajax from \"../lib/ajax.js\";\nimport { AjaxError } from \"../lib/error.js\";\nimport { basename } from \"../lib/path.js\";\nimport { loadCSS } from \"../helpers/loader.js\";\nimport { isDir } from \"../pages/filespage/helper.js\";\nimport assert from \"../lib/assert.js\";\nimport t from \"../locales/index.js\";\nimport notification from \"../components/notification.js\";\n\nimport ctrlError from \"./ctrl_error.js\";\n\nexport default function(render) {\n    const shareID = location.pathname.replace(toHref(\"/s/\"), \"\");\n    const state$ = new rxjs.BehaviorSubject({ step: null });\n    const setState = (newState) => state$.next(newState);\n\n    effect(state$.asObservable().pipe(rxjs.mergeMap(({ step, ...state }) => {\n        if (step === null) return verify(render, { shareID, setState, body: null });\n        else if (step === \"password\") return ctrlPassword(render, { shareID, setState });\n        else if (step === \"email\") return ctrlEmail(render, { shareID, setState });\n        else if (step === \"code\") return ctrlEmailCodeVerification(render, { shareID, setState });\n        else if (step === \"done\") {\n            if (isDir(state[\"path\"])) navigate(toHref(`/files/?share=${shareID}`));\n            else navigate(toHref(`/view/${encodeURIComponent(basename(state[\"path\"]))}?share=${shareID}&nav=false`));\n            return rxjs.EMPTY;\n        }\n        else assert.fail(`unknown step: \"${step}\"`);\n    }), rxjs.catchError(ctrlError())));\n}\n\nfunction ctrlPassword(render, { shareID, setState }) {\n    const $page = createElement(`\n        <div class=\"component_container component_page_sharelogin\">\n            <form>\n                <div class=\"input_group\">\n                    <input type=\"password\" name=\"password\" placeholder=\"${t(\"Password\")}\" class=\"component_input\" autocomplete>\n                    <button class=\"transparent\">\n                        <component-icon name=\"arrow_right\"></component-icon>\n                    </button>\n                </div>\n            </form>\n        </div>\n    `);\n    return ctrlAbstract(render, { shareID, setState, $page });\n}\n\nfunction ctrlEmail(render, { shareID, setState }) {\n    const $page = createElement(`\n        <div class=\"component_container component_page_sharelogin\">\n            <form>\n                <div class=\"input_group\">\n                    <input type=\"email\" name=\"email\" placeholder=\"${t(\"Your email address\")}\" class=\"component_input\" autocomplete>\n                    <button class=\"transparent\">\n                        <component-icon name=\"arrow_right\"></component-icon>\n                    </button>\n                </div>\n            </form>\n        </div>\n    `);\n    return ctrlAbstract(render, { shareID, setState, $page });\n}\n\nfunction ctrlEmailCodeVerification(render, { shareID, setState }) {\n    const $page = createElement(`\n        <div class=\"component_container component_page_sharelogin\">\n            <form>\n                <div class=\"input_group\">\n                    <input type=\"text\" name=\"code\" placeholder=\"${t(\"Code\")}\" class=\"component_input\" autocomplete>\n                    <button class=\"transparent\">\n                        <component-icon name=\"arrow_right\"></component-icon>\n                    </button>\n                </div>\n            </form>\n        </div>\n    `);\n    return ctrlAbstract(render, { shareID, setState, $page });\n}\n\nfunction verify(_, { shareID, setState, body }) {\n    return ajax({\n        method: \"POST\",\n        url: `api/share/${shareID}/proof`,\n        responseType: \"json\",\n        body,\n    }).pipe(rxjs.mergeMap(({ responseJSON }) => {\n        const { key = \"\", path } = responseJSON.result;\n        if (key === \"\") setState({ step: \"done\", path });\n        else setState({ step: key });\n\n        return rxjs.of(!(\"error\" in responseJSON.result));\n    }));\n}\n\nexport async function init() {\n    return loadCSS(import.meta.url, \"./ctrl_sharepage.css\");\n}\n\nfunction ctrlAbstract(render, { shareID, setState, $page }) {\n    // feature: nice transition\n    render(transition($page, {\n        timeEnter: 250,\n        enter: zoomIn(1.2),\n        timeLeave: 0,\n    }));\n\n    effect(rxjs.fromEvent(qs($page, \"form\"), \"submit\").pipe(\n        preventDefault(),\n        // STEP1: loading spinner\n        rxjs.mapTo([\"name\", \"loading\"]),\n        applyMutation(qs($page, \"component-icon\"), \"setAttribute\"),\n        // STEP2: attempt to login\n        rxjs.map(() => ({ type: qs($page, \"input\").name, value: qs($page, \"input\").value })),\n        rxjs.switchMap((creds) => verify(render, { shareID, body: creds, setState }).pipe(rxjs.catchError((err) => {\n            if (err instanceof AjaxError) {\n                switch (err.code()) {\n                case \"INTERNAL_SERVER_ERROR\":\n                    return rxjs.throwError(err);\n                case \"FORBIDDEN\":\n                    return rxjs.of(false);\n                }\n            }\n            notification.error(err && err.message);\n            return rxjs.of(false);\n        }))),\n        // STEP3: update the UI when authentication fails, happy path is handle at the middleware\n        //        level one layer above as the login ctrl has no idea what to show after login\n        rxjs.filter((ok) => !ok),\n        rxjs.mapTo([\"name\", \"arrow_right\"]), applyMutation(qs($page, \"component-icon\"), \"setAttribute\"),\n        rxjs.mapTo(\"\"), stateMutation(qs($page, \"input\"), \"value\"),\n        rxjs.mapTo([\"error\"]), applyMutation(qs($page, \".input_group\"), \"classList\", \"add\"),\n        rxjs.delay(300), applyMutation(qs($page, \".input_group\"), \"classList\", \"remove\"),\n        rxjs.catchError(ctrlError(render)),\n    ));\n\n    // feature: autofocus\n    effect(rxjs.of(null).pipe(\n        applyMutation(qs($page, \"input\"), \"focus\")\n    ));\n\n    // feature: vertically center the form\n    effect(rxjs.fromEvent(window, \"resize\").pipe(\n        rxjs.startWith(null),\n        rxjs.map(() => [\"margin-top\", `${Math.floor(window.innerHeight / 3)}px`]),\n        applyMutation($page, \"style\", \"setProperty\")\n    ));\n\n    return rxjs.EMPTY;\n}\n"
  },
  {
    "path": "public/assets/pages/ctrl_viewerpage.css",
    "content": ".component_page_viewerpage {\n    width: 100%;\n    display: flex;\n}\n.component_page_viewerpage > div {\n    width: 100%;\n    display: flex;\n    flex-direction: column;\n}\n"
  },
  {
    "path": "public/assets/pages/ctrl_viewerpage.js",
    "content": "import { createElement, createRender } from \"../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../lib/rx.js\";\nimport { ApplicationError } from \"../lib/error.js\";\nimport { basename } from \"../lib/path.js\";\nimport assert from \"../lib/assert.js\";\nimport { get as getConfig } from \"../model/config.js\";\nimport { loadCSS } from \"../helpers/loader.js\";\nimport WithShell, { init as initShell } from \"../components/decorator_shell_filemanager.js\";\nimport { init as initMenubar } from \"./viewerpage/component_menubar.js\";\nimport { init as initCache } from \"./filespage/cache.js\";\n\nimport ctrlError from \"./ctrl_error.js\";\nimport { getFilename, getDownloadUrl, getCurrentPath } from \"./viewerpage/common.js\";\nimport { opener } from \"./viewerpage/mimetype.js\";\nimport { options } from \"./viewerpage/model_files.js\";\n\nimport \"../components/breadcrumb.js\";\n\nfunction loadModule(appName) {\n    switch (appName) {\n    case \"editor\":\n        return import(\"./viewerpage/application_editor.js\");\n    case \"pdf\":\n        return import(\"./viewerpage/application_pdf.js\");\n    case \"image\":\n        return import(\"./viewerpage/application_image.js\");\n    case \"download\":\n        return import(\"./viewerpage/application_downloader.js\");\n    case \"form\":\n        return import(\"./viewerpage/application_form.js\");\n    case \"audio\":\n        return import(\"./viewerpage/application_audio.js\");\n    case \"video\":\n        return import(\"./viewerpage/application_video.js\");\n    case \"ebook\":\n        return import(\"./viewerpage/application_ebook.js\");\n    case \"3d\":\n        return import(\"./viewerpage/application_3d.js\");\n    case \"appframe\":\n        return import(\"./viewerpage/application_iframe.js\");\n    case \"map\":\n        return import(\"./viewerpage/application_map.js\");\n    case \"url\":\n        return import(\"./viewerpage/application_url.js\");\n    case \"table\":\n        return import(\"./viewerpage/application_table.js\");\n    case \"skeleton\":\n        return import(\"./viewerpage/application_skeleton.js\");\n    default:\n        throw new ApplicationError(\"Internal Error\", `Unknown opener app \"${appName}\" at \"${getCurrentPath()}\"`);\n    }\n};\nconst loadModuleWithMemory = (function() {\n    const memory = {};\n    return function(appName) {\n        if (memory[appName]) return Promise.resolve(memory[appName]);\n        return loadModule(appName).then((module) => {\n            memory[appName] = module;\n            return module;\n        });\n    };\n})();\n\nexport default WithShell(async function(render) {\n    const $page = createElement(`<div class=\"component_page_viewerpage\"></div>`);\n    render($page);\n\n    // feature: render viewer application\n    effect(rxjs.of(getConfig(\"mime\", {})).pipe(\n        rxjs.map((mimes) => opener(basename(getCurrentPath()), mimes)),\n        rxjs.mergeMap(([opener, opts]) => rxjs.from(loadModuleWithMemory(opener)).pipe(rxjs.switchMap(async(module) => {\n            module.default(createRender($page), { ...opts, acl$: options(), getFilename, getDownloadUrl });\n        }))),\n        rxjs.catchError(ctrlError()),\n    ));\n\n    // feature: cleanup up the design when navbar is not there\n    effect(rxjs.of(new URL(location.toString()).searchParams.get(\"nav\")).pipe(\n        rxjs.filter((value) => value === \"false\"),\n        rxjs.tap(() => {\n            const $parent = assert.truthy($page.parentElement);\n            $parent.style.border = \"none\";\n            $parent.style.borderRadius = \"0\";\n        }),\n    ));\n});\n\nexport async function init() {\n    return Promise.all([\n        loadCSS(import.meta.url, \"./ctrl_viewerpage.css\"),\n        initShell(), initMenubar(), initCache(),\n        rxjs.of(getConfig(\"mime\", {})).pipe(\n            rxjs.map((mimes) => opener(basename(getCurrentPath()), mimes)),\n            rxjs.mergeMap(([opener]) => loadModule(opener)),\n            rxjs.mergeMap((module) => typeof module.init === \"function\"? module.init() : rxjs.EMPTY),\n            rxjs.catchError(() => rxjs.EMPTY),\n        ).toPromise(),\n    ]);\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/cache.js",
    "content": "import assert from \"../../lib/assert.js\";\nimport { getSession } from \"../../model/session.js\";\nimport { onLogout } from \"../ctrl_logout.js\";\n\nclass ICache {\n    /**\n     * @param {string} _path\n     * @return {Promise<any>}\n     */\n    async get(_path) { throw new Error(\"NOT_IMPLEMENTED\"); }\n\n    /**\n     * @param {string} _path\n     * @param {any} _data\n     * @return {Promise<void>}\n     */\n    async store(_path, _data) { throw new Error(\"NOT_IMPLEMENTED\"); }\n\n    /**\n     * @return {Promise<void>}\n     */\n    async remove() { throw new Error(\"NOT_IMPLEMENTED\"); }\n\n    /**\n     * @param {string} path\n     * @param {function(any): any} fn\n     * @return {Promise<void>}\n     */\n    async update(path, fn) {\n        const data = await this.get(path);\n        return this.store(path, fn(data || {}));\n    }\n\n    /**\n     * @return {Promise<void>}\n     */\n    async destroy() { throw new Error(\"NOT_IMPLEMENTED\"); }\n}\n\nclass InMemoryCache extends ICache {\n    constructor() {\n        super();\n        this.data = {};\n    }\n\n    /**\n     * @override\n     */\n    async get(path) {\n        return this.data[this._key(path)] || null;\n    }\n\n    /**\n     * @override\n     */\n    async store(path, obj) {\n        this.data[this._key(path)] = obj;\n    }\n\n    /**\n     * @override\n     */\n    async remove(path, exact = true) {\n        if (!path) {\n            this.data = {};\n            return;\n        }\n        const key = this._key(path);\n        if (exact) {\n            delete this.data[key];\n            return;\n        }\n        for (const k in this.data) {\n            if (k.indexOf(key) === 0) {\n                delete this.data[k];\n            }\n        }\n    }\n\n    /**\n     * @override\n     */\n    async destroy() {\n        this.data = {};\n    }\n\n    _key(path) {\n        return currentBackend() + \"::\" + currentShare() + \"::\" + path;\n    }\n};\n\nclass IndexDBCache extends ICache {\n    DB_VERSION = 5;\n    FILE_PATH = \"file_path\";\n    /** @type {Promise<IDBDatabase> | null} */ db = null;\n\n    constructor() {\n        super();\n\n        const request = indexedDB.open(\"filestash\", this.DB_VERSION);\n        request.onupgradeneeded = this._migration.bind(this);\n\n        this.db = new Promise((done, err) => {\n            request.onsuccess = (e) => {\n                done(assert.truthy(e.target).result);\n            };\n            request.onerror = () => err(new Error(\"INDEXEDDB_NOT_SUPPORTED\"));\n        });\n    }\n\n    /**\n     * @override\n     */\n    async get(path) {\n        const db = assert.truthy(await this.db);\n        const tx = db.transaction(this.FILE_PATH, \"readonly\");\n        const store = tx.objectStore(this.FILE_PATH);\n        const query = store.get(this._key(path));\n        return await new Promise((done) => {\n            query.onsuccess = () => done(query.result || null);\n            query.onerror = () => done(null);\n        });\n    }\n\n    /**\n     * @override\n     */\n    async store(path, value = {}) {\n        const db = assert.truthy(await this.db);\n        const tx = db.transaction(this.FILE_PATH, \"readwrite\");\n        const store = tx.objectStore(this.FILE_PATH);\n\n        const request = store.put({\n            ...value,\n            backend: currentBackend(),\n            share: currentShare(),\n            path,\n        });\n        return await new Promise((done, error) => {\n            done(value);\n            request.onsuccess = () => done(value);\n            request.onerror = error;\n        });\n    }\n\n    /**\n     * @override\n     */\n    async remove(path, exact = true) {\n        const db = assert.truthy(await this.db);\n        const tx = db.transaction(this.FILE_PATH, \"readwrite\");\n        const store = tx.objectStore(this.FILE_PATH);\n        const key = this._key(path);\n\n        if (exact !== true) {\n            const request = store.openCursor(IDBKeyRange.bound(\n                [key[0], key[1], key[2]],\n                [key[0], key[1], key[2]+\"\\u{FFFF}\".repeat(5000)],\n                true, true,\n            ));\n            await new Promise((done, err) => {\n                request.onsuccess = function(event) {\n                    const cursor = event.target.result;\n                    if (cursor) {\n                        cursor.delete([key[0], key[1], cursor.value.path]);\n                        cursor.continue();\n                        return;\n                    }\n                    done(null);\n                };\n                request.onerror = err;\n            });\n        }\n\n        const req = store.delete(key);\n        return await new Promise((done, err) => {\n            req.onsuccess = () => done(null);\n            req.onerror = err;\n        });\n    }\n\n    _key(path) {\n        return [currentBackend(), currentShare(), path];\n    }\n\n    _migration(event) {\n        const db = event.target.result;\n        if (event.oldVersion === 1) {\n            // we've change the schema on v2 adding an index, let's flush\n            // to make sure everything will be fine\n            db.deleteObjectStore(\"file_path\");\n            db.deleteObjectStore(\"file_content\");\n        } else if (event.oldVersion === 2) {\n            // we've change the primary key to be a (path,share)\n            db.deleteObjectStore(\"file_path\");\n            db.deleteObjectStore(\"file_content\");\n        } else if (event.oldVersion === 3) {\n            // we've added a FILE_TAG to store tag related data and update\n            // keyPath to have \"backend\"\n            db.deleteObjectStore(\"file_path\");\n            db.deleteObjectStore(\"file_content\");\n        } else if (event.oldVersion === 4) {\n            // we got rid of the idea of offline first file manager so let's get rid of\n            // FILE_CONTENT and FILE_TAG\n            db.deleteObjectStore(\"file_path\");\n            db.deleteObjectStore(\"file_content\");\n            db.deleteObjectStore(\"file_tag\");\n        }\n        const store = db.createObjectStore(this.FILE_PATH, { keyPath: [\"backend\", \"share\", \"path\"] });\n        store.createIndex(\"idx_path\", [\"backend\", \"share\", \"path\"], { unique: true });\n    }\n}\n\nlet cache = null;\n\nexport async function clearCache(path) { // TODO: remove useless function\n    await cache.remove(path, false);\n}\n\nexport async function init() {\n    const setup_cache = () => {\n        cache = new InMemoryCache();\n        if (!(\"indexedDB\" in window)) return;\n        else if (window.self !== window.top) return;\n\n        cache = assert.truthy(new IndexDBCache());\n        return cache.db.catch((err) => {\n            if (err.message === \"INDEXEDDB_NOT_SUPPORTED\") {\n                // Firefox in private mode act like if it supports indexedDB but\n                // is throwing that string as an error if you try to use it ...\n                // so we fallback with our basic ram cache\n                cache = new InMemoryCache();\n                return;\n            }\n            throw err;\n        });\n    };\n    const setup_session = async() => {\n        if (!backendID) {\n            try {\n                const session = await getSession().toPromise();\n                backendID = session.backendID;\n                onLogout(() => backendID = \"\");\n            } catch (err) {}\n        }\n    };\n    return Promise.all([setup_cache(), setup_session()]);\n}\n\nexport default function() {\n    return cache;\n};\n\nlet backendID = \"\";\nexport function currentBackend() {\n    return backendID;\n}\nexport function updateBackend(id) {\n    backendID = id;\n}\n\nexport function currentShare() {\n    return new window.URL(location.href).searchParams.get(\"share\") || \"\";\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/ctrl_filesystem.css",
    "content": "[is=\"component_filesystem\"] .component_filesystem {\n    padding-top: 3px;\n}\n[is=\"component_filesystem\"] .component_filesystem [data-target=\"dragselect\"] {\n    position: fixed;\n    border-radius: 2px;\n    z-index: 3;\n    background: var(--primary);\n    border: 2px solid rgba(0, 0, 0, 0.1);\n    opacity: 0.25;\n    width: 500px;\n    height: 100px;\n}\n[is=\"component_filesystem\"] .component_filesystem [data-type=\"list\"] {\n    grid-gap: 2px;\n}\n[is=\"component_filesystem\"] .component_filesystem [data-type=\"grid\"] {\n    grid-gap: 5px;\n}\n@media screen and (min-width: 1100px) {\n    .component_filemanager_shell [data-bind=\"sidebar\"]:not(.hidden) ~ div\n    [is=\"component_filesystem\"] .component_filesystem [data-type=\"grid\"] {\n        grid-gap: 8px;\n    }\n}\n[is=\"component_filesystem\"] .empty {\n    text-align: center;\n    font-weight: 100;\n\n    margin: 0 auto;\n    opacity: 0.9;\n    font-size: 1.2rem;\n    color: var(--light);\n    height: 100%;\n    opacity: 0.8;\n    font-family: sans-serif;\n\n    padding: 50px 0;\n    @media screen and (max-width: 420px) {\n        padding: 30px;\n    }\n}\n[is=\"component_filesystem\"] .empty .component_icon {\n    height: 200px;\n    max-width: 100%;\n}\n[is=\"component_filesystem\"] .empty .label {\n    margin-top: -40px;\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/ctrl_filesystem.js",
    "content": "import { createElement, createRender, onDestroy } from \"../../lib/skeleton/index.js\";\nimport { animate, slideYIn } from \"../../lib/animate.js\";\nimport rxjs, { effect, preventDefault } from \"../../lib/rx.js\";\nimport assert from \"../../lib/assert.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport { ApplicationError } from \"../../lib/error.js\";\nimport { createLoader } from \"../../components/loader.js\";\nimport t from \"../../locales/index.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport { currentPath, sort, isMobile, isAlreadyFocused } from \"./helper.js\";\nimport { createThing } from \"./thing.js\";\nimport { clearSelection, addSelection, getSelection$, isSelected } from \"./state_selection.js\";\nimport { getState$ } from \"./state_config.js\";\nimport { ls, search } from \"./model_files.js\";\nimport { getPermission } from \"./model_acl.js\";\n\nconst ICONS = {\n    EMPTY_FILES: \"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMzAwIiBoZWlnaHQ9IjE3MCIgdmlld0JveD0iMCAwIDMwMCAxNzAiIGZpbGw9Im5vbmUiPgogIDxwYXRoCiAgICAgZD0ibSA1Mi42Mjk5MDUsMTYwLjE2Nzg1IGMgMS41NDYsMCAyLjgsLTEuMjUzNiAyLjgsLTIuOCAwLC0xLjU0NjQgLTEuMjU0LC0yLjggLTIuOCwtMi44IC0xLjU0NywwIC0yLjgsMS4yNTM2IC0yLjgsMi44IDAsMS41NDY0IDEuMjUzLDIuOCAyLjgsMi44IHoiCiAgICAgZmlsbD0iIzkwOTA5MCIKICAgICBpZD0icGF0aDUwNjciCiAgICAgc3R5bGU9ImZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4xMzMzMzMiIC8+CiAgPHBhdGgKICAgICBkPSJtIDExMy4wMzAxOCwyMi4zOTM2NDkgYyAxLjU0NjQsMCAyLjgsLTEuMjUzNiAyLjgsLTIuOCAwLC0xLjU0NjQgLTEuMjUzNiwtMi44IC0yLjgsLTIuOCAtMS41NDY0LDAgLTIuOCwxLjI1MzYgLTIuOCwyLjggMCwxLjU0NjQgMS4yNTM2LDIuOCAyLjgsMi44IHoiCiAgICAgZmlsbD0iIzkwOTA5MCIKICAgICBpZD0icGF0aDUwNjkiCiAgICAgc3R5bGU9ImZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4xMzMzMzMiIC8+CiAgPHBhdGgKICAgICBkPSJtIDczLjI5MzU2NSwxMTIuNDYyMTYgYyAyLjg3MTg5LDAgNS4yMDAwMywtMi4zMjgxIDUuMjAwMDMsLTUuMiAwLC0yLjg3MTkgLTIuMzI4MTQsLTUuMiAtNS4yMDAwMywtNS4yIC0yLjg3MTg4LDAgLTUuMTk5OTk1LDIuMzI4MSAtNS4xOTk5OTUsNS4yIDAsMi44NzE5IDIuMzI4MTE1LDUuMiA1LjE5OTk5NSw1LjIgeiIKICAgICBmaWxsPSIjOTA5MDkwIgogICAgIGlkPSJwYXRoNTA3MSIKICAgICBzdHlsZT0iZmlsbDojOTA5MDkwO2ZpbGwtb3BhY2l0eTowLjEzMzMzMzM0IiAvPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM1MTE3Ij4KICAgIDxmaWx0ZXIKICAgICAgIGlkPSJmaWx0ZXIwX2QiCiAgICAgICB4PSIxNi4wNzYyIgogICAgICAgeT0iMTIuOTU3NSIKICAgICAgIHdpZHRoPSIxMTQuOCIKICAgICAgIGhlaWdodD0iMTM0LjYwMDAxIgogICAgICAgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KICAgICAgPGZlRmxvb2QKICAgICAgICAgZmxvb2Qtb3BhY2l0eT0iMCIKICAgICAgICAgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiCiAgICAgICAgIGlkPSJmZUZsb29kNTA5NyIgLz4KICAgICAgPGZlQ29sb3JNYXRyaXgKICAgICAgICAgaW49IlNvdXJjZUFscGhhIgogICAgICAgICB0eXBlPSJtYXRyaXgiCiAgICAgICAgIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMTI3IDAiCiAgICAgICAgIGlkPSJmZUNvbG9yTWF0cml4NTA5OSIgLz4KICAgICAgPGZlT2Zmc2V0CiAgICAgICAgIGR5PSIxMSIKICAgICAgICAgaWQ9ImZlT2Zmc2V0NTEwMSIgLz4KICAgICAgPGZlR2F1c3NpYW5CbHVyCiAgICAgICAgIHN0ZERldmlhdGlvbj0iMTEiCiAgICAgICAgIGlkPSJmZUdhdXNzaWFuQmx1cjUxMDMiIC8+CiAgICAgIDxmZUNvbG9yTWF0cml4CiAgICAgICAgIHR5cGU9Im1hdHJpeCIKICAgICAgICAgdmFsdWVzPSIwIDAgMCAwIDAuMzk3NzA4IDAgMCAwIDAgMC40Nzc0OSAwIDAgMCAwIDAuNTc1IDAgMCAwIDAuMjcgMCIKICAgICAgICAgaWQ9ImZlQ29sb3JNYXRyaXg1MTA1IiAvPgogICAgICA8ZmVCbGVuZAogICAgICAgICBtb2RlPSJub3JtYWwiCiAgICAgICAgIGluMj0iQmFja2dyb3VuZEltYWdlRml4IgogICAgICAgICByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvdyIKICAgICAgICAgaWQ9ImZlQmxlbmQ1MTA3IiAvPgogICAgICA8ZmVCbGVuZAogICAgICAgICBtb2RlPSJub3JtYWwiCiAgICAgICAgIGluPSJTb3VyY2VHcmFwaGljIgogICAgICAgICBpbjI9ImVmZmVjdDFfZHJvcFNoYWRvdyIKICAgICAgICAgcmVzdWx0PSJzaGFwZSIKICAgICAgICAgaWQ9ImZlQmxlbmQ1MTA5IiAvPgogICAgPC9maWx0ZXI+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJwYWludDBfbGluZWFyIgogICAgICAgeDE9IjczLjQ1MzEwMiIKICAgICAgIHkxPSIyMS44NjE5IgogICAgICAgeDI9IjczLjQ1MzEwMiIKICAgICAgIHkyPSIxMTUuNTM0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjRkRGRUZGIgogICAgICAgICBpZD0ic3RvcDUxMTIiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIG9mZnNldD0iMC45OTY0IgogICAgICAgICBzdG9wLWNvbG9yPSIjRUNGMEY1IgogICAgICAgICBpZD0ic3RvcDUxMTQiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGZpbHRlcgogICAgICAgaWQ9ImZpbHRlcjBfZC03IgogICAgICAgeD0iMC4zOTExMTMwMSIKICAgICAgIHk9IjM1LjM5NDc5OCIKICAgICAgIHdpZHRoPSIxNDUuNTk1IgogICAgICAgaGVpZ2h0PSIxMDIuOCIKICAgICAgIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CiAgICAgIDxmZUZsb29kCiAgICAgICAgIGZsb29kLW9wYWNpdHk9IjAiCiAgICAgICAgIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4IgogICAgICAgICBpZD0iZmVGbG9vZDEyMjciIC8+CiAgICAgIDxmZUNvbG9yTWF0cml4CiAgICAgICAgIGluPSJTb3VyY2VBbHBoYSIKICAgICAgICAgdHlwZT0ibWF0cml4IgogICAgICAgICB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIgogICAgICAgICBpZD0iZmVDb2xvck1hdHJpeDEyMjkiIC8+CiAgICAgIDxmZU9mZnNldAogICAgICAgICBkeT0iMTEiCiAgICAgICAgIGlkPSJmZU9mZnNldDEyMzEiIC8+CiAgICAgIDxmZUdhdXNzaWFuQmx1cgogICAgICAgICBzdGREZXZpYXRpb249IjExIgogICAgICAgICBpZD0iZmVHYXVzc2lhbkJsdXIxMjMzIiAvPgogICAgICA8ZmVDb2xvck1hdHJpeAogICAgICAgICB0eXBlPSJtYXRyaXgiCiAgICAgICAgIHZhbHVlcz0iMCAwIDAgMCAwLjM5NzcwOCAwIDAgMCAwIDAuNDc3NDkgMCAwIDAgMCAwLjU3NSAwIDAgMCAwLjI3IDAiCiAgICAgICAgIGlkPSJmZUNvbG9yTWF0cml4MTIzNSIgLz4KICAgICAgPGZlQmxlbmQKICAgICAgICAgbW9kZT0ibm9ybWFsIgogICAgICAgICBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIKICAgICAgICAgcmVzdWx0PSJlZmZlY3QxX2Ryb3BTaGFkb3ciCiAgICAgICAgIGlkPSJmZUJsZW5kMTIzNyIgLz4KICAgICAgPGZlQmxlbmQKICAgICAgICAgbW9kZT0ibm9ybWFsIgogICAgICAgICBpbj0iU291cmNlR3JhcGhpYyIKICAgICAgICAgaW4yPSJlZmZlY3QxX2Ryb3BTaGFkb3ciCiAgICAgICAgIHJlc3VsdD0ic2hhcGUiCiAgICAgICAgIGlkPSJmZUJsZW5kMTIzOSIgLz4KICAgIDwvZmlsdGVyPgogIDwvZGVmcz4KICA8cGF0aAogICAgIGQ9Im0gMjEuMjIwODMyLDkyLjI2NDAxNiBoIC00LjkgYyAtMC4zLDAgLTAuNiwwLjEgLTAuNywwLjQgbCAtMi40LDQuMyBjIC0wLjEsMC4zIC0wLjEsMC42IDAsMC45IGwgMi40LDQuMzAwMDA0IGMgMC4xLDAuMyAwLjQsMC40IDAuNywwLjQgaCA0LjkgYyAwLjMsMCAwLjYsLTAuMSAwLjcsLTAuNCBsIDIuNCwtNC4zMDAwMDQgYyAwLjEsLTAuMyAwLjEsLTAuNiAwLC0wLjkgbCAtMi40LC00LjMgYyAtMC4xLC0wLjMgLTAuNCwtMC40IC0wLjcsLTAuNCB6IgogICAgIGlkPSJwYXRoNCIKICAgICBzdHlsZT0iZmlsbDojOTA5MDkwO2ZpbGwtb3BhY2l0eTowLjEzMzMzMyIgLz4KICA8cGF0aAogICAgIGNsYXNzPSJmaWxlIgogICAgIGQ9Im0gMjcxLjA5MjU5LDExLjY1MjMxMiAtNy4zNzAxOSwzLjU4MzkxOCBjIC0wLjc2MTk0LDAuNDMxMTE2IC0xLjcwNDkxLDAuNTgzNTAxIC0yLjU3MjQ2LDAuNjA1MjIyIC0wLjg2NzU0LDAuMDIxNzIgLTEuNzg3OTEsLTAuMTYxMjQ0IC0yLjYzMjg1LC0wLjQ3NDg2NyAtMS42MTQ0NCwtMC43NTc4OTggLTMuMDU1NCwtMi4xMTI0NDIgLTMuOTM4MTUsLTMuODQxNTA5IDAsMCAtMS41MDg5NiwtMy4zMTAwMzcyIC0zLjA3MDc1LC02LjgyNDc3NDUgbCAtMC4yMzM4OCwtMC40ODM0MzY4IC0xNS45NjI1Myw3LjUwNzQ2OTMgYyAtMi41NDIyMywxLjE0NTI3MSAtMy42MTMyOSw0LjE4NTE0NCAtMi41MTkyOCw2LjczMjk5OCBsIDExLjUyODU1LDI1LjY0NDA3NCBjIDEuMDk0MDIsMi41NDc4NTUgNC4wODksMy41ODAyMDUgNi42MzEyNSwyLjQzNDkzMSBsIDI1LjU5NTg0LC0xMi4wNDk0MDIgYyAyLjU0MjI0LC0xLjE0NTI3NSAzLjYxMzMxLC00LjE4NTE0MyAyLjUxOTMsLTYuNzMyOTk4IEwgMjcxLjcwMzY1LDExLjQ4MjQ5IFoiCiAgICAgc3R5bGU9ImNsaXAtcnVsZTpldmVub2RkO2ZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4xMzMzMzMzNDtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2Utd2lkdGg6MS40OTQ3MSIKICAgICBpZD0icGF0aDYiIC8+CiAgPHBhdGgKICAgICBjbGFzcz0iZmlsZSIKICAgICBkPSJtIDI1OS40MTQ2OCwxMi45MjMzNjggYyAtMS4xMDE0NCwtMC40NjE3MTMgLTEuOTc2NTksLTEuMzE1MzggLTIuNjI1NDMsLTIuNTYxMDA1IGwgLTAuMDUyOCwtMC4yMDQ3IC0wLjI4NjY4LC0wLjY4ODE0NzIgLTEuMDQxMTksLTIuMzQzMTU4IC0wLjU3MzQxLC0xLjM3NjI3NiAxMy4wMDU5OSw0LjcyMTc3MDIgLTUuMTU5OTMsMi40MjEyMDIgYyAtMC40ODI3NywwLjI0Mzg2NiAtMS4wMTg0MSwwLjI4MzAyOCAtMS41NTQwMSwwLjMyMjE5NiAtMC41MzU2NCwwLjAzOTE2IC0xLjEyNDA2LC0wLjEyNjM2NCAtMS43MTI0OSwtMC4yOTE4OTkgeiIKICAgICBzdHlsZT0iY2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojOTA5MDkwO2ZpbGwtb3BhY2l0eTowLjEzMzMzMzM0O2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZS13aWR0aDoxLjQ5NDcxIgogICAgIGlkPSJwYXRoOCIgLz4KICA8cGF0aAogICAgIGQ9Im0gMjMuOTQ0NDAzLDE3LjYzNTU2MyBjIC0yLjA3NzQxOSwxLjE5OTQxNSAtMi40NzQ2NjksMy44MzI3OTggLTEuMzAzNTc5LDUuODYxMjAyIGwgMTEuMTU3OTY5LDE5LjMyNjEzMyBjIDEuMjY4NjksMi4xOTc0MzUgMy41MzE5NiwyLjkxOTEzNSA1LjY2MjY0LDEuNjg4OTc1IGwgMjQuMzQzMTIsLTE0LjA1NDQ5MSBjIDEuMzg0OTYsLTAuNzk5NjEgMS45MjQxMSwtMy4wNjQxNjkgMC42MjI4OCwtNS4zMTc5MzkgTCA1NC45Mjg1NDMsOC42ODY4NjI3IGMgLTEuMDQwOTksLTEuODAzMDExIC0zLjA3NjU0LC0yLjEzMDMxMyAtNC40NjE0NiwtMS4zMzA3MzQgbCAtMTIuNTE3ODIsNy4yMjcxNjkzIC00Ljk0OTQ0LC0yLjE3NTg2OSB6IgogICAgIGlkPSJwYXRoMTYiCiAgICAgc3R5bGU9ImZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4xMzMzMzMzNDtzdHJva2Utd2lkdGg6MC42MzI1OTQiIC8+CiAgPGcKICAgICBpZD0iZzIwIgogICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI0My4yODEwNCwxMzEuMDczNDYpIgogICAgIHN0eWxlPSJmaWxsOiM5MDkwOTA7ZmlsbC1vcGFjaXR5OjAuMTMzMzMzMzQiPgogICAgPHBhdGgKICAgICAgIGQ9Im0gNDIuNCwyMy4yIDguNiw5LjkgYyAwLjMsMC4zIDAuMSwwLjggLTAuMywwLjkgTCAzOCwzNi4yIGMgLTAuNCwwLjEgLTAuNywtMC4zIC0wLjYsLTAuNyBsIDQuMSwtMTIuMSBjIDAuMSwtMC40IDAuNywtMC41IDAuOSwtMC4yIHoiCiAgICAgICBpZD0icGF0aDE4IgogICAgICAgc3R5bGU9ImZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4xMzMzMzMzNCIgLz4KICA8L2c+CiAgPHBhdGgKICAgICBkPSJtIDI4Ni4wODgzMiw4NS43MTYxIC0yLDEgYyAtMC4zLDAuMSAtMC42LDAgLTAuOCwtMC4zIGwgLTEsLTIgYyAtMC4xLC0wLjMgMCwtMC42IDAuMywtMC44IGwgMiwtMSBjIDAuMywtMC4xIDAuNiwwIDAuOCwwLjMgbCAxLDIgYyAwLjEsMC4zIDAsMC43IC0wLjMsMC44IHoiCiAgICAgaWQ9InBhdGgxMCIKICAgICBzdHlsZT0iZmlsbDojOTA5MDkwO2ZpbGwtb3BhY2l0eTowLjEzMzMzMyIgLz4KICA8cGF0aAogICAgIGQ9Im0gMTExLjM1ODEsNjcuODkzNDgzIGggNzIuMDk5NyBjIDIuOSwwIDUuMSwyLjIgNS4xLDUuMSB2IDQ1LjY5OTk5NyBjIDAsMi45IC0yLjIsNS4xIC01LjEsNS4xIGggLTcyLjA5OTcgYyAtMi45LDAgLTUuMSwtMi4yIC01LjEsLTUuMSBWIDcyLjk5MzQ4MyBjIDAsLTIuOSAyLjQsLTUuMSA1LjEsLTUuMSB6IgogICAgIGZpbGw9IiM5MDkwOTAiCiAgICAgaWQ9InBhdGgxMTk5IiAvPgogIDxnCiAgICAgZmlsdGVyPSJ1cmwoI2ZpbHRlcjBfZCkiCiAgICAgaWQ9ImcxMjAzIgogICAgIHN0eWxlPSJmaWx0ZXI6dXJsKCNmaWx0ZXIwX2QtNykiCiAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzYuNDY2OCwzMS4xOTg2ODMpIj4KICAgIDxwYXRoCiAgICAgICBkPSJNIDExNy42OTEsNDYuNDk0OCBIIDg3LjI5MTEgYyAtMywwIC01LjgsMSAtOC4xLDIuOSBsIC04LDYuNSBjIC0yLjIsMS44IC01LjEsMi45IC04LjEsMi45IGggLTM0LjQgYyAtMy41LDAgLTYuMywyLjkgLTYuMyw2LjMgMCwwLjMgMCwwLjYgMC4xLDAuOSBsIDYuMywzMy43IGMgMC41LDMuMjAwMiAzLjIsNS41MDAyIDYuMyw1LjUwMDIgaCA3My41OTk5IGMgMy4yLDAgNS44LC0yLjIgNi4zLC01LjQwMDIgbCA4LjksLTQ2LjEgYyAwLjYsLTMuNSAtMS43LC02LjYgLTUuMiwtNy4zIC0wLjMsMC4xIC0wLjcsMC4xIC0xLDAuMSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIGlkPSJwYXRoMTIwMSIgLz4KICA8L2c+CiAgPHBhdGgKICAgICBkPSJtIDEzNS4zNTgxLDExMi41OTM1OCBjIDEuOCwwIDMuMywtMS41IDMuMywtMy4zIDAsLTEuOCAtMS41LC0zLjMgLTMuMywtMy4zIC0xLjgsMCAtMy4zLDEuNSAtMy4zLDMuMyAwLDEuOCAxLjUsMy4zIDMuMywzLjMgeiIKICAgICBmaWxsPSIjOTA5MDkwIgogICAgIGlkPSJwYXRoMTIwNSIgLz4KICA8cGF0aAogICAgIGQ9Im0gMTYxLjA1ODEsMTEyLjQ5MzQ4IGMgMS44LDAgMy4zLC0xLjUgMy4zLC0zLjMgMCwtMS44IC0xLjUsLTMuMyAtMy4zLC0zLjMgLTEuOCwwIC0zLjMsMS41IC0zLjMsMy4zIDAsMS45IDEuNSwzLjMgMy4zLDMuMyB6IgogICAgIGZpbGw9IiM5MDkwOTAiCiAgICAgaWQ9InBhdGgxMjA3IiAvPgogIDxwYXRoCiAgICAgZD0ibSAxNDQuMTU4LDg4Ljg5MzQ4MSBjIC0zLjYsLTcgLTQuNCwtMTUuMzk5OTk4IC0yLC0yMi45OTk5OTggMi4zLC03LjYgNy44LC0xNC4xIDE0LjYsLTE3LjggMi4xLC0xLjEgNC41LC0yIDYuOSwtMi4xIDIuNCwtMC4xIDUsMC43IDYuNiwyLjcgMS42LDEuOCAxLjksNC44IDAuNiw2LjggLTEuNCwxLjkgLTQuMiwyLjcgLTYuNSwyLjEgLTMuNywtMC43IC02LjcsLTMuNiAtNy42LC03LjEgLTAuOSwtMy41IDAuMywtNy42IDMuMSwtOS45IDEuOCwtMS42IDQuMywtMi41IDYuNiwtMy4yIDExLjE5OTgsLTMuMyAyMy4zOTk4LC0zLjcgMzQuNzk5OCwtMS4yIgogICAgIHN0cm9rZT0iIzU3NTk1YSIKICAgICBzdHJva2Utd2lkdGg9IjIiCiAgICAgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIgogICAgIHN0cm9rZS1kYXNoYXJyYXk9IjQsIDQiCiAgICAgaWQ9InBhdGgxMjA5IiAvPgogIDxwYXRoCiAgICAgZD0ibSAxNTEuMzU4LDExNi4wOTM0OCBoIC02LjIgdiAxLjUgaCA2LjIgeiIKICAgICBmaWxsPSIjOTA5MDkwIgogICAgIGlkPSJwYXRoMTIxMSIgLz4KICA8cGF0aAogICAgIGQ9Im0gMjA3LjI1NzgsMzMuNTkzNDMzIGMgLTAuMSwxLjUgLTAuMiwyLjkgLTEuMywzLjIgLTEuMSwwLjMgLTEuNiwtMC43IC0yLjMsLTIuMSAtMC43LC0xLjMgLTAuMywtMi42OTk5OTYgMC45LC0yLjk5OTk5NiAxLjEsLTAuMyAyLjksMC4xIDIuNywxLjg5OTk5NiB6IgogICAgIGZpbGw9IiM5MDkwOTAiCiAgICAgaWQ9InBhdGgxMjEzIiAvPgogIDxwYXRoCiAgICAgZD0ibSAyMDYuMDU3OCw0MC43OTM0NTMgYyAwLjMsLTEuOCAwLjYsLTIuOCAtMC40LC0zLjMgLTEuMSwtMC41IC0xLjgsMC40IC0zLDEuNiAtMSwxLjEgLTAuNCwyLjcwMDAzIDAuNiwzLjIwMDAzIDEuMiwwLjYgMi41LDAgMi44LC0xLjUwMDAzIHoiCiAgICAgZmlsbD0iIzkwOTA5MCIKICAgICBpZD0icGF0aDEyMTUiIC8+CiAgPHBhdGgKICAgICBkPSJtIDIwNy40NTc4LDM3LjQ5MzUzMyBjIC0wLjEsMC43IC0wLjYsMS4yIC0xLjMsMS4zIC0wLjMsMCAtMC42LDAgLTEsMCAtMS40LC0wLjIgLTIuNSwtMS4xIC0yLjQsLTIgMC4xLC0wLjkgMS40LC0xLjQgMywtMS4yIDAuMywwIDAuNiwwLjEgMC44LDAuMiAwLjYsMC4yIDEsMC45IDAuOSwxLjcgMCwwIDAsLTAuMSAwLDAgeiIKICAgICBmaWxsPSIjNTc1OTVhIgogICAgIGlkPSJwYXRoMTIxNyIgLz4KICA8cGF0aAogICAgIGQ9Im0gOTUuODU3OSw2NS41OTM0ODMgYyAwLC0xLjcgMCwtMy40IDEuMiwtMy45IDEuMywtMC41IDIsMC43IDMsMi40IDAuOSwxLjUgMC41LDMuMSAtMC44LDMuNiAtMS4xLDAuNSAtMy40LDAuMiAtMy40LC0yLjEgeiIKICAgICBmaWxsPSIjOTA5MDkwIgogICAgIGlkPSJwYXRoMTIxOSIgLz4KICA8cGF0aAogICAgIGQ9Im0gOTYuNTU3OSw1Ny4xOTM1ODMgYyAtMC4yLDIuMSAtMC41LDMuMyAwLjgsMy44IDEuMywwLjUgMiwtMC42IDMuMywtMi4yIDEsLTEuNCAwLjMsLTMuMiAtMSwtMy43IC0xLjMsLTAuNSAtMi45LDAuNCAtMy4xLDIuMSB6IgogICAgIGZpbGw9IiM5MDkwOTAiCiAgICAgaWQ9InBhdGgxMjIxIiAvPgogIDxwYXRoCiAgICAgZD0ibSA5NS40NTgsNjEuMTkzNTgzIGMgMCwtMC44IDAuNiwtMS40IDEuMywtMS41IDAuMywtMC4xIDAuNywtMC4xIDEuMSwwIDEuNiwwLjEgMywxIDIuOSwyIC0wLjEsMSAtMS40LDEuNyAtMy4xLDEuNSAtMC4zLDAgLTAuNiwtMC4xIC0wLjksLTAuMiAtMC44LC0wLjEgLTEuMywtMC45IC0xLjMsLTEuOCB6IgogICAgIGZpbGw9IiM1NzU5NWEiCiAgICAgaWQ9InBhdGgxMjIzIiAvPgogIDxwYXRoCiAgICAgZD0ibSAxMDIuMjU4MSw2MS40OTM1ODMgYyAxMC41LDAgMjkuOSw2LjEgMzAuMiwyOC40OTk5OTgiCiAgICAgc3Ryb2tlPSIjNTc1OTVhIgogICAgIHN0cm9rZS13aWR0aD0iMiIKICAgICBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiCiAgICAgc3Ryb2tlLWRhc2hhcnJheT0iNCwgNCIKICAgICBpZD0icGF0aDEyMjUiIC8+CiAgPHBhdGgKICAgICBkPSJtIDE3NS4wNzg0MSw0LjE1ODgzOCBoIC0yLjk4ODA4IGMgLTAuMTgyOTQsMCAtMC4zNjU4OCwwLjA2NTAwMyAtMC40MjY4NywwLjI2MDAxMDIgbCAtMS40NjM1NCwyLjc5NTExMDEgYyAtMC4wNjEsMC4xOTUwMDcgLTAuMDYxLDAuMzkwMDE1IDAsMC41ODUwMjMgbCAxLjQ2MzU0LDIuNzk1MTEwNyBjIDAuMDYxLDAuMTk1MDA4IDAuMjQzOTMsMC4yNjAwMTEgMC40MjY4NywwLjI2MDAxMSBoIDIuOTg4MDggYyAwLjE4Mjk0LDAgMC4zNjU4OSwtMC4wNjUgMC40MjY4NywtMC4yNjAwMTEgbCAxLjQ2MzU1LC0yLjc5NTExMDcgYyAwLjA2MSwtMC4xOTUwMDggMC4wNjEsLTAuMzkwMDE2IDAsLTAuNTg1MDIzIGwgLTEuNDYzNTUsLTIuNzk1MTEwMSBjIC0wLjA2MSwtMC4xOTUwMDc2IC0wLjI0MzkzLC0wLjI2MDAxMDIgLTAuNDI2ODcsLTAuMjYwMDEwMiB6IgogICAgIGlkPSJwYXRoNC01IgogICAgIHN0eWxlPSJmaWxsOiM5MDkwOTA7ZmlsbC1vcGFjaXR5OjAuMTMzMzMzO3N0cm9rZS13aWR0aDowLjYyOTU5OCIgLz4KPC9zdmc+Cg==\",\n    EMPTY_SEARCH: \"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMzAwIiBoZWlnaHQ9IjE3MCIgdmlld0JveD0iMCAwIDMwMCAxNzAiIGZpbGw9Im5vbmUiPgogIDxwYXRoCiAgICAgZD0ibSAxODUuNzkzNjksNTguNTYxOTY0IGMgMi4yNjQsMCA0LjEsLTEuODM1NiA0LjEsLTQuMSAwLC0yLjI2NDQgLTEuODM2LC00LjEgLTQuMSwtNC4xIC0yLjI2NSwwIC00LjEsMS44MzU2IC00LjEsNC4xIDAsMi4yNjQ0IDEuODM1LDQuMSA0LjEsNC4xIHoiCiAgICAgZmlsbD0iIzkwOTA5MCIKICAgICBpZD0icGF0aDUwNjUiCiAgICAgc3R5bGU9ImZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4yIiAvPgogIDxwYXRoCiAgICAgZD0ibSAxOTEuNzkzNjksNDIuNTYyMDY0IGMgMS41NDYsMCAyLjgsLTEuMjUzNiAyLjgsLTIuOCAwLC0xLjU0NjQgLTEuMjU0LC0yLjggLTIuOCwtMi44IC0xLjU0NywwIC0yLjgsMS4yNTM2IC0yLjgsMi44IDAsMS41NDY0IDEuMjUzLDIuOCAyLjgsMi44IHoiCiAgICAgZmlsbD0iIzkwOTA5MCIKICAgICBpZD0icGF0aDUwNjciCiAgICAgc3R5bGU9ImZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4yIiAvPgogIDxwYXRoCiAgICAgZD0ibSA5MC4zOTM1OTUsNTguNDYxOTY0IGMgMS41NDY0LDAgMi44LC0xLjI1MzYgMi44LC0yLjggMCwtMS41NDY0IC0xLjI1MzYsLTIuOCAtMi44LC0yLjggLTEuNTQ2NCwwIC0yLjgsMS4yNTM2IC0yLjgsMi44IDAsMS41NDY0IDEuMjUzNiwyLjggMi44LDIuOCB6IgogICAgIGZpbGw9IiM5MDkwOTAiCiAgICAgaWQ9InBhdGg1MDY5IgogICAgIHN0eWxlPSJmaWxsOiM5MDkwOTA7ZmlsbC1vcGFjaXR5OjAuMiIgLz4KICA8cGF0aAogICAgIGQ9Im0gNzMuMjkzNTY1LDExMi40NjIxNiBjIDIuODcxODksMCA1LjIwMDAzLC0yLjMyODEgNS4yMDAwMywtNS4yIDAsLTIuODcxOSAtMi4zMjgxNCwtNS4yIC01LjIwMDAzLC01LjIgLTIuODcxODgsMCAtNS4xOTk5OTUsMi4zMjgxIC01LjE5OTk5NSw1LjIgMCwyLjg3MTkgMi4zMjgxMTUsNS4yIDUuMTk5OTk1LDUuMiB6IgogICAgIGZpbGw9IiM5MDkwOTAiCiAgICAgaWQ9InBhdGg1MDcxIgogICAgIHN0eWxlPSJmaWxsOiM5MDkwOTA7ZmlsbC1vcGFjaXR5OjAuMiIgLz4KICA8ZwogICAgIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2QpIgogICAgIGlkPSJnNTA3NSIKICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2Ny41NDY2OTUsMjIuMzE1NTY0KSI+CiAgICA8cGF0aAogICAgICAgZD0ibSAxMDguMDc2LDQ2LjI1NzUgMC44LDY0LjQwMDUgYyAwLDIuMiAtMS44LDMuOSAtNCwzLjkgSCA0Mi4wNzYyIGMgLTIuMiwwIC00LC0xLjggLTQsLTMuOSBWIDI3Ljg1NzUgYyAwLC0yLjIgMS44LC0zLjkgNCwtMy45IGggNDUuNSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIGlkPSJwYXRoNTA3MyIgLz4KICA8L2c+CiAgPHBhdGgKICAgICBkPSJtIDEzOC4zMjI3OSw1OS43NzI5NjQgaCAtMjIuMSBjIC0wLjcsMCAtMS4zLC0wLjYgLTEuMywtMS4zIDAsLTAuNyAwLjYsLTEuMyAxLjMsLTEuMyBoIDIyLjEgYyAwLjcsMCAxLjMsMC42IDEuMywxLjMgMCwwLjcgLTAuNiwxLjMgLTEuMywxLjMgeiIKICAgICBmaWxsPSIjZjJmM2Y1IgogICAgIGlkPSJwYXRoNTA3NyIKICAgICBzdHlsZT0iZmlsbDojZjJmM2Y1O2ZpbGwtb3BhY2l0eToxIiAvPgogIDxwYXRoCiAgICAgZD0ibSAxMjcuODIyNzksNjYuOTczMTY0IGggLTExLjYgYyAtMC43LDAgLTEuMywtMC42IC0xLjMsLTEuMyAwLC0wLjcgMC42LC0xLjMgMS4zLC0xLjMgaCAxMS41IGMgMC43LDAgMS4zLDAuNiAxLjMsMS4zIDAsMC43IC0wLjYsMS4zIC0xLjIsMS4zIHoiCiAgICAgZmlsbD0iI2YyZjNmNSIKICAgICBpZD0icGF0aDUwNzkiIC8+CiAgPHBhdGgKICAgICBkPSJtIDE1NS4xMjI4OSw0Ni4yNzMwNjQgdiAxNy44IGMgMCwyLjUgMi4yLDQuNSA0LjcsNC41IGggMTUuNzk5OCIKICAgICBmaWxsPSIjZjJmM2Y1IgogICAgIGlkPSJwYXRoNTA4MSIgLz4KICA8cGF0aAogICAgIGQ9Im0gMTI0Ljg1NjA5LDU5LjUzODI2NCA0Ljc1MTYsLTE2Ljc2IDE5LjUxNjYsMi4yNDggLTYuODEzOSwxMC45MjI0IDUuMjc3OCw0LjIxNjcgLTExLjM2MjgsMjQuODUzNyAwLjYzMzEsLTE5LjI0MTYgeiIKICAgICBmaWxsPSJ2YXIoLS1iZy1jb2xvcl8iCiAgICAgaWQ9InBhdGg1MDgzIiAvPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM1MTE3Ij4KICAgIDxmaWx0ZXIKICAgICAgIGlkPSJmaWx0ZXIwX2QiCiAgICAgICB4PSIxNi4wNzYyIgogICAgICAgeT0iMTIuOTU3NSIKICAgICAgIHdpZHRoPSIxMTQuOCIKICAgICAgIGhlaWdodD0iMTM0LjYwMDAxIgogICAgICAgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KICAgICAgPGZlRmxvb2QKICAgICAgICAgZmxvb2Qtb3BhY2l0eT0iMCIKICAgICAgICAgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiCiAgICAgICAgIGlkPSJmZUZsb29kNTA5NyIgLz4KICAgICAgPGZlQ29sb3JNYXRyaXgKICAgICAgICAgaW49IlNvdXJjZUFscGhhIgogICAgICAgICB0eXBlPSJtYXRyaXgiCiAgICAgICAgIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMTI3IDAiCiAgICAgICAgIGlkPSJmZUNvbG9yTWF0cml4NTA5OSIgLz4KICAgICAgPGZlT2Zmc2V0CiAgICAgICAgIGR5PSIxMSIKICAgICAgICAgaWQ9ImZlT2Zmc2V0NTEwMSIgLz4KICAgICAgPGZlR2F1c3NpYW5CbHVyCiAgICAgICAgIHN0ZERldmlhdGlvbj0iMTEiCiAgICAgICAgIGlkPSJmZUdhdXNzaWFuQmx1cjUxMDMiIC8+CiAgICAgIDxmZUNvbG9yTWF0cml4CiAgICAgICAgIHR5cGU9Im1hdHJpeCIKICAgICAgICAgdmFsdWVzPSIwIDAgMCAwIDAuMzk3NzA4IDAgMCAwIDAgMC40Nzc0OSAwIDAgMCAwIDAuNTc1IDAgMCAwIDAuMjcgMCIKICAgICAgICAgaWQ9ImZlQ29sb3JNYXRyaXg1MTA1IiAvPgogICAgICA8ZmVCbGVuZAogICAgICAgICBtb2RlPSJub3JtYWwiCiAgICAgICAgIGluMj0iQmFja2dyb3VuZEltYWdlRml4IgogICAgICAgICByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvdyIKICAgICAgICAgaWQ9ImZlQmxlbmQ1MTA3IiAvPgogICAgICA8ZmVCbGVuZAogICAgICAgICBtb2RlPSJub3JtYWwiCiAgICAgICAgIGluPSJTb3VyY2VHcmFwaGljIgogICAgICAgICBpbjI9ImVmZmVjdDFfZHJvcFNoYWRvdyIKICAgICAgICAgcmVzdWx0PSJzaGFwZSIKICAgICAgICAgaWQ9ImZlQmxlbmQ1MTA5IiAvPgogICAgPC9maWx0ZXI+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJwYWludDBfbGluZWFyIgogICAgICAgeDE9IjczLjQ1MzEwMiIKICAgICAgIHkxPSIyMS44NjE5IgogICAgICAgeDI9IjczLjQ1MzEwMiIKICAgICAgIHkyPSIxMTUuNTM0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjRkRGRUZGIgogICAgICAgICBpZD0ic3RvcDUxMTIiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIG9mZnNldD0iMC45OTY0IgogICAgICAgICBzdG9wLWNvbG9yPSIjRUNGMEY1IgogICAgICAgICBpZD0ic3RvcDUxMTQiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KICA8cGF0aAogICAgIGQ9Im0gMTgwLjE4MzM3LDE0My4xNTA0NiBjIC0xLDAgLTIsLTAuNCAtMi44LC0xLjMgbCAtMTYuNjk5NiwtMTYuNzAwMyAtMC42LDAuNCBjIC01LjQsNCAtMTEuNyw2LjEgLTE4LjEsNi4xIC03LjcsMCAtMTUuNCwtMy4xIC0yMS4xLC04LjUgLTUuOTk5OTksLTUuNjk5OTkgLTkuMjk5OTksLTEzLjM5OTk5IC05LjI5OTk5LC0yMS43OTk5OSAwLC0xNi43MDAwMDMgMTMuNTk5OTksLTMwLjMwMDAwMyAzMC4yOTk5OSwtMzAuMzAwMDAzIDExLjQsMCAyMS40LDYgMjYuOCwxNi4yIDUuMjk5NiwxMC4xIDQuNTk5NiwyMS45MDAwMDMgLTEuOSwzMS40MDAwMDMgbCAtMC40LDAuNiAxNi43OTk2LDE2Ljc5OTk5IGMgMS43LDEuNyAxLjMsMy40MDAzIDEsNC4zMDAzIC0wLjgsMS42IC0yLjQsMi44IC00LDIuOCB6IG0gLTM4LjI5OTYsLTYzLjgwMDI5MyBjIC0xMi4yLDAgLTIxLjk5OTk5LDkuOSAtMjEuOTk5OTksMjIuMDAwMDAzIDAsMTMuOCAxMS4yOTk5OSwyMi4wOTk5OSAyMi4yOTk5OSwyMi4wOTk5OSA2LjcsMCAxMi44LC0yLjk5OTk5IDE3LjEsLTguMzk5OTkgNS4zLC02LjYgNi4yLC0xNS41MDAwMDMgMi41LC0yMy4yMDAwMDMgLTMuOCwtNy43IC0xMS40LC0xMi41IC0xOS45LC0xMi41IHoiCiAgICAgZmlsbD0iIzU3NTk1YSIKICAgICBpZD0icGF0aDQ1MjMiIC8+CiAgPHBhdGgKICAgICBkPSJtIDEzMi4zODM3NywxMDQuOTUwMjcgYyAxLjMsMCAyLjQsLTEuMSAyLjQsLTIuNCAwLC0xLjMgLTEuMSwtMi40IC0yLjQsLTIuNCAtMS4zLDAgLTIuNCwxLjEgLTIuNCwyLjQgMCwxLjMgMS4xLDIuNCAyLjQsMi40IHoiCiAgICAgZmlsbD0iIzkwOTA5MCIKICAgICBpZD0icGF0aDQ1MjUiIC8+CiAgPHBhdGgKICAgICBkPSJtIDE1MC43ODM3NywxMDQuOTUwMjcgYyAxLjMsMCAyLjQsLTEuMSAyLjQsLTIuNCAwLC0xLjMgLTEuMSwtMi40IC0yLjQsLTIuNCAtMS4zLDAgLTIuNCwxLjEgLTIuNCwyLjQgMCwxLjQgMS4xLDIuNCAyLjQsMi40IHoiCiAgICAgZmlsbD0iIzkwOTA5MCIKICAgICBpZD0icGF0aDQ1MjciIC8+CiAgPHBhdGgKICAgICBkPSJtIDEzMi4yODk5Nyw5NC42MTAwNjcgLTUuMjc5NSwyLjg1MDUgMC43MTI3LDEuMzE5OSA1LjI3OTQsLTIuODUwNiB6IgogICAgIGZpbGw9IiM5MDkwOTAiCiAgICAgaWQ9InBhdGg0NTI5IiAvPgogIDxwYXRoCiAgICAgZD0ibSAxNTAuNDU3MzcsOTQuNTcyMjY3IC0wLjcxMjUsMS4zMiA1LjI4LDIuODUgMC43MTI1LC0xLjMyIHoiCiAgICAgZmlsbD0iIzkwOTA5MCIKICAgICBpZD0icGF0aDQ1MzEiIC8+CiAgPHBhdGgKICAgICBkPSJtIDE0MS41ODM2NywxMTAuNTUwMjcgYyAxLjU0NjQsMCAyLjgsLTAuOTQwMiAyLjgsLTIuMSAwLC0xLjE1OTggLTEuMjUzNiwtMi4xIC0yLjgsLTIuMSAtMS41NDY0LDAgLTIuOCwwLjk0MDIgLTIuOCwyLjEgMCwxLjE1OTggMS4yNTM2LDIuMSAyLjgsMi4xIHoiCiAgICAgZmlsbD0iIzkwOTA5MCIKICAgICBpZD0icGF0aDQ1MzMiIC8+CiAgPHBhdGgKICAgICBkPSJNIDEyLjA4MjE1NCwxNDEuNTQ0MDggSCA3LjE4MjE1NDQgYyAtMC4zLDAgLTAuNiwwLjEgLTAuNywwLjQgbCAtMi40LDQuMyBjIC0wLjEsMC4zIC0wLjEsMC42IDAsMC45IGwgMi40LDQuMyBjIDAuMSwwLjMgMC40LDAuNCAwLjcsMC40IGggNC44OTk5OTk2IGMgMC4zLDAgMC42LC0wLjEgMC43LC0wLjQgbCAyLjQsLTQuMyBjIDAuMSwtMC4zIDAuMSwtMC42IDAsLTAuOSBsIC0yLjQsLTQuMyBjIC0wLjEsLTAuMyAtMC40LC0wLjQgLTAuNywtMC40IHoiCiAgICAgaWQ9InBhdGg0IgogICAgIHN0eWxlPSJmaWxsOiM5MDkwOTA7ZmlsbC1vcGFjaXR5OjAuMiIgLz4KICA8cGF0aAogICAgIGNsYXNzPSJmaWxlIgogICAgIGQ9Im0gMjcxLjA5MjU5LDExLjY1MjMxMiAtNy4zNzAxOSwzLjU4MzkxOCBjIC0wLjc2MTk0LDAuNDMxMTE2IC0xLjcwNDkxLDAuNTgzNTAxIC0yLjU3MjQ2LDAuNjA1MjIyIC0wLjg2NzU0LDAuMDIxNzIgLTEuNzg3OTEsLTAuMTYxMjQ0IC0yLjYzMjg1LC0wLjQ3NDg2NyAtMS42MTQ0NCwtMC43NTc4OTggLTMuMDU1NCwtMi4xMTI0NDIgLTMuOTM4MTUsLTMuODQxNTA5IDAsMCAtMS41MDg5NiwtMy4zMTAwMzcyIC0zLjA3MDc1LC02LjgyNDc3NDUgbCAtMC4yMzM4OCwtMC40ODM0MzY4IC0xNS45NjI1Myw3LjUwNzQ2OTMgYyAtMi41NDIyMywxLjE0NTI3MSAtMy42MTMyOSw0LjE4NTE0NCAtMi41MTkyOCw2LjczMjk5OCBsIDExLjUyODU1LDI1LjY0NDA3NCBjIDEuMDk0MDIsMi41NDc4NTUgNC4wODksMy41ODAyMDUgNi42MzEyNSwyLjQzNDkzMSBsIDI1LjU5NTg0LC0xMi4wNDk0MDIgYyAyLjU0MjI0LC0xLjE0NTI3NSAzLjYxMzMxLC00LjE4NTE0MyAyLjUxOTMsLTYuNzMyOTk4IEwgMjcxLjcwMzY1LDExLjQ4MjQ5IFoiCiAgICAgc3R5bGU9ImNsaXAtcnVsZTpldmVub2RkO2ZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4yO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZS13aWR0aDoxLjQ5NDcxIgogICAgIGlkPSJwYXRoNiIgLz4KICA8cGF0aAogICAgIGNsYXNzPSJmaWxlIgogICAgIGQ9Im0gMjU5LjQxNDY4LDEyLjkyMzM2OCBjIC0xLjEwMTQ0LC0wLjQ2MTcxMyAtMS45NzY1OSwtMS4zMTUzOCAtMi42MjU0MywtMi41NjEwMDUgbCAtMC4wNTI4LC0wLjIwNDcgLTAuMjg2NjgsLTAuNjg4MTQ3MiAtMS4wNDExOSwtMi4zNDMxNTggLTAuNTczNDEsLTEuMzc2Mjc2IDEzLjAwNTk5LDQuNzIxNzcwMiAtNS4xNTk5MywyLjQyMTIwMiBjIC0wLjQ4Mjc3LDAuMjQzODY2IC0xLjAxODQxLDAuMjgzMDI4IC0xLjU1NDAxLDAuMzIyMTk2IC0wLjUzNTY0LDAuMDM5MTYgLTEuMTI0MDYsLTAuMTI2MzY0IC0xLjcxMjQ5LC0wLjI5MTg5OSB6IgogICAgIHN0eWxlPSJjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiM5MDkwOTA7ZmlsbC1vcGFjaXR5OjAuMjtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2Utd2lkdGg6MS40OTQ3MSIKICAgICBpZD0icGF0aDgiIC8+CiAgPHBhdGgKICAgICBkPSJtIDIzLjk0NDQwMywxNy42MzU1NjMgYyAtMi4wNzc0MTksMS4xOTk0MTUgLTIuNDc0NjY5LDMuODMyNzk4IC0xLjMwMzU3OSw1Ljg2MTIwMiBsIDExLjE1Nzk2OSwxOS4zMjYxMzMgYyAxLjI2ODY5LDIuMTk3NDM1IDMuNTMxOTYsMi45MTkxMzUgNS42NjI2NCwxLjY4ODk3NSBsIDI0LjM0MzEyLC0xNC4wNTQ0OTEgYyAxLjM4NDk2LC0wLjc5OTYxIDEuOTI0MTEsLTMuMDY0MTY5IDAuNjIyODgsLTUuMzE3OTM5IEwgNTQuOTI4NTQzLDguNjg2ODYyNyBjIC0xLjA0MDk5LC0xLjgwMzAxMSAtMy4wNzY1NCwtMi4xMzAzMTMgLTQuNDYxNDYsLTEuMzMwNzM0IGwgLTEyLjUxNzgyLDcuMjI3MTY5MyAtNC45NDk0NCwtMi4xNzU4NjkgeiIKICAgICBpZD0icGF0aDE2IgogICAgIHN0eWxlPSJmaWxsOiM5MDkwOTA7ZmlsbC1vcGFjaXR5OjAuMjtzdHJva2Utd2lkdGg6MC42MzI1OTQiIC8+CiAgPGcKICAgICBpZD0iZzIwIgogICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI0My4yODEwNCwxMzEuMDczNDYpIgogICAgIHN0eWxlPSJmaWxsOiM5MDkwOTA7ZmlsbC1vcGFjaXR5OjAuMiI+CiAgICA8cGF0aAogICAgICAgZD0ibSA0Mi40LDIzLjIgOC42LDkuOSBjIDAuMywwLjMgMC4xLDAuOCAtMC4zLDAuOSBMIDM4LDM2LjIgYyAtMC40LDAuMSAtMC43LC0wLjMgLTAuNiwtMC43IGwgNC4xLC0xMi4xIGMgMC4xLC0wLjQgMC43LC0wLjUgMC45LC0wLjIgeiIKICAgICAgIGlkPSJwYXRoMTgiCiAgICAgICBzdHlsZT0iZmlsbDojOTA5MDkwO2ZpbGwtb3BhY2l0eTowLjIiIC8+CiAgPC9nPgogIDxwYXRoCiAgICAgZD0ibSAyMjMuMDczMyw5Mi4zNzMwMzEgLTIsMSBjIC0wLjMsMC4xIC0wLjYsMCAtMC44LC0wLjMgbCAtMSwtMiBjIC0wLjEsLTAuMyAwLC0wLjYgMC4zLC0wLjggbCAyLC0xIGMgMC4zLC0wLjEgMC42LDAgMC44LDAuMyBsIDEsMiBjIDAuMSwwLjMgMCwwLjcgLTAuMywwLjggeiIKICAgICBpZD0icGF0aDEwIgogICAgIHN0eWxlPSJmaWxsOiM5MDkwOTA7ZmlsbC1vcGFjaXR5OjAuMiIgLz4KPC9zdmc+Cg==\",\n};\n\nconst VIRTUAL_SCROLL_MINIMUM_TRIGGER = 100;\n\nexport const files$ = new rxjs.BehaviorSubject(null);\n\nexport default async function(render) {\n    const $page = createElement(`\n        <div class=\"component_filesystem container\">\n            <div data-target=\"dragselect\" style=\"display:none;\"></div>\n            <div data-target=\"header\" style=\"text-align:center;\"></div>\n\n            <div class=\"ifscroll-before\"></div>\n            <div data-target=\"list\" class=\"list\"></div>\n            <div class=\"ifscroll-after\"></div>\n            <br>\n        </div>\n    `);\n    render($page);\n    onDestroy(() => files$.next(null));\n\n    // feature: virtual scrolling\n    const path = currentPath();\n    const $header = qs($page, `[data-target=\"header\"]`);\n    const $list = qs($page, `[data-target=\"list\"]`);\n    const removeLoader = createLoader($header);\n    const $listBefore = qs($page, \".ifscroll-before\");\n    const $listAfter = qs($page, \".ifscroll-after\");\n    const refreshScreen$ = rxjs.merge(\n        // case1: trigger the first display\n        rxjs.of(null),\n        // case2: height change => redraw screen\n        rxjs.fromEvent(window, \"resize\").pipe( // height change = always redraw\n            rxjs.startWith(null),\n            rxjs.map(() => document.body.clientHeight),\n            rxjs.distinctUntilChanged(),\n            rxjs.skip(1),\n        ),\n        // case3: width change => redraw if grid size change\n        rxjs.fromEvent(window, \"resize\").pipe(\n            rxjs.startWith(null),\n            rxjs.map(() => {\n                if ($list.getAttribute(\"data-type\") === \"grid\") {\n                    return gridSize($list.clientWidth, document.body.clientWidth);\n                }\n                return 0;\n            }),\n            rxjs.distinctUntilChanged(),\n            rxjs.skip(1),\n        ),\n    );\n\n    let count = 0;\n    effect(ls(path).pipe(\n        rxjs.switchMap(({ files, ...rest }) => getState$().pipe(rxjs.switchMap((state) => {\n            $header.innerHTML = \"\";\n            $list.innerHTML = \"\";\n            if (state.search) {\n                const removeLoader = createLoader($header);\n                $listBefore.setAttribute(\"style\", \"\");\n                $listAfter.setAttribute(\"style\", \"\");\n                return rxjs.timer(state.search ? 450 : 0).pipe(\n                    rxjs.switchMap(() => search(state.search).pipe(\n                        rxjs.map(({ files }) => ({\n                            files, ...state, ...rest,\n                        })),\n                    )),\n                    removeLoader,\n                );\n            }\n            return rxjs.of({ files, ...state, ...rest });\n        }))),\n        rxjs.mergeMap((obj) => getPermission(path).pipe(\n            rxjs.map((permissions) => ({ ...obj, permissions })),\n        )),\n        rxjs.mergeMap(({ show_hidden, files, search, ...rest }) => {\n            if (show_hidden === false) files = files.filter(({ name }) => name[0] !== \".\");\n            if (!search) files = sort(files, rest[\"sort\"], rest[\"order\"]);\n            return rxjs.of({ ...rest, files, search });\n        }),\n        rxjs.map((data) => ({ ...data, count: count++ })),\n        removeLoader,\n        rxjs.switchMap((obj) => refreshScreen$.pipe(rxjs.mapTo(obj))),\n        rxjs.mergeMap(({ files, search, ...rest }) => {\n            files$.next(files);\n            if (files.length === 0) {\n                renderEmpty(createRender(qs($page, `[data-target=\"header\"]`)), search ? ICONS.EMPTY_SEARCH : ICONS.EMPTY_FILES);\n                return rxjs.EMPTY;\n            }\n            return rxjs.of({ ...rest, files, search });\n        }),\n        rxjs.mergeMap(({ files, view, search, count, permissions }) => { // STEP1: setup the list of files\n            $list.closest(\".scroll-y\").scrollTop = 0;\n            let FILE_HEIGHT, COLUMN_PER_ROW;\n            switch (view) {\n            case \"grid\":\n                FILE_HEIGHT = 160;\n                COLUMN_PER_ROW = gridSize($list.clientWidth, document.body.clientWidth);\n                $list.style.gridTemplateColumns = `repeat(${COLUMN_PER_ROW}, 1fr)`;\n                $list.setAttribute(\"data-type\", \"grid\");\n                break;\n            case \"list\":\n                FILE_HEIGHT = 47;\n                COLUMN_PER_ROW = 1;\n                $list.style.gridTemplateColumns = `repeat(1, 1fr)`;\n                $list.setAttribute(\"data-type\", \"list\");\n                break;\n            default:\n                throw new Error(\"Not Implemented\");\n            }\n            const BLOCK_SIZE = Math.ceil(document.body.clientHeight / FILE_HEIGHT) + 1;\n\n            let size = files.length;\n            if (size > VIRTUAL_SCROLL_MINIMUM_TRIGGER) {\n                size = Math.min(files.length, BLOCK_SIZE * COLUMN_PER_ROW);\n            }\n            const $fs = document.createDocumentFragment();\n            for (let i = 0; i < size; i++) {\n                const file = files[i];\n                $fs.appendChild(createThing({\n                    ...file,\n                    ...createLink(file, currentPath()),\n                    view,\n                    search,\n                    n: i,\n                    permissions,\n                }));\n            }\n            if (count === 0) animate(\n                $list,\n                { time: 200, keyframes: slideYIn(isMobile ? 10 : 5) },\n            );\n            $list.replaceChildren($fs);\n\n            /// ///////////////////////////////////\n            // CASE 1: virtual scroll isn't enabled\n            if (files.length <= VIRTUAL_SCROLL_MINIMUM_TRIGGER) {\n                return rxjs.of({ virtual: false });\n            }\n\n            /// ///////////////////////////////////\n            // CASE 2: with virtual scroll\n            const height = (Math.ceil(files.length / COLUMN_PER_ROW) - BLOCK_SIZE) * FILE_HEIGHT;\n            if (height > 33554400) {\n                console.log(`maximum CSS height reached, requested height ${height} is too large`);\n            }\n            const setHeight = (size) => {\n                if (size < 0 || size > height) throw new ApplicationError(\n                    \"INTERNAL ERROR\",\n                    `assertion on size failed: size[${size}] height[${height}]`\n                );\n                $listBefore.style.height = `${size}px`;\n                $listAfter.style.height = `${height - size}px`;\n            };\n            setHeight(0);\n            const top = ($node) => $node.getBoundingClientRect().top;\n            return rxjs.of({\n                virtual: true,\n                files,\n                search,\n                path,\n                view,\n                permissions,\n                currentState: 0,\n                $list,\n                setHeight,\n                FILE_HEIGHT,\n                BLOCK_SIZE,\n                COLUMN_PER_ROW,\n                MARGIN: top($list) - top($list.closest(\".scroll-y\")),\n            });\n        }),\n        rxjs.switchMap(({\n            files, path, view, search, permissions,\n            BLOCK_SIZE, COLUMN_PER_ROW, FILE_HEIGHT,\n            MARGIN,\n            currentState,\n            setHeight,\n            $list,\n            virtual,\n        }) => (\n            virtual\n                ? rxjs.fromEvent($page.closest(\".scroll-y\"), \"scroll\", { passive: true })\n                : rxjs.EMPTY\n        ).pipe(\n            rxjs.map((e) => {\n                // 0-------------0-----------1-----------2-----------3 ....\n                //    [padding]     $block1     $block2     $block3    ....\n                const nextState = Math.floor((e.target.scrollTop - MARGIN) / FILE_HEIGHT);\n                return Math.max(nextState, 0);\n            }),\n            rxjs.distinctUntilChanged(),\n            rxjs.debounce(() => new rxjs.Observable((observer) => {\n                const id = requestAnimationFrame(() => observer.next());\n                return () => cancelAnimationFrame(id);\n            })),\n            rxjs.tap((nextState) => {\n                // STEP1: calculate the virtual scroll paramameters\n                let diff = nextState - currentState;\n                const diffSgn = Math.sign(diff);\n                if (Math.abs(diff) > BLOCK_SIZE) { // diff is bound by BLOCK_SIZE\n                    // we can't be moving more than what is on the screen\n                    diff = diffSgn * BLOCK_SIZE;\n                }\n                let fileStart = nextState * COLUMN_PER_ROW;\n                if (diffSgn > 0) { // => scroll down\n                    fileStart += BLOCK_SIZE * COLUMN_PER_ROW;\n                    fileStart -= Math.min(diff, BLOCK_SIZE) * COLUMN_PER_ROW;\n                }\n                let fileEnd = fileStart + diffSgn * diff * COLUMN_PER_ROW;\n                if (fileStart >= files.length) { // occur when BLOCK_SIZE is larger than its absolute minimum\n                    return;\n                }\n                else if (fileEnd > files.length) {\n                    // occur when files.length isn't a multiple of COLUMN_PER_ROW and\n                    // we've scrolled to the bottom of the list already\n                    nextState = Math.ceil(files.length / COLUMN_PER_ROW) - BLOCK_SIZE;\n                    fileEnd = files.length - 1;\n                    for (let i=0; i<COLUMN_PER_ROW; i++) {\n                        // add some padding to fileEnd to balance the list to the\n                        // nearest COLUMN_PER_ROW\n                        fileEnd += 1;\n                        if (fileEnd % COLUMN_PER_ROW === 0) {\n                            break;\n                        }\n                    }\n                }\n\n                // STEP2: create the new elements\n                const $fs = document.createDocumentFragment();\n                let n = 0;\n                for (let i = fileStart; i < fileEnd; i++) {\n                    const file = files[i];\n                    if (file === undefined) $fs.appendChild(createThing({\n                        type: \"hidden\",\n                    }));\n                    else $fs.appendChild(createThing({\n                        ...file,\n                        ...createLink(file, path),\n                        search,\n                        view,\n                        permissions,\n                        n: i,\n                    }));\n                    n += 1;\n                }\n\n                // STEP3: update the DOM\n                if (diffSgn > 0) { // scroll down\n                    $list.appendChild($fs);\n                    for (let i = 0; i < n; i++) $list.firstChild.remove();\n                } else { // scroll up\n                    $list.insertBefore($fs, $list.firstChild);\n                    for (let i = 0; i < n; i++) $list.lastChild.remove();\n                }\n                setHeight(nextState * FILE_HEIGHT);\n                currentState = nextState;\n            }),\n        )),\n        rxjs.catchError(ctrlError()),\n    ));\n\n    // feature: keyboard selection\n    effect(rxjs.fromEvent(window, \"keydown\").pipe(\n        rxjs.filter((e) => e.keyCode === 27),\n        rxjs.tap(() => clearSelection()),\n    ));\n    effect(rxjs.fromEvent(window, \"keydown\").pipe(\n        rxjs.filter((e) => e.key === \"a\" &&\n                    (e.ctrlKey || e.metaKey) &&\n                    (files$.value || []).length > 0 &&\n                    !isAlreadyFocused()),\n        preventDefault(),\n        rxjs.tap(() => {\n            clearSelection();\n            if (!Array.isArray(files$.value) || files$.value.length === 0) return;\n            const path = currentPath();\n            const el0 = files$.value[0];\n            const elm1 = files$.value.slice(-1)[0];\n            addSelection({\n                n: 0,\n                path: path + el0.name + (el0.type === \"directory\" ? \"/\" : \"\"),\n                shift: false,\n                files: [],\n            });\n            if (elm1) addSelection({\n                n: (files$.value || []).length - 1,\n                path: path + elm1.name + (elm1.type === \"directory\" ? \"/\" : \"\"),\n                shift: true,\n                files: (files$.value || []),\n            });\n        }),\n    ));\n    effect(getSelection$().pipe(rxjs.tap(() => {\n        for (const $thing of $page.querySelectorAll(\".component_thing\")) {\n            let checked = isSelected(parseInt(assert.truthy($thing.getAttribute(\"data-n\"))));\n            if ($thing.getAttribute(\"data-selectable\") === \"false\") checked = false;\n            $thing.classList.add(checked ? \"selected\" : \"not-selected\");\n            $thing.classList.remove(checked ? \"not-selected\" : \"selected\");\n            qs(assert.type($thing, HTMLElement), `input[type=\"checkbox\"]`).checked = checked;\n        };\n    })));\n\n    // feature: mouse drag selection\n    const $dragContainer = assert.type($page.closest(\".component_page_filespage\"), HTMLElement);\n    const $dragselect = qs($page, `[data-target=\"dragselect\"]`);\n    const dragmove = (x, y, w, h) => {\n        $dragselect.style.left = `${x}px`;\n        $dragselect.style.top = `${y}px`;\n        $dragselect.style.width = `${w}px`;\n        $dragselect.style.height = `${h}px`;\n        if ((w+1) * (h+1) < 50) {\n            $dragselect.style.display = \"none\";\n            return false;\n        }\n        $dragselect.style.display = \"block\";\n        return true;\n    };\n    if (isMobile === false) effect(rxjs.fromEvent($dragContainer, \"mousedown\").pipe(\n        rxjs.filter((e) => {\n            if (e.target.nodeName === \"INPUT\") return false;\n            else if (e.target.closest(\"button\")) return false;\n            else if (e.buttons !== 1) return false;\n            return !e.target.closest(`[draggable=\"true\"]`);\n        }),\n        rxjs.tap((e) => e.preventDefault()),\n        rxjs.map((e) => ({\n            start: [e.clientX, e.clientY],\n            state: [...$page.querySelectorAll(\".component_thing\")].map(($file) => {\n                const bounds = $file.getBoundingClientRect();\n                const $checkbox = qs(assert.type($file, HTMLElement), \".component_checkbox\");\n                return {\n                    $checkbox,\n                    checked: () => $checkbox.firstElementChild.checked,\n                    bounds: {\n                        x: bounds.x,\n                        y: bounds.y,\n                        w: bounds.width,\n                        h: bounds.height,\n                    },\n                };\n            }),\n        })),\n        rxjs.mergeMap(({ start, state }) => rxjs.fromEvent(document, \"mousemove\").pipe(\n            rxjs.takeUntil(rxjs.merge(\n                rxjs.fromEvent(window, \"mouseup\"),\n                rxjs.fromEvent(window, \"keydown\").pipe(rxjs.filter(({ key }) => key === \"Escape\")),\n            )),\n            rxjs.finalize(() => dragmove(0, 0, 0, 0)),\n            rxjs.map((e) => ({ start, end: [e.clientX, e.clientY], state })),\n        )),\n        rxjs.map(({ start, end, state }) => ({\n            state,\n            obj: {\n                x: Math.min(start[0], end[0]),\n                y: Math.min(start[1], end[1]),\n                w: Math.abs(start[0] - end[0]),\n                h: Math.abs(start[1] - end[1]),\n            },\n        })),\n        rxjs.filter(({ obj }) => dragmove(obj.x, obj.y, obj.w, obj.h)),\n        rxjs.tap(({ obj, state }) => {\n            for (let i=0; i<state.length; i++) {\n                const { bounds, $checkbox, checked } = state[i];\n                const collision = !(\n                    obj.x + obj.w < bounds.x ||\n                        obj.x > bounds.x + bounds.w ||\n                        obj.y + obj.h < bounds.y ||\n                        obj.y > bounds.y + bounds.h\n                );\n                if ((collision && !checked()) || (!collision && checked())) {\n                    $checkbox.click();\n                }\n            }\n        }),\n    ));\n\n    // feature: remove long touch popup on mobile\n    const disableLongTouch = (e) => {\n        if (isMobile === false) return;\n        e.preventDefault();\n    };\n    document.addEventListener(\"contextmenu\", disableLongTouch);\n    onDestroy(() => document.removeEventListener(\"contextmenu\", disableLongTouch));\n}\n\nfunction renderEmpty(render, base64Icon) {\n    const $page = createElement(`\n        <div class=\"empty no-select\">\n            <p class=\"empty_image\">\n                <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,${base64Icon}\" alt=\"empty_folder\">\n            </p>\n            <p class=\"label\">${t(\"There is nothing here\")}</p>\n        </div>\n    `);\n    animate(render($page), { time: 250, keyframes: slideYIn(5) });\n}\n\nexport function init() {\n    return Promise.all([\n        loadCSS(import.meta.url, \"./ctrl_filesystem.css\"),\n        loadCSS(import.meta.url, \"./thing.css\"),\n        loadCSS(import.meta.url, \"./modal.css\"),\n    ]);\n}\n\nexport function createLink(file, currentPath) {\n    let path = file.path;\n    if (!path) path = currentPath + file.name + (file.type === \"directory\" ? \"/\" : \"\");\n    let link = file.type === \"directory\" ? \"files\" + path : \"view\" + path;\n    link = encodeURIComponent(link).replaceAll(\"%2F\", \"/\");\n    return {\n        path,\n        link,\n    };\n}\n\nfunction gridSize(size, windowSize) {\n    const DESIRED_FILE_WIDTH_ON_LARGE_SCREEN = 210;\n    if (windowSize > 1100) return Math.max(\n        4,\n        Math.floor(size / DESIRED_FILE_WIDTH_ON_LARGE_SCREEN),\n    );\n    else if (size > 750) return 4;\n    else if (size > 520) return 3;\n    else if (size > 300) return 2;\n    return 1;\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/ctrl_frequentlyaccess.css",
    "content": "[is=\"component_frequently_access\"] .component_container {\n    padding: 0;\n    min-height: 145px;\n    padding: 0 0 7px 0;\n}\n[is=\"component_frequently_access\"] .component_icon {\n    height: 25px;\n}\n[is=\"component_frequently_access\"] .caption {\n    margin-top: 15px;\n    letter-spacing: 0.06em;\n    display: inline-block;\n    text-transform: uppercase;\n    font-size: 12px;\n    padding-bottom: 5px;\n    color: var(--light);\n}\n[is=\"component_frequently_access\"] .frequent_wrapper {\n    display: flex;\n}\n[is=\"component_frequently_access\"] .frequent_wrapper a {\n    width: 33.33%;\n    background: rgba(0, 0, 0, 0.05);\n    transition: background 0.05s linear 0s;\n    border-radius: 2px;\n    overflow: hidden;\n    margin-right: 5px;\n    padding: 5px;\n    cursor: pointer;\n    line-height: 25px;\n    display: block;\n}\n[is=\"component_frequently_access\"] .frequent_wrapper a img {\n    float: left;\n    height: 25px;\n    margin-right: 2px;\n}\n[is=\"component_frequently_access\"] .frequent_wrapper a div {\n    width: calc(100% - 30px);\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n}\n@media (max-width: 800px) {\n    [is=\"component_frequently_access\"] .frequent_wrapper a:nth-child(6) {\n        display: none;\n    }\n}\n@media (max-width: 750px) {\n    [is=\"component_frequently_access\"] .frequent_wrapper a:nth-child(5) {\n        display: none;\n    }\n}\n@media (max-width: 650px) {\n    [is=\"component_frequently_access\"] .frequent_wrapper a:nth-child(4) {\n        display: none;\n    }\n}\n@media (max-width: 450px) {\n    [is=\"component_frequently_access\"] .frequent_wrapper a:nth-child(3) {\n        display: none;\n    }\n}\n[is=\"component_frequently_access\"] .nothing_placeholder {\n    padding: 20px;\n    text-align: center;\n    background: var(--emphasis-primary);\n    color: rgba(0, 0, 0, 0.5);\n    border-radius: 5px;\n    position: relative;\n    top: 25px;\n}\n[is=\"component_frequently_access\"] .nothing_placeholder svg {\n    display: block;\n    width: 45px;\n    margin: 0 auto;\n    padding-bottom: 5px;\n}\n\n.touch-no [is=\"component_frequently_access\"] .frequent_wrapper a:hover {\n    transition: background 0.1s linear 0s;\n    background: var(--emphasis-primary);\n}\n\n.frequent-access-appear {\n    opacity: 0;\n    transition: opacity 0.3s ease-out;\n}\n.frequent-access-appear .frequent_wrapper a {\n    transition: all 0.3s ease-out;\n    opacity: 0;\n    transform: translateX(5px);\n}\n\n.frequent-access-appear.frequent-access-appear-active {\n    opacity: 1;\n}\n.frequent-access-appear.frequent-access-appear-active .frequent_wrapper a {\n    transform: translateX(0px);\n    opacity: 1;\n}\n\n.frequent-access-enter {\n    opacity: 0;\n    transition: opacity 0.5s ease-out;\n}\n.frequent-access-enter .frequent_wrapper a {\n    transition: all 0.3s ease-out;\n    transition-delay: 0.2s;\n    opacity: 0;\n    transform: translateY(-5px);\n}\n\n.frequent-access-enter.frequent-access-enter-active {\n    opacity: 1;\n    transition: opacity 0.5s ease-out;\n}\n.frequent-access-enter.frequent-access-enter-active .frequent_wrapper a {\n    transform: translateY(0px);\n    opacity: 1;\n}\n\n.dark-mode [is=\"component_frequently_access\"] .frequent_wrapper a {\n    color: var(--light);\n    background: var(--dark);\n}\n.dark-mode [is=\"component_frequently_access\"] .frequent_wrapper a:hover {\n    background: rgba(255, 255, 255, 0.05);\n}\n\n.dark-mode [is=\"component_frequently_access\"] .nothing_placeholder {\n    background: var(--dark);\n    color: rgba(255, 255, 255, 0.5);\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/ctrl_frequentlyaccess.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\nimport { transition, opacityIn } from \"../../lib/animate.js\";\nimport t from \"../../locales/index.js\";\n\nexport default function(render) {\n    const $page = createElement(`\n        <div class=\"component_container\">\n            <div class=\"nothing_placeholder no-select\">\n                <svg aria-hidden=\"true\" focusable=\"false\" data-icon=\"layer-group\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\">\n                    <path fill=\"currentColor\" d=\"M12.41 148.02l232.94 105.67c6.8 3.09 14.49 3.09 21.29 0l232.94-105.67c16.55-7.51 16.55-32.52 0-40.03L266.65 2.31a25.607 25.607 0 0 0-21.29 0L12.41 107.98c-16.55 7.51-16.55 32.53 0 40.04zm487.18 88.28l-58.09-26.33-161.64 73.27c-7.56 3.43-15.59 5.17-23.86 5.17s-16.29-1.74-23.86-5.17L70.51 209.97l-58.1 26.33c-16.55 7.5-16.55 32.5 0 40l232.94 105.59c6.8 3.08 14.49 3.08 21.29 0L499.59 276.3c16.55-7.5 16.55-32.5 0-40zm0 127.8l-57.87-26.23-161.86 73.37c-7.56 3.43-15.59 5.17-23.86 5.17s-16.29-1.74-23.86-5.17L70.29 337.87 12.41 364.1c-16.55 7.5-16.55 32.5 0 40l232.94 105.59c6.8 3.08 14.49 3.08 21.29 0L499.59 404.1c16.55-7.5 16.55-32.5 0-40z\"></path>\n                </svg>\n                ${t(\"Frequently access folders will be shown here\")}\n            </div>\n        </div>\n    `);\n    if (location.pathname === \"/files/\") render(transition($page, {\n        enter: opacityIn(),\n        timeEnter: 800,\n    }));\n}\n\nexport function init() {\n    return loadCSS(import.meta.url, \"./ctrl_frequentlyaccess.css\");\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/ctrl_newitem.css",
    "content": ".component_newitem {\n    overflow: hidden;\n}\n.component_newitem .component_thing {\n    margin: 2px 0;\n}\n.component_newitem .component_thing .box {\n    margin: 0;\n}\n.component_newitem .component_thing .box .file-details {\n    flex-grow: 1;\n}\n.component_newitem .component_thing .box .file-details input {\n    border-color: var(--border);\n    padding: 0;\n}\n.component_newitem .component_thing .box .component_action {\n    display: block;\n    opacity: 1;\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/ctrl_newitem.js",
    "content": "import { createElement, nop } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, onClick, preventDefault } from \"../../lib/rx.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport { animate } from \"../../lib/animate.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\n\nimport { getAction$, setAction } from \"./state_newthing.js\";\nimport { currentPath } from \"./helper.js\";\nimport { mkdir as mkdir$, touch as touch$ } from \"./model_files.js\";\nimport { mkdir as mkdirVL, touch as touchVL, withVirtualLayer } from \"./model_virtual_layer.js\";\n\nconst touch = (path) => withVirtualLayer(\n    touch$(path),\n    touchVL(path),\n);\nconst mkdir = (path) => withVirtualLayer(\n    mkdir$(path),\n    mkdirVL(path),\n);\n\nconst IMAGE = {\n    FILE: \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTYiIHdpZHRoPSIxNiI+CiAgPHBhdGggc3R5bGU9ImNvbG9yOiMwMDAwMDA7dGV4dC1pbmRlbnQ6MDt0ZXh0LXRyYW5zZm9ybTpub25lO2ZpbGw6IzhjOGM4YztmaWxsLW9wYWNpdHk6MTtzdHJva2Utd2lkdGg6MC45ODQ4MTA0MSIgZD0ibSAyLDEzLjA4MjQxMiAwLjAxOTQ2MiwxLjQ5MjM0NyBjIDVlLTYsMC4yMjIxNDUgMC4yMDU1OTAyLDAuNDI0MjYyIDAuNDMxMTUwMiwwLjQyNDI3MiBMIDEzLjU4OTYxMiwxNSBDIDEzLjgxNTE3MywxNC45OTk5OTUgMTMuOTk5OTksMTQuNzk3ODc0IDE0LDE0LjU3NTcyOSB2IC0xLjQ5MzMxNyBjIC00LjE3MTg2OTIsMC42NjIwMjMgLTcuNjUxNjkyOCwwLjM5ODY5NiAtMTIsMCB6IiAvPgogIDxwYXRoIHN0eWxlPSJjb2xvcjojMDAwMDAwO3RleHQtaW5kZW50OjA7dGV4dC10cmFuc2Zvcm06bm9uZTtkaXNwbGF5OmlubGluZTtmaWxsOiNhYWFhYWE7c3Ryb2tlLXdpZHRoOjAuOTg0MDgxMjciIGQ9Ik0gMi4zNTAxLDEuMDAxMzMxMiBDIDIuMTUyNTksMS4wMzgzMjQ3IDEuOTk2NTksMS4yMjcyNzIzIDIuMDAwMDksMS40MjQ5MzU2IFYgMTQuMTMzNDU3IGMgNWUtNiwwLjIyMTgxNiAwLjIwNTIzLDAuNDIzNjM0IDAuNDMwNzksMC40MjM2NDQgbCAxMS4xMzksLTEuMDFlLTQgYyAwLjIyNTU2LC02ZS02IDAuNDMwMTEsLTAuMjAwNzU4IDAuNDMwMTIsLTAuNDIyNTc0IGwgNi43ZS00LC05LjgyMjY0MjYgYyAtMi40ODQwNDYsLTEuMzU1MDA2IC0yLjQzNTIzNCwtMi4wMzEyMjU0IC0zLjUwMDEsLTMuMzA5NzA3IC0wLjA0MywtMC4wMTU4ODIgMC4wNDYsMC4wMDE3NCAwLDAgTCAyLjQzMDY3LDEuMDAxMTA4IEMgMi40MDM4MywwLjk5ODU5IDIuMzc2NzQsMC45OTg1OSAyLjM0OTksMS4wMDExMDggWiIgLz4KICA8cGF0aCBzdHlsZT0iZGlzcGxheTppbmxpbmU7ZmlsbDojOGM4YzhjO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojOWU3NTc1O3N0cm9rZS13aWR0aDowO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiIGQ9Im0gMTAuNTAwNTcsMS4wMDIwNzY0IGMgMCwzLjI3NjgwMjggLTAuMDA1MiwzLjE3MzkxNjEgMC4zNjI5MjEsMy4yNjk4MjAyIDAuMjgwMTA5LDAuMDcyOTg0IDMuMTM3MTgsMC4wMzk4ODcgMy4xMzcxOCwwLjAzOTg4NyAtMS4xMjAwNjcsLTEuMDU1NjY5MiAtMi4zMzM0LC0yLjIwNjQ3MTMgLTMuNTAwMSwtMy4zMDk3MDc0IHoiIC8+Cjwvc3ZnPg==\",\n    FOLDER: \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiPgogIDxnIHRyYW5zZm9ybT0ibWF0cml4KDAuODY2NjY0MzEsMCwwLDAuODY2NjcsLTE3Mi4wNDU3OCwtODY0LjMyNzU5KSIgc3R5bGU9ImZpbGw6Izc1YmJkOTtmaWxsLW9wYWNpdHk6MC45NDExNzY0NztmaWxsLXJ1bGU6ZXZlbm9kZCI+CiAgICA8cGF0aCBzdHlsZT0iZmlsbDojNzViYmQ5O2ZpbGwtb3BhY2l0eTowLjk0MTE3NjQ3O2ZpbGwtcnVsZTpldmVub2RkIiBkPSJtIDIwMC4yLDk5OS43MiBjIC0wLjI4OTEzLDAgLTAuNTMxMjUsMC4yNDIxIC0wLjUzMTI1LDAuNTMxMiB2IDEyLjc4NCBjIDAsMC4yOTg1IDAuMjMyNjQsMC41MzEyIDAuNTMxMjUsMC41MzEyIGggMTUuMDkxIGMgMC4yOTg2LDAgMC41MzEyNCwtMC4yMzI3IDAuNTMxMjQsLTAuNTMxMiBsIDRlLTQsLTEwLjQ3NCBjIDAsLTAuMjg4OSAtMC4yNDIxMSwtMC41MzM4IC0wLjUzMTI0LC0wLjUzMzggbCAtNy41NDU3LDVlLTQgLTIuMzA3NiwtMi4zMDc4MyB6IiAvPgogIDwvZz4KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgwLjg2NjY3LDAsMCwwLjg2NjY3LC0xNzIuMDQ2OTIsLTg2NC43ODM0KSIgc3R5bGU9ImZpbGw6IzlhZDFlZDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZCI+CiAgICA8cGF0aCBzdHlsZT0iZmlsbDojOWFkMWVkO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkIiBkPSJtIDIwMC4yLDk5OS43MiBjIC0wLjI4OTEzLDAgLTAuNTMxMjUsMC4yNDIxIC0wLjUzMTI1LDAuNTMxMiB2IDEyLjc4NCBjIDAsMC4yOTg1IDAuMjMyNjQsMC41MzEyIDAuNTMxMjUsMC41MzEyIGggMTUuMDkxIGMgMC4yOTg2LDAgMC41MzEyNCwtMC4yMzI3IDAuNTMxMjQsLTAuNTMxMiBsIDRlLTQsLTEwLjQ3NCBjIDAsLTAuMjg4OSAtMC4yNDIxMSwtMC41MzM4IC0wLjUzMTI0LC0wLjUzMzggbCAtNy41NDU3LDVlLTQgLTIuMzA3NiwtMi4zMDc4MyB6IiAvPgogIDwvZz4KPC9zdmc+Cg==\",\n    TRASH: \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0ODIuNDI4IDQ4Mi40MjkiPgogIDxwYXRoIHN0eWxlPSJmaWxsOiM2ZjZmNmY7c3Ryb2tlLXdpZHRoOjAuOTQ3MjAwNTQiIGQ9Im0gMjM5LjcxMDM4LDEwLjg1ODU2NyBjIC0yOS4yODYzMywwLjE0ODk1OSAtNTYuMjI4MjEsMjMuMTU4MTY3IC02MS4xMzg4Myw1MS45OTYxMjkgLTAuMDM1OSw1LjUyMjQ0IC04LjExOTM2LDEuNTIzODUyIC0xMS44MTQxMSwyLjczNDMwMSAtMjEuNjU5MywwLjM1NzE4IC00My4zODAyLC0wLjY3Njg3NSAtNjUuMDA3MTksMC40Mzg0NTIgLTI1Ljc0Mzk2MSwyLjgxNDg5NiAtNDcuMDQxMDg0LDI2LjM4MTc2IC00Ny4xNzMxNzIsNTIuMjkyMTMxIC0xLjcyMjExOCwyMi4zMjI3NyAxMS42Nzg0MSw0NC43NzgwOSAzMi4zMjg3NjgsNTMuNTM1MzIgMS41MDI3NjcsNy4xMzU1IDAuMjE0MTksMTYuMTEyMjggMC42NDM4LDIzLjk1NTY4IDAuMTEwMTQ1LDc1LjI4MzExIC0wLjIxODQzMywxNTAuNTc3MzcgMC4xNjA5NSwyMjUuODUzNjcgMS40ODk4MDUsMjUuODUxOTIgMjMuOTUyNDE0LDQ4LjI5NzYgNDkuODA1NzI0LDQ5Ljc2Njg3IDY4Ljk5NTMyLDAuMjc5OTggMTM4LjAxNjU0LDAuMjI5NjYgMjA3LjAxMzE3LDAuMDI0NyAyNi4wMTg1MiwtMS4yNzY5MSA0OC43MjA1LC0yMy44MzQ0MyA1MC4xOTI0OSwtNDkuODM3NjcgMC4zNjUyOCwtODMuMTUzOTggMC4wNDk3LC0xNjYuMzI1MDggMC4xNTUzOCwtMjQ5LjQ4NTU4IDIwLjg0ODU5LC04LjUyMTk5IDM0LjU5NTY3LC0zMC45NzQ5OSAzMi45NzkzNiwtNTMuNDExMzEgMC4wNzUyLC0yNi4wNzE2MTEgLTIxLjMyNDY5LC00OS45MDA0NDIgLTQ3LjIyOTkxLC01Mi42OTkyMDYgLTI0LjY2MTA5LC0xLjA5MzY1MSAtNDkuNDEyODgsLTAuMDg0ODcgLTc0LjEwNTUsLTAuNDMyOSAtMy45NDgzNywwLjYxMjkxMSAtMi4zMDc4NywtNS4zNzQ4NTkgLTMuODc5MTQsLTcuOTc4OTIzIC03LjI2MTcsLTI3LjU4NzA0MiAtMzQuMzcxMDIsLTQ3Ljg1NDgyMzggLTYyLjkzMTc5LC00Ni43NTE2NDMgeiBtIDEuNTA0MDQsMjguNTMwNzE3IGMgMTUuNDcwMDYsLTAuMzA1NjE5IDMwLjI2NjY3LDExLjA4NDk0OCAzNC4wMzQ0NywyNi4xOTk3MTMgLTIyLjY4MzQsLTAuMDA1OSAtNDUuMzk2OTIsMC4wMTIzMyAtNjguMDYxNTQsLTAuMDA5MiAzLjgwNzAyLC0xNS4wNzAyMDQgMTguMzQxMTcsLTI2LjQxOTgyMyAzNC4wMjcwNywtMjYuMTkwNDYzIHogTSAxMDguNjc4NTEsOTQuMTM2MzYzIGMgODkuNDUyNTcsMC4xMzkxNjYgMTc4LjkyODgzLC0wLjI3NzY1MSAyNjguMzY2NjksMC4yMDcyIDEzLjc0MTMxLDEuNDE4NTc4IDIzLjkyNjY0LDE1LjE3NjA3NyAyMi4yNjY2MiwyOC43MDgzMTcgMC4wMzI1LDE0LjU1MDU0IC0xNC4wNzUxNCwyNi41NjAyNiAtMjguNDA0OTIsMjQuODcxNDIgLTg4LjUwNjYsLTAuMTQwMzcgLTE3Ny4wMzY5MSwwLjI4MDA2IC0yNjUuNTI4OCwtMC4yMDkwNiBDIDkxLjEyNTU5LDE0Ni4yNDIyMyA4MC45ODkyODUsMTMxLjcwMTYzIDgzLjE4MTc5NCwxMTcuNzAxNjggODMuOTcwODg3LDEwNC43NDEzIDk1LjU3NzEzMSw5My45MjA1OTYgMTA4LjY3ODUxLDk0LjEzNjM2MyBaIE0gMzY2LjMzLDE3Ni40NzA2NiBjIC0wLjE0MDc3LDgxLjQyODQ4IDAuMjgwNjIsMTYyLjg4MDcgLTAuMjA5MDUsMjQ0LjI5NDQ3IC0xLjQzODYyLDEzLjkxMTUxIC0xNS40NzY4NSwyNC4xMDU4OCAtMjkuMTUyMzIsMjIuMjc1ODggLTY2LjE5Njc4LC0wLjE0MTYyIC0xMzIuNDE3NDMsMC4yODE3NSAtMTk4LjU5OTQ1LC0wLjIwOTA2IC0xMy44OTE2OSwtMS40NDg4IC0yNC4xMTkwOSwtMTUuNDc1NzUgLTIyLjI3MjE2LC0yOS4xNTc4NiAwLC03OS4wNjc4MSAwLC0xNTguMTM1NjEgMCwtMjM3LjIwMzQzIDgzLjQxMDk4LDAgMTY2LjgyMTk4LDAgMjUwLjIzMjk4LDAgeiIvPgogIDxwYXRoIHN0eWxlPSJmaWxsOiM2ZjZmNmY7c3Ryb2tlLXdpZHRoOjAuOTgyMDgxIiBkPSJtIDE3MS42ODY0NCwyNDcuNDczNzkgYyAtOS4zNDY3NiwwLjE1NjQ0IC0xNS43NDAzMiw5Ljg4ODA1IC0xNC4wODY3MywxOC43MTEzMyAwLjEyMzUxLDQ3LjYyNzAxIC0wLjI0NDAxLDk1LjI3OTAzIDAuMTc4MzksMTQyLjg5MDg3IDEuMjA3NjQsMTAuOTcxMzYgMTUuOTE4MDMsMTYuNTI3OTQgMjQuMDcyNDksOS4wODQyNSA4LjQxNzU5LC02LjgxODg3IDQuNDc0NjksLTE4Ljg4MzkyIDUuMzQ3NzQsLTI4LjA4MTM4IC0wLjEyNDM5LC00My4zNzEyNyAwLjI0NTIsLTg2Ljc2Nzg0IC0wLjE3ODM5LC0xMzAuMTIzODEgLTEuMDM3OTUsLTcuMzE0MzkgLTcuOTUwNTQsLTEyLjk1NzA1IC0xNS4zMzM1LC0xMi40ODEyNiB6Ii8+CiAgPHBhdGggc3R5bGU9ImZpbGw6IzZmNmY2ZjtzdHJva2Utd2lkdGg6MC45ODIwODEiIGQ9Im0gMjQwLjUwMTE2LDI0Ny40NzM3OSBjIC05LjM0NjQ5LDAuMTU2MTYgLTE1Ljc0MDY3LDkuODg4MTcgLTE0LjA4NjczLDE4LjcxMTMzIDAuMTIzNTIsNDcuNjI3MDEgLTAuMjQ0MDEsOTUuMjc5MDMgMC4xNzgzOSwxNDIuODkwODcgMS44MDUwNCwxNy41NjQ4OSAzMC4zNzQxMiwxNS4zNDIyNyAyOS40MjAyMywtMi4zMDE3NiAtMC4xMjMzMywtNDguOTM2NDIgMC4yNDM3NywtOTcuODk4MiAtMC4xNzgzOCwtMTQ2LjgxOTE4IC0xLjAzNzM1LC03LjMxNDEgLTcuOTUxMDEsLTEyLjk1Njk0IC0xNS4zMzM1MSwtMTIuNDgxMjYgeiIvPgogIDxwYXRoIHN0eWxlPSJmaWxsOiM2ZjZmNmY7c3Ryb2tlLXdpZHRoOjAuOTgyMDgxIiBkPSJtIDMwOS4zMTU4OCwyNDcuNDczNzkgYyAtOS4zNDcxMSwwLjE1NTMgLTE1Ljc0MzE0LDkuODg3MTMgLTE0LjA4NjcyLDE4LjcxMTMzIDAuMTIzNTQsNDcuNjI0OTkgLTAuMjQ0MDYsOTUuMjc1ODEgMC4xNzgzOCwxNDIuODg1MTEgMS4xOTg1NiwxMC45NzMyMSAxNS45MTY2NCwxNi41MzYwNiAyNC4wNzA1OCw5LjA5MDAxIDguNDE5MzMsLTYuODE3ODcgNC40NzM2NiwtMTguODg0MiA1LjM0Nzc0LC0yOC4wODEzOCAtMC4xMjQ0MiwtNDMuMzcxMjQgMC4yNDUyNCwtODYuNzY4MDQgLTAuMTc4MzksLTEzMC4xMjM4MSAtMS4wMzY3NCwtNy4zMTMyMSAtNy45NTAxMiwtMTIuOTU2NTggLTE1LjMzMTU5LC0xMi40ODEyNiB6Ii8+Cjwvc3ZnPg==\",\n};\n\nexport default async function(render) {\n    const $node = createElement(`\n        <div class=\"component_newitem container\" id=\"newthing\">\n            <div class=\"component_thing\">\n                <div class=\"mouse-is-hover highlight box flex\">\n                    <img class=\"component_icon\" draggable=\"false\" alt=\"directory\" aria-hidden=\"true\">\n                    <span class=\"file-details\">\n                        <form class=\"full-width\">\n                            <input type=\"text\" name=\"name\" value=\"\" class=\"full-width\">\n                            <input type=\"hidden\" name=\"type\" value=\"\">\n                        </form>\n                    </span>\n                    <span class=\"component_action\">\n                        <div class=\"action\">\n                            <div role=\"button\">\n                                <img class=\"component_icon\" draggable=\"false\" src=\"${IMAGE.TRASH}\" alt=\"trash\">\n                            </div>\n                        </div>\n                    </span>\n                </div>\n            </div>\n        </div>\n    `);\n    $node.classList.add(\"hidden\");\n\n    const $input = qs($node, \"input[type=\\\"text\\\"]\");\n    const $icon = qs($node, \".component_icon\");\n    const $remove = qs($node, \".action\");\n\n    // feature1: setup the dom\n    effect(getAction$().pipe(\n        rxjs.map((targetName) => {\n            if (targetName === \"NEW_FILE\") return {\n                targetName,\n                alt: \"file\",\n                img: IMAGE.FILE,\n            };\n            if (targetName === \"NEW_FOLDER\") return {\n                targetName,\n                alt: \"directory\",\n                img: IMAGE.FOLDER,\n            };\n            return null;\n        }),\n        rxjs.filter((val) => val),\n        rxjs.tap(async({ img, alt }) => {\n            $icon.setAttribute(\"src\", `${img}`);\n            $icon.setAttribute(\"alt\", alt);\n            $input.value = \"\";\n            $input.nextElementSibling.setAttribute(\"name\", alt);\n            let done = Promise.resolve(nop);\n            if ($node.classList.contains(\"hidden\")) done = animate($node, {\n                keyframes: [{ height: `0px` }, { height: \"50px\" }],\n                time: 100,\n                fill: \"forwards\",\n            });\n            $node.classList.remove(\"hidden\");\n            render($node);\n            await done;\n            await new Promise(requestAnimationFrame);\n            $input.focus();\n        }),\n    ));\n\n    // feature2: remove the component\n    effect(rxjs.merge(\n        rxjs.merge(\n            onClick($remove),\n            rxjs.fromEvent(window, \"keydown\").pipe(rxjs.filter((e) => e.keyCode === 27)),\n        ).pipe(rxjs.tap(() => setAction(null))),\n        getAction$().pipe(\n            rxjs.filter((actionName) => !actionName),\n        ),\n    ).pipe(rxjs.tap(async() => {\n        await animate($node, {\n            keyframes: [{ height: \"50px\" }, { height: `0px` }],\n            time: 50,\n            fill: \"backwards\",\n        });\n        $node.classList.add(\"hidden\");\n    })));\n\n    // feature3: submit form\n    effect(rxjs.fromEvent($node, \"submit\").pipe(\n        preventDefault(),\n        rxjs.filter(() => $input.value),\n        rxjs.mergeMap(() => {\n            window.dispatchEvent(new KeyboardEvent(\"keydown\", { keyCode: 27 })); // close\n            const type = $input.nextElementSibling.getAttribute(\"name\");\n            if (type === \"file\") return touch(currentPath() + $input.value);\n            return mkdir(currentPath() + $input.value + \"/\");\n        }),\n        rxjs.catchError((err) => console.log(\"ERR\", err)),\n    ));\n}\n\nexport function init() {\n    return loadCSS(import.meta.url, \"./ctrl_newitem.css\");\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/ctrl_submenu.css",
    "content": "/* General design */\n[is=\"component_submenu\"] {\n    margin-top: 10px;\n    position: sticky;\n    top: 0;\n    z-index: 3;\n    padding: 4px 0;\n    transition: 0.2s ease box-shadow;\n}\n[is=\"component_submenu\"]::before {\n    content: \"\";\n    backdrop-filter: blur(15px) saturate(2);\n    position: absolute;\n    inset: 0;\n    z-index: -1;\n}\n.scrolling [is=\"component_submenu\"] {\n    box-shadow: 0 5px 10px rgba(100,100,100,.05);\n    background: rgba(245,245,245,0.9);\n    border-bottom: 1px solid rgba(0,0,0,0.02);\n}\n[is=\"component_submenu\"] .component_submenu {\n    display: flex;\n    justify-content: space-between;\n    padding: 0 15px;\n}\n[is=\"component_submenu\"] .component_submenu::-webkit-scrollbar {\n    display: none;\n}\n[is=\"component_submenu\"] .component_submenu .action button {\n    padding: 6px 10px;\n    background: transparent;\n    text-transform: uppercase;\n    font-size: 0.8rem;\n    color: var(--light);\n    letter-spacing: 0;\n    border-radius: 5px;\n    line-height: 15px;\n}\n[is=\"component_submenu\"] .component_submenu .action button img {\n    width: 15px;\n    height: 15px;\n    margin-left: -3px;\n    margin-right: -3px;\n}\n.touch-yes [is=\"component_submenu\"] .component_submenu .action button img {\n    width: 16px;\n    height: 16px;\n}\n[is=\"component_submenu\"] .component_submenu .action.left {\n    margin-right: 2px;\n    display: flex;\n}\n[is=\"component_submenu\"] .component_submenu .action.left button {\n    min-width: 50px;\n}\n[is=\"component_submenu\"] .component_submenu .action.right.hover,\n.touch-no [is=\"component_submenu\"] .component_submenu .action.right:hover,\n.touch-yes [is=\"component_submenu\"] .component_submenu .action.right,\n.touch-yes [is=\"component_submenu\"] .component_submenu .action.left {\n    background: var(--border);\n}\n.touch-no [is=\"component_submenu\"] .component_submenu .action button:hover {\n    filter: brightness(0.9);\n    background: var(--border);\n}\n.touch-no [is=\"component_submenu\"] .component_submenu .action button:active,\n[is=\"component_submenu\"] .component_submenu .action button:active {\n    filter: brightness(0.75);\n    background: var(--border);\n}\n[is=\"component_submenu\"] .component_submenu .action.left,\n[is=\"component_submenu\"] .component_submenu .action.right {\n    border-radius: 5px;\n    white-space: nowrap;\n}\n[is=\"component_submenu\"] .component_submenu .action.right button[data-bind=\"clear\"] {\n    display: flex;\n    align-items: center;\n    background: var(--border);\n    font-weight: bold;\n}\n[is=\"component_submenu\"] .component_submenu .action.right button[data-bind=\"clear\"] img {\n    padding-left: 2px;\n}\n[is=\"component_submenu\"] .component_submenu .action form input[name=\"q\"] {\n    background: transparent;\n    border: none;\n    padding-left: 5px;\n    color: var(--color);\n    font-size: 0.95rem;\n}\n\n/* dark mode */\n.dark-mode [is=\"component_submenu\"] .component_submenu .action button img {\n    filter: brightness(70%) invert(1);\n}\n.dark-mode [is=\"component_submenu\"] .component_submenu .action button {\n    color: var(--light);\n    font-weight: bold;\n}\n.dark-mode .scrolling [is=\"component_submenu\"] {\n    background: rgba(43, 45, 48, 0.99);\n}\n\n/* ripple effect */\n.touch-no [is=\"component_submenu\"] .component_submenu .action button {\n    position: relative;\n    overflow: hidden;\n}\n.touch-no [is=\"component_submenu\"] .component_submenu .action button::after {\n    content: \"\";\n    position: absolute;\n    top: 50%; left: 50%;\n    width: 10px;\n    height: 10px;\n    background: var(--color);\n    border-radius: 50%;\n    transform: translate(-50%, -50%) scale(0);\n    opacity: 0;\n    pointer-events: none;\n}\n.touch-no [is=\"component_submenu\"] .component_submenu .action button:active::after {\n    animation: ripple 0.2s ease-out;\n}\n@keyframes ripple {\n    0% { transform: translate(-50%, -50%) scale(0); opacity: 0.2; }\n    100% { transform: translate(-50%, -50%) scale(20); opacity: 0; }\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/ctrl_submenu.js",
    "content": "import { createElement, createRender, createFragment, onDestroy } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, onClick, preventDefault } from \"../../lib/rx.js\";\nimport { animate, slideXIn, slideYIn } from \"../../lib/animate.js\";\nimport { basename, join, forwardURLParams } from \"../../lib/path.js\";\nimport assert from \"../../lib/assert.js\";\nimport { qs, qsa, safe } from \"../../lib/dom.js\";\nimport { get as getConfig } from \"../../model/config.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\nimport t from \"../../locales/index.js\";\n\nimport \"../../components/dropdown.js\";\nimport \"../../components/icon.js\";\nimport { createModal } from \"../../components/modal.js\";\n\nimport componentShare from \"./modal_share.js\";\nimport componentTag from \"./modal_tag.js\";\nimport componentRename from \"./modal_rename.js\";\nimport componentDelete from \"./modal_delete.js\";\n\nimport { getSelection$, clearSelection, lengthSelection, expandSelection } from \"./state_selection.js\";\nimport { getAction$, setAction } from \"./state_newthing.js\";\nimport { setState, getState$ } from \"./state_config.js\";\nimport { clearCache } from \"./cache.js\";\nimport { getPermission, calculatePermission } from \"./model_acl.js\";\nimport { currentPath, extractPath } from \"./helper.js\";\n\nimport { rm as rm$, mv as mv$ } from \"./model_files.js\";\nimport { rm as rmVL, mv as mvVL, withVirtualLayer } from \"./model_virtual_layer.js\";\n\nconst modalOpt = {\n    withButtonsRight: t(\"OK\"),\n    withButtonsLeft: t(\"CANCEL\"),\n};\n\nconst rm = (...paths) => withVirtualLayer(\n    rm$(...paths),\n    rmVL(...paths),\n);\n\nconst mv = (from, to) => withVirtualLayer(\n    mv$(from, to),\n    mvVL(from, to),\n);\n\nexport default async function(render) {\n    const $page = createElement(`\n        <div class=\"component_submenu container\" role=\"toolbar\">\n            <div class=\"action left no-select\"></div>\n            <div class=\"action right no-select\"></div>\n        </div>\n    `);\n    render($page);\n    onDestroy(() => clearSelection());\n\n    const getSelectionLength$ = getSelection$().pipe(\n        rxjs.map(() => lengthSelection()), // <- potentially expensive, hence the share\n        rxjs.shareReplay(),\n    );\n    const $scroll = assert.type($page.closest(\".scroll-y\"), HTMLElement);\n    componentLeft(createRender(qs($page, \".action.left\")), { $scroll, getSelectionLength$ });\n    componentRight(createRender(qs($page, \".action.right\")), { getSelectionLength$ });\n\n    effect(rxjs.fromEvent($scroll, \"scroll\", { passive: true }).pipe(\n        rxjs.map((e) => e.target.scrollTop > 12),\n        rxjs.distinctUntilChanged(),\n        rxjs.startWith(false),\n        rxjs.tap((scrolling) => scrolling\n            ? $scroll.classList.add(\"scrolling\")\n            : $scroll.classList.remove(\"scrolling\")),\n    ));\n}\n\nfunction componentLeft(render, { $scroll, getSelectionLength$ }) {\n    effect(getSelectionLength$.pipe(\n        rxjs.filter((l) => l === 0),\n        rxjs.mergeMap(() => getPermission()),\n        rxjs.map(() => render(createFragment(`\n            <button data-action=\"new-file\" title=\"${t(\"New File\")}\"${toggleDependingOnPermission(currentPath(), \"new-file\")} aria-controls=\"newthing\">\n                ${window.innerWidth < 410 && t(\"New File\").length > 10\n        ? t(\"New File\", null, \"NEW_FILE::SHORT\")\n        : t(\"New File\")}\n            </button>\n            <button data-action=\"new-folder\" title=\"${t(\"New Folder\")}\"${toggleDependingOnPermission(currentPath(), \"new-folder\")} aria-controls=\"newthing\">\n                ${window.innerWidth < 410 && t(\"New Folder\").length > 10\n        ? t(\"New Folder\", null, \"NEW_FOLDER::SHORT\")\n        : t(\"New Folder\")}\n            </button>\n        `))),\n        rxjs.mergeMap(($page) => rxjs.merge(\n            onClick(qs($page, `[data-action=\"new-file\"]`)).pipe(rxjs.mapTo(\"NEW_FILE\")),\n            onClick(qs($page, `[data-action=\"new-folder\"]`)).pipe(rxjs.mapTo(\"NEW_FOLDER\")),\n        )),\n        rxjs.mergeMap((actionName) => getAction$().pipe(\n            rxjs.first(),\n            rxjs.map((currentAction) => actionName === currentAction ? null : actionName),\n        )),\n        rxjs.tap((actionName) => {\n            $scroll.scrollTo({\n                top: 0,\n                behavior: window.chrome ? \"smooth\" : \"instant\", // prevent firefox bug\n            });\n            setAction(actionName);\n        }),\n    ));\n    onDestroy(() => setAction(null));\n\n    effect(getSelectionLength$.pipe(\n        rxjs.filter((l) => l === 1),\n        rxjs.map(() => render(createFragment(`\n            <a target=\"_blank\" ${generateLinkAttributes(expandSelection())}><button data-action=\"download\" title=\"${t(\"Download\")}\">\n                ${t(\"Download\")}\n            </button></a>\n            <button data-action=\"delete\"${toggleDependingOnPermission(currentPath(), \"delete\")} title=\"${t(\"Remove\")}\">\n                ${t(\"Remove\")}\n            </button>\n            <button data-action=\"rename\" title=\"${t(\"Rename\")}\"${toggleDependingOnPermission(currentPath(), \"rename\")}>\n                ${t(\"Rename\")}\n            </button>\n            <button data-action=\"share\" title=\"${t(\"Share\")}\" class=\"${(getConfig(\"enable_share\") && !new URLSearchParams(location.search).has(\"share\")) ? \"\" : \"hidden\"}\">\n                ${t(\"Share\")}\n            </button>\n            <button data-action=\"tag\" title=\"${t(\"Tag\")}\" class=\"${getConfig(\"enable_tags\", false) ? \"\" : \"hidden\"}\">\n                ${t(\"Tag\")}\n            </button>\n        `))),\n        rxjs.tap(($buttons) => animate($buttons, { time: 100, keyframes: slideYIn(5) })),\n        rxjs.switchMap(($page) => rxjs.merge(\n            onClick(qs($page, `[data-action=\"download\"]`), { preventDefault: true }).pipe(rxjs.tap(($button) => {\n                let url = $button.parentElement.getAttribute(\"href\");\n                url += \"&name=\" + $button.parentElement.getAttribute(\"download\");\n                window.open(url);\n            })),\n            onClick(qs($page, `[data-action=\"share\"]`)).pipe(rxjs.tap(() => {\n                componentShare(createModal({\n                    withButtonsRight: null,\n                    withButtonsLeft: null,\n                }), { path: expandSelection()[0].path });\n            })),\n            onClick(qs($page, `[data-action=\"tag\"]`)).pipe(rxjs.tap(() => {\n                componentTag(createModal({\n                    ...modalOpt,\n                    withButtonsLeft: null,\n                    withButtonsRight: null,\n                }), { path: expandSelection()[0].path });\n            })),\n            onClick(qs($page, `[data-action=\"rename\"]`)).pipe(rxjs.mergeMap(() => {\n                const path = expandSelection()[0].path;\n                return rxjs.from(componentRename(\n                    createModal(modalOpt),\n                    basename(path.replace(new RegExp(\"/$\"), \"\")),\n                )).pipe(rxjs.mergeMap((val) => {\n                    const [basepath] = extractPath(path);\n                    let newpath = join(location.origin + basepath, val);\n                    if (path.slice(-1) === \"/\" && newpath.slice(-1) !== \"/\") newpath += \"/\";\n                    clearSelection();\n                    clearCache(path);\n                    clearCache(newpath);\n                    return mv(path, newpath);\n                }));\n            })),\n            onClick(qs($page, `[data-action=\"delete\"]`)).pipe(rxjs.mergeMap(() => {\n                const path = expandSelection()[0].path;\n                return rxjs.from(componentDelete(\n                    createModal(modalOpt),\n                    basename(path.replace(new RegExp(\"/$\"), \"\")).substr(0, 15),\n                )).pipe(rxjs.mergeMap(() => {\n                    const selection = expandSelection()[0].path;\n                    clearSelection();\n                    clearCache(path);\n                    return rm(selection);\n                }));\n            })),\n        )),\n    ));\n\n    effect(getSelectionLength$.pipe(\n        rxjs.filter((l) => l > 1),\n        rxjs.map(() => render(createFragment(`\n            <a target=\"_blank\" ${generateLinkAttributes(expandSelection())}><button data-action=\"download\">\n                ${t(\"Download\")}\n            </button></a>\n            <button data-action=\"delete\"${toggleDependingOnPermission(currentPath(), \"delete\")}>\n                ${t(\"Remove\")}\n            </button>\n        `))),\n        rxjs.mergeMap(($page) => rxjs.merge(\n            onClick(qs($page, `[data-action=\"download\"]`), { preventDefault: true }).pipe(rxjs.tap(($button) => {\n                window.open($button.parentElement.getAttribute(\"href\"));\n            })),\n            onClick(qs($page, `[data-action=\"delete\"]`)).pipe(rxjs.mergeMap(() => {\n                const paths = expandSelection().map(({ path }) => path);\n                return rxjs.from(componentDelete(\n                    createModal(modalOpt),\n                    \"remove\",\n                )).pipe(rxjs.mergeMap(() => {\n                    clearSelection();\n                    return rm(...paths);\n                }));\n            })),\n        )),\n    ));\n}\n\nfunction componentRight(render, { getSelectionLength$ }) {\n    const ICONS = {\n        LIST_VIEW: \"PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojNjI2NDY5O2ZpbGwtb3BhY2l0eToxIiBkPSJtIDEzMy4zMzMsNTYgdiA2NCBjIDAsMTMuMjU1IC0xMC43NDUsMjQgLTI0LDI0IEggMjQgQyAxMC43NDUsMTQ0IDAsMTMzLjI1NSAwLDEyMCBWIDU2IEMgMCw0Mi43NDUgMTAuNzQ1LDMyIDI0LDMyIGggODUuMzMzIGMgMTMuMjU1LDAgMjQsMTAuNzQ1IDI0LDI0IHogbSAzNzkuMzM0LDIzMiB2IC02NCBjIDAsLTEzLjI1NSAtMTAuNzQ1LC0yNCAtMjQsLTI0IEggMjEzLjMzMyBjIC0xMy4yNTUsMCAtMjQsMTAuNzQ1IC0yNCwyNCB2IDY0IGMgMCwxMy4yNTUgMTAuNzQ1LDI0IDI0LDI0IGggMjc1LjMzMyBjIDEzLjI1NiwwIDI0LjAwMSwtMTAuNzQ1IDI0LjAwMSwtMjQgeiBtIDAsLTE2OCBWIDU2IGMgMCwtMTMuMjU1IC0xMC43NDUsLTI0IC0yNCwtMjQgSCAyMTMuMzMzIGMgLTEzLjI1NSwwIC0yNCwxMC43NDUgLTI0LDI0IHYgNjQgYyAwLDEzLjI1NSAxMC43NDUsMjQgMjQsMjQgaCAyNzUuMzMzIGMgMTMuMjU2LDAgMjQuMDAxLC0xMC43NDUgMjQuMDAxLC0yNCB6IE0gMTA5LjMzMywyMDAgSCAyNCBDIDEwLjc0NSwyMDAgMCwyMTAuNzQ1IDAsMjI0IHYgNjQgYyAwLDEzLjI1NSAxMC43NDUsMjQgMjQsMjQgaCA4NS4zMzMgYyAxMy4yNTUsMCAyNCwtMTAuNzQ1IDI0LC0yNCB2IC02NCBjIDAsLTEzLjI1NSAtMTAuNzQ1LC0yNCAtMjQsLTI0IHogTSAwLDM5MiB2IDY0IGMgMCwxMy4yNTUgMTAuNzQ1LDI0IDI0LDI0IGggODUuMzMzIGMgMTMuMjU1LDAgMjQsLTEwLjc0NSAyNCwtMjQgdiAtNjQgYyAwLC0xMy4yNTUgLTEwLjc0NSwtMjQgLTI0LC0yNCBIIDI0IEMgMTAuNzQ1LDM2OCAwLDM3OC43NDUgMCwzOTIgWiBtIDE4OS4zMzMsMCB2IDY0IGMgMCwxMy4yNTUgMTAuNzQ1LDI0IDI0LDI0IGggMjc1LjMzMyBjIDEzLjI1NSwwIDI0LC0xMC43NDUgMjQsLTI0IHYgLTY0IGMgMCwtMTMuMjU1IC0xMC43NDUsLTI0IC0yNCwtMjQgSCAyMTMuMzMzIGMgLTEzLjI1NSwwIC0yNCwxMC43NDUgLTI0LDI0IHoiIC8+Cjwvc3ZnPgo=\",\n        GRID_VIEW: \"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgNDQgNDQiPgogIDxwYXRoIGZpbGw9IiM2MjY0NjkiIGQ9Ik0gMTgsNCBIIDYgQyA0LjksNCA0LDQuOSA0LDYgdiAxMiBjIDAsMS4xIDAuOSwyIDIsMiBoIDEyIGMgMS4xLDAgMiwtMC45IDIsLTIgViA2IEMgMjAsNC45IDE5LjEsNCAxOCw0IFoiIC8+CiAgPHBhdGggZmlsbD0iIzYyNjQ2OSIgZD0iTSAzOCw0IEggMjYgYyAtMS4xLDAgLTIsMC45IC0yLDIgdiAxMiBjIDAsMS4xIDAuOSwyIDIsMiBoIDEyIGMgMS4xLDAgMiwtMC45IDIsLTIgViA2IEMgNDAsNC45IDM5LjEsNCAzOCw0IFoiIC8+CiAgPHBhdGggZmlsbD0iIzYyNjQ2OSIgZD0iTSAxOCwyNCBIIDYgYyAtMS4xLDAgLTIsMC45IC0yLDIgdiAxMiBjIDAsMS4xIDAuOSwyIDIsMiBoIDEyIGMgMS4xLDAgMiwtMC45IDIsLTIgViAyNiBjIDAsLTEuMSAtMC45LC0yIC0yLC0yIHoiIC8+CiAgPHBhdGggZmlsbD0iIzYyNjQ2OSIgZD0iTSAzOCwyNCBIIDI2IGMgLTEuMSwwIC0yLDAuOSAtMiwyIHYgMTIgYyAwLDEuMSAwLjksMiAyLDIgaCAxMiBjIDEuMSwwIDIsLTAuOSAyLC0yIFYgMjYgYyAwLC0xLjEgLTAuOSwtMiAtMiwtMiB6IiAvPgo8L3N2Zz4K\",\n\n        CROSS: \"PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MS45NzYgNTEuOTc2Ij4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjUzMzMzMjg1O3N0cm9rZS13aWR0aDoxLjQ1NjgxMTE5IiBkPSJtIDQxLjAwNTMxLDQwLjg0NDA2MiBjIC0xLjEzNzc2OCwxLjEzNzc2NSAtMi45ODIwODgsMS4xMzc3NjUgLTQuMTE5ODYxLDAgTCAyNi4wNjg2MjgsMzAuMDI3MjM0IDE0LjczNzU1MSw0MS4zNTgzMSBjIC0xLjEzNzc3MSwxLjEzNzc3MSAtMi45ODIwOTMsMS4xMzc3NzEgLTQuMTE5ODYxLDAgLTEuMTM3NzcyMiwtMS4xMzc3NjggLTEuMTM3NzcyMiwtMi45ODIwODggMCwtNC4xMTk4NjEgTCAyMS45NDg3NjYsMjUuOTA3MzcyIDExLjEzMTkzOCwxNS4wOTA1NTEgYyAtMS4xMzc3NjQ3LC0xLjEzNzc3MSAtMS4xMzc3NjQ3LC0yLjk4MzU1MyAwLC00LjExOTg2MSAxLjEzNzc3NCwtMS4xMzc3NzIxIDIuOTgyMDk4LC0xLjEzNzc3MjEgNC4xMTk4NjUsMCBMIDI2LjA2ODYyOCwyMS43ODc1MTIgMzYuMzY5NzM5LDExLjQ4NjM5OSBjIDEuMTM3NzY4LC0xLjEzNzc2OCAyLjk4MjA5MywtMS4xMzc3NjggNC4xMTk4NjIsMCAxLjEzNzc2NywxLjEzNzc2OSAxLjEzNzc2NywyLjk4MjA5NCAwLDQuMTE5ODYyIEwgMzAuMTg4NDg5LDI1LjkwNzM3MiA0MS4wMDUzMSwzNi43MjQxOTcgYyAxLjEzNzc3MSwxLjEzNzc2NyAxLjEzNzc3MSwyLjk4MjA5MSAwLDQuMTE5ODY1IHoiIC8+Cjwvc3ZnPgo=\",\n        MAGNIFYING_GLASS: \"PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojNjI2NDY5O2ZpbGwtb3BhY2l0eToxIiBkPSJNNTA1IDQ0Mi43TDQwNS4zIDM0M2MtNC41LTQuNS0xMC42LTctMTctN0gzNzJjMjcuNi0zNS4zIDQ0LTc5LjcgNDQtMTI4QzQxNiA5My4xIDMyMi45IDAgMjA4IDBTMCA5My4xIDAgMjA4czkzLjEgMjA4IDIwOCAyMDhjNDguMyAwIDkyLjctMTYuNCAxMjgtNDR2MTYuM2MwIDYuNCAyLjUgMTIuNSA3IDE3bDk5LjcgOTkuN2M5LjQgOS40IDI0LjYgOS40IDMzLjkgMGwyOC4zLTI4LjNjOS40LTkuNCA5LjQtMjQuNi4xLTM0ek0yMDggMzM2Yy03MC43IDAtMTI4LTU3LjItMTI4LTEyOCAwLTcwLjcgNTcuMi0xMjggMTI4LTEyOCA3MC43IDAgMTI4IDU3LjIgMTI4IDEyOCAwIDcwLjctNTcuMiAxMjgtMTI4IDEyOHoiIC8+Cjwvc3ZnPgo=\",\n\n        SORT: \"PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMjAgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojNjI2NDY5O2ZpbGwtb3BhY2l0eToxIiBkPSJNNDEgMjg4aDIzOGMyMS40IDAgMzIuMSAyNS45IDE3IDQxTDE3NyA0NDhjLTkuNCA5LjQtMjQuNiA5LjQtMzMuOSAwTDI0IDMyOWMtMTUuMS0xNS4xLTQuNC00MSAxNy00MXptMjU1LTEwNUwxNzcgNjRjLTkuNC05LjQtMjQuNi05LjQtMzMuOSAwTDI0IDE4M2MtMTUuMSAxNS4xLTQuNCA0MSAxNyA0MWgyMzhjMjEuNCAwIDMyLjEtMjUuOSAxNy00MXoiIC8+Cjwvc3ZnPgo=\",\n        CHECK: \"PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojOTA5MDkwO2ZpbGwtb3BhY2l0eToxIiBkPSJNMTczLjg5OCA0MzkuNDA0bC0xNjYuNC0xNjYuNGMtOS45OTctOS45OTctOS45OTctMjYuMjA2IDAtMzYuMjA0bDM2LjIwMy0zNi4yMDRjOS45OTctOS45OTggMjYuMjA3LTkuOTk4IDM2LjIwNCAwTDE5MiAzMTIuNjkgNDMyLjA5NSA3Mi41OTZjOS45OTctOS45OTcgMjYuMjA3LTkuOTk3IDM2LjIwNCAwbDM2LjIwMyAzNi4yMDRjOS45OTcgOS45OTcgOS45OTcgMjYuMjA2IDAgMzYuMjA0bC0yOTQuNCAyOTQuNDAxYy05Ljk5OCA5Ljk5Ny0yNi4yMDcgOS45OTctMzYuMjA0LS4wMDF6IiAvPgo8L3N2Zz4K\",\n    };\n\n    const escape$ = rxjs.fromEvent(window, \"keydown\").pipe(\n        rxjs.filter((event) => event.keyCode === 27),\n        rxjs.share(),\n    );\n    const defaultLayout = (view) => {\n        switch (view) {\n        case \"grid\": return `<img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,${ICONS.LIST_VIEW}\" alt=\"grid\" />`;\n        case \"list\": return `<img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,${ICONS.GRID_VIEW}\" alt=\"list\" />`;\n        default: throw new Error(\"NOT_IMPLEMENTED\");\n        }\n    };\n    const defaultSort = (sort) => {\n        return `<img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,${ICONS.SORT}\" alt=\"${sort}\" />`;\n    };\n\n    effect(getSelectionLength$.pipe(\n        rxjs.filter((l) => l === 0),\n        rxjs.mergeMap(() => getState$().pipe(rxjs.first())),\n        rxjs.map(({ view, sort, search }) => ({\n            search,\n            $page: render(createFragment(`\n                <form style=\"display: inline-block;\" onsubmit=\"event.preventDefault()\">\n                    <input value=\"${safe(search)}\" class=\"hidden\" placeholder=\"${t(\"search\")}\" name=\"q\" id=\"searchInput\" aria-label=\"${t(\"search\")}\" tabindex=\"-1\" autocapitalize=\"none\" />\n                </form>\n                <button data-action=\"search\" title=\"${t(\"Search\")}\" aria-controls=\"searchInput\" class=${getConfig(\"enable_search\") ? \"\" : \"hidden\"}>\n                    <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,${ICONS.MAGNIFYING_GLASS}\" alt=\"search\" />\n                </button>\n                <button data-action=\"view\" title=\"${t(\"Layout\")}\">\n                    ${defaultLayout(view)}\n                </button>\n                <button data-action=\"sort\" title=\"${t(\"Sort\")}\" aria-haspopup=\"listbox\" id=\"sortToggle\">\n                    ${defaultSort(sort)}\n                </button>\n                <div class=\"component_dropdown view sort\" data-target=\"sort\" role=\"listbox\" aria-labelledby=\"sortToggle\">\n                    <div class=\"dropdown_container\">\n                        <ul>\n                            <li data-target=\"type\" role=\"option\">\n                                ${t(\"Sort By Type\")}\n                                <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,${ICONS.CHECK}\" alt=\"check\" />\n                            </li>\n                            <li data-target=\"date\" role=\"option\">\n                                ${t(\"Sort By Date\")}\n                            </li>\n                            <li data-target=\"name\" role=\"option\">\n                                ${t(\"Sort By Name\")}\n                            </li>\n                            <li data-target=\"size\" role=\"option\">\n                                ${t(\"Sort By Size\")}\n                            </li>\n                        </ul>\n                    </div>\n                </div>\n            `))\n        })),\n        rxjs.mergeMap(({ $page, search }) => rxjs.merge(\n            // feature: view button\n            onClick(qs($page, `[data-action=\"view\"]`)).pipe(rxjs.tap(($button) => {\n                const $img = $button.querySelector(\"img\");\n                if ($img.getAttribute(\"alt\") === \"list\") {\n                    setState(\"view\", \"grid\");\n                    $button.setAttribute(\"aria-pressed\", \"true\");\n                    $img.setAttribute(\"alt\", \"grid\");\n                    $img.setAttribute(\"src\", \"data:image/svg+xml;base64,\" + ICONS.LIST_VIEW);\n                } else {\n                    setState(\"view\", \"list\");\n                    $button.setAttribute(\"aria-pressed\", \"false\");\n                    $img.setAttribute(\"alt\", \"list\");\n                    $img.setAttribute(\"src\", \"data:image/svg+xml;base64,\" + ICONS.GRID_VIEW);\n                }\n            })),\n            // feature: sort button\n            rxjs.merge(\n                onClick(qs($page, `[data-action=\"sort\"]`)).pipe(rxjs.map(($el) => { // toggle the dropdown\n                    return !$el.nextElementSibling.classList.contains(\"active\");\n                })),\n                escape$.pipe(rxjs.mapTo(false)), // quit the dropdown on esc\n                rxjs.fromEvent(window, \"click\").pipe( // quit when clicking outside the dropdown\n                    rxjs.filter((e) => !e.target.closest(`[data-action=\"sort\"]`) && !e.target.closest(\".dropdown_container\")),\n                    rxjs.mapTo(false),\n                ),\n            ).pipe(\n                rxjs.takeUntil(getSelection$().pipe(rxjs.skip(1))),\n                rxjs.mergeMap((targetStateIsOpen) => {\n                    const $sort = qs($page, `[data-target=\"sort\"]`);\n                    const $lis = qsa($page, `.dropdown_container li`);\n                    targetStateIsOpen\n                        ? $sort.classList.add(\"active\")\n                        : $sort.classList.remove(\"active\");\n                    $sort.setAttribute(\"aria-expanded\", targetStateIsOpen);\n\n                    return onClick($lis).pipe(\n                        rxjs.first(),\n                        rxjs.mergeMap(($el) => getState$().pipe(rxjs.first(), rxjs.map((state) => ({\n                            order: state.order,\n                            $el,\n                        })))),\n                        rxjs.tap(({ $el, order }) => {\n                            setState(\n                                \"sort\", $el.getAttribute(\"data-target\"),\n                                \"order\", order === \"asc\" ? \"des\" : \"asc\",\n                            );\n                            [...$lis].forEach(($li) => {\n                                const $img = $li.querySelector(\"img\");\n                                if ($img) $img.remove();\n                            });\n                            const $img = createElement(`<img class=\"component_icon\" src=\"data:image/svg+xml;base64,${ICONS.CHECK}\" alt=\"check\" />`);\n                            if (order !== \"asc\") $img.classList.add(\"inverted\");\n                            $el.appendChild($img);\n                            $sort.classList.remove(\"active\");\n                        }),\n                    );\n                }),\n            ),\n            // feature: search box\n            rxjs.merge(\n                rxjs.merge(\n                    onClick(qs($page, `[data-action=\"search\"]`)),\n                    rxjs.fromEvent(window, \"keydown\").pipe(\n                        rxjs.filter((e) => (e.ctrlKey || e.metaKey) && e.key === \"f\"),\n                        preventDefault(),\n                    ),\n                ).pipe(rxjs.map(() => qs($page, \"input\").classList.contains(\"hidden\"))),\n                escape$.pipe(rxjs.mapTo(false)),\n                search ? rxjs.of(true) : rxjs.EMPTY,\n            ).pipe(\n                rxjs.takeUntil(getSelection$().pipe(rxjs.skip(1))),\n                rxjs.mergeMap(async(show) => {\n                    const $input = qs($page, \"input\");\n                    const $searchImg = qs($page, \"img\");\n                    if (show) {\n                        $page.classList.add(\"hover\");\n                        $input.classList.remove(\"hidden\");\n                        $searchImg.setAttribute(\"src\", \"data:image/svg+xml;base64,\" + ICONS.CROSS);\n                        $searchImg.setAttribute(\"alt\", \"close\");\n\n                        const $listOfButtons = $page.parentElement.firstElementChild.children;\n                        for (const $item of $listOfButtons) {\n                            $item.classList.add(\"hidden\");\n                        }\n                        setAction(null); // reset new file, new folder\n                        await animate($input, {\n                            keyframes: [{ width: \"0px\" }, { width: \"180px\" }],\n                            time: 200,\n                        });\n                        $input.select();\n                        $input.focus();\n                    } else {\n                        $page.classList.remove(\"hover\");\n                        $searchImg.setAttribute(\"src\", \"data:image/svg+xml;base64,\" + ICONS.MAGNIFYING_GLASS);\n                        $searchImg.setAttribute(\"alt\", \"search\");\n                        await animate($input, {\n                            keyframes: [{ width: \"180px\" }, { width: \"0px\" }],\n                            time: 100,\n                        });\n                        $input.classList.add(\"hidden\");\n                        $input.value = \"\";\n                        const $listOfButtons = $page.parentElement.firstElementChild.children;\n                        for (const $item of $listOfButtons) {\n                            $item.classList.remove(\"hidden\");\n                            animate($item, { time: 100, keyframes: slideXIn(5) });\n                        }\n                        setState(\"search\", \"\");\n                    }\n                    return $input;\n                }),\n                rxjs.mergeMap(($input) => rxjs.merge(\n                    rxjs.fromEvent($input, \"input\"),\n                    rxjs.fromEvent($input, \"change\"),\n                ).pipe(\n                    rxjs.map(() => $input.value),\n                    rxjs.distinctUntilChanged(),\n                    rxjs.tap((val) => setState(\"search\", val)),\n                )),\n            ),\n        )),\n    ));\n    onDestroy(() => setState(\"search\", \"\"));\n\n    effect(getSelectionLength$.pipe(\n        rxjs.filter((l) => l >= 1),\n        rxjs.map((size) => render(createFragment(`\n            <button data-bind=\"clear\">\n                ${size} <component-icon name=\"close\"></component-icon>\n            </button>\n        `))),\n        rxjs.mergeMap(($page) => onClick(qs($page, `[data-bind=\"clear\"]`)).pipe(\n            rxjs.tap(() => clearSelection()),\n            rxjs.takeUntil(getSelection$().pipe(rxjs.skip(1))),\n        )),\n    ));\n}\n\nexport function init() {\n    return Promise.all([\n        loadCSS(import.meta.url, \"../../css/designsystem_dropdown.css\"),\n        loadCSS(import.meta.url, \"./ctrl_submenu.css\"),\n        loadCSS(import.meta.url, \"./modal_share.css\"),\n        loadCSS(import.meta.url, \"./modal_tag.css\"),\n    ]);\n}\n\nfunction generateLinkAttributes(selections) {\n    let filename = \"archive.zip\";\n    let href = \"api/files/zip?\";\n    if (selections.length === 1) {\n        const path = selections[0].path;\n        const regDir = new RegExp(\"/$\");\n        const isDir = regDir.test(path);\n        if (isDir) {\n            filename = basename(path.replace(regDir, \"\")) + \".zip\";\n        } else {\n            filename = basename(path);\n            href = \"api/files/cat?\";\n        }\n    }\n    href += selections.map(({ path }) => \"path=\" + encodeURIComponent(path)).join(\"&\");\n    href = forwardURLParams(href, [\"share\"]);\n    return `href=\"${href}\" download=\"${safe(filename)}\"`;\n}\n\nfunction toggleDependingOnPermission(path, action) {\n    return calculatePermission(path, action) === false ? ` style=\"display:none\"` : \"\";\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/ctrl_upload.css",
    "content": "/***************************/\n/* COMPONENT: UPLOAD QUEUE */\n.component_upload {\n    position: fixed;\n    bottom: 0;\n    right: 00px;\n    z-index: 999;\n    width: 100%;\n    max-width: 550px;\n    box-shadow: 1px 2px 20px rgba(0, 0, 0, 0.1);\n    background: white;\n    box-sizing: border-box;\n    border-top-left-radius: 15px;\n    border-top-right-radius: 15px;\n}\n.dark-mode .component_upload {\n    color: var(--bg-color);\n}\n\n.component_upload h2 {\n    padding: 20px 20px 5px 20px;\n    margin: 0 0 5px 0;\n    font-size: 1.2em;\n    font-weight: normal;\n}\n.component_upload h2 .percent {\n    color: var(--emphasis-primary);\n}\n.component_upload h2 .count_block {\n    display: inline;\n    margin-left: 10px;\n}\n.component_upload h2 .count_block span.grandTotal {\n    font-size: 0.8em;\n    opacity: 0.8;\n}\n.component_upload h2 .count_block span.grandTotal:before {\n    content: \"/\";\n}\n.component_upload h2 .count_block span.completed {\n    opacity: 0.8;\n}\n.component_upload h2 .component_icon {\n    cursor: pointer;\n    margin-left: 10px;\n    width: 20px;\n    float: right;\n}\n.component_upload h3 {\n    padding: 3px 20px 3px 20px;\n    margin: 0;\n    font-size: 0.95rem;\n    font-weight: normal;\n    background: var(--dark);\n    color: var(--super-light);\n}\n.component_upload h3 span {\n    float: right;\n}\n\n.component_upload .stats_content {\n    padding: 5px 10px 5px 20px;\n    clear: both;\n    max-height: 200px;\n    overflow-y: auto;\n    overflow-x: hidden;\n    font-size: 0.85em;\n}\n.component_upload .stats_content:empty {\n    display: none;\n}\n.component_upload .stats_content .error_color {\n    color: var(--error);\n}\n.component_upload .stats_content .todo_color {\n    opacity: 0.7;\n}\n.component_upload .stats_content .file_row {\n    display: flex;\n    flex-direction: row;\n    flex-wrap: wrap;\n    width: 100%;\n}\n.component_upload .stats_content .file_row .file_path {\n    flex: 1;\n    padding: 0px 3px 0px 0;\n    line-height: 25px;\n}\n.component_upload .stats_content .file_row .file_path .speed {\n    font-size: 0.7rem;\n    opacity: 0.7;\n    padding-left: 5px;\n    line-height: 0.7rem;\n}\n.component_upload .stats_content .file_row .file_path .speed:not(:empty):before {\n    content: \"(\";\n}\n.component_upload .stats_content .file_row .file_path .speed:not(:empty):after {\n    content: \")\";\n}\n.component_upload .stats_content .file_row .file_state {\n    width: 50px;\n    line-height: 25px;\n    text-align: right;\n    padding-right: 3px;\n}\n.component_upload .stats_content .file_row .file_control {\n    width: 25px;\n    display: flex;\n    justify-content: end;\n    align-items: center;\n    margin-left: -2px;\n    margin-right: -2px;\n}\n.component_upload .stats_content .file_row .file_control img {\n    display: none;\n    cursor: pointer;\n    height: 22px;\n    width: 22px;\n}\n.component_upload .stats_content .file_row:hover .file_control img {\n    display: inherit;\n    padding-top: 3px;\n}\n\n/***********************/\n/* COMPONENT: FILEZONE */\n[data-bind=\"filemanager-children\"].dropzone {\n    border: 2px dashed!important;\n}\n\n/**************************/\n/* COMPONENT: FILE UPLOAD */\n.component_mobilefileupload {\n    display: inline;\n    position: fixed;\n    bottom: 25px;\n    right: 25px;\n    z-index: 1;\n}\n.component_mobilefileupload input[type=\"file\"] {\n    position: absolute;\n    inset: 0;\n    border-radius: 50%;\n    appearance: none;\n    z-index: -1;\n}\n.component_mobilefileupload input[type=\"file\"]:focus-visible {\n    background: rgba(0, 0, 0, 0.8);\n    outline: 2px solid;\n}\n.component_mobilefileupload input[type=\"file\"]::file-selector-button {\n    display: none;\n}\n.component_mobilefileupload input[type=\"file\"]:focus + label, .component_mobilefileupload input[type=\"file\"] + label:hover {\n    opacity: 0.95;\n}\n.component_mobilefileupload input[type=\"file\"] + label {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n\n    cursor: pointer;\n    background: #57595A;\n    border-radius: 50%;\n    background-position: center;\n    box-shadow: rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px;\n    width: 50px;\n    height: 50px;\n}\n.component_mobilefileupload input[type=\"file\"] + label:hover {\n    transition: background 0.4s;\n    filter: brightness(0.95);\n}\n.component_mobilefileupload input[type=\"file\"] + label:hover::before {\n    content: \"\";\n    width: inherit;\n    height: inherit;\n    position: absolute;\n    border-radius: 100%;\n    animation: halofab 0.8s ease-in-out;\n    z-index: -1;\n}\n@keyframes halofab {\n    0% { border: 1px solid transparent; }\n    100% { border: 25px solid rgba(0,0,0,0.1); opacity: 0 }\n}\n.component_mobilefileupload input[type=\"file\"] + label:active {\n    filter: brightness(0.8);\n}\n.component_mobilefileupload input[type=\"file\"] + label .component_icon {\n    width: 24px;\n    height: 24px;\n    padding: 13px;\n}\n.component_mobilefileupload button {\n    padding: 0;\n    border-radius: inherit;\n}\n\n.mobilefileupload-appear {\n    transform: translateX(75px);\n    transition: transform 0.25s ease;\n}\n.mobilefileupload-appear-active {\n    transition-delay: 0.3s;\n    transform: translateX(0px);\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/ctrl_upload.d.ts",
    "content": "interface HttpContext {\n    xhr: XMLHttpRequest;\n}\n\ndeclare function executeHttp(\n    this: HttpContext,\n    url: string,\n    options: {\n        method: string;\n        headers: Record<string, string>;\n        body: any;\n        progress: (event: ProgressEvent) => void;\n        speed: (event: ProgressEvent) => void;\n    }\n): Promise<any>;\n\nexport function init();\n\nexport default function(any): void;"
  },
  {
    "path": "public/assets/pages/filespage/ctrl_upload.js",
    "content": "import { createElement, createFragment, createRender } from \"../../lib/skeleton/index.js\";\nimport { toHref } from \"../../lib/skeleton/router.js\";\nimport rxjs, { effect, onClick } from \"../../lib/rx.js\";\nimport { forwardURLParams } from \"../../lib/path.js\";\nimport { animate, slideYOut } from \"../../lib/animate.js\";\nimport { qs, qsa } from \"../../lib/dom.js\";\nimport { AjaxError } from \"../../lib/error.js\";\nimport assert from \"../../lib/assert.js\";\nimport { get as getConfig } from \"../../model/config.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\nimport { currentPath, isNativeFileUpload } from \"./helper.js\";\nimport { getPermission, calculatePermission } from \"./model_acl.js\";\nimport { mkdir, save } from \"./model_virtual_layer.js\";\nimport t from \"../../locales/index.js\";\n\nconst workers$ = new rxjs.BehaviorSubject({ tasks: [], size: null });\nconst ABORT_ERROR = new AjaxError(\"aborted\", null, \"ABORTED\");\nconst TUS_CHECKSUM = false;\n\nexport default async function(render) {\n    if (!document.querySelector(`[is=\"component_upload_queue\"]`)) {\n        const $queue = createElement(`<div is=\"component_upload_queue\"></div>`);\n        document.body.appendChild($queue);\n        componentUploadQueue(createRender($queue), { workers$ });\n    }\n\n    effect(getPermission().pipe(\n        rxjs.filter(() => calculatePermission(currentPath(), \"upload\")),\n        rxjs.tap(() => {\n            const $page = createFragment(`\n                <div is=\"component_filezone\"></div>\n                <div is=\"component_upload_fab\"></div>\n            `);\n            componentFilezone(createRender(assert.type($page.children[0], HTMLElement)), { workers$ });\n            componentUploadFAB(createRender(assert.type($page.children[1], HTMLElement)), { workers$ });\n            render($page);\n        }),\n    ));\n}\n\nexport function init() {\n    return loadCSS(import.meta.url, \"./ctrl_upload.css\");\n}\n\nfunction componentUploadFAB(render, { workers$ }) {\n    const $page = createElement(`\n        <div class=\"component_mobilefileupload no-select\">\n            <form>\n                <input type=\"file\" name=\"file\" id=\"mobilefileupload\" multiple tabindex=\"0\" />\n                <label for=\"mobilefileupload\" title=\"${t(\"Upload\")}\" role=\"button\">\n                    <img class=\"component_icon\" draggable=\"false\" alt=\"upload\" aria-hidden=\"true\" src=\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMzg0IDUxMiI+CiAgPHBhdGggZmlsbD0iI2YyZjJmMiIgZD0iTSAzNjAsNDYwIEggMjQgQyAxMC43LDQ2MCAwLDQ1My4zIDAsNDQwIHYgLTEyIGMgMCwtMTMuMyAxMC43LC0yMCAyNCwtMjAgaCAzMzYgYyAxMy4zLDAgMjQsNi43IDI0LDIwIHYgMTIgYyAwLDEzLjMgLTEwLjcsMjAgLTI0LDIwIHoiIC8+CiAgPHBhdGggZmlsbD0iI2YyZjJmMiIgZD0ibSAyMjYuNTUzOSwxNDkuMDAzMDMgdiAxNjEuOTQxIGMgMCw2LjYyNyAtNS4zNzMsMTIgLTEyLDEyIGggLTQ0IGMgLTYuNjI3LDAgLTEyLC01LjM3MyAtMTIsLTEyIHYgLTE2MS45NDEgaCAtNTIuMDU5IGMgLTIxLjM4MiwwIC0zMi4wOSwtMjUuODUxIC0xNi45NzEsLTQwLjk3MSBsIDg2LjA1OSwtODYuMDU4OTk3IGMgOS4zNzMsLTkuMzczIDI0LjU2OSwtOS4zNzMgMzMuOTQxLDAgbCA4Ni4wNTksODYuMDU4OTk3IGMgMTUuMTE5LDE1LjExOSA0LjQxMSw0MC45NzEgLTE2Ljk3MSw0MC45NzEgeiIgLz4KPC9zdmc+Cg==\" />\n                </label>\n            </form>\n        </div>\n    `);\n    effect(rxjs.fromEvent(qs($page, `input[type=\"file\"]`), \"change\").pipe(\n        rxjs.tap(async(e) => {\n            workers$.next({ loading: true });\n            workers$.next(await processFiles(e.target.files));\n        }),\n    ));\n    render($page);\n}\n\nfunction componentFilezone(render, { workers$ }) {\n    const selector = `[data-bind=\"filemanager-children\"]`;\n    const $target = assert.type(qs(document.body, selector), HTMLElement);\n\n    $target.ondragenter = (e) => {\n        if (!isNativeFileUpload(e)) return;\n        $target.classList.add(\"dropzone\");\n    };\n    $target.ondrop = async(e) => {\n        if (!isNativeFileUpload(e)) return;\n        $target.classList.remove(\"dropzone\");\n        e.preventDefault();\n        workers$.next({ loading: true });\n\n        if (e.dataTransfer.items instanceof window.DataTransferItemList) {\n            let i = 0;\n            const tickID = setInterval(() => workers$.next({ loading: true, size: ++i }), 1000);\n            workers$.next(await processItems(e.dataTransfer.items));\n            clearInterval(tickID);\n        } else if (e.dataTransfer.files instanceof window.FileList) {\n            workers$.next(await processFiles(e.dataTransfer.files));\n        } else {\n            assert.fail(\"NOT_IMPLEMENTED - unknown entry type in ctrl_upload.js\");\n        }\n    };\n    $target.ondragleave = (e) => {\n        if (!isNativeFileUpload(e)) return;\n        if (!(e.relatedTarget === null || // eg: drag outside the window\n              !e.relatedTarget.closest(selector) // eg: drag on the breadcrumb, ...\n        )) return;\n        $target.classList.remove(\"dropzone\");\n    };\n    $target.ondragover = (e) => e.preventDefault();\n}\n\nconst MAX_WORKERS = 4;\n\nfunction componentUploadQueue(render, { workers$ }) {\n    const $page = createElement(`\n        <div class=\"component_upload hidden\">\n            <h2 class=\"no-select\">${t(\"Current Upload\")}\n                <div class=\"count_block\">\n                    <span class=\"completed\">0</span>\n                    <span class=\"grandTotal\">0</span>\n                </div>\n                <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MS45NzYgNTEuOTc2Ij4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjUzMzMzMjg1O3N0cm9rZS13aWR0aDoxLjQ1NjgxMTE5IiBkPSJtIDQxLjAwNTMxLDQwLjg0NDA2MiBjIC0xLjEzNzc2OCwxLjEzNzc2NSAtMi45ODIwODgsMS4xMzc3NjUgLTQuMTE5ODYxLDAgTCAyNi4wNjg2MjgsMzAuMDI3MjM0IDE0LjczNzU1MSw0MS4zNTgzMSBjIC0xLjEzNzc3MSwxLjEzNzc3MSAtMi45ODIwOTMsMS4xMzc3NzEgLTQuMTE5ODYxLDAgLTEuMTM3NzcyMiwtMS4xMzc3NjggLTEuMTM3NzcyMiwtMi45ODIwODggMCwtNC4xMTk4NjEgTCAyMS45NDg3NjYsMjUuOTA3MzcyIDExLjEzMTkzOCwxNS4wOTA1NTEgYyAtMS4xMzc3NjQ3LC0xLjEzNzc3MSAtMS4xMzc3NjQ3LC0yLjk4MzU1MyAwLC00LjExOTg2MSAxLjEzNzc3NCwtMS4xMzc3NzIxIDIuOTgyMDk4LC0xLjEzNzc3MjEgNC4xMTk4NjUsMCBMIDI2LjA2ODYyOCwyMS43ODc1MTIgMzYuMzY5NzM5LDExLjQ4NjM5OSBjIDEuMTM3NzY4LC0xLjEzNzc2OCAyLjk4MjA5MywtMS4xMzc3NjggNC4xMTk4NjIsMCAxLjEzNzc2NywxLjEzNzc2OSAxLjEzNzc2NywyLjk4MjA5NCAwLDQuMTE5ODYyIEwgMzAuMTg4NDg5LDI1LjkwNzM3MiA0MS4wMDUzMSwzNi43MjQxOTcgYyAxLjEzNzc3MSwxLjEzNzc2NyAxLjEzNzc3MSwyLjk4MjA5MSAwLDQuMTE5ODY1IHoiIC8+Cjwvc3ZnPgo=\" alt=\"close\">\n            </h2>\n            <h3 class=\"no-select\">&nbsp;<span></span></h3>\n            <div class=\"stats_content\"></div>\n        </div>\n    `);\n    render($page);\n\n    const $content = qs($page, \".stats_content\");\n    const $file = createElement(`\n        <div class=\"file_row todo_color\">\n            <div class=\"file_path ellipsis\"><span class=\"path\"></span><span class=\"speed no-select\"></span></div>\n            <div class=\"file_state no-select\"></div>\n            <div class=\"file_control no-select\"></div>\n        </div>\n   `);\n    const updateTotal = {\n        reset: () => {\n            qs($page, \".grandTotal\").innerText = 0;\n            qs($page, \".completed\").innerText = 0;\n        },\n        addToTotal: (n) => {\n            const $total = qs($page, \".grandTotal\");\n            $total.innerText = parseInt($total.innerText) + n;\n        },\n        incrementCompleted: () => {\n            const $completed = qs($page, \".completed\");\n            $completed.innerText = parseInt($completed.innerText) + 1;\n        },\n    };\n\n    // feature1: close the queue\n    onClick(qs($page, `img[alt=\"close\"]`)).pipe(rxjs.tap(async() => {\n        const cleanup = await animate($page, { time: 200, keyframes: slideYOut(50) });\n        $content.innerHTML = \"\";\n        $page.classList.add(\"hidden\");\n        updateTotal.reset();\n        cleanup();\n    })).subscribe();\n\n    // feature2: setup the task queue in the dom\n    workers$.subscribe(({ tasks, loading = false, size = 0 }) => {\n        if (loading) {\n            $page.classList.remove(\"hidden\");\n            updateDOMGlobalTitle($page, t(\"Loading\")+ \".\".repeat((size+2)%3+1));\n            return;\n        }\n        if (tasks.length === 0) return;\n        updateTotal.addToTotal(tasks.length);\n        const $fragment = document.createDocumentFragment();\n        for (let i = 0; i<tasks.length; i++) {\n            const $task = assert.type($file.cloneNode(true), HTMLElement);\n            $fragment.appendChild($task);\n            $task.setAttribute(\"data-path\", tasks[i][\"path\"]);\n            $task.firstElementChild.firstElementChild.textContent = tasks[i][\"path\"]; // qs($todo, \".file_path span.path\")\n            $task.firstElementChild.firstElementChild.setAttribute(\"title\", tasks[i][\"path\"]);\n            $task.firstElementChild.nextElementSibling.classList.add(\"file_state_todo\"); // qs($todo, \".file_state\")\n            $task.firstElementChild.nextElementSibling.textContent = t(\"Waiting\");\n        }\n        $content.appendChild($fragment);\n        $content.classList.remove(\"hidden\");\n    });\n\n    // feature3: process tasks\n    const ICON = {\n        STOP: \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgdmlld0JveD0iMCAwIDM4NCA1MTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggc3R5bGU9ImZpbGw6ICM2MjY0Njk7IiBkPSJNMCAxMjhDMCA5Mi43IDI4LjcgNjQgNjQgNjRIMzIwYzM1LjMgMCA2NCAyOC43IDY0IDY0VjM4NGMwIDM1LjMtMjguNyA2NC02NCA2NEg2NGMtMzUuMyAwLTY0LTI4LjctNjQtNjRWMTI4eiIgLz4KPC9zdmc+Cg==\",\n        RETRY: \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0Ij48cGF0aCBzdHlsZT0iZmlsbDogIzYyNjQ2OTsiIGQ9Ik0xNy42NSA2LjM1QzE2LjIgNC45IDE0LjIxIDQgMTIgNGMtNC40MiAwLTcuOTkgMy41OC03Ljk5IDhzMy41NyA4IDcuOTkgOGMzLjczIDAgNi44NC0yLjU1IDcuNzMtNmgtMi4wOGMtLjgyIDIuMzMtMy4wNCA0LTUuNjUgNC0zLjMxIDAtNi0yLjY5LTYtNnMyLjY5LTYgNi02YzEuNjYgMCAzLjE0LjY5IDQuMjIgMS43OEwxMyAxMWg3VjRsLTIuMzUgMi4zNXoiLz48L3N2Zz4K\",\n    };\n    const $iconStop = createElement(`<img class=\"component_icon\" draggable=\"false\" src=\"${ICON.STOP}\" alt=\"stop\" title=\"${t(\"Aborted\")}\">`);\n    const $iconRetry = createElement(`<img class=\"component_icon\" draggable=\"false\" src=\"${ICON.RETRY}\" alt=\"retry\">`);\n    const $close = qs($page, `img[alt=\"close\"]`);\n    const updateDOMTaskProgress = ($task, text) => $task.firstElementChild.nextElementSibling.textContent = text;\n    const updateDOMTaskSpeed = ($task, text) => $task.firstElementChild.firstElementChild.nextElementSibling.textContent = formatSpeed(text);\n    const updateDOMGlobalSpeed = (function(workersSpeed) {\n        let last = 0;\n        return (nworker, currentWorkerSpeed) => {\n            workersSpeed[nworker] = currentWorkerSpeed;\n            if (new Date().getTime() - last <= 500) return;\n            last = new Date().getTime();\n            const speed = workersSpeed.reduce((acc, el) => acc + el, 0);\n            const $speed = assert.type($page.firstElementChild?.nextElementSibling?.firstElementChild, HTMLElement);\n            $speed.textContent = formatSpeed(speed);\n        };\n    }(new Array(MAX_WORKERS).fill(0)));\n    const updateDOMGlobalTitle = ($page, text) => $page.firstElementChild.nextElementSibling.firstChild.textContent = text;\n    const updateDOMWithStatus = ($task, { status, exec, nworker }) => {\n        const cancel = () => exec.cancel();\n        const executeMutation = (status) => {\n            switch (status) {\n            case \"todo\":\n                updateDOMGlobalTitle($page, t(\"Running\") + \"...\");\n                break;\n            case \"doing\":\n                const $stop = assert.type($iconStop.cloneNode(true), HTMLElement);\n                updateDOMTaskProgress($task, formatPercent(0));\n                $task.classList.remove(\"error_color\");\n                $task.classList.add(\"todo_color\");\n                $task.setAttribute(\"data-status\", \"running\");\n                $task.firstElementChild.nextElementSibling.nextElementSibling.replaceChildren($stop);\n                $stop.onclick = () => {\n                    cancel();\n                    $task.removeAttribute(\"data-status\");\n                    $task.firstElementChild.nextElementSibling.nextElementSibling.classList.add(\"hidden\");\n                };\n                $close.addEventListener(\"click\", cancel, { once: true });\n                break;\n            case \"done\":\n                updateDOMGlobalTitle($page, t(\"Done\"));\n                updateDOMTaskProgress($task, t(\"Done\"));\n                updateDOMGlobalSpeed(nworker, 0);\n                updateDOMTaskSpeed($task, 0);\n                $task.removeAttribute(\"data-path\");\n                $task.removeAttribute(\"data-status\");\n                $task.classList.remove(\"todo_color\");\n                $task.firstElementChild.nextElementSibling.nextElementSibling.classList.add(\"hidden\");\n                $close.removeEventListener(\"click\", cancel);\n                break;\n            case \"error\":\n                const $retry = assert.type($iconRetry.cloneNode(true), HTMLElement);\n                updateDOMGlobalTitle($page, t(\"Error\"));\n                updateDOMTaskProgress($task, t(\"Error\"));\n                updateDOMGlobalSpeed(nworker, 0);\n                updateDOMTaskSpeed($task, 0);\n\n                $task.removeAttribute(\"data-path\");\n                $task.removeAttribute(\"data-status\");\n                $task.classList.remove(\"todo_color\");\n                $task.classList.add(\"error_color\");\n                $task.firstElementChild.nextElementSibling.nextElementSibling.firstElementChild.remove();\n                $task.firstElementChild.nextElementSibling.nextElementSibling.appendChild($retry);\n                $retry.onclick = async() => {\n                    executeMutation(\"todo\");\n                    executeMutation(\"doing\");\n                    try {\n                        await exec.retry();\n                        executeMutation(\"done\");\n                    } catch (err) {\n                        executeMutation(\"error\");\n                    }\n                };\n                $close.removeEventListener(\"click\", cancel);\n                break;\n            default:\n                assert.fail(`UNEXPECTED_STATUS status=\"${status}\" path=\"${$task.getAttribute(\"path\")}\"`);\n            }\n        };\n        executeMutation(status);\n    };\n\n    let tasks = [];\n    const reservations = new Array(MAX_WORKERS).fill(false);\n    const processWorkerQueue = async(nworker) => {\n        while (tasks.length > 0) {\n            updateDOMGlobalTitle($page, t(\"Running\")+\"...\");\n\n            // step1: retrieve next task\n            const task = nextTask(tasks);\n            if (!task) {\n                await new Promise((resolve) => setTimeout(resolve, 1000));\n                continue;\n            }\n\n            // step2: validate the task is ready to run now\n            const $tasks = qsa($page, `[data-path=\"${task.path}\"][data-status=\"running\"]`);\n            if ($tasks.length > 0) {\n                await new Promise((resolve) => setTimeout(resolve, 1000));\n                tasks.unshift(task);\n                continue;\n            }\n\n            // step3: process the task through its entire lifecycle\n            const $task = qs($page, `[data-path=\"${task.path}\"]`);\n            const exec = task.exec({\n                progress: (progress) => updateDOMTaskProgress($task, formatPercent(progress)),\n                speed: (speed) => {\n                    updateDOMTaskSpeed($task, speed);\n                    updateDOMGlobalSpeed(nworker, speed);\n                },\n            });\n            updateDOMWithStatus($task, { exec, status: \"doing\", nworker });\n            try {\n                await exec.run(task);\n                updateDOMWithStatus($task, { exec, status: \"done\", nworker });\n            } catch (err) {\n                updateDOMWithStatus($task, { exec, status: \"error\", nworker });\n            }\n            updateTotal.incrementCompleted();\n            task.done = true;\n\n            // step4: execute only at task completion\n            if (tasks.length === 0 && // no remaining tasks\n                reservations.filter((t) => t === true).length === 1 // only for the last remaining job\n            ) updateDOMGlobalTitle($page, t(\"Done\"));\n        }\n    };\n    const nextTask = (tasks) => {\n        for (let i=0; i<tasks.length; i++) {\n            const possibleTask = tasks[i];\n            if (!possibleTask.ready()) continue;\n            tasks.splice(i, 1);\n            return possibleTask;\n        }\n        return null;\n    };\n    const noFailureAllowed = (fn) => fn().catch(() => noFailureAllowed(fn));\n    workers$.subscribe(async({ tasks: newTasks, loading = false }) => {\n        if (loading) return;\n        tasks = tasks.concat(newTasks); // add new tasks to the pool\n        while (!$page.classList.contains(\"hidden\")) {\n            const nworker = reservations.indexOf(false);\n            if (nworker === -1) break; // the pool of workers is already to its max\n            reservations[nworker] = true;\n            noFailureAllowed(processWorkerQueue.bind(null, nworker)).then(() => reservations[nworker] = false);\n        }\n        reservations.fill(false);\n    });\n}\n\nclass IExecutor {\n    contructor() {}\n    cancel() { throw new Error(\"NOT_IMPLEMENTED\"); }\n    retry() { throw new Error(\"NOT_IMPLEMENTED\"); }\n    run() { throw new Error(\"NOT_IMPLEMENTED\"); }\n}\n\nfunction workerImplFile({ progress, speed }) {\n    return new class Worker extends IExecutor {\n        constructor() {\n            super();\n            this.xhr = null;\n        }\n\n        /**\n         * @override\n         */\n        cancel() {\n            if (this.xhr) assert.type(this.xhr, XMLHttpRequest).abort();\n            this.xhr = null;\n        }\n\n        /**\n         * @override\n         */\n        async run({ file, path, virtual }) {\n            const _file = await file();\n            const executeJob = () => this.prepareJob({ file: _file, path, virtual });\n            this.retry = () => {\n                virtual.before();\n                return executeJob();\n            };\n            return executeJob();\n        }\n\n        async prepareJob({ file, path, virtual }) {\n            const chunkSize = getConfig(\"upload_chunk_size\", 0) *1024*1024;\n            const numberOfChunks = Math.ceil(file.size / chunkSize);\n            const cacheHeaders = {\n                \"Cache-Control\": \"no-store\",\n                \"Pragma\": \"no-cache\",\n            };\n            const tusHeaders = {\n                \"Tus-Resumable\": \"1.0.0\",\n                ...cacheHeaders,\n            };\n            // Case1: basic upload\n            if (chunkSize === 0 || numberOfChunks === 0 || numberOfChunks === 1) {\n                try {\n                    await executeHttp.call(this, toHref(`/api/files/save?path=${encodeURIComponent(path)}`), {\n                        method: \"POST\",\n                        headers: { ...cacheHeaders },\n                        body: file,\n                        progress,\n                        speed,\n                    });\n                    virtual.afterSuccess();\n                } catch (err) {\n                    virtual.afterError();\n                    if (err === ABORT_ERROR) return;\n                    throw err;\n                }\n                return;\n            }\n\n            // Case2: chunked upload => TUS: https://www.ietf.org/archive/id/draft-tus-httpbis-resumable-uploads-protocol-00.html\n            const apiURL = toHref(`/api/files/save?path=${encodeURIComponent(path)}`);\n            let uploadURL = \"\";\n            let offset = 0;\n            try { // tus: retry mechanism\n                const resp = await executeHttp.call(this, apiURL, {\n                    method: \"HEAD\",\n                    headers: { ...tusHeaders },\n                    body: null,\n                    progress: () => {},\n                    speed,\n                });\n                if (file.size === parseInt(resp.headers[\"upload-length\"])) {\n                    const tmp = parseInt(resp.headers[\"upload-offset\"]);\n                    if (tmp > 0) {\n                        offset = tmp;\n                        uploadURL = apiURL;\n                    }\n                }\n            } catch (err) {}\n            if (offset === 0) { // tus: upload creation\n                try {\n                    const resp = await executeHttp.call(this, apiURL, {\n                        method: \"POST\",\n                        headers: {\n                            ...tusHeaders,\n                            \"Upload-Length\": file.size,\n                        },\n                        body: null,\n                        progress: () => {},\n                        speed,\n                    });\n                    uploadURL = resp.headers.location;\n                    if (!uploadURL.startsWith(toHref(\"/api/files/save?\"))) throw new Error(\"Internal Error\");\n                } catch (err) {\n                    virtual.afterError();\n                    if (err === ABORT_ERROR) return;\n                    throw err;\n                }\n            }\n            try {\n                for (let i=Math.ceil(offset/chunkSize); i<numberOfChunks; i++) {\n                    if (this.xhr === null) break;\n                    const chunk = file.slice(offset, offset + chunkSize);\n                    const headers = {\n                        ...tusHeaders,\n                        \"Content-Type\": \"application/offset+octet-stream\",\n                        \"Upload-Offset\": offset,\n                    };\n                    if (TUS_CHECKSUM && crypto.subtle.digest) {\n                        const hash = await crypto.subtle.digest(\"SHA-1\", await chunk.arrayBuffer());\n                        const checksum = [...new Uint8Array(hash)].map(b => b.toString(16).padStart(2, \"0\")).join(\"\");\n                        headers[\"Upload-Checksum\"] = `sha1 ${checksum}`;\n                    }\n                    await executeHttp.call(this, uploadURL, {\n                        method: \"PATCH\",\n                        headers,\n                        body: chunk,\n                        progress: (p) => {\n                            const start = Math.ceil(100 * offset / file.size);\n                            const end = Math.ceil(100 * Math.min(file.size, offset + chunkSize) / file.size);\n                            progress(Math.floor(start + (end - start) * p / 100));\n                        },\n                        speed,\n                    });\n                    offset += chunkSize;\n                }\n                virtual.afterSuccess();\n            } catch (err) {\n                virtual.afterError();\n                if (err === ABORT_ERROR) return;\n                throw err;\n            }\n        }\n    }();\n}\n\nfunction workerImplDirectory({ progress }) {\n    return new class Worker extends IExecutor {\n        constructor() {\n            super();\n            this.xhr = null;\n        }\n\n        /**\n         * @override\n         */\n        cancel() {\n            assert.type(this.xhr, XMLHttpRequest).abort();\n        }\n\n        /**\n         * @override\n         */\n        async run({ virtual, path }) {\n            const executeJob = () => this.prepareJob({ virtual, path });\n            this.retry = () => {\n                virtual.before();\n                return executeJob();\n            };\n            return executeJob();\n        }\n\n        async prepareJob({ virtual, path }) {\n            let percent = 0;\n            const id = setInterval(() => {\n                percent += 10;\n                if (percent >= 100) {\n                    clearInterval(id);\n                    return;\n                }\n                progress(percent);\n            }, 100);\n            try {\n                await executeHttp.call(this, toHref(`/api/files/mkdir?path=${encodeURIComponent(path)}`), {\n                    method: \"POST\",\n                    headers: {},\n                    body: null,\n                    progress,\n                    speed: () => {},\n                });\n                clearInterval(id);\n                progress(100);\n                virtual.afterSuccess();\n            } catch (err) {\n                clearInterval(id);\n                virtual.afterError();\n                if (err === ABORT_ERROR) return;\n                throw err;\n            }\n        }\n    }();\n}\n\nfunction executeHttp(url, { method, headers, body, progress, speed }) {\n    const xhr = new XMLHttpRequest();\n    const prevProgress = [];\n    this.xhr = xhr;\n    return new Promise((resolve, reject) => {\n        xhr.open(method, forwardURLParams(url, [\"share\"]));\n        xhr.setRequestHeader(\"X-Requested-With\", \"XmlHttpRequest\");\n        xhr.withCredentials = true;\n        for (const key in headers) {\n            xhr.setRequestHeader(key, headers[key]);\n        }\n        xhr.upload.onprogress = (e) => {\n            if (!e.lengthComputable) return;\n            const percent = Math.floor(100 * e.loaded / e.total);\n            progress(percent);\n            if (prevProgress.length === 0) {\n                prevProgress.push(e);\n                return;\n            }\n            prevProgress.push(e);\n\n            const calculateTime = (p1, pm1) => (p1.timeStamp - pm1.timeStamp)/1000;\n            const calculateBytes = (p1, pm1) => p1.loaded - pm1.loaded;\n            let avgSpeed = 0;\n            for (let i=1; i<prevProgress.length; i++) {\n                const p1 = prevProgress[i];\n                const pm1 = prevProgress[i-1];\n                avgSpeed += calculateBytes(p1, pm1) / calculateTime(p1, pm1);\n            }\n            avgSpeed = avgSpeed / (prevProgress.length - 1);\n            speed(avgSpeed);\n            if (e.timeStamp - prevProgress[0].timeStamp > 5000) {\n                prevProgress.shift();\n            }\n        };\n        xhr.upload.onabort = () => reject(ABORT_ERROR);\n        xhr.onerror = (e) => reject(new AjaxError(\"failed\", e, \"FAILED\"));\n        xhr.onload = () => {\n            if ([200, 201, 204].indexOf(xhr.status) === -1) {\n                reject(new Error(xhr.statusText));\n                return;\n            }\n            progress(100);\n            resolve({\n                status: xhr.status,\n                headers: xhr.getAllResponseHeaders()\n                    .split(\"\\r\\n\")\n                    .reduce((acc, el) => {\n                        const tmp = el.split(\": \");\n                        if (typeof tmp[0] === \"string\" && typeof tmp[1] === \"string\") {\n                            acc[tmp[0]] = tmp[1];\n                        }\n                        return acc;\n                    }, {})\n            });\n        };\n        xhr.send(body);\n    });\n}\n\nasync function processFiles(filelist) {\n    const tasks = [];\n    let size = 0;\n    const detectFiletype = (file) => {\n        // the 4096 is an heuristic observed and taken from:\n        // https://stackoverflow.com/questions/25016442\n        // however the proposed answer is just wrong as it doesn't consider folder with\n        // name such as: test.png and as Stackoverflow favor consanguinity with their\n        // point system, I couldn't rectify the proposed answer. The following code is\n        // actually working as expected\n        if (file.size % 4096 !== 0) {\n            return Promise.resolve(\"file\");\n        }\n        return new Promise((resolve) => {\n            const reader = new window.FileReader();\n            const tid = setTimeout(() => reader.abort(), 1000);\n            reader.onload = () => resolve(\"file\");\n            reader.onabort = () => resolve(\"file\");\n            reader.onerror = () => { resolve(\"directory\"); clearTimeout(tid); };\n            reader.readAsArrayBuffer(file);\n        });\n    };\n    for (const currentFile of filelist) {\n        const type = await detectFiletype(currentFile);\n        let path = currentPath() + currentFile.name;\n        let task = null;\n        switch (type) {\n        case \"file\":\n            task = {\n                type: \"file\",\n                file: () => new Promise((resolve) => resolve(currentFile)),\n                path,\n                date: currentFile.lastModified,\n                exec: workerImplFile,\n                virtual: save(path, currentFile.size),\n                done: false,\n                ready: () => true,\n            };\n            size += currentFile.size;\n            break;\n        case \"directory\":\n            path += \"/\";\n            task = {\n                type: \"directory\",\n                path,\n                date: currentFile.lastModified,\n                exec: workerImplDirectory,\n                virtual: mkdir(path),\n                done: false,\n                ready: () => true,\n            };\n            size += 4096;\n            break;\n        default:\n            assert.fail(`NOT_SUPPORTED type=\"${type}\"`);\n        }\n        task.virtual.before();\n        tasks.push(task);\n    }\n    return { tasks, size };\n}\n\nasync function processItems(itemList) {\n    const bfs = async(queue) => {\n        const tasks = [];\n        let size = 0;\n        const basepath = currentPath();\n        while (queue.length > 0) {\n            const entry = queue.shift();\n            const path = basepath + entry.fullPath.substring(1);\n            let task = {};\n            if (entry === null) continue;\n            else if (entry.isFile) {\n                const entrySize = await new Promise((resolve) => {\n                    if (typeof entry.getMetadata !== \"function\") return resolve(-1); // eg: firefox\n                    entry.getMetadata(\n                        ({ size }) => resolve(size),\n                        (err) => resolve(-1),\n                    );\n                });\n                task = {\n                    type: \"file\",\n                    file: () => new Promise((resolve, reject) => entry.file(\n                        (file) => resolve(file),\n                        (error) => reject(error),\n                    )),\n                    path,\n                    exec: workerImplFile,\n                    virtual: save(path, entrySize),\n                    done: false,\n                    ready: () => false,\n                };\n                size += entrySize;\n            } else if (entry.isDirectory) {\n                task = {\n                    type: \"directory\",\n                    path: path + \"/\",\n                    exec: workerImplDirectory,\n                    virtual: mkdir(path),\n                    done: false,\n                    ready: () => false,\n                };\n                const reader = entry.createReader();\n                let q = [];\n                do {\n                    q = await new Promise((resolve) => reader.readEntries(resolve));\n                    queue = queue.concat(q);\n                } while (q.length > 0);\n            } else {\n                assert.fail(\"NOT_IMPLEMENTED - unknown entry type in ctrl_upload.js\");\n            }\n            task.ready = () => {\n                const isInDirectory = (filepath, folder) => folder.indexOf(filepath) === 0;\n                for (let i=0; i<tasks.length; i++) {\n                    // filter out tasks that are NOT dependencies of the current task\n                    if (tasks[i].path === task.path) break;\n                    else if (tasks[i].type === \"file\") continue;\n                    else if (isInDirectory(tasks[i].path, task.path) === false) continue;\n\n                    // block execution unless dependent task has completed\n                    if (tasks[i].done === false) return false;\n                }\n                return true;\n            };\n            task.virtual.before();\n            tasks.push(task);\n        }\n        return { tasks, size };\n    };\n    const entries = [];\n    for (const item of itemList) entries.push(item.webkitGetAsEntry());\n    return await bfs(entries);\n}\n\nfunction formatPercent(number) {\n    return `${number}%`;\n}\n\nfunction formatSize(bytes, si = true) {\n    const thresh = si ? 1000 : 1024;\n    if (Math.abs(bytes) < thresh) {\n        return bytes.toFixed(1) + \"B\";\n    }\n    const units = si\n        ? [\"kB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"]\n        : [\"KiB\", \"MiB\", \"GiB\", \"TiB\", \"PiB\", \"EiB\", \"ZiB\", \"YiB\"];\n    let u = -1;\n    do {\n        bytes /= thresh;\n        ++u;\n    } while (Math.abs(bytes) >= thresh && u < units.length - 1);\n    return bytes.toFixed(1) + units[u];\n}\nfunction formatSpeed(bytes, si = true) {\n    return formatSize(bytes, si)+ \"/s\";\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/helper.js",
    "content": "import { extname } from \"../../lib/path.js\";\nimport { fromHref } from \"../../lib/skeleton/router.js\";\nimport assert from \"../../lib/assert.js\";\n\nconst regexCurrentPath = new RegExp(\"^/files\");\nexport function currentPath() {\n    return decodeURIComponent(fromHref(location.pathname).replace(regexCurrentPath, \"\"));\n}\n\nconst regexDir = new RegExp(\"/$\");\nexport function isDir(path) {\n    return regexDir.test(path);\n}\n\nexport function extractPath(path) {\n    path = path.replace(regexDir, \"\");\n    const p = path.split(\"/\");\n    const filename = p.pop();\n    return [p.join(\"/\") + \"/\", filename];\n}\n\nexport function sort(files, type, order) {\n    switch (type) {\n    case \"name\": return sortByName(files, order);\n    case \"date\": return sortByDate(files, order);\n    case \"size\": return sortBySize(files, order);\n    default: return sortByType(files, order);\n    }\n}\n\nexport const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);\n\nfunction sortByType(files, order) {\n    let tmp;\n    return files.sort(function(fileA, fileB) {\n        tmp = _moveDownward(fileA, fileB);\n        if (tmp !== 0) return tmp;\n\n        tmp = _moveFolderUpward(fileA, fileB);\n        if (tmp !== 0) return tmp;\n\n        tmp = _moveHiddenFilesDownward(fileA, fileB);\n        if (tmp !== 0) return tmp;\n\n        const faname = fileA.name.toLowerCase();\n        const fbname = fileB.name.toLowerCase();\n\n        const aExt = extname(faname);\n        const bExt = extname(fbname);\n\n        if (aExt === bExt) return sortString(faname, fbname, order);\n        return sortString(aExt, bExt, order);\n    });\n}\nfunction sortByName(files, order) {\n    let tmp;\n    return files.sort(function(fileA, fileB) {\n        tmp = _moveDownward(fileA, fileB);\n        if (tmp !== 0) return tmp;\n\n        tmp = _moveFolderUpward(fileA, fileB);\n        if (tmp !== 0) return tmp;\n\n        tmp = _moveHiddenFilesDownward(fileA, fileB);\n        if (tmp !== 0) return tmp;\n\n        return sortString(fileA.name.toLowerCase(), fileB.name.toLowerCase(), order);\n    });\n}\n\nfunction sortByDate(files, order) {\n    return files.sort(function(fileA, fileB) {\n        if (fileB.time === fileA.time) {\n            return sortString(fileA.name, fileB.name, order);\n        }\n        return sortNumber(fileA.time, fileB.time, order);\n    });\n}\n\nfunction sortBySize(files, order) {\n    let tmp;\n    return files.sort(function(fileA, fileB) {\n        tmp = _moveFolderUpward(fileA, fileB);\n        if (tmp !== 0) return tmp;\n        tmp = _moveHiddenFilesDownward(fileA, fileB);\n        if (tmp !== 0) return tmp;\n\n        if (fileB.size === fileA.size) {\n            return sortString(fileA.name, fileB.name, order);\n        }\n        return sortNumber(fileA.size, fileB.size, order);\n    });\n}\n\nfunction sortString(a, b, order) {\n    if (order === \"asc\") return a > b ? +1 : -1;\n    return a < b ? +1 : -1;\n}\n\nfunction sortNumber(a, b, order) {\n    if (order === \"asc\") return b - a;\n    return a - b;\n}\n\nfunction _moveDownward(fileA, fileB) {\n    const aIsLast = fileA.last;\n    const bIsLast = fileB.last;\n\n    if (aIsLast && !bIsLast) return +1;\n    else if (!aIsLast && bIsLast) return -1;\n    return 0;\n}\n\nfunction _moveFolderUpward(fileA, fileB) {\n    const aIsDirectory = [\"directory\", \"link\"].indexOf(fileA.type) !== -1;\n    const bIsDirectory = [\"directory\", \"link\"].indexOf(fileB.type) !== -1;\n\n    if (!aIsDirectory && bIsDirectory) return +1;\n    else if (aIsDirectory && !bIsDirectory) return -1;\n    return 0;\n}\n\nfunction _moveHiddenFilesDownward(fileA, fileB) {\n    const aIsHidden = fileA.name[0] === \".\";\n    const bIsHidden = fileB.name[0] === \".\";\n\n    if (aIsHidden && !bIsHidden) return +1;\n    if (!aIsHidden && bIsHidden) return -1;\n    return 0;\n}\n\nexport const isNativeFileUpload = (e) => JSON.stringify(e.dataTransfer.types.slice(-1)) === \"[\\\"Files\\\"]\";\n\nexport const isAlreadyFocused = () => {\n    const tagName = assert.type(document.activeElement, HTMLElement).tagName;\n    return [\"INPUT\", \"TEXTAREA\"].indexOf(tagName) !== -1;\n};\n"
  },
  {
    "path": "public/assets/pages/filespage/modal.css",
    "content": ".component_modal .modal-error-message {\n    height: 1rem;\n    margin-top: -5px;\n}\n.component_modal .modal-error-message:not(:empty) {\n    animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both;\n    transform: translate3d(0, 0, 0);\n    backface-visibility: hidden;\n    perspective: 1000px;\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/modal_delete.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, preventDefault } from \"../../lib/rx.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport { MODAL_RIGHT_BUTTON } from \"../../components/modal.js\";\nimport t from \"../../locales/index.js\";\n\nexport default function(render, removeLabel) {\n    return document.body.classList.contains(\"touch-yes\")\n        ? renderMobile(render, removeLabel)\n        : renderDesktop(render, removeLabel);\n}\n\nfunction renderDesktop(render, removeLabel) {\n    const $modal = createElement(`\n        <div>\n            <span style=\"white-space: nowrap;\">\n                <span class=\"no-select\">${t(\"Confirm by typing\")} \"</span>${removeLabel}<span class=\"no-select\">\"</span>\n            </span>\n            <form style=\"margin-top: 10px;\">\n                <input class=\"component_input\" type=\"text\" autocomplete=\"new-password\" value=\"\">\n                <div class=\"modal-error-message\"></div>\n            </form>\n        </div>\n    `);\n    const ret = new rxjs.Subject();\n    const isValid = () => $input.value === removeLabel;\n    const $input = qs($modal, \"input\");\n    const pressOK = render($modal, (id) => {\n        if (id !== MODAL_RIGHT_BUTTON) {\n            return;\n        }\n        else if (!isValid()) {\n            qs($modal, \".modal-error-message\").textContent = t(\"Doesn't match\");\n            return ret.toPromise();\n        }\n        ret.next();\n        ret.complete();\n        return ret.toPromise();\n    }).bind(null, MODAL_RIGHT_BUTTON);\n\n    $input.focus();\n\n    effect(rxjs.fromEvent(qs($modal, \"form\"), \"submit\").pipe(\n        preventDefault(),\n        rxjs.tap(pressOK),\n    ));\n\n    return ret.toPromise();\n}\n\nfunction renderMobile(_, removeLabel) {\n    return new Promise((done) => {\n        const value = window.prompt(t(\"Confirm by typing\") + \": \" + removeLabel, \"\");\n        if (value !== removeLabel) {\n            return;\n        }\n        done(null);\n    });\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/modal_rename.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport { extname } from \"../../lib/path.js\";\nimport rxjs, { effect, preventDefault } from \"../../lib/rx.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport { MODAL_RIGHT_BUTTON } from \"../../components/modal.js\";\nimport t from \"../../locales/index.js\";\n\nexport default function(render, filename) {\n    return document.body.classList.contains(\"touch-yes\")\n        ? renderMobile(render, filename)\n        : renderDesktop(render, filename);\n}\n\nfunction renderDesktop(render, filename) {\n    const $modal = createElement(`\n        <div>\n            ${t(\"Rename as\")}:\n            <form style=\"margin-top: 10px;\">\n                <input class=\"component_input\" type=\"text\" autocomplete=\"new-password\" value=\"\">\n                <div class=\"modal-error-message\"></div>\n            </form>\n        </div>\n    `);\n    const ret = new rxjs.Subject();\n    const $input = qs($modal, \"input\");\n    const pressOK = render($modal, function(id) {\n        const value = $input.value.trim();\n        if (id !== MODAL_RIGHT_BUTTON) {\n            return;\n        } else if (!value || value === filename) {\n            qs($modal, \".modal-error-message\").textContent = t(\"Not Valid\");\n            return ret.toPromise();\n        }\n        ret.next(value);\n        ret.complete();\n    }).bind(null, MODAL_RIGHT_BUTTON);\n\n    const ext = extname(filename);\n    $input.value = filename;\n    $input.focus();\n    if (ext === filename) $input.select();\n    else $input.setSelectionRange(0, filename.length - ext.length - 1);\n\n    effect(rxjs.fromEvent(qs($modal, \"form\"), \"submit\").pipe(\n        preventDefault(),\n        rxjs.tap(pressOK),\n    ));\n    return ret.toPromise();\n}\n\nfunction renderMobile(_, filename) {\n    return new Promise((done) => {\n        const value = window.prompt(t(\"Rename as\"), filename);\n        if (!value || value === filename) {\n            return;\n        }\n        done(value);\n    });\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/modal_share.css",
    "content": ".component_share h2 {\n    margin: 0 0 5px 0;\n    font-size: 1.2em;\n    font-weight: 100;\n    display: flex;\n    justify-content: space-between;\n}\n.component_share h2 .component_icon {\n    width: 24px;\n}\n.component_share component-icon {\n    display: block;\n    text-align: center;\n    margin: 20px 0 30px 0;\n}\n.component_share component-icon .component_icon[alt=\"loading\"] {\n    width: 24px;\n}\n\n.component_share .copy {\n    cursor: copy;\n}\n\n.component_share .share--content {\n    margin-bottom: 10px;\n}\n.component_share .share--content.link-type {\n    display: flex;\n    flex-direction: row;\n}\n.component_share .share--content.link-type > button {\n    cursor: pointer;\n    text-align: center;\n    margin-right: 4px;\n    padding: 15px 0;\n    width: 100%;\n    border-radius: 3px;\n    color: rgba(0, 0, 0, 0.3);\n    text-shadow: 0px 0px 1px var(--color);\n    background: var(--bg-color);\n    background-position: center;\n    line-height: 1rem;\n    align-items: center;\n    justify-content: center;\n    display: flex;\n}\n.dark-mode .component_share .share--content.link-type > button {\n    background: var(--color);\n    color: rgba(0,0,0,0.6);\n}\n.component_share .share--content.link-type > button.active {\n    background: var(--primary);\n}\n.component_share .share--content.link-type > button:hover {\n    transition: background 0.4s;\n    background: var(--primary) radial-gradient(circle, transparent 1%, var(--emphasis-primary) 1%) center/15000%;\n}\n.component_share .share--content.link-type > button:active {\n    background-color: #00000033;\n    background-size: 100%;\n    transition: background 0s;\n}\n.component_share .share--content.existing-links {\n    overflow-y: auto;\n    -webkit-overflow-scrolling: touch;\n}\n.component_share .share--content.existing-links .link-details {\n    display: flex;\n    justify-content: space-between;\n    padding: 3px 5px;\n    border: 2px solid var(--super-light);\n    border-left: none;\n    border-right: none;\n    margin-top: -2px;\n    line-height: 20px;\n}\n.touch-yes .component_share .share--content.existing-links .link-details {\n    padding-top: 5px;\n    padding-bottom: 5px;\n}\n.component_share .share--content.existing-links .link-details .role {\n    margin-right: 5px;\n    padding-right: 5px;\n    font-size: 0.8em;\n    opacity: 0.6;\n    min-width: 75px;\n    text-transform: uppercase;\n}\n.component_share .share--content.existing-links .link-details .path {\n    font-size: 0.9em;\n    flex-grow: 2;\n    width: 100%;\n    overflow-x: hidden;\n    overflow-y: hidden;\n}\n.component_share .share--content.existing-links .link-details .link-details--icons {\n    display: flex;\n    flex-direction: row-reverse;\n    flex-wrap: nowrap;\n}\n.component_share .share--content.existing-links .link-details .component_icon {\n    width: 20px;\n    cursor: pointer;\n}\n.touch-yes .component_share .share--content.existing-links .link-details .component_icon {\n    padding: 0 3px;\n}\n.component_share .share--content.advanced-settings, .component_share .share--content.restrictions {\n    max-height: 115px;\n    overflow-y: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-right: 5px;\n}\n\n.component_share .share--content.existing-links {\n    min-height: 80px;\n}\n.component_share .shared-link {\n    display: flex;\n    justify-content: space-between;\n    font-size: .9em;\n    background: var(--bg-color);\n    border: 2px solid var(--primary);\n    color: var(--dark);\n    border-radius: 4px;\n    padding: 0px 5px;\n    margin-top: 10px;\n    margin-bottom: 20px;\n}\n.dark-mode .component_share .shared-link { background: var(--color); }\n.component_share .shared-link input {\n    flex-grow: 1;\n    border: none;\n    height: 25px;\n    font-family: arial;\n    font-size: 1rem;\n    box-sizing: border-box;\n    background: inherit;\n    color: inherit;\n}\n.touch-yes .component_share .shared-link input {\n    height: 30px;\n}\n.component_share .shared-link button {\n    padding: 0 2px;\n}\n.component_share .shared-link img {\n    width: 16px;\n    padding-bottom: 1px;\n}\n.component_share .formbuilder label {\n    display: block;\n}\n.component_share .formbuilder input {\n    background: var(--bg-color);\n    color: var(--color);\n    padding: 5px;\n    border-radius: 4px;\n    border: none;\n}\n.component_share .formbuilder input:focus {\n    border: none;\n}\n\n.component_supercheckbox > label {\n    font-size: 0.95em;\n    font-style: italic;\n}\n.component_supercheckbox > label .label {\n    margin-left: -5px;\n}\n.component_supercheckbox > label input {\n    margin-right: 5px;\n    margin-top: 2px;\n}\n\n.component_supercheckbox > div > input {\n    width: calc(100% - 10px);\n    box-sizing: border-box;\n    margin-bottom: 5px;\n    border: none;\n    border-radius: 3px;\n    font-size: 0.9em;\n    padding: 3px 5px;\n    color: var(--color);\n    background: var(--bg-color);\n    border-bottom: 0;\n}\n\n.dark-mode .component_share .component_checkbox span {\n    background-color: var(--color);\n    border: 2px solid var(--color);\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/modal_share.js",
    "content": "import { createElement, createRender } from \"../../lib/skeleton/index.js\";\nimport { toHref } from \"../../lib/skeleton/router.js\";\nimport rxjs, { effect, onClick } from \"../../lib/rx.js\";\nimport assert from \"../../lib/assert.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { forwardURLParams, join } from \"../../lib/path.js\";\nimport { qs, qsa, safe } from \"../../lib/dom.js\";\nimport { randomString } from \"../../lib/random.js\";\nimport { animate } from \"../../lib/animate.js\";\nimport { createForm, mutateForm } from \"../../lib/form.js\";\nimport { formTmpl } from \"../../components/form.js\";\nimport notification from \"../../components/notification.js\";\nimport t from \"../../locales/index.js\";\n\nimport { currentPath, isDir } from \"./helper.js\";\n\nconst IMAGE = {\n    COPY: \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjUzMzMzMzM2O3N0cm9rZS13aWR0aDowLjYzOTk5OTk5IiBkPSJNNDY0IDBIMTQ0Yy0yNi41MSAwLTQ4IDIxLjQ5LTQ4IDQ4djQ4SDQ4Yy0yNi41MSAwLTQ4IDIxLjQ5LTQ4IDQ4djMyMGMwIDI2LjUxIDIxLjQ5IDQ4IDQ4IDQ4aDMyMGMyNi41MSAwIDQ4LTIxLjQ5IDQ4LTQ4di00OGg0OGMyNi41MSAwIDQ4LTIxLjQ5IDQ4LTQ4VjQ4YzAtMjYuNTEtMjEuNDktNDgtNDgtNDh6TTM2MiA0NjRINTRhNiA2IDAgMCAxLTYtNlYxNTBhNiA2IDAgMCAxIDYtNmg0MnYyMjRjMCAyNi41MSAyMS40OSA0OCA0OCA0OGgyMjR2NDJhNiA2IDAgMCAxLTYgNnptOTYtOTZIMTUwYTYgNiAwIDAgMS02LTZWNTRhNiA2IDAgMCAxIDYtNmgzMDhhNiA2IDAgMCAxIDYgNnYzMDhhNiA2IDAgMCAxLTYgNnoiLz4KPC9zdmc+Cg==\",\n    LOADING: \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0nMTIwcHgnIGhlaWdodD0nMTIwcHgnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIiBjbGFzcz0idWlsLXJpbmctYWx0Ij4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0ibm9uZSIgY2xhc3M9ImJrIj48L3JlY3Q+CiAgPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iNDAiIHN0cm9rZT0ibm9uZSIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48L2NpcmNsZT4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjNmY2ZjZmIiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCI+CiAgICA8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJzdHJva2UtZGFzaG9mZnNldCIgZHVyPSIycyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGZyb209IjAiIHRvPSI1MDIiPjwvYW5pbWF0ZT4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9InN0cm9rZS1kYXNoYXJyYXkiIGR1cj0iMnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB2YWx1ZXM9IjE1MC42IDEwMC40OzEgMjUwOzE1MC42IDEwMC40Ij48L2FuaW1hdGU+CiAgPC9jaXJjbGU+Cjwvc3ZnPgo=\",\n    DELETE: \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0ODIuNDI4IDQ4Mi40MjkiPgogIDxwYXRoIHN0eWxlPSJmaWxsOiM2ZjZmNmY7c3Ryb2tlLXdpZHRoOjAuOTQ3MjAwNTQiIGQ9Im0gMjM5LjcxMDM4LDEwLjg1ODU2NyBjIC0yOS4yODYzMywwLjE0ODk1OSAtNTYuMjI4MjEsMjMuMTU4MTY3IC02MS4xMzg4Myw1MS45OTYxMjkgLTAuMDM1OSw1LjUyMjQ0IC04LjExOTM2LDEuNTIzODUyIC0xMS44MTQxMSwyLjczNDMwMSAtMjEuNjU5MywwLjM1NzE4IC00My4zODAyLC0wLjY3Njg3NSAtNjUuMDA3MTksMC40Mzg0NTIgLTI1Ljc0Mzk2MSwyLjgxNDg5NiAtNDcuMDQxMDg0LDI2LjM4MTc2IC00Ny4xNzMxNzIsNTIuMjkyMTMxIC0xLjcyMjExOCwyMi4zMjI3NyAxMS42Nzg0MSw0NC43NzgwOSAzMi4zMjg3NjgsNTMuNTM1MzIgMS41MDI3NjcsNy4xMzU1IDAuMjE0MTksMTYuMTEyMjggMC42NDM4LDIzLjk1NTY4IDAuMTEwMTQ1LDc1LjI4MzExIC0wLjIxODQzMywxNTAuNTc3MzcgMC4xNjA5NSwyMjUuODUzNjcgMS40ODk4MDUsMjUuODUxOTIgMjMuOTUyNDE0LDQ4LjI5NzYgNDkuODA1NzI0LDQ5Ljc2Njg3IDY4Ljk5NTMyLDAuMjc5OTggMTM4LjAxNjU0LDAuMjI5NjYgMjA3LjAxMzE3LDAuMDI0NyAyNi4wMTg1MiwtMS4yNzY5MSA0OC43MjA1LC0yMy44MzQ0MyA1MC4xOTI0OSwtNDkuODM3NjcgMC4zNjUyOCwtODMuMTUzOTggMC4wNDk3LC0xNjYuMzI1MDggMC4xNTUzOCwtMjQ5LjQ4NTU4IDIwLjg0ODU5LC04LjUyMTk5IDM0LjU5NTY3LC0zMC45NzQ5OSAzMi45NzkzNiwtNTMuNDExMzEgMC4wNzUyLC0yNi4wNzE2MTEgLTIxLjMyNDY5LC00OS45MDA0NDIgLTQ3LjIyOTkxLC01Mi42OTkyMDYgLTI0LjY2MTA5LC0xLjA5MzY1MSAtNDkuNDEyODgsLTAuMDg0ODcgLTc0LjEwNTUsLTAuNDMyOSAtMy45NDgzNywwLjYxMjkxMSAtMi4zMDc4NywtNS4zNzQ4NTkgLTMuODc5MTQsLTcuOTc4OTIzIC03LjI2MTcsLTI3LjU4NzA0MiAtMzQuMzcxMDIsLTQ3Ljg1NDgyMzggLTYyLjkzMTc5LC00Ni43NTE2NDMgeiBtIDEuNTA0MDQsMjguNTMwNzE3IGMgMTUuNDcwMDYsLTAuMzA1NjE5IDMwLjI2NjY3LDExLjA4NDk0OCAzNC4wMzQ0NywyNi4xOTk3MTMgLTIyLjY4MzQsLTAuMDA1OSAtNDUuMzk2OTIsMC4wMTIzMyAtNjguMDYxNTQsLTAuMDA5MiAzLjgwNzAyLC0xNS4wNzAyMDQgMTguMzQxMTcsLTI2LjQxOTgyMyAzNC4wMjcwNywtMjYuMTkwNDYzIHogTSAxMDguNjc4NTEsOTQuMTM2MzYzIGMgODkuNDUyNTcsMC4xMzkxNjYgMTc4LjkyODgzLC0wLjI3NzY1MSAyNjguMzY2NjksMC4yMDcyIDEzLjc0MTMxLDEuNDE4NTc4IDIzLjkyNjY0LDE1LjE3NjA3NyAyMi4yNjY2MiwyOC43MDgzMTcgMC4wMzI1LDE0LjU1MDU0IC0xNC4wNzUxNCwyNi41NjAyNiAtMjguNDA0OTIsMjQuODcxNDIgLTg4LjUwNjYsLTAuMTQwMzcgLTE3Ny4wMzY5MSwwLjI4MDA2IC0yNjUuNTI4OCwtMC4yMDkwNiBDIDkxLjEyNTU5LDE0Ni4yNDIyMyA4MC45ODkyODUsMTMxLjcwMTYzIDgzLjE4MTc5NCwxMTcuNzAxNjggODMuOTcwODg3LDEwNC43NDEzIDk1LjU3NzEzMSw5My45MjA1OTYgMTA4LjY3ODUxLDk0LjEzNjM2MyBaIE0gMzY2LjMzLDE3Ni40NzA2NiBjIC0wLjE0MDc3LDgxLjQyODQ4IDAuMjgwNjIsMTYyLjg4MDcgLTAuMjA5MDUsMjQ0LjI5NDQ3IC0xLjQzODYyLDEzLjkxMTUxIC0xNS40NzY4NSwyNC4xMDU4OCAtMjkuMTUyMzIsMjIuMjc1ODggLTY2LjE5Njc4LC0wLjE0MTYyIC0xMzIuNDE3NDMsMC4yODE3NSAtMTk4LjU5OTQ1LC0wLjIwOTA2IC0xMy44OTE2OSwtMS40NDg4IC0yNC4xMTkwOSwtMTUuNDc1NzUgLTIyLjI3MjE2LC0yOS4xNTc4NiAwLC03OS4wNjc4MSAwLC0xNTguMTM1NjEgMCwtMjM3LjIwMzQzIDgzLjQxMDk4LDAgMTY2LjgyMTk4LDAgMjUwLjIzMjk4LDAgeiIgLz4KICA8cGF0aCBzdHlsZT0iZmlsbDojNmY2ZjZmO3N0cm9rZS13aWR0aDowLjk4MjA4MSIgZD0ibSAxNzEuNjg2NDQsMjQ3LjQ3Mzc5IGMgLTkuMzQ2NzYsMC4xNTY0NCAtMTUuNzQwMzIsOS44ODgwNSAtMTQuMDg2NzMsMTguNzExMzMgMC4xMjM1MSw0Ny42MjcwMSAtMC4yNDQwMSw5NS4yNzkwMyAwLjE3ODM5LDE0Mi44OTA4NyAxLjIwNzY0LDEwLjk3MTM2IDE1LjkxODAzLDE2LjUyNzk0IDI0LjA3MjQ5LDkuMDg0MjUgOC40MTc1OSwtNi44MTg4NyA0LjQ3NDY5LC0xOC44ODM5MiA1LjM0Nzc0LC0yOC4wODEzOCAtMC4xMjQzOSwtNDMuMzcxMjcgMC4yNDUyLC04Ni43Njc4NCAtMC4xNzgzOSwtMTMwLjEyMzgxIC0xLjAzNzk1LC03LjMxNDM5IC03Ljk1MDU0LC0xMi45NTcwNSAtMTUuMzMzNSwtMTIuNDgxMjYgeiIgLz4KICA8cGF0aCBzdHlsZT0iZmlsbDojNmY2ZjZmO3N0cm9rZS13aWR0aDowLjk4MjA4MSIgZD0ibSAyNDAuNTAxMTYsMjQ3LjQ3Mzc5IGMgLTkuMzQ2NDksMC4xNTYxNiAtMTUuNzQwNjcsOS44ODgxNyAtMTQuMDg2NzMsMTguNzExMzMgMC4xMjM1Miw0Ny42MjcwMSAtMC4yNDQwMSw5NS4yNzkwMyAwLjE3ODM5LDE0Mi44OTA4NyAxLjgwNTA0LDE3LjU2NDg5IDMwLjM3NDEyLDE1LjM0MjI3IDI5LjQyMDIzLC0yLjMwMTc2IC0wLjEyMzMzLC00OC45MzY0MiAwLjI0Mzc3LC05Ny44OTgyIC0wLjE3ODM4LC0xNDYuODE5MTggLTEuMDM3MzUsLTcuMzE0MSAtNy45NTEwMSwtMTIuOTU2OTQgLTE1LjMzMzUxLC0xMi40ODEyNiB6IiAvPgogIDxwYXRoIHN0eWxlPSJmaWxsOiM2ZjZmNmY7c3Ryb2tlLXdpZHRoOjAuOTgyMDgxIiBkPSJtIDMwOS4zMTU4OCwyNDcuNDczNzkgYyAtOS4zNDcxMSwwLjE1NTMgLTE1Ljc0MzE0LDkuODg3MTMgLTE0LjA4NjcyLDE4LjcxMTMzIDAuMTIzNTQsNDcuNjI0OTkgLTAuMjQ0MDYsOTUuMjc1ODEgMC4xNzgzOCwxNDIuODg1MTEgMS4xOTg1NiwxMC45NzMyMSAxNS45MTY2NCwxNi41MzYwNiAyNC4wNzA1OCw5LjA5MDAxIDguNDE5MzMsLTYuODE3ODcgNC40NzM2NiwtMTguODg0MiA1LjM0Nzc0LC0yOC4wODEzOCAtMC4xMjQ0MiwtNDMuMzcxMjQgMC4yNDUyNCwtODYuNzY4MDQgLTAuMTc4MzksLTEzMC4xMjM4MSAtMS4wMzY3NCwtNy4zMTMyMSAtNy45NTAxMiwtMTIuOTU2NTggLTE1LjMzMTU5LC0xMi40ODEyNiB6IiAvPgo8L3N2Zz4K\",\n    EDIT: \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjkgMTI5Ij4KICA8cGF0aCBmaWxsPSIjNkY2RjZGIiBkPSJtMTE5LjIsMTE0LjNoLTEwOS40Yy0yLjMsMC00LjEsMS45LTQuMSw0LjFzMS45LDQuMSA0LjEsNC4xaDEwOS41YzIuMywwIDQuMS0xLjkgNC4xLTQuMXMtMS45LTQuMS00LjItNC4xeiIgLz4KICA8cGF0aCBmaWxsPSIjNkY2RjZGIiBkPSJtNS43LDc4bC0uMSwxOS41YzAsMS4xIDAuNCwyLjIgMS4yLDMgMC44LDAuOCAxLjgsMS4yIDIuOSwxLjJsMTkuNC0uMWMxLjEsMCAyLjEtMC40IDIuOS0xLjJsNjctNjdjMS42LTEuNiAxLjYtNC4yIDAtNS45bC0xOS4yLTE5LjRjLTEuNi0xLjYtNC4yLTEuNi01LjktMS43NzYzNmUtMTVsLTEzLjQsMTMuNS01My42LDUzLjVjLTAuNywwLjgtMS4yLDEuOC0xLjIsMi45em03MS4yLTYxLjFsMTMuNSwxMy41LTcuNiw3LjYtMTMuNS0xMy41IDcuNi03LjZ6bS02Mi45LDYyLjlsNDkuNC00OS40IDEzLjUsMTMuNS00OS40LDQ5LjMtMTMuNiwuMSAuMS0xMy41eiIvPgo8L3N2Zz4K\",\n};\n\nexport default function(render, { path }) {\n    const $modal = createElement(`\n        <div class=\"component_share\">\n            <h2>${t(\"Create a New Link\")}</h2>\n            <div class=\"share--content link-type no-select\">\n                <button data-role=\"viewer\">${t(\"Viewer\")}</button>\n                <button data-role=\"editor\">${t(\"Editor\")}</button>\n                <button data-role=\"uploader\" class=\"${isDir(path) ? \"\" : \"hidden\"}\">${t(\"Uploader\")}</button>\n            </div>\n            <div data-bind=\"share-body\"></div>\n        </div>\n    `);\n    render($modal);\n    const ret = new rxjs.Subject();\n    const role$ = new rxjs.BehaviorSubject(null);\n\n    const state = {\n        /** @type {object} */ form: {},\n        /** @type {any[] | null} */ links: null,\n    };\n\n    // feature: select\n    const toggle = (val) => rxjs.mergeMap(() => {\n        state.form = {};\n        role$.next(role$.value === val ? null : val);\n        return rxjs.EMPTY;\n    });\n    effect(rxjs.merge(\n        onClick(qs($modal, `[data-role=\"viewer\"]`)).pipe(toggle(\"viewer\")),\n        onClick(qs($modal, `[data-role=\"editor\"]`)).pipe(toggle(\"editor\")),\n        onClick(qs($modal, `[data-role=\"uploader\"]`)).pipe(toggle(\"uploader\")),\n        role$.asObservable(),\n    ).pipe(rxjs.tap(() => {\n        const ctrl = role$.value === null ? ctrlListShares : ctrlCreateShare;\n\n        // feature: set active button\n        for (const $button of qs($modal, \".share--content\").children) {\n            $button.getAttribute(\"data-role\") === role$.value\n                ? $button.classList.add(\"active\")\n                : $button.classList.remove(\"active\");\n        }\n\n        // feature: render body and associated events\n        ctrl(createRender(qs($modal, `[data-bind=\"share-body\"]`)), {\n            formState: {\n                path,\n                ...state.form,\n            },\n            formLinks: state.links,\n            load: (data) => {\n                const role = shareObjToRole(data);\n                state.form = {\n                    ...data,\n                    url_enable: !!data.url,\n                    password_enable: !!data.password,\n                    expire_enable: !!data.expire,\n                    users_enable: !!data.users,\n                };\n                role$.next(role);\n            },\n            save: async({ id, ...data }) => {\n                const body = { id, path, ...data, ...roleToShareObj(role$.value) };\n                await ajax({\n                    method: \"POST\",\n                    body,\n                    url: `api/share/${id}`,\n                }).toPromise();\n                assert.truthy(state.links).push({\n                    ...body,\n                    path: body.path.substring(currentPath().length - 1),\n                });\n                role$.next(null);\n            },\n            remove: async({ id }) => {\n                await ajax({\n                    method: \"DELETE\",\n                    url: `api/share/${id}`,\n                }).toPromise();\n                state.links = (state.links || []).filter((link) => link && link.id !== id);\n                role$.next(null);\n            },\n            all: async() => {\n                const { responseJSON } = await ajax({\n                    url: `api/share?path=` + encodeURIComponent(path),\n                    method: \"GET\",\n                    responseType: \"json\",\n                }).toPromise();\n                const currentFolder = path.replace(new RegExp(\"/$\"), \"\").split(\"/\").pop();\n                const sharedLinkIsFolder = new RegExp(\"/$\").test(path);\n                state.links = responseJSON.results.map((obj) => {\n                    obj.path = sharedLinkIsFolder\n                        ? `./${currentFolder}${obj.path}`\n                        : `./${currentFolder}`;\n                    return obj;\n                }).sort((a, b) => {\n                    if (a.path === b.path) return a.id > b.id ? 1 : -1;\n                    return a.path > b.path ? 1 : -1;\n                });\n                return state.links;\n            },\n        });\n    })));\n\n    return ret.toPromise();\n}\n\nasync function ctrlListShares(render, { load, remove, all, formLinks }) {\n    const $page = createElement(`\n        <div class=\"hidden\">\n            <h2>${t(\"Existing Links\")}</h2>\n            <div class=\"share--content existing-links\" style=\"max-height: 90px;\">\n                 <component-icon name=\"loading\"></component-icon>\n            </div>\n        </div>\n    `);\n    render($page);\n\n    effect(rxjs.merge(\n        rxjs.of(formLinks).pipe(rxjs.filter((val) => val !== null)),\n        rxjs.from(all()),\n    ).pipe(rxjs.tap((links) => {\n        if (links.length === 0) {\n            $page.classList.add(\"hidden\");\n            return;\n        }\n        $page.classList.remove(\"hidden\");\n\n        const $fragment = document.createDocumentFragment();\n        const $content = qs($page, \".share--content\");\n        let length = links.length;\n        links.forEach((shareObj) => {\n            const $share = createElement(`\n                <div class=\"link-details no-select\">\n                    <div class=\"copy role ellipsis\">${t(shareObjToRole(shareObj))}</div>\n                    <div class=\"copy path ellipsis\" title=\"${safe(shareObj.path)}\">${safe(shareObj.path)}</div>\n                    <div class=\"link-details--icons\">\n                        <img class=\"component_icon\" draggable=\"false\" src=\"${IMAGE.DELETE}\" alt=\"delete\">\n                        <img class=\"component_icon\" draggable=\"false\" src=\"${IMAGE.EDIT}\" alt=\"edit\">\n                    </div>\n                </div>\n            `);\n            qsa($share, \".copy\").forEach(($el) => $el.onclick = () => {\n                const link = location.origin + forwardURLParams(toHref(`/s/${shareObj.id}`), [\"share\"]);\n                copyToClipboard(link);\n                notification.info(t(\"The link was copied in the clipboard\"));\n            });\n            qs($share, `[alt=\"delete\"]`).onclick = async() => {\n                $share.remove();\n                length -= 1;\n                if (length === 0) $content.replaceChildren(createElement(`\n                    <component-icon name=\"loading\"></component-icon>\n                `));\n                await remove(shareObj);\n            };\n            qs($share, `[alt=\"edit\"]`).onclick = () => load(shareObj);\n            $fragment.appendChild($share);\n        });\n        $content.replaceChildren($fragment);\n    })));\n}\n\nasync function ctrlCreateShare(render, { save, formState }) {\n    const enc = (p) => encodeURIComponent(p).replaceAll(\"%2F\", \"/\");\n    if (formState.path) formState.path = join(\n        location.origin + enc(currentPath()),\n        enc(formState.path),\n    );\n    let id = formState.id || randomString(7);\n    const $page = createElement(`\n        <div>\n            <h2 class=\"no-select pointer\">${t(\"Advanced\")}<span><img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPHBhdGggc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MC41MzMzMzMyMSIgZD0ibSA3LjcwNSw4LjA0NSA0LjU5LDQuNTggNC41OSwtNC41OCAxLjQxLDEuNDEgLTYsNiAtNiwtNiB6IiAvPgogIDxwYXRoIGZpbGw9Im5vbmUiIGQ9Ik0wLS4yNWgyNHYyNEgweiIgLz4KPC9zdmc+Cg==\" alt=\"arrow_bottom\"></span></h2>\n            <form class=\"share--content restrictions no-select\"></form>\n            <div class=\"shared-link\">\n                <input name=\"create\" class=\"copy\" type=\"text\" readonly=\"\" value=\"${location.origin}${toHref(\"/s/\" + id)}\">\n                <button title=\"Copy URL\">\n                    <img class=\"component_icon\" draggable=\"false\" src=\"${IMAGE.COPY}\" alt=\"copy\">\n                </button>\n            </div>\n        </div>\n    `);\n    render($page);\n    const $body = qs($page, \".restrictions\");\n\n    // feature1: setup the shared link form\n    const formSpec = {\n        users_enable: {\n            type: \"enable\",\n            label: t(\"Only for users\"),\n            target: [\"users\"],\n            default: false,\n        },\n        users: {\n            id: \"users\",\n            type: \"text\",\n            placeholder: \"name0@email.com,name1@email.com\",\n        },\n        password_enable: {\n            label: t(\"Password\"),\n            type: \"enable\",\n            target: [\"password\"],\n            default: false,\n        },\n        password: {\n            id: \"password\",\n            type: \"text\",\n            placeholder: t(\"Password\"),\n        },\n        expire_enable: {\n            label: t(\"Expiration\"),\n            type: \"enable\",\n            target: [\"expire\"],\n            default: false,\n        },\n        expire: {\n            id: \"expire\",\n            type: \"date\",\n        },\n        url_enable: {\n            label: \"link\",\n            type: \"enable\",\n            target: [\"link\"],\n            default: false,\n        },\n        url: {\n            id: \"link\",\n            type: \"text\",\n        },\n        path: {\n            type: \"hidden\",\n        },\n    };\n    const tmpl = formTmpl({\n        renderNode: () => createElement(\"<div></div>\"),\n        renderLeaf: ({ label, type }) => {\n            if (type !== \"enable\") return createElement(\"<label></label>\");\n            const title =\n                  label === \"users_enable\"\n                      ? t(\"Only for users\")\n                      : label === \"expire_enable\"\n                          ? t(\"Expiration\")\n                          : label === \"password_enable\"\n                              ? t(\"Password\")\n                              : label === \"url_enable\"\n                                  ? t(\"Custom Link url\")\n                                  : assert.fail(\"unknown label\");\n            return createElement(`\n                <div class=\"component_supercheckbox\">\n                    <label class=\"ellipsis\">\n                        <span data-bind=\"children\"></span>\n                        <span class=\"label\">${title}</span>\n                    </label>\n                </div>\n            `);\n        },\n    });\n    const $form = await createForm(mutateForm(formSpec, formState), tmpl);\n    $body.replaceChildren($form);\n    const clientHeight = $body.offsetHeight;\n    $body.classList.add(\"hidden\");\n    qs($page, \"h2\").onclick = async() => { // toggle advanced button\n        if ($body.classList.contains(\"hidden\")) {\n            $body.classList.remove(\"hidden\");\n            await animate($body, {\n                time: 200,\n                keyframes: [{ height: \"0\" }, { height: `${clientHeight}px` }],\n            });\n            return;\n        }\n        await animate($body, {\n            time: 100,\n            keyframes: [{ height: `${clientHeight}px` }, { height: \"0\" }],\n        });\n        $body.classList.add(\"hidden\");\n    };\n    // sync editable custom link input with link id\n    effect(rxjs.fromEvent(qs($form, `[name=\"url\"]`), \"keyup\").pipe(rxjs.tap((e) => {\n        id = e.target.value.replaceAll(new RegExp(\"[^0-9A-Za-z\\-]\", \"g\"), \"\");\n        qs(assert.type($form.closest(\".component_share\"), HTMLElement), `input[name=\"create\"]`).value = `${location.origin}${toHref(\"/s/\" + id)}`;\n    })));\n\n    // feature: create a shared link\n    const $copy = qs($page, `[alt=\"copy\"]`);\n    effect(onClick(qs($page, \".shared-link\")).pipe(\n        rxjs.first(),\n        rxjs.switchMap(async() => {\n            const form = new FormData(assert.type(qs(document.body, \".component_share form\"), HTMLFormElement));\n            const body = [...form].reduce((acc, [key, value]) => {\n                if (form.has(`${key}_enable`)) acc[key] = value;\n                return acc;\n            }, { id, path: form.get(\"path\") });\n            $copy.setAttribute(\"src\", IMAGE.LOADING);\n            const link = location.origin + forwardURLParams(toHref(`/s/${id}`), [\"share\"]);\n            await save(body);\n            copyToClipboard(link);\n            notification.info(t(\"The link was copied in the clipboard\"));\n        }),\n        rxjs.catchError((err) => {\n            $copy.setAttribute(\"src\", IMAGE.COPY);\n            notification.error(t(err.message));\n            throw err;\n        }),\n        rxjs.retry(),\n    ));\n}\n\nfunction roleToShareObj(role) {\n    return {\n        can_read: (function(r) {\n            if (r === \"viewer\") return true;\n            else if (r === \"editor\") return true;\n            return false;\n        }(role)),\n        can_write: (function(r) {\n            if (r === \"editor\") return true;\n            return false;\n        }(role)),\n        can_upload: (function(r) {\n            if (r === \"uploader\") return true;\n            else if (r === \"editor\") return true;\n            return false;\n        }(role)),\n    };\n}\n\nfunction shareObjToRole({ can_read, can_write, can_upload }) {\n    if (can_read === true && can_write === false && can_upload === false) {\n        return \"viewer\";\n    } else if (can_read === false && can_write === false && can_upload === true) {\n        return \"uploader\";\n    } else if (can_read === true && can_write === true && can_upload === true) {\n        return \"editor\";\n    }\n    return undefined;\n}\n\nexport function copyToClipboard(str) {\n    if (!str) return;\n    const $input = document.createElement(\"input\");\n    $input.setAttribute(\"type\", \"text\");\n    $input.setAttribute(\"style\", \"position: absolute; top:0;left:0;background:red\");\n    $input.setAttribute(\"display\", \"none\");\n    document.body.appendChild($input);\n    $input.value = str;\n    $input.select();\n    document.execCommand(\"copy\");\n    $input.remove();\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/modal_tag.css",
    "content": ".component_tag {\n    font-size: 1rem;\n}\n.component_tag form {\n    margin-bottom: 15px;\n}\n.component_tag form input {\n    font-size: 1rem;\n    border: 2px solid var(--border);\n    border-radius: 5px;\n    padding: 5px 10px;\n    color: rgba(0,0,0,0.75);\n    width: 100%;\n    display: block;\n    box-sizing: border-box;\n}\n.component_tag form input::placeholder {\n    color: rgba(0,0,0,0.2);\n}\n.component_tag [data-bind=\"taglist\"] {\n    display: flex;\n    gap: 5px;\n    flex-wrap: wrap;\n    margin: -3px 0 0px 0px;\n    padding: 0;\n}\n.component_tag [data-bind=\"taglist\"] .component_skeleton {\n    margin: 0;\n    height: 29px;\n}\n.component_tag .tag {\n    display: flex;\n    background: #f2f2f2;\n    transition: all 0.1s ease;\n    padding: 3px 5px 3px 12px;\n    border-radius: 5px;\n    margin: 0;\n    font-size: 0.95rem;\n    cursor: pointer;\n    letter-spacing: -0.5px;\n    text-transform: lowercase;\n}\n.component_tag .tag.active {\n    background: var(--dark);\n    color: #f2f2f2;\n    box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);\n}\n.component_tag .tag svg {\n    width: 15px;\n    height: 15px;\n    border-radius: 50%;\n    margin-left: 5px;\n    padding: 4px;\n}\n.touch-no .component_tag .tag svg:hover {\n    background: rgba(0, 0, 0, 0.05);\n}\n.touch-no .component_tag .tag.active svg:hover {\n    background: rgba(255, 255, 255, 0.15);\n}\n.component_tag .tag img[alt=\"close\"] {\n    width: 14px;\n    padding: 5px;\n}\n.component_tag .scroll-y {\n    overflow-y: auto !important;\n    max-height: 200px;\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/modal_tag.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, onClick } from \"../../lib/rx.js\";\nimport { forwardURLParams } from \"../../lib/path.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport assert from \"../../lib/assert.js\";\nimport { generateSkeleton } from \"../../components/skeleton.js\";\nimport t from \"../../locales/index.js\";\n\nconst shareID = new URLSearchParams(location.search).get(\"share\");\n\nconst $tmpl = createElement(`\n    <div class=\"tag no-select\">\n        <div class=\"ellipsis\">Projects</div>\n        <svg style=\"${shareID ? \"opacity:0.2\" : \"\"}\" class=\"component_icon\" draggable=\"false\" alt=\"close\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n            <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n            <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n        </svg>\n    </div>\n`);\n\nexport default async function(render, { path }) {\n    const $modal = createElement(`\n        <div class=\"component_tag\">\n            <form class=\"${shareID ? \"hidden\" : \"\"}\">\n                <input name=\"tag\" type=\"text\" placeholder=\"${t(\"Add a Tag\")}\" value=\"\">\n            </form>\n            <div class=\"scroll-y\" data-bind=\"taglist\">\n                ${generateSkeleton(1)}\n            </div>\n        </div>\n    `);\n    render($modal);\n\n    const tags$ = new rxjs.BehaviorSubject(await rxjs.zip(\n        ajax({\n            url: forwardURLParams(`api/metadata?path=${path}`, [\"share\"]),\n            method: \"GET\",\n            responseType: \"json\",\n        }).pipe(\n            rxjs.map(({ responseJSON }) =>\n                responseJSON.results\n                    .reduce((acc, { id, value }) => {\n                        if (id !== \"tags\") return acc;\n                        acc = acc.concat(value.split(\", \"));\n                        return acc;\n                    }, [])\n            ),\n        ),\n        shareID ? rxjs.of([]) : ajax({\n            url: forwardURLParams(\"api/metadata/search\", [\"share\"]),\n            method: \"POST\",\n            responseType: \"json\",\n            body: { path: \"/\", tags: [] },\n        }).pipe(\n            rxjs.map(({ responseJSON }) =>\n                Object\n                    .values(responseJSON.results)\n                    .reduce((acc, forms) => forms.reduce((facc, { id, value }) => {\n                        if (id !== \"tags\") return facc;\n                        const vals = value.split(\", \");\n                        for (let i=0; i<vals.length; i++) {\n                            if (facc.indexOf(vals[i]) !== -1) continue;\n                            facc.push(vals[i]);\n                        }\n                        return facc;\n                    }, acc), []),\n            ),\n        ),\n    ).pipe(rxjs.map(([currentTags, allTags]) => {\n        const out = currentTags.map((name) => ({ name, active: true }));\n        for (let i=0; i<allTags.length; i++) {\n            if (currentTags.indexOf(allTags[i]) === -1) {\n                out.push({ name: allTags[i], active: false });\n            }\n        }\n        if (out.length === 0 && !shareID) {\n            return [{ name: t(\"bookmark\"), active: false }];\n        }\n        return out;\n    })).toPromise());\n    const save = (tags) => ajax({\n        url: forwardURLParams(`api/metadata?path=${path}`, [\"share\"]),\n        method: \"POST\",\n        body: tags.length === 0\n            ? []\n            : [{ id: \"tags\", type: \"hidden\", value: tags.join(\", \") }],\n    }).pipe(rxjs.tap(() => window.dispatchEvent(new Event(\"filestash::tag\"))));\n\n    // feature: create DOM\n    const dom$ = tags$.pipe(\n        rxjs.map((tags) => tags.sort((a, b) => a.name > b.name ? 1 : -1)),\n        rxjs.map((tags) => tags.map(({ name, active }) => {\n            const $el = assert.type($tmpl, HTMLElement).cloneNode(true);\n            $el.firstElementChild.innerText = name;\n            if (active) $el.classList.add(\"active\");\n            qs($el, \"svg\").onclick = (e) => {\n                if (shareID) return;\n                e.preventDefault();\n                e.stopPropagation();\n                $el.classList.remove(\"active\");\n                tags$.next(tags$.value.filter((tag) => {\n                    return tag.name !== $el.innerText.trim();\n                }));\n                save(tags$.value\n                    .filter(({ active }) => active)\n                    .map(({ name }) => name)).toPromise();\n            };\n            return $el;\n        })),\n        rxjs.tap(($nodes) => {\n            const $container = qs($modal, `[data-bind=\"taglist\"]`);\n            if ($nodes.length === 0) $container.replaceChildren(createElement(`<div class=\"center full-width\">∅</div>`));\n            else $container.replaceChildren(...$nodes);\n        }),\n    );\n\n    // feature: tag creation\n    effect(rxjs.fromEvent(qs($modal, \"form\"), \"submit\").pipe(\n        rxjs.filter(() => !shareID),\n        rxjs.tap((e) => {\n            e.preventDefault();\n            const tagname = assert.typeof(new FormData(e.target).get(\"tag\"), \"string\").toLowerCase().trim();\n            if (!tagname) return;\n            else if (tags$.value.find(({ name }) => name === tagname)) return;\n            qs($modal, `input[name=\"tag\"]`).value = \"\";\n            tags$.next(tags$.value.concat({\n                name: tagname,\n                active: true,\n            }));\n        }),\n        rxjs.mergeMap(() => save(\n            tags$.value\n                .filter(({ active }) => !!active)\n                .map(({ name }) => name)\n        )),\n    ));\n\n    // feature: toggle tags\n    effect(dom$.pipe(\n        rxjs.mergeMap(($nodes) => rxjs.merge(\n            ...$nodes.map(($node) => onClick($node)),\n        ).pipe(\n            rxjs.filter(() => !shareID),\n            rxjs.tap(($node) => $node.classList.toggle(\"active\")),\n            rxjs.debounceTime(800),\n            rxjs.map(() =>\n                $nodes\n                    .filter(($node) => $node.classList.contains(\"active\"))\n                    .map(($node) => $node.innerText.trim())\n                    .filter((text) => !!text)\n            ),\n        )),\n        rxjs.mergeMap((tags) => save(tags)),\n    ));\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/model_acl.js",
    "content": "import rxjs from \"../../lib/rx.js\";\n\nconst perms$ = new rxjs.BehaviorSubject({});\n\nexport function setPermissions(path, value = {}) {\n    if (JSON.stringify(value) === JSON.stringify(perms$.value[path])) return;\n    perms$.next({\n        ...perms$.value,\n        [path]: value,\n    });\n}\n\nexport function calculatePermission(path, action) {\n    const toBool = (n) => n === undefined ? true : n;\n    if (!perms$.value[path]) return false;\n    switch (action) {\n    case \"new-file\": return toBool(perms$.value[path][\"can_create_file\"]);\n    case \"new-folder\": return toBool(perms$.value[path][\"can_create_directory\"]);\n    case \"delete\": return toBool(perms$.value[path][\"can_delete\"]);\n    case \"rename\": return toBool(perms$.value[path][\"can_move\"]);\n    case \"upload\": return toBool(perms$.value[path][\"can_upload\"]);\n    default: return false;\n    }\n}\n\nexport function getPermission(path) {\n    return perms$.asObservable().pipe(\n        rxjs.map((perms) => perms[path]),\n    );\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/model_files.js",
    "content": "import { toHref, navigate } from \"../../lib/skeleton/router.js\";\nimport rxjs from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { basename, forwardURLParams } from \"../../lib/path.js\";\nimport notification from \"../../components/notification.js\";\nimport assert from \"../../lib/assert.js\";\nimport { AjaxError } from \"../../lib/error.js\";\nimport t from \"../../locales/index.js\";\n\nimport { currentPath, isAlreadyFocused } from \"./helper.js\";\nimport { setPermissions } from \"./model_acl.js\";\nimport fscache from \"./cache.js\";\nimport { ls as middlewareLs } from \"./model_virtual_layer.js\";\nimport { tagFilter } from \"./model_tag.js\";\n\n/*\n * The naive approach would be to make an API call and refresh the screen after an action\n * is made but that give a poor UX. Instead, we rely on 2 layers of caching:\n * - the indexedDB cache that stores the part of the filesystem we've already visited. That way\n *   we can make navigation feel instant by first returning what's in the cache first and only\n *   refresh the screen if our cache is out of date.\n * - the transcient cache which is used whenever the user do something. For example, when creating\n *   a file we have 3 actions being done:\n *   1. a new file is shown in the UI but with a loading spinner\n *   2. the api call is made\n *   3. the new file is being persisted in the screen if the API call is a success\n */\n\nconst handleSuccess = (text) => rxjs.tap(() => notification.info(text));\nconst handleError = rxjs.catchError((err) => {\n    notification.error(err.message);\n    throw err;\n});\nconst handleErrorRedirectLogin = rxjs.catchError((err) => {\n    if (err instanceof AjaxError && err.err().status === 401) {\n        navigate(toHref(\"/login?next=\" + location.pathname + location.hash + location.search));\n        return rxjs.EMPTY;\n    }\n    throw err;\n});\n\nconst trimDirectorySuffix = (name) => name.replace(new RegExp(\"/$\"), \"\");\n\nexport const touch = (path) => ajax({\n    url: withURLParams(`api/files/touch?path=${encodeURIComponent(path)}`),\n    method: \"POST\",\n    responseType: \"json\",\n}).pipe(\n    handleSuccess(t(\"A file named '{{VALUE}}' was created\", basename(path))),\n    handleError,\n);\n\nexport const mkdir = (path) => ajax({\n    url: withURLParams(`api/files/mkdir?path=${encodeURIComponent(path)}`),\n    method: \"POST\",\n    responseType: \"json\",\n}).pipe(\n    handleSuccess(t(\"A folder named '{{VALUE}}' was created\", basename(trimDirectorySuffix(path)))),\n    handleError,\n);\n\nexport const rm = (...paths) => rxjs.forkJoin(paths.map((path) => ajax({\n    url: withURLParams(`api/files/rm?path=${encodeURIComponent(path)}`),\n    method: \"POST\",\n    responseType: \"json\",\n}))).pipe(\n    handleSuccess(paths.length > 1 ? t(\"All Done!\") : t(\"The file '{{VALUE}}' was deleted\", basename(trimDirectorySuffix(paths[0])))),\n    handleError,\n);\n\nexport const mv = (from, to) => ajax({\n    url: withURLParams(`api/files/mv?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`),\n    method: \"POST\",\n    responseType: \"json\",\n}).pipe(\n    handleSuccess(t(\"The file '{{VALUE}}' was renamed\", basename(trimDirectorySuffix(from)))),\n    handleError,\n);\n\nexport const save = () => rxjs.of(null).pipe(rxjs.delay(1000));\n\nexport const ls = (path) => {\n    const lsFromCache = (path) => rxjs.from(fscache().get(path));\n    const lsFromHttp = (path) => ajax({\n        url: withURLParams(`api/files/ls?path=${encodeURIComponent(path)}`),\n        method: \"GET\",\n        responseType: \"json\",\n    }).pipe(\n        handleErrorRedirectLogin,\n        rxjs.map(({ responseJSON }) => ({\n            files: responseJSON.results,\n            permissions: responseJSON.permissions,\n        })),\n        rxjs.tap(({ files, permissions }) => {\n            fscache().store(path, { files, permissions });\n            hooks.ls.emit({ path, files, permissions });\n        }),\n    );\n\n    return rxjs.combineLatest(\n        lsFromCache(path),\n        rxjs.merge(\n            rxjs.of(null),\n            rxjs.merge(rxjs.of(null), rxjs.fromEvent(window, \"keydown\").pipe( // \"r\" shorcut\n                rxjs.filter((e) => e.keyCode === 82 && !isAlreadyFocused()),\n            )).pipe(\n                rxjs.switchMap(() => lsFromHttp(path)),\n                rxjs.catchError((err) => navigator.onLine ? rxjs.throwError(err) : rxjs.EMPTY),\n            ),\n        ),\n        rxjs.merge(\n            rxjs.of(navigator.onLine),\n            rxjs.fromEvent(window, \"online\"),\n            rxjs.fromEvent(window, \"offline\"),\n        )\n    ).pipe(\n        rxjs.mergeMap(([cache, http]) => {\n            if (http) return rxjs.of(http);\n            if (cache) return rxjs.of(cache);\n            return rxjs.EMPTY;\n        }),\n        rxjs.map(({ files, ...res }) => {\n            if (navigator.onLine) res[\"files\"] = files;\n            else res[\"files\"] = files.map((file) => file.type === \"file\" ? { ...file, offline: true } : file);\n            return res;\n        }),\n        rxjs.distinctUntilChanged((prev, curr) => {\n            let refresh = false;\n            if (prev.files.length !== curr.files.length) refresh = true;\n            else if (JSON.stringify(prev.permissions) !== JSON.stringify(curr.permissions)) refresh = true;\n            else {\n                for (let i=0; i<curr.files.length; i++) {\n                    if (curr.files[i].type !== prev.files[i].type ||\n                        curr.files[i].size !== prev.files[i].size ||\n                        curr.files[i].name !== prev.files[i].name ||\n                        curr.files[i].offline !== prev.files[i].offline) {\n                        refresh = true;\n                        break;\n                    }\n                }\n            }\n            return !refresh;\n        }),\n        rxjs.tap(({ permissions }) => setPermissions(path, permissions)),\n        middlewareLs(path),\n        tagFilter(path),\n    );\n};\n\nexport const search = (term) => ajax({\n    url: withURLParams(`api/files/search?path=${encodeURIComponent(currentPath())}&q=${encodeURIComponent(term)}`),\n    responseType: \"json\"\n}).pipe(rxjs.map(({ responseJSON }) => ({\n    files: responseJSON.results,\n})));\n\nclass Hook {\n    constructor() {\n        this.list = [];\n        this.id = 0;\n    }\n\n    listen(fn) {\n        if (typeof fn !== \"function\") assert.fail(\"hook must be a function\");\n        const id = this.id;\n        this.list.push({ id, fn });\n        this.id += 1;\n        return () => {\n            this.list = this.list.filter((obj) => obj.id !== id);\n        };\n    }\n\n    emit(data) {\n        this.list.map(({ fn }) => fn(data));\n    }\n}\n\nexport const hooks = {\n    ls: new Hook(),\n    mutation: new Hook(),\n    // ...\n    // add hooks on a needed basis\n};\n\nconst withURLParams = (url) => forwardURLParams(url, [\"share\"]);\n"
  },
  {
    "path": "public/assets/pages/filespage/model_tag.js",
    "content": "import rxjs from \"../../lib/rx.js\";\nimport { basename, forwardURLParams } from \"../../lib/path.js\";\nimport ajax from \"../../lib/ajax.js\";\n\nexport const tagFilter = (path) => rxjs.mergeMap((resp) => {\n    const tags = new URLSearchParams(location.search).getAll(\"tag\");\n    if (tags.length === 0) return rxjs.of(resp);\n    return ajax({\n        url: forwardURLParams(`api/metadata/search`, [\"share\"]),\n        body: JSON.stringify({\n            \"tags\": new URLSearchParams(location.search).getAll(\"tag\"),\n            path,\n        }),\n        method: \"POST\",\n        responseType: \"json\",\n    }).pipe(\n        rxjs.mergeMap((tags) => rxjs.of(Object.keys(tags.responseJSON.results).map((fullpath) => ({\n            name: basename(fullpath.replace(new RegExp(\"/$\"), \"\")),\n            type: fullpath.slice(-1) === \"/\" ? \"directory\" : \"file\",\n            size: -1,\n            path: fullpath,\n        })))),\n        rxjs.map((files) => {\n            resp.files = files;\n            return resp;\n        }),\n    );\n});\n"
  },
  {
    "path": "public/assets/pages/filespage/model_virtual_layer.js",
    "content": "import { onDestroy } from \"../../lib/skeleton/index.js\";\nimport rxjs from \"../../lib/rx.js\";\nimport fscache from \"./cache.js\";\nimport { hooks } from \"./model_files.js\";\nimport { extractPath, isDir, currentPath } from \"./helper.js\";\n\n/*\n * The virtual files is used to rerender the list of files in a particular location. That's used\n * when we want to update the dom when doing either of a touch, mkdir, rm, mv, ...\n *\n *   |---------------|      |---------------|\n *   |      LS       | ---> | Virtual Layer | ---> Observable\n *   |---------------|      |---------------|\n *\n * It is split onto 2 parts:\n * - the virtualFiles$: which are things we want to display in addition to what is currently\n *   visible on the screen\n * - the mutationFiles$: which are things already on the screen which we need to mutate. For\n *   example when we want a particular file to show a loading spinner, ...\n */\n\nconst virtualFiles$ = new rxjs.BehaviorSubject({\n    // \"/tmp/\": [],\n    // \"/home/\": [{ name: \"test\", type: \"directory\" }]\n});\nconst mutationFiles$ = new rxjs.BehaviorSubject({\n    // \"/home/\": [{ name: \"test\", fn: (file) => file, ...]\n});\n\nclass IVirtualLayer {\n    before() { throw new Error(\"NOT_IMPLEMENTED\"); }\n    async afterSuccess() { throw new Error(\"NOT_IMPLEMENTED\"); }\n    async afterError() { return rxjs.EMPTY; }\n}\n\nexport function withVirtualLayer(ajax$, mutate) {\n    mutate.before();\n    return ajax$.pipe(\n        rxjs.tap((resp) => mutate.afterSuccess(resp)),\n        rxjs.catchError(mutate.afterError),\n    );\n};\n\nexport function touch(path) {\n    const [basepath, filename] = extractPath(path);\n    const file = {\n        name: filename,\n        type: \"file\",\n        size: 0,\n        time: new Date().getTime(),\n    };\n\n    return new class TouchVL extends IVirtualLayer {\n        /**\n         * @override\n         */\n        before() {\n            stateAdd(virtualFiles$, basepath, {\n                ...file,\n                loading: true,\n            });\n        }\n\n        /**\n         * @override\n         */\n        async afterSuccess() {\n            removeLoading(virtualFiles$, basepath, filename);\n            onDestroy(() => statePop(virtualFiles$, basepath, filename));\n            await fscache().update(basepath, ({ files = [], ...rest }) => ({\n                files: files.concat([file]),\n                ...rest,\n            }));\n            hooks.mutation.emit({ op: \"touch\", path: basepath });\n        }\n\n        /**\n         * @override\n         */\n        async afterError() {\n            statePop(virtualFiles$, basepath, filename);\n            return rxjs.of(fscache().remove(basepath)).pipe(\n                rxjs.mergeMap(() => rxjs.EMPTY),\n            );\n        }\n    }();\n}\n\nexport function mkdir(path) {\n    const [basepath, dirname] = extractPath(path);\n    const file = {\n        name: dirname,\n        type: \"directory\",\n        size: 0,\n        time: new Date().getTime(),\n    };\n\n    return new class MkdirVL extends IVirtualLayer {\n        /**\n         * @override\n         */\n        before() {\n            stateAdd(virtualFiles$, basepath, {\n                ...file,\n                loading: true,\n            });\n            statePop(mutationFiles$, basepath, dirname); // case: rm followed by mkdir\n        }\n\n        /**\n         * @override\n         */\n        async afterSuccess() {\n            if (basepath === currentPath()) removeLoading(virtualFiles$, basepath, dirname);\n            else onDestroy(() => removeLoading(virtualFiles$, basepath, dirname));\n            onDestroy(() => statePop(virtualFiles$, basepath, dirname));\n            await fscache().update(basepath, ({ files = [], ...rest }) => ({\n                files: files.concat([file]),\n                ...rest,\n            }));\n            hooks.mutation.emit({ op: \"mkdir\", path: basepath });\n        }\n\n        /**\n         * @override\n         */\n        async afterError() {\n            statePop(virtualFiles$, basepath, dirname);\n            return rxjs.of(fscache().remove(basepath)).pipe(\n                rxjs.mergeMap(() => rxjs.EMPTY),\n            );\n        }\n    }();\n}\n\nexport function save(path, size) {\n    const [basepath, filename] = extractPath(path);\n    const file = {\n        name: filename,\n        type: \"file\",\n        size,\n        time: new Date().getTime(),\n    };\n\n    return new class SaveVL extends IVirtualLayer {\n        /**\n         * @override\n         */\n        before() {\n            stateAdd(virtualFiles$, basepath, {\n                ...file,\n                loading: true,\n            });\n            statePop(mutationFiles$, basepath, filename); // eg: rm followed by save\n        }\n\n        /**\n         * @override\n         */\n        async afterSuccess() {\n            if (basepath === currentPath()) removeLoading(virtualFiles$, basepath, filename);\n            else onDestroy(() => removeLoading(virtualFiles$, basepath, filename));\n            onDestroy(() => statePop(virtualFiles$, basepath, filename));\n            await fscache().update(basepath, ({ files = [], ...rest }) => ({\n                files: files.concat([file]),\n                ...rest,\n            }));\n            hooks.mutation.emit({ op: \"save\", path: basepath });\n        }\n\n        /**\n         * @override\n         */\n        async afterError() {\n            statePop(virtualFiles$, basepath, filename);\n            return rxjs.EMPTY;\n        }\n    }();\n}\n\nexport function rm(...paths) {\n    if (paths.length === 0) return rxjs.of(null);\n    const arr = new Array(paths.length * 2);\n    let basepath = null;\n    for (let i=0; i<paths.length; i++) {\n        [arr[2*i], arr[2*i+1]] = extractPath(paths[i]);\n        if (i === 0) basepath = arr[2*i];\n        else if (basepath !== arr[2*i]) throw new Error(\"NOT_IMPLEMENTED\");\n    }\n\n    return new class RmVL extends IVirtualLayer {\n        /**\n         * @override\n         */\n        before() {\n            for (let i=0; i<arr.length; i+=2) {\n                stateAdd(mutationFiles$, arr[i], {\n                    name: arr[i+1],\n                    fn: (file) => {\n                        if (file.name === arr[i+1]) {\n                            file.loading = true;\n                            file.last = true;\n                        }\n                        return file;\n                    },\n                });\n                statePop(virtualFiles$, arr[i], arr[i+1]); // eg: touch followed by rm\n            }\n        }\n\n        /**\n         * @override\n         */\n        async afterSuccess() {\n            for (let i=0; i<arr.length; i+=2) {\n                stateAdd(mutationFiles$, arr[i], {\n                    name: arr[i+1],\n                    fn: (file) => {\n                        for (let i=0; i<arr.length; i+=2) {\n                            if (file.name === arr[i+1]) return null;\n                        }\n                        return file;\n                    },\n                });\n            }\n            onDestroy(() => {\n                for (let i=0; i<arr.length; i+=2) {\n                    statePop(mutationFiles$, arr[i], arr[i+1]);\n                }\n            });\n            await Promise.all(paths.map((path) => fscache().remove(path, false)));\n            await fscache().update(basepath, ({ files = [], ...rest }) => ({\n                files: files.filter(({ name }) => {\n                    for (let i=0; i<arr.length; i+=2) {\n                        if (name === arr[i+1]) {\n                            return false;\n                        }\n                    }\n                    return true;\n                }),\n                ...rest,\n            }));\n            if (arr.length > 0) hooks.mutation.emit({ op: \"rm\", path: arr[0] });\n        }\n\n        /**\n         * @override\n         */\n        async afterError() {\n            for (let i=0; i<arr.length; i+=2) {\n                stateAdd(mutationFiles$, arr[i], {\n                    name: arr[i+1],\n                    fn: (file) => {\n                        if (file.name === arr[i+1]) {\n                            file = {\n                                ...file,\n                                loading: false,\n                                last: false\n                            };\n                        }\n                        return file;\n                    },\n                });\n            }\n            return rxjs.EMPTY;\n        }\n    }();\n}\n\nexport function mv(fromPath, toPath) {\n    const [fromBasepath, fromName] = extractPath(fromPath);\n    const [toBasepath, toName] = extractPath(toPath);\n    let type = null;\n\n    return new class MvVL extends IVirtualLayer {\n        /**\n         * @override\n         */\n        before() {\n            if (fromBasepath === toBasepath) this._beforeSamePath();\n            else this._beforeDifferentPath();\n        }\n\n        _beforeSamePath() {\n            stateAdd(mutationFiles$, fromBasepath, {\n                name: fromName,\n                fn: (file) => {\n                    if (file.name === fromName) {\n                        type = file.type;\n                        return {\n                            ...file,\n                            name: toName,\n                            loading: true,\n                        };\n                    }\n                    return file;\n                },\n            });\n        }\n\n        _beforeDifferentPath() {\n            stateAdd(mutationFiles$, fromBasepath, {\n                name: fromName,\n                fn: (file) => {\n                    if (file.name === fromName) {\n                        type = file.type;\n                        return {\n                            ...file,\n                            loading: true,\n                            last: true,\n                        };\n                    }\n                    return file;\n                },\n            });\n            stateAdd(virtualFiles$, toBasepath, {\n                name: toName,\n                loading: true,\n                type,\n            });\n        }\n\n        /**\n         * @override\n         */\n        async afterSuccess() {\n            fscache().remove(fromPath, false);\n            if (fromBasepath === toBasepath) await this._afterSuccessSamePath();\n            else await this._afterSuccessDifferentPath();\n        }\n\n        async _afterSuccessSamePath() {\n            stateAdd(mutationFiles$, fromBasepath, {\n                name: fromName,\n                fn: (file) => {\n                    if (file.name === toName) {\n                        file = { ...file, loading: false };\n                    }\n                    return file;\n                },\n            });\n            await fscache().update(fromBasepath, ({ files = [], ...rest }) => {\n                return {\n                    files: files.map((file) => {\n                        if (file.name === fromName) {\n                            file.name = toName;\n                        }\n                        return file;\n                    }),\n                    ...rest,\n                };\n            });\n            hooks.mutation.emit({ op: \"mv\", path: fromBasepath });\n        }\n\n        async _afterSuccessDifferentPath() {\n            stateAdd(mutationFiles$, fromBasepath, {\n                name: fromName,\n                fn: (file) => {\n                    if (file.name === fromName) return null;\n                    return file;\n                },\n            });\n            onDestroy(() => statePop(mutationFiles$, fromBasepath, fromName));\n            statePop(virtualFiles$, toBasepath, toName);\n            await fscache().update(fromBasepath, ({ files = [], ...rest }) => ({\n                files: files.filter((file) => file.name !== fromName),\n                ...rest,\n            }));\n            await fscache().update(toBasepath, ({ files = [], ...rest }) => ({\n                files: files.concat([{\n                    name: fromName,\n                    time: new Date().getTime(),\n                    type,\n                }]),\n                ...rest,\n            }));\n            if (isDir(fromPath)) await fscache().remove(fromPath);\n            hooks.mutation.emit({ op: \"mv\", path: fromBasepath });\n            hooks.mutation.emit({ op: \"mv\", path: toBasepath });\n        }\n\n        /**\n         * @override\n         */\n        async afterError() {\n            if (fromBasepath === toBasepath) stateAdd(mutationFiles$, fromBasepath, {\n                name: fromName,\n                fn: (file) => {\n                    if (file.name === toName) return {\n                        ...file,\n                        name: fromName,\n                        loading: false,\n                    };\n                    return file;\n                },\n            }); else {\n                stateAdd(mutationFiles$, fromBasepath, {\n                    name: fromName,\n                    fn: (file) => {\n                        if (file.name === fromName) return {\n                            ...file,\n                            loading: false,\n                            last: false,\n                        };\n                        return file;\n                    },\n                });\n                statePop(virtualFiles$, toBasepath, toName);\n            }\n            statePop(mutationFiles$, fromBasepath, fromName);\n            return rxjs.EMPTY;\n        }\n    }();\n}\n\nexport function ls(path) {\n    return rxjs.pipe(\n        // case1: file mutation = update a file state, typically to add a loading state to an\n        //                        file or remove it entirely\n        rxjs.switchMap(({ files, ...res }) => mutationFiles$.pipe(\n            rxjs.map((all) => all[path]),\n            rxjs.mergeMap((fns) => {\n                const shouldContinue = !!(fns && fns.length > 0);\n                if (!shouldContinue) return rxjs.of({ ...res, files });\n                for (let i=files.length-1; i>=0; i--) {\n                    for (let j=0; j<fns.length; j++) {\n                        files[i] = fns[j].fn(files[i]);\n                        if (!files[i]) {\n                            files.splice(i, 1);\n                            break;\n                        }\n                    }\n                }\n                return rxjs.of({ ...res, files });\n            }),\n        )),\n        // case2: virtual files = additional files we want to see displayed in the UI\n        rxjs.switchMap(({ files, ...res }) => virtualFiles$.pipe(\n            rxjs.map((all) => all[path] || []),\n            rxjs.distinctUntilChanged((prev, curr) => { // we only want to get notified of changes within \"path\"\n                if (prev.length !== curr.length) return false;\n                for (let i=0; i<prev.length; i++) {\n                    if (prev[i].name !== curr[i].name) return false;\n                    else if (prev[i].type !== curr[i].type) return false;\n                    else if (prev[i].loading !== curr[i].loading) return false;\n                }\n                return true;\n            }),\n            rxjs.mergeMap((virtualFiles) => {\n                if (virtualFiles.length === 0) return rxjs.of({ ...res, files });\n                return rxjs.of({\n                    ...res,\n                    files: files.concat(virtualFiles),\n                });\n            }),\n        )),\n    );\n}\n\nfunction stateAdd(behavior, path, obj) {\n    let arr = behavior.value[path];\n    if (!arr) arr = [];\n    let alreadyKnown = false;\n    for (let i=0; i<arr.length; i++) {\n        if (arr[i].name === obj.name) {\n            alreadyKnown = true;\n            arr[i] = obj;\n            break;\n        }\n    }\n    if (!alreadyKnown) {\n        arr = arr.concat([obj]);\n    }\n    behavior.next({\n        ...behavior.value,\n        [path]: arr,\n    });\n}\n\nfunction statePop(behavior, path, filename) {\n    const arr = behavior.value[path];\n    if (!arr) return;\n    const newArr = arr.filter(({ name }) => name !== filename);\n    if (newArr.length === 0) {\n        const newState = { ...behavior.value };\n        delete newState[path];\n        behavior.next(newState);\n        return;\n    }\n    behavior.next({\n        ...behavior.value,\n        [path]: newArr,\n    });\n}\n\nfunction removeLoading(behavior, path, filename) {\n    const arr = behavior.value[path];\n    if (!arr) return;\n    virtualFiles$.next({\n        ...virtualFiles$.value,\n        [path]: arr.map((file) => {\n            if (file.name === filename) {\n                return { ...file, loading: false };\n            }\n            return file;\n        }),\n    });\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/state_config.js",
    "content": "import rxjs, { effect, preventDefault } from \"../../lib/rx.js\";\nimport { get as getConfig } from \"../../model/config.js\";\nimport { settingsGet, settingsSave } from \"../../lib/store.js\";\n\nlet state$ = null;\nexport function init() {\n    state$ = new rxjs.BehaviorSubject(settingsGet({\n        view: getConfig(\"default_view\", \"grid\"),\n        show_hidden: getConfig(\"display_hidden\", false),\n        sort: getConfig(\"default_sort\", \"type\"),\n        order: null,\n        search: new URLSearchParams(location.search).get(\"q\"),\n    }, \"filespage\"));\n}\n\nexport const getState$ = () => state$.asObservable();\n\nexport const setState = (...args) => {\n    const obj = { ...state$.value };\n    let hasChange = false;\n    for (let i=0; i<args.length; i+=2) {\n        if (obj[args[i]] === args[i+1]) continue;\n        obj[args[i]] = args[i+1];\n        hasChange = true;\n    }\n    if (!hasChange) return;\n    state$.next(obj);\n    settingsSave({\n        view: state$.value.view,\n        show_hidden: state$.value.show_hidden,\n        sort: state$.value.sort,\n        order: state$.value.order,\n    }, \"filespage\");\n};\n\neffect(rxjs.fromEvent(window, \"keydown\").pipe(\n    rxjs.filter((e) => e.ctrlKey && e.key === \"h\"),\n    preventDefault(),\n    rxjs.tap(() => setState(\"show_hidden\", !state$.value.show_hidden)),\n));\n"
  },
  {
    "path": "public/assets/pages/filespage/state_newthing.js",
    "content": "import rxjs from \"../../lib/rx.js\";\n\nconst action$ = new rxjs.ReplaySubject(1);\naction$.next(null);\n\nexport function getAction$() {\n    return action$.asObservable();\n}\n\nexport function setAction(actionTarget) {\n    action$.next(actionTarget);\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/state_selection.js",
    "content": "import rxjs from \"../../lib/rx.js\";\nimport { onDestroy } from \"../../lib/skeleton/index.js\";\nimport { extractPath } from \"./helper.js\";\n\n/*\n * CAUTION: Use a lot of caution if you change this file as it's easy for a bug to slip in and be responsible\n * for someone using selection to delete something and lose some data. We really don't want this to happen!\n *\n * BACKGROUND: There's many way we could have work on the selection. In fact I've made a couple prior attempts\n * with very simple implementations but none of them felt \"right\" from a user point of view. The approach\n * taken here mimicks the behavior of osx finder. To understand what is going on, I strongly advise you\n * open up osx finder and play with it first.\n *\n * DATA STRUCTURE: The supporting data structure is a list containing 2 kind of elements:\n * - \"anchors\" which is created when clicking on something with the cmd key\n * - \"range\" which is when you click somewhere with the expectation it will expand your range\n *   using the shift key\n */\nconst selection$ = new rxjs.BehaviorSubject([\n    // { \"type\": \"anchor\", ...  } => user click somewhere and the shift key is NOT pressed\n    // { \"type\": \"range\", ...   } => ----------------------------------------- pressed\n]);\n\nonDestroy(clearSelection);\n\nexport function addSelection({ shift = false, n = 0, ...rest }) {\n    const selections = selection$.value;\n    const selection = { type: shift ? \"range\" : \"anchor\", n, ...rest };\n\n    // case1: select a single file/folder\n    if (selection.type === \"anchor\") {\n        selection$.next(selections.concat([selection]));\n        return;\n    }\n    // case2: range selection\n    const last = selection$.value[selections.length - 1] || { n: 0, type: \"anchor\" };\n    if (last.type === \"anchor\") {\n        selection$.next(selections.concat([selection]));\n        return;\n    }\n    // clear out previous range selector. That's the behavior on apple finder when we do:\n    // [A,1] [R,3]             = [A,1] [R,3] => expands to [1,2,3]\n    // [A,1] [R,3] [R,8]       = [A,1] [R,8] => expands to [1,2,3,4,5,6,7,8]\n    // [A,1] [R,3] [R,8] [R,6] = [A,1] [R,6] => expands to [1,2,3,4,5,6]\n    selections[selections.length - 1] = selection;\n    selection$.next(selections);\n}\n\nexport function clearSelection() {\n    if (selection$.value.length > 0) selection$.next([]);\n}\n\nexport function getSelection$() {\n    return selection$.asObservable();\n}\n\nexport function isSelected(n) {\n    let isChecked = false;\n    const selections = selection$.value;\n    for (let i=0; i<selections.length; i++) {\n        if (selections[i].type === \"anchor\" && selections[i].n === n) {\n            isChecked = !isChecked;\n        }\n        else if (selections[i].type === \"range\" && isBetween(\n            n, selections[i-1]?.n || 0, selections[i]?.n,\n        )) {\n            isChecked = true; // WARNING: change with caution\n        }\n    }\n    return isChecked;\n}\n\nexport function lengthSelection() {\n    return _selectionHelper((set) => set.size);\n}\n\nexport function expandSelection() {\n    return _selectionHelper((set) => {\n        const arr = new Array(set.size);\n        let i = 0;\n        for (const path of set) {\n            arr[i] = { path };\n            i += 1;\n        }\n        return arr;\n    });\n}\n\n// This is the core function used to not only calculate the selection length but also expand the\n// selection. We're quite slow with an algo running in 3N selection size with room for improvement\n// but be very cautious, a bug in here could cause terrible consequence\nfunction _selectionHelper(fn) {\n    const set = new Set();\n    const selections = selection$.value;\n    for (let i=0; i<selections.length; i++) {\n        const curr = selections[i];\n        if (selections[i].type === \"anchor\") {\n            if (isSelected(selections[i].n)) {\n                set.add(curr.path);\n            }\n            continue;\n        }\n        const [basepath] = extractPath(curr.path);\n\n        const min = i === 0 ? 0 : Math.min(selections[i].n, selections[i-1].n);\n        const max = i === 0 ? selections[i].n : Math.max(selections[i].n, selections[i-1].n);\n        for (let j=min; j<=max; j++) {\n            if (isSelected(j) === false) continue;\n            const { offline, name, type } = selections[i].files[j];\n            if (offline === true) continue;\n            set.add(basepath + name + (type === \"directory\" ? \"/\" : \"\"));\n        }\n    }\n    return fn(set);\n}\n\nfunction isBetween(n, lowerBound, higherBound) {\n    return n <= Math.max(higherBound, lowerBound) && n >= Math.min(lowerBound, higherBound);\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/thing.css",
    "content": ".touch-no .component_thing:hover, .component_thing .highlight,\n.component_thing.hover, .component_thing .highlight {\n    transition: 0.1s ease-out background;\n    background: var(--border);\n    border-color: var(--super-light);\n    border-radius: 5px;\n}\n.component_thing:not(.loading):hover .component_datetime {\n    display: none;\n}\n.component_thing:not([href]) {\n    cursor: not-allowed;\n}\n.component_thing .file-is-hover {\n    background: var(--emphasis-primary);\n}\n.component_thing .file-is-dragging {\n    opacity: 0.15;\n}\n.component_thing .file-details {\n    padding: 0 5px;\n    line-height: 25px;\n    white-space: nowrap;\n}\n.component_thing .file-details > span {\n    display: inline-block;\n    width: calc(100% - 130px);\n    max-width: 750px;\n    vertical-align: bottom;\n    color: inherit;\n}\n.component_thing form {\n    display: inline-block;\n}\n.component_thing form input {\n    font-size: 1em;\n    border-width: 0px;\n    padding: 0 2px 0 2px;\n    background: inherit;\n    border-bottom: 2px solid var(--emphasis-primary);\n    color: var(--color);\n}\n.component_thing.view-grid .component_action {\n    float: right;\n    color: #6f6f6f;\n    line-height: 25px;\n    margin: 0 -10px;\n    padding: 0 10px;\n    position: relative;\n}\n.component_thing .component_icon {\n    width: 25px;\n    height: 25px;\n}\n.component_thing .component_filesize {\n    color: var(--light);\n    font-size: 0.85em;\n    padding-left: 3px;\n}\n.component_thing .component_datetime {\n    float: right;\n    color: var(--light);\n    line-height: 25px;\n    margin: 0 -10px;\n    padding: 0 10px;\n    position: relative;\n}\n.component_thing .component_checkbox {\n    opacity: 0;\n    z-index: 2;\n    position: absolute;\n    transition: 0.15s ease-out all;\n    transform: translateX(0px);\n    padding: 2.5px;\n    border-radius: 50px;\n}\n.component_thing .component_checkbox:has(input:focus-visible) {\n    opacity: 1;\n}\n.component_thing .component_action{\n    display: none;\n    float: right;\n    color: #6f6f6f;\n    line-height: 25px;\n    margin: 0 -10px;\n    padding: 0 10px;\n    position: relative;\n}\n.component_thing .component_action .component_icon{\n    padding: 1px 0;\n    box-sizing: border-box;\n}\n.list > .component_thing.view-grid .component_action {\n    transform: translateX(5px);\n    transition: 0.15s ease-out all;\n    z-index: 2;\n    display: block;\n    position: absolute;\n    top: 5px;\n    right: 5px;\n    border-radius: 5px;\n    margin-right: 0px;\n    padding: 0px;\n}\n.list > .component_thing.view-grid:hover .component_action {\n    transition-delay: 0.1s;\n    transform: translateX(0);\n}\n.component_thing:hover .component_action,\n.component_thing:focus-visible .component_action { opacity: 1; display: block; }\n.component_thing .component_action { opacity: 0; }\n.component_thing.selected .component_action { opacity: 0; transition-delay: 0s; }\n.component_thing:has(.component_icon[alt=\"loading\"]) .component_action { display: none; }\n\n.touch-yes .component_thing .component_checkbox {\n    opacity: 1;\n}\n.component_thing .component_checkbox .indicator {\n    top: 7px;\n    left: 7px;\n}\n.touch-no .component_thing:hover .component_checkbox,\n.touch-no .component_thing:focus-visible .component_checkbox,\n.component_thing.selected .component_checkbox {\n    transition-delay: 0.1s;\n    opacity: 1;\n}\n\n.component_thing .selectionOverlay {\n    display: none;\n    border-radius: 3px;\n}\n.component_thing.selected .selectionOverlay {\n    display: block;\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    background: var(--primary);\n    box-shadow: 0 0 30px rgba(0, 0, 0, 0.1);\n    z-index: 1;\n    opacity: 0.2;\n}\n\n/* GRID & LIST VIEW */\n.list {\n    display: grid;\n}\n.list > .component_thing {\n    width: 100%;\n    position: relative;\n    box-sizing: border-box;\n    min-width: 0;\n}\n.list > .component_thing.view-grid {\n    border: 2px solid var(--border);\n    border-radius: 5px;\n    text-align: center;\n    height: 160px;\n    box-sizing: border-box;\n}\n.list > .component_thing.view-grid > img {\n    padding: 0;\n    margin: 0;\n    display: block;\n}\n.list > .component_thing.view-grid > img.component_icon.thumbnail {\n    width: 100%;\n    height: 100%;\n    object-fit: cover;\n    object-position: 50% 50%;\n    background: var(--dark);\n    padding: 0;\n    margin: 0;\n    display: block;\n    border-radius: 3px;\n}\n.list > .component_thing.view-grid > img.component_icon.thumbnail.placeholder {\n    position: absolute;\n    top: 0;\n}\n.list > .component_thing.view-grid > img.component_icon {\n    padding: 30px;\n    box-sizing: border-box;\n    width: 100%;\n    height: 100%;\n    object-fit: contain;\n    margin: 0 auto;\n    z-index: 0;\n}\n.list > .component_thing.view-grid .info_extension {\n    position: absolute;\n    top: 45%;\n    text-align: right;\n    left: 0;\n    right: 20%;\n    right: calc(50% - 50px);\n    margin: 0 auto;\n    text-transform: uppercase;\n    font-size: 0.95em;\n    text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2);\n}\n.list > .component_thing.view-grid .info_extension span {\n    background: var(--color);\n    color: var(--bg-color);\n    font-weight: bold;\n    border-radius: 4px;\n    padding: 2px 10px;\n    display: inline-block;\n    text-align: center;\n    min-width: 20px;\n    max-width: 65px;\n    font-size: 0.95em;\n}\n.list > .component_thing .info_extension span:empty {\n    display: none;\n}\n.list > .component_thing.view-grid .component_filename {\n    letter-spacing: -0.5px;\n    position: absolute;\n    bottom: 2px;\n    left: 2px;\n    right: 2px;\n    border-radius: 2px;\n    padding: 3px 0px;\n}\n.list > .component_thing.view-grid .component_filename .file-details {\n    width: calc(100% - 10px);\n    display: block;\n}\n.list > .component_thing.view-grid .component_filename .file-details > span {\n    width: 100%;\n}\n.list > .component_thing.view-grid .component_filename .file-details > span form input {\n    letter-spacing: -0.5px;\n    text-align: center;\n    width: 100%;\n    padding: 0;\n}\n.list > .component_thing.view-grid .image_layer {\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    z-index: 1;\n    background: rgba(0, 0, 0, 0);\n    transition: 0.2s ease-out background;\n}\n.list > .component_thing.view-grid .component_filesize,\n.list > .component_thing.view-grid .component_datetime {\n    display: none;\n}\n.list > .component_thing.view-grid img.thumbnail {\n    transition: 0.2s ease-out transform;\n}\n.list > .component_thing.view-grid.not-selected:hover img.thumbnail,\n.list > .component_thing.view-grid.not-selected.hover img.thumbnail {\n    transform: scale(0.6);\n}\n.list > .component_thing.view-grid.not-selected:hover .image_layer {\n    background: var(--border);\n}\n\n.list > .component_thing.view-grid.not-selected:hover .thumbnail ~ .component_filename {\n    opacity: 1;\n}\n.list > .component_thing.view-grid.not-selected .thumbnail ~ .component_filename {\n    transition: 0.2s ease-out opacity;\n    opacity: 0;\n}\n.list > .component_thing.view-grid.selected img.thumbnail {\n    transform: scale(0.6);\n}\n.list > .component_thing.view-grid .component_checkbox {\n    display: block;\n    top: 3px;\n    left: 3px;\n    transform: translateX(-5px);\n}\n.touch-yes .list > .component_thing.view-grid .component_checkbox {\n    transform: translateX(0px);\n}\n.touch-yes .list > .component_thing.view-grid .component_checkbox input[type=\"checkbox\"]:not(:checked) ~ span {\n    background-color: inherit;\n}\n.list > .component_thing.view-grid:hover .component_checkbox,\n.list > .component_thing.view-grid:focus-within .component_checkbox,\n.list > .component_thing.view-grid.selected .component_checkbox {\n    transform: translateX(0px);\n}\n\n.list > .component_thing.view-list {\n    padding: 10px;\n}\n.list > .component_thing.view-list .info_extension {\n    display: none;\n}\n.list > .component_thing.view-list .component_checkbox {\n    top: 8px;\n    left: 8px;\n    transition: 0.5s ease all;\n    background: rgba(0, 0, 0, 0);\n}\n.list > .component_thing.view-list .component_checkbox:hover {\n    background: rgba(0, 0, 0, 0.1);\n}\n.list > .component_thing.view-list:hover .component_checkbox,\n.list > .component_thing.view-list.selected .component_checkbox {\n    display: block;\n}\n.list > .component_thing.view-list:hover .component_checkbox ~ .component_icon,\n.list > .component_thing.view-list.selected .component_checkbox ~ .component_icon {\n    visibility: hidden;\n}\n.list > .component_thing.view-list:hover .component_checkbox .indicator,\n.list > .component_thing.view-list.selected .component_checkbox .indicator {\n    border-color: #57595A;\n}\n.list > .component_thing.view-list:hover .component_checkbox input ~ span,\n.list > .component_thing.view-list.selected .component_checkbox input ~ span {\n    background-color: rgba(255, 255, 255, 0.4);\n}\n.list > .component_thing.view-list:hover .component_checkbox input:checked ~ span,\n.list > .component_thing.view-list.selected .component_checkbox input:checked ~ span {\n    background-color: #57595A;\n}\n\n/* Dark Mode */\n.dark-mode .component_thing:hover {\n    background: rgba(255, 255, 255, 0.05);\n    border-radius: 3px;\n}\n.dark-mode .component_thing {\n    background: inherit;\n}\n.dark-mode .component_thing .component_filename {\n    color: var(--light);\n}\n.dark-mode .component_thing.highlight {\n    background: rgba(255, 255, 255, 0.05);\n}\n.dark-mode .component_thing form input {\n    border-color: var(--light);\n    color: rgba(255, 255, 255, 0.8);\n}\n"
  },
  {
    "path": "public/assets/pages/filespage/thing.d.ts",
    "content": "export function init();\n\nexport function createThing(any): HTMLElement;"
  },
  {
    "path": "public/assets/pages/filespage/thing.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport { animate, opacityOut, opacityIn } from \"../../lib/animate.js\";\nimport assert from \"../../lib/assert.js\";\nimport { forwardURLParams } from \"../../lib/path.js\";\nimport { get as getConfig } from \"../../model/config.js\";\n\nimport { extractPath, isDir, isNativeFileUpload } from \"./helper.js\";\nimport { files$ } from \"./ctrl_filesystem.js\";\nimport { addSelection, isSelected, clearSelection, expandSelection } from \"./state_selection.js\";\n\nimport { mv as mv$ } from \"./model_files.js\";\nimport { mv as mvVL, withVirtualLayer } from \"./model_virtual_layer.js\";\n\nconst mv = (from, to) => withVirtualLayer(\n    mv$(from, to),\n    mvVL(from, to),\n);\n\nconst IMAGE = {\n    FILE: \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiPgogIDxwYXRoIHN0eWxlPSJjb2xvcjojMDAwMDAwO3RleHQtaW5kZW50OjA7dGV4dC10cmFuc2Zvcm06bm9uZTtmaWxsOiM4YzhjOGM7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlLXdpZHRoOjAuOTg0ODEwNDEiIGQ9Im0gMiwxMy4wODI0MTIgMC4wMTk0NjIsMS40OTIzNDcgYyA1ZS02LDAuMjIyMTQ1IDAuMjA1NTkwMiwwLjQyNDI2MiAwLjQzMTE1MDIsMC40MjQyNzIgTCAxMy41ODk2MTIsMTUgQyAxMy44MTUxNzMsMTQuOTk5OTk1IDEzLjk5OTk5LDE0Ljc5Nzg3NCAxNCwxNC41NzU3MjkgdiAtMS40OTMzMTcgYyAtNC4xNzE4NjkyLDAuNjYyMDIzIC03LjY1MTY5MjgsMC4zOTg2OTYgLTEyLDAgeiIgLz4KICA8cGF0aCBzdHlsZT0iY29sb3I6IzAwMDAwMDt0ZXh0LWluZGVudDowO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlzcGxheTppbmxpbmU7ZmlsbDojYWFhYWFhO3N0cm9rZS13aWR0aDowLjk4NDA4MTI3IiBkPSJNIDIuMzUwMSwxLjAwMTMzMTIgQyAyLjE1MjU5LDEuMDM4MzI0NyAxLjk5NjU5LDEuMjI3MjcyMyAyLjAwMDA5LDEuNDI0OTM1NiBWIDE0LjEzMzQ1NyBjIDVlLTYsMC4yMjE4MTYgMC4yMDUyMywwLjQyMzYzNCAwLjQzMDc5LDAuNDIzNjQ0IGwgMTEuMTM5LC0xLjAxZS00IGMgMC4yMjU1NiwtNmUtNiAwLjQzMDExLC0wLjIwMDc1OCAwLjQzMDEyLC0wLjQyMjU3NCBsIDYuN2UtNCwtOS44MjI2NDI2IGMgLTIuNDg0MDQ2LC0xLjM1NTAwNiAtMi40MzUyMzQsLTIuMDMxMjI1NCAtMy41MDAxLC0zLjMwOTcwNyAtMC4wNDMsLTAuMDE1ODgyIDAuMDQ2LDAuMDAxNzQgMCwwIEwgMi40MzA2NywxLjAwMTEwOCBDIDIuNDAzODMsMC45OTg1OSAyLjM3Njc0LDAuOTk4NTkgMi4zNDk5LDEuMDAxMTA4IFoiIC8+CiAgPHBhdGggc3R5bGU9ImRpc3BsYXk6aW5saW5lO2ZpbGw6IzhjOGM4YztmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzllNzU3NTtzdHJva2Utd2lkdGg6MDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIiBkPSJtIDEwLjUwMDU3LDEuMDAyMDc2NCBjIDAsMy4yNzY4MDI4IC0wLjAwNTIsMy4xNzM5MTYxIDAuMzYyOTIxLDMuMjY5ODIwMiAwLjI4MDEwOSwwLjA3Mjk4NCAzLjEzNzE4LDAuMDM5ODg3IDMuMTM3MTgsMC4wMzk4ODcgLTEuMTIwMDY3LC0xLjA1NTY2OTIgLTIuMzMzNCwtMi4yMDY0NzEzIC0zLjUwMDEsLTMuMzA5NzA3NCB6IiAvPgo8L3N2Zz4K\",\n    FOLDER: \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiPgogIDxnIHRyYW5zZm9ybT0ibWF0cml4KDAuODY2NjY0MzEsMCwwLDAuODY2NjcsLTE3Mi4wNDU3OCwtODY0LjMyNzU5KSIgc3R5bGU9ImZpbGw6Izc1YmJkOTtmaWxsLW9wYWNpdHk6MC45NDExNzY0NztmaWxsLXJ1bGU6ZXZlbm9kZCI+CiAgICA8cGF0aCBzdHlsZT0iZmlsbDojNzViYmQ5O2ZpbGwtb3BhY2l0eTowLjk0MTE3NjQ3O2ZpbGwtcnVsZTpldmVub2RkIiBkPSJtIDIwMC4yLDk5OS43MiBjIC0wLjI4OTEzLDAgLTAuNTMxMjUsMC4yNDIxIC0wLjUzMTI1LDAuNTMxMiB2IDEyLjc4NCBjIDAsMC4yOTg1IDAuMjMyNjQsMC41MzEyIDAuNTMxMjUsMC41MzEyIGggMTUuMDkxIGMgMC4yOTg2LDAgMC41MzEyNCwtMC4yMzI3IDAuNTMxMjQsLTAuNTMxMiBsIDRlLTQsLTEwLjQ3NCBjIDAsLTAuMjg4OSAtMC4yNDIxMSwtMC41MzM4IC0wLjUzMTI0LC0wLjUzMzggbCAtNy41NDU3LDVlLTQgLTIuMzA3NiwtMi4zMDc4MyB6IiAvPgogIDwvZz4KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgwLjg2NjY3LDAsMCwwLjg2NjY3LC0xNzIuMDQ2OTIsLTg2NC43ODM0KSIgc3R5bGU9ImZpbGw6IzlhZDFlZDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZCI+CiAgICA8cGF0aCBzdHlsZT0iZmlsbDojOWFkMWVkO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkIiBkPSJtIDIwMC4yLDk5OS43MiBjIC0wLjI4OTEzLDAgLTAuNTMxMjUsMC4yNDIxIC0wLjUzMTI1LDAuNTMxMiB2IDEyLjc4NCBjIDAsMC4yOTg1IDAuMjMyNjQsMC41MzEyIDAuNTMxMjUsMC41MzEyIGggMTUuMDkxIGMgMC4yOTg2LDAgMC41MzEyNCwtMC4yMzI3IDAuNTMxMjQsLTAuNTMxMiBsIDRlLTQsLTEwLjQ3NCBjIDAsLTAuMjg4OSAtMC4yNDIxMSwtMC41MzM4IC0wLjUzMTI0LC0wLjUzMzggbCAtNy41NDU3LDVlLTQgLTIuMzA3NiwtMi4zMDc4MyB6IiAvPgogIDwvZz4KPC9zdmc+Cg==\",\n    LOADING: \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0nMTIwcHgnIGhlaWdodD0nMTIwcHgnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIiBjbGFzcz0idWlsLXJpbmctYWx0Ij4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0ibm9uZSIgY2xhc3M9ImJrIj48L3JlY3Q+CiAgPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iNDAiIHN0cm9rZT0ibm9uZSIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48L2NpcmNsZT4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjNmY2ZjZmIiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCI+CiAgICA8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJzdHJva2UtZGFzaG9mZnNldCIgZHVyPSIycyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGZyb209IjAiIHRvPSI1MDIiPjwvYW5pbWF0ZT4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9InN0cm9rZS1kYXNoYXJyYXkiIGR1cj0iMnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB2YWx1ZXM9IjE1MC42IDEwMC40OzEgMjUwOzE1MC42IDEwMC40Ij48L2FuaW1hdGU+CiAgPC9jaXJjbGU+Cjwvc3ZnPgo=\",\n    THUMBNAIL_PLACEHOLDER: \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAJhHpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHja7ZhZkuS4EUT/cQodARFAYDkOVjPdQMfXC2ZWdU9vmjbNj2STaUWySBBLeLiHI8P51z9v+AefpFZCttpKLyXyyT13HVy0+PqM5ygxP8ePj77v/uF++Hyg3Eqc0+tBK+/XDvdpIfq+v15nGdy3rzrq5/1g/vHBeHek7T3Ax4zeAyV5DRDfHYfx7ijpe+T8+n++lhVLb/XrJax3+/t+/oSh+dI45FS1WJGaOWaNtZbOddOYK3HbPtG7tPt7Nt8dffN/+GiqzElPkhQ5PjNMTD+1NDgXjpqyt0mJ65z6c7+9ZgpaGmLlun/E9defX808fEz9Dfm3UMuPoP68+grp8AT040H6BqHyef7hfbEv98PXkD64fTVyKZ8j/+F+XnH+Yc0fqPnfvbvde16rG7mw5PJe1MdSnivaTY/W81bhW/mz2DzY/u18G5RY5NGOPuLkuosC45UsW4ZcOc95yWKKWY9WzqpLk6zAzQYYXdcDd/avXK0AvAFf0yIdEnf1cy7yDNuf4ZY0Bt7SAtkjdCa88l99w59pdK8TSiS2z1gxL3WmMQtHTiREoRmIyH0H1Z4Af3y//TiuCQTtCXNjgSNO7wH4p8mX5EoP0ImGxvlFYKn73QEhYgbGZCSBQCySTIrEqhqqCIFsADSYuhNqAouY6WaSmlMqgAMLGJt3qjxN1fR1GyEECEslpAo2UBCwcjbyp+ZGDg1Lls2sWLVm3UZJxRlWSi2uqKOmmqvVUmtttdcRWmq5WSutttZ6G117QnGtw8feeu9jMOig58HbgwZjTJ1p5mmzzDrb7HMsDSutvGyVVVdbfY2tO214vMuuu+2+x5FDKp187JRTTzv9jEuq3XTztVtuve32cMcnam9Yv/v+BmryRk0fpLxh/USNu7V+dCEuJ+aYgZhmAfDqCEgKqo5ZbJKzOnKOWewuhKZM0hycLY4YCOYjalc+sfuCnAVk9C/BLQCE/hXIBYfuTyD3PW4/Qm2Pp9ClByGnoQc1Jth3qRnamE8DHr9AAn98DvE/NPiz5787+p/vqF9ZpnlAwpGS3CJ99HmWVLs3LdSdNCPnxoY7/c5ou951IweE7fnfm7zvtKjoAf1RtCYyoRmGaR0FHlbkwCSbfH8OP3vwu2c6sr3TWPXsqlJgW0OB77wT/R+np7ypHIdVd9mrnMS7p49T9kBvlNp6h2o7oQzIVrYHIK2R76aKnIpq1N5PLolaXmA+zuRUb5c+27V79mz10jr3GwYObDM1KI2GoGbTrlna8VYEqRHnMtVGniheFZzjRg8URdhz+12ExtJ1PdoFA1tyKwyQXbqklSzneK1bc4vR80bJmAkv33P7SAC0E1OZs959WW1bwYaiTd8+YRUyHNpa7p2nNJSltLF7wXowMV2npnMmgnpmvv3swFWR2RlkzYxGP0k13Kj/3jl892AOguJ51WPNds+xSiFAMjF0q7ac/FZMCUmmbb1aFguuoYMX6pdWe79+MHAnk+pfmurT9LNl/66l1hwOvgzwRddwgT2NRZLou6Dc88yTp7blVghjnY7ZxWDOtbMCb5tlOVlQeg1NdjQP3Nmaeq1QQ04mD6MUH3dauQ7DaM9/hzRG42d9UMwOjeroJgHo5vzukSebzXXBA7CBXCwPDyGQjGl79NXJsFuU1VtZ44aT5gaqRSnNq+rcrY1TdVCwsJvMVea4FrGsEcxHLUV3qXERAlLjwWCYyQinUS2xxJjlRkoXthUXjrGU31Ok8MMHsNKBcntd7+y21q55F1RJgYNILMhAVjjvfWPlwvZ6s8KjubBCCtvWqtN3kNITUMCgRH02QJhln9vibQvmnQ2lPINx2XicIHA8dZJsOCpto3yHnCJee5c7uL/uJIwDH9Eo7xUCXDunjF43fgSuLMQmhTNn2ySqlfr03Ju+Op6vjoFgSlpJJXfJY8aDrICy5xmOAQVTT/saZvPMWz71w9TXbLrBMLGHc99y8r0k3iUYDT+c8b0xo3j1jmXXMwL9TWhauABZ8kWw+oM7EdlWPdBT60GAPAV6dvwfMmZ5RTh+cw7xJw++nJm59eoDjodjDIZj+gZYPOQ6pXwOVuBf1L5lFULQBprpICLxNUuZJcXdMy4JX5YnykcO7HmYsIS+C4Y+9dkbFGP3NQUjqOaxE3I+9WOzvnx/xGqBO8zKm9qmjQKIPQOZfKgi7H8QvdhXZhNmckZEEaW3BanbosjkQQFpXCrzWlf2pByQkA217xBDrC122VzbqbuTOBWFXXVstoW4xF7YH5IM/SwgkK2dGJCEI69mLkW2oFk6qZU90SP2j8IWfkyITm1axJHd7NGElW11kN3QwQorIRupdqw/s6TSdjuVFL3Y8TwGm2O2qMRjtGk4xdbw4WX1c+JxzULWNU5qfY/46Gp4ZcSknDGddxutj/cUcAwQzFmP8iDR/VLwktg6X/DXPycC4ffI7zuNH9A/2Q05s1HFMwPMI8nUKoFM1Xf+JCFVVBVXU5PVmRGJ7UUaXlCFOynvQdsdwgbk1Cs7nuXVua7dPZfHOmw4WPCNCwUG+N0jRfBSEc1hyxR+5o7th4gRrmU3CLIv9di2LXIThWb34vuQd8LbEOQUjBNpr4U6M9m/kCgCaGgZ0pNCT0L+oM6QKMt4VMlplaH2C48XHLx+/Zc2tMfrQd/fUDn8J8r+nMpwpbJnLtgk8gL4+1M1cCt2+syQbvUGEz1B3A3witeGW+MVZI0d1K1i0XFMEJmJZ0VygoBCpAbDGCSI3TkhKmzsrmde0dNcuKF7zBd9KuI1GDJDlUVECi7oamKTFAqb9mVUdLwn5V9tEaNRvTwPL3iaGwRs/suCp8hwT8DecVYtm11ldYfA3vOERcI8AhDxinefvHiTQHgES7W+Z3IwccUM3Q7Fb2CP+2YrKVDEfxEi+VDItMfAGZJihRAxo/hgYnGOnbTg7RATUs09QsTuRbc0EbEmbGel+vIlZ4ZHPNcWt3f32eptXLgbPGYELzB9xqGghzoj2TaECuT7Szag0GLnU2A0Hc3IMiZaQuwMHzX9N61F5+gBiQKoex3yEFt8NZ8DNzZmjz1zx8h2ukVuSMi8fl4ZfuccnguvfRhMggcEBdWjCBJ9CuqKeNFR0R985+NrfAt80dJ97OWYinsgCqQ73qfM4GYt668a/6pt+J3Gv2obPhuTYcOwfv6rYUZAzlNC2CV1qhOOEEnK/eeBCv9VhP/u6P+3IxTi7h7+DQ9kU6FC0KmCAAAABmJLR0QANwBRAGDKjvo2AAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4gYYDTEITeuGKgAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAACAASURBVHja7d3XcmNXtqXhsbY38ASp09HnJfqiI/rx+nFbJDywvesLEFSqSlJxg2AmzP9FZJSqojKV2AQwx3Jzmf/1v/9PJwAA8FAsHgEAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAACAAAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAACAAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAgADAIwAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAEAAAAAABAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAACAAAAAAAgAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAAAIAAAA4ME4PALcRFK1LLmOI8dx5DiufN+X53lyXVeObctYlozMdb8Io+Pf8a/+ml2npmmU54X2h72SNFFVVfzgARAA8IBF3xh5nq8gCBQEgcIglO/7cl1XlmX9UVDvSBTFGo1G2u22WqyWyvNcXdfxZgBAAMD9sy1bYRhqMBgoiiKFQSjHdWRkZIy569dujJHneZpOZ7JtR6+LV2VZRggAQADAnRf+KNRkNFEcx/J9X7ZtP+azsG2NRiMZI72+vSolBAAgAODeWJYl3/M1nUw0Go3led7DFv5/DQHD4UiSeQ8BKSEAAAEAd/IGdByNhiPNZjNFYUTh/6sQMBpKRnp9JQQAIADgxhljFPi+ZtOZxpOJPNe7+/X9s0OAZWs0HMnouByQpIQAAAQA3CDLGMVxrPnTXMPhiFH/Z56ZZR2XA8xxOSBJEkIAAAIAbquQjYZDzefPiqP4eJwPnw8Bg+FxJkBvOiQHQgAAAgBuo4CNR2M9z58VRRFT/mc+w8FgqFM3IUIAAAIAbmDkP9Lz87OikOL/9RAw0PtUACEAAAEA18kYo0EcH0f+Fy7+Xdf98UuddEt10EiWsc5aBrEsS4N4IPMimTfpcDioJQQAIADgmop/GISaP80vMu3fvffMr+tadVOrripVda22adS+B4Ebqv/yfF+j4Uiu654VAuJ4oBcZSUb7w56ZAAAEAFzJG8xxNJvNNBgMv7Thr+1aVWWlLMuUZanSLFOe56qbWnovet2NPp+6qjSbPX0hBMT6zbzIGGm/3zMTAIAAgF/LsixNxmONR+Ozj/p1XaeyKnXY77U/7HVIEtV1fTcj3aqqtFguJWM0m87ODgFRFOvl+bfjTMB+RwgAQADAr2GMURSGmk7OK2qS1DSNkuSg9Wat/eFwt9fjllWpxXIhI2n6lRAQR3p5nwnY7fdq25Y3IgACAH4u27Y1nc4UBMFZ6/5VVWmz3Wi1Wip7gCtxy7LU23IhY4wm06lc54wQYCzFUSzz/JskQgAAAgB+weh/EA80HAx6T/13XaeqKrVcrbRcLe9quv9TIWDxJhmj6WQqx3HOevZRFH0sB+z2O0IAAAIAftKbynE0Ho/lut5ZI//lcqnFe/F/NEVZ6u3tTUZGk8nkSyHgt5cXGWO03W0JAQD+DX1YcfHRfxzFisKo967/uq61Xq+0XK0esvj/EQIKvS5etdluzn4OxhiFYaSX5xeNR2NaLgMgAOB72batwWAgz+s3+m/bVvv9Tsv1SlVdPfxzPC0HbLdbNU3zhRAQ6uX5RZMxIQDAn7EEgIsKfL/36L/rOuV5puVqpbIseYjvz6QojjMBxkjj8eSso5Q/hgBJ2u52ZwcKAMwAAH/9ZnovNr7v9/p9TdNos90qzbjn/i9DwNurdruvzQQEQaiX59+OPRksrl8GQADABdmOozCMeo1Uu65TlqXa7/eMTP/m+eRFod/fXrX7wuj9GAICvby8aDKZnN2YCQABAPg3nusq8Pud+2+aRofDQXmR8wD/KQTkuV7fftduv1PTfiEE+IGen581JQQAD489ALgY1/V6b/4ry1JJmnJM7RMhIMtzvb69yshoNBqdtanvIwTMXyQZbbYbnv0/P/iPOyZYngIBAPgbQRDIsntu/ityZXnGw/tsCMgyvb69SkYaDc8PAb7v6+X5RcPB8OwZhXt/1l3XqW3b482TdaWyrFRWpdq2Vdu2H/8fgAAAAoDv957+z/Octf+ehSnNUr29vcoYo+GZtyyeQoDneRSxTwSB06+maVSWpfI8U5qlyotCdV2raRqeIwgAeFye58moXwAoioIvzjOKUpKmen09LgcMBoOzz/gbY866q+Ghg24QaDgcqm1blWWpNEuVJImyPFNZlMyogACAB3wz9bzA5jSawrkhINHr2++SkQbxgEY/P8kpNFmWJcdxFIahJuOJ8jzT4XDQIUmUZikzWyAA4HHYjv3p0eRpOpWuf18LAYckkV5fZV6keDCQZQgBvyIQOI6jwWCoKIo1ynMlyUHb3U5JmrDJEgQA3L++I9DTZip8cSYgOej1TXoxRnEcEwJ+8WcgiiIFQaA4Hmi/32m92Sgvcpa6QADAHX/59Sw8bUfxv4S263Q4HGSMJduyFIYR6/pXEgQ8z1MUxVqtV9rtdw99yRUIAMBx5KpO6jqJQdGFnucxUDHKvLIvWMfRYDCQ7/sKw1Cr1VI5G19xLUGVR4BfVrFwEcYYDeJYL/NnRv9X+vPxPE9Psyf913/9Dw0HbNgEMwAALlBc4jjWy/OLBmf2BMDPYdu2RsORHMeRvVgc2zpzUgAEAADnFv/fKP43w7IsxVEs+8WWbdvabNaqCQH4Ve9HHgFwy8X/N4r/Df7sgiDQ8/xZ0+lUDpcygRkAAP1H/uevJ7ddq7bhJMY/P2zJyHw0/7nU/opTK+b5/Fld12m92bAcAAIAgM8W//NH/m3bfpxRr2nG9JeV37Is2bYt13XleZ58z5PrerKd4/T9V/stGGPke8cQ0DSttrstfTFAAADw98X/5QLFf7ff6e3tVUma8mA/8dyNjIxl5Lmu4ihWHMcKglCe58n+whT+H9czz9U0jfaHPUcEQQAA8NfFf3iB4v/69qo0TSk2n/DxjFqprmtlea7VZq0wCDUcDjWIBwrD8OwgYIxRGEZ6enpSVVfKMq7HBgEAgC477U/xv0wgaJpGh+SgNEu1DbYaD0cajccK/OCsn49lWRoMBpqVM73Wr6oqlmVAAAAo/tEFi//rq9KM4n8pbdsqTVMVRaEkTTSbzjR8P+vf+8vYdjQejZVlmTbbDfsBQAAAHr34v7xccORP8f8Wx/X7g4qiVF4Umk1n8jyv96kBz/M0m06V55lSlgLwzTg8DFxr8Y+PxX94gd3+TPt/v67rVJSFFsuFXl9/V573vwHQGKMoijUeT760uRAgAAC3PPJ/pvjforqutd5s9Pr2u7Is6/3cLcvSeDRWxL0OIAAAD1j8LzjyTyj+P13TNtpst3pbvPWeCThdHjQZj2VbzAKAAABQ/HsW/98p/r9U2x6b+yxXS1VV2ev32ratOB4oikIeJAgAAMW/38ifaf8rmAloGm02G222W9V13ev3ep6n4XDELAAIAADF//PFn5H/9ajqSsvVUkma9PqZWJalKIoUBD4PEQQA4G6L/zPF/56VRaH1eq2yLHu9N3zPVxTFbAbEt6APAH5R5ZOMseR6rqzmMXPosQ/88TIYiv99a7tOh+Sg/X4n13369M/acRxFYSTHcegOCAIA7qX+G0VRpP/+n/8tPWzBMnIcW/6Z7WMp/relrmvt9jsNBgMFwec29xljFASBfN8nAIAAgPsZ/XqeJ8/zeBjnjio/iv8bxf8GdF2nNMt0SBJ5nv/p0Od5ngLPV2ISfsa4KPYAADdb/PfvxZ/CcEuzAGma9DoRYNmW/CCgMyAIAADF/1T8Xyn+tzgLkGYqis83BzI6bgZ0CAAgAAAUf4r/7SrKold3wNNy2Tk3DAIEAIDijyuaBciLXHXz+WUAx3EIACAAABR/iv/tB4BCTd18/ovasuQ6Lg8PBACA4o9bVpalmqbutQzgcmIGBACA4o/b1jSN6h4zAJLYBAgCAPBQxf9A8b9HXdepaZpeMwAcAwQBAHik4v9K8b/nn3Gnz/9cjcXXNS6LbaXAFY4M94e9Fgs6/N31z1qdetR/LgQCAQD3o65rlVXZ60vwvot/q6qqlaSJdvudiqKg+N/3D7zX/53yDwIA7makmySJfn/9XW3X8kC644iwbVvVda225ZkAIADgTgNA2zbK81xN2/BAAOAnY1cJAAAEAAAA8AhYAgBwUX+3W50NjQABAMAdFHljjCzL+vjn03+3bVuWZcl6DwJddzzz3jTN8ex716rtuvd9IK26938GQAAAcIUsyzreSmc7cl1Hvu/L93y5nifXcWTbjizLkszxDvs/jf7fz7x33TEIVHWtqqpUFoXyolBVVaqbWnVdq2nYFAoQAAD88qLveZ58z1cYBArCUIEfyHXdfyv2fRrVBO8j/tPov64rFUWhLM+VZZmKolBRFoQBgAAA4GcxxsixHUVRdPwVhgqCUI7jfEz1X+Lf8eN/2rYtz/M1HI5U1/V7GMiUpomSJFVZlSwTAAQAAN9V+F3X1XAw1HAwUBiG8jz/ONL/Sf9+SXJdV67rKo5jVaOR8jzXIfmjOyJNkgACAIALFv7RcKjxaKwgCOW67i/vPW+Mkef5cl1PURRrPBppv99rs9sSBAACAICvcJ3jSHs2nSqK4o9p/msLKI7jyLZj+UGg4XCozWaj7X6nsmRpACAAAPg0y7IUhqGmk6nGo5Fc17v62+ZOexPsKJbvB4oHA63XKx2SRHVd80MFCAAA/nHU77oaj8aaTqeKwuinrfFfekZgPBor8ANtthutN2tuTwQIAAD+rnAGQaCn2ZPGo/FVrPNf4vXMnbkCP9BytdQhObA3ACAAADixLEuDeKD5fK5BPJBt2/fzJeY4Go1G8jxXy9VSm81GNf0DAAIA8Ohsy9Z4PNb8aa4wDG9uyv+zAScMI708O3KcYxCoqoofPkAAAB70A27bmkymmj/NFQTBt0z5/2tP/+M6fKfTcvzxX2n+dGeAsYwsc9kgYoyR7/uaP81lW5YWy4WKsuRNABAAgAf7cDuOZpOpnuZz+Z5/seJ/KvjHvv21yvee/mVVqapKVdXxf2/fE4AxRrZly3Ecea4rz/Pkeb48zzveLeA4H5cKXYLruprOZjKWpbfFm4qi4M0AEACAx2DbtqaTqebzubwLFf+2bVWWpYqiUJ5nSrNUWZ6rrus/3ej3dzvxP9r/vk8LOI6jMAgVRZHCIJDvB/I87yJLFK7jajqZSp30tnhlJgAgAAD3z7IsTcYTzZ++XvxPo/0sz5QmiZIkUZKlH0W/758lSd3xv6gsS5Vlqd1+J8dxFEeR4ihWHA8UBMGXNyo6jqPJdKKua/W6eGNPAEAAAO6XMUaj4VDzp7l8/2vFv2kaZXmm3W6nw+GgPM/VtJffXd91naqq0ma71X5/UBhuNRwMNRqNFQTBl2YEXMfVZDpV3TRaLhecDgAIAMB9Fv8oio4b/sLzN/y1bauiLLTdbLTd7ZQX+U87W9+0jQ5JoizPtT8cNJlMvtyzwHM9Pc1mquta682aPgEAAQC4L8dC96Qojs/aYd91neq61uFw0Gq9VJKman7RiLlpGiVpoqIolKaJnmZPCsPo7GUBz/P1NHtSVZXaHw50DAQIAMB9sG1bk8lEo+FIttW/SHZdp7zItV6vtd6sVVXVLy+SXdepqittNhsVRaGnp7lGw5Fc1+39ZxljFIahZrMnlWWlvMh504AAwCMAbpsxRnEcazKZnFUcu65TkiZaLBba7Xbfss7/FW3XKUlT1fX/U1VVmk2nZ11eZFmWhoOh8kmuxeKN/QB4eBaPALhtnudpNpkp8IMzimurQ3LQ6+vv2m43V1f8f1SUpRaLhd4Wi7OvAXYcR5PxRHE8uOl7EAACAPDoH2DL0mg40mAw6L1bvus6pUmi19dX7ff7j8Y916yqK63WK70t3s4OAb7vazKZyDtjtgQgAAC4CoEfaDKeyHH6reZ1Xacsy/S2WOhwuI3if3Lazb9cLc46239aChgMhnd5LwJAAADu3OmSn3OO/BVlocVyod1+d1PF/8cQsHrfsFjXde/f7zjOcRbA83gjgQAA4MZG/2Gg4XDYe9f/aWf9dru56TPxVVVptVppt++/cdEYoyiMNGQWAAQAADf1wX1f++97yU/bttrv91qtVnexCz4vci2XS2Vp1ns/gG3bGo3OO1YIEAAA/JrRv+9rEMe9GuN0Xac8z7VaLVVW93M5TpqlWm9WvV+TMUaBH2jIiQAQAADcguO5/4F8v9/af90cN8+lZ4yWr1nbttpudzrs972XAhzX0WAw6L2JEiAAAPjpXNdVHEW9ilbXdUoOiba77VWf9T/XKdyURdEr3FjGUhCECoOANxYIAACuW+AHCsKw1+i/qkptdxuVZXmXz6TrOqVZpu1u13tjo+u6iqKYzYAgAAC44g+sZSkMAnnu54+vdV2nQ5LokCR3fQlO0zTa7XcqiqLX77NtW2EQymUZAAQAANfKcRyFYdhrtFpVlQ6Hw1lNc25NURTaH/a9ZgGMMfJ9T57n8wYDAQDAdXJdV0Hw+c1/XdcpyzMldz76/3EWIEmS3mHHdT35vs9pABAAAFwfY4w815PjfP7cetM0StNURVk8xDM6XWucZf1OOti2Ld/3ZbMPAAQAAFf3YTXWsUj1OPtfVdXDjP5/fM1ZlvbeDOh5nmz2AYAAAODqPqyWpaDHNHXXdSrLUnmRP9RzattWeVGorj+/DGCMkeu6cmybNxoIAACu7MNqW3J77P5v21ZFkatpmod7VkVZqCyrXjMfruPIcRz2AYAAAOB6GGPkOI4cx/50gTqNhB9p+v+kKitVPVsD27Yjx3EJAHgYLHgBV1rwLcuSZVmybVue6723rHV7/Rn++50BZVWpbVs1TaOu6+42FBhjjr8so6Zp1XatbPO5aX3LsjQcDCR1KopSdV2paVu1778eMUiBAADgJ47yXdeV7/mKwlBBEMrzPTm28xEKPj+itTV/mms2namuaxXvu+PTPFNZlqqr6i5uBPx4bu/PLggC+X6gKIpkGavXnzMeTzQcjdS1neqmVlmU76cKUhVFqaquVNc1YQAEAAAX+BDatvwgUBiEiqJIURjJ87w/RrNnTkkbY2Tbtmzb/iiMo9FYTdOoKHKlaao0S5XlucqiUHtDRc0YI8d2FAS+giBQGEQKw0Ce58uyrI9n1vfZWZYlS5Zkv/dc8AMNu6HarlVZlscAlabKskz5g+6vAAEAwAVGrYM4VhwPFEXR+zl0+9vWoE9hwrKs44VC8UBVVSl/bxR0SBJlWXbVlwVZ78sap2cWheG/Ff1veWay5ISOwiDUeDRWURRK0+MzS9KEWQEQAAD854LiOq6Gw6FGw6HCMJLrub2mqi/5d/E8T57nKY4HGhe5kiTRdrdTmqVXNbo93YEwGo0Vx7ECP/glO/b/2Ix5bMk8Go2Uppl2+532hz1BAAQAAH9ROOzj3fOTyURRGMl1r2fHuW3biqJYvh9oMBjqcNhrvVkry/PeDXUuXfgDP9BkMtFwMPzYD3EtocT3j8sOcRxrnI603mx0SA4fmy0BAgDwwGzLVhiGmk2nGg5Hclznl4z4PxsEwjD8mBXYbNfabLcqy/KnFjRjjHzP03g80WQ86d0B8WeHO8/zjrMCUaT9fq/1eqUsy696OQUgAADfWRhcT+PxWNPJVEEQ3Myd838KAlGs1Xqlw+HwU04NHPdGDDSbzRRHsWzbvomz+ZZlyfd8uVNXURhpvVlru92qrEpmA0AAAB6FZVmKo1iz2UzD4fDjGN+tBRjHcTQajeX7gTabtVab9bfNBhhjFPi+ptOZJuOxPO82b+ezLEthGMr1XIVhqNVqpSRNfulSCkAAAH7Gh8p2NB6PNZvNFIXRzYz6/7EwB4Hm82f5vq/FcqkkvewFQ5ZlaTAY6Gn2dGx4ZDs3/8xcxz0uX3i+VuulNtut6rrmAwICAHCPXNfVfPak6XR6syPYv/2ycByNxxM5rqvFYqHdfneRUa1lWZqMJ5o/zRVG4dXujzj3tUVRJMdx5Hm+lsuFyqrigwICAHAvTpvW5vNnTcYTua777f/OvxqBf3fgsCxLg3ggx7blOLbWm82Xjgvatq3JeKzn+bOCIPz2v/+veGanlsxPsyc5jqO3xZvKouRDAwIAcA/FP/ADPT8/azway7nwnfJd1330ov/xP9u2OXbv6/74e1i29dFM6NQc59JNco5LAqGe5y+yLFur9eqsqW3P9TSdTjWbzuT7l50tOd13cHpeXdeqbTs1baOufb8LwUhG73cu2JYs88PzsszFZyIcx9FkPJFt21osFhKXDoEAANx48Q8CvcxfNB6PL3ZU7XRxT1VXqspSRVGoKItjP/qqVNO2Uid1+mNEa44VTZZlyXu/T8D3/fez6t5HA5tLFNrTqHb+NJekXiHgdGxu/jTXdDK92N+p644Fvq5r1VWl4vTciuJ490Hz3qTnh+f28cyMJcd15Hve+3N7f2auI8d2LvZztW1bo+HouC+k67h5EAQA4Fb5vq/n+fNFin/Xde99+gtleaYsTZVk6XHXfdt+lPrPbL4ry1KHJJGRZCzr2D43jBRGkcL3y3K++vf9UwjoOi3Xq08tB/iep+fnF03Gk4vMlrRtq6oqlee50lOv/jz7UzOezzyzoiyUpqnM8cXJdV1FYagojBSGoXw/uEjzpuOtg0O1bUsAAAEAuEWnUex49LXi33Wd6rr+o7d8kijPc7Xd+VfQfhQ+SWqajwtsrM1aYRBqEMcaDAYKw+jL5+w9z9PT01xN22q9Wf/jxkDf8y9W/Nu2VZ7nSpKDDkmiNE1V1dWXTid03fvcQNd9zB5stlv5742R4jjWIB58XNb0lfB0rY2NQAAA8A9cx9X8af6lQnYa8Sdpou12qyQ5qPjGjnunf98hOSjNUm33Ow0HA41H448g8NWZgLqutdvv/vI1uK6r+Xz+5eLftq3KstB2t9N+v1OWZd/aoKjrOuVFoaIstdvvFEeRRqOxhoPhVbVzBggAwDezbVvT6VTTyfTs3f6n0et6s9Zut1NRFj+1W1zbtsqyTEVR6HBINJlMvtR8xxijIAw0f5qrqitlWfan1+PYtmbT2ZcDU13Xx1a7m5XSLPup5+q7rlNVVdrudkrSVPt4/6duhQABALhjljEaDUeaTWdnF/9jEdtpuVoqSdNf2iGubVulWaqyPF5v+zSbK47PK2iWsRTHseazJ/2/199VlsejbpZlaTQaf+mZfQSm9Uqb7fbLU/0XCQLbjbI8+wg2X10WAAgAwBULw1Cz2UxBEPT+su+6TmVZar1eabVeX1WP+LpptN3tVJalnmZzjcfjs4q1bdsaDkfK81zL1Upt1yoKI81m5x/1a5pGh8NBi+VCh+RwNS11265Tnud6fXtVURR6mj0pDMOb7/wIAgCAf+G6rqaT45TvOcU/yzMtl0utN+svNc/5zpFtmmWqX39XVVeaTWdnjWpd19VkMlWW5yrLQrPp9Kxndpot2e62WizelOX5VV6qU9e11uuVqrrS89OzBoMBIQAEAOBenI5ujUaj3tPjx8Ka6u3tTdvd9uovhSmrUovlQm3TaD6f994XYIxRGIaaTqcqy1Kj0fisgngsrGu9Ld9++nXE58wG7Pd7tU2jtn3RcDSUbbEvAAQA4Ob5nqfp5LjO27f453l+M8X/x+K7Wq/Udd3HJUB9QoBlWRqPxuq67qxNfz8W/6IobuKZdV2nJE3Vvf2uTt0xLBICQAAAbpdt2RqNxorOmMYuy1LL5UK73e7mroOtm0arzVoy0vP8pfdywLm7/ZumOU7731Dx/zEEpFmmt7dXGRmNRiOWA3C1eGcC/2n07/tnNfup61qb7Ubr7UZN29zka2+aRuv1WsvVUtVPuMWubduPDX9FeZsX5nyEgMWbkuSgtmv5EIEAANzcB8SyNBqN5Af9psCPhWyv1Wp183fA102j1XqlzXbzra/ltFyyWC3+rY/ALYaAJE20WC6UX+nmRYAAAPyH0f9w0G9D17Fz3PEYXFEWd/EcqqrScrX81mN4VV1ptV7pcDjcRcHsuk67/V7r9VpVXfFhAgEAuJkPhzEaDoa9N8A1TaPtZqMkSe5q5FcUhVarpYri8l0L27bVfrfXdru9ub0S/+l1bbYbHfaHu3pdIAAAd81xXcVx3GszW9d1StNEm+32Ztf9/+m1HZJEm+3mon0MTlP/6819jpSr6jizURQsBYAAANyEKAwV+P06/lVVpfVmo/JOpv7/1WmHfpalFytmTXv8M9ML/pnXFpzSLL272Q0QAIC7ZFu24CGyTQAAEYFJREFUojDq1Q73NPpPkkTtHY/0iqLQdre9yIbAruuUZZn2+91Vdke8bHDaKcszZgFAAACumes6CsKw19G/41W4e1VVedfP5njCIbnITv2maXTY75Xl+d2/p4qyOHYLZBYABADgevl+IN/ze49k0/S+R/8/FrNDcvjyqL0oiqu64Oe7ZwEOyeHmmhuBAAA8zofCsuT7fq/p/7ZtlaTJzTavOWcWIE2Tjyt/zyqIbaM0TR9i9H+S57nSX3wFNEAAAP6Gbdnyfb9XC9eyKpVmj/XFnueF8jw/u9NdXdXHTnkP9MyaplGapTffHAoEAOA+A4Bjy+9xA17XdSqKYzF8JHVTK8uzs5YBuq5TWRZKs8faFHe6Frp8kJkiEACAm2GMkeu4vaf/izz/Kb3yr66YZZnqqv9otm1bZXn+kB3yiqJUURbcEQACAHBtHMfp1fynaRrlD9rkpSgLVXXV+7Uf9xCkD/nM2rZRURRqGwIACADAdc0AuG6v9f+6rlUUjzmlW9e1qqp/ADg+s+IhA8Bpyeie+x6AAADcZgBwHOmTzf+6rlNd1yqrxwwAx7X8std0dtd1qqrqoS/IKauSAAACAHBtAcBxHBl9fgNgXdcP/WVelqW69vMj+WMAKB/6KFxVVWqahq6AIAAA1xQA+nT/67pOdVNLD/xFXje1uj4zAOrOWja4J23bqmk4CggCAHA9HwhjZKx+H4umrvWopazrOrVNq7bHDIA6qarrhw4AXdepaVpmAEAAAK5pBsDqcftf13UP39Wt6/oXMta/j7MAnQgAIAAA15IAjr96juYeOwD0H/0y8j0uhVD/QQAArudbmS/lnxIaeMjfHbQAAgDwzQnAWOahn5gx5tPHJin+PDcQAICrHWX1+aI1xsiy7Id+ZsYyn7434VTIKGaSZaxPHzeVROtgEACA79R2be9NfbZl6VHnAIwxsi1blvn8V0nXdQ9fzI7HTa1eF06xcRIEAOBbpwCkpkcAODUO6rtx8J44jtNrBqBtW3XtYx+BsyyrV78JiZMTIAAA31v/u/b9XP/nitMpANgPvAzged6n7044HZtsHvwiHNdxZdt2rxmAR7ttEgQA3KsrHTC3Xaeqrj+9D9AYI9txel0ffFdfIMaS53q9ZgDqun743gme58m2nR7vy5YAAAIA7iwFXFkQOI20+kxPO7Yjz/Me8ifovIefPgGgqqqH3gNgjJHve72WAJq6ObacBggAuEa9ds/LXO3xubqpe33Z2ratIAh6FcF74fueHNfpNZVdVsVDzwBYliXf83stm1RVpaZmDwAIALhSfb/ULWNdXdHsuk51VauuegYA35fTY0r3XkayQRDKdT6//NE0jcrysS8C8jxPnv/5ACAdb1xkBgAEAFytPruUjTGy7P47oX/WDEBZlp8uUsYYeb4v3/cf6udt27bCIJBlf/5rpK7rh74J0BijMAjle59/r7Rdq6IsOAUAAgCuOQD0u+HNtmx5rneFr6NRURa9XovneQrD8KGWAQI/UBAEn+4B0HWdyrJQVT/uZjbbthWF4fHo6GdDU1WrKAqaJ4EAgOvVd5eybdvyPO/qimbbtiqKQnX9+SlXx3YUR9HDnAawLEtxFMnrMZLtuk55nvd6rvfG931FUdxv+r8qVZYEABAAcMWKHtPmpwDg+/7VnQjsuk5FUaisyk//HmOMwjBSGDzGLIDneorjuNcSTl3XyovH3QB4DE2x/KDH9H/bKs9zlRwBBAEAVx0Aek5TWpZ13Dx3haPmsix7vx7P8zQcDK5yX8NFvzSMURRFCsOoV9gpy0J5nj/sSNb3fI2Go15No+q6VpZlrP+DAIDrDwB9diofz0MHCoPw6l5L3Ry/ePtMV1uWpcFwqOjO9wK4rqfxaNRruaNtW2VZrrIsHvKzYVu2RqNhr30ipz0TaZYy/Q8CAK5bVVe9ds8fi4mrKIp6rYn+DF3XKU1TlWXZ6/f5nq/xaHK3RwIty9JgMFAcD3o3/0mzpNc9C/fieFwy0Hg86TU71Lat0ixTWZR8uYAAgCsPAFWlPM97F5Q4jhX4wRXOaOTKslRN2/R6PcPhUIPB4C5nAXzP13Qy6bWL/binIleSPuZI1rEdTadTBX7QOzQdDgeuAQYBANevaRrleda7H0AYhBoM4qubBWjaVofDoVdTIElyPVfT6fTu+gLYtq3JZNJ77b9pGh2SRFX5eCNZyxiNRqPj2n/P0X+WpUpTpv9BAMAN6LpOWZ6rKPqt89q2rdFofHWzAF3X6ZAmyrK01851yxx3e8+ms14j5WtmjNFoONSk5zR213XKi1z7/V7tgxUyY4zi+Pg+6HtXRF1X2u52dP8DAQC3oyiK3gXTGKMojDQZj+Vc2Q76uq6PX8Q9z647jqPxaKzRcCTrDpYCojDUbPokz/d6j/73+73yIn+4z4Lv+3qazRVF/WZM2rZVkqRKkgOjfxAAcDuaplGSJmc1BRqPJxoMhle1dt51nQ7JQWma9l6LPRaAp94b5q6ukHm+ZrMnxXH86a5/H6P/PNd2t324s/+e52k+e9JwOOy9tFVVlTbbzfFaaoAAgFvRdZ2SJFGWZ71HL77v6+np6eqa6Zy+kOueoca8n5efP81vtkGQ67qazWYaj8a9+xvUda3NdtN7SejWua6rp9mTJpNp7yWgtm212++UpAmjfxAAcHvKqtJ+v+/d890YoziKNZ/Pe6+Z/oxZgN1+3//Gw/dTAc/PzzfXH8BzPT1NZ5pNZ71bHLddqyQ5aLffPdTo/1T8Z7P+z+y4hybTdrt96HbJIADgxmcB9vu9srT/LIBt2xqPxnp+uq4QUNe1NpvNWTMbx+WNsX777b80HAyv7rTDXwWxIAj0/Pys+fz5rJ9DURRab9a9+yjcMt/39Tx/1vxpftYlV3Vda7vdsPMfP439P/7nf/9fHgMurW3bjynwvlPHlmXJ8zxZlqWyuJ5rUJumkWUshWHY/zUZS57rHU86dJ3KqrrKkbFlWRoOBnp+ftFkMpHr9G/TXNe11quVNtvHWPs/bWJ9mT9resZsyenzstvvtFguH/q2RBAAcCfqppbneb0boJxGzZ7vyXEcVWV5FSGg6zrVdS3PdeX5fq8NcadC4bquguB4HWxd12qbRtcw1jv93WbTmZ6fnzWIz7vToG1b7XY7LVaLhxj927at0XCkl+cXjUajs459dl2nLMv09vamLM/44gABALev6zq1TaswDOQ4bv8QYB1vC/Q9X01Tq66bXz412rat6rpR4Pty3f5XGRtj5DiOgiD42BNQ180v7fbm2LYGcazfnl80m87k+/5ZyxRd1ynN0mMhy+67kFnGyA8CzZ/mep4/f6mddVmWelu8abffMfUPAgDuR1M30vsU6TkjytNywEexbOpfPq1c15XatlMQBHJs56yNfZZlyXU9RafrYbtObdv91CDg2LbCMNLT05Oe5y+K41iOc97rOTX8eXt70/6wv9tCZoyR73kajyf67flF49FYnuedvbmzqiqt1iut1itu/AMBAPc3C1DXtRzbOXtkeRo1R1Ek3/dljI7F8hcFge79i9t0UhAEss+8+McYI9u2FfiB4nig4P35dDq+tu8ooqep/kEcazqd6eX5RcPRUJ7rfemEQlmWWiwX2mw3d7nuf7q6ejQaaz5/1mw6UxCEX7r6uW5qbTcbvS0XvftmABcZBPAI8N2qqtJyvZTruWfvgj+FgPForCiMlKaJDoeDkjRVXuQ/veg0TaPVZi3LtvX09HTWru8fX5vneXLdqQaDobIsU5omSrNMWZ6pqqovhQFjjGzLOl69HIaKo0hRFMnz/IucSCjKQsvVQuvN+q5GsaewFAahojBUFMcKw/DsWZ9/ff/stju9LRcPdVICBAA84CxAlmVaLBaybVtxFJ/9BfpHsXQVxwMVZaE8z5W9F8uyKI+3970XzO+ciG6bRuv1SpYxH7u/v1IYTgXHdV0NBgOVZamiLFQUhYoiV14c//mjyHbdv70+c/yD/thw6PsK/EB+EMj3fHmed/Y0/98V/9VyqdX6WPxvsdnR6ZlJx2URz/MVBL58P1Dg+/Len9uljm82TaPdbqu3xevDNUkCAQAPGgIOyUH2wpb1Yn25M96PxTIKIzWjRnVTq6kb1XWluq7VNI3arvv+9WhjVBSFbNv+0pTwjyzLUhAECoJAbduqad5fX9OoqWtVp9f3wymC05KCYztynOOv09/Jtu2LF+e2bVVVlSzb1mw6u8kRvjFGlmX98NxOz+v47C7ds6Fuau12O729vSrLczb9gQCAx9C2rba7rYyRXp5/UxAEFylKlmW9b6pzP8LGxy91+hnn7E6F5Dt89vWdnuWpsH33aNwYozAMr+4Wxz5Df6Of98xOrZHfFm8qioLiDwIAHjAEbHfqOunl+UXhN7TH/RnF7xpGrtfw97CNTT/R/6DrOlVVqfV6reVqqYI1fxAA8KiattF2t1XXtZrPnxVH8dW3xwXODbx5nmu5Wh4vlKLHPwgA4Iux1Xa3U900ms/mGg6HZ3VRA65VXdc6HPZarlY6JIeHuxIZBADgb52uDq7rWmVZaDKZfqmpCnAt4bYoCm22G202GxUl6/0gAAB/GQLyPNfr4k1Znms2m30sCRAEcGvv5ao+XoW92ayVpCnd/UAAAP6T01WoeZ5rMh5rPJ6c3TkQ+NmFv65rpWmq9WatQ3JQXdeM+kEAAD6r7bpjM5+y1D45aDKeaDAYyHO9i52vBy72fm1bVXWlPMu13W21P+y/3LURIADgoTVtoyRJlGWZ4ijSaDg6Xprj+9/S0AboM9pvmubYgTLLtD/sdXjfx0LhBwEAuOAX7W5//IINg+OFOVEYKgjCj9ashAF89/tQ0kfRL97bTidpqjzP1XYthR8EAOC7tG2rJE2VZpkcxzn2tvf9Y6tc/9Tf/r0P/w/d3YCvFPyqqlSWxccdDGVZqChK1Q2jfRAAgJ/+5VxVlaqqOt4r8EP/dsdxPi5tcR1Htu18zBCcggHwx5vpj5bKbdeqbRrVda2qqlSUpaqqOt670NSq6+Z4wRRAAACuIwzUdX3srMaFagDQG2esAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAAAEAAAAQAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAACAAAAIAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAABAAAAAAAQAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAAAIAAAAgAAAAgB/9f6C2m89BbnloAAAAAElFTkSuQmCC\",\n};\n\nlet TYPES = null;\nexport function init() {\n    TYPES = {\n        MIME: getConfig(\"mime\", {}),\n        THUMBNAILER: (function() {\n            const set = new Set();\n            const thumbnailers = getConfig(\"thumbnailer\");\n            for (let i=0; i<thumbnailers.length; i++) {\n                set.add(thumbnailers[i]);\n            }\n            return set;\n        })(),\n    };\n}\n\nconst $tmpl = createElement(`\n    <a href=\"__TEMPLATE__\" class=\"component_thing no-select\" data-selectable=\"true\" draggable=\"false\" data-link>\n        <div class=\"component_checkbox\"><input name=\"select\" type=\"checkbox\"><span class=\"indicator\"></span></div>\n        <img class=\"component_icon\" loading=\"lazy\" draggable=\"false\" src=\"__TEMPLATE__\" alt=\"directory\">\n        <div class=\"info_extension\"><span class=\"ellipsis\"></span></div>\n        <span class=\"component_filename\">\n            <span class=\"file-details\"><span class=\"ellipsis\">\n                <span class=\"component_filesize\">(281B)</span>\n            </span></span>\n        </span>\n        <span class=\"component_datetime\"></span>\n        <div class=\"component_action\"></div>\n        <div class=\"selectionOverlay\"></div>\n    </a>\n`);\n\n// a filesystem \"thing\" is typically either a file or folder which have a lot of behavior builtin.\n// Probably one day we can rename that to something more clear but the gist is a thing can be\n// displayed in list mode / grid mode, have some substate to enable loading state for upload,\n// can toggle links, potentially includes a thumbnail, can be used as a source and target for\n// drag and drop on other folders and many other non obvious stuff\nexport function createThing({\n    name = \"\",\n    type = \"N/A\",\n    time = 0,\n    path = \"\",\n    size = 0,\n    loading = false,\n    offline = false,\n    link = \"\",\n    view = \"\",\n    search = \"\",\n    n = 0,\n    permissions = {},\n}) {\n    const [, ext] = formatFile(name);\n    const mime = TYPES.MIME[ext.toLowerCase()];\n    const $thing = assert.type($tmpl.cloneNode(true), HTMLElement);\n\n    // you might wonder why don't we use querySelector to nicely get the dom nodes? Well,\n    // we're in the hot path, better performance here is critical to get 60FPS.\n    const $link = $thing;\n    const $checkbox = $thing.children[0]; // = qs($thing, \".component_checkbox\");\n    const $img = $thing.children[1]; // = qs($thing, \"img\")\n    const $extension = $thing.children[2].firstElementChild; // = qs($thing, \".info_extension > span\");\n    const $label = $thing.children[3].firstElementChild.firstElementChild; // = qs($thing, \".component_filename .file-details > span\");\n    const $time = $thing.children[4]; // = qs($thing, \".component_datetime\");\n\n    $link.setAttribute(\"href\", link);\n    if (location.search) $link.setAttribute(\"href\", forwardURLParams(link, [\"share\", \"canary\"]));\n    $thing.setAttribute(\"data-droptarget\", type === \"directory\");\n    $thing.setAttribute(\"data-n\", n);\n    $thing.setAttribute(\"data-path\", path);\n    $thing.classList.add(\"view-\" + view);\n    $time.textContent = formatTime(time);\n    $img.setAttribute(\"src\", (type === \"file\" ? IMAGE.FILE : IMAGE.FOLDER));\n    $img.setAttribute(\"alt\", type);\n    $label.textContent = name;\n\n    if (type === \"file\") {\n        $extension.textContent = ext;\n        const $filesize = document.createElement(\"span\");\n        $filesize.classList.add(\"component_filesize\");\n        $filesize.textContent = formatSize(size);\n        $label.appendChild($filesize);\n    }\n    if (mime && view === \"grid\" && TYPES.THUMBNAILER.has(mime) && offline === false) {\n        $extension.style.display = \"none\";\n        $img.classList.add(\"thumbnail\");\n        const $placeholder = $img.cloneNode(true);\n        $placeholder.classList.add(\"placeholder\");\n        $placeholder.setAttribute(\"src\", IMAGE.THUMBNAIL_PLACEHOLDER);\n        $img.parentElement.appendChild($placeholder);\n\n        $img.src = \"api/files/cat?path=\" + encodeURIComponent(path) + \"&thumbnail=true\" + location.search.replace(\"?\", \"&\");\n        $img.loaded = false;\n        const t = new Date().getTime();\n        $img.onload = async() => {\n            const duration = new Date().getTime() - t;\n            $img.loaded = true;\n            await Promise.all([\n                animate($img, {\n                    keyframes: opacityIn(),\n                    time: duration > 1500 ? 300 : duration > 50 ? 200 : 0,\n                }),\n                animate($placeholder, {\n                    keyframes: opacityOut(),\n                    time: duration > 1500 ? 300 : duration > 50 ? 200 : 0,\n                }),\n            ]);\n            $placeholder.remove();\n        };\n        const id = setInterval(() => { // cancellation when image is outside the viewport\n            if ($img.loaded === true) return clearInterval(id);\n            else if (typeof $thing.checkVisibility !== \"function\") return clearInterval(id);\n            else if ($thing.checkVisibility() === false) {\n                $img.src = \"\";\n                clearInterval(id);\n            }\n        }, 250);\n    }\n\n    if (loading) {\n        $img.setAttribute(\"src\", IMAGE.LOADING);\n        $img.setAttribute(\"alt\", \"loading\");\n        $link.removeAttribute(\"href\");\n        $extension.innerHTML = \"\";\n        return $thing;\n    } else if (type === \"hidden\") {\n        $thing.classList.add(\"hidden\");\n        return $thing;\n    } else if (offline) {\n        $thing.setAttribute(\"data-selectable\", \"false\");\n        $link.removeAttribute(\"href\");\n        $checkbox.classList.add(\"hidden\");\n        return $thing;\n    }\n\n    const checked = isSelected(n);\n    if (permissions && permissions.can_move !== false) $thing.setAttribute(\"draggable\", \"true\");\n    $thing.classList.add(checked ? \"selected\" : \"not-selected\");\n    $checkbox.firstElementChild.checked = checked;\n    $checkbox.onclick = (e) => {\n        if (e.target.nodeName !== \"INPUT\") e.preventDefault(); // eg: keyboard navigation\n        e.stopPropagation();\n        addSelection({\n            n,\n            path: $thing.getAttribute(\"data-path\"),\n            shift: (e.shiftKey || e.metaKey),\n            files: (files$.value || []),\n        });\n    };\n    if (getConfig(\"open_mode\") === \"double_click\") $thing.onclick = (e) => {\n        if (isSelected(n) && expandSelection().length === 1) return;\n        e.preventDefault();\n        e.stopPropagation();\n        clearSelection();\n        $checkbox.onclick(e);\n    };\n    $thing.ondragstart = (e) => {\n        if (expandSelection().length > 0) return e.preventDefault();\n        clearSelection();\n        $thing.classList.add(\"hover\");\n        $checkbox.style.display = \"none\";\n        e.dataTransfer.setData(\"path\", path);\n    };\n    $thing.ondrop = async(e) => {\n        $thing.classList.remove(\"hover\");\n        $checkbox.style.display = \"\";\n        const from = e.dataTransfer.getData(\"path\");\n        let to = path;\n        if ($thing.getAttribute(\"data-droptarget\") !== \"true\") return;\n        else if (from === to) return;\n\n        if (isDir(to)) {\n            const [, fromName] = extractPath(from);\n            to += fromName;\n            if (isDir(from)) to += \"/\";\n        }\n        await mv(from, to).toPromise();\n    };\n    $thing.ondragover = (e) => {\n        if (isNativeFileUpload(e)) return;\n        else if ($thing.getAttribute(\"data-droptarget\") !== \"true\") return;\n        e.preventDefault();\n        $thing.classList.add(\"hover\");\n    };\n    $thing.ondragleave = () => {\n        $thing.classList.remove(\"hover\");\n    };\n    return $thing;\n}\n\nfunction formatTime(unixTime) {\n    if (unixTime <= 0) return \"\";\n    const date = new Date(unixTime);\n    if (!date) return \"\";\n    // Intl.DateTimeFormat is slow and in the hot path, so\n    // let's render date manually if possible\n    if (navigator.language.substr(0, 2) === \"en\") {\n        return date.getFullYear() + \"/\" +\n            (date.getMonth() + 1).toString().padStart(2, \"0\") + \"/\" +\n            date.getDate().toString().padStart(2, \"0\");\n    }\n    return new Intl.DateTimeFormat(navigator.language).format(date);\n}\n\nfunction formatFile(filename) {\n    const fname = filename.split(\".\");\n    if (fname.length < 2) {\n        return [filename, \"\"];\n    }\n    const ext = fname.pop();\n    return [fname.join(\".\"), ext];\n}\n\nfunction formatSize(bytes) {\n    if (Number.isNaN(bytes) || bytes < 0 || bytes === undefined) {\n        return \"\";\n    } else if (bytes < 1024) {\n        return \"(\"+bytes+\"B)\";\n    } else if (bytes < 1048576) {\n        return \"(\"+Math.round(bytes/1024*10)/10+\"KB)\";\n    } else if (bytes < 1073741824) {\n        return \"(\"+Math.round(bytes/(1024*1024)*10)/10+\"MB)\";\n    } else if (bytes < 1099511627776) {\n        return \"(\"+Math.round(bytes/(1024*1024*1024)*10)/10+\"GB)\";\n    } else {\n        return \"(\"+Math.round(bytes/(1024*1024*1024*1024))+\"TB)\";\n    }\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_3d/init.js",
    "content": "import { createElement, onDestroy } from \"../../../lib/skeleton/index.js\";\nimport { OrbitControls } from \"../../../lib/vendor/three/OrbitControls.js\";\n\nexport default function({ THREE, $page, $menubar, mesh, refresh, is2D, background }) {\n    // setup the dom\n    const renderer = new THREE.WebGLRenderer({ antialias: true, shadowMapEnabled: true });\n    renderer.shadowMap.enabled = true;\n    renderer.shadowMap.type = THREE.PCFSoftShadowMap;\n    renderer.setClearColor(0xf5f5f5);\n    renderer.setSize($page.clientWidth, $page.clientHeight);\n    renderer.setPixelRatio(window.devicePixelRatio);\n    $page.appendChild(renderer.domElement);\n\n    // center everything\n    const box = new THREE.Box3().setFromObject(mesh);\n    const center = box.getCenter(new THREE.Vector3());\n    const size = box.getSize(new THREE.Vector3());\n    const maxDim = Math.max(size.x, size.y, size.z);\n\n    // setup the scene, camera and controls\n    const scene = new THREE.Scene();\n    scene.background = new THREE.Color(background);\n    const camera = new THREE.PerspectiveCamera(\n        45,\n        $page.clientWidth / $page.clientHeight,\n        Math.max(0.1, maxDim / 100),\n        maxDim * 1000,\n    );\n    const controls = new OrbitControls(camera, renderer.domElement);\n    if (is2D()) {\n        controls.zoomToCursor = true;\n        controls.enableRotate = false;\n        controls.mouseButtons = {\n            LEFT: THREE.MOUSE.PAN,\n            MIDDLE: THREE.MOUSE.DOLLY,\n            RIGHT: THREE.MOUSE.ORBIT\n        };\n    }\n\n    scene.add(mesh);\n    mesh.castShadow = true;\n    mesh.receiveShadow = true;\n    camera.position.set(center.x, center.y, center.z + maxDim * (is2D() ? 1.3 : 1.8));\n    controls.target.copy(center);\n\n    const mixer = new THREE.AnimationMixer(mesh);\n    if (mesh.animations.length > 0) {\n        const ICON = {\n            PLAY: \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgdmlld0JveD0iMCAwIDU4Ljc1MiA1OC43NTIiCiAgIHZlcnNpb249IjEuMSIKICAgaWQ9InN2ZzE1OCIKICAgc29kaXBvZGk6ZG9jbmFtZT0icGxheS5zdmciCiAgIGlua3NjYXBlOnZlcnNpb249IjEuMi4yIChiMGE4NDg2NTQxLCAyMDIyLTEyLTAxKSIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMTYyIiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0ibmFtZWR2aWV3MTYwIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzAwMDAwMCIKICAgICBib3JkZXJvcGFjaXR5PSIwLjI1IgogICAgIGlua3NjYXBlOnNob3dwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZWNoZWNrZXJib2FyZD0iMCIKICAgICBpbmtzY2FwZTpkZXNrY29sb3I9IiNkMWQxZDEiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGlua3NjYXBlOnpvb209IjE1LjQyMDc1MiIKICAgICBpbmtzY2FwZTpjeD0iMjkuMzQzNTc2IgogICAgIGlua3NjYXBlOmN5PSIyNS45MzkwNzMiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxOTA0IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjExNTciCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjciCiAgICAgaW5rc2NhcGU6d2luZG93LXk9IjM0IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ic3ZnMTU4IiAvPgogIDxwYXRoCiAgICAgZD0iTSA0NS42MDY5MTMsMjUuMTc0NzEyIDIyLjY5MTQwMSw2LjEwODk4MjUgYyAtMS40Njk2MjUsLTAuOTA3ODUyNSAtMy4zNzIzNTQsLTAuOTA1Mzc2NiAtNC44MzY1ODQsMCAtMS40OTM1MTUsMC45MjEwNTc3IC0yLjQyMjE0NSwyLjY0MTg1MSAtMi40MjIxNDUsNC40ODk3NDM1IFYgNDguNzMyNjYgYyAwLDEuODQ4NzE4IDAuOTI3ODYsMy41Njk1MTEgMi40MTI4OTcsNC40ODU2MTcgMC43MzQ0MjcsMC40NTgwNTMgMS41NzM2NjMsMC42OTk4NzIgMi40MjY3NjksMC42OTk4NzIgMC44NTA3OTUsMCAxLjY4OTI2LC0wLjI0MDk5NCAyLjQyMDYwNCwtMC42OTU3NDUgbCAyMi45MTU1MTIsLTE5LjA2NzM4IGMgMS40OTE5NzQsLTAuOTIzNTMzIDIuNDE4MjkyLC0yLjY0MzUwMSAyLjQxODI5MiwtNC40ODg5MTggLTcuN2UtNCwtMS44NDI5NDEgLTAuOTI2MzE4LC0zLjU2MjkwOSAtMi40MTk4MzMsLTQuNDkxMzk0IHogbSAtMi42MDU3MDIsNC42OTM1OTggYyAtMjguNjY3NDc0LC0xOS45MTIyMDY3IC0xNC4zMzM3MzcsLTkuOTU2MTAzIDAsMCB6IgogICAgIHN0eWxlPSJmaWxsOiNmMmYyZjIiCiAgICAgaWQ9InBhdGgxNTYiCiAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Nzc2NzY2NjY2NjIiAvPgo8L3N2Zz4K\",\n            PAUSE: \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgdmlld0JveD0iMCAwIDU4Ljc1MiA1OC43NTIiCiAgIHZlcnNpb249IjEuMSIKICAgaWQ9InN2ZzE1OCIKICAgc29kaXBvZGk6ZG9jbmFtZT0icGF1c2Uuc3ZnIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIxLjIuMiAoYjBhODQ4NjU0MSwgMjAyMi0xMi0wMSkiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGRlZnMKICAgICBpZD0iZGVmczE2MiIgLz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9Im5hbWVkdmlldzE2MCIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiMwMDAwMDAiCiAgICAgYm9yZGVyb3BhY2l0eT0iMC4yNSIKICAgICBpbmtzY2FwZTpzaG93cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VjaGVja2VyYm9hcmQ9IjAiCiAgICAgaW5rc2NhcGU6ZGVza2NvbG9yPSIjZDFkMWQxIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTp6b29tPSIxNS40MjA3NTIiCiAgICAgaW5rc2NhcGU6Y3g9IjI4LjQzNTcwOSIKICAgICBpbmtzY2FwZTpjeT0iMjUuOTM5MDczIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTkwNCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMTU3IgogICAgIGlua3NjYXBlOndpbmRvdy14PSI3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSIzNCIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9InN2ZzE1OCIgLz4KICA8cmVjdAogICAgIHN0eWxlPSJmaWxsOiNmMmYyZjI7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjAuOTM2NjQxIgogICAgIGlkPSJyZWN0NTA3IgogICAgIHdpZHRoPSIzOS42NTU5MTQiCiAgICAgaGVpZ2h0PSI0NS4xMjEzNTciCiAgICAgeD0iMTAuMzExNDcyIgogICAgIHk9IjcuMTUyMjY4OSIKICAgICByeD0iNS43NSIgLz4KPC9zdmc+Cg==\",\n        };\n        const $button = createElement(`<img class=\"component_icon\" draggable=\"false\" src=\"${ICON.PLAY}\" alt=\"play\">`);\n        const action = mixer.clipAction(mesh.animations[0]);\n        let isPlaying = false;\n        $button.onclick = () => {\n            if (isPlaying === false) action.play();\n            else action.stop();\n            isPlaying = !isPlaying;\n            $button.setAttribute(\"src\", isPlaying ? ICON.PAUSE : ICON.PLAY);\n        };\n        $menubar.add($button);\n    }\n\n    const onResize = () => {\n        camera.aspect = $page.clientWidth / $page.clientHeight;\n        camera.updateProjectionMatrix();\n        renderer.setSize($page.clientWidth, $page.clientHeight);\n    };\n    window.addEventListener(\"resize\", onResize);\n    onDestroy(() => window.removeEventListener(\"resize\", onResize));\n\n    const clock = new THREE.Clock();\n    refresh.push(() => {\n        controls.update();\n        renderer.render(scene, camera);\n        mixer.update(clock.getDelta());\n    });\n\n    return { renderer, scene, camera, controls, box };\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_3d/scene_cube.js",
    "content": "import { onDestroy } from \"../../../lib/skeleton/index.js\";\nimport { ViewCubeGizmo, SimpleCameraControls, ObjectPosition } from \"../../../lib/vendor/three/viewcube.js\";\n\nexport default function({ camera, renderer, refresh, controls }) {\n    const viewCubeGizmo = new ViewCubeGizmo(camera, renderer, {\n        pos: ObjectPosition.RIGHT_BOTTOM,\n        dimension: 130,\n        faceColor: 0xf9f9fa,\n        outlineColor: 0xe2e2e2,\n    });\n\n    const simpleCameraControls = new SimpleCameraControls(camera);\n    simpleCameraControls.setControls(controls);\n\n    refresh.push(() => {\n        viewCubeGizmo.update();\n        simpleCameraControls.update();\n    });\n\n    const onCubeClick = (event) => simpleCameraControls.flyTo(event.quaternion);\n    viewCubeGizmo.addEventListener(\"change\", onCubeClick);\n    onDestroy(() => viewCubeGizmo.removeEventListener(\"change\", onCubeClick));\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_3d/scene_light.js",
    "content": "import { settings_get } from \"../../../lib/settings.js\";\nimport * as THREE from \"../../../lib/vendor/three/three.module.js\";\n\nconst LIGHT_COLOR = 0xf5f5f5;\n\nexport default function({ scene, box, camera }) {\n    addLight(\n        scene,\n        new THREE.AmbientLight(LIGHT_COLOR),\n        settings_get(\"viewerpage_3d_light\", 2),\n    );\n\n    // to make things \"look nice\", a good setup is to get lights positioned\n    // in a 3D cube with a couple \"twist\" in term of position & intensity\n    const l = addLight.bind(this, scene, new THREE.DirectionalLight(LIGHT_COLOR));\n    l(0.25, [plus(box.max.x*3), 0, 20]); // right\n    l(0.25, [minus(box.min.x*3), 0, -20]); // left\n    l(0.35, [0, plus(box.max.y*4), 20]); // top\n    l(0.35, [0, minus(box.min.y*4), -20]); // bottom\n\n    l(0.2, [0, 0, plus(7*box.max.z)]); // front\n    l(0.2, [0, 0, minus(15*box.min.z)]); // back\n\n    l(0.4, [camera.position.x, camera.position.y, camera.position.z]); // camera\n}\n\nfunction addLight(scene, light, intensity, pos = []) {\n    light = light.clone();\n    light.intensity = intensity;\n    light.position.set(...pos);\n    if (light.type !== \"AmbientLight\") light.castShadow = true;\n    scene.add(light);\n}\n\nconst plus = notZero.bind(null, 1);\nconst minus = notZero.bind(null, -1);\nfunction notZero(sgn, n) {\n    if (n === 0) return sgn;\n    return n;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_3d/toolbar.js",
    "content": "import { createElement } from \"../../../lib/skeleton/index.js\";\nimport { qs } from \"../../../lib/dom.js\";\nimport * as THREE from \"../../../lib/vendor/three/three.module.js\";\n\nexport default function(render, { camera, controls, mesh, $menubar, $toolbar, is2D }) {\n    if (mesh.children.length <= 1) return;\n\n    $menubar.add(buttonLayers({ $toolbar }));\n    render(createChild(\n        document.createDocumentFragment(),\n        mesh,\n        0,\n        { camera, controls, is2D }\n    ));\n}\n\nfunction buttonLayers({ $toolbar }) {\n    const $button = createElement(`<img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9IiNmMmYyZjIiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJtIDEuODI0MDExMiw2LjUzMDQxMTMgOS44OTUxMjU4LDUuMzc3MjgwNyBhIDAuNTkyMzE0MDQsMC41OTIzMTQwNCAwIDAgMCAwLjU1NzQ3NCwwIGwgOS44OTUxMywtNS4zNzcyODA3IGEgMC41MTEwMTYwMiwwLjUxMTAxNjAyIDAgMCAwIC0wLjA1ODA4LC0wLjk0MDczMzkgbCAtOS44OTUxMiwtNC4wNDE2NzI1IGEgMC41ODA3MDAwNSwwLjU4MDcwMDA1IDAgMCAwIC0wLjQ0MTMzNCwwIEwgMS44ODIwODI1LDUuNTg5Njc3NCBhIDAuNTExMDE2MDIsMC41MTEwMTYwMiAwIDAgMCAtMC4wNTgwNzEsMC45NDA3MzM5IHoiIC8+CiAgPHBhdGggZD0iTSAyMi4xMTM2NywxMC40NDQzMzIgMTkuOTg4MzA2LDkuNTM4NDM3NiAxMi4yNzY2MTEsMTMuNzMxMDkxIGEgMC41OTIzMTQwNCwwLjU5MjMxNDA0IDAgMCAxIC0wLjU1NzQ3NCwwIEwgNC4wMDc0NDM1LDkuNTM4NDM3NiAxLjg4MjA4MTIsMTAuNDQ0MzMyIGEgMC41NTc0NzIwNCwwLjU1NzQ3MjA0IDAgMCAwIDAsMC45ODcxODcgbCA5Ljg5NTEyOTgsNS42OTA4NTkgYSAwLjUzNDI0NDA3LDAuNTM0MjQ0MDcgMCAwIDAgMC41NTc0NzEsMCBsIDkuODk1MTMsLTUuNjkwODU5IEEgMC41NTc0NzIwNCwwLjU1NzQ3MjA0IDAgMCAwIDIyLjExMzY3LDEwLjQ0NDMzMiBaIiAvPgogIDxwYXRoIGQ9Im0gMjIuMTEzNjcsMTUuNjAwOTQzIC0xLjgxMTc4NCwtMC43ODk3NSAtOC4wMjUyNzUsNC4zNjY4NjYgYSAwLjU5MjMxNDA0LDAuNTkyMzE0MDQgMCAwIDEgLTAuNTU3NDc0LDAgbCAtOC4wMjUyNzE1LC00LjM2Njg2NiAtMS44MTE3ODQzLDAuNzg5NzUgYSAwLjU2OTA4NjAzLDAuNTY5MDg2MDMgMCAwIDAgMCwxLjAxMDQyMiBsIDkuODk1MTI5OCw1LjgwNjk5OCBhIDAuNTkyMzE0MDQsMC41OTIzMTQwNCAwIDAgMCAwLjU1NzQ3MSwwIGwgOS44OTUxMywtNS44MDY5OTggQSAwLjU2OTA4NjAzLDAuNTY5MDg2MDMgMCAwIDAgMjIuMTEzNjcsMTUuNjAwOTQzIFoiIC8+Cjwvc3ZnPgo=\" alt=\"layers\">`);\n    $button.onclick = () => $toolbar.classList.toggle(\"open\");\n    return $button;\n}\n\nfunction createChild($fragment, mesh, child = 0, opts) {\n    if ([\"Bone\"].indexOf(mesh.type) >= 0) return;\n    buildDOM($fragment, mesh, child, opts);\n    if (mesh.children.length > 0 && child < 4) {\n        for (let i=0; i<mesh.children.length; i++) {\n            if (mesh.children[i].type === \"Group\" || !!mesh.children[i].name) {\n                createChild($fragment, mesh.children[i], child + 1, opts);\n            }\n        }\n    }\n    return $fragment;\n}\n\nfunction buildDOM($fragment, child, left, { camera, controls, is2D }) {\n    const $label = createElement(`\n        <label class=\"no-select\" style=\"padding-left: ${left*20}px\">\n            <div class=\"component_checkbox\">\n                <input type=\"checkbox\" ${child.visible ? \"checked\" : \"\"} />\n                <span class=\"indicator\"></span>\n            </div>\n            <span class=\"text\">${name(child)}</span>\n        </label>\n    `);\n    qs($label, \"input\").onchange = () => child.visible = !child.visible;\n    $label.onclick = async(e) => {\n        if (is2D()) return;\n        else if (e.target.nodeName === \"INPUT\" || e.target.classList.contains(\"component_checkbox\")) return;\n        e.preventDefault(); e.stopPropagation();\n        getRootObject(child).traverse((c) => {\n            if (!c.material) return;\n            c.material.opacity = c.id === child.id || c.parent.id === child.id ? 1 : 0.2;\n            c.material.depthWrite = c.material.opacity === 1;\n        });\n        await flyTo({ mesh: child, camera, controls });\n    };\n    $fragment.appendChild($label);\n}\n\nasync function flyTo({ mesh, camera, controls }) {\n    const box = new THREE.Box3().setFromObject(mesh);\n    const size = box.getSize(new THREE.Vector3());\n\n    const targetLookAt = box.getCenter(new THREE.Vector3());\n    const targetDistance = Math.max(size.x, size.y, size.z) * 1.1;\n    const targetPosition = targetLookAt.clone().add(new THREE.Vector3(targetDistance, targetDistance, targetDistance));\n\n    const [startPosition, startLookAt] = [camera.position.clone(), controls.target.clone()];\n    const startTime = performance.now();\n    return new Promise((resolve) => (function animate() {\n        const t = Math.min((performance.now() - startTime) / 500, 1);\n        camera.position.lerpVectors(startPosition, targetPosition, t);\n        controls.target.lerpVectors(startLookAt, targetLookAt, t);\n        controls.update();\n        t < 1 ? requestAnimationFrame(animate) : resolve();\n    })());\n}\n\nfunction getRootObject(mesh) {\n    if (mesh.type === \"Scene\" || mesh.parent.type === \"Scene\") return mesh;\n    return getRootObject(mesh.parent);\n}\n\nfunction name(mesh) {\n    if (mesh.name) return mesh.name;\n    else if (mesh.isGroup && mesh.uuid) return `group: ${mesh.uuid}`;\n    else if (mesh.uuid) return mesh.uuid;\n    return \"N/A\";\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_3d.css",
    "content": ".component_3dviewer .threeviewer_container {\n    position: relative;\n    overflow: hidden;\n}\n\n.component_3dviewer .threeviewer_container, .component_3dviewer .threeviewer_container .drawarea {\n    height: 100%;\n}\n\n.component_3dviewer .toolbar.open {\n    transition: 0.3s ease transform;\n    transform: translateX(0px);\n    height: 300px;\n}\n.component_3dviewer .toolbar {\n    position: absolute;\n    top: 0;\n    right: 0;\n    transform: translateX(250px);\n    transition: 0.1s ease transform;\n    border-left: 1px solid #e2e2e205;\n    background: #e2e2e205;\n    color: var(--dark);\n    width: 250px;\n    z-index: 1;\n    padding-top: 20px;\n}\n\n.component_3dviewer .toolbar label {\n    display: block;\n    text-transform: capitalize;\n    margin-bottom: -5px;\n}\n\n.component_3dviewer .toolbar label .component_checkbox {\n    margin-top: -10px;\n}\n\n.component_3dviewer .toolbar label .text {\n    margin-left: -10px;\n    display: inline-block;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    width: calc(100% - 25px);\n    cursor: pointer;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_3d.d.ts",
    "content": "export default function(any): void;"
  },
  {
    "path": "public/assets/pages/viewerpage/application_3d.js",
    "content": "import { createElement, createRender, nop } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport { qs, safe } from \"../../lib/dom.js\";\nimport { AjaxError } from \"../../lib/error.js\";\nimport { load as loadPlugin } from \"../../model/plugin.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\nimport { createLoader } from \"../../components/loader.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport componentDownloader, { init as initDownloader } from \"./application_downloader.js\";\nimport { renderMenubar, buttonDownload } from \"./component_menubar.js\";\n\nimport * as THREE from \"../../lib/vendor/three/three.module.js\";\nimport setup3D from \"./application_3d/init.js\";\nimport withLight from \"./application_3d/scene_light.js\";\nimport withCube from \"./application_3d/scene_cube.js\";\nimport ctrlToolbar from \"./application_3d/toolbar.js\";\n\nclass I3DLoader {\n    load() { throw new Error(\"NOT_IMPLEMENTED\"); }\n    transform() { throw new Error(\"NOT_IMPLEMENTED\"); }\n    background() { return 0xf2f2f4; }\n    is2D() { return false; }\n}\n\nexport default async function(render, { mime, acl$, getDownloadUrl = nop, getFilename = nop, hasCube = true, hasMenubar = true }) {\n    const $page = createElement(`\n        <div class=\"component_3dviewer\">\n            <component-menubar filename=\"${safe(getFilename())}\" class=\"${!hasMenubar && \"hidden\"}\"></component-menubar>\n            <div class=\"threeviewer_container\">\n              <div class=\"drawarea\"></div>\n              <div class=\"toolbar scroll-y\"></div>\n            </div>\n        </div>\n    `);\n    render($page);\n\n    const $menubar = renderMenubar(\n        qs($page, \"component-menubar\"),\n        buttonDownload(getFilename(), getDownloadUrl()),\n    );\n    const $draw = qs($page, \".drawarea\");\n    const $toolbar = qs($page, \".toolbar\");\n\n    const removeLoader = createLoader($draw);\n    await effect(rxjs.from(loadPlugin(mime)).pipe(\n        rxjs.mergeMap(async(loader) => {\n            if (!loader) {\n                componentDownloader(render, { mime, acl$, getFilename, getDownloadUrl });\n                return rxjs.EMPTY;\n            }\n            return new (await loader(I3DLoader, { THREE }))();\n        }),\n        rxjs.mergeMap((loader) => new rxjs.Observable((observer) => loader.load(\n            getDownloadUrl(),\n            (object) => observer.next(loader.transform(object)),\n            null,\n            (err) => observer.error(err),\n        )).pipe(\n            removeLoader,\n            rxjs.mergeMap((mesh) => create3DScene({\n                mime,\n                mesh,\n                is2D: loader.is2D,\n                background: loader.background(),\n                $draw,\n                $toolbar,\n                $menubar,\n                hasCube,\n            })),\n        )),\n        rxjs.catchError((err) => {\n            let _err = err;\n            console.log(\"ERR\", err);\n            if (err.response && err.response.status === 401) {\n                _err = new Error(err.message);\n                _err.status = err.response.status;\n                _err = new AjaxError(err.message, _err, \"Not Authorised\");\n            }\n            return ctrlError()(_err);\n        }),\n    ));\n}\n\nfunction create3DScene({ mesh, $draw, $toolbar, $menubar, hasCube, is2D, background }) {\n    const refresh = [];\n    const { renderer, camera, scene, controls, box } = setup3D({\n        THREE,\n        $page: $draw,\n        mesh,\n        refresh,\n        $menubar,\n        is2D,\n        background,\n    });\n\n    withLight({ scene, box, camera });\n    if (hasCube && !is2D()) withCube({ camera, renderer, refresh, controls });\n    ctrlToolbar(createRender($toolbar), {\n        mesh,\n        controls,\n        camera,\n        refresh,\n        $menubar,\n        $toolbar,\n        is2D,\n    });\n\n    return rxjs.animationFrames().pipe(rxjs.tap(() => {\n        refresh.forEach((fn) => fn());\n    }));\n}\n\nexport function init($root) {\n    const priors = ($root && [\n        $root.classList.add(\"component_page_viewerpage\"),\n        loadCSS(import.meta.url, \"./component_menubar.css\"),\n        loadCSS(import.meta.url, \"../ctrl_viewerpage.css\"),\n    ]);\n\n    return Promise.all([\n        loadCSS(import.meta.url, \"./application_3d.css\"),\n        initDownloader(),\n        ...priors,\n    ]);\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_audio.css",
    "content": "body:not(.dark-mode) .component_audioplayer .audioplayer_container {\n    background: var(--surface);\n}\n\n.component_audioplayer {\n    display: flex;\n    flex-direction: column;\n    flex: 1;\n    width: 100%;\n}\n.component_audioplayer .audioplayer_container {\n    display: flex;\n    flex-direction: column;\n    flex: 1;\n    width: 100%;\n    text-align: center;\n    height: 100%;\n    overflow: hidden;\n    padding: 20px;\n    box-sizing: border-box;\n}\n.component_audioplayer .audioplayer_container .audioplayer_error {\n    color: white;\n    margin-top: 30px;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box {\n    background: white;\n    box-shadow: rgba(0, 0, 0, 0.14) 0px 4px 5px 0px;\n    position: relative;\n    border-radius: 3px;\n    padding: 10px 0 15px 0;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control {\n    padding-top: 20px;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control.hidden {\n    position: unset !important;\n    width: inherit !important;\n    height: inherit !important;\n    opacity: 0;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control img {\n    height: 25px;\n    width: 25px;\n    cursor: pointer;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control .buttons {\n    float: left;\n    padding-left: 15px;\n    margin-top: -10px;\n    display: flex;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control .buttons > span {\n    margin-right: 10px;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control .buttons input[type=\"range\"] {\n    margin-left: -5px;\n    width: 60px;\n    cursor: pointer;\n    outline: none;\n    -webkit-appearance: none;\n    background: transparent;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control .buttons input[type=\"range\"]::-webkit-slider-thumb {\n    -webkit-appearance: none;\n    height: 12px;\n    width: 12px;\n    border: none;\n    border-radius: 50%;\n    background: #6f6f6f;\n    margin-top: -5px;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control .buttons input[type=\"range\"]::-moz-range-thumb {\n    -webkit-appearance: none;\n    height: 12px;\n    width: 12px;\n    border: none;\n    border-radius: 50%;\n    background: #6f6f6f;\n    margin-top: -5px;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control .buttons input[type=\"range\"]::-webkit-slider-runnable-track {\n    width: 100%;\n    height: 2px;\n    background-color: #6f6f6f;\n    border-radius: 2px;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control .buttons input[type=\"range\"]::-moz-range-track {\n    width: 100%;\n    height: 2px;\n    background-color: #6f6f6f;\n    border-radius: 2px;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control .timecode {\n    float: right;\n    padding-right: 25px;\n    margin-top: -5px;\n    line-height: 1rem;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control .timecode #separator {\n    padding: 0 5px;\n}\n@media screen and (max-width: 450px) {\n    .component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_control .timecode #separator, .component_audioplayer > .audioplayer_container .audioplayer_box .audioplayer_control .timecode #currentTime {\n        display: none;\n    }\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .audioplayer_loader {\n    height: 260px;\n    background: var(--color);\n    position: absolute;\n    opacity: 0.1;\n    top: 0;\n    left: 0;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .component_icon[alt=\"loading\"] {\n    position: absolute;\n    margin: 50px -60px;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .chromecast_loader .component_icon[alt=\"loading\"] {\n    margin: 0;\n    right: 20px;\n    top: 20px;\n    width: 30px;\n}\n.component_audioplayer .audioplayer_container .audioplayer_box .percent {\n    position: absolute;\n    margin: 100px -60px;\n    width: 120px;\n    font-size: 1.4rem;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_audio.d.ts",
    "content": "interface Window {\n    WaveSurfer: {\n        create: (options: any) => any;\n    };\n}\n\nexport default function(any): void;"
  },
  {
    "path": "public/assets/pages/viewerpage/application_audio.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, onClick } from \"../../lib/rx.js\";\nimport { qs, safe } from \"../../lib/dom.js\";\nimport { onDestroy } from \"../../lib/skeleton/lifecycle.js\";\nimport { loadCSS, loadJS } from \"../../helpers/loader.js\";\nimport { settings_get, settings_put } from \"../../lib/settings.js\";\nimport Chromecast from \"../../lib/chromecast.js\";\nimport assert from \"../../lib/assert.js\";\n\nimport ctrlError from \"../ctrl_error.js\";\nimport { renderMenubar, buttonDownload } from \"./component_menubar.js\";\n\nimport { ICON } from \"./common_icon.js\";\nimport { formatTimecode } from \"./common_player.js\";\nimport { transition } from \"./common.js\";\n\nconst STATUS_PLAYING = \"PLAYING\";\nconst STATUS_PAUSED = \"PAUSED\";\nconst STATUS_BUFFERING = \"BUFFERING\";\n\nexport default function(render, { getFilename, getDownloadUrl }) {\n    const $page = createElement(`\n        <div class=\"component_audioplayer\">\n            <component-menubar filename=\"${safe(getFilename())}\"></component-menubar>\n            <div class=\"audioplayer_container\">\n                <div class=\"audioplayer_box\">\n                    <div data-bind=\"loader\">\n                        <div class=\"audioplayer_loader\"></div>\n                        <span class=\"percent\"></span>\n                        <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0nMTIwcHgnIGhlaWdodD0nMTIwcHgnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIiBjbGFzcz0idWlsLXJpbmctYWx0Ij4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0ibm9uZSIgY2xhc3M9ImJrIj48L3JlY3Q+CiAgPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iNDAiIHN0cm9rZT0ibm9uZSIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48L2NpcmNsZT4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjNmY2ZjZmIiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCI+CiAgICA8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJzdHJva2UtZGFzaG9mZnNldCIgZHVyPSIycyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGZyb209IjAiIHRvPSI1MDIiPjwvYW5pbWF0ZT4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9InN0cm9rZS1kYXNoYXJyYXkiIGR1cj0iMnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB2YWx1ZXM9IjE1MC42IDEwMC40OzEgMjUwOzE1MC42IDEwMC40Ij48L2FuaW1hdGU+CiAgPC9jaXJjbGU+Cjwvc3ZnPgo=\" alt=\"loading\">\n                    </div>\n                    <div id=\"waveform\"></div>\n                    <div class=\"audioplayer_control hidden\">\n                        <div class=\"buttons no-select\">\n                            <span>\n                                <img class=\"component_icon\" draggable=\"false\" src=\"${ICON.PLAY}\" alt=\"play\">\n                                <img class=\"component_icon hidden\" draggable=\"false\" src=\"${ICON.PAUSE}\" alt=\"pause\">\n                                <component-icon name=\"loading\" class=\"hidden\"></component-icon>\n                            </span>\n                            <span>\n                                <img class=\"component_icon hidden\" draggable=\"false\" src=\"${ICON.VOLUME_MUTE}\" alt=\"volume_mute\">\n                                <img class=\"component_icon hidden\" draggable=\"false\" src=\"${ICON.VOLUME_LOW}\" alt=\"volume_low\">\n                                <img class=\"component_icon hidden\" draggable=\"false\" src=\"${ICON.VOLUME_NORMAL}\" alt=\"volume\">\n                            </span>\n                            <input type=\"range\" min=\"0\" max=\"100\" value=\"52\">\n                        </div>\n                        <div class=\"timecode\">\n                            <span id=\"currentTime\">00:00</span>\n                            <span id=\"separator\" class=\"no-select\">/</span>\n                            <span id=\"totalDuration\">02:13</span>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    `);\n    render($page);\n    renderMenubar(qs($page, \"component-menubar\"), buttonDownload(getFilename(), getDownloadUrl()));\n\n    transition(qs($page, \".audioplayer_box\"));\n\n    const $control = {\n        main: qs($page, `.audioplayer_control`),\n\n        play: qs($page, `.audioplayer_control [alt=\"play\"]`),\n        pause: qs($page, `.audioplayer_control [alt=\"pause\"]`),\n        loading: qs($page, `.audioplayer_control component-icon[name=\"loading\"]`),\n    };\n    const $volume = {\n        range: qs($page, `input[type=\"range\"]`),\n        icon_mute: qs($page, `img[alt=\"volume_mute\"]`),\n        icon_low: qs($page, `img[alt=\"volume_low\"]`),\n        icon_normal: qs($page, `img[alt=\"volume\"]`),\n    };\n    const currentTime$ = new rxjs.BehaviorSubject([\n        0, // starting time - does change when seeking to another point\n        0, // offset to align the audio context currentTime. otherwise when seeking, the\n        // currentTime keep growing and progress bar goes haywire\n    ]);\n    const currentTime = (wavesurfer) => {\n        return currentTime$.value[0] + (wavesurfer.backend.ac.currentTime - currentTime$.value[1]);\n    };\n    const setVolume = (volume, wavesurfer) => {\n        settings_put(\"volume\", volume);\n        wavesurfer.setVolume(volume / 100);\n        $volume.range.value = volume;\n        if (volume === 0) {\n            $volume.icon_mute.classList.remove(\"hidden\");\n            $volume.icon_low.classList.add(\"hidden\");\n            $volume.icon_normal.classList.add(\"hidden\");\n        } else if (volume < 50) {\n            $volume.icon_mute.classList.add(\"hidden\");\n            $volume.icon_low.classList.remove(\"hidden\");\n            $volume.icon_normal.classList.add(\"hidden\");\n        } else {\n            $volume.icon_mute.classList.add(\"hidden\");\n            $volume.icon_low.classList.add(\"hidden\");\n            $volume.icon_normal.classList.remove(\"hidden\");\n        }\n    };\n    const setStatus = (status, wavesurfer) => {\n        switch (status) {\n        case STATUS_PLAYING:\n            $control.play.classList.add(\"hidden\");\n            $control.pause.classList.remove(\"hidden\");\n            $control.loading.classList.add(\"hidden\");\n            wavesurfer.backend.ac.resume();\n            break;\n        case STATUS_PAUSED:\n            $control.play.classList.remove(\"hidden\");\n            $control.pause.classList.add(\"hidden\");\n            $control.loading.classList.add(\"hidden\");\n            wavesurfer.backend.ac.suspend();\n            break;\n        case STATUS_BUFFERING:\n            $control.play.classList.add(\"hidden\");\n            $control.pause.classList.add(\"hidden\");\n            $control.loading.classList.remove(\"hidden\");\n            break;\n        default:\n            assert.fail(status);\n        }\n    };\n    const setSeek = (newTime, wavesurfer) => {\n        currentTime$.next([newTime, wavesurfer.backend.ac.currentTime]);\n        wavesurfer.backend.source.stop(0);\n        wavesurfer.backend.disconnectSource();\n        wavesurfer.backend.createSource();\n        wavesurfer.backend.source.start(0, newTime);\n    };\n\n    // feature1: setup the dom\n    const setup$ = rxjs.of(qs($page, \"#waveform\")).pipe(\n        rxjs.mergeMap(($node) => Promise.resolve(window.WaveSurfer.create({\n            container: $node,\n            interact: false,\n\n            waveColor: \"#323639\",\n            progressColor: \"#808080\",\n            cursorColor: \"#6f6f6f\",\n            cursorWidth: 3,\n            height: 200,\n            barWidth: 1,\n        }))),\n        rxjs.tap((wavesurfer) => {\n            wavesurfer.load(getDownloadUrl());\n            wavesurfer.on(\"error\", (err) => {\n                throw new Error(err);\n            });\n            wavesurfer.on(\"ready\", () => {\n                $control.main.classList.remove(\"hidden\");\n                qs($control.main, \"#totalDuration\").textContent = formatTimecode(wavesurfer.getDuration());\n            });\n            onDestroy(() => wavesurfer.destroy());\n        }),\n        rxjs.catchError(ctrlError()),\n        rxjs.shareReplay(1),\n    );\n    effect(setup$);\n\n    // feature2: loading animation\n    effect(setup$.pipe(\n        rxjs.mergeMap((wavesurfer) => new rxjs.Observable((observer) => {\n            wavesurfer.on(\"loading\", (n) => observer.next(n));\n            wavesurfer.on(\"ready\", () => observer.next(100));\n        })),\n        rxjs.mergeMap((n) => {\n            const $loader = qs($page, `[data-bind=\"loader\"]`);\n            $loader.querySelector(\".audioplayer_loader\").style.width = `${n}%`;\n            $loader.querySelector(\".percent\").textContent = `${n}%`;\n            if (n !== 100) return rxjs.EMPTY;\n            return rxjs.of(null).pipe(\n                rxjs.delay(200),\n                rxjs.tap(() => $loader.classList.add(\"hidden\")),\n            );\n        }),\n    ));\n\n    // feature3: connect the audio\n    const ready$ = setup$.pipe(\n        rxjs.mergeMap((wavesurfer) => new rxjs.Observable((observer) => {\n            wavesurfer.on(\"ready\", () => observer.next(wavesurfer));\n        })),\n        rxjs.share(),\n    );\n    effect(ready$.pipe(rxjs.tap((wavesurfer) => {\n        wavesurfer.backend.createSource();\n        wavesurfer.backend.source.start(0, 0);\n        wavesurfer.backend.ac.suspend();\n        currentTime$.next([0, wavesurfer.backend.ac.currentTime]);\n    })));\n\n    // feature4: hint of song progress\n    effect(setup$.pipe(\n        rxjs.mergeMap(() => rxjs.merge(\n            onClick($control.play).pipe(rxjs.mapTo(STATUS_PLAYING)),\n            rxjs.fromEvent(document, \"keydown\").pipe(rxjs.mapTo(STATUS_PLAYING)),\n        )),\n        rxjs.first(),\n        rxjs.mergeMap((status) => setup$.pipe(rxjs.tap((wavesurfer) => setStatus(status, wavesurfer)))),\n        rxjs.switchMap((wavesurfer) => rxjs.animationFrames().pipe(rxjs.tap(() => {\n            const _currentTime = currentTime(wavesurfer);\n            const percent = _currentTime / wavesurfer.getDuration();\n            if (percent > 1) return;\n            wavesurfer.drawer.progress(percent);\n            qs($control.main, \"#currentTime\").textContent = formatTimecode(_currentTime);\n        }))),\n    ));\n\n    // feature5: player control - play / pause\n    effect(setup$.pipe(\n        rxjs.mergeMap((wavesurfer) => rxjs.merge(\n            onClick($control.play).pipe(rxjs.mapTo(STATUS_PLAYING)),\n            onClick($control.pause).pipe(rxjs.mapTo(STATUS_PAUSED)),\n        ).pipe(\n            rxjs.tap((status) => setStatus(status, wavesurfer)),\n        )),\n    ));\n\n    // feature6: player control - volume\n    effect(setup$.pipe(\n        rxjs.switchMap(() => rxjs.fromEvent($volume.range, \"input\").pipe(rxjs.map((e) => e.target.value))),\n        rxjs.startWith(settings_get(\"volume\") === null ? 80 : settings_get(\"volume\")),\n        rxjs.mergeMap((volume) => setup$.pipe(rxjs.tap((wavesurfer) => setVolume(parseInt(volume), wavesurfer)))),\n    ));\n\n    // feature7: player control - seek\n    effect(ready$.pipe(\n        rxjs.mergeMap((wavesurfer) => rxjs.fromEvent(qs($page, \"#waveform\"), \"click\").pipe(\n            rxjs.map((e) => ({ e, wavesurfer })),\n        )),\n        rxjs.map(({ e, wavesurfer }) => {\n            const rec = e.target.closest(\"#waveform\").getBoundingClientRect();\n            return { wavesurfer, progress: (e.clientX - rec.x) / rec.width };\n        }),\n        rxjs.tap(({ progress, wavesurfer }) => {\n            wavesurfer.drawer.progress(progress);\n            const newTime = wavesurfer.getDuration() * progress;\n            setSeek(newTime, wavesurfer);\n        }),\n    ));\n\n    // feature8: player control - keyboard shortcut\n    effect(ready$.pipe(\n        rxjs.switchMap((wavesurfer) => rxjs.fromEvent(document, \"keydown\").pipe(\n            rxjs.map((e) => e.code),\n            rxjs.tap((code) => {\n                switch (code) {\n                case \"Space\":\n                case \"KeyK\":\n                    setStatus(\n                        wavesurfer.backend.ac.state === \"suspended\"\n                            ? STATUS_PLAYING\n                            : STATUS_PAUSED,\n                        wavesurfer,\n                    );\n                    break;\n                case \"KeyM\":\n                    setVolume(wavesurfer.getVolume() > 0 ? 0 : settings_get(\"volume\"), wavesurfer);\n                    break;\n                case \"ArrowUp\":\n                    setVolume(Math.min(wavesurfer.getVolume()*100 + 10, 100), wavesurfer);\n                    break;\n                case \"ArrowDown\":\n                    setVolume(Math.max(wavesurfer.getVolume()*100 - 10, 0), wavesurfer);\n                    break;\n                case \"KeyL\":\n                    setSeek(Math.min(wavesurfer.getDuration(), currentTime(wavesurfer) + 10), wavesurfer);\n                    break;\n                case \"KeyF\":\n                    // chromecastLoader();\n                    break;\n                case \"KeyJ\":\n                    setSeek(Math.max(0, currentTime(wavesurfer) - 10), wavesurfer);\n                    break;\n                case \"Digit0\":\n                    setSeek(0, wavesurfer);\n                    break;\n                case \"Digit1\":\n                    setSeek(wavesurfer.getDuration() / 10, wavesurfer);\n                    break;\n                case \"Digit2\":\n                    setSeek(2 * wavesurfer.getDuration() / 10, wavesurfer);\n                    break;\n                case \"Digit3\":\n                    setSeek(3 * wavesurfer.getDuration() / 10, wavesurfer);\n                    break;\n                case \"Digit4\":\n                    setSeek(4 * wavesurfer.getDuration() / 10, wavesurfer);\n                    break;\n                case \"Digit5\":\n                    setSeek(5 * wavesurfer.getDuration() / 10, wavesurfer);\n                    break;\n                case \"Digit6\":\n                    setSeek(6 * wavesurfer.getDuration() / 10, wavesurfer);\n                    break;\n                case \"Digit7\":\n                    setSeek(7 * wavesurfer.getDuration() / 10, wavesurfer);\n                    break;\n                case \"Digit8\":\n                    setSeek(8 * wavesurfer.getDuration() / 10, wavesurfer);\n                    break;\n                case \"Digit9\":\n                    setSeek(9 * wavesurfer.getDuration() / 10, wavesurfer);\n                    break;\n                }\n            }),\n        )),\n    ));\n\n    // // feature9: setup chromecast\n    // effect(ready$.pipe(\n    //     rxjs.tap(() => renderMenubar(buildMenubar(\n    //         menubarChromecast(),\n    //         menubarDownload(),\n    //     ))),\n    // ));\n    // effect(rxjs.combineLatest(\n    //     setup$,\n    //     getSession(),\n    //     getConfig(),\n    // ).pipe(\n    //     rxjs.mergeMap(async ([wavesurfer, user, config]) => {\n    //         if (!Chromecast.isAvailable()) return;\n    //         const filename = basename(decodeURIComponent(location.pathname));\n    //         // const link = Chromecast.createLink(getDownloadUrl());\n    //         const media = new chrome.cast.media.MediaInfo(\n    //             getDownloadUrl(),\n    //             mime,\n    //         );\n    //         media.metadata = new chrome.cast.media.MusicTrackMediaMetadata()\n    //         media.metadata.title = \"test\";\n    //         media.metadata.title = filename.substr(0, filename.lastIndexOf(extname(filename)));\n    //         media.metadata.subtitle = config.name;\n    //         media.metadata.albumName = config.name;\n    //         media.metadata.images = [\n    //             new chrome.cast.Image(origin + \"/assets/icons/music.png\"),\n    //         ];\n    //         wavesurfer.setMute(true);\n    //         wavesurfer.pause();\n\n    //         const session = Chromecast.session();\n    //         if (!session) return\n    //         setVolume(session.getVolume() * 100);\n\n    //         const req = await Chromecast.createRequest(media, user.authorization);\n    //         return session.loadMedia(req);\n    //         // .catch((err) => {\n    //         //     console.error(err);\n    //         //     notify.send(t(\"Cannot establish a connection\"), \"error\");\n    //         //     setIsChromecast(false);\n    //         //     setIsLoading(false);\n    //         // });\n    //     }),\n    // ));\n}\n\nexport function init() {\n    return Promise.all([\n        setup_chromecast(),\n        loadJS(import.meta.url, \"../../lib/vendor/wavesurfer.js\"),\n        loadCSS(import.meta.url, \"./application_audio.css\"),\n    ]);\n}\n\nfunction setup_chromecast() {\n    if (!(\"chrome\" in window)) {\n        return Promise.resolve();\n    } else if (location.hostname === \"localhost\" || location.hostname === \"127.0.0.1\") {\n        return Promise.resolve();\n    }\n    // if (!CONFIG.enable_chromecast) {\n    //     return Promise.resolve();\n    // } else\n    return Chromecast.init();\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_downloader.css",
    "content": "body:not(.dark-mode) .component_filedownloader {\n    background: var(--surface);\n}\nbody.dark-mode .component_filedownloader {\n    background: #232426;\n}\n\n.component_filedownloader {\n    display: block!important;\n    text-align: center;\n}\n\n.component_filedownloader .download_button {\n    background: rgba(0, 0, 0, 0.3);\n    border-radius: 3px;\n    display: inline-block;\n    color: rgba(255,255,255,0.6);\n    margin-top: 50px;\n    text-transform: uppercase;\n    font-weight: bold;\n    box-shadow: rgba(0,0,0,.14) 0px 4px 5px 0px, rgba(0,0,0,.12) 0px 1px 10px 0px, rgba(0,0,0,.2) 0px 2px 4px -1px;\n}\n.component_filedownloader .download_button > * {\n    display: inline-block;\n    padding: 15px 20px;\n}\n.component_filedownloader component-icon img {\n    height: 20px;\n    float: right;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_downloader.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport { qs, safe } from \"../../lib/dom.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\nimport t from \"../../locales/index.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport { transition } from \"./common.js\";\nimport { renderMenubar } from \"./component_menubar.js\";\nimport \"../../components/icon.js\";\n\nexport default async function(render, { acl$, getFilename, getDownloadUrl, hasMenubar = true }) {\n    const $page = createElement(`\n        <div class=\"component_filedownloader\">\n            <component-menubar filename=\"${safe(getFilename())}\" class=\"${!hasMenubar && \"hidden\"}\"></component-menubar>\n            <div class=\"download_button no-select\">\n                <a download=\"${safe(getFilename())}\" href=\"${safe(getDownloadUrl())}\">${t(\"DOWNLOAD\")}</a>\n                <component-icon name=\"loading\" class=\"hidden\"></component-icon>\n            </div>\n        </div>\n    `);\n    render(transition($page));\n    renderMenubar(qs($page, \"component-menubar\"));\n\n    const $link = qs($page, \"a\");\n    const $loading = qs($page, \"component-icon\");\n    const setLoading = (isLoading) => {\n        isLoading ? $loading.classList.remove(\"hidden\") : $loading.classList.add(\"hidden\");\n        isLoading ? $link.classList.add(\"hidden\") : $link.classList.remove(\"hidden\");\n    };\n    effect(rxjs.fromEvent($link, \"click\").pipe(\n        rxjs.tap(() => {\n            setLoading(true);\n            document.cookie = \"download=yes; path=/; max-age=10;\";\n        }),\n        rxjs.mergeMap(() => new Promise((done) => {\n            const id = setInterval(() => {\n                if (/download=yes/.test(document.cookie)) return;\n                clearInterval(id);\n                done(null);\n            }, 200);\n        })),\n        rxjs.tap(() => setLoading(false)),\n    ));\n    effect(acl$.pipe(\n        rxjs.catchError(ctrlError()),\n    ));\n}\n\nexport function init() {\n    return loadCSS(import.meta.url, \"./application_downloader.css\");\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_ebook.css",
    "content": ".component_ebookviewer .ebookviewer_container {\n    display: flex;\n    flex-grow: 1;\n    min-height: 0;\n}\n.component_ebookviewer .ebookviewer_container .epub-container {\n    padding-top: 20px;\n    padding-bottom: 50px;\n    box-sizing: border-box;\n    overflow-x: hidden !important;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_ebook.d.ts",
    "content": "interface Window {\n    ePub: {\n        Book: new (options: any) => any;\n    };\n}\n\nexport default function(any): void;"
  },
  {
    "path": "public/assets/pages/viewerpage/application_ebook.js",
    "content": "import { createElement, onDestroy } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport { qs, safe } from \"../../lib/dom.js\";\nimport assert from \"../../lib/assert.js\";\nimport { loadJS, loadCSS } from \"../../helpers/loader.js\";\nimport { createLoader } from \"../../components/loader.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport { renderMenubar, buttonDownload } from \"./component_menubar.js\";\n\nexport default function(render, { getFilename, getDownloadUrl }) {\n    const $page = createElement(`\n        <div class=\"component_ebookviewer\">\n            <component-menubar filename=\"${safe(getFilename())}\"></component-menubar>\n            <div class=\"ebookviewer_container\" data-bind=\"epub\"></div>\n        </div>\n    `);\n    render($page);\n    renderMenubar(qs($page, \"component-menubar\"), buttonDownload(getFilename(), getDownloadUrl()));\n\n    const rendition$ = new rxjs.ReplaySubject(1);\n\n    // feature1: setup the dom\n    const $epub = qs($page, `[data-bind=\"epub\"]`);\n    const removeLoader = createLoader($page, {\n        append: ($loader) => $page.insertBefore($loader, qs($page, \".ebookviewer_container\")),\n    });\n    const setup$ = rxjs.of($epub).pipe(\n        rxjs.mergeMap(async($epub) => {\n            const book = new window.ePub.Book({\n                replacements: \"blobUrl\",\n            });\n            const rendition = book.renderTo($epub, {\n                height: \"100%\",\n                width: \"100%\",\n                flow: \"scrolled-doc\",\n                method: \"continuous\",\n                allowScriptedContent: false,\n            });\n            rendition.display();\n            onDestroy(() => {\n                book.destroy();\n                rendition.destroy();\n            });\n            book.open(getDownloadUrl());\n            await new Promise((done) => rendition.hooks.render.register(() => {\n                rendition$.next(rendition);\n                done(null);\n            }));\n        }),\n        removeLoader,\n        rxjs.catchError(ctrlError()),\n        rxjs.share(),\n    );\n    effect(setup$);\n\n    // feature2: navigation\n    effect(setup$.pipe(\n        rxjs.mergeMap(() => rxjs.merge(\n            rxjs.fromEvent(document, \"keydown\"),\n            rendition$.pipe(rxjs.mergeMap(() => rxjs.fromEvent(assert.type(qs(document.body, \"iframe\"), HTMLElement).contentDocument.body, \"keydown\"))),\n        )),\n        rxjs.map((e) => {\n            switch (e.code) {\n            case \"Space\": return (r) => r.next();\n            case \"ArrowRight\": return (r) => r.next();\n            case \"PageDown\": return (r) => r.next();\n            case \"ArrowLeft\": return (r) => r.prev();\n            case \"PageUp\": return (r) => r.prev();\n            }\n            return null;\n        }),\n        rxjs.mergeMap((fn) => fn === null\n            ? rxjs.EMPTY\n            : rendition$.asObservable().pipe(\n                rxjs.first(),\n                rxjs.tap((rendition) => fn(rendition))\n            )),\n        rxjs.catchError(ctrlError()),\n    ));\n}\n\nexport function init() {\n    return Promise.all([\n        loadJS(import.meta.url, \"../../lib/vendor/epub/zip.min.js\"),\n        loadJS(import.meta.url, \"../../lib/vendor/epub/epub.min.js\"),\n        loadCSS(import.meta.url, \"./application_ebook.css\"),\n    ]);\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/clike.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/clike/clike.js\";\nwindow.CodeMirror.__mode = \"text/x-c++src\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/clojure.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/clojure/clojure.js\";\nwindow.CodeMirror.__mode = \"clojure\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/cmake.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/cmake/cmake.js\";\nwindow.CodeMirror.__mode = \"cmake\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/commonlisp.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/commonlisp/commonlisp.js\";\nwindow.CodeMirror.__mode = \"commonlisp\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/css.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/css/css.js\";\nwindow.CodeMirror.__mode = \"css\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/diff.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/diff/diff.js\";\nwindow.CodeMirror.__mode = \"diff\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/dockerfile.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/dockerfile/dockerfile.js\";\nwindow.CodeMirror.__mode = \"dockerfile\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/elm.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/elm/elm.js\";\nwindow.CodeMirror.__mode = \"elm\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/emacs-org.js",
    "content": "export const org_cycle = (cm) => {\n    const pos = cm.getCursor();\n    isFold(cm, pos) ? unfold(cm, pos) : fold(cm, pos);\n};\n\nconst state = {\n    stab: \"CONTENT\",\n};\nexport const org_set_fold = (cm) => {\n    const cursor = cm.getCursor();\n    set_folding_mode(cm, state.stab);\n    cm.setCursor(cursor);\n    return state.stab;\n};\n/*\n * DONE: Global visibility cycling\n * TODO: or move to previous table field.\n */\nexport const org_shifttab = (cm) => {\n    if (state.stab === \"SHOW_ALL\") {\n        state.stab = \"OVERVIEW\";\n    } else if (state.stab === \"OVERVIEW\") {\n        state.stab = \"CONTENT\";\n    } else if (state.stab === \"CONTENT\") {\n        state.stab = \"SHOW_ALL\";\n    }\n    set_folding_mode(cm, state.stab);\n    return state.stab;\n};\n\nfunction set_folding_mode(cm, mode) {\n    if (mode === \"OVERVIEW\") {\n        folding_mode_overview(cm);\n    } else if (mode === \"SHOW_ALL\") {\n        folding_mode_all(cm);\n    } else if (mode === \"CONTENT\") {\n        folding_mode_content(cm);\n    }\n    cm.refresh();\n\n    function folding_mode_overview(cm) {\n        cm.operation(function() {\n            for (let i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) {\n                fold(cm, window.CodeMirror.Pos(i, 0));\n            }\n        });\n    }\n    function folding_mode_content(cm) {\n        cm.operation(function() {\n            let previous_header = null;\n            for (let i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) {\n                fold(cm, window.CodeMirror.Pos(i, 0));\n                if (/header/.test(cm.getTokenTypeAt(window.CodeMirror.Pos(i, 0))) === true) {\n                    const level = cm.getLine(i).replace(/^(\\*+).*/, \"$1\").length;\n                    if (previous_header && level > previous_header.level) {\n                        unfold(cm, window.CodeMirror.Pos(previous_header.line, 0));\n                    }\n                    previous_header = {\n                        line: i,\n                        level,\n                    };\n                }\n            }\n        });\n    }\n    function folding_mode_all(cm) {\n        cm.operation(function() {\n            for (let i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) {\n                if (/header/.test(cm.getTokenTypeAt(window.CodeMirror.Pos(i, 0))) === true) {\n                    unfold(cm, window.CodeMirror.Pos(i, 0));\n                }\n            }\n        });\n    }\n}\n\n/*\n * Promote heading or move table column to left.\n */\nexport const org_metaleft = (cm) => {\n    const line = cm.getCursor().line;\n    _metaleft(cm, line);\n};\nfunction _metaleft(cm, line) {\n    let p = null;\n    if (p = isTitle(cm, line)) {\n        if (p[\"level\"] > 1) {\n            cm.replaceRange(\n                \"\",\n                { line: p.start, ch: 0 },\n                { line: p.start, ch: 1 },\n            );\n        }\n    } else if (p = isItemList(cm, line)) {\n        for (let i=p.start; i<=p.end; i++) {\n            if (p[\"level\"] > 0) {\n                cm.replaceRange(\n                    \"\",\n                    { line: i, ch: 0 },\n                    { line: i, ch: 2 },\n                );\n            }\n        }\n    } else if (p = isNumberedList(cm, line)) {\n        for (let i=p.start; i<=p.end; i++) {\n            if (p[\"level\"] > 0) {\n                cm.replaceRange(\n                    \"\",\n                    { line: i, ch: 0 },\n                    { line: i, ch: 3 },\n                );\n            }\n        }\n        rearrange_list(cm, line);\n    }\n}\n\n/*\n * Demote a subtree, a list item or move table column to right.\n * In front of a drawer or a block keyword, indent it correctly.\n */\nexport const org_metaright = (cm) => {\n    const line = cm.getCursor().line;\n    _metaright(cm, line);\n};\n\nfunction _metaright(cm, line) {\n    let p = null;\n    let tmp = null;\n    if (p = isTitle(cm, line)) {\n        cm.replaceRange(\"*\", { line: p.start, ch: 0 });\n    } else if (p = isItemList(cm, line)) {\n        if (tmp = isItemList(cm, p.start - 1)) {\n            if (p.level < tmp.level + 1) {\n                for (let i=p.start; i<=p.end; i++) {\n                    cm.replaceRange(\"  \", { line: i, ch: 0 });\n                }\n            }\n        }\n    } else if (p = isNumberedList(cm, line)) {\n        if (tmp = isNumberedList(cm, p.start - 1)) {\n            if (p.level < tmp.level + 1) {\n                for (let i=p.start; i<=p.end; i++) {\n                    cm.replaceRange(\"   \", { line: i, ch: 0 });\n                }\n                rearrange_list(cm, p.start);\n            }\n        }\n    }\n}\n\n/*\n * Insert a new heading or wrap a region in a table\n */\nexport const org_meta_return = (cm) => {\n    const line = cm.getCursor().line;\n    const content = cm.getLine(line);\n    let p = null;\n\n    if (p = isItemList(cm, line)) {\n        const level = p.level;\n        cm.replaceRange(\n            \"\\n\"+\" \".repeat(level*2)+\"- \",\n            { line: p.end, ch: cm.getLine(p.end).length },\n        );\n        cm.setCursor({ line: p.end+1, ch: level*2+2 });\n    } else if (p = isNumberedList(cm, line)) {\n        const level = p.level;\n        cm.replaceRange(\n            \"\\n\"+\" \".repeat(level*3)+(p.n+1)+\". \",\n            { line: p.end, ch: cm.getLine(p.end).length },\n        );\n        cm.setCursor({ line: p.end+1, ch: level*3+3 });\n        rearrange_list(cm, line);\n    } else if (p = isTitle(cm, line)) {\n        const tmp = previousOfType(cm, \"title\", line);\n        const level = (tmp && tmp.level) || 1;\n        cm.replaceRange(\"\\n\"+\"*\".repeat(level)+\" \", { line, ch: content.length });\n        cm.setCursor({ line: line+1, ch: level+1 });\n    } else if (content.trim() === \"\") {\n        cm.replaceRange(\"* \", { line, ch: 0 });\n        cm.setCursor({ line, ch: 2 });\n    } else {\n        cm.replaceRange(\"\\n\\n* \", { line, ch: content.length });\n        cm.setCursor({ line: line + 2, ch: 2 });\n    }\n};\n\nconst TODO_CYCLES = [\"TODO\", \"DONE\", \"\"];\n/*\n * Cycle the thing at point or in the current line, depending on context.\n * Depending on context, this does one of the following:\n * - TODO: switch a timestamp at point one day into the past\n * - DONE: on a headline, switch to the previous TODO keyword.\n * - TODO: on an item, switch entire list to the previous bullet type\n * - TODO: on a property line, switch to the previous allowed value\n * - TODO: on a clocktable definition line, move time block into the past\n */\nexport const org_shiftleft = (cm) => {\n    const cycles = [].concat(TODO_CYCLES.slice(0).reverse(), TODO_CYCLES.slice(-1));\n    const line = cm.getCursor().line;\n    const content = cm.getLine(line);\n    const params = isTitle(cm, line);\n\n    if (params === null) return;\n    params[\"status\"] = cycles[cycles.indexOf(params[\"status\"]) + 1];\n    cm.replaceRange(\n        makeTitle(params),\n        { line, ch: 0 },\n        { line, ch: content.length },\n    );\n};\n/*\n * Cycle the thing at point or in the current line, depending on context.\n * Depending on context, this does one of the following:\n * - TODO: switch a timestamp at point one day into the future\n * - DONE: on a headline, switch to the next TODO keyword.\n * - TODO: on an item, switch entire list to the next bullet type\n * - TODO: on a property line, switch to the next allowed value\n * - TODO: on a clocktable definition line, move time block into the future\n */\nexport const org_shiftright = (cm) => {\n    cm.operation(() => {\n        const cycles = [].concat(TODO_CYCLES, [TODO_CYCLES[0]]);\n        const line = cm.getCursor().line;\n        const content = cm.getLine(line);\n        const params = isTitle(cm, line);\n\n        if (params === null) return;\n        params[\"status\"] = cycles[cycles.indexOf(params[\"status\"]) + 1];\n        cm.replaceRange(\n            makeTitle(params),\n            { line, ch: 0 },\n            { line, ch: content.length },\n        );\n    });\n};\n\nexport const org_insert_todo_heading = (cm) => {\n    cm.operation(() => {\n        const line = cm.getCursor().line;\n        const content = cm.getLine(line);\n\n        let p = null;\n        if (p = isItemList(cm, line)) {\n            const level = p.level;\n            cm.replaceRange(\n                \"\\n\"+\" \".repeat(level*2)+\"- [ ] \",\n                { line: p.end, ch: cm.getLine(p.end).length },\n            );\n            cm.setCursor({ line: line+1, ch: 6+level*2 });\n        } else if (p = isNumberedList(cm, line)) {\n            const level = p.level;\n            cm.replaceRange(\n                \"\\n\"+\" \".repeat(level*3)+(p.n+1)+\". [ ] \",\n                { line: p.end, ch: cm.getLine(p.end).length },\n            );\n            cm.setCursor({ line: p.end+1, ch: level*3+7 });\n            rearrange_list(cm, line);\n        } else if (p = isTitle(cm, line)) {\n            const level = (p && p.level) || 1;\n            cm.replaceRange(\"\\n\"+\"*\".repeat(level)+\" TODO \", { line, ch: content.length });\n            cm.setCursor({ line: line+1, ch: level+6 });\n        } else if (content.trim() === \"\") {\n            cm.replaceRange(\"* TODO \", { line, ch: 0 });\n            cm.setCursor({ line, ch: 7 });\n        } else {\n            cm.replaceRange(\"\\n\\n* TODO \", { line, ch: content.length });\n            cm.setCursor({ line: line + 2, ch: 7 });\n        }\n    });\n};\n\n/*\n * Move subtree up or move table row up.\n * Calls ‘org-move-subtree-up’ or ‘org-table-move-row’ or\n * ‘org-move-item-up’, depending on context\n */\nexport const org_metaup = (cm) => {\n    cm.operation(() => {\n        const line = cm.getCursor().line;\n        let p = null;\n\n        if (p = isItemList(cm, line)) {\n            const a = isItemList(cm, p.start - 1);\n            if (a) {\n                swap(cm, [p.start, p.end], [a.start, a.end]);\n                rearrange_list(cm, line);\n            }\n        } else if (p = isNumberedList(cm, line)) {\n            const a = isNumberedList(cm, p.start - 1);\n            if (a) {\n                swap(cm, [p.start, p.end], [a.start, a.end]);\n                rearrange_list(cm, line);\n            }\n        } else if (p = isTitle(cm, line)) {\n            let _line = line;\n            let a;\n            do {\n                _line -= 1;\n                if (a = isTitle(cm, _line, p.level)) {\n                    break;\n                }\n            } while (_line > 0);\n\n            if (a) {\n                swap(cm, [p.start, p.end], [a.start, a.end]);\n                org_set_fold(cm);\n            }\n        }\n    });\n};\n\n/*\n * Move subtree down or move table row down.\n * Calls ‘org-move-subtree-down’ or ‘org-table-move-row’ or\n * ‘org-move-item-down’, depending on context\n */\nexport const org_metadown = (cm) => {\n    cm.operation(() => {\n        const line = cm.getCursor().line;\n        let p = null;\n\n        if (p = isItemList(cm, line)) {\n            const a = isItemList(cm, p.end + 1);\n            if (a) {\n                swap(cm, [p.start, p.end], [a.start, a.end]);\n            }\n        } else if (p = isNumberedList(cm, line)) {\n            const a = isNumberedList(cm, p.end + 1);\n            if (a) {\n                swap(cm, [p.start, p.end], [a.start, a.end]);\n            }\n            rearrange_list(cm, line);\n        } else if (p = isTitle(cm, line)) {\n            const a = isTitle(cm, p.end + 1, p.level);\n            if (a) {\n                swap(cm, [p.start, p.end], [a.start, a.end]);\n                org_set_fold(cm);\n            }\n        }\n    });\n};\n\nexport const org_shiftmetaright = function(cm) {\n    cm.operation(() => {\n        const line = cm.getCursor().line;\n        let p = null;\n        if (p = isTitle(cm, line)) {\n            _metaright(cm, line);\n            for (let i=p.start + 1; i<=p.end; i++) {\n                if (isTitle(cm, i)) {\n                    _metaright(cm, i);\n                }\n            }\n        }\n    });\n};\n\nexport const org_shiftmetaleft = function(cm) {\n    cm.operation(() => {\n        const line = cm.getCursor().line;\n        let p = null;\n        if (p = isTitle(cm, line)) {\n            if (p.level === 1) return;\n            _metaleft(cm, line);\n            for (let i=p.start + 1; i<=p.end; i++) {\n                if (isTitle(cm, i)) {\n                    _metaleft(cm, i);\n                }\n            }\n        }\n    });\n};\n\nfunction makeTitle(p) {\n    let content = \"*\".repeat(p[\"level\"])+\" \";\n    if (p[\"status\"]) {\n        content += p[\"status\"]+\" \";\n    }\n    content += p[\"content\"];\n    return content;\n}\n\nfunction previousOfType(cm, type, line) {\n    let tmp;\n    let i;\n    for (i=line - 1; i>0; i--) {\n        if (type === \"list\" || type === null) {\n            tmp = isItemList(cm, line);\n        } else if (type === \"numbered\" || type === null) {\n            tmp = isNumberedList(cm, line);\n        } else if (type === \"title\" || type === null) {\n            tmp = isTitle(cm, line);\n        }\n        if (tmp !== null) {\n            return tmp;\n        }\n    }\n    return null;\n}\n\nfunction isItemList(cm, line) {\n    const rootLineItem = findRootLine(cm, line);\n    if (rootLineItem === null) return null;\n    line = rootLineItem;\n    const content = cm.getLine(line);\n\n    if (content && (content.trimLeft()[0] !== \"-\" || content.trimLeft()[1] !== \" \")) return null;\n    const padding = content.replace(/^(\\s*).*$/, \"$1\").length;\n    if (padding % 2 !== 0) return null;\n    return {\n        type: \"list\",\n        level: padding / 2,\n        content: content.trimLeft().replace(/^\\s*\\-\\s(.*)$/, \"$1\"),\n        start: line,\n        end: (function(_cm, _line) {\n            let line_candidate = _line;\n            let content = null;\n            do {\n                _line += 1;\n                content = _cm.getLine(_line);\n                if (content === undefined || content.trimLeft()[0] === \"-\") {\n                    break;\n                } else if (/^\\s+/.test(content)) {\n                    line_candidate = _line;\n                    continue;\n                } else {\n                    break;\n                }\n            } while (_line <= _cm.lineCount());\n            return line_candidate;\n        }(cm, line)),\n    };\n\n    function findRootLine(_cm, _line) {\n        let content;\n        do {\n            content = _cm.getLine(_line);\n            if (/^\\s*\\-/.test(content)) return _line;\n            else if (/^\\s+/.test(content) === false) {\n                break;\n            }\n            _line -= 1;\n        } while (_line >= 0);\n        return null;\n    }\n}\nfunction isNumberedList(cm, line) {\n    const rootLineItem = findRootLine(cm, line);\n    if (rootLineItem === null) return null;\n    line = rootLineItem;\n    const content = cm.getLine(line);\n\n    if (/^[0-9]+[\\.\\)]\\s.*$/.test(content && content.trimLeft()) === false) return null;\n    const padding = content.replace(/^(\\s*)[0-9]+.*$/, \"$1\").length;\n    if (padding % 3 !== 0) return null;\n    return {\n        type: \"numbered\",\n        level: padding / 3,\n        content: content.trimLeft().replace(/^[0-9]+[\\.\\)]\\s(.*)$/, \"$1\"),\n        start: line,\n        end: (function(_cm, _line) {\n            let line_candidate = _line;\n            let content = null;\n            do {\n                _line += 1;\n                content = _cm.getLine(_line);\n                if (content === undefined || /^[0-9]+[\\.\\)]/.test(content.trimLeft())) {\n                    break;\n                } else if (/^\\s+/.test(content)) {\n                    line_candidate = _line;\n                    continue;\n                } else {\n                    break;\n                }\n            } while (_line <= _cm.lineCount());\n            return line_candidate;\n        }(cm, line)),\n        // specific\n        n: parseInt(content.trimLeft().replace(/^([0-9]+).*$/, \"$1\")),\n        separator: content.trimLeft().replace(/^[0-9]+([\\.\\)]).*$/, \"$1\"),\n    };\n\n    function findRootLine(_cm, _line) {\n        let content;\n        do {\n            content = _cm.getLine(_line);\n            if (/^\\s*[0-9]+[\\.\\)]\\s/.test(content)) return _line;\n            else if (/^\\s+/.test(content) === false) {\n                break;\n            }\n            _line -= 1;\n        } while (_line >= 0);\n        return null;\n    }\n}\nfunction isTitle(cm, line, level) {\n    const content = cm.getLine(line);\n    if (/^\\*+\\s/.test(content) === false) return null;\n    const match = content.match(/^(\\*+)([\\sA-Z]*)\\s(.*)$/);\n    const reference_level = match[1].length;\n    if (level !== undefined && level !== reference_level) return null;\n    if (match === null) return null;\n    return {\n        type: \"title\",\n        level: reference_level,\n        content: match[3],\n        start: line,\n        end: (function(_cm, _line) {\n            let line_candidate = _line;\n            let content = null;\n            do {\n                _line += 1;\n                content = _cm.getLine(_line);\n                if (content === undefined) break;\n                const match = content.match(/^(\\*+)\\s.*/);\n                if (\n                    match && match[1] &&\n                    (match[1].length === reference_level || match[1].length < reference_level)\n                ) {\n                    break;\n                } else {\n                    line_candidate = _line;\n                    continue;\n                }\n            } while (_line <= _cm.lineCount());\n            return line_candidate;\n        }(cm, line)),\n        // specific\n        status: match[2].trim(),\n    };\n}\n\nfunction rearrange_list(cm, line) {\n    const line_inferior = find_limit_inferior(cm, line);\n    const line_superior = find_limit_superior(cm, line);\n\n    let last_p = null;\n    let p;\n\n    for (let i=line_inferior; i<=line_superior; i++) {\n        if (p = isNumberedList(cm, i)) {\n            // rearrange numbers on the numbered list\n            if (last_p) {\n                if (p.level === last_p.level) {\n                    const tmp = findLastAtLevel(cm, p.start, line_inferior, p.level);\n                    if (tmp && p.n !== tmp.n + 1) setNumber(cm, p.start, tmp.n + 1);\n                } else if (p.level > last_p.level) {\n                    if (p.n !== 1) {\n                        setNumber(cm, p.start, 1);\n                    }\n                } else if (p.level < last_p.level) {\n                    const tmp = findLastAtLevel(cm, p.start, line_inferior, p.level);\n                    if (tmp && p.n !== tmp.n + 1) setNumber(cm, p.start, tmp.n + 1);\n                }\n            } else {\n                if (p.n !== 1) setNumber(cm, p.start, 1);\n            }\n        }\n\n        if (p = (isNumberedList(cm, i) || isItemList(cm, i))) {\n            // rearrange spacing levels in list\n            if (last_p) {\n                if (p.level > last_p.level) {\n                    if (p.level !== last_p.level + 1) {\n                        setLevel(cm, [p.start, p.end], last_p.level + 1, p.type);\n                    }\n                }\n            } else {\n                if (p.level !== 0) {\n                    setLevel(cm, [p.start, p.end], 0, p.type);\n                }\n            }\n        }\n\n        last_p = p;\n        // we can process content block instead of line\n        if (p) {\n            i += (p.end - p.start);\n        }\n    }\n\n    function findLastAtLevel(_cm, line, line_limit_inf, level) {\n        let p;\n        do {\n            line -= 1;\n            if ((p = isNumberedList(_cm, line)) && p.level === level) {\n                return p;\n            }\n        } while (line > line_limit_inf);\n\n        return null;\n    }\n\n    function setLevel(_cm, range, level, type) {\n        let content;\n        let i;\n        for (i=range[0]; i<=range[1]; i++) {\n            content = cm.getLine(i).trimLeft();\n            const n_spaces = (function(_level, _line, _type) {\n                let spaces = _level * 3;\n                if (_line > 0) {\n                    spaces += _type === \"numbered\" ? 3 : 2;\n                }\n                return spaces;\n            }(level, i - range[0], type));\n\n            content = \" \".repeat(n_spaces) + content;\n            cm.replaceRange(\n                content,\n                { line: i, ch: 0 },\n                { line: i, ch: _cm.getLine(i).length },\n            );\n        }\n    }\n\n    function setNumber(_cm, line, level) {\n        const content = _cm.getLine(line);\n        const new_content = content.replace(/[0-9]+\\./, level+\".\");\n        cm.replaceRange(\n            new_content,\n            { line, ch: 0 },\n            { line, ch: content.length },\n        );\n    }\n\n    function find_limit_inferior(_cm, _line) {\n        let content;\n        let p;\n        let match;\n        let line_candidate = _line;\n        do {\n            content = _cm.getLine(_line);\n            p = isNumberedList(_cm, _line);\n            match = /(\\s+).*$/.exec(content);\n            if (p) line_candidate = _line;\n            if (!p || !match) break;\n            _line -= 1;\n        } while (_line >= 0);\n        return line_candidate;\n    }\n    function find_limit_superior(_cm, _line) {\n        let content;\n        let p;\n        let match;\n        let line_candidate = _line;\n        do {\n            content = _cm.getLine(_line);\n            p = isNumberedList(_cm, _line);\n            match = /(\\s+).*$/.exec(content);\n            if (p) line_candidate = _line;\n            if (!p || !match) break;\n            _line += 1;\n        } while (_line < _cm.lineCount());\n        return line_candidate;\n    }\n}\n\nfunction swap(cm, from, to) {\n    const from_content = cm.getRange(\n        { line: from[0], ch: 0 },\n        { line: from[1], ch: cm.getLine(from[1]).length },\n    );\n    const to_content = cm.getRange(\n        { line: to[0], ch: 0 },\n        { line: to[1], ch: cm.getLine(to[1]).length },\n    );\n    const cursor = cm.getCursor();\n\n    if (to[0] > from[0]) {\n        // moving down\n        cm.replaceRange(\n            from_content,\n            { line: to[0], ch: 0 },\n            { line: to[1], ch: cm.getLine(to[1]).length },\n        );\n        cm.replaceRange(\n            to_content,\n            { line: from[0], ch: 0 },\n            { line: from[1], ch: cm.getLine(from[1]).length },\n        );\n        cm.setCursor({\n            line: cursor.line + (to[1] - to[0] + 1),\n            ch: cursor.ch,\n        });\n    } else {\n        // moving up\n        cm.replaceRange(\n            to_content,\n            { line: from[0], ch: 0 },\n            { line: from[1], ch: cm.getLine(from[1]).length },\n        );\n        cm.replaceRange(\n            from_content,\n            { line: to[0], ch: 0 },\n            { line: to[1], ch: cm.getLine(to[1]).length },\n        );\n        cm.setCursor({\n            line: cursor.line - (to[1] - to[0] + 1),\n            ch: cursor.ch,\n        });\n    }\n}\n\nexport function fold(cm, start) {\n    cm.foldCode(start, null, \"fold\");\n}\nexport function unfold(cm, start) {\n    cm.foldCode(start, null, \"unfold\");\n}\nexport function isFold(cm, start) {\n    const line = start.line;\n    const marks = cm.findMarks(window.CodeMirror.Pos(line, 0), window.CodeMirror.Pos(line + 1, 0));\n    for (let i = 0; i < marks.length; ++i) {\n        if (marks[i].__isFold && marks[i].find().from.line === line) return marks[i];\n    }\n    return false;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/erlang.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/erlang/erlang.js\";\nwindow.CodeMirror.__mode = \"erlang\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/go.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/go/go.js\";\nwindow.CodeMirror.__mode = \"go\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/groovy.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/groovy/groovy.js\";\nwindow.CodeMirror.__mode = \"text/x-groovy\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/htmlmixed.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/htmlmixed/htmlmixed.js\";\nwindow.CodeMirror.__mode = \"htmlmixed\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/java.js",
    "content": "import \"./clike.js\";\nwindow.CodeMirror.__mode = \"text/x-java\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/javascript.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/javascript/javascript.js\";\nwindow.CodeMirror.__mode = \"javascript\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/jsx.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/jsx/jsx.js\";\nwindow.CodeMirror.__mode = \"jsx\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/keymap_base.js",
    "content": "import \"../../../lib/vendor/codemirror/keymap/sublime.js\";\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/keymap_vim.js",
    "content": "import \"../../../lib/vendor/codemirror/keymap/vim.js\";\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/lua.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/lua/lua.js\";\nwindow.CodeMirror.__mode = \"lua\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/orgmode.js",
    "content": "import \"../../../lib/vendor/codemirror/addon/mode/simple.js\";\nimport {\n    org_cycle, org_shifttab, org_metaleft, org_metaright, org_meta_return, org_metaup,\n    org_metadown, org_insert_todo_heading, org_shiftleft, org_shiftright, fold, unfold,\n    isFold, org_shiftmetaleft, org_shiftmetaright,\n} from \"./emacs-org.js\";\nimport { join } from \"../../../lib/path.js\";\nimport { currentShare } from \"../../filespage/cache.js\";\n\nwindow.CodeMirror.__mode = \"orgmode\";\n\nwindow.CodeMirror.defineSimpleMode(\"orgmode\", {\n    start: [\n        { regex: /(\\*\\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DONE|REJECTED|STOP|STOPPED|)(\\s+\\[\\#[A-C]\\]\\s+|)(.*?)(?:(\\s{10,}|))(\\:[\\S]+\\:|)$/, sol: true, token: [\"header level1 org-level-star\", \"header level1 org-todo\", \"header level1 org-done\", \"header level1 org-priority\", \"header level1\", \"header level1 void\", \"header level1 comment\"] },\n        { regex: /(\\*{1,}\\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DEFERRED|DONE|REJECTED|STOP|STOPPED|)(\\s+\\[\\#[A-C]\\]\\s+|)(.*?)(?:(\\s{10,}|))(\\:[\\S]+\\:|)$/, sol: true, token: [\"header org-level-star\", \"header org-todo\", \"header org-done\", \"header org-priority\", \"header\", \"header void\", \"header comment\"] },\n        { regex: /(\\+[^\\+]+\\+)/, token: [\"strikethrough\"] },\n        { regex: /(\\*[^\\*]+\\*)/, token: [\"strong\"] },\n        { regex: /(\\/[^\\/]+\\/)/, token: [\"em\"] },\n        { regex: /(\\_[^\\_]+\\_)/, token: [\"link\"] },\n        { regex: /(\\~[^\\~]+\\~)/, token: [\"comment\"] },\n        { regex: /(\\=[^\\=]+\\=)/, token: [\"comment\"] },\n        { regex: /\\[\\[[^\\[\\]]+\\]\\[[^\\[\\]]+\\]\\]/, token: \"org-url\" }, // links\n        { regex: /\\[\\[[^\\[\\]]+\\]\\]/, token: \"org-image\" }, // image\n        { regex: /\\[[xX\\s\\-\\_]\\]/, token: \"qualifier org-toggle\" }, // checkbox\n        { regex: /\\#\\+(?:(BEGIN|begin))_[a-zA-Z]*/, token: \"comment\", next: \"env\", sol: true }, // comments\n        { regex: /:?[A-Z_]+\\:.*/, token: \"comment\", sol: true }, // property drawers\n        { regex: /(\\#\\+[a-zA-Z_]*)(\\:.*)/, token: [\"keyword\", \"qualifier\"], sol: true }, // environments\n        { regex: /(CLOCK\\:|SHEDULED\\:|DEADLINE\\:)(\\s.+)/, token: [\"comment\", \"keyword\"] },\n    ],\n    env: [\n        { regex: /\\#\\+(?:(END|end))_[a-zA-Z]*/, token: \"comment\", next: \"start\", sol: true },\n        { regex: /.*/, token: \"comment\" },\n    ],\n});\n\nwindow.CodeMirror.registerHelper(\"fold\", \"orgmode\", function(cm, start) {\n    // init\n    const levelToMatch = headerLevel(start.line);\n\n    // no folding needed\n    if (levelToMatch === null) return;\n\n    // find folding limits\n    const lastLine = cm.lastLine();\n    let end = start.line;\n    while (end < lastLine) {\n        end += 1;\n        const level = headerLevel(end);\n        if (level && level <= levelToMatch) {\n            end = end - 1;\n            break;\n        };\n    }\n\n    return {\n        from: window.CodeMirror.Pos(start.line, cm.getLine(start.line).length),\n        to: window.CodeMirror.Pos(end, cm.getLine(end).length),\n    };\n\n    function headerLevel(lineNo) {\n        const line = cm.getLine(lineNo);\n        const match = /^\\*+/.exec(line);\n        if (match && match.length === 1 && /header/.test(cm.getTokenTypeAt(window.CodeMirror.Pos(lineNo, 0)))) {\n            return match[0].length;\n        }\n        return null;\n    }\n});\n\nwindow.CodeMirror.registerGlobalHelper(\"fold\", \"drawer\", function(mode) {\n    return mode.name === \"orgmode\";\n}, function(cm, start) {\n    const drawer = isBeginningOfADrawer(start.line);\n    if (drawer === false) return;\n\n    // find folding limits\n    const lastLine = cm.lastLine();\n    let end = start.line;\n    while (end < lastLine) {\n        end += 1;\n        if (isEndOfADrawer(end)) {\n            break;\n        }\n    }\n\n    return {\n        from: window.CodeMirror.Pos(start.line, cm.getLine(start.line).length),\n        to: window.CodeMirror.Pos(end, cm.getLine(end).length),\n    };\n\n    function isBeginningOfADrawer(lineNo) {\n        const line = cm.getLine(lineNo);\n        const match = /^\\:.*\\:$/.exec(line);\n        if (match && match.length === 1 && match[0] !== \":END:\") {\n            return true;\n        }\n        return false;\n    }\n    function isEndOfADrawer(lineNo) {\n        const line = cm.getLine(lineNo);\n        return line.trim() === \":END:\";\n    }\n});\n\nwindow.CodeMirror.registerHelper(\"orgmode\", \"init\", (editor) => {\n    editor.setOption(\"extraKeys\", {\n        \"Tab\": (cm) => org_cycle(cm),\n        \"Shift-Tab\": (cm) => org_shifttab(cm),\n        \"Alt-Left\": (cm) => org_metaleft(cm),\n        \"Alt-Right\": (cm) => org_metaright(cm),\n        \"Alt-Enter\": (cm) => org_meta_return(cm),\n        \"Alt-Up\": (cm) => org_metaup(cm),\n        \"Alt-Down\": (cm) => org_metadown(cm),\n        \"Shift-Alt-Left\": (cm) => org_shiftmetaleft(cm),\n        \"Shift-Alt-Right\": (cm) => org_shiftmetaright(cm),\n        \"Shift-Alt-Enter\": (cm) => org_insert_todo_heading(cm),\n        \"Shift-Left\": (cm) => org_shiftleft(cm),\n        \"Shift-Right\": (cm) => org_shiftright(cm),\n    });\n\n    editor.on(\"mousedown\", toggleHandler);\n    editor.on(\"touchstart\", toggleHandler);\n    editor.on(\"gutterClick\", foldLine);\n\n    // fold everything except headers by default\n    editor.operation(function() {\n        for (let i = 0; i < editor.lineCount(); i++) {\n            if (/header/.test(editor.getTokenTypeAt(window.CodeMirror.Pos(i, 0))) === false) {\n                fold(editor, window.CodeMirror.Pos(i, 0));\n            }\n        }\n    });\n    return window.CodeMirror.orgmode.destroy.bind(this, editor);\n});\n\nwindow.CodeMirror.registerHelper(\"orgmode\", \"destroy\", (editor) => {\n    editor.off(\"mousedown\", toggleHandler);\n    editor.off(\"touchstart\", toggleHandler);\n    editor.off(\"gutterClick\", foldLine);\n});\n\nfunction foldLine(cm, line) {\n    const cursor = { line, ch: 0 };\n    isFold(cm, cursor) ? unfold(cm, cursor) : fold(cm, cursor);\n}\n\nlet widgets = [];\nfunction toggleHandler(cm, e) {\n    const position = cm.coordsChar({\n        left: e.clientX || (e.targetTouches && e.targetTouches[0].clientX),\n        top: e.clientY || (e.targetTouches && e.targetTouches[0].clientY),\n    }, \"page\");\n    const token = cm.getTokenAt(position);\n\n    _disableSelection();\n    if (/org-level-star/.test(token.type)) {\n        _preventIfShould();\n        _foldHeadline();\n        _disableSelection();\n    } else if (/org-toggle/.test(token.type)) {\n        _preventIfShould();\n        _toggleCheckbox();\n        _disableSelection();\n    } else if (/org-todo/.test(token.type)) {\n        _preventIfShould();\n        _toggleTodo();\n        _disableSelection();\n    } else if (/org-done/.test(token.type)) {\n        _preventIfShould();\n        _toggleDone();\n        _disableSelection();\n    } else if (/org-priority/.test(token.type)) {\n        _preventIfShould();\n        _togglePriority();\n        _disableSelection();\n    } else if (/org-url/.test(token.type)) {\n        _disableSelection();\n        _navigateLink();\n    } else if (/org-image/.test(token.type)) {\n        _disableSelection();\n        _toggleImageWidget();\n    }\n\n    function _preventIfShould() {\n        if (\"ontouchstart\" in window) e.preventDefault();\n    }\n    function _disableSelection() {\n        cm.on(\"beforeSelectionChange\", _onSelectionChangeHandler);\n        function _onSelectionChangeHandler(cm, obj) {\n            obj.update([{\n                anchor: position,\n                head: position,\n            }]);\n            cm.off(\"beforeSelectionChange\", _onSelectionChangeHandler);\n        }\n    }\n\n    function _foldHeadline() {\n        const line = position.line;\n        if (line >= 0) {\n            const cursor = { line, ch: 0 };\n            isFold(cm, cursor) ? unfold(cm, cursor) : fold(cm, cursor);\n        }\n    }\n\n    function _toggleCheckbox() {\n        const line = position.line;\n        const content = cm.getRange(\n            { line, ch: token.start },\n            { line, ch: token.end },\n        );\n        const new_content = content === \"[X]\" || content === \"[x]\" ? \"[ ]\" : \"[X]\";\n        cm.replaceRange(\n            new_content,\n            { line, ch: token.start },\n            { line, ch: token.end },\n        );\n    }\n\n    function _toggleTodo() {\n        const line = position.line;\n        cm.replaceRange(\n            \"DONE\",\n            { line, ch: token.start },\n            { line, ch: token.end },\n        );\n    }\n\n    function _toggleDone() {\n        const line = position.line;\n        cm.replaceRange(\n            \"TODO\",\n            { line, ch: token.start },\n            { line, ch: token.end },\n        );\n    }\n\n    function _togglePriority() {\n        const PRIORITIES = [\" [#A] \", \" [#B] \", \" [#C] \", \" [#A] \"];\n        const line = position.line;\n        const content = cm.getRange({ line, ch: token.start }, { line, ch: token.end });\n        const new_content = PRIORITIES[PRIORITIES.indexOf(content) + 1];\n        cm.replaceRange(\n            new_content,\n            { line, ch: token.start },\n            { line, ch: token.end },\n        );\n    }\n\n    function _toggleImageWidget() {\n        const exist = !!widgets\n            .filter((line) => line === position.line)[0];\n\n        if (exist === false) {\n            if (!token.string.match(/\\[\\[(.*)\\]\\]/)) return null;\n            const $node = _buildImage(RegExp.$1);\n            const widget = cm.addLineWidget(position.line, $node, { coverGutter: false });\n            widgets.push(position.line);\n            $node.addEventListener(\"click\", closeWidget);\n\n            function closeWidget() {\n                widget.clear();\n                $node.removeEventListener(\"click\", closeWidget);\n                widgets = widgets.filter((line) => line !== position.line);\n            }\n        }\n        function _buildImage(src) {\n            const $el = document.createElement(\"div\");\n            const $img = document.createElement(\"img\");\n\n            if (/^https?\\:\\/\\//.test(src)) {\n                $img.src = src;\n            } else {\n                const path = join(location, src).replace(/^\\/view/, \"\");\n                $img.src = \"/api/files/cat?path=\" + path;\n                const share = currentShare();\n                if (share) $img.src += \"&share=\" + share;\n            }\n            $el.appendChild($img);\n            return $el;\n        }\n        return null;\n    }\n\n    function _navigateLink() {\n        token.string.match(/\\[\\[(.*?)\\]\\[/);\n        const link = RegExp.$1;\n        if (!link) return;\n\n        let open = \"_blank\";\n        const isMobile = screen.availWidth < screen.availHeight;\n        if (!document.querySelector(\".component_fab img.component_icon[alt=\\\"save\\\"]\")) {\n            open = \"_self\";\n        } else if (isMobile) {\n            open = \"_self\";\n        }\n\n        if (/^https?\\:\\/\\//.test(link)) {\n            window.open(link, open);\n        } else {\n            let url = join(location, link);\n            const share = currentShare();\n            if (share) url += \"?share=\" + share;\n            window.open(url, open);\n        }\n    }\n}\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/perl.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/perl/perl.js\";\nwindow.CodeMirror.__mode = \"perl\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/php.js",
    "content": "import \"./clike.js\";\nimport \"../../../lib/vendor/codemirror/mode/php/php.js\";\nwindow.CodeMirror.__mode = \"php\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/properties.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/properties/properties.js\";\nwindow.CodeMirror.__mode = \"properties\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/python.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/python/python.js\";\nwindow.CodeMirror.__mode = \"python\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/r.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/r/r.js\";\nwindow.CodeMirror.__mode = \"r\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/ruby.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/ruby/ruby.js\";\nwindow.CodeMirror.__mode = \"ruby\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/rust.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/rust/rust.js\";\nwindow.CodeMirror.__mode = \"rust\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/sass.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/sass/sass.js\";\nwindow.CodeMirror.__mode = \"sass\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/shell.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/shell/shell.js\";\nwindow.CodeMirror.__mode = \"shell\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/sparql.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/sparql/sparql.js\";\nwindow.CodeMirror.__mode = \"sparql\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/spreadsheet.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/spreadsheet/spreadsheet.js\";\nwindow.CodeMirror.__mode = \"spreadsheet\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/sql.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/sql/sql.js\";\nwindow.CodeMirror.__mode = \"sql\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/stex.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/stex/stex.js\";\nwindow.CodeMirror.__mode = \"stex\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/text.js",
    "content": "export default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/xml.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/xml/xml.js\";\nwindow.CodeMirror.__mode = \"xml\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/yaml-frontmatter.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/gfm/gfm.js\";\nimport \"../../../lib/vendor/codemirror/mode/yaml-frontmatter/yaml-frontmatter.js\";\nwindow.CodeMirror.__mode = \"yaml-frontmatter\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor/yaml.js",
    "content": "import \"../../../lib/vendor/codemirror/mode/yaml/yaml.js\";\nwindow.CodeMirror.__mode = \"yaml\";\nexport default window.CodeMirror;\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor.css",
    "content": "/* https://github.com/codemirror/codemirror5/issues/4895 */\n.component_editor {\n    position: relative;\n    flex-grow: 1;\n}\n.component_editor .CodeMirror {\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    height: 100%;\n}\n\n/* UX Integration */\n.CodeMirror-scroll {\n    -webkit-overflow-scrolling: touch;\n}\n\n.CodeMirror {\n    color: #3b4045;\n    background: var(--bg-color);\n}\n\n.CodeMirror-sizer > div {\n    padding-top: 4px;\n    padding-bottom: 5px;\n}\n\n.CodeMirror-foldmarker {\n    padding: 5px;\n}\n\n.CodeMirror .CodeMirror-cursor{\n    background-color: rgba(198,200,204,0.65);\n}\n.CodeMirror .cm-vim-message {\n    color: var(--error)!important;\n}\n\n/* HIDE LINE NUMBERS ON MOBILE */\n@media screen and (max-width: 400px) {\n    .CodeMirror-sizer {\n        margin-left: 0 !important;\n    }\n    .CodeMirror-gutters {\n        display: none;\n    }\n    .CodeMirror-gutter-wrapper {\n        display: none;\n    }\n}\n\n.CodeMirror-linenumber {\n    cursor: pointer;\n}\n\n/* SEARCH */\n.CodeMirror-dialog {\n    position: sticky;\n    left: 0;\n    right: 0;\n    background: var(--color);\n    z-index: 15;\n    padding: 5px .8em;\n    overflow: hidden;\n    color: #e2e2e2;\n    box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5);\n}\n\n.CodeMirror-dialog-top {\n    border-bottom: 1px solid #eee;\n    bottom: 0;\n}\n\n.CodeMirror-dialog-bottom {\n    border-top: 1px solid #eee;\n    bottom: 0;\n}\n\n.CodeMirror-dialog input {\n    border: none;\n    outline: none;\n    background: transparent;\n    width: 20em;\n    color: white;\n    font-family: inherit;\n    padding-left: 5px;\n    font-size: inherit;\n}\n\n.CodeMirror-dialog button {\n    font-size: 70%;\n}\n\n/* Font stuff */\n.CodeMirror {\n    font-size: 16px;\n    font-family: \"Source Code Pro\", monospace;\n}\n@font-face {\n    font-family: \"Source Code Pro\";\n    font-style: normal;\n    font-weight: 400;\n    src: local(\"Source Code Pro\"), local(\"SourceCodePro-Regular\"), url(../../fonts/SourceCodePro-Regular-400-latin-ext.woff2) format(\"woff2\");\n    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;\n}\n@font-face {\n    font-family: \"Source Code Pro\";\n    font-style: normal;\n    font-weight: 400;\n    src: local(\"Source Code Pro\"), local(\"SourceCodePro-Regular\"), url(../../fonts/SourceCodePro-Regular-400-latin.woff2) format(\"woff2\");\n    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;\n}\n@font-face {\n    font-family: \"Source Code Pro\";\n    font-style: normal;\n    font-weight: 600;\n    src: local('Source Code Pro Semibold'), local('SourceCodePro-Semibold'), url(../../fonts/SourceCodePro-Semibold-600-latin-ext.woff2) format(\"woff2\");\n    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;\n}\n@font-face {\n    font-family: \"Source Code Pro\";\n    font-style: normal;\n    font-weight: 600;\n    src: local(\"Source Code Pro Semibold\"), local(\"SourceCodePro-Semibold\"), url(../../fonts/SourceCodePro-Semibold-600-latin.woff2) format(\"woff2\");\n    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;\n}\n\n.cm-s-default .cm-header {\n    font-size: 18px;\n}\n\n.cm-s-default .cm-header.cm-level1 {\n    font-size: 19px;\n}\n\n@media only screen and (max-width: 600px) {\n    .CodeMirror {\n        font-size: 14px;\n    }\n    .cm-s-default .cm-header {\n        font-size: 15px;\n    }\n    .cm-s-default .cm-header.cm-level1 {\n        font-size: 16px;\n    }\n}\n\n/* Make things more confy */\n.CodeMirror .CodeMirror-code {\n    line-height: 1.3em;\n}\n.CodeMirror .CodeMirror-code > div {\n    clear: both;\n}\n\n.CodeMirror[mode=\"orgmode\"] .CodeMirror-code {\n    line-height: 1.5em;\n}\n\n.CodeMirror .CodeMirror-line {\n    padding-left: 10%;\n    padding-right: 0;\n    max-width: 950px;\n}\n@media screen and (max-width: 1150px) {\n    .CodeMirror .CodeMirror-line {\n        padding-left: 8%;\n        padding-right: 0;\n    }\n}\n@media screen and (max-width: 900px) {\n    .CodeMirror .CodeMirror-line {\n        padding-left: 5%;\n        padding-right: 0;\n    }\n}\n@media screen and (max-width: 800px) {\n    .CodeMirror .CodeMirror-line {\n        padding-left: 2%;\n        padding-right: 0;\n    }\n}\n.CodeMirror .CodeMirror-linenumber {\n    color: var(--color);\n    opacity: 0.6;\n}\n\n.CodeMirror .CodeMirror-gutters {\n    box-shadow: none;\n    background-color: inherit;\n    border-right: none;\n}\n\n/* Widget stuff */\n.CodeMirror-linewidget img {\n    cursor: pointer;\n    margin: 10px;\n    height: 300px;\n    max-width: 80%;\n    text-align: center;\n    box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.5);\n    background: var(--dark);\n}\n\n/* Code Highlight Theme */\n.cm-s-default .cm-header {\n    color: #3E7AA6;\n    line-height: 1em;\n    font-weight: 600;\n}\n\n.cm-header.cm-level1 {\n    color: #376e95;\n}\n\n.cm-s-default .cm-keyword {\n    color: var(--emphasis-secondary);\n}\n\n.cm-s-default .cm-header.cm-org-level-star {\n    color: #6f6f6f;\n    vertical-align: baseline;\n    display: inline-block;\n    padding-left: 5px;\n    margin-left: -5px;\n    cursor: pointer;\n}\n\n.cm-s-default .cm-header.cm-org-todo {\n    color: #FF8355;\n    font-weight: normal;\n    cursor: pointer;\n}\n\n.cm-s-default .cm-header.cm-org-done {\n    color: #3BB27C;\n    font-weight: normal;\n    cursor: pointer;\n}\n\n.cm-s-default .cm-header.cm-org-priority {\n    cursor: pointer;\n    font-weight: normal;\n}\n\n.cm-s-default .cm-org-toggle {\n    cursor: pointer;\n    background: var(--light);\n    color: var(--super-light);\n    border-radius: 3px;\n    font-weight: bold;\n    padding-bottom: 2px;\n    vertical-align: text-bottom;\n    opacity: 0.7;\n}\n\n.cm-s-default .cm-void {\n    display: inline-block;\n    max-width: 10px;\n    overflow: hidden;\n    white-space: nowrap;\n}\n\n.cm-s-default .cm-header.cm-comment {\n    font-weight: normal;\n    font-size: 0.9em !important;\n    float: right;\n    display: inline-block;\n    color: var(--emphasis);\n    line-height: 23px;\n}\n\npre.CodeMirror-line {\n    clear: right;\n}\n\n.cm-s-default .cm-link {\n    color: var(--emphasis);\n}\n\n.cm-s-default .cm-strong {\n    color: var(--emphasis);\n}\n\n.cm-s-default .cm-org-url, .cm-s-default .cm-org-image {\n    color: var(--emphasis) !important;\n    border-bottom: 1px dashed var(--light);\n    cursor: pointer;\n}\n\n.cm-s-default .cm-variable-3 {\n    color: #085;\n}\n\n.cm-s-default .cm-comment {\n    color: var(--light);\n}\n\n.cm-s-default .cm-string, .cm-s-default .cm-string-2 {\n    color: #c41a16;\n}\n\n.cm-s-default .cm-def {\n    color: #445588;\n}\n\n.cm-s-default .cm-quote {\n    color: var(--dark);\n}\n.cm-s-default .cm-invalidchar {\n    color: var(--error);\n}\n\n.CodeMirror-gutters {\n    box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.1);\n}\n\n.CodeMirror-foldmarker {\n    padding-left: 5px;\n    color: var(--color)!important;\n    text-shadow: 1px 1px 10px var(--color)!important;\n}\n\nspan.CodeMirror-matchingbracket,\nspan.CodeMirror-matchingtag {\n    background: rgba(0, 0, 0, 0.1);\n    color: inherit !important;\n}\n\n/* BUGFIX */\n.CodeMirror-cursor {\n    min-width: 1px !important;\n}\n\n/* DAR MODE THEME */\n.dark-mode .CodeMirror {\n    background: #232426;\n}\n.dark-mode .CodeMirror, .dark-mode .CodeMirror .CodeMirror-linenumber, .dark-mode .CodeMirror .cm-variable-2, .dark-mode .CodeMirror .cm-variable-3, .dark-mode .CodeMirror .cm-number, .dark-mode .CodeMirror .cm-quote {\n    color: #f6f3e8;\n}\n.dark-mode .CodeMirror .cm-string, .dark-mode .CodeMirror .cm-string-2 {\n    color: #95e454;\n}\n.dark-mode .CodeMirror .cm-atom {\n    color: #e5786d;\n}\n.dark-mode .CodeMirror .cm-keyword, .dark-mode .CodeMirror .cm-meta, .dark-mode .CodeMirror .cm-header, .dark-mode .CodeMirror .cm-property {\n    color: #8ac6f2;\n}\n.dark-mode .CodeMirror .cm-def, .dark-mode .CodeMirror .cm-tag, .dark-mode .CodeMirror .cm-attribute, .dark-mode .CodeMirror .cm-builtin, .dark-mode .CodeMirror .cm-qualifier {\n    color: #cae682;\n}\n.dark-mode .CodeMirror .cm-comment {\n    color: #99968b;\n}\n.dark-mode .CodeMirror .CodeMirror-cursor {\n    background-color: #99968b;\n}\n.dark-mode .CodeMirror .CodeMirror-selected {\n    background-color: rgba(0, 0, 0, 0.3);\n}\n.dark-mode .CodeMirror .cm-error {\n    color: var(--error);\n}\n.dark-mode .CodeMirror .cm-org-url, .dark-mode .CodeMirror .cm-link, .dark-mode .CodeMirror .cm-org-image, .dark-mode .CodeMirror .cm-url {\n    color: #ccaa8f !important;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor.d.ts",
    "content": "interface Window {\n    CodeMirror: {\n        (element: HTMLElement, options: any): any;\n        __mode: string;\n        commands: {\n            save: (editor: any) => void;\n        };\n    };\n}\n\nexport default function(any): void;"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor.js",
    "content": "import { createElement, onDestroy } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport { animate, slideXIn, opacityOut } from \"../../lib/animate.js\";\nimport { qs, safe } from \"../../lib/dom.js\";\nimport { get as getConfig } from \"../../model/config.js\";\nimport { load as loadPlugin } from \"../../model/plugin.js\";\nimport { createLoader } from \"../../components/loader.js\";\nimport { createModal, MODAL_RIGHT_BUTTON } from \"../../components/modal.js\";\nimport { loadCSS, loadJS } from \"../../helpers/loader.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { extname } from \"../../lib/path.js\";\nimport t from \"../../locales/index.js\";\n\nimport ctrlError from \"../ctrl_error.js\";\nimport ctrlDownloader, { init as initDownloader } from \"./application_downloader.js\";\nimport { $ICON } from \"./common_fab.js\";\nimport { cat, save } from \"./model_files.js\";\n\nimport { renderMenubar, buttonDownload } from \"./component_menubar.js\";\nimport \"../../components/fab.js\";\nimport \"../../components/icon.js\";\n\nconst TIME_BEFORE_ABORT_EDIT = 5000;\n\nclass IEditor {}\n\nexport default async function(render, { acl$, getFilename, getDownloadUrl, mime }) {\n    const $page = createElement(`\n        <div class=\"component_ide\">\n            <component-menubar filename=\"${safe(getFilename())}\" class=\"hidden\"></component-menubar>\n            <div class=\"component_editor hidden\"></div>\n            <button is=\"component-fab\" class=\"hidden\"></button>\n        </div>\n    `);\n    const $dom = {\n        editor: () => qs($page, \".component_editor\"),\n        menubar: () => qs($page, \"component-menubar\"),\n        fab: () => qs($page, `[is=\"component-fab\"]`),\n    };\n    render($page);\n    renderMenubar($dom.menubar(), buttonDownload(getFilename(), getDownloadUrl()));\n\n    const content$ = new rxjs.ReplaySubject(1);\n\n    // feature1: setup the dom\n    const removeLoader = createLoader($page);\n    const setup$ = rxjs.zip(\n        rxjs.race(\n            // when a download takes too long, abort, we don't want to spin for 2 hours\n            // only to find out the user tried to open something that don't even fit in\n            // memory. This also account for terrible network conditions when out in\n            // the bush; we abort after:\n            // TIME_BEFORE_ABORT_EDIT + NETWORK LATENCY seconds\n            cat(getDownloadUrl()),\n            rxjs.of(null).pipe(\n                rxjs.delay(TIME_BEFORE_ABORT_EDIT),\n                rxjs.mergeMap(() => ajax(\"about\")),\n                rxjs.mapTo(null),\n            ),\n        ),\n        acl$,\n    ).pipe(\n        rxjs.mergeMap(([content, acl]) => {\n            if (content === null || has_binary(content)) return rxjs.from(initDownloader()).pipe(\n                removeLoader,\n                rxjs.mergeMap(() => {\n                    ctrlDownloader(render, { acl$, getFilename, getDownloadUrl });\n                    return rxjs.EMPTY;\n                }),\n            );\n            return rxjs.of(content).pipe(\n                rxjs.mergeMap((content) => rxjs.of(getConfig()).pipe(\n                    rxjs.mergeMap((config) => rxjs.from(loadKeybinding(config.editor)).pipe(rxjs.mapTo(config))),\n                    rxjs.map((config) => [content, config]),\n                    rxjs.mergeMap((arr) => rxjs.from(loadMode(extname(getFilename()))).pipe(\n                        rxjs.map((mode) => arr.concat([mode])),\n                    )),\n                )),\n                removeLoader,\n                rxjs.map(([content, config, mode]) => {\n                    const $editor = $dom.editor();\n                    content$.next(content);\n                    $editor.classList.remove(\"hidden\");\n                    const editor = window.CodeMirror($editor, {\n                        value: content,\n                        lineNumbers: true,\n                        mode: window.CodeMirror.__mode,\n                        keyMap: [\"emacs\", \"vim\"].indexOf(config[\"editor\"]) === -1 ? \"sublime\" : config[\"editor\"],\n                        lineWrapping: true,\n                        readOnly: acl.indexOf(\"PUT\") === -1 && acl.indexOf(\"POST\") === -1,\n                        foldOptions: { widget: \"...\" },\n                        matchBrackets: {},\n                        autoCloseBrackets: true,\n                        matchTags: { bothTags: true },\n                        autoCloseTags: true,\n                    });\n                    editor.getWrapperElement().setAttribute(\"mode\", mode);\n                    if (!(\"ontouchstart\" in window)) editor.focus();\n                    if (config[\"editor\"] === \"emacs\") editor.addKeyMap({\n                        \"Ctrl-X Ctrl-C\": () => window.history.back(),\n                    });\n                    if (mode === \"orgmode\") {\n                        const cleanup = window.CodeMirror.orgmode.init(editor);\n                        onDestroy(cleanup);\n                    }\n                    onDestroy(() => editor.clearHistory());\n                    $dom.menubar().classList.remove(\"hidden\");\n                    return editor;\n                }),\n                rxjs.tap((editor) => requestAnimationFrame(() => editor.refresh())),\n            );\n        }),\n        rxjs.mergeMap(async(editor) => {\n            const loader = await loadPlugin(mime);\n            if (loader) new (await loader(IEditor, { mime, $menubar: $dom.menubar(), getFilename, getDownloadUrl }))(editor);\n            return editor;\n        }),\n        rxjs.catchError(ctrlError()),\n        rxjs.share(),\n    );\n    effect(setup$);\n\n    // feature2: handle resize\n    effect(setup$.pipe(\n        rxjs.mergeMap((editor) => rxjs.fromEvent(window, \"resize\").pipe(\n            rxjs.tap(() => editor.refresh()),\n        )),\n    ));\n\n    // feature3: handle UI for edit\n    effect(setup$.pipe(\n        rxjs.switchMap((editor) => new rxjs.Observable((observer) => editor.on(\"change\", (cm) => observer.next(cm)))),\n        rxjs.mergeMap((editor) => content$.pipe(rxjs.map((oldContent) => [editor, editor.getValue(), oldContent]))),\n        rxjs.tap(async([editor, newContent = \"\", oldContent = \"\"]) => {\n            const $fab = $dom.fab();\n            if ($fab.disabled) return;\n            const $breadcrumb = qs(document.body, \"component-breadcrumb\");\n            if (newContent === oldContent) {\n                await animate($fab, { time: 100, keyframes: opacityOut() });\n                $fab.classList.add(\"hidden\");\n                $breadcrumb.removeAttribute(\"indicator\");\n                return;\n            }\n            $breadcrumb.setAttribute(\"indicator\", \"true\");\n            const shouldAnimate = $fab.classList.contains(\"hidden\");\n            $fab.classList.remove(\"hidden\");\n            $fab.render($ICON.SAVING);\n            $fab.onclick = () => window.CodeMirror.commands.save(editor);\n\n            if (shouldAnimate) await animate($fab, { time: 100, keyframes: slideXIn(40) });\n        }),\n    ));\n\n    // feature4: save\n    effect(setup$.pipe(\n        rxjs.mergeMap(() => new rxjs.Observable((observer) => {\n            window.CodeMirror.commands.save = (cm) => observer.next(cm);\n        })),\n        rxjs.mergeMap((cm) => {\n            const $fab = $dom.fab();\n            $fab.classList.remove(\"hidden\");\n            $fab.render($ICON.LOADING);\n            $fab.disabled = true;\n            const content = cm.getValue();\n            return save(content).pipe(rxjs.tap(() => {\n                $fab.removeAttribute(\"disabled\");\n                content$.next(content);\n            }));\n        }),\n        rxjs.catchError(ctrlError()),\n    ));\n\n    // feature5: save on exit\n    effect(setup$.pipe(\n        rxjs.mergeMap((cm) => new Promise((resolve, reject) => window.history.block = async() => {\n            const block = qs(document.body, \"component-breadcrumb\").hasAttribute(\"indicator\");\n            if (block === false) return false;\n            const userAction = await new Promise((done) => {\n                createModal({\n                    withButtonsRight: t(\"Yes\"),\n                    withButtonsLeft: t(\"No\"),\n                })(\n                    createElement(`\n                        <div style=\"text-align:center;padding-bottom:5px;\">\n                            ${t(\"Do you want to save the changes ?\")}\n                        </div>\n                    `),\n                    (val) => done(val),\n                );\n            });\n            if (userAction === MODAL_RIGHT_BUTTON) {\n                const $fab = $dom.fab();\n                $fab.render($ICON.LOADING);\n                $fab.disabled = true;\n                try {\n                    await save(cm.getValue()).toPromise();\n                    resolve();\n                } catch (err) {\n                    reject(err);\n                    return true;\n                }\n            }\n            return false;\n        })),\n        rxjs.catchError(ctrlError()),\n    ));\n}\n\nfunction has_binary(str) {\n    let countUnrepresentableChar = 0;\n    for (let i=0; i<str.length; i++) {\n        if (countUnrepresentableChar > 2) return true;\n        else if (str[i] === \"\\ufffd\") countUnrepresentableChar += 1;\n    }\n    return false;\n}\n\nexport function init() {\n    return Promise.all([\n        loadCSS(import.meta.url, \"../../lib/vendor/codemirror/lib/codemirror.css\"),\n        loadJS(import.meta.url, \"../../lib/vendor/codemirror/lib/codemirror.js\"),\n        loadCSS(import.meta.url, \"./application_editor.css\"),\n    ]).then(() => Promise.all([\n        loadJS(import.meta.url, \"../../lib/vendor/codemirror/keymap/emacs.js\"),\n        // search\n        loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/search/searchcursor.js\"),\n        loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/search/search.js\"),\n        loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/comment/comment.js\"),\n        loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/dialog/dialog.js\"),\n        // folding\n        loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/fold/foldcode.js\"),\n        loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/fold/foldgutter.js\"),\n        loadCSS(import.meta.url, \"../../lib/vendor/codemirror/addon/fold/foldgutter.css\"),\n        // editing feature\n        loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/edit/matchbrackets.js\"),\n        loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/edit/closebrackets.js\"),\n        loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/edit/closetag.js\"),\n    ]));\n}\n\nfunction loadMode(ext) {\n    let mode = \"text\";\n    let before = Promise.resolve(null);\n\n    if (ext === \"org\" || ext === \"org_archive\") {\n        mode = \"orgmode\";\n        before = Promise.all([\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/mode/simple.js\"),\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/fold/xml-fold.js\"),\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/edit/matchtags.js\"),\n        ]).then(() => Promise.resolve(null));\n    } else if (ext === \"sh\") mode = \"shell\";\n    else if (ext === \"py\") mode = \"python\";\n    else if (ext === \"html\" || ext === \"htm\") {\n        mode = \"htmlmixed\";\n        before = Promise.all([\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/mode/xml/xml.js\"),\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/mode/javascript/javascript.js\"),\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/mode/css/css.js\"),\n        ]).then(() => Promise.resolve(null));\n    } else if (ext === \"css\") mode = \"css\";\n    else if (ext === \"less\" || ext === \"scss\" || ext === \"sass\") mode = \"sass\";\n    else if (ext === \"js\" || ext === \"json\") mode = \"javascript\";\n    else if (ext === \"jsx\") mode = \"jsx\";\n    else if (ext === \"php\" || ext === \"php5\" || ext === \"php4\") {\n        mode = \"php\";\n        before = Promise.all([\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/mode/xml/xml.js\"),\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/mode/javascript/javascript.js\"),\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/mode/css/css.js\"),\n        ]).then(() => Promise.resolve(null));\n    } else if (ext === \"elm\") mode = \"elm\";\n    else if (ext === \"erl\") mode = \"erlang\";\n    else if (ext === \"go\") mode = \"go\";\n    else if (ext === \"groovy\") mode = \"groovy\";\n    else if (ext === \"markdown\" || ext === \"md\") {\n        mode = \"yaml-frontmatter\";\n        before = Promise.all([\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/mode/markdown/markdown.js\"),\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/mode/gfm/gfm.js\"),\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/mode/yaml/yaml.js\"),\n            loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/mode/overlay.js\"),\n        ]).then(() => Promise.resolve(null));\n    } else if (ext === \"pl\" || ext === \"pm\") mode = \"perl\";\n    else if (ext === \"clj\") mode = \"clojure\";\n    else if (ext === \"el\" || ext === \"lisp\" || ext === \"cl\" ||\n             ext === \"emacs\") mode = \"commonlisp\";\n    else if (ext === \"dockerfile\") {\n        mode = \"dockerfile\";\n        before = loadJS(import.meta.url, \"../../lib/vendor/codemirror/addon/mode/simple.js\").then(() => Promise.resolve(null));\n    } else if (ext === \"R\") mode = \"r\";\n    else if (ext === \"makefile\") mode = \"cmake\";\n    else if (ext === \"rb\") mode = \"ruby\";\n    else if (ext === \"sql\") mode = \"sql\";\n    else if (ext === \"xml\" || ext === \"rss\" || ext === \"svg\" ||\n             ext === \"atom\") mode = \"xml\";\n    else if (ext === \"yml\" || ext === \"yaml\") mode = \"yaml\";\n    else if (ext === \"lua\") mode = \"lua\";\n    else if (ext === \"csv\") mode = \"spreadsheet\";\n    else if (ext === \"rs\" || ext === \"rlib\") mode = \"rust\";\n    else if (ext === \"latex\" || ext === \"tex\") mode = \"stex\";\n    else if (ext === \"diff\" || ext === \"patch\") mode = \"diff\";\n    else if (ext === \"sparql\") mode = \"sparql\";\n    else if (ext === \"properties\") mode = \"properties\";\n    else if (ext === \"c\" || ext === \"cpp\" || ext === \"h\") mode = \"clike\";\n    else if (ext === \"java\") mode = \"java\";\n\n    return before.then(() => loadJS(import.meta.url, `./application_editor/${mode}.js`, { type: \"module\" }))\n        .catch(() => loadJS(import.meta.url, \"./application_editor/text.js\", { type: \"module\" }))\n        .then(() => Promise.resolve(mode));\n}\n\nfunction loadKeybinding(editor) {\n    if (editor === \"emacs\" || !editor) {\n        return Promise.resolve();\n    }\n    return loadJS(import.meta.url, `./application_editor/keymap_${editor}.js`, { type: \"module\" });\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_editor_orgmode.js",
    "content": "export default function(editor) {\n    window.CodeMirror.orgmode.init(editor, (key, value) => {\n        if (key === \"shifttab\") {\n            // org_shifttab(this.state.editor)\n            // this.props.onFoldChange(value);\n        }\n    });\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_form.css",
    "content": ".component_formviewer {\n    background: var(--surface);\n}\n.dark-mode .component_formviewer {\n    color: rgba(0,0,0,0.5);\n}\n.component_formviewer > .formviewer_container {\n    padding-top: 20px;\n    padding-bottom: 50px;\n    overflow-y: auto;\n    flex-direction: column;\n    flex: 1;\n    width: 100%;\n}\n.component_formviewer > .formviewer_container .box {\n    padding: 25px 20px;\n    box-sizing: border-box;\n    border-radius: 5px;\n    background: #fff;\n    box-shadow: rgba(0,0,0,.1) 0px 4px 5px 0px;\n    width: 98%;\n    max-width: 800px;\n    margin: 0 auto;\n}\n.component_formviewer > .formviewer_container .box .formbuilder {\n    margin-bottom: 10px;\n}\n.component_formviewer > .formviewer_container .box .formbuilder label.no-select > div {\n    display: flex;\n    line-height: 30px;\n}\n.component_formviewer > .formviewer_container .box .formbuilder label.no-select > div > div {\n    width: 100%;\n}\n@media screen and (max-width: 470px) {\n    .component_formviewer > .formviewer_container .box .formbuilder label.no-select > div {\n        display: block;\n        line-height: inherit;\n    }\n    .component_formviewer > .formviewer_container .box .formbuilder label.no-select > div span.nothing {\n        position: absolute;\n    }\n}\n.component_formviewer > .formviewer_container .box .formbuilder label.no-select > div > span {\n    display: inline-block;\n    width: 160px;\n    max-width: 160px;\n    text-align: right;\n    padding-right: 15px;\n    color: var(--dark);\n}\n@media screen and (max-width: 470px) {\n    .component_formviewer > .formviewer_container .box .formbuilder label.no-select > div > span {\n        text-align: left;\n        font-weight: bold;\n    }\n}\n.component_formviewer > .formviewer_container .box .formbuilder label.no-select > div > span span.mandatory {\n    opacity: 0.6;\n}\n\n.component_formviewer .formbuilder .component_input[disabled],\n.component_formviewer .formbuilder .component_input[readonly],\n.component_formviewer .formbuilder .component_textarea[disabled],\n.component_formviewer .formbuilder .component_textarea[readonly],\n.component_formviewer .formbuilder .component_select[disabled],\n.component_formviewer .formbuilder .component_select[readonly] {\n    background: rgba(0, 0, 0, 0.05);\n    border-radius: 2px;\n    padding-left: 7px;\n    border-color: transparent;\n}\n\n.component_formviewer fieldset {\n    margin-bottom: 10px;\n    border: 2px solid var(--border);\n    border-radius: 5px;\n    color: var(--light);\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_form.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, applyMutation, onClick } from \"../../lib/rx.js\";\nimport { animate, slideXIn, opacityOut } from \"../../lib/animate.js\";\nimport { qs, qsa, safe } from \"../../lib/dom.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\nimport { createForm, mutateForm } from \"../../lib/form.js\";\nimport { formTmpl, $renderInput } from \"../../components/form.js\";\nimport { createLoader } from \"../../components/loader.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport { transition } from \"./common.js\";\nimport { $ICON } from \"./common_fab.js\";\nimport { cat, save } from \"./model_files.js\";\nimport \"./component_menubar.js\";\n\nimport \"../../components/icon.js\";\nimport \"../../components/fab.js\";\n\nexport default function(render, { acl$, getFilename, getDownloadUrl }) {\n    const $page = createElement(`\n        <div class=\"component_formviewer\">\n            <component-menubar filename=\"${safe(getFilename())}\"></component-menubar>\n            <div class=\"formviewer_container hidden\">\n                <form class=\"sticky box\"></form>\n            </div>\n            <button is=\"component-fab\" data-options=\"download\"></button>\n        </div>\n    `);\n    render($page);\n\n    const $container = qs($page, \".formviewer_container\");\n    const $fab = qs($page, `[is=\"component-fab\"]`);\n    const formState = () => {\n        const $form = qs($page, \"form\");\n        const fd = new FormData($form);\n        const json = [...fd].reduce((acc, el) => {\n            acc[el[0]] = el[1];\n            return acc;\n        }, {});\n        $form.querySelectorAll(\"input[type=\\\"checkbox\\\"][name]\").forEach((cb) => {\n            if (fd.has(cb.name)) json[cb.name] = true;\n            else json[cb.name] = false;\n        });\n        return json;\n    };\n    const file$ = new rxjs.ReplaySubject(1);\n\n    // feature1: setup the dom\n    const removeLoader = createLoader($page);\n    effect(cat(getDownloadUrl()).pipe(\n        rxjs.map((content) => JSON.parse(content)),\n        rxjs.mergeMap((formSpec) => acl$.pipe(rxjs.map((acl) => {\n            if (acl.indexOf(\"POST\") === -1) {\n                return readOnlyForm(formSpec);\n            }\n            return formSpec;\n        }))),\n        rxjs.mergeMap((formSpec) => rxjs.from(createForm(formSpec, formTmpl({\n            renderInput: (opts) => {\n                const $el = $renderInput({ autocomplete: true })(opts);\n                if ($el.hasAttribute(\"disabled\")) {\n                    $el.removeAttribute(\"disabled\");\n                    $el.setAttribute(\"readonly\", \"true\");\n                }\n                return $el;\n            },\n            renderLeaf: ({ label, format, description, required }) => label === \"banner\"\n                ? createElement(`\n                    <div class=\"banner\">\n                        ${fromMarkdown(safe(description))}\n                    </div>\n                `) : createElement(`\n                <label class=\"no-select\">\n                    <div>\n                        <span class=\"ellipsis\">\n                            ${safe(format(label))}\n                            ` + (required === true ? `<span class=\"mandatory\">*</span>`: \"\") +`\n                        </span>\n                        <div data-bind=\"children\"></div>\n                    </div>\n                    <div>\n                        <span class=\"nothing\"></span>\n                        <div class=\"description\">${fromMarkdown(safe(description))}</div>\n                    </div>\n                </label>\n            `),\n        }))).pipe(\n            removeLoader,\n            applyMutation(qs($page, \"form\"), \"replaceChildren\"),\n            rxjs.tap(() => {\n                $container.classList.remove(\"hidden\");\n                transition($container);\n            }),\n            rxjs.mapTo(formSpec),\n        )),\n        rxjs.tap((formSpec) => file$.next(formSpec)),\n        rxjs.catchError(ctrlError()),\n    ));\n\n    // feature2: display/hide save button\n    effect(rxjs.merge(\n        file$.asObservable(),\n        file$.pipe(\n            rxjs.first(),\n            rxjs.mergeMap((formSpec) => rxjs.from(qsa($page, \"form [name]\")).pipe(\n                rxjs.mergeMap(($el) => rxjs.fromEvent($el, \"input\")),\n                rxjs.mapTo(formSpec),\n            )),\n        ),\n    ).pipe(\n        rxjs.map((originalState) => {\n            const smod = (_, value) => value || undefined;\n            return JSON.stringify(formObjToJSON(originalState), smod) !== JSON.stringify(formState(), smod);\n        }),\n        rxjs.mergeMap(async(isSaveButtonVisible) => {\n            if (isSaveButtonVisible) {\n                if ($fab.classList.contains(\"hidden\")) await animate($fab, { time: 100, keyframes: slideXIn(40) });\n                $fab.render($ICON.SAVING);\n                $fab.classList.remove(\"hidden\");\n            } else if (!isSaveButtonVisible) {\n                if (!$fab.classList.contains(\"hidden\")) await animate($fab, { time: 100, keyframes: opacityOut() });\n                $fab.classList.add(\"hidden\");\n            }\n        }),\n        rxjs.catchError(ctrlError()),\n    ));\n\n    // feature3: submit the form\n    effect(onClick($fab).pipe(\n        rxjs.tap(() => {\n            $fab.render($ICON.LOADING);\n            $fab.disabled = true;\n        }),\n        rxjs.mergeMap(() => file$.pipe(\n            rxjs.first(),\n            rxjs.map((formSpec) => mutateForm(formSpec, formState())),\n            rxjs.mergeMap((formSpec) => save(formSpec).pipe(\n                rxjs.tap(() => file$.next(formSpec)),\n            )),\n        )),\n        rxjs.tap(() => {\n            $fab.render($ICON.SAVING);\n            $fab.removeAttribute(\"disabled\");\n            $fab.classList.add(\"hidden\");\n        }),\n        rxjs.catchError(ctrlError()),\n    ));\n}\n\nexport function init() {\n    return loadCSS(import.meta.url, \"./application_form.css\");\n}\n\nconst formObjToJSON = (o, level = 0) => {\n    const obj = Object.assign({}, o);\n    Object.keys(obj).forEach((key) => {\n        const t = obj[key];\n        if (typeof t !== \"object\") throw new Error(\"MALFORMED FORM\");\n        if (\"label\" in t && \"type\" in t && \"default\" in t && \"value\" in t) {\n            obj[key] = obj[key].value;\n        } else {\n            obj[key] = formObjToJSON(obj[key], level + 1);\n        }\n    });\n    return obj;\n};\n\nfunction readOnlyForm(formSpec) {\n    if (\"type\" in formSpec) {\n        formSpec[\"readonly\"] = true;\n        return formSpec;\n    }\n    for (const key in formSpec) {\n        formSpec[key] = readOnlyForm(formSpec[key]);\n    }\n    return formSpec;\n}\n\nfunction fromMarkdown(str = \"\") {\n    str = str.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, \"<a href=\\\"$2\\\">$1</a>\");\n    str = str.replaceAll(\"\\n\", \"<br>\");\n    return str;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_iframe.css",
    "content": "body:not(.dark-mode) .component_appframe {\n    background: var(--surface);\n}\n\n.component_appframe {\n    text-align: center;\n    width: 100%;\n}\n.component_appframe iframe {\n    width: 100%;\n    height: 100%;\n    border: none;\n}\n.component_appframe .error {\n    color: white;\n    font-size: 17px;\n    margin-top: 10px;\n    font-family: monospace;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_iframe.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport { forwardURLParams } from \"../../lib/path.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\nimport notification from \"../../components/notification.js\";\nimport t from \"../../locales/index.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport { getCurrentPath } from \"./common.js\";\n\nexport default function(render, { endpoint = \"\" }) {\n    const url = forwardURLParams(`${endpoint}?path=${encodeURIComponent(getCurrentPath())}`, [\"share\"]);\n    const $page = createElement(`\n        <div class=\"component_appframe\">\n            <iframe style=\"width:100%;height:100%\" src=\"${url}\" scrolling=\"no\"></iframe>\n        </div>\n    `);\n    render($page);\n\n    effect(rxjs.fromEvent(window, \"message\").pipe(\n        rxjs.filter((event) => event.origin === location.origin),\n        rxjs.map((event) => JSON.parse(event.data)),\n        rxjs.tap(({ type, msg }) => {\n            switch (type) {\n            case \"error\":\n                throw new Error(msg);\n            case \"notify::error\":\n                notification.error(t(msg));\n                break;\n            case \"notify::info\":\n                notification.info(t(msg));\n                break;\n            case \"notify::success\":\n                notification.success(t(msg));\n                break;\n            default:\n                break;\n            }\n        }),\n        rxjs.catchError((err) => {\n            notification.error(t(err.message));\n            return ctrlError()(err);\n        }),\n    ));\n}\n\nexport function init() {\n    return loadCSS(import.meta.url, \"./application_iframe.css\");\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_image/information.css",
    "content": ".component_imageviewer .images_aside button {\n    padding: 0;\n}\n.component_imageviewer .images_aside component-icon[name=\"loading\"] {\n    display: block;\n    text-align: center;\n    padding-top: 25px;\n}\n.component_imageviewer .images_aside component-icon[name=\"loading\"] img { height: 30px; }\n\n.component_mapshot {\n    overflow: hidden;\n    width: 100%;\n    max-width: 100%;\n    background: rgba(255, 255, 255, 0.3);\n    border-radius: 3px;\n}\n.component_mapshot .wrapper {\n    position: relative;\n}\n.component_mapshot .wrapper .marker {\n    position: absolute;\n    z-index: 1;\n}\n.component_mapshot .wrapper .marker img {\n    width: 30px;\n    height: 30px;\n}\n.component_mapshot .wrapper:hover .bigpicture {\n    transition: 0.4s ease transform;\n    transform: scale(3);\n}\n.component_mapshot .wrapper .bigpicture {\n    transition: 0.2s ease transform;\n}\n.component_mapshot .wrapper .bigpicture .line {\n    display: flex;\n    width: 33.33%;\n}\n.component_mapshot .wrapper .bigpicture .line img {\n    filter: grayscale(90%);\n}\n.component_mapshot .wrapper .bigpicture .line .btl {\n    border-top-left-radius: 2px;\n}\n.component_mapshot .wrapper .bigpicture .line .btr {\n    border-top-right-radius: 2px;\n}\n.component_mapshot .wrapper .bigpicture .line .bbl {\n    border-bottom-left-radius: 2px;\n}\n.component_mapshot .wrapper .bigpicture .line .bbr {\n    border-bottom-right-radius: 2px;\n}\n.component_mapshot .wrapper .mapshot_placeholder {\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    z-index: 2;\n    border-radius: 2px;\n    background: var(--color);\n}\n.component_mapshot .wrapper .mapshot_placeholder span {\n    display: flex;\n    height: 100%;\n    width: 100%;\n}\n.component_mapshot .wrapper .mapshot_placeholder span .component_loader, .component_mapshot .wrapper .mapshot_placeholder span div {\n    margin: auto;\n    color: var(--light);\n}\n.component_mapshot .wrapper .mapshot_placeholder span .component_loader svg, .component_mapshot .wrapper .mapshot_placeholder span div svg {\n    height: 50px;\n}\n.component_mapshot.loaded .mapshot_placeholder {\n    display: none;\n}\n.component_mapshot.error .mapshot_placeholder.loading {\n    display: none;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_image/information.js",
    "content": "import { createElement, createRender } from \"../../../lib/skeleton/index.js\";\nimport rxjs, { effect, onClick } from \"../../../lib/rx.js\";\nimport { qs } from \"../../../lib/dom.js\";\nimport t from \"../../../locales/index.js\";\nimport { loadJS, loadCSS } from \"../../../helpers/loader.js\";\n\nexport default async function(render, { toggle, load$ }) {\n    const $page = createElement(`\n        <div id=\"pane-info\">\n            <div data-bind=\"header\"></div>\n            <div data-bind=\"body\">\n                <component-icon name=\"loading\"></component-icon>\n            </div>\n        </div>\n    `);\n    render($page);\n    componentHeader(createRender(qs($page, `[data-bind=\"header\"]`)), { toggle });\n    componentBody(createRender(qs($page, `[data-bind=\"body\"]`)), { load$ });\n}\n\nfunction componentHeader(render, { toggle }) {\n    const $header = createElement(`\n        <div class=\"header\">\n            <div>${t(\"Info\")}</div>\n            <button style=\"flex: 1 1 0%;\" aria-label=\"${t(\"close\")}\">\n                <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MS45NzYgNTEuOTc2Ij4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjUzMzMzMjg1O3N0cm9rZS13aWR0aDoxLjQ1NjgxMTE5IiBkPSJtIDQxLjAwNTMxLDQwLjg0NDA2MiBjIC0xLjEzNzc2OCwxLjEzNzc2NSAtMi45ODIwODgsMS4xMzc3NjUgLTQuMTE5ODYxLDAgTCAyNi4wNjg2MjgsMzAuMDI3MjM0IDE0LjczNzU1MSw0MS4zNTgzMSBjIC0xLjEzNzc3MSwxLjEzNzc3MSAtMi45ODIwOTMsMS4xMzc3NzEgLTQuMTE5ODYxLDAgLTEuMTM3NzcyMiwtMS4xMzc3NjggLTEuMTM3NzcyMiwtMi45ODIwODggMCwtNC4xMTk4NjEgTCAyMS45NDg3NjYsMjUuOTA3MzcyIDExLjEzMTkzOCwxNS4wOTA1NTEgYyAtMS4xMzc3NjQ3LC0xLjEzNzc3MSAtMS4xMzc3NjQ3LC0yLjk4MzU1MyAwLC00LjExOTg2MSAxLjEzNzc3NCwtMS4xMzc3NzIxIDIuOTgyMDk4LC0xLjEzNzc3MjEgNC4xMTk4NjUsMCBMIDI2LjA2ODYyOCwyMS43ODc1MTIgMzYuMzY5NzM5LDExLjQ4NjM5OSBjIDEuMTM3NzY4LC0xLjEzNzc2OCAyLjk4MjA5MywtMS4xMzc3NjggNC4xMTk4NjIsMCAxLjEzNzc2NywxLjEzNzc2OSAxLjEzNzc2NywyLjk4MjA5NCAwLDQuMTE5ODYyIEwgMzAuMTg4NDg5LDI1LjkwNzM3MiA0MS4wMDUzMSwzNi43MjQxOTcgYyAxLjEzNzc3MSwxLjEzNzc2NyAxLjEzNzc3MSwyLjk4MjA5MSAwLDQuMTE5ODY1IHoiIC8+Cjwvc3ZnPgo=\" alt=\"close\">\n            </button>\n        </div>\n    `);\n    render($header);\n    effect(onClick(qs($header, `[alt=\"close\"]`)).pipe(rxjs.tap(toggle)));\n}\n\nfunction componentBody(render, { load$ }) {\n    const $page = createElement(`\n        <div>\n            <div class=\"content_box\">\n                <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDggNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjIiIGQ9Ik0gNDAwLDY0IEggMzUyIFYgMTIgQyAzNTIsNS40IDM0Ni42LDAgMzQwLDAgaCAtNDAgYyAtNi42LDAgLTEyLDUuNCAtMTIsMTIgViA2NCBIIDE2MCBWIDEyIEMgMTYwLDUuNCAxNTQuNiwwIDE0OCwwIEggMTA4IEMgMTAxLjQsMCA5Niw1LjQgOTYsMTIgViA2NCBIIDQ4IEMgMjEuNSw2NCAwLDg1LjUgMCwxMTIgdiAzNTIgYyAwLDI2LjUgMjEuNSw0OCA0OCw0OCBoIDM1MiBjIDI2LjUsMCA0OCwtMjEuNSA0OCwtNDggViAxMTIgQyA0NDgsODUuNSA0MjYuNSw2NCA0MDAsNjQgWiBtIC0yLDQwNCBIIDUwIGMgLTMuMywwIC02LjAyMjE0NywtMi43MDAwNyAtNiwtNiBWIDE1NCBoIDM2MCB2IDMwOCBjIDAsMy4zIC0yLjcsNiAtNiw2IHoiIC8+Cjwvc3ZnPgo=\" alt=\"schedule\">\n                <div class=\"headline ellipsis\" data-bind=\"date\">-</div>\n                <div class=\"description ellipsis\" data-bind=\"time\">-</div>\n            </div>\n            <div class=\"content_box\">\n                <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjIiIGQ9Ik01MTIgMTQ0djI4OGMwIDI2LjUtMjEuNSA0OC00OCA0OEg0OGMtMjYuNSAwLTQ4LTIxLjUtNDgtNDhWMTQ0YzAtMjYuNSAyMS41LTQ4IDQ4LTQ4aDg4bDEyLjMtMzIuOWM3LTE4LjcgMjQuOS0zMS4xIDQ0LjktMzEuMWgxMjUuNWMyMCAwIDM3LjkgMTIuNCA0NC45IDMxLjFMMzc2IDk2aDg4YzI2LjUgMCA0OCAyMS41IDQ4IDQ4ek0zNzYgMjg4YzAtNjYuMi01My44LTEyMC0xMjAtMTIwcy0xMjAgNTMuOC0xMjAgMTIwIDUzLjggMTIwIDEyMCAxMjAgMTIwLTUzLjggMTIwLTEyMHptLTMyIDBjMCA0OC41LTM5LjUgODgtODggODhzLTg4LTM5LjUtODgtODggMzkuNS04OCA4OC04OCA4OCAzOS41IDg4IDg4eiIgLz4KPC9zdmc+Cg==\" alt=\"camera\">\n                <div class=\"headline ellipsis\" data-bind=\"camera-setting\">-</div>\n                <div class=\"description ellipsis\" data-bind=\"camera-name\">-</div>\n            </div>\n            <div data-bind=\"map\"></div>\n            <div data-bind=\"all\">\n                <component-icon name=\"loading\"></component-icon>\n            </div>\n        </div>\n    `);\n    render($page);\n\n    effect(load$.pipe(rxjs.tap(async($img) => {\n        if (!$img) return;\n        const metadata = await extractExif($img);\n        qs($page, `[data-bind=\"date\"]`).innerText = formatDate(metadata.date) || \"-\";\n        qs($page, `[data-bind=\"time\"]`).innerText = formatTime(metadata.date) || \"-\";\n        qs($page, `[data-bind=\"camera-setting\"]`).innerText = formatCameraSettings(metadata) || \"-\";\n        qs($page, `[data-bind=\"camera-name\"]`).innerText = formatCameraName(metadata) || \"-\";\n\n        if (metadata.location) await componentMap(createRender(qs($page, `[data-bind=\"map\"]`)), { metadata });\n        componentMore(createRender(qs($page, `[data-bind=\"all\"]`)), { metadata });\n    }), rxjs.catchError((err) => {\n        qs($page, `[data-bind=\"all\"]`).remove();\n        return rxjs.EMPTY;\n    })));\n}\n\nasync function componentMap(render, { metadata }) {\n    const DMSToDD = (d) => {\n        if (!d || d.length !== 4) return null;\n        const [degrees, minutes, seconds, direction] = d;\n        const dd = degrees + minutes/60 + seconds/(60*60);\n        return direction === \"S\" || direction === \"W\" ? -dd : dd;\n    };\n    const lat = DMSToDD(metadata.location[0]);\n    const lng = DMSToDD(metadata.location[1]);\n    const $page = createElement(`\n        <div class=\"component_mapshot error\">\n            <div class=\"wrapper\">\n                <div class=\"mapshot_placeholder error hidden\">\n                    <span><div>Erreur</div></span>\n                </div>\n                <div class=\"mapshot_placeholder loading hidden\">\n                    <div class=\"loader\">\n                        <div class=\"component_loader\">\n                            <svg width=\"120px\" height=\"120px\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"xMidYMid\"><rect x=\"0\" y=\"0\" width=\"100\" height=\"100\" fill=\"none\"></rect><circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"rgba(100%,100%,100%,0.679)\" fill=\"none\" stroke-width=\"10\" stroke-linecap=\"round\"></circle><circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"#6f6f6f\" fill=\"none\" stroke-width=\"6\" stroke-linecap=\"round\"><animate attributeName=\"stroke-dashoffset\" dur=\"2s\" repeatCount=\"indefinite\" from=\"0\" to=\"502\"></animate><animate attributeName=\"stroke-dasharray\" dur=\"2s\" repeatCount=\"indefinite\" values=\"150.6 100.4;1 250;150.6 100.4\"></animate></circle></svg>\n                        </div>\n                    </div>\n                </div>\n                <a href=\"https://www.google.com/maps/search/?api=1&amp;query=${lat},${lng}\">\n                    <div class=\"marker\"><img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+CiAgPHBhdGggc3R5bGU9ImZpbGw6IzMxMzUzODtmaWxsLW9wYWNpdHk6MSIgZD0iTTgsMEM0LjY4NywwLDIsMi42ODcsMiw2YzAsMy44NTQsNC4zMjEsOC42NjMsNSw5LjM5OEM3LjI4MSwxNS43MDMsNy41MTYsMTYsOCwxNnMwLjcxOS0wLjI5NywxLTAuNjAyICBDOS42NzksMTQuNjYzLDE0LDkuODU0LDE0LDZDMTQsMi42ODcsMTEuMzEzLDAsOCwweiBNOCwxMGMtMi4yMDksMC00LTEuNzkxLTQtNHMxLjc5MS00LDQtNHM0LDEuNzkxLDQsNFMxMC4yMDksMTAsOCwxMHogTTgsNCAgQzYuODk2LDQsNiw0Ljg5Niw2LDZzMC44OTYsMiwyLDJzMi0wLjg5NiwyLTJTOS4xMDQsNCw4LDR6IiAvPgo8L3N2Zz4K\" alt=\"location\"></div>\n                    <div data-bind=\"maptile\"></div>\n                </a>\n            </div>\n        </div>\n    `);\n    render($page);\n\n    await new Promise((resolve) => setTimeout(resolve, 500));\n    const TILE_SERVER = \"https://tile.openstreetmap.org/${z}/${x}/${y}.png\";\n    const TILE_SIZE = Math.floor($page.clientWidth / 3 * 100) / 100;\n    if (TILE_SIZE === 0) return;\n    $page.style.height = \"${TILE_SIZE*3}px;\";\n    const mapper = (function map_url(lat, lng, zoom) {\n        // https://wiki.openstreetmap.org/wiki/Slippy_map_tilenamse\n        const n = Math.pow(2, zoom);\n        const tile_numbers = [\n            (lng+180)/360*n,\n            (1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2*n,\n            zoom,\n        ];\n        return {\n            tile: function(tile_server, x = 0, y = 0) {\n                return tile_server\n                    .replace(\"${x}\", Math.floor(tile_numbers[0] || 0)+x)\n                    .replace(\"${y}\", Math.floor(tile_numbers[1] || 0)+y)\n                    .replace(\"${z}\", Math.floor(zoom));\n            },\n            position: function() {\n                return [\n                    tile_numbers[0] - Math.floor(tile_numbers[0]),\n                    tile_numbers[1] - Math.floor(tile_numbers[1]),\n                ];\n            },\n        };\n    }(lat, lng, 11));\n    const $tiles = createElement(`\n        <div class=\"bigpicture\">\n            <div class=\"line\">\n                <img crossorigin=\"anonymous\" src=\"${mapper.tile(TILE_SERVER, -1, -1)}\" class=\"btl\" style=\"height: ${TILE_SIZE}px;\">\n                <img crossorigin=\"anonymous\" src=\"${mapper.tile(TILE_SERVER, 0, -1)}\" style=\"height: ${TILE_SIZE}px;\">\n                <img crossorigin=\"anonymous\" src=\"${mapper.tile(TILE_SERVER, 1, -1)}\" class=\"btr\" style=\"height: ${TILE_SIZE}px;\">\n            </div>\n            <div class=\"line\">\n                <img crossorigin=\"anonymous\" src=\"${mapper.tile(TILE_SERVER, -1, 0)}\" style=\"height: ${TILE_SIZE}px;\">\n                <img crossorigin=\"anonymous\" src=\"${mapper.tile(TILE_SERVER, 0, 0)}\" style=\"height: ${TILE_SIZE}px;\">\n                <img crossorigin=\"anonymous\" src=\"${mapper.tile(TILE_SERVER, 1, 0)}\" style=\"height: ${TILE_SIZE}px;\">\n            </div>\n            <div class=\"line\">\n                <img crossorigin=\"anonymous\" src=\"${mapper.tile(TILE_SERVER, -1, 1)}\" class=\"bbl\" style=\"height: ${TILE_SIZE}px;\">\n                <img crossorigin=\"anonymous\" src=\"${mapper.tile(TILE_SERVER, 0, 1)}\" style=\"height: ${TILE_SIZE}px;\">\n                <img crossorigin=\"anonymous\" src=\"${mapper.tile(TILE_SERVER, 1, 1)}\" class=\"bbr\" style=\"height: ${TILE_SIZE}px;\">\n            </div>\n        </div>\n    `);\n    qs($page, `[data-bind=\"maptile\"]`).appendChild($tiles);\n    const pos = mapper.position();\n    qs($page, \".marker\").setAttribute(\"style\", `\n        left: ${TILE_SIZE * (1 + pos[0]) - 15}px;\n        top: ${TILE_SIZE * (1 + pos[1]) - 30}px;\n    `);\n    const center = (position, i) => Math.floor(TILE_SIZE * (1 + position[i]) * 1000)/1000;\n    $tiles.setAttribute(\"style\", `transform-origin: ${center(mapper.position(), 0)}px ${center(mapper.position(), 1)}px;`);\n}\n\nfunction componentMore(render, { metadata }) {\n    const $all = document.createDocumentFragment();\n    const formatKey = (str) => str.replace(/([A-Z][a-z])/g, \" $1\");\n    const formatValue = (str) => {\n        if (!metadata.all || metadata.all[str] === undefined) return \"-\";\n        if (typeof metadata.all[str] === \"number\") {\n            return Math.floor(metadata.all[str]*100)/100;\n        } else if (metadata.all[str].denominator !== undefined &&\n                   metadata.all[str].numerator !== undefined) {\n            if (metadata.all[str].denominator === 1) {\n                return metadata.all[str].numerator;\n            } else if (metadata.all[str].numerator > metadata.all[str].denominator) {\n                return Math.floor(\n                    metadata.all[str].numerator * 10 / metadata.all[str].denominator,\n                ) / 10;\n            } else {\n                return metadata.all[str].numerator+\"/\"+metadata.all[str].denominator;\n            }\n        } else if (typeof metadata.all[str] === \"string\") {\n            return metadata.all[str];\n        } else if (Array.isArray(metadata.all[str])) {\n            let arr = metadata.all[str];\n            if (arr.length > 15) {\n                arr = arr.slice(0, 3);\n                arr.push(\"...\");\n            }\n            return arr.toString().split(\",\").join(\", \");\n        } else {\n            return JSON.stringify(metadata.all[str], null, 2);\n        }\n    };\n    Object.keys(metadata.all || {}).sort((a, b) => {\n        if (a.toLowerCase().trim() < b.toLowerCase().trim()) return -1;\n        else if (a.toLowerCase().trim() > b.toLowerCase().trim()) return +1;\n        return 0;\n    }).forEach((key) => {\n        switch (key) {\n        case \"undefined\":\n        case \"thumbnail\":\n            break;\n        default: $all.appendChild(createElement(`\n            <div class=\"meta_key\">\n                <div class=\"title ellipsis\">${formatKey(key)}: </div>\n                <div class=\"value ellipsis\" title=\"${formatValue(key)}\">${formatValue(key)}</div>\n            </div>\n        `));\n        }\n    });\n    render($all);\n}\n\nexport function init() {\n    return Promise.all([\n        loadJS(import.meta.url, \"../../../lib/vendor/exif-js.js\"),\n        loadCSS(import.meta.url, \"./information.css\"),\n    ]);\n}\n\nconst extractExif = ($img) => new Promise((resolve) => window.EXIF.getData($img, function() {\n    const metadata = window.EXIF.getAllTags($img);\n    const to_date = (str = \"\") => {\n        if (str === \"\") return null;\n        const digits = str.split(/[ :]/).map((digit) => parseInt(digit));\n        return new Date(\n            digits[0] || 0,\n            digits[1] || 0,\n            digits[2] || 0,\n            digits[3] || 0,\n            digits[4] || 0,\n            digits[5] || 0,\n        );\n    };\n    resolve({\n        date: to_date(\n            metadata[\"DateTime\"] || metadata[\"DateTimeDigitized\"] ||\n                metadata[\"DateTimeOriginal\"] || metadata[\"GPSDateStamp\"],\n        ),\n        location: (metadata[\"GPSLatitude\"] && metadata[\"GPSLongitude\"] && [\n            [\n                metadata[\"GPSLatitude\"][0], metadata[\"GPSLatitude\"][1],\n                metadata[\"GPSLatitude\"][2], metadata[\"GPSLatitudeRef\"],\n            ],\n            [\n                metadata[\"GPSLongitude\"][0], metadata[\"GPSLongitude\"][1],\n                metadata[\"GPSLongitude\"][2], metadata[\"GPSLongitudeRef\"],\n            ],\n        ]) || null,\n        maker: metadata[\"Make\"] || null,\n        model: metadata[\"Model\"] || null,\n        focal: metadata[\"FocalLength\"] || null,\n        aperture: metadata[\"FNumber\"] || null,\n        shutter: metadata[\"ExposureTime\"] || null,\n        iso: metadata[\"ISOSpeedRatings\"] || null,\n        dimension: (metadata[\"PixelXDimension\"] && metadata[\"PixelYDimension\"] && [\n            metadata[\"PixelXDimension\"],\n            metadata[\"PixelYDimension\"],\n        ]) || null,\n        all: Object.keys(metadata).length === 0 ? null : metadata,\n    });\n}));\n\nconst formatTime = (t) => t?.toLocaleTimeString(\n    \"en-us\",\n    { weekday: \"short\", hour: \"2-digit\", minute: \"2-digit\" },\n);\n\nconst formatDate = (t) => t?.toLocaleDateString(\n    navigator.language,\n    { year: \"numeric\", month: \"short\", day: \"numeric\" },\n);\n\nconst formatCameraSettings = (metadata) => {\n    const str = format(\"model\", metadata);\n    const f = format(\"focal\", metadata);\n    if (!f) return str;\n    return `${str} (${f})`;\n};\n\nconst formatCameraName = (metadata) => {\n    return [\n        format(\"shutter\", metadata),\n        format(\"aperture\", metadata),\n        format(\"iso\", metadata),\n    ].join(\" \").trim() || \"-\";\n};\n\nconst format = (key, metadata) => {\n    if (!metadata[key]) return \"\";\n    switch (key) {\n    case \"focal\":\n        return `${metadata.focal}mm`;\n    case \"iso\":\n        return `ISO${metadata.iso}`;\n    case \"aperture\":\n        return `ƒ${Math.floor(metadata.aperture*10)/10}`;\n    case \"shutter\":\n        if (metadata.shutter > 60) return metadata.shutter+\"m\";\n        else if (metadata.shutter > 1) return metadata.shutter+\"s\";\n        return `1/${Math.floor(metadata.shutter.denominator / metadata.shutter.numerator)}s`;\n    case \"dimension\":\n        if (metadata.dimension.length !== 2 || !metadata.dimension[0] || !metadata.dimension[1]) return \"-\";\n        return metadata.dimension[0]+\"x\"+metadata.dimension[1];\n    default:\n        return metadata[key];\n    }\n};\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_image/pagination.css",
    "content": "/* PAGINATION */\n.component_pager {\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    display: flex;\n    opacity: 0.2;\n    margin: auto;\n    padding: 15px;\n    color: #f2f2f2;\n    z-index: 1;\n}\n.component_pager.left { left: 0; padding-right: 50px; }\n.component_pager.right { right: 0; padding-left: 50px; }\n.touch-yes .component_pager.left { display: none; }\n.touch-yes .component_pager.right { display: none; }\n.component_pager:hover {\n    opacity: 0.9;\n    transition: opacity 0.5s ease;\n}\n.component_pager a { margin: auto; }\n.component_pager a svg {\n    transition: opacity 0.2s ease;\n    width: 37px;\n    padding: 8px;\n    background: var(--dark);\n    border-radius: 50%;\n    margin: auto;\n    opacity: 0.75;\n}\n.touch-no .component_pager a svg:hover {\n    opacity: 1;\n    box-shadow: 0px 0px 5px rgba(255, 255, 255, 0.05);\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_image/pagination.js",
    "content": "import { createFragment } from \"../../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../../lib/rx.js\";\nimport { qs } from \"../../../lib/dom.js\";\nimport { join, forwardURLParams } from \"../../../lib/path.js\";\nimport { animate, slideXOut } from \"../../../lib/animate.js\";\nimport { loadCSS } from \"../../../helpers/loader.js\";\nimport { get as getConfig } from \"../../../model/config.js\";\n\nimport { getCurrentPath, getFilename } from \"../common.js\";\nimport { getMimeType } from \"../mimetype.js\";\nimport { createLink } from \"../../filespage/ctrl_filesystem.js\";\nimport fscache from \"../../filespage/cache.js\";\nimport { sort } from \"../../filespage/helper.js\";\nimport { getState$ as getParams$, init as initParams } from \"../../filespage/state_config.js\";\n\nexport default async function(render, { $img }) {\n    if (window.self !== window.top) return;\n    const lsCache = await fscache().get(join(location, getCurrentPath() + \"/../\"));\n    if (!lsCache) return;\n    const params = await getParams$().pipe(rxjs.first()).toPromise();\n    const state = {\n        prev: null,\n        curr: null,\n        next: null,\n        length: 0,\n    };\n    const files = filterImages(\n        sort(lsCache.files, params[\"sort\"], params[\"order\"]),\n        state,\n    );\n    if (state.length <= 1) return;\n    const $page = createFragment(`\n        <div class=\"component_pager left hidden\">\n            <a data-link>\n                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n                    <polyline points=\"15 18 9 12 15 6\"/>\n                </svg>\n            </a>\n        </div>\n        <div class=\"component_pager right hidden\">\n            <a>\n                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n                    <polyline points=\"9 18 15 12 9 6\"></polyline>\n                </svg>\n            </a>\n        </div>\n    `);\n    if (state.prev !== null) updateDOM({\n        $el: $page.children[0],\n        name: files[state.prev].name,\n        $img,\n    });\n    if (state.next !== null) updateDOM({\n        $el: $page.children[1],\n        name: files[state.next].name,\n        $img,\n    });\n    const $navigation = render($page);\n    initMobileNavigation({ $img, $navigation });\n    initKeyboardNavigation({ $img, $navigation });\n}\n\nfunction filterImages(files, state) {\n    const currentFilename = getFilename();\n    const mimeTypes = getConfig(\"mime\", {});\n    for (let i=0; i<files.length; i++) {\n        const filename = files[i].name;\n        if (!getMimeType(filename, mimeTypes).startsWith(\"image/\")) {\n            continue;\n        }\n        state.length += 1;\n        if (currentFilename === filename) {\n            state.curr = i;\n        } else if (state.curr === null) {\n            state.prev = i;\n        } else {\n            state.next = i;\n            break;\n        }\n    }\n    return files;\n}\n\nfunction updateDOM({ $el, name, $img }) {\n    const $link = qs($el, \"a\");\n    $link.onclick = async(e) => {\n        if (e.target.hasAttribute(\"data-link\")) return;\n        e.preventDefault(); e.stopPropagation();\n        const sgn = $el.classList.contains(\"left\") ? +1 : -1;\n        await animate($img, {\n            keyframes: slideXOut(sgn * 25),\n            time: 100,\n            easing: \"ease-in\",\n        });\n        $link.setAttribute(\"data-link\", \"true\");\n        $link.click();\n    };\n    const { link } = createLink({ name }, join(location, getCurrentPath() + \"/../\"));\n    $link.setAttribute(\"href\", forwardURLParams(link, [\"share\", \"canary\"]));\n    $el.classList.remove(\"hidden\");\n}\n\nfunction initMobileNavigation({ $img, $navigation }) {\n    const state = {\n        active: false,\n        originX: null,\n        originT: null,\n        dist: null,\n    };\n\n    effect(rxjs.fromEvent($img, \"touchstart\", { passive: true }).pipe(rxjs.debounceTime(10), rxjs.tap((event) => {\n        if (event.touches.length !== 1) return;\n        $img.style.transition = \"0s ease transform\";\n        state.active = true;\n        state.originT = performance.now();\n        state.originX = event.touches[0].pageX;\n    })));\n\n    effect(rxjs.fromEvent($img, \"touchmove\", { passive: true }).pipe(rxjs.tap((event) => {\n        if (event.touches.length !== 1 || state.active === false) return;\n        state.dist = event.touches[0].pageX - state.originX;\n        $img.style.transform = `translateX(${state.dist}px)`;\n    })));\n\n    effect(rxjs.fromEvent($img, \"touchend\").pipe(rxjs.tap(async(event) => {\n        if (state.active === false) return;\n        state.active = false;\n\n        const shouldTurnPage = ((distPx, elapsedMs, widthPx) => {\n            const velocity = Math.abs(distPx) / elapsedMs;\n            const fastEnough = velocity > 1;\n            const farEnough = Math.abs(distPx) > widthPx * 0.5;\n            return farEnough || fastEnough;\n        })(state.dist, performance.now() - state.originT, $img.clientWidth);\n        if (!shouldTurnPage) {\n            $img.style.transition = \"0.2s ease transform\";\n            $img.style.transform = \"\";\n            return;\n        }\n\n        let $navlink = null;\n        if (state.dist > 0) $navlink = qs($navigation, \".left a\");\n        else $navlink = qs($navigation, \".right a\");\n        if (!$navlink.hasAttribute(\"href\")) {\n            $img.style.transition = \"0.5s ease transform\";\n            $img.style.transform = \"\";\n            return;\n        }\n\n        $navlink.click();\n        await animate($img, {\n            time: 200,\n            keyframes: [\n                { transform: `translateX(${state.dist}px)`, opacity: 1 },\n                { transform: `translateX(${$img.clientWidth*Math.sign(state.dist)}px)`, opacity: 0 },\n            ]\n        });\n        $img.classList.add(\"hidden\");\n    })));\n}\n\nfunction initKeyboardNavigation({ $img, $navigation }) {\n    effect(rxjs.fromEvent(window, \"keydown\").pipe(rxjs.tap(({ key }) => {\n        let $navlink = null;\n        switch (key) {\n        case \"ArrowLeft\":\n            $navlink = qs($navigation, \".left a\");\n            break;\n        case \"ArrowRight\":\n            $navlink = qs($navigation, \".right a\");\n            break;\n        }\n        if (!$navlink || !$navlink.hasAttribute(\"href\")) return;\n        $navlink.click();\n    })));\n}\n\nexport function init() {\n    return Promise.all([\n        loadCSS(import.meta.url, \"./pagination.css\"),\n        initParams(),\n    ]);\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_image/zoom.js",
    "content": "import rxjs, { effect } from \"../../../lib/rx.js\";\nimport { qs } from \"../../../lib/dom.js\";\n\nexport default function({ $img, $page }) {\n    const $navigation = qs($page, `[data-bind=\"component_navigation\"]`);\n\n    effect(rxjs.merge(...builder({ $img })).pipe(\n        rxjs.tap(() => $navigation.classList.add(\"hidden\")),\n        rxjs.scan((state, { clientX, clientY, moveX, moveY, scale, duration }) => {\n            state.x += moveX ?? 0;\n            state.y += moveY ?? 0;\n            const next = Math.min(20, Math.max(1, state.scale * (scale ?? 1)));\n            if (next > 1) {\n                const rect = $img.getBoundingClientRect();\n                const ox = (clientX ?? rect.left + rect.width / 2) - rect.left;\n                const oy = (clientY ?? rect.top + rect.height / 2) - rect.top;\n                const f = next / state.scale;\n                state.x += (1 - f) * ox;\n                state.y += (1 - f) * oy;\n            } else {\n                state.x = 0;\n                state.y = 0;\n            }\n            state.scale = next;\n            state.duration = duration ?? (next === 1 ? 500 : 0);\n            return state;\n        }, { scale: 1, x: 0, y: 0, duration: 0 }),\n        rxjs.tap(({ scale, x, y, duration }) => {\n            $img.style.transition = `transform ${duration}ms ease`;\n            $img.style.transform = `translate(${x}px,${y}px) scale(${scale})`;\n            if (scale === 1) $navigation.classList.remove(\"hidden\");\n        }),\n    ));\n}\n\nfunction builder({ $img }) {\n    $img.style.transformOrigin = \"0 0\";\n    $img.style.transition = \"\";\n\n    return [\n        // zoom via double click\n        rxjs.fromEvent($img.parentElement, \"dblclick\").pipe(\n            rxjs.filter((e) => e.target === $img),\n            rxjs.map((e) => ({ scale: 2, clientX: e.clientX, clientY: e.clientY })),\n        ),\n        // zoom via scroll wheel\n        rxjs.fromEvent($img.parentElement, \"wheel\").pipe(\n            rxjs.tap((e) => e.preventDefault()),\n            rxjs.map((event) => {\n                let scale = Math.exp(-event.deltaY / 300);\n                if (scale > 1.07) scale = 1.07;\n                else if (scale < 0.93) scale = 0.93;\n                return {\n                    scale,\n                    clientX: event.clientX,\n                    clientY: event.clientY,\n                };\n            }),\n        ),\n        // zoom via keyboard shortcut\n        rxjs.fromEvent(window, \"keydown\").pipe(\n            rxjs.filter(({ key }) => [\"Escape\", \"+\", \"-\", \"ArrowUp\", \"ArrowDown\"].indexOf(key) !== -1),\n            rxjs.withLatestFrom(rxjs.fromEvent(window, \"mousemove\").pipe(\n                rxjs.startWith({ clientX: null, clientY: null }),\n            )),\n            rxjs.map(([{ key }, { clientX, clientY }]) => {\n                let scale = 0;\n                if ([\"+\", \"ArrowUp\"].indexOf(key) !== -1) scale = 3/2;\n                else if ([\"-\", \"ArrowDown\"].indexOf(key) !== -1) scale = 2/3;\n                return { clientX, clientY, scale, duration: 100 };\n            }),\n        ),\n        // pinch zoom\n        rxjs.fromEvent($img.parentElement, \"touchstart\", { passive: false }).pipe(\n            rxjs.filter((e) => e.touches.length === 2),\n            rxjs.switchMap((event) => rxjs.fromEvent($img.parentElement, \"touchmove\", { passive: false }).pipe(\n                rxjs.filter((event) => event.touches.length >= 2),\n                rxjs.tap((event) => event.preventDefault()),\n                rxjs.takeUntil(rxjs.fromEvent(window, \"touchend\")),\n                rxjs.map((event) => ({\n                    clientX: (event.touches[0].pageX + event.touches[1].pageX) / 2,\n                    clientY: (event.touches[0].pageY + event.touches[1].pageY) / 2,\n                    distance: Math.hypot(\n                        event.touches[0].pageX - event.touches[1].pageX,\n                        event.touches[0].pageY - event.touches[1].pageY,\n                    ),\n                })),\n                rxjs.pairwise(),\n                rxjs.map(([prev, curr]) => ({\n                    clientX: curr.clientX,\n                    clientY: curr.clientY,\n                    scale: Math.min(1.15, Math.max(0.85, curr.distance / prev.distance)),\n                    moveX: curr.clientX - prev.clientX,\n                    moveY: curr.clientY - prev.clientY,\n                    duration: 0,\n                })),\n            )),\n        ),\n        // grab and drag\n        rxjs.fromEvent($img.parentElement, \"mousedown\", { passive: false }).pipe(\n            rxjs.filter((e) => e.target === $img && e.button === 0),\n            rxjs.switchMap((down) => {\n                let prev = { x: down.clientX, y: down.clientY, t: down.timeStamp };\n                const move$ = rxjs.fromEvent(window, \"mousemove\").pipe(\n                    rxjs.takeUntil(rxjs.fromEvent(window, \"mouseup\")),\n                    rxjs.map((m) => {\n                        const dx = m.clientX - prev.x;\n                        const dy = m.clientY - prev.y;\n                        const dt = m.timeStamp - prev.t || 1;\n                        prev = { x: m.clientX, y: m.clientY, t: m.timeStamp };\n                        return { moveX: dx, moveY: dy, dt, duration: 0 };\n                    }),\n                    rxjs.tap(() => ($img.style.cursor = \"move\")),\n                    rxjs.share(),\n                );\n                const $inertia = move$.pipe(\n                    rxjs.startWith({ moveX: 0, moveY: 0, dt: 1 }),\n                    rxjs.last(),\n                    rxjs.tap(() => ($img.style.cursor = \"default\")),\n                    rxjs.switchMap(({ moveX, moveY, dt }) => {\n                        const DECAY = 0.8;\n                        const FRAME = 16;\n                        const STOPV = 0.05;\n                        const vx = moveX / dt;\n                        const vy = moveY / dt;\n                        const speed = Math.hypot(vx, vy);\n                        if (speed < STOPV) return rxjs.EMPTY;\n                        const nFrames = Math.ceil(Math.log(STOPV / speed) / Math.log(DECAY));\n                        const gsum = (DECAY * (1 - Math.pow(DECAY, nFrames))) / (1 - DECAY);\n                        return rxjs.of({\n                            moveX: vx * gsum * FRAME,\n                            moveY: vy * gsum * FRAME,\n                            duration: nFrames * FRAME\n                        });\n                    }),\n                );\n                return rxjs.merge(move$, $inertia);\n            }),\n        ),\n    ];\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_image.css",
    "content": ".component_imageviewer .component_image_container,\nbody:not(.dark-mode) .component_imageviewer .component_image_container .fullscreen .component_pager .wrapper > span {\n    background: var(--surface);\n}\n.dark-mode .component_imageviewer .component_image_container {\n    background: #2d2f31;\n}\n\n.component_imageviewer .component_image_container {\n    display: flex;\n    flex-grow: 1;\n    height: 100%;\n    width: 100%;\n    text-align: center;\n    overflow: hidden;\n    height: 100%;\n    box-sizing: border-box;\n}\n.component_imageviewer, .component_imageviewer .images_wrapper {\n    flex: 1;\n    display: flex;\n    overflow: hidden;\n    width: 100%;\n    height: 100%;\n    flex-direction: column;\n    position: relative;\n    justify-content: center;\n}\n.component_imageviewer img.photo {\n    margin: 10px;\n    width: fit-content;\n    max-width: 98%;;\n    min-height: 100px;\n    max-height: 100%;\n    background: var(--dark);\n    box-shadow: rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px;\n    border-radius: 2px;\n    align-self: center;\n}\n.component_imageviewer img.photo.idle {\n    transition: 0.2s ease transform;\n}\n@media screen and (max-width: 500px) {\n    .component_imageviewer img.photo {\n        margin: 7px;\n    }\n}\n\n/* fullscreen mode */\n.component_imageviewer .component_image_container.fullscreen {\n    background: var(--dark);\n}\n.component_imageviewer .component_image_container.fullscreen .component_pager .wrapper > span {\n    background: var(--dark);\n}\n.component_imageviewer .component_image_container.fullscreen img.photo {\n    background: var(--color);\n}\n\n/* image loading spinner */\n.component_imageviewer .component_loader {\n    margin-top: 0;\n}\n.component_imageviewer .component_loader img {\n    width: 40px;\n}\n\n/* error loading image */\n.component_imageviewer .component_filedownloader { margin: 0 auto; }\n\n/* information menu */\n.component_imageviewer .images_aside {\n    flex: 0;\n    text-align: left;\n    width: 0;\n    z-index: 1;\n    min-width: 0px;\n    transition: 0.15s ease min-width;\n    background: #f2f2f2;\n    color: var(--dark);\n}\n.component_imageviewer .images_aside.open {\n    min-width: 300px;\n    transition: 0.5s ease min-width;\n    transition: 0.3s ease min-width;\n}\n@media screen and (max-width: 850px) {\n    .component_imageviewer .images_aside.open {\n        min-width: 250px;\n        font-size: 0.94em;\n    }\n    .component_imageviewer .images_aside.open [data-bind=\"header\"],\n    .component_imageviewer .images_aside.open [data-bind=\"body\"] {\n        padding: 15px 15px 0px 15px;\n    }\n    .component_imageviewer .images_aside [data-bind=\"header\"] .header {\n        padding: 10px 0;\n    }\n}\n@media screen and (max-width: 650px) {\n    .component_imageviewer .images_aside.open {\n        min-width: 200px;\n    }\n}\n@media screen and (max-width: 580px) {\n    .component_imageviewer component-menubar button .component_icon[alt=\"info\"] {\n        display: none;\n    }\n    .component_imageviewer .images_aside.open {\n        width: 0px;\n        min-width: 0;\n    }\n}\n.component_imageviewer .images_aside.open [data-bind=\"body\"] {\n    transform: translateX(0px);\n    opacity: 1;\n}\n.component_imageviewer .images_aside [data-bind=\"body\"] {\n    transition: 0.2s ease opacity, 0.3s ease transform;\n    opacity: 0;\n    transform: translateX(10px);\n    transition-delay: 0.2s;\n}\n.component_imageviewer .images_aside .header {\n    display: flex;\n    line-height: 25px;\n    white-space: nowrap;\n    padding: 20px 0px;\n    font-size: 1.25em;\n}\n.component_imageviewer .images_aside .header .component_icon {\n    height: 18px;\n    float: right;\n    cursor: pointer;\n    padding: 5px;\n    margin: -5px -5px 0 0;\n}\n.component_imageviewer .images_aside .header .component_icon:hover {\n    background: #0000000a;\n    border-radius: 50%;\n}\n.component_imageviewer .images_aside.open [data-bind=\"header\"],\n.component_imageviewer .images_aside [data-bind=\"body\"] {\n    padding: 10px 20px 0px 20px;\n}\n.component_imageviewer .images_aside [data-bind=\"body\"] .content_box {\n    clear: both;\n    opacity: 0.85;\n    margin-bottom: 20px;\n}\n.component_imageviewer .images_aside [data-bind=\"body\"] .content_box > div {\n    width: calc(100% - 40px);\n}\n.component_imageviewer .images_aside [data-bind=\"body\"] .content_box .component_icon {\n    height: 30px;\n    width: 30px;\n    float: left;\n    padding: 5px 10px 5px 0;\n}\n.component_imageviewer .images_aside [data-bind=\"body\"] .component_mapshot {\n    margin-bottom: 10px;\n}\n.component_imageviewer .images_aside .meta_key {\n    display: flex;\n    justify-content: space-between;\n    margin: 5px 0;\n    border-top: 1px solid #0000000a;\n    padding-top: 5px;\n    text-align: right;\n    font-size: 0.85em;\n}\n.component_imageviewer .images_aside .meta_key .title {\n    margin-right: 5px;\n}\n.component_imageviewer .images_aside .meta_key .value {\n    color: var(--light);\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_image.d.ts",
    "content": "interface Window {\n    EXIF: {\n        getAllTags: (any) => object;\n        getData: (HTMLElement, any) => void;\n    };\n}\n\nexport default function(any): void;"
  },
  {
    "path": "public/assets/pages/viewerpage/application_image.js",
    "content": "import { createElement, createRender, onDestroy } from \"../../lib/skeleton/index.js\";\nimport { toHref } from \"../../lib/skeleton/router.js\";\nimport rxjs, { effect, onLoad, onClick } from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { animate } from \"../../lib/animate.js\";\nimport { extname } from \"../../lib/path.js\";\nimport { qs, safe } from \"../../lib/dom.js\";\nimport { get as getConfig } from \"../../model/config.js\";\nimport { load as loadPlugin } from \"../../model/plugin.js\";\nimport { Chromecast } from \"../../model/chromecast.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\nimport { createLoader } from \"../../components/loader.js\";\nimport notification from \"../../components/notification.js\";\nimport t from \"../../locales/index.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport componentInformation, { init as initInformation } from \"./application_image/information.js\";\nimport componentPagination, { init as initPagination } from \"./application_image/pagination.js\";\nimport componentZoom from \"./application_image/zoom.js\";\nimport ctrlDownloader, { init as initDownloader } from \"./application_downloader.js\";\n\nimport { renderMenubar, buttonDownload, buttonFullscreen } from \"./component_menubar.js\";\n\nclass IImage {\n    getSRC() { throw new Error(\"NOT_IMPLEMENTED\"); }\n}\n\nexport default function(render, { getFilename, getDownloadUrl, mime, hasMenubar = true, acl$ }) {\n    const $page = createElement(`\n        <div class=\"component_imageviewer\">\n            <component-menubar filename=\"${safe(getFilename())}\" class=\"${!hasMenubar && \"hidden\"}\"></component-menubar>\n            <div class=\"component_image_container\">\n                <div class=\"images_wrapper no-select\">\n                    <img class=\"photo idle hidden\" draggable=\"false\" />\n                    <div data-bind=\"component_navigation\"></div>\n                </div>\n                <div class=\"images_aside scroll-y\"></div>\n            </div>\n        </div>\n    `);\n    render($page);\n\n    const $imgContainer = qs($page, \".images_wrapper\");\n    const $photo = qs($page, \"img.photo\");\n    const $menubar = qs($page, \"component-menubar\");\n    const removeLoader = createLoader($imgContainer);\n    const load$ = new rxjs.BehaviorSubject(null);\n    const toggleInfo = ($button) => {\n        const $aside = qs($page, \".images_aside\");\n        $aside.classList.toggle(\"open\");\n        $button.setAttribute(\"aria-expanded\", $aside.classList.contains(\"open\") ? \"true\" : \"false\");\n        componentInformation(createRender(qs($page, \".images_aside\")), { toggle: toggleInfo, load$ });\n    };\n\n    renderMenubar(\n        $menubar,\n        buttonDownload(getFilename(), getDownloadUrl()),\n        buttonFullscreen(qs($page, \".component_image_container\")),\n        mime === \"image/jpeg\" && buttonInfo({ toggle: toggleInfo }),\n        [\"image/jpeg\", \"image/png\"].indexOf(mime) !== -1 && buttonChromecast(getFilename(), getDownloadUrl()),\n    );\n\n    effect(rxjs.from(loadPlugin(mime)).pipe(\n        rxjs.mergeMap(async(loader) => {\n            let src = `${getDownloadUrl()}&size=${window.innerWidth}`;\n            if (loader) {\n                const { response } = await ajax({ url: getDownloadUrl(), responseType: \"arraybuffer\" }).toPromise();\n                const img = new (await loader(IImage, { mime, $menubar, getFilename, getDownloadUrl }))({ $photo });\n                src = await img.getSRC(response);\n            }\n            $photo.setAttribute(\"src\", src);\n            await onLoad($photo).toPromise();\n        }),\n        rxjs.tap(() => load$.next($photo)),\n        removeLoader,\n        rxjs.tap(() => {\n            const cancel = animate($photo, {\n                onEnter: () => $photo.classList.remove(\"hidden\"),\n                onExit: async() => (await cancel)(),\n                time: 300,\n                easing: \"cubic-bezier(.51,.92,.24,1.15)\",\n                keyframes: [\n                    { opacity: 0, transform: \"scale(.97)\" },\n                    { opacity: 1 },\n                    { opacity: 1, transform: \"scale(1)\" },\n                ],\n            });\n        }),\n        rxjs.catchError((err) => {\n            if (err.target instanceof HTMLElement && err.type === \"error\") {\n                return rxjs.of(initDownloader()).pipe(\n                    removeLoader,\n                    rxjs.mergeMap(() => {\n                        load$.error(err);\n                        ctrlDownloader(createRender(qs($page, \".images_wrapper\")), { acl$, getFilename, getDownloadUrl, hasMenubar: false });\n                        return rxjs.EMPTY;\n                    }),\n                );\n            }\n            return ctrlError()(err);\n        }),\n    ));\n\n    effect(load$.pipe(\n        rxjs.first(),\n        rxjs.tap(() => {\n            componentZoom({ $img: $photo, $page, $menubar, load$ });\n            componentPagination(createRender(qs($page, \"[data-bind=\\\"component_navigation\\\"]\")), { $img: $photo });\n        }),\n    ));\n}\n\nexport function init() {\n    return Promise.all([\n        loadCSS(import.meta.url, \"./application_image.css\"),\n        loadCSS(import.meta.url, \"./component_menubar.css\"),\n        initPagination(), initInformation(), Chromecast.init(),\n    ]);\n}\n\nfunction buttonInfo({ toggle }) {\n    const $el = createElement(`\n        <button aria-controls=\"pane-info\" aria-expanded=\"false\">\n            <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgwLjg4MiwwLDAsMC44ODIsNS45LDUuOSkiPgogICAgPHBhdGggc3R5bGU9ImZpbGw6I2YyZjJmMjtmaWxsLW9wYWNpdHk6MSIgZD0ibSA2Mi4xNjIsMCBjIDYuNjk2LDAgMTAuMDQzLDQuNTY3IDEwLjA0Myw5Ljc4OSAwLDYuNTIyIC01LjgxNCwxMi41NTUgLTEzLjM5MSwxMi41NTUgLTYuMzQ0LDAgLTEwLjA0NSwtMy43NTIgLTkuODY5LC05Ljk0NyBDIDQ4Ljk0NSw3LjE3NiA1My4zNSwwIDYyLjE2MiwwIFogTSA0MS41NDMsMTAwIGMgLTUuMjg3LDAgLTkuMTY0LC0zLjI2MiAtNS40NjMsLTE3LjYxNSBsIDYuMDcsLTI1LjQ1NyBjIDEuMDU3LC00LjA3NyAxLjIzLC01LjcwNyAwLC01LjcwNyAtMS41ODgsMCAtOC40NTEsMi44MTYgLTEyLjUxLDUuNTkgTCAyNyw1Mi40MDYgQyAzOS44NjMsNDEuNDggNTQuNjYyLDM1LjA3MiA2MS4wMDQsMzUuMDcyIGMgNS4yODUsMCA2LjE2OCw2LjM2MSAzLjUyNSwxNi4xNDggTCA1Ny41OCw3Ny45OCBjIC0xLjIzNCw0LjcyOSAtMC43MDMsNi4zNTkgMC41MjcsNi4zNTkgMS41ODYsMCA2Ljc4NywtMS45NjMgMTEuODk2LC02LjA0MSBMIDczLDgyLjM3NyBDIDYwLjQ4OCw5NS4xIDQ2LjgzLDEwMCA0MS41NDMsMTAwIFoiIC8+CiAgPC9nPgo8L3N2Zz4K\" alt=\"info\">\n        </button>\n    `);\n    effect(rxjs.merge(\n        onClick($el),\n        rxjs.fromEvent(window, \"keydown\").pipe(rxjs.filter((e) => e.key === \"i\")),\n    ).pipe(rxjs.mapTo($el), rxjs.tap(toggle)));\n    return $el;\n}\n\nfunction buttonChromecast(filename, downloadURL) {\n    const context = Chromecast.context();\n    if (!context) return;\n\n    const chromecastSetup = (event) => {\n        switch (event.sessionState) {\n        case window.cast.framework.SessionState.SESSION_STARTED:\n            chromecastLoader();\n            break;\n        }\n    };\n    const chromecastLoader = () => {\n        const session = Chromecast.session();\n        if (!session) return;\n\n        const link = Chromecast.createLink(\"/\" + toHref(downloadURL));\n        const media = new window.chrome.cast.media.MediaInfo(\n            link,\n            getConfig(\"mime\", {})[extname(filename)],\n        );\n        media.metadata = new window.chrome.cast.media.PhotoMediaMetadata();\n        media.metadata.title = filename;\n        media.metadata.images = [\n            new window.chrome.cast.Image(location.origin + \"/\" + toHref(\"/assets/icons/photo.png\")),\n        ];\n        try {\n            const req = Chromecast.createRequest(media);\n            session.loadMedia(req);\n        } catch (err) {\n            console.error(err);\n            notification.error(t(\"Cannot establish a connection\"));\n        }\n    };\n\n    context.addEventListener(\n        window.cast.framework.CastContextEventType.SESSION_STATE_CHANGED,\n        chromecastSetup,\n    );\n    onDestroy(() => context.removeEventListener(\n        window.cast.framework.CastContextEventType.SESSION_STATE_CHANGED,\n        chromecastSetup,\n    ));\n\n    const media = Chromecast.media();\n    if (media && media.media && media.media.mediaCategory === \"IMAGE\") chromecastLoader();\n\n    return document.createElement(\"google-cast-launcher\");\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_map.css",
    "content": "#map .leaflet-control-attribution { display: none; }\n#map { height: 100%; z-index: 2; }\n\n#map .leaflet-popup-content-wrapper {\n    border-radius: 2px;\n}\n#map .leaflet-popup-content-wrapper, #map .leaflet-popup-tip {\n    box-shadow: 0 3px 14px rgba(0,0,0,0.15);\n}\n#map .leaflet-control-layers-list { user-select: none; }\n\n#map .leaflet-control-measure .leaflet-control-measure-toggle { opacity: 0.6; }\n#map .leaflet-control-scale {\n    margin-left: 10px;\n    margin-bottom: 10px;\n}\n#map .leaflet-popup-content {\n    margin: 10px 20px 10px 15px;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_map.d.ts",
    "content": "interface Window {\n    L: any;\n}\n\nexport default function(any): void;"
  },
  {
    "path": "public/assets/pages/viewerpage/application_map.js",
    "content": "import { createElement, nop } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport { qs, safe } from \"../../lib/dom.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { load as loadPlugin } from \"../../model/plugin.js\";\nimport { loadCSS, loadJS } from \"../../helpers/loader.js\";\nimport { createLoader } from \"../../components/loader.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport componentDownloader, { init as initDownloader } from \"./application_downloader.js\";\nimport { renderMenubar, buttonDownload } from \"./component_menubar.js\";\n\nclass IMap {\n    toGeoJSON() { throw new Error(\"NOT_IMPLEMENTED\"); }\n}\n\nexport default async function(render, { mime, getDownloadUrl = nop, getFilename = nop, acl$ = rxjs.EMPTY }) {\n    const $page = createElement(`\n        <div class=\"component_map\">\n            <component-menubar filename=\"${safe(getFilename())}\"></component-menubar>\n            <div id=\"map\"></div>\n        </div>\n    `);\n    render($page);\n    const $menubar = renderMenubar(\n        qs($page, \"component-menubar\"),\n        buttonDownload(getFilename(), getDownloadUrl()),\n    );\n    const map = window.L.map(\"map\");\n    const removeLoader = createLoader(qs($page, \"#map\"));\n    await effect(ajax({ url: getDownloadUrl(), responseType: \"arraybuffer\" }).pipe(\n        rxjs.mergeMap(async({ response }) => {\n            const loader = await loadPlugin(mime);\n            if (!loader) {\n                try {\n                    loadGeoJSON(map, JSON.parse(new TextDecoder().decode(response)));\n                    window.L.tileLayer(\"https://tile.openstreetmap.org/{z}/{x}/{y}.png\", { maxZoom: 21 }).addTo(map);\n                }\n                catch (err) { componentDownloader(render, { mime, acl$, getFilename, getDownloadUrl }); }\n                return rxjs.EMPTY;\n            }\n            const mapImpl = new (await loader(IMap, { mime, getDownloadUrl, getFilename, $menubar }))(response, {\n                map, $page, L: window.L,\n            });\n            loadGeoJSON(map, await mapImpl.toGeoJSON());\n        }),\n        removeLoader,\n        rxjs.catchError(ctrlError()),\n    ));\n}\n\nexport async function init($root) {\n    const priors = $root ? [\n        $root.classList.add(\"component_page_viewerpage\"),\n        loadCSS(import.meta.url, \"./component_menubar.css\"),\n        loadCSS(import.meta.url, \"../ctrl_viewerpage.css\"),\n    ] : [];\n    await Promise.all([\n        loadJS(import.meta.url, \"../../lib/vendor/leaflet/leaflet.js\"),\n        loadCSS(import.meta.url, \"../../lib/vendor/leaflet/leaflet.css\"),\n        loadCSS(import.meta.url, \"./application_map.css\"),\n        initDownloader(),\n        ...priors,\n    ]);\n}\n\nfunction loadGeoJSON(map, content) {\n    const overlay = { global: window.L.layerGroup([]) };\n    let n = 0;\n    const geojson = window.L.geoJSON(content, {\n        style: (feature) => {\n            const style = { color: \"#3388ff\", weight: 3 };\n            if (feature.properties.color) style.color = feature.properties.color;\n            if (feature.properties.weight) style.weight = feature.properties.weight;\n            return style;\n        },\n        pointToLayer: (feature, latlng) => {\n            return window.L.circleMarker(latlng, {\n                radius: 6,\n                fillColor: \"#e2e2e2\",\n                color: \"#000000\",\n                opacity: 0.5,\n                weight: 1,\n                fillOpacity: 0.3\n            });\n        },\n        onEachFeature: (feature, shape) => {\n            n += 1;\n            if (n > 10000) return;\n            const { group = \"global\", ...props } = feature.properties || {};\n            const featureObject = (function() {\n                if (props[\"name\"]) shape = shape.bindPopup(props[\"name\"]);\n                switch (props[\"popup::type\"]) {\n                case \"text\":\n                    shape = shape.bindPopup(props[\"popup::content\"]);\n                    break;\n                case \"html\":\n                    shape = shape.bindPopup((function() {\n                        const $richLabel = window.L.DomUtil.create(\"div\", \"\");\n                        $richLabel.innerHTML = props[\"popup::content\"];\n                        if ($richLabel.querySelector(\"script\")) $richLabel.innerHTML = `__BLOCKED_CONTENT__`;\n                        return $richLabel;\n                    })(), {\n                        className: \"leaflet-measure-resultpopup\",\n                        autoPanPadding: [10, 10],\n                    });\n                    break;\n                }\n                return shape;\n            })(feature.geometry || {});\n            if (!featureObject) return;\n            if (!overlay[group]) overlay[group] = window.L.layerGroup([]);\n            overlay[group].addLayer(featureObject);\n        }\n    });\n\n    // center map around bounds\n    try {\n        const center = geojson.getBounds().getCenter();\n        const zoom = (function(p1, p2) {\n            const distance = Math.log10(1 + Math.abs(p1.lat - p2.lat) + Math.abs(p1.lng - p2.lng));\n            const [a, b] = distance > 0.5 ? [-4, 11] : [-15, 15];\n            return Math.floor(a * distance + b);\n        })(geojson.getBounds().getNorthEast(), geojson.getBounds().getSouthWest());\n        map.setView([center.lat, center.lng], zoom);\n    } catch (err) {\n        map.setView([0, 0], 2);\n    }\n\n    // display everything\n    Object.keys(overlay).forEach((key) => overlay[key].addTo(map));\n    delete overlay[\"global\"];\n    if (Object.keys(overlay).length > 0) window.L.control.layers({}, overlay).addTo(map);\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_pdf.css",
    "content": ".component_page_viewerpage .component_pdfviewer {\n    background: var(--surface);\n    text-align:center;\n}\n.component_page_viewerpage .component_pdfviewer [data-bind=\"pdf\"] {\n    overflow-y: scroll;\n    flex: 1 1 auto;\n}\n.component_page_viewerpage .component_pdfviewer [data-bind=\"pdf\"] embed {\n    width:100%;\n    height:100%;\n}\n.component_page_viewerpage .component_pdfviewer [data-bind=\"pdf\"] component-icon[name=\"loading\"] {\n    padding-top: 75px;\n    display: block;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_pdf.d.ts",
    "content": "interface Window {\n    pdfjsLib: {\n        getDocument: (url: string) => { promise: Promise<any> };\n        GlobalWorkerOptions: {\n            workerSrc: string;\n        };\n        // Add other properties and methods of pdfjsLib as needed\n    };\n    env?: string;\n    chrome: object;\n}\n\nexport default function(any): void;"
  },
  {
    "path": "public/assets/pages/viewerpage/application_pdf.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect, onLoad } from \"../../lib/rx.js\";\nimport { qs, safe } from \"../../lib/dom.js\";\nimport { createLoader } from \"../../components/loader.js\";\nimport { loadCSS, loadJS } from \"../../helpers/loader.js\";\nimport { join } from \"../../lib/path.js\";\nimport ctrlError from \"../ctrl_error.js\";\n\nimport { transition } from \"./common.js\";\nimport { renderMenubar, buttonDownload } from \"./component_menubar.js\";\n\nimport \"../../components/icon.js\";\n\nconst hasNativePDF = \"application/pdf\" in window.navigator.mimeTypes && !!window.chrome;\n\nexport default async function(render, opts) {\n    const ctrl = hasNativePDF ? ctrlPDFNative : ctrlPDFJs;\n    ctrl(render, opts);\n}\n\nfunction ctrlPDFNative(render, { getFilename, getDownloadUrl }) {\n    const $page = createElement(`\n        <div class=\"component_pdfviewer\">\n            <component-menubar filename=\"${safe(getFilename())}\"></component-menubar>\n            <div data-bind=\"pdf\">\n                <embed\n                    class=\"hidden\"\n                    src=\"${safe(getDownloadUrl())}#toolbar=0\"\n                    type=\"application/pdf\"\n                />\n            </div>\n        </div>\n    `);\n    render($page);\n    renderMenubar(qs($page, \"component-menubar\"), buttonDownload(getFilename(), getDownloadUrl()));\n\n    const removeLoader = createLoader(qs($page, `[data-bind=\"pdf\"]`));\n    effect(onLoad(qs($page, \"embed\")).pipe(\n        removeLoader,\n        rxjs.tap(($embed) => $embed.classList.remove(\"hidden\")),\n        rxjs.tap(($embed) => transition($embed)),\n        rxjs.catchError(ctrlError()),\n    ));\n}\n\nasync function ctrlPDFJs(render, { getFilename, getDownloadUrl }) {\n    const $page = createElement(`\n        <div class=\"component_pdfviewer\">\n            <component-menubar filename=\"${safe(getFilename())}\"></component-menubar>\n            <div data-bind=\"pdf\"></div>\n        </div>\n    `);\n    render($page);\n\n    const $container = qs($page, `[data-bind=\"pdf\"]`);\n    const createBr = () => $container.appendChild(createElement(`<div style=\"height:${document.body.clientWidth > 600 ? 20 : 5}px\">&nbsp;</div>`));\n    const removeLoader = createLoader($container);\n    const base = qs(document.head, \"base\").getAttribute(\"href\");\n    effect(rxjs.from(window.pdfjsLib.getDocument(base + getDownloadUrl()).promise).pipe(\n        removeLoader,\n        rxjs.mergeMap(async(pdf) => {\n            createBr();\n            for (let i=0; i<pdf.numPages; i++) {\n                const page = await pdf.getPage(i + 1);\n                const marginLeftRight = (document.body.clientWidth > 600 ? 50 : 15);\n                const ratio = window.devicePixelRatio || 1;\n                const viewport = page.getViewport({\n                    scale: Math.min(\n                        Math.max(\n                            document.body.clientWidth - marginLeftRight,\n                            0,\n                        ),\n                        800,\n                    ) / page.getViewport({ scale: 1 / ratio }).width,\n                });\n                const $canvas = document.createElement(\"canvas\");\n                $canvas.height = viewport.height;\n                $canvas.width = viewport.width;\n                $canvas.style.width = Math.floor(viewport.width / ratio) + \"px\";\n                $canvas.style.height = Math.floor(viewport.height / ratio) + \"px\";\n                $container.appendChild($canvas);\n                if (window.env === \"test\") $canvas.getContext = () => null;\n                await page.render({\n                    canvasContext: $canvas.getContext(\"2d\"),\n                    viewport,\n                });\n                await new Promise((done) => window.requestAnimationFrame(done));\n            }\n            createBr();\n        }),\n        rxjs.catchError(ctrlError()),\n    ));\n}\n\nexport function init() {\n    const deps = [\n        loadCSS(import.meta.url, \"./application_pdf.css\"),\n    ];\n    if (!hasNativePDF) {\n        deps.push(loadJS(import.meta.url, \"../../lib/vendor/pdfjs/pdf.js\", { type: \"module\" }));\n        deps.push(loadJS(import.meta.url, \"../../lib/vendor/pdfjs/pdf.worker.js\", { type: \"module\" }).then(() => {\n            window.pdfjsLib.GlobalWorkerOptions.workerSrc = join(import.meta.url, \"../../lib/vendor/pdfjs/pdf.worker.js\");\n        }));\n    }\n    return Promise.all(deps);\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_skeleton.css",
    "content": ".component_skeletonviewer {\n    background: var(--bg-color);\n}\n.component_skeleton_container {\n    height: 100%;\n    width: 100%;\n    position: relative;\n}\n.component_skeleton_container > .component_loader {\n    width: 100%;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_skeleton.js",
    "content": "import { createElement, createRender } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport { qs, safe } from \"../../lib/dom.js\";\nimport { load as loadPlugin } from \"../../model/plugin.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\nimport { createLoader } from \"../../components/loader.js\";\nimport ctrlError from \"../ctrl_error.js\";\nimport componentDownloader, { init as initDownloader } from \"./application_downloader.js\";\nimport { renderMenubar } from \"./component_menubar.js\";\n\nexport default function(render, { mime, getFilename, getDownloadUrl, acl$, hasMenubar = true }) {\n    const $page = createElement(`\n        <div class=\"component_skeletonviewer\">\n            <component-menubar filename=\"${safe(getFilename())}\" class=\"${!hasMenubar && \"hidden\"}\"></component-menubar>\n            <div class=\"component_skeleton_container flex\"></div>\n        </div>\n    `);\n    render($page);\n    const $menubar = renderMenubar(qs($page, \"component-menubar\"));\n    const $container = qs($page, \".component_skeleton_container\");\n    const removeLoader = createLoader($container);\n    effect(rxjs.from(loadPlugin(mime)).pipe(\n        rxjs.mergeMap((loader) => {\n            const opts = { mime, acl$, getFilename, getDownloadUrl };\n            if (!loader) {\n                componentDownloader(render, opts);\n                return rxjs.EMPTY;\n            }\n            return rxjs.from(loader(createRender($container), { $menubar, ...opts }));\n        }),\n        removeLoader,\n        rxjs.catchError(ctrlError()),\n    ));\n}\n\nexport function init() {\n    return Promise.all([\n        loadCSS(import.meta.url, \"./application_skeleton.css\"),\n        initDownloader(),\n    ]);\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_table.css",
    "content": ".component_tableviewer {\n    height: 100%;\n    background: var(--bg-color);\n}\n.component_table_container:has(.component_loader) .table {\n    display: none;\n}\n.component_tableviewer component-menubar input[type=\"search\"] {\n    background: rgba(255, 255, 255, 0.05);\n    border: none;\n    color: inherit;\n    border-radius: 3px;\n    margin-right: 5px;\n    margin-top: 2px;\n    padding-left: 3px;\n}\n.component_tableviewer input[type=\"search\"]::-webkit-search-cancel-button {\n  appearance: none;\n}\n.component_tableviewer .component_table_container {\n    height: 100%;\n    position: relative;\n}\n.component_tableviewer .table {\n    font-family: monospace;\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    white-space: nowrap;\n    display: block;\n    font-size: 13px;\n    display: flex;\n    flex-direction: column;\n}\n.component_tableviewer .table .thead {\n    overflow-x: scroll;\n    scrollbar-width: none;\n    border-bottom: 1px solid var(--border);\n}\n.component_tableviewer .table .tbody {\n    flex: 1;\n    overflow-y: auto;\n    overflow-x: auto;\n}\n\n.component_tableviewer .thead > div, .component_tableviewer .tbody > div {\n    display: block;\n}\n\n.component_tableviewer .table .thead .th {\n    padding: 5px 10px;\n    font-weight: normal;\n    display: inline-block;\n    position: sticky;\n    top: 0;\n    opacity: 0.8;\n    text-transform: capitalize;\n}\n.component_tableviewer .table .thead .th img {\n    height: 13px;\n    border-radius: 5px;\n    position: absolute;\n    margin-top: 4px;\n    right: 7px;\n    cursor: pointer;\n    transition: ease transform 0.2s;\n    filter: contrast(1) invert(0);\n}\n.dark-mode .component_tableviewer .table .thead .th img {\n    filter: contrast(1) invert(1);\n}\n\n.component_tableviewer .table .tbody .td {\n    border-top: 1px solid var(--border);\n    border-bottom: 1px solid var(--border);\n    margin-top: -1px;\n    display: inline-block;\n    padding: 5px 10px;\n    line-height: 17px;\n    height: 17px;\n    vertical-align: middle;\n    cursor: pointer;\n}\n.component_tableviewer .table .tbody .td .empty {\n    opacity: 0.4;\n}\n\n.component_tableviewer .table .tbody .tr:hover .td {\n    background: var(--border);\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_table.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport { qs, qsa, safe } from \"../../lib/dom.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\nimport t from \"../../locales/index.js\";\nimport { createLoader } from \"../../components/loader.js\";\nimport { get as getPlugin } from \"../../model/plugin.js\";\n\nimport ctrlDownloader, { init as initDownloader } from \"./application_downloader.js\";\nimport { renderMenubar, buttonDownload } from \"./component_menubar.js\";\nimport { transition } from \"./common.js\";\n\nconst MAX_ROWS = 200;\n\nclass ITable {\n    contructor() {}\n    getHeader() { throw new Error(\"NOT_IMPLEMENTED\"); }\n    getBody() { throw new Error(\"NOT_IMPLEMENTED\"); }\n}\n\nexport default async function(render, { mime, getDownloadUrl, getFilename, hasMenubar = true, acl$ = rxjs.EMPTY, module = null }) {\n    const $page = createElement(`\n        <div class=\"component_tableviewer\">\n            <component-menubar filename=\"${safe(getFilename())}\" class=\"${!hasMenubar && \"hidden\"}\"></component-menubar>\n            <div class=\"component_table_container\">\n                <table class=\"table\">\n                    <thead class=\"thead\"></thead>\n                    <tbody class=\"tbody\"></tbody>\n                </div>\n            </div>\n        </div>\n    `);\n    render($page);\n    const $menubar = renderMenubar(\n        qs($page, \"component-menubar\"),\n        buttonDownload(getFilename(), getDownloadUrl()),\n    );\n    const $dom = {\n        thead: qs($page, \".thead\"),\n        tbody: qs($page, \".tbody\"),\n    };\n    const removeLoader = createLoader(qs($page, \".component_table_container\"));\n    const padding = 10;\n    const STATE = {\n        header: {},\n        body: [],\n        rows: [],\n    };\n\n    // feature: initial render\n    const init$ = ajax({ url: getDownloadUrl(), responseType: \"arraybuffer\" }).pipe(\n        rxjs.mergeMap(async({ response }) => {\n            const loader = getPlugin(mime);\n            if (!loader) throw new TypeError(`unsupported mimetype \"${mime}\"`);\n            const [, url] = loader;\n            const m = module || (await import(url)).default;\n            let table = new (await m(ITable, { $menubar }))(response);\n            if (typeof table.then === \"function\") table = await table;\n            STATE.header = table.getHeader();\n            STATE.body = table.getBody();\n            STATE.rows = STATE.body;\n        }),\n        removeLoader,\n        rxjs.tap(() => {\n            buildHead(STATE, $dom, padding);\n            buildRows(STATE.rows.slice(0, MAX_ROWS), STATE.header, $dom.tbody, padding, true, false);\n        }),\n        rxjs.catchError((err) => rxjs.from(initDownloader()).pipe(\n            rxjs.tap(() => ctrlDownloader(render, { acl$, getFilename, getDownloadUrl })),\n            rxjs.tap(() => console.log(\"cannot open file\", err)),\n            rxjs.mergeMap(() => rxjs.EMPTY),\n        )),\n        rxjs.share(),\n    );\n    effect(init$);\n\n    // feature: search\n    const $search = createElement(`<input type=\"search\" placeholder=\"search\">`);\n    effect(init$.pipe(\n        rxjs.tap(() => $menubar.add($search)),\n        rxjs.mergeMap(() => rxjs.fromEvent($search, \"keyup\").pipe(rxjs.debounce((e) => {\n            if (!e.target.value) return rxjs.of(null);\n            return rxjs.timer(300);\n        }))),\n        rxjs.tap((e) => {\n            const terms = e.target.value.toLowerCase().trim().split(\" \");\n            $dom.tbody.scrollTo(0, 0);\n            if (terms === \"\") STATE.rows = STATE.body;\n            else STATE.rows = STATE.body.filter((row) => {\n                const line = Object.values(row).join(\"\").toLowerCase();\n                for (let i=0; i<terms.length; i++) {\n                    if (line.indexOf(terms[i]) === -1) {\n                        return false;\n                    }\n                }\n                return true;\n            });\n            buildRows(STATE.rows.slice(0, MAX_ROWS), STATE.header, $dom.tbody, padding, false, true);\n        }),\n    ));\n\n    // feature: fixed header scroll along\n    effect(rxjs.fromEvent($dom.tbody, \"scroll\").pipe(\n        rxjs.tap(() => $dom.thead.scrollTo($dom.tbody.scrollLeft, 0))\n    ));\n    effect(rxjs.fromEvent($dom.thead, \"scroll\").pipe(\n        rxjs.tap(() => $dom.tbody.scrollTo($dom.thead.scrollLeft, $dom.tbody.scrollTop))\n    ));\n\n    // feature: infinite scroll\n    effect(rxjs.fromEvent($dom.tbody, \"scroll\").pipe(\n        rxjs.mergeMap(async(e) => {\n            const scrollBottom = e.target.scrollHeight - (e.target.scrollTop + e.target.clientHeight);\n            if (scrollBottom > 0) return;\n            else if (STATE.rows.length <= MAX_ROWS) return;\n            else if (STATE.rows.length <= $dom.tbody.children.length) return;\n\n            const current = $dom.tbody.children.length;\n            const newRows = STATE.rows.slice(current, current + 10);\n            buildRows(newRows, STATE.header, $dom.tbody, padding, false, false);\n        }),\n    ));\n\n    // feature: make the last column to always fit the viewport\n    effect(rxjs.merge(\n        init$,\n        init$.pipe(\n            rxjs.mergeMap(() => rxjs.fromEvent(window, \"resize\")),\n            rxjs.debounce((e) => e[\"debounce\"] === false ? rxjs.of(null) : rxjs.timer(100)),\n        ),\n    ).pipe(\n        rxjs.tap(() => resizeLastColumnIfNeeded({\n            $target: $dom.thead,\n            $childs: qs($dom.thead, \".tr\"),\n            padding,\n        })),\n        rxjs.tap(() => qsa($dom.tbody, \".tr\").forEach(($tr) => resizeLastColumnIfNeeded({\n            $target: $dom.tbody,\n            $childs: $tr,\n            padding,\n        }))),\n    ));\n}\n\nexport function init($root) {\n    const priors = ($root && [\n        $root.classList.add(\"component_page_viewerpage\"),\n        loadCSS(import.meta.url, \"./component_menubar.css\"),\n        loadCSS(import.meta.url, \"../ctrl_viewerpage.css\"),\n    ]);\n    return Promise.all([\n        loadCSS(import.meta.url, \"./application_table.css\"),\n        ...priors,\n    ]);\n}\n\nasync function buildRows(rows, legends, $tbody, padding, isInit, withClear) {\n    if (withClear) $tbody.innerHTML = \"\";\n    for (let i=0; i<rows.length; i++) {\n        const obj = rows[i];\n        if (!obj) break;\n        const $tr = createElement(`<div class=\"tr\"></div>`);\n        legends.forEach(({ name, size }, i) => {\n            const $col = createElement(`<div class=\"${withCenter(\"td ellipsis\", size, i === legends.length -1)}\" style=\"${styleCell(size, name, padding)}\"></div>`);\n            $col.setAttribute(\"data-column\", name);\n            $col.setAttribute(\"title\", obj[name]);\n            if (obj[name]) $col.textContent = obj[name];\n            else $col.appendChild(createElement(`<span class=\\\"empty\\\">-</span>`));\n            $tr.appendChild($col);\n        });\n        $tbody.appendChild($tr);\n    }\n    $tbody.style.opacity = \"0\";\n    if (rows.length === 0) $tbody.appendChild(createElement(`\n        <h3 class=\"center no-select\" style=\"opacity:0.2; margin-top:30px\">\n            ${t(\"Empty\")}\n        </h3>\n    `));\n    if (!isInit) {\n        const e = new Event(\"resize\");\n        e[\"debounce\"] = false;\n        window.dispatchEvent(e);\n        await new Promise(requestAnimationFrame);\n    }\n    $tbody.style.opacity = \"1\";\n    if (isInit) transition($tbody.parentElement);\n}\n\nfunction buildHead(STATE, $dom, padding) {\n    const $tr = createElement(`<div class=\"tr\"></div>`);\n    STATE.header.forEach(({ name, size }, i) => {\n        const $th = createElement(`\n            <div class=\"${withCenter(\"th ellipsis\", size, i === STATE.header.length - 1)}\" style=\"${styleCell(size, name, padding)}\"></div>\n        `);\n        $th.setAttribute(\"title\", name);\n        $th.textContent = name;\n        $th.appendChild(createElement(`<img class=\"no-select\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgPHBhdGggc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MC41MzMzMzMyMSIgZD0ibSA3LjcwNSw4LjA0NSA0LjU5LDQuNTggNC41OSwtNC41OCAxLjQxLDEuNDEgLTYsNiAtNiwtNiB6IiAvPgogIDxwYXRoIGZpbGw9Im5vbmUiIGQ9Ik0wLS4yNWgyNHYyNEgweiIgLz4KPC9zdmc+Cg==\" />`));\n        let ascending = null;\n        qs($th, \"img\").onclick = (e) => {\n            ascending = !ascending;\n            STATE.rows = sortBy(STATE.rows, ascending, name);\n            qsa(e.target.closest(\".tr\"), \"img\").forEach(($img) => {\n                $img.style.transform = \"rotate(0deg)\";\n            });\n            if (ascending) e.target.style.transform = \"rotate(180deg)\";\n            $dom.tbody.scrollTo($dom.tbody.scrollLeft, 0);\n            buildRows(STATE.rows.slice(0, MAX_ROWS), STATE.header, $dom.tbody, padding, false, true);\n        };\n        $tr.appendChild($th);\n    });\n    $dom.thead.appendChild($tr);\n}\n\nfunction styleCell(l, name, padding) {\n    const maxSize = 40;\n    const charSize = 7;\n    let sizeInChar = Math.min(l, maxSize);\n    if (name.length >= sizeInChar) sizeInChar = Math.min(name.length + 1, maxSize);\n    return `width: ${sizeInChar*charSize+padding*2}px;`;\n}\n\nfunction withCenter(className, fieldLength, isLast) {\n    if (fieldLength > 4 || isLast) return className;\n    return `${className} center`;\n}\n\nfunction resizeLastColumnIfNeeded({ $target, $childs, padding = 0 }) {\n    const fullWidth = $target.clientWidth;\n    let currWidth = 0;\n    $childs.childNodes.forEach(($node) => currWidth += $node.clientWidth);\n    if (currWidth < fullWidth && $childs.lastChild !== null) {\n        const lastWidth = ($childs.lastChild.clientWidth - padding * 2) + fullWidth - currWidth;\n        $childs.lastChild.setAttribute(\"style\", `width: ${lastWidth}px`);\n    }\n}\n\nfunction sortBy(rows, ascending, key) {\n    const o = ascending ? 1 : -1;\n    return rows.sort((a, b) => {\n        let diff = a[key] - b[key];\n        if (isNaN(diff)) {\n            if (a[key] === b[key]) diff = 0;\n            else if (a[key] < b[key]) diff = -1;\n            else diff = 1;\n        }\n        return o*diff;\n    });\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_url.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport { ApplicationError } from \"../../lib/error.js\";\nimport assert from \"../../lib/assert.js\";\nimport { createLoader } from \"../../components/loader.js\";\nimport ctrlError from \"../ctrl_error.js\";\nimport { cat } from \"./model_files.js\";\n\nexport default function(render, { getDownloadUrl }) {\n    const $page = createElement(`\n        <div class=\"component_urlopener\" style=\"background: var(--surface);\"></div>\n    `);\n    render($page);\n    createLoader($page);\n\n    effect(cat(getDownloadUrl()).pipe(\n        rxjs.tap((content) => {\n            const url = content.replace(/[\\s\\S]*(https?:\\/\\/\\S+)[\\s\\S]*/, \"$1\");\n            try {\n                new URL(url); // throws on invalid URL\n                location.replace(url);\n            } catch (err) {\n                const message = assert.type(err, window.Error).message;\n                throw new ApplicationError(\"Not Valid\", message);\n            }\n        }),\n        rxjs.catchError(ctrlError()),\n    ));\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_video.css",
    "content": "body:not(.dark-mode) .component_videoplayer {\n    background: var(--surface);\n}\n\n.component_videoplayer {\n    display: flex;\n    flex: 1;\n    flex-direction: column;\n    width: 100%;\n}\n.component_videoplayer .video_container {\n    display: flex;\n    flex: 1;\n    text-align: center;\n    overflow: hidden;\n    padding: 15px 10px 65px 10px;\n    height: 100%;\n    box-sizing: border-box;\n}\n.component_videoplayer .video_container > span {\n    display: flex;\n    flex: 1;\n    text-align: center;\n    overflow: hidden;\n    height: 100%;\n    box-sizing: border-box;\n}\n.component_videoplayer .video_container .video_screen {\n    background: black;\n    box-shadow: rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px;\n    position: relative;\n    border-radius: 3px;\n    margin: auto;\n    position: relative;\n}\n.component_videoplayer .video_container .video_screen .video_wrapper {\n    width: 800px;\n    height: 450px;\n}\n@media (max-width: 850px) { .component_videoplayer .video_container .video_screen .video_wrapper { max-width: 600px; }}\n@media (max-width: 650px) { .component_videoplayer .video_container .video_screen .video_wrapper { max-width: 500px; max-height: 400px; }}\n@media (max-width: 550px) { .component_videoplayer .video_container .video_screen .video_wrapper { max-width: 450px; max-height: 350px; }}\n@media (max-width: 500px) { .component_videoplayer .video_container .video_screen .video_wrapper { max-width: 350px; max-height: 300px; }}\n@media (max-width: 370px) { .component_videoplayer .video_container .video_screen .video_wrapper { max-width: 250px; max-height: 200px; }}\n@media (max-height: 650px) {\n    .component_videoplayer .video_container .video_screen .video_wrapper { max-width: 600px; height: 420px; }\n    .component_videoplayer .video_container { padding: 10px 10px 10px 10px; }\n}\n@media (max-height: 550px) { .component_videoplayer .video_container .video_screen .video_wrapper { height: 330px; }}\n@media (max-height: 445px) { .component_videoplayer .video_container .video_screen .video_wrapper { height: 250px; }}\n@media (max-height: 370px) { .component_videoplayer .video_container .video_screen .video_wrapper { height: 200px; }}\n@media (max-height: 310px) { .component_videoplayer .video_container .video_screen .video_wrapper { height: 100px; }}\n\n.component_videoplayer .video_container .video_screen .loader {\n    position: absolute;\n    top: calc(50% - 85px);\n    width: 100%;\n}\n.component_videoplayer .video_container .video_screen .loader > img {\n    height: 170px;\n    cursor: pointer;\n    filter: brightness(0.5) invert(1);\n}\n@media (max-width: 700px) or (max-height: 650px) {\n    .component_videoplayer .video_container .video_screen .loader > img { height: 100px; }\n    .component_videoplayer .video_container .video_screen .loader { top: calc(50% - 60px); }\n}\n\n.component_videoplayer .video_container .video_screen.is-casting-yes .videoplayer_control, .component_videoplayer .video_container .video_screen.video-state-pause .videoplayer_control, .component_videoplayer .video_container .video_screen.video-state-buffer .videoplayer_control, .component_videoplayer .video_container .video_screen.video-state-play:hover .videoplayer_control {\n    opacity: 1;\n    transition: 0.1s opacity ease;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control {\n    transition: 0.5s opacity ease;\n    opacity: 0;\n    display: flex;\n    text-align: left;\n    position: absolute;\n    bottom: 0;\n    width: 100%;\n    border-bottom-left-radius: 3px;\n    border-bottom-right-radius: 3px;\n    background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADCCAYAAACIaaiTAAAAAXNSR0IArs4c6QAAARJJREFUOE9lyNdHBQAAhfHb3nvvuu2997jNe29TJJEkkkgSSSSJJJJEEkkiifRH5jsP56Xz8PM5gcC/xfCIWBNHiXiTQIlEk0SJZJNCiVRIM+mUyDCZlMgy2ZTIMbmUyDP5lCgwhZQoMsWUKDGllCgz5ZSogEpTRYlqU0OJoKmlRJ2pp0SDaaREk2mmRItppUSbaadEh+mkRBd0mx5K9Jo+SvSbAUoMmiFKDJsRSoyaMUqMmwlKhMwkJabMNCVmYNbMUSJsIpSImnlKLJhFSiyZZWoFVmEN1mEDNmELtmEHdmEP9uEADuEIjuEETuEMzuECLuEKruEGbuEO7uEBHuEJnuEFXuEN3uEDPuELvuEHfv8AoRErEi7Uc8UAAAAASUVORK5CYII=);\n    background-repeat: repeat-x;\n    background-size: contain;\n    padding: 0 0 7px 0;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control img {\n    cursor: pointer;\n    width: 25px;\n    filter: brightness(0) invert(1);\n    padding: 5px 5px 5px 10px;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control input[type=\"range\"] {\n    width: 60px;\n    cursor: pointer;\n    outline: none;\n    -webkit-appearance: none;\n    background: transparent;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control input[type=\"range\"]::-webkit-slider-thumb {\n    -webkit-appearance: none;\n    height: 12px;\n    width: 12px;\n    border: none;\n    border-radius: 50%;\n    background: white;\n    margin-top: -5px;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control input[type=\"range\"]::-moz-range-thumb {\n    -webkit-appearance: none;\n    height: 12px;\n    width: 12px;\n    border: none;\n    border-radius: 50%;\n    background: white;\n    margin-top: -5px;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control input[type=\"range\"]::-webkit-slider-runnable-track {\n    width: 100%;\n    height: 2px;\n    background-color: rgba(255, 255, 255, 0.7);\n    border-radius: 2px;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control input[type=\"range\"]::-moz-range-track {\n    width: 100%;\n    height: 2px;\n    background-color: rgba(255, 255, 255, 0.7);\n    border-radius: 2px;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control .timecode {\n    color: white;\n    margin: auto 0;\n    padding-left: 10px;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control .timecode .hint {\n    position: absolute;\n    top: -40px;\n    margin-left: -23px;\n    font-size: 0.9rem;\n    background: #f1f1f155;\n    border-radius: 3px;\n    padding: 2px 5px;\n    background: var(--dark);\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control .progress {\n    position: absolute;\n    width: 100%;\n    top: -20px;\n    height: 20px;\n    cursor: pointer;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control .progress .progress-active, .component_videoplayer .video_container .video_screen .videoplayer_control .progress .progress-buffer, .component_videoplayer .video_container .video_screen .videoplayer_control .progress .progress-placeholder {\n    top: 10px;\n    position: absolute;\n    height: 4px;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control .progress .progress-active {\n    background: var(--primary);\n    border-top-right-radius: 2px;\n    border-bottom-right-radius: 2px;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control .progress .progress-active .thumb {\n    display: none;\n    float: right;\n    width: 4px;\n    height: 4px;\n    background: rgba(255, 255, 255, 0.1);\n    border-radius: 50%;\n    margin-top: 0px;\n    position: relative;\n    left: 3px;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control .progress .progress-buffer {\n    background: #e2e2e244;\n}\n.component_videoplayer .video_container .video_screen .videoplayer_control .progress .progress-placeholder {\n    background: #e2e2e222;\n    width: 100%;\n}\n.component_videoplayer .video_container video {\n    width: 100%;\n    height: 100%;\n}\n\n.component_videoplayer .video_container .video_screen .videoplayer_control:hover .progress .progress-active .thumb {\n    display: block;\n}\n\n.video-enter, .video-appear {\n    opacity: 0;\n}\n\n.video-enter.video-enter-active, .video-appear.video-appear-active {\n    transition: top .3s,right .3s,bottom .3s,left .3s,max-width .3s,max-height .3s;\n    -webkit-animation-name: zoomIn;\n    animation-name: zoomIn;\n    animation-duration: .3s;\n    -webkit-animation-timing-function: cubic-bezier(0.51, 0.92, 0.24, 1.15);\n    animation-timing-function: cubic-bezier(0.51, 0.92, 0.24, 1.15);\n    opacity: 1;\n}\n\n@keyframes zoomIn {\n    0% {\n        opacity: 0;\n        transform: scale(0.85);\n    }\n    100% {\n        opacity: 1;\n        transform: scale(1);\n    }\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/application_video.js",
    "content": "import { createElement, onDestroy } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport { animate, slideYIn } from \"../../lib/animate.js\";\nimport { loadCSS, loadJS } from \"../../helpers/loader.js\";\nimport { qs, qsa, safe } from \"../../lib/dom.js\";\nimport { settings_get, settings_put } from \"../../lib/settings.js\";\nimport { ApplicationError } from \"../../lib/error.js\";\nimport assert from \"../../lib/assert.js\";\nimport Hls from \"../../lib/vendor/hlsjs/hls.js\";\n\nimport ctrlError from \"../ctrl_error.js\";\n\nimport { transition } from \"./common.js\";\nimport { formatTimecode } from \"./common_player.js\";\nimport { ICON } from \"./common_icon.js\";\nimport { renderMenubar, buttonDownload, buttonFullscreen } from \"./component_menubar.js\";\n\nimport \"../../components/icon.js\";\n\nconst STATUS_PLAYING = \"PLAYING\";\nconst STATUS_PAUSED = \"PAUSED\";\nconst STATUS_BUFFERING = \"BUFFERING\";\n\nexport default function(render, { mime, getFilename, getDownloadUrl }) {\n    const $page = createElement(`\n        <div class=\"component_videoplayer\">\n            <component-menubar filename=\"${safe(getFilename())}\"></component-menubar>\n            <div class=\"video_container\">\n                <span>\n                    <div class=\"video_screen video-state-pause is-casting-no\">\n                        <div class=\"video_wrapper\">\n                            <video></video>\n                        </div>\n                        <div class=\"loader no-select\">\n                            <component-icon name=\"loading\"></component-icon>\n                        </div>\n                        <div class=\"videoplayer_control no-select hidden\">\n                            <div class=\"progress\">\n                                <div data-bind=\"progress-buffer\">\n                                   <div class=\"progress-buffer\" style=\"left: 0%; width: 0%;\"></div>\n                                </div>\n                                <div class=\"progress-active\" style=\"width: 0%;\">\n                                    <div class=\"thumb\"></div>\n                                </div>\n                                <div class=\"progress-placeholder\"></div>\n                            </div>\n                            <img class=\"component_icon\" draggable=\"false\" src=\"${ICON.PLAY}\" alt=\"play\">\n                            <img class=\"component_icon hidden\" draggable=\"false\" src=\"${ICON.PAUSE}\" alt=\"pause\">\n                            <component-icon name=\"loading\" class=\"hidden\"></component-icon>\n\n                            <img class=\"component_icon hidden\" draggable=\"false\" src=\"${ICON.VOLUME_MUTE}\" alt=\"volume_mute\">\n                            <img class=\"component_icon hidden\" draggable=\"false\" src=\"${ICON.VOLUME_LOW}\" alt=\"volume_low\">\n                            <img class=\"component_icon hidden\" draggable=\"false\" src=\"${ICON.VOLUME_NORMAL}\" alt=\"volume\">\n\n                            <input type=\"range\" min=\"0\" max=\"100\" value=\"13\">\n                            <span class=\"timecode\">\n                                <div class=\"current\"></div>\n                                <div class=\"hint hidden\"></div>\n                            </span>\n                        </div>\n                    </div>\n                </span>\n            </div>\n        </div>\n    `);\n    render($page);\n    renderMenubar(\n        qs($page, \"component-menubar\"),\n        buttonDownload(getFilename(), getDownloadUrl()),\n        buttonFullscreen(qs($page, \"video\")),\n    );\n    transition(qs($page, \".video_container\"));\n\n    const $video = qs($page, \"video\");\n    const $control = {\n        play: qs($page, `.videoplayer_control [alt=\"play\"]`),\n        pause: qs($page, `.videoplayer_control [alt=\"pause\"]`),\n        loading: qs($page, `.videoplayer_control component-icon[name=\"loading\"]`),\n    };\n    const $volume = {\n        range: qs($page, `input[type=\"range\"]`),\n        icon_mute: qs($page, `img[alt=\"volume_mute\"]`),\n        icon_low: qs($page, `img[alt=\"volume_low\"]`),\n        icon_normal: qs($page, `img[alt=\"volume\"]`),\n    };\n    const setVolume = (volume) => {\n        settings_put(\"volume\", volume);\n        $video.volume = volume / 100;\n        $volume.range.value = volume;\n        if (volume === 0) {\n            $volume.icon_mute.classList.remove(\"hidden\");\n            $volume.icon_low.classList.add(\"hidden\");\n            $volume.icon_normal.classList.add(\"hidden\");\n        } else if (volume < 50) {\n            $volume.icon_mute.classList.add(\"hidden\");\n            $volume.icon_low.classList.remove(\"hidden\");\n            $volume.icon_normal.classList.add(\"hidden\");\n        } else {\n            $volume.icon_mute.classList.add(\"hidden\");\n            $volume.icon_low.classList.add(\"hidden\");\n            $volume.icon_normal.classList.remove(\"hidden\");\n        }\n    };\n    const setStatus = (status) => {\n        switch (status) {\n        case \"PLAYING\":\n            $control.play.classList.add(\"hidden\");\n            $control.pause.classList.remove(\"hidden\");\n            $control.loading.classList.add(\"hidden\");\n            $video.play();\n            break;\n        case \"PAUSED\":\n            $control.play.classList.remove(\"hidden\");\n            $control.pause.classList.add(\"hidden\");\n            $control.loading.classList.add(\"hidden\");\n            $video.pause();\n            break;\n        case \"BUFFERING\":\n            $control.play.classList.add(\"hidden\");\n            $control.pause.classList.add(\"hidden\");\n            $control.loading.classList.remove(\"hidden\");\n            break;\n        default:\n            assert.fail(status);\n        }\n    };\n    const setSeek = (newTime, shouldSet = false) => {\n        if (shouldSet) $video.currentTime = newTime;\n        const width = 100 * (newTime / $video.duration);\n        qs($page, \".progress .progress-active\").style.width = `${width}%`;\n        if (!isNaN($video.duration)) {\n            qs($page, \".timecode .current\").textContent = formatTimecode($video.currentTime) + \" / \" + formatTimecode($video.duration);\n        }\n    };\n\n    // feature1: setup the dom\n    const setup$ = rxjs.of(null).pipe(\n        rxjs.map(() => {\n            const loadPolicy = { default: { maxLoadTimeMs: 3600000, maxTimeToFirstByteMs: Infinity, timeoutRetry: { maxNumRetry: 0 } } };\n            const hls = new Hls({\n                debug: !!new URLSearchParams(location.search).get(\"debug\"),\n                manifestLoadPolicy: loadPolicy,\n            });\n            const sources = window.overrides[\"video-map-sources\"]([{\n                src: getDownloadUrl(),\n                type: mime,\n            }]);\n            for (let i=0; i<sources.length; i++) {\n                if (sources[i].type !== \"application/x-mpegURL\") {\n                    const $source = document.createElement(\"source\");\n                    $source.setAttribute(\"type\", \"video/mp4\");\n                    $source.setAttribute(\"src\", sources[i].src);\n                    $video.appendChild($source);\n                    return [{ ...sources[i], type: \"video/mp4\" }];\n                }\n                hls.loadSource(sources[i].src);\n            }\n            hls.attachMedia($video);\n            onDestroy(() => {\n                $video.pause();\n                $video.remove();\n            });\n            return sources;\n        }),\n        rxjs.mergeMap((sources) => rxjs.merge(\n            rxjs.fromEvent($video, \"loadeddata\"),\n            ...[...qsa($page, \"source\")].map(($source) => rxjs.fromEvent($source, \"error\").pipe(rxjs.tap(() => {\n                throw new ApplicationError(\"NOT_SUPPORTED\", JSON.stringify({ mime, sources }, null, 2));\n            }))),\n        )),\n        rxjs.mergeMap(() => {\n            const $loader = qs($page, \".loader\");\n            $loader.replaceChildren(createElement(`<img src=\"${ICON.PLAY}\" />`));\n            animate($loader, {\n                time: 150,\n                keyframes: [\n                    { transform: \"scale(0.7)\" },\n                    { transform: \"scale(1)\" },\n                ],\n            });\n            setSeek(0);\n            return rxjs.race(\n                rxjs.fromEvent($loader, \"click\").pipe(rxjs.mapTo($loader)),\n                rxjs.fromEvent(document, \"keydown\").pipe(rxjs.filter((e) => e.code === \"Space\"), rxjs.first()),\n            ).pipe(rxjs.mapTo($loader));\n        }),\n        rxjs.tap(($loader) => {\n            $loader.classList.add(\"hidden\");\n            const $control = qs($page, \".videoplayer_control\");\n            $control.classList.remove(\"hidden\");\n            animate($control, { time: 300, keyframes: slideYIn(5) });\n            setStatus(STATUS_PLAYING);\n        }),\n        rxjs.catchError(ctrlError()),\n        rxjs.share(),\n    );\n    effect(setup$);\n    effect(setup$.pipe(rxjs.mergeMap(() => rxjs.fromEvent($video, \"error\").pipe(rxjs.tap(() => {\n        // console.error(err);\n        // notify.send(t(\"Not supported\"), \"error\");\n        // setIsPlaying(false);\n        // setIsLoading(false);\n    })))));\n\n    // feature2: player control - volume\n    effect(setup$.pipe(\n        rxjs.switchMap(() => rxjs.fromEvent($volume.range, \"input\").pipe(rxjs.map((e) => e.target.value))),\n        rxjs.startWith(settings_get(\"volume\") === null ? 80 : settings_get(\"volume\")),\n        rxjs.tap((volume) => setVolume(parseInt(volume))),\n    ));\n\n    // feature3: player control - play/pause\n    effect(setup$.pipe(\n        rxjs.mergeMap(() => rxjs.merge(\n            rxjs.fromEvent($control.play, \"click\").pipe(rxjs.mapTo(STATUS_PLAYING)),\n            rxjs.fromEvent($control.pause, \"click\").pipe(rxjs.mapTo(STATUS_PAUSED)),\n            rxjs.fromEvent($video, \"ended\").pipe(rxjs.mapTo(STATUS_PAUSED)),\n            rxjs.fromEvent($video, \"waiting\").pipe(rxjs.mapTo(STATUS_BUFFERING)),\n            rxjs.fromEvent($video, \"playing\").pipe(rxjs.mapTo(STATUS_PLAYING)),\n        )),\n        rxjs.debounceTime(50),\n        rxjs.tap((status) => setStatus(status)),\n    ));\n\n    // feature4: hint\n    const $hint = qs($page, `.hint`);\n    effect(setup$.pipe(\n        rxjs.switchMap(() => rxjs.fromEvent(qs($page, \".progress\"), \"mousemove\")),\n        rxjs.map((e) => {\n            const rec = e.target.getBoundingClientRect();\n            const width = e.clientX - rec.x;\n            const time = $video.duration * width / rec.width;\n            let posX = width;\n            posX = Math.max(posX, 30);\n            posX = Math.min(posX, e.target.clientWidth - 30);\n            return { x: `${posX}px`, time };\n        }),\n        rxjs.tap(({ x, time }) => {\n            $hint.classList.remove(\"hidden\");\n            $hint.style.left = x;\n            $hint.textContent = formatTimecode(time);\n        }),\n    ));\n    effect(setup$.pipe(\n        rxjs.switchMap(() => rxjs.fromEvent(qs($page, \".progress\"), \"mouseleave\")),\n        rxjs.tap(() => $hint.classList.add(\"hidden\")),\n    ));\n\n    // feature5: player control - seek\n    effect(setup$.pipe(\n        rxjs.switchMap(() => rxjs.fromEvent(qs($page, \".progress\"), \"click\").pipe(\n            rxjs.map((e) => { // TODO: use onClick instead?\n                let $progress = e.target;\n                if (e.target.classList.contains(\"progress\") === false) {\n                    $progress = e.target.parentElement;\n                }\n                const rec = $progress.getBoundingClientRect();\n                return (e.clientX - rec.x) / rec.width;\n            }),\n            rxjs.tap((n) => {\n                if (n < 2/100) {\n                    setStatus(STATUS_PAUSED);\n                    n = 0;\n                }\n                setSeek(n * $video.duration, true);\n            }),\n        )),\n    ));\n\n    // feature6: player control - keyboard shortcut\n    effect(setup$.pipe(\n        rxjs.switchMap(() => rxjs.merge(\n            rxjs.fromEvent(document, \"keydown\").pipe(rxjs.map((e) => e.code)),\n            rxjs.fromEvent($video, \"click\").pipe(rxjs.mapTo(\"Space\")),\n        )),\n        rxjs.tap((code) => {\n            switch (code) {\n            case \"Space\":\n            case \"KeyK\":\n                setStatus($video.paused ? STATUS_PLAYING : STATUS_PAUSED);\n                break;\n            case \"KeyM\":\n                setVolume($video.volume > 0 ? 0 : settings_get(\"volume\"));\n                break;\n            case \"ArrowUp\":\n                setVolume(Math.min($video.volume*100 + 10, 100));\n                break;\n            case \"ArrowDown\":\n                setVolume(Math.max($video.volume*100 - 10, 0));\n                break;\n            case \"KeyL\":\n                setSeek(Math.min($video.duration, $video.currentTime + 10), true);\n                break;\n            case \"KeyJ\":\n                setSeek(Math.max(0, $video.currentTime - 10), true);\n                break;\n            case \"KeyF\":\n                // TODO\n                break;\n            case \"Digit0\":\n                setSeek(0, true);\n                break;\n            case \"Digit1\":\n                setSeek($video.duration / 10, true);\n                break;\n            case \"Digit2\":\n                setSeek($video.duration * 2 / 10, true);\n                break;\n            case \"Digit3\":\n                setSeek($video.duration * 3 / 10, true);\n                break;\n            case \"Digit4\":\n                setSeek($video.duration * 4 / 10, true);\n                break;\n            case \"Digit5\":\n                setSeek($video.duration * 5 / 10, true);\n                break;\n            case \"Digit6\":\n                setSeek($video.duration * 6 / 10, true);\n                break;\n            case \"Digit7\":\n                setSeek($video.duration * 7 / 10, true);\n                break;\n            case \"Digit8\":\n                setSeek($video.duration * 8 / 10, true);\n                break;\n            case \"Digit9\":\n                setSeek($video.duration * 9 / 10, true);\n                break;\n            }\n        }),\n    ));\n\n    // feature7: render the progress bar\n    effect(setup$.pipe(\n        rxjs.mergeMap(() => rxjs.fromEvent($video, \"timeupdate\")),\n        rxjs.tap(() => setSeek($video.currentTime)),\n    ));\n\n    // feature8: render loading buffer\n    effect(setup$.pipe(\n        rxjs.mergeMap(() => rxjs.fromEvent($video, \"timeupdate\")),\n        rxjs.tap(() => {\n            const calcWidth = (i) => {\n                return ($video.buffered.end(i) - $video.buffered.start(i)) / $video.duration * 100;\n            };\n            const calcLeft = (i) => {\n                return $video.buffered.start(i) / $video.duration * 100;\n            };\n            const $container = qs($page, `[data-bind=\"progress-buffer\"]`);\n            if ($video.buffered.length !== $container.children.length) {\n                $container.innerHTML = \"\";\n                const $fragment = document.createDocumentFragment();\n                Array.from({ length: $video.buffered.length })\n                    .map(() => $fragment.appendChild(createElement(`\n                        <div className=\"progress-buffer\" style=\"\"></div>\n                    `)));\n                $container.appendChild($fragment);\n            }\n            for (let i=0; i<$video.buffered.length; i++) {\n                $container.children[i].style.left = calcLeft(i) + \"%\";\n                $container.children[i].style.width = calcWidth(i) + \"%\";\n            }\n        }),\n    ));\n}\n\nexport function init() {\n    if (!window.overrides) window.overrides = {};\n    return Promise.all([\n        loadCSS(import.meta.url, \"./application_video.css\"),\n        loadJS(import.meta.url, \"/overrides/video-transcoder.js\"),\n    ]).then(async() => {\n        if (typeof window.overrides[\"video-map-sources\"] !== \"function\") window.overrides[\"video-map-sources\"] = (s) => (s);\n    });\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/common.js",
    "content": "import { fromHref } from \"../../lib/skeleton/router.js\";\nimport { transition as transitionLib, slideYIn } from \"../../lib/animate.js\";\nimport { basename, forwardURLParams } from \"../../lib/path.js\";\n\nexport function transition($node) {\n    return transitionLib($node, { timeEnter: 150, enter: slideYIn(2) });\n}\n\nexport function getFilename() {\n    return basename(getCurrentPath()) || \"&nbsp;\";\n}\n\nexport function getDownloadUrl() {\n    return forwardURLParams(\"api/files/cat?path=\" + encodeURIComponent(getCurrentPath()), [\"share\"]);\n}\n\nexport function getCurrentPath(start = \"/view/\") {\n    const fullpath = fromHref(location.pathname + location.hash);\n    return decodeURIComponent(fullpath.replace(new RegExp(\"^\" + start), \"/\"));\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/common_fab.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\n\nexport const $ICON = {\n    SAVING: createElement(`<img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MzguNTMzIDQzOC41MzMiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQzOC41MzMgNDM4LjUzMzsiPgogIDxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik00MzIuODIzLDEyMS4wNDljLTMuODA2LTkuMTMyLTguMzc3LTE2LjM2Ny0xMy43MDktMjEuNjk1bC03OS45NDEtNzkuOTQyYy01LjMyNS01LjMyNS0xMi41Ni05Ljg5NS0yMS42OTYtMTMuNzA0ICAgQzMwOC4zNDYsMS45MDMsMjk5Ljk2OSwwLDI5Mi4zNTcsMEgyNy40MDlDMTkuNzk4LDAsMTMuMzI1LDIuNjYzLDcuOTk1LDcuOTkzYy01LjMzLDUuMzI3LTcuOTkyLDExLjc5OS03Ljk5MiwxOS40MTR2MzgzLjcxOSAgIGMwLDcuNjE3LDIuNjYyLDE0LjA4OSw3Ljk5MiwxOS40MTdjNS4zMyw1LjMyNSwxMS44MDMsNy45OTEsMTkuNDE0LDcuOTkxaDM4My43MThjNy42MTgsMCwxNC4wODktMi42NjYsMTkuNDE3LTcuOTkxICAgYzUuMzI1LTUuMzI4LDcuOTg3LTExLjgsNy45ODctMTkuNDE3VjE0Ni4xNzhDNDM4LjUzMSwxMzguNTYyLDQzNi42MjksMTMwLjE4OCw0MzIuODIzLDEyMS4wNDl6IE0xODIuNzI1LDQ1LjY3NyAgIGMwLTIuNDc0LDAuOTA1LTQuNjExLDIuNzE0LTYuNDIzYzEuODA3LTEuODA0LDMuOTQ5LTIuNzA4LDYuNDIzLTIuNzA4aDU0LjgxOWMyLjQ2OCwwLDQuNjA5LDAuOTAyLDYuNDE3LDIuNzA4ICAgYzEuODEzLDEuODEyLDIuNzE3LDMuOTQ5LDIuNzE3LDYuNDIzdjkxLjM2MmMwLDIuNDc4LTAuOTEsNC42MTgtMi43MTcsNi40MjdjLTEuODA4LDEuODAzLTMuOTQ5LDIuNzA4LTYuNDE3LDIuNzA4aC01NC44MTkgICBjLTIuNDc0LDAtNC42MTctMC45MDItNi40MjMtMi43MDhjLTEuODA5LTEuODEyLTIuNzE0LTMuOTQ5LTIuNzE0LTYuNDI3VjQ1LjY3N3ogTTMyOC45MDYsNDAxLjk5MUgxMDkuNjMzVjI5Mi4zNTVoMjE5LjI3MyAgIFY0MDEuOTkxeiBNNDAyLDQwMS45OTFoLTM2LjU1MmgtMC4wMDdWMjgzLjIxOGMwLTcuNjE3LTIuNjYzLTE0LjA4NS03Ljk5MS0xOS40MTdjLTUuMzI4LTUuMzI4LTExLjgtNy45OTQtMTkuNDEtNy45OTRIMTAwLjQ5OCAgIGMtNy42MTQsMC0xNC4wODcsMi42NjYtMTkuNDE3LDcuOTk0Yy01LjMyNyw1LjMyOC03Ljk5MiwxMS44LTcuOTkyLDE5LjQxN3YxMTguNzczSDM2LjU0NFYzNi41NDJoMzYuNTQ0djExOC43NzEgICBjMCw3LjYxNSwyLjY2MiwxNC4wODQsNy45OTIsMTkuNDE0YzUuMzMsNS4zMjcsMTEuODAzLDcuOTkzLDE5LjQxNyw3Ljk5M2gxNjQuNDU2YzcuNjEsMCwxNC4wODktMi42NjYsMTkuNDEtNy45OTMgICBjNS4zMjUtNS4zMjcsNy45OTQtMTEuNzk5LDcuOTk0LTE5LjQxNFYzNi41NDJjMi44NTQsMCw2LjU2MywwLjk1LDExLjEzNiwyLjg1M2M0LjU3MiwxLjkwMiw3LjgwNiwzLjgwNSw5LjcwOSw1LjcwOGw4MC4yMzIsODAuMjMgICBjMS45MDIsMS45MDMsMy44MDYsNS4xOSw1LjcwOCw5Ljg1MWMxLjkwOSw0LjY2NSwyLjg1Nyw4LjMzLDIuODU3LDEwLjk5NFY0MDEuOTkxeiIvPgo8L3N2Zz4K\" alt=\"save\" style=\"height: 100%; width: 100%;\">`),\n    LOADING: createElement(`<component-icon name=\"loading\"></component-icon>`),\n};\n"
  },
  {
    "path": "public/assets/pages/viewerpage/common_icon.js",
    "content": "export const ICON = {\n    PLAY: \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgdmlld0JveD0iMCAwIDU4Ljc1MiA1OC43NTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+Cgk8cGF0aCBkPSJNIDQ1LjYwNjkxMywyNS4xNzQ3MTIgMjIuNjkxNDAxLDYuMTA4OTgyNSBjIC0xLjQ2OTYyNSwtMC45MDc4NTI1IC0zLjM3MjM1NCwtMC45MDUzNzY2IC00LjgzNjU4NCwwIC0xLjQ5MzUxNSwwLjkyMTA1NzcgLTIuNDIyMTQ1LDIuNjQxODUxIC0yLjQyMjE0NSw0LjQ4OTc0MzUgViA0OC43MzI2NiBjIDAsMS44NDg3MTggMC45Mjc4NiwzLjU2OTUxMSAyLjQxMjg5Nyw0LjQ4NTYxNyAwLjczNDQyNywwLjQ1ODA1MyAxLjU3MzY2MywwLjY5OTg3MiAyLjQyNjc2OSwwLjY5OTg3MiAwLjg1MDc5NSwwIDEuNjg5MjYsLTAuMjQwOTk0IDIuNDIwNjA0LC0wLjY5NTc0NSBsIDIyLjkxNTUxMiwtMTkuMDY3MzggYyAxLjQ5MTk3NCwtMC45MjM1MzMgMi40MTgyOTIsLTIuNjQzNTAxIDIuNDE4MjkyLC00LjQ4ODkxOCAtNy43ZS00LC0xLjg0Mjk0MSAtMC45MjYzMTgsLTMuNTYyOTA5IC0yLjQxOTgzMywtNC40OTEzOTQgeiBNIDQzLjAwMTIxMSwyOS44NjgzMSAyMC42NzA5MDYsNDguNjQyNzU0IGMgLTAuMDYzMTksMC4wMzg3OSAtMC4xMzg3MTYsMC4wNDI5MiAtMC4yMTUwMSwtMC4wMDQxIC0wLjA2NDc0LC0wLjA0MDQ0IC0wLjEwNTU3OSwtMC4xMTcxOTYgLTAuMTA1NTc5LC0wLjE5OTcyOCBWIDEwLjg5MTY2MSBjIDAsLTAuMDgyNTMgMC4wNDAwNywtMC4xNTg0NjIgMC4xMDc4OTEsLTAuMjAwNTUzIDAuMDMyMzcsLTAuMDIwNjMgMC4wNjkzNiwtMC4wMzEzNiAwLjEwNzEyLC0wLjAzMTM2IDAuMDM5MywwIDAuMDc2MjksMC4wMTA3MyAwLjEwOTQzMiwwLjAzMTM2IGwgMjIuMzIyNTk3LDE4Ljc2OTQ5MyBjIDAuMDY4NTksMC4wNDI5MiAwLjExMTc0NCwwLjEyMTMyMiAwLjExMTc0NCwwLjIwNTUwNSAtNy43ZS00LDAuMDg1MDEgLTAuMDQwODQsMC4xNjAxMTIgLTAuMTA3ODksMC4yMDIyMDQgeiIgc3R5bGU9ImZpbGw6IzZmNmY2ZiIgLz4KPC9zdmc+Cg==\",\n    PAUSE: \"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgZmlsbD0iIzZmNmY2ZiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNIDMyMCw4MS45ODc4NjggViA0MjcuNzY5MzEgYyAwLDkuMzQzMzYgNy42NDQ2LDE2Ljk4Nzg4IDE2Ljk4NzksMTYuOTg3ODggaCAyNS45NzU3IGMgOS4zNDMzLDAgMTYuOTg3OCwtNy42NDQ1MiAxNi45ODc4LC0xNi45ODc4OCBWIDgxLjk4Nzg2OCBDIDM3OS45NTE0LDcyLjY0NDUzNiAzNzIuMzA2OSw2NSAzNjIuOTYzNiw2NSBIIDMzNi45ODc5IEMgMzI3LjY0NDYsNjUgMzIwLDcyLjY0NDUzNiAzMjAsODEuOTg3ODY4IFoiIC8+CiAgPHBhdGggZD0iTSAxNTAsODEuOTg3ODY4IFYgNDI3Ljc2OTMxIGMgMCw5LjM0MzM2IDcuNjQ0NTUsMTYuOTg3ODggMTYuOTg3NzksMTYuOTg3ODggaCAyNS45NzU1MiBjIDkuMzQzMjQsMCAxNi45ODc2OSwtNy42NDQ1MiAxNi45ODc2OSwtMTYuOTg3ODggViA4MS45ODc4NjggQyAyMDkuOTUxLDcyLjY0NDUzNiAyMDIuMzA2NTUsNjUgMTkyLjk2MzMxLDY1IEggMTY2Ljk4Nzc5IEMgMTU3LjY0NDU1LDY1IDE1MCw3Mi42NDQ1MzYgMTUwLDgxLjk4Nzg2OCBaIiAvPgo8L3N2Zz4K\",\n\n    VOLUME_MUTE: \"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzZmNmY2ZiIgc3Ryb2tlLXdpZHRoPSIyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Im0gMjEuOTgzODgzLDE0Ljg4Mzg4MyAtNiwtNS45OTk5OTk1IG0gNiwwIC02LDUuOTk5OTk5NSIgLz4KICA8cGF0aCBkPSJNIDEuMTI1MzM1NiwxNS4yMTc4OTcgViA4Ljc4MTAzMjggYyAwLC0wLjYyNDIyMDUgMC40ODcxOTY3LC0xLjEzMDk5MTcgMS4wODc0OTIzLC0xLjEzMDk5MTcgSCA2LjExMjU3NDggQSAxLjA2NTc0MjIsMS4wNjU3NDIyIDAgMCAwIDYuODgxNDMxOCw3LjMxODM1NjIgTCAxMC4xNDM5MDksMy42MzM5MzI4IGMgMC42ODUxMiwtMC43MTMzOTUgMS44NTYzNDUsLTAuMjA3NzExMSAxLjg1NjM0NSwwLjgwMDM5NDIgdiAxNS4xMzEzNjggYyAwLDEuMDE1NzE1IC0xLjE4NTM2MiwxLjUxNzA0NSAtMS44NjYxMzEsMC43ODk1MTMgTCA2Ljg4MjUxOSwxNi42OTE0NSBBIDEuMDY1NzQyMiwxLjA2NTc0MjIgMCAwIDAgNi4xMDM4NzQ4LDE2LjM0OTk3NSBIIDIuMjEyODI3OSBjIC0wLjYwMDI5NTYsMCAtMS4wODc0OTIzLC0wLjUwNjc2OCAtMS4wODc0OTIzLC0xLjEzMjA3OCB6IiAvPgo8L3N2Zz4K\",\n    VOLUME_LOW: \"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzZmNmY2ZiIgc3Ryb2tlLXdpZHRoPSIyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Im0gMTYuMzUwMjI1LDguMTkzNzg3IGMgMS40NDk2MjYsMS45MzM1NTkgMS40NDk2MjYsNS42Nzg4ODQgMCw3LjYxMjQ0NiIgLz4KICA8cGF0aCBkPSJNIDEuMTI1MzM1NiwxNS4yMTc4OTcgViA4Ljc4MTAzMjggYyAwLC0wLjYyNDIyMDUgMC40ODcxOTY3LC0xLjEzMDk5MTcgMS4wODc0OTIzLC0xLjEzMDk5MTcgSCA2LjExMjU3NDggQSAxLjA2NTc0MjIsMS4wNjU3NDIyIDAgMCAwIDYuODgxNDMxOCw3LjMxODM1NjIgTCAxMC4xNDM5MDksMy42MzM5MzI4IGMgMC42ODUxMiwtMC43MTMzOTUgMS44NTYzNDUsLTAuMjA3NzExMSAxLjg1NjM0NSwwLjgwMDM5NDIgdiAxNS4xMzEzNjggYyAwLDEuMDE1NzE1IC0xLjE4NTM2MiwxLjUxNzA0NSAtMS44NjYxMzEsMC43ODk1MTMgTCA2Ljg4MjUxOSwxNi42OTE0NSBBIDEuMDY1NzQyMiwxLjA2NTc0MjIgMCAwIDAgNi4xMDM4NzQ4LDE2LjM0OTk3NSBIIDIuMjEyODI3OSBjIC0wLjYwMDI5NTYsMCAtMS4wODc0OTIzLC0wLjUwNjc2OCAtMS4wODc0OTIzLC0xLjEzMjA3OCB6IiAvPgo8L3N2Zz4K\",\n    VOLUME_NORMAL: \"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzZmNmY2ZiIgc3Ryb2tlLXdpZHRoPSIyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Im0gMTYuMzUwMjI1LDguMTkzNzg3IGMgMS40NDk2MjYsMS45MzM1NTkgMS40NDk2MjYsNS42Nzg4ODQgMCw3LjYxMjQ0NiIgLz4KICA8cGF0aCBkPSJtIDE5LjYxMjcwMyw0LjM4NzU2NDQgYyA0LjMzNjkxOSw0LjE0MTE3MDQgNC4zNjMwMTQsMTEuMTEwOTA4NiAwLDE1LjIyNDg4NzYiIC8+CiAgPHBhdGggZD0iTSAxLjEyNTMzNTYsMTUuMjE3ODk3IFYgOC43ODEwMzI4IGMgMCwtMC42MjQyMjA1IDAuNDg3MTk2NywtMS4xMzA5OTE3IDEuMDg3NDkyMywtMS4xMzA5OTE3IEggNi4xMTI1NzQ4IEEgMS4wNjU3NDIyLDEuMDY1NzQyMiAwIDAgMCA2Ljg4MTQzMTgsNy4zMTgzNTYyIEwgMTAuMTQzOTA5LDMuNjMzOTMyOCBjIDAuNjg1MTIsLTAuNzEzMzk1IDEuODU2MzQ1LC0wLjIwNzcxMTEgMS44NTYzNDUsMC44MDAzOTQyIHYgMTUuMTMxMzY4IGMgMCwxLjAxNTcxNSAtMS4xODUzNjIsMS41MTcwNDUgLTEuODY2MTMxLDAuNzg5NTEzIEwgNi44ODI1MTksMTYuNjkxNDUgQSAxLjA2NTc0MjIsMS4wNjU3NDIyIDAgMCAwIDYuMTAzODc0OCwxNi4zNDk5NzUgSCAyLjIxMjgyNzkgYyAtMC42MDAyOTU2LDAgLTEuMDg3NDkyMywtMC41MDY3NjggLTEuMDg3NDkyMywtMS4xMzIwNzggeiIgLz4KPC9zdmc+Cg==\",\n};\n"
  },
  {
    "path": "public/assets/pages/viewerpage/common_player.js",
    "content": "export function formatTimecode(seconds) {\n    return String(Math.floor(seconds / 60)).padStart(2, \"0\") +\n        \":\" +\n        String(Math.floor(seconds % 60)).padStart(2, \"0\");\n}\n\n// TODO: abstract setVolume, setSeek and setStatus\n"
  },
  {
    "path": "public/assets/pages/viewerpage/component_menubar.css",
    "content": ".component_menubar {\n    background: var(--dark);\n    color: #f1f1f1;\n    position: relative;\n    display: block;\n    box-shadow: 0 0px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);\n    z-index: 4;\n    text-align: left;\n}\n.component_menubar .container {\n    padding: 0;\n    font-size: 0.9em;\n}\n.component_menubar .container.inherit-width { max-width: inherit!important; }\n.component_menubar .container > span {\n    display: flex;\n}\n.component_menubar .titlebar {\n    flex: 1;\n    padding: 8px 0;\n    width: 0;\n    letter-spacing: 0.3px;\n    padding-left: 5px;\n    line-height: 19px;\n}\n.component_menubar .action-item {\n    padding-right: 10px;\n    display: flex;\n    align-items: center;\n}\n.component_menubar .action-item button {\n    padding: 0;\n}\n.component_menubar .action-item .component_icon {\n    height: 19px;\n    width: 19px;\n    cursor: pointer;\n    padding: 0 3px;\n}\n.component_menubar .action-item .download-button .component_icon {\n    padding-right: 1px;\n}\n.component_menubar .action-item .btn {\n    background: #e2e2e2;\n    color: rgba(0,0,0,0.6);\n    padding: 0 5px;\n    border-radius: 2px;\n    cursor: pointer;\n    text-transform: uppercase;\n    font-weight: bold;\n}\n.component_menubar select {\n    background: #f2f2f2;\n    color: var(--color);\n    border: 2px solid #f2f2f2;\n    border-radius: 2px;\n    padding: 0 3px 0 3px;\n    margin: 0 3px;\n}\n\n.dark-mode .component_menubar {\n    background: var(--bg-color);\n}\n.dark-mode .component_menubar .container {\n    color: inherit;\n}\n\n.touch-yes .component_menubar .action-item .component_icon {\n    height: 21px;\n    width: 21px;\n}\n\n/* icons in additional menu */\n.component_menubar .action-item [is=\"component-dropdown\"] .dropdown_button {\n    border: none;\n    padding: 2px 0px;\n    margin: 0;\n}\n\n/* chromecast */\n:root {\n    --disconnected-color: #F2F2F2;\n    --connected-color: var(--primary);\n}\n\ngoogle-cast-launcher {\n    display: inline-block;\n    height: 23px;\n    width: 23px;\n    cursor: pointer;\n    padding: 0 5px;\n    position: relative;\n    top: 1px;\n    cursor: pointer;\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/component_menubar.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport { qs, safe } from \"../../lib/dom.js\";\nimport { animate, slideYIn } from \"../../lib/animate.js\";\nimport { loadCSS } from \"../../helpers/loader.js\";\nimport { isSDK } from \"../../helpers/sdk.js\";\nimport assert from \"../../lib/assert.js\";\n\nimport \"../../components/dropdown.js\";\n\nexport default class ComponentMenubar extends HTMLElement {\n    constructor() {\n        super();\n        this.classList.add(\"component_menubar\");\n        this.innerHTML = `\n            <div class=\"container\">\n                <span>\n                    <div class=\"titlebar ellipsis\" style=\"opacity:0\">${safe(this.getAttribute(\"filename\")) || \"&nbsp;\"}</div>\n                    <div class=\"action-item no-select\"></div>\n                </span>\n            </div>\n        `;\n        if (new URLSearchParams(location.search).get(\"nav\") === \"false\") {\n            const $container = assert.type(this.firstElementChild, HTMLElement);\n            $container.classList.add(\"inherit-width\");\n        }\n    }\n\n    async connectedCallback() {\n        const $title = assert.type(this.querySelector(\".titlebar\"), HTMLElement);\n        this.timeoutID = setTimeout(() => animate($title, {\n            time: 250,\n            keyframes: slideYIn(2),\n            onExit: () => $title.style.opacity = 1,\n        }), 100);\n    }\n\n    disconnectedCallback() {\n        clearTimeout(this.timeoutID);\n    }\n\n    render(buttons) {\n        const $item = assert.type(this.querySelector(\".action-item\"), HTMLElement);\n        for (let i=buttons.length-1; i>=0; i--) {\n            $item.appendChild(buttons[i]);\n        }\n        animate($item, { time: 250, keyframes: slideYIn(2) });\n    }\n\n    add($button) {\n        const $item = assert.type(this.querySelector(\".action-item\"), HTMLElement);\n        $item.prepend($button);\n        animate($button, { time: 250, keyframes: slideYIn(2) });\n        return $button;\n    }\n}\n\nexport function buttonDownload(name, link) {\n    const ICON = {\n        DOWNLOAD: \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzODQgNTEyIj4KICA8cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNIDM2MCw0NjAgSCAyNCBDIDEwLjcsNDYwIDAsNDUzLjMgMCw0NDAgdiAtMTIgYyAwLC0xMy4zIDEwLjcsLTIwIDI0LC0yMCBoIDMzNiBjIDEzLjMsMCAyNCw2LjcgMjQsMjAgdiAxMiBjIDAsMTMuMyAtMTAuNywyMCAtMjQsMjAgeiIgLz4KICA8cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNIDIyNi41NTM5LDIzNC44ODQyOCBWIDUyLjk0MzI4MyBjIDAsLTYuNjI3IC01LjM3MywtMTIgLTEyLC0xMiBoIC00NCBjIC02LjYyNywwIC0xMiw1LjM3MyAtMTIsMTIgViAyMzQuODg0MjggaCAtNTIuMDU5IGMgLTIxLjM4MiwwIC0zMi4wOSwyNS44NTEgLTE2Ljk3MSw0MC45NzEgbCA4Ni4wNTksODYuMDU5IGMgOS4zNzMsOS4zNzMgMjQuNTY5LDkuMzczIDMzLjk0MSwwIGwgODYuMDU5LC04Ni4wNTkgYyAxNS4xMTksLTE1LjExOSA0LjQxMSwtNDAuOTcxIC0xNi45NzEsLTQwLjk3MSB6IiAvPgo8L3N2Zz4K\",\n        LOADING: \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0nMTIwcHgnIGhlaWdodD0nMTIwcHgnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIiBjbGFzcz0idWlsLXJpbmctYWx0Ij4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0ibm9uZSIgY2xhc3M9ImJrIj48L3JlY3Q+CiAgPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iNDAiIHN0cm9rZT0ibm9uZSIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48L2NpcmNsZT4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjZmZmZmZmIiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCI+CiAgICA8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJzdHJva2UtZGFzaG9mZnNldCIgZHVyPSIycyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGZyb209IjAiIHRvPSI1MDIiPjwvYW5pbWF0ZT4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9InN0cm9rZS1kYXNoYXJyYXkiIGR1cj0iMnMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB2YWx1ZXM9IjE1MC42IDEwMC40OzEgMjUwOzE1MC42IDEwMC40Ij48L2FuaW1hdGU+CiAgPC9jaXJjbGU+Cjwvc3ZnPgo=\",\n    };\n    const $el = createElement(`\n        <span class=\"download-button\">\n            <a href=\"${link}\" download=\"${safe(name)}\">\n                <img class=\"component_icon\" draggable=\"false\" src=\"${ICON.DOWNLOAD}\" alt=\"download\">\n            </a>\n        </span>\n    `);\n    const $img = qs($el, \"img\");\n    qs($el, \"a\").onclick = () => {\n        if (isSDK()) return;\n        document.cookie = \"download=yes; path=/; max-age=120;\";\n        $img.setAttribute(\"src\", ICON.LOADING);\n        const id = setInterval(() => {\n            if (/download=yes/.test(document.cookie) !== false) return;\n            clearInterval(id);\n            $img.setAttribute(\"src\", ICON.DOWNLOAD);\n        }, 500);\n    };\n    return $el;\n}\n\nexport function buttonFullscreen($screen, fullscreen) {\n    let fullscreenHandler = fullscreen;\n    if (!fullscreen) {\n        if (\"webkitRequestFullscreen\" in document.body) {\n            fullscreenHandler = () => $screen.webkitRequestFullscreen();\n        } else if (\"mozRequestFullScreen\" in document.body) {\n            fullscreenHandler = () => $screen.mozRequestFullScreen();\n        }\n    }\n    if (!fullscreenHandler) return document.createDocumentFragment();\n    const $el = createElement(`\n        <button role=\"button\">\n            <img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MzguNTQzIDQzOC41NDMiPgogIDxnIHRyYW5zZm9ybT0ibWF0cml4KDAuNzI5LDAsMCwwLjcyOSw1OS40MjI1NzYsNTkuNDIyNDQxKSI+CiAgICA8cGF0aCBzdHlsZT0iZmlsbDojZjJmMmYyO2ZpbGwtb3BhY2l0eToxIiBkPSJtIDQwNy40MiwxNTkuMDI5IGMgMy42MiwzLjYxNiA3Ljg5OCw1LjQyOCAxMi44NDcsNS40MjggMi4yODIsMCA0LjY2OCwtMC40NzYgNy4xMzksLTEuNDI5IDcuNDI2LC0zLjIzNSAxMS4xMzYsLTguODUzIDExLjEzNiwtMTYuODQ2IFYgMTguMjc2IGMgMCwtNC45NDkgLTEuODA3LC05LjIzMSAtNS40MjgsLTEyLjg0NyAtMy42MSwtMy42MTcgLTcuODk4LC01LjQyNCAtMTIuODQ3LC01LjQyNCBIIDI5Mi4zNiBjIC03Ljk5MSwwIC0xMy42MDcsMy44MDUgLTE2Ljg0OCwxMS40MTkgLTMuMjMsNy40MjMgLTEuOTAyLDEzLjk5IDQsMTkuNjk4IEwgMzIwLjYyMyw3Mi4yMzQgMjE5LjI3MSwxNzMuNTg5IDExNy45MTcsNzIuMjMxIDE1OS4wMjksMzEuMTE5IGMgNS45MDEsLTUuNzA4IDcuMjMyLC0xMi4yNzUgMy45OTksLTE5LjY5OCBDIDE1OS43ODksMy44MDcgMTU0LjE3NSwwIDE0Ni4xODIsMCBIIDE4LjI3NiBDIDEzLjMyNCwwIDkuMDQxLDEuODA5IDUuNDI1LDUuNDI2IDEuODA4LDkuMDQyIDAuMDAxLDEzLjMyNCAwLjAwMSwxOC4yNzMgViAxNDYuMTggYyAwLDcuOTk2IDMuODA5LDEzLjYxIDExLjQxOSwxNi44NDYgMi4yODUsMC45NDggNC41NywxLjQyOSA2Ljg1NSwxLjQyOSA0Ljk0OCwwIDkuMjI5LC0xLjgxMiAxMi44NDcsLTUuNDI3IEwgNzIuMjM0LDExNy45MTkgMTczLjU4OCwyMTkuMjczIDcyLjIzNCwzMjAuNjIyIDMxLjEyMiwyNzkuNTA5IGMgLTUuNzExLC01LjkwMyAtMTIuMjc1LC03LjIzMSAtMTkuNzAyLC00LjAwMSAtNy42MTQsMy4yNDEgLTExLjQxOSw4Ljg1NiAtMTEuNDE5LDE2Ljg1NCB2IDEyNy45MDYgYyAwLDQuOTQ4IDEuODA3LDkuMjI5IDUuNDI0LDEyLjg0NyAzLjYxOSwzLjYxNCA3LjkwMiw1LjQyMSAxMi44NTEsNS40MjEgaCAxMjcuOTA2IGMgNy45OTYsMCAxMy42MSwtMy44MDYgMTYuODQ2LC0xMS40MTYgMy4yMzQsLTcuNDI3IDEuOTAzLC0xMy45OSAtMy45OTksLTE5LjcwNSBMIDExNy45MTcsMzY2LjMwOSAyMTkuMjcxLDI2NC45NSAzMjAuNjI0LDM2Ni4zMTEgMjc5LjUxLDQwNy40MjEgYyAtNS44OTksNS43MDggLTcuMjI4LDEyLjI3OSAtMy45OTcsMTkuNjk4IDMuMjM3LDcuNjE3IDguODU2LDExLjQyMyAxNi44NTEsMTEuNDIzIGggMTI3LjkwNyBjIDQuOTQ4LDAgOS4yMzIsLTEuODEzIDEyLjg0NywtNS40MjggMy42MTMsLTMuNjEzIDUuNDIsLTcuODk4IDUuNDIsLTEyLjg0NyBWIDI5Mi4zNjIgYyAwLC03Ljk5NCAtMy43MDksLTEzLjYxMyAtMTEuMTM2LC0xNi44NTEgLTcuODAyLC0zLjIzIC0xNC40NjIsLTEuOTAzIC0xOS45ODUsNC4wMDQgTCAzNjYuMzExLDMyMC42MjEgMjY0Ljk1MiwyMTkuMjcxIDM2Ni4zMSwxMTcuOTE3IFoiIC8+CiAgPC9nPgo8L3N2Zz4K\" alt=\"fullscreen\">\n        </button>\n    `);\n    $el.onclick = fullscreenHandler;\n    return $el;\n}\n\nexport function renderMenubar($menubar, ...buttons) {\n    assert.type($menubar, ComponentMenubar);\n    $menubar.render(buttons.filter(($button) => $button));\n    return $menubar;\n}\n\nexport async function init() {\n    return loadCSS(import.meta.url, \"./component_menubar.css\");\n}\n\nif (!customElements.get(\"component-menubar\"))\n    customElements.define(\"component-menubar\", ComponentMenubar);\n"
  },
  {
    "path": "public/assets/pages/viewerpage/mimetype.js",
    "content": "import { get as getPlugin } from \"../../model/plugin.js\";\n\nexport function opener(file = \"\", mimes) {\n    const mime = getMimeType(file, mimes);\n    const type = mime.split(\"/\")[0];\n\n    if (window.overrides && typeof window.overrides[\"xdg-open\"] === \"function\") {\n        const openerFromPlugin = window.overrides[\"xdg-open\"](mime);\n        if (openerFromPlugin !== null) {\n            return openerFromPlugin;\n        }\n    }\n\n    const p = getPlugin(mime);\n    if (p) return [p[0], { mime, ...p[1] }];\n\n    if (type === \"text\") {\n        return [\"editor\", { mime }];\n    } else if (mime === \"application/pdf\") {\n        return [\"pdf\", { mime }];\n    } else if (type === \"image\") {\n        return [\"image\", { mime }];\n    } else if ([\"application/javascript\", \"application/xml\", \"application/json\",\n        \"application/x-perl\"].indexOf(mime) !== -1) {\n        return [\"editor\", { mime }];\n    } else if ([\"audio/wave\", \"audio/mp3\", \"audio/flac\", \"audio/ogg\", \"audio/mpeg\"].indexOf(mime) !== -1) {\n        return [\"audio\", { mime }];\n    } else if (mime === \"application/x-form\") {\n        return [\"form\", { mime }];\n    } else if (mime === \"application/geo+json\" || mime === \"application/vnd.ogc.wms_xml\" || mime === \"application/vnd.shp\") {\n        return [\"map\", { mime }];\n    } else if (type === \"video\" || mime === \"application/ogg\") {\n        return [\"video\", { mime }];\n    } else if ([\"application/epub+zip\"].indexOf(mime) !== -1) {\n        return [\"ebook\", { mime }];\n    } else if (mime === \"application/x-url\") {\n        return [\"url\", { mime }];\n    } else if (type === \"application\" && mime !== \"application/text\") {\n        return [\"download\", { mime }];\n    }\n    return [\"editor\", { mime }];\n}\n\nexport function getMimeType(file, mimes = {}) {\n    return mimes[file.split(\".\").slice(-1)[0].toLowerCase()] || \"text/plain\";\n}\n"
  },
  {
    "path": "public/assets/pages/viewerpage/model_files.js",
    "content": "import { toHref, navigate } from \"../../lib/skeleton/router.js\";\nimport rxjs from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { AjaxError } from \"../../lib/error.js\";\nimport { forwardURLParams } from \"../../lib/path.js\";\nimport { getCurrentPath } from \"./common.js\";\n\nexport const options = () => ajax({\n    url: forwardURLParams(`api/files/cat?path=${encodeURIComponent(getCurrentPath())}`, [\"share\"]),\n    method: \"OPTIONS\",\n}).pipe(\n    rxjs.catchError((err) => {\n        if (err instanceof AjaxError && err.err().status === 401) {\n            navigate(toHref(\"/login?next=\" + location.pathname + location.hash + location.search));\n            return rxjs.EMPTY;\n        }\n        throw err;\n    }),\n    rxjs.map((res) => res.responseHeaders.allow.replace(/\\r/, \"\").split(\", \")),\n);\n\nexport const cat = (url) => ajax({\n    url: forwardURLParams(url, [\"share\"]),\n    method: \"GET\",\n}).pipe(\n    rxjs.map(({ response }) => response),\n);\n\nexport const save = (content) => ajax({\n    url: forwardURLParams(\"api/files/cat?path=\" + encodeURIComponent(getCurrentPath()), [\"share\"]),\n    method: \"POST\",\n    body: content,\n});\n"
  },
  {
    "path": "public/global.d.ts",
    "content": "interface Window {\n    chrome: any;\n    cast: any;\n    overrides: {\n        [key: string]: any;\n        \"xdg-open\"?: (mime: string) => void;\n    };\n    VERSION: string;\n    bundler: any;\n    BEARER_TOKEN?: string;\n}"
  },
  {
    "path": "public/index.backoffice.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <base href=\"{{ .base }}\">\n        <meta charset=\"UTF-8\">\n        <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n        <link rel=\"stylesheet\" href=\"./admin/assets/css/designsystem.css\">\n        <link rel=\"icon\" href=\"favicon.ico\">\n        <title>Admin Console</title>\n    </head>\n    <body>\n        <script type=\"module\" src=\"./admin/assets/{{ .version }}/components/loader.js\"></script>\n        <div role=\"main\" id=\"app\">\n            <component-loader delay=\"500\"></component-loader>\n        </div>\n\n        <script type=\"module\">\n          import main from \"./admin/assets/{{ .version }}/lib/skeleton/index.js\";\n          import routes from \"./admin/assets/{{ .version }}/boot/router_backoffice.js\";\n          main(document.getElementById(\"app\"), routes, {\n              spinner: `<component-loader></component-loader>`,\n              beforeStart: import(\"{{ .base }}admin/assets/{{ .version }}/boot/ctrl_boot_backoffice.js\"),\n          });\n        </script>\n\n        <script type=\"module\" src=\"./admin/assets/{{ .version }}/components/modal.js\"></script>\n        <component-modal></component-modal>\n        <script type=\"module\" src=\"./admin/assets/{{ .version }}/components/notification.js\"></script>\n        <component-notification></component-notification>\n\n        <noscript>\n            <div>\n                <h2>Error: Javascript is off</h2>\n                <p>You need to enable Javascript to run this application</p>\n            </div>\n        </noscript>\n    </body>\n</html>\n"
  },
  {
    "path": "public/index.frontoffice.html",
    "content": "{{ if .appname | eq \"Filestash\" }}\n<!--\n    ┌──────────────────────────────────────────────────────────┐\n    │                       FILESTASH                          │\n    │            https://www.filestash.app/docs/               │\n    └──────────────────────────────────────────────────────────┘\n-->\n{{ end -}}\n<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <base href=\"{{ .base }}\">\n        {{- range .bundle }}\n        <link rel=\"modulepreload\" href=\"{{ . }}\" />\n        {{- end }}\n        <link rel=\"preload\" as=\"style\" href=\"custom.css\" onload=\"this.rel='stylesheet'\">\n        <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n        <meta name=\"application-name\" content=\"{{ .appname }}\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n        <script>{{ if .license | eq \"agpl\" }}{{ template \"loader-cat\" }}{{ else }}{{ template \"loader-basic\" }}{{ end }}</script>\n    </head>\n    <body>\n        <div id=\"app\"><component-bootscreen></component-bootscreen></div>\n\n        <template id=\"body\">\n            <script type=\"module\">\n             import main from \"./assets/{{ .version }}/lib/skeleton/index.js\";\n             import routes from \"./assets/{{ .version }}/boot/router_frontoffice.js\";\n             main(document.getElementById(\"app\"), routes, {\n                 spinner: `<component-loader></component-loader>`,\n                 beforeStart: import(\"{{ .base }}assets/{{ .version }}/boot/ctrl_boot_frontoffice.js\"),\n             });\n            </script>\n            <component-modal></component-modal>\n            <component-notification></component-notification>\n        </template>\n\n        <script type=\"module\">\n         //\n         //\n         //\n         //                      /\\\n         //                     /  \\\n         //                ||  /    \\\n         //                || /______\\\n         //                |||        |\n         //               |  |        |\n         //               |  |        |\n         //               |__|________|\n         //               |___________|\n         //               |  |        |\n         //               |__|   ||   |\\\n         //                |||   ||   | \\\n         //               /|||   ||   |  \\\n         //              /_|||...||...|___\\\n         //                |||::::::::|\n         //                || \\::::::/\n         //                ||  ||__||\n         //                ||    ||\n         //                ||     \\\\_______________\n         // _______________||______`---------------\n         // |\n         // |                                             |\n                     await ignitionSequence()          // |\n         // |\n                                    liftoff()          // |\n         // |                                             |\n         // |_____________________________________________|\n         //\n         //\n         async function ignitionSequence() {\n             try {\n                 if (!HTMLScriptElement.supports?.(\"importmap\")) throw new Error(\"fastboot is not supported on this platform\");\n                 {{ load_asset \"assets/boot/bundler_init.js\" }}\n                 await Promise.all([\n                     {{- range .bundle }}\n                     \"{{ . }}\",\n                     {{- end }}\n                 ].map((src) => new Promise((onload, onerror) => document.head.appendChild(Object.assign(document.createElement(\"script\"), {\n                     type: \"module\", src, onload, onerror,\n                 })))));\n                 {{ load_asset \"assets/boot/bundler_complete.js\" }}\n             } catch (err) { console.error(err); }\n\n             await Promise.all([\n                 import(\"{{ .base }}assets/{{ .version }}/components/loader.js\"),\n                 import(\"{{ .base }}assets/{{ .version }}/components/modal.js\"),\n                 import(\"{{ .base }}assets/{{ .version }}/components/notification.js\"),\n                 import(\"{{ .base }}assets/{{ .version }}/helpers/loader.js\").then(({ loadCSS }) => {\n                     loadCSS(import.meta.url, \"{{ .base }}assets/{{ .version }}/css/designsystem.css\");\n                 }),\n             ]);\n         }\n\n         function liftoff() {\n             document.body.appendChild(document.querySelector(\"template#body\").content);\n         }\n        </script>\n\n        <noscript><div style=\"text-align:center;font-family:monospace;margin-top:5%;font-size:15px;\"><h2>Error: Javascript is off</h2></div></noscript>\n    </body>\n</html>\n"
  },
  {
    "path": "public/tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"allowJs\": true,\n        \"allowUnreachableCode\": false,\n        \"allowUnusedLabels\": false,\n        \"alwaysStrict\": true,\n        \"checkJs\": true,\n        \"exactOptionalPropertyTypes\": true,\n        \"forceConsistentCasingInFileNames\": true,\n        \"isolatedModules\": true,\n        \"noEmit\": true,\n        \"noErrorTruncation\": true,\n        \"noFallthroughCasesInSwitch\": true,\n        \"noImplicitReturns\": true,\n        \"noImplicitThis\": true,\n        \"noImplicitOverride\": true,\n        \"noPropertyAccessFromIndexSignature\": true,\n        \"noImplicitAny\": false,\n        \"noUnusedLocals\": true,\n        \"noUnusedParameters\": true,\n        \"noUncheckedIndexedAccess\": true,\n        \"strictNullChecks\": true,\n        \"strictPropertyInitialization\": true,\n        \"strictBindCallApply\": true,\n        \"strictFunctionTypes\": true,\n        \"strict\": true,\n        \"lib\": [\n            \"dom\",\n            \"dom.iterable\",\n            \"es2022\"\n        ],\n        \"module\": \"es2022\",\n        \"target\": \"es2022\",\n        \"typeRoots\": []\n    },\n    \"include\": [\n        \"assets/boot/*.js\",\n        \"assets/pages/*.js\",\n        \"global.d.ts\"\n    ],\n    \"exclude\": [\n        \"**/*.test.js\",\n        \"assets/worker/sw_cache.js\",\n        \"coverage\",\n        \"vite.config.js\",\n        \"jest.setup.js\"\n    ]\n}\n"
  },
  {
    "path": "public/vite.config.js",
    "content": "import { defineConfig } from \"vitest/config\";\n\nexport default defineConfig(({ comand, mode }) => {\n    return {\n        plugins: [],\n        test: {\n            global: true,\n            environment: \"jsdom\",\n            setupFiles: [\"./vite.setup.js\"],\n        }\n    };\n});\n"
  },
  {
    "path": "public/vite.setup.js",
    "content": "import {\n    describe, it, test, expect, vi,\n    afterEach, afterAll, beforeEach, beforeAll,\n} from \"vitest\";\n\nglobal.nextTick = () => new Promise((done) => setTimeout(done, 0));\nglobal.requestAnimationFrame = (callback) => setTimeout(callback, 0);\n\nglobal.describe = describe;\nglobal.it = it;\nglobal.test = test;\nglobal.expect = expect;\nglobal.vi = vi;\nglobal.beforeEach = beforeEach;\nglobal.beforeAll = beforeAll;\nglobal.afterEach = afterEach;\nglobal.afterAll = afterAll;\n"
  },
  {
    "path": "server/common/app.go",
    "content": "package common\n\nimport (\n\t\"context\"\n)\n\ntype App struct {\n\tBackend       IBackend\n\tBody          map[string]interface{}\n\tSession       map[string]string\n\tShare         Share\n\tContext       context.Context\n\tAuthorization string\n\tLanguages     []string\n}\n"
  },
  {
    "path": "server/common/backend.go",
    "content": "package common\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n)\n\nconst BACKEND_NIL = \"_nothing_\"\n\nvar Backend = NewDriver()\n\nfunc NewDriver() Driver {\n\treturn Driver{make(map[string]IBackend)}\n}\n\ntype Driver struct {\n\tds map[string]IBackend\n}\n\nfunc (d *Driver) Register(name string, driver IBackend) {\n\tif driver == nil {\n\t\tpanic(\"backend: register invalid nil backend\")\n\t}\n\td.ds[name] = driver\n}\n\nfunc (d *Driver) Get(name string) IBackend {\n\tb := d.ds[name]\n\tif b == nil || name == BACKEND_NIL {\n\t\treturn Nothing{}\n\t}\n\treturn b\n}\n\nfunc (d *Driver) Drivers() map[string]IBackend {\n\treturn d.ds\n}\n\ntype Nothing struct{}\n\nfunc (b Nothing) Init(params map[string]string, app *App) (IBackend, error) {\n\treturn &b, nil\n}\nfunc (b Nothing) Ls(path string) ([]os.FileInfo, error) {\n\treturn []os.FileInfo{}, nil\n}\nfunc (b Nothing) Stat(path string) (os.FileInfo, error) {\n\treturn nil, ErrNotFound\n}\nfunc (b Nothing) Cat(path string) (io.ReadCloser, error) {\n\treturn NewReadCloserFromReader(strings.NewReader(\"\")), ErrNotImplemented\n}\nfunc (b Nothing) Mkdir(path string) error {\n\treturn ErrNotImplemented\n}\nfunc (b Nothing) Rm(path string) error {\n\treturn ErrNotImplemented\n}\nfunc (b Nothing) Mv(from string, to string) error {\n\treturn ErrNotImplemented\n}\nfunc (b Nothing) Touch(path string) error {\n\treturn ErrNotImplemented\n}\nfunc (b Nothing) Save(path string, file io.Reader) error {\n\treturn ErrNotImplemented\n}\nfunc (b Nothing) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"nothing\",\n\t\t\t},\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "server/common/cache.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\t\"github.com/mitchellh/hashstructure\"\n\t\"github.com/patrickmn/go-cache\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype AppCache struct {\n\tCache *cache.Cache\n\tsync.Mutex\n}\n\nfunc (a *AppCache) Get(key interface{}) interface{} {\n\thash, err := hashstructure.Hash(key, nil)\n\tif err != nil {\n\t\treturn nil\n\t}\n\ta.Lock()\n\tdefer a.Unlock()\n\tvalue, found := a.Cache.Get(fmt.Sprintf(\"%d\", hash))\n\tif found == false {\n\t\treturn nil\n\t}\n\treturn value\n}\n\nfunc (a *AppCache) Set(key map[string]string, value interface{}) {\n\thash, err := hashstructure.Hash(key, nil)\n\tif err != nil {\n\t\treturn\n\t}\n\ta.Cache.Set(fmt.Sprint(hash), value, cache.DefaultExpiration)\n}\n\nfunc (a *AppCache) SetKey(key string, value interface{}) {\n\ta.Cache.Set(key, value, cache.DefaultExpiration)\n}\n\nfunc (a *AppCache) Del(key map[string]string) {\n\thash, _ := hashstructure.Hash(key, nil)\n\ta.Cache.Delete(fmt.Sprint(hash))\n}\n\nfunc (a *AppCache) OnEvict(fn func(string, interface{})) {\n\ta.Cache.OnEvicted(fn)\n}\n\nfunc NewAppCache(arg ...time.Duration) AppCache {\n\tvar retention time.Duration = 5\n\tvar cleanup time.Duration = 10\n\tif len(arg) > 0 {\n\t\tretention = arg[0]\n\t\tif len(arg) > 1 {\n\t\t\tcleanup = arg[1]\n\t\t}\n\t}\n\tc := AppCache{}\n\tc.Cache = cache.New(retention*time.Minute, cleanup*time.Minute)\n\treturn c\n}\n\nfunc NewQuickCache(arg ...time.Duration) AppCache {\n\tvar retention time.Duration = 5\n\tvar cleanup time.Duration = 10\n\tif len(arg) > 0 {\n\t\tretention = arg[0]\n\t\tif len(arg) > 1 {\n\t\t\tcleanup = arg[1]\n\t\t}\n\t}\n\tc := AppCache{}\n\tc.Cache = cache.New(retention*time.Second, cleanup*time.Second)\n\treturn c\n}\n\n// ============================================================================\n\ntype KeyValueStore struct {\n\tcache map[string]interface{}\n\tsync.RWMutex\n}\n\nfunc NewKeyValueStore() KeyValueStore {\n\treturn KeyValueStore{cache: make(map[string]interface{})}\n}\n\nfunc (this *KeyValueStore) Get(key string) interface{} {\n\tvar val interface{}\n\tthis.RLock()\n\tval = this.cache[key]\n\tthis.RUnlock()\n\treturn val\n}\n\nfunc (this *KeyValueStore) Set(key string, value interface{}) {\n\tthis.Lock()\n\tthis.cache[key] = value\n\tthis.Unlock()\n}\n\nfunc (this *KeyValueStore) Clear() {\n\tthis.Lock()\n\tthis.cache = make(map[string]interface{})\n\tthis.Unlock()\n}\n"
  },
  {
    "path": "server/common/config.go",
    "content": "package common\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"os/user\"\n\t\"regexp\"\n\t\"strings\"\n\t\"sync\"\n)\n\nvar Config Configuration\n\ntype Configuration struct {\n\tmu    sync.RWMutex\n\tcache sync.Map\n\n\tForm  []Form\n\tConn  []map[string]any\n}\n\ntype ConfigElement struct {\n\tcurrentElement *FormElement\n\tcfg            *Configuration\n}\n\ntype Form struct {\n\tTitle  string\n\tForm   []Form\n\tElmnts []FormElement\n}\n\ntype FormElement struct {\n\tId          string      `json:\"id,omitempty\"`\n\tName        string      `json:\"label\"`\n\tType        string      `json:\"type\"`\n\tDescription string      `json:\"description,omitempty\"`\n\tPlaceholder string      `json:\"placeholder,omitempty\"`\n\tPattern     string      `json:\"pattern,omitempty\"`\n\tOpts        []string    `json:\"options,omitempty\"`\n\tTarget      []string    `json:\"target,omitempty\"`\n\tReadOnly    bool        `json:\"readonly\"`\n\tDefault     interface{} `json:\"default\"`\n\tValue       interface{} `json:\"value\"`\n\tMultiValue  bool        `json:\"multi,omitempty\"`\n\tDatalist    []string    `json:\"datalist,omitempty\"`\n\tOrder       int         `json:\"-\"`\n\tRequired    bool        `json:\"required\"`\n}\n\nfunc InitConfig() error {\n\tConfig = NewConfiguration()\n\tif err := Config.Load(); err != nil {\n\t\treturn err\n\t}\n\tConfig.Initialise()\n\treturn nil\n}\n\nfunc NewConfiguration() Configuration {\n\treturn Configuration{\n\t\tForm: []Form{\n\t\t\tForm{\n\t\t\t\tTitle: \"general\",\n\t\t\t\tElmnts: []FormElement{\n\t\t\t\t\tFormElement{Name: \"name\", Type: \"text\", Default: APPNAME, Description: \"Name as shown in the UI\", Placeholder: \"Default: \\\"\" + APPNAME + \"\\\"\"},\n\t\t\t\t\tFormElement{Name: \"port\", Type: \"number\", Default: 8334, Description: \"Port on which the application is available.\", Placeholder: \"Default: 8334\"},\n\t\t\t\t\tFormElement{Name: \"host\", Type: \"text\", Description: \"The host people need to use to access this server\", Placeholder: WhiteLabelText(\"Eg: \\\"demo.filestash.app\\\"\", \"Eg: \\\"files.yourcompany.com\\\"\")},\n\t\t\t\t\tFormElement{Name: \"secret_key\", Type: \"password\", Required: true, Pattern: \"[a-zA-Z0-9]{16}\", Description: \"The key that's used to encrypt and decrypt content. Update this settings will invalidate existing user sessions and shared links, use with caution!\"},\n\t\t\t\t\tFormElement{Name: \"force_ssl\", Type: \"boolean\", Description: \"Enable the web security mechanism called 'Strict Transport Security'\"},\n\t\t\t\t\tFormElement{Name: \"editor\", Type: \"select\", Default: \"emacs\", Opts: []string{\"base\", \"emacs\", \"vim\"}, Description: \"Keybinding to be use in the editor. Default: \\\"emacs\\\"\"},\n\t\t\t\t\tFormElement{Name: \"logout\", Type: \"text\", Default: \"\", Description: \"Redirection URL whenever user click on the logout button\"},\n\t\t\t\t\tFormElement{Name: \"display_hidden\", Type: \"boolean\", Default: false, Description: \"Should files starting with a dot be visible by default?\"},\n\t\t\t\t\tFormElement{Name: \"refresh_after_upload\", Type: \"boolean\", Default: false, Description: \"Refresh directory listing after upload\"},\n\t\t\t\t\tFormElement{Name: \"open_mode\", Type: \"select\", Default: \"single_click\", Opts: []string{\"single_click\", \"double_click\"}, Description: \"How files and folders are opened in the file browser\"},\n\t\t\t\t\tFormElement{Name: \"upload_button\", Type: \"boolean\", Default: false, Description: \"Display the upload button on any device\"},\n\t\t\t\t\tFormElement{Name: \"upload_pool_size\", Type: \"number\", Default: 15, Description: \"Maximum number of files upload in parallel. Default: 15\"},\n\t\t\t\t\tFormElement{Name: \"upload_chunk_size\", Type: \"number\", Default: 0, Description: \"Size of Chunks for Uploads in MB.\"},\n\t\t\t\t\tFormElement{Name: \"buffer_size\", Type: \"select\", Default: \"medium\", Opts: []string{\"small\", \"medium\", \"large\"}, Description: \"I/O buffer size for transfers. Larger buffers boost throughput on 20 GbE+ networks but use more memory.\"},\n\t\t\t\t\tFormElement{Name: \"filepage_default_view\", Type: \"select\", Default: \"grid\", Opts: []string{\"list\", \"grid\"}, Description: \"Default layout for files and folder on the file page\"},\n\t\t\t\t\tFormElement{Name: \"filepage_default_sort\", Type: \"select\", Default: \"type\", Opts: []string{\"type\", \"date\", \"name\"}, Description: \"Default order for files and folder on the file page\"},\n\t\t\t\t\tFormElement{Name: \"cookie_timeout\", Type: \"number\", Default: 60 * 24 * 7, Description: \"Authentication Cookie expiration in minutes. Default: 60 * 24 * 7 = 1 week\"},\n\t\t\t\t\tFormElement{Name: \"extended_session\", Type: \"boolean\", Default: false, Description: \"Store extra auth data in session\"},\n\t\t\t\t\tFormElement{Name: \"custom_css\", Type: \"long_text\", Default: \"\", Description: \"Setcustom css code for your instance\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tForm{\n\t\t\t\tTitle: \"features\",\n\t\t\t\tForm: []Form{\n\t\t\t\t\tForm{\n\t\t\t\t\t\tTitle: \"api\",\n\t\t\t\t\t\tElmnts: []FormElement{\n\t\t\t\t\t\t\tFormElement{Name: \"enable\", Type: \"boolean\", Default: true, Description: \"Enable/Disable the API\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tForm{\n\t\t\t\t\t\tTitle: \"share\",\n\t\t\t\t\t\tElmnts: []FormElement{\n\t\t\t\t\t\t\tFormElement{Name: \"enable\", Type: \"boolean\", Default: true, Description: \"Enable/Disable the share feature\"},\n\t\t\t\t\t\t\tFormElement{Name: \"default_access\", Type: \"select\", Default: \"editor\", Opts: []string{\"editor\", \"viewer\"}, Description: \"Default access for shared links\"},\n\t\t\t\t\t\t\tFormElement{Name: \"redirect\", Type: \"text\", Placeholder: \"redirection URL\", Description: \"When set, shared links will perform a redirection to another link. Example: https://example.com?full_path={{path}}\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tForm{\n\t\t\t\t\t\tTitle: \"protection\",\n\t\t\t\t\t\tElmnts: []FormElement{\n\t\t\t\t\t\t\tFormElement{Name: \"iframe\", Type: \"text\", Default: \"\", Description: \"list of domains who can use the application from an iframe. eg: https://example.com\"},\n\t\t\t\t\t\t\tFormElement{Name: \"enable_chromecast\", Type: \"boolean\", Default: true, Description: \"Enable users to stream content on a chromecast device. This feature requires the browser to access google's server to download the chromecast SDK.\"},\n\t\t\t\t\t\t\tFormElement{Name: \"signature\", Type: \"text\", Default: \"\", Description: \"Enforce signature when using URL parameters in the authentication process\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tForm{\n\t\t\t\tTitle: \"log\",\n\t\t\t\tElmnts: []FormElement{\n\t\t\t\t\tFormElement{Name: \"enable\", Type: \"enable\", Target: []string{\"log_level\"}, Default: true},\n\t\t\t\t\tFormElement{Name: \"level\", Type: \"select\", Default: defaultValue(\"INFO\", \"LOG_LEVEL\"), Opts: []string{\"DEBUG\", \"INFO\", \"WARNING\", \"ERROR\"}, Id: \"log_level\", Description: \"Default: \\\"INFO\\\". This setting determines the level of detail at which log events are written to the log file\"},\n\t\t\t\t\tFormElement{Name: \"telemetry\", Type: \"boolean\", Default: false, Description: \"We won't share anything with any third party. This will only to be used to improve our software\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tForm{\n\t\t\t\tTitle: \"email\",\n\t\t\t\tElmnts: []FormElement{\n\t\t\t\t\tFormElement{Name: \"server\", Type: \"text\", Default: \"smtp.gmail.com\", Description: \"Address of the SMTP email server.\", Placeholder: \"Default: smtp.gmail.com\"},\n\t\t\t\t\tFormElement{Name: \"port\", Type: \"number\", Default: 587, Description: \"Port of the SMTP email server. Eg: 587\", Placeholder: \"Default: 587\"},\n\t\t\t\t\tFormElement{Name: \"username\", Type: \"text\", Description: \"The username for authenticating to the SMTP server.\", Placeholder: \"Eg: username@gmail.com\"},\n\t\t\t\t\tFormElement{Name: \"password\", Type: \"password\", Description: \"The password associated with the SMTP username.\", Placeholder: \"Eg: Your google password\"},\n\t\t\t\t\tFormElement{Name: \"from\", Type: \"text\", Description: \"Email address visible on sent messages.\", Placeholder: \"Eg: username@gmail.com\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tForm{\n\t\t\t\tTitle: \"auth\",\n\t\t\t\tElmnts: []FormElement{\n\t\t\t\t\tFormElement{Name: \"admin\", Type: \"bcrypt\", Default: \"\", Description: \"Password of the admin section.\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tConn: []map[string]any{},\n\t}\n}\n\nfunc (this Form) MarshalJSON() ([]byte, error) {\n\treturn formToJSON(this, func(el FormElement) any { return el })\n}\n\nfunc formToJSON(f Form, fn func(FormElement) any) ([]byte, error) {\n\tvar buf bytes.Buffer\n\tbuf.WriteByte('{')\n\tfirst := true\n\tfor _, el := range f.Elmnts {\n\t\tv := fn(el)\n\t\tif v == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif !first {\n\t\t\tbuf.WriteByte(',')\n\t\t}\n\t\tfirst = false\n\t\tkey, _ := json.Marshal(strings.ReplaceAll(el.Name, \" \", \"_\"))\n\t\tval, _ := json.Marshal(v)\n\t\tbuf.Write(key)\n\t\tbuf.WriteByte(':')\n\t\tbuf.Write(val)\n\t}\n\tfor _, sub := range f.Form {\n\t\tsubBytes, _ := formToJSON(sub, fn)\n\t\tif bytes.Equal(subBytes, []byte(\"{}\")) {\n\t\t\tcontinue\n\t\t}\n\t\tif !first {\n\t\t\tbuf.WriteByte(',')\n\t\t}\n\t\tfirst = false\n\t\tkey, _ := json.Marshal(strings.ReplaceAll(sub.Title, \" \", \"_\"))\n\t\tbuf.Write(key)\n\t\tbuf.WriteByte(':')\n\t\tbuf.Write(subBytes)\n\t}\n\tbuf.WriteByte('}')\n\treturn buf.Bytes(), nil\n}\n\nfunc (this *Configuration) Load() error {\n\tcFile, err := LoadConfig()\n\tif err != nil {\n\t\tLog.Error(\"config::load %s\", err)\n\t\treturn err\n\t}\n\n\t// Extract enabled backends\n\tvar d struct {\n\t\tConnections []map[string]any `json:\"connections\"`\n\t}\n\tjson.Unmarshal(cFile, &d)\n\tthis.Conn = []map[string]any{}\n\tif d.Connections != nil {\n\t\tthis.Conn = d.Connections\n\t}\n\n\t// Hydrate Config with data coming from the config file\n\tvar raw map[string]any\n\tjson.Unmarshal(cFile, &raw)\n\tfor path, value := range flattenJSON(\"\", raw) {\n\t\tel := this.Get(path)\n\t\tif el.currentElement != nil && el.currentElement.Value != value {\n\t\t\tel.currentElement.Value = value\n\t\t}\n\t}\n\n\tthis.cache.Clear()\n\tLog.SetVisibility(this.Get(\"log.level\").String())\n\tfor _, fn := range Hooks.Get.OnConfig() {\n\t\tfn()\n\t}\n\treturn nil\n}\n\nfunc flattenJSON(prefix string, m map[string]any) map[string]any {\n\tout := map[string]any{}\n\tfor k, v := range m {\n\t\tkey := k\n\t\tif prefix != \"\" {\n\t\t\tkey = prefix + \".\" + k\n\t\t}\n\t\tswitch val := v.(type) {\n\t\tcase map[string]any:\n\t\t\tfor nk, nv := range flattenJSON(key, val) {\n\t\t\t\tout[nk] = nv\n\t\t\t}\n\t\tcase []any:\n\t\tdefault:\n\t\t\tout[key] = val\n\t\t}\n\t}\n\treturn out\n}\n\nfunc (this *Configuration) Initialise() {\n\tshouldSave := false\n\tif env := os.Getenv(\"ADMIN_PASSWORD\"); env != \"\" {\n\t\tshouldSave = true\n\t\tthis.Get(\"auth.admin\").Set(env)\n\t}\n\tif env := os.Getenv(\"APPLICATION_URL\"); env != \"\" {\n\t\tshouldSave = true\n\t\t_ = this.Get(\"general.host\").Set(env).String()\n\t}\n\tif this.Get(\"general.secret_key\").String() == \"\" {\n\t\tshouldSave = true\n\t\tkey := RandomString(16)\n\t\tthis.Get(\"general.secret_key\").Set(key)\n\t}\n\tif shouldSave {\n\t\tthis.Save()\n\t}\n\tInitSecretDerivate(this.Get(\"general.secret_key\").String())\n}\n\nfunc (this *Configuration) Save() {\n\tthis.mu.RLock()\n\tformBytes, err := formToJSON(Form{Form: this.Form}, func(el FormElement) any { return el.Value })\n\tconn, _ := json.Marshal(this.Conn)\n\tthis.mu.RUnlock()\n\tif err != nil {\n\t\tLog.Error(\"config::save marshal %s\", err.Error())\n\t\treturn\n\t}\n\tvar buf bytes.Buffer\n\tbuf.WriteByte('{')\n\tinner := formBytes[1 : len(formBytes)-1]\n\tif len(inner) > 0 {\n\t\tbuf.Write(inner)\n\t\tbuf.WriteByte(',')\n\t}\n\tbuf.WriteString(`\"connections\":`)\n\tbuf.Write(conn)\n\tbuf.WriteByte('}')\n\tif err := SaveConfig(PrettyPrint(buf.Bytes())); err != nil {\n\t\tLog.Error(\"config::save %s\", err.Error())\n\t}\n}\n\nfunc (this *Configuration) Export() interface{} {\n\treturn struct {\n\t\tEditor                  string            `json:\"editor\"`\n\t\tLicense                 string            `json:\"license\"`\n\t\tDisplayHidden           bool              `json:\"display_hidden\"`\n\t\tName                    string            `json:\"name\"`\n\t\tUploadButton            bool              `json:\"upload_button\"`\n\t\tConnections             interface{}       `json:\"connections\"`\n\t\tSharedLinkDefaultAccess string            `json:\"share_default_access\"`\n\t\tSharedLinkRedirect      string            `json:\"share_redirect\"`\n\t\tLogout                  string            `json:\"logout\"`\n\t\tMimeTypes               map[string]string `json:\"mime\"`\n\t\tUploadPoolSize          int               `json:\"upload_pool_size\"`\n\t\tUploadChunkSize         int               `json:\"upload_chunk_size\"`\n\t\tRefreshAfterUpload      bool              `json:\"refresh_after_upload\"`\n\t\tFilePageDefaultSort     string            `json:\"default_sort\"`\n\t\tFilePageDefaultView     string            `json:\"default_view\"`\n\t\tAuthMiddleware          []string          `json:\"auth\"`\n\t\tThumbnailer             []string          `json:\"thumbnailer\"`\n\t\tOrigin                  string            `json:\"origin\"`\n\t\tVersion                 string            `json:\"version\"`\n\t\tEnableChromecast        bool              `json:\"enable_chromecast\"`\n\t\tOpenMode                string            `json:\"open_mode\"`\n\t\tEnableSearch            bool              `json:\"enable_search\"`\n\t\tEnableShare             bool              `json:\"enable_share\"`\n\t\tEnableTags              bool              `json:\"enable_tags\"`\n\t}{\n\t\tEditor:                  this.Get(\"general.editor\").String(),\n\t\tLicense:                 LICENSE,\n\t\tDisplayHidden:           this.Get(\"general.display_hidden\").Bool(),\n\t\tName:                    this.Get(\"general.name\").String(),\n\t\tUploadButton:            this.Get(\"general.upload_button\").Bool(),\n\t\tConnections:             this.Conn,\n\t\tSharedLinkDefaultAccess: this.Get(\"features.share.default_access\").String(),\n\t\tSharedLinkRedirect:      this.Get(\"features.share.redirect\").String(),\n\t\tLogout:                  this.Get(\"general.logout\").String(),\n\t\tMimeTypes:               AllMimeTypes(),\n\t\tUploadPoolSize:          this.Get(\"general.upload_pool_size\").Int(),\n\t\tUploadChunkSize:         this.Get(\"general.upload_chunk_size\").Int(),\n\t\tRefreshAfterUpload:      this.Get(\"general.refresh_after_upload\").Bool(),\n\t\tFilePageDefaultSort:     this.Get(\"general.filepage_default_sort\").String(),\n\t\tFilePageDefaultView:     this.Get(\"general.filepage_default_view\").String(),\n\t\tAuthMiddleware: func() []string {\n\t\t\tif this.Get(\"middleware.identity_provider.type\").String() == \"\" {\n\t\t\t\treturn []string{}\n\t\t\t}\n\t\t\treturn regexp.MustCompile(\"\\\\s*,\\\\s*\").Split(\n\t\t\t\tthis.Get(\"middleware.attribute_mapping.related_backend\").String(), -1,\n\t\t\t)\n\t\t}(),\n\t\tThumbnailer: func() []string {\n\t\t\ttMap := Hooks.Get.Thumbnailer()\n\t\t\tout := make([]string, 0, len(tMap))\n\t\t\tfor k := range tMap {\n\t\t\t\tout = append(out, k)\n\t\t\t}\n\t\t\treturn out\n\t\t}(),\n\t\tOrigin: func() string {\n\t\t\thost := this.Get(\"general.host\").String()\n\t\t\tif host == \"\" {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\tscheme := \"http://\"\n\t\t\tif this.Get(\"general.force_ssl\").Bool() {\n\t\t\t\tscheme = \"https://\"\n\t\t\t}\n\t\t\treturn scheme + host\n\t\t}(),\n\t\tOpenMode:         this.Get(\"general.open_mode\").String(),\n\t\tVersion:          BUILD_REF,\n\t\tEnableChromecast: this.Get(\"features.protection.enable_chromecast\").Bool(),\n\t\tEnableSearch:     Hooks.Get.SearchEngine() != nil,\n\t\tEnableShare:      this.Get(\"features.share.enable\").Bool(),\n\t\tEnableTags:       Hooks.Get.Metadata() != nil,\n\t}\n}\n\nfunc (this *Configuration) Get(key string) *ConfigElement {\n\tif tmp, ok := this.cache.Load(key); ok {\n\t\treturn &ConfigElement{currentElement: tmp.(*FormElement), cfg: this}\n\t}\n\n\tvar traverse func(forms *[]Form, path []string) *FormElement\n\ttraverse = func(forms *[]Form, path []string) *FormElement {\n\t\tif len(path) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\tfor i := range *forms {\n\t\t\tcurrentForm := (*forms)[i]\n\t\t\tif currentForm.Title == path[0] {\n\t\t\t\tif len(path) == 2 {\n\t\t\t\t\t// we are on a leaf\n\t\t\t\t\t// 1) attempt to get a `formElement`\n\t\t\t\t\tfor j, el := range currentForm.Elmnts {\n\t\t\t\t\t\tif el.Name == path[1] {\n\t\t\t\t\t\t\treturn &(*forms)[i].Elmnts[j]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// 2) `formElement` does not exist, let's create it.\n\t\t\t\t\t(*forms)[i].Elmnts = append(currentForm.Elmnts, FormElement{Name: path[1], Type: \"hidden\"})\n\t\t\t\t\treturn &(*forms)[i].Elmnts[len(currentForm.Elmnts)]\n\t\t\t\t} else {\n\t\t\t\t\t// we are NOT on a leaf, let's continue our tree transversal\n\t\t\t\t\treturn traverse(&(*forms)[i].Form, path[1:])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// append a new `form` if the current key doesn't exist\n\t\t*forms = append(*forms, Form{Title: path[0]})\n\t\treturn traverse(forms, path)\n\t}\n\tthis.mu.Lock()\n\tcurrentElement := traverse(&this.Form, strings.Split(key, \".\"))\n\tthis.cache.Store(key, currentElement)\n\tthis.mu.Unlock()\n\treturn &ConfigElement{currentElement: currentElement, cfg: this}\n}\n\nfunc (this *ConfigElement) Schema(fn func(*FormElement) *FormElement) *ConfigElement {\n\tfn(this.currentElement)\n\tthis.cfg.cache.Clear()\n\treturn this\n}\n\nfunc (this *ConfigElement) Default(value interface{}) *ConfigElement {\n\tif this.currentElement == nil {\n\t\treturn this\n\t}\n\tthis.cfg.mu.Lock()\n\tshouldSave := this.currentElement.Default == nil\n\tif shouldSave {\n\t\tthis.currentElement.Default = value\n\t} else if this.currentElement.Default != value {\n\t\tLog.Debug(\"Attempt to set multiple default config value => %+v\", this.currentElement)\n\t}\n\tthis.cfg.mu.Unlock()\n\tif shouldSave {\n\t\tthis.cfg.Save()\n\t}\n\treturn this\n}\n\nfunc (this *ConfigElement) Set(value interface{}) *ConfigElement {\n\tif this.currentElement == nil {\n\t\treturn this\n\t}\n\tthis.cfg.mu.Lock()\n\tchanged := this.currentElement.Value != value\n\tif changed {\n\t\tthis.currentElement.Value = value\n\t\tthis.cfg.cache.Clear()\n\t}\n\tthis.cfg.mu.Unlock()\n\tif changed {\n\t\tthis.cfg.Save()\n\t}\n\treturn this\n}\n\nfunc (this *ConfigElement) String() string {\n\tswitch v := this.Interface().(type) {\n\tcase string:\n\t\treturn v\n\tcase []byte:\n\t\treturn string(v)\n\t}\n\treturn \"\"\n}\n\nfunc (this *ConfigElement) Int() int {\n\tswitch v := this.Interface().(type) {\n\tcase float64:\n\t\treturn int(v)\n\tcase int64:\n\t\treturn int(v)\n\tcase int:\n\t\treturn v\n\t}\n\treturn 0\n}\n\nfunc (this *ConfigElement) Bool() bool {\n\tif v, ok := this.Interface().(bool); ok {\n\t\treturn v\n\t}\n\treturn false\n}\n\nfunc (this *ConfigElement) Interface() interface{} {\n\tif this.currentElement == nil {\n\t\treturn nil\n\t}\n\tthis.cfg.mu.RLock()\n\tel := *this.currentElement\n\tthis.cfg.mu.RUnlock()\n\tif el.Value == nil {\n\t\treturn el.Default\n\t}\n\treturn el.Value\n}\n\nfunc (this *Configuration) MarshalJSON() ([]byte, error) {\n\tusername := \"n/a\"\n\tif u, err := user.Current(); err == nil {\n\t\tif u.Username != \"\" {\n\t\t\tusername = u.Username\n\t\t} else {\n\t\t\tusername = u.Name\n\t\t}\n\t}\n\treturn Form{Form: append(this.Form, Form{\n\t\tTitle: \"constant\",\n\t\tElmnts: []FormElement{\n\t\t\t{Name: \"user\", Type: \"boolean\", ReadOnly: true, Value: username},\n\t\t\t{Name: \"license\", Type: \"text\", ReadOnly: true, Value: LICENSE},\n\t\t},\n\t})}.MarshalJSON()\n}\n\nfunc defaultValue(dval string, envName string) string {\n\tif val := os.Getenv(envName); val != \"\" {\n\t\treturn val\n\t}\n\treturn dval\n}\n"
  },
  {
    "path": "server/common/config_state.go",
    "content": "package common\n\n/*\n * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNI\n * WARNING - CHANGE IN THIS FILE CAN SILENTLY BREAK OTHER INSTALLATION - WARNING\n * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARN\n *\n * Some contributors wanted to be able to load and persist config in other system\n * like S3 and provide custom encryption layer on top of it. Those contributors have\n * custom plugins which run generators that override this file before the build is\n * generated. Indeed for that specific use case we couldn't extend the runtime plugin\n * mechanism so had to fallback to this approach which would set the config loader at\n * build time, hence this warning.\n */\n\nimport (\n\t\"fmt\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\t\"io\"\n\t\"os\"\n)\n\nvar (\n\tconfigKeysToEncrypt []string = []string{\n\t\t\"middleware.identity_provider.params\",\n\t\t\"middleware.attribute_mapping.params\",\n\t}\n\tconfig_path func() string\n)\n\nfunc init() {\n\tconfig_path = func() string {\n\t\treturn GetAbsolutePath(CONFIG_PATH, \"config.json\")\n\t}\n}\n\nfunc LoadConfig() ([]byte, error) {\n\tfile, err := os.OpenFile(config_path(), os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\tos.MkdirAll(GetAbsolutePath(CONFIG_PATH), os.ModePerm)\n\t\t\treturn []byte(\"\"), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tcFile, err := io.ReadAll(file)\n\tfile.Close()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfigStr := string(cFile)\n\tfor _, jsonPathWithEncryptedData := range configKeysToEncrypt {\n\t\tp := gjson.Get(configStr, jsonPathWithEncryptedData).String()\n\t\tif p == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tkey := os.Getenv(\"CONFIG_SECRET\")\n\t\tif key == \"\" {\n\t\t\tInitSecretDerivate(gjson.Get(configStr, \"general.secret_key\").String())\n\t\t\tkey = SECRET_KEY_DERIVATE_FOR_PROOF\n\t\t}\n\t\tt, err := DecryptString(Hash(key, 16), p)\n\t\tif err != nil {\n\t\t\tLog.Warning(\"common::config_state::load cannot decrypt config path '%s': %s\", jsonPathWithEncryptedData, err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tval, err := sjson.Set(configStr, jsonPathWithEncryptedData, t)\n\t\tif err != nil {\n\t\t\tLog.Warning(\"common::config_state::load cannot put json value in config '%s': %s\", jsonPathWithEncryptedData, err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tconfigStr = val\n\t}\n\treturn []byte(configStr), nil\n}\n\nfunc SaveConfig(v []byte) error {\n\tfile, err := os.Create(config_path())\n\tif err != nil {\n\t\treturn fmt.Errorf(\n\t\t\tAPPNAME+\" needs to be able to create and edit its configuration, but it currently cannot. \"+\n\t\t\t\t\"Change the permissions to allow writing to `%s`\",\n\t\t\tconfig_path(),\n\t\t)\n\t}\n\n\tconfigStr := string(v)\n\tfor _, jsonPathWithEncryptedData := range configKeysToEncrypt {\n\t\tkey := os.Getenv(\"CONFIG_SECRET\")\n\t\tif key == \"\" {\n\t\t\tkey = SECRET_KEY_DERIVATE_FOR_PROOF\n\t\t}\n\t\tp := gjson.Get(configStr, jsonPathWithEncryptedData).String()\n\t\tif p == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tt, err := EncryptString(Hash(key, 16), p)\n\t\tif err != nil {\n\t\t\tLog.Warning(\"common::config_state::save cannot encrypt config path '%s': %s\", jsonPathWithEncryptedData, err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tval, err := sjson.Set(configStr, jsonPathWithEncryptedData, t)\n\t\tif err != nil {\n\t\t\tLog.Warning(\"common::config_state::save cannot put json value in config '%s': %s\", jsonPathWithEncryptedData, err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tconfigStr = val\n\t}\n\tfile.Write(PrettyPrint([]byte(configStr)))\n\tif err = file.Sync(); err != nil {\n\t\tfile.Close()\n\t\treturn err\n\t}\n\treturn file.Close()\n}\n"
  },
  {
    "path": "server/common/constants.go",
    "content": "package common\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\n//go:generate go run ../generator/constants.go\nvar (\n\tAPP_VERSION       = \"v0.6\"\n\tCOOKIE_NAME_AUTH  = \"auth\"\n\tCOOKIE_NAME_PROOF = \"proof\"\n\tCOOKIE_NAME_ADMIN = \"admin\"\n\tCOOKIE_PATH_ADMIN = \"/admin/api/\"\n\tCOOKIE_PATH       = \"/api/\"\n\tURL_SETUP         = \"/admin/setup\"\n)\n\nvar (\n\tCONFIG_PATH = \"state/config/\"\n\tCERT_PATH   = \"state/certs/\"\n\tPLUGIN_PATH = \"state/plugins/\"\n\tDB_PATH     = \"state/db/\"\n\tFTS_PATH    = \"state/search/\"\n\tLOG_PATH    = \"state/log/\"\n\tTMP_PATH    = \"cache/\"\n)\n\nfunc init() {\n\t// STEP1: setup app\n\trootPath := \"data/\"\n\tif p := os.Getenv(\"FILESTASH_PATH\"); p != \"\" {\n\t\trootPath = p\n\t}\n\tLOG_PATH = filepath.Join(rootPath, LOG_PATH)\n\tCONFIG_PATH = filepath.Join(rootPath, CONFIG_PATH)\n\tDB_PATH = filepath.Join(rootPath, DB_PATH)\n\tFTS_PATH = filepath.Join(rootPath, FTS_PATH)\n\tCERT_PATH = filepath.Join(rootPath, CERT_PATH)\n\tTMP_PATH = filepath.Join(rootPath, TMP_PATH)\n\tPLUGIN_PATH = filepath.Join(rootPath, PLUGIN_PATH)\n\tBASE = strings.TrimSuffix(os.Getenv(\"FILESTASH_BASE\"), \"/\")\n\tCOOKIE_PATH_ADMIN = WithBase(COOKIE_PATH_ADMIN)\n\tCOOKIE_PATH = WithBase(COOKIE_PATH)\n\tURL_SETUP = WithBase(URL_SETUP)\n\n\t// STEP2: initialise the config\n\tos.MkdirAll(GetAbsolutePath(CERT_PATH), os.ModePerm)\n\tos.MkdirAll(GetAbsolutePath(DB_PATH), os.ModePerm)\n\tos.MkdirAll(GetAbsolutePath(FTS_PATH), os.ModePerm)\n\tos.MkdirAll(GetAbsolutePath(LOG_PATH), os.ModePerm)\n\tos.MkdirAll(GetAbsolutePath(PLUGIN_PATH), os.ModePerm)\n\tos.RemoveAll(GetAbsolutePath(TMP_PATH))\n\tos.MkdirAll(GetAbsolutePath(TMP_PATH), os.ModePerm)\n}\n\nvar (\n\tAPPNAME                           string = \"Filestash\"\n\tBASE                              string\n\tBUILD_REF                         string\n\tBUILD_DATE                        string\n\tLICENSE                           string = \"agpl\"\n\tSECRET_KEY                        string\n\tSECRET_KEY_DERIVATE_FOR_PROOF     string\n\tSECRET_KEY_DERIVATE_FOR_ADMIN     string\n\tSECRET_KEY_DERIVATE_FOR_USER      string\n\tSECRET_KEY_DERIVATE_FOR_HASH      string\n\tSECRET_KEY_DERIVATE_FOR_SIGNATURE string\n)\n\nfunc InitSecretDerivate(secret string) {\n\tSECRET_KEY = secret\n\tSECRET_KEY_DERIVATE_FOR_PROOF = Hash(\"PROOF_\"+SECRET_KEY, len(SECRET_KEY))\n\tSECRET_KEY_DERIVATE_FOR_ADMIN = Hash(\"ADMIN_\"+SECRET_KEY, len(SECRET_KEY))\n\tSECRET_KEY_DERIVATE_FOR_USER = Hash(\"USER_\"+SECRET_KEY, len(SECRET_KEY))\n\tSECRET_KEY_DERIVATE_FOR_HASH = Hash(\"HASH_\"+SECRET_KEY, len(SECRET_KEY))\n\tSECRET_KEY_DERIVATE_FOR_SIGNATURE = Hash(\"SGN_\"+SECRET_KEY, len(SECRET_KEY))\n}\n\nfunc WithBase(href string) string {\n\tif BASE == \"\" {\n\t\treturn href\n\t}\n\treturn BASE + href\n}\n\nfunc TrimBase(href string) string {\n\tif BASE == \"\" {\n\t\treturn href\n\t}\n\treturn strings.TrimPrefix(href, BASE)\n}\n\nfunc IsWhiteLabel() bool {\n\treturn APPNAME != \"Filestash\"\n}\n\nfunc WhiteLabelText(a, b string) string {\n\tif IsWhiteLabel() {\n\t\treturn b\n\t}\n\treturn a\n}\n"
  },
  {
    "path": "server/common/crypto.go",
    "content": "package common\n\nimport (\n\t\"bytes\"\n\t\"compress/zlib\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"hash/fnv\"\n\t\"io\"\n\t\"math/big\"\n\tmathrand \"math/rand\"\n\t\"os\"\n\t\"runtime\"\n\t\"sort\"\n\t\"sync\"\n)\n\nvar (\n\tLetters                 = []rune(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\")\n\tGCMNonce NonceGenerator = NewNonceGenerator(12)\n)\n\nfunc EncryptString(secret string, data string) (string, error) {\n\td, err := compress([]byte(data))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\td, err = EncryptAESGCM([]byte(secret), d)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.URLEncoding.EncodeToString(d), nil\n}\n\nfunc DecryptString(secret string, data string) (string, error) {\n\td, err := base64.URLEncoding.DecodeString(data)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\td, err = DecryptAESGCM([]byte(secret), d)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\td, err = decompress(d)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(d), nil\n}\n\nfunc Hash(str string, n int) string {\n\thasher := sha256.New()\n\thasher.Write([]byte(str))\n\treturn hashSize(hasher.Sum(nil), n)\n}\n\nfunc QuickHash(str string, n int) string {\n\thasher := fnv.New64()\n\thasher.Write([]byte(str))\n\treturn hashSize(hasher.Sum(nil), n)\n}\n\nfunc HashStream(r io.Reader, n int) string {\n\thasher := sha256.New()\n\tio.Copy(hasher, r)\n\th := hex.EncodeToString(hasher.Sum(nil))\n\tif n == 0 {\n\t\treturn h\n\t} else if n >= len(h) {\n\t\treturn h\n\t}\n\treturn h[0:n]\n}\n\nfunc hashSize(b []byte, n int) string {\n\th := \"\"\n\tfor i := 0; i < len(b); i++ {\n\t\tif n > 0 && len(h) >= n {\n\t\t\tbreak\n\t\t}\n\t\th += ReversedBaseChange(Letters, int(b[i]))\n\t}\n\n\tif len(h) > n {\n\t\treturn h[0 : len(h)-1]\n\t}\n\treturn h\n}\n\nfunc ReversedBaseChange(alphabet []rune, i int) string {\n\tstr := \"\"\n\tfor {\n\t\tstr += string(alphabet[i%len(alphabet)])\n\t\ti = i / len(alphabet)\n\t\tif i == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn str\n}\n\nfunc RandomString(n int) string {\n\tb := make([]rune, n)\n\tfor i := range b {\n\t\tmax := *big.NewInt(int64(len(Letters)))\n\t\tr, err := rand.Int(rand.Reader, &max)\n\t\tif err != nil {\n\t\t\tb[i] = Letters[0]\n\t\t} else {\n\t\t\tb[i] = Letters[r.Int64()]\n\t\t}\n\t}\n\treturn string(b)\n}\n\nfunc QuickString(n int) string {\n\tb := make([]rune, n)\n\tfor i := range b {\n\t\tb[i] = Letters[mathrand.Intn(len(Letters))]\n\t}\n\treturn string(b)\n}\n\nfunc EncryptAESGCM(key []byte, plaintext []byte) ([]byte, error) {\n\tc, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tgcm, err := cipher.NewGCM(c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnonce := GCMNonce.Next()\n\tif gcm.NonceSize() != len(nonce) {\n\t\tLog.Error(\"common::crypto nonce size isn't '12' but '%d'\", gcm.NonceSize())\n\t\treturn nil, ErrNotValid\n\t}\n\treturn gcm.Seal(nonce, nonce, plaintext, nil), nil\n}\n\nfunc DecryptAESGCM(key []byte, ciphertext []byte) ([]byte, error) {\n\tc, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgcm, err := cipher.NewGCM(c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnonceSize := gcm.NonceSize()\n\tif len(ciphertext) < nonceSize {\n\t\treturn nil, NewError(\"ciphertext too short\", 500)\n\t}\n\n\tnonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]\n\treturn gcm.Open(nil, nonce, ciphertext, nil)\n}\n\nfunc compress(something []byte) ([]byte, error) {\n\tvar b bytes.Buffer\n\tw := zlib.NewWriter(&b)\n\tw.Write(something)\n\tw.Close()\n\treturn b.Bytes(), nil\n}\n\nfunc decompress(something []byte) ([]byte, error) {\n\tb := bytes.NewBuffer(something)\n\tr, err := zlib.NewReader(b)\n\tif err != nil {\n\t\treturn []byte(\"\"), nil\n\t}\n\tr.Close()\n\treturn io.ReadAll(r)\n}\n\nfunc sign(something []byte) ([]byte, error) {\n\treturn something, nil\n}\n\nfunc verify(something []byte) ([]byte, error) {\n\treturn something, nil\n}\n\n// Create a unique ID that can be use to identify different session\nfunc GenerateID(params map[string]string) string {\n\tp := \"\"\n\torderedKeys := make([]string, len(params))\n\tfor key, _ := range params {\n\t\torderedKeys = append(orderedKeys, key)\n\t}\n\tsort.Strings(orderedKeys)\n\n\tfor _, key := range orderedKeys {\n\t\tswitch key {\n\t\tcase \"password\":\n\t\tcase \"path\":\n\t\tcase \"session\":\n\t\tcase \"timestamp\":\n\t\tdefault:\n\t\t\tif val := params[key]; val != \"\" {\n\t\t\t\tp += key + \"=>\" + params[key] + \", \"\n\t\t\t}\n\t\t}\n\t}\n\tif p == \"\" {\n\t\treturn \"na\"\n\t}\n\tp += \"salt=>\" + SECRET_KEY\n\treturn Hash(p, 20)\n}\n\n// Create an ID that identify a machine\nfunc GenerateMachineID() string {\n\tif runtime.GOOS == \"linux\" {\n\t\tif f, err := os.OpenFile(\"/etc/machine-id\", os.O_RDONLY, os.ModePerm); err == nil {\n\t\t\tdefer f.Close()\n\t\t\tb := make([]byte, 32)\n\t\t\tif _, err = f.Read(b); err == nil {\n\t\t\t\treturn string(b)\n\t\t\t}\n\t\t} else if f, err := os.OpenFile(\"/var/lib/dbus/machine-id\", os.O_RDONLY, os.ModePerm); err == nil {\n\t\t\tdefer f.Close()\n\t\t\tb := make([]byte, 32)\n\t\t\tif _, err = f.Read(b); err == nil {\n\t\t\t\treturn string(b)\n\t\t\t}\n\t\t}\n\t}\n\treturn \"na\"\n}\n\ntype NonceGenerator struct {\n\tcurrent []byte\n\tcount   int\n\t*sync.Mutex\n}\n\nfunc NewNonceGenerator(size int) NonceGenerator {\n\tfirstNonce := make([]byte, size)\n\tio.ReadFull(rand.Reader, firstNonce)\n\tvar m sync.Mutex\n\treturn NonceGenerator{firstNonce, size, &m}\n}\n\nfunc (this *NonceGenerator) Next() []byte {\n\tthis.Lock()\n\tnewNonce := make([]byte, this.count)\n\tfor i := len(this.current) - 1; i >= 0; i-- {\n\t\tif this.current[i] < 255 {\n\t\t\tthis.current[i] += 1\n\t\t\tbreak\n\t\t}\n\t\tthis.current[i] = 0\n\t}\n\tnewNonce = this.current\n\tthis.Unlock()\n\treturn newNonce\n}\n"
  },
  {
    "path": "server/common/debug.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n)\n\nfunc PrintMemUsage() {\n\tvar m runtime.MemStats\n\truntime.ReadMemStats(&m)\n\t// For info on each, see: https://golang.org/pkg/runtime/#MemStats\n\tfmt.Printf(\"Alloc = %v MiB\", bToMb(m.Alloc))\n\tfmt.Printf(\"\\tTotalAlloc = %v MiB\", bToMb(m.TotalAlloc))\n\tfmt.Printf(\"\\tSys = %v MiB\", bToMb(m.Sys))\n\tfmt.Printf(\"\\tObjects = %d\", m.HeapObjects)\n\tfmt.Printf(\"\\tNumGC = %v\\n\", m.NumGC)\n}\n\nfunc bToMb(b uint64) uint64 {\n\treturn b / 1024 / 1024\n}\n"
  },
  {
    "path": "server/common/default.go",
    "content": "package common\n\nimport (\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n)\n\nvar USER_AGENT = fmt.Sprintf(\"Filestash/%s.%s (http://filestash.app)\", APP_VERSION, BUILD_DATE)\n\nfunc init() {\n\tif IsWhiteLabel() {\n\t\tUSER_AGENT = APPNAME\n\t}\n}\n\nvar HTTPClient = http.Client{\n\tTimeout: 5 * time.Hour,\n\tTransport: NewTransformedTransport(&http.Transport{\n\t\tDial: (&net.Dialer{\n\t\t\tTimeout:   10 * time.Second,\n\t\t\tKeepAlive: 10 * time.Second,\n\t\t}).Dial,\n\t\tTLSHandshakeTimeout:   5 * time.Second,\n\t\tIdleConnTimeout:       60 * time.Second,\n\t\tResponseHeaderTimeout: 60 * time.Second,\n\t}),\n}\n\nvar HTTP = http.Client{\n\tTimeout: 10000 * time.Millisecond,\n\tTransport: NewTransformedTransport(&http.Transport{\n\t\tDial: (&net.Dialer{\n\t\t\tTimeout:   5000 * time.Millisecond,\n\t\t\tKeepAlive: 5000 * time.Millisecond,\n\t\t}).Dial,\n\t\tTLSHandshakeTimeout:   5000 * time.Millisecond,\n\t\tIdleConnTimeout:       5000 * time.Millisecond,\n\t\tResponseHeaderTimeout: 5000 * time.Millisecond,\n\t}),\n}\n\nvar DefaultTLSConfig = tls.Config{\n\tMinVersion: tls.VersionTLS12,\n\tCipherSuites: []uint16{\n\t\ttls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,\n\t\ttls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\n\t\ttls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,\n\t\ttls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,\n\t\ttls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\n\t\ttls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,\n\t},\n\tPreferServerCipherSuites: true,\n\tCurvePreferences: []tls.CurveID{\n\t\ttls.CurveP256,\n\t\ttls.X25519,\n\t},\n}\n\nfunc NewTransformedTransport(transport *http.Transport) http.RoundTripper {\n\treturn &TransformedTransport{transport}\n}\n\ntype TransformedTransport struct {\n\tOrig http.RoundTripper\n}\n\nfunc (this *TransformedTransport) RoundTrip(req *http.Request) (*http.Response, error) {\n\treq.Header.Add(\"User-Agent\", USER_AGENT)\n\treturn this.Orig.RoundTrip(req)\n}\n"
  },
  {
    "path": "server/common/dummy.go",
    "content": "package common\n\nimport (\n\t\"io\"\n\tslog \"log\"\n)\n\nfunc NewNilLogger() *slog.Logger {\n\treturn slog.New(dummyWriter{}, \"\", slog.LstdFlags)\n}\n\ntype dummyWriter struct {\n\tio.Writer\n}\n\nfunc (this dummyWriter) Write(p []byte) (n int, err error) {\n\treturn len(p), nil\n}\n"
  },
  {
    "path": "server/common/error.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc NewError(message string, status int) AppError {\n\tif status == 0 {\n\t\tstatus = 500\n\t}\n\treturn AppError{message, status}\n}\n\nvar (\n\tErrNotFound             = NewError(\"Not Found\", 404)\n\tErrNotAllowed           = NewError(\"Not Allowed\", 403)\n\tErrPermissionDenied     = NewError(\"Permission Denied\", 403)\n\tErrNotValid             = NewError(\"Not Valid\", 405)\n\tErrConflict             = NewError(\"Already exist\", 409)\n\tErrNotReachable         = NewError(\"Cannot establish a connection\", 502)\n\tErrInvalidPassword      = NewError(\"Invalid Password\", 403)\n\tErrNotImplemented       = NewError(\"Not Implemented\", 501)\n\tErrNotSupported         = NewError(\"Not supported\", 501)\n\tErrFilesystemError      = NewError(\"Can't use filesystem\", 503)\n\tErrMissingDependency    = NewError(\"Missing dependency\", 424)\n\tErrNotAuthorized        = NewError(\"Not authorised\", 401)\n\tErrAuthenticationFailed = NewError(\"Invalid account\", 400)\n\tErrCongestion           = NewError(\"Traffic congestion, try again later\", 500)\n\tErrTimeout              = NewError(\"Timeout\", 500)\n\tErrInternal             = NewError(\"Internal Error\", 500)\n)\n\nfunc IsATranslatedError(err error) bool {\n\tif err == ErrNotFound || err == ErrNotAllowed || err == ErrPermissionDenied ||\n\t\terr == ErrNotValid || err == ErrInvalidPassword || err == ErrNotImplemented ||\n\t\terr == ErrNotSupported || err == ErrFilesystemError || err == ErrMissingDependency ||\n\t\terr == ErrNotAuthorized || err == ErrAuthenticationFailed || err == ErrCongestion ||\n\t\terr == ErrTimeout || err == ErrInternal {\n\t\treturn true\n\t}\n\treturn false\n}\n\ntype AppError struct {\n\tmessage string\n\tstatus  int\n}\n\nfunc (e AppError) Error() string {\n\treturn fmt.Sprintf(\"%s\", e.message)\n}\nfunc (e AppError) Status() int {\n\treturn e.status\n}\n\nfunc HTTPError(err error) AppError {\n\tswitch err.Error() {\n\tcase \"Not Found\":\n\t\treturn ErrNotFound\n\tcase \"Not Allowed\":\n\t\treturn ErrNotAllowed\n\tcase \"Permission Denied\":\n\t\treturn ErrPermissionDenied\n\tcase \"Not Valid\":\n\t\treturn ErrNotValid\n\tcase \"Already exist\":\n\t\treturn ErrConflict\n\tcase \"Cannot establish a connection\":\n\t\treturn ErrNotReachable\n\tcase \"Invalid Password\":\n\t\treturn ErrInvalidPassword\n\tcase \"Not Implemented\":\n\t\treturn ErrNotImplemented\n\tcase \"Not supported\":\n\t\treturn ErrNotSupported\n\tcase \"Can't use filesystem\":\n\t\treturn ErrFilesystemError\n\tcase \"Missing dependency\":\n\t\treturn ErrMissingDependency\n\tcase \"Not authorised\":\n\t\treturn ErrNotAuthorized\n\tcase \"Invalid account\":\n\t\treturn ErrAuthenticationFailed\n\tcase \"Traffic congestion, try again later\":\n\t\treturn ErrCongestion\n\tcase \"Timeout\":\n\t\treturn ErrTimeout\n\tcase \"Internal Error\":\n\t\treturn ErrInternal\n\tdefault:\n\t\treturn NewError(err.Error(), http.StatusBadRequest)\n\t}\n}\n\nfunc HTTPFriendlyStatus(n int) string {\n\tif n < 400 && n > 600 {\n\t\treturn \"Humm\"\n\t}\n\tswitch n {\n\tcase 400:\n\t\treturn \"Bad Request\"\n\tcase 401:\n\t\treturn \"Unauthorized\"\n\tcase 402:\n\t\treturn \"Payment Required\"\n\tcase 403:\n\t\treturn \"Forbidden\"\n\tcase 404:\n\t\treturn \"Not Found\"\n\tcase 405:\n\t\treturn \"Not Allowed\"\n\tcase 406:\n\t\treturn \"Not Acceptable\"\n\tcase 407:\n\t\treturn \"Authentication Required\"\n\tcase 408:\n\t\treturn \"Timeout\"\n\tcase 409:\n\t\treturn \"Conflict\"\n\tcase 410:\n\t\treturn \"Gone\"\n\tcase 411:\n\t\treturn \"Length Required\"\n\tcase 412:\n\t\treturn \"Failed\"\n\tcase 413:\n\t\treturn \"Too Large\"\n\tcase 414:\n\t\treturn \"URI Too Long\"\n\tcase 415:\n\t\treturn \"Unsupported Media\"\n\tcase 416:\n\t\treturn \"Not Like This\"\n\tcase 417:\n\t\treturn \"Unexpected\"\n\tcase 418:\n\t\treturn \"I'm a teapot\"\n\tcase 421:\n\t\treturn \"Redirection Problem\"\n\tcase 422:\n\t\treturn \"Unprocessable\"\n\tcase 423:\n\t\treturn \"Locked\"\n\tcase 424:\n\t\treturn \"Failed Dependency\"\n\tcase 426:\n\t\treturn \"Upgrade Required\"\n\tcase 428:\n\t\treturn \"Need Something\"\n\tcase 429:\n\t\treturn \"Too Many Requests\"\n\tcase 431:\n\t\treturn \"Request Too Large\"\n\tcase 451:\n\t\treturn \"Not Available\"\n\tcase 500:\n\t\treturn \"Internal Server Error\"\n\tcase 501:\n\t\treturn \"Not Implemented\"\n\tcase 502:\n\t\treturn \"Bad Gateway\"\n\tcase 503:\n\t\treturn \"Service Unavailable\"\n\tcase 504:\n\t\treturn \"Gateway Timeout\"\n\tcase 505:\n\t\treturn \"Unsupported HTTP Version\"\n\tcase 506:\n\t\treturn \"Need To Negotiate\"\n\tcase 507:\n\t\treturn \"Insufficient Storage\"\n\tcase 508:\n\t\treturn \"Loop Detected\"\n\tcase 510:\n\t\treturn \"Not Extended\"\n\tcase 511:\n\t\treturn \"Authentication Required\"\n\tdefault:\n\t\treturn \"Oops\"\n\t}\n}\n"
  },
  {
    "path": "server/common/files.go",
    "content": "package common\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/bmatcuk/doublestar/v4\"\n)\n\nvar MOCK_CURRENT_DIR string\n\nfunc GetCurrentDir() string {\n\tif MOCK_CURRENT_DIR != \"\" {\n\t\treturn MOCK_CURRENT_DIR\n\t}\n\tex, _ := os.Executable()\n\treturn filepath.Dir(ex)\n}\n\nfunc GetAbsolutePath(base string, opts ...string) string {\n\tfullPath := base\n\tif strings.HasPrefix(base, \"/\") == false { // relative filepath are relative to the binary\n\t\tfullPath = filepath.Join(GetCurrentDir(), base)\n\t}\n\tif len(opts) == 0 {\n\t\treturn fullPath\n\t}\n\treturn filepath.Join(append([]string{fullPath}, opts...)...)\n}\n\nfunc IsDirectory(path string) bool {\n\tif path == \"\" {\n\t\treturn false\n\t}\n\tif path[len(path)-1:] != \"/\" {\n\t\treturn false\n\t}\n\treturn true\n}\n\n/*\n * Join 2 path together, result has a file\n */\nfunc JoinPath(base, file string) string {\n\tfilePath := path.Join(base, file)\n\tif strings.HasPrefix(filePath, base) == false {\n\t\treturn base\n\t}\n\treturn filePath\n}\n\nfunc EnforceDirectory(path string) string {\n\tif path == \"\" {\n\t\treturn \"/\"\n\t} else if path[len(path)-1:] == \"/\" {\n\t\treturn path\n\t}\n\treturn path + \"/\"\n}\n\nfunc SplitPath(path string) (root string, filename string) {\n\tif path == \"\" {\n\t\tpath = \"/\"\n\t}\n\tif IsDirectory(path) == false {\n\t\tfilename = filepath.Base(path)\n\t}\n\tif root = strings.TrimSuffix(path, filename); root == \"\" {\n\t\troot = \"/\"\n\t}\n\treturn root, filename\n}\n\nfunc SafeOsMkdir(path string, mode os.FileMode) error {\n\tif err := safePath(path); err != nil {\n\t\tLog.Debug(\"common::files safeOsMkdir err[%s] path[%s]\", err.Error(), path)\n\t\treturn ErrFilesystemError\n\t}\n\treturn processError(os.Mkdir(path, mode))\n}\n\nfunc SafeOsRemove(path string) error {\n\tif err := safePath(path); err != nil {\n\t\tLog.Debug(\"common::files safeOsRemove err[%s] path[%s]\", err.Error(), path)\n\t\treturn ErrFilesystemError\n\t}\n\treturn processError(os.Remove(path))\n}\n\nfunc SafeOsRemoveAll(path string) error {\n\tif err := safePath(path); err != nil {\n\t\tLog.Debug(\"common::files safeOsRemoveAll err[%s] path[%s]\", err.Error(), path)\n\t\treturn ErrFilesystemError\n\t}\n\treturn processError(os.RemoveAll(path))\n}\n\nfunc SafeOsRename(from string, to string) error {\n\tif err := safePath(from); err != nil {\n\t\tLog.Debug(\"common::files safeOsRename err[%s] from[%s]\", err.Error(), from)\n\t\treturn ErrFilesystemError\n\t} else if err := safePath(to); err != nil {\n\t\tLog.Debug(\"common::files safeOsRemove err[%s] to[%s]\", err.Error(), to)\n\t\treturn ErrFilesystemError\n\t}\n\treturn processError(os.Rename(from, to))\n}\n\nfunc GlobMatch(pattern, name string) bool {\n\tm, _ := doublestar.Match(pattern, name)\n\treturn m\n}\n\nfunc safePath(path string) error {\n\tp, err := filepath.EvalSymlinks(path)\n\tif err != nil {\n\t\tif errors.Is(err, os.ErrNotExist) == false {\n\t\t\treturn err\n\t\t}\n\t\tparentPath := filepath.Join(path, \"../\")\n\t\treturn safePath(parentPath)\n\t}\n\tif p != filepath.Clean(path) {\n\t\tLog.Debug(\"common::files safePath path[%s] p[%s]\", path, p)\n\t\treturn ErrFilesystemError\n\t}\n\treturn nil\n}\n\nfunc processError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif pe, ok := err.(*os.PathError); ok {\n\t\treturn pe.Err\n\t}\n\tif le, ok := err.(*os.LinkError); ok {\n\t\treturn le.Err\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "server/common/files_all.go",
    "content": "// +build !linux\n\npackage common\n\nimport (\n\t\"os\"\n)\n\nfunc SafeOsOpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {\n\tif err := safePath(path); err != nil {\n\t\tLog.Debug(\"common::files safeOsOpenFile err[%s] path[%s]\", err.Error(), path)\n\t\treturn nil, ErrFilesystemError\n\t}\n\treturn os.OpenFile(path, flag, perm)\n}\n"
  },
  {
    "path": "server/common/files_linux.go",
    "content": "package common\n\nimport (\n\t\"errors\"\n\t\"io/fs\"\n\t\"os\"\n\t\"syscall\"\n)\n\nfunc SafeOsOpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {\n\tif err := safePath(path); err != nil {\n\t\tLog.Debug(\"common::files safeOsOpenFile err[%s] path[%s]\", err.Error(), path)\n\t\treturn nil, ErrFilesystemError\n\t}\n\tf, err := os.OpenFile(path, flag|syscall.O_NOFOLLOW, perm)\n\tif err != nil {\n\t\tif errors.Is(err, fs.ErrNotExist) {\n\t\t\treturn nil, ErrNotFound\n\t\t}\n\t\treturn nil, processError(err)\n\t}\n\treturn f, err\n}\n"
  },
  {
    "path": "server/common/log.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\tslog \"log\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar (\n\tLog     = &log{enable: true}\n\tlogfile *os.File\n)\n\nfunc InitLogger() (err error) {\n\tlogfile, err = os.OpenFile(GetAbsolutePath(LOG_PATH, \"access.log\"), os.O_APPEND|os.O_WRONLY|os.O_CREATE, os.ModePerm)\n\tif err != nil {\n\t\tslog.Printf(\"ERROR log file: %+v\", err)\n\t\treturn err\n\t}\n\tlogfile.WriteString(\"\")\n\treturn nil\n}\n\ntype log struct {\n\tenable bool\n\tdebug  bool\n\tinfo   bool\n\twarn   bool\n\terror  bool\n}\n\nfunc (l *log) Info(format string, v ...interface{}) {\n\tif l.info && l.enable {\n\t\tmessage := fmt.Sprintf(\"%s SYST INFO \", l.now())\n\t\tmessage = fmt.Sprintf(message+format+\"\\n\", v...)\n\n\t\tlogfile.WriteString(message)\n\t\tfmt.Print(strings.Replace(message, \"%\", \"%%\", -1))\n\t}\n}\n\nfunc (l *log) Warning(format string, v ...interface{}) {\n\tif l.warn && l.enable {\n\t\tmessage := fmt.Sprintf(\"%s SYST WARN \", l.now())\n\t\tmessage = fmt.Sprintf(message+format+\"\\n\", v...)\n\n\t\tlogfile.WriteString(message)\n\t\tfmt.Print(strings.Replace(message, \"%\", \"%%\", -1))\n\t}\n}\n\nfunc (l *log) Error(format string, v ...interface{}) {\n\tif l.error && l.enable {\n\t\tmessage := fmt.Sprintf(\"%s SYST ERROR \", l.now())\n\t\tmessage = fmt.Sprintf(message+format+\"\\n\", v...)\n\n\t\tlogfile.WriteString(message)\n\t\tfmt.Print(strings.Replace(message, \"%\", \"%%\", -1))\n\t}\n}\n\nfunc (l *log) Debug(format string, v ...interface{}) {\n\tif l.debug && l.enable {\n\t\tmessage := fmt.Sprintf(\"%s SYST DEBUG \", l.now())\n\t\tmessage = fmt.Sprintf(message+format+\"\\n\", v...)\n\n\t\tlogfile.WriteString(message)\n\t\tfmt.Print(strings.Replace(message, \"%\", \"%%\", -1))\n\t}\n}\n\nfunc (l *log) Stdout(format string, v ...interface{}) {\n\tmessage := fmt.Sprintf(\"%s \", l.now())\n\tmessage = fmt.Sprintf(message+format+\"\\n\", v...)\n\n\tlogfile.WriteString(message)\n\tfmt.Print(strings.Replace(message, \"%\", \"%%\", -1))\n}\n\nfunc (l *log) now() string {\n\treturn time.Now().Format(\"2006/01/02 15:04:05\")\n}\n\nfunc (l *log) Close() {\n\tlogfile.Close()\n}\n\nfunc (l *log) SetVisibility(str string) {\n\tswitch str {\n\tcase \"WARNING\":\n\t\tl.debug = false\n\t\tl.info = false\n\t\tl.warn = true\n\t\tl.error = true\n\tcase \"ERROR\":\n\t\tl.debug = false\n\t\tl.info = false\n\t\tl.warn = false\n\t\tl.error = true\n\tcase \"DEBUG\":\n\t\tl.debug = true\n\t\tl.info = true\n\t\tl.warn = true\n\t\tl.error = true\n\tcase \"INFO\":\n\t\tl.debug = false\n\t\tl.info = true\n\t\tl.warn = true\n\t\tl.error = true\n\tdefault:\n\t\tl.debug = false\n\t\tl.info = true\n\t\tl.warn = true\n\t\tl.error = true\n\t}\n}\n\nfunc (l *log) Enable(val bool) {\n\tl.enable = val\n}\n"
  },
  {
    "path": "server/common/mime.go",
    "content": "package common\n\nimport (\n\t\"path/filepath\"\n\t\"strings\"\n)\n\n//go:generate go run ../generator/mime.go\nvar MimeTypes map[string]string = make(map[string]string, 0)\n\nfunc GetMimeType(p string) string {\n\text := filepath.Ext(p)\n\tif ext != \"\" {\n\t\text = ext[1:]\n\t}\n\text = strings.ToLower(ext)\n\tmType := MimeTypes[ext]\n\tif mType == \"\" {\n\t\treturn \"application/octet-stream\"\n\t}\n\treturn mType\n}\n\nfunc AllMimeTypes() map[string]string {\n\treturn MimeTypes\n}\n"
  },
  {
    "path": "server/common/plugin.go",
    "content": "package common\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/gorilla/mux\"\n)\n\ntype Register struct{}\ntype Get struct{}\n\nvar Hooks = struct {\n\tGet      Get\n\tRegister Register\n}{\n\tGet:      Get{},\n\tRegister: Register{},\n}\n\ntype Options struct {\n\tID string\n}\n\ntype Option func(*Options)\n\nfunc WithID(id string) Option {\n\treturn func(o *Options) {\n\t\to.ID = id\n\t}\n}\n\n/*\n * ProcessFileContentBeforeSend is a processing hooks used in plugins like:\n * 1. pluggable image transcoding service: plg_image_light, plg_image_bimg, plg_image_golang\n * 2. video transcoding service: plg_video_transcode\n * 3. disallow certain type of file: plg_security_svg\n */\nvar process_file_content_before_send []func(io.ReadCloser, *App, *http.ResponseWriter, *http.Request) (io.ReadCloser, bool, error)\n\nfunc (this Register) ProcessFileContentBeforeSend(fn func(io.ReadCloser, *App, *http.ResponseWriter, *http.Request) (io.ReadCloser, bool, error)) {\n\tprocess_file_content_before_send = append(process_file_content_before_send, fn)\n}\nfunc (this Get) ProcessFileContentBeforeSend() []func(io.ReadCloser, *App, *http.ResponseWriter, *http.Request) (io.ReadCloser, bool, error) {\n\treturn process_file_content_before_send\n}\n\n/*\n * HttpEndpoint is a hook that makes it possible to register new endpoint in the application.\n * It is used in plugin like:\n * 1. plg_video_transcoder to server the transcoded video segment via hls\n * 2. plg_editor_onlyoffice to server the content for a custom type in an iframe\n * 3. plg_handler_syncthing to create better integration with syncthing\n * 4. plg_handler_console to server a full blown console for debugging the application\n */\nvar http_endpoint []func(*mux.Router) error\n\nfunc (this Register) HttpEndpoint(fn func(*mux.Router) error) {\n\thttp_endpoint = append(http_endpoint, fn)\n}\nfunc (this Get) HttpEndpoint() []func(*mux.Router) error {\n\treturn http_endpoint\n}\n\n/*\n * Override some urls with static content. The main use case for this is to enable\n * plugins to change the frontend code and overwrite some core components\n */\nfunc (this Register) Static(www fs.FS, chroot string) {\n\tfs.WalkDir(www, \".\", func(path string, d fs.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t} else if d.IsDir() {\n\t\t\treturn nil\n\t\t}\n\t\tthis.HttpEndpoint(func(r *mux.Router) error {\n\t\t\tr.PathPrefix(\"/\" + strings.TrimPrefix(path, chroot)).HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\tf, err := www.Open(path)\n\t\t\t\tif err != nil {\n\t\t\t\t\tw.Header().Set(\"Content-Type\", \"text/plain\")\n\t\t\t\t\tw.Write([]byte(\"plugin.go::static::err \" + err.Error()))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tw.Header().Set(\"Content-Type\", GetMimeType(filepath.Ext(path)))\n\t\t\t\tio.Copy(w, f)\n\t\t\t\tf.Close()\n\t\t\t})\n\t\t\treturn nil\n\t\t})\n\t\treturn nil\n\t})\n}\n\n/*\n * Starter is the meat that let us connect to a wide variety of server like:\n * - plg_starter_http which is the default that server the application under 8334\n * - plg_starter_tor to serve the application via tor\n * - plg_starter_web that create ssl certificate via letsencrypt\n * - plg_started_http2 to create an HTTP2 server\n * - ...\n */\nvar starter_process func(context.Context, *mux.Router)\n\nfunc (this Register) Starter(fn func(context.Context, *mux.Router)) {\n\tstarter_process = fn\n}\nfunc (this Get) Starter() func(context.Context, *mux.Router) {\n\treturn starter_process\n}\n\n/*\n * AuthenticationMiddleware is what enabled us to authenticate user via different means:\n * - plg_authentication_admin to enable connection to an admin\n * - plg_authentication_saml\n * - plg_authentication_openid\n * - plg_authentication_ldap\n * - ...\n */\nvar authentication_middleware map[string]IAuthentication = make(map[string]IAuthentication, 0)\n\nfunc (this Register) AuthenticationMiddleware(id string, am IAuthentication) {\n\tauthentication_middleware[id] = am\n}\n\nfunc (this Get) AuthenticationMiddleware() map[string]IAuthentication {\n\treturn authentication_middleware\n}\n\n/*\n * AuthorisationMiddleware is to enable custom rule for authorisation. eg: anonymous can see, registered\n * user can see/edit some files but not some others, admin can do everything\n */\nvar authorisation_middleware []IAuthorisation\n\nfunc (this Register) AuthorisationMiddleware(a IAuthorisation) {\n\tauthorisation_middleware = append(authorisation_middleware, a)\n}\n\nfunc (this Get) AuthorisationMiddleware() []IAuthorisation {\n\treturn authorisation_middleware\n}\n\n/*\n * Search is the pluggable search mechanism. By default, there's 2 options:\n * - plg_search_stateless which does stateless search based on filename only\n * - plg_search_statefull which does full text search with a sqlite data store\n * The idea here is to enable different type of usage like leveraging elastic search or solr\n * with custom stuff around it\n */\nvar search ISearch\n\nfunc (this Register) SearchEngine(s ISearch) {\n\tsearch = s\n}\n\nfunc (this Get) SearchEngine() ISearch {\n\treturn search\n}\n\n/*\n * The idea here is to enable plugin to register their own thumbnailing process, typically\n * images but could also be videos, pdf, excel documents, ...\n */\nvar thumbnailer map[string]IThumbnailer = make(map[string]IThumbnailer)\n\nfunc (this Register) Thumbnailer(mimeType string, fn IThumbnailer) {\n\tthumbnailer[mimeType] = fn\n}\n\nfunc (this Get) Thumbnailer() map[string]IThumbnailer {\n\treturn thumbnailer\n}\n\n/*\n * Pluggable Audit interface\n */\nvar audit IAuditPlugin\n\nfunc (this Register) AuditEngine(a IAuditPlugin) {\n\taudit = a\n}\n\nfunc (this Get) AuditEngine() IAuditPlugin {\n\treturn audit\n}\n\n/*\n * UI Overrides\n * They are the means by which server plugin change the frontend behaviors.\n */\nvar overrides []string\n\nfunc (this Register) FrontendOverrides(url string) {\n\toverrides = append(overrides, url)\n}\nfunc (this Get) FrontendOverrides() []string {\n\treturn overrides\n}\n\nvar xdg_open []string\n\nfunc (this Register) XDGOpen(jsString string) {\n\txdg_open = append(xdg_open, jsString)\n}\nfunc (this Get) XDGOpen() []string {\n\treturn xdg_open\n}\n\nvar cssOverride = map[string]string{}\n\nfunc (this Register) CSS(stylesheet string, opts ...Option) { // idempotent\n\toptions := Options{}\n\tfor _, opt := range opts {\n\t\topt(&options)\n\t}\n\tif options.ID == \"\" {\n\t\toptions.ID = QuickHash(stylesheet, 10)\n\t}\n\tcssOverride[options.ID] = stylesheet\n}\n\nfunc (this Get) CSS() string {\n\ts := \"\"\n\tfor _, v := range cssOverride {\n\t\ts += v + \"\\n\"\n\t}\n\treturn s\n}\n\nvar favicon struct {\n\tbinary []byte\n\tmime   string\n}\n\nfunc (this Register) Favicon(binary []byte) {\n\tfavicon.binary = binary\n\tfavicon.mime = \"image/svg+xml\"\n\tif bytes.HasPrefix(binary, []byte{0x00, 0x00, 0x01, 0x00}) {\n\t\tfavicon.mime = \"image/x-icon\"\n\t} else if bytes.HasPrefix(binary, []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}) {\n\t\tfavicon.mime = \"image/png\"\n\t} else if bytes.HasPrefix(binary, []byte{0x47, 0x49, 0x46, 0x38}) {\n\t\tfavicon.mime = \"image/vnd.microsoft.icon\"\n\t}\n}\n\nfunc (this Get) Favicon() ([]byte, string) {\n\treturn favicon.binary, favicon.mime\n}\n\nconst OverrideVideoSourceMapper = \"/overrides/video-transcoder.js\"\n\nvar afterload []func()\n\nfunc (this Register) Onload(fn func()) {\n\tafterload = append(afterload, fn)\n}\nfunc (this Get) Onload() []func() {\n\treturn afterload\n}\n\nvar onquit []func()\n\nfunc (this Register) OnQuit(fn func()) {\n\tonquit = append(onquit, fn)\n}\nfunc (this Get) OnQuit() []func() {\n\treturn onquit\n}\n\nvar configChange []func()\n\nfunc (this Register) OnConfig(fn func()) {\n\tconfigChange = append(configChange, fn)\n}\n\nfunc (this Get) OnConfig() []func() {\n\treturn configChange\n}\n\nvar middlewares []func(HandlerFunc) HandlerFunc\n\nfunc (this Register) Middleware(m func(HandlerFunc) HandlerFunc) {\n\tmiddlewares = append(middlewares, m)\n}\n\nfunc (this Get) Middleware() []func(HandlerFunc) HandlerFunc {\n\treturn middlewares\n}\n\nvar staticOverrides = map[string][]byte{}\n\nfunc (this Register) StaticPatch(patchFile []byte, opts ...Option) { // idempotent\n\toptions := Options{}\n\tfor _, opt := range opts {\n\t\topt(&options)\n\t}\n\tif options.ID == \"\" {\n\t\toptions.ID = QuickHash(string(patchFile), 10)\n\t}\n\tstaticOverrides[options.ID] = patchFile\n}\n\nfunc (this Get) StaticPatch() [][]byte {\n\ts := [][]byte{}\n\tfor _, v := range staticOverrides {\n\t\ts = append(s, v)\n\t}\n\treturn s\n}\n\nvar meta IMetadata\n\nfunc (this Register) Metadata(m IMetadata) {\n\tmeta = m\n}\n\nfunc (this Get) Metadata() IMetadata {\n\treturn meta\n}\n\nvar workflow_triggers []ITrigger\n\nfunc (this Register) WorkflowTrigger(t ITrigger) {\n\tworkflow_triggers = append(workflow_triggers, t)\n\tsort.Slice(workflow_triggers, func(i, j int) bool {\n\t\treturn workflow_triggers[i].Manifest().Order < workflow_triggers[j].Manifest().Order\n\t})\n}\nfunc (this Get) WorkflowTriggers() []ITrigger {\n\treturn workflow_triggers\n}\n\nvar workflow_actions []IAction\n\nfunc (this Register) WorkflowAction(a IAction) {\n\tworkflow_actions = append(workflow_actions, a)\n\tsort.Slice(workflow_actions, func(i, j int) bool {\n\t\treturn workflow_actions[i].Manifest().Order < workflow_actions[j].Manifest().Order\n\t})\n}\nfunc (this Get) WorkflowActions() []IAction {\n\treturn workflow_actions\n}\n\nvar directory IDirectoryService\n\nfunc (this Register) DirectoryService(d IDirectoryService) {\n\tdirectory = d\n}\n\nfunc (this Get) DirectoryService() IDirectoryService {\n\treturn directory\n}\n\nfunc init() {\n\tHooks.Register.FrontendOverrides(OverrideVideoSourceMapper)\n}\n"
  },
  {
    "path": "server/common/recovery.go",
    "content": "package common\n\nimport (\n\t\"net/http\"\n)\n\n// previous cookie configuration in canary release of 2024/10 break existing cookie and\n// can introduce weird error when a user has things in cache.\n// this code will deprecate early 2025\nfunc RecoverFromBadCookie(res http.ResponseWriter) {\n\tLog.Debug(\"common::recovery exec=RecoverFromBadCookie\")\n\thttp.SetCookie(res, &http.Cookie{\n\t\tName:     \"auth\",\n\t\tValue:    \"\",\n\t\tMaxAge:   -1,\n\t\tHttpOnly: true,\n\t\tSameSite: http.SameSiteStrictMode,\n\t\tPath:     WithBase(\"/api/\"),\n\t\tSecure:   false,\n\t})\n}\n"
  },
  {
    "path": "server/common/response.go",
    "content": "package common\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nconst IndentSize = \"    \"\n\ntype APISuccessResult struct {\n\tStatus string      `json:\"status\"`\n\tResult interface{} `json:\"result,omitempty\"`\n}\n\ntype APISuccessResults struct {\n\tStatus  string      `json:\"status\"`\n\tResults interface{} `json:\"results\"`\n}\n\ntype APISuccessResultsWithMetadata struct {\n\tStatus   string      `json:\"status\"`\n\tResults  interface{} `json:\"results\"`\n\tMetadata interface{} `json:\"permissions,omitempty\"`\n}\n\ntype APIErrorMessage struct {\n\tStatus  string `json:\"status\"`\n\tMessage string `json:\"message,omitempty\"`\n}\n\nfunc SendSuccessResult(res http.ResponseWriter, data interface{}) {\n\tencoder := json.NewEncoder(res)\n\tencoder.SetEscapeHTML(false)\n\tencoder.SetIndent(\"\", IndentSize)\n\tencoder.Encode(APISuccessResult{\"ok\", data})\n}\n\nfunc SendSuccessResultWithEtagAndGzip(res http.ResponseWriter, req *http.Request, data interface{}) {\n\tvar dataToSend []byte\n\tvar err error\n\tif dataToSend, err = json.MarshalIndent(APISuccessResult{\"ok\", data}, \"\", IndentSize); err != nil {\n\t\tLog.Warning(\"common::response Marshal %s\", err.Error())\n\t\tdataToSend = []byte(\"{}\")\n\t}\n\tmode := \"normal\"\n\tif strings.Contains(req.Header.Get(\"Accept-Encoding\"), \"gzip\") == true {\n\t\tmode = \"gzip\"\n\t}\n\thash := QuickHash(mode+string(dataToSend), 20)\n\tif req.Header.Get(\"If-None-Match\") == hash {\n\t\tres.WriteHeader(http.StatusNotModified)\n\t\treturn\n\t}\n\thead := res.Header()\n\thead.Set(\"Etag\", hash)\n\tif mode == \"gzip\" {\n\t\thead.Set(\"Content-Encoding\", \"gzip\")\n\n\t\tvar b bytes.Buffer\n\t\tw, _ := gzip.NewWriterLevel(&b, 1)\n\t\tw.Write(dataToSend)\n\t\tw.Close()\n\t\tdataToSend = b.Bytes()\n\t}\n\tres.Write(dataToSend)\n}\n\nfunc SendSuccessResults(res http.ResponseWriter, data interface{}) {\n\tencoder := json.NewEncoder(res)\n\tencoder.SetEscapeHTML(false)\n\tencoder.SetIndent(\"\", IndentSize)\n\tencoder.Encode(APISuccessResults{\"ok\", data})\n}\n\nfunc SendSuccessResultsWithMetadata(res http.ResponseWriter, data interface{}, p interface{}) {\n\tencoder := json.NewEncoder(res)\n\tencoder.SetEscapeHTML(false)\n\tencoder.SetIndent(\"\", IndentSize)\n\tencoder.Encode(APISuccessResultsWithMetadata{\"ok\", data, p})\n}\n\nfunc SendErrorResult(res http.ResponseWriter, err error) {\n\tencoder := json.NewEncoder(res)\n\tencoder.SetEscapeHTML(false)\n\tencoder.SetIndent(\"\", IndentSize)\n\tobj, ok := err.(interface{ Status() int })\n\tif ok == true {\n\t\tres.WriteHeader(obj.Status())\n\t} else {\n\t\tres.WriteHeader(http.StatusInternalServerError)\n\t}\n\tm := func(r string) string {\n\t\tif r == \"\" {\n\t\t\treturn r\n\t\t}\n\t\treturn strings.ToUpper(string(r[0])) + string(r[1:])\n\t}(err.Error())\n\tencoder.Encode(APIErrorMessage{\"error\", m})\n}\n\nfunc SendRaw(res http.ResponseWriter, data interface{}) {\n\tencoder := json.NewEncoder(res)\n\tencoder.SetEscapeHTML(false)\n\tencoder.SetIndent(\"\", IndentSize)\n\tencoder.Encode(data)\n}\n\nfunc Page(stuff string) string {\n\tif strings.Contains(stuff, \"</form>\") {\n\t\tstuff += \"<script>new URLSearchParams(location.search).forEach((value, name) => ($el = document.querySelector(`form [name=\\\"${name}\\\"]`)) && ($el.value = value))</script>\"\n\t}\n\treturn `<!DOCTYPE html>\n<html>\n  <head>\n    <base href=\"` + WithBase(\"\") + `\">\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\" name=\"viewport\">\n    <title>` + Config.Get(\"general.name\").String() + `</title>\n    <style>\n      html { background: #f2f3f5; font-size: 16px; font-family: \"San Francisco\",\"Roboto\",\"Arial\",sans-serif; height: 100%;}\n      body { text-align: center; padding-top: 50px; text-align: center; margin: 0; height: 100%; box-sizing: border-box;}\n      h1 { font-weight: 200; line-height: 1em; font-size: 40px; }\n      p { opacity: 0.8; font-size: 1.05em; }\n      form { max-width: 450px; margin: 0 auto; padding: 0 10px; text-align: left; }\n      button,.btn { padding: 11px 0px; width: 100%; background: #466372; color: white; margin-top: 10px; cursor: pointer; font-weight: bold; border: none; border-radius: 2px; box-shadow: 2px 2px 2px rgb(0 0 0 / 5%); }\n      input:not([type=\"checkbox\"]), textarea { display: block; margin: 8px 0; border: none; outline: none; padding: 13px 15px; box-shadow: 2px 2px 2px rgb(0 0 0 / 1%); min-width: 100%; max-width: 100%; max-height: 80px; box-sizing: border-box; border-radius: 2px; }\n      input, textarea, body { color: #313538; }\n      input, textarea, button { font-size: inherit; }\n    </style>\n  </head>\n  <body class=\"common_response_page\">\n    ` + stuff + `\n    <style>\n      ` + Hooks.Get.CSS() + `\n      ` + Config.Get(\"general.custom_css\").String() + `\n    </style>\n  </body>\n</html>`\n}\n\nfunc RedirectPage(url string) string {\n\treturn `<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Refresh\" content=\"0; url=` + url + `\" />\n  </head>\n<body>\n  <script>\n    location.href = \"` + url + `\"\n  </script>\n</body>\n</html>\n`\n}\n"
  },
  {
    "path": "server/common/ssl/cert.go",
    "content": "package ssl\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc GetCertificate(key *rsa.PrivateKey, root *x509.Certificate) (*x509.Certificate, []byte, error) {\n\tif cert, certPEM, err := pullCertificateFromFS(); err == nil {\n\t\treturn cert, certPEM, nil\n\t}\n\tcert, certPEM, err := generateNewCertificate(root, key)\n\tif err != nil {\n\t\tClear()\n\t\treturn nil, nil, err\n\t}\n\tif err = saveCertificateToFS(certPEM); err != nil {\n\t\tClear()\n\t\treturn nil, nil, err\n\t}\n\treturn cert, certPEM, nil\n}\n\nfunc generateNewCertificate(root *x509.Certificate, key *rsa.PrivateKey) (*x509.Certificate, []byte, error) {\n\tcertDER, err := x509.CreateCertificate(rand.Reader, root, root, &key.PublicKey, key)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tcert, err := x509.ParseCertificate(certDER)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn cert, pem.EncodeToMemory(&pem.Block{\n\t\tType: \"CERTIFICATE\", Bytes: certDER,\n\t}), nil\n}\n\nfunc pullCertificateFromFS() (*x509.Certificate, []byte, error) {\n\tfile, err := os.OpenFile(certPEMPath(), os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tdefer file.Close()\n\tcertPEM, err := io.ReadAll(file)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tblock, _ := pem.Decode(certPEM)\n\tcert, err := x509.ParseCertificate(block.Bytes)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn cert, certPEM, nil\n}\n\nfunc saveCertificateToFS(certPEM []byte) error {\n\tfile, err := os.OpenFile(certPEMPath(), os.O_WRONLY|os.O_CREATE, 0600)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = file.Write(certPEM)\n\tfile.Close()\n\treturn err\n\n}\n\nfunc clearCert() {\n\tos.Remove(certPEMPath())\n}\n"
  },
  {
    "path": "server/common/ssl/generate.go",
    "content": "package ssl\n\nimport (\n\t\"crypto/rsa\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc GenerateSelfSigned() (tls.Certificate, *x509.CertPool, error) {\n\tvar err error\n\tvar key *rsa.PrivateKey\n\tvar root *x509.Certificate\n\tvar keyPEM []byte\n\tvar certPEM []byte\n\tvar TLSCert tls.Certificate\n\n\tif key, keyPEM, err = GetPrivateKey(); err != nil {\n\t\tLog.Error(\"[https] key_generation %v\", err)\n\t\tClear()\n\t\treturn TLSCert, nil, err\n\t}\n\tif root, err = GetRoot(); err != nil {\n\t\tLog.Error(\"[https] root_certificate %v\", err)\n\t\tClear()\n\t\treturn TLSCert, nil, err\n\t}\n\tif _, certPEM, err = GetCertificate(key, root); err != nil {\n\t\tLog.Error(\"[https] x509_certificate %v\", err)\n\t\tClear()\n\t\treturn TLSCert, nil, err\n\t}\n\tif TLSCert, err = tls.X509KeyPair(certPEM, keyPEM); err != nil {\n\t\tLog.Error(\"[https] tls_certificate %v\", err)\n\t\tClear()\n\t\treturn TLSCert, nil, err\n\t}\n\n\trootCAs, _ := x509.SystemCertPool()\n\tif rootCAs == nil {\n\t\trootCAs = x509.NewCertPool()\n\t}\n\tif ok := rootCAs.AppendCertsFromPEM([]byte(certPEM)); ok == false {\n\t\tLog.Error(\"[https] tls_client\")\n\t\tClear()\n\t\treturn TLSCert, rootCAs, err\n\t}\n\treturn TLSCert, rootCAs, nil\n}\n"
  },
  {
    "path": "server/common/ssl/index.go",
    "content": "package ssl\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar (\n\tkeyPEMPath  func() string\n\tcertPEMPath func() string\n)\n\nfunc init() {\n\tkeyPEMPath = func() string {\n\t\treturn GetAbsolutePath(CERT_PATH, \"key.pem\")\n\t}\n\tcertPEMPath = func() string {\n\t\treturn GetAbsolutePath(CERT_PATH, \"cert.pem\")\n\t}\n}\n\nfunc Clear() {\n\tclearPrivateKey()\n\tclearCert()\n}\n"
  },
  {
    "path": "server/common/ssl/private.go",
    "content": "package ssl\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc GetPrivateKey() (*rsa.PrivateKey, []byte, error) {\n\tif private, privatePEM, err := pullPrivateKeyFromFS(); err == nil {\n\t\treturn private, privatePEM, nil\n\t}\n\tkey, keyPEM, err := generateNewPrivateKey()\n\tif err != nil {\n\t\tClear()\n\t\treturn nil, nil, err\n\t}\n\tif err = savePrivateKeyToFS(keyPEM); err != nil {\n\t\tClear()\n\t\treturn nil, nil, err\n\t}\n\treturn key, keyPEM, nil\n}\n\nfunc generateNewPrivateKey() (*rsa.PrivateKey, []byte, error) {\n\tkey, err := rsa.GenerateKey(rand.Reader, 2048)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn key, pem.EncodeToMemory(&pem.Block{\n\t\tType:  \"RSA PRIVATE KEY\",\n\t\tBytes: x509.MarshalPKCS1PrivateKey(key),\n\t}), nil\n}\n\nfunc pullPrivateKeyFromFS() (*rsa.PrivateKey, []byte, error) {\n\tfile, err := os.OpenFile(keyPEMPath(), os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tdefer file.Close()\n\n\tkeyPEM, err := io.ReadAll(file)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tblock, _ := pem.Decode(keyPEM)\n\tpriv, err := x509.ParsePKCS1PrivateKey(block.Bytes)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn priv, keyPEM, nil\n}\n\nfunc savePrivateKeyToFS(privatePEM []byte) error {\n\tfile, err := os.OpenFile(keyPEMPath(), os.O_WRONLY|os.O_CREATE, 0600)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = file.Write(privatePEM)\n\tfile.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc clearPrivateKey() {\n\tos.Remove(keyPEMPath())\n}\n"
  },
  {
    "path": "server/common/ssl/root.go",
    "content": "package ssl\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"math/big\"\n\t\"net\"\n\t\"time\"\n)\n\nfunc GetRoot() (*x509.Certificate, error) {\n\tserialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)\n\tserialNumber, err := rand.Int(rand.Reader, serialNumberLimit)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &x509.Certificate{\n\t\tSerialNumber: serialNumber,\n\t\tSubject: pkix.Name{\n\t\t\tOrganization: []string{\"Filestash\"},\n\t\t},\n\t\tNotBefore:             time.Now().Add(-24 * time.Hour),\n\t\tNotAfter:              time.Now().Add(24 * 365 * 100 * time.Hour),\n\t\tKeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,\n\t\tExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},\n\t\tBasicConstraintsValid: true,\n\t\tIsCA:                  false,\n\t\tIPAddresses: func() []net.IP {\n\t\t\tips := []net.IP{}\n\t\t\tifaces, err := net.Interfaces()\n\t\t\tif err != nil {\n\t\t\t\treturn []net.IP{net.ParseIP(\"127.0.0.1\")}\n\t\t\t}\n\t\t\tfor _, i := range ifaces {\n\t\t\t\taddrs, err := i.Addrs()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn []net.IP{net.ParseIP(\"127.0.0.1\")}\n\t\t\t\t}\n\t\t\t\tfor _, addr := range addrs {\n\t\t\t\t\tvar ip net.IP\n\t\t\t\t\tswitch v := addr.(type) {\n\t\t\t\t\tcase *net.IPNet:\n\t\t\t\t\t\tip = v.IP\n\t\t\t\t\tcase *net.IPAddr:\n\t\t\t\t\t\tip = v.IP\n\t\t\t\t\t}\n\t\t\t\t\tips = append(ips, ip)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ips\n\t\t}(),\n\t}, nil\n}\n"
  },
  {
    "path": "server/common/token.go",
    "content": "package common\n\nimport (\n\t\"time\"\n)\n\nconst (\n\tADMIN_CLAIM = \"ADMIN\"\n)\n\ntype AdminToken struct {\n\tClaim  string    `json:\"token\"`\n\tExpire time.Time `json:\"time\"`\n}\n\nfunc NewAdminToken() AdminToken {\n\treturn AdminToken{\n\t\tClaim:  ADMIN_CLAIM,\n\t\tExpire: time.Now().Add(time.Hour * 24),\n\t}\n}\n\nfunc (this AdminToken) IsAdmin() bool {\n\tif this.Claim != ADMIN_CLAIM {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (this AdminToken) IsValid() bool {\n\tif this.Expire.Sub(time.Now()) <= 0 {\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "server/common/types.go",
    "content": "package common\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n)\n\ntype IBackend interface {\n\tInit(params map[string]string, app *App) (IBackend, error)\n\tLs(path string) ([]os.FileInfo, error)\n\tStat(path string) (os.FileInfo, error)\n\tCat(path string) (io.ReadCloser, error)\n\tMkdir(path string) error\n\tRm(path string) error\n\tMv(from string, to string) error\n\tSave(path string, file io.Reader) error\n\tTouch(path string) error\n\tLoginForm() Form\n}\n\ntype IAuthentication interface {\n\tSetup() Form\n\tEntryPoint(idpParams map[string]string, req *http.Request, res http.ResponseWriter) error\n\tCallback(formData map[string]string, idpParams map[string]string, res http.ResponseWriter) (map[string]string, error)\n}\n\ntype IAuthorisation interface {\n\tLs(ctx *App, path string) error\n\tCat(ctx *App, path string) error\n\tStat(ctx *App, path string) error\n\tMkdir(ctx *App, path string) error\n\tRm(ctx *App, path string) error\n\tMv(ctx *App, from string, to string) error\n\tSave(ctx *App, path string) error\n\tTouch(ctx *App, path string) error\n}\n\ntype IFile interface {\n\tos.FileInfo\n\tPath() string\n}\n\ntype ISearch interface {\n\tQuery(ctx App, basePath string, term string) ([]IFile, error)\n}\n\ntype ILogger interface {\n\tDebug(format string, v ...interface{})\n\tInfo(format string, v ...interface{})\n\tWarning(format string, v ...interface{})\n\tError(format string, v ...interface{})\n\tStdout(format string, v ...interface{})\n\tSetVisibility(str string)\n}\n\ntype IThumbnailer interface {\n\tGenerate(io.ReadCloser, *App, *http.ResponseWriter, *http.Request) (io.ReadCloser, error)\n}\n\ntype IAuditPlugin interface {\n\tQuery(ctx *App, searchParams map[string]string) (AuditQueryResult, error)\n}\ntype AuditQueryResult struct {\n\tForm       *Form  `json:\"form\"`\n\tRenderHTML string `json:\"render\"`\n}\n\nconst (\n\tMetaModeTag = 1 << iota\n\tMetaModeBookmark\n\tMetaModeForm\n)\n\ntype IMetadata interface {\n\tGet(ctx *App, path string) ([]FormElement, error)\n\tSet(ctx *App, path string, value []FormElement) error\n\tSearch(ctx *App, path string, facets map[string]any) (map[string][]FormElement, error)\n}\n\ntype ITrigger interface {\n\tManifest() WorkflowSpecs\n\tInit() (chan ITriggerEvent, error)\n}\n\ntype IAction interface {\n\tManifest() WorkflowSpecs\n\tExecute(params map[string]string, input map[string]string) (map[string]string, error)\n}\n\ntype IDirectoryService interface {\n\tSearch(query string) ([]DirectoryUser, error)\n}\n\ntype DirectoryUser struct {\n\tId    string `json:\"id\"`\n\tName  string `json:\"name\"`\n\tEmail string `json:\"email\"`\n}\n\ntype ITriggerEvent interface {\n\tWorkflowID() string\n\tInput() map[string]string\n}\n\ntype WorkflowSpecs struct {\n\tName     string `json:\"name\"`\n\tTitle    string `json:\"title\"`\n\tSubtitle string `json:\"subtitle\"`\n\tIcon     string `json:\"icon\"`\n\tSpecs    Form   `json:\"specs\"`\n\tOrder    int    `json:\"-\"`\n}\n\ntype File struct {\n\tFName   string `json:\"name\"`\n\tFType   string `json:\"type\"`\n\tFTime   int64  `json:\"time\"`\n\tFSize   int64  `json:\"size\"`\n\tFPath   string `json:\"path,omitempty\"`\n\tOffline bool   `json:\"offline,omitempty\"`\n}\n\nfunc (f File) Name() string {\n\treturn f.FName\n}\nfunc (f File) Size() int64 {\n\treturn f.FSize\n}\nfunc (f File) Mode() os.FileMode {\n\tif f.IsDir() {\n\t\treturn os.ModeDir\n\t}\n\treturn os.FileMode(0664)\n}\nfunc (f File) ModTime() time.Time {\n\tif f.FTime == 0 {\n\t\tt := new(time.Time)\n\t\treturn *t\n\t}\n\treturn time.Unix(f.FTime, 0)\n}\nfunc (f File) IsDir() bool {\n\tif f.FType != \"directory\" {\n\t\treturn false\n\t}\n\treturn true\n}\nfunc (f File) Sys() interface{} {\n\treturn f\n}\n\nfunc (f File) Path() string {\n\treturn f.FPath\n}\n\ntype Metadata struct {\n\tCanSee             *bool      `json:\"can_read,omitempty\"`\n\tCanCreateFile      *bool      `json:\"can_create_file,omitempty\"`\n\tCanCreateDirectory *bool      `json:\"can_create_directory,omitempty\"`\n\tCanRename          *bool      `json:\"can_rename,omitempty\"`\n\tCanMove            *bool      `json:\"can_move,omitempty\"`\n\tCanUpload          *bool      `json:\"can_upload,omitempty\"`\n\tCanDelete          *bool      `json:\"can_delete,omitempty\"`\n\tCanShare           *bool      `json:\"can_share,omitempty\"`\n\tHideExtension      *bool      `json:\"hide_extension,omitempty\"`\n\tRefreshOnCreate    *bool      `json:\"refresh_on_create,omitempty\"`\n\tExpire             *time.Time `json:\"-\"`\n}\n\nconst PASSWORD_DUMMY = \"{{PASSWORD}}\"\n\ntype Share struct {\n\tId           string  `json:\"id\"`\n\tBackend      string  `json:\"-\"`\n\tAuth         string  `json:\"auth,omitempty\"`\n\tPath         string  `json:\"path\"`\n\tPassword     *string `json:\"password,omitempty\"`\n\tUsers        *string `json:\"users,omitempty\"`\n\tExpire       *int64  `json:\"expire,omitempty\"`\n\tUrl          *string `json:\"url,omitempty\"`\n\tCanShare     bool    `json:\"can_share\"`\n\tCanManageOwn bool    `json:\"can_manage_own\"`\n\tCanRead      bool    `json:\"can_read\"`\n\tCanWrite     bool    `json:\"can_write\"`\n\tCanUpload    bool    `json:\"can_upload\"`\n}\n\nfunc (s Share) IsValid() error {\n\tif s.Expire != nil {\n\t\tnow := time.Now().UnixNano() / 1000000\n\t\tif now > *s.Expire {\n\t\t\treturn NewError(\"Link has expired\", 410)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (s *Share) MarshalJSON() ([]byte, error) {\n\tp := Share{\n\t\ts.Id,\n\t\ts.Backend,\n\t\t\"\",\n\t\ts.Path,\n\t\tfunc(pass *string) *string {\n\t\t\tif pass != nil {\n\t\t\t\treturn NewString(PASSWORD_DUMMY)\n\t\t\t}\n\t\t\treturn nil\n\t\t}(s.Password),\n\t\ts.Users,\n\t\ts.Expire,\n\t\ts.Url,\n\t\ts.CanShare,\n\t\ts.CanManageOwn,\n\t\ts.CanRead,\n\t\ts.CanWrite,\n\t\ts.CanUpload,\n\t}\n\treturn json.Marshal(p)\n}\nfunc (s *Share) UnmarshallJSON(b []byte) error {\n\tvar tmp map[string]interface{}\n\tif err := json.Unmarshal(b, &tmp); err != nil {\n\t\treturn err\n\t}\n\tfor key, value := range tmp {\n\t\tswitch key {\n\t\tcase \"password\":\n\t\t\ts.Password = NewStringpFromInterface(value)\n\t\tcase \"users\":\n\t\t\ts.Users = NewStringpFromInterface(value)\n\t\tcase \"expire\":\n\t\t\ts.Expire = NewInt64pFromInterface(value)\n\t\tcase \"url\":\n\t\t\ts.Url = NewStringpFromInterface(value)\n\t\tcase \"can_share\":\n\t\t\ts.CanShare = NewBoolFromInterface(value)\n\t\tcase \"can_manage_own\":\n\t\t\ts.CanManageOwn = NewBoolFromInterface(value)\n\t\tcase \"can_read\":\n\t\t\ts.CanRead = NewBoolFromInterface(value)\n\t\tcase \"can_write\":\n\t\t\ts.CanWrite = NewBoolFromInterface(value)\n\t\tcase \"can_upload\":\n\t\t\ts.CanUpload = NewBoolFromInterface(value)\n\t\t}\n\t}\n\treturn nil\n}\n\ntype HandlerFunc func(*App, http.ResponseWriter, *http.Request)\n\ntype Middleware func(HandlerFunc) HandlerFunc\n"
  },
  {
    "path": "server/common/utils.go",
    "content": "package common\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc NewBool(t bool) *bool {\n\treturn &t\n}\n\nfunc NewString(t string) *string {\n\tif t == \"\" {\n\t\treturn nil\n\t}\n\treturn &t\n}\n\nfunc NewInt(t int) *int {\n\treturn &t\n}\n\nfunc NewBoolFromInterface(val interface{}) bool {\n\tswitch val.(type) {\n\tcase bool:\n\t\treturn val.(bool)\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc NewInt64pFromInterface(val interface{}) *int64 {\n\tswitch val.(type) {\n\tcase int64:\n\t\tv := val.(int64)\n\t\treturn &v\n\tcase float64:\n\t\tv := int64(val.(float64))\n\t\treturn &v\n\tdefault:\n\t\treturn nil\n\t}\n}\n\nfunc NewStringpFromInterface(val interface{}) *string {\n\tswitch val.(type) {\n\tcase string:\n\t\tv := val.(string)\n\t\treturn &v\n\tdefault:\n\t\treturn nil\n\t}\n}\n\nfunc NewStringFromInterface(val interface{}) string {\n\tswitch val.(type) {\n\tcase string:\n\t\treturn val.(string)\n\tcase float64:\n\t\treturn fmt.Sprintf(\"%d\", int64(val.(float64)))\n\tcase bool:\n\t\treturn fmt.Sprintf(\"%t\", val.(bool))\n\tcase []interface{}:\n\t\tlist := val.([]interface{})\n\t\tstrSlice := make([]string, len(list))\n\t\tfor i, el := range list {\n\t\t\tstrSlice[i] = NewStringFromInterface(el)\n\t\t}\n\t\treturn strings.Join(strSlice, \",\")\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\nfunc NewReadCloserFromBytes(t []byte) io.ReadCloser {\n\treturn io.NopCloser(bytes.NewReader(t))\n}\n\nfunc NewReadCloserFromReader(r io.Reader) io.ReadCloser {\n\treturn io.NopCloser(r)\n}\n\nfunc PrettyPrint(json_dirty []byte) []byte {\n\tvar json_pretty bytes.Buffer\n\terror := json.Indent(&json_pretty, json_dirty, \"\", \"    \")\n\tif error != nil {\n\t\treturn json_dirty\n\t}\n\tjson_pretty.Write([]byte(\"\\n\"))\n\treturn json_pretty.Bytes()\n}\n\nfunc CookieName(idx int) string {\n\tif idx == 0 {\n\t\treturn COOKIE_NAME_AUTH\n\t}\n\treturn COOKIE_NAME_AUTH + strconv.Itoa(idx)\n}\n"
  },
  {
    "path": "server/ctrl/about.go",
    "content": "package ctrl\n\nimport (\n\t\"fmt\"\n\t\"html\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\t\"text/template\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n)\n\nvar listOfPlugins = struct {\n\toss        []string\n\tenterprise []string\n\tcustom     []string\n\tapps       []string\n}{}\n\nfunc InitPluginList(code []byte, plgs map[string]model.PluginImpl) error {\n\tlistOfPackages := regexp.MustCompile(`\\t_?\\s*\\\"(github.com/[^\\\"]+)`).FindAllStringSubmatch(string(code), -1)\n\tfor _, packageNameMatch := range listOfPackages {\n\t\tif len(packageNameMatch) != 2 {\n\t\t\tLog.Error(\"ctrl::static error=assertion_failed msg=invalid_match_size arg=%d\", len(packageNameMatch))\n\t\t\treturn ErrNotValid\n\t\t}\n\t\tpackageName := packageNameMatch[1]\n\t\tpackageShortName := filepath.Base(packageName)\n\n\t\tif strings.HasPrefix(packageName, \"github.com/mickael-kerjean/filestash/server/plugin/\") {\n\t\t\tlistOfPlugins.oss = append(listOfPlugins.oss, packageShortName)\n\t\t} else if strings.HasPrefix(packageName, \"github.com/mickael-kerjean/filestash/filestash-enterprise/plugins/\") {\n\t\t\tlistOfPlugins.enterprise = append(listOfPlugins.enterprise, packageShortName)\n\t\t} else if strings.HasPrefix(packageName, \"github.com/mickael-kerjean/filestash/filestash-enterprise/customers/\") {\n\t\t\tlistOfPlugins.custom = append(listOfPlugins.custom, packageShortName)\n\t\t} else {\n\t\t\tlistOfPlugins.custom = append(listOfPlugins.custom, packageShortName)\n\t\t}\n\t}\n\tfor name, _ := range plgs {\n\t\tlistOfPlugins.apps = append(listOfPlugins.apps, name)\n\t}\n\treturn nil\n}\n\nfunc AboutHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tt, _ := template.\n\t\tNew(\"about\").\n\t\tFuncs(map[string]interface{}{\n\t\t\t\"renderPlugin\": func(lstr string, commit string) string {\n\t\t\t\tif len(lstr) == 0 {\n\t\t\t\t\treturn \"N/A\"\n\t\t\t\t} else if commit == \"\" {\n\t\t\t\t\treturn html.EscapeString(lstr)\n\t\t\t\t}\n\t\t\t\tlist := strings.Split(lstr, \" \")\n\t\t\t\tfor i, _ := range list {\n\t\t\t\t\tlist[i] = `<a href=\"https://github.com/mickael-kerjean/filestash/tree/` + commit +\n\t\t\t\t\t\t`/server/plugin/` + html.EscapeString(list[i]) + `\" target=\"_blank\">` + html.EscapeString(list[i]) + `</a>`\n\t\t\t\t}\n\t\t\t\treturn strings.Join(list, \" \")\n\t\t\t},\n\t\t}).\n\t\tParse(Page(`\n\t  <h1> {{ .Version }} </h1>\n\t  <table>\n\t\t<tr> <td style=\"width:150px;\"> Commit hash </td> <td> <a href=\"https://github.com/mickael-kerjean/filestash/tree/{{ .CommitHash }}\">{{ .CommitHash }}</a> </td> </tr>\n\t\t<tr> <td> Binary hash </td> <td> {{ index .Checksum 0}} </td> </tr>\n\t\t<tr> <td> Config hash </td> <td> {{ index .Checksum 1}} </td> </tr>\n\t\t<tr> <td> License </td> <td> {{ .License }} </td> </tr>\n\t\t<tr>\n          <td> Plugins </td>\n          <td>\n            STANDARD [<span class=\"small\">{{ renderPlugin (index .Plugins 0) .CommitHash }}</span>]\n            <br/>\n            ENTERPRISE [<span class=\"small\">{{ renderPlugin (index .Plugins 1) \"\" }}</span>]\n            <br/>\n            CUSTOM [<span class=\"small\">{{ renderPlugin (index .Plugins 2) \"\" }}</span>]\n            <br/>\n            RUNTIME [<span class=\"small\">{{ renderPlugin (index .Plugins 3) \"\" }}</span>]\n          </td>\n        </tr>\n\t  </table>\n\n\t  <style>\n\t\tbody.common_response_page { background: var(--bg-color); }\n\t\ttable { margin: 0 auto; font-family: monospace; opacity: 0.8; max-width: 1000px; width: 95%;}\n\t\ttable td { text-align: right; padding-left: 10px; vertical-align: top; }\n        table td span.small { font-size:0.8rem; }\n        table a { color: inherit; text-decoration: none; }\n\t  </style>\n\t`))\n\thashFileContent := func(path string, n int) string {\n\t\tf, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t\tdefer f.Close()\n\t\treturn HashStream(f, n)\n\t}\n\tt.Execute(res, struct {\n\t\tVersion    string\n\t\tCommitHash string\n\t\tChecksum   []string\n\t\tLicense    string\n\t\tPlugins    []string\n\t}{\n\t\tVersion:    fmt.Sprintf(\"Filestash %s.%s\", APP_VERSION, BUILD_DATE),\n\t\tCommitHash: BUILD_REF,\n\t\tChecksum: []string{\n\t\t\thashFileContent(GetAbsolutePath(\"filestash\"), 0),\n\t\t\thashFileContent(GetAbsolutePath(CONFIG_PATH, \"config.json\"), 0),\n\t\t},\n\t\tLicense: strings.ToUpper(LICENSE),\n\t\tPlugins: []string{\n\t\t\tstrings.Join(listOfPlugins.oss, \" \"),\n\t\t\tstrings.Join(listOfPlugins.enterprise, \" \"),\n\t\t\tstrings.Join(listOfPlugins.custom, \" \"),\n\t\t\tstrings.Join(listOfPlugins.apps, \" \"),\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "server/ctrl/admin.go",
    "content": "package ctrl\n\nimport (\n\t\"encoding/json\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"golang.org/x/crypto/bcrypt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n)\n\nvar logpath = GetAbsolutePath(LOG_PATH, \"access.log\")\n\nfunc AdminSessionGet(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif admin := Config.Get(\"auth.admin\").String(); admin == \"\" {\n\t\tSendSuccessResult(res, true)\n\t\treturn\n\t}\n\tobfuscate := func() string {\n\t\tc, err := req.Cookie(COOKIE_NAME_ADMIN)\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn c.Value\n\t}()\n\n\tstr, err := DecryptString(SECRET_KEY_DERIVATE_FOR_ADMIN, obfuscate)\n\tif err != nil {\n\t\tSendSuccessResult(res, false)\n\t\treturn\n\t}\n\ttoken := AdminToken{}\n\tjson.Unmarshal([]byte(str), &token)\n\n\tif token.IsValid() == false {\n\t\tSendSuccessResult(res, false)\n\t\treturn\n\t} else if token.IsAdmin() == false {\n\t\tSendSuccessResult(res, false)\n\t\treturn\n\t}\n\tSendSuccessResult(res, true)\n}\n\nfunc AdminSessionAuthenticate(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t// Step 1: Deliberatly make the request slower to make hacking attempt harder for the attacker\n\ttime.Sleep(1500 * time.Millisecond)\n\n\t// Step 2: Make sure current user has appropriate access\n\tadmin := Config.Get(\"auth.admin\").String()\n\tif admin == \"\" {\n\t\tSendErrorResult(res, NewError(\"Missing admin account, please contact your administrator\", 500))\n\t\treturn\n\t}\n\tvar params map[string]string\n\tb, _ := io.ReadAll(req.Body)\n\tjson.Unmarshal(b, &params)\n\tif err := bcrypt.CompareHashAndPassword([]byte(admin), []byte(params[\"password\"])); err != nil {\n\t\tSendErrorResult(res, ErrInvalidPassword)\n\t\treturn\n\t}\n\n\t// Step 3: Send response to the client\n\tbody, _ := json.Marshal(NewAdminToken())\n\tobfuscate, err := EncryptString(SECRET_KEY_DERIVATE_FOR_ADMIN, string(body))\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\thttp.SetCookie(res, &http.Cookie{\n\t\tName:     COOKIE_NAME_ADMIN,\n\t\tValue:    obfuscate,\n\t\tPath:     COOKIE_PATH_ADMIN,\n\t\tMaxAge:   60 * 60, // valid for 1 hour\n\t\tSameSite: http.SameSiteStrictMode,\n\t})\n\tif req.Header.Get(\"Accept\") == \"application/json\" {\n\t\tSendSuccessResult(res, map[string]string{\"access_token\": obfuscate})\n\t\treturn\n\t}\n\tSendSuccessResult(res, true)\n}\n\nfunc AdminBackend(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tdrivers := Backend.Drivers()\n\tbackends := make(map[string]Form, len(drivers))\n\tfor key := range drivers {\n\t\tbackends[key] = drivers[key].LoginForm()\n\t}\n\tSendSuccessResultWithEtagAndGzip(res, req, backends)\n\treturn\n}\n\nfunc AdminAuthenticationMiddleware(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tdrivers := Hooks.Get.AuthenticationMiddleware()\n\tmiddlewares := make(map[string]Form, len(drivers))\n\tfor id, driver := range drivers {\n\t\tmiddlewares[id] = driver.Setup()\n\t}\n\tSendSuccessResultWithEtagAndGzip(res, req, middlewares)\n\treturn\n}\n\nfunc FetchLogHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tfile, err := os.OpenFile(logpath, os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tdefer file.Close()\n\tmaxSize := req.URL.Query().Get(\"maxSize\")\n\tif maxSize != \"\" {\n\t\tcursor := func() int64 {\n\t\t\ttmp, err := strconv.Atoi(maxSize)\n\t\t\tif err != nil {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn int64(tmp)\n\t\t}()\n\t\tfor cursor >= 0 {\n\t\t\tif _, err := file.Seek(-cursor, io.SeekEnd); err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tchar := make([]byte, 1)\n\t\t\tfile.Read(char)\n\t\t\tif char[0] == 10 || char[0] == 13 { // stop if we find a line\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcursor += 1\n\t\t}\n\t}\n\tres.Header().Set(\"Content-Type\", \"text/plain\")\n\tio.Copy(res, file)\n}\n\nfunc FetchAuditHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tplg := Hooks.Get.AuditEngine()\n\tif plg == nil {\n\t\tSendErrorResult(res, ErrNotImplemented)\n\t\treturn\n\t}\n\tsearchParams := map[string]string{}\n\t_get := req.URL.Query()\n\tfor key, element := range _get {\n\t\tif len(element) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tsearchParams[key] = element[0]\n\t}\n\tresult, err := plg.Query(ctx, searchParams)\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tSendSuccessResult(res, result)\n}\n"
  },
  {
    "path": "server/ctrl/config.go",
    "content": "package ctrl\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"net/http\"\n)\n\nvar configpath = GetAbsolutePath(CONFIG_PATH, \"config.json\")\n\nfunc PrivateConfigHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tSendSuccessResult(res, &Config)\n}\n\nfunc PrivateConfigUpdateHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tb, _ := io.ReadAll(req.Body)\n\tif err := SaveConfig(b); err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tConfig.Load()\n\tSendSuccessResult(res, nil)\n}\n\nfunc PublicConfigHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tcfg := Config.Export()\n\tSendSuccessResultWithEtagAndGzip(res, req, cfg)\n}\n"
  },
  {
    "path": "server/ctrl/files.go",
    "content": "package ctrl\n\nimport (\n\t\"archive/zip\"\n\t\"context\"\n\t\"crypto/sha1\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"hash\"\n\t\"hash/crc32\"\n\t\"hash/fnv\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n)\n\ntype FileInfo struct {\n\tName    string `json:\"name\"`\n\tType    string `json:\"type\"`\n\tSize    int64  `json:\"size\"`\n\tTime    int64  `json:\"time\"`\n\tMode    uint32 `json:\"mode,omitempty\"`\n\tOffline bool   `json:\"offline,omitempty\"`\n}\n\nvar (\n\tfile_cache  AppCache\n\tzip_timeout func() int\n\tdisable_csp func() bool\n)\n\nfunc init() {\n\tzip_timeout = func() int {\n\t\treturn Config.Get(\"features.protection.zip_timeout\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Default = 60\n\t\t\tf.Name = \"zip_timeout\"\n\t\t\tf.Type = \"number\"\n\t\t\tf.Description = \"Timeout when user wants to download or extract a zip\"\n\t\t\tf.Placeholder = \"Default: 60seconds\"\n\t\t\treturn f\n\t\t}).Int()\n\t}\n\tdisable_csp = func() bool {\n\t\treturn Config.Get(\"features.protection.disable_csp\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Default = false\n\t\t\tf.Name = \"disable_csp\"\n\t\t\tf.Type = \"boolean\"\n\t\t\tf.Description = \"Disable the content security policy. Unless you 100% trust the content in your storage and want to execute code running from that storage, you shouldn't have this option checked\"\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\tfile_cache = NewAppCache()\n\tfile_cache.OnEvict(func(key string, value interface{}) {\n\t\tos.RemoveAll(filepath.Join(GetAbsolutePath(TMP_PATH), key))\n\t})\n\tHooks.Register.Onload(func() {\n\t\tzip_timeout()\n\t\tdisable_csp()\n\t})\n\tinitChunkedUploader()\n}\n\nfunc FileLs(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif model.CanRead(ctx) == false {\n\t\tif model.CanUpload(ctx) == false {\n\t\t\tLog.Debug(\"ls::permission 'permission denied'\")\n\t\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\t\treturn\n\t\t}\n\t\tSendSuccessResults(res, make([]FileInfo, 0))\n\t\treturn\n\t}\n\tpath, err := PathBuilder(ctx, req.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tLog.Debug(\"ls::path '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tvar perms Metadata = Metadata{}\n\tif obj, ok := ctx.Backend.(interface{ Meta(path string) Metadata }); ok {\n\t\tperms = obj.Meta(path)\n\t}\n\tfor _, auth := range Hooks.Get.AuthorisationMiddleware() {\n\t\tif err = auth.Ls(ctx, path); err != nil {\n\t\t\tLog.Info(\"ls::auth '%s'\", err.Error())\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\t\tctx.Context = context.WithValue(ctx.Context, \"AUDIT\", false)\n\t\tif err = auth.Mkdir(ctx, path); err != nil {\n\t\t\tperms.CanCreateDirectory = NewBool(false)\n\t\t}\n\t\tif err = auth.Touch(ctx, path); err != nil {\n\t\t\tperms.CanCreateFile = NewBool(false)\n\t\t}\n\t\tif err = auth.Mv(ctx, path, path); err != nil {\n\t\t\tperms.CanRename = NewBool(false)\n\t\t\tperms.CanMove = NewBool(false)\n\t\t}\n\t\tif err = auth.Save(ctx, path); err != nil {\n\t\t\tperms.CanUpload = NewBool(false)\n\t\t}\n\t\tif err = auth.Rm(ctx, path); err != nil {\n\t\t\tperms.CanDelete = NewBool(false)\n\t\t}\n\t\tif err = auth.Cat(ctx, path); err != nil {\n\t\t\tperms.CanSee = NewBool(false)\n\t\t}\n\t\tctx.Context = context.WithValue(ctx.Context, \"AUDIT\", nil)\n\t}\n\tif model.CanEdit(ctx) == false {\n\t\tperms.CanCreateFile = NewBool(false)\n\t\tperms.CanCreateDirectory = NewBool(false)\n\t\tperms.CanRename = NewBool(false)\n\t\tperms.CanMove = NewBool(false)\n\t\tperms.CanDelete = NewBool(false)\n\t\tperms.CanUpload = NewBool(false)\n\t}\n\tif model.CanUpload(ctx) == false {\n\t\tperms.CanCreateDirectory = NewBool(false)\n\t\tperms.CanRename = NewBool(false)\n\t\tperms.CanMove = NewBool(false)\n\t\tperms.CanDelete = NewBool(false)\n\t\tperms.CanUpload = NewBool(false)\n\t}\n\tif model.CanShare(ctx) == false {\n\t\tperms.CanShare = NewBool(false)\n\t}\n\n\tentries, err := ctx.Backend.Ls(path)\n\tif err != nil {\n\t\tLog.Debug(\"ls::backend '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\n\tfiles := make([]FileInfo, len(entries))\n\tetagger := fnv.New32()\n\tetagger.Write([]byte(path + strconv.Itoa(len(entries))))\n\tfor i := 0; i < len(entries); i++ {\n\t\tname := entries[i].Name()\n\t\tfiles[i] = FileInfo{\n\t\t\tName: name,\n\t\t\tSize: entries[i].Size(),\n\t\t\tTime: func(mt time.Time) (modTime int64) {\n\t\t\t\tif mt.IsZero() == false {\n\t\t\t\t\tmodTime = mt.UnixNano() / int64(time.Millisecond)\n\n\t\t\t\t}\n\t\t\t\tif i < 200 { // etag is generated from a few values to avoid large memory usage\n\t\t\t\t\tetagger.Write([]byte(name + strconv.Itoa(int(modTime))))\n\t\t\t\t}\n\n\t\t\t\treturn modTime\n\t\t\t}(entries[i].ModTime()),\n\t\t\tType: func(mode os.FileMode) string {\n\t\t\t\tif mode.IsRegular() {\n\t\t\t\t\treturn \"file\"\n\t\t\t\t}\n\t\t\t\treturn \"directory\"\n\t\t\t}(entries[i].Mode()),\n\t\t\tMode: func(mode os.FileMode) uint32 {\n\t\t\t\treturn uint32(mode)\n\t\t\t}(entries[i].Mode()),\n\t\t}\n\t\tif f, ok := entries[i].Sys().(File); ok && f.Offline == true {\n\t\t\tfiles[i].Offline = true\n\t\t}\n\t}\n\n\tetagValue := base64.StdEncoding.EncodeToString(etagger.Sum(nil))\n\tres.Header().Set(\"Etag\", etagValue)\n\tif etagValue != \"\" && req.Header.Get(\"If-None-Match\") == etagValue {\n\t\tres.WriteHeader(http.StatusNotModified)\n\t\treturn\n\t}\n\tSendSuccessResultsWithMetadata(res, files, perms)\n}\n\nfunc FileCat(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tvar (\n\t\tfile              io.ReadCloser\n\t\tfileMutation      bool        = false\n\t\tcontentLength     int64       = -1\n\t\tneedToCreateCache bool        = false\n\t\tquery             url.Values  = req.URL.Query()\n\t\theader            http.Header = res.Header()\n\t)\n\thttp.SetCookie(res, &http.Cookie{\n\t\tName:   \"download\",\n\t\tValue:  \"\",\n\t\tMaxAge: -1,\n\t\tPath:   \"/\",\n\t})\n\tif model.CanRead(ctx) == false {\n\t\tLog.Debug(\"cat::permission 'permission denied'\")\n\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\treturn\n\t}\n\tpath, err := PathBuilder(ctx, query.Get(\"path\"))\n\tif err != nil {\n\t\tLog.Debug(\"cat::path '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\n\tfor _, auth := range Hooks.Get.AuthorisationMiddleware() {\n\t\tif req.Method == http.MethodHead {\n\t\t\tif err = auth.Stat(ctx, path); err != nil {\n\t\t\t\tSendErrorResult(res, ErrNotAuthorized)\n\t\t\t\treturn\n\t\t\t}\n\t\t} else if err = auth.Cat(ctx, path); err != nil {\n\t\t\tSendErrorResult(res, ErrNotAuthorized)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// use our cache if necessary (range request) when possible\n\tif req.Header.Get(\"range\") != \"\" {\n\t\tctx.Session[\"fullpath\"] = path\n\t\tif p := file_cache.Get(ctx.Session); p != nil {\n\t\t\tf, err := os.OpenFile(p.(string), os.O_RDONLY, os.ModePerm)\n\t\t\tif err == nil {\n\t\t\t\tfile = f\n\t\t\t\tif fi, err := f.Stat(); err == nil {\n\t\t\t\t\tcontentLength = fi.Size()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// perform the actual `cat` if needed\n\tmType := GetMimeType(query.Get(\"path\"))\n\tif file == nil {\n\t\tif file, err = ctx.Backend.Cat(path); err != nil {\n\t\t\tif req.Method == http.MethodHead {\n\t\t\t\tif finfo, err := ctx.Backend.Stat(path); err == nil && finfo.IsDir() {\n\t\t\t\t\tif finfo.ModTime().Unix() > 0 {\n\t\t\t\t\t\theader.Set(\"Last-Modified\", finfo.ModTime().UTC().Format(http.TimeFormat))\n\t\t\t\t\t}\n\t\t\t\t\theader.Set(\"Content-Type\", \"inode/directory\")\n\t\t\t\t\tres.WriteHeader(http.StatusNoContent)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tLog.Debug(\"cat::backend '%s'\", err.Error())\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\t\tif mType == \"application/javascript\" {\n\t\t\tmType = \"text/plain\"\n\t\t}\n\t\theader.Set(\"Content-Type\", mType)\n\t\tif req.Header.Get(\"range\") != \"\" {\n\t\t\tneedToCreateCache = true\n\t\t}\n\t}\n\n\t// plugin hooks\n\tthumb := query.Get(\"thumbnail\")\n\tif thumb == \"true\" {\n\t\tfileMutation = true\n\t\tif finfo, err := ctx.Backend.Stat(path); err == nil && finfo.ModTime().Unix() > 0 {\n\t\t\tlm := finfo.ModTime().UTC().Format(http.TimeFormat)\n\t\t\tif lm == req.Header.Get(\"If-Modified-Since\") {\n\t\t\t\tres.WriteHeader(http.StatusNotModified)\n\t\t\t\treturn\n\t\t\t}\n\t\t\theader.Set(\"Last-Modified\", lm)\n\t\t}\n\t\tfor plgMType, plgHandler := range Hooks.Get.Thumbnailer() {\n\t\t\tif plgMType != mType {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfile, err = plgHandler.Generate(file, ctx, &res, req)\n\t\t\tif err != nil {\n\t\t\t\tif req.Context().Err() == nil {\n\t\t\t\t\tLog.Debug(\"cat::thumbnailer '%s'\", err.Error())\n\t\t\t\t}\n\t\t\t\tSendErrorResult(res, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\tfor _, obj := range Hooks.Get.ProcessFileContentBeforeSend() {\n\t\tf, changed, err := obj(file, ctx, &res, req)\n\t\tif err != nil {\n\t\t\tLog.Debug(\"cat::hooks '%s'\", err.Error())\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t} else if changed {\n\t\t\tfile = f\n\t\t\tfileMutation = true\n\t\t}\n\t}\n\n\t// The extra complexity is to support: https://en.wikipedia.org/wiki/Progressive_download\n\t// => range request requires a seeker to work, some backend support it, some don't. 2 strategies:\n\t// 1. backend support Seek: use what the current backend gives us\n\t// 2. backend doesn't support Seek: build up a cache so that subsequent call don't trigger multiple downloads\n\tif req.Header.Get(\"range\") != \"\" && needToCreateCache == true {\n\t\tif obj, ok := file.(io.Seeker); ok == true {\n\t\t\tif size, err := obj.Seek(0, io.SeekEnd); err == nil {\n\t\t\t\tif _, err = obj.Seek(0, io.SeekStart); err == nil {\n\t\t\t\t\tcontentLength = size\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\ttmpPath := GetAbsolutePath(TMP_PATH, \"file_\"+QuickString(20)+\".dat\")\n\t\t\tf, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE, os.ModePerm)\n\t\t\tif err != nil {\n\t\t\t\tLog.Debug(\"cat::range0 '%s'\", err.Error())\n\t\t\t\tSendErrorResult(res, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfile_cache.Set(ctx.Session, tmpPath)\n\t\t\tif _, err = io.Copy(f, file); err != nil {\n\t\t\t\tf.Close()\n\t\t\t\tfile.Close()\n\t\t\t\tLog.Debug(\"cat::range1 '%s'\", err.Error())\n\t\t\t\tSendErrorResult(res, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err = f.Sync(); err != nil {\n\t\t\t\tf.Close()\n\t\t\t\tfile.Close()\n\t\t\t\tLog.Debug(\"cat::range2 '%s'\", err.Error())\n\t\t\t\tSendErrorResult(res, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tf.Close()\n\t\t\tfile.Close()\n\t\t\tif f, err = os.OpenFile(tmpPath, os.O_RDONLY, os.ModePerm); err != nil {\n\t\t\t\tLog.Debug(\"cat::range3 '%s'\", err.Error())\n\t\t\t\tSendErrorResult(res, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif fi, err := f.Stat(); err == nil {\n\t\t\t\tcontentLength = fi.Size()\n\t\t\t}\n\t\t\tfile = f\n\t\t}\n\t}\n\n\t// Range request: find how much data we need to send\n\tvar ranges [][]int64\n\tif req.Header.Get(\"range\") != \"\" {\n\t\tranges = make([][]int64, 0)\n\t\tfor _, r := range strings.Split(strings.TrimPrefix(req.Header.Get(\"range\"), \"bytes=\"), \",\") {\n\t\t\tr = strings.TrimSpace(r)\n\t\t\tif r == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar start int64 = -1\n\t\t\tvar end int64 = -1\n\t\t\tsides := strings.Split(r, \"-\")\n\t\t\tif len(sides) == 2 {\n\t\t\t\tif start, err = strconv.ParseInt(sides[0], 10, 64); err != nil || start < 0 {\n\t\t\t\t\tstart = 0\n\t\t\t\t}\n\t\t\t\tif end, err = strconv.ParseInt(sides[1], 10, 64); err != nil || end < start {\n\t\t\t\t\tend = contentLength - 1\n\t\t\t\t}\n\t\t\t}\n\t\t\tif start != -1 && end != -1 && end-start >= 0 {\n\t\t\t\tranges = append(ranges, []int64{start, end})\n\t\t\t}\n\t\t}\n\t} else if fileMutation == false && contentLength < 0 {\n\t\tif finfo, err := ctx.Backend.Stat(path); err == nil {\n\t\t\tif finfo.ModTime().Unix() > 0 {\n\t\t\t\theader.Set(\"Last-Modified\", finfo.ModTime().UTC().Format(http.TimeFormat))\n\t\t\t}\n\t\t\tif finfo.IsDir() {\n\t\t\t\theader.Set(\"Content-Type\", \"inode/directory\")\n\t\t\t\tif req.Method == http.MethodHead {\n\t\t\t\t\tres.WriteHeader(http.StatusNoContent)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tSendErrorResult(res, ErrNotFound)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontentLength = finfo.Size()\n\t\t}\n\t}\n\n\t// publish headers\n\tif contentLength >= 0 {\n\t\theader.Set(\"Content-Length\", fmt.Sprintf(\"%d\", contentLength))\n\t}\n\tif disable_csp() == false {\n\t\theader.Set(\"Content-Security-Policy\", \"default-src 'none'; img-src 'self'; media-src 'self'; style-src 'unsafe-inline'; font-src data:; script-src-elem 'self'\")\n\t}\n\tif fname := query.Get(\"name\"); fname != \"\" {\n\t\theader.Set(\"Content-Disposition\", \"attachment; filename=\\\"\"+fname+\"\\\"\")\n\t}\n\theader.Set(\"Accept-Ranges\", \"bytes\")\n\n\tif req.Method != http.MethodHead {\n\t\tsize := 32\n\t\tif thumb != \"true\" {\n\t\t\tswitch Config.Get(\"general.buffer_size\").String() {\n\t\t\tcase \"small\":\n\t\t\t\tsize = 32\n\t\t\tcase \"medium\":\n\t\t\t\tsize = 128\n\t\t\tcase \"large\":\n\t\t\t\tsize = 2 * 1024\n\t\t\t}\n\t\t}\n\t\tbuf := make([]byte, size*1024)\n\t\tif f, ok := file.(io.ReadSeeker); ok && len(ranges) > 0 {\n\t\t\tif _, err = f.Seek(ranges[0][0], io.SeekStart); err == nil {\n\t\t\t\theader.Set(\"Content-Range\", fmt.Sprintf(\"bytes %d-%d/%d\", ranges[0][0], ranges[0][1], contentLength))\n\t\t\t\theader.Set(\"Content-Length\", fmt.Sprintf(\"%d\", ranges[0][1]-ranges[0][0]+1))\n\t\t\t\tres.WriteHeader(http.StatusPartialContent)\n\t\t\t\tio.CopyBuffer(res, io.LimitReader(f, ranges[0][1]-ranges[0][0]+1), buf)\n\t\t\t} else {\n\t\t\t\tres.WriteHeader(http.StatusRequestedRangeNotSatisfiable)\n\t\t\t}\n\t\t} else {\n\t\t\tio.CopyBuffer(res, file, buf)\n\t\t}\n\t}\n\tfile.Close()\n}\n\nfunc FileAccess(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tpath, err := PathBuilder(ctx, req.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tLog.Debug(\"access::path '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tvar perms Metadata = Metadata{}\n\tif obj, ok := ctx.Backend.(interface{ Meta(path string) Metadata }); ok {\n\t\tperms = obj.Meta(path)\n\t}\n\n\tallowed := []string{}\n\tif model.CanRead(ctx) {\n\t\tif perms.CanSee == nil || *perms.CanSee == true {\n\t\t\tallowed = append(allowed, \"GET\")\n\t\t}\n\t}\n\tif model.CanEdit(ctx) {\n\t\tif (perms.CanCreateFile == nil || *perms.CanCreateFile == true) &&\n\t\t\t(perms.CanCreateDirectory == nil || *perms.CanCreateDirectory == true) {\n\t\t\tallowed = append(allowed, \"PUT\")\n\t\t}\n\t}\n\tif model.CanUpload(ctx) {\n\t\tif perms.CanUpload == nil || *perms.CanUpload == true {\n\t\t\tallowed = append(allowed, \"POST\")\n\t\t}\n\t}\n\theader := res.Header()\n\theader.Set(\"Allow\", strings.Join(allowed, \", \"))\n\tSendSuccessResult(res, nil)\n}\n\nvar chunkedUploadCache AppCache\n\nfunc FileSave(ctx *App, res http.ResponseWriter, req *http.Request) {\n\th := res.Header()\n\th.Set(\"Cache-Control\", \"no-store\")\n\th.Set(\"Pragma\", \"no-cache\")\n\th.Set(\"Connection\", \"Close\")\n\n\tpath, err := PathBuilder(ctx, req.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tLog.Debug(\"files::save action=path_builder err=%s\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\n\tif model.CanEdit(ctx) == false {\n\t\tif model.CanUpload(ctx) == false {\n\t\t\tLog.Debug(\"files::save action=permission_upload err=permission_denied\")\n\t\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\t\treturn\n\t\t}\n\t\t// for user who cannot edit but can upload => we want to ensure there\n\t\t// won't be any overwritten data\n\t\troot, filename := SplitPath(path)\n\t\tentries, err := ctx.Backend.Ls(root)\n\t\tif err != nil {\n\t\t\tLog.Debug(\"files::save action=permission_ls err=%s\", err.Error())\n\t\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\t\treturn\n\t\t}\n\t\tfor i := 0; i < len(entries); i++ {\n\t\t\tif entries[i].Name() == filename {\n\t\t\t\tLog.Debug(\"files::save action=permission_ls err=already_exist\")\n\t\t\t\tSendErrorResult(res, ErrConflict)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, auth := range Hooks.Get.AuthorisationMiddleware() {\n\t\tif err = auth.Save(ctx, path); err != nil {\n\t\t\tLog.Info(\"files::save action=middleware err=%s\", err.Error())\n\t\t\tSendErrorResult(res, ErrNotAuthorized)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// There is 2 ways to save something:\n\t// - case1: regular upload, we just insert the file in the pipe\n\tproto := \"\"\n\tif _, ok := req.Header[\"Tus-Resumable\"]; ok {\n\t\tproto = \"tus\"\n\t}\n\tif proto == \"\" && req.Method == http.MethodPost {\n\t\terr = ctx.Backend.Save(path, req.Body)\n\t\treq.Body.Close()\n\t\tif err != nil {\n\t\t\tLog.Debug(\"files::save action=backend_save err=%s\", err.Error())\n\t\t\tSendErrorResult(res, NewError(err.Error(), 403))\n\t\t\treturn\n\t\t}\n\t\tSendSuccessResult(res, nil)\n\t\treturn\n\t}\n\n\t// - case2: chunked upload using the TUS protocol: https://tus.io/protocols/resumable-upload\n\tcacheKey := map[string]string{\n\t\t\"path\":    path,\n\t\t\"session\": GenerateID(ctx.Session),\n\t}\n\tif proto == \"tus\" && req.Method == http.MethodOptions {\n\t\th.Set(\"Tus-Resumable\", \"1.0.0\")\n\t\th.Set(\"Tus-Version\", \"1.0.0\")\n\t\th.Set(\"Tus-Extension\", \"creation,checksum\")\n\t\th.Set(\"Tus-Checksum-Algorithm\", \"sha1,crc32\")\n\t\treturn\n\t}\n\tif proto == \"tus\" && req.Method == http.MethodHead {\n\t\tc := chunkedUploadCache.Get(cacheKey)\n\t\tif c == nil {\n\t\t\tSendErrorResult(res, ErrNotFound)\n\t\t\treturn\n\t\t}\n\t\toffset, length := c.(*chunkedUpload).Meta()\n\t\th.Set(\"Tus-Resumable\", \"1.0.0\")\n\t\th.Set(\"Upload-Offset\", fmt.Sprintf(\"%d\", offset))\n\t\th.Set(\"Upload-Length\", fmt.Sprintf(\"%d\", length))\n\t\th.Set(\"Cache-Control\", \"no-store\")\n\t\tres.WriteHeader(http.StatusNoContent)\n\t\treturn\n\t}\n\tif proto == \"tus\" && req.Method == http.MethodPost {\n\t\tif c := chunkedUploadCache.Get(cacheKey); c != nil {\n\t\t\tchunkedUploadCache.Del(cacheKey)\n\t\t}\n\t\tsize, err := strconv.ParseUint(req.Header.Get(\"Upload-Length\"), 10, 0)\n\t\tif err != nil {\n\t\t\tLog.Debug(\"files::save::tus action=backend_save step=header_check_post err=%s\", err.Error())\n\t\t\tSendErrorResult(res, ErrNotValid)\n\t\t\treturn\n\t\t}\n\t\tctx.Context = context.Background()\n\t\tb, err := ctx.Backend.Init(ctx.Session, ctx)\n\t\tif err != nil {\n\t\t\tLog.Debug(\"files::save::tus action=backend_save step=backend_init err=%s\", err.Error())\n\t\t\tSendErrorResult(res, ErrNotValid)\n\t\t\treturn\n\t\t}\n\t\tuploader := createChunkedUploader(b.Save, path, size)\n\t\tchunkedUploadCache.Set(cacheKey, uploader)\n\t\th.Set(\"Tus-Resumable\", \"1.0.0\")\n\t\th.Set(\"Content-Length\", \"0\")\n\t\th.Set(\"Location\", req.URL.String())\n\t\tres.WriteHeader(http.StatusCreated)\n\t\treturn\n\t}\n\tif proto == \"tus\" && req.Method == http.MethodPatch {\n\t\tif req.Header.Get(\"Content-Type\") != \"application/offset+octet-stream\" {\n\t\t\tSendErrorResult(res, NewError(\"Unsupported Media Type\", 415))\n\t\t\treturn\n\t\t}\n\t\tvar (\n\t\t\thash             hash.Hash\n\t\t\texpectedChecksum string\n\t\t)\n\t\tif checksumHeader := req.Header.Get(\"upload-checksum\"); checksumHeader != \"\" {\n\t\t\tparts := strings.SplitN(checksumHeader, \" \", 2)\n\t\t\tif len(parts) != 2 {\n\t\t\t\tSendErrorResult(res, NewError(\"Bad Request\", 400))\n\t\t\t\treturn\n\t\t\t} else if parts[0] == \"sha1\" {\n\t\t\t\thash = sha1.New()\n\t\t\t} else if parts[1] == \"crc32\" {\n\t\t\t\thash = crc32.NewIEEE()\n\t\t\t} else {\n\t\t\t\tSendErrorResult(res, NewError(\"Bad Request\", 400))\n\t\t\t\treturn\n\t\t\t}\n\t\t\texpectedChecksum = parts[1]\n\t\t}\n\t\trequestOffset, err := strconv.ParseUint(req.Header.Get(\"Upload-Offset\"), 10, 0)\n\t\tif err != nil {\n\t\t\tLog.Debug(\"files::save::tus action=backend_save step=header_check_patch err=%s\", err.Error())\n\t\t\tSendErrorResult(res, ErrNotValid)\n\t\t\treturn\n\t\t}\n\t\tc := chunkedUploadCache.Get(cacheKey)\n\t\tif c == nil {\n\t\t\tLog.Debug(\"files::save::tus action=backend_save step=cache_fetch_patch\")\n\t\t\tSendErrorResult(res, NewError(\"Conflict\", 409))\n\t\t\treturn\n\t\t}\n\t\tuploader := c.(*chunkedUpload)\n\t\tinitialOffset, totalSize := uploader.Meta()\n\t\tif initialOffset != requestOffset {\n\t\t\tLog.Debug(\"files::save::tus action=uploader.next path=%s err=offset_missmatch\", path)\n\t\t\tSendErrorResult(res, ErrNotValid)\n\t\t\treturn\n\t\t}\n\t\treader := req.Body\n\t\tif hash != nil {\n\t\t\treader = io.NopCloser(io.TeeReader(req.Body, hash))\n\t\t}\n\t\tif err := uploader.Next(reader); err != nil {\n\t\t\tLog.Debug(\"files::save::tus action=uploader.next path=%s err=%s\", path, err.Error())\n\t\t\tSendErrorResult(res, NewError(err.Error(), 403))\n\t\t\treturn\n\t\t}\n\t\tif hash != nil && expectedChecksum != hex.EncodeToString(hash.Sum(nil)) {\n\t\t\tSendErrorResult(res, NewError(\"Checksum Mismatch\", 460))\n\t\t\treturn\n\t\t}\n\t\tnewOffset, _ := uploader.Meta()\n\t\tif newOffset > totalSize {\n\t\t\tuploader.Close()\n\t\t\tchunkedUploadCache.Del(cacheKey)\n\t\t\tLog.Warning(\"files::save::tus path=%s err=assert_offset size=%d old_offset=%d new_offset=%d\", path, totalSize, initialOffset, newOffset)\n\t\t\tSendErrorResult(res, NewError(\"aborted - offset larger than total size\", 403))\n\t\t\treturn\n\t\t} else if newOffset == totalSize {\n\t\t\tif err := uploader.Close(); err != nil {\n\t\t\t\tLog.Debug(\"files::save::tus action=uploader.close err=%s\", err.Error())\n\t\t\t\tSendErrorResult(res, ErrNotValid)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tchunkedUploadCache.Del(cacheKey)\n\t\t}\n\t\th.Set(\"Tus-Resumable\", \"1.0.0\")\n\t\th.Set(\"Upload-Offset\", fmt.Sprintf(\"%d\", newOffset))\n\t\tres.WriteHeader(http.StatusNoContent)\n\t\treturn\n\t}\n\tSendErrorResult(res, ErrNotImplemented)\n}\n\nfunc createChunkedUploader(save func(path string, file io.Reader) error, path string, size uint64) *chunkedUpload {\n\tr, w := io.Pipe()\n\tdone := make(chan error, 1)\n\tgo func() {\n\t\tdone <- save(path, r)\n\t}()\n\treturn &chunkedUpload{\n\t\tfn:     save,\n\t\tstream: w,\n\t\tdone:   done,\n\t\toffset: 0,\n\t\tsize:   size,\n\t}\n}\n\nfunc initChunkedUploader() {\n\tchunkedUploadCache = NewAppCache(60*24, 1)\n\tchunkedUploadCache.OnEvict(func(key string, value interface{}) {\n\t\tc := value.(*chunkedUpload)\n\t\tif c == nil {\n\t\t\tLog.Warning(\"ctrl::files::chunked::cleanup nil on close\")\n\t\t\treturn\n\t\t}\n\t\tif err := c.Close(); err != nil {\n\t\t\tLog.Warning(\"ctrl::files::chunked::cleanup action=close err=%s\", err.Error())\n\t\t\treturn\n\t\t}\n\t})\n}\n\ntype chunkedUpload struct {\n\tfn     func(path string, file io.Reader) error\n\tstream *io.PipeWriter\n\toffset uint64\n\tsize   uint64\n\tdone   chan error\n\tonce   sync.Once\n\tmu     sync.Mutex\n}\n\nfunc (this *chunkedUpload) Next(body io.ReadCloser) error {\n\tn, err := io.Copy(this.stream, body)\n\tbody.Close()\n\tthis.mu.Lock()\n\tthis.offset += uint64(n)\n\tthis.mu.Unlock()\n\treturn err\n}\n\nfunc (this *chunkedUpload) Close() error {\n\tthis.stream.Close()\n\terr := <-this.done\n\tthis.once.Do(func() {\n\t\tclose(this.done)\n\t})\n\treturn err\n}\n\nfunc (this *chunkedUpload) Meta() (uint64, uint64) {\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\treturn this.offset, this.size\n}\n\nfunc FileMv(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif model.CanEdit(ctx) == false {\n\t\tLog.Debug(\"mv::permission 'permission denied'\")\n\t\tSendErrorResult(res, NewError(\"Permission denied\", 403))\n\t\treturn\n\t}\n\n\tfrom, err := PathBuilder(ctx, req.URL.Query().Get(\"from\"))\n\tif err != nil {\n\t\tLog.Debug(\"mv::path::from '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tto, err := PathBuilder(ctx, req.URL.Query().Get(\"to\"))\n\tif err != nil {\n\t\tLog.Debug(\"mv::path::to '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tif from == \"\" || to == \"\" {\n\t\tLog.Debug(\"mv::params 'missing path parameter'\")\n\t\tSendErrorResult(res, NewError(\"missing path parameter\", 400))\n\t\treturn\n\t}\n\n\tfor _, auth := range Hooks.Get.AuthorisationMiddleware() {\n\t\tif err = auth.Mv(ctx, from, to); err != nil {\n\t\t\tLog.Info(\"mv::auth '%s'\", err.Error())\n\t\t\tSendErrorResult(res, ErrNotAuthorized)\n\t\t\treturn\n\t\t}\n\t}\n\n\terr = ctx.Backend.Mv(from, to)\n\tif err != nil {\n\t\tLog.Debug(\"mv::backend '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tSendSuccessResult(res, nil)\n}\n\nfunc FileRm(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif model.CanEdit(ctx) == false {\n\t\tLog.Debug(\"rm::permission 'permission denied'\")\n\t\tSendErrorResult(res, NewError(\"Permission denied\", 403))\n\t\treturn\n\t}\n\n\tpath, err := PathBuilder(ctx, req.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tLog.Debug(\"rm::path '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\n\tfor _, auth := range Hooks.Get.AuthorisationMiddleware() {\n\t\tif err = auth.Rm(ctx, path); err != nil {\n\t\t\tLog.Info(\"rm::auth '%s'\", err.Error())\n\t\t\tSendErrorResult(res, ErrNotAuthorized)\n\t\t\treturn\n\t\t}\n\t}\n\n\terr = ctx.Backend.Rm(path)\n\tif err != nil {\n\t\tLog.Debug(\"rm::backend '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tSendSuccessResult(res, nil)\n}\n\nfunc FileMkdir(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif model.CanUpload(ctx) == false {\n\t\tLog.Debug(\"mkdir::permission 'permission denied'\")\n\t\tSendErrorResult(res, NewError(\"Permission denied\", 403))\n\t\treturn\n\t}\n\n\tpath, err := PathBuilder(ctx, req.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tLog.Debug(\"mkdir::path '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\n\tfor _, auth := range Hooks.Get.AuthorisationMiddleware() {\n\t\tif err = auth.Mkdir(ctx, path); err != nil {\n\t\t\tLog.Info(\"mkdir::auth '%s'\", err.Error())\n\t\t\tSendErrorResult(res, ErrNotAuthorized)\n\t\t\treturn\n\t\t}\n\t}\n\n\terr = ctx.Backend.Mkdir(path)\n\tif err != nil {\n\t\tLog.Debug(\"mkdir::backend '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tSendSuccessResult(res, nil)\n}\n\nfunc FileTouch(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif model.CanUpload(ctx) == false {\n\t\tLog.Debug(\"touch::permission 'permission denied'\")\n\t\tSendErrorResult(res, NewError(\"Permission denied\", 403))\n\t\treturn\n\t}\n\n\tpath, err := PathBuilder(ctx, req.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tLog.Debug(\"touch::path '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\n\tfor _, auth := range Hooks.Get.AuthorisationMiddleware() {\n\t\tif err = auth.Touch(ctx, path); err != nil {\n\t\t\tLog.Info(\"touch::auth '%s'\", err.Error())\n\t\t\tSendErrorResult(res, ErrNotAuthorized)\n\t\t\treturn\n\t\t}\n\t}\n\n\terr = ctx.Backend.Touch(path)\n\tif err != nil {\n\t\tLog.Debug(\"touch::backend '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tSendSuccessResult(res, nil)\n}\n\nfunc FileDownloader(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tvar err error\n\tif model.CanRead(ctx) == false {\n\t\tLog.Debug(\"downloader::permission 'permission denied'\")\n\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\treturn\n\t}\n\tpaths := req.URL.Query()[\"path\"]\n\tfor i := 0; i < len(paths); i++ {\n\t\tif paths[i], err = PathBuilder(ctx, paths[i]); err != nil {\n\t\t\tLog.Debug(\"downloader::path '%s'\", err.Error())\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tresHeader := res.Header()\n\tresHeader.Set(\"Content-Type\", \"application/zip\")\n\tfilename := \"download\"\n\tif len(paths) == 1 {\n\t\tfilename = filepath.Base(paths[0])\n\t}\n\tresHeader.Set(\"Content-Disposition\", fmt.Sprintf(\"attachment; filename=\\\"%s.zip\\\"\", filename))\n\n\tstart := time.Now()\n\tvar addToZipRecursive func(*App, *zip.Writer, string, string, *[]string) error\n\taddToZipRecursive = func(c *App, zw *zip.Writer, backendPath string, zipRoot string, errList *[]string) (err error) {\n\t\tif time.Now().Sub(start) > time.Duration(zip_timeout())*time.Second {\n\t\t\tLog.Debug(\"downloader::timeout zip not completed due to timeout\")\n\t\t\treturn ErrTimeout\n\t\t}\n\t\tif strings.HasSuffix(backendPath, \"/\") == false {\n\t\t\t// Process File\n\t\t\tzipPath := strings.TrimPrefix(backendPath, zipRoot)\n\t\t\tfile, err := ctx.Backend.Cat(backendPath)\n\t\t\tif err != nil {\n\t\t\t\t*errList = append(*errList, fmt.Sprintf(\"downloader::cat %s %s\\n\", zipPath, err.Error()))\n\t\t\t\tif err == ErrNotReachable {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\tLog.Debug(\"downloader::cat backendPath['%s'] zipPath['%s'] error['%s']\", backendPath, zipPath, err.Error())\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tzipFile, err := zw.Create(zipPath)\n\t\t\tif err != nil {\n\t\t\t\t*errList = append(*errList, fmt.Sprintf(\"downloader::create %s %s\\n\", zipPath, err.Error()))\n\t\t\t\tLog.Debug(\"downloader::create backendPath['%s'] zipPath['%s'] error['%s']\", backendPath, zipPath, err.Error())\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif _, err = io.Copy(zipFile, file); err != nil {\n\t\t\t\t*errList = append(*errList, fmt.Sprintf(\"downloader::copy %s %s\\n\", zipPath, err.Error()))\n\t\t\t\tLog.Debug(\"downloader::copy backendPath['%s'] zipPath['%s'] error['%s']\", backendPath, zipPath, err.Error())\n\t\t\t\tio.Copy(zipFile, strings.NewReader(\"\"))\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfile.Close()\n\t\t\treturn nil\n\t\t}\n\t\t// Process Folder\n\t\tentries, err := c.Backend.Ls(backendPath)\n\t\tif err != nil {\n\t\t\t*errList = append(*errList, fmt.Sprintf(\"downloader::ls %s\\n\", err.Error()))\n\t\t\tLog.Debug(\"downloader::ls path['%s'] error['%s']\", backendPath, err.Error())\n\t\t\treturn err\n\t\t}\n\t\tfor i := 0; i < len(entries); i++ {\n\t\t\tnewBackendPath := backendPath + entries[i].Name()\n\t\t\tif entries[i].IsDir() {\n\t\t\t\tnewBackendPath += \"/\"\n\t\t\t}\n\t\t\tif err = addToZipRecursive(ctx, zw, newBackendPath, zipRoot, errList); err != nil {\n\t\t\t\t*errList = append(*errList, fmt.Sprintf(\"downloader::recursive %s\\n\", err.Error()))\n\t\t\t\tLog.Debug(\"downloader::recursive path['%s'] error['%s']\", newBackendPath, err.Error())\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tzipWriter := zip.NewWriter(res)\n\tdefer zipWriter.Close()\n\terrList := []string{}\n\tfor i := 0; i < len(paths); i++ {\n\t\tzipRoot := \"\"\n\t\tif strings.HasSuffix(paths[i], \"/\") {\n\t\t\tzipRoot = strings.TrimSuffix(paths[i], filepath.Base(paths[i])+\"/\")\n\t\t} else {\n\t\t\tzipRoot = strings.TrimSuffix(paths[i], filepath.Base(paths[i]))\n\t\t}\n\n\t\tfor _, auth := range Hooks.Get.AuthorisationMiddleware() {\n\t\t\tif err = auth.Ls(ctx, paths[i]); err != nil {\n\t\t\t\tLog.Info(\"downloader::ls::auth path['%s'] => '%s'\", paths[i], err.Error())\n\t\t\t\tSendErrorResult(res, ErrNotAuthorized)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err = auth.Cat(ctx, paths[i]); err != nil {\n\t\t\t\tLog.Info(\"downloader::cat::auth path['%s'] => '%s'\", paths[i], err.Error())\n\t\t\t\tSendErrorResult(res, ErrNotAuthorized)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\taddToZipRecursive(ctx, zipWriter, paths[i], zipRoot, &errList)\n\t}\n\tif len(errList) > 0 {\n\t\tif errorWriter, err := zipWriter.Create(\"error.log\"); err == nil {\n\t\t\tfor _, e := range errList {\n\t\t\t\tio.Copy(errorWriter, strings.NewReader(e))\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc FileExtract(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif model.CanRead(ctx) == false {\n\t\tLog.Debug(\"extract::permission 'permission denied'\")\n\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\treturn\n\t}\n\tpaths := req.URL.Query()[\"path\"]\n\tfor _, auth := range Hooks.Get.AuthorisationMiddleware() {\n\t\tfor i := 0; i < len(paths); i++ {\n\t\t\tif err := auth.Mkdir(ctx, paths[i]); err != nil {\n\t\t\t\tLog.Debug(\"extract::permission::mkdir %s\", err.Error())\n\t\t\t\tSendErrorResult(res, ErrNotAuthorized)\n\t\t\t\treturn\n\t\t\t} else if err := auth.Save(ctx, paths[i]); err != nil {\n\t\t\t\tLog.Debug(\"extract::permission::Save %s\", err.Error())\n\t\t\t\tSendErrorResult(res, ErrNotAuthorized)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tc, cancel := context.WithTimeout(ctx.Context, time.Duration(zip_timeout())*time.Second)\n\textractPath := func(base string, path string) (string, error) {\n\t\tbase = filepath.Dir(base)\n\t\tpath = filepath.Join(base, path)\n\t\tif strings.HasPrefix(path, base) == false {\n\t\t\treturn \"\", ErrFilesystemError\n\t\t}\n\t\treturn path, nil\n\t}\n\textractZip := func(path string) (err error) {\n\t\tif err = c.Err(); err != nil {\n\t\t\tcancel()\n\t\t\treturn ErrTimeout\n\t\t}\n\n\t\tzipFile, err := ctx.Backend.Cat(path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer zipFile.Close()\n\t\tf, err := os.CreateTemp(\"\", \"tmpzip.*.zip\")\n\t\tif err != nil {\n\t\t\tLog.Debug(\"extract::create_temp '%s'\", err.Error())\n\t\t\treturn nil\n\t\t}\n\t\tdefer os.Remove(f.Name())\n\t\tio.Copy(f, zipFile)\n\t\ts, err := f.Stat()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tr, err := zip.NewReader(f, s.Size())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tisFolderAlreadyCreated := map[string]bool{\n\t\t\tfmt.Sprintf(\"%s/\", filepath.Dir(path)): true,\n\t\t}\n\t\tfor _, f := range r.File {\n\t\t\ttime.Sleep(2 * time.Millisecond)\n\t\t\tif err = c.Err(); err != nil {\n\t\t\t\tcancel()\n\t\t\t\treturn ErrTimeout\n\t\t\t}\n\t\t\t// STEP1: ensure the underlying folders exists\n\t\t\tspl := strings.Split(f.Name, \"/\")\n\t\t\tfor i, p := range spl {\n\t\t\t\tif p == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tp = strings.Join(spl[0:i], \"/\")\n\t\t\t\tp, err = extractPath(path, p)\n\t\t\t\tif strings.HasSuffix(p, \"/\") == false {\n\t\t\t\t\tp += \"/\"\n\t\t\t\t}\n\t\t\t\tif isFolderAlreadyCreated[p] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tisFolderAlreadyCreated[p] = true\n\t\t\t\tif err := ctx.Backend.Mkdir(p); err != nil {\n\t\t\t\t\tLog.Debug(\"extract::mkdir err %s\", err.Error())\n\t\t\t\t}\n\t\t\t}\n\t\t\t// STEP2: create the file\n\t\t\tif f.FileInfo().IsDir() == false {\n\t\t\t\tp, err := extractPath(path, f.Name)\n\t\t\t\tif err != nil {\n\t\t\t\t\tLog.Debug(\"extract::chroot %s\", err.Error())\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\trc, err := f.Open()\n\t\t\t\tif err != nil {\n\t\t\t\t\tLog.Debug(\"extract::fopen %s\", err.Error())\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\terr = ctx.Backend.Save(p, rc)\n\t\t\t\trc.Close()\n\t\t\t\tif err != nil {\n\t\t\t\t\tLog.Debug(\"extract::save err %s\", err.Error())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tvar err error\n\tfor i := 0; i < len(paths); i++ {\n\t\tif paths[i], err = PathBuilder(ctx, paths[i]); err != nil {\n\t\t\tLog.Debug(\"extract::path '%s'\", err.Error())\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\t\tif err = extractZip(paths[i]); err != nil {\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\t}\n\tSendSuccessResult(res, nil)\n}\n\nfunc PathBuilder(ctx *App, path string) (string, error) {\n\tif path == \"\" {\n\t\treturn \"\", NewError(\"No path available\", 400)\n\t}\n\tsessionPath := ctx.Session[\"path\"]\n\tbasePath := filepath.ToSlash(filepath.Join(sessionPath, path))\n\tif path[len(path)-1:] == \"/\" && basePath != \"/\" {\n\t\tbasePath += \"/\"\n\t}\n\tif strings.HasPrefix(basePath, ctx.Session[\"path\"]) == false {\n\t\treturn \"\", ErrFilesystemError\n\t}\n\treturn basePath, nil\n}\n"
  },
  {
    "path": "server/ctrl/metadata.go",
    "content": "package ctrl\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc MetaGet(ctx *App, w http.ResponseWriter, r *http.Request) {\n\tm := Hooks.Get.Metadata()\n\tif m == nil {\n\t\tSendErrorResult(w, ErrNotImplemented)\n\t\treturn\n\t}\n\tpath, err := PathBuilder(ctx, r.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tout, err := m.Get(ctx, path)\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tSendSuccessResults(w, out)\n}\n\nfunc MetaUpsert(ctx *App, w http.ResponseWriter, r *http.Request) {\n\tm := Hooks.Get.Metadata()\n\tif m == nil {\n\t\tSendErrorResult(w, ErrNotImplemented)\n\t\treturn\n\t}\n\tpath, err := PathBuilder(ctx, r.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tforms := []FormElement{}\n\tif err := json.NewDecoder(r.Body).Decode(&forms); err != nil {\n\t\tSendErrorResult(w, ErrNotImplemented)\n\t\treturn\n\t}\n\tif err := m.Set(ctx, path, forms); err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tSendSuccessResult(w, nil)\n}\n\nfunc MetaSearch(ctx *App, w http.ResponseWriter, r *http.Request) {\n\tm := Hooks.Get.Metadata()\n\tif m == nil {\n\t\tSendErrorResult(w, ErrNotImplemented)\n\t\treturn\n\t}\n\tfacets := map[string]any{}\n\tif err := json.NewDecoder(r.Body).Decode(&facets); err != nil {\n\t\tSendErrorResult(w, ErrNotImplemented)\n\t\treturn\n\t}\n\tpath, err := PathBuilder(ctx, fmt.Sprintf(\"%s\", facets[\"path\"]))\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tdelete(facets, \"path\")\n\tout, err := m.Search(ctx, path, facets)\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tSendSuccessResults(w, out)\n}\n"
  },
  {
    "path": "server/ctrl/plugin.go",
    "content": "package ctrl\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n\n\t\"github.com/gorilla/mux\"\n)\n\nfunc PluginExportHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tplgExports := map[string][]string{}\n\tfor name, plg := range model.PLUGINS {\n\t\tfor _, module := range plg.Modules {\n\t\t\tif module[\"type\"] == \"xdg-open\" {\n\t\t\t\tindex := module[\"entrypoint\"]\n\t\t\t\tif index == \"\" {\n\t\t\t\t\tindex = \"/index.js\"\n\t\t\t\t}\n\t\t\t\tplgExports[module[\"mime\"]] = []string{\n\t\t\t\t\tmodule[\"application\"],\n\t\t\t\t\tWithBase(JoinPath(\"/assets/\"+BUILD_REF+\"/plugin/\", filepath.Join(name+\".zip\", index))),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tSendSuccessResultWithEtagAndGzip(res, req, plgExports)\n}\n\nfunc PluginStaticHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tpath := mux.Vars(req)[\"path\"]\n\tmtype := GetMimeType(path)\n\tstaticConfig := []struct {\n\t\tContentType string\n\t\tFileExt     string\n\t}{\n\t\t{\"br\", \".br\"},\n\t\t{\"gzip\", \".gz\"},\n\t\t{\"\", \"\"},\n\t}\n\n\tvar (\n\t\tb   []byte\n\t\terr error\n\t)\n\thead := res.Header()\n\tacceptEncoding := req.Header.Get(\"Accept-Encoding\")\n\tfor _, cfg := range staticConfig {\n\t\tif strings.Contains(acceptEncoding, cfg.ContentType) == false {\n\t\t\tcontinue\n\t\t}\n\t\tb, err = model.GetPluginFile(mux.Vars(req)[\"name\"], path+cfg.FileExt)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\thead.Set(\"Content-Type\", mtype)\n\t\tif cfg.ContentType != \"\" {\n\t\t\thead.Set(\"Content-Encoding\", cfg.ContentType)\n\t\t}\n\t\tres.Write(b)\n\t\treturn\n\t}\n\tSendErrorResult(res, err)\n\treturn\n}\n\nfunc PluginDownloadHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tf, err := os.Open(JoinPath(\n\t\tGetAbsolutePath(PLUGIN_PATH),\n\t\tmux.Vars(req)[\"name\"]+\".zip\",\n\t))\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tio.Copy(res, f)\n\tf.Close()\n}\n"
  },
  {
    "path": "server/ctrl/report.go",
    "content": "package ctrl\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc ReportHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t// This function is quite dumb indeed, the goal is to show a report trace in the logs\n\tSendSuccessResult(res, nil)\n}\n\nfunc WellKnownSecurityHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif IsWhiteLabel() {\n\t\tNotFoundHandler(ctx, res, req)\n\t\treturn\n\t}\n\tres.WriteHeader(http.StatusOK)\n\tres.Write([]byte(\"# If you would like to report a security issue\\n\"))\n\tres.Write([]byte(\"# you may report it to me via email\\n\"))\n\tres.Write([]byte(\"Contact: support@filestash.app\\n\"))\n}\n\nfunc HealthHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tres.Header().Set(\"Access-Control-Allow-Origin\", \"*\")\n\tres.Header().Set(\"Content-Type\", \"application/json\")\n\n\t// CHECK 1: open the config file\n\tfile, err := os.OpenFile(\n\t\tGetAbsolutePath(CONFIG_PATH, \"config.json\"),\n\t\tos.O_RDONLY, os.ModePerm,\n\t)\n\tif err != nil {\n\t\tres.WriteHeader(http.StatusInternalServerError)\n\t\tres.Write([]byte(`{\"status\": \"error\", \"reason\": \"fopen_error\"}`))\n\t\treturn\n\t}\n\tif _, err := file.Read(make([]byte, 10)); err != nil {\n\t\tfile.Close()\n\t\tres.WriteHeader(http.StatusInternalServerError)\n\t\tres.Write([]byte(`{\"status\": \"error\", \"reason\": \"fread_error\"}`))\n\t\treturn\n\t}\n\tfile.Close()\n\n\t// CHECK2: about page\n\tr, err := http.Get(fmt.Sprintf(\n\t\t\"%s://127.0.0.1:%d/about\",\n\t\tfunc() string {\n\t\t\tif req.TLS == nil {\n\t\t\t\treturn \"http\"\n\t\t\t}\n\t\t\treturn \"https\"\n\t\t}(),\n\t\tConfig.Get(\"general.port\").Int(),\n\t))\n\tif err == nil {\n\t\tr.Body.Close()\n\t\tif r.StatusCode != http.StatusOK {\n\t\t\tres.WriteHeader(http.StatusInternalServerError)\n\t\t\tres.Write([]byte(fmt.Sprintf(`{\"status\": \"error\", \"reason\": \"endpoint_error\", \"debug\": \"status=%d\"}`, r.StatusCode)))\n\t\t\treturn\n\t\t}\n\t}\n\n\t// CHECK3: config sanity check\n\tcgsk := Config.Get(\"general.secret_key\").String()\n\tcaa := Config.Get(\"auth.admin\").String()\n\tif len(cgsk) != 16 || len(caa) != 60 {\n\t\tm, _ := json.MarshalIndent(\n\t\t\t[]string{\n\t\t\t\tfmt.Sprintf(\n\t\t\t\t\t\"general.secret_key[size=%d]\",\n\t\t\t\t\tlen(cgsk),\n\t\t\t\t),\n\t\t\t\tfmt.Sprintf(\n\t\t\t\t\t\"admin.auth[size=%d]\",\n\t\t\t\t\tlen(caa),\n\t\t\t\t),\n\t\t\t\tfmt.Sprintf(\n\t\t\t\t\t\"log[level=%s]\",\n\t\t\t\t\tConfig.Get(\"log.level\").String(),\n\t\t\t\t),\n\t\t\t\tfmt.Sprintf(\n\t\t\t\t\t\"connections[size=%d]\",\n\t\t\t\t\tlen(Config.Conn),\n\t\t\t\t),\n\t\t\t\tfmt.Sprintf(\n\t\t\t\t\t\"middleware.identity_provider[type=%s][params=%d]\",\n\t\t\t\t\tConfig.Get(\"middleware.identity_provider.type\").String(),\n\t\t\t\t\tlen(Config.Get(\"middleware.identity_provider.params\").String()),\n\t\t\t\t),\n\t\t\t\tfmt.Sprintf(\n\t\t\t\t\t\"middleware.attribute_mapping[type=%s][params=%d]\",\n\t\t\t\t\tstrings.ReplaceAll(Config.Get(\"middleware.attribute_mapping.related_backend\").String(), \" \", \"\"),\n\t\t\t\t\tlen(Config.Get(\"middleware.attribute_mapping.params\").String()),\n\t\t\t\t),\n\t\t\t},\n\t\t\t\"\", \"    \",\n\t\t)\n\t\tstatus := \"error\"\n\t\tif os.Getenv(\"ADMIN_PASSWORD\") != \"\" && len(caa) == 0 {\n\t\t\tres.WriteHeader(http.StatusServiceUnavailable)\n\t\t\tLog.Error(\"ctrl::report::healthz message=corrupted_config check=3A config=%s\", m)\n\t\t} else if len(cgsk) != 16 {\n\t\t\tres.WriteHeader(http.StatusServiceUnavailable)\n\t\t\tLog.Error(\"ctrl::report::healthz message=corrupted_config check=3B config=%s\", m)\n\t\t} else {\n\t\t\tres.WriteHeader(http.StatusOK)\n\t\t\tstatus = \"transcient\"\n\t\t}\n\t\tres.Write([]byte(fmt.Sprintf(\n\t\t\t`{\"status\": \"%s\", \"reason\": \"configuration_error\", \"debug\": %s}`,\n\t\t\tstatus, m,\n\t\t)))\n\t\treturn\n\t}\n\n\t// SUCCESS!!\n\tres.WriteHeader(http.StatusOK)\n\tif req.Method != \"HEAD\" {\n\t\tres.Write([]byte(`{\"status\": \"pass\"}`))\n\t}\n}\n"
  },
  {
    "path": "server/ctrl/search.go",
    "content": "package ctrl\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc FileSearch(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tpath, err := PathBuilder(ctx, req.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tpath = \"/\"\n\t}\n\tq := req.URL.Query().Get(\"q\")\n\tif model.CanRead(ctx) == false {\n\t\tLog.Debug(\"ctrl::search 'can not read \\\"%s\\\"'\", path)\n\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\treturn\n\t}\n\n\tvar searchResults []IFile\n\tsearchEngine := Hooks.Get.SearchEngine()\n\tif searchEngine == nil {\n\t\tSendErrorResult(res, ErrMissingDependency)\n\t\treturn\n\t}\n\tsearchResults, err = searchEngine.Query(*ctx, path, q)\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\n\t// overwrite the path of a file according to chroot\n\tif ctx.Session[\"path\"] != \"\" {\n\t\tfor i := 0; i < len(searchResults); i++ {\n\t\t\tsearchResults[i] = File{\n\t\t\t\tFName: searchResults[i].Name(),\n\t\t\t\tFSize: searchResults[i].Size(),\n\t\t\t\tFType: func() string {\n\t\t\t\t\tif searchResults[i].IsDir() {\n\t\t\t\t\t\treturn \"directory\"\n\t\t\t\t\t}\n\t\t\t\t\treturn \"file\"\n\t\t\t\t}(),\n\t\t\t\tFPath: \"/\" + strings.TrimPrefix(\n\t\t\t\t\tsearchResults[i].Path(),\n\t\t\t\t\tctx.Session[\"path\"],\n\t\t\t\t),\n\t\t\t}\n\t\t}\n\t}\n\tSendSuccessResults(res, searchResults)\n}\n"
  },
  {
    "path": "server/ctrl/session.go",
    "content": "package ctrl\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/middleware\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n\n\t\"github.com/gorilla/mux\"\n)\n\ntype Session struct {\n\tHome          *string `json:\"home,omitempty\"`\n\tIsAuth        bool    `json:\"is_authenticated\"`\n\tBackend       string  `json:\"backendID\"`\n\tAuthorization string  `json:\"authorization,omitempty\"`\n}\n\nfunc SessionGet(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tr := Session{\n\t\tIsAuth: false,\n\t}\n\tif ctx.Backend == nil {\n\t\tSendSuccessResult(res, r)\n\t\treturn\n\t}\n\thome, err := model.GetHome(ctx.Backend, ctx.Session[\"path\"])\n\tif err != nil {\n\t\tSendSuccessResult(res, r)\n\t\treturn\n\t} else if ctx.Share.Id != \"\" {\n\t\thome = \"/\"\n\t}\n\tr.IsAuth = true\n\tr.Home = NewString(home)\n\tr.Backend = backendID(ctx.Session)\n\tif ctx.Share.Id == \"\" && Config.Get(\"features.protection.enable_chromecast\").Bool() {\n\t\tr.Authorization = ctx.Authorization\n\t}\n\tSendSuccessResult(res, r)\n}\n\nfunc SessionAuthenticate(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tctx.Body[\"timestamp\"] = time.Now().Format(time.RFC3339)\n\tsession := model.MapStringInterfaceToMapStringString(ctx.Body)\n\tsession[\"path\"] = EnforceDirectory(session[\"path\"])\n\n\tbackend, err := model.NewBackend(ctx, session)\n\tif err != nil {\n\t\tLog.Debug(\"[auth] action=authenticate::newBackend err=%s\", ferror(err))\n\t\tLog.Stdout(\"AUDIT action[fail] backend[%s] user[%s] target[%s]\", session[\"type\"], backendID(session), ip(req))\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\n\tif obj, ok := backend.(interface {\n\t\tOAuthToken(*map[string]interface{}) error\n\t}); ok {\n\t\terr := obj.OAuthToken(&ctx.Body)\n\t\tif err != nil {\n\t\t\tLog.Debug(\"[auth] action=authenticate::oauthtoken err=%s\", ferror(err))\n\t\t\tSendErrorResult(res, NewError(\"Can't authenticate (OAuth error)\", 401))\n\t\t\treturn\n\t\t}\n\t\tsession = model.MapStringInterfaceToMapStringString(ctx.Body)\n\t\tbackend, err = model.NewBackend(ctx, session)\n\t\tif err != nil {\n\t\t\tLog.Debug(\"[auth] action=authenticate::oauth::newBackend err=%s\", ferror(err))\n\t\t\tLog.Stdout(\"AUDIT action[fail] backend[%s] user[%s] target[%s]\", session[\"type\"], username(session), ip(req))\n\t\t\tSendErrorResult(res, NewError(\"Can't authenticate\", 401))\n\t\t\treturn\n\t\t}\n\t}\n\n\thome, err := model.GetHome(backend, session[\"path\"])\n\tif err != nil {\n\t\tLog.Debug(\"[auth] action=authenticate::getHome err=%s\", ferror(err))\n\t\tSendErrorResult(res, ErrAuthenticationFailed)\n\t\treturn\n\t}\n\n\ts, err := json.Marshal(session)\n\tif err != nil {\n\t\tLog.Debug(\"[auth] action=authenticate::marshall err=%s\", ferror(err))\n\t\tSendErrorResult(res, NewError(err.Error(), 500))\n\t\treturn\n\t}\n\tobfuscate, err := EncryptString(SECRET_KEY_DERIVATE_FOR_USER, string(s))\n\tif err != nil {\n\t\tLog.Debug(\"[auth] action=authenticate::encrypt err=%s\", ferror(err))\n\t\tSendErrorResult(res, NewError(err.Error(), 500))\n\t\treturn\n\t}\n\t// split session cookie if greater than 3800 bytes\n\tvalue_limit := 3800\n\tindex := 0\n\tend := 0\n\tfor {\n\t\tif len(obfuscate) >= (index+1)*value_limit {\n\t\t\tend = (index + 1) * value_limit\n\t\t} else {\n\t\t\tend = len(obfuscate)\n\t\t}\n\t\thttp.SetCookie(res, applyCookieRules(&http.Cookie{\n\t\t\tName:   CookieName(index),\n\t\t\tValue:  obfuscate[index*value_limit : end],\n\t\t\tMaxAge: 60 * Config.Get(\"general.cookie_timeout\").Int(),\n\t\t\tPath:   COOKIE_PATH,\n\t\t}, req))\n\t\tif end == len(obfuscate) {\n\t\t\tbreak\n\t\t} else {\n\t\t\tLog.Debug(\"[auth] action=authenticate::obfuscate index=%d length=%d total=%d\", index, len(obfuscate[index*value_limit:end]), len(obfuscate))\n\t\t\tindex++\n\t\t}\n\t}\n\tif Config.Get(\"features.protection.iframe\").String() != \"\" {\n\t\tres.Header().Set(\"bearer\", obfuscate)\n\t}\n\tLog.Stdout(\"AUDIT action[login] backend[%s] user[%s] target[%s]\", session[\"type\"], username(session), ip(req))\n\tSendSuccessResult(res, Session{\n\t\tIsAuth:        true,\n\t\tHome:          NewString(home),\n\t\tBackend:       backendID(session),\n\t\tAuthorization: obfuscate,\n\t})\n}\n\nfunc SessionLogout(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tgo func() {\n\t\t// user typically expect the logout to feel instant but in our case we still need to make sure\n\t\t// the connection is closed as lot of backend requires to hold an active session which we cache.\n\t\t// Whenever somebody logout after say 30 minutes idle, the logout would first create a connection\n\t\t// then close which can take a few seconds and make for a bad user experience.\n\t\t// By pushing that connection close in a goroutine, we make sure the logout is much faster for\n\t\t// the user while still retaining that functionality.\n\t\tmiddleware.SessionTry(func(c *App, _res http.ResponseWriter, _req *http.Request) {\n\t\t\tif c.Backend != nil {\n\t\t\t\tif obj, ok := c.Backend.(interface{ Close() error }); ok {\n\t\t\t\t\tobj.Close()\n\t\t\t\t}\n\t\t\t}\n\t\t})(ctx, res, req)\n\t}()\n\tindex := 0\n\tfor {\n\t\t_, err := req.Cookie(CookieName(index))\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\thttp.SetCookie(res, applyCookieRules(&http.Cookie{\n\t\t\tName:   CookieName(index),\n\t\t\tValue:  \"\",\n\t\t\tMaxAge: -1,\n\t\t\tPath:   COOKIE_PATH,\n\t\t}, req))\n\t\tindex++\n\t}\n\thttp.SetCookie(res, &http.Cookie{\n\t\tName:   COOKIE_NAME_ADMIN,\n\t\tValue:  \"\",\n\t\tMaxAge: -1,\n\t\tPath:   COOKIE_PATH_ADMIN,\n\t})\n\thttp.SetCookie(res, &http.Cookie{\n\t\tName:   COOKIE_NAME_PROOF,\n\t\tValue:  \"\",\n\t\tMaxAge: -1,\n\t\tPath:   COOKIE_PATH,\n\t})\n\tLog.Stdout(\"AUDIT action[logout] backend[%s] user[%s] target[%s]\", ctx.Session[\"type\"], username(ctx.Session), ip(req))\n\tSendSuccessResult(res, nil)\n}\n\nfunc SessionOAuthBackend(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tvars := mux.Vars(req)\n\ta := map[string]string{\n\t\t\"type\": vars[\"service\"],\n\t}\n\tb, err := model.NewBackend(ctx, a)\n\tif err != nil {\n\t\tLog.Debug(\"session::oauth 'NewBackend' %+v\", err)\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tobj, ok := b.(interface{ OAuthURL() string })\n\tif ok == false {\n\t\tLog.Debug(\"session::oauth 'Backend does not support oauth - \\\"%s\\\"'\", a[\"type\"])\n\t\tSendErrorResult(res, ErrNotSupported)\n\t\treturn\n\t}\n\tredirectUrl, err := url.Parse(obj.OAuthURL())\n\tif err != nil {\n\t\tLog.Debug(\"session::oauth 'Parse URL - \\\"%s\\\"'\", a[\"type\"])\n\t\tSendErrorResult(res, ErrNotValid)\n\t\treturn\n\t}\n\tstateValue := vars[\"service\"]\n\tif req.URL.Query().Get(\"next\") != \"\" {\n\t\tstateValue += \"::\" + req.URL.Query().Get(\"next\")\n\t}\n\tq := redirectUrl.Query()\n\tq.Set(\"state\", stateValue)\n\tredirectUrl.RawQuery = q.Encode()\n\tif strings.Contains(req.Header.Get(\"Accept\"), \"text/html\") {\n\t\thttp.Redirect(res, req, redirectUrl.String(), http.StatusSeeOther)\n\t\treturn\n\t}\n\tSendSuccessResult(res, redirectUrl.String())\n}\n\nfunc SessionAuthMiddleware(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tSSOCookieName := \"ssoref\"\n\n\t// Step0: Initialisation\n\t_get := req.URL.Query()\n\tplugin := func() IAuthentication {\n\t\tselectedPluginId := Config.Get(\"middleware.identity_provider.type\").String()\n\t\tif selectedPluginId == \"\" {\n\t\t\treturn nil\n\t\t}\n\t\tfor key, plugin := range Hooks.Get.AuthenticationMiddleware() {\n\t\t\tif key == selectedPluginId {\n\t\t\t\treturn plugin\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}()\n\tif plugin == nil {\n\t\thttp.Redirect(\n\t\t\tres, req,\n\t\t\t\"/?error=Not%20Found&trace=middleware not found\",\n\t\t\thttp.StatusTemporaryRedirect,\n\t\t)\n\t\treturn\n\t}\n\tformData := map[string]string{}\n\tfor key, element := range _get {\n\t\tif len(element) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tformData[key] = element[0]\n\t}\n\tif req.Method == http.MethodPost {\n\t\tif err := req.ParseForm(); err != nil {\n\t\t\thttp.Redirect(\n\t\t\t\tres, req,\n\t\t\t\t\"/?error=Not%20Valid&trace=parsing body - \"+err.Error(),\n\t\t\t\thttp.StatusTemporaryRedirect,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tfor key, values := range req.Form {\n\t\t\tif len(values) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tformData[key] = values[0]\n\t\t}\n\t}\n\n\tidpParams := TmplParams(map[string]string{})\n\tif err := json.Unmarshal(\n\t\t[]byte(Config.Get(\"middleware.identity_provider.params\").String()),\n\t\t&idpParams,\n\t); err != nil {\n\t\thttp.Redirect(\n\t\t\tres, req,\n\t\t\t\"/?error=Not%20Valid&trace=unpacking idp - \"+err.Error(),\n\t\t\thttp.StatusTemporaryRedirect,\n\t\t)\n\t\treturn\n\t}\n\tfor k, v := range idpParams {\n\t\tout, err := TmplExec(NewStringFromInterface(v), idpParams)\n\t\tif err != nil {\n\t\t\thttp.Redirect(\n\t\t\t\tres, req,\n\t\t\t\t\"/?error=Not%20Valid&trace=idp - \"+err.Error(),\n\t\t\t\thttp.StatusTemporaryRedirect,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tidpParams[k] = out\n\t}\n\n\t// Step1: Entrypoint of the authentication process is handled by the plugin\n\tif req.Method == \"GET\" && _get.Get(\"action\") == \"redirect\" {\n\t\tif label := _get.Get(\"label\"); label != \"\" {\n\t\t\thttp.SetCookie(\n\t\t\t\tres,\n\t\t\t\tapplyCookieSameSiteRule(\n\t\t\t\t\tapplyCookieRules(&http.Cookie{\n\t\t\t\t\t\tName:   SSOCookieName,\n\t\t\t\t\t\tValue:  label + \"::\" + _get.Get(\"state\"),\n\t\t\t\t\t\tMaxAge: 60 * 10,\n\t\t\t\t\t\tPath:   COOKIE_PATH,\n\t\t\t\t\t}, req),\n\t\t\t\t\thttp.SameSiteDefaultMode,\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tif err := plugin.EntryPoint(idpParams, req, res); err != nil {\n\t\t\tLog.Error(\"entrypoint - %s\", err.Error())\n\t\t\tres.Header().Set(\"Content-Type\", \"text/html; charset=utf-8\")\n\t\t\tres.WriteHeader(http.StatusOK)\n\t\t\tres.Write([]byte(Page(err.Error())))\n\t\t}\n\t\treturn\n\t}\n\n\t// Step2: End of the authentication process. Could come from:\n\t// - target of a html form. eg: ldap, mysql, ...\n\t// - identity provider redirection uri. eg: oauth2, openid, ...\n\tpluginCallback, err := plugin.Callback(formData, idpParams, res)\n\tif err == ErrAuthenticationFailed {\n\t\tLog.Warning(\"failed authentication - %s\", err.Error())\n\t\thttp.Redirect(\n\t\t\tres, req,\n\t\t\treq.URL.Path+\"?action=redirect\",\n\t\t\thttp.StatusSeeOther,\n\t\t)\n\t\treturn\n\t} else if err != nil && strings.HasPrefix(res.Header().Get(\"Content-Type\"), \"text/html\") == false {\n\t\tLog.Error(\"session::authMiddleware 'callback error - %s'\", err.Error())\n\t\thttp.Redirect(\n\t\t\tres, req,\n\t\t\t\"/?error=\"+ErrNotAllowed.Error()+\"&trace=redirect request failed - \"+err.Error(),\n\t\t\thttp.StatusSeeOther,\n\t\t)\n\t\treturn\n\t} else if err != nil { // response handled directly within a plugin\n\t\treturn\n\t}\n\ttemplateBind := TmplParams(pluginCallback)\n\n\tvar (\n\t\tlabel = \"\"\n\t\tstate = \"\"\n\t)\n\tif refCookie, err := req.Cookie(SSOCookieName); err == nil { // TODO: deprecate SSOCookieName\n\t\ts := strings.SplitN(refCookie.Value, \"::\", 2)\n\t\tswitch len(s) {\n\t\tcase 1:\n\t\t\tlabel = s[0]\n\t\tcase 2:\n\t\t\tlabel = s[0]\n\t\t\tstate = s[1]\n\t\t}\n\t} else if l := req.URL.Query().Get(\"label\"); l != \"\" {\n\t\tlabel = l\n\t\tstate = req.URL.Query().Get(\"state\")\n\t} else {\n\t\tLog.Warning(\"session::authMiddleware action=callback_error err=missing_label url=%s\", req.URL.String())\n\t}\n\tif decodedState, err := base64.StdEncoding.DecodeString(state); err == nil {\n\t\tstateStruct := map[string]string{}\n\t\tjson.Unmarshal(decodedState, &stateStruct)\n\n\t\t// check variables are \"legit\"\n\t\tattributes := \"\"\n\t\tsignature := \"\"\n\t\tfields := strings.Split(Config.Get(\"features.protection.signature\").String(), \",\")\n\t\tfor k, v := range stateStruct {\n\t\t\tif k == \"signature\" {\n\t\t\t\tsignature = v\n\t\t\t}\n\t\t\tif slices.Contains(fields, k) {\n\t\t\t\tattributes += fmt.Sprintf(\"%s[%s] \", k, v)\n\t\t\t}\n\t\t}\n\t\tif attributes = strings.TrimSpace(attributes); attributes != \"\" {\n\t\t\tv, err := DecryptString(SECRET_KEY_DERIVATE_FOR_SIGNATURE, signature)\n\t\t\tif err != nil || attributes != v {\n\t\t\t\tv, _ = EncryptString(SECRET_KEY_DERIVATE_FOR_SIGNATURE, attributes)\n\t\t\t\tLog.Debug(\"callback signature is required, signature=%s\", v)\n\t\t\t\thttp.Redirect(\n\t\t\t\t\tres, req,\n\t\t\t\t\tWithBase(\"/?error=Invalid%20Signature&trace=signature is not correct\"),\n\t\t\t\t\thttp.StatusTemporaryRedirect,\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// populate variable\n\t\tfor key, value := range stateStruct {\n\t\t\tif templateBind[key] != \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttemplateBind[key] = value\n\t\t}\n\t}\n\tredirectURI := templateBind[\"next\"]\n\tif redirectURI == \"\" {\n\t\tredirectURI = WithBase(\"/\")\n\t}\n\tif templateBind[\"nav\"] != \"\" {\n\t\tredirectURI += \"?nav=\" + templateBind[\"nav\"]\n\t}\n\n\t// Step3: create a backend connection object\n\tsession, err := func(tb map[string]string) (map[string]string, error) {\n\t\tglobalMapping := map[string]map[string]interface{}{}\n\t\tif err = json.Unmarshal(\n\t\t\t[]byte(Config.Get(\"middleware.attribute_mapping.params\").String()),\n\t\t\t&globalMapping,\n\t\t); err != nil {\n\t\t\tLog.Warning(\"session::authMiddlware 'attribute mapping error' %s\", err.Error())\n\t\t\treturn map[string]string{}, err\n\t\t}\n\t\tmappingToUse := map[string]string{}\n\t\tfor k, v := range globalMapping[label] {\n\t\t\tout, err := TmplExec(NewStringFromInterface(v), tb)\n\t\t\tif err != nil {\n\t\t\t\tLog.Debug(\"session::authMiddleware action=tmplExec err=%s\", err.Error())\n\t\t\t}\n\t\t\tmappingToUse[k] = out\n\t\t}\n\t\tmappingToUse[\"timestamp\"] = time.Now().Format(time.RFC3339)\n\t\tif label != \"\" && Config.Get(\"general.extended_session\").Bool() {\n\t\t\tpluginCallback[\"label\"] = label\n\t\t\tif jsonStr, err := json.Marshal(pluginCallback); err == nil {\n\t\t\t\tmappingToUse[\"session\"] = string(jsonStr)\n\t\t\t}\n\t\t}\n\t\treturn mappingToUse, nil\n\t}(templateBind)\n\tif err != nil {\n\t\tLog.Debug(\"session::authMiddleware 'auth mapping failed %s'\", err.Error())\n\t\thttp.Redirect(\n\t\t\tres, req,\n\t\t\tWithBase(\"/?error=Not%20Valid&trace=mapping_error - \"+err.Error()),\n\t\t\thttp.StatusTemporaryRedirect,\n\t\t)\n\t\treturn\n\t}\n\n\tif _, err := model.NewBackend(ctx, session); err != nil {\n\t\tLog.Debug(\"session::authMiddleware 'backend connection failed %s'\", err.Error())\n\t\tLog.Info(\"[auth] status=failed user=%s backend=%s::%s ip=%s err=%s\", username(session), session[\"type\"], backendID(session), ip(req), ferror(err))\n\t\turl := \"/?error=\" + ErrNotValid.Error() + \"&trace=backend error - \" + err.Error()\n\t\tif IsATranslatedError(err) {\n\t\t\turl = \"/?error=\" + err.Error() + \"&trace=backend error - \" + err.Error()\n\t\t}\n\t\thttp.Redirect(res, req, WithBase(url), http.StatusTemporaryRedirect)\n\t\treturn\n\t}\n\n\t// Step4: persist connection with a cookie\n\ts, err := json.Marshal(session)\n\tif err != nil {\n\t\tLog.Debug(\"session::authMiddleware 'session marshal error %+v'\", session)\n\t\tSendErrorResult(res, ErrNotValid)\n\t\treturn\n\t}\n\tobfuscate, err := EncryptString(SECRET_KEY_DERIVATE_FOR_USER, string(s))\n\tif err != nil {\n\t\tLog.Debug(\"session::authMiddleware 'encryption error - %s\", err.Error())\n\t\tSendErrorResult(res, ErrNotValid)\n\t\treturn\n\t}\n\thttp.SetCookie(res, applyCookieRules(&http.Cookie{ // TODO: deprecate SSOCookieName\n\t\tName:   SSOCookieName,\n\t\tValue:  \"\",\n\t\tMaxAge: -1,\n\t\tPath:   COOKIE_PATH,\n\t}, req))\n\thttp.SetCookie(res, applyCookieRules(&http.Cookie{\n\t\tName:   COOKIE_NAME_AUTH,\n\t\tValue:  obfuscate,\n\t\tMaxAge: 60 * Config.Get(\"general.cookie_timeout\").Int(),\n\t\tPath:   COOKIE_PATH,\n\t}, req))\n\tif Config.Get(\"features.protection.iframe\").String() != \"\" {\n\t\tredirectURI += \"#bearer=\" + obfuscate\n\t}\n\tLog.Info(\"[auth] status=success user=%s backend=%s::%s ip=%s\", username(session), session[\"type\"], backendID(session), ip(req))\n\thttp.Redirect(res, req, redirectURI, http.StatusSeeOther)\n}\n\nfunc applyCookieRules(cookie *http.Cookie, req *http.Request) *http.Cookie {\n\tcookie.HttpOnly = true\n\tcookie.SameSite = http.SameSiteStrictMode\n\tif Config.Get(\"features.protection.iframe\").String() != \"\" {\n\t\tif f := req.Header.Get(\"Referer\"); strings.HasPrefix(f, \"https://\") {\n\t\t\tcookie.Secure = true\n\t\t\tcookie.SameSite = http.SameSiteNoneMode\n\t\t\tcookie.Partitioned = true\n\t\t} else {\n\t\t\tLog.Warning(\"you are trying to access Filestash from a non secure origin ('%s') and with iframe enabled. Either use SSL or disable iframe from the admin console.\", f)\n\t\t}\n\t}\n\treturn cookie\n}\n\nfunc applyCookieSameSiteRule(cookie *http.Cookie, sameSiteValue http.SameSite) *http.Cookie {\n\tcookie.SameSite = sameSiteValue\n\treturn cookie\n}\n\nfunc backendID(session map[string]string) string {\n\treturn Hash(GenerateID(session)+session[\"path\"], 20)\n}\n\nfunc username(session map[string]string) string {\n\tif session[\"username\"] != \"\" {\n\t\treturn strings.ReplaceAll(session[\"username\"], \" \", \"+\")\n\t} else if session[\"user\"] != \"\" {\n\t\treturn strings.ReplaceAll(session[\"user\"], \" \", \"+\")\n\t}\n\treturn GenerateID(session)\n}\n\nfunc ip(req *http.Request) string {\n\tif xff := req.Header.Get(\"X-Forwarded-For\"); xff != \"\" {\n\t\tif parts := strings.Split(xff, \",\"); len(parts) > 0 {\n\t\t\treturn strings.TrimSpace(parts[0])\n\t\t}\n\t}\n\tif xrip := req.Header.Get(\"X-Real-Ip\"); xrip != \"\" {\n\t\treturn xrip\n\t}\n\thost, _, err := net.SplitHostPort(req.RemoteAddr)\n\tif err != nil {\n\t\treturn req.RemoteAddr\n\t}\n\treturn host\n}\n\nfunc ferror(err error) string {\n\treturn strings.ReplaceAll(err.Error(), \" \", \"+\")\n}\n"
  },
  {
    "path": "server/ctrl/share.go",
    "content": "package ctrl\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/gorilla/mux\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc ShareList(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tpath, err := PathBuilder(ctx, req.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tlistOfSharedLinks, err := model.ShareList(\n\t\tGenerateID(ctx.Session),\n\t\tpath,\n\t)\n\tif err != nil {\n\t\tLog.Debug(\"share::list '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\n\tfor i := 0; i < len(listOfSharedLinks); i++ {\n\t\tlistOfSharedLinks[i].Path = \"/\" + strings.TrimPrefix(listOfSharedLinks[i].Path, path)\n\t}\n\tSendSuccessResults(res, listOfSharedLinks)\n}\n\nfunc ShareUpsert(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tshare_id := mux.Vars(req)[\"share\"]\n\tif share_id == \"private\" {\n\t\tLog.Debug(\"share::upsert 'private'\")\n\t\tSendErrorResult(res, ErrNotValid)\n\t\treturn\n\t}\n\ts := Share{\n\t\tId: share_id,\n\t\tAuth: func() string {\n\t\t\tif ctx.Share.Id == \"\" {\n\t\t\t\tstr := \"\"\n\t\t\t\tindex := 0\n\t\t\t\tfor {\n\t\t\t\t\tcookie, err := req.Cookie(CookieName(index))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tindex++\n\t\t\t\t\tstr += cookie.Value\n\t\t\t\t}\n\t\t\t\treturn str\n\t\t\t}\n\t\t\treturn ctx.Share.Auth\n\t\t}(),\n\t\tBackend: func() string {\n\t\t\tif ctx.Share.Id == \"\" {\n\t\t\t\treturn GenerateID(ctx.Session)\n\t\t\t}\n\t\t\treturn ctx.Share.Backend\n\t\t}(),\n\t\tPath: func() string {\n\t\t\tleftPath := \"/\"\n\t\t\trightPath := strings.TrimPrefix(NewStringFromInterface(ctx.Body[\"path\"]), \"/\")\n\t\t\tif ctx.Share.Id != \"\" {\n\t\t\t\tleftPath = ctx.Share.Path\n\t\t\t} else {\n\t\t\t\tif ctx.Session[\"path\"] != \"\" {\n\t\t\t\t\tleftPath = EnforceDirectory(ctx.Session[\"path\"])\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn leftPath + rightPath\n\t\t}(),\n\t\tPassword:     NewStringpFromInterface(ctx.Body[\"password\"]),\n\t\tUsers:        NewStringpFromInterface(ctx.Body[\"users\"]),\n\t\tExpire:       NewInt64pFromInterface(ctx.Body[\"expire\"]),\n\t\tUrl:          NewStringpFromInterface(ctx.Body[\"url\"]),\n\t\tCanManageOwn: NewBoolFromInterface(ctx.Body[\"can_manage_own\"]),\n\t\tCanShare:     NewBoolFromInterface(ctx.Body[\"can_share\"]),\n\t\tCanRead:      NewBoolFromInterface(ctx.Body[\"can_read\"]),\n\t\tCanWrite:     NewBoolFromInterface(ctx.Body[\"can_write\"]),\n\t\tCanUpload:    NewBoolFromInterface(ctx.Body[\"can_upload\"]),\n\t}\n\tif err := model.ShareUpsert(&s); err != nil {\n\t\tLog.Debug(\"share::upsert '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tSendSuccessResult(res, nil)\n}\n\nfunc ShareDelete(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tshare_target := mux.Vars(req)[\"share\"]\n\tif err := model.ShareDelete(share_target); err != nil {\n\t\tLog.Debug(\"share::delete '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tSendSuccessResult(res, nil)\n}\n\nfunc ShareVerifyProof(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tvar submittedProof model.Proof\n\tvar verifiedProof []model.Proof\n\tvar requiredProof []model.Proof\n\tvar remainingProof []model.Proof\n\tvar s Share\n\tvar err error\n\n\t// 1) initialise the current context\n\tshare_id := mux.Vars(req)[\"share\"]\n\ts, err = model.ShareGet(share_id)\n\tif err != nil {\n\t\tLog.Debug(\"share::verify::init '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tsubmittedProof = model.Proof{\n\t\tKey:   fmt.Sprint(ctx.Body[\"type\"]),\n\t\tValue: fmt.Sprint(ctx.Body[\"value\"]),\n\t}\n\tverifiedProof = model.ShareProofGetAlreadyVerified(req)\n\trequiredProof = model.ShareProofGetRequired(s)\n\n\t// 2) validate the current context\n\tif len(verifiedProof) > 20 || len(requiredProof) > 20 {\n\t\thttp.SetCookie(res, &http.Cookie{\n\t\t\tName:   COOKIE_NAME_PROOF,\n\t\t\tValue:  \"\",\n\t\t\tMaxAge: -1,\n\t\t\tPath:   COOKIE_PATH,\n\t\t})\n\t\tLog.Debug(\"share::verify::validate 'proof issue' len(verifiedProof)[%d] len(requiredProof)[%d]\", len(verifiedProof), len(requiredProof))\n\t\tSendErrorResult(res, ErrNotValid)\n\t\treturn\n\t}\n\tif err := s.IsValid(); err != nil {\n\t\tLog.Debug(\"share::verify::validate '%s'\", err.Error())\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\n\t// 3) process the proof sent by the user\n\tsubmittedProof, err = model.ShareProofVerifier(s, submittedProof)\n\tif err != nil {\n\t\tLog.Debug(\"share::verify::process '%s'\", err.Error())\n\t\tsubmittedProof.Error = NewString(err.Error())\n\t\tSendSuccessResult(res, submittedProof)\n\t\treturn\n\t}\n\tif submittedProof.Key == \"code\" {\n\t\tsubmittedProof.Value = \"\"\n\t\tsubmittedProof.Message = NewString(\"We've sent you a message with a verification code\")\n\t\tSendSuccessResult(res, submittedProof)\n\t\treturn\n\t}\n\n\tif submittedProof.Key != \"\" {\n\t\tsubmittedProof.Id = Hash(submittedProof.Key+\"::\"+submittedProof.Value, 20)\n\t\talreadyExist := false\n\t\tfor i := 0; i < len(verifiedProof); i++ {\n\t\t\tif verifiedProof[i].Id == submittedProof.Id {\n\t\t\t\talreadyExist = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif alreadyExist == false {\n\t\t\tverifiedProof = append(verifiedProof, submittedProof)\n\t\t}\n\t}\n\n\t// 4) Find remaining proofs: requiredProof - verifiedProof\n\tremainingProof = model.ShareProofCalculateRemainings(requiredProof, verifiedProof)\n\n\t// 5) persist proofs in client cookie\n\tcookie := http.Cookie{\n\t\tName: COOKIE_NAME_PROOF,\n\t\tValue: func(p []model.Proof) string {\n\t\t\tj, _ := json.Marshal(p)\n\t\t\tstr, _ := EncryptString(SECRET_KEY_DERIVATE_FOR_PROOF, string(j))\n\t\t\treturn str\n\t\t}(verifiedProof),\n\t\tPath:     COOKIE_PATH,\n\t\tMaxAge:   60 * 60 * 24 * 30,\n\t\tHttpOnly: true,\n\t\tSameSite: http.SameSiteNoneMode,\n\t\tSecure:   true,\n\t}\n\thttp.SetCookie(res, &cookie)\n\n\tif len(remainingProof) > 0 {\n\t\tSendSuccessResult(res, remainingProof[0])\n\t\treturn\n\t}\n\n\tSendSuccessResult(res, struct {\n\t\tId        string `json:\"id\"`\n\t\tPath      string `json:\"path\"`\n\t\tCanRead   bool   `json:\"can_read\"`\n\t\tCanWrite  bool   `json:\"can_write\"`\n\t\tCanUpload bool   `json:\"can_upload\"`\n\t}{\n\t\tId:        s.Id,\n\t\tPath:      s.Path,\n\t\tCanRead:   s.CanRead,\n\t\tCanWrite:  s.CanWrite,\n\t\tCanUpload: s.CanUpload,\n\t})\n}\n"
  },
  {
    "path": "server/ctrl/static/404.html",
    "content": "<html>\n  <head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">     <title>404</title>\n  </head>\n  <body>\n    <div>\n        <h1>404</h1>\n        <p>Page Not Found</p>\n    </div>\n    <style>\n      body { font-family: monaco, monospace; background: #010088; color: #f8ffff;\n             display: flex; align-items: center; justify-content: center;\n             text-align: center; height: 100%; margin: 0; }\n      body > div { margin-top: -50px; }\n      h1 { font-size: 150px; line-height: 150px; margin: 0; background: #f8ffff;\n           color: #010088; padding: 5px 20px; }\n      p { font-size: 25px; line-height: 25px; }\n    </style>\n  </body>\n</html>\n"
  },
  {
    "path": "server/ctrl/static/loader.html",
    "content": "{{ define \"loader-cat\" }}customElements.define(\"component-bootscreen\", class ComponentBootScreen extends HTMLElement { connectedCallback() { this.innerHTML = this.render(); this.timeout = setTimeout(function(){ const $rbw = document.querySelector(\"#rbw .w\"); $rbw.innerHTML = $rbw.innerHTML.repeat(10); const $loader = document.querySelector(\"#n-lder\"); $loader.classList.add(\"loading\"); }, 500); } disconnectedCallback() { clearTimeout(this.timeout); } render() { return `<style>html{ overflow: hidden; } body{ background: #f2f3f5; color: #375160; } body.dark-mode{ background: #232426; } .background-color{ background: #f2f3f5; } body.dark-mode .background-color{ background: #232426; } #n-lder{ max-width: 100%; overflow: hidden; } #n-lder #cat{ position: absolute; top: calc(50% + 45px); left: 0%; margin-left: -250px; margin-top: -125px; width: 100%; height: 150px; } #n-lder.loading #cat{ left: 20%; left: calc(50% + 125px); transition: left 4s ease-out; } #n-lder.loading.done #cat { left: 100%; left: calc(100% + 250px); transition: left 0.5s linear; } #n-lder #cat svg{ height: 160px; width: 250px; position: absolute; } #n-lder #cat #hide-behind{ position: absolute; top: 0; left: 55px; bottom: 0; right: -250px; } #n-lder #rbw{ position: absolute; top: calc(50% + 45px); left: 0; overflow: hidden; height: 145px; margin-top: -110px; width: 100%; } #n-lder #rbw .w{ width: 10000px; } #n-lder #rbw .rbw { z-index: -1; font-size: 16em; float: left; position: relative; } #n-lder #rbw .rbw .wv { height: 20px; width: 55px; } #n-lder #rbw .rbw .wv.wv-1 { background: #ff0000; } #n-lder #rbw .rbw .wv.wv-2 { background: #ff9900; } #n-lder #rbw .rbw .wv.wv-3 { background: #ffff00; } #n-lder #rbw .rbw .wv.wv-4 { background: #33ff00; } #n-lder #rbw .rbw .wv.wv-5 { background: #0099ff; } #n-lder #rbw .rbw .wv.wv-6 { background: #6633ff; } #n-lder #rbw .rbw{ top: 0px; animation: rbw .6s linear infinite; } #n-lder #rbw .rbw.f1{ animation-delay: 0s; } #n-lder #rbw .rbw.f2{ animation-delay: 0.1s; } #n-lder #rbw .rbw.f3{ animation-delay: 0.2s; } #n-lder #rbw .rbw.f4{ animation-delay: 0.3s; } #n-lder #rbw .rbw.f5{ animation-delay: 0.4s; } #n-lder #rbw .rbw.f6{ animation-delay: 0.5s; } @keyframes rbw { 0%{ top: 0px; } 50%{ top: 15px; } 100%{ top: 0px; } } @keyframes nyan_all { 0%{ transform: translateY(0px); } 33%{ transform: translateY(0px); } 34%{ transform: translateY(1px); } 100%{ transform: translateY(1px); } } #n-lder svg g#nyan_all{ animation: nyan_all 0.40s linear infinite; } @keyframes nyan_head { 0%{ transform: translateX(0px) translateY(0px); } 16%{ transform: translateX(0px) translateY(0px); } 17%{ transform: translateX(1px) translateY(0px); } 66%{ transform: translateX(1px) translateY(0px); } 67%{ transform: translateX(0px) translateY(0px); } 83%{ transform: translateX(0px) translateY(0px); } 84%{ transform: translateX(0px) translateY(-1px); } 100%{ transform: translateX(0px) translateY(-1px); } } #n-lder svg g#nyan_head{ animation: nyan_head 0.4s linear infinite; } @keyframes nyan_walk { 0%{ transform: translateX(0px); } 16%{ transform: translateX(0px); } 17%{ transform: translateX(1px); } 33%{ transform: translateX(1px); } 34%{ transform: translateX(2px); } 50%{ transform: translateX(2px); } 51%{ transform: translateX(1px); } 100%{ transform: translateX(0px); } } #n-lder svg g#nyan_feet{ animation: nyan_walk 0.5s linear infinite; } @keyframes nyan_tail { 0%{ transform: rotate(0); } 16%{ transform: rotate(0); } 17%{ transform: rotate(-5deg); } 33%{ transform: rotate(-5deg); } 34%{ transform: rotate(-10deg); } 49%{ transform: rotate(-10deg); } 50%{ transform: rotate(-20deg); } 66%{ transform: rotate(-20deg); } 67%{ transform: rotate(-10deg); } 83%{ transform: rotate(-10deg); } 84%{ transform: rotate(-5deg); } 99%{ transform: rotate(-5deg); } 100%{ transform: rotate(0deg); } } #n-lder svg g#nyan_tail{ animation: nyan_tail 0.5s linear infinite; transform-origin: 4px 8px; }</style><div id=\"n-lder\"><div id=\"cat\"><div id=\"hide-behind\" class=\"background-color\"></div><svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"32\" height=\"21\" preserveAspectRatio=\"xMinYMin meet\" viewBox=\"0 0 33 21\"><g id=\"nyan_all\"><g id=\"nyan_feet\"><g><path d=\"m 4,20 0,-3 1,0 0,-1 4,0 0,3 -1,0 0,1 z\" style=\"fill:#000\"></path><path d=\"m 5,19 0,-2 3,0 0,1 -1,0 0,1 z\" style=\"fill:#999\"></path><path d=\"m 10,20 0,-2 4,0 0,1 -1,0 0,1 z\" style=\"fill:#000\"></path><path d=\"m 11,18 2,0 0,1 -2,0 z\" style=\"fill:#999\"></path></g><g transform=\"matrix(-1,0,0,1,32,0)\"><path d=\"m 10,20 0,-2 4,0 0,1 -1,0 0,1 z\" style=\"fill:#000\"></path><path d=\"m 11,18 2,0 0,1 -2,0 z\" style=\"fill:#999\"></path><path d=\"m 4,20 0,-3 1,0 0,-1 4,0 0,3 -1,0 0,1 z\" style=\"fill:#000\"></path><path d=\"m 5,19 0,-2 3,0 0,1 -1,0 0,1 z\" style=\"fill:#999\"></path></g></g><g id=\"nyan_tail\"><path d=\"M 0,10 0,7 4,7 4,8 5,8 5,9 6,9 6,14 5,14 5,13 3,13 3,12 2,12 2,11 1,11 1,10 z\" style=\"fill:#000\"/><path d=\"m 1,9 0,-1 2,0 0,1 1,0 0,1 1,0 0,1 1,0 0,1 -2,0 0,-1 -1,0 0,-1 -1,0 0,-1 z\" style=\"fill:#999\"/></g><g id=\"nyan_body\"><path d=\"m 7,1 19,0 0,16 -19,0 z\" style=\"fill:#fc9\"/><path d=\"m 8,14 0,-10 1,0 0,-1 1,0 0,-1 13,0 0,1 1,0 0,1 1,0 0,10 -1,0 0,1 -1,0 0,1 -13,0 0,-1 -1,0 0,-1 z\" style=\"fill:#f9f\"/><path d=\"m 22,5 1,0 0,1 -1,0 z m -4,-2 1,0 0,1 -1,0 z m -3,0 1,0 0,1 -1,0 z m -1,4 1,0 0,1 -1,0 z m 1,3 1,0 0,1 -1,0 z m -2,3 1,0 0,1 -1,0 z m -2,-4 1,0 0,1 -1,0 z m -2,2 1,0 0,1 -1,0 z m 1,3 1,0 0,1 -1,0 z m 0,-10 1,0 0,1 -1,0 z\" style=\"fill:#f39\"/><path d=\"m 8,17 17,0 0,1 -17,0 z m 0,-17 17,0 0,1 -17,0 z m 18,16 0,-14 1,0 0,14 z m -20,0 0,-14 1,0 0,14 z m 1,0 1,0 0,1 -1,0 z m 0,-15 1,0 0,1 -1,0 z m 18,0 1,0 0,1 -1,0 z m 0,15 1,0 0,1 -1,0 z\" style=\"fill:#000\"/></g><g id=\"nyan_head\"><path d=\"m 17,15 0,-5 1,0 0,-4 2,0 0,1 1,0 0,1 1,0 0,1 4,0 0,-1 1,0 0,-1 1,0 0,-1 2,0 0,4 1,0 0,5 -1,0 0,1 -1,0 0,1 -10,0 0,-1 -1,0 0,-1 z\" style=\"fill:#999;fill-opacity:1;stroke:none\"/><path d=\"m 29,16 1,0 0,1 -1,0 z m 1,-1 1,0 0,1 -1,0 z m 1,-5 1,0 0,5 -1,0 z m -1,-4 1,0 0,4 -1,0 z m -2,-1 2,0 0,1 -2,0 z m -6,3 4,0 0,1 -4,0 z m -4,-3 2,0 0,1 -2,0 z m -1,1 1,0 0,4 -1,0 z m -1,4 1,0 0,5 -1,0 z m 11,-4 1,0 0,1 -1,0 z m -1,1 1,0 0,1 -1,0 z m -5,0 1,0 0,1 -1,0 z m -1,-1 1,0 0,1 -1,0 z m -1,11 10,0 0,1 -10,0 z m -1,-1 1,0 0,1 -1,0 z m -1,-1 1,0 0,1 -1,0 z\" style=\"fill:#000;fill-opacity:1;stroke:none\"/><path d=\"m 18,13 2,0 0,2 -2,0 z\" style=\"fill:#f99;fill-opacity:1;stroke:none\"/><path d=\"m 29,13 2,0 0,2 -2,0 z\" style=\"fill:#f99;fill-opacity:1;stroke:none\"/><path d=\"m 21,16 0,-2 1,0 0,1 2,0 0,-1 1,0 0,1 2,0 0,-1 1,0 0,2 z\" style=\"fill:#000;fill-opacity:1;stroke:none\"/><path d=\"m 25,12 1,0 0,1 -1,0 z\" style=\"fill:#000;fill-opacity:1;stroke:none\"/><g><path d=\"m 27,13 0,-1 1,0 0,-1 1,0 0,2 z\" style=\"fill:#000;fill-opacity:1;stroke:none\"/><path d=\"m 27,11 1,0 0,1 -1,0 z\" style=\"fill:#fff;fill-opacity:1;stroke:none\"/><path d=\"m 20,13 0,-1 1,0 0,-1 1,0 0,2 z\" style=\"fill:#000;fill-opacity:1;stroke:none\"/><path d=\"m 20,11 1,0 0,1 -1,0 z\" style=\"fill:#fff;fill-opacity:1;stroke:none\"/></g></g></g></svg></div><div id=\"rbw\"><div class=\"w\"><div class=\"rbw f1\"><div class=\"wv wv-1\"></div><div class=\"wv wv-2\"></div><div class=\"wv wv-3\"></div><div class=\"wv wv-4\"></div><div class=\"wv wv-5\"></div><div class=\"wv wv-6\"></div></div><div class=\"rbw f2\"><div class=\"wv wv-1\"></div><div class=\"wv wv-2\"></div><div class=\"wv wv-3\"></div><div class=\"wv wv-4\"></div><div class=\"wv wv-5\"></div><div class=\"wv wv-6\"></div></div><div class=\"rbw f3\"><div class=\"wv wv-1\"></div><div class=\"wv wv-2\"></div><div class=\"wv wv-3\"></div><div class=\"wv wv-4\"></div><div class=\"wv wv-5\"></div><div class=\"wv wv-6\"></div></div><div class=\"rbw f4\"><div class=\"wv wv-1\"></div><div class=\"wv wv-2\"></div><div class=\"wv wv-3\"></div><div class=\"wv wv-4\"></div><div class=\"wv wv-5\"></div><div class=\"wv wv-6\"></div></div><div class=\"rbw f5\"><div class=\"wv wv-1\"></div><div class=\"wv wv-2\"></div><div class=\"wv wv-3\"></div><div class=\"wv wv-4\"></div><div class=\"wv wv-5\"></div><div class=\"wv wv-6\"></div></div><div class=\"rbw f6\"><div class=\"wv wv-1\"></div><div class=\"wv wv-2\"></div><div class=\"wv wv-3\"></div><div class=\"wv wv-4\"></div><div class=\"wv wv-5\"></div><div class=\"wv wv-6\"></div></div></div></div></div>`; }});{{ end }}\n{{ define \"loader-basic\" }}customElements.define(\"component-bootscreen\", class ComponentBootScreen extends HTMLElement { connectedCallback() { this.innerHTML = `<div class=\"component_loader\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 200\" style=\"width: 30px;\"><circle fill=\"#57595A\" stroke=\"#57595A\" stroke-width=\"15\" r=\"15\" cx=\"40\" cy=\"100\"><animate attributeName=\"opacity\" calcMode=\"spline\" dur=\"2\" values=\"1;0;1;\" keySplines=\".5 0 .5 1;.5 0 .5 1\" repeatCount=\"indefinite\" begin=\"-.4\"></animate></circle><circle fill=\"#57595A\" stroke=\"#57595A\" stroke-width=\"15\" r=\"15\" cx=\"100\" cy=\"100\"><animate attributeName=\"opacity\" calcMode=\"spline\" dur=\"2\" values=\"1;0;1;\" keySplines=\".5 0 .5 1;.5 0 .5 1\" repeatCount=\"indefinite\" begin=\"-.2\"></animate></circle><circle fill=\"#57595A\" stroke=\"#57595A\" stroke-width=\"15\" r=\"15\" cx=\"160\" cy=\"100\"><animate attributeName=\"opacity\" calcMode=\"spline\" dur=\"2\" values=\"1;0;1;\" keySplines=\".5 0 .5 1;.5 0 .5 1\" repeatCount=\"indefinite\" begin=\"0\"></animate></circle></svg><style>html, body, #app { height: 100%; margin: 0; } #app { display: flex; } component-bootscreen { margin: auto; }</style></div>`;}});{{ end }}\n"
  },
  {
    "path": "server/ctrl/static.go",
    "content": "package ctrl\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t_ \"embed\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"text/template\"\n\n\t. \"github.com/mickael-kerjean/filestash\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/pkg/compress\"\n\n\t\"github.com/bluekeyes/go-gitdiff/gitdiff\"\n)\n\nvar (\n\tWWWDir fs.FS\n\n\t//go:embed static/404.html\n\tHtmlPage404 []byte\n\n\t//go:embed static/loader.html\n\tTmplLoader []byte\n)\n\nfunc init() {\n\tWWWDir = os.DirFS(GetAbsolutePath(\"../\"))\n}\n\nfunc ServeBackofficeHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\turl := req.URL.Path\n\tif filepath.Ext(filepath.Base(url)) != \"\" {\n\t\treq.URL.Path = strings.TrimPrefix(TrimBase(req.URL.Path), \"/admin/\")\n\t\tServeFile(\"/\")(ctx, res, req)\n\t\treturn\n\t}\n\tif url != URL_SETUP && Config.Get(\"auth.admin\").String() == \"\" {\n\t\thttp.Redirect(res, req, URL_SETUP, http.StatusTemporaryRedirect)\n\t\treturn\n\t}\n\tServeIndex(\"index.backoffice.html\")(ctx, res, req)\n\treturn\n}\n\nfunc ServeFrontofficeHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tua := req.Header.Get(\"User-Agent\")\n\tif strings.Contains(ua, \"MSIE \") || strings.Contains(ua, \"Trident/\") || strings.Contains(ua, \"Edge/\") {\n\t\t// Microsoft is behaving on many occasion differently than Firefox / Chrome.\n\t\t// I have neither the time / motivation for it to work properly\n\t\tres.WriteHeader(http.StatusBadRequest)\n\t\tres.Write([]byte(Page(`\n\t\t\t<h1>Internet explorer is not supported</h1>\n\t\t\t<p>\n\t\t\t\tWe don't support IE / Edge at this time\n\t\t\t\t<br>\n\t\t\t\tPlease use either Chromium, Firefox or Chrome\n\t\t\t</p>\n\t\t`)))\n\t\treturn\n\t}\n\turl := TrimBase(req.URL.Path)\n\tif url != \"/\" && strings.HasPrefix(url, \"/s/\") == false &&\n\t\tstrings.HasPrefix(url, \"/view/\") == false && strings.HasPrefix(url, \"/files/\") == false &&\n\t\turl != \"/login\" && url != \"/logout\" && strings.HasPrefix(url, \"/tags\") == false {\n\t\tNotFoundHandler(ctx, res, req)\n\t\treturn\n\t}\n\tif url != URL_SETUP && Config.Get(\"auth.admin\").String() == \"\" {\n\t\thttp.Redirect(res, req, URL_SETUP, http.StatusTemporaryRedirect)\n\t\treturn\n\t}\n\tServeIndex(\"index.frontoffice.html\")(ctx, res, req)\n}\n\nfunc ServeFavicon(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif binary, mime := Hooks.Get.Favicon(); len(binary) > 0 {\n\t\tres.Header().Set(\"Content-Type\", mime)\n\t\tres.Write(binary)\n\t\treturn\n\t}\n\tr, _ := http.NewRequest(http.MethodGet, \"/favicon.svg\", nil)\n\tServeFile(\"/assets/logo/\")(ctx, res, r)\n}\n\nfunc NotFoundHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif strings.Contains(req.Header.Get(\"accept\"), \"text/html\") {\n\t\tres.WriteHeader(http.StatusNotFound)\n\t\tres.Write(HtmlPage404)\n\t\treturn\n\t}\n\tSendErrorResult(res, ErrNotFound)\n}\n\nfunc ManifestHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tres.WriteHeader(http.StatusOK)\n\tres.Write([]byte(fmt.Sprintf(`{\n    \"name\": \"%s\",\n    \"short_name\": \"%s\",\n    \"icons\": [\n        {\n            \"src\": \"/assets/logo/android-chrome-192x192.png\",\n            \"type\": \"image/png\",\n            \"sizes\": \"192x192\"\n        },\n        {\n            \"src\": \"/assets/logo/android-chrome-512x512.png\",\n             \"type\": \"image/png\",\n             \"sizes\": \"512x512\"\n        }\n    ],\n    \"theme_color\": \"#f2f3f5\",\n    \"background_color\": \"#f2f3f5\",\n    \"orientation\": \"any\",\n    \"display\": \"standalone\",\n    \"start_url\": \"/\"\n}`, Config.Get(\"general.name\"), Config.Get(\"general.name\"))))\n}\n\nfunc RobotsHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tres.Write([]byte(\"\"))\n}\n\nfunc CustomCssHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tres.Header().Set(\"Content-Type\", \"text/css\")\n\tio.WriteString(res, Hooks.Get.CSS())\n\tio.WriteString(res, Config.Get(\"general.custom_css\").String())\n}\n\nfunc ServeFile(chroot string) func(*App, http.ResponseWriter, *http.Request) {\n\treturn func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tfilePath := JoinPath(\n\t\t\tchroot,\n\t\t\tstrings.Replace(\n\t\t\t\tTrimBase(req.URL.Path),\n\t\t\t\t\"assets/\"+BUILD_REF+\"/\",\n\t\t\t\t\"assets/\",\n\t\t\t\t1,\n\t\t\t),\n\t\t)\n\t\thead := res.Header()\n\t\thead.Set(\"Cache-Control\", \"no-cache\")\n\t\tif f := applyPatch(filePath); f != nil {\n\t\t\thead.Set(\"Content-Type\", GetMimeType(filepath.Ext(filePath)))\n\t\t\tres.WriteHeader(http.StatusOK)\n\t\t\tres.Write(f.Bytes())\n\t\t\treturn\n\t\t}\n\n\t\t// case: main path\n\t\tacceptEncoding := req.Header.Get(\"Accept-Encoding\")\n\t\tstaticConfig := []struct {\n\t\t\tContentType string\n\t\t\tFileExt     string\n\t\t}{\n\t\t\t{\"br\", \".br\"},\n\t\t\t{\"gzip\", \".gz\"},\n\t\t\t{\"\", \"\"},\n\t\t}\n\t\tfor _, cfg := range staticConfig {\n\t\t\tif strings.Contains(acceptEncoding, cfg.ContentType) == false {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcurPath := filePath + cfg.FileExt\n\t\t\tfile, err := WWWPublic.Open(curPath)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t} else if stat, err := file.Stat(); err == nil {\n\t\t\t\tetag := QuickHash(fmt.Sprintf(\n\t\t\t\t\t\"%s %d %d %s\",\n\t\t\t\t\tcurPath, stat.Size(), stat.Mode(), stat.ModTime()), 10,\n\t\t\t\t)\n\t\t\t\tif etag == req.Header.Get(\"If-None-Match\") {\n\t\t\t\t\tres.WriteHeader(http.StatusNotModified)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\thead.Set(\"Etag\", etag)\n\t\t\t}\n\t\t\thead.Set(\"Content-Type\", GetMimeType(filepath.Ext(filePath)))\n\t\t\tif cfg.ContentType != \"\" {\n\t\t\t\thead.Set(\"Content-Encoding\", cfg.ContentType)\n\t\t\t}\n\t\t\tres.WriteHeader(http.StatusOK)\n\t\t\tio.Copy(res, file)\n\t\t\tfile.Close()\n\t\t\treturn\n\t\t}\n\t\thttp.NotFound(res, req)\n\t}\n}\n\nfunc ServeIndex(indexPath string) func(*App, http.ResponseWriter, *http.Request) {\n\t// STEP1: pull the data from the embed\n\tfile, err := WWWPublic.Open(indexPath)\n\tif err != nil {\n\t\treturn func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\t\thttp.NotFound(res, req)\n\t\t}\n\t}\n\tdefer file.Close()\n\n\t// STEP2: compile the template\n\tb, err := io.ReadAll(file)\n\tif err != nil {\n\t\treturn func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\t\tSendErrorResult(res, err)\n\t\t}\n\t}\n\ttmpl := template.Must(template.New(indexPath).Funcs(template.FuncMap{\n\t\t\"load_asset\": func(path string) (string, error) {\n\t\t\tfile, err := WWWPublic.Open(path)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\tout := \"/* LOAD \" + path + \" */ \"\n\t\t\tf, err := io.ReadAll(file)\n\t\t\tfile.Close()\n\t\t\tout += regexp.MustCompile(`\\s+`).ReplaceAllString(\n\t\t\t\tstrings.ReplaceAll(string(f), \"\\n\", \"\"),\n\t\t\t\t\" \",\n\t\t\t)\n\t\t\treturn out, err\n\t\t},\n\t}).Parse(string(b)))\n\ttmpl = template.Must(tmpl.Parse(string(TmplLoader)))\n\n\treturn func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\thead := res.Header()\n\t\tsign := signature()\n\t\tbase := WithBase(\"/\")\n\t\ttemplateData := map[string]any{\n\t\t\t\"appname\": APPNAME,\n\t\t\t\"base\":    base,\n\t\t\t\"version\": BUILD_REF,\n\t\t\t\"license\": LICENSE,\n\t\t\t\"hash\":    sign,\n\t\t\t\"favicon\": favicon(),\n\t\t\t\"bundle\": func() []string {\n\t\t\t\tb := make([]string, len(preload))\n\t\t\t\tv := BUILD_REF[0:7] + \"::\" + sign\n\t\t\t\tfor i := 0; i < len(preload); i++ {\n\t\t\t\t\tb[i] = fmt.Sprintf(\"./assets/bundle.js?version=%s&chunk=%d\", v, i+1)\n\t\t\t\t}\n\t\t\t\treturn b\n\t\t\t}(),\n\t\t}\n\t\tcalculatedEtag := QuickHash(base+BUILD_REF+LICENSE+sign, 10)\n\t\thead.Set(\"ETag\", calculatedEtag)\n\t\tif etag := req.Header.Get(\"If-None-Match\"); etag == calculatedEtag {\n\t\t\tres.WriteHeader(http.StatusNotModified)\n\t\t\treturn\n\t\t}\n\t\tvar out io.Writer = res\n\t\tif strings.Contains(req.Header.Get(\"Accept-Encoding\"), \"gzip\") {\n\t\t\thead.Set(\"Content-Encoding\", \"gzip\")\n\t\t\tgz := gzip.NewWriter(res)\n\t\t\tdefer gz.Close()\n\t\t\tout = gz\n\t\t}\n\t\thead.Set(\"Content-Type\", \"text/html\")\n\t\thead.Set(\"Cache-Control\", \"no-cache\")\n\t\ttmpl.Execute(out, templateData)\n\t}\n}\n\nvar preload = [][]string{\n\t{\n\t\t\"/assets/\" + BUILD_REF + \"/boot/ctrl_boot_frontoffice.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/boot/router_frontoffice.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/boot/common.js\",\n\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_input.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_textarea.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_inputgroup.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_checkbox.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_formbuilder.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_button.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_icon.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_dropdown.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_container.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_box.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_darkmode.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_skeleton.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_utils.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem_alert.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/css/designsystem.css\",\n\n\t\t\"/assets/\" + BUILD_REF + \"/components/decorator_shell_filemanager.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/loader.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/modal.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/modal.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/notification.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/notification.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/sidebar.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/sidebar_files.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/sidebar_tags.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/sidebar.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/dropdown.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/decorator_shell_filemanager.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/form.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/icon.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/breadcrumb.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/breadcrumb.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/components/skeleton.js\",\n\n\t\t\"/assets/\" + BUILD_REF + \"/helpers/loader.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/helpers/log.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/helpers/sdk.js\",\n\n\t\t\"/assets/\" + BUILD_REF + \"/lib/rx.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/ajax.js\",\n\t},\n\t{\n\t\t\"/assets/\" + BUILD_REF + \"/lib/vendor/rxjs/rxjs.min.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/vendor/rxjs/rxjs-ajax.min.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/vendor/rxjs/rxjs-shared.min.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/store.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/form.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/path.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/random.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/settings.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/animate.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/assert.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/dom.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/skeleton/index.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/skeleton/router.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/skeleton/lifecycle.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/lib/error.js\",\n\n\t\t\"/assets/\" + BUILD_REF + \"/locales/index.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/model/config.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/model/chromecast.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/model/session.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/model/plugin.js\",\n\n\t\t\"/assets/\" + BUILD_REF + \"/pages/ctrl_logout.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/ctrl_error.js\",\n\t},\n\t{\n\t\t\"/assets/\" + BUILD_REF + \"/pages/ctrl_homepage.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/ctrl_connectpage.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/ctrl_connectpage.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/connectpage/ctrl_form.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/connectpage/ctrl_form.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/connectpage/ctrl_forkme.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/connectpage/ctrl_poweredby.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/connectpage/model_backend.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/connectpage/model_config.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/connectpage/ctrl_form_state.js\",\n\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/thing.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/thing.css\",\n\t},\n\t{\n\t\t\"/assets/\" + BUILD_REF + \"/pages/ctrl_filespage.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/ctrl_filespage.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/model_acl.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/cache.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/ctrl_filesystem.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/ctrl_filesystem.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/ctrl_upload.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/ctrl_upload.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/ctrl_newitem.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/ctrl_newitem.css\",\n\t},\n\t{\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/ctrl_submenu.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/ctrl_submenu.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/state_config.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/helper.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/model_files.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/model_tag.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/model_virtual_layer.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/modal.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/modal_tag.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/modal_tag.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/modal_share.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/modal_share.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/modal_rename.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/modal_delete.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/state_selection.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/filespage/state_newthing.js\",\n\n\t\t// \"/assets/\" + BUILD_REF + \"/pages/ctrl_viewerpage.js\", // TODO: dynamic imports\n\t\t\"/assets/\" + BUILD_REF + \"/pages/ctrl_viewerpage.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/viewerpage/mimetype.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/viewerpage/model_files.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/viewerpage/common.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/viewerpage/application_downloader.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/viewerpage/application_downloader.css\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/viewerpage/component_menubar.js\",\n\t\t\"/assets/\" + BUILD_REF + \"/pages/viewerpage/component_menubar.css\",\n\t},\n}\n\nfunc ServeBundle() func(*App, http.ResponseWriter, *http.Request) {\n\tisDebug := os.Getenv(\"DEBUG\") == \"true\"\n\tbuildChunks := func(quality int) (chunks [][]byte, chunksBr [][]byte, chunksGzip [][]byte, etags []string) {\n\t\tnumChunks := len(preload)\n\t\tchunks = make([][]byte, numChunks+1)\n\t\tchunksBr = make([][]byte, numChunks+1)\n\t\tchunksGzip = make([][]byte, numChunks+1)\n\t\tetags = make([]string, numChunks+1)\n\t\tvar fullBuf bytes.Buffer\n\t\tfor i := 0; i < numChunks; i++ {\n\t\t\tvar chunkBuf bytes.Buffer\n\t\t\tfor _, path := range preload[i] {\n\t\t\t\tcurPath := \"/assets/\" + strings.TrimPrefix(path, \"/assets/\"+BUILD_REF+\"/\")\n\t\t\t\tf := applyPatch(curPath)\n\t\t\t\tif f == nil {\n\t\t\t\t\tfile, err := WWWPublic.Open(curPath)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tLog.Warning(\"static::bundler failed to find file %s\", err.Error())\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tf = new(bytes.Buffer)\n\t\t\t\t\tif _, err := io.Copy(f, file); err != nil {\n\t\t\t\t\t\tLog.Warning(\"static::bundler msg=copy_error err=%s\", err.Error())\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tfile.Close()\n\t\t\t\t}\n\t\t\t\tcode, err := json.Marshal(f.String())\n\t\t\t\tif err != nil {\n\t\t\t\t\tLog.Warning(\"static::bundle msg=marshal_failed path=%s err=%s\", path, err.Error())\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tline := fmt.Sprintf(\"bundler.register(%q, %s);\\n\", WithBase(path), code)\n\t\t\t\tchunkBuf.WriteString(line)\n\t\t\t\tfullBuf.WriteString(line)\n\t\t\t}\n\t\t\tchunks[i+1] = chunkBuf.Bytes()\n\t\t\tchunksGzip[i+1] = compress.Gzip(chunks[i+1], quality)\n\t\t\tchunksBr[i+1] = compress.Br(chunks[i+1], quality)\n\t\t\tetags[i+1] = QuickHash(string(chunks[i+1]), 10)\n\t\t}\n\t\tchunks[0] = fullBuf.Bytes()\n\t\tchunksGzip[0] = compress.Gzip(chunks[0], quality)\n\t\tchunksBr[0] = compress.Br(chunks[0], quality)\n\t\tetags[0] = QuickHash(string(chunks[0]), 10)\n\t\treturn chunks, chunksBr, chunksGzip, etags\n\t}\n\n\tquality := 11\n\tif isDebug {\n\t\tquality = 8\n\t}\n\tchunks, chunksBr, chunksGzip, etags := buildChunks(quality)\n\n\treturn func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tif isDebug {\n\t\t\tchunks, chunksBr, chunksGzip, etags = buildChunks(quality)\n\t\t}\n\t\tchunkIndex := 0\n\t\tif parsed, err := strconv.Atoi(req.URL.Query().Get(\"chunk\")); err == nil {\n\t\t\tchunkIndex = parsed\n\t\t}\n\t\tif chunkIndex >= len(chunks) {\n\t\t\thttp.NotFound(res, req)\n\t\t\treturn\n\t\t}\n\t\thead := res.Header()\n\t\thead.Set(\"Content-Type\", \"application/javascript\")\n\t\thead.Set(\"Cache-Control\", \"no-cache\")\n\t\thead.Set(\"Etag\", etags[chunkIndex])\n\t\tif req.Header.Get(\"If-None-Match\") == etags[chunkIndex] && etags[chunkIndex] != \"\" {\n\t\t\tres.WriteHeader(http.StatusNotModified)\n\t\t\treturn\n\t\t} else if strings.Contains(req.Header.Get(\"Accept-Encoding\"), \"br\") && len(chunksBr[chunkIndex]) > 0 {\n\t\t\thead.Set(\"Content-Encoding\", \"br\")\n\t\t\tres.Write(chunksBr[chunkIndex])\n\t\t\treturn\n\t\t} else if strings.Contains(req.Header.Get(\"Accept-Encoding\"), \"gzip\") && len(chunksGzip[chunkIndex]) > 0 {\n\t\t\thead.Set(\"Content-Encoding\", \"gzip\")\n\t\t\tres.Write(chunksGzip[chunkIndex])\n\t\t\treturn\n\t\t}\n\t\tres.Write(chunks[chunkIndex])\n\t}\n}\n\nfunc applyPatch(filePath string) (file *bytes.Buffer) {\n\tvar (\n\t\toutputBuffer bytes.Buffer\n\t\twasPatched   bool\n\t)\n\tfor i, patch := range Hooks.Get.StaticPatch() {\n\t\tif i == 0 {\n\t\t\torigFile, err := WWWPublic.Open(filePath)\n\t\t\tif err != nil {\n\t\t\t\tLog.Debug(\"ctrl::static cannot open public file - %+v\", err.Error())\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\t_, err = outputBuffer.ReadFrom(origFile)\n\t\t\torigFile.Close()\n\t\t\tif err != nil {\n\t\t\t\tLog.Debug(\"ctrl::static cannot read from origFile - %s\", err.Error())\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tpatchFiles, _, err := gitdiff.Parse(NewReadCloserFromBytes(patch))\n\t\tif err != nil {\n\t\t\tLog.Debug(\"ctrl::static cannot parse patch file - %s\", err.Error())\n\t\t\treturn nil\n\t\t}\n\t\tfor i := 0; i < len(patchFiles); i++ {\n\t\t\tif patchFiles[i].NewName != patchFiles[i].OldName {\n\t\t\t\tcontinue\n\t\t\t} else if filePath != strings.TrimPrefix(patchFiles[i].NewName, \"public\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar patched bytes.Buffer\n\t\t\tif err := gitdiff.Apply(\n\t\t\t\t&patched,\n\t\t\t\tbytes.NewReader(outputBuffer.Bytes()),\n\t\t\t\tpatchFiles[i],\n\t\t\t); err != nil {\n\t\t\t\tLog.Debug(\"ctrl::static err=cannot_apply_patch path=%s err=%s\", patchFiles[i].NewName, err.Error())\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\toutputBuffer = patched\n\t\t\twasPatched = true\n\t\t}\n\t}\n\tif wasPatched {\n\t\treturn &outputBuffer\n\t}\n\treturn nil\n}\n\nfunc signature() string {\n\ttext := BUILD_REF\n\tpatches := Hooks.Get.StaticPatch()\n\tfor i := 0; i < len(patches); i++ {\n\t\ttext += string(patches[i])\n\t}\n\tentries, _ := os.ReadDir(GetAbsolutePath(PLUGIN_PATH))\n\tfor _, e := range entries {\n\t\tstat, _ := e.Info()\n\t\ttext += fmt.Sprintf(\"[%s][%d][%s]\", stat.Name(), stat.Size(), stat.ModTime().String())\n\t}\n\treturn strings.ToLower(QuickHash(text, 3))\n}\n\nfunc favicon() string {\n\tf, mime := Hooks.Get.Favicon()\n\tif len(f) == 0 {\n\t\tfile, err := WWWPublic.Open(\"/assets/logo/favicon.svg\")\n\t\tmime = \"image/svg+xml\"\n\t\tif err != nil {\n\t\t\treturn \"favicon.ico\"\n\t\t}\n\t\tf, err = io.ReadAll(file)\n\t\tif err != nil {\n\t\t\treturn \"favicon.ico\"\n\t\t}\n\t}\n\treturn \"data:\" + mime + \";base64,\" + base64.StdEncoding.EncodeToString(f)\n}\n"
  },
  {
    "path": "server/ctrl/tmpl.go",
    "content": "package ctrl\n\nimport (\n\t\"bytes\"\n\t\"crypto/rsa\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\t\"text/template\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\nfunc TmplExec(params string, input map[string]string) (string, error) {\n\tif params == \"\" {\n\t\treturn \"\", nil\n\t}\n\ttmpl, err := template.\n\t\tNew(\"ctrl::session::auth_middleware\").\n\t\tFuncs(tmplFuncs).\n\t\tParse(params)\n\tif err != nil {\n\t\tLog.Debug(\"tmpl::execute action=parse err=%s\", err.Error())\n\t\treturn params, err\n\t}\n\tvar b bytes.Buffer\n\tif err = tmpl.Execute(&b, input); err != nil {\n\t\tLog.Debug(\"tmpl::execute action=execute err%s\", err.Error())\n\t\treturn params, err\n\t}\n\treturn b.String(), nil\n}\n\nfunc TmplParams(data map[string]string) map[string]string {\n\tout := map[string]string{}\n\tfor key, value := range data {\n\t\tout[key] = value\n\t}\n\tout[\"machine_id\"] = GenerateMachineID()\n\tfor _, value := range os.Environ() {\n\t\tpair := strings.SplitN(value, \"=\", 2)\n\t\tif len(pair) == 2 {\n\t\t\tout[fmt.Sprintf(\"ENV_%s\", pair[0])] = pair[1]\n\t\t}\n\t}\n\treturn out\n}\n\nvar tmplFuncs = template.FuncMap{\n\t\"split\": func(s, sep string) []string {\n\t\treturn strings.Split(sep, s)\n\t},\n\t\"get\": func(i int, arr any) (string, error) {\n\t\tswitch v := arr.(type) {\n\t\tcase string:\n\t\t\tsplits := strings.Split(v, \",\")\n\t\t\tif i < len(splits) && i >= 0 {\n\t\t\t\treturn strings.TrimSpace(splits[i]), nil\n\t\t\t}\n\t\t\treturn \"\", nil\n\t\tcase []string:\n\t\t\tif i < len(v) && i >= 0 {\n\t\t\t\treturn v[i], nil\n\t\t\t}\n\t\t\treturn \"\", nil\n\t\tdefault:\n\t\t\treturn \"\", ErrNotImplemented\n\t\t}\n\t},\n\t\"contains\": func(match string, opts ...any) (bool, error) {\n\t\texact := true\n\t\tvar input any\n\t\tif len(opts) == 0 {\n\t\t\treturn false, ErrNotValid\n\t\t} else if len(opts) == 1 {\n\t\t\tinput = opts[0]\n\t\t} else if len(opts) == 2 {\n\t\t\texact = opts[0].(bool)\n\t\t\tinput = opts[1]\n\t\t}\n\t\tswitch in := input.(type) {\n\t\tcase string:\n\t\t\tsplits := strings.Split(in, \",\")\n\t\t\tfor _, split := range splits {\n\t\t\t\tsplit = strings.TrimSpace(split)\n\t\t\t\tif exact && split == match {\n\t\t\t\t\treturn true, nil\n\t\t\t\t} else if !exact && strings.Contains(split, match) {\n\t\t\t\t\treturn true, nil\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false, nil\n\t\tcase []string:\n\t\t\tfor _, split := range in {\n\t\t\t\tsplit = strings.TrimSpace(split)\n\t\t\t\tif exact && split == match {\n\t\t\t\t\treturn true, nil\n\t\t\t\t} else if !exact && strings.Contains(split, match) {\n\t\t\t\t\treturn true, nil\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false, nil\n\t\tdefault:\n\t\t\treturn false, ErrNotImplemented\n\t\t}\n\t},\n\t\"filter\": func(arg string, stdin string) (string, error) {\n\t\tout := []string{}\n\t\tr, regErr := regexp.Compile(arg)\n\t\tif regErr != nil {\n\t\t\treturn \"\", regErr\n\t\t}\n\t\tfor _, chunk := range strings.Split(stdin, \",\") {\n\t\t\tc := strings.TrimSpace(chunk)\n\t\t\tif r.Match([]byte(c)) {\n\t\t\t\tout = append(out, c)\n\t\t\t}\n\t\t}\n\t\treturn strings.Join(out, \", \"), nil\n\t},\n\t\"replace\": func(arguments ...string) (string, error) {\n\t\tvar (\n\t\t\targ     string\n\t\t\tstdin   string\n\t\t\treplace string\n\t\t)\n\t\tif len(arguments) == 2 {\n\t\t\targ = arguments[0]\n\t\t\tstdin = arguments[1]\n\t\t} else if len(arguments) == 3 {\n\t\t\targ = arguments[0]\n\t\t\treplace = arguments[1]\n\t\t\tstdin = arguments[2]\n\t\t} else {\n\t\t\treturn \"\", ErrNotImplemented\n\t\t}\n\n\t\tchunks := strings.Split(stdin, \",\")\n\t\tr, regErr := regexp.Compile(arg)\n\t\tif regErr != nil {\n\t\t\treturn \"\", regErr\n\t\t}\n\t\tfor i := range chunks {\n\t\t\tc := strings.TrimSpace(chunks[i])\n\t\t\tchunks[i] = r.ReplaceAllString(c, replace)\n\t\t}\n\t\treturn strings.Join(chunks, \", \"), nil\n\t},\n\t\"debug\": func(data string) (string, error) {\n\t\tLog.Debug(\"ctrl/tmpl data=%s\", data)\n\t\treturn data, nil\n\t},\n\t\"decryptGCM\": func(key string, str string) (string, error) {\n\t\tt, err := base64.StdEncoding.DecodeString(str)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\td, err := DecryptAESGCM([]byte(key), t)\n\t\treturn string(d), err\n\t},\n\t\"encryptGCM\": func(key string, str string) (string, error) {\n\t\tdata, err := EncryptAESGCM([]byte(key), []byte(str))\n\t\treturn base64.StdEncoding.EncodeToString(data), err\n\t},\n\t\"jwt\": func(args ...string) (string, error) {\n\t\tif len(args) == 0 {\n\t\t\treturn \"\", ErrNotValid\n\t\t}\n\t\tvar stdin = args[len(args)-1]\n\t\tvar token *jwt.Token\n\t\tvar err error\n\t\tclaims := jwt.MapClaims{}\n\t\tif len(args) == 1 {\n\t\t\ttoken, _, err = jwt.NewParser(jwt.WithPaddingAllowed()).ParseUnverified(stdin, claims)\n\t\t\ttoken.Valid = true\n\t\t} else if len(args) == 2 {\n\t\t\ttoken, err = jwt.NewParser(jwt.WithPaddingAllowed()).ParseWithClaims(stdin, claims, func(token *jwt.Token) (interface{}, error) {\n\t\t\t\tif _, ok := token.Method.(*jwt.SigningMethodHMAC); ok {\n\t\t\t\t\treturn []byte(args[0]), nil\n\t\t\t\t} else if _, ok := token.Method.(*jwt.SigningMethodRSA); ok {\n\t\t\t\t\tmodBytes, err := base64.RawURLEncoding.DecodeString(args[0])\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn \"\", err\n\t\t\t\t\t}\n\t\t\t\t\treturn &rsa.PublicKey{\n\t\t\t\t\t\tN: new(big.Int).SetBytes(modBytes),\n\t\t\t\t\t\tE: 65537,\n\t\t\t\t\t}, nil\n\t\t\t\t}\n\t\t\t\tLog.Debug(\"ctrl::tmpl invalid jwt signing method: %v\", token.Header[\"alg\"])\n\t\t\t\treturn nil, ErrNotImplemented\n\t\t\t})\n\t\t}\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t} else if !token.Valid {\n\t\t\treturn \"\", ErrNotValid\n\t\t}\n\t\tb, err := json.Marshal(claims)\n\t\treturn string(b), err\n\t},\n\t\"jq\": func(args ...string) (out string, err error) {\n\t\tif len(args) == 0 || len(args) > 2 {\n\t\t\treturn \"\", ErrNotValid\n\t\t}\n\t\tstdin := args[len(args)-1]\n\t\tdata := map[string]any{}\n\t\tif err = json.Unmarshal([]byte(stdin), &data); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif len(args) == 1 {\n\t\t\treturn stdin, nil\n\t\t}\n\t\tfilters := strings.Split(args[0], \".\")\n\t\tfor i := 0; i < len(filters)-1; i++ {\n\t\t\tif filters[i] == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif obj, ok := data[filters[i]].(map[string]any); ok {\n\t\t\t\tdata = obj\n\t\t\t} else {\n\t\t\t\treturn \"\", nil\n\t\t\t}\n\t\t}\n\t\tswitch val := data[filters[len(filters)-1]].(type) {\n\t\tcase []any:\n\t\t\tvalStr := make([]string, len(val))\n\t\t\tfor i := range val {\n\t\t\t\tvalStr[i] = fmt.Sprintf(\"%v\", val[i])\n\t\t\t}\n\t\t\treturn strings.Join(valStr, \", \"), nil\n\t\tdefault:\n\t\t\treturn fmt.Sprintf(\"%v\", val), nil\n\t\t}\n\t},\n\t\"toLower\": func(data string) (string, error) {\n\t\treturn strings.ToLower(data), nil\n\t},\n\t\"toUpper\": func(data string) (string, error) {\n\t\treturn strings.ToUpper(data), nil\n\t},\n}\n"
  },
  {
    "path": "server/ctrl/webdav.go",
    "content": "package ctrl\n\nimport (\n\t\"net/http\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n\t\"github.com/mickael-kerjean/net/webdav\"\n)\n\nfunc WebdavHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif ctx.Share.Id == \"\" {\n\t\thttp.NotFound(res, req)\n\t\treturn\n\t}\n\n\t// https://github.com/golang/net/blob/master/webdav/webdav.go#L49-L68\n\tcanRead := model.CanRead(ctx)\n\tcanWrite := model.CanEdit(ctx)\n\tcanUpload := model.CanUpload(ctx)\n\tswitch req.Method {\n\tcase \"OPTIONS\", \"HEAD\", \"GET\":\n\t\tif canRead == false {\n\t\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\t\treturn\n\t\t}\n\tcase \"MKCOL\", \"DELETE\", \"COPY\", \"MOVE\", \"PROPPATCH\":\n\t\tif canWrite == false {\n\t\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\t\treturn\n\t\t}\n\tcase \"PROPFIND\":\n\t\tif canRead == false {\n\t\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\t\treturn\n\t\t}\n\tcase \"PUT\":\n\t\tif canWrite == false || canUpload == false {\n\t\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\t\treturn\n\t\t}\n\tcase \"LOCK\", \"UNLOCK\":\n\t\tif canWrite == false || canUpload == false {\n\t\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\t\treturn\n\t\t}\n\tdefault:\n\t\tSendErrorResult(res, ErrNotImplemented)\n\t\treturn\n\t}\n\n\th := &webdav.Handler{\n\t\tPrefix:     \"/s/\" + ctx.Share.Id,\n\t\tFileSystem: model.NewWebdavFs(ctx.Backend, ctx.Share.Backend, ctx.Share.Path, req),\n\t\tLockSystem: model.NewWebdavLock(),\n\t}\n\th.ServeHTTP(res, req)\n}\n\n/*\n * OSX ask for a lot of crap while mounting as a network drive. To avoid wasting resources with such\n * an imbecile and considering we can't even see the source code they are running, the best approach we\n * could go on is: \"crap in, crap out\" where useless request coming in are identified and answer appropriatly\n */\nfunc WebdavBlacklist(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tbase := filepath.Base(req.URL.String())\n\n\t\tif req.Method == \"PUT\" || req.Method == \"MKCOL\" {\n\t\t\tif strings.HasPrefix(base, \"._\") {\n\t\t\t\tres.WriteHeader(http.StatusMethodNotAllowed)\n\t\t\t\tres.Write([]byte(\"\"))\n\t\t\t\treturn\n\t\t\t} else if base == \".DS_Store\" {\n\t\t\t\tres.WriteHeader(http.StatusMethodNotAllowed)\n\t\t\t\tres.Write([]byte(\"\"))\n\t\t\t\treturn\n\t\t\t} else if base == \".localized\" {\n\t\t\t\tres.WriteHeader(http.StatusMethodNotAllowed)\n\t\t\t\tres.Write([]byte(\"\"))\n\t\t\t\treturn\n\t\t\t}\n\t\t} else if req.Method == \"PROPFIND\" {\n\t\t\tif strings.HasPrefix(base, \"._\") {\n\t\t\t\tres.WriteHeader(http.StatusForbidden)\n\t\t\t\treturn\n\t\t\t} else if base == \".DS_Store\" {\n\t\t\t\tres.WriteHeader(http.StatusForbidden)\n\t\t\t\tres.Write([]byte(\"\"))\n\t\t\t\treturn\n\t\t\t} else if base == \".localized\" {\n\t\t\t\tres.WriteHeader(http.StatusForbidden)\n\t\t\t\treturn\n\t\t\t} else if base == \".ql_disablethumbnails\" {\n\t\t\t\tres.WriteHeader(http.StatusForbidden)\n\t\t\t\tres.Write([]byte(\"\"))\n\t\t\t\treturn\n\t\t\t} else if base == \".ql_disablecache\" {\n\t\t\t\tres.WriteHeader(http.StatusForbidden)\n\t\t\t\treturn\n\t\t\t} else if base == \".hidden\" {\n\t\t\t\tres.WriteHeader(http.StatusForbidden)\n\t\t\t\treturn\n\t\t\t} else if base == \".Spotlight-V100\" {\n\t\t\t\tres.WriteHeader(http.StatusForbidden)\n\t\t\t\treturn\n\t\t\t} else if base == \".metadata_never_index\" {\n\t\t\t\tres.WriteHeader(http.StatusForbidden)\n\t\t\t\treturn\n\t\t\t} else if base == \"Contents\" {\n\t\t\t\tres.WriteHeader(http.StatusForbidden)\n\t\t\t\treturn\n\t\t\t} else if base == \".metadata_never_index_unless_rootfs\" {\n\t\t\t\tres.WriteHeader(http.StatusForbidden)\n\t\t\t\treturn\n\t\t\t}\n\t\t} else if req.Method == \"GET\" {\n\t\t\tif base == \".DS_Store\" {\n\t\t\t\tres.WriteHeader(http.StatusForbidden)\n\t\t\t\tres.Write([]byte(\"\"))\n\t\t\t\treturn\n\t\t\t}\n\t\t} else if req.Method == \"DELETE\" {\n\t\t\tif base == \".DS_Store\" {\n\t\t\t\tres.WriteHeader(http.StatusForbidden)\n\t\t\t\tres.Write([]byte(\"\"))\n\t\t\t\treturn\n\t\t\t}\n\t\t} else if req.Method == \"LOCK\" || req.Method == \"UNLOCK\" {\n\t\t\tif base == \".DS_Store\" {\n\t\t\t\tres.WriteHeader(http.StatusMethodNotAllowed)\n\t\t\t\tres.Write([]byte(\"\"))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tfn(ctx, res, req)\n\t})\n}\n"
  },
  {
    "path": "server/generator/constants.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc main() {\n\tcmd, b := exec.Command(\"git\", \"rev-parse\", \"HEAD\"), new(strings.Builder)\n\tcmd.Stdout = b\n\tcmd.Run()\n\n\tf, err := os.OpenFile(\"../common/constants_generated.go\", os.O_CREATE|os.O_WRONLY, os.ModePerm)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t\treturn\n\t}\n\tf.Write([]byte(fmt.Sprintf(`\npackage common\n\nfunc init() {\n    BUILD_REF = \"%s\"\n    BUILD_DATE = \"%s\"\n}\n\t`, strings.TrimSpace(b.String()), time.Now().Format(\"20060102\"))))\n\tf.Close()\n}\n"
  },
  {
    "path": "server/generator/emacs-el.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc main() {\n\tf, err := os.OpenFile(\"../../config/emacs.el\", os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t\treturn\n\t}\n\tdefer f.Close()\n\n\tj, err := io.ReadAll(f)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tf, err = os.OpenFile(\"./export_generated.go\", os.O_CREATE|os.O_WRONLY, os.ModePerm)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t\treturn\n\t}\n\tf.Write([]byte(fmt.Sprintf(`package ctrl\n\nfunc init() {\n  EmacsElConfig = `+\"`\"+`\n%s\n`+\"`\"+`\n}\n`, j)))\n\tf.Close()\n}\n"
  },
  {
    "path": "server/generator/mime.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"go/format\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc main() {\n\tf, err := os.OpenFile(\"../../config/mime.json\", os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t\treturn\n\t}\n\tdefer f.Close()\n\n\tj, err := io.ReadAll(f)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tmTypes := make(map[string]string, 0)\n\tjson.Unmarshal(j, &mTypes)\n\n\tvar buf bytes.Buffer\n\tbuf.WriteString(\"package common\\n\")\n\tbuf.WriteString(\"func init() {\\n\")\n\tfor key, value := range mTypes {\n\t\tfmt.Fprintf(&buf, \"MimeTypes[\\\"%s\\\"] = \\\"%s\\\"\\n\", key, value)\n\t}\n\tbuf.WriteString(\"}\\n\")\n\tformatted, err := format.Source(buf.Bytes())\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error formatting: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tif err = os.WriteFile(\"../common/mime_generated.go\", formatted, 0644); err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error writing file: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "server/middleware/context.go",
    "content": "package middleware\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc BodyParser(fn HandlerFunc) HandlerFunc {\n\textractBody := func(req *http.Request) (map[string]interface{}, error) {\n\t\tbody := map[string]interface{}{}\n\t\tbyt, err := io.ReadAll(req.Body)\n\t\tif err != nil {\n\t\t\treturn body, err\n\t\t}\n\t\tif err := json.Unmarshal(byt, &body); err != nil {\n\t\t\tif len(byt) == 0 {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\treturn body, err\n\t\t}\n\t\treturn body, nil\n\t}\n\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tvar err error\n\t\tif ctx.Body, err = extractBody(req); err != nil {\n\t\t\tSendErrorResult(res, ErrNotValid)\n\t\t\treturn\n\t\t}\n\t\tfn(ctx, res, req)\n\t})\n}\n"
  },
  {
    "path": "server/middleware/http.go",
    "content": "package middleware\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"golang.org/x/time/rate\"\n)\n\nfunc ApiHeaders(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\theader := res.Header()\n\t\theader.Set(\"Content-Type\", \"application/json\")\n\t\theader.Set(\"Cache-Control\", \"no-cache\")\n\t\tfn(ctx, res, req)\n\t})\n}\n\nfunc StaticHeaders(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\theader := res.Header()\n\t\theader.Set(\"Content-Type\", GetMimeType(filepath.Ext(req.URL.Path)))\n\t\theader.Set(\"Cache-Control\", \"max-age=2592000\")\n\t\tfn(ctx, res, req)\n\t})\n}\n\nfunc PublicCORS(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\theader := res.Header()\n\t\theader.Set(\"Access-Control-Allow-Origin\", \"*\")\n\t\theader.Set(\"Access-Control-Allow-Headers\", \"x-requested-with\")\n\t\tif req.Method == http.MethodOptions {\n\t\t\theader.Set(\"Access-Control-Allow-Methods\", \"GET, OPTIONS\")\n\t\t\tres.WriteHeader(http.StatusNoContent)\n\t\t\treturn\n\t\t}\n\t\tfn(ctx, res, req)\n\t})\n}\n\nfunc IndexHeaders(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\theader := res.Header()\n\t\theader.Set(\"Content-Type\", \"text/html\")\n\t\theader.Set(\"Cache-Control\", \"no-cache\")\n\t\theader.Set(\"Referrer-Policy\", \"same-origin\")\n\t\theader.Set(\"X-Content-Type-Options\", \"nosniff\")\n\t\theader.Set(\"X-XSS-Protection\", \"1; mode=block\")\n\t\tif !IsWhiteLabel() {\n\t\t\theader.Set(\"X-Powered-By\", fmt.Sprintf(\"Filestash/%s.%s <https://filestash.app>\", APP_VERSION, BUILD_DATE))\n\t\t}\n\t\tif ori := Config.Get(\"features.protection.iframe\").String(); ori == \"\" {\n\t\t\theader.Set(\"X-Frame-Options\", \"DENY\")\n\t\t}\n\t\tfn(ctx, res, req)\n\t})\n}\n\nfunc SecureHeaders(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\theader := res.Header()\n\t\tif Config.Get(\"general.force_ssl\").Bool() {\n\t\t\theader.Set(\"Strict-Transport-Security\", \"max-age=31536000; includeSubDomains; preload\")\n\t\t}\n\t\theader.Set(\"X-Content-Type-Options\", \"nosniff\")\n\t\theader.Set(\"X-XSS-Protection\", \"1; mode=block\")\n\t\tfn(ctx, res, req)\n\t})\n}\n\nfunc SecureOrigin(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tif host := Config.Get(\"general.host\").String(); host != \"\" {\n\t\t\thost = strings.TrimPrefix(host, \"http://\")\n\t\t\thost = strings.TrimPrefix(host, \"https://\")\n\t\t\tif req.Host != host && req.Host != fmt.Sprintf(\"%s:443\", host) {\n\t\t\t\tif strings.HasPrefix(req.URL.Path, \"/admin/\") == false {\n\t\t\t\t\tLog.Error(\"Request coming from \\\"%s\\\" was blocked, only traffic from \\\"%s\\\" is allowed. You can change this from the admin console under configure -> host\", req.Host, host)\n\t\t\t\t\tSendErrorResult(res, ErrNotAllowed)\n\t\t\t\t\treturn\n\t\t\t\t} else {\n\t\t\t\t\tLog.Warning(\"Access from incorrect hostname. From the admin console under configure -> host, you need to use the following hostname: '%s' current value is '%s'\", req.Host, host)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif req.Header.Get(\"X-Requested-With\") == \"XmlHttpRequest\" { // Browser XHR Access\n\t\t\tfn(ctx, res, req)\n\t\t\treturn\n\t\t} else if Config.Get(\"features.api.enable\").Bool() && len(req.Cookies()) == 0 { // API Access\n\t\t\tfn(ctx, res, req)\n\t\t\treturn\n\t\t}\n\n\t\tLog.Warning(\"Intrusion detection: %s - %s\", RetrievePublicIp(req), req.URL.String())\n\t\tSendErrorResult(res, ErrNotAllowed)\n\t})\n}\n\nvar limiter = rate.NewLimiter(10, 1000)\n\nfunc RateLimiter(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tif limiter.Allow() == false {\n\t\t\tLog.Warning(\"middleware::http::ratelimit too many requests\")\n\t\t\tSendErrorResult(\n\t\t\t\tres,\n\t\t\t\tNewError(http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests),\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tfn(ctx, res, req)\n\t})\n}\n\nfunc RetrievePublicIp(req *http.Request) string {\n\tif req.Header.Get(\"X-Forwarded-For\") != \"\" {\n\t\treturn req.Header.Get(\"X-Forwarded-For\")\n\t} else {\n\t\treturn req.RemoteAddr\n\t}\n}\n"
  },
  {
    "path": "server/middleware/index.go",
    "content": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\ttime.Sleep(10 * time.Second)\n\t\t\t\ttelemetry.Flush()\n\t\t\t}\n\t\t}()\n\t})\n}\n\nfunc NewMiddlewareChain(fn HandlerFunc, m []Middleware) http.HandlerFunc {\n\treturn func(res http.ResponseWriter, req *http.Request) {\n\t\tvar resw ResponseWriter = NewResponseWriter(res)\n\t\tvar f func(*App, http.ResponseWriter, *http.Request) = fn\n\t\tfor i := len(m) - 1; i >= 0; i-- {\n\t\t\tf = m[i](f)\n\t\t}\n\t\tapp := App{\n\t\t\tContext: req.Context(),\n\t\t}\n\t\tf(&app, &resw, req)\n\t\tif req.Body != nil {\n\t\t\treq.Body.Close()\n\t\t}\n\t\tgo logger(&app, &resw, req)\n\t}\n}\n\ntype ResponseWriter struct {\n\thttp.ResponseWriter\n\tstatus int\n\tstart  time.Time\n}\n\nfunc NewResponseWriter(res http.ResponseWriter) ResponseWriter {\n\treturn ResponseWriter{\n\t\tResponseWriter: res,\n\t\tstart:          time.Now(),\n\t}\n}\n\nfunc (w *ResponseWriter) WriteHeader(status int) {\n\tw.status = status\n\tw.ResponseWriter.WriteHeader(status)\n}\n\nfunc (w *ResponseWriter) Write(b []byte) (int, error) {\n\tif w.status == 0 {\n\t\tw.status = 200\n\t}\n\treturn w.ResponseWriter.Write(b)\n}\n\nfunc (w *ResponseWriter) Status() int {\n\treturn w.status\n}\n\nfunc (w *ResponseWriter) Flush() {\n\tw.ResponseWriter.(http.Flusher).Flush()\n}\n\nfunc PluginInjector(fn HandlerFunc) HandlerFunc {\n\tfor _, middleware := range Hooks.Get.Middleware() {\n\t\tfn = middleware(fn)\n\t}\n\treturn fn\n}\n"
  },
  {
    "path": "server/middleware/session.go",
    "content": "package middleware\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n\n\t\"github.com/gorilla/mux\"\n)\n\nfunc LoggedInOnly(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tif ctx.Backend == nil || ctx.Session == nil {\n\t\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\t\treturn\n\t\t}\n\t\tfn(ctx, res, req)\n\t})\n}\n\nfunc AdminOnly(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tif admin := Config.Get(\"auth.admin\").String(); admin != \"\" {\n\t\t\tauthStr := strings.TrimPrefix(req.Header.Get(\"Authorization\"), \"Bearer \")\n\t\t\tif authStr == \"\" {\n\t\t\t\tc, err := req.Cookie(COOKIE_NAME_ADMIN)\n\t\t\t\tif err != nil {\n\t\t\t\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tauthStr = c.Value\n\t\t\t}\n\t\t\tstr, err := DecryptString(SECRET_KEY_DERIVATE_FOR_ADMIN, authStr)\n\t\t\tif err != nil {\n\t\t\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttoken := AdminToken{}\n\t\t\tjson.Unmarshal([]byte(str), &token)\n\n\t\t\tif token.IsValid() == false || token.IsAdmin() == false {\n\t\t\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tfn(ctx, res, req)\n\t})\n}\n\nfunc SessionStart(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tvar err error\n\n\t\tif ctx.Share, err = _extractShare(req); err != nil {\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\t\tctx.Authorization = _extractAuthorization(req)\n\t\tif ctx.Session, err = _extractSession(req, ctx); err != nil {\n\t\t\tRecoverFromBadCookie(res)\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\t\tif ctx.Backend, err = _extractBackend(req, ctx); err != nil {\n\t\t\tif len(ctx.Session) == 0 {\n\t\t\t\tSendErrorResult(res, ErrNotAuthorized)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\t\tctx.Languages = _extractLanguages(req)\n\n\t\tfn(ctx, res, req)\n\t})\n}\n\nfunc SessionTry(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tctx.Share, _ = _extractShare(req)\n\t\tctx.Authorization = _extractAuthorization(req)\n\t\tctx.Session, _ = _extractSession(req, ctx)\n\t\tctx.Backend, _ = _extractBackend(req, ctx)\n\n\t\tfn(ctx, res, req)\n\t})\n}\n\nfunc CanManageShare(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tshare_id := mux.Vars(req)[\"share\"]\n\t\tif share_id == \"\" {\n\t\t\tLog.Debug(\"middleware::session::share 'invalid share id'\")\n\t\t\tSendErrorResult(res, ErrNotValid)\n\t\t\treturn\n\t\t}\n\n\t\t// anyone can manage a share_id that's not been attributed yet\n\t\ts, err := model.ShareGet(share_id)\n\t\tif err != nil {\n\t\t\tif err == ErrNotFound {\n\t\t\t\tSessionStart(fn)(ctx, res, req)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tLog.Debug(\"middleware::session::share 'cannot get share - %s'\", err.Error())\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\n\t\t// In a scenario where the shared link has already been atributed, we need to make sure\n\t\t// the user that's currently logged in can manage the link. 2 scenarios here:\n\t\t// 1) scenario 1: the user is the very same one that generated the shared link in the first place\n\t\tctx.Share = Share{}\n\t\tctx.Authorization = _extractAuthorization(req)\n\t\tif ctx.Session, err = _extractSession(req, ctx); err != nil {\n\t\t\tLog.Debug(\"middleware::session::share 'cannot extract session - %s'\", err.Error())\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\t\tif s.Backend == GenerateID(ctx.Session) {\n\t\t\tfn(ctx, res, req)\n\t\t\treturn\n\t\t}\n\t\t// 2) scenario 2: the user is different than the one that has generated the shared link\n\t\t// in this scenario, the link owner might have granted for user the right to reshare links\n\t\tif ctx.Share, err = _extractShare(req); err != nil {\n\t\t\tLog.Debug(\"middleware::session::share 'cannot extract share - %s'\", err.Error())\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\t\tctx.Authorization = _extractAuthorization(req)\n\t\tif ctx.Session, err = _extractSession(req, ctx); err != nil {\n\t\t\tLog.Debug(\"middleware::session::share 'cannot extract session 2 - %s'\", err.Error())\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\n\t\tid := GenerateID(ctx.Session)\n\t\tif s.Backend == id {\n\t\t\tif s.CanShare == true {\n\t\t\t\tfn(ctx, res, req)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tLog.Debug(\"middleware::session::share 'permission denied - s.CanShare[%+v] s.Backend[%s]'\", s.CanShare, s.Backend)\n\t\t} else {\n\t\t\tLog.Debug(\"middleware::session::share 'permission denied - s.CanShare[%+v] s.Backend[%s] GenerateID[%s]'\", s.CanShare, s.Backend, id)\n\t\t}\n\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\treturn\n\t})\n}\n\nfunc _extractAuthorization(req *http.Request) (token string) {\n\t// strategy 1: split cookie\n\tindex := 0\n\tfor {\n\t\tcookie, err := req.Cookie(CookieName(index))\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tindex++\n\t\ttoken += cookie.Value\n\t}\n\t// strategy 2: Authorization header\n\tif token == \"\" {\n\t\tauthHeader := req.Header.Get(\"Authorization\")\n\t\tif authHeader != \"\" && strings.HasPrefix(authHeader, \"Bearer \") {\n\t\t\ttoken = strings.TrimPrefix(req.Header.Get(\"Authorization\"), \"Bearer \")\n\t\t}\n\t}\n\t// strategy 3: Authorization query param\n\tif token == \"\" {\n\t\tif auth := req.URL.Query().Get(\"authorization\"); auth != \"\" {\n\t\t\ttoken = auth\n\t\t}\n\t}\n\treturn token\n}\n\nfunc _extractShareId(req *http.Request) string {\n\tshare := req.URL.Query().Get(\"share\")\n\tif share != \"\" {\n\t\treturn share\n\t}\n\tm := mux.Vars(req)[\"share\"]\n\tif m == \"private\" {\n\t\treturn \"\"\n\t}\n\treturn m\n}\n\nfunc _extractShare(req *http.Request) (Share, error) {\n\tvar err error\n\tshare_id := _extractShareId(req)\n\tif share_id == \"\" {\n\t\treturn Share{}, nil\n\t}\n\tif Config.Get(\"features.share.enable\").Bool() == false {\n\t\tLog.Debug(\"Share feature isn't enabled, contact your administrator\")\n\t\treturn Share{}, NewError(\"Feature isn't enabled, contact your administrator\", 405)\n\t}\n\n\ts, err := model.ShareGet(share_id)\n\tif err != nil {\n\t\treturn Share{}, nil\n\t}\n\tif err = s.IsValid(); err != nil {\n\t\treturn Share{}, err\n\t}\n\n\tvar verifiedProof []model.Proof = model.ShareProofGetAlreadyVerified(req)\n\tusername, password := func(authHeader string) (string, string) {\n\t\tdecoded, err := base64.StdEncoding.DecodeString(\n\t\t\tstrings.TrimPrefix(authHeader, \"Basic \"),\n\t\t)\n\t\tif err != nil {\n\t\t\treturn \"\", \"\"\n\t\t}\n\t\ts := bytes.Split(decoded, []byte(\":\"))\n\t\tif len(s) < 2 {\n\t\t\treturn \"\", \"\"\n\t\t}\n\t\tp := string(bytes.Join(s[1:], []byte(\":\")))\n\t\tusr := regexp.MustCompile(`^(.*)\\[([0-9a-zA-Z]+)\\]$`).FindStringSubmatch(string(s[0]))\n\t\tif len(usr) != 3 {\n\t\t\treturn \"\", p\n\t\t}\n\t\tif Hash(usr[1]+SECRET_KEY_DERIVATE_FOR_HASH, 10) != usr[2] {\n\t\t\treturn \"\", p\n\t\t}\n\t\treturn usr[1], p\n\t}(req.Header.Get(\"Authorization\"))\n\n\tif s.Users != nil && username != \"\" {\n\t\tif v, ok := model.ShareProofVerifierEmail(*s.Users, username); ok {\n\t\t\tverifiedProof = append(verifiedProof, model.Proof{Key: \"email\", Value: v})\n\t\t}\n\t}\n\tif s.Password != nil && password != \"\" {\n\t\tif v, ok := model.ShareProofVerifierPassword(*s.Password, password); ok {\n\t\t\tverifiedProof = append(verifiedProof, model.Proof{Key: \"password\", Value: v})\n\t\t}\n\t}\n\tvar requiredProof []model.Proof = model.ShareProofGetRequired(s)\n\tvar remainingProof []model.Proof = model.ShareProofCalculateRemainings(requiredProof, verifiedProof)\n\tif len(remainingProof) != 0 {\n\t\treturn Share{}, NewError(\"Unauthorized Shared space\", 400)\n\t}\n\treturn s, nil\n}\n\nfunc _extractSession(req *http.Request, ctx *App) (map[string]string, error) {\n\tvar (\n\t\tstr     string\n\t\terr     error\n\t\tsession map[string]string = make(map[string]string)\n\t)\n\n\tif ctx.Share.Id != \"\" { // Shared link\n\t\tstr, err = DecryptString(SECRET_KEY_DERIVATE_FOR_USER, ctx.Share.Auth)\n\t\tif err != nil {\n\t\t\t// This typically happen when changing the secret key\n\t\t\treturn session, ErrNotAuthorized\n\t\t}\n\t\terr = json.Unmarshal([]byte(str), &session)\n\t\tif IsDirectory(ctx.Share.Path) {\n\t\t\tsession[\"path\"] = ctx.Share.Path\n\t\t} else {\n\t\t\t// when the shared link is pointing to a file, we mustn't have access to the surroundings\n\t\t\t// => we need to take extra care of which path to use as a chroot\n\t\t\tvar path string = req.URL.Query().Get(\"path\")\n\t\t\tif strings.HasPrefix(req.URL.Path, \"/api/export/\") == true {\n\t\t\t\tvar re = regexp.MustCompile(`^/api/export/[^\\/]+/[^\\/]+/[^\\/]+(\\/.+)$`)\n\t\t\t\tpath = re.ReplaceAllString(req.URL.Path, `$1`)\n\t\t\t}\n\t\t\tif strings.HasSuffix(ctx.Share.Path, path) == false {\n\t\t\t\treturn make(map[string]string), ErrPermissionDenied\n\t\t\t}\n\t\t\tsession[\"path\"] = strings.TrimSuffix(ctx.Share.Path, path) + \"/\"\n\t\t}\n\t\treturn session, err\n\t}\n\n\tif ctx.Authorization == \"\" {\n\t\treturn session, nil\n\t}\n\tstr, err = DecryptString(SECRET_KEY_DERIVATE_FOR_USER, ctx.Authorization)\n\tif err != nil {\n\t\t// This typically happen when changing the secret key\n\t\tLog.Debug(\"middleware::session decrypt error '%s'\", err.Error())\n\t\treturn session, ErrNotAuthorized\n\t}\n\tif err = json.Unmarshal([]byte(str), &session); err != nil {\n\t\treturn session, err\n\t}\n\tt, err := time.Parse(time.RFC3339, session[\"timestamp\"])\n\tif err != nil {\n\t\tLog.Warning(\"middleware::session 'cannot parse time - %s'\", err.Error())\n\t\treturn session, ErrNotAuthorized\n\t} else if t.Add(24 * 365 * time.Hour).Before(time.Now()) {\n\t\tLog.Warning(\"middleware::session 'cookie too old - %s'\", t.Format(time.RFC3339))\n\t\treturn session, ErrNotAuthorized\n\t}\n\treturn session, err\n}\n\nfunc _extractBackend(req *http.Request, ctx *App) (IBackend, error) {\n\treturn model.NewBackend(ctx, ctx.Session)\n}\n\nfunc _extractLanguages(req *http.Request) []string {\n\tvar lng = []string{}\n\tfor _, lngs := range strings.Split(req.Header.Get(\"Accept-Language\"), \",\") {\n\t\tchunks := strings.Split(lngs, \";\")\n\t\tif len(chunks) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tlng = append(lng, chunks[0])\n\t}\n\treturn lng\n}\n"
  },
  {
    "path": "server/middleware/telemetry.go",
    "content": "package middleware\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar telemetry = Telemetry{Data: make([]LogEntry, 0)}\n\ntype Telemetry struct {\n\tData []LogEntry\n\tmu   sync.Mutex\n}\n\ntype LogEntry struct {\n\tHost       string  `json:\"host\"`\n\tMethod     string  `json:\"method\"`\n\tRequestURI string  `json:\"pathname\"`\n\tProto      string  `json:\"proto\"`\n\tStatus     int     `json:\"status\"`\n\tScheme     string  `json:\"scheme\"`\n\tUserAgent  string  `json:\"userAgent\"`\n\tIp         string  `json:\"ip\"`\n\tReferer    string  `json:\"referer\"`\n\tDuration   float64 `json:\"responseTime\"`\n\tVersion    string  `json:\"version\"`\n\tBackend    string  `json:\"backend\"`\n\tShare      string  `json:\"share\"`\n\tLicense    string  `json:\"license\"`\n\tSession    string  `json:\"session\"`\n\tRequestID  string  `json:\"requestID\"`\n}\n\nfunc logger(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif obj, ok := res.(*ResponseWriter); ok && req.RequestURI != \"/about\" {\n\t\tpoint := LogEntry{\n\t\t\tVersion:    APP_VERSION + \".\" + BUILD_DATE,\n\t\t\tLicense:    LICENSE,\n\t\t\tScheme:     req.URL.Scheme,\n\t\t\tHost:       req.Host,\n\t\t\tMethod:     req.Method,\n\t\t\tRequestURI: req.RequestURI,\n\t\t\tProto:      req.Proto,\n\t\t\tStatus:     obj.status,\n\t\t\tUserAgent:  req.Header.Get(\"User-Agent\"),\n\t\t\tIp:         req.RemoteAddr,\n\t\t\tReferer:    req.Referer(),\n\t\t\tDuration:   float64(time.Now().Sub(obj.start)) / (1000 * 1000),\n\t\t\tBackend: func() string {\n\t\t\t\tif ctx.Session[\"type\"] == \"\" {\n\t\t\t\t\treturn \"null\"\n\t\t\t\t}\n\t\t\t\treturn ctx.Session[\"type\"]\n\t\t\t}(),\n\t\t\tShare: func() string {\n\t\t\t\tif ctx.Share.Id == \"\" {\n\t\t\t\t\treturn \"null\"\n\t\t\t\t}\n\t\t\t\treturn ctx.Share.Id\n\t\t\t}(),\n\t\t\tSession: func() string {\n\t\t\t\tif ctx.Session[\"type\"] == \"\" {\n\t\t\t\t\treturn \"null\"\n\t\t\t\t}\n\t\t\t\treturn GenerateID(ctx.Session)\n\t\t\t}(),\n\t\t\tRequestID: func() string {\n\t\t\t\tdefer func() string {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\treturn \"oops\"\n\t\t\t\t\t}\n\t\t\t\t\treturn \"null\"\n\t\t\t\t}()\n\t\t\t\treturn res.Header().Get(\"X-Request-ID\")\n\t\t\t}(),\n\t\t}\n\t\tif Config.Get(\"log.telemetry\").Bool() {\n\t\t\ttelemetry.Record(point)\n\t\t}\n\t\tif Config.Get(\"log.enable\").Bool() {\n\t\t\tLog.Stdout(\"HTTP %3d %3s %6.1fms %s\", point.Status, point.Method, point.Duration, limit(point.RequestURI, 200))\n\t\t}\n\t}\n}\n\nfunc limit(input string, maxLength int) string {\n\tif len(input) > maxLength {\n\t\treturn input[:maxLength] + \"...\"\n\t}\n\treturn input\n}\n\nfunc (this *Telemetry) Record(point LogEntry) {\n\tthis.mu.Lock()\n\tthis.Data = append(this.Data, point)\n\tthis.mu.Unlock()\n}\n\nfunc (this *Telemetry) Flush() {\n\tif len(this.Data) == 0 {\n\t\treturn\n\t}\n\tthis.mu.Lock()\n\tpts := this.Data\n\tthis.Data = make([]LogEntry, 0)\n\tthis.mu.Unlock()\n\n\tbody, err := json.Marshal(pts)\n\tif err != nil {\n\t\treturn\n\t}\n\tr, err := http.NewRequest(\"POST\", \"https://downloads.filestash.app/event\", bytes.NewReader(body))\n\tr.Header.Set(\"Connection\", \"Close\")\n\tr.Header.Set(\"Content-Type\", \"application/json\")\n\tr.Close = true\n\tif err != nil {\n\t\treturn\n\t}\n\tresp, err := HTTP.Do(r)\n\tif err != nil {\n\t\treturn\n\t}\n\tresp.Body.Close()\n}\n"
  },
  {
    "path": "server/model/audit.go",
    "content": "package model\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.AuditEngine(SimpleAudit{})\n}\n\nvar AuditForm Form = Form{\n\tForm: []Form{\n\t\tForm{\n\t\t\tTitle: \"search\",\n\t\t\tElmnts: []FormElement{\n\t\t\t\tFormElement{\n\t\t\t\t\tName: \"date from\",\n\t\t\t\t\tType: \"datetime\",\n\t\t\t\t},\n\t\t\t\tFormElement{\n\t\t\t\t\tName: \"date to\",\n\t\t\t\t\tType: \"datetime\",\n\t\t\t\t},\n\t\t\t\tFormElement{\n\t\t\t\t\tName: \"action\",\n\t\t\t\t\tType: \"select\",\n\t\t\t\t\tOpts: []string{\"\", \"rename\", \"list\", \"download\", \"create_folder\", \"remove\", \"move\", \"save_file\", \"create_file\"},\n\t\t\t\t},\n\t\t\t\tFormElement{\n\t\t\t\t\tName: \"path\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t},\n\t\t\t\tFormElement{\n\t\t\t\t\tName: \"backend\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t},\n\t\t\t\tFormElement{\n\t\t\t\t\tName: \"session\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t},\n\t\t\t\tFormElement{\n\t\t\t\t\tName: \"share\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t},\n\t\t\t\tFormElement{\n\t\t\t\t\tName: \"user\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t},\n\t\t\t\tFormElement{\n\t\t\t\t\tName: \"target\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n}\n\ntype SimpleAudit struct{}\n\nfunc (this SimpleAudit) Query(ctx *App, searchParams map[string]string) (AuditQueryResult, error) {\n\treturn AuditQueryResult{\n\t\tForm: &AuditForm,\n\t\tRenderHTML: `<style>\n            #alert-audit-missing{\n                background: var(--error); color: var(--super-light);\n                padding: 15px 15px;\n                border-radius: 2px;\n                margin-top: 15px;\n            }\n        </style>\n        <div id=\"alert-audit-missing\">\n            You need to install an audit plugin to use this\n        </div>`,\n\t}, nil\n}\n"
  },
  {
    "path": "server/model/files.go",
    "content": "package model\n\nimport (\n\t\"fmt\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"strings\"\n)\n\nfunc NewBackend(ctx *App, conn map[string]string) (IBackend, error) {\n\tisAllowed := func() bool {\n\t\t// by default, a hacker could use filestash to establish connections outside of what's\n\t\t// define in the config file. We need to prevent this\n\t\tpossibilities := make([]map[string]interface{}, 0)\n\t\tfor i := 0; i < len(Config.Conn); i++ {\n\t\t\td := Config.Conn[i]\n\t\t\tif d[\"type\"] != conn[\"type\"] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif val, ok := d[\"hostname\"]; ok == true {\n\t\t\t\tif val != conn[\"hostname\"] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tif val, ok := d[\"path\"]; ok == true {\n\t\t\t\tif val == nil {\n\t\t\t\t\tval = \"/\"\n\t\t\t\t}\n\t\t\t\tif configPath, ok := val.(string); ok == false {\n\t\t\t\t\tcontinue\n\t\t\t\t} else if strings.HasPrefix(conn[\"path\"], configPath) == false {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tif val, ok := d[\"url\"]; ok == true {\n\t\t\t\tif val != conn[\"url\"] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tpossibilities = append(possibilities, Config.Conn[i])\n\t\t}\n\t\tif len(possibilities) > 0 {\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\n\tif isAllowed() == false {\n\t\treturn Backend.Get(BACKEND_NIL), ErrNotAllowed\n\t}\n\treturn Backend.Get(conn[\"type\"]).Init(conn, ctx)\n}\n\nfunc GetHome(b IBackend, base string) (string, error) {\n\tif strings.TrimSpace(base) == \"\" {\n\t\tbase = \"/\"\n\t}\n\thome := \"/\"\n\tif obj, ok := b.(interface{ Home() (string, error) }); ok {\n\t\ttmp, err := obj.Home()\n\t\tif err != nil {\n\t\t\treturn base, err\n\t\t}\n\t\thome = EnforceDirectory(tmp)\n\t} else if _, err := b.Ls(base); err != nil {\n\t\treturn base, err\n\t}\n\n\tbase = EnforceDirectory(base)\n\tif strings.HasPrefix(home, base) {\n\t\treturn \"/\" + home[len(base):], nil\n\t}\n\treturn \"/\", nil\n}\n\nfunc MapStringInterfaceToMapStringString(m map[string]interface{}) map[string]string {\n\tres := make(map[string]string)\n\tfor key, value := range m {\n\t\tres[key] = fmt.Sprintf(\"%v\", value)\n\t\tif res[key] == \"<nil>\" {\n\t\t\tres[key] = \"\"\n\t\t}\n\t}\n\treturn res\n}\n"
  },
  {
    "path": "server/model/formater/README.md",
    "content": "This is a bare bone utilities to convert a stream onto text for full text search purpose.\nThere's some other alternative but none of them run with a small footprint. \n\nAt the moment it supports:\n- office documents\n- pdf (TODO: remove dependency on pdftotext)\n- text base files\n"
  },
  {
    "path": "server/model/formater/office.go",
    "content": "package formater\n\nimport (\n\t\"archive/zip\"\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"math/rand\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nfunc OfficeFormater(r io.ReadCloser) (io.ReadCloser, error) {\n\ttmpName := fmt.Sprintf(\"/tmp/docx_%d.docx\", rand.Intn(1000000))\n\tdefer os.Remove(tmpName)\n\tf, err := os.OpenFile(tmpName, os.O_CREATE|os.O_WRONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t_, err = io.Copy(f, r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tz, err := zip.OpenReader(tmpName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer z.Close()\n\n\thasData := false\n\tcontent := bytes.NewBuffer([]byte{})\n\tfor _, f := range z.File {\n\t\tshouldExtract := false\n\t\tif f.Name == \"word/document.xml\" {\n\t\t\tshouldExtract = true\n\t\t} else if strings.HasPrefix(f.Name, \"ppt/slides/slide\") {\n\t\t\tshouldExtract = true\n\t\t} else if f.Name == \"xl/sharedStrings.xml\" {\n\t\t\tshouldExtract = true\n\t\t}\n\n\t\tif shouldExtract == false {\n\t\t\tcontinue\n\t\t}\n\t\thasData = true\n\t\to, err := f.Open()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdec := xml.NewDecoder(o)\n\t\tfor {\n\t\t\tt, err := dec.Token()\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif t == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tswitch el := t.(type) {\n\t\t\tcase xml.StartElement:\n\t\t\t\tif el.Name.Local == \"t\" {\n\t\t\t\t\tw := WordDoc{}\n\t\t\t\t\tdec.DecodeElement(&w, &el)\n\t\t\t\t\tif len(w.Text) > 0 {\n\t\t\t\t\t\tw.Text = regexp.MustCompile(\"\\\\s+\\\\.\\\\s+\").ReplaceAll(w.Text, []byte(\". \"))\n\t\t\t\t\t\tw.Text = regexp.MustCompile(\"\\\\s{2,}\").ReplaceAll(w.Text, []byte(\" \"))\n\t\t\t\t\t\tcontent.Write(w.Text)\n\t\t\t\t\t\tcontent.Write([]byte(\" \"))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif hasData == false {\n\t\treturn nil, ErrNotFound\n\t}\n\treturn NewReadCloserFromReader(content), nil\n}\n\ntype WordDoc struct {\n\tText []byte `xml:\",innerxml\"`\n}\n"
  },
  {
    "path": "server/model/formater/pdf.go",
    "content": "package formater\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"math/rand\"\n\t\"os\"\n\t\"os/exec\"\n)\n\nfunc PdfFormater(r io.ReadCloser) (io.ReadCloser, error) {\n\ttmpName := fmt.Sprintf(\"/tmp/pdf_%d.docx\", rand.Intn(1000000))\n\tdefer os.Remove(tmpName)\n\tf, err := os.OpenFile(tmpName, os.O_CREATE|os.O_WRONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t_, err = io.Copy(f, r)\n\tif err != nil {\n\t\tf.Close()\n\t\treturn nil, err\n\t}\n\tf.Close()\n\n\tcmd := exec.Command(\"pdftotext\", tmpName, \"-\")\n\tout := bytes.NewBuffer([]byte{})\n\tcmd.Stdout = out\n\terr = cmd.Run()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewReadCloserFromReader(out), nil\n}\n"
  },
  {
    "path": "server/model/formater/txt.go",
    "content": "package formater\n\nimport (\n\t\"io\"\n)\n\nfunc TxtFormater(rc io.ReadCloser) (io.ReadCloser, error) {\n\treturn rc, nil\n}\n"
  },
  {
    "path": "server/model/index.go",
    "content": "package model\n\nimport (\n\t\"database/sql\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar DB *sql.DB\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tvar err error\n\t\tif DB, err = sql.Open(\"sqlite3\", GetAbsolutePath(DB_PATH)+\"/share.sql?_fk=true\"); err != nil {\n\t\t\tLog.Error(\"model::index sqlite open error '%s'\", err.Error())\n\t\t\treturn\n\t\t}\n\n\t\tif stmt, err := DB.Prepare(\"CREATE TABLE IF NOT EXISTS Location(backend VARCHAR(16), path VARCHAR(512), CONSTRAINT pk_location PRIMARY KEY(backend, path))\"); err == nil {\n\t\t\tstmt.Exec()\n\t\t}\n\n\t\tif stmt, err := DB.Prepare(\"CREATE TABLE IF NOT EXISTS Share(id VARCHAR(64) PRIMARY KEY, related_backend VARCHAR(16), related_path VARCHAR(512), params JSON, auth VARCHAR(4093) NOT NULL, FOREIGN KEY (related_backend, related_path) REFERENCES Location(backend, path) ON UPDATE CASCADE ON DELETE CASCADE)\"); err == nil {\n\t\t\tstmt.Exec()\n\t\t}\n\n\t\tif stmt, err := DB.Prepare(\"CREATE TABLE IF NOT EXISTS Verification(key VARCHAR(512), code VARCHAR(4), expire DATETIME DEFAULT (datetime('now', '+10 minutes')))\"); err == nil {\n\t\t\tstmt.Exec()\n\t\t\tif stmt, err = DB.Prepare(\"CREATE INDEX idx_verification ON Verification(code, expire)\"); err == nil {\n\t\t\t\tstmt.Exec()\n\t\t\t}\n\t\t}\n\n\t\tgo func() {\n\t\t\tautovacuum()\n\t\t}()\n\t})\n}\n\nfunc autovacuum() {\n\tif stmt, err := DB.Prepare(\"DELETE FROM Verification WHERE expire < datetime('now')\"); err == nil {\n\t\tstmt.Exec()\n\t}\n\ttime.Sleep(6 * time.Hour)\n}\n"
  },
  {
    "path": "server/model/permissions.go",
    "content": "package model\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc CanRead(ctx *App) bool {\n\tif ctx.Share.Id != \"\" {\n\t\treturn ctx.Share.CanRead\n\t}\n\treturn true\n}\n\nfunc CanEdit(ctx *App) bool {\n\tif ctx.Share.Id != \"\" {\n\t\treturn ctx.Share.CanWrite\n\t}\n\treturn true\n}\n\nfunc CanUpload(ctx *App) bool {\n\tif ctx.Share.Id != \"\" {\n\t\treturn ctx.Share.CanUpload\n\t}\n\treturn true\n}\n\nfunc CanShare(ctx *App) bool {\n\tif ctx.Share.Id != \"\" {\n\t\treturn ctx.Share.CanShare\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "server/model/plugin.go",
    "content": "package model\n\nimport (\n\t\"archive/zip\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar PLUGINS = map[string]PluginImpl{}\n\ntype PluginImpl struct {\n\tAuthor  string              `json:\"author\"`\n\tVersion string              `json:\"version\"`\n\tModules []map[string]string `json:\"modules\"`\n}\n\nfunc PluginDiscovery() error {\n\tentries, err := os.ReadDir(GetAbsolutePath(PLUGIN_PATH))\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tfname := entry.Name()\n\t\tif strings.HasSuffix(fname, \".zip\") == false {\n\t\t\tcontinue\n\t\t}\n\t\tname, impl, err := InitModule(fname)\n\t\tif err != nil {\n\t\t\tLog.Error(\"could not initialise module name=%s err=%s\", entry.Name(), err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tfor i := 0; i < len(impl.Modules); i++ {\n\t\t\tswitch impl.Modules[i][\"type\"] {\n\t\t\tcase \"css\":\n\t\t\t\tb, err := GetPluginFile(name, impl.Modules[i][\"entrypoint\"])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tHooks.Register.CSS(string(b))\n\t\t\tcase \"patch\":\n\t\t\t\tb, err := GetPluginFile(name, impl.Modules[i][\"entrypoint\"])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tHooks.Register.StaticPatch(b)\n\t\t\tcase \"favicon\":\n\t\t\t\tb, err := GetPluginFile(name, impl.Modules[i][\"entrypoint\"])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tHooks.Register.Favicon(b)\n\t\t\tcase \"middleware\":\n\t\t\t\tb, err := GetPluginFile(name, impl.Modules[i][\"entrypoint\"])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tm, err := WasmAdapterForMiddleware(b)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tHooks.Register.Middleware(m)\n\t\t\tcase \"http\": // TODO\n\t\t\t\treturn ErrNotImplemented\n\t\t\t}\n\t\t}\n\t\tPLUGINS[name] = impl\n\t}\n\treturn nil\n}\n\nfunc GetPluginFile(pluginName string, path string) ([]byte, error) {\n\tzipReader, err := zip.OpenReader(JoinPath(\n\t\tGetAbsolutePath(PLUGIN_PATH),\n\t\tpluginName+\".zip\",\n\t))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, zipFile := range zipReader.File {\n\t\tif zipFile.Name != path {\n\t\t\tcontinue\n\t\t}\n\t\tf, err := zipFile.Open()\n\t\tif err != nil {\n\t\t\tzipReader.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\tdata, err := io.ReadAll(f)\n\t\tf.Close()\n\t\tzipReader.Close()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn data, nil\n\t}\n\tzipReader.Close()\n\treturn nil, ErrNotFound\n}\n\nfunc InitModule(plgName string) (string, PluginImpl, error) {\n\tvar plgImpl = PluginImpl{}\n\tr, err := zip.OpenReader(JoinPath(GetAbsolutePath(PLUGIN_PATH), plgName))\n\tplgName = strings.TrimSuffix(plgName, \".zip\")\n\tif err != nil {\n\t\treturn plgName, plgImpl, err\n\t}\n\tdefer r.Close()\n\n\tvar manifestFile io.ReadCloser\n\tfor _, f := range r.File {\n\t\tif f.Name != \"manifest.json\" {\n\t\t\tcontinue\n\t\t}\n\t\trc, err := f.Open()\n\t\tif err != nil {\n\t\t\treturn plgName, plgImpl, err\n\t\t}\n\t\tmanifestFile = rc\n\t\tbreak\n\t}\n\tif manifestFile == nil {\n\t\treturn plgName, plgImpl, ErrNotFound\n\t}\n\tdefer manifestFile.Close()\n\tif err = json.NewDecoder(manifestFile).Decode(&plgImpl); err != nil {\n\t\treturn plgName, plgImpl, err\n\t}\n\treturn plgName, plgImpl, nil\n}\n"
  },
  {
    "path": "server/model/plugin_adapter.go",
    "content": "package model\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"github.com/tetratelabs/wazero\"\n\t\"github.com/tetratelabs/wazero/api\"\n)\n\nfunc WasmAdapterForMiddleware(wasmBytes []byte) (Middleware, error) {\n\treturn func(next HandlerFunc) HandlerFunc {\n\t\treturn func(app *App, w http.ResponseWriter, r *http.Request) {\n\t\t\tmodule, err := wasmPrepare(wasmBytes)\n\t\t\tif err != nil {\n\t\t\t\tSendErrorResult(w, NewError(\"plugin::adapter action=prepare error=\"+err.Error(), http.StatusInternalServerError))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer module.Close(app.Context)\n\t\t\twasmFunc := module.ExportedFunction(\"middleware\")\n\t\t\tif wasmFunc == nil {\n\t\t\t\tSendErrorResult(w, NewError(\"plugin::adapter action=export error=missing+middleware+function\", http.StatusInternalServerError))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresponse, err := wasmFunc.Call(app.Context)\n\t\t\tif err != nil {\n\t\t\t\tSendErrorResult(w, NewError(err.Error(), http.StatusInternalServerError))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresponseBytes, err := wasmOutput(module.Memory(), response)\n\t\t\tif err != nil {\n\t\t\t\tSendErrorResult(w, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresp, err := http.ReadResponse(bufio.NewReader(bytes.NewReader(responseBytes)), nil)\n\t\t\tif err != nil {\n\t\t\t\tSendErrorResult(w, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer resp.Body.Close()\n\t\t\tfor head, value := range resp.Header {\n\t\t\t\tw.Header()[head] = value\n\t\t\t}\n\n\t\t\tif resp.StatusCode != http.StatusNoContent {\n\t\t\t\tw.WriteHeader(resp.StatusCode)\n\t\t\t\tio.Copy(w, resp.Body)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tnext(app, w, r)\n\t\t\treturn\n\t\t}\n\t}, nil\n}\n\nfunc wasmPrepare(wasmBytes []byte) (api.Module, error) {\n\tctx := context.Background()\n\truntime := wazero.NewRuntime(ctx)\n\tcompiledModule, err := runtime.CompileModule(ctx, wasmBytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn runtime.InstantiateModule(ctx, compiledModule, wazero.NewModuleConfig())\n}\n\nfunc wasmOutput(memory api.Memory, response []uint64) ([]byte, error) {\n\tif len(response) != 1 {\n\t\treturn nil, NewError(\"Invalid WASM response\", http.StatusInternalServerError)\n\t}\n\tptr := uint32(response[0])\n\tvar responseLength uint32\n\tfor offset := uint32(0); ; offset += 8192 {\n\t\tchunk, ok := memory.Read(ptr+offset, 8192)\n\t\tif !ok {\n\t\t\tresponseLength = offset\n\t\t\tbreak\n\t\t}\n\t\tfor i, b := range chunk {\n\t\t\tif b == 0 {\n\t\t\t\tresponseLength = offset + uint32(i)\n\t\t\t\tgoto found\n\t\t\t}\n\t\t}\n\t}\nfound:\n\tresponseBytes, _ := memory.Read(ptr, responseLength)\n\treturn responseBytes, nil\n}\n"
  },
  {
    "path": "server/model/share.go",
    "content": "package model\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/pkg/sqlite\"\n\n\t\"golang.org/x/crypto/bcrypt\"\n\t\"gopkg.in/gomail.v2\"\n\t\"html/template\"\n)\n\ntype Proof struct {\n\tId      string  `json:\"id\"`\n\tKey     string  `json:\"key\"`\n\tValue   string  `json:\"-\"`\n\tMessage *string `json:\"message,omitempty\"`\n\tError   *string `json:\"error,omitempty\"`\n}\n\nfunc ShareList(backend string, path string) ([]Share, error) {\n\tstmt, err := DB.Prepare(\"SELECT id, related_path, params FROM Share WHERE related_backend = ? AND related_path LIKE ? || '%' \")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trows, err := stmt.Query(backend, path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsharedFiles := []Share{}\n\tfor rows.Next() {\n\t\tvar a Share\n\t\tvar params []byte\n\t\trows.Scan(&a.Id, &a.Path, &params)\n\t\tjson.Unmarshal(params, &a)\n\t\tsharedFiles = append(sharedFiles, a)\n\t}\n\trows.Close()\n\treturn sharedFiles, nil\n}\n\nfunc ShareAll() ([]Share, error) {\n\trows, err := DB.Query(\"SELECT id, related_path, params FROM Share\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsharedFiles := []Share{}\n\tfor rows.Next() {\n\t\tvar a Share\n\t\tvar params []byte\n\t\trows.Scan(&a.Id, &a.Path, &params)\n\t\tjson.Unmarshal(params, &a)\n\t\tsharedFiles = append(sharedFiles, a)\n\t}\n\trows.Close()\n\treturn sharedFiles, nil\n}\n\nfunc ShareGet(id string) (Share, error) {\n\tvar p Share\n\tstmt, err := DB.Prepare(\"SELECT id, related_backend, related_path, auth, params FROM share WHERE id = ?\")\n\tif err != nil {\n\t\treturn p, err\n\t}\n\tdefer stmt.Close()\n\trow := stmt.QueryRow(id)\n\tvar str []byte\n\tif err = row.Scan(&p.Id, &p.Backend, &p.Path, &p.Auth, &str); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn p, ErrNotFound\n\t\t}\n\t\treturn p, err\n\t}\n\tjson.Unmarshal(str, &p)\n\treturn p, nil\n}\n\nfunc ShareUpsert(p *Share) error {\n\tif p.Password != nil {\n\t\tif *p.Password == PASSWORD_DUMMY {\n\t\t\tif s, err := ShareGet(p.Id); err != nil {\n\t\t\t\tp.Password = s.Password\n\t\t\t}\n\t\t} else {\n\t\t\thashedPassword, _ := bcrypt.GenerateFromPassword([]byte(*p.Password), bcrypt.DefaultCost)\n\t\t\tp.Password = NewString(string(hashedPassword))\n\t\t}\n\t}\n\n\tstmt, err := DB.Prepare(\"INSERT INTO Location(backend, path) VALUES($1, $2)\")\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = stmt.Exec(p.Backend, p.Path)\n\tif err != nil {\n\t\tthrow := true\n\t\tif sqlite.IsConstraint(err) {\n\t\t\tthrow = false\n\t\t}\n\t\tif throw == true {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tstmt, err = DB.Prepare(\"INSERT INTO Share(id, related_backend, related_path, params, auth) VALUES($1, $2, $3, $4, $5) ON CONFLICT(id) DO UPDATE SET related_backend = $2, related_path = $3, params = $4\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tj, _ := json.Marshal(&struct {\n\t\tPassword     *string `json:\"password,omitempty\"`\n\t\tUsers        *string `json:\"users,omitempty\"`\n\t\tExpire       *int64  `json:\"expire,omitempty\"`\n\t\tUrl          *string `json:\"url,omitempty\"`\n\t\tCanShare     bool    `json:\"can_share\"`\n\t\tCanManageOwn bool    `json:\"can_manage_own\"`\n\t\tCanRead      bool    `json:\"can_read\"`\n\t\tCanWrite     bool    `json:\"can_write\"`\n\t\tCanUpload    bool    `json:\"can_upload\"`\n\t}{\n\t\tPassword:     p.Password,\n\t\tUsers:        p.Users,\n\t\tExpire:       p.Expire,\n\t\tUrl:          p.Url,\n\t\tCanShare:     p.CanShare,\n\t\tCanManageOwn: p.CanManageOwn,\n\t\tCanRead:      p.CanRead,\n\t\tCanWrite:     p.CanWrite,\n\t\tCanUpload:    p.CanUpload,\n\t})\n\t_, err = stmt.Exec(p.Id, p.Backend, p.Path, j, p.Auth)\n\treturn err\n}\n\nfunc ShareDelete(id string) error {\n\tstmt, err := DB.Prepare(\"DELETE FROM Share WHERE id = ?\")\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = stmt.Exec(id)\n\treturn err\n}\n\nfunc ShareProofVerifier(s Share, proof Proof) (Proof, error) {\n\tp := proof\n\n\tif proof.Key == \"password\" {\n\t\tif s.Password == nil {\n\t\t\treturn p, NewError(\"No password required\", 400)\n\t\t}\n\n\t\tv, ok := ShareProofVerifierPassword(*s.Password, proof.Value)\n\t\tif ok == false {\n\t\t\ttime.Sleep(1000 * time.Millisecond)\n\t\t\treturn p, ErrInvalidPassword\n\t\t}\n\t\tp.Value = v\n\t}\n\n\tif proof.Key == \"email\" {\n\t\t// find out if user is authorized\n\t\tif s.Users == nil {\n\t\t\treturn p, NewError(\"Authentication not required\", 400)\n\t\t}\n\t\tv, ok := ShareProofVerifierEmail(*s.Users, proof.Value)\n\t\tif ok == false {\n\t\t\ttime.Sleep(1000 * time.Millisecond)\n\t\t\treturn p, ErrNotAuthorized\n\t\t}\n\t\tuser := v\n\n\t\t// prepare the verification code\n\t\tstmt, err := DB.Prepare(\"INSERT INTO Verification(key, code) VALUES(?, ?)\")\n\t\tif err != nil {\n\t\t\treturn p, err\n\t\t}\n\t\tcode := RandomString(4)\n\t\tif _, err := stmt.Exec(\"email::\"+user, code); err != nil {\n\t\t\treturn p, err\n\t\t}\n\n\t\t// Prepare message\n\t\tvar b bytes.Buffer\n\t\tt := template.New(\"email\")\n\t\tt.Parse(TmplEmailVerification())\n\t\tt.Execute(&b, struct {\n\t\t\tCode     string\n\t\t\tUsername string\n\t\t}{code, networkDriveUsernameEnc(user)})\n\n\t\tp.Key = \"code\"\n\t\tp.Value = \"\"\n\t\tp.Message = NewString(\"We've sent you a message with a verification code\")\n\n\t\t// Send email\n\t\temail := struct {\n\t\t\tHostname string `json:\"server\"`\n\t\t\tPort     int    `json:\"port\"`\n\t\t\tUsername string `json:\"username\"`\n\t\t\tPassword string `json:\"password\"`\n\t\t\tFrom     string `json:\"from\"`\n\t\t}{\n\t\t\tHostname: Config.Get(\"email.server\").String(),\n\t\t\tPort:     Config.Get(\"email.port\").Int(),\n\t\t\tUsername: Config.Get(\"email.username\").String(),\n\t\t\tPassword: Config.Get(\"email.password\").String(),\n\t\t\tFrom:     Config.Get(\"email.from\").String(),\n\t\t}\n\n\t\tm := gomail.NewMessage()\n\t\tm.SetHeader(\"From\", email.From)\n\t\tm.SetHeader(\"To\", proof.Value)\n\t\tm.SetHeader(\"Subject\", \"Your verification code\")\n\t\tm.SetBody(\"text/html\", b.String())\n\t\td := gomail.NewDialer(email.Hostname, email.Port, email.Username, email.Password)\n\t\td.TLSConfig = &tls.Config{InsecureSkipVerify: true}\n\t\tif err := d.DialAndSend(m); err != nil {\n\t\t\tLog.Error(\"Sendmail error: %v\", err)\n\t\t\tLog.Error(\"Verification code '%s'\", code)\n\t\t\treturn p, NewError(\"Couldn't send email\", 500)\n\t\t}\n\t\treturn p, nil\n\t}\n\n\tif proof.Key == \"code\" {\n\t\t// find key for given code\n\t\tstmt, err := DB.Prepare(\"SELECT key FROM Verification WHERE code = ? AND expire > datetime('now')\")\n\t\tif err != nil {\n\t\t\treturn p, NewError(\"Not found\", 404)\n\t\t}\n\t\trow := stmt.QueryRow(proof.Value)\n\t\tvar key string\n\t\tif err = row.Scan(&key); err != nil {\n\t\t\tif err == sql.ErrNoRows {\n\t\t\t\tstmt.Close()\n\t\t\t\tp.Key = \"email\"\n\t\t\t\tp.Value = \"\"\n\t\t\t\treturn p, NewError(\"Not found\", 404)\n\t\t\t}\n\t\t\tstmt.Close()\n\t\t\treturn p, err\n\t\t}\n\t\tstmt.Close()\n\n\t\t// cleanup current attempt so that it isn't used for malicious purpose\n\t\tif stmt, err = DB.Prepare(\"DELETE FROM Verification WHERE code = ?\"); err == nil {\n\t\t\tstmt.Exec(proof.Value)\n\t\t\tstmt.Close()\n\t\t}\n\t\tp.Key = \"email\"\n\t\tp.Value = strings.TrimPrefix(key, \"email::\")\n\t}\n\n\treturn p, nil\n}\n\nfunc ShareProofVerifierPassword(hashed string, given string) (string, bool) {\n\tif err := bcrypt.CompareHashAndPassword([]byte(hashed), []byte(given)); err != nil {\n\t\treturn \"\", false\n\t}\n\treturn hashed, true\n}\nfunc ShareProofVerifierEmail(users string, wanted string) (string, bool) {\n\ts := strings.Split(users, \",\")\n\tuser := \"\"\n\tfor _, possibleUser := range s {\n\t\tpossibleUser := strings.Trim(possibleUser, \" \")\n\t\tif wanted == possibleUser {\n\t\t\tuser = possibleUser\n\t\t\tbreak\n\t\t} else if possibleUser[0:1] == \"*\" {\n\t\t\tif strings.HasSuffix(wanted, strings.TrimPrefix(possibleUser, \"*\")) {\n\t\t\t\tuser = possibleUser\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tif user == \"\" {\n\t\treturn \"\", false\n\t}\n\treturn user, true\n}\n\nfunc ShareProofGetAlreadyVerified(req *http.Request) []Proof {\n\tvar p []Proof\n\tvar cookieValue string\n\n\tc, _ := req.Cookie(COOKIE_NAME_PROOF)\n\tif c == nil {\n\t\treturn p\n\t}\n\tcookieValue = c.Value\n\tif len(cookieValue) > 500 {\n\t\treturn p\n\t}\n\tj, err := DecryptString(SECRET_KEY_DERIVATE_FOR_PROOF, cookieValue)\n\tif err != nil {\n\t\treturn p\n\t}\n\t_ = json.Unmarshal([]byte(j), &p)\n\treturn p\n}\n\nfunc ShareProofGetRequired(s Share) []Proof {\n\tvar p []Proof\n\tif s.Password != nil {\n\t\tp = append(p, Proof{Key: \"password\", Value: *s.Password})\n\t}\n\tif s.Users != nil {\n\t\tp = append(p, Proof{Key: \"email\", Value: *s.Users})\n\t}\n\treturn p\n}\n\nfunc ShareProofCalculateRemainings(ref []Proof, mem []Proof) []Proof {\n\tvar remainingProof []Proof\n\n\tfor i := 0; i < len(ref); i++ {\n\t\tkeep := true\n\t\tfor j := 0; j < len(mem); j++ {\n\t\t\tif shareProofAreEquivalent(ref[i], mem[j]) {\n\t\t\t\tkeep = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif keep {\n\t\t\tremainingProof = append(remainingProof, ref[i])\n\t\t}\n\t}\n\n\treturn remainingProof\n}\n\nfunc shareProofAreEquivalent(ref Proof, p Proof) bool {\n\tif ref.Key != p.Key {\n\t\treturn false\n\t} else if ref.Value != \"\" && ref.Value == p.Value {\n\t\treturn true\n\t}\n\tfor _, chunk := range strings.Split(ref.Value, \",\") {\n\t\tchunk = strings.Trim(chunk, \" \")\n\t\tif p.Id == Hash(ref.Key+\"::\"+chunk, 20) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc TmplEmailVerification() string {\n\treturn `\n<!doctype html>\n<html>\n  <head>\n    <meta name=\"viewport\" content=\"width=device-width\" />\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n    <title>Verification code</title>\n    <style>\n      /* -------------------------------------\n          GLOBAL RESETS\n      ------------------------------------- */\n      img {\n        border: none;\n        -ms-interpolation-mode: bicubic;\n        max-width: 100%; }\n      body {\n        background-color: #f6f6f6;\n        font-family: sans-serif;\n        -webkit-font-smoothing: antialiased;\n        font-size: 14px;\n        line-height: 1.4;\n        margin: 0;\n        padding: 0;\n        -ms-text-size-adjust: 100%;\n        -webkit-text-size-adjust: 100%; }\n      table {\n        border-collapse: separate;\n        mso-table-lspace: 0pt;\n        mso-table-rspace: 0pt;\n        width: 100%; }\n        table td {\n          font-family: sans-serif;\n          font-size: 14px;\n          vertical-align: top; }\n      /* -------------------------------------\n          BODY & CONTAINER\n      ------------------------------------- */\n      .body {\n        background-color: #f6f6f6;\n        width: 100%; }\n      /* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */\n      .container {\n        display: block;\n        Margin: 0 auto !important;\n        /* makes it centered */\n        max-width: 450px;\n        padding: 10px;\n        width: 580px; }\n      /* This should also be a block element, so that it will fill 100% of the .container */\n      .content {\n        box-sizing: border-box;\n        display: block;\n        Margin: 0 auto;\n        max-width: 450px;\n        padding: 10px; }\n      /* -------------------------------------\n          HEADER, FOOTER, MAIN\n      ------------------------------------- */\n      .main {\n        background: #ffffff;\n        border-radius: 3px;\n        width: 100%; }\n      .wrapper {\n        box-sizing: border-box;\n        padding: 20px; }\n      .content-block {\n        padding-bottom: 10px;\n        padding-top: 10px;\n      }\n      .footer {\n        clear: both;\n        Margin-top: 10px;\n        text-align: center;\n        width: 100%; }\n        .footer td,\n        .footer p,\n        .footer span,\n        .footer a {\n          color: #999999;\n          font-size: 12px;\n          text-align: center; }\n      /* -------------------------------------\n          TYPOGRAPHY\n      ------------------------------------- */\n      h1,\n      h2,\n      h3,\n      h4 {\n        color: #000000;\n        font-family: sans-serif;\n        font-weight: 400;\n        line-height: 1.4;\n        margin: 0;\n        margin-bottom: 30px; }\n      h1 {\n        font-size: 35px;\n        font-weight: 300;\n        text-align: center;\n        text-transform: capitalize; }\n      p,\n      ul,\n      ol {\n        font-family: sans-serif;\n        font-size: 14px;\n        font-weight: normal;\n        margin: 0;\n        margin-bottom: 15px; }\n        p li,\n        ul li,\n        ol li {\n          list-style-position: inside;\n          margin-left: 5px; }\n      a {\n        color: #3498db;\n        text-decoration: underline; }\n      /* -------------------------------------\n          BUTTONS\n      ------------------------------------- */\n      .btn {\n        box-sizing: border-box;\n        width: 100%; }\n        .btn > tbody > tr > td {\n          padding-bottom: 15px; }\n        .btn table {\n          width: auto; }\n        .btn table td {\n          background-color: #ffffff;\n          border-radius: 5px;\n          text-align: center; }\n        .btn a {\n          background-color: #ffffff;\n          border: solid 1px #3498db;\n          border-radius: 5px;\n          box-sizing: border-box;\n          color: #3498db;\n          cursor: pointer;\n          display: inline-block;\n          font-size: 14px;\n          font-weight: bold;\n          margin: 0;\n          padding: 12px 25px;\n          text-decoration: none;\n          text-transform: capitalize; }\n      .btn-primary table td {\n        background-color: #3498db; }\n      .btn-primary a {\n        background-color: #3498db;\n        border-color: #3498db;\n        color: #ffffff; }\n      /* -------------------------------------\n          OTHER STYLES THAT MIGHT BE USEFUL\n      ------------------------------------- */\n      .last {\n        margin-bottom: 0; }\n      .first {\n        margin-top: 0; }\n      .align-center {\n        text-align: center; }\n      .align-right {\n        text-align: right; }\n      .align-left {\n        text-align: left; }\n      .clear {\n        clear: both; }\n      .mt0 {\n        margin-top: 0; }\n      .mb0 {\n        margin-bottom: 0; }\n      .preheader {\n        color: transparent;\n        display: none;\n        height: 0;\n        max-height: 0;\n        max-width: 0;\n        opacity: 0;\n        overflow: hidden;\n        mso-hide: all;\n        visibility: hidden;\n        width: 0; }\n      .powered-by a {\n        text-decoration: none; }\n      hr {\n        border: 0;\n        border-bottom: 1px solid #f6f6f6;\n        Margin: 20px 0; }\n      /* -------------------------------------\n          RESPONSIVE AND MOBILE FRIENDLY STYLES\n      ------------------------------------- */\n      @media only screen and (max-width: 490px) {\n        table[class=body] h1 {\n          font-size: 28px !important;\n          margin-bottom: 10px !important; }\n        table[class=body] p,\n        table[class=body] ul,\n        table[class=body] ol,\n        table[class=body] td,\n        table[class=body] span,\n        table[class=body] a {\n          font-size: 16px !important; }\n        table[class=body] .wrapper,\n        table[class=body] .article {\n          padding: 10px !important; }\n        table[class=body] .content {\n          padding: 0 !important; }\n        table[class=body] .container {\n          padding: 0 !important;\n          width: 100% !important; }\n        table[class=body] .main {\n          border-left-width: 0 !important;\n          border-radius: 0 !important;\n          border-right-width: 0 !important; }\n        table[class=body] .btn table {\n          width: 100% !important; }\n        table[class=body] .btn a {\n          width: 100% !important; }\n        table[class=body] .img-responsive {\n          height: auto !important;\n          max-width: 100% !important;\n          width: auto !important; }}\n      /* -------------------------------------\n          PRESERVE THESE STYLES IN THE HEAD\n      ------------------------------------- */\n      @media all {\n        .ExternalClass {\n          width: 100%; }\n        .ExternalClass,\n        .ExternalClass p,\n        .ExternalClass span,\n        .ExternalClass font,\n        .ExternalClass td,\n        .ExternalClass div {\n          line-height: 100%; }\n        .apple-link a {\n          color: inherit !important;\n          font-family: inherit !important;\n          font-size: inherit !important;\n          font-weight: inherit !important;\n          line-height: inherit !important;\n          text-decoration: none !important; }\n        .btn-primary table td:hover {\n          background-color: #34495e !important; }\n        .btn-primary a:hover {\n          background-color: #34495e !important;\n          border-color: #34495e !important; } }\n    </style>\n  </head>\n  <body class=\"\">\n    <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" class=\"body\">\n      <tr>\n        <td>&nbsp;</td>\n        <td class=\"container\">\n          <div class=\"content\">\n\n            <!-- START CENTERED WHITE CONTAINER -->\n            <span class=\"preheader\">Your code to login</span>\n            <table class=\"main\">\n\n              <!-- START MAIN CONTENT AREA -->\n              <tr>\n                <td class=\"wrapper\">\n                  <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n                    <tr>\n                      <td>\n                        <h2 style=\"font-weight:100;margin:0\">Your verification code is: <strong>{{.Code}}</strong></h2>\n                      </td>\n                    </tr>\n                  </table>\n                  <div style=\"margin-top:10px;font-style:italic;font-size:0.9em;\">When mounted as a network drive, you can authenticate as: {{.Username}}</div>\n                </td>\n              </tr>\n\n            <!-- END MAIN CONTENT AREA -->\n            </table>\n\n            <!-- START FOOTER -->\n            <div class=\"footer\">\n              <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n                <tr>\n                  <td class=\"content-block powered-by\">\n                    ` + WhiteLabelText(`Powered by <a href=\"https://www.filestash.app\">Filestash</a>.`, APPNAME) + `\n                  </td>\n                </tr>\n              </table>\n            </div>\n            <!-- END FOOTER -->\n\n          <!-- END CENTERED WHITE CONTAINER -->\n          </div>\n        </td>\n        <td>&nbsp;</td>\n      </tr>\n    </table>\n  </body>\n</html>\n`\n}\n\nfunc networkDriveUsernameEnc(email string) string {\n\treturn email + \"[\" + Hash(email+SECRET_KEY_DERIVATE_FOR_HASH, 10) + \"]\"\n}\n"
  },
  {
    "path": "server/model/webdav.go",
    "content": "package model\n\n/*\n * Implementation of a webdav.FileSystem: https://godoc.org/golang.org/x/net/webdav#FileSystem that is used\n * to generate our webdav server.\n * A lot of memoization is happening so that we don't DDOS the underlying storage which was important\n * considering most webdav client within OS are extremely greedy in HTTP request\n */\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/net/webdav\"\n)\n\nvar webdav_cache AppCache\n\nfunc init() {\n\twebdav_cache = NewQuickCache(20, 10)\n\twebdav_cache.OnEvict(func(filename string, _ interface{}) {\n\t\tos.Remove(filename)\n\t})\n}\n\ntype WebdavFs struct {\n\treq        *http.Request\n\tbackend    IBackend\n\tpath       string\n\tid         string\n\tchroot     string\n\twebdavFile *WebdavFile\n}\n\nfunc NewWebdavFs(b IBackend, primaryKey string, chroot string, req *http.Request) *WebdavFs {\n\treturn &WebdavFs{\n\t\tbackend: b,\n\t\tid:      primaryKey,\n\t\tchroot:  chroot,\n\t\treq:     req,\n\t}\n}\n\nfunc (this WebdavFs) Mkdir(ctx context.Context, name string, perm os.FileMode) error {\n\tif name = this.fullpath(name); name == \"\" {\n\t\treturn os.ErrNotExist\n\t}\n\treturn this.backend.Mkdir(name)\n}\n\nfunc (this *WebdavFs) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {\n\tcachePath := filepath.Join(GetAbsolutePath(TMP_PATH), \"webdav_\"+Hash(this.id+name, 20))\n\tfwriteFile := func() *os.File {\n\t\tif this.req.Method == \"PUT\" {\n\t\t\tf, err := os.OpenFile(cachePath+\"_writer\", os.O_WRONLY|os.O_CREATE|os.O_EXCL, os.ModePerm)\n\t\t\tif err != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn f\n\t\t}\n\t\treturn nil\n\t}\n\tif this.webdavFile != nil {\n\t\tthis.webdavFile.fwrite = fwriteFile()\n\t\treturn this.webdavFile, nil\n\t}\n\tif name = this.fullpath(name); name == \"\" {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tthis.webdavFile = &WebdavFile{\n\t\tpath:    name,\n\t\tbackend: this.backend,\n\t\tcache:   cachePath,\n\t\tfwrite:  fwriteFile(),\n\t}\n\treturn this.webdavFile, nil\n}\n\nfunc (this WebdavFs) RemoveAll(ctx context.Context, name string) error {\n\tif name = this.fullpath(name); name == \"\" {\n\t\treturn os.ErrNotExist\n\t}\n\treturn this.backend.Rm(name)\n}\n\nfunc (this WebdavFs) Rename(ctx context.Context, oldName, newName string) error {\n\tif oldName = this.fullpath(oldName); oldName == \"\" {\n\t\treturn os.ErrNotExist\n\t} else if newName = this.fullpath(newName); newName == \"\" {\n\t\treturn os.ErrNotExist\n\t}\n\treturn this.backend.Mv(oldName, newName)\n}\n\nfunc (this *WebdavFs) Stat(ctx context.Context, name string) (os.FileInfo, error) {\n\tif this.webdavFile != nil {\n\t\tthis.webdavFile.push_to_remote_if_needed()\n\t\treturn this.webdavFile.Stat()\n\t}\n\tfullname := this.fullpath(name)\n\tif isMicrosoftWebDAVClient(this.req) && this.req.Method == \"PROPFIND\" {\n\t\tif name == \"\" {\n\t\t\tfullname = this.chroot\n\t\t}\n\t\tfullname = EnforceDirectory(fullname)\n\t}\n\tif fullname == \"\" {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tthis.webdavFile = &WebdavFile{\n\t\tpath:    fullname,\n\t\tbackend: this.backend,\n\t\tcache:   filepath.Join(GetAbsolutePath(TMP_PATH), \"webdav_\"+Hash(this.id+name, 20)),\n\t}\n\treturn this.webdavFile.Stat()\n}\n\nfunc (this WebdavFs) fullpath(path string) string {\n\tp := filepath.Join(this.chroot, path)\n\tif strings.HasSuffix(path, \"/\") == true && strings.HasSuffix(p, \"/\") == false {\n\t\tp += \"/\"\n\t}\n\tif strings.HasPrefix(p, this.chroot) == false {\n\t\treturn \"\"\n\t}\n\treturn p\n}\n\n/*\n * Implement a webdav.File and os.Stat : https://godoc.org/golang.org/x/net/webdav#File\n */\ntype WebdavFile struct {\n\tpath    string\n\tbackend IBackend\n\tcache   string\n\tfread   *os.File\n\tfwrite  *os.File\n\tfiles   []os.FileInfo\n}\n\nfunc (this *WebdavFile) Read(p []byte) (n int, err error) {\n\tif strings.HasPrefix(filepath.Base(this.path), \".\") {\n\t\treturn 0, os.ErrNotExist\n\t}\n\tif this.fread == nil {\n\t\tif this.fread = this.pull_remote_file(); this.fread == nil {\n\t\t\treturn -1, os.ErrInvalid\n\t\t}\n\t}\n\treturn this.fread.Read(p)\n}\n\nfunc (this *WebdavFile) Close() error {\n\tif this.fread != nil {\n\t\tif this.fread.Close() == nil {\n\t\t\tthis.fread = nil\n\t\t}\n\t}\n\tif this.fwrite != nil {\n\t\tif err := this.push_to_remote_if_needed(); err == nil {\n\t\t\tif this.fwrite.Close() == nil {\n\t\t\t\tthis.fwrite = nil\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (this *WebdavFile) Seek(offset int64, whence int) (int64, error) {\n\tif this.fread == nil {\n\t\tthis.fread = this.pull_remote_file()\n\t\tif this.fread == nil {\n\t\t\treturn offset, ErrNotFound\n\t\t}\n\t}\n\ta, err := this.fread.Seek(offset, whence)\n\tif err != nil {\n\t\treturn a, ErrNotFound\n\t}\n\treturn a, nil\n}\n\nfunc (this *WebdavFile) Readdir(count int) ([]os.FileInfo, error) {\n\tif this.files != nil {\n\t\treturn this.files, nil\n\t}\n\tif strings.HasPrefix(filepath.Base(this.path), \".\") {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tf, err := this.backend.Ls(this.path)\n\tthis.files = f\n\treturn f, err\n}\n\nfunc (this *WebdavFile) Stat() (os.FileInfo, error) {\n\tthis.push_to_remote_if_needed()\n\tif strings.HasSuffix(this.path, \"/\") {\n\t\t_, err := this.Readdir(0)\n\t\tif err != nil {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn this, nil\n\t}\n\tbaseDir := filepath.Base(this.path)\n\tfiles, err := this.backend.Ls(strings.TrimSuffix(this.path, baseDir))\n\tif err != nil {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tfound := false\n\tfor i := range files {\n\t\tif files[i].Name() == baseDir {\n\t\t\tfound = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif found == false {\n\t\treturn nil, os.ErrNotExist\n\t}\n\treturn this, nil\n}\n\nfunc (this *WebdavFile) Write(p []byte) (int, error) {\n\tif this.fwrite == nil {\n\t\treturn 0, os.ErrNotExist\n\t}\n\tif strings.HasPrefix(filepath.Base(this.path), \".\") {\n\t\treturn 0, os.ErrNotExist\n\t}\n\treturn this.fwrite.Write(p)\n}\n\nfunc (this WebdavFile) pull_remote_file() *os.File {\n\tfilename := this.cache + \"_reader\"\n\tif f, err := os.OpenFile(filename, os.O_RDONLY, os.ModePerm); err == nil {\n\t\treturn f\n\t}\n\tif f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, os.ModePerm); err == nil {\n\t\tif reader, err := this.backend.Cat(this.path); err == nil {\n\t\t\tio.Copy(f, reader)\n\t\t\tf.Close()\n\t\t\twebdav_cache.SetKey(this.cache+\"_reader\", nil)\n\t\t\treader.Close()\n\t\t\tif f, err = os.OpenFile(filename, os.O_RDONLY, os.ModePerm); err == nil {\n\t\t\t\treturn f\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tf.Close()\n\t}\n\treturn nil\n}\n\nfunc (this *WebdavFile) push_to_remote_if_needed() error {\n\tif this.fwrite == nil {\n\t\treturn nil\n\t}\n\tthis.fwrite.Close()\n\tf, err := os.OpenFile(this.cache+\"_writer\", os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = this.backend.Save(this.path, f)\n\tif err == nil {\n\t\tif err = os.Rename(this.cache+\"_writer\", this.cache+\"_reader\"); err == nil {\n\t\t\tthis.fwrite = nil\n\t\t\twebdav_cache.SetKey(this.cache+\"_reader\", nil)\n\t\t}\n\t}\n\tf.Close()\n\treturn err\n}\n\nfunc (this WebdavFile) Name() string {\n\treturn filepath.Base(this.path)\n}\n\nfunc (this *WebdavFile) Size() int64 {\n\tif this.fread == nil {\n\t\tif this.fread = this.pull_remote_file(); this.fread == nil {\n\t\t\treturn 0\n\t\t}\n\t}\n\tif info, err := this.fread.Stat(); err == nil {\n\t\treturn info.Size()\n\t}\n\treturn 0\n}\n\nfunc (this WebdavFile) Mode() os.FileMode {\n\treturn 0\n}\n\nfunc (this WebdavFile) ModTime() time.Time {\n\treturn time.Now()\n}\nfunc (this WebdavFile) IsDir() bool {\n\tif strings.HasSuffix(this.path, \"/\") {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (this WebdavFile) Sys() interface{} {\n\treturn nil\n}\n\nfunc (this WebdavFile) ETag(ctx context.Context) (string, error) {\n\t// Building an etag can be an expensive call if the data isn't available locally.\n\t// => 2 etags strategies:\n\t// - use a legit etag value when the data is already in our cache\n\t// - use a dummy value that's changing all the time when we don't have much info\n\n\tetag := Hash(fmt.Sprintf(\"%d%s\", this.ModTime().UnixNano(), this.path), 20)\n\tif this.fread != nil {\n\t\tif s, err := this.fread.Stat(); err == nil {\n\t\t\tetag = Hash(fmt.Sprintf(`\"%x%x\"`, this.path, s.Size()), 20)\n\t\t}\n\t}\n\treturn etag, nil\n}\n\nvar lock webdav.LockSystem\n\nfunc NewWebdavLock() webdav.LockSystem {\n\tif lock == nil {\n\t\tlock = webdav.NewMemLS()\n\t}\n\treturn lock\n}\n\nfunc isMicrosoftWebDAVClient(req *http.Request) bool {\n\treturn strings.HasPrefix(req.Header.Get(\"User-Agent\"), \"Microsoft-WebDAV-MiniRedir/\")\n}\n"
  },
  {
    "path": "server/pkg/compress/cgo.go",
    "content": "//go:build cgo && linux\n\npackage compress\n\nimport (\n\t\"github.com/google/brotli/go/cbrotli\"\n)\n\nfunc Gzip(content []byte, quality int) []byte {\n\treturn []byte{}\n}\n\nfunc Br(content []byte, quality int) []byte {\n\tout, _ := cbrotli.Encode(content, cbrotli.WriterOptions{Quality: quality})\n\treturn out\n}\n"
  },
  {
    "path": "server/pkg/compress/nocgo.go",
    "content": "//go:build !cgo || windows\n\npackage compress\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n)\n\nfunc Gzip(content []byte, quality int) []byte {\n\tvar buf bytes.Buffer\n\tgz, err := gzip.NewWriterLevel(&buf, quality)\n\tif err != nil {\n\t\tgz = gzip.NewWriter(&buf)\n\t}\n\t_, _ = gz.Write(content)\n\tgz.Close()\n\treturn buf.Bytes()\n}\n\nfunc Br(content []byte, quality int) []byte {\n\treturn []byte(\"\")\n}\n"
  },
  {
    "path": "server/pkg/index.go",
    "content": "package core\n\nimport (\n\t_ \"github.com/mickael-kerjean/filestash/server/pkg/sqlite\"\n)\n"
  },
  {
    "path": "server/pkg/sqlite/cgo.go",
    "content": "//go:build cgo\n\npackage sqlite\n\nimport (\n\t\"errors\"\n\n\t\"github.com/mattn/go-sqlite3\"\n)\n\nfunc IsConstraint(err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\tvar sqliteErr sqlite3.Error\n\tif !errors.As(err, &sqliteErr) {\n\t\treturn false\n\t}\n\tif sqliteErr.Code == sqlite3.ErrConstraint {\n\t\treturn true\n\t}\n\tswitch sqliteErr.ExtendedCode {\n\tcase sqlite3.ErrConstraintPrimaryKey,\n\t\tsqlite3.ErrConstraintUnique,\n\t\tsqlite3.ErrConstraintForeignKey,\n\t\tsqlite3.ErrConstraintCheck,\n\t\tsqlite3.ErrConstraintNotNull:\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "server/pkg/sqlite/nocgo.go",
    "content": "//go:build !cgo\n\npackage sqlite\n\nimport (\n\t\"errors\"\n\t\"database/sql\"\n\n\tmodernc \"modernc.org/sqlite\"\n)\n\nfunc init() {\n\tsql.Register(\"sqlite3\", &modernc.Driver{})\n}\n\nfunc IsConstraint(err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\tvar sqliteErr *modernc.Error\n\tif !errors.As(err, &sqliteErr) {\n\t\treturn false\n\t}\n\tcode := sqliteErr.Code()\n\treturn code == 19 || (code >= 1550 && code <= 1599)\n}\n"
  },
  {
    "path": "server/pkg/workflow/action.go",
    "content": "package workflow\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t_ \"github.com/mickael-kerjean/filestash/server/pkg/workflow/actions\"\n\t. \"github.com/mickael-kerjean/filestash/server/pkg/workflow/model\"\n)\n\nfunc ExecuteAction(action Step, input map[string]string) (map[string]string, error) {\n\tcurrAction, err := findAction(action.Name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn currAction.Execute(action.Params, input)\n}\n\nfunc findAction(action string) (IAction, error) {\n\tfor _, currAction := range Hooks.Get.WorkflowActions() {\n\t\tif currAction.Manifest().Name == action {\n\t\t\treturn currAction, nil\n\t\t}\n\t}\n\treturn nil, ErrNotFound\n}\n"
  },
  {
    "path": "server/pkg/workflow/actions/notify_email.go",
    "content": "package actions\n\nimport (\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"gopkg.in/gomail.v2\"\n)\n\nfunc init() {\n\tHooks.Register.WorkflowAction(&ActionNotifyEmail{})\n}\n\ntype ActionNotifyEmail struct{}\n\nfunc (this *ActionNotifyEmail) Manifest() WorkflowSpecs {\n\treturn WorkflowSpecs{\n\t\tName:  \"notify/email\",\n\t\tTitle: \"Notify\",\n\t\tIcon:  `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><path d=\"M112 128C85.5 128 64 149.5 64 176C64 191.1 71.1 205.3 83.2 214.4L291.2 370.4C308.3 383.2 331.7 383.2 348.8 370.4L556.8 214.4C568.9 205.3 576 191.1 576 176C576 149.5 554.5 128 528 128L112 128zM64 260L64 448C64 483.3 92.7 512 128 512L512 512C547.3 512 576 483.3 576 448L576 260L377.6 408.8C343.5 434.4 296.5 434.4 262.4 408.8L64 260z\"></path></svg>`,\n\t\tSpecs: Form{\n\t\t\tElmnts: []FormElement{\n\t\t\t\t{\n\t\t\t\t\tName: \"email\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName: \"subject\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName: \"message\",\n\t\t\t\t\tType: \"long_text\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this *ActionNotifyEmail) Execute(params map[string]string, input map[string]string) (map[string]string, error) {\n\temail := struct {\n\t\tHostname string\n\t\tPort     int\n\t\tUsername string\n\t\tPassword string\n\t\tFrom     string\n\t\tTo       string\n\t\tSubject  string\n\t\tMessage  string\n\t}{\n\t\tHostname: Config.Get(\"email.server\").String(),\n\t\tPort:     Config.Get(\"email.port\").Int(),\n\t\tUsername: Config.Get(\"email.username\").String(),\n\t\tPassword: Config.Get(\"email.password\").String(),\n\t\tFrom:     Config.Get(\"email.from\").String(),\n\t\tTo:       Render(params[\"email\"], input),\n\t\tSubject:  Render(params[\"subject\"], input),\n\t\tMessage:  Render(params[\"message\"], input),\n\t}\n\tm := gomail.NewMessage()\n\tm.SetHeader(\"From\", email.From)\n\tm.SetHeader(\"To\", email.To)\n\tm.SetHeader(\"Subject\", email.Subject)\n\tm.SetBody(\"text/html\", strings.ReplaceAll(email.Message, \"\\n\", \"<br>\"))\n\tmail := gomail.NewDialer(email.Hostname, email.Port, email.Username, email.Password)\n\treturn input, mail.DialAndSend(m)\n}\n"
  },
  {
    "path": "server/pkg/workflow/actions/run_api.go",
    "content": "package actions\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"slices\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.WorkflowAction(&RunApi{})\n}\n\ntype RunApi struct{}\n\nfunc (this *RunApi) Manifest() WorkflowSpecs {\n\treturn WorkflowSpecs{\n\t\tName:  \"run/api\",\n\t\tTitle: \"Make API Call\",\n\t\tIcon:  `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><path d=\"M96 160C96 124.7 124.7 96 160 96L480 96C515.3 96 544 124.7 544 160L544 480C544 515.3 515.3 544 480 544L160 544C124.7 544 96 515.3 96 480L96 160zM240 164C215.7 164 196 183.7 196 208L196 256C196 280.3 215.7 300 240 300L272 300C296.3 300 316 280.3 316 256L316 208C316 183.7 296.3 164 272 164L240 164zM236 208C236 205.8 237.8 204 240 204L272 204C274.2 204 276 205.8 276 208L276 256C276 258.2 274.2 260 272 260L240 260C237.8 260 236 258.2 236 256L236 208zM376 164C365 164 356 173 356 184C356 193.7 362.9 201.7 372 203.6L372 280C372 291 381 300 392 300C403 300 412 291 412 280L412 184C412 173 403 164 392 164L376 164zM228 360C228 369.7 234.9 377.7 244 379.6L244 456C244 467 253 476 264 476C275 476 284 467 284 456L284 360C284 349 275 340 264 340L248 340C237 340 228 349 228 360zM324 384L324 432C324 456.3 343.7 476 368 476L400 476C424.3 476 444 456.3 444 432L444 384C444 359.7 424.3 340 400 340L368 340C343.7 340 324 359.7 324 384zM368 380L400 380C402.2 380 404 381.8 404 384L404 432C404 434.2 402.2 436 400 436L368 436C365.8 436 364 434.2 364 432L364 384C364 381.8 365.8 380 368 380z\"></path></svg>`,\n\t\tSpecs: Form{\n\t\t\tElmnts: []FormElement{\n\t\t\t\t{\n\t\t\t\t\tName: \"url\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName: \"method\",\n\t\t\t\t\tType: \"select\",\n\t\t\t\t\tOpts: []string{\"POST\", \"PUT\", \"GET\", \"PATCH\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName: \"headers\",\n\t\t\t\t\tType: \"long_text\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName: \"body\",\n\t\t\t\t\tType: \"long_text\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this *RunApi) Execute(params map[string]string, input map[string]string) (map[string]string, error) {\n\treq, err := http.NewRequest(params[\"method\"], Render(params[\"url\"], input), bytes.NewBufferString(Render(params[\"body\"], input)))\n\tif err != nil {\n\t\treturn input, err\n\t} else if params[\"headers\"] != \"\" {\n\t\tfor _, header := range strings.Split(Render(params[\"headers\"], input), \"\\n\") {\n\t\t\tif parts := strings.SplitN(strings.TrimSpace(header), \":\", 2); len(parts) == 2 {\n\t\t\t\treq.Header.Add(\n\t\t\t\t\tstrings.TrimSpace(parts[0]),\n\t\t\t\t\tstrings.TrimSpace(parts[1]),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\tif params[\"body\"] != \"\" && slices.Contains([]string{\"POST\", \"PUT\", \"PATCH\"}, params[\"method\"]) && req.Header.Get(\"Content-Type\") == \"\" {\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t}\n\tresp, err := HTTP.Do(req)\n\tif err != nil {\n\t\treturn input, err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\treturn input, NewError(fmt.Sprintf(\"received status code is %d\", resp.StatusCode), resp.StatusCode)\n\t}\n\tresponseBody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn input, err\n\t}\n\toutput := make(map[string]string)\n\tfor k, v := range input {\n\t\toutput[k] = v\n\t}\n\toutput[\"http::status\"] = string(resp.StatusCode)\n\toutput[\"http::response\"] = string(responseBody)\n\treturn output, nil\n}\n"
  },
  {
    "path": "server/pkg/workflow/actions/tools_debug.go",
    "content": "package actions\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.WorkflowAction(&ToolsDebug{})\n}\n\ntype ToolsDebug struct{}\n\nfunc (this *ToolsDebug) Manifest() WorkflowSpecs {\n\treturn WorkflowSpecs{\n\t\tName:  \"tools/debug\",\n\t\tTitle: \"Debug\",\n\t\tIcon:  `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><path d=\"M102.8 57.3C108.2 51.9 116.6 51.1 123 55.3L241.9 134.5C250.8 140.4 256.1 150.4 256.1 161.1L256.1 210.7L346.9 301.5C380.2 286.5 420.8 292.6 448.1 320L574.2 446.1C592.9 464.8 592.9 495.2 574.2 514L514.1 574.1C495.4 592.8 465 592.8 446.2 574.1L320.1 448C292.7 420.6 286.6 380.1 301.6 346.8L210.8 256L161.2 256C150.5 256 140.5 250.7 134.6 241.8L55.4 122.9C51.2 116.6 52 108.1 57.4 102.7L102.8 57.3zM247.8 360.8C241.5 397.7 250.1 436.7 274 468L179.1 563C151 591.1 105.4 591.1 77.3 563C49.2 534.9 49.2 489.3 77.3 461.2L212.7 325.7L247.9 360.8zM416.1 64C436.2 64 455.5 67.7 473.2 74.5C483.2 78.3 485 91 477.5 98.6L420.8 155.3C417.8 158.3 416.1 162.4 416.1 166.6L416.1 208C416.1 216.8 423.3 224 432.1 224L473.5 224C477.7 224 481.8 222.3 484.8 219.3L541.5 162.6C549.1 155.1 561.8 156.9 565.6 166.9C572.4 184.6 576.1 203.9 576.1 224C576.1 267.2 558.9 306.3 531.1 335.1L482 286C448.9 253 403.5 240.3 360.9 247.6L304.1 190.8L304.1 161.1L303.9 156.1C303.1 143.7 299.5 131.8 293.4 121.2C322.8 86.2 366.8 64 416.1 63.9z\"></path></svg>`,\n\t\tSpecs: Form{},\n\t}\n}\n\nfunc (this *ToolsDebug) Execute(params map[string]string, input map[string]string) (map[string]string, error) {\n\tLog.Info(\"[workflow] action=tools/debug input=%v\", input)\n\treturn input, nil\n}\n"
  },
  {
    "path": "server/pkg/workflow/actions/utils.go",
    "content": "package actions\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/ctrl\"\n)\n\nfunc Render(templateText string, variables map[string]string) string {\n\trendered, err := TmplExec(templateText, TmplParams(variables))\n\tif err != nil {\n\t\treturn templateText\n\t}\n\treturn rendered\n}\n"
  },
  {
    "path": "server/pkg/workflow/config.go",
    "content": "package workflow\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tPluginEnable()\n\t\tPluginNumberWorker()\n\t})\n}\n\nvar PluginEnable = func() bool {\n\treturn Config.Get(\"features.workflow.enable\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"enable\"\n\t\tf.Type = \"enable\"\n\t\tf.Target = []string{\"workflow_workers\"}\n\t\tf.Description = \"Enable/Disable workflows\"\n\t\tf.Default = true\n\t\treturn f\n\t}).Bool()\n}\n\nvar PluginNumberWorker = func() int {\n\treturn Config.Get(\"features.workflow.workers\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Id = \"workflow_workers\"\n\t\tf.Name = \"workers\"\n\t\tf.Type = \"number\"\n\t\tf.Description = \"Number of workers running in parallel. Default: 1\"\n\t\tf.Default = 1\n\t\treturn f\n\t}).Int()\n}\n"
  },
  {
    "path": "server/pkg/workflow/handler.go",
    "content": "package workflow\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/pkg/workflow/model\"\n\n\t\"github.com/gorilla/mux\"\n)\n\nfunc WorkflowAll(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tworkflows, err := AllWorkflows()\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\ttriggers := Hooks.Get.WorkflowTriggers()\n\ttm := make([]WorkflowSpecs, len(triggers))\n\tfor i, t := range triggers {\n\t\ttm[i] = t.Manifest()\n\t}\n\tactions := Hooks.Get.WorkflowActions()\n\tam := make([]WorkflowSpecs, len(actions))\n\tfor i, a := range actions {\n\t\tam[i] = a.Manifest()\n\t}\n\tSendSuccessResult(res, map[string]any{\n\t\t\"workflows\": workflows,\n\t\t\"triggers\":  tm,\n\t\t\"actions\":   am,\n\t})\n}\n\nfunc WorkflowUpsert(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tvar workflow Workflow\n\tif err := json.NewDecoder(req.Body).Decode(&workflow); err != nil {\n\t\tSendErrorResult(res, ErrInternal)\n\t\treturn\n\t}\n\tif err := UpsertWorkflow(workflow); err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tSendSuccessResult(res, nil)\n}\n\nfunc WorkflowGet(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tworkflowID := mux.Vars(req)[\"workflowID\"]\n\tif workflowID == \"\" {\n\t\tSendErrorResult(res, ErrNotValid)\n\t\treturn\n\t}\n\tworkflow, err := GetWorkflow(workflowID)\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tSendSuccessResult(res, workflow)\n}\n\nfunc WorkflowDelete(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tid := req.URL.Query().Get(\"id\")\n\tif id == \"\" {\n\t\tSendErrorResult(res, ErrNotValid)\n\t\treturn\n\t} else if err := DeleteWorkflow(id); err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tSendSuccessResult(res, nil)\n}\n"
  },
  {
    "path": "server/pkg/workflow/index.go",
    "content": "package workflow\n\nimport (\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/pkg/workflow/model\"\n)\n\nvar job_event = make(chan interface{}, 100)\n\nfunc Init() error {\n\tif err := InitState(); err != nil {\n\t\treturn err\n\t} else if PluginEnable() == false {\n\t\tLog.Debug(\"[workflow] state=disabled\")\n\t\treturn nil\n\t}\n\tLog.Debug(\"[workflow] state=enabled worker=%d\", PluginNumberWorker())\n\n\ttriggers := Hooks.Get.WorkflowTriggers()\n\tfor i := 0; i < len(triggers); i++ {\n\t\tt, err := triggers[i].Init()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgo func(t chan ITriggerEvent) {\n\t\t\tfor trigger := range t {\n\t\t\t\tif err := CreateJob(trigger.WorkflowID(), trigger.Input()); err != nil {\n\t\t\t\t\tLog.Error(\"[workflow] action=createJob err=%s\", err.Error())\n\t\t\t\t}\n\t\t\t\tselect {\n\t\t\t\tcase job_event <- nil:\n\t\t\t\tdefault:\n\t\t\t\t}\n\t\t\t}\n\t\t}(t)\n\t}\n\n\tfor i := 0; i < PluginNumberWorker(); i++ {\n\t\tgo func(i int) {\n\t\t\ttime.Sleep(time.Duration((i+1)*100) * time.Millisecond)\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase <-job_event:\n\t\t\t\tcase <-time.After(60 * time.Second):\n\t\t\t\t}\n\t\t\t\tjobID, workflow, input, err := NextJob()\n\t\t\t\tif err == ErrNotFound {\n\t\t\t\t\tcontinue\n\t\t\t\t} else if err != nil {\n\t\t\t\t\tLog.Error(\"[workflow] type=worker err=%s\", err.Error())\n\t\t\t\t\ttime.Sleep(10 * time.Second)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tExecuteJob(jobID, workflow, input)\n\t\t\t}\n\t\t}(i)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "server/pkg/workflow/job.go",
    "content": "package workflow\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/pkg/workflow/model\"\n)\n\nfunc ExecuteJob(jobID string, workflow Workflow, input map[string]string) {\n\tvar err error\n\tUpdateJob(jobID, \"RUNNING\", workflow.Actions, input)\n\tfor i := 0; i < len(workflow.Actions); i++ {\n\t\tif workflow.Actions[i].Done {\n\t\t\tcontinue\n\t\t}\n\t\tinput, err = ExecuteAction(workflow.Actions[i], input)\n\t\tworkflow.Actions[i].Done = true\n\t\tif err != nil {\n\t\t\tstatus := \"FAILURE\"\n\t\t\tworkflow.Actions[i].Done = false\n\t\t\tif input[\"status\"] == \"PENDING\" {\n\t\t\t\tstatus = \"PENDING\"\n\t\t\t}\n\t\t\tUpdateJob(jobID, status, workflow.Actions, input)\n\t\t\treturn\n\t\t}\n\t\tUpdateJob(jobID, \"RUNNING\", workflow.Actions, input)\n\t}\n\tUpdateJob(jobID, \"SUCCESS\", workflow.Actions, map[string]string{})\n\treturn\n}\n"
  },
  {
    "path": "server/pkg/workflow/model/block.go",
    "content": "package model\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\ntype StepDefinition struct {\n\tName     string                 `json:\"name\"`\n\tTitle    string                 `json:\"title\"`\n\tSubtitle string                 `json:\"subtitle\"`\n\tIcon     string                 `json:\"icon\"`\n\tSpecs    map[string]FormElement `json:\"specs\"`\n}\n"
  },
  {
    "path": "server/pkg/workflow/model/index.go",
    "content": "package model\n\nimport (\n\t\"database/sql\"\n\n\t\"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar db *sql.DB\n\nfunc InitState() (err error) {\n\tdb, err = sql.Open(\"sqlite3\", common.GetAbsolutePath(common.DB_PATH, \"workflow.sql\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdb.Exec(`\n\tCREATE TABLE IF NOT EXISTS workflows (\n\t\tid TEXT PRIMARY KEY,\n\t\tname TEXT NOT NULL,\n\t\tpublished BOOLEAN DEFAULT 0,\n\t\ttrigger TEXT NOT NULL, -- JSON encoded Step\n\t\tactions TEXT NOT NULL, -- JSON encoded []Step\n\t\tcreated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n\t\tupdated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n\t);\n\tCREATE INDEX IF NOT EXISTS idx_workflows_trigger_name ON workflows(json_extract(trigger, '$.name'));`)\n\n\tdb.Exec(`\n\tCREATE TABLE IF NOT EXISTS jobs (\n\t\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\t\trelated_workflow TEXT NOT NULL,\n\t\tstatus TEXT CHECK(status IN ('READY', 'PENDING', 'CLAIMED', 'RUNNING', 'SUCCESS', 'FAILURE')) DEFAULT 'READY',\n\t\tsteps TEXT NOT NULL,\n\t\tinput TEXT NOT NULL,\n\t\tcreated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n\t\tupdated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n\t\tFOREIGN KEY (related_workflow) REFERENCES workflows(id)\n\t);\n\tCREATE INDEX IF NOT EXISTS idx_jobs_status ON jobs(status);\n\tCREATE INDEX IF NOT EXISTS idx_jobs_workflow ON jobs(related_workflow, created_at DESC);`)\n\n\tdb.Exec(`\n\tUPDATE jobs\n\t\tSET status = 'READY', updated_at = CURRENT_TIMESTAMP\n\t\tWHERE status IN ('RUNNING', 'CLAIMED')`)\n\n\treturn nil\n}\n"
  },
  {
    "path": "server/pkg/workflow/model/job.go",
    "content": "package model\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\ntype Job struct {\n\tID              int    `json:\"id\"`\n\tRelatedWorkflow string `json:\"related_workflow\"`\n\tStatus          string `json:\"status\"`\n\tSteps           []Step `json:\"steps\"`\n\tCreatedAt       string `json:\"created_at\"`\n\tUpdatedAt       string `json:\"updated_at\"`\n}\n\nfunc CreateJob(workflowID string, input map[string]string) error {\n\tworkflow, err := GetWorkflow(workflowID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tstepsJSON, err := json.Marshal(workflow.Actions)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinputJSON, err := json.Marshal(input)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttx, err := db.Begin()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer tx.Rollback()\n\tif _, err = tx.Exec(`\n\t\tINSERT INTO jobs (related_workflow, status, steps, input)\n\t\t\tVALUES (?, 'READY', ?, ?)\n\t`, workflowID, string(stepsJSON), string(inputJSON)); err != nil {\n\t\treturn err\n\t}\n\tif _, err = tx.Exec(`DELETE FROM jobs WHERE related_workflow = ? AND id NOT IN (\n\t\tSELECT id FROM jobs\n\t\t\tWHERE related_workflow = ?\n\t\t\tORDER BY created_at DESC\n\t\t\tLIMIT 1000\n\t)`, workflowID, workflowID); err != nil {\n\t\treturn err\n\t}\n\treturn tx.Commit()\n}\n\nfunc NextJob() (string, Workflow, map[string]string, error) {\n\ttx, err := db.Begin()\n\tif err != nil {\n\t\treturn \"\", Workflow{}, nil, err\n\t}\n\tdefer tx.Rollback()\n\tquery := `\n\tSELECT id, related_workflow, steps, input\n\t\tFROM jobs\n\t\tWHERE status = 'READY'\n\t\tORDER BY updated_at ASC\n\t\tLIMIT 1`\n\tvar (\n\t\tjobID      string\n\t\tworkflowID string\n\t\tstepsJSON  string\n\t\tinputJSON  string\n\t)\n\tif err = tx.QueryRow(query).Scan(&jobID, &workflowID, &stepsJSON, &inputJSON); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn \"\", Workflow{}, nil, ErrNotFound\n\t\t}\n\t\treturn \"\", Workflow{}, nil, err\n\t}\n\tif _, err = tx.Exec(`UPDATE jobs SET status = 'CLAIMED', updated_at = CURRENT_TIMESTAMP WHERE id = ?`, jobID); err != nil {\n\t\treturn \"\", Workflow{}, nil, err\n\t} else if err = tx.Commit(); err != nil {\n\t\treturn \"\", Workflow{}, nil, err\n\t}\n\tworkflow, err := GetWorkflow(workflowID)\n\tif err != nil {\n\t\treturn \"\", Workflow{}, nil, err\n\t}\n\tvar input map[string]string\n\tif err = json.Unmarshal([]byte(inputJSON), &input); err != nil {\n\t\treturn \"\", Workflow{}, nil, err\n\t}\n\tinput[\"jobID\"] = jobID\n\tinput[\"workflowID\"] = workflow.ID\n\tinput[\"trigger\"] = workflow.Trigger.Name\n\treturn jobID, workflow, input, nil\n}\n\nfunc UpdateJob(jobID string, status string, steps []Step, input map[string]string) {\n\tstepsJSON, err := json.Marshal(steps)\n\tif err != nil {\n\t\tLog.Error(\"[workflow] from=job on=updateJob step=marshal err=%s\", err.Error())\n\t\treturn\n\t}\n\tinputJSON, err := json.Marshal(input)\n\tif err != nil {\n\t\tLog.Error(\"[workflow] from=job on=updateJob step=marshal err=%s\", err.Error())\n\t\treturn\n\t}\n\tquery := `\n\tUPDATE jobs\n\t\tSET status = ?, steps = ?, input = ?, updated_at = CURRENT_TIMESTAMP\n\t\tWHERE id = ?\n\t`\n\tif _, err := db.Exec(query, status, string(stepsJSON), string(inputJSON), jobID); err != nil {\n\t\tLog.Error(\"[workflow] from=job on=updateJob err=%s\", err.Error())\n\t}\n}\n"
  },
  {
    "path": "server/pkg/workflow/model/workflow.go",
    "content": "package model\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\ntype Workflow struct {\n\tID        string `json:\"id\"`\n\tName      string `json:\"name\"`\n\tPublished bool   `json:\"published\"`\n\tTrigger   Step   `json:\"trigger\"`\n\tActions   []Step `json:\"actions\"`\n\tUpdatedAt string `json:\"updated_at\"`\n\tCreatedAt string `json:\"created_at\"`\n\tHistory   []any  `json:\"history\"`\n}\n\ntype Step struct {\n\tName   string            `json:\"name\"`\n\tParams map[string]string `json:\"params\",omitzero`\n\tDone   bool              `json:\"done,omitempty\"`\n}\n\nfunc FindWorkflows(triggerName string) ([]Workflow, error) {\n\trows, err := db.Query(`\n\t\tSELECT w.id, w.name, w.published, w.trigger, w.actions, w.created_at, w.updated_at\n\t\t\tFROM workflows w\n\t\t\tWHERE json_extract(w.trigger, '$.name') = ?\n\t\t\tORDER BY w.created_at DESC\n\t`, triggerName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\n\tvar workflows = []Workflow{}\n\tfor rows.Next() {\n\t\tvar w Workflow\n\t\tvar triggerJSON, actionsJSON string\n\t\tif err := rows.Scan(&w.ID, &w.Name, &w.Published, &triggerJSON, &actionsJSON, &w.CreatedAt, &w.UpdatedAt); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err := json.Unmarshal([]byte(triggerJSON), &w.Trigger); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err := json.Unmarshal([]byte(actionsJSON), &w.Actions); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tworkflows = append(workflows, w)\n\t}\n\treturn workflows, rows.Err()\n}\n\nfunc AllWorkflows() ([]Workflow, error) {\n\trows, err := db.Query(`\n\t\tSELECT id, name, published, trigger, actions, created_at, updated_at\n\t\t\tFROM workflows\n\t\t\tORDER BY created_at DESC\n\t`)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\n\tvar workflows = []Workflow{}\n\tfor rows.Next() {\n\t\tvar w Workflow\n\t\tvar triggerJSON, actionsJSON string\n\t\terr := rows.Scan(&w.ID, &w.Name, &w.Published, &triggerJSON, &actionsJSON, &w.CreatedAt, &w.UpdatedAt)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err := json.Unmarshal([]byte(triggerJSON), &w.Trigger); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err := json.Unmarshal([]byte(actionsJSON), &w.Actions); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tworkflows = append(workflows, w)\n\t}\n\treturn workflows, rows.Err()\n}\n\nfunc UpsertWorkflow(workflow Workflow) error {\n\ttriggerJSON, err := json.Marshal(workflow.Trigger)\n\tif err != nil {\n\t\treturn err\n\t}\n\tactionsJSON, err := json.Marshal(workflow.Actions)\n\tif err != nil {\n\t\treturn err\n\t}\n\tquery := `\n\tINSERT OR REPLACE INTO workflows (id, name, published, trigger, actions, updated_at)\n\tVALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)`\n\t_, err = db.Exec(query, workflow.ID, workflow.Name, workflow.Published, string(triggerJSON), string(actionsJSON))\n\treturn err\n}\n\nfunc GetWorkflow(id string) (Workflow, error) {\n\tquery := `\n\tSELECT w.id, w.name, w.published, w.trigger, w.actions, w.created_at, w.updated_at,\n\t       (SELECT COALESCE(JSON_GROUP_ARRAY(JSON_OBJECT(\n\t           'id', j.id, 'status', j.status, 'created_at', j.created_at, 'steps', j.steps\n\t       )), JSON_ARRAY()) FROM (\n\t           SELECT * FROM jobs j\n\t           WHERE j.related_workflow = w.id\n\t           ORDER BY j.created_at DESC\n\t           LIMIT 3000\n\t       ) j) as history\n\tFROM workflows w\n\tWHERE w.id = ?`\n\trow := db.QueryRow(query, id)\n\n\tvar w Workflow\n\tvar triggerJSON, actionsJSON, historyJSON string\n\tif err := row.Scan(&w.ID, &w.Name, &w.Published, &triggerJSON, &actionsJSON, &w.CreatedAt, &w.UpdatedAt, &historyJSON); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn Workflow{}, ErrNotFound\n\t\t}\n\t\treturn Workflow{}, err\n\t}\n\tif err := json.Unmarshal([]byte(triggerJSON), &w.Trigger); err != nil {\n\t\treturn Workflow{}, err\n\t}\n\tif err := json.Unmarshal([]byte(actionsJSON), &w.Actions); err != nil {\n\t\treturn Workflow{}, err\n\t}\n\tif err := json.Unmarshal([]byte(historyJSON), &w.History); err != nil {\n\t\treturn Workflow{}, err\n\t}\n\treturn w, nil\n}\n\nfunc DeleteWorkflow(id string) error {\n\tresult, err := db.Exec(`DELETE FROM workflows WHERE id = ?`, id)\n\tif err != nil {\n\t\treturn err\n\t}\n\trowsAffected, err := result.RowsAffected()\n\tif err != nil {\n\t\treturn err\n\t} else if rowsAffected == 0 {\n\t\treturn ErrNotFound\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "server/pkg/workflow/trigger/fileaction.go",
    "content": "package trigger\n\nimport (\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/pkg/workflow/model\"\n)\n\nvar (\n\tfileaction_event = make(chan ITriggerEvent, 1)\n\tfileaction_name  = \"event\"\n)\n\nfunc init() {\n\tHooks.Register.WorkflowTrigger(&FileEventTrigger{})\n}\n\ntype hookAuthorisation struct{}\n\nfunc (this hookAuthorisation) Ls(ctx *App, path string) error {\n\tprocessFileAction(ctx, map[string]string{\"event\": \"ls\", \"path\": path})\n\treturn nil\n}\n\nfunc (this hookAuthorisation) Cat(ctx *App, path string) error {\n\tprocessFileAction(ctx, map[string]string{\"event\": \"cat\", \"path\": path})\n\treturn nil\n}\n\nfunc (this hookAuthorisation) Mkdir(ctx *App, path string) error {\n\tprocessFileAction(ctx, map[string]string{\"event\": \"mkdir\", \"path\": path})\n\treturn nil\n}\n\nfunc (this hookAuthorisation) Rm(ctx *App, path string) error {\n\tprocessFileAction(ctx, map[string]string{\"event\": \"rm\", \"path\": path})\n\treturn nil\n}\n\nfunc (this hookAuthorisation) Mv(ctx *App, from string, to string) error {\n\tprocessFileAction(ctx, map[string]string{\"event\": \"mv\", \"path\": from + \", \" + to})\n\treturn nil\n}\n\nfunc (this hookAuthorisation) Save(ctx *App, path string) error {\n\tprocessFileAction(ctx, map[string]string{\"event\": \"stat\", \"path\": path})\n\treturn nil\n}\n\nfunc (this hookAuthorisation) Stat(ctx *App, path string) error {\n\tprocessFileAction(ctx, map[string]string{\"event\": \"save\", \"path\": path})\n\treturn nil\n}\n\nfunc (this hookAuthorisation) Touch(ctx *App, path string) error {\n\tprocessFileAction(ctx, map[string]string{\"event\": \"touch\", \"path\": path})\n\treturn nil\n}\n\ntype FileEventTrigger struct{}\n\nfunc (this *FileEventTrigger) Manifest() WorkflowSpecs {\n\treturn WorkflowSpecs{\n\t\tName:  fileaction_name,\n\t\tTitle: \"When Something Happen\",\n\t\tIcon:  `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><path d=\"M192 64C156.7 64 128 92.7 128 128L128 368L310.1 368L279.1 337C269.7 327.6 269.7 312.4 279.1 303.1C288.5 293.8 303.7 293.7 313 303.1L385 375.1C394.4 384.5 394.4 399.7 385 409L313 481C303.6 490.4 288.4 490.4 279.1 481C269.8 471.6 269.7 456.4 279.1 447.1L310.1 416.1L128 416.1L128 512.1C128 547.4 156.7 576.1 192 576.1L448 576.1C483.3 576.1 512 547.4 512 512.1L512 234.6C512 217.6 505.3 201.3 493.3 189.3L386.7 82.7C374.7 70.7 358.5 64 341.5 64L192 64zM453.5 240L360 240C346.7 240 336 229.3 336 216L336 122.5L453.5 240z\"/></svg>`,\n\t\tSpecs: Form{\n\t\t\tElmnts: []FormElement{\n\t\t\t\t{\n\t\t\t\t\tName:       \"event\",\n\t\t\t\t\tType:       \"text\",\n\t\t\t\t\tDatalist:   []string{\"ls\", \"cat\", \"mkdir\", \"mv\", \"rm\", \"touch\", \"save\", \"stat\"},\n\t\t\t\t\tMultiValue: true,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName: \"path\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tOrder: 3,\n\t}\n}\n\nfunc (this *FileEventTrigger) Init() (chan ITriggerEvent, error) {\n\tHooks.Register.AuthorisationMiddleware(hookAuthorisation{})\n\treturn fileaction_event, nil\n}\n\nfunc processFileAction(ctx *App, params map[string]string) {\n\tif ctx.Context.Value(\"AUDIT\") == false {\n\t\treturn\n\t}\n\tif err := TriggerEvents(fileaction_event, fileaction_name, fileactionCallback(params)); err != nil {\n\t\tLog.Error(\"[workflow] trigger=event step=triggerEvents err=%s\", err.Error())\n\t}\n}\n\nfunc fileactionCallback(out map[string]string) func(Workflow) (map[string]string, bool) {\n\treturn func(w Workflow) (map[string]string, bool) {\n\t\tif !matchEvent(w.Trigger.Params[\"event\"], out[\"event\"]) {\n\t\t\treturn out, false\n\t\t} else if !matchPath(w.Trigger.Params[\"path\"], out[\"path\"]) {\n\t\t\treturn out, false\n\t\t}\n\t\treturn out, true\n\t}\n}\n\nfunc matchEvent(paramValue string, eventValue string) bool {\n\tif paramValue == \"\" {\n\t\treturn true\n\t}\n\tfor _, pvalue := range strings.Split(paramValue, \",\") {\n\t\tif strings.TrimSpace(pvalue) == eventValue {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc matchPath(paramValue string, eventValue string) bool {\n\tif paramValue == \"\" {\n\t\treturn true\n\t}\n\tfor _, epath := range strings.Split(eventValue, \",\") {\n\t\tif GlobMatch(paramValue, strings.TrimSpace(epath)) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "server/pkg/workflow/trigger/filewatch.go",
    "content": "package trigger\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/pkg/workflow/model\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n)\n\nvar (\n\tfilewatch_event = make(chan ITriggerEvent, 1)\n\tfilewatch_name  = \"watch\"\n\tfilewatch_state sync.Map\n)\n\nfunc init() {\n\tHooks.Register.WorkflowTrigger(&WatchTrigger{})\n}\n\ntype WatchTrigger struct{}\n\nfunc (this *WatchTrigger) Manifest() WorkflowSpecs {\n\treturn WorkflowSpecs{\n\t\tName:  \"watch\",\n\t\tTitle: \"When the Filesystem Changes\",\n\t\tIcon:  `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><path d=\"M128 64C92.7 64 64 92.7 64 128L64 512C64 547.3 92.7 576 128 576L308 576C285.3 544.5 272 505.8 272 464C272 363.4 349.4 280.8 448 272.7L448 234.6C448 217.6 441.3 201.3 429.3 189.3L322.7 82.7C310.7 70.7 294.5 64 277.5 64L128 64zM389.5 240L296 240C282.7 240 272 229.3 272 216L272 122.5L389.5 240zM464 608C543.5 608 608 543.5 608 464C608 384.5 543.5 320 464 320C384.5 320 320 384.5 320 464C320 543.5 384.5 608 464 608zM480 400L480 448L528 448C536.8 448 544 455.2 544 464C544 472.8 536.8 480 528 480L480 480L480 528C480 536.8 472.8 544 464 544C455.2 544 448 536.8 448 528L448 480L400 480C391.2 480 384 472.8 384 464C384 455.2 391.2 448 400 448L448 448L448 400C448 391.2 455.2 384 464 384C472.8 384 480 391.2 480 400z\"/></svg>`,\n\t\tSpecs: Form{\n\t\t\tElmnts: []FormElement{\n\t\t\t\t{\n\t\t\t\t\tName: \"token\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName: \"path\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tOrder: 4,\n\t}\n}\n\nfunc (this *WatchTrigger) Init() (chan ITriggerEvent, error) {\n\tgo func() {\n\t\tfor {\n\t\t\tif err := TriggerEvents(filewatch_event, filewatch_name, filewatchCallback); err != nil {\n\t\t\t\tLog.Error(\"[workflow] trigger=watch step=triggerEvents err=%s\", err.Error())\n\t\t\t}\n\t\t\ttime.Sleep(10 * time.Second)\n\t\t}\n\t}()\n\treturn filewatch_event, nil\n}\n\nfunc filewatchCallback(workflow Workflow) (map[string]string, bool) {\n\tpath := workflow.Trigger.Params[\"path\"]\n\tout := map[string]string{\"path\": path}\n\tbackend, session, err := createBackend(workflow.Trigger.Params[\"token\"])\n\tif err != nil {\n\t\tLog.Error(\"[workflow] trigger=filewatch step=callback::init err=%s\", err.Error())\n\t\treturn out, false\n\t}\n\tfiles, err := backend.Ls(path)\n\tif err != nil {\n\t\tLog.Error(\"[workflow] trigger=filewatch step=callback::ls err=%s\", err.Error())\n\t\treturn out, false\n\t}\n\tkey := GenerateID(session) + path\n\tfincache, exists := filewatch_state.Load(key)\n\tif !exists {\n\t\tfilewatch_state.Store(key, files)\n\t\treturn out, false\n\t}\n\tprevFiles := fincache.([]os.FileInfo)\n\tif len(files) != len(prevFiles) {\n\t\tfilewatch_state.Store(key, files)\n\t\treturn out, true\n\t}\n\tchanges := []string{}\n\tfor i := 0; i < len(files); i++ {\n\t\thasChange := false\n\t\tif files[i].Name() != prevFiles[i].Name() {\n\t\t\thasChange = true\n\t\t} else if files[i].Size() != prevFiles[i].Size() {\n\t\t\thasChange = true\n\t\t} else if files[i].ModTime() != prevFiles[i].ModTime() {\n\t\t\thasChange = true\n\t\t}\n\t\tif hasChange {\n\t\t\tp := JoinPath(path, files[i].Name())\n\t\t\tif files[i].IsDir() {\n\t\t\t\tp = EnforceDirectory(p)\n\t\t\t}\n\t\t\tchanges = append(changes, p)\n\t\t}\n\t}\n\tif len(changes) > 0 {\n\t\tfilewatch_state.Store(key, files)\n\t\treturn out, true\n\t}\n\treturn out, false\n}\n\nfunc createBackend(token string) (IBackend, map[string]string, error) {\n\tsession := map[string]string{}\n\tstr, err := DecryptString(SECRET_KEY_DERIVATE_FOR_USER, token)\n\tif err != nil {\n\t\treturn nil, session, err\n\t}\n\tif err = json.Unmarshal([]byte(str), &session); err != nil {\n\t\treturn nil, session, err\n\t}\n\tbackend, err := model.NewBackend(\n\t\t&App{Context: context.Background()},\n\t\tsession,\n\t)\n\treturn backend, session, err\n}\n"
  },
  {
    "path": "server/pkg/workflow/trigger/index.go",
    "content": "package trigger\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/pkg/workflow/model\"\n)\n\ntype TriggerEvent struct {\n\tID     string\n\tParams map[string]string\n}\n\nfunc (this TriggerEvent) Input() map[string]string {\n\treturn this.Params\n}\n\nfunc (this *TriggerEvent) WorkflowID() string {\n\treturn this.ID\n}\n\nfunc TriggerEvents(event chan ITriggerEvent, triggerID string, callback func(Workflow) (map[string]string, bool)) error {\n\tworkflows, err := FindWorkflows(triggerID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, workflow := range workflows {\n\t\tif !workflow.Published {\n\t\t\tcontinue\n\t\t}\n\t\tparams, emit := callback(workflow)\n\t\tif !emit {\n\t\t\tcontinue\n\t\t}\n\t\tselect {\n\t\tcase event <- &TriggerEvent{\n\t\t\tID:     workflow.ID,\n\t\t\tParams: params,\n\t\t}:\n\t\tdefault:\n\t\t\treturn NewError(\"Workflow is busy\", http.StatusServiceUnavailable)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc toJSON(val any) string {\n\tb, err := json.Marshal(val)\n\tif err != nil {\n\t\treturn \"{}\"\n\t}\n\treturn string(b)\n}\n"
  },
  {
    "path": "server/pkg/workflow/trigger/schedule.go",
    "content": "package trigger\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/pkg/workflow/model\"\n)\n\nvar (\n\tcron_name  = \"schedule\"\n\tcron_event = make(chan ITriggerEvent, 10)\n)\n\nfunc init() {\n\tHooks.Register.WorkflowTrigger(&ScheduleTrigger{})\n}\n\ntype ScheduleTrigger struct{}\n\nfunc (this *ScheduleTrigger) Manifest() WorkflowSpecs {\n\treturn WorkflowSpecs{\n\t\tName:     cron_name,\n\t\tTitle:    \"On a Schedule\",\n\t\tSubtitle: \"cron\",\n\t\tIcon:     `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><path d=\"M528 320C528 434.9 434.9 528 320 528C205.1 528 112 434.9 112 320C112 205.1 205.1 112 320 112C434.9 112 528 205.1 528 320zM64 320C64 461.4 178.6 576 320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320zM296 184L296 320C296 328 300 335.5 306.7 340L402.7 404C413.7 411.4 428.6 408.4 436 397.3C443.4 386.2 440.4 371.4 429.3 364L344 307.2L344 184C344 170.7 333.3 160 320 160C306.7 160 296 170.7 296 184z\"></path></svg>`,\n\t\tSpecs: Form{\n\t\t\tElmnts: []FormElement{\n\t\t\t\t{\n\t\t\t\t\tName:        \"cron\",\n\t\t\t\t\tType:        \"text\",\n\t\t\t\t\tPlaceholder: \"Default: @daily\",\n\t\t\t\t\tDefault:     \"@daily\",\n\t\t\t\t\tDatalist:    []string{\"@always\", \"@hourly\", \"@daily\", \"@weekly\", \"@monthly\", \"@yearly\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tOrder: 1,\n\t}\n}\n\nfunc (this *ScheduleTrigger) Init() (chan ITriggerEvent, error) {\n\tgo func() {\n\t\tfor {\n\t\t\tif err := TriggerEvents(cron_event, cron_name, scheduleCallback); err != nil {\n\t\t\t\tLog.Error(\"[workflow] trigger=schedule step=triggerEvents err=%s\", err.Error())\n\t\t\t}\n\t\t\ttime.Sleep(time.Until(time.Now().Truncate(time.Minute).Add(time.Minute)))\n\t\t}\n\t}()\n\treturn cron_event, nil\n}\n\nfunc scheduleCallback(workflow Workflow) (map[string]string, bool) {\n\texpr := workflow.Trigger.Params[\"cron\"]\n\tif v, ok := map[string]string{\n\t\t\"@yearly\":  \"0 0 1 1 *\",\n\t\t\"@monthly\": \"0 0 1 * *\",\n\t\t\"@weekly\":  \"0 0 * * 0\",\n\t\t\"@daily\":   \"0 0 * * *\",\n\t\t\"@hourly\":  \"0 * * * *\",\n\t\t\"@always\":  \"* * * * *\",\n\t}[expr]; ok {\n\t\texpr = v\n\t}\n\tfields := strings.Fields(expr)\n\tif len(fields) != 5 {\n\t\treturn map[string]string{\"cron\": expr}, false\n\t}\n\tt := time.Now()\n\treturn map[string]string{\"cron\": expr}, cronFieldMatch(fields[0], t.Minute(), 0) &&\n\t\tcronFieldMatch(fields[1], t.Hour(), 0) &&\n\t\tcronFieldMatch(fields[2], t.Day(), 1) &&\n\t\tcronFieldMatch(fields[3], int(t.Month()), 1) &&\n\t\tcronFieldMatch(fields[4], int(t.Weekday()), 0)\n}\n\nfunc cronFieldMatch(field string, value, min int) bool {\n\tfor _, part := range strings.Split(field, \",\") {\n\t\tstep := 1\n\t\tif i := strings.Index(part, \"/\"); i != -1 {\n\t\t\tstep, _ = strconv.Atoi(part[i+1:])\n\t\t\tpart = part[:i]\n\t\t}\n\t\tvar lo, hi int\n\t\tswitch {\n\t\tcase part == \"*\":\n\t\t\tlo, hi = min, value\n\t\tcase strings.Contains(part, \"-\"):\n\t\t\tlo, _ = strconv.Atoi(part[:strings.Index(part, \"-\")])\n\t\t\thi, _ = strconv.Atoi(part[strings.Index(part, \"-\")+1:])\n\t\tdefault:\n\t\t\tlo, _ = strconv.Atoi(part)\n\t\t\thi = lo\n\t\t}\n\t\tif value >= lo && value <= hi && (value-lo)%step == 0 && step > 0 {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "server/pkg/workflow/trigger/webhook.go",
    "content": "package trigger\n\nimport (\n\t\"bytes\"\n\t\"html/template\"\n\t\"net/http\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/pkg/workflow/model\"\n\n\t\"github.com/gorilla/mux\"\n)\n\nvar (\n\twebhook_event = make(chan ITriggerEvent, 5)\n\twebhook_name  = \"webhook\"\n\twebhookTmpl   = template.Must(template.New(\"webhook\").Parse(`\n\t\t<h1>Triggers</h1>\n\t\t<ul>\n\t\t\t{{range .}}<li><a href=\"?id={{.ID}}\">{{.Name}}</a></li>{{end}}\n\t\t</ul>\n\t\t<style>ul { list-style-type: none; padding: 0; }</style>\n\t`))\n)\n\nfunc init() {\n\tHooks.Register.WorkflowTrigger(&WebhookTrigger{})\n}\n\nfunc webhookCallback(r *http.Request, id string) func(w Workflow) (map[string]string, bool) {\n\treturn func(w Workflow) (map[string]string, bool) {\n\t\theaders := map[string]any{}\n\t\tfor k, v := range r.Header {\n\t\t\theaders[k] = strings.Join(v, \", \")\n\t\t}\n\t\tquery := map[string]any{}\n\t\tfor k, v := range r.URL.Query() {\n\t\t\tquery[k] = strings.Join(v, \", \")\n\t\t}\n\t\tout := map[string]string{\n\t\t\t\"method\":  r.Method,\n\t\t\t\"headers\": toJSON(headers),\n\t\t\t\"query\":   toJSON(query),\n\t\t}\n\t\tif id == \"\" {\n\t\t\treturn out, true\n\t\t}\n\t\treturn out, id == w.ID\n\t}\n}\n\ntype WebhookTrigger struct{}\n\nfunc (this *WebhookTrigger) Manifest() WorkflowSpecs {\n\treturn WorkflowSpecs{\n\t\tName:  webhook_name,\n\t\tTitle: \"From a WebHook\",\n\t\tIcon:  `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><path d=\"M392.8 65.2C375.8 60.3 358.1 70.2 353.2 87.2L225.2 535.2C220.3 552.2 230.2 569.9 247.2 574.8C264.2 579.7 281.9 569.8 286.8 552.8L414.8 104.8C419.7 87.8 409.8 70.1 392.8 65.2zM457.4 201.3C444.9 213.8 444.9 234.1 457.4 246.6L530.8 320L457.4 393.4C444.9 405.9 444.9 426.2 457.4 438.7C469.9 451.2 490.2 451.2 502.7 438.7L598.7 342.7C611.2 330.2 611.2 309.9 598.7 297.4L502.7 201.4C490.2 188.9 469.9 188.9 457.4 201.4zM182.7 201.3C170.2 188.8 149.9 188.8 137.4 201.3L41.4 297.3C28.9 309.8 28.9 330.1 41.4 342.6L137.4 438.6C149.9 451.1 170.2 451.1 182.7 438.6C195.2 426.1 195.2 405.8 182.7 393.3L109.3 320L182.6 246.6C195.1 234.1 195.1 213.8 182.6 201.3z\"/></svg>`,\n\t\tSpecs: Form{\n\t\t\tElmnts: []FormElement{\n\t\t\t\t{\n\t\t\t\t\tName:     \"url\",\n\t\t\t\t\tType:     \"text\",\n\t\t\t\t\tReadOnly: true,\n\t\t\t\t\tValue:    \"/api/workflow/webhook?web\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tOrder: 5,\n\t}\n}\n\nfunc (this *WebhookTrigger) Init() (chan ITriggerEvent, error) {\n\tHooks.Register.HttpEndpoint(func(r *mux.Router) error {\n\t\tr.HandleFunc(WithBase(\"/api/workflow/webhook\"), func(w http.ResponseWriter, r *http.Request) {\n\t\t\tif r.URL.Query().Has(\"web\") && strings.Contains(r.Header.Get(\"Accept\"), \"text/html\") {\n\t\t\t\tworkflows, err := FindWorkflows(webhook_name)\n\t\t\t\tif err != nil {\n\t\t\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tvar buf bytes.Buffer\n\t\t\t\tif err := webhookTmpl.Execute(&buf, workflows); err != nil {\n\t\t\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tw.Header().Set(\"Content-Type\", \"text/html\")\n\t\t\t\tw.Write([]byte(Page(buf.String())))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif err := TriggerEvents(webhook_event, webhook_name, webhookCallback(r, r.URL.Query().Get(\"id\"))); err != nil {\n\t\t\t\tSendErrorResult(w, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tSendSuccessResult(w, nil)\n\t\t}).Methods(\"GET\", \"POST\")\n\t\treturn nil\n\t})\n\treturn webhook_event, nil\n}\n"
  },
  {
    "path": "server/pkg/workflow/trigger.go",
    "content": "package workflow\n\nimport (\n\t_ \"github.com/mickael-kerjean/filestash/server/pkg/workflow/trigger\"\n)\n"
  },
  {
    "path": "server/plugin/index.go",
    "content": "package plugin\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_ldap\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_local\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_passthrough\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_wordpress\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_artifactory\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_backblaze\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_dav\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_dropbox\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_ftp\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_gdrive\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_git\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_ldap\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_local\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_mysql\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_nfs\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_nop\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_perkeep\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_psql\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_s3\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_samba\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_sftp\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_storj\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_tmp\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_url\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_webdav\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_editor_wopi\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_console\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_site\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_image_ascii\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_image_c\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_license\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_metadata_sqlite\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_stateless\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_security_scanner\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_security_svg\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_starter_http\"\n\t_ \"github.com/mickael-kerjean/filestash/server/plugin/plg_video_transcoder\"\n)\n\nfunc init() {\n\tHooks.Register.Onload(func() { Log.Debug(\"plugins loaded\") })\n}\n"
  },
  {
    "path": "server/plugin/plg_application_docxjs/Makefile",
    "content": "all:\n\tmake setup\n\tmake build\n\tmake install\n\nsetup:\n\t[ -d lib/vendor ] || mkdir -p lib/vendor\n\tcurl -L https://raw.githubusercontent.com/VolodymyrBaydalka/docxjs/refs/heads/master/dist/docx-preview.js > lib/vendor/docx-preview.js\n\tcurl -L https://unpkg.com/jszip/dist/jszip.min.js > lib/vendor/jszip.min.js\n\nbuild:\n\tzip -r application_docx.zip .\n\ninstall:\n\tmv application_docx.zip ../../../dist/data/state/plugins/\n"
  },
  {
    "path": "server/plugin/plg_application_docxjs/loader_docx.css",
    "content": ".component_docx {\n    width: 100%;\n}\n\n.component_docx .docx-wrapper {\n    background: var(--surface);\n}\n.component_docx .docx-wrapper > section.docx {\n    box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);\n    border-radius: 4px;\n}\n\n.component_filedownloader {\n    width: 100%;\n    margin: 0 auto;\n}\n"
  },
  {
    "path": "server/plugin/plg_application_docxjs/loader_docx.js",
    "content": "import { createElement } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { loadJS, loadCSS } from \"../../helpers/loader.js\";\nimport ctrlError from \"../../pages/ctrl_error.js\";\nimport { transition } from \"../../pages/viewerpage/common.js\";\nimport ctrlDownloader, { init as initDownloader } from \"../../pages/viewerpage/application_downloader.js\";\nimport { createLoader } from \"../../components/loader.js\";\n\nawait init();\n\n/*\n * This viewer application is for rendering a docx onto an HTML document. To get a smooth UI, the whole\n * flow is broken down to these steps:\n * 1) show a loading spinner => `... = createLoader($page);`\n * 2) fetch the docx file    => effect(ajax({ xxxx })).pipe(\n * 3) remove the spinner     =>    cancelLoader,\n * 4) render + transition    =>    ... renderDocx ...\n * 5) fallback on error      =>    ... catchError ... ctrlDownloader(...)\n *\n * note: you don't have to use rxjs if you don't like it, the same code could be written with plain\n *    promises instead. We just happen to be rxjs fanboys who enjoy the automatic resource cleanup it\n *    enforces. In this example, if the user navigates away before the docx is loaded, the ajax call\n *    is cancelled automatically. Additionally, we prefer thinking in terms of streams rather than\n *    the classical state management approach used by most frameworks.\n */\nexport default async function(render, { getDownloadUrl, getFilename, $menubar, acl$ }) {\n    const $page = createElement(`\n        <div class=\"component_docx\"></div>\n    `);\n    render($page);\n\n    const cancelLoader = createLoader($page);\n    effect(ajax({ url: getDownloadUrl(), responseType: \"arraybuffer\" }).pipe(\n        cancelLoader,\n        rxjs.mergeMap(async ({ response }) => renderDocx(response, $page)),\n        rxjs.catchError(() => ctrlDownloader(render, { acl$, getFilename, getDownloadUrl, hasMenubar: false })),\n    ));\n}\n\nasync function renderDocx(response, $page) {\n    $page.classList.add(\"hidden\");\n    await window.docx.renderAsync(response, $page)\n    $page.classList.remove(\"hidden\");\n    $page.parentElement.classList.add(\"scroll-y\");\n    transition($page);\n}\n\nfunction init() {\n    return Promise.all([\n        loadCSS(import.meta.url, \"./loader_docx.css\"),\n        initDownloader(),\n        loadJS(import.meta.url, \"./lib/vendor/jszip.min.js\").then(() => loadJS(import.meta.url, \"./lib/vendor/docx-preview.js\")),\n    ]);\n}\n"
  },
  {
    "path": "server/plugin/plg_application_docxjs/manifest.json",
    "content": "{\n    \"author\": \"Filestash Pty Ltd\",\n    \"version\": \"v0.0\",\n    \"modules\": [\n        {\n            \"type\": \"xdg-open\",\n            \"mime\": \"application/word\",\n            \"entrypoint\": \"loader_docx.js\",\n            \"application\": \"skeleton\"\n        }\n    ]\n}\n"
  },
  {
    "path": "server/plugin/plg_application_office/Makefile",
    "content": "all:\n\tmake build\n\tmake install\n\ninstall:\n\tzip -r application_office.zip . -x \"lib/vendor/*\"\n\tmv application_office.zip ../../../dist/data/state/plugins/\n\nbuild:\n\tclang --target=wasm32 -O3 -nostdlib -Wl,--no-entry -Wl,--export=middleware -o middleware.wasm middleware.c\n\tmake deps_zeta\n\ndeps_zeta:\n\t[ -d lib/lowa ] || mkdir lib/lowa\n\tcurl https://cdn.zetaoffice.net/zetaoffice_latest/soffice.js > lib/lowa/soffice.js\n\tcurl https://cdn.zetaoffice.net/zetaoffice_latest/soffice.wasm > lib/lowa/soffice.wasm.br\n\tcurl https://cdn.zetaoffice.net/zetaoffice_latest/soffice.data.js.metadata > lib/lowa/soffice.data.js.metadata\n\tcurl https://cdn.zetaoffice.net/zetaoffice_latest/soffice.data > lib/lowa/soffice.data.br\n\tcurl https://zetaoffice.net/demos/standalone/assets/vendor/zetajs/zeta.js > lib/lowa/zeta.js\n"
  },
  {
    "path": "server/plugin/plg_application_office/README.md",
    "content": "<figure>\n  <img src=\"https://github.com/user-attachments/assets/8baa32c0-c7be-4dd9-97b2-d0362a83f219\" />\n  <figcaption>Screenshot</figcaption>\n</figure>\n"
  },
  {
    "path": "server/plugin/plg_application_office/loader_lowa.css",
    "content": "@import url(\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/fontawesome.min.css\");\n\n.component_word {\n    display: flex;\n    flex: 1 1 auto;\n}\n.component_word canvas {\n    border: none;\n    outline: none;\n    height: 100%;\n    width: 100%;\n}\n.component_menubar .action-item .texteditor svg.component_icon {\n    width: 16px;\n    margin: 1px 0 0 2px;\n}\n.component_menubar .action-item .texteditor.active svg.component_icon {\n    background: rgba(255, 255, 255, 0.2);\n    border-radius: 5px;\n}\n\n.component_menubar .action-item .texteditor input[type=\"number\"] {\n    width: 15px;\n    color: var(--color);\n    border: 2px solid #f2f2f2;\n    background: #f2f2f2;\n    border-radius: 2px;\n    padding: 0 3px 0 3px;\n    margin: 0 3px;\n    -moz-appearance: textfield;\n}\n.component_menubar .action-item .texteditor input[type=\"number\"]::-webkit-outer-spin-button,\n.component_menubar .action-item .texteditor input[type=\"number\"]::-webkit-inner-spin-button {\n    -webkit-appearance: none;\n}\n\n.component_menubar .action-item .texteditor .fontawesome {\n    font-family: \"FontAwesome\";\n    color: var(--light);\n    background: inherit;\n    color: inherit;\n    border: none;\n}\n.component_menubar .action-item .texteditor select.fontawesome option {\n    background: var(--dark);\n}\n\n.component_menubar .action-item .texteditor.picker {\n    display: flex;\n    align-items: center;\n}\n.component_menubar .action-item .texteditor.picker input[type=\"color\"] {\n    padding: 0;\n    width: 0;\n    border: none;\n}\n.component_word + .component_loader {\n    position: absolute;\n    inset: 0;\n    margin-top: 150px;\n}\n"
  },
  {
    "path": "server/plugin/plg_application_office/loader_lowa.go",
    "content": "package plg_application_office\n\nimport (\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.Middleware(func(h HandlerFunc) HandlerFunc {\n\t\treturn func(app *App, res http.ResponseWriter, req *http.Request) {\n\t\t\thead := res.Header()\n\t\t\thead.Set(\"Cross-Origin-Opener-Policy\", \"same-origin\")\n\t\t\thead.Set(\"Cross-Origin-Embedder-Policy\", \"require-corp\")\n\t\t\th(app, res, req)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "server/plugin/plg_application_office/loader_lowa.js",
    "content": "import { createElement, onDestroy } from \"../../lib/skeleton/index.js\";\nimport rxjs, { effect } from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { qs } from \"../../lib/dom.js\";\nimport { join } from \"../../lib/path.js\";\nimport { loadJS, loadCSS } from \"../../helpers/loader.js\";\nimport { buttonDownload } from \"../../pages/viewerpage/component_menubar.js\";\nimport { $ICON } from \"../../pages/viewerpage/common_fab.js\";\nimport { save } from \"../../pages/viewerpage/model_files.js\";\nimport \"../../components/fab.js\";\n\nimport { $toolbar } from \"./lib/dom.js\";\n\nawait loadCSS(import.meta.url, \"./loader_lowa.css\");\n\nlet $canvas = null;\nwindow.Module = {\n    uno_scripts: [join(import.meta.url, \"./lib/lowa/zeta.js\"), join(import.meta.url, \"./loader_lowa.uno.js\")],\n    locateFile: (path, prefix) => (prefix || join(import.meta.url, \"./lib/lowa/\")) + path,\n};\n\nexport default async function(render, { mime, getDownloadUrl, getFilename, $menubar, acl$ }) {\n    const canWrite = (await acl$.toPromise()).indexOf(\"POST\") >= 0;\n    const $page = createElement(`\n        <div class=\"component_word\">\n            <canvas id=\"qtcanvas\" contenteditable=\"${canWrite}\" style=\"visibility:hidden\"></canvas>\n            <button is=\"component-fab\" class=\"hidden\"></button>\n        </div>\n    `);\n    render($page);\n\n    // feature1: init\n    const filename = getFilename();\n    const $fab = qs($page, `[is=\"component-fab\"]`);\n    const $qcanvas = qs($page, \"canvas\");\n    if ($canvas) {\n        $qcanvas.remove();\n        $page.appendChild($canvas);\n    } else {\n        $canvas = $qcanvas;\n    }\n    Object.assign($canvas.style, {\n        width: \"100%\",\n        height: \"100%\",\n    });\n\n    // feature2: toolbar init\n    if (canWrite) {\n        $menubar.add(buttonDownload(filename, getDownloadUrl()));\n        if (isWriter(mime)) {\n            $menubar.add($toolbar.bullet);\n            $menubar.add($toolbar.alignment);\n            $menubar.add($toolbar.title);\n        }\n        $menubar.add($toolbar.size);\n        $menubar.add($toolbar.strike);\n        $menubar.add($toolbar.underline);\n        $menubar.add($toolbar.italic);\n        $menubar.add($toolbar.bold);\n        $menubar.add($toolbar.color);\n    }\n\n    // feature3: setup lowa\n    window.Module.canvas = $canvas;\n    await loadJS(import.meta.url, \"./lib/lowa/soffice.js\");\n    let port = await Module.uno_main;\n    onDestroy(() => {\n        $canvas.style.visibility = \"hidden\";\n        port.postMessage({ cmd: \"destroy\", mime });\n    });\n\n    // feature4: display rule for save button\n    const action$ = new rxjs.Subject();\n    if (canWrite) effect(rxjs.merge(rxjs.fromEvent($canvas, \"keyup\"), action$).pipe(rxjs.tap(() => {\n        $fab.classList.remove(\"hidden\");\n        $fab.render($ICON.SAVING);\n        $fab.onclick = () => {\n            $fab.render($ICON.LOADING);\n            $fab.disabled = true;\n            port.postMessage({ cmd: \"save\" });\n        };\n    })));\n\n    // feature5: load file\n    await effect(ajax({ url: getDownloadUrl(), responseType: \"arraybuffer\" }).pipe(\n        rxjs.mergeMap(async ({ response }) => {\n            try { FS.mkdir(\"/tmp/office/\"); } catch {}\n            await FS.writeFile(\"/tmp/office/\" + filename , new Uint8Array(response));\n            await port.postMessage({ cmd: \"load\", filename, mime });\n            onDestroy(() => FS.unlink(\"/tmp/office/\" + filename));\n            $canvas.focus();\n        }),\n    ));\n    await new Promise((resolve) => {\n        port.onmessage = function(e) {\n            switch (e.data.cmd) {\n            case \"loaded\":\n                window.dispatchEvent(new Event(\"resize\"));\n                setTimeout(() => {\n                    resolve();\n                    $canvas.style.visibility = \"visible\";\n                }, 250);\n                break;\n            case \"save\":\n                const bytes = FS.readFile(\"/tmp/office/\" + filename);\n                effect(save(new Blob([bytes], {})).pipe(rxjs.tap(() => {\n                    $fab.classList.add(\"hidden\");\n                    $fab.render($ICON.SAVING);\n                    $fab.disabled = false;\n                })));\n                break;\n            case \"setFormat\":\n                switch(e.data.id) {\n                case \"Bold\":\n                    e.data.state ? $toolbar.bold.classList.add(\"active\") : $toolbar.bold.classList.remove(\"active\");\n                    break;\n                case \"Italic\":\n                    e.data.state ? $toolbar.italic.classList.add(\"active\") : $toolbar.italic.classList.remove(\"active\");\n                    break;\n                case \"Underline\":\n                    e.data.state ? $toolbar.underline.classList.add(\"active\") : $toolbar.underline.classList.remove(\"active\");\n                    break;\n                case \"Strikeout\":\n                    e.data.state ? $toolbar.strike.classList.add(\"active\") : $toolbar.strike.classList.remove(\"active\");\n                    break;\n                case \"LeftPara\":\n                    if (e.data.state) qs($toolbar.alignment, \"select\").value = \"left\";\n                    break;\n                case \"RightPara\":\n                    if (e.data.state) qs($toolbar.alignment, \"select\").value = \"right\";\n                    break;\n                case \"CenterPara\":\n                    if (e.data.state) qs($toolbar.alignment, \"select\").value = \"center\";\n                    break;\n                case \"JustifyPara\":\n                    if (e.data.state) qs($toolbar.alignment, \"select\").value = \"justify\";\n                    break;\n                case \"DefaultBullet\":\n                    qs($toolbar.bullet, \"select\").value = e.data.state ? \"ul\" : \"normal\";\n                    break;\n                case \"DefaultNumbering\":\n                    qs($toolbar.bullet, \"select\").value = e.data.state ? \"ol\" : \"normal\";\n                    break;\n                case \"StyleApply\":\n                    let value = \"normal\";\n                    if (e.data.state === \"Title\") value = \"title\";\n                    else if (e.data.state === \"Heading 1\") value = \"head1\";\n                    else if (e.data.state === \"Heading 2\") value = \"head2\";\n                    else if (e.data.state === \"Heading 3\") value = \"head3\";\n                    qs($toolbar.title, \"select\").value = value;\n                    break;\n                case \"Color\":\n                    const hex = e.data.state && e.data.state > 0 ? \"#\" + e.data.state.toString(16).padStart(6, \"0\") : \"#000000\";\n                    $toolbar.color.children[0].style.fill = hex;\n                    $toolbar.color.children[1].value = hex;\n                    break;\n                case \"FontHeight\":\n                    const fontSize = e.data.state;\n                    qs($toolbar.size, \"input\").value = fontSize;\n                    break;\n                default:\n                    console.log(\"format\", e);\n                    throw new Error(\"Unknown format\");\n                }\n                $canvas.focus();\n                break;\n            default:\n                console.log(\"message\", e);\n                throw new Error(\"Unknown message\");\n            }\n        };\n    });\n\n    // feature6: toolbar events\n    $toolbar.bold.onclick = () => {\n        $toolbar.bold.classList.toggle(\"active\");\n        action$.next();\n        port.postMessage({ cmd: \"toggleFormatting\", id: \"Bold\" });\n    };\n    $toolbar.italic.onclick = () => {\n        $toolbar.italic.classList.toggle(\"active\");\n        action$.next();\n        port.postMessage({ cmd: \"toggleFormatting\", id: \"Italic\" });\n    };\n    $toolbar.underline.onclick = () => {\n        $toolbar.underline.classList.toggle(\"active\");\n        action$.next();\n        port.postMessage({ cmd: \"toggleFormatting\", id: \"Underline\" });\n    };\n    $toolbar.bullet.onchange = (e) => {\n        switch(e.target.value) {\n        case \"normal\":\n            port.postMessage({ cmd: \"toggleFormatting\", id: \"RemoveBullets\" });\n            break;\n        case \"ul\":\n            port.postMessage({ cmd: \"toggleFormatting\", id: \"DefaultBullet\" });\n            break;\n        case \"ol\":\n            port.postMessage({ cmd: \"toggleFormatting\", id: \"DefaultNumbering\" });\n            break;\n        }\n        action$.next();\n    };\n    $toolbar.strike.onclick = () => {\n        $toolbar.strike.classList.toggle(\"active\");\n        action$.next();\n        port.postMessage({ cmd: \"toggleFormatting\", id: \"Strikeout\" });\n    };\n    $toolbar.alignment.onchange = (e) => {\n        switch(e.target.value) {\n        case \"left\":\n            port.postMessage({ cmd: \"toggleFormatting\", id: \"LeftPara\" });\n            break;\n        case \"right\":\n            port.postMessage({ cmd: \"toggleFormatting\", id: \"RightPara\" });\n            break;\n        case \"center\":\n            port.postMessage({ cmd: \"toggleFormatting\", id: \"CenterPara\" });\n            break;\n        case \"justify\":\n            port.postMessage({ cmd: \"toggleFormatting\", id: \"JustifyPara\" });\n            break;\n        default:\n            throw new Error(\"Unknown tool alignment\");\n        }\n        action$.next();\n    };\n    $toolbar.title.onchange = (e) => {\n        switch(e.target.value) {\n        case \"normal\":\n            port.postMessage({ cmd: \"toggleFormatting\", id: \"StyleApply?Style:string=Standard&FamilyName:string=ParagraphStyles\" });\n            break;\n        case \"title\":\n            port.postMessage({ cmd: \"toggleFormatting\", id: \"StyleApply?Style:string=Title&FamilyName:string=ParagraphStyles\" });\n            break;\n        case \"head1\":\n            port.postMessage({ cmd: \"toggleFormatting\", id: \"StyleApply?Style:string=Heading 1&FamilyName:string=ParagraphStyles\" });\n            break;\n        case \"head2\":\n            port.postMessage({ cmd: \"toggleFormatting\", id: \"StyleApply?Style:string=Heading 2&FamilyName:string=ParagraphStyles\" });\n            break;\n        case \"head3\":\n            port.postMessage({ cmd: \"toggleFormatting\", id: \"StyleApply?Style:string=Heading 3&FamilyName:string=ParagraphStyles\" });\n            break;\n        default:\n            throw new Error(\"Unknown text style\");\n        }\n        action$.next();\n    };\n    $toolbar.color.onclick = (e) => {\n        if (e.target.tagName === \"INPUT\") return;\n        const $svg = e.target.closest(\"svg\")\n        const $input = $svg.nextElementSibling;\n        $input.onchange = (e) => {\n            $svg.style.fill = e.target.value;\n            const color = parseInt(e.target.value.slice(1), 16);\n            port.postMessage({ cmd: \"toggleFormatting\", id: `Color?Color:long=${color}` })\n        };\n        $input.click();\n        action$.next();\n    };\n    effect(rxjs.fromEvent(qs($toolbar.size, \"input\"), \"keyup\").pipe(\n        rxjs.debounceTime(250),\n        rxjs.tap((e) => {\n            const fontSize = parseInt(e.target.value);\n            port.postMessage({ cmd: \"toggleFormatting\", id: `FontHeight?FontHeight.Height:float=${fontSize}` });\n            action$.next();\n        }),\n    ));\n\n    // feature7: workaround known lowa bug\n    // - when pressing escape, lowa goes out of fullscreen and show some unwanted stuff\n    // - context menu functions like \"replace\" image which does crash everything with errors generated from soffice.js\n    // - ctrl + s is broken\n    effect(rxjs.fromEvent($page, \"keydown\", { capture: true }).pipe(rxjs.tap((e) => {\n        if (e.key === \"Escape\") e.stopPropagation();\n        if (e.key === \"s\" && e.ctrlKey) e.stopPropagation();\n    })));\n    effect(rxjs.fromEvent($page, \"mousedown\", { capture: true }).pipe(rxjs.tap((e) => {\n        if (e.which === 3) e.stopPropagation();\n    })));\n}\n\nfunction isWriter(mime) {\n    return [\"application/word\", \"application/msword\", \"application/rtf\", \"application/vnd.oasis.opendocument.text\"].indexOf(mime) >= 0;\n}\n"
  },
  {
    "path": "server/plugin/plg_application_office/loader_lowa.uno.js",
    "content": "// reference:\n// - uno programming: https://www.youtube.com/watch?v=CzxLKG9CUvo\n// - dispatch commands: https://wiki.documentfoundation.org/Development/DispatchCommands\nModule.zetajs.then(function(zetajs) {\n    init({\n        css: zetajs.uno.com.sun.star,\n        zetajs,\n    });\n});\n\nfunction init({ zetajs, css }) {\n    const context = zetajs.getUnoComponentContext();\n    const desktop = css.frame.Desktop.create(context);\n    let ctrl, xModel;\n\n    // UI Element: remove toolbar in writer\n    const config = css.configuration.ReadWriteAccess.create(context, \"en-US\");\n    [\"Writer\", \"Calc\", \"Impress\"].forEach((app) => {\n        const uielems = config.getByHierarchicalName(`/org.openoffice.Office.UI.${app}WindowState/UIElements/States`);\n        for (const i of uielems.getElementNames()) {\n            const uielem = uielems.getByName(i);\n            if (uielem.getByName(\"Visible\")) uielem.setPropertyValue(\"Visible\", false);\n        }\n    });\n\n    // Theme & Colors\n    const elmnts = config.getByHierarchicalName(\"/org.openoffice.Office.UI/ColorScheme/ColorSchemes\");\n    for (const i of elmnts.getElementNames()) {\n        const colorScheme = elmnts.getByName(i);\n        // console.log(colorScheme.getElementNames());\n        colorScheme.getByName(\"AppBackground\").setPropertyValue(\"Color\", 16119285); // #f5f5f5\n        colorScheme.getByName(\"WriterPageBreaks\").setPropertyValue(\"Color\", 16119285); // #f5f5f5\n        colorScheme.getByName(\"WriterSectionBoundaries\").setPropertyValue(\"Color\", 16119285); // #f5f5f5\n        colorScheme.getByName(\"Shadow\").setPropertyValue(\"Color\", 16119285); // #f5f5f5\n        colorScheme.getByName(\"FontColor\").setPropertyValue(\"Color\", 2368548); // #242424\n        colorScheme.getByName(\"WriterHeaderFooterMark\").setPropertyValue(\"Color\", 16777215); // #ffffff\n    }\n    config.commitChanges();\n\n    zetajs.mainPort.onmessage = function(e) {\n        switch (e.data.cmd) {\n        case \"destroy\":\n            toggleTools({ mime: e.data.mime, css, ctrl, context });\n            xModel = null;\n            ctrl = null;\n            break;\n        case \"load\":\n            const { filename, mime } = e.data;\n            const in_path = `file:///tmp/office/${filename}`;\n            xModel = desktop.loadComponentFromURL(in_path, \"_default\", 0, []);\n            ctrl = xModel.getCurrentController();\n            ctrl.getFrame().LayoutManager.hideElement(\"private:resource/menubar/menubar\");\n            ctrl.getFrame().LayoutManager.hideElement(\"private:resource/statusbar/statusbar\");\n            ctrl.getFrame().getContainerWindow().FullScreen = true;\n            toggleTools({ mime, css, ctrl, context });\n            const commands = [ // ref: https://wiki.documentfoundation.org/Development/DispatchCommands\n                \"Bold\", \"Italic\", \"Underline\", \"Strikeout\", \"LeftPara\", \"RightPara\", \"CenterPara\",\n                \"JustifyPara\", \"Color\", \"FontHeight\", ...(isWriter(mime) ? [\"StyleApply\", \"DefaultBullet\", \"DefaultNumbering\"] : []),\n            ];\n            for (const id of commands) {\n                const urlObj = transformUrl(\".uno:\" + id, { css, context });\n                const listener = zetajs.unoObject([css.frame.XStatusListener], {\n                    disposing: function(source) {},\n                    statusChanged: function(state) {\n                        state = zetajs.fromAny(state.State);\n                        if (id === \"StyleApply\") state = state && state.StyleName || null;\n                        else if (id === \"Color\") state = typeof state === \"number\" ? state : null;\n                        else if (id === \"FontHeight\") state = state && state.Height || null;\n                        else if (typeof state !== \"boolean\") state = false;\n\n                        if (state === null) return;\n                        zetajs.mainPort.postMessage({ cmd: \"setFormat\", id, state });\n                    },\n                });\n                queryDispatch(urlObj, { ctrl }).addStatusListener(listener, urlObj);\n            }\n            zetajs.mainPort.postMessage({ cmd: \"loaded\" });\n            break;\n        case \"save\":\n            xModel.store();\n            zetajs.mainPort.postMessage({ cmd: \"save\" });\n            break;\n        case \"toggleFormatting\":\n            dispatch(\".uno:\" + e.data.id, { css, ctrl, context });\n            break;\n        default:\n            throw Error(\"Unknown message command: \" + e.data.cmd);\n        }\n    }\n}\n\nfunction transformUrl(unoUrl, { css, context }) {\n    const ioparam = {\n        val: new css.util.URL({\n            Complete: unoUrl\n        }),\n    };\n    css.util.URLTransformer.create(context).parseStrict(ioparam);\n    return ioparam.val;\n}\n\nfunction queryDispatch(urlObj, { ctrl }) {\n    return ctrl.queryDispatch(urlObj, \"_self\", 0);\n}\n\nfunction dispatch(unoUrl, { css, ctrl, context }) {\n    const urlObj = transformUrl(unoUrl, { css, context });\n    queryDispatch(urlObj, { ctrl }).dispatch(urlObj, []);\n}\n\nfunction toggleTools({ css, ctrl, context, mime }) {\n    dispatch(\".uno:Sidebar\", { css, ctrl, context });\n    if (isCalc(mime)) dispatch(\".uno:InputLineVisible\", { css, ctrl, context });\n    if (isWriter(mime)) dispatch(\".uno:Ruler\", { css, ctrl, context });\n}\n\nfunction isWriter(mime) {\n    return [\"application/word\", \"application/msword\", \"application/rtf\", \"application/vnd.oasis.opendocument.text\"].indexOf(mime) >= 0;\n}\n\nfunction isCalc(mime) {\n    return [\"application/excel\", \"application/vnd.ms-excel\", \"application/vnd.oasis.opendocument.spreadsheet\"].indexOf(mime) >= 0;\n}\n"
  },
  {
    "path": "server/plugin/plg_application_office/manifest.json",
    "content": "{\n    \"author\": \"Filestash Pty Ltd\",\n    \"version\": \"v0.0\",\n    \"modules\": [\n        {\n            \"type\": \"middleware\",\n            \"entrypoint\": \"middleware.wasm\"\n        },\n        {\n            \"type\": \"xdg-open\",\n            \"mime\": \"application/word\",\n            \"entrypoint\": \"loader_lowa.js\",\n            \"application\": \"skeleton\"\n        },\n        {\n            \"type\": \"xdg-open\",\n            \"mime\": \"application/msword\",\n            \"entrypoint\": \"loader_lowa.js\",\n            \"application\": \"skeleton\"\n        },\n        {\n            \"type\": \"xdg-open\",\n            \"mime\": \"application/rtf\",\n            \"entrypoint\": \"loader_lowa.js\",\n            \"application\": \"skeleton\"\n        },\n        {\n            \"type\": \"xdg-open\",\n            \"mime\": \"application/vnd.oasis.opendocument.text\",\n            \"entrypoint\": \"loader_lowa.js\",\n            \"application\": \"skeleton\"\n        },\n        {\n            \"type\": \"xdg-open\",\n            \"mime\": \"application/excel\",\n            \"entrypoint\": \"loader_lowa.js\",\n            \"application\": \"skeleton\"\n        },\n        {\n            \"type\": \"xdg-open\",\n            \"mime\": \"application/vnd.ms-excel\",\n            \"entrypoint\": \"loader_lowa.js\",\n            \"application\": \"skeleton\"\n        },\n        {\n            \"type\": \"xdg-open\",\n            \"mime\": \"application/vnd.oasis.opendocument.spreadsheet\",\n            \"entrypoint\": \"loader_lowa.js\",\n            \"application\": \"skeleton\"\n        },\n        {\n            \"type\": \"xdg-open\",\n            \"mime\": \"application/powerpoint\",\n            \"entrypoint\": \"loader_lowa.js\",\n            \"application\": \"skeleton\"\n        },\n        {\n            \"type\": \"xdg-open\",\n            \"mime\": \"application/vnd.ms-powerpoint\",\n            \"entrypoint\": \"loader_lowa.js\",\n            \"application\": \"skeleton\"\n        },\n        {\n            \"type\": \"xdg-open\",\n            \"mime\": \"application/vnd.oasis.opendocument.presentation\",\n            \"entrypoint\": \"loader_lowa.js\",\n            \"application\": \"skeleton\"\n        }\n    ]\n}\n"
  },
  {
    "path": "server/plugin/plg_application_office/middleware.c",
    "content": "//go:build ignore\n__attribute__((export_name(\"middleware\")))\nconst unsigned char* middleware(void) {\n    return (const unsigned char*)\n        \"HTTP/1.0 204 OK\\r\\n\"\n        \"Cross-Origin-Opener-Policy: same-origin\\r\\n\"\n        \"Cross-Origin-Embedder-Policy: require-corp\\r\\n\"\n        \"\\r\\n\"\n        \"\\0\";\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_admin/index.go",
    "content": "package plg_authenticate_admin\n\nimport (\n\t\"fmt\"\n\t\"html\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\nfunc init() {\n\tHooks.Register.AuthenticationMiddleware(\"admin\", Admin{})\n}\n\ntype Admin struct{}\n\nfunc (this Admin) Setup() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"admin\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:     \"password\",\n\t\t\t\tType:     \"text\",\n\t\t\t\tReadOnly: true,\n\t\t\t\tValue:    \"__YOUR_ADMIN_PASSWORD__\",\n\t\t\t\tDescription: `This plugin will redirect the user to a page asking for a password. Only the admin password will be considered valid.\nThis plugin exposes {{ .user }} (which is 'admin') and {{ .password }} for the attribute mapping section\n`,\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Admin) EntryPoint(idpParams map[string]string, req *http.Request, res http.ResponseWriter) error {\n\tgetFlash := func() string {\n\t\tc, err := req.Cookie(\"flash\")\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t\thttp.SetCookie(res, &http.Cookie{\n\t\t\tName:   \"flash\",\n\t\t\tMaxAge: -1,\n\t\t\tPath:   \"/\",\n\t\t})\n\t\treturn fmt.Sprintf(`<p class=\"flash\">%s</p>`, html.EscapeString(c.Value))\n\t}\n\tres.Header().Set(\"Content-Type\", \"text/html; charset=utf-8\")\n\tres.WriteHeader(http.StatusOK)\n\tres.Write([]byte(Page(`\n      <form method=\"post\">\n        <label>\n          <input type=\"password\" name=\"password\" value=\"\" placeholder=\"Admin Password\" />\n        </label>\n        <button>CONNECT</button>\n        ` + getFlash() + `\n        <style>\n          .flash{ color: #f26d6d; font-weight: bold; }\n          form { padding-top: 10vh; }\n        </style>\n      </form>`)))\n\treturn nil\n}\n\nfunc (this Admin) Callback(formData map[string]string, idpParams map[string]string, res http.ResponseWriter) (map[string]string, error) {\n\tif err := bcrypt.CompareHashAndPassword(\n\t\t[]byte(Config.Get(\"auth.admin\").String()),\n\t\t[]byte(formData[\"password\"]),\n\t); err != nil {\n\t\thttp.SetCookie(res, &http.Cookie{\n\t\t\tName:   \"flash\",\n\t\t\tValue:  \"Invalid password\",\n\t\t\tMaxAge: 1,\n\t\t\tPath:   \"/\",\n\t\t})\n\t\treturn nil, ErrAuthenticationFailed\n\t}\n\treturn map[string]string{\n\t\t\"user\":     \"admin\",\n\t\t\"password\": formData[\"password\"],\n\t}, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_htpasswd/deps/crypt/AUTHORS.md",
    "content": "### Initial author\n\n[Jeramey Crawford](https://github.com/jeramey)\n\n### Other authors\n\n[Jonas mg](https://github.com/tredoe)\n\n"
  },
  {
    "path": "server/plugin/plg_authenticate_htpasswd/deps/crypt/LICENSE",
    "content": "Copyright (c) 2012, Jeramey Crawford <jeramey@antihe.ro>\nCopyright (c) 2013, Jonas mg\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n  * Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n\n  * Redistributions in binary form must reproduce the above copyright\n    notice, this list of conditions and the following disclaimer in\n    the documentation and/or other materials provided with the\n    distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "server/plugin/plg_authenticate_htpasswd/deps/crypt/README.md",
    "content": "crypt\n=====\nA password hashing library.\n\nThe goal of crypt is to bring a library of many common and popular password\nhashing algorithms to Go and to provide a simple and consistent interface to\neach of them. As every hashing method is implemented in pure Go, this library\nshould be as portable as Go itself.\n\nAll hashing methods come with a test suite which verifies their operation\nagainst itself as well as the output of other password hashing implementations\nto ensure compatibility with them.\n\nI hope you find this library to be useful and easy to use!\n\nNote: forked from <https://github.com/jeramey/go-pwhash>\n\n## Installation\n\n\tgo get github.com/tredoe/osutil/user/crypt\n\n## License\n\nThe source files are distributed under a BSD-style license that can be found\nin the LICENSE file.\n"
  },
  {
    "path": "server/plugin/plg_authenticate_htpasswd/deps/crypt/apr1_crypt/apr1_crypt.go",
    "content": "// Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>\n// Copyright 2013, Jonas mg\n// All rights reserved.\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the LICENSE file.\n\n// Package apr1_crypt implements the standard Unix MD5-crypt algorithm created\n// by Poul-Henning Kamp for FreeBSD, and modified by the Apache project.\n//\n// The only change from MD5-crypt is the use of the magic constant \"$apr1$\"\n// instead of \"$1$\". The algorithms are otherwise identical.\npackage apr1_crypt\n\nimport (\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt/common\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt/md5_crypt\"\n)\n\nfunc init() {\n\tcrypt.RegisterCrypt(crypt.APR1, New, MagicPrefix)\n}\n\nconst (\n\tMagicPrefix   = \"$apr1$\"\n\tSaltLenMin    = 1\n\tSaltLenMax    = 8\n\tRoundsDefault = 1000\n)\n\nvar md5Crypt = md5_crypt.New()\n\nfunc init() {\n\tmd5Crypt.SetSalt(GetSalt())\n}\n\ntype crypter struct{ Salt common.Salt }\n\n// New returns a new crypt.Crypter computing the variant \"apr1\" of MD5-crypt\nfunc New() crypt.Crypter { return &crypter{common.Salt{}} }\n\nfunc (c *crypter) Generate(key, salt []byte) (string, error) {\n\treturn md5Crypt.Generate(key, salt)\n}\n\nfunc (c *crypter) Verify(hashedKey string, key []byte) error {\n\treturn md5Crypt.Verify(hashedKey, key)\n}\n\nfunc (c *crypter) Cost(hashedKey string) (int, error) { return RoundsDefault, nil }\n\nfunc (c *crypter) SetSalt(salt common.Salt) {}\n\nfunc GetSalt() common.Salt {\n\treturn common.Salt{\n\t\tMagicPrefix:   []byte(MagicPrefix),\n\t\tSaltLenMin:    SaltLenMin,\n\t\tSaltLenMax:    SaltLenMax,\n\t\tRoundsDefault: RoundsDefault,\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_htpasswd/deps/crypt/common/base64.go",
    "content": "// Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>\n// Copyright 2013, Jonas mg\n// All rights reserved.\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the LICENSE file.\n\npackage common\n\nconst alphabet = \"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\"\n\n// Base64_24Bit is a variant of Base64 encoding, commonly used with password\n// hashing algorithms to encode the result of their checksum output.\n//\n// The algorithm operates on up to 3 bytes at a time, encoding the following\n// 6-bit sequences into up to 4 hash64 ASCII bytes.\n//\n//   1. Bottom 6 bits of the first byte\n//   2. Top 2 bits of the first byte, and bottom 4 bits of the second byte.\n//   3. Top 4 bits of the second byte, and bottom 2 bits of the third byte.\n//   4. Top 6 bits of the third byte.\n//\n// This encoding method does not emit padding bytes as Base64 does.\nfunc Base64_24Bit(src []byte) (hash []byte) {\n\tif len(src) == 0 {\n\t\treturn []byte{} // TODO: return nil\n\t}\n\n\thashSize := (len(src) * 8) / 6\n\tif (len(src) % 6) != 0 {\n\t\thashSize += 1\n\t}\n\thash = make([]byte, hashSize)\n\n\tdst := hash\n\tfor len(src) > 0 {\n\t\tswitch len(src) {\n\t\tdefault:\n\t\t\tdst[0] = alphabet[src[0]&0x3f]\n\t\t\tdst[1] = alphabet[((src[0]>>6)|(src[1]<<2))&0x3f]\n\t\t\tdst[2] = alphabet[((src[1]>>4)|(src[2]<<4))&0x3f]\n\t\t\tdst[3] = alphabet[(src[2]>>2)&0x3f]\n\t\t\tsrc = src[3:]\n\t\t\tdst = dst[4:]\n\t\tcase 2:\n\t\t\tdst[0] = alphabet[src[0]&0x3f]\n\t\t\tdst[1] = alphabet[((src[0]>>6)|(src[1]<<2))&0x3f]\n\t\t\tdst[2] = alphabet[(src[1]>>4)&0x3f]\n\t\t\tsrc = src[2:]\n\t\t\tdst = dst[3:]\n\t\tcase 1:\n\t\t\tdst[0] = alphabet[src[0]&0x3f]\n\t\t\tdst[1] = alphabet[(src[0]>>6)&0x3f]\n\t\t\tsrc = src[1:]\n\t\t\tdst = dst[2:]\n\t\t}\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_htpasswd/deps/crypt/common/doc.go",
    "content": "// Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>\n// Copyright 2013, Jonas mg\n// All rights reserved.\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the LICENSE file.\n\n// Package common contains routines used by multiple password hashing\n// algorithms.\n//\n// Generally, you will never import this package directly. Many of the\n// *_crypt packages will import this package if they require it.\npackage common\n"
  },
  {
    "path": "server/plugin/plg_authenticate_htpasswd/deps/crypt/common/salt.go",
    "content": "// Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>\n// Copyright 2013, Jonas mg\n// All rights reserved.\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the LICENSE file.\n\npackage common\n\nimport (\n\t\"crypto/rand\"\n\t\"errors\"\n\t\"strconv\"\n)\n\nvar (\n\tErrSaltPrefix = errors.New(\"invalid magic prefix\")\n\tErrSaltFormat = errors.New(\"invalid salt format\")\n\tErrSaltRounds = errors.New(\"invalid rounds\")\n)\n\n// Salt represents a salt.\ntype Salt struct {\n\tMagicPrefix []byte\n\n\tSaltLenMin int\n\tSaltLenMax int\n\n\tRoundsMin     int\n\tRoundsMax     int\n\tRoundsDefault int\n}\n\n// Generate generates a random salt of a given length.\n//\n// The length is set thus:\n//\n//   length > SaltLenMax: length = SaltLenMax\n//   length < SaltLenMin: length = SaltLenMin\nfunc (s *Salt) Generate(length int) []byte {\n\tif length > s.SaltLenMax {\n\t\tlength = s.SaltLenMax\n\t} else if length < s.SaltLenMin {\n\t\tlength = s.SaltLenMin\n\t}\n\n\tsaltLen := (length * 6 / 8)\n\tif (length*6)%8 != 0 {\n\t\tsaltLen++\n\t}\n\tsalt := make([]byte, saltLen)\n\trand.Read(salt)\n\n\tout := make([]byte, len(s.MagicPrefix)+length)\n\tcopy(out, s.MagicPrefix)\n\tcopy(out[len(s.MagicPrefix):], Base64_24Bit(salt))\n\treturn out\n}\n\n// GenerateWRounds creates a random salt with the random bytes being of the\n// length provided, and the rounds parameter set as specified.\n//\n// The parameters are set thus:\n//\n//   length > SaltLenMax: length = SaltLenMax\n//   length < SaltLenMin: length = SaltLenMin\n//\n//   rounds < 0: rounds = RoundsDefault\n//   rounds < RoundsMin: rounds = RoundsMin\n//   rounds > RoundsMax: rounds = RoundsMax\n//\n// If rounds is equal to RoundsDefault, then the \"rounds=\" part of the salt is\n// removed.\nfunc (s *Salt) GenerateWRounds(length, rounds int) []byte {\n\tif length > s.SaltLenMax {\n\t\tlength = s.SaltLenMax\n\t} else if length < s.SaltLenMin {\n\t\tlength = s.SaltLenMin\n\t}\n\tif rounds < 0 {\n\t\trounds = s.RoundsDefault\n\t} else if rounds < s.RoundsMin {\n\t\trounds = s.RoundsMin\n\t} else if rounds > s.RoundsMax {\n\t\trounds = s.RoundsMax\n\t}\n\n\tsaltLen := (length * 6 / 8)\n\tif (length*6)%8 != 0 {\n\t\tsaltLen++\n\t}\n\tsalt := make([]byte, saltLen)\n\trand.Read(salt)\n\n\troundsText := \"\"\n\tif rounds != s.RoundsDefault {\n\t\troundsText = \"rounds=\" + strconv.Itoa(rounds) + \"$\"\n\t}\n\n\tout := make([]byte, len(s.MagicPrefix)+len(roundsText)+length)\n\tcopy(out, s.MagicPrefix)\n\tcopy(out[len(s.MagicPrefix):], []byte(roundsText))\n\tcopy(out[len(s.MagicPrefix)+len(roundsText):], Base64_24Bit(salt))\n\treturn out\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_htpasswd/deps/crypt/crypt.go",
    "content": "// Copyright 2013, Jonas mg\n// All rights reserved.\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the LICENSE file.\n\n// Package crypt provides interface for password crypt functions and collects\n// common constants.\npackage crypt\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt/common\"\n)\n\nvar ErrKeyMismatch = errors.New(\"hashed value is not the hash of the given password\")\n\n// Crypter is the common interface implemented by all crypt functions.\ntype Crypter interface {\n\t// Generate performs the hashing algorithm, returning a full hash suitable\n\t// for storage and later password verification.\n\t//\n\t// If the salt is empty, a randomly-generated salt will be generated with a\n\t// length of SaltLenMax and number RoundsDefault of rounds.\n\t//\n\t// Any error only can be got when the salt argument is not empty.\n\tGenerate(key, salt []byte) (string, error)\n\n\t// Verify compares a hashed key with its possible key equivalent.\n\t// Returns nil on success, or an error on failure; if the hashed key is\n\t// diffrent, the error is \"ErrKeyMismatch\".\n\tVerify(hashedKey string, key []byte) error\n\n\t// Cost returns the hashing cost (in rounds) used to create the given hashed\n\t// key.\n\t//\n\t// When, in the future, the hashing cost of a key needs to be increased in\n\t// order to adjust for greater computational power, this function allows one\n\t// to establish which keys need to be updated.\n\t//\n\t// The algorithms based in MD5-crypt use a fixed value of rounds.\n\tCost(hashedKey string) (int, error)\n\n\t// SetSalt sets a different salt. It is used to easily create derivated\n\t// algorithms, i.e. \"apr1_crypt\" from \"md5_crypt\".\n\tSetSalt(salt common.Salt)\n}\n\n// Crypt identifies a crypt function that is implemented in another package.\ntype Crypt uint\n\nconst (\n\tAPR1   Crypt = iota + 1 // import \"github.com/mickael-kerjean/filestash/server/plg_authenticate_htpasswd/deps/crypt/apr1_crypt\"\n\tMD5                     // import \"github.com/mickael-kerjean/filestash/server/plg_authenticate_htpasswd/deps/crypt/md5_crypt\"\n\tSHA256                  // import \"github.com/mickael-kerjean/filestash/server/plg_authenticate_htpasswd/deps/crypt/sha256_crypt\"\n\tSHA512                  // import \"github.com/mickael-kerjean/filestash/server/plg_authenticate_htpasswd/deps/crypt/sha512_crypt\"\n\tmaxCrypt\n)\n\nvar cryptPrefixes = make([]string, maxCrypt)\n\nvar crypts = make([]func() Crypter, maxCrypt)\n\n// RegisterCrypt registers a function that returns a new instance of the given\n// crypt function. This is intended to be called from the init function in\n// packages that implement crypt functions.\nfunc RegisterCrypt(c Crypt, f func() Crypter, prefix string) {\n\tif c >= maxCrypt {\n\t\tpanic(\"crypt: RegisterHash of unknown crypt function\")\n\t}\n\tcrypts[c] = f\n\tcryptPrefixes[c] = prefix\n}\n\n// New returns a new crypter.\nfunc New(c Crypt) Crypter {\n\tf := crypts[c]\n\tif f != nil {\n\t\treturn f()\n\t}\n\tpanic(\"crypt: requested crypt function is unavailable\")\n}\n\n// NewFromHash returns a new Crypter using the prefix in the given hashed key.\nfunc NewFromHash(hashedKey string) Crypter {\n\tvar f func() Crypter\n\n\tif strings.HasPrefix(hashedKey, cryptPrefixes[SHA512]) {\n\t\tf = crypts[SHA512]\n\t} else if strings.HasPrefix(hashedKey, cryptPrefixes[SHA256]) {\n\t\tf = crypts[SHA256]\n\t} else if strings.HasPrefix(hashedKey, cryptPrefixes[MD5]) {\n\t\tf = crypts[MD5]\n\t} else if strings.HasPrefix(hashedKey, cryptPrefixes[APR1]) {\n\t\tf = crypts[APR1]\n\t} else {\n\t\ttoks := strings.SplitN(hashedKey, \"$\", 3)\n\t\tprefix := \"$\" + toks[1] + \"$\"\n\t\tpanic(\"crypt: unknown cryp function from prefix: \" + prefix)\n\t}\n\n\tif f != nil {\n\t\treturn f()\n\t}\n\tpanic(\"crypt: requested cryp function is unavailable\")\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_htpasswd/deps/crypt/md5_crypt/md5_crypt.go",
    "content": "// Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>\n// Copyright 2013, Jonas mg\n// All rights reserved.\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the LICENSE file.\n\n// Package md5_crypt implements the standard Unix MD5-crypt algorithm created by\n// Poul-Henning Kamp for FreeBSD.\npackage md5_crypt\n\nimport (\n\t\"bytes\"\n\t\"crypto/md5\"\n\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt/common\"\n)\n\nfunc init() {\n\tcrypt.RegisterCrypt(crypt.MD5, New, MagicPrefix)\n}\n\n// NOTE: Cisco IOS only allows salts of length 4.\n\nconst (\n\tMagicPrefix   = \"$1$\"\n\tSaltLenMin    = 1 // Real minimum is 0, but that isn't useful.\n\tSaltLenMax    = 8\n\tRoundsDefault = 1000\n)\n\ntype crypter struct{ Salt common.Salt }\n\n// New returns a new crypt.Crypter computing the MD5-crypt password hashing.\nfunc New() crypt.Crypter {\n\treturn &crypter{GetSalt()}\n}\n\nfunc (c *crypter) Generate(key, salt []byte) (string, error) {\n\tif len(salt) == 0 {\n\t\tsalt = c.Salt.Generate(SaltLenMax)\n\t}\n\tif !bytes.HasPrefix(salt, c.Salt.MagicPrefix) {\n\t\treturn \"\", common.ErrSaltPrefix\n\t}\n\n\tsaltToks := bytes.Split(salt, []byte{'$'})\n\n\tif len(saltToks) < 3 {\n\t\treturn \"\", common.ErrSaltFormat\n\t} else {\n\t\tsalt = saltToks[2]\n\t}\n\tif len(salt) > 8 {\n\t\tsalt = salt[0:8]\n\t}\n\n\t// Compute alternate MD5 sum with input KEY, SALT, and KEY.\n\tAlternate := md5.New()\n\tAlternate.Write(key)\n\tAlternate.Write(salt)\n\tAlternate.Write(key)\n\tAlternateSum := Alternate.Sum(nil) // 16 bytes\n\n\tA := md5.New()\n\tA.Write(key)\n\tA.Write(c.Salt.MagicPrefix)\n\tA.Write(salt)\n\t// Add for any character in the key one byte of the alternate sum.\n\ti := len(key)\n\tfor ; i > 16; i -= 16 {\n\t\tA.Write(AlternateSum)\n\t}\n\tA.Write(AlternateSum[0:i])\n\n\t// The original implementation now does something weird:\n\t//   For every 1 bit in the key, the first 0 is added to the buffer\n\t//   For every 0 bit, the first character of the key\n\t// This does not seem to be what was intended but we have to follow this to\n\t// be compatible.\n\tfor i = len(key); i > 0; i >>= 1 {\n\t\tif (i & 1) == 0 {\n\t\t\tA.Write(key[0:1])\n\t\t} else {\n\t\t\tA.Write([]byte{0})\n\t\t}\n\t}\n\tCsum := A.Sum(nil)\n\n\t// In fear of password crackers here comes a quite long loop which just\n\t// processes the output of the previous round again.\n\t// We cannot ignore this here.\n\tfor i = 0; i < RoundsDefault; i++ {\n\t\tC := md5.New()\n\n\t\t// Add key or last result.\n\t\tif (i & 1) != 0 {\n\t\t\tC.Write(key)\n\t\t} else {\n\t\t\tC.Write(Csum)\n\t\t}\n\t\t// Add salt for numbers not divisible by 3.\n\t\tif (i % 3) != 0 {\n\t\t\tC.Write(salt)\n\t\t}\n\t\t// Add key for numbers not divisible by 7.\n\t\tif (i % 7) != 0 {\n\t\t\tC.Write(key)\n\t\t}\n\t\t// Add key or last result.\n\t\tif (i & 1) == 0 {\n\t\t\tC.Write(key)\n\t\t} else {\n\t\t\tC.Write(Csum)\n\t\t}\n\n\t\tCsum = C.Sum(nil)\n\t}\n\n\tout := make([]byte, 0, 23+len(c.Salt.MagicPrefix)+len(salt))\n\tout = append(out, c.Salt.MagicPrefix...)\n\tout = append(out, salt...)\n\tout = append(out, '$')\n\tout = append(out, common.Base64_24Bit([]byte{\n\t\tCsum[12], Csum[6], Csum[0],\n\t\tCsum[13], Csum[7], Csum[1],\n\t\tCsum[14], Csum[8], Csum[2],\n\t\tCsum[15], Csum[9], Csum[3],\n\t\tCsum[5], Csum[10], Csum[4],\n\t\tCsum[11],\n\t})...)\n\n\t// Clean sensitive data.\n\tA.Reset()\n\tAlternate.Reset()\n\tfor i = 0; i < len(AlternateSum); i++ {\n\t\tAlternateSum[i] = 0\n\t}\n\n\treturn string(out), nil\n}\n\nfunc (c *crypter) Verify(hashedKey string, key []byte) error {\n\tnewHash, err := c.Generate(key, []byte(hashedKey))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif newHash != hashedKey {\n\t\treturn crypt.ErrKeyMismatch\n\t}\n\treturn nil\n}\n\nfunc (c *crypter) Cost(hashedKey string) (int, error) { return RoundsDefault, nil }\n\nfunc (c *crypter) SetSalt(salt common.Salt) { c.Salt = salt }\n\nfunc GetSalt() common.Salt {\n\treturn common.Salt{\n\t\tMagicPrefix:   []byte(MagicPrefix),\n\t\tSaltLenMin:    SaltLenMin,\n\t\tSaltLenMax:    SaltLenMax,\n\t\tRoundsDefault: RoundsDefault,\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_htpasswd/deps/crypt/sha256_crypt/sha256_crypt.go",
    "content": "// Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>\n// Copyright 2013, Jonas mg\n// All rights reserved.\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the LICENSE file.\n\n// Package sha256_crypt implements Ulrich Drepper's SHA256-crypt password\n// hashing algorithm.\n//\n// The specification for this algorithm can be found here:\n// http://www.akkadia.org/drepper/SHA-crypt.txt\npackage sha256_crypt\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"strconv\"\n\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt/common\"\n)\n\nfunc init() {\n\tcrypt.RegisterCrypt(crypt.SHA256, New, MagicPrefix)\n}\n\nconst (\n\tMagicPrefix   = \"$5$\"\n\tSaltLenMin    = 1\n\tSaltLenMax    = 16\n\tRoundsMin     = 1000\n\tRoundsMax     = 999999999\n\tRoundsDefault = 5000\n)\n\nvar _rounds = []byte(\"rounds=\")\n\ntype crypter struct{ Salt common.Salt }\n\n// New returns a new crypt.Crypter computing the SHA256-crypt password hashing.\nfunc New() crypt.Crypter {\n\treturn &crypter{GetSalt()}\n}\n\nfunc (c *crypter) Generate(key, salt []byte) (string, error) {\n\tvar rounds int\n\tvar isRoundsDef bool\n\n\tif len(salt) == 0 {\n\t\tsalt = c.Salt.GenerateWRounds(SaltLenMax, RoundsDefault)\n\t}\n\tif !bytes.HasPrefix(salt, c.Salt.MagicPrefix) {\n\t\treturn \"\", common.ErrSaltPrefix\n\t}\n\n\tsaltToks := bytes.Split(salt, []byte{'$'})\n\tif len(saltToks) < 3 {\n\t\treturn \"\", common.ErrSaltFormat\n\t}\n\n\tif bytes.HasPrefix(saltToks[2], _rounds) {\n\t\tisRoundsDef = true\n\t\tpr, err := strconv.ParseInt(string(saltToks[2][7:]), 10, 32)\n\t\tif err != nil {\n\t\t\treturn \"\", common.ErrSaltRounds\n\t\t}\n\t\trounds = int(pr)\n\t\tif rounds < RoundsMin {\n\t\t\trounds = RoundsMin\n\t\t} else if rounds > RoundsMax {\n\t\t\trounds = RoundsMax\n\t\t}\n\t\tsalt = saltToks[3]\n\t} else {\n\t\trounds = RoundsDefault\n\t\tsalt = saltToks[2]\n\t}\n\n\tif len(salt) > 16 {\n\t\tsalt = salt[0:16]\n\t}\n\n\t// Compute alternate SHA256 sum with input KEY, SALT, and KEY.\n\tAlternate := sha256.New()\n\tAlternate.Write(key)\n\tAlternate.Write(salt)\n\tAlternate.Write(key)\n\tAlternateSum := Alternate.Sum(nil) // 32 bytes\n\n\tA := sha256.New()\n\tA.Write(key)\n\tA.Write(salt)\n\t// Add for any character in the key one byte of the alternate sum.\n\ti := len(key)\n\tfor ; i > 32; i -= 32 {\n\t\tA.Write(AlternateSum)\n\t}\n\tA.Write(AlternateSum[0:i])\n\n\t// Take the binary representation of the length of the key and for every add\n\t// the alternate sum, for every 0 the key.\n\tfor i = len(key); i > 0; i >>= 1 {\n\t\tif (i & 1) != 0 {\n\t\t\tA.Write(AlternateSum)\n\t\t} else {\n\t\t\tA.Write(key)\n\t\t}\n\t}\n\tAsum := A.Sum(nil)\n\n\t// Start computation of P byte sequence.\n\tP := sha256.New()\n\t// For every character in the password add the entire password.\n\tfor i = 0; i < len(key); i++ {\n\t\tP.Write(key)\n\t}\n\tPsum := P.Sum(nil)\n\t// Create byte sequence P.\n\tPseq := make([]byte, 0, len(key))\n\tfor i = len(key); i > 32; i -= 32 {\n\t\tPseq = append(Pseq, Psum...)\n\t}\n\tPseq = append(Pseq, Psum[0:i]...)\n\n\t// Start computation of S byte sequence.\n\tS := sha256.New()\n\tfor i = 0; i < (16 + int(Asum[0])); i++ {\n\t\tS.Write(salt)\n\t}\n\tSsum := S.Sum(nil)\n\t// Create byte sequence S.\n\tSseq := make([]byte, 0, len(salt))\n\tfor i = len(salt); i > 32; i -= 32 {\n\t\tSseq = append(Sseq, Ssum...)\n\t}\n\tSseq = append(Sseq, Ssum[0:i]...)\n\n\tCsum := Asum\n\n\t// Repeatedly run the collected hash value through SHA256 to burn CPU cycles.\n\tfor i = 0; i < rounds; i++ {\n\t\tC := sha256.New()\n\n\t\t// Add key or last result.\n\t\tif (i & 1) != 0 {\n\t\t\tC.Write(Pseq)\n\t\t} else {\n\t\t\tC.Write(Csum)\n\t\t}\n\t\t// Add salt for numbers not divisible by 3.\n\t\tif (i % 3) != 0 {\n\t\t\tC.Write(Sseq)\n\t\t}\n\t\t// Add key for numbers not divisible by 7.\n\t\tif (i % 7) != 0 {\n\t\t\tC.Write(Pseq)\n\t\t}\n\t\t// Add key or last result.\n\t\tif (i & 1) != 0 {\n\t\t\tC.Write(Csum)\n\t\t} else {\n\t\t\tC.Write(Pseq)\n\t\t}\n\n\t\tCsum = C.Sum(nil)\n\t}\n\n\tout := make([]byte, 0, 80)\n\tout = append(out, c.Salt.MagicPrefix...)\n\tif isRoundsDef {\n\t\tout = append(out, []byte(\"rounds=\"+strconv.Itoa(rounds)+\"$\")...)\n\t}\n\tout = append(out, salt...)\n\tout = append(out, '$')\n\tout = append(out, common.Base64_24Bit([]byte{\n\t\tCsum[20], Csum[10], Csum[0],\n\t\tCsum[11], Csum[1], Csum[21],\n\t\tCsum[2], Csum[22], Csum[12],\n\t\tCsum[23], Csum[13], Csum[3],\n\t\tCsum[14], Csum[4], Csum[24],\n\t\tCsum[5], Csum[25], Csum[15],\n\t\tCsum[26], Csum[16], Csum[6],\n\t\tCsum[17], Csum[7], Csum[27],\n\t\tCsum[8], Csum[28], Csum[18],\n\t\tCsum[29], Csum[19], Csum[9],\n\t\tCsum[30], Csum[31],\n\t})...)\n\n\t// Clean sensitive data.\n\tA.Reset()\n\tAlternate.Reset()\n\tP.Reset()\n\tfor i = 0; i < len(Asum); i++ {\n\t\tAsum[i] = 0\n\t}\n\tfor i = 0; i < len(AlternateSum); i++ {\n\t\tAlternateSum[i] = 0\n\t}\n\tfor i = 0; i < len(Pseq); i++ {\n\t\tPseq[i] = 0\n\t}\n\n\treturn string(out), nil\n}\n\nfunc (c *crypter) Verify(hashedKey string, key []byte) error {\n\tnewHash, err := c.Generate(key, []byte(hashedKey))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif newHash != hashedKey {\n\t\treturn crypt.ErrKeyMismatch\n\t}\n\treturn nil\n}\n\nfunc (c *crypter) Cost(hashedKey string) (int, error) {\n\tsaltToks := bytes.Split([]byte(hashedKey), []byte{'$'})\n\tif len(saltToks) < 3 {\n\t\treturn 0, common.ErrSaltFormat\n\t}\n\n\tif !bytes.HasPrefix(saltToks[2], _rounds) {\n\t\treturn RoundsDefault, nil\n\t}\n\troundToks := bytes.Split(saltToks[2], []byte{'='})\n\tcost, err := strconv.ParseInt(string(roundToks[1]), 10, 0)\n\treturn int(cost), err\n}\n\nfunc (c *crypter) SetSalt(salt common.Salt) { c.Salt = salt }\n\nfunc GetSalt() common.Salt {\n\treturn common.Salt{\n\t\tMagicPrefix:   []byte(MagicPrefix),\n\t\tSaltLenMin:    SaltLenMin,\n\t\tSaltLenMax:    SaltLenMax,\n\t\tRoundsDefault: RoundsDefault,\n\t\tRoundsMin:     RoundsMin,\n\t\tRoundsMax:     RoundsMax,\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_htpasswd/deps/crypt/sha512_crypt/sha512_crypt.go",
    "content": "// Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>\n// Copyright 2013, Jonas mg\n// All rights reserved.\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the LICENSE file.\n\n// Package sha512_crypt implements Ulrich Drepper's SHA512-crypt password\n// hashing algorithm.\n//\n// The specification for this algorithm can be found here:\n// http://www.akkadia.org/drepper/SHA-crypt.txt\npackage sha512_crypt\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha512\"\n\t\"strconv\"\n\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt/common\"\n)\n\nfunc init() {\n\tcrypt.RegisterCrypt(crypt.SHA512, New, MagicPrefix)\n}\n\nconst (\n\tMagicPrefix   = \"$6$\"\n\tSaltLenMin    = 1\n\tSaltLenMax    = 16\n\tRoundsMin     = 1000\n\tRoundsMax     = 999999999\n\tRoundsDefault = 5000\n)\n\nvar _rounds = []byte(\"rounds=\")\n\ntype crypter struct{ Salt common.Salt }\n\n// New returns a new crypt.Crypter computing the SHA512-crypt password hashing.\nfunc New() crypt.Crypter {\n\treturn &crypter{GetSalt()}\n}\n\nfunc (c *crypter) Generate(key, salt []byte) (string, error) {\n\tvar rounds int\n\tvar isRoundsDef bool\n\n\tif len(salt) == 0 {\n\t\tsalt = c.Salt.GenerateWRounds(SaltLenMax, RoundsDefault)\n\t}\n\tif !bytes.HasPrefix(salt, c.Salt.MagicPrefix) {\n\t\treturn \"\", common.ErrSaltPrefix\n\t}\n\n\tsaltToks := bytes.Split(salt, []byte{'$'})\n\tif len(saltToks) < 3 {\n\t\treturn \"\", common.ErrSaltFormat\n\t}\n\n\tif bytes.HasPrefix(saltToks[2], _rounds) {\n\t\tisRoundsDef = true\n\t\tpr, err := strconv.ParseInt(string(saltToks[2][7:]), 10, 32)\n\t\tif err != nil {\n\t\t\treturn \"\", common.ErrSaltRounds\n\t\t}\n\t\trounds = int(pr)\n\t\tif rounds < RoundsMin {\n\t\t\trounds = RoundsMin\n\t\t} else if rounds > RoundsMax {\n\t\t\trounds = RoundsMax\n\t\t}\n\t\tsalt = saltToks[3]\n\t} else {\n\t\trounds = RoundsDefault\n\t\tsalt = saltToks[2]\n\t}\n\n\tif len(salt) > SaltLenMax {\n\t\tsalt = salt[0:SaltLenMax]\n\t}\n\n\t// Compute alternate SHA512 sum with input KEY, SALT, and KEY.\n\tAlternate := sha512.New()\n\tAlternate.Write(key)\n\tAlternate.Write(salt)\n\tAlternate.Write(key)\n\tAlternateSum := Alternate.Sum(nil) // 64 bytes\n\n\tA := sha512.New()\n\tA.Write(key)\n\tA.Write(salt)\n\t// Add for any character in the key one byte of the alternate sum.\n\ti := len(key)\n\tfor ; i > 64; i -= 64 {\n\t\tA.Write(AlternateSum)\n\t}\n\tA.Write(AlternateSum[0:i])\n\n\t// Take the binary representation of the length of the key and for every add\n\t// the alternate sum, for every 0 the key.\n\tfor i = len(key); i > 0; i >>= 1 {\n\t\tif (i & 1) != 0 {\n\t\t\tA.Write(AlternateSum)\n\t\t} else {\n\t\t\tA.Write(key)\n\t\t}\n\t}\n\tAsum := A.Sum(nil)\n\n\t// Start computation of P byte sequence.\n\tP := sha512.New()\n\t// For every character in the password add the entire password.\n\tfor i = 0; i < len(key); i++ {\n\t\tP.Write(key)\n\t}\n\tPsum := P.Sum(nil)\n\t// Create byte sequence P.\n\tPseq := make([]byte, 0, len(key))\n\tfor i = len(key); i > 64; i -= 64 {\n\t\tPseq = append(Pseq, Psum...)\n\t}\n\tPseq = append(Pseq, Psum[0:i]...)\n\n\t// Start computation of S byte sequence.\n\tS := sha512.New()\n\tfor i = 0; i < (16 + int(Asum[0])); i++ {\n\t\tS.Write(salt)\n\t}\n\tSsum := S.Sum(nil)\n\t// Create byte sequence S.\n\tSseq := make([]byte, 0, len(salt))\n\tfor i = len(salt); i > 64; i -= 64 {\n\t\tSseq = append(Sseq, Ssum...)\n\t}\n\tSseq = append(Sseq, Ssum[0:i]...)\n\n\tCsum := Asum\n\n\t// Repeatedly run the collected hash value through SHA512 to burn CPU cycles.\n\tfor i = 0; i < rounds; i++ {\n\t\tC := sha512.New()\n\n\t\t// Add key or last result.\n\t\tif (i & 1) != 0 {\n\t\t\tC.Write(Pseq)\n\t\t} else {\n\t\t\tC.Write(Csum)\n\t\t}\n\t\t// Add salt for numbers not divisible by 3.\n\t\tif (i % 3) != 0 {\n\t\t\tC.Write(Sseq)\n\t\t}\n\t\t// Add key for numbers not divisible by 7.\n\t\tif (i % 7) != 0 {\n\t\t\tC.Write(Pseq)\n\t\t}\n\t\t// Add key or last result.\n\t\tif (i & 1) != 0 {\n\t\t\tC.Write(Csum)\n\t\t} else {\n\t\t\tC.Write(Pseq)\n\t\t}\n\n\t\tCsum = C.Sum(nil)\n\t}\n\n\tout := make([]byte, 0, 123)\n\tout = append(out, c.Salt.MagicPrefix...)\n\tif isRoundsDef {\n\t\tout = append(out, []byte(\"rounds=\"+strconv.Itoa(rounds)+\"$\")...)\n\t}\n\tout = append(out, salt...)\n\tout = append(out, '$')\n\tout = append(out, common.Base64_24Bit([]byte{\n\t\tCsum[42], Csum[21], Csum[0],\n\t\tCsum[1], Csum[43], Csum[22],\n\t\tCsum[23], Csum[2], Csum[44],\n\t\tCsum[45], Csum[24], Csum[3],\n\t\tCsum[4], Csum[46], Csum[25],\n\t\tCsum[26], Csum[5], Csum[47],\n\t\tCsum[48], Csum[27], Csum[6],\n\t\tCsum[7], Csum[49], Csum[28],\n\t\tCsum[29], Csum[8], Csum[50],\n\t\tCsum[51], Csum[30], Csum[9],\n\t\tCsum[10], Csum[52], Csum[31],\n\t\tCsum[32], Csum[11], Csum[53],\n\t\tCsum[54], Csum[33], Csum[12],\n\t\tCsum[13], Csum[55], Csum[34],\n\t\tCsum[35], Csum[14], Csum[56],\n\t\tCsum[57], Csum[36], Csum[15],\n\t\tCsum[16], Csum[58], Csum[37],\n\t\tCsum[38], Csum[17], Csum[59],\n\t\tCsum[60], Csum[39], Csum[18],\n\t\tCsum[19], Csum[61], Csum[40],\n\t\tCsum[41], Csum[20], Csum[62],\n\t\tCsum[63],\n\t})...)\n\n\t// Clean sensitive data.\n\tA.Reset()\n\tAlternate.Reset()\n\tP.Reset()\n\tfor i = 0; i < len(Asum); i++ {\n\t\tAsum[i] = 0\n\t}\n\tfor i = 0; i < len(AlternateSum); i++ {\n\t\tAlternateSum[i] = 0\n\t}\n\tfor i = 0; i < len(Pseq); i++ {\n\t\tPseq[i] = 0\n\t}\n\n\treturn string(out), nil\n}\n\nfunc (c *crypter) Verify(hashedKey string, key []byte) error {\n\tnewHash, err := c.Generate(key, []byte(hashedKey))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif newHash != hashedKey {\n\t\treturn crypt.ErrKeyMismatch\n\t}\n\treturn nil\n}\n\nfunc (c *crypter) Cost(hashedKey string) (int, error) {\n\tsaltToks := bytes.Split([]byte(hashedKey), []byte{'$'})\n\tif len(saltToks) < 3 {\n\t\treturn 0, common.ErrSaltFormat\n\t}\n\n\tif !bytes.HasPrefix(saltToks[2], _rounds) {\n\t\treturn RoundsDefault, nil\n\t}\n\troundToks := bytes.Split(saltToks[2], []byte{'='})\n\tcost, err := strconv.ParseInt(string(roundToks[1]), 10, 0)\n\treturn int(cost), err\n}\n\nfunc (c *crypter) SetSalt(salt common.Salt) { c.Salt = salt }\n\nfunc GetSalt() common.Salt {\n\treturn common.Salt{\n\t\tMagicPrefix:   []byte(MagicPrefix),\n\t\tSaltLenMin:    SaltLenMin,\n\t\tSaltLenMax:    SaltLenMax,\n\t\tRoundsDefault: RoundsDefault,\n\t\tRoundsMin:     RoundsMin,\n\t\tRoundsMax:     RoundsMax,\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_htpasswd/index.go",
    "content": "package plg_authenticate_htpasswd\n\nimport (\n\t\"crypto/sha1\"\n\t\"crypto/subtle\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"html\"\n\t\"net/http\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt/apr1_crypt\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt/md5_crypt\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt/sha256_crypt\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd/deps/crypt/sha512_crypt\"\n\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\nfunc init() {\n\tHooks.Register.AuthenticationMiddleware(\"htpasswd\", Htpasswd{})\n}\n\ntype Htpasswd struct{}\n\nfunc (this Htpasswd) Setup() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName: \"banner\",\n\t\t\t\tType: \"hidden\",\n\t\t\t\tDescription: `Inspired by Apache, the htpasswd plugin uses the content of a .htpasswd file to authenticate users. It displays a username and password login page, verifying credentials against the provided htpasswd data.\n\nThe plugin exposes 2 variables: {{ .user }} and {{ .password }} which can be used in the attribute mapping section to create rules tailored to your specific use case. Examples of this can be found [in the documentation](https://www.filestash.app/docs/install-and-upgrade/#advanced-authentication---facade-pattern)`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"htpasswd\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"users\",\n\t\t\t\tType: \"long_text\",\n\t\t\t\tPlaceholder: `test1:$apr1$ZiAIyyhS$ovyMo9eJRgDF/luvmAigP0\ntest2:{SHA}EJ9LPFDXsN9ynSmbxvjp75Bmlx8=\ntest3:$6$ME6DxvSEUjW4Kx/j$vQ5Yh1utmNEr4EZnWH0ZQa6hrG5yu2siybFW10aAax4u611W9awI5V90YWqGs4NjTSHkCrhpdbJoNErW9/Pbh1:19306:0:99999:7:::\ntest4:$6$wTy86P73X/DsCiQy$El3JVUjepBUO.e.1OTuDt4yL9w2CnzY4jHaIbg1P7p508n8vjzCC8ZNsWa1IlbhciBM8.0LqqXWi3OuhGfPmP.\ntest5:$5$RkdUxGLHGhmrO0yj$K6bCqmB.OPR7KM4i5eiAG.mxFyhElLNdthSL.dreqN5\ntest6:$1$vuUKD.37$R6eCPFBa6lKIVfnkABveB1`,\n\t\t\t\tDefault: \"\",\n\t\t\t\tDescription: `The list of users who are granted access using either or both the htpasswd file format or the /etc/shadow file format. To generate a password:\n'openssl passwd -6' or 'mkpasswd -m SHA-512' or the htpasswd cli tool.`,\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Htpasswd) EntryPoint(idpParams map[string]string, req *http.Request, res http.ResponseWriter) error {\n\tgetFlash := func() string {\n\t\tc, err := req.Cookie(\"flash\")\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t\thttp.SetCookie(res, &http.Cookie{\n\t\t\tName:   \"flash\",\n\t\t\tMaxAge: -1,\n\t\t\tPath:   \"/\",\n\t\t})\n\t\treturn fmt.Sprintf(`<p class=\"flash\">%s</p>`, html.EscapeString(c.Value))\n\t}\n\tres.Header().Set(\"Content-Type\", \"text/html; charset=utf-8\")\n\tres.WriteHeader(http.StatusOK)\n\tres.Write([]byte(Page(`\n      <form method=\"post\" class=\"component_middleware\">\n        <label>\n          <input type=\"text\" name=\"user\" value=\"\" placeholder=\"User\" autocorrect=\"off\" autocapitalize=\"off\" />\n        </label>\n        <label>\n          <input type=\"password\" name=\"password\" value=\"\" placeholder=\"Password\" />\n        </label>\n        <button>CONNECT</button>\n        ` + getFlash() + `\n        <style>\n          .flash{ color: #f26d6d; font-weight: bold; }\n          form { padding-top: 10vh; }\n        </style>\n      </form>`)))\n\treturn nil\n}\n\nfunc (this Htpasswd) Callback(formData map[string]string, idpParams map[string]string, res http.ResponseWriter) (map[string]string, error) {\n\tif idpParams[\"users\"] == \"\" {\n\t\tLog.Error(\"plg_authenticate_htpasswd::callback there is no user configured\")\n\t\treturn nil, NewError(\"You haven't configured any users\", 500)\n\t}\n\tlines := strings.Split(idpParams[\"users\"], \"\\n\")\n\tfor n, line := range lines {\n\t\tpair := strings.SplitN(line, \":\", 2)\n\t\tif len(pair) != 2 {\n\t\t\tcontinue\n\t\t} else if formData[\"user\"] != pair[0] {\n\t\t\tcontinue\n\t\t} else if verifyPassword(\n\t\t\tformData[\"password\"],\n\t\t\tstrings.SplitN(pair[1], \":\", 2)[0], // filter out unwanted fields from hash\n\t\t\tformData[\"user\"],\n\t\t) == false {\n\t\t\tcontinue\n\t\t}\n\t\treturn map[string]string{\n\t\t\t\"user\":     formData[\"user\"],\n\t\t\t\"password\": formData[\"password\"],\n\t\t\t\"n\":        fmt.Sprintf(\"%d\", n),\n\t\t}, nil\n\t}\n\thttp.SetCookie(res, &http.Cookie{\n\t\tName:   \"flash\",\n\t\tValue:  \"Invalid username or password\",\n\t\tMaxAge: 1,\n\t\tPath:   \"/\",\n\t})\n\treturn nil, ErrAuthenticationFailed\n}\n\nfunc verifyPassword(password string, hash string, _user string) bool {\n\tif strings.HasPrefix(hash, \"{SHA}\") {\n\t\td := sha1.New()\n\t\td.Write([]byte(password))\n\t\treturn subtle.ConstantTimeCompare(\n\t\t\t[]byte(strings.TrimPrefix(hash, \"{SHA}\")),\n\t\t\t[]byte(base64.StdEncoding.EncodeToString(d.Sum(nil))),\n\t\t) == 1\n\t}\n\tvar c crypt.Crypter\n\tparts := strings.SplitN(hash, \"$\", 4)\n\tif len(parts) != 4 {\n\t\tif password == hash {\n\t\t\tLog.Warning(\"plg_authenticate_htpasswd password for user '%s' isn't stored in a secure way, you should hash your password using something like 'openssl passwd -6'\", _user)\n\t\t\treturn true\n\t\t} else {\n\t\t\treturn false\n\t\t}\n\t}\n\tif strings.HasPrefix(hash, \"$apr1$\") {\n\t\tc = apr1_crypt.New()\n\t\tparts[2] = \"$apr1$\" + parts[2]\n\t} else if strings.HasPrefix(hash, \"$6$\") {\n\t\tc = sha512_crypt.New()\n\t\tparts[2] = \"$6$\" + parts[2]\n\t} else if strings.HasPrefix(hash, \"$5$\") {\n\t\tc = sha256_crypt.New()\n\t\tparts[2] = \"$5$\" + parts[2]\n\t} else if strings.HasPrefix(hash, \"$1$\") {\n\t\tc = md5_crypt.New()\n\t\tparts[2] = \"$1$\" + parts[2]\n\t} else if strings.HasPrefix(hash, \"$2a$\") {\n\t\treturn bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil\n\t} else {\n\t\treturn false\n\t}\n\tshadow, err := c.Generate(\n\t\t[]byte(password),\n\t\t[]byte(parts[2]),\n\t)\n\tif err != nil {\n\t\treturn false\n\t} else if shadow != hash {\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_ldap/index.go",
    "content": "package plg_authenticate_ldap\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"net/http\"\n)\n\nfunc init() {\n\tHooks.Register.AuthenticationMiddleware(\"ldap\", Ldap{})\n}\n\ntype Ldap struct{}\n\nfunc (this Ldap) Setup() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName: \"banner\",\n\t\t\t\tType: \"hidden\",\n\t\t\t\tDescription: `This enterprise SSO plugin delegates authentication to an LDAP server, presenting users with a username and password login page. Their credentials are then verified against your LDAP directory.\n\nThe plugin exposes the LDAP attribute of the authenticated users which can be used in the attribute mapping section to create rules tailored to your specific use case, see the documentation [on the website](https://www.filestash.app/setup-ldap.html).\n`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"ldap\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"Hostname\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tValue:       \"\",\n\t\t\t\tPlaceholder: \"eg: ldap.example.com\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"Port\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tValue:       \"\",\n\t\t\t\tPlaceholder: \"eg: 389\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"Bind DN\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tValue:       \"\",\n\t\t\t\tPlaceholder: \"Bind DN\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"Bind DN Password\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tValue:       \"\",\n\t\t\t\tPlaceholder: \"Bind CN Password\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"Base DN\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tValue:       \"\",\n\t\t\t\tPlaceholder: \"Base DN\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"Search Filter\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tValue:       \"\",\n\t\t\t\tPlaceholder: \"default: (&(objectclass=person)(|(uid={{.username}})(mail={{.username}})(sAMAccountName={{.username}})))\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Ldap) EntryPoint(idpParams map[string]string, req *http.Request, res http.ResponseWriter) error {\n\thttp.Redirect(\n\t\tres, req,\n\t\t\"https://www.filestash.app/purchase-enterprise-selfhosted.html\",\n\t\thttp.StatusTemporaryRedirect,\n\t)\n\treturn nil\n}\n\nfunc (this Ldap) Callback(formData map[string]string, idpParams map[string]string, res http.ResponseWriter) (map[string]string, error) {\n\treturn nil, ErrNotImplemented\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_local/README.md",
    "content": "\n```\nexport TOKEN=xxxx\n\n\n# list users\ncurl -H \"Accept: application/json\" -H \"Authorization: Bearer $TOKEN\" \"http://localhost:8334/admin/api/simple-user-management\"\n\n# upsert user\ncurl -X POST -d 'email=test@example.com&password=password&role=user' -H \"Accept: application/json\" -H \"Authorization: Bearer $TOKEN\" \"http://localhost:8334/admin/api/simple-user-management\"\n\n# delete user\ncurl -X DELETE -H \"Accept: application/json\" -H \"Authorization: Bearer $TOKEN\" \"http://localhost:8334/admin/api/simple-user-management?email=test@example.com\"\n```\n"
  },
  {
    "path": "server/plugin/plg_authenticate_local/auth.go",
    "content": "package plg_authenticate_local\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"html\"\n\t\"image/png\"\n\t\"net/http\"\n\t\"text/template\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"github.com/pquerna/otp/totp\"\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\ntype SimpleAuth struct{}\n\nfunc (this SimpleAuth) Setup() Form {\n\tnUsers := 0\n\taUsers := 0\n\tif users, err := getUsers(); err == nil {\n\t\tnUsers = len(users)\n\t\tfor i := range users {\n\t\t\tif users[i].Disabled == false {\n\t\t\t\taUsers += 1\n\t\t\t}\n\t\t}\n\t}\n\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName: \"banner\",\n\t\t\t\tType: \"hidden\",\n\t\t\t\tDescription: fmt.Sprintf(`<pre>MANAGEMENT GUI: <a href=\"`+WithBase(\"/admin/simple-user-management\")+`\">/admin/simple-user-management</a>\nSTATS:\n┌─────────────┐   ┌──────────────┐\n│ TOTAL USERS │   │ ACTIVE USERS │\n|    %.4d     │   |     %.4d     │\n└─────────────┘   └──────────────┘\nEMAIL SERVER: %t\n</pre>`, nUsers, aUsers, isEmailSetup()),\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"local\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:    \"mfa\",\n\t\t\t\tType:    \"select\",\n\t\t\t\tDefault: \"\",\n\t\t\t\tOpts:    []string{\"\", \"TOTP\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"notification_subject\",\n\t\t\t\tType: \"text\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"notification_body\",\n\t\t\t\tType: \"long_text\",\n\t\t\t\tPlaceholder: `Hello,\n\nYour account was created by an administrator. You can access\nit via http://{{ .instance_url }}.\n\nYour password is: {{ .password }}\nThe roles assigned to you: {{ .role }}\n\nCheers!`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"db\",\n\t\t\t\tType: \"hidden\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this SimpleAuth) EntryPoint(idpParams map[string]string, req *http.Request, res http.ResponseWriter) error {\n\tgetFlash := func() string {\n\t\tc, err := req.Cookie(\"flash\")\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t\thttp.SetCookie(res, &http.Cookie{\n\t\t\tName:   \"flash\",\n\t\t\tMaxAge: -1,\n\t\t\tPath:   \"/\",\n\t\t})\n\t\treturn fmt.Sprintf(`<p class=\"flash\">%s</p>`, html.EscapeString(c.Value))\n\t}\n\tres.Header().Set(\"Content-Type\", \"text/html; charset=utf-8\")\n\tres.WriteHeader(http.StatusOK)\n\tif c, err := req.Cookie(\"mfa\"); err == nil && c.Value != \"\" {\n\t\tuser := withMFA(User{}, c.Value)\n\t\tkey, err := totp.Generate(totp.GenerateOpts{\n\t\t\tIssuer:      Config.Get(\"general.name\").String(),\n\t\t\tAccountName: user.Email,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar buf bytes.Buffer\n\t\timg, err := key.Image(200, 200)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = png.Encode(&buf, img); err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttemplate.Must(template.New(\"app\").Parse(Page(`\n            <form method=\"post\" class=\"component_middleware\">\n                {{ if eq .User.MFA \"\" }}\n                <style>\n                    #init { padding: 20px 20px 10px 20px; text-align: center; background: rgba(0,0,0,0.1); border-radius: 10px; margin-top: -10vh; margin-bottom: 20px; }\n                    #init input { background: transparent; margin-bottom: 0; text-align: center; }\n                </style>\n                <div id=\"init\">\n                   <img src=\"data:image/png;base64,{{ .QRCode }}\" />\n                   <input type=\"text\" name=\"mfa\" value=\"{{ .MFASecret }}\" readonly />\n                </div>\n                {{ end }}\n                <label>\n                    <input type=\"text\" name=\"code\" placeholder=\"code\" />\n                </label>\n                <input type=\"hidden\" name=\"session\" value=\"{{ .Session }}\" />\n                <button>SUBMIT</button>\n                `+getFlash()+`\n                <style>\n                    form { padding-top: 10vh; }\n                </style>\n            </form>\n        `))).Execute(res, struct {\n\t\t\tUser      User\n\t\t\tSession   string\n\t\t\tMFASecret string\n\t\t\tQRCode    string\n\t\t}{\n\t\t\tUser:      user,\n\t\t\tSession:   c.Value,\n\t\t\tMFASecret: key.Secret(),\n\t\t\tQRCode:    base64.StdEncoding.EncodeToString(buf.Bytes()),\n\t\t})\n\t\treturn nil\n\t}\n\tres.Write([]byte(Page(`\n        <form method=\"post\" class=\"component_middleware\">\n            <label>\n                <input type=\"text\" name=\"user\" value=\"\" placeholder=\"Email\" />\n            </label>\n            <label>\n                <input type=\"password\" name=\"password\" value=\"\" placeholder=\"Password\" />\n            </label>\n            <button>CONNECT</button>\n            ` + getFlash() + `\n            <style>\n                .flash{ color: #f26d6d; font-weight: bold; }\n                form { padding-top: 10vh; }\n            </style>\n        </form>`)))\n\treturn nil\n}\n\nfunc (this SimpleAuth) Callback(formData map[string]string, idpParams map[string]string, res http.ResponseWriter) (map[string]string, error) {\n\tusers, err := getUsers()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trequestedUser := withMFA(User{\n\t\tEmail:    formData[\"user\"],\n\t\tPassword: formData[\"password\"],\n\t}, formData[\"session\"])\n\trequestedUser.Code = formData[\"code\"]\n\tfor i := range users {\n\t\tif users[i].Email != requestedUser.Email {\n\t\t\tcontinue\n\t\t}\n\t\tif err = bcrypt.CompareHashAndPassword([]byte(users[i].Password), []byte(requestedUser.Password)); err != nil {\n\t\t\tbreak\n\t\t}\n\t\tif users[i].Disabled == true {\n\t\t\thttp.SetCookie(res, &http.Cookie{\n\t\t\t\tName:   \"flash\",\n\t\t\t\tValue:  \"Account is disabled\",\n\t\t\t\tMaxAge: 1,\n\t\t\t\tPath:   \"/\",\n\t\t\t})\n\t\t\tLog.Warning(\"plg_authentication_simple::auth action=authenticate email=%s err=disabled\", users[i].Email)\n\t\t\treturn nil, ErrAuthenticationFailed\n\t\t}\n\t\tif idpParams[\"mfa\"] == \"TOTP\" {\n\t\t\tshouldSaveMFAKey := false\n\t\t\tif users[i].MFA == \"\" {\n\t\t\t\tusers[i].MFA = formData[\"mfa\"]\n\t\t\t\tshouldSaveMFAKey = true\n\t\t\t}\n\t\t\tif totp.Validate(requestedUser.Code, users[i].MFA) == false {\n\t\t\t\trequestedUser.MFA = users[i].MFA\n\t\t\t\thttp.SetCookie(res, &http.Cookie{\n\t\t\t\t\tName:   \"mfa\",\n\t\t\t\t\tValue:  requestedUser.EncryptedString(),\n\t\t\t\t\tMaxAge: 1,\n\t\t\t\t})\n\t\t\t\treturn nil, ErrAuthenticationFailed\n\t\t\t}\n\t\t\tif shouldSaveMFAKey {\n\t\t\t\tsaveUsers(users)\n\t\t\t}\n\t\t}\n\t\tsession := map[string]string{\n\t\t\t\"user\":     requestedUser.Email,\n\t\t\t\"password\": requestedUser.Password,\n\t\t\t\"bcrypt\":   users[i].Password,\n\t\t\t\"role\":     users[i].Role,\n\t\t}\n\t\ts := \"\"\n\t\tfor k, v := range session {\n\t\t\tif k == \"password\" || k == \"bcrypt\" {\n\t\t\t\tv = \"*****\"\n\t\t\t}\n\t\t\ts += fmt.Sprintf(\"%s[%s] \", k, v)\n\t\t}\n\t\tLog.Debug(\"IDP Attributes => %s\", s)\n\t\treturn session, nil\n\t}\n\n\thttp.SetCookie(res, &http.Cookie{\n\t\tName:   \"flash\",\n\t\tValue:  \"Invalid username or password\",\n\t\tMaxAge: 1,\n\t\tPath:   \"/\",\n\t})\n\treturn nil, ErrAuthenticationFailed\n}\n\nfunc withMFA(user User, session string) User {\n\tif session == \"\" {\n\t\treturn user\n\t}\n\tdata, err := DecryptString(SECRET_KEY_DERIVATE_FOR_USER, session)\n\tif err != nil {\n\t\treturn User{}\n\t}\n\tvar u User\n\tif err = json.Unmarshal([]byte(data), &u); err != nil {\n\t\treturn User{}\n\t}\n\tuser.Email = u.Email\n\tuser.Password = u.Password\n\treturn u\n}\n\nfunc (user User) EncryptedString() string {\n\tb, err := json.Marshal(user)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\td, err := EncryptString(SECRET_KEY_DERIVATE_FOR_USER, string(b))\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn d\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_local/config.go",
    "content": "package plg_authenticate_local\n\nimport (\n\t\"encoding/json\"\n)\n\ntype pluginConfig map[string]any\n\nfunc (this pluginConfig) GetUsers() ([]User, error) {\n\tdb, ok := this[\"db\"].(string)\n\tif !ok || db == \"\" {\n\t\treturn []User{}, nil\n\t}\n\tvar users []User\n\tif err := json.Unmarshal([]byte(db), &users); err != nil {\n\t\treturn nil, err\n\t}\n\treturn users, nil\n}\n\nfunc (this pluginConfig) GetMailSubject() string {\n\tif subject, ok := this[\"notification_subject\"].(string); ok {\n\t\treturn subject\n\t}\n\treturn \"\"\n}\n\nfunc (this pluginConfig) GetMailBody() string {\n\tif body, ok := this[\"notification_body\"].(string); ok {\n\t\treturn body\n\t}\n\treturn \"\"\n}\n\nfunc (this pluginConfig) SetUsers(users []User) {\n\tif usersData, err := json.Marshal(users); err == nil {\n\t\tthis[\"db\"] = string(usersData)\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_local/data.go",
    "content": "package plg_authenticate_local\n\nimport (\n\t\"encoding/json\"\n\t\"os\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar force bool\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tforce = os.Getenv(\"PLG_AUTHENTICATE_LOCAL_ENABLED\") == \"true\"\n\t})\n}\n\nfunc getPluginData() (pluginConfig, error) {\n\tcfg := make(pluginConfig)\n\tif !isEnabled() {\n\t\tLog.Warning(\"plg_authenticate_simple::disable msg=middleware_is_not_enabled\")\n\t\treturn cfg, ErrMissingDependency\n\t}\n\terr := json.Unmarshal(\n\t\t[]byte(Config.Get(\"middleware.identity_provider.params\").String()),\n\t\t&cfg,\n\t)\n\treturn cfg, err\n}\n\nfunc savePluginData(cfg pluginConfig) error {\n\tif !isEnabled() {\n\t\tLog.Warning(\"plg_authenticate_simple::disable msg=middleware_is_not_enabled\")\n\t\treturn ErrMissingDependency\n\t}\n\tb, err := json.Marshal(cfg)\n\tif err != nil {\n\t\treturn err\n\t}\n\tConfig.Get(\"middleware.identity_provider.params\").Set(string(b))\n\treturn nil\n}\n\nfunc isEnabled() bool {\n\tif Config.Get(\"middleware.identity_provider.type\").String() == \"local\" {\n\t\treturn true\n\t} else if force {\n\t\treturn true\n\t}\n\tLog.Warning(\"plg_authenticate_simple::disable msg=middleware_is_not_enabled\")\n\treturn false\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_local/handler.go",
    "content": "package plg_authenticate_local\n\nimport (\n\t_ \"embed\"\n\t\"html/template\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\n//go:embed handler.html\nvar PAGE string\n\nfunc UserManagementHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif req.Method == http.MethodDelete {\n\t\temail := req.FormValue(\"email\")\n\t\tif email == \"\" {\n\t\t\temail = req.URL.Query().Get(\"email\")\n\t\t}\n\t\tif err := removeUser(req.FormValue(\"email\")); err != nil {\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\t\tSendSuccessResult(res, nil)\n\t\treturn\n\t}\n\n\tcurrentUser := User{}\n\tusers, err := getUsers()\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\temail := formatEmail(req.URL.Query().Get(\"email\"))\n\tif email == \"\" {\n\t\temail = formatEmail(req.FormValue(\"email\"))\n\t}\n\tif email != \"\" {\n\t\tfor i := range users {\n\t\t\tif users[i].Email == email {\n\t\t\t\tcurrentUser = users[i]\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tif req.Method == http.MethodPost {\n\t\tuser := User{\n\t\t\tEmail:    email,\n\t\t\tPassword: formatPassword(req.FormValue(\"password\")),\n\t\t\tRole:     formatRole(req.FormValue(\"role\")),\n\t\t\tDisabled: req.FormValue(\"disabled\") == \"on\",\n\t\t}\n\t\tredirectURI := req.URL.String()\n\t\tfn := createUser\n\t\tif currentUser.Email != \"\" {\n\t\t\tfn = updateUser\n\t\t\tredirectURI = req.URL.Path\n\t\t}\n\t\tif err := fn(user); err != nil {\n\t\t\tSendErrorResult(res, err)\n\t\t\treturn\n\t\t}\n\t\tif currentUser.Email == \"\" {\n\t\t\tgo sendInvitateMail(user)\n\t\t}\n\t\tif isAPI(req) {\n\t\t\tSendSuccessResult(res, nil)\n\t\t\treturn\n\t\t}\n\t\thttp.Redirect(res, req, redirectURI, http.StatusSeeOther)\n\t\treturn\n\t}\n\n\tif isAPI(req) {\n\t\tSendSuccessResults(res, users)\n\t\treturn\n\t}\n\ttemplate.\n\t\tMust(template.New(\"app\").Parse(Page(PAGE))).\n\t\tExecute(res, struct {\n\t\t\tUsers       []User\n\t\t\tCurrentUser User\n\t\t\tBackURL     string\n\t\t}{\n\t\t\tUsers:       users,\n\t\t\tCurrentUser: currentUser,\n\t\t\tBackURL:     WithBase(\"/admin/storage\"),\n\t\t})\n}\n\nfunc isAPI(r *http.Request) bool {\n\treturn r.Header.Get(\"Accept\") == \"application/json\"\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_local/handler.html",
    "content": "<style>\n html { height: initial; }\n body { max-width: 800px; margin: 0 auto; text-align: left; padding-left: 10px; padding-right: 10px; color: #57595A; }\n .common_response_page { max-width: inherit; padding: 0; }\n header { background: white; padding: 100px 50px 75px; border-bottom: 1px solid #e2e2e2; }\n main { max-width: 1000px; margin: auto; width: 99%; }\n h1 { margin-top: 10px; margin-bottom: 0; font-size: 2.3rem; }\n p { margin: 7px 0; }\n table { width: 100%; margin: 30px auto 50px auto; padding: 0; }\n table .disabled td:not([onclick]) { text-decoration: line-through; }\n table td.action { color: white; background: rgba(0, 0, 0, 0.3); border-radius: 10px; font-weight: bold; font-size: 0.7rem; cursor: pointer; }\n form { width: 100%; padding: 0; }\n tbody tr:hover { background: rgba(255, 255, 255, 0.7); }\n th { font-weight: bold; font-size: 0.8rem; color: rgba(0, 0, 0, 0.5); text-transform: uppercase; }\n th, td { padding: 10px 10px; }\n input[disabled] { background: #d2d2d2; }\n .banner-empty { margin-top: 50px; font-size: 1.2rem; color: rgba(0, 0, 0, 0.5); text-align: center; }\n button { text-transform: uppercase; background: #9AD1ED; }\n dialog { position: absolute; inset: 0; border: none !important; border-radius: 5px; box-shadow: 0 0 #0000, 0 0 #0000, 0 25px 50px -12px rgba(0, 0, 0, 0.25); padding: 20px 40px 35px 40px; width: 450px; background: #505457; z-index: 5; color: white; }\n dialog[open] { animation: dialogin 0.2s ease forwards; }\n @keyframes dialogin{\n     0%{ opacity:0; transform: translateY(5px); }\n     100%{ opacity:1; transform: translateY(0); }\n }\n dialog h1 { padding: 0; margin: 5px 0 20px 0; }\n .center { text-align: center; }\n .pointer { cursor: pointer; }\n .button__round { border: 1px solid #313538; display: inline-block; width: 30px; height: 30px; line-height: 30px; text-align: center; border-radius: 50%; padding: 2px; box-shadow: 2px 2px rgba(0, 0, 0, 0.2); }\n dialog .button__round { border-color: white; box-shadow: none; }\n h1 span.pointer { float: right; }\n #modal-close { transform: rotate(45deg); }\n #back { position: absolute; top: 10px; left: 10px; color: inherit; text-decoration: none; }\n</style>\n\n<dialog closedby=\"any\">\n    <h1>\n        {{ if eq .CurrentUser.Email \"\" }}User Creation{{ else }}User Update{{ end }}\n        <span id=\"modal-close\" class=\"pointer button__round\">+</span>\n    </h1>\n    <form method=\"post\">\n        <input type=\"email\" name=\"email\" value=\"{{ .CurrentUser.Email }}\" placeholder=\"Email\" {{ if ne .CurrentUser.Email \"\" }}disabled{{ end }} />\n        <input type=\"password\" name=\"password\" value=\"{{ .CurrentUser.Password }}\" placeholder=\"Password\" />\n        <input type=\"text\" name=\"role\" value=\"{{ .CurrentUser.Role }}\" placeholder=\"Role\" />\n        <label>\n            <input type=\"checkbox\" name=\"disabled\" {{ if eq .CurrentUser.Disabled true }}checked{{ end }}>\n            Block\n        </label>\n        <button>\n            {{ if eq .CurrentUser.Email \"\" }}Create{{ else }}Update{{ end }}\n        </button>\n    </form>\n</dialog>\n\n<header>\n    <h1>User Management <span id=\"user-add\" class=\"pointer button__round\">+</span></h1>\n    <p>Manage your team members and their account permissions</p>\n</header>\n\n<main>\n    {{ $length := len .Users }} {{ if eq $length 0 }}\n    <p class=\"center banner-empty\">\n        There is not user yet, create one!\n    </p>\n    {{ else }}\n    <table>\n        <thead>\n            <tr>\n                <th>Email</th>\n                <th>Role</th>\n                <th style=\"width:10px;\"></th>\n                <th style=\"width:10px;\"></th>\n            </tr>\n        </thead>\n        <tbody>\n            {{range $user := .Users }}\n            <tr class=\"{{ if eq $user.Disabled true }}disabled{{ end }}\">\n                <td>{{ $user.Email }}</td>\n                <td>{{ $user.Role }}</td>\n                <td class=\"center action\" onclick=\"deleteItem('{{ $user.Email }}')\">DEL</td>\n                <td class=\"center action\" onclick=\"updateItem('{{ $user.Email }}')\">EDIT</td>\n            </tr>\n            {{end}}\n        </tbody>\n    </table>\n    {{ end }}\n</main>\n\n<a href=\"{{ .BackURL }}\" id=\"back\">&#60; back</a>\n\n<script>\n window.deleteItem = async (email) => {\n     const ok = window.confirm(\"Are you sure you want to remove: \" + email);\n     if (!ok) return;\n     const f = new FormData();\n     f.append(\"email\", email)\n     const resp = await fetch(location.pathname, {\n         method: \"DELETE\",\n         body: f,\n     });\n     if (!resp.ok) {\n         alert(resp.statusText);\n         return;\n     }\n     location.reload();\n\n };\n window.updateItem = async (email) => {\n     location.search = \"?email=\" + encodeURIComponent(email);\n };\n\n (function() {\n     const $dialog = document.querySelector(\"dialog\");\n\n     // feature: click the add button\n     document.getElementById(\"user-add\").onclick = () => $dialog.showModal();\n\n     // feature: autoopen modal\n     if(new URLSearchParams(location.search).has(\"email\")) {\n         $dialog.showModal();\n     }\n\n     // feature: modal close button\n     document.getElementById(\"modal-close\").onclick = () => {\n         $dialog.close();\n         location.search = \"\";\n     };\n     // feature: modal submit\n     document.querySelector(\"form\").onsubmit = (e) => {\n         e.target.querySelector(\"button\").setAttribute(\"disabled\", \"true\");\n     };\n     // feature: request admin refresh\n     const bus = new BroadcastChannel(\"admin\");\n     bus.postMessage(\"reload\");\n })();\n</script>\n"
  },
  {
    "path": "server/plugin/plg_authenticate_local/index.go",
    "content": "package plg_authenticate_local\n\nimport (\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/middleware\"\n\n\t\"github.com/gorilla/mux\"\n)\n\nfunc init() {\n\tHooks.Register.AuthenticationMiddleware(\"local\", SimpleAuth{})\n\tHooks.Register.HttpEndpoint(func(r *mux.Router) error {\n\t\tr.Handle(WithBase(\"/admin/simple-user-management\"), http.RedirectHandler(WithBase(\"/admin/api/simple-user-management\"), http.StatusSeeOther)).Methods(\"GET\")\n\t\tr.HandleFunc(WithBase(\"/admin/api/simple-user-management\"), middleware.NewMiddlewareChain(\n\t\t\tUserManagementHandler,\n\t\t\t[]Middleware{middleware.AdminOnly},\n\t\t)).Methods(\"GET\", \"POST\", \"DELETE\", \"PATCH\")\n\t\treturn nil\n\t})\n}\n\ntype User struct {\n\tEmail    string `json:\"email\"`\n\tPassword string `json:\"password\"`\n\tRole     string `json:\"role,omitempty\"`\n\tDisabled bool   `json:\"disabled,omitempty\"`\n\n\tCode string `json:\"-\"`\n\tMFA  string `json:\"mfa,omitempty\"`\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_local/notify.go",
    "content": "package plg_authenticate_local\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"text/template\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"gopkg.in/gomail.v2\"\n)\n\nfunc isEmailSetup() bool {\n\tif Config.Get(\"email.from\").String() == \"\" {\n\t\treturn false\n\t}\n\tif Config.Get(\"email.server\").String() == \"\" {\n\t\treturn false\n\t}\n\tif Config.Get(\"email.port\").Int() == 0 {\n\t\treturn false\n\t}\n\tif Config.Get(\"email.username\").String() == \"\" {\n\t\treturn false\n\t}\n\tif Config.Get(\"email.password\").String() == \"\" {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc sendInvitateMail(user User) error {\n\tcfg, err := getPluginData()\n\tif err != nil {\n\t\treturn err\n\t} else if cfg.GetMailSubject() == \"\" || cfg.GetMailBody() == \"\" {\n\t\treturn nil\n\t}\n\tm := gomail.NewMessage()\n\tm.SetHeader(\"From\", Config.Get(\"email.from\").String())\n\tm.SetHeader(\"To\", user.Email)\n\tm.SetHeader(\"Subject\", withTemplate(cfg.GetMailSubject(), user))\n\tm.SetBody(\"text/plain\", withTemplate(cfg.GetMailBody(), user))\n\td := gomail.NewDialer(\n\t\tConfig.Get(\"email.server\").String(),\n\t\tConfig.Get(\"email.port\").Int(),\n\t\tConfig.Get(\"email.username\").String(),\n\t\tConfig.Get(\"email.password\").String(),\n\t)\n\tif err := d.DialAndSend(m); err != nil {\n\t\treturn fmt.Errorf(\"cannot send mail - reason=%s\", err.Error())\n\t}\n\tLog.Info(\"plg_authenticate_simple::notification action=sent email=%s\", user.Email)\n\treturn nil\n}\n\nfunc withTemplate(in string, user User) string {\n\tvar b bytes.Buffer\n\ttmpl, err := template.New(\"app\").Parse(in)\n\tif err != nil {\n\t\tLog.Warning(\"plg_authenticate_simple::mail err=cannot_compile_template\")\n\t\treturn in\n\t}\n\ttmpl.Execute(&b, map[string]string{\n\t\t\"instance_url\": Config.Get(\"general.host\").String(),\n\t\t\"user\":         user.Email,\n\t\t\"password\":     user.Password,\n\t\t\"role\":         user.Role,\n\t})\n\treturn b.String()\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_local/service.go",
    "content": "package plg_authenticate_local\n\nimport (\n\t\"sort\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\nfunc removeUser(email string) error {\n\tusers, err := getUsers()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor i := range users {\n\t\tif users[i].Email == email {\n\t\t\tusers[i] = users[len(users)-1]\n\t\t\treturn saveUsers(users[:len(users)-1])\n\t\t}\n\t}\n\treturn ErrNotFound\n}\n\nfunc createUser(user User) error {\n\tif user.Password == \"\" {\n\t\treturn ErrNotValid\n\t}\n\tpwd := user.Password\n\tif len(pwd) > 72 {\n\t\tpwd = pwd[0:72]\n\t}\n\tp, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)\n\tif err != nil {\n\t\treturn err\n\t}\n\tuser.Password = string(p)\n\tusers, err := getUsers()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn saveUsers(append(users, user))\n}\n\nfunc updateUser(user User) error {\n\tusers, err := getUsers()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor i := range users {\n\t\tif users[i].Email == user.Email {\n\t\t\tif strings.HasPrefix(user.Password, \"$2a$\") == false {\n\t\t\t\tp, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tuser.Password = string(p)\n\t\t\t\tusers[i].Password = user.Password\n\t\t\t}\n\t\t\tusers[i].Disabled = user.Disabled\n\t\t\tusers[i].Role = user.Role\n\t\t\treturn saveUsers(users)\n\t\t}\n\t}\n\treturn ErrNotFound\n}\n\nfunc getUsers() ([]User, error) {\n\tcfg, err := getPluginData()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn cfg.GetUsers()\n}\n\nfunc saveUsers(users []User) error {\n\tcfg, err := getPluginData()\n\tif err != nil {\n\t\treturn err\n\t}\n\tsort.Slice(users, func(i, j int) bool {\n\t\tuserI := []byte(users[i].Email)\n\t\tuserJ := []byte(users[j].Email)\n\t\tn := len(userI)\n\t\tif len(userJ) < len(userI) {\n\t\t\tn = len(userJ)\n\t\t}\n\t\tfor i := 0; i < n; i++ {\n\t\t\tif userI[i] == userJ[i] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn userI[i] < userJ[i]\n\t\t}\n\t\treturn false\n\t})\n\tcfg.SetUsers(users)\n\treturn savePluginData(cfg)\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_local/utils.go",
    "content": "package plg_authenticate_local\n\nimport (\n\t\"strings\"\n)\n\nfunc formatRole(s string) string {\n\tarr := strings.Split(s, \",\")\n\tfor i := range arr {\n\t\tarr[i] = strings.TrimSpace(arr[i])\n\t}\n\treturn strings.Join(arr, \", \")\n}\n\nfunc formatEmail(s string) string {\n\treturn strings.TrimSpace(strings.ToLower(s))\n}\n\nfunc formatPassword(s string) string {\n\treturn strings.TrimSpace(s)\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_passthrough/index.go",
    "content": "package plg_authenticate_passthrough\n\nimport (\n\t\"fmt\"\n\t\"html\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.AuthenticationMiddleware(\"passthrough\", Passthrough{})\n}\n\ntype Passthrough struct{}\n\nfunc (this Passthrough) Setup() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"passthrough\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:  \"strategy\",\n\t\t\t\tType:  \"select\",\n\t\t\t\tValue: \"direct\",\n\t\t\t\tOpts:  []string{\"direct\", \"password_only\", \"username_and_password\"},\n\t\t\t\tDescription: `This plugin has 3 base strategies:\n1. The 'direct' strategy will redirect the user to your storage without asking for anything and use whatever is configured in the attribute mapping section.\n2. The 'password_only' strategy will redirect the user to a page asking for a password which you can map to a field in the attribute mapping section like this: {{ .password }}\n3. The 'username_and_password' strategy is similar to the 'password_only' strategy but you will see in the login page both a username and password field which can be used fom the attribute mapping section like this: {{ .user }} {{ .password }}`,\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Passthrough) EntryPoint(idpParams map[string]string, req *http.Request, res http.ResponseWriter) error {\n\tres.Header().Set(\"Content-Type\", \"text/html; charset=utf-8\")\n\tgetParams := \"?label=\" + html.EscapeString(req.URL.Query().Get(\"label\")) + \"&state=\" + html.EscapeString(req.URL.Query().Get(\"state\"))\n\tswitch idpParams[\"strategy\"] {\n\tcase \"direct\":\n\t\tres.WriteHeader(http.StatusOK)\n\t\tres.Write([]byte(Page(`\n            <form action=\"` + WithBase(\"/api/session/auth/\"+getParams) + `\" method=\"post\"></form>\n            <script>document.querySelector(\"form\").submit();</script>\n        `)))\n\tcase \"password_only\":\n\t\tres.WriteHeader(http.StatusOK)\n\t\tres.Write([]byte(Page(`\n            <form action=\"` + WithBase(\"/api/session/auth/\"+getParams) + `\" method=\"post\">\n                <label>\n                    <input type=\"password\" name=\"password\" value=\"\" placeholder=\"Password\" />\n                </label>\n                <button>CONNECT</button>\n            </form>\n        `)))\n\tcase \"username_and_password\":\n\t\tres.WriteHeader(http.StatusOK)\n\t\tres.Write([]byte(Page(`\n            <form action=\"` + WithBase(\"/api/session/auth/\"+getParams) + `\" method=\"post\">\n                <label>\n                    <input type=\"text\" name=\"user\" value=\"\" placeholder=\"User\" />\n                </label>\n                <label>\n                    <input type=\"password\" name=\"password\" value=\"\" placeholder=\"Password\" />\n                </label>\n                <button>CONNECT</button>\n            </form>\n        `)))\n\tdefault:\n\t\tres.WriteHeader(http.StatusNotFound)\n\t\tres.Write([]byte(Page(fmt.Sprintf(\"Unknown strategy: '%s'\", idpParams[\"strategy\"]))))\n\t}\n\treturn nil\n}\n\nfunc (this Passthrough) Callback(formData map[string]string, idpParams map[string]string, res http.ResponseWriter) (map[string]string, error) {\n\treturn map[string]string{\n\t\t\"user\":     formData[\"user\"],\n\t\t\"password\": formData[\"password\"],\n\t}, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_wordpress/README.md",
    "content": "# What is this?\n\nThis plugin lets you use a WordPress site as an identity provider. It surely is unconventional, but it enables access patterns such as read and write access to /var/www for editors, read-only access for subscribers, or secure links to S3 buckets, ...\n\nWith this plugin, users are authenticated against WordPress, and RBAC rules control who can do what and where.\n"
  },
  {
    "path": "server/plugin/plg_authenticate_wordpress/helper.go",
    "content": "package plg_authenticate_wordpress\n\nimport (\n\t\"strings\"\n)\n\nfunc extractXMLValue(xml, key string) string {\n\tstart := strings.Index(xml, \"<name>\"+key+\"</name>\")\n\tif start == -1 {\n\t\treturn \"\"\n\t}\n\tstart = strings.Index(xml[start:], \"<string>\")\n\tif start == -1 {\n\t\treturn \"\"\n\t}\n\tstart += len(\"<string>\")\n\tend := strings.Index(xml[start:], \"</string>\")\n\tif end == -1 {\n\t\treturn \"\"\n\t}\n\treturn xml[start : start+end]\n}\n\nfunc extractXMLArray(xml, key string) []string {\n\tstart := strings.Index(xml, \"<name>\"+key+\"</name>\")\n\tif start == -1 {\n\t\treturn []string{}\n\t}\n\tstart = strings.Index(xml[start:], \"<array>\")\n\tif start == -1 {\n\t\treturn []string{}\n\t}\n\tend := strings.Index(xml[start:], \"</array>\")\n\tif end == -1 {\n\t\treturn []string{}\n\t}\n\n\tarrayXML := xml[start : start+end]\n\tvar values []string\n\n\tfor {\n\t\tvalStart := strings.Index(arrayXML, \"<string>\")\n\t\tif valStart == -1 {\n\t\t\tbreak\n\t\t}\n\t\tvalStart += len(\"<string>\")\n\t\tvalEnd := strings.Index(arrayXML[valStart:], \"</string>\")\n\t\tif valEnd == -1 {\n\t\t\tbreak\n\t\t}\n\t\tvalues = append(values, arrayXML[valStart:valStart+valEnd])\n\t\tarrayXML = arrayXML[valStart+valEnd:]\n\t}\n\n\treturn values\n}\n"
  },
  {
    "path": "server/plugin/plg_authenticate_wordpress/index.go",
    "content": "package plg_authenticate_wordpress\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/ctrl\"\n)\n\nfunc init() {\n\tHooks.Register.AuthenticationMiddleware(\"wordpress\", Wordpress{})\n}\n\ntype Wordpress struct{}\n\nfunc (this Wordpress) Setup() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName: \"banner\",\n\t\t\t\tType: \"hidden\",\n\t\t\t\tDescription: `Make it possible for your Wordpress users to authenticate to your storage.\nIf valid, you will have the following attributes available about the user in the attribute mapping section:\n{{ .user }}, {{ .email }}, {{ .roles }}, {{ .id }}, and {{ .password }}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"wordpress\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"url\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tDescription: `The URL of your wordpress instance. Eg: http://localhost`,\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Wordpress) EntryPoint(idpParams map[string]string, req *http.Request, res http.ResponseWriter) error {\n\tres.Header().Set(\"Content-Type\", \"text/html; charset=utf-8\")\n\tres.WriteHeader(http.StatusOK)\n\tres.Write([]byte(Page(`\n      <form method=\"post\" class=\"component_middleware\">\n        <label>\n          <input type=\"text\" name=\"user\" value=\"\" placeholder=\"User\" autocorrect=\"off\" autocapitalize=\"off\" />\n        </label>\n        <label>\n          <input type=\"password\" name=\"password\" value=\"\" placeholder=\"Password\" />\n        </label>\n        <button>CONNECT</button>\n        <style>\n          form { padding-top: 10vh; }\n        </style>\n      </form>`)))\n\treturn nil\n}\n\nfunc (this Wordpress) Callback(formData map[string]string, idpParams map[string]string, res http.ResponseWriter) (map[string]string, error) {\n\tusername := formData[\"user\"]\n\tpassword := formData[\"password\"]\n\twpURL := strings.TrimRight(idpParams[\"url\"], \"/\")\n\tif username == \"\" || password == \"\" || wpURL == \"\" {\n\t\treturn nil, ErrAuthenticationFailed\n\t}\n\txmlBody, err := TmplExec(\n\t\t`<?xml version=\"1.0\"?>\n         <methodCall>\n             <methodName>wp.getProfile</methodName>\n             <params>\n                 <param><value><string>1</string></value></param>\n                 <param><value><string>{{ .user }}</string></value></param>\n                 <param><value><string>{{ .password }}</string></value></param>\n             </params>\n         </methodCall>`,\n\t\tmap[string]string{\n\t\t\t\"user\":     username,\n\t\t\t\"password\": password,\n\t\t},\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq, err := http.NewRequest(\"POST\", wpURL+\"/xmlrpc.php\", strings.NewReader(xmlBody))\n\tif err != nil {\n\t\treturn nil, NewError(\"Failed to create request: \"+err.Error(), 500)\n\t}\n\treq.Header.Set(\"Content-Type\", \"text/xml\")\n\tresp, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbodyS := string(body)\n\tif strings.Contains(bodyS, \"<fault>\") {\n\t\treturn nil, ErrAuthenticationFailed\n\t}\n\tuser := extractXMLValue(bodyS, \"username\")\n\tif user == \"\" {\n\t\treturn nil, ErrAuthenticationFailed\n\t}\n\tout := map[string]string{\n\t\t\"user\":     user,\n\t\t\"password\": password,\n\t}\n\tif email := extractXMLValue(bodyS, \"email\"); email != \"\" {\n\t\tout[\"email\"] = email\n\t}\n\tif userID := extractXMLValue(bodyS, \"user_id\"); userID != \"\" {\n\t\tout[\"id\"] = userID\n\t}\n\tout[\"role\"] = strings.Join(extractXMLArray(bodyS, \"roles\"), \", \")\n\treturn out, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_authorisation_example/index.go",
    "content": "package plg_authorisation_example\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.AuthorisationMiddleware(AuthM{})\n}\n\ntype AuthM struct{}\n\nfunc (this AuthM) Ls(ctx *App, path string) error {\n\tLog.Stdout(\"LS %+v\", ctx.Session)\n\treturn nil\n}\n\nfunc (this AuthM) Cat(ctx *App, path string) error {\n\tLog.Stdout(\"CAT %+v\", ctx.Session)\n\treturn nil\n}\n\nfunc (this AuthM) Mkdir(ctx *App, path string) error {\n\tLog.Stdout(\"MKDIR %+v\", ctx.Session)\n\treturn ErrNotAllowed\n}\n\nfunc (this AuthM) Rm(ctx *App, path string) error {\n\tLog.Stdout(\"RM %+v\", ctx.Session)\n\treturn ErrNotAllowed\n}\n\nfunc (this AuthM) Mv(ctx *App, from string, to string) error {\n\tLog.Stdout(\"MV %+v\", ctx.Session)\n\treturn ErrNotAllowed\n}\n\nfunc (this AuthM) Save(ctx *App, path string) error {\n\tLog.Stdout(\"SAVE %+v\", ctx.Session)\n\treturn ErrNotAllowed\n}\n\nfunc (this AuthM) Touch(ctx *App, path string) error {\n\tLog.Stdout(\"TOUCH %+v\", ctx.Session)\n\treturn ErrNotAllowed\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_artifactory/index.go",
    "content": "package plg_backend_artifactory\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc init() {\n\tBackend.Register(\"artifactory\", ArtifactoryStorage{})\n}\n\ntype ArtifactoryStorage struct {\n\tinstance string\n\ttoken    string\n}\n\nfunc (this ArtifactoryStorage) Init(params map[string]string, app *App) (IBackend, error) {\n\tif strings.HasPrefix(params[\"instance\"], \"https://\") == false &&\n\t\tstrings.HasPrefix(params[\"instance\"], \"http://\") == false {\n\t\treturn this, ErrNotValid\n\t}\n\tthis.token = params[\"token\"]\n\tthis.instance = strings.TrimSuffix(strings.TrimSuffix(params[\"instance\"], \"/\"), \"/artifactory\")\n\treturn this, nil\n}\n\nfunc (this ArtifactoryStorage) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"artifactory\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"instance\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Instance URL\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"token\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Access Token\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Path\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this ArtifactoryStorage) Meta(path string) Metadata {\n\tif path == \"/\" {\n\t\treturn Metadata{\n\t\t\tCanCreateFile: NewBool(false),\n\t\t\tCanRename:     NewBool(false),\n\t\t\tCanMove:       NewBool(false),\n\t\t\tCanUpload:     NewBool(false),\n\t\t}\n\t}\n\treturn Metadata{}\n}\n\nfunc (this ArtifactoryStorage) Ls(path string) ([]os.FileInfo, error) {\n\tp := this.artifactoryPath(path)\n\tvar (\n\t\treq   *http.Request\n\t\terr   error\n\t\tparse func([]byte) ([]os.FileInfo, error)\n\t)\n\tif p.repository == \"\" {\n\t\treq, err = http.NewRequest(\n\t\t\t\"GET\", fmt.Sprintf(\"%s/artifactory/api/repositories\", this.instance), nil,\n\t\t)\n\t\tparse = func(jsonStr []byte) ([]os.FileInfo, error) {\n\t\t\tartifactoryResponse := []struct {\n\t\t\t\tKey         string `json:\"key\"`\n\t\t\t\tDescription string `json:\"description\"`\n\t\t\t\tType        string `json:\"type\"`\n\t\t\t\tUrl         string `json:\"url\"`\n\t\t\t\tPackageType string `json:\"packageType\"`\n\t\t\t}{}\n\t\t\tif err := json.Unmarshal(jsonStr, &artifactoryResponse); err != nil {\n\t\t\t\tLog.Warning(\"plg_backend_artifactory::ls unmarshall %s\", err.Error())\n\t\t\t\treturn nil, ErrNotValid\n\t\t\t}\n\t\t\tfiles := make([]os.FileInfo, len(artifactoryResponse))\n\t\t\tfor i, artifactoryFile := range artifactoryResponse {\n\t\t\t\tfiles[i] = File{\n\t\t\t\t\tFName: artifactoryFile.Key,\n\t\t\t\t\tFType: \"directory\",\n\t\t\t\t\tFTime: 0,\n\t\t\t\t\tFSize: 0,\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn files, nil\n\t\t}\n\t} else {\n\t\treq, err = http.NewRequest(\n\t\t\t\"GET\",\n\t\t\tfmt.Sprintf(\"%s/artifactory/api/storage%s?list&deep=0&listFolders=1\", this.instance, path),\n\t\t\tnil,\n\t\t)\n\t\tparse = func(jsonStr []byte) ([]os.FileInfo, error) {\n\t\t\tartifactoryResponse := struct {\n\t\t\t\tFiles []struct {\n\t\t\t\t\tUri          string `json:\"uri\"`\n\t\t\t\t\tSize         int64  `json:\"size\"`\n\t\t\t\t\tLastModified string `json:\"lastModified\"`\n\t\t\t\t\tFolder       bool   `json:\"folder\"`\n\t\t\t\t\tSha1         string `json:\"sha1\"`\n\t\t\t\t\tSha2         string `json:\"sha2\"`\n\t\t\t\t} `json:\"files\"`\n\t\t\t\tCreationTime string `json:\"created\"`\n\t\t\t\tUri          string `json:\"uri\"`\n\t\t\t}{}\n\t\t\tif err := json.Unmarshal(jsonStr, &artifactoryResponse); err != nil {\n\t\t\t\tLog.Warning(\"plg_backend_artifactory::ls unmarshall %s\", err.Error())\n\t\t\t\treturn nil, ErrNotValid\n\t\t\t}\n\n\t\t\tfiles := make([]os.FileInfo, len(artifactoryResponse.Files))\n\t\t\tfor i, artifactoryFile := range artifactoryResponse.Files {\n\t\t\t\tfiles[i] = File{\n\t\t\t\t\tFName: filepath.Base(artifactoryFile.Uri),\n\t\t\t\t\tFType: func() string {\n\t\t\t\t\t\tif artifactoryFile.Folder {\n\t\t\t\t\t\t\treturn \"directory\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn \"file\"\n\t\t\t\t\t}(),\n\t\t\t\t\tFTime: func() int64 {\n\t\t\t\t\t\tt, err := time.Parse(\"2006-01-02T15:04:05.000Z\", artifactoryFile.LastModified)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn 0\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn t.Unix()\n\t\t\t\t\t}(),\n\t\t\t\t\tFSize: artifactoryFile.Size,\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn files, nil\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Add(\"Authorization\", \"Bearer \"+this.token)\n\tres, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tjsonStr, err := io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\tLog.Debug(\"plg_backend_artifactory::ls readall data[%s] status[%d]\", string(jsonStr), res.StatusCode)\n\t\treturn []os.FileInfo{}, ErrNotValid\n\t} else if res.StatusCode != 200 {\n\t\tLog.Debug(\"plg_backend_artifactory::ls nok status[%d] data[%s]\", res.StatusCode, string(jsonStr))\n\t\treturn []os.FileInfo{}, ErrNotValid\n\t}\n\treturn parse(jsonStr)\n}\n\nfunc (this ArtifactoryStorage) Stat(path string) (os.FileInfo, error) {\n\treturn nil, ErrNotImplemented\n}\n\nfunc (this ArtifactoryStorage) Cat(path string) (io.ReadCloser, error) {\n\t// https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-FileInfo\n\tif p := this.artifactoryPath(path); p.path == \"\" {\n\t\treturn nil, ErrNotValid\n\t}\n\treq, err := http.NewRequest(\n\t\t\"GET\", fmt.Sprintf(\"%s/artifactory/api/storage%s\", this.instance, path), nil,\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Add(\"Authorization\", \"Bearer \"+this.token)\n\tres, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tjsonStr, err := io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\tLog.Debug(\"plg_backend_artifactory::cat readall data[%s] status[%d]\", string(jsonStr), res.StatusCode)\n\t\treturn nil, ErrNotValid\n\t} else if res.StatusCode != 200 {\n\t\tLog.Debug(\"plg_backend_artifactory::cat nok status[%d] data[%s]\", res.StatusCode, string(jsonStr))\n\t\treturn nil, ErrNotValid\n\t}\n\tartifactoryResponse := struct {\n\t\tRepo         string `json:\"repo\"`\n\t\tPath         string `json:\"path\"`\n\t\tCreated      string `json:\"created\"`\n\t\tCreatedBy    string `json:\"createdBy\"`\n\t\tLastModified string `json:\"lastModified\"`\n\t\tModifiedBy   string `json:\"modifiedBy\"`\n\t\tLastUpdated  string `json:\"lastUpdated\"`\n\t\tDownloadLink string `json:\"downloadUri\"`\n\t\tMimeType     string `json:\"mimeType\"`\n\t\tSize         string `json:\"size\"`\n\t\tChecksums    struct {\n\t\t\tSha1   string `json:\"sha1\"`\n\t\t\tMd5    string `json:\"md5\"`\n\t\t\tSha256 string `json:\"sha256\"`\n\t\t} `json:\"checksums\"`\n\t\tOriginalChecksums struct {\n\t\t\tSha1   string `json:\"sha1\"`\n\t\t\tMd5    string `json:\"md5\"`\n\t\t\tSha256 string `json:\"sha256\"`\n\t\t} `json:\"originalChecksums\"`\n\t\tUri string `json:\"uri\"`\n\t}{}\n\tif err := json.Unmarshal(jsonStr, &artifactoryResponse); err != nil {\n\t\tLog.Warning(\"plg_backend_artifactory::ls unmarshall %s\", err.Error())\n\t\treturn nil, ErrNotValid\n\t}\n\treq, err = http.NewRequest(\"GET\", artifactoryResponse.DownloadLink, nil)\n\treq.Header.Add(\"Authorization\", \"Bearer \"+this.token)\n\tres, err = HTTPClient.Do(req)\n\treturn res.Body, err\n}\n\nfunc (this ArtifactoryStorage) Mkdir(path string) error {\n\tp := this.artifactoryPath(path)\n\tvar (\n\t\treq         *http.Request\n\t\terr         error\n\t\tvalidStatus int\n\t)\n\tif p.path == \"\" { // https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-CreateRepository\n\t\treq, err = http.NewRequest(\n\t\t\t\"PUT\",\n\t\t\tfmt.Sprintf(\"%s/artifactory/api/repositories/%s\", this.instance, p.repository),\n\t\t\tbytes.NewReader([]byte(`{\"rclass\" : \"local\"}`)),\n\t\t)\n\t\tvalidStatus = 200\n\t} else { // https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-CreateDirectory\n\t\treq, err = http.NewRequest(\n\t\t\t\"PUT\", fmt.Sprintf(\"%s/artifactory%s\", this.instance, path),\n\t\t\tbytes.NewReader([]byte(fmt.Sprintf(`{\n              \"key\": \"%s%s\",\n              \"repo\": \"%s\",\n              \"path\": \"%s\",\n              \"created\": \"%s\"\n    \t\t}`, this.instance, path, p.repository, path,\n\t\t\t))),\n\t\t)\n\t\tvalidStatus = 201\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\treq.Header.Add(\"Authorization\", \"Bearer \"+this.token)\n\tres, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tjsonStr, err := io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\tLog.Debug(\"plg_backend_artifactory::mkdir readall status[%d] data[%s]\", res.StatusCode, string(jsonStr))\n\t\treturn ErrNotValid\n\t} else if res.StatusCode != validStatus {\n\t\tLog.Debug(\"plg_backend_artifactory::mkdir nok url[%s] status[%d] data[%s]\", req.URL, res.StatusCode, string(jsonStr))\n\t\treturn ErrNotValid\n\t}\n\treturn nil\n}\n\nfunc (this ArtifactoryStorage) Rm(path string) error {\n\tvar (\n\t\treq         *http.Request\n\t\terr         error\n\t\tvalidStatus int\n\t)\n\tp := this.artifactoryPath(path)\n\tif p.path == \"\" { // https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-DeleteRepository\n\t\treq, err = http.NewRequest(\n\t\t\t\"DELETE\", fmt.Sprintf(\"%s/artifactory/api/repositories/%s\", this.instance, p.repository), nil,\n\t\t)\n\t\tvalidStatus = 200\n\t} else { // https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-DeleteItem\n\t\treq, err = http.NewRequest(\n\t\t\t\"DELETE\", fmt.Sprintf(\"%s/artifactory%s\", this.instance, path), nil,\n\t\t)\n\t\tvalidStatus = 204\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\treq.Header.Add(\"Authorization\", \"Bearer \"+this.token)\n\tres, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tjsonStr, err := io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\tLog.Debug(\"plg_backend_artifactory::rm readall status[%d] data[%s]\", res.StatusCode, string(jsonStr))\n\t\treturn ErrNotValid\n\t} else if res.StatusCode != validStatus {\n\t\tLog.Debug(\"plg_backend_artifactory::rm nok url[%s] status[%d] data[%s]\", req.URL, res.StatusCode, string(jsonStr))\n\t\treturn ErrNotValid\n\t}\n\treturn nil\n}\n\nfunc (this ArtifactoryStorage) Mv(from, to string) error {\n\t// https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-MoveItem\n\treq, err := http.NewRequest(\n\t\t\"POST\",\n\t\tfmt.Sprintf(\n\t\t\t\"%s/artifactory/api/move%s?to=%s\",\n\t\t\tthis.instance, from, url.QueryEscape(to),\n\t\t), nil,\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq.Header.Add(\"Authorization\", \"Bearer \"+this.token)\n\tres, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tjsonStr, err := io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\tLog.Debug(\"plg_backend_artifactory::mv readall status[%d] data[%s]\", res.StatusCode, string(jsonStr))\n\t\treturn ErrNotValid\n\t} else if res.StatusCode != 200 {\n\t\tLog.Debug(\"plg_backend_artifactory::mv nok url[%s] status[%d] data[%s]\", req.URL, res.StatusCode, string(jsonStr))\n\t\treturn ErrNotValid\n\t}\n\treturn nil\n}\n\nfunc (this ArtifactoryStorage) Save(path string, content io.Reader) error {\n\t// https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-Example-DeployinganArtifact\n\tp := this.artifactoryPath(path)\n\tif p.path == \"\" {\n\t\treturn ErrNotValid\n\t}\n\treq, err := http.NewRequest(\n\t\t\"PUT\",\n\t\tfmt.Sprintf(\"%s/artifactory%s\", this.instance, path),\n\t\tcontent,\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq.Header.Add(\"Authorization\", \"Bearer \"+this.token)\n\tres, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tjsonStr, err := io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\tLog.Debug(\"plg_backend_artifactory::save readall status[%d] data[%s]\", res.StatusCode, string(jsonStr))\n\t\treturn ErrNotValid\n\t} else if res.StatusCode != 201 {\n\t\tLog.Debug(\"plg_backend_artifactory::save nok url[%s] status[%d] data[%s]\", req.URL, res.StatusCode, string(jsonStr))\n\t\treturn ErrNotValid\n\t}\n\treturn nil\n}\n\nfunc (this ArtifactoryStorage) Touch(path string) error {\n\treturn this.Save(path, bytes.NewReader([]byte(\"\")))\n}\n\nfunc (this ArtifactoryStorage) artifactoryPath(_path string) (p struct {\n\trepository string\n\tpath       string\n}) {\n\tsp := strings.Split(_path, \"/\")\n\tif len(sp) > 1 {\n\t\tp.repository = sp[1]\n\t}\n\tif len(sp) > 2 {\n\t\tp.path = strings.Join(sp[2:], \"/\")\n\t}\n\treturn p\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_azure/index.go",
    "content": "package plg_backend_azure\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container\"\n)\n\ntype AzureBlob struct {\n\tclient *azblob.Client\n\tctx    context.Context\n}\n\nfunc init() {\n\tBackend.Register(\"azure\", &AzureBlob{})\n}\n\nfunc (this *AzureBlob) Init(params map[string]string, app *App) (IBackend, error) {\n\tcred, err := container.NewSharedKeyCredential(params[\"account_name\"], params[\"account_key\"])\n\tif err != nil {\n\t\tLog.Debug(\"plg_backend_azure::new_cred_error %s\", err.Error())\n\t\treturn nil, ErrAuthenticationFailed\n\t}\n\tserviceURL := fmt.Sprintf(\"https://%s.blob.core.windows.net/\", params[\"account_name\"])\n\tclient, err := azblob.NewClientWithSharedKeyCredential(serviceURL, cred, nil)\n\tif err != nil {\n\t\tLog.Debug(\"plg_backend_azure::new_client_error %s\", err.Error())\n\t\treturn nil, ErrAuthenticationFailed\n\t}\n\treturn &AzureBlob{client, app.Context}, nil\n}\n\nfunc (this *AzureBlob) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"azure\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"account_name\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Account Name\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"account_key\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"Account Key\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Path\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this *AzureBlob) Ls(path string) ([]os.FileInfo, error) {\n\tfiles := make([]os.FileInfo, 0)\n\tap := this.path(path)\n\n\tif ap.containerName == \"\" {\n\t\tpager := this.client.NewListContainersPager(nil)\n\t\tfor pager.More() {\n\t\t\tresp, err := pager.NextPage(this.ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn files, err\n\t\t\t}\n\t\t\tfor _, blob := range resp.ListContainersSegmentResponse.ContainerItems {\n\t\t\t\tfiles = append(files, File{\n\t\t\t\t\tFName: *blob.Name,\n\t\t\t\t\tFType: \"directory\",\n\t\t\t\t\tFTime: blob.Properties.LastModified.Unix(),\n\t\t\t\t\tFSize: -1,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\treturn files, nil\n\t}\n\n\tclient := this.client.ServiceClient().NewContainerClient(ap.containerName)\n\tpager := client.NewListBlobsHierarchyPager(\"/\", &container.ListBlobsHierarchyOptions{\n\t\tPrefix: &ap.blobName,\n\t})\n\tfor pager.More() {\n\t\tresp, err := pager.NextPage(this.ctx)\n\t\tif err != nil {\n\t\t\treturn files, err\n\t\t}\n\t\tfor _, blob := range resp.ListBlobsHierarchySegmentResponse.Segment.BlobPrefixes {\n\t\t\tif *blob.Name == \"/\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfiles = append(files, File{\n\t\t\t\tFName: filepath.Base(*blob.Name),\n\t\t\t\tFType: \"directory\",\n\t\t\t\tFTime: -1,\n\t\t\t\tFSize: -1,\n\t\t\t})\n\t\t}\n\t\tfor _, blob := range resp.ListBlobsHierarchySegmentResponse.Segment.BlobItems {\n\t\t\tfiles = append(files, File{\n\t\t\t\tFName: filepath.Base(*blob.Name),\n\t\t\t\tFType: \"file\",\n\t\t\t\tFTime: blob.Properties.LastModified.Unix(),\n\t\t\t\tFSize: *blob.Properties.ContentLength,\n\t\t\t})\n\t\t}\n\t}\n\treturn files, nil\n}\n\nfunc (this AzureBlob) Cat(path string) (io.ReadCloser, error) {\n\tap := this.path(path)\n\treturn &azureFilecat{\n\t\toffset: 0,\n\t\tctx:    this.ctx,\n\t\tap:     ap,\n\t\tclient: this.client,\n\t\treader: nil,\n\t}, nil\n}\n\ntype azureFilecat struct {\n\toffset int64\n\tctx    context.Context\n\tap     azurePath\n\treader io.ReadCloser\n\tclient *azblob.Client\n\tmu     sync.Mutex\n}\n\nfunc (this *azureFilecat) Read(p []byte) (n int, err error) {\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\tif this.reader == nil {\n\t\tresp, err := this.client.DownloadStream(\n\t\t\tthis.ctx,\n\t\t\tthis.ap.containerName,\n\t\t\tthis.ap.blobName,\n\t\t\t&azblob.DownloadStreamOptions{\n\t\t\t\tRange: azblob.HTTPRange{\n\t\t\t\t\tOffset: this.offset,\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tthis.reader = resp.Body\n\t}\n\treturn this.reader.Read(p)\n}\n\nfunc (this *azureFilecat) Seek(offset int64, whence int) (int64, error) {\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\tif offset < 0 {\n\t\treturn this.offset, os.ErrInvalid\n\t}\n\n\tswitch whence {\n\tcase io.SeekStart:\n\tcase io.SeekCurrent:\n\t\toffset += this.offset\n\tcase io.SeekEnd:\n\t\tprops, err := this.client.ServiceClient().NewContainerClient(this.ap.containerName).NewBlockBlobClient(this.ap.blobName).GetProperties(this.ctx, nil)\n\t\tif err != nil {\n\t\t\treturn this.offset, err\n\t\t}\n\t\toffset += *props.ContentLength\n\tdefault:\n\t\treturn this.offset, ErrNotImplemented\n\t}\n\n\tthis.offset = offset\n\treturn this.offset, nil\n}\n\nfunc (this *azureFilecat) Close() error {\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\tif this.reader == nil {\n\t\treturn nil\n\t}\n\treturn this.reader.Close()\n}\n\nfunc (this AzureBlob) Stat(path string) (os.FileInfo, error) {\n\tap := this.path(path)\n\tprops, err := this.client.ServiceClient().NewContainerClient(ap.containerName).NewBlockBlobClient(ap.blobName).GetProperties(this.ctx, nil)\n\tif err == nil {\n\t\tif props.ContentLength == nil || props.LastModified == nil {\n\t\t\treturn nil, ErrNotValid\n\t\t}\n\t\treturn File{\n\t\t\tFName: filepath.Base(path),\n\t\t\tFSize: *props.ContentLength,\n\t\t\tFTime: (*props.LastModified).Unix(),\n\t\t}, nil\n\t\treturn nil, err\n\t}\n\treturn File{\n\t\tFName: filepath.Base(path),\n\t\tFType: \"directory\",\n\t\tFTime: -1,\n\t}, nil\n}\n\nfunc (this *AzureBlob) Mkdir(path string) error {\n\tap := this.path(path)\n\tif ap.blobName == \"\" {\n\t\t_, err := this.client.CreateContainer(this.ctx, ap.containerName, nil)\n\t\treturn err\n\t}\n\t_, err := this.client.UploadBuffer(this.ctx, ap.containerName, EnforceDirectory(ap.blobName)+\".keep\", []byte(\"\"), nil)\n\treturn err\n}\n\nfunc (this *AzureBlob) Rm(path string) error {\n\tap := this.path(path)\n\tif ap.blobName == \"\" {\n\t\t_, err := this.client.DeleteContainer(this.ctx, ap.containerName, nil)\n\t\treturn err\n\t}\n\tfinfo, err := this.Stat(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif finfo.IsDir() == false {\n\t\t_, err := this.client.DeleteBlob(this.ctx, ap.containerName, ap.blobName, nil)\n\t\treturn err\n\t}\n\tpager := this.client.NewListBlobsFlatPager(ap.containerName, &container.ListBlobsFlatOptions{\n\t\tInclude: container.ListBlobsInclude{Snapshots: true, Versions: true},\n\t\tPrefix:  &ap.blobName,\n\t})\n\tfor pager.More() {\n\t\tresp, err := pager.NextPage(this.ctx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, blob := range resp.Segment.BlobItems {\n\t\t\t_, err := this.client.DeleteBlob(context.Background(), ap.containerName, *blob.Name, nil)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (this *AzureBlob) Mv(from string, to string) error {\n\tapFrom := this.path(from)\n\tapTo := this.path(to)\n\tif apFrom.containerName != apTo.containerName {\n\t\treturn ErrNotSupported\n\t}\n\tfinfo, err := this.Stat(from)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// CASE 1: Move a file\n\tif finfo.IsDir() == false {\n\t\tsourceClient := this.client.ServiceClient().NewContainerClient(apFrom.containerName).NewBlockBlobClient(apFrom.blobName)\n\t\tdestClient := this.client.ServiceClient().NewContainerClient(apTo.containerName).NewBlockBlobClient(apTo.blobName)\n\t\tif _, err := destClient.StartCopyFromURL(\n\t\t\tthis.ctx,\n\t\t\tsourceClient.URL(),\n\t\t\tnil,\n\t\t); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = this.client.DeleteBlob(this.ctx, apFrom.containerName, apFrom.blobName, nil)\n\t\treturn err\n\t}\n\n\t// CASE 2: Move a directory\n\tpager := this.client.NewListBlobsFlatPager(apFrom.containerName, &container.ListBlobsFlatOptions{\n\t\tInclude: container.ListBlobsInclude{Snapshots: true, Versions: true},\n\t\tPrefix: &apFrom.blobName,\n\t})\n\tfor pager.More() {\n\t\tresp, err := pager.NextPage(this.ctx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, blob := range resp.Segment.BlobItems {\n\t\t\tif err := this.ctx.Err(); err != nil {\n\t\t\t\treturn err\n\t\t\t} else if blob.Name == nil {\n\t\t\t\treturn ErrNotValid\n\t\t\t}\n\t\t\toldName := *blob.Name\n\t\t\tnewName := strings.Replace(oldName, apFrom.blobName, apTo.blobName, 1)\n\t\t\tsourceClient := this.client.ServiceClient().NewContainerClient(apFrom.containerName).NewBlockBlobClient(oldName)\n\t\t\tdestClient := this.client.ServiceClient().NewContainerClient(apTo.containerName).NewBlockBlobClient(newName)\n\t\t\tif _, err := destClient.StartCopyFromURL(\n\t\t\t\tcontext.Background(),\n\t\t\t\tsourceClient.URL(),\n\t\t\t\tnil,\n\t\t\t); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t_, err = this.client.DeleteBlob(context.Background(), apFrom.containerName, oldName, nil)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (this *AzureBlob) Touch(path string) error {\n\treturn this.Save(path, strings.NewReader(\"\"))\n}\n\nfunc (this *AzureBlob) Save(path string, file io.Reader) error {\n\tap := this.path(path)\n\t_, err := this.client.UploadStream(\n\t\tthis.ctx, ap.containerName, ap.blobName, file,\n\t\tnil,\n\t)\n\treturn err\n}\n\nfunc (this *AzureBlob) Meta(path string) Metadata {\n\tif path == \"/\" {\n\t\treturn Metadata{\n\t\t\tCanCreateFile: NewBool(false),\n\t\t\tCanRename:     NewBool(false),\n\t\t\tCanMove:       NewBool(false),\n\t\t\tCanUpload:     NewBool(false),\n\t\t}\n\t}\n\treturn Metadata{}\n}\n\ntype azurePath struct {\n\tcontainerName string\n\tblobName      string\n}\n\nfunc (this AzureBlob) path(path string) azurePath {\n\tap := azurePath{}\n\tpath = strings.TrimLeft(path, \"/\")\n\tsp := strings.SplitN(path, \"/\", 2)\n\tif len(sp) != 2 {\n\t\treturn ap\n\t}\n\tap.containerName = sp[0]\n\tap.blobName = sp[1]\n\treturn ap\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_backblaze/index.go",
    "content": "package plg_backend_backblaze\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha1\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar backblaze_cache AppCache\n\ntype Backblaze struct {\n\tparams      map[string]string\n\tBuckets     map[string]string\n\tApiUrl      string `json:\"apiUrl\"`\n\tDownloadUrl string `json:\"downloadUrl\"`\n\tAccountId   string `json:\"accountId\"`\n\tToken       string `json:\"authorizationToken\"`\n\tStatus      int    `json:\"status\"`\n}\n\ntype BackblazeError struct {\n\tCode    string `json:\"code\"`\n\tMessage string `json:\"message\"`\n\tStatus  int    `json:\"status\"`\n}\n\nfunc init() {\n\tBackend.Register(\"backblaze\", Backblaze{})\n\tbackblaze_cache = NewAppCache()\n}\n\nfunc (this Backblaze) Init(params map[string]string, app *App) (IBackend, error) {\n\tthis.params = params\n\n\t// By default backblaze required quite a few API calls to just find the data that's under a given bucket\n\t// This would result in a slow application hence we are caching everyting that's in the hot path\n\tif obj := backblaze_cache.Get(params); obj != nil {\n\t\treturn obj.(*Backblaze), nil\n\t}\n\n\t// To perform some query, we need to first know things like where we will have to query, get a token, ...\n\tres, err := this.request(\"GET\", \"https://api.backblazeb2.com/b2api/v2/b2_authorize_account\", nil, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbody, err := io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := json.Unmarshal(body, &this); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Extract bucket related information as backblaze use bucketId as an identifer\n\t// BucketId is just some internal ref as people expect to see the bucketName\n\tres, err = this.request(\n\t\t\"POST\",\n\t\tthis.ApiUrl+\"/b2api/v2/b2_list_buckets\",\n\t\tstrings.NewReader(fmt.Sprintf(\n\t\t\t`{\"accountId\":\"%s\"}`,\n\t\t\tthis.AccountId,\n\t\t)),\n\t\tnil,\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbody, err = io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar buckets struct {\n\t\tBuckets []struct {\n\t\t\tBucketId   string `json:\"bucketId\"`\n\t\t\tBucketName string `json:\"bucketName\"`\n\t\t} `json:\"buckets\"`\n\t}\n\tif err = json.Unmarshal(body, &buckets); err != nil {\n\t\treturn nil, err\n\t}\n\tthis.Buckets = make(map[string]string, len(buckets.Buckets))\n\tfor i := range buckets.Buckets {\n\t\tthis.Buckets[buckets.Buckets[i].BucketName] = buckets.Buckets[i].BucketId\n\t}\n\tdelete(params, \"password\")\n\tbackblaze_cache.Set(params, &this)\n\treturn this, nil\n}\n\nfunc (this Backblaze) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"backblaze\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"username\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"KeyID\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"password\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"applicationKey\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Backblaze) Ls(path string) ([]os.FileInfo, error) {\n\tif path == \"/\" {\n\t\tfiles := make([]os.FileInfo, 0, len(this.Buckets))\n\t\tfor key := range this.Buckets {\n\t\t\tfiles = append(files, File{\n\t\t\t\tFName: key,\n\t\t\t\tFType: \"directory\",\n\t\t\t})\n\t\t}\n\t\treturn files, nil\n\t}\n\n\t// prepare the query\n\tp := this.path(path)\n\treqJSON, _ := json.Marshal(struct {\n\t\tBucketId     string `json:\"bucketId\"`\n\t\tDelimiter    string `json:\"delimiter\"`\n\t\tMaxFileCount int    `json:\"maxFileCount\"`\n\t\tPrefix       string `json:\"prefix\"`\n\t}{p.BucketId, \"/\", 10000, p.Prefix})\n\tres, err := this.request(\n\t\t\"POST\",\n\t\tthis.ApiUrl+\"/b2api/v2/b2_list_file_names\",\n\t\tbytes.NewReader(reqJSON),\n\t\tnil,\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbody, err := io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar resBody struct {\n\t\tFiles []struct {\n\t\t\tFType string `json:\"action\"`\n\t\t\tSize  int64  `json:\"contentLength\"`\n\t\t\tName  string `json:\"fileName\"`\n\t\t\tTime  int64  `json:\"uploadTimestamp\"`\n\t\t} `json:\"files\"`\n\t}\n\tif err = json.Unmarshal(body, &resBody); err != nil {\n\t\treturn nil, err\n\t}\n\tfiles := make([]os.FileInfo, len(resBody.Files))\n\tfor i := range resBody.Files {\n\t\tfiles[i] = File{\n\t\t\tFName: strings.TrimSuffix(strings.TrimPrefix(resBody.Files[i].Name, p.Prefix), \"/\"),\n\t\t\tFType: func() string {\n\t\t\t\tif resBody.Files[i].FType == \"folder\" {\n\t\t\t\t\treturn \"directory\"\n\t\t\t\t}\n\t\t\t\treturn \"file\"\n\t\t\t}(),\n\t\t\tFSize: resBody.Files[i].Size,\n\t\t\tFTime: resBody.Files[i].Time / 1000,\n\t\t}\n\t}\n\treturn files, nil\n}\n\nfunc (this Backblaze) Cat(path string) (io.ReadCloser, error) {\n\tres, err := this.request(\n\t\t\"GET\",\n\t\tthis.DownloadUrl+\"/file\"+path+\"?Authorization=\"+this.Token,\n\t\tnil, nil,\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn res.Body, nil\n}\n\nfunc (this Backblaze) Stat(path string) (os.FileInfo, error) {\n\treturn nil, ErrNotImplemented\n}\n\nfunc (this Backblaze) Mkdir(path string) error {\n\tp := this.path(path)\n\n\tif p.BucketId == \"\" {\n\t\tbucketName := \"\"\n\t\tif bp := strings.Split(path, \"/\"); len(bp) > 1 {\n\t\t\tbucketName = bp[1]\n\t\t}\n\t\tif bucketName == \"\" {\n\t\t\treturn ErrNotValid\n\t\t}\n\t\tres, err := this.request(\n\t\t\t\"POST\",\n\t\t\tthis.ApiUrl+\"/b2api/v2/b2_create_bucket\",\n\t\t\tstrings.NewReader(fmt.Sprintf(\n\t\t\t\t`{\"accountId\": \"%s\", \"bucketName\": \"%s\", \"bucketType\": \"allPrivate\"}`,\n\t\t\t\tthis.AccountId,\n\t\t\t\tbucketName,\n\t\t\t)),\n\t\t\tnil,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tbody, err := io.ReadAll(res.Body)\n\t\tres.Body.Close()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar resError BackblazeError\n\t\tif err := json.Unmarshal(body, &resError); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif resError.Message != \"\" {\n\t\t\treturn NewError(resError.Message, resError.Status)\n\t\t}\n\t\treturn nil\n\t}\n\n\treturn this.Touch(path + \".bzEmpty\")\n}\n\nfunc (this Backblaze) Rm(path string) error {\n\tp := this.path(path)\n\tif p.BucketId == \"\" {\n\t\treturn ErrNotValid\n\t}\n\tif p.Prefix == \"\" {\n\t\tbackblaze_cache.Del(this.params) // cache invalidation\n\t\tres, err := this.request(\n\t\t\t\"POST\",\n\t\t\tthis.ApiUrl+\"/b2api/v2/b2_delete_bucket\",\n\t\t\tstrings.NewReader(fmt.Sprintf(\n\t\t\t\t`{\"accountId\": \"%s\", \"bucketId\": \"%s\"}`,\n\t\t\t\tthis.AccountId,\n\t\t\t\tp.BucketId,\n\t\t\t)),\n\t\t\tnil,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tbody, err := io.ReadAll(res.Body)\n\t\tres.Body.Close()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar resError BackblazeError\n\t\tif err := json.Unmarshal(body, &resError); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif resError.Message != \"\" {\n\t\t\treturn NewError(resError.Message, resError.Status)\n\t\t}\n\t\treturn nil\n\t}\n\n\t// Backblaze doesn't provide a recursive API to delete => requires multiple steps\n\t// Step 1: find every files in a folder: b2_list_file_names\n\tres, err := this.request(\n\t\t\"POST\",\n\t\tthis.ApiUrl+\"/b2api/v2/b2_list_file_names\",\n\t\tstrings.NewReader(fmt.Sprintf(\n\t\t\t`{\"bucketId\": \"%s\", \"maxFileCount\": 10000, \"delimiter\": \"/\", \"prefix\": \"%s\"}`,\n\t\t\tp.BucketId, p.Prefix,\n\t\t)),\n\t\tnil,\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbody, err := io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\tbRes := struct {\n\t\tFiles []struct {\n\t\t\tFileId   string `json:\"fileId\"`\n\t\t\tFileName string `json:\"fileName\"`\n\t\t} `json:\"files\"`\n\t}{}\n\tif err = json.Unmarshal(body, &bRes); err != nil {\n\t\treturn err\n\t}\n\t// Step 2: delete files 1 by 1: b2_delete_file_version\n\tfor i := range bRes.Files {\n\t\tres, err := this.request(\n\t\t\t\"POST\",\n\t\t\tthis.ApiUrl+\"/b2api/v2/b2_delete_file_version\",\n\t\t\tstrings.NewReader(fmt.Sprintf(\n\t\t\t\t`{\"fileName\": \"%s\", \"fileId\": \"%s\"}`,\n\t\t\t\tbRes.Files[i].FileName, bRes.Files[i].FileId,\n\t\t\t)),\n\t\t\tnil,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif body, err = io.ReadAll(res.Body); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tres.Body.Close()\n\t\tvar resError BackblazeError\n\t\tif err := json.Unmarshal(body, &resError); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif resError.Message != \"\" {\n\t\t\treturn NewError(resError.Message, resError.Status)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (this Backblaze) Mv(from string, to string) error {\n\treturn ErrNotSupported\n}\n\nfunc (this Backblaze) Touch(path string) error {\n\tp := this.path(path)\n\n\t// Step 1: get the URL we will proceed to the upload\n\tres, err := this.request(\n\t\t\"POST\",\n\t\tthis.ApiUrl+\"/b2api/v2/b2_get_upload_url\",\n\t\tstrings.NewReader(fmt.Sprintf(`{\"bucketId\": \"%s\"}`, p.BucketId)),\n\t\tnil,\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbody, err := io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar resBody struct {\n\t\tUploadUrl string `json:\"uploadUrl\"`\n\t\tToken     string `json:\"authorizationToken\"`\n\t}\n\tif err := json.Unmarshal(body, &resBody); err != nil {\n\t\treturn err\n\t}\n\n\t// Step 2: perform the upload of the empty file\n\tres, err = this.request(\n\t\t\"POST\",\n\t\tresBody.UploadUrl,\n\t\tnil,\n\t\tfunc(r *http.Request) {\n\t\t\tr.Header.Set(\"Authorization\", resBody.Token)\n\t\t\tr.Header.Set(\"X-Bz-File-Name\", url.QueryEscape(p.Prefix))\n\t\t\tr.Header.Set(\"Content-Type\", \"application/octet-stream\")\n\t\t\tr.Header.Set(\"Content-Length\", \"0\")\n\t\t\tr.Header.Set(\"X-Bz-Content-Sha1\", \"da39a3ee5e6b4b0d3255bfef95601890afd80709\")\n\t\t},\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbody, err = io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar resError BackblazeError\n\tif err := json.Unmarshal(body, &resError); err != nil {\n\t\treturn err\n\t}\n\tif resError.Message != \"\" {\n\t\treturn NewError(resError.Message, resError.Status)\n\t}\n\treturn nil\n}\n\nfunc (this Backblaze) Save(path string, file io.Reader) error {\n\tp := this.path(path)\n\n\t// Step 1: get the URL we will proceed to the upload\n\tres, err := this.request(\n\t\t\"POST\",\n\t\tthis.ApiUrl+\"/b2api/v2/b2_get_upload_url\",\n\t\tstrings.NewReader(fmt.Sprintf(`{\"bucketId\": \"%s\"}`, p.BucketId)),\n\t\tnil,\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbody, err := io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar resBody struct {\n\t\tUploadUrl string `json:\"uploadUrl\"`\n\t\tToken     string `json:\"authorizationToken\"`\n\t}\n\tif err := json.Unmarshal(body, &resBody); err != nil {\n\t\treturn err\n\t}\n\n\t// Step 2: get details backblaze requires to perform the upload\n\tbackblazeFileDetail := struct {\n\t\tpath          string\n\t\tContentLength int64\n\t\tSha1          []byte\n\t}{}\n\tbackblazeFileDetail.path = filepath.Join(\n\t\tGetAbsolutePath(TMP_PATH),\n\t\t\"data_\"+QuickString(20)+\".dat\",\n\t)\n\tf, err := os.OpenFile(backblazeFileDetail.path, os.O_CREATE|os.O_RDWR, os.ModePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\tdefer os.Remove(backblazeFileDetail.path)\n\tio.Copy(f, file)\n\tif obj, ok := file.(io.Closer); ok {\n\t\tobj.Close()\n\t}\n\ts, err := f.Stat()\n\tif err != nil {\n\t\treturn err\n\t}\n\tbackblazeFileDetail.ContentLength = s.Size()\n\tf.Seek(0, io.SeekStart)\n\th := sha1.New()\n\tif _, err := io.Copy(h, f); err != nil {\n\t\treturn err\n\t}\n\tbackblazeFileDetail.Sha1 = h.Sum(nil)\n\n\t// Step 3: perform the upload\n\tf.Seek(0, io.SeekStart)\n\tres, err = this.request(\n\t\t\"POST\",\n\t\tresBody.UploadUrl,\n\t\tf,\n\t\tfunc(r *http.Request) {\n\t\t\tr.ContentLength = backblazeFileDetail.ContentLength\n\t\t\tr.Header.Set(\"Authorization\", resBody.Token)\n\t\t\tr.Header.Set(\"X-Bz-File-Name\", url.QueryEscape(p.Prefix))\n\t\t\tr.Header.Set(\"Content-Type\", \"application/octet-stream\")\n\t\t\tr.Header.Set(\"X-Bz-Content-Sha1\", fmt.Sprintf(\"%x\", backblazeFileDetail.Sha1))\n\t\t},\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbody, err = io.ReadAll(res.Body)\n\tres.Body.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar resError BackblazeError\n\tif err := json.Unmarshal(body, &resError); err != nil {\n\t\treturn err\n\t}\n\tif resError.Message != \"\" {\n\t\treturn NewError(resError.Message, resError.Status)\n\t}\n\treturn nil\n}\n\nfunc (this Backblaze) Meta(path string) Metadata {\n\tm := Metadata{\n\t\tCanRename: NewBool(false),\n\t\tCanMove:   NewBool(false),\n\t}\n\tif path == \"/\" {\n\t\tm.CanCreateFile = NewBool(false)\n\t\tm.CanUpload = NewBool(false)\n\t}\n\treturn m\n}\n\nfunc (this Backblaze) request(method string, url string, body io.Reader, fn func(req *http.Request)) (*http.Response, error) {\n\treq, err := http.NewRequest(method, url, body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif this.Token == \"\" {\n\t\treq.Header.Set(\n\t\t\t\"Authorization\",\n\t\t\tfmt.Sprintf(\n\t\t\t\t\"Basic %s\",\n\t\t\t\tbase64.StdEncoding.EncodeToString(\n\t\t\t\t\t[]byte(fmt.Sprintf(\"%s:%s\", this.params[\"username\"], this.params[\"password\"])),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t} else {\n\t\treq.Header.Set(\"Authorization\", this.Token)\n\t}\n\treq.Header.Set(\"User-Agent\", \"Filestash \"+APP_VERSION+\".\"+BUILD_DATE)\n\treq.Header.Set(\"Accept\", \"application/json\")\n\tif fn != nil {\n\t\tfn(req)\n\t}\n\tif req.Body != nil {\n\t\tdefer req.Body.Close()\n\t}\n\n\tres, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn res, err\n\t}\n\n\tif res.StatusCode == 401 {\n\t\tres.Body.Close()\n\t\treturn res, ErrAuthenticationFailed\n\t} else if res.StatusCode == 403 {\n\t\tres.Body.Close()\n\t\treturn res, ErrNotAllowed\n\t} else if res.StatusCode == 429 {\n\t\tres.Body.Close()\n\t\tretryAfter, err := strconv.Atoi(res.Header.Get(\"Retry-After\"))\n\t\tif err == nil && retryAfter < 10 && retryAfter >= 0 {\n\t\t\ttime.Sleep(time.Duration(retryAfter) * time.Second)\n\t\t\treturn this.request(method, url, body, fn)\n\t\t}\n\t\treturn res, ErrCongestion\n\t} else if res.StatusCode == 503 {\n\t\tres.Body.Close()\n\t\tretryAfter, err := strconv.Atoi(res.Header.Get(\"Retry-After\"))\n\t\tif err == nil && retryAfter < 10 && retryAfter >= 0 {\n\t\t\ttime.Sleep(time.Duration(retryAfter) * time.Second)\n\t\t\treturn this.request(method, url, body, fn)\n\t\t}\n\t\treturn res, ErrCongestion\n\t}\n\treturn res, nil\n}\n\ntype BackblazePath struct {\n\tBucketId string\n\tPrefix   string\n}\n\nfunc (this Backblaze) path(path string) BackblazePath {\n\tbp := strings.Split(path, \"/\")\n\tbucket := \"\"\n\tif len(bp) > 1 {\n\t\tbucket = bp[1]\n\t}\n\tprefix := \"\"\n\tif len(bp) > 2 {\n\t\tprefix = strings.Join(bp[2:], \"/\")\n\t}\n\n\treturn BackblazePath{\n\t\tthis.Buckets[bucket],\n\t\tprefix,\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_dav/index.go",
    "content": "package plg_backend_dav\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar DavCache AppCache\n\nconst (\n\tCARDDAV string = \"carddav\"\n\tCALDAV  string = \"caldav\"\n)\n\nfunc init() {\n\tDavCache = NewAppCache(2, 1)\n\tBackend.Register(CARDDAV, Dav{})\n\tBackend.Register(CALDAV, Dav{})\n}\n\ntype Dav struct {\n\twhich  string\n\turl    string\n\tparams map[string]string\n\tcache  map[string]interface{}\n}\n\nfunc (this Dav) Init(params map[string]string, app *App) (IBackend, error) {\n\tif b := DavCache.Get(params); b != nil {\n\t\tbackend := b.(*Dav)\n\t\treturn backend, nil\n\t}\n\tbackend := Dav{\n\t\turl:    strings.ReplaceAll(params[\"url\"], \"%{username}\", url.PathEscape(params[\"username\"])),\n\t\twhich:  params[\"type\"],\n\t\tparams: params,\n\t}\n\tDavCache.Set(params, &backend)\n\treturn backend, nil\n}\n\nfunc (this Dav) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: this.which,\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"url\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"URL\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"username\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"username\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"password\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"password\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Dav) Ls(path string) ([]os.FileInfo, error) {\n\tvar files []os.FileInfo\n\tvar err error\n\n\tif path == \"/\" {\n\t\tvar collections []DavCollection\n\t\tif collections, err = this.getCollections(); err != nil {\n\t\t\treturn files, err\n\t\t}\n\t\tfiles = make([]os.FileInfo, 0, len(collections))\n\t\tfor _, collection := range collections {\n\t\t\tfiles = append(files, File{\n\t\t\t\tFName: collection.Name,\n\t\t\t\tFType: \"directory\",\n\t\t\t\tFSize: -1,\n\t\t\t})\n\t\t}\n\t\treturn files, nil\n\t}\n\n\tvar resources []DavResource\n\tif resources, err = this.getResources(path); err != nil {\n\t\treturn files, err\n\t}\n\tfiles = make([]os.FileInfo, 0, len(resources))\n\tfor _, card := range resources {\n\t\tfiles = append(files, File{\n\t\t\tFName: card.Name,\n\t\t\tFType: \"file\",\n\t\t\tFSize: -1,\n\t\t\tFTime: card.Time,\n\t\t})\n\t}\n\treturn files, nil\n}\n\nfunc (this Dav) Stat(path string) (os.FileInfo, error) {\n\treturn nil, ErrNotImplemented\n}\n\nfunc (this Dav) Cat(path string) (io.ReadCloser, error) {\n\tvar uri string\n\tvar err error\n\tvar res *http.Response\n\n\tif uri, err = this.getResourceURI(path); err != nil {\n\t\treturn nil, err\n\t}\n\tif res, err = this.request(\"GET\", uri, nil, nil); err != nil {\n\t\treturn nil, err\n\t}\n\treturn res.Body, nil\n}\n\nfunc (this Dav) Mkdir(path string) error {\n\tvar uri string\n\tvar err error\n\tvar res *http.Response\n\n\tif uri, err = this.getUserURI(); err != nil {\n\t\treturn err\n\t}\n\tif len(strings.Split(strings.TrimSuffix(strings.TrimPrefix(path, \"/\"), \"/\"), \"/\")) != 1 {\n\t\treturn ErrNotValid\n\t} else if _, err = this.getCollectionURI(path); err == nil {\n\t\treturn ErrConflict\n\t}\n\n\tname := filepath.Base(path)\n\tif strings.HasSuffix(uri, \"/\") {\n\t\turi = uri + name\n\t} else {\n\t\turi = uri + \"/\" + name\n\t}\n\tif this.which == CARDDAV {\n\t\tif res, err = this.request(\"MKCOL\", uri, queryNewAddressBook(name), nil); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tres.Body.Close()\n\t\tDavCache.Del(this.params)\n\t\treturn nil\n\t} else if this.which == CALDAV {\n\t\tif res, err = this.request(\"MKCOL\", uri, queryNewCalendar(name), nil); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tres.Body.Close()\n\t\tDavCache.Del(this.params)\n\t\treturn nil\n\t}\n\treturn ErrNotValid\n}\n\nfunc (this Dav) Rm(path string) error {\n\tvar uri string\n\tvar err error\n\tvar res *http.Response\n\n\tp := strings.Split(strings.TrimSuffix(strings.TrimPrefix(path, \"/\"), \"/\"), \"/\")\n\tif len(p) == 1 {\n\t\tif uri, err = this.getCollectionURI(path); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif res, err = this.request(\"DELETE\", uri, nil, nil); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tDavCache.Del(this.params)\n\t\tres.Body.Close()\n\t\treturn nil\n\t} else if len(p) == 2 {\n\t\tif uri, err = this.getResourceURI(path); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif res, err = this.request(\"DELETE\", uri, nil, nil); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tres.Body.Close()\n\t\treturn nil\n\t}\n\treturn ErrNotValid\n}\n\nfunc (this Dav) Mv(from string, to string) error {\n\tif filepath.Dir(from) != filepath.Dir(to) {\n\t\treturn ErrNotValid\n\t}\n\treader, err := this.Cat(from)\n\tif err != nil {\n\t\treturn err\n\t}\n\td, err := io.ReadAll(reader)\n\tif err != nil {\n\t\treturn ErrNotValid\n\t}\n\n\tcontent := strings.Split(string(d), \"\\n\")\n\tfor line := range content {\n\t\tif this.which == CARDDAV {\n\t\t\tif strings.HasPrefix(content[line], \"FN:\") {\n\t\t\t\tcontent[line] = fmt.Sprintf(\"FN:%s\\n\", strings.TrimSuffix(filepath.Base(to), filepath.Ext(to)))\n\t\t\t\tbreak\n\t\t\t}\n\t\t} else if this.which == CALDAV {\n\t\t\tif strings.HasPrefix(content[line], \"SUMMARY:\") {\n\t\t\t\tcontent[line] = fmt.Sprintf(\"SUMMARY:%s\\n\", strings.TrimSuffix(filepath.Base(to), filepath.Ext(to)))\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tif err = this.Save(to, strings.NewReader(strings.Join(content, \"\\n\"))); err != nil {\n\t\treturn err\n\t}\n\treturn this.Rm(from)\n}\n\nfunc (this Dav) Touch(path string) error {\n\tvar uri string\n\tvar uid string\n\tvar err error\n\tvar res *http.Response\n\tvar content string = \"\"\n\n\tif uri, err = this.getCollectionURI(path); err != nil {\n\t\treturn err\n\t} else if strings.HasSuffix(uri, \"/\") == false {\n\t\turi += \"/\"\n\t}\n\tuid = RandomString(20)\n\turi += uid\n\n\tif this.which == CARDDAV {\n\t\turi += \".vcf\"\n\t\tname := strings.Split(strings.TrimSuffix(filepath.Base(path), \".vcf\"), \" \")\n\t\tcontent += \"BEGIN:VCARD\\n\"\n\t\tcontent += \"PRODID:-//Filestash//Filestash Carddav client//EN\"\n\t\tcontent += \"VERSION:3.0\\n\"\n\t\tif len(name) == 1 {\n\t\t\tcontent += fmt.Sprintf(\"FN:%s\\n\", name[0])\n\t\t\tcontent += fmt.Sprintf(\"N:;%s;;;\\n\", name[0])\n\t\t} else if len(name) >= 2 {\n\t\t\tcontent += fmt.Sprintf(\"FN:%s\\n\", strings.Join(name, \" \"))\n\t\t\tif len(name) == 2 {\n\t\t\t\tcontent += fmt.Sprintf(\"N:%s;%s;;;\\n\", name[0], strings.Join(name[1:], \" \"))\n\t\t\t} else {\n\t\t\t\tcontent += fmt.Sprintf(\"N:%s\\n\", name[0])\n\t\t\t}\n\t\t}\n\t\tcontent += \"END:VCARD\"\n\t} else if this.which == CALDAV {\n\t\tnow := time.Now()\n\t\turi += \".ics\"\n\t\tname := strings.TrimSuffix(filepath.Base(path), \".ics\")\n\t\tcontent += \"BEGIN:VCALENDAR\\n\"\n\t\tcontent += \"VERSION:2.0\\n\"\n\t\tcontent += \"PRODID:-//Filestash//Filestash Caldav Client//EN\\n\"\n\t\tcontent += \"BEGIN:VEVENT\\n\"\n\t\tcontent += fmt.Sprintf(\"UID:%s\\n\", uid)\n\t\tcontent += fmt.Sprintf(\"DTSTART:%04d%02d%02dT080000Z\\n\", now.Year(), now.Month(), now.Day())\n\t\tcontent += fmt.Sprintf(\"DTEND:%04d%02d%02dT090000Z\\n\", now.Year(), now.Month(), now.Day())\n\t\tcontent += fmt.Sprintf(\"DTSTAMP:%04d%02d%02dT%02d%02d00Z\\n\", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute())\n\t\tcontent += fmt.Sprintf(\"SUMMARY:%s\\n\", name)\n\t\tcontent += \"END:VEVENT\\n\"\n\t\tcontent += \"END:VCALENDAR\"\n\t}\n\n\tif content == \"\" {\n\t\treturn ErrNotValid\n\t}\n\tif res, err = this.request(\"PUT\", uri, strings.NewReader(content), func(req *http.Request) {\n\t\tif this.which == CALDAV {\n\t\t\treq.Header.Add(\"Content-Type\", \"text/calendar\")\n\t\t} else if this.which == CARDDAV {\n\t\t\treq.Header.Add(\"Content-Type\", \"text/vcard\")\n\t\t}\n\t\treq.Header.Add(\"If-None-Match\", \"*\")\n\t}); err != nil {\n\t\treturn err\n\t}\n\tdefer res.Body.Close()\n\treturn nil\n}\n\nfunc (this Dav) Save(path string, file io.Reader) error {\n\tvar uriInit string\n\tvar uri string\n\tvar err error\n\tvar res *http.Response\n\n\tif uriInit, err = this.getResourceURI(path); err != nil {\n\t\turiInit = \"\"\n\t}\n\n\tif uri, err = this.getCollectionURI(path); err != nil {\n\t\treturn err\n\t} else if strings.HasSuffix(uri, \"/\") == false {\n\t\turi += \"/\"\n\t}\n\turi += RandomString(15)\n\n\tif this.which == CARDDAV && strings.HasSuffix(uri, \".vcf\") == false {\n\t\turi += \".vcf\"\n\t} else if this.which == CALDAV && strings.HasSuffix(uri, \".ics\") == false {\n\t\turi += \".ics\"\n\t}\n\n\tif res, err = this.request(\"PUT\", uri, file, func(req *http.Request) {\n\t\tif this.which == CALDAV {\n\t\t\treq.Header.Add(\"Content-Type\", \"text/calendar\")\n\t\t} else if this.which == CARDDAV {\n\t\t\treq.Header.Add(\"Content-Type\", \"text/vcard\")\n\t\t}\n\t\treq.Header.Add(\"If-None-Match\", \"*\")\n\t}); err != nil {\n\t\treturn err\n\t}\n\tres.Body.Close()\n\n\tif uriInit != \"\" {\n\t\tif res, err = this.request(\"DELETE\", uriInit, nil, nil); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tres.Body.Close()\n\t}\n\treturn nil\n}\n\nfunc (this Dav) Meta(path string) Metadata {\n\tm := Metadata{\n\t\tCanMove:       NewBool(false),\n\t\tHideExtension: NewBool(true),\n\t}\n\tif path == \"/\" {\n\t\tm.CanCreateFile = NewBool(false)\n\t\tm.CanCreateDirectory = NewBool(true)\n\t\tm.CanRename = NewBool(false)\n\t\tm.CanUpload = NewBool(false)\n\t\tm.RefreshOnCreate = NewBool(false)\n\t} else {\n\t\tm.CanCreateFile = NewBool(true)\n\t\tm.CanCreateDirectory = NewBool(false)\n\t\tm.CanRename = NewBool(true)\n\t\tm.CanUpload = NewBool(true)\n\t\tm.RefreshOnCreate = NewBool(true)\n\t}\n\treturn m\n}\n\nfunc (this Dav) request(method string, url string, body io.Reader, fn func(req *http.Request)) (*http.Response, error) {\n\treq, err := http.NewRequest(method, url, body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif this.params[\"username\"] != \"\" {\n\t\treq.SetBasicAuth(this.params[\"username\"], this.params[\"password\"])\n\t}\n\tif req.Body != nil {\n\t\tdefer req.Body.Close()\n\t}\n\tif fn != nil {\n\t\tfn(req)\n\t}\n\tres, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t} else if res.StatusCode > 400 {\n\t\tres.Body.Close()\n\t\treturn nil, NewError(HTTPFriendlyStatus(res.StatusCode), res.StatusCode)\n\t}\n\treturn res, nil\n}\n\nfunc (this Dav) getUserURI() (string, error) {\n\tvar res *http.Response\n\tvar err error\n\n\tif this.cache[\"getUserURI\"] != nil {\n\t\treturn this.cache[\"getUserURI\"].(string), nil\n\t}\n\n\tif res, err = this.request(\n\t\t\"PROPFIND\",\n\t\tthis.url,\n\t\tstrings.NewReader(`<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n         <propfind xmlns=\"DAV:\">\n           <prop>\n             <current-user-principal />\n             <displayname />\n           </prop>\n         </propfind>`),\n\t\tnil,\n\t); err != nil {\n\t\treturn \"\", err\n\t}\n\tvar t struct {\n\t\tResponses []DavCollection `xml:\"response\"`\n\t}\n\n\tdecoder := xml.NewDecoder(res.Body)\n\tdefer res.Body.Close()\n\tif err := decoder.Decode(&t); err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(t.Responses) == 0 {\n\t\treturn \"\", ErrNotReachable\n\t}\n\turl, err := this.parseURL(t.Responses[0].User)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif this.cache == nil {\n\t\tthis.cache = make(map[string]interface{})\n\t}\n\tthis.cache[\"getUserURI\"] = url\n\tDavCache.Set(this.params, &this)\n\treturn url, nil\n}\n\nfunc (this Dav) getCollections() ([]DavCollection, error) {\n\tvar uri string\n\tvar res *http.Response\n\tvar err error\n\n\tif this.cache[\"getCollections\"] != nil {\n\t\treturn this.cache[\"getCollections\"].([]DavCollection), nil\n\t}\n\n\tif uri, err = this.getUserURI(); err != nil {\n\t\treturn nil, err\n\t}\n\tif res, err = this.request(\"PROPFIND\", uri, strings.NewReader(`<?xml version='1.0' encoding='UTF-8' ?>\n         <propfind xmlns=\"DAV:\">\n           <prop>\n             <resourcetype />\n             <getcontenttype/>\n             <displayname />\n           </prop>\n         </propfind>`), func(req *http.Request) {\n\t\treq.Header.Add(\"Depth\", \"1\")\n\t\treq.Header.Add(\"Content-Type\", \"application/xml\")\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar t struct {\n\t\tResponses []DavCollection `xml:\"response\"`\n\t}\n\tdecoder := xml.NewDecoder(res.Body)\n\tdefer res.Body.Close()\n\tif err := decoder.Decode(&t); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar collections []DavCollection = make([]DavCollection, 0, len(t.Responses))\n\tfor i := range t.Responses {\n\t\tif t.Responses[i].Name == \"\" {\n\t\t\tcontinue\n\t\t} else if this.which == CARDDAV {\n\t\t\tif strings.Contains(t.Responses[i].Type.Inner, \"addressbook\") {\n\t\t\t\tcollections = append(collections, t.Responses[i])\n\t\t\t}\n\t\t} else if this.which == CALDAV {\n\t\t\tif strings.Contains(t.Responses[i].Type.Inner, \"calendar\") {\n\t\t\t\tcollections = append(collections, t.Responses[i])\n\t\t\t}\n\t\t}\n\t}\n\n\tif this.cache == nil {\n\t\tthis.cache = make(map[string]interface{})\n\t}\n\tthis.cache[\"getCollections\"] = collections\n\tDavCache.Set(this.params, &this)\n\treturn collections, nil\n}\n\nfunc (this Dav) getCollectionURI(path string) (string, error) {\n\tpath = strings.TrimSuffix(strings.TrimPrefix(path, \"/\"), \"/\")\n\tp := strings.Split(path, \"/\")\n\tif len(p) == 0 {\n\t\treturn \"\", ErrNotFound\n\t}\n\tcoll, err := this.getCollections()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tfor i := 0; i < len(coll); i++ {\n\t\tif coll[i].Name == string(p[0]) {\n\t\t\treturn this.parseURL(coll[i].Url)\n\t\t}\n\t}\n\treturn \"\", ErrNotFound\n}\n\nfunc (this Dav) getResources(path string) ([]DavResource, error) {\n\tvar uri string\n\tvar res *http.Response\n\tvar err error\n\n\tif uri, err = this.getCollectionURI(path); err != nil {\n\t\treturn nil, err\n\t}\n\tif res, err = this.request(\n\t\t\"REPORT\",\n\t\turi,\n\t\tfunc() io.Reader {\n\t\t\tvar query string = \"\"\n\t\t\tif this.which == CARDDAV {\n\t\t\t\tquery = `<C:addressbook-query xmlns:D=\"DAV:\" xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n                           <D:prop>\n                             <C:address-data>\n                               <C:prop name=\"FN\"/>\n                               <C:prop name=\"REV\"/>\n                             </C:address-data>\n                           </D:prop>\n                         </C:addressbook-query>`\n\t\t\t} else if this.which == CALDAV {\n\t\t\t\tquery = `<C:calendar-query xmlns:D=\"DAV:\" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">\n                           <D:prop>\n                             <C:calendar-data>\n                               <C:prop name=\"SUMMARY\"/>\n                             </C:calendar-data>\n                           </D:prop>\n                         </C:calendar-query>`\n\t\t\t}\n\t\t\treturn strings.NewReader(query)\n\t\t}(),\n\t\tfunc(req *http.Request) {\n\t\t\treq.Header.Add(\"Depth\", \"1\")\n\t\t\treq.Header.Add(\"Content-Type\", \"application/xml\")\n\t\t},\n\t); err != nil {\n\t\treturn nil, err\n\t}\n\n\tdecoder := xml.NewDecoder(res.Body)\n\tdefer res.Body.Close()\n\tvar r struct {\n\t\tResponses []DavResource `xml:\"response\"`\n\t}\n\tif err = decoder.Decode(&r); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor i := range r.Responses {\n\t\tr.Responses[i].Name, r.Responses[i].Time = func() (string, int64) {\n\t\t\tvar t int64 = 0\n\t\t\tname := \"unknown\"\n\t\t\tif this.which == CARDDAV {\n\t\t\t\tfor _, line := range strings.Split(r.Responses[i].Vcard, \"\\n\") {\n\t\t\t\t\tif strings.HasPrefix(line, \"FN:\") && this.which == CARDDAV {\n\t\t\t\t\t\tname = strings.TrimPrefix(line, \"FN:\")\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tname += \".vcf\"\n\t\t\t} else if this.which == CALDAV {\n\t\t\t\tstrToInt := func(chunk string) int {\n\t\t\t\t\tret, _ := strconv.Atoi(chunk)\n\t\t\t\t\treturn ret\n\t\t\t\t}\n\t\t\t\tfor _, line := range strings.Split(r.Responses[i].Ical, \"\\n\") {\n\t\t\t\t\tif strings.HasPrefix(line, \"SUMMARY:\") {\n\t\t\t\t\t\tname = strings.TrimPrefix(line, \"SUMMARY:\")\n\t\t\t\t\t} else if strings.HasPrefix(line, \"DTSTART:\") {\n\t\t\t\t\t\t// https://tools.ietf.org/html/rfc2445#section-4.3.5\n\t\t\t\t\t\t// quick and dirty parser for form 1 & 2\n\t\t\t\t\t\tc := strings.TrimSuffix(strings.TrimSpace(strings.TrimPrefix(line, \"DTSTART:\")), \"Z\")\n\t\t\t\t\t\tif len(c) == 15 && t == 0 {\n\t\t\t\t\t\t\tt = time.Date(\n\t\t\t\t\t\t\t\tstrToInt(c[0:4]), time.Month(strToInt(c[4:6])+1), strToInt(c[6:8]), // date\n\t\t\t\t\t\t\t\tstrToInt(c[9:11]), strToInt(c[11:13]), strToInt(c[13:15]), // time\n\t\t\t\t\t\t\t\t0, time.UTC,\n\t\t\t\t\t\t\t).Unix()\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if strings.HasPrefix(line, \"DTSTART;VALUE=DATE:\") {\n\t\t\t\t\t\tc := strings.TrimSpace(strings.TrimPrefix(line, \"DTSTART;VALUE=DATE:\"))\n\t\t\t\t\t\tif len(c) == 8 && t == 0 {\n\t\t\t\t\t\t\tt = time.Date(\n\t\t\t\t\t\t\t\tstrToInt(c[0:4]), time.Month(strToInt(c[4:6])+1), strToInt(c[6:8]), // date\n\t\t\t\t\t\t\t\t0, 0, 0, // time\n\t\t\t\t\t\t\t\t0, time.UTC,\n\t\t\t\t\t\t\t).Unix()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tname += \".ics\"\n\t\t\t}\n\t\t\treturn name, t\n\t\t}()\n\t}\n\treturn r.Responses, nil\n}\n\nfunc (this Dav) getResourceURI(path string) (string, error) {\n\tpath = strings.TrimSuffix(strings.TrimPrefix(path, \"/\"), \"/\")\n\tp := strings.Split(path, \"/\")\n\tif len(p) != 2 {\n\t\treturn \"\", ErrNotValid\n\t}\n\n\tvar resources []DavResource\n\tvar err error\n\tif resources, err = this.getResources(path); err != nil {\n\t\treturn \"\", ErrNotFound\n\t}\n\tfilename := filepath.Base(path)\n\tfor i := range resources {\n\t\tif resources[i].Name == filename {\n\t\t\treturn this.parseURL(resources[i].Url)\n\t\t}\n\t}\n\treturn \"\", ErrNotFound\n}\n\nfunc (this Dav) parseURL(link string) (string, error) {\n\tvar origin *url.URL\n\tvar destination *url.URL\n\tvar err error\n\n\tif destination, _ = url.Parse(link); err != nil {\n\t\treturn \"\", err\n\t}\n\tif origin, err = url.Parse(this.url); err != nil {\n\t\treturn \"\", err\n\t}\n\tif destination.Host == \"\" || destination.Scheme == \"\" {\n\t\tdestination.Host = origin.Host\n\t\tdestination.Scheme = origin.Scheme\n\t}\n\treturn destination.String(), nil\n}\n\nfunc joinURL(base string, bit string) string {\n\tif strings.HasSuffix(base, \"/\") == false {\n\t\tbase += \"/\"\n\t}\n\treturn base + bit\n}\n\ntype DavResource struct {\n\tUrl   string `xml:\"href\"`\n\tName  string `xml:\"-\"`\n\tTime  int64  `xml:\"-\"`\n\tVcard string `xml:\"propstat>prop>address-data,omitempty\"`\n\tIcal  string `xml:\"propstat>prop>calendar-data,omitempty\"`\n}\n\ntype DavCollection struct {\n\tUrl  string `xml:\"href\"`\n\tName string `xml:\"propstat>prop>displayname,omitempty\"`\n\tUser string `xml:\"propstat>prop>current-user-principal>href,omitempty\"`\n\tType struct {\n\t\tInner string `xml:\",innerxml\"`\n\t} `xml:\"propstat>prop>resourcetype,omitempty\"`\n}\n\nfunc queryNewCalendar(name string) io.Reader {\n\tquery := `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<create xmlns=\"DAV:\" xmlns:C=\"urn:ietf:params:xml:ns:caldav\" xmlns:I=\"http://apple.com/ns/ical/\">\n  <set>\n    <prop>\n      <resourcetype>\n        <collection />\n        <C:calendar />\n      </resourcetype>\n      <C:supported-calendar-component-set>\n        <C:comp name=\"VEVENT\" />\n        <C:comp name=\"VJOURNAL\" />\n        <C:comp name=\"VTODO\" />\n      </C:supported-calendar-component-set>\n      <displayname>{{NAME}}</displayname>\n      <C:calendar-description></C:calendar-description>\n      <I:calendar-color>#9AD1ED</I:calendar-color>\n    </prop>\n  </set>\n</create>`\n\tquery = strings.Replace(query, \"{{NAME}}\", name, -1)\n\treturn strings.NewReader(query)\n}\n\nfunc queryNewAddressBook(name string) io.Reader {\n\tquery := `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<create xmlns=\"DAV:\" xmlns:CR=\"urn:ietf:params:xml:ns:carddav\">\n  <set>\n    <prop>\n      <resourcetype>\n        <collection />\n        <CR:addressbook />\n      </resourcetype>\n      <displayname>{{NAME}}</displayname>\n      <CR:addressbook-description></CR:addressbook-description>\n    </prop>\n  </set>\n</create>`\n\tquery = strings.Replace(query, \"{{NAME}}\", name, -1)\n\treturn strings.NewReader(query)\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_dropbox/index.go",
    "content": "package plg_backend_dropbox\n\nimport (\n\t\"encoding/json\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc init() {\n\tBackend.Register(\"dropbox\", Dropbox{})\n}\n\ntype Dropbox struct {\n\tClientId string\n\tHostname string\n\tBearer   string\n}\n\nfunc (d Dropbox) Init(params map[string]string, app *App) (IBackend, error) {\n\tbackend := &Dropbox{}\n\tif env := os.Getenv(\"DROPBOX_CLIENT_ID\"); env != \"\" {\n\t\tbackend.ClientId = env\n\t} else {\n\t\tbackend.ClientId = Config.Get(\"auth.dropbox.client_id\").Default(\"\").String()\n\t}\n\tbackend.Hostname = Config.Get(\"general.host\").String()\n\tbackend.Bearer = params[\"access_token\"]\n\n\tif backend.ClientId == \"\" {\n\t\treturn backend, NewError(\"Missing ClientID: Contact your admin\", 502)\n\t} else if backend.Hostname == \"\" {\n\t\treturn backend, NewError(\"Missing Hostname: Contact your admin\", 502)\n\t}\n\treturn backend, nil\n}\n\nfunc (d Dropbox) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"dropbox\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tReadOnly: true,\n\t\t\t\tName:     \"oauth2\",\n\t\t\t\tType:     \"text\",\n\t\t\t\tValue:    \"/api/session/auth/dropbox\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tReadOnly: true,\n\t\t\t\tName:     \"image\",\n\t\t\t\tType:     \"image\",\n\t\t\t\tValue:    \"data:image/svg+xml;utf8;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNDIuNCAzOS41IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KICA8cG9seWdvbiBmaWxsPSIjMDA3RUU1IiBwb2ludHM9IjEyLjUsMCAwLDguMSA4LjcsMTUuMSAyMS4yLDcuMyIvPgo8cG9seWdvbiBmaWxsPSIjMDA3RUU1IiBwb2ludHM9IjAsMjEuOSAxMi41LDMwLjEgMjEuMiwyMi44IDguNywxNS4xIi8+Cjxwb2x5Z29uIGZpbGw9IiMwMDdFRTUiIHBvaW50cz0iMjEuMiwyMi44IDMwLDMwLjEgNDIuNCwyMiAzMy44LDE1LjEiLz4KPHBvbHlnb24gZmlsbD0iIzAwN0VFNSIgcG9pbnRzPSI0Mi40LDguMSAzMCwwIDIxLjIsNy4zIDMzLjgsMTUuMSIvPgo8cG9seWdvbiBmaWxsPSIjMDA3RUU1IiBwb2ludHM9IjIxLjMsMjQuNCAxMi41LDMxLjcgOC44LDI5LjIgOC44LDMyIDIxLjMsMzkuNSAzMy44LDMyIDMzLjgsMjkuMiAzMCwzMS43Ii8+Cjwvc3ZnPgo=\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (d Dropbox) OAuthURL() string {\n\turl := \"https://www.dropbox.com/oauth2/authorize?\"\n\turl += \"client_id=\" + d.ClientId\n\turl += \"&redirect_uri=https://\" + d.Hostname + \"/login\"\n\turl += \"&response_type=token\"\n\treturn url\n}\n\nfunc (d Dropbox) Ls(path string) ([]os.FileInfo, error) {\n\tfiles := make([]os.FileInfo, 0)\n\n\targs := struct {\n\t\tPath             string `json:\"path\"`\n\t\tRecursive        bool   `json:\"recursive\"`\n\t\tIncludeDeleted   bool   `json:\"include_deleted\"`\n\t\tIncludeMediaInfo bool   `json:\"include_media_info\"`\n\t}{d.path(path), false, false, true}\n\tres, err := d.request(\"POST\", \"https://api.dropboxapi.com/2/files/list_folder\", d.toReader(args), nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer res.Body.Close()\n\tif res.StatusCode >= 400 {\n\t\treturn nil, NewError(HTTPFriendlyStatus(res.StatusCode)+\": can't get things in\"+filepath.Base(path), res.StatusCode)\n\t}\n\n\tvar r struct {\n\t\tFiles []struct {\n\t\t\tType string    `json:\".tag\"`\n\t\t\tName string    `json:\"name\"`\n\t\t\tTime time.Time `json:\"client_modified\"`\n\t\t\tSize uint      `json:\"size\"`\n\t\t} `json:\"entries\"`\n\t}\n\tdecoder := json.NewDecoder(res.Body)\n\tdecoder.Decode(&r)\n\n\tfor _, obj := range r.Files {\n\t\tfiles = append(files, File{\n\t\t\tFName: obj.Name,\n\t\t\tFType: func(p string) string {\n\t\t\t\tif p == \"folder\" {\n\t\t\t\t\treturn \"directory\"\n\t\t\t\t}\n\t\t\t\treturn \"file\"\n\t\t\t}(obj.Type),\n\t\t\tFTime: obj.Time.UnixNano() / 1000,\n\t\t\tFSize: int64(obj.Size),\n\t\t})\n\t}\n\treturn files, nil\n}\n\nfunc (d Dropbox) Stat(path string) (os.FileInfo, error) {\n\treturn nil, ErrNotImplemented\n}\n\nfunc (d Dropbox) Cat(path string) (io.ReadCloser, error) {\n\tres, err := d.request(\"POST\", \"https://content.dropboxapi.com/2/files/download\", nil, func(req *http.Request) {\n\t\targ := struct {\n\t\t\tPath string `json:\"path\"`\n\t\t}{d.path(path)}\n\t\tjson, _ := io.ReadAll(d.toReader(arg))\n\t\treq.Header.Set(\"Dropbox-API-Arg\", string(json))\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn res.Body, nil\n}\n\nfunc (d Dropbox) Mkdir(path string) error {\n\targs := struct {\n\t\tPath       string `json:\"path\"`\n\t\tAutorename bool   `json:\"autorename\"`\n\t}{d.path(path), false}\n\tres, err := d.request(\"POST\", \"https://api.dropboxapi.com/2/files/create_folder_v2\", d.toReader(args), nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tres.Body.Close()\n\tif res.StatusCode >= 400 {\n\t\treturn NewError(HTTPFriendlyStatus(res.StatusCode)+\": can't create \"+filepath.Base(path), res.StatusCode)\n\t}\n\treturn nil\n}\n\nfunc (d Dropbox) Rm(path string) error {\n\targs := struct {\n\t\tPath string `json:\"path\"`\n\t}{d.path(path)}\n\tres, err := d.request(\"POST\", \"https://api.dropboxapi.com/2/files/delete_v2\", d.toReader(args), nil)\n\tif res.StatusCode >= 400 {\n\t\treturn NewError(HTTPFriendlyStatus(res.StatusCode)+\": can't remove \"+filepath.Base(path), res.StatusCode)\n\t}\n\tres.Body.Close()\n\treturn err\n}\n\nfunc (d Dropbox) Mv(from string, to string) error {\n\targs := struct {\n\t\tFromPath string `json:\"from_path\"`\n\t\tToPath   string `json:\"to_path\"`\n\t}{d.path(from), d.path(to)}\n\tres, err := d.request(\"POST\", \"https://api.dropboxapi.com/2/files/move_v2\", d.toReader(args), nil)\n\tif res.StatusCode >= 400 {\n\t\treturn NewError(HTTPFriendlyStatus(res.StatusCode)+\": can't do that\", res.StatusCode)\n\t}\n\tres.Body.Close()\n\treturn err\n}\n\nfunc (d Dropbox) Touch(path string) error {\n\treturn d.Save(path, strings.NewReader(\"\"))\n}\n\nfunc (d Dropbox) Save(path string, file io.Reader) error {\n\tres, err := d.request(\"POST\", \"https://content.dropboxapi.com/2/files/upload\", file, func(req *http.Request) {\n\t\targ := struct {\n\t\t\tPath       string `json:\"path\"`\n\t\t\tAutoRename bool   `json:\"autorename\"`\n\t\t\tMode       string `json:\"mode\"`\n\t\t}{d.path(path), false, \"overwrite\"}\n\t\tjson, _ := io.ReadAll(d.toReader(arg))\n\t\treq.Header.Set(\"Dropbox-API-Arg\", string(json))\n\t\treq.Header.Set(\"Content-Type\", \"application/octet-stream\")\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tres.Body.Close()\n\tif res.StatusCode >= 400 {\n\t\treturn NewError(HTTPFriendlyStatus(res.StatusCode)+\": can't do that\", res.StatusCode)\n\t}\n\treturn err\n}\n\nfunc (d Dropbox) request(method string, url string, body io.Reader, fn func(*http.Request)) (*http.Response, error) {\n\treq, err := http.NewRequest(method, url, body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"Authorization\", \"Bearer \"+d.Bearer)\n\tif fn == nil {\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\tfn(req)\n\t}\n\tif req.Body != nil {\n\t\tdefer req.Body.Close()\n\t}\n\treturn HTTPClient.Do(req)\n}\n\nfunc (d Dropbox) toReader(a interface{}) io.Reader {\n\tj, err := json.Marshal(a)\n\tif err != nil {\n\t\treturn nil\n\t}\n\treturn strings.NewReader(string(j))\n}\n\nfunc (d Dropbox) path(path string) string {\n\treturn regexp.MustCompile(`\\/$`).ReplaceAllString(path, \"\")\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_ftp/index.go",
    "content": "package plg_backend_ftp\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t//\"github.com/secsy/goftp\" <- FTP issue with microsoft FTP\n\t\"github.com/prasad83/goftp\"\n\t\"io\"\n\t\"os\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar FtpCache AppCache\n\ntype Ftp struct {\n\tclient *goftp.Client\n\tp      map[string]string\n\twg     *sync.WaitGroup\n\tctx    context.Context\n}\n\nfunc init() {\n\tBackend.Register(\"ftp\", Ftp{})\n\n\tFtpCache = NewAppCache(2, 1)\n\tFtpCache.OnEvict(func(key string, value interface{}) {\n\t\tc := value.(*Ftp)\n\t\tif c == nil {\n\t\t\tLog.Warning(\"plg_backend_ftp::ftp is nil on close\")\n\t\t\treturn\n\t\t} else if c.wg == nil {\n\t\t\tLog.Warning(\"plg_backend_ftp::wg is nil on close\")\n\t\t\tc.Close()\n\t\t\treturn\n\t\t}\n\t\tc.wg.Wait()\n\t\tLog.Debug(\"plg_backend_ftp::vacuum\")\n\t\tc.Close()\n\t})\n}\n\nfunc (f Ftp) Init(params map[string]string, app *App) (IBackend, error) {\n\tif c := FtpCache.Get(params); c != nil {\n\t\td := c.(*Ftp)\n\t\tif d == nil {\n\t\t\tLog.Warning(\"plg_backend_ftp::ftp is nil on get\")\n\t\t\treturn nil, ErrInternal\n\t\t} else if d.wg == nil {\n\t\t\tLog.Warning(\"plg_backend_ftp::wg is nil on get\")\n\t\t\treturn nil, ErrInternal\n\t\t}\n\t\td.wg.Add(1)\n\t\td.ctx = app.Context\n\t\tgo func() {\n\t\t\t<-d.ctx.Done()\n\t\t\td.wg.Done()\n\t\t}()\n\t\treturn d, nil\n\t}\n\tif params[\"hostname\"] == \"\" {\n\t\tparams[\"hostname\"] = \"localhost\"\n\t}\n\n\tif params[\"port\"] == \"\" {\n\t\tparams[\"port\"] = \"21\"\n\t}\n\tif params[\"username\"] == \"\" {\n\t\tparams[\"acl\"] = \"r\"\n\t\tparams[\"username\"] = \"anonymous\"\n\t}\n\tif params[\"username\"] == \"anonymous\" && params[\"password\"] == \"\" {\n\t\tparams[\"password\"] = \"anonymous\"\n\t}\n\tconn := 5\n\tif params[\"conn\"] != \"\" {\n\t\tif i, err := strconv.Atoi(params[\"conn\"]); err == nil && i > 0 {\n\t\t\tconn = i\n\t\t}\n\t}\n\n\tconnectStrategy := []string{\"ftp\", \"ftps::implicit\", \"ftps::explicit\"}\n\tif strings.HasPrefix(params[\"hostname\"], \"ftp://\") {\n\t\tconnectStrategy = []string{\"ftp\"}\n\t\tparams[\"hostname\"] = strings.TrimPrefix(params[\"hostname\"], \"ftp://\")\n\t} else if strings.HasPrefix(params[\"hostname\"], \"ftps://\") {\n\t\tconnectStrategy = []string{\"ftps::implicit\", \"ftps::explicit\"}\n\t\tparams[\"hostname\"] = strings.TrimPrefix(params[\"hostname\"], \"ftps://\")\n\t}\n\n\tvar backend *Ftp = nil\n\thostname := fmt.Sprintf(\"%s:%s\", params[\"hostname\"], params[\"port\"])\n\tcfgBuilder := func(timeout time.Duration, withTLS bool) goftp.Config {\n\t\tcfg := goftp.Config{\n\t\t\tUser:               params[\"username\"],\n\t\t\tPassword:           params[\"password\"],\n\t\t\tConnectionsPerHost: conn,\n\t\t\tTimeout:            timeout * time.Second,\n\t\t}\n\t\tcfg.Timeout = timeout\n\t\tif withTLS {\n\t\t\tcfg.TLSConfig = &tls.Config{\n\t\t\t\tInsecureSkipVerify:     true,\n\t\t\t\tClientSessionCache:     tls.NewLRUClientSessionCache(0),\n\t\t\t\tSessionTicketsDisabled: false,\n\t\t\t\tServerName:             hostname,\n\t\t\t}\n\t\t}\n\t\treturn cfg\n\t}\n\tfor i := 0; i < len(connectStrategy); i++ {\n\t\tif connectStrategy[i] == \"ftp\" {\n\t\t\tclient, err := goftp.DialConfig(cfgBuilder(5*time.Second, false), hostname)\n\t\t\tif err != nil {\n\t\t\t\tLog.Debug(\"plg_backend_ftp::ftp dial %s\", err.Error())\n\t\t\t\tcontinue\n\t\t\t} else if _, err := client.ReadDir(\"/\"); err != nil {\n\t\t\t\tclient.Close()\n\t\t\t\tLog.Debug(\"plg_backend_ftp::ftp verify %s\", err.Error())\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tclient.Close()\n\t\t\tclient, err = goftp.DialConfig(cfgBuilder(60*time.Second, false), hostname)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tparams[\"mode\"] = connectStrategy[i]\n\t\t\tbackend = &Ftp{client, params, nil, app.Context}\n\t\t\tbreak\n\t\t} else if connectStrategy[i] == \"ftps::implicit\" {\n\t\t\tcfg := cfgBuilder(60*time.Second, true)\n\t\t\tcfg.TLSMode = goftp.TLSImplicit\n\t\t\tclient, err := goftp.DialConfig(cfg, hostname)\n\t\t\tif err != nil {\n\t\t\t\tLog.Debug(\"plg_backend_ftp::ftps::implicit dial %s\", err.Error())\n\t\t\t\tcontinue\n\t\t\t} else if _, err := client.ReadDir(\"/\"); err != nil {\n\t\t\t\tLog.Debug(\"plg_backend_ftp::ftps::implicit verify %s\", err.Error())\n\t\t\t\tclient.Close()\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tparams[\"mode\"] = connectStrategy[i]\n\t\t\tbackend = &Ftp{client, params, nil, app.Context}\n\t\t\tbreak\n\t\t} else if connectStrategy[i] == \"ftps::explicit\" {\n\t\t\tcfg := cfgBuilder(5*time.Second, true)\n\t\t\tcfg.TLSMode = goftp.TLSExplicit\n\t\t\tclient, err := goftp.DialConfig(cfg, hostname)\n\t\t\tif err != nil {\n\t\t\t\tLog.Debug(\"plg_backend_ftp::ftps::explicit dial '%s'\", err.Error())\n\t\t\t\tcontinue\n\t\t\t} else if _, err := client.ReadDir(\"/\"); err != nil {\n\t\t\t\tLog.Debug(\"plg_backend_ftp::ftps::explicit verify %s\", err.Error())\n\t\t\t\tclient.Close()\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tclient.Close()\n\t\t\tcfg = cfgBuilder(60*time.Second, true)\n\t\t\tcfg.TLSMode = goftp.TLSExplicit\n\t\t\tclient, err = goftp.DialConfig(cfg, hostname)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tparams[\"mode\"] = connectStrategy[i]\n\t\t\tbackend = &Ftp{client, params, nil, app.Context}\n\t\t\tbreak\n\t\t}\n\t}\n\tif backend == nil {\n\t\treturn nil, ErrAuthenticationFailed\n\t}\n\tbackend.wg = new(sync.WaitGroup)\n\tbackend.wg.Add(1)\n\tbackend.ctx = app.Context\n\tgo func() {\n\t\t<-backend.ctx.Done()\n\t\tbackend.wg.Done()\n\t}()\n\tFtpCache.Set(params, backend)\n\treturn backend, nil\n}\n\nfunc (f Ftp) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"ftp\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"hostname\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Hostname*\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"username\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Username\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"password\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"Password\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"advanced\",\n\t\t\t\tType:        \"enable\",\n\t\t\t\tPlaceholder: \"Advanced\",\n\t\t\t\tTarget:      []string{\"ftp_path\", \"ftp_port\", \"ftp_conn\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"ftp_path\",\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Path\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"ftp_port\",\n\t\t\t\tName:        \"port\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"Port\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"ftp_conn\",\n\t\t\t\tName:        \"conn\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"Number of connections\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (f Ftp) Meta(path string) Metadata {\n\tif f.p[\"acl\"] == \"r\" {\n\t\treturn Metadata{\n\t\t\tCanCreateFile:      NewBool(false),\n\t\t\tCanCreateDirectory: NewBool(false),\n\t\t\tCanRename:          NewBool(false),\n\t\t\tCanMove:            NewBool(false),\n\t\t\tCanUpload:          NewBool(false),\n\t\t\tCanDelete:          NewBool(false),\n\t\t}\n\t}\n\treturn Metadata{}\n}\n\nfunc (f Ftp) Home() (home string, err error) {\n\tf.Execute(func(client *goftp.Client) error {\n\t\thome, err = f.client.Getwd()\n\t\treturn err\n\t})\n\treturn home, err\n}\n\nfunc (f Ftp) Ls(path string) (files []os.FileInfo, err error) {\n\tf.Execute(func(client *goftp.Client) error {\n\t\tfiles, err = client.ReadDir(path)\n\t\treturn err\n\t})\n\treturn files, err\n}\n\nfunc (f Ftp) Cat(path string) (reader io.ReadCloser, err error) {\n\tf.Execute(func(client *goftp.Client) error {\n\t\tif _, err = client.Stat(path); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpr, pw := io.Pipe()\n\t\tgo func() {\n\t\t\terr = client.Retrieve(path, pw)\n\t\t\tif err != nil {\n\t\t\t\tpr.CloseWithError(NewError(\"Problem\", 409))\n\t\t\t}\n\t\t\tpw.Close()\n\t\t}()\n\t\treader = pr\n\t\treturn nil\n\t})\n\treturn reader, err\n}\n\nfunc (f Ftp) Stat(path string) (finfo os.FileInfo, err error) {\n\tf.Execute(func(client *goftp.Client) error {\n\t\tfinfo, err = client.Stat(path)\n\t\treturn err\n\t})\n\tif err == nil {\n\t\treturn finfo, err\n\t}\n\tif ftpErr, ok := err.(goftp.Error); ok && ftpErr.Code() == 550 {\n\t\treturn nil, ErrNotFound\n\t}\n\treturn nil, ErrNotImplemented\n}\n\nfunc (f Ftp) Mkdir(path string) (err error) {\n\tf.Execute(func(client *goftp.Client) error {\n\t\t_, err = client.Mkdir(path)\n\t\treturn err\n\t})\n\treturn err\n}\n\nfunc (f Ftp) Rm(path string) (err error) {\n\tisDirectory := func(p string) bool {\n\t\treturn regexp.MustCompile(`\\/$`).MatchString(p)\n\t}\n\ttransformError := func(e error) error {\n\t\t// For some reasons bsftp is struggling with the library\n\t\t// sometimes returning a 200 OK\n\t\tif e == nil {\n\t\t\treturn nil\n\t\t}\n\t\tif obj, ok := e.(goftp.Error); ok {\n\t\t\tif obj.Code() < 300 && obj.Code() > 0 {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn e\n\t}\n\tvar recursiveDelete func(client *goftp.Client, _path string) error\n\trecursiveDelete = func(client *goftp.Client, _path string) error {\n\t\tif isDirectory(_path) {\n\t\t\tentries, err := client.ReadDir(_path)\n\t\t\tif transformError(err) != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfor _, entry := range entries {\n\t\t\t\tif entry.IsDir() {\n\t\t\t\t\terr = recursiveDelete(client, _path+entry.Name()+\"/\")\n\t\t\t\t\tif transformError(err) != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\terr = recursiveDelete(client, _path+entry.Name())\n\t\t\t\t\tif transformError(err) != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\terr = client.Rmdir(_path)\n\t\t\treturn transformError(err)\n\t\t}\n\t\terr = client.Delete(_path)\n\t\treturn transformError(err)\n\t}\n\tf.Execute(func(client *goftp.Client) error {\n\t\terr = recursiveDelete(client, path)\n\t\treturn err\n\t})\n\treturn err\n}\n\nfunc (f Ftp) Mv(from string, to string) (err error) {\n\tf.Execute(func(client *goftp.Client) error {\n\t\terr = client.Rename(from, to)\n\t\treturn err\n\t})\n\treturn err\n}\n\nfunc (f Ftp) Touch(path string) (err error) {\n\tf.Execute(func(client *goftp.Client) error {\n\t\terr = client.Store(path, strings.NewReader(\"\"))\n\t\treturn err\n\t})\n\treturn err\n}\n\nfunc (f Ftp) Save(path string, file io.Reader) (err error) {\n\tf.Execute(func(client *goftp.Client) error {\n\t\terr = client.Store(path, file)\n\t\treturn err\n\t})\n\treturn err\n}\n\nfunc (f Ftp) Close() error {\n\treturn f.client.Close()\n}\n\nfunc (f Ftp) Execute(fn func(*goftp.Client) error) {\n\terr := fn(f.client)\n\tif ftpErr, ok := err.(goftp.Error); ok {\n\t\tcode := ftpErr.Code()\n\t\tif code == 421 || (code == 0 && err.Error() == \"error reading response: EOF\") {\n\t\t\tf.Close()\n\t\t\tFtpCache.Set(f.p, nil)\n\t\t\tif b, err := f.Init(f.p, &App{Context: f.ctx}); err == nil {\n\t\t\t\tfn(b.(*Ftp).client)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_ftp_only/index.go",
    "content": "package plg_backend_ftp_only\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t//\"github.com/secsy/goftp\" <- FTP issue with microsoft FTP\n\t\"github.com/prasad83/goftp\"\n)\n\nvar FtpCache AppCache\n\ntype Ftp struct {\n\tclient *goftp.Client\n}\n\nfunc init() {\n\tBackend.Register(\"ftp\", Ftp{})\n\n\tFtpCache = NewAppCache(2, 1)\n\tFtpCache.OnEvict(func(key string, value interface{}) {\n\t\tc := value.(*Ftp)\n\t\tc.Close()\n\t})\n}\n\nfunc (f Ftp) Init(params map[string]string, app *App) (IBackend, error) {\n\tif c := FtpCache.Get(params); c != nil {\n\t\td := c.(*Ftp)\n\t\treturn d, nil\n\t}\n\tif params[\"hostname\"] == \"\" {\n\t\tparams[\"hostname\"] = \"localhost\"\n\t}\n\n\tif params[\"port\"] == \"\" {\n\t\tparams[\"port\"] = \"21\"\n\t}\n\tif params[\"username\"] == \"\" {\n\t\tparams[\"username\"] = \"anonymous\"\n\t}\n\tif params[\"username\"] == \"anonymous\" && params[\"password\"] == \"\" {\n\t\tparams[\"password\"] = \"anonymous\"\n\t}\n\tconn := 5\n\tif params[\"conn\"] != \"\" {\n\t\tif i, err := strconv.Atoi(params[\"conn\"]); err == nil && i > 0 {\n\t\t\tconn = i\n\t\t}\n\t}\n\n\tconfigWithoutTLS := goftp.Config{\n\t\tUser:               params[\"username\"],\n\t\tPassword:           params[\"password\"],\n\t\tConnectionsPerHost: conn,\n\t\tTimeout:            10 * time.Second,\n\t}\n\n\tvar backend *Ftp = nil\n\n\tclient, err := goftp.DialConfig(configWithoutTLS, fmt.Sprintf(\"%s:%s\", strings.TrimPrefix(params[\"hostname\"], \"ftp://\"), params[\"port\"]))\n\tif err != nil {\n\t\treturn backend, err\n\t}\n\tif _, err := client.ReadDir(\"/\"); err != nil {\n\t\tclient.Close()\n\t\treturn backend, ErrAuthenticationFailed\n\t}\n\tbackend = &Ftp{client}\n\tFtpCache.Set(params, backend)\n\treturn backend, nil\n}\n\nfunc (f Ftp) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"ftp\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"hostname\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Hostname*\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"username\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Username\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"password\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"Password\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"advanced\",\n\t\t\t\tType:        \"enable\",\n\t\t\t\tPlaceholder: \"Advanced\",\n\t\t\t\tTarget:      []string{\"ftp_path\", \"ftp_port\", \"ftp_conn\", \"ftp_disable_ftps\"},\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"ftp_path\",\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Path\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"ftp_port\",\n\t\t\t\tName:        \"port\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"Port\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"ftp_conn\",\n\t\t\t\tName:        \"conn\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"Number of connections\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"ftp_conn\",\n\t\t\t\tName:        \"conn\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"Number of connections\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:   \"ftp_disable_ftps\",\n\t\t\t\tName: \"Disable FTPS\",\n\t\t\t\tType: \"select\",\n\t\t\t\tOpts: []string{\"DEBUG\", \"INFO\", \"WARNING\", \"ERROR\"},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (f Ftp) Home() (string, error) {\n\treturn f.client.Getwd()\n}\n\nfunc (f Ftp) Ls(path string) ([]os.FileInfo, error) {\n\treturn f.client.ReadDir(path)\n}\n\nfunc (f Ftp) Cat(path string) (io.ReadCloser, error) {\n\tpr, pw := io.Pipe()\n\tgo func() {\n\t\t// TODO: prevent closing\n\t\tif err := f.client.Retrieve(path, pw); err != nil {\n\t\t\tpr.CloseWithError(NewError(\"Problem\", 409))\n\t\t}\n\t\tpw.Close()\n\t}()\n\treturn pr, nil\n}\n\nfunc (f Ftp) Mkdir(path string) error {\n\t_, err := f.client.Mkdir(path)\n\treturn err\n}\n\nfunc (f Ftp) Rm(path string) error {\n\tisDirectory := func(p string) bool {\n\t\treturn regexp.MustCompile(`\\/$`).MatchString(p)\n\t}\n\ttransformError := func(e error) error {\n\t\t// For some reasons bsftp is struggling with the library\n\t\t// sometimes returning a 200 OK\n\t\tif e == nil {\n\t\t\treturn nil\n\t\t}\n\t\tif obj, ok := e.(goftp.Error); ok {\n\t\t\tif obj.Code() < 300 && obj.Code() > 0 {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn e\n\t}\n\tif isDirectory(path) {\n\t\tentries, err := f.Ls(path)\n\t\tif transformError(err) != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, entry := range entries {\n\t\t\tif entry.IsDir() {\n\t\t\t\terr = f.Rm(path + entry.Name() + \"/\")\n\t\t\t\tif transformError(err) != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\terr = f.Rm(path + entry.Name())\n\t\t\t\tif transformError(err) != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\terr = f.client.Rmdir(path)\n\t\treturn transformError(err)\n\t}\n\terr := f.client.Delete(path)\n\treturn transformError(err)\n}\n\nfunc (f Ftp) Mv(from string, to string) error {\n\treturn f.client.Rename(from, to)\n}\n\nfunc (f Ftp) Touch(path string) error {\n\treturn f.client.Store(path, strings.NewReader(\"\"))\n}\n\nfunc (f Ftp) Save(path string, file io.Reader) error {\n\t// TODO: prevent closing\n\treturn f.client.Store(path, file)\n}\n\nfunc (f Ftp) Close() error {\n\treturn f.client.Close()\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_gdrive/index.go",
    "content": "package plg_backend_gdrive\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"golang.org/x/net/context\"\n\t\"golang.org/x/oauth2\"\n\t\"golang.org/x/oauth2/google\"\n\t\"google.golang.org/api/drive/v3\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nconst gdriveFolderMarker = \"application/vnd.google-apps.folder\"\n\ntype GDrive struct {\n\tClient *drive.Service\n\tConfig *oauth2.Config\n}\n\nfunc init() {\n\tBackend.Register(\"gdrive\", GDrive{})\n}\n\nfunc (g GDrive) Init(params map[string]string, app *App) (IBackend, error) {\n\tbackend := GDrive{}\n\n\tconfig := &oauth2.Config{\n\t\tEndpoint:     google.Endpoint,\n\t\tClientID:     Config.Get(\"auth.gdrive.client_id\").Default(os.Getenv(\"GDRIVE_CLIENT_ID\")).String(),\n\t\tClientSecret: Config.Get(\"auth.gdrive.client_secret\").Default(os.Getenv(\"GDRIVE_CLIENT_SECRET\")).String(),\n\t\tRedirectURL:  \"https://\" + Config.Get(\"general.host\").String() + \"/login\",\n\t\tScopes:       []string{\"https://www.googleapis.com/auth/drive\"},\n\t}\n\tif config.ClientID == \"\" {\n\t\treturn backend, NewError(\"Missing Client ID: Contact your admin\", 502)\n\t} else if config.ClientSecret == \"\" {\n\t\treturn backend, NewError(\"Missing Client Secret: Contact your admin\", 502)\n\t} else if config.RedirectURL == \"/login\" {\n\t\treturn backend, NewError(\"Missing Hostname: Contact your admin\", 502)\n\t}\n\n\ttoken := &oauth2.Token{\n\t\tAccessToken:  params[\"token\"],\n\t\tRefreshToken: params[\"refresh\"],\n\t\tExpiry: func(t string) time.Time {\n\t\t\texpiry, err := strconv.ParseInt(t, 10, 64)\n\t\t\tif err != nil {\n\t\t\t\treturn time.Now()\n\t\t\t}\n\t\t\treturn time.Unix(expiry, 0)\n\t\t}(params[\"expiry\"]),\n\t\tTokenType: \"bearer\",\n\t}\n\tclient := config.Client(context.Background(), token)\n\tsrv, err := drive.New(client)\n\tif err != nil {\n\t\treturn nil, NewError(err.Error(), 400)\n\t}\n\tbackend.Client = srv\n\tbackend.Config = config\n\treturn backend, nil\n}\n\nfunc (g GDrive) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"gdrive\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tReadOnly: true,\n\t\t\t\tName:     \"oauth2\",\n\t\t\t\tType:     \"text\",\n\t\t\t\tValue:    \"/api/session/auth/gdrive\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tReadOnly: true,\n\t\t\t\tName:     \"image\",\n\t\t\t\tType:     \"image\",\n\t\t\t\tValue:    \"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTM5IDEyMC40IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KICA8cGF0aCBkPSJtMjQuMiAxMjAuNC0yNC4yLTQxLjkgNDUuMy03OC41IDI0LjIgNDEuOXoiIGZpbGw9IiMwZGE5NjAiLz4KICA8cGF0aCBkPSJtNTguOSA2MC4yIDEwLjYtMTguMy0yNC4yLTQxLjl6IiBmaWxsPSIjMGRhOTYwIi8+CiAgPHBhdGggZD0ibTI0LjIgMTIwLjQgMjQuMi00MS45aDkwLjZsLTI0LjIgNDEuOXoiIGZpbGw9IiMyZDZmZGQiLz4KICA8cGF0aCBkPSJtNjkuNSA3OC41aC0yMS4xbDEwLjUtMTguMy0zNC43IDYwLjJ6IiBmaWxsPSIjMmQ2ZmRkIi8+ICAKICA8cGF0aCBkPSJtMTM5IDc4LjVoLTQ4LjRsLTQ1LjMtNzguNWg0OC40eiIgZmlsbD0iI2ZmZDI0ZCIvPgogIDxwYXRoIGQ9Im05MC42IDc4LjVoNDguNGwtNTguOS0xOC4zeiIgZmlsbD0iI2ZmZDI0ZCIvPgo8L3N2Zz4K\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (g GDrive) OAuthURL() string {\n\treturn g.Config.AuthCodeURL(\"gdrive\", oauth2.AccessTypeOnline)\n}\n\nfunc (g GDrive) OAuthToken(ctx *map[string]interface{}) error {\n\tcode := \"\"\n\tif str, ok := (*ctx)[\"code\"].(string); ok {\n\t\tcode = str\n\t}\n\n\ttoken, err := g.Config.Exchange(oauth2.NoContext, code)\n\tif err != nil {\n\t\treturn err\n\t}\n\t(*ctx)[\"token\"] = token.AccessToken\n\t(*ctx)[\"refresh\"] = token.RefreshToken\n\t(*ctx)[\"expiry\"] = strconv.FormatInt(token.Expiry.UnixNano()/1000, 10)\n\tdelete(*ctx, \"code\")\n\treturn nil\n}\n\nfunc (g GDrive) Ls(path string) ([]os.FileInfo, error) {\n\tfiles := make([]os.FileInfo, 0)\n\tfile, err := g.infoPath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres, err := g.Client.Files.List().Q(\"'\" + file.id + \"' in parents AND trashed = false\").Fields(\"nextPageToken, files(name, size, modifiedTime, mimeType)\").PageSize(500).Do()\n\tif err != nil {\n\t\treturn nil, NewError(err.Error(), 404)\n\t}\n\tfor _, obj := range res.Files {\n\t\tfiles = append(files, File{\n\t\t\tFName: obj.Name,\n\t\t\tFType: func(mType string) string {\n\t\t\t\tif mType == gdriveFolderMarker {\n\t\t\t\t\treturn \"directory\"\n\t\t\t\t}\n\t\t\t\treturn \"file\"\n\t\t\t}(obj.MimeType),\n\t\t\tFTime: func(t string) int64 {\n\t\t\t\ta, err := time.Parse(time.RFC3339, t)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn 0\n\t\t\t\t}\n\t\t\t\treturn a.UnixNano() / 1000\n\t\t\t}(obj.ModifiedTime),\n\t\t\tFSize: obj.Size,\n\t\t})\n\t}\n\treturn files, nil\n}\n\nfunc (g GDrive) Stat(path string) (os.FileInfo, error) {\n\treturn nil, ErrNotImplemented\n}\n\nfunc (g GDrive) Cat(path string) (io.ReadCloser, error) {\n\tfile, err := g.infoPath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif strings.HasPrefix(file.mType, \"application/vnd.google-apps\") {\n\t\tmType := \"text/plain\"\n\t\tif file.mType == \"application/vnd.google-apps.spreadsheet\" {\n\t\t\tmType = \"text/csv\"\n\t\t}\n\t\tdata, err := g.Client.Files.Export(file.id, mType).Download()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn data.Body, nil\n\t}\n\n\tdata, err := g.Client.Files.Get(file.id).Download()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn data.Body, nil\n}\n\nfunc (g GDrive) Mkdir(path string) error {\n\tparent, err := g.infoPath(getParentPath(path))\n\tif err != nil {\n\t\treturn NewError(\"Directory already exists\", 409)\n\t}\n\t_, err = g.Client.Files.Create(&drive.File{\n\t\tName:     filepath.Base(path),\n\t\tParents:  []string{parent.id},\n\t\tMimeType: gdriveFolderMarker,\n\t}).Do()\n\treturn err\n}\n\nfunc (g GDrive) Rm(path string) error {\n\tfile, err := g.infoPath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = g.Client.Files.Delete(file.id).Do(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (g GDrive) Mv(from string, to string) error {\n\tffile, err := g.infoPath(from)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttfile, err := g.infoPath(getParentPath(to))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = g.Client.Files.Update(ffile.id, &drive.File{\n\t\tName: filepath.Base(to),\n\t}).RemoveParents(ffile.parent).AddParents(tfile.id).Do()\n\treturn err\n}\n\nfunc (g GDrive) Touch(path string) error {\n\tfile, err := g.infoPath(getParentPath(path))\n\tif err != nil {\n\t\treturn NewError(\"Base folder not found\", 404)\n\t}\n\n\t_, err = g.Client.Files.Create(&drive.File{\n\t\tName:    filepath.Base(path),\n\t\tParents: []string{file.id},\n\t}).Media(strings.NewReader(\"\")).Do()\n\treturn err\n}\n\nfunc (g GDrive) Save(path string, reader io.Reader) error {\n\tif file, err := g.infoPath(path); err == nil {\n\t\t_, err = g.Client.Files.Update(file.id, &drive.File{}).Media(reader).Do()\n\t\treturn err\n\t}\n\n\tfile, err := g.infoPath(getParentPath(path))\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = g.Client.Files.Create(&drive.File{\n\t\tName:    filepath.Base(path),\n\t\tParents: []string{file.id},\n\t}).Media(reader).Do()\n\treturn err\n}\n\nfunc (g GDrive) infoPath(p string) (*GDriveMarker, error) {\n\tFindSolutions := func(level int, folder string) ([]GDriveMarker, error) {\n\t\tres, err := g.Client.Files.List().Q(\"name = '\" + folder + \"' AND trashed = false\").Fields(\"files(parents, id, name, mimeType)\").PageSize(500).Do()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsolutions := make([]GDriveMarker, 0)\n\t\tfor _, file := range res.Files {\n\t\t\tif len(file.Parents) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsolutions = append(solutions, GDriveMarker{\n\t\t\t\tfile.Id,\n\t\t\t\tfile.Parents[0],\n\t\t\t\tfile.Name,\n\t\t\t\tlevel,\n\t\t\t\tfile.MimeType,\n\t\t\t})\n\t\t}\n\t\treturn solutions, nil\n\t}\n\tFindRoot := func(level int) ([]GDriveMarker, error) {\n\t\troot := make([]GDriveMarker, 0)\n\t\tres, err := g.Client.Files.List().Q(\"'root' in parents\").Fields(\"files(parents, id, name, mimeType)\").PageSize(1).Do()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif len(res.Files) == 0 || len(res.Files[0].Parents) == 0 {\n\t\t\troot = append(root, GDriveMarker{\n\t\t\t\t\"root\",\n\t\t\t\t\"root\",\n\t\t\t\t\"root\",\n\t\t\t\tlevel,\n\t\t\t\tgdriveFolderMarker,\n\t\t\t})\n\t\t\treturn root, nil\n\t\t}\n\t\troot = append(root, GDriveMarker{\n\t\t\tres.Files[0].Parents[0],\n\t\t\t\"root\",\n\t\t\t\"root\",\n\t\t\tlevel,\n\t\t\tgdriveFolderMarker,\n\t\t})\n\t\treturn root, nil\n\t}\n\tMergeSolutions := func(solutions_bag []GDriveMarker, solutions_new []GDriveMarker) []GDriveMarker {\n\t\tif len(solutions_bag) == 0 {\n\t\t\treturn solutions_new\n\t\t}\n\n\t\tsolutions := make([]GDriveMarker, 0)\n\t\tfor _, new := range solutions_new {\n\t\t\tfor _, old := range solutions_bag {\n\t\t\t\tif new.id == old.parent && new.level+1 == old.level {\n\t\t\t\t\told.level = new.level\n\t\t\t\t\told.parent = new.id\n\t\t\t\t\tsolutions = append(solutions, old)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn solutions\n\t}\n\tvar FindId func(folders []string, solutions_bag []GDriveMarker) (*GDriveMarker, error)\n\tFindId = func(folders []string, solutions_bag []GDriveMarker) (*GDriveMarker, error) {\n\t\tvar solutions_new []GDriveMarker\n\t\tvar err error\n\t\tif len(folders) == 0 {\n\t\t\tsolutions_new, err = FindRoot(0)\n\t\t} else {\n\t\t\tsolutions_new, err = FindSolutions(len(folders), folders[len(folders)-1])\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn nil, NewError(\"Can't get data\", 500)\n\t\t}\n\t\tsolutions_bag = MergeSolutions(solutions_bag, solutions_new)\n\t\tif len(solutions_bag) == 0 {\n\t\t\treturn nil, NewError(\"Doesn't exist\", 404)\n\t\t} else if len(solutions_bag) == 1 {\n\t\t\treturn &solutions_bag[0], nil\n\t\t} else {\n\t\t\treturn FindId(folders[:len(folders)-1], solutions_bag)\n\t\t}\n\t}\n\n\tpath := make([]string, 0)\n\tfor _, chunk := range strings.Split(p, \"/\") {\n\t\tif chunk == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tpath = append(path, chunk)\n\t}\n\tif len(path) == 0 {\n\t\treturn &GDriveMarker{\n\t\t\t\"root\",\n\t\t\t\"\",\n\t\t\t\"root\",\n\t\t\t0,\n\t\t\tgdriveFolderMarker,\n\t\t}, nil\n\t}\n\treturn FindId(path, make([]GDriveMarker, 0))\n}\n\ntype GDriveMarker struct {\n\tid     string\n\tparent string\n\tname   string\n\tlevel  int\n\tmType  string\n}\n\nfunc getParentPath(path string) string {\n\tre := regexp.MustCompile(\"/$\")\n\tpath = re.ReplaceAllString(path, \"\")\n\treturn filepath.Dir(path) + \"/\"\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_git/index.go",
    "content": "package plg_backend_git\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-git/go-git/v6\"\n\t\"github.com/go-git/go-git/v6/plumbing\"\n\t\"github.com/go-git/go-git/v6/plumbing/object\"\n\t\"github.com/go-git/go-git/v6/plumbing/transport\"\n\t\"github.com/go-git/go-git/v6/plumbing/transport/http\"\n\tsshgit \"github.com/go-git/go-git/v6/plumbing/transport/ssh\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"golang.org/x/crypto/ssh\"\n)\n\nvar git_cache AppCache\n\ntype Git struct {\n\tgit *GitLib\n}\n\nfunc init() {\n\tBackend.Register(\"git\", Git{})\n\n\tgit_cache = NewAppCache()\n\tgit_cache.OnEvict(func(key string, value interface{}) {\n\t\tg := value.(*Git)\n\t\tg.Close()\n\t})\n}\n\ntype GitParams struct {\n\trepo           string\n\tusername       string\n\tpassword       string\n\tpassphrase     string\n\tcommit         string\n\tbranch         string\n\tauthorName     string\n\tauthorEmail    string\n\tcommitterName  string\n\tcommitterEmail string\n\tbasePath       string\n}\n\nfunc (git Git) Init(params map[string]string, app *App) (IBackend, error) {\n\tif obj := git_cache.Get(params); obj != nil {\n\t\treturn obj.(*Git), nil\n\t}\n\tg := &Git{\n\t\tgit: &GitLib{\n\t\t\tparams: &GitParams{\n\t\t\t\tparams[\"repo\"],\n\t\t\t\tparams[\"username\"],\n\t\t\t\tparams[\"password\"],\n\t\t\t\tparams[\"passphrase\"],\n\t\t\t\tparams[\"commit\"],\n\t\t\t\tparams[\"branch\"],\n\t\t\t\tparams[\"authorName\"],\n\t\t\t\tparams[\"authorEmail\"],\n\t\t\t\tparams[\"committerName\"],\n\t\t\t\tparams[\"committerEmail\"],\n\t\t\t\t\"\",\n\t\t\t},\n\t\t},\n\t}\n\tp := g.git.params\n\tif p.branch == \"\" {\n\t\tp.branch = \"master\"\n\t}\n\tif p.commit == \"\" {\n\t\tp.commit = \"{action} ({filename}): {path}\"\n\t}\n\tif p.authorName == \"\" {\n\t\tp.authorName = APPNAME\n\t}\n\tif p.authorEmail == \"\" && !IsWhiteLabel() {\n\t\tp.authorEmail = \"https://filestash.app\"\n\t}\n\tif p.committerName == \"\" {\n\t\tp.committerName = APPNAME\n\t}\n\tif p.committerEmail == \"\" && !IsWhiteLabel() {\n\t\tp.committerEmail = \"https://filestash.app\"\n\t}\n\n\thash := GenerateID(params)\n\tp.basePath = GetAbsolutePath(\n\t\tTMP_PATH,\n\t\t\"git_\"+hash,\n\t) + \"/\"\n\n\trepo, err := g.git.open(p, p.basePath)\n\tg.git.repo = repo\n\tif err != nil {\n\t\treturn g, err\n\t}\n\tgit_cache.Set(params, g)\n\treturn g, nil\n}\n\nfunc (g Git) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tValue: \"git\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"repo\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Repository*\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"username\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Username\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"password\",\n\t\t\t\tType:        \"long_password\",\n\t\t\t\tPlaceholder: \"Password\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"advanced\",\n\t\t\t\tType:        \"enable\",\n\t\t\t\tPlaceholder: \"Advanced\",\n\t\t\t\tTarget: []string{\n\t\t\t\t\t\"git_path\", \"git_passphrase\", \"git_commit\",\n\t\t\t\t\t\"git_branch\", \"git_author_email\", \"git_author_name\",\n\t\t\t\t\t\"git_committer_email\", \"git_committer_name\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"git_path\",\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Path\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"git_passphrase\",\n\t\t\t\tName:        \"passphrase\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Passphrase\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"git_commit\",\n\t\t\t\tName:        \"commit\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Commit Format: default to \\\"{action}({filename}): {path}\\\"\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"git_branch\",\n\t\t\t\tName:        \"branch\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Branch: default to \\\"master\\\"\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"git_author_email\",\n\t\t\t\tName:        \"author_email\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Author email\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"git_author_name\",\n\t\t\t\tName:        \"author_name\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Author name\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"git_committer_email\",\n\t\t\t\tName:        \"committer_email\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Committer email\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"git_committer_name\",\n\t\t\t\tName:        \"committer_name\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Committer name\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (g Git) Ls(path string) ([]os.FileInfo, error) {\n\tg.git.refresh()\n\tp, err := g.path(path)\n\tif err != nil {\n\t\treturn nil, NewError(err.Error(), 403)\n\t}\n\tf, err := SafeOsOpenFile(p, os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfiles, err := f.Readdir(-1)\n\tif err != nil {\n\t\tf.Close()\n\t\treturn nil, err\n\t}\n\treturn files, f.Close()\n}\n\nfunc (g Git) Stat(path string) (os.FileInfo, error) {\n\tg.git.refresh()\n\tp, err := g.path(path)\n\tif err != nil {\n\t\treturn nil, NewError(err.Error(), 403)\n\t}\n\tf, err := SafeOsOpenFile(p, os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfinfo, err := f.Stat()\n\tif err != nil {\n\t\tf.Close()\n\t\treturn nil, err\n\t}\n\treturn finfo, f.Close()\n}\n\nfunc (g Git) Cat(path string) (io.ReadCloser, error) {\n\tp, err := g.path(path)\n\tif err != nil {\n\t\treturn nil, NewError(err.Error(), 403)\n\t}\n\treturn SafeOsOpenFile(p, os.O_RDONLY, os.ModePerm)\n}\n\nfunc (g Git) Mkdir(path string) error {\n\tp, err := g.path(path)\n\tif err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\treturn SafeOsMkdir(p, os.ModePerm)\n}\n\nfunc (g Git) Rm(path string) error {\n\tp, err := g.path(path)\n\tif err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\tif err = SafeOsRemoveAll(p); err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\tmessage := g.git.message(\"delete\", path)\n\tif err = g.git.save(message); err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\treturn nil\n}\n\nfunc (g Git) Mv(from string, to string) error {\n\tfpath, err := g.path(from)\n\tif err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\ttpath, err := g.path(to)\n\tif err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\n\tif err = SafeOsRename(fpath, tpath); err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\tmessage := g.git.message(\"move\", from)\n\tif err = g.git.save(message); err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\treturn nil\n}\n\nfunc (g Git) Touch(path string) error {\n\tp, err := g.path(path)\n\tif err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\tfile, err := SafeOsOpenFile(p, os.O_WRONLY|os.O_CREATE, os.ModePerm)\n\tif err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\tfile.Close()\n\n\tmessage := g.git.message(\"create\", path)\n\tif err = g.git.save(message); err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\treturn nil\n}\n\nfunc (g Git) Save(path string, file io.Reader) error {\n\tp, err := g.path(path)\n\tif err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\n\tfo, err := SafeOsOpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tio.Copy(fo, file)\n\tfo.Close()\n\n\tmessage := g.git.message(\"save\", path)\n\tif err = g.git.save(message); err != nil {\n\t\treturn NewError(err.Error(), 403)\n\t}\n\treturn nil\n}\n\nfunc (g Git) Close() error {\n\treturn os.RemoveAll(g.git.params.basePath)\n}\n\nfunc (g Git) path(path string) (string, error) {\n\tif path == \"\" {\n\t\treturn \"\", ErrNotValid\n\t}\n\tbasePath := filepath.Join(g.git.params.basePath, path)\n\tif string(path[len(path)-1]) == \"/\" {\n\t\tbasePath += \"/\"\n\t}\n\tif strings.HasPrefix(basePath, g.git.params.basePath) == false {\n\t\treturn \"\", ErrNotFound\n\t}\n\treturn basePath, nil\n}\n\ntype GitLib struct {\n\trepo   *git.Repository\n\tparams *GitParams\n}\n\nfunc (g *GitLib) open(params *GitParams, path string) (*git.Repository, error) {\n\tg.params = params\n\n\tif _, err := os.Stat(g.params.basePath); os.IsNotExist(err) {\n\t\tauth, err := g.auth()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tg, err := git.PlainClone(path, &git.CloneOptions{\n\t\t\tURL:           g.params.repo,\n\t\t\tDepth:         1,\n\t\t\tReferenceName: plumbing.ReferenceName(fmt.Sprintf(\"refs/heads/%s\", g.params.branch)),\n\t\t\tSingleBranch:  true,\n\t\t\tAuth:          auth,\n\t\t})\n\t\tif err == transport.ErrEmptyRemoteRepository {\n\t\t\treturn g, nil\n\t\t}\n\t\treturn g, err\n\t}\n\treturn git.PlainOpen(g.params.basePath)\n}\n\nfunc (g *GitLib) save(message string) error {\n\tw, err := g.repo.Worktree()\n\tif err != nil {\n\t\treturn NewError(err.Error(), 500)\n\t}\n\t_, err = w.Add(\".\")\n\tif err != nil {\n\t\treturn NewError(err.Error(), 500)\n\t}\n\n\t_, err = w.Commit(message, &git.CommitOptions{\n\t\tAll: true,\n\t\tAuthor: &object.Signature{\n\t\t\tName:  g.params.authorName,\n\t\t\tEmail: g.params.authorEmail,\n\t\t\tWhen:  time.Now(),\n\t\t},\n\t\tCommitter: &object.Signature{\n\t\t\tName:  g.params.committerName,\n\t\t\tEmail: g.params.committerEmail,\n\t\t\tWhen:  time.Now(),\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tauth, err := g.auth()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn g.repo.Push(&git.PushOptions{\n\t\tAuth: auth,\n\t})\n}\n\nfunc (g *GitLib) refresh() error {\n\tw, err := g.repo.Worktree()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn w.Pull(&git.PullOptions{RemoteName: \"origin\"})\n}\n\nfunc (g *GitLib) auth() (transport.AuthMethod, error) {\n\tif strings.HasPrefix(g.params.repo, \"http\") {\n\t\treturn &http.BasicAuth{\n\t\t\tUsername: g.params.username,\n\t\t\tPassword: g.params.password,\n\t\t}, nil\n\t}\n\tisPrivateKey := func(pass string) bool {\n\t\tif len(pass) > 1000 && strings.HasPrefix(pass, \"-----\") {\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\n\tif isPrivateKey(g.params.password) {\n\t\tsigner, err := ssh.ParsePrivateKeyWithPassphrase([]byte(g.params.password), []byte(g.params.passphrase))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &sshgit.PublicKeys{\n\t\t\tUser:   \"git\",\n\t\t\tSigner: signer,\n\t\t\tHostKeyCallbackHelper: sshgit.HostKeyCallbackHelper{\n\t\t\t\tHostKeyCallback: ssh.InsecureIgnoreHostKey(),\n\t\t\t},\n\t\t}, nil\n\t}\n\n\treturn &sshgit.Password{\n\t\tUser:     g.params.username,\n\t\tPassword: g.params.password,\n\t\tHostKeyCallbackHelper: sshgit.HostKeyCallbackHelper{\n\t\t\tHostKeyCallback: ssh.InsecureIgnoreHostKey(),\n\t\t},\n\t}, nil\n}\n\nfunc (g *GitLib) message(action string, path string) string {\n\tmessage := strings.Replace(g.params.commit, \"{action}\", \"save\", -1)\n\tmessage = strings.Replace(message, \"{filename}\", filepath.Base(path), -1)\n\tmessage = strings.Replace(message, \"{path}\", strings.Replace(path, g.params.basePath, \"\", -1), -1)\n\treturn message\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_ipfs/README.md",
    "content": "\n```\n# run a IPFS server node:\ndocker run --rm --name ipfs_host -p 4001:4001 -p 4001:4001/udp -p 127.0.0.1:8080:8080 -p 127.0.0.1:5001:5001 ipfs/kubo:v0.39.0\n```\n\napi doc:\n- https://github.com/ipfs/kubo/blob/c1e1cfebbbc946f957e39345b9224a05704f701d/client/rpc/api.go#L60C6-L69C1\n- https://github.com/ipfs/kubo/tree/master/client/rpc\n- https://github.com/ipfs/go-ipfs-api\n"
  },
  {
    "path": "server/plugin/plg_backend_ipfs/index.go",
    "content": "package plg_backend_ipfs\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tBackend.Register(\"ipfs\", Ipfs{})\n}\n\ntype Ipfs struct {\n\turl string\n}\n\nfunc (this Ipfs) Init(params map[string]string, app *App) (IBackend, error) {\n\turl := strings.TrimSuffix(params[\"url\"], \"/\")\n\tif url == \"\" {\n\t\turl = \"http://127.0.0.1:5001\"\n\t}\n\tthis.url = url\n\tif _, err := this.Stat(\"/\"); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &this, nil\n}\n\nfunc (this Ipfs) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"ipfs\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"url\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Address (default: http://127.0.0.1:5001)\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Ipfs) Ls(path string) ([]os.FileInfo, error) {\n\tfiles := make([]os.FileInfo, 0)\n\tfile, err := this.Stat(path)\n\tif err != nil || !file.IsDir() {\n\t\treturn nil, ErrNotValid\n\t}\n\tresp := struct {\n\t\tEntries []struct {\n\t\t\tName string\n\t\t\tType int\n\t\t\tSize int64\n\t\t\tHash string\n\t\t}\n\t}{}\n\tif err := this.query(\"/api/v0/files/ls?long=true&arg=\"+url.QueryEscape(path), &resp); err != nil {\n\t\treturn nil, ErrNotValid\n\t}\n\tfor _, entry := range resp.Entries {\n\t\tt := \"\"\n\t\tif entry.Type == 1 {\n\t\t\tt = \"directory\"\n\t\t} else if entry.Type == 0 {\n\t\t\tt = \"file\"\n\t\t}\n\t\tif t == \"\" {\n\t\t\tLog.Debug(\"plg_backend_ipfs::ls msg=unknown_entry_type entry=%v\", entry)\n\t\t\tcontinue\n\t\t}\n\t\tfiles = append(files, File{\n\t\t\tFName: entry.Name,\n\t\t\tFSize: entry.Size,\n\t\t\tFType: t,\n\t\t})\n\t}\n\treturn files, nil\n}\n\nfunc (this Ipfs) Stat(path string) (os.FileInfo, error) {\n\tout := struct {\n\t\tHash           string\n\t\tType           string\n\t\tSize           int64\n\t\tCumulativeSize int\n\t}{}\n\tif err := this.query(\"/api/v0/files/stat?arg=\"+url.QueryEscape(path), &out); err != nil {\n\t\treturn nil, err\n\t}\n\treturn File{\n\t\tFName: filepath.Base(path),\n\t\tFSize: out.Size,\n\t\tFType: out.Type,\n\t}, nil\n}\n\nfunc (this Ipfs) Cat(path string) (io.ReadCloser, error) {\n\treq, err := http.NewRequest(http.MethodPost, this.url+\"/api/v0/files/read?arg=\"+url.QueryEscape(path), nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresp, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\tresp.Body.Close()\n\t\treturn nil, ErrNotValid\n\t}\n\treturn resp.Body, nil\n}\n\nfunc (this Ipfs) Mkdir(path string) error {\n\treturn this.query(\"/api/v0/files/mkdir?arg=\"+url.QueryEscape(path), nil)\n}\n\nfunc (this Ipfs) Rm(path string) error {\n\treturn this.query(\"/api/v0/files/rm?recursive=true&arg=\"+url.QueryEscape(path), nil)\n}\n\nfunc (this Ipfs) Mv(from, to string) error {\n\tfrom = strings.TrimSuffix(from, \"/\")\n\tto = strings.TrimSuffix(to, \"/\")\n\treturn this.query(\"/api/v0/files/mv?arg=\"+url.QueryEscape(from)+\"&arg=\"+url.QueryEscape(to), nil)\n}\n\nfunc (this Ipfs) Save(path string, content io.Reader) error {\n\tpipeReader, pipeWriter := io.Pipe()\n\twriter := multipart.NewWriter(pipeWriter)\n\tgo func() {\n\t\tdefer pipeWriter.Close()\n\t\tdefer writer.Close()\n\t\tpart, err := writer.CreateFormFile(\"data\", \"\")\n\t\tif err != nil {\n\t\t\tpipeWriter.CloseWithError(err)\n\t\t\treturn\n\t\t}\n\t\tif _, err := io.Copy(part, content); err != nil {\n\t\t\tpipeWriter.CloseWithError(err)\n\t\t\treturn\n\t\t}\n\t}()\n\treq, err := http.NewRequest(\n\t\thttp.MethodPost,\n\t\tthis.url+\"/api/v0/files/write?create=true&truncate=true&arg=\"+url.QueryEscape(path),\n\t\tpipeReader,\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq.Header.Set(\"Content-Type\", writer.FormDataContentType())\n\tresp, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn ErrNotValid\n\t}\n\treturn nil\n}\n\nfunc (this Ipfs) Touch(path string) error {\n\tif _, err := this.Stat(path); err == nil {\n\t\treturn nil\n\t}\n\treturn this.Save(path, strings.NewReader(\"\"))\n}\n\nfunc (this Ipfs) query(cmd string, response any) error {\n\treq, err := http.NewRequest(http.MethodPost, this.url+cmd, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq.Header.Set(\"Accept\", \"application/json\")\n\tresp, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn ErrNotValid\n\t}\n\tif response != nil {\n\t\treturn json.NewDecoder(resp.Body).Decode(&response)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_ldap/index.go",
    "content": "package plg_backend_ldap\n\n/*\n * Introduction\n * ============\n * To get a sample of what this backend can do:\n * - example.com: http://127.0.0.1:8334/login#type=ldap&hostname=ldap://ldap.forumsys.com&bind_dn=uid%3Dtesla,dc%3Dexample,dc%3Dcom&bind_password=password&base_dn=dc%3Dexample,dc%3Dcom\n * - freeipa:     http://127.0.0.1:8334/login#type=ldap&hostname=ldap://ipa.demo1.freeipa.org&bind_dn=uid%3Dadmin,cn%3Dusers,cn%3Daccounts,dc%3Ddemo1,dc%3Dfreeipa,dc%3Dorg&bind_password=Secret123&base_dn=dc%3Ddemo1,dc%3Dfreeipa,dc%3Dorg\n */\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"github.com/go-ldap/ldap/v3\"\n)\n\nvar LDAPCache AppCache\n\nfunc init() {\n\tBackend.Register(\"ldap\", LDAP{})\n\tLDAPCache = NewAppCache(2, 1)\n\tLDAPCache.OnEvict(func(key string, value interface{}) {\n\t\tc := value.(*LDAP)\n\t\tc.dial.Close()\n\t})\n}\n\ntype LDAP struct {\n\tdial   *ldap.Conn\n\tbaseDN string\n}\n\nfunc (this LDAP) Init(params map[string]string, app *App) (IBackend, error) {\n\tif obj := LDAPCache.Get(params); obj != nil {\n\t\treturn obj.(*LDAP), nil\n\t}\n\n\tdialURL := func() string {\n\t\tif params[\"port\"] == \"\" {\n\t\t\t// default port will be set by the LDAP library\n\t\t\treturn params[\"hostname\"]\n\t\t}\n\t\treturn fmt.Sprintf(\"%s:%s\", params[\"hostname\"], params[\"port\"])\n\t}()\n\n\tl, err := ldap.DialURL(dialURL)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err = l.Bind(params[\"bind_dn\"], params[\"bind_password\"]); err != nil {\n\t\treturn nil, err\n\t}\n\n\tb := &LDAP{baseDN: params[\"base_dn\"], dial: l}\n\tLDAPCache.Set(params, b)\n\treturn b, nil\n}\n\nfunc (this LDAP) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"ldap\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"hostname\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Hostname\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"bind_dn\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"bind DN\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"bind_password\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"Bind DN password\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"base_dn\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Base DN\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"advanced\",\n\t\t\t\tType:        \"enable\",\n\t\t\t\tPlaceholder: \"Advanced\",\n\t\t\t\tTarget:      []string{\"ldap_path\", \"ldap_port\"},\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"ldap_path\",\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Path\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"ldap_port\",\n\t\t\t\tName:        \"port\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"Port\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this LDAP) Ls(path string) ([]os.FileInfo, error) {\n\tbaseDN := this.pathToBase(path)\n\tfiles := make([]os.FileInfo, 0)\n\n\t// explore the current folder\n\tsr, err := this.dial.Search(ldap.NewSearchRequest(\n\t\tbaseDN,\n\t\tldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false,\n\t\t\"(objectClass=*)\",\n\t\t[]string{\"objectClass\"},\n\t\tnil,\n\t))\n\tif err != nil {\n\t\treturn files, err\n\t}\n\n\tfor i := 0; i < len(sr.Entries); i++ {\n\t\tentry := sr.Entries[i]\n\n\t\t// filename as will appear in the UI:\n\t\tfilename := strings.TrimSuffix(entry.DN, \",\"+baseDN)\n\n\t\t// data type as will appear in the UI\n\t\tt := \"file\"\n\t\tif len(entry.Attributes) != 1 {\n\t\t\tcontinue\n\t\t}\n\t\tobjectClasses := entry.Attributes[0].Values\n\t\tfor j := 0; j < len(objectClasses); j++ {\n\t\t\tif s := Schema[objectClasses[j]]; s != nil {\n\t\t\t\tif s.IsContainer {\n\t\t\t\t\tt = \"directory\"\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif t == \"file\" {\n\t\t\tfilename += \".form\"\n\t\t}\n\n\t\tfiles = append(files, File{\n\t\t\tFName: filename,\n\t\t\tFType: t,\n\t\t\tFTime: 1497276000000,\n\t\t\tFSize: -1,\n\t\t})\n\t}\n\treturn files, nil\n}\n\nfunc (this LDAP) Stat(path string) (os.FileInfo, error) {\n\treturn nil, ErrNotImplemented\n}\n\nfunc (this LDAP) Cat(path string) (io.ReadCloser, error) {\n\t///////////////////////////////////////////////\n\t// STEP1: search for the requested entry\n\tbaseDN := this.pathToBase(path)\n\tsr, err := this.dial.Search(ldap.NewSearchRequest(\n\t\tbaseDN,\n\t\tldap.ScopeBaseObject, ldap.NeverDerefAliases, 2, 0, false,\n\t\t\"(objectClass=*)\",\n\t\t[]string{},\n\t\tnil,\n\t))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(sr.Entries) != 1 {\n\t\treturn nil, ErrNotValid\n\t}\n\tentry := sr.Entries[0]\n\n\t///////////////////////////////////////////////\n\t// STEP2: create the form that fits in the entry schema\n\tvar forms []FormElement = []FormElement{\n\t\tNewFormElementFromAttributeWithValue(\"dn\", baseDN),\n\t\tNewFormElementFromAttributeWithValue(\"objectClass\", strings.Join(entry.GetAttributeValues(\"objectClass\"), \", \")),\n\t}\n\tforms[0].ReadOnly = true\n\tforms[0].Required = true\n\n\trequired := make([]FormElement, 0)\n\toptional := make([]FormElement, 0)\n\tfor _, value := range entry.GetAttributeValues(\"objectClass\") {\n\t\trequired = append(required, FindRequiredAttributesForObject(value)...)\n\t\toptional = append(optional, FindOptionalAttributesForObject(value)...)\n\t}\n\tsort.SliceStable(required, sortFormElement(required))\n\tsort.SliceStable(optional, sortFormElement(optional))\n\tforms = append(forms, required...)\n\tforms = append(forms, optional...)\n\n\t///////////////////////////////////////////////\n\t// STEP3: fillup the form with the entry values\n\n\tfor i := 0; i < len(entry.Attributes); i++ {\n\t\tdata := struct {\n\t\t\tkey   string\n\t\t\tvalue string\n\t\t}{\n\t\t\tkey:   entry.Attributes[i].Name,\n\t\t\tvalue: strings.Join(entry.Attributes[i].Values, \", \"),\n\t\t}\n\n\t\tvar i int\n\t\tfor i, _ = range forms {\n\t\t\tif forms[i].Name == data.key {\n\t\t\t\tforms[i].Value = data.value\n\t\t\t}\n\t\t}\n\n\t\tif i == len(forms) {\n\t\t\tforms = append(forms, NewFormElementFromAttributeWithValue(data.key, data.value))\n\t\t}\n\n\t\tif forms[i].Name == \"gidNumber\" {\n\t\t\tfor _, value := range entry.GetAttributeValues(\"objectClass\") {\n\t\t\t\tif value == \"posixAccount\" {\n\t\t\t\t\tforms[i].Datalist = this.autocompleteLDAP(\"(objectclass=posixGroup)\", \"gidNumber\")\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t} else if forms[i].Name == \"memberUid\" {\n\t\t\tfor _, value := range entry.GetAttributeValues(\"objectClass\") {\n\t\t\t\tif value == \"posixGroup\" {\n\t\t\t\t\tforms[i].Datalist = this.autocompleteLDAP(\"(objectclass=posixAccount)\", \"cn\")\n\t\t\t\t\tforms[i].MultiValue = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t///////////////////////////////////////////////\n\t// STEP4: Send the form data over\n\tb, err := Form{Elmnts: forms}.MarshalJSON()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewReadCloserFromBytes(b), nil\n}\n\nfunc (this LDAP) Mkdir(path string) error {\n\tldapNode := strings.Split(filepath.Base(path), \"=\")\n\tif len(ldapNode) != 2 {\n\t\treturn ErrNotValid\n\t}\n\n\tvar objectClass string\n\tswitch ldapNode[0] {\n\tcase \"ou\":\n\t\tobjectClass = \"organizationalUnit\"\n\tcase \"o\":\n\t\tobjectClass = \"organization\"\n\tcase \"c\":\n\t\tobjectClass = \"country\"\n\tdefault:\n\t\treturn ErrNotValid\n\t}\n\n\tforms := FindRequiredAttributesForObject(objectClass)\n\tfor i := range forms {\n\t\tif forms[i].Name == \"objectClass\" {\n\t\t\tforms[i].Value = strings.Join(FindDerivatedClasses(objectClass), \", \")\n\t\t} else {\n\t\t\tforms[i].Value = ldapNode[1]\n\t\t}\n\t}\n\n\tif err := this.dial.Add(&ldap.AddRequest{\n\t\tDN: this.pathToBase(path),\n\t\tAttributes: func() []ldap.Attribute {\n\t\t\tattributes := make([]ldap.Attribute, 0, len(forms))\n\t\t\tfor i := 0; i < len(forms); i++ {\n\t\t\t\tattributes = append(attributes, ldap.Attribute{\n\t\t\t\t\tType: forms[i].Name,\n\t\t\t\t\tVals: strings.Split(fmt.Sprintf(\"%s\", forms[i].Value), \", \"),\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn attributes\n\t\t}(),\n\t}); err != nil {\n\t\treturn ErrPermissionDenied\n\t}\n\treturn nil\n}\n\nfunc (this LDAP) Rm(path string) error {\n\tvar err error\n\n\tsr, err := this.dial.Search(ldap.NewSearchRequest(\n\t\tthis.pathToBase(path),\n\t\tldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,\n\t\t\"(objectClass=*)\",\n\t\t[]string{},\n\t\tnil,\n\t))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor i := len(sr.Entries) - 1; i >= 0; i-- {\n\t\tif err == nil {\n\t\t\terr = this.dial.Del(&ldap.DelRequest{\n\t\t\t\tDN: sr.Entries[i].DN,\n\t\t\t})\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (this LDAP) Mv(from string, to string) error {\n\ttoBase := this.pathToBase(to)\n\tfromBase := this.pathToBase(from)\n\n\treturn this.dial.ModifyDN(&ldap.ModifyDNRequest{\n\t\tDN: fromBase,\n\t\tNewRDN: func(t string) string {\n\t\t\ta := strings.Split(t, \",\")\n\t\t\tif len(a) == 0 {\n\t\t\t\treturn t\n\t\t\t}\n\t\t\treturn a[0]\n\t\t}(toBase),\n\t\tDeleteOldRDN: true,\n\t\tNewSuperior: func(t string) string {\n\t\t\ta := strings.Split(t, \",\")\n\t\t\tif len(a) == 0 {\n\t\t\t\treturn t\n\t\t\t}\n\t\t\treturn strings.Join(a[1:], \",\")\n\t\t}(toBase),\n\t})\n}\n\nfunc (this LDAP) Touch(path string) error {\n\tldapNode := strings.Split(filepath.Base(path), \"=\")\n\tif len(ldapNode) != 2 {\n\t\treturn ErrNotValid\n\t}\n\tvar objectClass []string\n\tswitch ldapNode[0] {\n\tcase \"cn\":\n\t\tobjectClass = []string{\"inetOrgPerson\", \"posixAccount\"}\n\tdefault:\n\t\treturn ErrNotValid\n\t}\n\tldapNode[1] = strings.TrimSuffix(ldapNode[1], \".form\")\n\n\tvar uniqueForms map[string]FormElement = make(map[string]FormElement)\n\tfor i := 0; i < len(objectClass); i++ {\n\t\tfor _, obj := range FindRequiredAttributesForObject(objectClass[i]) {\n\t\t\tuniqueForms[obj.Name] = obj\n\t\t}\n\t}\n\tvar forms []FormElement = make([]FormElement, 0)\n\tfor _, element := range uniqueForms {\n\t\tif element.Name == \"objectClass\" {\n\t\t\telement.Value = strings.Join(objectClass, \", \")\n\t\t} else {\n\t\t\telement.Value = this.generateLDAP(element.Name, ldapNode[1])\n\t\t}\n\t\tforms = append(forms, element)\n\t}\n\n\tif err := this.dial.Add(&ldap.AddRequest{\n\t\tDN: this.pathToBase(path),\n\t\tAttributes: func() []ldap.Attribute {\n\t\t\tattributes := make([]ldap.Attribute, 0, len(forms))\n\t\t\tfor i := 0; i < len(forms); i++ {\n\t\t\t\tattributes = append(attributes, ldap.Attribute{\n\t\t\t\t\tType: forms[i].Name,\n\t\t\t\t\tVals: strings.Split(fmt.Sprintf(\"%s\", forms[i].Value), \", \"),\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn attributes\n\t\t}(),\n\t}); err != nil {\n\t\treturn ErrPermissionDenied\n\t}\n\treturn nil\n}\n\nfunc (this LDAP) Save(path string, file io.Reader) error {\n\tvar data map[string]FormElement\n\tif err := json.NewDecoder(file).Decode(&data); err != nil {\n\t\treturn err\n\t} else if data[\"dn\"].Value == nil {\n\t\treturn ErrNotValid\n\t}\n\tif this.pathToBase(path) != data[\"dn\"].Value { // change in the path can only be perform via `MV`\n\t\treturn ErrNotAllowed\n\t}\n\n\tsr, err := this.dial.Search(ldap.NewSearchRequest(\n\t\tfmt.Sprintf(\"%s\", data[\"dn\"].Value),\n\t\tldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false,\n\t\t\"(objectClass=*)\",\n\t\t[]string{},\n\t\tnil,\n\t))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(sr.Entries) != 1 {\n\t\treturn ErrNotValid\n\t}\n\n\tvar attributes map[string]*[]string = make(map[string]*[]string)\n\tfor i := 0; i < len(sr.Entries[0].Attributes); i++ {\n\t\tattributes[sr.Entries[0].Attributes[i].Name] = &sr.Entries[0].Attributes[i].Values\n\t}\n\tmodifyRequest := ldap.NewModifyRequest(fmt.Sprintf(\"%s\", data[\"dn\"].Value), nil)\n\tfor key := range data {\n\t\tif data[key].Value == nil || key == \"dn\" {\n\t\t\tcontinue\n\t\t}\n\t\tif attributes[key] == nil {\n\t\t\tmodifyRequest.Add(key, strings.Split(fmt.Sprintf(\"%s\", data[key].Value), \", \"))\n\t\t} else if data[key].Value != strings.Join(*attributes[key], \", \") {\n\t\t\tmodifyRequest.Replace(key, strings.Split(fmt.Sprintf(\"%s\", data[key].Value), \", \"))\n\t\t}\n\t}\n\tfor key := range attributes {\n\t\tif data[key].Value == nil && attributes[key] != nil {\n\t\t\tmodifyRequest.Delete(key, *attributes[key])\n\t\t}\n\t}\n\n\tif err := this.dial.Modify(modifyRequest); err != nil {\n\t\treturn ErrPermissionDenied\n\t}\n\treturn nil\n}\n\nfunc (this LDAP) Meta(path string) Metadata {\n\treturn Metadata{\n\t\tCanUpload:       NewBool(false),\n\t\tHideExtension:   NewBool(true),\n\t\tRefreshOnCreate: NewBool(true),\n\t}\n}\n\nfunc (this LDAP) pathToBase(path string) string {\n\tpath = strings.TrimSuffix(path, \".form\")\n\tif path = strings.Trim(path, \"/\"); path == \"\" {\n\t\treturn this.baseDN\n\t}\n\tpathArray := strings.Split(path, \"/\")\n\tbaseArray := strings.Split(this.baseDN, \",\")\n\treversedPath := []string{}\n\tfor i := len(pathArray) - 1; i >= 0; i-- {\n\t\treversedPath = append(reversedPath, pathArray[i])\n\t}\n\treturn strings.Join(append(reversedPath, baseArray...), \",\")\n}\n\nfunc (this LDAP) autocompleteLDAP(filter string, value string) []string {\n\tval := []string{}\n\tsr, err := this.dial.Search(ldap.NewSearchRequest(\n\t\tthis.baseDN,\n\t\tldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,\n\t\tfilter,\n\t\t[]string{value},\n\t\tnil,\n\t))\n\tif err != nil {\n\t\treturn val\n\t}\n\tfor i := 0; i < len(sr.Entries); i++ {\n\t\tval = append(val, sr.Entries[i].GetAttributeValue(value))\n\t}\n\tsort.Strings(val)\n\treturn val\n}\n\nfunc (this LDAP) generateLDAP(name string, deflts string) string {\n\td := strings.Split(deflts, \"-\")\n\tswitch name {\n\tcase \"cn\":\n\t\treturn strings.ToLower(deflts)\n\tcase \"uid\":\n\t\treturn strings.ToLower(deflts)\n\tcase \"uidNumber\":\n\t\treturn \"65534\"\n\tcase \"homeDirectory\":\n\t\treturn \"/home/\" + strings.ToLower(deflts)\n\tcase \"loginShell\":\n\t\treturn \"/bin/false\"\n\tcase \"aliasedObjectName\":\n\t\treturn strings.ToLower(deflts)\n\tcase \"c\":\n\t\treturn strings.ToLower(deflts)\n\tcase \"o\":\n\t\treturn strings.ToLower(deflts)\n\tcase \"userPassword\":\n\t\treturn \"welcome\"\n\tcase \"gidNumber\":\n\t\treturn \"65534\"\n\tcase \"sn\":\n\t\tif len(d) == 2 {\n\t\t\treturn strings.Title(d[1])\n\t\t}\n\t\treturn strings.Title(strings.Join(d, \" \"))\n\tcase \"givenName\":\n\t\tif len(d) == 2 {\n\t\t\treturn strings.Title(d[0])\n\t\t}\n\t\treturn strings.Title(strings.Join(d, \" \"))\n\tdefault:\n\t\treturn deflts\n\t}\n}\n\ntype LDAPSchema struct {\n\tIsContainer bool     // can be used as a folder to store more entry?\n\tDescription string   // doc string coming from the schema\n\tType        string   // AUXILIARY / STRUCTURAL or ABSTRACT\n\tSilent      bool     // show up (or not) as part of the client autocomplete\n\tRequired    []string // required attributes\n\tOptional    []string // optional attributes\n\tInherit     []string // class this schema inherits\n}\n\nfunc FindRequiredAttributesForObject(objectClass string) []FormElement {\n\tif Schema[objectClass] == nil {\n\t\treturn make([]FormElement, 0)\n\t}\n\telements := make([]FormElement, 0, len(Schema[objectClass].Required))\n\tfor i := 0; i < len(Schema[objectClass].Inherit); i++ {\n\t\tels := FindRequiredAttributesForObject(Schema[objectClass].Inherit[i])\n\t\telements = append(elements, els...)\n\t}\n\tfor i := 0; i < len(Schema[objectClass].Required); i++ {\n\t\telements = append(\n\t\t\telements,\n\t\t\tfunc() FormElement {\n\t\t\t\tf := NewFormElementFromAttribute(Schema[objectClass].Required[i])\n\t\t\t\tf.Required = true\n\t\t\t\treturn f\n\t\t\t}(),\n\t\t)\n\t}\n\treturn elements\n}\n\nfunc FindOptionalAttributesForObject(objectClass string) []FormElement {\n\tif Schema[objectClass] == nil {\n\t\treturn make([]FormElement, 0)\n\t}\n\telements := make([]FormElement, 0, len(Schema[objectClass].Optional))\n\tfor i := 0; i < len(Schema[objectClass].Inherit); i++ {\n\t\tels := FindOptionalAttributesForObject(Schema[objectClass].Inherit[i])\n\t\telements = append(elements, els...)\n\t}\n\tfor i := 0; i < len(Schema[objectClass].Optional); i++ {\n\t\telements = append(\n\t\t\telements,\n\t\t\tNewFormElementFromAttribute(Schema[objectClass].Optional[i]),\n\t\t)\n\t}\n\treturn elements\n}\n\nfunc NewFormElementFromAttribute(attr string) FormElement {\n\tvar form FormElement = FormElement{}\n\tif LDAPAttribute[attr] != nil {\n\t\tform = *LDAPAttribute[attr]\n\t}\n\tif form.Name == \"\" {\n\t\tform.Name = attr\n\t}\n\tif form.Type == \"\" {\n\t\tform.Type = \"text\"\n\t}\n\treturn form\n}\n\nfunc NewFormElementFromAttributeWithValue(attr string, value string) FormElement {\n\tf := NewFormElementFromAttribute(attr)\n\tf.Value = value\n\treturn f\n}\n\nfunc FindDerivatedClasses(objectClass string) []string {\n\tclasses := []string{objectClass}\n\tif Schema[objectClass] == nil {\n\t\treturn classes\n\t}\n\tfor i := 0; i < len(Schema[objectClass].Inherit); i++ {\n\t\tclasses = append(classes, FindDerivatedClasses(Schema[objectClass].Inherit[i])...)\n\t}\n\treturn classes\n}\n\nfunc sortFormElement(e []FormElement) func(i, j int) bool {\n\treturn func(i, j int) bool {\n\t\tl := LDAPAttribute[e[i].Name]\n\t\tr := LDAPAttribute[e[j].Name]\n\n\t\tif l == nil && r == nil { // tie\n\t\t\treturn false\n\t\t} else if r == nil {\n\t\t\treturn true\n\t\t} else if l == nil {\n\t\t\treturn false\n\t\t}\n\n\t\tif l.Order == 0 && r.Order == 0 {\n\t\t\treturn false\n\t\t} else if l.Order == 0 {\n\t\t\treturn false\n\t\t} else if r.Order == 0 {\n\t\t\treturn true\n\t\t}\n\t\treturn l.Order < r.Order\n\t}\n}\n\n/*\n * The following is loading LDAP schema that was found on the openLDAP directory:\n * https://github.com/openldap/openldap/tree/master/servers/slapd/schema\n * As such, the source code in OpenLDAP says:\n * \"Redistribution and use in source and binary forms, with or without modification,\n * are permitted only as authorized by the OpenLDAP Public License.\"\n * This license can be found: http://www.openldap.org/software/release/license.html\n *\n * It includes: core.schema, inetorgperson.schema, collective.schema, corba.schema, cosine.schema\n * duaconf.schema, dyngroup.schema, java.schema, misc.schema, msuser.schema, nis.schema, openldap.schema\n * pmi.schema, ppolicy.schema.\n */\nvar Schema map[string]*LDAPSchema = map[string]*LDAPSchema{\n\t// SCHEMA: core.schema\n\t\"top\": &LDAPSchema{\n\t\tDescription: \"Top of the superclass chain - RFC2256\",\n\t\tType:        \"ABSTRACT\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{},\n\t\tRequired:    []string{\"objectClass\"},\n\t\tOptional:    []string{},\n\t},\n\t\"alias\": &LDAPSchema{\n\t\tDescription: \"An alias - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"aliasedObjectName\"},\n\t\tOptional:    []string{},\n\t},\n\t\"country\": &LDAPSchema{\n\t\tDescription: \"A country - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: true,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"c\"},\n\t\tOptional:    []string{\"searchGuide\", \"description\"},\n\t},\n\t\"locality\": &LDAPSchema{\n\t\tDescription: \"A locality - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"street\", \"seeAlso\", \"searchGuide\", \"st\", \"l\", \"description\"},\n\t},\n\t\"organization\": &LDAPSchema{\n\t\tDescription: \"An organization - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: true,\n\t\tSilent:      false,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"o\"},\n\t\tOptional:    []string{\"userPassword\", \"searchGuide\", \"seeAlso\", \"businessCategory\", \"x121Address\", \"registeredAddress\", \"destinationIndicator\", \"preferredDeliveryMethod\", \"telexNumber\", \"teletexTerminalIdentifier\", \"telephoneNumber\", \"internationaliSDNNumber\", \"facsimileTelephoneNumber\", \"street\", \"postOfficeBox\", \"postalCode\", \"postalAddress\", \"physicalDeliveryOfficeName\", \"st\", \"l\", \"description\"},\n\t},\n\t\"organizationalUnit\": &LDAPSchema{\n\t\tDescription: \"An organizational unit - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: true,\n\t\tSilent:      false,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"ou\"},\n\t\tOptional:    []string{\"userPassword\", \"searchGuide\", \"seeAlso\", \"businessCategory\", \"x121Address\", \"registeredAddress\", \"destinationIndicator\", \"preferredDeliveryMethod\", \"telexNumber\", \"teletexTerminalIdentifier\", \"telephoneNumber\", \"internationaliSDNNumber\", \"facsimileTelephoneNumber\", \"street\", \"postOfficeBox\", \"postalCode\", \"postalAddress\", \"physicalDeliveryOfficeName\", \"st\", \"l\", \"description\"},\n\t},\n\t\"person\": &LDAPSchema{\n\t\tDescription: \"A person - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      false,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"sn\", \"cn\"},\n\t\tOptional:    []string{\"userPassword\", \"telephoneNumber\", \"seeAlso\", \"description\"},\n\t},\n\t\"organizationalPerson\": &LDAPSchema{\n\t\tDescription: \"An organizational person - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      false,\n\t\tInherit:     []string{\"person\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"title\", \"x121Address\", \"registeredAddress\", \"destinationIndicator\", \"preferredDeliveryMethod\", \"telexNumber\", \"teletexTerminalIdentifier\", \"telephoneNumber\", \"internationaliSDNNumber\", \"facsimileTelephoneNumber\", \"street\", \"postOfficeBox\", \"postalCode\", \"postalAddress\", \"physicalDeliveryOfficeName\", \"ou\", \"st\", \"l\"},\n\t},\n\t\"organizationalRole\": &LDAPSchema{\n\t\tDescription: \"An organizational role - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\"},\n\t\tOptional:    []string{\"x121Address\", \"registeredAddress\", \"destinationIndicator\", \"preferredDeliveryMethod\", \"telexNumber\", \"teletexTerminalIdentifier\", \"telephoneNumber\", \"internationaliSDNNumber\", \"facsimileTelephoneNumber\", \"seeAlso\", \"roleOccupant\", \"preferredDeliveryMethod\", \"street\", \"postOfficeBox\", \"postalCode\", \"postalAddress\", \"physicalDeliveryOfficeName\", \"ou\", \"st\", \"l\", \"description\"},\n\t},\n\t\"groupOfNames\": &LDAPSchema{\n\t\tDescription: \"A group of names (DNs) - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"member\", \"cn\"},\n\t\tOptional:    []string{\"businessCategory\", \"seeAlso\", \"owner\", \"ou\", \"o\", \"description\"},\n\t},\n\t\"residentialPerson\": &LDAPSchema{\n\t\tDescription: \"An residential person - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"person\"},\n\t\tRequired:    []string{\"l\"},\n\t\tOptional:    []string{\"businessCategory\", \"x121Address\", \"registeredAddress\", \"destinationIndicator\", \"preferredDeliveryMethod\", \"telexNumber\", \"teletexTerminalIdentifier\", \"telephoneNumber\", \"internationaliSDNNumber\", \"facsimileTelephoneNumber\", \"preferredDeliveryMethod\", \"street\", \"postOfficeBox\", \"postalCode\", \"postalAddress\", \"physicalDeliveryOfficeName\", \"st\", \"l\"},\n\t},\n\t\"applicationProcess\": &LDAPSchema{\n\t\tDescription: \"An application process - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\"},\n\t\tOptional:    []string{\"seeAlso\", \"ou\", \"l\", \"description\"},\n\t},\n\t\"applicationEntity\": &LDAPSchema{\n\t\tDescription: \"An application entity - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"presentationAddress\", \"cn\"},\n\t\tOptional:    []string{\"supportedApplicationContext\", \"seeAlso\", \"ou\", \"o\", \"l\", \"description\"},\n\t},\n\t\"dSA\": &LDAPSchema{\n\t\tDescription: \"A directory system agent (a server) - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"applicationEntity STRUCTURAL\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"knowledgeInformation\"},\n\t},\n\t\"device\": &LDAPSchema{\n\t\tDescription: \"A device - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\"},\n\t\tOptional:    []string{\"serialNumber\", \"seeAlso\", \"owner\", \"ou\", \"o\", \"l\", \"description\"},\n\t},\n\t\"strongAuthenticationUser\": &LDAPSchema{\n\t\tDescription: \"A strong authentication user - RFC2256\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"userCertificate\"},\n\t\tOptional:    []string{},\n\t},\n\t\"certificationAuthority\": &LDAPSchema{\n\t\tDescription: \"A certificate authority - RFC2256\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"authorityRevocationList\", \"certificateRevocationList\", \"\"},\n\t\tOptional:    []string{\"crossCertificatePair\"},\n\t},\n\t\"groupOfUniqueNames\": &LDAPSchema{\n\t\tDescription: \"A group of unique names (DN and Unique Identifier) - RFC2256\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"uniqueMember\", \"cn\"},\n\t\tOptional:    []string{\"businessCategory\", \"seeAlso\", \"owner\", \"ou\", \"o\", \"description\"},\n\t},\n\t\"userSecurityInformation\": &LDAPSchema{\n\t\tDescription: \"A user security information - RFC2256\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"supportedAlgorithms\"},\n\t},\n\t\"certificationAuthority-V2\": &LDAPSchema{\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"certificationAuthority\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"deltaRevocationList\"},\n\t},\n\t\"cRLDistributionPoint\": &LDAPSchema{\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\"},\n\t\tOptional:    []string{\"certificateRevocationList\", \"authorityRevocationList\", \"deltaRevocationList\"},\n\t},\n\t\"dmd\": &LDAPSchema{\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"dmdName\"},\n\t\tOptional:    []string{\"userPassword\", \"searchGuide\", \"seeAlso\", \"businessCategory\", \"x121Address\", \"registeredAddress\", \"destinationIndicator\", \"preferredDeliveryMethod\", \"telexNumber\", \"teletexTerminalIdentifier\", \"telephoneNumber\", \"internationaliSDNNumber\", \"facsimileTelephoneNumber\", \"street\", \"postOfficeBox\", \"postalCode\", \"postalAddress\", \"physicalDeliveryOfficeName\", \"st\", \"l\", \"description\"},\n\t},\n\t\"pkiUser\": &LDAPSchema{\n\t\tDescription: \"A PKI user - RFC2587\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"userCertificate\"},\n\t},\n\t\"pkiCA\": &LDAPSchema{\n\t\tDescription: \"PKI certificate authority - RFC2587\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"authorityRevocationList\", \"certificateRevocationList\", \"cACertificate\", \"crossCertificatePair\"},\n\t},\n\t\"deltaCRL\": &LDAPSchema{\n\t\tDescription: \"PKI user - RFC2587\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"deltaRevocationList\"},\n\t},\n\t\"labeledURIObject\": &LDAPSchema{\n\t\tDescription: \"Object that contains the URI attribute type - RFC2079\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"labeledURI\"},\n\t},\n\t\"simpleSecurityObject\": &LDAPSchema{\n\t\tDescription: \"Simple security object - RFC1274\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      false,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"userPassword\"},\n\t\tOptional:    []string{},\n\t},\n\t\"dcObject\": &LDAPSchema{\n\t\tDescription: \"Domain component object - RFC2247\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: true,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"dc\"},\n\t\tOptional:    []string{},\n\t},\n\t\"uidObject\": &LDAPSchema{\n\t\tDescription: \"Uid object - RFC2377\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"uid\"},\n\t\tOptional:    []string{},\n\t},\n\t// SCHEMA: inetorgperson.schema\n\t\"inetOrgPerson\": &LDAPSchema{\n\t\tDescription: \"Internet Organizational Person - RFC2798\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      false,\n\t\tInherit:     []string{\"organizationalPerson\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"audio\", \"businessCategory\", \"carLicense\", \"departmentNumber\", \"displayName\", \"employeeNumber\", \"employeeType\", \"givenName\", \"homePhone\", \"homePostalAddress\", \"initials\", \"jpegPhoto\", \"labeledURI\", \"mail\", \"manager\", \"mobile\", \"o\", \"pager\", \"photo\", \"roomNumber\", \"secretary\", \"uid\", \"userCertificate\", \"x500uniqueIdentifier\", \"preferredLanguage\", \"userSMIMECertificate\", \"userPKCS12\"},\n\t},\n\t// SCHEMA: collective.schema\n\t// SCHEMA: corba.schema\n\t\"corbaContainer\": &LDAPSchema{\n\t\tDescription: \"Container for a CORBA object\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\"},\n\t\tOptional:    []string{},\n\t},\n\t\"corbaObject\": &LDAPSchema{\n\t\tDescription: \"CORBA object representation\",\n\t\tType:        \"ABSTRACT\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"corbaRepositoryId\", \"description\"},\n\t},\n\t\"corbaObjectReference\": &LDAPSchema{\n\t\tDescription: \"CORBA interoperable object reference\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"corbaObject\"},\n\t\tRequired:    []string{\"corbaIor\"},\n\t\tOptional:    []string{},\n\t},\n\t// SCHEMA: cosine.schema\n\t\"pilotObject\": &LDAPSchema{\n\t\tDescription: \"Pilot object - RFC1274\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"info\", \"photo\", \"manager\", \"uniqueIdentifier\", \"lastModifiedTime\", \"lastModifiedBy\", \"dITRedirect\", \"audio\"},\n\t},\n\t\"pilotPerson\": &LDAPSchema{\n\t\tDescription: \"The PilotPerson object class is used as a sub-class of person, to allow the use of a number of additional attributes to be assigned to entries of object class person\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"person\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"userid\", \"textEncodedORAddress\", \"rfc822Mailbox\", \"favouriteDrink\", \"roomNumber\", \"userClass\", \"homeTelephoneNumber\", \"homePostalAddress\", \"secretary\", \"personalTitle\", \"preferredDeliveryMethod\", \"businessCategory\", \"janetMailbox\", \"otherMailbox\", \"mobileTelephoneNumber\", \"pagerTelephoneNumber\", \"organizationalStatus\", \"mailPreferenceOption\", \"personalSignature\"},\n\t},\n\t\"newPilotPerson\": &LDAPSchema{\n\t\tDescription: \"The PilotPerson object class is used as a sub-class of person, to allow the use of a number of additional attributes to be assigned to entries of object class person\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"person\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"userid\", \"textEncodedORAddress\", \"rfc822Mailbox\", \"favouriteDrink\", \"roomNumber\", \"userClass\", \"homeTelephoneNumber\", \"homePostalAddress\", \"secretary\", \"personalTitle\", \"preferredDeliveryMethod\", \"businessCategory\", \"janetMailbox\", \"otherMailbox\", \"mobileTelephoneNumber\", \"pagerTelephoneNumber\", \"organizationalStatus\", \"mailPreferenceOption\", \"personalSignature\"},\n\t},\n\t\"account\": &LDAPSchema{\n\t\tDescription: \"The Account object class is used to define entries representing computer accounts.  The userid attribute should be used for naming entries of this object class.\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"userid\"},\n\t\tOptional:    []string{\"description\", \"seeAlso\", \"localityName\", \"organizationName\", \"organizationalUnitName\", \"host\"},\n\t},\n\t\"document\": &LDAPSchema{\n\t\tDescription: \"The Document object class is used to define entries which represent documents.\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"documentIdentifier\"},\n\t\tOptional:    []string{\"commonName\", \"description\", \"seeAlso\", \"localityName\", \"organizationName\", \"organizationalUnitName\", \"documentTitle\", \"documentVersion\", \"documentAuthor\", \"documentLocation\", \"documentPublisher\"},\n\t},\n\t\"room\": &LDAPSchema{\n\t\tDescription: \"The Room object class is used to define entries representing rooms. The commonName attribute should be used for naming pentries of this object class.\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"commonName\"},\n\t\tOptional:    []string{\"roomNumber\", \"description\", \"seeAlso\", \"telephoneNumber\"},\n\t},\n\t\"documentSeries\": &LDAPSchema{\n\t\tDescription: \"The Document Series object class is used to define an entry which represents a series of documents (e.g., The Request For Comments papers).\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"commonName\"},\n\t\tOptional:    []string{\"description\", \"seeAlso\", \"telephonenumber\", \"localityName\", \"organizationName\", \"organizationalUnitName\"},\n\t},\n\t\"domain\": &LDAPSchema{\n\t\tDescription: \"The Domain object class is used to define entries which represent DNS or NRS domains.  The domainComponent attribute should be used for naming entries of this object class.\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"domainComponent\"},\n\t\tOptional:    []string{\"associatedName\", \"organizationName\", \"description\", \"businessCategory\", \"seeAlso\", \"searchGuide\", \"userPassword\", \"localityName\", \"stateOrProvinceName\", \"streetAddress\", \"physicalDeliveryOfficeName\", \"postalAddress\", \"postalCode\", \"postOfficeBox\", \"streetAddress\", \"facsimileTelephoneNumber\", \"internationalISDNNumber\", \"telephoneNumber\", \"teletexTerminalIdentifier\", \"telexNumber\", \"preferredDeliveryMethod\", \"destinationIndicator\", \"registeredAddress\", \"x121Address\"},\n\t},\n\t\"RFC822localPart\": &LDAPSchema{\n\t\tDescription: \"The RFC822 Local Part object class is used to define entries which represent the local part of RFC822 mail addresses.  This treats this part of an RFC822 address as a domain.\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"domain\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"commonName\", \"surname\", \"description\", \"seeAlso\", \"telephoneNumber\", \"physicalDeliveryOfficeName\", \"postalAddress\", \"postalCode\", \"postOfficeBox\", \"streetAddress\", \"facsimileTelephoneNumber\", \"internationalISDNNumber\", \"telephoneNumber\", \"teletexTerminalIdentifier\", \"telexNumber\", \"preferredDeliveryMethod\", \"destinationIndicator\", \"registeredAddress\", \"x121Address\"},\n\t},\n\t\"dNSDomain\": &LDAPSchema{\n\t\tDescription: \"The DNS Domain (Domain NameServer) object class is used to define entries for DNS domains.\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"domain\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"ARecord\", \"MDRecord\", \"MXRecord\", \"NSRecord\", \"SOARecord\", \"CNAMERecord\"},\n\t},\n\t\"domainRelatedObject\": &LDAPSchema{\n\t\tDescription: \"An object related to an domain - RFC1274. The Domain Related Object object class is used to define entries which represent DNS/NRS domains which are \\\"equivalent\\\" to an X.500 domain: e.g., an organisation or organisational unit\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"associatedDomain\"},\n\t\tOptional:    []string{},\n\t},\n\t\"friendlyCountry\": &LDAPSchema{\n\t\tDescription: \"The Friendly Country object class is used to define country entries in the DIT.  The object class is used to allow friendlier naming of countries than that allowed by the object class country.  The naming attribute of object class country, countryName, has to be a 2 letter string defined in ISO 3166.\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"country\"},\n\t\tRequired:    []string{\"friendlyCountryName\"},\n\t\tOptional:    []string{},\n\t},\n\t\"pilotOrganization\": &LDAPSchema{\n\t\tDescription: \"The PilotOrganization object class is used as a sub-class of organization and organizationalUnit to allow a number of additional attributes to be assigned to entries of object classes organization and organizationalUnit.\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"organization\", \"organizationalUnit\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"buildingName\"},\n\t},\n\t\"pilotDSA\": &LDAPSchema{\n\t\tDescription: \"The PilotDSA object class is used as a sub-class of the dsa object class to allow additional attributes to be assigned to entries for DSAs.\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"dsa\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"dSAQuality\"},\n\t},\n\t\"qualityLabelledData\": &LDAPSchema{\n\t\tDescription: \"The Quality Labelled Data object class is used to allow the ssignment of the data quality attributes to subtrees in the DIT\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"dsaQuality\"},\n\t\tOptional:    []string{\"subtreeMinimumQuality\", \"subtreeMaximumQuality\"},\n\t},\n\t// SCHEMA: duaconf.schema\n\t\"DUAConfigProfile\": &LDAPSchema{\n\t\tDescription: \"Abstraction of a base configuration for a DUA\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\"},\n\t\tOptional:    []string{\"defaultServerList\", \"preferredServerList\", \"defaultSearchBase\", \"defaultSearchScope\", \"searchTimeLimit\", \"bindTimeLimit\", \"credentialLevel\", \"authenticationMethod\", \"followReferrals\", \"dereferenceAliases\", \"serviceSearchDescriptor\", \"serviceCredentialLevel\", \"serviceAuthenticationMethod\", \"objectclassMap\", \"attributeMap\", \"profileTTL\"},\n\t},\n\t// SCHEMA: dyngroup.schema\n\t\"groupOfURLs\": &LDAPSchema{\n\t\tDescription: \"undefined\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\"},\n\t\tOptional:    []string{\"memberURL\", \"businessCategory\", \"description\", \"o\", \"ou\", \"owner\", \"seeAlso\"},\n\t},\n\t\"dgIdentityAux\": &LDAPSchema{\n\t\tDescription: \"undefined\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"dgIdentity\", \"dgAuthz\"},\n\t},\n\t// SCHEMA: java.schema\n\t\"javaContainer\": &LDAPSchema{\n\t\tDescription: \"Container for a Java object\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\"},\n\t\tOptional:    []string{},\n\t},\n\t\"javaObject\": &LDAPSchema{\n\t\tDescription: \"Java object representation\",\n\t\tType:        \"ABSTRACT\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"javaClassName\"},\n\t\tOptional:    []string{\"javaClassNames\", \"javaCodebase\", \"javaDoc\", \"description\"},\n\t},\n\t\"javaSerializedObject\": &LDAPSchema{\n\t\tDescription: \"Java serialized object\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"javaObject\"},\n\t\tRequired:    []string{\"javaSerializedData\"},\n\t\tOptional:    []string{},\n\t},\n\t\"javaMarshalledObject\": &LDAPSchema{\n\t\tDescription: \"Java marshalled object\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"javaObject\"},\n\t\tRequired:    []string{\"javaSerializedData\"},\n\t\tOptional:    []string{},\n\t},\n\t\"javaNamingReference\": &LDAPSchema{\n\t\tDescription: \"JNDI reference\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"javaObject\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"javaReferenceAddress\", \"javaFactory\"},\n\t},\n\t// SCHEMA: misc.schema\n\t\"inetLocalMailRecipient\": &LDAPSchema{\n\t\tDescription: \"Internet local mail recipient\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{},\n\t},\n\t\"nisMailAlias\": &LDAPSchema{\n\t\tDescription: \"NIS mail alias\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\"},\n\t},\n\t// SCHEMA: msuser.schema\n\t\"mstop\": &LDAPSchema{\n\t\tType:        \"ABSTRACT\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"objectClass\", \"instanceType\", \"nTSecurityDescriptor\", \"objectCategory\"},\n\t\tOptional:    []string{\"cn\", \"description\", \"distinguishedName\", \"whenCreated\", \"whenChanged\", \"subRefs\", \"displayName\", \"uSNCreated\", \"isDeleted\", \"dSASignature\", \"objectVersion\", \"repsTo\", \"repsFrom\", \"memberOf\", \"ownerBL\", \"uSNChanged\", \"uSNLastObjRem\", \"showInAdvancedViewOnly\", \"adminDisplayName\", \"proxyAddresses\", \"adminDescription\", \"extensionName\", \"uSNDSALastObjRemoved\", \"displayNamePrintable\", \"directReports\", \"wWWHomePage\", \"USNIntersite\", \"name\", \"objectGUID\", \"replPropertyMetaData\", \"replUpToDateVector\", \"flags\", \"revision\", \"wbemPath\", \"fSMORoleOwner\", \"systemFlags\", \"siteObjectBL\", \"serverReferenceBL\", \"nonSecurityMemberBL\", \"queryPolicyBL\", \"wellKnownObjects\", \"isPrivilegeHolder\", \"partialAttributeSet\", \"managedObjects\", \"partialAttributeDeletionList\", \"url\", \"lastKnownParent\", \"bridgeheadServerListBL\", \"netbootSCPBL\", \"isCriticalSystemObject\", \"frsComputerReferenceBL\", \"fRSMemberReferenceBL\", \"uSNSource\", \"fromEntry\", \"allowedChildClasses\", \"allowedChildClassesEffective\", \"allowedAttributes\", \"allowedAttributesEffective\", \"possibleInferiors\", \"canonicalName\", \"proxiedObjectName\", \"sDRightsEffective\", \"dSCorePropagationData\", \"otherWellKnownObjects\", \"mS-DS-ConsistencyGuid\", \"mS-DS-ConsistencyChildCount\", \"masteredBy\", \"msCOM-PartitionSetLink\", \"msCOM-UserLink\", \"msDS-Approx-Immed-Subordinates\", \"msDS-NCReplCursors\", \"msDS-NCReplInboundNeighbors\", \"msDS-NCReplOutboundNeighbors\", \"msDS-ReplAttributeMetaData\", \"msDS-ReplValueMetaData\", \"msDS-NonMembersBL\", \"msDS-MembersForAzRoleBL\", \"msDS-OperationsForAzTaskBL\", \"msDS-TasksForAzTaskBL\", \"msDS-OperationsForAzRoleBL\", \"msDS-TasksForAzRoleBL\", \"msDs-masteredBy\", \"msDS-ObjectReferenceBL\", \"msDS-PrincipalName\", \"msDS-RevealedDSAs\", \"msDS-KrbTgtLinkBl\", \"msDS-IsFullReplicaFor\", \"msDS-IsDomainFor\", \"msDS-IsPartialReplicaFor\", \"msDS-AuthenticatedToAccountlist\", \"msDS-NC-RO-Replica-Locations-BL\", \"msDS-RevealedListBL\", \"msDS-PSOApplied\", \"msDS-NcType\", \"msDS-OIDToGroupLinkBl\", \"msDS-HostServiceAccountBL\", \"isRecycled\", \"msDS-LocalEffectiveDeletionTime\", \"msDS-LocalEffectiveRecycleTime\", \"msDS-LastKnownRDN\", \"msDS-EnabledFeatureBL\", \"msDS-ClaimSharesPossibleValuesWithBL\", \"msDS-MembersOfResourcePropertyListBL\", \"msDS-IsPrimaryComputerFor\", \"msDS-ValueTypeReferenceBL\", \"msDS-TDOIngressBL\", \"msDS-TDOEgressBL\", \"msDS-parentdistname\", \"msDS-ReplValueMetaDataExt\", \"msds-memberOfTransitive\", \"msds-memberTransitive\", \"msSFU30PosixMemberOf\", \"msDFSR-MemberReferenceBL\", \"msDFSR-ComputerReferenceBL\"},\n\t},\n\t\"group\": &LDAPSchema{\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"mstop\"},\n\t\tRequired:    []string{\"groupType\"},\n\t\tOptional:    []string{\"member\", \"nTGroupMembers\", \"operatorCount\", \"adminCount\", \"groupAttributes\", \"groupMembershipSAM\", \"controlAccessRights\", \"desktopProfile\", \"nonSecurityMember\", \"managedBy\", \"primaryGroupToken\", \"msDS-AzLDAPQuery\", \"msDS-NonMembers\", \"msDS-AzBizRule\", \"msDS-AzBizRuleLanguage\", \"msDS-AzLastImportedBizRulePath\", \"msDS-AzApplicationData\", \"msDS-AzObjectGuid\", \"msDS-AzGenericData\", \"msDS-PrimaryComputer\", \"mail\", \"msSFU30Name\", \"msSFU30NisDomain\", \"msSFU30PosixMember\"},\n\t},\n\t\"user\": &LDAPSchema{\n\t\tDescription: \"undefined\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"mstop\", \"organizationalPerson\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"o\", \"businessCategory\", \"userCertificate\", \"givenName\", \"initials\", \"x500uniqueIdentifier\", \"displayName\", \"networkAddress\", \"employeeNumber\", \"employeeType\", \"homePostalAddress\", \"userAccountControl\", \"badPwdCount\", \"codePage\", \"homeDirectory\", \"homeDrive\", \"badPasswordTime\", \"lastLogoff\", \"lastLogon\", \"dBCSPwd\", \"localeID\", \"scriptPath\", \"logonHours\", \"logonWorkstation\", \"maxStorage\", \"userWorkstations\", \"unicodePwd\", \"otherLoginWorkstations\", \"ntPwdHistory\", \"pwdLastSet\", \"preferredOU\", \"primaryGroupID\", \"userParameters\", \"profilePath\", \"operatorCount\", \"adminCount\", \"accountExpires\", \"lmPwdHistory\", \"groupMembershipSAM\", \"logonCount\", \"controlAccessRights\", \"defaultClassStore\", \"groupsToIgnore\", \"groupPriority\", \"desktopProfile\", \"dynamicLDAPServer\", \"userPrincipalName\", \"lockoutTime\", \"userSharedFolder\", \"userSharedFolderOther\", \"servicePrincipalName\", \"aCSPolicyName\", \"terminalServer\", \"mSMQSignCertificates\", \"mSMQDigests\", \"mSMQDigestsMig\", \"mSMQSignCertificatesMig\", \"msNPAllowDialin\", \"msNPCallingStationID\", \"msNPSavedCallingStationID\", \"msRADIUSCallbackNumber\", \"msRADIUSFramedIPAddress\", \"msRADIUSFramedRoute\", \"msRADIUSServiceType\", \"msRASSavedCallbackNumber\", \"msRASSavedFramedIPAddress\", \"msRASSavedFramedRoute\", \"mS-DS-CreatorSID\", \"msCOM-UserPartitionSetLink\", \"msDS-Cached-Membership\", \"msDS-Cached-Membership-Time-Stamp\", \"msDS-Site-Affinity\", \"msDS-User-Account-Control-Computed\", \"lastLogonTimestamp\", \"msIIS-FTPRoot\", \"msIIS-FTPDir\", \"msDRM-IdentityCertificate\", \"msDS-SourceObjectDN\", \"msPKIRoamingTimeStamp\", \"msPKIDPAPIMasterKeys\", \"msPKIAccountCredentials\", \"msRADIUS-FramedInterfaceId\", \"msRADIUS-SavedFramedInterfaceId\", \"msRADIUS-FramedIpv6Prefix\", \"msRADIUS-SavedFramedIpv6Prefix\", \"msRADIUS-FramedIpv6Route\", \"msRADIUS-SavedFramedIpv6Route\", \"msDS-SecondaryKrbTgtNumber\", \"msDS-AuthenticatedAtDC\", \"msDS-SupportedEncryptionTypes\", \"msDS-LastSuccessfulInteractiveLogonTime\", \"msDS-LastFailedInteractiveLogonTime\", \"msDS-FailedInteractiveLogonCount\", \"msDS-FailedInteractiveLogonCountAtLastSuccessfulLogon\", \"msTSProfilePath\", \"msTSHomeDirectory\", \"msTSHomeDrive\", \"msTSAllowLogon\", \"msTSRemoteControl\", \"msTSMaxDisconnectionTime\", \"msTSMaxConnectionTime\", \"msTSMaxIdleTime\", \"msTSReconnectionAction\", \"msTSBrokenConnectionAction\", \"msTSConnectClientDrives\", \"msTSConnectPrinterDrives\", \"msTSDefaultToMainPrinter\", \"msTSWorkDirectory\", \"msTSInitialProgram\", \"msTSProperty01\", \"msTSProperty02\", \"msTSExpireDate\", \"msTSLicenseVersion\", \"msTSManagingLS\", \"msDS-UserPasswordExpiryTimeComputed\", \"msTSExpireDate2\", \"msTSLicenseVersion2\", \"msTSManagingLS2\", \"msTSExpireDate3\", \"msTSLicenseVersion3\", \"msTSManagingLS3\", \"msTSExpireDate4\", \"msTSLicenseVersion4\", \"msTSManagingLS4\", \"msTSLSProperty01\", \"msTSLSProperty02\", \"msDS-ResultantPSO\", \"msPKI-CredentialRoamingTokens\", \"msTSPrimaryDesktop\", \"msTSSecondaryDesktops\", \"msDS-PrimaryComputer\", \"msDS-SyncServerUrl\", \"msDS-AssignedAuthNPolicySilo\", \"msDS-AuthNPolicySiloMembersBL\", \"msDS-AssignedAuthNPolicy\", \"userSMIMECertificate\", \"uid\", \"mail\", \"roomNumber\", \"photo\", \"manager\", \"homePhone\", \"secretary\", \"mobile\", \"pager\", \"audio\", \"jpegPhoto\", \"carLicense\", \"departmentNumber\", \"preferredLanguage\", \"userPKCS12\", \"labeledURI\", \"msSFU30Name\", \"msSFU30NisDomain\"},\n\t},\n\t\"container\": &LDAPSchema{\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"mstop\"},\n\t\tRequired:    []string{\"cn\"},\n\t\tOptional:    []string{\"schemaVersion\", \"defaultClassStore\", \"msDS-ObjectReference\"},\n\t},\n\t\"computer\": &LDAPSchema{\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"user\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"cn\", \"networkAddress\", \"localPolicyFlags\", \"defaultLocalPolicyObject\", \"machineRole\", \"location\", \"netbootInitialization\", \"netbootGUID\", \"netbootMachineFilePath\", \"siteGUID\", \"operatingSystem\", \"operatingSystemVersion\", \"operatingSystemServicePack\", \"operatingSystemHotfix\", \"volumeCount\", \"physicalLocationObject\", \"dNSHostName\", \"policyReplicationFlags\", \"managedBy\", \"rIDSetReferences\", \"catalogs\", \"netbootSIFFile\", \"netbootMirrorDataFile\", \"msDS-AdditionalDnsHostName\", \"msDS-AdditionalSamAccountName\", \"msDS-ExecuteScriptPassword\", \"msDS-KrbTgtLink\", \"msDS-RevealedUsers\", \"msDS-NeverRevealGroup\", \"msDS-RevealOnDemandGroup\", \"msDS-RevealedList\", \"msDS-AuthenticatedAtDC\", \"msDS-isGC\", \"msDS-isRODC\", \"msDS-SiteName\", \"msDS-PromotionSettings\", \"msTPM-OwnerInformation\", \"msTSProperty01\", \"msTSProperty02\", \"msDS-IsUserCachableAtRodc\", \"msDS-HostServiceAccount\", \"msTSEndpointData\", \"msTSEndpointType\", \"msTSEndpointPlugin\", \"msTSPrimaryDesktopBL\", \"msTSSecondaryDesktopBL\", \"msTPM-TpmInformationForComputer\", \"msDS-GenerationId\", \"msImaging-ThumbprintHash\", \"msImaging-HashAlgorithm\", \"netbootDUID\", \"msSFU30Name\", \"msSFU30Aliases\", \"msSFU30NisDomain\", \"nisMapName\"},\n\t},\n\t// SCHEMA: nis.schema\n\t\"posixAccount\": &LDAPSchema{\n\t\tDescription: \"Abstraction of an account with POSIX attributes\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      false,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\", \"uid\", \"uidNumber\", \"gidNumber\", \"homeDirectory\"},\n\t\tOptional:    []string{\"userPassword\", \"loginShell\", \"gecos\", \"description\"},\n\t},\n\t\"shadowAccount\": &LDAPSchema{\n\t\tDescription: \"Additional attributes for shadow passwords\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"uid\"},\n\t\tOptional:    []string{\"userPassword\", \"shadowLastChange\", \"shadowMin\", \"shadowMax\", \"shadowWarning\", \"shadowInactive\", \"shadowExpire\", \"shadowFlag\", \"description\"},\n\t},\n\t\"posixGroup\": &LDAPSchema{\n\t\tDescription: \"Abstraction of a group of accounts\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      false,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\", \"gidNumber\"},\n\t\tOptional:    []string{\"userPassword\", \"memberUid\", \"description\"},\n\t},\n\t\"ipService\": &LDAPSchema{\n\t\tDescription: \"Abstraction an Internet Protocol service\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\", \"ipServicePort\", \"ipServiceProtocol\"},\n\t\tOptional:    []string{\"description\"},\n\t},\n\t\"ipProtocol\": &LDAPSchema{\n\t\tDescription: \"Abstraction of an IP protocol\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\", \"ipProtocolNumber\", \"description\"},\n\t\tOptional:    []string{\"description\"},\n\t},\n\t\"oncRpc\": &LDAPSchema{\n\t\tDescription: \"Abstraction of an ONC/RPC binding\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\", \"oncRpcNumber\", \"description\"},\n\t\tOptional:    []string{\"description\"},\n\t},\n\t\"ipHost\": &LDAPSchema{\n\t\tDescription: \"Abstraction of a host, an IP device\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\", \"ipHostNumber\"},\n\t\tOptional:    []string{\"l\", \"description\", \"manager\"},\n\t},\n\t\"ipNetwork\": &LDAPSchema{\n\t\tDescription: \"Abstraction of an IP network\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\", \"ipNetworkNumber\"},\n\t\tOptional:    []string{\"ipNetmaskNumber\", \"l\", \"description\", \"manager\"},\n\t},\n\t\"nisNetgroup\": &LDAPSchema{\n\t\tDescription: \"Abstraction of a netgroup\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\"},\n\t\tOptional:    []string{\"nisNetgroupTriple\", \"memberNisNetgroup\", \"description\"},\n\t},\n\t\"nisMap\": &LDAPSchema{\n\t\tDescription: \"A generic abstraction of a NIS map\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"nisMapName\"},\n\t\tOptional:    []string{\"description\"},\n\t},\n\t\"nisObject\": &LDAPSchema{\n\t\tDescription: \"An entry in a NIS map\",\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\", \"nisMapEntry\", \"nisMapName\"},\n\t\tOptional:    []string{\"description\"},\n\t},\n\t\"ieee802Device\": &LDAPSchema{\n\t\tDescription: \"A device with a MAC address\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"macAddress\"},\n\t},\n\t\"bootableDevice\": &LDAPSchema{\n\t\tDescription: \"A device with boot parameters\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"bootFile\", \"bootParameter\"},\n\t},\n\t// SCHEMA: openldap.schema\n\t\"OpenLDAPorg\": &LDAPSchema{\n\t\tDescription: \"OpenLDAP Organizational Object\",\n\t\tType:        \"UNSPECIFIED\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"organization\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"buildingName\", \"displayName\", \"labeledURI\"},\n\t},\n\t\"OpenLDAPou\": &LDAPSchema{\n\t\tDescription: \"OpenLDAP Organizational Unit Object\",\n\t\tType:        \"UNSPECIFIED\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"organizationalUnit\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"buildingName\", \"displayName\", \"labeledURI\", \"o\"},\n\t},\n\t\"OpenLDAPperson\": &LDAPSchema{\n\t\tDescription: \"OpenLDAP Person\",\n\t\tType:        \"UNSPECIFIED\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"pilotPerson\", \"inetOrgPerson\"},\n\t\tRequired:    []string{\"uid\", \"cn\"},\n\t\tOptional:    []string{\"givenName\", \"labeledURI\", \"o\"},\n\t},\n\t\"OpenLDAPdisplayableObject\": &LDAPSchema{\n\t\tDescription: \"OpenLDAP Displayable Object\",\n\t\tType:        \"AUXILIARY\",\n\t\tIsContainer: false,\n\t\tSilent:      true,\n\t\tInherit:     []string{},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"displayName\"},\n\t},\n\t// SCHEMA: extra\n\t\"nsContainer\": &LDAPSchema{ // https://access.redhat.com/documentation/en-US/Red_Hat_Directory_Server/8.1/html/Configuration_and_Command_Reference/config-object-classes.html\n\t\tDescription: \"Container Entry\",\n\t\tType:        \"UNSPECIFIED\",\n\t\tIsContainer: true,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{\"cn\"},\n\t\tOptional:    []string{},\n\t},\n\t\"aeZone\": &LDAPSchema{ // https://docs.ldap.com/specs/draft-howard-namedobject-01.txt\n\t\tType:        \"STRUCTURAL\",\n\t\tIsContainer: true,\n\t\tSilent:      true,\n\t\tInherit:     []string{\"top\"},\n\t\tRequired:    []string{},\n\t\tOptional:    []string{\"cn\"},\n\t},\n}\n\nvar LDAPAttribute map[string]*FormElement = map[string]*FormElement{\n\t//////////////////////////\n\t// SCHEMA: core.schema:\n\t\"objectClass\": &FormElement{\n\t\tDescription: \"Object classes of the entity - RFC2256\",\n\t\tOrder:       1,\n\t\tDatalist: func() []string {\n\t\t\tlist := make([]string, 0)\n\t\t\tfor key, _ := range Schema {\n\t\t\t\tif Schema[key].Silent == false {\n\t\t\t\t\tlist = append(list, key)\n\t\t\t\t}\n\t\t\t}\n\t\t\tsort.Strings(list)\n\t\t\treturn list\n\t\t}(),\n\t\tMultiValue: true,\n\t},\n\t\"aliasedObjectName\": &FormElement{\n\t\tDescription: \"Name of aliased object - RFC2256\",\n\t},\n\t\"aliasedEntryName\": &FormElement{\n\t\tDescription: \"Name of aliased object - RFC2256\",\n\t},\n\t\"knowledgeInformation\": &FormElement{\n\t\tDescription: \"Knowledge information - RFC2256\",\n\t},\n\t\"cn\": &FormElement{\n\t\tDescription: \"Common name(s) for which the entity is known by - RFC2256\",\n\t\tOrder:       1,\n\t},\n\t\"commonName\": &FormElement{\n\t\tDescription: \"Common name(s) for which the entity is known by - RFC2256\",\n\t\tOrder:       2,\n\t},\n\t\"sn\": &FormElement{\n\t\tDescription: \"Last (family) name(s) for which the entity is known by - RFC2256\",\n\t\tOrder:       4,\n\t},\n\t\"surname\": &FormElement{\n\t\tDescription: \"Last (family) name(s) for which the entity is known by - RFC2256\",\n\t\tOrder:       4,\n\t},\n\t\"serialNumber\": &FormElement{\n\t\tDescription: \"Serial number of the entity - RFC2256\",\n\t},\n\t\"c\": &FormElement{\n\t\tDescription: \"Two-letter ISO-3166 country code - RFC4519\",\n\t},\n\t\"countryName\": &FormElement{\n\t\tDescription: \"Two-letter ISO-3166 country code - RFC4519\",\n\t\tOrder:       15,\n\t},\n\t\"l\": &FormElement{\n\t\tDescription: \"Locality which this object resides in - RFC2256\",\n\t\tOrder:       15,\n\t},\n\t\"localityName\": &FormElement{\n\t\tDescription: \"Locality which this object resides in - RFC2256\",\n\t\tOrder:       15,\n\t},\n\t\"st\": &FormElement{\n\t\tDescription: \"State or province which this object resides in - RFC2256\",\n\t\tOrder:       15,\n\t},\n\t\"stateOrProvinceName\": &FormElement{\n\t\tDescription: \"State or province which this object resides in - RFC2256\",\n\t\tOrder:       15,\n\t},\n\t\"street\": &FormElement{\n\t\tDescription: \"Street address of this object - RFC2256\",\n\t\tOrder:       15,\n\t},\n\t\"streetAddress\": &FormElement{\n\t\tDescription: \"Street address of this object - RFC2256\",\n\t\tOrder:       15,\n\t},\n\t\"o\": &FormElement{\n\t\tDescription: \"Organization this object belongs to - RFC2256\",\n\t\tOrder:       10,\n\t},\n\t\"organizationName\": &FormElement{\n\t\tDescription: \"Organization this object belongs to - RFC2256\",\n\t\tOrder:       10,\n\t},\n\t\"ou\": &FormElement{\n\t\tDescription: \"Organizational unit this object belongs to - RFC2256\",\n\t\tOrder:       10,\n\t},\n\t\"organizationalUnitName\": &FormElement{\n\t\tDescription: \"Organizational unit this object belongs to - RFC2256\",\n\t\tOrder:       10,\n\t},\n\t\"title\": &FormElement{\n\t\tDescription: \"Title associated with the entity - RFC2256\",\n\t\tOrder:       20,\n\t},\n\t\"description\": &FormElement{\n\t\tDescription: \"Descriptive information - RFC2256\",\n\t\tOrder:       5,\n\t},\n\t\"searchGuide\": &FormElement{\n\t\tDescription: \"Search guide, deprecated by enhancedSearchGuide - RFC2256\",\n\t},\n\t\"businessCategory\": &FormElement{\n\t\tDescription: \"Business category - RFC2256\",\n\t},\n\t\"postalAddress\": &FormElement{\n\t\tDescription: \"Postal address - RFC2256\",\n\t\tOrder:       15,\n\t},\n\t\"postalCode\": &FormElement{\n\t\tDescription: \"Postal code - RFC2256\",\n\t\tOrder:       15,\n\t},\n\t\"postOfficeBox\": &FormElement{\n\t\tDescription: \"Post Office Box - RFC2256\",\n\t\tOrder:       15,\n\t},\n\t\"physicalDeliveryOfficeName\": &FormElement{\n\t\tDescription: \"Physical Delivery Office Name - RFC2256\",\n\t\tOrder:       20,\n\t},\n\t\"telephoneNumber\": &FormElement{\n\t\tDescription: \"Telephone Number - RFC2256\",\n\t\tOrder:       8,\n\t},\n\t\"telexNumber\": &FormElement{\n\t\tDescription: \"Telex Number - RFC2256\",\n\t\tOrder:       25,\n\t},\n\t\"teletexTerminalIdentifier\": &FormElement{\n\t\tDescription: \"Teletex Terminal Identifier - RFC2256\",\n\t\tOrder:       25,\n\t},\n\t\"facsimileTelephoneNumber\": &FormElement{\n\t\tDescription: \"Facsimile (Fax) Telephone Number - RFC2256\",\n\t\tOrder:       25,\n\t},\n\t\"fax\": &FormElement{\n\t\tDescription: \"Facsimile (Fax) Telephone Number - RFC2256\",\n\t\tOrder:       25,\n\t},\n\t\"x121Address\": &FormElement{\n\t\tDescription: \"X.121 Address - RFC2256\",\n\t},\n\t\"internationaliSDNNumber\": &FormElement{\n\t\tDescription: \"International ISDN number - RFC2256\",\n\t},\n\t\"registeredAddress\": &FormElement{\n\t\tDescription: \"Registered postal address - RFC2256\",\n\t\tOrder:       15,\n\t},\n\t\"destinationIndicator\": &FormElement{\n\t\tDescription: \"Destination indicator - RFC2256\",\n\t},\n\t\"preferredDeliveryMethod\": &FormElement{\n\t\tDescription: \"Preferred delivery method - RFC2256\",\n\t},\n\t\"presentationAddress\": &FormElement{\n\t\tDescription: \"Presentation address - RFC2256\",\n\t},\n\t\"supportedApplicationContext\": &FormElement{\n\t\tDescription: \"Supported application context - RFC2256\",\n\t},\n\t\"member\": &FormElement{\n\t\tDescription: \"Member of a group - RFC2256\",\n\t},\n\t\"owner\": &FormElement{\n\t\tDescription: \"Owner (of the object) - RFC2256\",\n\t},\n\t\"roleOccupant\": &FormElement{\n\t\tDescription: \"Occupant of role - RFC2256\",\n\t},\n\t\"seeAlso\": &FormElement{\n\t\tDescription: \"DN of related object - RFC2256\",\n\t\tOrder:       20,\n\t},\n\t\"userPassword\": &FormElement{\n\t\tDescription: \"Password of user - RFC2256/2307\",\n\t\tOrder:       6,\n\t},\n\t\"userCertificate\": &FormElement{\n\t\tDescription: \"X.509 user certificate, use ;binary - RFC2256\",\n\t},\n\t\"cACertificate\": &FormElement{\n\t\tDescription: \"X.509 CA certificate, use ;binary - RFC2256\",\n\t},\n\t\"authorityRevocationList\": &FormElement{\n\t\tDescription: \"X.509 authority revocation list, use ;binary - RFC2256\",\n\t},\n\t\"certificateRevocationList\": &FormElement{\n\t\tDescription: \"X.509 certificate revocation list, use ;binary - RFC2256\",\n\t},\n\t\"crossCertificatePair\": &FormElement{\n\t\tDescription: \"X.509 cross certificate pair, use ;binary - RFC2256\",\n\t},\n\t\"name\": &FormElement{\n\t\tOrder: 4,\n\t},\n\t\"givenName\": &FormElement{\n\t\tDescription: \"First name(s) for which the entity is known by - RFC2256\",\n\t\tOrder:       4,\n\t},\n\t\"gn\": &FormElement{\n\t\tDescription: \"First name(s) for which the entity is known by - RFC2256\",\n\t\tOrder:       4,\n\t},\n\t\"initials\": &FormElement{\n\t\tDescription: \"Initials of some or all of names, but not the surname(s). - RFC2256\",\n\t\tOrder:       20,\n\t},\n\t\"generationQualifier\": &FormElement{\n\t\tDescription: \"Name qualifier indicating a generation - RFC2256\",\n\t},\n\t\"x500UniqueIdentifier\": &FormElement{\n\t\tDescription: \"X.500 unique identifier - RFC2256\",\n\t},\n\t\"dnQualifier\": &FormElement{\n\t\tDescription: \"DN qualifier - RFC2256\",\n\t},\n\t\"enhancedSearchGuide\": &FormElement{\n\t\tDescription: \"Enhanced search guide - RFC2256\",\n\t},\n\t\"protocolInformation\": &FormElement{\n\t\tDescription: \"Protocol information - RFC2256\",\n\t},\n\t\"distinguishedName\": &FormElement{},\n\t\"uniqueMember\": &FormElement{\n\t\tDescription: \"Unique member of a group - RFC2256\",\n\t\tOrder:       9,\n\t},\n\t\"houseIdentifier\": &FormElement{\n\t\tDescription: \"House identifier - RFC2256\",\n\t},\n\t\"supportedAlgorithms\": &FormElement{\n\t\tDescription: \"Supported algorithms - RFC2256\",\n\t},\n\t\"deltaRevocationList\": &FormElement{\n\t\tDescription: \"Delta revocation list; use ;binary - RFC2256\",\n\t},\n\t\"dmdName\": &FormElement{\n\t\tDescription: \"Name of DMD - RFC2256\",\n\t},\n\t\"pseudonym\": &FormElement{\n\t\tDescription: \"Pseudonym for the object - X.520(4th)\",\n\t},\n\t\"labeledURI\": &FormElement{\n\t\tDescription: \"Uniform Resource Identifier with optional label - RFC2079\",\n\t},\n\t\"uid\": &FormElement{\n\t\tDescription: \"User identifier - RFC1274\",\n\t\tOrder:       7,\n\t},\n\t\"userid\": &FormElement{\n\t\tDescription: \"User identifier - RFC1274\",\n\t},\n\t\"mail\": &FormElement{\n\t\tDescription: \"RFC822 Mailbox - RFC1274\",\n\t\tOrder:       7,\n\t},\n\t\"rfc822Mailbox\": &FormElement{\n\t\tDescription: \"RFC822 Mailbox - RFC1274\",\n\t},\n\t\"dc\": &FormElement{\n\t\tDescription: \"Domain component - RFC1274/2247\",\n\t\tOrder:       10,\n\t},\n\t\"domainComponent\": &FormElement{\n\t\tDescription: \"Domain component - RFC1274/2247\",\n\t\tOrder:       10,\n\t},\n\t\"associatedDomain\": &FormElement{\n\t\tDescription: \"Domain associated with object - RFC1274\",\n\t},\n\t\"email\": &FormElement{\n\t\tDescription: \"Legacy attribute for email addresses in DNs - RFC3280\",\n\t},\n\t\"emailAddress\": &FormElement{\n\t\tDescription: \"Legacy attribute for email addresses in DNs - RFC3280\",\n\t},\n\t\"pkcs9email\": &FormElement{\n\t\tDescription: \"Legacy attribute for email addresses in DNs - RFC3280\",\n\t},\n\t//////////////////////////\n\t// SCHEMA: inetorgperson.schema\n\t\"carLicense\": &FormElement{\n\t\tDescription: \"Vehicle license or registration plate - RFC2798\",\n\t\tOrder:       20,\n\t},\n\t\"departmentNumber\": &FormElement{\n\t\tDescription: \"Identifies a department within an organization - RFC2798\",\n\t\tOrder:       20,\n\t},\n\t\"displayName\": &FormElement{\n\t\tDescription: \"Preferred name to be used when displaying entries - RFC2798\",\n\t\tOrder:       5,\n\t},\n\t\"employeeNumber\": &FormElement{\n\t\tDescription: \"Numerically identifies an employee within an organization - RFC2798\",\n\t\tOrder:       20,\n\t},\n\t\"employeeType\": &FormElement{\n\t\tDescription: \"Type of employment for a person - RFC2798\",\n\t\tOrder:       20,\n\t},\n\t\"jpegPhoto\": &FormElement{\n\t\tDescription: \"A JPEG image - RFC2798\",\n\t\tOrder:       17,\n\t},\n\t\"preferredLanguage\": &FormElement{\n\t\tDescription: \"Preferred written or spoken language for a person - RFC2798\",\n\t\tOrder:       19,\n\t},\n\t\"userSMIMECertificate\": &FormElement{\n\t\tDescription: \"PKCS7 SignedData used to support S/MIME - RFC2798\",\n\t},\n\t\"userPKCS12\": &FormElement{\n\t\tDescription: \"Personal identity information, a PKCS 12 PFX - RFC2798\",\n\t},\n\t//////////////////////////\n\t// SCHEMA: collective.schema\n\t\"c-l\": &FormElement{\n\t\tDescription: \"Locality name for the collection of entries\",\n\t},\n\t\"c-st\": &FormElement{\n\t\tDescription: \"State or province name for the collection of entries\",\n\t},\n\t\"c-street\": &FormElement{\n\t\tDescription: \"Street address for the collection of entries\",\n\t},\n\t\"c-o\": &FormElement{\n\t\tDescription: \"Organization name for the collection of entries\",\n\t\tOrder:       10,\n\t},\n\t\"c-ou\": &FormElement{\n\t\tDescription: \"Organizational unit name for the collection of entries\",\n\t\tOrder:       10,\n\t},\n\t\"c-PostalAddress\": &FormElement{\n\t\tDescription: \"Postal address for the collection of entries\",\n\t\tOrder:       15,\n\t},\n\t\"c-PostalCode\": &FormElement{\n\t\tOrder:       15,\n\t\tDescription: \"Postal code for the collection of entries\",\n\t},\n\t\"c-PostOfficeBox\": &FormElement{\n\t\tOrder:       15,\n\t\tDescription: \"Post office box for the collection of entries\",\n\t},\n\t\"c-PhysicalDeliveryOfficeName\": &FormElement{\n\t\tOrder:       20,\n\t\tDescription: \"Physical dlivery office name for a collection of entries.\",\n\t},\n\t\"c-TelephoneNumber\": &FormElement{\n\t\tOrder:       8,\n\t\tDescription: \"telephone number for the collection of entries\",\n\t},\n\t\"c-TelexNumber\": &FormElement{\n\t\tOrder:       25,\n\t\tDescription: \"Telex number for the collection of entries\",\n\t},\n\t\"c-FacsimileTelephoneNumber\": &FormElement{\n\t\tDescription: \"Facsimile telephone number for a collection of entries.\",\n\t},\n\t\"c-InternationalISDNNumber\": &FormElement{\n\t\tDescription: \"International ISDN number for the collection of entries\",\n\t},\n\t//////////////////////////\n\t// SCHEMA: corba.schema\n\t\"corbaIor\": &FormElement{\n\t\tDescription: \"Stringified interoperable object reference of a CORBA object\",\n\t},\n\t\"corbaRepositoryId\": &FormElement{\n\t\tDescription: \"Repository ids of interfaces implemented by a CORBA object\",\n\t},\n\t//////////////////////////\n\t// SCHEMA: cosine.schema\n\t\"textEncodedORAddress\": &FormElement{\n\t\tDescription: \"Text encoding of an X.400 O/R address, as specified in RFC 987\",\n\t},\n\t\"info\": &FormElement{\n\t\tDescription: \"General information - RFC1274\",\n\t},\n\t\"drink\": &FormElement{\n\t\tDescription: \"Favorite drink - RFC1274\",\n\t},\n\t\"favouriteDrink\": &FormElement{\n\t\tDescription: \"Favorite drink - RFC1274\",\n\t},\n\t\"roomNumber\": &FormElement{\n\t\tDescription: \"Room number - RFC1274\",\n\t\tOrder:       20,\n\t},\n\t\"photo\": &FormElement{\n\t\tDescription: \"Photo (G3 fax) - RFC1274\",\n\t\tOrder:       17,\n\t},\n\t\"userClass\": &FormElement{\n\t\tDescription: \"Category of user - RFC1274\",\n\t},\n\t\"host\": &FormElement{\n\t\tDescription: \"Host computer - RFC1274\",\n\t},\n\t\"manager\": &FormElement{\n\t\tDescription: \"DN of manager - RFC1274\",\n\t\tOrder:       20,\n\t},\n\t\"documentIdentifier\": &FormElement{\n\t\tDescription: \"Unique identifier of document - RFC1274\",\n\t},\n\t\"documentTitle\": &FormElement{\n\t\tDescription: \"Title of document - RFC1274\",\n\t},\n\t\"documentVersion\": &FormElement{\n\t\tDescription: \"Version of document - RFC1274\",\n\t},\n\t\"documentAuthor\": &FormElement{\n\t\tDescription: \"DN of author of document - RFC1274\",\n\t},\n\t\"documentLocation\": &FormElement{\n\t\tDescription: \"Location of document original - RFC1274\",\n\t},\n\t\"homePhone\": &FormElement{\n\t\tDescription: \"Home telephone number - RFC1274\",\n\t\tOrder:       8,\n\t},\n\t\"homeTelephoneNumber\": &FormElement{\n\t\tDescription: \"Home telephone number - RFC1274\",\n\t},\n\t\"secretary\": &FormElement{\n\t\tDescription: \"DN of secretary - RFC1274\",\n\t},\n\t\"otherMailbox\": &FormElement{},\n\t\"lastModifiedTime\": &FormElement{\n\t\tDescription: \"Time of last modify, replaced by modifyTimestamp - RFC1274\",\n\t},\n\t\"lastModifiedBy\": &FormElement{\n\t\tDescription: \"Last modifier, replaced by modifiersName - RFC1274\",\n\t},\n\t\"aRecord\": &FormElement{\n\t\tDescription: \"Type A (Address) DNS resource record\",\n\t},\n\t\"mDRecord\": &FormElement{},\n\t\"mXRecord\": &FormElement{\n\t\tDescription: \"Mail Exchange DNS resource record\",\n\t},\n\t\"nSRecord\": &FormElement{\n\t\tDescription: \"Name Server DNS resource record\",\n\t},\n\t\"sOARecord\": &FormElement{\n\t\tDescription: \"Start of Authority DNS resource record\",\n\t},\n\t\"cNAMERecord\": &FormElement{\n\t\tDescription: \"CNAME (Canonical Name) DNS resource record\",\n\t},\n\t\"associatedName\": &FormElement{\n\t\tDescription: \"DN of entry associated with domain - RFC1274\",\n\t},\n\t\"homePostalAddress\": &FormElement{\n\t\tDescription: \"Home postal address - RFC1274\",\n\t\tOrder:       15,\n\t},\n\t\"personalTitle\": &FormElement{\n\t\tDescription: \"Personal title - RFC1274\",\n\t},\n\t\"mobile\": &FormElement{\n\t\tDescription: \"Mobile telephone number - RFC1274\",\n\t\tOrder:       8,\n\t},\n\t\"mobileTelephoneNumber\": &FormElement{\n\t\tDescription: \"Mobile telephone number - RFC1274\",\n\t\tOrder:       8,\n\t},\n\t\"pager\": &FormElement{\n\t\tDescription: \"Pager telephone number - RFC1274\",\n\t\tOrder:       20,\n\t},\n\t\"pagerTelephoneNumber\": &FormElement{\n\t\tDescription: \"Pager telephone number - RFC1274\",\n\t\tOrder:       20,\n\t},\n\t\"co\": &FormElement{\n\t\tDescription: \"Friendly country name - RFC1274\",\n\t},\n\t\"friendlyCountryName\": &FormElement{\n\t\tDescription: \"Friendly country name - RFC1274\",\n\t},\n\t\"uniqueIdentifier\": &FormElement{\n\t\tDescription: \"Unique identifer - RFC1274\",\n\t},\n\t\"organizationalStatus\": &FormElement{\n\t\tDescription: \"Organizational status - RFC1274\",\n\t},\n\t\"janetMailbox\": &FormElement{\n\t\tDescription: \"Janet mailbox - RFC1274\",\n\t},\n\t\"mailPreferenceOption\": &FormElement{\n\t\tDescription: \"Mail preference option - RFC1274\",\n\t},\n\t\"buildingName\": &FormElement{\n\t\tDescription: \"Name of building - RFC1274\",\n\t},\n\t\"dSAQuality\": &FormElement{\n\t\tDescription: \"DSA Quality - RFC1274\",\n\t},\n\t\"singleLevelQuality\": &FormElement{\n\t\tDescription: \"Single Level Quality - RFC1274\",\n\t},\n\t\"subtreeMinimumQuality\": &FormElement{\n\t\tDescription: \"Subtree Minimum Quality - RFC1274\",\n\t},\n\t\"subtreeMaximumQuality\": &FormElement{\n\t\tDescription: \"Subtree Maximum Quality - RFC1274\",\n\t},\n\t\"personalSignature\": &FormElement{\n\t\tDescription: \"Personal Signature (G3 fax) - RFC1274\",\n\t},\n\t\"dITRedirect\": &FormElement{\n\t\tDescription: \"DIT Redirect - RFC1274\",\n\t},\n\t\"audio\": &FormElement{\n\t\tDescription: \"Audio (u-law) - RFC1274\",\n\t},\n\t\"documentPublisher\": &FormElement{\n\t\tDescription: \"Publisher of document - RFC1274\",\n\t},\n\t//////////////////////////\n\t// SCHEMA: duaconf.schema\n\t\"defaultServerList\": &FormElement{\n\t\tDescription: \"Default LDAP server host address used by a DUA\",\n\t},\n\t\"defaultSearchBase\": &FormElement{\n\t\tDescription: \"Default LDAP base DN used by a DUA\",\n\t},\n\t\"preferredServerList\": &FormElement{\n\t\tDescription: \"Preferred LDAP server host addresses to be used by a DUA\",\n\t},\n\t\"searchTimeLimit\": &FormElement{\n\t\tDescription: \"Maximum time in seconds a DUA should allow for a search to complete\",\n\t},\n\t\"bindTimeLimit\": &FormElement{\n\t\tDescription: \"Maximum time in seconds a DUA should allow for the bind operation to complete\",\n\t},\n\t\"followReferrals\": &FormElement{\n\t\tDescription: \"Tells DUA if it should follow referrals returned by a DSA search result\",\n\t},\n\t\"dereferenceAliases\": &FormElement{\n\t\tDescription: \"Tells DUA if it should dereference aliases\",\n\t},\n\t\"authenticationMethod\": &FormElement{\n\t\tDescription: \"A keystring which identifies the type of authentication method used to contact the DSA\",\n\t},\n\t\"profileTTL\": &FormElement{\n\t\tDescription: \"Time to live, in seconds, before a client DUA should re-read this configuration profile\",\n\t},\n\t\"serviceSearchDescriptor\": &FormElement{\n\t\tDescription: \"LDAP search descriptor list used by a DUA\",\n\t},\n\t\"attributeMap\": &FormElement{\n\t\tDescription: \"Attribute mappings used by a DUA\",\n\t},\n\t\"credentialLevel\": &FormElement{\n\t\tDescription: \"Identifies type of credentials a DUA should use when binding to the LDAP server\",\n\t},\n\t\"objectclassMap\": &FormElement{\n\t\tDescription: \"Objectclass mappings used by a DUA\",\n\t},\n\t\"defaultSearchScope\": &FormElement{\n\t\tDescription: \"Default search scope used by a DUA\",\n\t},\n\t\"serviceCredentialLevel\": &FormElement{\n\t\tDescription: \"Identifies type of credentials a DUA should use when binding to the LDAP server for a specific service\",\n\t},\n\t\"serviceAuthenticationMethod\": &FormElement{\n\t\tDescription: \"Authentication method used by a service of the DUA\",\n\t},\n\t//////////////////////////\n\t// SCHEMA: dyngroup.schema\n\t\"memberURL\": &FormElement{\n\t\tDescription: \"Identifies an URL associated with each member of a group. Any type of labeled URL can be used.\",\n\t},\n\t\"dgIdentity\": &FormElement{\n\t\tDescription: \"Identity to use when processing the memberURL\",\n\t},\n\t\"dgAuthz\": &FormElement{\n\t\tDescription: \"Optional authorization rules that determine who is allowed to assume the dgIdentity\",\n\t},\n\t//////////////////////////\n\t// SCHEMA: java.schema\n\t\"javaClassName\": &FormElement{\n\t\tDescription: \"Fully qualified name of distinguished Java class or interface\",\n\t},\n\t\"javaCodebase\": &FormElement{\n\t\tDescription: \"URL(s) specifying the location of class definition\",\n\t},\n\t\"javaClassNames\": &FormElement{\n\t\tDescription: \"Fully qualified Java class or interface name\",\n\t},\n\t\"javaSerializedData\": &FormElement{\n\t\tDescription: \"Serialized form of a Java object\",\n\t},\n\t\"javaFactory\": &FormElement{\n\t\tDescription: \"Fully qualified Java class name of a JNDI object factory\",\n\t},\n\t\"javaReferenceAddress\": &FormElement{\n\t\tDescription: \"Addresses associated with a JNDI Reference\",\n\t},\n\t\"javaDoc\": &FormElement{\n\t\tDescription: \"The Java documentation for the class\",\n\t},\n\t//////////////////////////\n\t// SCHEMA: misc.schema\n\t\"mailLocalAddress\": &FormElement{\n\t\tDescription: \"RFC822 email address of this recipient\",\n\t},\n\t\"mailHost\": &FormElement{\n\t\tDescription: \"FQDN of the SMTP/MTA of this recipient\",\n\t},\n\t\"mailRoutingAddress\": &FormElement{\n\t\tDescription: \"RFC822 routing address of this recipient\",\n\t},\n\t\"rfc822MailMember\": &FormElement{\n\t\tDescription: \"Rfc822 mail address of group member(s)\",\n\t},\n\t//////////////////////////\n\t// SCHEMA: msuser.schema\n\t\"ownerBL\":                                               &FormElement{},\n\t\"msCOM-PartitionSetLink\":                                &FormElement{},\n\t\"msCOM-UserLink\":                                        &FormElement{},\n\t\"msDS-Approx-Immed-Subordinates\":                        &FormElement{},\n\t\"msDS-NCReplCursors\":                                    &FormElement{},\n\t\"msDS-NCReplInboundNeighbors\":                           &FormElement{},\n\t\"msDS-NCReplOutboundNeighbors\":                          &FormElement{},\n\t\"msDS-ReplAttributeMetaData\":                            &FormElement{},\n\t\"msDS-ReplValueMetaData\":                                &FormElement{},\n\t\"msDS-NonMembers\":                                       &FormElement{},\n\t\"msDS-NonMembersBL\":                                     &FormElement{},\n\t\"msDS-MembersForAzRole\":                                 &FormElement{},\n\t\"msDS-MembersForAzRoleBL\":                               &FormElement{},\n\t\"msDS-OperationsForAzTask\":                              &FormElement{},\n\t\"msDS-OperationsForAzTaskBL\":                            &FormElement{},\n\t\"msDS-TasksForAzTask\":                                   &FormElement{},\n\t\"msDS-TasksForAzTaskBL\":                                 &FormElement{},\n\t\"msDS-OperationsForAzRole\":                              &FormElement{},\n\t\"msDS-OperationsForAzRoleBL\":                            &FormElement{},\n\t\"msDS-TasksForAzRole\":                                   &FormElement{},\n\t\"msDS-TasksForAzRoleBL\":                                 &FormElement{},\n\t\"msDs-masteredBy\":                                       &FormElement{},\n\t\"msDS-ObjectReference\":                                  &FormElement{},\n\t\"msDS-ObjectReferenceBL\":                                &FormElement{},\n\t\"msDS-PrincipalName\":                                    &FormElement{},\n\t\"msDS-RevealedDSAs\":                                     &FormElement{},\n\t\"msDS-KrbTgtLinkBl\":                                     &FormElement{},\n\t\"msDS-IsFullReplicaFor\":                                 &FormElement{},\n\t\"msDS-IsDomainFor\":                                      &FormElement{},\n\t\"msDS-IsPartialReplicaFor\":                              &FormElement{},\n\t\"msDS-AuthenticatedToAccountlist\":                       &FormElement{},\n\t\"msDS-AuthenticatedAtDC\":                                &FormElement{},\n\t\"msDS-RevealedListBL\":                                   &FormElement{},\n\t\"msDS-NC-RO-Replica-Locations-BL\":                       &FormElement{},\n\t\"msDS-PSOApplied\":                                       &FormElement{},\n\t\"msDS-NcType\":                                           &FormElement{},\n\t\"msDS-OIDToGroupLinkBl\":                                 &FormElement{},\n\t\"isRecycled\":                                            &FormElement{},\n\t\"msDS-LocalEffectiveDeletionTime\":                       &FormElement{},\n\t\"msDS-LocalEffectiveRecycleTime\":                        &FormElement{},\n\t\"msDS-LastKnownRDN\":                                     &FormElement{},\n\t\"msDS-EnabledFeatureBL\":                                 &FormElement{},\n\t\"msDS-MembersOfResourcePropertyListBL\":                  &FormElement{},\n\t\"msDS-ValueTypeReferenceBL\":                             &FormElement{},\n\t\"msDS-TDOIngressBL\":                                     &FormElement{},\n\t\"msDS-TDOEgressBL\":                                      &FormElement{},\n\t\"msDS-parentdistname\":                                   &FormElement{},\n\t\"msDS-ReplValueMetaDataExt\":                             &FormElement{},\n\t\"msds-memberOfTransitive\":                               &FormElement{},\n\t\"msds-memberTransitive\":                                 &FormElement{},\n\t\"msSFU30PosixMemberOf\":                                  &FormElement{},\n\t\"msDFSR-MemberReferenceBL\":                              &FormElement{},\n\t\"msDFSR-ComputerReferenceBL\":                            &FormElement{},\n\t\"msDS-AzLDAPQuery\":                                      &FormElement{},\n\t\"msDS-AzBizRuleLanguage\":                                &FormElement{},\n\t\"msDS-AzLastImportedBizRulePath\":                        &FormElement{},\n\t\"msDS-AzApplicationData\":                                &FormElement{},\n\t\"msDS-AzObjectGuid\":                                     &FormElement{},\n\t\"msDS-AzGenericData\":                                    &FormElement{},\n\t\"msDS-PrimaryComputer\":                                  &FormElement{},\n\t\"msSFU30Name\":                                           &FormElement{},\n\t\"msSFU30NisDomain\":                                      &FormElement{},\n\t\"msSFU30PosixMember\":                                    &FormElement{},\n\t\"msCOM-UserPartitionSetLink\":                            &FormElement{},\n\t\"msDS-Cached-Membership\":                                &FormElement{},\n\t\"msDS-Cached-Membership-Time-Stamp\":                     &FormElement{},\n\t\"msDS-Site-Affinity\":                                    &FormElement{},\n\t\"msDS-User-Account-Control-Computed\":                    &FormElement{},\n\t\"lastLogonTimestamp\":                                    &FormElement{},\n\t\"msIIS-FTPRoot\":                                         &FormElement{},\n\t\"msIIS-FTPDir\":                                          &FormElement{},\n\t\"msDRM-IdentityCertificate\":                             &FormElement{},\n\t\"msDS-SourceObjectDN\":                                   &FormElement{},\n\t\"msPKIRoamingTimeStamp\":                                 &FormElement{},\n\t\"msPKIDPAPIMasterKeys\":                                  &FormElement{},\n\t\"msPKIAccountCredentials\":                               &FormElement{},\n\t\"msRADIUS-FramedInterfaceId\":                            &FormElement{},\n\t\"msRADIUS-SavedFramedInterfaceId\":                       &FormElement{},\n\t\"msRADIUS-FramedIpv6Prefix\":                             &FormElement{},\n\t\"msRADIUS-SavedFramedIpv6Prefix\":                        &FormElement{},\n\t\"msRADIUS-FramedIpv6Route\":                              &FormElement{},\n\t\"msRADIUS-SavedFramedIpv6Route\":                         &FormElement{},\n\t\"msDS-SecondaryKrbTgtNumber\":                            &FormElement{},\n\t\"msDS-SupportedEncryptionTypes\":                         &FormElement{},\n\t\"msDS-LastSuccessfulInteractiveLogonTime\":               &FormElement{},\n\t\"msDS-LastFailedInteractiveLogonTime\":                   &FormElement{},\n\t\"msDS-FailedInteractiveLogonCount\":                      &FormElement{},\n\t\"msDS-FailedInteractiveLogonCountAtLastSuccessfulLogon\": &FormElement{},\n\t\"msTSProfilePath\":                                       &FormElement{},\n\t\"msTSHomeDirectory\":                                     &FormElement{},\n\t\"msTSHomeDrive\":                                         &FormElement{},\n\t\"msTSAllowLogon\":                                        &FormElement{},\n\t\"msTSRemoteControl\":                                     &FormElement{},\n\t\"msTSMaxDisconnectionTime\":                              &FormElement{},\n\t\"msTSMaxConnectionTime\":                                 &FormElement{},\n\t\"msTSMaxIdleTime\":                                       &FormElement{},\n\t\"msTSReconnectionAction\":                                &FormElement{},\n\t\"msTSBrokenConnectionAction\":                            &FormElement{},\n\t\"msTSConnectClientDrives\":                               &FormElement{},\n\t\"msTSConnectPrinterDrives\":                              &FormElement{},\n\t\"msTSDefaultToMainPrinter\":                              &FormElement{},\n\t\"msTSWorkDirectory\":                                     &FormElement{},\n\t\"msTSInitialProgram\":                                    &FormElement{},\n\t\"msTSProperty01\":                                        &FormElement{},\n\t\"msTSProperty02\":                                        &FormElement{},\n\t\"msTSExpireDate\":                                        &FormElement{},\n\t\"msTSLicenseVersion\":                                    &FormElement{},\n\t\"msTSManagingLS\":                                        &FormElement{},\n\t\"msDS-UserPasswordExpiryTimeComputed\":                   &FormElement{},\n\t\"msTSManagingLS4\":                                       &FormElement{},\n\t\"msTSManagingLS3\":                                       &FormElement{},\n\t\"msTSManagingLS2\":                                       &FormElement{},\n\t\"msTSExpireDate4\":                                       &FormElement{},\n\t\"msTSExpireDate3\":                                       &FormElement{},\n\t\"msTSExpireDate2\":                                       &FormElement{},\n\t\"msTSLicenseVersion3\":                                   &FormElement{},\n\t\"msTSLicenseVersion2\":                                   &FormElement{},\n\t\"msTSLicenseVersion4\":                                   &FormElement{},\n\t\"msTSLSProperty01\":                                      &FormElement{},\n\t\"msTSLSProperty02\":                                      &FormElement{},\n\t\"msDS-ResultantPSO\":                                     &FormElement{},\n\t\"msPKI-CredentialRoamingTokens\":                         &FormElement{},\n\t\"msTSPrimaryDesktop\":                                    &FormElement{},\n\t\"msTSSecondaryDesktops\":                                 &FormElement{},\n\t\"msDS-SyncServerUrl\":                                    &FormElement{},\n\t\"msDS-AssignedAuthNPolicySilo\":                          &FormElement{},\n\t\"msDS-AuthNPolicySiloMembersBL\":                         &FormElement{},\n\t\"msDS-AssignedAuthNPolicy\":                              &FormElement{},\n\t\"msDS-Behavior-Version\":                                 &FormElement{},\n\t\"msDS-PerUserTrustQuota\":                                &FormElement{},\n\t\"msDS-AllUsersTrustQuota\":                               &FormElement{},\n\t\"msDS-PerUserTrustTombstonesQuota\":                      &FormElement{},\n\t\"msDS-AdditionalDnsHostName\":                            &FormElement{},\n\t\"msDS-AdditionalSamAccountName\":                         &FormElement{},\n\t\"msDS-ExecuteScriptPassword\":                            &FormElement{},\n\t\"msDS-KrbTgtLink\":                                       &FormElement{},\n\t\"msDS-RevealedUsers\":                                    &FormElement{},\n\t\"msDS-NeverRevealGroup\":                                 &FormElement{},\n\t\"msDS-RevealOnDemandGroup\":                              &FormElement{},\n\t\"msDS-RevealedList\":                                     &FormElement{},\n\t\"msDS-isGC\":                                             &FormElement{},\n\t\"msDS-isRODC\":                                           &FormElement{},\n\t\"msDS-SiteName\":                                         &FormElement{},\n\t\"msDS-PromotionSettings\":                                &FormElement{},\n\t\"msTPM-OwnerInformation\":                                &FormElement{},\n\t\"msDS-IsUserCachableAtRodc\":                             &FormElement{},\n\t\"msDS-HostServiceAccount\":                               &FormElement{},\n\t\"msTSEndpointData\":                                      &FormElement{},\n\t\"msTSEndpointType\":                                      &FormElement{},\n\t\"msTSEndpointPlugin\":                                    &FormElement{},\n\t\"msTSPrimaryDesktopBL\":                                  &FormElement{},\n\t\"msTSSecondaryDesktopBL\":                                &FormElement{},\n\t\"msTPM-TpmInformationForComputer\":                       &FormElement{},\n\t\"msDS-GenerationId\":                                     &FormElement{},\n\t\"msImaging-ThumbprintHash\":                              &FormElement{},\n\t\"msImaging-HashAlgorithm\":                               &FormElement{},\n\t\"netbootDUID\":                                           &FormElement{},\n\t\"msSFU30Aliases\":                                        &FormElement{},\n\t\"netbootNewMachineOU\":                                   &FormElement{},\n\t\"builtinCreationTime\":                                   &FormElement{},\n\t\"pKIEnrollmentAccess\":                                   &FormElement{},\n\t\"pKIExtendedKeyUsage\":                                   &FormElement{},\n\t\"msNPCalledStationID\":                                   &FormElement{},\n\t\"initialAuthIncoming\":                                   &FormElement{},\n\t\"objectClassCategory\":                                   &FormElement{},\n\t\"generatedConnection\":                                   &FormElement{},\n\t\"allowedChildClasses\":                                   &FormElement{},\n\t\"machineArchitecture\":                                   &FormElement{},\n\t\"aCSMaxPeakBandwidth\":                                   &FormElement{},\n\t\"marshalledInterface\":                                   &FormElement{},\n\t\"rIDManagerReference\":                                   &FormElement{},\n\t\"aCSEnableACSService\":                                   &FormElement{},\n\t\"mSMQRoutingService\":                                    &FormElement{},\n\t\"mS-SQL-AllowQueuedUpdatingSubscription\":                &FormElement{},\n\t\"primaryTelexNumber\":                                    &FormElement{},\n\t\"userAccountControl\":                                    &FormElement{},\n\t\"shellPropertyPages\":                                    &FormElement{},\n\t\"replUpToDateVector\":                                    &FormElement{},\n\t\"fRSDirectoryFilter\":                                    &FormElement{},\n\t\"printSeparatorFile\":                                    &FormElement{},\n\t\"pKIMaxIssuingDepth\":                                    &FormElement{},\n\t\"accountNameHistory\":                                    &FormElement{},\n\t\"mS-SQL-GPSLongitude\":                                   &FormElement{},\n\t\"adminPropertyPages\":                                    &FormElement{},\n\t\"securityIdentifier\":                                    &FormElement{},\n\t\"groupMembershipSAM\":                                    &FormElement{},\n\t\"serviceDNSNameType\":                                    &FormElement{},\n\t\"meetingIsEncrypted\":                                    &FormElement{},\n\t\"mS-SQL-Applications\":                                   &FormElement{},\n\t\"lastUpdateSequence\":                                    &FormElement{},\n\t\"lastContentIndexed\":                                    &FormElement{},\n\t\"meetingDescription\":                                    &FormElement{},\n\t\"fRSTimeLastCommand\":                                    &FormElement{},\n\t\"monikerDisplayName\":                                    &FormElement{},\n\t\"requiredCategories\":                                    &FormElement{},\n\t\"upgradeProductCode\":                                    &FormElement{},\n\t\"aCSMaxNoOfLogFiles\":                                    &FormElement{},\n\t\"mS-SQL-CharacterSet\":                                   &FormElement{},\n\t\"meetingContactInfo\":                                    &FormElement{},\n\t\"mS-SQL-CreationDate\":                                   &FormElement{},\n\t\"domainPolicyObject\":                                    &FormElement{},\n\t\"dhcpObjDescription\":                                    &FormElement{},\n\t\"meetingApplication\":                                    &FormElement{},\n\t\"defaultHidingValue\":                                    &FormElement{},\n\t\"fRSMemberReference\":                                    &FormElement{},\n\t\"dhcpIdentification\":                                    &FormElement{},\n\t\"trustAuthOutgoing\":                                     &FormElement{},\n\t\"systemMustContain\":                                     &FormElement{},\n\t\"primaryGroupToken\":                                     &FormElement{},\n\t\"rpcNsProfileEntry\":                                     &FormElement{},\n\t\"trustAuthIncoming\":                                     &FormElement{},\n\t\"mSMQPrevSiteGates\":                                     &FormElement{},\n\t\"queryPolicyObject\":                                     &FormElement{},\n\t\"optionDescription\":                                     &FormElement{},\n\t\"aCSMaximumSDUSize\":                                     &FormElement{},\n\t\"nonSecurityMember\":                                     &FormElement{},\n\t\"fRSReplicaSetType\":                                     &FormElement{},\n\t\"aCSTotalNoOfFlows\":                                     &FormElement{},\n\t\"possibleInferiors\":                                     &FormElement{},\n\t\"netbootMaxClients\":                                     &FormElement{},\n\t\"mS-SQL-GPSLatitude\":                                    &FormElement{},\n\t\"aCSPermissionBits\":                                     &FormElement{},\n\t\"mSMQTransactional\":                                     &FormElement{},\n\t\"mS-SQL-Description\":                                    &FormElement{},\n\t\"allowedAttributes\":                                     &FormElement{},\n\t\"fRSFaultCondition\":                                     &FormElement{},\n\t\"tombstoneLifetime\":                                     &FormElement{},\n\t\"remoteStorageGUID\":                                     &FormElement{},\n\t\"showInAddressBook\":                                     &FormElement{},\n\t\"defaultClassStore\":                                     &FormElement{},\n\t\"meetingOriginator\":                                     &FormElement{},\n\t\"userPrincipalName\":                                     &FormElement{},\n\t\"aCSMinimumLatency\":                                     &FormElement{},\n\t\"isPrivilegeHolder\":                                     &FormElement{},\n\t\"fRSReplicaSetGUID\":                                     &FormElement{},\n\t\"rIDAllocationPool\":                                     &FormElement{},\n\t\"pKIDefaultKeySpec\":                                     &FormElement{},\n\t\"dynamicLDAPServer\":                                     &FormElement{},\n\t\"serverReferenceBL\":                                     &FormElement{},\n\t\"fRSServiceCommand\":                                     &FormElement{},\n\t\"sDRightsEffective\":                                     &FormElement{},\n\t\"proxiedObjectName\":                                     &FormElement{},\n\t\"meetingRecurrence\":                                     &FormElement{},\n\t\"cOMTreatAsClassId\":                                     &FormElement{},\n\t\"globalAddressList\":                                     &FormElement{},\n\t\"extendedClassInfo\":                                     &FormElement{},\n\t\"machineWidePolicy\":                                     &FormElement{},\n\t\"foreignIdentifier\":                                     &FormElement{},\n\t\"dNReferenceUpdate\":                                     &FormElement{},\n\t\"trustPosixOffset\":                                      &FormElement{},\n\t\"enabledConnection\":                                     &FormElement{},\n\t\"ipsecNFAReference\":                                     &FormElement{},\n\t\"userWorkstations\":                                      &FormElement{},\n\t\"garbageCollPeriod\":                                     &FormElement{},\n\t\"mSMQComputerType\":                                      &FormElement{},\n\t\"logonWorkstation\":                                      &FormElement{},\n\t\"mSMQJournalQuota\":                                      &FormElement{},\n\t\"remoteSourceType\":                                      &FormElement{},\n\t\"pwdHistoryLength\":                                      &FormElement{},\n\t\"mSMQBasePriority\":                                      &FormElement{},\n\t\"systemMayContain\":                                      &FormElement{},\n\t\"mS-SQL-ThirdParty\":                                     &FormElement{},\n\t\"mSMQQueueNameExt\":                                      &FormElement{},\n\t\"fRSUpdateTimeout\":                                      &FormElement{},\n\t\"mSMQPrivacyLevel\":                                      &FormElement{},\n\t\"shellContextMenu\":                                      &FormElement{},\n\t\"wellKnownObjects\":                                      &FormElement{},\n\t\"transportDLLName\":                                      &FormElement{},\n\t\"qualityOfService\":                                      &FormElement{},\n\t\"lockoutThreshold\":                                      &FormElement{},\n\t\"remoteServerName\":                                      &FormElement{},\n\t\"previousParentCA\":                                      &FormElement{},\n\t\"dSUIShellMaximum\":                                      &FormElement{},\n\t\"notificationList\":                                      &FormElement{},\n\t\"addressBookRoots\":                                      &FormElement{},\n\t\"fRSPrimaryMember\":                                      &FormElement{},\n\t\"meetingStartTime\":                                      &FormElement{},\n\t\"mSMQSiteGatesMig\":                                      &FormElement{},\n\t\"dhcpReservations\":                                      &FormElement{},\n\t\"adminContextMenu\":                                      &FormElement{},\n\t\"pKIOverlapPeriod\":                                      &FormElement{},\n\t\"winsockAddresses\":                                      &FormElement{},\n\t\"mSMQAuthenticate\":                                      &FormElement{},\n\t\"dSUIAdminMaximum\":                                      &FormElement{},\n\t\"appSchemaVersion\":                                      &FormElement{},\n\t\"serviceClassInfo\":                                      &FormElement{},\n\t\"aCSEventLogLevel\":                                      &FormElement{},\n\t\"userSharedFolder\":                                      &FormElement{},\n\t\"domainWidePolicy\":                                      &FormElement{},\n\t\"rIDSetReferences\":                                      &FormElement{},\n\t\"canUpgradeScript\":                                      &FormElement{},\n\t\"classDisplayName\":                                      &FormElement{},\n\t\"adminDescription\":                                      &FormElement{},\n\t\"lSAModifiedCount\":                                      &FormElement{},\n\t\"serviceClassName\":                                      &FormElement{},\n\t\"localPolicyFlags\":                                      &FormElement{},\n\t\"rpcNsInterfaceID\":                                      &FormElement{},\n\t\"adminDisplayName\":                                      &FormElement{},\n\t\"nameServiceFlags\":                                      &FormElement{},\n\t\"meetingBandwidth\":                                      &FormElement{},\n\t\"domainIdentifier\":                                      &FormElement{},\n\t\"rIDAvailablePool\":                                      &FormElement{},\n\t\"legacyExchangeDN\":                                      &FormElement{},\n\t\"trustAttributes\":                                       &FormElement{},\n\t\"fRSRootSecurity\":                                       &FormElement{},\n\t\"superiorDNSRoot\":                                       &FormElement{},\n\t\"printMaxYExtent\":                                       &FormElement{},\n\t\"printMaxXExtent\":                                       &FormElement{},\n\t\"printMinYExtent\":                                       &FormElement{},\n\t\"printMinXExtent\":                                       &FormElement{},\n\t\"attributeSyntax\":                                       &FormElement{},\n\t\"printAttributes\":                                       &FormElement{},\n\t\"groupAttributes\":                                       &FormElement{},\n\t\"fileExtPriority\":                                       &FormElement{},\n\t\"mSMQServiceType\":                                       &FormElement{},\n\t\"operatingSystem\":                                       &FormElement{},\n\t\"mS-SQL-SortOrder\":                                      &FormElement{},\n\t\"versionNumberLo\":                                       &FormElement{},\n\t\"msRRASAttribute\":                                       &FormElement{},\n\t\"lastKnownParent\":                                       &FormElement{},\n\t\"shortServerName\":                                       &FormElement{},\n\t\"lockoutDuration\":                                       &FormElement{},\n\t\"defaultPriority\":                                       &FormElement{},\n\t\"rpcNsEntryFlags\":                                       &FormElement{},\n\t\"optionsLocation\":                                       &FormElement{},\n\t\"versionNumberHi\":                                       &FormElement{},\n\t\"rpcNsAnnotation\":                                       &FormElement{},\n\t\"purportedSearch\":                                       &FormElement{},\n\t\"aCSDSBMPriority\":                                       &FormElement{},\n\t\"mSMQSiteForeign\":                                       &FormElement{},\n\t\"currentLocation\":                                       &FormElement{},\n\t\"meetingProtocol\":                                       &FormElement{},\n\t\"publicKeyPolicy\":                                       &FormElement{},\n\t\"mS-SQL-Publisher\":                                      &FormElement{},\n\t\"createWizardExt\":                                       &FormElement{},\n\t\"mS-SQL-Clustered\":                                      &FormElement{},\n\t\"volTableIdxGUID\":                                       &FormElement{},\n\t\"currentParentCA\":                                       &FormElement{},\n\t\"seqNotification\":                                       &FormElement{},\n\t\"serverReference\":                                       &FormElement{},\n\t\"msNPAllowDialin\":                                       &FormElement{},\n\t\"mS-SQL-GPSHeight\":                                      &FormElement{},\n\t\"mS-SQL-AppleTalk\":                                      &FormElement{},\n\t\"linkTrackSecret\":                                       &FormElement{},\n\t\"dnsAllowDynamic\":                                       &FormElement{},\n\t\"badPasswordTime\":                                       &FormElement{},\n\t\"privilegeHolder\":                                       &FormElement{},\n\t\"printMediaReady\":                                       &FormElement{},\n\t\"printMACAddress\":                                       &FormElement{},\n\t\"lSACreationTime\":                                       &FormElement{},\n\t\"meetingLocation\":                                       &FormElement{},\n\t\"aCSIdentityName\":                                       &FormElement{},\n\t\"mS-DS-CreatorSID\":                                      &FormElement{},\n\t\"mS-SQL-NamedPipe\":                                      &FormElement{},\n\t\"lDAPAdminLimits\":                                       &FormElement{},\n\t\"lDAPDisplayName\":                                       &FormElement{},\n\t\"applicationName\":                                       &FormElement{},\n\t\"pendingParentCA\":                                       &FormElement{},\n\t\"aCSCacheTimeout\":                                       &FormElement{},\n\t\"meetingLanguage\":                                       &FormElement{},\n\t\"aCSDSBMDeadTime\":                                       &FormElement{},\n\t\"cACertificateDN\":                                       &FormElement{},\n\t\"userParameters\":                                        &FormElement{},\n\t\"trustDirection\":                                        &FormElement{},\n\t\"mSMQQueueQuota\":                                        &FormElement{},\n\t\"mSMQEncryptKey\":                                        &FormElement{},\n\t\"terminalServer\":                                        &FormElement{},\n\t\"printStartTime\":                                        &FormElement{},\n\t\"syncWithObject\":                                        &FormElement{},\n\t\"groupsToIgnore\":                                        &FormElement{},\n\t\"syncMembership\":                                        &FormElement{},\n\t\"syncAttributes\":                                        &FormElement{},\n\t\"nextLevelStore\":                                        &FormElement{},\n\t\"sAMAccountType\":                                        &FormElement{},\n\t\"mS-SQL-Keywords\":                                       &FormElement{},\n\t\"proxyAddresses\":                                        &FormElement{},\n\t\"bytesPerMinute\":                                        &FormElement{},\n\t\"printMaxCopies\":                                        &FormElement{},\n\t\"primaryGroupID\":                                        &FormElement{},\n\t\"nTGroupMembers\":                                        &FormElement{},\n\t\"mSMQDsServices\":                                        &FormElement{},\n\t\"fRSVersionGUID\":                                        &FormElement{},\n\t\"fRSWorkingPath\":                                        &FormElement{},\n\t\"otherTelephone\":                                        &FormElement{},\n\t\"otherHomePhone\":                                        &FormElement{},\n\t\"oEMInformation\":                                        &FormElement{},\n\t\"networkAddress\":                                        &FormElement{},\n\t\"mSMQDigestsMig\":                                        &FormElement{},\n\t\"meetingKeyword\":                                        &FormElement{},\n\t\"lDAPIPDenyList\":                                        &FormElement{},\n\t\"installUiLevel\":                                        &FormElement{},\n\t\"gPCFileSysPath\":                                        &FormElement{},\n\t\"fRSStagingPath\":                                        &FormElement{},\n\t\"auxiliaryClass\":                                        &FormElement{},\n\t\"accountExpires\":                                        &FormElement{},\n\t\"dhcpProperties\":                                        &FormElement{},\n\t\"desktopProfile\":                                        &FormElement{},\n\t\"aCSServiceType\":                                        &FormElement{},\n\t\"assocNTAccount\":                                        &FormElement{},\n\t\"creationWizard\":                                        &FormElement{},\n\t\"cOMOtherProgId\":                                        &FormElement{},\n\t\"auditingPolicy\":                                        &FormElement{},\n\t\"privilegeValue\":                                        &FormElement{},\n\t\"mS-SQL-Location\":                                       &FormElement{},\n\t\"pKIDefaultCSPs\":                                        &FormElement{},\n\t\"printShareName\":                                        &FormElement{},\n\t\"isSingleValued\":                                        &FormElement{},\n\t\"domainCrossRef\":                                        &FormElement{},\n\t\"netbootSIFFile\":                                        &FormElement{},\n\t\"cOMUniqueLIBID\":                                        &FormElement{},\n\t\"serviceDNSName\":                                        &FormElement{},\n\t\"objectCategory\":                                        &FormElement{},\n\t\"serviceClassID\":                                        &FormElement{},\n\t\"dhcpUpdateTime\":                                        &FormElement{},\n\t\"sAMAccountName\":                                        &FormElement{},\n\t\"meetingEndTime\":                                        &FormElement{},\n\t\"mS-SQL-Language\":                                       &FormElement{},\n\t\"aCSDSBMRefresh\":                                        &FormElement{},\n\t\"mS-SQL-Database\":                                       &FormElement{},\n\t\"cOMInterfaceID\":                                        &FormElement{},\n\t\"mS-SQL-AllowKnownPullSubscription\":                     &FormElement{},\n\t\"mS-SQL-AllowAnonymousSubscription\":                     &FormElement{},\n\t\"managedObjects\":                                        &FormElement{},\n\t\"possSuperiors\":                                         &FormElement{},\n\t\"transportType\":                                         &FormElement{},\n\t\"groupPriority\":                                         &FormElement{},\n\t\"rpcNsPriority\":                                         &FormElement{},\n\t\"mSMQQueueType\":                                         &FormElement{},\n\t\"versionNumber\":                                         &FormElement{},\n\t\"uSNLastObjRem\":                                         &FormElement{},\n\t\"templateRoots\":                                         &FormElement{},\n\t\"pwdProperties\":                                         &FormElement{},\n\t\"printNumberUp\":                                         &FormElement{},\n\t\"fRSExtensions\":                                         &FormElement{},\n\t\"printRateUnit\":                                         &FormElement{},\n\t\"msiScriptSize\":                                         &FormElement{},\n\t\"printSpooling\":                                         &FormElement{},\n\t\"queryPolicyBL\":                                         &FormElement{},\n\t\"proxyLifetime\":                                         &FormElement{},\n\t\"operatorCount\":                                         &FormElement{},\n\t\"netbootServer\":                                         &FormElement{},\n\t\"fSMORoleOwner\":                                         &FormElement{},\n\t\"driverVersion\":                                         &FormElement{},\n\t\"mS-SQL-Version\":                                        &FormElement{},\n\t\"mSMQNameStyle\":                                         &FormElement{},\n\t\"schemaVersion\":                                         &FormElement{},\n\t\"directReports\":                                         &FormElement{},\n\t\"addressSyntax\":                                         &FormElement{},\n\t\"printFormName\":                                         &FormElement{},\n\t\"msiScriptPath\":                                         &FormElement{},\n\t\"aCSServerList\":                                         &FormElement{},\n\t\"moveTreeState\":                                         &FormElement{},\n\t\"mSMQSiteGates\":                                         &FormElement{},\n\t\"mSMQDsService\":                                         &FormElement{},\n\t\"objectVersion\":                                         &FormElement{},\n\t\"dNSTombstoned\":                                         &FormElement{},\n\t\"mSMQLongLived\":                                         &FormElement{},\n\t\"fRSLevelLimit\":                                         &FormElement{},\n\t\"msiScriptName\":                                         &FormElement{},\n\t\"dhcpUniqueKey\":                                         &FormElement{},\n\t\"extensionName\":                                         &FormElement{},\n\t\"rpcNsBindings\":                                         &FormElement{},\n\t\"printBinNames\":                                         &FormElement{},\n\t\"replicaSource\":                                         &FormElement{},\n\t\"printLanguage\":                                         &FormElement{},\n\t\"mS-SQL-Contact\":                                        &FormElement{},\n\t\"nTMixedDomain\":                                         &FormElement{},\n\t\"fRSFileFilter\":                                         &FormElement{},\n\t\"birthLocation\":                                         &FormElement{},\n\t\"friendlyNames\":                                         &FormElement{},\n\t\"ipsecDataType\":                                         &FormElement{},\n\t\"meetingRating\":                                         &FormElement{},\n\t\"indexedScopes\":                                         &FormElement{},\n\t\"rpcNsObjectID\":                                         &FormElement{},\n\t\"modifiedCount\":                                         &FormElement{},\n\t\"oMObjectClass\":                                         &FormElement{},\n\t\"aCSPolicyName\":                                         &FormElement{},\n\t\"timeVolChange\":                                         &FormElement{},\n\t\"currMachineId\":                                         &FormElement{},\n\t\"schemaFlagsEx\":                                         &FormElement{},\n\t\"validAccesses\":                                         &FormElement{},\n\t\"domainReplica\":                                         &FormElement{},\n\t\"mSMQInterval2\":                                         &FormElement{},\n\t\"mSMQInterval1\":                                         &FormElement{},\n\t\"canonicalName\":                                         &FormElement{},\n\t\"ntPwdHistory\":                                          &FormElement{},\n\t\"trustPartner\":                                          &FormElement{},\n\t\"lmPwdHistory\":                                          &FormElement{},\n\t\"mS-SQL-Status\":                                         &FormElement{},\n\t\"USNIntersite\":                                          &FormElement{},\n\t\"netbootTools\":                                          &FormElement{},\n\t\"priorSetTime\":                                          &FormElement{},\n\t\"mS-SQL-Memory\":                                         &FormElement{},\n\t\"mSMQServices\":                                          &FormElement{},\n\t\"currentValue\":                                          &FormElement{},\n\t\"siteLinkList\":                                          &FormElement{},\n\t\"remoteSource\":                                          &FormElement{},\n\t\"setupCommand\":                                          &FormElement{},\n\t\"dSHeuristics\":                                          &FormElement{},\n\t\"replInterval\":                                          &FormElement{},\n\t\"printEndTime\":                                          &FormElement{},\n\t\"instanceType\":                                          &FormElement{},\n\t\"otherIpPhone\":                                          &FormElement{},\n\t\"mSMQSiteName\":                                          &FormElement{},\n\t\"meetingOwner\":                                          &FormElement{},\n\t\"printCollate\":                                          &FormElement{},\n\t\"defaultGroup\":                                          &FormElement{},\n\t\"minPwdLength\":                                          &FormElement{},\n\t\"netbootSCPBL\":                                          &FormElement{},\n\t\"mhsORAddress\":                                          &FormElement{},\n\t\"rpcNsCodeset\":                                          &FormElement{},\n\t\"hasMasterNCs\":                                          &FormElement{},\n\t\"mSMQMigrated\":                                          &FormElement{},\n\t\"dSASignature\":                                          &FormElement{},\n\t\"invocationId\":                                          &FormElement{},\n\t\"cOMTypelibId\":                                          &FormElement{},\n\t\"creationTime\":                                          &FormElement{},\n\t\"meetingScope\":                                          &FormElement{},\n\t\"volTableGUID\":                                          &FormElement{},\n\t\"siteObjectBL\":                                          &FormElement{},\n\t\"aCSTimeOfDay\":                                          &FormElement{},\n\t\"aCSDirection\":                                          &FormElement{},\n\t\"maxTicketAge\":                                          &FormElement{},\n\t\"schemaUpdate\":                                          &FormElement{},\n\t\"minTicketAge\":                                          &FormElement{},\n\t\"ipsecNegotiationPolicyReference\":                       &FormElement{},\n\t\"helpFileName\":                                          &FormElement{},\n\t\"schemaIDGUID\":                                          &FormElement{},\n\t\"createDialog\":                                          &FormElement{},\n\t\"mSMQNt4Flags\":                                          &FormElement{},\n\t\"packageFlags\":                                          &FormElement{},\n\t\"wWWHomePage\":                                           &FormElement{},\n\t\"volumeCount\":                                           &FormElement{},\n\t\"printStatus\":                                           &FormElement{},\n\t\"uPNSuffixes\":                                           &FormElement{},\n\t\"trustParent\":                                           &FormElement{},\n\t\"tokenGroups\":                                           &FormElement{},\n\t\"systemFlags\":                                           &FormElement{},\n\t\"syncWithSID\":                                           &FormElement{},\n\t\"dNSProperty\":                                           &FormElement{},\n\t\"superScopes\":                                           &FormElement{},\n\t\"sPNMappings\":                                           &FormElement{},\n\t\"printNotify\":                                           &FormElement{},\n\t\"printMemory\":                                           &FormElement{},\n\t\"serverState\":                                           &FormElement{},\n\t\"mSMQVersion\":                                           &FormElement{},\n\t\"rIDUsedPool\":                                           &FormElement{},\n\t\"queryFilter\":                                           &FormElement{},\n\t\"printerName\":                                           &FormElement{},\n\t\"preferredOU\":                                           &FormElement{},\n\t\"primaryInternationalISDNNumber\":                        &FormElement{},\n\t\"oMTIndxGuid\":                                           &FormElement{},\n\t\"mSMQUserSid\":                                           &FormElement{},\n\t\"fRSRootPath\":                                           &FormElement{},\n\t\"mSMQJournal\":                                           &FormElement{},\n\t\"contextMenu\":                                           &FormElement{},\n\t\"aCSPriority\":                                           &FormElement{},\n\t\"mSMQSignKey\":                                           &FormElement{},\n\t\"netbootGUID\":                                           &FormElement{},\n\t\"mSMQOwnerID\":                                           &FormElement{},\n\t\"mustContain\":                                           &FormElement{},\n\t\"dnsAllowXFR\":                                           &FormElement{},\n\t\"mS-SQL-Vines\":                                          &FormElement{},\n\t\"mSMQDigests\":                                           &FormElement{},\n\t\"lockoutTime\":                                           &FormElement{},\n\t\"lastSetTime\":                                           &FormElement{},\n\t\"countryCode\":                                           &FormElement{},\n\t\"mS-SQL-TCPIP\":                                          &FormElement{},\n\t\"mSMQForeign\":                                           &FormElement{},\n\t\"meetingType\":                                           &FormElement{},\n\t\"dhcpOptions\":                                           &FormElement{},\n\t\"dhcpServers\":                                           &FormElement{},\n\t\"assetNumber\":                                           &FormElement{},\n\t\"addressType\":                                           &FormElement{},\n\t\"mSMQCSPName\":                                           &FormElement{},\n\t\"msiFileList\":                                           &FormElement{},\n\t\"dNSHostName\":                                           &FormElement{},\n\t\"dhcpSubnets\":                                           &FormElement{},\n\t\"pKIKeyUsage\":                                           &FormElement{},\n\t\"attributeID\":                                           &FormElement{},\n\t\"objectCount\":                                           &FormElement{},\n\t\"timeRefresh\":                                           &FormElement{},\n\t\"profilePath\":                                           &FormElement{},\n\t\"productCode\":                                           &FormElement{},\n\t\"otherMobile\":                                           &FormElement{},\n\t\"badPwdCount\":                                           &FormElement{},\n\t\"mS-SQL-Build\":                                          &FormElement{},\n\t\"nETBIOSName\":                                           &FormElement{},\n\t\"mS-SQL-Alias\":                                          &FormElement{},\n\t\"maxRenewAge\":                                           &FormElement{},\n\t\"treatAsLeaf\":                                           &FormElement{},\n\t\"mSMQNt4Stub\":                                           &FormElement{},\n\t\"packageType\":                                           &FormElement{},\n\t\"isEphemeral\":                                           &FormElement{},\n\t\"dMDLocation\":                                           &FormElement{},\n\t\"dhcpClasses\":                                           &FormElement{},\n\t\"forceLogoff\":                                           &FormElement{},\n\t\"whenCreated\":                                           &FormElement{},\n\t\"meetingName\":                                           &FormElement{},\n\t\"mailAddress\":                                           &FormElement{},\n\t\"meetingBlob\":                                           &FormElement{},\n\t\"machineRole\":                                           &FormElement{},\n\t\"searchFlags\":                                           &FormElement{},\n\t\"whenChanged\":                                           &FormElement{},\n\t\"dhcpObjName\":                                           &FormElement{},\n\t\"aCSMaxAggregatePeakRatePerUser\":                        &FormElement{},\n\t\"packageName\":                                           &FormElement{},\n\t\"systemOnly\":                                            &FormElement{},\n\t\"mSMQOSType\":                                            &FormElement{},\n\t\"queryPoint\":                                            &FormElement{},\n\t\"printOwner\":                                            &FormElement{},\n\t\"uSNCreated\":                                            &FormElement{},\n\t\"siteServer\":                                            &FormElement{},\n\t\"rpcNsGroup\":                                            &FormElement{},\n\t\"sIDHistory\":                                            &FormElement{},\n\t\"fRSVersion\":                                            &FormElement{},\n\t\"logonHours\":                                            &FormElement{},\n\t\"netbootAnswerOnlyValidClients\":                         &FormElement{},\n\t\"pwdLastSet\":                                            &FormElement{},\n\t\"printColor\":                                            &FormElement{},\n\t\"mS-SQL-Type\":                                           &FormElement{},\n\t\"fromServer\":                                            &FormElement{},\n\t\"serverRole\":                                            &FormElement{},\n\t\"priorValue\":                                            &FormElement{},\n\t\"logonCount\":                                            &FormElement{},\n\t\"unicodePwd\":                                            &FormElement{},\n\t\"subClassOf\":                                            &FormElement{},\n\t\"mS-SQL-Size\":                                           &FormElement{},\n\t\"privateKey\":                                            &FormElement{},\n\t\"siteObject\":                                            &FormElement{},\n\t\"scriptPath\":                                            &FormElement{},\n\t\"serverName\":                                            &FormElement{},\n\t\"mSMQSiteID\":                                            &FormElement{},\n\t\"rightsGuid\":                                            &FormElement{},\n\t\"rIDNextRID\":                                            &FormElement{},\n\t\"meetingURL\":                                            &FormElement{},\n\t\"addressEntryDisplayTableMSDOS\":                         &FormElement{},\n\t\"maxStorage\":                                            &FormElement{},\n\t\"rangeUpper\":                                            &FormElement{},\n\t\"rangeLower\":                                            &FormElement{},\n\t\"otherPager\":                                            &FormElement{},\n\t\"isMemberOfPartialAttributeSet\":                         &FormElement{},\n\t\"parentGUID\":                                            &FormElement{},\n\t\"department\":                                            &FormElement{},\n\t\"mayContain\":                                            &FormElement{},\n\t\"adminCount\":                                            &FormElement{},\n\t\"lastLogoff\":                                            &FormElement{},\n\t\"masteredBy\":                                            &FormElement{},\n\t\"employeeID\":                                            &FormElement{},\n\t\"dhcpMaxKey\":                                            &FormElement{},\n\t\"driverName\":                                            &FormElement{},\n\t\"mS-SQL-Name\":                                           &FormElement{},\n\t\"categoryId\":                                            &FormElement{},\n\t\"additionalTrustedServiceNames\":                         &FormElement{},\n\t\"scopeFlags\":                                            &FormElement{},\n\t\"categories\":                                            &FormElement{},\n\t\"netbootNewMachineNamingPolicy\":                         &FormElement{},\n\t\"cOMClassID\":                                            &FormElement{},\n\t\"uSNChanged\":                                            &FormElement{},\n\t\"objectGUID\":                                            &FormElement{},\n\t\"dhcpRanges\":                                            &FormElement{},\n\t\"schemaInfo\":                                            &FormElement{},\n\t\"otherFacsimileTelephoneNumber\":                         &FormElement{},\n\t\"machinePasswordChangeInterval\":                         &FormElement{},\n\t\"rootTrust\":                                             &FormElement{},\n\t\"trustType\":                                             &FormElement{},\n\t\"groupType\":                                             &FormElement{},\n\t\"uSNSource\":                                             &FormElement{},\n\t\"mSMQQuota\":                                             &FormElement{},\n\t\"mSMQSites\":                                             &FormElement{},\n\t\"fromEntry\":                                             &FormElement{},\n\t\"mS-SQL-SPX\":                                            &FormElement{},\n\t\"gPOptions\":                                             &FormElement{},\n\t\"msiScript\":                                             &FormElement{},\n\t\"printRate\":                                             &FormElement{},\n\t\"cRLPartitionedRevocationList\":                          &FormElement{},\n\t\"assistant\":                                             &FormElement{},\n\t\"fRSDSPoll\":                                             &FormElement{},\n\t\"partialAttributeDeletionList\":                          &FormElement{},\n\t\"lastLogon\":                                             &FormElement{},\n\t\"governsID\":                                             &FormElement{},\n\t\"appliesTo\":                                             &FormElement{},\n\t\"eFSPolicy\":                                             &FormElement{},\n\t\"uASCompat\":                                             &FormElement{},\n\t\"prefixMap\":                                             &FormElement{},\n\t\"isDefunct\":                                             &FormElement{},\n\t\"dhcpSites\":                                             &FormElement{},\n\t\"iPSECNegotiationPolicyAction\":                          &FormElement{},\n\t\"dnsRecord\":                                             &FormElement{},\n\t\"cOMProgID\":                                             &FormElement{},\n\t\"homeDrive\":                                             &FormElement{},\n\t\"meetingIP\":                                             &FormElement{},\n\t\"aCSNonReservedMinPolicedSize\":                          &FormElement{},\n\t\"dhcpState\":                                             &FormElement{},\n\t\"mSMQLabel\":                                             &FormElement{},\n\t\"maxPwdAge\":                                             &FormElement{},\n\t\"minPwdAge\":                                             &FormElement{},\n\t\"cRLObject\":                                             &FormElement{},\n\t\"objectSid\":                                             &FormElement{},\n\t\"meetingID\":                                             &FormElement{},\n\t\"ipsecName\":                                             &FormElement{},\n\t\"isDeleted\":                                             &FormElement{},\n\t\"aCSAggregateTokenRatePerUser\":                          &FormElement{},\n\t\"ipsecData\":                                             &FormElement{},\n\t\"domainCAs\":                                             &FormElement{},\n\t\"cAConnect\":                                             &FormElement{},\n\t\"printMaxResolutionSupported\":                           &FormElement{},\n\t\"dhcpFlags\":                                             &FormElement{},\n\t\"helpData16\":                                            &FormElement{},\n\t\"managedBy\":                                             &FormElement{},\n\t\"helpData32\":                                            &FormElement{},\n\t\"mSMQSite2\":                                             &FormElement{},\n\t\"mSMQSite1\":                                             &FormElement{},\n\t\"replTopologyStayOfExecution\":                           &FormElement{},\n\t\"allowedChildClassesEffective\":                          &FormElement{},\n\t\"oMSyntax\":                                              &FormElement{},\n\t\"priority\":                                              &FormElement{},\n\t\"keywords\":                                              &FormElement{},\n\t\"mSMQCost\":                                              &FormElement{},\n\t\"siteList\":                                              &FormElement{},\n\t\"revision\":                                              &FormElement{},\n\t\"repsFrom\":                                              &FormElement{},\n\t\"userCert\":                                              &FormElement{},\n\t\"mSMQQMID\":                                              &FormElement{},\n\t\"portName\":                                              &FormElement{},\n\t\"netbootLocallyInstalledOSes\":                           &FormElement{},\n\t\"division\":                                              &FormElement{},\n\t\"aCSMaxSizeOfRSVPAccountFile\":                           &FormElement{},\n\t\"dhcpType\":                                              &FormElement{},\n\t\"wbemPath\":                                              &FormElement{},\n\t\"siteGUID\":                                              &FormElement{},\n\t\"rDNAttID\":                                              &FormElement{},\n\t\"aCSRSVPAccountFilesLocation\":                           &FormElement{},\n\t\"mSMQDependentClientServices\":                           &FormElement{},\n\t\"location\":                                              &FormElement{},\n\t\"fRSFlags\":                                              &FormElement{},\n\t\"iconPath\":                                              &FormElement{},\n\t\"cAWEBURL\":                                              &FormElement{},\n\t\"mscopeId\":                                              &FormElement{},\n\t\"treeName\":                                              &FormElement{},\n\t\"schedule\":                                              &FormElement{},\n\t\"parentCA\":                                              &FormElement{},\n\t\"cOMCLSID\":                                              &FormElement{},\n\t\"catalogs\":                                              &FormElement{},\n\t\"memberOf\":                                              &FormElement{},\n\t\"cAUsages\":                                              &FormElement{},\n\t\"dhcpMask\":                                              &FormElement{},\n\t\"flatName\":                                              &FormElement{},\n\t\"domainID\":                                              &FormElement{},\n\t\"localeID\":                                              &FormElement{},\n\t\"codePage\":                                              &FormElement{},\n\t\"aCSEnableRSVPMessageLogging\":                           &FormElement{},\n\t\"printOrientationsSupported\":                            &FormElement{},\n\t\"msRRASVendorAttributeEntry\":                            &FormElement{},\n\t\"interSiteTopologyGenerator\":                            &FormElement{},\n\t\"options\":                                               &FormElement{},\n\t\"dnsRoot\":                                               &FormElement{},\n\t\"iPSECNegotiationPolicyType\":                            &FormElement{},\n\t\"mS-SQL-InformationDirectory\":                           &FormElement{},\n\t\"operatingSystemServicePack\":                            &FormElement{},\n\t\"nextRid\":                                               &FormElement{},\n\t\"pekList\":                                               &FormElement{},\n\t\"subRefs\":                                               &FormElement{},\n\t\"oMTGuid\":                                               &FormElement{},\n\t\"pKTGuid\":                                               &FormElement{},\n\t\"company\":                                               &FormElement{},\n\t\"moniker\":                                               &FormElement{},\n\t\"comment\":                                               &FormElement{},\n\t\"ipPhone\":                                               &FormElement{},\n\t\"mS-DS-ConsistencyChildCount\":                           &FormElement{},\n\t\"creator\":                                               &FormElement{},\n\t\"uNCName\":                                               &FormElement{},\n\t\"dBCSPwd\":                                               &FormElement{},\n\t\"mSMQDependentClientService\":                            &FormElement{},\n\t\"certificateAuthorityObject\":                            &FormElement{},\n\t\"ipsecID\":                                               &FormElement{},\n\t\"allowedAttributesEffective\":                            &FormElement{},\n\t\"aCSMaxPeakBandwidthPerFlow\":                            &FormElement{},\n\t\"Enabled\":                                               &FormElement{},\n\t\"perRecipDialogDisplayTable\":                            &FormElement{},\n\t\"interSiteTopologyFailover\":                             &FormElement{},\n\t\"transportAddressAttribute\":                             &FormElement{},\n\t\"netbootCurrentClientCount\":                             &FormElement{},\n\t\"rIDPreviousAllocationPool\":                             &FormElement{},\n\t\"repsTo\":                                                &FormElement{},\n\t\"defaultSecurityDescriptor\":                             &FormElement{},\n\t\"lastBackupRestorationTime\":                             &FormElement{},\n\t\"fRSControlOutboundBacklog\":                             &FormElement{},\n\t\"vendor\":                                                &FormElement{},\n\t\"gPLink\":                                                &FormElement{},\n\t\"originalDisplayTableMSDOS\":                             &FormElement{},\n\t\"linkID\":                                                &FormElement{},\n\t\"msNPSavedCallingStationID\":                             &FormElement{},\n\t\"mAPIID\":                                                &FormElement{},\n\t\"serviceBindingInformation\":                             &FormElement{},\n\t\"nCName\":                                                &FormElement{},\n\t\"tokenGroupsNoGCAcceptable\":                             &FormElement{},\n\t\"tokenGroupsGlobalAndUniversal\":                         &FormElement{},\n\t\"msRASSavedFramedIPAddress\":                             &FormElement{},\n\t\"aCSAllocableRSVPBandwidth\":                             &FormElement{},\n\t\"lockOutObservationWindow\":                              &FormElement{},\n\t\"netbootIntelliMirrorOSes\":                              &FormElement{},\n\t\"aCSNonReservedMaxSDUSize\":                              &FormElement{},\n\t\"notes\":                                                 &FormElement{},\n\t\"retiredReplDSASignatures\":                              &FormElement{},\n\t\"aCSMaxTokenBucketPerFlow\":                              &FormElement{},\n\t\"addressEntryDisplayTable\":                              &FormElement{},\n\t\"aCSMinimumDelayVariation\":                              &FormElement{},\n\t\"fRSControlInboundBacklog\":                              &FormElement{},\n\t\"flags\":                                                 &FormElement{},\n\t\"mS-SQL-LastDiagnosticDate\":                             &FormElement{},\n\t\"gPCMachineExtensionNames\":                              &FormElement{},\n\t\"ms-DS-MachineAccountQuota\":                             &FormElement{},\n\t\"perMsgDialogDisplayTable\":                              &FormElement{},\n\t\"defaultLocalPolicyObject\":                              &FormElement{},\n\t\"msRASSavedCallbackNumber\":                              &FormElement{},\n\t\"parentCACertificateChain\":                              &FormElement{},\n\t\"gPCFunctionalityVersion\":                               &FormElement{},\n\t\"fRSServiceCommandStatus\":                               &FormElement{},\n\t\"aCSNonReservedTokenSize\":                               &FormElement{},\n\t\"aCSMaxSizeOfRSVPLogFile\":                               &FormElement{},\n\t\"cost\":                                                  &FormElement{},\n\t\"modifiedCountAtLastProm\":                               &FormElement{},\n\t\"aCSRSVPLogFilesLocation\":                               &FormElement{},\n\t\"supplementalCredentials\":                               &FormElement{},\n\t\"bridgeheadTransportList\":                               &FormElement{},\n\t\"mSMQSignCertificatesMig\":                               &FormElement{},\n\t\"msRADIUSFramedIPAddress\":                               &FormElement{},\n\t\"mS-DS-ReplicatesNCReason\":                              &FormElement{},\n\t\"aCSEnableRSVPAccounting\":                               &FormElement{},\n\t\"fRSTimeLastConfigChange\":                               &FormElement{},\n\t\"printStaplingSupported\":                                &FormElement{},\n\t\"interSiteTopologyRenew\":                                &FormElement{},\n\t\"operatingSystemVersion\":                                &FormElement{},\n\t\"otherLoginWorkstations\":                                &FormElement{},\n\t\"netbootAllowNewClients\":                                &FormElement{},\n\t\"mS-SQL-UnicodeSortOrder\":                               &FormElement{},\n\t\"url\":                                                   &FormElement{},\n\t\"pKT\":                                                   &FormElement{},\n\t\"serviceInstanceVersion\":                                &FormElement{},\n\t\"showInAdvancedViewOnly\":                                &FormElement{},\n\t\"aCSMaxTokenRatePerFlow\":                                &FormElement{},\n\t\"isCriticalSystemObject\":                                &FormElement{},\n\t\"meetingMaxParticipants\":                                &FormElement{},\n\t\"aNR\":                                                   &FormElement{},\n\t\"rid\":                                                   &FormElement{},\n\t\"proxyGenerationEnabled\":                                &FormElement{},\n\t\"fRSControlDataCreation\":                                &FormElement{},\n\t\"previousCACertificates\":                                &FormElement{},\n\t\"contentIndexingAllowed\":                                &FormElement{},\n\t\"policyReplicationFlags\":                                &FormElement{},\n\t\"frsComputerReferenceBL\":                                &FormElement{},\n\t\"aCSNonReservedPeakRate\":                                &FormElement{},\n\t\"aCSMaxNoOfAccountFiles\":                                &FormElement{},\n\t\"physicalLocationObject\":                                &FormElement{},\n\t\"mSMQOutRoutingServers\":                                 &FormElement{},\n\t\"bridgeheadServerListBL\":                                &FormElement{},\n\t\"msRADIUSCallbackNumber\":                                &FormElement{},\n\t\"netbootMachineFilePath\":                                &FormElement{},\n\t\"mSMQQueueJournalQuota\":                                 &FormElement{},\n\t\"netbootAnswerRequests\":                                 &FormElement{},\n\t\"operatingSystemHotfix\":                                 &FormElement{},\n\t\"attributeSecurityGUID\":                                 &FormElement{},\n\t\"superScopeDescription\":                                 &FormElement{},\n\t\"otherWellKnownObjects\":                                 &FormElement{},\n\t\"aCSNonReservedTxLimit\":                                 &FormElement{},\n\t\"authenticationOptions\":                                 &FormElement{},\n\t\"altSecurityIdentities\":                                 &FormElement{},\n\t\"gPCUserExtensionNames\":                                 &FormElement{},\n\t\"netbootInitialization\":                                 &FormElement{},\n\t\"mS-SQL-RegisteredOwner\":                                &FormElement{},\n\t\"aCSMaxDurationPerFlow\":                                 &FormElement{},\n\t\"pKICriticalExtensions\":                                 &FormElement{},\n\t\"attributeDisplayNames\":                                 &FormElement{},\n\t\"mS-SQL-AllowImmediateUpdatingSubscription\": &FormElement{},\n\t\"msRASSavedFramedRoute\":                     &FormElement{},\n\t\"userSharedFolderOther\":                     &FormElement{},\n\t\"extendedAttributeInfo\":                     &FormElement{},\n\t\"netbootMirrorDataFile\":                     &FormElement{},\n\t\"aCSMinimumPolicedSize\":                     &FormElement{},\n\t\"localizationDisplayId\":                     &FormElement{},\n\t\"meetingAdvertiseScope\":                     &FormElement{},\n\t\"dSUIAdminNotification\":                     &FormElement{},\n\t\"mS-SQL-LastUpdatedDate\":                    &FormElement{},\n\t\"dSCorePropagationData\":                     &FormElement{},\n\t\"implementedCategories\":                     &FormElement{},\n\t\"defaultObjectCategory\":                     &FormElement{},\n\t\"domainPolicyReference\":                     &FormElement{},\n\t\"mSMQInRoutingServers\":                      &FormElement{},\n\t\"printDuplexSupported\":                      &FormElement{},\n\t\"pendingCACertificates\":                     &FormElement{},\n\t\"nTSecurityDescriptor\":                      &FormElement{},\n\t\"systemAuxiliaryClass\":                      &FormElement{},\n\t\"aCSNonReservedTxSize\":                      &FormElement{},\n\t\"mS-SQL-InformationURL\":                     &FormElement{},\n\t\"replPropertyMetaData\":                      &FormElement{},\n\t\"mS-SQL-PublicationURL\":                     &FormElement{},\n\t\"printKeepPrintedJobs\":                      &FormElement{},\n\t\"uSNDSALastObjRemoved\":                      &FormElement{},\n\t\"dnsNotifySecondaries\":                      &FormElement{},\n\t\"mS-DS-ConsistencyGuid\":                     &FormElement{},\n\t\"frsComputerReference\":                      &FormElement{},\n\t\"mS-SQL-ServiceAccount\":                     &FormElement{},\n\t\"msNPCallingStationID\":                      &FormElement{},\n\t\"mSMQSignCertificates\":                      &FormElement{},\n\t\"ipsecOwnersReference\":                      &FormElement{},\n\t\"builtinModifiedCount\":                      &FormElement{},\n\t\"privilegeDisplayName\":                      &FormElement{},\n\t\"dnsSecureSecondaries\":                      &FormElement{},\n\t\"localizedDescription\":                      &FormElement{},\n\t\"systemPossSuperiors\":                       &FormElement{},\n\t\"displayNamePrintable\":                      &FormElement{},\n\t\"servicePrincipalName\":                      &FormElement{},\n\t\"pekKeyChangeInterval\":                      &FormElement{},\n\t\"originalDisplayTable\":                      &FormElement{},\n\t\"mS-SQL-LastBackupDate\":                     &FormElement{},\n\t\"ipsecPolicyReference\":                      &FormElement{},\n\t\"certificateTemplates\":                      &FormElement{},\n\t\"hasPartialReplicaNCs\":                      &FormElement{},\n\t\"localPolicyReference\":                      &FormElement{},\n\t\"extendedCharsAllowed\":                      &FormElement{},\n\t\"ipsecFilterReference\":                      &FormElement{},\n\t\"ipsecISAKMPReference\":                      &FormElement{},\n\t\"fRSMemberReferenceBL\":                      &FormElement{},\n\t\"rpcNsTransferSyntax\":                       &FormElement{},\n\t\"mSMQRoutingServices\":                       &FormElement{},\n\t\"mS-SQL-MultiProtocol\":                      &FormElement{},\n\t\"enrollmentProviders\":                       &FormElement{},\n\t\"printNetworkAddress\":                       &FormElement{},\n\t\"msRADIUSServiceType\":                       &FormElement{},\n\t\"printPagesPerMinute\":                       &FormElement{},\n\t\"printMediaSupported\":                       &FormElement{},\n\t\"signatureAlgorithms\":                       &FormElement{},\n\t\"fRSPartnerAuthLevel\":                       &FormElement{},\n\t\"privilegeAttributes\":                       &FormElement{},\n\t\"partialAttributeSet\":                       &FormElement{},\n\t\"netbootLimitClients\":                       &FormElement{},\n\t\"mS-SQL-ConnectionURL\":                      &FormElement{},\n\t\"mS-SQL-AllowSnapshotFilesFTPDownloading\":   &FormElement{},\n\t\"pKIExpirationPeriod\":                       &FormElement{},\n\t\"nonSecurityMemberBL\":                       &FormElement{},\n\t\"initialAuthOutgoing\":                       &FormElement{},\n\t\"msRADIUSFramedRoute\":                       &FormElement{},\n\t\"controlAccessRights\":                       &FormElement{},\n\t//////////////////////////\n\t// SCHEMA: nis.schema\n\t\"uidNumber\": &FormElement{\n\t\tDescription: \"An integer uniquely identifying a user in an administrative domain\",\n\t\tOrder:       9,\n\t},\n\t\"gidNumber\": &FormElement{\n\t\tDescription: \"An integer uniquely identifying a group in an administrative domain\",\n\t\tOrder:       9,\n\t},\n\t\"gecos\": &FormElement{\n\t\tDescription: \"The GECOS field; the common name\",\n\t},\n\t\"homeDirectory\": &FormElement{\n\t\tDescription: \"The absolute path to the home directory\",\n\t\tOrder:       6,\n\t\tDatalist: []string{\n\t\t\t\"/home/\",\n\t\t\t\"/home/{user}\",\n\t\t},\n\t},\n\t\"loginShell\": &FormElement{\n\t\tDescription: \"The path to the login shell\",\n\t\tOrder:       6,\n\t\tDatalist: []string{\n\t\t\t\"/bin/bash\",\n\t\t\t\"/bin/false\",\n\t\t\t\"/bin/sh\",\n\t\t},\n\t},\n\t\"shadowLastChange\":  &FormElement{},\n\t\"shadowMin\":         &FormElement{},\n\t\"shadowMax\":         &FormElement{},\n\t\"shadowWarning\":     &FormElement{},\n\t\"shadowInactive\":    &FormElement{},\n\t\"shadowExpire\":      &FormElement{},\n\t\"shadowFlag\":        &FormElement{},\n\t\"memberUid\":         &FormElement{},\n\t\"memberNisNetgroup\": &FormElement{},\n\t\"nisNetgroupTriple\": &FormElement{\n\t\tDescription: \"Netgroup triple\",\n\t},\n\t\"ipServicePort\":     &FormElement{},\n\t\"ipServiceProtocol\": &FormElement{},\n\t\"ipProtocolNumber\":  &FormElement{},\n\t\"oncRpcNumber\":      &FormElement{},\n\t\"ipHostNumber\": &FormElement{\n\t\tDescription: \"IP address\",\n\t},\n\t\"ipNetworkNumber\": &FormElement{\n\t\tDescription: \"IP network\",\n\t},\n\t\"ipNetmaskNumber\": &FormElement{\n\t\tDescription: \"IP netmask\",\n\t},\n\t\"macAddress\": &FormElement{\n\t\tDescription: \"MAC address\",\n\t},\n\t\"bootParameter\": &FormElement{\n\t\tDescription: \"Rpc.bootparamd parameter\",\n\t},\n\t\"bootFile\": &FormElement{\n\t\tDescription: \"Boot image name\",\n\t},\n\t\"nisMapName\":  &FormElement{},\n\t\"nisMapEntry\": &FormElement{},\n\t//////////////////////////\n\t// SCHEMA: openldap.schema\n\t//////////////////////////\n\t// SCHEMA: pmi.schema\n\t\"role\": &FormElement{\n\t\tDescription: \"X.509 Role attribute, use ;binary\",\n\t},\n\t\"xmlPrivilegeInfo\": &FormElement{\n\t\tDescription: \"X.509 XML privilege information attribute\",\n\t},\n\t\"attributeCertificateAttribute\": &FormElement{\n\t\tDescription: \"X.509 Attribute certificate attribute, use ;binary\",\n\t},\n\t\"aACertificate\": &FormElement{\n\t\tDescription: \"X.509 AA certificate attribute, use ;binary\",\n\t},\n\t\"attributeDescriptorCertificate\": &FormElement{\n\t\tDescription: \"X.509 Attribute descriptor certificate attribute, use ;binary\",\n\t},\n\t\"attributeCertificateRevocationList\": &FormElement{\n\t\tDescription: \"X.509 Attribute certificate revocation list attribute, use ;binary\",\n\t},\n\t\"attributeAuthorityRevocationList\": &FormElement{\n\t\tDescription: \"X.509 AA certificate revocation list attribute, use ;binary\",\n\t},\n\t\"delegationPath\": &FormElement{\n\t\tDescription: \"X.509 Delegation path attribute, use ;binary\",\n\t},\n\t\"privPolicy\": &FormElement{\n\t\tDescription: \"X.509 Privilege policy attribute, use ;binary\",\n\t},\n\t\"protPrivPolicy\": &FormElement{\n\t\tDescription: \"X.509 Protected privilege policy attribute, use ;binary\",\n\t},\n\t\"xmlPrivPolicy\": &FormElement{\n\t\tDescription: \"X.509 XML Protected privilege policy attribute\",\n\t},\n\t//////////////////////////\n\t// SCHEMA: ppolicy.schema\n\t\"pwdAttribute\": &FormElement{\n\t\tDescription: \"Name of the attribute to which the password policy is applied. For example, the password policy may be applied to the userPassword attribute\",\n\t},\n\t\"pwdMinAge\": &FormElement{\n\t\tDescription: \"Number of seconds that must elapse between modifications to the password. If this attribute is not present, 0 seconds is assumed.\",\n\t},\n\t\"pwdMaxAge\": &FormElement{\n\t\tDescription: \"Number of seconds after which a modified password will expire. If this attribute is not present, or if the value is 0 the password does not expire. If not 0, the value must be greater than or equal to the value of the pwdMinAge.\",\n\t},\n\t\"pwdInHistory\": &FormElement{\n\t\tDescription: \"Maximum number of used passwords stored in the pwdHistory attribute. If this attribute is not present, or if the value is 0, used passwords are not stored in the pwdHistory attribute and thus may be reused.\",\n\t},\n\t\"pwdCheckQuality\": &FormElement{\n\t\tDescription: \"Indicates how the password quality will be verified while being modified or added\",\n\t},\n\t\"pwdMinLength\": &FormElement{\n\t\tDescription: \"When quality checking is enabled, this attribute holds the minimum number of characters that must be used in a password. If this attribute is not present, no minimum password length will be enforced. If the server is unable to check the length (due to a hashed password or otherwise), the server will, depending on the value of the pwdCheckQuality attribute, either accept the password without checking it ('0' or '1') or refuse it ('2').\",\n\t},\n\t\"pwdExpireWarning\": &FormElement{\n\t\tDescription: \"specifies the maximum number of seconds before a password is due to expire that expiration warning messages will be returned to an authenticating user. If this attribute is not present, or if the value is 0 no warnings will be returned. If not 0, the value must be smaller than the value of the pwdMaxAge attribute.\",\n\t},\n\t\"pwdGraceAuthNLimit\": &FormElement{\n\t\tDescription: \"This attribute specifies the number of times an expired password can be used to authenticate. If this attribute is not present or if the value is 0, authentication will fail.\",\n\t},\n\t\"pwdLockout\": &FormElement{\n\t\tDescription: \"This attribute indicates, when its value is \\\"TRUE\\\", that the password may not be used to authenticate after a specified number of consecutive failed bind attempts. The maximum number of consecutive failed bind attempts is specified in pwdMaxFailure. If this attribute is not present, or if the value is \\\"FALSE\\\", the password may be used to authenticate when the number of failed bind attempts has been reached.\",\n\t},\n\t\"pwdLockoutDuration\": &FormElement{\n\t\tDescription: \"This attribute holds the number of seconds that the password cannot be used to authenticate due to too many failed bind attempts. If this attribute is not present, or if the value is 0 the password cannot be used to authenticate until reset by a password administrator.\",\n\t},\n\t\"pwdMaxFailure\": &FormElement{\n\t\tDescription: \"This attribute specifies the number of consecutive failed bind attempts after which the password may not be used to authenticate. If this attribute is not present, or if the value is 0, this policy is not checked, and the value of pwdLockout will be ignored.\",\n\t},\n\t\"pwdFailureCountInterval\": &FormElement{\n\t\tDescription: \"This attribute holds the number of seconds after which the password failures are purged from the failure counter, even though no successful authentication occurred. If this attribute is not present, or if its value is 0, the failure counter is only reset by a successful authentication.\",\n\t},\n\t\"pwdMustChange\": &FormElement{\n\t\tDescription: \"This attribute specifies with a value of \\\"TRUE\\\" that users must change their passwords when they first bind to the directory after a password is set or reset by a password administrator. If this attribute is not present, or if the value is \\\"FALSE\\\", users are not required to change their password upon binding after the password administrator sets or resets the password. This attribute is not set due to any actions specified by this document, it is typically set by a password administrator after resetting a user's password.\",\n\t},\n\t\"pwdAllowUserChange\": &FormElement{\n\t\tDescription: \"This attribute indicates whether users can change their own passwords, although the change operation is still subject to access control. If this attribute is not present, a value of \\\"TRUE\\\" is assumed. This attribute is intended to be used in the absence of an access control mechanism.\",\n\t},\n\t\"pwdSafeModify\": &FormElement{\n\t\tDescription: \"This attribute specifies whether or not the existing password must be sent along with the new password when being changed. If this attribute is not present, a \\\"FALSE\\\" value is assumed.\",\n\t},\n\t\"pwdMaxRecordedFailure\": &FormElement{\n\t\tDescription: \"This attribute specifies the maximum number of consecutive failed bind attempts to record. If this attribute is not present, or if the value is 0, it defaults to the value of pwdMaxFailure. If that value is also 0, this value defaults to 5.\",\n\t},\n\t\"pwdCheckModule\": &FormElement{\n\t\tDescription: \"Loadable module that instantiates check_password() function. This attribute names a user-defined loadable module that provides a check_password() function. If pwdCheckQuality is set to '1' or '2' this function will be called after all of the internal password quality checks have been passed. The function has this prototype: int check_password( char *password, char **errormessage, void *arg ) The function should return LDAP_SUCCESS for a valid password.\",\n\t},\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_local/index.go",
    "content": "package plg_backend_local\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"golang.org/x/crypto/bcrypt\"\n\t\"io\"\n\t\"os\"\n\t\"os/user\"\n)\n\nfunc init() {\n\tBackend.Register(\"local\", &Local{})\n}\n\ntype Local struct{}\n\nfunc (this Local) Init(params map[string]string, app *App) (IBackend, error) {\n\tbackend := &Local{}\n\tif params[\"password\"] == Config.Get(\"general.secret_key\").String() {\n\t\treturn backend, nil\n\t} else if err := bcrypt.CompareHashAndPassword(\n\t\t[]byte(Config.Get(\"auth.admin\").String()),\n\t\t[]byte(params[\"password\"]),\n\t); err == nil {\n\t\treturn backend, nil\n\t}\n\treturn nil, ErrAuthenticationFailed\n}\n\nfunc (this Local) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"local\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"password\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"Admin Password\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Path\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Local) Home() (string, error) {\n\tif home, err := os.UserHomeDir(); err == nil {\n\t\treturn ensurePath(home), nil\n\t}\n\tif currentUser, err := user.Current(); err == nil && currentUser.HomeDir != \"\" {\n\t\treturn ensurePath(currentUser.HomeDir), nil\n\t}\n\treturn \"/\", nil\n}\n\nfunc ensurePath(path string) string {\n\tif _, err := os.Stat(path); err != nil {\n\t\treturn \"/\"\n\t}\n\treturn path\n}\n\nfunc (this Local) Ls(path string) ([]os.FileInfo, error) {\n\tf, err := SafeOsOpenFile(path, os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfiles, err := f.Readdir(-1)\n\tif err != nil {\n\t\tf.Close()\n\t\treturn nil, err\n\t}\n\treturn files, f.Close()\n}\n\nfunc (this Local) Stat(path string) (os.FileInfo, error) {\n\tf, err := SafeOsOpenFile(path, os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfinfo, err := f.Stat()\n\tif err != nil {\n\t\tf.Close()\n\t\treturn nil, err\n\t}\n\treturn finfo, f.Close()\n}\n\nfunc (this Local) Cat(path string) (io.ReadCloser, error) {\n\tf, err := SafeOsOpenFile(path, os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfs, err := f.Stat()\n\tif err != nil {\n\t\tf.Close()\n\t\treturn nil, err\n\t} else if fs.IsDir() {\n\t\tf.Close()\n\t\treturn nil, ErrNotFound\n\t}\n\treturn f, nil\n}\n\nfunc (this Local) Mkdir(path string) error {\n\treturn SafeOsMkdir(path, 0755)\n}\n\nfunc (this Local) Rm(path string) error {\n\treturn SafeOsRemoveAll(path)\n}\n\nfunc (this Local) Mv(from, to string) error {\n\treturn SafeOsRename(from, to)\n}\n\nfunc (this Local) Save(path string, content io.Reader) error {\n\tf, err := SafeOsOpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err = io.Copy(f, content); err != nil {\n\t\tf.Close()\n\t\treturn err\n\t}\n\treturn f.Close()\n}\n\nfunc (this Local) Touch(path string) error {\n\tf, err := SafeOsOpenFile(path, os.O_WRONLY|os.O_CREATE, os.ModePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err = f.Write([]byte(\"\")); err != nil {\n\t\tf.Close()\n\t\treturn err\n\t}\n\treturn f.Close()\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_mysql/index.go",
    "content": "package plg_backend_mysql\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t_ \"github.com/go-sql-driver/mysql\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"os\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype Mysql struct {\n\tparams map[string]string\n\tdb     *sql.DB\n}\n\nfunc init() {\n\tBackend.Register(\"mysql\", Mysql{})\n}\n\nfunc (this Mysql) Init(params map[string]string, app *App) (IBackend, error) {\n\tif params[\"host\"] == \"\" {\n\t\tparams[\"host\"] = \"127.0.0.1\"\n\t}\n\tif params[\"port\"] == \"\" {\n\t\tparams[\"port\"] = \"3306\"\n\t}\n\n\tdb, err := sql.Open(\n\t\t\"mysql\",\n\t\tfmt.Sprintf(\n\t\t\t\"%s:%s@tcp(%s:%s)/\",\n\t\t\tparams[\"username\"],\n\t\t\tparams[\"password\"],\n\t\t\tparams[\"host\"],\n\t\t\tparams[\"port\"],\n\t\t),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn Mysql{\n\t\tparams: params,\n\t\tdb:     db,\n\t}, nil\n}\n\nfunc (this Mysql) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"mysql\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"host\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Host\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"username\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Username\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"password\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"Password\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"port\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"Port\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Mysql) Ls(path string) ([]os.FileInfo, error) {\n\tdefer this.db.Close()\n\tlocation, err := NewDBLocation(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfiles := make([]os.FileInfo, 0)\n\n\tif location.db == \"\" { // first level folder = a list all the available databases\n\t\trows, err := this.db.Query(\"SELECT s.schema_name, t.update_time, t.create_time FROM information_schema.SCHEMATA as s LEFT JOIN ( SELECT table_schema, MAX(update_time) as update_time, MAX(create_time) as create_time FROM information_schema.tables GROUP BY table_schema ) as t ON s.schema_name = t.table_schema ORDER BY schema_name\")\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor rows.Next() {\n\t\t\tvar name string\n\t\t\tvar create string\n\t\t\tvar rcreate sql.RawBytes\n\t\t\tvar update string\n\t\t\tvar rupdate sql.RawBytes\n\n\t\t\tif err := rows.Scan(&name, &rcreate, &rupdate); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tcreate = string(rcreate)\n\t\t\tupdate = string(rupdate)\n\n\t\t\tfiles = append(files, File{\n\t\t\t\tFName: name,\n\t\t\t\tFType: \"directory\",\n\t\t\t\tFTime: func() int64 {\n\t\t\t\t\tvar t time.Time\n\t\t\t\t\tvar err error\n\t\t\t\t\tif create == \"\" && update == \"\" {\n\t\t\t\t\t\treturn 0\n\t\t\t\t\t} else if update == \"\" {\n\t\t\t\t\t\tif t, err = time.Parse(\"2006-01-02 15:04:05\", create); err != nil {\n\t\t\t\t\t\t\treturn 0\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn t.Unix()\n\t\t\t\t\t}\n\t\t\t\t\tif t, err = time.Parse(\"2006-01-02 15:04:05\", update); err != nil {\n\t\t\t\t\t\treturn 0\n\t\t\t\t\t}\n\t\t\t\t\treturn t.Unix()\n\t\t\t\t}(),\n\t\t\t})\n\t\t}\n\t\treturn files, nil\n\t} else if location.table == \"\" { // second level folder = a list of all the tables available in a database\n\t\trows, err := this.db.Query(\"SELECT table_name, create_time, update_time FROM information_schema.tables WHERE table_schema = ?\", location.db)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor rows.Next() {\n\t\t\tvar name string\n\t\t\tvar create string\n\t\t\tvar rcreate sql.RawBytes\n\t\t\tvar update string\n\t\t\tvar rupdate sql.RawBytes\n\n\t\t\tif err := rows.Scan(&name, &rcreate, &rupdate); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tcreate = string(rcreate)\n\t\t\tupdate = string(rupdate)\n\n\t\t\tfiles = append(files, File{\n\t\t\t\tFName: name,\n\t\t\t\tFType: \"directory\",\n\t\t\t\tFTime: func() int64 {\n\t\t\t\t\tvar t time.Time\n\t\t\t\t\tvar err error\n\t\t\t\t\tif create == \"\" && update == \"\" {\n\t\t\t\t\t\treturn 0\n\t\t\t\t\t} else if update == \"\" {\n\t\t\t\t\t\tif t, err = time.Parse(\"2006-01-02 15:04:05\", create); err != nil {\n\t\t\t\t\t\t\treturn 0\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn t.Unix()\n\t\t\t\t\t}\n\t\t\t\t\tif t, err = time.Parse(\"2006-01-02 15:04:05\", update); err != nil {\n\t\t\t\t\t\treturn 0\n\t\t\t\t\t}\n\t\t\t\t\treturn t.Unix()\n\t\t\t\t}(),\n\t\t\t})\n\t\t}\n\t\treturn files, nil\n\t} else if location.row == \"\" { // third level folder = a list of all the available rows within the selected table\n\t\tsqlFields, err := FindQuerySelection(this.db, location)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\textractSingleName := func(s QuerySelection) string {\n\t\t\treturn s.Name\n\t\t}\n\t\textractName := func(s []QuerySelection) []string {\n\t\t\tt := make([]string, 0, len(s))\n\t\t\tfor i := range s {\n\t\t\t\tt = append(t, extractSingleName(s[i]))\n\t\t\t}\n\t\t\treturn t\n\t\t}\n\t\textractNamePlus := func(s []QuerySelection) []string {\n\t\t\tt := make([]string, 0, len(s))\n\t\t\tfor i := range s {\n\t\t\t\tt = append(t, \"IFNULL(\"+extractSingleName(s[i])+\", '')\")\n\t\t\t}\n\t\t\treturn t\n\t\t}\n\n\t\trows, err := this.db.Query(fmt.Sprintf(\n\t\t\t\"SELECT CONCAT(%s) as filename %sFROM %s.%s %s LIMIT 500000\",\n\t\t\tfunc() string {\n\t\t\t\tq := strings.Join(extractNamePlus(sqlFields.Select), \", ' - ', \")\n\t\t\t\tif len(sqlFields.Esthetics) != 0 {\n\t\t\t\t\tq += \", ' - ', \" + strings.Join(extractNamePlus(sqlFields.Esthetics), \", ' ', \")\n\t\t\t\t}\n\t\t\t\treturn q\n\t\t\t}(),\n\t\t\tfunc() string {\n\t\t\t\tif extractSingleName(sqlFields.Date) != \"\" {\n\t\t\t\t\treturn \", \" + extractSingleName(sqlFields.Date) + \" as date \"\n\t\t\t\t}\n\t\t\t\treturn \"\"\n\t\t\t}(),\n\t\t\tlocation.db,\n\t\t\tlocation.table,\n\t\t\tfunc() string {\n\t\t\t\tif len(sqlFields.Order) != 0 {\n\t\t\t\t\treturn \"ORDER BY \" + strings.Join(extractName(sqlFields.Order), \", \") + \" DESC \"\n\t\t\t\t}\n\t\t\t\treturn \"\"\n\t\t\t}(),\n\t\t))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor rows.Next() {\n\t\t\tvar name_raw sql.RawBytes\n\t\t\tvar date sql.RawBytes\n\t\t\tif extractSingleName(sqlFields.Date) == \"\" {\n\t\t\t\tif err := rows.Scan(&name_raw); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err := rows.Scan(&name_raw, &date); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\tfiles = append(files, File{\n\t\t\t\tFName: string(name_raw) + \".form\",\n\t\t\t\tFType: \"file\",\n\t\t\t\tFSize: -1,\n\t\t\t\tFTime: func() int64 {\n\t\t\t\t\tt, err := time.Parse(\"2006-01-02\", fmt.Sprintf(\"%s\", date))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn 0\n\t\t\t\t\t}\n\t\t\t\t\treturn t.Unix()\n\t\t\t\t}(),\n\t\t\t})\n\t\t}\n\t\treturn files, nil\n\t}\n\treturn nil, ErrNotValid\n}\n\nfunc (this Mysql) Stat(path string) (os.FileInfo, error) {\n\treturn nil, ErrNotImplemented\n}\n\nfunc (this Mysql) Cat(path string) (io.ReadCloser, error) {\n\tdefer this.db.Close()\n\tlocation, err := NewDBLocation(path)\n\tif err != nil {\n\t\treturn nil, err\n\t} else if location.db == \"\" || location.table == \"\" || location.row == \"\" {\n\t\treturn nil, ErrNotValid\n\t}\n\n\t// STEP 1: Perform the database query\n\tfields, err := FindQuerySelection(this.db, location)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\twhereSQL, whereParams := sqlWhereClause(fields, location)\n\tquery := fmt.Sprintf(\n\t\t\"SELECT * FROM %s.%s WHERE %s\",\n\t\tlocation.db,\n\t\tlocation.table,\n\t\twhereSQL,\n\t)\n\n\trows, err := this.db.Query(query, whereParams...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcolumnsName, err := rows.Columns()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// STEP 2: find potential foreign key on given results\n\t// those will be shown as a list of possible choice\n\tcolumnsChoice, err := FindForeignKeysChoices(this.db, location)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// STEP 3: Encode the result of the query into a form object\n\tvar forms []FormElement = []FormElement{}\n\n\tdummy := make([]interface{}, len(columnsName))\n\tcolumnPointers := make([]interface{}, len(columnsName))\n\tfor i := range columnsName {\n\t\tcolumnPointers[i] = &dummy[i]\n\t}\n\tfor rows.Next() {\n\t\tif err := rows.Scan(columnPointers...); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tbreak\n\t}\n\tfor i := range columnsName {\n\t\tif pval, ok := columnPointers[i].(*interface{}); ok {\n\t\t\tif pval == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tel := FormElement{\n\t\t\t\tName: columnsName[i],\n\t\t\t\tType: \"text\",\n\t\t\t}\n\n\t\t\tswitch fields.All[columnsName[i]].Type {\n\t\t\tcase \"int\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%d\", *pval)\n\t\t\t\tel.Type = \"number\"\n\t\t\tcase \"integer\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%d\", *pval)\n\t\t\t\tel.Type = \"number\"\n\t\t\tcase \"decimal\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%d\", *pval)\n\t\t\t\tel.Type = \"number\"\n\t\t\tcase \"dec\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%d\", *pval)\n\t\t\t\tel.Type = \"number\"\n\t\t\tcase \"float\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%d\", *pval)\n\t\t\t\tel.Type = \"number\"\n\t\t\tcase \"double\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%d\", *pval)\n\t\t\t\tel.Type = \"number\"\n\t\t\tcase \"tinyint\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%d\", *pval)\n\t\t\t\tel.Type = \"number\"\n\t\t\tcase \"smallint\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%d\", *pval)\n\t\t\t\tel.Type = \"number\"\n\t\t\tcase \"mediumint\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%d\", *pval)\n\t\t\t\tel.Type = \"number\"\n\t\t\tcase \"bigint\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%d\", *pval)\n\t\t\t\tel.Type = \"number\"\n\t\t\tcase \"enum\":\n\t\t\t\tel.Type = \"select\"\n\t\t\t\treg := regexp.MustCompile(`^'(.*)'$`)\n\t\t\t\tel.Opts = func() []string {\n\t\t\t\t\tr := strings.Split(strings.TrimSuffix(strings.TrimPrefix(fields.All[columnsName[i]].RawType, \"enum(\"), \")\"), \",\")\n\t\t\t\t\tfor i := 0; i < len(r); i++ {\n\t\t\t\t\t\tr[i] = reg.ReplaceAllString(r[i], `$1`)\n\t\t\t\t\t}\n\t\t\t\t\treturn r\n\t\t\t\t}()\n\t\t\t\tel.Value = fmt.Sprintf(\"%s\", *pval)\n\t\t\tcase \"datetime\":\n\t\t\t\tel.Value = strings.Replace(fmt.Sprintf(\"%s\", *pval), \" \", \"T\", 1)\n\t\t\t\tel.Type = \"datetime\"\n\t\t\tcase \"timestamp\":\n\t\t\t\tel.Value = strings.Replace(fmt.Sprintf(\"%s\", *pval), \" \", \"T\", 1)\n\t\t\t\tel.Type = \"datetime\"\n\t\t\tcase \"date\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%s\", *pval)\n\t\t\t\tel.Type = \"date\"\n\t\t\tcase \"text\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%s\", *pval)\n\t\t\t\tel.Type = \"long_text\"\n\t\t\tcase \"longblob\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%s\", *pval)\n\t\t\t\tel.Type = \"file\"\n\t\t\tcase \"mediumblob\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%s\", *pval)\n\t\t\t\tel.Type = \"file\"\n\t\t\tcase \"tinnyblob\":\n\t\t\t\tel.Value = fmt.Sprintf(\"%s\", *pval)\n\t\t\t\tel.Type = \"file\"\n\t\t\tdefault:\n\t\t\t\tel.Value = fmt.Sprintf(\"%s\", *pval)\n\t\t\t}\n\t\t\tif *pval == nil {\n\t\t\t\tel.Value = \"\"\n\t\t\t}\n\n\t\t\tif choices, ok := columnsChoice[columnsName[i]]; ok {\n\t\t\t\tel.Type = \"text\"\n\t\t\t\tel.MultiValue = false\n\t\t\t\tel.Datalist = choices\n\n\t\t\t\tif l, err := FindWhoOwns(this.db, DBLocation{location.db, location.table, columnsName[i]}); err == nil {\n\t\t\t\t\tel.Description = fmt.Sprintf(\n\t\t\t\t\t\t\"Relates to object in %s\",\n\t\t\t\t\t\tgenerateLink(this.params[\"path\"], l, el.Value),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} else if key := fields.All[columnsName[i]].Key; key == \"PRI\" {\n\t\t\t\tlocations, err := FindWhoIsUsing(this.db, location)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\tif len(locations) > 0 {\n\t\t\t\t\ttext := []string{}\n\t\t\t\t\tfor i := 0; i < len(locations); i++ {\n\t\t\t\t\t\ttext = append(\n\t\t\t\t\t\t\ttext,\n\t\t\t\t\t\t\tfmt.Sprintf(\n\t\t\t\t\t\t\t\t\"%s (%d)\",\n\t\t\t\t\t\t\t\tgenerateLink(this.params[\"path\"], DBLocation{locations[i].db, locations[i].table, locations[i].row}, el.Value),\n\t\t\t\t\t\t\t\tFindHowManyOccurenceOfaValue(this.db, locations[i], el.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\tel.Description = \"Used in \" + strings.Join(text, \", \")\n\t\t\t\t}\n\t\t\t}\n\t\t\tforms = append(forms, el)\n\t\t}\n\t}\n\n\t// STEP 3: Send the form back to the user\n\tb, err := Form{Elmnts: forms}.MarshalJSON()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewReadCloserFromBytes(b), nil\n}\n\nfunc (this Mysql) Mkdir(path string) error {\n\tdefer this.db.Close()\n\tlocation, err := NewDBLocation(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif location.db != \"\" && location.table == \"\" && location.row == \"\" {\n\t\t_, err = this.db.Exec(fmt.Sprintf(\"CREATE DATABASE %s\", strings.TrimPrefix(location.db, \"CREATE DATABASE \")))\n\t\treturn err\n\t}\n\treturn ErrNotAllowed\n}\n\nfunc (this Mysql) Rm(path string) error {\n\tdefer this.db.Close()\n\tlocation, err := NewDBLocation(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif location.db == \"\" {\n\t\treturn ErrNotValid\n\t} else if location.table == \"\" {\n\t\t_, err := this.db.Exec(fmt.Sprintf(\"DROP DATABASE %s\", location.db))\n\t\treturn err\n\t} else if location.row == \"\" {\n\t\t_, err := this.db.Exec(fmt.Sprintf(\"DROP TABLE %s.%s\", location.db, location.table))\n\t\treturn err\n\t}\n\tfields, err := FindQuerySelection(this.db, location)\n\tif err != nil {\n\t\treturn err\n\t}\n\twhereSQL, whereParams := sqlWhereClause(fields, location)\n\tquery := fmt.Sprintf(\n\t\t\"DELETE FROM %s.%s WHERE %s\",\n\t\tlocation.db,\n\t\tlocation.table,\n\t\twhereSQL,\n\t)\n\t_, err = this.db.Exec(query, whereParams...)\n\treturn err\n}\n\nfunc (this Mysql) Mv(from string, to string) error {\n\tdefer this.db.Close()\n\treturn ErrNotValid\n}\n\nfunc (this Mysql) Touch(path string) error {\n\tdefer this.db.Close()\n\tlocation, err := NewDBLocation(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif location.db == \"\" {\n\t\treturn ErrNotValid\n\t} else if location.table == \"\" {\n\t\treturn ErrNotValid\n\t} else if location.row == \"\" {\n\t\treturn ErrNotValid\n\t}\n\n\tfields, err := FindQuerySelection(this.db, location)\n\tif err != nil {\n\t\treturn err\n\t}\n\tquery := fmt.Sprintf(\n\t\t\"INSERT INTO %s.%s (%s) VALUES(%s)\",\n\t\tlocation.db,\n\t\tlocation.table,\n\t\tfunc() string {\n\t\t\tvalues := []string{}\n\t\t\tfor i := range fields.Select {\n\t\t\t\tvalues = append(values, fields.Select[i].Name)\n\t\t\t}\n\t\t\treturn strings.Join(values, \",\")\n\t\t}(),\n\t\tfunc() string {\n\t\t\tvalues := make([]string, len(fields.Select))\n\t\t\tfor i := range values {\n\t\t\t\tvalues[i] = \"?\"\n\t\t\t}\n\t\t\treturn strings.Join(values, \",\")\n\t\t}(),\n\t)\n\tqueryValues := func() []interface{} {\n\t\tvaluesOfQuery := make([]interface{}, 0, len(fields.Select))\n\t\tvaluesFromInput := strings.Split(location.row, \" - \")\n\t\tfor i := range fields.Select {\n\t\t\tif i < len(valuesFromInput) {\n\t\t\t\tvaluesOfQuery = append(valuesOfQuery, valuesFromInput[i])\n\t\t\t} else {\n\t\t\t\tif t := fields.Select[i].Type; t == \"int\" || t == \"integer\" || t == \"dec\" || t == \"double\" || t == \"float\" || t == \"smallint\" || t == \"mediumint\" || t == \"bigint\" {\n\t\t\t\t\tvaluesOfQuery = append(valuesOfQuery, 0)\n\t\t\t\t} else if t == \"datetime\" || t == \"date\" || t == \"timestamp\" {\n\t\t\t\t\tvaluesOfQuery = append(valuesOfQuery, time.Now())\n\t\t\t\t} else {\n\t\t\t\t\tvaluesOfQuery = append(valuesOfQuery, \"\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn valuesOfQuery\n\t}()\n\t_, err = this.db.Exec(query, queryValues...)\n\treturn err\n}\n\ntype SqlKeyParams struct {\n\tKey   string\n\tValue interface{}\n}\n\nfunc (this Mysql) Save(path string, file io.Reader) error {\n\tdefer this.db.Close()\n\tlocation, err := NewDBLocation(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif location.db == \"\" || location.table == \"\" || location.row == \"\" {\n\t\treturn ErrNotValid\n\t}\n\tsqlFields, err := FindQuerySelection(this.db, location)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar data map[string]FormElement\n\tif err := json.NewDecoder(file).Decode(&data); err != nil {\n\t\treturn err\n\t}\n\tvar d []SqlKeyParams = make([]SqlKeyParams, 0)\n\tfor key, value := range data {\n\t\td = append(d, SqlKeyParams{key, value.Value})\n\t}\n\n\twhereSQL, whereParams := sqlWhereClause(sqlFields, location)\n\tsetParams := make([]interface{}, 0, len(data))\n\tfor _, v := range d {\n\t\tsetParams = append(setParams, v.Value)\n\t}\n\n\t_, err = this.db.Exec(fmt.Sprintf(\n\t\t\"UPDATE %s.%s SET %s WHERE %s\",\n\t\tlocation.db,\n\t\tlocation.table,\n\t\tfunc() string {\n\t\t\ta := make([]string, 0, len(data))\n\t\t\tfor _, v := range d {\n\t\t\t\ta = append(a, fmt.Sprintf(\"%s = ?\", v.Key))\n\t\t\t}\n\t\t\treturn strings.Join(a, \", \")\n\t\t}(),\n\t\twhereSQL,\n\t), append(setParams, whereParams...)...)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (this Mysql) Meta(path string) Metadata {\n\tlocation, _ := NewDBLocation(path)\n\treturn Metadata{\n\t\tCanCreateDirectory: func(l DBLocation) *bool {\n\t\t\tif l.db == \"\" && l.table == \"\" && l.row == \"\" {\n\t\t\t\treturn NewBool(true)\n\t\t\t}\n\t\t\treturn NewBool(false)\n\t\t}(location),\n\t\tCanCreateFile: func(l DBLocation) *bool {\n\t\t\tif l.table == \"\" || l.db == \"\" {\n\t\t\t\treturn NewBool(false)\n\t\t\t}\n\t\t\treturn NewBool(true)\n\t\t}(location),\n\t\tCanRename:       NewBool(false),\n\t\tCanMove:         NewBool(false),\n\t\tRefreshOnCreate: NewBool(true),\n\t\tHideExtension:   NewBool(true),\n\t}\n}\n\ntype DBLocation struct {\n\tdb    string\n\ttable string\n\trow   string\n}\n\nfunc NewDBLocation(path string) (DBLocation, error) {\n\tvar location DBLocation\n\n\tp := strings.Split(strings.Trim(path, \"/\"), \"/\")\n\tisValid := func(str string) bool { // https://dev.mysql.com/doc/refman/8.0/en/identifiers.html\n\t\treturn regexp.MustCompile(`^[0-9,a-z,A-Z$_]*$`).MatchString(str)\n\t}\n\n\tif lPath := len(p); lPath == 0 {\n\t\treturn DBLocation{}, nil\n\t} else if lPath == 1 {\n\t\tlocation = DBLocation{\n\t\t\tdb: p[0],\n\t\t}\n\t\tif isValid(p[0]) == false {\n\t\t\treturn location, ErrNotValid\n\t\t}\n\t\treturn location, nil\n\t} else if lPath == 2 {\n\t\tlocation = DBLocation{\n\t\t\tdb:    p[0],\n\t\t\ttable: p[1],\n\t\t}\n\t\tif isValid(p[0]) == false || isValid(p[1]) == false {\n\t\t\treturn location, ErrNotValid\n\t\t}\n\t\treturn location, nil\n\t} else if lPath == 3 {\n\t\tlocation = DBLocation{\n\t\t\tdb:    p[0],\n\t\t\ttable: p[1],\n\t\t\trow:   strings.TrimSuffix(p[2], \".form\"),\n\t\t}\n\t\tif isValid(p[0]) == false || isValid(p[1]) == false {\n\t\t\treturn location, ErrNotValid\n\t\t}\n\t\treturn location, nil\n\t}\n\treturn DBLocation{}, ErrNotValid\n}\n\ntype QuerySelection struct {\n\tName     string\n\tType     string\n\tRawType  string\n\tSize     int\n\tKey      string\n\tNullable bool\n}\n\ntype SqlFields struct {\n\tOrder     []QuerySelection\n\tSelect    []QuerySelection\n\tEsthetics []QuerySelection\n\tDate      QuerySelection\n\tAll       map[string]QuerySelection\n}\n\nfunc sqlWhereClause(s SqlFields, location DBLocation) (string, []interface{}) {\n\twhere := []string{}\n\tqueryParams := make([]interface{}, 0)\n\n\tfor i := range s.Select {\n\t\twhere = append(where, fmt.Sprintf(\"%s = ?\", s.Select[i].Name))\n\t}\n\tfor i, value := range strings.Split(location.row, \" - \") {\n\t\tif i < len(s.Select) {\n\t\t\tqueryParams = append(queryParams, value)\n\t\t}\n\t}\n\treturn strings.Join(where, \" AND \"), queryParams\n}\n\nfunc FindQuerySelection(db *sql.DB, location DBLocation) (SqlFields, error) {\n\tvar queryCandidates []QuerySelection = make([]QuerySelection, 0)\n\tvar fields SqlFields = SqlFields{\n\t\tOrder:     make([]QuerySelection, 0),\n\t\tSelect:    make([]QuerySelection, 0),\n\t\tEsthetics: make([]QuerySelection, 0),\n\t\tAll:       make(map[string]QuerySelection, 0),\n\t}\n\tif location.db == \"\" || location.table == \"\" {\n\t\treturn fields, ErrNotValid\n\t}\n\n\t// STEP 1: extract possible values from the available schema\n\trows, err := db.Query(\"SELECT IS_NULLABLE, DATA_TYPE, COLUMN_TYPE, COLUMN_NAME, COLUMN_KEY FROM information_schema.COLUMNS WHERE table_schema = ? && table_name = ?\", location.db, location.table)\n\tif err != nil {\n\t\treturn fields, err\n\t}\n\tfor rows.Next() {\n\t\tvar data_type string\n\t\tvar column_type string\n\t\tvar column_name string\n\t\tvar column_key string\n\t\tvar is_nullable string\n\n\t\tif err := rows.Scan(&is_nullable, &data_type, &column_type, &column_name, &column_key); err != nil {\n\t\t\treturn fields, err\n\t\t}\n\t\tq := QuerySelection{\n\t\t\tName: column_name,\n\t\t\tType: data_type,\n\t\t\tSize: func() int {\n\t\t\t\tif strings.Contains(column_type, \"(\") && strings.Contains(column_type, \")\") {\n\t\t\t\t\tc := regexp.MustCompile(\"[0-9]+\").FindAllString(column_type, -1)\n\t\t\t\t\tif len(c) == 1 {\n\t\t\t\t\t\tif i, err := strconv.Atoi(c[0]); err == nil {\n\t\t\t\t\t\t\treturn i\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn 0\n\t\t\t}(),\n\t\t\tNullable: func() bool {\n\t\t\t\tif is_nullable == \"YES\" {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t}(),\n\t\t\tRawType: column_type,\n\t\t\tKey:     column_key,\n\t\t}\n\t\tfields.All[column_name] = q\n\t\tqueryCandidates = append(queryCandidates, q)\n\t}\n\tif len(queryCandidates) == 0 {\n\t\treturn fields, ErrNotValid\n\t}\n\n\t// STEP 2: filter out unwanted fields from the schema\n\tfor i := 0; i < len(queryCandidates); i++ {\n\t\tif queryCandidates[i].Key == \"PRI\" || queryCandidates[i].Key == \"UNI\" {\n\t\t\tfields.Select = append(fields.Select, queryCandidates[i])\n\t\t\tif queryCandidates[i].Type == \"date\" {\n\t\t\t\tfields.Order = append(fields.Order, queryCandidates[i])\n\t\t\t}\n\t\t} else if queryCandidates[i].Type == \"varchar\" {\n\t\t\tfields.Esthetics = append(fields.Esthetics, queryCandidates[i])\n\t\t}\n\n\t\tif queryCandidates[i].Type == \"date\" && queryCandidates[i].Nullable == false {\n\t\t\tfields.Date = queryCandidates[i]\n\t\t}\n\t}\n\n\t// STEP 3: Ensure the current selection is workable\n\tif len(fields.Select) == 0 {\n\t\t// worst case scenario with no defined keys in the schema, we populate the selection with:\n\t\t// - strategy 1: finding a field that can do the job (essentially COUNT(*) == DISTINCT(COUNT(*)))\n\t\t// - strategy 2: the key is a set of all the available fields (worst worst case)\n\t\t// This can be fairly slow on large tables but that's the cost to pay for bad database design\n\t\tsort.SliceStable(queryCandidates, func(i, j int) bool {\n\t\t\tif queryCandidates[i].Type == \"varchar\" && queryCandidates[i].Type != queryCandidates[j].Type {\n\t\t\t\treturn true\n\t\t\t} else if queryCandidates[j].Type == \"varchar\" && queryCandidates[i].Type != queryCandidates[j].Type {\n\t\t\t\treturn false\n\t\t\t} else if queryCandidates[i].Type == \"char\" && queryCandidates[i].Type != queryCandidates[j].Type {\n\t\t\t\treturn true\n\t\t\t} else if queryCandidates[j].Type == \"char\" && queryCandidates[i].Type != queryCandidates[j].Type {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn queryCandidates[i].Size < queryCandidates[j].Size\n\t\t})\n\t\tvar size int = 0\n\t\tvar i int = 0\n\t\tfor i = range queryCandidates {\n\t\t\tquery := fmt.Sprintf(\n\t\t\t\t\"SELECT COUNT(%s), COUNT(DISTINCT(%s)) FROM %s.%s\",\n\t\t\t\tqueryCandidates[i].Name,\n\t\t\t\tqueryCandidates[i].Name,\n\t\t\t\tlocation.db,\n\t\t\t\tlocation.table,\n\t\t\t)\n\t\t\tsize += queryCandidates[i].Size\n\t\t\tvar count_all int\n\t\t\tvar count_distinct int\n\t\t\tif err := db.QueryRow(query).Scan(&count_all, &count_distinct); err != nil {\n\t\t\t\treturn fields, err\n\t\t\t}\n\t\t\tif count_all == count_distinct {\n\t\t\t\tfields.Select = append(fields.Select, queryCandidates[i])\n\t\t\t\tfields.Esthetics = func() []QuerySelection {\n\t\t\t\t\tvar i int\n\t\t\t\t\testhetics := make([]QuerySelection, 0, len(fields.Esthetics))\n\t\t\t\t\tfor i = range fields.Esthetics {\n\t\t\t\t\t\tif fields.Esthetics[i].Name != queryCandidates[i].Name {\n\t\t\t\t\t\t\testhetics = append(esthetics, queryCandidates[i])\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn esthetics\n\t\t\t\t}()\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif i == len(queryCandidates)-1 {\n\t\t\tif size > 200 {\n\t\t\t\treturn fields, NewError(\"This table doesn't have any defined keys.\", 405)\n\t\t\t}\n\t\t\tfields.Select = queryCandidates\n\t\t\tfields.Esthetics = make([]QuerySelection, 0)\n\t\t}\n\t}\n\n\t// STEP 4: organise our finding into a data structure that's usable\n\tsortQuerySelection := func(s []QuerySelection) func(i, j int) bool {\n\t\tcalculateScore := func(q QuerySelection) int {\n\t\t\tscore := 0\n\t\t\tif q.Key == \"UNI\" {\n\t\t\t\tscore = 4\n\t\t\t} else if q.Key == \"PRI\" {\n\t\t\t\tscore = 5\n\t\t\t} else {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tif lowerName := strings.ToLower(q.Name); lowerName == \"id\" || lowerName == \"gid\" || lowerName == \"uid\" {\n\t\t\t\tscore -= 2\n\t\t\t}\n\t\t\tif q.Type == \"varchar\" || q.Type == \"char\" {\n\t\t\t\tscore += 1\n\t\t\t} else if q.Type == \"date\" {\n\t\t\t\tscore -= 1\n\t\t\t}\n\t\t\treturn score\n\t\t}\n\t\treturn func(i, j int) bool {\n\t\t\treturn calculateScore(s[i]) > calculateScore(s[j])\n\t\t}\n\t}\n\tsort.SliceStable(fields.Select, sortQuerySelection(fields.Select))\n\tsort.SliceStable(fields.Order, sortQuerySelection(fields.Order))\n\tfields.Date.Name = func() string {\n\t\tif len(fields.Order) == 0 {\n\t\t\treturn fields.Date.Name\n\t\t}\n\t\treturn fields.Order[0].Name\n\t}()\n\tfields.Esthetics = func() []QuerySelection { // fields whose only value is to make our generated field look good\n\t\tvar size int = 0\n\t\tvar i int\n\t\tfor i = range fields.Select {\n\t\t\tsize += fields.Select[i].Size\n\t\t}\n\t\tfor i = range fields.Esthetics {\n\t\t\ts := fields.Esthetics[i].Size\n\t\t\tif size+s > 100 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tsize += s\n\t\t}\n\t\tif i+1 > len(fields.Esthetics) {\n\t\t\treturn fields.Esthetics\n\t\t}\n\t\treturn fields.Esthetics[:i+1]\n\t}()\n\n\treturn fields, nil\n}\n\nfunc (this Mysql) Close() error {\n\treturn this.db.Close()\n}\n\nfunc FindForeignKeysChoices(db *sql.DB, location DBLocation) (map[string][]string, error) {\n\tchoices := make(map[string][]string, 0)\n\trows, err := db.Query(\"SELECT column_name, referenced_table_name, referenced_column_name FROM information_schema.key_column_usage WHERE table_schema = ? AND table_name = ? AND referenced_column_name IS NOT NULL\", location.db, location.table)\n\tif err != nil {\n\t\treturn choices, err\n\t}\n\tfor rows.Next() {\n\t\tvar column_name string\n\t\tvar referenced_table_schema string\n\t\tvar referenced_column_name string\n\t\tif err := rows.Scan(&column_name, &referenced_table_schema, &referenced_column_name); err != nil {\n\t\t\treturn choices, err\n\t\t}\n\t\tr, err := db.Query(fmt.Sprintf(\"SELECT DISTINCT(%s) FROM %s.%s LIMIT 10000\", column_name, location.db, location.table))\n\t\tif err != nil {\n\t\t\treturn choices, err\n\t\t}\n\t\tvar res []string = make([]string, 0)\n\t\tfor r.Next() {\n\t\t\tvar value string\n\t\t\tr.Scan(&value)\n\t\t\tres = append(res, value)\n\t\t}\n\t\tchoices[column_name] = res\n\t}\n\treturn choices, nil\n}\n\nfunc FindWhoIsUsing(db *sql.DB, location DBLocation) ([]DBLocation, error) {\n\tlocations := make([]DBLocation, 0)\n\trows, err := db.Query(\"SELECT table_schema, table_name, column_name FROM information_schema.key_column_usage WHERE referenced_table_schema = ? AND referenced_table_name = ? AND column_name IS NOT NULL\", location.db, location.table)\n\tif err != nil {\n\t\treturn locations, err\n\t}\n\tfor rows.Next() {\n\t\tvar table_schema string\n\t\tvar table_name string\n\t\tvar column_name string\n\t\tif err := rows.Scan(&table_schema, &table_name, &column_name); err != nil {\n\t\t\treturn locations, err\n\t\t}\n\t\tlocations = append(locations, DBLocation{\n\t\t\tdb:    table_schema,\n\t\t\ttable: table_name,\n\t\t\trow:   column_name,\n\t\t})\n\t}\n\treturn locations, nil\n}\n\nfunc FindWhoOwns(db *sql.DB, location DBLocation) (DBLocation, error) {\n\tvar referenced_table_schema string\n\tvar referenced_table_name string\n\tvar referenced_column_name string\n\n\tif err := db.QueryRow(\n\t\tfmt.Sprintf(\"SELECT referenced_table_schema, referenced_table_name, referenced_column_name FROM information_schema.key_column_usage WHERE table_schema = ? AND table_name = ? AND column_name = ? AND referenced_column_name IS NOT NULL\"),\n\t\tlocation.db,\n\t\tlocation.table,\n\t\tlocation.row,\n\t).Scan(&referenced_table_schema, &referenced_table_name, &referenced_column_name); err != nil {\n\t\treturn DBLocation{}, err\n\t}\n\treturn DBLocation{referenced_table_schema, referenced_table_name, referenced_column_name}, nil\n}\n\nfunc FindHowManyOccurenceOfaValue(db *sql.DB, location DBLocation, value interface{}) int {\n\tvar count int\n\tif err := db.QueryRow(\n\t\tfmt.Sprintf(\"SELECT COUNT(*) FROM %s.%s WHERE %s = ?\", location.db, location.table, location.row),\n\t\tvalue,\n\t).Scan(&count); err != nil {\n\t\treturn 0\n\t}\n\treturn count\n}\n\nfunc generateLink(chroot string, l DBLocation, value interface{}) string {\n\tchrootLocation, err := NewDBLocation(chroot)\n\tif err != nil {\n\t\treturn fmt.Sprintf(\"'%s'\", l.table)\n\t}\n\n\tif chrootLocation.db == \"\" {\n\t\treturn fmt.Sprintf(\n\t\t\t\"[%s](/files/%s/%s/%s)\",\n\t\t\tl.table,\n\t\t\tl.db,\n\t\t\tl.table,\n\t\t\tfunc() string {\n\t\t\t\tif l.row == \"\" {\n\t\t\t\t\treturn \"\"\n\t\t\t\t}\n\t\t\t\treturn fmt.Sprintf(\"?q=%s%%3D%s\", l.row, value)\n\t\t\t}(),\n\t\t)\n\t} else if chrootLocation.table == \"\" {\n\t\treturn fmt.Sprintf(\n\t\t\t\"[%s](/files/%s/%s)\",\n\t\t\tl.table,\n\t\t\tl.table,\n\t\t\tfunc() string {\n\t\t\t\tif l.row == \"\" {\n\t\t\t\t\treturn \"\"\n\t\t\t\t}\n\t\t\t\treturn fmt.Sprintf(\"?q=%s%%3D%s\", l.row, value)\n\t\t\t}(),\n\t\t)\n\t} else {\n\t\treturn fmt.Sprintf(\"'%s'\", l.table)\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs/auth_helper.go",
    "content": "package plg_backend_nfs\n\nimport (\n\t\"bufio\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar (\n\tcacheForEtc   AppCache\n\tcacheForGroup AppCache\n)\n\nfunc init() {\n\tcacheForEtc = NewAppCache(120, 60)\n\tcacheForGroup = NewAppCache(120, 60)\n}\n\nfunc ExtractUserInfo(uidHint string, gidHint string, gidsHint string) (uint32, uint32, []GroupLabel) {\n\t// case 1: everything is being sent as \"uid=number, gid=number and gids=number,number,number\"\n\tif _uid, err := strconv.Atoi(uidHint); err == nil {\n\t\tvar (\n\t\t\tuid  uint32 = uint32(_uid)\n\t\t\tgid  uint32\n\t\t\tgids []GroupLabel\n\t\t)\n\t\tif _gid, err := strconv.Atoi(gidHint); err == nil {\n\t\t\tgid = uint32(_gid)\n\t\t} else {\n\t\t\tgid = uid\n\t\t}\n\t\tfor _, t := range strings.Split(gidsHint, \",\") {\n\t\t\ttmp := strings.TrimSpace(t)\n\t\t\tif gid, err := strconv.Atoi(tmp); err == nil {\n\t\t\t\tgids = append(gids, GroupLabel{uint32(gid), tmp, 0})\n\t\t\t}\n\t\t}\n\t\treturn uid, gid, gids\n\t}\n\t// case 2: auto detect everything, aka \"uid=www-data gid=www-data gids=...\" based on uid=www-data\n\tif _uid, _gid, err := extractFromEtcPasswd(uidHint); err == nil {\n\t\treturn _uid, _gid, extractFromEtcGroup(uidHint, _gid)\n\t}\n\t// case 3: base case\n\treturn 0, 0, []GroupLabel{}\n}\n\nfunc extractFromEtcPasswd(username string) (uint32, uint32, error) {\n\tif v := cacheForEtc.Get(map[string]string{\"username\": username}); v != nil {\n\t\tinCache := v.([]int)\n\t\treturn uint32(inCache[0]), uint32(inCache[1]), nil\n\t}\n\tf, err := os.OpenFile(\"/etc/passwd\", os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn 0, 0, err\n\t}\n\tdefer f.Close()\n\tlines := bufio.NewReader(f)\n\tfor {\n\t\tline, _, err := lines.ReadLine()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\ts := strings.Split(string(line), \":\")\n\t\tif len(s) != 7 {\n\t\t\tcontinue\n\t\t} else if username != s[0] {\n\t\t\tcontinue\n\t\t}\n\t\tu, err := strconv.Atoi(s[2])\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tg, err := strconv.Atoi(s[3])\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tcacheForEtc.Set(map[string]string{\"username\": username}, []int{u, g})\n\t\treturn uint32(u), uint32(g), nil\n\t}\n\treturn 0, 0, ErrNotFound\n}\n\ntype GroupLabel struct {\n\tId       uint32\n\tLabel    string\n\tPriority int\n}\n\nfunc extractFromEtcGroup(username string, primary uint32) []GroupLabel {\n\tif v := cacheForGroup.Get(map[string]string{\"username\": username}); v != nil {\n\t\treturn v.([]GroupLabel)\n\t}\n\tf, err := os.OpenFile(\"/etc/group\", os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn []GroupLabel{}\n\t}\n\tdefer f.Close()\n\tgids := []GroupLabel{}\n\tlines := bufio.NewReader(f)\n\tfor {\n\t\tline, _, err := lines.ReadLine()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\ts := strings.Split(string(line), \":\")\n\t\tif len(s) != 4 {\n\t\t\tcontinue\n\t\t}\n\t\tuserInGroup := false\n\t\tfor _, user := range strings.Split(s[3], \",\") {\n\t\t\tif user == username {\n\t\t\t\tuserInGroup = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif userInGroup == false {\n\t\t\tcontinue\n\t\t}\n\t\tif gid, err := strconv.Atoi(s[2]); err == nil {\n\t\t\tugid := uint32(gid)\n\t\t\tif ugid != primary {\n\t\t\t\tgids = append(gids, GroupLabel{ugid, s[0], 0})\n\t\t\t}\n\t\t}\n\t\tcacheForGroup.Set(map[string]string{\"username\": username}, gids)\n\t}\n\treturn gids\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs/auth_unix.go",
    "content": "package plg_backend_nfs\n\nimport (\n\t\"bytes\"\n\t\"math/rand\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/vmware/go-nfs-client/nfs/rpc\"\n\t\"github.com/vmware/go-nfs-client/nfs/xdr\"\n)\n\n// ref: https://datatracker.ietf.org/doc/html/rfc5531#section-8.2\n// so far we only have implemented AUTH_SYS but one day we might want to add support\n// for RPCSEC_GSS as detailed in https://datatracker.ietf.org/doc/html/rfc2203\ntype AuthUnix struct {\n\tStamp       uint32\n\tMachinename string\n\tUid         uint32\n\tGid         uint32\n\tGids        []uint32\n}\n\n// ref: RFC5531 - page25\nfunc NewAuthUnix(machineName string, uid, gid uint32, gids []GroupLabel, gidsHint string) rpc.Auth {\n\tw := new(bytes.Buffer)\n\tif len(gids) > 16 { // https://www.rfc-editor.org/rfc/rfc5531.html#page-25\n\t\t// when the limit of AUTH_UNIX is reached, we want to filter out the\n\t\t// groups that are of less of importance\n\t\tfor i, _ := range gids {\n\t\t\tscore := 0\n\t\t\tfor _, h := range strings.Split(gidsHint, \",\") {\n\t\t\t\tif strings.Contains(gids[i].Label, strings.TrimSpace(h)) {\n\t\t\t\t\tscore += 1\n\t\t\t\t}\n\t\t\t}\n\t\t\tgids[i].Priority = score\n\t\t}\n\t\tsort.Slice(gids, func(i, j int) bool {\n\t\t\treturn gids[i].Priority > gids[j].Priority\n\t\t})\n\t\tgids = gids[0:16]\n\t\tsort.Slice(gids, func(i, j int) bool {\n\t\t\treturn gids[i].Id < gids[j].Id\n\t\t})\n\t}\n\txdr.Write(w, AuthUnix{\n\t\tStamp:       rand.New(rand.NewSource(time.Now().UnixNano())).Uint32(),\n\t\tMachinename: machineName,\n\t\tUid:         uid,\n\t\tGid:         gid,\n\t\tGids:        toGids(gids),\n\t})\n\treturn rpc.Auth{\n\t\t1, // = AUTH_SYS in RFC5531\n\t\tw.Bytes(),\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs/index.go",
    "content": "package plg_backend_nfs\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"github.com/vmware/go-nfs-client/nfs\"\n\t\"github.com/vmware/go-nfs-client/nfs/rpc\"\n\t\"github.com/vmware/go-nfs-client/nfs/util\"\n\t\"github.com/vmware/go-nfs-client/nfs/xdr\"\n)\n\nvar NfsCache AppCache\n\ntype NfsShare struct {\n\tmount *nfs.Mount\n\tv     *nfs.Target\n\tauth  rpc.Auth\n\tmu    *sync.Mutex\n\twg    *sync.WaitGroup\n\tuid   uint32\n\tgid   uint32\n\tgids  []uint32\n}\n\nfunc init() {\n\tBackend.Register(\"nfs\", NfsShare{})\n\tutil.DefaultLogger.SetDebug(false)\n\n\tNfsCache = NewAppCache(2, 1)\n\tNfsCache.OnEvict(func(key string, value interface{}) {\n\t\tc := value.(*NfsShare)\n\t\tif c == nil {\n\t\t\tLog.Warning(\"plg_backend_nfs::nfs is nil on close\")\n\t\t\treturn\n\t\t} else if c.wg == nil {\n\t\t\tc.Close()\n\t\t\tLog.Warning(\"plg_backend_nfs::wg is nil on close\")\n\t\t\treturn\n\t\t}\n\t\tc.wg.Wait()\n\t\tLog.Debug(\"plg_backend_nfs::vacuum\")\n\t\tc.Close()\n\t})\n}\n\nfunc (this NfsShare) Init(params map[string]string, app *App) (IBackend, error) {\n\tif params[\"hostname\"] == \"\" {\n\t\treturn nil, ErrNotFound\n\t}\n\tif params[\"machine_name\"] == \"\" {\n\t\tparams[\"machine_name\"] = \"Filestash\"\n\t}\n\tparams[\"username\"] = params[\"uid\"]\n\n\tif c := NfsCache.Get(params); c != nil {\n\t\td := c.(*NfsShare)\n\t\tif d == nil {\n\t\t\tLog.Warning(\"plg_backend_nfs::nfs is nil on get\")\n\t\t\treturn nil, ErrInternal\n\t\t} else if d.wg == nil {\n\t\t\tLog.Warning(\"plg_backend_nfs::wg is nil on get\")\n\t\t\treturn nil, ErrInternal\n\t\t}\n\t\td.wg.Add(1)\n\t\tgo func() {\n\t\t\t<-app.Context.Done()\n\t\t\td.wg.Done()\n\t\t}()\n\t\treturn d, nil\n\t}\n\n\tuid, gid, gids := ExtractUserInfo(params[\"uid\"], params[\"gid\"], params[\"gids\"])\n\tLog.Debug(\"plg_backend_nfs::userInfo user=%s uid=%d gid=%d gids=%v\", params[\"uid\"], uid, gid, gids)\n\tmount, err := nfs.DialMount(params[\"hostname\"])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tauth := NewAuthUnix(params[\"machine_name\"], uid, gid, gids, params[\"gids\"])\n\tv, err := mount.Mount(\n\t\tparams[\"target\"],\n\t\tauth,\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ts := &NfsShare{mount, v, auth, new(sync.Mutex), new(sync.WaitGroup), uid, gid, toGids(gids)}\n\ts.wg.Add(1)\n\tgo func() {\n\t\t<-app.Context.Done()\n\t\ts.wg.Done()\n\t}()\n\tNfsCache.Set(params, s)\n\treturn s, nil\n}\n\nfunc toGids(gids []GroupLabel) []uint32 {\n\tg := make([]uint32, len(gids))\n\tfor i, _ := range gids {\n\t\tg[i] = gids[i].Id\n\t}\n\treturn g\n}\n\nfunc (this NfsShare) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"nfs\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"hostname\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Hostname\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"target\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Mount Path\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"advanced\",\n\t\t\t\tType:        \"enable\",\n\t\t\t\tPlaceholder: \"Advanced\",\n\t\t\t\tTarget:      []string{\"nfs_uid\", \"nfs_gid\", \"nfs_gids\", \"nfs_machinename\", \"nfs_chroot\"},\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"nfs_uid\",\n\t\t\t\tName:        \"uid\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"UID\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"nfs_gid\",\n\t\t\t\tName:        \"gid\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"GID\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"nfs_gids\",\n\t\t\t\tName:        \"gids\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Auxiliary GIDs\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"nfs_machinename\",\n\t\t\t\tName:        \"machine_name\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Machine Name\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"nfs_chroot\",\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Chroot\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this NfsShare) Meta(path string) Metadata {\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\tf, _, err := this.v.Lookup(strings.TrimSuffix(path, \"/\"))\n\tif err != nil {\n\t\treturn Metadata{}\n\t} else if f == nil {\n\t\treturn Metadata{}\n\t}\n\tfattr, ok := f.(*nfs.Fattr)\n\tif ok == false {\n\t\treturn Metadata{}\n\t}\n\n\tif fattr == nil { // happen at the root\n\t\treturn Metadata{}\n\t}\n\tvar (\n\t\tr, w  bool\n\t\tperms = fattr.Mode().Perm()\n\t)\n\tif perms&0002 != 0 {\n\t\tw = true\n\t}\n\tif perms&0004 != 0 {\n\t\tr = true\n\t}\n\tif fattr.UID == this.uid {\n\t\tif perms&0400 != 0 {\n\t\t\tr = true\n\t\t}\n\t\tif perms&0200 != 0 {\n\t\t\tw = true\n\t\t}\n\t}\n\tif (fattr.GID == this.gid) || isIn(fattr.GID, this.gids) {\n\t\tif perms&0040 != 0 {\n\t\t\tr = true\n\t\t}\n\t\tif perms&0020 != 0 {\n\t\t\tw = true\n\t\t}\n\t}\n\treturn Metadata{\n\t\tCanSee:             NewBool(r),\n\t\tCanCreateFile:      NewBool(w),\n\t\tCanCreateDirectory: NewBool(w),\n\t\tCanRename:          NewBool(w),\n\t\tCanMove:            NewBool(w),\n\t\tCanUpload:          NewBool(w),\n\t\tCanDelete:          NewBool(w),\n\t}\n}\n\nfunc isIn(id uint32, list []uint32) bool {\n\tfor i, _ := range list {\n\t\tif list[i] == id {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (this NfsShare) Ls(path string) ([]os.FileInfo, error) {\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\tfiles := make([]os.FileInfo, 0)\n\n\tdirs, err := this.v.ReadDirPlus(path)\n\tif err != nil {\n\t\treturn files, err\n\t}\n\tfor _, dir := range dirs {\n\t\tif dir.FileName == \".\" || dir.FileName == \"..\" {\n\t\t\tcontinue\n\t\t} else if dir.Attr.Attr.Type != 1 && dir.Attr.Attr.Type != 2 {\n\t\t\t// don't show anything else than file and folder\n\t\t\tcontinue\n\t\t}\n\t\tif len(this.gids) > 0 { // filter out what users don't have access\n\t\t\thasAccess := false\n\t\t\tfor _, gid := range this.gids {\n\t\t\t\tif gid == dir.Attr.Attr.GID {\n\t\t\t\t\thasAccess = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tif this.gid == dir.Attr.Attr.GID {\n\t\t\t\thasAccess = true\n\t\t\t}\n\t\t\tif hasAccess == false {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tfiles = append(files, File{\n\t\t\tFName: dir.FileName,\n\t\t\tFType: func() string {\n\t\t\t\tif dir.Attr.Attr.Type == 1 {\n\t\t\t\t\treturn \"file\"\n\t\t\t\t}\n\t\t\t\treturn \"directory\"\n\t\t\t}(),\n\t\t\tFSize: int64(dir.Attr.Attr.Filesize),\n\t\t\tFTime: int64(dir.Attr.Attr.Ctime.Seconds),\n\t\t})\n\t}\n\treturn files, nil\n}\n\nfunc (this NfsShare) Stat(path string) (os.FileInfo, error) {\n\treturn nil, ErrNotImplemented\n}\n\nfunc (this NfsShare) Cat(path string) (io.ReadCloser, error) {\n\tthis.mu.Lock()\n\trc, err := this.v.Open(path)\n\tthis.mu.Unlock()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &nfsReadCloser{rc, this.mu}, nil\n}\n\ntype nfsReadCloser struct {\n\tio.ReadCloser\n\tmu *sync.Mutex\n}\n\nfunc (r *nfsReadCloser) Read(p []byte) (int, error) {\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\treturn r.ReadCloser.Read(p)\n}\n\nfunc (r *nfsReadCloser) Close() error {\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\treturn r.ReadCloser.Close()\n}\n\nfunc (this NfsShare) Mkdir(path string) error {\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\t_, err := this.v.Mkdir(this.nfsPath(path), 0775)\n\treturn err\n}\n\nfunc (this NfsShare) Rm(path string) error {\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\tif strings.HasSuffix(path, \"/\") {\n\t\treturn this.v.RemoveAll(this.nfsPath(path))\n\t}\n\treturn this.v.Remove(path)\n}\n\n// this wasn't implemented in the original lib and considering\n// PR aren't handled by vmware, we did come with the implementation as\n// of RFC1813 in: https://www.rfc-editor.org/rfc/rfc1813#section-3.3.14\nfunc (this NfsShare) Mv(from string, to string) error {\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\tf, fName := filepath.Split(this.nfsPath(from))\n\t_, fh, err := this.v.Lookup(f)\n\tif err != nil {\n\t\treturn err\n\t}\n\tt, tName := filepath.Split(this.nfsPath(to))\n\t_, th, err := this.v.Lookup(t)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttype RenameArgs struct {\n\t\trpc.Header\n\t\tFrom nfs.Diropargs3\n\t\tTo   nfs.Diropargs3\n\t}\n\tconst RENAME3res = 14\n\tres, err := this.v.Call(&RenameArgs{\n\t\tHeader: rpc.Header{\n\t\t\tRpcvers: 2,\n\t\t\tProg:    nfs.Nfs3Prog,\n\t\t\tVers:    nfs.Nfs3Vers,\n\t\t\tProc:    RENAME3res,\n\t\t\tCred:    this.auth,\n\t\t\tVerf:    rpc.AuthNull,\n\t\t},\n\t\tFrom: nfs.Diropargs3{\n\t\t\tFH:       fh,\n\t\t\tFilename: fName,\n\t\t},\n\t\tTo: nfs.Diropargs3{\n\t\t\tFH:       th,\n\t\t\tFilename: tName,\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tstatus, err := xdr.ReadUint32(res)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nfs.NFS3Error(status)\n}\n\nfunc (this NfsShare) Touch(path string) error {\n\treturn this.Save(path, strings.NewReader(\"\"))\n}\n\nfunc (this NfsShare) Save(path string, file io.Reader) error {\n\tthis.mu.Lock()\n\tw, err := this.v.OpenFile(path, 0644)\n\tthis.mu.Unlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\tnw := &nfsWriteCloser{w, this.mu}\n\t_, err = io.Copy(nw, file)\n\tnw.Close()\n\treturn err\n}\n\ntype nfsWriteCloser struct {\n\tio.WriteCloser\n\tmu *sync.Mutex\n}\n\nfunc (w *nfsWriteCloser) Write(p []byte) (int, error) {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\treturn w.WriteCloser.Write(p)\n}\n\nfunc (w *nfsWriteCloser) Close() error {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\treturn w.WriteCloser.Close()\n}\n\nfunc (this NfsShare) Close() {\n\tthis.v.Close()\n\tthis.mount.Close()\n}\n\nfunc (this NfsShare) nfsPath(path string) string {\n\treturn strings.TrimSuffix(path, \"/\")\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs4/index.go",
    "content": "package plg_backend_nfs4\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_nfs4/repo/nfs4\"\n)\n\nconst DEFAULT_PORT = \":2049\"\n\ntype Nfs4Share struct {\n\tclient *nfs4.NfsClient\n\tctx    context.Context\n}\n\nfunc init() {\n\tBackend.Register(\"nfs4\", Nfs4Share{})\n}\n\nfunc (this Nfs4Share) Init(params map[string]string, app *App) (IBackend, error) {\n\tif params[\"hostname\"] == \"\" {\n\t\treturn nil, ErrNotFound\n\t}\n\tvar (\n\t\tuid uint32 = 1000\n\t\tgid uint32 = 1000\n\t)\n\tif params[\"uid\"] != \"\" {\n\t\tif _uid, err := strconv.Atoi(params[\"uid\"]); err == nil {\n\t\t\tuid = uint32(_uid)\n\t\t}\n\t}\n\tif params[\"gid\"] != \"\" {\n\t\tif _gid, err := strconv.Atoi(params[\"gid\"]); err == nil {\n\t\t\tgid = uint32(_gid)\n\t\t}\n\t}\n\tif params[\"machine_name\"] == \"\" {\n\t\tparams[\"machine_name\"] = APPNAME\n\t}\n\tif strings.Contains(params[\"hostname\"], \":\") == false {\n\t\tparams[\"hostname\"] = params[\"hostname\"] + DEFAULT_PORT\n\t}\n\tclient, err := nfs4.NewNfsClient(app.Context, params[\"hostname\"], nfs4.AuthParams{\n\t\tMachineName: params[\"machine_name\"],\n\t\tUid:         uid,\n\t\tGid:         gid,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn Nfs4Share{\n\t\tclient,\n\t\tapp.Context,\n\t}, nil\n}\n\nfunc (this Nfs4Share) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"nfs4\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"hostname\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Hostname\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"advanced\",\n\t\t\t\tType:        \"enable\",\n\t\t\t\tPlaceholder: \"Advanced\",\n\t\t\t\tTarget:      []string{\"nfs_uid\", \"nfs_gid\", \"nfs_machinename\", \"nfs_chroot\"},\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"nfs_uid\",\n\t\t\t\tName:        \"uid\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"uid\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"nfs_gid\",\n\t\t\t\tName:        \"gid\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"gid\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"nfs_machinename\",\n\t\t\t\tName:        \"machine_name\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"machine name\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"nfs_chroot\",\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"chroot\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Nfs4Share) Ls(path string) ([]os.FileInfo, error) {\n\tlist, err := this.client.GetFileList(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfiles := make([]os.FileInfo, 0)\n\tfor _, info := range list {\n\t\tfiles = append(files, File{\n\t\t\tFName: info.Name,\n\t\t\tFType: func() string {\n\t\t\t\tif info.IsDir {\n\t\t\t\t\treturn \"directory\"\n\t\t\t\t}\n\t\t\t\treturn \"file\"\n\t\t\t}(),\n\t\t\tFSize: int64(info.Size),\n\t\t\tFTime: int64(info.Mtime.Nanosecond()),\n\t\t})\n\t}\n\treturn files, nil\n}\n\nfunc (this Nfs4Share) Stat(path string) (os.FileInfo, error) {\n\tfinfo, err := this.client.GetFileInfo(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn File{\n\t\tFName: finfo.Name,\n\t\tFType: func() string {\n\t\t\tif finfo.IsDir {\n\t\t\t\treturn \"directory\"\n\t\t\t}\n\t\t\treturn \"file\"\n\t\t}(),\n\t\tFSize: int64(finfo.Size),\n\t\tFTime: int64(finfo.Mtime.Nanosecond()),\n\t}, nil\n}\n\nfunc (this Nfs4Share) Cat(path string) (io.ReadCloser, error) {\n\t_, err := this.client.GetFileInfo(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpr, pw := io.Pipe()\n\tgo func() {\n\t\t_, _ = this.client.ReadFileAll(path, pw)\n\t\tpw.Close()\n\t}()\n\treturn pr, nil\n}\n\nfunc (this Nfs4Share) Mkdir(path string) error {\n\treturn this.client.MakePath(path)\n}\n\nfunc (this Nfs4Share) Rm(path string) error {\n\tif strings.HasSuffix(path, \"/\") {\n\t\treturn nfs4.RemoveRecursive(this.client, path)\n\t}\n\treturn this.client.DeleteFile(path)\n}\n\nfunc (this Nfs4Share) Mv(from string, to string) error {\n\treturn ErrNotImplemented\n}\n\nfunc (this Nfs4Share) Touch(path string) error {\n\t_, err := this.client.WriteFile(path, false, 0, strings.NewReader(\"\"))\n\treturn err\n}\n\nfunc (this Nfs4Share) Save(path string, file io.Reader) error {\n\t_, err := this.client.ReWriteFile(path, file)\n\treturn err\n}\n\nfunc (this Nfs4Share) Close() {\n\tthis.client.Close()\n\treturn\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs4/repo/README.md",
    "content": "TODO: we can't go get github.com/kha7iq/go-nfs-client => fork over and fix it instead\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs4/repo/internal/cleanuper.go",
    "content": "package internal\n\ntype cleanuper struct {\n\tcleanupErr func() error\n\tcleanup    func()\n}\n\nfunc NewCleanupErr(cl func() error) *cleanuper {\n\treturn &cleanuper{cleanupErr: cl}\n}\n\nfunc NewCleanup(cl func()) *cleanuper {\n\treturn &cleanuper{cleanup: cl}\n}\n\nfunc (c *cleanuper) Disarm() {\n\tc.cleanupErr = nil\n\tc.cleanup = nil\n}\n\nfunc (c *cleanuper) Cleanup() {\n\tif c.cleanupErr != nil {\n\t\t_ = c.cleanupErr()\n\t}\n\n\tif c.cleanup != nil {\n\t\tc.cleanup()\n\t}\n}\n\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs4/repo/internal/nfs4.go",
    "content": "// Code generated by goxdr -B -p nfs4 nfs4/nfs4.x; DO NOT EDIT.\n\npackage internal\nimport(\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"strings\"\n)\n\nvar _ = fmt.Sprintf\nvar _ context.Context\n\n//\n// Data types defined in XDR file\n//\n\n/*\n * Sizes\n */\nconst NFS4_FHSIZE = 128\n\nconst NFS4_VERIFIER_SIZE = 8\n\nconst NFS4_OPAQUE_LIMIT = 1024\n\nconst NFS4_SESSIONID_SIZE = 16\n\n/*\n * File types\n */\ntype Nfs_ftype4 int32\nconst (\n\t/* Regular File */\n\tNF4REG Nfs_ftype4 = 1\n\t/* Directory */\n\tNF4DIR Nfs_ftype4 = 2\n\t/* Special File - block device */\n\tNF4BLK Nfs_ftype4 = 3\n\t/* Special File - character device */\n\tNF4CHR Nfs_ftype4 = 4\n\t/* Symbolic Link */\n\tNF4LNK Nfs_ftype4 = 5\n\t/* Special File - socket */\n\tNF4SOCK Nfs_ftype4 = 6\n\t/* Special File - fifo */\n\tNF4FIFO Nfs_ftype4 = 7\n\t/* Attribute Directory */\n\tNF4ATTRDIR Nfs_ftype4 = 8\n\t/* Named Attribute */\n\tNF4NAMEDATTR Nfs_ftype4 = 9\n)\n\n/*\n * Error status\n */\ntype Nfsstat4 int32\nconst (\n\t/* everything is okay      */\n\tNFS4_OK Nfsstat4 = 0\n\t/* caller not privileged   */\n\tNFS4ERR_PERM Nfsstat4 = 1\n\t/* no such file/directory  */\n\tNFS4ERR_NOENT Nfsstat4 = 2\n\t/* hard I/O error          */\n\tNFS4ERR_IO Nfsstat4 = 5\n\t/* no such device          */\n\tNFS4ERR_NXIO Nfsstat4 = 6\n\t/* access denied           */\n\tNFS4ERR_ACCESS Nfsstat4 = 13\n\t/* file already exists     */\n\tNFS4ERR_EXIST Nfsstat4 = 17\n\t/* different filesystems   */\n\tNFS4ERR_XDEV Nfsstat4 = 18\n\t/* should be a directory   */\n\tNFS4ERR_NOTDIR Nfsstat4 = 20\n\t/* should not be directory */\n\tNFS4ERR_ISDIR Nfsstat4 = 21\n\t/* invalid argument        */\n\tNFS4ERR_INVAL Nfsstat4 = 22\n\t/* file exceeds server max */\n\tNFS4ERR_FBIG Nfsstat4 = 27\n\t/* no space on filesystem  */\n\tNFS4ERR_NOSPC Nfsstat4 = 28\n\t/* read-only filesystem    */\n\tNFS4ERR_ROFS Nfsstat4 = 30\n\t/* too many hard links     */\n\tNFS4ERR_MLINK Nfsstat4 = 31\n\t/* name exceeds server max */\n\tNFS4ERR_NAMETOOLONG Nfsstat4 = 63\n\t/* directory not empty     */\n\tNFS4ERR_NOTEMPTY Nfsstat4 = 66\n\t/* hard quota limit reached*/\n\tNFS4ERR_DQUOT Nfsstat4 = 69\n\t/* file no longer exists   */\n\tNFS4ERR_STALE Nfsstat4 = 70\n\t/* Illegal filehandle      */\n\tNFS4ERR_BADHANDLE Nfsstat4 = 10001\n\t/* READDIR cookie is stale */\n\tNFS4ERR_BAD_COOKIE Nfsstat4 = 10003\n\t/* operation not supported */\n\tNFS4ERR_NOTSUPP Nfsstat4 = 10004\n\t/* response limit exceeded */\n\tNFS4ERR_TOOSMALL Nfsstat4 = 10005\n\t/* undefined server error  */\n\tNFS4ERR_SERVERFAULT Nfsstat4 = 10006\n\t/* type invalid for CREATE */\n\tNFS4ERR_BADTYPE Nfsstat4 = 10007\n\t/* file \"busy\" - retry     */\n\tNFS4ERR_DELAY Nfsstat4 = 10008\n\t/* nverify says attrs same */\n\tNFS4ERR_SAME Nfsstat4 = 10009\n\t/* lock unavailable        */\n\tNFS4ERR_DENIED Nfsstat4 = 10010\n\t/* lock lease expired      */\n\tNFS4ERR_EXPIRED Nfsstat4 = 10011\n\t/* I/O failed due to lock  */\n\tNFS4ERR_LOCKED Nfsstat4 = 10012\n\t/* in grace period         */\n\tNFS4ERR_GRACE Nfsstat4 = 10013\n\t/* filehandle expired      */\n\tNFS4ERR_FHEXPIRED Nfsstat4 = 10014\n\t/* share reserve denied    */\n\tNFS4ERR_SHARE_DENIED Nfsstat4 = 10015\n\t/* wrong security flavor   */\n\tNFS4ERR_WRONGSEC Nfsstat4 = 10016\n\t/* clientid in use         */\n\tNFS4ERR_CLID_INUSE Nfsstat4 = 10017\n\t/* resource exhaustion     */\n\tNFS4ERR_RESOURCE Nfsstat4 = 10018\n\t/* filesystem relocated    */\n\tNFS4ERR_MOVED Nfsstat4 = 10019\n\t/* current FH is not set   */\n\tNFS4ERR_NOFILEHANDLE Nfsstat4 = 10020\n\t/* minor vers not supp */\n\tNFS4ERR_MINOR_VERS_MISMATCH Nfsstat4 = 10021\n\t/* server has rebooted     */\n\tNFS4ERR_STALE_CLIENTID Nfsstat4 = 10022\n\t/* server has rebooted     */\n\tNFS4ERR_STALE_STATEID Nfsstat4 = 10023\n\t/* state is out of sync    */\n\tNFS4ERR_OLD_STATEID Nfsstat4 = 10024\n\t/* incorrect stateid       */\n\tNFS4ERR_BAD_STATEID Nfsstat4 = 10025\n\t/* request is out of seq.  */\n\tNFS4ERR_BAD_SEQID Nfsstat4 = 10026\n\t/* verify - attrs not same */\n\tNFS4ERR_NOT_SAME Nfsstat4 = 10027\n\t/* lock range not supported*/\n\tNFS4ERR_LOCK_RANGE Nfsstat4 = 10028\n\t/* should be file/directory*/\n\tNFS4ERR_SYMLINK Nfsstat4 = 10029\n\t/* no saved filehandle     */\n\tNFS4ERR_RESTOREFH Nfsstat4 = 10030\n\t/* some filesystem moved   */\n\tNFS4ERR_LEASE_MOVED Nfsstat4 = 10031\n\t/* recommended attr not sup*/\n\tNFS4ERR_ATTRNOTSUPP Nfsstat4 = 10032\n\t/* reclaim outside of grace*/\n\tNFS4ERR_NO_GRACE Nfsstat4 = 10033\n\t/* reclaim error at server */\n\tNFS4ERR_RECLAIM_BAD Nfsstat4 = 10034\n\t/* conflict on reclaim    */\n\tNFS4ERR_RECLAIM_CONFLICT Nfsstat4 = 10035\n\t/* XDR decode failed       */\n\tNFS4ERR_BADXDR Nfsstat4 = 10036\n\t/* file locks held at CLOSE*/\n\tNFS4ERR_LOCKS_HELD Nfsstat4 = 10037\n\t/* conflict in OPEN and I/O*/\n\tNFS4ERR_OPENMODE Nfsstat4 = 10038\n\t/* owner translation bad   */\n\tNFS4ERR_BADOWNER Nfsstat4 = 10039\n\t/* utf-8 char not supported*/\n\tNFS4ERR_BADCHAR Nfsstat4 = 10040\n\t/* name not supported      */\n\tNFS4ERR_BADNAME Nfsstat4 = 10041\n\t/* lock range not supported*/\n\tNFS4ERR_BAD_RANGE Nfsstat4 = 10042\n\t/* no atomic up/downgrade  */\n\tNFS4ERR_LOCK_NOTSUPP Nfsstat4 = 10043\n\t/* undefined operation     */\n\tNFS4ERR_OP_ILLEGAL Nfsstat4 = 10044\n\t/* file locking deadlock   */\n\tNFS4ERR_DEADLOCK Nfsstat4 = 10045\n\t/* open file blocks op.    */\n\tNFS4ERR_FILE_OPEN Nfsstat4 = 10046\n\t/* lockowner state revoked */\n\tNFS4ERR_ADMIN_REVOKED Nfsstat4 = 10047\n\t/* callback path down      */\n\tNFS4ERR_CB_PATH_DOWN Nfsstat4 = 10048\n\tNFS4ERR_BADIOMODE Nfsstat4 = 10049\n\tNFS4ERR_BADLAYOUT Nfsstat4 = 10050\n\tNFS4ERR_BAD_SESSION_DIGEST Nfsstat4 = 10051\n\tNFS4ERR_BADSESSION Nfsstat4 = 10052\n\tNFS4ERR_BADSLOT Nfsstat4 = 10053\n\tNFS4ERR_COMPLETE_ALREADY Nfsstat4 = 10054\n\tNFS4ERR_CONN_NOT_BOUND_TO_SESSION Nfsstat4 = 10055\n\tNFS4ERR_DELEG_ALREADY_WANTED Nfsstat4 = 10056\n\tNFS4ERR_BACK_CHAN_BUSY Nfsstat4 = 10057\n\tNFS4ERR_LAYOUTTRYLATER Nfsstat4 = 10058\n\tNFS4ERR_LAYOUTUNAVAILABLE Nfsstat4 = 10059\n\tNFS4ERR_NOMATCHING_LAYOUT Nfsstat4 = 10060\n\tNFS4ERR_RECALLCONFLICT Nfsstat4 = 10061\n)\n\n/*\n * Basic data types\n */\ntype Bitmap4 = []Uint32_t\n\ntype Offset4 = Uint64_t\n\ntype Count4 = Uint32_t\n\ntype Length4 = Uint64_t\n\ntype Clientid4 = Uint64_t\n\ntype Sequenceid4 = Uint32_t\n\ntype Seqid4 = Uint32_t\n\ntype Slotid4 = Uint32_t\n\ntype Utf8string = []byte\n\ntype Utf8str_cis = Utf8string\n\ntype Utf8str_cs = Utf8string\n\ntype Utf8str_mixed = Utf8string\n\ntype Component4 = Utf8str_cs\n\ntype Pathname4 = []Component4\n\ntype Nfs_lockid4 = Uint64_t\n\ntype Nfs_cookie4 = Uint64_t\n\ntype Linktext4 = Utf8str_cs\n\ntype Sec_oid4 = []byte\n\ntype Qop4 = Uint32_t\n\ntype Mode4 = Uint32_t\n\ntype Changeid4 = Uint64_t\n\ntype Verifier4 = [NFS4_VERIFIER_SIZE]byte\n\ntype Sessionid4 = [NFS4_SESSIONID_SIZE]byte\n\n/*\n * Authsys_parms\n */\ntype Authsys_parms struct {\n\tStamp uint32\n\tMachinename string // bound 255\n\tUid uint32\n\tGid uint32\n\tGids []uint32 // bound 16\n}\n\nconst NFS4_DEVICEID4_SIZE = 16\n\ntype Deviceid4 = [NFS4_DEVICEID4_SIZE]byte\n\ntype Layouttype4 int32\nconst (\n\tLAYOUT4_NFSV4_1_FILES Layouttype4 = Layouttype4(0x1)\n\tLAYOUT4_OSD2_OBJECTS Layouttype4 = Layouttype4(0x2)\n\tLAYOUT4_BLOCK_VOLUME Layouttype4 = Layouttype4(0x3)\n)\n\ntype Layoutupdate4 struct {\n\tLou_type Layouttype4\n\tLou_body []byte\n}\n\ntype Device_addr4 struct {\n\tDa_layout_type Layouttype4\n\tDa_addr_body []byte\n}\n\n/*\n * Timeval\n */\ntype Nfstime4 struct {\n\tSeconds Int64_t\n\tNseconds Uint32_t\n}\n\ntype Time_how4 int32\nconst (\n\tSET_TO_SERVER_TIME4 Time_how4 = 0\n\tSET_TO_CLIENT_TIME4 Time_how4 = 1\n)\n\ntype Layoutiomode4 int32\nconst (\n\tLAYOUTIOMODE4_READ Layoutiomode4 = 1\n\tLAYOUTIOMODE4_RW Layoutiomode4 = 2\n\tLAYOUTIOMODE4_ANY Layoutiomode4 = 3\n)\n\ntype Layout_content4 struct {\n\tLoc_type Layouttype4\n\tLoc_body []byte\n}\n\ntype Layout4 struct {\n\tLo_offset Offset4\n\tLo_length Length4\n\tLo_iomode Layoutiomode4\n\tLo_content Layout_content4\n}\n\ntype Settime4 struct {\n\t// The union discriminant Set_it selects among the following arms:\n\t//   SET_TO_CLIENT_TIME4:\n\t//      Time() *Nfstime4\n\t//   default:\n\t//      void\n\tSet_it Time_how4\n\tU interface{}\n}\n\n/*\n * File access handle\n */\ntype Nfs_fh4 = []byte // bound NFS4_FHSIZE\n\n/*\n * FSID structure for major/minor\n */\ntype Fsid4 struct {\n\tMajor Uint64_t\n\tMinor Uint64_t\n}\n\n/*\n * Filesystem locations attribute for relocation/migration\n */\ntype Fs_location4 struct {\n\tServer []Utf8str_cis\n\tRootpath Pathname4\n}\n\ntype Fs_locations4 struct {\n\tFs_root Pathname4\n\tLocations []Fs_location4\n}\n\n/*\n * Mask that indicates which Access Control Entries are supported.\n * Values for the fattr4_aclsupport attribute.\n */\nconst ACL4_SUPPORT_ALLOW_ACL = 0x00000001\n\nconst ACL4_SUPPORT_DENY_ACL = 0x00000002\n\nconst ACL4_SUPPORT_AUDIT_ACL = 0x00000004\n\nconst ACL4_SUPPORT_ALARM_ACL = 0x00000008\n\ntype Acetype4 = Uint32_t\n\n/*\n * acetype4 values, others can be added as needed.\n */\nconst ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000\n\nconst ACE4_ACCESS_DENIED_ACE_TYPE = 0x00000001\n\nconst ACE4_SYSTEM_AUDIT_ACE_TYPE = 0x00000002\n\nconst ACE4_SYSTEM_ALARM_ACE_TYPE = 0x00000003\n\n/*\n * ACE flag\n */\ntype Aceflag4 = Uint32_t\n\n/*\n * ACE flag values\n */\nconst ACE4_FILE_INHERIT_ACE = 0x00000001\n\nconst ACE4_DIRECTORY_INHERIT_ACE = 0x00000002\n\nconst ACE4_NO_PROPAGATE_INHERIT_ACE = 0x00000004\n\nconst ACE4_INHERIT_ONLY_ACE = 0x00000008\n\nconst ACE4_SUCCESSFUL_ACCESS_ACE_FLAG = 0x00000010\n\nconst ACE4_FAILED_ACCESS_ACE_FLAG = 0x00000020\n\nconst ACE4_IDENTIFIER_GROUP = 0x00000040\n\n/*\n * ACE mask\n */\ntype Acemask4 = Uint32_t\n\n/*\n * ACE mask values\n */\nconst ACE4_READ_DATA = 0x00000001\n\nconst ACE4_LIST_DIRECTORY = 0x00000001\n\nconst ACE4_WRITE_DATA = 0x00000002\n\nconst ACE4_ADD_FILE = 0x00000002\n\nconst ACE4_APPEND_DATA = 0x00000004\n\nconst ACE4_ADD_SUBDIRECTORY = 0x00000004\n\nconst ACE4_READ_NAMED_ATTRS = 0x00000008\n\nconst ACE4_WRITE_NAMED_ATTRS = 0x00000010\n\nconst ACE4_EXECUTE = 0x00000020\n\nconst ACE4_DELETE_CHILD = 0x00000040\n\nconst ACE4_READ_ATTRIBUTES = 0x00000080\n\nconst ACE4_WRITE_ATTRIBUTES = 0x00000100\n\nconst ACE4_DELETE = 0x00010000\n\nconst ACE4_READ_ACL = 0x00020000\n\nconst ACE4_WRITE_ACL = 0x00040000\n\nconst ACE4_WRITE_OWNER = 0x00080000\n\nconst ACE4_SYNCHRONIZE = 0x00100000\n\n/*\n * ACE4_GENERIC_READ -- defined as combination of\n *      ACE4_READ_ACL |\n *      ACE4_READ_DATA |\n *      ACE4_READ_ATTRIBUTES |\n *      ACE4_SYNCHRONIZE\n */\nconst ACE4_GENERIC_READ = 0x00120081\n\n/*\n * ACE4_GENERIC_WRITE -- defined as combination of\n *      ACE4_READ_ACL |\n *      ACE4_WRITE_DATA |\n *      ACE4_WRITE_ATTRIBUTES |\n *      ACE4_WRITE_ACL |\n *      ACE4_APPEND_DATA |\n *      ACE4_SYNCHRONIZE\n */\nconst ACE4_GENERIC_WRITE = 0x00160106\n\n/*\n * ACE4_GENERIC_EXECUTE -- defined as combination of\n *      ACE4_READ_ACL\n *      ACE4_READ_ATTRIBUTES\n *      ACE4_EXECUTE\n *      ACE4_SYNCHRONIZE\n */\nconst ACE4_GENERIC_EXECUTE = 0x001200A0\n\n/*\n * Access Control Entry definition\n */\ntype Nfsace4 struct {\n\tType Acetype4\n\tFlag Aceflag4\n\tAccess_mask Acemask4\n\tWho Utf8str_mixed\n}\n\n/*\n * Field definitions for the fattr4_mode attribute\n */\nconst MODE4_SUID = 0x800\n\n/* set group id on execution */\nconst MODE4_SGID = 0x400\n\n/* save text even after use */\nconst MODE4_SVTX = 0x200\n\n/* read permission: owner */\nconst MODE4_RUSR = 0x100\n\n/* write permission: owner */\nconst MODE4_WUSR = 0x080\n\n/* execute permission: owner */\nconst MODE4_XUSR = 0x040\n\n/* read permission: group */\nconst MODE4_RGRP = 0x020\n\n/* write permission: group */\nconst MODE4_WGRP = 0x010\n\n/* execute permission: group */\nconst MODE4_XGRP = 0x008\n\n/* read permission: other */\nconst MODE4_ROTH = 0x004\n\n/* write permission: other */\nconst MODE4_WOTH = 0x002\n\n/* execute permission: other */\nconst MODE4_XOTH = 0x001\n\n/*\n * Special data/attribute associated with\n * file types NF4BLK and NF4CHR.\n */\ntype Specdata4 struct {\n\t/* major device number */\n\tSpecdata1 Uint32_t\n\t/* minor device number */\n\tSpecdata2 Uint32_t\n}\n\n/*\n * Values for fattr4_fh_expire_type\n */\nconst FH4_PERSISTENT = 0x00000000\n\nconst FH4_NOEXPIRE_WITH_OPEN = 0x00000001\n\nconst FH4_VOLATILE_ANY = 0x00000002\n\nconst FH4_VOL_MIGRATION = 0x00000004\n\nconst FH4_VOL_RENAME = 0x00000008\n\ntype Fattr4_supported_attrs = Bitmap4\n\ntype Fattr4_type = Nfs_ftype4\n\ntype Fattr4_fh_expire_type = Uint32_t\n\ntype Fattr4_change = Changeid4\n\ntype Fattr4_size = Uint64_t\n\ntype Fattr4_link_support = bool\n\ntype Fattr4_symlink_support = bool\n\ntype Fattr4_named_attr = bool\n\ntype Fattr4_fsid = Fsid4\n\ntype Fattr4_unique_handles = bool\n\ntype Fattr4_lease_time = Uint32_t\n\ntype Fattr4_rdattr_error = Nfsstat4\n\ntype Fattr4_acl = []Nfsace4\n\ntype Fattr4_aclsupport = Uint32_t\n\ntype Fattr4_archive = bool\n\ntype Fattr4_cansettime = bool\n\ntype Fattr4_case_insensitive = bool\n\ntype Fattr4_case_preserving = bool\n\ntype Fattr4_chown_restricted = bool\n\ntype Fattr4_fileid = Uint64_t\n\ntype Fattr4_files_avail = Uint64_t\n\ntype Fattr4_filehandle = Nfs_fh4\n\ntype Fattr4_files_free = Uint64_t\n\ntype Fattr4_files_total = Uint64_t\n\ntype Fattr4_fs_locations = Fs_locations4\n\ntype Fattr4_hidden = bool\n\ntype Fattr4_homogeneous = bool\n\ntype Fattr4_maxfilesize = Uint64_t\n\ntype Fattr4_maxlink = Uint32_t\n\ntype Fattr4_maxname = Uint32_t\n\ntype Fattr4_maxread = Uint64_t\n\ntype Fattr4_maxwrite = Uint64_t\n\ntype Fattr4_mimetype = Utf8str_cs\n\ntype Fattr4_mode = Mode4\n\ntype Fattr4_mounted_on_fileid = Uint64_t\n\ntype Fattr4_no_trunc = bool\n\ntype Fattr4_numlinks = Uint32_t\n\ntype Fattr4_owner = Utf8str_mixed\n\ntype Fattr4_owner_group = Utf8str_mixed\n\ntype Fattr4_quota_avail_hard = Uint64_t\n\ntype Fattr4_quota_avail_soft = Uint64_t\n\ntype Fattr4_quota_used = Uint64_t\n\ntype Fattr4_rawdev = Specdata4\n\ntype Fattr4_space_avail = Uint64_t\n\ntype Fattr4_space_free = Uint64_t\n\ntype Fattr4_space_total = Uint64_t\n\ntype Fattr4_space_used = Uint64_t\n\ntype Fattr4_system = bool\n\ntype Fattr4_time_access = Nfstime4\n\ntype Fattr4_time_access_set = Settime4\n\ntype Fattr4_time_backup = Nfstime4\n\ntype Fattr4_time_create = Nfstime4\n\ntype Fattr4_time_delta = Nfstime4\n\ntype Fattr4_time_metadata = Nfstime4\n\ntype Fattr4_time_modify = Nfstime4\n\ntype Fattr4_time_modify_set = Settime4\n\n/*\n * Mandatory Attributes\n */\nconst FATTR4_SUPPORTED_ATTRS = 0\n\nconst FATTR4_TYPE = 1\n\nconst FATTR4_FH_EXPIRE_TYPE = 2\n\nconst FATTR4_CHANGE = 3\n\nconst FATTR4_SIZE = 4\n\nconst FATTR4_LINK_SUPPORT = 5\n\nconst FATTR4_SYMLINK_SUPPORT = 6\n\nconst FATTR4_NAMED_ATTR = 7\n\nconst FATTR4_FSID = 8\n\nconst FATTR4_UNIQUE_HANDLES = 9\n\nconst FATTR4_LEASE_TIME = 10\n\nconst FATTR4_RDATTR_ERROR = 11\n\nconst FATTR4_FILEHANDLE = 19\n\n/*\n * Recommended Attributes\n */\nconst FATTR4_ACL = 12\n\nconst FATTR4_ACLSUPPORT = 13\n\nconst FATTR4_ARCHIVE = 14\n\nconst FATTR4_CANSETTIME = 15\n\nconst FATTR4_CASE_INSENSITIVE = 16\n\nconst FATTR4_CASE_PRESERVING = 17\n\nconst FATTR4_CHOWN_RESTRICTED = 18\n\nconst FATTR4_FILEID = 20\n\nconst FATTR4_FILES_AVAIL = 21\n\nconst FATTR4_FILES_FREE = 22\n\nconst FATTR4_FILES_TOTAL = 23\n\nconst FATTR4_FS_LOCATIONS = 24\n\nconst FATTR4_HIDDEN = 25\n\nconst FATTR4_HOMOGENEOUS = 26\n\nconst FATTR4_MAXFILESIZE = 27\n\nconst FATTR4_MAXLINK = 28\n\nconst FATTR4_MAXNAME = 29\n\nconst FATTR4_MAXREAD = 30\n\nconst FATTR4_MAXWRITE = 31\n\nconst FATTR4_MIMETYPE = 32\n\nconst FATTR4_MODE = 33\n\nconst FATTR4_NO_TRUNC = 34\n\nconst FATTR4_NUMLINKS = 35\n\nconst FATTR4_OWNER = 36\n\nconst FATTR4_OWNER_GROUP = 37\n\nconst FATTR4_QUOTA_AVAIL_HARD = 38\n\nconst FATTR4_QUOTA_AVAIL_SOFT = 39\n\nconst FATTR4_QUOTA_USED = 40\n\nconst FATTR4_RAWDEV = 41\n\nconst FATTR4_SPACE_AVAIL = 42\n\nconst FATTR4_SPACE_FREE = 43\n\nconst FATTR4_SPACE_TOTAL = 44\n\nconst FATTR4_SPACE_USED = 45\n\nconst FATTR4_SYSTEM = 46\n\nconst FATTR4_TIME_ACCESS = 47\n\nconst FATTR4_TIME_ACCESS_SET = 48\n\nconst FATTR4_TIME_BACKUP = 49\n\nconst FATTR4_TIME_CREATE = 50\n\nconst FATTR4_TIME_DELTA = 51\n\nconst FATTR4_TIME_METADATA = 52\n\nconst FATTR4_TIME_MODIFY = 53\n\nconst FATTR4_TIME_MODIFY_SET = 54\n\nconst FATTR4_MOUNTED_ON_FILEID = 55\n\ntype Attrlist4 = []byte\n\n/*\n * File attribute container\n */\ntype Fattr4 struct {\n\tAttrmask Bitmap4\n\tAttr_vals Attrlist4\n}\n\n/*\n * Change info for the client\n */\ntype Change_info4 struct {\n\tAtomic bool\n\tBefore Changeid4\n\tAfter Changeid4\n}\n\ntype Clientaddr4 struct {\n\t/* see struct rpcb in RFC 1833 */\n\tR_netid string\n\t/* universal address */\n\tR_addr string\n}\n\n/*\n * Callback program info as provided by the client\n */\ntype Cb_client4 struct {\n\tCb_program Uint32_t\n\tCb_location Clientaddr4\n}\n\n/*\n * Stateid\n */\ntype Stateid4 struct {\n\tSeqid Uint32_t\n\tOther [12]byte\n}\n\n/*\n * Client ID\n */\ntype Nfs_client_id4 struct {\n\tVerifier Verifier4\n\tId []byte // bound NFS4_OPAQUE_LIMIT\n}\n\ntype Open_owner4 struct {\n\tClientid Clientid4\n\tOwner []byte // bound NFS4_OPAQUE_LIMIT\n}\n\ntype Lock_owner4 struct {\n\tClientid Clientid4\n\tOwner []byte // bound NFS4_OPAQUE_LIMIT\n}\n\ntype Nfs_lock_type4 int32\nconst (\n\tREAD_LT Nfs_lock_type4 = 1\n\tWRITE_LT Nfs_lock_type4 = 2\n\t/* blocking read */\n\tREADW_LT Nfs_lock_type4 = 3\n\t/* blocking write */\n\tWRITEW_LT Nfs_lock_type4 = 4\n)\n\n/*\n * ACCESS: Check access permission\n */\nconst ACCESS4_READ = 0x00000001\n\nconst ACCESS4_LOOKUP = 0x00000002\n\nconst ACCESS4_MODIFY = 0x00000004\n\nconst ACCESS4_EXTEND = 0x00000008\n\nconst ACCESS4_DELETE = 0x00000010\n\nconst ACCESS4_EXECUTE = 0x00000020\n\ntype ACCESS4args struct {\n\t/* CURRENT_FH: object */\n\tAccess Uint32_t\n}\n\ntype ACCESS4resok struct {\n\tSupported Uint32_t\n\tAccess Uint32_t\n}\n\ntype ACCESS4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *ACCESS4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * CLOSE: Close a file and release share reservations\n */\ntype CLOSE4args struct {\n\t/* CURRENT_FH: object */\n\tSeqid Seqid4\n\tOpen_stateid Stateid4\n}\n\ntype CLOSE4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Open_stateid() *Stateid4\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * COMMIT: Commit cached data on server to stable storage\n */\ntype COMMIT4args struct {\n\t/* CURRENT_FH: file */\n\tOffset Offset4\n\tCount Count4\n}\n\ntype COMMIT4resok struct {\n\tWriteverf Verifier4\n}\n\ntype COMMIT4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *COMMIT4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * CREATE: Create a non-regular file\n */\ntype Createtype4 struct {\n\t// The union discriminant Type selects among the following arms:\n\t//   NF4LNK:\n\t//      Linkdata() *Linktext4\n\t//   NF4BLK, NF4CHR:\n\t//      Devdata() *Specdata4\n\t//   NF4SOCK, NF4FIFO, NF4DIR:\n\t//      void\n\t//   default:\n\t//      void\n\tType Nfs_ftype4\n\tU interface{}\n}\n\ntype CREATE4args struct {\n\t/* CURRENT_FH: directory for creation */\n\tObjtype Createtype4\n\tObjname Component4\n\tCreateattrs Fattr4\n}\n\ntype CREATE4resok struct {\n\tCinfo Change_info4\n\t/* attributes set */\n\tAttrset Bitmap4\n}\n\ntype CREATE4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *CREATE4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * DELEGPURGE: Purge Delegations Awaiting Recovery\n */\ntype DELEGPURGE4args struct {\n\tClientid Clientid4\n}\n\ntype DELEGPURGE4res struct {\n\tStatus Nfsstat4\n}\n\n/*\n * DELEGRETURN: Return a delegation\n */\ntype DELEGRETURN4args struct {\n\t/* CURRENT_FH: delegated file */\n\tDeleg_stateid Stateid4\n}\n\ntype DELEGRETURN4res struct {\n\tStatus Nfsstat4\n}\n\n/*\n * GETATTR: Get file attributes\n */\ntype GETATTR4args struct {\n\t/* CURRENT_FH: directory or file */\n\tAttr_request Bitmap4\n}\n\ntype GETATTR4resok struct {\n\tObj_attributes Fattr4\n}\n\ntype GETATTR4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *GETATTR4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * GETFH: Get current filehandle\n */\ntype GETFH4resok struct {\n\tObject Nfs_fh4\n}\n\ntype GETFH4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *GETFH4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * LINK: Create link to an object\n */\ntype LINK4args struct {\n\t/* CURRENT_FH: target directory */\n\tNewname Component4\n}\n\ntype LINK4resok struct {\n\tCinfo Change_info4\n}\n\ntype LINK4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *LINK4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * For LOCK, transition from open_owner to new lock_owner\n */\ntype Open_to_lock_owner4 struct {\n\tOpen_seqid Seqid4\n\tOpen_stateid Stateid4\n\tLock_seqid Seqid4\n\tLock_owner Lock_owner4\n}\n\n/*\n * For LOCK, existing lock_owner continues to request file locks\n */\ntype Exist_lock_owner4 struct {\n\tLock_stateid Stateid4\n\tLock_seqid Seqid4\n}\n\ntype Locker4 struct {\n\t// The union discriminant New_lock_owner selects among the following arms:\n\t//   true:\n\t//      Open_owner() *Open_to_lock_owner4\n\t//   false:\n\t//      Lock_owner() *Exist_lock_owner4\n\tNew_lock_owner bool\n\tU interface{}\n}\n\n/*\n * LOCK/LOCKT/LOCKU: Record lock management\n */\ntype LOCK4args struct {\n\t/* CURRENT_FH: file */\n\tLocktype Nfs_lock_type4\n\tReclaim bool\n\tOffset Offset4\n\tLength Length4\n\tLocker Locker4\n}\n\ntype LOCK4denied struct {\n\tOffset Offset4\n\tLength Length4\n\tLocktype Nfs_lock_type4\n\tOwner Lock_owner4\n}\n\ntype LOCK4resok struct {\n\tLock_stateid Stateid4\n}\n\ntype LOCK4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *LOCK4resok\n\t//   NFS4ERR_DENIED:\n\t//      Denied() *LOCK4denied\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\ntype LOCKT4args struct {\n\t/* CURRENT_FH: file */\n\tLocktype Nfs_lock_type4\n\tOffset Offset4\n\tLength Length4\n\tOwner Lock_owner4\n}\n\ntype LOCKT4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4ERR_DENIED:\n\t//      Denied() *LOCK4denied\n\t//   NFS4_OK:\n\t//      void\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\ntype LOCKU4args struct {\n\t/* CURRENT_FH: file */\n\tLocktype Nfs_lock_type4\n\tSeqid Seqid4\n\tLock_stateid Stateid4\n\tOffset Offset4\n\tLength Length4\n}\n\ntype LOCKU4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Lock_stateid() *Stateid4\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * LOOKUP: Lookup filename\n */\ntype LOOKUP4args struct {\n\t/* CURRENT_FH: directory */\n\tObjname Component4\n}\n\ntype LOOKUP4res struct {\n\t/* CURRENT_FH: object */\n\tStatus Nfsstat4\n}\n\n/*\n * LOOKUPP: Lookup parent directory\n */\ntype LOOKUPP4res struct {\n\t/* CURRENT_FH: directory */\n\tStatus Nfsstat4\n}\n\n/*\n * NVERIFY: Verify attributes different\n */\ntype NVERIFY4args struct {\n\t/* CURRENT_FH: object */\n\tObj_attributes Fattr4\n}\n\ntype NVERIFY4res struct {\n\tStatus Nfsstat4\n}\n\n/*\n * Various definitions for OPEN\n */\ntype Createmode4 int32\nconst (\n\tUNCHECKED4 Createmode4 = 0\n\tGUARDED4 Createmode4 = 1\n\tEXCLUSIVE4 Createmode4 = 2\n)\n\ntype Createhow4 struct {\n\t// The union discriminant Mode selects among the following arms:\n\t//   UNCHECKED4, GUARDED4:\n\t//      Createattrs() *Fattr4\n\t//   EXCLUSIVE4:\n\t//      Createverf() *Verifier4\n\tMode Createmode4\n\tU interface{}\n}\n\ntype Opentype4 int32\nconst (\n\tOPEN4_NOCREATE Opentype4 = 0\n\tOPEN4_CREATE Opentype4 = 1\n)\n\ntype Openflag4 struct {\n\t// The union discriminant Opentype selects among the following arms:\n\t//   OPEN4_CREATE:\n\t//      How() *Createhow4\n\t//   default:\n\t//      void\n\tOpentype Opentype4\n\tU interface{}\n}\n\n/* Next definitions used for OPEN delegation */\ntype Limit_by4 int32\nconst (\n\tNFS_LIMIT_SIZE Limit_by4 = 1\n\tNFS_LIMIT_BLOCKS Limit_by4 = 2\n)\n\ntype Nfs_modified_limit4 struct {\n\tNum_blocks Uint32_t\n\tBytes_per_block Uint32_t\n}\n\ntype Nfs_space_limit4 struct {\n\t// The union discriminant Limitby selects among the following arms:\n\t//   NFS_LIMIT_SIZE:\n\t//      Filesize() *Uint64_t\n\t//   NFS_LIMIT_BLOCKS:\n\t//      Mod_blocks() *Nfs_modified_limit4\n\tLimitby Limit_by4\n\tU interface{}\n}\n\n/*\n * Share Access and Deny constants for open argument\n */\nconst OPEN4_SHARE_ACCESS_READ = 0x00000001\n\nconst OPEN4_SHARE_ACCESS_WRITE = 0x00000002\n\nconst OPEN4_SHARE_ACCESS_BOTH = 0x00000003\n\nconst OPEN4_SHARE_DENY_NONE = 0x00000000\n\nconst OPEN4_SHARE_DENY_READ = 0x00000001\n\nconst OPEN4_SHARE_DENY_WRITE = 0x00000002\n\nconst OPEN4_SHARE_DENY_BOTH = 0x00000003\n\ntype Open_delegation_type4 int32\nconst (\n\tOPEN_DELEGATE_NONE Open_delegation_type4 = 0\n\tOPEN_DELEGATE_READ Open_delegation_type4 = 1\n\tOPEN_DELEGATE_WRITE Open_delegation_type4 = 2\n)\n\ntype Open_claim_type4 int32\nconst (\n\tCLAIM_NULL Open_claim_type4 = 0\n\tCLAIM_PREVIOUS Open_claim_type4 = 1\n\tCLAIM_DELEGATE_CUR Open_claim_type4 = 2\n\tCLAIM_DELEGATE_PREV Open_claim_type4 = 3\n\t/* new to v4.1 */\n\tCLAIM_FH Open_claim_type4 = 4\n\t/* new to v4.1 */\n\tCLAIM_DELEG_CUR_FH Open_claim_type4 = 5\n\t/* new to v4.1 */\n\tCLAIM_DELEG_PREV_FH Open_claim_type4 = 6\n)\n\ntype Open_claim_delegate_cur4 struct {\n\tDelegate_stateid Stateid4\n\tFile Component4\n}\n\ntype Open_claim4 struct {\n\t// The union discriminant Claim selects among the following arms:\n\t//   CLAIM_NULL:\n\t//      File() *Component4\n\t//   CLAIM_PREVIOUS:\n\t//      Delegate_type() *Open_delegation_type4\n\t//   CLAIM_DELEGATE_CUR:\n\t//      Delegate_cur_info() *Open_claim_delegate_cur4\n\t//   CLAIM_DELEGATE_PREV:\n\t//      File_delegate_prev() *Component4\n\tClaim Open_claim_type4\n\tU interface{}\n}\n\n/*\n * OPEN: Open a file, potentially receiving an open delegation\n */\ntype OPEN4args struct {\n\tSeqid Seqid4\n\tShare_access Uint32_t\n\tShare_deny Uint32_t\n\tOwner Open_owner4\n\tOpenhow Openflag4\n\tClaim Open_claim4\n}\n\ntype Open_read_delegation4 struct {\n\t/* Stateid for delegation*/\n\tStateid Stateid4\n\tRecall bool\n\tPermissions Nfsace4\n}\n\ntype Open_write_delegation4 struct {\n\t/* Stateid for delegation */\n\tStateid Stateid4\n\tRecall bool\n\tSpace_limit Nfs_space_limit4\n\tPermissions Nfsace4\n}\n\ntype Open_delegation4 struct {\n\t// The union discriminant Delegation_type selects among the following arms:\n\t//   OPEN_DELEGATE_NONE:\n\t//      void\n\t//   OPEN_DELEGATE_READ:\n\t//      Read() *Open_read_delegation4\n\t//   OPEN_DELEGATE_WRITE:\n\t//      Write() *Open_write_delegation4\n\tDelegation_type Open_delegation_type4\n\tU interface{}\n}\n\n/* Client must confirm open */\nconst OPEN4_RESULT_CONFIRM = 0x00000002\n\n/* Type of file locking behavior at the server */\nconst OPEN4_RESULT_LOCKTYPE_POSIX = 0x00000004\n\ntype OPEN4resok struct {\n\t/* Stateid for open */\n\tStateid Stateid4\n\t/* Directory Change Info */\n\tCinfo Change_info4\n\t/* Result flags */\n\tRflags Uint32_t\n\t/* attribute set for create*/\n\tAttrset Bitmap4\n\tDelegation Open_delegation4\n}\n\ntype OPEN4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *OPEN4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * OPENATTR: open named attributes directory\n */\ntype OPENATTR4args struct {\n\t/* CURRENT_FH: object */\n\tCreatedir bool\n}\n\ntype OPENATTR4res struct {\n\t/* CURRENT_FH: named attr directory */\n\tStatus Nfsstat4\n}\n\n/*\n * OPEN_CONFIRM: confirm the open\n */\ntype OPEN_CONFIRM4args struct {\n\t/* CURRENT_FH: opened file */\n\tOpen_stateid Stateid4\n\tSeqid Seqid4\n}\n\ntype OPEN_CONFIRM4resok struct {\n\tOpen_stateid Stateid4\n}\n\ntype OPEN_CONFIRM4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *OPEN_CONFIRM4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * OPEN_DOWNGRADE: downgrade the access/deny for a file\n */\ntype OPEN_DOWNGRADE4args struct {\n\t/* CURRENT_FH: opened file */\n\tOpen_stateid Stateid4\n\tSeqid Seqid4\n\tShare_access Uint32_t\n\tShare_deny Uint32_t\n}\n\ntype OPEN_DOWNGRADE4resok struct {\n\tOpen_stateid Stateid4\n}\n\ntype OPEN_DOWNGRADE4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *OPEN_DOWNGRADE4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * PUTFH: Set current filehandle\n */\ntype PUTFH4args struct {\n\tObject Nfs_fh4\n}\n\ntype PUTFH4res struct {\n\t/* CURRENT_FH: */\n\tStatus Nfsstat4\n}\n\n/*\n * PUTPUBFH: Set public filehandle\n */\ntype PUTPUBFH4res struct {\n\t/* CURRENT_FH: public fh */\n\tStatus Nfsstat4\n}\n\n/*\n * PUTROOTFH: Set root filehandle\n */\ntype PUTROOTFH4res struct {\n\t/* CURRENT_FH: root fh */\n\tStatus Nfsstat4\n}\n\n/*\n * READ: Read from file\n */\ntype READ4args struct {\n\t/* CURRENT_FH: file */\n\tStateid Stateid4\n\tOffset Offset4\n\tCount Count4\n}\n\ntype READ4resok struct {\n\tEof bool\n\tData []byte\n}\n\ntype READ4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *READ4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * READDIR: Read directory\n */\ntype READDIR4args struct {\n\t/* CURRENT_FH: directory */\n\tCookie Nfs_cookie4\n\tCookieverf Verifier4\n\tDircount Count4\n\tMaxcount Count4\n\tAttr_request Bitmap4\n}\n\ntype Entry4 struct {\n\tCookie Nfs_cookie4\n\tName Component4\n\tAttrs Fattr4\n\tNextentry *Entry4\n}\n\ntype Dirlist4 struct {\n\tEntries *Entry4\n\tEof bool\n}\n\ntype READDIR4resok struct {\n\tCookieverf Verifier4\n\tReply Dirlist4\n}\n\ntype READDIR4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *READDIR4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * READLINK: Read symbolic link\n */\ntype READLINK4resok struct {\n\tLink Linktext4\n}\n\ntype READLINK4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *READLINK4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * REMOVE: Remove filesystem object\n */\ntype REMOVE4args struct {\n\t/* CURRENT_FH: directory */\n\tTarget Component4\n}\n\ntype REMOVE4resok struct {\n\tCinfo Change_info4\n}\n\ntype REMOVE4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *REMOVE4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * RENAME: Rename directory entry\n */\ntype RENAME4args struct {\n\t/* SAVED_FH: source directory */\n\tOldname Component4\n\tNewname Component4\n}\n\ntype RENAME4resok struct {\n\tSource_cinfo Change_info4\n\tTarget_cinfo Change_info4\n}\n\ntype RENAME4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *RENAME4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * RENEW: Renew a Lease\n */\ntype RENEW4args struct {\n\tClientid Clientid4\n}\n\ntype RENEW4res struct {\n\tStatus Nfsstat4\n}\n\ntype RESTOREFH4res struct {\n\t/* CURRENT_FH: value of saved fh */\n\tStatus Nfsstat4\n}\n\n/*\n * SAVEFH: Save current filehandle\n */\ntype SAVEFH4res struct {\n\t/* SAVED_FH: value of current fh */\n\tStatus Nfsstat4\n}\n\n/*\n * SECINFO: Obtain Available Security Mechanisms\n */\ntype SECINFO4args struct {\n\tName Component4\n}\n\ntype Rpc_gss_svc_t int32\nconst (\n\tRPC_GSS_SVC_NONE Rpc_gss_svc_t = 1\n\tRPC_GSS_SVC_INTEGRITY Rpc_gss_svc_t = 2\n\tRPC_GSS_SVC_PRIVACY Rpc_gss_svc_t = 3\n)\n\ntype Rpcsec_gss_info struct {\n\tOid Sec_oid4\n\tQop Qop4\n\tService Rpc_gss_svc_t\n}\n\nconst RPCSEC_GSS = 6\n\ntype Secinfo4 struct {\n\t// The union discriminant Flavor selects among the following arms:\n\t//   RPCSEC_GSS:\n\t//      Flavor_info() *Rpcsec_gss_info\n\t//   default:\n\t//      void\n\tFlavor Uint32_t\n\tU interface{}\n}\n\ntype SECINFO4resok = []Secinfo4\n\ntype SECINFO4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *SECINFO4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * SETATTR: Set attributes\n */\ntype SETATTR4args struct {\n\t/* CURRENT_FH: target object */\n\tStateid Stateid4\n\tObj_attributes Fattr4\n}\n\ntype SETATTR4res struct {\n\tStatus Nfsstat4\n\tAttrsset Bitmap4\n}\n\n/*\n * SETCLIENTID\n */\ntype SETCLIENTID4args struct {\n\tClient Nfs_client_id4\n\tCallback Cb_client4\n\tCallback_ident Uint32_t\n}\n\ntype SETCLIENTID4resok struct {\n\tClientid Clientid4\n\tSetclientid_confirm Verifier4\n}\n\ntype SETCLIENTID4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *SETCLIENTID4resok\n\t//   NFS4ERR_CLID_INUSE:\n\t//      Client_using() *Clientaddr4\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\ntype SETCLIENTID_CONFIRM4args struct {\n\tClientid Clientid4\n\tSetclientid_confirm Verifier4\n}\n\ntype SETCLIENTID_CONFIRM4res struct {\n\tStatus Nfsstat4\n}\n\n/*\n * VERIFY: Verify attributes same\n */\ntype VERIFY4args struct {\n\t/* CURRENT_FH: object */\n\tObj_attributes Fattr4\n}\n\ntype VERIFY4res struct {\n\tStatus Nfsstat4\n}\n\n/*\n * WRITE: Write to file\n */\ntype Stable_how4 int32\nconst (\n\tUNSTABLE4 Stable_how4 = 0\n\tDATA_SYNC4 Stable_how4 = 1\n\tFILE_SYNC4 Stable_how4 = 2\n)\n\ntype WRITE4args struct {\n\t/* CURRENT_FH: file */\n\tStateid Stateid4\n\tOffset Offset4\n\tStable Stable_how4\n\tData []byte\n}\n\ntype WRITE4resok struct {\n\tCount Count4\n\tCommitted Stable_how4\n\tWriteverf Verifier4\n}\n\ntype WRITE4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *WRITE4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * RELEASE_LOCKOWNER: Notify server to release lockowner\n */\ntype RELEASE_LOCKOWNER4args struct {\n\tLock_owner Lock_owner4\n}\n\ntype RELEASE_LOCKOWNER4res struct {\n\tStatus Nfsstat4\n}\n\ntype Callback_sec_parms4 struct {\n\t// The union discriminant Cb_secflavor selects among the following arms:\n\t//   AUTH_NONE:\n\t//      void\n\t//   AUTH_SYS:\n\t//      Cbsp_sys_cred() *Authsys_parms\n\tCb_secflavor Uint32_t\n\tU interface{}\n}\n\n/*\n * CREATE_SESSION\n */\ntype Channel_attrs4 struct {\n\tCa_headerpadsize Count4\n\tCa_maxrequestsize Count4\n\tCa_maxresponsesize Count4\n\tCa_maxresponsesize_cached Count4\n\tCa_maxoperations Count4\n\tCa_maxrequests Count4\n\tCa_rdma_ird []Uint32_t // bound 1\n}\n\nconst CREATE_SESSION4_FLAG_PERSIST = 0x00000001\n\nconst CREATE_SESSION4_FLAG_CONN_BACK_CHAN = 0x00000002\n\nconst CREATE_SESSION4_FLAG_CONN_RDMA = 0x00000004\n\ntype CREATE_SESSION4args struct {\n\tCsa_clientid Clientid4\n\tCsa_sequence Sequenceid4\n\tCsa_flags Uint32_t\n\tCsa_fore_chan_attrs Channel_attrs4\n\tCsa_back_chan_attrs Channel_attrs4\n\tCsa_cb_program Uint32_t\n\tCsa_sec_parms []Callback_sec_parms4\n}\n\ntype CREATE_SESSION4resok struct {\n\tCsr_sessionid Sessionid4\n\tCsr_sequence Sequenceid4\n\tCsr_flags Uint32_t\n\tCsr_fore_chan_attrs Channel_attrs4\n\tCsr_back_chan_attrs Channel_attrs4\n}\n\ntype CREATE_SESSION4res struct {\n\t// The union discriminant Csr_status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Csr_resok4() *CREATE_SESSION4resok\n\t//   default:\n\t//      void\n\tCsr_status Nfsstat4\n\tU interface{}\n}\n\n/*\n * DESTROY_SESSION\n */\ntype DESTROY_SESSION4args struct {\n\tDsa_sessionid Sessionid4\n}\n\ntype DESTROY_SESSION4res struct {\n\tDsr_status Nfsstat4\n}\n\n/*\n * FREE_STATEID\n */\ntype FREE_STATEID4args struct {\n\tFsa_stateid Stateid4\n}\n\ntype FREE_STATEID4res struct {\n\tFsr_status Nfsstat4\n}\n\n/*\n * GET_DIR_DELEGATION\n */\ntype Attr_notice4 = Nfstime4\n\ntype GET_DIR_DELEGATION4args struct {\n\tGdda_signal_deleg_avail bool\n\tGdda_notification_types Bitmap4\n\tGdda_child_attr_delay Attr_notice4\n\tGdda_dir_attr_delay Attr_notice4\n\tGdda_child_attributes Bitmap4\n\tGdda_dir_attributes Bitmap4\n}\n\ntype GET_DIR_DELEGATION4resok struct {\n\tGddr_cookieverf Verifier4\n\tGddr_stateid Stateid4\n\tGddr_notification Bitmap4\n\tGddr_child_attributes Bitmap4\n\tGddr_dir_attributes Bitmap4\n}\n\ntype Gddrnf4_status int32\nconst (\n\tGDD4_OK Gddrnf4_status = 0\n\tGDD4_UNAVAIL Gddrnf4_status = 1\n)\n\ntype GET_DIR_DELEGATION4res_non_fatal struct {\n\t// The union discriminant Gddrnf_status selects among the following arms:\n\t//   GDD4_OK:\n\t//      Gddrnf_resok4() *GET_DIR_DELEGATION4resok\n\t//   GDD4_UNAVAIL:\n\t//      Gddrnf_will_signal_deleg_avail() *bool\n\tGddrnf_status Gddrnf4_status\n\tU interface{}\n}\n\ntype GET_DIR_DELEGATION4res struct {\n\t// The union discriminant Gddr_status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Gddr_res_non_fatal4() *GET_DIR_DELEGATION4res_non_fatal\n\t//   default:\n\t//      void\n\tGddr_status Nfsstat4\n\tU interface{}\n}\n\n/*\n * GETDEVICEINFO\n */\ntype GETDEVICEINFO4args struct {\n\tGdia_device_id Deviceid4\n\tGdia_layout_type Layouttype4\n\tGdia_maxcount Count4\n\tGdia_notify_types Bitmap4\n}\n\ntype GETDEVICEINFO4resok struct {\n\tGdir_device_addr Device_addr4\n\tGdir_notification Bitmap4\n}\n\ntype GETDEVICEINFO4res struct {\n\t// The union discriminant Gdir_status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Gdir_resok4() *GETDEVICEINFO4resok\n\t//   NFS4ERR_TOOSMALL:\n\t//      Gdir_mincount() *Count4\n\t//   default:\n\t//      void\n\tGdir_status Nfsstat4\n\tU interface{}\n}\n\n/*\n * GETDEVICELIST\n */\ntype GETDEVICELIST4args struct {\n\tGdla_layout_type Layouttype4\n\tGdla_maxdevices Count4\n\tGdla_cookie Nfs_cookie4\n\tGdla_cookieverf Verifier4\n}\n\ntype GETDEVICELIST4resok struct {\n\tGdlr_cookie Nfs_cookie4\n\tGdlr_cookieverf Verifier4\n\tGdlr_deviceid_list []Deviceid4\n\tGdlr_eof bool\n}\n\ntype GETDEVICELIST4res struct {\n\t// The union discriminant Gdlr_status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Gdlr_resok4() *GETDEVICELIST4resok\n\t//   default:\n\t//      void\n\tGdlr_status Nfsstat4\n\tU interface{}\n}\n\n/*\n * LAYOUTCOMMIT\n */\ntype Newtime4 struct {\n\t// The union discriminant Nt_timechanged selects among the following arms:\n\t//   true:\n\t//      Nt_time() *Nfstime4\n\t//   false:\n\t//      void\n\tNt_timechanged bool\n\tU interface{}\n}\n\ntype Newoffset4 struct {\n\t// The union discriminant No_newoffset selects among the following arms:\n\t//   true:\n\t//      No_offset() *Offset4\n\t//   false:\n\t//      void\n\tNo_newoffset bool\n\tU interface{}\n}\n\ntype LAYOUTCOMMIT4args struct {\n\tLoca_offset Offset4\n\tLoca_length Length4\n\tLoca_reclaim bool\n\tLoca_stateid Stateid4\n\tLoca_last_write_offset Newoffset4\n\tLoca_time_modify Newtime4\n\tLoca_layoutupdate Layoutupdate4\n}\n\ntype Newsize4 struct {\n\t// The union discriminant Ns_sizechanged selects among the following arms:\n\t//   true:\n\t//      Ns_size() *Length4\n\t//   false:\n\t//      void\n\tNs_sizechanged bool\n\tU interface{}\n}\n\ntype LAYOUTCOMMIT4resok struct {\n\tLocr_newsize Newsize4\n}\n\ntype LAYOUTCOMMIT4res struct {\n\t// The union discriminant Locr_status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Locr_resok4() *LAYOUTCOMMIT4resok\n\t//   default:\n\t//      void\n\tLocr_status Nfsstat4\n\tU interface{}\n}\n\n/*\n * LAYOUTGET\n */\ntype LAYOUTGET4args struct {\n\tLoga_signal_layout_avail bool\n\tLoga_layout_type Layouttype4\n\tLoga_iomode Layoutiomode4\n\tLoga_offset Offset4\n\tLoga_length Length4\n\tLoga_minlength Length4\n\tLoga_stateid Stateid4\n\tLoga_maxcount Count4\n}\n\ntype LAYOUTGET4resok struct {\n\tLogr_return_on_close bool\n\tLogr_stateid Stateid4\n\tLogr_layout []Layout4\n}\n\ntype LAYOUTGET4res struct {\n\t// The union discriminant Logr_status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Logr_resok4() *LAYOUTGET4resok\n\t//   NFS4ERR_LAYOUTTRYLATER:\n\t//      Logr_will_signal_layout_avail() *bool\n\t//   default:\n\t//      void\n\tLogr_status Nfsstat4\n\tU interface{}\n}\n\n/*\n * LAYOUTRETURN\n */\nconst LAYOUT4_RET_REC_FILE = 1\n\nconst LAYOUT4_RET_REC_FSID = 2\n\nconst LAYOUT4_RET_REC_ALL = 3\n\ntype Layoutreturn_type4 int32\nconst (\n\tLAYOUTRETURN4_FILE Layoutreturn_type4 = Layoutreturn_type4(LAYOUT4_RET_REC_FILE)\n\tLAYOUTRETURN4_FSID Layoutreturn_type4 = Layoutreturn_type4(LAYOUT4_RET_REC_FSID)\n\tLAYOUTRETURN4_ALL Layoutreturn_type4 = Layoutreturn_type4(LAYOUT4_RET_REC_ALL)\n)\n\ntype Layoutreturn_file4 struct {\n\tLrf_offset Offset4\n\tLrf_length Length4\n\tLrf_stateid Stateid4\n\tLrf_body []byte\n}\n\ntype Layoutreturn4 struct {\n\t// The union discriminant Lr_returntype selects among the following arms:\n\t//   LAYOUTRETURN4_FILE:\n\t//      Lr_layout() *Layoutreturn_file4\n\t//   default:\n\t//      void\n\tLr_returntype Layoutreturn_type4\n\tU interface{}\n}\n\ntype LAYOUTRETURN4args struct {\n\tLora_reclaim bool\n\tLora_layout_type Layouttype4\n\tLora_iomode Layoutiomode4\n\tLora_layoutreturn Layoutreturn4\n}\n\ntype Layoutreturn_stateid struct {\n\t// The union discriminant Lrs_present selects among the following arms:\n\t//   true:\n\t//      Lrs_stateid() *Stateid4\n\t//   false:\n\t//      void\n\tLrs_present bool\n\tU interface{}\n}\n\ntype LAYOUTRETURN4res struct {\n\t// The union discriminant Lorr_status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Lorr_stateid() *Layoutreturn_stateid\n\t//   default:\n\t//      void\n\tLorr_status Nfsstat4\n\tU interface{}\n}\n\n/*\n * SECINFO_NO_NAME\n */\ntype Secinfo_style4 int32\nconst (\n\tSECINFO_STYLE4_CURRENT_FH Secinfo_style4 = 0\n\tSECINFO_STYLE4_PARENT Secinfo_style4 = 1\n)\n\ntype SECINFO_NO_NAME4args = Secinfo_style4\n\ntype SECINFO_NO_NAME4res = SECINFO4res\n\n/*\n * SEQUENCE\n */\ntype SEQUENCE4args struct {\n\tSa_sessionid Sessionid4\n\tSa_sequenceid Sequenceid4\n\tSa_slotid Slotid4\n\tSa_highest_slotid Slotid4\n\tSa_cachethis bool\n}\n\nconst SEQ4_STATUS_CB_PATH_DOWN = 0x00000001\n\nconst SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRING = 0x00000002\n\nconst SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRED = 0x00000004\n\nconst SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED = 0x00000008\n\nconst SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED = 0x00000010\n\nconst SEQ4_STATUS_ADMIN_STATE_REVOKED = 0x00000020\n\nconst SEQ4_STATUS_RECALLABLE_STATE_REVOKED = 0x00000040\n\nconst SEQ4_STATUS_LEASE_MOVED = 0x00000080\n\nconst SEQ4_STATUS_RESTART_RECLAIM_NEEDED = 0x00000100\n\nconst SEQ4_STATUS_CB_PATH_DOWN_SESSION = 0x00000200\n\nconst SEQ4_STATUS_BACKCHANNEL_FAULT = 0x00000400\n\nconst SEQ4_STATUS_DEVID_CHANGED = 0x00000800\n\nconst SEQ4_STATUS_DEVID_DELETED = 0x00001000\n\ntype SEQUENCE4resok struct {\n\tSr_sessionid Sessionid4\n\tSr_sequenceid Sequenceid4\n\tSr_slotid Slotid4\n\tSr_highest_slotid Slotid4\n\tSr_target_highest_slotid Slotid4\n\tSr_status_flags Uint32_t\n}\n\ntype SEQUENCE4res struct {\n\t// The union discriminant Sr_status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Sr_resok4() *SEQUENCE4resok\n\t//   default:\n\t//      void\n\tSr_status Nfsstat4\n\tU interface{}\n}\n\n/*\n * SET_SSV\n */\ntype Ssa_digest_input4 struct {\n\tSdi_seqargs SEQUENCE4args\n}\n\ntype SET_SSV4args struct {\n\tSsa_ssv []byte\n\tSsa_digest []byte\n}\n\ntype Ssr_digest_input4 struct {\n\tSdi_seqres SEQUENCE4res\n}\n\ntype SET_SSV4resok struct {\n\tSsr_digest []byte\n}\n\ntype SET_SSV4res struct {\n\t// The union discriminant Ssr_status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Ssr_resok4() *SET_SSV4resok\n\t//   default:\n\t//      void\n\tSsr_status Nfsstat4\n\tU interface{}\n}\n\n/*\n * TEST_STATEID\n */\ntype TEST_STATEID4args struct {\n\tTs_stateids []Stateid4\n}\n\ntype TEST_STATEID4resok struct {\n\tTsr_status_codes []Nfsstat4\n}\n\ntype TEST_STATEID4res struct {\n\t// The union discriminant Tsr_status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Tsr_resok4() *TEST_STATEID4resok\n\t//   default:\n\t//      void\n\tTsr_status Nfsstat4\n\tU interface{}\n}\n\n/*\n * WANT_DELEGATION\n */\ntype Deleg_claim4 struct {\n\t// The union discriminant Dc_claim selects among the following arms:\n\t//   CLAIM_FH:\n\t//      void\n\t//   CLAIM_DELEG_PREV_FH:\n\t//      void\n\t//   CLAIM_PREVIOUS:\n\t//      Dc_delegate_type() *Open_delegation_type4\n\tDc_claim Open_claim_type4\n\tU interface{}\n}\n\ntype WANT_DELEGATION4args struct {\n\tWda_want Uint32_t\n\tWda_claim Deleg_claim4\n}\n\ntype WANT_DELEGATION4res struct {\n\t// The union discriminant Wdr_status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Wdr_resok4() *Open_delegation4\n\t//   default:\n\t//      void\n\tWdr_status Nfsstat4\n\tU interface{}\n}\n\n/*\n * DESTROY_CLIENTID\n */\ntype DESTROY_CLIENTID4args struct {\n\tDca_clientid Clientid4\n}\n\ntype DESTROY_CLIENTID4res struct {\n\tDcr_status Nfsstat4\n}\n\n/*\n * RECLAIM_COMPLETE\n */\ntype RECLAIM_COMPLETE4args struct {\n\tRca_one_fs bool\n}\n\ntype RECLAIM_COMPLETE4res struct {\n\tRcr_status Nfsstat4\n}\n\n/*\n * ILLEGAL: Response for illegal operation numbers\n */\ntype ILLEGAL4res struct {\n\tStatus Nfsstat4\n}\n\ntype Nfs_opnum4 int32\nconst (\n\tOP_ACCESS Nfs_opnum4 = 3\n\tOP_CLOSE Nfs_opnum4 = 4\n\tOP_COMMIT Nfs_opnum4 = 5\n\tOP_CREATE Nfs_opnum4 = 6\n\tOP_DELEGPURGE Nfs_opnum4 = 7\n\tOP_DELEGRETURN Nfs_opnum4 = 8\n\tOP_GETATTR Nfs_opnum4 = 9\n\tOP_GETFH Nfs_opnum4 = 10\n\tOP_LINK Nfs_opnum4 = 11\n\tOP_LOCK Nfs_opnum4 = 12\n\tOP_LOCKT Nfs_opnum4 = 13\n\tOP_LOCKU Nfs_opnum4 = 14\n\tOP_LOOKUP Nfs_opnum4 = 15\n\tOP_LOOKUPP Nfs_opnum4 = 16\n\tOP_NVERIFY Nfs_opnum4 = 17\n\tOP_OPEN Nfs_opnum4 = 18\n\tOP_OPENATTR Nfs_opnum4 = 19\n\tOP_OPEN_CONFIRM Nfs_opnum4 = 20\n\tOP_OPEN_DOWNGRADE Nfs_opnum4 = 21\n\tOP_PUTFH Nfs_opnum4 = 22\n\tOP_PUTPUBFH Nfs_opnum4 = 23\n\tOP_PUTROOTFH Nfs_opnum4 = 24\n\tOP_READ Nfs_opnum4 = 25\n\tOP_READDIR Nfs_opnum4 = 26\n\tOP_READLINK Nfs_opnum4 = 27\n\tOP_REMOVE Nfs_opnum4 = 28\n\tOP_RENAME Nfs_opnum4 = 29\n\tOP_RENEW Nfs_opnum4 = 30\n\tOP_RESTOREFH Nfs_opnum4 = 31\n\tOP_SAVEFH Nfs_opnum4 = 32\n\tOP_SECINFO Nfs_opnum4 = 33\n\tOP_SETATTR Nfs_opnum4 = 34\n\tOP_SETCLIENTID Nfs_opnum4 = 35\n\tOP_SETCLIENTID_CONFIRM Nfs_opnum4 = 36\n\tOP_VERIFY Nfs_opnum4 = 37\n\tOP_WRITE Nfs_opnum4 = 38\n\tOP_RELEASE_LOCKOWNER Nfs_opnum4 = 39\n\tOP_CREATE_SESSION Nfs_opnum4 = 43\n\tOP_DESTROY_SESSION Nfs_opnum4 = 44\n\tOP_FREE_STATEID Nfs_opnum4 = 45\n\tOP_GET_DIR_DELEGATION Nfs_opnum4 = 46\n\tOP_GETDEVICEINFO Nfs_opnum4 = 47\n\tOP_GETDEVICELIST Nfs_opnum4 = 48\n\tOP_LAYOUTCOMMIT Nfs_opnum4 = 49\n\tOP_LAYOUTGET Nfs_opnum4 = 50\n\tOP_LAYOUTRETURN Nfs_opnum4 = 51\n\tOP_SECINFO_NO_NAME Nfs_opnum4 = 52\n\tOP_SEQUENCE Nfs_opnum4 = 53\n\tOP_SET_SSV Nfs_opnum4 = 54\n\tOP_TEST_STATEID Nfs_opnum4 = 55\n\tOP_WANT_DELEGATION Nfs_opnum4 = 56\n\tOP_DESTROY_CLIENTID Nfs_opnum4 = 57\n\tOP_RECLAIM_COMPLETE Nfs_opnum4 = 58\n\tOP_ILLEGAL Nfs_opnum4 = 10044\n)\n\ntype Nfs_argop4 struct {\n\t// The union discriminant Argop selects among the following arms:\n\t//   OP_ACCESS:\n\t//      Opaccess() *ACCESS4args\n\t//   OP_CLOSE:\n\t//      Opclose() *CLOSE4args\n\t//   OP_COMMIT:\n\t//      Opcommit() *COMMIT4args\n\t//   OP_CREATE:\n\t//      Opcreate() *CREATE4args\n\t//   OP_DELEGPURGE:\n\t//      Opdelegpurge() *DELEGPURGE4args\n\t//   OP_DELEGRETURN:\n\t//      Opdelegreturn() *DELEGRETURN4args\n\t//   OP_GETATTR:\n\t//      Opgetattr() *GETATTR4args\n\t//   OP_GETFH:\n\t//      void\n\t//   OP_LINK:\n\t//      Oplink() *LINK4args\n\t//   OP_LOCK:\n\t//      Oplock() *LOCK4args\n\t//   OP_LOCKT:\n\t//      Oplockt() *LOCKT4args\n\t//   OP_LOCKU:\n\t//      Oplocku() *LOCKU4args\n\t//   OP_LOOKUP:\n\t//      Oplookup() *LOOKUP4args\n\t//   OP_LOOKUPP:\n\t//      void\n\t//   OP_NVERIFY:\n\t//      Opnverify() *NVERIFY4args\n\t//   OP_OPEN:\n\t//      Opopen() *OPEN4args\n\t//   OP_OPENATTR:\n\t//      Opopenattr() *OPENATTR4args\n\t//   OP_OPEN_CONFIRM:\n\t//      Opopen_confirm() *OPEN_CONFIRM4args\n\t//   OP_OPEN_DOWNGRADE:\n\t//      Opopen_downgrade() *OPEN_DOWNGRADE4args\n\t//   OP_PUTFH:\n\t//      Opputfh() *PUTFH4args\n\t//   OP_PUTPUBFH:\n\t//      void\n\t//   OP_PUTROOTFH:\n\t//      void\n\t//   OP_READ:\n\t//      Opread() *READ4args\n\t//   OP_READDIR:\n\t//      Opreaddir() *READDIR4args\n\t//   OP_READLINK:\n\t//      void\n\t//   OP_REMOVE:\n\t//      Opremove() *REMOVE4args\n\t//   OP_RENAME:\n\t//      Oprename() *RENAME4args\n\t//   OP_RENEW:\n\t//      Oprenew() *RENEW4args\n\t//   OP_RESTOREFH:\n\t//      void\n\t//   OP_SAVEFH:\n\t//      void\n\t//   OP_SECINFO:\n\t//      Opsecinfo() *SECINFO4args\n\t//   OP_SETATTR:\n\t//      Opsetattr() *SETATTR4args\n\t//   OP_SETCLIENTID:\n\t//      Opsetclientid() *SETCLIENTID4args\n\t//   OP_SETCLIENTID_CONFIRM:\n\t//      Opsetclientid_confirm() *SETCLIENTID_CONFIRM4args\n\t//   OP_VERIFY:\n\t//      Opverify() *VERIFY4args\n\t//   OP_WRITE:\n\t//      Opwrite() *WRITE4args\n\t//   OP_RELEASE_LOCKOWNER:\n\t//      Oprelease_lockowner() *RELEASE_LOCKOWNER4args\n\t//   OP_CREATE_SESSION:\n\t//      Opcreatesession() *CREATE_SESSION4args\n\t//   OP_DESTROY_SESSION:\n\t//      Opdestroysession() *DESTROY_SESSION4args\n\t//   OP_FREE_STATEID:\n\t//      Opfreestateid() *FREE_STATEID4args\n\t//   OP_GET_DIR_DELEGATION:\n\t//      Opgetdirdelegation() *GET_DIR_DELEGATION4args\n\t//   OP_GETDEVICEINFO:\n\t//      Opgetdeviceinfo() *GETDEVICEINFO4args\n\t//   OP_GETDEVICELIST:\n\t//      Opgetdevicelist() *GETDEVICELIST4args\n\t//   OP_LAYOUTCOMMIT:\n\t//      Oplayoutcommit() *LAYOUTCOMMIT4args\n\t//   OP_LAYOUTGET:\n\t//      Oplayoutget() *LAYOUTGET4args\n\t//   OP_LAYOUTRETURN:\n\t//      Oplayoutreturn() *LAYOUTRETURN4args\n\t//   OP_SECINFO_NO_NAME:\n\t//      Opsecinfononame() *SECINFO_NO_NAME4args\n\t//   OP_SEQUENCE:\n\t//      Opsequence() *SEQUENCE4args\n\t//   OP_SET_SSV:\n\t//      Opsetssv() *SET_SSV4args\n\t//   OP_TEST_STATEID:\n\t//      Opteststateid() *TEST_STATEID4args\n\t//   OP_WANT_DELEGATION:\n\t//      Opwantdelegation() *WANT_DELEGATION4args\n\t//   OP_DESTROY_CLIENTID:\n\t//      Opdestroyclientid() *DESTROY_CLIENTID4args\n\t//   OP_RECLAIM_COMPLETE:\n\t//      Opreclaimcomplete() *RECLAIM_COMPLETE4args\n\t//   OP_ILLEGAL:\n\t//      void\n\tArgop Nfs_opnum4\n\tU interface{}\n}\n\ntype Nfs_resop4 struct {\n\t// The union discriminant Resop selects among the following arms:\n\t//   OP_ACCESS:\n\t//      Opaccess() *ACCESS4res\n\t//   OP_CLOSE:\n\t//      Opclose() *CLOSE4res\n\t//   OP_COMMIT:\n\t//      Opcommit() *COMMIT4res\n\t//   OP_CREATE:\n\t//      Opcreate() *CREATE4res\n\t//   OP_DELEGPURGE:\n\t//      Opdelegpurge() *DELEGPURGE4res\n\t//   OP_DELEGRETURN:\n\t//      Opdelegreturn() *DELEGRETURN4res\n\t//   OP_GETATTR:\n\t//      Opgetattr() *GETATTR4res\n\t//   OP_GETFH:\n\t//      Opgetfh() *GETFH4res\n\t//   OP_LINK:\n\t//      Oplink() *LINK4res\n\t//   OP_LOCK:\n\t//      Oplock() *LOCK4res\n\t//   OP_LOCKT:\n\t//      Oplockt() *LOCKT4res\n\t//   OP_LOCKU:\n\t//      Oplocku() *LOCKU4res\n\t//   OP_LOOKUP:\n\t//      Oplookup() *LOOKUP4res\n\t//   OP_LOOKUPP:\n\t//      Oplookupp() *LOOKUPP4res\n\t//   OP_NVERIFY:\n\t//      Opnverify() *NVERIFY4res\n\t//   OP_OPEN:\n\t//      Opopen() *OPEN4res\n\t//   OP_OPENATTR:\n\t//      Opopenattr() *OPENATTR4res\n\t//   OP_OPEN_CONFIRM:\n\t//      Opopen_confirm() *OPEN_CONFIRM4res\n\t//   OP_OPEN_DOWNGRADE:\n\t//      Opopen_downgrade() *OPEN_DOWNGRADE4res\n\t//   OP_PUTFH:\n\t//      Opputfh() *PUTFH4res\n\t//   OP_PUTPUBFH:\n\t//      Opputpubfh() *PUTPUBFH4res\n\t//   OP_PUTROOTFH:\n\t//      Opputrootfh() *PUTROOTFH4res\n\t//   OP_READ:\n\t//      Opread() *READ4res\n\t//   OP_READDIR:\n\t//      Opreaddir() *READDIR4res\n\t//   OP_READLINK:\n\t//      Opreadlink() *READLINK4res\n\t//   OP_REMOVE:\n\t//      Opremove() *REMOVE4res\n\t//   OP_RENAME:\n\t//      Oprename() *RENAME4res\n\t//   OP_RENEW:\n\t//      Oprenew() *RENEW4res\n\t//   OP_RESTOREFH:\n\t//      Oprestorefh() *RESTOREFH4res\n\t//   OP_SAVEFH:\n\t//      Opsavefh() *SAVEFH4res\n\t//   OP_SECINFO:\n\t//      Opsecinfo() *SECINFO4res\n\t//   OP_SETATTR:\n\t//      Opsetattr() *SETATTR4res\n\t//   OP_SETCLIENTID:\n\t//      Opsetclientid() *SETCLIENTID4res\n\t//   OP_SETCLIENTID_CONFIRM:\n\t//      Opsetclientid_confirm() *SETCLIENTID_CONFIRM4res\n\t//   OP_VERIFY:\n\t//      Opverify() *VERIFY4res\n\t//   OP_WRITE:\n\t//      Opwrite() *WRITE4res\n\t//   OP_RELEASE_LOCKOWNER:\n\t//      Oprelease_lockowner() *RELEASE_LOCKOWNER4res\n\t//   OP_CREATE_SESSION:\n\t//      Opcreatesession() *CREATE_SESSION4res\n\t//   OP_DESTROY_SESSION:\n\t//      Opdestroysession() *DESTROY_SESSION4res\n\t//   OP_FREE_STATEID:\n\t//      Opfreestateid() *FREE_STATEID4res\n\t//   OP_GET_DIR_DELEGATION:\n\t//      Opgetdirdelegation() *GET_DIR_DELEGATION4res\n\t//   OP_GETDEVICEINFO:\n\t//      Opgetdeviceinfo() *GETDEVICEINFO4res\n\t//   OP_GETDEVICELIST:\n\t//      Opgetdevicelist() *GETDEVICELIST4res\n\t//   OP_LAYOUTCOMMIT:\n\t//      Oplayoutcommit() *LAYOUTCOMMIT4res\n\t//   OP_LAYOUTGET:\n\t//      Oplayoutget() *LAYOUTGET4res\n\t//   OP_LAYOUTRETURN:\n\t//      Oplayoutreturn() *LAYOUTRETURN4res\n\t//   OP_SECINFO_NO_NAME:\n\t//      Opsecinfononame() *SECINFO_NO_NAME4res\n\t//   OP_SEQUENCE:\n\t//      Opsequence() *SEQUENCE4res\n\t//   OP_SET_SSV:\n\t//      Opsetssv() *SET_SSV4res\n\t//   OP_TEST_STATEID:\n\t//      Opteststateid() *TEST_STATEID4res\n\t//   OP_WANT_DELEGATION:\n\t//      Opwantdelegation() *WANT_DELEGATION4res\n\t//   OP_DESTROY_CLIENTID:\n\t//      Opdestroyclientid() *DESTROY_CLIENTID4res\n\t//   OP_RECLAIM_COMPLETE:\n\t//      Opreclaimcomplete() *RECLAIM_COMPLETE4res\n\t//   OP_ILLEGAL:\n\t//      Opillegal() *ILLEGAL4res\n\tResop Nfs_opnum4\n\tU interface{}\n}\n\ntype COMPOUND4args struct {\n\tTag Utf8str_cs\n\tMinorversion Uint32_t\n\tArgarray []Nfs_argop4\n}\n\ntype COMPOUND4res struct {\n\tStatus Nfsstat4\n\tTag Utf8str_cs\n\tResarray []Nfs_resop4\n}\n\ntype NFS_V4 interface {\n\tNFSPROC4_NULL()\n\tNFSPROC4_COMPOUND(COMPOUND4args) COMPOUND4res\n}\n\n/*\n * CB_GETATTR: Get Current Attributes\n */\ntype CB_GETATTR4args struct {\n\tFh Nfs_fh4\n\tAttr_request Bitmap4\n}\n\ntype CB_GETATTR4resok struct {\n\tObj_attributes Fattr4\n}\n\ntype CB_GETATTR4res struct {\n\t// The union discriminant Status selects among the following arms:\n\t//   NFS4_OK:\n\t//      Resok4() *CB_GETATTR4resok\n\t//   default:\n\t//      void\n\tStatus Nfsstat4\n\tU interface{}\n}\n\n/*\n * CB_RECALL: Recall an Open Delegation\n */\ntype CB_RECALL4args struct {\n\tStateid Stateid4\n\tTruncate bool\n\tFh Nfs_fh4\n}\n\ntype CB_RECALL4res struct {\n\tStatus Nfsstat4\n}\n\n/*\n * CB_ILLEGAL: Response for illegal operation numbers\n */\ntype CB_ILLEGAL4res struct {\n\tStatus Nfsstat4\n}\n\n/*\n * Various definitions for CB_COMPOUND\n */\ntype Nfs_cb_opnum4 int32\nconst (\n\tOP_CB_GETATTR Nfs_cb_opnum4 = 3\n\tOP_CB_RECALL Nfs_cb_opnum4 = 4\n\tOP_CB_ILLEGAL Nfs_cb_opnum4 = 10044\n)\n\ntype Nfs_cb_argop4 struct {\n\t// The union discriminant Argop selects among the following arms:\n\t//   OP_CB_GETATTR:\n\t//      Opcbgetattr() *CB_GETATTR4args\n\t//   OP_CB_RECALL:\n\t//      Opcbrecall() *CB_RECALL4args\n\t//   OP_CB_ILLEGAL:\n\t//      void\n\tArgop uint32\n\tU interface{}\n}\n\ntype Nfs_cb_resop4 struct {\n\t// The union discriminant Resop selects among the following arms:\n\t//   OP_CB_GETATTR:\n\t//      Opcbgetattr() *CB_GETATTR4res\n\t//   OP_CB_RECALL:\n\t//      Opcbrecall() *CB_RECALL4res\n\t//   OP_CB_ILLEGAL:\n\t//      Opcbillegal() *CB_ILLEGAL4res\n\tResop uint32\n\tU interface{}\n}\n\ntype CB_COMPOUND4args struct {\n\tTag Utf8str_cs\n\tMinorversion Uint32_t\n\tCallback_ident Uint32_t\n\tArgarray []Nfs_cb_argop4\n}\n\ntype CB_COMPOUND4res struct {\n\tStatus Nfsstat4\n\tTag Utf8str_cs\n\tResarray []Nfs_cb_resop4\n}\n\ntype NFS_CB interface {\n\tCB_NULL()\n\tCB_COMPOUND(CB_COMPOUND4args) CB_COMPOUND4res\n}\n\n//\n// Helper types and generated marshaling functions\n//\n\nvar _XdrNames_Nfs_ftype4 = map[int32]string{\n\tint32(NF4REG): \"NF4REG\",\n\tint32(NF4DIR): \"NF4DIR\",\n\tint32(NF4BLK): \"NF4BLK\",\n\tint32(NF4CHR): \"NF4CHR\",\n\tint32(NF4LNK): \"NF4LNK\",\n\tint32(NF4SOCK): \"NF4SOCK\",\n\tint32(NF4FIFO): \"NF4FIFO\",\n\tint32(NF4ATTRDIR): \"NF4ATTRDIR\",\n\tint32(NF4NAMEDATTR): \"NF4NAMEDATTR\",\n}\nvar _XdrValues_Nfs_ftype4 = map[string]int32{\n\t\"NF4REG\": int32(NF4REG),\n\t\"NF4DIR\": int32(NF4DIR),\n\t\"NF4BLK\": int32(NF4BLK),\n\t\"NF4CHR\": int32(NF4CHR),\n\t\"NF4LNK\": int32(NF4LNK),\n\t\"NF4SOCK\": int32(NF4SOCK),\n\t\"NF4FIFO\": int32(NF4FIFO),\n\t\"NF4ATTRDIR\": int32(NF4ATTRDIR),\n\t\"NF4NAMEDATTR\": int32(NF4NAMEDATTR),\n}\nfunc (Nfs_ftype4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Nfs_ftype4\n}\nfunc (v Nfs_ftype4) String() string {\n\tif s, ok := _XdrNames_Nfs_ftype4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Nfs_ftype4#%d\", v)\n}\nfunc (v *Nfs_ftype4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Nfs_ftype4[stok]; ok {\n\t\t\t*v = Nfs_ftype4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Nfs_ftype4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Nfs_ftype4.\", stok))\n\t}\n}\nfunc (v Nfs_ftype4) GetU32() uint32 { return uint32(v) }\nfunc (v *Nfs_ftype4) SetU32(n uint32) { *v = Nfs_ftype4(n) }\nfunc (v *Nfs_ftype4) XdrPointer() interface{} { return v }\nfunc (Nfs_ftype4) XdrTypeName() string { return \"Nfs_ftype4\" }\nfunc (v Nfs_ftype4) XdrValue() interface{} { return v }\nfunc (v *Nfs_ftype4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Nfs_ftype4 = *Nfs_ftype4\nfunc XDR_Nfs_ftype4(v *Nfs_ftype4) *Nfs_ftype4 { return v }\nfunc (v *Nfs_ftype4) XdrInitialize() {\n\tswitch Nfs_ftype4(0) {\n\tcase NF4REG, NF4DIR, NF4BLK, NF4CHR, NF4LNK, NF4SOCK, NF4FIFO, NF4ATTRDIR, NF4NAMEDATTR:\n\tdefault:\n\t\tif *v == Nfs_ftype4(0) { *v = NF4REG }\n\t}\n}\nvar _XdrNames_Nfsstat4 = map[int32]string{\n\tint32(NFS4_OK): \"NFS4_OK\",\n\tint32(NFS4ERR_PERM): \"NFS4ERR_PERM\",\n\tint32(NFS4ERR_NOENT): \"NFS4ERR_NOENT\",\n\tint32(NFS4ERR_IO): \"NFS4ERR_IO\",\n\tint32(NFS4ERR_NXIO): \"NFS4ERR_NXIO\",\n\tint32(NFS4ERR_ACCESS): \"NFS4ERR_ACCESS\",\n\tint32(NFS4ERR_EXIST): \"NFS4ERR_EXIST\",\n\tint32(NFS4ERR_XDEV): \"NFS4ERR_XDEV\",\n\tint32(NFS4ERR_NOTDIR): \"NFS4ERR_NOTDIR\",\n\tint32(NFS4ERR_ISDIR): \"NFS4ERR_ISDIR\",\n\tint32(NFS4ERR_INVAL): \"NFS4ERR_INVAL\",\n\tint32(NFS4ERR_FBIG): \"NFS4ERR_FBIG\",\n\tint32(NFS4ERR_NOSPC): \"NFS4ERR_NOSPC\",\n\tint32(NFS4ERR_ROFS): \"NFS4ERR_ROFS\",\n\tint32(NFS4ERR_MLINK): \"NFS4ERR_MLINK\",\n\tint32(NFS4ERR_NAMETOOLONG): \"NFS4ERR_NAMETOOLONG\",\n\tint32(NFS4ERR_NOTEMPTY): \"NFS4ERR_NOTEMPTY\",\n\tint32(NFS4ERR_DQUOT): \"NFS4ERR_DQUOT\",\n\tint32(NFS4ERR_STALE): \"NFS4ERR_STALE\",\n\tint32(NFS4ERR_BADHANDLE): \"NFS4ERR_BADHANDLE\",\n\tint32(NFS4ERR_BAD_COOKIE): \"NFS4ERR_BAD_COOKIE\",\n\tint32(NFS4ERR_NOTSUPP): \"NFS4ERR_NOTSUPP\",\n\tint32(NFS4ERR_TOOSMALL): \"NFS4ERR_TOOSMALL\",\n\tint32(NFS4ERR_SERVERFAULT): \"NFS4ERR_SERVERFAULT\",\n\tint32(NFS4ERR_BADTYPE): \"NFS4ERR_BADTYPE\",\n\tint32(NFS4ERR_DELAY): \"NFS4ERR_DELAY\",\n\tint32(NFS4ERR_SAME): \"NFS4ERR_SAME\",\n\tint32(NFS4ERR_DENIED): \"NFS4ERR_DENIED\",\n\tint32(NFS4ERR_EXPIRED): \"NFS4ERR_EXPIRED\",\n\tint32(NFS4ERR_LOCKED): \"NFS4ERR_LOCKED\",\n\tint32(NFS4ERR_GRACE): \"NFS4ERR_GRACE\",\n\tint32(NFS4ERR_FHEXPIRED): \"NFS4ERR_FHEXPIRED\",\n\tint32(NFS4ERR_SHARE_DENIED): \"NFS4ERR_SHARE_DENIED\",\n\tint32(NFS4ERR_WRONGSEC): \"NFS4ERR_WRONGSEC\",\n\tint32(NFS4ERR_CLID_INUSE): \"NFS4ERR_CLID_INUSE\",\n\tint32(NFS4ERR_RESOURCE): \"NFS4ERR_RESOURCE\",\n\tint32(NFS4ERR_MOVED): \"NFS4ERR_MOVED\",\n\tint32(NFS4ERR_NOFILEHANDLE): \"NFS4ERR_NOFILEHANDLE\",\n\tint32(NFS4ERR_MINOR_VERS_MISMATCH): \"NFS4ERR_MINOR_VERS_MISMATCH\",\n\tint32(NFS4ERR_STALE_CLIENTID): \"NFS4ERR_STALE_CLIENTID\",\n\tint32(NFS4ERR_STALE_STATEID): \"NFS4ERR_STALE_STATEID\",\n\tint32(NFS4ERR_OLD_STATEID): \"NFS4ERR_OLD_STATEID\",\n\tint32(NFS4ERR_BAD_STATEID): \"NFS4ERR_BAD_STATEID\",\n\tint32(NFS4ERR_BAD_SEQID): \"NFS4ERR_BAD_SEQID\",\n\tint32(NFS4ERR_NOT_SAME): \"NFS4ERR_NOT_SAME\",\n\tint32(NFS4ERR_LOCK_RANGE): \"NFS4ERR_LOCK_RANGE\",\n\tint32(NFS4ERR_SYMLINK): \"NFS4ERR_SYMLINK\",\n\tint32(NFS4ERR_RESTOREFH): \"NFS4ERR_RESTOREFH\",\n\tint32(NFS4ERR_LEASE_MOVED): \"NFS4ERR_LEASE_MOVED\",\n\tint32(NFS4ERR_ATTRNOTSUPP): \"NFS4ERR_ATTRNOTSUPP\",\n\tint32(NFS4ERR_NO_GRACE): \"NFS4ERR_NO_GRACE\",\n\tint32(NFS4ERR_RECLAIM_BAD): \"NFS4ERR_RECLAIM_BAD\",\n\tint32(NFS4ERR_RECLAIM_CONFLICT): \"NFS4ERR_RECLAIM_CONFLICT\",\n\tint32(NFS4ERR_BADXDR): \"NFS4ERR_BADXDR\",\n\tint32(NFS4ERR_LOCKS_HELD): \"NFS4ERR_LOCKS_HELD\",\n\tint32(NFS4ERR_OPENMODE): \"NFS4ERR_OPENMODE\",\n\tint32(NFS4ERR_BADOWNER): \"NFS4ERR_BADOWNER\",\n\tint32(NFS4ERR_BADCHAR): \"NFS4ERR_BADCHAR\",\n\tint32(NFS4ERR_BADNAME): \"NFS4ERR_BADNAME\",\n\tint32(NFS4ERR_BAD_RANGE): \"NFS4ERR_BAD_RANGE\",\n\tint32(NFS4ERR_LOCK_NOTSUPP): \"NFS4ERR_LOCK_NOTSUPP\",\n\tint32(NFS4ERR_OP_ILLEGAL): \"NFS4ERR_OP_ILLEGAL\",\n\tint32(NFS4ERR_DEADLOCK): \"NFS4ERR_DEADLOCK\",\n\tint32(NFS4ERR_FILE_OPEN): \"NFS4ERR_FILE_OPEN\",\n\tint32(NFS4ERR_ADMIN_REVOKED): \"NFS4ERR_ADMIN_REVOKED\",\n\tint32(NFS4ERR_CB_PATH_DOWN): \"NFS4ERR_CB_PATH_DOWN\",\n\tint32(NFS4ERR_BADIOMODE): \"NFS4ERR_BADIOMODE\",\n\tint32(NFS4ERR_BADLAYOUT): \"NFS4ERR_BADLAYOUT\",\n\tint32(NFS4ERR_BAD_SESSION_DIGEST): \"NFS4ERR_BAD_SESSION_DIGEST\",\n\tint32(NFS4ERR_BADSESSION): \"NFS4ERR_BADSESSION\",\n\tint32(NFS4ERR_BADSLOT): \"NFS4ERR_BADSLOT\",\n\tint32(NFS4ERR_COMPLETE_ALREADY): \"NFS4ERR_COMPLETE_ALREADY\",\n\tint32(NFS4ERR_CONN_NOT_BOUND_TO_SESSION): \"NFS4ERR_CONN_NOT_BOUND_TO_SESSION\",\n\tint32(NFS4ERR_DELEG_ALREADY_WANTED): \"NFS4ERR_DELEG_ALREADY_WANTED\",\n\tint32(NFS4ERR_BACK_CHAN_BUSY): \"NFS4ERR_BACK_CHAN_BUSY\",\n\tint32(NFS4ERR_LAYOUTTRYLATER): \"NFS4ERR_LAYOUTTRYLATER\",\n\tint32(NFS4ERR_LAYOUTUNAVAILABLE): \"NFS4ERR_LAYOUTUNAVAILABLE\",\n\tint32(NFS4ERR_NOMATCHING_LAYOUT): \"NFS4ERR_NOMATCHING_LAYOUT\",\n\tint32(NFS4ERR_RECALLCONFLICT): \"NFS4ERR_RECALLCONFLICT\",\n}\nvar _XdrValues_Nfsstat4 = map[string]int32{\n\t\"NFS4_OK\": int32(NFS4_OK),\n\t\"NFS4ERR_PERM\": int32(NFS4ERR_PERM),\n\t\"NFS4ERR_NOENT\": int32(NFS4ERR_NOENT),\n\t\"NFS4ERR_IO\": int32(NFS4ERR_IO),\n\t\"NFS4ERR_NXIO\": int32(NFS4ERR_NXIO),\n\t\"NFS4ERR_ACCESS\": int32(NFS4ERR_ACCESS),\n\t\"NFS4ERR_EXIST\": int32(NFS4ERR_EXIST),\n\t\"NFS4ERR_XDEV\": int32(NFS4ERR_XDEV),\n\t\"NFS4ERR_NOTDIR\": int32(NFS4ERR_NOTDIR),\n\t\"NFS4ERR_ISDIR\": int32(NFS4ERR_ISDIR),\n\t\"NFS4ERR_INVAL\": int32(NFS4ERR_INVAL),\n\t\"NFS4ERR_FBIG\": int32(NFS4ERR_FBIG),\n\t\"NFS4ERR_NOSPC\": int32(NFS4ERR_NOSPC),\n\t\"NFS4ERR_ROFS\": int32(NFS4ERR_ROFS),\n\t\"NFS4ERR_MLINK\": int32(NFS4ERR_MLINK),\n\t\"NFS4ERR_NAMETOOLONG\": int32(NFS4ERR_NAMETOOLONG),\n\t\"NFS4ERR_NOTEMPTY\": int32(NFS4ERR_NOTEMPTY),\n\t\"NFS4ERR_DQUOT\": int32(NFS4ERR_DQUOT),\n\t\"NFS4ERR_STALE\": int32(NFS4ERR_STALE),\n\t\"NFS4ERR_BADHANDLE\": int32(NFS4ERR_BADHANDLE),\n\t\"NFS4ERR_BAD_COOKIE\": int32(NFS4ERR_BAD_COOKIE),\n\t\"NFS4ERR_NOTSUPP\": int32(NFS4ERR_NOTSUPP),\n\t\"NFS4ERR_TOOSMALL\": int32(NFS4ERR_TOOSMALL),\n\t\"NFS4ERR_SERVERFAULT\": int32(NFS4ERR_SERVERFAULT),\n\t\"NFS4ERR_BADTYPE\": int32(NFS4ERR_BADTYPE),\n\t\"NFS4ERR_DELAY\": int32(NFS4ERR_DELAY),\n\t\"NFS4ERR_SAME\": int32(NFS4ERR_SAME),\n\t\"NFS4ERR_DENIED\": int32(NFS4ERR_DENIED),\n\t\"NFS4ERR_EXPIRED\": int32(NFS4ERR_EXPIRED),\n\t\"NFS4ERR_LOCKED\": int32(NFS4ERR_LOCKED),\n\t\"NFS4ERR_GRACE\": int32(NFS4ERR_GRACE),\n\t\"NFS4ERR_FHEXPIRED\": int32(NFS4ERR_FHEXPIRED),\n\t\"NFS4ERR_SHARE_DENIED\": int32(NFS4ERR_SHARE_DENIED),\n\t\"NFS4ERR_WRONGSEC\": int32(NFS4ERR_WRONGSEC),\n\t\"NFS4ERR_CLID_INUSE\": int32(NFS4ERR_CLID_INUSE),\n\t\"NFS4ERR_RESOURCE\": int32(NFS4ERR_RESOURCE),\n\t\"NFS4ERR_MOVED\": int32(NFS4ERR_MOVED),\n\t\"NFS4ERR_NOFILEHANDLE\": int32(NFS4ERR_NOFILEHANDLE),\n\t\"NFS4ERR_MINOR_VERS_MISMATCH\": int32(NFS4ERR_MINOR_VERS_MISMATCH),\n\t\"NFS4ERR_STALE_CLIENTID\": int32(NFS4ERR_STALE_CLIENTID),\n\t\"NFS4ERR_STALE_STATEID\": int32(NFS4ERR_STALE_STATEID),\n\t\"NFS4ERR_OLD_STATEID\": int32(NFS4ERR_OLD_STATEID),\n\t\"NFS4ERR_BAD_STATEID\": int32(NFS4ERR_BAD_STATEID),\n\t\"NFS4ERR_BAD_SEQID\": int32(NFS4ERR_BAD_SEQID),\n\t\"NFS4ERR_NOT_SAME\": int32(NFS4ERR_NOT_SAME),\n\t\"NFS4ERR_LOCK_RANGE\": int32(NFS4ERR_LOCK_RANGE),\n\t\"NFS4ERR_SYMLINK\": int32(NFS4ERR_SYMLINK),\n\t\"NFS4ERR_RESTOREFH\": int32(NFS4ERR_RESTOREFH),\n\t\"NFS4ERR_LEASE_MOVED\": int32(NFS4ERR_LEASE_MOVED),\n\t\"NFS4ERR_ATTRNOTSUPP\": int32(NFS4ERR_ATTRNOTSUPP),\n\t\"NFS4ERR_NO_GRACE\": int32(NFS4ERR_NO_GRACE),\n\t\"NFS4ERR_RECLAIM_BAD\": int32(NFS4ERR_RECLAIM_BAD),\n\t\"NFS4ERR_RECLAIM_CONFLICT\": int32(NFS4ERR_RECLAIM_CONFLICT),\n\t\"NFS4ERR_BADXDR\": int32(NFS4ERR_BADXDR),\n\t\"NFS4ERR_LOCKS_HELD\": int32(NFS4ERR_LOCKS_HELD),\n\t\"NFS4ERR_OPENMODE\": int32(NFS4ERR_OPENMODE),\n\t\"NFS4ERR_BADOWNER\": int32(NFS4ERR_BADOWNER),\n\t\"NFS4ERR_BADCHAR\": int32(NFS4ERR_BADCHAR),\n\t\"NFS4ERR_BADNAME\": int32(NFS4ERR_BADNAME),\n\t\"NFS4ERR_BAD_RANGE\": int32(NFS4ERR_BAD_RANGE),\n\t\"NFS4ERR_LOCK_NOTSUPP\": int32(NFS4ERR_LOCK_NOTSUPP),\n\t\"NFS4ERR_OP_ILLEGAL\": int32(NFS4ERR_OP_ILLEGAL),\n\t\"NFS4ERR_DEADLOCK\": int32(NFS4ERR_DEADLOCK),\n\t\"NFS4ERR_FILE_OPEN\": int32(NFS4ERR_FILE_OPEN),\n\t\"NFS4ERR_ADMIN_REVOKED\": int32(NFS4ERR_ADMIN_REVOKED),\n\t\"NFS4ERR_CB_PATH_DOWN\": int32(NFS4ERR_CB_PATH_DOWN),\n\t\"NFS4ERR_BADIOMODE\": int32(NFS4ERR_BADIOMODE),\n\t\"NFS4ERR_BADLAYOUT\": int32(NFS4ERR_BADLAYOUT),\n\t\"NFS4ERR_BAD_SESSION_DIGEST\": int32(NFS4ERR_BAD_SESSION_DIGEST),\n\t\"NFS4ERR_BADSESSION\": int32(NFS4ERR_BADSESSION),\n\t\"NFS4ERR_BADSLOT\": int32(NFS4ERR_BADSLOT),\n\t\"NFS4ERR_COMPLETE_ALREADY\": int32(NFS4ERR_COMPLETE_ALREADY),\n\t\"NFS4ERR_CONN_NOT_BOUND_TO_SESSION\": int32(NFS4ERR_CONN_NOT_BOUND_TO_SESSION),\n\t\"NFS4ERR_DELEG_ALREADY_WANTED\": int32(NFS4ERR_DELEG_ALREADY_WANTED),\n\t\"NFS4ERR_BACK_CHAN_BUSY\": int32(NFS4ERR_BACK_CHAN_BUSY),\n\t\"NFS4ERR_LAYOUTTRYLATER\": int32(NFS4ERR_LAYOUTTRYLATER),\n\t\"NFS4ERR_LAYOUTUNAVAILABLE\": int32(NFS4ERR_LAYOUTUNAVAILABLE),\n\t\"NFS4ERR_NOMATCHING_LAYOUT\": int32(NFS4ERR_NOMATCHING_LAYOUT),\n\t\"NFS4ERR_RECALLCONFLICT\": int32(NFS4ERR_RECALLCONFLICT),\n}\nfunc (Nfsstat4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Nfsstat4\n}\nfunc (v Nfsstat4) String() string {\n\tif s, ok := _XdrNames_Nfsstat4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Nfsstat4#%d\", v)\n}\nfunc (v *Nfsstat4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Nfsstat4[stok]; ok {\n\t\t\t*v = Nfsstat4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Nfsstat4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Nfsstat4.\", stok))\n\t}\n}\nfunc (v Nfsstat4) GetU32() uint32 { return uint32(v) }\nfunc (v *Nfsstat4) SetU32(n uint32) { *v = Nfsstat4(n) }\nfunc (v *Nfsstat4) XdrPointer() interface{} { return v }\nfunc (Nfsstat4) XdrTypeName() string { return \"Nfsstat4\" }\nfunc (v Nfsstat4) XdrValue() interface{} { return v }\nfunc (v *Nfsstat4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Nfsstat4 = *Nfsstat4\nfunc XDR_Nfsstat4(v *Nfsstat4) *Nfsstat4 { return v }\ntype _XdrVec_unbounded_Uint32_t []Uint32_t\nfunc (_XdrVec_unbounded_Uint32_t) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Uint32_t) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Uint32_t length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Uint32_t length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Uint32_t) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Uint32_t) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Uint32_t, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Uint32_t) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Uint32_t(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Uint32_t) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Uint32_t) XdrTypeName() string { return \"Uint32_t<>\" }\nfunc (v *_XdrVec_unbounded_Uint32_t) XdrPointer() interface{} { return (*[]Uint32_t)(v) }\nfunc (v _XdrVec_unbounded_Uint32_t) XdrValue() interface{} { return ([]Uint32_t)(v) }\nfunc (v *_XdrVec_unbounded_Uint32_t) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Bitmap4 struct {\n\t*_XdrVec_unbounded_Uint32_t\n}\nfunc XDR_Bitmap4(v *Bitmap4) XdrType_Bitmap4 {\n\treturn XdrType_Bitmap4{(*_XdrVec_unbounded_Uint32_t)(v)}\n}\nfunc (XdrType_Bitmap4) XdrTypeName() string { return \"Bitmap4\" }\nfunc (v XdrType_Bitmap4) XdrUnwrap() XdrType { return v._XdrVec_unbounded_Uint32_t }\ntype XdrType_Offset4 struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Offset4(v *Offset4) *XdrType_Offset4 {\n\treturn &XdrType_Offset4{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Offset4) XdrTypeName() string { return \"Offset4\" }\nfunc (v XdrType_Offset4) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Count4 struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Count4(v *Count4) *XdrType_Count4 {\n\treturn &XdrType_Count4{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Count4) XdrTypeName() string { return \"Count4\" }\nfunc (v XdrType_Count4) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Length4 struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Length4(v *Length4) *XdrType_Length4 {\n\treturn &XdrType_Length4{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Length4) XdrTypeName() string { return \"Length4\" }\nfunc (v XdrType_Length4) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Clientid4 struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Clientid4(v *Clientid4) *XdrType_Clientid4 {\n\treturn &XdrType_Clientid4{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Clientid4) XdrTypeName() string { return \"Clientid4\" }\nfunc (v XdrType_Clientid4) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Sequenceid4 struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Sequenceid4(v *Sequenceid4) *XdrType_Sequenceid4 {\n\treturn &XdrType_Sequenceid4{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Sequenceid4) XdrTypeName() string { return \"Sequenceid4\" }\nfunc (v XdrType_Sequenceid4) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Seqid4 struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Seqid4(v *Seqid4) *XdrType_Seqid4 {\n\treturn &XdrType_Seqid4{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Seqid4) XdrTypeName() string { return \"Seqid4\" }\nfunc (v XdrType_Seqid4) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Slotid4 struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Slotid4(v *Slotid4) *XdrType_Slotid4 {\n\treturn &XdrType_Slotid4{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Slotid4) XdrTypeName() string { return \"Slotid4\" }\nfunc (v XdrType_Slotid4) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Utf8string struct {\n\tXdrVecOpaque\n}\nfunc XDR_Utf8string(v *Utf8string) XdrType_Utf8string {\n\treturn XdrType_Utf8string{XdrVecOpaque{v, 0xffffffff}}\n}\nfunc (XdrType_Utf8string) XdrTypeName() string { return \"Utf8string\" }\nfunc (v XdrType_Utf8string) XdrUnwrap() XdrType { return v.XdrVecOpaque }\ntype XdrType_Utf8str_cis struct {\n\tXdrType_Utf8string\n}\nfunc XDR_Utf8str_cis(v *Utf8str_cis) XdrType_Utf8str_cis {\n\treturn XdrType_Utf8str_cis{XDR_Utf8string(v)}\n}\nfunc (XdrType_Utf8str_cis) XdrTypeName() string { return \"Utf8str_cis\" }\nfunc (v XdrType_Utf8str_cis) XdrUnwrap() XdrType { return v.XdrType_Utf8string }\ntype XdrType_Utf8str_cs struct {\n\tXdrType_Utf8string\n}\nfunc XDR_Utf8str_cs(v *Utf8str_cs) XdrType_Utf8str_cs {\n\treturn XdrType_Utf8str_cs{XDR_Utf8string(v)}\n}\nfunc (XdrType_Utf8str_cs) XdrTypeName() string { return \"Utf8str_cs\" }\nfunc (v XdrType_Utf8str_cs) XdrUnwrap() XdrType { return v.XdrType_Utf8string }\ntype XdrType_Utf8str_mixed struct {\n\tXdrType_Utf8string\n}\nfunc XDR_Utf8str_mixed(v *Utf8str_mixed) XdrType_Utf8str_mixed {\n\treturn XdrType_Utf8str_mixed{XDR_Utf8string(v)}\n}\nfunc (XdrType_Utf8str_mixed) XdrTypeName() string { return \"Utf8str_mixed\" }\nfunc (v XdrType_Utf8str_mixed) XdrUnwrap() XdrType { return v.XdrType_Utf8string }\ntype XdrType_Component4 struct {\n\tXdrType_Utf8str_cs\n}\nfunc XDR_Component4(v *Component4) XdrType_Component4 {\n\treturn XdrType_Component4{XDR_Utf8str_cs(v)}\n}\nfunc (XdrType_Component4) XdrTypeName() string { return \"Component4\" }\nfunc (v XdrType_Component4) XdrUnwrap() XdrType { return v.XdrType_Utf8str_cs }\ntype _XdrVec_unbounded_Component4 []Component4\nfunc (_XdrVec_unbounded_Component4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Component4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Component4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Component4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Component4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Component4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Component4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Component4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Component4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Component4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Component4) XdrTypeName() string { return \"Component4<>\" }\nfunc (v *_XdrVec_unbounded_Component4) XdrPointer() interface{} { return (*[]Component4)(v) }\nfunc (v _XdrVec_unbounded_Component4) XdrValue() interface{} { return ([]Component4)(v) }\nfunc (v *_XdrVec_unbounded_Component4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Pathname4 struct {\n\t*_XdrVec_unbounded_Component4\n}\nfunc XDR_Pathname4(v *Pathname4) XdrType_Pathname4 {\n\treturn XdrType_Pathname4{(*_XdrVec_unbounded_Component4)(v)}\n}\nfunc (XdrType_Pathname4) XdrTypeName() string { return \"Pathname4\" }\nfunc (v XdrType_Pathname4) XdrUnwrap() XdrType { return v._XdrVec_unbounded_Component4 }\ntype XdrType_Nfs_lockid4 struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Nfs_lockid4(v *Nfs_lockid4) XdrType_Nfs_lockid4 {\n\treturn XdrType_Nfs_lockid4{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Nfs_lockid4) XdrTypeName() string { return \"Nfs_lockid4\" }\nfunc (v XdrType_Nfs_lockid4) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Nfs_cookie4 struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Nfs_cookie4(v *Nfs_cookie4) *XdrType_Nfs_cookie4 {\n\treturn &XdrType_Nfs_cookie4{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Nfs_cookie4) XdrTypeName() string { return \"Nfs_cookie4\" }\nfunc (v XdrType_Nfs_cookie4) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Linktext4 struct {\n\tXdrType_Utf8str_cs\n}\nfunc XDR_Linktext4(v *Linktext4) XdrType_Linktext4 {\n\treturn XdrType_Linktext4{XDR_Utf8str_cs(v)}\n}\nfunc (XdrType_Linktext4) XdrTypeName() string { return \"Linktext4\" }\nfunc (v XdrType_Linktext4) XdrUnwrap() XdrType { return v.XdrType_Utf8str_cs }\ntype XdrType_Sec_oid4 struct {\n\tXdrVecOpaque\n}\nfunc XDR_Sec_oid4(v *Sec_oid4) XdrType_Sec_oid4 {\n\treturn XdrType_Sec_oid4{XdrVecOpaque{v, 0xffffffff}}\n}\nfunc (XdrType_Sec_oid4) XdrTypeName() string { return \"Sec_oid4\" }\nfunc (v XdrType_Sec_oid4) XdrUnwrap() XdrType { return v.XdrVecOpaque }\ntype XdrType_Qop4 struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Qop4(v *Qop4) *XdrType_Qop4 {\n\treturn &XdrType_Qop4{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Qop4) XdrTypeName() string { return \"Qop4\" }\nfunc (v XdrType_Qop4) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Mode4 struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Mode4(v *Mode4) XdrType_Mode4 {\n\treturn XdrType_Mode4{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Mode4) XdrTypeName() string { return \"Mode4\" }\nfunc (v XdrType_Mode4) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Changeid4 struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Changeid4(v *Changeid4) *XdrType_Changeid4 {\n\treturn &XdrType_Changeid4{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Changeid4) XdrTypeName() string { return \"Changeid4\" }\nfunc (v XdrType_Changeid4) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype _XdrArray_8_opaque [8]byte\nfunc (v *_XdrArray_8_opaque) GetByteSlice() []byte { return v[:] }\nfunc (v *_XdrArray_8_opaque) XdrTypeName() string { return \"opaque[]\" }\nfunc (v *_XdrArray_8_opaque) XdrValue() interface{} { return v[:] }\nfunc (v *_XdrArray_8_opaque) XdrPointer() interface{} { return (*[8]byte)(v) }\nfunc (v *_XdrArray_8_opaque) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *_XdrArray_8_opaque) String() string { return fmt.Sprintf(\"%x\", v[:]) }\nfunc (v *_XdrArray_8_opaque) Scan(ss fmt.ScanState, c rune) error {\n\treturn XdrArrayOpaqueScan(v[:], ss, c)\n}\nfunc (_XdrArray_8_opaque) XdrArraySize() uint32 {\n\tconst bound uint32 = 8 // Force error if not const or doesn't fit\n\treturn bound\n}\ntype XdrType_Verifier4 struct {\n\t*_XdrArray_8_opaque\n}\nfunc XDR_Verifier4(v *Verifier4) XdrType_Verifier4 {\n\treturn XdrType_Verifier4{(*_XdrArray_8_opaque)(v)}\n}\nfunc (XdrType_Verifier4) XdrTypeName() string { return \"Verifier4\" }\nfunc (v XdrType_Verifier4) XdrUnwrap() XdrType { return v._XdrArray_8_opaque }\ntype _XdrArray_16_opaque [16]byte\nfunc (v *_XdrArray_16_opaque) GetByteSlice() []byte { return v[:] }\nfunc (v *_XdrArray_16_opaque) XdrTypeName() string { return \"opaque[]\" }\nfunc (v *_XdrArray_16_opaque) XdrValue() interface{} { return v[:] }\nfunc (v *_XdrArray_16_opaque) XdrPointer() interface{} { return (*[16]byte)(v) }\nfunc (v *_XdrArray_16_opaque) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *_XdrArray_16_opaque) String() string { return fmt.Sprintf(\"%x\", v[:]) }\nfunc (v *_XdrArray_16_opaque) Scan(ss fmt.ScanState, c rune) error {\n\treturn XdrArrayOpaqueScan(v[:], ss, c)\n}\nfunc (_XdrArray_16_opaque) XdrArraySize() uint32 {\n\tconst bound uint32 = 16 // Force error if not const or doesn't fit\n\treturn bound\n}\ntype XdrType_Sessionid4 struct {\n\t*_XdrArray_16_opaque\n}\nfunc XDR_Sessionid4(v *Sessionid4) XdrType_Sessionid4 {\n\treturn XdrType_Sessionid4{(*_XdrArray_16_opaque)(v)}\n}\nfunc (XdrType_Sessionid4) XdrTypeName() string { return \"Sessionid4\" }\nfunc (v XdrType_Sessionid4) XdrUnwrap() XdrType { return v._XdrArray_16_opaque }\ntype _XdrVec_16_uint32 []uint32\nfunc (_XdrVec_16_uint32) XdrBound() uint32 {\n\tconst bound uint32 = 16 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_16_uint32) XdrCheckLen(length uint32) {\n\tif length > uint32(16) {\n\t\tXdrPanic(\"_XdrVec_16_uint32 length %d exceeds bound 16\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_16_uint32 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_16_uint32) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_16_uint32) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(16); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]uint32, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_16_uint32) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_uint32(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_16_uint32) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 16 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_16_uint32) XdrTypeName() string { return \"uint32<>\" }\nfunc (v *_XdrVec_16_uint32) XdrPointer() interface{} { return (*[]uint32)(v) }\nfunc (v _XdrVec_16_uint32) XdrValue() interface{} { return ([]uint32)(v) }\nfunc (v *_XdrVec_16_uint32) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Authsys_parms = *Authsys_parms\nfunc (v *Authsys_parms) XdrPointer() interface{} { return v }\nfunc (Authsys_parms) XdrTypeName() string { return \"Authsys_parms\" }\nfunc (v Authsys_parms) XdrValue() interface{} { return v }\nfunc (v *Authsys_parms) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Authsys_parms) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstamp\", name), XDR_uint32(&v.Stamp))\n\tx.Marshal(x.Sprintf(\"%smachinename\", name), XdrString{&v.Machinename, 255})\n\tx.Marshal(x.Sprintf(\"%suid\", name), XDR_uint32(&v.Uid))\n\tx.Marshal(x.Sprintf(\"%sgid\", name), XDR_uint32(&v.Gid))\n\tx.Marshal(x.Sprintf(\"%sgids\", name), (*_XdrVec_16_uint32)(&v.Gids))\n}\nfunc XDR_Authsys_parms(v *Authsys_parms) *Authsys_parms { return v }\ntype XdrType_Deviceid4 struct {\n\t*_XdrArray_16_opaque\n}\nfunc XDR_Deviceid4(v *Deviceid4) *XdrType_Deviceid4 {\n\treturn &XdrType_Deviceid4{(*_XdrArray_16_opaque)(v)}\n}\nfunc (XdrType_Deviceid4) XdrTypeName() string { return \"Deviceid4\" }\nfunc (v XdrType_Deviceid4) XdrUnwrap() XdrType { return v._XdrArray_16_opaque }\nvar _XdrNames_Layouttype4 = map[int32]string{\n\tint32(LAYOUT4_NFSV4_1_FILES): \"LAYOUT4_NFSV4_1_FILES\",\n\tint32(LAYOUT4_OSD2_OBJECTS): \"LAYOUT4_OSD2_OBJECTS\",\n\tint32(LAYOUT4_BLOCK_VOLUME): \"LAYOUT4_BLOCK_VOLUME\",\n}\nvar _XdrValues_Layouttype4 = map[string]int32{\n\t\"LAYOUT4_NFSV4_1_FILES\": int32(LAYOUT4_NFSV4_1_FILES),\n\t\"LAYOUT4_OSD2_OBJECTS\": int32(LAYOUT4_OSD2_OBJECTS),\n\t\"LAYOUT4_BLOCK_VOLUME\": int32(LAYOUT4_BLOCK_VOLUME),\n}\nfunc (Layouttype4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Layouttype4\n}\nfunc (v Layouttype4) String() string {\n\tif s, ok := _XdrNames_Layouttype4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Layouttype4#%d\", v)\n}\nfunc (v *Layouttype4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Layouttype4[stok]; ok {\n\t\t\t*v = Layouttype4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Layouttype4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Layouttype4.\", stok))\n\t}\n}\nfunc (v Layouttype4) GetU32() uint32 { return uint32(v) }\nfunc (v *Layouttype4) SetU32(n uint32) { *v = Layouttype4(n) }\nfunc (v *Layouttype4) XdrPointer() interface{} { return v }\nfunc (Layouttype4) XdrTypeName() string { return \"Layouttype4\" }\nfunc (v Layouttype4) XdrValue() interface{} { return v }\nfunc (v *Layouttype4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Layouttype4 = *Layouttype4\nfunc XDR_Layouttype4(v *Layouttype4) *Layouttype4 { return v }\nfunc (v *Layouttype4) XdrInitialize() {\n\tswitch Layouttype4(0) {\n\tcase LAYOUT4_NFSV4_1_FILES, LAYOUT4_OSD2_OBJECTS, LAYOUT4_BLOCK_VOLUME:\n\tdefault:\n\t\tif *v == Layouttype4(0) { *v = LAYOUT4_NFSV4_1_FILES }\n\t}\n}\ntype XdrType_Layoutupdate4 = *Layoutupdate4\nfunc (v *Layoutupdate4) XdrPointer() interface{} { return v }\nfunc (Layoutupdate4) XdrTypeName() string { return \"Layoutupdate4\" }\nfunc (v Layoutupdate4) XdrValue() interface{} { return v }\nfunc (v *Layoutupdate4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Layoutupdate4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slou_type\", name), XDR_Layouttype4(&v.Lou_type))\n\tx.Marshal(x.Sprintf(\"%slou_body\", name), XdrVecOpaque{&v.Lou_body, 0xffffffff})\n}\nfunc XDR_Layoutupdate4(v *Layoutupdate4) *Layoutupdate4 { return v }\ntype XdrType_Device_addr4 = *Device_addr4\nfunc (v *Device_addr4) XdrPointer() interface{} { return v }\nfunc (Device_addr4) XdrTypeName() string { return \"Device_addr4\" }\nfunc (v Device_addr4) XdrValue() interface{} { return v }\nfunc (v *Device_addr4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Device_addr4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sda_layout_type\", name), XDR_Layouttype4(&v.Da_layout_type))\n\tx.Marshal(x.Sprintf(\"%sda_addr_body\", name), XdrVecOpaque{&v.Da_addr_body, 0xffffffff})\n}\nfunc XDR_Device_addr4(v *Device_addr4) *Device_addr4 { return v }\ntype XdrType_Nfstime4 = *Nfstime4\nfunc (v *Nfstime4) XdrPointer() interface{} { return v }\nfunc (Nfstime4) XdrTypeName() string { return \"Nfstime4\" }\nfunc (v Nfstime4) XdrValue() interface{} { return v }\nfunc (v *Nfstime4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Nfstime4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sseconds\", name), XDR_Int64_t(&v.Seconds))\n\tx.Marshal(x.Sprintf(\"%snseconds\", name), XDR_Uint32_t(&v.Nseconds))\n}\nfunc XDR_Nfstime4(v *Nfstime4) *Nfstime4 { return v }\nvar _XdrNames_Time_how4 = map[int32]string{\n\tint32(SET_TO_SERVER_TIME4): \"SET_TO_SERVER_TIME4\",\n\tint32(SET_TO_CLIENT_TIME4): \"SET_TO_CLIENT_TIME4\",\n}\nvar _XdrValues_Time_how4 = map[string]int32{\n\t\"SET_TO_SERVER_TIME4\": int32(SET_TO_SERVER_TIME4),\n\t\"SET_TO_CLIENT_TIME4\": int32(SET_TO_CLIENT_TIME4),\n}\nfunc (Time_how4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Time_how4\n}\nfunc (v Time_how4) String() string {\n\tif s, ok := _XdrNames_Time_how4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Time_how4#%d\", v)\n}\nfunc (v *Time_how4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Time_how4[stok]; ok {\n\t\t\t*v = Time_how4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Time_how4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Time_how4.\", stok))\n\t}\n}\nfunc (v Time_how4) GetU32() uint32 { return uint32(v) }\nfunc (v *Time_how4) SetU32(n uint32) { *v = Time_how4(n) }\nfunc (v *Time_how4) XdrPointer() interface{} { return v }\nfunc (Time_how4) XdrTypeName() string { return \"Time_how4\" }\nfunc (v Time_how4) XdrValue() interface{} { return v }\nfunc (v *Time_how4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Time_how4 = *Time_how4\nfunc XDR_Time_how4(v *Time_how4) *Time_how4 { return v }\nvar _XdrNames_Layoutiomode4 = map[int32]string{\n\tint32(LAYOUTIOMODE4_READ): \"LAYOUTIOMODE4_READ\",\n\tint32(LAYOUTIOMODE4_RW): \"LAYOUTIOMODE4_RW\",\n\tint32(LAYOUTIOMODE4_ANY): \"LAYOUTIOMODE4_ANY\",\n}\nvar _XdrValues_Layoutiomode4 = map[string]int32{\n\t\"LAYOUTIOMODE4_READ\": int32(LAYOUTIOMODE4_READ),\n\t\"LAYOUTIOMODE4_RW\": int32(LAYOUTIOMODE4_RW),\n\t\"LAYOUTIOMODE4_ANY\": int32(LAYOUTIOMODE4_ANY),\n}\nfunc (Layoutiomode4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Layoutiomode4\n}\nfunc (v Layoutiomode4) String() string {\n\tif s, ok := _XdrNames_Layoutiomode4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Layoutiomode4#%d\", v)\n}\nfunc (v *Layoutiomode4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Layoutiomode4[stok]; ok {\n\t\t\t*v = Layoutiomode4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Layoutiomode4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Layoutiomode4.\", stok))\n\t}\n}\nfunc (v Layoutiomode4) GetU32() uint32 { return uint32(v) }\nfunc (v *Layoutiomode4) SetU32(n uint32) { *v = Layoutiomode4(n) }\nfunc (v *Layoutiomode4) XdrPointer() interface{} { return v }\nfunc (Layoutiomode4) XdrTypeName() string { return \"Layoutiomode4\" }\nfunc (v Layoutiomode4) XdrValue() interface{} { return v }\nfunc (v *Layoutiomode4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Layoutiomode4 = *Layoutiomode4\nfunc XDR_Layoutiomode4(v *Layoutiomode4) *Layoutiomode4 { return v }\nfunc (v *Layoutiomode4) XdrInitialize() {\n\tswitch Layoutiomode4(0) {\n\tcase LAYOUTIOMODE4_READ, LAYOUTIOMODE4_RW, LAYOUTIOMODE4_ANY:\n\tdefault:\n\t\tif *v == Layoutiomode4(0) { *v = LAYOUTIOMODE4_READ }\n\t}\n}\ntype XdrType_Layout_content4 = *Layout_content4\nfunc (v *Layout_content4) XdrPointer() interface{} { return v }\nfunc (Layout_content4) XdrTypeName() string { return \"Layout_content4\" }\nfunc (v Layout_content4) XdrValue() interface{} { return v }\nfunc (v *Layout_content4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Layout_content4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sloc_type\", name), XDR_Layouttype4(&v.Loc_type))\n\tx.Marshal(x.Sprintf(\"%sloc_body\", name), XdrVecOpaque{&v.Loc_body, 0xffffffff})\n}\nfunc XDR_Layout_content4(v *Layout_content4) *Layout_content4 { return v }\ntype XdrType_Layout4 = *Layout4\nfunc (v *Layout4) XdrPointer() interface{} { return v }\nfunc (Layout4) XdrTypeName() string { return \"Layout4\" }\nfunc (v Layout4) XdrValue() interface{} { return v }\nfunc (v *Layout4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Layout4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slo_offset\", name), XDR_Offset4(&v.Lo_offset))\n\tx.Marshal(x.Sprintf(\"%slo_length\", name), XDR_Length4(&v.Lo_length))\n\tx.Marshal(x.Sprintf(\"%slo_iomode\", name), XDR_Layoutiomode4(&v.Lo_iomode))\n\tx.Marshal(x.Sprintf(\"%slo_content\", name), XDR_Layout_content4(&v.Lo_content))\n}\nfunc XDR_Layout4(v *Layout4) *Layout4 { return v }\nfunc (u *Settime4) Time() *Nfstime4 {\n\tswitch u.Set_it {\n\tcase SET_TO_CLIENT_TIME4:\n\t\tif v, ok := u.U.(*Nfstime4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Nfstime4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Settime4.Time accessed when Set_it == %v\", u.Set_it)\n\t\treturn nil\n\t}\n}\nfunc (u Settime4) XdrValid() bool {\n\treturn true\n}\nfunc (u *Settime4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Time_how4(&u.Set_it)\n}\nfunc (u *Settime4) XdrUnionTagName() string {\n\treturn \"Set_it\"\n}\nfunc (u *Settime4) XdrUnionBody() XdrType {\n\tswitch u.Set_it {\n\tcase SET_TO_CLIENT_TIME4:\n\t\treturn XDR_Nfstime4(u.Time())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *Settime4) XdrUnionBodyName() string {\n\tswitch u.Set_it {\n\tcase SET_TO_CLIENT_TIME4:\n\t\treturn \"Time\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_Settime4 = *Settime4\nfunc (v *Settime4) XdrPointer() interface{} { return v }\nfunc (Settime4) XdrTypeName() string { return \"Settime4\" }\nfunc (v Settime4) XdrValue() interface{} { return v }\nfunc (v *Settime4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Settime4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Time_how4(&u.Set_it).XdrMarshal(x, x.Sprintf(\"%sset_it\", name))\n\tswitch u.Set_it {\n\tcase SET_TO_CLIENT_TIME4:\n\t\tx.Marshal(x.Sprintf(\"%stime\", name), XDR_Nfstime4(u.Time()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_Settime4(v *Settime4) *Settime4 { return v}\ntype XdrType_Nfs_fh4 struct {\n\tXdrVecOpaque\n}\nfunc XDR_Nfs_fh4(v *Nfs_fh4) XdrType_Nfs_fh4 {\n\treturn XdrType_Nfs_fh4{XdrVecOpaque{v, NFS4_FHSIZE}}\n}\nfunc (XdrType_Nfs_fh4) XdrTypeName() string { return \"Nfs_fh4\" }\nfunc (v XdrType_Nfs_fh4) XdrUnwrap() XdrType { return v.XdrVecOpaque }\ntype XdrType_Fsid4 = *Fsid4\nfunc (v *Fsid4) XdrPointer() interface{} { return v }\nfunc (Fsid4) XdrTypeName() string { return \"Fsid4\" }\nfunc (v Fsid4) XdrValue() interface{} { return v }\nfunc (v *Fsid4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Fsid4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%smajor\", name), XDR_Uint64_t(&v.Major))\n\tx.Marshal(x.Sprintf(\"%sminor\", name), XDR_Uint64_t(&v.Minor))\n}\nfunc XDR_Fsid4(v *Fsid4) *Fsid4 { return v }\ntype _XdrVec_unbounded_Utf8str_cis []Utf8str_cis\nfunc (_XdrVec_unbounded_Utf8str_cis) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Utf8str_cis) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Utf8str_cis length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Utf8str_cis length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Utf8str_cis) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Utf8str_cis) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Utf8str_cis, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Utf8str_cis) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Utf8str_cis(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Utf8str_cis) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Utf8str_cis) XdrTypeName() string { return \"Utf8str_cis<>\" }\nfunc (v *_XdrVec_unbounded_Utf8str_cis) XdrPointer() interface{} { return (*[]Utf8str_cis)(v) }\nfunc (v _XdrVec_unbounded_Utf8str_cis) XdrValue() interface{} { return ([]Utf8str_cis)(v) }\nfunc (v *_XdrVec_unbounded_Utf8str_cis) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Fs_location4 = *Fs_location4\nfunc (v *Fs_location4) XdrPointer() interface{} { return v }\nfunc (Fs_location4) XdrTypeName() string { return \"Fs_location4\" }\nfunc (v Fs_location4) XdrValue() interface{} { return v }\nfunc (v *Fs_location4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Fs_location4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sserver\", name), (*_XdrVec_unbounded_Utf8str_cis)(&v.Server))\n\tx.Marshal(x.Sprintf(\"%srootpath\", name), XDR_Pathname4(&v.Rootpath))\n}\nfunc XDR_Fs_location4(v *Fs_location4) *Fs_location4 { return v }\ntype _XdrVec_unbounded_Fs_location4 []Fs_location4\nfunc (_XdrVec_unbounded_Fs_location4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Fs_location4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Fs_location4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Fs_location4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Fs_location4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Fs_location4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Fs_location4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Fs_location4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Fs_location4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Fs_location4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Fs_location4) XdrTypeName() string { return \"Fs_location4<>\" }\nfunc (v *_XdrVec_unbounded_Fs_location4) XdrPointer() interface{} { return (*[]Fs_location4)(v) }\nfunc (v _XdrVec_unbounded_Fs_location4) XdrValue() interface{} { return ([]Fs_location4)(v) }\nfunc (v *_XdrVec_unbounded_Fs_location4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Fs_locations4 = *Fs_locations4\nfunc (v *Fs_locations4) XdrPointer() interface{} { return v }\nfunc (Fs_locations4) XdrTypeName() string { return \"Fs_locations4\" }\nfunc (v Fs_locations4) XdrValue() interface{} { return v }\nfunc (v *Fs_locations4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Fs_locations4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sfs_root\", name), XDR_Pathname4(&v.Fs_root))\n\tx.Marshal(x.Sprintf(\"%slocations\", name), (*_XdrVec_unbounded_Fs_location4)(&v.Locations))\n}\nfunc XDR_Fs_locations4(v *Fs_locations4) *Fs_locations4 { return v }\ntype XdrType_Acetype4 struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Acetype4(v *Acetype4) *XdrType_Acetype4 {\n\treturn &XdrType_Acetype4{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Acetype4) XdrTypeName() string { return \"Acetype4\" }\nfunc (v XdrType_Acetype4) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Aceflag4 struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Aceflag4(v *Aceflag4) *XdrType_Aceflag4 {\n\treturn &XdrType_Aceflag4{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Aceflag4) XdrTypeName() string { return \"Aceflag4\" }\nfunc (v XdrType_Aceflag4) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Acemask4 struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Acemask4(v *Acemask4) *XdrType_Acemask4 {\n\treturn &XdrType_Acemask4{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Acemask4) XdrTypeName() string { return \"Acemask4\" }\nfunc (v XdrType_Acemask4) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Nfsace4 = *Nfsace4\nfunc (v *Nfsace4) XdrPointer() interface{} { return v }\nfunc (Nfsace4) XdrTypeName() string { return \"Nfsace4\" }\nfunc (v Nfsace4) XdrValue() interface{} { return v }\nfunc (v *Nfsace4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Nfsace4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%stype\", name), XDR_Acetype4(&v.Type))\n\tx.Marshal(x.Sprintf(\"%sflag\", name), XDR_Aceflag4(&v.Flag))\n\tx.Marshal(x.Sprintf(\"%saccess_mask\", name), XDR_Acemask4(&v.Access_mask))\n\tx.Marshal(x.Sprintf(\"%swho\", name), XDR_Utf8str_mixed(&v.Who))\n}\nfunc XDR_Nfsace4(v *Nfsace4) *Nfsace4 { return v }\ntype XdrType_Specdata4 = *Specdata4\nfunc (v *Specdata4) XdrPointer() interface{} { return v }\nfunc (Specdata4) XdrTypeName() string { return \"Specdata4\" }\nfunc (v Specdata4) XdrValue() interface{} { return v }\nfunc (v *Specdata4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Specdata4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sspecdata1\", name), XDR_Uint32_t(&v.Specdata1))\n\tx.Marshal(x.Sprintf(\"%sspecdata2\", name), XDR_Uint32_t(&v.Specdata2))\n}\nfunc XDR_Specdata4(v *Specdata4) *Specdata4 { return v }\ntype XdrType_Fattr4_supported_attrs struct {\n\tXdrType_Bitmap4\n}\nfunc XDR_Fattr4_supported_attrs(v *Fattr4_supported_attrs) XdrType_Fattr4_supported_attrs {\n\treturn XdrType_Fattr4_supported_attrs{XDR_Bitmap4(v)}\n}\nfunc (XdrType_Fattr4_supported_attrs) XdrTypeName() string { return \"Fattr4_supported_attrs\" }\nfunc (v XdrType_Fattr4_supported_attrs) XdrUnwrap() XdrType { return v.XdrType_Bitmap4 }\ntype XdrType_Fattr4_type struct {\n\tXdrType_Nfs_ftype4\n}\nfunc XDR_Fattr4_type(v *Fattr4_type) XdrType_Fattr4_type {\n\treturn XdrType_Fattr4_type{XDR_Nfs_ftype4(v)}\n}\nfunc (XdrType_Fattr4_type) XdrTypeName() string { return \"Fattr4_type\" }\nfunc (v XdrType_Fattr4_type) XdrUnwrap() XdrType { return v.XdrType_Nfs_ftype4 }\ntype XdrType_Fattr4_fh_expire_type struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Fattr4_fh_expire_type(v *Fattr4_fh_expire_type) XdrType_Fattr4_fh_expire_type {\n\treturn XdrType_Fattr4_fh_expire_type{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Fattr4_fh_expire_type) XdrTypeName() string { return \"Fattr4_fh_expire_type\" }\nfunc (v XdrType_Fattr4_fh_expire_type) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Fattr4_change struct {\n\tXdrType_Changeid4\n}\nfunc XDR_Fattr4_change(v *Fattr4_change) *XdrType_Fattr4_change {\n\treturn &XdrType_Fattr4_change{*XDR_Changeid4(v)}\n}\nfunc (XdrType_Fattr4_change) XdrTypeName() string { return \"Fattr4_change\" }\nfunc (v XdrType_Fattr4_change) XdrUnwrap() XdrType { return &v.XdrType_Changeid4 }\ntype XdrType_Fattr4_size struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_size(v *Fattr4_size) XdrType_Fattr4_size {\n\treturn XdrType_Fattr4_size{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_size) XdrTypeName() string { return \"Fattr4_size\" }\nfunc (v XdrType_Fattr4_size) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_link_support struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_link_support(v *Fattr4_link_support) XdrType_Fattr4_link_support {\n\treturn XdrType_Fattr4_link_support{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_link_support) XdrTypeName() string { return \"Fattr4_link_support\" }\nfunc (v XdrType_Fattr4_link_support) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_symlink_support struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_symlink_support(v *Fattr4_symlink_support) XdrType_Fattr4_symlink_support {\n\treturn XdrType_Fattr4_symlink_support{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_symlink_support) XdrTypeName() string { return \"Fattr4_symlink_support\" }\nfunc (v XdrType_Fattr4_symlink_support) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_named_attr struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_named_attr(v *Fattr4_named_attr) XdrType_Fattr4_named_attr {\n\treturn XdrType_Fattr4_named_attr{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_named_attr) XdrTypeName() string { return \"Fattr4_named_attr\" }\nfunc (v XdrType_Fattr4_named_attr) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_fsid struct {\n\tXdrType_Fsid4\n}\nfunc XDR_Fattr4_fsid(v *Fattr4_fsid) XdrType_Fattr4_fsid {\n\treturn XdrType_Fattr4_fsid{XDR_Fsid4(v)}\n}\nfunc (XdrType_Fattr4_fsid) XdrTypeName() string { return \"Fattr4_fsid\" }\nfunc (v XdrType_Fattr4_fsid) XdrUnwrap() XdrType { return v.XdrType_Fsid4 }\ntype XdrType_Fattr4_unique_handles struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_unique_handles(v *Fattr4_unique_handles) XdrType_Fattr4_unique_handles {\n\treturn XdrType_Fattr4_unique_handles{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_unique_handles) XdrTypeName() string { return \"Fattr4_unique_handles\" }\nfunc (v XdrType_Fattr4_unique_handles) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_lease_time struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Fattr4_lease_time(v *Fattr4_lease_time) XdrType_Fattr4_lease_time {\n\treturn XdrType_Fattr4_lease_time{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Fattr4_lease_time) XdrTypeName() string { return \"Fattr4_lease_time\" }\nfunc (v XdrType_Fattr4_lease_time) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Fattr4_rdattr_error struct {\n\tXdrType_Nfsstat4\n}\nfunc XDR_Fattr4_rdattr_error(v *Fattr4_rdattr_error) XdrType_Fattr4_rdattr_error {\n\treturn XdrType_Fattr4_rdattr_error{XDR_Nfsstat4(v)}\n}\nfunc (XdrType_Fattr4_rdattr_error) XdrTypeName() string { return \"Fattr4_rdattr_error\" }\nfunc (v XdrType_Fattr4_rdattr_error) XdrUnwrap() XdrType { return v.XdrType_Nfsstat4 }\ntype _XdrVec_unbounded_Nfsace4 []Nfsace4\nfunc (_XdrVec_unbounded_Nfsace4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Nfsace4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Nfsace4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Nfsace4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Nfsace4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Nfsace4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Nfsace4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Nfsace4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Nfsace4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Nfsace4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Nfsace4) XdrTypeName() string { return \"Nfsace4<>\" }\nfunc (v *_XdrVec_unbounded_Nfsace4) XdrPointer() interface{} { return (*[]Nfsace4)(v) }\nfunc (v _XdrVec_unbounded_Nfsace4) XdrValue() interface{} { return ([]Nfsace4)(v) }\nfunc (v *_XdrVec_unbounded_Nfsace4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Fattr4_acl struct {\n\t*_XdrVec_unbounded_Nfsace4\n}\nfunc XDR_Fattr4_acl(v *Fattr4_acl) XdrType_Fattr4_acl {\n\treturn XdrType_Fattr4_acl{(*_XdrVec_unbounded_Nfsace4)(v)}\n}\nfunc (XdrType_Fattr4_acl) XdrTypeName() string { return \"Fattr4_acl\" }\nfunc (v XdrType_Fattr4_acl) XdrUnwrap() XdrType { return v._XdrVec_unbounded_Nfsace4 }\ntype XdrType_Fattr4_aclsupport struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Fattr4_aclsupport(v *Fattr4_aclsupport) XdrType_Fattr4_aclsupport {\n\treturn XdrType_Fattr4_aclsupport{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Fattr4_aclsupport) XdrTypeName() string { return \"Fattr4_aclsupport\" }\nfunc (v XdrType_Fattr4_aclsupport) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Fattr4_archive struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_archive(v *Fattr4_archive) XdrType_Fattr4_archive {\n\treturn XdrType_Fattr4_archive{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_archive) XdrTypeName() string { return \"Fattr4_archive\" }\nfunc (v XdrType_Fattr4_archive) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_cansettime struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_cansettime(v *Fattr4_cansettime) XdrType_Fattr4_cansettime {\n\treturn XdrType_Fattr4_cansettime{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_cansettime) XdrTypeName() string { return \"Fattr4_cansettime\" }\nfunc (v XdrType_Fattr4_cansettime) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_case_insensitive struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_case_insensitive(v *Fattr4_case_insensitive) XdrType_Fattr4_case_insensitive {\n\treturn XdrType_Fattr4_case_insensitive{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_case_insensitive) XdrTypeName() string { return \"Fattr4_case_insensitive\" }\nfunc (v XdrType_Fattr4_case_insensitive) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_case_preserving struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_case_preserving(v *Fattr4_case_preserving) XdrType_Fattr4_case_preserving {\n\treturn XdrType_Fattr4_case_preserving{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_case_preserving) XdrTypeName() string { return \"Fattr4_case_preserving\" }\nfunc (v XdrType_Fattr4_case_preserving) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_chown_restricted struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_chown_restricted(v *Fattr4_chown_restricted) XdrType_Fattr4_chown_restricted {\n\treturn XdrType_Fattr4_chown_restricted{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_chown_restricted) XdrTypeName() string { return \"Fattr4_chown_restricted\" }\nfunc (v XdrType_Fattr4_chown_restricted) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_fileid struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_fileid(v *Fattr4_fileid) XdrType_Fattr4_fileid {\n\treturn XdrType_Fattr4_fileid{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_fileid) XdrTypeName() string { return \"Fattr4_fileid\" }\nfunc (v XdrType_Fattr4_fileid) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_files_avail struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_files_avail(v *Fattr4_files_avail) XdrType_Fattr4_files_avail {\n\treturn XdrType_Fattr4_files_avail{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_files_avail) XdrTypeName() string { return \"Fattr4_files_avail\" }\nfunc (v XdrType_Fattr4_files_avail) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_filehandle struct {\n\tXdrType_Nfs_fh4\n}\nfunc XDR_Fattr4_filehandle(v *Fattr4_filehandle) XdrType_Fattr4_filehandle {\n\treturn XdrType_Fattr4_filehandle{XDR_Nfs_fh4(v)}\n}\nfunc (XdrType_Fattr4_filehandle) XdrTypeName() string { return \"Fattr4_filehandle\" }\nfunc (v XdrType_Fattr4_filehandle) XdrUnwrap() XdrType { return v.XdrType_Nfs_fh4 }\ntype XdrType_Fattr4_files_free struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_files_free(v *Fattr4_files_free) XdrType_Fattr4_files_free {\n\treturn XdrType_Fattr4_files_free{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_files_free) XdrTypeName() string { return \"Fattr4_files_free\" }\nfunc (v XdrType_Fattr4_files_free) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_files_total struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_files_total(v *Fattr4_files_total) XdrType_Fattr4_files_total {\n\treturn XdrType_Fattr4_files_total{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_files_total) XdrTypeName() string { return \"Fattr4_files_total\" }\nfunc (v XdrType_Fattr4_files_total) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_fs_locations struct {\n\tXdrType_Fs_locations4\n}\nfunc XDR_Fattr4_fs_locations(v *Fattr4_fs_locations) XdrType_Fattr4_fs_locations {\n\treturn XdrType_Fattr4_fs_locations{XDR_Fs_locations4(v)}\n}\nfunc (XdrType_Fattr4_fs_locations) XdrTypeName() string { return \"Fattr4_fs_locations\" }\nfunc (v XdrType_Fattr4_fs_locations) XdrUnwrap() XdrType { return v.XdrType_Fs_locations4 }\ntype XdrType_Fattr4_hidden struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_hidden(v *Fattr4_hidden) XdrType_Fattr4_hidden {\n\treturn XdrType_Fattr4_hidden{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_hidden) XdrTypeName() string { return \"Fattr4_hidden\" }\nfunc (v XdrType_Fattr4_hidden) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_homogeneous struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_homogeneous(v *Fattr4_homogeneous) XdrType_Fattr4_homogeneous {\n\treturn XdrType_Fattr4_homogeneous{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_homogeneous) XdrTypeName() string { return \"Fattr4_homogeneous\" }\nfunc (v XdrType_Fattr4_homogeneous) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_maxfilesize struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_maxfilesize(v *Fattr4_maxfilesize) XdrType_Fattr4_maxfilesize {\n\treturn XdrType_Fattr4_maxfilesize{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_maxfilesize) XdrTypeName() string { return \"Fattr4_maxfilesize\" }\nfunc (v XdrType_Fattr4_maxfilesize) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_maxlink struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Fattr4_maxlink(v *Fattr4_maxlink) XdrType_Fattr4_maxlink {\n\treturn XdrType_Fattr4_maxlink{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Fattr4_maxlink) XdrTypeName() string { return \"Fattr4_maxlink\" }\nfunc (v XdrType_Fattr4_maxlink) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Fattr4_maxname struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Fattr4_maxname(v *Fattr4_maxname) XdrType_Fattr4_maxname {\n\treturn XdrType_Fattr4_maxname{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Fattr4_maxname) XdrTypeName() string { return \"Fattr4_maxname\" }\nfunc (v XdrType_Fattr4_maxname) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Fattr4_maxread struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_maxread(v *Fattr4_maxread) XdrType_Fattr4_maxread {\n\treturn XdrType_Fattr4_maxread{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_maxread) XdrTypeName() string { return \"Fattr4_maxread\" }\nfunc (v XdrType_Fattr4_maxread) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_maxwrite struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_maxwrite(v *Fattr4_maxwrite) XdrType_Fattr4_maxwrite {\n\treturn XdrType_Fattr4_maxwrite{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_maxwrite) XdrTypeName() string { return \"Fattr4_maxwrite\" }\nfunc (v XdrType_Fattr4_maxwrite) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_mimetype struct {\n\tXdrType_Utf8str_cs\n}\nfunc XDR_Fattr4_mimetype(v *Fattr4_mimetype) XdrType_Fattr4_mimetype {\n\treturn XdrType_Fattr4_mimetype{XDR_Utf8str_cs(v)}\n}\nfunc (XdrType_Fattr4_mimetype) XdrTypeName() string { return \"Fattr4_mimetype\" }\nfunc (v XdrType_Fattr4_mimetype) XdrUnwrap() XdrType { return v.XdrType_Utf8str_cs }\ntype XdrType_Fattr4_mode struct {\n\tXdrType_Mode4\n}\nfunc XDR_Fattr4_mode(v *Fattr4_mode) XdrType_Fattr4_mode {\n\treturn XdrType_Fattr4_mode{XDR_Mode4(v)}\n}\nfunc (XdrType_Fattr4_mode) XdrTypeName() string { return \"Fattr4_mode\" }\nfunc (v XdrType_Fattr4_mode) XdrUnwrap() XdrType { return &v.XdrType_Mode4 }\ntype XdrType_Fattr4_mounted_on_fileid struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_mounted_on_fileid(v *Fattr4_mounted_on_fileid) XdrType_Fattr4_mounted_on_fileid {\n\treturn XdrType_Fattr4_mounted_on_fileid{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_mounted_on_fileid) XdrTypeName() string { return \"Fattr4_mounted_on_fileid\" }\nfunc (v XdrType_Fattr4_mounted_on_fileid) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_no_trunc struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_no_trunc(v *Fattr4_no_trunc) XdrType_Fattr4_no_trunc {\n\treturn XdrType_Fattr4_no_trunc{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_no_trunc) XdrTypeName() string { return \"Fattr4_no_trunc\" }\nfunc (v XdrType_Fattr4_no_trunc) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_numlinks struct {\n\tXdrType_Uint32_t\n}\nfunc XDR_Fattr4_numlinks(v *Fattr4_numlinks) XdrType_Fattr4_numlinks {\n\treturn XdrType_Fattr4_numlinks{XDR_Uint32_t(v)}\n}\nfunc (XdrType_Fattr4_numlinks) XdrTypeName() string { return \"Fattr4_numlinks\" }\nfunc (v XdrType_Fattr4_numlinks) XdrUnwrap() XdrType { return v.XdrType_Uint32_t }\ntype XdrType_Fattr4_owner struct {\n\tXdrType_Utf8str_mixed\n}\nfunc XDR_Fattr4_owner(v *Fattr4_owner) XdrType_Fattr4_owner {\n\treturn XdrType_Fattr4_owner{XDR_Utf8str_mixed(v)}\n}\nfunc (XdrType_Fattr4_owner) XdrTypeName() string { return \"Fattr4_owner\" }\nfunc (v XdrType_Fattr4_owner) XdrUnwrap() XdrType { return v.XdrType_Utf8str_mixed }\ntype XdrType_Fattr4_owner_group struct {\n\tXdrType_Utf8str_mixed\n}\nfunc XDR_Fattr4_owner_group(v *Fattr4_owner_group) XdrType_Fattr4_owner_group {\n\treturn XdrType_Fattr4_owner_group{XDR_Utf8str_mixed(v)}\n}\nfunc (XdrType_Fattr4_owner_group) XdrTypeName() string { return \"Fattr4_owner_group\" }\nfunc (v XdrType_Fattr4_owner_group) XdrUnwrap() XdrType { return v.XdrType_Utf8str_mixed }\ntype XdrType_Fattr4_quota_avail_hard struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_quota_avail_hard(v *Fattr4_quota_avail_hard) XdrType_Fattr4_quota_avail_hard {\n\treturn XdrType_Fattr4_quota_avail_hard{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_quota_avail_hard) XdrTypeName() string { return \"Fattr4_quota_avail_hard\" }\nfunc (v XdrType_Fattr4_quota_avail_hard) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_quota_avail_soft struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_quota_avail_soft(v *Fattr4_quota_avail_soft) XdrType_Fattr4_quota_avail_soft {\n\treturn XdrType_Fattr4_quota_avail_soft{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_quota_avail_soft) XdrTypeName() string { return \"Fattr4_quota_avail_soft\" }\nfunc (v XdrType_Fattr4_quota_avail_soft) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_quota_used struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_quota_used(v *Fattr4_quota_used) XdrType_Fattr4_quota_used {\n\treturn XdrType_Fattr4_quota_used{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_quota_used) XdrTypeName() string { return \"Fattr4_quota_used\" }\nfunc (v XdrType_Fattr4_quota_used) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_rawdev struct {\n\tXdrType_Specdata4\n}\nfunc XDR_Fattr4_rawdev(v *Fattr4_rawdev) XdrType_Fattr4_rawdev {\n\treturn XdrType_Fattr4_rawdev{XDR_Specdata4(v)}\n}\nfunc (XdrType_Fattr4_rawdev) XdrTypeName() string { return \"Fattr4_rawdev\" }\nfunc (v XdrType_Fattr4_rawdev) XdrUnwrap() XdrType { return v.XdrType_Specdata4 }\ntype XdrType_Fattr4_space_avail struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_space_avail(v *Fattr4_space_avail) XdrType_Fattr4_space_avail {\n\treturn XdrType_Fattr4_space_avail{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_space_avail) XdrTypeName() string { return \"Fattr4_space_avail\" }\nfunc (v XdrType_Fattr4_space_avail) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_space_free struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_space_free(v *Fattr4_space_free) XdrType_Fattr4_space_free {\n\treturn XdrType_Fattr4_space_free{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_space_free) XdrTypeName() string { return \"Fattr4_space_free\" }\nfunc (v XdrType_Fattr4_space_free) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_space_total struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_space_total(v *Fattr4_space_total) XdrType_Fattr4_space_total {\n\treturn XdrType_Fattr4_space_total{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_space_total) XdrTypeName() string { return \"Fattr4_space_total\" }\nfunc (v XdrType_Fattr4_space_total) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_space_used struct {\n\tXdrType_Uint64_t\n}\nfunc XDR_Fattr4_space_used(v *Fattr4_space_used) XdrType_Fattr4_space_used {\n\treturn XdrType_Fattr4_space_used{XDR_Uint64_t(v)}\n}\nfunc (XdrType_Fattr4_space_used) XdrTypeName() string { return \"Fattr4_space_used\" }\nfunc (v XdrType_Fattr4_space_used) XdrUnwrap() XdrType { return v.XdrType_Uint64_t }\ntype XdrType_Fattr4_system struct {\n\tXdrType_bool\n}\nfunc XDR_Fattr4_system(v *Fattr4_system) XdrType_Fattr4_system {\n\treturn XdrType_Fattr4_system{XDR_bool(v)}\n}\nfunc (XdrType_Fattr4_system) XdrTypeName() string { return \"Fattr4_system\" }\nfunc (v XdrType_Fattr4_system) XdrUnwrap() XdrType { return v.XdrType_bool }\ntype XdrType_Fattr4_time_access struct {\n\tXdrType_Nfstime4\n}\nfunc XDR_Fattr4_time_access(v *Fattr4_time_access) XdrType_Fattr4_time_access {\n\treturn XdrType_Fattr4_time_access{XDR_Nfstime4(v)}\n}\nfunc (XdrType_Fattr4_time_access) XdrTypeName() string { return \"Fattr4_time_access\" }\nfunc (v XdrType_Fattr4_time_access) XdrUnwrap() XdrType { return v.XdrType_Nfstime4 }\ntype XdrType_Fattr4_time_access_set struct {\n\tXdrType_Settime4\n}\nfunc XDR_Fattr4_time_access_set(v *Fattr4_time_access_set) XdrType_Fattr4_time_access_set {\n\treturn XdrType_Fattr4_time_access_set{XDR_Settime4(v)}\n}\nfunc (XdrType_Fattr4_time_access_set) XdrTypeName() string { return \"Fattr4_time_access_set\" }\nfunc (v XdrType_Fattr4_time_access_set) XdrUnwrap() XdrType { return v.XdrType_Settime4 }\ntype XdrType_Fattr4_time_backup struct {\n\tXdrType_Nfstime4\n}\nfunc XDR_Fattr4_time_backup(v *Fattr4_time_backup) XdrType_Fattr4_time_backup {\n\treturn XdrType_Fattr4_time_backup{XDR_Nfstime4(v)}\n}\nfunc (XdrType_Fattr4_time_backup) XdrTypeName() string { return \"Fattr4_time_backup\" }\nfunc (v XdrType_Fattr4_time_backup) XdrUnwrap() XdrType { return v.XdrType_Nfstime4 }\ntype XdrType_Fattr4_time_create struct {\n\tXdrType_Nfstime4\n}\nfunc XDR_Fattr4_time_create(v *Fattr4_time_create) XdrType_Fattr4_time_create {\n\treturn XdrType_Fattr4_time_create{XDR_Nfstime4(v)}\n}\nfunc (XdrType_Fattr4_time_create) XdrTypeName() string { return \"Fattr4_time_create\" }\nfunc (v XdrType_Fattr4_time_create) XdrUnwrap() XdrType { return v.XdrType_Nfstime4 }\ntype XdrType_Fattr4_time_delta struct {\n\tXdrType_Nfstime4\n}\nfunc XDR_Fattr4_time_delta(v *Fattr4_time_delta) XdrType_Fattr4_time_delta {\n\treturn XdrType_Fattr4_time_delta{XDR_Nfstime4(v)}\n}\nfunc (XdrType_Fattr4_time_delta) XdrTypeName() string { return \"Fattr4_time_delta\" }\nfunc (v XdrType_Fattr4_time_delta) XdrUnwrap() XdrType { return v.XdrType_Nfstime4 }\ntype XdrType_Fattr4_time_metadata struct {\n\tXdrType_Nfstime4\n}\nfunc XDR_Fattr4_time_metadata(v *Fattr4_time_metadata) XdrType_Fattr4_time_metadata {\n\treturn XdrType_Fattr4_time_metadata{XDR_Nfstime4(v)}\n}\nfunc (XdrType_Fattr4_time_metadata) XdrTypeName() string { return \"Fattr4_time_metadata\" }\nfunc (v XdrType_Fattr4_time_metadata) XdrUnwrap() XdrType { return v.XdrType_Nfstime4 }\ntype XdrType_Fattr4_time_modify struct {\n\tXdrType_Nfstime4\n}\nfunc XDR_Fattr4_time_modify(v *Fattr4_time_modify) XdrType_Fattr4_time_modify {\n\treturn XdrType_Fattr4_time_modify{XDR_Nfstime4(v)}\n}\nfunc (XdrType_Fattr4_time_modify) XdrTypeName() string { return \"Fattr4_time_modify\" }\nfunc (v XdrType_Fattr4_time_modify) XdrUnwrap() XdrType { return v.XdrType_Nfstime4 }\ntype XdrType_Fattr4_time_modify_set struct {\n\tXdrType_Settime4\n}\nfunc XDR_Fattr4_time_modify_set(v *Fattr4_time_modify_set) XdrType_Fattr4_time_modify_set {\n\treturn XdrType_Fattr4_time_modify_set{XDR_Settime4(v)}\n}\nfunc (XdrType_Fattr4_time_modify_set) XdrTypeName() string { return \"Fattr4_time_modify_set\" }\nfunc (v XdrType_Fattr4_time_modify_set) XdrUnwrap() XdrType { return v.XdrType_Settime4 }\ntype XdrType_Attrlist4 struct {\n\tXdrVecOpaque\n}\nfunc XDR_Attrlist4(v *Attrlist4) XdrType_Attrlist4 {\n\treturn XdrType_Attrlist4{XdrVecOpaque{v, 0xffffffff}}\n}\nfunc (XdrType_Attrlist4) XdrTypeName() string { return \"Attrlist4\" }\nfunc (v XdrType_Attrlist4) XdrUnwrap() XdrType { return v.XdrVecOpaque }\ntype XdrType_Fattr4 = *Fattr4\nfunc (v *Fattr4) XdrPointer() interface{} { return v }\nfunc (Fattr4) XdrTypeName() string { return \"Fattr4\" }\nfunc (v Fattr4) XdrValue() interface{} { return v }\nfunc (v *Fattr4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Fattr4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sattrmask\", name), XDR_Bitmap4(&v.Attrmask))\n\tx.Marshal(x.Sprintf(\"%sattr_vals\", name), XDR_Attrlist4(&v.Attr_vals))\n}\nfunc XDR_Fattr4(v *Fattr4) *Fattr4 { return v }\ntype XdrType_Change_info4 = *Change_info4\nfunc (v *Change_info4) XdrPointer() interface{} { return v }\nfunc (Change_info4) XdrTypeName() string { return \"Change_info4\" }\nfunc (v Change_info4) XdrValue() interface{} { return v }\nfunc (v *Change_info4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Change_info4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%satomic\", name), XDR_bool(&v.Atomic))\n\tx.Marshal(x.Sprintf(\"%sbefore\", name), XDR_Changeid4(&v.Before))\n\tx.Marshal(x.Sprintf(\"%safter\", name), XDR_Changeid4(&v.After))\n}\nfunc XDR_Change_info4(v *Change_info4) *Change_info4 { return v }\ntype XdrType_Clientaddr4 = *Clientaddr4\nfunc (v *Clientaddr4) XdrPointer() interface{} { return v }\nfunc (Clientaddr4) XdrTypeName() string { return \"Clientaddr4\" }\nfunc (v Clientaddr4) XdrValue() interface{} { return v }\nfunc (v *Clientaddr4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Clientaddr4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sr_netid\", name), XdrString{&v.R_netid, 0xffffffff})\n\tx.Marshal(x.Sprintf(\"%sr_addr\", name), XdrString{&v.R_addr, 0xffffffff})\n}\nfunc XDR_Clientaddr4(v *Clientaddr4) *Clientaddr4 { return v }\ntype XdrType_Cb_client4 = *Cb_client4\nfunc (v *Cb_client4) XdrPointer() interface{} { return v }\nfunc (Cb_client4) XdrTypeName() string { return \"Cb_client4\" }\nfunc (v Cb_client4) XdrValue() interface{} { return v }\nfunc (v *Cb_client4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Cb_client4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%scb_program\", name), XDR_Uint32_t(&v.Cb_program))\n\tx.Marshal(x.Sprintf(\"%scb_location\", name), XDR_Clientaddr4(&v.Cb_location))\n}\nfunc XDR_Cb_client4(v *Cb_client4) *Cb_client4 { return v }\ntype _XdrArray_12_opaque [12]byte\nfunc (v *_XdrArray_12_opaque) GetByteSlice() []byte { return v[:] }\nfunc (v *_XdrArray_12_opaque) XdrTypeName() string { return \"opaque[]\" }\nfunc (v *_XdrArray_12_opaque) XdrValue() interface{} { return v[:] }\nfunc (v *_XdrArray_12_opaque) XdrPointer() interface{} { return (*[12]byte)(v) }\nfunc (v *_XdrArray_12_opaque) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *_XdrArray_12_opaque) String() string { return fmt.Sprintf(\"%x\", v[:]) }\nfunc (v *_XdrArray_12_opaque) Scan(ss fmt.ScanState, c rune) error {\n\treturn XdrArrayOpaqueScan(v[:], ss, c)\n}\nfunc (_XdrArray_12_opaque) XdrArraySize() uint32 {\n\tconst bound uint32 = 12 // Force error if not const or doesn't fit\n\treturn bound\n}\ntype XdrType_Stateid4 = *Stateid4\nfunc (v *Stateid4) XdrPointer() interface{} { return v }\nfunc (Stateid4) XdrTypeName() string { return \"Stateid4\" }\nfunc (v Stateid4) XdrValue() interface{} { return v }\nfunc (v *Stateid4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Stateid4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sseqid\", name), XDR_Uint32_t(&v.Seqid))\n\tx.Marshal(x.Sprintf(\"%sother\", name), (*_XdrArray_12_opaque)(&v.Other))\n}\nfunc XDR_Stateid4(v *Stateid4) *Stateid4 { return v }\ntype XdrType_Nfs_client_id4 = *Nfs_client_id4\nfunc (v *Nfs_client_id4) XdrPointer() interface{} { return v }\nfunc (Nfs_client_id4) XdrTypeName() string { return \"Nfs_client_id4\" }\nfunc (v Nfs_client_id4) XdrValue() interface{} { return v }\nfunc (v *Nfs_client_id4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Nfs_client_id4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sverifier\", name), XDR_Verifier4(&v.Verifier))\n\tx.Marshal(x.Sprintf(\"%sid\", name), XdrVecOpaque{&v.Id, NFS4_OPAQUE_LIMIT})\n}\nfunc XDR_Nfs_client_id4(v *Nfs_client_id4) *Nfs_client_id4 { return v }\ntype XdrType_Open_owner4 = *Open_owner4\nfunc (v *Open_owner4) XdrPointer() interface{} { return v }\nfunc (Open_owner4) XdrTypeName() string { return \"Open_owner4\" }\nfunc (v Open_owner4) XdrValue() interface{} { return v }\nfunc (v *Open_owner4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Open_owner4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sclientid\", name), XDR_Clientid4(&v.Clientid))\n\tx.Marshal(x.Sprintf(\"%sowner\", name), XdrVecOpaque{&v.Owner, NFS4_OPAQUE_LIMIT})\n}\nfunc XDR_Open_owner4(v *Open_owner4) *Open_owner4 { return v }\ntype XdrType_Lock_owner4 = *Lock_owner4\nfunc (v *Lock_owner4) XdrPointer() interface{} { return v }\nfunc (Lock_owner4) XdrTypeName() string { return \"Lock_owner4\" }\nfunc (v Lock_owner4) XdrValue() interface{} { return v }\nfunc (v *Lock_owner4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Lock_owner4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sclientid\", name), XDR_Clientid4(&v.Clientid))\n\tx.Marshal(x.Sprintf(\"%sowner\", name), XdrVecOpaque{&v.Owner, NFS4_OPAQUE_LIMIT})\n}\nfunc XDR_Lock_owner4(v *Lock_owner4) *Lock_owner4 { return v }\nvar _XdrNames_Nfs_lock_type4 = map[int32]string{\n\tint32(READ_LT): \"READ_LT\",\n\tint32(WRITE_LT): \"WRITE_LT\",\n\tint32(READW_LT): \"READW_LT\",\n\tint32(WRITEW_LT): \"WRITEW_LT\",\n}\nvar _XdrValues_Nfs_lock_type4 = map[string]int32{\n\t\"READ_LT\": int32(READ_LT),\n\t\"WRITE_LT\": int32(WRITE_LT),\n\t\"READW_LT\": int32(READW_LT),\n\t\"WRITEW_LT\": int32(WRITEW_LT),\n}\nfunc (Nfs_lock_type4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Nfs_lock_type4\n}\nfunc (v Nfs_lock_type4) String() string {\n\tif s, ok := _XdrNames_Nfs_lock_type4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Nfs_lock_type4#%d\", v)\n}\nfunc (v *Nfs_lock_type4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Nfs_lock_type4[stok]; ok {\n\t\t\t*v = Nfs_lock_type4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Nfs_lock_type4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Nfs_lock_type4.\", stok))\n\t}\n}\nfunc (v Nfs_lock_type4) GetU32() uint32 { return uint32(v) }\nfunc (v *Nfs_lock_type4) SetU32(n uint32) { *v = Nfs_lock_type4(n) }\nfunc (v *Nfs_lock_type4) XdrPointer() interface{} { return v }\nfunc (Nfs_lock_type4) XdrTypeName() string { return \"Nfs_lock_type4\" }\nfunc (v Nfs_lock_type4) XdrValue() interface{} { return v }\nfunc (v *Nfs_lock_type4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Nfs_lock_type4 = *Nfs_lock_type4\nfunc XDR_Nfs_lock_type4(v *Nfs_lock_type4) *Nfs_lock_type4 { return v }\nfunc (v *Nfs_lock_type4) XdrInitialize() {\n\tswitch Nfs_lock_type4(0) {\n\tcase READ_LT, WRITE_LT, READW_LT, WRITEW_LT:\n\tdefault:\n\t\tif *v == Nfs_lock_type4(0) { *v = READ_LT }\n\t}\n}\ntype XdrType_ACCESS4args = *ACCESS4args\nfunc (v *ACCESS4args) XdrPointer() interface{} { return v }\nfunc (ACCESS4args) XdrTypeName() string { return \"ACCESS4args\" }\nfunc (v ACCESS4args) XdrValue() interface{} { return v }\nfunc (v *ACCESS4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *ACCESS4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%saccess\", name), XDR_Uint32_t(&v.Access))\n}\nfunc XDR_ACCESS4args(v *ACCESS4args) *ACCESS4args { return v }\ntype XdrType_ACCESS4resok = *ACCESS4resok\nfunc (v *ACCESS4resok) XdrPointer() interface{} { return v }\nfunc (ACCESS4resok) XdrTypeName() string { return \"ACCESS4resok\" }\nfunc (v ACCESS4resok) XdrValue() interface{} { return v }\nfunc (v *ACCESS4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *ACCESS4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%ssupported\", name), XDR_Uint32_t(&v.Supported))\n\tx.Marshal(x.Sprintf(\"%saccess\", name), XDR_Uint32_t(&v.Access))\n}\nfunc XDR_ACCESS4resok(v *ACCESS4resok) *ACCESS4resok { return v }\nfunc (u *ACCESS4res) Resok4() *ACCESS4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*ACCESS4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero ACCESS4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"ACCESS4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u ACCESS4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *ACCESS4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *ACCESS4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *ACCESS4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_ACCESS4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *ACCESS4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_ACCESS4res = *ACCESS4res\nfunc (v *ACCESS4res) XdrPointer() interface{} { return v }\nfunc (ACCESS4res) XdrTypeName() string { return \"ACCESS4res\" }\nfunc (v ACCESS4res) XdrValue() interface{} { return v }\nfunc (v *ACCESS4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *ACCESS4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_ACCESS4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_ACCESS4res(v *ACCESS4res) *ACCESS4res { return v}\ntype XdrType_CLOSE4args = *CLOSE4args\nfunc (v *CLOSE4args) XdrPointer() interface{} { return v }\nfunc (CLOSE4args) XdrTypeName() string { return \"CLOSE4args\" }\nfunc (v CLOSE4args) XdrValue() interface{} { return v }\nfunc (v *CLOSE4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *CLOSE4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sseqid\", name), XDR_Seqid4(&v.Seqid))\n\tx.Marshal(x.Sprintf(\"%sopen_stateid\", name), XDR_Stateid4(&v.Open_stateid))\n}\nfunc XDR_CLOSE4args(v *CLOSE4args) *CLOSE4args { return v }\nfunc (u *CLOSE4res) Open_stateid() *Stateid4 {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*Stateid4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Stateid4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"CLOSE4res.Open_stateid accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u CLOSE4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *CLOSE4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *CLOSE4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *CLOSE4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_Stateid4(u.Open_stateid())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *CLOSE4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Open_stateid\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_CLOSE4res = *CLOSE4res\nfunc (v *CLOSE4res) XdrPointer() interface{} { return v }\nfunc (CLOSE4res) XdrTypeName() string { return \"CLOSE4res\" }\nfunc (v CLOSE4res) XdrValue() interface{} { return v }\nfunc (v *CLOSE4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *CLOSE4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sopen_stateid\", name), XDR_Stateid4(u.Open_stateid()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_CLOSE4res(v *CLOSE4res) *CLOSE4res { return v}\ntype XdrType_COMMIT4args = *COMMIT4args\nfunc (v *COMMIT4args) XdrPointer() interface{} { return v }\nfunc (COMMIT4args) XdrTypeName() string { return \"COMMIT4args\" }\nfunc (v COMMIT4args) XdrValue() interface{} { return v }\nfunc (v *COMMIT4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *COMMIT4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%soffset\", name), XDR_Offset4(&v.Offset))\n\tx.Marshal(x.Sprintf(\"%scount\", name), XDR_Count4(&v.Count))\n}\nfunc XDR_COMMIT4args(v *COMMIT4args) *COMMIT4args { return v }\ntype XdrType_COMMIT4resok = *COMMIT4resok\nfunc (v *COMMIT4resok) XdrPointer() interface{} { return v }\nfunc (COMMIT4resok) XdrTypeName() string { return \"COMMIT4resok\" }\nfunc (v COMMIT4resok) XdrValue() interface{} { return v }\nfunc (v *COMMIT4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *COMMIT4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%swriteverf\", name), XDR_Verifier4(&v.Writeverf))\n}\nfunc XDR_COMMIT4resok(v *COMMIT4resok) *COMMIT4resok { return v }\nfunc (u *COMMIT4res) Resok4() *COMMIT4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*COMMIT4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero COMMIT4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"COMMIT4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u COMMIT4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *COMMIT4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *COMMIT4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *COMMIT4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_COMMIT4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *COMMIT4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_COMMIT4res = *COMMIT4res\nfunc (v *COMMIT4res) XdrPointer() interface{} { return v }\nfunc (COMMIT4res) XdrTypeName() string { return \"COMMIT4res\" }\nfunc (v COMMIT4res) XdrValue() interface{} { return v }\nfunc (v *COMMIT4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *COMMIT4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_COMMIT4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_COMMIT4res(v *COMMIT4res) *COMMIT4res { return v}\nfunc (u *Createtype4) Linkdata() *Linktext4 {\n\tswitch u.Type {\n\tcase NF4LNK:\n\t\tif v, ok := u.U.(*Linktext4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Linktext4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Createtype4.Linkdata accessed when Type == %v\", u.Type)\n\t\treturn nil\n\t}\n}\nfunc (u *Createtype4) Devdata() *Specdata4 {\n\tswitch u.Type {\n\tcase NF4BLK, NF4CHR:\n\t\tif v, ok := u.U.(*Specdata4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Specdata4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Createtype4.Devdata accessed when Type == %v\", u.Type)\n\t\treturn nil\n\t}\n}\nfunc (u Createtype4) XdrValid() bool {\n\treturn true\n}\nfunc (u *Createtype4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfs_ftype4(&u.Type)\n}\nfunc (u *Createtype4) XdrUnionTagName() string {\n\treturn \"Type\"\n}\nfunc (u *Createtype4) XdrUnionBody() XdrType {\n\tswitch u.Type {\n\tcase NF4LNK:\n\t\treturn XDR_Linktext4(u.Linkdata())\n\tcase NF4BLK, NF4CHR:\n\t\treturn XDR_Specdata4(u.Devdata())\n\tcase NF4SOCK, NF4FIFO, NF4DIR:\n\t\treturn nil\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *Createtype4) XdrUnionBodyName() string {\n\tswitch u.Type {\n\tcase NF4LNK:\n\t\treturn \"Linkdata\"\n\tcase NF4BLK, NF4CHR:\n\t\treturn \"Devdata\"\n\tcase NF4SOCK, NF4FIFO, NF4DIR:\n\t\treturn \"\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_Createtype4 = *Createtype4\nfunc (v *Createtype4) XdrPointer() interface{} { return v }\nfunc (Createtype4) XdrTypeName() string { return \"Createtype4\" }\nfunc (v Createtype4) XdrValue() interface{} { return v }\nfunc (v *Createtype4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Createtype4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfs_ftype4(&u.Type).XdrMarshal(x, x.Sprintf(\"%stype\", name))\n\tswitch u.Type {\n\tcase NF4LNK:\n\t\tx.Marshal(x.Sprintf(\"%slinkdata\", name), XDR_Linktext4(u.Linkdata()))\n\t\treturn\n\tcase NF4BLK, NF4CHR:\n\t\tx.Marshal(x.Sprintf(\"%sdevdata\", name), XDR_Specdata4(u.Devdata()))\n\t\treturn\n\tcase NF4SOCK, NF4FIFO, NF4DIR:\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_Createtype4(v *Createtype4) *Createtype4 { return v}\ntype XdrType_CREATE4args = *CREATE4args\nfunc (v *CREATE4args) XdrPointer() interface{} { return v }\nfunc (CREATE4args) XdrTypeName() string { return \"CREATE4args\" }\nfunc (v CREATE4args) XdrValue() interface{} { return v }\nfunc (v *CREATE4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *CREATE4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sobjtype\", name), XDR_Createtype4(&v.Objtype))\n\tx.Marshal(x.Sprintf(\"%sobjname\", name), XDR_Component4(&v.Objname))\n\tx.Marshal(x.Sprintf(\"%screateattrs\", name), XDR_Fattr4(&v.Createattrs))\n}\nfunc XDR_CREATE4args(v *CREATE4args) *CREATE4args { return v }\ntype XdrType_CREATE4resok = *CREATE4resok\nfunc (v *CREATE4resok) XdrPointer() interface{} { return v }\nfunc (CREATE4resok) XdrTypeName() string { return \"CREATE4resok\" }\nfunc (v CREATE4resok) XdrValue() interface{} { return v }\nfunc (v *CREATE4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *CREATE4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%scinfo\", name), XDR_Change_info4(&v.Cinfo))\n\tx.Marshal(x.Sprintf(\"%sattrset\", name), XDR_Bitmap4(&v.Attrset))\n}\nfunc XDR_CREATE4resok(v *CREATE4resok) *CREATE4resok { return v }\nfunc (u *CREATE4res) Resok4() *CREATE4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*CREATE4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CREATE4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"CREATE4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u CREATE4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *CREATE4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *CREATE4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *CREATE4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_CREATE4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *CREATE4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_CREATE4res = *CREATE4res\nfunc (v *CREATE4res) XdrPointer() interface{} { return v }\nfunc (CREATE4res) XdrTypeName() string { return \"CREATE4res\" }\nfunc (v CREATE4res) XdrValue() interface{} { return v }\nfunc (v *CREATE4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *CREATE4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_CREATE4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_CREATE4res(v *CREATE4res) *CREATE4res { return v}\ntype XdrType_DELEGPURGE4args = *DELEGPURGE4args\nfunc (v *DELEGPURGE4args) XdrPointer() interface{} { return v }\nfunc (DELEGPURGE4args) XdrTypeName() string { return \"DELEGPURGE4args\" }\nfunc (v DELEGPURGE4args) XdrValue() interface{} { return v }\nfunc (v *DELEGPURGE4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *DELEGPURGE4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sclientid\", name), XDR_Clientid4(&v.Clientid))\n}\nfunc XDR_DELEGPURGE4args(v *DELEGPURGE4args) *DELEGPURGE4args { return v }\ntype XdrType_DELEGPURGE4res = *DELEGPURGE4res\nfunc (v *DELEGPURGE4res) XdrPointer() interface{} { return v }\nfunc (DELEGPURGE4res) XdrTypeName() string { return \"DELEGPURGE4res\" }\nfunc (v DELEGPURGE4res) XdrValue() interface{} { return v }\nfunc (v *DELEGPURGE4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *DELEGPURGE4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_DELEGPURGE4res(v *DELEGPURGE4res) *DELEGPURGE4res { return v }\ntype XdrType_DELEGRETURN4args = *DELEGRETURN4args\nfunc (v *DELEGRETURN4args) XdrPointer() interface{} { return v }\nfunc (DELEGRETURN4args) XdrTypeName() string { return \"DELEGRETURN4args\" }\nfunc (v DELEGRETURN4args) XdrValue() interface{} { return v }\nfunc (v *DELEGRETURN4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *DELEGRETURN4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sdeleg_stateid\", name), XDR_Stateid4(&v.Deleg_stateid))\n}\nfunc XDR_DELEGRETURN4args(v *DELEGRETURN4args) *DELEGRETURN4args { return v }\ntype XdrType_DELEGRETURN4res = *DELEGRETURN4res\nfunc (v *DELEGRETURN4res) XdrPointer() interface{} { return v }\nfunc (DELEGRETURN4res) XdrTypeName() string { return \"DELEGRETURN4res\" }\nfunc (v DELEGRETURN4res) XdrValue() interface{} { return v }\nfunc (v *DELEGRETURN4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *DELEGRETURN4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_DELEGRETURN4res(v *DELEGRETURN4res) *DELEGRETURN4res { return v }\ntype XdrType_GETATTR4args = *GETATTR4args\nfunc (v *GETATTR4args) XdrPointer() interface{} { return v }\nfunc (GETATTR4args) XdrTypeName() string { return \"GETATTR4args\" }\nfunc (v GETATTR4args) XdrValue() interface{} { return v }\nfunc (v *GETATTR4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *GETATTR4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sattr_request\", name), XDR_Bitmap4(&v.Attr_request))\n}\nfunc XDR_GETATTR4args(v *GETATTR4args) *GETATTR4args { return v }\ntype XdrType_GETATTR4resok = *GETATTR4resok\nfunc (v *GETATTR4resok) XdrPointer() interface{} { return v }\nfunc (GETATTR4resok) XdrTypeName() string { return \"GETATTR4resok\" }\nfunc (v GETATTR4resok) XdrValue() interface{} { return v }\nfunc (v *GETATTR4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *GETATTR4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sobj_attributes\", name), XDR_Fattr4(&v.Obj_attributes))\n}\nfunc XDR_GETATTR4resok(v *GETATTR4resok) *GETATTR4resok { return v }\nfunc (u *GETATTR4res) Resok4() *GETATTR4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*GETATTR4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GETATTR4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"GETATTR4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u GETATTR4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *GETATTR4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *GETATTR4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *GETATTR4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_GETATTR4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *GETATTR4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_GETATTR4res = *GETATTR4res\nfunc (v *GETATTR4res) XdrPointer() interface{} { return v }\nfunc (GETATTR4res) XdrTypeName() string { return \"GETATTR4res\" }\nfunc (v GETATTR4res) XdrValue() interface{} { return v }\nfunc (v *GETATTR4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *GETATTR4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_GETATTR4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_GETATTR4res(v *GETATTR4res) *GETATTR4res { return v}\ntype XdrType_GETFH4resok = *GETFH4resok\nfunc (v *GETFH4resok) XdrPointer() interface{} { return v }\nfunc (GETFH4resok) XdrTypeName() string { return \"GETFH4resok\" }\nfunc (v GETFH4resok) XdrValue() interface{} { return v }\nfunc (v *GETFH4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *GETFH4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sobject\", name), XDR_Nfs_fh4(&v.Object))\n}\nfunc XDR_GETFH4resok(v *GETFH4resok) *GETFH4resok { return v }\nfunc (u *GETFH4res) Resok4() *GETFH4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*GETFH4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GETFH4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"GETFH4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u GETFH4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *GETFH4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *GETFH4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *GETFH4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_GETFH4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *GETFH4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_GETFH4res = *GETFH4res\nfunc (v *GETFH4res) XdrPointer() interface{} { return v }\nfunc (GETFH4res) XdrTypeName() string { return \"GETFH4res\" }\nfunc (v GETFH4res) XdrValue() interface{} { return v }\nfunc (v *GETFH4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *GETFH4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_GETFH4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_GETFH4res(v *GETFH4res) *GETFH4res { return v}\ntype XdrType_LINK4args = *LINK4args\nfunc (v *LINK4args) XdrPointer() interface{} { return v }\nfunc (LINK4args) XdrTypeName() string { return \"LINK4args\" }\nfunc (v LINK4args) XdrValue() interface{} { return v }\nfunc (v *LINK4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LINK4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%snewname\", name), XDR_Component4(&v.Newname))\n}\nfunc XDR_LINK4args(v *LINK4args) *LINK4args { return v }\ntype XdrType_LINK4resok = *LINK4resok\nfunc (v *LINK4resok) XdrPointer() interface{} { return v }\nfunc (LINK4resok) XdrTypeName() string { return \"LINK4resok\" }\nfunc (v LINK4resok) XdrValue() interface{} { return v }\nfunc (v *LINK4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LINK4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%scinfo\", name), XDR_Change_info4(&v.Cinfo))\n}\nfunc XDR_LINK4resok(v *LINK4resok) *LINK4resok { return v }\nfunc (u *LINK4res) Resok4() *LINK4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*LINK4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LINK4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"LINK4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u LINK4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *LINK4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *LINK4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *LINK4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_LINK4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *LINK4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_LINK4res = *LINK4res\nfunc (v *LINK4res) XdrPointer() interface{} { return v }\nfunc (LINK4res) XdrTypeName() string { return \"LINK4res\" }\nfunc (v LINK4res) XdrValue() interface{} { return v }\nfunc (v *LINK4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *LINK4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_LINK4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_LINK4res(v *LINK4res) *LINK4res { return v}\ntype XdrType_Open_to_lock_owner4 = *Open_to_lock_owner4\nfunc (v *Open_to_lock_owner4) XdrPointer() interface{} { return v }\nfunc (Open_to_lock_owner4) XdrTypeName() string { return \"Open_to_lock_owner4\" }\nfunc (v Open_to_lock_owner4) XdrValue() interface{} { return v }\nfunc (v *Open_to_lock_owner4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Open_to_lock_owner4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sopen_seqid\", name), XDR_Seqid4(&v.Open_seqid))\n\tx.Marshal(x.Sprintf(\"%sopen_stateid\", name), XDR_Stateid4(&v.Open_stateid))\n\tx.Marshal(x.Sprintf(\"%slock_seqid\", name), XDR_Seqid4(&v.Lock_seqid))\n\tx.Marshal(x.Sprintf(\"%slock_owner\", name), XDR_Lock_owner4(&v.Lock_owner))\n}\nfunc XDR_Open_to_lock_owner4(v *Open_to_lock_owner4) *Open_to_lock_owner4 { return v }\ntype XdrType_Exist_lock_owner4 = *Exist_lock_owner4\nfunc (v *Exist_lock_owner4) XdrPointer() interface{} { return v }\nfunc (Exist_lock_owner4) XdrTypeName() string { return \"Exist_lock_owner4\" }\nfunc (v Exist_lock_owner4) XdrValue() interface{} { return v }\nfunc (v *Exist_lock_owner4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Exist_lock_owner4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slock_stateid\", name), XDR_Stateid4(&v.Lock_stateid))\n\tx.Marshal(x.Sprintf(\"%slock_seqid\", name), XDR_Seqid4(&v.Lock_seqid))\n}\nfunc XDR_Exist_lock_owner4(v *Exist_lock_owner4) *Exist_lock_owner4 { return v }\nvar _XdrTags_Locker4 = map[int32]bool{\n\tXdrToI32(true): true,\n\tXdrToI32(false): true,\n}\nfunc (_ Locker4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Locker4\n}\nfunc (u *Locker4) Open_owner() *Open_to_lock_owner4 {\n\tswitch u.New_lock_owner {\n\tcase true:\n\t\tif v, ok := u.U.(*Open_to_lock_owner4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Open_to_lock_owner4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Locker4.Open_owner accessed when New_lock_owner == %v\", u.New_lock_owner)\n\t\treturn nil\n\t}\n}\nfunc (u *Locker4) Lock_owner() *Exist_lock_owner4 {\n\tswitch u.New_lock_owner {\n\tcase false:\n\t\tif v, ok := u.U.(*Exist_lock_owner4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Exist_lock_owner4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Locker4.Lock_owner accessed when New_lock_owner == %v\", u.New_lock_owner)\n\t\treturn nil\n\t}\n}\nfunc (u Locker4) XdrValid() bool {\n\tswitch u.New_lock_owner {\n\tcase true,false:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Locker4) XdrUnionTag() XdrNum32 {\n\treturn XDR_bool(&u.New_lock_owner)\n}\nfunc (u *Locker4) XdrUnionTagName() string {\n\treturn \"New_lock_owner\"\n}\nfunc (u *Locker4) XdrUnionBody() XdrType {\n\tswitch u.New_lock_owner {\n\tcase true:\n\t\treturn XDR_Open_to_lock_owner4(u.Open_owner())\n\tcase false:\n\t\treturn XDR_Exist_lock_owner4(u.Lock_owner())\n\t}\n\treturn nil\n}\nfunc (u *Locker4) XdrUnionBodyName() string {\n\tswitch u.New_lock_owner {\n\tcase true:\n\t\treturn \"Open_owner\"\n\tcase false:\n\t\treturn \"Lock_owner\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Locker4 = *Locker4\nfunc (v *Locker4) XdrPointer() interface{} { return v }\nfunc (Locker4) XdrTypeName() string { return \"Locker4\" }\nfunc (v Locker4) XdrValue() interface{} { return v }\nfunc (v *Locker4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Locker4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_bool(&u.New_lock_owner).XdrMarshal(x, x.Sprintf(\"%snew_lock_owner\", name))\n\tswitch u.New_lock_owner {\n\tcase true:\n\t\tx.Marshal(x.Sprintf(\"%sopen_owner\", name), XDR_Open_to_lock_owner4(u.Open_owner()))\n\t\treturn\n\tcase false:\n\t\tx.Marshal(x.Sprintf(\"%slock_owner\", name), XDR_Exist_lock_owner4(u.Lock_owner()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid New_lock_owner (%v) in Locker4\", u.New_lock_owner)\n}\nfunc XDR_Locker4(v *Locker4) *Locker4 { return v}\ntype XdrType_LOCK4args = *LOCK4args\nfunc (v *LOCK4args) XdrPointer() interface{} { return v }\nfunc (LOCK4args) XdrTypeName() string { return \"LOCK4args\" }\nfunc (v LOCK4args) XdrValue() interface{} { return v }\nfunc (v *LOCK4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LOCK4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slocktype\", name), XDR_Nfs_lock_type4(&v.Locktype))\n\tx.Marshal(x.Sprintf(\"%sreclaim\", name), XDR_bool(&v.Reclaim))\n\tx.Marshal(x.Sprintf(\"%soffset\", name), XDR_Offset4(&v.Offset))\n\tx.Marshal(x.Sprintf(\"%slength\", name), XDR_Length4(&v.Length))\n\tx.Marshal(x.Sprintf(\"%slocker\", name), XDR_Locker4(&v.Locker))\n}\nfunc XDR_LOCK4args(v *LOCK4args) *LOCK4args { return v }\ntype XdrType_LOCK4denied = *LOCK4denied\nfunc (v *LOCK4denied) XdrPointer() interface{} { return v }\nfunc (LOCK4denied) XdrTypeName() string { return \"LOCK4denied\" }\nfunc (v LOCK4denied) XdrValue() interface{} { return v }\nfunc (v *LOCK4denied) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LOCK4denied) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%soffset\", name), XDR_Offset4(&v.Offset))\n\tx.Marshal(x.Sprintf(\"%slength\", name), XDR_Length4(&v.Length))\n\tx.Marshal(x.Sprintf(\"%slocktype\", name), XDR_Nfs_lock_type4(&v.Locktype))\n\tx.Marshal(x.Sprintf(\"%sowner\", name), XDR_Lock_owner4(&v.Owner))\n}\nfunc XDR_LOCK4denied(v *LOCK4denied) *LOCK4denied { return v }\ntype XdrType_LOCK4resok = *LOCK4resok\nfunc (v *LOCK4resok) XdrPointer() interface{} { return v }\nfunc (LOCK4resok) XdrTypeName() string { return \"LOCK4resok\" }\nfunc (v LOCK4resok) XdrValue() interface{} { return v }\nfunc (v *LOCK4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LOCK4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slock_stateid\", name), XDR_Stateid4(&v.Lock_stateid))\n}\nfunc XDR_LOCK4resok(v *LOCK4resok) *LOCK4resok { return v }\nfunc (u *LOCK4res) Resok4() *LOCK4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*LOCK4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LOCK4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"LOCK4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u *LOCK4res) Denied() *LOCK4denied {\n\tswitch u.Status {\n\tcase NFS4ERR_DENIED:\n\t\tif v, ok := u.U.(*LOCK4denied); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LOCK4denied\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"LOCK4res.Denied accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u LOCK4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *LOCK4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *LOCK4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *LOCK4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_LOCK4resok(u.Resok4())\n\tcase NFS4ERR_DENIED:\n\t\treturn XDR_LOCK4denied(u.Denied())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *LOCK4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tcase NFS4ERR_DENIED:\n\t\treturn \"Denied\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_LOCK4res = *LOCK4res\nfunc (v *LOCK4res) XdrPointer() interface{} { return v }\nfunc (LOCK4res) XdrTypeName() string { return \"LOCK4res\" }\nfunc (v LOCK4res) XdrValue() interface{} { return v }\nfunc (v *LOCK4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *LOCK4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_LOCK4resok(u.Resok4()))\n\t\treturn\n\tcase NFS4ERR_DENIED:\n\t\tx.Marshal(x.Sprintf(\"%sdenied\", name), XDR_LOCK4denied(u.Denied()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_LOCK4res(v *LOCK4res) *LOCK4res { return v}\ntype XdrType_LOCKT4args = *LOCKT4args\nfunc (v *LOCKT4args) XdrPointer() interface{} { return v }\nfunc (LOCKT4args) XdrTypeName() string { return \"LOCKT4args\" }\nfunc (v LOCKT4args) XdrValue() interface{} { return v }\nfunc (v *LOCKT4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LOCKT4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slocktype\", name), XDR_Nfs_lock_type4(&v.Locktype))\n\tx.Marshal(x.Sprintf(\"%soffset\", name), XDR_Offset4(&v.Offset))\n\tx.Marshal(x.Sprintf(\"%slength\", name), XDR_Length4(&v.Length))\n\tx.Marshal(x.Sprintf(\"%sowner\", name), XDR_Lock_owner4(&v.Owner))\n}\nfunc XDR_LOCKT4args(v *LOCKT4args) *LOCKT4args { return v }\nfunc (u *LOCKT4res) Denied() *LOCK4denied {\n\tswitch u.Status {\n\tcase NFS4ERR_DENIED:\n\t\tif v, ok := u.U.(*LOCK4denied); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LOCK4denied\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"LOCKT4res.Denied accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u LOCKT4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *LOCKT4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *LOCKT4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *LOCKT4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4ERR_DENIED:\n\t\treturn XDR_LOCK4denied(u.Denied())\n\tcase NFS4_OK:\n\t\treturn nil\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *LOCKT4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4ERR_DENIED:\n\t\treturn \"Denied\"\n\tcase NFS4_OK:\n\t\treturn \"\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_LOCKT4res = *LOCKT4res\nfunc (v *LOCKT4res) XdrPointer() interface{} { return v }\nfunc (LOCKT4res) XdrTypeName() string { return \"LOCKT4res\" }\nfunc (v LOCKT4res) XdrValue() interface{} { return v }\nfunc (v *LOCKT4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *LOCKT4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4ERR_DENIED:\n\t\tx.Marshal(x.Sprintf(\"%sdenied\", name), XDR_LOCK4denied(u.Denied()))\n\t\treturn\n\tcase NFS4_OK:\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_LOCKT4res(v *LOCKT4res) *LOCKT4res { return v}\ntype XdrType_LOCKU4args = *LOCKU4args\nfunc (v *LOCKU4args) XdrPointer() interface{} { return v }\nfunc (LOCKU4args) XdrTypeName() string { return \"LOCKU4args\" }\nfunc (v LOCKU4args) XdrValue() interface{} { return v }\nfunc (v *LOCKU4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LOCKU4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slocktype\", name), XDR_Nfs_lock_type4(&v.Locktype))\n\tx.Marshal(x.Sprintf(\"%sseqid\", name), XDR_Seqid4(&v.Seqid))\n\tx.Marshal(x.Sprintf(\"%slock_stateid\", name), XDR_Stateid4(&v.Lock_stateid))\n\tx.Marshal(x.Sprintf(\"%soffset\", name), XDR_Offset4(&v.Offset))\n\tx.Marshal(x.Sprintf(\"%slength\", name), XDR_Length4(&v.Length))\n}\nfunc XDR_LOCKU4args(v *LOCKU4args) *LOCKU4args { return v }\nfunc (u *LOCKU4res) Lock_stateid() *Stateid4 {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*Stateid4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Stateid4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"LOCKU4res.Lock_stateid accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u LOCKU4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *LOCKU4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *LOCKU4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *LOCKU4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_Stateid4(u.Lock_stateid())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *LOCKU4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Lock_stateid\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_LOCKU4res = *LOCKU4res\nfunc (v *LOCKU4res) XdrPointer() interface{} { return v }\nfunc (LOCKU4res) XdrTypeName() string { return \"LOCKU4res\" }\nfunc (v LOCKU4res) XdrValue() interface{} { return v }\nfunc (v *LOCKU4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *LOCKU4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%slock_stateid\", name), XDR_Stateid4(u.Lock_stateid()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_LOCKU4res(v *LOCKU4res) *LOCKU4res { return v}\ntype XdrType_LOOKUP4args = *LOOKUP4args\nfunc (v *LOOKUP4args) XdrPointer() interface{} { return v }\nfunc (LOOKUP4args) XdrTypeName() string { return \"LOOKUP4args\" }\nfunc (v LOOKUP4args) XdrValue() interface{} { return v }\nfunc (v *LOOKUP4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LOOKUP4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sobjname\", name), XDR_Component4(&v.Objname))\n}\nfunc XDR_LOOKUP4args(v *LOOKUP4args) *LOOKUP4args { return v }\ntype XdrType_LOOKUP4res = *LOOKUP4res\nfunc (v *LOOKUP4res) XdrPointer() interface{} { return v }\nfunc (LOOKUP4res) XdrTypeName() string { return \"LOOKUP4res\" }\nfunc (v LOOKUP4res) XdrValue() interface{} { return v }\nfunc (v *LOOKUP4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LOOKUP4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_LOOKUP4res(v *LOOKUP4res) *LOOKUP4res { return v }\ntype XdrType_LOOKUPP4res = *LOOKUPP4res\nfunc (v *LOOKUPP4res) XdrPointer() interface{} { return v }\nfunc (LOOKUPP4res) XdrTypeName() string { return \"LOOKUPP4res\" }\nfunc (v LOOKUPP4res) XdrValue() interface{} { return v }\nfunc (v *LOOKUPP4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LOOKUPP4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_LOOKUPP4res(v *LOOKUPP4res) *LOOKUPP4res { return v }\ntype XdrType_NVERIFY4args = *NVERIFY4args\nfunc (v *NVERIFY4args) XdrPointer() interface{} { return v }\nfunc (NVERIFY4args) XdrTypeName() string { return \"NVERIFY4args\" }\nfunc (v NVERIFY4args) XdrValue() interface{} { return v }\nfunc (v *NVERIFY4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *NVERIFY4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sobj_attributes\", name), XDR_Fattr4(&v.Obj_attributes))\n}\nfunc XDR_NVERIFY4args(v *NVERIFY4args) *NVERIFY4args { return v }\ntype XdrType_NVERIFY4res = *NVERIFY4res\nfunc (v *NVERIFY4res) XdrPointer() interface{} { return v }\nfunc (NVERIFY4res) XdrTypeName() string { return \"NVERIFY4res\" }\nfunc (v NVERIFY4res) XdrValue() interface{} { return v }\nfunc (v *NVERIFY4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *NVERIFY4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_NVERIFY4res(v *NVERIFY4res) *NVERIFY4res { return v }\nvar _XdrNames_Createmode4 = map[int32]string{\n\tint32(UNCHECKED4): \"UNCHECKED4\",\n\tint32(GUARDED4): \"GUARDED4\",\n\tint32(EXCLUSIVE4): \"EXCLUSIVE4\",\n}\nvar _XdrValues_Createmode4 = map[string]int32{\n\t\"UNCHECKED4\": int32(UNCHECKED4),\n\t\"GUARDED4\": int32(GUARDED4),\n\t\"EXCLUSIVE4\": int32(EXCLUSIVE4),\n}\nfunc (Createmode4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Createmode4\n}\nfunc (v Createmode4) String() string {\n\tif s, ok := _XdrNames_Createmode4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Createmode4#%d\", v)\n}\nfunc (v *Createmode4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Createmode4[stok]; ok {\n\t\t\t*v = Createmode4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Createmode4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Createmode4.\", stok))\n\t}\n}\nfunc (v Createmode4) GetU32() uint32 { return uint32(v) }\nfunc (v *Createmode4) SetU32(n uint32) { *v = Createmode4(n) }\nfunc (v *Createmode4) XdrPointer() interface{} { return v }\nfunc (Createmode4) XdrTypeName() string { return \"Createmode4\" }\nfunc (v Createmode4) XdrValue() interface{} { return v }\nfunc (v *Createmode4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Createmode4 = *Createmode4\nfunc XDR_Createmode4(v *Createmode4) *Createmode4 { return v }\nvar _XdrTags_Createhow4 = map[int32]bool{\n\tXdrToI32(UNCHECKED4): true,\n\tXdrToI32(GUARDED4): true,\n\tXdrToI32(EXCLUSIVE4): true,\n}\nfunc (_ Createhow4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Createhow4\n}\nfunc (u *Createhow4) Createattrs() *Fattr4 {\n\tswitch u.Mode {\n\tcase UNCHECKED4, GUARDED4:\n\t\tif v, ok := u.U.(*Fattr4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Fattr4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Createhow4.Createattrs accessed when Mode == %v\", u.Mode)\n\t\treturn nil\n\t}\n}\nfunc (u *Createhow4) Createverf() *Verifier4 {\n\tswitch u.Mode {\n\tcase EXCLUSIVE4:\n\t\tif v, ok := u.U.(*Verifier4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Verifier4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Createhow4.Createverf accessed when Mode == %v\", u.Mode)\n\t\treturn nil\n\t}\n}\nfunc (u Createhow4) XdrValid() bool {\n\tswitch u.Mode {\n\tcase UNCHECKED4, GUARDED4,EXCLUSIVE4:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Createhow4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Createmode4(&u.Mode)\n}\nfunc (u *Createhow4) XdrUnionTagName() string {\n\treturn \"Mode\"\n}\nfunc (u *Createhow4) XdrUnionBody() XdrType {\n\tswitch u.Mode {\n\tcase UNCHECKED4, GUARDED4:\n\t\treturn XDR_Fattr4(u.Createattrs())\n\tcase EXCLUSIVE4:\n\t\treturn XDR_Verifier4(u.Createverf())\n\t}\n\treturn nil\n}\nfunc (u *Createhow4) XdrUnionBodyName() string {\n\tswitch u.Mode {\n\tcase UNCHECKED4, GUARDED4:\n\t\treturn \"Createattrs\"\n\tcase EXCLUSIVE4:\n\t\treturn \"Createverf\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Createhow4 = *Createhow4\nfunc (v *Createhow4) XdrPointer() interface{} { return v }\nfunc (Createhow4) XdrTypeName() string { return \"Createhow4\" }\nfunc (v Createhow4) XdrValue() interface{} { return v }\nfunc (v *Createhow4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Createhow4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Createmode4(&u.Mode).XdrMarshal(x, x.Sprintf(\"%smode\", name))\n\tswitch u.Mode {\n\tcase UNCHECKED4, GUARDED4:\n\t\tx.Marshal(x.Sprintf(\"%screateattrs\", name), XDR_Fattr4(u.Createattrs()))\n\t\treturn\n\tcase EXCLUSIVE4:\n\t\tx.Marshal(x.Sprintf(\"%screateverf\", name), XDR_Verifier4(u.Createverf()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Mode (%v) in Createhow4\", u.Mode)\n}\nfunc XDR_Createhow4(v *Createhow4) *Createhow4 { return v}\nvar _XdrNames_Opentype4 = map[int32]string{\n\tint32(OPEN4_NOCREATE): \"OPEN4_NOCREATE\",\n\tint32(OPEN4_CREATE): \"OPEN4_CREATE\",\n}\nvar _XdrValues_Opentype4 = map[string]int32{\n\t\"OPEN4_NOCREATE\": int32(OPEN4_NOCREATE),\n\t\"OPEN4_CREATE\": int32(OPEN4_CREATE),\n}\nfunc (Opentype4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Opentype4\n}\nfunc (v Opentype4) String() string {\n\tif s, ok := _XdrNames_Opentype4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Opentype4#%d\", v)\n}\nfunc (v *Opentype4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Opentype4[stok]; ok {\n\t\t\t*v = Opentype4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Opentype4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Opentype4.\", stok))\n\t}\n}\nfunc (v Opentype4) GetU32() uint32 { return uint32(v) }\nfunc (v *Opentype4) SetU32(n uint32) { *v = Opentype4(n) }\nfunc (v *Opentype4) XdrPointer() interface{} { return v }\nfunc (Opentype4) XdrTypeName() string { return \"Opentype4\" }\nfunc (v Opentype4) XdrValue() interface{} { return v }\nfunc (v *Opentype4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Opentype4 = *Opentype4\nfunc XDR_Opentype4(v *Opentype4) *Opentype4 { return v }\nfunc (u *Openflag4) How() *Createhow4 {\n\tswitch u.Opentype {\n\tcase OPEN4_CREATE:\n\t\tif v, ok := u.U.(*Createhow4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Createhow4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Openflag4.How accessed when Opentype == %v\", u.Opentype)\n\t\treturn nil\n\t}\n}\nfunc (u Openflag4) XdrValid() bool {\n\treturn true\n}\nfunc (u *Openflag4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Opentype4(&u.Opentype)\n}\nfunc (u *Openflag4) XdrUnionTagName() string {\n\treturn \"Opentype\"\n}\nfunc (u *Openflag4) XdrUnionBody() XdrType {\n\tswitch u.Opentype {\n\tcase OPEN4_CREATE:\n\t\treturn XDR_Createhow4(u.How())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *Openflag4) XdrUnionBodyName() string {\n\tswitch u.Opentype {\n\tcase OPEN4_CREATE:\n\t\treturn \"How\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_Openflag4 = *Openflag4\nfunc (v *Openflag4) XdrPointer() interface{} { return v }\nfunc (Openflag4) XdrTypeName() string { return \"Openflag4\" }\nfunc (v Openflag4) XdrValue() interface{} { return v }\nfunc (v *Openflag4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Openflag4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Opentype4(&u.Opentype).XdrMarshal(x, x.Sprintf(\"%sopentype\", name))\n\tswitch u.Opentype {\n\tcase OPEN4_CREATE:\n\t\tx.Marshal(x.Sprintf(\"%show\", name), XDR_Createhow4(u.How()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_Openflag4(v *Openflag4) *Openflag4 { return v}\nvar _XdrNames_Limit_by4 = map[int32]string{\n\tint32(NFS_LIMIT_SIZE): \"NFS_LIMIT_SIZE\",\n\tint32(NFS_LIMIT_BLOCKS): \"NFS_LIMIT_BLOCKS\",\n}\nvar _XdrValues_Limit_by4 = map[string]int32{\n\t\"NFS_LIMIT_SIZE\": int32(NFS_LIMIT_SIZE),\n\t\"NFS_LIMIT_BLOCKS\": int32(NFS_LIMIT_BLOCKS),\n}\nfunc (Limit_by4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Limit_by4\n}\nfunc (v Limit_by4) String() string {\n\tif s, ok := _XdrNames_Limit_by4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Limit_by4#%d\", v)\n}\nfunc (v *Limit_by4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Limit_by4[stok]; ok {\n\t\t\t*v = Limit_by4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Limit_by4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Limit_by4.\", stok))\n\t}\n}\nfunc (v Limit_by4) GetU32() uint32 { return uint32(v) }\nfunc (v *Limit_by4) SetU32(n uint32) { *v = Limit_by4(n) }\nfunc (v *Limit_by4) XdrPointer() interface{} { return v }\nfunc (Limit_by4) XdrTypeName() string { return \"Limit_by4\" }\nfunc (v Limit_by4) XdrValue() interface{} { return v }\nfunc (v *Limit_by4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Limit_by4 = *Limit_by4\nfunc XDR_Limit_by4(v *Limit_by4) *Limit_by4 { return v }\nfunc (v *Limit_by4) XdrInitialize() {\n\tswitch Limit_by4(0) {\n\tcase NFS_LIMIT_SIZE, NFS_LIMIT_BLOCKS:\n\tdefault:\n\t\tif *v == Limit_by4(0) { *v = NFS_LIMIT_SIZE }\n\t}\n}\ntype XdrType_Nfs_modified_limit4 = *Nfs_modified_limit4\nfunc (v *Nfs_modified_limit4) XdrPointer() interface{} { return v }\nfunc (Nfs_modified_limit4) XdrTypeName() string { return \"Nfs_modified_limit4\" }\nfunc (v Nfs_modified_limit4) XdrValue() interface{} { return v }\nfunc (v *Nfs_modified_limit4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Nfs_modified_limit4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%snum_blocks\", name), XDR_Uint32_t(&v.Num_blocks))\n\tx.Marshal(x.Sprintf(\"%sbytes_per_block\", name), XDR_Uint32_t(&v.Bytes_per_block))\n}\nfunc XDR_Nfs_modified_limit4(v *Nfs_modified_limit4) *Nfs_modified_limit4 { return v }\nvar _XdrTags_Nfs_space_limit4 = map[int32]bool{\n\tXdrToI32(NFS_LIMIT_SIZE): true,\n\tXdrToI32(NFS_LIMIT_BLOCKS): true,\n}\nfunc (_ Nfs_space_limit4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Nfs_space_limit4\n}\nfunc (u *Nfs_space_limit4) Filesize() *Uint64_t {\n\tswitch u.Limitby {\n\tcase NFS_LIMIT_SIZE:\n\t\tif v, ok := u.U.(*Uint64_t); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Uint64_t\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_space_limit4.Filesize accessed when Limitby == %v\", u.Limitby)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_space_limit4) Mod_blocks() *Nfs_modified_limit4 {\n\tswitch u.Limitby {\n\tcase NFS_LIMIT_BLOCKS:\n\t\tif v, ok := u.U.(*Nfs_modified_limit4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Nfs_modified_limit4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_space_limit4.Mod_blocks accessed when Limitby == %v\", u.Limitby)\n\t\treturn nil\n\t}\n}\nfunc (u Nfs_space_limit4) XdrValid() bool {\n\tswitch u.Limitby {\n\tcase NFS_LIMIT_SIZE,NFS_LIMIT_BLOCKS:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Nfs_space_limit4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Limit_by4(&u.Limitby)\n}\nfunc (u *Nfs_space_limit4) XdrUnionTagName() string {\n\treturn \"Limitby\"\n}\nfunc (u *Nfs_space_limit4) XdrUnionBody() XdrType {\n\tswitch u.Limitby {\n\tcase NFS_LIMIT_SIZE:\n\t\treturn XDR_Uint64_t(u.Filesize())\n\tcase NFS_LIMIT_BLOCKS:\n\t\treturn XDR_Nfs_modified_limit4(u.Mod_blocks())\n\t}\n\treturn nil\n}\nfunc (u *Nfs_space_limit4) XdrUnionBodyName() string {\n\tswitch u.Limitby {\n\tcase NFS_LIMIT_SIZE:\n\t\treturn \"Filesize\"\n\tcase NFS_LIMIT_BLOCKS:\n\t\treturn \"Mod_blocks\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Nfs_space_limit4 = *Nfs_space_limit4\nfunc (v *Nfs_space_limit4) XdrPointer() interface{} { return v }\nfunc (Nfs_space_limit4) XdrTypeName() string { return \"Nfs_space_limit4\" }\nfunc (v Nfs_space_limit4) XdrValue() interface{} { return v }\nfunc (v *Nfs_space_limit4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Nfs_space_limit4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Limit_by4(&u.Limitby).XdrMarshal(x, x.Sprintf(\"%slimitby\", name))\n\tswitch u.Limitby {\n\tcase NFS_LIMIT_SIZE:\n\t\tx.Marshal(x.Sprintf(\"%sfilesize\", name), XDR_Uint64_t(u.Filesize()))\n\t\treturn\n\tcase NFS_LIMIT_BLOCKS:\n\t\tx.Marshal(x.Sprintf(\"%smod_blocks\", name), XDR_Nfs_modified_limit4(u.Mod_blocks()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Limitby (%v) in Nfs_space_limit4\", u.Limitby)\n}\nfunc (v *Nfs_space_limit4) XdrInitialize() {\n\tvar zero Limit_by4\n\tswitch zero {\n\tcase NFS_LIMIT_SIZE, NFS_LIMIT_BLOCKS:\n\tdefault:\n\t\tif v.Limitby == zero { v.Limitby = NFS_LIMIT_SIZE }\n\t}\n}\nfunc XDR_Nfs_space_limit4(v *Nfs_space_limit4) *Nfs_space_limit4 { return v}\nvar _XdrNames_Open_delegation_type4 = map[int32]string{\n\tint32(OPEN_DELEGATE_NONE): \"OPEN_DELEGATE_NONE\",\n\tint32(OPEN_DELEGATE_READ): \"OPEN_DELEGATE_READ\",\n\tint32(OPEN_DELEGATE_WRITE): \"OPEN_DELEGATE_WRITE\",\n}\nvar _XdrValues_Open_delegation_type4 = map[string]int32{\n\t\"OPEN_DELEGATE_NONE\": int32(OPEN_DELEGATE_NONE),\n\t\"OPEN_DELEGATE_READ\": int32(OPEN_DELEGATE_READ),\n\t\"OPEN_DELEGATE_WRITE\": int32(OPEN_DELEGATE_WRITE),\n}\nfunc (Open_delegation_type4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Open_delegation_type4\n}\nfunc (v Open_delegation_type4) String() string {\n\tif s, ok := _XdrNames_Open_delegation_type4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Open_delegation_type4#%d\", v)\n}\nfunc (v *Open_delegation_type4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Open_delegation_type4[stok]; ok {\n\t\t\t*v = Open_delegation_type4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Open_delegation_type4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Open_delegation_type4.\", stok))\n\t}\n}\nfunc (v Open_delegation_type4) GetU32() uint32 { return uint32(v) }\nfunc (v *Open_delegation_type4) SetU32(n uint32) { *v = Open_delegation_type4(n) }\nfunc (v *Open_delegation_type4) XdrPointer() interface{} { return v }\nfunc (Open_delegation_type4) XdrTypeName() string { return \"Open_delegation_type4\" }\nfunc (v Open_delegation_type4) XdrValue() interface{} { return v }\nfunc (v *Open_delegation_type4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Open_delegation_type4 = *Open_delegation_type4\nfunc XDR_Open_delegation_type4(v *Open_delegation_type4) *Open_delegation_type4 { return v }\nvar _XdrNames_Open_claim_type4 = map[int32]string{\n\tint32(CLAIM_NULL): \"CLAIM_NULL\",\n\tint32(CLAIM_PREVIOUS): \"CLAIM_PREVIOUS\",\n\tint32(CLAIM_DELEGATE_CUR): \"CLAIM_DELEGATE_CUR\",\n\tint32(CLAIM_DELEGATE_PREV): \"CLAIM_DELEGATE_PREV\",\n\tint32(CLAIM_FH): \"CLAIM_FH\",\n\tint32(CLAIM_DELEG_CUR_FH): \"CLAIM_DELEG_CUR_FH\",\n\tint32(CLAIM_DELEG_PREV_FH): \"CLAIM_DELEG_PREV_FH\",\n}\nvar _XdrValues_Open_claim_type4 = map[string]int32{\n\t\"CLAIM_NULL\": int32(CLAIM_NULL),\n\t\"CLAIM_PREVIOUS\": int32(CLAIM_PREVIOUS),\n\t\"CLAIM_DELEGATE_CUR\": int32(CLAIM_DELEGATE_CUR),\n\t\"CLAIM_DELEGATE_PREV\": int32(CLAIM_DELEGATE_PREV),\n\t\"CLAIM_FH\": int32(CLAIM_FH),\n\t\"CLAIM_DELEG_CUR_FH\": int32(CLAIM_DELEG_CUR_FH),\n\t\"CLAIM_DELEG_PREV_FH\": int32(CLAIM_DELEG_PREV_FH),\n}\nfunc (Open_claim_type4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Open_claim_type4\n}\nfunc (v Open_claim_type4) String() string {\n\tif s, ok := _XdrNames_Open_claim_type4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Open_claim_type4#%d\", v)\n}\nfunc (v *Open_claim_type4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Open_claim_type4[stok]; ok {\n\t\t\t*v = Open_claim_type4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Open_claim_type4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Open_claim_type4.\", stok))\n\t}\n}\nfunc (v Open_claim_type4) GetU32() uint32 { return uint32(v) }\nfunc (v *Open_claim_type4) SetU32(n uint32) { *v = Open_claim_type4(n) }\nfunc (v *Open_claim_type4) XdrPointer() interface{} { return v }\nfunc (Open_claim_type4) XdrTypeName() string { return \"Open_claim_type4\" }\nfunc (v Open_claim_type4) XdrValue() interface{} { return v }\nfunc (v *Open_claim_type4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Open_claim_type4 = *Open_claim_type4\nfunc XDR_Open_claim_type4(v *Open_claim_type4) *Open_claim_type4 { return v }\ntype XdrType_Open_claim_delegate_cur4 = *Open_claim_delegate_cur4\nfunc (v *Open_claim_delegate_cur4) XdrPointer() interface{} { return v }\nfunc (Open_claim_delegate_cur4) XdrTypeName() string { return \"Open_claim_delegate_cur4\" }\nfunc (v Open_claim_delegate_cur4) XdrValue() interface{} { return v }\nfunc (v *Open_claim_delegate_cur4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Open_claim_delegate_cur4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sdelegate_stateid\", name), XDR_Stateid4(&v.Delegate_stateid))\n\tx.Marshal(x.Sprintf(\"%sfile\", name), XDR_Component4(&v.File))\n}\nfunc XDR_Open_claim_delegate_cur4(v *Open_claim_delegate_cur4) *Open_claim_delegate_cur4 { return v }\nvar _XdrTags_Open_claim4 = map[int32]bool{\n\tXdrToI32(CLAIM_NULL): true,\n\tXdrToI32(CLAIM_PREVIOUS): true,\n\tXdrToI32(CLAIM_DELEGATE_CUR): true,\n\tXdrToI32(CLAIM_DELEGATE_PREV): true,\n}\nfunc (_ Open_claim4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Open_claim4\n}\n/* CURRENT_FH: directory */\nfunc (u *Open_claim4) File() *Component4 {\n\tswitch u.Claim {\n\tcase CLAIM_NULL:\n\t\tif v, ok := u.U.(*Component4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Component4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Open_claim4.File accessed when Claim == %v\", u.Claim)\n\t\treturn nil\n\t}\n}\n/* CURRENT_FH: file being reclaimed */\nfunc (u *Open_claim4) Delegate_type() *Open_delegation_type4 {\n\tswitch u.Claim {\n\tcase CLAIM_PREVIOUS:\n\t\tif v, ok := u.U.(*Open_delegation_type4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Open_delegation_type4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Open_claim4.Delegate_type accessed when Claim == %v\", u.Claim)\n\t\treturn nil\n\t}\n}\n/* CURRENT_FH: directory */\nfunc (u *Open_claim4) Delegate_cur_info() *Open_claim_delegate_cur4 {\n\tswitch u.Claim {\n\tcase CLAIM_DELEGATE_CUR:\n\t\tif v, ok := u.U.(*Open_claim_delegate_cur4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Open_claim_delegate_cur4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Open_claim4.Delegate_cur_info accessed when Claim == %v\", u.Claim)\n\t\treturn nil\n\t}\n}\n/* CURRENT_FH: directory */\nfunc (u *Open_claim4) File_delegate_prev() *Component4 {\n\tswitch u.Claim {\n\tcase CLAIM_DELEGATE_PREV:\n\t\tif v, ok := u.U.(*Component4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Component4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Open_claim4.File_delegate_prev accessed when Claim == %v\", u.Claim)\n\t\treturn nil\n\t}\n}\nfunc (u Open_claim4) XdrValid() bool {\n\tswitch u.Claim {\n\tcase CLAIM_NULL,CLAIM_PREVIOUS,CLAIM_DELEGATE_CUR,CLAIM_DELEGATE_PREV:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Open_claim4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Open_claim_type4(&u.Claim)\n}\nfunc (u *Open_claim4) XdrUnionTagName() string {\n\treturn \"Claim\"\n}\nfunc (u *Open_claim4) XdrUnionBody() XdrType {\n\tswitch u.Claim {\n\tcase CLAIM_NULL:\n\t\treturn XDR_Component4(u.File())\n\tcase CLAIM_PREVIOUS:\n\t\treturn XDR_Open_delegation_type4(u.Delegate_type())\n\tcase CLAIM_DELEGATE_CUR:\n\t\treturn XDR_Open_claim_delegate_cur4(u.Delegate_cur_info())\n\tcase CLAIM_DELEGATE_PREV:\n\t\treturn XDR_Component4(u.File_delegate_prev())\n\t}\n\treturn nil\n}\nfunc (u *Open_claim4) XdrUnionBodyName() string {\n\tswitch u.Claim {\n\tcase CLAIM_NULL:\n\t\treturn \"File\"\n\tcase CLAIM_PREVIOUS:\n\t\treturn \"Delegate_type\"\n\tcase CLAIM_DELEGATE_CUR:\n\t\treturn \"Delegate_cur_info\"\n\tcase CLAIM_DELEGATE_PREV:\n\t\treturn \"File_delegate_prev\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Open_claim4 = *Open_claim4\nfunc (v *Open_claim4) XdrPointer() interface{} { return v }\nfunc (Open_claim4) XdrTypeName() string { return \"Open_claim4\" }\nfunc (v Open_claim4) XdrValue() interface{} { return v }\nfunc (v *Open_claim4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Open_claim4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Open_claim_type4(&u.Claim).XdrMarshal(x, x.Sprintf(\"%sclaim\", name))\n\tswitch u.Claim {\n\tcase CLAIM_NULL:\n\t\tx.Marshal(x.Sprintf(\"%sfile\", name), XDR_Component4(u.File()))\n\t\treturn\n\tcase CLAIM_PREVIOUS:\n\t\tx.Marshal(x.Sprintf(\"%sdelegate_type\", name), XDR_Open_delegation_type4(u.Delegate_type()))\n\t\treturn\n\tcase CLAIM_DELEGATE_CUR:\n\t\tx.Marshal(x.Sprintf(\"%sdelegate_cur_info\", name), XDR_Open_claim_delegate_cur4(u.Delegate_cur_info()))\n\t\treturn\n\tcase CLAIM_DELEGATE_PREV:\n\t\tx.Marshal(x.Sprintf(\"%sfile_delegate_prev\", name), XDR_Component4(u.File_delegate_prev()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Claim (%v) in Open_claim4\", u.Claim)\n}\nfunc XDR_Open_claim4(v *Open_claim4) *Open_claim4 { return v}\ntype XdrType_OPEN4args = *OPEN4args\nfunc (v *OPEN4args) XdrPointer() interface{} { return v }\nfunc (OPEN4args) XdrTypeName() string { return \"OPEN4args\" }\nfunc (v OPEN4args) XdrValue() interface{} { return v }\nfunc (v *OPEN4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *OPEN4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sseqid\", name), XDR_Seqid4(&v.Seqid))\n\tx.Marshal(x.Sprintf(\"%sshare_access\", name), XDR_Uint32_t(&v.Share_access))\n\tx.Marshal(x.Sprintf(\"%sshare_deny\", name), XDR_Uint32_t(&v.Share_deny))\n\tx.Marshal(x.Sprintf(\"%sowner\", name), XDR_Open_owner4(&v.Owner))\n\tx.Marshal(x.Sprintf(\"%sopenhow\", name), XDR_Openflag4(&v.Openhow))\n\tx.Marshal(x.Sprintf(\"%sclaim\", name), XDR_Open_claim4(&v.Claim))\n}\nfunc XDR_OPEN4args(v *OPEN4args) *OPEN4args { return v }\ntype XdrType_Open_read_delegation4 = *Open_read_delegation4\nfunc (v *Open_read_delegation4) XdrPointer() interface{} { return v }\nfunc (Open_read_delegation4) XdrTypeName() string { return \"Open_read_delegation4\" }\nfunc (v Open_read_delegation4) XdrValue() interface{} { return v }\nfunc (v *Open_read_delegation4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Open_read_delegation4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstateid\", name), XDR_Stateid4(&v.Stateid))\n\tx.Marshal(x.Sprintf(\"%srecall\", name), XDR_bool(&v.Recall))\n\tx.Marshal(x.Sprintf(\"%spermissions\", name), XDR_Nfsace4(&v.Permissions))\n}\nfunc XDR_Open_read_delegation4(v *Open_read_delegation4) *Open_read_delegation4 { return v }\ntype XdrType_Open_write_delegation4 = *Open_write_delegation4\nfunc (v *Open_write_delegation4) XdrPointer() interface{} { return v }\nfunc (Open_write_delegation4) XdrTypeName() string { return \"Open_write_delegation4\" }\nfunc (v Open_write_delegation4) XdrValue() interface{} { return v }\nfunc (v *Open_write_delegation4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Open_write_delegation4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstateid\", name), XDR_Stateid4(&v.Stateid))\n\tx.Marshal(x.Sprintf(\"%srecall\", name), XDR_bool(&v.Recall))\n\tx.Marshal(x.Sprintf(\"%sspace_limit\", name), XDR_Nfs_space_limit4(&v.Space_limit))\n\tx.Marshal(x.Sprintf(\"%spermissions\", name), XDR_Nfsace4(&v.Permissions))\n}\nfunc XDR_Open_write_delegation4(v *Open_write_delegation4) *Open_write_delegation4 { return v }\nvar _XdrTags_Open_delegation4 = map[int32]bool{\n\tXdrToI32(OPEN_DELEGATE_NONE): true,\n\tXdrToI32(OPEN_DELEGATE_READ): true,\n\tXdrToI32(OPEN_DELEGATE_WRITE): true,\n}\nfunc (_ Open_delegation4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Open_delegation4\n}\nfunc (u *Open_delegation4) Read() *Open_read_delegation4 {\n\tswitch u.Delegation_type {\n\tcase OPEN_DELEGATE_READ:\n\t\tif v, ok := u.U.(*Open_read_delegation4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Open_read_delegation4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Open_delegation4.Read accessed when Delegation_type == %v\", u.Delegation_type)\n\t\treturn nil\n\t}\n}\nfunc (u *Open_delegation4) Write() *Open_write_delegation4 {\n\tswitch u.Delegation_type {\n\tcase OPEN_DELEGATE_WRITE:\n\t\tif v, ok := u.U.(*Open_write_delegation4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Open_write_delegation4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Open_delegation4.Write accessed when Delegation_type == %v\", u.Delegation_type)\n\t\treturn nil\n\t}\n}\nfunc (u Open_delegation4) XdrValid() bool {\n\tswitch u.Delegation_type {\n\tcase OPEN_DELEGATE_NONE,OPEN_DELEGATE_READ,OPEN_DELEGATE_WRITE:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Open_delegation4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Open_delegation_type4(&u.Delegation_type)\n}\nfunc (u *Open_delegation4) XdrUnionTagName() string {\n\treturn \"Delegation_type\"\n}\nfunc (u *Open_delegation4) XdrUnionBody() XdrType {\n\tswitch u.Delegation_type {\n\tcase OPEN_DELEGATE_NONE:\n\t\treturn nil\n\tcase OPEN_DELEGATE_READ:\n\t\treturn XDR_Open_read_delegation4(u.Read())\n\tcase OPEN_DELEGATE_WRITE:\n\t\treturn XDR_Open_write_delegation4(u.Write())\n\t}\n\treturn nil\n}\nfunc (u *Open_delegation4) XdrUnionBodyName() string {\n\tswitch u.Delegation_type {\n\tcase OPEN_DELEGATE_NONE:\n\t\treturn \"\"\n\tcase OPEN_DELEGATE_READ:\n\t\treturn \"Read\"\n\tcase OPEN_DELEGATE_WRITE:\n\t\treturn \"Write\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Open_delegation4 = *Open_delegation4\nfunc (v *Open_delegation4) XdrPointer() interface{} { return v }\nfunc (Open_delegation4) XdrTypeName() string { return \"Open_delegation4\" }\nfunc (v Open_delegation4) XdrValue() interface{} { return v }\nfunc (v *Open_delegation4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Open_delegation4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Open_delegation_type4(&u.Delegation_type).XdrMarshal(x, x.Sprintf(\"%sdelegation_type\", name))\n\tswitch u.Delegation_type {\n\tcase OPEN_DELEGATE_NONE:\n\t\treturn\n\tcase OPEN_DELEGATE_READ:\n\t\tx.Marshal(x.Sprintf(\"%sread\", name), XDR_Open_read_delegation4(u.Read()))\n\t\treturn\n\tcase OPEN_DELEGATE_WRITE:\n\t\tx.Marshal(x.Sprintf(\"%swrite\", name), XDR_Open_write_delegation4(u.Write()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Delegation_type (%v) in Open_delegation4\", u.Delegation_type)\n}\nfunc XDR_Open_delegation4(v *Open_delegation4) *Open_delegation4 { return v}\ntype XdrType_OPEN4resok = *OPEN4resok\nfunc (v *OPEN4resok) XdrPointer() interface{} { return v }\nfunc (OPEN4resok) XdrTypeName() string { return \"OPEN4resok\" }\nfunc (v OPEN4resok) XdrValue() interface{} { return v }\nfunc (v *OPEN4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *OPEN4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstateid\", name), XDR_Stateid4(&v.Stateid))\n\tx.Marshal(x.Sprintf(\"%scinfo\", name), XDR_Change_info4(&v.Cinfo))\n\tx.Marshal(x.Sprintf(\"%srflags\", name), XDR_Uint32_t(&v.Rflags))\n\tx.Marshal(x.Sprintf(\"%sattrset\", name), XDR_Bitmap4(&v.Attrset))\n\tx.Marshal(x.Sprintf(\"%sdelegation\", name), XDR_Open_delegation4(&v.Delegation))\n}\nfunc XDR_OPEN4resok(v *OPEN4resok) *OPEN4resok { return v }\n/* CURRENT_FH: opened file */\nfunc (u *OPEN4res) Resok4() *OPEN4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*OPEN4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero OPEN4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"OPEN4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u OPEN4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *OPEN4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *OPEN4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *OPEN4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_OPEN4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *OPEN4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_OPEN4res = *OPEN4res\nfunc (v *OPEN4res) XdrPointer() interface{} { return v }\nfunc (OPEN4res) XdrTypeName() string { return \"OPEN4res\" }\nfunc (v OPEN4res) XdrValue() interface{} { return v }\nfunc (v *OPEN4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *OPEN4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_OPEN4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_OPEN4res(v *OPEN4res) *OPEN4res { return v}\ntype XdrType_OPENATTR4args = *OPENATTR4args\nfunc (v *OPENATTR4args) XdrPointer() interface{} { return v }\nfunc (OPENATTR4args) XdrTypeName() string { return \"OPENATTR4args\" }\nfunc (v OPENATTR4args) XdrValue() interface{} { return v }\nfunc (v *OPENATTR4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *OPENATTR4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%screatedir\", name), XDR_bool(&v.Createdir))\n}\nfunc XDR_OPENATTR4args(v *OPENATTR4args) *OPENATTR4args { return v }\ntype XdrType_OPENATTR4res = *OPENATTR4res\nfunc (v *OPENATTR4res) XdrPointer() interface{} { return v }\nfunc (OPENATTR4res) XdrTypeName() string { return \"OPENATTR4res\" }\nfunc (v OPENATTR4res) XdrValue() interface{} { return v }\nfunc (v *OPENATTR4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *OPENATTR4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_OPENATTR4res(v *OPENATTR4res) *OPENATTR4res { return v }\ntype XdrType_OPEN_CONFIRM4args = *OPEN_CONFIRM4args\nfunc (v *OPEN_CONFIRM4args) XdrPointer() interface{} { return v }\nfunc (OPEN_CONFIRM4args) XdrTypeName() string { return \"OPEN_CONFIRM4args\" }\nfunc (v OPEN_CONFIRM4args) XdrValue() interface{} { return v }\nfunc (v *OPEN_CONFIRM4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *OPEN_CONFIRM4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sopen_stateid\", name), XDR_Stateid4(&v.Open_stateid))\n\tx.Marshal(x.Sprintf(\"%sseqid\", name), XDR_Seqid4(&v.Seqid))\n}\nfunc XDR_OPEN_CONFIRM4args(v *OPEN_CONFIRM4args) *OPEN_CONFIRM4args { return v }\ntype XdrType_OPEN_CONFIRM4resok = *OPEN_CONFIRM4resok\nfunc (v *OPEN_CONFIRM4resok) XdrPointer() interface{} { return v }\nfunc (OPEN_CONFIRM4resok) XdrTypeName() string { return \"OPEN_CONFIRM4resok\" }\nfunc (v OPEN_CONFIRM4resok) XdrValue() interface{} { return v }\nfunc (v *OPEN_CONFIRM4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *OPEN_CONFIRM4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sopen_stateid\", name), XDR_Stateid4(&v.Open_stateid))\n}\nfunc XDR_OPEN_CONFIRM4resok(v *OPEN_CONFIRM4resok) *OPEN_CONFIRM4resok { return v }\nfunc (u *OPEN_CONFIRM4res) Resok4() *OPEN_CONFIRM4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*OPEN_CONFIRM4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero OPEN_CONFIRM4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"OPEN_CONFIRM4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u OPEN_CONFIRM4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *OPEN_CONFIRM4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *OPEN_CONFIRM4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *OPEN_CONFIRM4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_OPEN_CONFIRM4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *OPEN_CONFIRM4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_OPEN_CONFIRM4res = *OPEN_CONFIRM4res\nfunc (v *OPEN_CONFIRM4res) XdrPointer() interface{} { return v }\nfunc (OPEN_CONFIRM4res) XdrTypeName() string { return \"OPEN_CONFIRM4res\" }\nfunc (v OPEN_CONFIRM4res) XdrValue() interface{} { return v }\nfunc (v *OPEN_CONFIRM4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *OPEN_CONFIRM4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_OPEN_CONFIRM4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_OPEN_CONFIRM4res(v *OPEN_CONFIRM4res) *OPEN_CONFIRM4res { return v}\ntype XdrType_OPEN_DOWNGRADE4args = *OPEN_DOWNGRADE4args\nfunc (v *OPEN_DOWNGRADE4args) XdrPointer() interface{} { return v }\nfunc (OPEN_DOWNGRADE4args) XdrTypeName() string { return \"OPEN_DOWNGRADE4args\" }\nfunc (v OPEN_DOWNGRADE4args) XdrValue() interface{} { return v }\nfunc (v *OPEN_DOWNGRADE4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *OPEN_DOWNGRADE4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sopen_stateid\", name), XDR_Stateid4(&v.Open_stateid))\n\tx.Marshal(x.Sprintf(\"%sseqid\", name), XDR_Seqid4(&v.Seqid))\n\tx.Marshal(x.Sprintf(\"%sshare_access\", name), XDR_Uint32_t(&v.Share_access))\n\tx.Marshal(x.Sprintf(\"%sshare_deny\", name), XDR_Uint32_t(&v.Share_deny))\n}\nfunc XDR_OPEN_DOWNGRADE4args(v *OPEN_DOWNGRADE4args) *OPEN_DOWNGRADE4args { return v }\ntype XdrType_OPEN_DOWNGRADE4resok = *OPEN_DOWNGRADE4resok\nfunc (v *OPEN_DOWNGRADE4resok) XdrPointer() interface{} { return v }\nfunc (OPEN_DOWNGRADE4resok) XdrTypeName() string { return \"OPEN_DOWNGRADE4resok\" }\nfunc (v OPEN_DOWNGRADE4resok) XdrValue() interface{} { return v }\nfunc (v *OPEN_DOWNGRADE4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *OPEN_DOWNGRADE4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sopen_stateid\", name), XDR_Stateid4(&v.Open_stateid))\n}\nfunc XDR_OPEN_DOWNGRADE4resok(v *OPEN_DOWNGRADE4resok) *OPEN_DOWNGRADE4resok { return v }\nfunc (u *OPEN_DOWNGRADE4res) Resok4() *OPEN_DOWNGRADE4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*OPEN_DOWNGRADE4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero OPEN_DOWNGRADE4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"OPEN_DOWNGRADE4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u OPEN_DOWNGRADE4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *OPEN_DOWNGRADE4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *OPEN_DOWNGRADE4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *OPEN_DOWNGRADE4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_OPEN_DOWNGRADE4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *OPEN_DOWNGRADE4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_OPEN_DOWNGRADE4res = *OPEN_DOWNGRADE4res\nfunc (v *OPEN_DOWNGRADE4res) XdrPointer() interface{} { return v }\nfunc (OPEN_DOWNGRADE4res) XdrTypeName() string { return \"OPEN_DOWNGRADE4res\" }\nfunc (v OPEN_DOWNGRADE4res) XdrValue() interface{} { return v }\nfunc (v *OPEN_DOWNGRADE4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *OPEN_DOWNGRADE4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_OPEN_DOWNGRADE4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_OPEN_DOWNGRADE4res(v *OPEN_DOWNGRADE4res) *OPEN_DOWNGRADE4res { return v}\ntype XdrType_PUTFH4args = *PUTFH4args\nfunc (v *PUTFH4args) XdrPointer() interface{} { return v }\nfunc (PUTFH4args) XdrTypeName() string { return \"PUTFH4args\" }\nfunc (v PUTFH4args) XdrValue() interface{} { return v }\nfunc (v *PUTFH4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *PUTFH4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sobject\", name), XDR_Nfs_fh4(&v.Object))\n}\nfunc XDR_PUTFH4args(v *PUTFH4args) *PUTFH4args { return v }\ntype XdrType_PUTFH4res = *PUTFH4res\nfunc (v *PUTFH4res) XdrPointer() interface{} { return v }\nfunc (PUTFH4res) XdrTypeName() string { return \"PUTFH4res\" }\nfunc (v PUTFH4res) XdrValue() interface{} { return v }\nfunc (v *PUTFH4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *PUTFH4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_PUTFH4res(v *PUTFH4res) *PUTFH4res { return v }\ntype XdrType_PUTPUBFH4res = *PUTPUBFH4res\nfunc (v *PUTPUBFH4res) XdrPointer() interface{} { return v }\nfunc (PUTPUBFH4res) XdrTypeName() string { return \"PUTPUBFH4res\" }\nfunc (v PUTPUBFH4res) XdrValue() interface{} { return v }\nfunc (v *PUTPUBFH4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *PUTPUBFH4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_PUTPUBFH4res(v *PUTPUBFH4res) *PUTPUBFH4res { return v }\ntype XdrType_PUTROOTFH4res = *PUTROOTFH4res\nfunc (v *PUTROOTFH4res) XdrPointer() interface{} { return v }\nfunc (PUTROOTFH4res) XdrTypeName() string { return \"PUTROOTFH4res\" }\nfunc (v PUTROOTFH4res) XdrValue() interface{} { return v }\nfunc (v *PUTROOTFH4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *PUTROOTFH4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_PUTROOTFH4res(v *PUTROOTFH4res) *PUTROOTFH4res { return v }\ntype XdrType_READ4args = *READ4args\nfunc (v *READ4args) XdrPointer() interface{} { return v }\nfunc (READ4args) XdrTypeName() string { return \"READ4args\" }\nfunc (v READ4args) XdrValue() interface{} { return v }\nfunc (v *READ4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *READ4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstateid\", name), XDR_Stateid4(&v.Stateid))\n\tx.Marshal(x.Sprintf(\"%soffset\", name), XDR_Offset4(&v.Offset))\n\tx.Marshal(x.Sprintf(\"%scount\", name), XDR_Count4(&v.Count))\n}\nfunc XDR_READ4args(v *READ4args) *READ4args { return v }\ntype XdrType_READ4resok = *READ4resok\nfunc (v *READ4resok) XdrPointer() interface{} { return v }\nfunc (READ4resok) XdrTypeName() string { return \"READ4resok\" }\nfunc (v READ4resok) XdrValue() interface{} { return v }\nfunc (v *READ4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *READ4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%seof\", name), XDR_bool(&v.Eof))\n\tx.Marshal(x.Sprintf(\"%sdata\", name), XdrVecOpaque{&v.Data, 0xffffffff})\n}\nfunc XDR_READ4resok(v *READ4resok) *READ4resok { return v }\nfunc (u *READ4res) Resok4() *READ4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*READ4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero READ4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"READ4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u READ4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *READ4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *READ4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *READ4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_READ4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *READ4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_READ4res = *READ4res\nfunc (v *READ4res) XdrPointer() interface{} { return v }\nfunc (READ4res) XdrTypeName() string { return \"READ4res\" }\nfunc (v READ4res) XdrValue() interface{} { return v }\nfunc (v *READ4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *READ4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_READ4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_READ4res(v *READ4res) *READ4res { return v}\ntype XdrType_READDIR4args = *READDIR4args\nfunc (v *READDIR4args) XdrPointer() interface{} { return v }\nfunc (READDIR4args) XdrTypeName() string { return \"READDIR4args\" }\nfunc (v READDIR4args) XdrValue() interface{} { return v }\nfunc (v *READDIR4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *READDIR4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%scookie\", name), XDR_Nfs_cookie4(&v.Cookie))\n\tx.Marshal(x.Sprintf(\"%scookieverf\", name), XDR_Verifier4(&v.Cookieverf))\n\tx.Marshal(x.Sprintf(\"%sdircount\", name), XDR_Count4(&v.Dircount))\n\tx.Marshal(x.Sprintf(\"%smaxcount\", name), XDR_Count4(&v.Maxcount))\n\tx.Marshal(x.Sprintf(\"%sattr_request\", name), XDR_Bitmap4(&v.Attr_request))\n}\nfunc XDR_READDIR4args(v *READDIR4args) *READDIR4args { return v }\ntype _XdrPtr_Entry4 struct {\n\tp **Entry4\n}\ntype _ptrflag_Entry4 _XdrPtr_Entry4\nfunc (v _ptrflag_Entry4) String() string {\n\tif *v.p == nil {\n\t\treturn \"nil\"\n\t}\n\treturn \"non-nil\"\n}\nfunc (v _ptrflag_Entry4) Scan(ss fmt.ScanState, r rune) error {\n\ttok, err := ss.Token(true, func(c rune) bool {\n\t\treturn c == '-' || (c >= 'a' && c <= 'z')\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tswitch string(tok) {\n\tcase \"nil\":\n\t\tv.SetU32(0)\n\tcase \"non-nil\":\n\t\tv.SetU32(1)\n\tdefault:\n\t\treturn XdrError(\"Entry4 flag should be \\\"nil\\\" or \\\"non-nil\\\"\")\n\t}\n\treturn nil\n}\nfunc (v _ptrflag_Entry4) GetU32() uint32 {\n\tif *v.p == nil {\n\t\treturn 0\n\t}\n\treturn 1\n}\nfunc (v _ptrflag_Entry4) SetU32(nv uint32) {\n\tswitch nv {\n\tcase 0:\n\t\t*v.p = nil\n\tcase 1:\n\t\tif *v.p == nil {\n\t\t\t*v.p = new(Entry4)\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"*Entry4 present flag value %d should be 0 or 1\", nv)\n\t}\n}\nfunc (_ptrflag_Entry4) XdrTypeName() string { return \"Entry4?\" }\nfunc (v _ptrflag_Entry4) XdrPointer() interface{} { return nil }\nfunc (v _ptrflag_Entry4) XdrValue() interface{} { return v.GetU32() != 0 }\nfunc (v _ptrflag_Entry4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v _ptrflag_Entry4) XdrBound() uint32 { return 1 }\nfunc (v _XdrPtr_Entry4) GetPresent() bool { return *v.p != nil }\nfunc (v _XdrPtr_Entry4) SetPresent(present bool) {\n\tif !present {\n\t\t*v.p = nil\n\t} else if *v.p == nil {\n\t\t*v.p = new(Entry4)\n\t}\n}\nfunc (v _XdrPtr_Entry4) XdrMarshalValue(x XDR, name string) {\n\tif *v.p != nil {\n\t\tXDR_Entry4(*v.p).XdrMarshal(x, name)\n\t}\n}\nfunc (v _XdrPtr_Entry4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v _XdrPtr_Entry4) XdrRecurse(x XDR, name string) {\n\tx.Marshal(name, _ptrflag_Entry4(v))\n\tv.XdrMarshalValue(x, name)\n}\nfunc (_XdrPtr_Entry4) XdrTypeName() string { return \"Entry4*\" }\nfunc (v _XdrPtr_Entry4) XdrPointer() interface{} { return v.p }\nfunc (v _XdrPtr_Entry4) XdrValue() interface{} { return *v.p }\ntype XdrType_Entry4 = *Entry4\nfunc (v *Entry4) XdrPointer() interface{} { return v }\nfunc (Entry4) XdrTypeName() string { return \"Entry4\" }\nfunc (v Entry4) XdrValue() interface{} { return v }\nfunc (v *Entry4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Entry4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%scookie\", name), XDR_Nfs_cookie4(&v.Cookie))\n\tx.Marshal(x.Sprintf(\"%sname\", name), XDR_Component4(&v.Name))\n\tx.Marshal(x.Sprintf(\"%sattrs\", name), XDR_Fattr4(&v.Attrs))\n\tx.Marshal(x.Sprintf(\"%snextentry\", name), _XdrPtr_Entry4{&v.Nextentry})\n}\nfunc XDR_Entry4(v *Entry4) *Entry4 { return v }\ntype XdrType_Dirlist4 = *Dirlist4\nfunc (v *Dirlist4) XdrPointer() interface{} { return v }\nfunc (Dirlist4) XdrTypeName() string { return \"Dirlist4\" }\nfunc (v Dirlist4) XdrValue() interface{} { return v }\nfunc (v *Dirlist4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Dirlist4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sentries\", name), _XdrPtr_Entry4{&v.Entries})\n\tx.Marshal(x.Sprintf(\"%seof\", name), XDR_bool(&v.Eof))\n}\nfunc XDR_Dirlist4(v *Dirlist4) *Dirlist4 { return v }\ntype XdrType_READDIR4resok = *READDIR4resok\nfunc (v *READDIR4resok) XdrPointer() interface{} { return v }\nfunc (READDIR4resok) XdrTypeName() string { return \"READDIR4resok\" }\nfunc (v READDIR4resok) XdrValue() interface{} { return v }\nfunc (v *READDIR4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *READDIR4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%scookieverf\", name), XDR_Verifier4(&v.Cookieverf))\n\tx.Marshal(x.Sprintf(\"%sreply\", name), XDR_Dirlist4(&v.Reply))\n}\nfunc XDR_READDIR4resok(v *READDIR4resok) *READDIR4resok { return v }\nfunc (u *READDIR4res) Resok4() *READDIR4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*READDIR4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero READDIR4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"READDIR4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u READDIR4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *READDIR4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *READDIR4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *READDIR4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_READDIR4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *READDIR4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_READDIR4res = *READDIR4res\nfunc (v *READDIR4res) XdrPointer() interface{} { return v }\nfunc (READDIR4res) XdrTypeName() string { return \"READDIR4res\" }\nfunc (v READDIR4res) XdrValue() interface{} { return v }\nfunc (v *READDIR4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *READDIR4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_READDIR4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_READDIR4res(v *READDIR4res) *READDIR4res { return v}\ntype XdrType_READLINK4resok = *READLINK4resok\nfunc (v *READLINK4resok) XdrPointer() interface{} { return v }\nfunc (READLINK4resok) XdrTypeName() string { return \"READLINK4resok\" }\nfunc (v READLINK4resok) XdrValue() interface{} { return v }\nfunc (v *READLINK4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *READLINK4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slink\", name), XDR_Linktext4(&v.Link))\n}\nfunc XDR_READLINK4resok(v *READLINK4resok) *READLINK4resok { return v }\nfunc (u *READLINK4res) Resok4() *READLINK4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*READLINK4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero READLINK4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"READLINK4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u READLINK4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *READLINK4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *READLINK4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *READLINK4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_READLINK4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *READLINK4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_READLINK4res = *READLINK4res\nfunc (v *READLINK4res) XdrPointer() interface{} { return v }\nfunc (READLINK4res) XdrTypeName() string { return \"READLINK4res\" }\nfunc (v READLINK4res) XdrValue() interface{} { return v }\nfunc (v *READLINK4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *READLINK4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_READLINK4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_READLINK4res(v *READLINK4res) *READLINK4res { return v}\ntype XdrType_REMOVE4args = *REMOVE4args\nfunc (v *REMOVE4args) XdrPointer() interface{} { return v }\nfunc (REMOVE4args) XdrTypeName() string { return \"REMOVE4args\" }\nfunc (v REMOVE4args) XdrValue() interface{} { return v }\nfunc (v *REMOVE4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *REMOVE4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%starget\", name), XDR_Component4(&v.Target))\n}\nfunc XDR_REMOVE4args(v *REMOVE4args) *REMOVE4args { return v }\ntype XdrType_REMOVE4resok = *REMOVE4resok\nfunc (v *REMOVE4resok) XdrPointer() interface{} { return v }\nfunc (REMOVE4resok) XdrTypeName() string { return \"REMOVE4resok\" }\nfunc (v REMOVE4resok) XdrValue() interface{} { return v }\nfunc (v *REMOVE4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *REMOVE4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%scinfo\", name), XDR_Change_info4(&v.Cinfo))\n}\nfunc XDR_REMOVE4resok(v *REMOVE4resok) *REMOVE4resok { return v }\nfunc (u *REMOVE4res) Resok4() *REMOVE4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*REMOVE4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero REMOVE4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"REMOVE4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u REMOVE4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *REMOVE4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *REMOVE4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *REMOVE4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_REMOVE4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *REMOVE4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_REMOVE4res = *REMOVE4res\nfunc (v *REMOVE4res) XdrPointer() interface{} { return v }\nfunc (REMOVE4res) XdrTypeName() string { return \"REMOVE4res\" }\nfunc (v REMOVE4res) XdrValue() interface{} { return v }\nfunc (v *REMOVE4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *REMOVE4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_REMOVE4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_REMOVE4res(v *REMOVE4res) *REMOVE4res { return v}\ntype XdrType_RENAME4args = *RENAME4args\nfunc (v *RENAME4args) XdrPointer() interface{} { return v }\nfunc (RENAME4args) XdrTypeName() string { return \"RENAME4args\" }\nfunc (v RENAME4args) XdrValue() interface{} { return v }\nfunc (v *RENAME4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *RENAME4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%soldname\", name), XDR_Component4(&v.Oldname))\n\tx.Marshal(x.Sprintf(\"%snewname\", name), XDR_Component4(&v.Newname))\n}\nfunc XDR_RENAME4args(v *RENAME4args) *RENAME4args { return v }\ntype XdrType_RENAME4resok = *RENAME4resok\nfunc (v *RENAME4resok) XdrPointer() interface{} { return v }\nfunc (RENAME4resok) XdrTypeName() string { return \"RENAME4resok\" }\nfunc (v RENAME4resok) XdrValue() interface{} { return v }\nfunc (v *RENAME4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *RENAME4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%ssource_cinfo\", name), XDR_Change_info4(&v.Source_cinfo))\n\tx.Marshal(x.Sprintf(\"%starget_cinfo\", name), XDR_Change_info4(&v.Target_cinfo))\n}\nfunc XDR_RENAME4resok(v *RENAME4resok) *RENAME4resok { return v }\nfunc (u *RENAME4res) Resok4() *RENAME4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*RENAME4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero RENAME4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"RENAME4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u RENAME4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *RENAME4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *RENAME4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *RENAME4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_RENAME4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *RENAME4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_RENAME4res = *RENAME4res\nfunc (v *RENAME4res) XdrPointer() interface{} { return v }\nfunc (RENAME4res) XdrTypeName() string { return \"RENAME4res\" }\nfunc (v RENAME4res) XdrValue() interface{} { return v }\nfunc (v *RENAME4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *RENAME4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_RENAME4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_RENAME4res(v *RENAME4res) *RENAME4res { return v}\ntype XdrType_RENEW4args = *RENEW4args\nfunc (v *RENEW4args) XdrPointer() interface{} { return v }\nfunc (RENEW4args) XdrTypeName() string { return \"RENEW4args\" }\nfunc (v RENEW4args) XdrValue() interface{} { return v }\nfunc (v *RENEW4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *RENEW4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sclientid\", name), XDR_Clientid4(&v.Clientid))\n}\nfunc XDR_RENEW4args(v *RENEW4args) *RENEW4args { return v }\ntype XdrType_RENEW4res = *RENEW4res\nfunc (v *RENEW4res) XdrPointer() interface{} { return v }\nfunc (RENEW4res) XdrTypeName() string { return \"RENEW4res\" }\nfunc (v RENEW4res) XdrValue() interface{} { return v }\nfunc (v *RENEW4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *RENEW4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_RENEW4res(v *RENEW4res) *RENEW4res { return v }\ntype XdrType_RESTOREFH4res = *RESTOREFH4res\nfunc (v *RESTOREFH4res) XdrPointer() interface{} { return v }\nfunc (RESTOREFH4res) XdrTypeName() string { return \"RESTOREFH4res\" }\nfunc (v RESTOREFH4res) XdrValue() interface{} { return v }\nfunc (v *RESTOREFH4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *RESTOREFH4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_RESTOREFH4res(v *RESTOREFH4res) *RESTOREFH4res { return v }\ntype XdrType_SAVEFH4res = *SAVEFH4res\nfunc (v *SAVEFH4res) XdrPointer() interface{} { return v }\nfunc (SAVEFH4res) XdrTypeName() string { return \"SAVEFH4res\" }\nfunc (v SAVEFH4res) XdrValue() interface{} { return v }\nfunc (v *SAVEFH4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *SAVEFH4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_SAVEFH4res(v *SAVEFH4res) *SAVEFH4res { return v }\ntype XdrType_SECINFO4args = *SECINFO4args\nfunc (v *SECINFO4args) XdrPointer() interface{} { return v }\nfunc (SECINFO4args) XdrTypeName() string { return \"SECINFO4args\" }\nfunc (v SECINFO4args) XdrValue() interface{} { return v }\nfunc (v *SECINFO4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *SECINFO4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sname\", name), XDR_Component4(&v.Name))\n}\nfunc XDR_SECINFO4args(v *SECINFO4args) *SECINFO4args { return v }\nvar _XdrNames_Rpc_gss_svc_t = map[int32]string{\n\tint32(RPC_GSS_SVC_NONE): \"RPC_GSS_SVC_NONE\",\n\tint32(RPC_GSS_SVC_INTEGRITY): \"RPC_GSS_SVC_INTEGRITY\",\n\tint32(RPC_GSS_SVC_PRIVACY): \"RPC_GSS_SVC_PRIVACY\",\n}\nvar _XdrValues_Rpc_gss_svc_t = map[string]int32{\n\t\"RPC_GSS_SVC_NONE\": int32(RPC_GSS_SVC_NONE),\n\t\"RPC_GSS_SVC_INTEGRITY\": int32(RPC_GSS_SVC_INTEGRITY),\n\t\"RPC_GSS_SVC_PRIVACY\": int32(RPC_GSS_SVC_PRIVACY),\n}\nfunc (Rpc_gss_svc_t) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Rpc_gss_svc_t\n}\nfunc (v Rpc_gss_svc_t) String() string {\n\tif s, ok := _XdrNames_Rpc_gss_svc_t[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Rpc_gss_svc_t#%d\", v)\n}\nfunc (v *Rpc_gss_svc_t) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Rpc_gss_svc_t[stok]; ok {\n\t\t\t*v = Rpc_gss_svc_t(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Rpc_gss_svc_t\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Rpc_gss_svc_t.\", stok))\n\t}\n}\nfunc (v Rpc_gss_svc_t) GetU32() uint32 { return uint32(v) }\nfunc (v *Rpc_gss_svc_t) SetU32(n uint32) { *v = Rpc_gss_svc_t(n) }\nfunc (v *Rpc_gss_svc_t) XdrPointer() interface{} { return v }\nfunc (Rpc_gss_svc_t) XdrTypeName() string { return \"Rpc_gss_svc_t\" }\nfunc (v Rpc_gss_svc_t) XdrValue() interface{} { return v }\nfunc (v *Rpc_gss_svc_t) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Rpc_gss_svc_t = *Rpc_gss_svc_t\nfunc XDR_Rpc_gss_svc_t(v *Rpc_gss_svc_t) *Rpc_gss_svc_t { return v }\nfunc (v *Rpc_gss_svc_t) XdrInitialize() {\n\tswitch Rpc_gss_svc_t(0) {\n\tcase RPC_GSS_SVC_NONE, RPC_GSS_SVC_INTEGRITY, RPC_GSS_SVC_PRIVACY:\n\tdefault:\n\t\tif *v == Rpc_gss_svc_t(0) { *v = RPC_GSS_SVC_NONE }\n\t}\n}\ntype XdrType_Rpcsec_gss_info = *Rpcsec_gss_info\nfunc (v *Rpcsec_gss_info) XdrPointer() interface{} { return v }\nfunc (Rpcsec_gss_info) XdrTypeName() string { return \"Rpcsec_gss_info\" }\nfunc (v Rpcsec_gss_info) XdrValue() interface{} { return v }\nfunc (v *Rpcsec_gss_info) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Rpcsec_gss_info) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%soid\", name), XDR_Sec_oid4(&v.Oid))\n\tx.Marshal(x.Sprintf(\"%sqop\", name), XDR_Qop4(&v.Qop))\n\tx.Marshal(x.Sprintf(\"%sservice\", name), XDR_Rpc_gss_svc_t(&v.Service))\n}\nfunc XDR_Rpcsec_gss_info(v *Rpcsec_gss_info) *Rpcsec_gss_info { return v }\nfunc (u *Secinfo4) Flavor_info() *Rpcsec_gss_info {\n\tswitch u.Flavor {\n\tcase RPCSEC_GSS:\n\t\tif v, ok := u.U.(*Rpcsec_gss_info); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Rpcsec_gss_info\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Secinfo4.Flavor_info accessed when Flavor == %v\", u.Flavor)\n\t\treturn nil\n\t}\n}\nfunc (u Secinfo4) XdrValid() bool {\n\treturn true\n}\nfunc (u *Secinfo4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Uint32_t(&u.Flavor)\n}\nfunc (u *Secinfo4) XdrUnionTagName() string {\n\treturn \"Flavor\"\n}\nfunc (u *Secinfo4) XdrUnionBody() XdrType {\n\tswitch u.Flavor {\n\tcase RPCSEC_GSS:\n\t\treturn XDR_Rpcsec_gss_info(u.Flavor_info())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *Secinfo4) XdrUnionBodyName() string {\n\tswitch u.Flavor {\n\tcase RPCSEC_GSS:\n\t\treturn \"Flavor_info\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_Secinfo4 = *Secinfo4\nfunc (v *Secinfo4) XdrPointer() interface{} { return v }\nfunc (Secinfo4) XdrTypeName() string { return \"Secinfo4\" }\nfunc (v Secinfo4) XdrValue() interface{} { return v }\nfunc (v *Secinfo4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Secinfo4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Uint32_t(&u.Flavor).XdrMarshal(x, x.Sprintf(\"%sflavor\", name))\n\tswitch u.Flavor {\n\tcase RPCSEC_GSS:\n\t\tx.Marshal(x.Sprintf(\"%sflavor_info\", name), XDR_Rpcsec_gss_info(u.Flavor_info()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_Secinfo4(v *Secinfo4) *Secinfo4 { return v}\ntype _XdrVec_unbounded_Secinfo4 []Secinfo4\nfunc (_XdrVec_unbounded_Secinfo4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Secinfo4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Secinfo4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Secinfo4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Secinfo4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Secinfo4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Secinfo4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Secinfo4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Secinfo4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Secinfo4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Secinfo4) XdrTypeName() string { return \"Secinfo4<>\" }\nfunc (v *_XdrVec_unbounded_Secinfo4) XdrPointer() interface{} { return (*[]Secinfo4)(v) }\nfunc (v _XdrVec_unbounded_Secinfo4) XdrValue() interface{} { return ([]Secinfo4)(v) }\nfunc (v *_XdrVec_unbounded_Secinfo4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_SECINFO4resok struct {\n\t*_XdrVec_unbounded_Secinfo4\n}\nfunc XDR_SECINFO4resok(v *SECINFO4resok) XdrType_SECINFO4resok {\n\treturn XdrType_SECINFO4resok{(*_XdrVec_unbounded_Secinfo4)(v)}\n}\nfunc (XdrType_SECINFO4resok) XdrTypeName() string { return \"SECINFO4resok\" }\nfunc (v XdrType_SECINFO4resok) XdrUnwrap() XdrType { return v._XdrVec_unbounded_Secinfo4 }\nfunc (u *SECINFO4res) Resok4() *SECINFO4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*SECINFO4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SECINFO4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"SECINFO4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u SECINFO4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *SECINFO4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *SECINFO4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *SECINFO4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_SECINFO4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *SECINFO4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_SECINFO4res = *SECINFO4res\nfunc (v *SECINFO4res) XdrPointer() interface{} { return v }\nfunc (SECINFO4res) XdrTypeName() string { return \"SECINFO4res\" }\nfunc (v SECINFO4res) XdrValue() interface{} { return v }\nfunc (v *SECINFO4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *SECINFO4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_SECINFO4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_SECINFO4res(v *SECINFO4res) *SECINFO4res { return v}\ntype XdrType_SETATTR4args = *SETATTR4args\nfunc (v *SETATTR4args) XdrPointer() interface{} { return v }\nfunc (SETATTR4args) XdrTypeName() string { return \"SETATTR4args\" }\nfunc (v SETATTR4args) XdrValue() interface{} { return v }\nfunc (v *SETATTR4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *SETATTR4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstateid\", name), XDR_Stateid4(&v.Stateid))\n\tx.Marshal(x.Sprintf(\"%sobj_attributes\", name), XDR_Fattr4(&v.Obj_attributes))\n}\nfunc XDR_SETATTR4args(v *SETATTR4args) *SETATTR4args { return v }\ntype XdrType_SETATTR4res = *SETATTR4res\nfunc (v *SETATTR4res) XdrPointer() interface{} { return v }\nfunc (SETATTR4res) XdrTypeName() string { return \"SETATTR4res\" }\nfunc (v SETATTR4res) XdrValue() interface{} { return v }\nfunc (v *SETATTR4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *SETATTR4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n\tx.Marshal(x.Sprintf(\"%sattrsset\", name), XDR_Bitmap4(&v.Attrsset))\n}\nfunc XDR_SETATTR4res(v *SETATTR4res) *SETATTR4res { return v }\ntype XdrType_SETCLIENTID4args = *SETCLIENTID4args\nfunc (v *SETCLIENTID4args) XdrPointer() interface{} { return v }\nfunc (SETCLIENTID4args) XdrTypeName() string { return \"SETCLIENTID4args\" }\nfunc (v SETCLIENTID4args) XdrValue() interface{} { return v }\nfunc (v *SETCLIENTID4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *SETCLIENTID4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sclient\", name), XDR_Nfs_client_id4(&v.Client))\n\tx.Marshal(x.Sprintf(\"%scallback\", name), XDR_Cb_client4(&v.Callback))\n\tx.Marshal(x.Sprintf(\"%scallback_ident\", name), XDR_Uint32_t(&v.Callback_ident))\n}\nfunc XDR_SETCLIENTID4args(v *SETCLIENTID4args) *SETCLIENTID4args { return v }\ntype XdrType_SETCLIENTID4resok = *SETCLIENTID4resok\nfunc (v *SETCLIENTID4resok) XdrPointer() interface{} { return v }\nfunc (SETCLIENTID4resok) XdrTypeName() string { return \"SETCLIENTID4resok\" }\nfunc (v SETCLIENTID4resok) XdrValue() interface{} { return v }\nfunc (v *SETCLIENTID4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *SETCLIENTID4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sclientid\", name), XDR_Clientid4(&v.Clientid))\n\tx.Marshal(x.Sprintf(\"%ssetclientid_confirm\", name), XDR_Verifier4(&v.Setclientid_confirm))\n}\nfunc XDR_SETCLIENTID4resok(v *SETCLIENTID4resok) *SETCLIENTID4resok { return v }\nfunc (u *SETCLIENTID4res) Resok4() *SETCLIENTID4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*SETCLIENTID4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SETCLIENTID4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"SETCLIENTID4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u *SETCLIENTID4res) Client_using() *Clientaddr4 {\n\tswitch u.Status {\n\tcase NFS4ERR_CLID_INUSE:\n\t\tif v, ok := u.U.(*Clientaddr4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Clientaddr4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"SETCLIENTID4res.Client_using accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u SETCLIENTID4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *SETCLIENTID4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *SETCLIENTID4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *SETCLIENTID4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_SETCLIENTID4resok(u.Resok4())\n\tcase NFS4ERR_CLID_INUSE:\n\t\treturn XDR_Clientaddr4(u.Client_using())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *SETCLIENTID4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tcase NFS4ERR_CLID_INUSE:\n\t\treturn \"Client_using\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_SETCLIENTID4res = *SETCLIENTID4res\nfunc (v *SETCLIENTID4res) XdrPointer() interface{} { return v }\nfunc (SETCLIENTID4res) XdrTypeName() string { return \"SETCLIENTID4res\" }\nfunc (v SETCLIENTID4res) XdrValue() interface{} { return v }\nfunc (v *SETCLIENTID4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *SETCLIENTID4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_SETCLIENTID4resok(u.Resok4()))\n\t\treturn\n\tcase NFS4ERR_CLID_INUSE:\n\t\tx.Marshal(x.Sprintf(\"%sclient_using\", name), XDR_Clientaddr4(u.Client_using()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_SETCLIENTID4res(v *SETCLIENTID4res) *SETCLIENTID4res { return v}\ntype XdrType_SETCLIENTID_CONFIRM4args = *SETCLIENTID_CONFIRM4args\nfunc (v *SETCLIENTID_CONFIRM4args) XdrPointer() interface{} { return v }\nfunc (SETCLIENTID_CONFIRM4args) XdrTypeName() string { return \"SETCLIENTID_CONFIRM4args\" }\nfunc (v SETCLIENTID_CONFIRM4args) XdrValue() interface{} { return v }\nfunc (v *SETCLIENTID_CONFIRM4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *SETCLIENTID_CONFIRM4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sclientid\", name), XDR_Clientid4(&v.Clientid))\n\tx.Marshal(x.Sprintf(\"%ssetclientid_confirm\", name), XDR_Verifier4(&v.Setclientid_confirm))\n}\nfunc XDR_SETCLIENTID_CONFIRM4args(v *SETCLIENTID_CONFIRM4args) *SETCLIENTID_CONFIRM4args { return v }\ntype XdrType_SETCLIENTID_CONFIRM4res = *SETCLIENTID_CONFIRM4res\nfunc (v *SETCLIENTID_CONFIRM4res) XdrPointer() interface{} { return v }\nfunc (SETCLIENTID_CONFIRM4res) XdrTypeName() string { return \"SETCLIENTID_CONFIRM4res\" }\nfunc (v SETCLIENTID_CONFIRM4res) XdrValue() interface{} { return v }\nfunc (v *SETCLIENTID_CONFIRM4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *SETCLIENTID_CONFIRM4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_SETCLIENTID_CONFIRM4res(v *SETCLIENTID_CONFIRM4res) *SETCLIENTID_CONFIRM4res { return v }\ntype XdrType_VERIFY4args = *VERIFY4args\nfunc (v *VERIFY4args) XdrPointer() interface{} { return v }\nfunc (VERIFY4args) XdrTypeName() string { return \"VERIFY4args\" }\nfunc (v VERIFY4args) XdrValue() interface{} { return v }\nfunc (v *VERIFY4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *VERIFY4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sobj_attributes\", name), XDR_Fattr4(&v.Obj_attributes))\n}\nfunc XDR_VERIFY4args(v *VERIFY4args) *VERIFY4args { return v }\ntype XdrType_VERIFY4res = *VERIFY4res\nfunc (v *VERIFY4res) XdrPointer() interface{} { return v }\nfunc (VERIFY4res) XdrTypeName() string { return \"VERIFY4res\" }\nfunc (v VERIFY4res) XdrValue() interface{} { return v }\nfunc (v *VERIFY4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *VERIFY4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_VERIFY4res(v *VERIFY4res) *VERIFY4res { return v }\nvar _XdrNames_Stable_how4 = map[int32]string{\n\tint32(UNSTABLE4): \"UNSTABLE4\",\n\tint32(DATA_SYNC4): \"DATA_SYNC4\",\n\tint32(FILE_SYNC4): \"FILE_SYNC4\",\n}\nvar _XdrValues_Stable_how4 = map[string]int32{\n\t\"UNSTABLE4\": int32(UNSTABLE4),\n\t\"DATA_SYNC4\": int32(DATA_SYNC4),\n\t\"FILE_SYNC4\": int32(FILE_SYNC4),\n}\nfunc (Stable_how4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Stable_how4\n}\nfunc (v Stable_how4) String() string {\n\tif s, ok := _XdrNames_Stable_how4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Stable_how4#%d\", v)\n}\nfunc (v *Stable_how4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Stable_how4[stok]; ok {\n\t\t\t*v = Stable_how4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Stable_how4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Stable_how4.\", stok))\n\t}\n}\nfunc (v Stable_how4) GetU32() uint32 { return uint32(v) }\nfunc (v *Stable_how4) SetU32(n uint32) { *v = Stable_how4(n) }\nfunc (v *Stable_how4) XdrPointer() interface{} { return v }\nfunc (Stable_how4) XdrTypeName() string { return \"Stable_how4\" }\nfunc (v Stable_how4) XdrValue() interface{} { return v }\nfunc (v *Stable_how4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Stable_how4 = *Stable_how4\nfunc XDR_Stable_how4(v *Stable_how4) *Stable_how4 { return v }\ntype XdrType_WRITE4args = *WRITE4args\nfunc (v *WRITE4args) XdrPointer() interface{} { return v }\nfunc (WRITE4args) XdrTypeName() string { return \"WRITE4args\" }\nfunc (v WRITE4args) XdrValue() interface{} { return v }\nfunc (v *WRITE4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *WRITE4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstateid\", name), XDR_Stateid4(&v.Stateid))\n\tx.Marshal(x.Sprintf(\"%soffset\", name), XDR_Offset4(&v.Offset))\n\tx.Marshal(x.Sprintf(\"%sstable\", name), XDR_Stable_how4(&v.Stable))\n\tx.Marshal(x.Sprintf(\"%sdata\", name), XdrVecOpaque{&v.Data, 0xffffffff})\n}\nfunc XDR_WRITE4args(v *WRITE4args) *WRITE4args { return v }\ntype XdrType_WRITE4resok = *WRITE4resok\nfunc (v *WRITE4resok) XdrPointer() interface{} { return v }\nfunc (WRITE4resok) XdrTypeName() string { return \"WRITE4resok\" }\nfunc (v WRITE4resok) XdrValue() interface{} { return v }\nfunc (v *WRITE4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *WRITE4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%scount\", name), XDR_Count4(&v.Count))\n\tx.Marshal(x.Sprintf(\"%scommitted\", name), XDR_Stable_how4(&v.Committed))\n\tx.Marshal(x.Sprintf(\"%swriteverf\", name), XDR_Verifier4(&v.Writeverf))\n}\nfunc XDR_WRITE4resok(v *WRITE4resok) *WRITE4resok { return v }\nfunc (u *WRITE4res) Resok4() *WRITE4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*WRITE4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero WRITE4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"WRITE4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u WRITE4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *WRITE4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *WRITE4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *WRITE4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_WRITE4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *WRITE4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_WRITE4res = *WRITE4res\nfunc (v *WRITE4res) XdrPointer() interface{} { return v }\nfunc (WRITE4res) XdrTypeName() string { return \"WRITE4res\" }\nfunc (v WRITE4res) XdrValue() interface{} { return v }\nfunc (v *WRITE4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *WRITE4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_WRITE4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_WRITE4res(v *WRITE4res) *WRITE4res { return v}\ntype XdrType_RELEASE_LOCKOWNER4args = *RELEASE_LOCKOWNER4args\nfunc (v *RELEASE_LOCKOWNER4args) XdrPointer() interface{} { return v }\nfunc (RELEASE_LOCKOWNER4args) XdrTypeName() string { return \"RELEASE_LOCKOWNER4args\" }\nfunc (v RELEASE_LOCKOWNER4args) XdrValue() interface{} { return v }\nfunc (v *RELEASE_LOCKOWNER4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *RELEASE_LOCKOWNER4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slock_owner\", name), XDR_Lock_owner4(&v.Lock_owner))\n}\nfunc XDR_RELEASE_LOCKOWNER4args(v *RELEASE_LOCKOWNER4args) *RELEASE_LOCKOWNER4args { return v }\ntype XdrType_RELEASE_LOCKOWNER4res = *RELEASE_LOCKOWNER4res\nfunc (v *RELEASE_LOCKOWNER4res) XdrPointer() interface{} { return v }\nfunc (RELEASE_LOCKOWNER4res) XdrTypeName() string { return \"RELEASE_LOCKOWNER4res\" }\nfunc (v RELEASE_LOCKOWNER4res) XdrValue() interface{} { return v }\nfunc (v *RELEASE_LOCKOWNER4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *RELEASE_LOCKOWNER4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_RELEASE_LOCKOWNER4res(v *RELEASE_LOCKOWNER4res) *RELEASE_LOCKOWNER4res { return v }\nvar _XdrTags_Callback_sec_parms4 = map[int32]bool{\n\tXdrToI32(AUTH_NONE): true,\n\tXdrToI32(AUTH_SYS): true,\n}\nfunc (_ Callback_sec_parms4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Callback_sec_parms4\n}\n/* RFC 1831 */\nfunc (u *Callback_sec_parms4) Cbsp_sys_cred() *Authsys_parms {\n\tswitch Auth_flavor(u.Cb_secflavor) {\n\tcase AUTH_SYS:\n\t\tif v, ok := u.U.(*Authsys_parms); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Authsys_parms\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Callback_sec_parms4.Cbsp_sys_cred accessed when Cb_secflavor == %v\", u.Cb_secflavor)\n\t\treturn nil\n\t}\n}\nfunc (u Callback_sec_parms4) XdrValid() bool {\n\tswitch Auth_flavor(u.Cb_secflavor) {\n\tcase AUTH_NONE,AUTH_SYS:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Callback_sec_parms4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Uint32_t(&u.Cb_secflavor)\n}\nfunc (u *Callback_sec_parms4) XdrUnionTagName() string {\n\treturn \"Cb_secflavor\"\n}\nfunc (u *Callback_sec_parms4) XdrUnionBody() XdrType {\n\tswitch Auth_flavor(u.Cb_secflavor) {\n\tcase AUTH_NONE:\n\t\treturn nil\n\tcase AUTH_SYS:\n\t\treturn XDR_Authsys_parms(u.Cbsp_sys_cred())\n\t}\n\treturn nil\n}\nfunc (u *Callback_sec_parms4) XdrUnionBodyName() string {\n\tswitch Auth_flavor(u.Cb_secflavor) {\n\tcase AUTH_NONE:\n\t\treturn \"\"\n\tcase AUTH_SYS:\n\t\treturn \"Cbsp_sys_cred\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Callback_sec_parms4 = *Callback_sec_parms4\nfunc (v *Callback_sec_parms4) XdrPointer() interface{} { return v }\nfunc (Callback_sec_parms4) XdrTypeName() string { return \"Callback_sec_parms4\" }\nfunc (v Callback_sec_parms4) XdrValue() interface{} { return v }\nfunc (v *Callback_sec_parms4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Callback_sec_parms4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Uint32_t(&u.Cb_secflavor).XdrMarshal(x, x.Sprintf(\"%scb_secflavor\", name))\n\tswitch Auth_flavor(u.Cb_secflavor) {\n\tcase AUTH_NONE:\n\t\treturn\n\tcase AUTH_SYS:\n\t\tx.Marshal(x.Sprintf(\"%scbsp_sys_cred\", name), XDR_Authsys_parms(u.Cbsp_sys_cred()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Cb_secflavor (%v) in Callback_sec_parms4\", u.Cb_secflavor)\n}\nfunc (v *Callback_sec_parms4) XdrInitialize() {\n\tvar zero Uint32_t\n\tswitch Auth_flavor(zero) {\n\tcase AUTH_NONE, AUTH_SYS:\n\tdefault:\n\t\tif v.Cb_secflavor == zero { v.Cb_secflavor = uint32(AUTH_NONE) }\n\t}\n}\nfunc XDR_Callback_sec_parms4(v *Callback_sec_parms4) *Callback_sec_parms4 { return v}\ntype _XdrVec_1_Uint32_t []Uint32_t\nfunc (_XdrVec_1_Uint32_t) XdrBound() uint32 {\n\tconst bound uint32 = 1 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_1_Uint32_t) XdrCheckLen(length uint32) {\n\tif length > uint32(1) {\n\t\tXdrPanic(\"_XdrVec_1_Uint32_t length %d exceeds bound 1\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_1_Uint32_t length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_1_Uint32_t) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_1_Uint32_t) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(1); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Uint32_t, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_1_Uint32_t) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Uint32_t(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_1_Uint32_t) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 1 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_1_Uint32_t) XdrTypeName() string { return \"Uint32_t<>\" }\nfunc (v *_XdrVec_1_Uint32_t) XdrPointer() interface{} { return (*[]Uint32_t)(v) }\nfunc (v _XdrVec_1_Uint32_t) XdrValue() interface{} { return ([]Uint32_t)(v) }\nfunc (v *_XdrVec_1_Uint32_t) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Channel_attrs4 = *Channel_attrs4\nfunc (v *Channel_attrs4) XdrPointer() interface{} { return v }\nfunc (Channel_attrs4) XdrTypeName() string { return \"Channel_attrs4\" }\nfunc (v Channel_attrs4) XdrValue() interface{} { return v }\nfunc (v *Channel_attrs4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Channel_attrs4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sca_headerpadsize\", name), XDR_Count4(&v.Ca_headerpadsize))\n\tx.Marshal(x.Sprintf(\"%sca_maxrequestsize\", name), XDR_Count4(&v.Ca_maxrequestsize))\n\tx.Marshal(x.Sprintf(\"%sca_maxresponsesize\", name), XDR_Count4(&v.Ca_maxresponsesize))\n\tx.Marshal(x.Sprintf(\"%sca_maxresponsesize_cached\", name), XDR_Count4(&v.Ca_maxresponsesize_cached))\n\tx.Marshal(x.Sprintf(\"%sca_maxoperations\", name), XDR_Count4(&v.Ca_maxoperations))\n\tx.Marshal(x.Sprintf(\"%sca_maxrequests\", name), XDR_Count4(&v.Ca_maxrequests))\n\tx.Marshal(x.Sprintf(\"%sca_rdma_ird\", name), (*_XdrVec_1_Uint32_t)(&v.Ca_rdma_ird))\n}\nfunc XDR_Channel_attrs4(v *Channel_attrs4) *Channel_attrs4 { return v }\ntype _XdrVec_unbounded_Callback_sec_parms4 []Callback_sec_parms4\nfunc (_XdrVec_unbounded_Callback_sec_parms4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Callback_sec_parms4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Callback_sec_parms4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Callback_sec_parms4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Callback_sec_parms4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Callback_sec_parms4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Callback_sec_parms4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Callback_sec_parms4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Callback_sec_parms4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Callback_sec_parms4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Callback_sec_parms4) XdrTypeName() string { return \"Callback_sec_parms4<>\" }\nfunc (v *_XdrVec_unbounded_Callback_sec_parms4) XdrPointer() interface{} { return (*[]Callback_sec_parms4)(v) }\nfunc (v _XdrVec_unbounded_Callback_sec_parms4) XdrValue() interface{} { return ([]Callback_sec_parms4)(v) }\nfunc (v *_XdrVec_unbounded_Callback_sec_parms4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_CREATE_SESSION4args = *CREATE_SESSION4args\nfunc (v *CREATE_SESSION4args) XdrPointer() interface{} { return v }\nfunc (CREATE_SESSION4args) XdrTypeName() string { return \"CREATE_SESSION4args\" }\nfunc (v CREATE_SESSION4args) XdrValue() interface{} { return v }\nfunc (v *CREATE_SESSION4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *CREATE_SESSION4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%scsa_clientid\", name), XDR_Clientid4(&v.Csa_clientid))\n\tx.Marshal(x.Sprintf(\"%scsa_sequence\", name), XDR_Sequenceid4(&v.Csa_sequence))\n\tx.Marshal(x.Sprintf(\"%scsa_flags\", name), XDR_Uint32_t(&v.Csa_flags))\n\tx.Marshal(x.Sprintf(\"%scsa_fore_chan_attrs\", name), XDR_Channel_attrs4(&v.Csa_fore_chan_attrs))\n\tx.Marshal(x.Sprintf(\"%scsa_back_chan_attrs\", name), XDR_Channel_attrs4(&v.Csa_back_chan_attrs))\n\tx.Marshal(x.Sprintf(\"%scsa_cb_program\", name), XDR_Uint32_t(&v.Csa_cb_program))\n\tx.Marshal(x.Sprintf(\"%scsa_sec_parms\", name), (*_XdrVec_unbounded_Callback_sec_parms4)(&v.Csa_sec_parms))\n}\nfunc XDR_CREATE_SESSION4args(v *CREATE_SESSION4args) *CREATE_SESSION4args { return v }\ntype XdrType_CREATE_SESSION4resok = *CREATE_SESSION4resok\nfunc (v *CREATE_SESSION4resok) XdrPointer() interface{} { return v }\nfunc (CREATE_SESSION4resok) XdrTypeName() string { return \"CREATE_SESSION4resok\" }\nfunc (v CREATE_SESSION4resok) XdrValue() interface{} { return v }\nfunc (v *CREATE_SESSION4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *CREATE_SESSION4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%scsr_sessionid\", name), XDR_Sessionid4(&v.Csr_sessionid))\n\tx.Marshal(x.Sprintf(\"%scsr_sequence\", name), XDR_Sequenceid4(&v.Csr_sequence))\n\tx.Marshal(x.Sprintf(\"%scsr_flags\", name), XDR_Uint32_t(&v.Csr_flags))\n\tx.Marshal(x.Sprintf(\"%scsr_fore_chan_attrs\", name), XDR_Channel_attrs4(&v.Csr_fore_chan_attrs))\n\tx.Marshal(x.Sprintf(\"%scsr_back_chan_attrs\", name), XDR_Channel_attrs4(&v.Csr_back_chan_attrs))\n}\nfunc XDR_CREATE_SESSION4resok(v *CREATE_SESSION4resok) *CREATE_SESSION4resok { return v }\nfunc (u *CREATE_SESSION4res) Csr_resok4() *CREATE_SESSION4resok {\n\tswitch u.Csr_status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*CREATE_SESSION4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CREATE_SESSION4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"CREATE_SESSION4res.Csr_resok4 accessed when Csr_status == %v\", u.Csr_status)\n\t\treturn nil\n\t}\n}\nfunc (u CREATE_SESSION4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *CREATE_SESSION4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Csr_status)\n}\nfunc (u *CREATE_SESSION4res) XdrUnionTagName() string {\n\treturn \"Csr_status\"\n}\nfunc (u *CREATE_SESSION4res) XdrUnionBody() XdrType {\n\tswitch u.Csr_status {\n\tcase NFS4_OK:\n\t\treturn XDR_CREATE_SESSION4resok(u.Csr_resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *CREATE_SESSION4res) XdrUnionBodyName() string {\n\tswitch u.Csr_status {\n\tcase NFS4_OK:\n\t\treturn \"Csr_resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_CREATE_SESSION4res = *CREATE_SESSION4res\nfunc (v *CREATE_SESSION4res) XdrPointer() interface{} { return v }\nfunc (CREATE_SESSION4res) XdrTypeName() string { return \"CREATE_SESSION4res\" }\nfunc (v CREATE_SESSION4res) XdrValue() interface{} { return v }\nfunc (v *CREATE_SESSION4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *CREATE_SESSION4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Csr_status).XdrMarshal(x, x.Sprintf(\"%scsr_status\", name))\n\tswitch u.Csr_status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%scsr_resok4\", name), XDR_CREATE_SESSION4resok(u.Csr_resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_CREATE_SESSION4res(v *CREATE_SESSION4res) *CREATE_SESSION4res { return v}\ntype XdrType_DESTROY_SESSION4args = *DESTROY_SESSION4args\nfunc (v *DESTROY_SESSION4args) XdrPointer() interface{} { return v }\nfunc (DESTROY_SESSION4args) XdrTypeName() string { return \"DESTROY_SESSION4args\" }\nfunc (v DESTROY_SESSION4args) XdrValue() interface{} { return v }\nfunc (v *DESTROY_SESSION4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *DESTROY_SESSION4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sdsa_sessionid\", name), XDR_Sessionid4(&v.Dsa_sessionid))\n}\nfunc XDR_DESTROY_SESSION4args(v *DESTROY_SESSION4args) *DESTROY_SESSION4args { return v }\ntype XdrType_DESTROY_SESSION4res = *DESTROY_SESSION4res\nfunc (v *DESTROY_SESSION4res) XdrPointer() interface{} { return v }\nfunc (DESTROY_SESSION4res) XdrTypeName() string { return \"DESTROY_SESSION4res\" }\nfunc (v DESTROY_SESSION4res) XdrValue() interface{} { return v }\nfunc (v *DESTROY_SESSION4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *DESTROY_SESSION4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sdsr_status\", name), XDR_Nfsstat4(&v.Dsr_status))\n}\nfunc XDR_DESTROY_SESSION4res(v *DESTROY_SESSION4res) *DESTROY_SESSION4res { return v }\ntype XdrType_FREE_STATEID4args = *FREE_STATEID4args\nfunc (v *FREE_STATEID4args) XdrPointer() interface{} { return v }\nfunc (FREE_STATEID4args) XdrTypeName() string { return \"FREE_STATEID4args\" }\nfunc (v FREE_STATEID4args) XdrValue() interface{} { return v }\nfunc (v *FREE_STATEID4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *FREE_STATEID4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sfsa_stateid\", name), XDR_Stateid4(&v.Fsa_stateid))\n}\nfunc XDR_FREE_STATEID4args(v *FREE_STATEID4args) *FREE_STATEID4args { return v }\ntype XdrType_FREE_STATEID4res = *FREE_STATEID4res\nfunc (v *FREE_STATEID4res) XdrPointer() interface{} { return v }\nfunc (FREE_STATEID4res) XdrTypeName() string { return \"FREE_STATEID4res\" }\nfunc (v FREE_STATEID4res) XdrValue() interface{} { return v }\nfunc (v *FREE_STATEID4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *FREE_STATEID4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sfsr_status\", name), XDR_Nfsstat4(&v.Fsr_status))\n}\nfunc XDR_FREE_STATEID4res(v *FREE_STATEID4res) *FREE_STATEID4res { return v }\ntype XdrType_Attr_notice4 struct {\n\tXdrType_Nfstime4\n}\nfunc XDR_Attr_notice4(v *Attr_notice4) *XdrType_Attr_notice4 {\n\treturn &XdrType_Attr_notice4{XDR_Nfstime4(v)}\n}\nfunc (XdrType_Attr_notice4) XdrTypeName() string { return \"Attr_notice4\" }\nfunc (v XdrType_Attr_notice4) XdrUnwrap() XdrType { return v.XdrType_Nfstime4 }\ntype XdrType_GET_DIR_DELEGATION4args = *GET_DIR_DELEGATION4args\nfunc (v *GET_DIR_DELEGATION4args) XdrPointer() interface{} { return v }\nfunc (GET_DIR_DELEGATION4args) XdrTypeName() string { return \"GET_DIR_DELEGATION4args\" }\nfunc (v GET_DIR_DELEGATION4args) XdrValue() interface{} { return v }\nfunc (v *GET_DIR_DELEGATION4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *GET_DIR_DELEGATION4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sgdda_signal_deleg_avail\", name), XDR_bool(&v.Gdda_signal_deleg_avail))\n\tx.Marshal(x.Sprintf(\"%sgdda_notification_types\", name), XDR_Bitmap4(&v.Gdda_notification_types))\n\tx.Marshal(x.Sprintf(\"%sgdda_child_attr_delay\", name), XDR_Attr_notice4(&v.Gdda_child_attr_delay))\n\tx.Marshal(x.Sprintf(\"%sgdda_dir_attr_delay\", name), XDR_Attr_notice4(&v.Gdda_dir_attr_delay))\n\tx.Marshal(x.Sprintf(\"%sgdda_child_attributes\", name), XDR_Bitmap4(&v.Gdda_child_attributes))\n\tx.Marshal(x.Sprintf(\"%sgdda_dir_attributes\", name), XDR_Bitmap4(&v.Gdda_dir_attributes))\n}\nfunc XDR_GET_DIR_DELEGATION4args(v *GET_DIR_DELEGATION4args) *GET_DIR_DELEGATION4args { return v }\ntype XdrType_GET_DIR_DELEGATION4resok = *GET_DIR_DELEGATION4resok\nfunc (v *GET_DIR_DELEGATION4resok) XdrPointer() interface{} { return v }\nfunc (GET_DIR_DELEGATION4resok) XdrTypeName() string { return \"GET_DIR_DELEGATION4resok\" }\nfunc (v GET_DIR_DELEGATION4resok) XdrValue() interface{} { return v }\nfunc (v *GET_DIR_DELEGATION4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *GET_DIR_DELEGATION4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sgddr_cookieverf\", name), XDR_Verifier4(&v.Gddr_cookieverf))\n\tx.Marshal(x.Sprintf(\"%sgddr_stateid\", name), XDR_Stateid4(&v.Gddr_stateid))\n\tx.Marshal(x.Sprintf(\"%sgddr_notification\", name), XDR_Bitmap4(&v.Gddr_notification))\n\tx.Marshal(x.Sprintf(\"%sgddr_child_attributes\", name), XDR_Bitmap4(&v.Gddr_child_attributes))\n\tx.Marshal(x.Sprintf(\"%sgddr_dir_attributes\", name), XDR_Bitmap4(&v.Gddr_dir_attributes))\n}\nfunc XDR_GET_DIR_DELEGATION4resok(v *GET_DIR_DELEGATION4resok) *GET_DIR_DELEGATION4resok { return v }\nvar _XdrNames_Gddrnf4_status = map[int32]string{\n\tint32(GDD4_OK): \"GDD4_OK\",\n\tint32(GDD4_UNAVAIL): \"GDD4_UNAVAIL\",\n}\nvar _XdrValues_Gddrnf4_status = map[string]int32{\n\t\"GDD4_OK\": int32(GDD4_OK),\n\t\"GDD4_UNAVAIL\": int32(GDD4_UNAVAIL),\n}\nfunc (Gddrnf4_status) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Gddrnf4_status\n}\nfunc (v Gddrnf4_status) String() string {\n\tif s, ok := _XdrNames_Gddrnf4_status[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Gddrnf4_status#%d\", v)\n}\nfunc (v *Gddrnf4_status) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Gddrnf4_status[stok]; ok {\n\t\t\t*v = Gddrnf4_status(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Gddrnf4_status\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Gddrnf4_status.\", stok))\n\t}\n}\nfunc (v Gddrnf4_status) GetU32() uint32 { return uint32(v) }\nfunc (v *Gddrnf4_status) SetU32(n uint32) { *v = Gddrnf4_status(n) }\nfunc (v *Gddrnf4_status) XdrPointer() interface{} { return v }\nfunc (Gddrnf4_status) XdrTypeName() string { return \"Gddrnf4_status\" }\nfunc (v Gddrnf4_status) XdrValue() interface{} { return v }\nfunc (v *Gddrnf4_status) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Gddrnf4_status = *Gddrnf4_status\nfunc XDR_Gddrnf4_status(v *Gddrnf4_status) *Gddrnf4_status { return v }\nvar _XdrTags_GET_DIR_DELEGATION4res_non_fatal = map[int32]bool{\n\tXdrToI32(GDD4_OK): true,\n\tXdrToI32(GDD4_UNAVAIL): true,\n}\nfunc (_ GET_DIR_DELEGATION4res_non_fatal) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_GET_DIR_DELEGATION4res_non_fatal\n}\nfunc (u *GET_DIR_DELEGATION4res_non_fatal) Gddrnf_resok4() *GET_DIR_DELEGATION4resok {\n\tswitch u.Gddrnf_status {\n\tcase GDD4_OK:\n\t\tif v, ok := u.U.(*GET_DIR_DELEGATION4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GET_DIR_DELEGATION4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"GET_DIR_DELEGATION4res_non_fatal.Gddrnf_resok4 accessed when Gddrnf_status == %v\", u.Gddrnf_status)\n\t\treturn nil\n\t}\n}\nfunc (u *GET_DIR_DELEGATION4res_non_fatal) Gddrnf_will_signal_deleg_avail() *bool {\n\tswitch u.Gddrnf_status {\n\tcase GDD4_UNAVAIL:\n\t\tif v, ok := u.U.(*bool); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero bool\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"GET_DIR_DELEGATION4res_non_fatal.Gddrnf_will_signal_deleg_avail accessed when Gddrnf_status == %v\", u.Gddrnf_status)\n\t\treturn nil\n\t}\n}\nfunc (u GET_DIR_DELEGATION4res_non_fatal) XdrValid() bool {\n\tswitch u.Gddrnf_status {\n\tcase GDD4_OK,GDD4_UNAVAIL:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *GET_DIR_DELEGATION4res_non_fatal) XdrUnionTag() XdrNum32 {\n\treturn XDR_Gddrnf4_status(&u.Gddrnf_status)\n}\nfunc (u *GET_DIR_DELEGATION4res_non_fatal) XdrUnionTagName() string {\n\treturn \"Gddrnf_status\"\n}\nfunc (u *GET_DIR_DELEGATION4res_non_fatal) XdrUnionBody() XdrType {\n\tswitch u.Gddrnf_status {\n\tcase GDD4_OK:\n\t\treturn XDR_GET_DIR_DELEGATION4resok(u.Gddrnf_resok4())\n\tcase GDD4_UNAVAIL:\n\t\treturn XDR_bool(u.Gddrnf_will_signal_deleg_avail())\n\t}\n\treturn nil\n}\nfunc (u *GET_DIR_DELEGATION4res_non_fatal) XdrUnionBodyName() string {\n\tswitch u.Gddrnf_status {\n\tcase GDD4_OK:\n\t\treturn \"Gddrnf_resok4\"\n\tcase GDD4_UNAVAIL:\n\t\treturn \"Gddrnf_will_signal_deleg_avail\"\n\t}\n\treturn \"\"\n}\ntype XdrType_GET_DIR_DELEGATION4res_non_fatal = *GET_DIR_DELEGATION4res_non_fatal\nfunc (v *GET_DIR_DELEGATION4res_non_fatal) XdrPointer() interface{} { return v }\nfunc (GET_DIR_DELEGATION4res_non_fatal) XdrTypeName() string { return \"GET_DIR_DELEGATION4res_non_fatal\" }\nfunc (v GET_DIR_DELEGATION4res_non_fatal) XdrValue() interface{} { return v }\nfunc (v *GET_DIR_DELEGATION4res_non_fatal) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *GET_DIR_DELEGATION4res_non_fatal) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Gddrnf4_status(&u.Gddrnf_status).XdrMarshal(x, x.Sprintf(\"%sgddrnf_status\", name))\n\tswitch u.Gddrnf_status {\n\tcase GDD4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sgddrnf_resok4\", name), XDR_GET_DIR_DELEGATION4resok(u.Gddrnf_resok4()))\n\t\treturn\n\tcase GDD4_UNAVAIL:\n\t\tx.Marshal(x.Sprintf(\"%sgddrnf_will_signal_deleg_avail\", name), XDR_bool(u.Gddrnf_will_signal_deleg_avail()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Gddrnf_status (%v) in GET_DIR_DELEGATION4res_non_fatal\", u.Gddrnf_status)\n}\nfunc XDR_GET_DIR_DELEGATION4res_non_fatal(v *GET_DIR_DELEGATION4res_non_fatal) *GET_DIR_DELEGATION4res_non_fatal { return v}\nfunc (u *GET_DIR_DELEGATION4res) Gddr_res_non_fatal4() *GET_DIR_DELEGATION4res_non_fatal {\n\tswitch u.Gddr_status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*GET_DIR_DELEGATION4res_non_fatal); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GET_DIR_DELEGATION4res_non_fatal\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"GET_DIR_DELEGATION4res.Gddr_res_non_fatal4 accessed when Gddr_status == %v\", u.Gddr_status)\n\t\treturn nil\n\t}\n}\nfunc (u GET_DIR_DELEGATION4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *GET_DIR_DELEGATION4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Gddr_status)\n}\nfunc (u *GET_DIR_DELEGATION4res) XdrUnionTagName() string {\n\treturn \"Gddr_status\"\n}\nfunc (u *GET_DIR_DELEGATION4res) XdrUnionBody() XdrType {\n\tswitch u.Gddr_status {\n\tcase NFS4_OK:\n\t\treturn XDR_GET_DIR_DELEGATION4res_non_fatal(u.Gddr_res_non_fatal4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *GET_DIR_DELEGATION4res) XdrUnionBodyName() string {\n\tswitch u.Gddr_status {\n\tcase NFS4_OK:\n\t\treturn \"Gddr_res_non_fatal4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_GET_DIR_DELEGATION4res = *GET_DIR_DELEGATION4res\nfunc (v *GET_DIR_DELEGATION4res) XdrPointer() interface{} { return v }\nfunc (GET_DIR_DELEGATION4res) XdrTypeName() string { return \"GET_DIR_DELEGATION4res\" }\nfunc (v GET_DIR_DELEGATION4res) XdrValue() interface{} { return v }\nfunc (v *GET_DIR_DELEGATION4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *GET_DIR_DELEGATION4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Gddr_status).XdrMarshal(x, x.Sprintf(\"%sgddr_status\", name))\n\tswitch u.Gddr_status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sgddr_res_non_fatal4\", name), XDR_GET_DIR_DELEGATION4res_non_fatal(u.Gddr_res_non_fatal4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_GET_DIR_DELEGATION4res(v *GET_DIR_DELEGATION4res) *GET_DIR_DELEGATION4res { return v}\ntype XdrType_GETDEVICEINFO4args = *GETDEVICEINFO4args\nfunc (v *GETDEVICEINFO4args) XdrPointer() interface{} { return v }\nfunc (GETDEVICEINFO4args) XdrTypeName() string { return \"GETDEVICEINFO4args\" }\nfunc (v GETDEVICEINFO4args) XdrValue() interface{} { return v }\nfunc (v *GETDEVICEINFO4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *GETDEVICEINFO4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sgdia_device_id\", name), XDR_Deviceid4(&v.Gdia_device_id))\n\tx.Marshal(x.Sprintf(\"%sgdia_layout_type\", name), XDR_Layouttype4(&v.Gdia_layout_type))\n\tx.Marshal(x.Sprintf(\"%sgdia_maxcount\", name), XDR_Count4(&v.Gdia_maxcount))\n\tx.Marshal(x.Sprintf(\"%sgdia_notify_types\", name), XDR_Bitmap4(&v.Gdia_notify_types))\n}\nfunc XDR_GETDEVICEINFO4args(v *GETDEVICEINFO4args) *GETDEVICEINFO4args { return v }\ntype XdrType_GETDEVICEINFO4resok = *GETDEVICEINFO4resok\nfunc (v *GETDEVICEINFO4resok) XdrPointer() interface{} { return v }\nfunc (GETDEVICEINFO4resok) XdrTypeName() string { return \"GETDEVICEINFO4resok\" }\nfunc (v GETDEVICEINFO4resok) XdrValue() interface{} { return v }\nfunc (v *GETDEVICEINFO4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *GETDEVICEINFO4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sgdir_device_addr\", name), XDR_Device_addr4(&v.Gdir_device_addr))\n\tx.Marshal(x.Sprintf(\"%sgdir_notification\", name), XDR_Bitmap4(&v.Gdir_notification))\n}\nfunc XDR_GETDEVICEINFO4resok(v *GETDEVICEINFO4resok) *GETDEVICEINFO4resok { return v }\nfunc (u *GETDEVICEINFO4res) Gdir_resok4() *GETDEVICEINFO4resok {\n\tswitch u.Gdir_status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*GETDEVICEINFO4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GETDEVICEINFO4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"GETDEVICEINFO4res.Gdir_resok4 accessed when Gdir_status == %v\", u.Gdir_status)\n\t\treturn nil\n\t}\n}\nfunc (u *GETDEVICEINFO4res) Gdir_mincount() *Count4 {\n\tswitch u.Gdir_status {\n\tcase NFS4ERR_TOOSMALL:\n\t\tif v, ok := u.U.(*Count4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Count4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"GETDEVICEINFO4res.Gdir_mincount accessed when Gdir_status == %v\", u.Gdir_status)\n\t\treturn nil\n\t}\n}\nfunc (u GETDEVICEINFO4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *GETDEVICEINFO4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Gdir_status)\n}\nfunc (u *GETDEVICEINFO4res) XdrUnionTagName() string {\n\treturn \"Gdir_status\"\n}\nfunc (u *GETDEVICEINFO4res) XdrUnionBody() XdrType {\n\tswitch u.Gdir_status {\n\tcase NFS4_OK:\n\t\treturn XDR_GETDEVICEINFO4resok(u.Gdir_resok4())\n\tcase NFS4ERR_TOOSMALL:\n\t\treturn XDR_Count4(u.Gdir_mincount())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *GETDEVICEINFO4res) XdrUnionBodyName() string {\n\tswitch u.Gdir_status {\n\tcase NFS4_OK:\n\t\treturn \"Gdir_resok4\"\n\tcase NFS4ERR_TOOSMALL:\n\t\treturn \"Gdir_mincount\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_GETDEVICEINFO4res = *GETDEVICEINFO4res\nfunc (v *GETDEVICEINFO4res) XdrPointer() interface{} { return v }\nfunc (GETDEVICEINFO4res) XdrTypeName() string { return \"GETDEVICEINFO4res\" }\nfunc (v GETDEVICEINFO4res) XdrValue() interface{} { return v }\nfunc (v *GETDEVICEINFO4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *GETDEVICEINFO4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Gdir_status).XdrMarshal(x, x.Sprintf(\"%sgdir_status\", name))\n\tswitch u.Gdir_status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sgdir_resok4\", name), XDR_GETDEVICEINFO4resok(u.Gdir_resok4()))\n\t\treturn\n\tcase NFS4ERR_TOOSMALL:\n\t\tx.Marshal(x.Sprintf(\"%sgdir_mincount\", name), XDR_Count4(u.Gdir_mincount()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_GETDEVICEINFO4res(v *GETDEVICEINFO4res) *GETDEVICEINFO4res { return v}\ntype XdrType_GETDEVICELIST4args = *GETDEVICELIST4args\nfunc (v *GETDEVICELIST4args) XdrPointer() interface{} { return v }\nfunc (GETDEVICELIST4args) XdrTypeName() string { return \"GETDEVICELIST4args\" }\nfunc (v GETDEVICELIST4args) XdrValue() interface{} { return v }\nfunc (v *GETDEVICELIST4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *GETDEVICELIST4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sgdla_layout_type\", name), XDR_Layouttype4(&v.Gdla_layout_type))\n\tx.Marshal(x.Sprintf(\"%sgdla_maxdevices\", name), XDR_Count4(&v.Gdla_maxdevices))\n\tx.Marshal(x.Sprintf(\"%sgdla_cookie\", name), XDR_Nfs_cookie4(&v.Gdla_cookie))\n\tx.Marshal(x.Sprintf(\"%sgdla_cookieverf\", name), XDR_Verifier4(&v.Gdla_cookieverf))\n}\nfunc XDR_GETDEVICELIST4args(v *GETDEVICELIST4args) *GETDEVICELIST4args { return v }\ntype _XdrVec_unbounded_Deviceid4 []Deviceid4\nfunc (_XdrVec_unbounded_Deviceid4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Deviceid4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Deviceid4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Deviceid4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Deviceid4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Deviceid4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Deviceid4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Deviceid4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Deviceid4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Deviceid4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Deviceid4) XdrTypeName() string { return \"Deviceid4<>\" }\nfunc (v *_XdrVec_unbounded_Deviceid4) XdrPointer() interface{} { return (*[]Deviceid4)(v) }\nfunc (v _XdrVec_unbounded_Deviceid4) XdrValue() interface{} { return ([]Deviceid4)(v) }\nfunc (v *_XdrVec_unbounded_Deviceid4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_GETDEVICELIST4resok = *GETDEVICELIST4resok\nfunc (v *GETDEVICELIST4resok) XdrPointer() interface{} { return v }\nfunc (GETDEVICELIST4resok) XdrTypeName() string { return \"GETDEVICELIST4resok\" }\nfunc (v GETDEVICELIST4resok) XdrValue() interface{} { return v }\nfunc (v *GETDEVICELIST4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *GETDEVICELIST4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sgdlr_cookie\", name), XDR_Nfs_cookie4(&v.Gdlr_cookie))\n\tx.Marshal(x.Sprintf(\"%sgdlr_cookieverf\", name), XDR_Verifier4(&v.Gdlr_cookieverf))\n\tx.Marshal(x.Sprintf(\"%sgdlr_deviceid_list\", name), (*_XdrVec_unbounded_Deviceid4)(&v.Gdlr_deviceid_list))\n\tx.Marshal(x.Sprintf(\"%sgdlr_eof\", name), XDR_bool(&v.Gdlr_eof))\n}\nfunc XDR_GETDEVICELIST4resok(v *GETDEVICELIST4resok) *GETDEVICELIST4resok { return v }\nfunc (u *GETDEVICELIST4res) Gdlr_resok4() *GETDEVICELIST4resok {\n\tswitch u.Gdlr_status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*GETDEVICELIST4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GETDEVICELIST4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"GETDEVICELIST4res.Gdlr_resok4 accessed when Gdlr_status == %v\", u.Gdlr_status)\n\t\treturn nil\n\t}\n}\nfunc (u GETDEVICELIST4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *GETDEVICELIST4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Gdlr_status)\n}\nfunc (u *GETDEVICELIST4res) XdrUnionTagName() string {\n\treturn \"Gdlr_status\"\n}\nfunc (u *GETDEVICELIST4res) XdrUnionBody() XdrType {\n\tswitch u.Gdlr_status {\n\tcase NFS4_OK:\n\t\treturn XDR_GETDEVICELIST4resok(u.Gdlr_resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *GETDEVICELIST4res) XdrUnionBodyName() string {\n\tswitch u.Gdlr_status {\n\tcase NFS4_OK:\n\t\treturn \"Gdlr_resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_GETDEVICELIST4res = *GETDEVICELIST4res\nfunc (v *GETDEVICELIST4res) XdrPointer() interface{} { return v }\nfunc (GETDEVICELIST4res) XdrTypeName() string { return \"GETDEVICELIST4res\" }\nfunc (v GETDEVICELIST4res) XdrValue() interface{} { return v }\nfunc (v *GETDEVICELIST4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *GETDEVICELIST4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Gdlr_status).XdrMarshal(x, x.Sprintf(\"%sgdlr_status\", name))\n\tswitch u.Gdlr_status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sgdlr_resok4\", name), XDR_GETDEVICELIST4resok(u.Gdlr_resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_GETDEVICELIST4res(v *GETDEVICELIST4res) *GETDEVICELIST4res { return v}\nvar _XdrTags_Newtime4 = map[int32]bool{\n\tXdrToI32(true): true,\n\tXdrToI32(false): true,\n}\nfunc (_ Newtime4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Newtime4\n}\nfunc (u *Newtime4) Nt_time() *Nfstime4 {\n\tswitch u.Nt_timechanged {\n\tcase true:\n\t\tif v, ok := u.U.(*Nfstime4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Nfstime4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Newtime4.Nt_time accessed when Nt_timechanged == %v\", u.Nt_timechanged)\n\t\treturn nil\n\t}\n}\nfunc (u Newtime4) XdrValid() bool {\n\tswitch u.Nt_timechanged {\n\tcase true,false:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Newtime4) XdrUnionTag() XdrNum32 {\n\treturn XDR_bool(&u.Nt_timechanged)\n}\nfunc (u *Newtime4) XdrUnionTagName() string {\n\treturn \"Nt_timechanged\"\n}\nfunc (u *Newtime4) XdrUnionBody() XdrType {\n\tswitch u.Nt_timechanged {\n\tcase true:\n\t\treturn XDR_Nfstime4(u.Nt_time())\n\tcase false:\n\t\treturn nil\n\t}\n\treturn nil\n}\nfunc (u *Newtime4) XdrUnionBodyName() string {\n\tswitch u.Nt_timechanged {\n\tcase true:\n\t\treturn \"Nt_time\"\n\tcase false:\n\t\treturn \"\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Newtime4 = *Newtime4\nfunc (v *Newtime4) XdrPointer() interface{} { return v }\nfunc (Newtime4) XdrTypeName() string { return \"Newtime4\" }\nfunc (v Newtime4) XdrValue() interface{} { return v }\nfunc (v *Newtime4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Newtime4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_bool(&u.Nt_timechanged).XdrMarshal(x, x.Sprintf(\"%snt_timechanged\", name))\n\tswitch u.Nt_timechanged {\n\tcase true:\n\t\tx.Marshal(x.Sprintf(\"%snt_time\", name), XDR_Nfstime4(u.Nt_time()))\n\t\treturn\n\tcase false:\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Nt_timechanged (%v) in Newtime4\", u.Nt_timechanged)\n}\nfunc XDR_Newtime4(v *Newtime4) *Newtime4 { return v}\nvar _XdrTags_Newoffset4 = map[int32]bool{\n\tXdrToI32(true): true,\n\tXdrToI32(false): true,\n}\nfunc (_ Newoffset4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Newoffset4\n}\nfunc (u *Newoffset4) No_offset() *Offset4 {\n\tswitch u.No_newoffset {\n\tcase true:\n\t\tif v, ok := u.U.(*Offset4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Offset4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Newoffset4.No_offset accessed when No_newoffset == %v\", u.No_newoffset)\n\t\treturn nil\n\t}\n}\nfunc (u Newoffset4) XdrValid() bool {\n\tswitch u.No_newoffset {\n\tcase true,false:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Newoffset4) XdrUnionTag() XdrNum32 {\n\treturn XDR_bool(&u.No_newoffset)\n}\nfunc (u *Newoffset4) XdrUnionTagName() string {\n\treturn \"No_newoffset\"\n}\nfunc (u *Newoffset4) XdrUnionBody() XdrType {\n\tswitch u.No_newoffset {\n\tcase true:\n\t\treturn XDR_Offset4(u.No_offset())\n\tcase false:\n\t\treturn nil\n\t}\n\treturn nil\n}\nfunc (u *Newoffset4) XdrUnionBodyName() string {\n\tswitch u.No_newoffset {\n\tcase true:\n\t\treturn \"No_offset\"\n\tcase false:\n\t\treturn \"\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Newoffset4 = *Newoffset4\nfunc (v *Newoffset4) XdrPointer() interface{} { return v }\nfunc (Newoffset4) XdrTypeName() string { return \"Newoffset4\" }\nfunc (v Newoffset4) XdrValue() interface{} { return v }\nfunc (v *Newoffset4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Newoffset4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_bool(&u.No_newoffset).XdrMarshal(x, x.Sprintf(\"%sno_newoffset\", name))\n\tswitch u.No_newoffset {\n\tcase true:\n\t\tx.Marshal(x.Sprintf(\"%sno_offset\", name), XDR_Offset4(u.No_offset()))\n\t\treturn\n\tcase false:\n\t\treturn\n\t}\n\tXdrPanic(\"invalid No_newoffset (%v) in Newoffset4\", u.No_newoffset)\n}\nfunc XDR_Newoffset4(v *Newoffset4) *Newoffset4 { return v}\ntype XdrType_LAYOUTCOMMIT4args = *LAYOUTCOMMIT4args\nfunc (v *LAYOUTCOMMIT4args) XdrPointer() interface{} { return v }\nfunc (LAYOUTCOMMIT4args) XdrTypeName() string { return \"LAYOUTCOMMIT4args\" }\nfunc (v LAYOUTCOMMIT4args) XdrValue() interface{} { return v }\nfunc (v *LAYOUTCOMMIT4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LAYOUTCOMMIT4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sloca_offset\", name), XDR_Offset4(&v.Loca_offset))\n\tx.Marshal(x.Sprintf(\"%sloca_length\", name), XDR_Length4(&v.Loca_length))\n\tx.Marshal(x.Sprintf(\"%sloca_reclaim\", name), XDR_bool(&v.Loca_reclaim))\n\tx.Marshal(x.Sprintf(\"%sloca_stateid\", name), XDR_Stateid4(&v.Loca_stateid))\n\tx.Marshal(x.Sprintf(\"%sloca_last_write_offset\", name), XDR_Newoffset4(&v.Loca_last_write_offset))\n\tx.Marshal(x.Sprintf(\"%sloca_time_modify\", name), XDR_Newtime4(&v.Loca_time_modify))\n\tx.Marshal(x.Sprintf(\"%sloca_layoutupdate\", name), XDR_Layoutupdate4(&v.Loca_layoutupdate))\n}\nfunc XDR_LAYOUTCOMMIT4args(v *LAYOUTCOMMIT4args) *LAYOUTCOMMIT4args { return v }\nvar _XdrTags_Newsize4 = map[int32]bool{\n\tXdrToI32(true): true,\n\tXdrToI32(false): true,\n}\nfunc (_ Newsize4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Newsize4\n}\nfunc (u *Newsize4) Ns_size() *Length4 {\n\tswitch u.Ns_sizechanged {\n\tcase true:\n\t\tif v, ok := u.U.(*Length4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Length4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Newsize4.Ns_size accessed when Ns_sizechanged == %v\", u.Ns_sizechanged)\n\t\treturn nil\n\t}\n}\nfunc (u Newsize4) XdrValid() bool {\n\tswitch u.Ns_sizechanged {\n\tcase true,false:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Newsize4) XdrUnionTag() XdrNum32 {\n\treturn XDR_bool(&u.Ns_sizechanged)\n}\nfunc (u *Newsize4) XdrUnionTagName() string {\n\treturn \"Ns_sizechanged\"\n}\nfunc (u *Newsize4) XdrUnionBody() XdrType {\n\tswitch u.Ns_sizechanged {\n\tcase true:\n\t\treturn XDR_Length4(u.Ns_size())\n\tcase false:\n\t\treturn nil\n\t}\n\treturn nil\n}\nfunc (u *Newsize4) XdrUnionBodyName() string {\n\tswitch u.Ns_sizechanged {\n\tcase true:\n\t\treturn \"Ns_size\"\n\tcase false:\n\t\treturn \"\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Newsize4 = *Newsize4\nfunc (v *Newsize4) XdrPointer() interface{} { return v }\nfunc (Newsize4) XdrTypeName() string { return \"Newsize4\" }\nfunc (v Newsize4) XdrValue() interface{} { return v }\nfunc (v *Newsize4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Newsize4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_bool(&u.Ns_sizechanged).XdrMarshal(x, x.Sprintf(\"%sns_sizechanged\", name))\n\tswitch u.Ns_sizechanged {\n\tcase true:\n\t\tx.Marshal(x.Sprintf(\"%sns_size\", name), XDR_Length4(u.Ns_size()))\n\t\treturn\n\tcase false:\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Ns_sizechanged (%v) in Newsize4\", u.Ns_sizechanged)\n}\nfunc XDR_Newsize4(v *Newsize4) *Newsize4 { return v}\ntype XdrType_LAYOUTCOMMIT4resok = *LAYOUTCOMMIT4resok\nfunc (v *LAYOUTCOMMIT4resok) XdrPointer() interface{} { return v }\nfunc (LAYOUTCOMMIT4resok) XdrTypeName() string { return \"LAYOUTCOMMIT4resok\" }\nfunc (v LAYOUTCOMMIT4resok) XdrValue() interface{} { return v }\nfunc (v *LAYOUTCOMMIT4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LAYOUTCOMMIT4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slocr_newsize\", name), XDR_Newsize4(&v.Locr_newsize))\n}\nfunc XDR_LAYOUTCOMMIT4resok(v *LAYOUTCOMMIT4resok) *LAYOUTCOMMIT4resok { return v }\nfunc (u *LAYOUTCOMMIT4res) Locr_resok4() *LAYOUTCOMMIT4resok {\n\tswitch u.Locr_status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*LAYOUTCOMMIT4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LAYOUTCOMMIT4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"LAYOUTCOMMIT4res.Locr_resok4 accessed when Locr_status == %v\", u.Locr_status)\n\t\treturn nil\n\t}\n}\nfunc (u LAYOUTCOMMIT4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *LAYOUTCOMMIT4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Locr_status)\n}\nfunc (u *LAYOUTCOMMIT4res) XdrUnionTagName() string {\n\treturn \"Locr_status\"\n}\nfunc (u *LAYOUTCOMMIT4res) XdrUnionBody() XdrType {\n\tswitch u.Locr_status {\n\tcase NFS4_OK:\n\t\treturn XDR_LAYOUTCOMMIT4resok(u.Locr_resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *LAYOUTCOMMIT4res) XdrUnionBodyName() string {\n\tswitch u.Locr_status {\n\tcase NFS4_OK:\n\t\treturn \"Locr_resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_LAYOUTCOMMIT4res = *LAYOUTCOMMIT4res\nfunc (v *LAYOUTCOMMIT4res) XdrPointer() interface{} { return v }\nfunc (LAYOUTCOMMIT4res) XdrTypeName() string { return \"LAYOUTCOMMIT4res\" }\nfunc (v LAYOUTCOMMIT4res) XdrValue() interface{} { return v }\nfunc (v *LAYOUTCOMMIT4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *LAYOUTCOMMIT4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Locr_status).XdrMarshal(x, x.Sprintf(\"%slocr_status\", name))\n\tswitch u.Locr_status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%slocr_resok4\", name), XDR_LAYOUTCOMMIT4resok(u.Locr_resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_LAYOUTCOMMIT4res(v *LAYOUTCOMMIT4res) *LAYOUTCOMMIT4res { return v}\ntype XdrType_LAYOUTGET4args = *LAYOUTGET4args\nfunc (v *LAYOUTGET4args) XdrPointer() interface{} { return v }\nfunc (LAYOUTGET4args) XdrTypeName() string { return \"LAYOUTGET4args\" }\nfunc (v LAYOUTGET4args) XdrValue() interface{} { return v }\nfunc (v *LAYOUTGET4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LAYOUTGET4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sloga_signal_layout_avail\", name), XDR_bool(&v.Loga_signal_layout_avail))\n\tx.Marshal(x.Sprintf(\"%sloga_layout_type\", name), XDR_Layouttype4(&v.Loga_layout_type))\n\tx.Marshal(x.Sprintf(\"%sloga_iomode\", name), XDR_Layoutiomode4(&v.Loga_iomode))\n\tx.Marshal(x.Sprintf(\"%sloga_offset\", name), XDR_Offset4(&v.Loga_offset))\n\tx.Marshal(x.Sprintf(\"%sloga_length\", name), XDR_Length4(&v.Loga_length))\n\tx.Marshal(x.Sprintf(\"%sloga_minlength\", name), XDR_Length4(&v.Loga_minlength))\n\tx.Marshal(x.Sprintf(\"%sloga_stateid\", name), XDR_Stateid4(&v.Loga_stateid))\n\tx.Marshal(x.Sprintf(\"%sloga_maxcount\", name), XDR_Count4(&v.Loga_maxcount))\n}\nfunc XDR_LAYOUTGET4args(v *LAYOUTGET4args) *LAYOUTGET4args { return v }\ntype _XdrVec_unbounded_Layout4 []Layout4\nfunc (_XdrVec_unbounded_Layout4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Layout4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Layout4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Layout4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Layout4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Layout4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Layout4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Layout4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Layout4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Layout4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Layout4) XdrTypeName() string { return \"Layout4<>\" }\nfunc (v *_XdrVec_unbounded_Layout4) XdrPointer() interface{} { return (*[]Layout4)(v) }\nfunc (v _XdrVec_unbounded_Layout4) XdrValue() interface{} { return ([]Layout4)(v) }\nfunc (v *_XdrVec_unbounded_Layout4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_LAYOUTGET4resok = *LAYOUTGET4resok\nfunc (v *LAYOUTGET4resok) XdrPointer() interface{} { return v }\nfunc (LAYOUTGET4resok) XdrTypeName() string { return \"LAYOUTGET4resok\" }\nfunc (v LAYOUTGET4resok) XdrValue() interface{} { return v }\nfunc (v *LAYOUTGET4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LAYOUTGET4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slogr_return_on_close\", name), XDR_bool(&v.Logr_return_on_close))\n\tx.Marshal(x.Sprintf(\"%slogr_stateid\", name), XDR_Stateid4(&v.Logr_stateid))\n\tx.Marshal(x.Sprintf(\"%slogr_layout\", name), (*_XdrVec_unbounded_Layout4)(&v.Logr_layout))\n}\nfunc XDR_LAYOUTGET4resok(v *LAYOUTGET4resok) *LAYOUTGET4resok { return v }\nfunc (u *LAYOUTGET4res) Logr_resok4() *LAYOUTGET4resok {\n\tswitch u.Logr_status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*LAYOUTGET4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LAYOUTGET4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"LAYOUTGET4res.Logr_resok4 accessed when Logr_status == %v\", u.Logr_status)\n\t\treturn nil\n\t}\n}\nfunc (u *LAYOUTGET4res) Logr_will_signal_layout_avail() *bool {\n\tswitch u.Logr_status {\n\tcase NFS4ERR_LAYOUTTRYLATER:\n\t\tif v, ok := u.U.(*bool); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero bool\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"LAYOUTGET4res.Logr_will_signal_layout_avail accessed when Logr_status == %v\", u.Logr_status)\n\t\treturn nil\n\t}\n}\nfunc (u LAYOUTGET4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *LAYOUTGET4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Logr_status)\n}\nfunc (u *LAYOUTGET4res) XdrUnionTagName() string {\n\treturn \"Logr_status\"\n}\nfunc (u *LAYOUTGET4res) XdrUnionBody() XdrType {\n\tswitch u.Logr_status {\n\tcase NFS4_OK:\n\t\treturn XDR_LAYOUTGET4resok(u.Logr_resok4())\n\tcase NFS4ERR_LAYOUTTRYLATER:\n\t\treturn XDR_bool(u.Logr_will_signal_layout_avail())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *LAYOUTGET4res) XdrUnionBodyName() string {\n\tswitch u.Logr_status {\n\tcase NFS4_OK:\n\t\treturn \"Logr_resok4\"\n\tcase NFS4ERR_LAYOUTTRYLATER:\n\t\treturn \"Logr_will_signal_layout_avail\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_LAYOUTGET4res = *LAYOUTGET4res\nfunc (v *LAYOUTGET4res) XdrPointer() interface{} { return v }\nfunc (LAYOUTGET4res) XdrTypeName() string { return \"LAYOUTGET4res\" }\nfunc (v LAYOUTGET4res) XdrValue() interface{} { return v }\nfunc (v *LAYOUTGET4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *LAYOUTGET4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Logr_status).XdrMarshal(x, x.Sprintf(\"%slogr_status\", name))\n\tswitch u.Logr_status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%slogr_resok4\", name), XDR_LAYOUTGET4resok(u.Logr_resok4()))\n\t\treturn\n\tcase NFS4ERR_LAYOUTTRYLATER:\n\t\tx.Marshal(x.Sprintf(\"%slogr_will_signal_layout_avail\", name), XDR_bool(u.Logr_will_signal_layout_avail()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_LAYOUTGET4res(v *LAYOUTGET4res) *LAYOUTGET4res { return v}\nvar _XdrNames_Layoutreturn_type4 = map[int32]string{\n\tint32(LAYOUTRETURN4_FILE): \"LAYOUTRETURN4_FILE\",\n\tint32(LAYOUTRETURN4_FSID): \"LAYOUTRETURN4_FSID\",\n\tint32(LAYOUTRETURN4_ALL): \"LAYOUTRETURN4_ALL\",\n}\nvar _XdrValues_Layoutreturn_type4 = map[string]int32{\n\t\"LAYOUTRETURN4_FILE\": int32(LAYOUTRETURN4_FILE),\n\t\"LAYOUTRETURN4_FSID\": int32(LAYOUTRETURN4_FSID),\n\t\"LAYOUTRETURN4_ALL\": int32(LAYOUTRETURN4_ALL),\n}\nfunc (Layoutreturn_type4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Layoutreturn_type4\n}\nfunc (v Layoutreturn_type4) String() string {\n\tif s, ok := _XdrNames_Layoutreturn_type4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Layoutreturn_type4#%d\", v)\n}\nfunc (v *Layoutreturn_type4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Layoutreturn_type4[stok]; ok {\n\t\t\t*v = Layoutreturn_type4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Layoutreturn_type4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Layoutreturn_type4.\", stok))\n\t}\n}\nfunc (v Layoutreturn_type4) GetU32() uint32 { return uint32(v) }\nfunc (v *Layoutreturn_type4) SetU32(n uint32) { *v = Layoutreturn_type4(n) }\nfunc (v *Layoutreturn_type4) XdrPointer() interface{} { return v }\nfunc (Layoutreturn_type4) XdrTypeName() string { return \"Layoutreturn_type4\" }\nfunc (v Layoutreturn_type4) XdrValue() interface{} { return v }\nfunc (v *Layoutreturn_type4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Layoutreturn_type4 = *Layoutreturn_type4\nfunc XDR_Layoutreturn_type4(v *Layoutreturn_type4) *Layoutreturn_type4 { return v }\nfunc (v *Layoutreturn_type4) XdrInitialize() {\n\tswitch Layoutreturn_type4(0) {\n\tcase LAYOUTRETURN4_FILE, LAYOUTRETURN4_FSID, LAYOUTRETURN4_ALL:\n\tdefault:\n\t\tif *v == Layoutreturn_type4(0) { *v = LAYOUTRETURN4_FILE }\n\t}\n}\ntype XdrType_Layoutreturn_file4 = *Layoutreturn_file4\nfunc (v *Layoutreturn_file4) XdrPointer() interface{} { return v }\nfunc (Layoutreturn_file4) XdrTypeName() string { return \"Layoutreturn_file4\" }\nfunc (v Layoutreturn_file4) XdrValue() interface{} { return v }\nfunc (v *Layoutreturn_file4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Layoutreturn_file4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slrf_offset\", name), XDR_Offset4(&v.Lrf_offset))\n\tx.Marshal(x.Sprintf(\"%slrf_length\", name), XDR_Length4(&v.Lrf_length))\n\tx.Marshal(x.Sprintf(\"%slrf_stateid\", name), XDR_Stateid4(&v.Lrf_stateid))\n\tx.Marshal(x.Sprintf(\"%slrf_body\", name), XdrVecOpaque{&v.Lrf_body, 0xffffffff})\n}\nfunc XDR_Layoutreturn_file4(v *Layoutreturn_file4) *Layoutreturn_file4 { return v }\nfunc (u *Layoutreturn4) Lr_layout() *Layoutreturn_file4 {\n\tswitch u.Lr_returntype {\n\tcase LAYOUTRETURN4_FILE:\n\t\tif v, ok := u.U.(*Layoutreturn_file4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Layoutreturn_file4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Layoutreturn4.Lr_layout accessed when Lr_returntype == %v\", u.Lr_returntype)\n\t\treturn nil\n\t}\n}\nfunc (u Layoutreturn4) XdrValid() bool {\n\treturn true\n}\nfunc (u *Layoutreturn4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Layoutreturn_type4(&u.Lr_returntype)\n}\nfunc (u *Layoutreturn4) XdrUnionTagName() string {\n\treturn \"Lr_returntype\"\n}\nfunc (u *Layoutreturn4) XdrUnionBody() XdrType {\n\tswitch u.Lr_returntype {\n\tcase LAYOUTRETURN4_FILE:\n\t\treturn XDR_Layoutreturn_file4(u.Lr_layout())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *Layoutreturn4) XdrUnionBodyName() string {\n\tswitch u.Lr_returntype {\n\tcase LAYOUTRETURN4_FILE:\n\t\treturn \"Lr_layout\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_Layoutreturn4 = *Layoutreturn4\nfunc (v *Layoutreturn4) XdrPointer() interface{} { return v }\nfunc (Layoutreturn4) XdrTypeName() string { return \"Layoutreturn4\" }\nfunc (v Layoutreturn4) XdrValue() interface{} { return v }\nfunc (v *Layoutreturn4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Layoutreturn4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Layoutreturn_type4(&u.Lr_returntype).XdrMarshal(x, x.Sprintf(\"%slr_returntype\", name))\n\tswitch u.Lr_returntype {\n\tcase LAYOUTRETURN4_FILE:\n\t\tx.Marshal(x.Sprintf(\"%slr_layout\", name), XDR_Layoutreturn_file4(u.Lr_layout()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_Layoutreturn4(v *Layoutreturn4) *Layoutreturn4 { return v}\ntype XdrType_LAYOUTRETURN4args = *LAYOUTRETURN4args\nfunc (v *LAYOUTRETURN4args) XdrPointer() interface{} { return v }\nfunc (LAYOUTRETURN4args) XdrTypeName() string { return \"LAYOUTRETURN4args\" }\nfunc (v LAYOUTRETURN4args) XdrValue() interface{} { return v }\nfunc (v *LAYOUTRETURN4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *LAYOUTRETURN4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slora_reclaim\", name), XDR_bool(&v.Lora_reclaim))\n\tx.Marshal(x.Sprintf(\"%slora_layout_type\", name), XDR_Layouttype4(&v.Lora_layout_type))\n\tx.Marshal(x.Sprintf(\"%slora_iomode\", name), XDR_Layoutiomode4(&v.Lora_iomode))\n\tx.Marshal(x.Sprintf(\"%slora_layoutreturn\", name), XDR_Layoutreturn4(&v.Lora_layoutreturn))\n}\nfunc XDR_LAYOUTRETURN4args(v *LAYOUTRETURN4args) *LAYOUTRETURN4args { return v }\nvar _XdrTags_Layoutreturn_stateid = map[int32]bool{\n\tXdrToI32(true): true,\n\tXdrToI32(false): true,\n}\nfunc (_ Layoutreturn_stateid) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Layoutreturn_stateid\n}\nfunc (u *Layoutreturn_stateid) Lrs_stateid() *Stateid4 {\n\tswitch u.Lrs_present {\n\tcase true:\n\t\tif v, ok := u.U.(*Stateid4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Stateid4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Layoutreturn_stateid.Lrs_stateid accessed when Lrs_present == %v\", u.Lrs_present)\n\t\treturn nil\n\t}\n}\nfunc (u Layoutreturn_stateid) XdrValid() bool {\n\tswitch u.Lrs_present {\n\tcase true,false:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Layoutreturn_stateid) XdrUnionTag() XdrNum32 {\n\treturn XDR_bool(&u.Lrs_present)\n}\nfunc (u *Layoutreturn_stateid) XdrUnionTagName() string {\n\treturn \"Lrs_present\"\n}\nfunc (u *Layoutreturn_stateid) XdrUnionBody() XdrType {\n\tswitch u.Lrs_present {\n\tcase true:\n\t\treturn XDR_Stateid4(u.Lrs_stateid())\n\tcase false:\n\t\treturn nil\n\t}\n\treturn nil\n}\nfunc (u *Layoutreturn_stateid) XdrUnionBodyName() string {\n\tswitch u.Lrs_present {\n\tcase true:\n\t\treturn \"Lrs_stateid\"\n\tcase false:\n\t\treturn \"\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Layoutreturn_stateid = *Layoutreturn_stateid\nfunc (v *Layoutreturn_stateid) XdrPointer() interface{} { return v }\nfunc (Layoutreturn_stateid) XdrTypeName() string { return \"Layoutreturn_stateid\" }\nfunc (v Layoutreturn_stateid) XdrValue() interface{} { return v }\nfunc (v *Layoutreturn_stateid) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Layoutreturn_stateid) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_bool(&u.Lrs_present).XdrMarshal(x, x.Sprintf(\"%slrs_present\", name))\n\tswitch u.Lrs_present {\n\tcase true:\n\t\tx.Marshal(x.Sprintf(\"%slrs_stateid\", name), XDR_Stateid4(u.Lrs_stateid()))\n\t\treturn\n\tcase false:\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Lrs_present (%v) in Layoutreturn_stateid\", u.Lrs_present)\n}\nfunc XDR_Layoutreturn_stateid(v *Layoutreturn_stateid) *Layoutreturn_stateid { return v}\nfunc (u *LAYOUTRETURN4res) Lorr_stateid() *Layoutreturn_stateid {\n\tswitch u.Lorr_status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*Layoutreturn_stateid); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Layoutreturn_stateid\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"LAYOUTRETURN4res.Lorr_stateid accessed when Lorr_status == %v\", u.Lorr_status)\n\t\treturn nil\n\t}\n}\nfunc (u LAYOUTRETURN4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *LAYOUTRETURN4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Lorr_status)\n}\nfunc (u *LAYOUTRETURN4res) XdrUnionTagName() string {\n\treturn \"Lorr_status\"\n}\nfunc (u *LAYOUTRETURN4res) XdrUnionBody() XdrType {\n\tswitch u.Lorr_status {\n\tcase NFS4_OK:\n\t\treturn XDR_Layoutreturn_stateid(u.Lorr_stateid())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *LAYOUTRETURN4res) XdrUnionBodyName() string {\n\tswitch u.Lorr_status {\n\tcase NFS4_OK:\n\t\treturn \"Lorr_stateid\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_LAYOUTRETURN4res = *LAYOUTRETURN4res\nfunc (v *LAYOUTRETURN4res) XdrPointer() interface{} { return v }\nfunc (LAYOUTRETURN4res) XdrTypeName() string { return \"LAYOUTRETURN4res\" }\nfunc (v LAYOUTRETURN4res) XdrValue() interface{} { return v }\nfunc (v *LAYOUTRETURN4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *LAYOUTRETURN4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Lorr_status).XdrMarshal(x, x.Sprintf(\"%slorr_status\", name))\n\tswitch u.Lorr_status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%slorr_stateid\", name), XDR_Layoutreturn_stateid(u.Lorr_stateid()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_LAYOUTRETURN4res(v *LAYOUTRETURN4res) *LAYOUTRETURN4res { return v}\nvar _XdrNames_Secinfo_style4 = map[int32]string{\n\tint32(SECINFO_STYLE4_CURRENT_FH): \"SECINFO_STYLE4_CURRENT_FH\",\n\tint32(SECINFO_STYLE4_PARENT): \"SECINFO_STYLE4_PARENT\",\n}\nvar _XdrValues_Secinfo_style4 = map[string]int32{\n\t\"SECINFO_STYLE4_CURRENT_FH\": int32(SECINFO_STYLE4_CURRENT_FH),\n\t\"SECINFO_STYLE4_PARENT\": int32(SECINFO_STYLE4_PARENT),\n}\nfunc (Secinfo_style4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Secinfo_style4\n}\nfunc (v Secinfo_style4) String() string {\n\tif s, ok := _XdrNames_Secinfo_style4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Secinfo_style4#%d\", v)\n}\nfunc (v *Secinfo_style4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Secinfo_style4[stok]; ok {\n\t\t\t*v = Secinfo_style4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Secinfo_style4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Secinfo_style4.\", stok))\n\t}\n}\nfunc (v Secinfo_style4) GetU32() uint32 { return uint32(v) }\nfunc (v *Secinfo_style4) SetU32(n uint32) { *v = Secinfo_style4(n) }\nfunc (v *Secinfo_style4) XdrPointer() interface{} { return v }\nfunc (Secinfo_style4) XdrTypeName() string { return \"Secinfo_style4\" }\nfunc (v Secinfo_style4) XdrValue() interface{} { return v }\nfunc (v *Secinfo_style4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Secinfo_style4 = *Secinfo_style4\nfunc XDR_Secinfo_style4(v *Secinfo_style4) *Secinfo_style4 { return v }\ntype XdrType_SECINFO_NO_NAME4args struct {\n\tXdrType_Secinfo_style4\n}\nfunc XDR_SECINFO_NO_NAME4args(v *SECINFO_NO_NAME4args) *XdrType_SECINFO_NO_NAME4args {\n\treturn &XdrType_SECINFO_NO_NAME4args{XDR_Secinfo_style4(v)}\n}\nfunc (XdrType_SECINFO_NO_NAME4args) XdrTypeName() string { return \"SECINFO_NO_NAME4args\" }\nfunc (v XdrType_SECINFO_NO_NAME4args) XdrUnwrap() XdrType { return v.XdrType_Secinfo_style4 }\ntype XdrType_SECINFO_NO_NAME4res struct {\n\tXdrType_SECINFO4res\n}\nfunc XDR_SECINFO_NO_NAME4res(v *SECINFO_NO_NAME4res) *XdrType_SECINFO_NO_NAME4res {\n\treturn &XdrType_SECINFO_NO_NAME4res{XDR_SECINFO4res(v)}\n}\nfunc (XdrType_SECINFO_NO_NAME4res) XdrTypeName() string { return \"SECINFO_NO_NAME4res\" }\nfunc (v XdrType_SECINFO_NO_NAME4res) XdrUnwrap() XdrType { return v.XdrType_SECINFO4res }\ntype XdrType_SEQUENCE4args = *SEQUENCE4args\nfunc (v *SEQUENCE4args) XdrPointer() interface{} { return v }\nfunc (SEQUENCE4args) XdrTypeName() string { return \"SEQUENCE4args\" }\nfunc (v SEQUENCE4args) XdrValue() interface{} { return v }\nfunc (v *SEQUENCE4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *SEQUENCE4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%ssa_sessionid\", name), XDR_Sessionid4(&v.Sa_sessionid))\n\tx.Marshal(x.Sprintf(\"%ssa_sequenceid\", name), XDR_Sequenceid4(&v.Sa_sequenceid))\n\tx.Marshal(x.Sprintf(\"%ssa_slotid\", name), XDR_Slotid4(&v.Sa_slotid))\n\tx.Marshal(x.Sprintf(\"%ssa_highest_slotid\", name), XDR_Slotid4(&v.Sa_highest_slotid))\n\tx.Marshal(x.Sprintf(\"%ssa_cachethis\", name), XDR_bool(&v.Sa_cachethis))\n}\nfunc XDR_SEQUENCE4args(v *SEQUENCE4args) *SEQUENCE4args { return v }\ntype XdrType_SEQUENCE4resok = *SEQUENCE4resok\nfunc (v *SEQUENCE4resok) XdrPointer() interface{} { return v }\nfunc (SEQUENCE4resok) XdrTypeName() string { return \"SEQUENCE4resok\" }\nfunc (v SEQUENCE4resok) XdrValue() interface{} { return v }\nfunc (v *SEQUENCE4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *SEQUENCE4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%ssr_sessionid\", name), XDR_Sessionid4(&v.Sr_sessionid))\n\tx.Marshal(x.Sprintf(\"%ssr_sequenceid\", name), XDR_Sequenceid4(&v.Sr_sequenceid))\n\tx.Marshal(x.Sprintf(\"%ssr_slotid\", name), XDR_Slotid4(&v.Sr_slotid))\n\tx.Marshal(x.Sprintf(\"%ssr_highest_slotid\", name), XDR_Slotid4(&v.Sr_highest_slotid))\n\tx.Marshal(x.Sprintf(\"%ssr_target_highest_slotid\", name), XDR_Slotid4(&v.Sr_target_highest_slotid))\n\tx.Marshal(x.Sprintf(\"%ssr_status_flags\", name), XDR_Uint32_t(&v.Sr_status_flags))\n}\nfunc XDR_SEQUENCE4resok(v *SEQUENCE4resok) *SEQUENCE4resok { return v }\nfunc (u *SEQUENCE4res) Sr_resok4() *SEQUENCE4resok {\n\tswitch u.Sr_status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*SEQUENCE4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SEQUENCE4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"SEQUENCE4res.Sr_resok4 accessed when Sr_status == %v\", u.Sr_status)\n\t\treturn nil\n\t}\n}\nfunc (u SEQUENCE4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *SEQUENCE4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Sr_status)\n}\nfunc (u *SEQUENCE4res) XdrUnionTagName() string {\n\treturn \"Sr_status\"\n}\nfunc (u *SEQUENCE4res) XdrUnionBody() XdrType {\n\tswitch u.Sr_status {\n\tcase NFS4_OK:\n\t\treturn XDR_SEQUENCE4resok(u.Sr_resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *SEQUENCE4res) XdrUnionBodyName() string {\n\tswitch u.Sr_status {\n\tcase NFS4_OK:\n\t\treturn \"Sr_resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_SEQUENCE4res = *SEQUENCE4res\nfunc (v *SEQUENCE4res) XdrPointer() interface{} { return v }\nfunc (SEQUENCE4res) XdrTypeName() string { return \"SEQUENCE4res\" }\nfunc (v SEQUENCE4res) XdrValue() interface{} { return v }\nfunc (v *SEQUENCE4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *SEQUENCE4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Sr_status).XdrMarshal(x, x.Sprintf(\"%ssr_status\", name))\n\tswitch u.Sr_status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%ssr_resok4\", name), XDR_SEQUENCE4resok(u.Sr_resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_SEQUENCE4res(v *SEQUENCE4res) *SEQUENCE4res { return v}\ntype XdrType_Ssa_digest_input4 = *Ssa_digest_input4\nfunc (v *Ssa_digest_input4) XdrPointer() interface{} { return v }\nfunc (Ssa_digest_input4) XdrTypeName() string { return \"Ssa_digest_input4\" }\nfunc (v Ssa_digest_input4) XdrValue() interface{} { return v }\nfunc (v *Ssa_digest_input4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Ssa_digest_input4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%ssdi_seqargs\", name), XDR_SEQUENCE4args(&v.Sdi_seqargs))\n}\nfunc XDR_Ssa_digest_input4(v *Ssa_digest_input4) *Ssa_digest_input4 { return v }\ntype XdrType_SET_SSV4args = *SET_SSV4args\nfunc (v *SET_SSV4args) XdrPointer() interface{} { return v }\nfunc (SET_SSV4args) XdrTypeName() string { return \"SET_SSV4args\" }\nfunc (v SET_SSV4args) XdrValue() interface{} { return v }\nfunc (v *SET_SSV4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *SET_SSV4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sssa_ssv\", name), XdrVecOpaque{&v.Ssa_ssv, 0xffffffff})\n\tx.Marshal(x.Sprintf(\"%sssa_digest\", name), XdrVecOpaque{&v.Ssa_digest, 0xffffffff})\n}\nfunc XDR_SET_SSV4args(v *SET_SSV4args) *SET_SSV4args { return v }\ntype XdrType_Ssr_digest_input4 = *Ssr_digest_input4\nfunc (v *Ssr_digest_input4) XdrPointer() interface{} { return v }\nfunc (Ssr_digest_input4) XdrTypeName() string { return \"Ssr_digest_input4\" }\nfunc (v Ssr_digest_input4) XdrValue() interface{} { return v }\nfunc (v *Ssr_digest_input4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Ssr_digest_input4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%ssdi_seqres\", name), XDR_SEQUENCE4res(&v.Sdi_seqres))\n}\nfunc XDR_Ssr_digest_input4(v *Ssr_digest_input4) *Ssr_digest_input4 { return v }\ntype XdrType_SET_SSV4resok = *SET_SSV4resok\nfunc (v *SET_SSV4resok) XdrPointer() interface{} { return v }\nfunc (SET_SSV4resok) XdrTypeName() string { return \"SET_SSV4resok\" }\nfunc (v SET_SSV4resok) XdrValue() interface{} { return v }\nfunc (v *SET_SSV4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *SET_SSV4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sssr_digest\", name), XdrVecOpaque{&v.Ssr_digest, 0xffffffff})\n}\nfunc XDR_SET_SSV4resok(v *SET_SSV4resok) *SET_SSV4resok { return v }\nfunc (u *SET_SSV4res) Ssr_resok4() *SET_SSV4resok {\n\tswitch u.Ssr_status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*SET_SSV4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SET_SSV4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"SET_SSV4res.Ssr_resok4 accessed when Ssr_status == %v\", u.Ssr_status)\n\t\treturn nil\n\t}\n}\nfunc (u SET_SSV4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *SET_SSV4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Ssr_status)\n}\nfunc (u *SET_SSV4res) XdrUnionTagName() string {\n\treturn \"Ssr_status\"\n}\nfunc (u *SET_SSV4res) XdrUnionBody() XdrType {\n\tswitch u.Ssr_status {\n\tcase NFS4_OK:\n\t\treturn XDR_SET_SSV4resok(u.Ssr_resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *SET_SSV4res) XdrUnionBodyName() string {\n\tswitch u.Ssr_status {\n\tcase NFS4_OK:\n\t\treturn \"Ssr_resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_SET_SSV4res = *SET_SSV4res\nfunc (v *SET_SSV4res) XdrPointer() interface{} { return v }\nfunc (SET_SSV4res) XdrTypeName() string { return \"SET_SSV4res\" }\nfunc (v SET_SSV4res) XdrValue() interface{} { return v }\nfunc (v *SET_SSV4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *SET_SSV4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Ssr_status).XdrMarshal(x, x.Sprintf(\"%sssr_status\", name))\n\tswitch u.Ssr_status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sssr_resok4\", name), XDR_SET_SSV4resok(u.Ssr_resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_SET_SSV4res(v *SET_SSV4res) *SET_SSV4res { return v}\ntype _XdrVec_unbounded_Stateid4 []Stateid4\nfunc (_XdrVec_unbounded_Stateid4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Stateid4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Stateid4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Stateid4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Stateid4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Stateid4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Stateid4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Stateid4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Stateid4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Stateid4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Stateid4) XdrTypeName() string { return \"Stateid4<>\" }\nfunc (v *_XdrVec_unbounded_Stateid4) XdrPointer() interface{} { return (*[]Stateid4)(v) }\nfunc (v _XdrVec_unbounded_Stateid4) XdrValue() interface{} { return ([]Stateid4)(v) }\nfunc (v *_XdrVec_unbounded_Stateid4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_TEST_STATEID4args = *TEST_STATEID4args\nfunc (v *TEST_STATEID4args) XdrPointer() interface{} { return v }\nfunc (TEST_STATEID4args) XdrTypeName() string { return \"TEST_STATEID4args\" }\nfunc (v TEST_STATEID4args) XdrValue() interface{} { return v }\nfunc (v *TEST_STATEID4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *TEST_STATEID4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sts_stateids\", name), (*_XdrVec_unbounded_Stateid4)(&v.Ts_stateids))\n}\nfunc XDR_TEST_STATEID4args(v *TEST_STATEID4args) *TEST_STATEID4args { return v }\ntype _XdrVec_unbounded_Nfsstat4 []Nfsstat4\nfunc (_XdrVec_unbounded_Nfsstat4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Nfsstat4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Nfsstat4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Nfsstat4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Nfsstat4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Nfsstat4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Nfsstat4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Nfsstat4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Nfsstat4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Nfsstat4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Nfsstat4) XdrTypeName() string { return \"Nfsstat4<>\" }\nfunc (v *_XdrVec_unbounded_Nfsstat4) XdrPointer() interface{} { return (*[]Nfsstat4)(v) }\nfunc (v _XdrVec_unbounded_Nfsstat4) XdrValue() interface{} { return ([]Nfsstat4)(v) }\nfunc (v *_XdrVec_unbounded_Nfsstat4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_TEST_STATEID4resok = *TEST_STATEID4resok\nfunc (v *TEST_STATEID4resok) XdrPointer() interface{} { return v }\nfunc (TEST_STATEID4resok) XdrTypeName() string { return \"TEST_STATEID4resok\" }\nfunc (v TEST_STATEID4resok) XdrValue() interface{} { return v }\nfunc (v *TEST_STATEID4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *TEST_STATEID4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%stsr_status_codes\", name), (*_XdrVec_unbounded_Nfsstat4)(&v.Tsr_status_codes))\n}\nfunc XDR_TEST_STATEID4resok(v *TEST_STATEID4resok) *TEST_STATEID4resok { return v }\nfunc (u *TEST_STATEID4res) Tsr_resok4() *TEST_STATEID4resok {\n\tswitch u.Tsr_status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*TEST_STATEID4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero TEST_STATEID4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"TEST_STATEID4res.Tsr_resok4 accessed when Tsr_status == %v\", u.Tsr_status)\n\t\treturn nil\n\t}\n}\nfunc (u TEST_STATEID4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *TEST_STATEID4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Tsr_status)\n}\nfunc (u *TEST_STATEID4res) XdrUnionTagName() string {\n\treturn \"Tsr_status\"\n}\nfunc (u *TEST_STATEID4res) XdrUnionBody() XdrType {\n\tswitch u.Tsr_status {\n\tcase NFS4_OK:\n\t\treturn XDR_TEST_STATEID4resok(u.Tsr_resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *TEST_STATEID4res) XdrUnionBodyName() string {\n\tswitch u.Tsr_status {\n\tcase NFS4_OK:\n\t\treturn \"Tsr_resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_TEST_STATEID4res = *TEST_STATEID4res\nfunc (v *TEST_STATEID4res) XdrPointer() interface{} { return v }\nfunc (TEST_STATEID4res) XdrTypeName() string { return \"TEST_STATEID4res\" }\nfunc (v TEST_STATEID4res) XdrValue() interface{} { return v }\nfunc (v *TEST_STATEID4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *TEST_STATEID4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Tsr_status).XdrMarshal(x, x.Sprintf(\"%stsr_status\", name))\n\tswitch u.Tsr_status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%stsr_resok4\", name), XDR_TEST_STATEID4resok(u.Tsr_resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_TEST_STATEID4res(v *TEST_STATEID4res) *TEST_STATEID4res { return v}\nvar _XdrTags_Deleg_claim4 = map[int32]bool{\n\tXdrToI32(CLAIM_FH): true,\n\tXdrToI32(CLAIM_DELEG_PREV_FH): true,\n\tXdrToI32(CLAIM_PREVIOUS): true,\n}\nfunc (_ Deleg_claim4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Deleg_claim4\n}\nfunc (u *Deleg_claim4) Dc_delegate_type() *Open_delegation_type4 {\n\tswitch u.Dc_claim {\n\tcase CLAIM_PREVIOUS:\n\t\tif v, ok := u.U.(*Open_delegation_type4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Open_delegation_type4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Deleg_claim4.Dc_delegate_type accessed when Dc_claim == %v\", u.Dc_claim)\n\t\treturn nil\n\t}\n}\nfunc (u Deleg_claim4) XdrValid() bool {\n\tswitch u.Dc_claim {\n\tcase CLAIM_FH,CLAIM_DELEG_PREV_FH,CLAIM_PREVIOUS:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Deleg_claim4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Open_claim_type4(&u.Dc_claim)\n}\nfunc (u *Deleg_claim4) XdrUnionTagName() string {\n\treturn \"Dc_claim\"\n}\nfunc (u *Deleg_claim4) XdrUnionBody() XdrType {\n\tswitch u.Dc_claim {\n\tcase CLAIM_FH:\n\t\treturn nil\n\tcase CLAIM_DELEG_PREV_FH:\n\t\treturn nil\n\tcase CLAIM_PREVIOUS:\n\t\treturn XDR_Open_delegation_type4(u.Dc_delegate_type())\n\t}\n\treturn nil\n}\nfunc (u *Deleg_claim4) XdrUnionBodyName() string {\n\tswitch u.Dc_claim {\n\tcase CLAIM_FH:\n\t\treturn \"\"\n\tcase CLAIM_DELEG_PREV_FH:\n\t\treturn \"\"\n\tcase CLAIM_PREVIOUS:\n\t\treturn \"Dc_delegate_type\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Deleg_claim4 = *Deleg_claim4\nfunc (v *Deleg_claim4) XdrPointer() interface{} { return v }\nfunc (Deleg_claim4) XdrTypeName() string { return \"Deleg_claim4\" }\nfunc (v Deleg_claim4) XdrValue() interface{} { return v }\nfunc (v *Deleg_claim4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Deleg_claim4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Open_claim_type4(&u.Dc_claim).XdrMarshal(x, x.Sprintf(\"%sdc_claim\", name))\n\tswitch u.Dc_claim {\n\tcase CLAIM_FH:\n\t\treturn\n\tcase CLAIM_DELEG_PREV_FH:\n\t\treturn\n\tcase CLAIM_PREVIOUS:\n\t\tx.Marshal(x.Sprintf(\"%sdc_delegate_type\", name), XDR_Open_delegation_type4(u.Dc_delegate_type()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Dc_claim (%v) in Deleg_claim4\", u.Dc_claim)\n}\nfunc (v *Deleg_claim4) XdrInitialize() {\n\tvar zero Open_claim_type4\n\tswitch zero {\n\tcase CLAIM_FH, CLAIM_DELEG_PREV_FH, CLAIM_PREVIOUS:\n\tdefault:\n\t\tif v.Dc_claim == zero { v.Dc_claim = CLAIM_FH }\n\t}\n}\nfunc XDR_Deleg_claim4(v *Deleg_claim4) *Deleg_claim4 { return v}\ntype XdrType_WANT_DELEGATION4args = *WANT_DELEGATION4args\nfunc (v *WANT_DELEGATION4args) XdrPointer() interface{} { return v }\nfunc (WANT_DELEGATION4args) XdrTypeName() string { return \"WANT_DELEGATION4args\" }\nfunc (v WANT_DELEGATION4args) XdrValue() interface{} { return v }\nfunc (v *WANT_DELEGATION4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *WANT_DELEGATION4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%swda_want\", name), XDR_Uint32_t(&v.Wda_want))\n\tx.Marshal(x.Sprintf(\"%swda_claim\", name), XDR_Deleg_claim4(&v.Wda_claim))\n}\nfunc XDR_WANT_DELEGATION4args(v *WANT_DELEGATION4args) *WANT_DELEGATION4args { return v }\nfunc (u *WANT_DELEGATION4res) Wdr_resok4() *Open_delegation4 {\n\tswitch u.Wdr_status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*Open_delegation4); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Open_delegation4\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"WANT_DELEGATION4res.Wdr_resok4 accessed when Wdr_status == %v\", u.Wdr_status)\n\t\treturn nil\n\t}\n}\nfunc (u WANT_DELEGATION4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *WANT_DELEGATION4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Wdr_status)\n}\nfunc (u *WANT_DELEGATION4res) XdrUnionTagName() string {\n\treturn \"Wdr_status\"\n}\nfunc (u *WANT_DELEGATION4res) XdrUnionBody() XdrType {\n\tswitch u.Wdr_status {\n\tcase NFS4_OK:\n\t\treturn XDR_Open_delegation4(u.Wdr_resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *WANT_DELEGATION4res) XdrUnionBodyName() string {\n\tswitch u.Wdr_status {\n\tcase NFS4_OK:\n\t\treturn \"Wdr_resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_WANT_DELEGATION4res = *WANT_DELEGATION4res\nfunc (v *WANT_DELEGATION4res) XdrPointer() interface{} { return v }\nfunc (WANT_DELEGATION4res) XdrTypeName() string { return \"WANT_DELEGATION4res\" }\nfunc (v WANT_DELEGATION4res) XdrValue() interface{} { return v }\nfunc (v *WANT_DELEGATION4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *WANT_DELEGATION4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Wdr_status).XdrMarshal(x, x.Sprintf(\"%swdr_status\", name))\n\tswitch u.Wdr_status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%swdr_resok4\", name), XDR_Open_delegation4(u.Wdr_resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_WANT_DELEGATION4res(v *WANT_DELEGATION4res) *WANT_DELEGATION4res { return v}\ntype XdrType_DESTROY_CLIENTID4args = *DESTROY_CLIENTID4args\nfunc (v *DESTROY_CLIENTID4args) XdrPointer() interface{} { return v }\nfunc (DESTROY_CLIENTID4args) XdrTypeName() string { return \"DESTROY_CLIENTID4args\" }\nfunc (v DESTROY_CLIENTID4args) XdrValue() interface{} { return v }\nfunc (v *DESTROY_CLIENTID4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *DESTROY_CLIENTID4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sdca_clientid\", name), XDR_Clientid4(&v.Dca_clientid))\n}\nfunc XDR_DESTROY_CLIENTID4args(v *DESTROY_CLIENTID4args) *DESTROY_CLIENTID4args { return v }\ntype XdrType_DESTROY_CLIENTID4res = *DESTROY_CLIENTID4res\nfunc (v *DESTROY_CLIENTID4res) XdrPointer() interface{} { return v }\nfunc (DESTROY_CLIENTID4res) XdrTypeName() string { return \"DESTROY_CLIENTID4res\" }\nfunc (v DESTROY_CLIENTID4res) XdrValue() interface{} { return v }\nfunc (v *DESTROY_CLIENTID4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *DESTROY_CLIENTID4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sdcr_status\", name), XDR_Nfsstat4(&v.Dcr_status))\n}\nfunc XDR_DESTROY_CLIENTID4res(v *DESTROY_CLIENTID4res) *DESTROY_CLIENTID4res { return v }\ntype XdrType_RECLAIM_COMPLETE4args = *RECLAIM_COMPLETE4args\nfunc (v *RECLAIM_COMPLETE4args) XdrPointer() interface{} { return v }\nfunc (RECLAIM_COMPLETE4args) XdrTypeName() string { return \"RECLAIM_COMPLETE4args\" }\nfunc (v RECLAIM_COMPLETE4args) XdrValue() interface{} { return v }\nfunc (v *RECLAIM_COMPLETE4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *RECLAIM_COMPLETE4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%srca_one_fs\", name), XDR_bool(&v.Rca_one_fs))\n}\nfunc XDR_RECLAIM_COMPLETE4args(v *RECLAIM_COMPLETE4args) *RECLAIM_COMPLETE4args { return v }\ntype XdrType_RECLAIM_COMPLETE4res = *RECLAIM_COMPLETE4res\nfunc (v *RECLAIM_COMPLETE4res) XdrPointer() interface{} { return v }\nfunc (RECLAIM_COMPLETE4res) XdrTypeName() string { return \"RECLAIM_COMPLETE4res\" }\nfunc (v RECLAIM_COMPLETE4res) XdrValue() interface{} { return v }\nfunc (v *RECLAIM_COMPLETE4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *RECLAIM_COMPLETE4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%srcr_status\", name), XDR_Nfsstat4(&v.Rcr_status))\n}\nfunc XDR_RECLAIM_COMPLETE4res(v *RECLAIM_COMPLETE4res) *RECLAIM_COMPLETE4res { return v }\ntype XdrType_ILLEGAL4res = *ILLEGAL4res\nfunc (v *ILLEGAL4res) XdrPointer() interface{} { return v }\nfunc (ILLEGAL4res) XdrTypeName() string { return \"ILLEGAL4res\" }\nfunc (v ILLEGAL4res) XdrValue() interface{} { return v }\nfunc (v *ILLEGAL4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *ILLEGAL4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_ILLEGAL4res(v *ILLEGAL4res) *ILLEGAL4res { return v }\nvar _XdrNames_Nfs_opnum4 = map[int32]string{\n\tint32(OP_ACCESS): \"OP_ACCESS\",\n\tint32(OP_CLOSE): \"OP_CLOSE\",\n\tint32(OP_COMMIT): \"OP_COMMIT\",\n\tint32(OP_CREATE): \"OP_CREATE\",\n\tint32(OP_DELEGPURGE): \"OP_DELEGPURGE\",\n\tint32(OP_DELEGRETURN): \"OP_DELEGRETURN\",\n\tint32(OP_GETATTR): \"OP_GETATTR\",\n\tint32(OP_GETFH): \"OP_GETFH\",\n\tint32(OP_LINK): \"OP_LINK\",\n\tint32(OP_LOCK): \"OP_LOCK\",\n\tint32(OP_LOCKT): \"OP_LOCKT\",\n\tint32(OP_LOCKU): \"OP_LOCKU\",\n\tint32(OP_LOOKUP): \"OP_LOOKUP\",\n\tint32(OP_LOOKUPP): \"OP_LOOKUPP\",\n\tint32(OP_NVERIFY): \"OP_NVERIFY\",\n\tint32(OP_OPEN): \"OP_OPEN\",\n\tint32(OP_OPENATTR): \"OP_OPENATTR\",\n\tint32(OP_OPEN_CONFIRM): \"OP_OPEN_CONFIRM\",\n\tint32(OP_OPEN_DOWNGRADE): \"OP_OPEN_DOWNGRADE\",\n\tint32(OP_PUTFH): \"OP_PUTFH\",\n\tint32(OP_PUTPUBFH): \"OP_PUTPUBFH\",\n\tint32(OP_PUTROOTFH): \"OP_PUTROOTFH\",\n\tint32(OP_READ): \"OP_READ\",\n\tint32(OP_READDIR): \"OP_READDIR\",\n\tint32(OP_READLINK): \"OP_READLINK\",\n\tint32(OP_REMOVE): \"OP_REMOVE\",\n\tint32(OP_RENAME): \"OP_RENAME\",\n\tint32(OP_RENEW): \"OP_RENEW\",\n\tint32(OP_RESTOREFH): \"OP_RESTOREFH\",\n\tint32(OP_SAVEFH): \"OP_SAVEFH\",\n\tint32(OP_SECINFO): \"OP_SECINFO\",\n\tint32(OP_SETATTR): \"OP_SETATTR\",\n\tint32(OP_SETCLIENTID): \"OP_SETCLIENTID\",\n\tint32(OP_SETCLIENTID_CONFIRM): \"OP_SETCLIENTID_CONFIRM\",\n\tint32(OP_VERIFY): \"OP_VERIFY\",\n\tint32(OP_WRITE): \"OP_WRITE\",\n\tint32(OP_RELEASE_LOCKOWNER): \"OP_RELEASE_LOCKOWNER\",\n\tint32(OP_CREATE_SESSION): \"OP_CREATE_SESSION\",\n\tint32(OP_DESTROY_SESSION): \"OP_DESTROY_SESSION\",\n\tint32(OP_FREE_STATEID): \"OP_FREE_STATEID\",\n\tint32(OP_GET_DIR_DELEGATION): \"OP_GET_DIR_DELEGATION\",\n\tint32(OP_GETDEVICEINFO): \"OP_GETDEVICEINFO\",\n\tint32(OP_GETDEVICELIST): \"OP_GETDEVICELIST\",\n\tint32(OP_LAYOUTCOMMIT): \"OP_LAYOUTCOMMIT\",\n\tint32(OP_LAYOUTGET): \"OP_LAYOUTGET\",\n\tint32(OP_LAYOUTRETURN): \"OP_LAYOUTRETURN\",\n\tint32(OP_SECINFO_NO_NAME): \"OP_SECINFO_NO_NAME\",\n\tint32(OP_SEQUENCE): \"OP_SEQUENCE\",\n\tint32(OP_SET_SSV): \"OP_SET_SSV\",\n\tint32(OP_TEST_STATEID): \"OP_TEST_STATEID\",\n\tint32(OP_WANT_DELEGATION): \"OP_WANT_DELEGATION\",\n\tint32(OP_DESTROY_CLIENTID): \"OP_DESTROY_CLIENTID\",\n\tint32(OP_RECLAIM_COMPLETE): \"OP_RECLAIM_COMPLETE\",\n\tint32(OP_ILLEGAL): \"OP_ILLEGAL\",\n}\nvar _XdrValues_Nfs_opnum4 = map[string]int32{\n\t\"OP_ACCESS\": int32(OP_ACCESS),\n\t\"OP_CLOSE\": int32(OP_CLOSE),\n\t\"OP_COMMIT\": int32(OP_COMMIT),\n\t\"OP_CREATE\": int32(OP_CREATE),\n\t\"OP_DELEGPURGE\": int32(OP_DELEGPURGE),\n\t\"OP_DELEGRETURN\": int32(OP_DELEGRETURN),\n\t\"OP_GETATTR\": int32(OP_GETATTR),\n\t\"OP_GETFH\": int32(OP_GETFH),\n\t\"OP_LINK\": int32(OP_LINK),\n\t\"OP_LOCK\": int32(OP_LOCK),\n\t\"OP_LOCKT\": int32(OP_LOCKT),\n\t\"OP_LOCKU\": int32(OP_LOCKU),\n\t\"OP_LOOKUP\": int32(OP_LOOKUP),\n\t\"OP_LOOKUPP\": int32(OP_LOOKUPP),\n\t\"OP_NVERIFY\": int32(OP_NVERIFY),\n\t\"OP_OPEN\": int32(OP_OPEN),\n\t\"OP_OPENATTR\": int32(OP_OPENATTR),\n\t\"OP_OPEN_CONFIRM\": int32(OP_OPEN_CONFIRM),\n\t\"OP_OPEN_DOWNGRADE\": int32(OP_OPEN_DOWNGRADE),\n\t\"OP_PUTFH\": int32(OP_PUTFH),\n\t\"OP_PUTPUBFH\": int32(OP_PUTPUBFH),\n\t\"OP_PUTROOTFH\": int32(OP_PUTROOTFH),\n\t\"OP_READ\": int32(OP_READ),\n\t\"OP_READDIR\": int32(OP_READDIR),\n\t\"OP_READLINK\": int32(OP_READLINK),\n\t\"OP_REMOVE\": int32(OP_REMOVE),\n\t\"OP_RENAME\": int32(OP_RENAME),\n\t\"OP_RENEW\": int32(OP_RENEW),\n\t\"OP_RESTOREFH\": int32(OP_RESTOREFH),\n\t\"OP_SAVEFH\": int32(OP_SAVEFH),\n\t\"OP_SECINFO\": int32(OP_SECINFO),\n\t\"OP_SETATTR\": int32(OP_SETATTR),\n\t\"OP_SETCLIENTID\": int32(OP_SETCLIENTID),\n\t\"OP_SETCLIENTID_CONFIRM\": int32(OP_SETCLIENTID_CONFIRM),\n\t\"OP_VERIFY\": int32(OP_VERIFY),\n\t\"OP_WRITE\": int32(OP_WRITE),\n\t\"OP_RELEASE_LOCKOWNER\": int32(OP_RELEASE_LOCKOWNER),\n\t\"OP_CREATE_SESSION\": int32(OP_CREATE_SESSION),\n\t\"OP_DESTROY_SESSION\": int32(OP_DESTROY_SESSION),\n\t\"OP_FREE_STATEID\": int32(OP_FREE_STATEID),\n\t\"OP_GET_DIR_DELEGATION\": int32(OP_GET_DIR_DELEGATION),\n\t\"OP_GETDEVICEINFO\": int32(OP_GETDEVICEINFO),\n\t\"OP_GETDEVICELIST\": int32(OP_GETDEVICELIST),\n\t\"OP_LAYOUTCOMMIT\": int32(OP_LAYOUTCOMMIT),\n\t\"OP_LAYOUTGET\": int32(OP_LAYOUTGET),\n\t\"OP_LAYOUTRETURN\": int32(OP_LAYOUTRETURN),\n\t\"OP_SECINFO_NO_NAME\": int32(OP_SECINFO_NO_NAME),\n\t\"OP_SEQUENCE\": int32(OP_SEQUENCE),\n\t\"OP_SET_SSV\": int32(OP_SET_SSV),\n\t\"OP_TEST_STATEID\": int32(OP_TEST_STATEID),\n\t\"OP_WANT_DELEGATION\": int32(OP_WANT_DELEGATION),\n\t\"OP_DESTROY_CLIENTID\": int32(OP_DESTROY_CLIENTID),\n\t\"OP_RECLAIM_COMPLETE\": int32(OP_RECLAIM_COMPLETE),\n\t\"OP_ILLEGAL\": int32(OP_ILLEGAL),\n}\nfunc (Nfs_opnum4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Nfs_opnum4\n}\nfunc (v Nfs_opnum4) String() string {\n\tif s, ok := _XdrNames_Nfs_opnum4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Nfs_opnum4#%d\", v)\n}\nfunc (v *Nfs_opnum4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Nfs_opnum4[stok]; ok {\n\t\t\t*v = Nfs_opnum4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Nfs_opnum4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Nfs_opnum4.\", stok))\n\t}\n}\nfunc (v Nfs_opnum4) GetU32() uint32 { return uint32(v) }\nfunc (v *Nfs_opnum4) SetU32(n uint32) { *v = Nfs_opnum4(n) }\nfunc (v *Nfs_opnum4) XdrPointer() interface{} { return v }\nfunc (Nfs_opnum4) XdrTypeName() string { return \"Nfs_opnum4\" }\nfunc (v Nfs_opnum4) XdrValue() interface{} { return v }\nfunc (v *Nfs_opnum4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Nfs_opnum4 = *Nfs_opnum4\nfunc XDR_Nfs_opnum4(v *Nfs_opnum4) *Nfs_opnum4 { return v }\nfunc (v *Nfs_opnum4) XdrInitialize() {\n\tswitch Nfs_opnum4(0) {\n\tcase OP_ACCESS, OP_CLOSE, OP_COMMIT, OP_CREATE, OP_DELEGPURGE, OP_DELEGRETURN, OP_GETATTR, OP_GETFH, OP_LINK, OP_LOCK, OP_LOCKT, OP_LOCKU, OP_LOOKUP, OP_LOOKUPP, OP_NVERIFY, OP_OPEN, OP_OPENATTR, OP_OPEN_CONFIRM, OP_OPEN_DOWNGRADE, OP_PUTFH, OP_PUTPUBFH, OP_PUTROOTFH, OP_READ, OP_READDIR, OP_READLINK, OP_REMOVE, OP_RENAME, OP_RENEW, OP_RESTOREFH, OP_SAVEFH, OP_SECINFO, OP_SETATTR, OP_SETCLIENTID, OP_SETCLIENTID_CONFIRM, OP_VERIFY, OP_WRITE, OP_RELEASE_LOCKOWNER, OP_CREATE_SESSION, OP_DESTROY_SESSION, OP_FREE_STATEID, OP_GET_DIR_DELEGATION, OP_GETDEVICEINFO, OP_GETDEVICELIST, OP_LAYOUTCOMMIT, OP_LAYOUTGET, OP_LAYOUTRETURN, OP_SECINFO_NO_NAME, OP_SEQUENCE, OP_SET_SSV, OP_TEST_STATEID, OP_WANT_DELEGATION, OP_DESTROY_CLIENTID, OP_RECLAIM_COMPLETE, OP_ILLEGAL:\n\tdefault:\n\t\tif *v == Nfs_opnum4(0) { *v = OP_ACCESS }\n\t}\n}\nvar _XdrTags_Nfs_argop4 = map[int32]bool{\n\tXdrToI32(OP_ACCESS): true,\n\tXdrToI32(OP_CLOSE): true,\n\tXdrToI32(OP_COMMIT): true,\n\tXdrToI32(OP_CREATE): true,\n\tXdrToI32(OP_DELEGPURGE): true,\n\tXdrToI32(OP_DELEGRETURN): true,\n\tXdrToI32(OP_GETATTR): true,\n\tXdrToI32(OP_GETFH): true,\n\tXdrToI32(OP_LINK): true,\n\tXdrToI32(OP_LOCK): true,\n\tXdrToI32(OP_LOCKT): true,\n\tXdrToI32(OP_LOCKU): true,\n\tXdrToI32(OP_LOOKUP): true,\n\tXdrToI32(OP_LOOKUPP): true,\n\tXdrToI32(OP_NVERIFY): true,\n\tXdrToI32(OP_OPEN): true,\n\tXdrToI32(OP_OPENATTR): true,\n\tXdrToI32(OP_OPEN_CONFIRM): true,\n\tXdrToI32(OP_OPEN_DOWNGRADE): true,\n\tXdrToI32(OP_PUTFH): true,\n\tXdrToI32(OP_PUTPUBFH): true,\n\tXdrToI32(OP_PUTROOTFH): true,\n\tXdrToI32(OP_READ): true,\n\tXdrToI32(OP_READDIR): true,\n\tXdrToI32(OP_READLINK): true,\n\tXdrToI32(OP_REMOVE): true,\n\tXdrToI32(OP_RENAME): true,\n\tXdrToI32(OP_RENEW): true,\n\tXdrToI32(OP_RESTOREFH): true,\n\tXdrToI32(OP_SAVEFH): true,\n\tXdrToI32(OP_SECINFO): true,\n\tXdrToI32(OP_SETATTR): true,\n\tXdrToI32(OP_SETCLIENTID): true,\n\tXdrToI32(OP_SETCLIENTID_CONFIRM): true,\n\tXdrToI32(OP_VERIFY): true,\n\tXdrToI32(OP_WRITE): true,\n\tXdrToI32(OP_RELEASE_LOCKOWNER): true,\n\tXdrToI32(OP_CREATE_SESSION): true,\n\tXdrToI32(OP_DESTROY_SESSION): true,\n\tXdrToI32(OP_FREE_STATEID): true,\n\tXdrToI32(OP_GET_DIR_DELEGATION): true,\n\tXdrToI32(OP_GETDEVICEINFO): true,\n\tXdrToI32(OP_GETDEVICELIST): true,\n\tXdrToI32(OP_LAYOUTCOMMIT): true,\n\tXdrToI32(OP_LAYOUTGET): true,\n\tXdrToI32(OP_LAYOUTRETURN): true,\n\tXdrToI32(OP_SECINFO_NO_NAME): true,\n\tXdrToI32(OP_SEQUENCE): true,\n\tXdrToI32(OP_SET_SSV): true,\n\tXdrToI32(OP_TEST_STATEID): true,\n\tXdrToI32(OP_WANT_DELEGATION): true,\n\tXdrToI32(OP_DESTROY_CLIENTID): true,\n\tXdrToI32(OP_RECLAIM_COMPLETE): true,\n\tXdrToI32(OP_ILLEGAL): true,\n}\nfunc (_ Nfs_argop4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Nfs_argop4\n}\nfunc (u *Nfs_argop4) Opaccess() *ACCESS4args {\n\tswitch u.Argop {\n\tcase OP_ACCESS:\n\t\tif v, ok := u.U.(*ACCESS4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero ACCESS4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opaccess accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opclose() *CLOSE4args {\n\tswitch u.Argop {\n\tcase OP_CLOSE:\n\t\tif v, ok := u.U.(*CLOSE4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CLOSE4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opclose accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opcommit() *COMMIT4args {\n\tswitch u.Argop {\n\tcase OP_COMMIT:\n\t\tif v, ok := u.U.(*COMMIT4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero COMMIT4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opcommit accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opcreate() *CREATE4args {\n\tswitch u.Argop {\n\tcase OP_CREATE:\n\t\tif v, ok := u.U.(*CREATE4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CREATE4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opcreate accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opdelegpurge() *DELEGPURGE4args {\n\tswitch u.Argop {\n\tcase OP_DELEGPURGE:\n\t\tif v, ok := u.U.(*DELEGPURGE4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero DELEGPURGE4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opdelegpurge accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opdelegreturn() *DELEGRETURN4args {\n\tswitch u.Argop {\n\tcase OP_DELEGRETURN:\n\t\tif v, ok := u.U.(*DELEGRETURN4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero DELEGRETURN4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opdelegreturn accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opgetattr() *GETATTR4args {\n\tswitch u.Argop {\n\tcase OP_GETATTR:\n\t\tif v, ok := u.U.(*GETATTR4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GETATTR4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opgetattr accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Oplink() *LINK4args {\n\tswitch u.Argop {\n\tcase OP_LINK:\n\t\tif v, ok := u.U.(*LINK4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LINK4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Oplink accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Oplock() *LOCK4args {\n\tswitch u.Argop {\n\tcase OP_LOCK:\n\t\tif v, ok := u.U.(*LOCK4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LOCK4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Oplock accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Oplockt() *LOCKT4args {\n\tswitch u.Argop {\n\tcase OP_LOCKT:\n\t\tif v, ok := u.U.(*LOCKT4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LOCKT4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Oplockt accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Oplocku() *LOCKU4args {\n\tswitch u.Argop {\n\tcase OP_LOCKU:\n\t\tif v, ok := u.U.(*LOCKU4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LOCKU4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Oplocku accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Oplookup() *LOOKUP4args {\n\tswitch u.Argop {\n\tcase OP_LOOKUP:\n\t\tif v, ok := u.U.(*LOOKUP4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LOOKUP4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Oplookup accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opnverify() *NVERIFY4args {\n\tswitch u.Argop {\n\tcase OP_NVERIFY:\n\t\tif v, ok := u.U.(*NVERIFY4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero NVERIFY4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opnverify accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opopen() *OPEN4args {\n\tswitch u.Argop {\n\tcase OP_OPEN:\n\t\tif v, ok := u.U.(*OPEN4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero OPEN4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opopen accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opopenattr() *OPENATTR4args {\n\tswitch u.Argop {\n\tcase OP_OPENATTR:\n\t\tif v, ok := u.U.(*OPENATTR4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero OPENATTR4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opopenattr accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opopen_confirm() *OPEN_CONFIRM4args {\n\tswitch u.Argop {\n\tcase OP_OPEN_CONFIRM:\n\t\tif v, ok := u.U.(*OPEN_CONFIRM4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero OPEN_CONFIRM4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opopen_confirm accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opopen_downgrade() *OPEN_DOWNGRADE4args {\n\tswitch u.Argop {\n\tcase OP_OPEN_DOWNGRADE:\n\t\tif v, ok := u.U.(*OPEN_DOWNGRADE4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero OPEN_DOWNGRADE4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opopen_downgrade accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opputfh() *PUTFH4args {\n\tswitch u.Argop {\n\tcase OP_PUTFH:\n\t\tif v, ok := u.U.(*PUTFH4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero PUTFH4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opputfh accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opread() *READ4args {\n\tswitch u.Argop {\n\tcase OP_READ:\n\t\tif v, ok := u.U.(*READ4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero READ4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opread accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opreaddir() *READDIR4args {\n\tswitch u.Argop {\n\tcase OP_READDIR:\n\t\tif v, ok := u.U.(*READDIR4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero READDIR4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opreaddir accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opremove() *REMOVE4args {\n\tswitch u.Argop {\n\tcase OP_REMOVE:\n\t\tif v, ok := u.U.(*REMOVE4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero REMOVE4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opremove accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Oprename() *RENAME4args {\n\tswitch u.Argop {\n\tcase OP_RENAME:\n\t\tif v, ok := u.U.(*RENAME4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero RENAME4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Oprename accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Oprenew() *RENEW4args {\n\tswitch u.Argop {\n\tcase OP_RENEW:\n\t\tif v, ok := u.U.(*RENEW4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero RENEW4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Oprenew accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opsecinfo() *SECINFO4args {\n\tswitch u.Argop {\n\tcase OP_SECINFO:\n\t\tif v, ok := u.U.(*SECINFO4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SECINFO4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opsecinfo accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opsetattr() *SETATTR4args {\n\tswitch u.Argop {\n\tcase OP_SETATTR:\n\t\tif v, ok := u.U.(*SETATTR4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SETATTR4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opsetattr accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opsetclientid() *SETCLIENTID4args {\n\tswitch u.Argop {\n\tcase OP_SETCLIENTID:\n\t\tif v, ok := u.U.(*SETCLIENTID4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SETCLIENTID4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opsetclientid accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opsetclientid_confirm() *SETCLIENTID_CONFIRM4args {\n\tswitch u.Argop {\n\tcase OP_SETCLIENTID_CONFIRM:\n\t\tif v, ok := u.U.(*SETCLIENTID_CONFIRM4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SETCLIENTID_CONFIRM4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opsetclientid_confirm accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opverify() *VERIFY4args {\n\tswitch u.Argop {\n\tcase OP_VERIFY:\n\t\tif v, ok := u.U.(*VERIFY4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero VERIFY4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opverify accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opwrite() *WRITE4args {\n\tswitch u.Argop {\n\tcase OP_WRITE:\n\t\tif v, ok := u.U.(*WRITE4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero WRITE4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opwrite accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Oprelease_lockowner() *RELEASE_LOCKOWNER4args {\n\tswitch u.Argop {\n\tcase OP_RELEASE_LOCKOWNER:\n\t\tif v, ok := u.U.(*RELEASE_LOCKOWNER4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero RELEASE_LOCKOWNER4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Oprelease_lockowner accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opcreatesession() *CREATE_SESSION4args {\n\tswitch u.Argop {\n\tcase OP_CREATE_SESSION:\n\t\tif v, ok := u.U.(*CREATE_SESSION4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CREATE_SESSION4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opcreatesession accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opdestroysession() *DESTROY_SESSION4args {\n\tswitch u.Argop {\n\tcase OP_DESTROY_SESSION:\n\t\tif v, ok := u.U.(*DESTROY_SESSION4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero DESTROY_SESSION4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opdestroysession accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opfreestateid() *FREE_STATEID4args {\n\tswitch u.Argop {\n\tcase OP_FREE_STATEID:\n\t\tif v, ok := u.U.(*FREE_STATEID4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero FREE_STATEID4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opfreestateid accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opgetdirdelegation() *GET_DIR_DELEGATION4args {\n\tswitch u.Argop {\n\tcase OP_GET_DIR_DELEGATION:\n\t\tif v, ok := u.U.(*GET_DIR_DELEGATION4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GET_DIR_DELEGATION4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opgetdirdelegation accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opgetdeviceinfo() *GETDEVICEINFO4args {\n\tswitch u.Argop {\n\tcase OP_GETDEVICEINFO:\n\t\tif v, ok := u.U.(*GETDEVICEINFO4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GETDEVICEINFO4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opgetdeviceinfo accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opgetdevicelist() *GETDEVICELIST4args {\n\tswitch u.Argop {\n\tcase OP_GETDEVICELIST:\n\t\tif v, ok := u.U.(*GETDEVICELIST4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GETDEVICELIST4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opgetdevicelist accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Oplayoutcommit() *LAYOUTCOMMIT4args {\n\tswitch u.Argop {\n\tcase OP_LAYOUTCOMMIT:\n\t\tif v, ok := u.U.(*LAYOUTCOMMIT4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LAYOUTCOMMIT4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Oplayoutcommit accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Oplayoutget() *LAYOUTGET4args {\n\tswitch u.Argop {\n\tcase OP_LAYOUTGET:\n\t\tif v, ok := u.U.(*LAYOUTGET4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LAYOUTGET4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Oplayoutget accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Oplayoutreturn() *LAYOUTRETURN4args {\n\tswitch u.Argop {\n\tcase OP_LAYOUTRETURN:\n\t\tif v, ok := u.U.(*LAYOUTRETURN4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LAYOUTRETURN4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Oplayoutreturn accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opsecinfononame() *SECINFO_NO_NAME4args {\n\tswitch u.Argop {\n\tcase OP_SECINFO_NO_NAME:\n\t\tif v, ok := u.U.(*SECINFO_NO_NAME4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SECINFO_NO_NAME4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opsecinfononame accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opsequence() *SEQUENCE4args {\n\tswitch u.Argop {\n\tcase OP_SEQUENCE:\n\t\tif v, ok := u.U.(*SEQUENCE4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SEQUENCE4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opsequence accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opsetssv() *SET_SSV4args {\n\tswitch u.Argop {\n\tcase OP_SET_SSV:\n\t\tif v, ok := u.U.(*SET_SSV4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SET_SSV4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opsetssv accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opteststateid() *TEST_STATEID4args {\n\tswitch u.Argop {\n\tcase OP_TEST_STATEID:\n\t\tif v, ok := u.U.(*TEST_STATEID4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero TEST_STATEID4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opteststateid accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opwantdelegation() *WANT_DELEGATION4args {\n\tswitch u.Argop {\n\tcase OP_WANT_DELEGATION:\n\t\tif v, ok := u.U.(*WANT_DELEGATION4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero WANT_DELEGATION4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opwantdelegation accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opdestroyclientid() *DESTROY_CLIENTID4args {\n\tswitch u.Argop {\n\tcase OP_DESTROY_CLIENTID:\n\t\tif v, ok := u.U.(*DESTROY_CLIENTID4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero DESTROY_CLIENTID4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opdestroyclientid accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_argop4) Opreclaimcomplete() *RECLAIM_COMPLETE4args {\n\tswitch u.Argop {\n\tcase OP_RECLAIM_COMPLETE:\n\t\tif v, ok := u.U.(*RECLAIM_COMPLETE4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero RECLAIM_COMPLETE4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_argop4.Opreclaimcomplete accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u Nfs_argop4) XdrValid() bool {\n\tswitch u.Argop {\n\tcase OP_ACCESS,OP_CLOSE,OP_COMMIT,OP_CREATE,OP_DELEGPURGE,OP_DELEGRETURN,OP_GETATTR,OP_GETFH,OP_LINK,OP_LOCK,OP_LOCKT,OP_LOCKU,OP_LOOKUP,OP_LOOKUPP,OP_NVERIFY,OP_OPEN,OP_OPENATTR,OP_OPEN_CONFIRM,OP_OPEN_DOWNGRADE,OP_PUTFH,OP_PUTPUBFH,OP_PUTROOTFH,OP_READ,OP_READDIR,OP_READLINK,OP_REMOVE,OP_RENAME,OP_RENEW,OP_RESTOREFH,OP_SAVEFH,OP_SECINFO,OP_SETATTR,OP_SETCLIENTID,OP_SETCLIENTID_CONFIRM,OP_VERIFY,OP_WRITE,OP_RELEASE_LOCKOWNER,OP_CREATE_SESSION,OP_DESTROY_SESSION,OP_FREE_STATEID,OP_GET_DIR_DELEGATION,OP_GETDEVICEINFO,OP_GETDEVICELIST,OP_LAYOUTCOMMIT,OP_LAYOUTGET,OP_LAYOUTRETURN,OP_SECINFO_NO_NAME,OP_SEQUENCE,OP_SET_SSV,OP_TEST_STATEID,OP_WANT_DELEGATION,OP_DESTROY_CLIENTID,OP_RECLAIM_COMPLETE,OP_ILLEGAL:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Nfs_argop4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfs_opnum4(&u.Argop)\n}\nfunc (u *Nfs_argop4) XdrUnionTagName() string {\n\treturn \"Argop\"\n}\nfunc (u *Nfs_argop4) XdrUnionBody() XdrType {\n\tswitch u.Argop {\n\tcase OP_ACCESS:\n\t\treturn XDR_ACCESS4args(u.Opaccess())\n\tcase OP_CLOSE:\n\t\treturn XDR_CLOSE4args(u.Opclose())\n\tcase OP_COMMIT:\n\t\treturn XDR_COMMIT4args(u.Opcommit())\n\tcase OP_CREATE:\n\t\treturn XDR_CREATE4args(u.Opcreate())\n\tcase OP_DELEGPURGE:\n\t\treturn XDR_DELEGPURGE4args(u.Opdelegpurge())\n\tcase OP_DELEGRETURN:\n\t\treturn XDR_DELEGRETURN4args(u.Opdelegreturn())\n\tcase OP_GETATTR:\n\t\treturn XDR_GETATTR4args(u.Opgetattr())\n\tcase OP_GETFH:\n\t\treturn nil\n\tcase OP_LINK:\n\t\treturn XDR_LINK4args(u.Oplink())\n\tcase OP_LOCK:\n\t\treturn XDR_LOCK4args(u.Oplock())\n\tcase OP_LOCKT:\n\t\treturn XDR_LOCKT4args(u.Oplockt())\n\tcase OP_LOCKU:\n\t\treturn XDR_LOCKU4args(u.Oplocku())\n\tcase OP_LOOKUP:\n\t\treturn XDR_LOOKUP4args(u.Oplookup())\n\tcase OP_LOOKUPP:\n\t\treturn nil\n\tcase OP_NVERIFY:\n\t\treturn XDR_NVERIFY4args(u.Opnverify())\n\tcase OP_OPEN:\n\t\treturn XDR_OPEN4args(u.Opopen())\n\tcase OP_OPENATTR:\n\t\treturn XDR_OPENATTR4args(u.Opopenattr())\n\tcase OP_OPEN_CONFIRM:\n\t\treturn XDR_OPEN_CONFIRM4args(u.Opopen_confirm())\n\tcase OP_OPEN_DOWNGRADE:\n\t\treturn XDR_OPEN_DOWNGRADE4args(u.Opopen_downgrade())\n\tcase OP_PUTFH:\n\t\treturn XDR_PUTFH4args(u.Opputfh())\n\tcase OP_PUTPUBFH:\n\t\treturn nil\n\tcase OP_PUTROOTFH:\n\t\treturn nil\n\tcase OP_READ:\n\t\treturn XDR_READ4args(u.Opread())\n\tcase OP_READDIR:\n\t\treturn XDR_READDIR4args(u.Opreaddir())\n\tcase OP_READLINK:\n\t\treturn nil\n\tcase OP_REMOVE:\n\t\treturn XDR_REMOVE4args(u.Opremove())\n\tcase OP_RENAME:\n\t\treturn XDR_RENAME4args(u.Oprename())\n\tcase OP_RENEW:\n\t\treturn XDR_RENEW4args(u.Oprenew())\n\tcase OP_RESTOREFH:\n\t\treturn nil\n\tcase OP_SAVEFH:\n\t\treturn nil\n\tcase OP_SECINFO:\n\t\treturn XDR_SECINFO4args(u.Opsecinfo())\n\tcase OP_SETATTR:\n\t\treturn XDR_SETATTR4args(u.Opsetattr())\n\tcase OP_SETCLIENTID:\n\t\treturn XDR_SETCLIENTID4args(u.Opsetclientid())\n\tcase OP_SETCLIENTID_CONFIRM:\n\t\treturn XDR_SETCLIENTID_CONFIRM4args(u.Opsetclientid_confirm())\n\tcase OP_VERIFY:\n\t\treturn XDR_VERIFY4args(u.Opverify())\n\tcase OP_WRITE:\n\t\treturn XDR_WRITE4args(u.Opwrite())\n\tcase OP_RELEASE_LOCKOWNER:\n\t\treturn XDR_RELEASE_LOCKOWNER4args(u.Oprelease_lockowner())\n\tcase OP_CREATE_SESSION:\n\t\treturn XDR_CREATE_SESSION4args(u.Opcreatesession())\n\tcase OP_DESTROY_SESSION:\n\t\treturn XDR_DESTROY_SESSION4args(u.Opdestroysession())\n\tcase OP_FREE_STATEID:\n\t\treturn XDR_FREE_STATEID4args(u.Opfreestateid())\n\tcase OP_GET_DIR_DELEGATION:\n\t\treturn XDR_GET_DIR_DELEGATION4args(u.Opgetdirdelegation())\n\tcase OP_GETDEVICEINFO:\n\t\treturn XDR_GETDEVICEINFO4args(u.Opgetdeviceinfo())\n\tcase OP_GETDEVICELIST:\n\t\treturn XDR_GETDEVICELIST4args(u.Opgetdevicelist())\n\tcase OP_LAYOUTCOMMIT:\n\t\treturn XDR_LAYOUTCOMMIT4args(u.Oplayoutcommit())\n\tcase OP_LAYOUTGET:\n\t\treturn XDR_LAYOUTGET4args(u.Oplayoutget())\n\tcase OP_LAYOUTRETURN:\n\t\treturn XDR_LAYOUTRETURN4args(u.Oplayoutreturn())\n\tcase OP_SECINFO_NO_NAME:\n\t\treturn XDR_SECINFO_NO_NAME4args(u.Opsecinfononame())\n\tcase OP_SEQUENCE:\n\t\treturn XDR_SEQUENCE4args(u.Opsequence())\n\tcase OP_SET_SSV:\n\t\treturn XDR_SET_SSV4args(u.Opsetssv())\n\tcase OP_TEST_STATEID:\n\t\treturn XDR_TEST_STATEID4args(u.Opteststateid())\n\tcase OP_WANT_DELEGATION:\n\t\treturn XDR_WANT_DELEGATION4args(u.Opwantdelegation())\n\tcase OP_DESTROY_CLIENTID:\n\t\treturn XDR_DESTROY_CLIENTID4args(u.Opdestroyclientid())\n\tcase OP_RECLAIM_COMPLETE:\n\t\treturn XDR_RECLAIM_COMPLETE4args(u.Opreclaimcomplete())\n\tcase OP_ILLEGAL:\n\t\treturn nil\n\t}\n\treturn nil\n}\nfunc (u *Nfs_argop4) XdrUnionBodyName() string {\n\tswitch u.Argop {\n\tcase OP_ACCESS:\n\t\treturn \"Opaccess\"\n\tcase OP_CLOSE:\n\t\treturn \"Opclose\"\n\tcase OP_COMMIT:\n\t\treturn \"Opcommit\"\n\tcase OP_CREATE:\n\t\treturn \"Opcreate\"\n\tcase OP_DELEGPURGE:\n\t\treturn \"Opdelegpurge\"\n\tcase OP_DELEGRETURN:\n\t\treturn \"Opdelegreturn\"\n\tcase OP_GETATTR:\n\t\treturn \"Opgetattr\"\n\tcase OP_GETFH:\n\t\treturn \"\"\n\tcase OP_LINK:\n\t\treturn \"Oplink\"\n\tcase OP_LOCK:\n\t\treturn \"Oplock\"\n\tcase OP_LOCKT:\n\t\treturn \"Oplockt\"\n\tcase OP_LOCKU:\n\t\treturn \"Oplocku\"\n\tcase OP_LOOKUP:\n\t\treturn \"Oplookup\"\n\tcase OP_LOOKUPP:\n\t\treturn \"\"\n\tcase OP_NVERIFY:\n\t\treturn \"Opnverify\"\n\tcase OP_OPEN:\n\t\treturn \"Opopen\"\n\tcase OP_OPENATTR:\n\t\treturn \"Opopenattr\"\n\tcase OP_OPEN_CONFIRM:\n\t\treturn \"Opopen_confirm\"\n\tcase OP_OPEN_DOWNGRADE:\n\t\treturn \"Opopen_downgrade\"\n\tcase OP_PUTFH:\n\t\treturn \"Opputfh\"\n\tcase OP_PUTPUBFH:\n\t\treturn \"\"\n\tcase OP_PUTROOTFH:\n\t\treturn \"\"\n\tcase OP_READ:\n\t\treturn \"Opread\"\n\tcase OP_READDIR:\n\t\treturn \"Opreaddir\"\n\tcase OP_READLINK:\n\t\treturn \"\"\n\tcase OP_REMOVE:\n\t\treturn \"Opremove\"\n\tcase OP_RENAME:\n\t\treturn \"Oprename\"\n\tcase OP_RENEW:\n\t\treturn \"Oprenew\"\n\tcase OP_RESTOREFH:\n\t\treturn \"\"\n\tcase OP_SAVEFH:\n\t\treturn \"\"\n\tcase OP_SECINFO:\n\t\treturn \"Opsecinfo\"\n\tcase OP_SETATTR:\n\t\treturn \"Opsetattr\"\n\tcase OP_SETCLIENTID:\n\t\treturn \"Opsetclientid\"\n\tcase OP_SETCLIENTID_CONFIRM:\n\t\treturn \"Opsetclientid_confirm\"\n\tcase OP_VERIFY:\n\t\treturn \"Opverify\"\n\tcase OP_WRITE:\n\t\treturn \"Opwrite\"\n\tcase OP_RELEASE_LOCKOWNER:\n\t\treturn \"Oprelease_lockowner\"\n\tcase OP_CREATE_SESSION:\n\t\treturn \"Opcreatesession\"\n\tcase OP_DESTROY_SESSION:\n\t\treturn \"Opdestroysession\"\n\tcase OP_FREE_STATEID:\n\t\treturn \"Opfreestateid\"\n\tcase OP_GET_DIR_DELEGATION:\n\t\treturn \"Opgetdirdelegation\"\n\tcase OP_GETDEVICEINFO:\n\t\treturn \"Opgetdeviceinfo\"\n\tcase OP_GETDEVICELIST:\n\t\treturn \"Opgetdevicelist\"\n\tcase OP_LAYOUTCOMMIT:\n\t\treturn \"Oplayoutcommit\"\n\tcase OP_LAYOUTGET:\n\t\treturn \"Oplayoutget\"\n\tcase OP_LAYOUTRETURN:\n\t\treturn \"Oplayoutreturn\"\n\tcase OP_SECINFO_NO_NAME:\n\t\treturn \"Opsecinfononame\"\n\tcase OP_SEQUENCE:\n\t\treturn \"Opsequence\"\n\tcase OP_SET_SSV:\n\t\treturn \"Opsetssv\"\n\tcase OP_TEST_STATEID:\n\t\treturn \"Opteststateid\"\n\tcase OP_WANT_DELEGATION:\n\t\treturn \"Opwantdelegation\"\n\tcase OP_DESTROY_CLIENTID:\n\t\treturn \"Opdestroyclientid\"\n\tcase OP_RECLAIM_COMPLETE:\n\t\treturn \"Opreclaimcomplete\"\n\tcase OP_ILLEGAL:\n\t\treturn \"\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Nfs_argop4 = *Nfs_argop4\nfunc (v *Nfs_argop4) XdrPointer() interface{} { return v }\nfunc (Nfs_argop4) XdrTypeName() string { return \"Nfs_argop4\" }\nfunc (v Nfs_argop4) XdrValue() interface{} { return v }\nfunc (v *Nfs_argop4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Nfs_argop4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfs_opnum4(&u.Argop).XdrMarshal(x, x.Sprintf(\"%sargop\", name))\n\tswitch u.Argop {\n\tcase OP_ACCESS:\n\t\tx.Marshal(x.Sprintf(\"%sopaccess\", name), XDR_ACCESS4args(u.Opaccess()))\n\t\treturn\n\tcase OP_CLOSE:\n\t\tx.Marshal(x.Sprintf(\"%sopclose\", name), XDR_CLOSE4args(u.Opclose()))\n\t\treturn\n\tcase OP_COMMIT:\n\t\tx.Marshal(x.Sprintf(\"%sopcommit\", name), XDR_COMMIT4args(u.Opcommit()))\n\t\treturn\n\tcase OP_CREATE:\n\t\tx.Marshal(x.Sprintf(\"%sopcreate\", name), XDR_CREATE4args(u.Opcreate()))\n\t\treturn\n\tcase OP_DELEGPURGE:\n\t\tx.Marshal(x.Sprintf(\"%sopdelegpurge\", name), XDR_DELEGPURGE4args(u.Opdelegpurge()))\n\t\treturn\n\tcase OP_DELEGRETURN:\n\t\tx.Marshal(x.Sprintf(\"%sopdelegreturn\", name), XDR_DELEGRETURN4args(u.Opdelegreturn()))\n\t\treturn\n\tcase OP_GETATTR:\n\t\tx.Marshal(x.Sprintf(\"%sopgetattr\", name), XDR_GETATTR4args(u.Opgetattr()))\n\t\treturn\n\tcase OP_GETFH:\n\t\treturn\n\tcase OP_LINK:\n\t\tx.Marshal(x.Sprintf(\"%soplink\", name), XDR_LINK4args(u.Oplink()))\n\t\treturn\n\tcase OP_LOCK:\n\t\tx.Marshal(x.Sprintf(\"%soplock\", name), XDR_LOCK4args(u.Oplock()))\n\t\treturn\n\tcase OP_LOCKT:\n\t\tx.Marshal(x.Sprintf(\"%soplockt\", name), XDR_LOCKT4args(u.Oplockt()))\n\t\treturn\n\tcase OP_LOCKU:\n\t\tx.Marshal(x.Sprintf(\"%soplocku\", name), XDR_LOCKU4args(u.Oplocku()))\n\t\treturn\n\tcase OP_LOOKUP:\n\t\tx.Marshal(x.Sprintf(\"%soplookup\", name), XDR_LOOKUP4args(u.Oplookup()))\n\t\treturn\n\tcase OP_LOOKUPP:\n\t\treturn\n\tcase OP_NVERIFY:\n\t\tx.Marshal(x.Sprintf(\"%sopnverify\", name), XDR_NVERIFY4args(u.Opnverify()))\n\t\treturn\n\tcase OP_OPEN:\n\t\tx.Marshal(x.Sprintf(\"%sopopen\", name), XDR_OPEN4args(u.Opopen()))\n\t\treturn\n\tcase OP_OPENATTR:\n\t\tx.Marshal(x.Sprintf(\"%sopopenattr\", name), XDR_OPENATTR4args(u.Opopenattr()))\n\t\treturn\n\tcase OP_OPEN_CONFIRM:\n\t\tx.Marshal(x.Sprintf(\"%sopopen_confirm\", name), XDR_OPEN_CONFIRM4args(u.Opopen_confirm()))\n\t\treturn\n\tcase OP_OPEN_DOWNGRADE:\n\t\tx.Marshal(x.Sprintf(\"%sopopen_downgrade\", name), XDR_OPEN_DOWNGRADE4args(u.Opopen_downgrade()))\n\t\treturn\n\tcase OP_PUTFH:\n\t\tx.Marshal(x.Sprintf(\"%sopputfh\", name), XDR_PUTFH4args(u.Opputfh()))\n\t\treturn\n\tcase OP_PUTPUBFH:\n\t\treturn\n\tcase OP_PUTROOTFH:\n\t\treturn\n\tcase OP_READ:\n\t\tx.Marshal(x.Sprintf(\"%sopread\", name), XDR_READ4args(u.Opread()))\n\t\treturn\n\tcase OP_READDIR:\n\t\tx.Marshal(x.Sprintf(\"%sopreaddir\", name), XDR_READDIR4args(u.Opreaddir()))\n\t\treturn\n\tcase OP_READLINK:\n\t\treturn\n\tcase OP_REMOVE:\n\t\tx.Marshal(x.Sprintf(\"%sopremove\", name), XDR_REMOVE4args(u.Opremove()))\n\t\treturn\n\tcase OP_RENAME:\n\t\tx.Marshal(x.Sprintf(\"%soprename\", name), XDR_RENAME4args(u.Oprename()))\n\t\treturn\n\tcase OP_RENEW:\n\t\tx.Marshal(x.Sprintf(\"%soprenew\", name), XDR_RENEW4args(u.Oprenew()))\n\t\treturn\n\tcase OP_RESTOREFH:\n\t\treturn\n\tcase OP_SAVEFH:\n\t\treturn\n\tcase OP_SECINFO:\n\t\tx.Marshal(x.Sprintf(\"%sopsecinfo\", name), XDR_SECINFO4args(u.Opsecinfo()))\n\t\treturn\n\tcase OP_SETATTR:\n\t\tx.Marshal(x.Sprintf(\"%sopsetattr\", name), XDR_SETATTR4args(u.Opsetattr()))\n\t\treturn\n\tcase OP_SETCLIENTID:\n\t\tx.Marshal(x.Sprintf(\"%sopsetclientid\", name), XDR_SETCLIENTID4args(u.Opsetclientid()))\n\t\treturn\n\tcase OP_SETCLIENTID_CONFIRM:\n\t\tx.Marshal(x.Sprintf(\"%sopsetclientid_confirm\", name), XDR_SETCLIENTID_CONFIRM4args(u.Opsetclientid_confirm()))\n\t\treturn\n\tcase OP_VERIFY:\n\t\tx.Marshal(x.Sprintf(\"%sopverify\", name), XDR_VERIFY4args(u.Opverify()))\n\t\treturn\n\tcase OP_WRITE:\n\t\tx.Marshal(x.Sprintf(\"%sopwrite\", name), XDR_WRITE4args(u.Opwrite()))\n\t\treturn\n\tcase OP_RELEASE_LOCKOWNER:\n\t\tx.Marshal(x.Sprintf(\"%soprelease_lockowner\", name), XDR_RELEASE_LOCKOWNER4args(u.Oprelease_lockowner()))\n\t\treturn\n\tcase OP_CREATE_SESSION:\n\t\tx.Marshal(x.Sprintf(\"%sopcreatesession\", name), XDR_CREATE_SESSION4args(u.Opcreatesession()))\n\t\treturn\n\tcase OP_DESTROY_SESSION:\n\t\tx.Marshal(x.Sprintf(\"%sopdestroysession\", name), XDR_DESTROY_SESSION4args(u.Opdestroysession()))\n\t\treturn\n\tcase OP_FREE_STATEID:\n\t\tx.Marshal(x.Sprintf(\"%sopfreestateid\", name), XDR_FREE_STATEID4args(u.Opfreestateid()))\n\t\treturn\n\tcase OP_GET_DIR_DELEGATION:\n\t\tx.Marshal(x.Sprintf(\"%sopgetdirdelegation\", name), XDR_GET_DIR_DELEGATION4args(u.Opgetdirdelegation()))\n\t\treturn\n\tcase OP_GETDEVICEINFO:\n\t\tx.Marshal(x.Sprintf(\"%sopgetdeviceinfo\", name), XDR_GETDEVICEINFO4args(u.Opgetdeviceinfo()))\n\t\treturn\n\tcase OP_GETDEVICELIST:\n\t\tx.Marshal(x.Sprintf(\"%sopgetdevicelist\", name), XDR_GETDEVICELIST4args(u.Opgetdevicelist()))\n\t\treturn\n\tcase OP_LAYOUTCOMMIT:\n\t\tx.Marshal(x.Sprintf(\"%soplayoutcommit\", name), XDR_LAYOUTCOMMIT4args(u.Oplayoutcommit()))\n\t\treturn\n\tcase OP_LAYOUTGET:\n\t\tx.Marshal(x.Sprintf(\"%soplayoutget\", name), XDR_LAYOUTGET4args(u.Oplayoutget()))\n\t\treturn\n\tcase OP_LAYOUTRETURN:\n\t\tx.Marshal(x.Sprintf(\"%soplayoutreturn\", name), XDR_LAYOUTRETURN4args(u.Oplayoutreturn()))\n\t\treturn\n\tcase OP_SECINFO_NO_NAME:\n\t\tx.Marshal(x.Sprintf(\"%sopsecinfononame\", name), XDR_SECINFO_NO_NAME4args(u.Opsecinfononame()))\n\t\treturn\n\tcase OP_SEQUENCE:\n\t\tx.Marshal(x.Sprintf(\"%sopsequence\", name), XDR_SEQUENCE4args(u.Opsequence()))\n\t\treturn\n\tcase OP_SET_SSV:\n\t\tx.Marshal(x.Sprintf(\"%sopsetssv\", name), XDR_SET_SSV4args(u.Opsetssv()))\n\t\treturn\n\tcase OP_TEST_STATEID:\n\t\tx.Marshal(x.Sprintf(\"%sopteststateid\", name), XDR_TEST_STATEID4args(u.Opteststateid()))\n\t\treturn\n\tcase OP_WANT_DELEGATION:\n\t\tx.Marshal(x.Sprintf(\"%sopwantdelegation\", name), XDR_WANT_DELEGATION4args(u.Opwantdelegation()))\n\t\treturn\n\tcase OP_DESTROY_CLIENTID:\n\t\tx.Marshal(x.Sprintf(\"%sopdestroyclientid\", name), XDR_DESTROY_CLIENTID4args(u.Opdestroyclientid()))\n\t\treturn\n\tcase OP_RECLAIM_COMPLETE:\n\t\tx.Marshal(x.Sprintf(\"%sopreclaimcomplete\", name), XDR_RECLAIM_COMPLETE4args(u.Opreclaimcomplete()))\n\t\treturn\n\tcase OP_ILLEGAL:\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Argop (%v) in Nfs_argop4\", u.Argop)\n}\nfunc (v *Nfs_argop4) XdrInitialize() {\n\tvar zero Nfs_opnum4\n\tswitch zero {\n\tcase OP_ACCESS, OP_CLOSE, OP_COMMIT, OP_CREATE, OP_DELEGPURGE, OP_DELEGRETURN, OP_GETATTR, OP_GETFH, OP_LINK, OP_LOCK, OP_LOCKT, OP_LOCKU, OP_LOOKUP, OP_LOOKUPP, OP_NVERIFY, OP_OPEN, OP_OPENATTR, OP_OPEN_CONFIRM, OP_OPEN_DOWNGRADE, OP_PUTFH, OP_PUTPUBFH, OP_PUTROOTFH, OP_READ, OP_READDIR, OP_READLINK, OP_REMOVE, OP_RENAME, OP_RENEW, OP_RESTOREFH, OP_SAVEFH, OP_SECINFO, OP_SETATTR, OP_SETCLIENTID, OP_SETCLIENTID_CONFIRM, OP_VERIFY, OP_WRITE, OP_RELEASE_LOCKOWNER, OP_CREATE_SESSION, OP_DESTROY_SESSION, OP_FREE_STATEID, OP_GET_DIR_DELEGATION, OP_GETDEVICEINFO, OP_GETDEVICELIST, OP_LAYOUTCOMMIT, OP_LAYOUTGET, OP_LAYOUTRETURN, OP_SECINFO_NO_NAME, OP_SEQUENCE, OP_SET_SSV, OP_TEST_STATEID, OP_WANT_DELEGATION, OP_DESTROY_CLIENTID, OP_RECLAIM_COMPLETE, OP_ILLEGAL:\n\tdefault:\n\t\tif v.Argop == zero { v.Argop = OP_ACCESS }\n\t}\n}\nfunc XDR_Nfs_argop4(v *Nfs_argop4) *Nfs_argop4 { return v}\nvar _XdrTags_Nfs_resop4 = map[int32]bool{\n\tXdrToI32(OP_ACCESS): true,\n\tXdrToI32(OP_CLOSE): true,\n\tXdrToI32(OP_COMMIT): true,\n\tXdrToI32(OP_CREATE): true,\n\tXdrToI32(OP_DELEGPURGE): true,\n\tXdrToI32(OP_DELEGRETURN): true,\n\tXdrToI32(OP_GETATTR): true,\n\tXdrToI32(OP_GETFH): true,\n\tXdrToI32(OP_LINK): true,\n\tXdrToI32(OP_LOCK): true,\n\tXdrToI32(OP_LOCKT): true,\n\tXdrToI32(OP_LOCKU): true,\n\tXdrToI32(OP_LOOKUP): true,\n\tXdrToI32(OP_LOOKUPP): true,\n\tXdrToI32(OP_NVERIFY): true,\n\tXdrToI32(OP_OPEN): true,\n\tXdrToI32(OP_OPENATTR): true,\n\tXdrToI32(OP_OPEN_CONFIRM): true,\n\tXdrToI32(OP_OPEN_DOWNGRADE): true,\n\tXdrToI32(OP_PUTFH): true,\n\tXdrToI32(OP_PUTPUBFH): true,\n\tXdrToI32(OP_PUTROOTFH): true,\n\tXdrToI32(OP_READ): true,\n\tXdrToI32(OP_READDIR): true,\n\tXdrToI32(OP_READLINK): true,\n\tXdrToI32(OP_REMOVE): true,\n\tXdrToI32(OP_RENAME): true,\n\tXdrToI32(OP_RENEW): true,\n\tXdrToI32(OP_RESTOREFH): true,\n\tXdrToI32(OP_SAVEFH): true,\n\tXdrToI32(OP_SECINFO): true,\n\tXdrToI32(OP_SETATTR): true,\n\tXdrToI32(OP_SETCLIENTID): true,\n\tXdrToI32(OP_SETCLIENTID_CONFIRM): true,\n\tXdrToI32(OP_VERIFY): true,\n\tXdrToI32(OP_WRITE): true,\n\tXdrToI32(OP_RELEASE_LOCKOWNER): true,\n\tXdrToI32(OP_CREATE_SESSION): true,\n\tXdrToI32(OP_DESTROY_SESSION): true,\n\tXdrToI32(OP_FREE_STATEID): true,\n\tXdrToI32(OP_GET_DIR_DELEGATION): true,\n\tXdrToI32(OP_GETDEVICEINFO): true,\n\tXdrToI32(OP_GETDEVICELIST): true,\n\tXdrToI32(OP_LAYOUTCOMMIT): true,\n\tXdrToI32(OP_LAYOUTGET): true,\n\tXdrToI32(OP_LAYOUTRETURN): true,\n\tXdrToI32(OP_SECINFO_NO_NAME): true,\n\tXdrToI32(OP_SEQUENCE): true,\n\tXdrToI32(OP_SET_SSV): true,\n\tXdrToI32(OP_TEST_STATEID): true,\n\tXdrToI32(OP_WANT_DELEGATION): true,\n\tXdrToI32(OP_DESTROY_CLIENTID): true,\n\tXdrToI32(OP_RECLAIM_COMPLETE): true,\n\tXdrToI32(OP_ILLEGAL): true,\n}\nfunc (_ Nfs_resop4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Nfs_resop4\n}\nfunc (u *Nfs_resop4) Opaccess() *ACCESS4res {\n\tswitch u.Resop {\n\tcase OP_ACCESS:\n\t\tif v, ok := u.U.(*ACCESS4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero ACCESS4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opaccess accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opclose() *CLOSE4res {\n\tswitch u.Resop {\n\tcase OP_CLOSE:\n\t\tif v, ok := u.U.(*CLOSE4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CLOSE4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opclose accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opcommit() *COMMIT4res {\n\tswitch u.Resop {\n\tcase OP_COMMIT:\n\t\tif v, ok := u.U.(*COMMIT4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero COMMIT4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opcommit accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opcreate() *CREATE4res {\n\tswitch u.Resop {\n\tcase OP_CREATE:\n\t\tif v, ok := u.U.(*CREATE4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CREATE4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opcreate accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opdelegpurge() *DELEGPURGE4res {\n\tswitch u.Resop {\n\tcase OP_DELEGPURGE:\n\t\tif v, ok := u.U.(*DELEGPURGE4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero DELEGPURGE4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opdelegpurge accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opdelegreturn() *DELEGRETURN4res {\n\tswitch u.Resop {\n\tcase OP_DELEGRETURN:\n\t\tif v, ok := u.U.(*DELEGRETURN4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero DELEGRETURN4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opdelegreturn accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opgetattr() *GETATTR4res {\n\tswitch u.Resop {\n\tcase OP_GETATTR:\n\t\tif v, ok := u.U.(*GETATTR4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GETATTR4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opgetattr accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opgetfh() *GETFH4res {\n\tswitch u.Resop {\n\tcase OP_GETFH:\n\t\tif v, ok := u.U.(*GETFH4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GETFH4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opgetfh accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oplink() *LINK4res {\n\tswitch u.Resop {\n\tcase OP_LINK:\n\t\tif v, ok := u.U.(*LINK4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LINK4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oplink accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oplock() *LOCK4res {\n\tswitch u.Resop {\n\tcase OP_LOCK:\n\t\tif v, ok := u.U.(*LOCK4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LOCK4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oplock accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oplockt() *LOCKT4res {\n\tswitch u.Resop {\n\tcase OP_LOCKT:\n\t\tif v, ok := u.U.(*LOCKT4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LOCKT4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oplockt accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oplocku() *LOCKU4res {\n\tswitch u.Resop {\n\tcase OP_LOCKU:\n\t\tif v, ok := u.U.(*LOCKU4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LOCKU4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oplocku accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oplookup() *LOOKUP4res {\n\tswitch u.Resop {\n\tcase OP_LOOKUP:\n\t\tif v, ok := u.U.(*LOOKUP4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LOOKUP4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oplookup accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oplookupp() *LOOKUPP4res {\n\tswitch u.Resop {\n\tcase OP_LOOKUPP:\n\t\tif v, ok := u.U.(*LOOKUPP4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LOOKUPP4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oplookupp accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opnverify() *NVERIFY4res {\n\tswitch u.Resop {\n\tcase OP_NVERIFY:\n\t\tif v, ok := u.U.(*NVERIFY4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero NVERIFY4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opnverify accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opopen() *OPEN4res {\n\tswitch u.Resop {\n\tcase OP_OPEN:\n\t\tif v, ok := u.U.(*OPEN4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero OPEN4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opopen accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opopenattr() *OPENATTR4res {\n\tswitch u.Resop {\n\tcase OP_OPENATTR:\n\t\tif v, ok := u.U.(*OPENATTR4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero OPENATTR4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opopenattr accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opopen_confirm() *OPEN_CONFIRM4res {\n\tswitch u.Resop {\n\tcase OP_OPEN_CONFIRM:\n\t\tif v, ok := u.U.(*OPEN_CONFIRM4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero OPEN_CONFIRM4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opopen_confirm accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opopen_downgrade() *OPEN_DOWNGRADE4res {\n\tswitch u.Resop {\n\tcase OP_OPEN_DOWNGRADE:\n\t\tif v, ok := u.U.(*OPEN_DOWNGRADE4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero OPEN_DOWNGRADE4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opopen_downgrade accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opputfh() *PUTFH4res {\n\tswitch u.Resop {\n\tcase OP_PUTFH:\n\t\tif v, ok := u.U.(*PUTFH4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero PUTFH4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opputfh accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opputpubfh() *PUTPUBFH4res {\n\tswitch u.Resop {\n\tcase OP_PUTPUBFH:\n\t\tif v, ok := u.U.(*PUTPUBFH4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero PUTPUBFH4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opputpubfh accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opputrootfh() *PUTROOTFH4res {\n\tswitch u.Resop {\n\tcase OP_PUTROOTFH:\n\t\tif v, ok := u.U.(*PUTROOTFH4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero PUTROOTFH4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opputrootfh accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opread() *READ4res {\n\tswitch u.Resop {\n\tcase OP_READ:\n\t\tif v, ok := u.U.(*READ4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero READ4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opread accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opreaddir() *READDIR4res {\n\tswitch u.Resop {\n\tcase OP_READDIR:\n\t\tif v, ok := u.U.(*READDIR4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero READDIR4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opreaddir accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opreadlink() *READLINK4res {\n\tswitch u.Resop {\n\tcase OP_READLINK:\n\t\tif v, ok := u.U.(*READLINK4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero READLINK4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opreadlink accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opremove() *REMOVE4res {\n\tswitch u.Resop {\n\tcase OP_REMOVE:\n\t\tif v, ok := u.U.(*REMOVE4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero REMOVE4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opremove accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oprename() *RENAME4res {\n\tswitch u.Resop {\n\tcase OP_RENAME:\n\t\tif v, ok := u.U.(*RENAME4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero RENAME4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oprename accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oprenew() *RENEW4res {\n\tswitch u.Resop {\n\tcase OP_RENEW:\n\t\tif v, ok := u.U.(*RENEW4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero RENEW4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oprenew accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oprestorefh() *RESTOREFH4res {\n\tswitch u.Resop {\n\tcase OP_RESTOREFH:\n\t\tif v, ok := u.U.(*RESTOREFH4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero RESTOREFH4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oprestorefh accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opsavefh() *SAVEFH4res {\n\tswitch u.Resop {\n\tcase OP_SAVEFH:\n\t\tif v, ok := u.U.(*SAVEFH4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SAVEFH4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opsavefh accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opsecinfo() *SECINFO4res {\n\tswitch u.Resop {\n\tcase OP_SECINFO:\n\t\tif v, ok := u.U.(*SECINFO4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SECINFO4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opsecinfo accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opsetattr() *SETATTR4res {\n\tswitch u.Resop {\n\tcase OP_SETATTR:\n\t\tif v, ok := u.U.(*SETATTR4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SETATTR4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opsetattr accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opsetclientid() *SETCLIENTID4res {\n\tswitch u.Resop {\n\tcase OP_SETCLIENTID:\n\t\tif v, ok := u.U.(*SETCLIENTID4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SETCLIENTID4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opsetclientid accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opsetclientid_confirm() *SETCLIENTID_CONFIRM4res {\n\tswitch u.Resop {\n\tcase OP_SETCLIENTID_CONFIRM:\n\t\tif v, ok := u.U.(*SETCLIENTID_CONFIRM4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SETCLIENTID_CONFIRM4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opsetclientid_confirm accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opverify() *VERIFY4res {\n\tswitch u.Resop {\n\tcase OP_VERIFY:\n\t\tif v, ok := u.U.(*VERIFY4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero VERIFY4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opverify accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opwrite() *WRITE4res {\n\tswitch u.Resop {\n\tcase OP_WRITE:\n\t\tif v, ok := u.U.(*WRITE4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero WRITE4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opwrite accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oprelease_lockowner() *RELEASE_LOCKOWNER4res {\n\tswitch u.Resop {\n\tcase OP_RELEASE_LOCKOWNER:\n\t\tif v, ok := u.U.(*RELEASE_LOCKOWNER4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero RELEASE_LOCKOWNER4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oprelease_lockowner accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opcreatesession() *CREATE_SESSION4res {\n\tswitch u.Resop {\n\tcase OP_CREATE_SESSION:\n\t\tif v, ok := u.U.(*CREATE_SESSION4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CREATE_SESSION4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opcreatesession accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opdestroysession() *DESTROY_SESSION4res {\n\tswitch u.Resop {\n\tcase OP_DESTROY_SESSION:\n\t\tif v, ok := u.U.(*DESTROY_SESSION4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero DESTROY_SESSION4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opdestroysession accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opfreestateid() *FREE_STATEID4res {\n\tswitch u.Resop {\n\tcase OP_FREE_STATEID:\n\t\tif v, ok := u.U.(*FREE_STATEID4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero FREE_STATEID4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opfreestateid accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opgetdirdelegation() *GET_DIR_DELEGATION4res {\n\tswitch u.Resop {\n\tcase OP_GET_DIR_DELEGATION:\n\t\tif v, ok := u.U.(*GET_DIR_DELEGATION4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GET_DIR_DELEGATION4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opgetdirdelegation accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opgetdeviceinfo() *GETDEVICEINFO4res {\n\tswitch u.Resop {\n\tcase OP_GETDEVICEINFO:\n\t\tif v, ok := u.U.(*GETDEVICEINFO4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GETDEVICEINFO4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opgetdeviceinfo accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opgetdevicelist() *GETDEVICELIST4res {\n\tswitch u.Resop {\n\tcase OP_GETDEVICELIST:\n\t\tif v, ok := u.U.(*GETDEVICELIST4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero GETDEVICELIST4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opgetdevicelist accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oplayoutcommit() *LAYOUTCOMMIT4res {\n\tswitch u.Resop {\n\tcase OP_LAYOUTCOMMIT:\n\t\tif v, ok := u.U.(*LAYOUTCOMMIT4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LAYOUTCOMMIT4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oplayoutcommit accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oplayoutget() *LAYOUTGET4res {\n\tswitch u.Resop {\n\tcase OP_LAYOUTGET:\n\t\tif v, ok := u.U.(*LAYOUTGET4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LAYOUTGET4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oplayoutget accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Oplayoutreturn() *LAYOUTRETURN4res {\n\tswitch u.Resop {\n\tcase OP_LAYOUTRETURN:\n\t\tif v, ok := u.U.(*LAYOUTRETURN4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero LAYOUTRETURN4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Oplayoutreturn accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opsecinfononame() *SECINFO_NO_NAME4res {\n\tswitch u.Resop {\n\tcase OP_SECINFO_NO_NAME:\n\t\tif v, ok := u.U.(*SECINFO_NO_NAME4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SECINFO_NO_NAME4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opsecinfononame accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opsequence() *SEQUENCE4res {\n\tswitch u.Resop {\n\tcase OP_SEQUENCE:\n\t\tif v, ok := u.U.(*SEQUENCE4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SEQUENCE4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opsequence accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opsetssv() *SET_SSV4res {\n\tswitch u.Resop {\n\tcase OP_SET_SSV:\n\t\tif v, ok := u.U.(*SET_SSV4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero SET_SSV4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opsetssv accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opteststateid() *TEST_STATEID4res {\n\tswitch u.Resop {\n\tcase OP_TEST_STATEID:\n\t\tif v, ok := u.U.(*TEST_STATEID4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero TEST_STATEID4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opteststateid accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opwantdelegation() *WANT_DELEGATION4res {\n\tswitch u.Resop {\n\tcase OP_WANT_DELEGATION:\n\t\tif v, ok := u.U.(*WANT_DELEGATION4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero WANT_DELEGATION4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opwantdelegation accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opdestroyclientid() *DESTROY_CLIENTID4res {\n\tswitch u.Resop {\n\tcase OP_DESTROY_CLIENTID:\n\t\tif v, ok := u.U.(*DESTROY_CLIENTID4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero DESTROY_CLIENTID4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opdestroyclientid accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opreclaimcomplete() *RECLAIM_COMPLETE4res {\n\tswitch u.Resop {\n\tcase OP_RECLAIM_COMPLETE:\n\t\tif v, ok := u.U.(*RECLAIM_COMPLETE4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero RECLAIM_COMPLETE4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opreclaimcomplete accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_resop4) Opillegal() *ILLEGAL4res {\n\tswitch u.Resop {\n\tcase OP_ILLEGAL:\n\t\tif v, ok := u.U.(*ILLEGAL4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero ILLEGAL4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_resop4.Opillegal accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u Nfs_resop4) XdrValid() bool {\n\tswitch u.Resop {\n\tcase OP_ACCESS,OP_CLOSE,OP_COMMIT,OP_CREATE,OP_DELEGPURGE,OP_DELEGRETURN,OP_GETATTR,OP_GETFH,OP_LINK,OP_LOCK,OP_LOCKT,OP_LOCKU,OP_LOOKUP,OP_LOOKUPP,OP_NVERIFY,OP_OPEN,OP_OPENATTR,OP_OPEN_CONFIRM,OP_OPEN_DOWNGRADE,OP_PUTFH,OP_PUTPUBFH,OP_PUTROOTFH,OP_READ,OP_READDIR,OP_READLINK,OP_REMOVE,OP_RENAME,OP_RENEW,OP_RESTOREFH,OP_SAVEFH,OP_SECINFO,OP_SETATTR,OP_SETCLIENTID,OP_SETCLIENTID_CONFIRM,OP_VERIFY,OP_WRITE,OP_RELEASE_LOCKOWNER,OP_CREATE_SESSION,OP_DESTROY_SESSION,OP_FREE_STATEID,OP_GET_DIR_DELEGATION,OP_GETDEVICEINFO,OP_GETDEVICELIST,OP_LAYOUTCOMMIT,OP_LAYOUTGET,OP_LAYOUTRETURN,OP_SECINFO_NO_NAME,OP_SEQUENCE,OP_SET_SSV,OP_TEST_STATEID,OP_WANT_DELEGATION,OP_DESTROY_CLIENTID,OP_RECLAIM_COMPLETE,OP_ILLEGAL:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Nfs_resop4) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfs_opnum4(&u.Resop)\n}\nfunc (u *Nfs_resop4) XdrUnionTagName() string {\n\treturn \"Resop\"\n}\nfunc (u *Nfs_resop4) XdrUnionBody() XdrType {\n\tswitch u.Resop {\n\tcase OP_ACCESS:\n\t\treturn XDR_ACCESS4res(u.Opaccess())\n\tcase OP_CLOSE:\n\t\treturn XDR_CLOSE4res(u.Opclose())\n\tcase OP_COMMIT:\n\t\treturn XDR_COMMIT4res(u.Opcommit())\n\tcase OP_CREATE:\n\t\treturn XDR_CREATE4res(u.Opcreate())\n\tcase OP_DELEGPURGE:\n\t\treturn XDR_DELEGPURGE4res(u.Opdelegpurge())\n\tcase OP_DELEGRETURN:\n\t\treturn XDR_DELEGRETURN4res(u.Opdelegreturn())\n\tcase OP_GETATTR:\n\t\treturn XDR_GETATTR4res(u.Opgetattr())\n\tcase OP_GETFH:\n\t\treturn XDR_GETFH4res(u.Opgetfh())\n\tcase OP_LINK:\n\t\treturn XDR_LINK4res(u.Oplink())\n\tcase OP_LOCK:\n\t\treturn XDR_LOCK4res(u.Oplock())\n\tcase OP_LOCKT:\n\t\treturn XDR_LOCKT4res(u.Oplockt())\n\tcase OP_LOCKU:\n\t\treturn XDR_LOCKU4res(u.Oplocku())\n\tcase OP_LOOKUP:\n\t\treturn XDR_LOOKUP4res(u.Oplookup())\n\tcase OP_LOOKUPP:\n\t\treturn XDR_LOOKUPP4res(u.Oplookupp())\n\tcase OP_NVERIFY:\n\t\treturn XDR_NVERIFY4res(u.Opnverify())\n\tcase OP_OPEN:\n\t\treturn XDR_OPEN4res(u.Opopen())\n\tcase OP_OPENATTR:\n\t\treturn XDR_OPENATTR4res(u.Opopenattr())\n\tcase OP_OPEN_CONFIRM:\n\t\treturn XDR_OPEN_CONFIRM4res(u.Opopen_confirm())\n\tcase OP_OPEN_DOWNGRADE:\n\t\treturn XDR_OPEN_DOWNGRADE4res(u.Opopen_downgrade())\n\tcase OP_PUTFH:\n\t\treturn XDR_PUTFH4res(u.Opputfh())\n\tcase OP_PUTPUBFH:\n\t\treturn XDR_PUTPUBFH4res(u.Opputpubfh())\n\tcase OP_PUTROOTFH:\n\t\treturn XDR_PUTROOTFH4res(u.Opputrootfh())\n\tcase OP_READ:\n\t\treturn XDR_READ4res(u.Opread())\n\tcase OP_READDIR:\n\t\treturn XDR_READDIR4res(u.Opreaddir())\n\tcase OP_READLINK:\n\t\treturn XDR_READLINK4res(u.Opreadlink())\n\tcase OP_REMOVE:\n\t\treturn XDR_REMOVE4res(u.Opremove())\n\tcase OP_RENAME:\n\t\treturn XDR_RENAME4res(u.Oprename())\n\tcase OP_RENEW:\n\t\treturn XDR_RENEW4res(u.Oprenew())\n\tcase OP_RESTOREFH:\n\t\treturn XDR_RESTOREFH4res(u.Oprestorefh())\n\tcase OP_SAVEFH:\n\t\treturn XDR_SAVEFH4res(u.Opsavefh())\n\tcase OP_SECINFO:\n\t\treturn XDR_SECINFO4res(u.Opsecinfo())\n\tcase OP_SETATTR:\n\t\treturn XDR_SETATTR4res(u.Opsetattr())\n\tcase OP_SETCLIENTID:\n\t\treturn XDR_SETCLIENTID4res(u.Opsetclientid())\n\tcase OP_SETCLIENTID_CONFIRM:\n\t\treturn XDR_SETCLIENTID_CONFIRM4res(u.Opsetclientid_confirm())\n\tcase OP_VERIFY:\n\t\treturn XDR_VERIFY4res(u.Opverify())\n\tcase OP_WRITE:\n\t\treturn XDR_WRITE4res(u.Opwrite())\n\tcase OP_RELEASE_LOCKOWNER:\n\t\treturn XDR_RELEASE_LOCKOWNER4res(u.Oprelease_lockowner())\n\tcase OP_CREATE_SESSION:\n\t\treturn XDR_CREATE_SESSION4res(u.Opcreatesession())\n\tcase OP_DESTROY_SESSION:\n\t\treturn XDR_DESTROY_SESSION4res(u.Opdestroysession())\n\tcase OP_FREE_STATEID:\n\t\treturn XDR_FREE_STATEID4res(u.Opfreestateid())\n\tcase OP_GET_DIR_DELEGATION:\n\t\treturn XDR_GET_DIR_DELEGATION4res(u.Opgetdirdelegation())\n\tcase OP_GETDEVICEINFO:\n\t\treturn XDR_GETDEVICEINFO4res(u.Opgetdeviceinfo())\n\tcase OP_GETDEVICELIST:\n\t\treturn XDR_GETDEVICELIST4res(u.Opgetdevicelist())\n\tcase OP_LAYOUTCOMMIT:\n\t\treturn XDR_LAYOUTCOMMIT4res(u.Oplayoutcommit())\n\tcase OP_LAYOUTGET:\n\t\treturn XDR_LAYOUTGET4res(u.Oplayoutget())\n\tcase OP_LAYOUTRETURN:\n\t\treturn XDR_LAYOUTRETURN4res(u.Oplayoutreturn())\n\tcase OP_SECINFO_NO_NAME:\n\t\treturn XDR_SECINFO_NO_NAME4res(u.Opsecinfononame())\n\tcase OP_SEQUENCE:\n\t\treturn XDR_SEQUENCE4res(u.Opsequence())\n\tcase OP_SET_SSV:\n\t\treturn XDR_SET_SSV4res(u.Opsetssv())\n\tcase OP_TEST_STATEID:\n\t\treturn XDR_TEST_STATEID4res(u.Opteststateid())\n\tcase OP_WANT_DELEGATION:\n\t\treturn XDR_WANT_DELEGATION4res(u.Opwantdelegation())\n\tcase OP_DESTROY_CLIENTID:\n\t\treturn XDR_DESTROY_CLIENTID4res(u.Opdestroyclientid())\n\tcase OP_RECLAIM_COMPLETE:\n\t\treturn XDR_RECLAIM_COMPLETE4res(u.Opreclaimcomplete())\n\tcase OP_ILLEGAL:\n\t\treturn XDR_ILLEGAL4res(u.Opillegal())\n\t}\n\treturn nil\n}\nfunc (u *Nfs_resop4) XdrUnionBodyName() string {\n\tswitch u.Resop {\n\tcase OP_ACCESS:\n\t\treturn \"Opaccess\"\n\tcase OP_CLOSE:\n\t\treturn \"Opclose\"\n\tcase OP_COMMIT:\n\t\treturn \"Opcommit\"\n\tcase OP_CREATE:\n\t\treturn \"Opcreate\"\n\tcase OP_DELEGPURGE:\n\t\treturn \"Opdelegpurge\"\n\tcase OP_DELEGRETURN:\n\t\treturn \"Opdelegreturn\"\n\tcase OP_GETATTR:\n\t\treturn \"Opgetattr\"\n\tcase OP_GETFH:\n\t\treturn \"Opgetfh\"\n\tcase OP_LINK:\n\t\treturn \"Oplink\"\n\tcase OP_LOCK:\n\t\treturn \"Oplock\"\n\tcase OP_LOCKT:\n\t\treturn \"Oplockt\"\n\tcase OP_LOCKU:\n\t\treturn \"Oplocku\"\n\tcase OP_LOOKUP:\n\t\treturn \"Oplookup\"\n\tcase OP_LOOKUPP:\n\t\treturn \"Oplookupp\"\n\tcase OP_NVERIFY:\n\t\treturn \"Opnverify\"\n\tcase OP_OPEN:\n\t\treturn \"Opopen\"\n\tcase OP_OPENATTR:\n\t\treturn \"Opopenattr\"\n\tcase OP_OPEN_CONFIRM:\n\t\treturn \"Opopen_confirm\"\n\tcase OP_OPEN_DOWNGRADE:\n\t\treturn \"Opopen_downgrade\"\n\tcase OP_PUTFH:\n\t\treturn \"Opputfh\"\n\tcase OP_PUTPUBFH:\n\t\treturn \"Opputpubfh\"\n\tcase OP_PUTROOTFH:\n\t\treturn \"Opputrootfh\"\n\tcase OP_READ:\n\t\treturn \"Opread\"\n\tcase OP_READDIR:\n\t\treturn \"Opreaddir\"\n\tcase OP_READLINK:\n\t\treturn \"Opreadlink\"\n\tcase OP_REMOVE:\n\t\treturn \"Opremove\"\n\tcase OP_RENAME:\n\t\treturn \"Oprename\"\n\tcase OP_RENEW:\n\t\treturn \"Oprenew\"\n\tcase OP_RESTOREFH:\n\t\treturn \"Oprestorefh\"\n\tcase OP_SAVEFH:\n\t\treturn \"Opsavefh\"\n\tcase OP_SECINFO:\n\t\treturn \"Opsecinfo\"\n\tcase OP_SETATTR:\n\t\treturn \"Opsetattr\"\n\tcase OP_SETCLIENTID:\n\t\treturn \"Opsetclientid\"\n\tcase OP_SETCLIENTID_CONFIRM:\n\t\treturn \"Opsetclientid_confirm\"\n\tcase OP_VERIFY:\n\t\treturn \"Opverify\"\n\tcase OP_WRITE:\n\t\treturn \"Opwrite\"\n\tcase OP_RELEASE_LOCKOWNER:\n\t\treturn \"Oprelease_lockowner\"\n\tcase OP_CREATE_SESSION:\n\t\treturn \"Opcreatesession\"\n\tcase OP_DESTROY_SESSION:\n\t\treturn \"Opdestroysession\"\n\tcase OP_FREE_STATEID:\n\t\treturn \"Opfreestateid\"\n\tcase OP_GET_DIR_DELEGATION:\n\t\treturn \"Opgetdirdelegation\"\n\tcase OP_GETDEVICEINFO:\n\t\treturn \"Opgetdeviceinfo\"\n\tcase OP_GETDEVICELIST:\n\t\treturn \"Opgetdevicelist\"\n\tcase OP_LAYOUTCOMMIT:\n\t\treturn \"Oplayoutcommit\"\n\tcase OP_LAYOUTGET:\n\t\treturn \"Oplayoutget\"\n\tcase OP_LAYOUTRETURN:\n\t\treturn \"Oplayoutreturn\"\n\tcase OP_SECINFO_NO_NAME:\n\t\treturn \"Opsecinfononame\"\n\tcase OP_SEQUENCE:\n\t\treturn \"Opsequence\"\n\tcase OP_SET_SSV:\n\t\treturn \"Opsetssv\"\n\tcase OP_TEST_STATEID:\n\t\treturn \"Opteststateid\"\n\tcase OP_WANT_DELEGATION:\n\t\treturn \"Opwantdelegation\"\n\tcase OP_DESTROY_CLIENTID:\n\t\treturn \"Opdestroyclientid\"\n\tcase OP_RECLAIM_COMPLETE:\n\t\treturn \"Opreclaimcomplete\"\n\tcase OP_ILLEGAL:\n\t\treturn \"Opillegal\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Nfs_resop4 = *Nfs_resop4\nfunc (v *Nfs_resop4) XdrPointer() interface{} { return v }\nfunc (Nfs_resop4) XdrTypeName() string { return \"Nfs_resop4\" }\nfunc (v Nfs_resop4) XdrValue() interface{} { return v }\nfunc (v *Nfs_resop4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Nfs_resop4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfs_opnum4(&u.Resop).XdrMarshal(x, x.Sprintf(\"%sresop\", name))\n\tswitch u.Resop {\n\tcase OP_ACCESS:\n\t\tx.Marshal(x.Sprintf(\"%sopaccess\", name), XDR_ACCESS4res(u.Opaccess()))\n\t\treturn\n\tcase OP_CLOSE:\n\t\tx.Marshal(x.Sprintf(\"%sopclose\", name), XDR_CLOSE4res(u.Opclose()))\n\t\treturn\n\tcase OP_COMMIT:\n\t\tx.Marshal(x.Sprintf(\"%sopcommit\", name), XDR_COMMIT4res(u.Opcommit()))\n\t\treturn\n\tcase OP_CREATE:\n\t\tx.Marshal(x.Sprintf(\"%sopcreate\", name), XDR_CREATE4res(u.Opcreate()))\n\t\treturn\n\tcase OP_DELEGPURGE:\n\t\tx.Marshal(x.Sprintf(\"%sopdelegpurge\", name), XDR_DELEGPURGE4res(u.Opdelegpurge()))\n\t\treturn\n\tcase OP_DELEGRETURN:\n\t\tx.Marshal(x.Sprintf(\"%sopdelegreturn\", name), XDR_DELEGRETURN4res(u.Opdelegreturn()))\n\t\treturn\n\tcase OP_GETATTR:\n\t\tx.Marshal(x.Sprintf(\"%sopgetattr\", name), XDR_GETATTR4res(u.Opgetattr()))\n\t\treturn\n\tcase OP_GETFH:\n\t\tx.Marshal(x.Sprintf(\"%sopgetfh\", name), XDR_GETFH4res(u.Opgetfh()))\n\t\treturn\n\tcase OP_LINK:\n\t\tx.Marshal(x.Sprintf(\"%soplink\", name), XDR_LINK4res(u.Oplink()))\n\t\treturn\n\tcase OP_LOCK:\n\t\tx.Marshal(x.Sprintf(\"%soplock\", name), XDR_LOCK4res(u.Oplock()))\n\t\treturn\n\tcase OP_LOCKT:\n\t\tx.Marshal(x.Sprintf(\"%soplockt\", name), XDR_LOCKT4res(u.Oplockt()))\n\t\treturn\n\tcase OP_LOCKU:\n\t\tx.Marshal(x.Sprintf(\"%soplocku\", name), XDR_LOCKU4res(u.Oplocku()))\n\t\treturn\n\tcase OP_LOOKUP:\n\t\tx.Marshal(x.Sprintf(\"%soplookup\", name), XDR_LOOKUP4res(u.Oplookup()))\n\t\treturn\n\tcase OP_LOOKUPP:\n\t\tx.Marshal(x.Sprintf(\"%soplookupp\", name), XDR_LOOKUPP4res(u.Oplookupp()))\n\t\treturn\n\tcase OP_NVERIFY:\n\t\tx.Marshal(x.Sprintf(\"%sopnverify\", name), XDR_NVERIFY4res(u.Opnverify()))\n\t\treturn\n\tcase OP_OPEN:\n\t\tx.Marshal(x.Sprintf(\"%sopopen\", name), XDR_OPEN4res(u.Opopen()))\n\t\treturn\n\tcase OP_OPENATTR:\n\t\tx.Marshal(x.Sprintf(\"%sopopenattr\", name), XDR_OPENATTR4res(u.Opopenattr()))\n\t\treturn\n\tcase OP_OPEN_CONFIRM:\n\t\tx.Marshal(x.Sprintf(\"%sopopen_confirm\", name), XDR_OPEN_CONFIRM4res(u.Opopen_confirm()))\n\t\treturn\n\tcase OP_OPEN_DOWNGRADE:\n\t\tx.Marshal(x.Sprintf(\"%sopopen_downgrade\", name), XDR_OPEN_DOWNGRADE4res(u.Opopen_downgrade()))\n\t\treturn\n\tcase OP_PUTFH:\n\t\tx.Marshal(x.Sprintf(\"%sopputfh\", name), XDR_PUTFH4res(u.Opputfh()))\n\t\treturn\n\tcase OP_PUTPUBFH:\n\t\tx.Marshal(x.Sprintf(\"%sopputpubfh\", name), XDR_PUTPUBFH4res(u.Opputpubfh()))\n\t\treturn\n\tcase OP_PUTROOTFH:\n\t\tx.Marshal(x.Sprintf(\"%sopputrootfh\", name), XDR_PUTROOTFH4res(u.Opputrootfh()))\n\t\treturn\n\tcase OP_READ:\n\t\tx.Marshal(x.Sprintf(\"%sopread\", name), XDR_READ4res(u.Opread()))\n\t\treturn\n\tcase OP_READDIR:\n\t\tx.Marshal(x.Sprintf(\"%sopreaddir\", name), XDR_READDIR4res(u.Opreaddir()))\n\t\treturn\n\tcase OP_READLINK:\n\t\tx.Marshal(x.Sprintf(\"%sopreadlink\", name), XDR_READLINK4res(u.Opreadlink()))\n\t\treturn\n\tcase OP_REMOVE:\n\t\tx.Marshal(x.Sprintf(\"%sopremove\", name), XDR_REMOVE4res(u.Opremove()))\n\t\treturn\n\tcase OP_RENAME:\n\t\tx.Marshal(x.Sprintf(\"%soprename\", name), XDR_RENAME4res(u.Oprename()))\n\t\treturn\n\tcase OP_RENEW:\n\t\tx.Marshal(x.Sprintf(\"%soprenew\", name), XDR_RENEW4res(u.Oprenew()))\n\t\treturn\n\tcase OP_RESTOREFH:\n\t\tx.Marshal(x.Sprintf(\"%soprestorefh\", name), XDR_RESTOREFH4res(u.Oprestorefh()))\n\t\treturn\n\tcase OP_SAVEFH:\n\t\tx.Marshal(x.Sprintf(\"%sopsavefh\", name), XDR_SAVEFH4res(u.Opsavefh()))\n\t\treturn\n\tcase OP_SECINFO:\n\t\tx.Marshal(x.Sprintf(\"%sopsecinfo\", name), XDR_SECINFO4res(u.Opsecinfo()))\n\t\treturn\n\tcase OP_SETATTR:\n\t\tx.Marshal(x.Sprintf(\"%sopsetattr\", name), XDR_SETATTR4res(u.Opsetattr()))\n\t\treturn\n\tcase OP_SETCLIENTID:\n\t\tx.Marshal(x.Sprintf(\"%sopsetclientid\", name), XDR_SETCLIENTID4res(u.Opsetclientid()))\n\t\treturn\n\tcase OP_SETCLIENTID_CONFIRM:\n\t\tx.Marshal(x.Sprintf(\"%sopsetclientid_confirm\", name), XDR_SETCLIENTID_CONFIRM4res(u.Opsetclientid_confirm()))\n\t\treturn\n\tcase OP_VERIFY:\n\t\tx.Marshal(x.Sprintf(\"%sopverify\", name), XDR_VERIFY4res(u.Opverify()))\n\t\treturn\n\tcase OP_WRITE:\n\t\tx.Marshal(x.Sprintf(\"%sopwrite\", name), XDR_WRITE4res(u.Opwrite()))\n\t\treturn\n\tcase OP_RELEASE_LOCKOWNER:\n\t\tx.Marshal(x.Sprintf(\"%soprelease_lockowner\", name), XDR_RELEASE_LOCKOWNER4res(u.Oprelease_lockowner()))\n\t\treturn\n\tcase OP_CREATE_SESSION:\n\t\tx.Marshal(x.Sprintf(\"%sopcreatesession\", name), XDR_CREATE_SESSION4res(u.Opcreatesession()))\n\t\treturn\n\tcase OP_DESTROY_SESSION:\n\t\tx.Marshal(x.Sprintf(\"%sopdestroysession\", name), XDR_DESTROY_SESSION4res(u.Opdestroysession()))\n\t\treturn\n\tcase OP_FREE_STATEID:\n\t\tx.Marshal(x.Sprintf(\"%sopfreestateid\", name), XDR_FREE_STATEID4res(u.Opfreestateid()))\n\t\treturn\n\tcase OP_GET_DIR_DELEGATION:\n\t\tx.Marshal(x.Sprintf(\"%sopgetdirdelegation\", name), XDR_GET_DIR_DELEGATION4res(u.Opgetdirdelegation()))\n\t\treturn\n\tcase OP_GETDEVICEINFO:\n\t\tx.Marshal(x.Sprintf(\"%sopgetdeviceinfo\", name), XDR_GETDEVICEINFO4res(u.Opgetdeviceinfo()))\n\t\treturn\n\tcase OP_GETDEVICELIST:\n\t\tx.Marshal(x.Sprintf(\"%sopgetdevicelist\", name), XDR_GETDEVICELIST4res(u.Opgetdevicelist()))\n\t\treturn\n\tcase OP_LAYOUTCOMMIT:\n\t\tx.Marshal(x.Sprintf(\"%soplayoutcommit\", name), XDR_LAYOUTCOMMIT4res(u.Oplayoutcommit()))\n\t\treturn\n\tcase OP_LAYOUTGET:\n\t\tx.Marshal(x.Sprintf(\"%soplayoutget\", name), XDR_LAYOUTGET4res(u.Oplayoutget()))\n\t\treturn\n\tcase OP_LAYOUTRETURN:\n\t\tx.Marshal(x.Sprintf(\"%soplayoutreturn\", name), XDR_LAYOUTRETURN4res(u.Oplayoutreturn()))\n\t\treturn\n\tcase OP_SECINFO_NO_NAME:\n\t\tx.Marshal(x.Sprintf(\"%sopsecinfononame\", name), XDR_SECINFO_NO_NAME4res(u.Opsecinfononame()))\n\t\treturn\n\tcase OP_SEQUENCE:\n\t\tx.Marshal(x.Sprintf(\"%sopsequence\", name), XDR_SEQUENCE4res(u.Opsequence()))\n\t\treturn\n\tcase OP_SET_SSV:\n\t\tx.Marshal(x.Sprintf(\"%sopsetssv\", name), XDR_SET_SSV4res(u.Opsetssv()))\n\t\treturn\n\tcase OP_TEST_STATEID:\n\t\tx.Marshal(x.Sprintf(\"%sopteststateid\", name), XDR_TEST_STATEID4res(u.Opteststateid()))\n\t\treturn\n\tcase OP_WANT_DELEGATION:\n\t\tx.Marshal(x.Sprintf(\"%sopwantdelegation\", name), XDR_WANT_DELEGATION4res(u.Opwantdelegation()))\n\t\treturn\n\tcase OP_DESTROY_CLIENTID:\n\t\tx.Marshal(x.Sprintf(\"%sopdestroyclientid\", name), XDR_DESTROY_CLIENTID4res(u.Opdestroyclientid()))\n\t\treturn\n\tcase OP_RECLAIM_COMPLETE:\n\t\tx.Marshal(x.Sprintf(\"%sopreclaimcomplete\", name), XDR_RECLAIM_COMPLETE4res(u.Opreclaimcomplete()))\n\t\treturn\n\tcase OP_ILLEGAL:\n\t\tx.Marshal(x.Sprintf(\"%sopillegal\", name), XDR_ILLEGAL4res(u.Opillegal()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Resop (%v) in Nfs_resop4\", u.Resop)\n}\nfunc (v *Nfs_resop4) XdrInitialize() {\n\tvar zero Nfs_opnum4\n\tswitch zero {\n\tcase OP_ACCESS, OP_CLOSE, OP_COMMIT, OP_CREATE, OP_DELEGPURGE, OP_DELEGRETURN, OP_GETATTR, OP_GETFH, OP_LINK, OP_LOCK, OP_LOCKT, OP_LOCKU, OP_LOOKUP, OP_LOOKUPP, OP_NVERIFY, OP_OPEN, OP_OPENATTR, OP_OPEN_CONFIRM, OP_OPEN_DOWNGRADE, OP_PUTFH, OP_PUTPUBFH, OP_PUTROOTFH, OP_READ, OP_READDIR, OP_READLINK, OP_REMOVE, OP_RENAME, OP_RENEW, OP_RESTOREFH, OP_SAVEFH, OP_SECINFO, OP_SETATTR, OP_SETCLIENTID, OP_SETCLIENTID_CONFIRM, OP_VERIFY, OP_WRITE, OP_RELEASE_LOCKOWNER, OP_CREATE_SESSION, OP_DESTROY_SESSION, OP_FREE_STATEID, OP_GET_DIR_DELEGATION, OP_GETDEVICEINFO, OP_GETDEVICELIST, OP_LAYOUTCOMMIT, OP_LAYOUTGET, OP_LAYOUTRETURN, OP_SECINFO_NO_NAME, OP_SEQUENCE, OP_SET_SSV, OP_TEST_STATEID, OP_WANT_DELEGATION, OP_DESTROY_CLIENTID, OP_RECLAIM_COMPLETE, OP_ILLEGAL:\n\tdefault:\n\t\tif v.Resop == zero { v.Resop = OP_ACCESS }\n\t}\n}\nfunc XDR_Nfs_resop4(v *Nfs_resop4) *Nfs_resop4 { return v}\ntype _XdrVec_unbounded_Nfs_argop4 []Nfs_argop4\nfunc (_XdrVec_unbounded_Nfs_argop4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Nfs_argop4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Nfs_argop4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Nfs_argop4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Nfs_argop4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Nfs_argop4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Nfs_argop4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Nfs_argop4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Nfs_argop4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Nfs_argop4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Nfs_argop4) XdrTypeName() string { return \"Nfs_argop4<>\" }\nfunc (v *_XdrVec_unbounded_Nfs_argop4) XdrPointer() interface{} { return (*[]Nfs_argop4)(v) }\nfunc (v _XdrVec_unbounded_Nfs_argop4) XdrValue() interface{} { return ([]Nfs_argop4)(v) }\nfunc (v *_XdrVec_unbounded_Nfs_argop4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_COMPOUND4args = *COMPOUND4args\nfunc (v *COMPOUND4args) XdrPointer() interface{} { return v }\nfunc (COMPOUND4args) XdrTypeName() string { return \"COMPOUND4args\" }\nfunc (v COMPOUND4args) XdrValue() interface{} { return v }\nfunc (v *COMPOUND4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *COMPOUND4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%stag\", name), XDR_Utf8str_cs(&v.Tag))\n\tx.Marshal(x.Sprintf(\"%sminorversion\", name), XDR_Uint32_t(&v.Minorversion))\n\tx.Marshal(x.Sprintf(\"%sargarray\", name), (*_XdrVec_unbounded_Nfs_argop4)(&v.Argarray))\n}\nfunc XDR_COMPOUND4args(v *COMPOUND4args) *COMPOUND4args { return v }\ntype _XdrVec_unbounded_Nfs_resop4 []Nfs_resop4\nfunc (_XdrVec_unbounded_Nfs_resop4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Nfs_resop4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Nfs_resop4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Nfs_resop4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Nfs_resop4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Nfs_resop4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Nfs_resop4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Nfs_resop4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Nfs_resop4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Nfs_resop4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Nfs_resop4) XdrTypeName() string { return \"Nfs_resop4<>\" }\nfunc (v *_XdrVec_unbounded_Nfs_resop4) XdrPointer() interface{} { return (*[]Nfs_resop4)(v) }\nfunc (v _XdrVec_unbounded_Nfs_resop4) XdrValue() interface{} { return ([]Nfs_resop4)(v) }\nfunc (v *_XdrVec_unbounded_Nfs_resop4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_COMPOUND4res = *COMPOUND4res\nfunc (v *COMPOUND4res) XdrPointer() interface{} { return v }\nfunc (COMPOUND4res) XdrTypeName() string { return \"COMPOUND4res\" }\nfunc (v COMPOUND4res) XdrValue() interface{} { return v }\nfunc (v *COMPOUND4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *COMPOUND4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n\tx.Marshal(x.Sprintf(\"%stag\", name), XDR_Utf8str_cs(&v.Tag))\n\tx.Marshal(x.Sprintf(\"%sresarray\", name), (*_XdrVec_unbounded_Nfs_resop4)(&v.Resarray))\n}\nfunc XDR_COMPOUND4res(v *COMPOUND4res) *COMPOUND4res { return v }\n\ntype XdrProc_NFSPROC4_NULL struct {\n\tArg *XdrVoid\n\tRes *XdrVoid\n}\nfunc (XdrProc_NFSPROC4_NULL) Prog() uint32     { return 100003 }\nfunc (XdrProc_NFSPROC4_NULL) Vers() uint32     { return 4 }\nfunc (XdrProc_NFSPROC4_NULL) Proc() uint32     { return 0 }\nfunc (XdrProc_NFSPROC4_NULL) ProgName() string { return \"NFS4_PROGRAM\" }\nfunc (XdrProc_NFSPROC4_NULL) VersName() string { return \"NFS_V4\" }\nfunc (XdrProc_NFSPROC4_NULL) ProcName() string { return \"NFSPROC4_NULL\" }\nfunc (p *XdrProc_NFSPROC4_NULL) GetArg() XdrType {\n\tif p.Arg == nil {\n\t\tp.Arg = new(XdrVoid)\n\t}\n\treturn XDR_XdrVoid(p.Arg)\n}\nfunc (p *XdrProc_NFSPROC4_NULL) GetRes() XdrType {\n\tif p.Res == nil {\n\t\tp.Res = new(XdrVoid)\n\t}\n\treturn XDR_XdrVoid(p.Res)\n}\nvar _ XdrProc = &XdrProc_NFSPROC4_NULL{} // XXX\n\ntype xdrSrvProc_NFSPROC4_NULL struct {\n\tXdrProc_NFSPROC4_NULL\n\tSrv NFS_V4\n}\nfunc (p *xdrSrvProc_NFSPROC4_NULL) SetContext(ctx context.Context) {\n\tif wc, ok := p.Srv.(interface {\n\t\tWithContext(context.Context) NFS_V4\n\t}); ok {\n\t\tp.Srv = wc.WithContext(ctx)\n\t}\n}\nfunc (p *xdrSrvProc_NFSPROC4_NULL) Do() {\n\tp.Srv.NFSPROC4_NULL()\n}\nvar _ XdrSrvProc = &xdrSrvProc_NFSPROC4_NULL{} // XXX\n\ntype XdrProc_NFSPROC4_COMPOUND struct {\n\tArg *COMPOUND4args\n\tRes *COMPOUND4res\n}\nfunc (XdrProc_NFSPROC4_COMPOUND) Prog() uint32 { return 100003 }\nfunc (XdrProc_NFSPROC4_COMPOUND) Vers() uint32 { return 4 }\nfunc (XdrProc_NFSPROC4_COMPOUND) Proc() uint32 { return 1 }\nfunc (XdrProc_NFSPROC4_COMPOUND) ProgName() string { return \"NFS4_PROGRAM\" }\nfunc (XdrProc_NFSPROC4_COMPOUND) VersName() string { return \"NFS_V4\" }\nfunc (XdrProc_NFSPROC4_COMPOUND) ProcName() string { return \"NFSPROC4_COMPOUND\" }\nfunc (p *XdrProc_NFSPROC4_COMPOUND) GetArg() XdrType {\n\tif p.Arg == nil {\n\t\tp.Arg = new(COMPOUND4args)\n\t}\n\treturn XDR_COMPOUND4args(p.Arg)\n}\nfunc (p *XdrProc_NFSPROC4_COMPOUND) GetRes() XdrType {\n\tif p.Res == nil {\n\t\tp.Res = new(COMPOUND4res)\n\t}\n\treturn XDR_COMPOUND4res(p.Res)\n}\nvar _ XdrProc = &XdrProc_NFSPROC4_COMPOUND{} // XXX\n\ntype xdrSrvProc_NFSPROC4_COMPOUND struct {\n\tXdrProc_NFSPROC4_COMPOUND\n\tSrv NFS_V4\n}\nfunc (p *xdrSrvProc_NFSPROC4_COMPOUND) SetContext(ctx context.Context) {\n\tif wc, ok := p.Srv.(interface {\n\t\tWithContext(context.Context) NFS_V4\n\t}); ok {\n\t\tp.Srv = wc.WithContext(ctx)\n\t}\n}\nfunc (p *xdrSrvProc_NFSPROC4_COMPOUND) Do() {\n\tr := p.Srv.NFSPROC4_COMPOUND(*p.Arg)\n\tp.Res = &r\n\n}\nvar _ XdrSrvProc = &xdrSrvProc_NFSPROC4_COMPOUND{} // XXX\n\nfunc init() {\n\tXdrCatalog[100003<<32|4] = func(p uint32) XdrProc {\n\t\tswitch(p) {\n\t\tcase 0:\n\t\t\treturn &XdrProc_NFSPROC4_NULL{}\n\t\tcase 1:\n\t\t\treturn &XdrProc_NFSPROC4_COMPOUND{}\n\t\t}\n\t\treturn nil\n\t}\n}\n\ntype NFS_V4_Server struct {\n\tSrv NFS_V4\n}\nfunc (NFS_V4_Server) Prog() uint32 { return 100003 }\nfunc (NFS_V4_Server) Vers() uint32 { return 4 }\nfunc (NFS_V4_Server) ProgName() string { return \"NFS4_PROGRAM\" }\nfunc (NFS_V4_Server) VersName() string { return \"NFS_V4\" }\nfunc (s NFS_V4_Server) GetProc(p uint32) XdrSrvProc {\n\tswitch p {\n\tcase 0:  // NFSPROC4_NULL\n\t\treturn &xdrSrvProc_NFSPROC4_NULL{ Srv: s.Srv }\n\tcase 1:  // NFSPROC4_COMPOUND\n\t\treturn &xdrSrvProc_NFSPROC4_COMPOUND{ Srv: s.Srv }\n\tdefault:\n\t\treturn nil\n\t}\n}\nvar _ XdrSrv = NFS_V4_Server{} // XXX\n\ntype NFS_V4_Client struct {\n\tSend XdrSendCall\n\tCtx context.Context\n}\nvar _ NFS_V4 = NFS_V4_Client{} // XXX\nfunc (c NFS_V4_Client) WithContext(ctx context.Context) NFS_V4 {\n\tc.Ctx = ctx\n\treturn c\n}\nfunc (c NFS_V4_Client) NFSPROC4_NULL() {\n\tvar proc XdrProc_NFSPROC4_NULL\n\tif err := c.Send.SendCall(c.Ctx, &proc); err != nil {\n\t\tpanic(err)\n\t}\n}\nfunc (c NFS_V4_Client) NFSPROC4_COMPOUND(a1 COMPOUND4args) COMPOUND4res {\n\tvar proc XdrProc_NFSPROC4_COMPOUND\n\tproc.Arg = &a1\n\tif err := c.Send.SendCall(c.Ctx, &proc); err != nil {\n\t\tpanic(err)\n\t}\n\treturn *proc.Res\n}\ntype XdrType_CB_GETATTR4args = *CB_GETATTR4args\nfunc (v *CB_GETATTR4args) XdrPointer() interface{} { return v }\nfunc (CB_GETATTR4args) XdrTypeName() string { return \"CB_GETATTR4args\" }\nfunc (v CB_GETATTR4args) XdrValue() interface{} { return v }\nfunc (v *CB_GETATTR4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *CB_GETATTR4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sfh\", name), XDR_Nfs_fh4(&v.Fh))\n\tx.Marshal(x.Sprintf(\"%sattr_request\", name), XDR_Bitmap4(&v.Attr_request))\n}\nfunc XDR_CB_GETATTR4args(v *CB_GETATTR4args) *CB_GETATTR4args { return v }\ntype XdrType_CB_GETATTR4resok = *CB_GETATTR4resok\nfunc (v *CB_GETATTR4resok) XdrPointer() interface{} { return v }\nfunc (CB_GETATTR4resok) XdrTypeName() string { return \"CB_GETATTR4resok\" }\nfunc (v CB_GETATTR4resok) XdrValue() interface{} { return v }\nfunc (v *CB_GETATTR4resok) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *CB_GETATTR4resok) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sobj_attributes\", name), XDR_Fattr4(&v.Obj_attributes))\n}\nfunc XDR_CB_GETATTR4resok(v *CB_GETATTR4resok) *CB_GETATTR4resok { return v }\nfunc (u *CB_GETATTR4res) Resok4() *CB_GETATTR4resok {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tif v, ok := u.U.(*CB_GETATTR4resok); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CB_GETATTR4resok\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"CB_GETATTR4res.Resok4 accessed when Status == %v\", u.Status)\n\t\treturn nil\n\t}\n}\nfunc (u CB_GETATTR4res) XdrValid() bool {\n\treturn true\n}\nfunc (u *CB_GETATTR4res) XdrUnionTag() XdrNum32 {\n\treturn XDR_Nfsstat4(&u.Status)\n}\nfunc (u *CB_GETATTR4res) XdrUnionTagName() string {\n\treturn \"Status\"\n}\nfunc (u *CB_GETATTR4res) XdrUnionBody() XdrType {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn XDR_CB_GETATTR4resok(u.Resok4())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *CB_GETATTR4res) XdrUnionBodyName() string {\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\treturn \"Resok4\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_CB_GETATTR4res = *CB_GETATTR4res\nfunc (v *CB_GETATTR4res) XdrPointer() interface{} { return v }\nfunc (CB_GETATTR4res) XdrTypeName() string { return \"CB_GETATTR4res\" }\nfunc (v CB_GETATTR4res) XdrValue() interface{} { return v }\nfunc (v *CB_GETATTR4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *CB_GETATTR4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Nfsstat4(&u.Status).XdrMarshal(x, x.Sprintf(\"%sstatus\", name))\n\tswitch u.Status {\n\tcase NFS4_OK:\n\t\tx.Marshal(x.Sprintf(\"%sresok4\", name), XDR_CB_GETATTR4resok(u.Resok4()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_CB_GETATTR4res(v *CB_GETATTR4res) *CB_GETATTR4res { return v}\ntype XdrType_CB_RECALL4args = *CB_RECALL4args\nfunc (v *CB_RECALL4args) XdrPointer() interface{} { return v }\nfunc (CB_RECALL4args) XdrTypeName() string { return \"CB_RECALL4args\" }\nfunc (v CB_RECALL4args) XdrValue() interface{} { return v }\nfunc (v *CB_RECALL4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *CB_RECALL4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstateid\", name), XDR_Stateid4(&v.Stateid))\n\tx.Marshal(x.Sprintf(\"%struncate\", name), XDR_bool(&v.Truncate))\n\tx.Marshal(x.Sprintf(\"%sfh\", name), XDR_Nfs_fh4(&v.Fh))\n}\nfunc XDR_CB_RECALL4args(v *CB_RECALL4args) *CB_RECALL4args { return v }\ntype XdrType_CB_RECALL4res = *CB_RECALL4res\nfunc (v *CB_RECALL4res) XdrPointer() interface{} { return v }\nfunc (CB_RECALL4res) XdrTypeName() string { return \"CB_RECALL4res\" }\nfunc (v CB_RECALL4res) XdrValue() interface{} { return v }\nfunc (v *CB_RECALL4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *CB_RECALL4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_CB_RECALL4res(v *CB_RECALL4res) *CB_RECALL4res { return v }\ntype XdrType_CB_ILLEGAL4res = *CB_ILLEGAL4res\nfunc (v *CB_ILLEGAL4res) XdrPointer() interface{} { return v }\nfunc (CB_ILLEGAL4res) XdrTypeName() string { return \"CB_ILLEGAL4res\" }\nfunc (v CB_ILLEGAL4res) XdrValue() interface{} { return v }\nfunc (v *CB_ILLEGAL4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *CB_ILLEGAL4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n}\nfunc XDR_CB_ILLEGAL4res(v *CB_ILLEGAL4res) *CB_ILLEGAL4res { return v }\nvar _XdrNames_Nfs_cb_opnum4 = map[int32]string{\n\tint32(OP_CB_GETATTR): \"OP_CB_GETATTR\",\n\tint32(OP_CB_RECALL): \"OP_CB_RECALL\",\n\tint32(OP_CB_ILLEGAL): \"OP_CB_ILLEGAL\",\n}\nvar _XdrValues_Nfs_cb_opnum4 = map[string]int32{\n\t\"OP_CB_GETATTR\": int32(OP_CB_GETATTR),\n\t\"OP_CB_RECALL\": int32(OP_CB_RECALL),\n\t\"OP_CB_ILLEGAL\": int32(OP_CB_ILLEGAL),\n}\nfunc (Nfs_cb_opnum4) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Nfs_cb_opnum4\n}\nfunc (v Nfs_cb_opnum4) String() string {\n\tif s, ok := _XdrNames_Nfs_cb_opnum4[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Nfs_cb_opnum4#%d\", v)\n}\nfunc (v *Nfs_cb_opnum4) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Nfs_cb_opnum4[stok]; ok {\n\t\t\t*v = Nfs_cb_opnum4(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Nfs_cb_opnum4\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Nfs_cb_opnum4.\", stok))\n\t}\n}\nfunc (v Nfs_cb_opnum4) GetU32() uint32 { return uint32(v) }\nfunc (v *Nfs_cb_opnum4) SetU32(n uint32) { *v = Nfs_cb_opnum4(n) }\nfunc (v *Nfs_cb_opnum4) XdrPointer() interface{} { return v }\nfunc (Nfs_cb_opnum4) XdrTypeName() string { return \"Nfs_cb_opnum4\" }\nfunc (v Nfs_cb_opnum4) XdrValue() interface{} { return v }\nfunc (v *Nfs_cb_opnum4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Nfs_cb_opnum4 = *Nfs_cb_opnum4\nfunc XDR_Nfs_cb_opnum4(v *Nfs_cb_opnum4) *Nfs_cb_opnum4 { return v }\nfunc (v *Nfs_cb_opnum4) XdrInitialize() {\n\tswitch Nfs_cb_opnum4(0) {\n\tcase OP_CB_GETATTR, OP_CB_RECALL, OP_CB_ILLEGAL:\n\tdefault:\n\t\tif *v == Nfs_cb_opnum4(0) { *v = OP_CB_GETATTR }\n\t}\n}\nvar _XdrTags_Nfs_cb_argop4 = map[int32]bool{\n\tXdrToI32(OP_CB_GETATTR): true,\n\tXdrToI32(OP_CB_RECALL): true,\n\tXdrToI32(OP_CB_ILLEGAL): true,\n}\nfunc (_ Nfs_cb_argop4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Nfs_cb_argop4\n}\nfunc (u *Nfs_cb_argop4) Opcbgetattr() *CB_GETATTR4args {\n\tswitch Nfs_cb_opnum4(u.Argop) {\n\tcase OP_CB_GETATTR:\n\t\tif v, ok := u.U.(*CB_GETATTR4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CB_GETATTR4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_cb_argop4.Opcbgetattr accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_cb_argop4) Opcbrecall() *CB_RECALL4args {\n\tswitch Nfs_cb_opnum4(u.Argop) {\n\tcase OP_CB_RECALL:\n\t\tif v, ok := u.U.(*CB_RECALL4args); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CB_RECALL4args\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_cb_argop4.Opcbrecall accessed when Argop == %v\", u.Argop)\n\t\treturn nil\n\t}\n}\nfunc (u Nfs_cb_argop4) XdrValid() bool {\n\tswitch Nfs_cb_opnum4(u.Argop) {\n\tcase OP_CB_GETATTR,OP_CB_RECALL,OP_CB_ILLEGAL:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Nfs_cb_argop4) XdrUnionTag() XdrNum32 {\n\treturn XDR_uint32(&u.Argop)\n}\nfunc (u *Nfs_cb_argop4) XdrUnionTagName() string {\n\treturn \"Argop\"\n}\nfunc (u *Nfs_cb_argop4) XdrUnionBody() XdrType {\n\tswitch Nfs_cb_opnum4(u.Argop) {\n\tcase OP_CB_GETATTR:\n\t\treturn XDR_CB_GETATTR4args(u.Opcbgetattr())\n\tcase OP_CB_RECALL:\n\t\treturn XDR_CB_RECALL4args(u.Opcbrecall())\n\tcase OP_CB_ILLEGAL:\n\t\treturn nil\n\t}\n\treturn nil\n}\nfunc (u *Nfs_cb_argop4) XdrUnionBodyName() string {\n\tswitch Nfs_cb_opnum4(u.Argop) {\n\tcase OP_CB_GETATTR:\n\t\treturn \"Opcbgetattr\"\n\tcase OP_CB_RECALL:\n\t\treturn \"Opcbrecall\"\n\tcase OP_CB_ILLEGAL:\n\t\treturn \"\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Nfs_cb_argop4 = *Nfs_cb_argop4\nfunc (v *Nfs_cb_argop4) XdrPointer() interface{} { return v }\nfunc (Nfs_cb_argop4) XdrTypeName() string { return \"Nfs_cb_argop4\" }\nfunc (v Nfs_cb_argop4) XdrValue() interface{} { return v }\nfunc (v *Nfs_cb_argop4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Nfs_cb_argop4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_uint32(&u.Argop).XdrMarshal(x, x.Sprintf(\"%sargop\", name))\n\tswitch Nfs_cb_opnum4(u.Argop) {\n\tcase OP_CB_GETATTR:\n\t\tx.Marshal(x.Sprintf(\"%sopcbgetattr\", name), XDR_CB_GETATTR4args(u.Opcbgetattr()))\n\t\treturn\n\tcase OP_CB_RECALL:\n\t\tx.Marshal(x.Sprintf(\"%sopcbrecall\", name), XDR_CB_RECALL4args(u.Opcbrecall()))\n\t\treturn\n\tcase OP_CB_ILLEGAL:\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Argop (%v) in Nfs_cb_argop4\", u.Argop)\n}\nfunc (v *Nfs_cb_argop4) XdrInitialize() {\n\tvar zero uint32\n\tswitch Nfs_cb_opnum4(zero) {\n\tcase OP_CB_GETATTR, OP_CB_RECALL, OP_CB_ILLEGAL:\n\tdefault:\n\t\tif v.Argop == zero { v.Argop = uint32(OP_CB_GETATTR) }\n\t}\n}\nfunc XDR_Nfs_cb_argop4(v *Nfs_cb_argop4) *Nfs_cb_argop4 { return v}\nvar _XdrTags_Nfs_cb_resop4 = map[int32]bool{\n\tXdrToI32(OP_CB_GETATTR): true,\n\tXdrToI32(OP_CB_RECALL): true,\n\tXdrToI32(OP_CB_ILLEGAL): true,\n}\nfunc (_ Nfs_cb_resop4) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Nfs_cb_resop4\n}\nfunc (u *Nfs_cb_resop4) Opcbgetattr() *CB_GETATTR4res {\n\tswitch Nfs_cb_opnum4(u.Resop) {\n\tcase OP_CB_GETATTR:\n\t\tif v, ok := u.U.(*CB_GETATTR4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CB_GETATTR4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_cb_resop4.Opcbgetattr accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_cb_resop4) Opcbrecall() *CB_RECALL4res {\n\tswitch Nfs_cb_opnum4(u.Resop) {\n\tcase OP_CB_RECALL:\n\t\tif v, ok := u.U.(*CB_RECALL4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CB_RECALL4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_cb_resop4.Opcbrecall accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u *Nfs_cb_resop4) Opcbillegal() *CB_ILLEGAL4res {\n\tswitch Nfs_cb_opnum4(u.Resop) {\n\tcase OP_CB_ILLEGAL:\n\t\tif v, ok := u.U.(*CB_ILLEGAL4res); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero CB_ILLEGAL4res\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Nfs_cb_resop4.Opcbillegal accessed when Resop == %v\", u.Resop)\n\t\treturn nil\n\t}\n}\nfunc (u Nfs_cb_resop4) XdrValid() bool {\n\tswitch Nfs_cb_opnum4(u.Resop) {\n\tcase OP_CB_GETATTR,OP_CB_RECALL,OP_CB_ILLEGAL:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Nfs_cb_resop4) XdrUnionTag() XdrNum32 {\n\treturn XDR_uint32(&u.Resop)\n}\nfunc (u *Nfs_cb_resop4) XdrUnionTagName() string {\n\treturn \"Resop\"\n}\nfunc (u *Nfs_cb_resop4) XdrUnionBody() XdrType {\n\tswitch Nfs_cb_opnum4(u.Resop) {\n\tcase OP_CB_GETATTR:\n\t\treturn XDR_CB_GETATTR4res(u.Opcbgetattr())\n\tcase OP_CB_RECALL:\n\t\treturn XDR_CB_RECALL4res(u.Opcbrecall())\n\tcase OP_CB_ILLEGAL:\n\t\treturn XDR_CB_ILLEGAL4res(u.Opcbillegal())\n\t}\n\treturn nil\n}\nfunc (u *Nfs_cb_resop4) XdrUnionBodyName() string {\n\tswitch Nfs_cb_opnum4(u.Resop) {\n\tcase OP_CB_GETATTR:\n\t\treturn \"Opcbgetattr\"\n\tcase OP_CB_RECALL:\n\t\treturn \"Opcbrecall\"\n\tcase OP_CB_ILLEGAL:\n\t\treturn \"Opcbillegal\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Nfs_cb_resop4 = *Nfs_cb_resop4\nfunc (v *Nfs_cb_resop4) XdrPointer() interface{} { return v }\nfunc (Nfs_cb_resop4) XdrTypeName() string { return \"Nfs_cb_resop4\" }\nfunc (v Nfs_cb_resop4) XdrValue() interface{} { return v }\nfunc (v *Nfs_cb_resop4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Nfs_cb_resop4) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_uint32(&u.Resop).XdrMarshal(x, x.Sprintf(\"%sresop\", name))\n\tswitch Nfs_cb_opnum4(u.Resop) {\n\tcase OP_CB_GETATTR:\n\t\tx.Marshal(x.Sprintf(\"%sopcbgetattr\", name), XDR_CB_GETATTR4res(u.Opcbgetattr()))\n\t\treturn\n\tcase OP_CB_RECALL:\n\t\tx.Marshal(x.Sprintf(\"%sopcbrecall\", name), XDR_CB_RECALL4res(u.Opcbrecall()))\n\t\treturn\n\tcase OP_CB_ILLEGAL:\n\t\tx.Marshal(x.Sprintf(\"%sopcbillegal\", name), XDR_CB_ILLEGAL4res(u.Opcbillegal()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Resop (%v) in Nfs_cb_resop4\", u.Resop)\n}\nfunc (v *Nfs_cb_resop4) XdrInitialize() {\n\tvar zero uint32\n\tswitch Nfs_cb_opnum4(zero) {\n\tcase OP_CB_GETATTR, OP_CB_RECALL, OP_CB_ILLEGAL:\n\tdefault:\n\t\tif v.Resop == zero { v.Resop = uint32(OP_CB_GETATTR) }\n\t}\n}\nfunc XDR_Nfs_cb_resop4(v *Nfs_cb_resop4) *Nfs_cb_resop4 { return v}\ntype _XdrVec_unbounded_Nfs_cb_argop4 []Nfs_cb_argop4\nfunc (_XdrVec_unbounded_Nfs_cb_argop4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Nfs_cb_argop4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Nfs_cb_argop4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Nfs_cb_argop4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Nfs_cb_argop4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Nfs_cb_argop4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Nfs_cb_argop4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Nfs_cb_argop4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Nfs_cb_argop4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Nfs_cb_argop4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Nfs_cb_argop4) XdrTypeName() string { return \"Nfs_cb_argop4<>\" }\nfunc (v *_XdrVec_unbounded_Nfs_cb_argop4) XdrPointer() interface{} { return (*[]Nfs_cb_argop4)(v) }\nfunc (v _XdrVec_unbounded_Nfs_cb_argop4) XdrValue() interface{} { return ([]Nfs_cb_argop4)(v) }\nfunc (v *_XdrVec_unbounded_Nfs_cb_argop4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_CB_COMPOUND4args = *CB_COMPOUND4args\nfunc (v *CB_COMPOUND4args) XdrPointer() interface{} { return v }\nfunc (CB_COMPOUND4args) XdrTypeName() string { return \"CB_COMPOUND4args\" }\nfunc (v CB_COMPOUND4args) XdrValue() interface{} { return v }\nfunc (v *CB_COMPOUND4args) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *CB_COMPOUND4args) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%stag\", name), XDR_Utf8str_cs(&v.Tag))\n\tx.Marshal(x.Sprintf(\"%sminorversion\", name), XDR_Uint32_t(&v.Minorversion))\n\tx.Marshal(x.Sprintf(\"%scallback_ident\", name), XDR_Uint32_t(&v.Callback_ident))\n\tx.Marshal(x.Sprintf(\"%sargarray\", name), (*_XdrVec_unbounded_Nfs_cb_argop4)(&v.Argarray))\n}\nfunc XDR_CB_COMPOUND4args(v *CB_COMPOUND4args) *CB_COMPOUND4args { return v }\ntype _XdrVec_unbounded_Nfs_cb_resop4 []Nfs_cb_resop4\nfunc (_XdrVec_unbounded_Nfs_cb_resop4) XdrBound() uint32 {\n\tconst bound uint32 = 4294967295 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (_XdrVec_unbounded_Nfs_cb_resop4) XdrCheckLen(length uint32) {\n\tif length > uint32(4294967295) {\n\t\tXdrPanic(\"_XdrVec_unbounded_Nfs_cb_resop4 length %d exceeds bound 4294967295\", length)\n\t} else if int(length) < 0 {\n\t\tXdrPanic(\"_XdrVec_unbounded_Nfs_cb_resop4 length %d exceeds max int\", length)\n\t}\n}\nfunc (v _XdrVec_unbounded_Nfs_cb_resop4) GetVecLen() uint32 { return uint32(len(v)) }\nfunc (v *_XdrVec_unbounded_Nfs_cb_resop4) SetVecLen(length uint32) {\n\tv.XdrCheckLen(length)\n\tif int(length) <= cap(*v) {\n\t\tif int(length) != len(*v) {\n\t\t\t*v = (*v)[:int(length)]\n\t\t}\n\t\treturn\n\t}\n\tnewcap := 2*cap(*v)\n\tif newcap < int(length) { // also catches overflow where 2*cap < 0\n\t\tnewcap = int(length)\n\t} else if bound := uint(4294967295); uint(newcap) > bound {\n\t\tif int(bound) < 0 {\n\t\t\tbound = ^uint(0) >> 1\n\t\t}\n\t\tnewcap = int(bound)\n\t}\n\tnv := make([]Nfs_cb_resop4, int(length), newcap)\n\tcopy(nv, *v)\n\t*v = nv\n}\nfunc (v *_XdrVec_unbounded_Nfs_cb_resop4) XdrMarshalN(x XDR, name string, n uint32) {\n\tv.XdrCheckLen(n)\n\tfor i := 0; i < int(n); i++ {\n\t\tif (i >= len(*v)) {\n\t\t\tv.SetVecLen(uint32(i+1))\n\t\t}\n\t\tXDR_Nfs_cb_resop4(&(*v)[i]).XdrMarshal(x, x.Sprintf(\"%s[%d]\", name, i))\n\t}\n\tif int(n) < len(*v) {\n\t\t*v = (*v)[:int(n)]\n\t}\n}\nfunc (v *_XdrVec_unbounded_Nfs_cb_resop4) XdrRecurse(x XDR, name string) {\n\tsize := XdrSize{ Size: uint32(len(*v)), Bound: 4294967295 }\n\tx.Marshal(name, &size)\n\tv.XdrMarshalN(x, name, size.Size)\n}\nfunc (_XdrVec_unbounded_Nfs_cb_resop4) XdrTypeName() string { return \"Nfs_cb_resop4<>\" }\nfunc (v *_XdrVec_unbounded_Nfs_cb_resop4) XdrPointer() interface{} { return (*[]Nfs_cb_resop4)(v) }\nfunc (v _XdrVec_unbounded_Nfs_cb_resop4) XdrValue() interface{} { return ([]Nfs_cb_resop4)(v) }\nfunc (v *_XdrVec_unbounded_Nfs_cb_resop4) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_CB_COMPOUND4res = *CB_COMPOUND4res\nfunc (v *CB_COMPOUND4res) XdrPointer() interface{} { return v }\nfunc (CB_COMPOUND4res) XdrTypeName() string { return \"CB_COMPOUND4res\" }\nfunc (v CB_COMPOUND4res) XdrValue() interface{} { return v }\nfunc (v *CB_COMPOUND4res) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *CB_COMPOUND4res) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sstatus\", name), XDR_Nfsstat4(&v.Status))\n\tx.Marshal(x.Sprintf(\"%stag\", name), XDR_Utf8str_cs(&v.Tag))\n\tx.Marshal(x.Sprintf(\"%sresarray\", name), (*_XdrVec_unbounded_Nfs_cb_resop4)(&v.Resarray))\n}\nfunc XDR_CB_COMPOUND4res(v *CB_COMPOUND4res) *CB_COMPOUND4res { return v }\n\ntype xdrProc_CB_NULL struct {\n\tArg *XdrVoid\n\tRes *XdrVoid\n}\nfunc (xdrProc_CB_NULL) Prog() uint32 { return 1073741824 }\nfunc (xdrProc_CB_NULL) Vers() uint32 { return 1 }\nfunc (xdrProc_CB_NULL) Proc() uint32 { return 0 }\nfunc (xdrProc_CB_NULL) ProgName() string { return \"NFS4_CALLBACK\" }\nfunc (xdrProc_CB_NULL) VersName() string { return \"NFS_CB\" }\nfunc (xdrProc_CB_NULL) ProcName() string { return \"CB_NULL\" }\nfunc (p *xdrProc_CB_NULL) GetArg() XdrType {\n\tif p.Arg == nil {\n\t\tp.Arg = new(XdrVoid)\n\t}\n\treturn XDR_XdrVoid(p.Arg)\n}\nfunc (p *xdrProc_CB_NULL) GetRes() XdrType {\n\tif p.Res == nil {\n\t\tp.Res = new(XdrVoid)\n\t}\n\treturn XDR_XdrVoid(p.Res)\n}\nvar _ XdrProc = &xdrProc_CB_NULL{} // XXX\n\ntype xdrSrvProc_CB_NULL struct {\n\txdrProc_CB_NULL\n\tSrv NFS_CB\n}\nfunc (p *xdrSrvProc_CB_NULL) SetContext(ctx context.Context) {\n\tif wc, ok := p.Srv.(interface {\n\t\tWithContext(context.Context) NFS_CB\n\t}); ok {\n\t\tp.Srv = wc.WithContext(ctx)\n\t}\n}\nfunc (p *xdrSrvProc_CB_NULL) Do() {\n\tp.Srv.CB_NULL()\n}\nvar _ XdrSrvProc = &xdrSrvProc_CB_NULL{} // XXX\n\ntype xdrProc_CB_COMPOUND struct {\n\tArg *CB_COMPOUND4args\n\tRes *CB_COMPOUND4res\n}\nfunc (xdrProc_CB_COMPOUND) Prog() uint32 { return 1073741824 }\nfunc (xdrProc_CB_COMPOUND) Vers() uint32 { return 1 }\nfunc (xdrProc_CB_COMPOUND) Proc() uint32 { return 1 }\nfunc (xdrProc_CB_COMPOUND) ProgName() string { return \"NFS4_CALLBACK\" }\nfunc (xdrProc_CB_COMPOUND) VersName() string { return \"NFS_CB\" }\nfunc (xdrProc_CB_COMPOUND) ProcName() string { return \"CB_COMPOUND\" }\nfunc (p *xdrProc_CB_COMPOUND) GetArg() XdrType {\n\tif p.Arg == nil {\n\t\tp.Arg = new(CB_COMPOUND4args)\n\t}\n\treturn XDR_CB_COMPOUND4args(p.Arg)\n}\nfunc (p *xdrProc_CB_COMPOUND) GetRes() XdrType {\n\tif p.Res == nil {\n\t\tp.Res = new(CB_COMPOUND4res)\n\t}\n\treturn XDR_CB_COMPOUND4res(p.Res)\n}\nvar _ XdrProc = &xdrProc_CB_COMPOUND{} // XXX\n\ntype xdrSrvProc_CB_COMPOUND struct {\n\txdrProc_CB_COMPOUND\n\tSrv NFS_CB\n}\nfunc (p *xdrSrvProc_CB_COMPOUND) SetContext(ctx context.Context) {\n\tif wc, ok := p.Srv.(interface {\n\t\tWithContext(context.Context) NFS_CB\n\t}); ok {\n\t\tp.Srv = wc.WithContext(ctx)\n\t}\n}\nfunc (p *xdrSrvProc_CB_COMPOUND) Do() {\n\tr := p.Srv.CB_COMPOUND(*p.Arg)\n\tp.Res = &r\n\n}\nvar _ XdrSrvProc = &xdrSrvProc_CB_COMPOUND{} // XXX\n\nfunc init() {\n\tXdrCatalog[1073741824<<32|1] = func(p uint32) XdrProc {\n\t\tswitch(p) {\n\t\tcase 0:\n\t\t\treturn &xdrProc_CB_NULL{}\n\t\tcase 1:\n\t\t\treturn &xdrProc_CB_COMPOUND{}\n\t\t}\n\t\treturn nil\n\t}\n}\n\ntype NFS_CB_Server struct {\n\tSrv NFS_CB\n}\nfunc (NFS_CB_Server) Prog() uint32 { return 1073741824 }\nfunc (NFS_CB_Server) Vers() uint32 { return 1 }\nfunc (NFS_CB_Server) ProgName() string { return \"NFS4_CALLBACK\" }\nfunc (NFS_CB_Server) VersName() string { return \"NFS_CB\" }\nfunc (s NFS_CB_Server) GetProc(p uint32) XdrSrvProc {\n\tswitch p {\n\tcase 0:  // CB_NULL\n\t\treturn &xdrSrvProc_CB_NULL{ Srv: s.Srv }\n\tcase 1:  // CB_COMPOUND\n\t\treturn &xdrSrvProc_CB_COMPOUND{ Srv: s.Srv }\n\tdefault:\n\t\treturn nil\n\t}\n}\nvar _ XdrSrv = NFS_CB_Server{} // XXX\n\ntype NFS_CB_Client struct {\n\tSend XdrSendCall\n\tCtx context.Context\n}\nvar _ NFS_CB = NFS_CB_Client{} // XXX\nfunc (c NFS_CB_Client) WithContext(ctx context.Context) NFS_CB {\n\tc.Ctx = ctx\n\treturn c\n}\nfunc (c NFS_CB_Client) CB_NULL() {\n\tvar proc xdrProc_CB_NULL\n\tif err := c.Send.SendCall(c.Ctx, &proc); err != nil {\n\t\tpanic(err)\n\t}\n}\nfunc (c NFS_CB_Client) CB_COMPOUND(a1 CB_COMPOUND4args) CB_COMPOUND4res {\n\tvar proc xdrProc_CB_COMPOUND\n\tproc.Arg = &a1\n\tif err := c.Send.SendCall(c.Ctx, &proc); err != nil {\n\t\tpanic(err)\n\t}\n\treturn *proc.Res\n}\n\n//\n// begin boilerplate\n//\n\n// Types passed to the XDR Marshal method implementing the XdrType\n// interface.  Pointers to some generated types T already implement\n// the interface.  However, since built-in types such as string and\n// int32 cannot implement interfaces, each type T has a function\n// XDR_T(*T) that returns some type implementing XdrType.  These\n// XdrType interfaces also encode limits on string and vector sizes\n// and simplify code by collapsing similar cases (e.g., all 32-bit\n// numbers are cast to a pointer implementing XdrNum32).\n//\n// For most XdrType instances, XdrPointer() and XdrValue() provide\n// access to the underlying type (which might not implement XdrType),\n// with the following exceptions:\n//\n// * opaque[] (fixed-length arrays of bytes) are returned as\n// XdrArrayOpaque (by XdrValue()) or nil (by XdrPointer()).  This is\n// because XdrArrayOpaque wraps a byte slice rather than an actual\n// array.  One generally doesn't want to pass arrays around; moreover,\n// getting a pointer to the actual array provides less information,\n// because one can't test for arrays in a type switch without knowing\n// the exact length of the array, while one can always dynamically\n// test the length of a slice.\n//\n// * Pointer types, in their XdrRecurse methods, marshal a special\n// XdrNum32 type of 0 or 1 to record whether the pointer is nil or the\n// value is present.  Since this bool does not exist, XdrPointer()\n// returns nil (while XdrValue returns a bool).\n//\n// * For arrays, XdrValue() passes a slice, rather than the full\n// array, so as to avoid copying the array.\ntype XdrType interface {\n\t// Return a string describing the name of the type (which will\n\t// reflect the name of a typedef).\n\tXdrTypeName() string\n\n\t// Return the underlying native representation of an XdrType\n\t// (e.g., int32 for an XdrInt32).\n\tXdrValue() interface{}\n\n\t// Return a pointer to the underlying native representation of an\n\t// XdrType (often just the same type as the XdrType, but sometimes\n\t// not, e.g., for primitive types that can't have methods\n\t// defined).\n\tXdrPointer() interface{}\n\n\t// Calls x.Marshal(name, v).\n\tXdrMarshal(XDR, string)\n}\n\n// The interface through which values are serialized, printed, etc.\ntype XDR interface {\n\t// A function that gets called for each value to be marshalled.\n\t// The val XdrType argument will be one of the following:\n\t//\n\t// * A pointer type implementing XdrNum32 for bool, int, unsigned\n\t//   int, float, the size of variable-length arrays except string\n\t//   and opaque, and enums.  In the case of enums, that instance\n\t//   will just be a pointer to the enum.  In the case of the other\n\t//   types, it will be a pointer to a defined type that implements\n\t//   the XdrNum32 interface (since plain go types cannot implement\n\t//   methods)--e.g., *XdrUint32 for unsigned int.  Variable array\n\t//   sizes are passed as *XdrSize, which enforces the bound.\n\t//\n\t// * A pointer type implementing XdrNum64 for hyper, unsigned\n\t//   hyper, and double.  These are user-defined versions of int64,\n\t//   uint64, and float64, called XdrInt64, XdrUint64, and\n\t//   XdrFloat64, respectively.\n\t//\n\t// * An instance of XdrBytes for strings and opaque.\n\t//   Specifically, strings are passed as XdrString, and\n\t//   variable-length opaque<> vectors are passed as XdrVecOpaque,\n\t//   both of which implement XdrVarBytes.\n\t//\n\t// * An XdrArrayOpaque containing a slice referencing a byte array\n\t//   for fixed-length opaque[].  XdrArrayOpaque implements\n\t//   XdrBytes, but not XdrVarBytes.\n\t//\n\t// * An instance of XdrAggregate for structs, unions, and\n\t//   pointers.  Note that structs and unions are just passed as\n\t//   pointers to the underlying structures (which all implement\n\t//   XdrAggregate).  Pointers are passed as an XdrPtr interface\n\t//   implemented by a defined pointer type (since plain pointers\n\t//   cannot have methods).\n\t//\n\t// Note that the Marshal method is responsible for recursing into\n\t// XdrAggregate instance by calling the XdrRecurse method.\n\t// Requiring the Marshal method to recurse manually allows it to\n\t// refrain from recursing in cases where it needs to special-case\n\t// the handling of specific types.\n\tMarshal(name string, val XdrType)\n\n\t// This method should just be fmt.Sprintf for XDRs that use name.\n\t// Those that don't use name can use a trivial method returning \"\"\n\tSprintf(string, ...interface{}) string\n}\n\n// The error thrown by marshaling functions when data has a bad value.\ntype XdrError string\nfunc (v XdrError) Error() string { return string(v) }\n\n// Throws an XdrError.\nfunc XdrPanic(s string, args ...interface{}) {\n\tpanic(XdrError(fmt.Sprintf(s, args...)))\n}\n\n// RFC4506 defines bool as equivalent to an enum with values all-caps\n// TRUE and FALSE.  For convenience, we represent an XDR bool as a Go\n// bool instead, and so define these constants for use in union cases.\n// (XDR source files should not use lower-case true and false, as\n// these will not work in other languages or with other XDR\n// compilers.)\nconst (\n\tTRUE = true\n\tFALSE = false\n)\n\n// Unfortunately, you can't cast a bool to an int.  Hence, in\n// situations like a union discriminant where we don't know if a type\n// is equivalent to bool or not, then, short of using reflection, this\n// is how we have to convert to an integer.  Note that this expects\n// types int, [u]int32, enum, and bool, rather than pointers to these\n// types.\nfunc XdrToI32(i interface{}) int32 {\n\tswitch v := i.(type) {\n\tcase interface{ GetU32() uint32 }:\n\t\treturn int32(v.GetU32())\n\tcase int32:\n\t\treturn v\n\tcase uint32:\n\t\treturn int32(v)\n\tcase int:\n\t\treturn int32(v)\n\tcase bool:\n\t\tif v {\n\t\t\treturn 1\n\t\t}\n\t\treturn 0\n\t}\n\tpanic(XdrError(fmt.Sprintf(\"XdrToI32: bad type %T\", i)))\n}\n\n//\n// User-defined types used to place methods on basic types\n//\n\n// All quantities that should be serialized as 32-bit numbers\n// (including bools, enums, union discriminants, the bit saying\n// whether or not a pointer is NULL, floats, and vector lenghts) are\n// passed to the XDR.Marshal function as a pointer to a defined type\n// implementing the XdrNum32 interface.  The one exception is string<>\n// and opaque<>, for which it is the job of XDR.Marshal to serialize\n// the 32-bit length.\ntype XdrNum32 interface {\n\tXdrType\n\tfmt.Stringer\n\tfmt.Scanner\n\tGetU32() uint32\n\tSetU32(uint32)\n}\n\n// Enums additionally provide access to a map of values to names.  You\n// generally don't need to invoke the XdrEnumNames() method (since the\n// String and Scan methods already access the underlying maps), but\n// the presence of this method can be useful to differentiate enums\n// from other XdrNum32 types.\ntype XdrEnum interface {\n\tXdrNum32\n\tXdrEnumNames() map[int32]string\n}\n\n// All 64-bit numbers (hyper, unsigned hyper, and double) are passed\n// to XDR.Marshal as a pointer to a defined type implementing\n// XdrNum64.\ntype XdrNum64 interface {\n\tXdrType\n\tfmt.Stringer\n\tfmt.Scanner\n\tGetU64() uint64\n\tSetU64(uint64)\n}\n\n// A common interface implemented by XdrString, XdrVecOpaque, and\n// XdrArrayOpaque (and hence used to operate on the XdrTypes\n// corresponding to value os ftype string<>, opaque<>, and opaque[]).\ntype XdrBytes interface {\n\tXdrType\n\tGetByteSlice() []byte\n}\n\n// A common interface implemented by XdrString and XdrVecOpaque, the\n// XdrTypes for opaque<> and string<>.  Since XdrVarBytes is a subtype\n// of XdrBytes, Marshal functions that want to distinguish between\n// opaque<> and opaque[] should first try a type assertion for\n// XdrVarBytes (to check for opaque<>) and then if that fails try\n// XdrBytes (to check for opaque[]).\ntype XdrVarBytes interface {\n\tXdrBytes\n\tXdrBound() uint32\n\tSetByteSlice([]byte)\n}\n\n// Any struct, union, pointer, or variable-length array type (except\n// opaque<> and string<>) is passed to XDR.Marshal as a pointer\n// implementing the XdrAggregate interface.  It is the responsibility\n// of the XDR.Marshal function to call the XdrRecurse method so as to\n// recurse into the data structure.  Placing reponsibility on\n// XDR.Marshal for recursing allows a custom XDR to prune the\n// serialization at particular types (e.g., for pretty-printing a\n// partucular struct in a non-standard way).\ntype XdrAggregate interface {\n\tXdrType\n\tXdrRecurse(XDR, string)\n}\n\n// A union type is an XdrAggregate with extra methods for individually\n// accessing the tag and active branch.\ntype XdrUnion interface {\n\tXdrAggregate\n\tXdrValid() bool\n\tXdrValidTags() map[int32]bool\n\tXdrUnionTag() XdrNum32\n\tXdrUnionTagName() string\n\tXdrUnionBody() XdrType\n\tXdrUnionBodyName() string\n}\n\n// Any pointer type is converted to an XdrType implementing the XdrPtr\n// interface for marshaling.  Note XdrPtr is a subtype of\n// XdrAggregate.  When a Marshal function does nothing special for\n// XdrPtr and treats the XdrPtr like any other XdrAggregate (calling\n// XdrRecurse), the Marshal function will then get called one or two\n// more times, first with an XdrSize (implementing XdrNum32) to\n// marshal the non-NULL bit, and then again with the underlying value\n// if the pointer is non-NULL.  An XDR.Marshal function that wants to\n// special case pointers can access the present bit from the\n// GetPresent and SetPresent methods, then bypass marshaling of the\n// bit by calling XdrMarshalValue instead of XdrRecurse.\ntype XdrPtr interface {\n\t// Marshals first the present/not-present bool, then if true, the\n\t// underlying value.\n\tXdrAggregate\n\n\tGetPresent() bool\n\tSetPresent(bool)\n\n\t// If the present/not-present bool is false, this function does\n\t// nothing.  Otherwise, it marshals just the value, not the bit.\n\tXdrMarshalValue(XDR, string)\n}\n\n// Any vector type is passed as a pointer to a user-defined slice type\n// that implements the XdrVec interface.  XdrVec is a superset of\n// XdrAggregate, so calling XdrRecurse will recurse to call\n// XDR.Marshal first on the size (of type XdrSize), then on each\n// element of the slice.  An XDR.Marshal function can manually marshal\n// the size and then call XdrMarshalN to marshal n vector elements.\n// (When deserializing, it is advisable *not* to call SetVecLen before\n// calling XdrMarshalN in case the length is huge and would exhaust\n// memory; XdrMarshalN gradually grows the slice size as needed so as\n// to throw a premature EOF error if N is larger than the actual input\n// data.)\ntype XdrVec interface {\n\tXdrAggregate\n\tXdrBound() uint32\n\tGetVecLen() uint32\n\tSetVecLen(uint32)\n\tXdrMarshalN(XDR, string, uint32)\n}\n\n// Any array type is passed as a pointer to a user-defined array\n// supporting the XdrArray interface.  This is a subtype of\n// XdrAggregate, and so supports recursing to marshal each individual\n// array member.\ntype XdrArray interface {\n\tXdrAggregate\n\tXdrArraySize() uint32\n}\n\ntype XdrTypedef interface {\n\tXdrType\n\tXdrUnwrap() XdrType\n}\n\n// Unwrap typedefs to obtain the underlying XdrType.\nfunc XdrBaseType(v XdrType) XdrType {\n\tfor {\n\t\tt, ok := v.(XdrTypedef)\n\t\tif !ok {\n\t\t\treturn v\n\t\t}\n\t\tv = t.XdrUnwrap()\n\t}\n}\n\n// Returns true for A-Z, a-z, 0-9, and _.  (Used by the generated code\n// that implements Scanner for enums.)\nfunc XdrSymChar(r rune) bool {\n\treturn (r|0x20 >= 'a' && r|0x20 <= 'z') ||\n\t\t(r >= '0' && r <= '9') || r == '_'\n}\n\n// An XdrType that marshals as zero bytes, and hence is used to\n// represent void argument and return types of functions.\ntype XdrVoid struct{}\ntype XdrType_void = *XdrVoid\nfunc (XdrVoid) XdrTypeName() string { return \"void\" }\nfunc (XdrVoid) XdrValue() interface{} { return nil }\nfunc (XdrVoid) XdrPointer() interface{} { return nil }\nfunc (XdrVoid) XdrMarshal(XDR, string) {}\nfunc XDR_XdrVoid(v *XdrVoid) XdrType_void { return v }\n\n// The XdrType that bool gets turned into for marshaling.\ntype XdrBool bool\ntype XdrType_bool = *XdrBool\nfunc (XdrBool) XdrTypeName() string { return \"bool\" }\nfunc (v XdrBool) String() string { return fmt.Sprintf(\"%v\", v.XdrValue()) }\nfunc (v *XdrBool) Scan(ss fmt.ScanState, r rune) error {\n\t_, err := fmt.Fscanf(ss, string([]rune{'%', r}), v.XdrPointer())\n\treturn err\n}\nfunc (v XdrBool) GetU32() uint32 { if v { return 1 }; return 0 }\nfunc (v *XdrBool) SetU32(nv uint32) {\n\tswitch nv {\n\tcase 0:\n\t\t*v = false\n\tcase 1:\n\t\t*v = true\n\tdefault:\n\t\tXdrPanic(\"bool must be 0 or 1\")\n\t}\n}\nfunc (v *XdrBool) XdrPointer() interface{} { return (*bool)(v) }\nfunc (v XdrBool) XdrValue() interface{} { return bool(v) }\nfunc (v *XdrBool) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc XDR_bool(v *bool) *XdrBool { return (*XdrBool)(v) }\n\n// The XdrType that int gets converted to for marshaling.\ntype XdrInt32 int32\ntype XdrType_int32 = *XdrInt32\nfunc (XdrInt32) XdrTypeName() string { return \"int32\" }\nfunc (v XdrInt32) String() string { return fmt.Sprintf(\"%v\", v.XdrValue()) }\nfunc (v *XdrInt32) Scan(ss fmt.ScanState, r rune) error {\n\t_, err := fmt.Fscanf(ss, string([]rune{'%', r}), v.XdrPointer())\n\treturn err\n}\nfunc (v XdrInt32) GetU32() uint32 { return uint32(v) }\nfunc (v *XdrInt32) SetU32(nv uint32) { *v = XdrInt32(nv) }\nfunc (v *XdrInt32) XdrPointer() interface{} { return (*int32)(v) }\nfunc (v XdrInt32) XdrValue() interface{} { return int32(v) }\nfunc (v *XdrInt32) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc XDR_int32(v *int32) *XdrInt32 { return (*XdrInt32)(v) }\n\n// The XdrType that unsigned int gets converted to for marshaling.\ntype XdrUint32 uint32\ntype XdrType_uint32 = *XdrUint32\nfunc (XdrUint32) XdrTypeName() string { return \"uint32\" }\nfunc (v XdrUint32) String() string { return fmt.Sprintf(\"%v\", v.XdrValue()) }\nfunc (v *XdrUint32) Scan(ss fmt.ScanState, r rune) error {\n\t_, err := fmt.Fscanf(ss, string([]rune{'%', r}), v.XdrPointer())\n\treturn err\n}\nfunc (v XdrUint32) GetU32() uint32 { return uint32(v) }\nfunc (v *XdrUint32) SetU32(nv uint32) { *v = XdrUint32(nv) }\nfunc (v *XdrUint32) XdrPointer() interface{} { return (*uint32)(v) }\nfunc (v XdrUint32) XdrValue() interface{} { return uint32(v) }\nfunc (v *XdrUint32) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc XDR_uint32(v *uint32) *XdrUint32 { return (*XdrUint32)(v) }\n\n// The XdrType that float gets converted to for marshaling.\ntype XdrFloat32 float32\ntype XdrType_float32 = *XdrFloat32\nfunc (XdrFloat32) XdrTypeName() string { return \"float32\" }\nfunc (v XdrFloat32) String() string { return fmt.Sprintf(\"%v\", v.XdrValue()) }\nfunc (v *XdrFloat32) Scan(ss fmt.ScanState, r rune) error {\n\t_, err := fmt.Fscanf(ss, string([]rune{'%', r}), v.XdrPointer())\n\treturn err\n}\nfunc (v XdrFloat32) GetU32() uint32 { return math.Float32bits(float32(v)) }\nfunc (v *XdrFloat32) SetU32(nv uint32) {\n\t*v = XdrFloat32(math.Float32frombits(nv))\n}\nfunc (v *XdrFloat32) XdrPointer() interface{} { return (*float32)(v) }\nfunc (v XdrFloat32) XdrValue() interface{} { return float32(v) }\nfunc (v *XdrFloat32) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc XDR_float32(v *float32) *XdrFloat32 { return (*XdrFloat32)(v) }\n\n// This XdrType is used to marshal the length of vectors and the\n// present/not-present value of pointers.\ntype XdrSize struct {\n\tSize uint32\n\tBound uint32\n}\nfunc (v XdrSize) String() string { return fmt.Sprintf(\"%v\", v.Size) }\nfunc (v *XdrSize) Scan(ss fmt.ScanState, r rune) (err error) {\n\tdefer func() {\n\t\tif err == nil {\n\t\t\terr = recover().(error)\n\t\t}\n\t}()\n\tvar nv uint32\n\tif _, err := fmt.Fscanf(ss, string([]rune{'%', r}), &nv); err != nil {\n\t\treturn err\n\t}\n\tv.SetU32(nv)\n\treturn\n}\nfunc (v XdrSize) GetU32() uint32 { return v.Size }\nfunc (v *XdrSize) SetU32(nv uint32) {\n\tif nv > v.Bound {\n\t\tXdrPanic(\"size %d greater than bound %d\", nv, v.Bound)\n\t} else if int(nv) < 0 {\n\t\tXdrPanic(\"size %d greater than max slice len\", nv)\n\t}\n\tv.Size = nv\n}\nfunc (XdrSize) XdrTypeName() string { return \"len\" }\nfunc (v *XdrSize) XdrPointer() interface{} { return &v.Size }\nfunc (v XdrSize) XdrValue() interface{} { return v.Size }\nfunc (v *XdrSize) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v XdrSize) XdrBound() uint32 { return v.Bound }\n\n// The XdrType that hyper gets converted to for marshaling.\ntype XdrInt64 int64\ntype XdrType_int64 = *XdrInt64\nfunc (XdrInt64) XdrTypeName() string { return \"int64\" }\nfunc (v XdrInt64) String() string { return fmt.Sprintf(\"%v\", v.XdrValue()) }\nfunc (v *XdrInt64) Scan(ss fmt.ScanState, r rune) error {\n\t_, err := fmt.Fscanf(ss, string([]rune{'%', r}), v.XdrPointer())\n\treturn err\n}\nfunc (v XdrInt64) GetU64() uint64 { return uint64(v) }\nfunc (v *XdrInt64) SetU64(nv uint64) { *v = XdrInt64(nv) }\nfunc (v *XdrInt64) XdrPointer() interface{} { return (*int64)(v) }\nfunc (v XdrInt64) XdrValue() interface{} { return int64(v) }\nfunc (v *XdrInt64) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc XDR_int64(v *int64) *XdrInt64 { return (*XdrInt64)(v) }\n\n// The XdrType that unsigned hyper gets converted to for marshaling.\ntype XdrUint64 uint64\ntype XdrType_uint64 = *XdrUint64\nfunc (XdrUint64) XdrTypeName() string { return \"uint64\" }\nfunc (v XdrUint64) String() string { return fmt.Sprintf(\"%v\", v.XdrValue()) }\nfunc (v *XdrUint64) Scan(ss fmt.ScanState, r rune) error {\n\t_, err := fmt.Fscanf(ss, string([]rune{'%', r}), v.XdrPointer())\n\treturn err\n}\nfunc (v XdrUint64) GetU64() uint64 { return uint64(v) }\nfunc (v *XdrUint64) SetU64(nv uint64) { *v = XdrUint64(nv) }\nfunc (v *XdrUint64) XdrPointer() interface{} { return (*uint64)(v) }\nfunc (v XdrUint64) XdrValue() interface{} { return uint64(v) }\nfunc (v *XdrUint64) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc XDR_uint64(v *uint64) *XdrUint64 { return (*XdrUint64)(v) }\n\n// The XdrType that double gets converted to for marshaling.\ntype XdrFloat64 float64\ntype XdrType_float64 = *XdrFloat64\nfunc (XdrFloat64) XdrTypeName() string { return \"float64\" }\nfunc (v XdrFloat64) String() string { return fmt.Sprintf(\"%v\", v.XdrValue()) }\nfunc (v *XdrFloat64) Scan(ss fmt.ScanState, r rune) error {\n\t_, err := fmt.Fscanf(ss, string([]rune{'%', r}), v.XdrPointer())\n\treturn err\n}\nfunc (v XdrFloat64) GetU64() uint64 { return math.Float64bits(float64(v)) }\nfunc (v *XdrFloat64) SetU64(nv uint64) {\n\t*v = XdrFloat64(math.Float64frombits(nv))\n}\nfunc (v *XdrFloat64) XdrPointer() interface{} { return (*float64)(v) }\nfunc (v XdrFloat64) XdrValue() interface{} { return float64(v) }\nfunc (v *XdrFloat64) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc XDR_float64(v *float64) *XdrFloat64 { return (*XdrFloat64)(v) }\n\n// The XdrType that strings get converted to for marshaling.\ntype XdrString struct {\n\tStr *string\n\tBound uint32\n}\nfunc (v XdrString) String() string { return fmt.Sprintf(\"%q\", *v.Str) }\nfunc (v XdrString) Scan(ss fmt.ScanState, _ rune) error {\n\tvar s string\n\tif _, err := fmt.Fscanf(ss, \"%q\", &s); err != nil {\n\t\treturn err\n\t} else if int64(len(s)) > int64(v.Bound) {\n\t\treturn XdrError(fmt.Sprintf(\"Cannot store %d bytes in string<%d>\",\n\t\t\tlen(s), v.Bound))\n\t}\n\t*v.Str = s\n\treturn nil\n}\nfunc (v XdrString) XdrBound() uint32 { return v.Bound }\nfunc (v XdrString) GetString() string { return *v.Str }\nfunc (v XdrString) SetString(s string) {\n\tif uint(len(s)) > uint(v.Bound) {\n\t\tXdrPanic(\"Cannot store %d bytes in string<%d>\", len(s), v.Bound)\n\t}\n\t*v.Str = s\n}\nfunc (v XdrString) GetByteSlice() []byte { return ([]byte)(*v.Str) }\nfunc (v XdrString) SetByteSlice(bs []byte) {\n\tif uint(len(bs)) > uint(v.Bound) {\n\t\tXdrPanic(\"Cannot store %d bytes in string<%d>\", len(bs), v.Bound)\n\t}\n\t*v.Str = string(bs)\n}\nfunc (XdrString) XdrTypeName() string { return \"string\" }\nfunc (v XdrString) XdrPointer() interface{} { return v.Str }\nfunc (v XdrString) XdrValue() interface{} { return *v.Str }\nfunc (v XdrString) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\n\n// The XdrType that opaque<> gets converted to for marshaling.\ntype XdrVecOpaque struct {\n\tBytes *[]byte\n\tBound uint32\n}\nfunc (v XdrVecOpaque) String() string {\n\treturn fmt.Sprintf(\"%x\", []byte(*v.Bytes))\n}\nfunc (v XdrVecOpaque) Scan(ss fmt.ScanState, _ rune) error {\n\tvar bs []byte\n\t_, err := fmt.Fscanf(ss, \"%x\", &bs)\n\tif err == nil {\n\t\tv.SetByteSlice(bs)\n\t}\n\treturn err\n}\nfunc (v XdrVecOpaque) GetByteSlice() []byte { return *v.Bytes }\nfunc (v XdrVecOpaque) XdrBound() uint32 { return v.Bound }\nfunc (v XdrVecOpaque) SetByteSlice(bs []byte) {\n\tif uint(len(bs)) > uint(v.Bound) {\n\t\tXdrPanic(\"Cannot store %d bytes in string<%d>\", len(bs), v.Bound)\n\t}\n\t*v.Bytes = bs\n}\nfunc (XdrVecOpaque) XdrTypeName() string { return \"opaque<>\" }\nfunc (v XdrVecOpaque) XdrPointer() interface{} { return v.Bytes }\nfunc (v XdrVecOpaque) XdrValue() interface{} { return *v.Bytes }\nfunc (v XdrVecOpaque) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\n\n// Helper function for scanning fix-size opaque arrays.\nfunc XdrArrayOpaqueScan(dest []byte, ss fmt.ScanState, _ rune) error {\n\tvar bs []byte\n\t_, err := fmt.Fscanf(ss, \"%x\", &bs)\n\tif err != nil {\n\t\treturn err\n\t} else if len(bs) != len (dest) {\n\t\treturn XdrError(\"Wrong number of bytes when scanning opaque[]\")\n\t}\n\tcopy(dest, bs)\n\treturn nil\n}\n\n// The interface of XdrTypes of opaque[n] for all n\ntype XdrArrayOpaque interface {\n\tXdrBytes\n\tXdrArraySize() uint32\n}\n\n\n//\n// Basic implementations of XDR interface\n//\n\n// Back end that renders an XDR data structure as simple text.\n// Example:\n//\n//    XDR_MyType(&myVal).XdrMarshal(&XdrPrint{os.Stdout}, \"\")\n//\ntype XdrPrint struct {\n\tOut io.Writer\n}\n\n// Generic function that converts any compiled XDR type to a\n// multi-line string.  Useful for debugging.\nfunc XdrToString(t XdrType) string {\n\tout := &strings.Builder{}\n\tt.XdrMarshal(&XdrPrint{out}, \"\")\n\treturn out.String()\n}\n\nfunc (xp XdrPrint) Sprintf(f string, args ...interface{}) string {\n\treturn fmt.Sprintf(f, args...)\n}\n\nfunc (xp XdrPrint) Marshal(name string, i XdrType) {\n\tswitch v := i.(type) {\n\tcase fmt.Stringer:\n\t\tfmt.Fprintf(xp.Out, \"%s: %s\\n\", name, v.String())\n\tcase XdrPtr:\n\t\tfmt.Fprintf(xp.Out, \"%s.present: %v\\n\", name, v.GetPresent())\n\t\tv.XdrMarshalValue(xp, fmt.Sprintf(\"(*%s)\", name))\n\tcase XdrVec:\n\t\tfmt.Fprintf(xp.Out, \"%s.len: %d\\n\", name, v.GetVecLen())\n\t\tv.XdrMarshalN(xp, name, v.GetVecLen())\n\tcase XdrAggregate:\n\t\tv.XdrRecurse(xp, name)\n\tdefault:\n\t\tfmt.Fprintf(xp.Out, \"%s: %v\\n\", name, i)\n\t}\n}\n\nvar xdrZerofill [4][]byte = [...][]byte{\n\t{}, {0,0,0}, {0,0}, {0},\n}\n\nfunc xdrPutBytes(out io.Writer, val []byte) {\n\tout.Write(val)\n\tout.Write(xdrZerofill[len(val)&3])\n}\n\nfunc xdrPut32(out io.Writer, val uint32) {\n\tb := make([]byte, 4)\n\tbinary.BigEndian.PutUint32(b, val)\n\tout.Write(b)\n}\n\nfunc xdrPut64(out io.Writer, val uint64) {\n\tb := make([]byte, 8)\n\tbinary.BigEndian.PutUint64(b, val)\n\tout.Write(b)\n}\n\n\n// XDR that marshals to canonical binary format\ntype XdrOut struct {\n\tOut io.Writer\n}\n\nfunc (xp XdrOut) Sprintf(f string, args ...interface{}) string {\n\treturn \"\"\n}\n\nfunc (xo XdrOut) Marshal(name string, i XdrType) {\n\tswitch v := i.(type) {\n\tcase XdrNum32:\n\t\txdrPut32(xo.Out, v.GetU32())\n\tcase XdrNum64:\n\t\txdrPut64(xo.Out, v.GetU64())\n\tcase XdrString:\n\t\ts := v.GetString()\n\t\txdrPut32(xo.Out, uint32(len(s)))\n\t\tio.WriteString(xo.Out, s)\n\t\txo.Out.Write(xdrZerofill[len(s)&3])\n\tcase XdrVarBytes:\n\t\txdrPut32(xo.Out, uint32(len(v.GetByteSlice())))\n\t\txdrPutBytes(xo.Out, v.GetByteSlice())\n\tcase XdrBytes:\n\t\txdrPutBytes(xo.Out, v.GetByteSlice())\n\tcase XdrAggregate:\n\t\tv.XdrRecurse(xo, name)\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"XdrOut: unhandled type %T\", i))\n\t}\n}\n\nfunc xdrReadN(in io.Reader, n uint32) []byte {\n\tvar b bytes.Buffer\n\tif _, err := io.CopyN(&b, in, int64(n)); err != nil {\n\t\tXdrPanic(err.Error())\n\t}\n\treturn b.Bytes()\n}\n\nfunc xdrReadPad(in io.Reader, n uint32) {\n\tif n & 3 != 0 {\n\t\tgot := xdrReadN(in, 4-(n&3))\n\t\tfor _, b := range got {\n\t\t\tif b != 0 {\n\t\t\t\tXdrPanic(\"padding contained non-zero bytes\")\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc xdrGet32(in io.Reader) uint32 {\n\tb := xdrReadN(in, 4)\n\treturn binary.BigEndian.Uint32(b)\n}\n\nfunc xdrGet64(in io.Reader) uint64 {\n\tb := xdrReadN(in, 8)\n\treturn binary.BigEndian.Uint64(b)\n}\n\n\n// XDR that unmarshals from canonical binary format\ntype XdrIn struct {\n\tIn io.Reader\n}\n\nfunc (xp XdrIn) Sprintf(f string, args ...interface{}) string {\n\treturn \"\"\n}\n\nfunc (xi XdrIn) Marshal(name string, i XdrType) {\n\tswitch v := i.(type) {\n\tcase XdrNum32:\n\t\tv.SetU32(xdrGet32(xi.In))\n\tcase XdrNum64:\n\t\tv.SetU64(xdrGet64(xi.In))\n\tcase XdrVarBytes:\n\t\tn := xdrGet32(xi.In)\n\t\tv.SetByteSlice(xdrReadN(xi.In, n))\n\t\txdrReadPad(xi.In, n)\n\tcase XdrBytes:\n\t\tif _, err := io.ReadFull(xi.In, v.GetByteSlice()); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\txdrReadPad(xi.In, uint32(len(v.GetByteSlice())))\n\tcase XdrAggregate:\n\t\tv.XdrRecurse(xi, name)\n\t}\n}\n\n\n//\n// RFC5531 RPC support\n//\n\n// Interface for a data structure containing the argument and result\n// types of a call.\ntype XdrProc interface {\n\tProg() uint32\n\tVers() uint32\n\tProc() uint32\n\tProgName() string\n\tVersName() string\n\tProcName() string\n\tGetArg() XdrType\n\tGetRes() XdrType\n}\n\n// Interface for a data structure containing argument and results of a\n// procedure plus a way to invoke the procedure on an instance of the\n// RPC version.  In other words, Do() actually performs the work of an\n// RPC on the server side.\ntype XdrSrvProc interface {\n\tXdrProc\n\tDo()\n\tSetContext(context.Context)\n}\n\n// Interface for an RPC version interface bound to a server\n// implementation.  The result of GetProc() can be used to marshal the\n// arguments and results as well as actually invoke the function\n// (though Do()).\ntype XdrSrv interface {\n\tProg() uint32\n\tVers() uint32\n\tProgName() string\n\tVersName() string\n\tGetProc(uint32) XdrSrvProc\n}\n\n// An interface for types that can send remote procedure calls and\n// await their reply.\ntype XdrSendCall interface {\n\tSendCall(context.Context, XdrProc) error\n}\n\n// A catalog of procedures of different RPC programs and versions,\n// mostly useful for debugging (e.g., pretty-printing a log of RPC\n// messages).  The key to the map is prog<<32|vers.  The function\n// returns a new instance of an appropriate XdrProc.\nvar XdrCatalog = make(map[uint64]func(uint32)XdrProc)\n\n//\n// end boilerplate\n//\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs4/repo/internal/nfs4.x",
    "content": "/* This is based on RFC3530 */\n\n/*\n * NFS v4 Definitions\n */\n\n\n/*\n *  Copyright (C) The Internet Society (1998,1999,2000,2001,2002).\n *  All Rights Reserved.\n */\n\n/*\n *      nfs4_prot.x\n *\n */\n\n/*\n * Sizes\n */\nconst NFS4_FHSIZE               = 128;\nconst NFS4_VERIFIER_SIZE        = 8;\nconst NFS4_OPAQUE_LIMIT         = 1024;\nconst NFS4_SESSIONID_SIZE       = 16;\n/*\n * File types\n */\nenum nfs_ftype4 {\n     NF4REG          = 1,    /* Regular File */\n     NF4DIR          = 2,    /* Directory */\n     NF4BLK          = 3,    /* Special File - block device */\n     NF4CHR          = 4,    /* Special File - character device */\n     NF4LNK          = 5,    /* Symbolic Link */\n     NF4SOCK         = 6,    /* Special File - socket */\n     NF4FIFO         = 7,    /* Special File - fifo */\n     NF4ATTRDIR      = 8,    /* Attribute Directory */\n     NF4NAMEDATTR    = 9     /* Named Attribute */\n};\n\n/*\n * Error status\n */\nenum nfsstat4 {\n     NFS4_OK                 = 0,    /* everything is okay      */\n     NFS4ERR_PERM            = 1,    /* caller not privileged   */\n     NFS4ERR_NOENT           = 2,    /* no such file/directory  */\n     NFS4ERR_IO              = 5,    /* hard I/O error          */\n     NFS4ERR_NXIO            = 6,    /* no such device          */\n     NFS4ERR_ACCESS          = 13,   /* access denied           */\n     NFS4ERR_EXIST           = 17,   /* file already exists     */\n     NFS4ERR_XDEV            = 18,   /* different filesystems   */\n     /* Unused/reserved        19 */\n     NFS4ERR_NOTDIR          = 20,   /* should be a directory   */\n     NFS4ERR_ISDIR           = 21,   /* should not be directory */\n     NFS4ERR_INVAL           = 22,   /* invalid argument        */\n     NFS4ERR_FBIG            = 27,   /* file exceeds server max */\n     NFS4ERR_NOSPC           = 28,   /* no space on filesystem  */\n     NFS4ERR_ROFS            = 30,   /* read-only filesystem    */\n     NFS4ERR_MLINK           = 31,   /* too many hard links     */\n     NFS4ERR_NAMETOOLONG     = 63,   /* name exceeds server max */\n     NFS4ERR_NOTEMPTY        = 66,   /* directory not empty     */\n     NFS4ERR_DQUOT           = 69,   /* hard quota limit reached*/\n     NFS4ERR_STALE           = 70,   /* file no longer exists   */\n     NFS4ERR_BADHANDLE       = 10001,/* Illegal filehandle      */\n     NFS4ERR_BAD_COOKIE      = 10003,/* READDIR cookie is stale */\n     NFS4ERR_NOTSUPP         = 10004,/* operation not supported */\n     NFS4ERR_TOOSMALL        = 10005,/* response limit exceeded */\n     NFS4ERR_SERVERFAULT     = 10006,/* undefined server error  */\n     NFS4ERR_BADTYPE         = 10007,/* type invalid for CREATE */\n     NFS4ERR_DELAY           = 10008,/* file \"busy\" - retry     */\n     NFS4ERR_SAME            = 10009,/* nverify says attrs same */\n     NFS4ERR_DENIED          = 10010,/* lock unavailable        */\n     NFS4ERR_EXPIRED         = 10011,/* lock lease expired      */\n     NFS4ERR_LOCKED          = 10012,/* I/O failed due to lock  */\n     NFS4ERR_GRACE           = 10013,/* in grace period         */\n     NFS4ERR_FHEXPIRED       = 10014,/* filehandle expired      */\n     NFS4ERR_SHARE_DENIED    = 10015,/* share reserve denied    */\n     NFS4ERR_WRONGSEC        = 10016,/* wrong security flavor   */\n     NFS4ERR_CLID_INUSE      = 10017,/* clientid in use         */\n     NFS4ERR_RESOURCE        = 10018,/* resource exhaustion     */\n     NFS4ERR_MOVED           = 10019,/* filesystem relocated    */\n     NFS4ERR_NOFILEHANDLE    = 10020,/* current FH is not set   */\n     NFS4ERR_MINOR_VERS_MISMATCH = 10021,/* minor vers not supp */\n     NFS4ERR_STALE_CLIENTID  = 10022,/* server has rebooted     */\n     NFS4ERR_STALE_STATEID   = 10023,/* server has rebooted     */\n     NFS4ERR_OLD_STATEID     = 10024,/* state is out of sync    */\n     NFS4ERR_BAD_STATEID     = 10025,/* incorrect stateid       */\n     NFS4ERR_BAD_SEQID       = 10026,/* request is out of seq.  */\n     NFS4ERR_NOT_SAME        = 10027,/* verify - attrs not same */\n     NFS4ERR_LOCK_RANGE      = 10028,/* lock range not supported*/\n     NFS4ERR_SYMLINK         = 10029,/* should be file/directory*/\n     NFS4ERR_RESTOREFH       = 10030,/* no saved filehandle     */\n     NFS4ERR_LEASE_MOVED     = 10031,/* some filesystem moved   */\n     NFS4ERR_ATTRNOTSUPP     = 10032,/* recommended attr not sup*/\n     NFS4ERR_NO_GRACE        = 10033,/* reclaim outside of grace*/\n     NFS4ERR_RECLAIM_BAD     = 10034,/* reclaim error at server */\n     NFS4ERR_RECLAIM_CONFLICT = 10035,/* conflict on reclaim    */\n     NFS4ERR_BADXDR          = 10036,/* XDR decode failed       */\n     NFS4ERR_LOCKS_HELD      = 10037,/* file locks held at CLOSE*/\n     NFS4ERR_OPENMODE        = 10038,/* conflict in OPEN and I/O*/\n     NFS4ERR_BADOWNER        = 10039,/* owner translation bad   */\n     NFS4ERR_BADCHAR         = 10040,/* utf-8 char not supported*/\n     NFS4ERR_BADNAME         = 10041,/* name not supported      */\n     NFS4ERR_BAD_RANGE       = 10042,/* lock range not supported*/\n     NFS4ERR_LOCK_NOTSUPP    = 10043,/* no atomic up/downgrade  */\n     NFS4ERR_OP_ILLEGAL      = 10044,/* undefined operation     */\n     NFS4ERR_DEADLOCK        = 10045,/* file locking deadlock   */\n     NFS4ERR_FILE_OPEN       = 10046,/* open file blocks op.    */\n     NFS4ERR_ADMIN_REVOKED   = 10047,/* lockowner state revoked */\n     NFS4ERR_CB_PATH_DOWN    = 10048,/* callback path down      */\n     NFS4ERR_BADIOMODE       = 10049,\n     NFS4ERR_BADLAYOUT       = 10050,\n     NFS4ERR_BAD_SESSION_DIGEST = 10051,\n     NFS4ERR_BADSESSION      = 10052,\n     NFS4ERR_BADSLOT         = 10053,\n     NFS4ERR_COMPLETE_ALREADY = 10054,\n     NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055,\n     NFS4ERR_DELEG_ALREADY_WANTED = 10056,\n     NFS4ERR_BACK_CHAN_BUSY  = 10057,\n     NFS4ERR_LAYOUTTRYLATER  = 10058,\n     NFS4ERR_LAYOUTUNAVAILABLE = 10059,\n     NFS4ERR_NOMATCHING_LAYOUT = 10060,\n     NFS4ERR_RECALLCONFLICT  = 10061\n};\n\n/*\n * Basic data types\n */\ntypedef uint32_t        bitmap4<>;\ntypedef uint64_t        offset4;\ntypedef uint32_t        count4;\ntypedef uint64_t        length4;\ntypedef uint64_t        clientid4;\ntypedef uint32_t\tsequenceid4;\ntypedef uint32_t        seqid4;\ntypedef uint32_t        slotid4;\ntypedef opaque          utf8string<>;\ntypedef utf8string      utf8str_cis;\ntypedef utf8string      utf8str_cs;\ntypedef utf8string      utf8str_mixed;\ntypedef utf8str_cs      component4;\ntypedef component4      pathname4<>;\ntypedef uint64_t        nfs_lockid4;\ntypedef uint64_t        nfs_cookie4;\ntypedef utf8str_cs      linktext4;\ntypedef opaque          sec_oid4<>;\ntypedef uint32_t        qop4;\ntypedef uint32_t        mode4;\ntypedef uint64_t        changeid4;\ntypedef opaque          verifier4[NFS4_VERIFIER_SIZE];\ntypedef opaque          sessionid4[NFS4_SESSIONID_SIZE];\n/*\n * Authsys_parms\n */\nstruct authsys_parms {\n     unsigned int stamp;\n     string machinename<255>;\n     unsigned int uid;\n     unsigned int gid;\n     unsigned int gids<16>;\n};\n\nconst NFS4_DEVICEID4_SIZE = 16;\n\ntypedef opaque  deviceid4[NFS4_DEVICEID4_SIZE];\n\nenum layouttype4 {\n       LAYOUT4_NFSV4_1_FILES   = 0x1,\n       LAYOUT4_OSD2_OBJECTS    = 0x2,\n       LAYOUT4_BLOCK_VOLUME    = 0x3\n};\n\nstruct layoutupdate4 {\n       layouttype4             lou_type;\n       opaque                  lou_body<>;\n};\n\n\nstruct device_addr4 {\n       layouttype4             da_layout_type;\n       opaque                  da_addr_body<>;\n};\n\n/*\n * Timeval\n */\nstruct nfstime4 {\n     int64_t         seconds;\n     uint32_t        nseconds;\n};\n\nenum time_how4 {\n     SET_TO_SERVER_TIME4 = 0,\n     SET_TO_CLIENT_TIME4 = 1\n};\n\nenum layoutiomode4 {\n       LAYOUTIOMODE4_READ      = 1,\n       LAYOUTIOMODE4_RW        = 2,\n       LAYOUTIOMODE4_ANY       = 3\n};\n\nstruct layout_content4 {\n       layouttype4 loc_type;\n       opaque      loc_body<>;\n};\n\nstruct layout4 {\n       offset4                 lo_offset;\n       length4                 lo_length;\n       layoutiomode4           lo_iomode;\n       layout_content4         lo_content;\n};\n\nunion settime4 switch (time_how4 set_it) {\n   case SET_TO_CLIENT_TIME4:\n        nfstime4 time;\n   default:\n        void;\n};\n\n/*\n * File access handle\n */\ntypedef opaque  nfs_fh4<NFS4_FHSIZE>;\n\n/*\n * File attribute definitions\n */\n\n/*\n * FSID structure for major/minor\n */\nstruct fsid4 {\n     uint64_t        major;\n     uint64_t        minor;\n};\n\n/*\n * Filesystem locations attribute for relocation/migration\n */\nstruct fs_location4 {\n     utf8str_cis     server<>;\n     pathname4       rootpath;\n};\n\nstruct fs_locations4 {\n     pathname4       fs_root;\n     fs_location4    locations<>;\n};\n\n/*\n * Various Access Control Entry definitions\n */\n\n/*\n * Mask that indicates which Access Control Entries are supported.\n * Values for the fattr4_aclsupport attribute.\n */\nconst ACL4_SUPPORT_ALLOW_ACL    = 0x00000001;\nconst ACL4_SUPPORT_DENY_ACL     = 0x00000002;\nconst ACL4_SUPPORT_AUDIT_ACL    = 0x00000004;\nconst ACL4_SUPPORT_ALARM_ACL    = 0x00000008;\n\n\ntypedef uint32_t        acetype4;\n/*\n * acetype4 values, others can be added as needed.\n */\nconst ACE4_ACCESS_ALLOWED_ACE_TYPE      = 0x00000000;\nconst ACE4_ACCESS_DENIED_ACE_TYPE       = 0x00000001;\nconst ACE4_SYSTEM_AUDIT_ACE_TYPE        = 0x00000002;\nconst ACE4_SYSTEM_ALARM_ACE_TYPE        = 0x00000003;\n\n\n/*\n * ACE flag\n */\ntypedef uint32_t aceflag4;\n\n/*\n * ACE flag values\n */\nconst ACE4_FILE_INHERIT_ACE             = 0x00000001;\nconst ACE4_DIRECTORY_INHERIT_ACE        = 0x00000002;\nconst ACE4_NO_PROPAGATE_INHERIT_ACE     = 0x00000004;\nconst ACE4_INHERIT_ONLY_ACE             = 0x00000008;\nconst ACE4_SUCCESSFUL_ACCESS_ACE_FLAG   = 0x00000010;\nconst ACE4_FAILED_ACCESS_ACE_FLAG       = 0x00000020;\nconst ACE4_IDENTIFIER_GROUP             = 0x00000040;\n\n\n/*\n * ACE mask\n */\ntypedef uint32_t        acemask4;\n\n/*\n * ACE mask values\n */\nconst ACE4_READ_DATA            = 0x00000001;\nconst ACE4_LIST_DIRECTORY       = 0x00000001;\nconst ACE4_WRITE_DATA           = 0x00000002;\nconst ACE4_ADD_FILE             = 0x00000002;\nconst ACE4_APPEND_DATA          = 0x00000004;\nconst ACE4_ADD_SUBDIRECTORY     = 0x00000004;\nconst ACE4_READ_NAMED_ATTRS     = 0x00000008;\nconst ACE4_WRITE_NAMED_ATTRS    = 0x00000010;\nconst ACE4_EXECUTE              = 0x00000020;\nconst ACE4_DELETE_CHILD         = 0x00000040;\nconst ACE4_READ_ATTRIBUTES      = 0x00000080;\nconst ACE4_WRITE_ATTRIBUTES     = 0x00000100;\nconst ACE4_DELETE               = 0x00010000;\nconst ACE4_READ_ACL             = 0x00020000;\nconst ACE4_WRITE_ACL            = 0x00040000;\nconst ACE4_WRITE_OWNER          = 0x00080000;\nconst ACE4_SYNCHRONIZE          = 0x00100000;\n\n/*\n * ACE4_GENERIC_READ -- defined as combination of\n *      ACE4_READ_ACL |\n *      ACE4_READ_DATA |\n *      ACE4_READ_ATTRIBUTES |\n *      ACE4_SYNCHRONIZE\n */\nconst ACE4_GENERIC_READ = 0x00120081;\n\n/*\n * ACE4_GENERIC_WRITE -- defined as combination of\n *      ACE4_READ_ACL |\n *      ACE4_WRITE_DATA |\n *      ACE4_WRITE_ATTRIBUTES |\n *      ACE4_WRITE_ACL |\n *      ACE4_APPEND_DATA |\n *      ACE4_SYNCHRONIZE\n */\nconst ACE4_GENERIC_WRITE = 0x00160106;\n\n/*\n * ACE4_GENERIC_EXECUTE -- defined as combination of\n *      ACE4_READ_ACL\n *      ACE4_READ_ATTRIBUTES\n *      ACE4_EXECUTE\n *      ACE4_SYNCHRONIZE\n */\nconst ACE4_GENERIC_EXECUTE = 0x001200A0;\n\n\n/*\n * Access Control Entry definition\n */\nstruct nfsace4 {\n     acetype4        type;\n     aceflag4        flag;\n     acemask4        access_mask;\n     utf8str_mixed   who;\n};\n\n/*\n * Field definitions for the fattr4_mode attribute\n */\nconst MODE4_SUID = 0x800;  /* set user id on execution */\nconst MODE4_SGID = 0x400;  /* set group id on execution */\nconst MODE4_SVTX = 0x200;  /* save text even after use */\nconst MODE4_RUSR = 0x100;  /* read permission: owner */\nconst MODE4_WUSR = 0x080;  /* write permission: owner */\nconst MODE4_XUSR = 0x040;  /* execute permission: owner */\nconst MODE4_RGRP = 0x020;  /* read permission: group */\nconst MODE4_WGRP = 0x010;  /* write permission: group */\nconst MODE4_XGRP = 0x008;  /* execute permission: group */\nconst MODE4_ROTH = 0x004;  /* read permission: other */\nconst MODE4_WOTH = 0x002;  /* write permission: other */\nconst MODE4_XOTH = 0x001;  /* execute permission: other */\n\n/*\n * Special data/attribute associated with\n * file types NF4BLK and NF4CHR.\n */\nstruct specdata4 {\n     uint32_t        specdata1;      /* major device number */\n     uint32_t        specdata2;      /* minor device number */\n};\n\n/*\n * Values for fattr4_fh_expire_type\n */\nconst   FH4_PERSISTENT          = 0x00000000;\nconst   FH4_NOEXPIRE_WITH_OPEN  = 0x00000001;\nconst   FH4_VOLATILE_ANY        = 0x00000002;\nconst   FH4_VOL_MIGRATION       = 0x00000004;\nconst   FH4_VOL_RENAME          = 0x00000008;\n\n\ntypedef bitmap4         fattr4_supported_attrs;\ntypedef nfs_ftype4      fattr4_type;\ntypedef uint32_t        fattr4_fh_expire_type;\ntypedef changeid4       fattr4_change;\ntypedef uint64_t        fattr4_size;\ntypedef bool            fattr4_link_support;\ntypedef bool            fattr4_symlink_support;\ntypedef bool            fattr4_named_attr;\ntypedef fsid4           fattr4_fsid;\ntypedef bool            fattr4_unique_handles;\ntypedef uint32_t        fattr4_lease_time;\ntypedef nfsstat4        fattr4_rdattr_error;\ntypedef nfsace4         fattr4_acl<>;\ntypedef uint32_t        fattr4_aclsupport;\ntypedef bool            fattr4_archive;\ntypedef bool            fattr4_cansettime;\ntypedef bool            fattr4_case_insensitive;\ntypedef bool            fattr4_case_preserving;\ntypedef bool            fattr4_chown_restricted;\ntypedef uint64_t        fattr4_fileid;\ntypedef uint64_t        fattr4_files_avail;\ntypedef nfs_fh4         fattr4_filehandle;\ntypedef uint64_t        fattr4_files_free;\ntypedef uint64_t        fattr4_files_total;\ntypedef fs_locations4   fattr4_fs_locations;\ntypedef bool            fattr4_hidden;\ntypedef bool            fattr4_homogeneous;\ntypedef uint64_t        fattr4_maxfilesize;\ntypedef uint32_t        fattr4_maxlink;\ntypedef uint32_t        fattr4_maxname;\ntypedef uint64_t        fattr4_maxread;\ntypedef uint64_t        fattr4_maxwrite;\ntypedef utf8str_cs      fattr4_mimetype;\ntypedef mode4           fattr4_mode;\ntypedef uint64_t        fattr4_mounted_on_fileid;\ntypedef bool            fattr4_no_trunc;\ntypedef uint32_t        fattr4_numlinks;\ntypedef utf8str_mixed   fattr4_owner;\ntypedef utf8str_mixed   fattr4_owner_group;\ntypedef uint64_t        fattr4_quota_avail_hard;\ntypedef uint64_t        fattr4_quota_avail_soft;\ntypedef uint64_t        fattr4_quota_used;\ntypedef specdata4       fattr4_rawdev;\ntypedef uint64_t        fattr4_space_avail;\ntypedef uint64_t        fattr4_space_free;\ntypedef uint64_t        fattr4_space_total;\ntypedef uint64_t        fattr4_space_used;\ntypedef bool            fattr4_system;\ntypedef nfstime4        fattr4_time_access;\ntypedef settime4        fattr4_time_access_set;\ntypedef nfstime4        fattr4_time_backup;\ntypedef nfstime4        fattr4_time_create;\ntypedef nfstime4        fattr4_time_delta;\ntypedef nfstime4        fattr4_time_metadata;\ntypedef nfstime4        fattr4_time_modify;\ntypedef settime4        fattr4_time_modify_set;\n\n\n/*\n * Mandatory Attributes\n */\nconst FATTR4_SUPPORTED_ATTRS    = 0;\nconst FATTR4_TYPE               = 1;\nconst FATTR4_FH_EXPIRE_TYPE     = 2;\nconst FATTR4_CHANGE             = 3;\nconst FATTR4_SIZE               = 4;\nconst FATTR4_LINK_SUPPORT       = 5;\nconst FATTR4_SYMLINK_SUPPORT    = 6;\nconst FATTR4_NAMED_ATTR         = 7;\nconst FATTR4_FSID               = 8;\nconst FATTR4_UNIQUE_HANDLES     = 9;\nconst FATTR4_LEASE_TIME         = 10;\nconst FATTR4_RDATTR_ERROR       = 11;\nconst FATTR4_FILEHANDLE         = 19;\n\n/*\n * Recommended Attributes\n */\nconst FATTR4_ACL                = 12;\nconst FATTR4_ACLSUPPORT         = 13;\nconst FATTR4_ARCHIVE            = 14;\nconst FATTR4_CANSETTIME         = 15;\nconst FATTR4_CASE_INSENSITIVE   = 16;\nconst FATTR4_CASE_PRESERVING    = 17;\nconst FATTR4_CHOWN_RESTRICTED   = 18;\nconst FATTR4_FILEID             = 20;\nconst FATTR4_FILES_AVAIL        = 21;\nconst FATTR4_FILES_FREE         = 22;\nconst FATTR4_FILES_TOTAL        = 23;\nconst FATTR4_FS_LOCATIONS       = 24;\nconst FATTR4_HIDDEN             = 25;\nconst FATTR4_HOMOGENEOUS        = 26;\nconst FATTR4_MAXFILESIZE        = 27;\nconst FATTR4_MAXLINK            = 28;\nconst FATTR4_MAXNAME            = 29;\nconst FATTR4_MAXREAD            = 30;\nconst FATTR4_MAXWRITE           = 31;\nconst FATTR4_MIMETYPE           = 32;\nconst FATTR4_MODE               = 33;\nconst FATTR4_NO_TRUNC           = 34;\nconst FATTR4_NUMLINKS           = 35;\nconst FATTR4_OWNER              = 36;\nconst FATTR4_OWNER_GROUP        = 37;\nconst FATTR4_QUOTA_AVAIL_HARD   = 38;\nconst FATTR4_QUOTA_AVAIL_SOFT   = 39;\nconst FATTR4_QUOTA_USED         = 40;\nconst FATTR4_RAWDEV             = 41;\nconst FATTR4_SPACE_AVAIL        = 42;\nconst FATTR4_SPACE_FREE         = 43;\nconst FATTR4_SPACE_TOTAL        = 44;\nconst FATTR4_SPACE_USED         = 45;\nconst FATTR4_SYSTEM             = 46;\nconst FATTR4_TIME_ACCESS        = 47;\nconst FATTR4_TIME_ACCESS_SET    = 48;\nconst FATTR4_TIME_BACKUP        = 49;\nconst FATTR4_TIME_CREATE        = 50;\nconst FATTR4_TIME_DELTA         = 51;\nconst FATTR4_TIME_METADATA      = 52;\nconst FATTR4_TIME_MODIFY        = 53;\nconst FATTR4_TIME_MODIFY_SET    = 54;\nconst FATTR4_MOUNTED_ON_FILEID  = 55;\n\ntypedef opaque  attrlist4<>;\n\n/*\n * File attribute container\n */\nstruct fattr4 {\n     bitmap4         attrmask;\n     attrlist4       attr_vals;\n};\n\n/*\n * Change info for the client\n */\nstruct change_info4 {\n     bool            atomic;\n     changeid4       before;\n     changeid4       after;\n};\n\nstruct clientaddr4 {\n        /* see struct rpcb in RFC 1833 */\n        string r_netid<>;               /* network id */\n        string r_addr<>;                /* universal address */\n};\n\n/*\n * Callback program info as provided by the client\n */\nstruct cb_client4 {\n        uint32_t        cb_program;\n        clientaddr4     cb_location;\n};\n\n/*\n * Stateid\n */\nstruct stateid4 {\n        uint32_t        seqid;\n        opaque          other[12];\n};\n\n/*\n * Client ID\n */\nstruct nfs_client_id4 {\n        verifier4       verifier;\n        opaque          id<NFS4_OPAQUE_LIMIT>;\n};\n\nstruct open_owner4 {\n        clientid4       clientid;\n        opaque          owner<NFS4_OPAQUE_LIMIT>;\n};\n\nstruct lock_owner4 {\n        clientid4       clientid;\n        opaque          owner<NFS4_OPAQUE_LIMIT>;\n};\n\nenum nfs_lock_type4 {\n        READ_LT         = 1,\n        WRITE_LT        = 2,\n        READW_LT        = 3,    /* blocking read */\n        WRITEW_LT       = 4     /* blocking write */\n};\n\n/*\n * ACCESS: Check access permission\n */\nconst ACCESS4_READ      = 0x00000001;\nconst ACCESS4_LOOKUP    = 0x00000002;\nconst ACCESS4_MODIFY    = 0x00000004;\nconst ACCESS4_EXTEND    = 0x00000008;\nconst ACCESS4_DELETE    = 0x00000010;\nconst ACCESS4_EXECUTE   = 0x00000020;\n\nstruct ACCESS4args {\n        /* CURRENT_FH: object */\n        uint32_t        access;\n};\n\nstruct ACCESS4resok {\n        uint32_t        supported;\n        uint32_t        access;\n};\n\nunion ACCESS4res switch (nfsstat4 status) {\n case NFS4_OK:\n         ACCESS4resok   resok4;\n default:\n         void;\n};\n\n/*\n * CLOSE: Close a file and release share reservations\n */\nstruct CLOSE4args {\n        /* CURRENT_FH: object */\n        seqid4          seqid;\n        stateid4        open_stateid;\n};\n\nunion CLOSE4res switch (nfsstat4 status) {\n case NFS4_OK:\n         stateid4       open_stateid;\n default:\n         void;\n};\n\n/*\n * COMMIT: Commit cached data on server to stable storage\n */\nstruct COMMIT4args {\n        /* CURRENT_FH: file */\n        offset4         offset;\n        count4          count;\n};\n\nstruct COMMIT4resok {\n        verifier4       writeverf;\n};\n\n\nunion COMMIT4res switch (nfsstat4 status) {\n case NFS4_OK:\n         COMMIT4resok   resok4;\n default:\n         void;\n};\n\n/*\n * CREATE: Create a non-regular file\n */\nunion createtype4 switch (nfs_ftype4 type) {\n case NF4LNK:\n         linktext4      linkdata;\n case NF4BLK:\n case NF4CHR:\n         specdata4      devdata;\n case NF4SOCK:\n case NF4FIFO:\n case NF4DIR:\n         void;\n default:\n         void;          /* server should return NFS4ERR_BADTYPE */\n};\n\nstruct CREATE4args {\n        /* CURRENT_FH: directory for creation */\n        createtype4     objtype;\n        component4      objname;\n        fattr4          createattrs;\n};\n\nstruct CREATE4resok {\n        change_info4    cinfo;\n        bitmap4         attrset;        /* attributes set */\n};\n\nunion CREATE4res switch (nfsstat4 status) {\n case NFS4_OK:\n         CREATE4resok resok4;\n default:\n         void;\n};\n\n/*\n * DELEGPURGE: Purge Delegations Awaiting Recovery\n */\nstruct DELEGPURGE4args {\n        clientid4       clientid;\n};\n\nstruct DELEGPURGE4res {\n        nfsstat4        status;\n};\n\n/*\n * DELEGRETURN: Return a delegation\n */\nstruct DELEGRETURN4args {\n        /* CURRENT_FH: delegated file */\n        stateid4        deleg_stateid;\n};\n\nstruct DELEGRETURN4res {\n        nfsstat4        status;\n};\n\n/*\n * GETATTR: Get file attributes\n */\nstruct GETATTR4args {\n        /* CURRENT_FH: directory or file */\n        bitmap4         attr_request;\n};\n\nstruct GETATTR4resok {\n        fattr4          obj_attributes;\n};\n\nunion GETATTR4res switch (nfsstat4 status) {\n case NFS4_OK:\n         GETATTR4resok  resok4;\n default:\n         void;\n};\n\n/*\n * GETFH: Get current filehandle\n */\nstruct GETFH4resok {\n        nfs_fh4         object;\n};\n\nunion GETFH4res switch (nfsstat4 status) {\n case NFS4_OK:\n        GETFH4resok     resok4;\n default:\n        void;\n};\n\n/*\n * LINK: Create link to an object\n */\nstruct LINK4args {\n        /* SAVED_FH: source object */\n        /* CURRENT_FH: target directory */\n        component4      newname;\n};\n\nstruct LINK4resok {\n        change_info4    cinfo;\n};\n\nunion LINK4res switch (nfsstat4 status) {\n case NFS4_OK:\n         LINK4resok resok4;\n default:\n         void;\n};\n\n/*\n * For LOCK, transition from open_owner to new lock_owner\n */\nstruct open_to_lock_owner4 {\n        seqid4          open_seqid;\n        stateid4        open_stateid;\n        seqid4          lock_seqid;\n        lock_owner4     lock_owner;\n};\n\n/*\n * For LOCK, existing lock_owner continues to request file locks\n */\nstruct exist_lock_owner4 {\n        stateid4        lock_stateid;\n        seqid4          lock_seqid;\n};\n\nunion locker4 switch (bool new_lock_owner) {\n case TRUE:\n        open_to_lock_owner4     open_owner;\n case FALSE:\n        exist_lock_owner4       lock_owner;\n};\n\n/*\n * LOCK/LOCKT/LOCKU: Record lock management\n */\nstruct LOCK4args {\n        /* CURRENT_FH: file */\n        nfs_lock_type4  locktype;\n        bool            reclaim;\n        offset4         offset;\n        length4         length;\n        locker4         locker;\n};\n\nstruct LOCK4denied {\n        offset4         offset;\n        length4         length;\n        nfs_lock_type4  locktype;\n        lock_owner4     owner;\n};\n\nstruct LOCK4resok {\n        stateid4        lock_stateid;\n};\n\nunion LOCK4res switch (nfsstat4 status) {\n case NFS4_OK:\n         LOCK4resok     resok4;\n case NFS4ERR_DENIED:\n         LOCK4denied    denied;\n default:\n         void;\n};\n\nstruct LOCKT4args {\n        /* CURRENT_FH: file */\n        nfs_lock_type4  locktype;\n        offset4         offset;\n        length4         length;\n        lock_owner4     owner;\n};\n\nunion LOCKT4res switch (nfsstat4 status) {\n case NFS4ERR_DENIED:\n         LOCK4denied    denied;\n case NFS4_OK:\n         void;\n default:\n         void;\n};\n\nstruct LOCKU4args {\n        /* CURRENT_FH: file */\n        nfs_lock_type4  locktype;\n        seqid4          seqid;\n        stateid4        lock_stateid;\n        offset4         offset;\n        length4         length;\n};\n\nunion LOCKU4res switch (nfsstat4 status) {\n case   NFS4_OK:\n         stateid4       lock_stateid;\n default:\n         void;\n};\n\n/*\n * LOOKUP: Lookup filename\n */\nstruct LOOKUP4args {\n        /* CURRENT_FH: directory */\n        component4      objname;\n};\n\nstruct LOOKUP4res {\n        /* CURRENT_FH: object */\n        nfsstat4        status;\n};\n\n/*\n * LOOKUPP: Lookup parent directory\n */\nstruct LOOKUPP4res {\n        /* CURRENT_FH: directory */\n        nfsstat4        status;\n};\n\n/*\n * NVERIFY: Verify attributes different\n */\nstruct NVERIFY4args {\n        /* CURRENT_FH: object */\n        fattr4          obj_attributes;\n};\n\nstruct NVERIFY4res {\n        nfsstat4        status;\n};\n\n/*\n * Various definitions for OPEN\n */\nenum createmode4 {\n        UNCHECKED4      = 0,\n        GUARDED4        = 1,\n        EXCLUSIVE4      = 2\n};\n\nunion createhow4 switch (createmode4 mode) {\n case UNCHECKED4:\n case GUARDED4:\n         fattr4         createattrs;\n case EXCLUSIVE4:\n         verifier4      createverf;\n};\n\nenum opentype4 {\n        OPEN4_NOCREATE  = 0,\n        OPEN4_CREATE    = 1\n};\n\nunion openflag4 switch (opentype4 opentype) {\n case OPEN4_CREATE:\n         createhow4     how;\n default:\n         void;\n};\n\n/* Next definitions used for OPEN delegation */\nenum limit_by4 {\n        NFS_LIMIT_SIZE          = 1,\n        NFS_LIMIT_BLOCKS        = 2\n        /* others as needed */\n};\n\nstruct nfs_modified_limit4 {\n        uint32_t        num_blocks;\n        uint32_t        bytes_per_block;\n};\n\nunion nfs_space_limit4 switch (limit_by4 limitby) {\n /* limit specified as file size */\n case NFS_LIMIT_SIZE:\n         uint64_t               filesize;\n /* limit specified by number of blocks */\n case NFS_LIMIT_BLOCKS:\n         nfs_modified_limit4    mod_blocks;\n} ;\n\n/*\n * Share Access and Deny constants for open argument\n */\nconst OPEN4_SHARE_ACCESS_READ   = 0x00000001;\nconst OPEN4_SHARE_ACCESS_WRITE  = 0x00000002;\nconst OPEN4_SHARE_ACCESS_BOTH   = 0x00000003;\n\nconst OPEN4_SHARE_DENY_NONE     = 0x00000000;\nconst OPEN4_SHARE_DENY_READ     = 0x00000001;\nconst OPEN4_SHARE_DENY_WRITE    = 0x00000002;\nconst OPEN4_SHARE_DENY_BOTH     = 0x00000003;\n\nenum open_delegation_type4 {\n        OPEN_DELEGATE_NONE      = 0,\n        OPEN_DELEGATE_READ      = 1,\n        OPEN_DELEGATE_WRITE     = 2\n};\n\nenum open_claim_type4 {\n        CLAIM_NULL              = 0,\n        CLAIM_PREVIOUS          = 1,\n        CLAIM_DELEGATE_CUR      = 2,\n        CLAIM_DELEGATE_PREV     = 3,\n        CLAIM_FH                = 4, /* new to v4.1 */\n        CLAIM_DELEG_CUR_FH      = 5, /* new to v4.1 */\n        CLAIM_DELEG_PREV_FH     = 6 /* new to v4.1 */\n};\n\nstruct open_claim_delegate_cur4 {\n        stateid4        delegate_stateid;\n        component4      file;\n};\n\nunion open_claim4 switch (open_claim_type4 claim) {\n /*\n  * No special rights to file. Ordinary OPEN of the specified file.\n  */\n case CLAIM_NULL:\n        /* CURRENT_FH: directory */\n        component4      file;\n\n /*\n  * Right to the file established by an open previous to server\n  * reboot.  File identified by filehandle obtained at that time\n  * rather than by name.\n  */\n case CLAIM_PREVIOUS:\n        /* CURRENT_FH: file being reclaimed */\n        open_delegation_type4   delegate_type;\n\n /*\n  * Right to file based on a delegation granted by the server.\n  * File is specified by name.\n  */\n case CLAIM_DELEGATE_CUR:\n        /* CURRENT_FH: directory */\n        open_claim_delegate_cur4        delegate_cur_info;\n\n /* Right to file based on a delegation granted to a previous boot\n  * instance of the client.  File is specified by name.\n  */\n case CLAIM_DELEGATE_PREV:\n         /* CURRENT_FH: directory */\n        component4      file_delegate_prev;\n};\n\n/*\n * OPEN: Open a file, potentially receiving an open delegation\n */\nstruct OPEN4args {\n        seqid4          seqid;\n        uint32_t        share_access;\n        uint32_t        share_deny;\n        open_owner4     owner;\n        openflag4       openhow;\n        open_claim4     claim;\n};\n\nstruct open_read_delegation4 {\n        stateid4        stateid;        /* Stateid for delegation*/\n        bool            recall;         /* Pre-recalled flag for\n                                           delegations obtained\n                                           by reclaim\n                                           (CLAIM_PREVIOUS) */\n        nfsace4         permissions;    /* Defines users who don't\n                                           need an ACCESS call to\n                                           open for read */\n};\n\nstruct open_write_delegation4 {\n        stateid4        stateid;        /* Stateid for delegation */\n        bool            recall;         /* Pre-recalled flag for\n                                           delegations obtained\n                                           by reclaim\n                                           (CLAIM_PREVIOUS) */\n        nfs_space_limit4 space_limit;   /* Defines condition that\n                                           the client must check to\n                                           determine whether the\n                                           file needs to be flushed\n                                           to the server on close.\n                                           */\n        nfsace4         permissions;    /* Defines users who don't\n                                           need an ACCESS call as\n                                           part of a delegated\n                                           open. */\n};\n\nunion open_delegation4\nswitch (open_delegation_type4 delegation_type) {\n        case OPEN_DELEGATE_NONE:\n                void;\n        case OPEN_DELEGATE_READ:\n                open_read_delegation4 read;\n        case OPEN_DELEGATE_WRITE:\n                open_write_delegation4 write;\n};\n/*\n * Result flags\n */\n/* Client must confirm open */\nconst OPEN4_RESULT_CONFIRM      = 0x00000002;\n/* Type of file locking behavior at the server */\nconst OPEN4_RESULT_LOCKTYPE_POSIX = 0x00000004;\n\nstruct OPEN4resok {\n        stateid4        stateid;        /* Stateid for open */\n        change_info4    cinfo;          /* Directory Change Info */\n        uint32_t        rflags;         /* Result flags */\n        bitmap4         attrset;        /* attribute set for create*/\n        open_delegation4 delegation;    /* Info on any open\n                                           delegation */\n};\n\nunion OPEN4res switch (nfsstat4 status) {\n case NFS4_OK:\n        /* CURRENT_FH: opened file */\n        OPEN4resok      resok4;\n default:\n        void;\n};\n\n/*\n * OPENATTR: open named attributes directory\n */\nstruct OPENATTR4args {\n        /* CURRENT_FH: object */\n        bool    createdir;\n};\n\nstruct OPENATTR4res {\n        /* CURRENT_FH: named attr directory */\n        nfsstat4        status;\n};\n\n/*\n * OPEN_CONFIRM: confirm the open\n */\nstruct OPEN_CONFIRM4args {\n        /* CURRENT_FH: opened file */\n        stateid4        open_stateid;\n        seqid4          seqid;\n};\n\nstruct OPEN_CONFIRM4resok {\n        stateid4        open_stateid;\n};\n\nunion OPEN_CONFIRM4res switch (nfsstat4 status) {\n    case NFS4_OK:\n            OPEN_CONFIRM4resok     resok4;\n default:\n         void;\n};\n\n/*\n * OPEN_DOWNGRADE: downgrade the access/deny for a file\n */\nstruct OPEN_DOWNGRADE4args {\n        /* CURRENT_FH: opened file */\n        stateid4        open_stateid;\n        seqid4          seqid;\n        uint32_t        share_access;\n        uint32_t        share_deny;\n};\n\nstruct OPEN_DOWNGRADE4resok {\n        stateid4        open_stateid;\n};\n\nunion OPEN_DOWNGRADE4res switch(nfsstat4 status) {\n case NFS4_OK:\n        OPEN_DOWNGRADE4resok    resok4;\n default:\n         void;\n};\n\n/*\n * PUTFH: Set current filehandle\n */\nstruct PUTFH4args {\n        nfs_fh4         object;\n};\n\nstruct PUTFH4res {\n        /* CURRENT_FH: */\n        nfsstat4        status;\n};\n\n/*\n * PUTPUBFH: Set public filehandle\n */\nstruct PUTPUBFH4res {\n        /* CURRENT_FH: public fh */\n        nfsstat4        status;\n};\n\n/*\n * PUTROOTFH: Set root filehandle\n */\nstruct PUTROOTFH4res {\n\n        /* CURRENT_FH: root fh */\n        nfsstat4        status;\n};\n\n/*\n * READ: Read from file\n */\nstruct READ4args {\n        /* CURRENT_FH: file */\n        stateid4        stateid;\n        offset4         offset;\n        count4          count;\n};\n\nstruct READ4resok {\n        bool            eof;\n        opaque          data<>;\n};\n\nunion READ4res switch (nfsstat4 status) {\n case NFS4_OK:\n         READ4resok     resok4;\n default:\n         void;\n};\n\n/*\n * READDIR: Read directory\n */\nstruct READDIR4args {\n        /* CURRENT_FH: directory */\n        nfs_cookie4     cookie;\n        verifier4       cookieverf;\n        count4          dircount;\n        count4          maxcount;\n        bitmap4         attr_request;\n};\n\nstruct entry4 {\n        nfs_cookie4     cookie;\n        component4      name;\n        fattr4          attrs;\n        entry4          *nextentry;\n};\n\nstruct dirlist4 {\n        entry4          *entries;\n        bool            eof;\n};\n\nstruct READDIR4resok {\n        verifier4       cookieverf;\n        dirlist4        reply;\n};\n\n\nunion READDIR4res switch (nfsstat4 status) {\n case NFS4_OK:\n         READDIR4resok  resok4;\n default:\n         void;\n};\n\n\n/*\n * READLINK: Read symbolic link\n */\nstruct READLINK4resok {\n        linktext4       link;\n};\n\nunion READLINK4res switch (nfsstat4 status) {\n case NFS4_OK:\n         READLINK4resok resok4;\n default:\n         void;\n};\n\n/*\n * REMOVE: Remove filesystem object\n */\nstruct REMOVE4args {\n        /* CURRENT_FH: directory */\n        component4      target;\n};\n\nstruct REMOVE4resok {\n        change_info4    cinfo;\n};\n\nunion REMOVE4res switch (nfsstat4 status) {\n case NFS4_OK:\n         REMOVE4resok   resok4;\n default:\n         void;\n};\n\n/*\n * RENAME: Rename directory entry\n */\nstruct RENAME4args {\n        /* SAVED_FH: source directory */\n        component4      oldname;\n        /* CURRENT_FH: target directory */\n\n        component4      newname;\n};\n\nstruct RENAME4resok {\n        change_info4    source_cinfo;\n        change_info4    target_cinfo;\n};\n\nunion RENAME4res switch (nfsstat4 status) {\n case NFS4_OK:\n        RENAME4resok    resok4;\n default:\n        void;\n};\n\n/*\n * RENEW: Renew a Lease\n */\nstruct RENEW4args {\n        clientid4       clientid;\n};\n\nstruct RENEW4res {\n        nfsstat4        status;\n};\n\n/*\n * RESTOREFH: Restore saved filehandle\n */\n\nstruct RESTOREFH4res {\n        /* CURRENT_FH: value of saved fh */\n        nfsstat4        status;\n};\n\n/*\n * SAVEFH: Save current filehandle\n */\nstruct SAVEFH4res {\n        /* SAVED_FH: value of current fh */\n        nfsstat4        status;\n};\n\n/*\n * SECINFO: Obtain Available Security Mechanisms\n */\nstruct SECINFO4args {\n        component4      name;\n};\n\nenum rpc_gss_svc_t {\n        RPC_GSS_SVC_NONE        = 1,\n        RPC_GSS_SVC_INTEGRITY   = 2,\n        RPC_GSS_SVC_PRIVACY     = 3\n};\n\nstruct rpcsec_gss_info {\n        sec_oid4        oid;\n        qop4            qop;\n        rpc_gss_svc_t   service;\n};\n\nconst RPCSEC_GSS = 6;\n\nunion secinfo4 switch (uint32_t flavor) {\n case RPCSEC_GSS:\n         rpcsec_gss_info        flavor_info;\n default:\n         void;\n};\n\ntypedef secinfo4 SECINFO4resok<>;\n\nunion SECINFO4res switch (nfsstat4 status) {\n case NFS4_OK:\n         SECINFO4resok resok4;\n default:\n         void;\n};\n\n/*\n * SETATTR: Set attributes\n */\nstruct SETATTR4args {\n        /* CURRENT_FH: target object */\n        stateid4        stateid;\n        fattr4          obj_attributes;\n};\n\nstruct SETATTR4res {\n        nfsstat4        status;\n        bitmap4         attrsset;\n};\n\n/*\n * SETCLIENTID\n */\nstruct SETCLIENTID4args {\n        nfs_client_id4  client;\n        cb_client4      callback;\n        uint32_t        callback_ident;\n\n};\n\nstruct SETCLIENTID4resok {\n        clientid4       clientid;\n        verifier4       setclientid_confirm;\n};\n\nunion SETCLIENTID4res switch (nfsstat4 status) {\n case NFS4_OK:\n         SETCLIENTID4resok      resok4;\n case NFS4ERR_CLID_INUSE:\n         clientaddr4    client_using;\n default:\n         void;\n};\n\nstruct SETCLIENTID_CONFIRM4args {\n        clientid4       clientid;\n        verifier4       setclientid_confirm;\n};\n\nstruct SETCLIENTID_CONFIRM4res {\n        nfsstat4        status;\n};\n\n/*\n * VERIFY: Verify attributes same\n */\nstruct VERIFY4args {\n        /* CURRENT_FH: object */\n        fattr4          obj_attributes;\n};\n\nstruct VERIFY4res {\n        nfsstat4        status;\n};\n\n/*\n * WRITE: Write to file\n */\nenum stable_how4 {\n        UNSTABLE4       = 0,\n        DATA_SYNC4      = 1,\n        FILE_SYNC4      = 2\n};\n\nstruct WRITE4args {\n        /* CURRENT_FH: file */\n        stateid4        stateid;\n        offset4         offset;\n        stable_how4     stable;\n        opaque          data<>;\n};\n\nstruct WRITE4resok {\n        count4          count;\n        stable_how4     committed;\n        verifier4       writeverf;\n};\n\nunion WRITE4res switch (nfsstat4 status) {\n case NFS4_OK:\n         WRITE4resok    resok4;\n default:\n         void;\n};\n\n/*\n * RELEASE_LOCKOWNER: Notify server to release lockowner\n */\nstruct RELEASE_LOCKOWNER4args {\n        lock_owner4     lock_owner;\n};\n\nstruct RELEASE_LOCKOWNER4res {\n        nfsstat4        status;\n};\n\n/*\n * BACKCHANNEL_CTL\n */\n/*\ntypedef opaque gsshandle4_t<>;\n\nstruct gss_cb_handles4 {\n       rpc_gss_svc_t           gcbp_service; RFC 2203\n       gsshandle4_t            gcbp_handle_from_server;\n       gsshandle4_t            gcbp_handle_from_client;\n};\n*/\n\nunion callback_sec_parms4 switch (uint32_t cb_secflavor) {\ncase AUTH_NONE:\n       void;\ncase AUTH_SYS:\n       authsys_parms   cbsp_sys_cred; /* RFC 1831 */\n/*\n * case RPCSEC_GSS:\n *     gss_cb_handles4 cbsp_gss_handles;\n */\n};\n\n/*\nstruct BACKCHANNEL_CTL4args {\n       uint32_t                bca_cb_program;\n       callback_sec_parms4     bca_sec_parms<>;\n};\n*/\n\n/*\n * CREATE_SESSION\n */\nstruct channel_attrs4 {\n       count4                  ca_headerpadsize;\n       count4                  ca_maxrequestsize;\n       count4                  ca_maxresponsesize;\n       count4                  ca_maxresponsesize_cached;\n       count4                  ca_maxoperations;\n       count4                  ca_maxrequests;\n       uint32_t                ca_rdma_ird<1>;\n};\n\nconst CREATE_SESSION4_FLAG_PERSIST              = 0x00000001;\nconst CREATE_SESSION4_FLAG_CONN_BACK_CHAN       = 0x00000002;\nconst CREATE_SESSION4_FLAG_CONN_RDMA            = 0x00000004;\n\nstruct CREATE_SESSION4args {\n       clientid4               csa_clientid;\n       sequenceid4             csa_sequence;\n       uint32_t                csa_flags;\n       channel_attrs4          csa_fore_chan_attrs;\n       channel_attrs4          csa_back_chan_attrs;\n       uint32_t                csa_cb_program;\n       callback_sec_parms4     csa_sec_parms<>;\n};\n\nstruct CREATE_SESSION4resok {\n       sessionid4              csr_sessionid;\n       sequenceid4             csr_sequence;\n       uint32_t                csr_flags;\n       channel_attrs4          csr_fore_chan_attrs;\n       channel_attrs4          csr_back_chan_attrs;\n};\n\nunion CREATE_SESSION4res switch (nfsstat4 csr_status) {\ncase NFS4_OK:\n       CREATE_SESSION4resok    csr_resok4;\ndefault:\n       void;\n};\n\n/*\n * DESTROY_SESSION\n */\nstruct DESTROY_SESSION4args {\n       sessionid4      dsa_sessionid;\n};\n\nstruct DESTROY_SESSION4res {\n       nfsstat4        dsr_status;\n};\n\n/*\n * FREE_STATEID\n */\nstruct FREE_STATEID4args {\n       stateid4        fsa_stateid;\n};\n\nstruct FREE_STATEID4res {\n       nfsstat4        fsr_status;\n};\n\n/*\n * GET_DIR_DELEGATION\n */\ntypedef nfstime4 attr_notice4;\n\nstruct GET_DIR_DELEGATION4args {\n       bool            gdda_signal_deleg_avail;\n       bitmap4         gdda_notification_types;\n       attr_notice4    gdda_child_attr_delay;\n       attr_notice4    gdda_dir_attr_delay;\n       bitmap4         gdda_child_attributes;\n       bitmap4         gdda_dir_attributes;\n};\n\nstruct GET_DIR_DELEGATION4resok {\n       verifier4       gddr_cookieverf;\n       stateid4        gddr_stateid;\n       bitmap4         gddr_notification;\n       bitmap4         gddr_child_attributes;\n       bitmap4         gddr_dir_attributes;\n};\n\nenum gddrnf4_status {\n       GDD4_OK         = 0,\n       GDD4_UNAVAIL    = 1\n};\n\nunion GET_DIR_DELEGATION4res_non_fatal switch (gddrnf4_status gddrnf_status) {\n case GDD4_OK:\n     GET_DIR_DELEGATION4resok      gddrnf_resok4;\n case GDD4_UNAVAIL:\n     bool                          gddrnf_will_signal_deleg_avail;\n};\n\nunion GET_DIR_DELEGATION4res switch (nfsstat4 gddr_status) {\n case NFS4_OK:\n     GET_DIR_DELEGATION4res_non_fatal      gddr_res_non_fatal4;\n default:\n     void;\n};\n\n/*\n * GETDEVICEINFO\n */\nstruct GETDEVICEINFO4args {\n       deviceid4       gdia_device_id;\n       layouttype4     gdia_layout_type;\n       count4          gdia_maxcount;\n       bitmap4         gdia_notify_types;\n};\n\nstruct GETDEVICEINFO4resok {\n       device_addr4    gdir_device_addr;\n       bitmap4         gdir_notification;\n};\n\nunion GETDEVICEINFO4res switch (nfsstat4 gdir_status) {\ncase NFS4_OK:\n       GETDEVICEINFO4resok     gdir_resok4;\ncase NFS4ERR_TOOSMALL:\n       count4                  gdir_mincount;\ndefault:\n       void;\n};\n\n/*\n * GETDEVICELIST\n */\nstruct GETDEVICELIST4args {\n        layouttype4     gdla_layout_type;\n        count4          gdla_maxdevices;\n        nfs_cookie4     gdla_cookie;\n        verifier4       gdla_cookieverf;\n};\n\nstruct GETDEVICELIST4resok {\n       nfs_cookie4             gdlr_cookie;\n       verifier4               gdlr_cookieverf;\n       deviceid4               gdlr_deviceid_list<>;\n       bool                    gdlr_eof;\n};\n\nunion GETDEVICELIST4res switch (nfsstat4 gdlr_status) {\ncase NFS4_OK:\n       GETDEVICELIST4resok     gdlr_resok4;\ndefault:\n       void;\n};\n\n/*\n * LAYOUTCOMMIT\n */\nunion newtime4 switch (bool nt_timechanged) {\ncase TRUE:\n       nfstime4           nt_time;\ncase FALSE:\n       void;\n};\n\nunion newoffset4 switch (bool no_newoffset) {\ncase TRUE:\n       offset4           no_offset;\ncase FALSE:\n       void;\n};\n\nstruct LAYOUTCOMMIT4args {\n       offset4                 loca_offset;\n       length4                 loca_length;\n       bool                    loca_reclaim;\n       stateid4                loca_stateid;\n       newoffset4              loca_last_write_offset;\n       newtime4                loca_time_modify;\n       layoutupdate4           loca_layoutupdate;\n};\n\nunion newsize4 switch (bool ns_sizechanged) {\ncase TRUE:\n       length4         ns_size;\ncase FALSE:\n       void;\n};\n\nstruct LAYOUTCOMMIT4resok {\n       newsize4                locr_newsize;\n};\n\nunion LAYOUTCOMMIT4res switch (nfsstat4 locr_status) {\ncase NFS4_OK:\n       LAYOUTCOMMIT4resok      locr_resok4;\ndefault:\n       void;\n};\n\n/*\n * LAYOUTGET\n */\nstruct LAYOUTGET4args {\n       bool                    loga_signal_layout_avail;\n       layouttype4             loga_layout_type;\n       layoutiomode4           loga_iomode;\n       offset4                 loga_offset;\n       length4                 loga_length;\n       length4                 loga_minlength;\n       stateid4                loga_stateid;\n       count4                  loga_maxcount;\n};\n\nstruct LAYOUTGET4resok {\n       bool               logr_return_on_close;\n       stateid4           logr_stateid;\n       layout4            logr_layout<>;\n};\n\nunion LAYOUTGET4res switch (nfsstat4 logr_status) {\ncase NFS4_OK:\n       LAYOUTGET4resok     logr_resok4;\ncase NFS4ERR_LAYOUTTRYLATER:\n       bool                logr_will_signal_layout_avail;\ndefault:\n       void;\n};\n\n/*\n * LAYOUTRETURN\n */\nconst LAYOUT4_RET_REC_FILE      = 1;\nconst LAYOUT4_RET_REC_FSID      = 2;\nconst LAYOUT4_RET_REC_ALL       = 3;\n\nenum layoutreturn_type4 {\n       LAYOUTRETURN4_FILE = LAYOUT4_RET_REC_FILE,\n       LAYOUTRETURN4_FSID = LAYOUT4_RET_REC_FSID,\n       LAYOUTRETURN4_ALL  = LAYOUT4_RET_REC_ALL\n};\n\nstruct layoutreturn_file4 {\n       offset4         lrf_offset;\n       length4         lrf_length;\n       stateid4        lrf_stateid;\n       opaque          lrf_body<>;\n};\n\nunion layoutreturn4 switch(layoutreturn_type4 lr_returntype) {\n       case LAYOUTRETURN4_FILE:\n               layoutreturn_file4      lr_layout;\n       default:\n               void;\n};\n\nstruct LAYOUTRETURN4args {\n       bool                    lora_reclaim;\n       layouttype4             lora_layout_type;\n       layoutiomode4           lora_iomode;\n       layoutreturn4           lora_layoutreturn;\n};\n\nunion layoutreturn_stateid switch (bool lrs_present) {\ncase TRUE:\n       stateid4                lrs_stateid;\ncase FALSE:\n       void;\n};\n\nunion LAYOUTRETURN4res switch (nfsstat4 lorr_status) {\ncase NFS4_OK:\n       layoutreturn_stateid    lorr_stateid;\ndefault:\n       void;\n};\n\n/*\n * SECINFO_NO_NAME\n */\nenum secinfo_style4 {\n       SECINFO_STYLE4_CURRENT_FH       = 0,\n       SECINFO_STYLE4_PARENT           = 1\n};\n\ntypedef secinfo_style4 SECINFO_NO_NAME4args;\n\ntypedef SECINFO4res SECINFO_NO_NAME4res;\n\n/*\n * SEQUENCE\n */\nstruct SEQUENCE4args {\n       sessionid4     sa_sessionid;\n       sequenceid4    sa_sequenceid;\n       slotid4        sa_slotid;\n       slotid4        sa_highest_slotid;\n       bool           sa_cachethis;\n};\n\nconst SEQ4_STATUS_CB_PATH_DOWN                  = 0x00000001;\nconst SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRING      = 0x00000002;\nconst SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRED       = 0x00000004;\nconst SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED     = 0x00000008;\nconst SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED    = 0x00000010;\nconst SEQ4_STATUS_ADMIN_STATE_REVOKED           = 0x00000020;\nconst SEQ4_STATUS_RECALLABLE_STATE_REVOKED      = 0x00000040;\nconst SEQ4_STATUS_LEASE_MOVED                   = 0x00000080;\nconst SEQ4_STATUS_RESTART_RECLAIM_NEEDED        = 0x00000100;\nconst SEQ4_STATUS_CB_PATH_DOWN_SESSION          = 0x00000200;\nconst SEQ4_STATUS_BACKCHANNEL_FAULT             = 0x00000400;\nconst SEQ4_STATUS_DEVID_CHANGED                 = 0x00000800;\nconst SEQ4_STATUS_DEVID_DELETED                 = 0x00001000;\n\nstruct SEQUENCE4resok {\n       sessionid4      sr_sessionid;\n       sequenceid4     sr_sequenceid;\n       slotid4         sr_slotid;\n       slotid4         sr_highest_slotid;\n       slotid4         sr_target_highest_slotid;\n       uint32_t        sr_status_flags;\n};\n\nunion SEQUENCE4res switch (nfsstat4 sr_status) {\ncase NFS4_OK:\n       SEQUENCE4resok  sr_resok4;\ndefault:\n       void;\n};\n\n/*\n * SET_SSV\n */\nstruct ssa_digest_input4 {\n       SEQUENCE4args sdi_seqargs;\n};\n\nstruct SET_SSV4args {\n       opaque          ssa_ssv<>;\n       opaque          ssa_digest<>;\n};\n\nstruct ssr_digest_input4 {\n       SEQUENCE4res sdi_seqres;\n};\n\nstruct SET_SSV4resok {\n       opaque          ssr_digest<>;\n};\n\nunion SET_SSV4res switch (nfsstat4 ssr_status) {\ncase NFS4_OK:\n       SET_SSV4resok   ssr_resok4;\ndefault:\n       void;\n};\n\n/*\n * TEST_STATEID\n */\nstruct TEST_STATEID4args {\n       stateid4        ts_stateids<>;\n};\n\nstruct TEST_STATEID4resok {\n       nfsstat4        tsr_status_codes<>;\n};\n\nunion TEST_STATEID4res switch (nfsstat4 tsr_status) {\n   case NFS4_OK:\n       TEST_STATEID4resok tsr_resok4;\n   default:\n       void;\n};\n\n/*\n * WANT_DELEGATION\n */\nunion deleg_claim4 switch (open_claim_type4 dc_claim) {\ncase CLAIM_FH:\n       void;\ncase CLAIM_DELEG_PREV_FH:\n       void;\ncase CLAIM_PREVIOUS:\n       open_delegation_type4   dc_delegate_type;\n};\n\nstruct WANT_DELEGATION4args {\n       uint32_t        wda_want;\n       deleg_claim4    wda_claim;\n};\n\nunion WANT_DELEGATION4res switch (nfsstat4 wdr_status) {\ncase NFS4_OK:\n       open_delegation4 wdr_resok4;\ndefault:\n       void;\n};\n\n/*\n * DESTROY_CLIENTID\n */\nstruct DESTROY_CLIENTID4args {\n       clientid4       dca_clientid;\n};\n\nstruct DESTROY_CLIENTID4res {\n       nfsstat4        dcr_status;\n};\n\n/*\n * RECLAIM_COMPLETE\n */\nstruct RECLAIM_COMPLETE4args {\n       bool            rca_one_fs;\n};\n\nstruct RECLAIM_COMPLETE4res {\n       nfsstat4        rcr_status;\n};\n\n/*\n * ILLEGAL: Response for illegal operation numbers\n */\nstruct ILLEGAL4res {\n        nfsstat4        status;\n};\n\n/*\n * Operation arrays\n */\n\nenum nfs_opnum4 {\n        OP_ACCESS               = 3,\n        OP_CLOSE                = 4,\n        OP_COMMIT               = 5,\n        OP_CREATE               = 6,\n        OP_DELEGPURGE           = 7,\n        OP_DELEGRETURN          = 8,\n        OP_GETATTR              = 9,\n        OP_GETFH                = 10,\n        OP_LINK                 = 11,\n        OP_LOCK                 = 12,\n        OP_LOCKT                = 13,\n        OP_LOCKU                = 14,\n        OP_LOOKUP               = 15,\n        OP_LOOKUPP              = 16,\n        OP_NVERIFY              = 17,\n        OP_OPEN                 = 18,\n        OP_OPENATTR             = 19,\n        OP_OPEN_CONFIRM         = 20,\n        OP_OPEN_DOWNGRADE       = 21,\n        OP_PUTFH                = 22,\n        OP_PUTPUBFH             = 23,\n        OP_PUTROOTFH            = 24,\n        OP_READ                 = 25,\n        OP_READDIR              = 26,\n        OP_READLINK             = 27,\n        OP_REMOVE               = 28,\n        OP_RENAME               = 29,\n        OP_RENEW                = 30,\n        OP_RESTOREFH            = 31,\n        OP_SAVEFH               = 32,\n        OP_SECINFO              = 33,\n        OP_SETATTR              = 34,\n        OP_SETCLIENTID          = 35,\n        OP_SETCLIENTID_CONFIRM  = 36,\n        OP_VERIFY               = 37,\n        OP_WRITE                = 38,\n        OP_RELEASE_LOCKOWNER    = 39,\n        OP_CREATE_SESSION       = 43,\n        OP_DESTROY_SESSION      = 44,\n        OP_FREE_STATEID         = 45,\n        OP_GET_DIR_DELEGATION   = 46,\n        OP_GETDEVICEINFO        = 47,\n        OP_GETDEVICELIST        = 48,\n        OP_LAYOUTCOMMIT         = 49,\n        OP_LAYOUTGET            = 50,\n        OP_LAYOUTRETURN         = 51,\n        OP_SECINFO_NO_NAME      = 52,\n        OP_SEQUENCE             = 53,\n        OP_SET_SSV              = 54,\n        OP_TEST_STATEID         = 55,\n        OP_WANT_DELEGATION      = 56,\n        OP_DESTROY_CLIENTID     = 57,\n        OP_RECLAIM_COMPLETE     = 58,\n        OP_ILLEGAL              = 10044\n};\n\nunion nfs_argop4 switch (nfs_opnum4 argop) {\n case OP_ACCESS:        ACCESS4args opaccess;\n case OP_CLOSE:         CLOSE4args opclose;\n case OP_COMMIT:        COMMIT4args opcommit;\n case OP_CREATE:        CREATE4args opcreate;\n case OP_DELEGPURGE:    DELEGPURGE4args opdelegpurge;\n case OP_DELEGRETURN:   DELEGRETURN4args opdelegreturn;\n case OP_GETATTR:       GETATTR4args opgetattr;\n case OP_GETFH:         void;\n case OP_LINK:          LINK4args oplink;\n case OP_LOCK:          LOCK4args oplock;\n case OP_LOCKT:         LOCKT4args oplockt;\n case OP_LOCKU:         LOCKU4args oplocku;\n case OP_LOOKUP:        LOOKUP4args oplookup;\n case OP_LOOKUPP:       void;\n case OP_NVERIFY:       NVERIFY4args opnverify;\n case OP_OPEN:          OPEN4args opopen;\n case OP_OPENATTR:      OPENATTR4args opopenattr;\n case OP_OPEN_CONFIRM:  OPEN_CONFIRM4args opopen_confirm;\n case OP_OPEN_DOWNGRADE:        OPEN_DOWNGRADE4args opopen_downgrade;\n case OP_PUTFH:         PUTFH4args opputfh;\n case OP_PUTPUBFH:      void;\n case OP_PUTROOTFH:     void;\n case OP_READ:          READ4args opread;\n case OP_READDIR:       READDIR4args opreaddir;\n case OP_READLINK:      void;\n case OP_REMOVE:        REMOVE4args opremove;\n case OP_RENAME:        RENAME4args oprename;\n case OP_RENEW:         RENEW4args oprenew;\n case OP_RESTOREFH:     void;\n case OP_SAVEFH:        void;\n case OP_SECINFO:       SECINFO4args opsecinfo;\n case OP_SETATTR:       SETATTR4args opsetattr;\n case OP_SETCLIENTID:   SETCLIENTID4args opsetclientid;\n case OP_SETCLIENTID_CONFIRM:   SETCLIENTID_CONFIRM4args\n                                        opsetclientid_confirm;\n case OP_VERIFY:        VERIFY4args opverify;\n case OP_WRITE:         WRITE4args opwrite;\n case OP_RELEASE_LOCKOWNER:     RELEASE_LOCKOWNER4args\n                                    oprelease_lockowner;\n case OP_CREATE_SESSION:        CREATE_SESSION4args opcreatesession;\n case OP_DESTROY_SESSION:       DESTROY_SESSION4args opdestroysession;\n case OP_FREE_STATEID:          FREE_STATEID4args opfreestateid;\n case OP_GET_DIR_DELEGATION:    GET_DIR_DELEGATION4args opgetdirdelegation;\n case OP_GETDEVICEINFO:         GETDEVICEINFO4args opgetdeviceinfo;\n case OP_GETDEVICELIST:         GETDEVICELIST4args opgetdevicelist;\n case OP_LAYOUTCOMMIT:          LAYOUTCOMMIT4args oplayoutcommit;\n case OP_LAYOUTGET:             LAYOUTGET4args oplayoutget;\n case OP_LAYOUTRETURN:          LAYOUTRETURN4args oplayoutreturn;\n case OP_SECINFO_NO_NAME:       SECINFO_NO_NAME4args opsecinfononame;\n case OP_SEQUENCE:              SEQUENCE4args opsequence;\n case OP_SET_SSV:               SET_SSV4args opsetssv;\n case OP_TEST_STATEID:          TEST_STATEID4args opteststateid;\n case OP_WANT_DELEGATION:       WANT_DELEGATION4args opwantdelegation;\n case OP_DESTROY_CLIENTID:      DESTROY_CLIENTID4args opdestroyclientid;\n case OP_RECLAIM_COMPLETE:      RECLAIM_COMPLETE4args opreclaimcomplete;\n case OP_ILLEGAL:       void;\n};\n\nunion nfs_resop4 switch (nfs_opnum4 resop){\n case OP_ACCESS:        ACCESS4res opaccess;\n case OP_CLOSE:         CLOSE4res opclose;\n case OP_COMMIT:        COMMIT4res opcommit;\n case OP_CREATE:        CREATE4res opcreate;\n case OP_DELEGPURGE:    DELEGPURGE4res opdelegpurge;\n case OP_DELEGRETURN:   DELEGRETURN4res opdelegreturn;\n case OP_GETATTR:       GETATTR4res opgetattr;\n case OP_GETFH:         GETFH4res opgetfh;\n case OP_LINK:          LINK4res oplink;\n case OP_LOCK:          LOCK4res oplock;\n case OP_LOCKT:         LOCKT4res oplockt;\n case OP_LOCKU:         LOCKU4res oplocku;\n case OP_LOOKUP:        LOOKUP4res oplookup;\n case OP_LOOKUPP:       LOOKUPP4res oplookupp;\n case OP_NVERIFY:       NVERIFY4res opnverify;\n case OP_OPEN:          OPEN4res opopen;\n case OP_OPENATTR:      OPENATTR4res opopenattr;\n case OP_OPEN_CONFIRM:  OPEN_CONFIRM4res opopen_confirm;\n case OP_OPEN_DOWNGRADE:        OPEN_DOWNGRADE4res opopen_downgrade;\n case OP_PUTFH:         PUTFH4res opputfh;\n case OP_PUTPUBFH:      PUTPUBFH4res opputpubfh;\n case OP_PUTROOTFH:     PUTROOTFH4res opputrootfh;\n case OP_READ:          READ4res opread;\n case OP_READDIR:       READDIR4res opreaddir;\n case OP_READLINK:      READLINK4res opreadlink;\n case OP_REMOVE:        REMOVE4res opremove;\n case OP_RENAME:        RENAME4res oprename;\n case OP_RENEW:         RENEW4res oprenew;\n case OP_RESTOREFH:     RESTOREFH4res oprestorefh;\n case OP_SAVEFH:        SAVEFH4res opsavefh;\n case OP_SECINFO:       SECINFO4res opsecinfo;\n case OP_SETATTR:       SETATTR4res opsetattr;\n case OP_SETCLIENTID:   SETCLIENTID4res opsetclientid;\n case OP_SETCLIENTID_CONFIRM:   SETCLIENTID_CONFIRM4res\n                                        opsetclientid_confirm;\n case OP_VERIFY:        VERIFY4res opverify;\n case OP_WRITE:         WRITE4res opwrite;\n case OP_RELEASE_LOCKOWNER:     RELEASE_LOCKOWNER4res\n                                    oprelease_lockowner;\n case OP_CREATE_SESSION:        CREATE_SESSION4res opcreatesession;\n case OP_DESTROY_SESSION:       DESTROY_SESSION4res opdestroysession;\n case OP_FREE_STATEID:          FREE_STATEID4res opfreestateid;\n case OP_GET_DIR_DELEGATION:    GET_DIR_DELEGATION4res opgetdirdelegation;\n case OP_GETDEVICEINFO:         GETDEVICEINFO4res opgetdeviceinfo;\n case OP_GETDEVICELIST:         GETDEVICELIST4res opgetdevicelist;\n case OP_LAYOUTCOMMIT:          LAYOUTCOMMIT4res oplayoutcommit;\n case OP_LAYOUTGET:             LAYOUTGET4res oplayoutget;\n case OP_LAYOUTRETURN:          LAYOUTRETURN4res oplayoutreturn;\n case OP_SECINFO_NO_NAME:       SECINFO_NO_NAME4res opsecinfononame;\n case OP_SEQUENCE:              SEQUENCE4res opsequence;\n case OP_SET_SSV:               SET_SSV4res opsetssv;\n case OP_TEST_STATEID:          TEST_STATEID4res opteststateid;\n case OP_WANT_DELEGATION:       WANT_DELEGATION4res opwantdelegation;\n case OP_DESTROY_CLIENTID:      DESTROY_CLIENTID4res opdestroyclientid;\n case OP_RECLAIM_COMPLETE:      RECLAIM_COMPLETE4res opreclaimcomplete;\n case OP_ILLEGAL:       ILLEGAL4res opillegal;\n};\n\nstruct COMPOUND4args {\n        utf8str_cs      tag;\n        uint32_t        minorversion;\n        nfs_argop4      argarray<>;\n};\n\nstruct COMPOUND4res {\n        nfsstat4 status;\n        utf8str_cs      tag;\n        nfs_resop4      resarray<>;\n};\n\n/*\n * Remote file service routines\n */\nprogram NFS4_PROGRAM {\n        version NFS_V4 {\n                void\n                        NFSPROC4_NULL(void) = 0;\n\n                COMPOUND4res\n                        NFSPROC4_COMPOUND(COMPOUND4args) = 1;\n\n        } = 4;\n} = 100003;\n\n\n\n/*\n * NFS4 Callback Procedure Definitions and Program\n */\n\n/*\n * CB_GETATTR: Get Current Attributes\n */\nstruct CB_GETATTR4args {\n        nfs_fh4 fh;\n        bitmap4 attr_request;\n};\n\nstruct CB_GETATTR4resok {\n        fattr4  obj_attributes;\n};\n\nunion CB_GETATTR4res switch (nfsstat4 status) {\n case NFS4_OK:\n         CB_GETATTR4resok       resok4;\n default:\n         void;\n};\n\n/*\n * CB_RECALL: Recall an Open Delegation\n */\nstruct CB_RECALL4args {\n        stateid4        stateid;\n        bool            truncate;\n        nfs_fh4         fh;\n};\n\nstruct CB_RECALL4res {\n        nfsstat4        status;\n};\n\n/*\n * CB_ILLEGAL: Response for illegal operation numbers\n */\nstruct CB_ILLEGAL4res {\n        nfsstat4        status;\n};\n\n/*\n * Various definitions for CB_COMPOUND\n */\nenum nfs_cb_opnum4 {\n        OP_CB_GETATTR           = 3,\n        OP_CB_RECALL            = 4,\n        OP_CB_ILLEGAL           = 10044\n};\n\nunion nfs_cb_argop4 switch (unsigned argop) {\n case OP_CB_GETATTR:    CB_GETATTR4args opcbgetattr;\n case OP_CB_RECALL:     CB_RECALL4args  opcbrecall;\n case OP_CB_ILLEGAL:    void;\n};\n\nunion nfs_cb_resop4 switch (unsigned resop){\n case OP_CB_GETATTR:    CB_GETATTR4res  opcbgetattr;\n case OP_CB_RECALL:     CB_RECALL4res   opcbrecall;\n case OP_CB_ILLEGAL:    CB_ILLEGAL4res  opcbillegal;\n};\n\nstruct CB_COMPOUND4args {\n        utf8str_cs      tag;\n        uint32_t        minorversion;\n        uint32_t        callback_ident;\n        nfs_cb_argop4   argarray<>;\n};\n\nstruct CB_COMPOUND4res {\n        nfsstat4 status;\n        utf8str_cs      tag;\n        nfs_cb_resop4   resarray<>;\n};\n\n\n/*\n * Program number is in the transient range since the client\n * will assign the exact transient program number and provide\n * that to the server via the SETCLIENTID operation.\n */\nprogram NFS4_CALLBACK {\n        version NFS_CB {\n                void\n                        CB_NULL(void) = 0;\n                CB_COMPOUND4res\n                        CB_COMPOUND(CB_COMPOUND4args) = 1;\n        } = 1;\n} = 0x40000000;\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs4/repo/internal/nfsconst.go",
    "content": "package internal\n\n/*\nconst OPEN4_SHARE_ACCESS_READ = 0x00000001\n\nconst OPEN4_SHARE_ACCESS_WRITE = 0x00000002\n\nconst OPEN4_SHARE_ACCESS_BOTH = 0x00000003\n\nconst OPEN4_SHARE_DENY_NONE = 0x00000000\n\nconst OPEN4_SHARE_DENY_READ = 0x00000001\n\nconst OPEN4_SHARE_DENY_WRITE = 0x00000002\n\nconst OPEN4_SHARE_DENY_BOTH = 0x00000003\n*/\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs4/repo/internal/rpc.go",
    "content": "// Code generated by goxdr -B -p nfs4 nfs4/rpc.x; DO NOT EDIT.\n\npackage internal\nimport (\n\t\"context\"\n\t\"fmt\"\n)\n\nvar _ = fmt.Sprintf\nvar _ context.Context\n\n//\n// Data types defined in XDR file\n//\n\ntype Auth_flavor int32\nconst (\n\tAUTH_NONE Auth_flavor = 0\n\tAUTH_SYS Auth_flavor = 1\n\tAUTH_SHORT Auth_flavor = 2\n\tAUTH_DH Auth_flavor = 3\n)\n\ntype Opaque_auth struct {\n\tFlavor Auth_flavor\n\tBody []byte // bound 400\n}\n\ntype Msg_type int32\nconst (\n\tCALL Msg_type = 0\n\tREPLY Msg_type = 1\n)\n\n/* A reply to a call message can take on two forms: the message was\n   either accepted or rejected. */\ntype Reply_stat int32\nconst (\n\tMSG_ACCEPTED Reply_stat = 0\n\tMSG_DENIED Reply_stat = 1\n)\n\n/* Given that a call message was accepted, the following is the status\n   of an attempt to call a remote procedure. */\ntype Accept_stat int32\nconst (\n\t/* RPC executed successfully       */\n\tSUCCESS Accept_stat = 0\n\t/* remote hasn't exported program  */\n\tPROG_UNAVAIL Accept_stat = 1\n\t/* remote can't support version #  */\n\tPROG_MISMATCH Accept_stat = 2\n\t/* program can't support procedure */\n\tPROC_UNAVAIL Accept_stat = 3\n\t/* procedure can't decode params   */\n\tGARBAGE_ARGS Accept_stat = 4\n\t/* e.g. memory allocation failure  */\n\tSYSTEM_ERR Accept_stat = 5\n)\n\n/* Reasons why a call message was rejected: */\ntype Reject_stat int32\nconst (\n\t/* RPC version number != 2          */\n\tRPC_MISMATCH Reject_stat = 0\n\t/* remote can't authenticate caller */\n\tAUTH_ERROR Reject_stat = 1\n)\n\n/* Why authentication failed: */\ntype Auth_stat int32\nconst (\n\t/* success                        */\n\tAUTH_OK Auth_stat = 0\n\t/* bad credential (seal broken)   */\n\tAUTH_BADCRED Auth_stat = 1\n\t/* client must begin new session  */\n\tAUTH_REJECTEDCRED Auth_stat = 2\n\t/* bad verifier (seal broken)     */\n\tAUTH_BADVERF Auth_stat = 3\n\t/* verifier expired or replayed   */\n\tAUTH_REJECTEDVERF Auth_stat = 4\n\t/* rejected for security reasons  */\n\tAUTH_TOOWEAK Auth_stat = 5\n\t/* bogus response verifier        */\n\tAUTH_INVALIDRESP Auth_stat = 6\n\t/* reason unknown                 */\n\tAUTH_FAILED Auth_stat = 7\n\t/* kerberos generic error */\n\tAUTH_KERB_GENERIC Auth_stat = 8\n\t/* time of credential expired */\n\tAUTH_TIMEEXPIRE Auth_stat = 9\n\t/* problem with ticket file */\n\tAUTH_TKT_FILE Auth_stat = 10\n\t/* can't decode authenticator */\n\tAUTH_DECODE Auth_stat = 11\n\t/* wrong net address in ticket */\n\tAUTH_NET_ADDR Auth_stat = 12\n\t/* no credentials for user */\n\tRPCSEC_GSS_CREDPROBLEM Auth_stat = 13\n\t/* problem with context */\n\tRPCSEC_GSS_CTXPROBLEM Auth_stat = 14\n)\n\n/* Body of an RPC call: */\ntype Call_body struct {\n\t/* must be equal to two (2) */\n\tRpcvers uint32\n\tProg uint32\n\tVers uint32\n\tProc uint32\n\tCred Opaque_auth\n\tVerf Opaque_auth\n}\n\n/* Reply to an RPC call that was accepted by the server: */\ntype Accepted_reply struct {\n\tVerf Opaque_auth\n\tReply_data XdrAnon_Accepted_reply_Reply_data\n}\ntype XdrAnon_Accepted_reply_Reply_data struct {\n\t// The union discriminant Stat selects among the following arms:\n\t//   SUCCESS:\n\t//      Results() *[0]byte\n\t//   PROG_MISMATCH:\n\t//      Mismatch_info() *XdrAnon_Accepted_reply_Reply_data_Mismatch_info\n\t//   default:\n\t//      void\n\tStat Accept_stat\n\tU interface{}\n}\ntype XdrAnon_Accepted_reply_Reply_data_Mismatch_info struct {\n\tLow uint32\n\tHigh uint32\n}\n\n/* Reply to an RPC call that was rejected by the server: */\ntype Rejected_reply struct {\n\t// The union discriminant Stat selects among the following arms:\n\t//   RPC_MISMATCH:\n\t//      Mismatch_info() *XdrAnon_Rejected_reply_Mismatch_info\n\t//   AUTH_ERROR:\n\t//      Rj_why() *Auth_stat\n\tStat Reject_stat\n\tU interface{}\n}\ntype XdrAnon_Rejected_reply_Mismatch_info struct {\n\tLow uint32\n\tHigh uint32\n}\n\n/* Body of a reply to an RPC call: */\ntype Reply_body struct {\n\t// The union discriminant Stat selects among the following arms:\n\t//   MSG_ACCEPTED:\n\t//      Areply() *Accepted_reply\n\t//   MSG_DENIED:\n\t//      Rreply() *Rejected_reply\n\tStat Reply_stat\n\tU interface{}\n}\n\n/* The RPC message: */\ntype Rpc_msg struct {\n\tXid uint32\n\tBody XdrAnon_Rpc_msg_Body\n}\ntype XdrAnon_Rpc_msg_Body struct {\n\t// The union discriminant Mtype selects among the following arms:\n\t//   CALL:\n\t//      Cbody() *Call_body\n\t//   REPLY:\n\t//      Rbody() *Reply_body\n\tMtype Msg_type\n\tU interface{}\n}\n\n//\n// Helper types and generated marshaling functions\n//\n\nvar _XdrNames_Auth_flavor = map[int32]string{\n\tint32(AUTH_NONE): \"AUTH_NONE\",\n\tint32(AUTH_SYS): \"AUTH_SYS\",\n\tint32(AUTH_SHORT): \"AUTH_SHORT\",\n\tint32(AUTH_DH): \"AUTH_DH\",\n\tint32(RPCSEC_GSS): \"RPCSEC_GSS\",\n}\nvar _XdrValues_Auth_flavor = map[string]int32{\n\t\"AUTH_NONE\": int32(AUTH_NONE),\n\t\"AUTH_SYS\": int32(AUTH_SYS),\n\t\"AUTH_SHORT\": int32(AUTH_SHORT),\n\t\"AUTH_DH\": int32(AUTH_DH),\n\t\"RPCSEC_GSS\": int32(RPCSEC_GSS),\n}\nfunc (Auth_flavor) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Auth_flavor\n}\nfunc (v Auth_flavor) String() string {\n\tif s, ok := _XdrNames_Auth_flavor[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Auth_flavor#%d\", v)\n}\nfunc (v *Auth_flavor) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Auth_flavor[stok]; ok {\n\t\t\t*v = Auth_flavor(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Auth_flavor\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Auth_flavor.\", stok))\n\t}\n}\nfunc (v Auth_flavor) GetU32() uint32 { return uint32(v) }\nfunc (v *Auth_flavor) SetU32(n uint32) { *v = Auth_flavor(n) }\nfunc (v *Auth_flavor) XdrPointer() interface{} { return v }\nfunc (Auth_flavor) XdrTypeName() string { return \"Auth_flavor\" }\nfunc (v Auth_flavor) XdrValue() interface{} { return v }\nfunc (v *Auth_flavor) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Auth_flavor = *Auth_flavor\nfunc XDR_Auth_flavor(v *Auth_flavor) *Auth_flavor { return v }\ntype XdrType_Opaque_auth = *Opaque_auth\nfunc (v *Opaque_auth) XdrPointer() interface{} { return v }\nfunc (Opaque_auth) XdrTypeName() string { return \"Opaque_auth\" }\nfunc (v Opaque_auth) XdrValue() interface{} { return v }\nfunc (v *Opaque_auth) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Opaque_auth) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sflavor\", name), XDR_Auth_flavor(&v.Flavor))\n\tx.Marshal(x.Sprintf(\"%sbody\", name), XdrVecOpaque{&v.Body, 400})\n}\nfunc XDR_Opaque_auth(v *Opaque_auth) *Opaque_auth { return v }\nvar _XdrNames_Msg_type = map[int32]string{\n\tint32(CALL): \"CALL\",\n\tint32(REPLY): \"REPLY\",\n}\nvar _XdrValues_Msg_type = map[string]int32{\n\t\"CALL\": int32(CALL),\n\t\"REPLY\": int32(REPLY),\n}\nfunc (Msg_type) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Msg_type\n}\nfunc (v Msg_type) String() string {\n\tif s, ok := _XdrNames_Msg_type[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Msg_type#%d\", v)\n}\nfunc (v *Msg_type) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Msg_type[stok]; ok {\n\t\t\t*v = Msg_type(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Msg_type\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Msg_type.\", stok))\n\t}\n}\nfunc (v Msg_type) GetU32() uint32 { return uint32(v) }\nfunc (v *Msg_type) SetU32(n uint32) { *v = Msg_type(n) }\nfunc (v *Msg_type) XdrPointer() interface{} { return v }\nfunc (Msg_type) XdrTypeName() string { return \"Msg_type\" }\nfunc (v Msg_type) XdrValue() interface{} { return v }\nfunc (v *Msg_type) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Msg_type = *Msg_type\nfunc XDR_Msg_type(v *Msg_type) *Msg_type { return v }\nvar _XdrNames_Reply_stat = map[int32]string{\n\tint32(MSG_ACCEPTED): \"MSG_ACCEPTED\",\n\tint32(MSG_DENIED): \"MSG_DENIED\",\n}\nvar _XdrValues_Reply_stat = map[string]int32{\n\t\"MSG_ACCEPTED\": int32(MSG_ACCEPTED),\n\t\"MSG_DENIED\": int32(MSG_DENIED),\n}\nfunc (Reply_stat) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Reply_stat\n}\nfunc (v Reply_stat) String() string {\n\tif s, ok := _XdrNames_Reply_stat[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Reply_stat#%d\", v)\n}\nfunc (v *Reply_stat) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Reply_stat[stok]; ok {\n\t\t\t*v = Reply_stat(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Reply_stat\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Reply_stat.\", stok))\n\t}\n}\nfunc (v Reply_stat) GetU32() uint32 { return uint32(v) }\nfunc (v *Reply_stat) SetU32(n uint32) { *v = Reply_stat(n) }\nfunc (v *Reply_stat) XdrPointer() interface{} { return v }\nfunc (Reply_stat) XdrTypeName() string { return \"Reply_stat\" }\nfunc (v Reply_stat) XdrValue() interface{} { return v }\nfunc (v *Reply_stat) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Reply_stat = *Reply_stat\nfunc XDR_Reply_stat(v *Reply_stat) *Reply_stat { return v }\nvar _XdrNames_Accept_stat = map[int32]string{\n\tint32(SUCCESS): \"SUCCESS\",\n\tint32(PROG_UNAVAIL): \"PROG_UNAVAIL\",\n\tint32(PROG_MISMATCH): \"PROG_MISMATCH\",\n\tint32(PROC_UNAVAIL): \"PROC_UNAVAIL\",\n\tint32(GARBAGE_ARGS): \"GARBAGE_ARGS\",\n\tint32(SYSTEM_ERR): \"SYSTEM_ERR\",\n}\nvar _XdrValues_Accept_stat = map[string]int32{\n\t\"SUCCESS\": int32(SUCCESS),\n\t\"PROG_UNAVAIL\": int32(PROG_UNAVAIL),\n\t\"PROG_MISMATCH\": int32(PROG_MISMATCH),\n\t\"PROC_UNAVAIL\": int32(PROC_UNAVAIL),\n\t\"GARBAGE_ARGS\": int32(GARBAGE_ARGS),\n\t\"SYSTEM_ERR\": int32(SYSTEM_ERR),\n}\nfunc (Accept_stat) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Accept_stat\n}\nfunc (v Accept_stat) String() string {\n\tif s, ok := _XdrNames_Accept_stat[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Accept_stat#%d\", v)\n}\nfunc (v *Accept_stat) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Accept_stat[stok]; ok {\n\t\t\t*v = Accept_stat(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Accept_stat\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Accept_stat.\", stok))\n\t}\n}\nfunc (v Accept_stat) GetU32() uint32 { return uint32(v) }\nfunc (v *Accept_stat) SetU32(n uint32) { *v = Accept_stat(n) }\nfunc (v *Accept_stat) XdrPointer() interface{} { return v }\nfunc (Accept_stat) XdrTypeName() string { return \"Accept_stat\" }\nfunc (v Accept_stat) XdrValue() interface{} { return v }\nfunc (v *Accept_stat) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Accept_stat = *Accept_stat\nfunc XDR_Accept_stat(v *Accept_stat) *Accept_stat { return v }\nvar _XdrNames_Reject_stat = map[int32]string{\n\tint32(RPC_MISMATCH): \"RPC_MISMATCH\",\n\tint32(AUTH_ERROR): \"AUTH_ERROR\",\n}\nvar _XdrValues_Reject_stat = map[string]int32{\n\t\"RPC_MISMATCH\": int32(RPC_MISMATCH),\n\t\"AUTH_ERROR\": int32(AUTH_ERROR),\n}\nfunc (Reject_stat) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Reject_stat\n}\nfunc (v Reject_stat) String() string {\n\tif s, ok := _XdrNames_Reject_stat[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Reject_stat#%d\", v)\n}\nfunc (v *Reject_stat) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Reject_stat[stok]; ok {\n\t\t\t*v = Reject_stat(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Reject_stat\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Reject_stat.\", stok))\n\t}\n}\nfunc (v Reject_stat) GetU32() uint32 { return uint32(v) }\nfunc (v *Reject_stat) SetU32(n uint32) { *v = Reject_stat(n) }\nfunc (v *Reject_stat) XdrPointer() interface{} { return v }\nfunc (Reject_stat) XdrTypeName() string { return \"Reject_stat\" }\nfunc (v Reject_stat) XdrValue() interface{} { return v }\nfunc (v *Reject_stat) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Reject_stat = *Reject_stat\nfunc XDR_Reject_stat(v *Reject_stat) *Reject_stat { return v }\nvar _XdrNames_Auth_stat = map[int32]string{\n\tint32(AUTH_OK): \"AUTH_OK\",\n\tint32(AUTH_BADCRED): \"AUTH_BADCRED\",\n\tint32(AUTH_REJECTEDCRED): \"AUTH_REJECTEDCRED\",\n\tint32(AUTH_BADVERF): \"AUTH_BADVERF\",\n\tint32(AUTH_REJECTEDVERF): \"AUTH_REJECTEDVERF\",\n\tint32(AUTH_TOOWEAK): \"AUTH_TOOWEAK\",\n\tint32(AUTH_INVALIDRESP): \"AUTH_INVALIDRESP\",\n\tint32(AUTH_FAILED): \"AUTH_FAILED\",\n\tint32(AUTH_KERB_GENERIC): \"AUTH_KERB_GENERIC\",\n\tint32(AUTH_TIMEEXPIRE): \"AUTH_TIMEEXPIRE\",\n\tint32(AUTH_TKT_FILE): \"AUTH_TKT_FILE\",\n\tint32(AUTH_DECODE): \"AUTH_DECODE\",\n\tint32(AUTH_NET_ADDR): \"AUTH_NET_ADDR\",\n\tint32(RPCSEC_GSS_CREDPROBLEM): \"RPCSEC_GSS_CREDPROBLEM\",\n\tint32(RPCSEC_GSS_CTXPROBLEM): \"RPCSEC_GSS_CTXPROBLEM\",\n}\nvar _XdrValues_Auth_stat = map[string]int32{\n\t\"AUTH_OK\": int32(AUTH_OK),\n\t\"AUTH_BADCRED\": int32(AUTH_BADCRED),\n\t\"AUTH_REJECTEDCRED\": int32(AUTH_REJECTEDCRED),\n\t\"AUTH_BADVERF\": int32(AUTH_BADVERF),\n\t\"AUTH_REJECTEDVERF\": int32(AUTH_REJECTEDVERF),\n\t\"AUTH_TOOWEAK\": int32(AUTH_TOOWEAK),\n\t\"AUTH_INVALIDRESP\": int32(AUTH_INVALIDRESP),\n\t\"AUTH_FAILED\": int32(AUTH_FAILED),\n\t\"AUTH_KERB_GENERIC\": int32(AUTH_KERB_GENERIC),\n\t\"AUTH_TIMEEXPIRE\": int32(AUTH_TIMEEXPIRE),\n\t\"AUTH_TKT_FILE\": int32(AUTH_TKT_FILE),\n\t\"AUTH_DECODE\": int32(AUTH_DECODE),\n\t\"AUTH_NET_ADDR\": int32(AUTH_NET_ADDR),\n\t\"RPCSEC_GSS_CREDPROBLEM\": int32(RPCSEC_GSS_CREDPROBLEM),\n\t\"RPCSEC_GSS_CTXPROBLEM\": int32(RPCSEC_GSS_CTXPROBLEM),\n}\nfunc (Auth_stat) XdrEnumNames() map[int32]string {\n\treturn _XdrNames_Auth_stat\n}\nfunc (v Auth_stat) String() string {\n\tif s, ok := _XdrNames_Auth_stat[int32(v)]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"Auth_stat#%d\", v)\n}\nfunc (v *Auth_stat) Scan(ss fmt.ScanState, _ rune) error {\n\tif tok, err := ss.Token(true, XdrSymChar); err != nil {\n\t\treturn err\n\t} else {\n\t\tstok := string(tok)\n\t\tif val, ok := _XdrValues_Auth_stat[stok]; ok {\n\t\t\t*v = Auth_stat(val)\n\t\t\treturn nil\n\t\t} else if stok == \"Auth_stat\" {\n\t\t\tif n, err := fmt.Fscanf(ss, \"#%d\", (*int32)(v));\n\t\t\t\tn == 1 && err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn XdrError(fmt.Sprintf(\"%s is not a valid Auth_stat.\", stok))\n\t}\n}\nfunc (v Auth_stat) GetU32() uint32 { return uint32(v) }\nfunc (v *Auth_stat) SetU32(n uint32) { *v = Auth_stat(n) }\nfunc (v *Auth_stat) XdrPointer() interface{} { return v }\nfunc (Auth_stat) XdrTypeName() string { return \"Auth_stat\" }\nfunc (v Auth_stat) XdrValue() interface{} { return v }\nfunc (v *Auth_stat) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\ntype XdrType_Auth_stat = *Auth_stat\nfunc XDR_Auth_stat(v *Auth_stat) *Auth_stat { return v }\ntype XdrType_Call_body = *Call_body\nfunc (v *Call_body) XdrPointer() interface{} { return v }\nfunc (Call_body) XdrTypeName() string { return \"Call_body\" }\nfunc (v Call_body) XdrValue() interface{} { return v }\nfunc (v *Call_body) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Call_body) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%srpcvers\", name), XDR_uint32(&v.Rpcvers))\n\tx.Marshal(x.Sprintf(\"%sprog\", name), XDR_uint32(&v.Prog))\n\tx.Marshal(x.Sprintf(\"%svers\", name), XDR_uint32(&v.Vers))\n\tx.Marshal(x.Sprintf(\"%sproc\", name), XDR_uint32(&v.Proc))\n\tx.Marshal(x.Sprintf(\"%scred\", name), XDR_Opaque_auth(&v.Cred))\n\tx.Marshal(x.Sprintf(\"%sverf\", name), XDR_Opaque_auth(&v.Verf))\n}\nfunc XDR_Call_body(v *Call_body) *Call_body { return v }\ntype XdrType_XdrAnon_Accepted_reply_Reply_data_Mismatch_info = *XdrAnon_Accepted_reply_Reply_data_Mismatch_info\nfunc (v *XdrAnon_Accepted_reply_Reply_data_Mismatch_info) XdrPointer() interface{} { return v }\nfunc (XdrAnon_Accepted_reply_Reply_data_Mismatch_info) XdrTypeName() string { return \"XdrAnon_Accepted_reply_Reply_data_Mismatch_info\" }\nfunc (v XdrAnon_Accepted_reply_Reply_data_Mismatch_info) XdrValue() interface{} { return v }\nfunc (v *XdrAnon_Accepted_reply_Reply_data_Mismatch_info) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *XdrAnon_Accepted_reply_Reply_data_Mismatch_info) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slow\", name), XDR_uint32(&v.Low))\n\tx.Marshal(x.Sprintf(\"%shigh\", name), XDR_uint32(&v.High))\n}\nfunc XDR_XdrAnon_Accepted_reply_Reply_data_Mismatch_info(v *XdrAnon_Accepted_reply_Reply_data_Mismatch_info) *XdrAnon_Accepted_reply_Reply_data_Mismatch_info { return v }\ntype _XdrArray_0_opaque [0]byte\nfunc (v *_XdrArray_0_opaque) GetByteSlice() []byte { return v[:] }\nfunc (v *_XdrArray_0_opaque) XdrTypeName() string { return \"opaque[]\" }\nfunc (v *_XdrArray_0_opaque) XdrValue() interface{} { return v[:] }\nfunc (v *_XdrArray_0_opaque) XdrPointer() interface{} { return (*[0]byte)(v) }\nfunc (v *_XdrArray_0_opaque) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *_XdrArray_0_opaque) String() string { return fmt.Sprintf(\"%x\", v[:]) }\nfunc (v *_XdrArray_0_opaque) Scan(ss fmt.ScanState, c rune) error {\n\treturn XdrArrayOpaqueScan(v[:], ss, c)\n}\nfunc (_XdrArray_0_opaque) XdrArraySize() uint32 {\n\tconst bound uint32 = 0 // Force error if not const or doesn't fit\n\treturn bound\n}\nfunc (u *XdrAnon_Accepted_reply_Reply_data) Results() *[0]byte {\n\tswitch u.Stat {\n\tcase SUCCESS:\n\t\tif v, ok := u.U.(*[0]byte); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero [0]byte\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"XdrAnon_Accepted_reply_Reply_data.Results accessed when Stat == %v\", u.Stat)\n\t\treturn nil\n\t}\n}\nfunc (u *XdrAnon_Accepted_reply_Reply_data) Mismatch_info() *XdrAnon_Accepted_reply_Reply_data_Mismatch_info {\n\tswitch u.Stat {\n\tcase PROG_MISMATCH:\n\t\tif v, ok := u.U.(*XdrAnon_Accepted_reply_Reply_data_Mismatch_info); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero XdrAnon_Accepted_reply_Reply_data_Mismatch_info\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"XdrAnon_Accepted_reply_Reply_data.Mismatch_info accessed when Stat == %v\", u.Stat)\n\t\treturn nil\n\t}\n}\nfunc (u XdrAnon_Accepted_reply_Reply_data) XdrValid() bool {\n\treturn true\n}\nfunc (u *XdrAnon_Accepted_reply_Reply_data) XdrUnionTag() XdrNum32 {\n\treturn XDR_Accept_stat(&u.Stat)\n}\nfunc (u *XdrAnon_Accepted_reply_Reply_data) XdrUnionTagName() string {\n\treturn \"Stat\"\n}\nfunc (u *XdrAnon_Accepted_reply_Reply_data) XdrUnionBody() XdrType {\n\tswitch u.Stat {\n\tcase SUCCESS:\n\t\treturn (*_XdrArray_0_opaque)(u.Results())\n\tcase PROG_MISMATCH:\n\t\treturn XDR_XdrAnon_Accepted_reply_Reply_data_Mismatch_info(u.Mismatch_info())\n\tdefault:\n\t\treturn nil\n\t}\n}\nfunc (u *XdrAnon_Accepted_reply_Reply_data) XdrUnionBodyName() string {\n\tswitch u.Stat {\n\tcase SUCCESS:\n\t\treturn \"Results\"\n\tcase PROG_MISMATCH:\n\t\treturn \"Mismatch_info\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\ntype XdrType_XdrAnon_Accepted_reply_Reply_data = *XdrAnon_Accepted_reply_Reply_data\nfunc (v *XdrAnon_Accepted_reply_Reply_data) XdrPointer() interface{} { return v }\nfunc (XdrAnon_Accepted_reply_Reply_data) XdrTypeName() string { return \"XdrAnon_Accepted_reply_Reply_data\" }\nfunc (v XdrAnon_Accepted_reply_Reply_data) XdrValue() interface{} { return v }\nfunc (v *XdrAnon_Accepted_reply_Reply_data) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *XdrAnon_Accepted_reply_Reply_data) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Accept_stat(&u.Stat).XdrMarshal(x, x.Sprintf(\"%sstat\", name))\n\tswitch u.Stat {\n\tcase SUCCESS:\n\t\tx.Marshal(x.Sprintf(\"%sresults\", name), (*_XdrArray_0_opaque)(u.Results()))\n\t\treturn\n\tcase PROG_MISMATCH:\n\t\tx.Marshal(x.Sprintf(\"%smismatch_info\", name), XDR_XdrAnon_Accepted_reply_Reply_data_Mismatch_info(u.Mismatch_info()))\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\nfunc XDR_XdrAnon_Accepted_reply_Reply_data(v *XdrAnon_Accepted_reply_Reply_data) *XdrAnon_Accepted_reply_Reply_data { return v}\ntype XdrType_Accepted_reply = *Accepted_reply\nfunc (v *Accepted_reply) XdrPointer() interface{} { return v }\nfunc (Accepted_reply) XdrTypeName() string { return \"Accepted_reply\" }\nfunc (v Accepted_reply) XdrValue() interface{} { return v }\nfunc (v *Accepted_reply) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Accepted_reply) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sverf\", name), XDR_Opaque_auth(&v.Verf))\n\tx.Marshal(x.Sprintf(\"%sreply_data\", name), XDR_XdrAnon_Accepted_reply_Reply_data(&v.Reply_data))\n}\nfunc XDR_Accepted_reply(v *Accepted_reply) *Accepted_reply { return v }\ntype XdrType_XdrAnon_Rejected_reply_Mismatch_info = *XdrAnon_Rejected_reply_Mismatch_info\nfunc (v *XdrAnon_Rejected_reply_Mismatch_info) XdrPointer() interface{} { return v }\nfunc (XdrAnon_Rejected_reply_Mismatch_info) XdrTypeName() string { return \"XdrAnon_Rejected_reply_Mismatch_info\" }\nfunc (v XdrAnon_Rejected_reply_Mismatch_info) XdrValue() interface{} { return v }\nfunc (v *XdrAnon_Rejected_reply_Mismatch_info) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *XdrAnon_Rejected_reply_Mismatch_info) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%slow\", name), XDR_uint32(&v.Low))\n\tx.Marshal(x.Sprintf(\"%shigh\", name), XDR_uint32(&v.High))\n}\nfunc XDR_XdrAnon_Rejected_reply_Mismatch_info(v *XdrAnon_Rejected_reply_Mismatch_info) *XdrAnon_Rejected_reply_Mismatch_info { return v }\nvar _XdrTags_Rejected_reply = map[int32]bool{\n\tXdrToI32(RPC_MISMATCH): true,\n\tXdrToI32(AUTH_ERROR): true,\n}\nfunc (_ Rejected_reply) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Rejected_reply\n}\nfunc (u *Rejected_reply) Mismatch_info() *XdrAnon_Rejected_reply_Mismatch_info {\n\tswitch u.Stat {\n\tcase RPC_MISMATCH:\n\t\tif v, ok := u.U.(*XdrAnon_Rejected_reply_Mismatch_info); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero XdrAnon_Rejected_reply_Mismatch_info\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Rejected_reply.Mismatch_info accessed when Stat == %v\", u.Stat)\n\t\treturn nil\n\t}\n}\nfunc (u *Rejected_reply) Rj_why() *Auth_stat {\n\tswitch u.Stat {\n\tcase AUTH_ERROR:\n\t\tif v, ok := u.U.(*Auth_stat); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Auth_stat\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Rejected_reply.Rj_why accessed when Stat == %v\", u.Stat)\n\t\treturn nil\n\t}\n}\nfunc (u Rejected_reply) XdrValid() bool {\n\tswitch u.Stat {\n\tcase RPC_MISMATCH,AUTH_ERROR:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Rejected_reply) XdrUnionTag() XdrNum32 {\n\treturn XDR_Reject_stat(&u.Stat)\n}\nfunc (u *Rejected_reply) XdrUnionTagName() string {\n\treturn \"Stat\"\n}\nfunc (u *Rejected_reply) XdrUnionBody() XdrType {\n\tswitch u.Stat {\n\tcase RPC_MISMATCH:\n\t\treturn XDR_XdrAnon_Rejected_reply_Mismatch_info(u.Mismatch_info())\n\tcase AUTH_ERROR:\n\t\treturn XDR_Auth_stat(u.Rj_why())\n\t}\n\treturn nil\n}\nfunc (u *Rejected_reply) XdrUnionBodyName() string {\n\tswitch u.Stat {\n\tcase RPC_MISMATCH:\n\t\treturn \"Mismatch_info\"\n\tcase AUTH_ERROR:\n\t\treturn \"Rj_why\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Rejected_reply = *Rejected_reply\nfunc (v *Rejected_reply) XdrPointer() interface{} { return v }\nfunc (Rejected_reply) XdrTypeName() string { return \"Rejected_reply\" }\nfunc (v Rejected_reply) XdrValue() interface{} { return v }\nfunc (v *Rejected_reply) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Rejected_reply) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Reject_stat(&u.Stat).XdrMarshal(x, x.Sprintf(\"%sstat\", name))\n\tswitch u.Stat {\n\tcase RPC_MISMATCH:\n\t\tx.Marshal(x.Sprintf(\"%smismatch_info\", name), XDR_XdrAnon_Rejected_reply_Mismatch_info(u.Mismatch_info()))\n\t\treturn\n\tcase AUTH_ERROR:\n\t\tx.Marshal(x.Sprintf(\"%srj_why\", name), XDR_Auth_stat(u.Rj_why()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Stat (%v) in Rejected_reply\", u.Stat)\n}\nfunc XDR_Rejected_reply(v *Rejected_reply) *Rejected_reply { return v}\nvar _XdrTags_Reply_body = map[int32]bool{\n\tXdrToI32(MSG_ACCEPTED): true,\n\tXdrToI32(MSG_DENIED): true,\n}\nfunc (_ Reply_body) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_Reply_body\n}\nfunc (u *Reply_body) Areply() *Accepted_reply {\n\tswitch u.Stat {\n\tcase MSG_ACCEPTED:\n\t\tif v, ok := u.U.(*Accepted_reply); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Accepted_reply\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Reply_body.Areply accessed when Stat == %v\", u.Stat)\n\t\treturn nil\n\t}\n}\nfunc (u *Reply_body) Rreply() *Rejected_reply {\n\tswitch u.Stat {\n\tcase MSG_DENIED:\n\t\tif v, ok := u.U.(*Rejected_reply); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Rejected_reply\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"Reply_body.Rreply accessed when Stat == %v\", u.Stat)\n\t\treturn nil\n\t}\n}\nfunc (u Reply_body) XdrValid() bool {\n\tswitch u.Stat {\n\tcase MSG_ACCEPTED,MSG_DENIED:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *Reply_body) XdrUnionTag() XdrNum32 {\n\treturn XDR_Reply_stat(&u.Stat)\n}\nfunc (u *Reply_body) XdrUnionTagName() string {\n\treturn \"Stat\"\n}\nfunc (u *Reply_body) XdrUnionBody() XdrType {\n\tswitch u.Stat {\n\tcase MSG_ACCEPTED:\n\t\treturn XDR_Accepted_reply(u.Areply())\n\tcase MSG_DENIED:\n\t\treturn XDR_Rejected_reply(u.Rreply())\n\t}\n\treturn nil\n}\nfunc (u *Reply_body) XdrUnionBodyName() string {\n\tswitch u.Stat {\n\tcase MSG_ACCEPTED:\n\t\treturn \"Areply\"\n\tcase MSG_DENIED:\n\t\treturn \"Rreply\"\n\t}\n\treturn \"\"\n}\ntype XdrType_Reply_body = *Reply_body\nfunc (v *Reply_body) XdrPointer() interface{} { return v }\nfunc (Reply_body) XdrTypeName() string { return \"Reply_body\" }\nfunc (v Reply_body) XdrValue() interface{} { return v }\nfunc (v *Reply_body) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *Reply_body) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Reply_stat(&u.Stat).XdrMarshal(x, x.Sprintf(\"%sstat\", name))\n\tswitch u.Stat {\n\tcase MSG_ACCEPTED:\n\t\tx.Marshal(x.Sprintf(\"%sareply\", name), XDR_Accepted_reply(u.Areply()))\n\t\treturn\n\tcase MSG_DENIED:\n\t\tx.Marshal(x.Sprintf(\"%srreply\", name), XDR_Rejected_reply(u.Rreply()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Stat (%v) in Reply_body\", u.Stat)\n}\nfunc XDR_Reply_body(v *Reply_body) *Reply_body { return v}\nvar _XdrTags_XdrAnon_Rpc_msg_Body = map[int32]bool{\n\tXdrToI32(CALL): true,\n\tXdrToI32(REPLY): true,\n}\nfunc (_ XdrAnon_Rpc_msg_Body) XdrValidTags() map[int32]bool {\n\treturn _XdrTags_XdrAnon_Rpc_msg_Body\n}\nfunc (u *XdrAnon_Rpc_msg_Body) Cbody() *Call_body {\n\tswitch u.Mtype {\n\tcase CALL:\n\t\tif v, ok := u.U.(*Call_body); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Call_body\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"XdrAnon_Rpc_msg_Body.Cbody accessed when Mtype == %v\", u.Mtype)\n\t\treturn nil\n\t}\n}\nfunc (u *XdrAnon_Rpc_msg_Body) Rbody() *Reply_body {\n\tswitch u.Mtype {\n\tcase REPLY:\n\t\tif v, ok := u.U.(*Reply_body); ok {\n\t\t\treturn v\n\t\t} else {\n\t\t\tvar zero Reply_body\n\t\t\tu.U = &zero\n\t\t\treturn &zero\n\t\t}\n\tdefault:\n\t\tXdrPanic(\"XdrAnon_Rpc_msg_Body.Rbody accessed when Mtype == %v\", u.Mtype)\n\t\treturn nil\n\t}\n}\nfunc (u XdrAnon_Rpc_msg_Body) XdrValid() bool {\n\tswitch u.Mtype {\n\tcase CALL,REPLY:\n\t\treturn true\n\t}\n\treturn false\n}\nfunc (u *XdrAnon_Rpc_msg_Body) XdrUnionTag() XdrNum32 {\n\treturn XDR_Msg_type(&u.Mtype)\n}\nfunc (u *XdrAnon_Rpc_msg_Body) XdrUnionTagName() string {\n\treturn \"Mtype\"\n}\nfunc (u *XdrAnon_Rpc_msg_Body) XdrUnionBody() XdrType {\n\tswitch u.Mtype {\n\tcase CALL:\n\t\treturn XDR_Call_body(u.Cbody())\n\tcase REPLY:\n\t\treturn XDR_Reply_body(u.Rbody())\n\t}\n\treturn nil\n}\nfunc (u *XdrAnon_Rpc_msg_Body) XdrUnionBodyName() string {\n\tswitch u.Mtype {\n\tcase CALL:\n\t\treturn \"Cbody\"\n\tcase REPLY:\n\t\treturn \"Rbody\"\n\t}\n\treturn \"\"\n}\ntype XdrType_XdrAnon_Rpc_msg_Body = *XdrAnon_Rpc_msg_Body\nfunc (v *XdrAnon_Rpc_msg_Body) XdrPointer() interface{} { return v }\nfunc (XdrAnon_Rpc_msg_Body) XdrTypeName() string { return \"XdrAnon_Rpc_msg_Body\" }\nfunc (v XdrAnon_Rpc_msg_Body) XdrValue() interface{} { return v }\nfunc (v *XdrAnon_Rpc_msg_Body) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (u *XdrAnon_Rpc_msg_Body) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tXDR_Msg_type(&u.Mtype).XdrMarshal(x, x.Sprintf(\"%smtype\", name))\n\tswitch u.Mtype {\n\tcase CALL:\n\t\tx.Marshal(x.Sprintf(\"%scbody\", name), XDR_Call_body(u.Cbody()))\n\t\treturn\n\tcase REPLY:\n\t\tx.Marshal(x.Sprintf(\"%srbody\", name), XDR_Reply_body(u.Rbody()))\n\t\treturn\n\t}\n\tXdrPanic(\"invalid Mtype (%v) in XdrAnon_Rpc_msg_Body\", u.Mtype)\n}\nfunc XDR_XdrAnon_Rpc_msg_Body(v *XdrAnon_Rpc_msg_Body) *XdrAnon_Rpc_msg_Body { return v}\ntype XdrType_Rpc_msg = *Rpc_msg\nfunc (v *Rpc_msg) XdrPointer() interface{} { return v }\nfunc (Rpc_msg) XdrTypeName() string { return \"Rpc_msg\" }\nfunc (v Rpc_msg) XdrValue() interface{} { return v }\nfunc (v *Rpc_msg) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }\nfunc (v *Rpc_msg) XdrRecurse(x XDR, name string) {\n\tif name != \"\" {\n\t\tname = x.Sprintf(\"%s.\", name)\n\t}\n\tx.Marshal(x.Sprintf(\"%sxid\", name), XDR_uint32(&v.Xid))\n\tx.Marshal(x.Sprintf(\"%sbody\", name), XDR_XdrAnon_Rpc_msg_Body(&v.Body))\n}\nfunc XDR_Rpc_msg(v *Rpc_msg) *Rpc_msg { return v }\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs4/repo/internal/rpc.x",
    "content": "/* RPC message format as defined by RFC 5531 */\n\nenum auth_flavor {\n  AUTH_NONE       = 0,\n  AUTH_SYS        = 1,\n  AUTH_SHORT      = 2,\n  AUTH_DH         = 3,\n  RPCSEC_GSS      = 6\n};\n\nstruct opaque_auth {\n  auth_flavor flavor;\n  opaque body<400>;\n};\n\nenum msg_type {\n  CALL  = 0,\n  REPLY = 1\n};\n\n/* A reply to a call message can take on two forms: the message was\n   either accepted or rejected. */\nenum reply_stat {\n  MSG_ACCEPTED = 0,\n  MSG_DENIED   = 1\n};\n\n/* Given that a call message was accepted, the following is the status\n   of an attempt to call a remote procedure. */\nenum accept_stat {\n  SUCCESS       = 0, /* RPC executed successfully       */\n  PROG_UNAVAIL  = 1, /* remote hasn't exported program  */\n  PROG_MISMATCH = 2, /* remote can't support version #  */\n  PROC_UNAVAIL  = 3, /* program can't support procedure */\n  GARBAGE_ARGS  = 4, /* procedure can't decode params   */\n  SYSTEM_ERR    = 5  /* e.g. memory allocation failure  */\n};\n\n/* Reasons why a call message was rejected: */\nenum reject_stat {\n  RPC_MISMATCH = 0, /* RPC version number != 2          */\n  AUTH_ERROR = 1    /* remote can't authenticate caller */\n};\n\n/* Why authentication failed: */\nenum auth_stat {\n  AUTH_OK           = 0,  /* success                        */\n  /*\n   * failed at remote end\n   */\n  AUTH_BADCRED      = 1,  /* bad credential (seal broken)   */\n  AUTH_REJECTEDCRED = 2,  /* client must begin new session  */\n  AUTH_BADVERF      = 3,  /* bad verifier (seal broken)     */\n  AUTH_REJECTEDVERF = 4,  /* verifier expired or replayed   */\n  AUTH_TOOWEAK      = 5,  /* rejected for security reasons  */\n  /*\n   * failed locally\n   */\n  AUTH_INVALIDRESP  = 6,  /* bogus response verifier        */\n  AUTH_FAILED       = 7,  /* reason unknown                 */\n  /*\n   * AUTH_KERB errors; deprecated.  See [RFC2695]\n   */\n  AUTH_KERB_GENERIC = 8,  /* kerberos generic error */\n  AUTH_TIMEEXPIRE = 9,    /* time of credential expired */\n  AUTH_TKT_FILE = 10,     /* problem with ticket file */\n  AUTH_DECODE = 11,       /* can't decode authenticator */\n  AUTH_NET_ADDR = 12,     /* wrong net address in ticket */\n  /*\n   * RPCSEC_GSS GSS related errors\n   */\n  RPCSEC_GSS_CREDPROBLEM = 13, /* no credentials for user */\n  RPCSEC_GSS_CTXPROBLEM = 14   /* problem with context */\n};\n\n/* Body of an RPC call: */\nstruct call_body {\n  unsigned int rpcvers;       /* must be equal to two (2) */\n  unsigned int prog;\n  unsigned int vers;\n  unsigned int proc;\n  opaque_auth cred;\n  opaque_auth verf;\n  /* procedure-specific parameters start here */\n};\n\n/* Reply to an RPC call that was accepted by the server: */\nstruct accepted_reply {\n  opaque_auth verf;\n  union switch (accept_stat stat) {\n  case SUCCESS:\n    opaque results[0];\n    /*\n     * procedure-specific results start here\n     */\n  case PROG_MISMATCH:\n    struct {\n      unsigned int low;\n      unsigned int high;\n    } mismatch_info;\n  default:\n    /*\n     * Void.  Cases include PROG_UNAVAIL, PROC_UNAVAIL,\n     * GARBAGE_ARGS, and SYSTEM_ERR.\n     */\n    void;\n  } reply_data;\n};\n\n/* Reply to an RPC call that was rejected by the server: */\nunion rejected_reply switch (reject_stat stat) {\n case RPC_MISMATCH:\n   struct {\n     unsigned int low;\n     unsigned int high;\n   } mismatch_info;\n case AUTH_ERROR:\n   auth_stat rj_why;\n};\n\n/* Body of a reply to an RPC call: */\nunion reply_body switch (reply_stat stat) {\n case MSG_ACCEPTED:\n   accepted_reply areply;\n case MSG_DENIED:\n   rejected_reply rreply;\n};\n\n/* The RPC message: */\nstruct rpc_msg {\n  unsigned int xid;\n  union switch (msg_type mtype) {\n  case CALL:\n    call_body cbody;\n  case REPLY:\n    reply_body rbody;\n  } body;\n};\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs4/repo/internal/types.go",
    "content": "package internal\n\n//goland:noinspection GoSnakeCaseUsage\ntype Uint32_t = uint32\n//goland:noinspection GoSnakeCaseUsage\ntype Uint64_t = uint64\n//goland:noinspection GoSnakeCaseUsage\ntype Int64_t = int64\n\n//goland:noinspection GoSnakeCaseUsage\ntype XDR_Uint32_t = *XdrUint32\n//goland:noinspection GoSnakeCaseUsage\ntype XdrType_Uint32_t = XdrType_uint32\n//goland:noinspection GoSnakeCaseUsage\ntype XDR_Uint64_t = *XdrUint64\n//goland:noinspection GoSnakeCaseUsage\ntype XdrType_Uint64_t = XdrType_uint64\n//goland:noinspection GoSnakeCaseUsage\ntype XdrType_Int64_t = XdrType_int64\n//goland:noinspection GoSnakeCaseUsage\ntype XDR_Int64_t = *XdrInt64\n\nfunc MinUint64(i1, i2 uint64) uint64 {\n\tif i1 < i2 {\n\t\treturn i1\n\t}\n\treturn i2\n}"
  },
  {
    "path": "server/plugin/plg_backend_nfs4/repo/nfs4/client.go",
    "content": "package nfs4\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_nfs4/repo/internal\"\n)\n\nconst NfsReadBlockLen = 512 * 1024\n\nvar standardNfsAttrs = Bitmap4{\n\t1<<FATTR4_TYPE | 1<<FATTR4_SIZE,\n\t1 << (FATTR4_TIME_MODIFY - 32),\n}\n\ntype NfsInterface interface {\n\tPing() error\n\tClose()\n\n\tGetFileList(path string) ([]FileInfo, error)\n\tGetFileInfo(path string) (FileInfo, error)\n\tReadFileAll(path string, writer io.Writer) (uint64, error)\n\tReadFile(path string, offset, count uint64, writer io.Writer) (uint64, error)\n\n\tReWriteFile(path string, reader io.Reader) (written uint64, err error)\n\tWriteFile(path string, truncate bool, offset uint64, reader io.Reader) (written uint64, err error)\n\n\tDeleteFile(path string) error\n\tMakePath(path string) error\n}\ntype NfsClient struct {\n\tconn net.Conn\n\txid  uint32\n\n\tnfsSeqId uint32\n\n\tauthType Auth_flavor\n\tauthData []byte\n\n\tclientId      string\n\tclientIdShort uint64\n\topenConfirmed bool\n\n\trootFh Nfs_fh4\n}\n\nvar _ NfsInterface = &NfsClient{}\n\ntype AuthParams struct {\n\tUid, Gid    uint32\n\tMachineName string\n}\n\ntype FileInfo struct {\n\tName  string\n\tIsDir bool\n\tSize  uint64\n\tMtime time.Time\n}\n\n// Create the NFS client with the specified parameters. The `server` string must include port.\n// The default NFS port is 2049. E.g.: \"127.0.0.1:2049\".\n// `auth` should contain a fairly unique MachineId to make sure clients can be distinguished.\nfunc NewNfsClient(ctx context.Context, server string, auth AuthParams) (*NfsClient, error) {\n\td := net.Dialer{}\n\tconn, err := d.DialContext(ctx, \"tcp\", server)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewNfsClientWithConn(conn, auth)\n}\n\nfunc NewNfsClientWithConn(conn net.Conn, auth AuthParams) (*NfsClient, error) {\n\tclientId, err := os.Hostname()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trandId := make([]byte, 8)\n\t_, _ = rand.Read(randId)\n\tclientId += \"-\" + hex.EncodeToString(randId)\n\n\tcli := &NfsClient{\n\t\tconn:     conn,\n\t\tauthType: AUTH_SYS,\n\t\tauthData: makeAuthData(auth),\n\t\tnfsSeqId: 0,\n\t\tclientId: clientId,\n\t}\n\tcl := NewCleanup(cli.Close)\n\tdefer cl.Cleanup()\n\n\terr = cli.Ping()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = cli.setClientId()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = cli.retrieveRootFh()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcl.Disarm()\n\treturn cli, nil\n}\n\nfunc (c *NfsClient) Close() {\n\t_ = c.conn.Close()\n}\n\nfunc makeAuthData(auth AuthParams) []byte {\n\tmachName := auth.MachineName\n\tif len(machName) > 255 {\n\t\tmachName = machName[0:255]\n\t}\n\n\tap := Authsys_parms{\n\t\tMachinename: machName,\n\t\tUid:         auth.Uid,\n\t\tGid:         auth.Gid,\n\t\tGids:        nil,\n\t}\n\n\t// Fill the machine ID (needs not to be super-unique but nice to have them distinct)\n\t_ = binary.Read(rand.Reader, binary.LittleEndian, &ap.Stamp)\n\n\tapBuf := bytes.NewBuffer([]byte{})\n\tXdrOut{Out: apBuf}.Marshal(\"\", &ap)\n\n\treturn apBuf.Bytes()\n}\n\nfunc (c *NfsClient) sendMessage(proc XdrProc) (xid uint32, err error) {\n\txid = c.xid\n\tc.xid++\n\n\tmsg := Rpc_msg{\n\t\tXid: xid,\n\t\tBody: XdrAnon_Rpc_msg_Body{\n\t\t\tMtype: CALL,\n\t\t\tU: &Call_body{\n\t\t\t\tRpcvers: 2,\n\t\t\t\tProg:    proc.Prog(),\n\t\t\t\tVers:    proc.Vers(),\n\t\t\t\tProc:    proc.Proc(),\n\t\t\t\tCred: Opaque_auth{\n\t\t\t\t\tFlavor: c.authType,\n\t\t\t\t\tBody:   c.authData,\n\t\t\t\t},\n\t\t\t\tVerf: Opaque_auth{\n\t\t\t\t\tFlavor: AUTH_NONE,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\t// The marshaller for some reason loves to panic.\n\tdefer func() {\n\t\tif i := recover(); i != nil {\n\t\t\tif e, ok := i.(XdrError); ok {\n\t\t\t\terr = e\n\t\t\t} else {\n\t\t\t\tpanic(i)\n\t\t\t}\n\t\t}\n\t}()\n\n\tbuffer := bytes.NewBuffer([]byte{})\n\tout := XdrOut{Out: buffer}\n\tout.Marshal(\"\", &msg)\n\tif _, ok := proc.GetArg().(XdrType_void); !ok {\n\t\tout.Marshal(\"\", proc.GetArg())\n\t}\n\n\t// Yep, the RPC protocol requires this strange OR\n\terr = binary.Write(c.conn, binary.BigEndian, 0x80000000|uint32(buffer.Len()))\n\tif err != nil {\n\t\treturn\n\t}\n\t_, err = c.conn.Write(buffer.Bytes())\n\tif err != nil {\n\t\treturn\n\t}\n\n\treturn\n}\n\nfunc (c *NfsClient) readNfsMessage(result XdrType) (xid uint32, err error) {\n\tlenBuf := make([]byte, 4)\n\t_, err = io.ReadFull(c.conn, lenBuf)\n\tif err != nil {\n\t\treturn\n\t}\n\t// The RPC protocol sets the MSB to 1 for length fields. Don't ask me why.\n\tmsgLen := binary.BigEndian.Uint32(lenBuf) & 0x7fffffff\n\n\tmsgBuf := make([]byte, msgLen)\n\t_, err = io.ReadFull(c.conn, msgBuf)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// The unmarshaller for some reason loves to panic. Sigh.\n\tdefer func() {\n\t\tif i := recover(); i != nil {\n\t\t\tif e, ok := i.(XdrError); ok {\n\t\t\t\terr = e\n\t\t\t} else {\n\t\t\t\tpanic(i)\n\t\t\t}\n\t\t}\n\t}()\n\n\treply := Rpc_msg{}\n\tin := XdrIn{In: bytes.NewReader(msgBuf)}\n\tin.Marshal(\"\", &reply)\n\tif _, ok := result.(XdrType_void); !ok {\n\t\tin.Marshal(\"\", result)\n\t}\n\n\tif !c.isRpcSuccess(&reply) {\n\t\terr = fmt.Errorf(\"RPC error: %s\", c.getRpcError(&reply))\n\t\treturn\n\t}\n\txid = reply.Xid\n\n\treturn\n}\n\n// Returns true iff msg is an accepted REPLY with status SUCCESS.\nfunc (c *NfsClient) isRpcSuccess(msg *Rpc_msg) bool {\n\treturn msg != nil &&\n\t\tmsg.Body.Mtype == REPLY &&\n\t\tmsg.Body.Rbody().Stat == MSG_ACCEPTED &&\n\t\tmsg.Body.Rbody().Areply().Reply_data.Stat == SUCCESS\n}\n\n// An *Rpc_msg can represent an error.  Call IsSuccess to see if there\n// was actually an error.\nfunc (c *NfsClient) getRpcError(m *Rpc_msg) string {\n\tif m.Body.Mtype != REPLY {\n\t\treturn \"RPC message not a REPLY\"\n\t} else if m.Body.Rbody().Stat == MSG_ACCEPTED {\n\t\tstat := m.Body.Rbody().Areply().Reply_data.Stat\n\t\tc := stat.String()\n\t\tif stat == PROG_MISMATCH {\n\t\t\tmmi := m.Body.Rbody().Areply().Reply_data.Mismatch_info()\n\t\t\tc = fmt.Sprintf(\"%s (low %d, high %d)\", c, mmi.Low, mmi.High)\n\t\t}\n\t\treturn c\n\t} else if m.Body.Rbody().Stat == MSG_DENIED {\n\t\tstat := m.Body.Rbody().Rreply().Stat\n\t\tc := stat.String()\n\t\treturn c\n\t}\n\treturn \"Invalid reply_stat\"\n}\n\nfunc (c *NfsClient) Ping() error {\n\tnullProc := XdrProc_NFSPROC4_NULL{}\n\n\txid, err := c.sendMessage(&nullProc)\n\tif err != nil {\n\t\treturn err\n\t}\n\txidRes, err := c.readNfsMessage(nullProc.GetRes())\n\tif err != nil {\n\t\treturn err\n\t}\n\tif xidRes != xid {\n\t\treturn fmt.Errorf(\"mismathced xids: %d and %d\", xid, xidRes)\n\t}\n\n\treturn nil\n}\n\nfunc (c *NfsClient) runNfsTransaction(ops []Nfs_argop4, pathHint string) ([]Nfs_resop4, error) {\n\tcompound := XdrProc_NFSPROC4_COMPOUND{}\n\n\targs := compound.GetArg().(*COMPOUND4args)\n\targs.Argarray = ops\n\n\txid, err := c.sendMessage(&compound)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\txid2, err := c.readNfsMessage(compound.GetRes())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif xid != xid2 {\n\t\treturn nil, fmt.Errorf(\"xids don't match: %d and %d\", xid, xid2)\n\t}\n\n\tres := compound.GetRes().(*COMPOUND4res)\n\t// TODO: translate the error better\n\tif res.Status != NFS4_OK {\n\t\treturn nil, &NfsError{\n\t\t\tPath:      pathHint,\n\t\t\tErrorCode: NfsErrorCode(res.Status),\n\t\t\tErrorString: fmt.Sprintf(\"NFS error: %s (%d), path='%s'\",\n\t\t\t\tres.Status.String(), int32(res.Status), pathHint),\n\t\t}\n\t}\n\n\treturn res.Resarray, nil\n}\n\nfunc (c *NfsClient) setClientId() error {\n\tres, err := c.runNfsTransaction([]Nfs_argop4{{\n\t\tArgop: OP_SETCLIENTID,\n\t\tU: &SETCLIENTID4args{\n\t\t\tClient: Nfs_client_id4{\n\t\t\t\tVerifier: Verifier4{},\n\t\t\t\tId:       []byte(c.clientId),\n\t\t\t},\n\t\t\tCallback:       Cb_client4{},\n\t\t\tCallback_ident: 0,\n\t\t},\n\t}}, \"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tresOk := res[0].Opsetclientid().Resok4()\n\tc.clientIdShort = resOk.Clientid\n\n\t_, err = c.runNfsTransaction([]Nfs_argop4{{\n\t\tArgop: OP_SETCLIENTID_CONFIRM,\n\t\tU: &SETCLIENTID_CONFIRM4args{\n\t\t\tClientid:            resOk.Clientid,\n\t\t\tSetclientid_confirm: resOk.Setclientid_confirm,\n\t\t},\n\t}}, \"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (c *NfsClient) retrieveRootFh() error {\n\tres, err := c.runNfsTransaction([]Nfs_argop4{\n\t\t{\n\t\t\tArgop: OP_PUTROOTFH,\n\t\t},\n\t\t{\n\t\t\tArgop: OP_GETFH,\n\t\t},\n\t}, \"/\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.rootFh = res[1].Opgetfh().Resok4().Object\n\treturn nil\n}\n\nfunc splitPath(path string) []string {\n\tsplits := strings.Split(path, \"/\")\n\tcurPos := 0\n\tfor _, s := range splits {\n\t\tif s == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tsplits[curPos] = s\n\t\tcurPos++\n\t}\n\treturn splits[0:curPos]\n}\n\nfunc (c *NfsClient) GetFileList(path string) ([]FileInfo, error) {\n\tvar args = c.makePathLookupArgs(splitPath(path))\n\n\targs = append(args,\n\t\tNfs_argop4{Argop: OP_GETFH},\n\t\tNfs_argop4{Argop: OP_READDIR,\n\t\t\tU: &READDIR4args{\n\t\t\t\tCookie:       0,\n\t\t\t\tCookieverf:   Verifier4{},\n\t\t\t\tDircount:     1024 * 128,\n\t\t\t\tMaxcount:     1024 * 128,\n\t\t\t\tAttr_request: standardNfsAttrs,\n\t\t\t}},\n\t)\n\n\tres, err := c.runNfsTransaction(args, path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar fileList []FileInfo\n\n\tdirFh := res[len(res)-2].Opgetfh().Resok4().Object\n\n\tcurDirList := res[len(res)-1].Opreaddir().Resok4()\n\tfor {\n\t\tent := curDirList.Reply.Entries\n\t\tif ent == nil {\n\t\t\tbreak\n\t\t}\n\t\tfor {\n\t\t\tfileList = append(fileList, c.translateFileMeta(string(ent.Name), ent.Attrs))\n\t\t\tif ent.Nextentry == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tent = ent.Nextentry\n\t\t}\n\n\t\tif curDirList.Reply.Eof {\n\t\t\tbreak\n\t\t}\n\t\tres, err := c.runNfsTransaction([]Nfs_argop4{\n\t\t\t{\n\t\t\t\tArgop: OP_PUTFH,\n\t\t\t\tU:     &PUTFH4args{Object: dirFh},\n\t\t\t},\n\t\t\t{\n\t\t\t\tArgop: OP_READDIR,\n\t\t\t\tU: &READDIR4args{\n\t\t\t\t\tCookie:       ent.Cookie,\n\t\t\t\t\tCookieverf:   curDirList.Cookieverf,\n\t\t\t\t\tDircount:     1024 * 128,\n\t\t\t\t\tMaxcount:     1024 * 128,\n\t\t\t\t\tAttr_request: standardNfsAttrs,\n\t\t\t\t},\n\t\t\t},\n\t\t}, path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcurDirList = res[1].Opreaddir().Resok4()\n\t}\n\n\treturn fileList, nil\n}\n\n// Make the commands to navigate the path to its leaf\nfunc (c *NfsClient) makePathLookupArgs(path []string) []Nfs_argop4 {\n\tvar args []Nfs_argop4\n\targs = append(args, Nfs_argop4{Argop: OP_PUTROOTFH})\n\n\t// Add lookups for the path components\n\tfor _, p := range path {\n\t\targs = append(args, Nfs_argop4{\n\t\t\tArgop: OP_LOOKUP,\n\t\t\tU:     &LOOKUP4args{Objname: Component4(p)},\n\t\t})\n\t}\n\treturn args\n}\n\nfunc (c *NfsClient) translateFileMeta(name string, attrs Fattr4) FileInfo {\n\tres := FileInfo{\n\t\tName: name,\n\t}\n\n\tcurOff := 0\n\n\tatm := attrs.Attrmask\n\tif len(atm) > 0 && atm[0]&(1<<FATTR4_TYPE) != 0 {\n\t\tfileType := binary.BigEndian.Uint32(attrs.Attr_vals[curOff : curOff+4])\n\t\tcurOff += 4\n\n\t\tres.IsDir = Nfs_ftype4(fileType) == NF4DIR\n\t}\n\n\tif len(atm) > 0 && atm[0]&(1<<FATTR4_SIZE) != 0 {\n\t\tres.Size = binary.BigEndian.Uint64(attrs.Attr_vals[curOff : curOff+8])\n\t\tcurOff += 8\n\t}\n\n\tif len(atm) > 1 && atm[1]&(1<<(FATTR4_TIME_MODIFY-32)) != 0 {\n\t\tmtimeSec := binary.BigEndian.Uint64(attrs.Attr_vals[curOff : curOff+8])\n\t\tcurOff += 8\n\n\t\tmtimeNsec := binary.BigEndian.Uint32(attrs.Attr_vals[curOff : curOff+4])\n\t\tcurOff += 4\n\n\t\t// I hope this works for times before 1970-01-01...\n\t\tres.Mtime = time.Unix(int64(mtimeSec), int64(mtimeNsec))\n\t}\n\n\treturn res\n}\n\nfunc (c *NfsClient) GetFileInfo(path string) (FileInfo, error) {\n\targs := c.makePathLookupArgs(splitPath(path))\n\targs = append(args,\n\t\tNfs_argop4{\n\t\t\tArgop: OP_GETATTR,\n\t\t\tU: &GETATTR4args{\n\t\t\t\tAttr_request: standardNfsAttrs,\n\t\t\t},\n\t\t})\n\n\tres, err := c.runNfsTransaction(args, path)\n\tif err != nil {\n\t\treturn FileInfo{}, err\n\t}\n\n\tsplits := splitPath(path)\n\tvar name string\n\tif len(splits) == 1 {\n\t\tname = path\n\t} else {\n\t\tname = splits[len(splits)-1]\n\t}\n\n\tresInfo := c.translateFileMeta(name,\n\t\tres[len(res)-1].Opgetattr().Resok4().Obj_attributes)\n\treturn resInfo, nil\n}\n\nfunc (c *NfsClient) ReadFileAll(path string, writer io.Writer) (uint64, error) {\n\treturn c.ReadFile(path, 0, math.MaxUint64, writer)\n}\n\nfunc (c *NfsClient) ReadFile(path string, offset, count uint64, writer io.Writer) (uint64, error) {\n\tanonymousStateId := Stateid4{}\n\targs := c.makePathLookupArgs(splitPath(path))\n\n\targs = append(args,\n\t\tNfs_argop4{Argop: OP_GETFH},\n\t\tNfs_argop4{\n\t\t\tArgop: OP_READ,\n\t\t\tU: &READ4args{\n\t\t\t\tStateid: anonymousStateId,\n\t\t\t\tOffset:  offset,\n\t\t\t\tCount:   Count4(MinUint64(NfsReadBlockLen, count)),\n\t\t\t},\n\t\t})\n\n\tres, err := c.runNfsTransaction(args, path)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tflDataBlock := res[len(res)-1].Opread().Resok4()\n\tfileFh := res[len(res)-2].Opgetfh().Resok4().Object\n\n\tvar dataRead uint64\n\tfor {\n\t\t_, err := writer.Write(flDataBlock.Data)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tln := len(flDataBlock.Data)\n\t\toffset += uint64(ln)\n\t\tcount -= uint64(ln)\n\t\tdataRead += uint64(ln)\n\t\tif flDataBlock.Eof || count == 0 {\n\t\t\tbreak\n\t\t}\n\n\t\t// Get the next file block\n\t\tres, err := c.runNfsTransaction([]Nfs_argop4{\n\t\t\t{\n\t\t\t\tArgop: OP_PUTFH,\n\t\t\t\tU:     &PUTFH4args{Object: fileFh},\n\t\t\t},\n\t\t\t{\n\t\t\t\tArgop: OP_READ,\n\t\t\t\tU: &READ4args{\n\t\t\t\t\tStateid: anonymousStateId,\n\t\t\t\t\tOffset:  offset,\n\t\t\t\t\tCount:   Count4(MinUint64(NfsReadBlockLen, count)),\n\t\t\t\t},\n\t\t\t},\n\t\t}, path)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tflDataBlock = res[1].Opread().Resok4()\n\t}\n\n\treturn dataRead, nil\n}\n\nfunc (c *NfsClient) openFileForWrite(path string, truncate bool) (Stateid4, Nfs_fh4, error) {\n\tsplits := splitPath(path)\n\n\t// Put the directory FH as the current one\n\targs := c.makePathLookupArgs(splits[0 : len(splits)-1])\n\t// Make the file claim (i.e. the file name on top of the directory FH)\n\tflClaim := Component4(splits[len(splits)-1])\n\n\tvar fileAttrs Fattr4\n\tif truncate {\n\t\t// Set the file size and mode (Unix access mask)\n\t\tfileAttrs.Attr_vals = make([]byte, 12)\n\t\t// This file size is set to 0\n\t\tbinary.BigEndian.PutUint32(fileAttrs.Attr_vals[8:], MODE4_WUSR|MODE4_RUSR|MODE4_WGRP|MODE4_RGRP)\n\t\tfileAttrs.Attrmask = Bitmap4{1 << FATTR4_SIZE, 1 << (FATTR4_MODE - 32)}\n\t} else {\n\t\t// Set the file mode (Unix access mask)\n\t\tfileAttrs.Attr_vals = make([]byte, 4)\n\t\tbinary.BigEndian.PutUint32(fileAttrs.Attr_vals, MODE4_WUSR|MODE4_RUSR|MODE4_WGRP|MODE4_RGRP)\n\t\tfileAttrs.Attrmask = Bitmap4{0, 1 << (FATTR4_MODE - 32)}\n\t}\n\n\targs = append(args,\n\t\tNfs_argop4{\n\t\t\tArgop: OP_OPEN,\n\t\t\tU: &OPEN4args{\n\t\t\t\tSeqid:        c.nfsSeqId,\n\t\t\t\tShare_access: OPEN4_SHARE_ACCESS_WRITE,\n\t\t\t\tShare_deny:   OPEN4_SHARE_DENY_NONE,\n\t\t\t\tOwner: Open_owner4{\n\t\t\t\t\tClientid: c.clientIdShort,\n\t\t\t\t\tOwner:    []byte(c.clientId),\n\t\t\t\t},\n\t\t\t\tOpenhow: Openflag4{\n\t\t\t\t\tOpentype: OPEN4_CREATE,\n\t\t\t\t\tU: &Createhow4{\n\t\t\t\t\t\tMode: UNCHECKED4,\n\t\t\t\t\t\tU:    &fileAttrs,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tClaim: Open_claim4{\n\t\t\t\t\tClaim: CLAIM_NULL,\n\t\t\t\t\tU:     &flClaim,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tNfs_argop4{\n\t\t\tArgop: OP_GETFH,\n\t\t})\n\n\tres, err := c.runNfsTransaction(args, path)\n\tc.incrementNfsSeq(err)\n\tif err != nil {\n\t\treturn Stateid4{}, Nfs_fh4{}, err\n\t}\n\n\topenRes := res[len(res)-2].Opopen().Resok4()\n\topenFh := res[len(res)-1].Opgetfh().Resok4().Object\n\n\t// We need to confirm the opened file receipt the first time we run the operation\n\tif !c.openConfirmed {\n\t\tres, err = c.runNfsTransaction([]Nfs_argop4{\n\t\t\t{\n\t\t\t\tArgop: OP_PUTFH,\n\t\t\t\tU:     &PUTFH4args{Object: openFh},\n\t\t\t},\n\t\t\t{\n\t\t\t\tArgop: OP_OPEN_CONFIRM,\n\t\t\t\tU: &OPEN_CONFIRM4args{\n\t\t\t\t\tOpen_stateid: openRes.Stateid,\n\t\t\t\t\tSeqid:        c.nfsSeqId,\n\t\t\t\t},\n\t\t\t},\n\t\t}, path)\n\t\tc.incrementNfsSeq(err)\n\t\tif err != nil {\n\t\t\treturn Stateid4{}, Nfs_fh4{}, err\n\t\t}\n\t\tc.openConfirmed = true\n\t\treturn res[len(res)-1].Opopen_confirm().Resok4().Open_stateid, openFh, nil\n\t}\n\n\treturn openRes.Stateid, openFh, nil\n}\n\nfunc (c *NfsClient) closeFile(stateId Stateid4, fh Nfs_fh4, path string) error {\n\t_, err := c.runNfsTransaction([]Nfs_argop4{\n\t\t{\n\t\t\tArgop: OP_PUTFH,\n\t\t\tU:     &PUTFH4args{Object: fh},\n\t\t},\n\t\t{\n\t\t\tArgop: OP_CLOSE,\n\t\t\tU: &CLOSE4args{\n\t\t\t\tOpen_stateid: stateId,\n\t\t\t\tSeqid:        c.nfsSeqId,\n\t\t\t},\n\t\t},\n\t}, path)\n\tc.incrementNfsSeq(err)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (c *NfsClient) ReWriteFile(path string, reader io.Reader) (written uint64, err error) {\n\treturn c.WriteFile(path, true, 0, reader)\n}\n\nfunc (c *NfsClient) WriteFile(path string, truncate bool, offset uint64,\n\treader io.Reader) (written uint64, err error) {\n\n\tstateId, fh, err := c.openFileForWrite(path, truncate)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer func() {\n\t\terr = c.closeFile(stateId, fh, path)\n\t}()\n\n\tblock := make([]byte, NfsReadBlockLen)\n\tfor {\n\t\tvar curRead int\n\t\tcurRead, err = reader.Read(block)\n\t\tif curRead == 0 || err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t// Write the block!\n\t\terr = c.writeBlock(stateId, fh, written+offset, block[0:curRead], path)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\twritten += uint64(curRead)\n\t}\n\n\treturn\n}\n\nfunc (c *NfsClient) writeBlock(id Stateid4, fh Nfs_fh4, offset uint64, data []byte, path string) error {\n\tfor len(data) > 0 {\n\t\tres, err := c.runNfsTransaction([]Nfs_argop4{\n\t\t\t{\n\t\t\t\tArgop: OP_PUTFH,\n\t\t\t\tU:     &PUTFH4args{Object: fh},\n\t\t\t},\n\t\t\t{\n\t\t\t\tArgop: OP_WRITE,\n\t\t\t\tU: &WRITE4args{\n\t\t\t\t\tStateid: id,\n\t\t\t\t\tOffset:  offset,\n\t\t\t\t\tStable:  UNSTABLE4,\n\t\t\t\t\tData:    data,\n\t\t\t\t},\n\t\t\t},\n\t\t}, path)\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\twritten := res[1].Opwrite().Resok4().Count\n\t\tdata = data[written:]\n\t}\n\n\treturn nil\n}\n\nfunc (c *NfsClient) DeleteFile(path string) error {\n\tsplits := splitPath(path)\n\t// Put the directory FH as the current one\n\targs := c.makePathLookupArgs(splits[0 : len(splits)-1])\n\t// Make the file claim (i.e. the file name on top of the directory FH)\n\tflClaim := Component4(splits[len(splits)-1])\n\n\targs = append(args,\n\t\tNfs_argop4{\n\t\t\tArgop: OP_REMOVE,\n\t\t\tU: &REMOVE4args{\n\t\t\t\tTarget: flClaim,\n\t\t\t},\n\t\t})\n\n\t_, err := c.runNfsTransaction(args, path)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (c *NfsClient) MakePath(path string) error {\n\tcurPath := \"\"\n\tvar curPathElems []string\n\n\tfor _, curElem := range splitPath(path) {\n\t\tif curPath != \"\" {\n\t\t\tcurPath += \"/\"\n\t\t}\n\t\tcurPath += curElem\n\t\tcurPathElems = append(curPathElems, curElem)\n\n\t\tfi, err := c.GetFileInfo(curPath)\n\t\tif err == nil && !fi.IsDir {\n\t\t\treturn &NfsError{\n\t\t\t\tErrorCode: ERROR_NOTDIR,\n\t\t\t\tErrorString: fmt.Sprintf(\"NFS error: should be a directory (%d), path='%s'\",\n\t\t\t\t\tERROR_NOTDIR, curPath),\n\t\t\t\tPath: curPath,\n\t\t\t}\n\t\t}\n\n\t\tif err != nil {\n\t\t\tif IsNfsError(err, ERROR_NOENT) {\n\t\t\t\targs := c.makePathLookupArgs(curPathElems[0 : len(curPathElems)-1])\n\t\t\t\t// Make the file claim (i.e. the file name on top of the directory FH)\n\t\t\t\tflClaim := Component4(curElem)\n\n\t\t\t\t// Set the file mode (Unix access mask)\n\t\t\t\tvar dirAttrs Fattr4\n\t\t\t\tdirAttrs.Attr_vals = make([]byte, 4)\n\t\t\t\tbinary.BigEndian.PutUint32(dirAttrs.Attr_vals,\n\t\t\t\t\tMODE4_WUSR|MODE4_RUSR|MODE4_XUSR|MODE4_WGRP|MODE4_RGRP|MODE4_XGRP)\n\t\t\t\tdirAttrs.Attrmask = Bitmap4{0, 1 << (FATTR4_MODE - 32)}\n\n\t\t\t\targs = append(args,\n\t\t\t\t\tNfs_argop4{\n\t\t\t\t\t\tArgop: OP_CREATE,\n\t\t\t\t\t\tU: &CREATE4args{\n\t\t\t\t\t\t\tObjtype: Createtype4{\n\t\t\t\t\t\t\t\tType: NF4DIR,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tObjname:     flClaim,\n\t\t\t\t\t\t\tCreateattrs: dirAttrs,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t_, err := c.runNfsTransaction(args, curPath)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Increment NFS sequence ID for all operations, even the ones that return\n// errors, except for a pre-defined list in https://tools.ietf.org/html/rfc3530#section-8.1.5\nfunc (c *NfsClient) incrementNfsSeq(err error) {\n\tif err == nil {\n\t\tc.nfsSeqId++\n\t\treturn\n\t}\n\n\tnfsErr, ok := err.(*NfsError)\n\tif !ok {\n\t\tc.nfsSeqId++\n\t\treturn\n\t}\n\n\tswitch Nfsstat4(nfsErr.ErrorCode) {\n\tcase\n\t\tNFS4ERR_STALE_CLIENTID, NFS4ERR_STALE_STATEID,\n\t\tNFS4ERR_BAD_STATEID, NFS4ERR_BAD_SEQID, NFS4ERR_BADXDR,\n\t\tNFS4ERR_RESOURCE, NFS4ERR_NOFILEHANDLE:\n\t\treturn\n\tdefault:\n\t\tc.nfsSeqId++\n\t}\n}\n\nfunc RemoveRecursive(nfs NfsInterface, path string) error {\n\tlist, err := nfs.GetFileList(path)\n\tif IsNfsError(err, ERROR_NOENT) {\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, fl := range list {\n\t\tcurPath := path + \"/\" + fl.Name\n\t\tif fl.IsDir {\n\t\t\terr = RemoveRecursive(nfs, curPath)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\terr = nfs.DeleteFile(curPath)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\terr = nfs.DeleteFile(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs4/repo/nfs4/nfs_err.go",
    "content": "package nfs4\n\ntype NfsError struct {\n\tPath        string\n\tErrorCode   NfsErrorCode\n\tErrorString string\n}\n\nfunc (n *NfsError) Error() string {\n\treturn n.ErrorString\n}\n\nfunc IsNfsError(err error, code NfsErrorCode) bool {\n\tne, ok := err.(*NfsError)\n\treturn ok && ne.ErrorCode == code\n}\n\n/*\n * Error status. See https://www.rfc-editor.org/rfc/rfc7530\n */\ntype NfsErrorCode int32\nconst (\n\t/* everything is okay      */\n\tOK NfsErrorCode = 0\n\t/* caller not privileged   */\n\tERROR_PERM NfsErrorCode = 1\n\t/* no such file/directory  */\n\tERROR_NOENT NfsErrorCode = 2\n\t/* hard I/O error          */\n\tERROR_IO NfsErrorCode = 5\n\t/* no such device          */\n\tERROR_NXIO NfsErrorCode = 6\n\t/* access denied           */\n\tERROR_ACCESS NfsErrorCode = 13\n\t/* file already exists     */\n\tERROR_EXIST NfsErrorCode = 17\n\t/* different filesystems   */\n\tERROR_XDEV NfsErrorCode = 18\n\t/* should be a directory   */\n\tERROR_NOTDIR NfsErrorCode = 20\n\t/* should not be directory */\n\tERROR_ISDIR NfsErrorCode = 21\n\t/* invalid argument        */\n\tERROR_INVAL NfsErrorCode = 22\n\t/* file exceeds server max */\n\tERROR_FBIG NfsErrorCode = 27\n\t/* no space on filesystem  */\n\tERROR_NOSPC NfsErrorCode = 28\n\t/* read-only filesystem    */\n\tERROR_ROFS NfsErrorCode = 30\n\t/* too many hard links     */\n\tERROR_MLINK NfsErrorCode = 31\n\t/* name exceeds server max */\n\tERROR_NAMETOOLONG NfsErrorCode = 63\n\t/* directory not empty     */\n\tERROR_NOTEMPTY NfsErrorCode = 66\n\t/* hard quota limit reached*/\n\tERROR_DQUOT NfsErrorCode = 69\n\t/* file no longer exists   */\n\tERROR_STALE NfsErrorCode = 70\n\t/* Illegal filehandle      */\n\tERROR_BADHANDLE NfsErrorCode = 10001\n\t/* READDIR cookie is stale */\n\tERROR_BAD_COOKIE NfsErrorCode = 10003\n\t/* operation not supported */\n\tERROR_NOTSUPP NfsErrorCode = 10004\n\t/* response limit exceeded */\n\tERROR_TOOSMALL NfsErrorCode = 10005\n\t/* undefined server error  */\n\tERROR_SERVERFAULT NfsErrorCode = 10006\n\t/* type invalid for CREATE */\n\tERROR_BADTYPE NfsErrorCode = 10007\n\t/* file \"busy\" - retry     */\n\tERROR_DELAY NfsErrorCode = 10008\n\t/* nverify says attrs same */\n\tERROR_SAME NfsErrorCode = 10009\n\t/* lock unavailable        */\n\tERROR_DENIED NfsErrorCode = 10010\n\t/* lock lease expired      */\n\tERROR_EXPIRED NfsErrorCode = 10011\n\t/* I/O failed due to lock  */\n\tERROR_LOCKED NfsErrorCode = 10012\n\t/* in grace period         */\n\tERROR_GRACE NfsErrorCode = 10013\n\t/* filehandle expired      */\n\tERROR_FHEXPIRED NfsErrorCode = 10014\n\t/* share reserve denied    */\n\tERROR_SHARE_DENIED NfsErrorCode = 10015\n\t/* wrong security flavor   */\n\tERROR_WRONGSEC NfsErrorCode = 10016\n\t/* clientid in use         */\n\tERROR_CLID_INUSE NfsErrorCode = 10017\n\t/* resource exhaustion     */\n\tERROR_RESOURCE NfsErrorCode = 10018\n\t/* filesystem relocated    */\n\tERROR_MOVED NfsErrorCode = 10019\n\t/* current FH is not set   */\n\tERROR_NOFILEHANDLE NfsErrorCode = 10020\n\t/* minor vers not supp */\n\tERROR_MINOR_VERS_MISMATCH NfsErrorCode = 10021\n\t/* server has rebooted     */\n\tERROR_STALE_CLIENTID NfsErrorCode = 10022\n\t/* server has rebooted     */\n\tERROR_STALE_STATEID NfsErrorCode = 10023\n\t/* state is out of sync    */\n\tERROR_OLD_STATEID NfsErrorCode = 10024\n\t/* incorrect stateid       */\n\tERROR_BAD_STATEID NfsErrorCode = 10025\n\t/* request is out of seq.  */\n\tERROR_BAD_SEQID NfsErrorCode = 10026\n\t/* verify - attrs not same */\n\tERROR_NOT_SAME NfsErrorCode = 10027\n\t/* lock range not supported*/\n\tERROR_LOCK_RANGE NfsErrorCode = 10028\n\t/* should be file/directory*/\n\tERROR_SYMLINK NfsErrorCode = 10029\n\t/* no saved filehandle     */\n\tERROR_RESTOREFH NfsErrorCode = 10030\n\t/* some filesystem moved   */\n\tERROR_LEASE_MOVED NfsErrorCode = 10031\n\t/* recommended attr not sup*/\n\tERROR_ATTRNOTSUPP NfsErrorCode = 10032\n\t/* reclaim outside of grace*/\n\tERROR_NO_GRACE NfsErrorCode = 10033\n\t/* reclaim error at server */\n\tERROR_RECLAIM_BAD NfsErrorCode = 10034\n\t/* conflict on reclaim    */\n\tERROR_RECLAIM_CONFLICT NfsErrorCode = 10035\n\t/* ZDR decode failed       */\n\tERROR_BADZDR NfsErrorCode = 10036\n\t/* file locks held at CLOSE*/\n\tERROR_LOCKS_HELD NfsErrorCode = 10037\n\t/* conflict in OPEN and I/O*/\n\tERROR_OPENMODE NfsErrorCode = 10038\n\t/* owner translation bad   */\n\tERROR_BADOWNER NfsErrorCode = 10039\n\t/* utf-8 char not supported*/\n\tERROR_BADCHAR NfsErrorCode = 10040\n\t/* name not supported      */\n\tERROR_BADNAME NfsErrorCode = 10041\n\t/* lock range not supported*/\n\tERROR_BAD_RANGE NfsErrorCode = 10042\n\t/* no atomic up/downgrade  */\n\tERROR_LOCK_NOTSUPP NfsErrorCode = 10043\n\t/* undefined operation     */\n\tERROR_OP_ILLEGAL NfsErrorCode = 10044\n\t/* file locking deadlock   */\n\tERROR_DEADLOCK NfsErrorCode = 10045\n\t/* open file blocks op.    */\n\tERROR_FILE_OPEN NfsErrorCode = 10046\n\t/* lockowner state revoked */\n\tERROR_ADMIN_REVOKED NfsErrorCode = 10047\n)\n"
  },
  {
    "path": "server/plugin/plg_backend_nfs4/repo/nfs4/supervised_conn.go",
    "content": "package nfs4\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n)\n\n// A wrapper for net.Conn that adds ability to cancel operations via a context.Context and\n// also supports deadlines.\ntype SupervisedConnection struct {\n\tdelegate net.Conn\n\tctx      context.Context\n\tdeadline time.Time\n\n\tmtx         sync.Mutex\n\tcloseSignal chan bool\n\tcloseErr    error\n\tisClosed    int32\n}\n\nfunc NewSupervisedConnection(delegate net.Conn,\n\tctx context.Context) (*SupervisedConnection, error) {\n\n\tres := &SupervisedConnection{\n\t\tdelegate:    delegate,\n\t\tctx:         ctx,\n\t\tcloseSignal: make(chan bool),\n\t}\n\tgo func() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tres.signalDone()\n\t\tcase <-res.closeSignal:\n\t\t}\n\t}()\n\n\tdeadline, ok := ctx.Deadline()\n\tif ok {\n\t\tres.deadline = deadline\n\t\terr := delegate.SetDeadline(deadline)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc (s *SupervisedConnection) signalDone() {\n\ts.mtx.Lock()\n\tdefer s.mtx.Unlock()\n\tif s.isClosed != 0 {\n\t\treturn\n\t}\n\t// Cancel pending operations\n\ts.closeErr = s.delegate.Close()\n\ts.isClosed = 1\n}\n\nfunc (s *SupervisedConnection) SetReadDeadline(t time.Time) error {\n\ts.mtx.Lock()\n\tdefer s.mtx.Unlock()\n\n\t// Don't allow deadline extensions if we're getting interrupted\n\tif !s.deadline.IsZero() && s.deadline.Before(t) {\n\t\treturn nil\n\t}\n\n\treturn s.delegate.SetReadDeadline(t)\n}\n\nfunc (s *SupervisedConnection) SetWriteDeadline(t time.Time) error {\n\ts.mtx.Lock()\n\tdefer s.mtx.Unlock()\n\n\t// Don't allow deadline extensions if we're getting interrupted\n\tif !s.deadline.IsZero() && s.deadline.Before(t) {\n\t\treturn nil\n\t}\n\n\treturn s.delegate.SetWriteDeadline(t)\n}\n\nfunc (s *SupervisedConnection) SetDeadline(t time.Time) error {\n\ts.mtx.Lock()\n\tdefer s.mtx.Unlock()\n\n\t// Don't allow deadline extensions if we're getting interrupted\n\tif !s.deadline.IsZero() && s.deadline.Before(t) {\n\t\treturn nil\n\t}\n\n\treturn s.delegate.SetDeadline(t)\n}\n\nfunc (s *SupervisedConnection) Close() error {\n\ts.mtx.Lock()\n\tdefer s.mtx.Unlock()\n\tif s.isClosed != 0 {\n\t\treturn s.closeErr\n\t}\n\n\tclose(s.closeSignal)\n\ts.isClosed = 1\n\n\ts.closeErr = s.delegate.Close()\n\treturn s.closeErr\n}\n\nfunc (s *SupervisedConnection) Read(b []byte) (n int, err error) {\n\t// s.isClosed can only go from 0 to 1, so there's no need to do\n\t// synchronization here.\n\tif s.isClosed != 0 {\n\t\treturn 0, io.EOF\n\t}\n\treturn s.delegate.Read(b)\n}\n\nfunc (s *SupervisedConnection) Write(b []byte) (n int, err error) {\n\t// s.isClosed can only go from 0 to 1, so there's no need to do\n\t// synchronization here.\n\tif s.isClosed != 0 {\n\t\treturn 0, io.EOF\n\t}\n\treturn s.delegate.Write(b)\n}\n\nfunc (s *SupervisedConnection) LocalAddr() net.Addr {\n\treturn s.delegate.LocalAddr()\n}\n\nfunc (s *SupervisedConnection) RemoteAddr() net.Addr {\n\treturn s.delegate.RemoteAddr()\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_nop/index.go",
    "content": "package plg_backend_nop\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc init() {\n\tBackend.Register(\"blackhole\", BlackHole{})\n}\n\ntype LargeFile struct {\n\tCounter int\n}\n\nfunc (this *LargeFile) Read(p []byte) (n int, err error) {\n\tif this.Counter <= 0 {\n\t\treturn 0, io.EOF\n\t}\n\tthis.Counter = this.Counter - len(p)\n\tlenp := len(p)\n\tif lenp > 0 {\n\t\tp[0] = '_'\n\t}\n\tfor i := 0; i < lenp; i += 100 {\n\t\tp[i] = '_'\n\t}\n\treturn lenp, nil\n}\n\nfunc (this LargeFile) Close() error {\n\treturn nil\n}\n\ntype BlackHole struct{}\n\nfunc (this BlackHole) Init(params map[string]string, app *App) (IBackend, error) {\n\tLog.Debug(\"plg_backend_nop::init params[%s]\", params)\n\treturn &BlackHole{}, nil\n}\n\nfunc (this BlackHole) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"blackhole\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this BlackHole) Ls(path string) ([]os.FileInfo, error) {\n\tfiles := make([]os.FileInfo, 0)\n\tfiles = append(\n\t\tfiles,\n\t\tFile{FName: \"1M.bin\", FType: \"file\", FSize: 1024 * 1024},\n\t\tFile{FName: \"10M.bin\", FType: \"file\", FSize: 1024 * 1024 * 10},\n\t\tFile{FName: \"100M.bin\", FType: \"file\", FSize: 1024 * 1024 * 100},\n\t\tFile{FName: \"1G.bin\", FType: \"file\", FSize: 1024 * 1024 * 1024},\n\t\tFile{FName: \"10G.bin\", FType: \"file\", FSize: 1024 * 1024 * 1024 * 1024},\n\t\tFile{FName: \"100G.bin\", FType: \"file\", FSize: 1024 * 1024 * 1024 * 1024 * 1024},\n\t)\n\treturn files, nil\n}\n\nfunc (this BlackHole) Stat(path string) (os.FileInfo, error) {\n\treturn nil, ErrNotImplemented\n}\n\nfunc (this BlackHole) Cat(path string) (io.ReadCloser, error) {\n\tpath = strings.TrimPrefix(path, \"/\")\n\tif strings.HasSuffix(path, \".bin\") == false {\n\t\treturn nil, ErrNotImplemented\n\t}\n\tpath = strings.TrimSuffix(path, \".bin\")\n\torder := 1\n\tif strings.HasSuffix(path, \"K\") {\n\t\tpath = strings.TrimSuffix(path, \"K\")\n\t\torder = order * 1024\n\t} else if strings.HasSuffix(path, \"M\") {\n\t\tpath = strings.TrimSuffix(path, \"M\")\n\t\torder = order * 1024 * 1024\n\t} else if strings.HasSuffix(path, \"G\") {\n\t\tpath = strings.TrimSuffix(path, \"G\")\n\t\torder = order * 1024 * 1024 * 1024\n\t}\n\ti, err := strconv.Atoi(path)\n\tif err != nil {\n\t\treturn nil, ErrNotImplemented\n\t}\n\treturn &LargeFile{i * order}, nil\n}\n\nfunc (this BlackHole) Mkdir(path string) error {\n\treturn nil\n}\n\nfunc (this BlackHole) Rm(path string) error {\n\treturn ErrNotImplemented\n}\n\nfunc (this BlackHole) Mv(from, to string) error {\n\treturn ErrNotImplemented\n}\n\nfunc (this BlackHole) Save(path string, content io.Reader) error {\n\tb := make([]byte, 32*1024*1024) // 32MB\n\tfor {\n\t\t_, err := content.Read(b)\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (this BlackHole) Touch(path string) error {\n\treturn nil\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_perkeep/index.go",
    "content": "package plg_backend_perkeep\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tBackend.Register(\"perkeep\", &Perkeep{})\n}\n\ntype Perkeep struct {\n\tserverURL string\n}\n\nfunc (this Perkeep) Init(params map[string]string, app *App) (IBackend, error) {\n\turl := params[\"url\"]\n\tif url == \"\" {\n\t\turl = \"http://localhost:3179/\"\n\t}\n\tif !strings.HasSuffix(url, \"/\") {\n\t\turl += \"/\"\n\t}\n\treturn &Perkeep{\n\t\tserverURL: url,\n\t}, nil\n}\n\nfunc (this Perkeep) Meta(path string) Metadata {\n\treturn Metadata{\n\t\tCanCreateFile:      NewBool(false), // TODO\n\t\tCanCreateDirectory: NewBool(false), // TODO\n\t\tCanRename:          NewBool(false), // TODO\n\t\tCanMove:            NewBool(false), // TODO\n\t\tCanUpload:          NewBool(false), // TODO: see http://localhost:3179/bs-and-maybe-also-index/camli/upload\n\n\t\tCanDelete: NewBool(false), // TODO\n\t}\n}\n\nfunc (this Perkeep) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"perkeep\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"url\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"eg: http://localhost:3179\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Perkeep) Ls(path string) ([]os.FileInfo, error) {\n\tvar files []os.FileInfo\n\n\tif path == \"/\" {\n\t\tresponse, err := this.query(map[string]interface{}{ // curl 'http://localhost:3179/my-search/camli/search/query' -d '{\"sort\":\"-created\",\"constraint\":{\"permanode\":{\"attr\": \"camliRoot\",\"valueMatches\": {}}},\"describe\":{},\"limit\":-1}'\n\t\t\t\"sort\": \"-created\",\n\t\t\t\"constraint\": map[string]interface{}{\n\t\t\t\t\"permanode\": map[string]interface{}{\n\t\t\t\t\t\"attr\":         \"camliRoot\",\n\t\t\t\t\t\"valueMatches\": map[string]interface{}{},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"describe\": map[string]interface{}{},\n\t\t\t\"limit\":    1,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, blob := range response.Blobs {\n\t\t\tif meta, ok := response.Description.Meta[blob.Blob]; ok {\n\t\t\t\tif rootNames, ok := meta.Permanode.Attr[\"camliRoot\"]; ok && len(rootNames) > 0 {\n\t\t\t\t\tfiles = append(files, File{\n\t\t\t\t\t\tFName: rootNames[0],\n\t\t\t\t\t\tFType: \"directory\",\n\t\t\t\t\t\tFTime: meta.Permanode.ModTime.Unix(),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn files, nil\n\t}\n\n\tref, err := this.getRef(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresponse, err := this.query(map[string]interface{}{ // curl 'http://localhost:3179/my-search/camli/search/query' -d '{\"sort\":\"-created\",\"constraint\":{\"permanode\":{\"relation\":{\"relation\":\"parent\",\"any\":{\"blobRefPrefix\":\"sha224-ff8f64ab406dc5aec7a35bf182dee79ea20d41bfffc2311fcb4acd9f\"}}}},\"describe\":{\"rules\":[{\"attrs\": [\"camliContent\"]}]},\"limit\":50}'\n\t\t\"sort\": \"-created\",\n\t\t\"constraint\": map[string]interface{}{\n\t\t\t\"permanode\": map[string]interface{}{\n\t\t\t\t\"relation\": map[string]interface{}{\n\t\t\t\t\t\"relation\": \"parent\",\n\t\t\t\t\t\"any\": map[string]interface{}{\n\t\t\t\t\t\t\"blobRefPrefix\": ref,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"describe\": map[string]interface{}{\n\t\t\t\"rules\": []map[string]interface{}{\n\t\t\t\t{\n\t\t\t\t\t\"attrs\": []string{\"camliContent\", \"title\", \"camliNodeType\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"limit\": 50,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, blob := range response.Blobs {\n\t\tif meta, ok := response.Description.Meta[blob.Blob]; ok {\n\t\t\tvar (\n\t\t\t\tfileName string\n\t\t\t\tfileType string\n\t\t\t\tfileSize int64\n\t\t\t\tfileTime int64 = -1\n\t\t\t)\n\t\t\tif nodeType, ok := meta.Permanode.Attr[\"camliNodeType\"]; ok && len(nodeType) > 0 && nodeType[0] == \"directory\" {\n\t\t\t\tif titles, ok := meta.Permanode.Attr[\"title\"]; ok && len(titles) > 0 {\n\t\t\t\t\tfileType = \"directory\"\n\t\t\t\t\tfileName = titles[0]\n\t\t\t\t\tfileTime = meta.Permanode.ModTime.Unix()\n\t\t\t\t}\n\t\t\t} else if contentRefs, hasContent := meta.Permanode.Attr[\"camliContent\"]; hasContent && len(contentRefs) > 0 {\n\t\t\t\tcontentRef := contentRefs[0]\n\t\t\t\tif contentMeta, ok := response.Description.Meta[contentRef]; ok && contentMeta.File != nil {\n\t\t\t\t\tfileType = \"file\"\n\t\t\t\t\tfileName = contentMeta.File.FileName\n\t\t\t\t\tfileTime = contentMeta.File.Time.Unix()\n\t\t\t\t\tfileSize = contentMeta.File.Size\n\t\t\t\t}\n\t\t\t}\n\t\t\tif fileName != \"\" && fileType != \"\" {\n\t\t\t\tfiles = append(files, File{\n\t\t\t\t\tFName: fileName,\n\t\t\t\t\tFType: fileType,\n\t\t\t\t\tFSize: fileSize,\n\t\t\t\t\tFTime: fileTime,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\treturn files, nil\n}\n\nfunc (this Perkeep) Stat(path string) (os.FileInfo, error) {\n\treturn nil, ErrNotImplemented\n}\n\nfunc (this Perkeep) Cat(path string) (io.ReadCloser, error) {\n\tref, err := this.getRef(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresponse, err := this.describe(ref)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcontentRefs, hasContent := response.Meta[ref].Permanode.Attr[\"camliContent\"]\n\tif !hasContent || len(contentRefs) == 0 {\n\t\treturn nil, NewError(\"No content\", 400)\n\t}\n\tresp, err := http.Get(this.serverURL + \"ui/download/\" + contentRefs[0])\n\tif err != nil {\n\t\treturn nil, NewError(\"Failed to fetch file: \"+err.Error(), 500)\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\tresp.Body.Close()\n\t\treturn nil, NewError(\"Failed to fetch file\", resp.StatusCode)\n\t}\n\treturn resp.Body, nil\n}\n\nfunc (this Perkeep) Mkdir(path string) error {\n\treturn ErrNotImplemented\n}\n\nfunc (this Perkeep) Rm(path string) error {\n\treturn ErrNotImplemented\n}\n\nfunc (this Perkeep) Mv(from, to string) error {\n\treturn ErrNotImplemented\n}\n\nfunc (this Perkeep) Save(path string, content io.Reader) error {\n\treturn ErrNotImplemented\n}\n\nfunc (this Perkeep) Touch(path string) error {\n\treturn ErrNotImplemented\n}\n\nfunc (this *Perkeep) query(searchRequest any) (*SearchResponse, error) {\n\tqueryJSON, err := json.Marshal(searchRequest)\n\tif err != nil {\n\t\treturn nil, NewError(\"Failed to marshal search query: \"+err.Error(), 500)\n\t}\n\treq, err := http.NewRequest(\n\t\t\"POST\",\n\t\tthis.serverURL+\"my-search/camli/search/query\",\n\t\tbytes.NewBuffer(queryJSON),\n\t)\n\tif err != nil {\n\t\treturn nil, NewError(\"Failed to create request: \"+err.Error(), 500)\n\t}\n\tresp, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn nil, NewError(\"Failed to query perkeep: \"+err.Error(), 500)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(resp.Body)\n\t\treturn nil, NewError(fmt.Sprintf(\"Perkeep API error (%d): %s\", resp.StatusCode, string(body)), 500)\n\t}\n\tvar result SearchResponse\n\terr = json.NewDecoder(resp.Body).Decode(&result)\n\treturn &result, err\n}\n\nfunc (this *Perkeep) describe(blobRef string) (*DescribeResponse, error) {\n\tdescribeJSON, err := json.Marshal(map[string]interface{}{\n\t\t\"blobRef\": blobRef,\n\t})\n\tif err != nil {\n\t\treturn nil, NewError(\"Failed to marshal describe request: \"+err.Error(), 500)\n\t}\n\treq, err := http.NewRequest(\n\t\t\"POST\",\n\t\tthis.serverURL+\"my-search/camli/search/describe\",\n\t\tbytes.NewBuffer(describeJSON),\n\t)\n\tif err != nil {\n\t\treturn nil, NewError(\"Failed to create request: \"+err.Error(), 500)\n\t}\n\tresp, err := HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn nil, NewError(\"Failed to describe perkeep: \"+err.Error(), 500)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(resp.Body)\n\t\treturn nil, NewError(fmt.Sprintf(\"Perkeep API error (%d): %s\", resp.StatusCode, string(body)), 500)\n\t}\n\tvar result DescribeResponse\n\terr = json.NewDecoder(resp.Body).Decode(&result)\n\treturn &result, err\n}\n\nfunc (this *Perkeep) getRef(path string) (string, error) {\n\tpath = strings.Trim(path, \"/\")\n\tif path == \"\" {\n\t\treturn \"\", NewError(\"Empty path\", 400)\n\t}\n\tpathChunks := strings.Split(path, \"/\")\n\tresponse, err := this.query(map[string]interface{}{\n\t\t\"constraint\": map[string]interface{}{\n\t\t\t\"permanode\": map[string]interface{}{\n\t\t\t\t\"attr\":  \"camliRoot\",\n\t\t\t\t\"value\": pathChunks[0],\n\t\t\t},\n\t\t},\n\t\t\"describe\": map[string]interface{}{},\n\t\t\"limit\":    1,\n\t})\n\tif err != nil {\n\t\treturn \"\", err\n\t} else if len(response.Blobs) == 0 {\n\t\treturn \"\", NewError(\"Root folder not found: \"+pathChunks[0], 404)\n\t}\n\tcurrentRef := response.Blobs[0].Blob\n\tfor i := 1; i < len(pathChunks); i++ {\n\t\tchildResponse, err := this.query(map[string]interface{}{\n\t\t\t\"constraint\": map[string]interface{}{\n\t\t\t\t\"permanode\": map[string]interface{}{\n\t\t\t\t\t\"relation\": map[string]interface{}{\n\t\t\t\t\t\t\"relation\": \"parent\",\n\t\t\t\t\t\t\"any\": map[string]interface{}{\n\t\t\t\t\t\t\t\"blobRefPrefix\": currentRef,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"describe\": map[string]interface{}{\n\t\t\t\t\"rules\": []map[string]interface{}{\n\t\t\t\t\t{\n\t\t\t\t\t\t\"attrs\": []string{\"camliContent\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"limit\": -1,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tfound := false\n\t\tfor _, blob := range childResponse.Blobs {\n\t\t\tif meta, ok := childResponse.Description.Meta[blob.Blob]; ok {\n\t\t\t\tif titles, ok := meta.Permanode.Attr[\"title\"]; ok && len(titles) > 0 {\n\t\t\t\t\tif titles[0] == pathChunks[i] {\n\t\t\t\t\t\tcurrentRef = blob.Blob\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t} else if contentRefs, hasContent := meta.Permanode.Attr[\"camliContent\"]; hasContent && len(contentRefs) > 0 {\n\t\t\t\t\tif contentMeta, ok := childResponse.Description.Meta[contentRefs[0]]; ok && contentMeta.File != nil {\n\t\t\t\t\t\tif contentMeta.File.FileName == pathChunks[i] {\n\t\t\t\t\t\t\tcurrentRef = blob.Blob\n\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\treturn \"\", NewError(\"Path element not found: \"+pathChunks[i], 404)\n\t\t}\n\t}\n\treturn currentRef, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_perkeep/types.go",
    "content": "package plg_backend_perkeep\n\nimport (\n\t\"time\"\n)\n\ntype SearchResponse struct {\n\tBlobs []struct {\n\t\tBlob string `json:\"blob\"`\n\t} `json:\"blobs\"`\n\tDescription struct {\n\t\tMeta map[string]struct {\n\t\t\tPermanode struct {\n\t\t\t\tAttr    map[string][]string `json:\"attr\"`\n\t\t\t\tModTime time.Time\n\t\t\t} `json:\"permanode\"`\n\t\t\tFile *struct {\n\t\t\t\tFileName string    `json:\"fileName\"`\n\t\t\t\tSize     int64     `json:\"size\"`\n\t\t\t\tTime     time.Time `json:\"time\"`\n\t\t\t\tWholeRef string    `json:\"wholeRef\"`\n\t\t\t} `json:\"file\"`\n\t\t\tCamliType string `json:\"camliType\"`\n\t\t} `json:\"meta\"`\n\t} `json:\"description\"`\n}\n\ntype DescribeResponse struct {\n\tMeta map[string]struct {\n\t\tPermanode struct {\n\t\t\tAttr    map[string][]string `json:\"attr\"`\n\t\t\tModTime time.Time\n\t\t\tSize    int64\n\t\t} `json:\"permanode\"`\n\t\tCamliType string `json:\"camliType\"`\n\t} `json:\"meta\"`\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_psql/index.go",
    "content": "package plg_backend_psql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t_ \"github.com/lib/pq\"\n)\n\ntype PSQL struct {\n\tdb  *sql.DB\n\tctx context.Context\n}\n\nfunc init() {\n\tBackend.Register(\"psql\", PSQL{})\n}\n\nfunc (this PSQL) Init(params map[string]string, app *App) (IBackend, error) {\n\thost := params[\"host\"]\n\tport := withDefault(params[\"port\"], \"5432\")\n\tuser := params[\"user\"]\n\tpassword := params[\"password\"]\n\tdbname := withDefault(params[\"dbname\"], \"postgres\")\n\tsslmode := withDefault(params[\"sslmode\"], \"disable\")\n\n\tif host == \"\" || user == \"\" || password == \"\" {\n\t\treturn nil, ErrNotValid\n\t}\n\tdb, err := sql.Open(\n\t\t\"postgres\",\n\t\tfmt.Sprintf(\n\t\t\t\"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s\",\n\t\t\thost, port, user, password, dbname, sslmode,\n\t\t),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t} else if err := db.Ping(); err != nil {\n\t\tLog.Debug(\"plg_backend_psql::init err=%s\", err.Error())\n\t\treturn nil, ErrNotValid\n\t}\n\tbackend := &PSQL{\n\t\tdb:  db,\n\t\tctx: app.Context,\n\t}\n\treturn backend, nil\n}\n\nfunc withDefault(val string, def string) string {\n\tif val == \"\" {\n\t\treturn def\n\t}\n\treturn val\n}\n\nfunc (this PSQL) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"psql\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"host\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Host\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"port\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"Port\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"user\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"User\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"password\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"Password\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"dbname\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"DB Name\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"sslmode\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"SSL Mode\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this PSQL) Touch(path string) error {\n\tdefer this.Close()\n\tif !strings.HasSuffix(path, \".form\") {\n\t\treturn NewError(\"Create a form file instead. eg: xxxx.form\", 403)\n\t}\n\treturn nil\n}\n\nfunc (this PSQL) Mkdir(path string) error {\n\tdefer this.Close()\n\treturn ErrNotValid\n}\n\nfunc (this PSQL) Mv(from string, to string) error {\n\tdefer this.Close()\n\treturn ErrNotValid\n}\n\nfunc (this PSQL) Meta(path string) Metadata {\n\tlocation, _ := getPath(path)\n\treturn Metadata{\n\t\tCanCreateDirectory: NewBool(false),\n\t\tCanCreateFile: func(l LocationRow) *bool {\n\t\t\tif l.table == \"\" {\n\t\t\t\treturn NewBool(false)\n\t\t\t}\n\t\t\treturn NewBool(true)\n\t\t}(location),\n\t\tCanRename: NewBool(false),\n\t\tCanDelete: func(l LocationRow) *bool {\n\t\t\tif l.table == \"\" {\n\t\t\t\treturn NewBool(false)\n\t\t\t}\n\t\t\treturn NewBool(true)\n\t\t}(location),\n\t\tCanMove: NewBool(false),\n\t\tCanUpload: func(l LocationRow) *bool {\n\t\t\tif l.row == \"\" {\n\t\t\t\treturn NewBool(false)\n\t\t\t}\n\t\t\treturn NewBool(true)\n\t\t}(location),\n\t\tRefreshOnCreate: NewBool(true),\n\t\tHideExtension:   NewBool(true),\n\t}\n}\n\nfunc (this PSQL) Close() error {\n\tthis.db.Close()\n\treturn nil\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_psql/index_cat.go",
    "content": "package plg_backend_psql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"slices\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc (this PSQL) Cat(path string) (io.ReadCloser, error) {\n\tdefer this.Close()\n\tl, err := getPath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcolumns, columnName, err := processTable(this.ctx, this.db, l.table)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trows, err := this.db.QueryContext(this.ctx, `\n\t\tSELECT *\n\t\t\tFROM \"`+l.table+`\"\n\t\t\tWHERE \"`+columnName+`\"=$1\n    `, l.row)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\tc, err := rows.Columns()\n\tif err != nil {\n\t\treturn nil, err\n\t} else if len(columns) != len(c) {\n\t\tLog.Error(\"plg_backend_psql::index_cat columns is not of the expected size columns[%d]=%v c[%d]=%v\", len(columns), columns, len(c), c)\n\t\treturn nil, ErrNotValid\n\t}\n\ti := 0\n\tcol := make([]interface{}, len(c))\n\tfor rows.Next() {\n\t\tif i != 0 {\n\t\t\treturn nil, ErrNotValid\n\t\t}\n\t\tpcol := make([]any, len(c))\n\t\tfor i, _ := range pcol {\n\t\t\tpcol[i] = &col[i]\n\t\t}\n\t\tif err := rows.Scan(pcol...); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tforms := make([]FormElement, len(c))\n\tfor i, _ := range columns {\n\t\tforms[i] = createFormElement(col[i], columns[i])\n\t\tif columnComment := _findCommentColumn(this.ctx, this.db, l.table, columns[i].Name); columnComment != \"\" {\n\t\t\tforms[i].Description = columnComment\n\t\t}\n\t\tif slices.Contains(columns[i].Constraint, \"PRIMARY KEY\") && forms[i].Value != nil {\n\t\t\tforms[i].ReadOnly = true\n\t\t} else if slices.Contains(columns[i].Constraint, \"FOREIGN KEY\") {\n\t\t\tif link, err := _findRelation(this.ctx, this.db, columns[i]); err == nil {\n\t\t\t\tif len(link.values) > 0 {\n\t\t\t\t\tforms[i].Type = \"select\"\n\t\t\t\t\tforms[i].Opts = link.values\n\t\t\t\t}\n\t\t\t\tif forms[i].Description == \"\" {\n\t\t\t\t\tforms[i].Description = _createDescription(columns[i], link)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if values, err := _findEnumValues(this.ctx, this.db, columns[i]); err == nil && len(values) > 0 {\n\t\t\tforms[i].Type = \"select\"\n\t\t\tforms[i].Opts = values\n\t\t}\n\t}\n\tif comment := _findCommentTable(this.ctx, this.db, l.table); comment != \"\" {\n\t\tforms = append([]FormElement{\n\t\t\t{\n\t\t\t\tName:        \"banner\",\n\t\t\t\tType:        \"hidden\",\n\t\t\t\tDescription: comment,\n\t\t\t},\n\t\t}, forms...)\n\t}\n\tb, err := Form{Elmnts: forms}.MarshalJSON()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewReadCloserFromBytes(b), nil\n}\n\nfunc (this PSQL) Stat(path string) (os.FileInfo, error) {\n\treturn nil, ErrNotImplemented\n}\n\nfunc _createDescription(el Column, link LocationColumn) string {\n\tif slices.Contains(el.Constraint, \"FOREIGN KEY\") {\n\t\treturn fmt.Sprintf(\"points to [<%s> → <%s>](/files/%s/)\", link.table, link.column, link.table)\n\t}\n\treturn \"\"\n}\n\nfunc _findCommentTable(ctx context.Context, db *sql.DB, tableName string) string {\n\tvar comment string\n\tif err := db.QueryRowContext(ctx, `\n\t\tSELECT obj_description(c.oid)\n\t\t\t\tFROM pg_class c\n\t\t\t\tWHERE c.relname = $1 AND c.relkind = 'r'\n\t`, tableName).Scan(&comment); err != nil {\n\t\treturn \"\"\n\t}\n\treturn comment\n}\n\nfunc _findCommentColumn(ctx context.Context, db *sql.DB, tableName, columnName string) string {\n\tvar comment string\n\tif err := db.QueryRowContext(ctx, `\n\t\tSELECT col_description(c.oid, a.attnum)\n\t\t\t\tFROM pg_class c\n\t\t\t\tJOIN pg_attribute a ON a.attrelid = c.oid\n\t\t\t\tWHERE c.relname = $1 AND a.attname = $2 AND c.relkind = 'r'\n\t`, tableName, columnName).Scan(&comment); err != nil {\n\t\treturn \"\"\n\t}\n\treturn comment\n}\n\nfunc _findRelation(ctx context.Context, db *sql.DB, el Column) (LocationColumn, error) {\n\tl := LocationColumn{}\n\trows, err := db.QueryContext(ctx, `\n\t\tSELECT ccu.table_name, ccu.column_name\n\t\t\tFROM information_schema.table_constraints AS tc\n\t\t\tJOIN information_schema.key_column_usage AS kcu USING (constraint_name)\n\t\t\tJOIN information_schema.constraint_column_usage AS ccu USING (constraint_name)\n\t\t\tWHERE tc.constraint_type = 'FOREIGN KEY'\n\t\t\t\tAND tc.table_name = $1\n\t\t\t\tAND kcu.column_name = $2\n    `, el.Table, el.Name)\n\tif err != nil {\n\t\treturn l, err\n\t}\n\tdefer rows.Close()\n\tfor rows.Next() {\n\t\tif err := rows.Scan(&l.table, &l.column); err != nil {\n\t\t\treturn l, err\n\t\t}\n\t}\n\tvalueRows, err := db.QueryContext(ctx, fmt.Sprintf(\n\t\t`SELECT DISTINCT \"%s\" FROM \"%s\" ORDER BY \"%s\" LIMIT 5000`,\n\t\tl.column, l.table, l.column,\n\t))\n\tif err != nil {\n\t\treturn l, err\n\t}\n\tdefer valueRows.Close()\n\tl.values = []string{}\n\tfor valueRows.Next() {\n\t\tvar value string\n\t\tif err := valueRows.Scan(&value); err != nil {\n\t\t\treturn l, err\n\t\t}\n\t\tl.values = append(l.values, value)\n\t}\n\treturn l, nil\n}\n\nfunc _findEnumValues(ctx context.Context, db *sql.DB, el Column) ([]string, error) {\n\tvar count int\n\tif err := db.QueryRowContext(ctx, `\n\t\tSELECT COUNT(*)\n\t\t\tFROM pg_type\n\t\t\tWHERE typname = $1 AND typtype = 'e'\n\t`, el.Type).Scan(&count); err != nil || count == 0 {\n\t\treturn nil, err\n\t}\n\trows, err := db.QueryContext(ctx, `SELECT unnest(enum_range(NULL::`+el.Type+`))`)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvalues := []string{}\n\tfor rows.Next() {\n\t\tvar value string\n\t\tif err := rows.Scan(&value); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvalues = append(values, value)\n\t}\n\trows.Close()\n\treturn values, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_psql/index_ls.go",
    "content": "package plg_backend_psql\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc (this PSQL) Ls(path string) ([]os.FileInfo, error) {\n\tdefer this.Close()\n\tl, err := getPath(path)\n\tif err != nil {\n\t\tLog.Debug(\"pl_backend_psql::ls method=getPath err=%s\", err.Error())\n\t\treturn nil, err\n\t}\n\tif l.table == \"\" {\n\t\trows, err := this.db.QueryContext(this.ctx, `\n            SELECT table_name FROM information_schema.tables\n                WHERE table_schema = 'public'\n                AND table_type = 'BASE TABLE'\n        `)\n\t\tif err != nil {\n\t\t\tLog.Debug(\"plg_backend_psql::ls method=query err=%s\", err.Error())\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer rows.Close()\n\t\tout := []os.FileInfo{}\n\t\tfor rows.Next() {\n\t\t\tvar name string\n\t\t\tif err := rows.Scan(&name); err != nil {\n\t\t\t\tLog.Debug(\"plg_backend_psql::ls method=scan err=%s\", err.Error())\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tout = append(out, File{\n\t\t\t\tFName: name,\n\t\t\t\tFType: \"directory\",\n\t\t\t})\n\t\t}\n\t\treturn out, nil\n\t} else if l.row == \"\" {\n\t\tcolumns, key, err := processTable(this.ctx, this.db, l.table)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tquery := `SELECT \"` + key + `\", NULL FROM \"` + l.table + `\" LIMIT 500000`\n\t\tfor _, c := range columns {\n\t\t\tif c.Type == \"timestamptz\" {\n\t\t\t\tquery = `SELECT \"` + key + `\", \"` + c.Name + `\" FROM \"` + l.table + `\" LIMIT 500000`\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\trows, err := this.db.QueryContext(this.ctx, query)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer rows.Close()\n\t\tout := []os.FileInfo{}\n\t\tfor rows.Next() {\n\t\t\tvar name string\n\t\t\tvar t *time.Time\n\t\t\tif err = rows.Scan(&name, &t); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tout = append(out, File{\n\t\t\t\tFName: name + \".form\",\n\t\t\t\tFType: \"file\",\n\t\t\t\tFTime: func() int64 {\n\t\t\t\t\tif t == nil {\n\t\t\t\t\t\treturn 0\n\t\t\t\t\t}\n\t\t\t\t\treturn t.Unix()\n\t\t\t\t}(),\n\t\t\t\tFSize: -1,\n\t\t\t})\n\t\t}\n\t\treturn out, nil\n\t}\n\treturn []os.FileInfo{}, ErrNotValid\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_psql/index_rm.go",
    "content": "package plg_backend_psql\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc (this PSQL) Rm(path string) error {\n\tdefer this.Close()\n\tl, err := getPath(path)\n\tif err != nil {\n\t\treturn err\n\t} else if l.table == \"\" {\n\t\treturn ErrNotFound\n\t}\n\t_, key, err := processTable(this.ctx, this.db, l.table)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = this.db.ExecContext(\n\t\tthis.ctx,\n\t\t`DELETE FROM \"`+l.table+`\" WHERE \"`+key+`\" = $1`,\n\t\tl.row,\n\t)\n\treturn err\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_psql/index_save.go",
    "content": "package plg_backend_psql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"slices\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc (this PSQL) Save(path string, file io.Reader) error {\n\tdefer this.Close()\n\tl, err := getPath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcolumns, key, err := processTable(this.ctx, this.db, l.table)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf := map[string]FormElement{}\n\tif err := json.NewDecoder(file).Decode(&f); err != nil {\n\t\treturn err\n\t}\n\ttx, err := this.db.BeginTx(this.ctx, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer tx.Rollback()\n\n\trows, err := tx.QueryContext(this.ctx, `SELECT * FROM \"`+l.table+`\" WHERE \"`+key+`\" = $1`, l.row)\n\tif err != nil {\n\t\treturn err\n\t}\n\ti := 0\n\tdbvals := make([]any, len(columns))\n\tfor rows.Next() {\n\t\tcurrentPtrs := make([]any, len(columns))\n\t\tfor i := range dbvals {\n\t\t\tcurrentPtrs[i] = &dbvals[i]\n\t\t}\n\t\tif serr := rows.Scan(currentPtrs...); serr != nil {\n\t\t\trows.Close()\n\t\t\terr = serr\n\t\t\tbreak\n\t\t} else if i >= 1 {\n\t\t\terr = ErrNotValid\n\t\t\tbreak\n\t\t}\n\t\ti += 1\n\t}\n\trows.Close()\n\tif i == 0 {\n\t\terr = _createRow(tx, this.ctx, l.table, columns, f)\n\t}\n\tif err == nil && i == 1 {\n\t\terr = _updateRow(tx, this.ctx, l.table, columns, f, key, l.row, dbvals)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn tx.Commit()\n}\n\nfunc _createRow(tx *sql.Tx, ctx context.Context, table string, columns []Column, f map[string]FormElement) error {\n\tcolNames := []string{}\n\tplaceholders := []string{}\n\tvalues := []interface{}{}\n\tparamIndex := 1\n\tfor _, col := range columns {\n\t\tif formEl, exists := f[col.Name]; exists {\n\t\t\tif slices.Contains(col.Constraint, \"PRIMARY KEY\") && col.Default {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcolNames = append(colNames, `\"`+col.Name+`\"`)\n\t\t\tplaceholders = append(placeholders, fmt.Sprintf(\"$%d\", paramIndex))\n\t\t\tvalues = append(values, formEl.Value)\n\t\t\tparamIndex++\n\t\t}\n\t}\n\tif len(colNames) == 0 {\n\t\treturn ErrNotValid\n\t}\n\t_, err := tx.ExecContext(\n\t\tctx,\n\t\t`INSERT INTO \"`+table+`\" (`+strings.Join(colNames, \", \")+`) VALUES (`+strings.Join(placeholders, \", \")+`)`,\n\t\tvalues...,\n\t)\n\treturn err\n}\n\nfunc _updateRow(tx *sql.Tx, ctx context.Context, table string, columns []Column, f map[string]FormElement, keyName string, keyValue any, dbvals []any) error {\n\tfor i, col := range columns {\n\t\tdbval := convertFromDB(dbvals[i])\n\t\tformval, ok := f[col.Name]\n\t\tif !ok || formval.Value == dbval {\n\t\t\tcontinue\n\t\t}\n\t\tif _, err := tx.ExecContext(\n\t\t\tctx,\n\t\t\t`UPDATE \"`+table+`\" SET \"`+col.Name+`\" = $1 WHERE \"`+keyName+`\" = $2`,\n\t\t\tformval.Value, keyValue,\n\t\t); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_psql/types.go",
    "content": "package plg_backend_psql\n\ntype Column struct {\n\tTable      string\n\tName       string\n\tType       string\n\tNullable   bool\n\tDefault    bool\n\tConstraint []string\n}\n\ntype LocationRow struct {\n\ttable string\n\trow   string\n}\n\ntype LocationColumn struct {\n\ttable  string\n\tcolumn string\n\tvalues []string\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_psql/utils.go",
    "content": "package plg_backend_psql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc getPath(path string) (LocationRow, error) {\n\tl := LocationRow{}\n\tfor i, chunk := range strings.Split(path, \"/\") {\n\t\tif i == 0 {\n\t\t\tif chunk != \"\" {\n\t\t\t\treturn l, ErrNotValid\n\t\t\t}\n\t\t} else if i == 1 {\n\t\t\tif strings.Contains(chunk, `\"`) {\n\t\t\t\treturn l, ErrNotValid\n\t\t\t}\n\t\t\tl.table = chunk\n\t\t} else if i == 2 {\n\t\t\tl.row = strings.TrimSuffix(chunk, \".form\")\n\t\t} else {\n\t\t\treturn l, ErrNotValid\n\t\t}\n\t}\n\treturn l, nil\n}\n\nfunc processTable(ctx context.Context, db *sql.DB, table string) ([]Column, string, error) {\n\tcolumns, err := _getColumns(ctx, db, table)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tkey := \"\"\n\tscore := 0\n\tfor _, column := range columns {\n\t\tif c := _calculateScore(column); c > score {\n\t\t\tkey = column.Name\n\t\t\tscore = c\n\t\t}\n\t}\n\tif key == \"\" {\n\t\treturn columns, \"\", ErrNotValid\n\t}\n\treturn columns, key, nil\n}\n\nfunc _getColumns(ctx context.Context, db *sql.DB, table string) ([]Column, error) {\n\trows, err := db.QueryContext(ctx, `\n        SELECT\n            c.column_name,\n            c.udt_name as type,\n            (c.is_nullable = 'YES') AS nullable,\n            (c.column_default IS NOT NULL) AS has_default,\n            coalesce(string_agg(tc.constraint_type, ', '), '') as constraint\n        FROM information_schema.columns AS c\n        LEFT JOIN information_schema.key_column_usage kcu USING (table_name, column_name)\n        LEFT JOIN information_schema.table_constraints tc USING (table_name, constraint_name)\n        WHERE c.table_name = $1\n        GROUP BY c.column_name, c.is_nullable, c.udt_name, c.column_default\n        ORDER BY MIN(c.ordinal_position)\n    `, table)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcolumns := []Column{}\n\tfor rows.Next() {\n\t\tvar c Column\n\t\tvar constraints string\n\t\tif err := rows.Scan(&c.Name, &c.Type, &c.Nullable, &c.Default, &constraints); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tc.Constraint = strings.Split(constraints, \", \")\n\t\tc.Table = table\n\t\tcolumns = append(columns, c)\n\t}\n\treturn columns, rows.Close()\n}\n\nfunc _calculateScore(column Column) int {\n\tscoreType := 0\n\tscoreName := 1\n\tif slices.Contains(column.Constraint, \"PRIMARY KEY\") {\n\t\tscoreType = 3\n\t\tif column.Type == \"uuid\" {\n\t\t\tscoreType = 1\n\t\t}\n\t} else if slices.Contains(column.Constraint, \"UNIQUE\") {\n\t\tscoreType = 2\n\t}\n\tswitch strings.ToLower(column.Name) {\n\tcase \"name\":\n\t\tscoreName = 2\n\tcase \"label\":\n\t\tscoreName = 2\n\tcase \"email\":\n\t\tscoreName = 5\n\t}\n\treturn scoreType * scoreName\n}\n\nfunc convertFromDB(val any) any {\n\tswitch tmp := val.(type) {\n\tcase []byte:\n\t\treturn string(tmp)\n\tcase time.Time:\n\t\treturn tmp.UTC().Format(\"2006-01-02T15:04\")\n\t}\n\treturn val\n}\n\nfunc createFormElement(val any, column Column) FormElement {\n\tf := FormElement{\n\t\tType: \"text\",\n\t}\n\tswitch column.Type {\n\tcase \"timestamptz\":\n\t\tf.Type = \"datetime\"\n\tcase \"bool\":\n\t\tf.Type = \"boolean\"\n\t}\n\tf.Value = convertFromDB(val)\n\n\tf.Name = column.Name\n\tf.Required = !column.Nullable && !column.Default\n\n\tif strings.Contains(strings.ToLower(column.Name), \"password\") {\n\t\tf.Type = \"password\"\n\t}\n\treturn f\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_s3/index.go",
    "content": "package plg_backend_s3\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/aws/aws-sdk-go/aws\"\n\t\"github.com/aws/aws-sdk-go/aws/awserr\"\n\t\"github.com/aws/aws-sdk-go/aws/credentials\"\n\t\"github.com/aws/aws-sdk-go/aws/credentials/stscreds\"\n\t\"github.com/aws/aws-sdk-go/aws/defaults\"\n\t\"github.com/aws/aws-sdk-go/aws/session\"\n\t\"github.com/aws/aws-sdk-go/service/s3\"\n\t\"github.com/aws/aws-sdk-go/service/s3/s3manager\"\n\t\"github.com/aws/aws-sdk-go/service/sts\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nvar S3Cache AppCache\n\ntype S3Backend struct {\n\tclient     *s3.S3\n\tconfig     *aws.Config\n\tparams     map[string]string\n\tContext    context.Context\n\tthreadSize int\n\ttimeout    time.Duration\n}\n\nfunc init() {\n\tBackend.Register(\"s3\", S3Backend{})\n\tS3Cache = NewAppCache(2, 1)\n}\n\nfunc (this S3Backend) Init(params map[string]string, app *App) (IBackend, error) {\n\tif params[\"encryption_key\"] != \"\" && len(params[\"encryption_key\"]) != 32 {\n\t\treturn nil, NewError(fmt.Sprintf(\"Encryption key needs to be 32 characters (current: %d)\", len(params[\"encryption_key\"])), 400)\n\t}\n\tregion := params[\"region\"]\n\tif region == \"\" {\n\t\tregion = \"us-east-1\"\n\t\tif strings.HasSuffix(params[\"endpoint\"], \".cloudflarestorage.com\") {\n\t\t\tregion = \"auto\"\n\t\t}\n\t}\n\tcreds := []credentials.Provider{}\n\tif params[\"access_key_id\"] != \"\" || params[\"secret_access_key\"] != \"\" {\n\t\tcreds = append(creds, &credentials.StaticProvider{Value: credentials.Value{\n\t\t\tAccessKeyID:     params[\"access_key_id\"],\n\t\t\tSecretAccessKey: params[\"secret_access_key\"],\n\t\t\tSessionToken:    params[\"session_token\"],\n\t\t}})\n\t}\n\tif params[\"role_arn\"] != \"\" {\n\t\tcreds = append(creds, &stscreds.AssumeRoleProvider{\n\t\t\tClient:   sts.New(session.Must(session.NewSessionWithOptions(session.Options{Config: aws.Config{Region: aws.String(region)}}))),\n\t\t\tRoleARN:  params[\"role_arn\"],\n\t\t\tDuration: stscreds.DefaultDuration,\n\t\t})\n\t}\n\tcreds = append(\n\t\tcreds,\n\t\t&credentials.EnvProvider{},\n\t\tdefaults.RemoteCredProvider(*defaults.Config(), defaults.Handlers()),\n\t)\n\n\tconfig := &aws.Config{\n\t\tCredentials:                   credentials.NewChainCredentials(creds),\n\t\tCredentialsChainVerboseErrors: aws.Bool(true),\n\t\tS3ForcePathStyle:              aws.Bool(true),\n\t\tRegion:                        aws.String(region),\n\t}\n\tif params[\"endpoint\"] != \"\" {\n\t\tconfig.Endpoint = aws.String(params[\"endpoint\"])\n\t}\n\tvar timeout time.Duration\n\tif secs, err := strconv.Atoi(params[\"timeout\"]); err == nil {\n\t\ttimeout = time.Duration(secs) * time.Second\n\t}\n\tthreadSize, err := strconv.Atoi(params[\"number_thread\"])\n\tif err != nil {\n\t\tthreadSize = 50\n\t} else if threadSize > 5000 || threadSize < 1 {\n\t\tthreadSize = 2\n\t}\n\tbackend := &S3Backend{\n\t\tconfig:     config,\n\t\tparams:     params,\n\t\tclient:     s3.New(session.New(config)),\n\t\tContext:    app.Context,\n\t\tthreadSize: threadSize,\n\t\ttimeout:    timeout,\n\t}\n\treturn backend, nil\n}\n\nfunc (this S3Backend) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"s3\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"access_key_id\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Access Key ID*\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"secret_access_key\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"Secret Access Key*\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"advanced\",\n\t\t\t\tType:        \"enable\",\n\t\t\t\tPlaceholder: \"Advanced\",\n\t\t\t\tTarget: []string{\n\t\t\t\t\t\"s3_region\", \"s3_endpoint\", \"s3_role_arn\", \"s3_session_token\",\n\t\t\t\t\t\"s3_path\", \"s3_encryption_key\", \"s3_number_thread\", \"s3_timeout\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"s3_region\",\n\t\t\t\tName:        \"region\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Region\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"s3_endpoint\",\n\t\t\t\tName:        \"endpoint\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Endpoint\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"s3_role_arn\",\n\t\t\t\tName:        \"role_arn\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Role ARN\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"s3_session_token\",\n\t\t\t\tName:        \"session_token\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Session Token\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"s3_path\",\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Path\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"s3_encryption_key\",\n\t\t\t\tName:        \"encryption_key\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Encryption Key\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"s3_number_thread\",\n\t\t\t\tName:        \"number_thread\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Num. Thread\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"s3_timeout\",\n\t\t\t\tName:        \"timeout\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"List Object Timeout\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this S3Backend) Meta(path string) Metadata {\n\tif path == \"/\" {\n\t\treturn Metadata{\n\t\t\tCanCreateFile: NewBool(false),\n\t\t\tCanRename:     NewBool(false),\n\t\t\tCanMove:       NewBool(false),\n\t\t\tCanUpload:     NewBool(false),\n\t\t}\n\t}\n\treturn Metadata{}\n}\n\nfunc (this S3Backend) Ls(path string) (files []os.FileInfo, err error) {\n\tfiles = make([]os.FileInfo, 0)\n\tp := this.path(path)\n\tif p.bucket == \"\" {\n\t\tb, err := this.client.ListBuckets(&s3.ListBucketsInput{})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, bucket := range b.Buckets {\n\t\t\tfiles = append(files, &File{\n\t\t\t\tFName: *bucket.Name,\n\t\t\t\tFType: \"directory\",\n\t\t\t\tFTime: bucket.CreationDate.Unix(),\n\t\t\t})\n\t\t}\n\t\treturn files, nil\n\t}\n\tclient := s3.New(this.createSession(p.bucket))\n\tstart := time.Now()\n\terr = client.ListObjectsV2PagesWithContext(\n\t\tthis.Context,\n\t\t&s3.ListObjectsV2Input{\n\t\t\tBucket:    aws.String(p.bucket),\n\t\t\tPrefix:    aws.String(p.path),\n\t\t\tDelimiter: aws.String(\"/\"),\n\t\t},\n\t\tfunc(objs *s3.ListObjectsV2Output, lastPage bool) bool {\n\t\t\tfor i, object := range objs.Contents {\n\t\t\t\tif i == 0 && *object.Key == p.path {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tvar size int64 = -1\n\t\t\t\tif object.Size != nil {\n\t\t\t\t\tsize = *object.Size\n\t\t\t\t}\n\t\t\t\tisOffline := false\n\t\t\t\tif object.StorageClass != nil && *object.StorageClass == \"GLACIER\" {\n\t\t\t\t\tisOffline = true\n\t\t\t\t}\n\t\t\t\tfiles = append(files, &File{\n\t\t\t\t\tFName:   filepath.Base(*object.Key),\n\t\t\t\t\tFType:   \"file\",\n\t\t\t\t\tFTime:   object.LastModified.Unix(),\n\t\t\t\t\tFSize:   size,\n\t\t\t\t\tOffline: isOffline,\n\t\t\t\t})\n\t\t\t}\n\t\t\tfor _, object := range objs.CommonPrefixes {\n\t\t\t\tfiles = append(files, &File{\n\t\t\t\t\tFName: filepath.Base(*object.Prefix),\n\t\t\t\t\tFType: \"directory\",\n\t\t\t\t\tFTime: 0,\n\t\t\t\t})\n\t\t\t}\n\t\t\tif this.timeout > 0 && time.Since(start) > this.timeout {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn aws.BoolValue(objs.IsTruncated)\n\t\t},\n\t)\n\treturn files, err\n}\n\nfunc (this S3Backend) Stat(path string) (os.FileInfo, error) {\n\tp := this.path(path)\n\tif p.path == \"\" {\n\t\tb, err := this.client.ListBuckets(&s3.ListBucketsInput{})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, bucket := range b.Buckets {\n\t\t\tif bucket.Name != nil && *bucket.Name == p.bucket {\n\t\t\t\treturn &File{\n\t\t\t\t\tFName: *bucket.Name,\n\t\t\t\t\tFType: \"directory\",\n\t\t\t\t\tFTime: bucket.CreationDate.Unix(),\n\t\t\t\t}, nil\n\t\t\t}\n\t\t}\n\t\treturn nil, ErrNotFound\n\t}\n\tclient := s3.New(this.createSession(p.bucket))\n\tinput := &s3.HeadObjectInput{\n\t\tBucket: aws.String(p.bucket),\n\t\tKey:    aws.String(p.path),\n\t}\n\tobj, err := client.HeadObjectWithContext(this.Context, input)\n\tif err != nil {\n\t\tawsErr, ok := err.(awserr.Error)\n\t\tif ok == false || awsErr.Code() != \"NotFound\" {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn File{\n\t\t\tFName: filepath.Base(path),\n\t\t\tFType: \"directory\",\n\t\t\tFTime: -1,\n\t\t}, nil\n\t} else if obj.ContentLength == nil || obj.LastModified == nil {\n\t\treturn nil, ErrNotValid\n\t}\n\treturn File{\n\t\tFName: filepath.Base(path),\n\t\tFType: \"file\",\n\t\tFSize: (*obj.ContentLength),\n\t\tFTime: (*obj.LastModified).Unix(),\n\t}, err\n}\n\nfunc (this S3Backend) Cat(path string) (io.ReadCloser, error) {\n\tp := this.path(path)\n\tclient := s3.New(this.createSession(p.bucket))\n\tinput := &s3.GetObjectInput{\n\t\tBucket: aws.String(p.bucket),\n\t\tKey:    aws.String(p.path),\n\t}\n\tif this.params[\"encryption_key\"] != \"\" {\n\t\tinput.SSECustomerAlgorithm = aws.String(\"AES256\")\n\t\tinput.SSECustomerKey = aws.String(this.params[\"encryption_key\"])\n\t}\n\tobj, err := client.GetObjectWithContext(this.Context, input)\n\tif err != nil {\n\t\tawsErr, ok := err.(awserr.Error)\n\t\tif ok == false {\n\t\t\treturn nil, err\n\t\t}\n\t\tif awsErr.Code() == \"InvalidRequest\" && strings.Contains(awsErr.Message(), \"encryption\") {\n\t\t\tinput.SSECustomerAlgorithm = nil\n\t\t\tinput.SSECustomerKey = nil\n\t\t\tobj, err = client.GetObject(input)\n\t\t\treturn obj.Body, err\n\t\t} else if awsErr.Code() == \"InvalidArgument\" && strings.Contains(awsErr.Message(), \"secret key was invalid\") {\n\t\t\treturn nil, NewError(\"This file is encrypted file, you need the correct key!\", 400)\n\t\t} else if awsErr.Code() == \"AccessDenied\" {\n\t\t\treturn nil, ErrNotAllowed\n\t\t} else if awsErr.Code() == \"InvalidObjectState\" {\n\t\t\treturn nil, ErrNotReachable\n\t\t}\n\t\treturn nil, err\n\t}\n\treturn obj.Body, nil\n}\n\nfunc (this S3Backend) Mkdir(path string) error {\n\tp := this.path(path)\n\tclient := s3.New(this.createSession(p.bucket))\n\tif p.path == \"\" {\n\t\t_, err := client.CreateBucket(&s3.CreateBucketInput{\n\t\t\tBucket: aws.String(path),\n\t\t})\n\t\treturn err\n\t}\n\t_, err := client.PutObject(&s3.PutObjectInput{\n\t\tBucket: aws.String(p.bucket),\n\t\tKey:    aws.String(EnforceDirectory(p.path)),\n\t})\n\treturn err\n}\n\nfunc (this S3Backend) Rm(path string) error {\n\tp := this.path(path)\n\tclient := s3.New(this.createSession(p.bucket))\n\tif p.bucket == \"\" {\n\t\treturn ErrNotFound\n\t}\n\tfinfo, err := this.Stat(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// CASE 1: remove a file\n\tif finfo.IsDir() == false {\n\t\t_, err := client.DeleteObject(&s3.DeleteObjectInput{\n\t\t\tBucket: aws.String(p.bucket),\n\t\t\tKey:    aws.String(p.path),\n\t\t})\n\t\treturn err\n\t}\n\t// CASE 2: remove a folder\n\tjobChan := make(chan S3Path, this.threadSize)\n\terrChan := make(chan error, this.threadSize)\n\tctx, cancel := context.WithCancel(this.Context)\n\tvar wg sync.WaitGroup\n\tfor i := 1; i <= this.threadSize; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tfor spath := range jobChan {\n\t\t\t\tif ctx.Err() != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif _, err := client.DeleteObject(&s3.DeleteObjectInput{\n\t\t\t\t\tBucket: aws.String(spath.bucket),\n\t\t\t\t\tKey:    aws.String(spath.path),\n\t\t\t\t}); err != nil {\n\t\t\t\t\tcancel()\n\t\t\t\t\terrChan <- err\n\t\t\t\t}\n\t\t\t}\n\t\t\twg.Done()\n\t\t}()\n\t}\n\terr = client.ListObjectsV2PagesWithContext(\n\t\tthis.Context,\n\t\t&s3.ListObjectsV2Input{\n\t\t\tBucket: aws.String(p.bucket),\n\t\t\tPrefix: aws.String(p.path),\n\t\t},\n\t\tfunc(objs *s3.ListObjectsV2Output, lastPage bool) bool {\n\t\t\tif ctx.Err() != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tfor _, object := range objs.Contents {\n\t\t\t\tjobChan <- S3Path{p.bucket, *object.Key}\n\t\t\t}\n\t\t\treturn aws.BoolValue(objs.IsTruncated)\n\t\t},\n\t)\n\tclose(jobChan)\n\twg.Wait()\n\tclose(errChan)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor err := range errChan {\n\t\treturn err\n\t}\n\tif p.path == \"\" {\n\t\t_, err := client.DeleteBucket(&s3.DeleteBucketInput{\n\t\t\tBucket: aws.String(p.bucket),\n\t\t})\n\t\treturn err\n\t}\n\treturn err\n}\n\nfunc (this S3Backend) Mv(from string, to string) error {\n\tif from == to {\n\t\treturn nil\n\t}\n\tf := this.path(from)\n\tt := this.path(to)\n\tclient := s3.New(this.createSession(f.bucket))\n\n\t// CASE 1: Rename a bucket\n\tif f.path == \"\" {\n\t\treturn ErrNotImplemented\n\t}\n\n\tfinfo, err := this.Stat(from)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// CASE 2: Rename/Move a file\n\tif finfo.IsDir() == false {\n\t\tinput := &s3.CopyObjectInput{\n\t\t\tCopySource: aws.String(fmt.Sprintf(\"%s/%s\", f.bucket, f.path)),\n\t\t\tBucket:     aws.String(t.bucket),\n\t\t\tKey:        aws.String(t.path),\n\t\t}\n\t\tif this.params[\"encryption_key\"] != \"\" {\n\t\t\tinput.CopySourceSSECustomerAlgorithm = aws.String(\"AES256\")\n\t\t\tinput.CopySourceSSECustomerKey = aws.String(this.params[\"encryption_key\"])\n\t\t\tinput.SSECustomerAlgorithm = aws.String(\"AES256\")\n\t\t\tinput.SSECustomerKey = aws.String(this.params[\"encryption_key\"])\n\t\t}\n\t\t_, err := client.CopyObject(input)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = client.DeleteObject(&s3.DeleteObjectInput{\n\t\t\tBucket: aws.String(f.bucket),\n\t\t\tKey:    aws.String(f.path),\n\t\t})\n\t\treturn err\n\t}\n\t// CASE 3: Rename/Move a folder\n\tjobChan := make(chan []S3Path, this.threadSize)\n\terrChan := make(chan error, this.threadSize)\n\tctx, cancel := context.WithCancel(this.Context)\n\tvar wg sync.WaitGroup\n\tfor i := 1; i <= this.threadSize; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tfor spath := range jobChan {\n\t\t\t\tif ctx.Err() != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tinput := &s3.CopyObjectInput{\n\t\t\t\t\tCopySource: aws.String(fmt.Sprintf(\"%s/%s\", spath[0].bucket, spath[0].path)),\n\t\t\t\t\tBucket:     aws.String(spath[1].bucket),\n\t\t\t\t\tKey:        aws.String(spath[1].path),\n\t\t\t\t}\n\t\t\t\tif this.params[\"encryption_key\"] != \"\" {\n\t\t\t\t\tinput.CopySourceSSECustomerAlgorithm = aws.String(\"AES256\")\n\t\t\t\t\tinput.CopySourceSSECustomerKey = aws.String(this.params[\"encryption_key\"])\n\t\t\t\t\tinput.SSECustomerAlgorithm = aws.String(\"AES256\")\n\t\t\t\t\tinput.SSECustomerKey = aws.String(this.params[\"encryption_key\"])\n\t\t\t\t}\n\t\t\t\t_, err := client.CopyObject(input)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcancel()\n\t\t\t\t\terrChan <- err\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t_, err = client.DeleteObject(&s3.DeleteObjectInput{\n\t\t\t\t\tBucket: aws.String(spath[0].bucket),\n\t\t\t\t\tKey:    aws.String(spath[0].path),\n\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\tcancel()\n\t\t\t\t\terrChan <- err\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\twg.Done()\n\t\t}()\n\t}\n\terr = client.ListObjectsV2PagesWithContext(\n\t\tthis.Context,\n\t\t&s3.ListObjectsV2Input{\n\t\t\tBucket: aws.String(f.bucket),\n\t\t\tPrefix: aws.String(f.path),\n\t\t},\n\t\tfunc(objs *s3.ListObjectsV2Output, lastPage bool) bool {\n\t\t\tif ctx.Err() != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tfor _, object := range objs.Contents {\n\t\t\t\tjobChan <- []S3Path{\n\t\t\t\t\t{f.bucket, *object.Key},\n\t\t\t\t\t{t.bucket, t.path + strings.TrimPrefix(*object.Key, f.path)},\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn aws.BoolValue(objs.IsTruncated)\n\t\t},\n\t)\n\tclose(jobChan)\n\twg.Wait()\n\tclose(errChan)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor err := range errChan {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (this S3Backend) Touch(path string) error {\n\tp := this.path(path)\n\tclient := s3.New(this.createSession(p.bucket))\n\tif p.bucket == \"\" {\n\t\treturn ErrNotValid\n\t}\n\tinput := &s3.PutObjectInput{\n\t\tBody:          strings.NewReader(\"\"),\n\t\tContentLength: aws.Int64(0),\n\t\tBucket:        aws.String(p.bucket),\n\t\tKey:           aws.String(p.path),\n\t\tContentType:   aws.String(GetMimeType(path)),\n\t}\n\tif this.params[\"encryption_key\"] != \"\" {\n\t\tinput.SSECustomerAlgorithm = aws.String(\"AES256\")\n\t\tinput.SSECustomerKey = aws.String(this.params[\"encryption_key\"])\n\t}\n\t_, err := client.PutObject(input)\n\treturn err\n}\n\nfunc (this S3Backend) Save(path string, file io.Reader) error {\n\tp := this.path(path)\n\tif p.bucket == \"\" {\n\t\treturn ErrNotValid\n\t}\n\tuploader := s3manager.NewUploader(this.createSession(p.bucket))\n\tinput := s3manager.UploadInput{\n\t\tBody:        file,\n\t\tBucket:      aws.String(p.bucket),\n\t\tKey:         aws.String(p.path),\n\t\tContentType: aws.String(GetMimeType(path)),\n\t}\n\tif this.params[\"encryption_key\"] != \"\" {\n\t\tinput.SSECustomerAlgorithm = aws.String(\"AES256\")\n\t\tinput.SSECustomerKey = aws.String(this.params[\"encryption_key\"])\n\t}\n\t_, err := uploader.Upload(&input)\n\treturn err\n}\n\nfunc (this S3Backend) createSession(bucket string) *session.Session {\n\tif this.params[\"region\"] == \"\" {\n\t\tnewParams := map[string]string{\"bucket\": bucket}\n\t\tfor k, v := range this.params {\n\t\t\tnewParams[k] = v\n\t\t}\n\t\tif c := S3Cache.Get(newParams); c == nil {\n\t\t\tres, err := this.client.GetBucketLocation(&s3.GetBucketLocationInput{\n\t\t\t\tBucket: aws.String(bucket),\n\t\t\t})\n\t\t\tif err == nil && res.LocationConstraint != nil {\n\t\t\t\tthis.config.Region = res.LocationConstraint\n\t\t\t}\n\t\t\tS3Cache.Set(newParams, this.config.Region)\n\t\t} else {\n\t\t\tthis.config.Region = c.(*string)\n\t\t}\n\t}\n\tsess := session.New(this.config)\n\treturn sess\n}\n\ntype S3Path struct {\n\tbucket string\n\tpath   string\n}\n\nfunc (s S3Backend) path(p string) S3Path {\n\tsp := strings.Split(p, \"/\")\n\tbucket := \"\"\n\tif len(sp) > 1 {\n\t\tbucket = sp[1]\n\t}\n\tpath := \"\"\n\tif len(sp) > 2 {\n\t\tpath = strings.Join(sp[2:], \"/\")\n\t}\n\treturn S3Path{\n\t\tbucket,\n\t\tpath,\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_samba/index.go",
    "content": "package plg_backend_samba\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/hirochachacha/go-smb2\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar SambaCache AppCache\n\nfunc init() {\n\tBackend.Register(\"samba\", Samba{})\n\n\tSambaCache = NewAppCache(30)\n\tSambaCache.OnEvict(func(key string, value interface{}) {\n\t\tsmb := value.(*Samba)\n\t\tfor key, _ := range smb.share {\n\t\t\tif err := smb.share[key].Umount(); err != nil {\n\t\t\t\tLog.Warning(\"samba: error unmounting share: %v\", err)\n\t\t\t}\n\t\t}\n\t\tif err := smb.session.Logoff(); err != nil {\n\t\t\tLog.Warning(\"samba: error logging out: %v\", err)\n\t\t}\n\t})\n}\n\ntype Samba struct {\n\tsession *smb2.Session\n\tshare   map[string]*smb2.Share\n}\n\nfunc (smb Samba) Init(params map[string]string, app *App) (IBackend, error) {\n\tif c := SambaCache.Get(params); c != nil {\n\t\treturn c.(*Samba), nil\n\t}\n\tif strings.HasPrefix(params[\"host\"], \"smb://\") == false {\n\t\tparams[\"host\"] = \"smb://\" + params[\"host\"]\n\t}\n\tif u, err := url.Parse(params[\"host\"]); err == nil {\n\t\tparams[\"host\"] = u.Host\n\t\tif params[\"port\"] == \"\" {\n\t\t\tparams[\"port\"] = u.Port()\n\t\t}\n\t\tif params[\"share\"] == \"\" {\n\t\t\tparams[\"share\"] = strings.ReplaceAll(u.Path, \"/\", \"\")\n\t\t}\n\t\tif params[\"username\"] == \"\" && u.User != nil {\n\t\t\tparams[\"username\"] = u.User.Username()\n\t\t}\n\t\tif params[\"password\"] == \"\" && u.User != nil {\n\t\t\tparams[\"password\"], _ = u.User.Password()\n\t\t}\n\t}\n\tif params[\"port\"] == \"\" {\n\t\tparams[\"port\"] = \"445\"\n\t}\n\n\thost := fmt.Sprintf(\"%s:%s\", params[\"host\"], params[\"port\"])\n\tconn, err := net.DialTimeout(\"tcp\", host, 10*time.Second)\n\tif err != nil {\n\t\tLog.Debug(\"plg_backend_samba::netdial host[%s] err[%s]\", host, err.Error())\n\t\treturn nil, err\n\t}\n\n\tsmb.share = make(map[string]*smb2.Share, 0)\n\tsmb.session, err = (&smb2.Dialer{\n\t\tInitiator: &smb2.NTLMInitiator{\n\t\t\tUser: func() string {\n\t\t\t\tif params[\"username\"] == \"\" {\n\t\t\t\t\treturn \"Guest\"\n\t\t\t\t}\n\t\t\t\treturn params[\"username\"]\n\t\t\t}(),\n\t\t\tPassword: params[\"password\"],\n\t\t\tDomain:   params[\"domain\"],\n\t\t},\n\t}).Dial(conn)\n\tif err != nil {\n\t\tLog.Debug(\"plg_backend_samba::smbdial host[%s] err[%s] username[%s] domain[%s]\", host, err.Error(), params[\"username\"], params[\"domain\"])\n\t\treturn nil, err\n\t}\n\tif params[\"share\"] == \"\" {\n\t\tnames, err := smb.session.ListSharenames()\n\t\tif err != nil {\n\t\t\tLog.Debug(\"plg_backend_samba::list host[%s] err[%s]\", host, err.Error())\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, name := range names {\n\t\t\tif strings.HasSuffix(name, \"$\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif m, err := smb.session.Mount(name); err == nil {\n\t\t\t\tsmb.share[name] = m\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif m, err := smb.session.Mount(params[\"share\"]); err == nil {\n\t\t\tsmb.share[params[\"share\"]] = m\n\t\t}\n\t}\n\tSambaCache.Set(params, &smb)\n\treturn &smb, nil\n}\n\nfunc (smb Samba) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"samba\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"host\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Hostname\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"username\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Username\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"password\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"Password\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"advanced\",\n\t\t\t\tType:        \"enable\",\n\t\t\t\tPlaceholder: \"Advanced\",\n\t\t\t\tTarget:      []string{\"samba_port\", \"samba_path\", \"samba_domain\", \"samba_share\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"samba_path\",\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Path\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"samba_port\",\n\t\t\t\tName:        \"port\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"Port - eg: 445\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"samba_domain\",\n\t\t\t\tName:        \"domain\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Domain\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tId:          \"samba_share\",\n\t\t\t\tName:        \"share\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Share Name\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (smb Samba) Ls(path string) ([]os.FileInfo, error) {\n\tif path == \"/\" {\n\t\tf := make([]os.FileInfo, 0)\n\t\tfor key, _ := range smb.share {\n\t\t\tf = append(f, File{\n\t\t\t\tFName: key,\n\t\t\t\tFType: \"directory\",\n\t\t\t})\n\t\t}\n\t\treturn f, nil\n\t}\n\tshare, path, err := smb.toSambaPath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdir, err := share.Open(path)\n\tif err != nil {\n\t\treturn nil, fromSambaErr(err)\n\t}\n\tdefer dir.Close()\n\n\tfs, err := dir.Readdir(-1)\n\treturn fs, fromSambaErr(err)\n}\n\nfunc (smb Samba) Stat(path string) (os.FileInfo, error) {\n\tshare, path, err := smb.toSambaPath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tf, err := share.Stat(path)\n\treturn f, fromSambaErr(err)\n}\n\nfunc (smb Samba) Cat(path string) (io.ReadCloser, error) {\n\tshare, path, err := smb.toSambaPath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tf, err := share.Open(path)\n\treturn f, fromSambaErr(err)\n}\n\nfunc (smb Samba) Mkdir(path string) error {\n\tshare, path, err := smb.toSambaPath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn fromSambaErr(share.Mkdir(path, os.ModeDir))\n}\n\nfunc (smb Samba) Rm(path string) error {\n\tshare, path, err := smb.toSambaPath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn fromSambaErr(share.RemoveAll(path))\n}\n\nfunc (smb Samba) Mv(from, to string) error {\n\tfromShare, fromPath, err := smb.toSambaPath(from)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttoShare, toPath, err := smb.toSambaPath(to)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif fromShare != toShare {\n\t\treturn ErrNotImplemented\n\t}\n\treturn fromSambaErr(fromShare.Rename(fromPath, toPath))\n}\n\nfunc (smb Samba) Save(path string, content io.Reader) error {\n\tshare, path, err := smb.toSambaPath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf, err := share.Create(path)\n\tif err != nil {\n\t\treturn fromSambaErr(err)\n\t}\n\tif _, err = io.Copy(f, content); err != nil {\n\t\tf.Close()\n\t\treturn fromSambaErr(err)\n\t}\n\treturn f.Close()\n}\n\nfunc (smb Samba) Touch(path string) error {\n\tshare, path, err := smb.toSambaPath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf, err := share.Create(path)\n\tif err != nil {\n\t\treturn fromSambaErr(err)\n\t}\n\treturn fromSambaErr(f.Close())\n}\n\nfunc (smb Samba) toSambaPath(path string) (*smb2.Share, string, error) {\n\tp := strings.Split(strings.Trim(path, \"/\"), \"/\")\n\tif len(p) == 0 {\n\t\treturn nil, \"\", ErrNotAllowed\n\t}\n\tsharename := p[0]\n\toPath := strings.TrimLeft(strings.Join(p[1:], \"\\\\\"), \"\\\\\")\n\tif smb.share[sharename] == nil {\n\t\treturn nil, \"\", ErrNotFound\n\t}\n\treturn smb.share[sharename], oPath, nil\n}\n\nfunc fromSambaErr(err error) error {\n\tswitch {\n\tcase os.IsPermission(err):\n\t\treturn ErrPermissionDenied\n\tcase os.IsNotExist(err):\n\t\treturn ErrNotFound\n\tdefault:\n\t\treturn err\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_sftp/index.go",
    "content": "package plg_backend_sftp\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\t\"sync\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/pkg/sftp\"\n\t\"golang.org/x/crypto/ssh\"\n)\n\nvar SftpCache AppCache\n\ntype Sftp struct {\n\tSSHClient  *ssh.Client\n\tSFTPClient *sftp.Client\n\twg         *sync.WaitGroup\n}\n\nfunc init() {\n\tBackend.Register(\"sftp\", Sftp{})\n\n\tSftpCache = NewAppCache(1, 1)\n\tSftpCache.OnEvict(func(key string, value interface{}) {\n\t\tc := value.(*Sftp)\n\t\tif c == nil {\n\t\t\tLog.Warning(\"plg_backend_sftp::sftp is nil on close\")\n\t\t\treturn\n\t\t} else if c.wg == nil {\n\t\t\tc.Close()\n\t\t\tLog.Warning(\"plg_backend_sftp::wg is nil on close\")\n\t\t\treturn\n\t\t}\n\t\tc.wg.Wait()\n\t\tLog.Debug(\"plg_backend_sftp::vacuum\")\n\t\tc.Close()\n\t})\n}\n\nfunc (s Sftp) Init(params map[string]string, app *App) (IBackend, error) {\n\tp := struct {\n\t\thostname   string\n\t\tport       string\n\t\tusername   string\n\t\tpassword   string\n\t\tpassphrase string\n\t}{\n\t\tparams[\"hostname\"],\n\t\tparams[\"port\"],\n\t\tparams[\"username\"],\n\t\tparams[\"password\"],\n\t\tparams[\"passphrase\"],\n\t}\n\tif p.port == \"\" {\n\t\tp.port = \"22\"\n\t}\n\n\tc := SftpCache.Get(params)\n\tif c != nil {\n\t\td := c.(*Sftp)\n\t\tif d == nil {\n\t\t\tLog.Warning(\"plg_backend_sftp::sftp is nil on get\")\n\t\t\treturn nil, ErrInternal\n\t\t} else if d.wg == nil {\n\t\t\tLog.Warning(\"plg_backend_sftp::wg is nil on get\")\n\t\t\treturn nil, ErrInternal\n\t\t}\n\t\td.wg.Add(1)\n\t\tgo func() {\n\t\t\t<-app.Context.Done()\n\t\t\td.wg.Done()\n\t\t}()\n\t\treturn d, nil\n\t}\n\n\taddr := p.hostname + \":\" + p.port\n\n\tkeyStartMatcher := regexp.MustCompile(`^-----BEGIN [A-Z\\ ]+-----`)\n\tkeyEndMatcher := regexp.MustCompile(`-----END [A-Z\\ ]+-----$`)\n\tkeyContentMatcher := regexp.MustCompile(`^[a-zA-Z0-9\\+\\/\\=\\n]+$`)\n\tisPrivateKey := func(pass string) bool {\n\t\tp := strings.TrimSpace(pass)\n\n\t\t// match private key beginning\n\t\tif keyStartMatcher.FindStringIndex(p) == nil {\n\t\t\treturn false\n\t\t}\n\t\tp = keyStartMatcher.ReplaceAllString(p, \"\")\n\t\t// match private key ending\n\t\tif keyEndMatcher.FindStringIndex(p) == nil {\n\t\t\treturn false\n\t\t}\n\t\tp = keyEndMatcher.ReplaceAllString(p, \"\")\n\t\tp = strings.Replace(p, \" \", \"\", -1)\n\t\t// match private key content\n\t\tif keyContentMatcher.FindStringIndex(p) == nil {\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t}\n\n\trestorePrivateKeyLineBreaks := func(pass string) string {\n\t\tp := strings.TrimSpace(pass)\n\n\t\tkeyStartString := keyStartMatcher.FindString(p)\n\t\tp = keyStartMatcher.ReplaceAllString(p, \"\")\n\t\tkeyEndString := keyEndMatcher.FindString(p)\n\t\tp = keyEndMatcher.ReplaceAllString(p, \"\")\n\t\tp = strings.Replace(p, \" \", \"\", -1)\n\t\tkeyContentString := keyContentMatcher.FindString(p)\n\n\t\treturn keyStartString + \"\\n\" + keyContentString + \"\\n\" + keyEndString\n\t}\n\n\t/*\n\t * SSH has a range of authentication methods available: publickey, password,\n\t * keyboard-interactive, password-callback, publickey-callback, gss, .... Typical sftp\n\t * servers only have those 2: 'publickey' and 'password' but some exotic ones we've seen\n\t * have 'publickey' and 'keyboard-interactive'. If you're unlucky enough to have to work\n\t * with something else than those 3 we provide support for, either create a PR here\n\t * or contact us\n\t */\n\tvar auth []ssh.AuthMethod\n\tif isPrivateKey(p.password) {\n\t\tprivateKey := restorePrivateKeyLineBreaks(p.password)\n\t\tsigner, err := func() (ssh.Signer, error) {\n\t\t\tif p.passphrase == \"\" {\n\t\t\t\treturn ssh.ParsePrivateKey([]byte(privateKey))\n\t\t\t}\n\t\t\treturn ssh.ParsePrivateKeyWithPassphrase([]byte(privateKey), []byte(p.passphrase))\n\t\t}()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tauth = []ssh.AuthMethod{ssh.PublicKeys(signer)}\n\t} else {\n\t\tauth = []ssh.AuthMethod{\n\t\t\tssh.Password(p.password),\n\t\t\tssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) ([]string, error) {\n\t\t\t\tanswers := make([]string, len(questions))\n\t\t\t\tfor i, _ := range answers {\n\t\t\t\t\tanswers[i] = p.password\n\t\t\t\t}\n\t\t\t\treturn answers, nil\n\t\t\t}),\n\t\t}\n\t}\n\n\tconfig := &ssh.ClientConfig{\n\t\tUser: p.username,\n\t\tAuth: auth,\n\t\tHostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {\n\t\t\tif params[\"hostkey\"] == \"\" {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tfsha := ssh.FingerprintSHA256(key)\n\t\t\tif fsha == params[\"hostkey\"] {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tfmd := ssh.FingerprintLegacyMD5(key)\n\t\t\tif fmd == params[\"hostkey\"] {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tLog.Debug(\"plg_backend_sftp::fingerprint host key isn't correct on %s => '%s'\", hostname, fsha)\n\t\t\treturn ErrNotValid\n\t\t},\n\t}\n\n\tclient, err := ssh.Dial(\"tcp\", addr, config)\n\tif err != nil {\n\t\tconfig.User = strings.ToLower(p.username)\n\t\tclient, err = ssh.Dial(\"tcp\", addr, config)\n\t\tif err != nil {\n\t\t\treturn &s, ErrAuthenticationFailed\n\t\t}\n\t}\n\ts.SSHClient = client\n\n\tsession, err := sftp.NewClient(s.SSHClient)\n\tif err != nil {\n\t\treturn &s, err\n\t}\n\ts.SFTPClient = session\n\ts.wg = new(sync.WaitGroup)\n\ts.wg.Add(1)\n\tgo func() {\n\t\t<-app.Context.Done()\n\t\ts.wg.Done()\n\t}()\n\tSftpCache.Set(params, &s)\n\treturn &s, nil\n}\n\nfunc (b Sftp) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"sftp\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"hostname\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Hostname*\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"username\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Username\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"password\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"Password\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"advanced\",\n\t\t\t\tType:        \"enable\",\n\t\t\t\tPlaceholder: \"Advanced\",\n\t\t\t\tTarget:      []string{\"sftp_path\", \"sftp_port\", \"sftp_passphrase\", \"sftp_hostkey\"},\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"sftp_path\",\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Path\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"sftp_port\",\n\t\t\t\tName:        \"port\",\n\t\t\t\tType:        \"number\",\n\t\t\t\tPlaceholder: \"Port\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"sftp_passphrase\",\n\t\t\t\tName:        \"passphrase\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"Passphrase\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"sftp_hostkey\",\n\t\t\t\tName:        \"hostkey\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Host key\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (b Sftp) Home() (string, error) {\n\tcwd, err := b.SFTPClient.Getwd()\n\tif err != nil {\n\t\treturn \"\", b.err(err)\n\t}\n\tlength := len(cwd)\n\tif length > 0 && cwd[length-1:] != \"/\" {\n\t\treturn cwd + \"/\", nil\n\t}\n\treturn cwd, nil\n}\n\nfunc (b Sftp) Ls(path string) ([]os.FileInfo, error) {\n\tfiles, err := b.SFTPClient.ReadDir(path)\n\treturn files, b.err(err)\n}\n\nfunc (b Sftp) Cat(path string) (io.ReadCloser, error) {\n\tremoteFile, err := b.SFTPClient.OpenFile(path, os.O_RDONLY)\n\tif err != nil {\n\t\treturn nil, b.err(err)\n\t}\n\treturn remoteFile, nil\n}\n\nfunc (b Sftp) Mkdir(path string) error {\n\terr := b.SFTPClient.Mkdir(path)\n\treturn b.err(err)\n}\n\nfunc (b Sftp) Rm(path string) error {\n\tif IsDirectory(path) {\n\t\tlist, err := b.SFTPClient.ReadDir(path)\n\t\tif err != nil {\n\t\t\treturn b.err(err)\n\t\t}\n\t\tfor _, entry := range list {\n\t\t\tp := path + entry.Name()\n\t\t\tif entry.IsDir() {\n\t\t\t\tp += \"/\"\n\t\t\t\terr := b.Rm(p)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn b.err(err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\terr := b.SFTPClient.Remove(p)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn b.err(err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\terr = b.SFTPClient.RemoveDirectory(path)\n\t\tif err != nil {\n\t\t\treturn b.err(err)\n\t\t}\n\t} else {\n\t\terr := b.SFTPClient.Remove(path)\n\t\treturn b.err(err)\n\t}\n\treturn nil\n}\n\nfunc (b Sftp) Mv(from string, to string) error {\n\terr := b.SFTPClient.Rename(from, to)\n\treturn b.err(err)\n}\n\nfunc (b Sftp) Touch(path string) error {\n\tfile, err := b.SFTPClient.OpenFile(path, os.O_WRONLY|os.O_CREATE)\n\tif err != nil {\n\t\treturn b.err(err)\n\t}\n\t_, err = file.ReadFrom(strings.NewReader(\"\"))\n\tfile.Close()\n\treturn b.err(err)\n}\n\nfunc (b Sftp) Save(path string, file io.Reader) error {\n\tremoteFile, err := b.SFTPClient.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)\n\tif err != nil {\n\t\treturn b.err(err)\n\t}\n\t_, err = io.Copy(remoteFile, file)\n\tremoteFile.Close()\n\treturn b.err(err)\n}\n\nfunc (b Sftp) Stat(path string) (os.FileInfo, error) {\n\tf, err := b.SFTPClient.Stat(path)\n\treturn f, b.err(err)\n}\n\nfunc (b Sftp) Close() error {\n\terr0 := b.SFTPClient.Close()\n\terr1 := b.SSHClient.Close()\n\n\tif err0 != nil {\n\t\treturn err0\n\t}\n\treturn err1\n}\n\nfunc (b Sftp) err(e error) error {\n\tf, ok := e.(*sftp.StatusError)\n\tif ok == false {\n\t\tif e == os.ErrNotExist {\n\t\t\treturn ErrNotFound\n\t\t}\n\t\treturn e\n\t}\n\tswitch f.Code {\n\tcase 0:\n\t\treturn nil\n\tcase 1:\n\t\treturn NewError(\"There's nothing more to see\", 404)\n\tcase 2:\n\t\treturn NewError(\"Does not exist\", 404)\n\tcase 3:\n\t\treturn NewError(\"Permission denied\", 403)\n\tcase 4:\n\t\treturn NewError(\"Failure\", 409)\n\tcase 5:\n\t\treturn NewError(\"Not Compatible\", 400)\n\tcase 6:\n\t\treturn NewError(\"No Connection\", 503)\n\tcase 7:\n\t\treturn NewError(\"Connection Lost\", 503)\n\tcase 8:\n\t\treturn NewError(\"Operation not supported\", 501)\n\tcase 9:\n\t\treturn NewError(\"Not valid\", 400)\n\tcase 10:\n\t\treturn NewError(\"No such path\", 404)\n\tcase 11:\n\t\treturn NewError(\"File already exists\", 409)\n\tcase 12:\n\t\treturn NewError(\"Write protected\", 403)\n\tcase 13:\n\t\treturn NewError(\"No media\", 404)\n\tcase 14:\n\t\treturn NewError(\"No space left\", 400)\n\tcase 15:\n\t\treturn NewError(\"Quota exceeded\", 400)\n\tcase 16:\n\t\treturn NewError(\"Unknown\", 400)\n\tcase 17:\n\t\treturn NewError(\"Lock conflict\", 409)\n\tcase 18:\n\t\treturn NewError(\"Directory not empty\", 400)\n\tcase 19:\n\t\treturn NewError(\"Not a directory\", 400)\n\tcase 20:\n\t\treturn NewError(\"Invalid filename\", 400)\n\tcase 21:\n\t\treturn NewError(\"Link loop\", 508)\n\tcase 22:\n\t\treturn NewError(\"Cannot delete\", 400)\n\tcase 23:\n\t\treturn NewError(\"Invalid query\", 400)\n\tcase 24:\n\t\treturn NewError(\"File is a directory\", 400)\n\tcase 25:\n\t\treturn NewError(\"Lock conflict\", 409)\n\tcase 26:\n\t\treturn NewError(\"Lock refused\", 400)\n\tcase 27:\n\t\treturn NewError(\"Delete pending\", 400)\n\tcase 28:\n\t\treturn NewError(\"File corrupt\", 400)\n\tcase 29:\n\t\treturn NewError(\"Invalid owner\", 400)\n\tcase 30:\n\t\treturn NewError(\"Invalid group\", 400)\n\tcase 31:\n\t\treturn NewError(\"Lock wasn't granted\", 400)\n\tdefault:\n\t\treturn NewError(\"Oops! Something went wrong\", 500)\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_storj/index.go",
    "content": "package plg_backend_storj\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"storj.io/uplink\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"golang.org/x/sync/semaphore\"\n)\n\nconst CONCURRENCY_LEVEL = 20\n\ntype Storj struct {\n\tContext context.Context\n\tProject *uplink.Project\n}\n\nfunc init() {\n\tBackend.Register(\"storj\", Storj{})\n}\n\nfunc (this Storj) Init(params map[string]string, app *App) (IBackend, error) {\n\taccess, err := uplink.ParseAccess(params[\"access-grant\"])\n\tif err != nil {\n\t\treturn nil, ErrAuthenticationFailed\n\t}\n\tproject, err := uplink.OpenProject(app.Context, access)\n\tif err != nil {\n\t\treturn nil, ErrAuthenticationFailed\n\t}\n\treturn Storj{app.Context, project}, nil\n}\n\nfunc (this Storj) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"storj\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"access-grant\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Access Grant\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this Storj) Meta(path string) Metadata {\n\tdefer this.Project.Close()\n\tif path == \"/\" {\n\t\treturn Metadata{\n\t\t\tCanCreateFile: NewBool(false),\n\t\t\tCanRename:     NewBool(false),\n\t\t\tCanMove:       NewBool(false),\n\t\t\tCanUpload:     NewBool(false),\n\t\t}\n\t}\n\treturn Metadata{}\n}\n\nfunc (this Storj) Ls(path string) ([]os.FileInfo, error) {\n\tdefer this.Project.Close()\n\tfiles := make([]os.FileInfo, 0)\n\tbucket, prefix := this.path(path)\n\tif bucket == \"\" {\n\t\tbuckets := this.Project.ListBuckets(this.Context, nil)\n\t\tfor buckets.Next() {\n\t\t\titem := buckets.Item()\n\t\t\tfiles = append(files, File{\n\t\t\t\tFName: item.Name,\n\t\t\t\tFType: \"directory\",\n\t\t\t\tFTime: item.Created.Unix(),\n\t\t\t})\n\t\t}\n\t\tif err := buckets.Err(); err != nil {\n\t\t\treturn files, err\n\t\t}\n\t} else {\n\t\tobjects := this.Project.ListObjects(this.Context, bucket, &uplink.ListObjectsOptions{\n\t\t\tPrefix:    prefix,\n\t\t\tRecursive: false,\n\t\t})\n\t\tfor objects.Next() {\n\t\t\titem := objects.Item()\n\t\t\tfname := filepath.Base(item.Key)\n\t\t\tif fname == \".file_placeholder\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfiles = append(files, File{\n\t\t\t\tFName: fname,\n\t\t\t\tFType: func() string {\n\t\t\t\t\tif item.IsPrefix {\n\t\t\t\t\t\treturn \"directory\"\n\t\t\t\t\t}\n\t\t\t\t\treturn \"file\"\n\t\t\t\t}(),\n\t\t\t\tFSize: -1,\n\t\t\t\tFTime: -1,\n\t\t\t})\n\t\t}\n\t\tif err := objects.Err(); err != nil {\n\t\t\treturn files, err\n\t\t}\n\t}\n\treturn files, nil\n}\n\nfunc (this Storj) Stat(path string) (os.FileInfo, error) {\n\tdefer this.Project.Close()\n\tbucket, prefix := this.path(path)\n\tif finfo, err := this.Project.StatObject(this.Context, bucket, prefix); err == nil {\n\t\treturn File{\n\t\t\tFName: filepath.Base(path),\n\t\t\tFTime: finfo.System.Created.Unix(),\n\t\t\tFType: func() string {\n\t\t\t\tif finfo.IsPrefix {\n\t\t\t\t\treturn \"directory\"\n\t\t\t\t}\n\t\t\t\treturn \"file\"\n\t\t\t}(),\n\t\t\tFSize: finfo.System.ContentLength,\n\t\t}, nil\n\t}\n\treturn File{\n\t\tFName: filepath.Base(path),\n\t\tFType: \"directory\",\n\t\tFTime: -1,\n\t}, nil\n}\n\nfunc (this Storj) Cat(path string) (io.ReadCloser, error) {\n\tdefer this.Project.Close()\n\tbucket, prefix := this.path(path)\n\treturn this.Project.DownloadObject(this.Context, bucket, prefix, nil)\n}\n\nfunc (this Storj) Mkdir(path string) error {\n\tif bucket, prefix := this.path(path); prefix == \"\" {\n\t\t_, err := this.Project.CreateBucket(this.Context, bucket)\n\t\tthis.Project.Close()\n\t\treturn err\n\t}\n\treturn this.Touch(path + \".keep\")\n}\n\nfunc (this Storj) Rm(path string) error {\n\tdefer this.Project.Close()\n\tbucket, prefix := this.path(path)\n\tif prefix == \"\" { // remove an entire bucket with its content\n\t\t_, err := this.Project.DeleteBucketWithObjects(this.Context, bucket)\n\t\treturn err\n\t} else if strings.HasSuffix(path, \"/\") == false { // remove a file\n\t\t_, err := this.Project.DeleteObject(this.Context, bucket, prefix)\n\t\treturn err\n\t}\n\t// remove a directory\n\tobjects := this.Project.ListObjects(this.Context, bucket, &uplink.ListObjectsOptions{\n\t\tPrefix:    prefix,\n\t\tRecursive: true,\n\t})\n\n\tvar (\n\t\terr error\n\t\tmu  sync.Mutex\n\t)\n\tsem := semaphore.NewWeighted(CONCURRENCY_LEVEL)\n\tfor objects.Next() {\n\t\titem := objects.Item()\n\t\tif item.IsPrefix {\n\t\t\tcontinue\n\t\t}\n\t\tmu.Lock()\n\t\tif err != nil {\n\t\t\tmu.Unlock()\n\t\t\tbreak\n\t\t}\n\t\tmu.Unlock()\n\t\tsem.Acquire(this.Context, 1)\n\t\tgo func() {\n\t\t\t_, _err := this.Project.DeleteObject(this.Context, bucket, item.Key)\n\t\t\tif _err != nil {\n\t\t\t\tmu.Lock()\n\t\t\t\terr = _err\n\t\t\t\tmu.Unlock()\n\t\t\t}\n\t\t\tsem.Release(1)\n\t\t}()\n\t}\n\tsem.Acquire(this.Context, CONCURRENCY_LEVEL) // wait\n\treturn err\n}\n\nfunc (this Storj) Mv(from, to string) error { // TODO\n\tdefer this.Project.Close()\n\tfromBucket, fromPrefix := this.path(from)\n\ttoBucket, toPrefix := this.path(to)\n\n\tif strings.HasSuffix(from, \"/\") == false && strings.HasSuffix(to, \"/\") == false {\n\t\t// move single file\n\t\treturn this.Project.MoveObject(\n\t\t\tthis.Context,\n\t\t\tfromBucket, fromPrefix,\n\t\t\ttoBucket, toPrefix, nil,\n\t\t)\n\t} else if strings.HasSuffix(from, \"/\") && strings.HasSuffix(to, \"/\") {\n\t\t// move a directory\n\t\tobjects := this.Project.ListObjects(this.Context, fromBucket, &uplink.ListObjectsOptions{\n\t\t\tPrefix:    fromPrefix,\n\t\t\tRecursive: true,\n\t\t})\n\t\tvar (\n\t\t\terr error\n\t\t\tmu  sync.Mutex\n\t\t)\n\t\tsem := semaphore.NewWeighted(CONCURRENCY_LEVEL)\n\t\tfor objects.Next() {\n\t\t\titem := objects.Item()\n\t\t\tif item.IsPrefix {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tmu.Lock()\n\t\t\tif err != nil {\n\t\t\t\tmu.Unlock()\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tmu.Unlock()\n\t\t\tsem.Acquire(this.Context, 1)\n\t\t\tgo func() {\n\t\t\t\t_err := this.Project.MoveObject(\n\t\t\t\t\tthis.Context,\n\t\t\t\t\tfromBucket, item.Key,\n\t\t\t\t\ttoBucket, strings.Replace(item.Key, fromPrefix, toPrefix, 1),\n\t\t\t\t\tnil,\n\t\t\t\t)\n\t\t\t\tif _err != nil {\n\t\t\t\t\tmu.Lock()\n\t\t\t\t\terr = _err\n\t\t\t\t\tmu.Unlock()\n\t\t\t\t}\n\t\t\t\tsem.Release(1)\n\t\t\t}()\n\t\t}\n\t\tsem.Acquire(this.Context, CONCURRENCY_LEVEL) // wait\n\t\treturn err\n\t}\n\t// attempt to move a directory onto a file or some other weird combination\n\treturn ErrNotValid\n}\n\nfunc (this Storj) Save(path string, content io.Reader) error {\n\tdefer this.Project.Close()\n\tbucket, prefix := this.path(path)\n\tupload, err := this.Project.UploadObject(this.Context, bucket, prefix, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err = io.Copy(upload, content); err != nil {\n\t\t_ = upload.Abort()\n\t\treturn err\n\t}\n\t_ = upload.SetCustomMetadata(this.Context, map[string]string{\n\t\t\"creation_date\": time.Now().Format(time.RFC3339),\n\t\t\"created_by\":    \"Filestash\",\n\t})\n\treturn upload.Commit()\n}\n\nfunc (this Storj) Touch(path string) error {\n\treturn this.Save(path, strings.NewReader(\"\"))\n}\n\nfunc (this Storj) path(path string) (string, string) {\n\tpath = strings.TrimPrefix(path, \"/\")\n\tsp := strings.SplitN(path, \"/\", 2)\n\tif len(sp) != 2 {\n\t\treturn \"\", \"\"\n\t}\n\treturn sp[0], sp[1]\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_syncthing/README.md",
    "content": "```\n# Test instance:\ndocker run --name=syncthing-sync -d -e PUID=1000 -e PGID=1000 -p 8384:8384 -v ./syncthing/config:/var/syncthing -v ./syncthing/sync:/sync syncthing/syncthing\n```\n\nref: https://docs.syncthing.net/v1.0.0/rest/system-browse-get.html\n"
  },
  {
    "path": "server/plugin/plg_backend_syncthing/index.go",
    "content": "package plg_backend_syncthing\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tBackend.Register(\"syncthing\", &Syncthing{})\n}\n\ntype Syncthing struct {\n\tbaseURL  string\n\tapiKey   string\n\tclient   *http.Client\n\tsyncPath string\n\tfolders  []SyncFolder\n}\n\nfunc (s *Syncthing) Init(params map[string]string, app *App) (IBackend, error) {\n\turl := params[\"url\"]\n\tif url == \"\" {\n\t\turl = \"http://localhost:8384\"\n\t}\n\tbackend := &Syncthing{\n\t\tbaseURL:  url,\n\t\tapiKey:   params[\"api_key\"],\n\t\tsyncPath: params[\"sync_path\"],\n\t\tclient:   &http.Client{},\n\t}\n\tvar err error\n\tbackend.folders, err = backend.fetchFolders()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn backend, nil\n}\n\nfunc (s *Syncthing) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"syncthing\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"url\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Address\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"api_key\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"API Key\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"sync_path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Sync directory\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (s *Syncthing) Meta(path string) Metadata {\n\tif path == \"/\" || s.syncPath == \"\" {\n\t\treturn Metadata{\n\t\t\tCanCreateDirectory: NewBool(false),\n\t\t\tCanCreateFile:      NewBool(false),\n\t\t\tCanRename:          NewBool(false),\n\t\t\tCanMove:            NewBool(false),\n\t\t\tCanUpload:          NewBool(false),\n\t\t\tCanDelete:          NewBool(false),\n\t\t}\n\t}\n\treturn Metadata{}\n}\n\nfunc (s *Syncthing) Ls(path string) ([]os.FileInfo, error) {\n\tif path == \"/\" {\n\t\tvar fileInfos []os.FileInfo\n\t\tfor _, folder := range s.folders {\n\t\t\tfileInfos = append(fileInfos, &File{\n\t\t\t\tFName: folder.Name,\n\t\t\t\tFType: \"directory\",\n\t\t\t})\n\t\t}\n\t\treturn fileInfos, nil\n\t}\n\tif s.syncPath != \"\" {\n\t\tfullpath, err := s.resolvePath(path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tf, err := SafeOsOpenFile(fullpath, os.O_RDONLY, os.ModePerm)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfiles, err := f.Readdir(-1)\n\t\tif err != nil {\n\t\t\tf.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\treturn files, f.Close()\n\t}\n\treturn s.listDirectory(path)\n}\n\nfunc (s *Syncthing) Stat(path string) (os.FileInfo, error) {\n\tfullpath, err := s.resolvePath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tf, err := SafeOsOpenFile(fullpath, os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\treturn f.Stat()\n}\n\nfunc (s *Syncthing) Cat(path string) (io.ReadCloser, error) {\n\tfullpath, err := s.resolvePath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tf, err := SafeOsOpenFile(fullpath, os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfs, err := f.Stat()\n\tif err != nil {\n\t\tf.Close()\n\t\treturn nil, err\n\t}\n\tif fs.IsDir() {\n\t\tf.Close()\n\t\treturn nil, ErrNotFound\n\t}\n\treturn f, nil\n}\n\nfunc (s *Syncthing) Mkdir(path string) error {\n\tfullpath, err := s.resolvePath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn SafeOsMkdir(fullpath, 0755)\n}\n\nfunc (s *Syncthing) Rm(path string) error {\n\tfullpath, err := s.resolvePath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn SafeOsRemoveAll(fullpath)\n}\n\nfunc (s *Syncthing) Mv(from, to string) error {\n\tfromPath, err := s.resolvePath(from)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttoPath, err := s.resolvePath(to)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn SafeOsRename(fromPath, toPath)\n}\n\nfunc (s *Syncthing) Save(path string, content io.Reader) error {\n\tfullpath, err := s.resolvePath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf, err := SafeOsOpenFile(fullpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err = io.Copy(f, content); err != nil {\n\t\tf.Close()\n\t\treturn err\n\t}\n\treturn f.Close()\n}\n\nfunc (s *Syncthing) Touch(path string) error {\n\tfullpath, err := s.resolvePath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf, err := SafeOsOpenFile(fullpath, os.O_WRONLY|os.O_CREATE, os.ModePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn f.Close()\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_syncthing/index_helper.go",
    "content": "package plg_backend_syncthing\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\ntype SyncFolder struct {\n\tID       string\n\tName     string\n\tPath     string\n\tSyncPath string\n}\n\nfunc (s *Syncthing) makeAPICall(endpoint string, params map[string]string) ([]byte, error) {\n\tquery := url.Values{}\n\tfor k, v := range params {\n\t\tquery.Set(k, v)\n\t}\n\tapiURL := fmt.Sprintf(\"%s%s\", s.baseURL, endpoint)\n\tif len(query) > 0 {\n\t\tapiURL += \"?\" + query.Encode()\n\t}\n\n\treq, err := http.NewRequest(\"GET\", apiURL, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"X-API-Key\", s.apiKey)\n\tresp, err := s.client.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"API returned status %d: %s\", resp.StatusCode, string(body))\n\t}\n\treturn body, nil\n}\n\nfunc (s *Syncthing) fetchFolders() ([]SyncFolder, error) {\n\tdata, err := s.makeAPICall(\"/rest/config\", nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar config struct {\n\t\tFolders []struct {\n\t\t\tID    string `json:\"id\"`\n\t\t\tLabel string `json:\"label\"`\n\t\t\tPath  string `json:\"path\"`\n\t\t} `json:\"folders\"`\n\t}\n\tif err := json.Unmarshal(data, &config); err != nil {\n\t\treturn nil, err\n\t}\n\tfolders := make([]SyncFolder, 0, len(config.Folders))\n\tfor _, folder := range config.Folders {\n\t\tname := folder.Label\n\t\tif name == \"\" {\n\t\t\tname = folder.ID\n\t\t}\n\t\tfolders = append(folders, SyncFolder{\n\t\t\tID:   folder.ID,\n\t\t\tName: name,\n\t\t\tPath: folder.Path,\n\t\t})\n\t}\n\treturn folders, nil\n}\n\nfunc (s *Syncthing) listDirectory(path string) ([]os.FileInfo, error) {\n\tparts := strings.Split(strings.Trim(path, \"/\"), \"/\")\n\tif len(parts) == 0 {\n\t\treturn nil, ErrNotValid\n\t}\n\tfolderName := parts[0]\n\tprefix := \"\"\n\tif len(parts) > 1 {\n\t\tprefix = strings.Join(parts[1:], \"/\")\n\t}\n\tvar folderID string\n\tfor _, folder := range s.folders {\n\t\tif folder.Name == folderName {\n\t\t\tfolderID = folder.ID\n\t\t\tbreak\n\t\t}\n\t}\n\tif folderID == \"\" {\n\t\treturn nil, ErrNotFound\n\t}\n\tdata, err := s.makeAPICall(\"/rest/db/browse\", map[string]string{\n\t\t\"folder\": folderID,\n\t\t\"prefix\": prefix,\n\t\t\"levels\": \"1\",\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar result []struct {\n\t\tName     string `json:\"name\"`\n\t\tType     string `json:\"type\"`\n\t\tSize     int64  `json:\"size\"`\n\t\tModified string `json:\"modTime\"`\n\t}\n\tif err := json.Unmarshal(data, &result); err != nil {\n\t\treturn nil, err\n\t}\n\tvar fileInfos []os.FileInfo\n\tfor _, item := range result {\n\t\tmodTime := time.Time{}\n\t\tif item.Modified != \"\" {\n\t\t\tif t, err := time.Parse(time.RFC3339Nano, item.Modified); err == nil {\n\t\t\t\tmodTime = t\n\t\t\t}\n\t\t}\n\t\tif item.Type == \"FILE_INFO_TYPE_DIRECTORY\" {\n\t\t\tfileInfos = append(fileInfos, &File{\n\t\t\t\tFName: item.Name,\n\t\t\t\tFType: \"directory\",\n\t\t\t\tFTime: modTime.Unix(),\n\t\t\t})\n\t\t} else if item.Type == \"FILE_INFO_TYPE_FILE\" {\n\t\t\tisOffline := s.syncPath == \"\"\n\t\t\tfileInfos = append(fileInfos, &File{\n\t\t\t\tFName:   item.Name,\n\t\t\t\tFType:   \"file\",\n\t\t\t\tFSize:   item.Size,\n\t\t\t\tFTime:   modTime.Unix(),\n\t\t\t\tOffline: isOffline,\n\t\t\t})\n\t\t} else {\n\t\t\tLog.Error(\"plg_backend_syncthing::ls error=unknownItemType type=%s\", item.Type)\n\t\t}\n\t}\n\treturn fileInfos, nil\n}\n\nfunc (s *Syncthing) resolvePath(path string) (string, error) {\n\tparts := strings.Split(strings.Trim(path, \"/\"), \"/\")\n\tif len(parts) == 0 {\n\t\treturn \"\", ErrNotValid\n\t}\n\tfolderName := parts[0]\n\trelativePath := \"\"\n\tif len(parts) > 1 {\n\t\trelativePath = strings.Join(parts[1:], \"/\")\n\t}\n\tfor _, folder := range s.folders {\n\t\tif folder.Name == folderName {\n\t\t\treturn filepath.Join(s.syncPath, folder.Path, relativePath), nil\n\t\t}\n\t}\n\treturn \"\", ErrNotFound\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_tmp/index.go",
    "content": "package plg_backend_tmp\n\nimport (\n\t\"encoding/base64\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nconst (\n\tFILESTASH_DIRECTORY = \"/tmp/filestash_tmp/\"\n\tEMPTY_DOCX          = \"UEsDBBQACAgIAE80OVQAAAAAAAAAAAAAAAALAAAAX3JlbHMvLnJlbHOtkk1LA0EMhu/9FUPu3WwriMjO9iJCbyL1B4SZ7O7Qzgczaa3/3kEKulCKoMe8efPwHNJtzv6gTpyLi0HDqmlBcTDRujBqeNs9Lx9g0y+6Vz6Q1EqZXCqq3oSiYRJJj4jFTOypNDFxqJshZk9SxzxiIrOnkXHdtveYfzKgnzHV1mrIW7sCtftI/Dc2ehayJIQmZl6mXK+zOC4VTnlk0WCjealx+Wo0lQx4XWj9e6E4DM7wUzRHz0GuefFZOFi2t5UopVtGd/9pNG98y7zHbNFe4ovNosPZG/SfUEsHCOjQASPZAAAAPQIAAFBLAwQUAAgICABPNDlUAAAAAAAAAAAAAAAAEQAAAGRvY1Byb3BzL2NvcmUueG1sbVLJTsMwEL3zFZHviZ0UKIqSVCzqiUpIFIG4GXuaGhLHsqdN+/c4SZuy9DZv8Zuxx9lsV1fBFqxTjc5JHDESgBaNVLrMyctyHt6QwCHXkleNhpzswZFZcZEJk4rGwpNtDFhU4AIfpF0qTE7WiCal1Ik11NxF3qG9uGpszdFDW1LDxRcvgSaMXdMakEuOnHaBoRkTySFSijHSbGzVB0hBoYIaNDoaRzE9eRFs7c4e6JUfzlrh3sBZ61Ec3TunRmPbtlE76a1+/pi+LR6f+6uGSndPJYAU2WGQVFjgCDLwAenQ7qi8Tu4flnNSJCxJQhaHydUynqaTy5RN3zP653wXONSNLTr1BHwtwQmrDPodDuIvwuOK63LjH7wAHd6+9JaR6lZZcYcLv/SVAnm39xlnOE9Z2KruoxSsd4ywa+E2H58gcOg/Al+jwgoG+lj++zzFN1BLBwgfJ++WUQEAAIgCAABQSwMEFAAICAgATzQ5VAAAAAAAAAAAAAAAABAAAABkb2NQcm9wcy9hcHAueG1snZHNbsIwEITvfYrI4kqcIEoRcoz6o56QitQUekOuvSSuEtuyFwRvX4eoEPVYn3ZmR9+ubbY8tU1yBB+0NQXJ04wkYKRV2lQF+Shfx3OSBBRGicYaKMgZAlnyO7b21oFHDSGJBBMKUiO6BaVB1tCKkMa2iZ299a3AKH1F7X6vJbxYeWjBIJ1k2YzCCcEoUGN3BZKeuDjif6HKym6/sCnPLvI4K6F1jUDgjN7K0qJoSt0Cz6J9FezRuUZLgfFF+Ep/eXi7jKAPaZ5O08lopc3htPucz3azaTII7OIVvkEizbPR00E3ajxhdAjryJv+qXl+n2bxXAK/HluLCgLPGe0LtrVehW67vmDPtfBCYox35kANOluN9bsTEv5kBn6c40XlhasvmYGK4voN/AdQSwcIXlesNisBAAAcAgAAUEsDBBQACAgIAE80OVQAAAAAAAAAAAAAAAAcAAAAd29yZC9fcmVscy9kb2N1bWVudC54bWwucmVsc62RTQrCMBCF954izN6mVRCRpm5EcCv1ADGdtsE2CckoensDiloo4sLl/H3vMS9fX/uOXdAHbY2ALEmBoVG20qYRcCi30yWsi0m+x05SXAmtdoHFGxMEtERuxXlQLfYyJNahiZPa+l5SLH3DnVQn2SCfpemC+08GFAMm21UC/K7KgJU3h7+wbV1rhRurzj0aGpHggW4dhkiUvkES8KiTyAE+Lj/7p3xtDZXy2OHbwav1zcT8rz9Aopjl5xeenaeFSc4H4RZ3UEsHCPkvMMDFAAAAEwIAAFBLAwQUAAgICABPNDlUAAAAAAAAAAAAAAAAEQAAAHdvcmQvZG9jdW1lbnQueG1spZRNbtswEIX3PYXAvS2pCQJXiJyN0aKLBgbsHoCiKIktySGGlBX19CX1m6RFYCQb05w3/GbeSNT9w5OS0YWjFaBzkm4TEnHNoBS6zsnP89fNjkTWUV1SCZrnpOeWPOw/3XdZCaxVXLvIE7TNICct6syyhitqN0owBAuV2zBQGVSVYHxayHQCc9I4Z7I4ng5twXDttQpQUee3WMfjkcNUK/6cJHcxckmd79c2wtiZdnmr/kXJOa+7pmoHWBoExq31g1ByrKuo0AsmTa4wHDjLCXNN5RJp96zky0YOo7gS7T/IpY2tb2Oa3kDxvDR5xTs11PCVVn+M9g2hNTNNsWvcKoq/WxMmZvwTLYQUrh+Mr02ltx/r6vXM3scL749i2fdaA9JC+ovgQVHojuz9XSig7MNqhp8jDsvJ9ZJHXXahMiePwbUk8ZAtSjHHkzH0i80BySs3xjBw4nWduPg/bcoIkuXMjZmuN0t9zZ/ckdZ8RJv69Mcr/i6k6Zcw7S5r/P+73c1uTvhB0UdDNyHp5jbkoKibZ9uG05Jj8OA3DsyqVABuUQpwDtQq1q2bxKnUY6vOY6uV8viSM7HMKrwtRwQ3+6iotJMJ5y0dBHq7/luwjA/PRZDjdRDx/Hzi9aO1/wtQSwcIwYDsx98BAAD5BAAAUEsDBBQACAgIAE80OVQAAAAAAAAAAAAAAAAPAAAAd29yZC9zdHlsZXMueG1sxVTbbuIwEH3fr4j8TkNR1a1Q04plhYrEslUvH2CcCbHq29pOKf36HZukpSRsbyv1BeIz8vjMOUdzev4gRXIP1nGtMnJ40CcJKKZzrpYZub2Z9E5I4jxVORVaQUbW4Mj52bfT1dD5tQCX4H3lhquMlN6bYZo6VoKk7kAbUFgrtJXU49Eu05W2ubGagXPYXop00O8fp5JyRZo2h0etRpIzq50u/AHTMtVFwRnEVnj9sB+/pGgaSPYWIpLau8r0sJ+hni+44H4dyZBEsuF0qbSlC4HTIh9yhrPmmv2EglbCu3C0l7Y+1qf4N9HKu2Q1pI5xnpEZX4DF9lol12B5QbBUjpTbUwLq/MhxmpHRVXI5S25/oUbJeB5qzGVkYgGuqXIkDY/dgVVYuKciI4MN5B6fgKMGGbtdTFDsWmOgeqPbl28/lr3NkwueI9GS96bzcDGtx0x3hze7p/C34rlejVEOq8WGSWWMRdtHldcXa1OCeiLmbQX1C6Z+Ybtn2hI/5g5v+7VBhwy1dGmpKQPpWJrmGZkHs0W0TlEJzVs1HCn9mcRApP+iHURo7va3SX6N6UwLbRs+FKX88ixEwd9qygXQsFZarjT4RnLqIP+tuhxT8OAb/Aa/f+h8vdfLOwAz37rQxAz5GMp4HHwBuBQg6NEPRGnhweIOHLzf6mBRt9N15X1Gb9l30mHfyWdceFJu14YAJqH6qhG1Ls9CCq7gqgoLM6ayRpDp92OypfMLlY+6VP7oUDPufGugCHbN8jI8W1uny+1ddz5KcUxNSESLZYO/JnpHxputOkOx55XE0Lk9CQ+ZfkfC24nkm9+xe/N6+ahOU5XDQ0ulDfrfNPqM3c2XO/sLUEsHCKSuMVSCAgAAPQkAAFBLAwQUAAgICABPNDlUAAAAAAAAAAAAAAAAEgAAAHdvcmQvZm9udFRhYmxlLnhtbK1QQU7DMBC88wrLd+q0B4SiphUS4oR6oOUBW2fTWLLXkdck9Pe4TishyKGg3uyd2ZnZWa4/nRU9BjaeKjmfFVIgaV8bOlTyffdy/ygFR6AarCes5BFZrld3y6FsPEUWaZ24HCrZxtiVSrFu0QHPfIeUsMYHBzF9w0ENPtRd8BqZk7qzalEUD8qBIXmWCdfI+KYxGp+9/nBIcRQJaCGmC7g1HcvVOZ0YSgKXQu+MQxYbHMSbd0CZoFsIjCdOD7aSRSFV3gNn7PEyDZmegc5E3V7mPQQDe4snSI1mv0y3R7f3dtJrcWuvp0SZtpo8iwfD/E+rV7PHkMsWWwymya5g4yahF52ffaupZPNbl/A9GRBPBRt7uj7On4o6P3j1BVBLBwjBx9kIHQEAAFUDAABQSwMEFAAICAgATzQ5VAAAAAAAAAAAAAAAABEAAAB3b3JkL3NldHRpbmdzLnhtbGWQPW7DMAyF957C0N5ICdA/I3a2okunpAdgZDoWIImCRMd1T1+mRuChG8XvkY9P+8N38NUVc3EUG7XdGFVhtNS5eGnU1+n98VVVhSF24Clio2Ys6tA+7Ke6ILOoSiUbYqmnRg3Mqda62AEDlA0ljMJ6ygFYnvmiJ8pdymSxFBkNXu+MedYBXFStrPwhCtVUJ8wWI8s5xih9Ax32MHo+wfnIlERyBd+oF/O2YBiZPuY0YASWHHfOecRFYCkk4LU6LreLMEKQVEvXnZ13PH9Sh0rQmN2/TMHZTIV63siIpr53Fv9Sqbvp9ulmqVdPvX5V+wtQSwcInYQHjPEAAABvAQAAUEsDBBQACAgIAE80OVQAAAAAAAAAAAAAAAATAAAAW0NvbnRlbnRfVHlwZXNdLnhtbL2Uy07DMBBF9/2KyFuUuLBACCXpgscSughrZOxJaogfst3S/j3jNKpQFZoChWU8c++ZuU6Sz9aqTVbgvDS6IOfZlCSguRFSNwV5qu7TKzIrJ3m1seAT7NW+IIsQ7DWlni9AMZ8ZCxortXGKBXx0DbWMv7EG6MV0ekm50QF0SEP0IGV+CzVbtiG5W+Pxlotyktxs+yKqIMzaVnIWsExjlQ7qHLT+gHClxd50aT9Zhsquxy+k9WdfE6xu9gBSxc3i+bDi1cKwpCug5hHjdlJAMmcuPDCFDfQ5bkKzE+8zRBKGz52xHq/FQXY4+AO8qE4tGoELEo4jovX3gaauJQf0WCqUZBCDFiCOZL8bJ/pwdxbY/h9Bd+jP0F/tHd1wZQ7e46eJG+wqikk9OocPmxb86afY+o7ia0RW7KX9wQs3NsHOejwDCAE1f5FC79yPMMlp978sPwBQSwcIC9URx1QBAABeBQAAUEsBAhQAFAAICAgATzQ5VOjQASPZAAAAPQIAAAsAAAAAAAAAAAAAAAAAAAAAAF9yZWxzLy5yZWxzUEsBAhQAFAAICAgATzQ5VB8n75ZRAQAAiAIAABEAAAAAAAAAAAAAAAAAEgEAAGRvY1Byb3BzL2NvcmUueG1sUEsBAhQAFAAICAgATzQ5VF5XrDYrAQAAHAIAABAAAAAAAAAAAAAAAAAAogIAAGRvY1Byb3BzL2FwcC54bWxQSwECFAAUAAgICABPNDlU+S8wwMUAAAATAgAAHAAAAAAAAAAAAAAAAAALBAAAd29yZC9fcmVscy9kb2N1bWVudC54bWwucmVsc1BLAQIUABQACAgIAE80OVTBgOzH3wEAAPkEAAARAAAAAAAAAAAAAAAAABoFAAB3b3JkL2RvY3VtZW50LnhtbFBLAQIUABQACAgIAE80OVSkrjFUggIAAD0JAAAPAAAAAAAAAAAAAAAAADgHAAB3b3JkL3N0eWxlcy54bWxQSwECFAAUAAgICABPNDlUwcfZCB0BAABVAwAAEgAAAAAAAAAAAAAAAAD3CQAAd29yZC9mb250VGFibGUueG1sUEsBAhQAFAAICAgATzQ5VJ2EB4zxAAAAbwEAABEAAAAAAAAAAAAAAAAAVAsAAHdvcmQvc2V0dGluZ3MueG1sUEsBAhQAFAAICAgATzQ5VAvVEcdUAQAAXgUAABMAAAAAAAAAAAAAAAAAhAwAAFtDb250ZW50X1R5cGVzXS54bWxQSwUGAAAAAAkACQA8AgAAGQ4AAAAA\"\n)\n\nvar ChrootCache AppCache\n\nfunc init() {\n\tBackend.Register(\"tmp\", TmpStorage{})\n\tChrootCache = NewAppCache(60 * 24 * 30)\n\tChrootCache.OnEvict(func(key string, value interface{}) {\n\t\tchroot := value.(string)\n\t\tif strings.HasPrefix(chroot, FILESTASH_DIRECTORY) {\n\t\t\tos.RemoveAll(chroot)\n\t\t}\n\t})\n\tos.RemoveAll(FILESTASH_DIRECTORY)\n}\n\ntype TmpStorage struct {\n\tuserID string\n}\n\nfunc (this TmpStorage) Init(params map[string]string, app *App) (IBackend, error) {\n\tif len(params[\"userID\"]) == 0 {\n\t\treturn nil, ErrAuthenticationFailed\n\t} else if regexp.MustCompile(`^[a-zA-Z0-9]*$`).MatchString(params[\"userID\"]) == false {\n\t\treturn nil, ErrAuthenticationFailed\n\t}\n\tthis.userID = params[\"userID\"]\n\troot, err := this.fullpath(\"/\")\n\tif err != nil {\n\t\treturn nil, ErrAuthenticationFailed\n\t}\n\tif c := ChrootCache.Get(params); c == nil {\n\t\tChrootCache.Set(params, root)\n\t}\n\tos.MkdirAll(root, 0755)\n\treturn &this, nil\n}\n\nfunc (this TmpStorage) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"tmp\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"userID\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"user ID\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this TmpStorage) Ls(path string) ([]os.FileInfo, error) {\n\tpath, err := this.fullpath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tf, err := SafeOsOpenFile(path, os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfiles, err := f.Readdir(-1)\n\tif err != nil {\n\t\tf.Close()\n\t\treturn nil, err\n\t}\n\treturn files, f.Close()\n}\n\nfunc (this TmpStorage) Stat(path string) (os.FileInfo, error) {\n\tpath, err := this.fullpath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tf, err := SafeOsOpenFile(path, os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfinfo, err := f.Stat()\n\tif err != nil {\n\t\tf.Close()\n\t\treturn nil, err\n\t}\n\treturn finfo, f.Close()\n}\n\nfunc (this TmpStorage) Cat(path string) (io.ReadCloser, error) {\n\tpath, err := this.fullpath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treader, err := SafeOsOpenFile(path, os.O_RDONLY, os.ModePerm)\n\tif err == nil {\n\t\treturn reader, nil\n\t} else if os.IsExist(err) == false {\n\t\tif strings.HasSuffix(path, \".doc\") || strings.HasSuffix(path, \".docx\") {\n\t\t\tdocx, err := base64.StdEncoding.DecodeString(EMPTY_DOCX)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn NewReadCloserFromBytes(docx), nil\n\t\t}\n\t\treturn NewReadCloserFromBytes([]byte(\"\")), nil\n\t}\n\treturn reader, err\n}\n\nfunc (this TmpStorage) Mkdir(path string) error {\n\tpath, err := this.fullpath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn SafeOsMkdir(path, 0755)\n}\n\nfunc (this TmpStorage) Rm(path string) error {\n\tpath, err := this.fullpath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn SafeOsRemoveAll(path)\n}\n\nfunc (this TmpStorage) Mv(from, to string) error {\n\tfrom, err := this.fullpath(from)\n\tif err != nil {\n\t\treturn err\n\t}\n\tto, err = this.fullpath(to)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn SafeOsRename(from, to)\n}\n\nfunc (this TmpStorage) Save(path string, content io.Reader) error {\n\tpath, err := this.fullpath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf, err := SafeOsOpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err = io.Copy(f, content); err != nil {\n\t\tf.Close()\n\t\treturn nil\n\t}\n\treturn f.Close()\n}\n\nfunc (this TmpStorage) Touch(path string) error {\n\tpath, err := this.fullpath(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf, err := SafeOsOpenFile(path, os.O_WRONLY|os.O_CREATE, os.ModePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err = f.Write([]byte(\"\")); err != nil {\n\t\tf.Close()\n\t\treturn err\n\t}\n\treturn f.Close()\n}\n\nfunc (this TmpStorage) fullpath(path string) (string, error) {\n\tpath = filepath.Join(FILESTASH_DIRECTORY, this.userID, path)\n\tif strings.HasPrefix(path, FILESTASH_DIRECTORY) == false {\n\t\tLog.Warning(\"plg_backend_tmp::chroot attempt to circumvent chroot via path[%s]\", path)\n\t\treturn \"\", ErrPermissionDenied\n\t}\n\treturn path, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_url/index.go",
    "content": "package plg_backend_url\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"golang.org/x/net/html\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tBackend.Register(\"url\", &Url{})\n}\n\ntype Url struct {\n\troot url.URL\n\thome string\n\tctx  context.Context\n}\n\nfunc (this Url) Meta(path string) Metadata {\n\treturn Metadata{\n\t\tCanCreateFile:      NewBool(false),\n\t\tCanCreateDirectory: NewBool(false),\n\t\tCanRename:          NewBool(false),\n\t\tCanMove:            NewBool(false),\n\t\tCanUpload:          NewBool(false),\n\t\tCanDelete:          NewBool(false),\n\t}\n}\n\nfunc (this Url) Init(params map[string]string, app *App) (IBackend, error) {\n\tu, err := url.Parse(params[\"url\"])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\thome := u.Path\n\tu.Path = \"/\"\n\treturn &Url{*u, home, app.Context}, nil\n}\n\nfunc (this *Url) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\t{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"url\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:        \"url\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"base URL\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this *Url) Ls(path string) ([]os.FileInfo, error) {\n\tthis.root.Path = path\n\tif strings.HasSuffix(this.root.Path, \"/\") == false {\n\t\tthis.root.Path += \"/\"\n\t}\n\tresp, err := request(this.ctx, http.MethodGet, this.root.String(), \"\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\tif resp.StatusCode == http.StatusNotFound {\n\t\t\treturn nil, ErrNotFound\n\t\t} else if resp.StatusCode == http.StatusForbidden {\n\t\t\treturn nil, ErrNotAllowed\n\t\t}\n\t\treturn nil, fmt.Errorf(\"HTTP Error %d\", resp.StatusCode)\n\t}\n\tdoc, err := html.Parse(resp.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar links []os.FileInfo\n\tvar crawler func(*html.Node)\n\tcrawler = func(node *html.Node) {\n\t\tif node.Type == html.ElementNode && slices.Contains([]string{\"a\", \"img\", \"object\", \"iframe\"}, strings.ToLower(node.Data)) {\n\t\t\tfor _, attr := range node.Attr {\n\t\t\t\tlink := \"\"\n\t\t\t\tif strings.ToLower(attr.Key) == \"href\" {\n\t\t\t\t\tlink = attr.Val\n\t\t\t\t}\n\t\t\t\tif link == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif f := this.processLink(attr.Val, node); f != nil {\n\t\t\t\t\tinsertPos := -1\n\t\t\t\t\tfor i := 0; i < len(links); i++ {\n\t\t\t\t\t\tif links[i].Name() == f.Name() {\n\t\t\t\t\t\t\tinsertPos = i\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif insertPos < 0 {\n\t\t\t\t\t\tlinks = append(links, f)\n\t\t\t\t\t} else if links[insertPos].(*File).FTime == 0 {\n\t\t\t\t\t\tlinks[insertPos] = f\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tfor child := node.FirstChild; child != nil; child = child.NextSibling {\n\t\t\tcrawler(child)\n\t\t}\n\t}\n\tcrawler(doc)\n\treturn links, nil\n}\n\nfunc (this *Url) Stat(path string) (os.FileInfo, error) {\n\tthis.root.Path = path\n\tresp, err := request(this.ctx, http.MethodHead, this.root.String(), \"\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\tif resp.StatusCode == http.StatusNotFound {\n\t\t\treturn nil, ErrNotFound\n\t\t} else if resp.StatusCode == http.StatusForbidden {\n\t\t\treturn nil, ErrNotAllowed\n\t\t}\n\t\treturn nil, fmt.Errorf(\"HTTP Error %d\", resp.StatusCode)\n\t}\n\tfinfo := &File{\n\t\tFName: filepath.Base(path),\n\t\tFType: \"file\",\n\t\tFSize: -1,\n\t\tFTime: -1,\n\t}\n\tif cl := resp.Header.Get(\"Content-Length\"); cl != \"\" {\n\t\tif s, err := strconv.ParseInt(cl, 10, 64); err == nil {\n\t\t\tfinfo.FSize = s\n\t\t}\n\t}\n\tif lm := resp.Header.Get(\"Last-Modified\"); lm != \"\" {\n\t\tif t, err := time.Parse(time.RFC1123, lm); err == nil {\n\t\t\tfinfo.FTime = t.Unix()\n\t\t}\n\t}\n\tif strings.HasSuffix(path, \"/\") {\n\t\tfinfo.FType = \"directory\"\n\t}\n\treturn finfo, nil\n}\n\nfunc (this Url) processLink(link string, n *html.Node) *File {\n\tu, err := url.Parse(link)\n\tif err != nil {\n\t\treturn nil\n\t} else if u.Host != \"\" && u.Host != this.root.Host {\n\t\treturn nil\n\t} else if u.Path == \"\" {\n\t\treturn nil\n\t}\n\tfType := \"file\"\n\tfullpath := this.JoinPath(u)\n\tif !strings.HasPrefix(fullpath, this.root.Path) {\n\t\treturn nil\n\t}\n\tfName := strings.TrimPrefix(fullpath, this.root.Path)\n\tvar fSize int64 = -1\n\tvar fTime int64 = 0\n\tif strings.HasSuffix(fName, \"/\") {\n\t\tfType = \"directory\"\n\t\tfName = strings.Trim(fName, \"/\")\n\t}\n\tif fName == \"\" || fName == \".\" || fName == \"/\" {\n\t\treturn nil\n\t}\n\tfor _, extr := range []func(node *html.Node) (int64, int64, error){\n\t\textractNginxList,\n\t\textractASPNetList,\n\t\textractApacheList,\n\t\textractApacheList2,\n\t\textractApacheList3,\n\t} {\n\t\tif s, t, err := extr(n); err == nil {\n\t\t\tfSize = s\n\t\t\tfTime = t\n\t\t\tbreak\n\t\t}\n\t}\n\tf := &File{\n\t\tFName: fName,\n\t\tFType: fType,\n\t\tFTime: fTime,\n\t\tFSize: fSize,\n\t}\n\treturn f\n}\n\nfunc extract(reg *regexp.Regexp, layout string, toText func(n *html.Node) string) func(node *html.Node) (int64, int64, error) {\n\treturn func(node *html.Node) (int64, int64, error) {\n\t\tnodeData := toText(node)\n\t\tif nodeData == \"\" {\n\t\t\treturn -1, -1, ErrNotFound\n\t\t}\n\t\tmatch := reg.FindStringSubmatch(nodeData)\n\t\tif len(match) != 3 {\n\t\t\treturn -1, -1, ErrNotFound\n\t\t}\n\t\tsizeStr := strings.ToUpper(match[2])\n\t\tvar m int64 = 1\n\t\tif strings.HasSuffix(sizeStr, \"K\") {\n\t\t\tsizeStr = strings.TrimSuffix(sizeStr, \"K\")\n\t\t\tm = 1024\n\t\t}\n\t\tif strings.HasSuffix(sizeStr, \"M\") {\n\t\t\tsizeStr = strings.TrimSuffix(sizeStr, \"M\")\n\t\t\tm = 1024 * 1024\n\t\t}\n\t\tif strings.HasSuffix(sizeStr, \"G\") {\n\t\t\tsizeStr = strings.TrimSuffix(sizeStr, \"G\")\n\t\t\tm = 1024 * 1024 * 1024\n\t\t}\n\t\tif strings.HasSuffix(sizeStr, \"T\") {\n\t\t\tsizeStr = strings.TrimSuffix(sizeStr, \"T\")\n\t\t\tm = 1024 * 1024 * 1024 * 1024\n\t\t}\n\t\ts, err := strconv.ParseFloat(strings.TrimSpace(sizeStr), 64)\n\t\tif err != nil {\n\t\t\ts = 0\n\t\t}\n\t\tsize := int64(s*1000) * m / 1000\n\t\tt, err := time.Parse(layout, match[1])\n\t\tif err != nil {\n\t\t\treturn -1, -1, ErrNotFound\n\t\t}\n\t\treturn size, t.Unix(), nil\n\t}\n}\n\nfunc request(ctx context.Context, method string, url string, rangeHeader string) (*http.Response, error) {\n\tr, err := http.NewRequestWithContext(ctx, method, url, nil)\n\tif rangeHeader != \"\" {\n\t\tr.Header.Set(\"Range\", rangeHeader)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn (&http.Client{\n\t\tTimeout: 5 * time.Hour,\n\t\tTransport: NewTransformedTransport(&http.Transport{\n\t\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true},\n\t\t\tDial: (&net.Dialer{\n\t\t\t\tTimeout:   10 * time.Second,\n\t\t\t\tKeepAlive: 10 * time.Second,\n\t\t\t}).Dial,\n\t\t\tTLSHandshakeTimeout:   5 * time.Second,\n\t\t\tIdleConnTimeout:       60 * time.Second,\n\t\t\tResponseHeaderTimeout: 60 * time.Second,\n\t\t}),\n\t}).Do(r)\n}\n\nvar extractASPNetList = extract(\n\tregexp.MustCompile(`^\\s*([0-9]{1,2}\\/[0-9]{1,2}\\/[0-9]{4}\\s+[0-9]{1,2}:[0-9]{1,2} [AM|PM]{2})\\s+([0-9]+|<dir>)\\s*$`),\n\t\"1/2/2006 3:4 PM\",\n\tfunc(n *html.Node) string {\n\t\tif n.PrevSibling == nil {\n\t\t\treturn \"\"\n\t\t} else if n.Parent == nil {\n\t\t\treturn \"\"\n\t\t} else if n.Parent.Type == 3 && n.Parent.Data != \"pre\" {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn n.PrevSibling.Data\n\t},\n)\n\nvar extractNginxList = extract(\n\tregexp.MustCompile(`\\s*([0-9]{2}-[A-Z][a-z]{2}-[0-9]{4} [0-9]{2}:[0-9]{2})\\s+([0-9\\-]+[KMGT]?)`),\n\t\"_2-Jan-2006 15:04\",\n\tfunc(n *html.Node) string {\n\t\tif n.NextSibling == nil {\n\t\t\treturn \"\"\n\t\t} else if len(n.NextSibling.Attr) > 0 {\n\t\t\treturn \"\"\n\t\t} else if n.Parent == nil {\n\t\t\treturn \"\"\n\t\t} else if n.Parent.Type == 3 && n.Parent.Data != \"pre\" {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn n.NextSibling.Data\n\t},\n)\n\nvar nodeApacheExtract = func(n *html.Node) (msg string) {\n\tif n.Parent == nil {\n\t\treturn \"\"\n\t}\n\tdefer func() {\n\t\tif msg != \"\" {\n\t\t\treturn\n\t\t}\n\t\tif n.NextSibling != nil {\n\t\t\tmsg = n.NextSibling.Data\n\t\t}\n\t}()\n\tif n.Parent.NextSibling == nil || n.Parent.NextSibling.FirstChild == nil {\n\t\treturn msg\n\t} else if n.Parent.NextSibling.NextSibling == nil || n.Parent.NextSibling.NextSibling.FirstChild == nil {\n\t\treturn msg\n\t}\n\tcol0 := n.Parent.NextSibling.FirstChild.Data\n\tcol1 := n.Parent.NextSibling.NextSibling.FirstChild.Data\n\tmsg = col0 + col1\n\tif n.Parent.NextSibling.NextSibling.NextSibling != nil && n.Parent.NextSibling.NextSibling.NextSibling.FirstChild != nil {\n\t\tmsg += n.Parent.NextSibling.NextSibling.NextSibling.FirstChild.Data\n\t}\n\tmsg += \" \" + col0\n\treturn msg\n}\n\nvar extractApacheList = extract(\n\tregexp.MustCompile(`([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2})\\s+([0-9\\.\\-]+\\s?[kKMGT]?)`),\n\t\"2006-01-02 15:04\",\n\tnodeApacheExtract,\n)\n\nvar extractApacheList2 = extract(\n\tregexp.MustCompile(`([0-9]{2}-[A-Z][a-z]{2}-[0-9]{4} [0-9]{2}:[0-9]{2})\\s+([0-9\\.\\-]+\\s?[kKMGT]?)`),\n\t\"02-Jan-2006 15:04\",\n\tnodeApacheExtract,\n)\n\nvar extractApacheList3 = extract(\n\tregexp.MustCompile(`([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2})`+`[ \\xA0]+`+`([0-9]+(?:\\.[0-9]+)?|-)`+`[ \\xA0]*`+`[kKMGT]?`),\n\t\"2006-01-02 15:04\",\n\tnodeApacheExtract,\n)\n\nfunc (this *Url) Home() (string, error) {\n\treturn this.home, nil\n}\n\nfunc (this *Url) Cat(path string) (io.ReadCloser, error) {\n\tu := this.root\n\tu.Path = filepath.Join(u.Path, path)\n\turl := u.String()\n\n\tresp, err := request(this.ctx, http.MethodHead, url, \"\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresp.Body.Close()\n\tvar r int64 = -1\n\tif resp.Header.Get(\"Accept-Ranges\") == \"bytes\" {\n\t\tr, err = strconv.ParseInt(resp.Header.Get(\"Content-Length\"), 10, 64)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn &urlFilecat{\n\t\toffset:        0,\n\t\turl:           url,\n\t\tctx:           this.ctx,\n\t\tcontentLength: r,\n\t\treader:        nil,\n\t}, nil\n}\n\ntype urlFilecat struct {\n\toffset        int64\n\turl           string\n\tctx           context.Context\n\tcontentLength int64\n\treader        io.ReadCloser\n\tmu            sync.Mutex\n}\n\nfunc (this *urlFilecat) Read(p []byte) (n int, err error) {\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\tif this.reader == nil {\n\t\trangeHeader := \"\"\n\t\tstatusOK := http.StatusOK\n\t\tif this.contentLength > 0 {\n\t\t\trangeHeader = fmt.Sprintf(\"bytes=%d-%d\", this.offset, this.contentLength-1)\n\t\t\tstatusOK = http.StatusPartialContent\n\t\t}\n\t\tresp, err := request(this.ctx, http.MethodGet, this.url, rangeHeader)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif resp.StatusCode != statusOK {\n\t\t\tresp.Body.Close()\n\t\t\treturn -1, ErrNotFound\n\t\t}\n\t\tthis.reader = resp.Body\n\t}\n\tn, err = this.reader.Read(p)\n\treturn n, err\n}\n\nfunc (this *urlFilecat) Close() error {\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\tif this.reader == nil {\n\t\treturn nil\n\t}\n\treturn this.reader.Close()\n}\n\nfunc (this *urlFilecat) Seek(offset int64, whence int) (int64, error) {\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\tif offset < 0 {\n\t\treturn this.offset, os.ErrInvalid\n\t}\n\n\tswitch whence {\n\tcase io.SeekStart:\n\tcase io.SeekCurrent:\n\t\toffset += this.offset\n\tcase io.SeekEnd:\n\t\toffset = this.contentLength\n\tdefault:\n\t\treturn this.offset, ErrNotImplemented\n\t}\n\n\tthis.offset = offset\n\treturn this.offset, nil\n}\n\nfunc (this *Url) Mkdir(path string) error {\n\treturn ErrNotAllowed\n}\n\nfunc (this *Url) Rm(path string) error {\n\treturn ErrNotAllowed\n}\n\nfunc (this *Url) Mv(from, to string) error {\n\treturn ErrNotAllowed\n}\n\nfunc (this *Url) Save(path string, content io.Reader) error {\n\treturn ErrNotAllowed\n}\n\nfunc (this *Url) Touch(path string) error {\n\treturn ErrNotAllowed\n}\n\nfunc (this *Url) JoinPath(link *url.URL) string {\n\tif strings.HasPrefix(link.Path, \"/\") {\n\t\treturn link.Path\n\t}\n\treturn this.root.JoinPath(link.Path).Path\n}\n"
  },
  {
    "path": "server/plugin/plg_backend_webdav/index.go",
    "content": "package plg_backend_webdav\n\nimport (\n\t\"encoding/xml\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype WebDav struct {\n\tparams *WebDavParams\n}\n\ntype WebDavParams struct {\n\turl      string\n\tusername string\n\tpassword string\n\tpath     string\n}\n\nfunc init() {\n\tBackend.Register(\"webdav\", WebDav{})\n}\n\nfunc (w WebDav) Init(params map[string]string, app *App) (IBackend, error) {\n\tparams[\"url\"] = regexp.MustCompile(`\\/$`).ReplaceAllString(params[\"url\"], \"\")\n\tif strings.HasPrefix(params[\"url\"], \"http://\") == false && strings.HasPrefix(params[\"url\"], \"https://\") == false {\n\t\treturn nil, NewError(\"Malformed URL - missing http or https\", 400)\n\t}\n\tbackend := WebDav{\n\t\tparams: &WebDavParams{\n\t\t\tstrings.ReplaceAll(params[\"url\"], \"%{username}\", url.PathEscape(params[\"username\"])),\n\t\t\tparams[\"username\"],\n\t\t\tparams[\"password\"],\n\t\t\tparams[\"path\"],\n\t\t},\n\t}\n\treturn backend, nil\n}\n\nfunc (w WebDav) LoginForm() Form {\n\treturn Form{\n\t\tElmnts: []FormElement{\n\t\t\tFormElement{\n\t\t\t\tName:  \"type\",\n\t\t\t\tType:  \"hidden\",\n\t\t\t\tValue: \"webdav\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"url\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Address\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"username\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Username\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"password\",\n\t\t\t\tType:        \"password\",\n\t\t\t\tPlaceholder: \"Password\",\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tName:        \"advanced\",\n\t\t\t\tType:        \"enable\",\n\t\t\t\tPlaceholder: \"Advanced\",\n\t\t\t\tTarget:      []string{\"webdav_path\"},\n\t\t\t},\n\t\t\tFormElement{\n\t\t\t\tId:          \"webdav_path\",\n\t\t\t\tName:        \"path\",\n\t\t\t\tType:        \"text\",\n\t\t\t\tPlaceholder: \"Path\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (w WebDav) Ls(path string) ([]os.FileInfo, error) {\n\tfiles := make([]os.FileInfo, 0)\n\tquery := `<d:propfind xmlns:d='DAV:'>\n\t\t\t<d:prop>\n\t\t\t\t<d:displayname/>\n\t\t\t\t<d:resourcetype/>\n\t\t\t\t<d:getlastmodified/>\n\t\t\t\t<d:getcontentlength/>\n\t\t\t</d:prop>\n\t\t</d:propfind>`\n\tres, err := w.request(\"PROPFIND\", w.params.url+encodeURL(path), strings.NewReader(query), func(req *http.Request) {\n\t\treq.Header.Add(\"Depth\", \"1\")\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer res.Body.Close()\n\tif res.StatusCode >= 400 {\n\t\treturn nil, NewError(HTTPFriendlyStatus(res.StatusCode)+\": can't get things in \"+filepath.Base(path), res.StatusCode)\n\t}\n\n\tvar r WebDavResp\n\tdecoder := xml.NewDecoder(res.Body)\n\tdecoder.Decode(&r)\n\tif len(r.Responses) == 0 {\n\t\treturn nil, NewError(\"Server not found\", 404)\n\t}\n\n\tLongURLDav := w.params.url + path\n\tShortURLDav := regexp.MustCompile(`^http[s]?://[^/]*`).ReplaceAllString(LongURLDav, \"\")\n\tfor _, tag := range r.Responses {\n\t\tdecodedHref := decodeURL(tag.Href)\n\t\tif decodedHref == ShortURLDav || decodedHref == LongURLDav {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor i, prop := range tag.Props {\n\t\t\tif i > 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfiles = append(files, File{\n\t\t\t\tFName: filepath.Base(decodedHref),\n\t\t\t\tFType: func(p string) string {\n\t\t\t\t\tif p == \"collection\" {\n\t\t\t\t\t\treturn \"directory\"\n\t\t\t\t\t}\n\t\t\t\t\treturn \"file\"\n\t\t\t\t}(prop.Type.Local),\n\t\t\t\tFTime: func() int64 {\n\t\t\t\t\tt, err := time.Parse(time.RFC1123, prop.Modified)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn 0\n\t\t\t\t\t}\n\t\t\t\t\treturn t.Unix()\n\t\t\t\t}(),\n\t\t\t\tFSize: prop.Size,\n\t\t\t})\n\t\t}\n\t}\n\treturn files, nil\n}\n\nfunc (w WebDav) Stat(path string) (os.FileInfo, error) {\n\tquery := `<d:propfind xmlns:d='DAV:'>\n\t\t\t<d:prop>\n\t\t\t\t<d:displayname/>\n\t\t\t\t<d:resourcetype/>\n\t\t\t\t<d:getlastmodified/>\n\t\t\t\t<d:getcontentlength/>\n\t\t\t</d:prop>\n\t\t</d:propfind>`\n\tres, err := w.request(\"PROPFIND\", w.params.url+encodeURL(path), strings.NewReader(query), func(req *http.Request) {\n\t\treq.Header.Add(\"Depth\", \"0\")\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer res.Body.Close()\n\tif res.StatusCode >= 400 {\n\t\treturn nil, NewError(HTTPFriendlyStatus(res.StatusCode)+\": can't get things in \"+filepath.Base(path), res.StatusCode)\n\t}\n\tvar r WebDavResp\n\tif err := xml.NewDecoder(res.Body).Decode(&r); err != nil {\n\t\treturn nil, err\n\t}\n\tif len(r.Responses) == 0 || len(r.Responses[0].Props) == 0 {\n\t\treturn nil, ErrNotFound\n\t}\n\tprop := r.Responses[0].Props[0]\n\tvar modTime int64\n\tif prop.Modified != \"\" {\n\t\tif t, err := time.Parse(time.RFC1123, prop.Modified); err == nil {\n\t\t\tmodTime = t.Unix()\n\t\t}\n\t}\n\treturn File{\n\t\tFName: filepath.Base(decodeURL(r.Responses[0].Href)),\n\t\tFType: func() string {\n\t\t\tif prop.Type.Local == \"collection\" {\n\t\t\t\treturn \"directory\"\n\t\t\t}\n\t\t\treturn \"file\"\n\t\t}(),\n\t\tFTime: modTime,\n\t\tFSize: prop.Size,\n\t}, nil\n}\n\nfunc (w WebDav) Cat(path string) (io.ReadCloser, error) {\n\tres, err := w.request(\"GET\", w.params.url+encodeURL(path), nil, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif res.StatusCode >= 400 {\n\t\treturn nil, NewError(HTTPFriendlyStatus(res.StatusCode)+\": can't fetch \"+filepath.Base(path), res.StatusCode)\n\t}\n\treturn res.Body, nil\n}\n\nfunc (w WebDav) Mkdir(path string) error {\n\tres, err := w.request(\"MKCOL\", w.params.url+encodeURL(path), nil, func(req *http.Request) {\n\t\treq.Header.Add(\"Overwrite\", \"F\")\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tres.Body.Close()\n\tif res.StatusCode >= 400 {\n\t\treturn NewError(HTTPFriendlyStatus(res.StatusCode)+\": can't create \"+filepath.Base(path), res.StatusCode)\n\t}\n\treturn nil\n}\n\nfunc (w WebDav) Rm(path string) error {\n\tres, err := w.request(\"DELETE\", w.params.url+encodeURL(path), nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tres.Body.Close()\n\tif res.StatusCode >= 400 {\n\t\treturn NewError(HTTPFriendlyStatus(res.StatusCode)+\": can't remove \"+filepath.Base(path), res.StatusCode)\n\t}\n\treturn nil\n}\n\nfunc (w WebDav) Mv(from string, to string) error {\n\tres, err := w.request(\"MOVE\", w.params.url+encodeURL(from), nil, func(req *http.Request) {\n\t\treq.Header.Add(\"Destination\", w.params.url+encodeURL(to))\n\t\treq.Header.Add(\"Overwrite\", \"T\")\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tres.Body.Close()\n\tif res.StatusCode >= 400 {\n\t\treturn NewError(HTTPFriendlyStatus(res.StatusCode)+\": can't do that\", res.StatusCode)\n\t}\n\treturn nil\n}\n\nfunc (w WebDav) Touch(path string) error {\n\treturn w.Save(path, strings.NewReader(\"\"))\n}\n\nfunc (w WebDav) Save(path string, file io.Reader) error {\n\tres, err := w.request(\"PUT\", w.params.url+encodeURL(path), file, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tres.Body.Close()\n\tif res.StatusCode >= 400 {\n\t\treturn NewError(HTTPFriendlyStatus(res.StatusCode)+\": can't do that\", res.StatusCode)\n\t}\n\treturn nil\n}\n\nfunc (w WebDav) request(method string, url string, body io.Reader, fn func(req *http.Request)) (*http.Response, error) {\n\treq, err := http.NewRequest(method, url, body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif w.params.username != \"\" {\n\t\treq.SetBasicAuth(w.params.username, w.params.password)\n\t}\n\n\treq.Header.Add(\"Content-Type\", \"text/xml;charset=UTF-8\")\n\treq.Header.Add(\"Accept-Charset\", \"utf-8\")\n\tswitch method {\n\tcase \"GET\":\n\t\treq.Header.Add(\"Accept\", \"*/*\")\n\tdefault:\n\t\treq.Header.Add(\"Accept\", \"application/xml,text/xml\")\n\t}\n\n\tif req.Body != nil {\n\t\tdefer req.Body.Close()\n\t}\n\tif fn != nil {\n\t\tfn(req)\n\t}\n\treturn HTTPClient.Do(req)\n}\n\ntype WebDavResp struct {\n\tResponses []struct {\n\t\tHref  string `xml:\"href\"`\n\t\tProps []struct {\n\t\t\tName     string   `xml:\"prop>displayname,omitempty\"`\n\t\t\tType     xml.Name `xml:\"prop>resourcetype>collection,omitempty\"`\n\t\t\tSize     int64    `xml:\"prop>getcontentlength,omitempty\"`\n\t\t\tModified string   `xml:\"prop>getlastmodified,omitempty\"`\n\t\t} `xml:\"propstat\"`\n\t} `xml:\"response\"`\n}\n\nfunc encodeURL(path string) string {\n\tp := url.PathEscape(path)\n\treturn strings.Replace(p, \"%2F\", \"/\", -1)\n}\n\nfunc decodeURL(path string) string {\n\tstr, err := url.PathUnescape(path)\n\tif err != nil {\n\t\treturn path\n\t}\n\treturn str\n}\n"
  },
  {
    "path": "server/plugin/plg_editor_onlyoffice/index.go",
    "content": "package plg_editor_onlyoffice\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"text/template\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/ctrl\"\n\t\"github.com/mickael-kerjean/filestash/server/middleware\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n\n\t\"github.com/gorilla/mux\"\n\t\"github.com/patrickmn/go-cache\"\n)\n\nvar (\n\tSECRET_KEY_DERIVATE_FOR_ONLYOFFICE string\n\n\tonlyoffice_cache *cache.Cache\n\tplugin_enable    func() bool\n\tserver_url       func() string\n\tcan_chat         func() bool\n\tcan_copy         func() bool\n\tcan_comment      func() bool\n\tcan_download     func() bool\n\tcan_edit         func() bool\n\tcan_print        func() bool\n)\n\ntype onlyOfficeCacheData struct {\n\tPath string\n\tSave func(path string, file io.Reader) error\n\tCat  func(path string) (io.ReadCloser, error)\n}\n\nfunc init() {\n\tSECRET_KEY_DERIVATE_FOR_ONLYOFFICE = Hash(\"ONLYOFFICE_\"+SECRET_KEY, len(SECRET_KEY))\n\tonlyoffice_cache = cache.New(720*time.Minute, 720*time.Minute)\n\tplugin_enable = func() bool {\n\t\treturn Config.Get(\"features.office.enable\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Name = \"enable\"\n\t\t\tf.Type = \"enable\"\n\t\t\tf.Target = []string{\"onlyoffice_server\", \"onlyoffice_can_chat\", \"onlyoffice_can_copy\", \"onlyoffice_can_comment\", \"onlyoffice_can_download\", \"onlyoffice_can_edit\", \"onlyoffice_can_print\"}\n\t\t\tf.Description = \"Enable/Disable the office suite and options to manage word, excel and powerpoint documents.\"\n\t\t\tf.Default = false\n\t\t\tif u := os.Getenv(\"ONLYOFFICE_URL\"); u != \"\" {\n\t\t\t\tf.Default = true\n\t\t\t}\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\n\tserver_url = func() string {\n\t\treturn Config.Get(\"features.office.onlyoffice_server\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"onlyoffice_server\"\n\t\t\tf.Name = \"onlyoffice_server\"\n\t\t\tf.Type = \"text\"\n\t\t\tf.Description = \"Location of your OnlyOffice server\"\n\t\t\tf.Default = \"http://127.0.0.1:8080\"\n\t\t\tf.Placeholder = \"Eg: http://127.0.0.1:8080\"\n\t\t\tif u := os.Getenv(\"ONLYOFFICE_URL\"); u != \"\" {\n\t\t\t\tf.Default = u\n\t\t\t\tf.Placeholder = fmt.Sprintf(\"Default: '%s'\", u)\n\t\t\t}\n\t\t\treturn f\n\t\t}).String()\n\t}\n\n\tcan_chat = func() bool {\n\t\treturn Config.Get(\"features.office.can_chat\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"onlyoffice_can_chat\"\n\t\t\tf.Name = \"can_chat\"\n\t\t\tf.Type = \"boolean\"\n\t\t\tf.Description = \"Enable/Disable chat in onlyoffice\"\n\t\t\tf.Default = false\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\n\tcan_copy = func() bool {\n\t\treturn Config.Get(\"features.office.can_copy\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"onlyoffice_can_copy\"\n\t\t\tf.Name = \"can_copy\"\n\t\t\tf.Type = \"boolean\"\n\t\t\tf.Description = \"Enable/Disable copy text in onlyoffice\"\n\t\t\tf.Default = false\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\n\tcan_comment = func() bool {\n\t\treturn Config.Get(\"features.office.can_comment\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"onlyoffice_can_comment\"\n\t\t\tf.Name = \"can_comment\"\n\t\t\tf.Type = \"boolean\"\n\t\t\tf.Description = \"Enable/Disable comments in onlyoffice\"\n\t\t\tf.Default = false\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\n\tcan_edit = func() bool {\n\t\treturn Config.Get(\"features.office.can_edit\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"onlyoffice_can_edit\"\n\t\t\tf.Name = \"can_edit\"\n\t\t\tf.Type = \"boolean\"\n\t\t\tf.Description = \"Enable/Disable editing in onlyoffice\"\n\t\t\tf.Default = true\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\n\tcan_download = func() bool {\n\t\treturn Config.Get(\"features.office.can_download\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"onlyoffice_can_download\"\n\t\t\tf.Name = \"can_download\"\n\t\t\tf.Type = \"boolean\"\n\t\t\tf.Description = \"Display Download button in onlyoffice\"\n\t\t\tf.Default = true\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\tcan_print = func() bool {\n\t\treturn Config.Get(\"features.office.can_print\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"onlyoffice_can_print\"\n\t\t\tf.Name = \"can_print\"\n\t\t\tf.Type = \"boolean\"\n\t\t\tf.Description = \"Enable/Disable printing in onlyoffice\"\n\t\t\tf.Default = false\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\n\tHooks.Register.Onload(func() {\n\t\tplugin_enable()\n\t\tserver_url()\n\t\tcan_chat()\n\t\tcan_copy()\n\t\tcan_comment()\n\t\tcan_download()\n\t\tcan_edit()\n\t\tcan_print()\n\t})\n\n\tHooks.Register.HttpEndpoint(func(r *mux.Router, app *App) error {\n\t\toods := r.PathPrefix(\"/onlyoffice\").Subrouter()\n\t\toods.PathPrefix(\"/static/\").HandlerFunc(StaticHandler).Methods(\"GET\", \"POST\")\n\t\toods.HandleFunc(\"/event\", OnlyOfficeEventHandler).Methods(\"POST\")\n\t\toods.HandleFunc(\"/content\", FetchContentHandler).Methods(\"GET\")\n\n\t\tr.HandleFunc(\n\t\t\tCOOKIE_PATH+\"onlyoffice/iframe\",\n\t\t\tmiddleware.NewMiddlewareChain(\n\t\t\t\tIframeContentHandler,\n\t\t\t\t[]Middleware{middleware.SessionStart, middleware.LoggedInOnly},\n\t\t\t),\n\t\t).Methods(\"GET\")\n\t\treturn nil\n\t})\n\tHooks.Register.XDGOpen(`\n        if(mime === \"application/word\" || mime === \"application/msword\" ||\n           mime === \"application/vnd.oasis.opendocument.text\" || mime === \"application/vnd.oasis.opendocument.spreadsheet\" ||\n           mime === \"application/excel\" || mime === \"application/vnd.ms-excel\" || mime === \"application/powerpoint\" ||\n           mime === \"application/vnd.ms-powerpoint\" || mime === \"application/vnd.oasis.opendocument.presentation\" ) {\n              return [\"appframe\", {\"endpoint\": \"/api/onlyoffice/iframe\"}];\n           }\n   `)\n}\n\nfunc StaticHandler(res http.ResponseWriter, req *http.Request) {\n\tif plugin_enable() == false {\n\t\treturn\n\t}\n\treq.URL.Path = strings.TrimPrefix(req.URL.Path, \"/onlyoffice/static\")\n\tu, err := url.Parse(server_url())\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\treq.Header.Set(\"X-Forwarded-Host\", req.Host+\"/onlyoffice/static\")\n\treq.Header.Set(\"X-Forwarded-Proto\", func() string {\n\t\tif scheme := req.Header.Get(\"X-Forwarded-Proto\"); scheme != \"\" {\n\t\t\treturn scheme\n\t\t} else if req.TLS != nil {\n\t\t\treturn \"https\"\n\t\t}\n\t\treturn \"http\"\n\t}())\n\n\t// This code is a copy and paste from httputil.NewSingleHostReverseProxy with 1 single change\n\t// to do SSL termination.\n\treverseProxy := &httputil.ReverseProxy{\n\t\tDirector: func(rq *http.Request) {\n\t\t\trq.URL.Scheme = \"http\" // <- this is the only change from NewSingleHostReverseProxy\n\t\t\trq.URL.Host = u.Host\n\t\t\trq.URL.Path = func(a, b string) string {\n\t\t\t\taslash := strings.HasSuffix(a, \"/\")\n\t\t\t\tbslash := strings.HasPrefix(b, \"/\")\n\t\t\t\tswitch {\n\t\t\t\tcase aslash && bslash:\n\t\t\t\t\treturn a + b[1:]\n\t\t\t\tcase !aslash && !bslash:\n\t\t\t\t\treturn a + \"/\" + b\n\t\t\t\t}\n\t\t\t\treturn a + b\n\t\t\t}(u.Path, rq.URL.Path)\n\t\t\tif u.RawQuery == \"\" || rq.URL.RawQuery == \"\" {\n\t\t\t\trq.URL.RawQuery = u.RawQuery + rq.URL.RawQuery\n\t\t\t} else {\n\t\t\t\trq.URL.RawQuery = u.RawQuery + \"&\" + rq.URL.RawQuery\n\t\t\t}\n\t\t},\n\t}\n\treverseProxy.ErrorHandler = func(rw http.ResponseWriter, rq *http.Request, err error) {\n\t\tLog.Warning(\"[onlyoffice] %s\", err.Error())\n\t\tSendErrorResult(rw, NewError(err.Error(), http.StatusBadGateway))\n\t}\n\treverseProxy.ServeHTTP(res, req)\n}\n\nfunc IframeContentHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif plugin_enable() == false {\n\t\tLog.Warning(\"plg_editor_onlyoffice::handler request_disabled\")\n\t\treturn\n\t}\n\tif model.CanRead(ctx) == false {\n\t\tSendErrorResult(res, ErrPermissionDenied)\n\t\treturn\n\t} else if server_url() == \"\" {\n\t\tres.WriteHeader(http.StatusServiceUnavailable)\n\t\tres.Write([]byte(\"<p>The Onlyoffice server hasn't been configured</p>\"))\n\t\tres.Write([]byte(\"<style>p {color: white; text-align: center; margin-top: 50px; font-size: 20px; opacity: 0.6; font-family: monospace; } </style>\"))\n\t\treturn\n\t}\n\n\tvar (\n\t\tpath                    string // path of the file we want to open via onlyoffice\n\t\tfilestashServerLocation string // location from which the oods server can reach filestash\n\t\tuserId                  string // as seen by onlyoffice to distinguish different users\n\t\tusername                string // username as displayed by only office\n\t\tkey                     string // unique identifier for a file as seen be only office\n\t\tcontentType             string // name of the application in onlyoffice\n\t\tfiletype                string // extension of the document\n\t\tfilename                string // filename of the document\n\t\toodsMode                string // edit mode\n\t\toodsDevice              string // mobile, desktop of embedded\n\t\tlocalip                 string\n\t)\n\tquery := req.URL.Query()\n\tpath, err := ctrl.PathBuilder(ctx, query.Get(\"path\"))\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\n\tuserId = GenerateID(ctx.Session)\n\tf, err := ctx.Backend.Cat(path)\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\tkey = HashStream(f, 20)\n\tkey = Hash(key+userId+path, 20)\n\n\tfilename = filepath.Base(path)\n\toodsMode = func() string {\n\t\tif model.CanEdit(ctx) == false {\n\t\t\treturn \"view\"\n\t\t}\n\t\treturn \"edit\"\n\t}()\n\toodsDevice = func() string {\n\t\tua := req.Header.Get(\"User-Agent\")\n\t\tif ua == \"\" {\n\t\t\treturn \"desktop\"\n\t\t} else if strings.Contains(ua, \"iPhone\") {\n\t\t\treturn \"mobile\"\n\t\t} else if strings.Contains(ua, \"iPad\") {\n\t\t\treturn \"mobile\"\n\t\t} else if strings.Contains(ua, \"Android\") {\n\t\t\treturn \"mobile\"\n\t\t} else if strings.Contains(ua, \"Mobile\") {\n\t\t\treturn \"mobile\"\n\t\t}\n\n\t\tif oodsMode == \"view\" {\n\t\t\treturn \"embedded\"\n\t\t}\n\t\treturn \"desktop\"\n\t}()\n\tusername = func() string {\n\t\tif ctx.Session[\"username\"] != \"\" {\n\t\t\treturn ctx.Session[\"username\"]\n\t\t}\n\t\treturn \"Me\"\n\t}()\n\tif ctx.Share.Id != \"\" {\n\t\tusername = \"Anonymous\"\n\t\tuserId = RandomString(10)\n\t}\n\tlocalip = func() string { // https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go#23558495\n\t\taddrs, err := net.InterfaceAddrs()\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\n\t\tmaybeips := []string{}\n\t\tfor _, address := range addrs {\n\t\t\tif ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {\n\t\t\t\tif ipnet.IP.To4() != nil {\n\t\t\t\t\tmaybeips = append(maybeips, ipnet.IP.String())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// if there is just one interface, we can just pick that one\n\t\tif len(maybeips) == 1 {\n\t\t\treturn maybeips[0]\n\t\t}\n\n\t\t// if not, fallback to capturing our outgoing local ip\n\t\tconn, err := net.Dial(\"udp\", \"8.8.8.8:80\")\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t\tdefer conn.Close()\n\n\t\tlocalAddr := conn.LocalAddr().(*net.UDPAddr)\n\n\t\treturn localAddr.IP.String()\n\t}()\n\tfilestashServerLocation = fmt.Sprintf(\n\t\t\"%s://%s:%d\",\n\t\tfunc() string { // proto\n\t\t\tif req.TLS == nil {\n\t\t\t\treturn \"http\"\n\t\t\t}\n\t\t\treturn \"https\"\n\t\t}(),\n\t\tlocalip,\n\t\tConfig.Get(\"general.port\").Int(),\n\t)\n\tcontentType = func(p string) string {\n\t\tvar (\n\t\t\tword       string = \"text\"\n\t\t\texcel      string = \"spreadsheet\"\n\t\t\tpowerpoint string = \"presentation\"\n\t\t)\n\t\tswitch GetMimeType(p) {\n\t\tcase \"application/word\":\n\t\t\treturn word\n\t\tcase \"application/msword\":\n\t\t\treturn word\n\t\tcase \"application/vnd.oasis.opendocument.text\":\n\t\t\treturn word\n\t\tcase \"application/vnd.oasis.opendocument.spreadsheet\":\n\t\t\treturn excel\n\t\tcase \"application/excel\":\n\t\t\treturn excel\n\t\tcase \"application/vnd.ms-excel\":\n\t\t\treturn excel\n\t\tcase \"application/powerpoint\":\n\t\t\treturn powerpoint\n\t\tcase \"application/vnd.ms-powerpoint\":\n\t\t\treturn powerpoint\n\t\tcase \"application/vnd.oasis.opendocument.presentation\":\n\t\t\treturn powerpoint\n\t\t}\n\t\treturn \"\"\n\t}(path)\n\tfiletype = strings.TrimPrefix(filepath.Ext(filename), \".\")\n\tonlyoffice_cache.Set(key, &onlyOfficeCacheData{path, ctx.Backend.Save, ctx.Backend.Cat}, cache.DefaultExpiration)\n\n\ttmpl, err := template.New(\"onlyoffice\").Parse(`<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n   <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  </head>\n  <body>\n    <style> body { margin: 0; } body, html{ height: 100%; } iframe { width: 100%; height: 100%; } </style>\n    <div id=\"placeholder\"></div>\n    <script type=\"text/javascript\" src=\"/onlyoffice/static/web-apps/apps/api/documents/api.js\"></script>\n    <script>\n      if(\"DocsAPI\" in window) loadApplication();\n      else sendError(\"[error] Can't reach the onlyoffice server\");\n\n      function loadApplication() {\n          new DocsAPI.DocEditor(\"placeholder\", {\n              \"token\": \"{{ .token }}\",\n              \"documentType\": \"{{ .contentType }}\",\n              \"type\": \"{{ .device }}\",\n              \"document\": {\n                  \"title\": \"{{ .filename }}\",\n                  \"url\": \"{{ .base }}/onlyoffice/content?key={{ .key }}\",\n                  \"fileType\": \"{{ .filetype }}\",\n                  \"key\": \"{{ .key }}\",\n                  \"permissions\": {\n\t\t\t\t  \t  \"chat\": {{ .can_chat }},\n\t\t \t\t\t  \"copy\": {{ .can_copy }},\n\t\t\t\t\t  \"comment\": {{ .can_comment }},\n                      \"download\": {{ .can_download }},\n\t\t\t\t\t  \"edit\": {{ .can_edit }},\n\t   \t\t\t\t  \"print\": {{ .can_print }}\n                  }\n              },\n              \"editorConfig\": {\n                  \"callbackUrl\": \"{{ .base }}/onlyoffice/event\",\n                  \"mode\": \"{{ .mode }}\",\n                  \"customization\": {\n                      \"autosave\": false,\n                      \"forcesave\": true,\n                      \"compactHeader\": true\n                  },\n                  \"user\": {\n                      \"id\": \"{{ .userID }}\",\n                      \"name\": \"{{ .userName }}\"\n                  }\n              }\n          });\n      }\n      function sendError(message){\n          let $el = document.createElement(\"p\");\n          $el.innerHTML = message;\n          $el.setAttribute(\"style\", \"text-align: center; color: white; opacity: 0.8; font-size: 20px; font-family: monospace;\");\n          document.body.appendChild($el);\n      }\n    </script>\n  </body>\n</html>\n`)\n\tif err != nil {\n\t\tres.Write([]byte(err.Error()))\n\t\treturn\n\t}\n\tif err := tmpl.Execute(res, map[string]interface{}{\n\t\t\"base\":         filestashServerLocation,\n\t\t\"can_chat\":     can_chat(),\n\t\t\"can_copy\":     can_copy(),\n\t\t\"can_comment\":  can_comment(),\n\t\t\"can_download\": can_download(),\n\t\t\"can_edit\":     can_edit(),\n\t\t\"can_print\":    can_print(),\n\t\t\"contentType\":  contentType,\n\t\t\"device\":       oodsDevice,\n\t\t\"filename\":     filename,\n\t\t\"filetype\":     filetype,\n\t\t\"key\":          key,\n\t\t\"mode\":         oodsMode,\n\t\t\"token\":        \"foobar\",\n\t\t\"type\":         contentType,\n\t\t\"userID\":       userId,\n\t\t\"userName\":     username,\n\t}); err != nil {\n\t\tres.Write([]byte(err.Error()))\n\t\treturn\n\t}\n}\n\nfunc FetchContentHandler(res http.ResponseWriter, req *http.Request) {\n\tif plugin_enable() == false {\n\t\treturn\n\t}\n\tvar key string\n\tif key = req.URL.Query().Get(\"key\"); key == \"\" {\n\t\tSendErrorResult(res, NewError(\"unspecified key\", http.StatusBadRequest))\n\t\treturn\n\t}\n\tc, found := onlyoffice_cache.Get(key)\n\tif found == false {\n\t\tres.WriteHeader(http.StatusInternalServerError)\n\t\tres.Write([]byte(`{\"error\": 1, \"message\": \"missing data fetcher handler\"}`))\n\t\treturn\n\t}\n\tcData, valid := c.(*onlyOfficeCacheData)\n\tif valid == false {\n\t\tres.WriteHeader(http.StatusInternalServerError)\n\t\tres.Write([]byte(`{\"error\": 1, \"message\": \"invalid cache\"}`))\n\t\treturn\n\t}\n\tf, err := cData.Cat(cData.Path)\n\tif err != nil {\n\t\tres.WriteHeader(http.StatusNotFound)\n\t\tres.Write([]byte(`{\"error\": 1, \"message\": \"error while fetching data\"}`))\n\t\treturn\n\t}\n\tio.Copy(res, f)\n\tf.Close()\n}\n\ntype onlyOfficeEventObject struct {\n\tActions []struct {\n\t\tType   int    `json: \"type\"`\n\t\tUserId string `json: \"userid\" `\n\t} `json: \"actions\"`\n\tChangesURL    string `json: \"changesurl\"`\n\tForcesavetype int    `json: \"forcesavetype\"`\n\tHistory       struct {\n\t\tServerVersion string `json: \"serverVersion\"`\n\t\tChanges       []struct {\n\t\t\tCreated string `json: \"created\"`\n\t\t\tUser    struct {\n\t\t\t\tId   string `json: \"id\"`\n\t\t\t\tName string `json: \"name\"`\n\t\t\t}\n\t\t} `json: \"changes\"`\n\t} `json: \"history\"`\n\tKey      string   `json: \"key\"`\n\tStatus   int      `json: \"status\"`\n\tUrl      string   `json: \"url\"`\n\tUserData string   `json: \"userdata\"`\n\tLastsave string   `json: \"lastsave\"`\n\tUsers    []string `json: \"users\"`\n}\n\nfunc OnlyOfficeEventHandler(res http.ResponseWriter, req *http.Request) {\n\tif plugin_enable() == false {\n\t\treturn\n\t}\n\tevent := onlyOfficeEventObject{}\n\tif err := json.NewDecoder(req.Body).Decode(&event); err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\treq.Body.Close()\n\n\tswitch event.Status {\n\tcase 0:\n\t\tLog.Warning(\"[onlyoffice] no document with the key identifier could be found. %+v\", event)\n\tcase 1:\n\t\t// document is being edited\n\tcase 2:\n\t\t// document is ready for saving\n\tcase 3:\n\t\t// document saving error has occurred\n\t\tLog.Warning(\"[onlyoffice] document saving error has occurred. %+v\", event)\n\tcase 4:\n\t\t// document is closed with no changes\n\tcase 5:\n\t\tLog.Warning(\"[onlyoffice] undocumented status. %+v\", event)\n\tcase 6: // document is being edited, but the current document state is saved\n\t\tsaveObject, found := onlyoffice_cache.Get(event.Key)\n\t\tif found == false {\n\t\t\tres.WriteHeader(http.StatusInternalServerError)\n\t\t\tres.Write([]byte(`{\"error\": 1, \"message\": \"doens't know where to store the given data\"}`))\n\t\t\treturn\n\t\t}\n\t\tcData, valid := saveObject.(*onlyOfficeCacheData)\n\t\tif valid == false {\n\t\t\tres.WriteHeader(http.StatusInternalServerError)\n\t\t\tres.Write([]byte(`{\"error\": 1, \"message\": \"[internal error] invalid save handler\"}`))\n\t\t\treturn\n\t\t}\n\n\t\tr, err := http.NewRequest(\"GET\", event.Url, nil)\n\t\tif err != nil {\n\t\t\tres.WriteHeader(http.StatusInternalServerError)\n\t\t\tres.Write([]byte(`{\"error\": 1, \"message\": \"couldn't fetch the document on the oods server\"}`))\n\t\t\treturn\n\t\t}\n\t\tf, err := HTTPClient.Do(r)\n\t\tif err = cData.Save(cData.Path, f.Body); err != nil {\n\t\t\tres.WriteHeader(http.StatusInternalServerError)\n\t\t\tres.Write([]byte(`{\"error\": 1, \"message\": \"error while saving the document\"}`))\n\t\t\treturn\n\t\t}\n\t\tf.Body.Close()\n\tcase 7:\n\t\tLog.Warning(\"[onlyoffice] error has occurred while force saving the document. %+v\", event)\n\tdefault:\n\t\tLog.Warning(\"[onlyoffice] undocumented status. %+v\", event)\n\t}\n\tres.Write([]byte(`{\"error\": 0}`))\n}\n"
  },
  {
    "path": "server/plugin/plg_editor_wopi/config.go",
    "content": "package plg_editor_wopi\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc plugin_enable() bool {\n\treturn Config.Get(\"features.office.enable\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"enable\"\n\t\tf.Type = \"enable\"\n\t\tf.Target = []string{\"office_server\", \"filestash_server\", \"rewrite_discovery_url\"}\n\t\tf.Description = \"Enable/Disable the wopi office suite and options to manage word, excel and powerpoint documents.\"\n\t\tf.Default = false\n\t\tif u := os.Getenv(\"OFFICE_URL\"); u != \"\" {\n\t\t\tf.Default = true\n\t\t}\n\t\treturn f\n\t}).Bool()\n}\n\nfunc server_url() string {\n\treturn Config.Get(\"features.office.office_server\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Id = \"office_server\"\n\t\tf.Name = \"office_server\"\n\t\tf.Type = \"text\"\n\t\tf.Description = \"Location of your WOPI Office server\"\n\t\tf.Default = \"http://127.0.0.1:9980\"\n\t\tf.Placeholder = \"Eg: http://127.0.0.1:9980\"\n\t\tif u := os.Getenv(\"OFFICE_URL\"); u != \"\" {\n\t\t\tf.Default = u\n\t\t\tf.Placeholder = fmt.Sprintf(\"Default: '%s'\", u)\n\t\t}\n\t\treturn f\n\t}).String()\n}\n\nfunc origin() string {\n\treturn Config.Get(\"features.office.filestash_server\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Id = \"filestash_server\"\n\t\tf.Name = \"filestash_server\"\n\t\tf.Type = \"text\"\n\t\tf.Description = \"Location of your Filestash server from the point of view of the office server. Keep blank if you don't use fancy networking via docker/k8s,...\"\n\t\tf.Default = \"http://app:8334\"\n\t\tf.Placeholder = \"Eg: http://app:8334\"\n\t\tif u := os.Getenv(\"OFFICE_FILESTASH_URL\"); u != \"\" {\n\t\t\tf.Default = u\n\t\t\tf.Placeholder = fmt.Sprintf(\"Default: '%s'\", u)\n\t\t}\n\t\treturn f\n\t}).String()\n}\n\nfunc rewrite_url() string {\n\treturn Config.Get(\"features.office.rewrite_discovery_url\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Id = \"rewrite_discovery_url\"\n\t\tf.Name = \"rewrite_discovery_url\"\n\t\tf.Type = \"text\"\n\t\tf.Description = \"Rewrite the discovery URL to something else. Typical example is a deployment via docker where your office server resolve via http://wopi_service:9980 but such URL would be unknown when given to a browser. Keep empty if you're not doing a docker / k8s deployment\"\n\t\tf.Default = \"\"\n\t\tf.Placeholder = \"Eg: http://localhost:9980\"\n\t\tif u := os.Getenv(\"OFFICE_REWRITE_URL\"); u != \"\" {\n\t\t\tf.Default = u\n\t\t\tf.Placeholder = fmt.Sprintf(\"Default: '%s'\", u)\n\t\t}\n\t\treturn f\n\t}).String()\n}\n"
  },
  {
    "path": "server/plugin/plg_editor_wopi/handler.go",
    "content": "package plg_editor_wopi\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"encoding/xml\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"text/template\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/ctrl\"\n\t\"github.com/mickael-kerjean/filestash/server/middleware\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n\n\t\"github.com/gorilla/mux\"\n)\n\nvar WOPIRoutes = func(r *mux.Router) error {\n\tr.HandleFunc(\n\t\t\"/api/wopi/iframe\",\n\t\tmiddleware.NewMiddlewareChain(\n\t\t\tIframeContentHandler,\n\t\t\t[]Middleware{middleware.SessionStart, middleware.LoggedInOnly},\n\t\t),\n\t).Methods(\"GET\")\n\tr.HandleFunc(\"/api/wopi/files/{path64}\", WOPIHandler_CheckFileInfo).Methods(\"GET\")\n\tr.HandleFunc(\"/api/wopi/files/{path64}/contents\", WOPIHandler_GetFile).Methods(\"GET\")\n\tr.HandleFunc(\"/api/wopi/files/{path64}/contents\", WOPIHandler_PutFile).Methods(\"POST\")\n\treturn nil\n}\n\nvar WOPIOverrides = `\n    if (mime === \"application/word\" || mime === \"application/msword\" ||\n        mime === \"application/vnd.oasis.opendocument.text\" || mime === \"application/vnd.oasis.opendocument.spreadsheet\" ||\n        mime === \"application/excel\" || mime === \"application/vnd.ms-excel\" || mime === \"application/powerpoint\" ||\n        mime === \"application/vnd.ms-powerpoint\" || mime === \"application/vnd.oasis.opendocument.presentation\" ) {\n        return [\"appframe\", {\"endpoint\": \"/api/wopi/iframe\"}];\n    }\n`\n\nfunc WOPIHandler_CheckFileInfo(w http.ResponseWriter, r *http.Request) {\n\tif plugin_enable() == false {\n\t\tSendErrorResult(w, ErrNotFound)\n\t\treturn\n\t}\n\tWOPIExecute(w, r)(func(ctx *App, fullpath string, w http.ResponseWriter) {\n\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\tif err := json.NewEncoder(w).Encode(map[string]any{\n\t\t\t\"BaseFileName\":     filepath.Base(fullpath),\n\t\t\t\"UserFriendlyName\": \"Unknown\",\n\t\t\t\"UserCanWrite\":     model.CanEdit(ctx),\n\t\t\t\"IsAdminUser\":      false,\n\t\t\t\"IsAnonymousUser\":  true,\n\t\t}); err != nil {\n\t\t\tSendErrorResult(w, err)\n\t\t\treturn\n\t\t}\n\t})\n}\n\nfunc WOPIHandler_GetFile(w http.ResponseWriter, r *http.Request) {\n\tWOPIExecute(w, r)(func(ctx *App, fullpath string, w http.ResponseWriter) {\n\t\tf, err := ctx.Backend.Cat(fullpath)\n\t\tif err != nil {\n\t\t\tSendErrorResult(w, err)\n\t\t\treturn\n\t\t}\n\t\tio.Copy(w, f)\n\t})\n}\n\nfunc WOPIHandler_PutFile(w http.ResponseWriter, r *http.Request) {\n\tdefer r.Body.Close()\n\tWOPIExecute(w, r)(func(ctx *App, fullpath string, w http.ResponseWriter) {\n\t\terr := ctx.Backend.Save(fullpath, r.Body)\n\t\tif err != nil {\n\t\t\tSendErrorResult(w, err)\n\t\t\treturn\n\t\t}\n\t\tSendSuccessResult(w, nil)\n\t})\n}\n\nfunc WOPIExecute(w http.ResponseWriter, r *http.Request) func(func(*App, string, http.ResponseWriter)) {\n\treturn func(fn func(*App, string, http.ResponseWriter)) {\n\t\tmiddleware.NewMiddlewareChain(\n\t\t\tfunc(ctx *App, w http.ResponseWriter, r *http.Request) {\n\t\t\t\tfullpath, err := ctrl.PathBuilder(ctx, r.URL.Query().Get(\"path\"))\n\t\t\t\tif err != nil {\n\t\t\t\t\tSendErrorResult(w, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tfn(ctx, fullpath, w)\n\t\t\t},\n\t\t\t[]Middleware{wopiToCommonAPI, middleware.SessionStart},\n\t\t).ServeHTTP(w, r)\n\t}\n}\n\nfunc wopiToCommonAPI(fn HandlerFunc) HandlerFunc {\n\textractInfo := func(encodedString string) (path string, shareID string) {\n\t\ttmp := strings.Split(encodedString, \"::\") // eg: backendID::b64(path)::shareID\n\t\tif len(tmp) < 2 {\n\t\t\treturn \"\", \"\"\n\t\t}\n\t\tbpath, err := base64.StdEncoding.DecodeString(tmp[1])\n\t\tif err != nil {\n\t\t\treturn \"\", \"\"\n\t\t} else if len(tmp) > 2 {\n\t\t\tshareID = tmp[2]\n\t\t}\n\t\treturn string(bpath), shareID\n\t}\n\n\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tpath, shareID := extractInfo(mux.Vars(req)[\"path64\"])\n\t\tif path == \"\" {\n\t\t\tSendErrorResult(res, ErrNotValid)\n\t\t\treturn\n\t\t}\n\t\turlQuery := req.URL.Query()\n\t\turlQuery.Set(\"path\", path)\n\t\tif shareID != \"\" {\n\t\t\turlQuery.Set(\"share\", shareID)\n\t\t\turlQuery.Del(\"access_key\")\n\t\t} else {\n\t\t\turlQuery.Set(\"authorization\", urlQuery.Get(\"access_token\"))\n\t\t\turlQuery.Del(\"access_key\")\n\t\t}\n\t\treq.URL.RawQuery = urlQuery.Encode()\n\t\tfn(ctx, res, req)\n\t})\n}\n\nfunc IframeContentHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tu, err := wopiDiscovery(ctx, req.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tLog.Warning(\"plg_editor_wopi::discovery err=%s\", err.Error())\n\t\tSendErrorResult(res, ErrNotValid)\n\t\treturn\n\t}\n\ttmpl, err := template.New(\"wopi\").Parse(`<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n      <meta charset=\"utf-8\">\n      <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n      <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n      <style>iframe.hidden{ opacity: 0; } iframe{ opacity: 1; transition: opacity 0.5s ease; transition-delay: 1s; }</style>\n  </head>\n  <body>\n    <style> body { margin: 0; } body, html{ height: 100%; } iframe { width: 100%; height: 100%; background: white; } </style>\n    <iframe frameborder=\"0\" src=\"{{ .server }}\" class=\"hidden\"></iframe>\n\n    <script type=\"module\" src=\"/assets/components/loader.js\"></script>\n    <component-loader />\n    <script>\n        const postChild = (data) => $iframe.contentWindow.postMessage(JSON.stringify(data), \"*\");\n        const postParent = (data) => window.parent.postMessage(JSON.stringify(data));\n\n        const $iframe = document.querySelector(\"iframe\");\n        $iframe.onerror = () => postParent({type: \"error\", msg: \"Not Found\" });\n\n        window.addEventListener(\"message\", (event) => {\n            let msg = JSON.parse(event.data);\n            if (!msg) return;\n            switch(msg.MessageId) {\n                case \"App_LoadingStatus\": if (msg.Values.Status === \"Initialized\") {\n                        postChild({ MessageId: \"Host_PostmessageReady\" });\n                        requestAnimationFrame(() => $iframe.classList.remove(\"hidden\"));\n                        document.querySelector(\"component-loader\").remove();\n                    }\n                    break;\n                case \"Action_Load_Resp\": if (msg.Values.errorMsg) {\n                        postParent({ type: \"error\", msg: msg.Values.errorMsg });\n                    }\n                    break;\n                default:\n                    console.log(\"postMessage:\", msg);\n                    break;\n            }\n        });\n    </script>\n  </body>\n</html>\n`)\n\tif err != nil {\n\t\tres.Write([]byte(err.Error()))\n\t\treturn\n\t}\n\tif err := tmpl.Execute(res, map[string]interface{}{\n\t\t\"server\": u,\n\t}); err != nil {\n\t\tres.Write([]byte(err.Error()))\n\t\treturn\n\t}\n}\n\ntype WOPIDiscovery struct {\n\tXMLName  xml.Name      `xml:\"wopi-discovery\"`\n\tNetZones []WOPINetZone `xml:\"net-zone\"`\n}\n\ntype WOPINetZone struct {\n\tApps []WOPIApp `xml:\"app\"`\n}\n\ntype WOPIApp struct {\n\tName    string       `xml:\"name,attr\"`\n\tActions []WOPIAction `xml:\"action\"`\n}\n\ntype WOPIAction struct {\n\tExt    string `xml:\"ext,attr\"`\n\tURLSrc string `xml:\"urlsrc,attr\"`\n}\n\nfunc wopiDiscovery(ctx *App, fullpath string) (string, error) {\n\t// STEP1: fetch discovery\n\tresp, err := http.Get(server_url() + \"/hosting/discovery\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", ErrInternal\n\t}\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// STEP2: parse discovery\n\tvar discovery WOPIDiscovery\n\tif err := xml.Unmarshal(body, &discovery); err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// STEP3: find the right URLsrc for the desired filetype\n\tvar urlsrc = \"\"\n\tfileType := strings.TrimPrefix(filepath.Ext(fullpath), \".\")\n\tfor _, netZone := range discovery.NetZones {\n\t\tfor _, app := range netZone.Apps {\n\t\t\tfor _, action := range app.Actions {\n\t\t\t\tif action.Ext == fileType {\n\t\t\t\t\turlsrc = action.URLSrc\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif urlsrc == \"\" {\n\t\treturn \"\", ErrNotFound\n\t}\n\n\t// STEP4: build the iframe URL\n\tu, err := url.Parse(urlsrc)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\twopiSRC := origin()\n\tif wopiSRC == \"\" {\n\t\twopiSRC := \"http://\"\n\t\tif Config.Get(\"general.force_ssl\").Bool() {\n\t\t\twopiSRC = \"https://\"\n\t\t}\n\t\twopiSRC += Config.Get(\"general.host\").String()\n\t}\n\twopiSRC += \"/api/wopi/files/\"\n\twopiSRC += GenerateID(map[string]string{\n\t\t\"id\":   GenerateID(ctx.Session),\n\t\t\"path\": fullpath,\n\t})\n\twopiSRC += \"::\" + base64.StdEncoding.EncodeToString([]byte(fullpath))\n\tif ctx.Share.Id != \"\" {\n\t\twopiSRC += \"::\" + ctx.Share.Id\n\t}\n\tp := u.Query()\n\tp.Set(\"WOPISrc\", wopiSRC)\n\tp.Set(\"access_token\", ctx.Authorization)\n\tif len(ctx.Languages) > 0 {\n\t\tp.Set(\"lang\", ctx.Languages[0])\n\t}\n\tu.RawQuery = p.Encode()\n\tif newHost := rewrite_url(); newHost != \"\" {\n\t\tif p, err := url.Parse(newHost); err == nil {\n\t\t\tu.Host = p.Host\n\t\t\tu.Scheme = p.Scheme\n\t\t}\n\t}\n\treturn u.String(), nil\n}\n"
  },
  {
    "path": "server/plugin/plg_editor_wopi/index.go",
    "content": "package plg_editor_wopi\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tserver_url()\n\t\torigin()\n\t\trewrite_url()\n\t\tif plugin_enable() {\n\t\t\tHooks.Register.XDGOpen(WOPIOverrides)\n\t\t}\n\t})\n\tHooks.Register.HttpEndpoint(WOPIRoutes)\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_console/generator.go",
    "content": "//go:build ignore\n\npackage main\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nfunc main() {\n\tdownload(\n\t\t\"src/xterm.js\",\n\t\t\"https://cdnjs.cloudflare.com/ajax/libs/xterm/3.12.2/xterm.js\",\n\t\t\"https://cdnjs.cloudflare.com/ajax/libs/xterm/3.12.2/addons/fit/fit.js\",\n\t)\n\tdownload(\n\t\t\"src/xterm.css\",\n\t\t\"https://cdnjs.cloudflare.com/ajax/libs/xterm/3.12.2/xterm.css\",\n\t)\n}\n\nfunc download(dst string, urls ...string) {\n\tf, err := os.Create(dst)\n\tif err != nil {\n\t\tlog.Fatalf(\"create %s: %v\", dst, err)\n\t}\n\tdefer f.Close()\n\tfor _, url := range urls {\n\t\tresp, err := http.Get(url)\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"fetch %s: %v\", url, err)\n\t\t}\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\tlog.Fatalf(\"fetch %s: status %d\", url, resp.StatusCode)\n\t\t}\n\t\tif _, err = io.Copy(f, resp.Body); err != nil {\n\t\t\tlog.Fatalf(\"write %s: %v\", dst, err)\n\t\t}\n\t\tresp.Body.Close()\n\t}\n\tlog.Printf(\"wrote %s\", dst)\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_console/index.go",
    "content": "//go:generate go run generator.go\n\npackage plg_handler_console\n"
  },
  {
    "path": "server/plugin/plg_handler_console/index_linux.go",
    "content": "/*\n * This plugin provide a full fledge terminal application. The code was\n * adapted from https://github.com/freman/goterm\n */\npackage plg_handler_console\n\nimport (\n\t_ \"embed\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\t\"unsafe\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"github.com/creack/pty\"\n\t\"github.com/gorilla/mux\"\n\t\"github.com/gorilla/websocket\"\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\n//go:embed src/app.css\nvar AppStyle []byte\n\n//go:embed src/xterm.js\nvar VendorScript []byte // made of xterm.js (https://cdnjs.cloudflare.com/ajax/libs/xterm/3.12.2/xterm.js) and the fit addon(https://cdnjs.cloudflare.com/ajax/libs/xterm/3.12.2/addons/fit/fit.js)\n\n//go:embed src/xterm.css\nvar VendorStyle []byte\n\nvar console_enable = func() bool {\n\treturn Config.Get(\"features.server.console_enable\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Default = false\n\t\tf.Name = \"console_enable\"\n\t\tf.Type = \"boolean\"\n\t\tf.Description = \"Enable/Disable the interactive web console on your instance. It will be available under `/admin/tty/` where username is 'admin' and password is your admin console\"\n\t\tf.Placeholder = \"Default: false\"\n\t\treturn f\n\t}).Bool()\n}\n\nfunc init() {\n\tHooks.Register.HttpEndpoint(func(r *mux.Router) error {\n\t\tif console_enable() == false {\n\t\t\treturn ErrNotFound\n\t\t}\n\t\tr.PathPrefix(\"/admin/tty/\").Handler(\n\t\t\tAuthBasic(\n\t\t\t\tfunc() (string, string) { return \"admin\", Config.Get(\"auth.admin\").String() },\n\t\t\t\tTTYHandler(\"/admin/tty/\"),\n\t\t\t),\n\t\t)\n\t\treturn nil\n\t})\n}\n\nvar notAuthorised = func(res http.ResponseWriter, req *http.Request) {\n\ttime.Sleep(1 * time.Second)\n\tres.Header().Set(\"WWW-Authenticate\", `Basic realm=\"User protect\", charset=\"UTF-8\"`)\n\tres.WriteHeader(http.StatusUnauthorized)\n\tres.Write([]byte(\"Not Authorised\"))\n\treturn\n}\n\nfunc AuthBasic(credentials func() (string, string), fn http.Handler) http.HandlerFunc {\n\treturn func(res http.ResponseWriter, req *http.Request) {\n\t\tif strings.HasSuffix(Config.Get(\"general.host\").String(), \"filestash.app\") {\n\t\t\thttp.NotFoundHandler().ServeHTTP(res, req)\n\t\t\treturn\n\t\t} else if console_enable() == false {\n\t\t\thttp.NotFoundHandler().ServeHTTP(res, req)\n\t\t\treturn\n\t\t}\n\n\t\tauth := req.Header.Get(\"Authorization\")\n\t\tif strings.HasPrefix(auth, \"Basic \") == false {\n\t\t\tnotAuthorised(res, req)\n\t\t\treturn\n\t\t}\n\t\tauth = strings.TrimPrefix(auth, \"Basic \")\n\t\tdecoded, err := base64.StdEncoding.DecodeString(auth)\n\t\tif err != nil {\n\t\t\tnotAuthorised(res, req)\n\t\t\treturn\n\t\t}\n\t\tauth = string(decoded)\n\t\tstuffs := strings.Split(auth, \":\")\n\t\tif len(stuffs) < 2 {\n\t\t\tnotAuthorised(res, req)\n\t\t\treturn\n\t\t}\n\t\tusername := stuffs[0]\n\t\tpassword := strings.Join(stuffs[1:], \":\")\n\t\trefUsername, refPassword := credentials()\n\t\tif refUsername != username {\n\t\t\tLog.Info(\"[tty] username is 'admin'\")\n\t\t\tnotAuthorised(res, req)\n\t\t\treturn\n\t\t} else if len(strings.TrimSpace(password)) < 5 {\n\t\t\tLog.Info(\"[tty] password is too short\")\n\t\t\tnotAuthorised(res, req)\n\t\t\treturn\n\t\t} else if err = bcrypt.CompareHashAndPassword([]byte(refPassword), []byte(password)); err != nil {\n\t\t\tnotAuthorised(res, req)\n\t\t\treturn\n\t\t}\n\t\tfn.ServeHTTP(res, req)\n\t\treturn\n\t}\n}\n\nfunc TTYHandler(pathPrefix string) http.Handler {\n\tif strings.HasSuffix(pathPrefix, \"/\") == false {\n\t\treturn http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {\n\t\t\tres.WriteHeader(http.StatusInternalServerError)\n\t\t\tres.Write([]byte(\"unsafe path prefix, use a '/'\"))\n\t\t\treturn\n\t\t})\n\t}\n\n\treturn http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {\n\t\treq.URL.Path = \"/\" + strings.TrimPrefix(req.URL.Path, pathPrefix)\n\n\t\tif req.Method == \"GET\" {\n\t\t\tif req.URL.Path == \"/\" {\n\t\t\t\tres.Header().Set(\"Content-Type\", \"text/html\")\n\t\t\t\tres.Write(htmlIndex(pathPrefix))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tif req.URL.Path == \"/socket\" {\n\t\t\thandleSocket(res, req)\n\t\t\treturn\n\t\t}\n\t\tres.WriteHeader(http.StatusNotFound)\n\t\tres.Write([]byte(\"NOT FOUND\"))\n\t})\n}\n\nvar upgrader = websocket.Upgrader{\n\tReadBufferSize:  1024,\n\tWriteBufferSize: 1024,\n}\nvar resizeMessage = struct {\n\tRows uint16 `json:\"rows\"`\n\tCols uint16 `json:\"cols\"`\n\tX    uint16\n\tY    uint16\n}{}\n\nfunc handleSocket(res http.ResponseWriter, req *http.Request) {\n\tconn, err := upgrader.Upgrade(res, req, nil)\n\tif err != nil {\n\t\tres.WriteHeader(http.StatusInternalServerError)\n\t\tres.Write([]byte(\"upgrade error\"))\n\t\treturn\n\t}\n\tdefer conn.Close()\n\n\tvar cmd *exec.Cmd\n\tif _, err = exec.LookPath(\"/bin/bash\"); err == nil {\n\t\tbashCommand := `bash --noprofile --init-file <(cat <<EOF\nexport TERM=\"xterm\"\nexport PS1=\"\\[\\033[1;34m\\]\\w\\[\\033[0;37m\\] # \\[\\033[0m\\]\"\nexport EDITOR=\"emacs\"`\n\t\tbashCommand += strings.Join([]string{\n\t\t\t\"\",\n\t\t\t\"export PATH=\" + os.Getenv(\"PATH\"),\n\t\t\t\"export HOME=\" + os.Getenv(\"HOME\"),\n\t\t\t\"\",\n\t\t}, \"\\n\")\n\t\tbashCommand += `\nalias ls='ls --color'\nalias ll='ls -lah'\nEOF\n)`\n\t\tcmd = exec.Command(\"/bin/bash\", \"-c\", bashCommand)\n\t} else if _, err = exec.LookPath(\"/bin/sh\"); err == nil {\n\t\tcmd = exec.Command(\"/bin/sh\")\n\t\tcmd.Env = []string{\n\t\t\t\"TERM=xterm\",\n\t\t\t\"PATH=\" + os.Getenv(\"PATH\"),\n\t\t\t\"HOME=\" + os.Getenv(\"HOME\"),\n\t\t}\n\t} else {\n\t\tres.WriteHeader(http.StatusNotFound)\n\t\tres.Write([]byte(\"No terminal found\"))\n\t\treturn\n\t}\n\n\ttty, err := pty.Start(cmd)\n\tif err != nil {\n\t\tLog.Debug(\"plugin::plg_handler_console pty.Start error '%s'\", err)\n\t\tconn.WriteMessage(websocket.TextMessage, []byte(err.Error()))\n\t\treturn\n\t}\n\tdefer func() {\n\t\tcmd.Process.Kill()\n\t\tcmd.Process.Wait()\n\t\ttty.Close()\n\t}()\n\n\tgo func() {\n\t\tfor {\n\t\t\tbuf := make([]byte, 1024)\n\t\t\tread, err := tty.Read(buf)\n\t\t\tif err != nil {\n\t\t\t\tconn.WriteMessage(websocket.TextMessage, []byte(err.Error()))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconn.WriteMessage(websocket.BinaryMessage, buf[:read])\n\t\t}\n\t}()\n\n\tfor {\n\t\tmessageType, reader, err := conn.NextReader()\n\t\tif err != nil {\n\t\t\treturn\n\t\t} else if messageType == websocket.TextMessage {\n\t\t\tconn.WriteMessage(websocket.TextMessage, []byte(\"Unexpected text message\"))\n\t\t\tcontinue\n\t\t}\n\n\t\tdataTypeBuf := make([]byte, 1)\n\t\tread, err := reader.Read(dataTypeBuf)\n\t\tif err != nil {\n\t\t\tconn.WriteMessage(websocket.TextMessage, []byte(\"Unable to read message type from reader\"))\n\t\t\treturn\n\t\t} else if read != 1 {\n\t\t\treturn\n\t\t}\n\n\t\tswitch dataTypeBuf[0] {\n\t\tcase 0:\n\t\t\tif _, err := io.Copy(tty, reader); err != nil {\n\t\t\t\tconn.WriteMessage(websocket.TextMessage, []byte(\"Error copying bytes: \"+err.Error()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\tcase 1:\n\t\t\tdecoder := json.NewDecoder(reader)\n\t\t\tif err := decoder.Decode(&resizeMessage); err != nil {\n\t\t\t\tconn.WriteMessage(websocket.TextMessage, []byte(\"Error decoding resize message: \"+err.Error()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif _, _, errno := syscall.Syscall(\n\t\t\t\tsyscall.SYS_IOCTL,\n\t\t\t\ttty.Fd(),\n\t\t\t\tsyscall.TIOCSWINSZ,\n\t\t\t\tuintptr(unsafe.Pointer(&resizeMessage)),\n\t\t\t); errno != 0 {\n\t\t\t\tconn.WriteMessage(websocket.TextMessage, []byte(\"Unable to resize terminal: \"+err.Error()))\n\t\t\t}\n\t\tdefault:\n\t\t\tconn.WriteMessage(websocket.TextMessage, []byte(\"Unknown data type: \"+err.Error()))\n\t\t}\n\t}\n}\n\nfunc htmlIndex(pathPrefix string) []byte {\n\treturn []byte(`<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\" name=\"viewport\">\n    <meta content=\"yes\" name=\"apple-mobile-web-app-capable\">\n    <meta content=\"name\" name=\"apple-mobile-web-app-title\">\n    <meta content=\"black-translucent\" name=\"apple-mobile-web-app-status-bar-style\">\n    <title></title>\n    <script>` + string(VendorScript) + `</script>\n    <style>` + string(VendorStyle) + `</style>\n    <style>` + string(AppStyle) + `</style>\n    <style>body{ background: #1d1f21; }</style>\n  </head>\n  <body>\n    <div id=\"terminal\"></div>\n    <div id=\"error-message\"></div>\n    <script>` + AppScript(pathPrefix) + `</script>    \n  </body>\n</html>`)\n}\n\nfunc AppScript(pathPrefix string) string {\n\treturn `\n\n(function() {\n    Terminal.applyAddon(fit);\n    var term;\n    function Boot() {\n        term = new Terminal({\n            cursorBlink: true,\n            theme: {\n                background: \"#1d1f21\",\n                foreground: \"#c5c8c6\",\n                cursor: \"#c5c8c6\",\n                black: \"#282a2e\",\n                brightBlack: \"#373b41\",\n                red: \"#cc645a\",\n                brightRed: \"#cc6666\",\n                green: \"#5fa88d\",\n                brightGreen: \"#aebd66\",\n                yellow: \"#f0c666\",\n                brightYellow: \"#f0c673\",\n                blue: \"#709dbe\",\n                brightBlue: \"#81a2be\",\n                magenta: \"#b394ba\",\n                brightMagenta: \"#b394ba\",\n                cyan: \"#88beb3\",\n                brightCyan: \"#8bbfb6\",\n                white: \"#707880\"\n            }\n        });\n\n        var websocket = new WebSocket(\n            (location.protocol === \"https:\" ? \"wss://\" : \"ws://\") +\n            location.hostname + ((location.port) ? (\":\" + location.port) : \"\") +\n            \"` + pathPrefix + `socket\"\n        );\n        websocket.binaryType = \"arraybuffer\";\n\n        websocket.onopen = function(e) {\n            term.open(document.getElementById(\"terminal\"));\n            term.fit();\n            term.on(\"data\", function(data) {\n                websocket.send(new TextEncoder().encode(\"\\x00\" + data));\n                websocket.send(new TextEncoder().encode(\"\\x01\" + JSON.stringify({cols: term.cols, rows: term.rows})))\n            });\n            term.on('resize', function(evt) {\n                term.fit();\n                websocket.send(new TextEncoder().encode(\"\\x01\" + JSON.stringify({cols: evt.cols, rows: evt.rows})))\n            });\n            window.onresize = function() {\n                term.fit();\n            }\n            term.on('title', function(title) {\n                document.title = title;\n            });\n        }\n\n        websocket.onmessage = function(e) {\n            if (e.data instanceof ArrayBuffer) {\n                term.write(String.fromCharCode.apply(null, new Uint8Array(e.data)));\n                return;\n            }\n            websocket.close()\n            term.destroy();\n            alert(\"Something went wrong\");\n        }\n\n        websocket.onclose = function(){\n            term.write(\"Session terminated\");\n            term.destroy();\n        }\n\n        websocket.onerror = function(e){\n            var $term = document.getElementById(\"terminal\");\n            if($term) $term.remove();\n            document.getElementById(\"terminal\").remove()\n            document.getElementById(\"error-message\").innerText = \"Websocket Error\";\n        }\n    }\n    Boot();\n})()`\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_console/src/app.css",
    "content": "html{ height: 100%; }\nbody{ margin: 0; height: 100%; padding: 10px 0 0px 10px; box-sizing: border-box; }\n#terminal{ height: 100%; }\n#error-message{\n    text-align: center;\n    font-size: 1.5em;\n    color: #333;\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/README.md",
    "content": "This is a MCP plugin acting as an interface for AI agent to do stuff on a remote storage you have configured in Filestash, be it a FTP server, SFTP, S3, WebDAV, SMB, GIT, NFS, ....\n\n<img src=\"https://i.imgur.com/Ekj7AK5.png\" />\n\n- demo server: https://demo.filestash.app/sse\n- release note: https://www.filestash.app/2025/04/01/mcp-feature/\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/config/config.go",
    "content": "package plg_handler_mcp\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar PluginEnable = func() bool {\n\treturn Config.Get(\"features.mcp.enable\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"enable\"\n\t\tf.Type = \"enable\"\n\t\tf.Target = []string{}\n\t\tf.Description = \"Enable/Disable the Model Context Protocol\"\n\t\tf.Default = false\n\t\treturn f\n\t}).Bool()\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/handler.go",
    "content": "package plg_handler_mcp\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/impl\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/types\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/utils\"\n\n\t\"github.com/google/uuid\"\n)\n\nconst SESSION_TIME = 60\n\nfunc (this *Server) messageHandler(_ *App, w http.ResponseWriter, r *http.Request) {\n\tsessionID := r.URL.Query().Get(\"sessionId\")\n\tif r.Method != http.MethodPost {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tw.Write([]byte(\"Invalid Request\"))\n\t\treturn\n\t}\n\trequest := JSONRPCRequest{}\n\tif err := json.NewDecoder(r.Body).Decode(&request); err != nil {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tw.Write([]byte(\"ERR: \" + err.Error()))\n\t\treturn\n\t}\n\tthis.GetSession(sessionID).Chan <- request\n\tw.WriteHeader(http.StatusNoContent)\n}\n\nfunc (this *Server) sseHandler(_ *App, w http.ResponseWriter, r *http.Request) {\n\ttoken := ExtractToken(r)\n\tif token == \"\" {\n\t\tLog.Debug(\"plg_handler_mcp::sse msg=invalid_token\")\n\t\tw.Header().Add(\"Content-Type\", \"application/json\")\n\t\tw.Header().Add(\"WWW-Authenticate\", \"Bearer resource_metadata=\\\"\"+this.baseURL(r)+\"/.well-known/oauth-protected-resource\\\"\")\n\t\tw.WriteHeader(http.StatusUnauthorized)\n\t\tjson.NewEncoder(w).Encode(JSONRPCResponse{\n\t\t\tJSONRPC: \"2.0\",\n\t\t\tError: &JSONRPCError{\n\t\t\t\tCode:    http.StatusUnauthorized,\n\t\t\t\tMessage: \"Missing or invalid access token\",\n\t\t\t},\n\t\t})\n\t\treturn\n\t}\n\n\tuserSession := this.GetSession(uuid.New().String())\n\tuserSession.Token = token\n\tif b, err := getBackend(userSession.Token); err == nil {\n\t\tuserSession.HomeDir, _ = model.GetHome(b, \"/\")\n\t\tuserSession.CurrDir = ToString(userSession.HomeDir, \"/\")\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"text/event-stream\")\n\tw.Header().Set(\"Cache-Control\", \"no-cache\")\n\tw.Header().Set(\"Connection\", \"keep-alive\")\n\n\tfmt.Fprintf(w, \"event: endpoint\\ndata: %s?sessionId=%s\\n\\n\", \"/messages\", userSession.Id)\n\tw.(http.Flusher).Flush()\n\n\tfor {\n\t\tselect {\n\t\tcase request := <-userSession.Chan:\n\t\t\tb, err := getBackend(userSession.Token)\n\t\t\tif err != nil {\n\t\t\t\tif err == ErrNotAuthorized {\n\t\t\t\t\terr = JSONRPCError{\n\t\t\t\t\t\tCode:    ErrNotAuthorized.Status(),\n\t\t\t\t\t\tMessage: \"You aren't authenticated\",\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tSendError(w, request.ID, err)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tuserSession.Backend = b\n\n\t\t\tswitch request.Method {\n\t\t\tcase \"initialize\":\n\t\t\t\tSendMessage(w, request.ID, InitializeResponse{\n\t\t\t\t\tProtocolVersion: \"2024-11-05\",\n\t\t\t\t\tServerInfo: ServerInfo{\n\t\t\t\t\t\tName:    \"Universal Storage Server\",\n\t\t\t\t\t\tVersion: \"1.0.0\",\n\t\t\t\t\t},\n\t\t\t\t\tCapabilities: Capabilities{\n\t\t\t\t\t\tTools:     map[string]interface{}{},\n\t\t\t\t\t\tResources: map[string]interface{}{},\n\t\t\t\t\t\tPrompts:   map[string]interface{}{},\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\tcase \"resources/list\":\n\t\t\t\tSendMessage(w, request.ID, &ResourcesListResponse{\n\t\t\t\t\tResources: AllResources(),\n\t\t\t\t})\n\t\t\tcase \"resources/templates/list\":\n\t\t\t\tSendMessage(w, request.ID, &ResourceTemplatesListResponse{\n\t\t\t\t\tResourceTemplates: AllResourceTemplates(),\n\t\t\t\t})\n\t\t\tcase \"resources/read\":\n\t\t\t\tif uri, ok := request.Params[\"uri\"].(string); ok {\n\t\t\t\t\tif resource, err := FindResource(uri); err != nil {\n\t\t\t\t\t\tSendError(w, request.ID, JSONRPCError{\n\t\t\t\t\t\t\tCode:    http.StatusBadRequest,\n\t\t\t\t\t\t\tMessage: fmt.Sprintf(\"Unknown tool: %s\", request.Params[\"name\"]),\n\t\t\t\t\t\t})\n\t\t\t\t\t} else {\n\t\t\t\t\t\tSendMessage(w, request.ID, &ResourceReadResponse{\n\t\t\t\t\t\t\tContents: []ResourceContent{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tURI:      uri,\n\t\t\t\t\t\t\t\t\tMimeType: resource.MimeType,\n\t\t\t\t\t\t\t\t\tText:     resource.Content,\n\t\t\t\t\t\t\t\t\tMeta:     resource.Meta,\n\t\t\t\t\t\t\t\t},\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} else {\n\t\t\t\t\tSendError(w, request.ID, JSONRPCError{\n\t\t\t\t\t\tCode:    http.StatusBadRequest,\n\t\t\t\t\t\tMessage: fmt.Sprintf(\"Unexpected parameters: %v\", request.Params),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tSendMessage(w, request.ID, &ResourceReadResponse{\n\t\t\t\t\tContents: ExecResourceRead(request.Params),\n\t\t\t\t})\n\t\t\tcase \"prompts/list\":\n\t\t\t\tSendMessage(w, request.ID, &PromptsListResponse{\n\t\t\t\t\tPrompts: AllPrompts(),\n\t\t\t\t})\n\t\t\tcase \"prompts/get\":\n\t\t\t\tif m, ok := request.Params[\"name\"].(string); ok {\n\t\t\t\t\tres, err := ExecPromptGet(m, request.Params, &userSession)\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\tSendMessage(w, request.ID, PromptGetResponse{\n\t\t\t\t\t\t\tMessages:    res,\n\t\t\t\t\t\t\tDescription: ExecPromptDescription(request.Params),\n\t\t\t\t\t\t})\n\t\t\t\t\t} else {\n\t\t\t\t\t\tSendError(w, request.ID, err)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tSendError(w, request.ID, JSONRPCError{\n\t\t\t\t\t\tCode:    http.StatusBadRequest,\n\t\t\t\t\t\tMessage: fmt.Sprintf(\"Unexpected parameters: %v\", request.Params),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\tcase \"tools/list\":\n\t\t\t\tSendMessage(w, request.ID, &ListToolsResponse{\n\t\t\t\t\tTools: AllTools(),\n\t\t\t\t})\n\t\t\tcase \"tools/call\":\n\t\t\t\tif tname, ok := request.Params[\"name\"].(string); ok {\n\t\t\t\t\tif tool, err := FindTool(tname); err != nil {\n\t\t\t\t\t\tSendError(w, request.ID, JSONRPCError{\n\t\t\t\t\t\t\tCode:    http.StatusBadRequest,\n\t\t\t\t\t\t\tMessage: fmt.Sprintf(\"Unknown tool: %s\", request.Params[\"name\"]),\n\t\t\t\t\t\t})\n\t\t\t\t\t} else if res, err := tool.Run(request.Params, &userSession); err != nil {\n\t\t\t\t\t\tSendMessage(w, request.ID, ToolResponse{\n\t\t\t\t\t\t\tContent: []TextContent{{\"text\", err.Error()}},\n\t\t\t\t\t\t\tIsError: true,\n\t\t\t\t\t\t})\n\t\t\t\t\t} else {\n\t\t\t\t\t\tSendMessage(w, request.ID, res)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tSendError(w, request.ID, JSONRPCError{\n\t\t\t\t\t\tCode:    http.StatusBadRequest,\n\t\t\t\t\t\tMessage: fmt.Sprintf(\"Unexpected parameters: %v\", request.Params),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\tcase \"notifications/initialized\":\n\t\t\t\tSendMessage(w, request.ID, map[string]string{})\n\t\t\tcase \"completion/complete\":\n\t\t\t\tSendMessage(w, request.ID, CompletionResponse{\n\t\t\t\t\tCompletion: ExecCompletion(request.Params, &userSession),\n\t\t\t\t})\n\t\t\tcase \"ping\":\n\t\t\t\tSendMessage(w, request.ID, map[string]string{})\n\t\t\tdefault:\n\t\t\t\tif request.Method == \"\" && userSession.Ping.ID == request.ID { // response to ping\n\t\t\t\t\tuserSession.Ping.LastResponse = time.Now()\n\t\t\t\t\tuserSession.Ping.ID += 1\n\t\t\t\t} else {\n\t\t\t\t\tLog.Warning(\"plg_handler_mcp::sse message=unknown_method method=%s requestID=%d\", request.Method, request.ID)\n\t\t\t\t\tSendError(w, request.ID, JSONRPCError{\n\t\t\t\t\t\tCode:    http.StatusMethodNotAllowed,\n\t\t\t\t\t\tMessage: fmt.Sprintf(\"Unknown request: %s\", request.Method),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\tcase <-r.Context().Done():\n\t\t\tthis.RemoveSession(&userSession)\n\t\t\treturn\n\t\tcase <-time.After(15 * time.Second):\n\t\t\tSendPing(w, userSession.Ping.ID)\n\t\t\tif time.Since(userSession.Ping.LastResponse) > SESSION_TIME*time.Second {\n\t\t\t\tSendMethod(w, userSession.Ping.ID+1, \"notifications/cancelled\", map[string]interface{}{\n\t\t\t\t\t\"requestId\": userSession.Ping.ID,\n\t\t\t\t\t\"reason\":    \"Request timed out\",\n\t\t\t\t})\n\t\t\t\ttime.Sleep(2 * time.Second)\n\t\t\t\tthis.RemoveSession(&userSession)\n\t\t\t\tif hi, ok := w.(http.Hijacker); ok {\n\t\t\t\t\tif conn, rw, err := hi.Hijack(); err == nil {\n\t\t\t\t\t\trw.WriteString(\"0\\r\\n\\r\\n\")\n\t\t\t\t\t\trw.Flush()\n\t\t\t\t\t\ttime.Sleep(1 * time.Second)\n\t\t\t\t\t\tconn.Close()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc getBackend(token string) (IBackend, error) {\n\tstr, err := DecryptString(SECRET_KEY_DERIVATE_FOR_USER, token)\n\tif err != nil {\n\t\treturn nil, ErrNotAuthorized\n\t}\n\tsession := map[string]string{}\n\tif err = json.Unmarshal([]byte(str), &session); err != nil {\n\t\treturn nil, err\n\t}\n\treturn model.NewBackend(&App{\n\t\tContext: context.Background(),\n\t}, session)\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/handler_auth.go",
    "content": "package plg_handler_mcp\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nconst (\n\tDEFAULT_TOKEN_EXPIRY  = 3600\n\tDEFAULT_SECRET_EXPIRY = 30 * 24 * 3600\n)\n\nvar KEY_FOR_CODE string\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tKEY_FOR_CODE = Hash(\"MCP_CODE_\"+SECRET_KEY, len(SECRET_KEY))\n\t})\n}\n\nfunc (this Server) WellKnownOAuthAuthorizationServerHandler(_ *App, w http.ResponseWriter, r *http.Request) {\n\tbaseURL := this.baseURL(r)\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(map[string]interface{}{\n\t\t\"issuer\":                   baseURL,\n\t\t\"authorization_endpoint\":   fmt.Sprintf(\"%s/mcp/authorize\", baseURL),\n\t\t\"token_endpoint\":           fmt.Sprintf(\"%s/mcp/token\", baseURL),\n\t\t\"registration_endpoint\":    fmt.Sprintf(\"%s/mcp/register\", baseURL),\n\t\t\"response_types_supported\": []string{\"code\"},\n\t\t\"grant_types_supported\":    []string{\"authorization_code\"},\n\t\t\"token_endpoint_auth_methods_supported\": []string{\n\t\t\t\"none\",\n\t\t},\n\t\t\"code_challenge_methods_supported\": []string{\n\t\t\t\"S256\",\n\t\t},\n\t})\n}\n\nfunc (this Server) WellKnownOAuthProtectedResourceHandler(_ *App, w http.ResponseWriter, r *http.Request) {\n\tbaseURL := this.baseURL(r)\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(map[string]interface{}{\n\t\t\"resource\":                 baseURL,\n\t\t\"authorization_servers\":    []string{baseURL},\n\t\t\"bearer_methods_supported\": []string{\"header\"},\n\t\t\"scopes_supported\":         []string{\"openid\"},\n\t})\n}\n\nfunc (this Server) baseURL(r *http.Request) string {\n\tscheme := \"https\"\n\thost := r.Host\n\tif strings.HasPrefix(host, \"localhost\") || strings.HasPrefix(host, \"127.0.0.1\") {\n\t\tscheme = \"http\"\n\t}\n\treturn fmt.Sprintf(\"%s://%s\", scheme, host)\n}\n\nfunc (this Server) AuthorizeHandler(_ *App, w http.ResponseWriter, r *http.Request) {\n\tresponseType := r.URL.Query().Get(\"response_type\")\n\tclientID := r.URL.Query().Get(\"client_id\")\n\tredirectURI := r.URL.Query().Get(\"redirect_uri\")\n\tstate := r.URL.Query().Get(\"state\")\n\n\tif responseType != \"code\" {\n\t\thttp.Error(w, \"response_type must be 'code'\", http.StatusBadRequest)\n\t\treturn\n\t} else if clientID == \"\" {\n\t\thttp.Error(w, \"client_id is required\", http.StatusBadRequest)\n\t\treturn\n\t} else if redirectURI == \"\" {\n\t\thttp.Error(w, \"redirect_uri is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, fmt.Sprintf(\n\t\t\"/login?next=/api/mcp?redirect_uri=%s%%26state=%s%%26client_id=%s\",\n\t\tredirectURI, state, clientID,\n\t), http.StatusSeeOther)\n}\n\nfunc (this Server) TokenHandler(_ *App, w http.ResponseWriter, r *http.Request) {\n\tif err := r.ParseForm(); err != nil {\n\t\thttp.Error(w, \"Invalid request\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif grantType := r.FormValue(\"grant_type\"); grantType != \"authorization_code\" {\n\t\thttp.Error(w, \"Invalid Grant Type\", http.StatusBadRequest)\n\t\treturn\n\t}\n\ttoken, err := DecryptString(KEY_FOR_CODE, r.FormValue(\"code\"))\n\tif err != nil {\n\t\thttp.Error(w, \"Invalid authorization code\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(map[string]interface{}{\n\t\t\"access_token\": token,\n\t\t\"token_type\":   \"Bearer\",\n\t})\n}\n\nfunc (this Server) RegisterHandler(ctx *App, w http.ResponseWriter, r *http.Request) {\n\tclientName := regexp.MustCompile(\"[^a-zA-Z0-9\\\\-]+\").ReplaceAllString(\n\t\tfmt.Sprintf(\"%s\", ctx.Body[\"client_name\"]),\n\t\t\"\",\n\t)\n\tclientID := clientName + \".\" + Hash(clientName+time.Now().String(), 8)\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusCreated)\n\tjson.NewEncoder(w).Encode(struct {\n\t\tClientID                string   `json:\"client_id\"`\n\t\tClientSecret            string   `json:\"client_secret\"`\n\t\tClientIDIssuedAt        int64    `json:\"client_id_issued_at\"`\n\t\tClientSecretExpiresAt   int64    `json:\"client_secret_expires_at\"`\n\t\tClientName              string   `json:\"client_name\"`\n\t\tRedirectURIs            []string `json:\"redirect_uris\"`\n\t\tGrantTypes              []string `json:\"grant_types\"`\n\t\tTokenEndpointAuthMethod string   `json:\"token_endpoint_auth_method\"`\n\t}{\n\t\tClientID:                clientID,\n\t\tClientSecret:            Hash(clientID, 32), // unused. eg: chatgpt act as public client\n\t\tClientIDIssuedAt:        time.Now().Unix(),\n\t\tClientSecretExpiresAt:   time.Now().Unix() + DEFAULT_SECRET_EXPIRY,\n\t\tClientName:              clientName,\n\t\tRedirectURIs:            []string{},\n\t\tGrantTypes:              []string{\"authorization_code\"},\n\t\tTokenEndpointAuthMethod: \"none\",\n\t})\n}\n\nfunc (this Server) CallbackHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\turi := req.URL.Query().Get(\"redirect_uri\")\n\tstate := req.URL.Query().Get(\"state\")\n\tif uri == \"\" {\n\t\tSendErrorResult(res, ErrNotValid)\n\t\treturn\n\t}\n\tcode, err := EncryptString(KEY_FOR_CODE, ctx.Authorization)\n\tif err != nil {\n\t\tSendErrorResult(res, ErrNotValid)\n\t\treturn\n\t}\n\turi += \"?code=\" + code\n\tif state != \"\" {\n\t\turi += \"&state=\" + state\n\t}\n\thttp.Redirect(res, req, uri, http.StatusSeeOther)\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/handler_state.go",
    "content": "package plg_handler_mcp\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/types\"\n)\n\nfunc (this *Server) RemoveSession(userSession *UserSession) {\n\tthis.sessions.Delete(userSession.Id)\n}\n\nfunc ExtractToken(r *http.Request) string {\n\tauthHeader := r.Header.Get(\"Authorization\")\n\tif hasToken := strings.HasPrefix(authHeader, \"Bearer \"); hasToken == false {\n\t\treturn \"\"\n\t}\n\treturn strings.TrimPrefix(authHeader, \"Bearer \")\n}\n\nfunc (this *Server) GetSession(uuid string) UserSession {\n\tch, _ := this.sessions.LoadOrStore(uuid, UserSession{\n\t\tId:      uuid,\n\t\tChan:    make(chan JSONRPCRequest),\n\t\tCurrDir: \"/\",\n\t\tHomeDir: \"/\",\n\t\tPing: Ping{\n\t\t\tID:           0,\n\t\t\tLastResponse: time.Now(),\n\t\t},\n\t})\n\treturn ch.(UserSession)\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/impl/completion.go",
    "content": "package impl\n\nimport (\n\t\"path/filepath\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/types\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/utils\"\n)\n\nfunc ExecCompletion(params map[string]any, userSession *UserSession) Completion {\n\tif path := GetArgumentString(params, \"value\"); path != \"\" && GetArgumentString(params, \"name\") == \"path\" {\n\t\tfpath := filepath.Dir(path)\n\t\tfname := filepath.Base(path)\n\t\tif strings.HasSuffix(path, \"/\") {\n\t\t\tfname = \"\"\n\t\t}\n\t\tfiles, err := userSession.Backend.Ls(EnforceDirectory(fpath))\n\t\tif err == nil {\n\t\t\tvalues := []string{}\n\t\t\tfor _, file := range files {\n\t\t\t\tval := JoinPath(fpath, file.Name())\n\t\t\t\tif file.IsDir() {\n\t\t\t\t\tval = EnforceDirectory(val)\n\t\t\t\t}\n\n\t\t\t\tif fname == \"\" && strings.HasPrefix(file.Name(), \".\") == false {\n\t\t\t\t\tvalues = append(values, val)\n\t\t\t\t} else if fname != \"\" && strings.HasPrefix(file.Name(), fname) {\n\t\t\t\t\tvalues = append(values, val)\n\t\t\t\t}\n\n\t\t\t\tif len(values) >= 100 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Completion{\n\t\t\t\tValues:  values,\n\t\t\t\tTotal:   uint64(len(values)),\n\t\t\t\tHasMore: false,\n\t\t\t}\n\t\t}\n\t}\n\treturn Completion{\n\t\tValues:  []string{},\n\t\tTotal:   0,\n\t\tHasMore: false,\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/impl/prompts.go",
    "content": "package impl\n\nimport (\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/types\"\n)\n\nvar listOfPrompts = map[string]PromptDefinition{}\n\ntype PromptDefinition struct {\n\tPrompt\n\tExecMessage     func(params map[string]any, userSession *UserSession) ([]PromptMessage, error)\n\tExecDescription func(params map[string]any) string\n}\n\nfunc RegisterPrompt(t PromptDefinition) {\n\tlistOfPrompts[t.Name] = t\n}\n\nfunc AllPrompts() []Prompt {\n\tt := []Prompt{}\n\tfor _, v := range listOfPrompts {\n\t\tt = append(t, v.Prompt)\n\t}\n\treturn t\n}\n\nfunc ExecPromptGet(name string, params map[string]any, userSession *UserSession) ([]PromptMessage, error) {\n\tt, ok := listOfPrompts[name]\n\tif !ok {\n\t\treturn nil, &JSONRPCError{\n\t\t\tCode:    http.StatusNotFound,\n\t\t\tMessage: \"Not Found\",\n\t\t}\n\t}\n\treturn t.ExecMessage(params, userSession)\n}\n\nfunc ExecPromptDescription(params map[string]any) string {\n\tn, ok := params[\"name\"].(string)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\treturn n\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/impl/prompts_fs.go",
    "content": "package impl\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/types\"\n)\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tRegisterPrompt(PromptDefinition{\n\t\t\tPrompt: Prompt{\n\t\t\t\tName:        \"ls\",\n\t\t\t\tDescription: \"list directory contents\",\n\t\t\t\tArguments: []PromptArgument{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:        \"path\",\n\t\t\t\t\t\tDescription: \"path where the query is made\",\n\t\t\t\t\t\tRequired:    false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tExecMessage: func(params map[string]any, userSession *UserSession) ([]PromptMessage, error) {\n\t\t\t\treturn []PromptMessage{\n\t\t\t\t\t{\n\t\t\t\t\t\tRole: \"user\",\n\t\t\t\t\t\tContent: TextContent{\n\t\t\t\t\t\t\tType: \"text\",\n\t\t\t\t\t\t\tText: \"call ls(path)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}, nil\n\t\t\t},\n\t\t\tExecDescription: func(params map[string]any) string {\n\t\t\t\treturn \"list directory contents\"\n\t\t\t},\n\t\t})\n\n\t\tRegisterPrompt(PromptDefinition{\n\t\t\tPrompt: Prompt{\n\t\t\t\tName:        \"cat\",\n\t\t\t\tDescription: \"read a file at a specified path\",\n\t\t\t\tArguments: []PromptArgument{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:        \"path\",\n\t\t\t\t\t\tDescription: \"path where the query is made\",\n\t\t\t\t\t\tRequired:    true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tExecMessage: func(params map[string]any, userSession *UserSession) ([]PromptMessage, error) {\n\t\t\t\treturn []PromptMessage{\n\t\t\t\t\t{\n\t\t\t\t\t\tRole: \"user\",\n\t\t\t\t\t\tContent: TextContent{\n\t\t\t\t\t\t\tType: \"text\",\n\t\t\t\t\t\t\tText: \"call cat(path)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}, nil\n\t\t\t},\n\t\t\tExecDescription: func(params map[string]any) string {\n\t\t\t\treturn \"read a file at a specified path\"\n\t\t\t},\n\t\t})\n\n\t\tRegisterPrompt(PromptDefinition{\n\t\t\tPrompt: Prompt{\n\t\t\t\tName:        \"pwd\",\n\t\t\t\tDescription: \"print name of current/working directory\",\n\t\t\t\tArguments:   []PromptArgument{},\n\t\t\t},\n\t\t\tExecMessage: func(params map[string]any, userSession *UserSession) ([]PromptMessage, error) {\n\t\t\t\treturn []PromptMessage{\n\t\t\t\t\t{\n\t\t\t\t\t\tRole: \"user\",\n\t\t\t\t\t\tContent: TextContent{\n\t\t\t\t\t\t\tType: \"text\",\n\t\t\t\t\t\t\tText: \"call pwd\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}, nil\n\t\t\t},\n\t\t\tExecDescription: func(params map[string]any) string {\n\t\t\t\treturn \"print name of current/working directory\"\n\t\t\t},\n\t\t})\n\n\t\tRegisterPrompt(PromptDefinition{\n\t\t\tPrompt: Prompt{\n\t\t\t\tName:        \"cd\",\n\t\t\t\tDescription: \"change the working directory\",\n\t\t\t\tArguments: []PromptArgument{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:        \"path\",\n\t\t\t\t\t\tDescription: \"path where the query is made\",\n\t\t\t\t\t\tRequired:    false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tExecMessage: func(params map[string]any, userSession *UserSession) ([]PromptMessage, error) {\n\t\t\t\treturn []PromptMessage{\n\t\t\t\t\t{\n\t\t\t\t\t\tRole: \"user\",\n\t\t\t\t\t\tContent: TextContent{\n\t\t\t\t\t\t\tType: \"text\",\n\t\t\t\t\t\t\tText: \"call cd(path)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}, nil\n\t\t\t},\n\t\t\tExecDescription: func(params map[string]any) string {\n\t\t\t\treturn \"change the working directory\"\n\t\t\t},\n\t\t})\n\n\t\tRegisterPrompt(PromptDefinition{\n\t\t\tPrompt: Prompt{\n\t\t\t\tName:        \"save\",\n\t\t\t\tDescription: \"save content to a file at the desired path\",\n\t\t\t\tArguments: []PromptArgument{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:        \"path\",\n\t\t\t\t\t\tDescription: \"path where the file is stored\",\n\t\t\t\t\t\tRequired:    true,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tName:        \"content\",\n\t\t\t\t\t\tDescription: \"content of the file\",\n\t\t\t\t\t\tRequired:    true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tExecMessage: func(params map[string]any, userSession *UserSession) ([]PromptMessage, error) {\n\t\t\t\treturn []PromptMessage{\n\t\t\t\t\t{\n\t\t\t\t\t\tRole: \"user\",\n\t\t\t\t\t\tContent: TextContent{\n\t\t\t\t\t\t\tType: \"text\",\n\t\t\t\t\t\t\tText: \"call save(path, content)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}, nil\n\t\t\t},\n\t\t\tExecDescription: func(params map[string]any) string {\n\t\t\t\treturn \"save content to a file at the desired path\"\n\t\t\t},\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/impl/public/file-list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <meta charset=\"UTF-8\" />\n        <title>File List</title>\n        <style>\n         :root {\n             --color: #1d1c1c;\n             --light: #5b5d5c;\n             --border: #f1f1f1;\n         }\n         body { font-family: system-ui, sans-serif; color: var(--color); margin: 0; }\n         #app { max-width: 800px; }\n         .ellipsis { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }\n         h1 { font-size: 1.7rem; padding: 10px 10px 5px 10px; margin: 0; }\n         .component_thing { display: flex; border-bottom: 1px solid var(--border); padding: 7px 10px; align-items: baseline; justify-content: space-between; }\n         .component_thing .component_icon { position: relative; top: 4px; width: 20px; }\n         .component_thing .component_datetime { float: right; color: var(--light); font-size: 0.9rem; }\n        </style>\n    </head>\n    <body>\n        <div id=\"app\"></div>\n\n        <script>\n         const ICON = {\n             DIRECTORY: \"PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjAgMjAiIGZpbGw9ImN1cnJlbnRDb2xvciI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xLjY2NjY5IDVDMS42NjY2OSAzLjYxOTI5IDIuNzg1OTggMi41IDQuMTY2NjkgMi41SDcuNjQzQzguMzA2MDQgMi41IDguOTQxOTIgMi43NjMzOSA5LjQxMDc3IDMuMjMyMjNMMTAuMTAxMSAzLjkyMjU5QzEwLjI1NzQgNC4wNzg4NyAxMC40Njk0IDQuMTY2NjcgMTAuNjkwNCA0LjE2NjY3SDE1LjgzMzRDMTcuMjE0MSA0LjE2NjY3IDE4LjMzMzQgNS4yODU5NSAxOC4zMzM0IDYuNjY2NjdWMTVDMTguMzMzNCAxNi4zODA3IDE3LjIxNDEgMTcuNSAxNS44MzM0IDE3LjVINC4xNjY2OUMyLjc4NTk4IDE3LjUgMS42NjY2OSAxNi4zODA3IDEuNjY2NjkgMTVWNVpNNC4xNjY2OSA0LjE2NjY3QzMuNzA2NDUgNC4xNjY2NyAzLjMzMzM1IDQuNTM5NzYgMy4zMzMzNSA1VjguMzMzMzNIMTYuNjY2N1Y2LjY2NjY3QzE2LjY2NjcgNi4yMDY0MyAxNi4yOTM2IDUuODMzMzMgMTUuODMzNCA1LjgzMzMzSDEwLjY5MDRDMTAuMDI3MyA1LjgzMzMzIDkuMzkxNDUgNS41Njk5NCA4LjkyMjYxIDUuMTAxMUw4LjIzMjI1IDQuNDEwNzRDOC4wNzU5NyA0LjI1NDQ2IDcuODY0MDEgNC4xNjY2NyA3LjY0MyA0LjE2NjY3SDQuMTY2NjlaTTE2LjY2NjcgMTBIMy4zMzMzNVYxNUMzLjMzMzM1IDE1LjQ2MDIgMy43MDY0NSAxNS44MzMzIDQuMTY2NjkgMTUuODMzM0gxNS44MzM0QzE2LjI5MzYgMTUuODMzMyAxNi42NjY3IDE1LjQ2MDIgMTYuNjY2NyAxNVYxMFoiPjwvcGF0aD48L3N2Zz4=\",\n             FILE: \"PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9ImN1cnJlbnRDb2xvciI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNyAyYTMgMyAwIDAgMC0zIDN2MTRhMyAzIDAgMCAwIDMgM2gxMGEzIDMgMCAwIDAgMy0zVjguODI4YTMgMyAwIDAgMC0uODc5LTIuMTJsLTMuODI4LTMuODNBMyAzIDAgMCAwIDEzLjE3MiAySDdabTUgMkg3YTEgMSAwIDAgMC0xIDF2MTRhMSAxIDAgMCAwIDEgMWgxMGExIDEgMCAwIDAgMS0xdi05aC0zYTMgMyAwIDAgMS0zLTNWNFptNS41ODYgNEgxNWExIDEgMCAwIDEtMS0xVjQuNDE0TDE3LjU4NiA4WiIgY2xpcC1ydWxlPSJldmVub2RkIj48L3BhdGg+PC9zdmc+\",\n         };\n         const MAX_FILES = 10;\n         const render = ({ files = null, title = null, $app }) => {\n             $app.innerHTML = \"\";\n             if (Array.isArray(files)) files = files.sort((fileA, fileB) => {\n                 if (fileA.type === \"directory\" && fileB.type !== \"directory\") return -1;\n                 if (fileA.type !== \"directory\" && fileB.type === \"directory\") return 1;\n                 return fileA.name.localeCompare(fileB.name);\n             });\n             if (title) $app.appendChild(Object.assign(document.createElement(\"h1\"), {\n                 className: \"ellipsis\",\n                 innerText: title,\n             }));\n             if (files === null) return $app.appendChild(Object.assign(document.createElement(\"div\"), {\n                 style: `text-align:center;padding:20px 0;`,\n                 innerHTML: `\n                     <svg width=\"25\" viewBox=\"0 0 36 36\" xmlns=\"http://www.w3.org/2000/svg\">\n                         <g>\n                             <circle stroke=\"#ececec\" fill=\"transparent\" stroke-width=\"2\" cx=\"18\" cy=\"18\" r=\"17\"></circle>\n                             <circle stroke=\"#212121\" fill=\"transparent\" stroke-width=\"2\" stroke-dasharray=\"26.7035 80.1106\" stroke-dashoffset=\"0\" stroke-linecap=\"butt\" cx=\"18\" cy=\"18\" r=\"17\"></circle>\n                             <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 18 18\" to=\"360 18 18\" dur=\"1s\" repeatCount=\"indefinite\"></animateTransform>\n                         </g>\n                     </svg>\n                 `,\n             }));\n\n             files.slice(0, MAX_FILES).forEach(({ type, name, time }) => {\n                 $app.appendChild(Object.assign(document.createElement(\"a\"), {\n                     className: \"component_thing\",\n                     innerHTML: `\n                         <div class=\"ellipsis\">\n                             <img class=\"component_icon\" src=\"data:image/svg+xml;base64,${type === \"directory\" ? ICON.DIRECTORY : ICON.FILE}\" alt=\"${type}\">\n                             <span class=\"component_filename\">${name}</span>\n                         </div>\n                         <span class=\"component_datetime\">${new Date(time*1000).toISOString().substr(0,10).replaceAll(\"-\", \"/\")}</span>\n                     `,\n                 }));\n             });\n             if (files.length > MAX_FILES) $app.appendChild(Object.assign(document.createElement(\"div\"), {\n                 innerText: \"...\",\n                 className: \"component_thing\",\n             }));\n         };\n         const debug = (object) => document.body.appendChild(Object.assign(document.createElement(\"div\"), {\n             innerHTML: `DEBUG: ${JSON.stringify(object) || \"n/a\"}`,\n             className: \"ellipsis\",\n             style: `display: none; background: var(--border); margin:5px; padding: 7px; font-size: 0.9rem; border: 2px dashed var(--light); border-radius: 5px; color: var(--light); `\n         }));\n\n         const $app = document.getElementById(\"app\");\n         render({ $app });\n         window.addEventListener(\n             \"openai:set_globals\",\n             (event) => {\n                 const globals = event.detail?.globals;\n                 if (!globals?.toolOutput?.files) return;\n                 debug(globals);\n                 render({\n                     files: globals.toolOutput.files,\n                     title: globals.toolInput.path || \"\",\n                     $app,\n                 });\n             },\n             { passive: true },\n         );\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/impl/resources.go",
    "content": "package impl\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/types\"\n)\n\nvar listOfResources = map[string]Resource{}\n\nfunc RegisterResource(r Resource) {\n\tlistOfResources[r.URI] = r\n}\n\nfunc AllResources() []Resource {\n\tr := []Resource{}\n\tfor _, val := range listOfResources {\n\t\tr = append(r, val)\n\t}\n\treturn r\n}\n\nfunc AllResourceTemplates() []ResourceTemplate {\n\treturn []ResourceTemplate{}\n}\n\nfunc FindResource(uri string) (*Resource, error) {\n\tr, ok := listOfResources[uri]\n\tif !ok {\n\t\treturn nil, JSONRPCError{\n\t\t\tCode:    http.StatusNotFound,\n\t\t\tMessage: fmt.Sprintf(\"Unknown resource: %s\", uri),\n\t\t}\n\t}\n\treturn &r, nil\n}\n\nfunc ExecResourceRead(params map[string]any) []ResourceContent {\n\treturn []ResourceContent{}\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/impl/tools.go",
    "content": "package impl\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/types\"\n)\n\nvar listOfTools = map[string]Tool{}\n\nfunc RegisterTool(t Tool) {\n\tlistOfTools[t.Name] = t\n}\n\nfunc AllTools() []Tool {\n\tt := []Tool{}\n\tfor _, v := range listOfTools {\n\t\tt = append(t, v)\n\t}\n\treturn t\n}\n\nfunc FindTool(name string) (*Tool, error) {\n\tt, ok := listOfTools[name]\n\tif !ok {\n\t\treturn nil, JSONRPCError{\n\t\t\tCode:    http.StatusNotFound,\n\t\t\tMessage: fmt.Sprintf(\"Unknown tool: %s\", name),\n\t\t}\n\t}\n\treturn &t, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/impl/tools_fs.go",
    "content": "package impl\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"errors\"\n\t\"io\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/types\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/utils\"\n)\n\n//go:embed public/file-list.html\nvar widget_ls string\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tRegisterTool(Tool{\n\t\t\tName:        \"ls\",\n\t\t\tDescription: \"Use this when you need to list files and subdirectories in a directory, based on the Unix command: ls. If path is omitted, the current working directory is used\",\n\t\t\tInputSchema: JsonSchema(map[string]interface{}{\n\t\t\t\t\"type\": \"object\",\n\t\t\t\t\"properties\": map[string]interface{}{\n\t\t\t\t\t\"path\": map[string]string{\n\t\t\t\t\t\t\"type\":        \"string\",\n\t\t\t\t\t\t\"description\": \"path where the query is made\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"required\": []string{},\n\t\t\t}),\n\t\t\tRun: ToolFSLs,\n\t\t\tMeta: Meta{\n\t\t\t\t\"openai/outputTemplate\":          \"ui://widget/render-ls.html\",\n\t\t\t\t\"openai/toolInvocation/invoking\": \"Querying…\",\n\t\t\t\t\"openai/toolInvocation/invoked\":  \"Results ready\",\n\t\t\t},\n\t\t\tAnnotations: Meta{\n\t\t\t\t\"destructiveHint\": false,\n\t\t\t\t\"openWorldHint\":   true,\n\t\t\t\t\"readOnlyHint\":    true,\n\t\t\t},\n\t\t})\n\n\t\tRegisterResource(Resource{\n\t\t\tURI:         \"ui://widget/render-ls.html\",\n\t\t\tName:        \"render-ls.html\",\n\t\t\tDescription: \"file listing widget\",\n\t\t\tMimeType:    \"text/html+skybridge\",\n\t\t\tContent:     widget_ls,\n\t\t\tMeta: Meta{\n\t\t\t\t\"openai/widgetPrefersBorder\": true,\n\t\t\t\t\"openai/widgetDomain\":        \"https://chatgpt.com\",\n\t\t\t\t\"openai/widgetCSP\": map[string][]string{\n\t\t\t\t\t\"connect_domains\":  []string{\"https://chatgpt.com\"},\n\t\t\t\t\t\"resource_domains\": []string{\"https://*.oaistatic.com\"},\n\t\t\t\t\t\"frame_domains\":    []string{},\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\n\t\tRegisterTool(Tool{\n\t\t\tName:        \"cat\",\n\t\t\tDescription: \"Use this when you need to read and return the contents of a file at a specific path, based on the Unix command: `cat`.\",\n\t\t\tInputSchema: JsonSchema(map[string]interface{}{\n\t\t\t\t\"type\": \"object\",\n\t\t\t\t\"properties\": map[string]interface{}{\n\t\t\t\t\t\"path\": map[string]string{\n\t\t\t\t\t\t\"type\":        \"string\",\n\t\t\t\t\t\t\"description\": \"path where the query is made\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"required\": []string{\"path\"},\n\t\t\t}),\n\t\t\tRun: ToolFSCat,\n\t\t\tAnnotations: Meta{\n\t\t\t\t\"destructiveHint\": false,\n\t\t\t\t\"openWorldHint\":   true,\n\t\t\t\t\"readOnlyHint\":    true,\n\t\t\t},\n\t\t})\n\n\t\tRegisterTool(Tool{\n\t\t\tName:        \"pwd\",\n\t\t\tDescription: \"Use this when you need to know the current working directory, based on the Unix command: `pwd`. The initial working directory is the user home directory, or `/` if none is defined.\",\n\t\t\tInputSchema: JsonSchema(map[string]interface{}{\n\t\t\t\t\"type\":       \"object\",\n\t\t\t\t\"properties\": map[string]interface{}{},\n\t\t\t\t\"required\":   []string{},\n\t\t\t}),\n\t\t\tRun: ToolFSPwd,\n\t\t\tAnnotations: Meta{\n\t\t\t\t\"destructiveHint\": false,\n\t\t\t\t\"openWorldHint\":   true,\n\t\t\t\t\"readOnlyHint\":    true,\n\t\t\t},\n\t\t})\n\n\t\tRegisterTool(Tool{\n\t\t\tName:        \"cd\",\n\t\t\tDescription: \"Use this when you need to change the current working directory so that subsequent file operations run relative to a different path, based on the Unix command: `cd`.\",\n\t\t\tInputSchema: JsonSchema(map[string]interface{}{\n\t\t\t\t\"type\": \"object\",\n\t\t\t\t\"properties\": map[string]interface{}{\n\t\t\t\t\t\"path\": map[string]string{\n\t\t\t\t\t\t\"type\":        \"string\",\n\t\t\t\t\t\t\"description\": \"path where the query is made\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"required\": []string{\"path\"},\n\t\t\t}),\n\t\t\tRun: ToolFSCd,\n\t\t\tAnnotations: Meta{\n\t\t\t\t\"destructiveHint\": false,\n\t\t\t\t\"openWorldHint\":   true,\n\t\t\t\t\"readOnlyHint\":    true,\n\t\t\t},\n\t\t})\n\n\t\tRegisterTool(Tool{\n\t\t\tName:        \"mv\",\n\t\t\tDescription: \"Use this when you need to move or rename a file or directory from one path to another, based on the Unix command: `mv`.\",\n\t\t\tInputSchema: JsonSchema(map[string]interface{}{\n\t\t\t\t\"type\": \"object\",\n\t\t\t\t\"properties\": map[string]interface{}{\n\t\t\t\t\t\"from\": map[string]string{\n\t\t\t\t\t\t\"type\":        \"string\",\n\t\t\t\t\t\t\"description\": \"origin path\",\n\t\t\t\t\t},\n\t\t\t\t\t\"to\": map[string]string{\n\t\t\t\t\t\t\"type\":        \"string\",\n\t\t\t\t\t\t\"description\": \"destination path\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"required\": []string{\"from\", \"to\"},\n\t\t\t}),\n\t\t\tRun: ToolFSMv,\n\t\t\tAnnotations: Meta{\n\t\t\t\t\"destructiveHint\": true,\n\t\t\t\t\"openWorldHint\":   true,\n\t\t\t\t\"readOnlyHint\":    false,\n\t\t\t},\n\t\t})\n\n\t\tRegisterTool(Tool{\n\t\t\tName:        \"mkdir\",\n\t\t\tDescription: \"Use this when you need to create a new directory at a specified path, based on the Unix command: `mkdir`.\",\n\t\t\tInputSchema: JsonSchema(map[string]interface{}{\n\t\t\t\t\"type\": \"object\",\n\t\t\t\t\"properties\": map[string]interface{}{\n\t\t\t\t\t\"path\": map[string]string{\n\t\t\t\t\t\t\"type\":        \"string\",\n\t\t\t\t\t\t\"description\": \"path where the query is made\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"required\": []string{\"path\"},\n\t\t\t}),\n\t\t\tRun: ToolFSMkdir,\n\t\t\tAnnotations: Meta{\n\t\t\t\t\"destructiveHint\": true,\n\t\t\t\t\"openWorldHint\":   true,\n\t\t\t\t\"readOnlyHint\":    false,\n\t\t\t},\n\t\t})\n\n\t\tRegisterTool(Tool{\n\t\t\tName:        \"touch\",\n\t\t\tDescription: \"Use this when you need to create an empty file at a specified path, based on the Unix command: `touch`.\",\n\t\t\tInputSchema: JsonSchema(map[string]interface{}{\n\t\t\t\t\"type\": \"object\",\n\t\t\t\t\"properties\": map[string]interface{}{\n\t\t\t\t\t\"path\": map[string]string{\n\t\t\t\t\t\t\"type\":        \"string\",\n\t\t\t\t\t\t\"description\": \"path where the query is made\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"required\": []string{\"path\"},\n\t\t\t}),\n\t\t\tRun: ToolFSTouch,\n\t\t\tAnnotations: Meta{\n\t\t\t\t\"destructiveHint\": true,\n\t\t\t\t\"openWorldHint\":   true,\n\t\t\t\t\"readOnlyHint\":    false,\n\t\t\t},\n\t\t})\n\n\t\tRegisterTool(Tool{\n\t\t\tName:        \"rm\",\n\t\t\tDescription: \"Use this when you need to remove a file or directory at a specified path, based on the Unix command: `rm`.\",\n\t\t\tInputSchema: JsonSchema(map[string]interface{}{\n\t\t\t\t\"type\": \"object\",\n\t\t\t\t\"properties\": map[string]interface{}{\n\t\t\t\t\t\"path\": map[string]string{\n\t\t\t\t\t\t\"type\":        \"string\",\n\t\t\t\t\t\t\"description\": \"path where the query is made\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"required\": []string{\"path\"},\n\t\t\t}),\n\t\t\tRun: ToolFSRm,\n\t\t\tAnnotations: Meta{\n\t\t\t\t\"destructiveHint\": true,\n\t\t\t\t\"openWorldHint\":   true,\n\t\t\t\t\"readOnlyHint\":    false,\n\t\t\t},\n\t\t})\n\n\t\tRegisterTool(Tool{\n\t\t\tName:        \"save\",\n\t\t\tDescription: \"Use this when you need to write or overwrite the contents of a file at a specified path.\",\n\t\t\tInputSchema: JsonSchema(map[string]interface{}{\n\t\t\t\t\"type\": \"object\",\n\t\t\t\t\"properties\": map[string]interface{}{\n\t\t\t\t\t\"path\": map[string]string{\n\t\t\t\t\t\t\"type\":        \"string\",\n\t\t\t\t\t\t\"description\": \"path where the query is made\",\n\t\t\t\t\t},\n\t\t\t\t\t\"content\": map[string]string{\n\t\t\t\t\t\t\"type\":        \"string\",\n\t\t\t\t\t\t\"description\": \"content of the file\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"required\": []string{\"path\"},\n\t\t\t}),\n\t\t\tRun: ToolFSSave,\n\t\t\tAnnotations: Meta{\n\t\t\t\t\"destructiveHint\": true,\n\t\t\t\t\"openWorldHint\":   true,\n\t\t\t\t\"readOnlyHint\":    false,\n\t\t\t},\n\t\t})\n\t})\n}\n\nfunc ToolFSLs(params map[string]any, userSession *UserSession) (*ToolResponse, error) {\n\tpath := getPath(params, userSession, \"path\")\n\tfiles, err := userSession.Backend.Ls(EnforceDirectory(path))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tstructuredContent := make([]File, len(files))\n\tcontent := bytes.Buffer{}\n\tfor i, file := range files {\n\t\tif file.IsDir() {\n\t\t\tcontent.Write([]byte(\"[DIR]  \"))\n\t\t} else {\n\t\t\tcontent.Write([]byte(\"[FILE] \"))\n\t\t}\n\t\tcontent.Write([]byte(file.Name()))\n\t\tcontent.Write([]byte(\"\\n\"))\n\t\tftype := \"file\"\n\t\tif file.IsDir() {\n\t\t\tftype = \"directory\"\n\t\t}\n\t\tstructuredContent[i] = File{\n\t\t\tFName: file.Name(),\n\t\t\tFType: ftype,\n\t\t\tFSize: file.Size(),\n\t\t\tFTime: file.ModTime().Unix(),\n\t\t}\n\t}\n\treturn &ToolResponse{\n\t\tStructuredContent: map[string]any{\n\t\t\t\"files\": structuredContent,\n\t\t},\n\t\tContent: []TextContent{\n\t\t\t{\n\t\t\t\tType: \"text\",\n\t\t\t\tText: content.String(),\n\t\t\t},\n\t\t},\n\t}, nil\n}\n\nfunc ToolFSCat(params map[string]any, userSession *UserSession) (*ToolResponse, error) {\n\tif isArgEmpty(params, \"path\") {\n\t\treturn nil, ErrNotValid\n\t}\n\tr, err := userSession.Backend.Cat(getPath(params, userSession, \"path\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tb, err := io.ReadAll(r)\n\tr.Close()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ToolResponse{\n\t\tContent: []TextContent{\n\t\t\t{\n\t\t\t\tType: \"text\",\n\t\t\t\tText: string(b),\n\t\t\t},\n\t\t},\n\t}, nil\n}\n\nfunc ToolFSPwd(params map[string]any, userSession *UserSession) (*ToolResponse, error) {\n\treturn &ToolResponse{\n\t\tContent: []TextContent{\n\t\t\t{\n\t\t\t\tType: \"text\",\n\t\t\t\tText: userSession.CurrDir,\n\t\t\t},\n\t\t},\n\t}, nil\n}\n\nfunc ToolFSCd(params map[string]any, userSession *UserSession) (*ToolResponse, error) {\n\tpath := EnforceDirectory(getPath(params, userSession, \"path\"))\n\tif _, err := userSession.Backend.Ls(path); err != nil {\n\t\treturn nil, errors.New(\"No such file or directory\")\n\t}\n\tuserSession.CurrDir = EnforceDirectory(path)\n\treturn &ToolResponse{\n\t\tContent: []TextContent{\n\t\t\t{\n\t\t\t\tType: \"text\",\n\t\t\t\tText: userSession.CurrDir,\n\t\t\t},\n\t\t},\n\t}, nil\n}\n\nfunc ToolFSMv(params map[string]any, userSession *UserSession) (*ToolResponse, error) {\n\tif isArgEmpty(params, \"from\") || isArgEmpty(params, \"to\") {\n\t\treturn nil, ErrNotValid\n\t}\n\tif err := userSession.Backend.Mv(\n\t\tgetPath(params, userSession, \"from\"),\n\t\tgetPath(params, userSession, \"to\"),\n\t); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ToolResponse{\n\t\tContent: []TextContent{\n\t\t\t{\n\t\t\t\tType: \"text\",\n\t\t\t\tText: \"done\",\n\t\t\t},\n\t\t},\n\t}, nil\n}\n\nfunc ToolFSMkdir(params map[string]any, userSession *UserSession) (*ToolResponse, error) {\n\tif isArgEmpty(params, \"path\") {\n\t\treturn nil, ErrNotValid\n\t}\n\tif err := userSession.Backend.Mkdir(EnforceDirectory(getPath(params, userSession, \"path\"))); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ToolResponse{\n\t\tContent: []TextContent{\n\t\t\t{\n\t\t\t\tType: \"text\",\n\t\t\t\tText: \"done\",\n\t\t\t},\n\t\t},\n\t}, nil\n}\n\nfunc ToolFSTouch(params map[string]any, userSession *UserSession) (*ToolResponse, error) {\n\tif isArgEmpty(params, \"path\") {\n\t\treturn nil, ErrNotValid\n\t}\n\tif err := userSession.Backend.Touch(getPath(params, userSession, \"path\")); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ToolResponse{\n\t\tContent: []TextContent{\n\t\t\t{\n\t\t\t\tType: \"text\",\n\t\t\t\tText: \"done\",\n\t\t\t},\n\t\t},\n\t}, nil\n}\n\nfunc ToolFSRm(params map[string]any, userSession *UserSession) (*ToolResponse, error) {\n\tif isArgEmpty(params, \"path\") {\n\t\treturn nil, ErrNotValid\n\t}\n\tif err := userSession.Backend.Rm(getPath(params, userSession, \"path\")); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ToolResponse{\n\t\tContent: []TextContent{\n\t\t\t{\n\t\t\t\tType: \"text\",\n\t\t\t\tText: \"done\",\n\t\t\t},\n\t\t},\n\t}, nil\n}\n\nfunc ToolFSSave(params map[string]any, userSession *UserSession) (*ToolResponse, error) {\n\tif isArgEmpty(params, \"path\") {\n\t\treturn nil, ErrNotValid\n\t}\n\tif err := userSession.Backend.Save(\n\t\tgetPath(params, userSession, \"path\"),\n\t\tNewReadCloserFromBytes([]byte(GetArgumentsString(params, \"content\"))),\n\t); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ToolResponse{\n\t\tContent: []TextContent{\n\t\t\t{\n\t\t\t\tType: \"text\",\n\t\t\t\tText: \"done\",\n\t\t\t},\n\t\t},\n\t}, nil\n}\n\nfunc getPath(params map[string]any, userSession *UserSession, name string) string {\n\tpath := GetArgumentsString(params, name)\n\tcurrDir := \"\"\n\tif path == \"\" {\n\t\tcurrDir = userSession.CurrDir\n\t} else if strings.HasPrefix(path, \"~/\") {\n\t\tcurrDir = \".\" + strings.TrimPrefix(path, \"~\")\n\t\tcurrDir = JoinPath(userSession.HomeDir, currDir)\n\t} else if strings.HasPrefix(path, \"/\") {\n\t\tcurrDir = path\n\t} else {\n\t\tcurrDir = filepath.Join(userSession.CurrDir, ToString(path, \"./\"))\n\t}\n\treturn currDir\n}\n\nfunc isArgEmpty(params map[string]any, name string) bool {\n\tif arg := GetArgumentsString(params, name); arg == \"\" {\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/index.go",
    "content": "package plg_handler_mcp\n\nimport (\n\t\"sync\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/middleware\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/config\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/utils\"\n\n\t\"github.com/gorilla/mux\"\n)\n\ntype Server struct {\n\tsessions sync.Map\n}\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tPluginEnable()\n\t})\n\n\tHooks.Register.HttpEndpoint(func(r *mux.Router) error {\n\t\tif !PluginEnable() {\n\t\t\treturn nil\n\t\t}\n\t\tsrv := Server{}\n\t\tm := []Middleware{WithCORS}\n\t\tr.HandleFunc(\"/sse\", NewMiddlewareChain(srv.sseHandler, m)).Methods(\"GET\", \"OPTIONS\")\n\t\tr.HandleFunc(\"/messages\", NewMiddlewareChain(srv.messageHandler, m)).Methods(\"POST\", \"OPTIONS\")\n\t\tr.HandleFunc(\"/.well-known/oauth-authorization-server\", NewMiddlewareChain(srv.WellKnownOAuthAuthorizationServerHandler, m)).Methods(\"GET\", \"OPTIONS\")\n\t\tr.HandleFunc(\"/.well-known/oauth-protected-resource\", NewMiddlewareChain(srv.WellKnownOAuthProtectedResourceHandler, m)).Methods(\"GET\", \"OPTIONS\")\n\t\tr.HandleFunc(\"/.well-known/oauth-protected-resource/sse\", NewMiddlewareChain(srv.WellKnownOAuthProtectedResourceHandler, m)).Methods(\"GET\", \"OPTIONS\")\n\n\t\tr.HandleFunc(\"/mcp/token\", NewMiddlewareChain(srv.TokenHandler, m)).Methods(\"POST\")\n\t\tm = []Middleware{}\n\t\tr.HandleFunc(\"/mcp/authorize\", NewMiddlewareChain(srv.AuthorizeHandler, m)).Methods(\"GET\")\n\t\tm = []Middleware{BodyParser}\n\t\tr.HandleFunc(\"/mcp/register\", NewMiddlewareChain(srv.RegisterHandler, m)).Methods(\"POST\")\n\t\tm = []Middleware{SessionStart, LoggedInOnly}\n\t\tr.HandleFunc(\"/api/mcp\", NewMiddlewareChain(srv.CallbackHandler, m)).Methods(\"GET\")\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/types/mcp_completion.go",
    "content": "package types\n\ntype CompletionResponse struct {\n\tCompletion Completion `json:\"completion\"`\n}\n\ntype Completion struct {\n\tValues  []string `json:\"values\"`\n\tTotal   uint64   `json:\"total\"`\n\tHasMore bool     `json:\"hasMore\"`\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/types/mcp_init.go",
    "content": "package types\n\ntype InitializeResponse struct {\n\tProtocolVersion string       `json:\"protocolVersion\"`\n\tServerInfo      ServerInfo   `json:\"serverInfo\"`\n\tCapabilities    Capabilities `json:\"capabilities\"`\n}\n\ntype ServerInfo struct {\n\tName    string `json:\"name\"`\n\tVersion string `json:\"version\"`\n}\n\ntype Capabilities struct {\n\tTools     map[string]interface{} `json:\"tools\",omitempty`\n\tResources map[string]interface{} `json:\"resources\",omitempty`\n\tPrompts   map[string]interface{} `json:\"prompts\",omitempty`\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/types/mcp_notification.go",
    "content": "package types\n\nvar (\n\tErrToolsListChanges = newError(\"notifications/tools/list_changed\")\n\tErrDisconnect       = newError(\"internal/disconnect\")\n)\n\nfunc newError(s string) error {\n\treturn notification{s}\n}\n\ntype notification struct {\n\tmsg string\n}\n\nfunc (this notification) Error() string {\n\treturn this.msg\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/types/mcp_prompts.go",
    "content": "package types\n\ntype PromptsListResponse struct {\n\tPrompts    []Prompt `json:\"prompts\"`\n\tNextCursor string   `json:\"nextCursor\",omitempty`\n}\n\ntype Prompt struct {\n\tName        string           `json:\"name\"`\n\tDescription string           `json:\"description\"`\n\tArguments   []PromptArgument `json:\"arguments\"`\n}\n\ntype PromptArgument struct {\n\tName        string `json:\"name\"`\n\tDescription string `json:\"description\"`\n\tRequired    bool   `json:\"required\"`\n}\n\ntype PromptGetResponse struct {\n\tDescription string          `json:\"description\"`\n\tMessages    []PromptMessage `json:\"messages\"`\n}\n\ntype PromptMessage struct {\n\tRole    string      `json:\"role\"`\n\tContent TextContent `json:\"content\"`\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/types/mcp_resources.go",
    "content": "package types\n\ntype ResourcesListResponse struct {\n\tResources []Resource `json:\"resources\"`\n}\n\ntype ResourceTemplatesListResponse struct {\n\tResourceTemplates []ResourceTemplate `json:\"resourceTemplates\"`\n}\n\ntype ResourceReadResponse struct {\n\tContents []ResourceContent `json:\"contents\"`\n}\n\ntype Resource struct {\n\tURI         string `json:\"uri\"`\n\tName        string `json:\"name\"`\n\tDescription string `json:\"description\"`\n\tMimeType    string `json:\"mimeType\"`\n\tContent     string `json:\"-\"`\n\tMeta        Meta   `json:\"-\"`\n}\n\ntype ResourceTemplate struct {\n\tURITemplate string `json:\"uriTemplate\"`\n\tName        string `json:\"name\"`\n\tDescription string `json:\"description\"`\n\tMimeType    string `json:\"mimeType\"`\n}\n\ntype ResourceContent struct {\n\tURI      string `json:\"uri\"`\n\tMimeType string `json:\"mimeType\"`\n\tText     string `json:\"text\"`\n\tMeta     Meta   `json:\"_meta,omitempty\"`\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/types/mcp_tools.go",
    "content": "package types\n\nimport (\n\t\"encoding/json\"\n)\n\ntype Meta map[string]any\n\ntype Run func(params map[string]any, userSession *UserSession) (*ToolResponse, error)\n\ntype Tool struct {\n\tName         string          `json:\"name\"`\n\tDescription  string          `json:\"description\"`\n\tInputSchema  json.RawMessage `json:\"inputSchema\"`\n\tOutputSchema json.RawMessage `json:\"outputSchema,omitempty\"`\n\tMeta         Meta            `json:\"_meta,omitempty\"`\n\tRun          Run             `json:\"-\"`\n\tAnnotations  Meta            `json:\"annotations,omitempty\"`\n}\n\ntype ListToolsResponse struct {\n\tTools []Tool `json:\"tools\"`\n}\n\ntype ToolResponse struct {\n\tContent           []TextContent  `json:\"content\"`\n\tStructuredContent map[string]any `json:\"structuredContent,omitempty\"`\n\tIsError           bool           `json:\"isError\"`\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/types/resources.go",
    "content": "package types\n\nimport \"encoding/json\"\n\ntype IResource interface {\n\tResource() ([]json.RawMessage, error)\n}\n\ntype TextContent struct {\n\tType string `json:\"type\"`\n\tText string `json:\"text\"`\n}\n\ntype BinaryContent struct {\n\tURI      string `json:uri\"`\n\tMimeType string `json:\"mimeType\"`\n\tBlob     []byte `json:\"blob\"`\n}\n\nfunc (this TextContent) Resource() ([]byte, error) {\n\treturn json.Marshal(this)\n}\n\nfunc (this BinaryContent) Resource() ([]byte, error) {\n\treturn json.Marshal(this)\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/types/rpc.go",
    "content": "package types\n\ntype JSONRPCRequest struct {\n\tJSONRPC string         `json:\"jsonrpc\"`\n\tID      uint64         `json:\"id\"`\n\tMethod  string         `json:\"method\"`\n\tParams  map[string]any `json:\"params,omitempty\"`\n}\n\ntype JSONRPCResponse struct {\n\tJSONRPC string        `json:\"jsonrpc\"`\n\tID      uint64        `json:\"id\"`\n\tResult  *any          `json:\"result,omitempty\"`\n\tError   *JSONRPCError `json:\"error,omitempty\"`\n}\n\ntype JSONRPCMethod struct {\n\tJSONRPC string      `json:\"jsonrpc\"`\n\tMethod  string      `json:\"method\"`\n\tParams  interface{} `json:\"params\",omitempty`\n}\n\ntype JSONRPCError struct {\n\tCode    int         `json:\"code\"`\n\tMessage string      `json:\"message\"`\n\tData    interface{} `json:\"data,omitempty\"`\n}\n\nfunc (this JSONRPCError) Error() string {\n\treturn this.Message\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/types/session.go",
    "content": "package types\n\nimport (\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\ntype UserSession struct {\n\tId      string\n\tChan    chan JSONRPCRequest\n\tHomeDir string\n\tCurrDir string\n\tToken   string\n\tBackend IBackend\n\tPing    Ping\n}\n\ntype Ping struct {\n\tID           uint64\n\tLastResponse time.Time\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/utils/cors.go",
    "content": "package utils\n\nimport (\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc WithCORS(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, w http.ResponseWriter, r *http.Request) {\n\t\tw.Header().Set(\"Access-Control-Allow-Origin\", \"*\")\n\t\tw.Header().Set(\"Access-Control-Allow-Headers\", \"mcp-protocol-version, Content-Type, Authorization\")\n\t\tif r.Method == http.MethodOptions {\n\t\t\tw.WriteHeader(http.StatusOK)\n\t\t\treturn\n\t\t}\n\t\tfn(ctx, w, r)\n\t})\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/utils/default.go",
    "content": "package utils\n\nimport (\n\t\"fmt\"\n)\n\nfunc ToString(val any, def string) string {\n\tif val == nil {\n\t\treturn def\n\t} else if val == \"\" {\n\t\treturn def\n\t}\n\treturn fmt.Sprintf(\"%v\", val)\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/utils/json.go",
    "content": "package utils\n\nimport (\n\t\"encoding/json\"\n)\n\nfunc JsonSchema(in any) json.RawMessage {\n\tb, _ := json.Marshal(in)\n\treturn json.RawMessage(b)\n}\n\nfunc JsonText(in any) string {\n\tb, _ := json.MarshalIndent(in, \"\", \"    \")\n\treturn string(b)\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/utils/mcp.go",
    "content": "package utils\n\nfunc GetArgumentsString(params map[string]any, name string) string {\n\tm, ok := params[\"arguments\"].(map[string]any)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\tp, ok := m[name].(string)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\treturn p\n}\n\nfunc GetArgumentString(params map[string]any, name string) string {\n\tm, ok := params[\"argument\"].(map[string]any)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\tp, ok := m[name].(string)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\treturn p\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_mcp/utils/response.go",
    "content": "package utils\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/types\"\n)\n\nfunc SendMessage(w io.Writer, requestID uint64, response any) {\n\tb, err := json.Marshal(JSONRPCResponse{\n\t\tJSONRPC: \"2.0\",\n\t\tID:      requestID,\n\t\tResult:  &response,\n\t})\n\tif err != nil {\n\t\tSendError(w, requestID, JSONRPCError{\n\t\t\tCode:    http.StatusInternalServerError,\n\t\t\tMessage: err.Error(),\n\t\t})\n\t}\n\tfmt.Fprintf(w, \"event: message\\ndata: %s\\n\\n\", string(b))\n\tw.(http.Flusher).Flush()\n}\n\nfunc SendPing(w io.Writer, requestID uint64) {\n\tb, err := json.Marshal(JSONRPCRequest{\n\t\tJSONRPC: \"2.0\",\n\t\tID:      requestID,\n\t\tMethod:  \"ping\",\n\t})\n\tif err != nil {\n\t\tSendError(w, requestID, JSONRPCError{\n\t\t\tCode:    http.StatusInternalServerError,\n\t\t\tMessage: err.Error(),\n\t\t})\n\t}\n\tfmt.Fprintf(w, \"event: message\\ndata: %s\\n\\n\", string(b))\n\tw.(http.Flusher).Flush()\n}\n\nfunc SendMethod(w io.Writer, requestID uint64, method string, args ...map[string]any) {\n\tvar params map[string]any\n\tif len(args) == 1 {\n\t\tparams = args[0]\n\t}\n\n\tb, err := json.Marshal(JSONRPCMethod{\n\t\tJSONRPC: \"2.0\",\n\t\tMethod:  method,\n\t\tParams:  params,\n\t})\n\tif err != nil {\n\t\tSendError(w, requestID, JSONRPCError{\n\t\t\tCode:    http.StatusInternalServerError,\n\t\t\tMessage: err.Error(),\n\t\t})\n\t}\n\tfmt.Fprintf(w, \"event: message\\ndata: %s\\n\\n\", string(b))\n\tw.(http.Flusher).Flush()\n}\n\nfunc SendError(w io.Writer, requestID uint64, d error) {\n\tvar rpcErr JSONRPCError\n\tswitch v := d.(type) {\n\tcase JSONRPCError:\n\t\trpcErr = v\n\tcase AppError:\n\t\trpcErr = JSONRPCError{\n\t\t\tCode:    v.Status(),\n\t\t\tMessage: v.Error(),\n\t\t}\n\tdefault:\n\t\trpcErr = JSONRPCError{\n\t\t\tCode:    http.StatusInternalServerError,\n\t\t\tMessage: d.Error(),\n\t\t}\n\t}\n\tb, err := json.Marshal(JSONRPCResponse{\n\t\tJSONRPC: \"2.0\",\n\t\tID:      requestID,\n\t\tError:   &rpcErr,\n\t})\n\tif err != nil {\n\t\tfmt.Fprintf(w, \"event: message\\ndata: %s\\n\\n\", string(`nil`))\n\t} else {\n\t\tfmt.Fprintf(w, \"event: message\\ndata: %s\\n\\n\", string(b))\n\t}\n\tw.(http.Flusher).Flush()\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_site/config.go",
    "content": "package plg_handler_site\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tPluginEnable()\n\t\tPluginParamAutoindex()\n\t\tPluginParamCORSAllowOrigins()\n\t})\n\n}\n\nvar PluginEnable = func() bool {\n\treturn Config.Get(\"features.site.enable\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"enable\"\n\t\tf.Type = \"enable\"\n\t\tf.Target = []string{\"site_autoindex\", \"site_cors_allow_origins\"}\n\t\tf.Description = \"Enable/Disable the creation of site via shared links. Sites will be made available under /public/{shareID}/\"\n\t\tf.Default = false\n\t\treturn f\n\t}).Bool()\n}\n\nvar PluginParamAutoindex = func() bool {\n\treturn Config.Get(\"features.site.autoindex\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Id = \"site_autoindex\"\n\t\tf.Name = \"autoindex\"\n\t\tf.Type = \"boolean\"\n\t\tf.Description = \"Enables or disables automatic directory listing when no index file is present.\"\n\t\tf.Default = false\n\t\treturn f\n\t}).Bool()\n}\n\nvar PluginParamCORSAllowOrigins = func() string {\n\treturn Config.Get(\"features.site.cors_allow_origins\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Id = \"site_cors_allow_origins\"\n\t\tf.Name = \"cors_allow_origins\"\n\t\tf.Type = \"text\"\n\t\tf.Placeholder = \"* or https://example.com, https://app.example.com\"\n\t\tf.Description = \"List of allowed origins for CORS. Use '*' to allow all origins, or provide a comma-separated list.\"\n\t\treturn f\n\t}).String()\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_site/index.go",
    "content": "package plg_handler_site\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/ctrl\"\n\t. \"github.com/mickael-kerjean/filestash/server/middleware\"\n\t\"github.com/mickael-kerjean/filestash/server/model\"\n\n\t\"github.com/gorilla/mux\"\n)\n\nfunc init() {\n\tHooks.Register.HttpEndpoint(func(r *mux.Router) error {\n\t\tif PluginEnable() == false {\n\t\t\treturn nil\n\t\t}\n\t\tr.PathPrefix(\"/public/{share}/\").HandlerFunc(NewMiddlewareChain(\n\t\t\tSiteHandler,\n\t\t\t[]Middleware{SessionStart, SecureHeaders, cors},\n\t\t)).Methods(\"GET\", \"HEAD\")\n\n\t\tr.HandleFunc(\"/public/\", NewMiddlewareChain(\n\t\t\tSharesListHandler,\n\t\t\t[]Middleware{SecureHeaders, basicAdmin},\n\t\t)).Methods(\"GET\")\n\t\treturn nil\n\t})\n}\n\nfunc SiteHandler(app *App, w http.ResponseWriter, r *http.Request) {\n\tif r.Method == http.MethodOptions {\n\t\tw.WriteHeader(http.StatusNoContent)\n\t\treturn\n\t} else if app.Backend == nil {\n\t\tSendErrorResult(w, ErrNotFound)\n\t\treturn\n\t} else if model.CanRead(app) == false {\n\t\tSendErrorResult(w, ErrPermissionDenied)\n\t\treturn\n\t}\n\tpath := strings.TrimPrefix(r.URL.Path, \"/public/\"+app.Share.Id)\n\tif strings.HasSuffix(path, \"/\") {\n\t\tpath += \"index.html\"\n\t}\n\tpath, err := ctrl.PathBuilder(app, path)\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tfor _, auth := range Hooks.Get.AuthorisationMiddleware() {\n\t\tif err = auth.Cat(app, path); err != nil {\n\t\t\tSendErrorResult(w, ErrNotAuthorized)\n\t\t\treturn\n\t\t}\n\t}\n\tif f, err := app.Backend.Cat(path); err == nil {\n\t\tw.Header().Set(\"Content-Type\", GetMimeType(path))\n\t\tio.Copy(w, f)\n\t\tf.Close()\n\t\treturn\n\t} else if err == ErrNotFound && PluginParamAutoindex() {\n\t\tif files, err := app.Backend.Ls(strings.TrimSuffix(path, \"index.html\")); err == nil {\n\t\t\tif strings.HasSuffix(r.URL.Path, \"/\") == false {\n\t\t\t\thttp.Redirect(w, r, r.URL.Path+\"/\", http.StatusSeeOther)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tw.Header().Set(\"Content-Type\", \"text/html; charset=utf-8\")\n\t\t\tif err := TmplAutoindex.Execute(w, map[string]any{\n\t\t\t\t\"Base\":  r.URL.Path,\n\t\t\t\t\"Files\": files,\n\t\t\t}); err != nil {\n\t\t\t\tSendErrorResult(w, err)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\tSendErrorResult(w, ErrNotFound)\n}\n\nfunc SharesListHandler(app *App, w http.ResponseWriter, r *http.Request) {\n\tshares, err := model.ShareAll()\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tfiles := make([]os.FileInfo, len(shares))\n\tfor i, share := range shares {\n\t\tt := int64(-1)\n\t\tif share.Expire != nil {\n\t\t\tt = *share.Expire\n\t\t}\n\t\tfiles[i] = File{\n\t\t\tFName: share.Id,\n\t\t\tFType: \"directory\",\n\t\t\tFTime: t,\n\t\t}\n\t}\n\tw.Header().Set(\"Content-Type\", \"text/html; charset=utf-8\")\n\tif err := TmplAutoindex.Execute(w, map[string]any{\n\t\t\"Base\":  r.URL.Path,\n\t\t\"Files\": files,\n\t}); err != nil {\n\t\tSendErrorResult(w, err)\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_site/middleware.go",
    "content": "package plg_handler_site\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\nfunc cors(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, w http.ResponseWriter, r *http.Request) {\n\t\tif allowed := PluginParamCORSAllowOrigins(); allowed != \"\" {\n\t\t\tw.Header().Add(\"Vary\", \"Origin\")\n\t\t\tw.Header().Set(\"Access-Control-Allow-Methods\", \"GET, HEAD, OPTIONS\")\n\t\t\tswitch allowed {\n\t\t\tcase \"*\":\n\t\t\t\tw.Header().Set(\"Access-Control-Allow-Origin\", \"*\")\n\t\t\tdefault:\n\t\t\t\torigin := r.Header.Get(\"Origin\")\n\t\t\t\tfor _, o := range strings.Split(allowed, \",\") {\n\t\t\t\t\tif strings.TrimSpace(o) == origin {\n\t\t\t\t\t\tw.Header().Set(\"Access-Control-Allow-Origin\", origin)\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\t\tfn(ctx, w, r)\n\t})\n}\n\nfunc basicAdmin(fn HandlerFunc) HandlerFunc {\n\treturn HandlerFunc(func(ctx *App, w http.ResponseWriter, r *http.Request) {\n\t\tuser, pass, ok := r.BasicAuth()\n\t\tif !ok || user != \"admin\" {\n\t\t\tw.Header().Set(\"WWW-Authenticate\", `Basic realm=\"Restricted\"`)\n\t\t\thttp.Error(w, \"Unauthorized\", http.StatusUnauthorized)\n\t\t\treturn\n\t\t} else if err := bcrypt.CompareHashAndPassword([]byte(Config.Get(\"auth.admin\").String()), []byte(pass)); err != nil {\n\t\t\tw.Header().Set(\"WWW-Authenticate\", `Basic realm=\"Restricted\"`)\n\t\t\thttp.Error(w, \"Unauthorized\", http.StatusUnauthorized)\n\t\t\treturn\n\t\t}\n\t\tfn(ctx, w, r)\n\t})\n}\n"
  },
  {
    "path": "server/plugin/plg_handler_site/template.go",
    "content": "package plg_handler_site\n\nimport \"html/template\"\n\nvar TmplAutoindex = template.Must(template.New(\"autoindex\").Parse(`\n<html>\n    <head>\n        <title>Index of {{ .Base }}</title>\n    </head>\n    <body>\n        <h1>Index of {{ .Base }}</h1><pre><a href=\"../\">../</a><br>\n        {{- range .Files -}}\n           <a href=\"{{if .IsDir}}{{printf \"./%s/\" .Name}}{{else}}{{printf \"./%s\" .Name}}{{end}}\">\n               {{- if .IsDir -}}\n                   {{ printf \"%-40.40s\" (printf \"%s/\" .Name) }}\n               {{- else -}}\n                   {{ printf \"%-40.40s\" .Name }}\n               {{- end -}}\n           </a>  {{ (.ModTime).Format \"2006-01-02 15:04:05\" }}  {{ printf \"%8d\" .Size }}<br>\n        {{- end }}\n        <hr>\n        </pre>\n    </body>\n</html>`))\n"
  },
  {
    "path": "server/plugin/plg_handler_syncthing/index.go",
    "content": "/*\n * This plugin expose syncthing to the admin user\n */\npackage plg_handler_syncthing\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"github.com/gorilla/mux\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"golang.org/x/crypto/bcrypt\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n)\n\nconst SYNCTHING_URI = \"/admin/syncthing\"\n\nvar (\n\tplugin_enable func() bool\n\tserver_url    func() string\n)\n\nfunc init() {\n\tplugin_enable = func() bool {\n\t\treturn Config.Get(\"features.syncthing.enable\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Name = \"enable\"\n\t\t\tf.Type = \"enable\"\n\t\t\tf.Target = []string{\"syncthing_server_url\"}\n\t\t\tf.Description = \"Enable/Disable integration with the syncthing server. This will make your syncthing server available at `/admin/syncthing`\"\n\t\t\tf.Default = false\n\t\t\tif u := os.Getenv(\"SYNCTHING_URL\"); u != \"\" {\n\t\t\t\tf.Default = true\n\t\t\t}\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\tserver_url = func() string {\n\t\treturn Config.Get(\"features.syncthing.server_url\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"syncthing_server_url\"\n\t\t\tf.Name = \"server_url\"\n\t\t\tf.Type = \"text\"\n\t\t\tf.Description = \"Location of your Syncthing server\"\n\t\t\tf.Default = \"\"\n\t\t\tf.Placeholder = \"Eg: http://127.0.0.1:8080\"\n\t\t\tif u := os.Getenv(\"SYNCTHING_URL\"); u != \"\" {\n\t\t\t\tf.Default = u\n\t\t\t\tf.Placeholder = fmt.Sprintf(\"Default: '%s'\", u)\n\t\t\t}\n\t\t\treturn f\n\t\t}).String()\n\t}\n\tHooks.Register.Onload(func() {\n\t\tplugin_enable()\n\t\tserver_url()\n\t})\n\n\tHooks.Register.HttpEndpoint(func(r *mux.Router, _ *App) error {\n\t\tif plugin_enable() == false {\n\t\t\treturn nil\n\t\t}\n\t\tr.HandleFunc(SYNCTHING_URI, func(res http.ResponseWriter, req *http.Request) {\n\t\t\thttp.Redirect(res, req, SYNCTHING_URI+\"/\", http.StatusTemporaryRedirect)\n\t\t})\n\t\tr.Handle(SYNCTHING_URI+\"/\", AuthBasic(\n\t\t\tfunc() (string, string) { return \"admin\", Config.Get(\"auth.admin\").String() },\n\t\t\thttp.HandlerFunc(SyncthingProxyHandler),\n\t\t))\n\n\t\tr.PathPrefix(SYNCTHING_URI + \"/\").HandlerFunc(SyncthingProxyHandler)\n\t\treturn nil\n\t})\n}\n\nfunc AuthBasic(credentials func() (string, string), fn http.Handler) http.HandlerFunc {\n\tvar notAuthorised = func(res http.ResponseWriter, req *http.Request) {\n\t\ttime.Sleep(1 * time.Second)\n\t\tres.Header().Set(\"WWW-Authenticate\", `Basic realm=\"User protect\", charset=\"UTF-8\"`)\n\t\tres.WriteHeader(http.StatusUnauthorized)\n\t\tres.Write([]byte(\"Not Authorised\"))\n\t\treturn\n\t}\n\n\treturn func(res http.ResponseWriter, req *http.Request) {\n\t\tauth := req.Header.Get(\"Authorization\")\n\t\tif strings.HasPrefix(auth, \"Basic \") == false {\n\t\t\tnotAuthorised(res, req)\n\t\t\treturn\n\t\t}\n\t\tauth = strings.TrimPrefix(auth, \"Basic \")\n\t\tdecoded, err := base64.StdEncoding.DecodeString(auth)\n\t\tif err != nil {\n\t\t\tnotAuthorised(res, req)\n\t\t\treturn\n\t\t}\n\t\tauth = string(decoded)\n\t\tstuffs := strings.Split(auth, \":\")\n\t\tif len(stuffs) < 2 {\n\t\t\tnotAuthorised(res, req)\n\t\t\treturn\n\t\t}\n\t\tusername := stuffs[0]\n\t\tpassword := strings.Join(stuffs[1:], \":\")\n\t\trefUsername, refPassword := credentials()\n\t\tif refUsername != username {\n\t\t\tnotAuthorised(res, req)\n\t\t\treturn\n\t\t} else if err = bcrypt.CompareHashAndPassword([]byte(refPassword), []byte(password)); err != nil {\n\t\t\tnotAuthorised(res, req)\n\t\t\treturn\n\t\t}\n\t\tfn.ServeHTTP(res, req)\n\t\treturn\n\t}\n}\n\nfunc SyncthingProxyHandler(res http.ResponseWriter, req *http.Request) {\n\treq.URL.Path = strings.TrimPrefix(req.URL.Path, SYNCTHING_URI)\n\treq.Header.Set(\"X-Forwarded-Host\", req.Host+SYNCTHING_URI)\n\treq.Header.Set(\"X-Forwarded-Proto\", func() string {\n\t\tif scheme := req.Header.Get(\"X-Forwarded-Proto\"); scheme != \"\" {\n\t\t\treturn scheme\n\t\t} else if req.TLS != nil {\n\t\t\treturn \"https\"\n\t\t}\n\t\treturn \"http\"\n\t}())\n\tu, err := url.Parse(server_url())\n\tif err != nil {\n\t\tSendErrorResult(res, err)\n\t\treturn\n\t}\n\n\treverseProxy := &httputil.ReverseProxy{\n\t\tDirector: func(rq *http.Request) {\n\t\t\trq.URL.Scheme = \"http\"\n\t\t\trq.URL.Host = u.Host\n\t\t\trq.URL.Path = func(a, b string) string {\n\t\t\t\taslash := strings.HasSuffix(a, \"/\")\n\t\t\t\tbslash := strings.HasPrefix(b, \"/\")\n\t\t\t\tswitch {\n\t\t\t\tcase aslash && bslash:\n\t\t\t\t\treturn a + b[1:]\n\t\t\t\tcase !aslash && !bslash:\n\t\t\t\t\treturn a + \"/\" + b\n\t\t\t\t}\n\t\t\t\treturn a + b\n\t\t\t}(u.Path, rq.URL.Path)\n\t\t\tif u.RawQuery == \"\" || rq.URL.RawQuery == \"\" {\n\t\t\t\trq.URL.RawQuery = u.RawQuery + rq.URL.RawQuery\n\t\t\t} else {\n\t\t\t\trq.URL.RawQuery = u.RawQuery + \"&\" + rq.URL.RawQuery\n\t\t\t}\n\t\t},\n\t}\n\treverseProxy.ErrorHandler = func(rw http.ResponseWriter, rq *http.Request, err error) {\n\t\tLog.Warning(\"[syncthing] %s\", err.Error())\n\t\tSendErrorResult(rw, NewError(err.Error(), http.StatusBadGateway))\n\t}\n\treverseProxy.ServeHTTP(res, req)\n}\n"
  },
  {
    "path": "server/plugin/plg_image_ascii/index.go",
    "content": "package plg_image_ascii\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/qeesung/image2ascii/convert\"\n\t\"image\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc init() {\n\tHooks.Register.ProcessFileContentBeforeSend(func(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, bool, error) {\n\t\tvalue, isIn := req.URL.Query()[\"ascii\"]\n\t\tif isIn == false {\n\t\t\treturn reader, false, nil\n\t\t} else if strings.Join(value, \"\") == \"false\" {\n\t\t\treturn reader, false, nil\n\t\t}\n\n\t\timg, _, err := image.Decode(reader)\n\t\treader.Close()\n\t\tif err != nil {\n\t\t\treturn NewReadCloserFromBytes([]byte(\"\")), true, err\n\t\t}\n\t\topt := convert.DefaultOptions\n\t\topt.FixedWidth = 80\n\t\topt.FixedHeight = 40\n\t\tout := convert.NewImageConverter().Image2ASCIIString(img, &opt)\n\t\t(*res).Header().Set(\"Content-Type\", \"application/octet-stream\")\n\t\treturn NewReadCloserFromBytes([]byte(out)), true, nil\n\t})\n}\n"
  },
  {
    "path": "server/plugin/plg_image_bimg/index.go",
    "content": "package plg_image_golang\n\nimport (\n\t\"github.com/h2non/bimg\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"net/http\"\n)\n\nconst THUMB_SIZE int = 150\n\nfunc init() {\n\tHooks.Register.Thumbnailer(\"image/jpeg\", thumbnailer{})\n\tHooks.Register.Thumbnailer(\"image/png\", thumbnailer{})\n\tHooks.Register.Thumbnailer(\"image/gif\", thumbnailer{})\n}\n\ntype thumbnailer struct{}\n\nfunc (this thumbnailer) Generate(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, error) {\n\tquery := req.URL.Query()\n\tmType := GetMimeType(query.Get(\"path\"))\n\n\tif query.Get(\"thumbnail\") != \"true\" {\n\t\treturn reader, nil\n\t} else if mType != \"image/jpeg\" && mType != \"image/png\" && mType != \"image/gif\" {\n\t\treturn reader, nil\n\t}\n\n\tb, err := io.ReadAll(reader)\n\tif err != nil {\n\t\treturn reader, err\n\t}\n\tnewImage, err := bimg.NewImage(b).Thumbnail(THUMB_SIZE)\n\tif err != nil {\n\t\treturn reader, err\n\t}\n\treturn NewReadCloserFromBytes(newImage), err\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_gif.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <gif_lib.h>\n#include <webp/encode.h>\n#include <unistd.h>\n#include \"utils.h\"\n#include \"image_gif_vendor.h\"\n\nint DGifSlurp2(GifFileType *GifFile);\nint DGifCloseFile2(GifFileType *GifFile, int *ErrorCode);\n\nint gif_to_webp(int inputDesc, int outputDesc, int targetSize) {\n#ifdef HAS_DEBUG\n  clock_t t;\n  t = clock();\n#endif\n  int status = 0;\n  int error;\n  if (targetSize < 0) targetSize = -targetSize;\n\n  // STEP1: setup gif\n  GifFileType* gif;\n  uint8_t* gif_rgba;\n  uint8_t* scaled_rgba;\n  if((gif = DGifOpenFileHandle(inputDesc, &error)) == NULL) {\n    status = 1;\n    goto CLEANUP_AND_ABORT;\n  }\n  DEBUG(\"after gif opened\");\n  if (DGifSlurp2(gif) != GIF_OK) {\n    status = 1;\n    goto CLEANUP_AND_ABORT_A;\n  }\n  int width = gif->SWidth;\n  int height = gif->SHeight;\n  int scale_factor = (width > targetSize) ? width / targetSize : 1;\n  int thumb_width = width / scale_factor;\n  int thumb_height = height / scale_factor;\n  DEBUG(\"after gif ready\");\n\n  // STEP2: convert frame to RGBA\n  if (gif->ImageCount == 0) {\n\tstatus = 1;\n    goto CLEANUP_AND_ABORT_A;\n  } else if (!(gif_rgba = (uint8_t*)malloc(width * height * 4))) {\n    status = 1;\n    goto CLEANUP_AND_ABORT_A;\n  }\n  GifColorType* colorMapEntry;\n  ColorMapObject* colorMap = (gif->Image.ColorMap) ? gif->Image.ColorMap : gif->SColorMap;\n  SavedImage* firstFrame = &gif->SavedImages[0];\n  GifByteType* gifBytes = firstFrame->RasterBits;\n  for (int i = 0; i < gif->SWidth * gif->SHeight; ++i) {\n    colorMapEntry = &colorMap->Colors[gifBytes[i]];\n    gif_rgba[i * 4 + 0] = colorMapEntry->Red;\n    gif_rgba[i * 4 + 1] = colorMapEntry->Green;\n    gif_rgba[i * 4 + 2] = colorMapEntry->Blue;\n    gif_rgba[i * 4 + 3] = 0xFF;\n  }\n  DEBUG(\"after gif rgba convert\");\n\n  // STEP3: scale the image\n  if (!(scaled_rgba = (uint8_t*)malloc(thumb_width * thumb_height * 4))) {\n    free(gif_rgba);\n    status = 1;\n    goto CLEANUP_AND_ABORT_A;\n  }\n  int x, y, srcIndex, destIndex;\n  for (int i = 0; i < thumb_height; ++i) {\n    for (int j = 0; j < thumb_width; ++j) {\n      x = j * width / thumb_width;\n      y = i * height / thumb_height;\n      srcIndex = (y * width + x) * 4;\n      destIndex = (i * thumb_width + j) * 4;\n      memcpy(&scaled_rgba[destIndex], &gif_rgba[srcIndex], 4);\n    }\n  }\n  free(gif_rgba);\n  DEBUG(\"after image scaled\");\n\n  // STEP4: write image as webp\n  uint8_t* webp_output_data;\n  size_t webp_output_size = WebPEncodeRGBA(scaled_rgba, thumb_width, thumb_height, thumb_width * 4, 75, &webp_output_data);\n  free(scaled_rgba);\n  if (webp_output_size == 0) {\n    status = 1;\n    goto CLEANUP_AND_ABORT_A;\n  }\n  if (write(outputDesc, webp_output_data, webp_output_size) != webp_output_size) {\n    status = 1;\n    ERROR(\"unexpected number of bytes written\");\n  }\n  WebPFree(webp_output_data);\n  DEBUG(\"after webp written\");\n\n CLEANUP_AND_ABORT_A:\n  DGifCloseFile2(gif, &error);\n\n CLEANUP_AND_ABORT:\n  return status;\n}\n\n// adapted from https://android.googlesource.com/platform/external/giflib/+/dc07290edccc2c3fc4062da835306f809cea1fdc/dgif_lib.c\n// we got rid of unecessary stuff for our use case and reduce the processing\n// to the first frame only which isn't possible using stock libgif functions\nint DGifSlurp2(GifFileType *GifFile) {\n  clock_t t = clock();\n  size_t ImageSize;\n  GifRecordType RecordType;\n  SavedImage *sp;\n  GifByteType *ExtData;\n  int ExtFunction;\n  GifFile->ExtensionBlocks = NULL;\n  GifFile->ExtensionBlockCount = 0;\n  do {\n    if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {\n      return GIF_ERROR;\n    }\n\n    if (RecordType == IMAGE_DESC_RECORD_TYPE) {\n      if (DGifGetImageDesc(GifFile) == GIF_ERROR) {\n        return GIF_ERROR;\n      }\n\n      sp = &GifFile->SavedImages[GifFile->ImageCount - 1];\n      if (sp->ImageDesc.Width < 0 && sp->ImageDesc.Height < 0 && sp->ImageDesc.Width > (INT_MAX / sp->ImageDesc.Height)) {\n        return GIF_ERROR;\n      }\n      ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height;\n      if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) {\n        return GIF_ERROR;\n      }\n      sp->RasterBits = (unsigned char *)reallocarray(NULL, ImageSize, sizeof(GifPixelType));\n      if (sp->RasterBits == NULL) {\n        return GIF_ERROR;\n      }\n      if (DGifGetLine(GifFile, sp->RasterBits, ImageSize) == GIF_ERROR) {\n        return GIF_ERROR;\n      }\n      return GIF_OK;\n    } else if (RecordType == EXTENSION_RECORD_TYPE) {\n      if (DGifGetExtension(GifFile, &ExtFunction, &ExtData) == GIF_ERROR) {\n        return GIF_ERROR;\n      }\n      while (ExtData != NULL) {\n        if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR) {\n          return GIF_ERROR;\n        }\n      }\n    }\n  } while (RecordType != TERMINATE_RECORD_TYPE);\n\n  return GIF_OK;\n}\n\n\n// adapted from: https://android.googlesource.com/platform/external/giflib/+/dc07290edccc2c3fc4062da835306f809cea1fdc/dgif_lib.c#626\n// as we don't want libgif to manage the lifecycle of the file descriptor, in our case\n// this is the responsibility of the downstream program, that's why we've recopied it here\n// a commented the fclose call\nint DGifCloseFile2(GifFileType *GifFile, int *ErrorCode)\n{\n  GifFilePrivateType *Private;\n  if (GifFile == NULL || GifFile->Private == NULL) {\n    return GIF_ERROR;\n  }\n  if (GifFile->Image.ColorMap) {\n    GifFreeMapObject(GifFile->Image.ColorMap);\n    GifFile->Image.ColorMap = NULL;\n  }\n  if (GifFile->SColorMap) {\n    GifFreeMapObject(GifFile->SColorMap);\n    GifFile->SColorMap = NULL;\n  }\n  if (GifFile->SavedImages) {\n    GifFreeSavedImages(GifFile);\n    GifFile->SavedImages = NULL;\n  }\n  GifFreeExtensions(&GifFile->ExtensionBlockCount, &GifFile->ExtensionBlocks);\n  Private = (GifFilePrivateType *) GifFile->Private;\n  if (!IS_READABLE(Private)) {\n    if (ErrorCode != NULL) {\n      *ErrorCode = D_GIF_ERR_NOT_READABLE;\n    }\n\tfree((char *)GifFile->Private);\n\tfree(GifFile);\n    return GIF_ERROR;\n  }\n  if (Private->File /*&& (fclose(Private->File) != 0)*/) {\n\tif (ErrorCode != NULL) {\n      *ErrorCode = D_GIF_ERR_CLOSE_FAILED;\n    }\n\tfree((char *)GifFile->Private);\n\tfree(GifFile);\n    return GIF_ERROR;\n  }\n  free((char *)GifFile->Private);\n  free(GifFile);\n  if (ErrorCode != NULL) {\n\t*ErrorCode = D_GIF_SUCCEEDED;\n  }\n  return GIF_OK;\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_gif.go",
    "content": "package plg_image_c\n\n// #include \"image_gif.h\"\n// #cgo LDFLAGS: -l:libgif.a -l:libwebp.a\nimport \"C\"\n\nfunc gif(input uintptr, output uintptr, size int) {\n\tC.gif_to_webp(C.int(input), C.int(output), C.int(size))\n\treturn\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_gif.h",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint gif_to_webp(int inputDesc, int outputDesc, int targetSize);\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_gif_vendor.h",
    "content": "#define FILE_STATE_READ     0x08\n#define IS_READABLE(Private)    (Private->FileState & FILE_STATE_READ)\n#define INT_MAX 2147483647\n\ntypedef struct GifFilePrivateType {\n  GifWord FileState;\n  FILE *File;\n} GifFilePrivateType;\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_jpeg.c",
    "content": "#include <stdio.h>\n#include <jpeglib.h>\n#include <setjmp.h>\n#include <stdlib.h>\n#include \"utils.h\"\n\n#define JPEG_QUALITY 50\n\ntypedef struct filestash_jpeg_error_mgr {\n  struct jpeg_error_mgr pub;\n  jmp_buf jmp;\n} *filestash_jpeg_error_ptr;\n\nvoid filestash_jpeg_error_exit (j_common_ptr cinfo);\n\nint jpeg_to_jpeg(int inputDesc, int outputDesc, int targetSize) {\n#ifdef HAS_DEBUG\n  clock_t t;\n  t = clock();\n#endif\n  int status = 0;\n  FILE* input = fdopen(inputDesc, \"rb\");\n  FILE* output = fdopen(outputDesc, \"wb\");\n  if (!input || !output) {\n    return 1;\n  }\n\n  struct jpeg_decompress_struct jpeg_config_input;\n  struct jpeg_compress_struct jpeg_config_output;\n  struct filestash_jpeg_error_mgr jerr;\n\n  jpeg_config_input.err = jpeg_std_error(&jerr.pub);\n  jpeg_config_output.err = jpeg_std_error(&jerr.pub);\n  jpeg_config_input.dct_method = JDCT_IFAST;\n  jpeg_config_input.do_fancy_upsampling = FALSE;\n  jpeg_config_input.two_pass_quantize = FALSE;\n  jpeg_config_input.dither_mode = JDITHER_ORDERED;\n\n  jpeg_create_decompress(&jpeg_config_input);\n  jpeg_create_compress(&jpeg_config_output);\n  jpeg_stdio_src(&jpeg_config_input, input);\n  jpeg_stdio_dest(&jpeg_config_output, output);\n\n  jerr.pub.error_exit = filestash_jpeg_error_exit;\n  if (setjmp(jerr.jmp)) {\n    ERROR(\"exception\");\n    goto CLEANUP_AND_ABORT;\n  }\n\n  DEBUG(\"after constructor decompress\");\n  if(jpeg_read_header(&jpeg_config_input, TRUE) != JPEG_HEADER_OK) {\n    status = 1;\n    ERROR(\"not a jpeg\");\n    goto CLEANUP_AND_ABORT;\n  }\n  DEBUG(\"after header read\");\n  jpeg_config_input.dct_method = JDCT_IFAST;\n  jpeg_config_input.do_fancy_upsampling = FALSE;\n  jpeg_config_input.two_pass_quantize = FALSE;\n  jpeg_config_input.dither_mode = JDITHER_ORDERED;\n  jpeg_calc_output_dimensions(&jpeg_config_input);\n\n  int image_min_size = min(jpeg_config_input.output_width, jpeg_config_input.output_height);\n  jpeg_config_input.scale_num = 1;\n  jpeg_config_input.scale_denom = 1;\n  int targetSizeAbs = abs(targetSize);\n  if (image_min_size / 8 >= targetSizeAbs) {\n    jpeg_config_input.scale_num = 1;\n    jpeg_config_input.scale_denom = 8;\n  } else if (image_min_size * 2 / 8 >= targetSizeAbs) {\n    jpeg_config_input.scale_num = 1;\n    jpeg_config_input.scale_denom = 4;\n  } else if (image_min_size * 3 / 8 >= targetSizeAbs) {\n    jpeg_config_input.scale_num = 3;\n    jpeg_config_input.scale_denom = 8;\n  } else if (image_min_size * 4 / 8 >= targetSizeAbs) {\n    jpeg_config_input.scale_num = 4;\n    jpeg_config_input.scale_denom = 8;\n  } else if (image_min_size * 5 / 8 >= targetSizeAbs) {\n    jpeg_config_input.scale_num = 5;\n    jpeg_config_input.scale_denom = 8;\n  } else if (image_min_size * 6 / 8 >= targetSizeAbs) {\n    jpeg_config_input.scale_num = 6;\n    jpeg_config_input.scale_denom = 8;\n  } else if (image_min_size * 7 / 8 >= targetSizeAbs) {\n    jpeg_config_input.scale_num = 7;\n    jpeg_config_input.scale_denom = 8;\n  }\n\n  DEBUG(\"start decompress\");\n  if(jpeg_start_decompress(&jpeg_config_input) == FALSE) {\n    ERROR(\"jpeg_start_decompress\");\n    status = 1;\n    goto CLEANUP_AND_ABORT;\n  }\n  DEBUG(\"processing image setup\");\n  int jpeg_row_stride = jpeg_config_input.output_width * jpeg_config_input.output_components;\n  jpeg_config_output.image_width = jpeg_config_input.output_width;\n  jpeg_config_output.image_height = jpeg_config_input.output_height;\n  jpeg_config_output.input_components = jpeg_config_input.num_components;\n  jpeg_config_output.in_color_space = jpeg_config_input.out_color_space;\n  jpeg_set_defaults(&jpeg_config_output);\n  jpeg_set_quality(&jpeg_config_output, JPEG_QUALITY, TRUE);\n  jpeg_start_compress(&jpeg_config_output, TRUE);\n  JSAMPARRAY buffer = jpeg_config_input.mem->alloc_sarray((j_common_ptr) &jpeg_config_input, JPOOL_IMAGE, jpeg_row_stride, 1);\n\n  DEBUG(\"processing image\");\n  while (jpeg_config_output.next_scanline < jpeg_config_output.image_height) {\n    jpeg_read_scanlines(&jpeg_config_input, buffer, 1);\n    jpeg_write_scanlines(&jpeg_config_output, buffer, 1);\n  }\n\n  DEBUG(\"end decompress\");\n  jpeg_finish_decompress(&jpeg_config_input);\n  DEBUG(\"finish decompress\");\n  jpeg_finish_compress(&jpeg_config_output);\n\n CLEANUP_AND_ABORT:\n  jpeg_destroy_decompress(&jpeg_config_input);\n  jpeg_destroy_compress(&jpeg_config_output);\n  DEBUG(\"final\");\n  return status;\n}\n\nvoid filestash_jpeg_error_exit (j_common_ptr cinfo) {\n  filestash_jpeg_error_ptr filestash_err = (filestash_jpeg_error_ptr) cinfo->err;\n  longjmp(filestash_err->jmp, 1);\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_jpeg.h",
    "content": "int jpeg_to_jpeg(int input, int output, int targetSize);\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_jpeg_freebsd.go",
    "content": "package plg_image_c\n\n// #include \"image_jpeg.h\"\n// #cgo LDFLAGS: -L /usr/local/lib -L /usr/lib -L /lib -l:libjpeg.a\n// #cgo CFLAGS: -I /usr/local/include\nimport \"C\"\n\nfunc jpeg(input uintptr, output uintptr, size int) {\n\tC.jpeg_to_jpeg(C.int(input), C.int(output), C.int(size))\n\treturn\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_jpeg_linux.go",
    "content": "package plg_image_c\n\n// #include \"image_jpeg.h\"\n// #cgo LDFLAGS: -l:libjpeg.a\nimport \"C\"\n\nfunc jpeg(input uintptr, output uintptr, size int) {\n\tC.jpeg_to_jpeg(C.int(input), C.int(output), C.int(size))\n\treturn\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_png.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <png.h>\n#include <webp/encode.h>\n#include \"utils.h\"\n\nvoid png_read_error(png_structp png_ptr, png_const_charp error_msg) {\n  longjmp(png_jmpbuf(png_ptr), 1);\n}\n\nvoid png_read_warning(png_structp png_ptr, png_const_charp warning_msg) {\n  longjmp(png_jmpbuf(png_ptr), 1);\n}\n\nint png_to_webp(int inputDesc, int outputDesc, int targetSize) {\n#ifdef HAS_DEBUG\n  clock_t t;\n  t = clock();\n#endif\n  if (targetSize < 0 ) {\n    targetSize = -targetSize;\n  }\n  int status = 0;\n  FILE* input = fdopen(inputDesc, \"rb\");\n  FILE* output = fdopen(outputDesc, \"wb\");\n  if (!input || !output) {\n    return 1;\n  }\n\n  // STEP1: setup png\n  png_structp png_ptr = NULL;\n  png_infop info_ptr = NULL;\n  if(!(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_read_error, png_read_warning))) {\n    status = 1;\n    goto CLEANUP_AND_ABORT;\n  }\n  if (!(info_ptr = png_create_info_struct(png_ptr))) {\n    status = 1;\n    goto CLEANUP_AND_ABORT_A;\n  }\n  if (setjmp(png_jmpbuf(png_ptr))) {\n    status = 1;\n    goto CLEANUP_AND_ABORT_B;\n  }\n  png_init_io(png_ptr, input);\n  png_read_info(png_ptr, info_ptr);\n  png_set_strip_alpha(png_ptr);\n  png_uint_32 width = png_get_image_width(png_ptr, info_ptr);\n  png_uint_32 height = png_get_image_height(png_ptr, info_ptr);\n  png_byte color_type = png_get_color_type(png_ptr, info_ptr);\n  png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr);\n  if (color_type == PNG_COLOR_TYPE_PALETTE) {\n    png_set_palette_to_rgb(png_ptr);\n  }\n  if (color_type == PNG_COLOR_TYPE_GRAY) {\n    png_set_expand_gray_1_2_4_to_8(png_ptr);\n  }\n  if (color_type & PNG_COLOR_MASK_ALPHA) {\n    png_set_strip_alpha(png_ptr);\n  }\n  png_read_update_info(png_ptr, info_ptr);\n  DEBUG(\"after png construct\");\n\n  // STEP2: process the image\n  int scale_factor = height > targetSize ? height / targetSize : 1;\n  png_uint_32 thumb_width = width / scale_factor;\n  png_uint_32 thumb_height = height / scale_factor;\n\n  if (thumb_width == 0 || thumb_height == 0) {\n    ERROR(\"0 dimensions\");\n    status = 1;\n    goto CLEANUP_AND_ABORT_B;\n  }\n  uint8_t* webp_image_data = (uint8_t*)malloc(thumb_width * thumb_height * 3);\n  if (!webp_image_data) {\n    ERROR(\"malloc error\");\n    status = 1;\n    goto CLEANUP_AND_ABORT_B;\n  }\n  png_bytep row = (png_bytep)malloc(png_get_rowbytes(png_ptr, info_ptr));\n  if (!row) {\n    ERROR(\"malloc error\");\n    status = 1;\n    goto CLEANUP_AND_ABORT_B;\n  }\n  DEBUG(\"after png malloc\");\n  for (png_uint_32 y = 0; y < height; y++) {\n    png_read_row(png_ptr, row, NULL);\n    if (y % scale_factor == 0 && (y / scale_factor < thumb_height)) {\n      for (png_uint_32 x = 0; x < width; x += scale_factor) {\n        if (x / scale_factor < thumb_width) {\n          png_uint_32 thumb_x = x / scale_factor;\n          png_uint_32 thumb_y = y / scale_factor;\n          memcpy(webp_image_data + (thumb_y * thumb_width + thumb_x) * 3, row + x * 3, 3);\n        }\n      }\n    }\n  }\n  DEBUG(\"after png process\");\n  free(row);\n  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);\n  DEBUG(\"after png cleanup\");\n\n  // STEP3: save as webp\n  uint8_t* webp_output_data = NULL;\n  size_t webp_output_size = WebPEncodeRGB(webp_image_data, thumb_width, thumb_height, thumb_width * 3, 75, &webp_output_data);\n  free(webp_image_data);\n  DEBUG(\"after webp init\");\n  if (webp_output_data == NULL) {\n    status = 1;\n    goto CLEANUP_AND_ABORT_B;\n  } else if (webp_output_size == 0) {\n    status = 1;\n    goto CLEANUP_AND_ABORT_C;\n  }\n  fwrite(webp_output_data, webp_output_size, 1, output);\n  fflush(output);\n  DEBUG(\"after webp written\");\n\n CLEANUP_AND_ABORT_C:\n  if (webp_output_data != NULL) WebPFree(webp_output_data);\n\n CLEANUP_AND_ABORT_B:\n  if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);\n\n CLEANUP_AND_ABORT_A:\n  if (png_ptr != NULL) png_destroy_read_struct(&png_ptr, (info_ptr != NULL) ? &info_ptr : NULL, NULL);\n\n CLEANUP_AND_ABORT:\n  return status;\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_png.h",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint png_to_png(int input, int output, int targetSize);\n\nint png_to_webp(int input, int output, int targetSize);\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_png_freebsd.go",
    "content": "package plg_image_c\n\n// #include \"image_png.h\"\n// #cgo LDFLAGS: -L /usr/local/lib -L /usr/lib -L /lib -l:libsharpyuv.a -l:libpng.a -l:libz.a -l:libwebp.a -l:libpthread.a -fopenmp\n// #cgo CFLAGS: -I /usr/local/include\nimport \"C\"\n\nfunc png(input uintptr, output uintptr, size int) {\n\tC.png_to_webp(C.int(input), C.int(output), C.int(size))\n\treturn\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_png_linux.go",
    "content": "package plg_image_c\n\n// #include \"image_png.h\"\n// #cgo LDFLAGS: -l:libpng.a -l:libz.a -l:libwebp.a -fopenmp\nimport \"C\"\n\nfunc png(input uintptr, output uintptr, size int) {\n\tC.png_to_webp(C.int(input), C.int(output), C.int(size))\n\treturn\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_psd.c",
    "content": "#define STB_IMAGE_IMPLEMENTATION\n#include \"image_psd_vendor.h\"\n#include <webp/encode.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include \"utils.h\"\n\n#define BUF_SIZE 1024 * 16\n#define WEBP_QUALITY 50\n\nint psd_to_webp(int inputDesc, int outputDesc, int targetSize) {\n#ifdef HAS_DEBUG\n  clock_t t;\n  t = clock();\n#endif\n  FILE* input = fdopen(inputDesc, \"rb\");\n  FILE* output = fdopen(outputDesc, \"wb\");\n  int status = 0;\n  if (!input || !output) {\n    return 1;\n  }\n\n  // STEP1: write input to a file as stb doesn't work out well with our descriptor\n  char fname_in[32] = \"/tmp/filestash.XXXXXX\";\n  int _mkstemp_in = mkstemp(fname_in);\n  if (!_mkstemp_in) {\n    ERROR(\"mkstemp_in\");\n    return 1;\n  }\n  FILE* f_in = fdopen(_mkstemp_in, \"wb\");\n  if (!f_in) {\n    ERROR(\"fdopen\");\n    return 1;\n  }\n  char content[BUF_SIZE];\n  int read;\n  while ((read = fread(content, sizeof(char), BUF_SIZE, input))) {\n    fwrite(content, read, sizeof(char), f_in);\n  }\n  fclose(f_in);\n  DEBUG(\"setup\");\n\n  // STEP2: decode psd\n  DEBUG(\"init\");\n  int width, height, channels;\n  unsigned char* imageData = stbi_load(fname_in, &width, &height, &channels, 0);\n  if (!imageData) {\n    ERROR(\"cannot_load\");\n    status = 1;\n    goto CLEANUP_AND_ABORT;\n  }\n  DEBUG(\"decoded\");\n\n  size_t webp_output_size;\n  uint8_t* webp_output_data = NULL;\n  int success = 0;\n  if (channels == 3) {\n    success = WebPEncodeRGB(imageData, width, height, width * channels, WEBP_QUALITY, &webp_output_data);\n  } else if (channels == 4) {\n    success = WebPEncodeRGBA(imageData, width, height, width * channels, WEBP_QUALITY, &webp_output_data);\n  }\n  DEBUG(\"encoded\");\n\n  if (!success) {\n    stbi_image_free(imageData);\n    status = 1;\n    goto CLEANUP_AND_ABORT;\n  }\n  fwrite(webp_output_data, webp_output_size, 1, output);\n  fprintf(stderr, \"WRITEN[%d]\", webp_output_size);\n  fflush(output);\n\n  WebPFree(webp_output_data);\n  stbi_image_free(imageData);\n  DEBUG(\"done\");\n\n\n CLEANUP_AND_ABORT:\n  remove(fname_in);\n  return 0;\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_psd.go",
    "content": "//go:generate go run image_psd_generator.go\n\npackage plg_image_c\n\n// #include \"image_psd.h\"\n// #cgo LDFLAGS: -l:libwebp.a\nimport \"C\"\n\nfunc psd(input uintptr, output uintptr, size int) {\n\tC.psd_to_webp(C.int(input), C.int(output), C.int(size))\n\treturn\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_psd.h",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint psd_to_webp(int input, int output, int targetSize);\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_psd_generator.go",
    "content": "//go:build ignore\n\npackage main\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\n// stb_image v2.28 - https://github.com/nothings/stb\nconst stbImageURL = \"https://raw.githubusercontent.com/nothings/stb/5736b15f7ea0ffb08dd38af21067c314d6a3aae9/stb_image.h\"\n\nfunc main() {\n\tresp, err := http.Get(stbImageURL)\n\tif err != nil {\n\t\tlog.Fatalf(\"fetch stb_image.h: %v\", err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\tlog.Fatalf(\"fetch stb_image.h: status %d\", resp.StatusCode)\n\t}\n\tf, err := os.Create(\"image_psd_vendor.h\")\n\tif err != nil {\n\t\tlog.Fatalf(\"create image_psd_vendor.h: %v\", err)\n\t}\n\tdefer f.Close()\n\tif _, err = io.Copy(f, resp.Body); err != nil {\n\t\tlog.Fatalf(\"write image_psd_vendor.h: %v\", err)\n\t}\n\tlog.Println(\"wrote image_psd_vendor.h\")\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_raw.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <stdbool.h>\n#include <string.h>\n#include <unistd.h>\n#include <jpeglib.h>\n#include <setjmp.h>\n\nstatic bool write_preview(const uint8_t *buf, size_t len, int output);\nstatic bool write_thumbnail(const uint8_t *buf, size_t len, int output, int targetSize);\n\nvoid raw_to_jpeg(int inputDesc, int outputDesc, int targetSize) {\n    FILE *in = fdopen(inputDesc, \"rb\");\n    if (!in) { perror(\"fdopen\"); return; }\n\n    uint8_t  chunk[4096];\n    uint8_t  last = 0, curr;\n    bool     dumping = false;\n\n    uint8_t *cur_buf = NULL;\n    size_t   cur_len = 0, cur_cap = 0;\n\n    while (!feof(in)) {\n        size_t n = fread(chunk, 1, sizeof(chunk), in);\n        if (n == 0) break;\n\n        for (size_t i = 0; i < n; i++) {\n            curr = chunk[i];\n\n            if (dumping == true && cur_len + 1 > cur_cap) {\n                cur_cap = (cur_cap == 0 ? 4096 : cur_cap * 2);\n                cur_buf = realloc(cur_buf, cur_cap);\n                if (!cur_buf) {\n                    free(cur_buf);\n                    return;\n                }\n            }\n\n            // start of jpeg\n            if (dumping == false && last == 0xFF && curr == 0xD8) {\n                dumping = true;\n                cur_cap = 4096;\n                cur_len = 0;\n                cur_buf = malloc(cur_cap);\n                if (!cur_buf) return;\n                cur_buf[cur_len++] = 0xFF;\n                cur_buf[cur_len++] = 0xD8;\n            }\n            // end of jpeg\n            else if (dumping == true && last == 0xFF && curr == 0xD9) {\n                cur_buf[cur_len++] = curr;\n                if (targetSize > 0 && write_preview(cur_buf, cur_len, outputDesc)) {\n                    free(cur_buf);\n                    return;\n                } else if (targetSize <= 0 && write_thumbnail(cur_buf, cur_len, outputDesc, -targetSize)) {\n                    free(cur_buf);\n                    return;\n                }\n                cur_buf = NULL;\n                cur_len = cur_cap = 0;\n                dumping = false;\n            }\n            // body of jpeg\n            else if (dumping == true) {\n                cur_buf[cur_len++] = curr;\n            }\n            last = curr;\n        }\n    }\n    free(cur_buf);\n}\n\ntypedef struct filestash_raw_error_mgr {\n    struct jpeg_error_mgr pub;\n    jmp_buf jmp;\n} *filestash_raw_error_ptr;\n\nstatic void filestash_raw_error_exit (j_common_ptr cinfo) {\n    filestash_raw_error_ptr filestash_err = (filestash_raw_error_ptr) cinfo->err;\n    longjmp(filestash_err->jmp, 1);\n}\n\nstatic bool write_preview(const uint8_t *buf, size_t len, int output) {\n    struct jpeg_decompress_struct  cinfo;\n    struct filestash_raw_error_mgr jerr;\n    bool                           ok = true;\n\n    jpeg_create_decompress(&cinfo);\n    jpeg_mem_src(&cinfo, buf, len);\n    cinfo.err = jpeg_std_error(&jerr.pub);\n    jerr.pub.error_exit = filestash_raw_error_exit;\n    if (setjmp(jerr.jmp)) {\n        jpeg_destroy_decompress(&cinfo);\n        return false;\n    }\n\n    if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) ok = false;\n    else if (cinfo.image_width < 700) ok = false;\n\n    jpeg_destroy_decompress(&cinfo);\n    if (ok == true && write(output, buf, len) != (ssize_t)len) {\n        perror(\"write\");\n    }\n    return ok;\n}\n\nstatic bool write_thumbnail(const uint8_t *buf, size_t len, int output, int targetSize) {\n    struct jpeg_decompress_struct dinfo;\n    struct filestash_raw_error_mgr jerr;\n    bool ok = true;\n\n    jpeg_create_decompress(&dinfo);\n    jpeg_mem_src(&dinfo, buf, len);\n    dinfo.err           = jpeg_std_error(&jerr.pub);\n    jerr.pub.error_exit = filestash_raw_error_exit;\n    if (setjmp(jerr.jmp)) {\n        jpeg_destroy_decompress(&dinfo);\n        return false;\n    }\n\n    if (jpeg_read_header(&dinfo, TRUE) != JPEG_HEADER_OK || dinfo.image_width < 500) {\n        jpeg_destroy_decompress(&dinfo);\n        return false;\n    }\n\n    if (dinfo.image_width / 8 >= targetSize) {\n        dinfo.scale_num = 1;\n        dinfo.scale_denom = 8;\n    } else if (dinfo.image_width * 2 / 8 >= targetSize) {\n        dinfo.scale_num = 1;\n        dinfo.scale_denom = 4;\n    } else if (dinfo.image_width * 3 / 8 >= targetSize) {\n        dinfo.scale_num = 3;\n        dinfo.scale_denom = 8;\n    } else if (dinfo.image_width * 4 / 8 >= targetSize) {\n        dinfo.scale_num = 4;\n        dinfo.scale_denom = 8;\n    } else if (dinfo.image_width * 5 / 8 >= targetSize) {\n        dinfo.scale_num = 5;\n        dinfo.scale_denom = 8;\n    } else if (dinfo.image_width * 6 / 8 >= targetSize) {\n        dinfo.scale_num = 6;\n        dinfo.scale_denom = 8;\n    } else if (dinfo.image_width * 7 / 8 >= targetSize) {\n        dinfo.scale_num = 7;\n        dinfo.scale_denom = 8;\n    }\n\n    jpeg_start_decompress(&dinfo);\n    size_t stride = dinfo.output_width * dinfo.output_components;\n    JSAMPARRAY rowbuf = dinfo.mem->alloc_sarray((j_common_ptr)&dinfo, JPOOL_IMAGE, stride, 1);\n\n    struct jpeg_compress_struct cinfo;\n    struct jpeg_error_mgr       cerr;\n\n    cinfo.err = jpeg_std_error(&cerr);\n    jpeg_create_compress(&cinfo);\n\n    unsigned char *outbuf = NULL;\n    unsigned long  outlen = 0;\n    jpeg_mem_dest(&cinfo, &outbuf, &outlen);\n\n    cinfo.image_width      = dinfo.output_width;\n    cinfo.image_height     = dinfo.output_height;\n    cinfo.input_components = dinfo.output_components;\n    cinfo.in_color_space   = dinfo.out_color_space;\n\n    jpeg_set_defaults(&cinfo);\n    jpeg_set_quality(&cinfo, 70, TRUE);\n    jpeg_start_compress(&cinfo, TRUE);\n    while (cinfo.next_scanline < cinfo.image_height) {\n        jpeg_read_scanlines(&dinfo, rowbuf, 1);\n        jpeg_write_scanlines(&cinfo, rowbuf, 1);\n    }\n    jpeg_finish_compress(&cinfo);\n    jpeg_destroy_compress(&cinfo);\n\n    jpeg_finish_decompress(&dinfo);\n    jpeg_destroy_decompress(&dinfo);\n\n    if (write(output, outbuf, outlen) != (ssize_t)outlen) {\n        perror(\"write\");\n        ok = false;\n    }\n    free(outbuf);\n    return ok;\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_raw.h",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <libraw/libraw.h>\n#include \"utils.h\"\n#include \"image_jpeg.h\"\n\nint raw_to_jpeg(int inputDesc, int outputDesc, int targetSize);\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_raw_freebsd.go",
    "content": "package plg_image_c\n\n// #include \"image_raw.h\"\n// #cgo LDFLAGS: -L /usr/local/lib -L /usr/lib -L /lib -l:libyuv.a -l:libjpeg.a -l:libraw.a -fopenmp -l:libc++.a -llcms2 -lm\n// #cgo CFLAGS: -I /usr/local/include\nimport \"C\"\n\nfunc raw(input uintptr, output uintptr, size int) {\n\tC.raw_to_jpeg(C.int(input), C.int(output), C.int(size))\n\treturn\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_raw_linux.go",
    "content": "package plg_image_c\n\n// #include \"image_raw.h\"\n// #cgo LDFLAGS: -l:libjpeg.a -l:libraw.a -fopenmp -l:libstdc++.a -llcms2 -lm\nimport \"C\"\n\nfunc raw(input uintptr, output uintptr, size int) {\n\tC.raw_to_jpeg(C.int(input), C.int(output), C.int(size))\n\treturn\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_webp.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <webp/decode.h>\n#include <webp/encode.h>\n#include \"utils.h\"\n\n#define WEBP_QUALITY 75\n#define INITIAL_BUFFER_SIZE 1024*64 // 128kB\n#define MAX_BUFFER_SIZE 1024*1024*2 // 2MB\n\nint webp_to_webp(int inputDesc, int outputDesc, int targetSize) {\n#ifdef HAS_DEBUG\n  clock_t t;\n  t = clock();\n#endif\n  if (targetSize < 0) {\n    targetSize = -targetSize;\n  }\n  int status = 0;\n  FILE* input = fdopen(inputDesc, \"rb\");\n  FILE* output = fdopen(outputDesc, \"wb\");\n  if (!input || !output) {\n    ERROR(\"setup\");\n    return 1;\n  }\n\n  // STEP1: setup everything\n  size_t data_size = 0;\n  size_t buffer_size = INITIAL_BUFFER_SIZE;\n  uint8_t* data = (uint8_t*)malloc(buffer_size);\n  if (!data) {\n    ERROR(\"malloc\");\n    return 1;\n  }\n  size_t bytes_read;\n  while ((bytes_read = fread(data + data_size, 1, buffer_size - data_size, input)) > 0) {\n    data_size += bytes_read;\n    if (buffer_size - data_size == 0) {\n      DEBUG(\"realloc\");\n      if (buffer_size >= MAX_BUFFER_SIZE) {\n        free(data);\n        ERROR(\"abort\");\n        return 1;\n      }\n      buffer_size *= 2;\n      if (buffer_size > MAX_BUFFER_SIZE) buffer_size = MAX_BUFFER_SIZE;\n      uint8_t* new_data = (uint8_t*)realloc(data, buffer_size);\n      if (!new_data) {\n        free(data);\n        ERROR(\"realloc\");\n        return 1;\n      }\n      data = new_data;\n    }\n  }\n\n  // STEP2: decode\n  int width, height, scale_factor;\n  if (!WebPGetInfo(data, data_size, &width, &height)) {\n    free(data);\n    ERROR(\"init\");\n    return 1;\n  }\n  DEBUG(\"init\");\n  WebPDecoderConfig config;\n  if (!WebPInitDecoderConfig(&config)) {\n    free(data);\n    ERROR(\"config\");\n    return 1;\n  }\n  scale_factor = (height > targetSize) ? height / targetSize : 1;\n  config.options.use_scaling = 1;\n  config.options.scaled_width = width / scale_factor;\n  config.options.scaled_height = height / scale_factor;\n  config.output.colorspace = MODE_rgbA;\n  DEBUG(\"config\");\n  if (WebPDecode(data, data_size, &config) != VP8_STATUS_OK) {\n    WebPFreeDecBuffer(&config.output);\n    free(data);\n    ERROR(\"decode\");\n    return 1;\n  }\n  free(data);\n  DEBUG(\"decode\");\n\n  // STEP3: encode\n  size_t output_size = 0;\n  uint8_t* output_data = NULL;\n  output_size = WebPEncodeRGBA(\n      config.output.u.RGBA.rgba, config.options.scaled_width,\n      config.options.scaled_height, config.output.u.RGBA.stride,\n      WEBP_QUALITY, &output_data\n  );\n  if (output_data == NULL) {\n    WebPFreeDecBuffer(&config.output);\n    ERROR(\"encode\");\n    return 1;\n  }\n  DEBUG(\"encode\");\n  fwrite(output_data, output_size, 1, output);\n  fflush(output);\n  WebPFree(output_data);\n  WebPFreeDecBuffer(&config.output);\n  DEBUG(\"done\");\n  return status;\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_webp.go",
    "content": "package plg_image_c\n\n// #include \"image_webp.h\"\n// #cgo LDFLAGS: -l:libwebp.a -l:libsharpyuv.a\nimport \"C\"\n\nfunc webp(input uintptr, output uintptr, size int) {\n\tC.webp_to_webp(C.int(input), C.int(output), C.int(size))\n\treturn\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/image_webp.h",
    "content": "int webp_to_webp(int inputDesc, int outputDesc, int targetSize);\n"
  },
  {
    "path": "server/plugin/plg_image_c/index.go",
    "content": "package plg_image_c\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\n/*\n * All the transcoders are reponsible for:\n * 1. create thumbnails if needed\n * 2. transcode various files if needed\n *\n * Under the hood, our transcoders are C programs that takes 3 arguments:\n * 1/2. the input/output file descriptors. We use file descriptors to communicate from go -> C -> go\n * 3. the target size. by convention those program handles:\n *    - positive size: when we want to transcode a file with best effort in regards to quality and\n *      not lose metadata, typically when this will be open in an image viewer from which we might have\n *      frontend code to extract exif/xmp metadata, ...\n *    - negative size: when we want transcode to be done as quickly as possible, typically when we want\n *      to create a thumbnail and don't care/need anything else than speed\n */\n\nfunc init() {\n\tHooks.Register.Thumbnailer(\"image/jpeg\", &transcoder{runner(jpeg), \"image/jpeg\", -200})\n\tHooks.Register.Thumbnailer(\"image/png\", &transcoder{runner(png), \"image/webp\", -200})\n\tHooks.Register.Thumbnailer(\"image/gif\", &transcoder{runner(gif), \"image/webp\", -300})\n\tHooks.Register.Thumbnailer(\"image/webp\", &transcoder{runner(webp), \"image/webp\", -200})\n\trawMimeType := []string{\n\t\t\"image/x-canon-cr2\", \"image/x-tif\", \"image/x-canon-cr2\", \"image/x-canon-crw\",\n\t\t\"image/x-nikon-nef\", \"image/x-nikon-nrw\", \"image/x-sony-arw\", \"image/x-sony-sr2\",\n\t\t\"image/x-minolta-mrw\", \"image/x-minolta-mdc\", \"image/x-olympus-orf\", \"image/x-panasonic-rw2\",\n\t\t\"image/x-pentax-pef\", \"image/x-epson-erf\", \"image/x-raw\", \"image/x-x3f\", \"image/x-fuji-raf\",\n\t\t\"image/x-aptus-mos\", \"image/x-mamiya-mef\", \"image/x-hasselblad-3fr\", \"image/x-adobe-dng\",\n\t\t\"image/x-samsung-srw\", \"image/x-kodak-kdc\", \"image/x-kodak-dcr\",\n\t}\n\tfor _, mType := range rawMimeType {\n\t\tHooks.Register.Thumbnailer(mType, &transcoder{runner(raw), \"image/jpeg\", -200})\n\t}\n\n\tHooks.Register.ProcessFileContentBeforeSend(func(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, bool, error) {\n\t\tquery := req.URL.Query()\n\t\tmType := GetMimeType(query.Get(\"path\"))\n\t\tif strings.HasPrefix(mType, \"image/\") == false {\n\t\t\treturn reader, false, nil\n\t\t} else if query.Get(\"thumbnail\") == \"true\" {\n\t\t\treturn reader, false, nil\n\t\t} else if query.Get(\"size\") == \"\" {\n\t\t\treturn reader, false, nil\n\t\t}\n\t\tsizeInt, err := strconv.Atoi(query.Get(\"size\"))\n\t\tif err != nil {\n\t\t\treturn reader, false, nil\n\t\t}\n\t\tif !contains(rawMimeType, mType) {\n\t\t\treturn reader, false, nil\n\t\t}\n\t\tthumb, err := transcoder{runner(raw), \"image/jpeg\", sizeInt}.Generate(reader, ctx, res, req)\n\t\treturn thumb, true, err\n\t})\n}\n\ntype transcoder struct {\n\tfn   func(input io.ReadCloser, size int) (io.ReadCloser, error)\n\tmime string\n\tsize int\n}\n\nfunc (this transcoder) Generate(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, error) {\n\tthumb, err := this.fn(reader, this.size)\n\tif err == nil && this.mime != \"\" {\n\t\t(*res).Header().Set(\"Content-Type\", this.mime)\n\t}\n\treturn thumb, err\n}\n\n/*\n * uuuh, what is this stuff you might rightly wonder? Trying to send a go stream to C isn't obvious,\n * but if you try to stream from C back to go in the same time, this is what you endup with.\n * To my knowledge using file descriptor is the best way we can do that if we don't make the assumption\n * that everything fits in memory.\n */\nfunc runner(fn func(uintptr, uintptr, int)) func(io.ReadCloser, int) (io.ReadCloser, error) {\n\treturn func(inputGo io.ReadCloser, size int) (io.ReadCloser, error) {\n\t\tinputC, tmpw, err := os.Pipe()\n\t\tlogErrors(err, \"plg_image_c::pipe\")\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\toutputGo, outputC, err := os.Pipe()\n\t\tlogErrors(err, \"plg_image_c::pipe\")\n\t\tif err != nil {\n\t\t\ttmpw.Close()\n\t\t\treturn nil, err\n\t\t}\n\n\t\tgo func() {\n\t\t\tfn(inputC.Fd(), outputC.Fd(), size) // <-- all this code so we can do that\n\t\t\tlogErrors(inputC.Close(), \"plg_image_c::inputC\")\n\t\t\tlogErrors(inputGo.Close(), \"plg_image_c::inputGo\")\n\t\t\tlogErrors(outputC.Close(), \"plg_image_c::outputC\")\n\t\t}()\n\t\tgo func() {\n\t\t\tio.Copy(tmpw, inputGo)\n\t\t\tlogErrors(tmpw.Close(), \"plg_image_c::tmpw\")\n\t\t}()\n\t\treturn outputGo, nil\n\t}\n}\n\nfunc logErrors(err error, msg string) {\n\tif err == nil {\n\t\treturn\n\t}\n\tLog.Debug(msg + \": \" + err.Error())\n}\n\nfunc contains(s []string, str string) bool {\n\tfor _, v := range s {\n\t\tif v == str {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "server/plugin/plg_image_c/utils.h",
    "content": "#define HAS_DEBUG 0\n#include <time.h>\n#if HAS_DEBUG == 1\n#define DEBUG(r) (fprintf(stderr,  \"[DEBUG::('\" r \"')(%.2Fms)]\", ((double)clock() - t)/CLOCKS_PER_SEC * 1000))\n#else\n#define DEBUG(r) ((void)0)\n#endif\n\n#define ERROR(r) (fprintf(stderr, \"[ERROR:('\" r \"')]\"))\n\n#define min(a, b) (a > b ? b : a)\n"
  },
  {
    "path": "server/plugin/plg_image_golang/index.go",
    "content": "package plg_image_golang\n\nimport (\n\t\"bytes\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"golang.org/x/image/draw\"\n\t\"image\"\n\t_ \"image/gif\"\n\t_ \"image/jpeg\"\n\t\"image/png\"\n\n\t\"io\"\n\t\"net/http\"\n)\n\nconst THUMB_SIZE int = 400\n\nfunc init() {\n\tHooks.Register.Thumbnailer(\"image/jpeg\", thumbnailer{})\n\tHooks.Register.Thumbnailer(\"image/png\", thumbnailer{})\n\tHooks.Register.Thumbnailer(\"image/gif\", thumbnailer{})\n}\n\ntype thumbnailer struct{}\n\nfunc (this thumbnailer) Generate(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, error) {\n\tquery := req.URL.Query()\n\tmType := GetMimeType(query.Get(\"path\"))\n\n\tif query.Get(\"thumbnail\") != \"true\" {\n\t\treturn reader, nil\n\t} else if mType != \"image/jpeg\" && mType != \"image/png\" && mType != \"image/gif\" {\n\t\treturn reader, nil\n\t}\n\n\tsrc, _, err := image.Decode(reader)\n\tif err != nil {\n\t\treturn reader, nil\n\t}\n\tratio := func(i image.Image) int {\n\t\tb := src.Bounds()\n\t\tmax := b.Max.X\n\t\tif b.Max.X < b.Max.Y {\n\t\t\tmax = b.Max.Y\n\t\t}\n\t\tr := max / THUMB_SIZE\n\t\tif r <= 1 {\n\t\t\treturn 1\n\t\t}\n\t\treturn r\n\t}(src)\n\n\tdst := image.NewRGBA(image.Rect(0, 0, src.Bounds().Max.X/ratio, src.Bounds().Max.Y/ratio))\n\tdraw.ApproxBiLinear.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)\n\toutput := bytes.NewBuffer([]byte(\"\"))\n\tif err = png.Encode(output, dst); err != nil {\n\t\treturn reader, err\n\t}\n\treturn NewReadCloserFromBytes(output.Bytes()), nil\n}\n"
  },
  {
    "path": "server/plugin/plg_image_light/deps/README.md",
    "content": "plg_image_light rely on a few libraries for:\n- image transcoding: libtranscode.a: a library build on top of of libraw\n- image resizing: libresize.a: a library built on top of libvips\n\nTo create the libraries to be used by Filestash:\n```\n./create_libtranscode.sh\n./create_libresize.sh\n```\n\nTo test the libraries are working fine:\n```\n# libtranscode:\ngcc -Wall -c src/libtranscode_test.c\ngcc -o main_transcode.bin libtranscode_test.o -lm -lpthread -L. -l:libtranscode.a\ncurl -O https://archive.kerjean.me/public/2020/sample.CR2\n./main_transcode.bin ./sample.CR2\n\n# libresize:\ngcc -Wall -c src/libresize_test.c `pkg-config --cflags glib-2.0`\ngcc -o main_resize.bin libresize_test.o -lm -lgmodule-2.0 -lgobject-2.0 -lglib-2.0 -L. -l:libresize.a\ncurl -O https://archive.kerjean.me/public/2020/sample.jpg\n./main_resize.bin ./sample.jpg\n```\n"
  },
  {
    "path": "server/plugin/plg_image_light/deps/create_libresize.sh",
    "content": "#!/bin/sh\n# # This script handle the creation of the static library used by the plugin to\n# # perform image resizing jobs. You can run it like this:\n# docker run -ti --name debian_build_dep -v /home/:/home/ debian:9 bash\n# apt-get -y update && apt-get -y install git\n# git clone https://github.com/mickael-kerjean/filestash\n# cd filestash/server/plugin/plg_image_light/deps/\n# ./create_libresize.sh\nset -e\narch=$(dpkg --print-architecture)\nif [ $arch != \"amd64\" ] && [ $arch != \"armhf\" ]; then\n    echo \"PLATFORM NOT SUPPORTED\"\n    exit 1\nfi\n\n################################################\n# Tooling\napt install -y curl make gcc g++ xz-utils pkg-config python3-pip autoconf libtool unzip python-setuptools cmake git\n#pip3 install --user meson ninja\nexport PATH=~/.local/bin:$PATH\n\n################################################\n# Stage 1: Get libvips and its dependencies + recompile for less headaches\nINITIAL_PATH=`pwd`\nmkdir -p /tmp/filestash/libresize/tmp\ncd /tmp/filestash/libresize\napt install -y libvips-dev\ncd tmp\ncurl -L -X GET https://github.com/libvips/libvips/releases/download/v8.7.0/vips-8.7.0.tar.gz > libvips.tar.gz\ntar -zxf libvips.tar.gz\ncd vips-8.7.0/\n./configure --enable-static --without-magick --without-lcms  --without-OpenEXR --without-nifti --without-pdfium --without-rsvg --without-matio --without-libwebp --without-cfitsio --without-zlib --without-poppler --without-pangoft2 --enable-introspection=no --without-openslide\nmake -j 8\nmake install\ncd $INITIAL_PATH\n\n################################################\n# Stage 2: Create our own library as a static build\ngcc -Wall -c src/libresize.c `pkg-config --cflags glib-2.0`\nar rcs libresize.a libresize.o\n\n################################################\n# Stage 3: Gather and assemble all the bits and pieces together\nlibpath=$(\n    if [ $arch = \"amd64\" ]; then\n        echo \"x86_64-linux-gnu\";\n    elif [ $arch = \"armhf\" ]; then\n        echo \"arm-linux-gnueabihf\"\n    fi\n)\n#ar x /tmp/libresize.a\nar x /usr/local/lib/libvips.a\n\nar x /usr/lib/$libpath/libz.a\nar x /usr/lib/$libpath/libbz2.a\nar x /usr/lib/$libpath/libjpeg.a\nar x /usr/lib/$libpath/libgif.a\nar x /usr/lib/$libpath/libdl.a\nar x /usr/lib/$libpath/libicui18n.a\nar x /usr/lib/$libpath/libgsf-1.a\nar x /usr/lib/$libpath/libicuuc.a\nar x /usr/lib/$libpath/libicudata.a\nar x /usr/lib/$libpath/liblzma.a\nar x /usr/lib/$libpath/libfreetype.a\nar x /usr/lib/$libpath/libfftw3.a\nar x /usr/lib/$libpath/libfontconfig.a\nar x /usr/lib/$libpath/libXext.a\nar x /usr/lib/$libpath/libSM.a\nar x /usr/lib/$libpath/libX11.a\nar x /usr/lib/$libpath/liborc-0.4.a\nar x /usr/lib/$libpath/libltdl.a\nar x /usr/lib/$libpath/librt.a\nar x /usr/lib/$libpath/libharfbuzz.a\nar x /usr/lib/$libpath/libexpat.a\nar x /usr/lib/$libpath/libgio-2.0.a\nar x /usr/lib/$libpath/libpng16.a\nar x /usr/lib/$libpath/libpixman-1.a\nar x /usr/lib/$libpath/libxcb.a\nar x /usr/lib/$libpath/libjbig.a\nar x /usr/lib/$libpath/libexif.a\nar x /usr/lib/$libpath/libpcre.a\nar x /usr/lib/$libpath/libtiff.a\nar x /usr/lib/$libpath/libpangoft2-1.0.a\nar x /usr/lib/$libpath/libpoppler.a\n\nar rcs libresize.a *.o\nrm *.o *.ao\n\n#scp libresize.a mickael@hal.kerjean.me:/home/app/pages/data/projects/filestash/downloads/upload/libresize_`uname -s`-`uname -m`.a\n\n################################################\n# Stage 4: Gather all the related headers\n#cd /usr/include/\n#tar zcf /tmp/libresize-headers.tar.gz .\n#scp /tmp/libresize-headers.tar.gz mickael@hal.kerjean.me:/home/app/pages/data/projects/filestash/downloads/upload/libresize_`uname -s`-`uname -m`_headers.tar.gz\n"
  },
  {
    "path": "server/plugin/plg_image_light/deps/create_libtranscode.sh",
    "content": "#!/bin/sh\n# # This script handle the creation of the static library used by the plugin to\n# # perform transcoding jobs. You can run it like this:\n# docker run -ti --name debian_build_dep -v /home/:/home/ debian:9 bash\n# apt-get -y update && apt-get -y install git\n# git clone https://github.com/mickael-kerjean/filestash\n# cd filestash/server/plugin/plg_image_light/deps/\n# ./create_libtranscode.sh\nset -e\narch=$(dpkg --print-architecture)\nif [ $arch != \"amd64\" ] && [ $arch != \"armhf\" ]; then\n    echo \"PLATFORM NOT SUPPORTED\"\n    exit 1\nfi\n\n################################################\n# Tooling\napt install -y curl make gcc g++ xz-utils pkg-config python3-pip autoconf libtool unzip python-setuptools cmake git\nexport PATH=~/.local/bin:$PATH\n\n################################################\n# Stage 1: Get libraw and its dependencies\nINITIAL_PATH=`pwd`\napt install -y libraw-dev\ncd /tmp/\n# libgomp and libstdc++\napt-get install -y libgcc-6-dev\n# libjpeg\napt-get install -y libjpeg-dev\n# liblcms2\ncurl -L -O https://downloads.sourceforge.net/project/lcms/lcms/2.9/lcms2-2.9.tar.gz\ntar zxf lcms2-2.9.tar.gz\ncd lcms2-2.9\n./configure --enable-static --without-zlib --without-threads\nmake -j 8\nmake install\n\ncd $INITIAL_PATH\n################################################\n# Stage 2: Create our own library as a static build\ngcc -Wall -c src/libtranscode.c\n\n################################################\n# Stage 3: Gather and assemble all the bits and pieces together\nlibpath=$(\n    if [ $arch = \"amd64\" ]; then\n        echo \"x86_64-linux-gnu\";\n    elif [ $arch = \"armhf\" ]; then\n        echo \"arm-linux-gnueabihf\"\n    fi\n)\nar x /usr/lib/$libpath/libraw.a\nar x /usr/lib/$libpath/libjpeg.a\nar x /usr/local/lib/liblcms2.a\nar x /usr/lib/gcc/$libpath/6/libstdc++.a\nar x /usr/lib/gcc/$libpath/6/libgomp.a\nar x /usr/lib/$libpath/libpthread.a\n\nar rcs libtranscode.a *.o\nrm *.o\n\n#scp libtranscode.a mickael@hal.kerjean.me:/home/app/pages/data/projects/filestash/downloads/upload/libtranscode_`uname -s`-`uname -m`.a\n\n################################################\n# Stage 4: Gather all the related headers\n#cd /usr/include/\n#tar zcf /tmp/libtranscode-headers.tar.gz .\n#scp /tmp/libtranscode-headers.tar.gz mickael@hal.kerjean.me:/home/app/pages/data/projects/filestash/downloads/upload/libtranscode_`uname -s`-`uname -m`_headers.tar.gz\n"
  },
  {
    "path": "server/plugin/plg_image_light/deps/src/libresize.c",
    "content": "#include <stdio.h>\n#include <vips/vips.h>\n\nint image_resize(const char *filename, void **buf, size_t *len, int size, int crop, int quality, int exif){\n  VipsImage *img;\n  int err;\n\n  size = size > 4000 || size < 0 ? 1000 : size;\n  crop = crop == 0 ? VIPS_INTERESTING_NONE : VIPS_INTERESTING_CENTRE;\n  quality = quality > 100 || quality < 0 ? 80 : quality;\n  exif = exif == 0 ? TRUE : FALSE;\n\n  if(crop == VIPS_INTERESTING_CENTRE){\n    // Generate a thumbnails: a square picture crop in the center\n    err = vips_thumbnail(filename, &img, size,\n        \"size\", VIPS_SIZE_BOTH,\n        \"auto_rotate\", TRUE,\n        \"crop\", VIPS_INTERESTING_CENTRE,\n        NULL\n    );\n  }else{\n    // normal resize of an image with libvips\n    err = vips_thumbnail(filename, &img, size,\n        \"size\", VIPS_SIZE_DOWN,\n        \"auto_rotate\", TRUE,\n        \"crop\", VIPS_INTERESTING_NONE,\n        NULL\n    );\n  }\n  if(err != 0){\n    return err;\n  }\n\n  err = vips_jpegsave_buffer(img, buf, len, \"Q\", quality, \"strip\", exif, NULL);\n  g_object_unref(img);\n  return err;\n}\n\nvoid null_log_handler (const gchar *a, GLogLevelFlags l, const gchar *m, gpointer ud){ }\n\nvoid __attribute__ ((constructor)) initLibrary(void) {\n  VIPS_INIT(\"imagevips\");\n  vips_cache_set_max(0);\n  g_log_set_handler( \"VIPS\", G_LOG_LEVEL_WARNING, null_log_handler, NULL);\n}\nvoid __attribute__ ((destructor)) cleanUpLibrary(void) {\n  vips_shutdown();\n}\n"
  },
  {
    "path": "server/plugin/plg_image_light/deps/src/libresize.h",
    "content": "#include <stdlib.h>\n\nint image_resize(const char *filename, void **buf, size_t *len, int size, int crop, int quality, int exif);\n"
  },
  {
    "path": "server/plugin/plg_image_light/deps/src/libresize_test.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include \"libresize.h\"\n\ndouble benchmark_image_resize(int n, const char*input);\n\nint main(int argc, char **argv) {\n  if(argc != 2){\n    printf(\"missing argument: need a path to an image\\n\");\n    exit(1);\n  }\n  printf(\"=> benchmark %s: %.2fms\\n\", argv[1], benchmark_image_resize(20, argv[1]));\n}\n\ndouble benchmark_image_resize(int n, const char* input) {\n  double total = 0;\n  void *buffer;\n  size_t len;\n\n  int i = 0;\n  for(i=0; i<n; i++){\n    clock_t begin = clock();\n    image_resize(input, &buffer, &len, 200, 1, 90, 0);\n    clock_t end = clock();\n    total += (double)(end - begin) / CLOCKS_PER_SEC * 1000;\n  }\n  return total / n;\n}\n"
  },
  {
    "path": "server/plugin/plg_image_light/deps/src/libtranscode.c",
    "content": "#include <stdlib.h>\n#include <libraw/libraw.h>\n\n#define FALSE 0\n#define TRUE !FALSE\n\nint image_transcode_compute(const char* filename, int min_width) {\n  int err;\n  libraw_data_t *raw;\n  int has_thumbnail = FALSE;\n\n  //////////////////////\n  // boot up libraw\n  raw = libraw_init(0);\n  if(libraw_open_file(raw, filename) != 0){\n    libraw_close(raw);\n    return 1;\n  }\n  raw->params.output_tiff = 1;\n\n  //////////////////////\n  // use thumbnail if available\n  if(libraw_unpack_thumb(raw) == 0){\n    has_thumbnail = TRUE;\n    if(raw->thumbnail.twidth > min_width && raw->thumbnail.tformat == LIBRAW_THUMBNAIL_JPEG){\n      err = libraw_dcraw_thumb_writer(raw, filename);\n      libraw_close(raw);\n      return err;\n    }\n  }\n  fflush(stdout);\n\n  //////////////////////\n  // transcode image\n  if(libraw_unpack(raw) != 0){\n    if(has_thumbnail == TRUE){\n      err = libraw_dcraw_thumb_writer(raw, filename);\n      libraw_close(raw);\n      return err;\n    }\n    libraw_close(raw);\n    return 0;\n  }\n\n  err = libraw_dcraw_process(raw);\n  if(err != 0){\n    if(err == LIBRAW_UNSUFFICIENT_MEMORY){\n      libraw_close(raw);\n      return -1;\n    }\n    if(has_thumbnail == TRUE){\n      err = libraw_dcraw_thumb_writer(raw, filename);\n      libraw_close(raw);\n      return err;\n    }\n    libraw_close(raw);\n    return 1;\n  }\n\n  if(libraw_dcraw_ppm_tiff_writer(raw, filename) != 0){\n    if(has_thumbnail == TRUE){\n      err = libraw_dcraw_thumb_writer(raw, filename);\n      libraw_close(raw);\n      return err;\n    }\n    libraw_close(raw);\n    return 1;\n  }\n\n  libraw_close(raw);\n  return 0;\n}\n"
  },
  {
    "path": "server/plugin/plg_image_light/deps/src/libtranscode.h",
    "content": "#include <stdlib.h>\n\nint image_transcode_compute(const char* filename, int min_width);\n"
  },
  {
    "path": "server/plugin/plg_image_light/deps/src/libtranscode_test.c",
    "content": "#include <stdio.h>\n#include <time.h>\n#include \"libtranscode.h\"\n\ndouble benchmark_image_transcode(int n, const char*input);\n\nint main(int argc, char **argv) {\n  if(argc != 2){\n    printf(\"missing argument: need a path to an image\\n\");\n    exit(1);\n  }\n  printf(\"=> benchmark %s: %.2fms\\n\", argv[1], benchmark_image_transcode(20, argv[1]));\n}\n\ndouble benchmark_image_transcode(int n, const char* input) {\n  double total = 0;\n  int i = 0;\n  for(i=0; i<n; i++){\n    clock_t begin = clock();\n    image_transcode_compute(input, 200);\n    clock_t end = clock();\n    total += (double)(end - begin) / CLOCKS_PER_SEC * 1000;\n  }\n  return total / n;\n}\n"
  },
  {
    "path": "server/plugin/plg_image_light/index.go",
    "content": "package plg_image_light\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tplugin_enable := func() bool {\n\t\treturn Config.Get(\"features.image.enable_image\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Name = \"enable_image\"\n\t\t\tf.Type = \"enable\"\n\t\t\tf.Target = []string{\"image_thumbsize\", \"image_thumbquality\", \"image_imagequality\", \"image_thumbcache\", \"image_imagecache\"}\n\t\t\tf.Default = true\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\tthumb_size := func() int {\n\t\treturn Config.Get(\"features.image.thumbnail_size\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Type = \"number\"\n\t\t\tf.Id = \"image_thumbsize\"\n\t\t\tf.Name = \"thumbnail_size\"\n\t\t\tf.Description = \"Thumbnail size in pixel\"\n\t\t\tf.Placeholder = \"Default: 300\"\n\t\t\tf.Default = 300\n\t\t\treturn f\n\t\t}).Int()\n\t}\n\tthumb_quality := func() int {\n\t\treturn Config.Get(\"features.image.thumbnail_quality\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"image_thumbquality\"\n\t\t\tf.Type = \"number\"\n\t\t\tf.Name = \"thumbnail_quality\"\n\t\t\tf.Description = \"image quality on thumbnails. A lower number means smaller size at the cost of potential visual artifacts\"\n\t\t\tf.Placeholder = \"Default: 50\"\n\t\t\tf.Default = 50\n\t\t\treturn f\n\t\t}).Int()\n\t}\n\tthumb_caching := func() int {\n\t\treturn Config.Get(\"features.image.thumbnail_caching\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"image_thumbcache\"\n\t\t\tf.Type = \"number\"\n\t\t\tf.Name = \"thumbnail_caching\"\n\t\t\tf.Description = \"How much time in seconds we want to store a thumbnail in the browser\"\n\t\t\tf.Placeholder = \"Default: 259200 => 3 days\"\n\t\t\tf.Default = 259200\n\t\t\treturn f\n\t\t}).Int()\n\t}\n\timage_quality := func() int {\n\t\treturn Config.Get(\"features.image.image_quality\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"image_imagequality\"\n\t\t\tf.Type = \"number\"\n\t\t\tf.Name = \"image_quality\"\n\t\t\tf.Description = \"image quality on fullsize images. A lower number means smaller size at the cost of potential visual artifacts\"\n\t\t\tf.Placeholder = \"Default: 90\"\n\t\t\tf.Default = 90\n\t\t\treturn f\n\t\t}).Int()\n\t}\n\timage_caching := func() int {\n\t\treturn Config.Get(\"features.image.image_caching\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"image_imagecache\"\n\t\t\tf.Type = \"number\"\n\t\t\tf.Name = \"image_caching\"\n\t\t\tf.Description = \"How much time in seconds we want to store images on the browser\"\n\t\t\tf.Placeholder = \"Default: 3600 => 1 hour\"\n\t\t\tf.Default = 3600\n\t\t\treturn f\n\t\t}).Int()\n\t}\n\tHooks.Register.Onload(func() {\n\t\tplugin_enable()\n\t\tthumb_size()\n\t\tthumb_quality()\n\t\tthumb_caching()\n\t\timage_quality()\n\t\timage_caching()\n\t})\n\n\tHooks.Register.ProcessFileContentBeforeSend(func(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, error) {\n\t\tif plugin_enable() == false {\n\t\t\treturn reader, nil\n\t\t}\n\n\t\tquery := req.URL.Query()\n\t\tmType := GetMimeType(query.Get(\"path\"))\n\n\t\tif strings.HasPrefix(mType, \"image/\") == false {\n\t\t\treturn reader, nil\n\t\t} else if mType == \"image/svg+xml\" {\n\t\t\treturn reader, nil\n\t\t} else if mType == \"image/x-icon\" {\n\t\t\treturn reader, nil\n\t\t} else if query.Get(\"thumbnail\") != \"true\" && query.Get(\"size\") == \"\" {\n\t\t\treturn reader, nil\n\t\t} else if query.Get(\"thumbnail\") != \"true\" && mType == \"image/gif\" {\n\t\t\treturn reader, nil\n\t\t}\n\n\t\t/////////////////////////\n\t\t// Specify transformation\n\t\ttransform := &Transform{\n\t\t\tInput:   filepath.Join(GetAbsolutePath(TMP_PATH), \"imagein_\"+QuickString(10)),\n\t\t\tSize:    thumb_size(),\n\t\t\tCrop:    true,\n\t\t\tQuality: thumb_quality(),\n\t\t\tExif:    false,\n\t\t}\n\t\tif query.Get(\"thumbnail\") == \"true\" {\n\t\t\t(*res).Header().Set(\"Cache-Control\", fmt.Sprintf(\"max-age=%d\", thumb_caching()))\n\t\t} else if query.Get(\"size\") != \"\" {\n\t\t\t(*res).Header().Set(\"Cache-Control\", fmt.Sprintf(\"max-age=%d\", image_caching()))\n\t\t\tsize, err := strconv.ParseInt(query.Get(\"size\"), 10, 64)\n\t\t\tif err != nil {\n\t\t\t\treturn reader, nil\n\t\t\t}\n\t\t\ttransform.Size = int(size)\n\t\t\ttransform.Crop = false\n\t\t\ttransform.Quality = image_quality()\n\t\t\ttransform.Exif = true\n\t\t}\n\n\t\t/////////////////////////////\n\t\t// Insert file in the fs\n\t\t// => impedance matching with something usable by CGO\n\t\tfile, err := os.OpenFile(transform.Input, os.O_WRONLY|os.O_CREATE, os.ModePerm)\n\t\tif err != nil {\n\t\t\treturn reader, ErrFilesystemError\n\t\t}\n\t\tio.Copy(file, reader)\n\t\tfile.Close()\n\t\treader.Close()\n\t\tdefer func() {\n\t\t\tos.Remove(transform.Input)\n\t\t}()\n\n\t\t/////////////////////////\n\t\t// Transcode RAW image\n\t\tif IsRaw(mType) {\n\t\t\tif ExtractPreview(transform) == nil {\n\t\t\t\tmType = \"image/jpeg\"\n\t\t\t\t(*res).Header().Set(\"Content-Type\", mType)\n\t\t\t} else {\n\t\t\t\treturn reader, nil\n\t\t\t}\n\t\t}\n\n\t\t/////////////////////////\n\t\t// final stage: resizing\n\t\tif mType != \"image/jpeg\" && mType != \"image/png\" && mType != \"image/gif\" && mType != \"image/tiff\" {\n\t\t\treturn reader, nil\n\t\t}\n\n\t\treturn CreateThumbnail(transform)\n\t})\n}\n\ntype Transform struct {\n\tInput   string\n\tSize    int\n\tCrop    bool\n\tQuality int\n\tExif    bool\n}\n"
  },
  {
    "path": "server/plugin/plg_image_light/install.sh",
    "content": "#/bin/bash\nset -e\n\necho \"= INSTALL LIBS\"\nSCRIPTPATH=\"$( cd \"$(dirname \"$0\")\" ; pwd -P )\"\ncd \"$(dirname \"$0\")\"/deps\n\n# AMD64\ncurl -sk https://downloads.filestash.app/upload/libresize_Linux-x86_64.a > libresize_linux_amd64.a &\ncurl -sk https://downloads.filestash.app/upload/libtranscode_Linux-x86_64.a > libtranscode_linux_amd64.a &\n\n# ARM\ncurl -sk https://downloads.filestash.app/upload/libresize_Linux-armv7l.a > libresize_linux_arm.a &\ncurl -sk https://downloads.filestash.app/upload/libtranscode_Linux-armv7l.a > libtranscode_linux_arm.a &\n\nwait\n"
  },
  {
    "path": "server/plugin/plg_image_light/lib_resize.go",
    "content": "package plg_image_light\n\n// #cgo CFLAGS: -I./deps/src\n// #cgo pkg-config:glib-2.0\n// #include \"glib-2.0/glib.h\"\n// #include \"libresize.h\"\nimport \"C\"\n\nimport (\n\t\"context\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"golang.org/x/sync/semaphore\"\n\t\"io\"\n\t\"time\"\n\t\"unsafe\"\n)\n\nconst (\n\tTHUMBNAIL_TIMEOUT        = 5 * time.Second\n\tTHUMBNAIL_MAX_CONCURRENT = 50\n)\n\nvar VIPS_LOCK = semaphore.NewWeighted(THUMBNAIL_MAX_CONCURRENT)\n\nfunc CreateThumbnail(t *Transform) (io.ReadCloser, error) {\n\tctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(THUMBNAIL_TIMEOUT))\n\tdefer cancel()\n\tif err := VIPS_LOCK.Acquire(ctx, 1); err != nil {\n\t\treturn nil, ErrCongestion\n\t}\n\tdefer VIPS_LOCK.Release(1)\n\n\timageChannel := make(chan io.ReadCloser, 1)\n\tgo func() {\n\t\tfilename := C.CString(t.Input)\n\t\tlen := C.size_t(0)\n\t\tvar buffer unsafe.Pointer\n\t\tif C.image_resize(filename, &buffer, &len, C.int(t.Size), boolToCInt(t.Crop), C.int(t.Quality), boolToCInt(t.Exif)) != 0 {\n\t\t\tC.free(unsafe.Pointer(filename))\n\t\t\timageChannel <- nil\n\t\t\treturn\n\t\t}\n\t\tC.free(unsafe.Pointer(filename))\n\t\tbuf := C.GoBytes(buffer, C.int(len))\n\t\tC.g_free(C.gpointer(buffer))\n\t\timageChannel <- NewReadCloserFromBytes(buf)\n\t}()\n\n\tselect {\n\tcase img := <-imageChannel:\n\t\tif img == nil {\n\t\t\treturn nil, ErrNotValid\n\t\t}\n\t\treturn img, nil\n\tcase <-ctx.Done():\n\t\treturn nil, ErrTimeout\n\t}\n}\n\nfunc boolToCInt(val bool) C.int {\n\tif val == false {\n\t\treturn C.int(0)\n\t}\n\treturn C.int(1)\n}\n"
  },
  {
    "path": "server/plugin/plg_image_light/lib_resize_linux_amd64.go",
    "content": "package plg_image_light\n\n// #cgo LDFLAGS: -lm -lgmodule-2.0 -lgobject-2.0 -lglib-2.0 -ldl -L./deps -l:libresize_linux_amd64.a\nimport \"C\"\n"
  },
  {
    "path": "server/plugin/plg_image_light/lib_resize_linux_arm.go",
    "content": "package plg_image_light\n\n// #cgo LDFLAGS: -lm -lgmodule-2.0 -lgobject-2.0 -lglib-2.0 -ldl -L./deps -l:libresize_linux_arm.a\nimport \"C\"\n"
  },
  {
    "path": "server/plugin/plg_image_light/lib_transcode.go",
    "content": "package plg_image_light\n\n// #cgo CFLAGS: -I./deps/src\n// #include \"libtranscode.h\"\nimport \"C\"\n\nimport (\n\t\"context\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"golang.org/x/sync/semaphore\"\n\t\"time\"\n\t\"unsafe\"\n)\n\nconst (\n\tTRANSCODE_TIMEOUT        = 10 * time.Second\n\tTRANSCODE_MAX_CONCURRENT = 5\n)\n\nvar LIBRAW_LOCK = semaphore.NewWeighted(int64(TRANSCODE_MAX_CONCURRENT))\n\nfunc IsRaw(mType string) bool {\n\tswitch mType {\n\tcase \"image/x-tif\":\n\tcase \"image/x-canon-cr2\":\n\tcase \"image/x-canon-crw\":\n\tcase \"image/x-nikon-nef\":\n\tcase \"image/x-nikon-nrw\":\n\tcase \"image/x-sony-arw\":\n\tcase \"image/x-sony-sr2\":\n\tcase \"image/x-minolta-mrw\":\n\tcase \"image/x-minolta-mdc\":\n\tcase \"image/x-olympus-orf\":\n\tcase \"image/x-panasonic-rw2\":\n\tcase \"image/x-pentax-pef\":\n\tcase \"image/x-epson-erf\":\n\tcase \"image/x-raw\":\n\tcase \"image/x-x3f\":\n\tcase \"image/x-fuji-raf\":\n\tcase \"image/x-aptus-mos\":\n\tcase \"image/x-mamiya-mef\":\n\tcase \"image/x-hasselblad-3fr\":\n\tcase \"image/x-adobe-dng\":\n\tcase \"image/x-samsung-srw\":\n\tcase \"image/x-kodak-kdc\":\n\tcase \"image/x-kodak-dcr\":\n\tdefault:\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc ExtractPreview(t *Transform) error {\n\tctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(TRANSCODE_TIMEOUT))\n\tdefer cancel()\n\n\tif err := LIBRAW_LOCK.Acquire(ctx, 1); err != nil {\n\t\treturn ErrCongestion\n\t}\n\tdefer LIBRAW_LOCK.Release(1)\n\n\ttranscodeChannel := make(chan error, 1)\n\tgo func() {\n\t\tfilename := C.CString(t.Input)\n\t\tdefer C.free(unsafe.Pointer(filename))\n\t\tif err := C.image_transcode_compute(filename, C.int(t.Size)); err != 0 {\n\t\t\ttranscodeChannel <- ErrNotValid\n\t\t}\n\t\ttranscodeChannel <- nil\n\t}()\n\n\tselect {\n\tcase err := <-transcodeChannel:\n\t\treturn err\n\tcase <-ctx.Done():\n\t\treturn ErrTimeout\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_image_light/lib_transcode_linux_amd64.go",
    "content": "package plg_image_light\n\n// #cgo LDFLAGS: -lm -lpthread -L./deps -l:libtranscode_linux_amd64.a\nimport \"C\"\n"
  },
  {
    "path": "server/plugin/plg_image_light/lib_transcode_linux_arm.go",
    "content": "package plg_image_light\n\n// #cgo LDFLAGS: -lm -lpthread -L./deps -l:libtranscode_linux_arm.a\nimport \"C\"\n"
  },
  {
    "path": "server/plugin/plg_image_transcode/index.go",
    "content": "package plg_image_transcode\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc init() {\n\tHooks.Register.ProcessFileContentBeforeSend(renderImages)\n}\n\nfunc renderImages(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, bool, error) {\n\tquery := req.URL.Query()\n\tif query.Get(\"thumbnail\") == \"true\" {\n\t\treturn reader, false, nil\n\t} else if query.Get(\"size\") == \"\" {\n\t\treturn reader, false, nil\n\t}\n\n\tvar (\n\t\tout io.ReadCloser = nil\n\t\terr error         = nil\n\t)\n\tmType := GetMimeType(query.Get(\"path\"))\n\tswitch mType {\n\tcase \"image/x-ms-bmp\":\n\t\tout, mType, err = transcodeBmp(reader)\n\tcase \"image/tiff\":\n\t\tout, mType, err = transcodeTiff(reader)\n\tcase \"image/dicom\":\n\t\tout, mType, err = transcodeDicom(reader)\n\tdefault:\n\t\treturn reader, false, nil\n\t}\n\treader.Close()\n\tif err == nil {\n\t\t(*res).Header().Set(\"Content-Type\", mType)\n\t}\n\tif err != nil && err != ErrNotImplemented && err != ErrNotValid {\n\t\tLog.Debug(\"plg_image_transcode::err %s\", err.Error())\n\t\treturn nil, false, ErrNotValid\n\t}\n\treturn out, true, err\n}\n"
  },
  {
    "path": "server/plugin/plg_image_transcode/transcode_bmp.go",
    "content": "package plg_image_transcode\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t_ \"golang.org/x/image/bmp\"\n\t\"image\"\n\t\"image/jpeg\"\n\t\"io\"\n)\n\nfunc transcodeBmp(reader io.Reader) (io.ReadCloser, string, error) {\n\timg, _, err := image.Decode(reader)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\tr, w := io.Pipe()\n\tgo func() {\n\t\terr := jpeg.Encode(w, img, &jpeg.Options{Quality: 80})\n\t\tw.Close()\n\t\tif err != nil {\n\t\t\tLog.Debug(\"plg_image_transcode::bmp jpeg encoding error '%s'\", err.Error())\n\t\t}\n\t}()\n\treturn NewReadCloserFromReader(r), \"image/jpeg\", nil\n}\n"
  },
  {
    "path": "server/plugin/plg_image_transcode/transcode_dicom.go",
    "content": "package plg_image_transcode\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/suyashkumar/dicom\"\n\t\"github.com/suyashkumar/dicom/pkg/tag\"\n\t\"image/jpeg\"\n\t\"io\"\n)\n\nfunc transcodeDicom(reader io.Reader) (io.ReadCloser, string, error) {\n\tvar b bytes.Buffer\n\tw := bufio.NewWriter(&b)\n\tio.Copy(w, reader)\n\n\tdataset, err := dicom.Parse(&b, int64(len(b.Bytes())), nil)\n\tif err != nil {\n\t\tLog.Debug(\"plg_image_transcode::dicom::parse '%s'\", err.Error())\n\t\treturn nil, \"\", ErrNotValid\n\t}\n\tpixelDataElement, err := dataset.FindElementByTag(tag.PixelData)\n\tif err != nil {\n\t\tLog.Debug(\"plg_image_transcode::dicom::findElementByTag '%s'\", err.Error())\n\t\treturn nil, \"\", ErrNotValid\n\t}\n\tpixelDataInfo := dicom.MustGetPixelDataInfo(pixelDataElement.Value)\n\n\tfor _, fr := range pixelDataInfo.Frames {\n\t\timg, err := fr.GetImage()\n\t\tif err != nil {\n\t\t\tif err.Error() == \"unsupported JPEG feature: unknown marker\" {\n\t\t\t\t// known issue with lossless jpeg codec which isn't supported in golang\n\t\t\t\t// and is not trivial to support in Filestash\n\t\t\t\treturn nil, \"\", ErrNotImplemented\n\t\t\t}\n\t\t\tLog.Stdout(\"plg_image_transcode_dicom::getImage '%s'\", err.Error())\n\t\t\treturn nil, \"\", err\n\t\t}\n\n\t\tr, w := io.Pipe()\n\t\tgo func() {\n\t\t\terr := jpeg.Encode(w, img, &jpeg.Options{Quality: 80})\n\t\t\tw.Close()\n\t\t\tif err != nil {\n\t\t\t\tLog.Debug(\"plg_image_transcode::dicom jpeg encoding error '%s'\", err.Error())\n\t\t\t}\n\t\t}()\n\t\treturn NewReadCloserFromReader(r), \"image/jpeg\", nil\n\t}\n\treturn nil, \"\", ErrNotValid\n}\n"
  },
  {
    "path": "server/plugin/plg_image_transcode/transcode_svg.go",
    "content": "package plg_image_transcode\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/srwiley/oksvg\"\n\t\"github.com/srwiley/rasterx\"\n\t\"image\"\n\t\"image/png\"\n\t\"io\"\n)\n\n/*\n * This bit isn't used because the rendering is very poor and would\n * generate too many bug reports\n */\nfunc transcodeSvg(reader io.Reader) (io.ReadCloser, string, error) {\n\ticon, err := oksvg.ReadIconStream(reader)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\ticon.SetTarget(0, 0, icon.ViewBox.W, icon.ViewBox.H)\n\twidth := int(icon.ViewBox.W)\n\theight := int(icon.ViewBox.H)\n\timg := image.NewRGBA(image.Rect(0, 0, width, height))\n\ticon.Draw(\n\t\trasterx.NewDasher(\n\t\t\twidth, height,\n\t\t\trasterx.NewScannerGV(width, height, img, img.Bounds()),\n\t\t), 1,\n\t)\n\n\tr, w := io.Pipe()\n\tgo func() {\n\t\terr := png.Encode(w, img)\n\t\tw.Close()\n\t\tif err != nil {\n\t\t\tLog.Debug(\"plg_image_transcode::svg png encoding error '%s'\", err.Error())\n\t\t}\n\t}()\n\treturn NewReadCloserFromReader(r), \"image/png\", nil\n}\n"
  },
  {
    "path": "server/plugin/plg_image_transcode/transcode_tiff.go",
    "content": "package plg_image_transcode\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t_ \"golang.org/x/image/tiff\"\n\t\"image\"\n\t\"image/jpeg\"\n\t\"io\"\n)\n\nfunc transcodeTiff(reader io.Reader) (io.ReadCloser, string, error) {\n\timg, _, err := image.Decode(reader)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\tr, w := io.Pipe()\n\tgo func() {\n\t\terr := jpeg.Encode(w, img, &jpeg.Options{Quality: 80})\n\t\tw.Close()\n\t\tif err != nil {\n\t\t\tLog.Debug(\"plg_image_transcode::tiff jpeg encoding error '%s'\", err.Error())\n\t\t}\n\t}()\n\treturn NewReadCloserFromReader(r), \"image/jpeg\", nil\n}\n"
  },
  {
    "path": "server/plugin/plg_license/index.go",
    "content": "package plg_license\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\ntype license struct {\n\tExpiry time.Time `json:\"expiry\"`\n\tName   string    `json:\"name\"`\n}\n\nfunc init() {\n\tlenv := os.Getenv(\"LICENSE\")\n\tHooks.Register.Onload(func() {\n\t\tif LICENSE != \"agpl\" && lenv == \"\" {\n\t\t\treturn\n\t\t}\n\t\tdata, err := DecryptString(fmt.Sprintf(\"%-16s\", \"filestash\"), Config.Get(\"general.license\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Name = \"license\"\n\t\t\tf.Type = \"text\"\n\t\t\tf.Placeholder = \"License Key\"\n\t\t\tf.Description = \"Reach out to support@filestash.app to get your license\"\n\t\t\tif lenv != \"\" {\n\t\t\t\tf.Value = lenv\n\t\t\t\tf.ReadOnly = true\n\t\t\t}\n\t\t\treturn f\n\t\t}).String())\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar lic license\n\t\tif err := json.Unmarshal([]byte(data), &lic); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif time.Now().After(lic.Expiry) {\n\t\t\tLog.Error(\"License expired. expiry=%s name=%s\", lic.Expiry, lic.Name)\n\t\t\tLog.Error(\"Contact support at support@filestash.app\")\n\t\t\tos.Exit(1)\n\t\t\treturn\n\t\t}\n\t\tsuffix := LICENSE\n\t\tif suffix == \"agpl\" {\n\t\t\tsuffix = \"base\"\n\t\t}\n\t\tLICENSE = lic.Name + \"::\" + suffix\n\t\tLog.Info(\"You are running Filestash \\\"%s\\\"\", LICENSE)\n\t})\n}\n"
  },
  {
    "path": "server/plugin/plg_metadata_sqlite/index.go",
    "content": "package plg_metadata_sqlite\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tdb, err := sql.Open(\"sqlite3\", GetAbsolutePath(DB_PATH, \"metadata.sql\"))\n\tif err != nil {\n\t\tLog.Error(\"plg_metadata_sqlite - cannot open sqlite metadata db: %s\", err.Error())\n\t\tos.Exit(1)\n\t\treturn\n\t}\n\tdb.Exec(`CREATE TABLE IF NOT EXISTS metadata (\n        tenantID TEXT NOT NULL,\n        path     TEXT NOT NULL,\n        value    TEXT NOT NULL,\n        PRIMARY KEY (tenantID, path)\n    )`)\n\tHooks.Register.Metadata(MetaImpl{db: db})\n}\n\ntype MetaImpl struct {\n\tdb *sql.DB\n}\n\nfunc (this MetaImpl) Get(ctx *App, path string) ([]FormElement, error) {\n\ttenantID := GenerateID(ctx.Session)\n\tforms := []FormElement{}\n\tvar blob string\n\terr := this.db.QueryRowContext(ctx.Context, \"SELECT value FROM metadata WHERE tenantID=? AND path = ?\", tenantID, path).Scan(&blob)\n\tif err == sql.ErrNoRows {\n\t\treturn forms, nil\n\t} else if err != nil {\n\t\treturn forms, err\n\t}\n\terr = json.Unmarshal([]byte(blob), &forms)\n\treturn forms, err\n}\n\nfunc (this MetaImpl) Set(ctx *App, path string, value []FormElement) error {\n\ttenantID := GenerateID(ctx.Session)\n\tif len(value) == 0 {\n\t\t_, err := this.db.Exec(\"DELETE FROM metadata WHERE tenantID=? AND path=?\", tenantID, path)\n\t\treturn err\n\t}\n\tblob, err := json.Marshal(value)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = this.db.Exec(`\n        INSERT INTO metadata (tenantID, path, value) VALUES (?, ?, ?)\n        ON CONFLICT(tenantID, path) DO UPDATE SET value=excluded.value\n    `, tenantID, path, string(blob))\n\treturn err\n}\n\nfunc (this MetaImpl) Search(ctx *App, path string, facets map[string]any) (map[string][]FormElement, error) {\n\ttenantID := GenerateID(ctx.Session)\n\trows, err := this.db.QueryContext(ctx.Context, `\n\t    SELECT path, value FROM metadata WHERE\n\t        tenantID=? AND\n\t        path LIKE ?\n            ORDER BY path\n\t`, tenantID, path+\"%\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tout := make(map[string][]FormElement)\n\tfor rows.Next() {\n\t\tvar (\n\t\t\tmetapath  string\n\t\t\tmetavalue []byte\n\t\t\tmetaforms []FormElement\n\t\t)\n\t\tif err = rows.Scan(&metapath, &metavalue); err != nil {\n\t\t\tbreak\n\t\t}\n\t\tif err = json.Unmarshal(metavalue, &metaforms); err != nil {\n\t\t\tbreak\n\t\t}\n\t\tisResult := false\n\t\tfor _, form := range metaforms {\n\t\t\tif form.Id == \"\" || facets[form.Id] == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfacetValues, ok := facets[form.Id].([]any)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tformValue := fmt.Sprintf(\"%s\", form.Value)\n\t\t\tisOK := true\n\t\t\tfor _, facetValue := range facetValues {\n\t\t\t\tif strings.Contains(formValue, fmt.Sprintf(\"%s\", facetValue)) == false {\n\t\t\t\t\tisOK = false\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif isOK {\n\t\t\t\tisResult = true\n\t\t\t}\n\t\t}\n\t\tif isResult {\n\t\t\tchroot := ctx.Session[\"path\"]\n\t\t\tif key := strings.TrimPrefix(metapath, chroot); key != \"\" {\n\t\t\t\tif !strings.HasPrefix(key, \"/\") {\n\t\t\t\t\tkey = \"/\" + key\n\t\t\t\t}\n\t\t\t\tout[key] = metaforms\n\t\t\t}\n\t\t}\n\t}\n\trows.Close()\n\treturn out, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_override_actiondelete/Makefile",
    "content": "all:\n\tmake install\n\ninstall:\n\tzip -r override_actiondelete.zip .\n\tmv override_actiondelete.zip ../../../dist/data/state/plugins/\n"
  },
  {
    "path": "server/plugin/plg_override_actiondelete/README.md",
    "content": "This plugin brings back the delete button from the original version of Filestash:\n\n<img src=\"https://i.imgur.com/HsFbbPs.png\" />\n\nYou can install it either as a [compiled time plugin](https://www.filestash.app/docs/guide/plugin-development.html#compiled-plugin) or a [runtime plugin](https://www.filestash.app/docs/guide/plugin-development.html#runtime-plugins)\n"
  },
  {
    "path": "server/plugin/plg_override_actiondelete/filespage_thing.diff",
    "content": "diff --git a/public/assets/pages/filespage/thing.js b/public/assets/pages/filespage/thing.js\nindex f6f0ec48..3c86ffcb 100644\n--- a/public/assets/pages/filespage/thing.js\n+++ b/public/assets/pages/filespage/thing.js\n@@ -1,0 +1,8 @@ import { addSelection, isSelected, clearSelection } from \"./state_selection.js\";\n+import rxjs from \"../../lib/rx.js\";\n+import { rm as rm$ } from \"./model_files.js\";\n+import { rm as rmVL } from \"./model_virtual_layer.js\";\n+import { basename } from \"../../lib/path.js\";\n+import t from \"../../locales/index.js\";\n+import { createModal } from \"../../components/modal.js\";\n+import componentDelete from \"./modal_delete.js\";\n+\n@@ -156,3 +156,24 @@ export function createThing({\n         return $thing;\n     }\n\n+    let $action = $thing.querySelector(\".component_action\");\n+    $action.innerHTML = `<img class=\"component_icon\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0ODIuNDI4IDQ4Mi40MjkiPgogIDxwYXRoIHN0eWxlPSJmaWxsOiM2ZjZmNmY7c3Ryb2tlLXdpZHRoOjAuOTQ3MjAwNTQiIGQ9Im0gMjM5LjcxMDM4LDEwLjg1ODU2NyBjIC0yOS4yODYzMywwLjE0ODk1OSAtNTYuMjI4MjEsMjMuMTU4MTY3IC02MS4xMzg4Myw1MS45OTYxMjkgLTAuMDM1OSw1LjUyMjQ0IC04LjExOTM2LDEuNTIzODUyIC0xMS44MTQxMSwyLjczNDMwMSAtMjEuNjU5MywwLjM1NzE4IC00My4zODAyLC0wLjY3Njg3NSAtNjUuMDA3MTksMC40Mzg0NTIgLTI1Ljc0Mzk2MSwyLjgxNDg5NiAtNDcuMDQxMDg0LDI2LjM4MTc2IC00Ny4xNzMxNzIsNTIuMjkyMTMxIC0xLjcyMjExOCwyMi4zMjI3NyAxMS42Nzg0MSw0NC43NzgwOSAzMi4zMjg3NjgsNTMuNTM1MzIgMS41MDI3NjcsNy4xMzU1IDAuMjE0MTksMTYuMTEyMjggMC42NDM4LDIzLjk1NTY4IDAuMTEwMTQ1LDc1LjI4MzExIC0wLjIxODQzMywxNTAuNTc3MzcgMC4xNjA5NSwyMjUuODUzNjcgMS40ODk4MDUsMjUuODUxOTIgMjMuOTUyNDE0LDQ4LjI5NzYgNDkuODA1NzI0LDQ5Ljc2Njg3IDY4Ljk5NTMyLDAuMjc5OTggMTM4LjAxNjU0LDAuMjI5NjYgMjA3LjAxMzE3LDAuMDI0NyAyNi4wMTg1MiwtMS4yNzY5MSA0OC43MjA1LC0yMy44MzQ0MyA1MC4xOTI0OSwtNDkuODM3NjcgMC4zNjUyOCwtODMuMTUzOTggMC4wNDk3LC0xNjYuMzI1MDggMC4xNTUzOCwtMjQ5LjQ4NTU4IDIwLjg0ODU5LC04LjUyMTk5IDM0LjU5NTY3LC0zMC45NzQ5OSAzMi45NzkzNiwtNTMuNDExMzEgMC4wNzUyLC0yNi4wNzE2MTEgLTIxLjMyNDY5LC00OS45MDA0NDIgLTQ3LjIyOTkxLC01Mi42OTkyMDYgLTI0LjY2MTA5LC0xLjA5MzY1MSAtNDkuNDEyODgsLTAuMDg0ODcgLTc0LjEwNTUsLTAuNDMyOSAtMy45NDgzNywwLjYxMjkxMSAtMi4zMDc4NywtNS4zNzQ4NTkgLTMuODc5MTQsLTcuOTc4OTIzIC03LjI2MTcsLTI3LjU4NzA0MiAtMzQuMzcxMDIsLTQ3Ljg1NDgyMzggLTYyLjkzMTc5LC00Ni43NTE2NDMgeiBtIDEuNTA0MDQsMjguNTMwNzE3IGMgMTUuNDcwMDYsLTAuMzA1NjE5IDMwLjI2NjY3LDExLjA4NDk0OCAzNC4wMzQ0NywyNi4xOTk3MTMgLTIyLjY4MzQsLTAuMDA1OSAtNDUuMzk2OTIsMC4wMTIzMyAtNjguMDYxNTQsLTAuMDA5MiAzLjgwNzAyLC0xNS4wNzAyMDQgMTguMzQxMTcsLTI2LjQxOTgyMyAzNC4wMjcwNywtMjYuMTkwNDYzIHogTSAxMDguNjc4NTEsOTQuMTM2MzYzIGMgODkuNDUyNTcsMC4xMzkxNjYgMTc4LjkyODgzLC0wLjI3NzY1MSAyNjguMzY2NjksMC4yMDcyIDEzLjc0MTMxLDEuNDE4NTc4IDIzLjkyNjY0LDE1LjE3NjA3NyAyMi4yNjY2MiwyOC43MDgzMTcgMC4wMzI1LDE0LjU1MDU0IC0xNC4wNzUxNCwyNi41NjAyNiAtMjguNDA0OTIsMjQuODcxNDIgLTg4LjUwNjYsLTAuMTQwMzcgLTE3Ny4wMzY5MSwwLjI4MDA2IC0yNjUuNTI4OCwtMC4yMDkwNiBDIDkxLjEyNTU5LDE0Ni4yNDIyMyA4MC45ODkyODUsMTMxLjcwMTYzIDgzLjE4MTc5NCwxMTcuNzAxNjggODMuOTcwODg3LDEwNC43NDEzIDk1LjU3NzEzMSw5My45MjA1OTYgMTA4LjY3ODUxLDk0LjEzNjM2MyBaIE0gMzY2LjMzLDE3Ni40NzA2NiBjIC0wLjE0MDc3LDgxLjQyODQ4IDAuMjgwNjIsMTYyLjg4MDcgLTAuMjA5MDUsMjQ0LjI5NDQ3IC0xLjQzODYyLDEzLjkxMTUxIC0xNS40NzY4NSwyNC4xMDU4OCAtMjkuMTUyMzIsMjIuMjc1ODggLTY2LjE5Njc4LC0wLjE0MTYyIC0xMzIuNDE3NDMsMC4yODE3NSAtMTk4LjU5OTQ1LC0wLjIwOTA2IC0xMy44OTE2OSwtMS40NDg4IC0yNC4xMTkwOSwtMTUuNDc1NzUgLTIyLjI3MjE2LC0yOS4xNTc4NiAwLC03OS4wNjc4MSAwLC0xNTguMTM1NjEgMCwtMjM3LjIwMzQzIDgzLjQxMDk4LDAgMTY2LjgyMTk4LDAgMjUwLjIzMjk4LDAgeiIgLz4KICA8cGF0aCBzdHlsZT0iZmlsbDojNmY2ZjZmO3N0cm9rZS13aWR0aDowLjk4MjA4MSIgZD0ibSAxNzEuNjg2NDQsMjQ3LjQ3Mzc5IGMgLTkuMzQ2NzYsMC4xNTY0NCAtMTUuNzQwMzIsOS44ODgwNSAtMTQuMDg2NzMsMTguNzExMzMgMC4xMjM1MSw0Ny42MjcwMSAtMC4yNDQwMSw5NS4yNzkwMyAwLjE3ODM5LDE0Mi44OTA4NyAxLjIwNzY0LDEwLjk3MTM2IDE1LjkxODAzLDE2LjUyNzk0IDI0LjA3MjQ5LDkuMDg0MjUgOC40MTc1OSwtNi44MTg4NyA0LjQ3NDY5LC0xOC44ODM5MiA1LjM0Nzc0LC0yOC4wODEzOCAtMC4xMjQzOSwtNDMuMzcxMjcgMC4yNDUyLC04Ni43Njc4NCAtMC4xNzgzOSwtMTMwLjEyMzgxIC0xLjAzNzk1LC03LjMxNDM5IC03Ljk1MDU0LC0xMi45NTcwNSAtMTUuMzMzNSwtMTIuNDgxMjYgeiIgLz4KICA8cGF0aCBzdHlsZT0iZmlsbDojNmY2ZjZmO3N0cm9rZS13aWR0aDowLjk4MjA4MSIgZD0ibSAyNDAuNTAxMTYsMjQ3LjQ3Mzc5IGMgLTkuMzQ2NDksMC4xNTYxNiAtMTUuNzQwNjcsOS44ODgxNyAtMTQuMDg2NzMsMTguNzExMzMgMC4xMjM1Miw0Ny42MjcwMSAtMC4yNDQwMSw5NS4yNzkwMyAwLjE3ODM5LDE0Mi44OTA4NyAxLjgwNTA0LDE3LjU2NDg5IDMwLjM3NDEyLDE1LjM0MjI3IDI5LjQyMDIzLC0yLjMwMTc2IC0wLjEyMzMzLC00OC45MzY0MiAwLjI0Mzc3LC05Ny44OTgyIC0wLjE3ODM4LC0xNDYuODE5MTggLTEuMDM3MzUsLTcuMzE0MSAtNy45NTEwMSwtMTIuOTU2OTQgLTE1LjMzMzUxLC0xMi40ODEyNiB6IiAvPgogIDxwYXRoIHN0eWxlPSJmaWxsOiM2ZjZmNmY7c3Ryb2tlLXdpZHRoOjAuOTgyMDgxIiBkPSJtIDMwOS4zMTU4OCwyNDcuNDczNzkgYyAtOS4zNDcxMSwwLjE1NTMgLTE1Ljc0MzE0LDkuODg3MTMgLTE0LjA4NjcyLDE4LjcxMTMzIDAuMTIzNTQsNDcuNjI0OTkgLTAuMjQ0MDYsOTUuMjc1ODEgMC4xNzgzOCwxNDIuODg1MTEgMS4xOTg1NiwxMC45NzMyMSAxNS45MTY2NCwxNi41MzYwNiAyNC4wNzA1OCw5LjA5MDAxIDguNDE5MzMsLTYuODE3ODcgNC40NzM2NiwtMTguODg0MiA1LjM0Nzc0LC0yOC4wODEzOCAtMC4xMjQ0MiwtNDMuMzcxMjQgMC4yNDUyNCwtODYuNzY4MDQgLTAuMTc4MzksLTEzMC4xMjM4MSAtMS4wMzY3NCwtNy4zMTMyMSAtNy45NTAxMiwtMTIuOTU2NTggLTE1LjMzMTU5LC0xMi40ODEyNiB6IiAvPgo8L3N2Zz4K\" />`;\n+    $action.onclick = (e) => {\n+        e.preventDefault();\n+        e.stopPropagation();\n+        const rm = (...paths) => withVirtualLayer(\n+            rm$(...paths),\n+            rmVL(...paths),\n+        );\n+        return rxjs.from(componentDelete(\n+            createModal({\n+                withButtonsRight: t(\"OK\"),\n+                withButtonsLeft: t(\"CANCEL\"),\n+            }),\n+            basename(path.replace(new RegExp(\"/$\"), \"\")).substr(0, 15),\n+        )).pipe(\n+            rxjs.mergeMap(() => rm(path)),\n+            rxjs.tap(() =>  window.dispatchEvent(new KeyboardEvent(\"keydown\", { keyCode: 27 }))),\n+        ).subscribe();\n+    };\n+\n"
  },
  {
    "path": "server/plugin/plg_override_actiondelete/index.go",
    "content": "package plg_override_actiondelete\n\nimport (\n\t_ \"embed\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\n//go:embed filespage_thing.diff\nvar PATCH []byte\n\nfunc init() {\n\tHooks.Register.StaticPatch(PATCH)\n}\n"
  },
  {
    "path": "server/plugin/plg_override_actiondelete/manifest.json",
    "content": "{\n    \"author\": \"Filestash Pty Ltd\",\n    \"version\": \"v0.0\",\n    \"modules\": [\n        {\n            \"type\": \"patch\",\n            \"entrypoint\": \"filespage_thing.diff\"\n        }\n    ]\n}\n"
  },
  {
    "path": "server/plugin/plg_override_download/README.md",
    "content": "```\ngit diff public/assets/pages/filespage/thing.js > server/plugin/plg_override_download/assets/pages/filespage/thing.js\n```\n"
  },
  {
    "path": "server/plugin/plg_override_download/assets/pages/filespage/thing.js",
    "content": "diff --git a/public/assets/pages/filespage/thing.js b/public/assets/pages/filespage/thing.js\nindex dd4f69ec..3bdcb97c 100644\n--- a/public/assets/pages/filespage/thing.js\n+++ b/public/assets/pages/filespage/thing.js\n@@ -35,6 +35,20 @@ export function init() {\n     };\n }\n \n+window.onDownload = function(e, self) {\n+    e.preventDefault(); e.stopPropagation();\n+    const path = self.parentElement.getAttribute(\"data-path\");\n+    const $link = document.createElement(\"a\");\n+    $link.download = path.split(\"/\").pop();\n+    if (isDir(path)) $link.download += \".zip\";\n+    $link.href = `/api/files/{verb}?path=${path}`.replace(\n+        \"{verb}\",\n+        isDir(path) ? \"zip\" : \"cat\",\n+    );\n+    $link.click();\n+    $link.remove();\n+};\n+\n const $tmpl = createElement(`\n     <a href=\"__TEMPLATE__\" class=\"component_thing no-select\" draggable=\"false\" data-link>\n         <div class=\"component_checkbox\"><input name=\"select\" type=\"checkbox\"><span class=\"indicator\"></span></div>\n@@ -46,6 +60,15 @@ const $tmpl = createElement(`\n             </span></span>\n         </span>\n         <span class=\"component_datetime\"></span>\n+        <div class=\"component_action\" onclick=\"onDownload(event, this)\">\n+            <span><img class=\"component_icon\" draggable=\"false\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzODQgNTEyIj4KICA8cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNIDM2MCw0NjAgSCAyNCBDIDEwLjcsNDYwIDAsNDUzLjMgMCw0NDAgdiAtMTIgYyAwLC0xMy4zIDEwLjcsLTIwIDI0LC0yMCBoIDMzNiBjIDEzLjMsMCAyNCw2LjcgMjQsMjAgdiAxMiBjIDAsMTMuMyAtMTAuNywyMCAtMjQsMjAgeiIgLz4KICA8cGF0aCBmaWxsPSIjZjJmMmYyIiBkPSJNIDIyNi41NTM5LDIzNC44ODQyOCBWIDUyLjk0MzI4MyBjIDAsLTYuNjI3IC01LjM3MywtMTIgLTEyLC0xMiBoIC00NCBjIC02LjYyNywwIC0xMiw1LjM3MyAtMTIsMTIgViAyMzQuODg0MjggaCAtNTIuMDU5IGMgLTIxLjM4MiwwIC0zMi4wOSwyNS44NTEgLTE2Ljk3MSw0MC45NzEgbCA4Ni4wNTksODYuMDU5IGMgOS4zNzMsOS4zNzMgMjQuNTY5LDkuMzczIDMzLjk0MSwwIGwgODYuMDU5LC04Ni4wNTkgYyAxNS4xMTksLTE1LjExOSA0LjQxMSwtNDAuOTcxIC0xNi45NzEsLTQwLjk3MSB6IiAvPgo8L3N2Zz4K\" alt=\"download\"></span>\n+        </div>\n+        <style>\n+        .component_action > span > .component_icon { box-sizing: border-box; background: rgba(0,0,0,0.1); border-radius: 50%; padding: 4px; filter: contrast(0); }\n+        .component_action { display: none; float: right; color: #6f6f6f; line-height: 25px; margin: 0 -10px; padding: 0 10px; position: relative; }\n+        .list .component_action { position: absolute; top: 10px; right: 10px; }\n+        .component_thing:hover .component_action { display: block; }\n+        </style>\n         <div class=\"selectionOverlay\"></div>\n     </a>\n `);\n"
  },
  {
    "path": "server/plugin/plg_override_download/index.go",
    "content": "package plg_override_download\n\nimport (\n\t\"embed\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\n//go:embed assets/*\nvar STATIC embed.FS\n\nfunc init() {\n\tHooks.Register.StaticPatch(STATIC)\n}\n"
  },
  {
    "path": "server/plugin/plg_search_example/index.go",
    "content": "package plg_search_example\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.SearchEngine(ExampleSearch{})\n}\n\ntype ExampleSearch struct{}\n\nfunc (this ExampleSearch) Query(app App, path string, keyword string) ([]IFile, error) {\n\tfiles := []IFile{}\n\tfiles = append(files, File{\n\t\tFName: \"keyword-\" + keyword + \".txt\",\n\t\tFType: \"file\", // ENUM(\"file\", \"directory\")\n\t\tFSize: 42,\n\t\tFPath: \"/fullpath/keyword-\" + keyword + \".txt\",\n\t})\n\treturn files, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/config/configuration.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tSEARCH_ENABLE()\n\t\tSEARCH_EXCLUSION()\n\t\tSEARCH_PROCESS_MAX()\n\t\tSEARCH_PROCESS_PAR()\n\t\tSEARCH_REINDEX()\n\t\tSEARCH_SHARED_INDEX()\n\t\tCYCLE_TIME()\n\t\tMAX_INDEXING_FSIZE()\n\t\tINDEXING_EXT()\n\t})\n}\n\nvar SEARCH_ENABLE = func() bool {\n\treturn Config.Get(\"features.search.enable\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"enable\"\n\t\tf.Type = \"boolean\"\n\t\tf.Description = \"Enable/Disable full text search automatic indexing\"\n\t\tf.Placeholder = \"Default: false\"\n\t\tf.Default = false\n\t\treturn f\n\t}).Bool()\n}\n\nvar SEARCH_EXCLUSION = func() []string {\n\tlistOfFolders := Config.Get(\"features.search.folder_exclusion\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"folder_exclusion\"\n\t\tf.Type = \"text\"\n\t\tf.Description = \"Exclude some specific folder from the crawl / index\"\n\t\tf.Placeholder = \"Default: node_modules,bower_components,.cache,.npm,.git\"\n\t\tf.Default = \"node_modules,bower_components,.cache,.npm,.git\"\n\t\treturn f\n\t}).String()\n\tout := []string{}\n\tfor _, folder := range strings.Split(listOfFolders, \",\") {\n\t\tout = append(out, strings.TrimSpace(folder))\n\t}\n\treturn out\n}\n\nvar SEARCH_PROCESS_MAX = func() int {\n\treturn Config.Get(\"features.search.process_max\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"process_max\"\n\t\tf.Type = \"number\"\n\t\tf.Description = \"Size of the pool containing the indexers\"\n\t\tf.Placeholder = \"Default: 5\"\n\t\tf.Default = 5\n\t\treturn f\n\t}).Int()\n}\n\nvar SEARCH_PROCESS_PAR = func() int {\n\treturn Config.Get(\"features.search.process_par\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"process_par\"\n\t\tf.Type = \"number\"\n\t\tf.Description = \"How many concurrent indexers are running in the same time (requires a restart)\"\n\t\tf.Placeholder = \"Default: 2\"\n\t\tf.Default = 2\n\t\treturn f\n\t}).Int()\n}\n\nvar SEARCH_REINDEX = func() int {\n\treturn Config.Get(\"features.search.reindex_time\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"reindex_time\"\n\t\tf.Type = \"number\"\n\t\tf.Description = \"Time in hours after which we consider our index to be stale and needs to be reindexed\"\n\t\tf.Placeholder = \"Default: 24h\"\n\t\tf.Default = 24\n\t\treturn f\n\t}).Int()\n}\n\nvar SEARCH_SHARED_INDEX = func() bool {\n\treturn Config.Get(\"features.search.shared_index\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"shared_index\"\n\t\tf.Type = \"boolean\"\n\t\tf.Description = \"Use a single search index shared across all users\"\n\t\tf.Default = false\n\t\treturn f\n\t}).Bool()\n}\n\nvar CYCLE_TIME = func() int {\n\treturn Config.Get(\"features.search.cycle_time\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"cycle_time\"\n\t\tf.Type = \"number\"\n\t\tf.Description = \"Time allocation for each cycle in seconds (discovery, indexing and maintenance)\"\n\t\tf.Placeholder = \"Default: 10s\"\n\t\tf.Default = 10\n\t\treturn f\n\t}).Int()\n}\n\nvar MAX_INDEXING_FSIZE = func() int {\n\treturn Config.Get(\"features.search.max_size\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"max_size\"\n\t\tf.Type = \"number\"\n\t\tf.Description = \"Maximum size of files the indexer will perform full text search\"\n\t\tf.Placeholder = \"Default: 524288000 => 512MB\"\n\t\tf.Default = 524288000\n\t\treturn f\n\t}).Int()\n}\nvar INDEXING_EXT = func() string {\n\treturn Config.Get(\"features.search.indexer_ext\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"indexer_ext\"\n\t\tf.Type = \"text\"\n\t\tf.Description = \"Extensions that will be handled by the full text search engine\"\n\t\tf.Placeholder = \"Default: org,txt,docx,pdf,md,form,xlsx,pptx\"\n\t\tf.Default = \"org,txt,docx,pdf,md,form,xlsx,pptx\"\n\t\treturn f\n\t}).String()\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/converter/index.go",
    "content": "package converter\n\nimport (\n\t\"io\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/model/formater\"\n)\n\nfunc Convert(path string, reader io.ReadCloser) (out io.ReadCloser, err error) {\n\tswitch GetMimeType(path) {\n\tcase \"text/plain\":\n\t\tout, err = formater.TxtFormater(reader)\n\tcase \"text/org\":\n\t\tout, err = formater.TxtFormater(reader)\n\tcase \"text/markdown\":\n\t\tout, err = formater.TxtFormater(reader)\n\tcase \"application/x-form\":\n\t\tout, err = formater.TxtFormater(reader)\n\tcase \"application/pdf\":\n\t\tout, err = formater.PdfFormater(reader)\n\tcase \"application/excel\":\n\t\tout, err = formater.OfficeFormater(reader)\n\tcase \"application/powerpoint\":\n\t\tout, err = formater.OfficeFormater(reader)\n\tcase \"application/vnd.ms-powerpoint\":\n\t\tout, err = formater.OfficeFormater(reader)\n\tcase \"application/word\":\n\t\tout, err = formater.OfficeFormater(reader)\n\tcase \"application/msword\":\n\t\tout, err = formater.OfficeFormater(reader)\n\tdefault:\n\t\terr = ErrNotImplemented\n\t}\n\treturn out, err\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/crawler/daemon.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/config\"\n)\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tfor i := 0; i < SEARCH_PROCESS_PAR(); i++ {\n\t\t\tgo runner()\n\t\t}\n\t})\n}\n\nfunc runner() {\n\tfor {\n\t\tif SEARCH_ENABLE() == false {\n\t\t\ttime.Sleep(60 * 5 * time.Second)\n\t\t\tcontinue\n\t\t}\n\t\tcrwlr := NextCrawler()\n\t\tif crwlr == nil {\n\t\t\ttime.Sleep(5 * time.Second)\n\t\t\tcontinue\n\t\t}\n\t\tcrwlr.mu.Lock()\n\t\tcrwlr.Run()\n\t\tcrwlr.mu.Unlock()\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/crawler/daemon_state.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t\"container/heap\"\n\t\"sync\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/config\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/indexer\"\n)\n\nvar DaemonState = daemonState{\n\tidx: make([]Crawler, 0),\n\tn:   -1,\n}\n\ntype daemonState struct {\n\tidx []Crawler\n\tn   int\n\tmu  sync.RWMutex\n}\n\ntype Crawler struct {\n\tId             string\n\tFoldersUnknown HeapDoc\n\tCurrentPhase   string\n\tBackend        IBackend\n\tState          indexer.Index\n\tmu             sync.Mutex\n}\n\nfunc NewCrawler(app *App) (Crawler, error) {\n\tid := GenerateID(app.Session)\n\tidpath := id\n\tif SEARCH_SHARED_INDEX() {\n\t\tidpath = \"\"\n\t}\n\ts := Crawler{\n\t\tId:             id,\n\t\tBackend:        app.Backend,\n\t\tState:          indexer.NewIndex(idpath),\n\t\tFoldersUnknown: make(HeapDoc, 0, 1),\n\t}\n\tif err := s.State.Init(); err != nil {\n\t\treturn s, err\n\t}\n\theap.Init(&s.FoldersUnknown)\n\treturn s, nil\n}\n\nfunc GetCrawler(app *App) *Crawler {\n\tid := GenerateID(app.Session)\n\tDaemonState.mu.RLock()\n\tdefer DaemonState.mu.RUnlock()\n\tfor i := len(DaemonState.idx) - 1; i >= 0; i-- {\n\t\tif id == DaemonState.idx[i].Id {\n\t\t\treturn &DaemonState.idx[i]\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc NextCrawler() *Crawler {\n\tDaemonState.mu.Lock()\n\tdefer DaemonState.mu.Unlock()\n\tif len(DaemonState.idx) == 0 {\n\t\treturn nil\n\t}\n\tif DaemonState.n >= len(DaemonState.idx)-1 || DaemonState.n < 0 {\n\t\tDaemonState.n = 0\n\t} else {\n\t\tDaemonState.n = DaemonState.n + 1\n\t}\n\treturn &DaemonState.idx[DaemonState.n]\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/crawler/events.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t\"container/heap\"\n\t\"context\"\n\t\"path/filepath\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/config\"\n)\n\n/*\n * We're listening to what the user is doing to hint the crawler over\n * what needs to be updated in priority, what file got updated and would need\n * to be reindexed, what should disappear from the index, ....\n * This way we can fine tune how full text search is behaving\n */\n\ntype FileHook struct{}\n\nfunc (this FileHook) Ls(ctx *App, path string) error {\n\tif this.record(ctx) {\n\t\tgo DaemonState.HintLs(ctx, path)\n\t}\n\treturn nil\n}\n\nfunc (this FileHook) Cat(ctx *App, path string) error {\n\tif this.record(ctx) {\n\t\tgo DaemonState.HintLs(ctx, filepath.Dir(path)+\"/\")\n\t}\n\treturn nil\n}\n\nfunc (this FileHook) Stat(ctx *App, path string) error {\n\treturn nil\n}\n\nfunc (this FileHook) Mkdir(ctx *App, path string) error {\n\tif this.record(ctx) {\n\t\tgo func() {\n\t\t\tDaemonState.HintLs(ctx, filepath.Dir(path)+\"/\")\n\t\t\tDaemonState.HintLs(ctx, path)\n\t\t}()\n\t}\n\treturn nil\n}\n\nfunc (this FileHook) Rm(ctx *App, path string) error {\n\tif this.record(ctx) {\n\t\tgo DaemonState.HintRm(ctx, path)\n\t}\n\treturn nil\n}\n\nfunc (this FileHook) Mv(ctx *App, from string, to string) error {\n\tif this.record(ctx) {\n\t\tgo func() {\n\t\t\tDaemonState.HintRm(ctx, filepath.Dir(from)+\"/\")\n\t\t\tDaemonState.HintLs(ctx, to+\"/\")\n\t\t\tDaemonState.HintLs(ctx, filepath.Dir(to)+\"/\")\n\t\t}()\n\t}\n\treturn nil\n}\n\nfunc (this FileHook) Save(ctx *App, path string) error {\n\tif this.record(ctx) {\n\t\tgo func() {\n\t\t\tDaemonState.HintLs(ctx, filepath.Dir(path)+\"/\")\n\t\t\tDaemonState.HintFile(ctx, path)\n\t\t}()\n\t}\n\treturn nil\n}\n\nfunc (this FileHook) Touch(ctx *App, path string) error {\n\tif this.record(ctx) {\n\t\tgo func() {\n\t\t\tDaemonState.HintLs(ctx, filepath.Dir(path)+\"/\")\n\t\t\tDaemonState.HintFile(ctx, path)\n\t\t}()\n\t}\n\treturn nil\n}\n\nfunc (this FileHook) record(ctx *App) bool {\n\tif ctx.Context.Value(\"AUDIT\") == false {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (this *daemonState) HintLs(app *App, path string) {\n\tid := GenerateID(app.Session)\n\tthis.mu.Lock()\n\tdefer this.mu.Unlock()\n\n\t// try to find the search indexer among the existing ones\n\tfor i := len(this.idx) - 1; i >= 0; i-- {\n\t\tif id != this.idx[i].Id {\n\t\t\tcontinue\n\t\t}\n\t\talreadyHasPath := false\n\t\tfor j := 0; j < len(this.idx[i].FoldersUnknown); j++ {\n\t\t\tif this.idx[i].FoldersUnknown[j].Path == path {\n\t\t\t\talreadyHasPath = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif alreadyHasPath == false {\n\t\t\theap.Push(&this.idx[i].FoldersUnknown, &Document{\n\t\t\t\tType:        \"directory\",\n\t\t\t\tPath:        path,\n\t\t\t\tInitialPath: path,\n\t\t\t\tName:        filepath.Base(path),\n\t\t\t})\n\t\t}\n\t\treturn\n\t}\n\n\t// Having all indexers running in memory could be expensive => instead we're cycling a pool\n\tsearch_process_max := SEARCH_PROCESS_MAX()\n\tlenIdx := len(this.idx)\n\tif lenIdx > 0 && search_process_max > 0 && lenIdx > (search_process_max-1) {\n\t\ttoDel := this.idx[0 : lenIdx-(search_process_max-1)]\n\t\tfor i := range toDel {\n\t\t\ttoDel[i].Close()\n\t\t}\n\t\tthis.idx = this.idx[lenIdx-(search_process_max-1):]\n\t}\n\t// instantiate the new indexer\n\tapp.Context = context.Background()\n\tcrawlerBackend, err := app.Backend.Init(app.Session, app)\n\tif err != nil {\n\t\tLog.Warning(\"plg_search_sqlitefs::init message=cannot_create_crawler err=%s\", err.Error())\n\t\treturn\n\t}\n\tapp.Backend = crawlerBackend\n\ts, err := NewCrawler(app)\n\tif err != nil {\n\t\tLog.Warning(\"plg_search_sqlitefs::init message=cannot_create_crawler err=%s\", err.Error())\n\t\treturn\n\t}\n\tdefer func() {\n\t\t// recover from panic if one occurred. Set err to nil otherwise.\n\t\tif r := recover(); r != nil {\n\t\t\tname := \"na\"\n\t\t\tfor _, el := range crawlerBackend.LoginForm().Elmnts {\n\t\t\t\tif el.Name == \"type\" {\n\t\t\t\t\tname = el.Value.(string)\n\t\t\t\t}\n\t\t\t}\n\t\t\tLog.Error(\"plg_search_sqlitefs::panic backend=\\\"%s\\\" recover=\\\"%s\\\"\", name, r)\n\t\t}\n\t}()\n\theap.Push(&s.FoldersUnknown, &Document{\n\t\tType:        \"directory\",\n\t\tPath:        path,\n\t\tInitialPath: path,\n\t\tName:        filepath.Base(path),\n\t})\n\tthis.idx = append(this.idx, s)\n}\n\nfunc (this *daemonState) HintRm(app *App, path string) {\n\tid := GenerateID(app.Session)\n\tthis.mu.RLock()\n\tfor i := len(this.idx) - 1; i >= 0; i-- {\n\t\tif id != this.idx[i].Id {\n\t\t\tcontinue\n\t\t}\n\t\tif op, err := this.idx[i].State.Change(); err == nil {\n\t\t\top.RemoveAll(path)\n\t\t\top.Commit()\n\t\t}\n\t\tbreak\n\t}\n\tthis.mu.RUnlock()\n}\n\nfunc (this *daemonState) HintFile(app *App, path string) {\n\tid := GenerateID(app.Session)\n\tthis.mu.RLock()\n\tfor i := len(this.idx) - 1; i >= 0; i-- {\n\t\tif id != this.idx[i].Id {\n\t\t\tcontinue\n\t\t}\n\t\tif op, err := this.idx[i].State.Change(); err == nil {\n\t\t\top.IndexTimeClear(path)\n\t\t\top.Commit()\n\t\t}\n\t\tbreak\n\t}\n\tthis.mu.RUnlock()\n}\n\nfunc (this *daemonState) Reset() {\n\tthis.mu.Lock()\n\tfor i := range this.idx {\n\t\tthis.idx[i].Close()\n\t}\n\tthis.idx = make([]Crawler, 0)\n\tthis.n = -1\n\tthis.mu.Unlock()\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/crawler/phase.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/config\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/indexer\"\n)\n\nfunc (this *Crawler) Run() {\n\tif this.CurrentPhase == \"\" {\n\t\tthis.CurrentPhase = PHASE_EXPLORE\n\t}\n\tLog.Debug(\"search::phase Execute %s\", this.CurrentPhase)\n\n\tcycleExecute := func(fn func(indexer.Manager) bool) {\n\t\top, err := this.State.Change()\n\t\tif err != nil {\n\t\t\tLog.Warning(\"search::index cycle_begin (%+v)\", err)\n\t\t\ttime.Sleep(5 * time.Second)\n\t\t}\n\t\tstopTime := time.Now().Add(time.Duration(CYCLE_TIME()) * time.Second)\n\t\tfor {\n\t\t\tif fn(op) == false {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif time.Now().After(stopTime) {\n\t\t\t\tthis.Next()\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif err = op.Commit(); err != nil {\n\t\t\tLog.Warning(\"search::index cycle_commit (%+v)\", err)\n\t\t}\n\t}\n\tif this.CurrentPhase == PHASE_EXPLORE {\n\t\tcycleExecute(this.Discover)\n\t\treturn\n\t} else if this.CurrentPhase == PHASE_INDEXING {\n\t\tcycleExecute(this.Indexing)\n\t\treturn\n\t} else if this.CurrentPhase == PHASE_MAINTAIN {\n\t\tcycleExecute(this.Consolidate)\n\t\treturn\n\t} else if this.CurrentPhase == PHASE_PAUSE {\n\t\tcycleExecute(this.Pause)\n\t\treturn\n\t}\n\tLog.Error(\"plg_search_sqlitefts::error message=unknown_phase phase=%s\", this.CurrentPhase)\n\treturn\n}\n\nfunc (this *Crawler) Close() error {\n\treturn this.State.Close()\n}\n\nfunc (this *Crawler) Next() {\n\tswitch this.CurrentPhase {\n\tcase PHASE_EXPLORE:\n\t\tthis.CurrentPhase = PHASE_INDEXING\n\tcase PHASE_INDEXING:\n\t\tthis.CurrentPhase = PHASE_MAINTAIN\n\tcase PHASE_MAINTAIN:\n\t\tthis.CurrentPhase = PHASE_PAUSE\n\tcase PHASE_PAUSE:\n\t\tthis.CurrentPhase = PHASE_EXPLORE\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/crawler/phase_explore.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t\"container/heap\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/config\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/indexer\"\n)\n\nfunc (this *Crawler) Discover(tx indexer.Manager) bool {\n\tdoc := this.DiscoverPop()\n\tif doc == nil {\n\t\tthis.Next()\n\t\treturn false\n\t}\n\tLog.Debug(\"search::debug phase=discovery path=%s\", doc.Path)\n\tfiles, err := this.Backend.Ls(doc.Path)\n\tif err != nil {\n\t\tthis.CurrentPhase = PHASE_PAUSE\n\t\treturn true\n\t}\n\treturn this.DiscoverPush(doc, files, tx)\n}\n\nfunc (this *Crawler) DiscoverPop() *Document {\n\tif this.FoldersUnknown.Len() == 0 {\n\t\treturn nil\n\t}\n\treturn heap.Pop(&this.FoldersUnknown).(*Document)\n}\n\nfunc (this *Crawler) DiscoverPush(doc *Document, files []os.FileInfo, tx indexer.Manager) bool {\n\texisting := make(map[string]bool, len(files))\n\texcluded := SEARCH_EXCLUSION()\n\tfor i := range files {\n\t\tf := files[i]\n\t\tname := f.Name()\n\t\texisting[name] = true\n\t\tskip := false\n\t\tfor i := 0; i < len(excluded); i++ {\n\t\t\tif name == excluded[i] || strings.Contains(doc.Path, excluded[i]) {\n\t\t\t\tskip = true\n\t\t\t}\n\t\t}\n\t\tif skip {\n\t\t\tcontinue\n\t\t}\n\t\tif f.IsDir() {\n\t\t\tvar performPush bool = false\n\t\t\tp := filepath.Join(doc.Path, name) + \"/\"\n\t\t\tif err := dbInsert(doc.Path, f, tx); err == nil {\n\t\t\t\tperformPush = true\n\t\t\t} else if err == indexer.ErrConstraint {\n\t\t\t\tperformPush = func(path string) bool {\n\t\t\t\t\ttm, err := tx.IndexTimeGet(p)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tLog.Warning(\"search::discovery unknown_path (%v)\", err)\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif time.Now().Add(time.Duration(-SEARCH_REINDEX()) * time.Hour).Before(tm) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif err = tx.IndexTimeUpdate(p, time.Now()); err != nil {\n\t\t\t\t\t\tLog.Warning(\"search::discovery insertion_failed (%v)\", err)\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t}(p)\n\t\t\t} else {\n\t\t\t\tLog.Error(\"search::indexing insert_index (%v)\", err)\n\t\t\t}\n\t\t\tif performPush {\n\t\t\t\theap.Push(&this.FoldersUnknown, &Document{\n\t\t\t\t\tType:    \"directory\",\n\t\t\t\t\tName:    name,\n\t\t\t\t\tPath:    p,\n\t\t\t\t\tSize:    f.Size(),\n\t\t\t\t\tModTime: f.ModTime(),\n\t\t\t\t})\n\t\t\t}\n\t\t} else {\n\t\t\tif err := dbUpsert(doc.Path, f, tx); err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\tif rows, err := tx.FindParent(doc.Path); err == nil {\n\t\tfor rows.Next() {\n\t\t\tif r, err := rows.Value(); err == nil {\n\t\t\t\tif !existing[r.Name] {\n\t\t\t\t\ttx.RemoveAll(r.Path)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\trows.Close()\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/crawler/phase_indexing.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/config\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/indexer\"\n)\n\nfunc (this *Crawler) Indexing(tx indexer.Manager) bool {\n\trows, err := tx.FindNew(MAX_INDEXING_FSIZE(), strings.Split(INDEXING_EXT(), \",\"))\n\tif err != nil {\n\t\tLog.Warning(\"search::insert index_query (%v)\", err)\n\t\treturn false\n\t}\n\tdefer rows.Close()\n\thasRows := false\n\tfor rows.Next() {\n\t\thasRows = true\n\t\tr, err := rows.Value()\n\t\tif err != nil {\n\t\t\tLog.Warning(\"search::indexing index_scan (%v)\", err)\n\t\t\treturn false\n\t\t}\n\t\tLog.Debug(\"search::debug phase=indexing path=%s\", r.Path)\n\t\tif err = updateFile(r.Path, this.Backend, tx); err != nil {\n\t\t\tLog.Warning(\"search::indexing index_update (%v)\", err)\n\t\t\treturn false\n\t\t}\n\t}\n\tif hasRows == false {\n\t\tthis.Next()\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/crawler/phase_maintain.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/config\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/indexer\"\n)\n\nfunc (this *Crawler) Consolidate(tx indexer.Manager) bool {\n\trows, err := tx.FindBefore(time.Now().Add(-time.Duration(SEARCH_REINDEX())*time.Hour - time.Duration(CYCLE_TIME())*time.Second).UTC())\n\tif err != nil {\n\t\tif err == indexer.ErrNoRows {\n\t\t\tthis.Next()\n\t\t\treturn false\n\t\t}\n\t\tthis.CurrentPhase = \"\"\n\t\treturn false\n\t}\n\tdefer rows.Close()\n\thasRows := false\n\tfor rows.Next() {\n\t\thasRows = true\n\t\tr, err := rows.Value()\n\t\tif err != nil {\n\t\t\tLog.Warning(\"search::index db_stale (%v)\", err)\n\t\t\treturn false\n\t\t}\n\t\tLog.Debug(\"search::debug phase=maintenance path=%s\", r.Path)\n\t\tif r.CType == \"directory\" {\n\t\t\tupdateFolder(r.Path, this.Backend, tx)\n\t\t} else {\n\t\t\tupdateFile(r.Path, this.Backend, tx)\n\t\t}\n\t}\n\tif hasRows == false {\n\t\tthis.Next()\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/crawler/phase_pause.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t\"time\"\n\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/indexer\"\n)\n\nfunc (this *Crawler) Pause(tx indexer.Manager) bool {\n\tif this.FoldersUnknown.Len() == 0 {\n\t\ttime.Sleep(10 * time.Second)\n\t}\n\tthis.Next()\n\treturn false\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/crawler/phase_utils.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/converter\"\n\t\"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/indexer\"\n)\n\nfunc updateFile(path string, backend IBackend, tx indexer.Manager) error {\n\tif err := tx.IndexTimeUpdate(path, time.Now().UTC()); err != nil {\n\t\treturn err\n\t}\n\treader, err := backend.Cat(path)\n\tif err != nil {\n\t\ttx.RemoveAll(path)\n\t\treturn err\n\t}\n\tdefer reader.Close()\n\tconvertedReader, err := converter.Convert(path, reader)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tdefer convertedReader.Close()\n\tif err = tx.FileContentUpdate(path, convertedReader); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc updateFolder(path string, backend IBackend, tx indexer.Manager) error {\n\tif err := tx.IndexTimeUpdate(path, time.Now().UTC()); err != nil {\n\t\treturn err\n\t}\n\n\t// Fetch list of folders as in the remote filesystem\n\tcurrFiles, err := backend.Ls(path)\n\tif err != nil {\n\t\ttx.RemoveAll(path)\n\t\treturn err\n\t}\n\n\t// Fetch FS as appear in our search cache\n\trows, err := tx.FindParent(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer rows.Close()\n\tpreviousFiles := make([]File, 0)\n\tfor rows.Next() {\n\t\tr, err := rows.Value()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpreviousFiles = append(previousFiles, File{\n\t\t\tFName: r.Name,\n\t\t\tFSize: r.Size,\n\t\t\tFPath: r.Path,\n\t\t})\n\t}\n\n\t// Perform the DB operation to ensure previousFiles and currFiles are in sync\n\t// 1. Find the content that have been created and did not exist before\n\tfor i := 0; i < len(currFiles); i++ {\n\t\tcurrFilenameAlreadyExist := false\n\t\tcurrFilename := currFiles[i].Name()\n\t\tfor j := 0; j < len(previousFiles); j++ {\n\t\t\tif currFilename == previousFiles[j].Name() {\n\t\t\t\tif currFiles[i].Size() != previousFiles[j].Size() {\n\t\t\t\t\tif err = dbUpdate(path, currFiles[i], tx); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcurrFilenameAlreadyExist = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif currFilenameAlreadyExist == false {\n\t\t\tdbUpsert(path, currFiles[i], tx)\n\t\t}\n\t}\n\t// 2. Find the content that was existing before but got removed\n\tfor i := 0; i < len(previousFiles); i++ {\n\t\tpreviousFilenameStillExist := false\n\t\tpreviousFilename := previousFiles[i].Name()\n\t\tfor j := 0; j < len(currFiles); j++ {\n\t\t\tif previousFilename == currFiles[j].Name() {\n\t\t\t\tpreviousFilenameStillExist = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif previousFilenameStillExist == false {\n\t\t\tp := filepath.Join(path, previousFiles[i].Name())\n\t\t\tif previousFiles[i].IsDir() {\n\t\t\t\tp += \"/\"\n\t\t\t}\n\t\t\ttx.RemoveAll(p)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc dbInsert(parent string, f os.FileInfo, tx indexer.Manager) error {\n\treturn tx.FileCreate(f, parent, false)\n}\n\nfunc dbUpsert(parent string, f os.FileInfo, tx indexer.Manager) error {\n\treturn tx.FileCreate(f, parent, true)\n}\n\nfunc dbUpdate(parent string, f fs.FileInfo, tx indexer.Manager) error {\n\tpath := filepath.Join(parent, f.Name())\n\tif f.IsDir() {\n\t\tpath += \"/\"\n\t}\n\treturn tx.FileMetaUpdate(path, f)\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/crawler/types.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t\"strings\"\n\t\"time\"\n)\n\nconst (\n\tPHASE_EXPLORE  = \"PHASE_EXPLORE\"\n\tPHASE_INDEXING = \"PHASE_INDEXING\"\n\tPHASE_MAINTAIN = \"PHASE_MAINTAIN\"\n\tPHASE_PAUSE    = \"PHASE_PAUSE\"\n)\n\nconst MAX_HEAP_SIZE = 100000\n\ntype Document struct {\n\tHash        string    `json:\"-\"`\n\tType        string    `json:\"type\"`\n\tName        string    `json:\"name\"`\n\tPath        string    `json:\"path\"`\n\tInitialPath string    `json:\"-\"`\n\tExt         string    `json:\"ext\"`\n\tModTime     time.Time `json:\"time\"`\n\tSize        int64     `json:\"size\"`\n\tContent     []byte    `json:\"content\"`\n\tPriority    int       `json:\"-\"`\n}\n\n// https://golang.org/pkg/container/heap/\ntype HeapDoc []*Document\n\nfunc (h HeapDoc) Len() int { return len(h) }\nfunc (h HeapDoc) Less(i, j int) bool {\n\tif h[i].Priority != 0 || h[j].Priority != 0 {\n\t\treturn h[i].Priority < h[j].Priority\n\t}\n\tscoreA := len(strings.Split(h[i].Path, \"/\")) / len(strings.Split(h[i].InitialPath, \"/\"))\n\tscoreB := len(strings.Split(h[j].Path, \"/\")) / len(strings.Split(h[j].InitialPath, \"/\"))\n\treturn scoreA < scoreB\n}\nfunc (h HeapDoc) Swap(i, j int) {\n\ta := h[i]\n\th[i] = h[j]\n\th[j] = a\n}\nfunc (h *HeapDoc) Push(x interface{}) {\n\tif h.Len() < MAX_HEAP_SIZE {\n\t\t*h = append(*h, x.(*Document))\n\t}\n}\nfunc (h *HeapDoc) Pop() interface{} {\n\told := *h\n\tn := len(old)\n\tif n == 0 {\n\t\treturn nil\n\t}\n\tx := old[n-1]\n\t*h = old[0 : n-1]\n\treturn x\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/index.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/crawler\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/workflow\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/config\"\n)\n\nfunc init() {\n\tHooks.Register.SearchEngine(SearchEngine{})\n\tHooks.Register.WorkflowAction(StepIndexer{})\n\n\tHooks.Register.Onload(func() {\n\t\tif SEARCH_ENABLE() {\n\t\t\tHooks.Register.AuthorisationMiddleware(FileHook{})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/indexer/error.go",
    "content": "package indexer\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\n\t\"github.com/mickael-kerjean/filestash/server/pkg/sqlite\"\n)\n\nvar (\n\tErrConstraint = fmt.Errorf(\"DB_CONSTRAINT_FAILED_ERROR\")\n\tErrNoRows     = fmt.Errorf(\"NO_ROWS\")\n)\n\nfunc toErr(err error) error {\n\tif err == sql.ErrNoRows {\n\t\treturn ErrNoRows\n\t} else if sqlite.IsConstraint(err) {\n\t\treturn ErrConstraint\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/indexer/index.go",
    "content": "package indexer\n\nimport (\n\t\"database/sql\"\n\t\"io\"\n\t\"io/fs\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\ntype Index interface {\n\tInit() error\n\tSearch(path string, q string) ([]IFile, error)\n\tChange() (Manager, error)\n\tClose() error\n}\n\ntype Manager interface {\n\tFindParent(path string) (RowMapper, error)\n\tFindBefore(time time.Time) (RowMapper, error)\n\tFindNew(maxSize int, toOmit []string) (RowMapper, error)\n\n\tFileCreate(f fs.FileInfo, parent string, withUpsert bool) error\n\tFileContentUpdate(path string, f io.ReadCloser) error\n\tFileMetaUpdate(path string, f fs.FileInfo) error\n\n\tIndexTimeGet(path string) (time.Time, error)\n\tIndexTimeUpdate(path string, t time.Time) error\n\tIndexTimeClear(path string) error\n\n\tRemoveAll(path string) error\n\tCommit() error\n}\n\nfunc NewIndex(id string) Index {\n\treturn &sqliteIndex{id, nil}\n}\n\ntype sqliteIndex struct {\n\tid string\n\tdb *sql.DB\n}\n\nfunc (this *sqliteIndex) Init() error {\n\tpath := \"fts_\" + this.id + \".sql\"\n\tif this.id == \"\" {\n\t\tpath = \"fts.sql\"\n\t}\n\tdb, err := sql.Open(\"sqlite3\", GetAbsolutePath(FTS_PATH, path)+\"?_journal_mode=wal\")\n\tif err != nil {\n\t\tLog.Warning(\"search::init can't open database (%v)\", err)\n\t\treturn toErr(err)\n\t}\n\tthis.db = db\n\n\tqueryDB := func(sqlQuery string) error {\n\t\tstmt, err := db.Prepare(sqlQuery)\n\t\tif err != nil {\n\t\t\tLog.Warning(\"search::initschema prepare schema error(%v)\", err)\n\t\t\treturn toErr(err)\n\t\t}\n\t\tdefer stmt.Close()\n\t\t_, err = stmt.Exec()\n\t\tif err != nil {\n\t\t\tLog.Warning(\"search::initschema execute error(%v)\", err)\n\t\t\treturn toErr(err)\n\t\t}\n\t\treturn nil\n\t}\n\tif queryDB(\"CREATE TABLE IF NOT EXISTS file(path VARCHAR(1024) PRIMARY KEY, filename VARCHAR(64), filetype VARCHAR(16), type VARCHAR(16), parent VARCHAR(1024), size INTEGER, modTime timestamp, indexTime timestamp DEFAULT NULL);\"); err != nil {\n\t\treturn err\n\t}\n\tif queryDB(\"CREATE INDEX IF NOT EXISTS idx_file_index_time ON file(indexTime) WHERE indexTime IS NOT NULL;\"); err != nil {\n\t\treturn err\n\t}\n\tif queryDB(\"CREATE INDEX IF NOT EXISTS idx_file_parent ON file(parent);\"); err != nil {\n\t\treturn err\n\t}\n\tif queryDB(\"CREATE VIRTUAL TABLE IF NOT EXISTS file_index USING fts5(path UNINDEXED, filename, filetype, content, tokenize = 'porter');\"); err != nil {\n\t\treturn err\n\t}\n\tif queryDB(\"CREATE TRIGGER IF NOT EXISTS after_file_insert AFTER INSERT ON file BEGIN INSERT INTO file_index (path, filename, filetype) VALUES(new.path, new.filename, new.filetype); END;\"); err != nil {\n\t\treturn err\n\t}\n\tif queryDB(\"CREATE TRIGGER IF NOT EXISTS after_file_delete AFTER DELETE ON file BEGIN DELETE FROM file_index WHERE path = old.path; END;\"); err != nil {\n\t\treturn err\n\t}\n\tif queryDB(\"CREATE TRIGGER IF NOT EXISTS after_file_update_path UPDATE OF path ON file BEGIN UPDATE file_index SET path = new.path, filename = new.filename, filetype = new.filetype WHERE path = old.path; END;\"); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (this sqliteIndex) Change() (Manager, error) {\n\ttx, err := this.db.Begin()\n\tif err != nil {\n\t\treturn nil, toErr(err)\n\t}\n\treturn sqliteQueries{tx}, nil\n}\n\nfunc (this sqliteIndex) Close() error {\n\treturn this.db.Close()\n}\n\ntype sqliteQueries struct {\n\ttx *sql.Tx\n}\n\nfunc (this sqliteQueries) Commit() error {\n\treturn this.tx.Commit()\n}\n\nfunc (this sqliteQueries) IndexTimeGet(path string) (time.Time, error) {\n\tvar t string\n\tif err := this.tx.QueryRow(\"SELECT indexTime FROM file WHERE path = ?\", path).Scan(&t); err != nil {\n\t\treturn time.Now(), toErr(err)\n\t}\n\ttm, err := time.Parse(time.RFC3339, t)\n\tif err != nil {\n\t\treturn tm, toErr(err)\n\t}\n\treturn tm, nil\n}\n\nfunc (this sqliteQueries) IndexTimeUpdate(path string, time time.Time) error {\n\tif _, err := this.tx.Exec(\"UPDATE file SET indexTime = ? WHERE path = ?\", time, path); err != nil {\n\t\treturn toErr(err)\n\t}\n\treturn nil\n}\n\nfunc (this sqliteQueries) IndexTimeClear(path string) error {\n\tif _, err := this.tx.Exec(\"UPDATE file SET indexTime = NULL WHERE path = ?\", path); err != nil {\n\t\treturn toErr(err)\n\t}\n\treturn nil\n}\n\ntype Record struct {\n\tName  string\n\tPath  string\n\tSize  int64\n\tCType string\n}\n\ntype RowMapper struct {\n\trows *sql.Rows\n}\n\nfunc (this *RowMapper) Next() bool {\n\treturn this.rows.Next()\n}\n\nfunc (this *RowMapper) Value() (Record, error) {\n\tvar r Record\n\tif err := this.rows.Scan(&r.Name, &r.CType, &r.Path, &r.Size); err != nil {\n\t\treturn r, toErr(err)\n\t}\n\treturn r, nil\n}\n\nfunc (this *RowMapper) Close() error {\n\treturn this.rows.Close()\n}\n\nfunc (this sqliteQueries) FindNew(maxSize int, filetypes []string) (RowMapper, error) {\n\tfor i := 0; i < len(filetypes); i++ {\n\t\tfiletypes[i] = \"'\" + strings.TrimSpace(filetypes[i]) + \"'\"\n\t}\n\n\trows, err := this.tx.Query(\n\t\t\"SELECT filename, type, path, size FROM file WHERE (\"+\n\t\t\t\"  type = 'file' AND size < ? AND filetype IN (\"+strings.Join(filetypes, \",\")+\") AND indexTime IS NULL \"+\n\t\t\t\") LIMIT 2\",\n\t\tmaxSize,\n\t)\n\tif err != nil {\n\t\treturn RowMapper{}, toErr(err)\n\t}\n\treturn RowMapper{rows: rows}, nil\n}\n\nfunc (this sqliteQueries) FindBefore(t time.Time) (RowMapper, error) {\n\trows, err := this.tx.Query(\n\t\t\"SELECT filename, type, path, size FROM file WHERE indexTime < ? ORDER BY indexTime DESC LIMIT 5\",\n\t\tt,\n\t)\n\tif err != nil {\n\t\treturn RowMapper{}, toErr(err)\n\t}\n\treturn RowMapper{rows: rows}, nil\n}\n\nfunc (this sqliteQueries) FindParent(path string) (RowMapper, error) {\n\trows, err := this.tx.Query(\"SELECT filename, type, path, size FROM file WHERE parent = ?\", path)\n\tif err != nil {\n\t\treturn RowMapper{}, err\n\t}\n\treturn RowMapper{rows: rows}, nil\n}\n\nfunc (this sqliteQueries) FileMetaUpdate(path string, f fs.FileInfo) error {\n\t_, err := this.tx.Exec(\n\t\t\"UPDATE file SET size = ?, modTime = ? indexTime = NULL WHERE path = ?\",\n\t\tf.Size(), f.ModTime(), path,\n\t)\n\treturn toErr(err)\n}\n\nfunc (this sqliteQueries) FileContentUpdate(path string, reader io.ReadCloser) error {\n\tcontent, err := io.ReadAll(reader)\n\tif err != nil {\n\t\treturn toErr(err)\n\t}\n\tif _, err := this.tx.Exec(\"UPDATE file_index SET content = ? WHERE path = ?\", content, path); err != nil {\n\t\treturn toErr(err)\n\t}\n\treturn nil\n}\n\nfunc (this sqliteQueries) FileCreate(f fs.FileInfo, parentPath string, withUpsert bool) (err error) {\n\tname := f.Name()\n\tpath := filepath.Join(parentPath, f.Name())\n\tif f.IsDir() {\n\t\tsql := \"INSERT INTO file(path, parent, filename, type, size, modTime, indexTime) VALUES(?, ?, ?, ?, ?, ?, ?)\"\n\t\tif withUpsert {\n\t\t\tsql += \" ON CONFLICT(path) DO UPDATE SET size=excluded.size, modTime=excluded.modTime\" +\n\t\t\t\t\" WHERE excluded.modTime != file.modTime OR excluded.size != file.size\"\n\t\t}\n\t\t_, err = this.tx.Exec(sql, path+\"/\", parentPath, name, \"directory\", f.Size(), f.ModTime(), time.Now())\n\t} else {\n\t\tsql := \"INSERT INTO file(path, parent, filename, type, size, modTime, indexTime, filetype) VALUES(?, ?, ?, ?, ?, ?, ?, ?)\"\n\t\tif withUpsert {\n\t\t\tsql += \" ON CONFLICT(path) DO UPDATE SET size=excluded.size, modTime=excluded.modTime, indexTime=NULL\" +\n\t\t\t\t\" WHERE excluded.modTime != file.modTime OR excluded.size != file.size\"\n\t\t}\n\t\t_, err = this.tx.Exec(sql, path, parentPath, name, \"file\", f.Size(), f.ModTime(), nil, strings.TrimPrefix(filepath.Ext(name), \".\"))\n\t}\n\treturn toErr(err)\n}\n\nfunc (this sqliteQueries) Remove(path string) error {\n\tif _, a := this.tx.Exec(\"DELETE FROM file WHERE path = ?\", path); a != nil {\n\t\treturn toErr(a)\n\t}\n\treturn nil\n}\n\nfunc (this sqliteQueries) RemoveAll(path string) error {\n\tif _, a := this.tx.Exec(\"DELETE FROM file WHERE path >= ? AND path < ?\", path, path+\"~\"); a != nil {\n\t\treturn toErr(a)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/indexer/query.go",
    "content": "package indexer\n\nimport (\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc (this sqliteIndex) Search(path string, q string) ([]IFile, error) {\n\tfiles := []IFile{}\n\trows, err := this.db.Query(\n\t\t\"SELECT type, path, size, modTime FROM file WHERE path IN (\"+\n\t\t\t\"   SELECT path FROM file_index WHERE file_index MATCH ? AND path > ? AND path < ?\"+\n\t\t\t\"   ORDER BY rank LIMIT 50000\"+\n\t\t\t\")\",\n\t\tregexp.MustCompile(`(\\.|\\-)`).ReplaceAllString(q, \"\\\"$1\\\"\"),\n\t\tpath, path+\"~\",\n\t)\n\tif err != nil {\n\t\tLog.Warning(\"search::query DBQuery (%s)\", err.Error())\n\t\treturn files, ErrNotReachable\n\t}\n\tdefer rows.Close()\n\tfor rows.Next() {\n\t\tf := File{}\n\t\tvar t string\n\t\tif err = rows.Scan(&f.FType, &f.FPath, &f.FSize, &t); err != nil {\n\t\t\tLog.Warning(\"search::query scan (%s)\", err.Error())\n\t\t\treturn files, ErrNotReachable\n\t\t}\n\t\tif tm, err := time.Parse(time.RFC3339, t); err == nil {\n\t\t\tf.FTime = tm.Unix() * 1000\n\t\t}\n\t\tf.FName = filepath.Base(f.FPath)\n\t\tfiles = append(files, f)\n\t}\n\treturn files, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/query.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/crawler\"\n)\n\ntype SearchEngine struct{}\n\nfunc (this SearchEngine) Query(app App, path string, keyword string) ([]IFile, error) {\n\tDaemonState.HintLs(&app, path)\n\ts := GetCrawler(&app)\n\tif s == nil {\n\t\treturn nil, ErrNotReachable\n\t}\n\tif path == \"\" {\n\t\tpath = \"/\"\n\t}\n\treturn s.State.Search(path, keyword)\n}\n"
  },
  {
    "path": "server/plugin/plg_search_sqlitefts/workflow/index.go",
    "content": "package plg_search_sqlitefts\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"sync\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/ctrl\"\n\t. \"github.com/mickael-kerjean/filestash/server/model\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/config\"\n\t. \"github.com/mickael-kerjean/filestash/server/plugin/plg_search_sqlitefts/crawler\"\n)\n\ntype StepIndexer struct{}\n\nvar runningIndexers sync.Map\n\nfunc (this StepIndexer) Manifest() WorkflowSpecs {\n\treturn WorkflowSpecs{\n\t\tName:  \"tools/index\",\n\t\tTitle: \"Refresh Search Index\",\n\t\tIcon:  `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><path d=\"M433.3 33C445.2 29.5 457.7 35.7 462.3 46.9L463.1 49.2L503.1 185.2C505.6 193.6 503.2 202.7 497 208.9L432.9 273L509.3 252.2L529.2 192.4L530.1 190.1C535.1 179 547.8 173.3 559.6 177.2C571.4 181.1 578.1 193.3 575.4 205.2L574.7 207.6L550.7 279.6C548.2 287.2 542 293 534.2 295.2L443.1 320L534.2 344.8C541.9 346.9 548.1 352.8 550.7 360.4L574.7 432.4L575.4 434.8C578.1 446.7 571.3 458.9 559.6 462.8C547.8 466.7 535.1 461 530.1 449.9L529.2 447.6L509.3 387.8L432.9 367L497 431.1C503.1 437.2 505.5 446.2 503.2 454.5L463.2 598.5L462.4 600.8C458 612.1 445.6 618.5 433.6 615.2C421.6 611.9 414.3 600 416.4 588L416.9 585.6L453.1 455.1L415.9 417.9C415 470 372.4 512 320 512C267.6 512 225 470 224 417.9L187 454.9L223 577.2L223.6 579.6C225.8 591.5 218.7 603.5 206.8 607C194.9 610.5 182.4 604.3 177.8 593.1L177 590.8L137 454.8C134.5 446.4 136.9 437.3 143.1 431L207.2 366.9L130.8 387.7L110.9 447.5L110 449.8C105 460.9 92.3 466.6 80.5 462.7C68.7 458.8 62 446.6 64.7 434.7L65.4 432.3L89.4 360.3C91.9 352.7 98.1 346.9 105.9 344.7L197 319.9L105.9 295.1C98.2 293 92 287.1 89.4 279.5L65.4 207.5L64.7 205.1C62 193.2 68.8 181 80.5 177.1C92.2 173.2 105 178.9 110 190L110.9 192.3L130.8 252.1L207.2 272.9L143.1 208.8C136.9 202.6 134.6 193.5 137 185.1L177 49.1L177.8 46.8C182.4 35.5 194.9 29.4 206.8 32.9C218.7 36.4 225.8 48.4 223.6 60.3L223 62.7L187 185L240 238C241 194.7 276.4 159.9 319.9 159.9C363.4 159.9 398.8 194.7 399.8 238.1L452.9 185L416.9 62.7L416.3 60.3C414.1 48.3 421.2 36.4 433.1 32.9z\"/></svg>`,\n\t\tSpecs: Form{\n\t\t\tElmnts: []FormElement{\n\t\t\t\t{\n\t\t\t\t\tName: \"token\",\n\t\t\t\t\tType: \"password\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName:        \"path\",\n\t\t\t\t\tType:        \"text\",\n\t\t\t\t\tPlaceholder: \"default: /\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (this StepIndexer) Execute(params map[string]string, input map[string]string) (map[string]string, error) {\n\tstr, err := DecryptString(SECRET_KEY_DERIVATE_FOR_USER, params[\"token\"])\n\tif err != nil {\n\t\tLog.Warning(\"plg_search_sqlitefts::workflow message=invalid_token err=%s\", err.Error())\n\t\treturn input, err\n\t}\n\tsession := map[string]string{}\n\tif err = json.Unmarshal([]byte(str), &session); err != nil {\n\t\tLog.Warning(\"plg_search_sqlitefts::workflow message=invalid_session err=%s\", err.Error())\n\t\treturn input, err\n\t}\n\tid := GenerateID(session)\n\tif _, loaded := runningIndexers.LoadOrStore(id, struct{}{}); loaded {\n\t\treturn input, nil\n\t}\n\tdefer runningIndexers.Delete(id)\n\tapp := &App{\n\t\tContext: context.Background(),\n\t\tSession: session,\n\t}\n\tbackend, err := NewBackend(app, app.Session)\n\tif err != nil {\n\t\tLog.Warning(\"plg_search_sqlitefts::workflow message=cannot_create_backend err=%s\", err.Error())\n\t\treturn input, err\n\t}\n\tapp.Backend = backend\n\tDaemonState.HintLs(app, EnforceDirectory(params[\"path\"]))\n\tcrwlr := GetCrawler(app)\n\n\ttr, err := crwlr.State.Change()\n\tif err != nil {\n\t\tLog.Warning(\"plg_search_sqlitefts::workflow message=cannot_begin_tx err=%s\", err.Error())\n\t\treturn input, err\n\t}\n\n\tn := SEARCH_PROCESS_PAR()\n\tcond := sync.NewCond(&sync.Mutex{})\n\tinflight := 0\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < n; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tcond.L.Lock()\n\t\t\tfor crwlr.FoldersUnknown.Len() > 0 || inflight > 0 {\n\t\t\t\tfor crwlr.FoldersUnknown.Len() == 0 && inflight > 0 {\n\t\t\t\t\tcond.Wait()\n\t\t\t\t}\n\t\t\t\tif crwlr.FoldersUnknown.Len() == 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tdoc := crwlr.DiscoverPop()\n\t\t\t\tinflight++\n\t\t\t\tcond.L.Unlock()\n\n\t\t\t\tpath, err := ctrl.PathBuilder(app, doc.Path)\n\t\t\t\tif err != nil {\n\t\t\t\t\tLog.Warning(\"plg_search_sqlitefts::workflow message=path_builder path=%s err=%s\", doc.Path, err.Error())\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tfiles, err := crwlr.Backend.Ls(path)\n\t\t\t\tif err != nil {\n\t\t\t\t\tLog.Warning(\"plg_search_sqlitefts::workflow message=ls_error path=%s err=%s\", doc.Path, err.Error())\n\t\t\t\t}\n\n\t\t\t\tcond.L.Lock()\n\t\t\t\tif err == nil {\n\t\t\t\t\tcrwlr.DiscoverPush(doc, files, tr)\n\t\t\t\t}\n\t\t\t\tinflight--\n\t\t\t\tcond.Broadcast()\n\t\t\t}\n\t\t\tcond.L.Unlock()\n\t\t}()\n\t}\n\twg.Wait()\n\tif err = tr.Commit(); err != nil {\n\t\tLog.Warning(\"plg_search_sqlitefts::workflow message=commit_error err=%s\", err.Error())\n\t}\n\treturn input, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_search_stateless/config.go",
    "content": "package plg_search_stateless\n\nimport (\n\t\"fmt\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"time\"\n)\n\nvar (\n\tSEARCH_TIMEOUT func() time.Duration\n)\n\nfunc init() {\n\tSEARCH_TIMEOUT = func() time.Duration {\n\t\treturn time.Duration(Config.Get(\"features.search.explore_timeout\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Name = \"explore_timeout\"\n\t\t\tf.Type = \"number\"\n\t\t\tf.Default = 1500\n\t\t\tf.Description = `When full text search is disabled, the search engine recursively explore\n directories to find results. Exploration can't last longer than what is configured here`\n\t\t\tf.Placeholder = fmt.Sprintf(\"Default: %dms\", f.Default)\n\t\t\treturn f\n\t\t}).Int()) * time.Millisecond\n\t}\n\tHooks.Register.Onload(func() {\n\t\tSEARCH_TIMEOUT()\n\t})\n}\n"
  },
  {
    "path": "server/plugin/plg_search_stateless/index.go",
    "content": "package plg_search_stateless\n\nimport (\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.SearchEngine(StatelessSearch{})\n}\n\ntype PathQuandidate struct {\n\tPath  string\n\tScore int\n}\n\ntype StatelessSearch struct{}\n\nfunc (this StatelessSearch) Query(app App, path string, keyword string) ([]IFile, error) {\n\tfiles := make([]IFile, 0)\n\ttoVisit := []PathQuandidate{PathQuandidate{path, 0}}\n\tMAX_SEARCH_TIME := SEARCH_TIMEOUT()\n\n\tfor start := time.Now(); time.Since(start) < MAX_SEARCH_TIME; {\n\t\tif len(toVisit) == 0 {\n\t\t\treturn files, nil\n\t\t}\n\t\tcurrentPath := toVisit[0]\n\t\tif len(toVisit) == 0 {\n\t\t\ttoVisit = make([]PathQuandidate, 0)\n\t\t} else {\n\t\t\ttoVisit = toVisit[1:]\n\t\t}\n\n\t\t// Ls on the directory\n\t\tf, err := app.Backend.Ls(currentPath.Path)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tscore1 := scoreBoostForFilesInDirectory(f)\n\t\tfor i := 0; i < len(f); i++ {\n\t\t\tname := f[i].Name()\n\t\t\tif isAMatch := IsSearchQueryMatchingFilename(\n\t\t\t\t[]rune(strings.ToLower(name)),\n\t\t\t\t[]rune(strings.ToLower(keyword)),\n\t\t\t); isAMatch {\n\t\t\t\tfiles = append(files, File{\n\t\t\t\t\tFName: name,\n\t\t\t\t\tFType: func() string {\n\t\t\t\t\t\tif f[i].IsDir() {\n\t\t\t\t\t\t\treturn \"directory\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn \"file\"\n\t\t\t\t\t}(),\n\t\t\t\t\tFSize: f[i].Size(),\n\t\t\t\t\tFTime: f[i].ModTime().Unix() * 1000,\n\t\t\t\t\tFPath: func() string {\n\t\t\t\t\t\tp := JoinPath(currentPath.Path, name)\n\t\t\t\t\t\tif f[i].IsDir() {\n\t\t\t\t\t\t\tp = p + \"/\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn p\n\t\t\t\t\t}(),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// follow directories\n\t\t\tfullpath := currentPath.Path + name + \"/\"\n\t\t\trelativePath := strings.ToLower(strings.TrimSuffix(strings.TrimPrefix(fullpath, path), \"/\"))\n\t\t\tscore2 := scoreBoostOnDepth(relativePath) * 2\n\t\t\tif f[i].IsDir() {\n\t\t\t\tscore := scoreBoostForPath(relativePath)\n\t\t\t\tif score < 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tscore += score1\n\t\t\t\tscore += score2\n\t\t\t\tscore += currentPath.Score\n\t\t\t\tt := make([]PathQuandidate, len(toVisit)+1)\n\t\t\t\tk := 0\n\t\t\t\tfor k = 0; k < len(toVisit); k++ {\n\t\t\t\t\tif score > toVisit[k].Score {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tt[k] = toVisit[k]\n\t\t\t\t}\n\t\t\t\tt[k] = PathQuandidate{fullpath, score}\n\t\t\t\tfor k = k + 1; k < len(toVisit)+1; k++ {\n\t\t\t\t\tt[k] = toVisit[k-1]\n\t\t\t\t}\n\t\t\t\ttoVisit = t\n\t\t\t}\n\t\t}\n\t}\n\treturn files, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_search_stateless/scoring.go",
    "content": "package plg_search_stateless\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nfunc scoreBoostForPath(p string) int {\n\tb := strings.ToLower(filepath.Base(p))\n\n\t// some path are garbage we don't want to explore unless there's nothing else to do\n\tif b == \"node_modules\" {\n\t\treturn -100\n\t} else if strings.HasPrefix(b, \".\") {\n\t\treturn -10\n\t}\n\n\t// not all path are equally interesting, we bump the score of what we thing is interesting\n\tscore := 0\n\tif strings.Contains(b, \"document\") {\n\t\tscore += 3\n\t} else if strings.Contains(b, \"project\") {\n\t\tscore += 3\n\t} else if strings.Contains(b, \"home\") {\n\t\tscore += 3\n\t} else if strings.Contains(b, \"note\") {\n\t\tscore += 3\n\t}\n\treturn score\n}\n\nfunc scoreBoostForFilesInDirectory(f []os.FileInfo) int {\n\ts := 0\n\tfor i := 0; i < len(f); i++ {\n\t\tname := f[i].Name()\n\t\tif f[i].IsDir() == false {\n\t\t\tif strings.HasSuffix(name, \".org\") {\n\t\t\t\ts += 2\n\t\t\t} else if strings.HasSuffix(name, \".pdf\") {\n\t\t\t\ts += 1\n\t\t\t} else if strings.HasSuffix(name, \".doc\") || strings.HasSuffix(name, \".docx\") {\n\t\t\t\ts += 1\n\t\t\t} else if strings.HasSuffix(name, \".md\") {\n\t\t\t\ts += 1\n\t\t\t} else if strings.HasSuffix(name, \".pdf\") {\n\t\t\t\ts += 1\n\t\t\t}\n\t\t}\n\t\tif s > 4 {\n\t\t\treturn 4\n\t\t}\n\t}\n\treturn s\n}\n\nfunc scoreBoostOnDepth(p string) int {\n\treturn -strings.Count(p, \"/\")\n}\n\nfunc IsSearchQueryMatchingFilename(strRune []rune, patternRune []rune) bool {\n\tif len(patternRune) == 0 {\n\t\treturn true\n\t} else if patternRune[0] == '*' {\n\t\treturn IsSearchQueryMatchingFilename(strRune, patternRune[1:])\n\t}\n\n\tdumbMatch := func(s []rune, p []rune) bool {\n\t\tcurrPattern := 0\n\t\tmoveCursor := false\n\t\ti := 0\n\t\tfor i = 0; i < len(s); i++ {\n\t\t\tif moveCursor {\n\t\t\t\tcurrPattern += 1\n\t\t\t\tif currPattern >= len(p) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif p[currPattern] == '*' {\n\t\t\t\treturn IsSearchQueryMatchingFilename(s[i:], p[currPattern:])\n\t\t\t} else if s[i] == p[currPattern] {\n\t\t\t\tmoveCursor = true\n\t\t\t} else {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\tcurrPattern += 1\n\t\tif currPattern <= len(p)-1 {\n\t\t\tif p[currPattern] == '$' && i == len(s) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t}\n\n\tfor i := 0; i < len(strRune); i++ {\n\t\tif strRune[i] == patternRune[0] {\n\t\t\tif dumbMatch(strRune[i:], patternRune) == true {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "server/plugin/plg_security_scanner/index.go",
    "content": "package plg_security_scanner\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"github.com/gorilla/mux\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"math/rand\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nvar (\n\tgzipBomb      *bytes.Buffer\n\tbillionsOfLol *bytes.Buffer\n\n\tplugin_enable func() bool\n)\n\nfunc init() {\n\tplugin_enable = func() bool {\n\t\treturn Config.Get(\"features.protection.enable\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Default = true\n\t\t\tf.Name = \"enable\"\n\t\t\tf.Type = \"boolean\"\n\t\t\tf.Target = []string{}\n\t\t\tf.Description = \"Enable/Disable active protection against scanners\"\n\t\t\tf.Placeholder = \"Default: true\"\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\tb, err := base64.StdEncoding.DecodeString(\"H4sICDy9j1kAAzEwRy5nemlwAOzdIQ7zUHqG0d/yBaMCOwFBAfUyQiIlxNggAwZFXkDWEEtWUMF0J0VhJWXZgVOWBVQDDIKiuZW9hQuuRjpnBc8CXn3fv//HX/7M//33v/0p/+9/iuLPnz/Df/7zv/7xl3/7AwAAAAAAAAD8ixv+dyyXMcCfJk5t91cAAAAAAAAA4F9dX1dhHQX8rvtz7hgAAAAAAAAAINnjeNisFwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+F3359wxAAAAAAAAAECyx/GwWZ8EzPdbyB0DAAAAAAAAACTbfT+ndQvwis/tJXcNAAAAAAAAAJDsPZbFsgVo4tR2uWMAAAAAAAAAgGR9XYVlCzD8rvtz7hgAAAAAAAAAINnjeNisTwLm+y3kjgEAAAAAAAAAku2+n9O6BXjF5/aSuwYAAAAAAAAASPYey2LZAjRxarvcMQAAAAAAAABAsr6uwrIFGH7X/Tl3DAAAAAAAAACQ7HE8bNYnAfP9FnLHAAAAAAAAAADJdt/Pad0CvOJze8ldAwAAAAAAAAAke49lsWwBmji1Xe4YAAAAAAAAACBZX1dh2QIMv+v+nDsGAAAAAAAAAEj2OB4265OA+X4LuWMAAAAAAAAAgGS77+e0bgFe8bm95K4BAAAAAAAAAJK9x7JYtgBNnNoudwwAAAAAAAAAkKyvq7BsAYbfdX/OHQMAAAAAAAAAJHscD5v1ScB8v4XcMQAAAAAAAABAst33c1q3AK/43F5y1wAAAAAAAAAAyd5jWSxbgCZObZc7BgAAAAAAAABI1tdVWLYAw++6P+eOAQAAAAAAAACSPY6HzfokYL7fQu4YAAAAAAAAACDZ7vs5rVuAV3xuL7lrAAAAAAAAAIBk77Esli1AE6e2yx0DAAAAAAAAACTr6yosW4Dhd92fc8cAAAAAAAAAAMkex8NmfRIw328hdwwAAAAAAAAAkGz3/ZzWLcArPreX3DUAAAAAAAAAQLL3WBbLFqCJU9vljgEAAAAAAAAAkvV1FZYtwPC77s+5YwAAAAAAAACAZI/jYbM+CZjvt5A7BgAAAAAAAABItvt+TusW4BWf20vuGgAAAAAAAAAg2Xssi2UL0MSp7XLHAAAAAAAAAADJ+roKyxZg+P/27tiEYe4Mw2iEblrZKlyZgDdISjUGqXHtQl0K4zKFZ5BAeIB/klTewRvIpUcIJggCAiXSCre4BM6pvvIZ4IVvvh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARLvvirBuAfr5dmxSxwAAAAAAAAAA0Z7nar89Cfh1j5A6BgAAAAAAAACIdpi+9bYFeC+vsk1dAwAAAAAAAABE+wx5tm4BTst4uaaOAQAAAAAAAACi3XdFWLcA/Xw7NqljAAAAAAAAAIBoz3O1354E/LpHSB0DAAAAAAAAAEQ7TN962wK8l1fZpq4BAAAAAAAAAKJ9hjxbtwCnZbxcU8cAAAAAAAAAANHuuyKsW4B+vh2b1DEAAAAAAAAAQLTnudpvTwJ+3SOkjgEAAAAAAAAAoh2mb71tAd7Lq2xT1wAAAAAAAAAA0T5Dnq1bgNMyXq6pYwAAAAAAAACAaPddEdYtQD/fjk3qGAAAAAAAAAAg2vNc7bcnAb/uEVLHAAAAAAAAAADRDtO33rYA7+VVtqlrAAAAAAAAAIBonyHP1i3AaRkv19QxAAAAAAAAAEC0+64I6xagn2/HJnUMAAAAAAAAABDtea7225OAX/cIqWMAAAAAAAAAgGiH6VtvW4D38irb1DUAAAAAAAAAQLTPkGfrFuC0jJdr6hgAAAAAAAAAINp9V4R1C9DPt2OTOgYAAAAAAAAAiPY8V/vtScCve4TUMQAAAAAAAABAtMP0rbctwHt5lW3qGgAAAAAAAAAg2mfIs3ULcFrGyzV1DAAAAAAAAAAQ7b4rwroF6OfbsUkdAwAAAAAAAABEe56r/fYk4Nc9QuoYAAAAAAAAACDaYfrW2xbgvbzKNnUNAAAAAAAAABDtM+TZugU4LePlmjoGAAAAAAAAAIh23xVh3QL08+3YpI4BAAAAAAAAAKI9z9V+exLw6x4hdQwAAAAAAAAAEO0wfettC/BeXmWbugYAAAAAAAAAiPYZ8mzdApyW8XJNHQMAAAAAAAAARPvHrgjrFiD85+///Fv95z8B/3f+Ff76x1/+/b+j/y/KOXX7oQCfAA==\")\n\tif err != nil {\n\t\treturn\n\t}\n\tgzipBomb = bytes.NewBuffer(b)\n\tb, err = base64.StdEncoding.DecodeString(\"PD94bWwgdmVyc2lvbj0iMS4wIj8+CjwhRE9DVFlQRSBsb2x6IFsKIDwhRU5USVRZIGxvbCAibG9sIj4KIDwhRUxFTUVOVCBsb2x6ICgjUENEQVRBKT4KIDwhRU5USVRZIGxvbDEgIiZsb2w7JmxvbDsmbG9sOyZsb2w7JmxvbDsmbG9sOyZsb2w7JmxvbDsmbG9sOyZsb2w7Ij4KIDwhRU5USVRZIGxvbDIgIiZsb2wxOyZsb2wxOyZsb2wxOyZsb2wxOyZsb2wxOyZsb2wxOyZsb2wxOyZsb2wxOyZsb2wxOyZsb2wxOyI+CiA8IUVOVElUWSBsb2wzICImbG9sMjsmbG9sMjsmbG9sMjsmbG9sMjsmbG9sMjsmbG9sMjsmbG9sMjsmbG9sMjsmbG9sMjsmbG9sMjsiPgogPCFFTlRJVFkgbG9sNCAiJmxvbDM7JmxvbDM7JmxvbDM7JmxvbDM7JmxvbDM7JmxvbDM7JmxvbDM7JmxvbDM7JmxvbDM7JmxvbDM7Ij4KIDwhRU5USVRZIGxvbDUgIiZsb2w0OyZsb2w0OyZsb2w0OyZsb2w0OyZsb2w0OyZsb2w0OyZsb2w0OyZsb2w0OyZsb2w0OyZsb2w0OyI+CiA8IUVOVElUWSBsb2w2ICImbG9sNTsmbG9sNTsmbG9sNTsmbG9sNTsmbG9sNTsmbG9sNTsmbG9sNTsmbG9sNTsmbG9sNTsmbG9sNTsiPgogPCFFTlRJVFkgbG9sNyAiJmxvbDY7JmxvbDY7JmxvbDY7JmxvbDY7JmxvbDY7JmxvbDY7JmxvbDY7JmxvbDY7JmxvbDY7JmxvbDY7Ij4KIDwhRU5USVRZIGxvbDggIiZsb2w3OyZsb2w3OyZsb2w3OyZsb2w3OyZsb2w3OyZsb2w3OyZsb2w3OyZsb2w3OyZsb2w3OyZsb2w3OyI+CiA8IUVOVElUWSBsb2w5ICImbG9sODsmbG9sODsmbG9sODsmbG9sODsmbG9sODsmbG9sODsmbG9sODsmbG9sODsmbG9sODsmbG9sODsiPgpdPgo8bG9sej4mbG9sOTs8L2xvbHo+\")\n\tif err != nil {\n\t\treturn\n\t}\n\tbillionsOfLol = bytes.NewBuffer(b)\n\n\tHooks.Register.HttpEndpoint(func(r *mux.Router) error {\n\t\tif plugin_enable() == false {\n\t\t\treturn nil\n\t\t}\n\n\t\t// DEFAULT\n\t\tr.HandleFunc(\"/index.php\", WelcomePackHandle)\n\t\tr.PathPrefix(\"/html/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/webdav/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/www/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/MAMP/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/xampp/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/web/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/scripts/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\n\t\t// CMS\n\t\tr.PathPrefix(\"/blog/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/cms/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/wordpress/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/wp/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/wp-admin/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/wp-content/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.HandleFunc(\"/wp-config.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/wp-login.php\", WelcomePackHandle)\n\t\tr.PathPrefix(\"/wp1/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/wp2/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/wp3/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/wp4/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/wp5/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/wp6/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/wp7/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/wp8/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/images/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/joomla/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/libraries/joomla/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/administrator/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/components/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/templates/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/includes/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/modules/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/plugins/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.HandleFunc(\"/drupal/\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/Drupal.php\", WelcomePackHandle)\n\t\tr.PathPrefix(\"/core/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.HandleFunc(\"/web.config\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/server.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/composer.json\", WelcomePackHandle)\n\t\tr.PathPrefix(\"/cacti/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/thinkphp/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\n\t\t// SQL\n\t\tr.PathPrefix(\"/phpmyadmin/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/pma/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/phpMyAdmin/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/mysqladmin/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/sql/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/myadmin/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/mysql/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/db/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/phpmy/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/phppma/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\n\t\t// OTHER\n\t\tr.HandleFunc(\"/echo.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/composer.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/uploader.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/shell.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/freenode-proxy-checker.txt\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/cmd.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/muhstiks.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/muhstik.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/jmx-console\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/status.php\", WelcomePackHandle)\n\t\tr.PathPrefix(\"/TP/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/HNAP1/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/manager/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/program/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/shopdb/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/programs/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/jenkins/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.HandleFunc(\"/w00tw00t.at.blackhats.romanian.anti-sec:)\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/judge.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/muieblackcat\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/.env\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/log\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/configs\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/config\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/cfg\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/gs\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/gsProvision\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/overrides\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/polycom\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/spa.xml\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/yealink\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/help.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/java.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/_query.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/test.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/db_cts.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/db_pma.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/logon.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/help-e.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/license.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/log.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/hell.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/pmd_online.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/x.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/htdocs.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/b.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/desktop.ini.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/z.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/lala.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/lala-dpr.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/wpc.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/wpo.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/t6nv.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/text.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/muhstik2.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/muhstik-dpr.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/lol.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/cmv.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/cmdd.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/knal.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/appserv.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/d7.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/rxr.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/1x.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/home.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/undx.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/spider.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/payload.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/composers.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/izom.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/hue2.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/new_license.php\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/up.php\", WelcomePackHandle)\n\t\tr.PathPrefix(\"/pmd/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/PMA/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/PMA2/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/pmamy/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/pmamy2/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/dbadmin/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/tools/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/phpma/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/php-my-admin/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/websql/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.PathPrefix(\"/dbadmin/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.HandleFunc(\"/xmlrpc.php\", WelcomePackHandle)\n\t\tr.PathPrefix(\"/user/\").Handler(http.HandlerFunc(WelcomePackHandle))\n\t\tr.HandleFunc(\"/vuln.htm\", WelcomePackHandle)\n\t\tr.HandleFunc(\"/webconfig.txt.php\", WelcomePackHandle)\n\t\treturn nil\n\t})\n}\n\nfunc WelcomePackHandle(res http.ResponseWriter, req *http.Request) {\n\tLog.Info(\"Attack attempt %s %s %s\", req.RemoteAddr, req.URL.String(), req.Header.Get(\"User-Agent\"))\n\tr := rand.Intn(100)\n\n\tif r < 5 {\n\t\tres.Header().Set(\"Content-Length\", \"1000\")\n\t\tres.Write([]byte(\"\"))\n\t} else if r < 10 {\n\t\tres.Header().Set(\"Content-Length\", \"10000000000000\")\n\t\tio.Copy(res, billionsOfLol)\n\t} else if r < 15 {\n\t\tres.Header().Set(\"Content-Encoding\", \"gzip\")\n\t\tres.Header().Set(\"Content-Type\", \"text/html\")\n\t\tres.Write([]byte(\"WAZAAAA\"))\n\t} else if r < 20 {\n\t\tres.Header().Set(\"Content-Encoding\", \"gzip\")\n\t\tres.Header().Set(\"Content-Type\", \"text/html\")\n\t\tio.Copy(res, billionsOfLol)\n\t} else if r < 25 {\n\t\tres.Header().Set(\"Content-Language\", \"en-US\\u000bContent‑Encoding: gzip\")\n\t\tio.Copy(res, gzipBomb)\n\t} else if r < 30 {\n\t\tres.Header().Set(\"Content‑Encoding\", \"gzip\")\n\t\tio.Copy(res, gzipBomb)\n\t} else if r < 35 {\n\t\tres.Header().Set(\"Content-Type\", \"application/json\")\n\t\tres.Write([]byte(strings.Repeat(\"[\", 10000) + `\"WAZAAAAA\"` + strings.Repeat(\"]\", 10000)))\n\t} else if r < 40 {\n\t\tres.Header().Set(\"Content-Type\", \"application/json\")\n\t\tres.Write([]byte(`{\"response\"꞉ \"success\"}`)) // not an ASCII colon -> not valid json\n\t} else if r < 45 {\n\t\treq.URL.Host = \"127.0.0.1\"\n\t\tif req.URL.Scheme == \"\" {\n\t\t\treq.URL.Scheme = \"http\"\n\t\t}\n\t\thttp.Redirect(res, req, req.URL.String(), 301)\n\t} else if r < 50 {\n\t\treq.URL.Host = req.RemoteAddr\n\t\tif req.URL.Scheme == \"\" {\n\t\t\treq.URL.Scheme = \"http\"\n\t\t}\n\t\thttp.Redirect(res, req, req.URL.String(), 301)\n\t} else if r < 55 {\n\t\thttp.Redirect(res, req, \"geo:37.786971,-122.399677\", 301)\n\t} else if r < 60 {\n\t\tres.Header().Set(\"Content-Type\", \"text/html\")\n\t\tres.Write([]byte(`<html><script>alert(\"WAZAAAA\");</script></html>`))\n\t} else if r < 65 {\n\t\tres.Header().Set(\"Content-Type\", \"text/html\")\n\t\tres.Write([]byte(`<html><head><script>do { console.log(\"WAZAAAA\"); } while(true);</script></head><body></body></html>`))\n\t} else if r < 85 { // xml bomb\n\t\t// https://en.wikipedia.org/wiki/Billion_laughs_attack\n\t\tif rand.Intn(100) < 50 {\n\t\t\tres.Header().Set(\"Content-Type\", \"text/xml\")\n\t\t} else {\n\t\t\tres.Header().Set(\"Content-Type\", \"application/xml\")\n\t\t}\n\t\tio.Copy(res, billionsOfLol)\n\t} else {\n\t\tres.Header().Set(\"Content-Encoding\", \"gzip\")\n\t\tres.Header().Set(\"Content-Type\", \"text/html\")\n\t\tio.Copy(res, gzipBomb)\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_security_svg/index.go",
    "content": "package plg_security_svg\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"io\"\n\t\"net/http\"\n\t\"regexp\"\n)\n\nvar (\n\tdisable_svg func() bool\n)\n\nfunc init() {\n\tdisable_svg = func() bool {\n\t\treturn Config.Get(\"features.protection.disable_svg\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Default = true\n\t\t\tf.Name = \"disable_svg\"\n\t\t\tf.Type = \"boolean\"\n\t\t\tf.Target = []string{}\n\t\t\tf.Description = \"Disable the display of SVG documents\"\n\t\t\tf.Placeholder = \"Default: true\"\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\tHooks.Register.Onload(func() {\n\t\tif disable_svg() == false {\n\t\t\treturn\n\t\t}\n\t\tHooks.Register.ProcessFileContentBeforeSend(func(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, bool, error) {\n\t\t\tif GetMimeType(req.URL.Query().Get(\"path\")) != \"image/svg+xml\" {\n\t\t\t\treturn reader, false, nil\n\t\t\t} else if disable_svg() == false {\n\t\t\t\treturn reader, false, nil\n\t\t\t}\n\n\t\t\t// XSS\n\t\t\t(*res).Header().Set(\"Content-Security-Policy\", \"script-src 'none'; default-src 'none'; img-src 'self'\")\n\t\t\t(*res).Header().Set(\"Content-Type\", \"text/plain\")\n\t\t\t// XML bomb\n\t\t\ttxt, _ := io.ReadAll(reader)\n\t\t\tif regexp.MustCompile(\"(?is)entity\").Match(txt) {\n\t\t\t\ttxt = []byte(\"\")\n\t\t\t}\n\t\t\treturn NewReadCloserFromBytes(txt), true, nil\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "server/plugin/plg_starter_http/index.go",
    "content": "package plg_starter_http\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"github.com/gorilla/mux\"\n)\n\nfunc init() {\n\tHooks.Register.Starter(Start)\n}\n\nfunc Start(ctx context.Context, r *mux.Router) {\n\tLog.Info(\"[http] starting ...\")\n\tport := Config.Get(\"general.port\").Int()\n\tsrv := &http.Server{\n\t\tAddr:    fmt.Sprintf(\":%d\", port),\n\t\tHandler: r,\n\t}\n\tgo func() {\n\t\tensureAppHasBooted(\n\t\t\tfmt.Sprintf(\"http://127.0.0.1:%d%s\", port, WithBase(\"/about\")),\n\t\t\tfmt.Sprintf(\"[http] listening on :%d\", port),\n\t\t)\n\t\t<-ctx.Done()\n\t\tsrv.Shutdown(context.Background())\n\t}()\n\tif err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {\n\t\tLog.Error(\"error: %v\", err)\n\t}\n}\n\nfunc ensureAppHasBooted(address string, message string) {\n\tfor i := 0; i < 10; i++ {\n\t\tif i > 10 {\n\t\t\tLog.Warning(\"[http] didn't boot\")\n\t\t\tbreak\n\t\t}\n\t\ttime.Sleep(250 * time.Millisecond)\n\t\tres, err := http.Get(address)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tres.Body.Close()\n\t\tif res.StatusCode != http.StatusOK && res.StatusCode != http.StatusNotFound {\n\t\t\tcontinue\n\t\t}\n\t\tLog.Info(message)\n\t\tbreak\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_starter_http2/index.go",
    "content": "package plg_starter_http2\n\n/*\n * In golang, HTTP2 server are written in the same way as HTTPS server, the only difference beeing\n * describe in the documentation: https://golang.org/src/net/http/doc.go#L81\n * In our implementation, we use the 'TLSNextProto' trick\n */\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/common/ssl\"\n\n\t\"github.com/gorilla/mux\"\n\t\"golang.org/x/crypto/acme/autocert\"\n)\n\nvar (\n\tSSL_PATH    string\n\tconfig_port func() int\n)\n\nfunc init() {\n\tconfig_port = func() int {\n\t\treturn Config.Get(\"general.port\").Int()\n\t}\n\tHooks.Register.Onload(func() {\n\t\tSSL_PATH = filepath.Join(GetAbsolutePath(CERT_PATH), \"ssl\")\n\t\tos.MkdirAll(SSL_PATH, os.ModePerm)\n\t})\n\n\tHooks.Register.Starter(func(ctx context.Context, r *mux.Router) {\n\t\tdomain := Config.Get(\"general.host\").String()\n\t\tLog.Info(\"[https] starting ...%s\", domain)\n\t\tsrv := &http.Server{\n\t\t\tAddr:      fmt.Sprintf(\":%d\", config_port()),\n\t\t\tHandler:   r,\n\t\t\tTLSConfig: &DefaultTLSConfig,\n\t\t\tErrorLog:  NewNilLogger(),\n\t\t}\n\n\t\tswitch domain {\n\t\tcase \"\":\n\t\t\tTLSCert, roots, err := ssl.GenerateSelfSigned()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsrv.TLSConfig.Certificates = []tls.Certificate{TLSCert}\n\t\t\tHTTPClient.Transport.(*TransformedTransport).Orig.(*http.Transport).TLSClientConfig = &tls.Config{\n\t\t\t\tRootCAs: roots,\n\t\t\t}\n\t\t\tHTTP.Transport.(*TransformedTransport).Orig.(*http.Transport).TLSClientConfig = &tls.Config{\n\t\t\t\tRootCAs: roots,\n\t\t\t}\n\t\tdefault:\n\t\t\tmngr := autocert.Manager{\n\t\t\t\tPrompt:     autocert.AcceptTOS,\n\t\t\t\tHostPolicy: autocert.HostWhitelist(domain),\n\t\t\t\tCache:      autocert.DirCache(SSL_PATH),\n\t\t\t}\n\t\t\tsrv.TLSConfig.GetCertificate = mngr.GetCertificate\n\t\t}\n\n\t\tgo func() {\n\t\t\tensureAppHasBooted(\n\t\t\t\tfmt.Sprintf(\"https://127.0.0.1:%d/about\", config_port()),\n\t\t\t\tfmt.Sprintf(\"[https] started\"),\n\t\t\t)\n\t\t\t<-ctx.Done()\n\t\t\tsrv.Shutdown(context.Background())\n\t\t}()\n\t\tif err := srv.ListenAndServeTLS(\"\", \"\"); err != nil && err != http.ErrServerClosed {\n\t\t\tLog.Error(\"[https]: listen_serve %v\", err)\n\t\t\treturn\n\t\t}\n\t})\n}\n\nfunc ensureAppHasBooted(address string, message string) {\n\tfor i := 0; i < 10; i++ {\n\t\tif i > 10 {\n\t\t\tLog.Warning(\"[https] didn't boot\")\n\t\t\tbreak\n\t\t}\n\t\ttime.Sleep(250 * time.Millisecond)\n\t\tres, err := HTTPClient.Get(address)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tres.Body.Close()\n\t\tif res.StatusCode != http.StatusOK && res.StatusCode != http.StatusNotFound {\n\t\t\tcontinue\n\t\t}\n\t\tLog.Info(message)\n\t\tbreak\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_starter_https/index.go",
    "content": "package plg_starter_https\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t\"github.com/mickael-kerjean/filestash/server/common/ssl\"\n\n\t\"github.com/gorilla/mux\"\n)\n\nfunc init() {\n\tHooks.Register.Starter(func(ctx context.Context, r *mux.Router) {\n\t\tdomain := Config.Get(\"general.host\").String()\n\t\tport := Config.Get(\"general.port\").Int()\n\t\tLog.Info(\"[https] starting ...%s\", domain)\n\t\tsrv := &http.Server{\n\t\t\tAddr:         fmt.Sprintf(\":%d\", port),\n\t\t\tHandler:      r,\n\t\t\tTLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0),\n\t\t\tTLSConfig:    &DefaultTLSConfig,\n\t\t\tErrorLog:     NewNilLogger(),\n\t\t}\n\n\t\tTLSCert, roots, err := ssl.GenerateSelfSigned()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tsrv.TLSConfig.Certificates = []tls.Certificate{TLSCert}\n\t\tHTTPClient.Transport.(*TransformedTransport).Orig.(*http.Transport).TLSClientConfig = &tls.Config{\n\t\t\tRootCAs: roots,\n\t\t}\n\t\tHTTP.Transport.(*TransformedTransport).Orig.(*http.Transport).TLSClientConfig = &tls.Config{\n\t\t\tRootCAs: roots,\n\t\t}\n\n\t\tgo func() {\n\t\t\tensureAppHasBooted(\n\t\t\t\tfmt.Sprintf(\"https://127.0.0.1:%d/about\", port),\n\t\t\t\tfmt.Sprintf(\"[https] listening on :%d\", port),\n\t\t\t)\n\t\t\t<-ctx.Done()\n\t\t\tsrv.Shutdown(context.Background())\n\t\t}()\n\t\tif err := srv.ListenAndServeTLS(\"\", \"\"); err != nil && err != http.ErrServerClosed {\n\t\t\tLog.Error(\"[https]: listen_serve %v\", err)\n\t\t\treturn\n\t\t}\n\t})\n}\n\nfunc ensureAppHasBooted(address string, message string) {\n\tfor i := 0; i < 10; i++ {\n\t\tif i > 10 {\n\t\t\tLog.Warning(\"[https] didn't boot\")\n\t\t\tbreak\n\t\t}\n\t\ttime.Sleep(250 * time.Millisecond)\n\t\tres, err := HTTPClient.Get(address)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tres.Body.Close()\n\t\tif res.StatusCode != http.StatusOK && res.StatusCode != http.StatusNotFound {\n\t\t\tcontinue\n\t\t}\n\t\tLog.Info(message)\n\t\tbreak\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_starter_tor/index.go",
    "content": "package plg_starter_tor\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"github.com/cretz/bine/tor\"\n\t\"github.com/gorilla/mux\"\n)\n\nfunc init() {\n\tHooks.Register.Starter(func(ctx context.Context, r *mux.Router) {\n\t\ttorPath := GetAbsolutePath(CERT_PATH, \"tor\")\n\t\tos.MkdirAll(torPath, os.ModePerm)\n\n\t\tLog.Info(\"[tor] starting ...\")\n\t\tt, err := tor.Start(nil, &tor.StartConf{\n\t\t\tDataDir: torPath,\n\t\t})\n\t\tif err != nil {\n\t\t\tLog.Error(\"[tor] Unable to start Tor: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tdefer t.Close()\n\t\tlistenCtx, listenCancel := context.WithTimeout(context.Background(), 3*time.Minute)\n\t\tdefer listenCancel()\n\t\tonion, err := t.Listen(listenCtx, &tor.ListenConf{Version3: true, RemotePorts: []int{80}})\n\t\tif err != nil {\n\t\t\tLog.Error(\"[tor] Unable to create onion service: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tdefer onion.Close()\n\n\t\tsrv := &http.Server{\n\t\t\tHandler: r,\n\t\t}\n\t\tgo func() {\n\t\t\t<-ctx.Done()\n\t\t\tsrv.Shutdown(context.Background())\n\t\t}()\n\t\tLog.Info(\"[tor] started http://%s.onion\\n\", onion.ID)\n\t\tConfig.Get(\"features.server.tor_url\").Set(\"http://\" + onion.ID + \".onion\")\n\t\tsrv.Serve(onion)\n\t})\n}\n"
  },
  {
    "path": "server/plugin/plg_video_thumbnail/index.go",
    "content": "package plg_video_thumbnail\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nconst (\n\tVideoCachePath = \"data/cache/video-thumbnail/\"\n)\n\nvar plugin_enable = func() bool {\n\treturn Config.Get(\"features.video.enable_thumbnail\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"enable_thumbnail\"\n\t\tf.Type = \"enable\"\n\t\tf.Target = []string{}\n\t\tf.Description = \"Enable/Disable on video thumbnail generation\"\n\t\tf.Default = false\n\t\treturn f\n\t}).Bool()\n}\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tif plugin_enable() == false {\n\t\t\treturn\n\t\t} else if _, err := exec.LookPath(\"ffmpeg\"); err != nil {\n\t\t\tLog.Warning(\"plg_video_thumbnail::init error=ffmpeg_not_installed\")\n\t\t\treturn\n\t\t}\n\n\t\tcachePath := GetAbsolutePath(VideoCachePath)\n\t\tos.RemoveAll(cachePath)\n\t\tos.MkdirAll(cachePath, os.ModePerm)\n\n\t\tHooks.Register.Thumbnailer(\"video/mp4\", &ffmpegThumbnail{})\n\t\tHooks.Register.Thumbnailer(\"video/x-matroska\", &ffmpegThumbnail{})\n\t\tHooks.Register.Thumbnailer(\"video/x-msvideo\", &ffmpegThumbnail{})\n\t})\n}\n\ntype ffmpegThumbnail struct{}\n\nfunc (this *ffmpegThumbnail) Generate(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, error) {\n\tvar (\n\t\terrBuff bytes.Buffer\n\t\tfullURL = strings.Replace(\n\t\t\tfmt.Sprintf(\"http://127.0.0.1:%d%s?%s\", Config.Get(\"general.port\").Int(), req.URL.Path, req.URL.RawQuery),\n\t\t\t\"&thumbnail=true\", \"&origin=plg_video_thumbnail\", 1,\n\t\t)\n\t\tcacheName = \"thumb_\" + GenerateID(ctx.Session) + \"_\" + QuickHash(req.URL.Query().Get(\"path\"), 10) + \".jpeg\"\n\t\tcachePath = GetAbsolutePath(VideoCachePath, cacheName)\n\t)\n\n\treader.Close()\n\tthumbnail, err := os.OpenFile(cachePath, os.O_RDONLY, os.ModePerm)\n\tif err == nil {\n\t\tthis.setHeader(res)\n\t\treturn thumbnail, nil\n\t}\n\n\tcmd := exec.CommandContext(req.Context(), \"ffmpeg\", []string{\n\t\t\"-hide_banner\", \"-headers\", \"cookie: \" + req.Header.Get(\"Cookie\") + \"\\r\\n\",\n\t\t\"-skip_frame\", \"nokey\",\n\t\t\"-i\", fullURL, \"-y\",\n\t\t\"-an\", \"-sn\",\n\t\t\"-vf\", \"thumbnail, scale=320:320: force_original_aspect_ratio=decrease\", \"-fps_mode\", \"passthrough\", \"-frames:v\", \"1\",\n\t\t\"-c:v\", \"mjpeg\", cachePath,\n\t}...)\n\tcmd.Stderr = &errBuff\n\tif err := cmd.Run(); err != nil {\n\t\tif req.Context().Err() == nil {\n\t\t\tLog.Debug(\"plg_video_thumbnail::generate::run path=%s err=%s\", req.URL.Query().Get(\"path\"), base64.StdEncoding.EncodeToString(errBuff.Bytes()))\n\t\t\treturn nil, err\n\t\t}\n\t\treturn nil, err\n\t}\n\tcmd.Wait()\n\tthumbnail, err = os.OpenFile(cachePath, os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\tLog.Error(\"plg_video_thumbnail::generate::open path=%s err=%s\", cachePath, err.Error())\n\t\treturn nil, err\n\t}\n\tthis.setHeader(res)\n\treturn thumbnail, nil\n}\n\nfunc (this *ffmpegThumbnail) setHeader(res *http.ResponseWriter) {\n\t(*res).Header().Set(\"Content-Type\", \"image/jpeg\")\n\t(*res).Header().Set(\"Cache-Control\", fmt.Sprintf(\"max-age=%d\", 3600*12))\n}\n"
  },
  {
    "path": "server/plugin/plg_video_transcoder/index.go",
    "content": "package plg_video_transcoder\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gorilla/mux\"\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/middleware\"\n)\n\nconst (\n\tHLS_SEGMENT_LENGTH = 5\n\tFPS                = 30\n\tCLEAR_CACHE_AFTER  = 12\n\tVideoCachePath     = \"data/cache/video/\"\n)\n\nvar (\n\tplugin_enable    func() bool\n\tblacklist_format func() string\n)\n\nfunc init() {\n\tplugin_enable = func() bool {\n\t\treturn Config.Get(\"features.video.enable_transcoder\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Name = \"enable_transcoder\"\n\t\t\tf.Type = \"enable\"\n\t\t\tf.Target = []string{\"transcoding_blacklist_format\"}\n\t\t\tf.Description = \"Enable/Disable on demand video transcoding. The transcoder\"\n\t\t\tf.Default = true\n\t\t\treturn f\n\t\t}).Bool()\n\t}\n\tblacklist_format = func() string {\n\t\treturn Config.Get(\"features.video.blacklist_format\").Schema(func(f *FormElement) *FormElement {\n\t\t\tif f == nil {\n\t\t\t\tf = &FormElement{}\n\t\t\t}\n\t\t\tf.Id = \"transcoding_blacklist_format\"\n\t\t\tf.Name = \"blacklist_format\"\n\t\t\tf.Type = \"text\"\n\t\t\tf.Description = \"Video format that won't be transcoded\"\n\t\t\tf.Default = os.Getenv(\"FEATURE_TRANSCODING_VIDEO_BLACKLIST\")\n\t\t\tif f.Default != \"\" {\n\t\t\t\tf.Placeholder = fmt.Sprintf(\"Default: '%s'\", f.Default)\n\t\t\t}\n\t\t\treturn f\n\t\t}).String()\n\t}\n\n\tHooks.Register.Onload(func() {\n\t\tblacklist_format()\n\t\tif !plugin_enable() {\n\t\t\treturn\n\t\t} else if _, err := exec.LookPath(\"ffmpeg\"); err != nil {\n\t\t\tLog.Warning(\"plg_video_thumbnail::init error=ffmpeg_not_installed\")\n\t\t\treturn\n\t\t} else if _, err := exec.LookPath(\"ffprobe\"); err != nil {\n\t\t\tLog.Warning(\"plg_video_thumbnail::init error=ffprobe_not_installed\")\n\t\t\treturn\n\t\t}\n\n\t\tcachePath := GetAbsolutePath(VideoCachePath)\n\t\tos.RemoveAll(cachePath)\n\t\tos.MkdirAll(cachePath, os.ModePerm)\n\n\t\tHooks.Register.HttpEndpoint(func(r *mux.Router) error {\n\t\t\tr.HandleFunc(OverrideVideoSourceMapper, func(res http.ResponseWriter, req *http.Request) {\n\t\t\t\tres.Header().Set(\"Content-Type\", GetMimeType(req.URL.String()))\n\t\t\t\tif plugin_enable() == false {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tres.Write([]byte(`window.overrides[\"video-map-sources\"] = function(sources){`))\n\t\t\t\tres.Write([]byte(`    return sources.map(function(source){`))\n\n\t\t\t\tblacklists := strings.Split(blacklist_format(), \",\")\n\t\t\t\tfor i := 0; i < len(blacklists); i++ {\n\t\t\t\t\tblacklists[i] = strings.TrimSpace(blacklists[i])\n\t\t\t\t\tres.Write([]byte(fmt.Sprintf(`if(source.type == \"%s\"){ return source; } `, GetMimeType(\".\"+blacklists[i]))))\n\t\t\t\t}\n\t\t\t\tres.Write([]byte(`        source.src = source.src + \"&transcode=hls\";`))\n\t\t\t\tres.Write([]byte(`        source.type = \"application/x-mpegURL\";`))\n\t\t\t\tres.Write([]byte(`        return source;`))\n\t\t\t\tres.Write([]byte(`    })`))\n\t\t\t\tres.Write([]byte(`}`))\n\t\t\t})\n\n\t\t\tr.PathPrefix(\"/hls/hls_{segment}.ts\").Handler(NewMiddlewareChain(\n\t\t\t\thlsTranscodeHandler,\n\t\t\t\t[]Middleware{SecureHeaders},\n\t\t\t)).Methods(\"GET\")\n\n\t\t\treturn nil\n\t\t})\n\t\tHooks.Register.ProcessFileContentBeforeSend(hlsPlaylistHandler)\n\t})\n}\n\nfunc hlsPlaylistHandler(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, bool, error) {\n\tquery := req.URL.Query()\n\tif query.Get(\"transcode\") != \"hls\" {\n\t\treturn reader, false, nil\n\t}\n\tpath := query.Get(\"path\")\n\tif strings.HasPrefix(GetMimeType(path), \"video/\") == false {\n\t\treturn reader, false, nil\n\t}\n\n\tcacheName := \"vid_\" + GenerateID(ctx.Session) + \"_\" + QuickHash(path, 10) + \".dat\"\n\tcachePath := GetAbsolutePath(\n\t\tVideoCachePath,\n\t\tcacheName,\n\t)\n\tf, err := os.OpenFile(cachePath, os.O_CREATE|os.O_RDWR, os.ModePerm)\n\tif err != nil {\n\t\treturn reader, false, err\n\t}\n\tio.Copy(f, reader)\n\treader.Close()\n\tf.Close()\n\ttime.AfterFunc(CLEAR_CACHE_AFTER*time.Hour, func() { os.Remove(cachePath) })\n\n\tp, err := ffprobe(cachePath)\n\tif err != nil {\n\t\treturn reader, false, err\n\t}\n\n\tvar response string\n\tvar i int\n\tresponse = \"#EXTM3U\\n\"\n\tresponse += \"#EXT-X-VERSION:3\\n\"\n\tresponse += \"#EXT-X-MEDIA-SEQUENCE:0\\n\"\n\tresponse += \"#EXT-X-ALLOW-CACHE:YES\\n\"\n\tresponse += fmt.Sprintf(\"#EXT-X-TARGETDURATION:%d\\n\", HLS_SEGMENT_LENGTH)\n\tfor i = 0; i < int(p.Format.Duration)/HLS_SEGMENT_LENGTH; i++ {\n\t\tresponse += fmt.Sprintf(\"#EXTINF:%d.0000, nodesc\\n\", HLS_SEGMENT_LENGTH)\n\t\tresponse += fmt.Sprintf(\"/hls/hls_%d.ts?path=%s\\n\", i, cacheName)\n\t}\n\tif md := math.Mod(p.Format.Duration, HLS_SEGMENT_LENGTH); md > 0 {\n\t\tresponse += fmt.Sprintf(\"#EXTINF:%.4f, nodesc\\n\", md)\n\t\tresponse += fmt.Sprintf(\"/hls/hls_%d.ts?path=%s\\n\", i, cacheName)\n\t}\n\tresponse += \"#EXT-X-ENDLIST\\n\"\n\t(*res).Header().Set(\"Content-Type\", \"application/x-mpegURL\")\n\treturn NewReadCloserFromBytes([]byte(response)), true, nil\n}\n\nfunc hlsTranscodeHandler(ctx *App, res http.ResponseWriter, req *http.Request) {\n\tif plugin_enable() == false {\n\t\treturn\n\t}\n\tsegmentNumber, err := strconv.Atoi(mux.Vars(req)[\"segment\"])\n\tif err != nil {\n\t\tLog.Info(\"[plugin hls] invalid segment request '%s'\", mux.Vars(req)[\"segment\"])\n\t\tres.WriteHeader(http.StatusBadRequest)\n\t\treturn\n\t}\n\tstartTime := segmentNumber * HLS_SEGMENT_LENGTH\n\tcachePath := GetAbsolutePath(\n\t\tVideoCachePath,\n\t\treq.URL.Query().Get(\"path\"),\n\t)\n\tif _, err := os.Stat(cachePath); os.IsNotExist(err) {\n\t\tLog.Info(\"[plugin hls]: invalid video\")\n\t\tres.WriteHeader(http.StatusServiceUnavailable)\n\t\treturn\n\t}\n\n\tcmd := exec.CommandContext(req.Context(), \"ffmpeg\", []string{\n\t\t\"-timelimit\", \"30\",\n\t\t\"-ss\", fmt.Sprintf(\"%d.00\", startTime),\n\t\t\"-i\", cachePath,\n\t\t\"-t\", fmt.Sprintf(\"%d.00\", HLS_SEGMENT_LENGTH),\n\t\t\"-vf\", fmt.Sprintf(\"scale=-2:%d\", 720),\n\t\t\"-vcodec\", \"libx264\",\n\t\t\"-preset\", \"veryfast\",\n\t\t\"-acodec\", \"aac\",\n\t\t\"-b:a\", \"128k\",\n\t\t\"-ac\", \"2\",\n\t\t\"-pix_fmt\", \"yuv420p\",\n\t\t\"-x264opts\", strings.Join([]string{\n\t\t\t\"subme=0\",\n\t\t\t\"me_range=4\",\n\t\t\t\"rc_lookahead=10\",\n\t\t\t\"me=dia\",\n\t\t\t\"no_chroma_me\",\n\t\t\t\"8x8dct=0\",\n\t\t\t\"partitions=none\",\n\t\t}, \":\"),\n\t\t\"-force_key_frames\", fmt.Sprintf(\"expr:gte(t,n_forced*%d.000)\", HLS_SEGMENT_LENGTH),\n\t\t\"-f\", \"mpegts\",\n\t\t\"-output_ts_offset\", fmt.Sprintf(\"%d.00\", startTime),\n\t\t\"-fps_mode\", \"cfr\",\n\t\t\"pipe:1\",\n\t}...)\n\n\tvar buffer bytes.Buffer\n\tcmd.Stdout = res\n\tcmd.Stderr = &buffer\n\terr = cmd.Run()\n\tif err != nil {\n\t\tLog.Error(\"plg_video_transcoder::ffmpeg::run '%s' - %s\", err.Error(), base64.StdEncoding.EncodeToString(buffer.Bytes()))\n\t}\n}\n\ntype FFProbeData struct {\n\tFormat struct {\n\t\tDuration float64 `json:\"duration,string\"`\n\t\tBitRate  int     `json:\"bit_rate,string\"`\n\t} `json: \"format\"`\n\tStreams []struct {\n\t\tCodecType   string `json:\"codec_type\"`\n\t\tCodecName   string `json:\"codec_name\"`\n\t\tPixelFormat string `json:\"pix_fmt\"`\n\t} `json:\"streams\"`\n}\n\nfunc ffprobe(videoPath string) (FFProbeData, error) {\n\tvar stream bytes.Buffer\n\tvar probe FFProbeData\n\n\tcmd := exec.Command(\n\t\t\"ffprobe\", strings.Split(fmt.Sprintf(\n\t\t\t\"-v quiet -print_format json -show_format -show_streams %s\",\n\t\t\tvideoPath,\n\t\t), \" \")...,\n\t)\n\tcmd.Stdout = &stream\n\tif err := cmd.Run(); err != nil {\n\t\treturn probe, nil\n\t}\n\tcmd.Run()\n\tif err := json.Unmarshal([]byte(stream.String()), &probe); err != nil {\n\t\treturn probe, err\n\t}\n\treturn probe, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_chat/assets/sidebar.diff",
    "content": "diff --git a/public/assets/components/sidebar.js b/public/assets/components/sidebar.js\nindex 268aa4eb..e7b93142 100644\n--- a/public/assets/components/sidebar.js\n+++ b/public/assets/components/sidebar.js\n@@ -52,1 +52,5 @@ export default async function ctrlSidebar(render, {}) {\n\n+    qs($sidebar, \".component_sidebar > div\").appendChild(createElement(`<div data-bind=\"chat\"></div>`));\n+    import(location.origin + \"/plg_handler_chat/sidebar_chat.js\")\n+        .then((module) => module.default(createRender(qs($sidebar, `[data-bind=\"chat\"]`)), { path }))\n+        .catch((err) => console.log(err));\n"
  },
  {
    "path": "server/plugin/plg_widget_chat/assets/sidebar_chat.js",
    "content": "import { createElement, createRender, nop } from \"../lib/skeleton/index.js\";\nimport { qs, safe } from \"../lib/dom.js\";\nimport rxjs, { effect, applyMutation, preventDefault } from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { forwardURLParams } from \"../../lib/path.js\";\nimport t from \"../locales/index.js\";\nimport { createModal } from \"../components/modal.js\";\nimport { generateSkeleton } from \"../components/skeleton.js\";\n\nexport default async function(render, { path }) {\n    const $page = createElement(`\n        <div class=\"plg_widget_chat\">\n            <h3 class=\"no-select\">\n                <img src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIHN0eWxlPSJzdHJva2U6IzU3NTk1YSIgZD0iTTIxIDE1YTIgMiAwIDAgMS0yIDJIN2wtNCA0VjVhMiAyIDAgMCAxIDItMmgxNGEyIDIgMCAwIDEgMiAyeiIvPjwvc3ZnPg==\" alt=\"chat\">\n                <span data-bind=\"compose\"></span>\n            </h3>\n            <div data-bind=\"mentions\"></div>\n            <ul data-bind=\"messages\">${generateSkeleton(1)}</ul>\n            <style>${CSS}</style>\n        </div>\n    `);\n    render($page);\n\n    const $dom = {\n        compose: ($el) => qs($el, `[data-bind=\"compose\"]`),\n        messages: ($el) => qs($el, `[data-bind=\"messages\"]`),\n        mentions: ($el) => qs($el, `[data-bind=\"mentions\"]`),\n        input: ($el) => qs($el, \"input\"),\n    };\n\n    const refresh$ = getMessages(path).pipe(\n        rxjs.catchError(() => rxjs.EMPTY),\n    );\n    renderMessages(createRender($dom.messages($page)), { $dom, refresh$ });\n    renderCompose(createRender($dom.compose($page)), {\n        path, refresh$, $dom,\n        onRefresh: () => renderMessages(createRender($dom.messages($page)), { $dom, refresh$ }),\n        onLoad: () => renderMentions(createRender($dom.mentions($page)), {\n            $input: $dom.input($page),\n        }),\n    });\n}\n\nfunction renderMessages(render, { $dom, refresh$, sidebar = true }) {\n    const onMessageClick = ({ path }) => {\n        const loadingHTML = `\n            <div data-bind=\"thread\">\n                <component-icon name=\"loading\"></component-icon>\n            </div>\n        `;\n        const $modal = createElement(loadingHTML);\n        createModal({})($modal);\n\n        const trigger$ = new rxjs.Subject();\n        const refresh$ = trigger$.pipe(\n            rxjs.startWith(null),\n            rxjs.switchMap(() => getMessages(path)),\n            rxjs.catchError(() => rxjs.EMPTY),\n        );\n\n        effect(refresh$.pipe(\n            rxjs.map((messages) => {\n                const $page = createElement(`\n                    <div class=\"plg_widget_chat\">\n                        <div data-bind=\"compose\"></div>\n                        <div data-bind=\"mentions\"></div>\n                        <ul data-bind=\"messages\" class=\"${messages.length > 7 ? \"scroll-y\" : \"\"}\"></ul>\n                    </div>\n                `);\n                renderCompose(createRender($dom.compose($page)), {\n                    path, refresh$, $dom,\n                    onRefresh: () => trigger$.next(),\n                    onLoad: () => renderMentions(createRender($dom.mentions($page)), {\n                        $input: $dom.input($page),\n                    }),\n                });\n                const $messages = document.createDocumentFragment();\n                for (const message of messages) {\n                    $messages.appendChild(renderMessage(message, { sidebar: false }));\n                }\n                $dom.messages($page).appendChild($messages);\n                return $page;\n            }),\n            applyMutation($modal, \"replaceChildren\"),\n        ));\n    }\n\n    effect(refresh$.pipe(\n        rxjs.map((messages) => {\n            if (messages.length === 0) return createElement(`<div class=\"placeholder center no-select\">∅</div>`);\n            const $messages = document.createDocumentFragment();\n            for (const message of messages) {\n                $messages.appendChild(renderMessage(message, {\n                    onClick: () => onMessageClick({ path: message.path }),\n                    sidebar,\n                }));\n            }\n            return $messages;\n        }),\n        applyMutation(render(createElement(`<ul></ul>`)), \"replaceChildren\"),\n    ));\n}\n\nfunction renderCompose(render, { path, refresh$, $dom, onRefresh, onLoad }) {\n    const $form = createElement(`<form><input type=\"text\" name=\"message\" placeholder=\"${t(\"Chat\")}\" autocomplete=\"off\" /></form>`);\n\n    render($form);\n    onLoad();\n\n    const $input = $dom.input($form);\n    effect(rxjs.fromEvent($form, \"submit\").pipe(\n        preventDefault(),\n        rxjs.mergeMap((e) => {\n            const message = new FormData(e.target).get(\"message\");\n            $input.disabled = true;\n            return createMessage({ message, path });\n        }),\n        rxjs.tap(() => {\n            $input.value = \"\";\n            $input.disabled = false;\n            onRefresh();\n        }),\n    ));\n}\n\nfunction renderMessage(obj, { onClick = nop, sidebar = true }) {\n    const $message = createElement(`\n        <li title=\"${safe(obj.message)}\">\n            <a data-link draggable=\"false\">\n                <div class=\"${sidebar ? \"ellipsis\" : \"\" }\">\n                    <span class=\"message-author\">${safe(obj.author)}:</span>\n                    <span class=\"message-content\">${safe(obj.message)}</span>\n                </div>\n            </a>\n        </li>\n    `);\n    effect(rxjs.fromEvent($message, \"click\").pipe(\n        rxjs.tap(() => onClick()),\n    ));\n    effect(rxjs.of(null).pipe(\n        rxjs.filter(() => document.body.classList.contains(\"touch-no\")),\n        rxjs.tap(() => $message.onmouseenter = () => {\n            const $things = document.querySelectorAll(\".component_thing\");\n            $things.forEach(($thing) => {\n                const thingpath = $thing.getAttribute(\"data-path\");\n                if (obj.path.indexOf(thingpath) !== -1) $thing.classList.add(\"hover\");\n            });\n            $message.onmouseleave = () => $things.forEach(($thing) => $thing.classList.remove(\"hover\"));\n        }),\n    ));\n    return $message;\n}\n\nfunction renderMentions(render, { $input }) {\n    const $list = createElement(`<ul class=\"mentions hidden\"></ul>`);\n    render($list);\n\n    let active = -1;\n    const hide = () => { $list.classList.add(\"hidden\"); active = -1; };\n    const pick = (name) => {\n        const before = $input.value.substring(0, $input.selectionStart);\n        const after = $input.value.substring($input.selectionStart);\n        const atIdx = before.lastIndexOf(\"@\");\n        $input.value = before.substring(0, atIdx) + \"@\" + name + \" \" + after;\n        const cursor = atIdx + name.length + 2;\n        $input.setSelectionRange(cursor, cursor);\n        $input.focus();\n    };\n    const getMentionQuery = () => {\n        const text = $input.value.substring(0, $input.selectionStart);\n        const atIdx = text.lastIndexOf(\"@\");\n        if (atIdx === -1) return null;\n        if (atIdx > 0 && text[atIdx - 1] !== \" \") return null;\n        const query = text.substring(atIdx + 1);\n        if (query.indexOf(\" \") !== -1) return null;\n        return query;\n    };\n\n    effect(rxjs.fromEvent($input, \"input\").pipe(\n        rxjs.map(() => getMentionQuery()),\n        rxjs.switchMap((q) => {\n            if (q === null) {\n                hide();\n                return rxjs.EMPTY;\n            }\n            return rxjs.of(q);\n        }),\n        rxjs.debounceTime(150),\n        rxjs.switchMap((q) => searchUsers(q)),\n        rxjs.map((users) => {\n            if (users.length === 0) return document.createDocumentFragment();\n            const $messages = document.createDocumentFragment();\n            for (const user of users) {\n                const handle = user.name.replace(/\\s+/g, \".\");\n                const $li = createElement(`<li data-handle=\"${safe(handle)}\">${safe(user.name)}</li>`);\n                $li.onmousedown = (e) => { e.preventDefault(); pick(handle); hide(); };\n                $messages.appendChild($li);\n            }\n            return $messages;\n        }),\n        rxjs.tap(($messages) => {\n            $messages.childNodes.length > 0 ? $list.classList.remove(\"hidden\") : $list.classList.add(\"hidden\");\n            active = -1;\n        }),\n        applyMutation($list, \"replaceChildren\"),\n    ));\n    effect(rxjs.fromEvent($input, \"keydown\").pipe(\n        rxjs.filter(() => !$list.classList.contains(\"hidden\")),\n        rxjs.tap((e) => {\n            const items = $list.children;\n            if (e.key === \"ArrowDown\") {\n                e.preventDefault();\n                if (active >= 0) items[active].classList.remove(\"active\");\n                active = (active + 1) % items.length;\n                items[active].classList.add(\"active\");\n            } else if (e.key === \"ArrowUp\") {\n                e.preventDefault();\n                if (active >= 0) items[active].classList.remove(\"active\");\n                active = (active - 1 + items.length) % items.length;\n                items[active].classList.add(\"active\");\n            } else if (e.key === \"Enter\" && active >= 0) {\n                e.preventDefault();\n                pick(items[active].getAttribute(\"data-handle\"));\n                hide();\n            } else if (e.key === \"Escape\") {\n                hide();\n            }\n        }),\n    ));\n    effect(rxjs.fromEvent($input, \"blur\").pipe(\n        rxjs.tap(() => hide),\n    ));\n}\n\nconst searchUsers = (q) => ajax({ url: forwardURLParams(\"api/plg_widget_chat/lookup?q=\"+encodeURIComponent(q), [\"share\"]), responseType: \"json\" }).pipe(\n    rxjs.map(({ responseJSON }) => responseJSON.results || []),\n    rxjs.catchError(() => rxjs.of([])),\n);\n\nconst getMessages = (path = \"/\") => ajax({ url: forwardURLParams(\"api/plg_widget_chat/messages?path=\"+encodeURIComponent(path), [\"share\"]), responseType: \"json\" }).pipe(\n    rxjs.map(({ responseJSON }) => responseJSON.results.reverse()),\n);\n\nconst createMessage = ({ path, ...body }) => ajax({\n    url: forwardURLParams(\"api/plg_widget_chat/messages?path=\"+encodeURIComponent(path), [\"share\"]),\n    method: \"POST\",\n    body,\n});\n\nconst CSS = `\n/* MESSAGES */\n[data-bind=\"messages\"] {\n    font-size: 0.9rem;\n}\n[data-bind=\"messages\"] .message-author {\n    font-weight: 500;\n    opacity: 0.5;\n}\n\n/* SIDEBAR */\n.component_filemanager_shell .component_sidebar [data-bind=\"chat\"] h3 img {\n    position: relative;\n    top: 1px;\n}\n.component_filemanager_shell .component_sidebar [data-bind=\"chat\"] a {\n    cursor: pointer;\n}\n\n/* MODAL */\ncomponent-modal [data-bind=\"thread\"] component-icon[name=\"loading\"] {\n    display: block;\n    height: 30px;\n    text-align: center;\n}\ncomponent-modal [data-bind=\"thread\"] form input {\n    font-size: 1rem;\n    border: 2px solid var(--border);\n    border-radius: 5px;\n    padding: 5px 10px;\n    color: rgba(0, 0, 0, 0.75);\n    width: 100%;\n    display: block;\n    box-sizing: border-box;\n}\ncomponent-modal [data-bind=\"thread\"] [data-bind=\"messages\"] {\n    list-style-type: none;\n    margin: 10px 0 0 0;\n    padding: 0;\n    max-height: 200px;\n}\ncomponent-modal [data-bind=\"thread\"] [data-bind=\"messages\"] > li {\n    line-height: 1rem;\n    margin: 5px 5px;\n    text-transform: capitalize;\n}\n\n/* MENTIONS */\n.plg_widget_chat ul.mentions {\n    background: rgba(0, 0, 0, 0.65);\n    border-radius: 3px;\n    list-style: none;\n    margin: 0;\n    padding: 0;\n    max-height: 120px;\n    overflow-y: auto;\n    color: var(--bg-color);\n}\n.plg_widget_chat ul.mentions li {\n    padding: 3px 8px;\n    cursor: pointer;\n    font-size: 0.8rem;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n}\n.plg_widget_chat ul.mentions li:hover,\n.plg_widget_chat ul.mentions li.active {\n    background: rgba(198, 200, 204, 0.25);\n    border-radius: 3px;\n}\n`;\n"
  },
  {
    "path": "server/plugin/plg_widget_chat/config.go",
    "content": "package plg_widget_chat\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tPluginEnable()\n\t})\n}\n\nvar PluginEnable = func() bool {\n\treturn Config.Get(\"features.chat.enable\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"enable\"\n\t\tf.Type = \"enable\"\n\t\tf.Target = []string{}\n\t\tf.Default = false\n\t\treturn f\n\t}).Bool()\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_chat/db.go",
    "content": "package plg_widget_chat\n\nimport (\n\t\"os\"\n\t\"database/sql\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar db *sql.DB\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tif err := initDB(); err != nil {\n\t\t\tLog.Error(\"plg_handler_chat::db err=cannot_init msg=%s\", err.Error())\n\t\t\tos.Exit(1)\n\t\t}\n\t})\n}\n\nfunc initDB () error {\n\tvar err error\n\tdb, err = sql.Open(\"sqlite3\", GetAbsolutePath(DB_PATH, \"chat.db\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = db.Exec(`\n\t\tCREATE TABLE IF NOT EXISTS messages (\n\t\t\tid TEXT PRIMARY KEY,\n\t\t\tpath TEXT NOT NULL,\n\t\t\tauthor TEXT NOT NULL,\n\t\t\tmessage TEXT NOT NULL,\n\t\t\tcreation_date INTEGER NOT NULL\n\t\t);\n\t\tCREATE INDEX IF NOT EXISTS idx_messages ON messages(path, creation_date);\n\t`)\n\treturn err\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_chat/handler.go",
    "content": "package plg_widget_chat\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/ctrl\"\n)\n\nfunc listMessages(ctx *App, w http.ResponseWriter, r *http.Request) {\n\tpath, err := PathBuilder(ctx, r.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\trows, err := db.QueryContext(r.Context(), `\n\t\tSELECT path, author, message, creation_date\n\t\t\tFROM messages\n\t\t\tWHERE path GLOB ?\n\t\t\tORDER BY creation_date ASC\n\t`, globAll(path))\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tdefer rows.Close()\n\n\tout := []Message{}\n\tfor rows.Next() {\n\t\tvar m Message\n\t\tif err := rows.Scan(\n\t\t\t&m.Path,\n\t\t\t&m.Author,\n\t\t\t&m.Message,\n\t\t\t&m.CreatedAt,\n\t\t); err != nil {\n\t\t\tSendErrorResult(w, err)\n\t\t\treturn\n\t\t}\n\t\tif ctx.Session[\"path\"] != \"\" {\n\t\t\tm.Path = strings.TrimPrefix(m.Path, strings.TrimSuffix(ctx.Session[\"path\"], \"/\"))\n\t\t}\n\t\tout = append(out, m)\n\t}\n\tSendSuccessResults(w, out)\n}\n\nfunc createMessage(ctx *App, w http.ResponseWriter, r *http.Request) {\n\tpath, err := PathBuilder(ctx, r.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tmsg, ok := ctx.Body[\"message\"].(string)\n\tif !ok {\n\t\tSendErrorResult(w, NewError(\"Invalid parameters\", 400))\n\t\treturn\n\t}\n\tauthor := getUser(ctx.Session)\n\t_, err = db.ExecContext(ctx.Context, `\n\t\tINSERT INTO messages(id, path, author, message, creation_date)\n\t\tVALUES(?,?,?,?,?)\n\t`, newID(), path, author, msg, time.Now().Unix())\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\n\textractMentions := func(message string) []string {\n\t\tmatches := mention_re.FindAllStringSubmatch(message, -1)\n\t\tout := make([]string, 0, len(matches))\n\t\tfor _, m := range matches {\n\t\t\tname := strings.TrimSpace(m[1])\n\t\t\tif name != \"\" {\n\t\t\t\tout = append(out, name)\n\t\t\t}\n\t\t}\n\t\treturn out\n\t}\n\tfor _, name := range extractMentions(msg) {\n\t\tgo processMention(map[string]string{\n\t\t\t\"path\":    path,\n\t\t\t\"author\":  author,\n\t\t\t\"mention\": name,\n\t\t\t\"message\": msg,\n\t\t})\n\t}\n\tSendSuccessResult(w, nil)\n}\n\nfunc lookupUsers(ctx *App, w http.ResponseWriter, r *http.Request) {\n\tif ctx.Share.Id != \"\" {\n\t\tSendSuccessResults(w, []DirectoryUser{})\n\t\treturn\n\t}\n\tdir := Hooks.Get.DirectoryService()\n\tif dir == nil {\n\t\tSendSuccessResults(w, []DirectoryUser{})\n\t\treturn\n\t}\n\tresults, err := dir.Search(r.URL.Query().Get(\"q\"))\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tSendSuccessResults(w, results)\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_chat/index.go",
    "content": "package plg_widget_chat\n\nimport (\n\t_ \"embed\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/middleware\"\n\n\t\"github.com/gorilla/mux\"\n)\n\n//go:embed assets/sidebar_chat.js\nvar CTRLJS []byte\n\n//go:embed assets/sidebar.diff\nvar PATCH []byte\n\nfunc init() {\n\tHooks.Register.HttpEndpoint(func(r *mux.Router) error {\n\t\tr.HandleFunc(\"/api/plg_widget_chat/messages\", NewMiddlewareChain(createMessage, []Middleware{ApiHeaders, SecureHeaders, PluginGuard, SessionStart, LoggedInOnly, BodyParser})).Methods(\"POST\")\n\t\tr.HandleFunc(\"/api/plg_widget_chat/messages\", NewMiddlewareChain(listMessages, []Middleware{ApiHeaders, SecureHeaders, PluginGuard, SessionStart, LoggedInOnly})).Methods(\"GET\")\n\t\tr.HandleFunc(\"/api/plg_widget_chat/lookup\", NewMiddlewareChain(lookupUsers, []Middleware{ApiHeaders, SecureHeaders, PluginGuard, SessionStart, LoggedInOnly})).Methods(\"GET\")\n\n\t\tr.HandleFunc(WithBase(\"/plg_handler_chat/sidebar_chat.js\"), func(res http.ResponseWriter, req *http.Request) {\n\t\t\thttp.Redirect(res, req, WithBase(\"/assets/\"+BUILD_REF+\"/components/sidebar_chat.js\"), http.StatusSeeOther)\n\t\t})\n\t\tr.HandleFunc(WithBase(\"/assets/\"+BUILD_REF+\"/components/sidebar_chat.js\"), func(res http.ResponseWriter, req *http.Request) {\n\t\t\tres.Header().Set(\"Content-Type\", \"application/javascript\")\n\t\t\tres.Write(CTRLJS)\n\t\t}).Methods(\"GET\")\n\t\treturn nil\n\t})\n\n\tHooks.Register.OnConfig(func() {\n\t\tif PluginEnable() {\n\t\t\tHooks.Register.StaticPatch(PATCH, WithID(\"plg_widget_chat\"))\n\t\t} else {\n\t\t\tHooks.Register.StaticPatch([]byte(\"\"), WithID(\"plg_widget_chat\"))\n\t\t}\n\t})\n}\n\nfunc PluginGuard(fn HandlerFunc) HandlerFunc {\n\treturn func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tif !PluginEnable() {\n\t\t\tSendErrorResult(res, ErrNotAllowed)\n\t\t\treturn\n\t\t}\n\t\tfn(ctx, res, req)\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_chat/type.go",
    "content": "package plg_widget_chat\n\ntype Message struct {\n\tPath      string `json:\"path\"`\n\tAuthor    string `json:\"author\"`\n\tMessage   string `json:\"message\"`\n\tCreatedAt int64  `json:\"created_at\"`\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_chat/utils.go",
    "content": "package plg_widget_chat\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n)\n\nfunc newID() string {\n\tvar b [16]byte\n\t_, _ = rand.Read(b[:])\n\treturn hex.EncodeToString(b[:])\n}\n\nfunc getUser(session map[string]string) string {\n\tif session[\"username\"] != \"\" {\n\t\treturn session[\"username\"]\n\t} else if session[\"user\"] != \"\" {\n\t\treturn session[\"user\"]\n\t}\n\treturn \"unknown\"\n}\n\nfunc globAll(path string) string {\n\treturn path + \"**\"\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_chat/workflow.go",
    "content": "package plg_widget_chat\n\nimport (\n\t\"regexp\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/pkg/workflow/trigger\"\n\t. \"github.com/mickael-kerjean/filestash/server/pkg/workflow/model\"\n)\n\nvar (\n\tmention_name  = \"mention\"\n\tmention_event = make(chan ITriggerEvent, 10)\n\tmention_re    = regexp.MustCompile(`(?:^|\\s)@([\\w.]+)`)\n)\n\nfunc init() {\n\tHooks.Register.WorkflowTrigger(&MentionTrigger{})\n}\n\ntype MentionTrigger struct{}\n\nfunc (this *MentionTrigger) Manifest() WorkflowSpecs {\n\treturn WorkflowSpecs{\n\t\tName:  mention_name,\n\t\tTitle: \"When Someone is Mentioned\",\n\t\tIcon:  `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><path d=\"M320 128C214 128 128 214 128 320C128 426 214 512 320 512C337.7 512 352 526.3 352 544C352 561.7 337.7 576 320 576C178.6 576 64 461.4 64 320C64 178.6 178.6 64 320 64C461.4 64 576 178.6 576 320L576 352C576 405 533 448 480 448C450.7 448 424.4 434.8 406.8 414.1C384 435.1 353.5 448 320 448C249.3 448 192 390.7 192 320C192 249.3 249.3 192 320 192C347.9 192 373.7 200.9 394.7 216.1C400.4 211.1 407.8 208 416 208C433.7 208 448 222.3 448 240L448 352C448 369.7 462.3 384 480 384C497.7 384 512 369.7 512 352L512 320C512 214 426 128 320 128zM384 320C384 284.7 355.3 256 320 256C284.7 256 256 284.7 256 320C256 355.3 284.7 384 320 384C355.3 384 384 355.3 384 320z\"/></svg>`,\n\t\tSpecs: Form{\n\t\t\tElmnts: []FormElement{\n\t\t\t\t{\n\t\t\t\t\tName: \"path\",\n\t\t\t\t\tType: \"text\",\n\t\t\t\t\tDefault: \"/**\",\n\t\t\t\t\tPlaceholder: \"Default: /**\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tOrder: 4,\n\t}\n}\n\nfunc (this *MentionTrigger) Init() (chan ITriggerEvent, error) {\n\treturn mention_event, nil\n}\n\nfunc processMention(params map[string]string) {\n\tif dir := Hooks.Get.DirectoryService(); dir != nil {\n\t\tquery := strings.ReplaceAll(params[\"mention\"], \".\", \" \")\n\t\tif users, err := dir.Search(query); err == nil {\n\t\t\tfor _, u := range users {\n\t\t\t\tif strings.EqualFold(u.Name, query) {\n\t\t\t\t\tparams[\"email\"] = u.Email\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif err := TriggerEvents(mention_event, mention_name, mentionCallback(params)); err != nil {\n\t\tLog.Error(\"[workflow] trigger=mention step=triggerEvents err=%s\", err.Error())\n\t}\n}\n\nfunc mentionCallback(out map[string]string) func(Workflow) (map[string]string, bool) {\n\treturn func(w Workflow) (map[string]string, bool) {\n\t\tpath := w.Trigger.Params[\"path\"]\n\t\tif path != \"\" && !GlobMatch(path, out[\"path\"]) {\n\t\t\treturn out, false\n\t\t}\n\t\treturn out, true\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_description/assets/sidebar.diff",
    "content": "diff --git a/public/assets/components/sidebar.js b/public/assets/components/sidebar.js\nindex 268aa4eb..e7b93142 100644\n--- a/public/assets/components/sidebar.js\n+++ b/public/assets/components/sidebar.js\n@@ -52,1 +52,5 @@ export default async function ctrlSidebar(render, {}) {\n\n+    if (!$sidebar.querySelector(`[data-bind=\"description\"]`)) qs($sidebar, \".component_sidebar > div\").appendChild(createElement(`<div data-bind=\"description\"></div>`));\n+    import(location.origin + \"/plg_widget_description/sidebar_description.js\")\n+        .then((module) => module.default(createRender(qs($sidebar, `[data-bind=\"description\"]`)), { path }))\n+        .catch((err) => console.log(err));\n"
  },
  {
    "path": "server/plugin/plg_widget_description/assets/sidebar_description.js",
    "content": "import { createElement } from \"../lib/skeleton/index.js\";\nimport { qs } from \"../lib/dom.js\";\nimport rxjs, { effect, applyMutation, preventDefault } from \"../../lib/rx.js\";\nimport ajax from \"../../lib/ajax.js\";\nimport { forwardURLParams } from \"../../lib/path.js\";\nimport t from \"../locales/index.js\";\n\nconst ICONS = {\n    DEFAULT: `<img src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIHN0eWxlPSJzdHJva2U6IzU3NTk1YSIgZD0iTTEzIDJINmEyIDIgMCAwIDAtMiAydjE2YTIgMiAwIDAgMCAyIDJoMTJhMiAyIDAgMCAwIDItMlY5eiIvPjxwb2x5bGluZSBzdHlsZT0ic3Ryb2tlOiM1NzU5NWEiIHBvaW50cz0iMTMgMiAxMyA5IDIwIDkiLz48L3N2Zz4=\" alt=\"description\">`,\n    LOADING: `<component-icon name=\"loading\"></component-icon>`,\n};\n\nexport default async function(render, { path }) {\n    const $page = createElement(`\n        <div>\n            <h3 class=\"no-select\">\n                ${ICONS.DEFAULT}\n                <span>${t(\"Description\")}</span>\n            </h3>\n            <div data-bind=\"content\">\n                <textarea class=\"placeholder\" name=\"text\"></textarea>\n            </div>\n            <style>${CSS}</style>\n        </div>\n    `);\n    render($page);\n\n    const $DOM = {\n        textarea: qs($page, \"textarea\"),\n        h3: qs($page, \"h3\"),\n        icon_default: () => createElement(ICONS.DEFAULT),\n        icon_loading: () => createElement(ICONS.LOADING),\n    };\n\n    effect(getDescription(path).pipe(\n        rxjs.tap(({ text }) => $DOM.textarea.value = text),\n    ));\n\n    effect(rxjs.fromEvent($DOM.textarea, \"input\").pipe(\n        rxjs.map((e) => e.target.value),\n        rxjs.debounceTime(200),\n        rxjs.tap(() => $DOM.h3.replaceChild($DOM.icon_loading(), $DOM.h3.firstElementChild)),\n        rxjs.mergeMap((text) => updateDescription({ path, text })),\n        rxjs.tap(() => $DOM.h3.replaceChild($DOM.icon_default(), $DOM.h3.firstElementChild)),\n    ));\n}\n\nconst getDescription = (path) => ajax({\n    url: forwardURLParams(\"api/plg_widget_description/description?path=\" + encodeURIComponent(path), [\"share\"]),\n    responseType: \"json\"\n}).pipe(\n    rxjs.map(({ responseJSON }) => responseJSON.result || { path, text: \"\" }),\n);\n\nconst updateDescription = ({ path, ...body }) => ajax({\n    url: forwardURLParams(\"api/plg_widget_description/description?path=\" + encodeURIComponent(path), [\"share\"]),\n    method: \"PUT\",\n    body,\n});\n\nconst CSS = `\n[data-bind=\"description\"] [data-bind=\"content\"] {\n    padding-left: 5px;\n    padding-right: 2px;\n}\n[data-bind=\"description\"] [data-bind=\"content\"] textarea {\n    max-width: 100%;\n    min-width: 100%;\n    font-size: 0.9rem;\n}`;\n"
  },
  {
    "path": "server/plugin/plg_widget_description/config.go",
    "content": "package plg_widget_description\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tPluginEnable()\n\t})\n}\n\nvar PluginEnable = func() bool {\n\treturn Config.Get(\"features.description.enable\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"enable\"\n\t\tf.Type = \"enable\"\n\t\tf.Target = []string{}\n\t\tf.Default = false\n\t\treturn f\n\t}).Bool()\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_description/db.go",
    "content": "package plg_widget_description\n\nimport (\n\t\"os\"\n\t\"database/sql\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar db *sql.DB\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tif err := initDB(); err != nil {\n\t\t\tLog.Error(\"plg_widget_description::db err=cannot_init msg=%s\", err.Error())\n\t\t\tos.Exit(1)\n\t\t}\n\t})\n}\n\nfunc initDB() error {\n\tvar err error\n\tdb, err = sql.Open(\"sqlite3\", GetAbsolutePath(DB_PATH, \"descriptions.db\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = db.Exec(`\n\t\tCREATE TABLE IF NOT EXISTS descriptions (\n\t\t\tbackend TEXT NOT NULL,\n\t\t\tpath TEXT NOT NULL,\n\t\t\tauthor TEXT NOT NULL,\n\t\t\ttext TEXT NOT NULL,\n\t\t\tupdated_at INTEGER NOT NULL,\n\t\t\tPRIMARY KEY (backend, path)\n\t\t);\n\t`)\n\treturn err\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_description/handler.go",
    "content": "package plg_widget_description\n\nimport (\n\t\"database/sql\"\n\t\"net/http\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/ctrl\"\n)\n\nfunc get(ctx *App, w http.ResponseWriter, r *http.Request) {\n\tpath, err := PathBuilder(ctx, r.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\n\tvar d Description\n\tif err = db.QueryRowContext(ctx.Context, `\n\t\tSELECT author, text, updated_at\n\t\tFROM descriptions\n\t\tWHERE backend = ? AND path = ?\n\t`, GenerateID(ctx.Session), path).Scan(&d.Author, &d.Text, &d.UpdatedAt); err != nil && err != sql.ErrNoRows {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tSendSuccessResult(w, d)\n}\n\nfunc update(ctx *App, w http.ResponseWriter, r *http.Request) {\n\tpath, err := PathBuilder(ctx, r.URL.Query().Get(\"path\"))\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\ttext, ok := ctx.Body[\"text\"].(string)\n\tif !ok {\n\t\tSendErrorResult(w, NewError(\"Invalid parameters\", 400))\n\t\treturn\n\t}\n\tif text == \"\" {\n\t\t_, err = db.ExecContext(ctx.Context, `\n\t\t\tDELETE FROM descriptions WHERE backend = ? AND path = ?\n\t\t`, GenerateID(ctx.Session), path)\n\t} else {\n\t\t_, err = db.ExecContext(ctx.Context, `\n\t\t\tINSERT INTO descriptions(backend, path, author, text, updated_at)\n\t\t\tVALUES(?, ?, ?, ?, ?)\n\t\t\tON CONFLICT(backend, path) DO UPDATE SET\n\t\t\t\tauthor = excluded.author,\n\t\t\t\ttext = excluded.text,\n\t\t\t\tupdated_at = excluded.updated_at\n\t\t`, GenerateID(ctx.Session), path, getUser(ctx.Session), text, time.Now().Unix())\n\t}\n\tif err != nil {\n\t\tSendErrorResult(w, err)\n\t\treturn\n\t}\n\tSendSuccessResult(w, nil)\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_description/index.go",
    "content": "package plg_widget_description\n\nimport (\n\t_ \"embed\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/middleware\"\n\n\t\"github.com/gorilla/mux\"\n)\n\n//go:embed assets/sidebar_description.js\nvar CTRLJS []byte\n\n//go:embed assets/sidebar.diff\nvar PATCH []byte\n\nfunc init() {\n\tHooks.Register.HttpEndpoint(func(r *mux.Router) error {\n\t\tr.HandleFunc(\"/api/plg_widget_description/description\", NewMiddlewareChain(get, []Middleware{ApiHeaders, SecureHeaders, PluginGuard, SessionStart})).Methods(\"GET\")\n\t\tr.HandleFunc(\"/api/plg_widget_description/description\", NewMiddlewareChain(update, []Middleware{ApiHeaders, SecureHeaders, PluginGuard, SessionStart, BodyParser})).Methods(\"PUT\")\n\n\t\tr.HandleFunc(WithBase(\"/plg_widget_description/sidebar_description.js\"), func(res http.ResponseWriter, req *http.Request) {\n\t\t\thttp.Redirect(res, req, WithBase(\"/assets/\"+BUILD_REF+\"/components/sidebar_description.js\"), http.StatusSeeOther)\n\t\t})\n\t\tr.HandleFunc(WithBase(\"/assets/\"+BUILD_REF+\"/components/sidebar_description.js\"), func(res http.ResponseWriter, req *http.Request) {\n\t\t\tres.Header().Set(\"Content-Type\", \"application/javascript\")\n\t\t\tres.Write(CTRLJS)\n\t\t}).Methods(\"GET\")\n\t\treturn nil\n\t})\n\n\tHooks.Register.OnConfig(func() {\n\t\tif PluginEnable() {\n\t\t\tHooks.Register.StaticPatch(PATCH, WithID(\"plg_widget_description\"))\n\t\t} else {\n\t\t\tHooks.Register.StaticPatch([]byte(\"\"), WithID(\"plg_widget_description\"))\n\t\t}\n\t})\n}\n\nfunc PluginGuard(fn HandlerFunc) HandlerFunc {\n\treturn func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\tif !PluginEnable() {\n\t\t\tSendErrorResult(res, ErrNotAllowed)\n\t\t\treturn\n\t\t}\n\t\tfn(ctx, res, req)\n\t}\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_description/type.go",
    "content": "package plg_widget_description\n\ntype Description struct {\n\tAuthor    string `json:\"author\"`\n\tText      string `json:\"text\"`\n\tUpdatedAt int64  `json:\"updated_at\"`\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_description/utils.go",
    "content": "package plg_widget_description\n\nfunc getUser(session map[string]string) string {\n\tif session[\"user\"] != \"\" {\n\t\treturn session[\"user\"]\n\t}\n\treturn \"unknown\"\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_favourite/assets/favourite.diff",
    "content": "diff --git a/public/assets/components/sidebar.js b/public/assets/components/sidebar.js\nindex 268aa4eb..e7b93142 100644\n--- a/public/assets/components/sidebar.js\n+++ b/public/assets/components/sidebar.js\n@@ -52,1 +52,5 @@ export default async function ctrlSidebar(render, {}) {\n\n+    if (!$sidebar.querySelector(`[data-bind=\"favourites\"]`)) qs($sidebar, `[data-bind=\"your-tags\"]`).before(createElement(`<div data-bind=\"favourites\"></div>`));\n+    import(new URL(\"./sidebar_favourite.js\", import.meta.url).toString())\n+        .then((module) => module.default(createRender(qs($sidebar, `[data-bind=\"favourites\"]`)), { path }))\n+        .catch((err) => console.log(err));\ndiff --git a/public/assets/pages/filespage/ctrl_submenu.js b/public/assets/pages/filespage/ctrl_submenu.js\nindex cdc4015c..5ec564b5 100644\n--- a/public/assets/pages/filespage/ctrl_submenu.js\n+++ b/public/assets/pages/filespage/ctrl_submenu.js\n@@ -125,2 +125,12 @@ function componentLeft(render, { $scroll, getSelectionLength$ }) {\n         `))),\n+        rxjs.map(($buttons) => {\n+            $buttons.appendChild(Object.assign(createElement(`<button>Pin</button>`), {\n+                onclick: async () => {\n+                    const url = new URL(\"../../components/sidebar_favourite.js\", import.meta.url).toString();\n+                    const { toggleFavourite } = await import(url);\n+                    toggleFavourite(expandSelection()[0].path)\n+                },\n+            }));\n+            return $buttons;\n+        }),\n         rxjs.tap(($buttons) => animate($buttons, { time: 100, keyframes: slideYIn(5) })),\n"
  },
  {
    "path": "server/plugin/plg_widget_favourite/assets/sidebar_favourite.js",
    "content": "import { createElement } from \"../lib/skeleton/index.js\";\nimport { qs, safe } from \"../lib/dom.js\";\nimport rxjs, { effect, applyMutation } from \"../../lib/rx.js\";\nimport { basename } from \"../lib/path.js\";\nimport { getSession } from \"../model/session.js\";\nimport { onLogout } from \"../pages/ctrl_logout.js\";\nimport t from \"../locales/index.js\";\n\nconst refresh$ = new rxjs.Subject();\nconst currentShare = () => new window.URL(location.href).searchParams.get(\"share\") || \"\";\n\nlet backendID = \"\";\nconst initBackendID = async () => {\n    if (backendID) return;\n    backendID = await getSession().toPromise().then(({ backendID }) => backendID);\n    onLogout(() => backendID = \"\");\n};\n\nexport default async function(render, { path }) {\n    const $page = createElement(`\n        <div id=\"sidebar_favorite\">\n            <h3 class=\"no-select\">\n                <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/></svg>\n                <span>${t(\"Favourites\")}</span>\n            </h3>\n            <ul data-bind=\"content\"></ul>\n            <style>${CSS}</style>\n        </div>\n    `);\n    render($page);\n\n    effect(rxjs.merge(refresh$, rxjs.of(null)).pipe(\n        rxjs.mergeMap(() => initBackendID()),\n        rxjs.mergeMap(() => listFavourites(path)),\n        rxjs.map((favourites) => {\n            if (favourites.length === 0) return createElement(`<div class=\"placeholder center no-select\">∅</div>`);\n            const $list = document.createDocumentFragment();\n            for (let i=0; i<favourites.length; i++) {\n                const path = favourites[i].path;\n                const type = path.endsWith(\"/\") ? \"directory\" : \"file\"\n                const href = (type === \"directory\" ? \"/files\" : \"/view\") + path;\n                const ICON = {\n                    \"FILE\": \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIHN0eWxlPSJzdHJva2U6IzU3NTk1YTtmaWxsOiAjNTc1OTVhOyIgZD0iTTEzIDJINmEyIDIgMCAwIDAtMiAydjE2YTIgMiAwIDAgMCAyIDJoMTJhMiAyIDAgMCAwIDItMlY5eiIvPjwvc3ZnPgo=\",\n                    \"FOLDER\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgYXJpYS1oaWRkZW49InRydWUiCiAgIGZvY3VzYWJsZT0iZmFsc2UiCiAgIGNsYXNzPSJvY3RpY29uIG9jdGljb24tZmlsZS1kaXJlY3RvcnktZmlsbCIKICAgdmlld0JveD0iMCAwIDE2IDE2IgogICB3aWR0aD0iMTYiCiAgIGhlaWdodD0iMTYiCiAgIGZpbGw9ImN1cnJlbnRDb2xvciIKICAgc3R5bGU9ImRpc3BsYXk6IGlubGluZS1ibG9jazsgdXNlci1zZWxlY3Q6IG5vbmU7IHZlcnRpY2FsLWFsaWduOiB0ZXh0LWJvdHRvbTsgb3ZlcmZsb3c6IHZpc2libGU7IgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmcxNTgiCiAgIHNvZGlwb2RpOmRvY25hbWU9ImdpdGh1YmZvbGRlci5zdmciCiAgIGlua3NjYXBlOnZlcnNpb249IjEuMi4yIChiMGE4NDg2NTQxLCAyMDIyLTEyLTAxKSIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMTYyIiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0ibmFtZWR2aWV3MTYwIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzAwMDAwMCIKICAgICBib3JkZXJvcGFjaXR5PSIwLjI1IgogICAgIGlua3NjYXBlOnNob3dwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZWNoZWNrZXJib2FyZD0iMCIKICAgICBpbmtzY2FwZTpkZXNrY29sb3I9IiNkMWQxZDEiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGlua3NjYXBlOnpvb209IjcxLjYyNSIKICAgICBpbmtzY2FwZTpjeD0iNy44MTE1MTgzIgogICAgIGlua3NjYXBlOmN5PSI4IgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMjAzNiIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMzk3IgogICAgIGlua3NjYXBlOndpbmRvdy14PSI3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSIzNCIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9InN2ZzE1OCIgLz4KICA8cGF0aAogICAgIGQ9Ik0xLjc1IDFBMS43NSAxLjc1IDAgMCAwIDAgMi43NXYxMC41QzAgMTQuMjE2Ljc4NCAxNSAxLjc1IDE1aDEyLjVBMS43NSAxLjc1IDAgMCAwIDE2IDEzLjI1di04LjVBMS43NSAxLjc1IDAgMCAwIDE0LjI1IDNINy41YS4yNS4yNSAwIDAgMS0uMi0uMWwtLjktMS4yQzYuMDcgMS4yNiA1LjU1IDEgNSAxSDEuNzVaIgogICAgIGlkPSJwYXRoMTU2IgogICAgIHN0eWxlPSJmaWxsOiM1NzU5NWE7ZmlsbC1vcGFjaXR5OjEiIC8+Cjwvc3ZnPgo=\",\n                };\n                const withRemove = ($el) => {\n                    qs($el, \"svg\").onclick = (e) => {\n                        e.preventDefault();\n                        e.stopPropagation();\n                        $el.closest(\"li\").remove();\n                        removeFavourite(path);\n                    };\n                    return $el;\n                };\n                $list.appendChild(withRemove(createElement(`\n                    <li data-path=\"${safe(path)}\" title=\"${safe(path)}\" class=\"no-select\">\n                        <a data-link href=\"${safe(href)}\" draggable=\"false\" aria-selected=\"false\">\n                            <div class=\"flex ellipsis\">\n                                <img class=\"component_icon\" alt=\"${type}\" src=\"${type === \"directory\" ? ICON.FOLDER : ICON.FILE}\">\n                                <span>${safe(basename(path.replace(new RegExp(\"/$\"), \"\")))}</span>\n                            </div>\n                            <svg class=\"component_icon\" draggable=\"false\" alt=\"close\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n                                <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n                                <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n                            </svg>\n                        </a>\n                    </li>\n                `)));\n            }\n            return $list;\n        }),\n        applyMutation(qs($page, `[data-bind=\"content\"]`), \"replaceChildren\"),\n    ));\n}\n\nexport async function toggleFavourite(path) {\n    return db().then((db) => new Promise((resolve, reject) => {\n        const tx = db.transaction(\"favourites\", \"readonly\");\n        const store = tx.objectStore(\"favourites\");\n        const req = store.get([backendID, currentShare(), path]);\n        req.onsuccess = () => resolve(req.result);\n        req.onerror = () => reject(req.error);\n    })).then((existing) => existing ? removeFavourite(path) : addFavourite(path)).then(() => refresh$.next(null));\n}\n\nfunction listFavourites(path) {\n    return db().then((db) => new Promise((resolve, reject) => {\n        const tx = db.transaction(\"favourites\", \"readonly\");\n        const store = tx.objectStore(\"favourites\");\n        const req = store.index(\"parent\").getAll(IDBKeyRange.bound(\n            [backendID, currentShare(), path],\n            [backendID, currentShare(), path + \"\\uffff\"],\n        ));\n        req.onsuccess = () => resolve(req.result);\n        req.onerror = () => reject(req.error);\n    }));\n}\n\nfunction addFavourite(path) {\n    return db().then((db) => new Promise((resolve, reject) => {\n        const tx = db.transaction(\"favourites\", \"readwrite\");\n        const store = tx.objectStore(\"favourites\");\n        store.put({ backend: backendID, path, share: currentShare(), parent: path.replace(new RegExp(\"[^/]*\\/?$\"), \"\") });\n        tx.oncomplete = () => resolve();\n        tx.onerror = () => reject(tx.error);\n    }));\n}\n\nfunction removeFavourite(path) {\n    return db().then((db) => new Promise((resolve, reject) => {\n        const tx = db.transaction(\"favourites\", \"readwrite\");\n        const store = tx.objectStore(\"favourites\");\n        store.delete([backendID, currentShare(), path]);\n        tx.oncomplete = () => resolve();\n        tx.onerror = () => reject(tx.error);\n    }));\n}\n\nconst db = () => {\n    let _db = null;\n    return new Promise((resolve, reject) => {\n        if (_db) return Promise.resolve(_db);\n        const req = indexedDB.open(\"favourites\", 1);\n        req.onupgradeneeded = () => {\n            const db = req.result;\n            const store = db.createObjectStore(\"favourites\", { keyPath: [\"backend\", \"share\", \"path\"] });\n            store.createIndex(\"parent\", [\"backend\", \"share\", \"parent\"]);\n        };\n        req.onsuccess = () => { _db = req.result; resolve(_db); };\n        req.onerror = () => reject(req.error);\n    });\n};\n\nconst CSS = `\n#sidebar_favorite svg {\n    position: relative;\n    bottom: 3px;\n}\n#sidebar_favorite ul li a {\n    justify-content: space-between;\n}\n#sidebar_favorite ul li a > div {\n    margin-left: -5px;\n}\n#sidebar_favorite ul li a svg {\n    display: none;\n    background: rgba(255, 255, 255, 0.6);\n    align-self: center;\n    width: 13px;\n    height: 13px;\n    border-radius: 50%;\n    padding: 4px;\n    position: initial;\n}\n#sidebar_favorite ul li a:hover svg {\n    display: block;\n}\n`;\n"
  },
  {
    "path": "server/plugin/plg_widget_favourite/config.go",
    "content": "package plg_widget_favourite\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tPluginEnable()\n\t})\n}\n\nvar PluginEnable = func() bool {\n\treturn Config.Get(\"features.favourite.enable\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"enable\"\n\t\tf.Type = \"enable\"\n\t\tf.Target = []string{}\n\t\tf.Default = false\n\t\treturn f\n\t}).Bool()\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_favourite/index.go",
    "content": "package plg_widget_favourite\n\nimport (\n\t_ \"embed\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"github.com/gorilla/mux\"\n)\n\n//go:embed assets/favourite.diff\nvar PATCH []byte\n\n//go:embed assets/sidebar_favourite.js\nvar JS []byte\n\nfunc init() {\n\tHooks.Register.HttpEndpoint(func(r *mux.Router) error {\n\t\tr.HandleFunc(WithBase(\"/assets/\"+BUILD_REF+\"/components/sidebar_favourite.js\"), func(res http.ResponseWriter, req *http.Request) {\n\t\t\tres.Header().Set(\"Content-Type\", \"application/javascript\")\n\t\t\tres.Write(JS)\n\t\t}).Methods(\"GET\")\n\t\treturn nil\n\t})\n\tHooks.Register.OnConfig(func() {\n\t\tif PluginEnable() {\n\t\t\tHooks.Register.StaticPatch(PATCH, WithID(\"plg_widget_favourite\"))\n\t\t} else {\n\t\t\tHooks.Register.StaticPatch([]byte(\"\"), WithID(\"plg_widget_favourite\"))\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_pgp/assets/pgp.diff",
    "content": "diff --git a/public/assets/pages/viewerpage/mimetype.js b/public/assets/pages/viewerpage/mimetype.js\nindex 0adb0934..d002a70e 100644\n--- a/public/assets/pages/viewerpage/mimetype.js\n+++ b/public/assets/pages/viewerpage/mimetype.js\n@@ -3,1 +3,2 @@ import { get as getPlugin } from \"../../model/plugin.js\";\n export function opener(file = \"\", mimes) {\n+    file = file.replace(new RegExp(\".gpg$\"), \"\");\ndiff --git a/public/assets/pages/viewerpage/application_image.js b/public/assets/pages/viewerpage/application_image.js\nindex 1227d426..b82330aa 100644\n--- a/public/assets/pages/viewerpage/application_image.js\n+++ b/public/assets/pages/viewerpage/application_image.js\n@@ -65,1 +65,3 @@ export default function(render, { getFilename, getDownloadUrl, mime, hasMenubar\n             let src = `${getDownloadUrl()}&size=${window.innerWidth}`;\n+            const m = await import(\"/plg_widget_pgp/pgp.js\");\n+            if (m.isPGP(getFilename())) src = await m.decode(src);\ndiff --git a/public/assets/pages/viewerpage/model_files.js b/public/assets/pages/viewerpage/model_files.js\nindex 743914ab..4f20546d 100644\n--- a/public/assets/pages/viewerpage/model_files.js\n+++ b/public/assets/pages/viewerpage/model_files.js\n@@ -22,4 +22,4 @@ export const options = () => ajax({\n-export const cat = (url) => ajax({\n-    url: forwardURLParams(url, [\"share\"]),\n-    method: \"GET\",\n-}).pipe(\n+const { isPGP, decode, encode } = await import(location.origin + \"/plg_widget_pgp/pgp.js\");\n+export const cat = (url) => rxjs.of(forwardURLParams(url, [\"share\"])).pipe(\n+    rxjs.mergeMap((url) => isPGP(getCurrentPath()) ? decode(url) : rxjs.of(url)),\n+    rxjs.mergeMap((url) => ajax({ url, method: \"GET\" })),\n@@ -29,5 +29,3 @@ export const cat = (url) => ajax({\n-export const save = (content) => ajax({\n-    url: forwardURLParams(\"api/files/cat?path=\" + encodeURIComponent(getCurrentPath()), [\"share\"]),\n-    method: \"POST\",\n-    body: content,\n-});\n+export const save = (content) => (isPGP(getCurrentPath()) ? rxjs.from(encode(content)) : rxjs.of(content)).pipe(\n+    rxjs.mergeMap((body) => ajax({ body, method: \"POST\", url: forwardURLParams(\"api/files/cat?path=\" + encodeURIComponent(getCurrentPath()), [\"share\"]) })),\n+);\n"
  },
  {
    "path": "server/plugin/plg_widget_pgp/assets/pgp.js",
    "content": "import { createElement, onDestroy } from \"../../lib/skeleton/index.js\";\nimport { createModal, MODAL_RIGHT_BUTTON } from \"../../components/modal.js\";\nimport notification from \"../../components/notification.js\";\nimport { qs, safe } from \"../../lib/dom.js\";\n\nimport { getFilename } from \"./common.js\";\n\nconst openpgp = (window.crypto && window.crypto.subtle) ? await import(\"https://unpkg.com/openpgp@6.3.0/dist/openpgp.min.mjs\") : null;\n\nlet KEY = null;\nonDestroy(() => KEY = null);\n\nexport function isPGP(path) {\n    if (!openpgp) return false;\n    else if (new RegExp(\".gpg$\").test(path)) return true;\n    else if (new RegExp(\".pgp$\").test(path)) return true;\n    return false;\n}\n\nexport async function decode(src) {\n    const $page = createElement(`\n        <div class=\"component_pgp\">\n            <input type=\"file\" accept=\".key\" />\n            <style>${CSS}</style>\n        </div>\n    `);\n    const encrypted = await new Promise(async (done, error) => {\n        try {\n            const res = await fetch(src);\n            done(await res.arrayBuffer());\n        } catch (err) {\n            error(err);\n        }\n    });\n    if (encrypted.byteLength === 0) {\n        return URL.createObjectURL(new Blob([encrypted]));\n    }\n    return new Promise((done) => createModal({ withButtonsRight: \"decrypt\", withButtonsLeft: \"cancel\" })($page, async (n) => {\n        if (n !== MODAL_RIGHT_BUTTON) return done(src);\n        try {\n            const file = qs($page, \"input\").files?.[0];\n            if (!file) throw new Error(\"Missing Key\");\n            const privateKey = await openpgp.readPrivateKey({\n                armoredKey: await file.text(),\n            });\n            KEY = privateKey;\n            const { data } = await openpgp.decrypt({\n                message: await openpgp.readMessage({\n                    binaryMessage: new Uint8Array(encrypted),\n                }),\n                decryptionKeys: privateKey,\n                format: \"binary\",\n            });\n            done(URL.createObjectURL(new Blob([data])));\n        } catch (err) {\n            notification.error(err);\n            done(src);\n        } finally {\n            $page.remove();\n        }\n    }));\n}\n\nexport async function encode(data) {\n    const $page = createElement(`\n        <div class=\"component_pgp\">\n            <input type=\"file\" accept=\".key\" />\n            <style>${CSS}</style>\n        </div>\n    `);\n    if (KEY === null) KEY = await new Promise((done, error) => {\n        createModal({ withButtonsRight: \"encrypt\", withButtonsLeft: \"cancel\" })($page, async (n) => {\n            if (n !== MODAL_RIGHT_BUTTON) return error(new Error(\"missing key\"));\n            try {\n                const file = qs($page, \"input\").files?.[0];\n                if (!file) return done(null);\n                const text = await file.text();\n                done(await openpgp.readPrivateKey({\n                    armoredKey: text,\n                }));\n            } catch (err) {\n                if (err.message !== \"Misformed armored text\") {\n                    notification.error(err);\n                    error(err);\n                    return\n                }\n                done(null);\n            } finally {\n                $page.remove();\n            }\n        });\n    });\n    if (KEY === null) try {\n        const { privateKey, publicKey } = await openpgp.generateKey({\n            type: \"rsa\",\n            rsaBits: 2048,\n            userIDs: [{ name: \"anonymous\", email: \"anomymous@local\" }],\n            format: \"armored\",\n        });\n        KEY = await openpgp.readPrivateKey({\n            armoredKey: privateKey,\n        });\n        const $link = document.body.appendChild(Object.assign(document.createElement(\"a\"), {\n            href: URL.createObjectURL(new Blob([privateKey], { type: \"application/pgp-keys\" })),\n            download: getFilename() + \".key\",\n        }));\n        $link.click();\n        $link.remove();\n    } catch (err) {\n        return error(err);\n    }\n\n    const message = await openpgp.createMessage({\n        text: data,\n    });\n    const encrypted = await openpgp.encrypt({\n        message,\n        encryptionKeys: KEY.toPublic(),\n        format: \"binary\",\n        config: { minRSABits: 1024 },\n    });\n    return new Blob([encrypted]);\n}\n\nconst CSS = `\n.component_pgp textarea {\n    width: 100%;\n    border: none;\n    height: 200px;\n    font-size: 0.5rem;\n}\n`;\n"
  },
  {
    "path": "server/plugin/plg_widget_pgp/index.go",
    "content": "package plg_widget_chat\n\nimport (\n\t_ \"embed\"\n\t\"net/http\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\n\t\"github.com/gorilla/mux\"\n)\n\n//go:embed assets/pgp.js\nvar CTRLJS []byte\n\n//go:embed assets/pgp.diff\nvar PATCH []byte\n\nfunc init() {\n\tHooks.Register.HttpEndpoint(func(r *mux.Router) error {\n\t\tr.HandleFunc(WithBase(\"/plg_widget_pgp/pgp.js\"), func(res http.ResponseWriter, req *http.Request) {\n\t\t\thttp.Redirect(res, req, WithBase(\"/assets/\"+BUILD_REF+\"/pages/viewerpage/pgp.js\"), http.StatusSeeOther)\n\t\t})\n\t\tr.HandleFunc(WithBase(\"/assets/\"+BUILD_REF+\"/pages/viewerpage/pgp.js\"), func(res http.ResponseWriter, req *http.Request) {\n\t\t\tres.Header().Set(\"Content-Type\", \"application/javascript\")\n\t\t\tres.Write(CTRLJS)\n\t\t}).Methods(\"GET\")\n\t\treturn nil\n\t})\n\tHooks.Register.StaticPatch(PATCH)\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_recent/ai.go",
    "content": "package plg_widget_recent\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc aiFilterRecent(ctx context.Context, query string, files []os.FileInfo) ([]os.FileInfo, error) {\n\tendpoint := PluginEndpoint()\n\tmodel := PluginModel()\n\tif endpoint == \"\" || model == \"\" {\n\t\treturn files, nil\n\t}\n\n\tentries := make([]string, 0, len(files))\n\tfor i, f := range files {\n\t\tfile := f.(File)\n\t\taccessed := time.UnixMilli(file.FTime).Format(\"2006-01-02 15:04\")\n\t\tentryType := \"file\"\n\t\tif strings.HasSuffix(file.FPath, \"/\") {\n\t\t\tentryType = \"directory\"\n\t\t}\n\t\tentries = append(entries, fmt.Sprintf(\"%d: path=%s type=%s size=%d last_accessed=%s\", i, file.FPath, entryType, file.FSize, accessed))\n\t}\n\n\tbody, err := json.Marshal(map[string]any{\n\t\t\"model\": model,\n\t\t\"messages\": []map[string]string{\n\t\t\t{\n\t\t\t\t\"role\": \"system\",\n\t\t\t\t\"content\": fmt.Sprintf(`You filter recently accessed files and folders based on a search query.\nToday is %s.\nEach entry has: index, path, type (file or directory), size (bytes), last_accessed (YYYY-MM-DD HH:MM).\n\nApply these rules strictly:\n- Temporal queries (\"2 hours ago\", \"3 days ago\", \"last week\", \"today\", \"recent\"): include ONLY entries whose last_accessed falls within that date range. Exclude everything else.\n- Type queries (\"files\", \"folders\", \"images\", \"documents\"): include ONLY entries matching that type or extension.\n- Name queries: match against path.\n- Size queries (\"big\", \"small\", \"large\"): filter by size.\n- Combined queries: apply all matching rules together.\n\nOutput ONLY comma-separated indices, e.g.: 3,0,7,1\nNo brackets, no spaces, no explanation. Most relevant first. Omit irrelevant results entirely.`, time.Now().Format(\"Monday January 2, 2006\")),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"role\":    \"user\",\n\t\t\t\t\"content\": fmt.Sprintf(\"Query: %s\\n\\nFiles:\\n%s\", query, strings.Join(entries, \"\\n\")),\n\t\t\t},\n\t\t},\n\t\t\"temperature\": 0,\n\t\t\"max_tokens\":  500,\n\t})\n\tif err != nil {\n\t\treturn files, nil\n\t}\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))\n\tif err != nil {\n\t\treturn files, nil\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tif apiKey := PluginAPIKey(); apiKey != \"\" {\n\t\treq.Header.Set(\"Authorization\", \"Bearer \"+apiKey)\n\t}\n\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\treturn files, nil\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn files, nil\n\t}\n\n\tvar result struct {\n\t\tChoices []struct {\n\t\t\tMessage struct {\n\t\t\t\tContent string `json:\"content\"`\n\t\t\t} `json:\"message\"`\n\t\t} `json:\"choices\"`\n\t}\n\tif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {\n\t\treturn files, nil\n\t} else if len(result.Choices) == 0 {\n\t\treturn files, nil\n\t}\n\tfiltered := make([]os.FileInfo, 0)\n\tfor _, part := range strings.Split(result.Choices[0].Message.Content, \",\") {\n\t\tidx, err := strconv.Atoi(strings.TrimSpace(part))\n\t\tif err != nil || idx < 0 || idx >= len(files) {\n\t\t\tcontinue\n\t\t}\n\t\tfiltered = append(filtered, files[idx])\n\t}\n\treturn filtered, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_recent/config.go",
    "content": "package plg_widget_recent\n\nimport (\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tPluginEnable()\n\t\tPluginFolderName()\n\t\tPluginEnableAI()\n\t\tPluginEndpoint()\n\t\tPluginModel()\n\t\tPluginAPIKey()\n\t})\n}\n\nvar PluginEnable = func() bool {\n\treturn Config.Get(\"features.recent.enable\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Name = \"enable\"\n\t\tf.Type = \"enable\"\n\t\tf.Target = []string{\n\t\t\t\"recent_folder_name\",\n\t\t\t\"recent_enable_ai\",\n\t\t\t\"recent_model_address\",\n\t\t\t\"recent_model_name\",\n\t\t\t\"recent_api_key\",\n\t\t}\n\t\tf.Default = false\n\t\treturn f\n\t}).Bool()\n}\n\nvar PluginFolderName = func() string {\n\treturn Config.Get(\"features.recent.folder_name\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Id = \"recent_folder_name\"\n\t\tf.Name = \"folder_name\"\n\t\tf.Type = \"text\"\n\t\tf.Description = \"Name of the virtual folder for recent files\"\n\t\tf.Default = \"Recent\"\n\t\tf.Placeholder = \"Recent\"\n\t\treturn f\n\t}).String()\n}\n\nvar PluginEnableAI = func() bool {\n\treturn Config.Get(\"features.recent.enable_ai\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Id = \"recent_enable_ai\"\n\t\tf.Name = \"enable_ai\"\n\t\tf.Type = \"boolean\"\n\t\tf.Description = \"Use AI to power search within recent files\"\n\t\tf.Default = false\n\t\treturn f\n\t}).Bool()\n}\n\nvar PluginEndpoint = func() string {\n\treturn Config.Get(\"features.recent.model_address\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Id = \"recent_model_address\"\n\t\tf.Name = \"model_address\"\n\t\tf.Type = \"text\"\n\t\tf.Default = \"https://api.openai.com/v1/chat/completions\"\n\t\tf.Placeholder = \"default: https://api.openai.com/v1/chat/completions\"\n\t\treturn f\n\t}).String()\n}\n\nvar PluginModel = func() string {\n\treturn Config.Get(\"features.recent.model_name\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Id = \"recent_model_name\"\n\t\tf.Name = \"model_name\"\n\t\tf.Type = \"text\"\n\t\tf.Default = \"gpt-4o-mini\"\n\t\tf.Placeholder = \"default: gpt-4o-mini\"\n\t\treturn f\n\t}).String()\n}\n\nvar PluginAPIKey = func() string {\n\treturn Config.Get(\"features.recent.api_key\").Schema(func(f *FormElement) *FormElement {\n\t\tif f == nil {\n\t\t\tf = &FormElement{}\n\t\t}\n\t\tf.Id = \"recent_api_key\"\n\t\tf.Name = \"api_key\"\n\t\tf.Type = \"password\"\n\t\tf.Placeholder = \"sk-...\"\n\t\treturn f\n\t}).String()\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_recent/db.go",
    "content": "package plg_widget_recent\n\nimport (\n\t\"os\"\n\t\"database/sql\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nvar db *sql.DB\n\nfunc init() {\n\tHooks.Register.Onload(func() {\n\t\tif err := initDB(); err != nil {\n\t\t\tLog.Error(\"plg_widget_recent::db err=cannot_init msg=%s\", err.Error())\n\t\t\tos.Exit(1)\n\t\t}\n\t})\n}\n\nfunc initDB() (err error) {\n\tdb, err = sql.Open(\"sqlite3\", GetAbsolutePath(DB_PATH, \"recent.db\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = db.Exec(`\n\t\tCREATE TABLE IF NOT EXISTS recent (\n\t\t\tbackend TEXT NOT NULL,\n\t\t\tuser TEXT NOT NULL,\n\t\t\tpath TEXT NOT NULL,\n\t\t\tlast_accessed INTEGER NOT NULL,\n\t\t\tsize INTEGER DEFAULT 0,\n\t\t\tPRIMARY KEY (backend, user, path)\n\t\t);\n\t\tCREATE INDEX IF NOT EXISTS idx_recent_lookup ON recent(backend, user, last_accessed DESC);\n\t`)\n\treturn err\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_recent/decorator.go",
    "content": "package plg_widget_recent\n\nimport (\n\t\"strings\"\n\t\"os\"\n\t\"time\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\ntype Decorator struct {\n\tIBackend\n\tchroot string\n}\n\nfunc NewRecentDecorator(app *App) Decorator {\n\treturn Decorator{app.Backend, EnforceDirectory(app.Session[\"path\"])}\n}\n\nfunc (this Decorator) Ls(path string) ([]os.FileInfo, error) {\n\tfiles, err := this.IBackend.Ls(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif strings.TrimPrefix(path, this.chroot) == \"\" {\n\t\tif folderName := PluginFolderName(); folderName != \"\" {\n\t\t\tfiles = append(files, File{\n\t\t\t\tFName: folderName,\n\t\t\t\tFType: \"directory\",\n\t\t\t\tFSize: 0,\n\t\t\t\tFTime: time.Now().Unix(),\n\t\t\t})\n\t\t}\n\t}\n\treturn files, nil\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_recent/index.go",
    "content": "package plg_widget_recent\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc init() {\n\tHooks.Register.Middleware(func(next HandlerFunc) HandlerFunc {\n\t\treturn HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {\n\t\t\tif ctx.Share.Id != \"\" || !PluginEnable() {\n\t\t\t\tnext(ctx, res, req)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tctx.Backend = NewRecentDecorator(ctx)\n\t\t\tpath := req.URL.Query().Get(\"path\")\n\t\t\trecentPathAdd := \"\"\n\t\t\trecentPathRemove := \"\"\n\t\t\tif strings.HasSuffix(req.URL.Path, \"/api/files/search\") && req.Method == http.MethodGet && strings.HasPrefix(path, \"/\"+PluginFolderName()+\"/\") {\n\t\t\t\tfiles, err := SearchRecent(req.Context(), GenerateID(ctx.Session), getUser(ctx.Session), req.URL.Query().Get(\"q\"))\n\t\t\t\tif err != nil {\n\t\t\t\t\tSendErrorResult(res, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tSendSuccessResults(res, files)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif strings.HasSuffix(req.URL.Path, \"/api/files/ls\") && req.Method == http.MethodGet {\n\t\t\t\tif path == \"/\"+PluginFolderName()+\"/\" {\n\t\t\t\t\tfiles, err := GetRecent(GenerateID(ctx.Session), getUser(ctx.Session))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tSendErrorResult(res, err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tSendSuccessResultsWithMetadata(res, files, Metadata{\n\t\t\t\t\t\tCanCreateFile: NewBool(false),\n\t\t\t\t\t\tCanCreateDirectory: NewBool(false),\n\t\t\t\t\t\tCanRename:     NewBool(false),\n\t\t\t\t\t\tCanMove:       NewBool(false),\n\t\t\t\t\t\tCanUpload:     NewBool(false),\n\t\t\t\t\t\tCanDelete: NewBool(false),\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\trecentPathAdd = EnforceDirectory(req.URL.Query().Get(\"path\"))\n\t\t\t}\n\t\t\tif strings.HasSuffix(req.URL.Path, \"/api/files/cat\") && (req.Method == http.MethodGet || req.Method == http.MethodPost) {\n\t\t\t\trecentPathAdd = req.URL.Query().Get(\"path\")\n\t\t\t}\n\t\t\tif strings.HasSuffix(req.URL.Path, \"/api/files/save\") && (req.Method == http.MethodPost) {\n\t\t\t\trecentPathAdd = req.URL.Query().Get(\"path\")\n\t\t\t}\n\t\t\tif strings.HasSuffix(req.URL.Path, \"/api/files/rm\") && req.Method == http.MethodPost  {\n\t\t\t\trecentPathRemove = req.URL.Query().Get(\"path\")\n\t\t\t}\n\t\t\tif strings.HasSuffix(req.URL.Path, \"/api/files/mkdir\") && req.Method == http.MethodPost  {\n\t\t\t\trecentPathAdd = EnforceDirectory(req.URL.Query().Get(\"path\"))\n\t\t\t}\n\t\t\tif strings.HasSuffix(req.URL.Path, \"/api/files/mv\") && req.Method == http.MethodPost  {\n\t\t\t\trecentPathRemove = req.URL.Query().Get(\"from\")\n\t\t\t\trecentPathAdd = req.URL.Query().Get(\"to\")\n\t\t\t}\n\n\t\t\tif recentPathAdd != \"\" {\n\t\t\t\tvar size int64 = 0\n\t\t\t\tif b, err := ctx.Backend.Init(ctx.Session, ctx); err == nil {\n\t\t\t\t\tif finfo, err := b.Stat(JoinPath(ctx.Session[\"path\"], recentPathAdd)); err == nil {\n\t\t\t\t\t\tsize = finfo.Size()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tgo StoreRecent(\n\t\t\t\t\tGenerateID(ctx.Session),\n\t\t\t\t\tgetUser(ctx.Session),\n\t\t\t\t\trecentPathAdd,\n\t\t\t\t\tsize,\n\t\t\t\t)\n\t\t\t}\n\t\t\tif recentPathRemove != \"\" {\n\t\t\t\tgo RemoveRecent(\n\t\t\t\t\tGenerateID(ctx.Session),\n\t\t\t\t\tgetUser(ctx.Session),\n\t\t\t\t\trecentPathRemove,\n\t\t\t\t)\n\t\t\t}\n\t\t\tnext(ctx, res, req)\n\t\t})\n\t})\n}\n\nfunc getUser(session map[string]string) string {\n\tif session[\"user\"] != \"\" {\n\t\treturn session[\"user\"]\n\t}\n\treturn \"unknown\"\n}\n"
  },
  {
    "path": "server/plugin/plg_widget_recent/service.go",
    "content": "package plg_widget_recent\n\nimport (\n\t\"context\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\t\"os\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n)\n\nfunc GetRecent(backendID string, user string) ([]os.FileInfo, error) {\n\trows, err := db.Query(`\n\t\tSELECT path, last_accessed, size\n\t\tFROM recent\n\t\tWHERE backend = ? AND user = ?\n\t\tORDER BY last_accessed DESC\n\t\tLIMIT 100\n\t`, backendID, user)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\n\tfiles := make([]os.FileInfo, 0)\n\tfor rows.Next() {\n\t\tfile := File{\n\t\t\tFType: \"file\",\n\t\t\tFTime: -1,\n\t\t\tFSize: -1,\n\t\t}\n\t\tif err := rows.Scan(&file.FPath, &file.FTime, &file.FSize); err != nil {\n\t\t\tLog.Warning(\"plg_widget_recent::get err=%s\", err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasSuffix(file.FPath, \"/\") {\n\t\t\tfile.FType = \"directory\"\n\t\t}\n\t\tfile.FName = filepath.Base(strings.TrimSuffix(file.FPath, \"/\"))\n\t\tfiles = append(files, file)\n\t}\n\treturn files, nil\n}\n\nfunc StoreRecent(backendID string, user string, path string, size int64) error {\n\t_, err := db.Exec(`\n\t\tINSERT INTO recent(backend, user, path, last_accessed, size) VALUES(?, ?, ?, ?, ?)\n\t\tON CONFLICT(backend, user, path) DO UPDATE SET last_accessed = excluded.last_accessed\n\t`, backendID, user, path, time.Now().UnixMilli(), size)\n\tif err != nil {\n\t\tLog.Warning(\"plg_widget_recent::store path=%s err=%s\", path, err.Error())\n\t\treturn err\n\t}\n\treturn err\n}\n\nfunc SearchRecent(ctx context.Context, backendID string, user string, query string) ([]os.FileInfo, error) {\n\trows, err := db.Query(`\n\t\tSELECT path, last_accessed, size\n\t\tFROM recent\n\t\tWHERE backend = ? AND user = ?\n\t\tORDER BY last_accessed DESC\n\t\tLIMIT 500\n\t`, backendID, user)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\n\tfiles := make([]os.FileInfo, 0)\n\tfor rows.Next() {\n\t\tfile := File{\n\t\t\tFType: \"file\",\n\t\t\tFTime: -1,\n\t\t\tFSize: -1,\n\t\t}\n\t\tif err := rows.Scan(&file.FPath, &file.FTime, &file.FSize); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasSuffix(file.FPath, \"/\") {\n\t\t\tfile.FType = \"directory\"\n\t\t}\n\t\tfile.FName = filepath.Base(strings.TrimSuffix(file.FPath, \"/\"))\n\t\tfiles = append(files, file)\n\t}\n\tif len(files) == 0 || !PluginEnableAI() {\n\t\treturn files, nil\n\t}\n\treturn aiFilterRecent(ctx, query, files)\n}\n\nfunc RemoveRecent(backendID string, user string, path string) error {\n\t_, err := db.Exec(`\n\t\tDELETE FROM recent WHERE backend = ? AND user = ? AND path > ?\n\t`, backendID, user, path)\n\tif err != nil {\n\t\tLog.Warning(\"plg_widget_recent::remove path=%s err=%s\", path, err.Error())\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "server/routes.go",
    "content": "package server\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/pprof\"\n\t\"runtime\"\n\t\"runtime/debug\"\n\t\"strconv\"\n\n\t\"github.com/gorilla/mux\"\n\n\t. \"github.com/mickael-kerjean/filestash/server/common\"\n\t. \"github.com/mickael-kerjean/filestash/server/ctrl\"\n\t. \"github.com/mickael-kerjean/filestash/server/middleware\"\n\t. \"github.com/mickael-kerjean/filestash/server/pkg/workflow\"\n)\n\nfunc Build(r *mux.Router) {\n\tvar middlewares []Middleware\n\n\t// API for Session\n\tsession := r.PathPrefix(WithBase(\"/api/session\")).Subrouter()\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, SecureOrigin, SessionStart, PluginInjector}\n\tsession.HandleFunc(\"\", NewMiddlewareChain(SessionGet, middlewares)).Methods(\"GET\")\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, SecureOrigin, RateLimiter, BodyParser, PluginInjector}\n\tsession.HandleFunc(\"\", NewMiddlewareChain(SessionAuthenticate, middlewares)).Methods(\"POST\")\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, SecureOrigin, PluginInjector}\n\tsession.HandleFunc(\"\", NewMiddlewareChain(SessionLogout, middlewares)).Methods(\"DELETE\")\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, PluginInjector}\n\tsession.HandleFunc(\"/auth/{service}\", NewMiddlewareChain(SessionOAuthBackend, middlewares)).Methods(\"GET\")\n\tsession.HandleFunc(\"/auth/\", NewMiddlewareChain(SessionAuthMiddleware, middlewares)).Methods(\"GET\", \"POST\")\n\n\t// API for Admin Console\n\tadmin := r.PathPrefix(WithBase(\"/admin/api\")).Subrouter()\n\tmiddlewares = []Middleware{ApiHeaders, SecureOrigin, PluginInjector}\n\tadmin.HandleFunc(\"/session\", NewMiddlewareChain(AdminSessionGet, middlewares)).Methods(\"GET\")\n\tmiddlewares = []Middleware{ApiHeaders, SecureOrigin, RateLimiter, PluginInjector}\n\tadmin.HandleFunc(\"/session\", NewMiddlewareChain(AdminSessionAuthenticate, middlewares)).Methods(\"POST\")\n\tmiddlewares = []Middleware{ApiHeaders, AdminOnly, SecureOrigin, PluginInjector}\n\tadmin.HandleFunc(\"/config\", NewMiddlewareChain(PrivateConfigHandler, middlewares)).Methods(\"GET\")\n\tadmin.HandleFunc(\"/config\", NewMiddlewareChain(PrivateConfigUpdateHandler, middlewares)).Methods(\"POST\")\n\tadmin.HandleFunc(\"/workflow\", NewMiddlewareChain(WorkflowAll, middlewares)).Methods(\"GET\")\n\tadmin.HandleFunc(\"/workflow/{workflowID}\", NewMiddlewareChain(WorkflowGet, middlewares)).Methods(\"GET\")\n\tadmin.HandleFunc(\"/workflow\", NewMiddlewareChain(WorkflowUpsert, middlewares)).Methods(\"POST\")\n\tadmin.HandleFunc(\"/workflow\", NewMiddlewareChain(WorkflowDelete, middlewares)).Methods(\"DELETE\")\n\tadmin.HandleFunc(\"/middlewares/authentication\", NewMiddlewareChain(AdminAuthenticationMiddleware, middlewares)).Methods(\"GET\")\n\tadmin.HandleFunc(\"/audit\", NewMiddlewareChain(FetchAuditHandler, middlewares)).Methods(\"GET\")\n\tmiddlewares = []Middleware{IndexHeaders, AdminOnly, PluginInjector}\n\tadmin.HandleFunc(\"/logs\", NewMiddlewareChain(FetchLogHandler, middlewares)).Methods(\"GET\")\n\n\t// API for File management\n\tfiles := r.PathPrefix(WithBase(\"/api/files\")).Subrouter()\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, SessionStart, LoggedInOnly, PluginInjector}\n\tfiles.HandleFunc(\"/cat\", NewMiddlewareChain(FileCat, middlewares)).Methods(\"GET\", \"HEAD\")\n\tfiles.HandleFunc(\"/zip\", NewMiddlewareChain(FileDownloader, middlewares)).Methods(\"GET\")\n\tfiles.HandleFunc(\"/unzip\", NewMiddlewareChain(FileExtract, middlewares)).Methods(\"POST\")\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, SecureOrigin, SessionStart, LoggedInOnly, PluginInjector}\n\tfiles.HandleFunc(\"/cat\", NewMiddlewareChain(FileAccess, middlewares)).Methods(\"OPTIONS\")\n\tfiles.HandleFunc(\"/cat\", NewMiddlewareChain(FileSave, middlewares)).Methods(\"POST\", \"PATCH\")\n\tfiles.HandleFunc(\"/save\", NewMiddlewareChain(FileSave, middlewares)).Methods(\"POST\", \"PATCH\", \"HEAD\", \"OPTIONS\")\n\tfiles.HandleFunc(\"/ls\", NewMiddlewareChain(FileLs, middlewares)).Methods(\"GET\")\n\tfiles.HandleFunc(\"/mv\", NewMiddlewareChain(FileMv, middlewares)).Methods(\"POST\")\n\tfiles.HandleFunc(\"/rm\", NewMiddlewareChain(FileRm, middlewares)).Methods(\"POST\")\n\tfiles.HandleFunc(\"/mkdir\", NewMiddlewareChain(FileMkdir, middlewares)).Methods(\"POST\")\n\tfiles.HandleFunc(\"/touch\", NewMiddlewareChain(FileTouch, middlewares)).Methods(\"POST\")\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, SecureOrigin, SessionStart, LoggedInOnly, PluginInjector}\n\tfiles.HandleFunc(\"/search\", NewMiddlewareChain(FileSearch, middlewares)).Methods(\"GET\")\n\n\t// API for Shared link\n\tshare := r.PathPrefix(WithBase(\"/api/share\")).Subrouter()\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, SecureOrigin, SessionStart, LoggedInOnly, PluginInjector}\n\tshare.HandleFunc(\"\", NewMiddlewareChain(ShareList, middlewares)).Methods(\"GET\")\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, SecureOrigin, BodyParser, PluginInjector}\n\tshare.HandleFunc(\"/{share}/proof\", NewMiddlewareChain(ShareVerifyProof, middlewares)).Methods(\"POST\")\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, SecureOrigin, CanManageShare, PluginInjector}\n\tshare.HandleFunc(\"/{share}\", NewMiddlewareChain(ShareDelete, middlewares)).Methods(\"DELETE\")\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, SecureOrigin, BodyParser, CanManageShare, PluginInjector}\n\tshare.HandleFunc(\"/{share}\", NewMiddlewareChain(ShareUpsert, middlewares)).Methods(\"POST\")\n\n\tmeta := r.PathPrefix(WithBase(\"/api/metadata\")).Subrouter()\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, SecureOrigin, SessionStart, LoggedInOnly, PluginInjector}\n\tmeta.HandleFunc(\"\", NewMiddlewareChain(MetaGet, middlewares)).Methods(\"GET\")\n\tmeta.HandleFunc(\"\", NewMiddlewareChain(MetaUpsert, middlewares)).Methods(\"POST\")\n\tmeta.HandleFunc(\"/search\", NewMiddlewareChain(MetaSearch, middlewares)).Methods(\"POST\")\n\n\t// Webdav server / Shared Link\n\tmiddlewares = []Middleware{IndexHeaders, SecureHeaders, PluginInjector}\n\tr.HandleFunc(WithBase(\"/s/{share}\"), NewMiddlewareChain(ServeFrontofficeHandler, middlewares)).Methods(\"GET\")\n\tmiddlewares = []Middleware{WebdavBlacklist, SessionStart, PluginInjector}\n\tr.PathPrefix(WithBase(\"/s/{share}\")).Handler(NewMiddlewareChain(WebdavHandler, middlewares))\n\n\t// Application Resources\n\tmiddlewares = []Middleware{ApiHeaders, SecureHeaders, PluginInjector}\n\tr.HandleFunc(WithBase(\"/api/backend\"), NewMiddlewareChain(AdminBackend, middlewares)).Methods(\"GET\")\n\tr.HandleFunc(WithBase(\"/api/plugin\"), NewMiddlewareChain(PluginExportHandler, append(middlewares, PublicCORS))).Methods(\"GET\", \"OPTIONS\")\n\tr.HandleFunc(WithBase(\"/api/config\"), NewMiddlewareChain(PublicConfigHandler, append(middlewares, PublicCORS))).Methods(\"GET\", \"OPTIONS\")\n\tmiddlewares = []Middleware{StaticHeaders, SecureHeaders, PublicCORS, PluginInjector}\n\tr.PathPrefix(WithBase(\"/assets/bundle.js\")).Handler(http.HandlerFunc(NewMiddlewareChain(ServeBundle(), middlewares))).Methods(\"GET\", \"OPTIONS\")\n\tr.HandleFunc(WithBase(\"/assets/\"+BUILD_REF+\"/plugin/{name}.zip/{path:.+}\"), NewMiddlewareChain(PluginStaticHandler, middlewares)).Methods(\"GET\", \"OPTIONS\", \"HEAD\")\n\tr.HandleFunc(WithBase(\"/assets/\"+BUILD_REF+\"/plugin/{name}.zip\"), NewMiddlewareChain(PluginDownloadHandler, middlewares)).Methods(\"GET\")\n\tr.HandleFunc(WithBase(\"/assets/plugin/{name}.zip\"), NewMiddlewareChain(PluginDownloadHandler, middlewares)).Methods(\"GET\")\n\tr.PathPrefix(WithBase(\"/assets/\"+BUILD_REF)).Handler(http.HandlerFunc(NewMiddlewareChain(ServeFile(\"/\"), middlewares))).Methods(\"GET\", \"OPTIONS\")\n\tr.PathPrefix(WithBase(\"/assets/\")).Handler(http.HandlerFunc(NewMiddlewareChain(ServeFile(\"/\"), middlewares))).Methods(\"GET\", \"OPTIONS\")\n\tr.HandleFunc(WithBase(\"/sw.js\"), http.HandlerFunc(NewMiddlewareChain(ServeFile(\"/assets/\"), middlewares))).Methods(\"GET\")\n\tr.HandleFunc(WithBase(\"/favicon.ico\"), NewMiddlewareChain(ServeFavicon, middlewares)).Methods(\"GET\")\n\n\t// Other endpoints\n\tmiddlewares = []Middleware{ApiHeaders, PluginInjector, PublicCORS}\n\tr.HandleFunc(WithBase(\"/report\"), NewMiddlewareChain(ReportHandler, middlewares)).Methods(\"POST\", \"OPTIONS\")\n\tmiddlewares = []Middleware{IndexHeaders, SecureHeaders, PluginInjector}\n\tr.HandleFunc(WithBase(\"/about\"), NewMiddlewareChain(AboutHandler, middlewares)).Methods(\"GET\")\n\tr.HandleFunc(WithBase(\"/robots.txt\"), NewMiddlewareChain(RobotsHandler, []Middleware{}))\n\tr.HandleFunc(WithBase(\"/manifest.json\"), NewMiddlewareChain(ManifestHandler, []Middleware{})).Methods(\"GET\")\n\tr.HandleFunc(WithBase(\"/.well-known/security.txt\"), NewMiddlewareChain(WellKnownSecurityHandler, []Middleware{})).Methods(\"GET\")\n\tr.HandleFunc(WithBase(\"/healthz\"), NewMiddlewareChain(HealthHandler, []Middleware{})).Methods(\"GET\", \"HEAD\")\n\tr.HandleFunc(WithBase(\"/custom.css\"), NewMiddlewareChain(CustomCssHandler, []Middleware{})).Methods(\"GET\")\n}\n\nfunc CatchAll(r *mux.Router) {\n\tmiddlewares := []Middleware{SecureHeaders, PluginInjector}\n\tr.PathPrefix(WithBase(\"/admin\")).Handler(http.HandlerFunc(NewMiddlewareChain(ServeBackofficeHandler, middlewares))).Methods(\"GET\")\n\tmiddlewares = []Middleware{IndexHeaders, SecureHeaders, PluginInjector}\n\tr.PathPrefix(\"/\").Handler(http.HandlerFunc(NewMiddlewareChain(ServeFrontofficeHandler, middlewares))).Methods(\"GET\", \"POST\")\n}\n\nfunc DebugRoutes(r *mux.Router) {\n\tr.HandleFunc(\"/debug/pprof/\", pprof.Index)\n\tr.HandleFunc(\"/debug/pprof/cmdline\", pprof.Cmdline)\n\tr.HandleFunc(\"/debug/pprof/profile\", pprof.Profile)\n\tr.HandleFunc(\"/debug/pprof/symbol\", pprof.Symbol)\n\tr.HandleFunc(\"/debug/pprof/trace\", pprof.Trace)\n\tr.Handle(\"/debug/pprof/goroutine\", pprof.Handler(\"goroutine\"))\n\tr.Handle(\"/debug/pprof/heap\", pprof.Handler(\"heap\"))\n\tr.Handle(\"/debug/pprof/threadcreate\", pprof.Handler(\"threadcreate\"))\n\tr.Handle(\"/debug/pprof/block\", pprof.Handler(\"block\"))\n\tr.Handle(\"/debug/pprof/allocs\", pprof.Handler(\"allocs\"))\n\tr.Handle(\"/debug/pprof/mutex\", pprof.Handler(\"mutex\"))\n\tr.HandleFunc(\"/debug/free\", func(w http.ResponseWriter, r *http.Request) {\n\t\tdebug.FreeOSMemory()\n\t\tw.Write([]byte(\"DONE\"))\n\t})\n\tbToMb := func(b uint64) string {\n\t\treturn strconv.Itoa(int(b / 1024 / 1024))\n\t}\n\tr.HandleFunc(\"/debug/memory\", func(w http.ResponseWriter, r *http.Request) {\n\t\tvar m runtime.MemStats\n\t\truntime.ReadMemStats(&m)\n\t\tw.Write([]byte(\"<p style='font-family:monospace'>\"))\n\t\tw.Write([]byte(\"Alloc      = \" + bToMb(m.Alloc) + \"MiB <br>\"))\n\t\tw.Write([]byte(\"TotalAlloc = \" + bToMb(m.TotalAlloc) + \"MiB <br>\"))\n\t\tw.Write([]byte(\"Sys        = \" + bToMb(m.Sys) + \"MiB <br>\"))\n\t\tw.Write([]byte(\"NumGC      = \" + strconv.Itoa(int(m.NumGC))))\n\t\tw.Write([]byte(\"</p>\"))\n\t})\n}\n\nfunc PluginRoutes(r *mux.Router) {\n\t// frontoffice overrides: it is the mean by which plugin can interact with the frontoffice\n\tfor _, obj := range Hooks.Get.FrontendOverrides() {\n\t\tr.HandleFunc(obj, func(res http.ResponseWriter, req *http.Request) {\n\t\t\tres.Header().Set(\"Content-Type\", GetMimeType(req.URL.String()))\n\t\t\tres.Write([]byte(fmt.Sprintf(\"/* Default '%s' */\", obj)))\n\t\t})\n\t}\n\t// map file types to application handler\n\tr.HandleFunc(WithBase(\"/overrides/xdg-open.js\"), func(res http.ResponseWriter, req *http.Request) {\n\t\tres.Header().Set(\"Content-Type\", GetMimeType(req.URL.String()))\n\t\tres.Write([]byte(`window.overrides[\"xdg-open\"] = function(mime){`))\n\t\topeners := Hooks.Get.XDGOpen()\n\t\tfor i := 0; i < len(openers); i++ {\n\t\t\tres.Write([]byte(openers[i]))\n\t\t}\n\t\tres.Write([]byte(`return null;}`))\n\t})\n}\n"
  }
]